blob: e5fa9c8499c8e2517f5f93be9282c56fc1361e75 [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 Wyman1d7a7df2020-03-26 10:14:05 -050012#include <chrono> // sleep_for()
13#include <cstdint> // uint8_t...
B. J. Wyman681b2a32021-04-20 22:31:22 +000014#include <fstream>
15#include <thread> // sleep_for()
Brandon Wyman1d7a7df2020-03-26 10:14:05 -050016
Brandon Wyman3f1242f2020-01-28 13:11:25 -060017namespace phosphor::power::psu
Brandon Wymanaed1f752019-11-25 18:10:52 -060018{
B. J. Wyman681b2a32021-04-20 22:31:22 +000019// Amount of time in milliseconds to delay between power supply going from
20// missing to present before running the bind command(s).
21constexpr auto bindDelay = 1000;
Brandon Wymanaed1f752019-11-25 18:10:52 -060022
23using namespace phosphor::logging;
Brandon Wyman3f1242f2020-01-28 13:11:25 -060024using namespace sdbusplus::xyz::openbmc_project::Common::Device::Error;
Brandon Wymanaed1f752019-11-25 18:10:52 -060025
Brandon Wyman510acaa2020-11-05 18:32:04 -060026PowerSupply::PowerSupply(sdbusplus::bus::bus& bus, const std::string& invpath,
B. J. Wyman681b2a32021-04-20 22:31:22 +000027 std::uint8_t i2cbus, std::uint16_t i2caddr,
28 const std::string& gpioLineName) :
Brandon Wyman510acaa2020-11-05 18:32:04 -060029 bus(bus),
B. J. Wyman681b2a32021-04-20 22:31:22 +000030 inventoryPath(invpath), bindPath("/sys/bus/i2c/drivers/ibm-cffps")
Brandon Wyman510acaa2020-11-05 18:32:04 -060031{
32 if (inventoryPath.empty())
33 {
34 throw std::invalid_argument{"Invalid empty inventoryPath"};
35 }
36
B. J. Wyman681b2a32021-04-20 22:31:22 +000037 if (gpioLineName.empty())
38 {
39 throw std::invalid_argument{"Invalid empty gpioLineName"};
40 }
Brandon Wyman510acaa2020-11-05 18:32:04 -060041
B. J. Wyman681b2a32021-04-20 22:31:22 +000042 log<level::DEBUG>(fmt::format("gpioLineName: {}", gpioLineName).c_str());
43 presenceGPIO = createGPIO(gpioLineName);
Brandon Wyman510acaa2020-11-05 18:32:04 -060044
45 std::ostringstream ss;
46 ss << std::hex << std::setw(4) << std::setfill('0') << i2caddr;
47 std::string addrStr = ss.str();
B. J. Wyman681b2a32021-04-20 22:31:22 +000048 std::string busStr = std::to_string(i2cbus);
49 bindDevice = busStr;
50 bindDevice.append("-");
51 bindDevice.append(addrStr);
52
Brandon Wyman510acaa2020-11-05 18:32:04 -060053 pmbusIntf = phosphor::pmbus::createPMBus(i2cbus, addrStr);
54
55 // Get the current state of the Present property.
B. J. Wyman681b2a32021-04-20 22:31:22 +000056 try
57 {
58 updatePresenceGPIO();
59 }
60 catch (...)
61 {
62 // If the above attempt to use the GPIO failed, it likely means that the
63 // GPIOs are in use by the kernel, meaning it is using gpio-keys.
64 // So, I should rely on phosphor-gpio-presence to update D-Bus, and
65 // work that way for power supply presence.
66 presenceGPIO = nullptr;
67 // Setup the functions to call when the D-Bus inventory path for the
68 // Present property changes.
69 presentMatch = std::make_unique<sdbusplus::bus::match_t>(
70 bus,
71 sdbusplus::bus::match::rules::propertiesChanged(inventoryPath,
72 INVENTORY_IFACE),
73 [this](auto& msg) { this->inventoryChanged(msg); });
74
75 presentAddedMatch = std::make_unique<sdbusplus::bus::match_t>(
76 bus,
77 sdbusplus::bus::match::rules::interfacesAdded() +
78 sdbusplus::bus::match::rules::argNpath(0, inventoryPath),
79 [this](auto& msg) { this->inventoryAdded(msg); });
80
81 updatePresence();
82 updateInventory();
83 }
84}
85
86void PowerSupply::bindOrUnbindDriver(bool present)
87{
88 auto action = (present) ? "bind" : "unbind";
89 auto path = bindPath / action;
90
91 if (present)
92 {
93 log<level::INFO>(
94 fmt::format("Binding device driver. path: {} device: {}",
95 path.string(), bindDevice)
96 .c_str());
97 }
98 else
99 {
100 log<level::INFO>(
101 fmt::format("Unbinding device driver. path: {} device: {}",
102 path.string(), bindDevice)
103 .c_str());
104 }
105
106 std::ofstream file;
107
108 file.exceptions(std::ofstream::failbit | std::ofstream::badbit |
109 std::ofstream::eofbit);
110
111 try
112 {
113 file.open(path);
114 file << bindDevice;
115 file.close();
116 }
Patrick Williamsc1d4de52021-10-06 12:45:57 -0500117 catch (const std::exception& e)
B. J. Wyman681b2a32021-04-20 22:31:22 +0000118 {
119 auto err = errno;
120
121 log<level::ERR>(
122 fmt::format("Failed binding or unbinding device. errno={}", err)
123 .c_str());
124 }
Brandon Wyman510acaa2020-11-05 18:32:04 -0600125}
126
Brandon Wymanaed1f752019-11-25 18:10:52 -0600127void PowerSupply::updatePresence()
128{
129 try
130 {
Brandon Wyman3f1242f2020-01-28 13:11:25 -0600131 present = getPresence(bus, inventoryPath);
Brandon Wymanaed1f752019-11-25 18:10:52 -0600132 }
Patrick Williams69f10ad2021-09-02 09:46:49 -0500133 catch (const sdbusplus::exception::exception& e)
Brandon Wymanaed1f752019-11-25 18:10:52 -0600134 {
135 // Relying on property change or interface added to retry.
136 // Log an informational trace to the journal.
Brandon Wymandf13c3a2020-12-15 14:25:22 -0600137 log<level::INFO>(
138 fmt::format("D-Bus property {} access failure exception",
139 inventoryPath)
140 .c_str());
Brandon Wymanaed1f752019-11-25 18:10:52 -0600141 }
142}
143
B. J. Wyman681b2a32021-04-20 22:31:22 +0000144void PowerSupply::updatePresenceGPIO()
145{
146 bool presentOld = present;
147
148 try
149 {
150 if (presenceGPIO->read() > 0)
151 {
152 present = true;
153 }
154 else
155 {
156 present = false;
157 }
158 }
Patrick Williamsc1d4de52021-10-06 12:45:57 -0500159 catch (const std::exception& e)
B. J. Wyman681b2a32021-04-20 22:31:22 +0000160 {
161 log<level::ERR>(
162 fmt::format("presenceGPIO read fail: {}", e.what()).c_str());
163 throw;
164 }
165
166 if (presentOld != present)
167 {
168 log<level::DEBUG>(
169 fmt::format("presentOld: {} present: {}", presentOld, present)
170 .c_str());
171 if (present)
172 {
173 std::this_thread::sleep_for(std::chrono::milliseconds(bindDelay));
174 bindOrUnbindDriver(present);
175 pmbusIntf->findHwmonDir();
176 onOffConfig(phosphor::pmbus::ON_OFF_CONFIG_CONTROL_PIN_ONLY);
177 clearFaults();
178 }
179 else
180 {
181 bindOrUnbindDriver(present);
182 }
183
184 auto invpath = inventoryPath.substr(strlen(INVENTORY_OBJ_PATH));
185 auto const lastSlashPos = invpath.find_last_of('/');
186 std::string prettyName = invpath.substr(lastSlashPos + 1);
187 setPresence(bus, invpath, present, prettyName);
188 updateInventory();
Matt Spinler0975eaf2022-02-14 15:38:30 -0600189 checkAvailability();
B. J. Wyman681b2a32021-04-20 22:31:22 +0000190 }
191}
192
Brandon Wymanc2203432021-12-21 23:09:48 +0000193void PowerSupply::analyzeCMLFault()
194{
195 if (statusWord & phosphor::pmbus::status_word::CML_FAULT)
196 {
Brandon Wymanc2906f42021-12-21 20:14:56 +0000197 if (cmlFault < DEGLITCH_LIMIT)
Brandon Wymanc2203432021-12-21 23:09:48 +0000198 {
199 log<level::ERR>(fmt::format("CML fault: STATUS_WORD = {:#04x}, "
200 "STATUS_CML = {:#02x}",
201 statusWord, statusCML)
202 .c_str());
Brandon Wymanc2203432021-12-21 23:09:48 +0000203
Brandon Wymanc2906f42021-12-21 20:14:56 +0000204 cmlFault++;
205 }
206 }
207 else
208 {
209 cmlFault = 0;
Brandon Wymanc2203432021-12-21 23:09:48 +0000210 }
211}
212
Brandon Wymane3b0bb02021-12-21 23:16:48 +0000213void PowerSupply::analyzeInputFault()
214{
215 if (statusWord & phosphor::pmbus::status_word::INPUT_FAULT_WARN)
216 {
Brandon Wymanc2906f42021-12-21 20:14:56 +0000217 if (inputFault < DEGLITCH_LIMIT)
Brandon Wymane3b0bb02021-12-21 23:16:48 +0000218 {
219 log<level::ERR>(fmt::format("INPUT fault: STATUS_WORD = {:#04x}, "
220 "STATUS_MFR_SPECIFIC = {:#02x}, "
221 "STATUS_INPUT = {:#02x}",
222 statusWord, statusMFR, statusInput)
223 .c_str());
Brandon Wymane3b0bb02021-12-21 23:16:48 +0000224
Brandon Wymanc2906f42021-12-21 20:14:56 +0000225 inputFault++;
226 }
Brandon Wymane3b0bb02021-12-21 23:16:48 +0000227 }
Brandon Wyman82affd92021-11-24 19:12:49 +0000228
229 // If had INPUT/VIN_UV fault, and now off.
230 // Trace that odd behavior.
231 if (inputFault &&
232 !(statusWord & phosphor::pmbus::status_word::INPUT_FAULT_WARN))
233 {
234 log<level::INFO>(
235 fmt::format("INPUT fault cleared: STATUS_WORD = {:#04x}, "
236 "STATUS_MFR_SPECIFIC = {:#02x}, "
237 "STATUS_INPUT = {:#02x}",
238 statusWord, statusMFR, statusInput)
239 .c_str());
Brandon Wymanc2906f42021-12-21 20:14:56 +0000240 inputFault = 0;
Brandon Wyman82affd92021-11-24 19:12:49 +0000241 }
Brandon Wymane3b0bb02021-12-21 23:16:48 +0000242}
243
Brandon Wymanc2c87132021-12-21 23:22:18 +0000244void PowerSupply::analyzeVoutOVFault()
245{
246 if (statusWord & phosphor::pmbus::status_word::VOUT_OV_FAULT)
247 {
Brandon Wymanc2906f42021-12-21 20:14:56 +0000248 if (voutOVFault < DEGLITCH_LIMIT)
Brandon Wymanc2c87132021-12-21 23:22:18 +0000249 {
250 log<level::ERR>(
251 fmt::format("VOUT_OV_FAULT fault: STATUS_WORD = {:#04x}, "
252 "STATUS_MFR_SPECIFIC = {:#02x}, "
253 "STATUS_VOUT = {:#02x}",
254 statusWord, statusMFR, statusVout)
255 .c_str());
Brandon Wymanc2c87132021-12-21 23:22:18 +0000256
Brandon Wymanc2906f42021-12-21 20:14:56 +0000257 voutOVFault++;
258 }
259 }
260 else
261 {
262 voutOVFault = 0;
Brandon Wymanc2c87132021-12-21 23:22:18 +0000263 }
264}
265
Brandon Wymana00e7302021-12-21 23:28:29 +0000266void PowerSupply::analyzeIoutOCFault()
267{
268 if (statusWord & phosphor::pmbus::status_word::IOUT_OC_FAULT)
269 {
Brandon Wymanc2906f42021-12-21 20:14:56 +0000270 if (ioutOCFault < DEGLITCH_LIMIT)
Brandon Wymana00e7302021-12-21 23:28:29 +0000271 {
272 log<level::ERR>(fmt::format("IOUT fault: STATUS_WORD = {:#04x}, "
273 "STATUS_MFR_SPECIFIC = {:#02x}, "
274 "STATUS_IOUT = {:#02x}",
275 statusWord, statusMFR, statusIout)
276 .c_str());
Brandon Wymana00e7302021-12-21 23:28:29 +0000277
Brandon Wymanc2906f42021-12-21 20:14:56 +0000278 ioutOCFault++;
279 }
280 }
281 else
282 {
283 ioutOCFault = 0;
Brandon Wymana00e7302021-12-21 23:28:29 +0000284 }
285}
286
Brandon Wyman08378782021-12-21 23:48:15 +0000287void PowerSupply::analyzeVoutUVFault()
288{
289 if ((statusWord & phosphor::pmbus::status_word::VOUT_FAULT) &&
290 !(statusWord & phosphor::pmbus::status_word::VOUT_OV_FAULT))
291 {
Brandon Wymanc2906f42021-12-21 20:14:56 +0000292 if (voutUVFault < DEGLITCH_LIMIT)
Brandon Wyman08378782021-12-21 23:48:15 +0000293 {
294 log<level::ERR>(
295 fmt::format("VOUT_UV_FAULT fault: STATUS_WORD = {:#04x}, "
296 "STATUS_MFR_SPECIFIC = {:#02x}, "
297 "STATUS_VOUT = {:#02x}",
298 statusWord, statusMFR, statusVout)
299 .c_str());
Brandon Wyman08378782021-12-21 23:48:15 +0000300
Brandon Wymanc2906f42021-12-21 20:14:56 +0000301 voutUVFault++;
302 }
303 }
304 else
305 {
306 voutUVFault = 0;
Brandon Wyman08378782021-12-21 23:48:15 +0000307 }
308}
309
Brandon Wymand5d9a222021-12-21 23:59:05 +0000310void PowerSupply::analyzeFanFault()
311{
312 if (statusWord & phosphor::pmbus::status_word::FAN_FAULT)
313 {
Brandon Wymanc2906f42021-12-21 20:14:56 +0000314 if (fanFault < DEGLITCH_LIMIT)
Brandon Wymand5d9a222021-12-21 23:59:05 +0000315 {
316 log<level::ERR>(fmt::format("FANS fault/warning: "
317 "STATUS_WORD = {:#04x}, "
318 "STATUS_MFR_SPECIFIC = {:#02x}, "
319 "STATUS_FANS_1_2 = {:#02x}",
320 statusWord, statusMFR, statusFans12)
321 .c_str());
Brandon Wymand5d9a222021-12-21 23:59:05 +0000322
Brandon Wymanc2906f42021-12-21 20:14:56 +0000323 fanFault++;
324 }
325 }
326 else
327 {
328 fanFault = 0;
Brandon Wymand5d9a222021-12-21 23:59:05 +0000329 }
330}
331
Brandon Wyman52cb3f22021-12-21 23:02:47 +0000332void PowerSupply::analyzeTemperatureFault()
333{
334 if (statusWord & phosphor::pmbus::status_word::TEMPERATURE_FAULT_WARN)
335 {
Brandon Wymanc2906f42021-12-21 20:14:56 +0000336 if (tempFault < DEGLITCH_LIMIT)
Brandon Wyman52cb3f22021-12-21 23:02:47 +0000337 {
338 log<level::ERR>(fmt::format("TEMPERATURE fault/warning: "
339 "STATUS_WORD = {:#04x}, "
340 "STATUS_MFR_SPECIFIC = {:#02x}, "
341 "STATUS_TEMPERATURE = {:#02x}",
342 statusWord, statusMFR,
343 statusTemperature)
344 .c_str());
Brandon Wyman52cb3f22021-12-21 23:02:47 +0000345
Brandon Wymanc2906f42021-12-21 20:14:56 +0000346 tempFault++;
347 }
348 }
349 else
350 {
351 tempFault = 0;
Brandon Wyman52cb3f22021-12-21 23:02:47 +0000352 }
353}
354
Brandon Wyman993b5542021-12-21 22:55:16 +0000355void PowerSupply::analyzePgoodFault()
356{
357 if ((statusWord & phosphor::pmbus::status_word::POWER_GOOD_NEGATED) ||
358 (statusWord & phosphor::pmbus::status_word::UNIT_IS_OFF))
359 {
360 if (pgoodFault < DEGLITCH_LIMIT)
361 {
362 log<level::ERR>(fmt::format("PGOOD fault: "
363 "STATUS_WORD = {:#04x}, "
364 "STATUS_MFR_SPECIFIC = {:#02x}",
365 statusWord, statusMFR)
366 .c_str());
367
368 pgoodFault++;
369 }
370 }
371 else
372 {
373 pgoodFault = 0;
374 }
375}
376
Brandon Wyman39ea02b2021-11-23 23:22:23 +0000377void PowerSupply::determineMFRFault()
378{
379 if (bindPath.string().find("ibm-cffps") != std::string::npos)
380 {
381 // IBM MFR_SPECIFIC[4] is PS_Kill fault
382 if (statusMFR & 0x10)
383 {
Brandon Wymanc2906f42021-12-21 20:14:56 +0000384 if (psKillFault < DEGLITCH_LIMIT)
385 {
386 psKillFault++;
387 }
388 }
389 else
390 {
391 psKillFault = 0;
Brandon Wyman39ea02b2021-11-23 23:22:23 +0000392 }
393 // IBM MFR_SPECIFIC[6] is 12Vcs fault.
394 if (statusMFR & 0x40)
395 {
Brandon Wymanc2906f42021-12-21 20:14:56 +0000396 if (ps12VcsFault < DEGLITCH_LIMIT)
397 {
398 ps12VcsFault++;
399 }
400 }
401 else
402 {
403 ps12VcsFault = 0;
Brandon Wyman39ea02b2021-11-23 23:22:23 +0000404 }
405 // IBM MFR_SPECIFIC[7] is 12V Current-Share fault.
406 if (statusMFR & 0x80)
407 {
Brandon Wymanc2906f42021-12-21 20:14:56 +0000408 if (psCS12VFault < DEGLITCH_LIMIT)
409 {
410 psCS12VFault++;
411 }
412 }
413 else
414 {
415 psCS12VFault = 0;
Brandon Wyman39ea02b2021-11-23 23:22:23 +0000416 }
417 }
418}
419
Brandon Wyman6c2ac392021-12-21 22:23:06 +0000420void PowerSupply::analyzeMFRFault()
421{
422 if (statusWord & phosphor::pmbus::status_word::MFR_SPECIFIC_FAULT)
423 {
Brandon Wymanc2906f42021-12-21 20:14:56 +0000424 if (mfrFault < DEGLITCH_LIMIT)
Brandon Wyman6c2ac392021-12-21 22:23:06 +0000425 {
426 log<level::ERR>(fmt::format("MFR fault: "
427 "STATUS_WORD = {:#04x} "
428 "STATUS_MFR_SPECIFIC = {:#02x}",
429 statusWord, statusMFR)
430 .c_str());
Brandon Wymanc2906f42021-12-21 20:14:56 +0000431 mfrFault++;
Brandon Wyman6c2ac392021-12-21 22:23:06 +0000432 }
433
Brandon Wyman6c2ac392021-12-21 22:23:06 +0000434 determineMFRFault();
435 }
Brandon Wymanc2906f42021-12-21 20:14:56 +0000436 else
437 {
438 mfrFault = 0;
439 }
Brandon Wyman6c2ac392021-12-21 22:23:06 +0000440}
441
Brandon Wymanf087f472021-12-22 00:04:27 +0000442void PowerSupply::analyzeVinUVFault()
443{
444 if (statusWord & phosphor::pmbus::status_word::VIN_UV_FAULT)
445 {
Brandon Wymanc2906f42021-12-21 20:14:56 +0000446 if (vinUVFault < DEGLITCH_LIMIT)
Brandon Wymanf087f472021-12-22 00:04:27 +0000447 {
448 log<level::ERR>(fmt::format("VIN_UV fault: STATUS_WORD = {:#04x}, "
449 "STATUS_MFR_SPECIFIC = {:#02x}, "
450 "STATUS_INPUT = {:#02x}",
451 statusWord, statusMFR, statusInput)
452 .c_str());
Brandon Wymanc2906f42021-12-21 20:14:56 +0000453 vinUVFault++;
Brandon Wymanf087f472021-12-22 00:04:27 +0000454 }
Brandon Wymanf087f472021-12-22 00:04:27 +0000455 }
Brandon Wyman82affd92021-11-24 19:12:49 +0000456
457 if (vinUVFault &&
458 !(statusWord & phosphor::pmbus::status_word::VIN_UV_FAULT))
459 {
460 log<level::INFO>(
461 fmt::format("VIN_UV fault cleared: STATUS_WORD = {:#04x}, "
462 "STATUS_MFR_SPECIFIC = {:#02x}, "
463 "STATUS_INPUT = {:#02x}",
464 statusWord, statusMFR, statusInput)
465 .c_str());
Brandon Wymanc2906f42021-12-21 20:14:56 +0000466 vinUVFault = 0;
Brandon Wyman82affd92021-11-24 19:12:49 +0000467 }
Brandon Wymanf087f472021-12-22 00:04:27 +0000468}
469
Brandon Wyman3f1242f2020-01-28 13:11:25 -0600470void PowerSupply::analyze()
471{
472 using namespace phosphor::pmbus;
473
B. J. Wyman681b2a32021-04-20 22:31:22 +0000474 if (presenceGPIO)
475 {
476 updatePresenceGPIO();
477 }
478
Brandon Wyman32453e92021-12-15 19:00:14 +0000479 if (present)
Brandon Wyman3f1242f2020-01-28 13:11:25 -0600480 {
481 try
482 {
Brandon Wyman32453e92021-12-15 19:00:14 +0000483 statusWord = pmbusIntf->read(STATUS_WORD, Type::Debug,
484 (readFail < LOG_LIMIT));
Brandon Wymanf65c4062020-08-19 13:15:53 -0500485 // Read worked, reset the fail count.
486 readFail = 0;
Brandon Wyman3f1242f2020-01-28 13:11:25 -0600487
488 if (statusWord)
489 {
Brandon Wymanf07bc792021-10-12 19:00:35 +0000490 statusInput = pmbusIntf->read(STATUS_INPUT, Type::Debug);
Jay Meyer10d94052020-11-30 14:41:21 -0600491 statusMFR = pmbusIntf->read(STATUS_MFR, Type::Debug);
Brandon Wyman85c7bf42021-10-19 22:28:48 +0000492 statusCML = pmbusIntf->read(STATUS_CML, Type::Debug);
Brandon Wyman6710ba22021-10-27 17:39:31 +0000493 auto status0Vout = pmbusIntf->insertPageNum(STATUS_VOUT, 0);
494 statusVout = pmbusIntf->read(status0Vout, Type::Debug);
Brandon Wymanb10b3be2021-11-09 22:12:15 +0000495 statusIout = pmbusIntf->read(STATUS_IOUT, Type::Debug);
Brandon Wyman7ee4d7e2021-11-19 20:48:23 +0000496 statusFans12 = pmbusIntf->read(STATUS_FANS_1_2, Type::Debug);
Brandon Wyman96893a42021-11-05 19:56:57 +0000497 statusTemperature =
498 pmbusIntf->read(STATUS_TEMPERATURE, Type::Debug);
Brandon Wyman9ddc6222021-10-28 17:28:01 +0000499
Brandon Wymanc2203432021-12-21 23:09:48 +0000500 analyzeCMLFault();
Brandon Wyman85c7bf42021-10-19 22:28:48 +0000501
Brandon Wymane3b0bb02021-12-21 23:16:48 +0000502 analyzeInputFault();
Brandon Wyman3f1242f2020-01-28 13:11:25 -0600503
Brandon Wymanc2c87132021-12-21 23:22:18 +0000504 analyzeVoutOVFault();
Brandon Wyman6710ba22021-10-27 17:39:31 +0000505
Brandon Wymana00e7302021-12-21 23:28:29 +0000506 analyzeIoutOCFault();
Brandon Wymanb10b3be2021-11-09 22:12:15 +0000507
Brandon Wyman08378782021-12-21 23:48:15 +0000508 analyzeVoutUVFault();
Brandon Wyman2cf46942021-10-28 19:09:16 +0000509
Brandon Wymand5d9a222021-12-21 23:59:05 +0000510 analyzeFanFault();
Brandon Wyman7ee4d7e2021-11-19 20:48:23 +0000511
Brandon Wyman52cb3f22021-12-21 23:02:47 +0000512 analyzeTemperatureFault();
Brandon Wyman96893a42021-11-05 19:56:57 +0000513
Brandon Wyman993b5542021-12-21 22:55:16 +0000514 analyzePgoodFault();
Brandon Wyman2916ea52021-11-06 03:31:18 +0000515
Brandon Wyman6c2ac392021-12-21 22:23:06 +0000516 analyzeMFRFault();
Brandon Wyman3f1242f2020-01-28 13:11:25 -0600517
Brandon Wymanf087f472021-12-22 00:04:27 +0000518 analyzeVinUVFault();
Brandon Wyman3f1242f2020-01-28 13:11:25 -0600519 }
520 else
521 {
Brandon Wymane3f7ad22021-12-21 20:27:45 +0000522 // if INPUT/VIN_UV fault was on, it cleared, trace it.
523 if (inputFault)
524 {
525 log<level::INFO>(
526 fmt::format(
527 "INPUT fault cleared: STATUS_WORD = {:#04x}",
528 statusWord)
529 .c_str());
530 }
531
532 if (vinUVFault)
533 {
534 log<level::INFO>(
535 fmt::format("VIN_UV cleared: STATUS_WORD = {:#04x}",
536 statusWord)
537 .c_str());
538 }
539
Brandon Wyman06ca4592021-12-06 22:52:23 +0000540 if (pgoodFault > 0)
Brandon Wyman4aecc292021-11-10 22:40:41 +0000541 {
542 log<level::INFO>(fmt::format("pgoodFault cleared path: {}",
543 inventoryPath)
544 .c_str());
Brandon Wyman4aecc292021-11-10 22:40:41 +0000545 }
Brandon Wymane3f7ad22021-12-21 20:27:45 +0000546
547 clearFaultFlags();
Brandon Wyman3f1242f2020-01-28 13:11:25 -0600548 }
Brandon Wyman82affd92021-11-24 19:12:49 +0000549
550 // Save off old inputVoltage value.
551 // Get latest inputVoltage.
552 // If voltage went from below minimum, and now is not, clear faults.
553 // Note: getInputVoltage() has its own try/catch.
554 int inputVoltageOld = inputVoltage;
555 double actualInputVoltage;
556 getInputVoltage(actualInputVoltage, inputVoltage);
557 if ((inputVoltageOld == in_input::VIN_VOLTAGE_0) &&
558 (inputVoltage != in_input::VIN_VOLTAGE_0))
559 {
560 log<level::INFO>(
561 fmt::format(
562 "READ_VIN back in range: inputVoltageOld = {} inputVoltage = {}",
563 inputVoltageOld, inputVoltage)
564 .c_str());
565 clearFaults();
566 }
Matt Spinler0975eaf2022-02-14 15:38:30 -0600567
568 checkAvailability();
Brandon Wyman3f1242f2020-01-28 13:11:25 -0600569 }
Patrick Williamsc1d4de52021-10-06 12:45:57 -0500570 catch (const ReadFailure& e)
Brandon Wyman3f1242f2020-01-28 13:11:25 -0600571 {
Brandon Wyman32453e92021-12-15 19:00:14 +0000572 if (readFail < SIZE_MAX)
573 {
574 readFail++;
575 }
576 if (readFail == LOG_LIMIT)
577 {
578 phosphor::logging::commit<ReadFailure>();
579 }
Brandon Wyman3f1242f2020-01-28 13:11:25 -0600580 }
581 }
582}
583
Brandon Wyman59a35792020-06-04 12:37:40 -0500584void PowerSupply::onOffConfig(uint8_t data)
585{
586 using namespace phosphor::pmbus;
587
588 if (present)
589 {
590 log<level::INFO>("ON_OFF_CONFIG write", entry("DATA=0x%02X", data));
591 try
592 {
593 std::vector<uint8_t> configData{data};
594 pmbusIntf->writeBinary(ON_OFF_CONFIG, configData,
595 Type::HwmonDeviceDebug);
596 }
597 catch (...)
598 {
599 // The underlying code in writeBinary will log a message to the
B. J. Wyman681b2a32021-04-20 22:31:22 +0000600 // journal if the write fails. If the ON_OFF_CONFIG is not setup
601 // as desired, later fault detection and analysis code should
602 // catch any of the fall out. We should not need to terminate
603 // the application if this write fails.
Brandon Wyman59a35792020-06-04 12:37:40 -0500604 }
605 }
606}
607
Brandon Wyman3c208462020-05-13 16:25:58 -0500608void PowerSupply::clearFaults()
609{
Brandon Wyman82affd92021-11-24 19:12:49 +0000610 log<level::DEBUG>(
611 fmt::format("clearFaults() inventoryPath: {}", inventoryPath).c_str());
Brandon Wyman5474c912021-02-23 14:39:43 -0600612 faultLogged = false;
Brandon Wyman3c208462020-05-13 16:25:58 -0500613 // The PMBus device driver does not allow for writing CLEAR_FAULTS
614 // directly. However, the pmbus hwmon device driver code will send a
615 // CLEAR_FAULTS after reading from any of the hwmon "files" in sysfs, so
616 // reading in1_input should result in clearing the fault bits in
617 // STATUS_BYTE/STATUS_WORD.
618 // I do not care what the return value is.
Brandon Wyman11151532020-11-10 13:45:57 -0600619 if (present)
Brandon Wyman3c208462020-05-13 16:25:58 -0500620 {
Brandon Wymane3f7ad22021-12-21 20:27:45 +0000621 clearFaultFlags();
Matt Spinler0975eaf2022-02-14 15:38:30 -0600622 checkAvailability();
Brandon Wyman9564e942020-11-10 14:01:42 -0600623 readFail = 0;
Brandon Wyman9564e942020-11-10 14:01:42 -0600624
Brandon Wyman11151532020-11-10 13:45:57 -0600625 try
626 {
627 static_cast<void>(
628 pmbusIntf->read("in1_input", phosphor::pmbus::Type::Hwmon));
629 }
Patrick Williamsc1d4de52021-10-06 12:45:57 -0500630 catch (const ReadFailure& e)
Brandon Wyman11151532020-11-10 13:45:57 -0600631 {
632 // Since I do not care what the return value is, I really do not
B. J. Wyman681b2a32021-04-20 22:31:22 +0000633 // care much if it gets a ReadFailure either. However, this
634 // should not prevent the application from continuing to run, so
635 // catching the read failure.
Brandon Wyman11151532020-11-10 13:45:57 -0600636 }
Brandon Wyman3c208462020-05-13 16:25:58 -0500637 }
638}
639
Brandon Wymanaed1f752019-11-25 18:10:52 -0600640void PowerSupply::inventoryChanged(sdbusplus::message::message& msg)
641{
642 std::string msgSensor;
Patrick Williamsabe49412020-05-13 17:59:47 -0500643 std::map<std::string, std::variant<uint32_t, bool>> msgData;
Brandon Wymanaed1f752019-11-25 18:10:52 -0600644 msg.read(msgSensor, msgData);
645
646 // Check if it was the Present property that changed.
647 auto valPropMap = msgData.find(PRESENT_PROP);
648 if (valPropMap != msgData.end())
649 {
650 if (std::get<bool>(valPropMap->second))
651 {
652 present = true;
B. J. Wyman681b2a32021-04-20 22:31:22 +0000653 // TODO: Immediately trying to read or write the "files" causes
654 // read or write failures.
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500655 using namespace std::chrono_literals;
656 std::this_thread::sleep_for(20ms);
Brandon Wyman9564e942020-11-10 14:01:42 -0600657 pmbusIntf->findHwmonDir();
Brandon Wyman59a35792020-06-04 12:37:40 -0500658 onOffConfig(phosphor::pmbus::ON_OFF_CONFIG_CONTROL_PIN_ONLY);
Brandon Wymanaed1f752019-11-25 18:10:52 -0600659 clearFaults();
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500660 updateInventory();
Brandon Wymanaed1f752019-11-25 18:10:52 -0600661 }
662 else
663 {
664 present = false;
665
666 // Clear out the now outdated inventory properties
667 updateInventory();
668 }
Matt Spinler0975eaf2022-02-14 15:38:30 -0600669 checkAvailability();
Brandon Wymanaed1f752019-11-25 18:10:52 -0600670 }
671}
672
Brandon Wyman9a507db2021-02-25 16:15:22 -0600673void PowerSupply::inventoryAdded(sdbusplus::message::message& msg)
674{
675 sdbusplus::message::object_path path;
676 msg.read(path);
677 // Make sure the signal is for the PSU inventory path
678 if (path == inventoryPath)
679 {
680 std::map<std::string, std::map<std::string, std::variant<bool>>>
681 interfaces;
682 // Get map of interfaces and their properties
683 msg.read(interfaces);
684
685 auto properties = interfaces.find(INVENTORY_IFACE);
686 if (properties != interfaces.end())
687 {
688 auto property = properties->second.find(PRESENT_PROP);
689 if (property != properties->second.end())
690 {
691 present = std::get<bool>(property->second);
692
693 log<level::INFO>(fmt::format("Power Supply {} Present {}",
694 inventoryPath, present)
695 .c_str());
696
697 updateInventory();
Matt Spinler0975eaf2022-02-14 15:38:30 -0600698 checkAvailability();
Brandon Wyman9a507db2021-02-25 16:15:22 -0600699 }
700 }
701 }
702}
703
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500704void PowerSupply::updateInventory()
705{
706 using namespace phosphor::pmbus;
707
Chanh Nguyenc12c53b2021-04-06 17:24:47 +0700708#if IBM_VPD
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500709 std::string ccin;
710 std::string pn;
711 std::string fn;
712 std::string header;
713 std::string sn;
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500714 using PropertyMap =
George Liu070c1bc2020-10-12 11:28:01 +0800715 std::map<std::string,
716 std::variant<std::string, std::vector<uint8_t>, bool>>;
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500717 PropertyMap assetProps;
George Liu070c1bc2020-10-12 11:28:01 +0800718 PropertyMap operProps;
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500719 PropertyMap versionProps;
720 PropertyMap ipzvpdDINFProps;
721 PropertyMap ipzvpdVINIProps;
722 using InterfaceMap = std::map<std::string, PropertyMap>;
723 InterfaceMap interfaces;
724 using ObjectMap = std::map<sdbusplus::message::object_path, InterfaceMap>;
725 ObjectMap object;
726#endif
B. J. Wyman681b2a32021-04-20 22:31:22 +0000727 log<level::DEBUG>(
728 fmt::format("updateInventory() inventoryPath: {}", inventoryPath)
729 .c_str());
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500730
731 if (present)
732 {
733 // TODO: non-IBM inventory updates?
734
Chanh Nguyenc12c53b2021-04-06 17:24:47 +0700735#if IBM_VPD
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500736 try
737 {
738 ccin = pmbusIntf->readString(CCIN, Type::HwmonDeviceDebug);
739 assetProps.emplace(MODEL_PROP, ccin);
Adriana Kobylak572a9052021-03-30 15:58:07 +0000740 modelName = ccin;
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500741 }
Patrick Williamsc1d4de52021-10-06 12:45:57 -0500742 catch (const ReadFailure& e)
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500743 {
B. J. Wyman681b2a32021-04-20 22:31:22 +0000744 // Ignore the read failure, let pmbus code indicate failure,
745 // path...
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500746 // TODO - ibm918
747 // https://github.com/openbmc/docs/blob/master/designs/vpd-collection.md
748 // The BMC must log errors if any of the VPD cannot be properly
749 // parsed or fails ECC checks.
750 }
751
752 try
753 {
754 pn = pmbusIntf->readString(PART_NUMBER, Type::HwmonDeviceDebug);
755 assetProps.emplace(PN_PROP, pn);
756 }
Patrick Williamsc1d4de52021-10-06 12:45:57 -0500757 catch (const ReadFailure& e)
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500758 {
B. J. Wyman681b2a32021-04-20 22:31:22 +0000759 // Ignore the read failure, let pmbus code indicate failure,
760 // path...
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500761 }
762
763 try
764 {
765 fn = pmbusIntf->readString(FRU_NUMBER, Type::HwmonDeviceDebug);
Brandon Wymana169b0f2021-12-07 20:18:06 +0000766 assetProps.emplace(SPARE_PN_PROP, fn);
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500767 }
Patrick Williamsc1d4de52021-10-06 12:45:57 -0500768 catch (const ReadFailure& e)
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500769 {
B. J. Wyman681b2a32021-04-20 22:31:22 +0000770 // Ignore the read failure, let pmbus code indicate failure,
771 // path...
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500772 }
773
774 try
775 {
776 header =
777 pmbusIntf->readString(SERIAL_HEADER, Type::HwmonDeviceDebug);
778 sn = pmbusIntf->readString(SERIAL_NUMBER, Type::HwmonDeviceDebug);
779 assetProps.emplace(SN_PROP, sn);
780 }
Patrick Williamsc1d4de52021-10-06 12:45:57 -0500781 catch (const ReadFailure& e)
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500782 {
B. J. Wyman681b2a32021-04-20 22:31:22 +0000783 // Ignore the read failure, let pmbus code indicate failure,
784 // path...
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500785 }
786
787 try
788 {
Brandon Wymanc9efe412020-10-09 15:42:50 -0500789 fwVersion =
790 pmbusIntf->readString(FW_VERSION, Type::HwmonDeviceDebug);
791 versionProps.emplace(VERSION_PROP, fwVersion);
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500792 }
Patrick Williamsc1d4de52021-10-06 12:45:57 -0500793 catch (const ReadFailure& e)
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500794 {
B. J. Wyman681b2a32021-04-20 22:31:22 +0000795 // Ignore the read failure, let pmbus code indicate failure,
796 // path...
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500797 }
798
799 ipzvpdVINIProps.emplace("CC",
800 std::vector<uint8_t>(ccin.begin(), ccin.end()));
801 ipzvpdVINIProps.emplace("PN",
802 std::vector<uint8_t>(pn.begin(), pn.end()));
803 ipzvpdVINIProps.emplace("FN",
804 std::vector<uint8_t>(fn.begin(), fn.end()));
805 std::string header_sn = header + sn + '\0';
806 ipzvpdVINIProps.emplace(
807 "SN", std::vector<uint8_t>(header_sn.begin(), header_sn.end()));
808 std::string description = "IBM PS";
809 ipzvpdVINIProps.emplace(
810 "DR", std::vector<uint8_t>(description.begin(), description.end()));
811
Ben Tynerf8d8c462022-01-27 16:09:45 -0600812 // Populate the VINI Resource Type (RT) keyword
813 ipzvpdVINIProps.emplace("RT", std::vector<uint8_t>{'V', 'I', 'N', 'I'});
814
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500815 // Update the Resource Identifier (RI) keyword
816 // 2 byte FRC: 0x0003
817 // 2 byte RID: 0x1000, 0x1001...
818 std::uint8_t num = std::stoul(
819 inventoryPath.substr(inventoryPath.size() - 1, 1), nullptr, 0);
820 std::vector<uint8_t> ri{0x00, 0x03, 0x10, num};
821 ipzvpdDINFProps.emplace("RI", ri);
822
823 // Fill in the FRU Label (FL) keyword.
824 std::string fl = "E";
825 fl.push_back(inventoryPath.back());
826 fl.resize(FL_KW_SIZE, ' ');
827 ipzvpdDINFProps.emplace("FL",
828 std::vector<uint8_t>(fl.begin(), fl.end()));
829
Ben Tynerf8d8c462022-01-27 16:09:45 -0600830 // Populate the DINF Resource Type (RT) keyword
831 ipzvpdDINFProps.emplace("RT", std::vector<uint8_t>{'D', 'I', 'N', 'F'});
832
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500833 interfaces.emplace(ASSET_IFACE, std::move(assetProps));
834 interfaces.emplace(VERSION_IFACE, std::move(versionProps));
835 interfaces.emplace(DINF_IFACE, std::move(ipzvpdDINFProps));
836 interfaces.emplace(VINI_IFACE, std::move(ipzvpdVINIProps));
837
George Liu070c1bc2020-10-12 11:28:01 +0800838 // Update the Functional
839 operProps.emplace(FUNCTIONAL_PROP, present);
840 interfaces.emplace(OPERATIONAL_STATE_IFACE, std::move(operProps));
841
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500842 auto path = inventoryPath.substr(strlen(INVENTORY_OBJ_PATH));
843 object.emplace(path, std::move(interfaces));
844
845 try
846 {
847 auto service =
848 util::getService(INVENTORY_OBJ_PATH, INVENTORY_MGR_IFACE, bus);
849
850 if (service.empty())
851 {
852 log<level::ERR>("Unable to get inventory manager service");
853 return;
854 }
855
856 auto method =
857 bus.new_method_call(service.c_str(), INVENTORY_OBJ_PATH,
858 INVENTORY_MGR_IFACE, "Notify");
859
860 method.append(std::move(object));
861
862 auto reply = bus.call(method);
863 }
Patrick Williamsc1d4de52021-10-06 12:45:57 -0500864 catch (const std::exception& e)
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500865 {
Jay Meyer6a3fd2c2020-08-25 16:37:16 -0500866 log<level::ERR>(
867 std::string(e.what() + std::string(" PATH=") + inventoryPath)
868 .c_str());
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500869 }
870#endif
871 }
872}
873
Adriana Kobylak4175ffb2021-08-02 14:51:05 +0000874void PowerSupply::getInputVoltage(double& actualInputVoltage,
875 int& inputVoltage) const
876{
877 using namespace phosphor::pmbus;
878
879 actualInputVoltage = in_input::VIN_VOLTAGE_0;
880 inputVoltage = in_input::VIN_VOLTAGE_0;
881
882 if (present)
883 {
884 try
885 {
886 // Read input voltage in millivolts
887 auto inputVoltageStr = pmbusIntf->readString(READ_VIN, Type::Hwmon);
888
889 // Convert to volts
890 actualInputVoltage = std::stod(inputVoltageStr) / 1000;
891
892 // Calculate the voltage based on voltage thresholds
893 if (actualInputVoltage < in_input::VIN_VOLTAGE_MIN)
894 {
895 inputVoltage = in_input::VIN_VOLTAGE_0;
896 }
897 else if (actualInputVoltage < in_input::VIN_VOLTAGE_110_THRESHOLD)
898 {
899 inputVoltage = in_input::VIN_VOLTAGE_110;
900 }
901 else
902 {
903 inputVoltage = in_input::VIN_VOLTAGE_220;
904 }
905 }
906 catch (const std::exception& e)
907 {
908 log<level::ERR>(
909 fmt::format("READ_VIN read error: {}", e.what()).c_str());
910 }
911 }
912}
913
Matt Spinler0975eaf2022-02-14 15:38:30 -0600914void PowerSupply::checkAvailability()
915{
916 bool origAvailability = available;
917 available = present && !hasInputFault() && !hasVINUVFault() &&
918 !hasPSKillFault() && !hasIoutOCFault();
919
920 if (origAvailability != available)
921 {
922 auto invpath = inventoryPath.substr(strlen(INVENTORY_OBJ_PATH));
923 phosphor::power::psu::setAvailable(bus, invpath, available);
924 }
925}
926
Brandon Wyman3f1242f2020-01-28 13:11:25 -0600927} // namespace phosphor::power::psu