xref: /openbmc/phosphor-bmc-code-mgmt/i2c-vr/isl69269/isl69269.cpp (revision 9ce83ded4fb60a348277158b66258fa27d505462)
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     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 
132 sdbusplus::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 
149 sdbusplus::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 
167 sdbusplus::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 
187 sdbusplus::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 
213 sdbusplus::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 
229 bool 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 
346 bool 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 
367 sdbusplus::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 
389 sdbusplus::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 
448 sdbusplus::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 
467 sdbusplus::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 
579 sdbusplus::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 
619 bool ISL69269::forcedUpdateAllowed()
620 {
621     return true;
622 }
623 
624 sdbusplus::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
637