cpld: Virtualize CpldLatticeManager for XO3/XO5 separation

This change makes `CpldLatticeManager` a virtual base class, allowing
other classes to inherit from it. This refactoring enables a cleaner
separation of XO3 and XO5 implementations while sharing common CPLD
management logic.

Test on harma:
```
1. Check firmware info
curl -k -u root:0penBmc -X GET
https://10.10.15.8/redfish/v1/UpdateService/FirmwareInventory/Harma_MB_CPLD_5688
{
    "@odata.id": "/redfish/v1/UpdateService/FirmwareInventory/Harma_MB_CPLD_5688",
    "@odata.type": "#SoftwareInventory.v1_1_0.SoftwareInventory",
    "Description": "Unknown image",
    "Id": "Harma_MB_CPLD_5688",
    "Name": "Software Inventory",
    "Status": {
    "Health": "OK",
    "HealthRollup": "OK",
    "State": "Enabled"
    },
    "Updateable": true,
    "Version": "00000220"
}
2. Trigger Update
curl -k -u root:0penBmc \
    -H "Content-Type:multipart/form-data" \
    -X POST \
    -F UpdateParameters="{\"Targets\":[\"${targetpath}\"], \
    \"@Redfish.OperationApplyTime\":\"Immediate\"};type=application/json" \
    -F "UpdateFile=@${fwpath};type=application/octet-stream" \
    https://${bmc}/redfish/v1/UpdateService/update-multipart
{
    "@odata.id": "/redfish/v1/TaskService/Tasks/0",
    "@odata.type": "#Task.v1_4_3.Task",
    "HidePayload": false,
    "Id": "0",
    "Messages": [
    {
        "@odata.type": "#Message.v1_1_1.Message",
        "Message": "The task with Id '0' has started.",
        "MessageArgs": [
        "0"
        ],
        "MessageId": "TaskEvent.1.0.TaskStarted",
        "MessageSeverity": "OK",
        "Resolution": "None."
    }
    ],
    "Name": "Task 0",
    "Payload": {
    "HttpHeaders": [],
    "HttpOperation": "POST",
    "TargetUri": "/redfish/v1/UpdateService/update-multipart"
    },
    "PercentComplete": 0,
    "StartTime": "2025-08-13T07:22:06+00:00",
    "TaskMonitor": "/redfish/v1/TaskService/TaskMonitors/0",
    "TaskState": "Running",
    "TaskStatus": "OK"
}
3. After AC cycle check firmware info again
curl -k -u root:0penBmc -X GET
https://10.10.15.8/redfish/v1/UpdateService/FirmwareInventory/Harma_MB_CPLD_5688
{
    "@odata.id": "/redfish/v1/UpdateService/FirmwareInventory/Harma_MB_CPLD_5688",
    "@odata.type": "#SoftwareInventory.v1_1_0.SoftwareInventory",
    "Description": "Unknown image",
    "Id": "Harma_MB_CPLD_5688",
    "Name": "Software Inventory",
    "Status": {
    "Health": "OK",
    "HealthRollup": "OK",
    "State": "Enabled"
    },
    "Updateable": true,
    "Version": "00000224"
}
```

Change-Id: Ic7265dbeeb9f93d4f466cba75ca38fc86342c689
Signed-off-by: Daniel Hsu <Daniel-Hsu@quantatw.com>
diff --git a/cpld/cpld_interface.cpp b/cpld/cpld_interface.cpp
index 85d357c..f98ff51 100644
--- a/cpld/cpld_interface.cpp
+++ b/cpld/cpld_interface.cpp
@@ -1,7 +1,5 @@
 #include "cpld_interface.hpp"
 
