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