blob: 28894e02e8ddcee4fa20c7a0e1053d658ff2e9ee [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
64// NOLINTBEGIN(readability-static-accessed-through-instance)
65sdbusplus::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)
89sdbusplus::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)
155sdbusplus::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
189int 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)
215sdbusplus::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)
237sdbusplus::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
448int 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
467int 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
590int 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)
633sdbusplus::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)
653sdbusplus::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)
684sdbusplus::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
700uint32_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
728bool XDPE1X2XX::forcedUpdateAllowed()
729{
730 return true;
731}
732
733} // namespace phosphor::software::VR