xref: /openbmc/phosphor-bmc-code-mgmt/i2c-vr/xdpe1x2xx/xdpe1x2xx.cpp (revision c1b36628298d7799677680e70d455f85acf83650)
1 #include "xdpe1x2xx.hpp"
2 
3 #include "common/include/i2c/i2c.hpp"
4 
5 #include <unistd.h>
6 
7 #include <phosphor-logging/lg2.hpp>
8 
9 #include <cstdio>
10 
11 #define REMAINING_TIMES(x, y) (((((x)[1]) << 8) | ((x)[0])) / (y))
12 
13 PHOSPHOR_LOG2_USING;
14 
15 namespace phosphor::software::VR
16 {
17 
18 enum RevisionCode
19 {
20     REV_A = 0x00,
21     REV_B,
22     REV_C,
23     REV_D,
24     REV_E,
25 };
26 
27 enum ProductID
28 {
29     ProductIDXDPE15254 = 0x90,   // Revision C,D
30     ProductIDXDPE15284 = 0x8A,   // Revision A,B,C,D
31     ProductIDXDPE19283AC = 0x95, // Revision A,B,C
32     ProductIDXDPE19283D = 0xAE,  // Revision D
33     ProductIDXDPE192C3AC = 0x96, // Revision A,B,C
34     ProductIDXDPE192C3D = 0xAF,  // Revision D
35     ProductIDXDPE192C3E = 0xB8,  // Revision E
36     ProductIDXDPE1D2G3B = 0xA5,  // Revision B
37 };
38 
39 constexpr uint8_t PMBusICDeviceID = 0xAD;
40 constexpr uint8_t PMBusSTLCml = 0x7E;
41 constexpr uint8_t IFXICDeviceIDLen = 2;
42 constexpr uint8_t IFXMFRAHBAddr = 0xCE;
43 constexpr uint8_t IFXMFRRegWrite = 0xDE;
44 constexpr uint8_t IFXMFRFwCmdData = 0xFD;
45 constexpr uint8_t IFXMFRFwCmd = 0xFE;
46 constexpr uint8_t MFRFwCmdRmng = 0x10;
47 constexpr uint8_t MFRFwCmdGetHWAddress = 0x2E;
48 constexpr uint8_t MFRFwCmdOTPConfSTO = 0x11;
49 constexpr uint8_t MFRFwCmdOTPFileInvd = 0x12;
50 constexpr uint8_t MFRFwCmdGetCRC = 0x2D;
51 constexpr int XDPE152XXConfSize = 1344;
52 constexpr int XDPE152XXDConfSize = 1312;
53 constexpr int XDPE192XXBConfSize = 1416; // Config(728) + PMBus(568) + SVID(120)
54 constexpr int XDPE192C3EConfSize = 1532; // Config(844) + PMBus(568) + SVID(120)
55 constexpr int XDPE1D2G3BConfSize = 1552; // Config(864) + PMBus(568) + SVID(120)
56 constexpr uint8_t VRWarnRemaining = 3;
57 constexpr uint8_t SectTrim = 0x02;
58 
59 constexpr uint16_t MFRDefaultWaitTime = 20;
60 constexpr uint16_t MFRGetHWAddressWaitTime = 5;
61 constexpr uint16_t MFROTPFileInvalidationWaitTime = 100;
62 constexpr uint16_t MFRSectionInvalidationWaitTime = 4;
63 
64 constexpr uint32_t CRC32Poly = 0xEDB88320;
65 
66 const char* const AddressField = "PMBus Address :";
67 const char* const ChecksumField = "Checksum :";
68 const char* const DataStartTag = "[Configuration Data]";
69 const char* const DataEndTag = "[End Configuration Data]";
70 const char* const DataComment = "//";
71 const char* const DataXV = "XV";
72 
73 XDPE1X2XX::XDPE1X2XX(sdbusplus::async::context& ctx, uint16_t bus,
74                      uint16_t address) :
75     VoltageRegulator(ctx), i2cInterface(phosphor::i2c::I2C(bus, address))
76 {}
77 
78 sdbusplus::async::task<bool> XDPE1X2XX::getDeviceId(uint8_t* deviceID)
79 {
80     uint8_t tbuf[16] = {0};
81     tbuf[0] = PMBusICDeviceID;
82     tbuf[1] = 2;
83     uint8_t tSize = 1;
84     uint8_t rbuf[16] = {0};
85     uint8_t rSize = IFXICDeviceIDLen + 1;
86 
87     if (!(co_await this->i2cInterface.sendReceive(tbuf, tSize, rbuf, rSize)))
88     {
89         error("Failed to get device ID");
90         co_return false;
91     }
92 
93     // According to datasheet:
94     // rbuf[1]: device revision
95     // rbuf[2]: device id
96     std::memcpy(deviceID, &rbuf[1], IFXICDeviceIDLen);
97     info.deviceRev = deviceID[0];
98     info.deviceId = deviceID[1];
99     debug("VR Device ID: {ID}", "ID", lg2::hex, rbuf[2]);
100     debug("VR Device Rev: {REV}", "REV", lg2::hex, rbuf[1]);
101 
102     co_return true;
103 }
104 
105 sdbusplus::async::task<bool> XDPE1X2XX::mfrFWcmd(
106     uint8_t cmd, uint16_t processTime, uint8_t* data, uint8_t* resp)
107 {
108     uint8_t tBuf[16] = {0};
109     uint8_t rBuf[16] = {0};
110     uint8_t tSize = 0;
111     uint8_t rSize = 0;
112 
113     if (data)
114     {
115         tBuf[0] = IFXMFRFwCmdData;
116         tBuf[1] = 4; // Block write 4 bytes
117         tSize = 6;
118         std::memcpy(&tBuf[2], data, 4);
119         if (!(co_await this->i2cInterface.sendReceive(tBuf, tSize, rBuf,
120                                                       rSize)))
121         {
122             error("Failed to send MFR command: {CMD}", "CMD",
123                   std::string("IFXMFRFwCmdDAta"));
124             co_return false;
125         }
126     }
127 
128     co_await sdbusplus::async::sleep_for(ctx, std::chrono::microseconds(300));
129 
130     tBuf[0] = IFXMFRFwCmd;
131     tBuf[1] = cmd;
132     tSize = 2;
133     rSize = 0;
134     if (!(co_await this->i2cInterface.sendReceive(tBuf, tSize, rBuf, rSize)))
135     {
136         error("Failed to send MFR command: {CMD}", "CMD",
137               std::string("IFXMFRFwCmd"));
138         co_return false;
139     }
140 
141     co_await sdbusplus::async::sleep_for(
142         ctx, std::chrono::milliseconds(processTime));
143 
144     if (resp)
145     {
146         tBuf[0] = IFXMFRFwCmdData;
147         tSize = 1;
148         rSize = 6;
149         if (!(co_await this->i2cInterface.sendReceive(tBuf, tSize, rBuf,
150                                                       rSize)))
151         {
152             error("Failed to send MFR command: {CMD}", "CMD",
153                   std::string("IFXMFRFwCmdData"));
154             co_return false;
155         }
156         if (rBuf[0] != 4)
157         {
158             error(
159                 "Failed to receive MFR response with unexpected response size");
160             co_return false;
161         }
162         std::memcpy(resp, rBuf + 1, 4);
163     }
164 
165     co_return true;
166 }
167 
168 sdbusplus::async::task<bool> XDPE1X2XX::getRemainingWrites(uint8_t* remain)
169 {
170     // According to datasheet:
171     // remaingin OTP size = rBuf[0] + 256 * rBuf[1]
172     uint8_t tBuf[16] = {0};
173     uint8_t rBuf[16] = {0};
174     uint8_t devId[2] = {0};
175 
176     if (!(co_await this->mfrFWcmd(MFRFwCmdRmng, MFRDefaultWaitTime, tBuf,
177                                   rBuf)))
178     {
179         error("Failed to request remaining writes");
180         co_return false;
181     }
182 
183     if (!(co_await this->getDeviceId(devId)))
184     {
185         error("Failed to request device ID for remaining writes");
186         co_return false;
187     }
188 
189     int configSize = getConfigSize(devId[1], devId[0]);
190     if (configSize < 0)
191     {
192         error("Failed to request valid configuration size");
193         co_return false;
194     }
195 
196     *remain = REMAINING_TIMES(rBuf, configSize);
197 
198     co_return true;
199 }
200 
201 int XDPE1X2XX::getConfigSize(uint8_t deviceId, uint8_t revision)
202 {
203     static const std::map<std::pair<uint8_t, uint8_t>, int> configSizeMap = {
204         {{ProductIDXDPE19283AC, REV_B}, XDPE192XXBConfSize},
205         {{ProductIDXDPE15284, REV_A}, XDPE152XXConfSize},
206         {{ProductIDXDPE15284, REV_B}, XDPE152XXConfSize},
207         {{ProductIDXDPE15284, REV_C}, XDPE152XXConfSize},
208         {{ProductIDXDPE15284, REV_D}, XDPE152XXDConfSize},
209         {{ProductIDXDPE192C3AC, REV_B}, XDPE192XXBConfSize},
210         {{ProductIDXDPE192C3E, REV_E}, XDPE192C3EConfSize},
211         {{ProductIDXDPE1D2G3B, REV_B}, XDPE1D2G3BConfSize},
212     };
213 
214     auto it = configSizeMap.find({deviceId, revision});
215     if (it != configSizeMap.end())
216     {
217         return it->second;
218     }
219 
220     error("Failed to get configuration size of {DEVID} with revision {REV}",
221           "DEVID", deviceId, "REV", revision);
222     return -1;
223 }
224 
225 sdbusplus::async::task<bool> XDPE1X2XX::getCRC(uint32_t* checksum)
226 {
227     uint8_t tBuf[16] = {0};
228     uint8_t rBuf[16] = {0};
229 
230     if (!(co_await this->mfrFWcmd(MFRFwCmdGetCRC, MFRDefaultWaitTime, tBuf,
231                                   rBuf)))
232     {
233         error("Failed to get CRC value");
234         co_return false;
235     }
236 
237     *checksum = (static_cast<uint32_t>(rBuf[3]) << 24) |
238                 (static_cast<uint32_t>(rBuf[2]) << 16) |
239                 (static_cast<uint32_t>(rBuf[1]) << 8) |
240                 (static_cast<uint32_t>(rBuf[0]));
241 
242     co_return true;
243 }
244 
245 sdbusplus::async::task<bool> XDPE1X2XX::program(bool force)
246 {
247     uint8_t tBuf[16] = {0};
248     uint8_t rBuf[16] = {0};
249     uint8_t remain = 0;
250     uint32_t sum = 0;
251     int size = 0;
252 
253     // NOLINTBEGIN(clang-analyzer-core.uninitialized.Branch)
254     if (!(co_await getCRC(&sum)))
255     // NOLINTEND(clang-analyzer-core.uninitialized.Branch)
256     {
257         error("Failed to program the VR");
258         co_return -1;
259     }
260 
261     debug("CRC before programming: {CRC}", "CRC", lg2::hex, sum);
262     debug("CRC of configuration: {CRC}", "CRC", lg2::hex, configuration.sumExp);
263 
264     if (!force && (sum == configuration.sumExp))
265     {
266         error("Failed to program the VR - CRC value are equal with no force");
267         co_return -1;
268     }
269 
270     // NOLINTBEGIN(clang-analyzer-core.uninitialized.Branch)
271     if (!(co_await this->getRemainingWrites(&remain)))
272     // NOLINTEND(clang-analyzer-core.uninitialized.Branch)
273     {
274         error("Failed to program the VR - unable to obtain remaing writes");
275         co_return -1;
276     }
277 
278     debug("Remaining write cycles of VR: {REMAIN}", "REMAIN", remain);
279 
280     if (!remain)
281     {
282         error("Failed to program the VR - no remaining write cycles left");
283         co_return -1;
284     }
285 
286     if (!force && (remain <= VRWarnRemaining))
287     {
288         error(
289             "Failed to program the VR - {REMAIN} remaining writes left and not force",
290             "REMAIN", remain);
291         co_return -1;
292     }
293 
294     // Added reprogramming of the entire configuration file.
295     // Except for the trim section, all other data will be replaced.
296     // 0xfe 0xfe 0x00 0x00 instructs the command to reprogram all header codes
297     // and XVcode. If the old sections are not invalidated in OTP, they can
298     // affect the CRC calculation.
299     debug("Invalidate current Configuration");
300 
301     tBuf[0] = 0xfe;
302     tBuf[1] = 0xfe;
303     tBuf[2] = 0x00;
304     tBuf[3] = 0x00;
305 
306     if (!(co_await this->mfrFWcmd(MFRFwCmdOTPFileInvd,
307                                   MFROTPFileInvalidationWaitTime, tBuf, NULL)))
308     {
309         error("Failed to program the VR - Invalidation of current FW");
310         co_return false;
311     }
312 
313     for (int i = 0; i < configuration.sectCnt; i++)
314     {
315         debug("Programming section: {SEC}", "SEC", i);
316         struct configSect* sect = &configuration.section[i];
317         if (sect == NULL)
318         {
319             error(
320                 "Failed to program the VR - unexpected NULL section in config");
321             co_return false;
322         }
323 
324         if ((i <= 0) || (sect->type != configuration.section[i - 1].type))
325         {
326             debug("Section Type: {TYPE}", "TYPE", lg2::hex,
327                   configuration.section[i].type);
328 
329             // clear bit0 of PMBUS_STS_CML
330             tBuf[0] = PMBusSTLCml;
331             tBuf[1] = 0x1;
332             uint8_t tSize = 2;
333             uint8_t rSize = 0;
334             if (!(co_await this->i2cInterface.sendReceive(tBuf, tSize, rBuf,
335                                                           rSize)))
336             {
337                 error("Failed to program the VR on sendReceive {CMD}", "CMD",
338                       std::string("PMBusSTLCml"));
339                 co_return false;
340             }
341 
342             debug("Invalidating section type: {TYPE}", "TYPE", sect->type);
343             tBuf[0] = sect->type;
344             tBuf[1] = 0x00;
345             tBuf[2] = 0x00;
346             tBuf[3] = 0x00;
347 
348             if (!(co_await this->mfrFWcmd(MFRFwCmdOTPFileInvd,
349                                           MFRSectionInvalidationWaitTime, tBuf,
350                                           NULL)))
351             {
352                 error("Failed to program VR on mfrFWCmd on {CMD}", "CMD",
353                       std::string("MFRFwCmdOTPFileInvd"));
354                 co_return false;
355             }
356 
357             // set scratchpad addr
358             // XDPE192XX Rev A/B: 0x2005e000
359             //           Rev C  : 0x2005e400
360             //           Rev D  : 0x2005f000
361 
362             debug("Setting scratchpad address: {ADDR}", "ADDR", lg2::hex,
363                   info.scratchPadAddress);
364 
365             tBuf[0] = IFXMFRAHBAddr;
366             tBuf[1] = 4;
367             tBuf[2] = (info.scratchPadAddress) & 0xFF;
368             tBuf[3] = (info.scratchPadAddress >> 8) & 0xFF;
369             tBuf[4] = (info.scratchPadAddress >> 16) & 0xFF;
370             tBuf[5] = (info.scratchPadAddress >> 24) & 0xFF;
371             tSize = 6;
372             rSize = 0;
373 
374             if (!(co_await this->i2cInterface.sendReceive(tBuf, tSize, rBuf,
375                                                           rSize)))
376             {
377                 error("Failed to program VR on sendReceive on {CMD}", "CMD",
378                       std::string("IFXMFRAHBAddr"));
379                 co_return false;
380             }
381 
382             co_await sdbusplus::async::sleep_for(
383                 ctx, std::chrono::microseconds(10000));
384             size = 0;
385         }
386 
387         // programm into scratchpad
388         for (int j = 0; j < sect->dataCnt; j++)
389         {
390             tBuf[0] = IFXMFRRegWrite;
391             tBuf[1] = 4;
392             uint8_t tSize = 6;
393             uint8_t rSize = 0;
394             memcpy(&tBuf[2], &sect->data[j], 4);
395             if (!(co_await this->i2cInterface.sendReceive(tBuf, tSize, rBuf,
396                                                           rSize)))
397             {
398                 error("Failed to program the VR on sendReceive {CMD}", "CMD",
399                       std::string("IFXMFRRegWrite"));
400                 co_return false;
401             }
402             co_await sdbusplus::async::sleep_for(ctx,
403                                                  std::chrono::milliseconds(10));
404         }
405 
406         size += sect->dataCnt * 4;
407         if ((i + 1 >= configuration.sectCnt) ||
408             (sect->type != configuration.section[i + 1].type))
409         {
410             // wait for programming soak (2ms/byte, at least 200ms)
411             // ex: Config (604 bytes): (604 / 50) + 2 = 14 (1400 ms)
412             uint16_t soakTime = 100 * ((size / 50) + 2);
413 
414             // Upload to scratchpad
415             debug("Upload from scratch pad to OTP with soak time: {TIME}ms",
416                   "TIME", soakTime);
417             std::memcpy(tBuf, &size, 2);
418             tBuf[2] = 0x00;
419             tBuf[3] = 0x00;
420             if (!(co_await this->mfrFWcmd(MFRFwCmdOTPConfSTO, soakTime, tBuf,
421                                           NULL)))
422             {
423                 error("Failed to program the VR on mfrFWcmd {CMD}", "CMD",
424                       std::string("MFRFwCmdOTPConfSTO"));
425                 co_return false;
426             }
427 
428             // Read status faults after programming
429             tBuf[0] = PMBusSTLCml;
430             uint8_t tSize = 1;
431             uint8_t rSize = 1;
432             if (!(co_await this->i2cInterface.sendReceive(rBuf, tSize, tBuf,
433                                                           rSize)))
434             {
435                 error("Failed to program VR on sendReceive {CMD}", "CMD",
436                       std::string("PMBusSTLCml"));
437                 co_return false;
438             }
439             if (rBuf[0] & 0x01)
440             {
441                 error("Failed to program VR - status fault indicated error");
442                 co_return false;
443             }
444         }
445     }
446 
447     co_return true;
448 }
449 
450 int XDPE1X2XX::lineSplit(char** dest, char* src, char* delim)
451 {
452     char* s = strtok(src, delim);
453     int size = 0;
454     int maxSz = 5;
455 
456     while (s)
457     {
458         *dest++ = s;
459         if ((++size) >= maxSz)
460         {
461             break;
462         }
463         s = strtok(NULL, delim);
464     }
465 
466     return size;
467 }
468 
469 bool XDPE1X2XX::parseImage(const uint8_t* image, size_t image_size)
470 {
471     size_t lenEndTag = strlen(DataEndTag);
472     size_t lenStartTag = strlen(DataStartTag);
473     size_t lenComment = strlen(DataComment);
474     size_t lenXV = strlen(DataXV);
475     size_t start = 0;
476     const int maxLineLength = 40;
477     char line[maxLineLength];
478     char* token = NULL;
479     bool isData = false;
480     char delim = ' ';
481     uint16_t offset;
482     uint8_t sectType = 0x0;
483     uint32_t dWord;
484     int dataCnt = 0;
485     int sectIndex = -1;
486 
487     for (size_t i = 0; i < image_size; i++)
488     {
489         if (image[i] == '\n')
490         {
491             size_t lineLength = i - start;
492             if (i > start && image[i - 1] == '\r')
493             {
494                 lineLength--;
495             }
496             if (lineLength >= maxLineLength)
497             {
498                 error("line length >= 40, please check image file.");
499                 return false;
500             }
501             std::memcpy(line, image + start, lineLength);
502             line[lineLength] = '\0';
503 
504             if (!strncmp(line, DataComment, lenComment))
505             {
506                 token = line + lenComment;
507                 if (!strncmp(token, DataXV, lenXV))
508                 {
509                     debug("Parsing: {OBJ}", "OBJ",
510                           reinterpret_cast<const char*>(line));
511                 }
512                 start = i + 1;
513                 continue;
514             }
515             if (!strncmp(line, DataEndTag, lenEndTag))
516             {
517                 debug("Parsing: {OBJ}", "OBJ",
518                       reinterpret_cast<const char*>(line));
519                 break;
520             }
521             else if (isData)
522             {
523                 char* tokenList[8] = {0};
524                 int tokenSize = lineSplit(tokenList, line, &delim);
525                 if (tokenSize < 1)
526                 {
527                     start = i + 1;
528                     continue;
529                 }
530 
531                 offset = (uint16_t)strtol(tokenList[0], NULL, 16);
532                 if (sectType == SectTrim && offset != 0x0)
533                 {
534                     continue;
535                 }
536 
537                 for (int i = 1; i < tokenSize; i++)
538                 {
539                     dWord = (uint32_t)strtoul(tokenList[i], NULL, 16);
540                     if ((offset == 0x0) && (i == 1))
541                     {
542                         sectType = (uint8_t)dWord;
543                         if (sectType == SectTrim)
544                         {
545                             break;
546                         }
547                         if ((++sectIndex) >= MaxSectCnt)
548                         {
549                             return false;
550                         }
551 
552                         configuration.section[sectIndex].type = sectType;
553                         configuration.sectCnt = sectIndex + 1;
554                         dataCnt = 0;
555                     }
556 
557                     if (dataCnt >= MaxSectDataCnt)
558                     {
559                         return false;
560                     }
561 
562                     configuration.section[sectIndex].data[dataCnt++] = dWord;
563                     configuration.section[sectIndex].dataCnt = dataCnt;
564                     configuration.totalCnt++;
565                 }
566             }
567             else
568             {
569                 if ((token = strstr(line, AddressField)) != NULL)
570                 {
571                     if ((token = strstr(token, "0x")) != NULL)
572                     {
573                         configuration.addr =
574                             (uint8_t)(strtoul(token, NULL, 16) << 1);
575                     }
576                 }
577                 else if ((token = strstr(line, ChecksumField)) != NULL)
578                 {
579                     if ((token = strstr(token, "0x")) != NULL)
580                     {
581                         configuration.sumExp =
582                             (uint32_t)strtoul(token, NULL, 16);
583                     }
584                 }
585                 else if (!strncmp(line, DataStartTag, lenStartTag))
586                 {
587                     isData = true;
588                     start = i + 1;
589                     continue;
590                 }
591                 else
592                 {
593                     start = i + 1;
594                     continue;
595                 }
596             }
597             start = i + 1;
598         }
599     }
600 
601     return true;
602 }
603 
604 bool XDPE1X2XX::checkImage()
605 {
606     uint8_t i;
607     uint32_t crc;
608     uint32_t sum = 0;
609 
610     for (i = 0; i < configuration.sectCnt; i++)
611     {
612         struct configSect* sect = &configuration.section[i];
613         if (sect == NULL)
614         {
615             error("Failed to check image - unexpected NULL section");
616             return false;
617         }
618 
619         crc = calcCRC32(&sect->data[0], 2);
620         if (crc != sect->data[2])
621         {
622             error("Failed to check image - first CRC value mismatch");
623             return false;
624         }
625         sum += crc;
626 
627         // check CRC of section data
628         crc = calcCRC32(&sect->data[3], sect->dataCnt - 4);
629         if (crc != sect->data[sect->dataCnt - 1])
630         {
631             error("Failed to check image - second CRC value mismatch");
632             return false;
633         }
634         sum += crc;
635     }
636 
637     if (sum != configuration.sumExp)
638     {
639         debug("Calculated CRC: {CRC}", "CRC", lg2::hex, sum);
640         debug("Config CRC {CRC}", "CRC", lg2::hex, configuration.sumExp);
641         error("Failed to check image - third CRC value mismatch");
642         return false;
643     }
644 
645     return true;
646 }
647 
648 sdbusplus::async::task<bool> XDPE1X2XX::verifyImage(const uint8_t* image,
649                                                     size_t imageSize)
650 {
651     if (!parseImage(image, imageSize))
652     {
653         error("Failed to update firmware on parsing Image");
654         co_return false;
655     }
656 
657     if (!checkImage())
658     {
659         error("Failed to update firmware on check image");
660         co_return false;
661     }
662 
663     co_return true;
664 }
665 
666 sdbusplus::async::task<bool> XDPE1X2XX::getScratchPadAddress()
667 {
668     uint8_t tbuf[16] = {0};
669     uint8_t rbuf[16] = {0};
670 
671     tbuf[0] = 0x02;
672     tbuf[1] = 0x00;
673     tbuf[2] = 0x00;
674     tbuf[3] = 0x00;
675 
676     if (!(co_await mfrFWcmd(MFRFwCmdGetHWAddress, MFRGetHWAddressWaitTime, tbuf,
677                             rbuf)))
678     {
679         error("mfrFWcmd call failed to retrieve scratchpad address");
680         co_return false;
681     }
682 
683     info.scratchPadAddress = (static_cast<uint32_t>(rbuf[3]) << 24) |
684                              (static_cast<uint32_t>(rbuf[2]) << 16) |
685                              (static_cast<uint32_t>(rbuf[1]) << 8) |
686                              (static_cast<uint32_t>(rbuf[0]));
687 
688     debug("Scratchpad Address: {ADDR}", "ADDR", lg2::hex,
689           info.scratchPadAddress);
690 
691     co_return true;
692 }
693 
694 sdbusplus::async::task<bool> XDPE1X2XX::updateFirmware(bool force)
695 {
696     bool ret = true;
697     if (!(co_await getScratchPadAddress()))
698     {
699         error("Failed to retrieve scratchpad address");
700         co_return false;
701     }
702 
703     // NOLINTBEGIN(clang-analyzer-core.uninitialized.Branch)
704     ret = co_await program(force);
705     if (!ret)
706     // NOLINTEND(clang-analyzer-core.uninitialized.Branch)
707     {
708         error("Failed to update firmware on program");
709     }
710 
711     info.deviceId = 0;
712     info.deviceRev = 0;
713     info.remainingWrites = 0;
714     info.scratchPadAddress = 0;
715     info.actualCRC = 0;
716     info.configSize = 0;
717 
718     // Reset the configuration
719     configuration.addr = 0;
720     configuration.totalCnt = 0;
721     configuration.sumExp = 0;
722     configuration.sectCnt = 0;
723     for (int i = 0; i <= MaxSectCnt - 1; i++)
724     {
725         configuration.section[i].type = 0;
726         configuration.section[i].dataCnt = 0;
727         for (int j = 0; j < MaxSectDataCnt; j++)
728         {
729             configuration.section[i].data[j] = 0;
730         }
731     }
732 
733     if (!ret)
734     {
735         co_return false;
736     }
737 
738     co_return true;
739 }
740 
741 uint32_t XDPE1X2XX::calcCRC32(const uint32_t* data, int len)
742 {
743     if (data == NULL)
744     {
745         return 0;
746     }
747 
748     uint32_t crc = 0xFFFFFFFF;
749     for (int i = 0; i < len; i++)
750     {
751         crc ^= data[i];
752 
753         for (int b = 0; b < 32; b++)
754         {
755             if (crc & 0x1)
756             {
757                 crc = (crc >> 1) ^ CRC32Poly; // lsb-first
758             }
759             else
760             {
761                 crc >>= 1;
762             }
763         }
764     }
765 
766     return ~crc;
767 }
768 
769 bool XDPE1X2XX::forcedUpdateAllowed()
770 {
771     return true;
772 }
773 
774 } // namespace phosphor::software::VR
775