1 #include "isl69269.hpp" 2 3 #include "common/include/i2c/i2c.hpp" 4 5 #include <phosphor-logging/lg2.hpp> 6 7 #include <string> 8 9 PHOSPHOR_LOG2_USING; 10 11 namespace phosphor::software::VR 12 { 13 14 constexpr uint8_t regProgStatus = 0x7E; 15 constexpr uint8_t regHexModeCFG0 = 0x87; 16 constexpr uint8_t regCRC = 0x94; 17 constexpr uint8_t regHexModeCFG1 = 0xBD; 18 constexpr uint8_t regDMAData = 0xC5; 19 constexpr uint8_t regDMAAddr = 0xC7; 20 constexpr uint8_t regRestoreCfg = 0xF2; 21 22 constexpr uint8_t regRemainginWrites = 0x35; 23 24 constexpr uint8_t gen3SWRevMin = 0x06; 25 constexpr uint8_t deviceIdLength = 4; 26 27 constexpr uint8_t gen3Legacy = 1; 28 constexpr uint8_t gen3Production = 2; 29 30 constexpr uint16_t cfgId = 7; 31 constexpr uint16_t gen3FileHead = 5; 32 constexpr uint16_t gen3LegacyCRC = 276 - gen3FileHead; 33 constexpr uint16_t gen3ProductionCRC = 290 - gen3FileHead; 34 constexpr uint8_t checksumLen = 4; 35 constexpr uint8_t deviceRevisionLen = 4; 36 37 // Common pmBus Command codes 38 constexpr uint8_t pmBusDeviceId = 0xAD; 39 constexpr uint8_t pmBusDeviceRev = 0xAE; 40 41 // Config file constants 42 constexpr char recordTypeData = 0x00; 43 constexpr char recordTypeHeader = 0x49; 44 45 constexpr uint8_t defaultBufferSize = 16; 46 constexpr uint8_t programBufferSize = 32; 47 48 constexpr uint8_t zeroByteLen = 0; 49 constexpr uint8_t oneByteLen = 1; 50 constexpr uint8_t threeByteLen = 3; 51 constexpr uint8_t fourByteLen = 4; 52 53 ISL69269::ISL69269(sdbusplus::async::context& ctx, uint16_t bus, 54 uint16_t address) : 55 VoltageRegulator(ctx), i2cInterface(phosphor::i2c::I2C(bus, address)) 56 {} 57 58 inline void shiftLeftFromLSB(const uint8_t* data, uint32_t* result) 59 { 60 *result = (static_cast<uint32_t>(data[3]) << 24) | 61 (static_cast<uint32_t>(data[2]) << 16) | 62 (static_cast<uint32_t>(data[1]) << 8) | 63 (static_cast<uint32_t>(data[0])); 64 } 65 66 inline void shiftLeftFromMSB(const uint8_t* data, uint32_t* result) 67 { 68 *result = (static_cast<uint32_t>(data[0]) << 24) | 69 (static_cast<uint32_t>(data[1]) << 16) | 70 (static_cast<uint32_t>(data[2]) << 8) | 71 (static_cast<uint32_t>(data[3])); 72 } 73 74 static uint8_t calcCRC8(const uint8_t* data, uint8_t len) 75 { 76 uint8_t crc = 0x00; 77 int i = 0; 78 int b = 0; 79 80 for (i = 0; i < len; i++) 81 { 82 crc ^= data[i]; 83 for (b = 0; b < 8; b++) 84 { 85 if (crc & 0x80) 86 { 87 crc = (crc << 1) ^ 0x07; // polynomial 0x07 88 } 89 else 90 { 91 crc = (crc << 1); 92 } 93 } 94 } 95 96 return crc; 97 } 98 99 sdbusplus::async::task<bool> ISL69269::dmaReadWrite(uint8_t* reg, uint8_t* resp) 100 { 101 uint8_t tbuf[defaultBufferSize] = {0}; 102 uint8_t tlen = threeByteLen; 103 uint8_t rbuf[defaultBufferSize] = {0}; 104 uint8_t rlen = zeroByteLen; 105 106 tbuf[0] = regDMAAddr; 107 std::memcpy(&tbuf[1], reg, 2); 108 109 // NOLINTBEGIN(clang-analyzer-core.uninitialized.Branch) 110 if (!(co_await i2cInterface.sendReceive(tbuf, tlen, rbuf, rlen))) 111 // NOLINTEND(clang-analyzer-core.uninitialized.Branch) 112 { 113 error("dmaReadWrite failed with {CMD}", "CMD", 114 std::string("_REG_DMA_ADDR")); 115 co_return false; 116 } 117 118 tlen = oneByteLen; 119 rlen = fourByteLen; 120 121 tbuf[0] = regDMAData; 122 if (!(co_await i2cInterface.sendReceive(tbuf, tlen, resp, rlen))) 123 { 124 error("dmaReadWrite failed with {CMD}", "CMD", 125 std::string("_REG_DMA_DATA")); 126 co_return false; 127 } 128 129 co_return true; 130 } 131 132 sdbusplus::async::task<bool> ISL69269::getRemainingWrites(uint8_t* remain) 133 { 134 uint8_t tbuf[defaultBufferSize] = {0}; 135 uint8_t rbuf[defaultBufferSize] = {0}; 136 137 tbuf[0] = regRemainginWrites; 138 tbuf[1] = 0x00; 139 if (!(co_await dmaReadWrite(tbuf, rbuf))) 140 { 141 error("getRemainingWrites failed"); 142 co_return false; 143 } 144 145 *remain = rbuf[0]; 146 co_return true; 147 } 148 149 sdbusplus::async::task<bool> ISL69269::getHexMode(uint8_t* mode) 150 { 151 uint8_t tbuf[defaultBufferSize] = {0}; 152 uint8_t rbuf[defaultBufferSize] = {0}; 153 154 tbuf[0] = regHexModeCFG0; 155 tbuf[1] = regHexModeCFG1; 156 if (!(co_await dmaReadWrite(tbuf, rbuf))) 157 { 158 error("getHexMode failed"); 159 co_return false; 160 } 161 162 *mode = (rbuf[0] == 0) ? gen3Legacy : gen3Production; 163 164 co_return true; 165 } 166 167 sdbusplus::async::task<bool> ISL69269::getDeviceId(uint32_t* deviceId) 168 { 169 uint8_t tbuf[defaultBufferSize] = {0}; 170 uint8_t tLen = oneByteLen; 171 uint8_t rbuf[defaultBufferSize] = {0}; 172 uint8_t rLen = deviceIdLength + 1; 173 174 tbuf[0] = pmBusDeviceId; 175 176 if (!(co_await i2cInterface.sendReceive(tbuf, tLen, rbuf, rLen))) 177 { 178 error("getDeviceId failed"); 179 co_return false; 180 } 181 182 std::memcpy(deviceId, &rbuf[1], deviceIdLength); 183 184 co_return true; 185 } 186 187 sdbusplus::async::task<bool> ISL69269::getDeviceRevision(uint32_t* revision) 188 { 189 uint8_t tbuf[defaultBufferSize] = {0}; 190 uint8_t tlen = oneByteLen; 191 uint8_t rbuf[defaultBufferSize] = {0}; 192 uint8_t rlen = deviceRevisionLen + 1; 193 194 tbuf[0] = pmBusDeviceRev; 195 if (!(co_await i2cInterface.sendReceive(tbuf, tlen, rbuf, rlen))) 196 { 197 error("getDeviceRevision failed with sendreceive"); 198 co_return false; 199 } 200 201 if (mode == gen3Legacy) 202 { 203 std::memcpy(revision, &rbuf[1], deviceRevisionLen); 204 } 205 else 206 { 207 shiftLeftFromLSB(rbuf + 1, revision); 208 } 209 210 co_return true; 211 } 212 213 sdbusplus::async::task<bool> ISL69269::getCRC(uint32_t* sum) 214 { 215 uint8_t tbuf[defaultBufferSize] = {0}; 216 uint8_t rbuf[defaultBufferSize] = {0}; 217 218 tbuf[0] = regCRC; 219 if (!(co_await dmaReadWrite(tbuf, rbuf))) 220 { 221 error("getCRC failed"); 222 co_return false; 223 } 224 std::memcpy(sum, rbuf, sizeof(uint32_t)); 225 226 co_return true; 227 } 228 229 bool ISL69269::parseImage(const uint8_t* image, size_t imageSize) 230 { 231 size_t nextLineStart = 0; 232 int dcnt = 0; 233 234 for (size_t i = 0; i < imageSize; i++) 235 { 236 if (image[i] == '\n') // We have a hex file, so we check new line. 237 { 238 char line[40]; 239 char xdigit[8] = {0}; 240 uint8_t sepLine[32] = {0}; 241 242 size_t lineLen = i - nextLineStart; 243 std::memcpy(line, image + nextLineStart, lineLen); 244 int k = 0; 245 size_t j = 0; 246 for (k = 0, j = 0; j < lineLen; k++, j += 2) 247 { 248 // Convert two chars into a array of single values 249 std::memcpy(xdigit, &line[j], 2); 250 sepLine[k] = (uint8_t)std::strtol(xdigit, NULL, 16); 251 } 252 253 if (sepLine[0] == recordTypeHeader) 254 { 255 if (sepLine[3] == pmBusDeviceId) 256 { 257 shiftLeftFromMSB(sepLine + 4, &configuration.devIdExp); 258 debug("device id from configuration: {ID}", "ID", lg2::hex, 259 configuration.devIdExp); 260 } 261 else if (sepLine[3] == pmBusDeviceRev) 262 { 263 shiftLeftFromMSB(sepLine + 4, &configuration.devRevExp); 264 debug("device revision from config: {ID}", "ID", lg2::hex, 265 configuration.devRevExp); 266 267 // According to programing guide: 268 // If legacy hex file 269 // MSB device revision == 0x00 | 0x01 270 if (configuration.devRevExp < (gen3SWRevMin << 24)) 271 { 272 debug("Legacy hex file format recognized"); 273 configuration.mode = gen3Legacy; 274 } 275 else 276 { 277 debug("Production hex file format recognized"); 278 configuration.mode = gen3Production; 279 } 280 } 281 } 282 else if (sepLine[0] == recordTypeData) 283 { 284 if (((sepLine[1] + 2) >= (uint8_t)sizeof(sepLine))) 285 { 286 dcnt = 0; 287 break; 288 } 289 // According to documentation: 290 // 00 05 C2 E7 08 00 F6 291 // | | | | | | | 292 // | | | | | | - Packet Error Code (CRC8) 293 // | | | | - - Data 294 // | | | - Command Code 295 // | | - Address 296 // | - Size of data (including Addr, Cmd, CRC8) 297 // - Line type (0x00 - Data, 0x49 header information) 298 configuration.pData[dcnt].len = sepLine[1] - 2; 299 configuration.pData[dcnt].pec = 300 sepLine[3 + configuration.pData[dcnt].len]; 301 configuration.pData[dcnt].addr = sepLine[2]; 302 configuration.pData[dcnt].cmd = sepLine[3]; 303 std::memcpy(configuration.pData[dcnt].data, sepLine + 2, 304 configuration.pData[dcnt].len + 1); 305 switch (dcnt) 306 { 307 case cfgId: 308 configuration.cfgId = sepLine[4] & 0x0F; 309 debug("Config ID: {ID}", "ID", lg2::hex, 310 configuration.cfgId); 311 break; 312 case gen3LegacyCRC: 313 if (configuration.mode == gen3Legacy) 314 { 315 std::memcpy(&configuration.crcExp, &sepLine[4], 316 checksumLen); 317 debug("Config Legacy CRC: {CRC}", "CRC", lg2::hex, 318 configuration.crcExp); 319 } 320 break; 321 case gen3ProductionCRC: 322 if (configuration.mode == gen3Production) 323 { 324 std::memcpy(&configuration.crcExp, &sepLine[4], 325 checksumLen); 326 debug("Config Production CRC: {CRC}", "CRC", 327 lg2::hex, configuration.crcExp); 328 } 329 break; 330 } 331 dcnt++; 332 } 333 else 334 { 335 error("parseImage failed. Unknown recordType"); 336 return false; 337 } 338 339 nextLineStart = i + 1; 340 } 341 } 342 configuration.wrCnt = dcnt; 343 return true; 344 } 345 346 bool ISL69269::checkImage() 347 { 348 uint8_t crc8 = 0; 349 350 for (int i = 0; i < configuration.wrCnt; i++) 351 { 352 crc8 = calcCRC8(configuration.pData[i].data, 353 configuration.pData[i].len + 1); 354 if (crc8 != configuration.pData[i].pec) 355 { 356 debug( 357 "Config line: {LINE}, failed to calculate CRC. Have {HAVE}, Want: {WANT}", 358 "LINE", i, "HAVE", lg2::hex, crc8, "WANT", lg2::hex, 359 configuration.pData[i].pec); 360 return false; 361 } 362 } 363 364 return true; 365 } 366 367 sdbusplus::async::task<bool> ISL69269::program() 368 { 369 uint8_t tbuf[programBufferSize] = {0}; 370 uint8_t rbuf[programBufferSize] = {0}; 371 uint8_t rlen = zeroByteLen; 372 373 for (int i = 0; i < configuration.wrCnt; i++) 374 { 375 tbuf[0] = configuration.pData[i].cmd; 376 std::memcpy(tbuf + 1, &configuration.pData[i].data, 377 configuration.pData[i].len - 1); 378 379 if (!(co_await i2cInterface.sendReceive( 380 tbuf, configuration.pData[i].len, rbuf, rlen))) 381 { 382 error("program failed at writing data to voltage regulator"); 383 } 384 } 385 386 co_return true; 387 } 388 389 sdbusplus::async::task<bool> ISL69269::getProgStatus() 390 { 391 uint8_t tbuf[programBufferSize] = {0}; 392 uint8_t rbuf[programBufferSize] = {0}; 393 int retry = 3; 394 395 tbuf[0] = regProgStatus; 396 tbuf[1] = 0x00; 397 398 do 399 { 400 // NOLINTBEGIN(clang-analyzer-core.uninitialized.Branch) 401 if (!(co_await dmaReadWrite(tbuf, rbuf))) 402 // NOLINTEND(clang-analyzer-core.uninitialized.Branch) 403 { 404 error("getProgStatus failed on dmaReadWrite"); 405 co_return false; 406 } 407 408 if (rbuf[0] & 0x01) 409 { 410 debug("Programming succesful"); 411 break; 412 } 413 if (--retry == 0) 414 { 415 if ((!(rbuf[1] & 0x1)) || (rbuf[1] & 0x2)) 416 { 417 error("programming the device failed"); 418 } 419 if (!(rbuf[1] & 0x4)) 420 { 421 error( 422 "HEX file contains more configurations than are available"); 423 } 424 if (!(rbuf[1] & 0x8)) 425 { 426 error( 427 "A CRC mismatch exists within the configuration data. Programming failed before TP banks are consumed"); 428 } 429 if (!(rbuf[1] & 0x10)) 430 { 431 error( 432 "CRC check fails on the OTP memory. Programming fails after OTP banks are consumed"); 433 } 434 if (!(rbuf[1] & 0x20)) 435 { 436 error("Programming fails after OTP banks are consumed."); 437 } 438 439 error("failed to program the device after exceeding retries"); 440 co_return false; 441 } 442 co_await sdbusplus::async::sleep_for(ctx, std::chrono::seconds(1)); 443 } while (retry > 0); 444 445 co_return true; 446 } 447 448 sdbusplus::async::task<bool> ISL69269::restoreCfg() 449 { 450 uint8_t tbuf[defaultBufferSize] = {0}; 451 uint8_t rbuf[defaultBufferSize] = {0}; 452 453 tbuf[0] = regRestoreCfg; 454 tbuf[1] = configuration.cfgId; 455 456 debug("Restore configurtion ID: {ID}", "ID", lg2::hex, configuration.cfgId); 457 458 if (!(co_await dmaReadWrite(tbuf, rbuf))) 459 { 460 error("restoreCfg failed at dmaReadWrite"); 461 co_return false; 462 } 463 464 co_return true; 465 } 466 467 sdbusplus::async::task<bool> ISL69269::verifyImage(const uint8_t* image, 468 size_t imageSize) 469 { 470 uint8_t mode = 0xFF; 471 uint8_t remain = 0; 472 uint32_t devID = 0; 473 uint32_t devRev = 0; 474 uint32_t crc = 0; 475 476 if (!(co_await getHexMode(&mode))) 477 { 478 error("program failed at getHexMode"); 479 co_return false; 480 } 481 482 if (!parseImage(image, imageSize)) 483 { 484 error("verifyImage failed at parseImage"); 485 co_return false; 486 } 487 488 if (!checkImage()) 489 { 490 error("verifyImage failed at checkImage"); 491 co_return false; 492 } 493 494 if (mode != configuration.mode) 495 { 496 error( 497 "program failed with mode of device and configuration are not equal"); 498 co_return false; 499 } 500 501 if (!(co_await getRemainingWrites(&remain))) 502 { 503 error("program failed at getRemainingWrites"); 504 co_return false; 505 } 506 507 if (!remain) 508 { 509 error("program failed with no remaining writes left on device"); 510 co_return false; 511 } 512 513 if (!(co_await getDeviceId(&devID))) 514 { 515 error("program failed at getDeviceId"); 516 co_return false; 517 } 518 519 if (devID != configuration.devIdExp) 520 { 521 error( 522 "program failed with not matching device id of device and config"); 523 co_return false; 524 } 525 526 if (!(co_await getDeviceRevision(&devRev))) 527 { 528 error("program failed at getDeviceRevision"); 529 co_return false; 530 } 531 debug("Device revision read from device: {REV}", "REV", lg2::hex, devRev); 532 533 switch (mode) 534 { 535 case gen3Legacy: 536 if (((devRev >> 24) >= gen3SWRevMin) && 537 (configuration.devRevExp <= 0x1)) 538 { 539 debug("Legacy mode revision checks out"); 540 } 541 else 542 { 543 error( 544 "revision requirements for legacy mode device not fulfilled"); 545 } 546 break; 547 case gen3Production: 548 if (((devRev >> 24) >= gen3SWRevMin) && 549 (configuration.devRevExp >= gen3SWRevMin)) 550 { 551 debug("Production mode revision checks out"); 552 } 553 else 554 { 555 error( 556 "revision requirements for production mode device not fulfilled"); 557 } 558 break; 559 } 560 561 if (!(co_await getCRC(&crc))) 562 { 563 error("program failed at getCRC"); 564 co_return false; 565 } 566 567 debug("CRC from device: {CRC}", "CRC", lg2::hex, crc); 568 debug("CRC from config: {CRC}", "CRC", lg2::hex, configuration.crcExp); 569 570 if (crc == configuration.crcExp) 571 { 572 error("program failed with same CRC value at device and configuration"); 573 co_return false; 574 } 575 576 co_return true; 577 } 578 579 sdbusplus::async::task<bool> ISL69269::reset() 580 { 581 bool ret = true; 582 // NOLINTBEGIN(clang-analyzer-core.uninitialized.Branch) 583 ret = co_await getProgStatus(); 584 // NOLINTEND(clang-analyzer-core.uninitialized.Branch) 585 if (!ret) 586 { 587 error("reset failed at getProgStatus"); 588 } 589 590 ret = co_await restoreCfg(); 591 if (!ret) 592 { 593 error("reset failed at restoreCfg"); 594 } 595 596 // Reset configuration for next update. 597 configuration.addr = 0; 598 configuration.mode = 0; 599 configuration.cfgId = 0; 600 configuration.devIdExp = 0; 601 configuration.devRevExp = 0; 602 configuration.crcExp = 0x00; 603 for (int i = 0; i < configuration.wrCnt; i++) 604 { 605 configuration.pData[i].pec = 0x00; 606 configuration.pData[i].addr = 0x00; 607 configuration.pData[i].cmd = 0x00; 608 for (int j = 0; j < configuration.pData[i].len; j++) 609 { 610 configuration.pData[i].data[j] = 0x00; 611 } 612 configuration.pData[i].len = 0x00; 613 } 614 configuration.wrCnt = 0; 615 616 co_return ret; 617 } 618 619 bool ISL69269::forcedUpdateAllowed() 620 { 621 return true; 622 } 623 624 sdbusplus::async::task<bool> ISL69269::updateFirmware(bool force) 625 { 626 (void)force; 627 if (!(co_await program())) 628 { 629 error("programing ISL69269 failed"); 630 co_return false; 631 } 632 633 co_return true; 634 } 635 636 } // namespace phosphor::software::VR 637