blob: b364b362f846f55fd5d911eb6adddcdc32c1832c [file] [log] [blame]
Brandon Wyman1d7a7df2020-03-26 10:14:05 -05001#include "config.h"
2
Brandon Wymanaed1f752019-11-25 18:10:52 -06003#include "power_supply.hpp"
4
5#include "types.hpp"
Brandon Wyman3f1242f2020-01-28 13:11:25 -06006#include "util.hpp"
Brandon Wymanaed1f752019-11-25 18:10:52 -06007
Brandon Wymandf13c3a2020-12-15 14:25:22 -06008#include <fmt/format.h>
9
Brandon Wyman3f1242f2020-01-28 13:11:25 -060010#include <xyz/openbmc_project/Common/Device/error.hpp>
11
Brandon Wyman4fc191f2022-03-10 23:07:13 +000012#include <chrono> // sleep_for()
13#include <cmath>
Brandon Wyman1d7a7df2020-03-26 10:14:05 -050014#include <cstdint> // uint8_t...
B. J. Wyman681b2a32021-04-20 22:31:22 +000015#include <fstream>
16#include <thread> // sleep_for()
Brandon Wyman1d7a7df2020-03-26 10:14:05 -050017
Brandon Wyman3f1242f2020-01-28 13:11:25 -060018namespace phosphor::power::psu
Brandon Wymanaed1f752019-11-25 18:10:52 -060019{
B. J. Wyman681b2a32021-04-20 22:31:22 +000020// Amount of time in milliseconds to delay between power supply going from
21// missing to present before running the bind command(s).
22constexpr auto bindDelay = 1000;
Brandon Wymanaed1f752019-11-25 18:10:52 -060023
Brandon Wymanc3324422022-03-24 20:30:57 +000024// The number of INPUT_HISTORY records to keep on D-Bus.
25// Each record covers a 30-second span. That means two records are needed to
26// cover a minute of time. If we want one (1) hour of data, that would be 120
27// records.
28constexpr auto INPUT_HISTORY_MAX_RECORDS = 120;
29
Brandon Wymanaed1f752019-11-25 18:10:52 -060030using namespace phosphor::logging;
Brandon Wyman3f1242f2020-01-28 13:11:25 -060031using namespace sdbusplus::xyz::openbmc_project::Common::Device::Error;
Brandon Wymanaed1f752019-11-25 18:10:52 -060032
Brandon Wyman510acaa2020-11-05 18:32:04 -060033PowerSupply::PowerSupply(sdbusplus::bus::bus& bus, const std::string& invpath,
B. J. Wyman681b2a32021-04-20 22:31:22 +000034 std::uint8_t i2cbus, std::uint16_t i2caddr,
Brandon Wymanc3324422022-03-24 20:30:57 +000035 const std::string& driver,
B. J. Wyman681b2a32021-04-20 22:31:22 +000036 const std::string& gpioLineName) :
Brandon Wyman510acaa2020-11-05 18:32:04 -060037 bus(bus),
Brandon Wymanc3324422022-03-24 20:30:57 +000038 inventoryPath(invpath), bindPath("/sys/bus/i2c/drivers/" + driver)
Brandon Wyman510acaa2020-11-05 18:32:04 -060039{
40 if (inventoryPath.empty())
41 {
42 throw std::invalid_argument{"Invalid empty inventoryPath"};
43 }
44
B. J. Wyman681b2a32021-04-20 22:31:22 +000045 if (gpioLineName.empty())
46 {
47 throw std::invalid_argument{"Invalid empty gpioLineName"};
48 }
Brandon Wyman510acaa2020-11-05 18:32:04 -060049
Brandon Wyman321a6152022-03-19 00:11:44 +000050 shortName = findShortName(inventoryPath);
51
52 log<level::DEBUG>(
53 fmt::format("{} gpioLineName: {}", shortName, gpioLineName).c_str());
B. J. Wyman681b2a32021-04-20 22:31:22 +000054 presenceGPIO = createGPIO(gpioLineName);
Brandon Wyman510acaa2020-11-05 18:32:04 -060055
56 std::ostringstream ss;
57 ss << std::hex << std::setw(4) << std::setfill('0') << i2caddr;
58 std::string addrStr = ss.str();
B. J. Wyman681b2a32021-04-20 22:31:22 +000059 std::string busStr = std::to_string(i2cbus);
60 bindDevice = busStr;
61 bindDevice.append("-");
62 bindDevice.append(addrStr);
63
Brandon Wyman510acaa2020-11-05 18:32:04 -060064 pmbusIntf = phosphor::pmbus::createPMBus(i2cbus, addrStr);
65
66 // Get the current state of the Present property.
B. J. Wyman681b2a32021-04-20 22:31:22 +000067 try
68 {
69 updatePresenceGPIO();
70 }
71 catch (...)
72 {
73 // If the above attempt to use the GPIO failed, it likely means that the
74 // GPIOs are in use by the kernel, meaning it is using gpio-keys.
75 // So, I should rely on phosphor-gpio-presence to update D-Bus, and
76 // work that way for power supply presence.
77 presenceGPIO = nullptr;
78 // Setup the functions to call when the D-Bus inventory path for the
79 // Present property changes.
80 presentMatch = std::make_unique<sdbusplus::bus::match_t>(
81 bus,
82 sdbusplus::bus::match::rules::propertiesChanged(inventoryPath,
83 INVENTORY_IFACE),
84 [this](auto& msg) { this->inventoryChanged(msg); });
85
86 presentAddedMatch = std::make_unique<sdbusplus::bus::match_t>(
87 bus,
88 sdbusplus::bus::match::rules::interfacesAdded() +
89 sdbusplus::bus::match::rules::argNpath(0, inventoryPath),
90 [this](auto& msg) { this->inventoryAdded(msg); });
91
92 updatePresence();
93 updateInventory();
Brandon Wymanc3324422022-03-24 20:30:57 +000094 setupInputHistory();
B. J. Wyman681b2a32021-04-20 22:31:22 +000095 }
96}
97
98void PowerSupply::bindOrUnbindDriver(bool present)
99{
100 auto action = (present) ? "bind" : "unbind";
101 auto path = bindPath / action;
102
103 if (present)
104 {
Brandon Wymanb1ee60f2022-03-22 22:37:12 +0000105 std::this_thread::sleep_for(std::chrono::milliseconds(bindDelay));
B. J. Wyman681b2a32021-04-20 22:31:22 +0000106 log<level::INFO>(
107 fmt::format("Binding device driver. path: {} device: {}",
108 path.string(), bindDevice)
109 .c_str());
110 }
111 else
112 {
113 log<level::INFO>(
114 fmt::format("Unbinding device driver. path: {} device: {}",
115 path.string(), bindDevice)
116 .c_str());
117 }
118
119 std::ofstream file;
120
121 file.exceptions(std::ofstream::failbit | std::ofstream::badbit |
122 std::ofstream::eofbit);
123
124 try
125 {
126 file.open(path);
127 file << bindDevice;
128 file.close();
129 }
Patrick Williamsc1d4de52021-10-06 12:45:57 -0500130 catch (const std::exception& e)
B. J. Wyman681b2a32021-04-20 22:31:22 +0000131 {
132 auto err = errno;
133
134 log<level::ERR>(
135 fmt::format("Failed binding or unbinding device. errno={}", err)
136 .c_str());
137 }
Brandon Wyman510acaa2020-11-05 18:32:04 -0600138}
139
Brandon Wymanaed1f752019-11-25 18:10:52 -0600140void PowerSupply::updatePresence()
141{
142 try
143 {
Brandon Wyman3f1242f2020-01-28 13:11:25 -0600144 present = getPresence(bus, inventoryPath);
Brandon Wymanaed1f752019-11-25 18:10:52 -0600145 }
Patrick Williams69f10ad2021-09-02 09:46:49 -0500146 catch (const sdbusplus::exception::exception& e)
Brandon Wymanaed1f752019-11-25 18:10:52 -0600147 {
148 // Relying on property change or interface added to retry.
149 // Log an informational trace to the journal.
Brandon Wymandf13c3a2020-12-15 14:25:22 -0600150 log<level::INFO>(
151 fmt::format("D-Bus property {} access failure exception",
152 inventoryPath)
153 .c_str());
Brandon Wymanaed1f752019-11-25 18:10:52 -0600154 }
155}
156
B. J. Wyman681b2a32021-04-20 22:31:22 +0000157void PowerSupply::updatePresenceGPIO()
158{
159 bool presentOld = present;
160
161 try
162 {
163 if (presenceGPIO->read() > 0)
164 {
165 present = true;
166 }
167 else
168 {
169 present = false;
170 }
171 }
Patrick Williamsc1d4de52021-10-06 12:45:57 -0500172 catch (const std::exception& e)
B. J. Wyman681b2a32021-04-20 22:31:22 +0000173 {
174 log<level::ERR>(
175 fmt::format("presenceGPIO read fail: {}", e.what()).c_str());
176 throw;
177 }
178
179 if (presentOld != present)
180 {
Brandon Wyman321a6152022-03-19 00:11:44 +0000181 log<level::DEBUG>(fmt::format("{} presentOld: {} present: {}",
182 shortName, presentOld, present)
183 .c_str());
Matt Spinlerca1e9ea2022-02-18 14:03:08 -0600184
185 auto invpath = inventoryPath.substr(strlen(INVENTORY_OBJ_PATH));
Brandon Wyman90d529a2022-03-22 23:02:54 +0000186
187 bindOrUnbindDriver(present);
188 if (present)
189 {
190 // If the power supply was present, then missing, and present again,
191 // the hwmon path may have changed. We will need the correct/updated
192 // path before any reads or writes are attempted.
193 pmbusIntf->findHwmonDir();
194 }
195
Brandon Wyman321a6152022-03-19 00:11:44 +0000196 setPresence(bus, invpath, present, shortName);
Brandon Wymanc3324422022-03-24 20:30:57 +0000197 setupInputHistory();
Matt Spinlerca1e9ea2022-02-18 14:03:08 -0600198 updateInventory();
199
Brandon Wyman90d529a2022-03-22 23:02:54 +0000200 // Need Functional to already be correct before calling this.
Matt Spinlerca1e9ea2022-02-18 14:03:08 -0600201 checkAvailability();
202
B. J. Wyman681b2a32021-04-20 22:31:22 +0000203 if (present)
204 {
B. J. Wyman681b2a32021-04-20 22:31:22 +0000205 onOffConfig(phosphor::pmbus::ON_OFF_CONFIG_CONTROL_PIN_ONLY);
206 clearFaults();
Brandon Wyman18a24d92022-04-19 22:48:34 +0000207 // Indicate that the input history data and timestamps between all
208 // the power supplies that are present in the system need to be
209 // synchronized.
210 syncHistoryRequired = true;
B. J. Wyman681b2a32021-04-20 22:31:22 +0000211 }
B. J. Wyman681b2a32021-04-20 22:31:22 +0000212 }
213}
214
Brandon Wymanc2203432021-12-21 23:09:48 +0000215void PowerSupply::analyzeCMLFault()
216{
217 if (statusWord & phosphor::pmbus::status_word::CML_FAULT)
218 {
Brandon Wymanc2906f42021-12-21 20:14:56 +0000219 if (cmlFault < DEGLITCH_LIMIT)
Brandon Wymanc2203432021-12-21 23:09:48 +0000220 {
Brandon Wyman9e292ee2022-03-10 22:56:23 +0000221 if (statusWord != statusWordOld)
222 {
Brandon Wyman321a6152022-03-19 00:11:44 +0000223 log<level::ERR>(
224 fmt::format("{} CML fault: STATUS_WORD = {:#06x}, "
225 "STATUS_CML = {:#02x}",
226 shortName, statusWord, statusCML)
227 .c_str());
Brandon Wyman9e292ee2022-03-10 22:56:23 +0000228 }
Brandon Wymanc2906f42021-12-21 20:14:56 +0000229 cmlFault++;
230 }
231 }
232 else
233 {
234 cmlFault = 0;
Brandon Wymanc2203432021-12-21 23:09:48 +0000235 }
236}
237
Brandon Wymane3b0bb02021-12-21 23:16:48 +0000238void PowerSupply::analyzeInputFault()
239{
240 if (statusWord & phosphor::pmbus::status_word::INPUT_FAULT_WARN)
241 {
Brandon Wymanc2906f42021-12-21 20:14:56 +0000242 if (inputFault < DEGLITCH_LIMIT)
Brandon Wymane3b0bb02021-12-21 23:16:48 +0000243 {
Brandon Wyman9e292ee2022-03-10 22:56:23 +0000244 if (statusWord != statusWordOld)
245 {
246 log<level::ERR>(
Brandon Wyman321a6152022-03-19 00:11:44 +0000247 fmt::format("{} INPUT fault: STATUS_WORD = {:#06x}, "
Brandon Wyman9e292ee2022-03-10 22:56:23 +0000248 "STATUS_MFR_SPECIFIC = {:#04x}, "
249 "STATUS_INPUT = {:#04x}",
Brandon Wyman321a6152022-03-19 00:11:44 +0000250 shortName, statusWord, statusMFR, statusInput)
Brandon Wyman9e292ee2022-03-10 22:56:23 +0000251 .c_str());
252 }
Brandon Wymanc2906f42021-12-21 20:14:56 +0000253 inputFault++;
254 }
Brandon Wymane3b0bb02021-12-21 23:16:48 +0000255 }
Brandon Wyman82affd92021-11-24 19:12:49 +0000256
257 // If had INPUT/VIN_UV fault, and now off.
258 // Trace that odd behavior.
259 if (inputFault &&
260 !(statusWord & phosphor::pmbus::status_word::INPUT_FAULT_WARN))
261 {
262 log<level::INFO>(
Brandon Wyman321a6152022-03-19 00:11:44 +0000263 fmt::format("{} INPUT fault cleared: STATUS_WORD = {:#06x}, "
Brandon Wyman6f939a32022-03-10 18:42:20 +0000264 "STATUS_MFR_SPECIFIC = {:#04x}, "
265 "STATUS_INPUT = {:#04x}",
Brandon Wyman321a6152022-03-19 00:11:44 +0000266 shortName, statusWord, statusMFR, statusInput)
Brandon Wyman82affd92021-11-24 19:12:49 +0000267 .c_str());
Brandon Wymanc2906f42021-12-21 20:14:56 +0000268 inputFault = 0;
Brandon Wyman82affd92021-11-24 19:12:49 +0000269 }
Brandon Wymane3b0bb02021-12-21 23:16:48 +0000270}
271
Brandon Wymanc2c87132021-12-21 23:22:18 +0000272void PowerSupply::analyzeVoutOVFault()
273{
274 if (statusWord & phosphor::pmbus::status_word::VOUT_OV_FAULT)
275 {
Brandon Wymanc2906f42021-12-21 20:14:56 +0000276 if (voutOVFault < DEGLITCH_LIMIT)
Brandon Wymanc2c87132021-12-21 23:22:18 +0000277 {
Brandon Wyman9e292ee2022-03-10 22:56:23 +0000278 if (statusWord != statusWordOld)
279 {
280 log<level::ERR>(
Brandon Wyman321a6152022-03-19 00:11:44 +0000281 fmt::format(
282 "{} VOUT_OV_FAULT fault: STATUS_WORD = {:#06x}, "
283 "STATUS_MFR_SPECIFIC = {:#04x}, "
284 "STATUS_VOUT = {:#02x}",
285 shortName, statusWord, statusMFR, statusVout)
Brandon Wyman9e292ee2022-03-10 22:56:23 +0000286 .c_str());
287 }
Brandon Wymanc2c87132021-12-21 23:22:18 +0000288
Brandon Wymanc2906f42021-12-21 20:14:56 +0000289 voutOVFault++;
290 }
291 }
292 else
293 {
294 voutOVFault = 0;
Brandon Wymanc2c87132021-12-21 23:22:18 +0000295 }
296}
297
Brandon Wymana00e7302021-12-21 23:28:29 +0000298void PowerSupply::analyzeIoutOCFault()
299{
300 if (statusWord & phosphor::pmbus::status_word::IOUT_OC_FAULT)
301 {
Brandon Wymanc2906f42021-12-21 20:14:56 +0000302 if (ioutOCFault < DEGLITCH_LIMIT)
Brandon Wymana00e7302021-12-21 23:28:29 +0000303 {
Brandon Wyman9e292ee2022-03-10 22:56:23 +0000304 if (statusWord != statusWordOld)
305 {
306 log<level::ERR>(
Brandon Wyman321a6152022-03-19 00:11:44 +0000307 fmt::format("{} IOUT fault: STATUS_WORD = {:#06x}, "
Brandon Wyman9e292ee2022-03-10 22:56:23 +0000308 "STATUS_MFR_SPECIFIC = {:#04x}, "
309 "STATUS_IOUT = {:#04x}",
Brandon Wyman321a6152022-03-19 00:11:44 +0000310 shortName, statusWord, statusMFR, statusIout)
Brandon Wyman9e292ee2022-03-10 22:56:23 +0000311 .c_str());
312 }
Brandon Wymana00e7302021-12-21 23:28:29 +0000313
Brandon Wymanc2906f42021-12-21 20:14:56 +0000314 ioutOCFault++;
315 }
316 }
317 else
318 {
319 ioutOCFault = 0;
Brandon Wymana00e7302021-12-21 23:28:29 +0000320 }
321}
322
Brandon Wyman08378782021-12-21 23:48:15 +0000323void PowerSupply::analyzeVoutUVFault()
324{
325 if ((statusWord & phosphor::pmbus::status_word::VOUT_FAULT) &&
326 !(statusWord & phosphor::pmbus::status_word::VOUT_OV_FAULT))
327 {
Brandon Wymanc2906f42021-12-21 20:14:56 +0000328 if (voutUVFault < DEGLITCH_LIMIT)
Brandon Wyman08378782021-12-21 23:48:15 +0000329 {
Brandon Wyman9e292ee2022-03-10 22:56:23 +0000330 if (statusWord != statusWordOld)
331 {
332 log<level::ERR>(
Brandon Wyman321a6152022-03-19 00:11:44 +0000333 fmt::format(
334 "{} VOUT_UV_FAULT fault: STATUS_WORD = {:#06x}, "
335 "STATUS_MFR_SPECIFIC = {:#04x}, "
336 "STATUS_VOUT = {:#04x}",
337 shortName, statusWord, statusMFR, statusVout)
Brandon Wyman9e292ee2022-03-10 22:56:23 +0000338 .c_str());
339 }
Brandon Wymanc2906f42021-12-21 20:14:56 +0000340 voutUVFault++;
341 }
342 }
343 else
344 {
345 voutUVFault = 0;
Brandon Wyman08378782021-12-21 23:48:15 +0000346 }
347}
348
Brandon Wymand5d9a222021-12-21 23:59:05 +0000349void PowerSupply::analyzeFanFault()
350{
351 if (statusWord & phosphor::pmbus::status_word::FAN_FAULT)
352 {
Brandon Wymanc2906f42021-12-21 20:14:56 +0000353 if (fanFault < DEGLITCH_LIMIT)
Brandon Wymand5d9a222021-12-21 23:59:05 +0000354 {
Brandon Wyman9e292ee2022-03-10 22:56:23 +0000355 if (statusWord != statusWordOld)
356 {
Brandon Wyman321a6152022-03-19 00:11:44 +0000357 log<level::ERR>(fmt::format("{} FANS fault/warning: "
Brandon Wyman9e292ee2022-03-10 22:56:23 +0000358 "STATUS_WORD = {:#06x}, "
359 "STATUS_MFR_SPECIFIC = {:#04x}, "
360 "STATUS_FANS_1_2 = {:#04x}",
Brandon Wyman321a6152022-03-19 00:11:44 +0000361 shortName, statusWord, statusMFR,
362 statusFans12)
Brandon Wyman9e292ee2022-03-10 22:56:23 +0000363 .c_str());
364 }
Brandon Wymanc2906f42021-12-21 20:14:56 +0000365 fanFault++;
366 }
367 }
368 else
369 {
370 fanFault = 0;
Brandon Wymand5d9a222021-12-21 23:59:05 +0000371 }
372}
373
Brandon Wyman52cb3f22021-12-21 23:02:47 +0000374void PowerSupply::analyzeTemperatureFault()
375{
376 if (statusWord & phosphor::pmbus::status_word::TEMPERATURE_FAULT_WARN)
377 {
Brandon Wymanc2906f42021-12-21 20:14:56 +0000378 if (tempFault < DEGLITCH_LIMIT)
Brandon Wyman52cb3f22021-12-21 23:02:47 +0000379 {
Brandon Wyman9e292ee2022-03-10 22:56:23 +0000380 if (statusWord != statusWordOld)
381 {
Brandon Wyman321a6152022-03-19 00:11:44 +0000382 log<level::ERR>(fmt::format("{} TEMPERATURE fault/warning: "
Brandon Wyman9e292ee2022-03-10 22:56:23 +0000383 "STATUS_WORD = {:#06x}, "
384 "STATUS_MFR_SPECIFIC = {:#04x}, "
385 "STATUS_TEMPERATURE = {:#04x}",
Brandon Wyman321a6152022-03-19 00:11:44 +0000386 shortName, statusWord, statusMFR,
Brandon Wyman9e292ee2022-03-10 22:56:23 +0000387 statusTemperature)
388 .c_str());
389 }
Brandon Wymanc2906f42021-12-21 20:14:56 +0000390 tempFault++;
391 }
392 }
393 else
394 {
395 tempFault = 0;
Brandon Wyman52cb3f22021-12-21 23:02:47 +0000396 }
397}
398
Brandon Wyman993b5542021-12-21 22:55:16 +0000399void PowerSupply::analyzePgoodFault()
400{
401 if ((statusWord & phosphor::pmbus::status_word::POWER_GOOD_NEGATED) ||
402 (statusWord & phosphor::pmbus::status_word::UNIT_IS_OFF))
403 {
404 if (pgoodFault < DEGLITCH_LIMIT)
405 {
Brandon Wyman9e292ee2022-03-10 22:56:23 +0000406 if (statusWord != statusWordOld)
407 {
Brandon Wyman321a6152022-03-19 00:11:44 +0000408 log<level::ERR>(fmt::format("{} PGOOD fault: "
Brandon Wyman9e292ee2022-03-10 22:56:23 +0000409 "STATUS_WORD = {:#06x}, "
410 "STATUS_MFR_SPECIFIC = {:#04x}",
Brandon Wyman321a6152022-03-19 00:11:44 +0000411 shortName, statusWord, statusMFR)
Brandon Wyman9e292ee2022-03-10 22:56:23 +0000412 .c_str());
413 }
Brandon Wyman993b5542021-12-21 22:55:16 +0000414 pgoodFault++;
415 }
416 }
417 else
418 {
419 pgoodFault = 0;
420 }
421}
422
Brandon Wyman39ea02b2021-11-23 23:22:23 +0000423void PowerSupply::determineMFRFault()
424{
425 if (bindPath.string().find("ibm-cffps") != std::string::npos)
426 {
427 // IBM MFR_SPECIFIC[4] is PS_Kill fault
428 if (statusMFR & 0x10)
429 {
Brandon Wymanc2906f42021-12-21 20:14:56 +0000430 if (psKillFault < DEGLITCH_LIMIT)
431 {
432 psKillFault++;
433 }
434 }
435 else
436 {
437 psKillFault = 0;
Brandon Wyman39ea02b2021-11-23 23:22:23 +0000438 }
439 // IBM MFR_SPECIFIC[6] is 12Vcs fault.
440 if (statusMFR & 0x40)
441 {
Brandon Wymanc2906f42021-12-21 20:14:56 +0000442 if (ps12VcsFault < DEGLITCH_LIMIT)
443 {
444 ps12VcsFault++;
445 }
446 }
447 else
448 {
449 ps12VcsFault = 0;
Brandon Wyman39ea02b2021-11-23 23:22:23 +0000450 }
451 // IBM MFR_SPECIFIC[7] is 12V Current-Share fault.
452 if (statusMFR & 0x80)
453 {
Brandon Wymanc2906f42021-12-21 20:14:56 +0000454 if (psCS12VFault < DEGLITCH_LIMIT)
455 {
456 psCS12VFault++;
457 }
458 }
459 else
460 {
461 psCS12VFault = 0;
Brandon Wyman39ea02b2021-11-23 23:22:23 +0000462 }
463 }
464}
465
Brandon Wyman6c2ac392021-12-21 22:23:06 +0000466void PowerSupply::analyzeMFRFault()
467{
468 if (statusWord & phosphor::pmbus::status_word::MFR_SPECIFIC_FAULT)
469 {
Brandon Wymanc2906f42021-12-21 20:14:56 +0000470 if (mfrFault < DEGLITCH_LIMIT)
Brandon Wyman6c2ac392021-12-21 22:23:06 +0000471 {
Brandon Wyman9e292ee2022-03-10 22:56:23 +0000472 if (statusWord != statusWordOld)
473 {
Brandon Wyman321a6152022-03-19 00:11:44 +0000474 log<level::ERR>(fmt::format("{} MFR fault: "
Brandon Wyman9e292ee2022-03-10 22:56:23 +0000475 "STATUS_WORD = {:#06x} "
476 "STATUS_MFR_SPECIFIC = {:#04x}",
Brandon Wyman321a6152022-03-19 00:11:44 +0000477 shortName, statusWord, statusMFR)
Brandon Wyman9e292ee2022-03-10 22:56:23 +0000478 .c_str());
479 }
Brandon Wymanc2906f42021-12-21 20:14:56 +0000480 mfrFault++;
Brandon Wyman6c2ac392021-12-21 22:23:06 +0000481 }
482
Brandon Wyman6c2ac392021-12-21 22:23:06 +0000483 determineMFRFault();
484 }
Brandon Wymanc2906f42021-12-21 20:14:56 +0000485 else
486 {
487 mfrFault = 0;
488 }
Brandon Wyman6c2ac392021-12-21 22:23:06 +0000489}
490
Brandon Wymanf087f472021-12-22 00:04:27 +0000491void PowerSupply::analyzeVinUVFault()
492{
493 if (statusWord & phosphor::pmbus::status_word::VIN_UV_FAULT)
494 {
Brandon Wymanc2906f42021-12-21 20:14:56 +0000495 if (vinUVFault < DEGLITCH_LIMIT)
Brandon Wymanf087f472021-12-22 00:04:27 +0000496 {
Brandon Wyman9e292ee2022-03-10 22:56:23 +0000497 if (statusWord != statusWordOld)
498 {
499 log<level::ERR>(
Brandon Wyman321a6152022-03-19 00:11:44 +0000500 fmt::format("{} VIN_UV fault: STATUS_WORD = {:#06x}, "
Brandon Wyman9e292ee2022-03-10 22:56:23 +0000501 "STATUS_MFR_SPECIFIC = {:#04x}, "
502 "STATUS_INPUT = {:#04x}",
Brandon Wyman321a6152022-03-19 00:11:44 +0000503 shortName, statusWord, statusMFR, statusInput)
Brandon Wyman9e292ee2022-03-10 22:56:23 +0000504 .c_str());
505 }
Brandon Wymanc2906f42021-12-21 20:14:56 +0000506 vinUVFault++;
Brandon Wymanf087f472021-12-22 00:04:27 +0000507 }
Brandon Wymanf087f472021-12-22 00:04:27 +0000508 }
Brandon Wyman82affd92021-11-24 19:12:49 +0000509
510 if (vinUVFault &&
511 !(statusWord & phosphor::pmbus::status_word::VIN_UV_FAULT))
512 {
513 log<level::INFO>(
Brandon Wyman321a6152022-03-19 00:11:44 +0000514 fmt::format("{} VIN_UV fault cleared: STATUS_WORD = {:#06x}, "
Brandon Wyman6f939a32022-03-10 18:42:20 +0000515 "STATUS_MFR_SPECIFIC = {:#04x}, "
516 "STATUS_INPUT = {:#04x}",
Brandon Wyman321a6152022-03-19 00:11:44 +0000517 shortName, statusWord, statusMFR, statusInput)
Brandon Wyman82affd92021-11-24 19:12:49 +0000518 .c_str());
Brandon Wymanc2906f42021-12-21 20:14:56 +0000519 vinUVFault = 0;
Brandon Wyman82affd92021-11-24 19:12:49 +0000520 }
Brandon Wymanf087f472021-12-22 00:04:27 +0000521}
522
Brandon Wyman3f1242f2020-01-28 13:11:25 -0600523void PowerSupply::analyze()
524{
525 using namespace phosphor::pmbus;
526
B. J. Wyman681b2a32021-04-20 22:31:22 +0000527 if (presenceGPIO)
528 {
529 updatePresenceGPIO();
530 }
531
Brandon Wyman32453e92021-12-15 19:00:14 +0000532 if (present)
Brandon Wyman3f1242f2020-01-28 13:11:25 -0600533 {
534 try
535 {
Brandon Wyman9e292ee2022-03-10 22:56:23 +0000536 statusWordOld = statusWord;
Brandon Wyman32453e92021-12-15 19:00:14 +0000537 statusWord = pmbusIntf->read(STATUS_WORD, Type::Debug,
538 (readFail < LOG_LIMIT));
Brandon Wymanf65c4062020-08-19 13:15:53 -0500539 // Read worked, reset the fail count.
540 readFail = 0;
Brandon Wyman3f1242f2020-01-28 13:11:25 -0600541
542 if (statusWord)
543 {
Brandon Wymanf07bc792021-10-12 19:00:35 +0000544 statusInput = pmbusIntf->read(STATUS_INPUT, Type::Debug);
Jay Meyer10d94052020-11-30 14:41:21 -0600545 statusMFR = pmbusIntf->read(STATUS_MFR, Type::Debug);
Brandon Wyman85c7bf42021-10-19 22:28:48 +0000546 statusCML = pmbusIntf->read(STATUS_CML, Type::Debug);
Brandon Wyman6710ba22021-10-27 17:39:31 +0000547 auto status0Vout = pmbusIntf->insertPageNum(STATUS_VOUT, 0);
548 statusVout = pmbusIntf->read(status0Vout, Type::Debug);
Brandon Wymanb10b3be2021-11-09 22:12:15 +0000549 statusIout = pmbusIntf->read(STATUS_IOUT, Type::Debug);
Brandon Wyman7ee4d7e2021-11-19 20:48:23 +0000550 statusFans12 = pmbusIntf->read(STATUS_FANS_1_2, Type::Debug);
Brandon Wyman96893a42021-11-05 19:56:57 +0000551 statusTemperature =
552 pmbusIntf->read(STATUS_TEMPERATURE, Type::Debug);
Brandon Wyman9ddc6222021-10-28 17:28:01 +0000553
Brandon Wymanc2203432021-12-21 23:09:48 +0000554 analyzeCMLFault();
Brandon Wyman85c7bf42021-10-19 22:28:48 +0000555
Brandon Wymane3b0bb02021-12-21 23:16:48 +0000556 analyzeInputFault();
Brandon Wyman3f1242f2020-01-28 13:11:25 -0600557
Brandon Wymanc2c87132021-12-21 23:22:18 +0000558 analyzeVoutOVFault();
Brandon Wyman6710ba22021-10-27 17:39:31 +0000559
Brandon Wymana00e7302021-12-21 23:28:29 +0000560 analyzeIoutOCFault();
Brandon Wymanb10b3be2021-11-09 22:12:15 +0000561
Brandon Wyman08378782021-12-21 23:48:15 +0000562 analyzeVoutUVFault();
Brandon Wyman2cf46942021-10-28 19:09:16 +0000563
Brandon Wymand5d9a222021-12-21 23:59:05 +0000564 analyzeFanFault();
Brandon Wyman7ee4d7e2021-11-19 20:48:23 +0000565
Brandon Wyman52cb3f22021-12-21 23:02:47 +0000566 analyzeTemperatureFault();
Brandon Wyman96893a42021-11-05 19:56:57 +0000567
Brandon Wyman993b5542021-12-21 22:55:16 +0000568 analyzePgoodFault();
Brandon Wyman2916ea52021-11-06 03:31:18 +0000569
Brandon Wyman6c2ac392021-12-21 22:23:06 +0000570 analyzeMFRFault();
Brandon Wyman3f1242f2020-01-28 13:11:25 -0600571
Brandon Wymanf087f472021-12-22 00:04:27 +0000572 analyzeVinUVFault();
Brandon Wyman3f1242f2020-01-28 13:11:25 -0600573 }
574 else
575 {
Brandon Wyman9e292ee2022-03-10 22:56:23 +0000576 if (statusWord != statusWordOld)
577 {
Brandon Wyman321a6152022-03-19 00:11:44 +0000578 log<level::INFO>(fmt::format("{} STATUS_WORD = {:#06x}",
579 shortName, statusWord)
Brandon Wyman9e292ee2022-03-10 22:56:23 +0000580 .c_str());
581 }
582
Brandon Wymane3f7ad22021-12-21 20:27:45 +0000583 // if INPUT/VIN_UV fault was on, it cleared, trace it.
584 if (inputFault)
585 {
586 log<level::INFO>(
587 fmt::format(
Brandon Wyman321a6152022-03-19 00:11:44 +0000588 "{} INPUT fault cleared: STATUS_WORD = {:#06x}",
589 shortName, statusWord)
Brandon Wymane3f7ad22021-12-21 20:27:45 +0000590 .c_str());
591 }
592
593 if (vinUVFault)
594 {
595 log<level::INFO>(
Brandon Wyman321a6152022-03-19 00:11:44 +0000596 fmt::format("{} VIN_UV cleared: STATUS_WORD = {:#06x}",
597 shortName, statusWord)
Brandon Wymane3f7ad22021-12-21 20:27:45 +0000598 .c_str());
599 }
600
Brandon Wyman06ca4592021-12-06 22:52:23 +0000601 if (pgoodFault > 0)
Brandon Wyman4aecc292021-11-10 22:40:41 +0000602 {
Brandon Wyman321a6152022-03-19 00:11:44 +0000603 log<level::INFO>(
604 fmt::format("{} pgoodFault cleared", shortName)
605 .c_str());
Brandon Wyman4aecc292021-11-10 22:40:41 +0000606 }
Brandon Wymane3f7ad22021-12-21 20:27:45 +0000607
608 clearFaultFlags();
Brandon Wyman3f1242f2020-01-28 13:11:25 -0600609 }
Brandon Wyman82affd92021-11-24 19:12:49 +0000610
611 // Save off old inputVoltage value.
612 // Get latest inputVoltage.
613 // If voltage went from below minimum, and now is not, clear faults.
614 // Note: getInputVoltage() has its own try/catch.
615 int inputVoltageOld = inputVoltage;
Brandon Wyman4fc191f2022-03-10 23:07:13 +0000616 double actualInputVoltageOld = actualInputVoltage;
Brandon Wyman82affd92021-11-24 19:12:49 +0000617 getInputVoltage(actualInputVoltage, inputVoltage);
618 if ((inputVoltageOld == in_input::VIN_VOLTAGE_0) &&
619 (inputVoltage != in_input::VIN_VOLTAGE_0))
620 {
621 log<level::INFO>(
622 fmt::format(
Brandon Wyman4fc191f2022-03-10 23:07:13 +0000623 "{} READ_VIN back in range: actualInputVoltageOld = {} "
624 "actualInputVoltage = {}",
625 shortName, actualInputVoltageOld, actualInputVoltage)
Brandon Wyman82affd92021-11-24 19:12:49 +0000626 .c_str());
Brandon Wyman3225a452022-03-18 18:51:49 +0000627 clearVinUVFault();
Brandon Wyman82affd92021-11-24 19:12:49 +0000628 }
Brandon Wyman4fc191f2022-03-10 23:07:13 +0000629 else if (vinUVFault && (inputVoltage != in_input::VIN_VOLTAGE_0))
630 {
631 log<level::INFO>(
632 fmt::format(
633 "{} CLEAR_FAULTS: vinUVFault {} actualInputVoltage {}",
634 shortName, vinUVFault, actualInputVoltage)
635 .c_str());
636 // Do we have a VIN_UV fault latched that can now be cleared
Brandon Wyman3225a452022-03-18 18:51:49 +0000637 // due to voltage back in range? Attempt to clear the fault(s),
638 // re-check faults on next call.
639 clearVinUVFault();
Brandon Wyman4fc191f2022-03-10 23:07:13 +0000640 }
641 else if (std::abs(actualInputVoltageOld - actualInputVoltage) > 1.0)
642 {
643 log<level::INFO>(
644 fmt::format(
645 "{} actualInputVoltageOld = {} actualInputVoltage = {}",
646 shortName, actualInputVoltageOld, actualInputVoltage)
647 .c_str());
648 }
Matt Spinler0975eaf2022-02-14 15:38:30 -0600649
650 checkAvailability();
Brandon Wymanc3324422022-03-24 20:30:57 +0000651
652 if (inputHistorySupported)
653 {
654 updateHistory();
655 }
Brandon Wyman3f1242f2020-01-28 13:11:25 -0600656 }
Patrick Williamsc1d4de52021-10-06 12:45:57 -0500657 catch (const ReadFailure& e)
Brandon Wyman3f1242f2020-01-28 13:11:25 -0600658 {
Brandon Wyman32453e92021-12-15 19:00:14 +0000659 if (readFail < SIZE_MAX)
660 {
661 readFail++;
662 }
663 if (readFail == LOG_LIMIT)
664 {
665 phosphor::logging::commit<ReadFailure>();
666 }
Brandon Wyman3f1242f2020-01-28 13:11:25 -0600667 }
668 }
669}
670
Brandon Wyman59a35792020-06-04 12:37:40 -0500671void PowerSupply::onOffConfig(uint8_t data)
672{
673 using namespace phosphor::pmbus;
674
675 if (present)
676 {
677 log<level::INFO>("ON_OFF_CONFIG write", entry("DATA=0x%02X", data));
678 try
679 {
680 std::vector<uint8_t> configData{data};
681 pmbusIntf->writeBinary(ON_OFF_CONFIG, configData,
682 Type::HwmonDeviceDebug);
683 }
684 catch (...)
685 {
686 // The underlying code in writeBinary will log a message to the
B. J. Wyman681b2a32021-04-20 22:31:22 +0000687 // journal if the write fails. If the ON_OFF_CONFIG is not setup
688 // as desired, later fault detection and analysis code should
689 // catch any of the fall out. We should not need to terminate
690 // the application if this write fails.
Brandon Wyman59a35792020-06-04 12:37:40 -0500691 }
692 }
693}
694
Brandon Wyman3225a452022-03-18 18:51:49 +0000695void PowerSupply::clearVinUVFault()
696{
697 // Read in1_lcrit_alarm to clear bits 3 and 4 of STATUS_INPUT.
698 // The fault bits in STAUTS_INPUT roll-up to STATUS_WORD. Clearing those
699 // bits in STATUS_INPUT should result in the corresponding STATUS_WORD bits
700 // also clearing.
701 //
702 // Do not care about return value. Should be 1 if active, 0 if not.
703 static_cast<void>(
704 pmbusIntf->read("in1_lcrit_alarm", phosphor::pmbus::Type::Hwmon));
705 vinUVFault = 0;
706}
707
Brandon Wyman3c208462020-05-13 16:25:58 -0500708void PowerSupply::clearFaults()
709{
Brandon Wyman82affd92021-11-24 19:12:49 +0000710 log<level::DEBUG>(
711 fmt::format("clearFaults() inventoryPath: {}", inventoryPath).c_str());
Brandon Wyman5474c912021-02-23 14:39:43 -0600712 faultLogged = false;
Brandon Wyman3c208462020-05-13 16:25:58 -0500713 // The PMBus device driver does not allow for writing CLEAR_FAULTS
714 // directly. However, the pmbus hwmon device driver code will send a
715 // CLEAR_FAULTS after reading from any of the hwmon "files" in sysfs, so
716 // reading in1_input should result in clearing the fault bits in
717 // STATUS_BYTE/STATUS_WORD.
718 // I do not care what the return value is.
Brandon Wyman11151532020-11-10 13:45:57 -0600719 if (present)
Brandon Wyman3c208462020-05-13 16:25:58 -0500720 {
Brandon Wymane3f7ad22021-12-21 20:27:45 +0000721 clearFaultFlags();
Matt Spinler0975eaf2022-02-14 15:38:30 -0600722 checkAvailability();
Brandon Wyman9564e942020-11-10 14:01:42 -0600723 readFail = 0;
Brandon Wyman9564e942020-11-10 14:01:42 -0600724
Brandon Wyman11151532020-11-10 13:45:57 -0600725 try
726 {
Brandon Wyman3225a452022-03-18 18:51:49 +0000727 clearVinUVFault();
Brandon Wyman11151532020-11-10 13:45:57 -0600728 static_cast<void>(
729 pmbusIntf->read("in1_input", phosphor::pmbus::Type::Hwmon));
730 }
Patrick Williamsc1d4de52021-10-06 12:45:57 -0500731 catch (const ReadFailure& e)
Brandon Wyman11151532020-11-10 13:45:57 -0600732 {
733 // Since I do not care what the return value is, I really do not
B. J. Wyman681b2a32021-04-20 22:31:22 +0000734 // care much if it gets a ReadFailure either. However, this
735 // should not prevent the application from continuing to run, so
736 // catching the read failure.
Brandon Wyman11151532020-11-10 13:45:57 -0600737 }
Brandon Wyman3c208462020-05-13 16:25:58 -0500738 }
739}
740
Brandon Wymanaed1f752019-11-25 18:10:52 -0600741void PowerSupply::inventoryChanged(sdbusplus::message::message& msg)
742{
743 std::string msgSensor;
Patrick Williamsabe49412020-05-13 17:59:47 -0500744 std::map<std::string, std::variant<uint32_t, bool>> msgData;
Brandon Wymanaed1f752019-11-25 18:10:52 -0600745 msg.read(msgSensor, msgData);
746
747 // Check if it was the Present property that changed.
748 auto valPropMap = msgData.find(PRESENT_PROP);
749 if (valPropMap != msgData.end())
750 {
751 if (std::get<bool>(valPropMap->second))
752 {
753 present = true;
B. J. Wyman681b2a32021-04-20 22:31:22 +0000754 // TODO: Immediately trying to read or write the "files" causes
755 // read or write failures.
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500756 using namespace std::chrono_literals;
757 std::this_thread::sleep_for(20ms);
Brandon Wyman9564e942020-11-10 14:01:42 -0600758 pmbusIntf->findHwmonDir();
Brandon Wyman59a35792020-06-04 12:37:40 -0500759 onOffConfig(phosphor::pmbus::ON_OFF_CONFIG_CONTROL_PIN_ONLY);
Brandon Wymanaed1f752019-11-25 18:10:52 -0600760 clearFaults();
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500761 updateInventory();
Brandon Wymanaed1f752019-11-25 18:10:52 -0600762 }
763 else
764 {
765 present = false;
766
767 // Clear out the now outdated inventory properties
768 updateInventory();
769 }
Matt Spinler0975eaf2022-02-14 15:38:30 -0600770 checkAvailability();
Brandon Wymanaed1f752019-11-25 18:10:52 -0600771 }
772}
773
Brandon Wyman9a507db2021-02-25 16:15:22 -0600774void PowerSupply::inventoryAdded(sdbusplus::message::message& msg)
775{
776 sdbusplus::message::object_path path;
777 msg.read(path);
778 // Make sure the signal is for the PSU inventory path
779 if (path == inventoryPath)
780 {
781 std::map<std::string, std::map<std::string, std::variant<bool>>>
782 interfaces;
783 // Get map of interfaces and their properties
784 msg.read(interfaces);
785
786 auto properties = interfaces.find(INVENTORY_IFACE);
787 if (properties != interfaces.end())
788 {
789 auto property = properties->second.find(PRESENT_PROP);
790 if (property != properties->second.end())
791 {
792 present = std::get<bool>(property->second);
793
794 log<level::INFO>(fmt::format("Power Supply {} Present {}",
795 inventoryPath, present)
796 .c_str());
797
798 updateInventory();
Matt Spinler0975eaf2022-02-14 15:38:30 -0600799 checkAvailability();
Brandon Wyman9a507db2021-02-25 16:15:22 -0600800 }
801 }
802 }
803}
804
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500805void PowerSupply::updateInventory()
806{
807 using namespace phosphor::pmbus;
808
Chanh Nguyenc12c53b2021-04-06 17:24:47 +0700809#if IBM_VPD
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500810 std::string ccin;
811 std::string pn;
812 std::string fn;
813 std::string header;
814 std::string sn;
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500815 using PropertyMap =
George Liu070c1bc2020-10-12 11:28:01 +0800816 std::map<std::string,
817 std::variant<std::string, std::vector<uint8_t>, bool>>;
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500818 PropertyMap assetProps;
George Liu070c1bc2020-10-12 11:28:01 +0800819 PropertyMap operProps;
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500820 PropertyMap versionProps;
821 PropertyMap ipzvpdDINFProps;
822 PropertyMap ipzvpdVINIProps;
823 using InterfaceMap = std::map<std::string, PropertyMap>;
824 InterfaceMap interfaces;
825 using ObjectMap = std::map<sdbusplus::message::object_path, InterfaceMap>;
826 ObjectMap object;
827#endif
B. J. Wyman681b2a32021-04-20 22:31:22 +0000828 log<level::DEBUG>(
829 fmt::format("updateInventory() inventoryPath: {}", inventoryPath)
830 .c_str());
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500831
832 if (present)
833 {
834 // TODO: non-IBM inventory updates?
835
Chanh Nguyenc12c53b2021-04-06 17:24:47 +0700836#if IBM_VPD
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500837 try
838 {
839 ccin = pmbusIntf->readString(CCIN, Type::HwmonDeviceDebug);
840 assetProps.emplace(MODEL_PROP, ccin);
Adriana Kobylak572a9052021-03-30 15:58:07 +0000841 modelName = ccin;
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500842 }
Patrick Williamsc1d4de52021-10-06 12:45:57 -0500843 catch (const ReadFailure& e)
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500844 {
B. J. Wyman681b2a32021-04-20 22:31:22 +0000845 // Ignore the read failure, let pmbus code indicate failure,
846 // path...
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500847 // TODO - ibm918
848 // https://github.com/openbmc/docs/blob/master/designs/vpd-collection.md
849 // The BMC must log errors if any of the VPD cannot be properly
850 // parsed or fails ECC checks.
851 }
852
853 try
854 {
855 pn = pmbusIntf->readString(PART_NUMBER, Type::HwmonDeviceDebug);
856 assetProps.emplace(PN_PROP, pn);
857 }
Patrick Williamsc1d4de52021-10-06 12:45:57 -0500858 catch (const ReadFailure& e)
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500859 {
B. J. Wyman681b2a32021-04-20 22:31:22 +0000860 // Ignore the read failure, let pmbus code indicate failure,
861 // path...
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500862 }
863
864 try
865 {
866 fn = pmbusIntf->readString(FRU_NUMBER, Type::HwmonDeviceDebug);
Brandon Wymana169b0f2021-12-07 20:18:06 +0000867 assetProps.emplace(SPARE_PN_PROP, fn);
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500868 }
Patrick Williamsc1d4de52021-10-06 12:45:57 -0500869 catch (const ReadFailure& e)
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500870 {
B. J. Wyman681b2a32021-04-20 22:31:22 +0000871 // Ignore the read failure, let pmbus code indicate failure,
872 // path...
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500873 }
874
875 try
876 {
877 header =
878 pmbusIntf->readString(SERIAL_HEADER, Type::HwmonDeviceDebug);
879 sn = pmbusIntf->readString(SERIAL_NUMBER, Type::HwmonDeviceDebug);
Mike Capps1cb0f132022-03-14 11:40:30 -0400880 assetProps.emplace(SN_PROP, header + sn);
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500881 }
Patrick Williamsc1d4de52021-10-06 12:45:57 -0500882 catch (const ReadFailure& e)
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500883 {
B. J. Wyman681b2a32021-04-20 22:31:22 +0000884 // Ignore the read failure, let pmbus code indicate failure,
885 // path...
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500886 }
887
888 try
889 {
Brandon Wymanc9efe412020-10-09 15:42:50 -0500890 fwVersion =
891 pmbusIntf->readString(FW_VERSION, Type::HwmonDeviceDebug);
892 versionProps.emplace(VERSION_PROP, fwVersion);
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500893 }
Patrick Williamsc1d4de52021-10-06 12:45:57 -0500894 catch (const ReadFailure& e)
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500895 {
B. J. Wyman681b2a32021-04-20 22:31:22 +0000896 // Ignore the read failure, let pmbus code indicate failure,
897 // path...
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500898 }
899
900 ipzvpdVINIProps.emplace("CC",
901 std::vector<uint8_t>(ccin.begin(), ccin.end()));
902 ipzvpdVINIProps.emplace("PN",
903 std::vector<uint8_t>(pn.begin(), pn.end()));
904 ipzvpdVINIProps.emplace("FN",
905 std::vector<uint8_t>(fn.begin(), fn.end()));
Brandon Wyman33d492f2022-03-23 20:45:17 +0000906 std::string header_sn = header + sn;
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500907 ipzvpdVINIProps.emplace(
908 "SN", std::vector<uint8_t>(header_sn.begin(), header_sn.end()));
909 std::string description = "IBM PS";
910 ipzvpdVINIProps.emplace(
911 "DR", std::vector<uint8_t>(description.begin(), description.end()));
912
Ben Tynerf8d8c462022-01-27 16:09:45 -0600913 // Populate the VINI Resource Type (RT) keyword
914 ipzvpdVINIProps.emplace("RT", std::vector<uint8_t>{'V', 'I', 'N', 'I'});
915
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500916 // Update the Resource Identifier (RI) keyword
917 // 2 byte FRC: 0x0003
918 // 2 byte RID: 0x1000, 0x1001...
919 std::uint8_t num = std::stoul(
920 inventoryPath.substr(inventoryPath.size() - 1, 1), nullptr, 0);
921 std::vector<uint8_t> ri{0x00, 0x03, 0x10, num};
922 ipzvpdDINFProps.emplace("RI", ri);
923
924 // Fill in the FRU Label (FL) keyword.
925 std::string fl = "E";
926 fl.push_back(inventoryPath.back());
927 fl.resize(FL_KW_SIZE, ' ');
928 ipzvpdDINFProps.emplace("FL",
929 std::vector<uint8_t>(fl.begin(), fl.end()));
930
Ben Tynerf8d8c462022-01-27 16:09:45 -0600931 // Populate the DINF Resource Type (RT) keyword
932 ipzvpdDINFProps.emplace("RT", std::vector<uint8_t>{'D', 'I', 'N', 'F'});
933
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500934 interfaces.emplace(ASSET_IFACE, std::move(assetProps));
935 interfaces.emplace(VERSION_IFACE, std::move(versionProps));
936 interfaces.emplace(DINF_IFACE, std::move(ipzvpdDINFProps));
937 interfaces.emplace(VINI_IFACE, std::move(ipzvpdVINIProps));
938
George Liu070c1bc2020-10-12 11:28:01 +0800939 // Update the Functional
940 operProps.emplace(FUNCTIONAL_PROP, present);
941 interfaces.emplace(OPERATIONAL_STATE_IFACE, std::move(operProps));
942
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500943 auto path = inventoryPath.substr(strlen(INVENTORY_OBJ_PATH));
944 object.emplace(path, std::move(interfaces));
945
946 try
947 {
948 auto service =
949 util::getService(INVENTORY_OBJ_PATH, INVENTORY_MGR_IFACE, bus);
950
951 if (service.empty())
952 {
953 log<level::ERR>("Unable to get inventory manager service");
954 return;
955 }
956
957 auto method =
958 bus.new_method_call(service.c_str(), INVENTORY_OBJ_PATH,
959 INVENTORY_MGR_IFACE, "Notify");
960
961 method.append(std::move(object));
962
963 auto reply = bus.call(method);
964 }
Patrick Williamsc1d4de52021-10-06 12:45:57 -0500965 catch (const std::exception& e)
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500966 {
Jay Meyer6a3fd2c2020-08-25 16:37:16 -0500967 log<level::ERR>(
968 std::string(e.what() + std::string(" PATH=") + inventoryPath)
969 .c_str());
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500970 }
971#endif
972 }
973}
974
Brandon Wymanc3324422022-03-24 20:30:57 +0000975void PowerSupply::setupInputHistory()
976{
977 if (bindPath.string().find("ibm-cffps") != std::string::npos)
978 {
979 // Do not enable input history for power supplies that are missing
980 if (present)
981 {
982 inputHistorySupported = true;
983 log<level::INFO>(
984 fmt::format("{} INPUT_HISTORY enabled", shortName).c_str());
985
986 std::string name{fmt::format("{}_input_power", shortName)};
987
988 historyObjectPath =
989 std::string{INPUT_HISTORY_SENSOR_ROOT} + '/' + name;
990
991 // If the power supply was present, we created the recordManager.
992 // If it then went missing, the recordManager is still there.
993 // If it then is reinserted, we should be able to use the
994 // recordManager that was allocated when it was initially present.
995 if (!recordManager)
996 {
997 recordManager = std::make_unique<history::RecordManager>(
998 INPUT_HISTORY_MAX_RECORDS);
999 }
1000
1001 if (!average)
1002 {
1003 auto avgPath = historyObjectPath + '/' + history::Average::name;
1004 average = std::make_unique<history::Average>(bus, avgPath);
1005 log<level::DEBUG>(
1006 fmt::format("{} avgPath: {}", shortName, avgPath).c_str());
1007 }
Brandon Wyman18a24d92022-04-19 22:48:34 +00001008
Brandon Wymanc3324422022-03-24 20:30:57 +00001009 if (!maximum)
1010 {
1011 auto maxPath = historyObjectPath + '/' + history::Maximum::name;
1012 maximum = std::make_unique<history::Maximum>(bus, maxPath);
1013 log<level::DEBUG>(
1014 fmt::format("{} maxPath: {}", shortName, maxPath).c_str());
1015 }
1016
1017 log<level::DEBUG>(fmt::format("{} historyObjectPath: {}", shortName,
1018 historyObjectPath)
1019 .c_str());
1020 }
1021 }
1022 else
1023 {
1024 inputHistorySupported = false;
1025 }
1026}
1027
1028void PowerSupply::updateHistory()
1029{
1030 if (!recordManager)
1031 {
1032 // Not enabled
1033 return;
1034 }
1035
1036 if (!present)
1037 {
1038 // Cannot read when not present
1039 return;
1040 }
1041
1042 // Read just the most recent average/max record
1043 auto data =
1044 pmbusIntf->readBinary(INPUT_HISTORY, pmbus::Type::HwmonDeviceDebug,
1045 history::RecordManager::RAW_RECORD_SIZE);
1046
1047 // Update D-Bus only if something changed (a new record ID, or cleared out)
1048 auto changed = recordManager->add(data);
1049 if (changed)
1050 {
1051 average->values(std::move(recordManager->getAverageRecords()));
1052 maximum->values(std::move(recordManager->getMaximumRecords()));
1053 }
1054}
1055
Adriana Kobylak4175ffb2021-08-02 14:51:05 +00001056void PowerSupply::getInputVoltage(double& actualInputVoltage,
1057 int& inputVoltage) const
1058{
1059 using namespace phosphor::pmbus;
1060
1061 actualInputVoltage = in_input::VIN_VOLTAGE_0;
1062 inputVoltage = in_input::VIN_VOLTAGE_0;
1063
1064 if (present)
1065 {
1066 try
1067 {
1068 // Read input voltage in millivolts
1069 auto inputVoltageStr = pmbusIntf->readString(READ_VIN, Type::Hwmon);
1070
1071 // Convert to volts
1072 actualInputVoltage = std::stod(inputVoltageStr) / 1000;
1073
1074 // Calculate the voltage based on voltage thresholds
1075 if (actualInputVoltage < in_input::VIN_VOLTAGE_MIN)
1076 {
1077 inputVoltage = in_input::VIN_VOLTAGE_0;
1078 }
1079 else if (actualInputVoltage < in_input::VIN_VOLTAGE_110_THRESHOLD)
1080 {
1081 inputVoltage = in_input::VIN_VOLTAGE_110;
1082 }
1083 else
1084 {
1085 inputVoltage = in_input::VIN_VOLTAGE_220;
1086 }
1087 }
1088 catch (const std::exception& e)
1089 {
1090 log<level::ERR>(
Brandon Wyman321a6152022-03-19 00:11:44 +00001091 fmt::format("{} READ_VIN read error: {}", shortName, e.what())
1092 .c_str());
Adriana Kobylak4175ffb2021-08-02 14:51:05 +00001093 }
1094 }
1095}
1096
Matt Spinler0975eaf2022-02-14 15:38:30 -06001097void PowerSupply::checkAvailability()
1098{
1099 bool origAvailability = available;
1100 available = present && !hasInputFault() && !hasVINUVFault() &&
1101 !hasPSKillFault() && !hasIoutOCFault();
1102
1103 if (origAvailability != available)
1104 {
1105 auto invpath = inventoryPath.substr(strlen(INVENTORY_OBJ_PATH));
1106 phosphor::power::psu::setAvailable(bus, invpath, available);
Matt Spinlerca1e9ea2022-02-18 14:03:08 -06001107
1108 // Check if the health rollup needs to change based on the
1109 // new availability value.
1110 phosphor::power::psu::handleChassisHealthRollup(bus, inventoryPath,
1111 !available);
Matt Spinler0975eaf2022-02-14 15:38:30 -06001112 }
1113}
1114
Brandon Wyman3f1242f2020-01-28 13:11:25 -06001115} // namespace phosphor::power::psu