Files
OpenBMC/meta-luxshare/meta-common/recipes-luxshare/leds/phosphor-led-manager/0001-Implement-DIMM-LED-control.patch
T
2026-04-23 17:07:55 +08:00

569 lines
18 KiB
Diff
Executable File

From 685ddf56f7f7d5d01ec6285d72dad653962753ef Mon Sep 17 00:00:00 2001
From: wangjue <jue.wang2@luxshare-ict.com>
Date: Thu, 24 Oct 2024 20:30:19 +0800
Subject: [PATCH] Implement DIMM LED control
Signed-off-by: wangjue <jue.wang2@luxshare-ict.com>
---
fault-monitor/fru-fault-monitor.cpp | 313 +++++++++++++++++++++++++++-
fault-monitor/fru-fault-monitor.hpp | 72 ++++++-
fault-monitor/meson.build | 5 +-
fault-monitor/monitor-main.cpp | 13 +-
meson.build | 2 +
5 files changed, 392 insertions(+), 13 deletions(-)
diff --git a/fault-monitor/fru-fault-monitor.cpp b/fault-monitor/fru-fault-monitor.cpp
index 4946065..03e1700 100644
--- a/fault-monitor/fru-fault-monitor.cpp
+++ b/fault-monitor/fru-fault-monitor.cpp
@@ -8,6 +8,16 @@
#include <string>
#include <vector>
#include <iostream>
+
+extern "C"
+{
+#include <i2c/smbus.h>
+#include <linux/i2c-dev.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+}
+
namespace phosphor
{
namespace led
@@ -30,6 +40,13 @@ static constexpr auto logPath = "/xyz/openbmc_project/logging";
static constexpr auto logIntf = "xyz.openbmc_project.Logging.Entry";
static constexpr auto logService = "xyz.openbmc_project.Logging";
+#define SET_GREEN_LED(x) (~(uint8_t(1 << (x*2))))
+#define SET_RED_LED(x) (~(uint8_t(1 << (x*2 + 1))))
+#define SET_ORANGE_LED(x) (SET_GREEN_LED(x) | SET_RED_LED(x))
+#define UNSET_GREEN_LED(x) (uint8_t(1 << (x*2)))
+#define UNSET_RED_LED(x) (uint8_t(1 << (x*2 + 1)))
+#define SET_LED_OFF(x) ((uint8_t(1 << (x*2))) | (uint8_t(1 << (x*2+1))))
+
using AssociationList =
std::vector<std::tuple<std::string, std::string, std::string>>;
using Attributes = std::variant<bool, AssociationList>;
@@ -68,6 +85,7 @@ constexpr auto ACPI_S5_STATE = 6;
std::unordered_map<std::string, SensorStatusInfo> sensorStatusRec;
std::unordered_map<std::string, std::unordered_map<uint8_t, DriveStatus>> driveStatusRec;
+
enum AlarmType
{
NORMAL = 0,
@@ -237,6 +255,41 @@ static void setDiscreteSensorDiscription(sdbusplus::bus::bus& bus,
return;
}
+static void setDimmLEDState(sdbusplus::bus::bus& bus,
+ const std::string& sensorPath,const std::string& ledState)
+{
+ std::string sensorName;
+ std::string ledStatePath;
+ size_t lastSlashPos = sensorPath.rfind('/');
+ size_t statusPos = sensorPath.find("_Status");
+
+ if (lastSlashPos != std::string::npos && statusPos != std::string::npos) {
+ sensorName = sensorPath.substr(lastSlashPos + 1, statusPos - lastSlashPos - 1);
+ ledStatePath = "/xyz/openbmc_project/sensors/temperature/" + sensorName + "_Temp";
+ } else {
+ lg2::info("Invalid sensorPath, sensorPath = {PATH}",
+ "PATH", sensorPath);
+ return;
+ }
+
+ auto method = bus.new_method_call(DIMMTempSensorBusname, ledStatePath.c_str(),
+ dbusProperties, "Set");
+
+ std::variant<std::string> value{ledState};
+ method.append(dimmLEDInterface, "LEDState", value);
+ try
+ {
+ bus.call_noreply(method);
+ }
+ catch (const sdbusplus::exception::exception& e)
+ {
+ lg2::info("Error setting Dimm LED State, ERROR = {ERROR}",
+ "ERROR", e);
+ }
+
+ return;
+}
+
static void setDiscreteSensorHealth(sdbusplus::bus::bus& bus,
const std::string& sensorPath)
{
@@ -441,6 +494,7 @@ void parseSpecificSensorSelSeverity(sdbusplus::bus::bus& bus,
case SENSOR_TYPE_MEMORY:
{
sensorStatusRec[sensorPath].alarmStatus = alarmStatus;
+ setDimmLEDState(bus, sensorPath, "FAULT");
break;
}
default:
@@ -458,9 +512,6 @@ void parseSpecificSensorSelSeverity(sdbusplus::bus::bus& bus,
return;
}
-
-
-
void Add::filterSEL(sdbusplus::bus::bus& bus, const std::string& path)
{
//AlarmType severity = NORMAL;
@@ -631,6 +682,250 @@ void Add::created(sdbusplus::message_t& msg)
return;
}
+std::string getFileName(const std::string& path) {
+ size_t lastSlash = path.find_last_of("/\\");
+ return (lastSlash == std::string::npos) ? "" : path.substr(lastSlash + 1);
+}
+
+static int i2cWriteRead(std::string& i2cBus, const uint8_t slaveAddr,
+ std::vector<uint8_t> writeData,
+ std::vector<uint8_t>& readBuf)
+{
+ int fd = open(i2cBus.c_str(), O_RDWR);
+ if (fd < 0)
+ {
+ std::cerr << " unable to open i2c device" << i2cBus << " err=" << fd
+ << "\n";
+ return -1;
+ }
+
+ uint8_t writeCount = writeData.size();
+ uint8_t readCount = readBuf.size();
+ int msgCount = 0;
+ struct i2c_msg i2cmsg[2]; // = {0};
+
+ memset(i2cmsg, 0, sizeof(i2cmsg));
+
+ if (writeCount)
+ {
+ // Data will be writtern to the slave address
+ i2cmsg[msgCount].addr = slaveAddr;
+ i2cmsg[msgCount].flags = 0x00;
+ i2cmsg[msgCount].len = writeCount;
+ i2cmsg[msgCount].buf = writeData.data();
+ msgCount++;
+ }
+
+ if (readCount)
+ {
+ // Data will be read into the buffer from the slave address
+ i2cmsg[msgCount].addr = slaveAddr;
+ i2cmsg[msgCount].flags = I2C_M_RD;
+ i2cmsg[msgCount].len = readCount;
+ i2cmsg[msgCount].buf = readBuf.data();
+ msgCount++;
+ }
+
+ struct i2c_rdwr_ioctl_data msgReadWrite; // = {0};
+ memset((void*)&msgReadWrite, 0, sizeof(msgReadWrite));
+ msgReadWrite.msgs = i2cmsg;
+ msgReadWrite.nmsgs = msgCount;
+
+ // Perform the combined write/read
+ int ret = ioctl(fd, I2C_RDWR, &msgReadWrite);
+ close(fd);
+ if (ret < 0)
+ {
+ std::cerr << "getDIMMRegsInfoWord I2C Write Failed!"
+ << "\n";
+ return -1;
+ }
+
+ if (readCount)
+ {
+ readBuf.resize(msgReadWrite.msgs[msgCount - 1].len);
+ }
+
+ return 0;
+}
+
+void Add::dimmledInit()
+{
+ int ret = -1;
+ std::string i2cBus = "/dev/i2c-5";
+
+ for(uint8_t addr : i2cAddrs) {
+ std::vector<uint8_t> writeData{regConfig[0], 0x0};
+ std::vector<uint8_t> readBuf(0);
+ ret = i2cWriteRead(i2cBus, addr, writeData, readBuf);
+ if (ret < 0)
+ goto err;
+
+ writeData[0] = regConfig[1];
+ ret = i2cWriteRead(i2cBus, addr, writeData, readBuf);
+ if (ret < 0)
+ goto err;
+
+ writeData[0] = regOutput[0];
+ writeData[1] = 0xff;
+ ret = i2cWriteRead(i2cBus, addr, writeData, readBuf);
+ if (ret < 0)
+ goto err;
+
+ writeData[0] = regOutput[1];
+ ret = i2cWriteRead(i2cBus, addr, writeData, readBuf);
+ if (ret < 0)
+ goto err;
+ }
+
+ return;
+ err:
+ std::cerr << "i2c read/write error, DIMM ledInit failed. \n";
+ return;
+}
+
+void Add::updateSysHealthLED(std::pair<std::string, std::string>& pair)
+{
+ if(pair.second == "FAULT") {
+ setHealthLED(pair.first);
+ } else {
+ unsetHealthLED(pair.first);
+ }
+}
+
+void Add::setHealthLED(const std::string& path)
+{
+ if constexpr (debug) {
+ std::cout << "setHealthLED path: " << path << std::endl;
+ }
+
+ std::vector<Association> associations;
+ associations.emplace_back("", "dimmfault", path.c_str());
+
+ faultMonitorAssociation->set_property("Associations", associations);
+}
+
+void Add::unsetHealthLED(const std::string& path)
+{
+ if constexpr (debug) {
+ std::cout << "unsetHealthLED path: " << path << std::endl;
+ }
+
+ std::vector<Association> associations;
+ associations.emplace_back("", "", path.c_str());
+
+ faultMonitorAssociation->set_property("Associations", associations);
+}
+
+void Add::updateDimmLed(int cpuId, int dimmRank, std::string ledState)
+{
+ int ret = -1;
+ std::string i2cBus = "/dev/i2c-5";
+
+ if constexpr (debug) {
+ std::cout << "cpuId: " << cpuId << " dimmrank: " << dimmRank << " ledState: " << ledState <<std::endl;
+ }
+
+ uint8_t preset = (cpuId * 12 + dimmRank) / 8;
+ if(preset >= 3)
+ {
+ std::cerr << " fail to update Dimm LED, invalid preset. \n";
+ return;
+ }
+
+ uint8_t port = ((cpuId * 12 + dimmRank) % 8) / 4;
+ if(port >= 2)
+ {
+ std::cerr << " fail to update Dimm LED, invalid port. \n";
+ return;
+ }
+
+ uint8_t ledSlot = ((cpuId * 12 + dimmRank) % 8) % 4;
+
+ std::vector<uint8_t> writeData{regOutput[port]};
+ std::vector<uint8_t> readBuf(1);
+ ret = i2cWriteRead(i2cBus, i2cAddrs[preset], writeData, readBuf);
+ if (ret < 0)
+ goto err;
+
+ uint8_t wdata;
+ if(ledState == "ON")
+ wdata = (readBuf[0] & SET_GREEN_LED(ledSlot)) | UNSET_RED_LED(ledSlot);
+ else if(ledState == "FAULT")
+ wdata = (readBuf[0] & SET_RED_LED(ledSlot)) | UNSET_GREEN_LED(ledSlot);
+ else if(ledState == "OFF")
+ wdata = readBuf[0] | SET_LED_OFF(ledSlot);
+ else if(ledState == "WAR")
+ wdata = readBuf[0] | SET_LED_OFF(ledSlot);
+ else
+ std::cerr << " Invalid DIMM CPU" << cpuId << " rank" << dimmRank << " LED State " << ledState << "\n";
+
+ writeData.push_back(wdata);
+ readBuf.resize(0);
+ ret = i2cWriteRead(i2cBus, i2cAddrs[preset], writeData, readBuf);
+ if (ret < 0)
+ goto err;
+
+ return;
+
+err:
+ std::cerr << "i2c read/write error, update DIMM led failed. \n";
+ return;
+}
+
+void Add::dimmLEDMatchHandler(sdbusplus::message_t& msg)
+{
+ // Get the ObjectPath of the `xyz.openbmc_project.Inventory.Manager`
+ // service
+ int cpuId, dimmrank;
+ std::pair<std::string, std::string> dimmSensorLEDStatus;
+ std::string* ledState;
+ std::string dimmTempSensorPath = msg.get_path();
+
+ std::string dimmTempSenName = getFileName(dimmTempSensorPath);
+ if(dimmTempSenName != "" && dimmTempSenName.find("DIMM_CPU") != std::string::npos)
+ {
+ std::regex regex(R"(CPU(\d+)_([A-Z]))");
+ std::smatch match;
+
+ if (std::regex_search(dimmTempSenName, match, regex)) {
+ cpuId = std::stoi(match[1].str());
+ dimmrank = match[2].str()[0] - 'A';
+ }
+ } else {
+ lg2::error(
+ "Faild to get the DIMM temp sensor path, PATH = {PATH}",
+ "PATH", dimmTempSensorPath);
+ return;
+ }
+
+ // Get all the properties of
+ // "xyz.openbmc_project.State.Decorator.OperationalStatus" interface
+ std::string interfaceName{};
+ std::unordered_map<std::string, std::variant<std::string>> properties;
+ msg.read(interfaceName, properties);
+
+ const auto it = properties.find("LEDState");
+ if (it != properties.end())
+ {
+ ledState = std::get_if<std::string>(&it->second);
+ if (!ledState)
+ {
+ lg2::error(
+ "Faild to get the Functional property, INVENTORY_PATH = {PATH}",
+ "PATH", dimmTempSensorPath);
+ return;
+ }
+ }
+
+ dimmSensorLEDStatus.first = dimmTempSensorPath;
+ dimmSensorLEDStatus.second = *ledState;
+
+ updateDimmLed(cpuId, dimmrank, *ledState);
+
+ updateSysHealthLED(dimmSensorLEDStatus);
+}
+
void getLoggingSubTree(sdbusplus::bus_t& bus, MapperResponseType& subtree)
{
auto depth = 0;
@@ -653,6 +948,18 @@ void getLoggingSubTree(sdbusplus::bus_t& bus, MapperResponseType& subtree)
}
}
+void Add::createAssociation(sdbusplus::asio::object_server& objectServer)
+{
+ // Associations interface for led status
+ std::vector<Association> associations;
+ associations.emplace_back("", "", "");
+
+ faultMonitorAssociation = objectServer.add_interface(
+ monitorPath, associationPath);
+ faultMonitorAssociation->register_property("Associations", associations);
+ faultMonitorAssociation->initialize();
+}
+
void Add::processExistingCallouts(sdbusplus::bus_t& bus)
{
MapperResponseType mapperResponse;
diff --git a/fault-monitor/fru-fault-monitor.hpp b/fault-monitor/fru-fault-monitor.hpp
index 065e8c8..bd4a852 100644
--- a/fault-monitor/fru-fault-monitor.hpp
+++ b/fault-monitor/fru-fault-monitor.hpp
@@ -4,9 +4,18 @@
#include <sdbusplus/bus.hpp>
#include <sdbusplus/server.hpp>
+#include <sdbusplus/asio/connection.hpp>
+#include <sdbusplus/asio/object_server.hpp>
+#include <boost/asio/io_context.hpp>
+#include <boost/container/flat_map.hpp>
#include <string>
#include <iostream>
+#include <regex>
+#include <vector>
+
+constexpr const bool debug = false;
+
namespace phosphor
{
namespace led
@@ -17,6 +26,25 @@ namespace fault
{
namespace monitor
{
+constexpr auto faultLedPath = "/xyz/openbmc_project/led/groups/status_fault";
+constexpr auto dimmfaultLedPath = "/xyz/openbmc_project/led/groups/dimm_fault";
+constexpr auto okLedPath = "/xyz/openbmc_project/led/groups/status_ok";
+constexpr auto ledIface = "xyz.openbmc_project.Led.Group";
+constexpr auto ledAssertProp = "Asserted";
+constexpr auto ledManagerBusname = "xyz.openbmc_project.LED.GroupManager";
+constexpr auto DIMMTempSensorBusname = "xyz.openbmc_project.DIMMTempSensor";
+constexpr auto dbusProperties = "org.freedesktop.DBus.Properties";
+constexpr auto dimmLEDInterface = "xyz.openbmc_project.Sensor.dimmLED";
+constexpr auto dimmSensorPath = "/xyz/openbmc_project/sensors/temperature";
+constexpr auto monitorPath = "/xyz/openbmc_project/led/fault/monitor";
+constexpr auto callbackMgrPath = "/xyz/openbmc_project/CallbackManager";
+constexpr auto associationPath = "xyz.openbmc_project.Association.Definitions";
+
+using Association = std::tuple<std::string, std::string, std::string>;
+
+const std::vector<uint8_t> i2cAddrs = {0x20, 0x21, 0x22};
+const std::vector<uint8_t> regConfig = {0x06, 0x07};
+const std::vector<uint8_t> regOutput = {0x02, 0x03};
/** @brief Assert or deassert an LED based on the input FRU
* @param[in] bus - The Dbus bus object
@@ -117,31 +145,61 @@ class Add
/** @brief constructs Add a watch for FRU faults.
* @param[in] bus - The Dbus bus object
*/
- explicit Add(sdbusplus::bus_t& bus) :
- bus(bus), matchCreated(bus,
- sdbusplus::bus::match::rules::interfacesAdded() +
- sdbusplus::bus::match::rules::path_namespace(
- "/xyz/openbmc_project/logging"),
- std::bind(std::mem_fn(&Add::created), this,
- std::placeholders::_1))
+
+ explicit Add(sdbusplus::bus_t& bus, sdbusplus::asio::object_server& objectServer) :
+ bus(bus),
+ matchCreated(bus,
+ sdbusplus::bus::match::rules::interfacesAdded() +
+ sdbusplus::bus::match::rules::path_namespace(
+ "/xyz/openbmc_project/logging"),
+ std::bind(std::mem_fn(&Add::created), this,
+ std::placeholders::_1)),
+ dimmLEDMatch(bus,
+ "type='signal',member='PropertiesChanged',path_namespace='" +
+ std::string(dimmSensorPath) + "',arg0namespace='" +
+ std::string(dimmLEDInterface) + "'",
+ std::bind(std::mem_fn(&Add::dimmLEDMatchHandler), this,
+ std::placeholders::_1))
{
+
+ createAssociation(objectServer);
+ dimmledInit();
processExistingCallouts(bus);
}
+
private:
sdbusplus::bus::bus& bus;
/** @brief sdbusplus signal match for fault created */
sdbusplus::bus::match_t matchCreated;
+ sdbusplus::bus::match_t dimmLEDMatch;
+
std::vector<std::unique_ptr<Remove>> removeWatches;
+ std::shared_ptr<sdbusplus::asio::dbus_interface> faultMonitorAssociation;
+
/** @brief Callback function for fru fault created
* @param[in] msg - Data associated with subscribed signal
*/
void created(sdbusplus::message_t& msg);
+ void dimmLEDMatchHandler(sdbusplus::message_t& msg);
+
void filterSEL(sdbusplus::bus::bus& bus, const std::string& path);
+ void dimmledInit();
+
+ void updateDimmLed(int cpuId, int dimmRank, std::string ledState);
+
+ void updateSysHealthLED(std::pair<std::string, std::string>& pair);
+
+ void setHealthLED(const std::string& path);
+
+ void unsetHealthLED(const std::string& path);
+
+ void createAssociation(sdbusplus::asio::object_server& objectServer);
+
/** @brief This function process all callouts at application start
* @param[in] bus - The Dbus bus object
*/
diff --git a/fault-monitor/meson.build b/fault-monitor/meson.build
index 39ec458..7ced362 100644
--- a/fault-monitor/meson.build
+++ b/fault-monitor/meson.build
@@ -17,7 +17,10 @@ executable(
'phosphor-fru-fault-monitor',
fault_monitor_sources,
include_directories: ['.', '../'],
- dependencies: deps,
+ dependencies: [
+ deps,
+ i2c,
+ ],
install: true,
install_dir: get_option('bindir')
)
diff --git a/fault-monitor/monitor-main.cpp b/fault-monitor/monitor-main.cpp
index 92b332d..3083c13 100644
--- a/fault-monitor/monitor-main.cpp
+++ b/fault-monitor/monitor-main.cpp
@@ -6,15 +6,24 @@
#include "fru-fault-monitor.hpp"
#endif
+constexpr auto busName = "xyz.openbmc_project.led.fault.monitor";
+
int main(void)
{
+ boost::asio::io_context io;
+ auto systemBus = std::make_shared<sdbusplus::asio::connection>(io);
+ systemBus->request_name(busName);
+
+ sdbusplus::asio::object_server server =
+ sdbusplus::asio::object_server(systemBus);
+
/** @brief Dbus constructs used by Fault Monitor */
- sdbusplus::bus_t bus = sdbusplus::bus::new_default();
+ sdbusplus::bus_t& bus = static_cast<sdbusplus::bus_t&>(*systemBus);
#ifdef MONITOR_OPERATIONAL_STATUS
phosphor::led::Operational::status::monitor::Monitor monitor(bus);
#else
- phosphor::led::fru::fault::monitor::Add monitor(bus);
+ phosphor::led::fru::fault::monitor::Add monitor(bus, server);
#endif
/** @brief Wait for client requests */
while (true)
diff --git a/meson.build b/meson.build
index f1a65a8..bf5fc76 100644
--- a/meson.build
+++ b/meson.build
@@ -31,6 +31,8 @@ phosphor_logging_dep = dependency('phosphor-logging')
prog_python = find_program('python3', required: true)
realpath_prog = find_program('realpath')
+i2c = meson.get_compiler('cpp').find_library('i2c')
+
cpp = meson.get_compiler('cpp')
if cpp.has_header('nlohmann/json.hpp')
nlohmann_json_dep = declare_dependency()
--
2.34.1