1 #include "mp297x.hpp" 2 3 #include "common/include/utils.hpp" 4 5 #include <phosphor-logging/lg2.hpp> 6 7 #include <fstream> 8 9 PHOSPHOR_LOG2_USING; 10 11 namespace phosphor::software::VR 12 { 13 14 static constexpr size_t statusByteLength = 1; 15 16 static constexpr std::string_view crcUserRegName = "CRC_USER"; 17 static constexpr std::string_view crcMultiRegName = "CRC_MULTI"; 18 19 enum class MP297XCmd : uint8_t 20 { 21 // Page 0 commands 22 storeDataIntoMTP = 0xF1, 23 24 // Page 1 commands 25 writeProtectMode = 0x35, 26 enableMTPPageWR = 0x4F, 27 28 // Page 2 commands 29 enableMultiConfigCRC = 0xF3, 30 31 // Page 0x29 commands 32 readUserCodeCRC = 0xFF, 33 34 // Page 0x2A commands 35 readMultiConfigCRC = 0xBF, 36 }; 37 38 sdbusplus::async::task<bool> MP297X::parseDeviceConfiguration() 39 { 40 if (!configuration) 41 { 42 error("Device configuration not initialized"); 43 co_return false; 44 } 45 46 configuration->vendorId = 0x0025; 47 configuration->productId = 0x0071; 48 49 for (const auto& tokens : parser->lineTokens) 50 { 51 if (!parser->isValidDataTokens(tokens)) 52 { 53 continue; 54 } 55 56 auto regName = parser->getVal<std::string>(tokens, ATE::regName); 57 58 if (regName == crcUserRegName) 59 { 60 configuration->configId = 61 parser->getVal<uint32_t>(tokens, ATE::configId); 62 configuration->crcUser = 63 parser->getVal<uint32_t>(tokens, ATE::regDataHex); 64 } 65 else if (regName == crcMultiRegName) 66 { 67 configuration->crcMulti = 68 parser->getVal<uint32_t>(tokens, ATE::regDataHex); 69 break; 70 } 71 } 72 73 co_return true; 74 } 75 76 sdbusplus::async::task<bool> MP297X::verifyImage(const uint8_t* image, 77 size_t imageSize) 78 { 79 if (!co_await parseImage(image, imageSize, MPSImageType::type1)) 80 { 81 error("Image verification failed: image parsing failed"); 82 co_return false; 83 } 84 85 if (configuration->registersData.empty()) 86 { 87 error("Image verification failed - no data found"); 88 co_return false; 89 } 90 91 if (configuration->configId == 0) 92 { 93 error("Image verification failed - missing config ID"); 94 co_return false; 95 } 96 97 co_return true; 98 } 99 100 sdbusplus::async::task<bool> MP297X::checkId(PMBusCmd idCmd, uint32_t expected) 101 { 102 constexpr size_t idLength = 2; 103 104 std::vector<uint8_t> tbuf; 105 std::vector<uint8_t> rbuf; 106 107 tbuf = buildByteVector(PMBusCmd::page, MPSPage::page0); 108 if (!i2cInterface.sendReceive(tbuf, rbuf)) 109 { 110 error("Failed to set page 0 for ID check"); 111 co_return false; 112 } 113 114 tbuf = buildByteVector(idCmd); 115 rbuf.resize(statusByteLength + idLength); 116 if (!i2cInterface.sendReceive(tbuf, rbuf)) 117 { 118 error("Failed to read ID, cmd={CMD}", "CMD", lg2::hex, 119 static_cast<uint8_t>(idCmd)); 120 co_return false; 121 } 122 123 auto idBytes = std::span(rbuf).subspan(statusByteLength); 124 auto id = bytesToInt<uint32_t>(idBytes); 125 126 if (id != expected) 127 { 128 error("ID check failed for cmd {CMD}: got {ID}, expected {EXP}", "CMD", 129 lg2::hex, static_cast<uint8_t>(idCmd), "ID", lg2::hex, id, "EXP", 130 lg2::hex, expected); 131 co_return false; 132 } 133 134 co_return true; 135 } 136 137 sdbusplus::async::task<bool> MP297X::isPasswordUnlock() 138 { 139 constexpr uint8_t passwordMatchMask = 0x08; 140 141 std::vector<uint8_t> tbuf; 142 std::vector<uint8_t> rbuf; 143 144 tbuf = buildByteVector(PMBusCmd::page, MPSPage::page0); 145 if (!i2cInterface.sendReceive(tbuf, rbuf)) 146 { 147 error("Failed to set page 0 for password unlock check"); 148 co_return false; 149 } 150 151 tbuf = buildByteVector(PMBusCmd::statusCML); 152 rbuf.resize(statusByteLength); 153 154 if (!i2cInterface.sendReceive(tbuf, rbuf)) 155 { 156 error("Failed to check password unlock status"); 157 co_return false; 158 } 159 160 auto unlocked = (rbuf[0] & passwordMatchMask) != 0; 161 162 debug("Password unlock status: {STATUS}", "STATUS", 163 unlocked ? "Unlocked" : "Locked"); 164 165 co_return unlocked; 166 } 167 168 sdbusplus::async::task<bool> MP297X::unlockWriteProtect() 169 { 170 constexpr uint8_t writeProtectModeMask = 0x04; 171 constexpr uint8_t unlockMemoryProtect = 0x00; 172 constexpr uint8_t unlockMTPProtect = 0x63; 173 174 std::vector<uint8_t> tbuf; 175 std::vector<uint8_t> rbuf(statusByteLength); 176 177 // Get write protection mode 178 tbuf = buildByteVector(PMBusCmd::page, MPSPage::page1); 179 if (!i2cInterface.sendReceive(tbuf, rbuf)) 180 { 181 error("Failed to set page 1 to check write protection mode"); 182 co_return false; 183 } 184 185 tbuf = buildByteVector(MP297XCmd::writeProtectMode); 186 if (!i2cInterface.sendReceive(tbuf, rbuf)) 187 { 188 error("Failed to get write protect mode"); 189 co_return false; 190 } 191 192 auto isMTPMode = (rbuf[0] & writeProtectModeMask) == 0; 193 auto unlockData = isMTPMode ? unlockMTPProtect : unlockMemoryProtect; 194 195 // Unlock write protection 196 tbuf = buildByteVector(PMBusCmd::page, MPSPage::page0); 197 if (!i2cInterface.sendReceive(tbuf, rbuf)) 198 { 199 error("Failed to set page 0 to unlock write protection"); 200 co_return false; 201 } 202 203 tbuf = buildByteVector(PMBusCmd::writeProtect, unlockData); 204 if (!i2cInterface.sendReceive(tbuf, rbuf)) 205 { 206 error("Failed to unlock write protection"); 207 co_return false; 208 } 209 210 debug("Unlocked {MODE} write protection", "MODE", 211 isMTPMode ? "MTP" : "memory"); 212 co_return true; 213 } 214 215 sdbusplus::async::task<bool> MP297X::programPageRegisters( 216 MPSPage page, const std::map<uint8_t, std::vector<MPSData>>& groupedData) 217 { 218 auto pageNum = static_cast<uint8_t>(page); 219 220 if (groupedData.find(pageNum) == groupedData.end()) 221 { 222 debug("No data found for page {PAGE}", "PAGE", pageNum); 223 co_return true; 224 } 225 226 const auto& data = groupedData.at(pageNum); 227 228 // Page 2 is the multi-config page. Enter page2A to write config value 229 // to MTP space directly during multi-config page programming. 230 if (page == MPSPage::page2) 231 { 232 page = MPSPage::page2A; 233 pageNum = static_cast<uint8_t>(page); 234 } 235 236 std::vector<uint8_t> tbuf; 237 std::vector<uint8_t> rbuf; 238 239 tbuf = buildByteVector(PMBusCmd::page, page); 240 if (!i2cInterface.sendReceive(tbuf, rbuf)) 241 { 242 error("Failed to set page {PAGE} to program registers", "PAGE", 243 pageNum); 244 co_return false; 245 } 246 247 auto i2cWriteWithRetry = 248 [&](const std::vector<uint8_t>& tbuf) -> sdbusplus::async::task<bool> { 249 std::vector<uint8_t> rbuf; 250 constexpr size_t maxRetries = 3; 251 constexpr auto retryDelay = std::chrono::milliseconds(10); 252 253 for (size_t i = 1; i <= maxRetries; ++i) 254 { 255 if (i2cInterface.sendReceive(tbuf, rbuf)) 256 { 257 co_return true; 258 } 259 error("I2C write failed, retry {RETRY}", "RETRY", i); 260 co_await sdbusplus::async::sleep_for(ctx, retryDelay); 261 } 262 co_return false; 263 }; 264 265 for (const auto& regData : data) 266 { 267 tbuf = {regData.addr}; 268 tbuf.insert(tbuf.end(), regData.data.begin(), 269 regData.data.begin() + regData.length); 270 271 if (!(co_await i2cWriteWithRetry(tbuf))) 272 { 273 error( 274 "Failed to write data {DATA} to register {REG} on page {PAGE}", 275 "DATA", lg2::hex, bytesToInt<uint32_t>(regData.data), "REG", 276 lg2::hex, regData.addr, "PAGE", pageNum); 277 co_return false; 278 } 279 280 if (page == MPSPage::page2A) 281 { 282 // Page 2A requires a delay after each register write 283 co_await sdbusplus::async::sleep_for(ctx, 284 std::chrono::milliseconds(2)); 285 } 286 } 287 288 debug("Programmed {N} registers on page {PAGE}", "N", data.size(), "PAGE", 289 pageNum); 290 291 co_return true; 292 } 293 294 sdbusplus::async::task<bool> MP297X::storeDataIntoMTP() 295 { 296 std::vector<uint8_t> tbuf; 297 std::vector<uint8_t> rbuf; 298 299 tbuf = buildByteVector(PMBusCmd::page, MPSPage::page0); 300 if (!i2cInterface.sendReceive(tbuf, rbuf)) 301 { 302 error("Failed to set page 0 for storing data into MTP"); 303 co_return false; 304 } 305 306 tbuf = buildByteVector(MP297XCmd::storeDataIntoMTP); 307 if (!i2cInterface.sendReceive(tbuf, rbuf)) 308 { 309 error("Failed to store data into MTP"); 310 co_return false; 311 } 312 313 // Wait store data into MTP 314 co_await sdbusplus::async::sleep_for(ctx, std::chrono::milliseconds(500)); 315 316 debug("Stored data into MTP"); 317 318 co_return true; 319 } 320 321 sdbusplus::async::task<bool> MP297X::enableMTPPageWriteRead() 322 { 323 constexpr uint8_t mtpByteRWEnable = 0x20; 324 325 std::vector<uint8_t> tbuf; 326 std::vector<uint8_t> rbuf; 327 328 tbuf = buildByteVector(PMBusCmd::page, MPSPage::page1); 329 if (!i2cInterface.sendReceive(tbuf, rbuf)) 330 { 331 error("Failed to set page 1 to enable MTP page write/read"); 332 co_return false; 333 } 334 335 tbuf = buildByteVector(MP297XCmd::enableMTPPageWR); 336 rbuf.resize(statusByteLength); 337 if (!i2cInterface.sendReceive(tbuf, rbuf)) 338 { 339 error("Failed to read MTP page write/read status"); 340 co_return false; 341 } 342 343 uint8_t enableMTPPageWRData = rbuf[0] | mtpByteRWEnable; 344 tbuf = buildByteVector(MP297XCmd::enableMTPPageWR, enableMTPPageWRData); 345 rbuf.resize(0); 346 if (!i2cInterface.sendReceive(tbuf, rbuf)) 347 { 348 error("Failed to enable MTP page write/read"); 349 co_return false; 350 } 351 352 debug("Enabled MTP page write/read"); 353 co_return true; 354 } 355 356 sdbusplus::async::task<bool> MP297X::enableMultiConfigCRC() 357 { 358 std::vector<uint8_t> tbuf; 359 std::vector<uint8_t> rbuf; 360 361 tbuf = buildByteVector(PMBusCmd::page, MPSPage::page2); 362 if (!i2cInterface.sendReceive(tbuf, rbuf)) 363 { 364 error("Failed to set page 2 to enable multi-config CRC"); 365 co_return false; 366 } 367 368 tbuf = buildByteVector(MP297XCmd::enableMultiConfigCRC); 369 if (!i2cInterface.sendReceive(tbuf, rbuf)) 370 { 371 error("Failed to enable multi-config CRC"); 372 co_return false; 373 } 374 375 debug("Enabled multi-config CRC"); 376 co_return true; 377 } 378 379 sdbusplus::async::task<bool> MP297X::getCRC(uint32_t* checksum) 380 { 381 if (checksum == nullptr) 382 { 383 error("getCRC() called with null checksum pointer"); 384 co_return false; 385 } 386 387 constexpr size_t crcLength = 2; 388 389 std::vector<uint8_t> tbuf; 390 std::vector<uint8_t> rbuf; 391 392 uint16_t userCodeCRC = 0; 393 uint16_t multiConfigCRC = 0; 394 395 // Read User Code CRC 396 tbuf = buildByteVector(PMBusCmd::page, MPSPage::page29); 397 rbuf.resize(0); 398 if (!i2cInterface.sendReceive(tbuf, rbuf)) 399 { 400 error("Failed to set page 29 for User Code CRC read"); 401 co_return false; 402 } 403 404 tbuf = buildByteVector(MP297XCmd::readUserCodeCRC); 405 rbuf.resize(crcLength); 406 if (!i2cInterface.sendReceive(tbuf, rbuf)) 407 { 408 error("Failed to read User Code CRC from device"); 409 co_return false; 410 } 411 412 userCodeCRC = bytesToInt<uint16_t>(rbuf); 413 414 // Read Multi Config CRC 415 tbuf = buildByteVector(PMBusCmd::page, MPSPage::page2A); 416 rbuf.resize(0); 417 if (!i2cInterface.sendReceive(tbuf, rbuf)) 418 { 419 error("Failed to set page 2A for Multi Config CRC read"); 420 co_return false; 421 } 422 423 tbuf = buildByteVector(MP297XCmd::readMultiConfigCRC); 424 rbuf.resize(crcLength); 425 if (!i2cInterface.sendReceive(tbuf, rbuf)) 426 { 427 error("Failed to read Multi Config CRC from device"); 428 co_return false; 429 } 430 431 multiConfigCRC = bytesToInt<uint16_t>(rbuf); 432 433 // Combine: [byte3 byte2] = userCodeCRC, [byte1 byte0] = multiConfigCRC 434 *checksum = (static_cast<uint32_t>(userCodeCRC) << 16) | 435 static_cast<uint32_t>(multiConfigCRC); 436 437 debug("Read CRC: {CRC}", "CRC", lg2::hex, *checksum); 438 439 co_return true; 440 } 441 442 sdbusplus::async::task<bool> MP297X::checkMTPCRC() 443 { 444 uint32_t crc = 0; 445 auto expectedCRC = (configuration->crcUser << 16) | configuration->crcMulti; 446 447 // NOLINTBEGIN(clang-analyzer-core.uninitialized.Branch) 448 if (!co_await getCRC(&crc)) 449 // NOLINTEND(clang-analyzer-core.uninitialized.Branch) 450 { 451 error("Failed to get CRC for MTP check"); 452 co_return false; 453 } 454 455 debug("MTP CRC: {CRC}, Expected: {EXP}", "CRC", lg2::hex, crc, "EXP", 456 lg2::hex, expectedCRC); 457 458 co_return crc == expectedCRC; 459 } 460 461 bool MP297X::forcedUpdateAllowed() 462 { 463 return true; 464 } 465 466 sdbusplus::async::task<bool> MP297X::updateFirmware(bool force) 467 { 468 (void)force; 469 470 auto groupedConfigData = getGroupedConfigData(); 471 472 if (!co_await checkId(PMBusCmd::mfrId, configuration->vendorId)) 473 { 474 co_return false; 475 } 476 477 if (!co_await checkId(PMBusCmd::mfrModel, configuration->productId)) 478 { 479 co_return false; 480 } 481 482 if (!co_await isPasswordUnlock()) 483 { 484 co_return false; 485 } 486 487 if (!co_await unlockWriteProtect()) 488 { 489 co_return false; 490 } 491 492 if (!co_await programPageRegisters(MPSPage::page0, groupedConfigData)) 493 { 494 co_return false; 495 } 496 497 if (!co_await programPageRegisters(MPSPage::page1, groupedConfigData)) 498 { 499 co_return false; 500 } 501 502 if (!co_await storeDataIntoMTP()) 503 { 504 co_return false; 505 } 506 507 if (!co_await enableMTPPageWriteRead()) 508 { 509 co_return false; 510 } 511 512 if (!co_await enableMultiConfigCRC()) 513 { 514 co_return false; 515 } 516 517 if (!co_await programPageRegisters(MPSPage::page2, groupedConfigData)) 518 { 519 co_return false; 520 } 521 522 if (!(co_await checkMTPCRC())) 523 { 524 co_return false; 525 } 526 527 co_return true; 528 } 529 530 } // namespace phosphor::software::VR 531