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