From 685ddf56f7f7d5d01ec6285d72dad653962753ef Mon Sep 17 00:00:00 2001 From: wangjue Date: Thu, 24 Oct 2024 20:30:19 +0800 Subject: [PATCH] Implement DIMM LED control Signed-off-by: wangjue --- 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 #include #include + +extern "C" +{ +#include +#include +#include +#include +#include +} + 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>; using Attributes = std::variant; @@ -68,6 +85,7 @@ constexpr auto ACPI_S5_STATE = 6; std::unordered_map sensorStatusRec; std::unordered_map> 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 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 writeData, + std::vector& 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 writeData{regConfig[0], 0x0}; + std::vector 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& 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 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 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 <= 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 writeData{regOutput[port]}; + std::vector 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 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> properties; + msg.read(interfaceName, properties); + + const auto it = properties.find("LEDState"); + if (it != properties.end()) + { + ledState = std::get_if(&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 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 #include +#include +#include +#include +#include #include #include +#include +#include + +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; + +const std::vector i2cAddrs = {0x20, 0x21, 0x22}; +const std::vector regConfig = {0x06, 0x07}; +const std::vector 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> removeWatches; + std::shared_ptr 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& 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(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(*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