Add serial uart mux interface class
1. This change adds the multi-host serial uart mux interface class.
In a multi-host system when the host position is changed,
then the serial uart mux is configured to enable the
respective host's serial console which is accessed via
OCP debug card.
2. Introduced two new methods in gpio.cpp
setGpioState - set state for gpio fd based on polarity
getGpiostate - get state for gpio fd based on polarity
3. Updated the readme file about details on the new gpio
configs
design : https://github.com/openbmc/docs/blob/master/designs/multihost-phosphor-buttons.md
Testing : This change is verified in yosemiteV2 platform.
Signed-off-by: Naveen Moses <naveen.mosess@hcl.com>
Change-Id: I861d70570650d7dfcab842a35bdcf63a9fdd3bd0
diff --git a/README.md b/README.md
index 77c5e8a..dbf05eb 100644
--- a/README.md
+++ b/README.md
@@ -9,7 +9,7 @@
the respective event handlers.
## Gpio defs config
- In order to monitor a button/input interface the
+In order to monitor a button/input interface the
respective gpio config details should be mentioned in the
gpio defs json file - "/etc/default/obmc/gpio/gpio_defs.json"
@@ -18,7 +18,7 @@
gpio configs associated with the specific button interface.
## example gpio def Json config
-
+```
{
"gpio_definitions": [
{
@@ -42,7 +42,7 @@
{
"name" : "HOST_SELECTOR",
- "gpio_config" : [
+ "group_gpio_config" : [
{
"pin": "AA4",
"direction": "both"
@@ -63,3 +63,157 @@
},
}
+```
+## Single gpio config
+This config is original config which can be used for configs with only single gpio
+such as power button,reset button, OCP debug card host select button.
+```
+ {
+ "name": "POWER_BUTTON",
+ "pin": "D0",
+ "direction": "both"
+ },
+```
+**Note:** this config is used by most of the other platforms so this format is kept
+as it is so that existing gpio configs do not get affected.
+
+## Group gpio config
+The following configs are related to multi-host bmc systems
+more info explained in the design:
+https://github.com/openbmc/docs/blob/master/designs/multihost-phosphor-buttons.md
+
+### Host selector gpio config example
+The host selector has four gpios associated. So the related gpios are mentioned
+in a json array named "group_gpio_config".
+
+* name - This is name of the specific gpio line
+* pin - This represents the pin number from linux dts file.
+* polarity - polarity type of the gpio
+* max_position - This represents the max number of hosts in the multi-host
+bmc system.
+* host_selector_map - This map is oem specific host position map which has how the
+value read from the host selector gpios is mapped to the respective host number.
+
+Example : The value of "7" derived from the 4 host select gpio lines are mapped
+to host position 1.
+```
+ {
+ "name" : "HOST_SELECTOR",
+
+ "group_gpio_config" : [
+ {
+ "name" : "host_select_0",
+ "pin": "AA4",
+ "direction": "both",
+ "polarity" : "active_high"
+
+ },
+ {
+ "name" : "host_select_1",
+ "pin": "AA5",
+ "direction": "both",
+ "polarity" : "active_high"
+
+ },
+ {
+ "name" : "host_select_2",
+ "pin": "AA6",
+ "direction": "both",
+ "polarity" : "active_high"
+
+ },
+ {
+ "name" : "host_select_3",
+ "pin": "AA7",
+ "direction": "both",
+ "polarity" : "active_high"
+
+ }
+ ],
+ "max_position" : 4,
+ "host_selector_map" : {
+ "6" : 0,
+ "7": 1,
+ "8": 2,
+ "9": 3,
+ "10": 4,
+ "11": 0,
+ "12": 1,
+ "13": 2,
+ "14" : 3,
+ "15" : 4
+ }
+
+ },
+```
+### Serial uart mux config
+Similar to host selector there are multiple gpios associated with the
+serial uart mux. These gpio configs are specificed as part of json array
+ "group_gpio_config".
+
+Here the serial uart mux output is accessed via OCP debug card. SO the OCP
+debug card present gpio is mentioned part of the group_gpio_config. The debug
+card present gpio is identified by its name "debug_card_present".
+
+The other gpios part of the group gpio config is serial uart MUX gpio select lines
+and serial_uart_rx line.
+
+* name - this is name of the specific gpio line
+* pin - this represents the pin number from linux dts file.
+* polarity - polarity type of the gpio
+* serial_uart_mux_map - This is the map for selected host position to the serial
+uart mux select output value
+```
+ {
+ "name" : "SERIAL_UART_MUX",
+
+ "group_gpio_config" : [
+ {
+ "name" : "serial_uart_sel_0",
+ "pin": "E0",
+ "direction": "out",
+ "polarity" : "active_high"
+ },
+ {
+ "name" : "serial_uart_sel_1",
+ "pin": "E1",
+ "direction": "out",
+ "polarity" : "active_high"
+ },
+ {
+ "name" : "serial_uart_sel_2",
+ "pin": "E2",
+ "direction": "out",
+ "polarity" : "active_high"
+ },
+ {
+ "name" : "serial_uart_sel_3",
+ "pin": "E3",
+ "direction": "out",
+ "polarity" : "active_high"
+ },
+ {
+ "name" : "serial_uart_rx",
+ "pin": "E4",
+ "direction": "out",
+ "polarity" : "active_high"
+ },
+ {
+ "name" : "debug_card_present",
+ "pin": "R3",
+ "direction": "both",
+ "polarity" : "active_high"
+
+ }
+
+ ],
+ "serial_uart_mux_map" : {
+ "0" : 4,
+ "1": 0,
+ "2": 1,
+ "3": 2,
+ "4": 3
+ }
+
+ }
+```
\ No newline at end of file
diff --git a/inc/gpio.hpp b/inc/gpio.hpp
index 6a63056..9cfd57d 100644
--- a/inc/gpio.hpp
+++ b/inc/gpio.hpp
@@ -21,10 +21,24 @@
#include <string>
#include <vector>
+// enum to represent gpio states
enum class GpioState
{
- low,
- high
+ assert,
+ deassert,
+ invalid
+};
+// enum to represent gpio polarity
+enum class GpioPolarity
+{
+ activeLow,
+ activeHigh
+};
+
+struct GPIOBufferValue
+{
+ char assert;
+ char deassert;
};
// this struct has the gpio config for single gpio
@@ -32,7 +46,9 @@
{
int fd; // io fd mapped with the gpio
uint32_t number;
+ std::string name;
std::string direction;
+ GpioPolarity polarity;
};
// this struct represents button interface
@@ -60,6 +76,11 @@
int configGpio(gpioInfo& gpioConfig);
uint32_t getGpioNum(const std::string& gpioPin);
+// Set gpio state based on polarity
+void setGpioState(int fd, GpioPolarity polarity, GpioState state);
+// 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/serial_uart_mux.hpp b/inc/serial_uart_mux.hpp
new file mode 100644
index 0000000..5c506b9
--- /dev/null
+++ b/inc/serial_uart_mux.hpp
@@ -0,0 +1,83 @@
+
+#pragma once
+#include "config.h"
+
+#include "button_factory.hpp"
+#include "button_interface.hpp"
+#include "common.hpp"
+#include "gpio.hpp"
+#include "xyz/openbmc_project/Chassis/Buttons/HostSelector/server.hpp"
+#include "xyz/openbmc_project/Chassis/Common/error.hpp"
+#include "xyz/openbmc_project/Inventory/Item/server.hpp"
+
+#include <unistd.h>
+
+#include <phosphor-logging/elog-errors.hpp>
+#include <sdbusplus/bus.hpp>
+#include <sdbusplus/bus/match.hpp>
+static constexpr std::string_view DEBUG_CARD_PRESENT_GPIO =
+ "debug_card_present";
+static constexpr std::string_view SERIAL_CONSOLE_SWITCH = "SERIAL_UART_MUX";
+
+class SerialUartMux final : public ButtonIface
+{
+ public:
+ SerialUartMux(sdbusplus::bus_t& bus, [[maybe_unused]] const char* path,
+ EventPtr& event, buttonConfig& buttonCfg) :
+ ButtonIface(bus, event, buttonCfg)
+ {
+ init();
+
+ // read the platform specific config of host number to uart mux map
+ std::unordered_map<std::string, size_t> uartMuxMapJson =
+ buttonCfg.extraJsonInfo.at("serial_uart_mux_map")
+ .get<decltype(uartMuxMapJson)>();
+ for (auto& [key, value] : uartMuxMapJson)
+ {
+ auto index = std::stoi(key);
+ serialUartMuxMap[index] = value;
+ }
+ if (buttonCfg.gpios.size() < 3)
+ {
+ throw std::runtime_error("not enough gpio configs found");
+ }
+
+ for (auto& gpio : buttonCfg.gpios)
+ {
+ if (gpio.name == DEBUG_CARD_PRESENT_GPIO)
+ {
+ debugCardPresentGpio = gpio;
+ break;
+ }
+ }
+
+ gpioLineCount = buttonCfg.gpios.size() - 1;
+ }
+
+ ~SerialUartMux()
+ {
+ deInit();
+ }
+ void init() override;
+ static const std::string_view getFormFactorName()
+ {
+ return SERIAL_CONSOLE_SWITCH;
+ }
+ static const char* getDbusObjectPath()
+ {
+ return "NO_DBUS_OBJECT";
+ }
+
+ void hostSelectorPositionChanged(sdbusplus::message_t& msg);
+ void configSerialConsoleMux(size_t position);
+ bool isOCPDebugCardPresent();
+
+ void handleEvent(sd_event_source*, int, uint32_t)
+ {}
+
+ protected:
+ size_t gpioLineCount;
+ std::unique_ptr<sdbusplus::bus::match_t> hostPositionChanged;
+ gpioInfo debugCardPresentGpio;
+ std::unordered_map<size_t, size_t> serialUartMuxMap;
+};
diff --git a/meson.build b/meson.build
index 8b726f4..6a3d319 100644
--- a/meson.build
+++ b/meson.build
@@ -20,6 +20,8 @@
'/xyz/openbmc_project/Chassis/Buttons/HostSelector')
conf_data.set_quoted('DBG_HS_DBUS_OBJECT_NAME',
'/xyz/openbmc_project/Chassis/Buttons/DebugHostSelector')
+conf_data.set_quoted('SERIAL_CONSOLE_MUX_DBUS_OBJECT_NAME',
+ '/xyz/openbmc_project/Chassis/Buttons/SerialUartMux')
conf_data.set_quoted('GPIO_BASE_LABEL_NAME', '1e780000.gpio')
conf_data.set_quoted('CHASSIS_STATE_OBJECT_NAME',
'/xyz/openbmc_project/state/chassis')
@@ -63,6 +65,7 @@
'src/gpio.cpp',
'src/hostSelector_switch.cpp',
'src/debugHostSelector_button.cpp',
+ 'src/serial_uart_mux.cpp',
'src/id_button.cpp',
'src/main.cpp',
'src/power_button.cpp',
diff --git a/src/gpio.cpp b/src/gpio.cpp
index 9f8b98d..0a16ce5 100644
--- a/src/gpio.cpp
+++ b/src/gpio.cpp
@@ -30,9 +30,59 @@
#include <fstream>
const std::string gpioDev = "/sys/class/gpio";
-
namespace fs = std::filesystem;
+std::unordered_map<GpioPolarity, GPIOBufferValue> GpioValueMap = {
+ {GpioPolarity::activeLow, {'0', '1'}},
+ {GpioPolarity::activeHigh, {'1', '0'}}};
+void setGpioState(int fd, GpioPolarity polarity, GpioState state)
+{
+ char writeBuffer;
+
+ if (state == GpioState::assert)
+ {
+ writeBuffer = GpioValueMap[polarity].assert;
+ }
+ else
+ {
+ writeBuffer = GpioValueMap[polarity].deassert;
+ }
+
+ auto result = ::write(fd, &writeBuffer, sizeof(writeBuffer));
+ if (result < 0)
+ {
+ lg2::error("GPIO write error {GPIOFD} : {ERRORNO}", "GPIOFD", fd,
+ "ERRORNO", errno);
+ }
+ return;
+}
+GpioState getGpioState(int fd, GpioPolarity polarity)
+{
+ int result = -1;
+ char readBuffer = '0';
+
+ result = ::lseek(fd, 0, SEEK_SET);
+
+ if (result < 0)
+ {
+ lg2::error("GPIO lseek error {GPIOFD}: {ERROR}", "GPIOFD", fd, "ERROR",
+ errno);
+ return GpioState::invalid;
+ }
+
+ result = ::read(fd, &readBuffer, sizeof(readBuffer));
+ if (result < 0)
+ {
+ lg2::error("GPIO read error {GPIOFD}: {ERRORNO}", "GPIOFD", fd,
+ "ERRORNO", errno);
+ throw std::runtime_error("GPIO read failed");
+ }
+ // read the gpio state for the io event received
+ GpioState gpioState = (readBuffer == GpioValueMap[polarity].assert)
+ ? (GpioState::assert)
+ : (GpioState::deassert);
+ return gpioState;
+}
void closeGpio(int fd)
{
if (fd > 0)
diff --git a/src/hostSelector_switch.cpp b/src/hostSelector_switch.cpp
index 66b79c8..efbccd2 100644
--- a/src/hostSelector_switch.cpp
+++ b/src/hostSelector_switch.cpp
@@ -61,7 +61,7 @@
IOError();
}
GpioState gpioState =
- (buf == '0') ? (GpioState::low) : (GpioState::high);
+ (buf == '0') ? (GpioState::deassert) : (GpioState::assert);
setHostSelectorValue(config.gpios[index].fd, gpioState);
size_t hsPosMapped = getMappedHSConfig(hostSelectorPosition);
if (hsPosMapped != INVALID_INDEX)
@@ -83,7 +83,7 @@
auto clr_bit = [](size_t& val, size_t n) { val &= ~(0xff & (1 << n)); };
- auto bit_op = (state == GpioState::low) ? set_bit : clr_bit;
+ auto bit_op = (state == GpioState::deassert) ? set_bit : clr_bit;
bit_op(hostSelectorPosition, pos);
return;
@@ -120,7 +120,8 @@
}
// read the gpio state for the io event received
- GpioState gpioState = (buf == '0') ? (GpioState::low) : (GpioState::high);
+ GpioState gpioState =
+ (buf == '0') ? (GpioState::deassert) : (GpioState::assert);
setHostSelectorValue(fd, gpioState);
diff --git a/src/main.cpp b/src/main.cpp
index 16ae7b0..ecb9402 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -47,7 +47,6 @@
bus, "/xyz/openbmc_project/Chassis/Buttons"};
bus.request_name("xyz.openbmc_project.Chassis.Buttons");
- //
std::vector<std::unique_ptr<ButtonIface>> buttonInterfaces;
std::ifstream gpios{gpioDefFile};
@@ -78,6 +77,10 @@
gpioInfo gpioCfg;
gpioCfg.number = getGpioNum(config["pin"]);
gpioCfg.direction = config["direction"];
+ gpioCfg.name = config["name"];
+ gpioCfg.polarity = (config["polarity"] == "active_high")
+ ? GpioPolarity::activeHigh
+ : GpioPolarity::activeLow;
buttonCfg.gpios.push_back(gpioCfg);
}
}
diff --git a/src/serial_uart_mux.cpp b/src/serial_uart_mux.cpp
new file mode 100644
index 0000000..00724dd
--- /dev/null
+++ b/src/serial_uart_mux.cpp
@@ -0,0 +1,112 @@
+#include "serial_uart_mux.hpp"
+
+#include "xyz/openbmc_project/Chassis/Buttons/HostSelector/client.hpp"
+#include "xyz/openbmc_project/Chassis/Buttons/HostSelector/server.hpp"
+
+#include <error.h>
+
+#include <phosphor-logging/lg2.hpp>
+namespace sdbusRule = sdbusplus::bus::match::rules;
+// add the button iface class to registry
+static ButtonIFRegister<SerialUartMux> buttonRegister;
+namespace HostSelectorServerObj =
+ sdbusplus::xyz::openbmc_project::Chassis::Buttons::server;
+namespace HostSelectorClientObj =
+ sdbusplus::xyz::openbmc_project::Chassis::Buttons::client::HostSelector;
+
+constexpr std::string_view SERIAL_UART_RX_GPIO = "serial_uart_rx";
+void SerialUartMux::init()
+{
+ try
+ {
+ // when Host Selector Position is changed call the handler
+
+ std::string matchPattern = sdbusRule::propertiesChanged(
+ HS_DBUS_OBJECT_NAME, HostSelectorClientObj::interface);
+
+ hostPositionChanged = std::make_unique<sdbusplus::bus::match_t>(
+ bus, matchPattern,
+ std::bind(std::mem_fn(&SerialUartMux::hostSelectorPositionChanged),
+ this, std::placeholders::_1));
+ }
+ catch (const std::exception& e)
+ {
+ lg2::error(
+ "Failed binding to matching function : {BUTTON_TYPE},Exception : {ERROR}",
+ "BUTTON_TYPE", getFormFactorName(), "ERROR", e);
+ throw sdbusplus::xyz::openbmc_project::Chassis::Common::Error::
+ IOError();
+ }
+}
+// check the debug card present pin
+bool SerialUartMux::isOCPDebugCardPresent()
+{
+ auto gpioState =
+ getGpioState(debugCardPresentGpio.fd, debugCardPresentGpio.polarity);
+ return (gpioState == GpioState::assert);
+}
+// set the serial uart MUX to select the console w.r.t host selector position
+void SerialUartMux::configSerialConsoleMux(size_t position)
+{
+ auto debugCardPresent = isOCPDebugCardPresent();
+
+ if (debugCardPresent)
+ {
+ lg2::info("Debug card is present ");
+ }
+ else
+ {
+ lg2::info("Debug card not present ");
+ }
+
+ for (size_t uartMuxSel = 0; uartMuxSel < gpioLineCount; uartMuxSel++)
+ {
+ auto gpioState = GpioState::invalid;
+ gpioInfo gpioConfig = config.gpios[uartMuxSel];
+
+ if (gpioConfig.name == SERIAL_UART_RX_GPIO)
+ {
+ gpioState =
+ debugCardPresent ? GpioState::assert : GpioState::deassert;
+ }
+ else
+ {
+ gpioState = (serialUartMuxMap[position] & (0x1 << uartMuxSel))
+ ? GpioState::assert
+ : GpioState::deassert;
+ }
+ setGpioState(gpioConfig.fd, gpioConfig.polarity, gpioState);
+ }
+}
+
+void SerialUartMux::hostSelectorPositionChanged(sdbusplus::message_t& msg)
+{
+ std::string interface;
+ std::map<std::string,
+ HostSelectorServerObj::HostSelector::PropertiesVariant>
+ propertiesChanged;
+ lg2::info("hostSelectorPositionChanged callback : {BUTTON_TYPE}",
+ "BUTTON_TYPE", getFormFactorName());
+
+ try
+ {
+ msg.read(interface, propertiesChanged);
+ for (auto& property : propertiesChanged)
+ {
+ auto propertyName = property.first;
+ if (propertyName == "Position")
+ {
+ size_t hostPosition = std::get<size_t>(property.second);
+ lg2::debug("property changed : {VALUE}", "VALUE", hostPosition);
+ configSerialConsoleMux(hostPosition);
+ return;
+ }
+ }
+ }
+ catch (const std::exception& e)
+ {
+ lg2::error("exception while reading dbus property : {ERROR}", "ERROR",
+ e.what());
+ return;
+ }
+}