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
31 constexpr uint16_t cfgId = 7;
32 constexpr uint16_t gen3FileHead = 5;
33 constexpr uint16_t gen3LegacyCRC = 276 - gen3FileHead;
34 constexpr uint16_t gen3ProductionCRC = 290 - gen3FileHead;
35 constexpr uint8_t checksumLen = 4;
36 constexpr uint8_t deviceRevisionLen = 4;
37
38 // Common pmBus Command codes
39 constexpr uint8_t pmBusDeviceId = 0xAD;
40 constexpr uint8_t pmBusDeviceRev = 0xAE;
41
42 // Config file constants
43 constexpr char recordTypeData = 0x00;
44 constexpr char recordTypeHeader = 0x49;
45
46 constexpr uint8_t defaultBufferSize = 16;
47 constexpr uint8_t programBufferSize = 32;
48
49 constexpr uint8_t zeroByteLen = 0;
50 constexpr uint8_t oneByteLen = 1;
51 constexpr uint8_t threeByteLen = 3;
52 constexpr uint8_t fourByteLen = 4;
53
54 // RAA Gen2
55 constexpr uint8_t gen2RegProgStatus = 0x07;
56 constexpr uint8_t gen2RegCRC = 0x3F;
57 constexpr uint8_t gen2RegRemainginWrites = 0xC2;
58
59 constexpr uint32_t gen2RevMin = 0x02000003;
60
61 constexpr uint8_t hexFileRev = 0x00;
62 constexpr uint16_t gen2FileHead = 6;
63 constexpr uint16_t gen2CRC = 600 - gen2FileHead;
64
ISL69269(sdbusplus::async::context & ctx,uint16_t bus,uint16_t address,Gen gen)65 ISL69269::ISL69269(sdbusplus::async::context& ctx, uint16_t bus,
66 uint16_t address, Gen gen) :
67 VoltageRegulator(ctx), i2cInterface(phosphor::i2c::I2C(bus, address)),
68 generation(gen)
69 {}
70
shiftLeftFromLSB(const uint8_t * data,uint32_t * result)71 inline 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
shiftLeftFromMSB(const uint8_t * data,uint32_t * result)79 inline 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
calcCRC8(const uint8_t * data,uint8_t len)87 static 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
dmaReadWrite(uint8_t * reg,uint8_t * resp)112 sdbusplus::async::task<bool> ISL69269::dmaReadWrite(uint8_t* reg, uint8_t* resp)
113 {
114 if (reg == nullptr || resp == nullptr)
115 {
116 error("dmaReadWrite invalid input");
117 co_return false;
118 }
119
120 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
getRemainingWrites(uint8_t * remain)151 sdbusplus::async::task<bool> ISL69269::getRemainingWrites(uint8_t* remain)
152 {
153 uint8_t tbuf[defaultBufferSize] = {0};
154 uint8_t rbuf[defaultBufferSize] = {0};
155
156 tbuf[0] =
157 (generation == Gen::Gen2) ? gen2RegRemainginWrites : regRemainginWrites;
158 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
getHexMode(uint8_t * mode)169 sdbusplus::async::task<bool> ISL69269::getHexMode(uint8_t* mode)
170 {
171 if (generation == Gen::Gen2)
172 {
173 *mode = gen2Hex;
174 co_return true;
175 }
176
177 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
getDeviceId(uint32_t * deviceId)193 sdbusplus::async::task<bool> ISL69269::getDeviceId(uint32_t* deviceId)
194 {
195 if (deviceId == nullptr)
196 {
197 error("getDeviceId invalid input");
198 co_return false;
199 }
200
201 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
getDeviceRevision(uint32_t * revision)219 sdbusplus::async::task<bool> ISL69269::getDeviceRevision(uint32_t* revision)
220 {
221 if (revision == nullptr)
222 {
223 error("getDeviceRevision 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 = deviceRevisionLen + 1;
231
232 tbuf[0] = pmBusDeviceRev;
233 // NOLINTBEGIN(clang-analyzer-core.uninitialized.Branch)
234 if (!(co_await i2cInterface.sendReceive(tbuf, tlen, rbuf, rlen)))
235 // NOLINTEND(clang-analyzer-core.uninitialized.Branch)
236 {
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
getCRC(uint32_t * sum)253 sdbusplus::async::task<bool> ISL69269::getCRC(uint32_t* sum)
254 {
255 uint8_t tbuf[defaultBufferSize] = {0};
256 uint8_t rbuf[defaultBufferSize] = {0};
257
258 tbuf[0] = (generation == Gen::Gen2) ? gen2RegCRC : regCRC;
259 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
parseImage(const uint8_t * image,size_t imageSize)269 bool 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
307 if (generation != Gen::Gen2)
308 {
309 // 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 }
322 }
323 }
324 else if (sepLine[3] == hexFileRev)
325 {
326 debug("Gen2 hex file format recognized");
327 configuration.mode = gen2Hex;
328 }
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;
378 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;
387 }
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
checkImage()403 bool 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
program()424 sdbusplus::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 {
432 std::memcpy(tbuf, configuration.pData[i].data + 1,
433 configuration.pData[i].len);
434
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
442 if (!(co_await getProgStatus()))
443 {
444 error("program failed at getProgStatus");
445 co_return false;
446 }
447
448 co_return true;
449 }
450
getProgStatus()451 sdbusplus::async::task<bool> ISL69269::getProgStatus()
452 {
453 uint8_t tbuf[programBufferSize] = {0};
454 uint8_t rbuf[programBufferSize] = {0};
455 int retry = 3;
456
457 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 }
467
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 {
480 debug("Programming successful");
481 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
restoreCfg()518 sdbusplus::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
526 debug("Restore configuration ID: {ID}", "ID", lg2::hex,
527 configuration.cfgId);
528
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
verifyImage(const uint8_t * image,size_t imageSize)538 sdbusplus::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;
630 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;
640 }
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
forcedUpdateAllowed()660 bool ISL69269::forcedUpdateAllowed()
661 {
662 return true;
663 }
664
updateFirmware(bool force)665 sdbusplus::async::task<bool> ISL69269::updateFirmware(bool force)
666 {
667 (void)force;
668 // NOLINTBEGIN(clang-analyzer-core.uninitialized.Branch)
669 if (!(co_await program()))
670 // NOLINTEND(clang-analyzer-core.uninitialized.Branch)
671 {
672 error("programing ISL69269 failed");
673 co_return false;
674 }
675
676 co_return true;
677 }
678
679 } // namespace phosphor::software::VR
680