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 enum RevisionCode 19 { 20 REV_A = 0x00, 21 REV_B, 22 REV_C, 23 REV_D, 24 }; 25 26 enum ProductID 27 { 28 ProductIDXDPE15254 = 0x90, // Revision C,D 29 ProductIDXDPE15284 = 0x8A, // Revision A,B,C,D 30 ProductIDXDPE19283AC = 0x95, // Revision A,B,C 31 ProductIDXDPE19283D = 0xAE, // Revision D 32 ProductIDXDPE192C3AC = 0x96, // Revision A,B,C 33 ProductIDXDPE192C3D = 0xAF, // Revision D 34 }; 35 36 constexpr uint8_t PMBusICDeviceID = 0xAD; 37 constexpr uint8_t PMBusSTLCml = 0x7E; 38 constexpr uint8_t IFXICDeviceIDLen = 2; 39 constexpr uint8_t IFXMFRAHBAddr = 0xCE; 40 constexpr uint8_t IFXMFRRegWrite = 0xDE; 41 constexpr uint8_t IFXMFRFwCmdData = 0xFD; 42 constexpr uint8_t IFXMFRFwCmd = 0xFE; 43 constexpr uint8_t MFRFwCmdReset = 0x0e; 44 constexpr uint8_t MFRFwCmdRmng = 0x10; 45 constexpr uint8_t MFRFwCmdGetHWAddress = 0x2E; 46 constexpr uint8_t MFRFwCmdOTPConfSTO = 0x11; 47 constexpr uint8_t MFRFwCmdOTPFileInvd = 0x12; 48 constexpr uint8_t MFRFwCmdGetCRC = 0x2D; 49 constexpr int XDPE152XXConfSize = 1344; 50 constexpr int XDPE152XXDConfSize = 1312; 51 constexpr int XDPE192XXBConfSize = 1416; // Config(728) + PMBus(568) + SVID(120) 52 constexpr uint8_t VRWarnRemaining = 3; 53 constexpr uint8_t SectTrim = 0x02; 54 55 constexpr uint16_t MFRDefaultWaitTime = 20; 56 constexpr uint16_t MFRGetHWAddressWaitTime = 5; 57 constexpr uint16_t MFROTPFileInvalidationWaitTime = 100; 58 constexpr uint16_t MFRSectionInvalidationWaitTime = 4; 59 constexpr uint16_t VRResetDelay = 500; 60 61 constexpr uint32_t CRC32Poly = 0xEDB88320; 62 63 const char* const AddressField = "PMBus Address :"; 64 const char* const ChecksumField = "Checksum :"; 65 const char* const DataStartTag = "[Configuration Data]"; 66 const char* const DataEndTag = "[End Configuration Data]"; 67 const char* const DataComment = "//"; 68 const char* const DataXV = "XV"; 69 70 XDPE1X2XX::XDPE1X2XX(sdbusplus::async::context& ctx, uint16_t bus, 71 uint16_t address) : 72 VoltageRegulator(ctx), i2cInterface(phosphor::i2c::I2C(bus, address)) 73 {} 74 75 sdbusplus::async::task<bool> XDPE1X2XX::getDeviceId(uint8_t* deviceID) 76 { 77 uint8_t tbuf[16] = {0}; 78 tbuf[0] = PMBusICDeviceID; 79 tbuf[1] = 2; 80 uint8_t tSize = 1; 81 uint8_t rbuf[16] = {0}; 82 uint8_t rSize = IFXICDeviceIDLen + 1; 83 84 if (!(co_await this->i2cInterface.sendReceive(tbuf, tSize, rbuf, rSize))) 85 { 86 error("Failed to get device ID"); 87 co_return false; 88 } 89 90 // According to datasheet: 91 // rbuf[1]: device revision 92 // rbuf[2]: device id 93 std::memcpy(deviceID, &rbuf[1], IFXICDeviceIDLen); 94 info.deviceRev = deviceID[0]; 95 info.deviceId = deviceID[1]; 96 debug("VR Device ID: {ID}", "ID", lg2::hex, rbuf[2]); 97 debug("VR Device Rev: {REV}", "REV", lg2::hex, rbuf[1]); 98 99 co_return true; 100 } 101 102 sdbusplus::async::task<bool> XDPE1X2XX::mfrFWcmd( 103 uint8_t cmd, uint16_t processTime, uint8_t* data, uint8_t* resp) 104 { 105 uint8_t tBuf[16] = {0}; 106 uint8_t rBuf[16] = {0}; 107 uint8_t tSize = 0; 108 uint8_t rSize = 0; 109 110 if (data) 111 { 112 tBuf[0] = IFXMFRFwCmdData; 113 tBuf[1] = 4; // Block write 4 bytes 114 tSize = 6; 115 std::memcpy(&tBuf[2], data, 4); 116 if (!(co_await this->i2cInterface.sendReceive(tBuf, tSize, rBuf, 117 rSize))) 118 { 119 error("Failed to send MFR command: {CMD}", "CMD", 120 std::string("IFXMFRFwCmdDAta")); 121 co_return false; 122 } 123 } 124 125 co_await sdbusplus::async::sleep_for(ctx, std::chrono::microseconds(300)); 126 127 tBuf[0] = IFXMFRFwCmd; 128 tBuf[1] = cmd; 129 tSize = 2; 130 rSize = 0; 131 if (!(co_await this->i2cInterface.sendReceive(tBuf, tSize, rBuf, rSize))) 132 { 133 error("Failed to send MFR command: {CMD}", "CMD", 134 std::string("IFXMFRFwCmd")); 135 co_return false; 136 } 137 138 co_await sdbusplus::async::sleep_for( 139 ctx, std::chrono::milliseconds(processTime)); 140 141 if (resp) 142 { 143 tBuf[0] = IFXMFRFwCmdData; 144 tSize = 1; 145 rSize = 6; 146 if (!(co_await this->i2cInterface.sendReceive(tBuf, tSize, rBuf, 147 rSize))) 148 { 149 error("Failed to send MFR command: {CMD}", "CMD", 150 std::string("IFXMFRFwCmdData")); 151 co_return false; 152 } 153 if (rBuf[0] != 4) 154 { 155 error( 156 "Failed to receive MFR response with unexpected response size"); 157 co_return false; 158 } 159 std::memcpy(resp, rBuf + 1, 4); 160 } 161 162 co_return true; 163 } 164 165 sdbusplus::async::task<bool> XDPE1X2XX::getRemainingWrites(uint8_t* remain) 166 { 167 // According to datasheet: 168 // remaingin OTP size = rBuf[0] + 256 * rBuf[1] 169 uint8_t tBuf[16] = {0}; 170 uint8_t rBuf[16] = {0}; 171 uint8_t devId[2] = {0}; 172 173 if (!(co_await this->mfrFWcmd(MFRFwCmdRmng, MFRDefaultWaitTime, tBuf, 174 rBuf))) 175 { 176 error("Failed to request remaining writes"); 177 co_return false; 178 } 179 180 if (!(co_await this->getDeviceId(devId))) 181 { 182 error("Failed to request device ID for remaining writes"); 183 co_return false; 184 } 185 186 int configSize = getConfigSize(devId[1], devId[0]); 187 if (configSize < 0) 188 { 189 error("Failed to request valid configuration size"); 190 co_return false; 191 } 192 193 *remain = REMAINING_TIMES(rBuf, configSize); 194 195 co_return true; 196 } 197 198 int XDPE1X2XX::getConfigSize(uint8_t deviceId, uint8_t revision) 199 { 200 static const std::map<std::pair<uint8_t, uint8_t>, int> configSizeMap = { 201 {{ProductIDXDPE19283AC, REV_B}, XDPE192XXBConfSize}, 202 {{ProductIDXDPE15284, REV_A}, XDPE152XXConfSize}, 203 {{ProductIDXDPE15284, REV_B}, XDPE152XXConfSize}, 204 {{ProductIDXDPE15284, REV_C}, XDPE152XXConfSize}, 205 {{ProductIDXDPE15284, REV_D}, XDPE152XXDConfSize}, 206 {{ProductIDXDPE192C3AC, REV_B}, XDPE192XXBConfSize}, 207 }; 208 209 auto it = configSizeMap.find({deviceId, revision}); 210 if (it != configSizeMap.end()) 211 { 212 return it->second; 213 } 214 215 error("Failed to get configuration size of {DEVID} with revision {REV}", 216 "DEVID", deviceId, "REV", revision); 217 return -1; 218 } 219 220 sdbusplus::async::task<bool> XDPE1X2XX::getCRC(uint32_t* checksum) 221 { 222 uint8_t tBuf[16] = {0}; 223 uint8_t rBuf[16] = {0}; 224 225 if (!(co_await this->mfrFWcmd(MFRFwCmdGetCRC, MFRDefaultWaitTime, tBuf, 226 rBuf))) 227 { 228 error("Failed to get CRC value"); 229 co_return false; 230 } 231 232 *checksum = (static_cast<uint32_t>(rBuf[3]) << 24) | 233 (static_cast<uint32_t>(rBuf[2]) << 16) | 234 (static_cast<uint32_t>(rBuf[1]) << 8) | 235 (static_cast<uint32_t>(rBuf[0])); 236 237 co_return true; 238 } 239 240 sdbusplus::async::task<bool> XDPE1X2XX::program(bool force) 241 { 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 if (!(co_await getCRC(&sum))) 250 // NOLINTEND(clang-analyzer-core.uninitialized.Branch) 251 { 252 error("Failed to program the VR"); 253 co_return -1; 254 } 255 256 debug("CRC before programming: {CRC}", "CRC", lg2::hex, sum); 257 debug("CRC of configuration: {CRC}", "CRC", lg2::hex, configuration.sumExp); 258 259 if (!force && (sum == configuration.sumExp)) 260 { 261 error("Failed to program the VR - CRC value are equal with no force"); 262 co_return -1; 263 } 264 265 // NOLINTBEGIN(clang-analyzer-core.uninitialized.Branch) 266 if (!(co_await this->getRemainingWrites(&remain))) 267 // NOLINTEND(clang-analyzer-core.uninitialized.Branch) 268 { 269 error("Failed to program the VR - unable to obtain remaing writes"); 270 co_return -1; 271 } 272 273 debug("Remaining write cycles of VR: {REMAIN}", "REMAIN", remain); 274 275 if (!remain) 276 { 277 error("Failed to program the VR - no remaining write cycles left"); 278 co_return -1; 279 } 280 281 if (!force && (remain <= VRWarnRemaining)) 282 { 283 error( 284 "Failed to program the VR - {REMAIN} remaining writes left and not force", 285 "REMAIN", remain); 286 co_return -1; 287 } 288 289 // Added reprogramming of the entire configuration file. 290 // Except for the trim section, all other data will be replaced. 291 // 0xfe 0xfe 0x00 0x00 instructs the command to reprogram all header codes 292 // and XVcode. If the old sections are not invalidated in OTP, they can 293 // affect the CRC calculation. 294 debug("Invalidate current Configuration"); 295 296 tBuf[0] = 0xfe; 297 tBuf[1] = 0xfe; 298 tBuf[2] = 0x00; 299 tBuf[3] = 0x00; 300 301 if (!(co_await this->mfrFWcmd(MFRFwCmdOTPFileInvd, 302 MFROTPFileInvalidationWaitTime, tBuf, NULL))) 303 { 304 error("Failed to program the VR - Invalidation of current FW"); 305 co_return false; 306 } 307 308 for (int i = 0; i < configuration.sectCnt; i++) 309 { 310 debug("Programming section: {SEC}", "SEC", i); 311 struct configSect* sect = &configuration.section[i]; 312 if (sect == NULL) 313 { 314 error( 315 "Failed to program the VR - unexpected NULL section in config"); 316 co_return false; 317 } 318 319 if ((i <= 0) || (sect->type != configuration.section[i - 1].type)) 320 { 321 debug("Section Type: {TYPE}", "TYPE", lg2::hex, 322 configuration.section[i].type); 323 324 // clear bit0 of PMBUS_STS_CML 325 tBuf[0] = PMBusSTLCml; 326 tBuf[1] = 0x1; 327 uint8_t tSize = 2; 328 uint8_t rSize = 0; 329 if (!(co_await this->i2cInterface.sendReceive(tBuf, tSize, rBuf, 330 rSize))) 331 { 332 error("Failed to program the VR on sendReceive {CMD}", "CMD", 333 std::string("PMBusSTLCml")); 334 co_return false; 335 } 336 337 debug("Invalidating section type: {TYPE}", "TYPE", sect->type); 338 tBuf[0] = sect->type; 339 tBuf[1] = 0x00; 340 tBuf[2] = 0x00; 341 tBuf[3] = 0x00; 342 343 if (!(co_await this->mfrFWcmd(MFRFwCmdOTPFileInvd, 344 MFRSectionInvalidationWaitTime, tBuf, 345 NULL))) 346 { 347 error("Failed to program VR on mfrFWCmd on {CMD}", "CMD", 348 std::string("MFRFwCmdOTPFileInvd")); 349 co_return false; 350 } 351 352 // set scratchpad addr 353 // XDPE192XX Rev A/B: 0x2005e000 354 // Rev C : 0x2005e400 355 // Rev D : 0x2005f000 356 357 debug("Setting scratchpad address: {ADDR}", "ADDR", lg2::hex, 358 info.scratchPadAddress); 359 360 tBuf[0] = IFXMFRAHBAddr; 361 tBuf[1] = 4; 362 tBuf[2] = (info.scratchPadAddress) & 0xFF; 363 tBuf[3] = (info.scratchPadAddress >> 8) & 0xFF; 364 tBuf[4] = (info.scratchPadAddress >> 16) & 0xFF; 365 tBuf[5] = (info.scratchPadAddress >> 24) & 0xFF; 366 tSize = 6; 367 rSize = 0; 368 369 if (!(co_await this->i2cInterface.sendReceive(tBuf, tSize, rBuf, 370 rSize))) 371 { 372 error("Failed to program VR on sendReceive on {CMD}", "CMD", 373 std::string("IFXMFRAHBAddr")); 374 co_return false; 375 } 376 377 co_await sdbusplus::async::sleep_for( 378 ctx, std::chrono::microseconds(10000)); 379 size = 0; 380 } 381 382 // programm into scratchpad 383 for (int j = 0; j < sect->dataCnt; j++) 384 { 385 tBuf[0] = IFXMFRRegWrite; 386 tBuf[1] = 4; 387 uint8_t tSize = 6; 388 uint8_t rSize = 0; 389 memcpy(&tBuf[2], §->data[j], 4); 390 if (!(co_await this->i2cInterface.sendReceive(tBuf, tSize, rBuf, 391 rSize))) 392 { 393 error("Failed to program the VR on sendReceive {CMD}", "CMD", 394 std::string("IFXMFRRegWrite")); 395 co_return false; 396 } 397 co_await sdbusplus::async::sleep_for(ctx, 398 std::chrono::milliseconds(10)); 399 } 400 401 size += sect->dataCnt * 4; 402 if ((i + 1 >= configuration.sectCnt) || 403 (sect->type != configuration.section[i + 1].type)) 404 { 405 // wait for programming soak (2ms/byte, at least 200ms) 406 // ex: Config (604 bytes): (604 / 50) + 2 = 14 (1400 ms) 407 uint16_t soakTime = 100 * ((size / 50) + 2); 408 409 // Upload to scratchpad 410 debug("Upload from scratch pad to OTP with soak time: {TIME}ms", 411 "TIME", soakTime); 412 std::memcpy(tBuf, &size, 2); 413 tBuf[2] = 0x00; 414 tBuf[3] = 0x00; 415 if (!(co_await this->mfrFWcmd(MFRFwCmdOTPConfSTO, soakTime, tBuf, 416 NULL))) 417 { 418 error("Failed to program the VR on mfrFWcmd {CMD}", "CMD", 419 std::string("MFRFwCmdOTPConfSTO")); 420 co_return false; 421 } 422 423 // Read status faults after programming 424 tBuf[0] = PMBusSTLCml; 425 uint8_t tSize = 1; 426 uint8_t rSize = 1; 427 if (!(co_await this->i2cInterface.sendReceive(rBuf, tSize, tBuf, 428 rSize))) 429 { 430 error("Failed to program VR on sendReceive {CMD}", "CMD", 431 std::string("PMBusSTLCml")); 432 co_return false; 433 } 434 if (rBuf[0] & 0x01) 435 { 436 error("Failed to program VR - status fault indicated error"); 437 co_return false; 438 } 439 } 440 } 441 442 co_return true; 443 } 444 445 int XDPE1X2XX::lineSplit(char** dest, char* src, char* delim) 446 { 447 char* s = strtok(src, delim); 448 int size = 0; 449 int maxSz = 5; 450 451 while (s) 452 { 453 *dest++ = s; 454 if ((++size) >= maxSz) 455 { 456 break; 457 } 458 s = strtok(NULL, delim); 459 } 460 461 return size; 462 } 463 464 bool XDPE1X2XX::parseImage(const uint8_t* image, size_t image_size) 465 { 466 size_t lenEndTag = strlen(DataEndTag); 467 size_t lenStartTag = strlen(DataStartTag); 468 size_t lenComment = strlen(DataComment); 469 size_t lenXV = strlen(DataXV); 470 size_t start = 0; 471 const int maxLineLength = 40; 472 char line[maxLineLength]; 473 char* token = NULL; 474 bool isData = false; 475 char delim = ' '; 476 uint16_t offset; 477 uint8_t sectType = 0x0; 478 uint32_t dWord; 479 int dataCnt = 0; 480 int sectIndex = -1; 481 482 for (size_t i = 0; i < image_size; i++) 483 { 484 if (image[i] == '\n') 485 { 486 std::memcpy(line, image + start, i - start); 487 if (!strncmp(line, DataComment, lenComment)) 488 { 489 token = line + lenComment; 490 if (!strncmp(token, DataXV, lenXV)) 491 { 492 debug("Parsing: {OBJ}", "OBJ", 493 reinterpret_cast<const char*>(line)); 494 } 495 start = i + 1; 496 continue; 497 } 498 if (!strncmp(line, DataEndTag, lenEndTag)) 499 { 500 debug("Parsing: {OBJ}", "OBJ", 501 reinterpret_cast<const char*>(line)); 502 break; 503 } 504 else if (isData) 505 { 506 char* tokenList[8] = {0}; 507 int tokenSize = lineSplit(tokenList, line, &delim); 508 if (tokenSize < 1) 509 { 510 start = i + 1; 511 continue; 512 } 513 514 offset = (uint16_t)strtol(tokenList[0], NULL, 16); 515 if (sectType == SectTrim && offset != 0x0) 516 { 517 continue; 518 } 519 520 for (int i = 1; i < tokenSize; i++) 521 { 522 dWord = (uint32_t)strtoul(tokenList[i], NULL, 16); 523 if ((offset == 0x0) && (i == 1)) 524 { 525 sectType = (uint8_t)dWord; 526 if (sectType == SectTrim) 527 { 528 break; 529 } 530 if ((++sectIndex) >= MaxSectCnt) 531 { 532 return false; 533 } 534 535 configuration.section[sectIndex].type = sectType; 536 configuration.sectCnt = sectIndex + 1; 537 dataCnt = 0; 538 } 539 540 if (dataCnt >= MaxSectDataCnt) 541 { 542 return false; 543 } 544 545 configuration.section[sectIndex].data[dataCnt++] = dWord; 546 configuration.section[sectIndex].dataCnt = dataCnt; 547 configuration.totalCnt++; 548 } 549 } 550 else 551 { 552 if ((token = strstr(line, AddressField)) != NULL) 553 { 554 if ((token = strstr(token, "0x")) != NULL) 555 { 556 configuration.addr = 557 (uint8_t)(strtoul(token, NULL, 16) << 1); 558 } 559 } 560 else if ((token = strstr(line, ChecksumField)) != NULL) 561 { 562 if ((token = strstr(token, "0x")) != NULL) 563 { 564 configuration.sumExp = 565 (uint32_t)strtoul(token, NULL, 16); 566 } 567 } 568 else if (!strncmp(line, DataStartTag, lenStartTag)) 569 { 570 isData = true; 571 start = i + 1; 572 continue; 573 } 574 else 575 { 576 start = i + 1; 577 continue; 578 } 579 } 580 start = i + 1; 581 } 582 } 583 584 return true; 585 } 586 587 bool XDPE1X2XX::checkImage() 588 { 589 uint8_t i; 590 uint32_t crc; 591 uint32_t sum = 0; 592 593 for (i = 0; i < configuration.sectCnt; i++) 594 { 595 struct configSect* sect = &configuration.section[i]; 596 if (sect == NULL) 597 { 598 error("Failed to check image - unexpected NULL section"); 599 return false; 600 } 601 602 crc = calcCRC32(§->data[0], 2); 603 if (crc != sect->data[2]) 604 { 605 error("Failed to check image - first CRC value mismatch"); 606 return false; 607 } 608 sum += crc; 609 610 // check CRC of section data 611 crc = calcCRC32(§->data[3], sect->dataCnt - 4); 612 if (crc != sect->data[sect->dataCnt - 1]) 613 { 614 error("Failed to check image - second CRC value mismatch"); 615 return false; 616 } 617 sum += crc; 618 } 619 620 if (sum != configuration.sumExp) 621 { 622 debug("Calculated CRC: {CRC}", "CRC", lg2::hex, sum); 623 debug("Config CRC {CRC}", "CRC", lg2::hex, configuration.sumExp); 624 error("Failed to check image - third CRC value mismatch"); 625 return false; 626 } 627 628 return true; 629 } 630 631 sdbusplus::async::task<bool> XDPE1X2XX::verifyImage(const uint8_t* image, 632 size_t imageSize) 633 { 634 if (!parseImage(image, imageSize)) 635 { 636 error("Failed to update firmware on parsing Image"); 637 co_return false; 638 } 639 640 if (!checkImage()) 641 { 642 error("Failed to update firmware on check image"); 643 co_return false; 644 } 645 646 co_return true; 647 } 648 649 sdbusplus::async::task<bool> XDPE1X2XX::getScratchPadAddress() 650 { 651 uint8_t tbuf[16] = {0}; 652 uint8_t rbuf[16] = {0}; 653 654 tbuf[0] = 0x02; 655 tbuf[1] = 0x00; 656 tbuf[2] = 0x00; 657 tbuf[3] = 0x00; 658 659 if (!(co_await mfrFWcmd(MFRFwCmdGetHWAddress, MFRGetHWAddressWaitTime, tbuf, 660 rbuf))) 661 { 662 error("mfrFWcmd call failed to retrieve scratchpad address"); 663 co_return false; 664 } 665 666 info.scratchPadAddress = (static_cast<uint32_t>(rbuf[3]) << 24) | 667 (static_cast<uint32_t>(rbuf[2]) << 16) | 668 (static_cast<uint32_t>(rbuf[1]) << 8) | 669 (static_cast<uint32_t>(rbuf[0])); 670 671 debug("Scratchpad Address: {ADDR}", "ADDR", lg2::hex, 672 info.scratchPadAddress); 673 674 co_return true; 675 } 676 677 sdbusplus::async::task<bool> XDPE1X2XX::updateFirmware(bool force) 678 { 679 bool ret = true; 680 if (!(co_await getScratchPadAddress())) 681 { 682 error("Failed to retrieve scratchpad address"); 683 co_return false; 684 } 685 686 // NOLINTBEGIN(clang-analyzer-core.uninitialized.Branch) 687 ret = co_await program(force); 688 if (!ret) 689 // NOLINTEND(clang-analyzer-core.uninitialized.Branch) 690 { 691 error("Failed to update firmware on program"); 692 } 693 694 info.deviceId = 0; 695 info.deviceRev = 0; 696 info.remainingWrites = 0; 697 info.scratchPadAddress = 0; 698 info.actualCRC = 0; 699 info.configSize = 0; 700 701 // Reset the configuration 702 configuration.addr = 0; 703 configuration.totalCnt = 0; 704 configuration.sumExp = 0; 705 configuration.sectCnt = 0; 706 for (int i = 0; i <= MaxSectCnt - 1; i++) 707 { 708 configuration.section[i].type = 0; 709 configuration.section[i].dataCnt = 0; 710 for (int j = 0; j <= MaxSectDataCnt; j++) 711 { 712 configuration.section[i].data[j] = 0; 713 } 714 } 715 716 if (!ret) 717 { 718 co_return false; 719 } 720 721 co_return true; 722 } 723 724 sdbusplus::async::task<bool> XDPE1X2XX::reset() 725 { 726 if (!(co_await mfrFWcmd(MFRFwCmdReset, VRResetDelay, NULL, NULL))) 727 { 728 error("Failed to reset the VR"); 729 co_return false; 730 } 731 732 co_return true; 733 } 734 735 uint32_t XDPE1X2XX::calcCRC32(const uint32_t* data, int len) 736 { 737 if (data == NULL) 738 { 739 return 0; 740 } 741 742 uint32_t crc = 0xFFFFFFFF; 743 for (int i = 0; i < len; i++) 744 { 745 crc ^= data[i]; 746 747 for (int b = 0; b < 32; b++) 748 { 749 if (crc & 0x1) 750 { 751 crc = (crc >> 1) ^ CRC32Poly; // lsb-first 752 } 753 else 754 { 755 crc >>= 1; 756 } 757 } 758 } 759 760 return ~crc; 761 } 762 763 bool XDPE1X2XX::forcedUpdateAllowed() 764 { 765 return true; 766 } 767 768 } // namespace phosphor::software::VR 769