blob: c2dccfb22990df7898510aec8cb0127a69086640 [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();
189 }
190}
191
Brandon Wymanc2203432021-12-21 23:09:48 +0000192void PowerSupply::analyzeCMLFault()
193{
194 if (statusWord & phosphor::pmbus::status_word::CML_FAULT)
195 {
196 if (!cmlFault)
197 {
198 log<level::ERR>(fmt::format("CML fault: STATUS_WORD = {:#04x}, "
199 "STATUS_CML = {:#02x}",
200 statusWord, statusCML)
201 .c_str());
202 }
203
204 cmlFault = true;
205 }
206}
207
Brandon Wymane3b0bb02021-12-21 23:16:48 +0000208void PowerSupply::analyzeInputFault()
209{
210 if (statusWord & phosphor::pmbus::status_word::INPUT_FAULT_WARN)
211 {
212 if (!inputFault)
213 {
214 log<level::ERR>(fmt::format("INPUT fault: STATUS_WORD = {:#04x}, "
215 "STATUS_MFR_SPECIFIC = {:#02x}, "
216 "STATUS_INPUT = {:#02x}",
217 statusWord, statusMFR, statusInput)
218 .c_str());
219 }
220
221 inputFault = true;
222 }
Brandon Wyman82affd92021-11-24 19:12:49 +0000223
224 // If had INPUT/VIN_UV fault, and now off.
225 // Trace that odd behavior.
226 if (inputFault &&
227 !(statusWord & phosphor::pmbus::status_word::INPUT_FAULT_WARN))
228 {
229 log<level::INFO>(
230 fmt::format("INPUT fault cleared: STATUS_WORD = {:#04x}, "
231 "STATUS_MFR_SPECIFIC = {:#02x}, "
232 "STATUS_INPUT = {:#02x}",
233 statusWord, statusMFR, statusInput)
234 .c_str());
235 inputFault = false;
236 }
Brandon Wymane3b0bb02021-12-21 23:16:48 +0000237}
238
Brandon Wymanc2c87132021-12-21 23:22:18 +0000239void PowerSupply::analyzeVoutOVFault()
240{
241 if (statusWord & phosphor::pmbus::status_word::VOUT_OV_FAULT)
242 {
243 if (!voutOVFault)
244 {
245 log<level::ERR>(
246 fmt::format("VOUT_OV_FAULT fault: STATUS_WORD = {:#04x}, "
247 "STATUS_MFR_SPECIFIC = {:#02x}, "
248 "STATUS_VOUT = {:#02x}",
249 statusWord, statusMFR, statusVout)
250 .c_str());
251 }
252
253 voutOVFault = true;
254 }
255}
256
Brandon Wymana00e7302021-12-21 23:28:29 +0000257void PowerSupply::analyzeIoutOCFault()
258{
259 if (statusWord & phosphor::pmbus::status_word::IOUT_OC_FAULT)
260 {
261 if (!ioutOCFault)
262 {
263 log<level::ERR>(fmt::format("IOUT fault: STATUS_WORD = {:#04x}, "
264 "STATUS_MFR_SPECIFIC = {:#02x}, "
265 "STATUS_IOUT = {:#02x}",
266 statusWord, statusMFR, statusIout)
267 .c_str());
268 }
269
270 ioutOCFault = true;
271 }
272}
273
Brandon Wyman08378782021-12-21 23:48:15 +0000274void PowerSupply::analyzeVoutUVFault()
275{
276 if ((statusWord & phosphor::pmbus::status_word::VOUT_FAULT) &&
277 !(statusWord & phosphor::pmbus::status_word::VOUT_OV_FAULT))
278 {
279 if (!voutUVFault)
280 {
281 log<level::ERR>(
282 fmt::format("VOUT_UV_FAULT fault: STATUS_WORD = {:#04x}, "
283 "STATUS_MFR_SPECIFIC = {:#02x}, "
284 "STATUS_VOUT = {:#02x}",
285 statusWord, statusMFR, statusVout)
286 .c_str());
287 }
288
289 voutUVFault = true;
290 }
291}
292
Brandon Wymand5d9a222021-12-21 23:59:05 +0000293void PowerSupply::analyzeFanFault()
294{
295 if (statusWord & phosphor::pmbus::status_word::FAN_FAULT)
296 {
297 if (!fanFault)
298 {
299 log<level::ERR>(fmt::format("FANS fault/warning: "
300 "STATUS_WORD = {:#04x}, "
301 "STATUS_MFR_SPECIFIC = {:#02x}, "
302 "STATUS_FANS_1_2 = {:#02x}",
303 statusWord, statusMFR, statusFans12)
304 .c_str());
305 }
306
307 fanFault = true;
308 }
309}
310
Brandon Wyman52cb3f22021-12-21 23:02:47 +0000311void PowerSupply::analyzeTemperatureFault()
312{
313 if (statusWord & phosphor::pmbus::status_word::TEMPERATURE_FAULT_WARN)
314 {
315 if (!tempFault)
316 {
317 log<level::ERR>(fmt::format("TEMPERATURE fault/warning: "
318 "STATUS_WORD = {:#04x}, "
319 "STATUS_MFR_SPECIFIC = {:#02x}, "
320 "STATUS_TEMPERATURE = {:#02x}",
321 statusWord, statusMFR,
322 statusTemperature)
323 .c_str());
324 }
325
326 tempFault = true;
327 }
328}
329
Brandon Wyman993b5542021-12-21 22:55:16 +0000330void PowerSupply::analyzePgoodFault()
331{
332 if ((statusWord & phosphor::pmbus::status_word::POWER_GOOD_NEGATED) ||
333 (statusWord & phosphor::pmbus::status_word::UNIT_IS_OFF))
334 {
335 if (pgoodFault < DEGLITCH_LIMIT)
336 {
337 log<level::ERR>(fmt::format("PGOOD fault: "
338 "STATUS_WORD = {:#04x}, "
339 "STATUS_MFR_SPECIFIC = {:#02x}",
340 statusWord, statusMFR)
341 .c_str());
342
343 pgoodFault++;
344 }
345 }
346 else
347 {
348 pgoodFault = 0;
349 }
350}
351
Brandon Wyman39ea02b2021-11-23 23:22:23 +0000352void PowerSupply::determineMFRFault()
353{
354 if (bindPath.string().find("ibm-cffps") != std::string::npos)
355 {
356 // IBM MFR_SPECIFIC[4] is PS_Kill fault
357 if (statusMFR & 0x10)
358 {
359 psKillFault = true;
360 }
361 // IBM MFR_SPECIFIC[6] is 12Vcs fault.
362 if (statusMFR & 0x40)
363 {
364 ps12VcsFault = true;
365 }
366 // IBM MFR_SPECIFIC[7] is 12V Current-Share fault.
367 if (statusMFR & 0x80)
368 {
369 psCS12VFault = true;
370 }
371 }
372}
373
Brandon Wyman6c2ac392021-12-21 22:23:06 +0000374void PowerSupply::analyzeMFRFault()
375{
376 if (statusWord & phosphor::pmbus::status_word::MFR_SPECIFIC_FAULT)
377 {
378 if (!mfrFault)
379 {
380 log<level::ERR>(fmt::format("MFR fault: "
381 "STATUS_WORD = {:#04x} "
382 "STATUS_MFR_SPECIFIC = {:#02x}",
383 statusWord, statusMFR)
384 .c_str());
385 }
386
387 mfrFault = true;
388 determineMFRFault();
389 }
390}
391
Brandon Wymanf087f472021-12-22 00:04:27 +0000392void PowerSupply::analyzeVinUVFault()
393{
394 if (statusWord & phosphor::pmbus::status_word::VIN_UV_FAULT)
395 {
396 if (!vinUVFault)
397 {
398 log<level::ERR>(fmt::format("VIN_UV fault: STATUS_WORD = {:#04x}, "
399 "STATUS_MFR_SPECIFIC = {:#02x}, "
400 "STATUS_INPUT = {:#02x}",
401 statusWord, statusMFR, statusInput)
402 .c_str());
403 }
404
405 vinUVFault = true;
406 }
Brandon Wyman82affd92021-11-24 19:12:49 +0000407
408 if (vinUVFault &&
409 !(statusWord & phosphor::pmbus::status_word::VIN_UV_FAULT))
410 {
411 log<level::INFO>(
412 fmt::format("VIN_UV fault cleared: STATUS_WORD = {:#04x}, "
413 "STATUS_MFR_SPECIFIC = {:#02x}, "
414 "STATUS_INPUT = {:#02x}",
415 statusWord, statusMFR, statusInput)
416 .c_str());
417 vinUVFault = false;
418 }
Brandon Wymanf087f472021-12-22 00:04:27 +0000419}
420
Brandon Wyman3f1242f2020-01-28 13:11:25 -0600421void PowerSupply::analyze()
422{
423 using namespace phosphor::pmbus;
424
B. J. Wyman681b2a32021-04-20 22:31:22 +0000425 if (presenceGPIO)
426 {
427 updatePresenceGPIO();
428 }
429
Brandon Wymanf65c4062020-08-19 13:15:53 -0500430 if ((present) && (readFail < LOG_LIMIT))
Brandon Wyman3f1242f2020-01-28 13:11:25 -0600431 {
432 try
433 {
Brandon Wymanfed0ba22020-09-26 20:02:51 -0500434 statusWord = pmbusIntf->read(STATUS_WORD, Type::Debug);
Brandon Wymanf65c4062020-08-19 13:15:53 -0500435 // Read worked, reset the fail count.
436 readFail = 0;
Brandon Wyman3f1242f2020-01-28 13:11:25 -0600437
438 if (statusWord)
439 {
Brandon Wymanf07bc792021-10-12 19:00:35 +0000440 statusInput = pmbusIntf->read(STATUS_INPUT, Type::Debug);
Jay Meyer10d94052020-11-30 14:41:21 -0600441 statusMFR = pmbusIntf->read(STATUS_MFR, Type::Debug);
Brandon Wyman85c7bf42021-10-19 22:28:48 +0000442 statusCML = pmbusIntf->read(STATUS_CML, Type::Debug);
Brandon Wyman6710ba22021-10-27 17:39:31 +0000443 auto status0Vout = pmbusIntf->insertPageNum(STATUS_VOUT, 0);
444 statusVout = pmbusIntf->read(status0Vout, Type::Debug);
Brandon Wymanb10b3be2021-11-09 22:12:15 +0000445 statusIout = pmbusIntf->read(STATUS_IOUT, Type::Debug);
Brandon Wyman7ee4d7e2021-11-19 20:48:23 +0000446 statusFans12 = pmbusIntf->read(STATUS_FANS_1_2, Type::Debug);
Brandon Wyman96893a42021-11-05 19:56:57 +0000447 statusTemperature =
448 pmbusIntf->read(STATUS_TEMPERATURE, Type::Debug);
Brandon Wyman9ddc6222021-10-28 17:28:01 +0000449
Brandon Wymanc2203432021-12-21 23:09:48 +0000450 analyzeCMLFault();
Brandon Wyman85c7bf42021-10-19 22:28:48 +0000451
Brandon Wymane3b0bb02021-12-21 23:16:48 +0000452 analyzeInputFault();
Brandon Wyman3f1242f2020-01-28 13:11:25 -0600453
Brandon Wymanc2c87132021-12-21 23:22:18 +0000454 analyzeVoutOVFault();
Brandon Wyman6710ba22021-10-27 17:39:31 +0000455
Brandon Wymana00e7302021-12-21 23:28:29 +0000456 analyzeIoutOCFault();
Brandon Wymanb10b3be2021-11-09 22:12:15 +0000457
Brandon Wyman08378782021-12-21 23:48:15 +0000458 analyzeVoutUVFault();
Brandon Wyman2cf46942021-10-28 19:09:16 +0000459
Brandon Wymand5d9a222021-12-21 23:59:05 +0000460 analyzeFanFault();
Brandon Wyman7ee4d7e2021-11-19 20:48:23 +0000461
Brandon Wyman52cb3f22021-12-21 23:02:47 +0000462 analyzeTemperatureFault();
Brandon Wyman96893a42021-11-05 19:56:57 +0000463
Brandon Wyman993b5542021-12-21 22:55:16 +0000464 analyzePgoodFault();
Brandon Wyman2916ea52021-11-06 03:31:18 +0000465
Brandon Wyman6c2ac392021-12-21 22:23:06 +0000466 analyzeMFRFault();
Brandon Wyman3f1242f2020-01-28 13:11:25 -0600467
Brandon Wymanf087f472021-12-22 00:04:27 +0000468 analyzeVinUVFault();
Brandon Wyman3f1242f2020-01-28 13:11:25 -0600469 }
470 else
471 {
Brandon Wymane3f7ad22021-12-21 20:27:45 +0000472 // if INPUT/VIN_UV fault was on, it cleared, trace it.
473 if (inputFault)
474 {
475 log<level::INFO>(
476 fmt::format(
477 "INPUT fault cleared: STATUS_WORD = {:#04x}",
478 statusWord)
479 .c_str());
480 }
481
482 if (vinUVFault)
483 {
484 log<level::INFO>(
485 fmt::format("VIN_UV cleared: STATUS_WORD = {:#04x}",
486 statusWord)
487 .c_str());
488 }
489
Brandon Wyman06ca4592021-12-06 22:52:23 +0000490 if (pgoodFault > 0)
Brandon Wyman4aecc292021-11-10 22:40:41 +0000491 {
492 log<level::INFO>(fmt::format("pgoodFault cleared path: {}",
493 inventoryPath)
494 .c_str());
Brandon Wyman4aecc292021-11-10 22:40:41 +0000495 }
Brandon Wymane3f7ad22021-12-21 20:27:45 +0000496
497 clearFaultFlags();
Brandon Wyman3f1242f2020-01-28 13:11:25 -0600498 }
Brandon Wyman82affd92021-11-24 19:12:49 +0000499
500 // Save off old inputVoltage value.
501 // Get latest inputVoltage.
502 // If voltage went from below minimum, and now is not, clear faults.
503 // Note: getInputVoltage() has its own try/catch.
504 int inputVoltageOld = inputVoltage;
505 double actualInputVoltage;
506 getInputVoltage(actualInputVoltage, inputVoltage);
507 if ((inputVoltageOld == in_input::VIN_VOLTAGE_0) &&
508 (inputVoltage != in_input::VIN_VOLTAGE_0))
509 {
510 log<level::INFO>(
511 fmt::format(
512 "READ_VIN back in range: inputVoltageOld = {} inputVoltage = {}",
513 inputVoltageOld, inputVoltage)
514 .c_str());
515 clearFaults();
516 }
Brandon Wyman3f1242f2020-01-28 13:11:25 -0600517 }
Patrick Williamsc1d4de52021-10-06 12:45:57 -0500518 catch (const ReadFailure& e)
Brandon Wyman3f1242f2020-01-28 13:11:25 -0600519 {
Brandon Wymanf65c4062020-08-19 13:15:53 -0500520 readFail++;
Brandon Wyman3f1242f2020-01-28 13:11:25 -0600521 phosphor::logging::commit<ReadFailure>();
522 }
523 }
524}
525
Brandon Wyman59a35792020-06-04 12:37:40 -0500526void PowerSupply::onOffConfig(uint8_t data)
527{
528 using namespace phosphor::pmbus;
529
530 if (present)
531 {
532 log<level::INFO>("ON_OFF_CONFIG write", entry("DATA=0x%02X", data));
533 try
534 {
535 std::vector<uint8_t> configData{data};
536 pmbusIntf->writeBinary(ON_OFF_CONFIG, configData,
537 Type::HwmonDeviceDebug);
538 }
539 catch (...)
540 {
541 // The underlying code in writeBinary will log a message to the
B. J. Wyman681b2a32021-04-20 22:31:22 +0000542 // journal if the write fails. If the ON_OFF_CONFIG is not setup
543 // as desired, later fault detection and analysis code should
544 // catch any of the fall out. We should not need to terminate
545 // the application if this write fails.
Brandon Wyman59a35792020-06-04 12:37:40 -0500546 }
547 }
548}
549
Brandon Wyman3c208462020-05-13 16:25:58 -0500550void PowerSupply::clearFaults()
551{
Brandon Wyman82affd92021-11-24 19:12:49 +0000552 log<level::DEBUG>(
553 fmt::format("clearFaults() inventoryPath: {}", inventoryPath).c_str());
Brandon Wyman5474c912021-02-23 14:39:43 -0600554 faultLogged = false;
Brandon Wyman3c208462020-05-13 16:25:58 -0500555 // The PMBus device driver does not allow for writing CLEAR_FAULTS
556 // directly. However, the pmbus hwmon device driver code will send a
557 // CLEAR_FAULTS after reading from any of the hwmon "files" in sysfs, so
558 // reading in1_input should result in clearing the fault bits in
559 // STATUS_BYTE/STATUS_WORD.
560 // I do not care what the return value is.
Brandon Wyman11151532020-11-10 13:45:57 -0600561 if (present)
Brandon Wyman3c208462020-05-13 16:25:58 -0500562 {
Brandon Wymane3f7ad22021-12-21 20:27:45 +0000563 clearFaultFlags();
Brandon Wyman9564e942020-11-10 14:01:42 -0600564 readFail = 0;
Brandon Wyman9564e942020-11-10 14:01:42 -0600565
Brandon Wyman11151532020-11-10 13:45:57 -0600566 try
567 {
568 static_cast<void>(
569 pmbusIntf->read("in1_input", phosphor::pmbus::Type::Hwmon));
570 }
Patrick Williamsc1d4de52021-10-06 12:45:57 -0500571 catch (const ReadFailure& e)
Brandon Wyman11151532020-11-10 13:45:57 -0600572 {
573 // Since I do not care what the return value is, I really do not
B. J. Wyman681b2a32021-04-20 22:31:22 +0000574 // care much if it gets a ReadFailure either. However, this
575 // should not prevent the application from continuing to run, so
576 // catching the read failure.
Brandon Wyman11151532020-11-10 13:45:57 -0600577 }
Brandon Wyman3c208462020-05-13 16:25:58 -0500578 }
579}
580
Brandon Wymanaed1f752019-11-25 18:10:52 -0600581void PowerSupply::inventoryChanged(sdbusplus::message::message& msg)
582{
583 std::string msgSensor;
Patrick Williamsabe49412020-05-13 17:59:47 -0500584 std::map<std::string, std::variant<uint32_t, bool>> msgData;
Brandon Wymanaed1f752019-11-25 18:10:52 -0600585 msg.read(msgSensor, msgData);
586
587 // Check if it was the Present property that changed.
588 auto valPropMap = msgData.find(PRESENT_PROP);
589 if (valPropMap != msgData.end())
590 {
591 if (std::get<bool>(valPropMap->second))
592 {
593 present = true;
B. J. Wyman681b2a32021-04-20 22:31:22 +0000594 // TODO: Immediately trying to read or write the "files" causes
595 // read or write failures.
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500596 using namespace std::chrono_literals;
597 std::this_thread::sleep_for(20ms);
Brandon Wyman9564e942020-11-10 14:01:42 -0600598 pmbusIntf->findHwmonDir();
Brandon Wyman59a35792020-06-04 12:37:40 -0500599 onOffConfig(phosphor::pmbus::ON_OFF_CONFIG_CONTROL_PIN_ONLY);
Brandon Wymanaed1f752019-11-25 18:10:52 -0600600 clearFaults();
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500601 updateInventory();
Brandon Wymanaed1f752019-11-25 18:10:52 -0600602 }
603 else
604 {
605 present = false;
606
607 // Clear out the now outdated inventory properties
608 updateInventory();
609 }
610 }
611}
612
Brandon Wyman9a507db2021-02-25 16:15:22 -0600613void PowerSupply::inventoryAdded(sdbusplus::message::message& msg)
614{
615 sdbusplus::message::object_path path;
616 msg.read(path);
617 // Make sure the signal is for the PSU inventory path
618 if (path == inventoryPath)
619 {
620 std::map<std::string, std::map<std::string, std::variant<bool>>>
621 interfaces;
622 // Get map of interfaces and their properties
623 msg.read(interfaces);
624
625 auto properties = interfaces.find(INVENTORY_IFACE);
626 if (properties != interfaces.end())
627 {
628 auto property = properties->second.find(PRESENT_PROP);
629 if (property != properties->second.end())
630 {
631 present = std::get<bool>(property->second);
632
633 log<level::INFO>(fmt::format("Power Supply {} Present {}",
634 inventoryPath, present)
635 .c_str());
636
637 updateInventory();
638 }
639 }
640 }
641}
642
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500643void PowerSupply::updateInventory()
644{
645 using namespace phosphor::pmbus;
646
Chanh Nguyenc12c53b2021-04-06 17:24:47 +0700647#if IBM_VPD
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500648 std::string ccin;
649 std::string pn;
650 std::string fn;
651 std::string header;
652 std::string sn;
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500653 using PropertyMap =
George Liu070c1bc2020-10-12 11:28:01 +0800654 std::map<std::string,
655 std::variant<std::string, std::vector<uint8_t>, bool>>;
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500656 PropertyMap assetProps;
George Liu070c1bc2020-10-12 11:28:01 +0800657 PropertyMap operProps;
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500658 PropertyMap versionProps;
659 PropertyMap ipzvpdDINFProps;
660 PropertyMap ipzvpdVINIProps;
661 using InterfaceMap = std::map<std::string, PropertyMap>;
662 InterfaceMap interfaces;
663 using ObjectMap = std::map<sdbusplus::message::object_path, InterfaceMap>;
664 ObjectMap object;
665#endif
B. J. Wyman681b2a32021-04-20 22:31:22 +0000666 log<level::DEBUG>(
667 fmt::format("updateInventory() inventoryPath: {}", inventoryPath)
668 .c_str());
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500669
670 if (present)
671 {
672 // TODO: non-IBM inventory updates?
673
Chanh Nguyenc12c53b2021-04-06 17:24:47 +0700674#if IBM_VPD
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500675 try
676 {
677 ccin = pmbusIntf->readString(CCIN, Type::HwmonDeviceDebug);
678 assetProps.emplace(MODEL_PROP, ccin);
Adriana Kobylak572a9052021-03-30 15:58:07 +0000679 modelName = ccin;
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500680 }
Patrick Williamsc1d4de52021-10-06 12:45:57 -0500681 catch (const ReadFailure& e)
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500682 {
B. J. Wyman681b2a32021-04-20 22:31:22 +0000683 // Ignore the read failure, let pmbus code indicate failure,
684 // path...
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500685 // TODO - ibm918
686 // https://github.com/openbmc/docs/blob/master/designs/vpd-collection.md
687 // The BMC must log errors if any of the VPD cannot be properly
688 // parsed or fails ECC checks.
689 }
690
691 try
692 {
693 pn = pmbusIntf->readString(PART_NUMBER, Type::HwmonDeviceDebug);
694 assetProps.emplace(PN_PROP, pn);
695 }
Patrick Williamsc1d4de52021-10-06 12:45:57 -0500696 catch (const ReadFailure& e)
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500697 {
B. J. Wyman681b2a32021-04-20 22:31:22 +0000698 // Ignore the read failure, let pmbus code indicate failure,
699 // path...
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500700 }
701
702 try
703 {
704 fn = pmbusIntf->readString(FRU_NUMBER, Type::HwmonDeviceDebug);
Brandon Wymana169b0f2021-12-07 20:18:06 +0000705 assetProps.emplace(SPARE_PN_PROP, fn);
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500706 }
Patrick Williamsc1d4de52021-10-06 12:45:57 -0500707 catch (const ReadFailure& e)
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500708 {
B. J. Wyman681b2a32021-04-20 22:31:22 +0000709 // Ignore the read failure, let pmbus code indicate failure,
710 // path...
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500711 }
712
713 try
714 {
715 header =
716 pmbusIntf->readString(SERIAL_HEADER, Type::HwmonDeviceDebug);
717 sn = pmbusIntf->readString(SERIAL_NUMBER, Type::HwmonDeviceDebug);
718 assetProps.emplace(SN_PROP, sn);
719 }
Patrick Williamsc1d4de52021-10-06 12:45:57 -0500720 catch (const ReadFailure& e)
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500721 {
B. J. Wyman681b2a32021-04-20 22:31:22 +0000722 // Ignore the read failure, let pmbus code indicate failure,
723 // path...
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500724 }
725
726 try
727 {
Brandon Wymanc9efe412020-10-09 15:42:50 -0500728 fwVersion =
729 pmbusIntf->readString(FW_VERSION, Type::HwmonDeviceDebug);
730 versionProps.emplace(VERSION_PROP, fwVersion);
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500731 }
Patrick Williamsc1d4de52021-10-06 12:45:57 -0500732 catch (const ReadFailure& e)
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500733 {
B. J. Wyman681b2a32021-04-20 22:31:22 +0000734 // Ignore the read failure, let pmbus code indicate failure,
735 // path...
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500736 }
737
738 ipzvpdVINIProps.emplace("CC",
739 std::vector<uint8_t>(ccin.begin(), ccin.end()));
740 ipzvpdVINIProps.emplace("PN",
741 std::vector<uint8_t>(pn.begin(), pn.end()));
742 ipzvpdVINIProps.emplace("FN",
743 std::vector<uint8_t>(fn.begin(), fn.end()));
744 std::string header_sn = header + sn + '\0';
745 ipzvpdVINIProps.emplace(
746 "SN", std::vector<uint8_t>(header_sn.begin(), header_sn.end()));
747 std::string description = "IBM PS";
748 ipzvpdVINIProps.emplace(
749 "DR", std::vector<uint8_t>(description.begin(), description.end()));
750
751 // Update the Resource Identifier (RI) keyword
752 // 2 byte FRC: 0x0003
753 // 2 byte RID: 0x1000, 0x1001...
754 std::uint8_t num = std::stoul(
755 inventoryPath.substr(inventoryPath.size() - 1, 1), nullptr, 0);
756 std::vector<uint8_t> ri{0x00, 0x03, 0x10, num};
757 ipzvpdDINFProps.emplace("RI", ri);
758
759 // Fill in the FRU Label (FL) keyword.
760 std::string fl = "E";
761 fl.push_back(inventoryPath.back());
762 fl.resize(FL_KW_SIZE, ' ');
763 ipzvpdDINFProps.emplace("FL",
764 std::vector<uint8_t>(fl.begin(), fl.end()));
765
766 interfaces.emplace(ASSET_IFACE, std::move(assetProps));
767 interfaces.emplace(VERSION_IFACE, std::move(versionProps));
768 interfaces.emplace(DINF_IFACE, std::move(ipzvpdDINFProps));
769 interfaces.emplace(VINI_IFACE, std::move(ipzvpdVINIProps));
770
George Liu070c1bc2020-10-12 11:28:01 +0800771 // Update the Functional
772 operProps.emplace(FUNCTIONAL_PROP, present);
773 interfaces.emplace(OPERATIONAL_STATE_IFACE, std::move(operProps));
774
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500775 auto path = inventoryPath.substr(strlen(INVENTORY_OBJ_PATH));
776 object.emplace(path, std::move(interfaces));
777
778 try
779 {
780 auto service =
781 util::getService(INVENTORY_OBJ_PATH, INVENTORY_MGR_IFACE, bus);
782
783 if (service.empty())
784 {
785 log<level::ERR>("Unable to get inventory manager service");
786 return;
787 }
788
789 auto method =
790 bus.new_method_call(service.c_str(), INVENTORY_OBJ_PATH,
791 INVENTORY_MGR_IFACE, "Notify");
792
793 method.append(std::move(object));
794
795 auto reply = bus.call(method);
796 }
Patrick Williamsc1d4de52021-10-06 12:45:57 -0500797 catch (const std::exception& e)
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500798 {
Jay Meyer6a3fd2c2020-08-25 16:37:16 -0500799 log<level::ERR>(
800 std::string(e.what() + std::string(" PATH=") + inventoryPath)
801 .c_str());
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500802 }
803#endif
804 }
805}
806
Adriana Kobylak4175ffb2021-08-02 14:51:05 +0000807void PowerSupply::getInputVoltage(double& actualInputVoltage,
808 int& inputVoltage) const
809{
810 using namespace phosphor::pmbus;
811
812 actualInputVoltage = in_input::VIN_VOLTAGE_0;
813 inputVoltage = in_input::VIN_VOLTAGE_0;
814
815 if (present)
816 {
817 try
818 {
819 // Read input voltage in millivolts
820 auto inputVoltageStr = pmbusIntf->readString(READ_VIN, Type::Hwmon);
821
822 // Convert to volts
823 actualInputVoltage = std::stod(inputVoltageStr) / 1000;
824
825 // Calculate the voltage based on voltage thresholds
826 if (actualInputVoltage < in_input::VIN_VOLTAGE_MIN)
827 {
828 inputVoltage = in_input::VIN_VOLTAGE_0;
829 }
830 else if (actualInputVoltage < in_input::VIN_VOLTAGE_110_THRESHOLD)
831 {
832 inputVoltage = in_input::VIN_VOLTAGE_110;
833 }
834 else
835 {
836 inputVoltage = in_input::VIN_VOLTAGE_220;
837 }
838 }
839 catch (const std::exception& e)
840 {
841 log<level::ERR>(
842 fmt::format("READ_VIN read error: {}", e.what()).c_str());
843 }
844 }
845}
846
Brandon Wyman3f1242f2020-01-28 13:11:25 -0600847} // namespace phosphor::power::psu