xref: /openbmc/phosphor-bmc-code-mgmt/i2c-vr/isl69269/isl69269.cpp (revision 6e78f087b59129bf456dc986475318d4cb115688)
1 #include "isl69269.hpp"
2 
3 #include "common/include/i2c/i2c.hpp"
4 
5 #include <phosphor-logging/lg2.hpp>
6 
7 #include <string>
8 
9 PHOSPHOR_LOG2_USING;
10 
11 namespace phosphor::software::VR
12 {
13 
14 constexpr uint8_t regProgStatus = 0x7E;
15 constexpr uint8_t regHexModeCFG0 = 0x87;
16 constexpr uint8_t regCRC = 0x94;
17 constexpr uint8_t regHexModeCFG1 = 0xBD;
18 constexpr uint8_t regDMAData = 0xC5;
19 constexpr uint8_t regDMAAddr = 0xC7;
20 constexpr uint8_t regRestoreCfg = 0xF2;
21 
22 constexpr uint8_t regRemainginWrites = 0x35;
23 
24 constexpr uint8_t gen3SWRevMin = 0x06;
25 constexpr uint8_t deviceIdLength = 4;
26 
27 constexpr uint8_t gen3Legacy = 1;
28 constexpr uint8_t gen3Production = 2;
29 constexpr uint8_t gen2Hex = 3;
30 constexpr uint8_t gen3p5 = 4;
31 
32 constexpr uint16_t cfgId = 7;
33 constexpr uint16_t gen3FileHead = 5;
34 constexpr uint16_t gen3LegacyCRC = 276 - gen3FileHead;
35 constexpr uint16_t gen3ProductionCRC = 290 - gen3FileHead;
36 constexpr uint8_t checksumLen = 4;
37 constexpr uint8_t deviceRevisionLen = 4;
38 
39 // Common pmBus Command codes
40 constexpr uint8_t pmBusDeviceId = 0xAD;
41 constexpr uint8_t pmBusDeviceRev = 0xAE;
42 
43 // Config file constants
44 constexpr char recordTypeData = 0x00;
45 constexpr char recordTypeHeader = 0x49;
46 
47 constexpr uint8_t defaultBufferSize = 16;
48 constexpr uint8_t programBufferSize = 32;
49 
50 constexpr uint8_t zeroByteLen = 0;
51 constexpr uint8_t oneByteLen = 1;
52 constexpr uint8_t threeByteLen = 3;
53 constexpr uint8_t fourByteLen = 4;
54 
55 // RAA Gen2
56 constexpr uint8_t gen2RegProgStatus = 0x07;
57 constexpr uint8_t gen2RegCRC = 0x3F;
58 constexpr uint8_t gen2RegRemainginWrites = 0xC2;
59 
60 constexpr uint32_t gen2RevMin = 0x02000003;
61 
62 constexpr uint8_t hexFileRev = 0x00;
63 constexpr uint16_t gen2FileHead = 6;
64 constexpr uint16_t gen2CRC = 600 - gen2FileHead;
65 
66 // RAA Gen3p5
67 constexpr uint8_t regGen3p5ProgStatus = 0x83;
68 constexpr uint8_t gen3p5RegCRC = 0xF8;
69 constexpr uint8_t gen3p5HWRevMax = 0x06;
70 constexpr uint8_t gen3p5HWRevMin = 0x03;
71 constexpr uint16_t gen3p5cfgId = 3;
72 constexpr uint16_t gen3p5FileHead = 5;
73 constexpr uint16_t gen3p5CRC = 336 - gen3p5FileHead;
74 
ISL69269(sdbusplus::async::context & ctx,uint16_t bus,uint16_t address,Gen gen)75 ISL69269::ISL69269(sdbusplus::async::context& ctx, uint16_t bus,
76                    uint16_t address, Gen gen) :
77     VoltageRegulator(ctx), i2cInterface(phosphor::i2c::I2C(bus, address)),
78     generation(gen)
79 {}
80 
shiftLeftFromLSB(const uint8_t * data,uint32_t * result)81 inline void shiftLeftFromLSB(const uint8_t* data, uint32_t* result)
82 {
83     *result = (static_cast<uint32_t>(data[3]) << 24) |
84               (static_cast<uint32_t>(data[2]) << 16) |
85               (static_cast<uint32_t>(data[1]) << 8) |
86               (static_cast<uint32_t>(data[0]));
87 }
88 
shiftLeftFromMSB(const uint8_t * data,uint32_t * result)89 inline void shiftLeftFromMSB(const uint8_t* data, uint32_t* result)
90 {
91     *result = (static_cast<uint32_t>(data[0]) << 24) |
92               (static_cast<uint32_t>(data[1]) << 16) |
93               (static_cast<uint32_t>(data[2]) << 8) |
94               (static_cast<uint32_t>(data[3]));
95 }
96 
calcCRC8(const uint8_t * data,uint8_t len)97 static uint8_t calcCRC8(const uint8_t* data, uint8_t len)
98 {
99     uint8_t crc = 0x00;
100     int i = 0;
101     int b = 0;
102 
103     for (i = 0; i < len; i++)
104     {
105         crc ^= data[i];
106         for (b = 0; b < 8; b++)
107         {
108             if (crc & 0x80)
109             {
110                 crc = (crc << 1) ^ 0x07; // polynomial 0x07
111             }
112             else
113             {
114                 crc = (crc << 1);
115             }
116         }
117     }
118 
119     return crc;
120 }
121 
dmaReadWrite(uint8_t * reg,uint8_t * resp)122 sdbusplus::async::task<bool> ISL69269::dmaReadWrite(uint8_t* reg, uint8_t* resp)
123 {
124     if (reg == nullptr || resp == nullptr)
125     {
126         error("dmaReadWrite invalid input");
127         co_return false;
128     }
129 
130     uint8_t tbuf[defaultBufferSize] = {0};
131     uint8_t tlen = threeByteLen;
132     uint8_t rbuf[defaultBufferSize] = {0};
133     uint8_t rlen = zeroByteLen;
134 
135     tbuf[0] = regDMAAddr;
136     std::memcpy(&tbuf[1], reg, 2);
137 
138     // NOLINTBEGIN(clang-analyzer-core.uninitialized.Branch)
139     if (!(co_await i2cInterface.sendReceive(tbuf, tlen, rbuf, rlen)))
140     // NOLINTEND(clang-analyzer-core.uninitialized.Branch)
141     {
142         error("dmaReadWrite failed with {CMD}", "CMD",
143               std::string("_REG_DMA_ADDR"));
144         co_return false;
145     }
146 
147     tlen = oneByteLen;
148     rlen = fourByteLen;
149 
150     tbuf[0] = regDMAData;
151     if (!(co_await i2cInterface.sendReceive(tbuf, tlen, resp, rlen)))
152     {
153         error("dmaReadWrite failed with {CMD}", "CMD",
154               std::string("_REG_DMA_DATA"));
155         co_return false;
156     }
157 
158     co_return true;
159 }
160 
getRemainingWrites(uint8_t * remain)161 sdbusplus::async::task<bool> ISL69269::getRemainingWrites(uint8_t* remain)
162 {
163     uint8_t tbuf[defaultBufferSize] = {0};
164     uint8_t rbuf[defaultBufferSize] = {0};
165 
166     tbuf[0] =
167         (generation == Gen::Gen2) ? gen2RegRemainginWrites : regRemainginWrites;
168     tbuf[1] = 0x00;
169     if (!(co_await dmaReadWrite(tbuf, rbuf)))
170     {
171         error("getRemainingWrites failed");
172         co_return false;
173     }
174 
175     *remain = rbuf[0];
176     co_return true;
177 }
178 
getHexMode(uint8_t * mode)179 sdbusplus::async::task<bool> ISL69269::getHexMode(uint8_t* mode)
180 {
181     if (generation == Gen::Gen2)
182     {
183         *mode = gen2Hex;
184         co_return true;
185     }
186     else if (generation == Gen::Gen3p5)
187     {
188         uint32_t devID = 0;
189         if (!(co_await getDeviceId(&devID)))
190         {
191             error("program failed at getDeviceId");
192             co_return false;
193         }
194         devID = (devID >> 8) & 0xFF;
195 
196         if (devID >= 0xBA)
197         {
198             *mode = gen3p5;
199         }
200         co_return true;
201     }
202 
203     uint8_t tbuf[defaultBufferSize] = {0};
204     uint8_t rbuf[defaultBufferSize] = {0};
205 
206     tbuf[0] = regHexModeCFG0;
207     tbuf[1] = regHexModeCFG1;
208     if (!(co_await dmaReadWrite(tbuf, rbuf)))
209     {
210         error("getHexMode failed");
211         co_return false;
212     }
213 
214     *mode = (rbuf[0] == 0) ? gen3Legacy : gen3Production;
215 
216     co_return true;
217 }
218 
getDeviceId(uint32_t * deviceId)219 sdbusplus::async::task<bool> ISL69269::getDeviceId(uint32_t* deviceId)
220 {
221     if (deviceId == nullptr)
222     {
223         error("getDeviceId invalid input");
224         co_return false;
225     }
226 
227     uint8_t tbuf[defaultBufferSize] = {0};
228     uint8_t tLen = oneByteLen;
229     uint8_t rbuf[defaultBufferSize] = {0};
230     uint8_t rLen = deviceIdLength + 1;
231 
232     tbuf[0] = pmBusDeviceId;
233 
234     if (!(co_await i2cInterface.sendReceive(tbuf, tLen, rbuf, rLen)))
235     {
236         error("getDeviceId failed");
237         co_return false;
238     }
239 
240     std::memcpy(deviceId, &rbuf[1], deviceIdLength);
241 
242     co_return true;
243 }
244 
getDeviceRevision(uint32_t * revision)245 sdbusplus::async::task<bool> ISL69269::getDeviceRevision(uint32_t* revision)
246 {
247     if (revision == nullptr)
248     {
249         error("getDeviceRevision invalid input");
250         co_return false;
251     }
252 
253     uint8_t tbuf[defaultBufferSize] = {0};
254     uint8_t tlen = oneByteLen;
255     uint8_t rbuf[defaultBufferSize] = {0};
256     uint8_t rlen = deviceRevisionLen + 1;
257 
258     tbuf[0] = pmBusDeviceRev;
259     // NOLINTBEGIN(clang-analyzer-core.uninitialized.Branch)
260     if (!(co_await i2cInterface.sendReceive(tbuf, tlen, rbuf, rlen)))
261     // NOLINTEND(clang-analyzer-core.uninitialized.Branch)
262     {
263         error("getDeviceRevision failed with sendreceive");
264         co_return false;
265     }
266 
267     if (mode == gen3Legacy || mode == gen3p5)
268     {
269         std::memcpy(revision, &rbuf[1], deviceRevisionLen);
270     }
271     else
272     {
273         shiftLeftFromLSB(rbuf + 1, revision);
274     }
275 
276     co_return true;
277 }
278 
getCRC(uint32_t * sum)279 sdbusplus::async::task<bool> ISL69269::getCRC(uint32_t* sum)
280 {
281     uint8_t tbuf[defaultBufferSize] = {0};
282     uint8_t rbuf[defaultBufferSize] = {0};
283 
284     switch (generation)
285     {
286         case Gen::Gen2:
287             tbuf[0] = gen2RegCRC;
288             break;
289         case Gen::Gen3p5:
290             tbuf[0] = gen3p5RegCRC;
291             break;
292         default:
293             tbuf[0] = regCRC;
294             break;
295     }
296 
297     if (!(co_await dmaReadWrite(tbuf, rbuf)))
298     {
299         error("getCRC failed");
300         co_return false;
301     }
302     std::memcpy(sum, rbuf, sizeof(uint32_t));
303 
304     co_return true;
305 }
306 
parseImage(const uint8_t * image,size_t imageSize)307 bool ISL69269::parseImage(const uint8_t* image, size_t imageSize)
308 {
309     size_t nextLineStart = 0;
310     int dcnt = 0;
311     const int maxLineLength = 40;
312 
313     for (size_t i = 0; i < imageSize; i++)
314     {
315         if (image[i] == '\n') // We have a hex file, so we check new line.
316         {
317             char line[maxLineLength];
318             char xdigit[8] = {0};
319             uint8_t sepLine[32] = {0};
320 
321             size_t lineLen = i - nextLineStart;
322             if (i > nextLineStart && image[i - 1] == '\r')
323             {
324                 lineLen--;
325             }
326             if (lineLen > maxLineLength)
327             {
328                 error("line length > 40, please check image file.");
329                 return false;
330             }
331             std::memcpy(line, image + nextLineStart, lineLen);
332             int k = 0;
333             size_t j = 0;
334             for (k = 0, j = 0; j < lineLen; k++, j += 2)
335             {
336                 // Convert two chars into a array of single values
337                 std::memcpy(xdigit, &line[j], 2);
338                 sepLine[k] = (uint8_t)std::strtol(xdigit, NULL, 16);
339             }
340 
341             if (sepLine[0] == recordTypeHeader)
342             {
343                 if (sepLine[3] == pmBusDeviceId)
344                 {
345                     shiftLeftFromMSB(sepLine + 4, &configuration.devIdExp);
346                     debug("device id from configuration: {ID}", "ID", lg2::hex,
347                           configuration.devIdExp);
348                     // GEN3p5 IC_DEVICE_ID Byte ID[1]
349                     if (generation == Gen::Gen3p5 && sepLine[6] >= 0xBA)
350                     {
351                         debug("Gen3p5 hex file format recognized");
352                         configuration.mode = gen3p5;
353                     }
354                 }
355                 else if (sepLine[3] == pmBusDeviceRev)
356                 {
357                     shiftLeftFromMSB(sepLine + 4, &configuration.devRevExp);
358                     debug("device revision from config: {ID}", "ID", lg2::hex,
359                           configuration.devRevExp);
360 
361                     if (generation == Gen::Gen3)
362                     {
363                         // According to programing guide:
364                         // If legacy hex file
365                         // MSB device revision == 0x00 | 0x01
366                         if (configuration.devRevExp < (gen3SWRevMin << 24))
367                         {
368                             debug("Legacy hex file format recognized");
369                             configuration.mode = gen3Legacy;
370                         }
371                         else
372                         {
373                             debug("Production hex file format recognized");
374                             configuration.mode = gen3Production;
375                         }
376                     }
377                 }
378                 else if (sepLine[3] == hexFileRev)
379                 {
380                     debug("Gen2 hex file format recognized");
381                     configuration.mode = gen2Hex;
382                 }
383             }
384             else if (sepLine[0] == recordTypeData)
385             {
386                 if (((sepLine[1] + 2) >= (uint8_t)sizeof(sepLine)))
387                 {
388                     dcnt = 0;
389                     break;
390                 }
391                 // According to documentation:
392                 // 00 05 C2 E7 08 00 F6
393                 //  |  |  |  |  |  |  |
394                 //  |  |  |  |  |  |  - Packet Error Code (CRC8)
395                 //  |  |  |  |  -  - Data
396                 //  |  |  |  - Command Code
397                 //  |  |  - Address
398                 //  |  - Size of data (including Addr, Cmd, CRC8)
399                 //  - Line type (0x00 - Data, 0x49 header information)
400                 configuration.pData[dcnt].len = sepLine[1] - 2;
401                 configuration.pData[dcnt].pec =
402                     sepLine[3 + configuration.pData[dcnt].len];
403                 configuration.pData[dcnt].addr = sepLine[2];
404                 configuration.pData[dcnt].cmd = sepLine[3];
405                 std::memcpy(configuration.pData[dcnt].data, sepLine + 2,
406                             configuration.pData[dcnt].len + 1);
407                 switch (dcnt)
408                 {
409                     case cfgId:
410                         if (configuration.mode != gen3p5)
411                         {
412                             configuration.cfgId = sepLine[4] & 0x0F;
413                             debug("Config ID: {ID}", "ID", lg2::hex,
414                                   configuration.cfgId);
415                         }
416                         break;
417                     case gen3p5cfgId:
418                         if (configuration.mode == gen3p5)
419                         {
420                             configuration.cfgId = sepLine[4];
421                             debug("Config ID: {ID}", "ID", lg2::hex,
422                                   configuration.cfgId);
423                         }
424                         break;
425                     case gen3LegacyCRC:
426                         if (configuration.mode == gen3Legacy)
427                         {
428                             std::memcpy(&configuration.crcExp, &sepLine[4],
429                                         checksumLen);
430                             debug("Config Legacy CRC: {CRC}", "CRC", lg2::hex,
431                                   configuration.crcExp);
432                         }
433                         break;
434                     case gen3ProductionCRC:
435                         if (configuration.mode == gen3Production)
436                         {
437                             std::memcpy(&configuration.crcExp, &sepLine[4],
438                                         checksumLen);
439                             debug("Config Production CRC: {CRC}", "CRC",
440                                   lg2::hex, configuration.crcExp);
441                         }
442                         break;
443                     case gen2CRC:
444                         if (configuration.mode == gen2Hex)
445                         {
446                             std::memcpy(&configuration.crcExp, &sepLine[4],
447                                         checksumLen);
448                             debug("Config Gen2 CRC: {CRC}", "CRC", lg2::hex,
449                                   configuration.crcExp);
450                         }
451                         break;
452                     case gen3p5CRC:
453                         if (configuration.mode == gen3p5)
454                         {
455                             std::memcpy(&configuration.crcExp, &sepLine[4],
456                                         checksumLen);
457                             debug("Config Gen3p5 CRC: {CRC}", "CRC", lg2::hex,
458                                   configuration.crcExp);
459                         }
460                         break;
461                 }
462                 dcnt++;
463             }
464             else
465             {
466                 error("parseImage failed. Unknown recordType");
467                 return false;
468             }
469 
470             nextLineStart = i + 1;
471         }
472     }
473     configuration.wrCnt = dcnt;
474     return true;
475 }
476 
checkImage()477 bool ISL69269::checkImage()
478 {
479     uint8_t crc8 = 0;
480 
481     for (int i = 0; i < configuration.wrCnt; i++)
482     {
483         crc8 = calcCRC8(configuration.pData[i].data,
484                         configuration.pData[i].len + 1);
485         if (crc8 != configuration.pData[i].pec)
486         {
487             debug(
488                 "Config line: {LINE}, failed to calculate CRC. Have {HAVE}, Want: {WANT}",
489                 "LINE", i, "HAVE", lg2::hex, crc8, "WANT", lg2::hex,
490                 configuration.pData[i].pec);
491             return false;
492         }
493     }
494 
495     return true;
496 }
497 
program()498 sdbusplus::async::task<bool> ISL69269::program()
499 {
500     uint8_t tbuf[programBufferSize] = {0};
501     uint8_t rbuf[programBufferSize] = {0};
502     uint8_t rlen = zeroByteLen;
503 
504     for (int i = 0; i < configuration.wrCnt; i++)
505     {
506         std::memcpy(tbuf, configuration.pData[i].data + 1,
507                     configuration.pData[i].len);
508 
509         if (!(co_await i2cInterface.sendReceive(
510                 tbuf, configuration.pData[i].len, rbuf, rlen)))
511         {
512             error("program failed at writing data to voltage regulator");
513         }
514     }
515 
516     if (!(co_await getProgStatus()))
517     {
518         error("program failed at getProgStatus");
519         co_return false;
520     }
521 
522     co_return true;
523 }
524 
getProgStatus()525 sdbusplus::async::task<bool> ISL69269::getProgStatus()
526 {
527     uint8_t tbuf[programBufferSize] = {0};
528     uint8_t rbuf[programBufferSize] = {0};
529     int retry = 3;
530 
531     if (generation == Gen::Gen2)
532     {
533         tbuf[0] = gen2RegProgStatus;
534         tbuf[1] = gen2RegProgStatus;
535     }
536     else if (generation == Gen::Gen3p5)
537     {
538         tbuf[0] = regGen3p5ProgStatus;
539         tbuf[1] = 0x00;
540     }
541     else
542     {
543         tbuf[0] = regProgStatus;
544         tbuf[1] = 0x00;
545     }
546 
547     do
548     {
549         // NOLINTBEGIN(clang-analyzer-core.uninitialized.Branch)
550         if (!(co_await dmaReadWrite(tbuf, rbuf)))
551         // NOLINTEND(clang-analyzer-core.uninitialized.Branch)
552         {
553             error("getProgStatus failed on dmaReadWrite");
554             co_return false;
555         }
556 
557         if (rbuf[0] & 0x01)
558         {
559             debug("Programming successful");
560             break;
561         }
562         if (--retry == 0)
563         {
564             if ((!(rbuf[1] & 0x1)) || (rbuf[1] & 0x2))
565             {
566                 error("programming the device failed");
567             }
568             if (!(rbuf[1] & 0x4))
569             {
570                 error(
571                     "HEX file contains more configurations than are available");
572             }
573             if (!(rbuf[1] & 0x8))
574             {
575                 error(
576                     "A CRC mismatch exists within the configuration data. Programming failed before  TP banks are consumed");
577             }
578             if (!(rbuf[1] & 0x10))
579             {
580                 error(
581                     "CRC check fails on the OTP memory. Programming fails after OTP banks are consumed");
582             }
583             if (!(rbuf[1] & 0x20))
584             {
585                 error("Programming fails after OTP banks are consumed.");
586             }
587 
588             error("failed to program the device after exceeding retries");
589             co_return false;
590         }
591         co_await sdbusplus::async::sleep_for(ctx, std::chrono::seconds(1));
592     } while (retry > 0);
593 
594     co_return true;
595 }
596 
restoreCfg()597 sdbusplus::async::task<bool> ISL69269::restoreCfg()
598 {
599     uint8_t tbuf[defaultBufferSize] = {0};
600     uint8_t rbuf[defaultBufferSize] = {0};
601 
602     tbuf[0] = regRestoreCfg;
603     tbuf[1] = configuration.cfgId;
604 
605     debug("Restore configuration ID: {ID}", "ID", lg2::hex,
606           configuration.cfgId);
607 
608     if (!(co_await dmaReadWrite(tbuf, rbuf)))
609     {
610         error("restoreCfg failed at dmaReadWrite");
611         co_return false;
612     }
613 
614     co_return true;
615 }
616 
verifyImage(const uint8_t * image,size_t imageSize)617 sdbusplus::async::task<bool> ISL69269::verifyImage(const uint8_t* image,
618                                                    size_t imageSize)
619 {
620     uint8_t mode = 0xFF;
621     uint8_t remain = 0;
622     uint32_t devID = 0;
623     uint32_t devRev = 0;
624     uint32_t crc = 0;
625 
626     if (!(co_await getHexMode(&mode)))
627     {
628         error("program failed at getHexMode");
629         co_return false;
630     }
631 
632     if (!parseImage(image, imageSize))
633     {
634         error("verifyImage failed at parseImage");
635         co_return false;
636     }
637 
638     if (!checkImage())
639     {
640         error("verifyImage failed at checkImage");
641         co_return false;
642     }
643 
644     if (mode != configuration.mode)
645     {
646         error(
647             "program failed with mode of device and configuration are not equal");
648         co_return false;
649     }
650 
651     if (!(co_await getRemainingWrites(&remain)))
652     {
653         error("program failed at getRemainingWrites");
654         co_return false;
655     }
656 
657     if (!remain)
658     {
659         error("program failed with no remaining writes left on device");
660         co_return false;
661     }
662 
663     if (!(co_await getDeviceId(&devID)))
664     {
665         error("program failed at getDeviceId");
666         co_return false;
667     }
668 
669     if (devID != configuration.devIdExp)
670     {
671         error(
672             "program failed with not matching device id of device and config");
673         co_return false;
674     }
675 
676     if (!(co_await getDeviceRevision(&devRev)))
677     {
678         error("program failed at getDeviceRevision");
679         co_return false;
680     }
681     debug("Device revision read from device: {REV}", "REV", lg2::hex, devRev);
682 
683     switch (mode)
684     {
685         case gen3Legacy:
686             if (((devRev >> 24) >= gen3SWRevMin) &&
687                 (configuration.devRevExp <= 0x1))
688             {
689                 debug("Legacy mode revision checks out");
690             }
691             else
692             {
693                 error(
694                     "revision requirements for legacy mode device not fulfilled");
695             }
696             break;
697         case gen3Production:
698             if (((devRev >> 24) >= gen3SWRevMin) &&
699                 (configuration.devRevExp >= gen3SWRevMin))
700             {
701                 debug("Production mode revision checks out");
702             }
703             else
704             {
705                 error(
706                     "revision requirements for production mode device not fulfilled");
707             }
708             break;
709         case gen2Hex:
710             if (devRev >= gen2RevMin)
711             {
712                 debug("Gen2 mode revision checks out");
713             }
714             else
715             {
716                 error("revision requirements for Gen2 device not fulfilled");
717             }
718             break;
719         case gen3p5:
720             if (((devRev >> 24) >= gen3p5HWRevMin) &&
721                 ((devRev >> 24) <= gen3p5HWRevMax))
722             {
723                 debug("Gen3p5 revision checks out");
724             }
725             else
726             {
727                 error("revision requirements for Gen3p5 device not fulfilled");
728             }
729             break;
730     }
731 
732     if (!(co_await getCRC(&crc)))
733     {
734         error("program failed at getCRC");
735         co_return false;
736     }
737 
738     debug("CRC from device: {CRC}", "CRC", lg2::hex, crc);
739     debug("CRC from config: {CRC}", "CRC", lg2::hex, configuration.crcExp);
740 
741     if (crc == configuration.crcExp)
742     {
743         error("program failed with same CRC value at device and configuration");
744         co_return false;
745     }
746 
747     co_return true;
748 }
749 
forcedUpdateAllowed()750 bool ISL69269::forcedUpdateAllowed()
751 {
752     return true;
753 }
754 
updateFirmware(bool force)755 sdbusplus::async::task<bool> ISL69269::updateFirmware(bool force)
756 {
757     (void)force;
758     // NOLINTBEGIN(clang-analyzer-core.uninitialized.Branch)
759     if (!(co_await program()))
760     // NOLINTEND(clang-analyzer-core.uninitialized.Branch)
761     {
762         error("programing ISL69269 failed");
763         co_return false;
764     }
765 
766     co_return true;
767 }
768 
769 } // namespace phosphor::software::VR
770