-#include "lattice/interface.hpp"
-
 namespace phosphor::software::cpld
 {
 
diff --git a/cpld/lattice/interface.cpp b/cpld/lattice/interface.cpp
deleted file mode 100644
index b2734ce..0000000
--- a/cpld/lattice/interface.cpp
+++ /dev/null
@@ -1,59 +0,0 @@
-#include "interface.hpp"
-
-#include "lattice.hpp"
-
-#include <phosphor-logging/lg2.hpp>
-
-namespace phosphor::software::cpld
-{
-
-sdbusplus::async::task<bool> LatticeCPLD::updateFirmware(
-    bool /*force*/, const uint8_t* image, size_t imageSize,
-    std::function<bool(int)> progressCallBack)
-{
-    lg2::info("Updating Lattice CPLD firmware");
-
-    std::replace(chipname.begin(), chipname.end(), '_', '-');
-    auto cpldManager = std::make_unique<CpldLatticeManager>(
-        ctx, bus, address, image, imageSize, chipname, "CFG0", false);
-
-    co_return co_await cpldManager->updateFirmware(progressCallBack);
-}
-
-sdbusplus::async::task<bool> LatticeCPLD::getVersion(std::string& version)
-{
-    lg2::info("Getting Lattice CPLD version");
-
-    std::replace(chipname.begin(), chipname.end(), '_', '-');
-    auto cpldManager = std::make_unique<CpldLatticeManager>(
-        ctx, bus, address, nullptr, 0, chipname, "CFG0", false);
-
-    co_return co_await cpldManager->getVersion(version);
-}
-
-} // namespace phosphor::software::cpld
-
-// Factory function to create lattice CPLD device
-namespace
-{
-using namespace phosphor::software::cpld;
-
-// Register all the CPLD type with the CPLD factory
-const bool vendorRegistered = [] {
-    for (const auto& [type, info] : supportedDeviceMap)
-    {
-        auto typeStr = std::string(type);
-        CPLDFactory::instance().registerCPLD(
-            type, [info](sdbusplus::async::context& ctx,
-                         const std::string& /*chipName*/, uint16_t bus,
-                         uint8_t address) {
-                // Create and return a LatticeCPLD instance
-                // Pass the parameters to the constructor
-                return std::make_unique<LatticeCPLD>(ctx, info.chipName, bus,
-                                                     address);
-            });
-    }
-    return true;
-}();
-
-} // namespace
diff --git a/cpld/lattice/interface.hpp b/cpld/lattice/interface.hpp
deleted file mode 100644
index 67d0915..0000000
--- a/cpld/lattice/interface.hpp
+++ /dev/null
@@ -1,21 +0,0 @@
-#include "cpld/cpld_interface.hpp"
-
-namespace phosphor::software::cpld
-{
-
-class LatticeCPLD : public CPLDInterface
-{
-  public:
-    LatticeCPLD(sdbusplus::async::context& ctx, const std::string& chipname,
-                uint16_t bus, uint8_t address) :
-        CPLDInterface(ctx, chipname, bus, address)
-    {}
-
-    sdbusplus::async::task<bool> updateFirmware(
-        bool force, const uint8_t* image, size_t imageSize,
-        std::function<bool(int)> progress) final;
-
-    sdbusplus::async::task<bool> getVersion(std::string& version) final;
-};
-
-} // namespace phosphor::software::cpld
diff --git a/cpld/lattice/lattice.cpp b/cpld/lattice/lattice.cpp
deleted file mode 100644
index 2fb7c86..0000000
--- a/cpld/lattice/lattice.cpp
+++ /dev/null
@@ -1,916 +0,0 @@
-#include "lattice.hpp"
-
-#include <phosphor-logging/lg2.hpp>
-
-#include <algorithm>
-#include <cstddef>
-#include <fstream>
-#include <map>
-#include <numeric>
-#include <thread>
-#include <vector>
-
-constexpr uint8_t busyWaitmaxRetry = 30;
-constexpr uint8_t busyFlagBit = 0x80;
-constexpr std::chrono::milliseconds waitBusyTime(200);
-
-static constexpr std::string_view tagFuseQuantity = "QF";
-static constexpr std::string_view tagUserCodeHex = "UH";
-static constexpr std::string_view tagCFStart = "L000";
-static constexpr std::string_view tagData = "NOTE TAG DATA";
-static constexpr std::string_view tagUserFlashMemory = "NOTE USER MEMORY DATA";
-static constexpr std::string_view tagChecksum = "C";
-static constexpr std::string_view tagUserCode = "NOTE User Electronic";
-static constexpr std::string_view tagEbrInitData = "NOTE EBR_INIT DATA";
-static constexpr std::string_view tagEndConfig = "NOTE END CONFIG DATA";
-static constexpr std::string_view tagDevName = "NOTE DEVICE NAME";
-
-constexpr uint8_t isOK = 0;
-constexpr uint8_t isReady = 0;
-constexpr uint8_t busyOrReadyBit = 4;
-constexpr uint8_t failOrOKBit = 5;
-
-enum cpldI2cCmd
-{
-    commandEraseFlash = 0x0E,
-    commandDisableConfigInterface = 0x26,
-    commandReadStatusReg = 0x3C,
-    commandResetConfigFlash = 0x46,
-    commandProgramDone = 0x5E,
-    commandProgramPage = 0x70,
-    commandReadPage = 0x73,
-    commandEnableConfigMode = 0x74,
-    commandSetPageAddress = 0xB4,
-    commandReadFwVersion = 0xC0,
-    commandProgramUserCode = 0xC2,
-    commandReadDeviceId = 0xE0,
-    commandReadBusyFlag = 0xF0,
-};
-
-static uint8_t reverse_bit(uint8_t b)
-{
-    b = (b & 0xF0) >> 4 | (b & 0x0F) << 4;
-    b = (b & 0xCC) >> 2 | (b & 0x33) << 2;
-    b = (b & 0xAA) >> 1 | (b & 0x55) << 1;
-    return b;
-}
-
-std::string uint32ToHexStr(uint32_t value)
-{
-    std::ostringstream oss;
-    oss << std::setfill('0') << std::setw(8) << std::hex << std::uppercase
-        << value;
-    return oss.str();
-}
-
-bool CpldLatticeManager::jedFileParser()
-{
-    enum class ParseState
-    {
-        none,
-        cfg,
-        endCfg,
-        ufm,
-        checksum,
-        userCode
-    };
-    ParseState state = ParseState::none;
-
-    if (image == nullptr || imageSize == 0)
-    {
-        lg2::error(
-            "Error: JED file is empty or not found. Please check the file.");
-        return false;
-    }
-
-    std::string content(reinterpret_cast<const char*>(image), imageSize);
-    std::istringstream iss(content);
-    std::string line;
-
-    auto pushPage = [](std::string& line, std::vector<uint8_t>& sector) {
-        if (line[0] == '0' || line[0] == '1')
-        {
-            while (line.size() >= 8)
-            {
-                try
-                {
-                    sector.push_back(static_cast<uint8_t>(
-                        std::stoi(line.substr(0, 8), 0, 2)));
-                    line.erase(0, 8);
-                }
-                catch (...)
-                {
-                    break;
-                }
-            }
-        }
-    };
-
-    while (getline(iss, line))
-    {
-        if (!line.empty() && line.back() == '\r')
-        {
-            line.pop_back();
-        }
-        if (line.empty())
-        {
-            continue;
-        }
-
-        if (line.starts_with(tagFuseQuantity))
-        {
-            ssize_t numberSize = static_cast<ssize_t>(line.find('*')) -
-                                 static_cast<ssize_t>(line.find('F')) - 1;
-            if (numberSize > 0)
-            {
-                fwInfo.fuseQuantity = std::stoul(
-                    line.substr(tagFuseQuantity.length(), numberSize));
-                lg2::debug("fuseQuantity Size = {QFSIZE}", "QFSIZE",
-                           fwInfo.fuseQuantity);
-            }
-        }
-        else if (line.starts_with(tagCFStart) ||
-                 line.starts_with(tagEbrInitData))
-        {
-            state = ParseState::cfg;
-            continue;
-        }
-        else if (line.starts_with(tagEndConfig))
-        {
-            state = ParseState::endCfg;
-            continue;
-        }
-        else if (line.starts_with(tagUserFlashMemory) ||
-                 line.starts_with(tagData))
-        {
-            state = ParseState::ufm;
-            continue;
-        }
-        else if (line.starts_with(tagUserCode))
-        {
-            state = ParseState::userCode;
-            continue;
-        }
-        else if (line.starts_with(tagChecksum))
-        {
-            state = ParseState::checksum;
-        }
-        else if (line.starts_with(tagDevName))
-        {
-            lg2::debug("{DEVNAME}", "DEVNAME", line);
-            if (line.find(chip) == std::string::npos)
-            {
-                lg2::debug("STOP UPDATING: The image does not match the chip.");
-                return -1;
-            }
-        }
-
-        switch (state)
-        {
-            case ParseState::cfg:
-                pushPage(line, fwInfo.cfgData);
-                break;
-            case ParseState::endCfg:
-                pushPage(line, sumOnly);
-                break;
-            case ParseState::ufm:
-                pushPage(line, fwInfo.ufmData);
-                break;
-            case ParseState::checksum:
-                if (line.size() > 1)
-                {
-                    state = ParseState::none;
-                    ssize_t numberSize =
-                        static_cast<ssize_t>(line.find('*')) -
-                        static_cast<ssize_t>(line.find('C')) - 1;
-                    if (numberSize <= 0)
-                    {
-                        lg2::debug("Error in parsing checksum");
-                        return -1;
-                    }
-                    static constexpr auto start = tagChecksum.length();
-                    std::istringstream iss(line.substr(start, numberSize));
-                    iss >> std::hex >> fwInfo.checksum;
-                    lg2::debug("Checksum = 0x{CHECKSUM}", "CHECKSUM",
-                               fwInfo.checksum);
-                }
-                break;
-            case ParseState::userCode:
-                if (line.starts_with(tagUserCodeHex))
-                {
-                    state = ParseState::none;
-                    ssize_t numberSize =
-                        static_cast<ssize_t>(line.find('*')) -
-                        static_cast<ssize_t>(line.find('H')) - 1;
-                    if (numberSize <= 0)
-                    {
-                        lg2::debug("Error in parsing usercode");
-                        return -1;
-                    }
-                    std::istringstream iss(
-                        line.substr(tagUserCodeHex.length(), numberSize));
-                    iss >> std::hex >> fwInfo.version;
-                    lg2::debug("UserCode = 0x{USERCODE}", "USERCODE",
-                               fwInfo.version);
-                }
-                break;
-            default:
-                break;
-        }
-    }
-
-    lg2::debug("CFG Size = {CFGSIZE}", "CFGSIZE", fwInfo.cfgData.size());
-    if (!fwInfo.ufmData.empty())
-    {
-        lg2::debug("userFlashMemory size = {UFMSIZE}", "UFMSIZE",
-                   fwInfo.ufmData.size());
-    }
-
-    return true;
-}
-
-bool CpldLatticeManager::verifyChecksum()
-{
-    uint32_t calculated = 0U;
-    auto addByte = [](uint32_t sum, uint8_t byte) {
-        return sum + reverse_bit(byte);
-    };
-
-    calculated = std::accumulate(fwInfo.cfgData.begin(), fwInfo.cfgData.end(),
-                                 calculated, addByte);
-    calculated =
-        std::accumulate(sumOnly.begin(), sumOnly.end(), calculated, addByte);
-    calculated = std::accumulate(fwInfo.ufmData.begin(), fwInfo.ufmData.end(),
-                                 calculated, addByte);
-
-    lg2::debug("Calculated checksum = {CALCULATED}", "CALCULATED", lg2::hex,
-               calculated);
-    lg2::debug("Checksum from JED file = {JEDFILECHECKSUM}", "JEDFILECHECKSUM",
-               lg2::hex, fwInfo.checksum);
-
-    if (fwInfo.checksum != (calculated & 0xFFFF))
-    {
-        lg2::error("JED file checksum compare fail, "
-                   "Calculated checksum = {CALCULATED}, "
-                   "Checksum from JED file = {JEDFILECHECKSUM}",
-                   "CALCULATED", lg2::hex, calculated, "JEDFILECHECKSUM",
-                   lg2::hex, fwInfo.checksum);
-        return false;
-    }
-
-    lg2::debug("JED file checksum compare success");
-    return true;
-}
-
-sdbusplus::async::task<bool> CpldLatticeManager::readDeviceId()
-{
-    std::vector<uint8_t> request = {commandReadDeviceId, 0x0, 0x0, 0x0};
-    constexpr size_t resSize = 4;
-    std::vector<uint8_t> readData(resSize, 0);
-    bool success = co_await i2cInterface.sendReceive(
-        request.data(), request.size(), readData.data(), resSize);
-
-    if (!success)
-    {
-        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) {
-                         return pair.second.chipName == this->chip;
-                     });
-
-    if (chipWantToUpdate != supportedDeviceMap.end() &&
-        chipWantToUpdate->second.deviceId == readData)
-    {
-        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<bool> CpldLatticeManager::enableProgramMode()
-{
-    std::vector<uint8_t> request = {commandEnableConfigMode, 0x08, 0x0, 0x0};
-    std::vector<uint8_t> response;
-
-    if (!i2cInterface.sendReceive(request, response))
-    {
-        lg2::error("Failed to send enable program mode 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<bool> CpldLatticeManager::eraseFlash()
-{
-    std::vector<uint8_t> request;
-    std::vector<uint8_t> 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<bool> CpldLatticeManager::resetConfigFlash()
-{
-    std::vector<uint8_t> request;
-    std::vector<uint8_t> response;
-    if (isLCMXO3D)
-    {
-        /*
-        Set Page Address pointer to the
-        beginning of the different internal
-        Flash sectors. The bit in YYYY
-        defines which sector is selected.
-        Bit Flash sector selected
-        8 CFG0
-        9 CFG1
-        10 FEA
-        11 PUBKEY
-        12 AESKEY
-        13 CSEC
-        14 UFM0
-        15 UFM1
-        16 UFM2
-        17 UFM3
-        18 USEC
-        19 Reserved
-        20 Reserved
-        21 Reserved
-        22 Reserved
-        commandResetConfigFlash = 0x46, YY YY 00
-        */
-        if (target.empty() || target == "CFG0")
-        {
-            request = {commandResetConfigFlash, 0x00, 0x01, 0x00};
-        }
-        else if (target == "CFG1")
-        {
-            request = {commandResetConfigFlash, 0x00, 0x02, 0x00};
-        }
-        else
-        {
-            lg2::error(
-                "Error: unknown target. Only CFG0 and CFG1 are supported.");
-            co_return false;
-        }
-    }
-    else
-    {
-        request = {commandResetConfigFlash, 0x0, 0x0, 0x0};
-    }
-
-    co_return i2cInterface.sendReceive(request, response);
-}
-
-sdbusplus::async::task<bool> CpldLatticeManager::programSinglePage(
-    uint16_t pageOffset, std::span<const uint8_t> pageData)
-{
-    // Set Page Offset
-    std::vector<uint8_t> emptyResp(0);
-    std::vector<uint8_t> setPageAddrCmd = {
-        commandSetPageAddress, 0x0, 0x0, 0x0, 0x00, 0x00, 0x00, 0x00};
-    setPageAddrCmd[6] = static_cast<uint8_t>(pageOffset >> 8); // high byte
-    setPageAddrCmd[7] = static_cast<uint8_t>(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<uint8_t> 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<bool> CpldLatticeManager::verifySinglePage(
-    uint16_t pageOffset, std::span<const uint8_t> pageData)
-{
-    // Set Page Offset
-    std::vector<uint8_t> emptyResp(0);
-    std::vector<uint8_t> setPageAddrCmd = {
-        commandSetPageAddress, 0x0, 0x0, 0x0, 0x00, 0x00, 0x00, 0x00};
-    setPageAddrCmd[6] = static_cast<uint8_t>(pageOffset >> 8); // high byte
-    setPageAddrCmd[7] = static_cast<uint8_t>(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<uint8_t> readData(pageData.size());
-    std::vector<uint8_t> 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<size_t>(pageSize * pageOffset)) + idx));
-        co_return false;
-    }
-
-    co_return true;
-}
-
-sdbusplus::async::task<bool> CpldLatticeManager::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<uint8_t>(
-            fwInfo.cfgData.begin() + static_cast<std::ptrdiff_t>(byteOffset),
-            fwInfo.cfgData.begin() +
-                static_cast<std::ptrdiff_t>(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<bool> CpldLatticeManager::programUserCode()
-{
-    std::vector<uint8_t> request = {commandProgramUserCode, 0x0, 0x0, 0x0};
-    std::vector<uint8_t> 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<bool> CpldLatticeManager::programDone()
-{
-    std::vector<uint8_t> request = {commandProgramDone, 0x0, 0x0, 0x0};
-    std::vector<uint8_t> response;
-
-    if (!i2cInterface.sendReceive(request, response))
-    {
-        lg2::error("Failed to send program done request.");
-        co_return false;
-    }
-
-    if (!(co_await waitBusyAndVerify()))
-    {
-        lg2::error("Wait busy and verify fail");
-        co_return false;
-    }
-
-    co_return true;
-}
-
-sdbusplus::async::task<bool> CpldLatticeManager::disableConfigInterface()
-{
-    std::vector<uint8_t> request = {commandDisableConfigInterface, 0x0, 0x0};
-    std::vector<uint8_t> response;
-    co_return i2cInterface.sendReceive(request, response);
-}
-
-sdbusplus::async::task<bool> CpldLatticeManager::waitBusyAndVerify()
-{
-    uint8_t retry = 0;
-
-    while (retry <= busyWaitmaxRetry)
-    {
-        uint8_t busyFlag = 0xff;
-
-        auto readBusyFlagResult = co_await readBusyFlag(busyFlag);
-        if (!readBusyFlagResult)
-        {
-            lg2::error("Fail to read busy flag.");
-            co_return false;
-        }
-
-        if (busyFlag & busyFlagBit)
-        {
-            co_await sdbusplus::async::sleep_for(ctx, waitBusyTime);
-            retry++;
-            if (retry > busyWaitmaxRetry)
-            {
-                lg2::error(
-                    "Status Reg : Busy! Please check the I2C bus and address.");
-                co_return false;
-            }
-        }
-        else
-        {
-            break;
-        }
-    } // while loop busy check
-
-    // Check out status reg
-    auto statusReg = std::make_unique<uint8_t>(0xff);
-
-    if (!(co_await readStatusReg(*statusReg)))
-    {
-        lg2::error("Fail to read status register.");
-        co_return false;
-    }
-
-    if (((*statusReg >> busyOrReadyBit) & 1) == isReady &&
-        ((*statusReg >> failOrOKBit) & 1) == isOK)
-    {
-        lg2::debug("Status Reg : OK");
-        co_return true;
-    }
-
-    lg2::error("Status Reg : Fail! Please check the I2C bus and address.");
-    co_return false;
-}
-
-sdbusplus::async::task<bool> CpldLatticeManager::readBusyFlag(uint8_t& busyFlag)
-{
-    constexpr size_t resSize = 1;
-    std::vector<uint8_t> request = {commandReadBusyFlag, 0x0, 0x0, 0x0};
-    std::vector<uint8_t> response(resSize, 0);
-
-    auto success = i2cInterface.sendReceive(request, response);
-    if (!success && response.size() != resSize)
-    {
-        co_return false;
-    }
-    busyFlag = response.at(0);
-    co_return true;
-}
-
-sdbusplus::async::task<bool> CpldLatticeManager::readStatusReg(
-    uint8_t& statusReg)
-{
-    std::vector<uint8_t> request = {commandReadStatusReg, 0x0, 0x0, 0x0};
-    std::vector<uint8_t> response(4, 0);
-
-    if (!i2cInterface.sendReceive(request, response))
-    {
-        lg2::error("Failed to send read status register request.");
-        co_return false;
-    }
-    /*
-    Read Status Register
-    [LSC_READ_STATUS]
-    0x3C 00 00 00 N/A YY YY YY YY Bit 1 0
-    12 Busy Ready
-    13 Fail OK
-    */
-    statusReg = response.at(2);
-    co_return true;
-}
-
-sdbusplus::async::task<bool> CpldLatticeManager::readUserCode(
-    uint32_t& userCode)
-{
-    constexpr size_t resSize = 4;
-    std::vector<uint8_t> request = {commandReadFwVersion, 0x0, 0x0, 0x0};
-    std::vector<uint8_t> 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<bool> CpldLatticeManager::XO2XO3FamilyUpdate(
-    std::function<bool(int)> progressCallBack)
-{
-    if (progressCallBack == nullptr)
-    {
-        lg2::error("Error: progressCallBack is null.");
-        co_return false;
-    }
-
-    if (!(co_await readDeviceId()))
-    {
-        co_return false;
-    }
-    progressCallBack(10);
-
-    if (!jedFileParser())
-    {
-        lg2::error("JED file parsing failed");
-        co_return false;
-    }
-    progressCallBack(15);
-
-    if (!verifyChecksum())
-    {
-        lg2::error("Checksum verification failed");
-        co_return false;
-    }
-    progressCallBack(20);
-
-    if (!isLCMXO3D)
-    {
-        lg2::error("is not LCMXO3D");
-    }
-
-    lg2::debug("Starts to update ...");
-    lg2::debug("Enable program mode.");
-    progressCallBack(25);
-
-    co_await waitBusyAndVerify();
-
-    if (!(co_await enableProgramMode()))
-    {
-        lg2::error("Enable program mode failed.");
-        co_return false;
-    }
-    progressCallBack(30);
-
-    lg2::debug("Erase flash.");
-    if (!(co_await eraseFlash()))
-    {
-        lg2::error("Erase flash failed.");
-        co_return false;
-    }
-    progressCallBack(40);
-
-    lg2::debug("Reset config flash.");
-    if (!(co_await resetConfigFlash()))
-    {
-        lg2::error("Reset config flash failed.");
-        co_return false;
-    }
-    progressCallBack(50);
-
-    lg2::debug("Write program page ...");
-    if (!(co_await writeProgramPage()))
-    {
-        lg2::error("Write program page failed.");
-        co_return false;
-    }
-    lg2::debug("Write program page done.");
-    progressCallBack(60);
-
-    lg2::debug("Program user code.");
-    if (!(co_await programUserCode()))
-    {
-        lg2::error("Program user code failed.");
-        co_return false;
-    }
-    progressCallBack(70);
-
-    if (!(co_await programDone()))
-    {
-        lg2::error("Program not done.");
-        co_return false;
-    }
-    progressCallBack(80);
-
-    lg2::debug("Disable config interface.");
-    if (!(co_await disableConfigInterface()))
-    {
-        lg2::error("Disable Config Interface failed.");
-        co_return false;
-    }
-    progressCallBack(90);
-
-    lg2::debug("Update completed!");
-
-    co_return true;
-}
-
-sdbusplus::async::task<bool> CpldLatticeManager::updateFirmware(
-    std::function<bool(int)> progressCallBack)
-{
-    co_return co_await XO2XO3FamilyUpdate(progressCallBack);
-}
-
-sdbusplus::async::task<bool> CpldLatticeManager::getVersion(
-    std::string& version)
-{
-    auto userCode = std::make_unique<uint32_t>(0);
-
-    if (target.empty())
-    {
-        if (!(co_await readUserCode(*userCode)))
-        {
-            lg2::error("Read usercode failed.");
-            co_return false;
-        }
-
-        lg2::debug("CPLD version: {VERSION}", "VERSION", *userCode);
-    }
-    else if (target == "CFG0" || target == "CFG1")
-    {
-        isLCMXO3D = true;
-        co_await waitBusyAndVerify();
-
-        if (!(co_await enableProgramMode()))
-        {
-            lg2::error("Enable program mode failed.");
-            co_return false;
-        }
-
-        if (!(co_await resetConfigFlash()))
-        {
-            lg2::error("Reset config flash failed.");
-            co_return false;
-        }
-
-        if (!(co_await readUserCode(*userCode)))
-        {
-            lg2::error("Read usercode failed.");
-            co_return false;
-        }
-
-        if (!(co_await programDone()))
-        {
-            lg2::error("Program not done.");
-            co_return false;
-        }
-
-        if (!(co_await disableConfigInterface()))
-        {
-            lg2::error("Disable Config Interface failed.");
-            co_return false;
-        }
-
-        lg2::debug("CPLD {TARGET} version: {VERSION}", "TARGET", target,
-                   "VERSION", *userCode);
-    }
-    else
-    {
-        lg2::error("Error: unknown target.");
-        co_return false;
-    }
-
-    if (*userCode == 0)
-    {
-        lg2::error("User code is zero, cannot get version.");
-        co_return false;
-    }
-    version = uint32ToHexStr(*userCode);
-    co_return true;
-}
diff --git a/cpld/lattice/lattice.hpp b/cpld/lattice/lattice.hpp
deleted file mode 100644
index acb13f8..0000000
--- a/cpld/lattice/lattice.hpp
+++ /dev/null
@@ -1,85 +0,0 @@
-#pragma once
-#include "common/include/i2c/i2c.hpp"
-
-#include <chrono>
-#include <iostream>
-#include <string_view>
-#include <utility>
-
-struct cpldInfo
-{
-    std::string chipName;
-    std::vector<uint8_t> deviceId;
-};
-
-const std::map<std::string, cpldInfo> supportedDeviceMap = {
-    {"LatticeLCMXO2_4000HCFirmware",
-     {"LCMXO2-4000HC", {0x01, 0x2b, 0xc0, 0x43}}},
-    {"LatticeLCMXO3LF_2100CFirmware",
-     {"LCMXO3LF-2100C", {0x61, 0x2b, 0xb0, 0x43}}},
-    {"LatticeLCMXO3LF_4300CFirmware",
-     {"LCMXO3LF-4300C", {0x61, 0x2b, 0xc0, 0x43}}},
-    {"LatticeLCMXO3D_4300Firmware", {"LCMXO3D-4300", {0x01, 0x2e, 0x20, 0x43}}},
-    {"LatticeLCMXO3D_9400Firmware", {"LCMXO3D-9400", {0x21, 0x2e, 0x30, 0x43}}},
-};
-
-struct cpldI2cInfo
-{
-    unsigned long int fuseQuantity;
-    unsigned int* userFlashMemory;
-    unsigned int version;
-    unsigned int checksum;
-    std::vector<uint8_t> cfgData;
-    std::vector<uint8_t> ufmData;
-};
-
-class CpldLatticeManager
-{
-  public:
-    CpldLatticeManager(sdbusplus::async::context& ctx, const uint16_t bus,
-                       const uint8_t address, const uint8_t* image,
-                       size_t imageSize, const std::string& chip,
-                       const std::string& target, const bool debugMode) :
-        ctx(ctx), image(image), imageSize(imageSize), chip(chip),
-        target(target), debugMode(debugMode),
-        i2cInterface(phosphor::i2c::I2C(bus, address))
-    {}
-    sdbusplus::async::task<bool> updateFirmware(
-        std::function<bool(int)> progressCallBack);
-    sdbusplus::async::task<bool> getVersion(std::string& version);
-
-  private:
-    sdbusplus::async::context& ctx;
-    cpldI2cInfo fwInfo{};
-    const uint8_t* image;
-    size_t imageSize;
-    std::string chip;
-    std::string target;
-    std::vector<uint8_t> sumOnly;
-    bool isLCMXO3D = false;
-    bool debugMode = false;
-    phosphor::i2c::I2C i2cInterface;
-
-    sdbusplus::async::task<bool> XO2XO3FamilyUpdate(
-        std::function<bool(int)> progressCallBack);
-
-    int indexof(const char* str, const char* ptn);
-    bool jedFileParser();
-    bool verifyChecksum();
-    sdbusplus::async::task<bool> readDeviceId();
-    sdbusplus::async::task<bool> enableProgramMode();
-    sdbusplus::async::task<bool> eraseFlash();
-    sdbusplus::async::task<bool> resetConfigFlash();
-    sdbusplus::async::task<bool> writeProgramPage();
-    sdbusplus::async::task<bool> programUserCode();
-    sdbusplus::async::task<bool> programDone();
-    sdbusplus::async::task<bool> disableConfigInterface();
-    sdbusplus::async::task<bool> readBusyFlag(uint8_t& busyFlag);
-    sdbusplus::async::task<bool> readStatusReg(uint8_t& statusReg);
-    sdbusplus::async::task<bool> waitBusyAndVerify();
-    sdbusplus::async::task<bool> readUserCode(uint32_t& userCode);
-    sdbusplus::async::task<bool> programSinglePage(
-        uint16_t pageOffset, std::span<const uint8_t> pageData);
-    sdbusplus::async::task<bool> verifySinglePage(
-        uint16_t pageOffset, std::span<const uint8_t> pageData);
-};
diff --git a/cpld/lattice/lattice_base_cpld.cpp b/cpld/lattice/lattice_base_cpld.cpp
new file mode 100644
index 0000000..9055b42
--- /dev/null
+++ b/cpld/lattice/lattice_base_cpld.cpp
@@ -0,0 +1,548 @@
+#include "lattice_base_cpld.hpp"
+
+#include <algorithm>
+#include <cstddef>
+#include <fstream>
+#include <map>
+#include <numeric>
+#include <vector>
+
+namespace phosphor::software::cpld
+{
+
+constexpr uint8_t busyWaitmaxRetry = 30;
+constexpr uint8_t busyFlagBit = 0x80;
+
+static constexpr std::string_view tagFuseQuantity = "QF";
+static constexpr std::string_view tagUserCodeHex = "UH";
+static constexpr std::string_view tagCFStart = "L000";
+static constexpr std::string_view tagData = "NOTE TAG DATA";
+static constexpr std::string_view tagUserFlashMemory = "NOTE USER MEMORY DATA";
+static constexpr std::string_view tagChecksum = "C";
+static constexpr std::string_view tagUserCode = "NOTE User Electronic";
+static constexpr std::string_view tagEbrInitData = "NOTE EBR_INIT DATA";
+static constexpr std::string_view tagEndConfig = "NOTE END CONFIG DATA";
+static constexpr std::string_view tagDevName = "NOTE DEVICE NAME";
+
+constexpr uint8_t isOK = 0;
+constexpr uint8_t isReady = 0;
+constexpr uint8_t busyOrReadyBit = 4;
+constexpr uint8_t failOrOKBit = 5;
+
+static uint8_t reverse_bit(uint8_t b)
+{
+    b = (b & 0xF0) >> 4 | (b & 0x0F) << 4;
+    b = (b & 0xCC) >> 2 | (b & 0x33) << 2;
+    b = (b & 0xAA) >> 1 | (b & 0x55) << 1;
+    return b;
+}
+
+std::string LatticeBaseCPLD::uint32ToHexStr(uint32_t value)
+{
+    std::ostringstream oss;
+    oss << std::setfill('0') << std::setw(8) << std::hex << std::uppercase
+        << value;
+    return oss.str();
+}
+
+sdbusplus::async::task<bool> LatticeBaseCPLD::updateFirmware(
+    const uint8_t* image, size_t imageSize,
+    std::function<bool(int)> progressCallBack)
+{
+    if (progressCallBack == nullptr)
+    {
+        lg2::error("Error: progressCallBack is null.");
+        co_return false;
+    }
+
+    if (!image || imageSize == 0)
+    {
+        lg2::error("Error: image is null.");
+        co_return false;
+    }
+
+    lg2::debug("CPLD image size: {IMAGESIZE}", "IMAGESIZE", imageSize);
+    auto result = co_await prepareUpdate(image, imageSize);
+    if (!result)
+    {
+        lg2::error("Prepare update failed.");
+        co_return false;
+    }
+    lg2::debug("Prepare update success");
+    progressCallBack(50);
+
+    result = co_await doUpdate();
+    if (!result)
+    {
+        lg2::error("Do update failed.");
+        co_return false;
+    }
+    lg2::debug("Do update success");
+    progressCallBack(90);
+
+    result = co_await finishUpdate();
+    if (!result)
+    {
+        lg2::error("Finish update failed.");
+        co_return false;
+    }
+    lg2::debug("Finish update success");
+    progressCallBack(100);
+
+    co_return true;
+}
+
+bool LatticeBaseCPLD::jedFileParser(const uint8_t* image, size_t imageSize)
+{
+    enum class ParseState
+    {
+        none,
+        cfg,
+        endCfg,
+        ufm,
+        checksum,
+        userCode
+    };
+    ParseState state = ParseState::none;
+
+    if (image == nullptr || imageSize == 0)
+    {
+        lg2::error(
+            "Error: JED file is empty or not found. Please check the file.");
+        return false;
+    }
+
+    std::string content(reinterpret_cast<const char*>(image), imageSize);
+    std::istringstream iss(content);
+    std::string line;
+
+    auto pushPage = [](std::string& line, std::vector<uint8_t>& sector) {
+        if (line[0] == '0' || line[0] == '1')
+        {
+            while (line.size() >= 8)
+            {
+                try
+                {
+                    sector.push_back(static_cast<uint8_t>(
+                        std::stoi(line.substr(0, 8), 0, 2)));
+                    line.erase(0, 8);
+                }
+                catch (...)
+                {
+                    break;
+                }
+            }
+        }
+    };
+
+    while (getline(iss, line))
+    {
+        if (!line.empty() && line.back() == '\r')
+        {
+            line.pop_back();
+        }
+        if (line.empty())
+        {
+            continue;
+        }
+
+        if (line.starts_with(tagFuseQuantity))
+        {
+            ssize_t numberSize = static_cast<ssize_t>(line.find('*')) -
+                                 static_cast<ssize_t>(line.find('F')) - 1;
+            if (numberSize > 0)
+            {
+                fwInfo.fuseQuantity = std::stoul(
+                    line.substr(tagFuseQuantity.length(), numberSize));
+                lg2::debug("fuseQuantity Size = {QFSIZE}", "QFSIZE",
+                           fwInfo.fuseQuantity);
+            }
+        }
+        else if (line.starts_with(tagCFStart) ||
+                 line.starts_with(tagEbrInitData))
+        {
+            state = ParseState::cfg;
+            continue;
+        }
+        else if (line.starts_with(tagEndConfig))
+        {
+            state = ParseState::endCfg;
+            continue;
+        }
+        else if (line.starts_with(tagUserFlashMemory) ||
+                 line.starts_with(tagData))
+        {
+            state = ParseState::ufm;
+            continue;
+        }
+        else if (line.starts_with(tagUserCode))
+        {
+            state = ParseState::userCode;
+            continue;
+        }
+        else if (line.starts_with(tagChecksum))
+        {
+            state = ParseState::checksum;
+        }
+        else if (line.starts_with(tagDevName))
+        {
+            lg2::debug("{DEVNAME}", "DEVNAME", line);
+            if (line.find(chip) == std::string::npos)
+            {
+                lg2::debug("STOP UPDATING: The image does not match the chip.");
+                return -1;
+            }
+        }
+
+        switch (state)
+        {
+            case ParseState::cfg:
+                pushPage(line, fwInfo.cfgData);
+                break;
+            case ParseState::endCfg:
+                pushPage(line, sumOnly);
+                break;
+            case ParseState::ufm:
+                pushPage(line, fwInfo.ufmData);
+                break;
+            case ParseState::checksum:
+                if (line.size() > 1)
+                {
+                    state = ParseState::none;
+                    ssize_t numberSize =
+                        static_cast<ssize_t>(line.find('*')) -
+                        static_cast<ssize_t>(line.find('C')) - 1;
+                    if (numberSize <= 0)
+                    {
+                        lg2::debug("Error in parsing checksum");
+                        return -1;
+                    }
+                    static constexpr auto start = tagChecksum.length();
+                    std::istringstream iss(line.substr(start, numberSize));
+                    iss >> std::hex >> fwInfo.checksum;
+                    lg2::debug("Checksum = 0x{CHECKSUM}", "CHECKSUM",
+                               fwInfo.checksum);
+                }
+                break;
+            case ParseState::userCode:
+                if (line.starts_with(tagUserCodeHex))
+                {
+                    state = ParseState::none;
+                    ssize_t numberSize =
+                        static_cast<ssize_t>(line.find('*')) -
+                        static_cast<ssize_t>(line.find('H')) - 1;
+                    if (numberSize <= 0)
+                    {
+                        lg2::debug("Error in parsing usercode");
+                        return -1;
+                    }
+                    std::istringstream iss(
+                        line.substr(tagUserCodeHex.length(), numberSize));
+                    iss >> std::hex >> fwInfo.version;
+                    lg2::debug("UserCode = 0x{USERCODE}", "USERCODE",
+                               fwInfo.version);
+                }
+                break;
+            default:
+                break;
+        }
+    }
+
+    lg2::debug("CFG Size = {CFGSIZE}", "CFGSIZE", fwInfo.cfgData.size());
+    if (!fwInfo.ufmData.empty())
+    {
+        lg2::debug("userFlashMemory size = {UFMSIZE}", "UFMSIZE",
+                   fwInfo.ufmData.size());
+    }
+
+    return true;
+}
+
+bool LatticeBaseCPLD::verifyChecksum()
+{
+    uint32_t calculated = 0U;
+    auto addByte = [](uint32_t sum, uint8_t byte) {
+        return sum + reverse_bit(byte);
+    };
+
+    calculated = std::accumulate(fwInfo.cfgData.begin(), fwInfo.cfgData.end(),
+                                 calculated, addByte);
+    calculated =
+        std::accumulate(sumOnly.begin(), sumOnly.end(), calculated, addByte);
+    calculated = std::accumulate(fwInfo.ufmData.begin(), fwInfo.ufmData.end(),
+                                 calculated, addByte);
+
+    lg2::debug("Calculated checksum = {CALCULATED}", "CALCULATED", lg2::hex,
+               calculated);
+    lg2::debug("Checksum from JED file = {JEDFILECHECKSUM}", "JEDFILECHECKSUM",
+               lg2::hex, fwInfo.checksum);
+
+    if (fwInfo.checksum != (calculated & 0xFFFF))
+    {
+        lg2::error("JED file checksum compare fail, "
+                   "Calculated checksum = {CALCULATED}, "
+                   "Checksum from JED file = {JEDFILECHECKSUM}",
+                   "CALCULATED", lg2::hex, calculated, "JEDFILECHECKSUM",
+                   lg2::hex, fwInfo.checksum);
+        return false;
+    }
+
+    lg2::debug("JED file checksum compare success");
+    return true;
+}
+
+sdbusplus::async::task<bool> LatticeBaseCPLD::enableProgramMode()
+{
+    std::vector<uint8_t> request = {commandEnableConfigMode, 0x08, 0x0, 0x0};
+    std::vector<uint8_t> response;
+
+    if (!i2cInterface.sendReceive(request, response))
+    {
+        lg2::error("Failed to send enable program mode 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<bool> LatticeBaseCPLD::resetConfigFlash()
+{
+    std::vector<uint8_t> request;
+    std::vector<uint8_t> response;
+    if (isLCMXO3D)
+    {
+        /*
+        Set Page Address pointer to the
+        beginning of the different internal
+        Flash sectors. The bit in YYYY
+        defines which sector is selected.
+        Bit Flash sector selected
+        8 CFG0
+        9 CFG1
+        10 FEA
+        11 PUBKEY
+        12 AESKEY
+        13 CSEC
+        14 UFM0
+        15 UFM1
+        16 UFM2
+        17 UFM3
+        18 USEC
+        19 Reserved
+        20 Reserved
+        21 Reserved
+        22 Reserved
+        commandResetConfigFlash = 0x46, YY YY 00
+        */
+        if (target.empty() || target == "CFG0")
+        {
+            request = {commandResetConfigFlash, 0x00, 0x01, 0x00};
+        }
+        else if (target == "CFG1")
+        {
+            request = {commandResetConfigFlash, 0x00, 0x02, 0x00};
+        }
+        else
+        {
+            lg2::error(
+                "Error: unknown target. Only CFG0 and CFG1 are supported.");
+            co_return false;
+        }
+    }
+    else
+    {
+        request = {commandResetConfigFlash, 0x0, 0x0, 0x0};
+    }
+
+    co_return i2cInterface.sendReceive(request, response);
+}
+
+sdbusplus::async::task<bool> LatticeBaseCPLD::programDone()
+{
+    std::vector<uint8_t> request = {commandProgramDone, 0x0, 0x0, 0x0};
+    std::vector<uint8_t> response;
+
+    if (!i2cInterface.sendReceive(request, response))
+    {
+        lg2::error("Failed to send program done request.");
+        co_return false;
+    }
+
+    if (!(co_await waitBusyAndVerify()))
+    {
+        lg2::error("Wait busy and verify fail");
+        co_return false;
+    }
+
+    co_return true;
+}
+
+sdbusplus::async::task<bool> LatticeBaseCPLD::disableConfigInterface()
+{
+    std::vector<uint8_t> request = {commandDisableConfigInterface, 0x0, 0x0};
+    std::vector<uint8_t> response;
+    co_return i2cInterface.sendReceive(request, response);
+}
+
+sdbusplus::async::task<bool> LatticeBaseCPLD::waitBusyAndVerify()
+{
+    uint8_t retry = 0;
+
+    while (retry <= busyWaitmaxRetry)
+    {
+        uint8_t busyFlag = 0xff;
+
+        auto readBusyFlagResult = co_await readBusyFlag(busyFlag);
+        if (!readBusyFlagResult)
+        {
+            lg2::error("Fail to read busy flag.");
+            co_return false;
+        }
+
+        if (busyFlag & busyFlagBit)
+        {
+            co_await sdbusplus::async::sleep_for(ctx, waitBusyTime);
+            retry++;
+            if (retry > busyWaitmaxRetry)
+            {
+                lg2::error(
+                    "Status Reg : Busy! Please check the I2C bus and address.");
+                co_return false;
+            }
+        }
+        else
+        {
+            break;
+        }
+    } // while loop busy check
+
+    // Check out status reg
+    auto statusReg = std::make_unique<uint8_t>(0xff);
+
+    if (!(co_await readStatusReg(*statusReg)))
+    {
+        lg2::error("Fail to read status register.");
+        co_return false;
+    }
+
+    if (((*statusReg >> busyOrReadyBit) & 1) == isReady &&
+        ((*statusReg >> failOrOKBit) & 1) == isOK)
+    {
+        lg2::debug("Status Reg : OK");
+        co_return true;
+    }
+
+    lg2::error("Status Reg : Fail! Please check the I2C bus and address.");
+    co_return false;
+}
+
+sdbusplus::async::task<bool> LatticeBaseCPLD::readBusyFlag(uint8_t& busyFlag)
+{
+    constexpr size_t resSize = 1;
+    std::vector<uint8_t> request = {commandReadBusyFlag, 0x0, 0x0, 0x0};
+    std::vector<uint8_t> response(resSize, 0);
+
+    auto success = i2cInterface.sendReceive(request, response);
+    if (!success && response.size() != resSize)
+    {
+        co_return false;
+    }
+    busyFlag = response.at(0);
+    co_return true;
+}
+
+sdbusplus::async::task<bool> LatticeBaseCPLD::readStatusReg(uint8_t& statusReg)
+{
+    std::vector<uint8_t> request = {commandReadStatusReg, 0x0, 0x0, 0x0};
+    std::vector<uint8_t> response(4, 0);
+
+    if (!i2cInterface.sendReceive(request, response))
+    {
+        lg2::error("Failed to send read status register request.");
+        co_return false;
+    }
+    /*
+    Read Status Register
+    [LSC_READ_STATUS]
+    0x3C 00 00 00 N/A YY YY YY YY Bit 1 0
+    12 Busy Ready
+    13 Fail OK
+    */
+    statusReg = response.at(2);
+    co_return true;
+}
+
+sdbusplus::async::task<bool> LatticeBaseCPLD::getVersion(std::string& version)
+{
+    auto userCode = std::make_unique<uint32_t>(0);
+
+    if (target.empty())
+    {
+        if (!(co_await readUserCode(*userCode)))
+        {
+            lg2::error("Read usercode failed.");
+            co_return false;
+        }
+
+        lg2::debug("CPLD version: {VERSION}", "VERSION", *userCode);
+    }
+    else if (target == "CFG0" || target == "CFG1")
+    {
+        isLCMXO3D = true;
+        co_await waitBusyAndVerify();
+
+        if (!(co_await enableProgramMode()))
+        {
+            lg2::error("Enable program mode failed.");
+            co_return false;
+        }
+
+        if (!(co_await resetConfigFlash()))
+        {
+            lg2::error("Reset config flash failed.");
+            co_return false;
+        }
+
+        if (!(co_await readUserCode(*userCode)))
+        {
+            lg2::error("Read usercode failed.");
+            co_return false;
+        }
+
+        if (!(co_await programDone()))
+        {
+            lg2::error("Program not done.");
+            co_return false;
+        }
+
+        if (!(co_await disableConfigInterface()))
+        {
+            lg2::error("Disable Config Interface failed.");
+            co_return false;
+        }
+
+        lg2::debug("CPLD {TARGET} version: {VERSION}", "TARGET", target,
+                   "VERSION", *userCode);
+    }
+    else
+    {
+        lg2::error("Error: unknown target.");
+        co_return false;
+    }
+
+    if (*userCode == 0)
+    {
+        lg2::error("User code is zero, cannot get version.");
+        co_return false;
+    }
+    version = uint32ToHexStr(*userCode);
+    co_return true;
+}
+
+} // namespace phosphor::software::cpld
diff --git a/cpld/lattice/lattice_base_cpld.hpp b/cpld/lattice/lattice_base_cpld.hpp
new file mode 100644
index 0000000..cb45c39
--- /dev/null
+++ b/cpld/lattice/lattice_base_cpld.hpp
@@ -0,0 +1,167 @@
+#pragma once
+#include "common/include/i2c/i2c.hpp"
+
+#include <phosphor-logging/lg2.hpp>
+
+#include <chrono>
+#include <iostream>
+#include <string_view>
+#include <unordered_map>
+#include <utility>
+
+namespace phosphor::software::cpld
+{
+
+enum class latticeChip
+{
+    LCMXO2_4000HC,
+    LCMXO3LF_2100C,
+    LCMXO3LF_4300C,
+    LCMXO3D_4300,
+    LCMXO3D_9400,
+    UNSUPPORTED = -1,
+};
+
+enum class latticeStringType
+{
+    typeString,
+    modelString,
+};
+
+inline std::string getLatticeChipStr(latticeChip chip,
+                                     latticeStringType stringType)
+{
+    static const std::unordered_map<latticeChip, std::string> chipStringMap = {
+        {latticeChip::LCMXO2_4000HC, "LCMXO2_4000HC"},
+        {latticeChip::LCMXO3LF_2100C, "LCMXO3LF_2100C"},
+        {latticeChip::LCMXO3LF_4300C, "LCMXO3LF_4300C"},
+        {latticeChip::LCMXO3D_4300, "LCMXO3D_4300"},
+        {latticeChip::LCMXO3D_9400, "LCMXO3D_9400"},
+    };
+    auto chipString = chipStringMap.at(chip);
+    if (chipStringMap.find(chip) == chipStringMap.end())
+    {
+        lg2::error("Unsupported chip enum: {CHIPENUM}", "CHIPENUM", chip);
+        return "";
+    }
+
+    switch (stringType)
+    {
+        case latticeStringType::typeString:
+            return std::string("Lattice" + chipString + "Firmware");
+        case latticeStringType::modelString:
+            std::replace(chipString.begin(), chipString.end(), '_', '-');
+            return chipString;
+        default:
+            lg2::error("Unsupported string type: {STRINGTYPE}", "STRINGTYPE",
+                       stringType);
+            return "";
+    }
+};
+
+enum class latticeChipFamily
+{
+    XO2,
+    XO3,
+};
+
+struct cpldInfo
+{
+    latticeChipFamily chipFamily;
+    std::vector<uint8_t> deviceId;
+};
+
+const std::map<latticeChip, cpldInfo> supportedDeviceMap = {
+    {latticeChip::LCMXO2_4000HC,
+     {latticeChipFamily::XO2, {0x01, 0x2b, 0xc0, 0x43}}},
+    {latticeChip::LCMXO3LF_2100C,
+     {latticeChipFamily::XO3, {0x61, 0x2b, 0xb0, 0x43}}},
+    {latticeChip::LCMXO3LF_4300C,
+     {latticeChipFamily::XO3, {0x61, 0x2b, 0xc0, 0x43}}},
+    {latticeChip::LCMXO3D_4300,
+     {latticeChipFamily::XO3, {0x01, 0x2e, 0x20, 0x43}}},
+    {latticeChip::LCMXO3D_9400,
+     {latticeChipFamily::XO3, {0x21, 0x2e, 0x30, 0x43}}},
+};
+
+struct cpldI2cInfo
+{
+    unsigned long int fuseQuantity;
+    unsigned int* userFlashMemory;
+    unsigned int version;
+    unsigned int checksum;
+    std::vector<uint8_t> cfgData;
+    std::vector<uint8_t> ufmData;
+};
+
+enum cpldI2cCmd
+{
+    commandEraseFlash = 0x0E,
+    commandDisableConfigInterface = 0x26,
+    commandReadStatusReg = 0x3C,
+    commandResetConfigFlash = 0x46,
+    commandProgramDone = 0x5E,
+    commandProgramPage = 0x70,
+    commandReadPage = 0x73,
+    commandEnableConfigMode = 0x74,
+    commandSetPageAddress = 0xB4,
+    commandReadFwVersion = 0xC0,
+    commandProgramUserCode = 0xC2,
+    commandReadDeviceId = 0xE0,
+    commandReadBusyFlag = 0xF0,
+};
+
+constexpr std::chrono::milliseconds waitBusyTime(200);
+
+class LatticeBaseCPLD
+{
+  public:
+    LatticeBaseCPLD(sdbusplus::async::context& ctx, const uint16_t bus,
+                    const uint8_t address, const std::string& chip,
+                    const std::string& target, const bool debugMode) :
+        ctx(ctx), chip(chip), target(target), debugMode(debugMode),
+        i2cInterface(phosphor::i2c::I2C(bus, address))
+    {}
+    virtual ~LatticeBaseCPLD() = default;
+    LatticeBaseCPLD(const LatticeBaseCPLD&) = delete;
+    LatticeBaseCPLD& operator=(const LatticeBaseCPLD&) = delete;
+    LatticeBaseCPLD(LatticeBaseCPLD&&) noexcept = delete;
+    LatticeBaseCPLD& operator=(LatticeBaseCPLD&&) noexcept = delete;
+
+    sdbusplus::async::task<bool> updateFirmware(
+        const uint8_t* image, size_t imageSize,
+        std::function<bool(int)> progressCallBack);
+
+    sdbusplus::async::task<bool> getVersion(std::string& version);
+
+  protected:
+    sdbusplus::async::context& ctx;
+    cpldI2cInfo fwInfo{};
+    std::string chip;
+    std::string target;
+    std::vector<uint8_t> sumOnly;
+    bool isLCMXO3D = false;
+    bool debugMode = false;
+    phosphor::i2c::I2C i2cInterface;
+
+    virtual sdbusplus::async::task<bool> prepareUpdate(const uint8_t*,
+                                                       size_t) = 0;
+    virtual sdbusplus::async::task<bool> doUpdate() = 0;
+    virtual sdbusplus::async::task<bool> finishUpdate() = 0;
+
+    bool jedFileParser(const uint8_t* image, size_t imageSize);
+    bool verifyChecksum();
+    sdbusplus::async::task<bool> enableProgramMode();
+    sdbusplus::async::task<bool> resetConfigFlash();
+    sdbusplus::async::task<bool> programDone();
+    sdbusplus::async::task<bool> disableConfigInterface();
+    sdbusplus::async::task<bool> waitBusyAndVerify();
+
+  private:
+    virtual sdbusplus::async::task<bool> readUserCode(uint32_t&) = 0;
+    sdbusplus::async::task<bool> readBusyFlag(uint8_t& busyFlag);
+    sdbusplus::async::task<bool> readStatusReg(uint8_t& statusReg);
+    static std::string uint32ToHexStr(uint32_t value);
+};
+
+} // namespace phosphor::software::cpld
diff --git a/cpld/lattice/lattice_cpld_factory.cpp b/cpld/lattice/lattice_cpld_factory.cpp
new file mode 100644
index 0000000..ab0cb85
--- /dev/null
+++ b/cpld/lattice/lattice_cpld_factory.cpp
@@ -0,0 +1,91 @@
+#include "lattice_cpld_factory.hpp"
+
+#include "lattice_xo3_cpld.hpp"
+
+#include <phosphor-logging/lg2.hpp>
+
+namespace phosphor::software::cpld
+{
+
+std::unique_ptr<LatticeBaseCPLD> LatticeCPLDFactory::getLatticeCPLD()
+{
+    if (supportedDeviceMap.find(chipEnum) == supportedDeviceMap.end())
+    {
+        // invalid
+        lg2::error("Unsupported Lattice CPLD chip enum: {CHIPENUM}", "CHIPENUM",
+                   chipEnum);
+        return nullptr;
+    }
+
+    auto chipFamily = supportedDeviceMap.at(chipEnum).chipFamily;
+    auto chipModelStr =
+        getLatticeChipStr(chipEnum, latticeStringType::modelString);
+    switch (chipFamily)
+    {
+        case latticeChipFamily::XO2:
+        case latticeChipFamily::XO3:
+            return std::make_unique<LatticeXO3CPLD>(
+                CPLDInterface::ctx, CPLDInterface::bus, CPLDInterface::address,
+                chipModelStr, "CFG0", false);
+        default:
+            lg2::error("Unsupported Lattice CPLD chip family: {CHIPMODEL}",
+                       "CHIPMODEL", chipModelStr);
+            return nullptr;
+    }
+}
+
+sdbusplus::async::task<bool> LatticeCPLDFactory::updateFirmware(
+    bool /*force*/, const uint8_t* image, size_t imageSize,
+    std::function<bool(int)> progressCallBack)
+{
+    lg2::info("Updating Lattice CPLD firmware");
+    auto cpldManager = getLatticeCPLD();
+    if (cpldManager == nullptr)
+    {
+        lg2::error("CPLD manager is not initialized.");
+        co_return false;
+    }
+    co_return co_await cpldManager->updateFirmware(image, imageSize,
+                                                   progressCallBack);
+}
+
+sdbusplus::async::task<bool> LatticeCPLDFactory::getVersion(
+    std::string& version)
+{
+    lg2::info("Getting Lattice CPLD version");
+    auto cpldManager = getLatticeCPLD();
+    if (cpldManager == nullptr)
+    {
+        lg2::error("CPLD manager is not initialized.");
+        co_return false;
+    }
+    co_return co_await cpldManager->getVersion(version);
+}
+
+} // namespace phosphor::software::cpld
+
+// Factory function to create lattice CPLD device
+namespace
+{
+using namespace phosphor::software::cpld;
+
+// Register all the CPLD type with the CPLD factory
+const bool vendorRegistered = [] {
+    for (const auto& [chipEnum, info] : supportedDeviceMap)
+    {
+        auto typeStr =
+            getLatticeChipStr(chipEnum, latticeStringType::typeString);
+        CPLDFactory::instance().registerCPLD(
+            typeStr, [chipEnum](sdbusplus::async::context& ctx,
+                                const std::string& chipName, uint16_t bus,
+                                uint8_t address) {
+                // Create and return a LatticeCPLD instance
+                // Pass the parameters to the constructor
+                return std::make_unique<LatticeCPLDFactory>(
+                    ctx, chipName, chipEnum, bus, address);
+            });
+    }
+    return true;
+}();
+
+} // namespace
diff --git a/cpld/lattice/lattice_cpld_factory.hpp b/cpld/lattice/lattice_cpld_factory.hpp
new file mode 100644
index 0000000..bec61d8
--- /dev/null
+++ b/cpld/lattice/lattice_cpld_factory.hpp
@@ -0,0 +1,27 @@
+#include "cpld/cpld_interface.hpp"
+#include "cpld/lattice/lattice_base_cpld.hpp"
+
+namespace phosphor::software::cpld
+{
+
+class LatticeCPLDFactory : public CPLDInterface
+{
+  public:
+    LatticeCPLDFactory(sdbusplus::async::context& ctx,
+                       const std::string& chipName, latticeChip chipEnum,
+                       uint16_t bus, uint8_t address) :
+        CPLDInterface(ctx, chipName, bus, address), chipEnum(chipEnum)
+    {}
+
+    sdbusplus::async::task<bool> updateFirmware(
+        bool force, const uint8_t* image, size_t imageSize,
+        std::function<bool(int)> progress) final;
+
+    sdbusplus::async::task<bool> getVersion(std::string& version) final;
+
+  private:
+    std::unique_ptr<LatticeBaseCPLD> getLatticeCPLD();
+    latticeChip chipEnum;
+};
+
+} // namespace phosphor::software::cpld
diff --git a/cpld/lattice/lattice_xo3_cpld.cpp b/cpld/lattice/lattice_xo3_cpld.cpp
new file mode 100644
index 0000000..ee95ba4
--- /dev/null
+++ b/cpld/lattice/lattice_xo3_cpld.cpp
@@ -0,0 +1,384 @@
+#include "lattice_xo3_cpld.hpp"
+
+#include <phosphor-logging/lg2.hpp>
+
+#include <fstream>
+#include <vector>
+
+namespace phosphor::software::cpld
+{
+
+sdbusplus::async::task<bool> LatticeXO3CPLD::readDeviceId()
+{
+    std::vector<uint8_t> request = {commandReadDeviceId, 0x0, 0x0, 0x0};
+    std::vector<uint8_t> 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<bool> LatticeXO3CPLD::eraseFlash()
+{
+    std::vector<uint8_t> request;
+    std::vector<uint8_t> 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<bool> 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<uint8_t>(
+            fwInfo.cfgData.begin() + static_cast<std::ptrdiff_t>(byteOffset),
+            fwInfo.cfgData.begin() +
+                static_cast<std::ptrdiff_t>(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<bool> LatticeXO3CPLD::readUserCode(uint32_t& userCode)
+{
+    constexpr size_t resSize = 4;
+    std::vector<uint8_t> request = {commandReadFwVersion, 0x0, 0x0, 0x0};
+    std::vector<uint8_t> 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<bool> LatticeXO3CPLD::programUserCode()
+{
+    std::vector<uint8_t> request = {commandProgramUserCode, 0x0, 0x0, 0x0};
+    std::vector<uint8_t> 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<bool> 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<bool> 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<bool> 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<bool> LatticeXO3CPLD::programSinglePage(
+    uint16_t pageOffset, std::span<const uint8_t> pageData)
+{
+    // Set Page Offset
+    std::vector<uint8_t> emptyResp(0);
+    std::vector<uint8_t> setPageAddrCmd = {
+        commandSetPageAddress, 0x0, 0x0, 0x0, 0x00, 0x00, 0x00, 0x00};
+    setPageAddrCmd[6] = static_cast<uint8_t>(pageOffset >> 8); // high byte
+    setPageAddrCmd[7] = static_cast<uint8_t>(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<uint8_t> 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<bool> LatticeXO3CPLD::verifySinglePage(
+    uint16_t pageOffset, std::span<const uint8_t> pageData)
+{
+    // Set Page Offset
+    std::vector<uint8_t> emptyResp(0);
+    std::vector<uint8_t> setPageAddrCmd = {
+        commandSetPageAddress, 0x0, 0x0, 0x0, 0x00, 0x00, 0x00, 0x00};
+    setPageAddrCmd[6] = static_cast<uint8_t>(pageOffset >> 8); // high byte
+    setPageAddrCmd[7] = static_cast<uint8_t>(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<uint8_t> readData(pageData.size());
+    std::vector<uint8_t> 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<size_t>(pageSize * pageOffset)) + idx));
+        co_return false;
+    }
+
+    co_return true;
+}
+
+} // namespace phosphor::software::cpld
diff --git a/cpld/lattice/lattice_xo3_cpld.hpp b/cpld/lattice/lattice_xo3_cpld.hpp
new file mode 100644
index 0000000..99da64c
--- /dev/null
+++ b/cpld/lattice/lattice_xo3_cpld.hpp
@@ -0,0 +1,38 @@
+#include "lattice_base_cpld.hpp"
+
+namespace phosphor::software::cpld
+{
+
+class LatticeXO3CPLD : public LatticeBaseCPLD
+{
+  public:
+    LatticeXO3CPLD(sdbusplus::async::context& ctx, const uint16_t bus,
+                   const uint8_t address, const std::string& chip,
+                   const std::string& target, const bool debugMode) :
+        LatticeBaseCPLD(ctx, bus, address, chip, target, debugMode)
+    {}
+    ~LatticeXO3CPLD() override = default;
+    LatticeXO3CPLD(const LatticeXO3CPLD&) = delete;
+    LatticeXO3CPLD& operator=(const LatticeXO3CPLD&) = delete;
+    LatticeXO3CPLD(LatticeXO3CPLD&&) noexcept = delete;
+    LatticeXO3CPLD& operator=(LatticeXO3CPLD&&) noexcept = delete;
+
+  protected:
+    sdbusplus::async::task<bool> prepareUpdate(const uint8_t* image,
+                                               size_t imageSize) override;
+    sdbusplus::async::task<bool> doUpdate() override;
+    sdbusplus::async::task<bool> finishUpdate() override;
+
+  private:
+    sdbusplus::async::task<bool> readUserCode(uint32_t& userCode) override;
+    sdbusplus::async::task<bool> readDeviceId();
+    sdbusplus::async::task<bool> eraseFlash();
+    sdbusplus::async::task<bool> writeProgramPage();
+    sdbusplus::async::task<bool> programUserCode();
+    sdbusplus::async::task<bool> programSinglePage(
+        uint16_t pageOffset, std::span<const uint8_t> pageData);
+    sdbusplus::async::task<bool> verifySinglePage(
+        uint16_t pageOffset, std::span<const uint8_t> pageData);
+};
+
+} // namespace phosphor::software::cpld
diff --git a/cpld/meson.build b/cpld/meson.build
index 7f1a421..005bb67 100644
--- a/cpld/meson.build
+++ b/cpld/meson.build
@@ -1,6 +1,10 @@
 cpld_src = files('cpld.cpp', 'cpld_interface.cpp', 'cpld_software_manager.cpp')
 
-cpld_vendor_src = files('lattice/interface.cpp', 'lattice/lattice.cpp')
+cpld_vendor_src = files(
+    'lattice/lattice_base_cpld.cpp',
+    'lattice/lattice_cpld_factory.cpp',
+    'lattice/lattice_xo3_cpld.cpp',
+)
 
 executable(
     'phosphor-cpld-software-update',