blob: 254c9c720240521aeaeb324e2122982a2e075760 [file] [log] [blame]
Brandon Wyman1d7a7df2020-03-26 10:14:05 -05001#include "config.h"
2
Brandon Wymanaed1f752019-11-25 18:10:52 -06003#include "power_supply.hpp"
4
5#include "types.hpp"
Brandon Wyman3f1242f2020-01-28 13:11:25 -06006#include "util.hpp"
Brandon Wymanaed1f752019-11-25 18:10:52 -06007
Brandon Wymandf13c3a2020-12-15 14:25:22 -06008#include <fmt/format.h>
9
Brandon Wyman3f1242f2020-01-28 13:11:25 -060010#include <xyz/openbmc_project/Common/Device/error.hpp>
11
Brandon Wyman4fc191f2022-03-10 23:07:13 +000012#include <chrono> // sleep_for()
13#include <cmath>
Brandon Wyman1d7a7df2020-03-26 10:14:05 -050014#include <cstdint> // uint8_t...
B. J. Wyman681b2a32021-04-20 22:31:22 +000015#include <fstream>
16#include <thread> // sleep_for()
Brandon Wyman1d7a7df2020-03-26 10:14:05 -050017
Brandon Wyman3f1242f2020-01-28 13:11:25 -060018namespace phosphor::power::psu
Brandon Wymanaed1f752019-11-25 18:10:52 -060019{
B. J. Wyman681b2a32021-04-20 22:31:22 +000020// Amount of time in milliseconds to delay between power supply going from
21// missing to present before running the bind command(s).
22constexpr auto bindDelay = 1000;
Brandon Wymanaed1f752019-11-25 18:10:52 -060023
Brandon Wymanc3324422022-03-24 20:30:57 +000024// The number of INPUT_HISTORY records to keep on D-Bus.
25// Each record covers a 30-second span. That means two records are needed to
26// cover a minute of time. If we want one (1) hour of data, that would be 120
27// records.
28constexpr auto INPUT_HISTORY_MAX_RECORDS = 120;
29
Brandon Wymanaed1f752019-11-25 18:10:52 -060030using namespace phosphor::logging;
Brandon Wyman3f1242f2020-01-28 13:11:25 -060031using namespace sdbusplus::xyz::openbmc_project::Common::Device::Error;
Brandon Wymanaed1f752019-11-25 18:10:52 -060032
Brandon Wyman510acaa2020-11-05 18:32:04 -060033PowerSupply::PowerSupply(sdbusplus::bus::bus& bus, const std::string& invpath,
B. J. Wyman681b2a32021-04-20 22:31:22 +000034 std::uint8_t i2cbus, std::uint16_t i2caddr,
Brandon Wymanc3324422022-03-24 20:30:57 +000035 const std::string& driver,
B. J. Wyman681b2a32021-04-20 22:31:22 +000036 const std::string& gpioLineName) :
Brandon Wyman510acaa2020-11-05 18:32:04 -060037 bus(bus),
Brandon Wymanc3324422022-03-24 20:30:57 +000038 inventoryPath(invpath), bindPath("/sys/bus/i2c/drivers/" + driver)
Brandon Wyman510acaa2020-11-05 18:32:04 -060039{
40 if (inventoryPath.empty())
41 {
42 throw std::invalid_argument{"Invalid empty inventoryPath"};
43 }
44
B. J. Wyman681b2a32021-04-20 22:31:22 +000045 if (gpioLineName.empty())
46 {
47 throw std::invalid_argument{"Invalid empty gpioLineName"};
48 }
Brandon Wyman510acaa2020-11-05 18:32:04 -060049
Brandon Wyman321a6152022-03-19 00:11:44 +000050 shortName = findShortName(inventoryPath);
51
52 log<level::DEBUG>(
53 fmt::format("{} gpioLineName: {}", shortName, gpioLineName).c_str());
B. J. Wyman681b2a32021-04-20 22:31:22 +000054 presenceGPIO = createGPIO(gpioLineName);
Brandon Wyman510acaa2020-11-05 18:32:04 -060055
56 std::ostringstream ss;
57 ss << std::hex << std::setw(4) << std::setfill('0') << i2caddr;
58 std::string addrStr = ss.str();
B. J. Wyman681b2a32021-04-20 22:31:22 +000059 std::string busStr = std::to_string(i2cbus);
60 bindDevice = busStr;
61 bindDevice.append("-");
62 bindDevice.append(addrStr);
63
Brandon Wyman510acaa2020-11-05 18:32:04 -060064 pmbusIntf = phosphor::pmbus::createPMBus(i2cbus, addrStr);
65
66 // Get the current state of the Present property.
B. J. Wyman681b2a32021-04-20 22:31:22 +000067 try
68 {
69 updatePresenceGPIO();
70 }
71 catch (...)
72 {
73 // If the above attempt to use the GPIO failed, it likely means that the
74 // GPIOs are in use by the kernel, meaning it is using gpio-keys.
75 // So, I should rely on phosphor-gpio-presence to update D-Bus, and
76 // work that way for power supply presence.
77 presenceGPIO = nullptr;
78 // Setup the functions to call when the D-Bus inventory path for the
79 // Present property changes.
80 presentMatch = std::make_unique<sdbusplus::bus::match_t>(
81 bus,
82 sdbusplus::bus::match::rules::propertiesChanged(inventoryPath,
83 INVENTORY_IFACE),
84 [this](auto& msg) { this->inventoryChanged(msg); });
85
86 presentAddedMatch = std::make_unique<sdbusplus::bus::match_t>(
87 bus,
88 sdbusplus::bus::match::rules::interfacesAdded() +
89 sdbusplus::bus::match::rules::argNpath(0, inventoryPath),
90 [this](auto& msg) { this->inventoryAdded(msg); });
91
92 updatePresence();
93 updateInventory();
Brandon Wymanc3324422022-03-24 20:30:57 +000094 setupInputHistory();
B. J. Wyman681b2a32021-04-20 22:31:22 +000095 }
96}
97
98void PowerSupply::bindOrUnbindDriver(bool present)
99{
100 auto action = (present) ? "bind" : "unbind";
101 auto path = bindPath / action;
102
103 if (present)
104 {
Brandon Wymanb1ee60f2022-03-22 22:37:12 +0000105 std::this_thread::sleep_for(std::chrono::milliseconds(bindDelay));
B. J. Wyman681b2a32021-04-20 22:31:22 +0000106 log<level::INFO>(
107 fmt::format("Binding device driver. path: {} device: {}",
108 path.string(), bindDevice)
109 .c_str());
110 }
111 else
112 {
113 log<level::INFO>(
114 fmt::format("Unbinding device driver. path: {} device: {}",
115 path.string(), bindDevice)
116 .c_str());
117 }
118
119 std::ofstream file;
120
121 file.exceptions(std::ofstream::failbit | std::ofstream::badbit |
122 std::ofstream::eofbit);
123
124 try
125 {
126 file.open(path);
127 file << bindDevice;
128 file.close();
129 }
Patrick Williamsc1d4de52021-10-06 12:45:57 -0500130 catch (const std::exception& e)
B. J. Wyman681b2a32021-04-20 22:31:22 +0000131 {
132 auto err = errno;
133
134 log<level::ERR>(
135 fmt::format("Failed binding or unbinding device. errno={}", err)
136 .c_str());
137 }
Brandon Wyman510acaa2020-11-05 18:32:04 -0600138}
139
Brandon Wymanaed1f752019-11-25 18:10:52 -0600140void PowerSupply::updatePresence()
141{
142 try
143 {
Brandon Wyman3f1242f2020-01-28 13:11:25 -0600144 present = getPresence(bus, inventoryPath);
Brandon Wymanaed1f752019-11-25 18:10:52 -0600145 }
Patrick Williams69f10ad2021-09-02 09:46:49 -0500146 catch (const sdbusplus::exception::exception& e)
Brandon Wymanaed1f752019-11-25 18:10:52 -0600147 {
148 // Relying on property change or interface added to retry.
149 // Log an informational trace to the journal.
Brandon Wymandf13c3a2020-12-15 14:25:22 -0600150 log<level::INFO>(
151 fmt::format("D-Bus property {} access failure exception",
152 inventoryPath)
153 .c_str());
Brandon Wymanaed1f752019-11-25 18:10:52 -0600154 }
155}
156
B. J. Wyman681b2a32021-04-20 22:31:22 +0000157void PowerSupply::updatePresenceGPIO()
158{
159 bool presentOld = present;
160
161 try
162 {
163 if (presenceGPIO->read() > 0)
164 {
165 present = true;
166 }
167 else
168 {
169 present = false;
170 }
171 }
Patrick Williamsc1d4de52021-10-06 12:45:57 -0500172 catch (const std::exception& e)
B. J. Wyman681b2a32021-04-20 22:31:22 +0000173 {
174 log<level::ERR>(
175 fmt::format("presenceGPIO read fail: {}", e.what()).c_str());
176 throw;
177 }
178
179 if (presentOld != present)
180 {
Brandon Wyman321a6152022-03-19 00:11:44 +0000181 log<level::DEBUG>(fmt::format("{} presentOld: {} present: {}",
182 shortName, presentOld, present)
183 .c_str());
Matt Spinlerca1e9ea2022-02-18 14:03:08 -0600184
185 auto invpath = inventoryPath.substr(strlen(INVENTORY_OBJ_PATH));
Brandon Wyman90d529a2022-03-22 23:02:54 +0000186
187 bindOrUnbindDriver(present);
188 if (present)
189 {
190 // If the power supply was present, then missing, and present again,
191 // the hwmon path may have changed. We will need the correct/updated
192 // path before any reads or writes are attempted.
193 pmbusIntf->findHwmonDir();
194 }
195
Brandon Wyman321a6152022-03-19 00:11:44 +0000196 setPresence(bus, invpath, present, shortName);
Brandon Wymanc3324422022-03-24 20:30:57 +0000197 setupInputHistory();
Matt Spinlerca1e9ea2022-02-18 14:03:08 -0600198 updateInventory();
199
Brandon Wyman90d529a2022-03-22 23:02:54 +0000200 // Need Functional to already be correct before calling this.
Matt Spinlerca1e9ea2022-02-18 14:03:08 -0600201 checkAvailability();
202
B. J. Wyman681b2a32021-04-20 22:31:22 +0000203 if (present)
204 {
B. J. Wyman681b2a32021-04-20 22:31:22 +0000205 onOffConfig(phosphor::pmbus::ON_OFF_CONFIG_CONTROL_PIN_ONLY);
206 clearFaults();
207 }
B. J. Wyman681b2a32021-04-20 22:31:22 +0000208 }
209}
210
Brandon Wymanc2203432021-12-21 23:09:48 +0000211void PowerSupply::analyzeCMLFault()
212{
213 if (statusWord & phosphor::pmbus::status_word::CML_FAULT)
214 {
Brandon Wymanc2906f42021-12-21 20:14:56 +0000215 if (cmlFault < DEGLITCH_LIMIT)
Brandon Wymanc2203432021-12-21 23:09:48 +0000216 {
Brandon Wyman9e292ee2022-03-10 22:56:23 +0000217 if (statusWord != statusWordOld)
218 {
Brandon Wyman321a6152022-03-19 00:11:44 +0000219 log<level::ERR>(
220 fmt::format("{} CML fault: STATUS_WORD = {:#06x}, "
221 "STATUS_CML = {:#02x}",
222 shortName, statusWord, statusCML)
223 .c_str());
Brandon Wyman9e292ee2022-03-10 22:56:23 +0000224 }
Brandon Wymanc2906f42021-12-21 20:14:56 +0000225 cmlFault++;
226 }
227 }
228 else
229 {
230 cmlFault = 0;
Brandon Wymanc2203432021-12-21 23:09:48 +0000231 }
232}
233
Brandon Wymane3b0bb02021-12-21 23:16:48 +0000234void PowerSupply::analyzeInputFault()
235{
236 if (statusWord & phosphor::pmbus::status_word::INPUT_FAULT_WARN)
237 {
Brandon Wymanc2906f42021-12-21 20:14:56 +0000238 if (inputFault < DEGLITCH_LIMIT)
Brandon Wymane3b0bb02021-12-21 23:16:48 +0000239 {
Brandon Wyman9e292ee2022-03-10 22:56:23 +0000240 if (statusWord != statusWordOld)
241 {
242 log<level::ERR>(
Brandon Wyman321a6152022-03-19 00:11:44 +0000243 fmt::format("{} INPUT fault: STATUS_WORD = {:#06x}, "
Brandon Wyman9e292ee2022-03-10 22:56:23 +0000244 "STATUS_MFR_SPECIFIC = {:#04x}, "
245 "STATUS_INPUT = {:#04x}",
Brandon Wyman321a6152022-03-19 00:11:44 +0000246 shortName, statusWord, statusMFR, statusInput)
Brandon Wyman9e292ee2022-03-10 22:56:23 +0000247 .c_str());
248 }
Brandon Wymanc2906f42021-12-21 20:14:56 +0000249 inputFault++;
250 }
Brandon Wymane3b0bb02021-12-21 23:16:48 +0000251 }
Brandon Wyman82affd92021-11-24 19:12:49 +0000252
253 // If had INPUT/VIN_UV fault, and now off.
254 // Trace that odd behavior.
255 if (inputFault &&
256 !(statusWord & phosphor::pmbus::status_word::INPUT_FAULT_WARN))
257 {
258 log<level::INFO>(
Brandon Wyman321a6152022-03-19 00:11:44 +0000259 fmt::format("{} INPUT fault cleared: STATUS_WORD = {:#06x}, "
Brandon Wyman6f939a32022-03-10 18:42:20 +0000260 "STATUS_MFR_SPECIFIC = {:#04x}, "
261 "STATUS_INPUT = {:#04x}",
Brandon Wyman321a6152022-03-19 00:11:44 +0000262 shortName, statusWord, statusMFR, statusInput)
Brandon Wyman82affd92021-11-24 19:12:49 +0000263 .c_str());
Brandon Wymanc2906f42021-12-21 20:14:56 +0000264 inputFault = 0;
Brandon Wyman82affd92021-11-24 19:12:49 +0000265 }
Brandon Wymane3b0bb02021-12-21 23:16:48 +0000266}
267
Brandon Wymanc2c87132021-12-21 23:22:18 +0000268void PowerSupply::analyzeVoutOVFault()
269{
270 if (statusWord & phosphor::pmbus::status_word::VOUT_OV_FAULT)
271 {
Brandon Wymanc2906f42021-12-21 20:14:56 +0000272 if (voutOVFault < DEGLITCH_LIMIT)
Brandon Wymanc2c87132021-12-21 23:22:18 +0000273 {
Brandon Wyman9e292ee2022-03-10 22:56:23 +0000274 if (statusWord != statusWordOld)
275 {
276 log<level::ERR>(
Brandon Wyman321a6152022-03-19 00:11:44 +0000277 fmt::format(
278 "{} VOUT_OV_FAULT fault: STATUS_WORD = {:#06x}, "
279 "STATUS_MFR_SPECIFIC = {:#04x}, "
280 "STATUS_VOUT = {:#02x}",
281 shortName, statusWord, statusMFR, statusVout)
Brandon Wyman9e292ee2022-03-10 22:56:23 +0000282 .c_str());
283 }
Brandon Wymanc2c87132021-12-21 23:22:18 +0000284
Brandon Wymanc2906f42021-12-21 20:14:56 +0000285 voutOVFault++;
286 }
287 }
288 else
289 {
290 voutOVFault = 0;
Brandon Wymanc2c87132021-12-21 23:22:18 +0000291 }
292}
293
Brandon Wymana00e7302021-12-21 23:28:29 +0000294void PowerSupply::analyzeIoutOCFault()
295{
296 if (statusWord & phosphor::pmbus::status_word::IOUT_OC_FAULT)
297 {
Brandon Wymanc2906f42021-12-21 20:14:56 +0000298 if (ioutOCFault < DEGLITCH_LIMIT)
Brandon Wymana00e7302021-12-21 23:28:29 +0000299 {
Brandon Wyman9e292ee2022-03-10 22:56:23 +0000300 if (statusWord != statusWordOld)
301 {
302 log<level::ERR>(
Brandon Wyman321a6152022-03-19 00:11:44 +0000303 fmt::format("{} IOUT fault: STATUS_WORD = {:#06x}, "
Brandon Wyman9e292ee2022-03-10 22:56:23 +0000304 "STATUS_MFR_SPECIFIC = {:#04x}, "
305 "STATUS_IOUT = {:#04x}",
Brandon Wyman321a6152022-03-19 00:11:44 +0000306 shortName, statusWord, statusMFR, statusIout)
Brandon Wyman9e292ee2022-03-10 22:56:23 +0000307 .c_str());
308 }
Brandon Wymana00e7302021-12-21 23:28:29 +0000309
Brandon Wymanc2906f42021-12-21 20:14:56 +0000310 ioutOCFault++;
311 }
312 }
313 else
314 {
315 ioutOCFault = 0;
Brandon Wymana00e7302021-12-21 23:28:29 +0000316 }
317}
318
Brandon Wyman08378782021-12-21 23:48:15 +0000319void PowerSupply::analyzeVoutUVFault()
320{
321 if ((statusWord & phosphor::pmbus::status_word::VOUT_FAULT) &&
322 !(statusWord & phosphor::pmbus::status_word::VOUT_OV_FAULT))
323 {
Brandon Wymanc2906f42021-12-21 20:14:56 +0000324 if (voutUVFault < DEGLITCH_LIMIT)
Brandon Wyman08378782021-12-21 23:48:15 +0000325 {
Brandon Wyman9e292ee2022-03-10 22:56:23 +0000326 if (statusWord != statusWordOld)
327 {
328 log<level::ERR>(
Brandon Wyman321a6152022-03-19 00:11:44 +0000329 fmt::format(
330 "{} VOUT_UV_FAULT fault: STATUS_WORD = {:#06x}, "
331 "STATUS_MFR_SPECIFIC = {:#04x}, "
332 "STATUS_VOUT = {:#04x}",
333 shortName, statusWord, statusMFR, statusVout)
Brandon Wyman9e292ee2022-03-10 22:56:23 +0000334 .c_str());
335 }
Brandon Wymanc2906f42021-12-21 20:14:56 +0000336 voutUVFault++;
337 }
338 }
339 else
340 {
341 voutUVFault = 0;
Brandon Wyman08378782021-12-21 23:48:15 +0000342 }
343}
344
Brandon Wymand5d9a222021-12-21 23:59:05 +0000345void PowerSupply::analyzeFanFault()
346{
347 if (statusWord & phosphor::pmbus::status_word::FAN_FAULT)
348 {
Brandon Wymanc2906f42021-12-21 20:14:56 +0000349 if (fanFault < DEGLITCH_LIMIT)
Brandon Wymand5d9a222021-12-21 23:59:05 +0000350 {
Brandon Wyman9e292ee2022-03-10 22:56:23 +0000351 if (statusWord != statusWordOld)
352 {
Brandon Wyman321a6152022-03-19 00:11:44 +0000353 log<level::ERR>(fmt::format("{} FANS fault/warning: "
Brandon Wyman9e292ee2022-03-10 22:56:23 +0000354 "STATUS_WORD = {:#06x}, "
355 "STATUS_MFR_SPECIFIC = {:#04x}, "
356 "STATUS_FANS_1_2 = {:#04x}",
Brandon Wyman321a6152022-03-19 00:11:44 +0000357 shortName, statusWord, statusMFR,
358 statusFans12)
Brandon Wyman9e292ee2022-03-10 22:56:23 +0000359 .c_str());
360 }
Brandon Wymanc2906f42021-12-21 20:14:56 +0000361 fanFault++;
362 }
363 }
364 else
365 {
366 fanFault = 0;
Brandon Wymand5d9a222021-12-21 23:59:05 +0000367 }
368}
369
Brandon Wyman52cb3f22021-12-21 23:02:47 +0000370void PowerSupply::analyzeTemperatureFault()
371{
372 if (statusWord & phosphor::pmbus::status_word::TEMPERATURE_FAULT_WARN)
373 {
Brandon Wymanc2906f42021-12-21 20:14:56 +0000374 if (tempFault < DEGLITCH_LIMIT)
Brandon Wyman52cb3f22021-12-21 23:02:47 +0000375 {
Brandon Wyman9e292ee2022-03-10 22:56:23 +0000376 if (statusWord != statusWordOld)
377 {
Brandon Wyman321a6152022-03-19 00:11:44 +0000378 log<level::ERR>(fmt::format("{} TEMPERATURE fault/warning: "
Brandon Wyman9e292ee2022-03-10 22:56:23 +0000379 "STATUS_WORD = {:#06x}, "
380 "STATUS_MFR_SPECIFIC = {:#04x}, "
381 "STATUS_TEMPERATURE = {:#04x}",
Brandon Wyman321a6152022-03-19 00:11:44 +0000382 shortName, statusWord, statusMFR,
Brandon Wyman9e292ee2022-03-10 22:56:23 +0000383 statusTemperature)
384 .c_str());
385 }
Brandon Wymanc2906f42021-12-21 20:14:56 +0000386 tempFault++;
387 }
388 }
389 else
390 {
391 tempFault = 0;
Brandon Wyman52cb3f22021-12-21 23:02:47 +0000392 }
393}
394
Brandon Wyman993b5542021-12-21 22:55:16 +0000395void PowerSupply::analyzePgoodFault()
396{
397 if ((statusWord & phosphor::pmbus::status_word::POWER_GOOD_NEGATED) ||
398 (statusWord & phosphor::pmbus::status_word::UNIT_IS_OFF))
399 {
400 if (pgoodFault < DEGLITCH_LIMIT)
401 {
Brandon Wyman9e292ee2022-03-10 22:56:23 +0000402 if (statusWord != statusWordOld)
403 {
Brandon Wyman321a6152022-03-19 00:11:44 +0000404 log<level::ERR>(fmt::format("{} PGOOD fault: "
Brandon Wyman9e292ee2022-03-10 22:56:23 +0000405 "STATUS_WORD = {:#06x}, "
406 "STATUS_MFR_SPECIFIC = {:#04x}",
Brandon Wyman321a6152022-03-19 00:11:44 +0000407 shortName, statusWord, statusMFR)
Brandon Wyman9e292ee2022-03-10 22:56:23 +0000408 .c_str());
409 }
Brandon Wyman993b5542021-12-21 22:55:16 +0000410 pgoodFault++;
411 }
412 }
413 else
414 {
415 pgoodFault = 0;
416 }
417}
418
Brandon Wyman39ea02b2021-11-23 23:22:23 +0000419void PowerSupply::determineMFRFault()
420{
421 if (bindPath.string().find("ibm-cffps") != std::string::npos)
422 {
423 // IBM MFR_SPECIFIC[4] is PS_Kill fault
424 if (statusMFR & 0x10)
425 {
Brandon Wymanc2906f42021-12-21 20:14:56 +0000426 if (psKillFault < DEGLITCH_LIMIT)
427 {
428 psKillFault++;
429 }
430 }
431 else
432 {
433 psKillFault = 0;
Brandon Wyman39ea02b2021-11-23 23:22:23 +0000434 }
435 // IBM MFR_SPECIFIC[6] is 12Vcs fault.
436 if (statusMFR & 0x40)
437 {
Brandon Wymanc2906f42021-12-21 20:14:56 +0000438 if (ps12VcsFault < DEGLITCH_LIMIT)
439 {
440 ps12VcsFault++;
441 }
442 }
443 else
444 {
445 ps12VcsFault = 0;
Brandon Wyman39ea02b2021-11-23 23:22:23 +0000446 }
447 // IBM MFR_SPECIFIC[7] is 12V Current-Share fault.
448 if (statusMFR & 0x80)
449 {
Brandon Wymanc2906f42021-12-21 20:14:56 +0000450 if (psCS12VFault < DEGLITCH_LIMIT)
451 {
452 psCS12VFault++;
453 }
454 }
455 else
456 {
457 psCS12VFault = 0;
Brandon Wyman39ea02b2021-11-23 23:22:23 +0000458 }
459 }
460}
461
Brandon Wyman6c2ac392021-12-21 22:23:06 +0000462void PowerSupply::analyzeMFRFault()
463{
464 if (statusWord & phosphor::pmbus::status_word::MFR_SPECIFIC_FAULT)
465 {
Brandon Wymanc2906f42021-12-21 20:14:56 +0000466 if (mfrFault < DEGLITCH_LIMIT)
Brandon Wyman6c2ac392021-12-21 22:23:06 +0000467 {
Brandon Wyman9e292ee2022-03-10 22:56:23 +0000468 if (statusWord != statusWordOld)
469 {
Brandon Wyman321a6152022-03-19 00:11:44 +0000470 log<level::ERR>(fmt::format("{} MFR fault: "
Brandon Wyman9e292ee2022-03-10 22:56:23 +0000471 "STATUS_WORD = {:#06x} "
472 "STATUS_MFR_SPECIFIC = {:#04x}",
Brandon Wyman321a6152022-03-19 00:11:44 +0000473 shortName, statusWord, statusMFR)
Brandon Wyman9e292ee2022-03-10 22:56:23 +0000474 .c_str());
475 }
Brandon Wymanc2906f42021-12-21 20:14:56 +0000476 mfrFault++;
Brandon Wyman6c2ac392021-12-21 22:23:06 +0000477 }
478
Brandon Wyman6c2ac392021-12-21 22:23:06 +0000479 determineMFRFault();
480 }
Brandon Wymanc2906f42021-12-21 20:14:56 +0000481 else
482 {
483 mfrFault = 0;
484 }
Brandon Wyman6c2ac392021-12-21 22:23:06 +0000485}
486
Brandon Wymanf087f472021-12-22 00:04:27 +0000487void PowerSupply::analyzeVinUVFault()
488{
489 if (statusWord & phosphor::pmbus::status_word::VIN_UV_FAULT)
490 {
Brandon Wymanc2906f42021-12-21 20:14:56 +0000491 if (vinUVFault < DEGLITCH_LIMIT)
Brandon Wymanf087f472021-12-22 00:04:27 +0000492 {
Brandon Wyman9e292ee2022-03-10 22:56:23 +0000493 if (statusWord != statusWordOld)
494 {
495 log<level::ERR>(
Brandon Wyman321a6152022-03-19 00:11:44 +0000496 fmt::format("{} VIN_UV fault: STATUS_WORD = {:#06x}, "
Brandon Wyman9e292ee2022-03-10 22:56:23 +0000497 "STATUS_MFR_SPECIFIC = {:#04x}, "
498 "STATUS_INPUT = {:#04x}",
Brandon Wyman321a6152022-03-19 00:11:44 +0000499 shortName, statusWord, statusMFR, statusInput)
Brandon Wyman9e292ee2022-03-10 22:56:23 +0000500 .c_str());
501 }
Brandon Wymanc2906f42021-12-21 20:14:56 +0000502 vinUVFault++;
Brandon Wymanf087f472021-12-22 00:04:27 +0000503 }
Brandon Wymanf087f472021-12-22 00:04:27 +0000504 }
Brandon Wyman82affd92021-11-24 19:12:49 +0000505
506 if (vinUVFault &&
507 !(statusWord & phosphor::pmbus::status_word::VIN_UV_FAULT))
508 {
509 log<level::INFO>(
Brandon Wyman321a6152022-03-19 00:11:44 +0000510 fmt::format("{} VIN_UV fault cleared: STATUS_WORD = {:#06x}, "
Brandon Wyman6f939a32022-03-10 18:42:20 +0000511 "STATUS_MFR_SPECIFIC = {:#04x}, "
512 "STATUS_INPUT = {:#04x}",
Brandon Wyman321a6152022-03-19 00:11:44 +0000513 shortName, statusWord, statusMFR, statusInput)
Brandon Wyman82affd92021-11-24 19:12:49 +0000514 .c_str());
Brandon Wymanc2906f42021-12-21 20:14:56 +0000515 vinUVFault = 0;
Brandon Wyman82affd92021-11-24 19:12:49 +0000516 }
Brandon Wymanf087f472021-12-22 00:04:27 +0000517}
518
Brandon Wyman3f1242f2020-01-28 13:11:25 -0600519void PowerSupply::analyze()
520{
521 using namespace phosphor::pmbus;
522
B. J. Wyman681b2a32021-04-20 22:31:22 +0000523 if (presenceGPIO)
524 {
525 updatePresenceGPIO();
526 }
527
Brandon Wyman32453e92021-12-15 19:00:14 +0000528 if (present)
Brandon Wyman3f1242f2020-01-28 13:11:25 -0600529 {
530 try
531 {
Brandon Wyman9e292ee2022-03-10 22:56:23 +0000532 statusWordOld = statusWord;
Brandon Wyman32453e92021-12-15 19:00:14 +0000533 statusWord = pmbusIntf->read(STATUS_WORD, Type::Debug,
534 (readFail < LOG_LIMIT));
Brandon Wymanf65c4062020-08-19 13:15:53 -0500535 // Read worked, reset the fail count.
536 readFail = 0;
Brandon Wyman3f1242f2020-01-28 13:11:25 -0600537
538 if (statusWord)
539 {
Brandon Wymanf07bc792021-10-12 19:00:35 +0000540 statusInput = pmbusIntf->read(STATUS_INPUT, Type::Debug);
Jay Meyer10d94052020-11-30 14:41:21 -0600541 statusMFR = pmbusIntf->read(STATUS_MFR, Type::Debug);
Brandon Wyman85c7bf42021-10-19 22:28:48 +0000542 statusCML = pmbusIntf->read(STATUS_CML, Type::Debug);
Brandon Wyman6710ba22021-10-27 17:39:31 +0000543 auto status0Vout = pmbusIntf->insertPageNum(STATUS_VOUT, 0);
544 statusVout = pmbusIntf->read(status0Vout, Type::Debug);
Brandon Wymanb10b3be2021-11-09 22:12:15 +0000545 statusIout = pmbusIntf->read(STATUS_IOUT, Type::Debug);
Brandon Wyman7ee4d7e2021-11-19 20:48:23 +0000546 statusFans12 = pmbusIntf->read(STATUS_FANS_1_2, Type::Debug);
Brandon Wyman96893a42021-11-05 19:56:57 +0000547 statusTemperature =
548 pmbusIntf->read(STATUS_TEMPERATURE, Type::Debug);
Brandon Wyman9ddc6222021-10-28 17:28:01 +0000549
Brandon Wymanc2203432021-12-21 23:09:48 +0000550 analyzeCMLFault();
Brandon Wyman85c7bf42021-10-19 22:28:48 +0000551
Brandon Wymane3b0bb02021-12-21 23:16:48 +0000552 analyzeInputFault();
Brandon Wyman3f1242f2020-01-28 13:11:25 -0600553
Brandon Wymanc2c87132021-12-21 23:22:18 +0000554 analyzeVoutOVFault();
Brandon Wyman6710ba22021-10-27 17:39:31 +0000555
Brandon Wymana00e7302021-12-21 23:28:29 +0000556 analyzeIoutOCFault();
Brandon Wymanb10b3be2021-11-09 22:12:15 +0000557
Brandon Wyman08378782021-12-21 23:48:15 +0000558 analyzeVoutUVFault();
Brandon Wyman2cf46942021-10-28 19:09:16 +0000559
Brandon Wymand5d9a222021-12-21 23:59:05 +0000560 analyzeFanFault();
Brandon Wyman7ee4d7e2021-11-19 20:48:23 +0000561
Brandon Wyman52cb3f22021-12-21 23:02:47 +0000562 analyzeTemperatureFault();
Brandon Wyman96893a42021-11-05 19:56:57 +0000563
Brandon Wyman993b5542021-12-21 22:55:16 +0000564 analyzePgoodFault();
Brandon Wyman2916ea52021-11-06 03:31:18 +0000565
Brandon Wyman6c2ac392021-12-21 22:23:06 +0000566 analyzeMFRFault();
Brandon Wyman3f1242f2020-01-28 13:11:25 -0600567
Brandon Wymanf087f472021-12-22 00:04:27 +0000568 analyzeVinUVFault();
Brandon Wyman3f1242f2020-01-28 13:11:25 -0600569 }
570 else
571 {
Brandon Wyman9e292ee2022-03-10 22:56:23 +0000572 if (statusWord != statusWordOld)
573 {
Brandon Wyman321a6152022-03-19 00:11:44 +0000574 log<level::INFO>(fmt::format("{} STATUS_WORD = {:#06x}",
575 shortName, statusWord)
Brandon Wyman9e292ee2022-03-10 22:56:23 +0000576 .c_str());
577 }
578
Brandon Wymane3f7ad22021-12-21 20:27:45 +0000579 // if INPUT/VIN_UV fault was on, it cleared, trace it.
580 if (inputFault)
581 {
582 log<level::INFO>(
583 fmt::format(
Brandon Wyman321a6152022-03-19 00:11:44 +0000584 "{} INPUT fault cleared: STATUS_WORD = {:#06x}",
585 shortName, statusWord)
Brandon Wymane3f7ad22021-12-21 20:27:45 +0000586 .c_str());
587 }
588
589 if (vinUVFault)
590 {
591 log<level::INFO>(
Brandon Wyman321a6152022-03-19 00:11:44 +0000592 fmt::format("{} VIN_UV cleared: STATUS_WORD = {:#06x}",
593 shortName, statusWord)
Brandon Wymane3f7ad22021-12-21 20:27:45 +0000594 .c_str());
595 }
596
Brandon Wyman06ca4592021-12-06 22:52:23 +0000597 if (pgoodFault > 0)
Brandon Wyman4aecc292021-11-10 22:40:41 +0000598 {
Brandon Wyman321a6152022-03-19 00:11:44 +0000599 log<level::INFO>(
600 fmt::format("{} pgoodFault cleared", shortName)
601 .c_str());
Brandon Wyman4aecc292021-11-10 22:40:41 +0000602 }
Brandon Wymane3f7ad22021-12-21 20:27:45 +0000603
604 clearFaultFlags();
Brandon Wyman3f1242f2020-01-28 13:11:25 -0600605 }
Brandon Wyman82affd92021-11-24 19:12:49 +0000606
607 // Save off old inputVoltage value.
608 // Get latest inputVoltage.
609 // If voltage went from below minimum, and now is not, clear faults.
610 // Note: getInputVoltage() has its own try/catch.
611 int inputVoltageOld = inputVoltage;
Brandon Wyman4fc191f2022-03-10 23:07:13 +0000612 double actualInputVoltageOld = actualInputVoltage;
Brandon Wyman82affd92021-11-24 19:12:49 +0000613 getInputVoltage(actualInputVoltage, inputVoltage);
614 if ((inputVoltageOld == in_input::VIN_VOLTAGE_0) &&
615 (inputVoltage != in_input::VIN_VOLTAGE_0))
616 {
617 log<level::INFO>(
618 fmt::format(
Brandon Wyman4fc191f2022-03-10 23:07:13 +0000619 "{} READ_VIN back in range: actualInputVoltageOld = {} "
620 "actualInputVoltage = {}",
621 shortName, actualInputVoltageOld, actualInputVoltage)
Brandon Wyman82affd92021-11-24 19:12:49 +0000622 .c_str());
Brandon Wyman3225a452022-03-18 18:51:49 +0000623 clearVinUVFault();
Brandon Wyman82affd92021-11-24 19:12:49 +0000624 }
Brandon Wyman4fc191f2022-03-10 23:07:13 +0000625 else if (vinUVFault && (inputVoltage != in_input::VIN_VOLTAGE_0))
626 {
627 log<level::INFO>(
628 fmt::format(
629 "{} CLEAR_FAULTS: vinUVFault {} actualInputVoltage {}",
630 shortName, vinUVFault, actualInputVoltage)
631 .c_str());
632 // Do we have a VIN_UV fault latched that can now be cleared
Brandon Wyman3225a452022-03-18 18:51:49 +0000633 // due to voltage back in range? Attempt to clear the fault(s),
634 // re-check faults on next call.
635 clearVinUVFault();
Brandon Wyman4fc191f2022-03-10 23:07:13 +0000636 }
637 else if (std::abs(actualInputVoltageOld - actualInputVoltage) > 1.0)
638 {
639 log<level::INFO>(
640 fmt::format(
641 "{} actualInputVoltageOld = {} actualInputVoltage = {}",
642 shortName, actualInputVoltageOld, actualInputVoltage)
643 .c_str());
644 }
Matt Spinler0975eaf2022-02-14 15:38:30 -0600645
646 checkAvailability();
Brandon Wymanc3324422022-03-24 20:30:57 +0000647
648 if (inputHistorySupported)
649 {
650 updateHistory();
651 }
Brandon Wyman3f1242f2020-01-28 13:11:25 -0600652 }
Patrick Williamsc1d4de52021-10-06 12:45:57 -0500653 catch (const ReadFailure& e)
Brandon Wyman3f1242f2020-01-28 13:11:25 -0600654 {
Brandon Wyman32453e92021-12-15 19:00:14 +0000655 if (readFail < SIZE_MAX)
656 {
657 readFail++;
658 }
659 if (readFail == LOG_LIMIT)
660 {
661 phosphor::logging::commit<ReadFailure>();
662 }
Brandon Wyman3f1242f2020-01-28 13:11:25 -0600663 }
664 }
665}
666
Brandon Wyman59a35792020-06-04 12:37:40 -0500667void PowerSupply::onOffConfig(uint8_t data)
668{
669 using namespace phosphor::pmbus;
670
671 if (present)
672 {
673 log<level::INFO>("ON_OFF_CONFIG write", entry("DATA=0x%02X", data));
674 try
675 {
676 std::vector<uint8_t> configData{data};
677 pmbusIntf->writeBinary(ON_OFF_CONFIG, configData,
678 Type::HwmonDeviceDebug);
679 }
680 catch (...)
681 {
682 // The underlying code in writeBinary will log a message to the
B. J. Wyman681b2a32021-04-20 22:31:22 +0000683 // journal if the write fails. If the ON_OFF_CONFIG is not setup
684 // as desired, later fault detection and analysis code should
685 // catch any of the fall out. We should not need to terminate
686 // the application if this write fails.
Brandon Wyman59a35792020-06-04 12:37:40 -0500687 }
688 }
689}
690
Brandon Wyman3225a452022-03-18 18:51:49 +0000691void PowerSupply::clearVinUVFault()
692{
693 // Read in1_lcrit_alarm to clear bits 3 and 4 of STATUS_INPUT.
694 // The fault bits in STAUTS_INPUT roll-up to STATUS_WORD. Clearing those
695 // bits in STATUS_INPUT should result in the corresponding STATUS_WORD bits
696 // also clearing.
697 //
698 // Do not care about return value. Should be 1 if active, 0 if not.
699 static_cast<void>(
700 pmbusIntf->read("in1_lcrit_alarm", phosphor::pmbus::Type::Hwmon));
701 vinUVFault = 0;
702}
703
Brandon Wyman3c208462020-05-13 16:25:58 -0500704void PowerSupply::clearFaults()
705{
Brandon Wyman82affd92021-11-24 19:12:49 +0000706 log<level::DEBUG>(
707 fmt::format("clearFaults() inventoryPath: {}", inventoryPath).c_str());
Brandon Wyman5474c912021-02-23 14:39:43 -0600708 faultLogged = false;
Brandon Wyman3c208462020-05-13 16:25:58 -0500709 // The PMBus device driver does not allow for writing CLEAR_FAULTS
710 // directly. However, the pmbus hwmon device driver code will send a
711 // CLEAR_FAULTS after reading from any of the hwmon "files" in sysfs, so
712 // reading in1_input should result in clearing the fault bits in
713 // STATUS_BYTE/STATUS_WORD.
714 // I do not care what the return value is.
Brandon Wyman11151532020-11-10 13:45:57 -0600715 if (present)
Brandon Wyman3c208462020-05-13 16:25:58 -0500716 {
Brandon Wymane3f7ad22021-12-21 20:27:45 +0000717 clearFaultFlags();
Matt Spinler0975eaf2022-02-14 15:38:30 -0600718 checkAvailability();
Brandon Wyman9564e942020-11-10 14:01:42 -0600719 readFail = 0;
Brandon Wyman9564e942020-11-10 14:01:42 -0600720
Brandon Wyman11151532020-11-10 13:45:57 -0600721 try
722 {
Brandon Wyman3225a452022-03-18 18:51:49 +0000723 clearVinUVFault();
Brandon Wyman11151532020-11-10 13:45:57 -0600724 static_cast<void>(
725 pmbusIntf->read("in1_input", phosphor::pmbus::Type::Hwmon));
726 }
Patrick Williamsc1d4de52021-10-06 12:45:57 -0500727 catch (const ReadFailure& e)
Brandon Wyman11151532020-11-10 13:45:57 -0600728 {
729 // Since I do not care what the return value is, I really do not
B. J. Wyman681b2a32021-04-20 22:31:22 +0000730 // care much if it gets a ReadFailure either. However, this
731 // should not prevent the application from continuing to run, so
732 // catching the read failure.
Brandon Wyman11151532020-11-10 13:45:57 -0600733 }
Brandon Wyman3c208462020-05-13 16:25:58 -0500734 }
735}
736
Brandon Wymanaed1f752019-11-25 18:10:52 -0600737void PowerSupply::inventoryChanged(sdbusplus::message::message& msg)
738{
739 std::string msgSensor;
Patrick Williamsabe49412020-05-13 17:59:47 -0500740 std::map<std::string, std::variant<uint32_t, bool>> msgData;
Brandon Wymanaed1f752019-11-25 18:10:52 -0600741 msg.read(msgSensor, msgData);
742
743 // Check if it was the Present property that changed.
744 auto valPropMap = msgData.find(PRESENT_PROP);
745 if (valPropMap != msgData.end())
746 {
747 if (std::get<bool>(valPropMap->second))
748 {
749 present = true;
B. J. Wyman681b2a32021-04-20 22:31:22 +0000750 // TODO: Immediately trying to read or write the "files" causes
751 // read or write failures.
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500752 using namespace std::chrono_literals;
753 std::this_thread::sleep_for(20ms);
Brandon Wyman9564e942020-11-10 14:01:42 -0600754 pmbusIntf->findHwmonDir();
Brandon Wyman59a35792020-06-04 12:37:40 -0500755 onOffConfig(phosphor::pmbus::ON_OFF_CONFIG_CONTROL_PIN_ONLY);
Brandon Wymanaed1f752019-11-25 18:10:52 -0600756 clearFaults();
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500757 updateInventory();
Brandon Wymanaed1f752019-11-25 18:10:52 -0600758 }
759 else
760 {
761 present = false;
762
763 // Clear out the now outdated inventory properties
764 updateInventory();
765 }
Matt Spinler0975eaf2022-02-14 15:38:30 -0600766 checkAvailability();
Brandon Wymanaed1f752019-11-25 18:10:52 -0600767 }
768}
769
Brandon Wyman9a507db2021-02-25 16:15:22 -0600770void PowerSupply::inventoryAdded(sdbusplus::message::message& msg)
771{
772 sdbusplus::message::object_path path;
773 msg.read(path);
774 // Make sure the signal is for the PSU inventory path
775 if (path == inventoryPath)
776 {
777 std::map<std::string, std::map<std::string, std::variant<bool>>>
778 interfaces;
779 // Get map of interfaces and their properties
780 msg.read(interfaces);
781
782 auto properties = interfaces.find(INVENTORY_IFACE);
783 if (properties != interfaces.end())
784 {
785 auto property = properties->second.find(PRESENT_PROP);
786 if (property != properties->second.end())
787 {
788 present = std::get<bool>(property->second);
789
790 log<level::INFO>(fmt::format("Power Supply {} Present {}",
791 inventoryPath, present)
792 .c_str());
793
794 updateInventory();
Matt Spinler0975eaf2022-02-14 15:38:30 -0600795 checkAvailability();
Brandon Wyman9a507db2021-02-25 16:15:22 -0600796 }
797 }
798 }
799}
800
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500801void PowerSupply::updateInventory()
802{
803 using namespace phosphor::pmbus;
804
Chanh Nguyenc12c53b2021-04-06 17:24:47 +0700805#if IBM_VPD
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500806 std::string ccin;
807 std::string pn;
808 std::string fn;
809 std::string header;
810 std::string sn;
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500811 using PropertyMap =
George Liu070c1bc2020-10-12 11:28:01 +0800812 std::map<std::string,
813 std::variant<std::string, std::vector<uint8_t>, bool>>;
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500814 PropertyMap assetProps;
George Liu070c1bc2020-10-12 11:28:01 +0800815 PropertyMap operProps;
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500816 PropertyMap versionProps;
817 PropertyMap ipzvpdDINFProps;
818 PropertyMap ipzvpdVINIProps;
819 using InterfaceMap = std::map<std::string, PropertyMap>;
820 InterfaceMap interfaces;
821 using ObjectMap = std::map<sdbusplus::message::object_path, InterfaceMap>;
822 ObjectMap object;
823#endif
B. J. Wyman681b2a32021-04-20 22:31:22 +0000824 log<level::DEBUG>(
825 fmt::format("updateInventory() inventoryPath: {}", inventoryPath)
826 .c_str());
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500827
828 if (present)
829 {
830 // TODO: non-IBM inventory updates?
831
Chanh Nguyenc12c53b2021-04-06 17:24:47 +0700832#if IBM_VPD
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500833 try
834 {
835 ccin = pmbusIntf->readString(CCIN, Type::HwmonDeviceDebug);
836 assetProps.emplace(MODEL_PROP, ccin);
Adriana Kobylak572a9052021-03-30 15:58:07 +0000837 modelName = ccin;
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500838 }
Patrick Williamsc1d4de52021-10-06 12:45:57 -0500839 catch (const ReadFailure& e)
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500840 {
B. J. Wyman681b2a32021-04-20 22:31:22 +0000841 // Ignore the read failure, let pmbus code indicate failure,
842 // path...
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500843 // TODO - ibm918
844 // https://github.com/openbmc/docs/blob/master/designs/vpd-collection.md
845 // The BMC must log errors if any of the VPD cannot be properly
846 // parsed or fails ECC checks.
847 }
848
849 try
850 {
851 pn = pmbusIntf->readString(PART_NUMBER, Type::HwmonDeviceDebug);
852 assetProps.emplace(PN_PROP, pn);
853 }
Patrick Williamsc1d4de52021-10-06 12:45:57 -0500854 catch (const ReadFailure& e)
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500855 {
B. J. Wyman681b2a32021-04-20 22:31:22 +0000856 // Ignore the read failure, let pmbus code indicate failure,
857 // path...
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500858 }
859
860 try
861 {
862 fn = pmbusIntf->readString(FRU_NUMBER, Type::HwmonDeviceDebug);
Brandon Wymana169b0f2021-12-07 20:18:06 +0000863 assetProps.emplace(SPARE_PN_PROP, fn);
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500864 }
Patrick Williamsc1d4de52021-10-06 12:45:57 -0500865 catch (const ReadFailure& e)
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500866 {
B. J. Wyman681b2a32021-04-20 22:31:22 +0000867 // Ignore the read failure, let pmbus code indicate failure,
868 // path...
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500869 }
870
871 try
872 {
873 header =
874 pmbusIntf->readString(SERIAL_HEADER, Type::HwmonDeviceDebug);
875 sn = pmbusIntf->readString(SERIAL_NUMBER, Type::HwmonDeviceDebug);
Mike Capps1cb0f132022-03-14 11:40:30 -0400876 assetProps.emplace(SN_PROP, header + sn);
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500877 }
Patrick Williamsc1d4de52021-10-06 12:45:57 -0500878 catch (const ReadFailure& e)
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500879 {
B. J. Wyman681b2a32021-04-20 22:31:22 +0000880 // Ignore the read failure, let pmbus code indicate failure,
881 // path...
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500882 }
883
884 try
885 {
Brandon Wymanc9efe412020-10-09 15:42:50 -0500886 fwVersion =
887 pmbusIntf->readString(FW_VERSION, Type::HwmonDeviceDebug);
888 versionProps.emplace(VERSION_PROP, fwVersion);
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500889 }
Patrick Williamsc1d4de52021-10-06 12:45:57 -0500890 catch (const ReadFailure& e)
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500891 {
B. J. Wyman681b2a32021-04-20 22:31:22 +0000892 // Ignore the read failure, let pmbus code indicate failure,
893 // path...
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500894 }
895
896 ipzvpdVINIProps.emplace("CC",
897 std::vector<uint8_t>(ccin.begin(), ccin.end()));
898 ipzvpdVINIProps.emplace("PN",
899 std::vector<uint8_t>(pn.begin(), pn.end()));
900 ipzvpdVINIProps.emplace("FN",
901 std::vector<uint8_t>(fn.begin(), fn.end()));
Brandon Wyman33d492f2022-03-23 20:45:17 +0000902 std::string header_sn = header + sn;
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500903 ipzvpdVINIProps.emplace(
904 "SN", std::vector<uint8_t>(header_sn.begin(), header_sn.end()));
905 std::string description = "IBM PS";
906 ipzvpdVINIProps.emplace(
907 "DR", std::vector<uint8_t>(description.begin(), description.end()));
908
Ben Tynerf8d8c462022-01-27 16:09:45 -0600909 // Populate the VINI Resource Type (RT) keyword
910 ipzvpdVINIProps.emplace("RT", std::vector<uint8_t>{'V', 'I', 'N', 'I'});
911
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500912 // Update the Resource Identifier (RI) keyword
913 // 2 byte FRC: 0x0003
914 // 2 byte RID: 0x1000, 0x1001...
915 std::uint8_t num = std::stoul(
916 inventoryPath.substr(inventoryPath.size() - 1, 1), nullptr, 0);
917 std::vector<uint8_t> ri{0x00, 0x03, 0x10, num};
918 ipzvpdDINFProps.emplace("RI", ri);
919
920 // Fill in the FRU Label (FL) keyword.
921 std::string fl = "E";
922 fl.push_back(inventoryPath.back());
923 fl.resize(FL_KW_SIZE, ' ');
924 ipzvpdDINFProps.emplace("FL",
925 std::vector<uint8_t>(fl.begin(), fl.end()));
926
Ben Tynerf8d8c462022-01-27 16:09:45 -0600927 // Populate the DINF Resource Type (RT) keyword
928 ipzvpdDINFProps.emplace("RT", std::vector<uint8_t>{'D', 'I', 'N', 'F'});
929
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500930 interfaces.emplace(ASSET_IFACE, std::move(assetProps));
931 interfaces.emplace(VERSION_IFACE, std::move(versionProps));
932 interfaces.emplace(DINF_IFACE, std::move(ipzvpdDINFProps));
933 interfaces.emplace(VINI_IFACE, std::move(ipzvpdVINIProps));
934
George Liu070c1bc2020-10-12 11:28:01 +0800935 // Update the Functional
936 operProps.emplace(FUNCTIONAL_PROP, present);
937 interfaces.emplace(OPERATIONAL_STATE_IFACE, std::move(operProps));
938
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500939 auto path = inventoryPath.substr(strlen(INVENTORY_OBJ_PATH));
940 object.emplace(path, std::move(interfaces));
941
942 try
943 {
944 auto service =
945 util::getService(INVENTORY_OBJ_PATH, INVENTORY_MGR_IFACE, bus);
946
947 if (service.empty())
948 {
949 log<level::ERR>("Unable to get inventory manager service");
950 return;
951 }
952
953 auto method =
954 bus.new_method_call(service.c_str(), INVENTORY_OBJ_PATH,
955 INVENTORY_MGR_IFACE, "Notify");
956
957 method.append(std::move(object));
958
959 auto reply = bus.call(method);
960 }
Patrick Williamsc1d4de52021-10-06 12:45:57 -0500961 catch (const std::exception& e)
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500962 {
Jay Meyer6a3fd2c2020-08-25 16:37:16 -0500963 log<level::ERR>(
964 std::string(e.what() + std::string(" PATH=") + inventoryPath)
965 .c_str());
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500966 }
967#endif
968 }
969}
970
Brandon Wymanc3324422022-03-24 20:30:57 +0000971void PowerSupply::setupInputHistory()
972{
973 if (bindPath.string().find("ibm-cffps") != std::string::npos)
974 {
975 // Do not enable input history for power supplies that are missing
976 if (present)
977 {
978 inputHistorySupported = true;
979 log<level::INFO>(
980 fmt::format("{} INPUT_HISTORY enabled", shortName).c_str());
981
982 std::string name{fmt::format("{}_input_power", shortName)};
983
984 historyObjectPath =
985 std::string{INPUT_HISTORY_SENSOR_ROOT} + '/' + name;
986
987 // If the power supply was present, we created the recordManager.
988 // If it then went missing, the recordManager is still there.
989 // If it then is reinserted, we should be able to use the
990 // recordManager that was allocated when it was initially present.
991 if (!recordManager)
992 {
993 recordManager = std::make_unique<history::RecordManager>(
994 INPUT_HISTORY_MAX_RECORDS);
995 }
996
997 if (!average)
998 {
999 auto avgPath = historyObjectPath + '/' + history::Average::name;
1000 average = std::make_unique<history::Average>(bus, avgPath);
1001 log<level::DEBUG>(
1002 fmt::format("{} avgPath: {}", shortName, avgPath).c_str());
1003 }
1004 if (!maximum)
1005 {
1006 auto maxPath = historyObjectPath + '/' + history::Maximum::name;
1007 maximum = std::make_unique<history::Maximum>(bus, maxPath);
1008 log<level::DEBUG>(
1009 fmt::format("{} maxPath: {}", shortName, maxPath).c_str());
1010 }
1011
1012 log<level::DEBUG>(fmt::format("{} historyObjectPath: {}", shortName,
1013 historyObjectPath)
1014 .c_str());
1015 }
1016 }
1017 else
1018 {
1019 inputHistorySupported = false;
1020 }
1021}
1022
1023void PowerSupply::updateHistory()
1024{
1025 if (!recordManager)
1026 {
1027 // Not enabled
1028 return;
1029 }
1030
1031 if (!present)
1032 {
1033 // Cannot read when not present
1034 return;
1035 }
1036
1037 // Read just the most recent average/max record
1038 auto data =
1039 pmbusIntf->readBinary(INPUT_HISTORY, pmbus::Type::HwmonDeviceDebug,
1040 history::RecordManager::RAW_RECORD_SIZE);
1041
1042 // Update D-Bus only if something changed (a new record ID, or cleared out)
1043 auto changed = recordManager->add(data);
1044 if (changed)
1045 {
1046 average->values(std::move(recordManager->getAverageRecords()));
1047 maximum->values(std::move(recordManager->getMaximumRecords()));
1048 }
1049}
1050
Adriana Kobylak4175ffb2021-08-02 14:51:05 +00001051void PowerSupply::getInputVoltage(double& actualInputVoltage,
1052 int& inputVoltage) const
1053{
1054 using namespace phosphor::pmbus;
1055
1056 actualInputVoltage = in_input::VIN_VOLTAGE_0;
1057 inputVoltage = in_input::VIN_VOLTAGE_0;
1058
1059 if (present)
1060 {
1061 try
1062 {
1063 // Read input voltage in millivolts
1064 auto inputVoltageStr = pmbusIntf->readString(READ_VIN, Type::Hwmon);
1065
1066 // Convert to volts
1067 actualInputVoltage = std::stod(inputVoltageStr) / 1000;
1068
1069 // Calculate the voltage based on voltage thresholds
1070 if (actualInputVoltage < in_input::VIN_VOLTAGE_MIN)
1071 {
1072 inputVoltage = in_input::VIN_VOLTAGE_0;
1073 }
1074 else if (actualInputVoltage < in_input::VIN_VOLTAGE_110_THRESHOLD)
1075 {
1076 inputVoltage = in_input::VIN_VOLTAGE_110;
1077 }
1078 else
1079 {
1080 inputVoltage = in_input::VIN_VOLTAGE_220;
1081 }
1082 }
1083 catch (const std::exception& e)
1084 {
1085 log<level::ERR>(
Brandon Wyman321a6152022-03-19 00:11:44 +00001086 fmt::format("{} READ_VIN read error: {}", shortName, e.what())
1087 .c_str());
Adriana Kobylak4175ffb2021-08-02 14:51:05 +00001088 }
1089 }
1090}
1091
Matt Spinler0975eaf2022-02-14 15:38:30 -06001092void PowerSupply::checkAvailability()
1093{
1094 bool origAvailability = available;
1095 available = present && !hasInputFault() && !hasVINUVFault() &&
1096 !hasPSKillFault() && !hasIoutOCFault();
1097
1098 if (origAvailability != available)
1099 {
1100 auto invpath = inventoryPath.substr(strlen(INVENTORY_OBJ_PATH));
1101 phosphor::power::psu::setAvailable(bus, invpath, available);
Matt Spinlerca1e9ea2022-02-18 14:03:08 -06001102
1103 // Check if the health rollup needs to change based on the
1104 // new availability value.
1105 phosphor::power::psu::handleChassisHealthRollup(bus, inventoryPath,
1106 !available);
Matt Spinler0975eaf2022-02-14 15:38:30 -06001107 }
1108}
1109
Brandon Wyman3f1242f2020-01-28 13:11:25 -06001110} // namespace phosphor::power::psu