11d7a7df8SBrandon Wyman #include "config.h"
21d7a7df8SBrandon Wyman
3aed1f75dSBrandon Wyman #include "power_supply.hpp"
4aed1f75dSBrandon Wyman
5aed1f75dSBrandon Wyman #include "types.hpp"
63f1242f3SBrandon Wyman #include "util.hpp"
7aed1f75dSBrandon Wyman
83f1242f3SBrandon Wyman #include <xyz/openbmc_project/Common/Device/error.hpp>
93f1242f3SBrandon Wyman
101d7a7df8SBrandon Wyman #include <chrono> // sleep_for()
114fc191f0SBrandon Wyman #include <cmath>
121d7a7df8SBrandon Wyman #include <cstdint> // uint8_t...
13768d2269SShawn McCarney #include <format>
14681b2a36SB. J. Wyman #include <fstream>
15056935caSBrandon Wyman #include <regex>
161d7a7df8SBrandon Wyman #include <thread> // sleep_for()
171d7a7df8SBrandon Wyman
183f1242f3SBrandon Wyman namespace phosphor::power::psu
19aed1f75dSBrandon Wyman {
20681b2a36SB. J. Wyman // Amount of time in milliseconds to delay between power supply going from
21681b2a36SB. J. Wyman // missing to present before running the bind command(s).
22681b2a36SB. J. Wyman constexpr auto bindDelay = 1000;
23aed1f75dSBrandon Wyman
24aed1f75dSBrandon Wyman using namespace phosphor::logging;
253f1242f3SBrandon Wyman using namespace sdbusplus::xyz::openbmc_project::Common::Device::Error;
26aed1f75dSBrandon Wyman
PowerSupply(sdbusplus::bus_t & bus,const std::string & invpath,std::uint8_t i2cbus,std::uint16_t i2caddr,const std::string & driver,const std::string & gpioLineName,std::function<bool ()> && callback)27*f5402197SPatrick Williams PowerSupply::PowerSupply(
28*f5402197SPatrick Williams sdbusplus::bus_t& bus, const std::string& invpath, std::uint8_t i2cbus,
29*f5402197SPatrick Williams std::uint16_t i2caddr, const std::string& driver,
30*f5402197SPatrick Williams const std::string& gpioLineName, std::function<bool()>&& callback) :
31*f5402197SPatrick Williams bus(bus), inventoryPath(invpath),
32*f5402197SPatrick Williams bindPath("/sys/bus/i2c/drivers/" + driver), isPowerOn(std::move(callback)),
33*f5402197SPatrick Williams driverName(driver)
34510acaabSBrandon Wyman {
35510acaabSBrandon Wyman if (inventoryPath.empty())
36510acaabSBrandon Wyman {
37510acaabSBrandon Wyman throw std::invalid_argument{"Invalid empty inventoryPath"};
38510acaabSBrandon Wyman }
39510acaabSBrandon Wyman
40681b2a36SB. J. Wyman if (gpioLineName.empty())
41681b2a36SB. J. Wyman {
42681b2a36SB. J. Wyman throw std::invalid_argument{"Invalid empty gpioLineName"};
43681b2a36SB. J. Wyman }
44681b2a36SB. J. Wyman
45321a615bSBrandon Wyman shortName = findShortName(inventoryPath);
46321a615bSBrandon Wyman
47321a615bSBrandon Wyman log<level::DEBUG>(
48768d2269SShawn McCarney std::format("{} gpioLineName: {}", shortName, gpioLineName).c_str());
49681b2a36SB. J. Wyman presenceGPIO = createGPIO(gpioLineName);
50681b2a36SB. J. Wyman
51681b2a36SB. J. Wyman std::ostringstream ss;
52681b2a36SB. J. Wyman ss << std::hex << std::setw(4) << std::setfill('0') << i2caddr;
53681b2a36SB. J. Wyman std::string addrStr = ss.str();
54681b2a36SB. J. Wyman std::string busStr = std::to_string(i2cbus);
55681b2a36SB. J. Wyman bindDevice = busStr;
56681b2a36SB. J. Wyman bindDevice.append("-");
57681b2a36SB. J. Wyman bindDevice.append(addrStr);
58681b2a36SB. J. Wyman
59681b2a36SB. J. Wyman pmbusIntf = phosphor::pmbus::createPMBus(i2cbus, addrStr);
60681b2a36SB. J. Wyman
61681b2a36SB. J. Wyman // Get the current state of the Present property.
62681b2a36SB. J. Wyman try
63681b2a36SB. J. Wyman {
64681b2a36SB. J. Wyman updatePresenceGPIO();
65681b2a36SB. J. Wyman }
66681b2a36SB. J. Wyman catch (...)
67681b2a36SB. J. Wyman {
68681b2a36SB. J. Wyman // If the above attempt to use the GPIO failed, it likely means that the
69681b2a36SB. J. Wyman // GPIOs are in use by the kernel, meaning it is using gpio-keys.
70681b2a36SB. J. Wyman // So, I should rely on phosphor-gpio-presence to update D-Bus, and
71681b2a36SB. J. Wyman // work that way for power supply presence.
72681b2a36SB. J. Wyman presenceGPIO = nullptr;
73510acaabSBrandon Wyman // Setup the functions to call when the D-Bus inventory path for the
74510acaabSBrandon Wyman // Present property changes.
75510acaabSBrandon Wyman presentMatch = std::make_unique<sdbusplus::bus::match_t>(
76510acaabSBrandon Wyman bus,
77510acaabSBrandon Wyman sdbusplus::bus::match::rules::propertiesChanged(inventoryPath,
78510acaabSBrandon Wyman INVENTORY_IFACE),
79510acaabSBrandon Wyman [this](auto& msg) { this->inventoryChanged(msg); });
80510acaabSBrandon Wyman
81510acaabSBrandon Wyman presentAddedMatch = std::make_unique<sdbusplus::bus::match_t>(
82510acaabSBrandon Wyman bus,
83510acaabSBrandon Wyman sdbusplus::bus::match::rules::interfacesAdded() +
84510acaabSBrandon Wyman sdbusplus::bus::match::rules::argNpath(0, inventoryPath),
85510acaabSBrandon Wyman [this](auto& msg) { this->inventoryAdded(msg); });
86510acaabSBrandon Wyman
87510acaabSBrandon Wyman updatePresence();
88cb091801SBrandon Wyman updateInventory();
89592bd27cSMatt Spinler setupSensors();
90510acaabSBrandon Wyman }
91a068f424SMatt Spinler
92a068f424SMatt Spinler setInputVoltageRating();
93681b2a36SB. J. Wyman }
94681b2a36SB. J. Wyman
bindOrUnbindDriver(bool present)95681b2a36SB. J. Wyman void PowerSupply::bindOrUnbindDriver(bool present)
96681b2a36SB. J. Wyman {
97b7131a12SFaisal Awada // Symbolic link to the device will exist if the driver is bound.
98b7131a12SFaisal Awada // So exit no action required if both the link and PSU are present
99b7131a12SFaisal Awada // or neither is present.
100b7131a12SFaisal Awada namespace fs = std::filesystem;
101b7131a12SFaisal Awada fs::path path;
102681b2a36SB. J. Wyman auto action = (present) ? "bind" : "unbind";
103681b2a36SB. J. Wyman
104b7131a12SFaisal Awada // This case should not happen, if no device driver name return.
105b7131a12SFaisal Awada if (driverName.empty())
106b7131a12SFaisal Awada {
107b7131a12SFaisal Awada log<level::INFO>("No device driver name found");
108b7131a12SFaisal Awada return;
109b7131a12SFaisal Awada }
110b7131a12SFaisal Awada if (bindPath.string().find(driverName) != std::string::npos)
111b7131a12SFaisal Awada {
112b7131a12SFaisal Awada // bindPath has driver name
113b7131a12SFaisal Awada path = bindPath / action;
114b7131a12SFaisal Awada }
115b7131a12SFaisal Awada else
116b7131a12SFaisal Awada {
117b7131a12SFaisal Awada // Add driver name to bindPath
118b7131a12SFaisal Awada path = bindPath / driverName / action;
119b7131a12SFaisal Awada bindPath = bindPath / driverName;
120b7131a12SFaisal Awada }
121b7131a12SFaisal Awada
122b7131a12SFaisal Awada if ((std::filesystem::exists(bindPath / bindDevice) && present) ||
123b7131a12SFaisal Awada (!std::filesystem::exists(bindPath / bindDevice) && !present))
124b7131a12SFaisal Awada {
125b7131a12SFaisal Awada return;
126b7131a12SFaisal Awada }
127681b2a36SB. J. Wyman if (present)
128681b2a36SB. J. Wyman {
129b1ee60f0SBrandon Wyman std::this_thread::sleep_for(std::chrono::milliseconds(bindDelay));
130681b2a36SB. J. Wyman log<level::INFO>(
131768d2269SShawn McCarney std::format("Binding device driver. path: {} device: {}",
132681b2a36SB. J. Wyman path.string(), bindDevice)
133681b2a36SB. J. Wyman .c_str());
134681b2a36SB. J. Wyman }
135681b2a36SB. J. Wyman else
136681b2a36SB. J. Wyman {
137681b2a36SB. J. Wyman log<level::INFO>(
138768d2269SShawn McCarney std::format("Unbinding device driver. path: {} device: {}",
139681b2a36SB. J. Wyman path.string(), bindDevice)
140681b2a36SB. J. Wyman .c_str());
141681b2a36SB. J. Wyman }
142681b2a36SB. J. Wyman
143681b2a36SB. J. Wyman std::ofstream file;
144681b2a36SB. J. Wyman
145681b2a36SB. J. Wyman file.exceptions(std::ofstream::failbit | std::ofstream::badbit |
146681b2a36SB. J. Wyman std::ofstream::eofbit);
147681b2a36SB. J. Wyman
148681b2a36SB. J. Wyman try
149681b2a36SB. J. Wyman {
150681b2a36SB. J. Wyman file.open(path);
151681b2a36SB. J. Wyman file << bindDevice;
152681b2a36SB. J. Wyman file.close();
153681b2a36SB. J. Wyman }
154c1d4de5eSPatrick Williams catch (const std::exception& e)
155681b2a36SB. J. Wyman {
156681b2a36SB. J. Wyman auto err = errno;
157681b2a36SB. J. Wyman
158681b2a36SB. J. Wyman log<level::ERR>(
159768d2269SShawn McCarney std::format("Failed binding or unbinding device. errno={}", err)
160681b2a36SB. J. Wyman .c_str());
161681b2a36SB. J. Wyman }
162681b2a36SB. J. Wyman }
163510acaabSBrandon Wyman
updatePresence()164aed1f75dSBrandon Wyman void PowerSupply::updatePresence()
165aed1f75dSBrandon Wyman {
166aed1f75dSBrandon Wyman try
167aed1f75dSBrandon Wyman {
1683f1242f3SBrandon Wyman present = getPresence(bus, inventoryPath);
169aed1f75dSBrandon Wyman }
1707354ce62SPatrick Williams catch (const sdbusplus::exception_t& e)
171aed1f75dSBrandon Wyman {
172aed1f75dSBrandon Wyman // Relying on property change or interface added to retry.
173aed1f75dSBrandon Wyman // Log an informational trace to the journal.
174df13c3abSBrandon Wyman log<level::INFO>(
175768d2269SShawn McCarney std::format("D-Bus property {} access failure exception",
176df13c3abSBrandon Wyman inventoryPath)
177df13c3abSBrandon Wyman .c_str());
178aed1f75dSBrandon Wyman }
179aed1f75dSBrandon Wyman }
180aed1f75dSBrandon Wyman
updatePresenceGPIO()181681b2a36SB. J. Wyman void PowerSupply::updatePresenceGPIO()
182681b2a36SB. J. Wyman {
183681b2a36SB. J. Wyman bool presentOld = present;
184681b2a36SB. J. Wyman
185681b2a36SB. J. Wyman try
186681b2a36SB. J. Wyman {
187681b2a36SB. J. Wyman if (presenceGPIO->read() > 0)
188681b2a36SB. J. Wyman {
189681b2a36SB. J. Wyman present = true;
190681b2a36SB. J. Wyman }
191681b2a36SB. J. Wyman else
192681b2a36SB. J. Wyman {
193681b2a36SB. J. Wyman present = false;
194681b2a36SB. J. Wyman }
195681b2a36SB. J. Wyman }
196c1d4de5eSPatrick Williams catch (const std::exception& e)
197681b2a36SB. J. Wyman {
198681b2a36SB. J. Wyman log<level::ERR>(
199768d2269SShawn McCarney std::format("presenceGPIO read fail: {}", e.what()).c_str());
200681b2a36SB. J. Wyman throw;
201681b2a36SB. J. Wyman }
202681b2a36SB. J. Wyman
203681b2a36SB. J. Wyman if (presentOld != present)
204681b2a36SB. J. Wyman {
205768d2269SShawn McCarney log<level::DEBUG>(std::format("{} presentOld: {} present: {}",
206321a615bSBrandon Wyman shortName, presentOld, present)
207681b2a36SB. J. Wyman .c_str());
208ca1e9ea1SMatt Spinler
209ca1e9ea1SMatt Spinler auto invpath = inventoryPath.substr(strlen(INVENTORY_OBJ_PATH));
21090d529afSBrandon Wyman
21190d529afSBrandon Wyman bindOrUnbindDriver(present);
21290d529afSBrandon Wyman if (present)
21390d529afSBrandon Wyman {
21490d529afSBrandon Wyman // If the power supply was present, then missing, and present again,
21590d529afSBrandon Wyman // the hwmon path may have changed. We will need the correct/updated
21690d529afSBrandon Wyman // path before any reads or writes are attempted.
21790d529afSBrandon Wyman pmbusIntf->findHwmonDir();
21890d529afSBrandon Wyman }
21990d529afSBrandon Wyman
220321a615bSBrandon Wyman setPresence(bus, invpath, present, shortName);
221592bd27cSMatt Spinler setupSensors();
222ca1e9ea1SMatt Spinler updateInventory();
223ca1e9ea1SMatt Spinler
22490d529afSBrandon Wyman // Need Functional to already be correct before calling this.
225ca1e9ea1SMatt Spinler checkAvailability();
226ca1e9ea1SMatt Spinler
227681b2a36SB. J. Wyman if (present)
228681b2a36SB. J. Wyman {
229681b2a36SB. J. Wyman onOffConfig(phosphor::pmbus::ON_OFF_CONFIG_CONTROL_PIN_ONLY);
230681b2a36SB. J. Wyman clearFaults();
23118a24d92SBrandon Wyman // Indicate that the input history data and timestamps between all
23218a24d92SBrandon Wyman // the power supplies that are present in the system need to be
23318a24d92SBrandon Wyman // synchronized.
23418a24d92SBrandon Wyman syncHistoryRequired = true;
235681b2a36SB. J. Wyman }
236592bd27cSMatt Spinler else
237592bd27cSMatt Spinler {
238592bd27cSMatt Spinler setSensorsNotAvailable();
239592bd27cSMatt Spinler }
240681b2a36SB. J. Wyman }
241681b2a36SB. J. Wyman }
242681b2a36SB. J. Wyman
analyzeCMLFault()243c220343cSBrandon Wyman void PowerSupply::analyzeCMLFault()
244c220343cSBrandon Wyman {
245c220343cSBrandon Wyman if (statusWord & phosphor::pmbus::status_word::CML_FAULT)
246c220343cSBrandon Wyman {
247c2906f47SBrandon Wyman if (cmlFault < DEGLITCH_LIMIT)
248c220343cSBrandon Wyman {
2499e292ee2SBrandon Wyman if (statusWord != statusWordOld)
2509e292ee2SBrandon Wyman {
251321a615bSBrandon Wyman log<level::ERR>(
252768d2269SShawn McCarney std::format("{} CML fault: STATUS_WORD = {:#06x}, "
2539e292ee2SBrandon Wyman "STATUS_CML = {:#02x}",
254321a615bSBrandon Wyman shortName, statusWord, statusCML)
255c220343cSBrandon Wyman .c_str());
2569e292ee2SBrandon Wyman }
257c2906f47SBrandon Wyman cmlFault++;
258c2906f47SBrandon Wyman }
259c2906f47SBrandon Wyman }
260c2906f47SBrandon Wyman else
261c2906f47SBrandon Wyman {
262c2906f47SBrandon Wyman cmlFault = 0;
263c220343cSBrandon Wyman }
264c220343cSBrandon Wyman }
265c220343cSBrandon Wyman
analyzeInputFault()266e3b0bb01SBrandon Wyman void PowerSupply::analyzeInputFault()
267e3b0bb01SBrandon Wyman {
268e3b0bb01SBrandon Wyman if (statusWord & phosphor::pmbus::status_word::INPUT_FAULT_WARN)
269e3b0bb01SBrandon Wyman {
270c2906f47SBrandon Wyman if (inputFault < DEGLITCH_LIMIT)
271e3b0bb01SBrandon Wyman {
2729e292ee2SBrandon Wyman if (statusWord != statusWordOld)
2739e292ee2SBrandon Wyman {
2749e292ee2SBrandon Wyman log<level::ERR>(
275768d2269SShawn McCarney std::format("{} INPUT fault: STATUS_WORD = {:#06x}, "
2766f939a31SBrandon Wyman "STATUS_MFR_SPECIFIC = {:#04x}, "
2776f939a31SBrandon Wyman "STATUS_INPUT = {:#04x}",
278321a615bSBrandon Wyman shortName, statusWord, statusMFR, statusInput)
279e3b0bb01SBrandon Wyman .c_str());
2809e292ee2SBrandon Wyman }
281c2906f47SBrandon Wyman inputFault++;
282c2906f47SBrandon Wyman }
283e3b0bb01SBrandon Wyman }
28482affd94SBrandon Wyman
28582affd94SBrandon Wyman // If had INPUT/VIN_UV fault, and now off.
28682affd94SBrandon Wyman // Trace that odd behavior.
28782affd94SBrandon Wyman if (inputFault &&
28882affd94SBrandon Wyman !(statusWord & phosphor::pmbus::status_word::INPUT_FAULT_WARN))
28982affd94SBrandon Wyman {
29082affd94SBrandon Wyman log<level::INFO>(
291768d2269SShawn McCarney std::format("{} INPUT fault cleared: STATUS_WORD = {:#06x}, "
2926f939a31SBrandon Wyman "STATUS_MFR_SPECIFIC = {:#04x}, "
2936f939a31SBrandon Wyman "STATUS_INPUT = {:#04x}",
294321a615bSBrandon Wyman shortName, statusWord, statusMFR, statusInput)
29582affd94SBrandon Wyman .c_str());
296c2906f47SBrandon Wyman inputFault = 0;
29782affd94SBrandon Wyman }
298e3b0bb01SBrandon Wyman }
299e3b0bb01SBrandon Wyman
analyzeVoutOVFault()300c2c87131SBrandon Wyman void PowerSupply::analyzeVoutOVFault()
301c2c87131SBrandon Wyman {
302c2c87131SBrandon Wyman if (statusWord & phosphor::pmbus::status_word::VOUT_OV_FAULT)
303c2c87131SBrandon Wyman {
304c2906f47SBrandon Wyman if (voutOVFault < DEGLITCH_LIMIT)
305c2c87131SBrandon Wyman {
3069e292ee2SBrandon Wyman if (statusWord != statusWordOld)
3079e292ee2SBrandon Wyman {
308c2c87131SBrandon Wyman log<level::ERR>(
309768d2269SShawn McCarney std::format(
310321a615bSBrandon Wyman "{} VOUT_OV_FAULT fault: STATUS_WORD = {:#06x}, "
3116f939a31SBrandon Wyman "STATUS_MFR_SPECIFIC = {:#04x}, "
3129e292ee2SBrandon Wyman "STATUS_VOUT = {:#02x}",
313321a615bSBrandon Wyman shortName, statusWord, statusMFR, statusVout)
314c2c87131SBrandon Wyman .c_str());
3159e292ee2SBrandon Wyman }
316c2c87131SBrandon Wyman
317c2906f47SBrandon Wyman voutOVFault++;
318c2906f47SBrandon Wyman }
319c2906f47SBrandon Wyman }
320c2906f47SBrandon Wyman else
321c2906f47SBrandon Wyman {
322c2906f47SBrandon Wyman voutOVFault = 0;
323c2c87131SBrandon Wyman }
324c2c87131SBrandon Wyman }
325c2c87131SBrandon Wyman
analyzeIoutOCFault()326a00e7300SBrandon Wyman void PowerSupply::analyzeIoutOCFault()
327a00e7300SBrandon Wyman {
328a00e7300SBrandon Wyman if (statusWord & phosphor::pmbus::status_word::IOUT_OC_FAULT)
329a00e7300SBrandon Wyman {
330c2906f47SBrandon Wyman if (ioutOCFault < DEGLITCH_LIMIT)
331a00e7300SBrandon Wyman {
3329e292ee2SBrandon Wyman if (statusWord != statusWordOld)
3339e292ee2SBrandon Wyman {
3349e292ee2SBrandon Wyman log<level::ERR>(
335768d2269SShawn McCarney std::format("{} IOUT fault: STATUS_WORD = {:#06x}, "
3366f939a31SBrandon Wyman "STATUS_MFR_SPECIFIC = {:#04x}, "
3376f939a31SBrandon Wyman "STATUS_IOUT = {:#04x}",
338321a615bSBrandon Wyman shortName, statusWord, statusMFR, statusIout)
339a00e7300SBrandon Wyman .c_str());
3409e292ee2SBrandon Wyman }
341a00e7300SBrandon Wyman
342c2906f47SBrandon Wyman ioutOCFault++;
343c2906f47SBrandon Wyman }
344c2906f47SBrandon Wyman }
345c2906f47SBrandon Wyman else
346c2906f47SBrandon Wyman {
347c2906f47SBrandon Wyman ioutOCFault = 0;
348a00e7300SBrandon Wyman }
349a00e7300SBrandon Wyman }
350a00e7300SBrandon Wyman
analyzeVoutUVFault()35108378784SBrandon Wyman void PowerSupply::analyzeVoutUVFault()
35208378784SBrandon Wyman {
35308378784SBrandon Wyman if ((statusWord & phosphor::pmbus::status_word::VOUT_FAULT) &&
35408378784SBrandon Wyman !(statusWord & phosphor::pmbus::status_word::VOUT_OV_FAULT))
35508378784SBrandon Wyman {
356c2906f47SBrandon Wyman if (voutUVFault < DEGLITCH_LIMIT)
35708378784SBrandon Wyman {
3589e292ee2SBrandon Wyman if (statusWord != statusWordOld)
3599e292ee2SBrandon Wyman {
36008378784SBrandon Wyman log<level::ERR>(
361768d2269SShawn McCarney std::format(
362321a615bSBrandon Wyman "{} VOUT_UV_FAULT fault: STATUS_WORD = {:#06x}, "
3636f939a31SBrandon Wyman "STATUS_MFR_SPECIFIC = {:#04x}, "
3646f939a31SBrandon Wyman "STATUS_VOUT = {:#04x}",
365321a615bSBrandon Wyman shortName, statusWord, statusMFR, statusVout)
36608378784SBrandon Wyman .c_str());
3679e292ee2SBrandon Wyman }
368c2906f47SBrandon Wyman voutUVFault++;
369c2906f47SBrandon Wyman }
370c2906f47SBrandon Wyman }
371c2906f47SBrandon Wyman else
372c2906f47SBrandon Wyman {
373c2906f47SBrandon Wyman voutUVFault = 0;
37408378784SBrandon Wyman }
37508378784SBrandon Wyman }
37608378784SBrandon Wyman
analyzeFanFault()377d5d9a225SBrandon Wyman void PowerSupply::analyzeFanFault()
378d5d9a225SBrandon Wyman {
379d5d9a225SBrandon Wyman if (statusWord & phosphor::pmbus::status_word::FAN_FAULT)
380d5d9a225SBrandon Wyman {
381c2906f47SBrandon Wyman if (fanFault < DEGLITCH_LIMIT)
382d5d9a225SBrandon Wyman {
3839e292ee2SBrandon Wyman if (statusWord != statusWordOld)
3849e292ee2SBrandon Wyman {
385*f5402197SPatrick Williams log<level::ERR>(
386*f5402197SPatrick Williams std::format("{} FANS fault/warning: "
3876f939a31SBrandon Wyman "STATUS_WORD = {:#06x}, "
3886f939a31SBrandon Wyman "STATUS_MFR_SPECIFIC = {:#04x}, "
3896f939a31SBrandon Wyman "STATUS_FANS_1_2 = {:#04x}",
390*f5402197SPatrick Williams shortName, statusWord, statusMFR, statusFans12)
391d5d9a225SBrandon Wyman .c_str());
3929e292ee2SBrandon Wyman }
393c2906f47SBrandon Wyman fanFault++;
394c2906f47SBrandon Wyman }
395c2906f47SBrandon Wyman }
396c2906f47SBrandon Wyman else
397c2906f47SBrandon Wyman {
398c2906f47SBrandon Wyman fanFault = 0;
399d5d9a225SBrandon Wyman }
400d5d9a225SBrandon Wyman }
401d5d9a225SBrandon Wyman
analyzeTemperatureFault()40252cb3f28SBrandon Wyman void PowerSupply::analyzeTemperatureFault()
40352cb3f28SBrandon Wyman {
40452cb3f28SBrandon Wyman if (statusWord & phosphor::pmbus::status_word::TEMPERATURE_FAULT_WARN)
40552cb3f28SBrandon Wyman {
406c2906f47SBrandon Wyman if (tempFault < DEGLITCH_LIMIT)
40752cb3f28SBrandon Wyman {
4089e292ee2SBrandon Wyman if (statusWord != statusWordOld)
4099e292ee2SBrandon Wyman {
410768d2269SShawn McCarney log<level::ERR>(std::format("{} TEMPERATURE fault/warning: "
4116f939a31SBrandon Wyman "STATUS_WORD = {:#06x}, "
4126f939a31SBrandon Wyman "STATUS_MFR_SPECIFIC = {:#04x}, "
4136f939a31SBrandon Wyman "STATUS_TEMPERATURE = {:#04x}",
414321a615bSBrandon Wyman shortName, statusWord, statusMFR,
41552cb3f28SBrandon Wyman statusTemperature)
41652cb3f28SBrandon Wyman .c_str());
4179e292ee2SBrandon Wyman }
418c2906f47SBrandon Wyman tempFault++;
419c2906f47SBrandon Wyman }
420c2906f47SBrandon Wyman }
421c2906f47SBrandon Wyman else
422c2906f47SBrandon Wyman {
423c2906f47SBrandon Wyman tempFault = 0;
42452cb3f28SBrandon Wyman }
42552cb3f28SBrandon Wyman }
42652cb3f28SBrandon Wyman
analyzePgoodFault()427993b554fSBrandon Wyman void PowerSupply::analyzePgoodFault()
428993b554fSBrandon Wyman {
429993b554fSBrandon Wyman if ((statusWord & phosphor::pmbus::status_word::POWER_GOOD_NEGATED) ||
430993b554fSBrandon Wyman (statusWord & phosphor::pmbus::status_word::UNIT_IS_OFF))
431993b554fSBrandon Wyman {
4326d469fd4SBrandon Wyman if (pgoodFault < PGOOD_DEGLITCH_LIMIT)
433993b554fSBrandon Wyman {
4349e292ee2SBrandon Wyman if (statusWord != statusWordOld)
4359e292ee2SBrandon Wyman {
436768d2269SShawn McCarney log<level::ERR>(std::format("{} PGOOD fault: "
4376f939a31SBrandon Wyman "STATUS_WORD = {:#06x}, "
4386f939a31SBrandon Wyman "STATUS_MFR_SPECIFIC = {:#04x}",
439321a615bSBrandon Wyman shortName, statusWord, statusMFR)
440993b554fSBrandon Wyman .c_str());
4419e292ee2SBrandon Wyman }
442993b554fSBrandon Wyman pgoodFault++;
443993b554fSBrandon Wyman }
444993b554fSBrandon Wyman }
445993b554fSBrandon Wyman else
446993b554fSBrandon Wyman {
447993b554fSBrandon Wyman pgoodFault = 0;
448993b554fSBrandon Wyman }
449993b554fSBrandon Wyman }
450993b554fSBrandon Wyman
determineMFRFault()45139ea02bcSBrandon Wyman void PowerSupply::determineMFRFault()
45239ea02bcSBrandon Wyman {
453b66ae50aSFaisal Awada if (bindPath.string().find(IBMCFFPS_DD_NAME) != std::string::npos)
45439ea02bcSBrandon Wyman {
45539ea02bcSBrandon Wyman // IBM MFR_SPECIFIC[4] is PS_Kill fault
45639ea02bcSBrandon Wyman if (statusMFR & 0x10)
45739ea02bcSBrandon Wyman {
458c2906f47SBrandon Wyman if (psKillFault < DEGLITCH_LIMIT)
459c2906f47SBrandon Wyman {
460c2906f47SBrandon Wyman psKillFault++;
461c2906f47SBrandon Wyman }
462c2906f47SBrandon Wyman }
463c2906f47SBrandon Wyman else
464c2906f47SBrandon Wyman {
465c2906f47SBrandon Wyman psKillFault = 0;
46639ea02bcSBrandon Wyman }
46739ea02bcSBrandon Wyman // IBM MFR_SPECIFIC[6] is 12Vcs fault.
46839ea02bcSBrandon Wyman if (statusMFR & 0x40)
46939ea02bcSBrandon Wyman {
470c2906f47SBrandon Wyman if (ps12VcsFault < DEGLITCH_LIMIT)
471c2906f47SBrandon Wyman {
472c2906f47SBrandon Wyman ps12VcsFault++;
473c2906f47SBrandon Wyman }
474c2906f47SBrandon Wyman }
475c2906f47SBrandon Wyman else
476c2906f47SBrandon Wyman {
477c2906f47SBrandon Wyman ps12VcsFault = 0;
47839ea02bcSBrandon Wyman }
47939ea02bcSBrandon Wyman // IBM MFR_SPECIFIC[7] is 12V Current-Share fault.
48039ea02bcSBrandon Wyman if (statusMFR & 0x80)
48139ea02bcSBrandon Wyman {
482c2906f47SBrandon Wyman if (psCS12VFault < DEGLITCH_LIMIT)
483c2906f47SBrandon Wyman {
484c2906f47SBrandon Wyman psCS12VFault++;
485c2906f47SBrandon Wyman }
486c2906f47SBrandon Wyman }
487c2906f47SBrandon Wyman else
488c2906f47SBrandon Wyman {
489c2906f47SBrandon Wyman psCS12VFault = 0;
49039ea02bcSBrandon Wyman }
49139ea02bcSBrandon Wyman }
49239ea02bcSBrandon Wyman }
49339ea02bcSBrandon Wyman
analyzeMFRFault()4946c2ac394SBrandon Wyman void PowerSupply::analyzeMFRFault()
4956c2ac394SBrandon Wyman {
4966c2ac394SBrandon Wyman if (statusWord & phosphor::pmbus::status_word::MFR_SPECIFIC_FAULT)
4976c2ac394SBrandon Wyman {
498c2906f47SBrandon Wyman if (mfrFault < DEGLITCH_LIMIT)
4996c2ac394SBrandon Wyman {
5009e292ee2SBrandon Wyman if (statusWord != statusWordOld)
5019e292ee2SBrandon Wyman {
502768d2269SShawn McCarney log<level::ERR>(std::format("{} MFR fault: "
5036f939a31SBrandon Wyman "STATUS_WORD = {:#06x} "
5046f939a31SBrandon Wyman "STATUS_MFR_SPECIFIC = {:#04x}",
505321a615bSBrandon Wyman shortName, statusWord, statusMFR)
5066c2ac394SBrandon Wyman .c_str());
5079e292ee2SBrandon Wyman }
508c2906f47SBrandon Wyman mfrFault++;
5096c2ac394SBrandon Wyman }
5106c2ac394SBrandon Wyman
5116c2ac394SBrandon Wyman determineMFRFault();
5126c2ac394SBrandon Wyman }
513c2906f47SBrandon Wyman else
514c2906f47SBrandon Wyman {
515c2906f47SBrandon Wyman mfrFault = 0;
516c2906f47SBrandon Wyman }
5176c2ac394SBrandon Wyman }
5186c2ac394SBrandon Wyman
analyzeVinUVFault()519f087f475SBrandon Wyman void PowerSupply::analyzeVinUVFault()
520f087f475SBrandon Wyman {
521f087f475SBrandon Wyman if (statusWord & phosphor::pmbus::status_word::VIN_UV_FAULT)
522f087f475SBrandon Wyman {
523c2906f47SBrandon Wyman if (vinUVFault < DEGLITCH_LIMIT)
524f087f475SBrandon Wyman {
5259e292ee2SBrandon Wyman if (statusWord != statusWordOld)
5269e292ee2SBrandon Wyman {
5279e292ee2SBrandon Wyman log<level::ERR>(
528768d2269SShawn McCarney std::format("{} VIN_UV fault: STATUS_WORD = {:#06x}, "
5296f939a31SBrandon Wyman "STATUS_MFR_SPECIFIC = {:#04x}, "
5306f939a31SBrandon Wyman "STATUS_INPUT = {:#04x}",
531321a615bSBrandon Wyman shortName, statusWord, statusMFR, statusInput)
532f087f475SBrandon Wyman .c_str());
5339e292ee2SBrandon Wyman }
534c2906f47SBrandon Wyman vinUVFault++;
535f087f475SBrandon Wyman }
5364ab86564SJim Wright // Remember that this PSU has seen an AC fault
5374ab86564SJim Wright acFault = AC_FAULT_LIMIT;
538f087f475SBrandon Wyman }
5397f9288ceSJim Wright else
5407f9288ceSJim Wright {
5417f9288ceSJim Wright if (vinUVFault != 0)
54282affd94SBrandon Wyman {
54382affd94SBrandon Wyman log<level::INFO>(
544768d2269SShawn McCarney std::format("{} VIN_UV fault cleared: STATUS_WORD = {:#06x}, "
5456f939a31SBrandon Wyman "STATUS_MFR_SPECIFIC = {:#04x}, "
5466f939a31SBrandon Wyman "STATUS_INPUT = {:#04x}",
547321a615bSBrandon Wyman shortName, statusWord, statusMFR, statusInput)
54882affd94SBrandon Wyman .c_str());
549c2906f47SBrandon Wyman vinUVFault = 0;
5507f9288ceSJim Wright }
5514ab86564SJim Wright // No AC fail, decrement counter
5527f9288ceSJim Wright if (acFault != 0)
5534ab86564SJim Wright {
5544ab86564SJim Wright --acFault;
5554ab86564SJim Wright }
55682affd94SBrandon Wyman }
557f087f475SBrandon Wyman }
558f087f475SBrandon Wyman
analyze()5593f1242f3SBrandon Wyman void PowerSupply::analyze()
5603f1242f3SBrandon Wyman {
5613f1242f3SBrandon Wyman using namespace phosphor::pmbus;
5623f1242f3SBrandon Wyman
563681b2a36SB. J. Wyman if (presenceGPIO)
564681b2a36SB. J. Wyman {
565681b2a36SB. J. Wyman updatePresenceGPIO();
566681b2a36SB. J. Wyman }
567681b2a36SB. J. Wyman
56832453e9bSBrandon Wyman if (present)
5693f1242f3SBrandon Wyman {
5703f1242f3SBrandon Wyman try
5713f1242f3SBrandon Wyman {
5729e292ee2SBrandon Wyman statusWordOld = statusWord;
57332453e9bSBrandon Wyman statusWord = pmbusIntf->read(STATUS_WORD, Type::Debug,
57432453e9bSBrandon Wyman (readFail < LOG_LIMIT));
575f65c4060SBrandon Wyman // Read worked, reset the fail count.
576f65c4060SBrandon Wyman readFail = 0;
5773f1242f3SBrandon Wyman
5783f1242f3SBrandon Wyman if (statusWord)
5793f1242f3SBrandon Wyman {
580f07bc797SBrandon Wyman statusInput = pmbusIntf->read(STATUS_INPUT, Type::Debug);
581b66ae50aSFaisal Awada if (bindPath.string().find(IBMCFFPS_DD_NAME) !=
582b66ae50aSFaisal Awada std::string::npos)
583b66ae50aSFaisal Awada {
58410d94055SJay Meyer statusMFR = pmbusIntf->read(STATUS_MFR, Type::Debug);
585b66ae50aSFaisal Awada }
58685c7bf41SBrandon Wyman statusCML = pmbusIntf->read(STATUS_CML, Type::Debug);
5876710ba2cSBrandon Wyman auto status0Vout = pmbusIntf->insertPageNum(STATUS_VOUT, 0);
5886710ba2cSBrandon Wyman statusVout = pmbusIntf->read(status0Vout, Type::Debug);
589b10b3be0SBrandon Wyman statusIout = pmbusIntf->read(STATUS_IOUT, Type::Debug);
5907ee4d7e4SBrandon Wyman statusFans12 = pmbusIntf->read(STATUS_FANS_1_2, Type::Debug);
591*f5402197SPatrick Williams statusTemperature =
592*f5402197SPatrick Williams pmbusIntf->read(STATUS_TEMPERATURE, Type::Debug);
5939ddc622eSBrandon Wyman
594c220343cSBrandon Wyman analyzeCMLFault();
59585c7bf41SBrandon Wyman
596e3b0bb01SBrandon Wyman analyzeInputFault();
5973f1242f3SBrandon Wyman
598c2c87131SBrandon Wyman analyzeVoutOVFault();
5996710ba2cSBrandon Wyman
600a00e7300SBrandon Wyman analyzeIoutOCFault();
601b10b3be0SBrandon Wyman
60208378784SBrandon Wyman analyzeVoutUVFault();
6032cf46945SBrandon Wyman
604d5d9a225SBrandon Wyman analyzeFanFault();
6057ee4d7e4SBrandon Wyman
60652cb3f28SBrandon Wyman analyzeTemperatureFault();
60796893a46SBrandon Wyman
608993b554fSBrandon Wyman analyzePgoodFault();
6092916ea52SBrandon Wyman
6106c2ac394SBrandon Wyman analyzeMFRFault();
6113f1242f3SBrandon Wyman
612f087f475SBrandon Wyman analyzeVinUVFault();
6133f1242f3SBrandon Wyman }
6143f1242f3SBrandon Wyman else
6153f1242f3SBrandon Wyman {
6169e292ee2SBrandon Wyman if (statusWord != statusWordOld)
6179e292ee2SBrandon Wyman {
618768d2269SShawn McCarney log<level::INFO>(std::format("{} STATUS_WORD = {:#06x}",
619321a615bSBrandon Wyman shortName, statusWord)
6209e292ee2SBrandon Wyman .c_str());
6219e292ee2SBrandon Wyman }
6229e292ee2SBrandon Wyman
623e3f7ad23SBrandon Wyman // if INPUT/VIN_UV fault was on, it cleared, trace it.
624e3f7ad23SBrandon Wyman if (inputFault)
625e3f7ad23SBrandon Wyman {
626e3f7ad23SBrandon Wyman log<level::INFO>(
627768d2269SShawn McCarney std::format(
628321a615bSBrandon Wyman "{} INPUT fault cleared: STATUS_WORD = {:#06x}",
629321a615bSBrandon Wyman shortName, statusWord)
630e3f7ad23SBrandon Wyman .c_str());
631e3f7ad23SBrandon Wyman }
632e3f7ad23SBrandon Wyman
633e3f7ad23SBrandon Wyman if (vinUVFault)
634e3f7ad23SBrandon Wyman {
635e3f7ad23SBrandon Wyman log<level::INFO>(
636768d2269SShawn McCarney std::format("{} VIN_UV cleared: STATUS_WORD = {:#06x}",
637321a615bSBrandon Wyman shortName, statusWord)
638e3f7ad23SBrandon Wyman .c_str());
639e3f7ad23SBrandon Wyman }
640e3f7ad23SBrandon Wyman
64106ca4590SBrandon Wyman if (pgoodFault > 0)
6424aecc295SBrandon Wyman {
643321a615bSBrandon Wyman log<level::INFO>(
644768d2269SShawn McCarney std::format("{} pgoodFault cleared", shortName)
6454aecc295SBrandon Wyman .c_str());
6463f1242f3SBrandon Wyman }
647e3f7ad23SBrandon Wyman
648e3f7ad23SBrandon Wyman clearFaultFlags();
6494ab86564SJim Wright // No AC fail, decrement counter
6507f9288ceSJim Wright if (acFault != 0)
6514ab86564SJim Wright {
6524ab86564SJim Wright --acFault;
6534ab86564SJim Wright }
6543f1242f3SBrandon Wyman }
65582affd94SBrandon Wyman
65682affd94SBrandon Wyman // Save off old inputVoltage value.
65782affd94SBrandon Wyman // Get latest inputVoltage.
65882affd94SBrandon Wyman // If voltage went from below minimum, and now is not, clear faults.
65982affd94SBrandon Wyman // Note: getInputVoltage() has its own try/catch.
66082affd94SBrandon Wyman int inputVoltageOld = inputVoltage;
6614fc191f0SBrandon Wyman double actualInputVoltageOld = actualInputVoltage;
66282affd94SBrandon Wyman getInputVoltage(actualInputVoltage, inputVoltage);
66382affd94SBrandon Wyman if ((inputVoltageOld == in_input::VIN_VOLTAGE_0) &&
66482affd94SBrandon Wyman (inputVoltage != in_input::VIN_VOLTAGE_0))
66582affd94SBrandon Wyman {
66682affd94SBrandon Wyman log<level::INFO>(
667768d2269SShawn McCarney std::format(
6684fc191f0SBrandon Wyman "{} READ_VIN back in range: actualInputVoltageOld = {} "
6694fc191f0SBrandon Wyman "actualInputVoltage = {}",
6704fc191f0SBrandon Wyman shortName, actualInputVoltageOld, actualInputVoltage)
67182affd94SBrandon Wyman .c_str());
6723225a45cSBrandon Wyman clearVinUVFault();
67382affd94SBrandon Wyman }
6744fc191f0SBrandon Wyman else if (vinUVFault && (inputVoltage != in_input::VIN_VOLTAGE_0))
6754fc191f0SBrandon Wyman {
6764fc191f0SBrandon Wyman log<level::INFO>(
677768d2269SShawn McCarney std::format(
6784fc191f0SBrandon Wyman "{} CLEAR_FAULTS: vinUVFault {} actualInputVoltage {}",
6794fc191f0SBrandon Wyman shortName, vinUVFault, actualInputVoltage)
6804fc191f0SBrandon Wyman .c_str());
6814fc191f0SBrandon Wyman // Do we have a VIN_UV fault latched that can now be cleared
6824ab86564SJim Wright // due to voltage back in range? Attempt to clear the
6834ab86564SJim Wright // fault(s), re-check faults on next call.
6843225a45cSBrandon Wyman clearVinUVFault();
6854fc191f0SBrandon Wyman }
686ae35ac5dSBrandon Wyman else if (std::abs(actualInputVoltageOld - actualInputVoltage) >
687ae35ac5dSBrandon Wyman 10.0)
6884fc191f0SBrandon Wyman {
6894fc191f0SBrandon Wyman log<level::INFO>(
690768d2269SShawn McCarney std::format(
6914fc191f0SBrandon Wyman "{} actualInputVoltageOld = {} actualInputVoltage = {}",
6924fc191f0SBrandon Wyman shortName, actualInputVoltageOld, actualInputVoltage)
6934fc191f0SBrandon Wyman .c_str());
6944fc191f0SBrandon Wyman }
6950975eaf4SMatt Spinler
696592bd27cSMatt Spinler monitorSensors();
697592bd27cSMatt Spinler
6980975eaf4SMatt Spinler checkAvailability();
6994aecc295SBrandon Wyman }
700c1d4de5eSPatrick Williams catch (const ReadFailure& e)
7013f1242f3SBrandon Wyman {
70232453e9bSBrandon Wyman if (readFail < SIZE_MAX)
70332453e9bSBrandon Wyman {
704f65c4060SBrandon Wyman readFail++;
70532453e9bSBrandon Wyman }
70632453e9bSBrandon Wyman if (readFail == LOG_LIMIT)
70732453e9bSBrandon Wyman {
7083f1242f3SBrandon Wyman phosphor::logging::commit<ReadFailure>();
7093f1242f3SBrandon Wyman }
7103f1242f3SBrandon Wyman }
7113f1242f3SBrandon Wyman }
71232453e9bSBrandon Wyman }
7133f1242f3SBrandon Wyman
onOffConfig(uint8_t data)71459a35793SBrandon Wyman void PowerSupply::onOffConfig(uint8_t data)
71559a35793SBrandon Wyman {
71659a35793SBrandon Wyman using namespace phosphor::pmbus;
71759a35793SBrandon Wyman
7189582d9ccSFaisal Awada if (present && driverName != ACBEL_FSG032_DD_NAME)
71959a35793SBrandon Wyman {
72059a35793SBrandon Wyman log<level::INFO>("ON_OFF_CONFIG write", entry("DATA=0x%02X", data));
72159a35793SBrandon Wyman try
72259a35793SBrandon Wyman {
72359a35793SBrandon Wyman std::vector<uint8_t> configData{data};
72459a35793SBrandon Wyman pmbusIntf->writeBinary(ON_OFF_CONFIG, configData,
72559a35793SBrandon Wyman Type::HwmonDeviceDebug);
72659a35793SBrandon Wyman }
72759a35793SBrandon Wyman catch (...)
72859a35793SBrandon Wyman {
72959a35793SBrandon Wyman // The underlying code in writeBinary will log a message to the
730681b2a36SB. J. Wyman // journal if the write fails. If the ON_OFF_CONFIG is not setup
731681b2a36SB. J. Wyman // as desired, later fault detection and analysis code should
732681b2a36SB. J. Wyman // catch any of the fall out. We should not need to terminate
733681b2a36SB. J. Wyman // the application if this write fails.
73459a35793SBrandon Wyman }
73559a35793SBrandon Wyman }
73659a35793SBrandon Wyman }
73759a35793SBrandon Wyman
clearVinUVFault()7383225a45cSBrandon Wyman void PowerSupply::clearVinUVFault()
7393225a45cSBrandon Wyman {
7403225a45cSBrandon Wyman // Read in1_lcrit_alarm to clear bits 3 and 4 of STATUS_INPUT.
7413225a45cSBrandon Wyman // The fault bits in STAUTS_INPUT roll-up to STATUS_WORD. Clearing those
7423225a45cSBrandon Wyman // bits in STATUS_INPUT should result in the corresponding STATUS_WORD bits
7433225a45cSBrandon Wyman // also clearing.
7443225a45cSBrandon Wyman //
7453225a45cSBrandon Wyman // Do not care about return value. Should be 1 if active, 0 if not.
7469582d9ccSFaisal Awada if (driverName != ACBEL_FSG032_DD_NAME)
7479582d9ccSFaisal Awada {
7483225a45cSBrandon Wyman static_cast<void>(
7493225a45cSBrandon Wyman pmbusIntf->read("in1_lcrit_alarm", phosphor::pmbus::Type::Hwmon));
7509582d9ccSFaisal Awada }
7519582d9ccSFaisal Awada else
7529582d9ccSFaisal Awada {
7539582d9ccSFaisal Awada static_cast<void>(
7549582d9ccSFaisal Awada pmbusIntf->read("curr1_crit_alarm", phosphor::pmbus::Type::Hwmon));
7559582d9ccSFaisal Awada }
7563225a45cSBrandon Wyman vinUVFault = 0;
7573225a45cSBrandon Wyman }
7583225a45cSBrandon Wyman
clearFaults()7593c208464SBrandon Wyman void PowerSupply::clearFaults()
7603c208464SBrandon Wyman {
76182affd94SBrandon Wyman log<level::DEBUG>(
762768d2269SShawn McCarney std::format("clearFaults() inventoryPath: {}", inventoryPath).c_str());
7635474c91aSBrandon Wyman faultLogged = false;
7643c208464SBrandon Wyman // The PMBus device driver does not allow for writing CLEAR_FAULTS
7653c208464SBrandon Wyman // directly. However, the pmbus hwmon device driver code will send a
7663c208464SBrandon Wyman // CLEAR_FAULTS after reading from any of the hwmon "files" in sysfs, so
7673c208464SBrandon Wyman // reading in1_input should result in clearing the fault bits in
7683c208464SBrandon Wyman // STATUS_BYTE/STATUS_WORD.
7693c208464SBrandon Wyman // I do not care what the return value is.
7701115153dSBrandon Wyman if (present)
7711115153dSBrandon Wyman {
772e3f7ad23SBrandon Wyman clearFaultFlags();
7730975eaf4SMatt Spinler checkAvailability();
7749564e945SBrandon Wyman readFail = 0;
7759564e945SBrandon Wyman
7763c208464SBrandon Wyman try
7773c208464SBrandon Wyman {
7783225a45cSBrandon Wyman clearVinUVFault();
7793c208464SBrandon Wyman static_cast<void>(
7803c208464SBrandon Wyman pmbusIntf->read("in1_input", phosphor::pmbus::Type::Hwmon));
7813c208464SBrandon Wyman }
782c1d4de5eSPatrick Williams catch (const ReadFailure& e)
7833c208464SBrandon Wyman {
7843c208464SBrandon Wyman // Since I do not care what the return value is, I really do not
785681b2a36SB. J. Wyman // care much if it gets a ReadFailure either. However, this
786681b2a36SB. J. Wyman // should not prevent the application from continuing to run, so
787681b2a36SB. J. Wyman // catching the read failure.
7881115153dSBrandon Wyman }
7893c208464SBrandon Wyman }
7903c208464SBrandon Wyman }
7913c208464SBrandon Wyman
inventoryChanged(sdbusplus::message_t & msg)7927354ce62SPatrick Williams void PowerSupply::inventoryChanged(sdbusplus::message_t& msg)
793aed1f75dSBrandon Wyman {
794aed1f75dSBrandon Wyman std::string msgSensor;
795abe49418SPatrick Williams std::map<std::string, std::variant<uint32_t, bool>> msgData;
796aed1f75dSBrandon Wyman msg.read(msgSensor, msgData);
797aed1f75dSBrandon Wyman
798aed1f75dSBrandon Wyman // Check if it was the Present property that changed.
799aed1f75dSBrandon Wyman auto valPropMap = msgData.find(PRESENT_PROP);
800aed1f75dSBrandon Wyman if (valPropMap != msgData.end())
801aed1f75dSBrandon Wyman {
802aed1f75dSBrandon Wyman if (std::get<bool>(valPropMap->second))
803aed1f75dSBrandon Wyman {
804aed1f75dSBrandon Wyman present = true;
805681b2a36SB. J. Wyman // TODO: Immediately trying to read or write the "files" causes
806681b2a36SB. J. Wyman // read or write failures.
8071d7a7df8SBrandon Wyman using namespace std::chrono_literals;
8081d7a7df8SBrandon Wyman std::this_thread::sleep_for(20ms);
8099564e945SBrandon Wyman pmbusIntf->findHwmonDir();
81059a35793SBrandon Wyman onOffConfig(phosphor::pmbus::ON_OFF_CONFIG_CONTROL_PIN_ONLY);
811aed1f75dSBrandon Wyman clearFaults();
8121d7a7df8SBrandon Wyman updateInventory();
813aed1f75dSBrandon Wyman }
814aed1f75dSBrandon Wyman else
815aed1f75dSBrandon Wyman {
816aed1f75dSBrandon Wyman present = false;
817aed1f75dSBrandon Wyman
818aed1f75dSBrandon Wyman // Clear out the now outdated inventory properties
819aed1f75dSBrandon Wyman updateInventory();
820aed1f75dSBrandon Wyman }
8210975eaf4SMatt Spinler checkAvailability();
822aed1f75dSBrandon Wyman }
823aed1f75dSBrandon Wyman }
824aed1f75dSBrandon Wyman
inventoryAdded(sdbusplus::message_t & msg)8257354ce62SPatrick Williams void PowerSupply::inventoryAdded(sdbusplus::message_t& msg)
8269a507db5SBrandon Wyman {
8279a507db5SBrandon Wyman sdbusplus::message::object_path path;
8289a507db5SBrandon Wyman msg.read(path);
8299a507db5SBrandon Wyman // Make sure the signal is for the PSU inventory path
8309a507db5SBrandon Wyman if (path == inventoryPath)
8319a507db5SBrandon Wyman {
8329a507db5SBrandon Wyman std::map<std::string, std::map<std::string, std::variant<bool>>>
8339a507db5SBrandon Wyman interfaces;
8349a507db5SBrandon Wyman // Get map of interfaces and their properties
8359a507db5SBrandon Wyman msg.read(interfaces);
8369a507db5SBrandon Wyman
8379a507db5SBrandon Wyman auto properties = interfaces.find(INVENTORY_IFACE);
8389a507db5SBrandon Wyman if (properties != interfaces.end())
8399a507db5SBrandon Wyman {
8409a507db5SBrandon Wyman auto property = properties->second.find(PRESENT_PROP);
8419a507db5SBrandon Wyman if (property != properties->second.end())
8429a507db5SBrandon Wyman {
8439a507db5SBrandon Wyman present = std::get<bool>(property->second);
8449a507db5SBrandon Wyman
845768d2269SShawn McCarney log<level::INFO>(std::format("Power Supply {} Present {}",
8469a507db5SBrandon Wyman inventoryPath, present)
8479a507db5SBrandon Wyman .c_str());
8489a507db5SBrandon Wyman
8499a507db5SBrandon Wyman updateInventory();
8500975eaf4SMatt Spinler checkAvailability();
8519a507db5SBrandon Wyman }
8529a507db5SBrandon Wyman }
8539a507db5SBrandon Wyman }
8549a507db5SBrandon Wyman }
8559a507db5SBrandon Wyman
readVPDValue(const std::string & vpdName,const phosphor::pmbus::Type & type,const std::size_t & vpdSize)8568393f467SBrandon Wyman auto PowerSupply::readVPDValue(const std::string& vpdName,
8578393f467SBrandon Wyman const phosphor::pmbus::Type& type,
8588393f467SBrandon Wyman const std::size_t& vpdSize)
8598393f467SBrandon Wyman {
8608393f467SBrandon Wyman std::string vpdValue;
861*f5402197SPatrick Williams const std::regex illegalVPDRegex =
862*f5402197SPatrick Williams std::regex("[^[:alnum:]]", std::regex::basic);
8638393f467SBrandon Wyman
8648393f467SBrandon Wyman try
8658393f467SBrandon Wyman {
8668393f467SBrandon Wyman vpdValue = pmbusIntf->readString(vpdName, type);
8678393f467SBrandon Wyman }
8688393f467SBrandon Wyman catch (const ReadFailure& e)
8698393f467SBrandon Wyman {
8708393f467SBrandon Wyman // Ignore the read failure, let pmbus code indicate failure,
8718393f467SBrandon Wyman // path...
8728393f467SBrandon Wyman // TODO - ibm918
8738393f467SBrandon Wyman // https://github.com/openbmc/docs/blob/master/designs/vpd-collection.md
8748393f467SBrandon Wyman // The BMC must log errors if any of the VPD cannot be properly
8758393f467SBrandon Wyman // parsed or fails ECC checks.
8768393f467SBrandon Wyman }
8778393f467SBrandon Wyman
8788393f467SBrandon Wyman if (vpdValue.size() != vpdSize)
8798393f467SBrandon Wyman {
880768d2269SShawn McCarney log<level::INFO>(std::format("{} {} resize needed. size: {}", shortName,
8818393f467SBrandon Wyman vpdName, vpdValue.size())
8828393f467SBrandon Wyman .c_str());
8838393f467SBrandon Wyman vpdValue.resize(vpdSize, ' ');
8848393f467SBrandon Wyman }
8858393f467SBrandon Wyman
886056935caSBrandon Wyman // Replace any illegal values with space(s).
887056935caSBrandon Wyman std::regex_replace(vpdValue.begin(), vpdValue.begin(), vpdValue.end(),
888056935caSBrandon Wyman illegalVPDRegex, " ");
889056935caSBrandon Wyman
8908393f467SBrandon Wyman return vpdValue;
8918393f467SBrandon Wyman }
8928393f467SBrandon Wyman
updateInventory()8931d7a7df8SBrandon Wyman void PowerSupply::updateInventory()
8941d7a7df8SBrandon Wyman {
8951d7a7df8SBrandon Wyman using namespace phosphor::pmbus;
8961d7a7df8SBrandon Wyman
897c12c53b9SChanh Nguyen #if IBM_VPD
8981d7a7df8SBrandon Wyman std::string pn;
8991d7a7df8SBrandon Wyman std::string fn;
9001d7a7df8SBrandon Wyman std::string header;
9011d7a7df8SBrandon Wyman std::string sn;
9028393f467SBrandon Wyman // The IBM power supply splits the full serial number into two parts.
9038393f467SBrandon Wyman // Each part is 6 bytes long, which should match up with SN_KW_SIZE.
9048393f467SBrandon Wyman const auto HEADER_SIZE = 6;
9058393f467SBrandon Wyman const auto SERIAL_SIZE = 6;
9068393f467SBrandon Wyman // The IBM PSU firmware version size is a bit complicated. It was originally
9078393f467SBrandon Wyman // 1-byte, per command. It was later expanded to 2-bytes per command, then
9088393f467SBrandon Wyman // up to 8-bytes per command. The device driver only reads up to 2 bytes per
9098393f467SBrandon Wyman // command, but combines all three of the 2-byte reads, or all 4 of the
9108393f467SBrandon Wyman // 1-byte reads into one string. So, the maximum size expected is 6 bytes.
911983c989dSShawn McCarney // However, it is formatted by the driver as a hex string with two ASCII
912983c989dSShawn McCarney // characters per byte. So the maximum ASCII string size is 12.
9139582d9ccSFaisal Awada const auto IBMCFFPS_FW_VERSION_SIZE = 12;
9149582d9ccSFaisal Awada const auto ACBEL_FSG032_FW_VERSION_SIZE = 6;
9158393f467SBrandon Wyman
9161d7a7df8SBrandon Wyman using PropertyMap =
917070c1bc6SGeorge Liu std::map<std::string,
918070c1bc6SGeorge Liu std::variant<std::string, std::vector<uint8_t>, bool>>;
9191d7a7df8SBrandon Wyman PropertyMap assetProps;
920070c1bc6SGeorge Liu PropertyMap operProps;
9211d7a7df8SBrandon Wyman PropertyMap versionProps;
9221d7a7df8SBrandon Wyman PropertyMap ipzvpdDINFProps;
9231d7a7df8SBrandon Wyman PropertyMap ipzvpdVINIProps;
9241d7a7df8SBrandon Wyman using InterfaceMap = std::map<std::string, PropertyMap>;
9251d7a7df8SBrandon Wyman InterfaceMap interfaces;
9261d7a7df8SBrandon Wyman using ObjectMap = std::map<sdbusplus::message::object_path, InterfaceMap>;
9271d7a7df8SBrandon Wyman ObjectMap object;
9281d7a7df8SBrandon Wyman #endif
929681b2a36SB. J. Wyman log<level::DEBUG>(
930768d2269SShawn McCarney std::format("updateInventory() inventoryPath: {}", inventoryPath)
931681b2a36SB. J. Wyman .c_str());
9321d7a7df8SBrandon Wyman
9331d7a7df8SBrandon Wyman if (present)
9341d7a7df8SBrandon Wyman {
9351d7a7df8SBrandon Wyman // TODO: non-IBM inventory updates?
9361d7a7df8SBrandon Wyman
937c12c53b9SChanh Nguyen #if IBM_VPD
9389582d9ccSFaisal Awada if (driverName == ACBEL_FSG032_DD_NAME)
939aded7a0dSfaisal {
940aded7a0dSfaisal getPsuVpdFromDbus("CC", modelName);
941aded7a0dSfaisal getPsuVpdFromDbus("PN", pn);
942aded7a0dSfaisal getPsuVpdFromDbus("FN", fn);
943aded7a0dSfaisal getPsuVpdFromDbus("SN", sn);
944aded7a0dSfaisal assetProps.emplace(SN_PROP, sn);
9459582d9ccSFaisal Awada fwVersion = readVPDValue(FW_VERSION, Type::Debug,
9469582d9ccSFaisal Awada ACBEL_FSG032_FW_VERSION_SIZE);
9479582d9ccSFaisal Awada versionProps.emplace(VERSION_PROP, fwVersion);
948aded7a0dSfaisal }
949aded7a0dSfaisal else
950aded7a0dSfaisal {
9518393f467SBrandon Wyman modelName = readVPDValue(CCIN, Type::HwmonDeviceDebug, CC_KW_SIZE);
952b40f04c2SMatt Spinler pn = readVPDValue(PART_NUMBER, Type::Debug, PN_KW_SIZE);
953b40f04c2SMatt Spinler fn = readVPDValue(FRU_NUMBER, Type::Debug, FN_KW_SIZE);
9541d7a7df8SBrandon Wyman
955b40f04c2SMatt Spinler header = readVPDValue(SERIAL_HEADER, Type::Debug, HEADER_SIZE);
956b40f04c2SMatt Spinler sn = readVPDValue(SERIAL_NUMBER, Type::Debug, SERIAL_SIZE);
9571cb0f13eSMike Capps assetProps.emplace(SN_PROP, header + sn);
9589582d9ccSFaisal Awada fwVersion = readVPDValue(FW_VERSION, Type::HwmonDeviceDebug,
9599582d9ccSFaisal Awada IBMCFFPS_FW_VERSION_SIZE);
9609582d9ccSFaisal Awada versionProps.emplace(VERSION_PROP, fwVersion);
961aded7a0dSfaisal }
962aded7a0dSfaisal
963aded7a0dSfaisal assetProps.emplace(MODEL_PROP, modelName);
964aded7a0dSfaisal assetProps.emplace(PN_PROP, pn);
965aded7a0dSfaisal assetProps.emplace(SPARE_PN_PROP, fn);
9661d7a7df8SBrandon Wyman
9678393f467SBrandon Wyman ipzvpdVINIProps.emplace(
9688393f467SBrandon Wyman "CC", std::vector<uint8_t>(modelName.begin(), modelName.end()));
9691d7a7df8SBrandon Wyman ipzvpdVINIProps.emplace("PN",
9701d7a7df8SBrandon Wyman std::vector<uint8_t>(pn.begin(), pn.end()));
9711d7a7df8SBrandon Wyman ipzvpdVINIProps.emplace("FN",
9721d7a7df8SBrandon Wyman std::vector<uint8_t>(fn.begin(), fn.end()));
97333d492f4SBrandon Wyman std::string header_sn = header + sn;
9741d7a7df8SBrandon Wyman ipzvpdVINIProps.emplace(
9751d7a7df8SBrandon Wyman "SN", std::vector<uint8_t>(header_sn.begin(), header_sn.end()));
9761d7a7df8SBrandon Wyman std::string description = "IBM PS";
9771d7a7df8SBrandon Wyman ipzvpdVINIProps.emplace(
9781d7a7df8SBrandon Wyman "DR", std::vector<uint8_t>(description.begin(), description.end()));
9791d7a7df8SBrandon Wyman
980f8d8c464SBen Tyner // Populate the VINI Resource Type (RT) keyword
981f8d8c464SBen Tyner ipzvpdVINIProps.emplace("RT", std::vector<uint8_t>{'V', 'I', 'N', 'I'});
982f8d8c464SBen Tyner
9831d7a7df8SBrandon Wyman // Update the Resource Identifier (RI) keyword
9841d7a7df8SBrandon Wyman // 2 byte FRC: 0x0003
9851d7a7df8SBrandon Wyman // 2 byte RID: 0x1000, 0x1001...
9861d7a7df8SBrandon Wyman std::uint8_t num = std::stoul(
9871d7a7df8SBrandon Wyman inventoryPath.substr(inventoryPath.size() - 1, 1), nullptr, 0);
9881d7a7df8SBrandon Wyman std::vector<uint8_t> ri{0x00, 0x03, 0x10, num};
9891d7a7df8SBrandon Wyman ipzvpdDINFProps.emplace("RI", ri);
9901d7a7df8SBrandon Wyman
9911d7a7df8SBrandon Wyman // Fill in the FRU Label (FL) keyword.
9921d7a7df8SBrandon Wyman std::string fl = "E";
9931d7a7df8SBrandon Wyman fl.push_back(inventoryPath.back());
9941d7a7df8SBrandon Wyman fl.resize(FL_KW_SIZE, ' ');
9951d7a7df8SBrandon Wyman ipzvpdDINFProps.emplace("FL",
9961d7a7df8SBrandon Wyman std::vector<uint8_t>(fl.begin(), fl.end()));
9971d7a7df8SBrandon Wyman
998f8d8c464SBen Tyner // Populate the DINF Resource Type (RT) keyword
999f8d8c464SBen Tyner ipzvpdDINFProps.emplace("RT", std::vector<uint8_t>{'D', 'I', 'N', 'F'});
1000f8d8c464SBen Tyner
10011d7a7df8SBrandon Wyman interfaces.emplace(ASSET_IFACE, std::move(assetProps));
10021d7a7df8SBrandon Wyman interfaces.emplace(VERSION_IFACE, std::move(versionProps));
10031d7a7df8SBrandon Wyman interfaces.emplace(DINF_IFACE, std::move(ipzvpdDINFProps));
10041d7a7df8SBrandon Wyman interfaces.emplace(VINI_IFACE, std::move(ipzvpdVINIProps));
10051d7a7df8SBrandon Wyman
1006070c1bc6SGeorge Liu // Update the Functional
1007070c1bc6SGeorge Liu operProps.emplace(FUNCTIONAL_PROP, present);
1008070c1bc6SGeorge Liu interfaces.emplace(OPERATIONAL_STATE_IFACE, std::move(operProps));
1009070c1bc6SGeorge Liu
10101d7a7df8SBrandon Wyman auto path = inventoryPath.substr(strlen(INVENTORY_OBJ_PATH));
10111d7a7df8SBrandon Wyman object.emplace(path, std::move(interfaces));
10121d7a7df8SBrandon Wyman
10131d7a7df8SBrandon Wyman try
10141d7a7df8SBrandon Wyman {
1015*f5402197SPatrick Williams auto service =
1016*f5402197SPatrick Williams util::getService(INVENTORY_OBJ_PATH, INVENTORY_MGR_IFACE, bus);
10171d7a7df8SBrandon Wyman
10181d7a7df8SBrandon Wyman if (service.empty())
10191d7a7df8SBrandon Wyman {
10201d7a7df8SBrandon Wyman log<level::ERR>("Unable to get inventory manager service");
10211d7a7df8SBrandon Wyman return;
10221d7a7df8SBrandon Wyman }
10231d7a7df8SBrandon Wyman
1024*f5402197SPatrick Williams auto method =
1025*f5402197SPatrick Williams bus.new_method_call(service.c_str(), INVENTORY_OBJ_PATH,
10261d7a7df8SBrandon Wyman INVENTORY_MGR_IFACE, "Notify");
10271d7a7df8SBrandon Wyman
10281d7a7df8SBrandon Wyman method.append(std::move(object));
10291d7a7df8SBrandon Wyman
10301d7a7df8SBrandon Wyman auto reply = bus.call(method);
10311d7a7df8SBrandon Wyman }
1032c1d4de5eSPatrick Williams catch (const std::exception& e)
10331d7a7df8SBrandon Wyman {
10346a3fd2c2SJay Meyer log<level::ERR>(
10356a3fd2c2SJay Meyer std::string(e.what() + std::string(" PATH=") + inventoryPath)
10366a3fd2c2SJay Meyer .c_str());
10371d7a7df8SBrandon Wyman }
10381d7a7df8SBrandon Wyman #endif
10391d7a7df8SBrandon Wyman }
10401d7a7df8SBrandon Wyman }
10411d7a7df8SBrandon Wyman
getMaxPowerOut() const1042ae35ac5dSBrandon Wyman auto PowerSupply::getMaxPowerOut() const
1043ae35ac5dSBrandon Wyman {
1044ae35ac5dSBrandon Wyman using namespace phosphor::pmbus;
1045ae35ac5dSBrandon Wyman
1046ae35ac5dSBrandon Wyman auto maxPowerOut = 0;
1047ae35ac5dSBrandon Wyman
1048ae35ac5dSBrandon Wyman if (present)
1049ae35ac5dSBrandon Wyman {
1050ae35ac5dSBrandon Wyman try
1051ae35ac5dSBrandon Wyman {
1052ae35ac5dSBrandon Wyman // Read max_power_out, should be direct format
1053*f5402197SPatrick Williams auto maxPowerOutStr =
1054*f5402197SPatrick Williams pmbusIntf->readString(MFR_POUT_MAX, Type::HwmonDeviceDebug);
1055768d2269SShawn McCarney log<level::INFO>(std::format("{} MFR_POUT_MAX read {}", shortName,
1056ae35ac5dSBrandon Wyman maxPowerOutStr)
1057ae35ac5dSBrandon Wyman .c_str());
1058ae35ac5dSBrandon Wyman maxPowerOut = std::stod(maxPowerOutStr);
1059ae35ac5dSBrandon Wyman }
1060ae35ac5dSBrandon Wyman catch (const std::exception& e)
1061ae35ac5dSBrandon Wyman {
1062768d2269SShawn McCarney log<level::ERR>(std::format("{} MFR_POUT_MAX read error: {}",
1063ae35ac5dSBrandon Wyman shortName, e.what())
1064ae35ac5dSBrandon Wyman .c_str());
1065ae35ac5dSBrandon Wyman }
1066ae35ac5dSBrandon Wyman }
1067ae35ac5dSBrandon Wyman
1068ae35ac5dSBrandon Wyman return maxPowerOut;
1069ae35ac5dSBrandon Wyman }
1070ae35ac5dSBrandon Wyman
setupSensors()1071592bd27cSMatt Spinler void PowerSupply::setupSensors()
1072592bd27cSMatt Spinler {
1073592bd27cSMatt Spinler setupInputPowerPeakSensor();
1074592bd27cSMatt Spinler }
1075592bd27cSMatt Spinler
setupInputPowerPeakSensor()1076592bd27cSMatt Spinler void PowerSupply::setupInputPowerPeakSensor()
1077592bd27cSMatt Spinler {
1078592bd27cSMatt Spinler if (peakInputPowerSensor || !present ||
1079592bd27cSMatt Spinler (bindPath.string().find(IBMCFFPS_DD_NAME) == std::string::npos))
1080592bd27cSMatt Spinler {
1081592bd27cSMatt Spinler return;
1082592bd27cSMatt Spinler }
1083592bd27cSMatt Spinler
1084592bd27cSMatt Spinler // This PSU has problems with the input_history command
1085592bd27cSMatt Spinler if (getMaxPowerOut() == phosphor::pmbus::IBM_CFFPS_1400W)
1086592bd27cSMatt Spinler {
1087592bd27cSMatt Spinler return;
1088592bd27cSMatt Spinler }
1089592bd27cSMatt Spinler
1090592bd27cSMatt Spinler auto sensorPath =
1091768d2269SShawn McCarney std::format("/xyz/openbmc_project/sensors/power/ps{}_input_power_peak",
1092592bd27cSMatt Spinler shortName.back());
1093592bd27cSMatt Spinler
1094592bd27cSMatt Spinler peakInputPowerSensor = std::make_unique<PowerSensorObject>(
1095592bd27cSMatt Spinler bus, sensorPath.c_str(), PowerSensorObject::action::defer_emit);
1096592bd27cSMatt Spinler
1097592bd27cSMatt Spinler // The others can remain at the defaults.
1098592bd27cSMatt Spinler peakInputPowerSensor->functional(true, true);
1099592bd27cSMatt Spinler peakInputPowerSensor->available(true, true);
1100592bd27cSMatt Spinler peakInputPowerSensor->value(0, true);
1101592bd27cSMatt Spinler peakInputPowerSensor->unit(
1102592bd27cSMatt Spinler sdbusplus::xyz::openbmc_project::Sensor::server::Value::Unit::Watts,
1103592bd27cSMatt Spinler true);
1104592bd27cSMatt Spinler
1105592bd27cSMatt Spinler auto associations = getSensorAssociations();
1106592bd27cSMatt Spinler peakInputPowerSensor->associations(associations, true);
1107592bd27cSMatt Spinler
1108592bd27cSMatt Spinler peakInputPowerSensor->emit_object_added();
1109592bd27cSMatt Spinler }
1110592bd27cSMatt Spinler
setSensorsNotAvailable()1111592bd27cSMatt Spinler void PowerSupply::setSensorsNotAvailable()
1112592bd27cSMatt Spinler {
1113592bd27cSMatt Spinler if (peakInputPowerSensor)
1114592bd27cSMatt Spinler {
1115592bd27cSMatt Spinler peakInputPowerSensor->value(std::numeric_limits<double>::quiet_NaN());
1116592bd27cSMatt Spinler peakInputPowerSensor->available(false);
1117592bd27cSMatt Spinler }
1118592bd27cSMatt Spinler }
1119592bd27cSMatt Spinler
monitorSensors()1120592bd27cSMatt Spinler void PowerSupply::monitorSensors()
1121592bd27cSMatt Spinler {
1122592bd27cSMatt Spinler monitorPeakInputPowerSensor();
1123592bd27cSMatt Spinler }
1124592bd27cSMatt Spinler
monitorPeakInputPowerSensor()1125592bd27cSMatt Spinler void PowerSupply::monitorPeakInputPowerSensor()
1126592bd27cSMatt Spinler {
1127592bd27cSMatt Spinler if (!peakInputPowerSensor)
1128592bd27cSMatt Spinler {
1129592bd27cSMatt Spinler return;
1130592bd27cSMatt Spinler }
1131592bd27cSMatt Spinler
1132592bd27cSMatt Spinler constexpr size_t recordSize = 5;
1133592bd27cSMatt Spinler std::vector<uint8_t> data;
1134592bd27cSMatt Spinler
1135592bd27cSMatt Spinler // Get the peak input power with input history command.
1136592bd27cSMatt Spinler // New data only shows up every 30s, but just try to read it every 1s
1137592bd27cSMatt Spinler // anyway so we always have the most up to date value.
1138592bd27cSMatt Spinler try
1139592bd27cSMatt Spinler {
1140592bd27cSMatt Spinler data = pmbusIntf->readBinary(INPUT_HISTORY,
1141592bd27cSMatt Spinler pmbus::Type::HwmonDeviceDebug, recordSize);
1142592bd27cSMatt Spinler }
1143592bd27cSMatt Spinler catch (const ReadFailure& e)
1144592bd27cSMatt Spinler {
1145592bd27cSMatt Spinler peakInputPowerSensor->value(std::numeric_limits<double>::quiet_NaN());
1146592bd27cSMatt Spinler peakInputPowerSensor->functional(false);
1147592bd27cSMatt Spinler throw;
1148592bd27cSMatt Spinler }
1149592bd27cSMatt Spinler
1150592bd27cSMatt Spinler if (data.size() != recordSize)
1151592bd27cSMatt Spinler {
1152592bd27cSMatt Spinler log<level::DEBUG>(
1153768d2269SShawn McCarney std::format("Input history command returned {} bytes instead of 5",
1154592bd27cSMatt Spinler data.size())
1155592bd27cSMatt Spinler .c_str());
1156592bd27cSMatt Spinler peakInputPowerSensor->value(std::numeric_limits<double>::quiet_NaN());
1157592bd27cSMatt Spinler peakInputPowerSensor->functional(false);
1158592bd27cSMatt Spinler return;
1159592bd27cSMatt Spinler }
1160592bd27cSMatt Spinler
1161592bd27cSMatt Spinler // The format is SSAAAAPPPP:
1162592bd27cSMatt Spinler // SS = packet sequence number
1163592bd27cSMatt Spinler // AAAA = average power (linear format, little endian)
1164592bd27cSMatt Spinler // PPPP = peak power (linear format, little endian)
1165592bd27cSMatt Spinler auto peak = static_cast<uint16_t>(data[4]) << 8 | data[3];
1166592bd27cSMatt Spinler auto peakPower = linearToInteger(peak);
1167592bd27cSMatt Spinler
1168592bd27cSMatt Spinler peakInputPowerSensor->value(peakPower);
1169592bd27cSMatt Spinler peakInputPowerSensor->functional(true);
1170592bd27cSMatt Spinler peakInputPowerSensor->available(true);
1171592bd27cSMatt Spinler }
1172592bd27cSMatt Spinler
getInputVoltage(double & actualInputVoltage,int & inputVoltage) const11734175ffb7SAdriana Kobylak void PowerSupply::getInputVoltage(double& actualInputVoltage,
11744175ffb7SAdriana Kobylak int& inputVoltage) const
11754175ffb7SAdriana Kobylak {
11764175ffb7SAdriana Kobylak using namespace phosphor::pmbus;
11774175ffb7SAdriana Kobylak
11784175ffb7SAdriana Kobylak actualInputVoltage = in_input::VIN_VOLTAGE_0;
11794175ffb7SAdriana Kobylak inputVoltage = in_input::VIN_VOLTAGE_0;
11804175ffb7SAdriana Kobylak
11814175ffb7SAdriana Kobylak if (present)
11824175ffb7SAdriana Kobylak {
11834175ffb7SAdriana Kobylak try
11844175ffb7SAdriana Kobylak {
11854175ffb7SAdriana Kobylak // Read input voltage in millivolts
11864175ffb7SAdriana Kobylak auto inputVoltageStr = pmbusIntf->readString(READ_VIN, Type::Hwmon);
11874175ffb7SAdriana Kobylak
11884175ffb7SAdriana Kobylak // Convert to volts
11894175ffb7SAdriana Kobylak actualInputVoltage = std::stod(inputVoltageStr) / 1000;
11904175ffb7SAdriana Kobylak
11914175ffb7SAdriana Kobylak // Calculate the voltage based on voltage thresholds
11924175ffb7SAdriana Kobylak if (actualInputVoltage < in_input::VIN_VOLTAGE_MIN)
11934175ffb7SAdriana Kobylak {
11944175ffb7SAdriana Kobylak inputVoltage = in_input::VIN_VOLTAGE_0;
11954175ffb7SAdriana Kobylak }
11964175ffb7SAdriana Kobylak else if (actualInputVoltage < in_input::VIN_VOLTAGE_110_THRESHOLD)
11974175ffb7SAdriana Kobylak {
11984175ffb7SAdriana Kobylak inputVoltage = in_input::VIN_VOLTAGE_110;
11994175ffb7SAdriana Kobylak }
12004175ffb7SAdriana Kobylak else
12014175ffb7SAdriana Kobylak {
12024175ffb7SAdriana Kobylak inputVoltage = in_input::VIN_VOLTAGE_220;
12034175ffb7SAdriana Kobylak }
12044175ffb7SAdriana Kobylak }
12054175ffb7SAdriana Kobylak catch (const std::exception& e)
12064175ffb7SAdriana Kobylak {
12074175ffb7SAdriana Kobylak log<level::ERR>(
1208768d2269SShawn McCarney std::format("{} READ_VIN read error: {}", shortName, e.what())
1209321a615bSBrandon Wyman .c_str());
12104175ffb7SAdriana Kobylak }
12114175ffb7SAdriana Kobylak }
12124175ffb7SAdriana Kobylak }
12134175ffb7SAdriana Kobylak
checkAvailability()12140975eaf4SMatt Spinler void PowerSupply::checkAvailability()
12150975eaf4SMatt Spinler {
12160975eaf4SMatt Spinler bool origAvailability = available;
12179464c429SGeorge Liu bool faulted = isPowerOn() && (hasPSKillFault() || hasIoutOCFault());
12189464c429SGeorge Liu available = present && !hasInputFault() && !hasVINUVFault() && !faulted;
12190975eaf4SMatt Spinler
12200975eaf4SMatt Spinler if (origAvailability != available)
12210975eaf4SMatt Spinler {
12220975eaf4SMatt Spinler auto invpath = inventoryPath.substr(strlen(INVENTORY_OBJ_PATH));
12230975eaf4SMatt Spinler phosphor::power::psu::setAvailable(bus, invpath, available);
1224ca1e9ea1SMatt Spinler
1225ca1e9ea1SMatt Spinler // Check if the health rollup needs to change based on the
1226ca1e9ea1SMatt Spinler // new availability value.
1227ca1e9ea1SMatt Spinler phosphor::power::psu::handleChassisHealthRollup(bus, inventoryPath,
1228ca1e9ea1SMatt Spinler !available);
12290975eaf4SMatt Spinler }
12300975eaf4SMatt Spinler }
12310975eaf4SMatt Spinler
setInputVoltageRating()1232a068f424SMatt Spinler void PowerSupply::setInputVoltageRating()
1233a068f424SMatt Spinler {
1234a068f424SMatt Spinler if (!present)
1235a068f424SMatt Spinler {
12361aaf9f85SMatt Spinler if (inputVoltageRatingIface)
12371aaf9f85SMatt Spinler {
12381aaf9f85SMatt Spinler inputVoltageRatingIface->value(0);
12391aaf9f85SMatt Spinler inputVoltageRatingIface.reset();
12401aaf9f85SMatt Spinler }
1241a068f424SMatt Spinler return;
1242a068f424SMatt Spinler }
1243a068f424SMatt Spinler
1244a068f424SMatt Spinler double inputVoltageValue{};
1245a068f424SMatt Spinler int inputVoltageRating{};
1246a068f424SMatt Spinler getInputVoltage(inputVoltageValue, inputVoltageRating);
1247a068f424SMatt Spinler
1248a068f424SMatt Spinler if (!inputVoltageRatingIface)
1249a068f424SMatt Spinler {
1250768d2269SShawn McCarney auto path = std::format(
1251a068f424SMatt Spinler "/xyz/openbmc_project/sensors/voltage/ps{}_input_voltage_rating",
1252a068f424SMatt Spinler shortName.back());
1253a068f424SMatt Spinler
1254a068f424SMatt Spinler inputVoltageRatingIface = std::make_unique<SensorObject>(
1255a068f424SMatt Spinler bus, path.c_str(), SensorObject::action::defer_emit);
1256a068f424SMatt Spinler
1257a068f424SMatt Spinler // Leave other properties at their defaults
1258a068f424SMatt Spinler inputVoltageRatingIface->unit(SensorInterface::Unit::Volts, true);
1259a068f424SMatt Spinler inputVoltageRatingIface->value(static_cast<double>(inputVoltageRating),
1260a068f424SMatt Spinler true);
1261a068f424SMatt Spinler
1262a068f424SMatt Spinler inputVoltageRatingIface->emit_object_added();
1263a068f424SMatt Spinler }
1264a068f424SMatt Spinler else
1265a068f424SMatt Spinler {
1266a068f424SMatt Spinler inputVoltageRatingIface->value(static_cast<double>(inputVoltageRating));
1267a068f424SMatt Spinler }
1268a068f424SMatt Spinler }
1269a068f424SMatt Spinler
getPsuVpdFromDbus(const std::string & keyword,std::string & vpdStr)1270aded7a0dSfaisal void PowerSupply::getPsuVpdFromDbus(const std::string& keyword,
1271aded7a0dSfaisal std::string& vpdStr)
1272aded7a0dSfaisal {
1273aded7a0dSfaisal try
1274aded7a0dSfaisal {
1275aded7a0dSfaisal std::vector<uint8_t> value;
1276aded7a0dSfaisal vpdStr.clear();
1277aded7a0dSfaisal util::getProperty(VINI_IFACE, keyword, inventoryPath,
1278aded7a0dSfaisal INVENTORY_MGR_IFACE, bus, value);
1279aded7a0dSfaisal for (char c : value)
1280aded7a0dSfaisal {
1281aded7a0dSfaisal vpdStr += c;
1282aded7a0dSfaisal }
1283aded7a0dSfaisal }
1284aded7a0dSfaisal catch (const sdbusplus::exception_t& e)
1285aded7a0dSfaisal {
1286aded7a0dSfaisal log<level::ERR>(
1287768d2269SShawn McCarney std::format("Failed getProperty error: {}", e.what()).c_str());
1288aded7a0dSfaisal }
1289aded7a0dSfaisal }
1290592bd27cSMatt Spinler
linearToInteger(uint16_t data)1291592bd27cSMatt Spinler double PowerSupply::linearToInteger(uint16_t data)
1292592bd27cSMatt Spinler {
1293592bd27cSMatt Spinler // The exponent is the first 5 bits, followed by 11 bits of mantissa.
1294592bd27cSMatt Spinler int8_t exponent = (data & 0xF800) >> 11;
1295592bd27cSMatt Spinler int16_t mantissa = (data & 0x07FF);
1296592bd27cSMatt Spinler
1297592bd27cSMatt Spinler // If exponent's MSB on, then it's negative.
1298592bd27cSMatt Spinler // Convert from two's complement.
1299592bd27cSMatt Spinler if (exponent & 0x10)
1300592bd27cSMatt Spinler {
1301592bd27cSMatt Spinler exponent = (~exponent) & 0x1F;
1302592bd27cSMatt Spinler exponent = (exponent + 1) * -1;
1303592bd27cSMatt Spinler }
1304592bd27cSMatt Spinler
1305592bd27cSMatt Spinler // If mantissa's MSB on, then it's negative.
1306592bd27cSMatt Spinler // Convert from two's complement.
1307592bd27cSMatt Spinler if (mantissa & 0x400)
1308592bd27cSMatt Spinler {
1309592bd27cSMatt Spinler mantissa = (~mantissa) & 0x07FF;
1310592bd27cSMatt Spinler mantissa = (mantissa + 1) * -1;
1311592bd27cSMatt Spinler }
1312592bd27cSMatt Spinler
1313592bd27cSMatt Spinler auto value = static_cast<double>(mantissa) * pow(2, exponent);
1314592bd27cSMatt Spinler return value;
1315592bd27cSMatt Spinler }
1316592bd27cSMatt Spinler
getSensorAssociations()1317592bd27cSMatt Spinler std::vector<AssociationTuple> PowerSupply::getSensorAssociations()
1318592bd27cSMatt Spinler {
1319592bd27cSMatt Spinler std::vector<AssociationTuple> associations;
1320592bd27cSMatt Spinler
1321592bd27cSMatt Spinler associations.emplace_back("inventory", "sensors", inventoryPath);
1322592bd27cSMatt Spinler
1323592bd27cSMatt Spinler auto chassis = getChassis(bus, inventoryPath);
1324592bd27cSMatt Spinler associations.emplace_back("chassis", "all_sensors", std::move(chassis));
1325592bd27cSMatt Spinler
1326592bd27cSMatt Spinler return associations;
1327592bd27cSMatt Spinler }
1328592bd27cSMatt Spinler
13293f1242f3SBrandon Wyman } // namespace phosphor::power::psu
1330