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