blob: 85e2a88d3809ea88d8c280e9e3f1daecbc2c5d66 [file] [log] [blame]
Faisal Awada9864f832025-05-30 12:21:00 -05001#include "config.h"
2
3#include "chassis.hpp"
4
5#include <filesystem>
6#include <iostream>
7
8using namespace phosphor::logging;
9using namespace phosphor::power::util;
10namespace phosphor::power::chassis
11{
12
13constexpr auto IBMCFFPSInterface =
14 "xyz.openbmc_project.Configuration.IBMCFFPSConnector";
15constexpr auto chassisIdProp = "SlotNumber";
16constexpr auto i2cBusProp = "I2CBus";
17constexpr auto i2cAddressProp = "I2CAddress";
18constexpr auto psuNameProp = "Name";
19constexpr auto presLineName = "NamedPresenceGpio";
20constexpr auto supportedConfIntf =
21 "xyz.openbmc_project.Configuration.SupportedConfiguration";
22const auto deviceDirPath = "/sys/bus/i2c/devices/";
23const auto driverDirName = "/driver";
24
25const auto entityMgrService = "xyz.openbmc_project.EntityManager";
26const auto decoratorChassisId = "xyz.openbmc_project.Inventory.Decorator.Slot";
27
Faisal Awada348168b2025-07-08 11:23:02 -050028Chassis::Chassis(sdbusplus::bus_t& bus, const std::string& chassisPath,
29 const sdeventplus::Event& e) :
Faisal Awada9864f832025-05-30 12:21:00 -050030 bus(bus), chassisPath(chassisPath),
Faisal Awada348168b2025-07-08 11:23:02 -050031 chassisPathUniqueId(getChassisPathUniqueId(chassisPath)), eventLoop(e)
Faisal Awada9864f832025-05-30 12:21:00 -050032{
33 getPSUConfiguration();
34}
35
36void Chassis::getPSUConfiguration()
37{
38 namespace fs = std::filesystem;
39 auto depth = 0;
40
41 try
42 {
43 if (chassisPathUniqueId == invalidObjectPathUniqueId)
44 {
45 lg2::error("Chassis does not have chassis ID: {CHASSISPATH}",
46 "CHASSISPATH", chassisPath);
47 return;
48 }
49 auto connectorsSubTree = getSubTree(bus, "/", IBMCFFPSInterface, depth);
50 for (const auto& [path, services] : connectorsSubTree)
51 {
52 uint64_t id;
53 fs::path fspath(path);
54 getProperty(decoratorChassisId, chassisIdProp, fspath.parent_path(),
55 entityMgrService, bus, id);
56 if (id == static_cast<uint64_t>(chassisPathUniqueId))
57
58 {
59 // For each object in the array of objects, I want
60 // to get properties from the service, path, and
61 // interface.
62 auto properties = getAllProperties(bus, path, IBMCFFPSInterface,
63 entityMgrService);
64 getPSUProperties(properties);
65 }
66 }
67 }
68 catch (const sdbusplus::exception_t& e)
69 {
70 lg2::error("Failed while getting configuration - exception: {ERROR}",
71 "ERROR", e);
72 }
73
74 if (psus.empty())
75 {
76 // Interface or properties not found. Let the Interfaces Added callback
77 // process the information once the interfaces are added to D-Bus.
78 lg2::info("No power supplies to monitor");
79 }
80}
81
82void Chassis::getPSUProperties(util::DbusPropertyMap& properties)
83{
84 std::string basePSUInvPath = chassisPath + "/motherboard/powersupply";
85
86 // From passed in properties, I want to get: I2CBus, I2CAddress,
87 // and Name. Create a power supply object, using Name to build the inventory
88 // path.
89
90 uint64_t* i2cbus = nullptr;
91 uint64_t* i2caddr = nullptr;
92 std::string* psuname = nullptr;
93 std::string* preslineptr = nullptr;
94
95 for (const auto& property : properties)
96 {
97 try
98 {
99 if (property.first == i2cBusProp)
100 {
101 i2cbus = std::get_if<uint64_t>(&properties[i2cBusProp]);
102 }
103 else if (property.first == i2cAddressProp)
104 {
105 i2caddr = std::get_if<uint64_t>(&properties[i2cAddressProp]);
106 }
107 else if (property.first == psuNameProp)
108 {
109 psuname = std::get_if<std::string>(&properties[psuNameProp]);
110 }
111 else if (property.first == presLineName)
112 {
113 preslineptr =
114 std::get_if<std::string>(&properties[presLineName]);
115 }
116 }
117 catch (const std::exception& e)
118 {}
119 }
120
121 if (i2cbus && i2caddr && psuname && !psuname->empty())
122 {
123 std::string invpath = basePSUInvPath;
124 invpath.push_back(psuname->back());
125 std::string presline = "";
126
127 lg2::debug("Inventory Path: {INVPATH}", "INVPATH", invpath);
128
129 if (nullptr != preslineptr)
130 {
131 presline = *preslineptr;
132 }
133
134 auto invMatch =
135 std::find_if(psus.begin(), psus.end(), [&invpath](auto& psu) {
136 return psu->getInventoryPath() == invpath;
137 });
138 if (invMatch != psus.end())
139 {
140 // This power supply has the same inventory path as the one with
141 // information just added to D-Bus.
142 // Changes to GPIO line name unlikely, so skip checking.
143 // Changes to the I2C bus and address unlikely, as that would
144 // require corresponding device tree updates.
145 // Return out to avoid duplicate object creation.
146 return;
147 }
148
149 buildDriverName(*i2cbus, *i2caddr);
150 lg2::debug(
151 "make PowerSupply bus: {I2CBUS} addr: {I2CADDR} presline: {PRESLINE}",
152 "I2CBUS", *i2cbus, "I2CADDR", *i2caddr, "PRESLINE", presline);
Faisal Awada348168b2025-07-08 11:23:02 -0500153 // auto psu = std::make_unique<PowerSupply>(
154 // bus, invpath, *i2cbus, *i2caddr, driverName, presline,
155 // std::bind(&Chassis::isPowerOn, this), chassisPath);
Faisal Awada9864f832025-05-30 12:21:00 -0500156 auto psu = std::make_unique<PowerSupply>(
157 bus, invpath, *i2cbus, *i2caddr, driverName, presline,
Faisal Awada348168b2025-07-08 11:23:02 -0500158 std::bind(&Chassis::isPowerOn, this));
Faisal Awada9864f832025-05-30 12:21:00 -0500159 psus.emplace_back(std::move(psu));
160
161 // Subscribe to power supply presence changes
162 auto presenceMatch = std::make_unique<sdbusplus::bus::match_t>(
163 bus,
164 sdbusplus::bus::match::rules::propertiesChanged(invpath,
165 INVENTORY_IFACE),
166 [this](auto& msg) { this->psuPresenceChanged(msg); });
167 presenceMatches.emplace_back(std::move(presenceMatch));
168 }
169 if (psus.empty())
170 {
171 lg2::info("No power supplies to monitor");
172 }
173 else
174 {
175 populateDriverName();
176 }
177}
178
179void Chassis::getSupportedConfiguration()
180{
181 // TBD
182}
183
184void Chassis::populateSupportedConfiguration(
185 const util::DbusPropertyMap& properties)
186{
187 try
188 {
189 auto propIt = properties.find("SupportedType");
190 if (propIt == properties.end())
191 {
192 return;
193 }
194 const std::string* type = std::get_if<std::string>(&(propIt->second));
195 if ((type == nullptr) || (*type != "PowerSupply"))
196 {
197 return;
198 }
199
200 propIt = properties.find("SupportedModel");
201 if (propIt == properties.end())
202 {
203 return;
204 }
205 const std::string* model = std::get_if<std::string>(&(propIt->second));
206 if (model == nullptr)
207 {
208 return;
209 }
210
211 SupportedPsuConfiguration supportedPsuConfig;
212 propIt = properties.find("RedundantCount");
213 if (propIt != properties.end())
214 {
215 const uint64_t* count = std::get_if<uint64_t>(&(propIt->second));
216 if (count != nullptr)
217 {
218 supportedPsuConfig.powerSupplyCount = *count;
219 }
220 }
221 propIt = properties.find("InputVoltage");
222 if (propIt != properties.end())
223 {
224 const std::vector<uint64_t>* voltage =
225 std::get_if<std::vector<uint64_t>>(&(propIt->second));
226 if (voltage != nullptr)
227 {
228 supportedPsuConfig.inputVoltage = *voltage;
229 }
230 }
231
232 // The PowerConfigFullLoad is an optional property, default it to false
233 // since that's the default value of the power-config-full-load GPIO.
234 supportedPsuConfig.powerConfigFullLoad = false;
235 propIt = properties.find("PowerConfigFullLoad");
236 if (propIt != properties.end())
237 {
238 const bool* fullLoad = std::get_if<bool>(&(propIt->second));
239 if (fullLoad != nullptr)
240 {
241 supportedPsuConfig.powerConfigFullLoad = *fullLoad;
242 }
243 }
244
245 supportedConfigs.emplace(*model, supportedPsuConfig);
246 }
247 catch (const std::exception& e)
248 {
249 lg2::info("populateSupportedConfiguration error {ERR}", "ERR", e);
250 }
251}
252
253void Chassis::psuPresenceChanged(sdbusplus::message_t& msg)
254{
255 std::string msgSensor;
256 std::map<std::string, std::variant<uint32_t, bool>> msgData;
257 msg.read(msgSensor, msgData);
258
259 // Check if it was the Present property that changed.
260 auto valPropMap = msgData.find(PRESENT_PROP);
261 if (valPropMap != msgData.end())
262 {
263 if (std::get<bool>(valPropMap->second))
264 {
265 // A PSU became present, force the PSU validation to run.
266 runValidateConfig = true;
267 validationTimer->restartOnce(validationTimeout);
268 }
269 }
270}
271
272void Chassis::buildDriverName(uint64_t i2cbus, uint64_t i2caddr)
273{
274 namespace fs = std::filesystem;
275 std::stringstream ss;
276 ss << std::hex << std::setw(4) << std::setfill('0') << i2caddr;
277 std::string symLinkPath =
278 deviceDirPath + std::to_string(i2cbus) + "-" + ss.str() + driverDirName;
279 try
280 {
281 fs::path linkStrPath = fs::read_symlink(symLinkPath);
282 driverName = linkStrPath.filename();
283 }
284 catch (const std::exception& e)
285 {
286 lg2::error(
287 "Failed to find device driver {SYM_LINK_PATH}, error {ERROR_STR}",
288 "SYM_LINK_PATH", symLinkPath, "ERROR_STR", e);
289 }
290}
291
292void Chassis::populateDriverName()
293{
294 std::string driverName;
295 // Search in PSUs for driver name
296 std::for_each(psus.begin(), psus.end(), [&driverName](auto& psu) {
297 if (!psu->getDriverName().empty())
298 {
299 driverName = psu->getDriverName();
300 }
301 });
302 // Assign driver name to all PSUs
303 std::for_each(psus.begin(), psus.end(),
304 [&driverName](auto& psu) { psu->setDriverName(driverName); });
305}
306
Faisal Awada348168b2025-07-08 11:23:02 -0500307uint64_t Chassis::getChassisPathUniqueId(const std::string& path)
Faisal Awada9864f832025-05-30 12:21:00 -0500308{
Faisal Awada9864f832025-05-30 12:21:00 -0500309 try
310 {
Faisal Awada348168b2025-07-08 11:23:02 -0500311 return getChassisInventoryUniqueId(bus, path);
Faisal Awada9864f832025-05-30 12:21:00 -0500312 }
313 catch (const sdbusplus::exception_t& e)
314 {
Faisal Awada348168b2025-07-08 11:23:02 -0500315 lg2::error(
316 "Failed to find chassis path {CHASSIS_PATH} ID - exception: {ERROR}",
317 "CHASSIS_PATH", path, "ERROR", e);
Faisal Awada9864f832025-05-30 12:21:00 -0500318 }
Faisal Awada348168b2025-07-08 11:23:02 -0500319 return invalidObjectPathUniqueId;
Faisal Awada9864f832025-05-30 12:21:00 -0500320}
321
Faisal Awada348168b2025-07-08 11:23:02 -0500322void Chassis::analyze() {}
Faisal Awada9864f832025-05-30 12:21:00 -0500323} // namespace phosphor::power::chassis