Support host selector using cpld definitions

Description:
- Support host selector using cpld definitions

Design:
- Because the current structure only supports config defined by GPIO
  (Yosemite V2), but there are also have the system (Yosemite V3.5)
  gets the host-selector's selection from CPLD.

- So this commit is to extend the current configuration to use CPLD
  definitions. Also, support adding event io from the register file
  which was exported from the CLD driver.

- For example with config json below:
{
   "cpld_definitions": [
        {
            "name": "HOST_SELECTOR",
            "i2c_address": 15,
            "i2c_bus": 12,
            "register_name": "uart-selection-debug-card",
            "max_position": 4
        }
   ]
}

Dependency:
- CLD driver is required (link: https://lore.kernel.org/lkml/20230117094425.19004-1-Delphine_CC_Chiu@Wiwynn.com/)

Test Case:
- When ocp debug card uart button is pressed the position property on
  dbus is correct.

Signed-off-by: Delphine CC Chiu <Delphine_CC_Chiu@wiwynn.com>
Change-Id: I6746afa6d905bd3c681e2921c643b3cd4cb9944c
diff --git a/README.md b/README.md
index 79297c4..48dd5dc 100644
--- a/README.md
+++ b/README.md
@@ -152,6 +152,33 @@
 }
 ```
 
+### Host selector cpld config example
+
+There are also some systems that get the host selector selection from the CPLD,
+the configuration is provided below.
+
+- name - The button interface type name.
+- i2c_bus - The i2c bus of cpld
+- i2c_address - The i2c address of cpld
+- register_name - The register file name exported by CLD driver for IO event
+  listen
+- max_position - This represents the max number of hosts in the multi-host bmc
+  system.
+
+```json
+{
+  "cpld_definitions": [
+    {
+      "name": "HOST_SELECTOR",
+      "i2c_bus": 12,
+      "i2c_address": 15,
+      "register_name": "uart-selection-debug-card",
+      "max_position": 4
+    }
+  ]
+}
+```
+
 ### Serial uart mux config
 
 Similar to host selector there are multiple gpios associated with the serial
diff --git a/inc/button_config.hpp b/inc/button_config.hpp
new file mode 100644
index 0000000..7678d25
--- /dev/null
+++ b/inc/button_config.hpp
@@ -0,0 +1,27 @@
+#pragma once
+#include "config.h"
+
+#include "cpld.hpp"
+#include "gpio.hpp"
+
+#include <nlohmann/json.hpp>
+
+#include <iostream>
+
+enum class ConfigType
+{
+    gpio,
+    cpld
+};
+
+// this struct represents button interface
+struct ButtonConfig
+{
+    ConfigType type;
+    std::string formFactorName;   // name of the button interface
+    std::vector<GpioInfo> gpios;  // holds single or group gpio config
+    CpldInfo cpld;                // holds single cpld config
+    std::vector<int> fds;         // store all the fds listen io event which
+                                  // mapped with the gpio or cpld
+    nlohmann::json extraJsonInfo; // corresponding to button interface
+};
diff --git a/inc/button_factory.hpp b/inc/button_factory.hpp
index faf948d..29fb990 100644
--- a/inc/button_factory.hpp
+++ b/inc/button_factory.hpp
@@ -1,14 +1,14 @@
 #pragma once
 
+#include "button_config.hpp"
 #include "button_interface.hpp"
-#include "gpio.hpp"
 
 #include <phosphor-logging/elog-errors.hpp>
 
 #include <unordered_map>
 
 using buttonIfCreatorMethod = std::function<std::unique_ptr<ButtonIface>(
-    sdbusplus::bus_t& bus, EventPtr& event, buttonConfig& buttonCfg)>;
+    sdbusplus::bus_t& bus, EventPtr& event, ButtonConfig& buttonCfg)>;
 
 /**
  * @brief This is abstract factory for the creating phosphor buttons objects
@@ -37,7 +37,7 @@
     {
         buttonIfaceRegistry[std::string(T::getFormFactorName())] =
             [](sdbusplus::bus_t& bus, EventPtr& event,
-               buttonConfig& buttonCfg) {
+               ButtonConfig& buttonCfg) {
             return std::make_unique<T>(bus, T::getDbusObjectPath(), event,
                                        buttonCfg);
         };
@@ -49,7 +49,7 @@
     std::unique_ptr<ButtonIface> createInstance(const std::string& name,
                                                 sdbusplus::bus_t& bus,
                                                 EventPtr& event,
-                                                buttonConfig& buttonCfg)
+                                                ButtonConfig& buttonCfg)
     {
         // find matching name in the registry and call factory method.
         auto objectIter = buttonIfaceRegistry.find(name);
diff --git a/inc/button_interface.hpp b/inc/button_interface.hpp
index 9905304..b3a98cc 100644
--- a/inc/button_interface.hpp
+++ b/inc/button_interface.hpp
@@ -1,7 +1,7 @@
 #pragma once
 
+#include "button_config.hpp"
 #include "common.hpp"
-#include "gpio.hpp"
 #include "xyz/openbmc_project/Chassis/Common/error.hpp"
 
 #include <phosphor-logging/elog-errors.hpp>
@@ -10,20 +10,31 @@
 class ButtonIface
 {
   public:
-    ButtonIface(sdbusplus::bus_t& bus, EventPtr& event, buttonConfig& buttonCfg,
+    ButtonIface(sdbusplus::bus_t& bus, EventPtr& event, ButtonConfig& buttonCfg,
                 sd_event_io_handler_t handler = ButtonIface::EventHandler) :
         bus(bus),
         event(event), config(buttonCfg), callbackHandler(handler)
     {
         int ret = -1;
+        std::string configType;
 
-        // config group gpio based on the gpio defs read from the json file
-        ret = configGroupGpio(config);
+        // config group gpio or cpld based on the defs read from the json file
+        if (buttonCfg.type == ConfigType::gpio)
+        {
+            configType = "GPIO";
+            ret = configGroupGpio(config);
+        }
+        else if (buttonCfg.type == ConfigType::cpld)
+        {
+            configType = "CPLD";
+            ret = configCpld(config);
+        }
 
         if (ret < 0)
         {
             phosphor::logging::log<phosphor::logging::level::ERR>(
-                (getFormFactorType() + " : failed to config GPIO").c_str());
+                (getFormFactorType() + " : failed to config " + configType)
+                    .c_str());
             throw sdbusplus::xyz::openbmc_project::Chassis::Common::Error::
                 IOError();
         }
@@ -65,12 +76,11 @@
 
     virtual void init()
     {
-        // initialize the button io fd from the buttonConfig
-        // which has fd stored when configGroupGpio is called
-        for (auto gpioCfg : config.gpios)
+        // initialize the button io fd from the ButtonConfig
+        // which has fd stored when configGroupGpio or configCpld is called
+        for (auto fd : config.fds)
         {
             char buf;
-            int fd = gpioCfg.fd;
 
             int ret = ::read(fd, &buf, sizeof(buf));
             if (ret < 0)
@@ -86,7 +96,10 @@
                 phosphor::logging::log<phosphor::logging::level::ERR>(
                     (getFormFactorType() + " : failed to add to event loop")
                         .c_str());
-                ::closeGpio(fd);
+                if (fd > 0)
+                {
+                    ::close(fd);
+                }
                 throw sdbusplus::xyz::openbmc_project::Chassis::Common::Error::
                     IOError();
             }
@@ -100,14 +113,17 @@
      */
     virtual void deInit()
     {
-        for (auto gpioCfg : config.gpios)
+        for (auto fd : config.fds)
         {
-            ::closeGpio(gpioCfg.fd);
+            if (fd > 0)
+            {
+                ::close(fd);
+            }
         }
     }
 
     sdbusplus::bus_t& bus;
     EventPtr& event;
-    buttonConfig config;
+    ButtonConfig config;
     sd_event_io_handler_t callbackHandler;
 };
diff --git a/inc/cpld.hpp b/inc/cpld.hpp
new file mode 100644
index 0000000..206f0c6
--- /dev/null
+++ b/inc/cpld.hpp
@@ -0,0 +1,18 @@
+
+#pragma once
+#include "config.h"
+
+#include <cstdint>
+#include <string>
+
+struct ButtonConfig;
+
+struct CpldInfo
+{
+    std::string registerName;
+    uint32_t i2cAddress;
+    uint32_t i2cBus;
+    int cpldMappedFd; // io fd mapped with the cpld
+};
+
+int configCpld(ButtonConfig& buttonCfg);
diff --git a/inc/debugHostSelector_button.hpp b/inc/debugHostSelector_button.hpp
index fa13d02..ea5dd5f 100644
--- a/inc/debugHostSelector_button.hpp
+++ b/inc/debugHostSelector_button.hpp
@@ -25,7 +25,7 @@
 {
   public:
     DebugHostSelector(sdbusplus::bus_t& bus, const char* path, EventPtr& event,
-                      buttonConfig& buttonCfg) :
+                      ButtonConfig& buttonCfg) :
         sdbusplus::server::object_t<
             sdbusplus::xyz::openbmc_project::Chassis::Buttons::server::Button>(
             bus, path, action::defer_emit),
diff --git a/inc/gpio.hpp b/inc/gpio.hpp
index 9cfd57d..2351da9 100644
--- a/inc/gpio.hpp
+++ b/inc/gpio.hpp
@@ -21,6 +21,8 @@
 #include <string>
 #include <vector>
 
+struct ButtonConfig;
+
 // enum to represent gpio states
 enum class GpioState
 {
@@ -42,7 +44,7 @@
 };
 
 // this struct has the gpio config for single gpio
-struct gpioInfo
+struct GpioInfo
 {
     int fd; // io fd mapped with the gpio
     uint32_t number;
@@ -51,29 +53,21 @@
     GpioPolarity polarity;
 };
 
-// this struct represents button interface
-struct buttonConfig
-{
-    std::string formFactorName;   // name of the button interface
-    std::vector<gpioInfo> gpios;  // holds single or group gpio config
-    nlohmann::json extraJsonInfo; // corresponding to button interface
-};
-
 /**
  * @brief iterates over the list of gpios and configures gpios them
  * config which is set from gpio defs json file.
- * The fd of the configured gpio is stored in buttonConfig.gpios container
+ * The fd of the configured gpio is stored in ButtonConfig.gpios container
  * @return int returns 0 on successful config of all gpios
  */
 
-int configGroupGpio(buttonConfig& buttonCfg);
+int configGroupGpio(ButtonConfig& buttonCfg);
 
 /**
  * @brief  configures and initializes the single gpio
  * @return int returns 0 on successful config of all gpios
  */
 
-int configGpio(gpioInfo& gpioConfig);
+int configGpio(GpioInfo& gpioConfig, ButtonConfig& buttonIFConfig);
 
 uint32_t getGpioNum(const std::string& gpioPin);
 // Set gpio state based on polarity
@@ -81,6 +75,5 @@
 // Get gpio state based on polarity
 GpioState getGpioState(int fd, GpioPolarity polarity);
 
-void closeGpio(int fd);
 // global json object which holds gpio_defs.json configs
 extern nlohmann::json gpioDefs;
diff --git a/inc/hostSelector_switch.hpp b/inc/hostSelector_switch.hpp
index 8030564..806da06 100644
--- a/inc/hostSelector_switch.hpp
+++ b/inc/hostSelector_switch.hpp
@@ -28,7 +28,7 @@
 {
   public:
     HostSelector(sdbusplus::bus_t& bus, const char* path, EventPtr& event,
-                 buttonConfig& buttonCfg) :
+                 ButtonConfig& buttonCfg) :
         sdbusplus::server::object_t<sdbusplus::xyz::openbmc_project::Chassis::
                                         Buttons::server::HostSelector>(
             bus, path, action::defer_emit),
@@ -36,11 +36,14 @@
     {
         init();
         // read and store the host selector position Map
-        hsPosMap = buttonCfg.extraJsonInfo.at("host_selector_map")
-                       .get<std::map<std::string, int>>();
-        maxPosition(buttonCfg.extraJsonInfo["max_position"], true);
-        gpioLineCount = buttonCfg.gpios.size();
+        if (buttonCfg.type == ConfigType::gpio)
+        {
+            hsPosMap = buttonCfg.extraJsonInfo.at("host_selector_map")
+                           .get<std::map<std::string, int>>();
+            gpioLineCount = buttonCfg.gpios.size();
+        }
         setInitialHostSelectorValue();
+        maxPosition(buttonCfg.extraJsonInfo["max_position"], true);
         emit_object_added();
     }
 
@@ -63,6 +66,7 @@
     size_t getGpioIndex(int fd);
     void setInitialHostSelectorValue(void);
     void setHostSelectorValue(int fd, GpioState state);
+    char getValueFromFd(int fd);
 
   protected:
     size_t hostSelectorPosition = 0;
diff --git a/inc/id_button.hpp b/inc/id_button.hpp
index e48843b..24514d9 100644
--- a/inc/id_button.hpp
+++ b/inc/id_button.hpp
@@ -37,7 +37,7 @@
 {
   public:
     IDButton(sdbusplus::bus_t& bus, const char* path, EventPtr& event,
-             buttonConfig& buttonCfg) :
+             ButtonConfig& buttonCfg) :
         sdbusplus::server::object_t<
             sdbusplus::xyz::openbmc_project::Chassis::Buttons::server::ID>(
             bus, path),
diff --git a/inc/power_button.hpp b/inc/power_button.hpp
index 90076f3..549b51f 100644
--- a/inc/power_button.hpp
+++ b/inc/power_button.hpp
@@ -39,7 +39,7 @@
 {
   public:
     PowerButton(sdbusplus::bus_t& bus, const char* path, EventPtr& event,
-                buttonConfig& buttonCfg) :
+                ButtonConfig& buttonCfg) :
         sdbusplus::server::object_t<
             sdbusplus::xyz::openbmc_project::Chassis::Buttons::server::Power>(
             bus, path),
diff --git a/inc/reset_button.hpp b/inc/reset_button.hpp
index 925bed1..918d0de 100644
--- a/inc/reset_button.hpp
+++ b/inc/reset_button.hpp
@@ -37,7 +37,7 @@
 {
   public:
     ResetButton(sdbusplus::bus_t& bus, const char* path, EventPtr& event,
-                buttonConfig& buttonCfg) :
+                ButtonConfig& buttonCfg) :
         sdbusplus::server::object_t<
             sdbusplus::xyz::openbmc_project::Chassis::Buttons::server::Reset>(
             bus, path),
diff --git a/inc/serial_uart_mux.hpp b/inc/serial_uart_mux.hpp
index c1ad166..6898e8f 100644
--- a/inc/serial_uart_mux.hpp
+++ b/inc/serial_uart_mux.hpp
@@ -23,7 +23,7 @@
 {
   public:
     SerialUartMux(sdbusplus::bus_t& bus, [[maybe_unused]] const char* path,
-                  EventPtr& event, buttonConfig& buttonCfg) :
+                  EventPtr& event, ButtonConfig& buttonCfg) :
         ButtonIface(bus, event, buttonCfg)
     {
         init();
@@ -77,6 +77,6 @@
   protected:
     size_t gpioLineCount;
     std::unique_ptr<sdbusplus::bus::match_t> hostPositionChanged;
-    gpioInfo debugCardPresentGpio;
+    GpioInfo debugCardPresentGpio;
     std::unordered_map<size_t, size_t> serialUartMuxMap;
 };
diff --git a/meson.build b/meson.build
index db7eee8..4d1d370 100644
--- a/meson.build
+++ b/meson.build
@@ -63,6 +63,7 @@
 
 sources_buttons = [
     'src/gpio.cpp',
+    'src/cpld.cpp',
     'src/hostSelector_switch.cpp',
     'src/debugHostSelector_button.cpp',
     'src/serial_uart_mux.cpp',
diff --git a/src/cpld.cpp b/src/cpld.cpp
new file mode 100644
index 0000000..af1bbd9
--- /dev/null
+++ b/src/cpld.cpp
@@ -0,0 +1,38 @@
+
+#include "config.h"
+
+#include "button_config.hpp"
+
+#include <error.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include <phosphor-logging/lg2.hpp>
+
+const std::string cpldDev = "/sys/bus/i2c/devices/";
+
+std::string getCpldDevPath(const CpldInfo& info)
+{
+    std::stringstream devPath;
+    devPath << cpldDev << info.i2cBus << "-" << std::hex << std::setw(4)
+            << std::setfill('0') << info.i2cAddress << "/" << info.registerName;
+    return devPath.str();
+}
+
+int configCpld(ButtonConfig& buttonIFConfig)
+{
+    std::string devPath = getCpldDevPath(buttonIFConfig.cpld);
+
+    auto fd = ::open(devPath.c_str(), O_RDONLY | O_NONBLOCK);
+
+    if (fd < 0)
+    {
+        lg2::error("Open {PATH} error: {ERROR}", "PATH", devPath, "ERROR",
+                   errno);
+        return -1;
+    }
+
+    buttonIFConfig.cpld.cpldMappedFd = fd;
+    buttonIFConfig.fds.push_back(fd);
+    return 0;
+}
diff --git a/src/gpio.cpp b/src/gpio.cpp
index 0a16ce5..a946aa3 100644
--- a/src/gpio.cpp
+++ b/src/gpio.cpp
@@ -16,7 +16,7 @@
 
 #include "config.h"
 
-#include "gpio.hpp"
+#include "button_config.hpp"
 
 #include <error.h>
 #include <fcntl.h>
@@ -83,13 +83,6 @@
                               : (GpioState::deassert);
     return gpioState;
 }
-void closeGpio(int fd)
-{
-    if (fd > 0)
-    {
-        ::close(fd);
-    }
-}
 
 uint32_t getGpioBase()
 {
@@ -134,14 +127,14 @@
     return getGpioBase() + offset;
 }
 
-int configGroupGpio(buttonConfig& buttonIFConfig)
+int configGroupGpio(ButtonConfig& buttonIFConfig)
 {
     int result = 0;
     // iterate the list of gpios from the button interface config
     // and initialize them
     for (auto& gpioCfg : buttonIFConfig.gpios)
     {
-        result = configGpio(gpioCfg);
+        result = configGpio(gpioCfg, buttonIFConfig);
         if (result < 0)
         {
             lg2::error("{NAME}: Error configuring gpio-{NUM}: {RESULT}", "NAME",
@@ -155,7 +148,7 @@
     return result;
 }
 
-int configGpio(gpioInfo& gpioConfig)
+int configGpio(GpioInfo& gpioConfig, ButtonConfig& buttonIFConfig)
 {
     auto gpioNum = gpioConfig.number;
     auto gpioDirection = gpioConfig.direction;
@@ -300,6 +293,7 @@
     }
 
     gpioConfig.fd = fd;
+    buttonIFConfig.fds.push_back(fd);
 
     return 0;
 }
diff --git a/src/hostSelector_switch.cpp b/src/hostSelector_switch.cpp
index ce168d0..81bd247 100644
--- a/src/hostSelector_switch.cpp
+++ b/src/hostSelector_switch.cpp
@@ -37,38 +37,60 @@
     }
     return INVALID_INDEX;
 }
-void HostSelector::setInitialHostSelectorValue()
+
+char HostSelector::getValueFromFd(int fd)
 {
     char buf;
-    for (size_t index = 0; index < gpioLineCount; index++)
+    auto result = ::lseek(fd, 0, SEEK_SET);
+
+    if (result < 0)
     {
-        auto result = ::lseek(config.gpios[index].fd, 0, SEEK_SET);
+        throw sdbusplus::xyz::openbmc_project::Chassis::Common::Error::
+            IOError();
+    }
 
-        if (result < 0)
-        {
-            lg2::error("{TYPE}: Gpio fd lseek error: {ERROR}", "TYPE",
-                       getFormFactorType(), "ERROR", errno);
-            throw sdbusplus::xyz::openbmc_project::Chassis::Common::Error::
-                IOError();
-        }
+    result = ::read(fd, &buf, sizeof(buf));
+    if (result < 0)
+    {
+        throw sdbusplus::xyz::openbmc_project::Chassis::Common::Error::
+            IOError();
+    }
+    return buf;
+}
 
-        result = ::read(config.gpios[index].fd, &buf, sizeof(buf));
-        if (result < 0)
+void HostSelector::setInitialHostSelectorValue()
+{
+    size_t hsPosMapped = 0;
+
+    try
+    {
+        if (config.type == ConfigType::gpio)
         {
-            lg2::error("{TYPE}: Gpio fd read error: {ERROR}", "TYPE",
-                       getFormFactorType(), "ERROR", errno);
-            throw sdbusplus::xyz::openbmc_project::Chassis::Common::Error::
-                IOError();
+            for (size_t index = 0; index < gpioLineCount; index++)
+            {
+                GpioState gpioState =
+                    (getValueFromFd(config.gpios[index].fd) == '0')
+                        ? (GpioState::deassert)
+                        : (GpioState::assert);
+                setHostSelectorValue(config.gpios[index].fd, gpioState);
+            }
+            hsPosMapped = getMappedHSConfig(hostSelectorPosition);
         }
-        GpioState gpioState = (buf == '0') ? (GpioState::deassert)
-                                           : (GpioState::assert);
-        setHostSelectorValue(config.gpios[index].fd, gpioState);
-        size_t hsPosMapped = getMappedHSConfig(hostSelectorPosition);
-        if (hsPosMapped != INVALID_INDEX)
+        else if (config.type == ConfigType::cpld)
         {
-            position(hsPosMapped, true);
+            hsPosMapped = getValueFromFd(config.cpld.cpldMappedFd) - '0';
         }
     }
+    catch (const std::exception& e)
+    {
+        lg2::error("{TYPE}: exception while reading fd : {ERROR}", "TYPE",
+                   getFormFactorType(), "ERROR", e.what());
+    }
+
+    if (hsPosMapped != INVALID_INDEX)
+    {
+        position(hsPosMapped, true);
+    }
 }
 
 void HostSelector::setHostSelectorValue(int fd, GpioState state)
@@ -98,34 +120,32 @@
 void HostSelector::handleEvent(sd_event_source* /* es */, int fd,
                                uint32_t /* revents */)
 {
-    int n = -1;
     char buf = '0';
-
-    n = ::lseek(fd, 0, SEEK_SET);
-
-    if (n < 0)
+    try
     {
-        lg2::error("{TYPE}: Gpio fd lseek error: {ERROR}", "TYPE",
-                   getFormFactorType(), "ERROR", errno);
+        buf = getValueFromFd(fd);
+    }
+    catch (const std::exception& e)
+    {
+        lg2::error("{TYPE}: exception while reading fd : {ERROR}", "TYPE",
+                   getFormFactorType(), "ERROR", e.what());
         return;
     }
 
-    n = ::read(fd, &buf, sizeof(buf));
-    if (n < 0)
+    size_t hsPosMapped = 0;
+    if (config.type == ConfigType::gpio)
     {
-        lg2::error("{TYPE}: Gpio fd read error: {ERROR}", "TYPE",
-                   getFormFactorType(), "ERROR", errno);
-        throw sdbusplus::xyz::openbmc_project::Chassis::Common::Error::
-            IOError();
+        // read the gpio state for the io event received
+        GpioState gpioState = (buf == '0') ? (GpioState::deassert)
+                                           : (GpioState::assert);
+
+        setHostSelectorValue(fd, gpioState);
+        hsPosMapped = getMappedHSConfig(hostSelectorPosition);
     }
-
-    // read the gpio state for the io event received
-    GpioState gpioState = (buf == '0') ? (GpioState::deassert)
-                                       : (GpioState::assert);
-
-    setHostSelectorValue(fd, gpioState);
-
-    size_t hsPosMapped = getMappedHSConfig(hostSelectorPosition);
+    else if (config.type == ConfigType::cpld)
+    {
+        hsPosMapped = buf - '0';
+    }
 
     if (hsPosMapped != INVALID_INDEX)
     {
diff --git a/src/main.cpp b/src/main.cpp
index ecb9402..cf38e1f 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -14,8 +14,8 @@
 // limitations under the License.
 */
 
+#include "button_config.hpp"
 #include "button_factory.hpp"
-#include "gpio.hpp"
 
 #include <nlohmann/json.hpp>
 #include <phosphor-logging/elog-errors.hpp>
@@ -24,10 +24,11 @@
 #include <fstream>
 static constexpr auto gpioDefFile = "/etc/default/obmc/gpio/gpio_defs.json";
 
-nlohmann::json gpioDefs;
-
 int main(void)
 {
+    nlohmann::json gpioDefs;
+    nlohmann::json cpldDefs;
+
     int ret = 0;
 
     lg2::info("Start Phosphor buttons service...");
@@ -50,8 +51,34 @@
     std::vector<std::unique_ptr<ButtonIface>> buttonInterfaces;
 
     std::ifstream gpios{gpioDefFile};
-    auto gpioDefJson = nlohmann::json::parse(gpios, nullptr, true);
-    gpioDefs = gpioDefJson["gpio_definitions"];
+    auto configDefJson = nlohmann::json::parse(gpios, nullptr, true);
+    gpioDefs = configDefJson["gpio_definitions"];
+    cpldDefs = configDefJson["cpld_definitions"];
+
+    // load cpld config from gpio defs json file and create button interface
+    for (const auto& cpldConfig : cpldDefs)
+    {
+        std::string formFactorName = cpldConfig["name"];
+
+        ButtonConfig buttonCfg;
+        buttonCfg.type = ConfigType::cpld;
+        buttonCfg.formFactorName = formFactorName;
+        buttonCfg.extraJsonInfo = cpldConfig;
+
+        CpldInfo cpldCfg;
+        cpldCfg.registerName = cpldConfig["register_name"];
+
+        cpldCfg.i2cAddress = cpldConfig["i2c_address"].get<int>();
+        cpldCfg.i2cBus = cpldConfig["i2c_bus"].get<int>();
+        buttonCfg.cpld = cpldCfg;
+
+        auto tempButtonIf = ButtonFactory::instance().createInstance(
+            formFactorName, bus, eventP, buttonCfg);
+        if (tempButtonIf)
+        {
+            buttonInterfaces.emplace_back(std::move(tempButtonIf));
+        }
+    }
 
     // load gpio config from gpio defs json file and create button interface
     // objects based on the button form factor type
@@ -59,9 +86,10 @@
     for (const auto& gpioConfig : gpioDefs)
     {
         std::string formFactorName = gpioConfig["name"];
-        buttonConfig buttonCfg;
+        ButtonConfig buttonCfg;
         buttonCfg.formFactorName = formFactorName;
         buttonCfg.extraJsonInfo = gpioConfig;
+        buttonCfg.type = ConfigType::gpio;
 
         /* The folloing code checks if the gpio config read
         from json file is single gpio config or group gpio config,
@@ -74,7 +102,7 @@
 
             for (const auto& config : groupGpio)
             {
-                gpioInfo gpioCfg;
+                GpioInfo gpioCfg;
                 gpioCfg.number = getGpioNum(config["pin"]);
                 gpioCfg.direction = config["direction"];
                 gpioCfg.name = config["name"];
@@ -86,7 +114,7 @@
         }
         else
         {
-            gpioInfo gpioCfg;
+            GpioInfo gpioCfg;
             gpioCfg.number = getGpioNum(gpioConfig["pin"]);
             gpioCfg.direction = gpioConfig["direction"];
             buttonCfg.gpios.push_back(gpioCfg);
diff --git a/src/serial_uart_mux.cpp b/src/serial_uart_mux.cpp
index 31c2051..ee3d5d5 100644
--- a/src/serial_uart_mux.cpp
+++ b/src/serial_uart_mux.cpp
@@ -62,7 +62,7 @@
     for (size_t uartMuxSel = 0; uartMuxSel < gpioLineCount; uartMuxSel++)
     {
         auto gpioState = GpioState::invalid;
-        gpioInfo gpioConfig = config.gpios[uartMuxSel];
+        GpioInfo gpioConfig = config.gpios[uartMuxSel];
 
         if (gpioConfig.name == SERIAL_UART_RX_GPIO)
         {