blob: 42fb925166620d029caaea47f850bb8d67c76863 [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 {
393 tbuf[0] = configuration.pData[i].cmd;
394 std::memcpy(tbuf + 1, &configuration.pData[i].data,
395 configuration.pData[i].len - 1);
396
397 if (!(co_await i2cInterface.sendReceive(
398 tbuf, configuration.pData[i].len, rbuf, rlen)))
399 {
400 error("program failed at writing data to voltage regulator");
401 }
402 }
403
Kevin Tungee551172025-08-22 16:49:40 +0800404 if (!(co_await getProgStatus()))
405 {
406 error("program failed at getProgStatus");
407 co_return false;
408 }
409
Christopher Meisf00ce802025-04-08 08:07:31 +0200410 co_return true;
411}
412
413sdbusplus::async::task<bool> ISL69269::getProgStatus()
414{
415 uint8_t tbuf[programBufferSize] = {0};
416 uint8_t rbuf[programBufferSize] = {0};
417 int retry = 3;
418
419 tbuf[0] = regProgStatus;
420 tbuf[1] = 0x00;
421
422 do
423 {
424 // NOLINTBEGIN(clang-analyzer-core.uninitialized.Branch)
425 if (!(co_await dmaReadWrite(tbuf, rbuf)))
426 // NOLINTEND(clang-analyzer-core.uninitialized.Branch)
427 {
428 error("getProgStatus failed on dmaReadWrite");
429 co_return false;
430 }
431
432 if (rbuf[0] & 0x01)
433 {
434 debug("Programming succesful");
435 break;
436 }
437 if (--retry == 0)
438 {
439 if ((!(rbuf[1] & 0x1)) || (rbuf[1] & 0x2))
440 {
441 error("programming the device failed");
442 }
443 if (!(rbuf[1] & 0x4))
444 {
445 error(
446 "HEX file contains more configurations than are available");
447 }
448 if (!(rbuf[1] & 0x8))
449 {
450 error(
451 "A CRC mismatch exists within the configuration data. Programming failed before TP banks are consumed");
452 }
453 if (!(rbuf[1] & 0x10))
454 {
455 error(
456 "CRC check fails on the OTP memory. Programming fails after OTP banks are consumed");
457 }
458 if (!(rbuf[1] & 0x20))
459 {
460 error("Programming fails after OTP banks are consumed.");
461 }
462
463 error("failed to program the device after exceeding retries");
464 co_return false;
465 }
466 co_await sdbusplus::async::sleep_for(ctx, std::chrono::seconds(1));
467 } while (retry > 0);
468
469 co_return true;
470}
471
472sdbusplus::async::task<bool> ISL69269::restoreCfg()
473{
474 uint8_t tbuf[defaultBufferSize] = {0};
475 uint8_t rbuf[defaultBufferSize] = {0};
476
477 tbuf[0] = regRestoreCfg;
478 tbuf[1] = configuration.cfgId;
479
480 debug("Restore configurtion ID: {ID}", "ID", lg2::hex, configuration.cfgId);
481
482 if (!(co_await dmaReadWrite(tbuf, rbuf)))
483 {
484 error("restoreCfg failed at dmaReadWrite");
485 co_return false;
486 }
487
488 co_return true;
489}
490
491sdbusplus::async::task<bool> ISL69269::verifyImage(const uint8_t* image,
492 size_t imageSize)
493{
494 uint8_t mode = 0xFF;
495 uint8_t remain = 0;
496 uint32_t devID = 0;
497 uint32_t devRev = 0;
498 uint32_t crc = 0;
499
500 if (!(co_await getHexMode(&mode)))
501 {
502 error("program failed at getHexMode");
503 co_return false;
504 }
505
506 if (!parseImage(image, imageSize))
507 {
508 error("verifyImage failed at parseImage");
509 co_return false;
510 }
511
512 if (!checkImage())
513 {
514 error("verifyImage failed at checkImage");
515 co_return false;
516 }
517
518 if (mode != configuration.mode)
519 {
520 error(
521 "program failed with mode of device and configuration are not equal");
522 co_return false;
523 }
524
525 if (!(co_await getRemainingWrites(&remain)))
526 {
527 error("program failed at getRemainingWrites");
528 co_return false;
529 }
530
531 if (!remain)
532 {
533 error("program failed with no remaining writes left on device");
534 co_return false;
535 }
536
537 if (!(co_await getDeviceId(&devID)))
538 {
539 error("program failed at getDeviceId");
540 co_return false;
541 }
542
543 if (devID != configuration.devIdExp)
544 {
545 error(
546 "program failed with not matching device id of device and config");
547 co_return false;
548 }
549
550 if (!(co_await getDeviceRevision(&devRev)))
551 {
552 error("program failed at getDeviceRevision");
553 co_return false;
554 }
555 debug("Device revision read from device: {REV}", "REV", lg2::hex, devRev);
556
557 switch (mode)
558 {
559 case gen3Legacy:
560 if (((devRev >> 24) >= gen3SWRevMin) &&
561 (configuration.devRevExp <= 0x1))
562 {
563 debug("Legacy mode revision checks out");
564 }
565 else
566 {
567 error(
568 "revision requirements for legacy mode device not fulfilled");
569 }
570 break;
571 case gen3Production:
572 if (((devRev >> 24) >= gen3SWRevMin) &&
573 (configuration.devRevExp >= gen3SWRevMin))
574 {
575 debug("Production mode revision checks out");
576 }
577 else
578 {
579 error(
580 "revision requirements for production mode device not fulfilled");
581 }
582 break;
583 }
584
585 if (!(co_await getCRC(&crc)))
586 {
587 error("program failed at getCRC");
588 co_return false;
589 }
590
591 debug("CRC from device: {CRC}", "CRC", lg2::hex, crc);
592 debug("CRC from config: {CRC}", "CRC", lg2::hex, configuration.crcExp);
593
594 if (crc == configuration.crcExp)
595 {
596 error("program failed with same CRC value at device and configuration");
597 co_return false;
598 }
599
600 co_return true;
601}
602
Christopher Meisf00ce802025-04-08 08:07:31 +0200603bool ISL69269::forcedUpdateAllowed()
604{
605 return true;
606}
607
608sdbusplus::async::task<bool> ISL69269::updateFirmware(bool force)
609{
610 (void)force;
Kevin Tungee551172025-08-22 16:49:40 +0800611 // NOLINTBEGIN(clang-analyzer-core.uninitialized.Branch)
Christopher Meisf00ce802025-04-08 08:07:31 +0200612 if (!(co_await program()))
Kevin Tungee551172025-08-22 16:49:40 +0800613 // NOLINTEND(clang-analyzer-core.uninitialized.Branch)
Christopher Meisf00ce802025-04-08 08:07:31 +0200614 {
615 error("programing ISL69269 failed");
616 co_return false;
617 }
618
619 co_return true;
620}
621
622} // namespace phosphor::software::VR