xref: /openbmc/phosphor-power/phosphor-power-supply/power_supply.cpp (revision f54021972b91be5058b50e9046bb0dd5a3b22a80)
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