summaryrefslogtreecommitdiffstats
path: root/src/usr/isteps/ucd/updateUcdFlash.C
blob: 1211b460b86238112a44988ffe2dba3d7a04a873 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
/* IBM_PROLOG_BEGIN_TAG                                                   */
/* This is an automatically generated prolog.                             */
/*                                                                        */
/* $Source: src/usr/isteps/ucd/updateUcdFlash.C $                         */
/*                                                                        */
/* OpenPOWER HostBoot Project                                             */
/*                                                                        */
/* Contributors Listed Below - COPYRIGHT 2019                             */
/* [+] International Business Machines Corp.                              */
/*                                                                        */
/*                                                                        */
/* Licensed under the Apache License, Version 2.0 (the "License");        */
/* you may not use this file except in compliance with the License.       */
/* You may obtain a copy of the License at                                */
/*                                                                        */
/*     http://www.apache.org/licenses/LICENSE-2.0                         */
/*                                                                        */
/* Unless required by applicable law or agreed to in writing, software    */
/* distributed under the License is distributed on an "AS IS" BASIS,      */
/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or        */
/* implied. See the License for the specific language governing           */
/* permissions and limitations under the License.                         */
/*                                                                        */
/* IBM_PROLOG_END_TAG                                                     */

#include <config.h>
#include <isteps/ucd/updateUcdFlash.H>
#include <ucd/ucd_reasoncodes.H>
#include <devicefw/driverif.H>

#include <hwas/common/hwasCallout.H>

#include <targeting/common/entitypath.H>
#include <targeting/common/target.H>
#include <targeting/common/targetservice.H>
#include <targeting/common/utilFilter.H>

#include <errl/errlentry.H>
#include <errl/errlmanager.H>
#include <errl/hberrltypes.H>

#include <trace/interface.H>
#include <string.h>
#include <hbotcompid.H>

namespace POWER_SEQUENCER
{
namespace TI
{
namespace UCD // UCD Series
{

trace_desc_t* g_trac_ucd = nullptr;
TRAC_INIT(&g_trac_ucd, UCD_COMP_NAME, 2*KILOBYTE);

class Ucd
{
private:

    enum DEVICE_OP_LENGTH : size_t
    {
        MFR_REVISION_SIZE = 12,
        DEVICE_ID_SIZE = 32,
    };

    enum COMMAND : uint8_t
    {
        // PMBUS Specificiation
        MFR_REVISION = 0x9B, // Common, max 12 ASCII bytes

        // Manufacturer specific (0xD0-> 0xFD)
        DEVICE_ID    = 0xFD, // Common. max 32 ASCII bytes
    };

    const TARGETING::TargetHandle_t iv_pUcd;
    char* iv_deviceId;
    uint16_t iv_mfrRevision;

    /*
     *  Delete Copy Constructor
     */
    Ucd(const Ucd&) = delete;

    /*
     *  Delete Copy Assignment
     */
    Ucd& operator=(const Ucd&) = delete;

    /*
     *  Delete Move Constructor
     */
    Ucd (Ucd&&) = delete;

    /*
     *  Delete Move Assignment
     */
    Ucd& operator=(Ucd&&) = delete;

public:

    /* @brief      Constructor that takes a UCD target and sets up all of the
     *             instance variables. Will assert if a nullptr is given or if
     *             the given target is not of type POWER_SEQUENCER.
     *
     * @param[in] i_ucd      A pointer to the UCD target. Must not be nullptr
     *                       and must be of type POWER_SEQUENCER.
     */
    Ucd(const TARGETING::TargetHandle_t i_ucd)
        : iv_pUcd(i_ucd), iv_deviceId(nullptr), iv_mfrRevision(0)
    {
        assert(i_ucd != nullptr, "i_ucd must not be nullptr");
        assert(i_ucd->getAttr<TARGETING::ATTR_TYPE>()
                == TARGETING::TYPE_POWER_SEQUENCER,
                "i_ucd must be of type POWER_SEQUENCER");
    }

    /* @brief           Destructor that cleans up the iv_deviceId instance
     *                  variable.
     *
     */
    ~Ucd()
    {
        delete[] iv_deviceId;
        iv_deviceId = nullptr;
    }

