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