#include "lattice_xo3_cpld.hpp" #include #include #include namespace phosphor::software::cpld { sdbusplus::async::task LatticeXO3CPLD::readDeviceId() { std::vector request = {commandReadDeviceId, 0x0, 0x0, 0x0}; std::vector response = {0, 0, 0, 0}; if (!i2cInterface.sendReceive(request, response)) { lg2::error( "Fail to read device Id. Please check the I2C bus and address."); co_return false; } auto chipWantToUpdate = std::find_if( supportedDeviceMap.begin(), supportedDeviceMap.end(), [this](const auto& pair) { auto chipModel = getLatticeChipStr(pair.first, latticeStringType::modelString); return chipModel == this->chip; }); if (chipWantToUpdate != supportedDeviceMap.end() && chipWantToUpdate->second.deviceId == response) { if (chip.rfind("LCMXO3D", 0) == 0) { isLCMXO3D = true; if (!target.empty() && target != "CFG0" && target != "CFG1") { lg2::error("Unknown target. Only CFG0 and CFG1 are supported."); co_return false; } } lg2::debug("Device ID match with chip. Chip name: {CHIPNAME}", "CHIPNAME", chip); co_return true; } lg2::error("The device id doesn't match with chip."); co_return false; } sdbusplus::async::task LatticeXO3CPLD::eraseFlash() { std::vector request; std::vector response; if (isLCMXO3D) { /* Erase the different internal memories. The bit in YYY defines which memory is erased in Flash access mode. Bit 1=Enable 8 Erase CFG0 9 Erase CFG1 10 Erase UFM0 11 Erase UFM1 12 Erase UFM2 13 Erase UFM3 14 Erase CSEC 15 Erase USEC 16 Erase PUBKEY 17 Erase AESKEY 18 Erase FEA 19 Reserved commandEraseFlash = 0x0E, 0Y YY 00 */ if (target.empty() || target == "CFG0") { request = {commandEraseFlash, 0x00, 0x01, 0x00}; } else if (target == "CFG1") { request = {commandEraseFlash, 0x00, 0x02, 0x00}; } else { lg2::error("Error: unknown target."); co_return false; } } else { request = {commandEraseFlash, 0xC, 0x0, 0x0}; } if (!i2cInterface.sendReceive(request, response)) { lg2::error("Failed to send erase flash request."); co_return false; } if (!(co_await waitBusyAndVerify())) { lg2::error("Wait busy and verify fail"); co_return false; } co_await sdbusplus::async::sleep_for(ctx, waitBusyTime); co_return true; } sdbusplus::async::task LatticeXO3CPLD::writeProgramPage() { /* Program one NVCM/Flash page. Can be used to program the NVCM0/CFG or NVCM1/UFM. */ size_t iterSize = 16; for (size_t i = 0; (i * iterSize) < fwInfo.cfgData.size(); i++) { size_t byteOffset = i * iterSize; double progressRate = ((double(byteOffset) / double(fwInfo.cfgData.size())) * 100); std::cout << "Update :" << std::fixed << std::dec << std::setprecision(2) << progressRate << "% \r"; uint8_t len = ((byteOffset + iterSize) < fwInfo.cfgData.size()) ? iterSize : (fwInfo.cfgData.size() - byteOffset); auto pageData = std::vector( fwInfo.cfgData.begin() + static_cast(byteOffset), fwInfo.cfgData.begin() + static_cast(byteOffset + len)); size_t retry = 0; const size_t maxWriteRetry = 10; while (retry < maxWriteRetry) { if (!(co_await programSinglePage(i, pageData))) { retry++; continue; } if (!(co_await verifySinglePage(i, pageData))) { retry++; continue; } break; } if (retry >= maxWriteRetry) { lg2::error("Program and verify page failed"); co_return false; } } if (!(co_await waitBusyAndVerify())) { lg2::error("Wait busy and verify fail"); co_return false; } co_return true; } sdbusplus::async::task LatticeXO3CPLD::readUserCode(uint32_t& userCode) { constexpr size_t resSize = 4; std::vector request = {commandReadFwVersion, 0x0, 0x0, 0x0}; std::vector response(resSize, 0); if (!i2cInterface.sendReceive(request, response)) { lg2::error("Failed to send read user code request."); co_return false; } for (size_t i = 0; i < resSize; i++) { userCode |= response.at(i) << ((3 - i) * 8); } co_return true; } sdbusplus::async::task LatticeXO3CPLD::programUserCode() { std::vector request = {commandProgramUserCode, 0x0, 0x0, 0x0}; std::vector response; for (int i = 3; i >= 0; i--) { request.push_back((fwInfo.version >> (i * 8)) & 0xFF); } if (!i2cInterface.sendReceive(request, response)) { lg2::error("Failed to send program user code request."); co_return false; } if (!(co_await waitBusyAndVerify())) { lg2::error("Wait busy and verify fail"); co_return false; } co_return true; } sdbusplus::async::task LatticeXO3CPLD::prepareUpdate(const uint8_t* image, size_t imageSize) { if (!(co_await readDeviceId())) { co_return false; } if (!jedFileParser(image, imageSize)) { lg2::error("JED file parsing failed"); co_return false; } lg2::debug("JED file parsing success"); if (!verifyChecksum()) { lg2::error("Checksum verification failed"); co_return false; } if (!isLCMXO3D) { lg2::error("is not LCMXO3D"); } co_return true; } sdbusplus::async::task LatticeXO3CPLD::doUpdate() { co_await waitBusyAndVerify(); if (!(co_await enableProgramMode())) { lg2::error("Enable program mode failed."); co_return false; } if (!(co_await eraseFlash())) { lg2::error("Erase flash failed."); co_return false; } if (!(co_await resetConfigFlash())) { lg2::error("Reset config flash failed."); co_return false; } if (!(co_await writeProgramPage())) { lg2::error("Write program page failed."); co_return false; } if (!(co_await programUserCode())) { lg2::error("Program user code failed."); co_return false; } if (!(co_await programDone())) { lg2::error("Program not done."); co_return false; } co_return true; } sdbusplus::async::task LatticeXO3CPLD::finishUpdate() { // NOLINTNEXTLINE(clang-analyzer-core.uninitialized.Branch) if (!(co_await disableConfigInterface())) { lg2::error("Disable Config Interface failed."); co_return false; } co_return true; } sdbusplus::async::task LatticeXO3CPLD::programSinglePage( uint16_t pageOffset, std::span pageData) { // Set Page Offset std::vector emptyResp(0); std::vector setPageAddrCmd = { commandSetPageAddress, 0x0, 0x0, 0x0, 0x00, 0x00, 0x00, 0x00}; setPageAddrCmd[6] = static_cast(pageOffset >> 8); // high byte setPageAddrCmd[7] = static_cast(pageOffset); // low byte // NOLINTNEXTLINE(clang-analyzer-core.uninitialized.Branch) bool success = co_await i2cInterface.sendReceive( setPageAddrCmd.data(), setPageAddrCmd.size(), nullptr, 0); if (!success) { lg2::error("Write page address failed"); co_return false; } // Write Page Data constexpr uint8_t pageCount = 1; std::vector writeCmd = {commandProgramPage, 0x0, 0x0, pageCount}; writeCmd.insert(writeCmd.end(), pageData.begin(), pageData.end()); success = co_await i2cInterface.sendReceive(writeCmd.data(), writeCmd.size(), nullptr, 0); if (!success) { lg2::error("Write page data failed"); co_return false; } co_await sdbusplus::async::sleep_for(ctx, std::chrono::microseconds(200)); if (!(co_await waitBusyAndVerify())) { lg2::error("Wait busy and verify fail"); co_return false; } co_return true; } sdbusplus::async::task LatticeXO3CPLD::verifySinglePage( uint16_t pageOffset, std::span pageData) { // Set Page Offset std::vector emptyResp(0); std::vector setPageAddrCmd = { commandSetPageAddress, 0x0, 0x0, 0x0, 0x00, 0x00, 0x00, 0x00}; setPageAddrCmd[6] = static_cast(pageOffset >> 8); // high byte setPageAddrCmd[7] = static_cast(pageOffset); // low byte if (!i2cInterface.sendReceive(setPageAddrCmd, emptyResp)) { lg2::error("Write page address failed"); co_return false; } // Read Page Data constexpr uint8_t pageCount = 1; std::vector readData(pageData.size()); std::vector readCmd = {commandReadPage, 0x0, 0x0, pageCount}; if (!i2cInterface.sendReceive(readCmd, readData)) { lg2::error("Read page data failed"); co_return false; } constexpr size_t pageSize = 16; auto mismatch_pair = std::mismatch(pageData.begin(), pageData.end(), readData.begin()); if (mismatch_pair.first != pageData.end()) { size_t idx = std::distance(pageData.begin(), mismatch_pair.first); lg2::error("Verify failed at {INDEX}", "INDEX", ((static_cast(pageSize * pageOffset)) + idx)); co_return false; } co_return true; } } // namespace phosphor::software::cpld