    /*
     * @brief           Sets up the device id and mfr revision instance
     *                  variables by performing two device reads on the UCD.
     *
     * @return          nullptr on success. Otherwise, a error that occurred.
     *
     */
    errlHndl_t initialize()
    {
        errlHndl_t err = nullptr;

        // Wipe out the instance variables in case this function is called
        // multiple times. That way if we fail during one of those attempts
        // old values aren't preserved.
        delete[] iv_deviceId;
        iv_deviceId = nullptr;
        iv_mfrRevision = 0;

        do
        {
            char deviceIdBuffer[DEVICE_ID_SIZE]{};

            // Get the I2C info for this UCD.
            const auto i2cInfo = iv_pUcd->
                getAttr<TARGETING::ATTR_I2C_CONTROL_INFO>();

            TARGETING::TargetHandle_t i2cMaster =
                TARGETING::targetService().toTarget(i2cInfo.i2cMasterPath);

           assert(i2cMaster != nullptr, "i2cMaster for UCD 0x%.8X was nullptr",
                  get_huid(iv_pUcd));

            size_t size = sizeof(deviceIdBuffer);

            err = deviceOp(DeviceFW::READ,
                           i2cMaster,
                           deviceIdBuffer,
                           size,
                           DEVICE_I2C_SMBUS_BLOCK(i2cInfo.engine,
                                                  i2cInfo.port,
                                                  i2cInfo.devAddr,
                                                  DEVICE_ID,
                                                  i2cInfo.i2cMuxBusSelector,
                                                  &i2cInfo.i2cMuxPath)
                          );

            // @TODO RTC 205982: Handle the PEC byte if it exists.
            if (err)
            {
                TRACFCOMP(g_trac_ucd, ERR_MRK"Ucd::Initialize(): Could not "
                          "read DEVICE_ID from UCD "
                          "0x%.8X", get_huid(iv_pUcd));
                break;
            }

            // Verify that the buffer is the size the we expected to get.
            if (size != DEVICE_ID_SIZE)
            {
                TRACFCOMP(g_trac_ucd, ERR_MRK"Ucd::Initialize(): Read from "
                          "UCD 0x%.8X returned "
                          "size different than requested. "
                          "Actual %d, expected %d",
                          get_huid(iv_pUcd),
                          size, DEVICE_ID_SIZE);

                /*@
                 * @errortype
                 * @severity           ERRL_SEV_UNRECOVERABLE
                 * @moduleid           UCD_RC::MOD_UCD_INIT
                 * @reasoncode         UCD_RC::RC_DEVICE_READ_UNEXPECTED_SIZE_DEVICE_ID
                 * @devdesc            A device read from the UCD didn't read
                 *                     the expected number of bytes.
                 * @custdesc           A problem occurred during the IPL of the
                 *                     system: Internal Firmware Error
                 * @userdata1[00:31]   Expected read size
                 * @userdata1[32:63]   Actual read size
                 * @userdata2          HUID of the UCD
                 */
                err = new ERRORLOG::ErrlEntry(
                              ERRORLOG::ERRL_SEV_UNRECOVERABLE,
                              UCD_RC::MOD_UCD_INIT,
                              UCD_RC::RC_DEVICE_READ_UNEXPECTED_SIZE_DEVICE_ID,
                              TWO_UINT32_TO_UINT64(DEVICE_ID_SIZE, size),
                              get_huid(iv_pUcd)
                          );

                err->addI2cDeviceCallout(i2cMaster,
                                         i2cInfo.engine,
                                         i2cInfo.port,
                                         i2cInfo.devAddr,
                                         HWAS::SRCI_PRIORITY_HIGH);

                err->addProcedureCallout(HWAS::EPUB_PRC_HB_CODE,
                                         HWAS::SRCI_PRIORITY_LOW);


                break;
            }

            // Verify there is a null terminator at the end of the buffer.
            if (deviceIdBuffer[DEVICE_ID_SIZE-1] != '\0')
            {
                deviceIdBuffer[DEVICE_ID_SIZE-1] = '\0';
            }

            // Since the format of the buffer will be: Device Id|..|..|..
            // Replace the first occurence of the | symbol with null to
            // exclude irrelevant info.
            auto pDelimiter = strchr(deviceIdBuffer, '|');

            if (pDelimiter != nullptr)
            {
                *pDelimiter = '\0';
            }

            // Copy the device id into the instance variable.
            iv_deviceId = new char[strlen(deviceIdBuffer)+1]();
            strcpy(iv_deviceId, deviceIdBuffer);

            TRACFCOMP(g_trac_ucd, INFO_MRK
                     "Ucd::Initialize(): DEVICE_ID read from UCD 0x%.8X as %s",
                     get_huid(iv_pUcd),
                     iv_deviceId);

            // This is the buffer that will be used to read the MFR Revision
            // from the UCD device.
            union mfrRevisionBuffer
            {
                // The MFR Revision represented as a value.
                uint16_t value;
                // The MFR Revision represented as ASCII characters excluding
                // null terminator.
                uint8_t str[MFR_REVISION_SIZE];
            } mfrBuf;

            size = MFR_REVISION_SIZE;

            // Read the MFR revision from the UCD device.
            err = deviceOp(DeviceFW::READ,
                           i2cMaster,
                           mfrBuf.str,
                           size,
                           DEVICE_I2C_SMBUS_BLOCK(i2cInfo.engine,
                                                  i2cInfo.port,
                                                  i2cInfo.devAddr,
                                                  MFR_REVISION,
                                                  i2cInfo.i2cMuxBusSelector,
                                                  &i2cInfo.i2cMuxPath)
                          );

            // @TODO RTC 205982: Need to handle the case where a bad PEC byte
            //                   is returned
            if (err)
            {
                TRACFCOMP(g_trac_ucd, ERR_MRK"Ucd::Initializei(): Could not "
                          "read MFR_REVISION from UCD 0x%.8X",
                          get_huid(iv_pUcd));
                break;
            }

            // Verify that the buffer is the size the we expected to get.
            if (size != MFR_REVISION_SIZE)
            {
                TRACFCOMP(g_trac_ucd, ERR_MRK"Ucd::Initialize(): Read from UCD "
                          "0x%.8X returned "
                          "size different than requested. "
                          "Actual %d, expected %d",
                          get_huid(iv_pUcd),
                          size, MFR_REVISION_SIZE);

                /*@
                 * @errortype
                 * @severity         ERRL_SEV_UNRECOVERABLE
                 * @moduleid         UCD_RC::MOD_UCD_INIT
                 * @reasoncode       UCD_RC::RC_DEVICE_READ_UNEXPECTED_SIZE_MFR_REVISION
                 * @devdesc          A device read from the UCD didn't read
                 *                   the expected number of bytes.
                 * @custdesc         A problem occurred during the IPL of the
                 *                   system: Internal Firmware Error
                 * @userdata1[00:31] Expected read size
                 * @userdata1[32:63] Actual read size
                 * @userdata2        HUID of the UCD
                 */
                err = new ERRORLOG::ErrlEntry(
                            ERRORLOG::ERRL_SEV_UNRECOVERABLE,
                            UCD_RC::MOD_UCD_INIT,
                            UCD_RC::RC_DEVICE_READ_UNEXPECTED_SIZE_MFR_REVISION,
                            TWO_UINT32_TO_UINT64(MFR_REVISION_SIZE, size),
                            get_huid(iv_pUcd));

                err->addI2cDeviceCallout(i2cMaster,
                                         i2cInfo.engine,
                                         i2cInfo.port,
                                         i2cInfo.devAddr,
                                         HWAS::SRCI_PRIORITY_HIGH);

                err->addProcedureCallout(HWAS::EPUB_PRC_HB_CODE,
                                         HWAS::SRCI_PRIORITY_LOW);
                break;
            }

            // Convert the ASCII MFR revision to unsigned int.
            iv_mfrRevision = mfrBuf.value;

            TRACFCOMP(g_trac_ucd, INFO_MRK
                      "Ucd::Initialize(): MFR_REVISION read from UCD 0x%.8X "
                      "as 0x%.4X",
                      get_huid(iv_pUcd),
                      mfrBuf.value);

        } while(0);

        return err;
    }

    /*
     * @brief                     Gets the Device ID for the UCD member of this
     *                            struct.
     *
     * @return                    A constant pointer to the device id string.
     *
     */
    const char* getDeviceId() const
    {
        return iv_deviceId;
    }

    /* @brief                     Will get the MFR Revision for the UCD member
     *                            of this struct.
     *
     * @return                    The ASCII MFR revision as a uint16_t.
     */
    uint16_t getMfrRevision() const
    {
        return iv_mfrRevision;
    }

};


errlHndl_t updateUcdFlash(
          TARGETING::Target* i_pUcd,
    const void*              i_pFlashImage)
{
    errlHndl_t pError = nullptr;

    // Stub for future additional support

    return pError;
}

} // End namespace UCD

} // End namespace TI

} // End namespace POWER_SEQUENCER
OpenPOWER on IntegriCloud