xref: /openbmc/phosphor-bmc-code-mgmt/i2c-vr/xdpe1x2xx/xdpe1x2xx.cpp (revision 7e446a407a579ec72f48009008ad2f83c01c2262)
1 #include "xdpe1x2xx.hpp"
2 
3 #include "common/include/i2c/i2c.hpp"
4 
5 #include <unistd.h>
6 
7 #include <phosphor-logging/lg2.hpp>
8 
9 #include <cstdio>
10 
11 #define REMAINING_TIMES(x, y) (((((x)[1]) << 8) | ((x)[0])) / (y))
12 
13 PHOSPHOR_LOG2_USING;
14 
15 namespace phosphor::software::VR
16 {
17 
18 const uint32_t CRC32Poly = 0xEDB88320;
19 const int VRResetDelay = 500000;
20 
21 enum RevisionCode
22 {
23     REV_A = 0x00,
24     REV_B,
25     REV_C,
26     REV_D,
27 };
28 
29 enum ProductID
30 {
31     ProductIDXDPE15284 = 0x8A,
32     ProductIDXDPE19283 = 0x95,
33 };
34 
35 const uint32_t PMBusICDeviceID = 0xAD;
36 const uint32_t PMBusSTLCml = 0x7E;
37 const uint32_t IFXICDeviceIDLen = 2;
38 const uint32_t IFXMFRAHBAddr = 0xCE;
39 const uint32_t IFXMFRRegWrite = 0xDE;
40 const uint32_t IFXMFRFwCmdData = 0xFD;
41 const uint32_t IFXMFRFwCmd = 0xFE;
42 const uint32_t MFRFwCmdReset = 0x0e;
43 const uint32_t MFRFwCmdRmng = 0x10;
44 const uint32_t MFRFwCmdOTPConfSTO = 0x11;
45 const uint32_t MFRFwCmdOTPFileInvd = 0x12;
46 const uint32_t MFRFwCmdGetCRC = 0x2D;
47 const int XDPE15284CConfSize = 1344;
48 const int XDPE19283BConfSize = 1416;
49 const uint32_t VRWarnRemaining = 3;
50 const uint32_t SectTrim = 0x02;
51 
52 const char* const AddressField = "PMBus Address :";
53 const char* const ChecksumField = "Checksum :";
54 const char* const DataStartTag = "Configuration Data]";
55 const char* const DataEndTag = "[End Configuration Data]";
56 const char* const DataComment = "//";
57 const char* const DataXV = "XV";
58 
59 XDPE1X2XX::XDPE1X2XX(sdbusplus::async::context& ctx, uint16_t bus,
60                      uint16_t address) :
61     VoltageRegulator(ctx), i2cInterface(phosphor::i2c::I2C(bus, address))
62 {}
63 
64 // NOLINTBEGIN(readability-static-accessed-through-instance)
65 sdbusplus::async::task<bool> XDPE1X2XX::getDeviceId(uint8_t* deviceID)
66 // NOLINTEND(readability-static-accessed-through-instance)
67 {
68     bool ret = false;
69     uint8_t tbuf[16] = {0};
70     tbuf[0] = PMBusICDeviceID;
71     tbuf[1] = 2;
72     uint8_t tSize = 1;
73     uint8_t rbuf[16] = {0};
74     uint8_t rSize = IFXICDeviceIDLen + 1;
75 
76     ret = co_await this->i2cInterface.sendReceive(tbuf, tSize, rbuf, rSize);
77     if (!ret)
78     {
79         error("Failed to get device ID");
80         co_return false;
81     }
82 
83     std::memcpy(deviceID, &rbuf[1], IFXICDeviceIDLen);
84 
85     co_return true;
86 }
87 
88 // NOLINTBEGIN(readability-static-accessed-through-instance)
89 sdbusplus::async::task<bool> XDPE1X2XX::mfrFWcmd(uint8_t cmd, uint8_t* data,
90                                                  uint8_t* resp)
91 // NOLINTEND(readability-static-accessed-through-instance)
92 {
93     bool ret = false;
94     uint8_t tBuf[16] = {0};
95     uint8_t rBuf[16] = {0};
96     uint8_t tSize = 0;
97     uint8_t rSize = 0;
98 
99     if (data)
100     {
101         tBuf[0] = IFXMFRFwCmdData;
102         tBuf[1] = 4; // Block write 4 bytes
103         tSize = 6;
104         std::memcpy(&tBuf[2], data, 4);
105         ret = co_await this->i2cInterface.sendReceive(tBuf, tSize, rBuf, rSize);
106         if (!ret)
107         {
108             error("Failed to send MFR command: {CMD}", "CMD",
109                   std::string("IFXMFRFwCmdDAta"));
110             co_return false;
111         }
112     }
113 
114     co_await sdbusplus::async::sleep_for(ctx, std::chrono::microseconds(300));
115 
116     tBuf[0] = IFXMFRFwCmd;
117     tBuf[1] = cmd;
118     tSize = 2;
119     rSize = 0;
120     ret = co_await this->i2cInterface.sendReceive(tBuf, tSize, rBuf, rSize);
121     if (!ret)
122     {
123         error("Failed to send MFR command: {CMD}", "CMD",
124               std::string("IFXMFRFwCmd"));
125         co_return false;
126     }
127 
128     co_await sdbusplus::async::sleep_for(ctx, std::chrono::microseconds(20000));
129 
130     if (resp)
131     {
132         tBuf[0] = IFXMFRFwCmdData;
133         tSize = 1;
134         rSize = 6;
135         ret = co_await this->i2cInterface.sendReceive(tBuf, tSize, rBuf, rSize);
136         if (!ret)
137         {
138             error("Failed to send MFR command: {CMD}", "CMD",
139                   std::string("IFXMFRFwCmdData"));
140             co_return false;
141         }
142         if (rBuf[0] != 4)
143         {
144             error(
145                 "Failed to receive MFR response with unexpected response size");
146             co_return false;
147         }
148         std::memcpy(resp, rBuf + 1, 4);
149     }
150 
151     co_return true;
152 }
153 
154 // NOLINTBEGIN(readability-static-accessed-through-instance)
155 sdbusplus::async::task<bool> XDPE1X2XX::getRemainingWrites(uint8_t* remain)
156 // NOLINTEND(readability-static-accessed-through-instance)
157 {
158     bool ret = false;
159     uint8_t tBuf[16] = {0};
160     uint8_t rBuf[16] = {0};
161     uint8_t devId[2] = {0};
162 
163     ret = co_await this->mfrFWcmd(MFRFwCmdRmng, tBuf, rBuf);
164     if (!ret)
165     {
166         error("Failed to request remaining writes");
167         co_return false;
168     }
169 
170     ret = co_await this->getDeviceId(devId);
171     if (!ret)
172     {
173         error("Failed to request device ID for remaining writes");
174         co_return false;
175     }
176 
177     int configSize = getConfigSize(devId[1], devId[0]);
178     if (configSize < 0)
179     {
180         error("Failed to request valid configuration size");
181         co_return false;
182     }
183 
184     *remain = REMAINING_TIMES(rBuf, configSize);
185 
186     co_return 0;
187 }
188 
189 int XDPE1X2XX::getConfigSize(uint8_t deviceId, uint8_t revision)
190 {
191     int size = -1;
192 
193     switch (deviceId)
194     {
195         case ProductIDXDPE19283:
196             if (revision == REV_B)
197             {
198                 size = XDPE19283BConfSize;
199             }
200             break;
201         case ProductIDXDPE15284:
202             size = XDPE15284CConfSize;
203             break;
204         default:
205             error(
206                 "Failed to get configuration size of {DEVID} with revision {REV}",
207                 "DEVID", deviceId, "REV", revision);
208             return -1;
209     }
210 
211     return size;
212 }
213 
214 // NOLINTBEGIN(readability-static-accessed-through-instance)
215 sdbusplus::async::task<bool> XDPE1X2XX::getCRC(uint32_t* checksum)
216 // NOLINTEND(readability-static-accessed-through-instance)
217 {
218     uint8_t tBuf[16] = {0};
219     uint8_t rBuf[16] = {0};
220 
221     bool ret = co_await this->mfrFWcmd(MFRFwCmdGetCRC, tBuf, rBuf);
222     if (!ret)
223     {
224         error("Failed to get CRC value");
225         co_return false;
226     }
227 
228     *checksum = (static_cast<uint32_t>(rBuf[3]) << 24) |
229                 (static_cast<uint32_t>(rBuf[2]) << 16) |
230                 (static_cast<uint32_t>(rBuf[1]) << 8) |
231                 (static_cast<uint32_t>(rBuf[0]));
232 
233     co_return true;
234 }
235 
236 // NOLINTBEGIN(readability-static-accessed-through-instance)
237 sdbusplus::async::task<bool> XDPE1X2XX::program(bool force)
238 // NOLINTEND(readability-static-accessed-through-instance)
239 
240 {
241     bool ret = false;
242     uint8_t tBuf[16] = {0};
243     uint8_t rBuf[16] = {0};
244     uint8_t remain = 0;
245     uint32_t sum = 0;
246     int size = 0;
247 
248     // NOLINTBEGIN(clang-analyzer-core.uninitialized.Branch)
249     ret = co_await getCRC(&sum);
250     // NOLINTEND(clang-analyzer-core.uninitialized.Branch)
251     if (!ret)
252     {
253         error("Failed to program the VR");
254         co_return -1;
255     }
256 
257     if (!force && (sum == configuration.sumExp))
258     {
259         error("Failed to program the VR - CRC value are equal with no force");
260         co_return -1;
261     }
262 
263     // NOLINTBEGIN(clang-analyzer-core.uninitialized.Branch)
264     ret = co_await this->getRemainingWrites(&remain);
265     // NOLINTEND(clang-analyzer-core.uninitialized.Branch)
266     if (!ret)
267     {
268         error("Failed to program the VR - unable to obtain remaing writes");
269         co_return -1;
270     }
271 
272     if (!remain)
273     {
274         error("Failed to program the VR - no remaining write cycles left");
275         co_return -1;
276     }
277 
278     if (!force && (remain <= VRWarnRemaining))
279     {
280         error(
281             "Failed to program the VR - {REMAIN} remaining writes left and not force",
282             "REMAIN", remain);
283         co_return -1;
284     }
285 
286     // Added reprogramming of the entire configuration file.
287     // Except for the trim section, all other data will be replaced.
288     // 0xfe 0xfe 0x00 0x00 instructs the command to reprogram all header codes
289     // and XVcode. If the old sections are not invalidated in OTP, they can
290     // affect the CRC calculation.
291 
292     tBuf[0] = 0xfe;
293     tBuf[1] = 0xfe;
294     tBuf[2] = 0x00;
295     tBuf[3] = 0x00;
296 
297     ret = co_await this->mfrFWcmd(MFRFwCmdOTPFileInvd, tBuf, NULL);
298     if (!ret)
299     {
300         error("Failed to program the VR - Invalidation of currect FW");
301         co_return ret;
302     }
303 
304     co_await sdbusplus::async::sleep_for(ctx,
305                                          std::chrono::microseconds(500000));
306 
307     for (int i = 0; i < configuration.sectCnt; i++)
308     {
309         struct configSect* sect = &configuration.section[i];
310         if (sect == NULL)
311         {
312             error(
313                 "Failed to program the VR - unexpected NULL section in config");
314             ret = -1;
315             break;
316         }
317 
318         if ((i <= 0) || (sect->type != configuration.section[i - 1].type))
319         {
320             tBuf[0] = PMBusSTLCml;
321             tBuf[1] = 0x1;
322             uint8_t tSize = 2;
323             uint8_t rSize = 0;
324             ret = co_await this->i2cInterface.sendReceive(tBuf, tSize, rBuf,
325                                                           rSize);
326             if (!ret)
327             {
328                 error("Failed to program the VR on sendReceive {CMD}", "CMD",
329                       std::string("PMBusSTLCml"));
330                 break;
331             }
332 
333             tBuf[0] = sect->type;
334             tBuf[1] = 0x00;
335             tBuf[2] = 0x00;
336             tBuf[3] = 0x00;
337 
338             ret = co_await this->mfrFWcmd(MFRFwCmdOTPFileInvd, tBuf, NULL);
339             if (!ret)
340             {
341                 error("Failed to program VR on mfrFWCmd on {CMD}", "CMD",
342                       std::string("MFRFwCmdOTPFileInvd"));
343                 break;
344             }
345 
346             co_await sdbusplus::async::sleep_for(
347                 ctx, std::chrono::microseconds(10000)); // Write delay
348 
349             tBuf[0] = IFXMFRAHBAddr;
350             tBuf[1] = 4;
351             tBuf[2] = 0x00;
352             tBuf[3] = 0xe0;
353             tBuf[4] = 0x05;
354             tBuf[5] = 0x20;
355             tSize = 6;
356             rSize = 0;
357 
358             ret = co_await this->i2cInterface.sendReceive(tBuf, tSize, rBuf,
359                                                           rSize);
360             if (!ret)
361             {
362                 error("Failed to program VR on sendReceive on {CMD}", "CMD",
363                       std::string("IFXMFRAHBAddr"));
364                 break;
365             }
366 
367             co_await sdbusplus::async::sleep_for(
368                 ctx, std::chrono::microseconds(10000));
369             size = 0;
370         }
371 
372         // programm into scratchpad
373         for (int j = 0; j < sect->dataCnt; j++)
374         {
375             tBuf[0] = IFXMFRRegWrite;
376             tBuf[1] = 4;
377             uint8_t tSize = 6;
378             uint8_t rSize = 0;
379             memcpy(&tBuf[2], &sect->data[j], 4);
380             ret = co_await this->i2cInterface.sendReceive(tBuf, tSize, rBuf,
381                                                           rSize);
382             if (!ret)
383             {
384                 error("Failed to program the VR on sendReceive {CMD}", "CMD",
385                       std::string("IFXMFRRegWrite"));
386                 break;
387             }
388             co_await sdbusplus::async::sleep_for(
389                 ctx, std::chrono::microseconds(10000));
390         }
391         if (ret)
392         {
393             break;
394         }
395 
396         size += sect->dataCnt * 4;
397         if ((i + 1 >= configuration.sectCnt) ||
398             (sect->type != configuration.section[i + 1].type))
399         {
400             // Upload to scratchpad
401             std::memcpy(tBuf, &size, 2);
402             tBuf[2] = 0x00;
403             tBuf[3] = 0x00;
404             bool ret = co_await this->mfrFWcmd(MFRFwCmdOTPConfSTO, tBuf, NULL);
405             if (ret)
406             {
407                 error("Failed to program the VR on mfrFWcmd {CMD}", "CMD",
408                       std::string("MFRFwCmdOTPConfSTO"));
409                 break;
410             }
411 
412             // wait for programming soak (2ms/byte, at least 200ms)
413             // ex: Config (604 bytes): (604 / 50) + 2 = 14 (1400 ms)
414             size = (size / 50) + 2;
415             for (int j = 0; j < size; j++)
416             {
417                 co_await sdbusplus::async::sleep_for(
418                     ctx, std::chrono::microseconds(100000));
419             }
420 
421             tBuf[0] = PMBusSTLCml;
422             uint8_t tSize = 1;
423             uint8_t rSize = 1;
424             ret = co_await this->i2cInterface.sendReceive(rBuf, tSize, tBuf,
425                                                           rSize);
426             if (!ret)
427             {
428                 error("Failed to program VR on sendReceive {CMD}", "CMD",
429                       std::string("PMBusSTLCml"));
430                 break;
431             }
432             if (rBuf[0] & 0x01)
433             {
434                 error("Failed to program VR - response code invalid");
435                 break;
436             }
437         }
438     }
439 
440     if (!ret)
441     {
442         co_return false;
443     }
444 
445     co_return true;
446 }
447 
448 int XDPE1X2XX::lineSplit(char** dest, char* src, char* delim)
449 {
450     char* s = strtok(src, delim);
451     int size = 0;
452     int maxSz = 5;
453 
454     while (s)
455     {
456         *dest++ = s;
457         if ((++size) >= maxSz)
458         {
459             break;
460         }
461         s = strtok(NULL, delim);
462     }
463 
464     return size;
465 }
466 
467 int XDPE1X2XX::parseImage(const uint8_t* image, size_t image_size)
468 {
469     size_t lenEndTag = strlen(DataEndTag);
470     size_t lenStartTag = strlen(DataStartTag);
471     size_t lenComment = strlen(DataComment);
472     size_t lenXV = strlen(DataXV);
473     size_t start = 0;
474     const int maxLineLength = 40;
475     char line[maxLineLength];
476     char* token = NULL;
477     bool isData = false;
478     char delim = ' ';
479     uint16_t offset;
480     uint8_t sectType = 0x0;
481     uint32_t dWord;
482     int dataCnt = 0;
483     int sectIndex = -1;
484 
485     for (size_t i = 0; i < image_size; i++)
486     {
487         if (image[i] == '\n')
488         {
489             std::memcpy(line, image + start, i - start);
490             if (!strncmp(line, DataComment, lenComment))
491             {
492                 token = line + lenComment;
493                 if (!strncmp(token, DataXV, lenXV))
494                 {
495                     debug("Parsing: {OBJ}", "OBJ",
496                           reinterpret_cast<const char*>(line));
497                 }
498                 start = i + 1;
499                 continue;
500             }
501             if (!strncmp(line, DataEndTag, lenEndTag))
502             {
503                 debug("Parsing: {OBJ}", "OBJ",
504                       reinterpret_cast<const char*>(line));
505                 break;
506             }
507             else if (isData)
508             {
509                 char* tokenList[8] = {0};
510                 int tokenSize = lineSplit(tokenList, line, &delim);
511                 if (tokenSize < 1)
512                 {
513                     start = i + 1;
514                     continue;
515                 }
516 
517                 offset = (uint16_t)strtol(tokenList[0], NULL, 16);
518                 if (sectType == SectTrim && offset != 0x0)
519                 {
520                     continue;
521                 }
522 
523                 for (int i = 1; i < tokenSize; i++)
524                 {
525                     dWord = (uint32_t)strtol(tokenList[i], NULL, 16);
526                     if ((offset == 0x0) && (i == 1))
527                     {
528                         sectType = (uint8_t)dWord;
529                         if (sectType == SectTrim)
530                         {
531                             break;
532                         }
533                         if ((++sectIndex) >= MaxSectCnt)
534                         {
535                             return -1;
536                         }
537 
538                         configuration.section[sectIndex].type = sectType;
539                         configuration.sectCnt = sectIndex + 1;
540                         dataCnt = 0;
541                     }
542 
543                     if (dataCnt >= MaxSectDataCnt)
544                     {
545                         return -1;
546                     }
547 
548                     configuration.section[sectIndex].data[dataCnt++] = dWord;
549                     configuration.section[sectIndex].dataCnt = dataCnt;
550                     configuration.totalCnt++;
551                 }
552             }
553             else
554             {
555                 if ((token = strstr(line, AddressField)) != NULL)
556                 {
557                     if ((token = strstr(token, "0x")) != NULL)
558                     {
559                         configuration.addr =
560                             (uint8_t)(strtoul(token, NULL, 16) << 1);
561                     }
562                 }
563                 else if ((token = strstr(line, ChecksumField)) != NULL)
564                 {
565                     if ((token = strstr(token, "0x")) != NULL)
566                     {
567                         configuration.sumExp =
568                             (uint32_t)strtoul(token, NULL, 16);
569                     }
570                 }
571                 else if (!strncmp(line, DataStartTag, lenStartTag))
572                 {
573                     isData = true;
574                     start = i + 1;
575                     continue;
576                 }
577                 else
578                 {
579                     start = i + 1;
580                     continue;
581                 }
582             }
583             start = i + 1;
584         }
585     }
586 
587     return 0;
588 }
589 
590 int XDPE1X2XX::checkImage()
591 {
592     uint8_t i;
593     uint32_t crc;
594     uint32_t sum = 0;
595 
596     for (i = 0; i < configuration.sectCnt; i++)
597     {
598         struct configSect* sect = &configuration.section[i];
599         if (sect == NULL)
600         {
601             error("Failed to check image - unexpected NULL section");
602             return -1;
603         }
604 
605         crc = calcCRC32(&sect->data[2], 2);
606         if (crc != sect->data[2])
607         {
608             error("Failed to check image - first CRC value mismatch");
609             return -1;
610         }
611         sum += crc;
612 
613         // check CRC of section data
614         crc = calcCRC32(&sect->data[3], sect->dataCnt - 4);
615         if (crc != sect->data[sect->dataCnt - 1])
616         {
617             error("Failed to check image - second CRC value mismatch");
618             return -1;
619         }
620         sum += crc;
621     }
622 
623     if (sum != configuration.sumExp)
624     {
625         error("Failed to check image - third CRC value mismatch");
626         return -1;
627     }
628 
629     return 0;
630 }
631 
632 // NOLINTBEGIN(readability-static-accessed-through-instance)
633 sdbusplus::async::task<bool> XDPE1X2XX::verifyImage(const uint8_t* image,
634                                                     size_t imageSize)
635 // NOLINTEND(readability-static-accessed-through-instance)
636 {
637     if (parseImage(image, imageSize) < 0)
638     {
639         error("Failed to update firmware on parsing Image");
640         co_return false;
641     }
642 
643     if (checkImage() < 0)
644     {
645         error("Failed to update firmware on check image");
646         co_return false;
647     }
648 
649     co_return true;
650 }
651 
652 // NOLINTBEGIN(readability-static-accessed-through-instance)
653 sdbusplus::async::task<bool> XDPE1X2XX::updateFirmware(bool force)
654 // NOLINTEND(readability-static-accessed-through-instance)
655 {
656     // NOLINTBEGIN(clang-analyzer-core.uninitialized.Branch)
657     bool ret = co_await program(force);
658     // NOLINTEND(clang-analyzer-core.uninitialized.Branch)
659     if (!ret)
660     {
661         error("Failed to update firmware on program");
662         co_return false;
663     }
664 
665     // Reset the configuration
666     configuration.addr = 0;
667     configuration.totalCnt = 0;
668     configuration.sumExp = 0;
669     configuration.sectCnt = 0;
670     for (int i = 0; i <= MaxSectCnt - 1; i++)
671     {
672         configuration.section[i].type = 0;
673         configuration.section[i].dataCnt = 0;
674         for (int j = 0; j <= MaxSectDataCnt; j++)
675         {
676             configuration.section[i].data[j] = 0;
677         }
678     }
679 
680     co_return true;
681 }
682 
683 // NOLINTBEGIN(readability-static-accessed-through-instance)
684 sdbusplus::async::task<bool> XDPE1X2XX::reset()
685 // NOLINTEND(readability-static-accessed-through-instance)
686 {
687     bool ret = co_await mfrFWcmd(MFRFwCmdReset, NULL, NULL);
688     if (!ret)
689     {
690         error("Failed to reset the VR");
691         co_return false;
692     }
693 
694     co_await sdbusplus::async::sleep_for(
695         ctx, std::chrono::microseconds(VRResetDelay));
696 
697     co_return true;
698 }
699 
700 uint32_t XDPE1X2XX::calcCRC32(const uint32_t* data, int len)
701 {
702     if (data == NULL)
703     {
704         return 0;
705     }
706 
707     uint32_t crc = 0xFFFFFFFF;
708     for (int i = 0; i < len; i++)
709     {
710         crc ^= data[i];
711 
712         for (int b = 0; b < 32; b++)
713         {
714             if (crc & 0x1)
715             {
716                 crc = (crc >> 1) ^ CRC32Poly; // lsb-first
717             }
718             else
719             {
720                 crc >>= 1;
721             }
722         }
723     }
724 
725     return ~crc;
726 }
727 
728 bool XDPE1X2XX::forcedUpdateAllowed()
729 {
730     return true;
731 }
732 
733 } // namespace phosphor::software::VR
734