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], §->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(§->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(§->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