1 #include "mp5998.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 std::string_view crcUserRegName = "CRC_USER"; 15 static constexpr uint8_t eepromFaultBit = 0x01; 16 static constexpr uint8_t unlockData = 0x00; 17 static constexpr size_t statusByteLength = 1; 18 19 enum class MP5998Cmd : uint8_t 20 { 21 crcUser = 0xF8, 22 passwordReg = 0x0E, 23 }; 24 25 sdbusplus::async::task<bool> MP5998::parseDeviceConfiguration() 26 { 27 if (!configuration) 28 { 29 error("Device configuration not initialized"); 30 co_return false; 31 } 32 33 configuration->vendorId = 0x4D5053; 34 configuration->productId = 0x35393938; 35 36 for (const auto& tokens : parser->lineTokens) 37 { 38 if (!parser->isValidDataTokens(tokens)) 39 { 40 continue; 41 } 42 43 auto regName = parser->getVal<std::string>(tokens, ATE::regName); 44 45 if (regName == crcUserRegName) 46 { 47 configuration->configId = 48 parser->getVal<uint32_t>(tokens, ATE::configId); 49 configuration->crcUser = 50 parser->getVal<uint32_t>(tokens, ATE::regDataHex); 51 } 52 } 53 54 co_return true; 55 } 56 57 sdbusplus::async::task<bool> MP5998::verifyImage(const uint8_t* image, 58 size_t imageSize) 59 { 60 if (!co_await parseImage(image, imageSize)) 61 { 62 error("Image verification failed: image parsing failed"); 63 co_return false; 64 } 65 66 if (configuration->registersData.empty()) 67 { 68 error("Image verification failed - no register data found"); 69 co_return false; 70 } 71 72 if (configuration->configId == 0) 73 { 74 error("Image verification failed - missing config ID"); 75 co_return false; 76 } 77 78 co_return true; 79 } 80 81 sdbusplus::async::task<bool> MP5998::checkId(PMBusCmd idCmd, uint32_t expected) 82 { 83 static constexpr size_t mfrIdLength = 3; 84 static constexpr size_t mfrModelLength = 5; 85 86 std::vector<uint8_t> tbuf; 87 std::vector<uint8_t> rbuf; 88 89 tbuf = buildByteVector(PMBusCmd::page, MPSPage::page0); 90 if (!i2cInterface.sendReceive(tbuf, rbuf)) 91 { 92 error("MP5998: Failed to set page 0 for ID check"); 93 co_return false; 94 } 95 96 size_t bufferSize; 97 98 if (idCmd == PMBusCmd::mfrId) 99 { 100 bufferSize = statusByteLength + mfrIdLength; 101 } 102 else if (idCmd == PMBusCmd::mfrModel) 103 { 104 bufferSize = statusByteLength + mfrModelLength; 105 } 106 else 107 { 108 error("MP5998: Unsupported ID command: 0x{CMD}", "CMD", lg2::hex, 109 static_cast<uint8_t>(idCmd)); 110 co_return false; 111 } 112 113 tbuf = buildByteVector(idCmd); 114 rbuf.resize(bufferSize); 115 116 if (!i2cInterface.sendReceive(tbuf, rbuf)) 117 { 118 error("MP5998: I2C sendReceive failed for command 0x{CMD}", "CMD", 119 lg2::hex, static_cast<uint8_t>(idCmd)); 120 co_return false; 121 } 122 123 auto idBytes = std::span(rbuf).subspan(statusByteLength); 124 uint32_t id; 125 if (idCmd == PMBusCmd::mfrModel) 126 { 127 auto productBytes = idBytes.subspan(1, 4); 128 id = bytesToInt<uint32_t>(productBytes); 129 } 130 else 131 { 132 id = bytesToInt<uint32_t>(idBytes); 133 } 134 135 debug("Check ID cmd {CMD}: Got={ID}, Expected={EXP}", "CMD", lg2::hex, 136 static_cast<uint8_t>(idCmd), "ID", lg2::hex, id, "EXP", lg2::hex, 137 expected); 138 139 co_return id == expected; 140 } 141 142 sdbusplus::async::task<bool> MP5998::unlockPasswordProtection() 143 { 144 constexpr uint8_t passwordUnlockBit = 0x08; 145 constexpr uint16_t passwordData = 0x0000; 146 147 std::vector<uint8_t> tbuf; 148 std::vector<uint8_t> rbuf; 149 150 tbuf = buildByteVector(PMBusCmd::page, MPSPage::page0); 151 if (!i2cInterface.sendReceive(tbuf, rbuf)) 152 { 153 error("Failed to set page 0 for password unlock"); 154 co_return false; 155 } 156 157 tbuf = buildByteVector(MP5998Cmd::passwordReg, passwordData); 158 if (!i2cInterface.sendReceive(tbuf, rbuf)) 159 { 160 error("Failed to write password"); 161 co_return false; 162 } 163 164 tbuf = buildByteVector(PMBusCmd::statusCML); 165 rbuf.resize(statusByteLength); 166 if (!i2cInterface.sendReceive(tbuf, rbuf)) 167 { 168 error("Failed to read STATUS_CML"); 169 co_return false; 170 } 171 172 bool unlocked = (rbuf[0] & passwordUnlockBit) == 0; 173 174 co_return unlocked; 175 } 176 177 sdbusplus::async::task<bool> MP5998::unlockWriteProtection() 178 { 179 std::vector<uint8_t> tbuf; 180 std::vector<uint8_t> rbuf; 181 182 tbuf = buildByteVector(PMBusCmd::page, MPSPage::page0); 183 if (!i2cInterface.sendReceive(tbuf, rbuf)) 184 { 185 error("Failed to set page 0 for write protection unlock"); 186 co_return false; 187 } 188 189 tbuf = buildByteVector(PMBusCmd::writeProtect, unlockData); 190 if (!i2cInterface.sendReceive(tbuf, rbuf)) 191 { 192 error("Failed to unlock write protection"); 193 co_return false; 194 } 195 196 debug("Write protection unlocked"); 197 co_return true; 198 } 199 200 sdbusplus::async::task<bool> MP5998::programAllRegisters() 201 { 202 uint8_t currentPage = 0xFF; 203 204 for (const auto& regData : configuration->registersData) 205 { 206 if (regData.page != currentPage) 207 { 208 std::vector<uint8_t> tbuf = 209 buildByteVector(PMBusCmd::page, regData.page); 210 std::vector<uint8_t> rbuf; 211 if (!i2cInterface.sendReceive(tbuf, rbuf)) 212 { 213 error("Failed to set page {PAGE}", "PAGE", regData.page); 214 co_return false; 215 } 216 currentPage = regData.page; 217 } 218 219 std::vector<uint8_t> tbuf; 220 std::vector<uint8_t> rbuf; 221 222 tbuf.push_back(regData.addr); 223 224 for (uint8_t i = 0; i < regData.length && i < 4; ++i) 225 { 226 tbuf.push_back(regData.data[i]); 227 } 228 229 if (!i2cInterface.sendReceive(tbuf, rbuf)) 230 { 231 error("Failed to write register 0x{REG} on page {PAGE}", "REG", 232 lg2::hex, regData.addr, "PAGE", regData.page); 233 co_return false; 234 } 235 } 236 237 debug("All registers programmed successfully"); 238 co_return true; 239 } 240 241 sdbusplus::async::task<bool> MP5998::storeMTP() 242 { 243 std::vector<uint8_t> tbuf; 244 std::vector<uint8_t> rbuf; 245 246 tbuf = buildByteVector(PMBusCmd::page, MPSPage::page0); 247 if (!i2cInterface.sendReceive(tbuf, rbuf)) 248 { 249 error("Failed to set page 0 for MTP store"); 250 co_return false; 251 } 252 253 tbuf = buildByteVector(PMBusCmd::storeUserCode); 254 if (!i2cInterface.sendReceive(tbuf, rbuf)) 255 { 256 error("Failed to send STORE_USER_ALL command"); 257 co_return false; 258 } 259 260 co_return true; 261 } 262 263 sdbusplus::async::task<bool> MP5998::waitForMTPComplete() 264 { 265 constexpr uint16_t mtpStoreWaitmS = 1200; 266 co_await sdbusplus::async::sleep_for( 267 ctx, std::chrono::milliseconds(mtpStoreWaitmS)); 268 std::vector<uint8_t> tbuf = buildByteVector(PMBusCmd::statusCML); 269 std::vector<uint8_t> rbuf; 270 rbuf.resize(statusByteLength); 271 272 if (!i2cInterface.sendReceive(tbuf, rbuf)) 273 { 274 error("Failed to read STATUS_CML after MTP store"); 275 co_return false; 276 } 277 278 bool eepromFault = rbuf[0] & eepromFaultBit; 279 280 if (eepromFault) 281 { 282 error("EEPROM fault detected after MTP store"); 283 co_return false; 284 } 285 286 co_return true; 287 } 288 289 sdbusplus::async::task<bool> MP5998::verifyCRC() 290 { 291 uint32_t deviceCRC{0}; 292 // NOLINTBEGIN(clang-analyzer-core.uninitialized.Branch) 293 bool getCRCSuccess = co_await getCRC(&deviceCRC); 294 // NOLINTEND(clang-analyzer-core.uninitialized.Branch) 295 if (!getCRCSuccess) 296 { 297 error("Failed to read CRC from device"); 298 co_return false; 299 } 300 301 bool crcMatch = (deviceCRC == configuration->crcUser); 302 303 co_return crcMatch; 304 } 305 306 sdbusplus::async::task<bool> MP5998::getCRC(uint32_t* checksum) 307 { 308 constexpr size_t crcLength = 2; 309 310 std::vector<uint8_t> tbuf; 311 std::vector<uint8_t> rbuf; 312 313 tbuf = buildByteVector(PMBusCmd::page, MPSPage::page0); 314 if (!i2cInterface.sendReceive(tbuf, rbuf)) 315 { 316 error("Failed to set page 0 for CRC read"); 317 co_return false; 318 } 319 320 tbuf = buildByteVector(MP5998Cmd::crcUser); 321 rbuf.resize(crcLength); 322 if (!i2cInterface.sendReceive(tbuf, rbuf)) 323 { 324 error("Failed to read CRC_USER register"); 325 co_return false; 326 } 327 328 *checksum = bytesToInt<uint32_t>(rbuf); 329 330 co_return true; 331 } 332 333 sdbusplus::async::task<bool> MP5998::sendRestoreMTPCommand() 334 { 335 std::vector<uint8_t> tbuf; 336 std::vector<uint8_t> rbuf; 337 338 tbuf = buildByteVector(PMBusCmd::page, MPSPage::page0); 339 if (!i2cInterface.sendReceive(tbuf, rbuf)) 340 { 341 error("Failed to set page 0 for MTP restore"); 342 co_return false; 343 } 344 345 tbuf = buildByteVector(PMBusCmd::restoreUserAll); 346 if (!i2cInterface.sendReceive(tbuf, rbuf)) 347 { 348 error("Failed to send RESTORE_ALL command"); 349 co_return false; 350 } 351 352 co_return true; 353 } 354 355 sdbusplus::async::task<bool> MP5998::checkEEPROMFaultAfterRestore() 356 { 357 std::vector<uint8_t> tbuf; 358 std::vector<uint8_t> rbuf; 359 360 tbuf = buildByteVector(PMBusCmd::page, MPSPage::page0); 361 if (!i2cInterface.sendReceive(tbuf, rbuf)) 362 { 363 error("Failed to set page 0 for EEPROM fault check"); 364 co_return false; 365 } 366 367 tbuf = buildByteVector(PMBusCmd::statusCML); 368 rbuf.resize(1); 369 if (!i2cInterface.sendReceive(tbuf, rbuf)) 370 { 371 error("Failed to read STATUS_CML register"); 372 co_return false; 373 } 374 375 bool eepromFault = (rbuf[0] & eepromFaultBit) != 0; 376 377 co_return !eepromFault; 378 } 379 380 sdbusplus::async::task<bool> MP5998::restoreMTPAndVerify() 381 { 382 constexpr uint16_t mtpRestoreWait = 1600; 383 384 if (!co_await sendRestoreMTPCommand()) 385 { 386 error("Failed to send RESTORE_ALL command"); 387 co_return false; 388 } 389 390 co_await sdbusplus::async::sleep_for( 391 ctx, std::chrono::microseconds(mtpRestoreWait)); 392 if (!co_await checkEEPROMFaultAfterRestore()) 393 { 394 error("EEPROM fault detected after MTP restore"); 395 co_return false; 396 } 397 398 co_return true; 399 } 400 401 bool MP5998::forcedUpdateAllowed() 402 { 403 return true; 404 } 405 406 sdbusplus::async::task<bool> MP5998::updateFirmware(bool force) 407 { 408 (void)force; 409 410 if (!co_await checkId(PMBusCmd::mfrId, configuration->vendorId)) 411 { 412 co_return false; 413 } 414 415 if (!co_await checkId(PMBusCmd::mfrModel, configuration->productId)) 416 { 417 co_return false; 418 } 419 420 if (!co_await unlockWriteProtection()) 421 { 422 co_return false; 423 } 424 425 if (!co_await programAllRegisters()) 426 { 427 co_return false; 428 } 429 430 if (!co_await storeMTP()) 431 { 432 co_return false; 433 } 434 435 if (!co_await waitForMTPComplete()) 436 { 437 co_return false; 438 } 439 440 if (!co_await verifyCRC()) 441 { 442 co_return false; 443 } 444 445 if (!co_await restoreMTPAndVerify()) 446 { 447 co_return false; 448 } 449 450 co_return true; 451 } 452 453 } // namespace phosphor::software::VR 454