xref: /openbmc/phosphor-bmc-code-mgmt/i2c-vr/isl69269/isl69269.cpp (revision 975fbbeb3f11e01a2ba4bcad5213935019e2568b)
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 
30 constexpr uint16_t cfgId = 7;
31 constexpr uint16_t gen3FileHead = 5;
32 constexpr uint16_t gen3LegacyCRC = 276 - gen3FileHead;
33 constexpr uint16_t gen3ProductionCRC = 290 - gen3FileHead;
34 constexpr uint8_t checksumLen = 4;
35 constexpr uint8_t deviceRevisionLen = 4;
36 
37 // Common pmBus Command codes
38 constexpr uint8_t pmBusDeviceId = 0xAD;
39 constexpr uint8_t pmBusDeviceRev = 0xAE;
40 
41 // Config file constants
42 constexpr char recordTypeData = 0x00;
43 constexpr char recordTypeHeader = 0x49;
44 
45 constexpr uint8_t defaultBufferSize = 16;
46 constexpr uint8_t programBufferSize = 32;
47 
48 constexpr uint8_t zeroByteLen = 0;
49 constexpr uint8_t oneByteLen = 1;
50 constexpr uint8_t threeByteLen = 3;
51 constexpr uint8_t fourByteLen = 4;
52 
53 ISL69269::ISL69269(sdbusplus::async::context& ctx, uint16_t bus,
54                    uint16_t address) :
55     VoltageRegulator(ctx), i2cInterface(phosphor::i2c::I2C(bus, address))
56 {}
57 
58 inline 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 
66 inline 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 
74 static 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 
99 sdbusplus::async::task<bool> ISL69269::dmaReadWrite(uint8_t* reg, uint8_t* resp)
100 {
101     if (reg == nullptr || resp == nullptr)
102     {
103         error("dmaReadWrite invalid input");
104         co_return false;
105     }
106 
107     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 
138 sdbusplus::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 
155 sdbusplus::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 
173 sdbusplus::async::task<bool> ISL69269::getDeviceId(uint32_t* deviceId)
174 {
175     if (deviceId == nullptr)
176     {
177         error("getDeviceId invalid input");
178         co_return false;
179     }
180 
181     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 
199 sdbusplus::async::task<bool> ISL69269::getDeviceRevision(uint32_t* revision)
200 {
201     if (revision == nullptr)
202     {
203         error("getDeviceRevision invalid input");
204         co_return false;
205     }
206 
207     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 
231 sdbusplus::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 
247 bool 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 
364 bool 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 
385 sdbusplus::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 
404     if (!(co_await getProgStatus()))
405     {
406         error("program failed at getProgStatus");
407         co_return false;
408     }
409 
410     co_return true;
411 }
412 
413 sdbusplus::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 
472 sdbusplus::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 
491 sdbusplus::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 
603 bool ISL69269::forcedUpdateAllowed()
604 {
605     return true;
606 }
607 
608 sdbusplus::async::task<bool> ISL69269::updateFirmware(bool force)
609 {
610     (void)force;
611     // NOLINTBEGIN(clang-analyzer-core.uninitialized.Branch)
612     if (!(co_await program()))
613     // NOLINTEND(clang-analyzer-core.uninitialized.Branch)
614     {
615         error("programing ISL69269 failed");
616         co_return false;
617     }
618 
619     co_return true;
620 }
621 
622 } // namespace phosphor::software::VR
623