blob: 59c05605d26e30fb0ab83b7deb28c08677c95f6c [file] [log] [blame]
Christopher Meis7e446a42024-10-22 09:36:41 +02001#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
13PHOSPHOR_LOG2_USING;
14
15namespace phosphor::software::VR
16{
17
18const uint32_t CRC32Poly = 0xEDB88320;
19const int VRResetDelay = 500000;
20
21enum RevisionCode
22{
23 REV_A = 0x00,
24 REV_B,
25 REV_C,
26 REV_D,
27};
28
29enum ProductID
30{
31 ProductIDXDPE15284 = 0x8A,
32 ProductIDXDPE19283 = 0x95,
33};
34
35const uint32_t PMBusICDeviceID = 0xAD;
36const uint32_t PMBusSTLCml = 0x7E;
37const uint32_t IFXICDeviceIDLen = 2;
38const uint32_t IFXMFRAHBAddr = 0xCE;
39const uint32_t IFXMFRRegWrite = 0xDE;
40const uint32_t IFXMFRFwCmdData = 0xFD;
41const uint32_t IFXMFRFwCmd = 0xFE;
42const uint32_t MFRFwCmdReset = 0x0e;
43const uint32_t MFRFwCmdRmng = 0x10;
44const uint32_t MFRFwCmdOTPConfSTO = 0x11;
45const uint32_t MFRFwCmdOTPFileInvd = 0x12;
46const uint32_t MFRFwCmdGetCRC = 0x2D;
47const int XDPE15284CConfSize = 1344;
48const int XDPE19283BConfSize = 1416;
49const uint32_t VRWarnRemaining = 3;
50const uint32_t SectTrim = 0x02;
51
52const char* const AddressField = "PMBus Address :";
53const char* const ChecksumField = "Checksum :";
54const char* const DataStartTag = "Configuration Data]";
55const char* const DataEndTag = "[End Configuration Data]";
56const char* const DataComment = "//";
57const char* const DataXV = "XV";
58
59XDPE1X2XX::XDPE1X2XX(sdbusplus::async::context& ctx, uint16_t bus,
60 uint16_t address) :
61 VoltageRegulator(ctx), i2cInterface(phosphor::i2c::I2C(bus, address))
62{}
63
Christopher Meis7e446a42024-10-22 09:36:41 +020064sdbusplus::async::task<bool> XDPE1X2XX::getDeviceId(uint8_t* deviceID)
Christopher Meis7e446a42024-10-22 09:36:41 +020065{
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
Christopher Meis7e446a42024-10-22 09:36:41 +020086sdbusplus::async::task<bool> XDPE1X2XX::mfrFWcmd(uint8_t cmd, uint8_t* data,
87 uint8_t* resp)
Christopher Meis7e446a42024-10-22 09:36:41 +020088{
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
Christopher Meis7e446a42024-10-22 09:36:41 +0200150sdbusplus::async::task<bool> XDPE1X2XX::getRemainingWrites(uint8_t* remain)
Christopher Meis7e446a42024-10-22 09:36:41 +0200151{
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
183int 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
Christopher Meis7e446a42024-10-22 09:36:41 +0200208sdbusplus::async::task<bool> XDPE1X2XX::getCRC(uint32_t* checksum)
Christopher Meis7e446a42024-10-22 09:36:41 +0200209{
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
Christopher Meis7e446a42024-10-22 09:36:41 +0200228sdbusplus::async::task<bool> XDPE1X2XX::program(bool force)
Christopher Meis7e446a42024-10-22 09:36:41 +0200229
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
438int 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
457int 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
580int 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
Christopher Meis7e446a42024-10-22 09:36:41 +0200622sdbusplus::async::task<bool> XDPE1X2XX::verifyImage(const uint8_t* image,
623 size_t imageSize)
Christopher Meis7e446a42024-10-22 09:36:41 +0200624{
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
Christopher Meis7e446a42024-10-22 09:36:41 +0200640sdbusplus::async::task<bool> XDPE1X2XX::updateFirmware(bool force)
Christopher Meis7e446a42024-10-22 09:36:41 +0200641{
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
Christopher Meis7e446a42024-10-22 09:36:41 +0200669sdbusplus::async::task<bool> XDPE1X2XX::reset()
Christopher Meis7e446a42024-10-22 09:36:41 +0200670{
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
684uint32_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
712bool XDPE1X2XX::forcedUpdateAllowed()
713{
714 return true;
715}
716
717} // namespace phosphor::software::VR