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