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