#include "mp5998.hpp" #include "common/include/utils.hpp" #include #include PHOSPHOR_LOG2_USING; namespace phosphor::software::VR { static constexpr std::string_view crcUserRegName = "CRC_USER"; static constexpr uint8_t eepromFaultBit = 0x01; static constexpr uint8_t unlockData = 0x00; static constexpr size_t statusByteLength = 1; enum class MP5998Cmd : uint8_t { crcUser = 0xF8, passwordReg = 0x0E, }; sdbusplus::async::task MP5998::parseDeviceConfiguration() { if (!configuration) { error("Device configuration not initialized"); co_return false; } configuration->vendorId = 0x4D5053; configuration->productId = 0x35393938; for (const auto& tokens : parser->lineTokens) { if (!parser->isValidDataTokens(tokens)) { continue; } auto regName = parser->getVal(tokens, ATE::regName); if (regName == crcUserRegName) { configuration->configId = parser->getVal(tokens, ATE::configId); configuration->crcUser = parser->getVal(tokens, ATE::regDataHex); } } co_return true; } sdbusplus::async::task MP5998::verifyImage(const uint8_t* image, size_t imageSize) { if (!co_await parseImage(image, imageSize)) { error("Image verification failed: image parsing failed"); co_return false; } if (configuration->registersData.empty()) { error("Image verification failed - no register data found"); co_return false; } if (configuration->configId == 0) { error("Image verification failed - missing config ID"); co_return false; } co_return true; } sdbusplus::async::task MP5998::checkId(PMBusCmd idCmd, uint32_t expected) { static constexpr size_t mfrIdLength = 3; static constexpr size_t mfrModelLength = 5; std::vector tbuf; std::vector rbuf; tbuf = buildByteVector(PMBusCmd::page, MPSPage::page0); if (!i2cInterface.sendReceive(tbuf, rbuf)) { error("MP5998: Failed to set page 0 for ID check"); co_return false; } size_t bufferSize; if (idCmd == PMBusCmd::mfrId) { bufferSize = statusByteLength + mfrIdLength; } else if (idCmd == PMBusCmd::mfrModel) { bufferSize = statusByteLength + mfrModelLength; } else { error("MP5998: Unsupported ID command: 0x{CMD}", "CMD", lg2::hex, static_cast(idCmd)); co_return false; } tbuf = buildByteVector(idCmd); rbuf.resize(bufferSize); if (!i2cInterface.sendReceive(tbuf, rbuf)) { error("MP5998: I2C sendReceive failed for command 0x{CMD}", "CMD", lg2::hex, static_cast(idCmd)); co_return false; } auto idBytes = std::span(rbuf).subspan(statusByteLength); uint32_t id; if (idCmd == PMBusCmd::mfrModel) { auto productBytes = idBytes.subspan(1, 4); id = bytesToInt(productBytes); } else { id = bytesToInt(idBytes); } debug("Check ID cmd {CMD}: Got={ID}, Expected={EXP}", "CMD", lg2::hex, static_cast(idCmd), "ID", lg2::hex, id, "EXP", lg2::hex, expected); co_return id == expected; } sdbusplus::async::task MP5998::unlockPasswordProtection() { constexpr uint8_t passwordUnlockBit = 0x08; constexpr uint16_t passwordData = 0x0000; std::vector tbuf; std::vector rbuf; tbuf = buildByteVector(PMBusCmd::page, MPSPage::page0); if (!i2cInterface.sendReceive(tbuf, rbuf)) { error("Failed to set page 0 for password unlock"); co_return false; } tbuf = buildByteVector(MP5998Cmd::passwordReg, passwordData); if (!i2cInterface.sendReceive(tbuf, rbuf)) { error("Failed to write password"); co_return false; } tbuf = buildByteVector(PMBusCmd::statusCML); rbuf.resize(statusByteLength); if (!i2cInterface.sendReceive(tbuf, rbuf)) { error("Failed to read STATUS_CML"); co_return false; } bool unlocked = (rbuf[0] & passwordUnlockBit) == 0; co_return unlocked; } sdbusplus::async::task MP5998::unlockWriteProtection() { std::vector tbuf; std::vector rbuf; tbuf = buildByteVector(PMBusCmd::page, MPSPage::page0); if (!i2cInterface.sendReceive(tbuf, rbuf)) { error("Failed to set page 0 for write protection unlock"); co_return false; } tbuf = buildByteVector(PMBusCmd::writeProtect, unlockData); if (!i2cInterface.sendReceive(tbuf, rbuf)) { error("Failed to unlock write protection"); co_return false; } debug("Write protection unlocked"); co_return true; } sdbusplus::async::task MP5998::programAllRegisters() { uint8_t currentPage = 0xFF; for (const auto& regData : configuration->registersData) { if (regData.page != currentPage) { std::vector tbuf = buildByteVector(PMBusCmd::page, regData.page); std::vector rbuf; if (!i2cInterface.sendReceive(tbuf, rbuf)) { error("Failed to set page {PAGE}", "PAGE", regData.page); co_return false; } currentPage = regData.page; } std::vector tbuf; std::vector rbuf; tbuf.push_back(regData.addr); for (uint8_t i = 0; i < regData.length && i < 4; ++i) { tbuf.push_back(regData.data[i]); } if (!i2cInterface.sendReceive(tbuf, rbuf)) { error("Failed to write register 0x{REG} on page {PAGE}", "REG", lg2::hex, regData.addr, "PAGE", regData.page); co_return false; } } debug("All registers programmed successfully"); co_return true; } sdbusplus::async::task MP5998::storeMTP() { std::vector tbuf; std::vector rbuf; tbuf = buildByteVector(PMBusCmd::page, MPSPage::page0); if (!i2cInterface.sendReceive(tbuf, rbuf)) { error("Failed to set page 0 for MTP store"); co_return false; } tbuf = buildByteVector(PMBusCmd::storeUserCode); if (!i2cInterface.sendReceive(tbuf, rbuf)) { error("Failed to send STORE_USER_ALL command"); co_return false; } co_return true; } sdbusplus::async::task MP5998::waitForMTPComplete() { constexpr uint16_t mtpStoreWaitmS = 1200; co_await sdbusplus::async::sleep_for( ctx, std::chrono::milliseconds(mtpStoreWaitmS)); std::vector tbuf = buildByteVector(PMBusCmd::statusCML); std::vector rbuf; rbuf.resize(statusByteLength); if (!i2cInterface.sendReceive(tbuf, rbuf)) { error("Failed to read STATUS_CML after MTP store"); co_return false; } bool eepromFault = rbuf[0] & eepromFaultBit; if (eepromFault) { error("EEPROM fault detected after MTP store"); co_return false; } co_return true; } sdbusplus::async::task MP5998::verifyCRC() { uint32_t deviceCRC{0}; // NOLINTBEGIN(clang-analyzer-core.uninitialized.Branch) bool getCRCSuccess = co_await getCRC(&deviceCRC); // NOLINTEND(clang-analyzer-core.uninitialized.Branch) if (!getCRCSuccess) { error("Failed to read CRC from device"); co_return false; } bool crcMatch = (deviceCRC == configuration->crcUser); co_return crcMatch; } sdbusplus::async::task MP5998::getCRC(uint32_t* checksum) { constexpr size_t crcLength = 2; std::vector tbuf; std::vector rbuf; tbuf = buildByteVector(PMBusCmd::page, MPSPage::page0); if (!i2cInterface.sendReceive(tbuf, rbuf)) { error("Failed to set page 0 for CRC read"); co_return false; } tbuf = buildByteVector(MP5998Cmd::crcUser); rbuf.resize(crcLength); if (!i2cInterface.sendReceive(tbuf, rbuf)) { error("Failed to read CRC_USER register"); co_return false; } *checksum = bytesToInt(rbuf); co_return true; } sdbusplus::async::task MP5998::sendRestoreMTPCommand() { std::vector tbuf; std::vector rbuf; tbuf = buildByteVector(PMBusCmd::page, MPSPage::page0); if (!i2cInterface.sendReceive(tbuf, rbuf)) { error("Failed to set page 0 for MTP restore"); co_return false; } tbuf = buildByteVector(PMBusCmd::restoreUserAll); if (!i2cInterface.sendReceive(tbuf, rbuf)) { error("Failed to send RESTORE_ALL command"); co_return false; } co_return true; } sdbusplus::async::task MP5998::checkEEPROMFaultAfterRestore() { std::vector tbuf; std::vector rbuf; tbuf = buildByteVector(PMBusCmd::page, MPSPage::page0); if (!i2cInterface.sendReceive(tbuf, rbuf)) { error("Failed to set page 0 for EEPROM fault check"); co_return false; } tbuf = buildByteVector(PMBusCmd::statusCML); rbuf.resize(1); if (!i2cInterface.sendReceive(tbuf, rbuf)) { error("Failed to read STATUS_CML register"); co_return false; } bool eepromFault = (rbuf[0] & eepromFaultBit) != 0; co_return !eepromFault; } sdbusplus::async::task MP5998::restoreMTPAndVerify() { constexpr uint16_t mtpRestoreWait = 1600; if (!co_await sendRestoreMTPCommand()) { error("Failed to send RESTORE_ALL command"); co_return false; } co_await sdbusplus::async::sleep_for( ctx, std::chrono::microseconds(mtpRestoreWait)); if (!co_await checkEEPROMFaultAfterRestore()) { error("EEPROM fault detected after MTP restore"); co_return false; } co_return true; } bool MP5998::forcedUpdateAllowed() { return true; } sdbusplus::async::task MP5998::updateFirmware(bool force) { (void)force; if (!co_await checkId(PMBusCmd::mfrId, configuration->vendorId)) { co_return false; } if (!co_await checkId(PMBusCmd::mfrModel, configuration->productId)) { co_return false; } if (!co_await unlockWriteProtection()) { co_return false; } if (!co_await programAllRegisters()) { co_return false; } if (!co_await storeMTP()) { co_return false; } if (!co_await waitForMTPComplete()) { co_return false; } if (!co_await verifyCRC()) { co_return false; } if (!co_await restoreMTPAndVerify()) { co_return false; } co_return true; } } // namespace phosphor::software::VR