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