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