xref: /openbmc/phosphor-bmc-code-mgmt/i2c-vr/isl69269/isl69269.cpp (revision 86a2fd040a4a4184ad07be94eefba92ffb46561a)
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