blob: 864ae0ec641b3dd6210b6aa395e89ff52020f281 [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());
Matt Spinlerca1e9ea2022-02-18 14:03:08 -0600171
172 auto invpath = inventoryPath.substr(strlen(INVENTORY_OBJ_PATH));
173 auto const lastSlashPos = invpath.find_last_of('/');
174 std::string prettyName = invpath.substr(lastSlashPos + 1);
175 setPresence(bus, invpath, present, prettyName);
176 updateInventory();
177
178 // Need Functional to already be correct before calling this
179 checkAvailability();
180
B. J. Wyman681b2a32021-04-20 22:31:22 +0000181 if (present)
182 {
183 std::this_thread::sleep_for(std::chrono::milliseconds(bindDelay));
184 bindOrUnbindDriver(present);
185 pmbusIntf->findHwmonDir();
186 onOffConfig(phosphor::pmbus::ON_OFF_CONFIG_CONTROL_PIN_ONLY);
187 clearFaults();
188 }
189 else
190 {
191 bindOrUnbindDriver(present);
192 }
B. J. Wyman681b2a32021-04-20 22:31:22 +0000193 }
194}
195
Brandon Wymanc2203432021-12-21 23:09:48 +0000196void PowerSupply::analyzeCMLFault()
197{
198 if (statusWord & phosphor::pmbus::status_word::CML_FAULT)
199 {
Brandon Wymanc2906f42021-12-21 20:14:56 +0000200 if (cmlFault < DEGLITCH_LIMIT)
Brandon Wymanc2203432021-12-21 23:09:48 +0000201 {
Brandon Wyman9e292ee2022-03-10 22:56:23 +0000202 if (statusWord != statusWordOld)
203 {
204 log<level::ERR>(fmt::format("CML fault: STATUS_WORD = {:#06x}, "
205 "STATUS_CML = {:#02x}",
206 statusWord, statusCML)
207 .c_str());
208 }
Brandon Wymanc2906f42021-12-21 20:14:56 +0000209 cmlFault++;
210 }
211 }
212 else
213 {
214 cmlFault = 0;
Brandon Wymanc2203432021-12-21 23:09:48 +0000215 }
216}
217
Brandon Wymane3b0bb02021-12-21 23:16:48 +0000218void PowerSupply::analyzeInputFault()
219{
220 if (statusWord & phosphor::pmbus::status_word::INPUT_FAULT_WARN)
221 {
Brandon Wymanc2906f42021-12-21 20:14:56 +0000222 if (inputFault < DEGLITCH_LIMIT)
Brandon Wymane3b0bb02021-12-21 23:16:48 +0000223 {
Brandon Wyman9e292ee2022-03-10 22:56:23 +0000224 if (statusWord != statusWordOld)
225 {
226 log<level::ERR>(
227 fmt::format("INPUT fault: STATUS_WORD = {:#06x}, "
228 "STATUS_MFR_SPECIFIC = {:#04x}, "
229 "STATUS_INPUT = {:#04x}",
230 statusWord, statusMFR, statusInput)
231 .c_str());
232 }
Brandon Wymanc2906f42021-12-21 20:14:56 +0000233 inputFault++;
234 }
Brandon Wymane3b0bb02021-12-21 23:16:48 +0000235 }
Brandon Wyman82affd92021-11-24 19:12:49 +0000236
237 // If had INPUT/VIN_UV fault, and now off.
238 // Trace that odd behavior.
239 if (inputFault &&
240 !(statusWord & phosphor::pmbus::status_word::INPUT_FAULT_WARN))
241 {
242 log<level::INFO>(
Brandon Wyman6f939a32022-03-10 18:42:20 +0000243 fmt::format("INPUT fault cleared: STATUS_WORD = {:#06x}, "
244 "STATUS_MFR_SPECIFIC = {:#04x}, "
245 "STATUS_INPUT = {:#04x}",
Brandon Wyman82affd92021-11-24 19:12:49 +0000246 statusWord, statusMFR, statusInput)
247 .c_str());
Brandon Wymanc2906f42021-12-21 20:14:56 +0000248 inputFault = 0;
Brandon Wyman82affd92021-11-24 19:12:49 +0000249 }
Brandon Wymane3b0bb02021-12-21 23:16:48 +0000250}
251
Brandon Wymanc2c87132021-12-21 23:22:18 +0000252void PowerSupply::analyzeVoutOVFault()
253{
254 if (statusWord & phosphor::pmbus::status_word::VOUT_OV_FAULT)
255 {
Brandon Wymanc2906f42021-12-21 20:14:56 +0000256 if (voutOVFault < DEGLITCH_LIMIT)
Brandon Wymanc2c87132021-12-21 23:22:18 +0000257 {
Brandon Wyman9e292ee2022-03-10 22:56:23 +0000258 if (statusWord != statusWordOld)
259 {
260 log<level::ERR>(
261 fmt::format("VOUT_OV_FAULT fault: STATUS_WORD = {:#06x}, "
262 "STATUS_MFR_SPECIFIC = {:#04x}, "
263 "STATUS_VOUT = {:#02x}",
264 statusWord, statusMFR, statusVout)
265 .c_str());
266 }
Brandon Wymanc2c87132021-12-21 23:22:18 +0000267
Brandon Wymanc2906f42021-12-21 20:14:56 +0000268 voutOVFault++;
269 }
270 }
271 else
272 {
273 voutOVFault = 0;
Brandon Wymanc2c87132021-12-21 23:22:18 +0000274 }
275}
276
Brandon Wymana00e7302021-12-21 23:28:29 +0000277void PowerSupply::analyzeIoutOCFault()
278{
279 if (statusWord & phosphor::pmbus::status_word::IOUT_OC_FAULT)
280 {
Brandon Wymanc2906f42021-12-21 20:14:56 +0000281 if (ioutOCFault < DEGLITCH_LIMIT)
Brandon Wymana00e7302021-12-21 23:28:29 +0000282 {
Brandon Wyman9e292ee2022-03-10 22:56:23 +0000283 if (statusWord != statusWordOld)
284 {
285 log<level::ERR>(
286 fmt::format("IOUT fault: STATUS_WORD = {:#06x}, "
287 "STATUS_MFR_SPECIFIC = {:#04x}, "
288 "STATUS_IOUT = {:#04x}",
289 statusWord, statusMFR, statusIout)
290 .c_str());
291 }
Brandon Wymana00e7302021-12-21 23:28:29 +0000292
Brandon Wymanc2906f42021-12-21 20:14:56 +0000293 ioutOCFault++;
294 }
295 }
296 else
297 {
298 ioutOCFault = 0;
Brandon Wymana00e7302021-12-21 23:28:29 +0000299 }
300}
301
Brandon Wyman08378782021-12-21 23:48:15 +0000302void PowerSupply::analyzeVoutUVFault()
303{
304 if ((statusWord & phosphor::pmbus::status_word::VOUT_FAULT) &&
305 !(statusWord & phosphor::pmbus::status_word::VOUT_OV_FAULT))
306 {
Brandon Wymanc2906f42021-12-21 20:14:56 +0000307 if (voutUVFault < DEGLITCH_LIMIT)
Brandon Wyman08378782021-12-21 23:48:15 +0000308 {
Brandon Wyman9e292ee2022-03-10 22:56:23 +0000309 if (statusWord != statusWordOld)
310 {
311 log<level::ERR>(
312 fmt::format("VOUT_UV_FAULT fault: STATUS_WORD = {:#06x}, "
313 "STATUS_MFR_SPECIFIC = {:#04x}, "
314 "STATUS_VOUT = {:#04x}",
315 statusWord, statusMFR, statusVout)
316 .c_str());
317 }
Brandon Wymanc2906f42021-12-21 20:14:56 +0000318 voutUVFault++;
319 }
320 }
321 else
322 {
323 voutUVFault = 0;
Brandon Wyman08378782021-12-21 23:48:15 +0000324 }
325}
326
Brandon Wymand5d9a222021-12-21 23:59:05 +0000327void PowerSupply::analyzeFanFault()
328{
329 if (statusWord & phosphor::pmbus::status_word::FAN_FAULT)
330 {
Brandon Wymanc2906f42021-12-21 20:14:56 +0000331 if (fanFault < DEGLITCH_LIMIT)
Brandon Wymand5d9a222021-12-21 23:59:05 +0000332 {
Brandon Wyman9e292ee2022-03-10 22:56:23 +0000333 if (statusWord != statusWordOld)
334 {
335 log<level::ERR>(fmt::format("FANS fault/warning: "
336 "STATUS_WORD = {:#06x}, "
337 "STATUS_MFR_SPECIFIC = {:#04x}, "
338 "STATUS_FANS_1_2 = {:#04x}",
339 statusWord, statusMFR, statusFans12)
340 .c_str());
341 }
Brandon Wymanc2906f42021-12-21 20:14:56 +0000342 fanFault++;
343 }
344 }
345 else
346 {
347 fanFault = 0;
Brandon Wymand5d9a222021-12-21 23:59:05 +0000348 }
349}
350
Brandon Wyman52cb3f22021-12-21 23:02:47 +0000351void PowerSupply::analyzeTemperatureFault()
352{
353 if (statusWord & phosphor::pmbus::status_word::TEMPERATURE_FAULT_WARN)
354 {
Brandon Wymanc2906f42021-12-21 20:14:56 +0000355 if (tempFault < DEGLITCH_LIMIT)
Brandon Wyman52cb3f22021-12-21 23:02:47 +0000356 {
Brandon Wyman9e292ee2022-03-10 22:56:23 +0000357 if (statusWord != statusWordOld)
358 {
359 log<level::ERR>(fmt::format("TEMPERATURE fault/warning: "
360 "STATUS_WORD = {:#06x}, "
361 "STATUS_MFR_SPECIFIC = {:#04x}, "
362 "STATUS_TEMPERATURE = {:#04x}",
363 statusWord, statusMFR,
364 statusTemperature)
365 .c_str());
366 }
Brandon Wymanc2906f42021-12-21 20:14:56 +0000367 tempFault++;
368 }
369 }
370 else
371 {
372 tempFault = 0;
Brandon Wyman52cb3f22021-12-21 23:02:47 +0000373 }
374}
375
Brandon Wyman993b5542021-12-21 22:55:16 +0000376void PowerSupply::analyzePgoodFault()
377{
378 if ((statusWord & phosphor::pmbus::status_word::POWER_GOOD_NEGATED) ||
379 (statusWord & phosphor::pmbus::status_word::UNIT_IS_OFF))
380 {
381 if (pgoodFault < DEGLITCH_LIMIT)
382 {
Brandon Wyman9e292ee2022-03-10 22:56:23 +0000383 if (statusWord != statusWordOld)
384 {
385 log<level::ERR>(fmt::format("PGOOD fault: "
386 "STATUS_WORD = {:#06x}, "
387 "STATUS_MFR_SPECIFIC = {:#04x}",
388 statusWord, statusMFR)
389 .c_str());
390 }
Brandon Wyman993b5542021-12-21 22:55:16 +0000391 pgoodFault++;
392 }
393 }
394 else
395 {
396 pgoodFault = 0;
397 }
398}
399
Brandon Wyman39ea02b2021-11-23 23:22:23 +0000400void PowerSupply::determineMFRFault()
401{
402 if (bindPath.string().find("ibm-cffps") != std::string::npos)
403 {
404 // IBM MFR_SPECIFIC[4] is PS_Kill fault
405 if (statusMFR & 0x10)
406 {
Brandon Wymanc2906f42021-12-21 20:14:56 +0000407 if (psKillFault < DEGLITCH_LIMIT)
408 {
409 psKillFault++;
410 }
411 }
412 else
413 {
414 psKillFault = 0;
Brandon Wyman39ea02b2021-11-23 23:22:23 +0000415 }
416 // IBM MFR_SPECIFIC[6] is 12Vcs fault.
417 if (statusMFR & 0x40)
418 {
Brandon Wymanc2906f42021-12-21 20:14:56 +0000419 if (ps12VcsFault < DEGLITCH_LIMIT)
420 {
421 ps12VcsFault++;
422 }
423 }
424 else
425 {
426 ps12VcsFault = 0;
Brandon Wyman39ea02b2021-11-23 23:22:23 +0000427 }
428 // IBM MFR_SPECIFIC[7] is 12V Current-Share fault.
429 if (statusMFR & 0x80)
430 {
Brandon Wymanc2906f42021-12-21 20:14:56 +0000431 if (psCS12VFault < DEGLITCH_LIMIT)
432 {
433 psCS12VFault++;
434 }
435 }
436 else
437 {
438 psCS12VFault = 0;
Brandon Wyman39ea02b2021-11-23 23:22:23 +0000439 }
440 }
441}
442
Brandon Wyman6c2ac392021-12-21 22:23:06 +0000443void PowerSupply::analyzeMFRFault()
444{
445 if (statusWord & phosphor::pmbus::status_word::MFR_SPECIFIC_FAULT)
446 {
Brandon Wymanc2906f42021-12-21 20:14:56 +0000447 if (mfrFault < DEGLITCH_LIMIT)
Brandon Wyman6c2ac392021-12-21 22:23:06 +0000448 {
Brandon Wyman9e292ee2022-03-10 22:56:23 +0000449 if (statusWord != statusWordOld)
450 {
451 log<level::ERR>(fmt::format("MFR fault: "
452 "STATUS_WORD = {:#06x} "
453 "STATUS_MFR_SPECIFIC = {:#04x}",
454 statusWord, statusMFR)
455 .c_str());
456 }
Brandon Wymanc2906f42021-12-21 20:14:56 +0000457 mfrFault++;
Brandon Wyman6c2ac392021-12-21 22:23:06 +0000458 }
459
Brandon Wyman6c2ac392021-12-21 22:23:06 +0000460 determineMFRFault();
461 }
Brandon Wymanc2906f42021-12-21 20:14:56 +0000462 else
463 {
464 mfrFault = 0;
465 }
Brandon Wyman6c2ac392021-12-21 22:23:06 +0000466}
467
Brandon Wymanf087f472021-12-22 00:04:27 +0000468void PowerSupply::analyzeVinUVFault()
469{
470 if (statusWord & phosphor::pmbus::status_word::VIN_UV_FAULT)
471 {
Brandon Wymanc2906f42021-12-21 20:14:56 +0000472 if (vinUVFault < DEGLITCH_LIMIT)
Brandon Wymanf087f472021-12-22 00:04:27 +0000473 {
Brandon Wyman9e292ee2022-03-10 22:56:23 +0000474 if (statusWord != statusWordOld)
475 {
476 log<level::ERR>(
477 fmt::format("VIN_UV fault: STATUS_WORD = {:#06x}, "
478 "STATUS_MFR_SPECIFIC = {:#04x}, "
479 "STATUS_INPUT = {:#04x}",
480 statusWord, statusMFR, statusInput)
481 .c_str());
482 }
Brandon Wymanc2906f42021-12-21 20:14:56 +0000483 vinUVFault++;
Brandon Wymanf087f472021-12-22 00:04:27 +0000484 }
Brandon Wymanf087f472021-12-22 00:04:27 +0000485 }
Brandon Wyman82affd92021-11-24 19:12:49 +0000486
487 if (vinUVFault &&
488 !(statusWord & phosphor::pmbus::status_word::VIN_UV_FAULT))
489 {
490 log<level::INFO>(
Brandon Wyman6f939a32022-03-10 18:42:20 +0000491 fmt::format("VIN_UV fault cleared: STATUS_WORD = {:#06x}, "
492 "STATUS_MFR_SPECIFIC = {:#04x}, "
493 "STATUS_INPUT = {:#04x}",
Brandon Wyman82affd92021-11-24 19:12:49 +0000494 statusWord, statusMFR, statusInput)
495 .c_str());
Brandon Wymanc2906f42021-12-21 20:14:56 +0000496 vinUVFault = 0;
Brandon Wyman82affd92021-11-24 19:12:49 +0000497 }
Brandon Wymanf087f472021-12-22 00:04:27 +0000498}
499
Brandon Wyman3f1242f2020-01-28 13:11:25 -0600500void PowerSupply::analyze()
501{
502 using namespace phosphor::pmbus;
503
B. J. Wyman681b2a32021-04-20 22:31:22 +0000504 if (presenceGPIO)
505 {
506 updatePresenceGPIO();
507 }
508
Brandon Wyman32453e92021-12-15 19:00:14 +0000509 if (present)
Brandon Wyman3f1242f2020-01-28 13:11:25 -0600510 {
511 try
512 {
Brandon Wyman9e292ee2022-03-10 22:56:23 +0000513 statusWordOld = statusWord;
Brandon Wyman32453e92021-12-15 19:00:14 +0000514 statusWord = pmbusIntf->read(STATUS_WORD, Type::Debug,
515 (readFail < LOG_LIMIT));
Brandon Wymanf65c4062020-08-19 13:15:53 -0500516 // Read worked, reset the fail count.
517 readFail = 0;
Brandon Wyman3f1242f2020-01-28 13:11:25 -0600518
519 if (statusWord)
520 {
Brandon Wymanf07bc792021-10-12 19:00:35 +0000521 statusInput = pmbusIntf->read(STATUS_INPUT, Type::Debug);
Jay Meyer10d94052020-11-30 14:41:21 -0600522 statusMFR = pmbusIntf->read(STATUS_MFR, Type::Debug);
Brandon Wyman85c7bf42021-10-19 22:28:48 +0000523 statusCML = pmbusIntf->read(STATUS_CML, Type::Debug);
Brandon Wyman6710ba22021-10-27 17:39:31 +0000524 auto status0Vout = pmbusIntf->insertPageNum(STATUS_VOUT, 0);
525 statusVout = pmbusIntf->read(status0Vout, Type::Debug);
Brandon Wymanb10b3be2021-11-09 22:12:15 +0000526 statusIout = pmbusIntf->read(STATUS_IOUT, Type::Debug);
Brandon Wyman7ee4d7e2021-11-19 20:48:23 +0000527 statusFans12 = pmbusIntf->read(STATUS_FANS_1_2, Type::Debug);
Brandon Wyman96893a42021-11-05 19:56:57 +0000528 statusTemperature =
529 pmbusIntf->read(STATUS_TEMPERATURE, Type::Debug);
Brandon Wyman9ddc6222021-10-28 17:28:01 +0000530
Brandon Wymanc2203432021-12-21 23:09:48 +0000531 analyzeCMLFault();
Brandon Wyman85c7bf42021-10-19 22:28:48 +0000532
Brandon Wymane3b0bb02021-12-21 23:16:48 +0000533 analyzeInputFault();
Brandon Wyman3f1242f2020-01-28 13:11:25 -0600534
Brandon Wymanc2c87132021-12-21 23:22:18 +0000535 analyzeVoutOVFault();
Brandon Wyman6710ba22021-10-27 17:39:31 +0000536
Brandon Wymana00e7302021-12-21 23:28:29 +0000537 analyzeIoutOCFault();
Brandon Wymanb10b3be2021-11-09 22:12:15 +0000538
Brandon Wyman08378782021-12-21 23:48:15 +0000539 analyzeVoutUVFault();
Brandon Wyman2cf46942021-10-28 19:09:16 +0000540
Brandon Wymand5d9a222021-12-21 23:59:05 +0000541 analyzeFanFault();
Brandon Wyman7ee4d7e2021-11-19 20:48:23 +0000542
Brandon Wyman52cb3f22021-12-21 23:02:47 +0000543 analyzeTemperatureFault();
Brandon Wyman96893a42021-11-05 19:56:57 +0000544
Brandon Wyman993b5542021-12-21 22:55:16 +0000545 analyzePgoodFault();
Brandon Wyman2916ea52021-11-06 03:31:18 +0000546
Brandon Wyman6c2ac392021-12-21 22:23:06 +0000547 analyzeMFRFault();
Brandon Wyman3f1242f2020-01-28 13:11:25 -0600548
Brandon Wymanf087f472021-12-22 00:04:27 +0000549 analyzeVinUVFault();
Brandon Wyman3f1242f2020-01-28 13:11:25 -0600550 }
551 else
552 {
Brandon Wyman9e292ee2022-03-10 22:56:23 +0000553 if (statusWord != statusWordOld)
554 {
555 log<level::INFO>(fmt::format("STATUS_WORD = {:#06x} {}",
556 statusWord, inventoryPath)
557 .c_str());
558 }
559
Brandon Wymane3f7ad22021-12-21 20:27:45 +0000560 // if INPUT/VIN_UV fault was on, it cleared, trace it.
561 if (inputFault)
562 {
563 log<level::INFO>(
564 fmt::format(
Brandon Wyman6f939a32022-03-10 18:42:20 +0000565 "INPUT fault cleared: STATUS_WORD = {:#06x}",
Brandon Wymane3f7ad22021-12-21 20:27:45 +0000566 statusWord)
567 .c_str());
568 }
569
570 if (vinUVFault)
571 {
572 log<level::INFO>(
Brandon Wyman6f939a32022-03-10 18:42:20 +0000573 fmt::format("VIN_UV cleared: STATUS_WORD = {:#06x}",
Brandon Wymane3f7ad22021-12-21 20:27:45 +0000574 statusWord)
575 .c_str());
576 }
577
Brandon Wyman06ca4592021-12-06 22:52:23 +0000578 if (pgoodFault > 0)
Brandon Wyman4aecc292021-11-10 22:40:41 +0000579 {
580 log<level::INFO>(fmt::format("pgoodFault cleared path: {}",
581 inventoryPath)
582 .c_str());
Brandon Wyman4aecc292021-11-10 22:40:41 +0000583 }
Brandon Wymane3f7ad22021-12-21 20:27:45 +0000584
585 clearFaultFlags();
Brandon Wyman3f1242f2020-01-28 13:11:25 -0600586 }
Brandon Wyman82affd92021-11-24 19:12:49 +0000587
588 // Save off old inputVoltage value.
589 // Get latest inputVoltage.
590 // If voltage went from below minimum, and now is not, clear faults.
591 // Note: getInputVoltage() has its own try/catch.
592 int inputVoltageOld = inputVoltage;
593 double actualInputVoltage;
594 getInputVoltage(actualInputVoltage, inputVoltage);
595 if ((inputVoltageOld == in_input::VIN_VOLTAGE_0) &&
596 (inputVoltage != in_input::VIN_VOLTAGE_0))
597 {
598 log<level::INFO>(
599 fmt::format(
600 "READ_VIN back in range: inputVoltageOld = {} inputVoltage = {}",
601 inputVoltageOld, inputVoltage)
602 .c_str());
603 clearFaults();
604 }
Matt Spinler0975eaf2022-02-14 15:38:30 -0600605
606 checkAvailability();
Brandon Wyman3f1242f2020-01-28 13:11:25 -0600607 }
Patrick Williamsc1d4de52021-10-06 12:45:57 -0500608 catch (const ReadFailure& e)
Brandon Wyman3f1242f2020-01-28 13:11:25 -0600609 {
Brandon Wyman32453e92021-12-15 19:00:14 +0000610 if (readFail < SIZE_MAX)
611 {
612 readFail++;
613 }
614 if (readFail == LOG_LIMIT)
615 {
616 phosphor::logging::commit<ReadFailure>();
617 }
Brandon Wyman3f1242f2020-01-28 13:11:25 -0600618 }
619 }
620}
621
Brandon Wyman59a35792020-06-04 12:37:40 -0500622void PowerSupply::onOffConfig(uint8_t data)
623{
624 using namespace phosphor::pmbus;
625
626 if (present)
627 {
628 log<level::INFO>("ON_OFF_CONFIG write", entry("DATA=0x%02X", data));
629 try
630 {
631 std::vector<uint8_t> configData{data};
632 pmbusIntf->writeBinary(ON_OFF_CONFIG, configData,
633 Type::HwmonDeviceDebug);
634 }
635 catch (...)
636 {
637 // The underlying code in writeBinary will log a message to the
B. J. Wyman681b2a32021-04-20 22:31:22 +0000638 // journal if the write fails. If the ON_OFF_CONFIG is not setup
639 // as desired, later fault detection and analysis code should
640 // catch any of the fall out. We should not need to terminate
641 // the application if this write fails.
Brandon Wyman59a35792020-06-04 12:37:40 -0500642 }
643 }
644}
645
Brandon Wyman3c208462020-05-13 16:25:58 -0500646void PowerSupply::clearFaults()
647{
Brandon Wyman82affd92021-11-24 19:12:49 +0000648 log<level::DEBUG>(
649 fmt::format("clearFaults() inventoryPath: {}", inventoryPath).c_str());
Brandon Wyman5474c912021-02-23 14:39:43 -0600650 faultLogged = false;
Brandon Wyman3c208462020-05-13 16:25:58 -0500651 // The PMBus device driver does not allow for writing CLEAR_FAULTS
652 // directly. However, the pmbus hwmon device driver code will send a
653 // CLEAR_FAULTS after reading from any of the hwmon "files" in sysfs, so
654 // reading in1_input should result in clearing the fault bits in
655 // STATUS_BYTE/STATUS_WORD.
656 // I do not care what the return value is.
Brandon Wyman11151532020-11-10 13:45:57 -0600657 if (present)
Brandon Wyman3c208462020-05-13 16:25:58 -0500658 {
Brandon Wymane3f7ad22021-12-21 20:27:45 +0000659 clearFaultFlags();
Matt Spinler0975eaf2022-02-14 15:38:30 -0600660 checkAvailability();
Brandon Wyman9564e942020-11-10 14:01:42 -0600661 readFail = 0;
Brandon Wyman9564e942020-11-10 14:01:42 -0600662
Brandon Wyman11151532020-11-10 13:45:57 -0600663 try
664 {
665 static_cast<void>(
666 pmbusIntf->read("in1_input", phosphor::pmbus::Type::Hwmon));
667 }
Patrick Williamsc1d4de52021-10-06 12:45:57 -0500668 catch (const ReadFailure& e)
Brandon Wyman11151532020-11-10 13:45:57 -0600669 {
670 // Since I do not care what the return value is, I really do not
B. J. Wyman681b2a32021-04-20 22:31:22 +0000671 // care much if it gets a ReadFailure either. However, this
672 // should not prevent the application from continuing to run, so
673 // catching the read failure.
Brandon Wyman11151532020-11-10 13:45:57 -0600674 }
Brandon Wyman3c208462020-05-13 16:25:58 -0500675 }
676}
677
Brandon Wymanaed1f752019-11-25 18:10:52 -0600678void PowerSupply::inventoryChanged(sdbusplus::message::message& msg)
679{
680 std::string msgSensor;
Patrick Williamsabe49412020-05-13 17:59:47 -0500681 std::map<std::string, std::variant<uint32_t, bool>> msgData;
Brandon Wymanaed1f752019-11-25 18:10:52 -0600682 msg.read(msgSensor, msgData);
683
684 // Check if it was the Present property that changed.
685 auto valPropMap = msgData.find(PRESENT_PROP);
686 if (valPropMap != msgData.end())
687 {
688 if (std::get<bool>(valPropMap->second))
689 {
690 present = true;
B. J. Wyman681b2a32021-04-20 22:31:22 +0000691 // TODO: Immediately trying to read or write the "files" causes
692 // read or write failures.
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500693 using namespace std::chrono_literals;
694 std::this_thread::sleep_for(20ms);
Brandon Wyman9564e942020-11-10 14:01:42 -0600695 pmbusIntf->findHwmonDir();
Brandon Wyman59a35792020-06-04 12:37:40 -0500696 onOffConfig(phosphor::pmbus::ON_OFF_CONFIG_CONTROL_PIN_ONLY);
Brandon Wymanaed1f752019-11-25 18:10:52 -0600697 clearFaults();
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500698 updateInventory();
Brandon Wymanaed1f752019-11-25 18:10:52 -0600699 }
700 else
701 {
702 present = false;
703
704 // Clear out the now outdated inventory properties
705 updateInventory();
706 }
Matt Spinler0975eaf2022-02-14 15:38:30 -0600707 checkAvailability();
Brandon Wymanaed1f752019-11-25 18:10:52 -0600708 }
709}
710
Brandon Wyman9a507db2021-02-25 16:15:22 -0600711void PowerSupply::inventoryAdded(sdbusplus::message::message& msg)
712{
713 sdbusplus::message::object_path path;
714 msg.read(path);
715 // Make sure the signal is for the PSU inventory path
716 if (path == inventoryPath)
717 {
718 std::map<std::string, std::map<std::string, std::variant<bool>>>
719 interfaces;
720 // Get map of interfaces and their properties
721 msg.read(interfaces);
722
723 auto properties = interfaces.find(INVENTORY_IFACE);
724 if (properties != interfaces.end())
725 {
726 auto property = properties->second.find(PRESENT_PROP);
727 if (property != properties->second.end())
728 {
729 present = std::get<bool>(property->second);
730
731 log<level::INFO>(fmt::format("Power Supply {} Present {}",
732 inventoryPath, present)
733 .c_str());
734
735 updateInventory();
Matt Spinler0975eaf2022-02-14 15:38:30 -0600736 checkAvailability();
Brandon Wyman9a507db2021-02-25 16:15:22 -0600737 }
738 }
739 }
740}
741
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500742void PowerSupply::updateInventory()
743{
744 using namespace phosphor::pmbus;
745
Chanh Nguyenc12c53b2021-04-06 17:24:47 +0700746#if IBM_VPD
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500747 std::string ccin;
748 std::string pn;
749 std::string fn;
750 std::string header;
751 std::string sn;
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500752 using PropertyMap =
George Liu070c1bc2020-10-12 11:28:01 +0800753 std::map<std::string,
754 std::variant<std::string, std::vector<uint8_t>, bool>>;
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500755 PropertyMap assetProps;
George Liu070c1bc2020-10-12 11:28:01 +0800756 PropertyMap operProps;
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500757 PropertyMap versionProps;
758 PropertyMap ipzvpdDINFProps;
759 PropertyMap ipzvpdVINIProps;
760 using InterfaceMap = std::map<std::string, PropertyMap>;
761 InterfaceMap interfaces;
762 using ObjectMap = std::map<sdbusplus::message::object_path, InterfaceMap>;
763 ObjectMap object;
764#endif
B. J. Wyman681b2a32021-04-20 22:31:22 +0000765 log<level::DEBUG>(
766 fmt::format("updateInventory() inventoryPath: {}", inventoryPath)
767 .c_str());
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500768
769 if (present)
770 {
771 // TODO: non-IBM inventory updates?
772
Chanh Nguyenc12c53b2021-04-06 17:24:47 +0700773#if IBM_VPD
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500774 try
775 {
776 ccin = pmbusIntf->readString(CCIN, Type::HwmonDeviceDebug);
777 assetProps.emplace(MODEL_PROP, ccin);
Adriana Kobylak572a9052021-03-30 15:58:07 +0000778 modelName = ccin;
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500779 }
Patrick Williamsc1d4de52021-10-06 12:45:57 -0500780 catch (const ReadFailure& e)
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500781 {
B. J. Wyman681b2a32021-04-20 22:31:22 +0000782 // Ignore the read failure, let pmbus code indicate failure,
783 // path...
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500784 // TODO - ibm918
785 // https://github.com/openbmc/docs/blob/master/designs/vpd-collection.md
786 // The BMC must log errors if any of the VPD cannot be properly
787 // parsed or fails ECC checks.
788 }
789
790 try
791 {
792 pn = pmbusIntf->readString(PART_NUMBER, Type::HwmonDeviceDebug);
793 assetProps.emplace(PN_PROP, pn);
794 }
Patrick Williamsc1d4de52021-10-06 12:45:57 -0500795 catch (const ReadFailure& e)
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500796 {
B. J. Wyman681b2a32021-04-20 22:31:22 +0000797 // Ignore the read failure, let pmbus code indicate failure,
798 // path...
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500799 }
800
801 try
802 {
803 fn = pmbusIntf->readString(FRU_NUMBER, Type::HwmonDeviceDebug);
Brandon Wymana169b0f2021-12-07 20:18:06 +0000804 assetProps.emplace(SPARE_PN_PROP, fn);
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500805 }
Patrick Williamsc1d4de52021-10-06 12:45:57 -0500806 catch (const ReadFailure& e)
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500807 {
B. J. Wyman681b2a32021-04-20 22:31:22 +0000808 // Ignore the read failure, let pmbus code indicate failure,
809 // path...
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500810 }
811
812 try
813 {
814 header =
815 pmbusIntf->readString(SERIAL_HEADER, Type::HwmonDeviceDebug);
816 sn = pmbusIntf->readString(SERIAL_NUMBER, Type::HwmonDeviceDebug);
Mike Capps1cb0f132022-03-14 11:40:30 -0400817 assetProps.emplace(SN_PROP, header + sn);
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500818 }
Patrick Williamsc1d4de52021-10-06 12:45:57 -0500819 catch (const ReadFailure& e)
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500820 {
B. J. Wyman681b2a32021-04-20 22:31:22 +0000821 // Ignore the read failure, let pmbus code indicate failure,
822 // path...
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500823 }
824
825 try
826 {
Brandon Wymanc9efe412020-10-09 15:42:50 -0500827 fwVersion =
828 pmbusIntf->readString(FW_VERSION, Type::HwmonDeviceDebug);
829 versionProps.emplace(VERSION_PROP, fwVersion);
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500830 }
Patrick Williamsc1d4de52021-10-06 12:45:57 -0500831 catch (const ReadFailure& e)
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500832 {
B. J. Wyman681b2a32021-04-20 22:31:22 +0000833 // Ignore the read failure, let pmbus code indicate failure,
834 // path...
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500835 }
836
837 ipzvpdVINIProps.emplace("CC",
838 std::vector<uint8_t>(ccin.begin(), ccin.end()));
839 ipzvpdVINIProps.emplace("PN",
840 std::vector<uint8_t>(pn.begin(), pn.end()));
841 ipzvpdVINIProps.emplace("FN",
842 std::vector<uint8_t>(fn.begin(), fn.end()));
843 std::string header_sn = header + sn + '\0';
844 ipzvpdVINIProps.emplace(
845 "SN", std::vector<uint8_t>(header_sn.begin(), header_sn.end()));
846 std::string description = "IBM PS";
847 ipzvpdVINIProps.emplace(
848 "DR", std::vector<uint8_t>(description.begin(), description.end()));
849
Ben Tynerf8d8c462022-01-27 16:09:45 -0600850 // Populate the VINI Resource Type (RT) keyword
851 ipzvpdVINIProps.emplace("RT", std::vector<uint8_t>{'V', 'I', 'N', 'I'});
852
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500853 // Update the Resource Identifier (RI) keyword
854 // 2 byte FRC: 0x0003
855 // 2 byte RID: 0x1000, 0x1001...
856 std::uint8_t num = std::stoul(
857 inventoryPath.substr(inventoryPath.size() - 1, 1), nullptr, 0);
858 std::vector<uint8_t> ri{0x00, 0x03, 0x10, num};
859 ipzvpdDINFProps.emplace("RI", ri);
860
861 // Fill in the FRU Label (FL) keyword.
862 std::string fl = "E";
863 fl.push_back(inventoryPath.back());
864 fl.resize(FL_KW_SIZE, ' ');
865 ipzvpdDINFProps.emplace("FL",
866 std::vector<uint8_t>(fl.begin(), fl.end()));
867
Ben Tynerf8d8c462022-01-27 16:09:45 -0600868 // Populate the DINF Resource Type (RT) keyword
869 ipzvpdDINFProps.emplace("RT", std::vector<uint8_t>{'D', 'I', 'N', 'F'});
870
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500871 interfaces.emplace(ASSET_IFACE, std::move(assetProps));
872 interfaces.emplace(VERSION_IFACE, std::move(versionProps));
873 interfaces.emplace(DINF_IFACE, std::move(ipzvpdDINFProps));
874 interfaces.emplace(VINI_IFACE, std::move(ipzvpdVINIProps));
875
George Liu070c1bc2020-10-12 11:28:01 +0800876 // Update the Functional
877 operProps.emplace(FUNCTIONAL_PROP, present);
878 interfaces.emplace(OPERATIONAL_STATE_IFACE, std::move(operProps));
879
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500880 auto path = inventoryPath.substr(strlen(INVENTORY_OBJ_PATH));
881 object.emplace(path, std::move(interfaces));
882
883 try
884 {
885 auto service =
886 util::getService(INVENTORY_OBJ_PATH, INVENTORY_MGR_IFACE, bus);
887
888 if (service.empty())
889 {
890 log<level::ERR>("Unable to get inventory manager service");
891 return;
892 }
893
894 auto method =
895 bus.new_method_call(service.c_str(), INVENTORY_OBJ_PATH,
896 INVENTORY_MGR_IFACE, "Notify");
897
898 method.append(std::move(object));
899
900 auto reply = bus.call(method);
901 }
Patrick Williamsc1d4de52021-10-06 12:45:57 -0500902 catch (const std::exception& e)
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500903 {
Jay Meyer6a3fd2c2020-08-25 16:37:16 -0500904 log<level::ERR>(
905 std::string(e.what() + std::string(" PATH=") + inventoryPath)
906 .c_str());
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500907 }
908#endif
909 }
910}
911
Adriana Kobylak4175ffb2021-08-02 14:51:05 +0000912void PowerSupply::getInputVoltage(double& actualInputVoltage,
913 int& inputVoltage) const
914{
915 using namespace phosphor::pmbus;
916
917 actualInputVoltage = in_input::VIN_VOLTAGE_0;
918 inputVoltage = in_input::VIN_VOLTAGE_0;
919
920 if (present)
921 {
922 try
923 {
924 // Read input voltage in millivolts
925 auto inputVoltageStr = pmbusIntf->readString(READ_VIN, Type::Hwmon);
926
927 // Convert to volts
928 actualInputVoltage = std::stod(inputVoltageStr) / 1000;
929
930 // Calculate the voltage based on voltage thresholds
931 if (actualInputVoltage < in_input::VIN_VOLTAGE_MIN)
932 {
933 inputVoltage = in_input::VIN_VOLTAGE_0;
934 }
935 else if (actualInputVoltage < in_input::VIN_VOLTAGE_110_THRESHOLD)
936 {
937 inputVoltage = in_input::VIN_VOLTAGE_110;
938 }
939 else
940 {
941 inputVoltage = in_input::VIN_VOLTAGE_220;
942 }
943 }
944 catch (const std::exception& e)
945 {
946 log<level::ERR>(
947 fmt::format("READ_VIN read error: {}", e.what()).c_str());
948 }
949 }
950}
951
Matt Spinler0975eaf2022-02-14 15:38:30 -0600952void PowerSupply::checkAvailability()
953{
954 bool origAvailability = available;
955 available = present && !hasInputFault() && !hasVINUVFault() &&
956 !hasPSKillFault() && !hasIoutOCFault();
957
958 if (origAvailability != available)
959 {
960 auto invpath = inventoryPath.substr(strlen(INVENTORY_OBJ_PATH));
961 phosphor::power::psu::setAvailable(bus, invpath, available);
Matt Spinlerca1e9ea2022-02-18 14:03:08 -0600962
963 // Check if the health rollup needs to change based on the
964 // new availability value.
965 phosphor::power::psu::handleChassisHealthRollup(bus, inventoryPath,
966 !available);
Matt Spinler0975eaf2022-02-14 15:38:30 -0600967 }
968}
969
Brandon Wyman3f1242f2020-01-28 13:11:25 -0600970} // namespace phosphor::power::psu