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