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