blob: 9aa271987245357d45b1643cd43ee0d8d9eb60c3 [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 }
Brandon Wymanae35ac52022-05-23 22:33:40 +0000641 else if (std::abs(actualInputVoltageOld - actualInputVoltage) >
642 10.0)
Brandon Wyman4fc191f2022-03-10 23:07:13 +0000643 {
644 log<level::INFO>(
645 fmt::format(
646 "{} actualInputVoltageOld = {} actualInputVoltage = {}",
647 shortName, actualInputVoltageOld, actualInputVoltage)
648 .c_str());
649 }
Matt Spinler0975eaf2022-02-14 15:38:30 -0600650
651 checkAvailability();
Brandon Wymanc3324422022-03-24 20:30:57 +0000652
653 if (inputHistorySupported)
654 {
655 updateHistory();
656 }
Brandon Wyman3f1242f2020-01-28 13:11:25 -0600657 }
Patrick Williamsc1d4de52021-10-06 12:45:57 -0500658 catch (const ReadFailure& e)
Brandon Wyman3f1242f2020-01-28 13:11:25 -0600659 {
Brandon Wyman32453e92021-12-15 19:00:14 +0000660 if (readFail < SIZE_MAX)
661 {
662 readFail++;
663 }
664 if (readFail == LOG_LIMIT)
665 {
666 phosphor::logging::commit<ReadFailure>();
667 }
Brandon Wyman3f1242f2020-01-28 13:11:25 -0600668 }
669 }
670}
671
Brandon Wyman59a35792020-06-04 12:37:40 -0500672void PowerSupply::onOffConfig(uint8_t data)
673{
674 using namespace phosphor::pmbus;
675
676 if (present)
677 {
678 log<level::INFO>("ON_OFF_CONFIG write", entry("DATA=0x%02X", data));
679 try
680 {
681 std::vector<uint8_t> configData{data};
682 pmbusIntf->writeBinary(ON_OFF_CONFIG, configData,
683 Type::HwmonDeviceDebug);
684 }
685 catch (...)
686 {
687 // The underlying code in writeBinary will log a message to the
B. J. Wyman681b2a32021-04-20 22:31:22 +0000688 // journal if the write fails. If the ON_OFF_CONFIG is not setup
689 // as desired, later fault detection and analysis code should
690 // catch any of the fall out. We should not need to terminate
691 // the application if this write fails.
Brandon Wyman59a35792020-06-04 12:37:40 -0500692 }
693 }
694}
695
Brandon Wyman3225a452022-03-18 18:51:49 +0000696void PowerSupply::clearVinUVFault()
697{
698 // Read in1_lcrit_alarm to clear bits 3 and 4 of STATUS_INPUT.
699 // The fault bits in STAUTS_INPUT roll-up to STATUS_WORD. Clearing those
700 // bits in STATUS_INPUT should result in the corresponding STATUS_WORD bits
701 // also clearing.
702 //
703 // Do not care about return value. Should be 1 if active, 0 if not.
704 static_cast<void>(
705 pmbusIntf->read("in1_lcrit_alarm", phosphor::pmbus::Type::Hwmon));
706 vinUVFault = 0;
707}
708
Brandon Wyman3c208462020-05-13 16:25:58 -0500709void PowerSupply::clearFaults()
710{
Brandon Wyman82affd92021-11-24 19:12:49 +0000711 log<level::DEBUG>(
712 fmt::format("clearFaults() inventoryPath: {}", inventoryPath).c_str());
Brandon Wyman5474c912021-02-23 14:39:43 -0600713 faultLogged = false;
Brandon Wyman3c208462020-05-13 16:25:58 -0500714 // The PMBus device driver does not allow for writing CLEAR_FAULTS
715 // directly. However, the pmbus hwmon device driver code will send a
716 // CLEAR_FAULTS after reading from any of the hwmon "files" in sysfs, so
717 // reading in1_input should result in clearing the fault bits in
718 // STATUS_BYTE/STATUS_WORD.
719 // I do not care what the return value is.
Brandon Wyman11151532020-11-10 13:45:57 -0600720 if (present)
Brandon Wyman3c208462020-05-13 16:25:58 -0500721 {
Brandon Wymane3f7ad22021-12-21 20:27:45 +0000722 clearFaultFlags();
Matt Spinler0975eaf2022-02-14 15:38:30 -0600723 checkAvailability();
Brandon Wyman9564e942020-11-10 14:01:42 -0600724 readFail = 0;
Brandon Wyman9564e942020-11-10 14:01:42 -0600725
Brandon Wyman11151532020-11-10 13:45:57 -0600726 try
727 {
Brandon Wyman3225a452022-03-18 18:51:49 +0000728 clearVinUVFault();
Brandon Wyman11151532020-11-10 13:45:57 -0600729 static_cast<void>(
730 pmbusIntf->read("in1_input", phosphor::pmbus::Type::Hwmon));
731 }
Patrick Williamsc1d4de52021-10-06 12:45:57 -0500732 catch (const ReadFailure& e)
Brandon Wyman11151532020-11-10 13:45:57 -0600733 {
734 // Since I do not care what the return value is, I really do not
B. J. Wyman681b2a32021-04-20 22:31:22 +0000735 // care much if it gets a ReadFailure either. However, this
736 // should not prevent the application from continuing to run, so
737 // catching the read failure.
Brandon Wyman11151532020-11-10 13:45:57 -0600738 }
Brandon Wyman3c208462020-05-13 16:25:58 -0500739 }
740}
741
Brandon Wymanaed1f752019-11-25 18:10:52 -0600742void PowerSupply::inventoryChanged(sdbusplus::message::message& msg)
743{
744 std::string msgSensor;
Patrick Williamsabe49412020-05-13 17:59:47 -0500745 std::map<std::string, std::variant<uint32_t, bool>> msgData;
Brandon Wymanaed1f752019-11-25 18:10:52 -0600746 msg.read(msgSensor, msgData);
747
748 // Check if it was the Present property that changed.
749 auto valPropMap = msgData.find(PRESENT_PROP);
750 if (valPropMap != msgData.end())
751 {
752 if (std::get<bool>(valPropMap->second))
753 {
754 present = true;
B. J. Wyman681b2a32021-04-20 22:31:22 +0000755 // TODO: Immediately trying to read or write the "files" causes
756 // read or write failures.
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500757 using namespace std::chrono_literals;
758 std::this_thread::sleep_for(20ms);
Brandon Wyman9564e942020-11-10 14:01:42 -0600759 pmbusIntf->findHwmonDir();
Brandon Wyman59a35792020-06-04 12:37:40 -0500760 onOffConfig(phosphor::pmbus::ON_OFF_CONFIG_CONTROL_PIN_ONLY);
Brandon Wymanaed1f752019-11-25 18:10:52 -0600761 clearFaults();
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500762 updateInventory();
Brandon Wymanaed1f752019-11-25 18:10:52 -0600763 }
764 else
765 {
766 present = false;
767
768 // Clear out the now outdated inventory properties
769 updateInventory();
770 }
Matt Spinler0975eaf2022-02-14 15:38:30 -0600771 checkAvailability();
Brandon Wymanaed1f752019-11-25 18:10:52 -0600772 }
773}
774
Brandon Wyman9a507db2021-02-25 16:15:22 -0600775void PowerSupply::inventoryAdded(sdbusplus::message::message& msg)
776{
777 sdbusplus::message::object_path path;
778 msg.read(path);
779 // Make sure the signal is for the PSU inventory path
780 if (path == inventoryPath)
781 {
782 std::map<std::string, std::map<std::string, std::variant<bool>>>
783 interfaces;
784 // Get map of interfaces and their properties
785 msg.read(interfaces);
786
787 auto properties = interfaces.find(INVENTORY_IFACE);
788 if (properties != interfaces.end())
789 {
790 auto property = properties->second.find(PRESENT_PROP);
791 if (property != properties->second.end())
792 {
793 present = std::get<bool>(property->second);
794
795 log<level::INFO>(fmt::format("Power Supply {} Present {}",
796 inventoryPath, present)
797 .c_str());
798
799 updateInventory();
Matt Spinler0975eaf2022-02-14 15:38:30 -0600800 checkAvailability();
Brandon Wyman9a507db2021-02-25 16:15:22 -0600801 }
802 }
803 }
804}
805
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500806void PowerSupply::updateInventory()
807{
808 using namespace phosphor::pmbus;
809
Chanh Nguyenc12c53b2021-04-06 17:24:47 +0700810#if IBM_VPD
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500811 std::string ccin;
812 std::string pn;
813 std::string fn;
814 std::string header;
815 std::string sn;
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500816 using PropertyMap =
George Liu070c1bc2020-10-12 11:28:01 +0800817 std::map<std::string,
818 std::variant<std::string, std::vector<uint8_t>, bool>>;
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500819 PropertyMap assetProps;
George Liu070c1bc2020-10-12 11:28:01 +0800820 PropertyMap operProps;
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500821 PropertyMap versionProps;
822 PropertyMap ipzvpdDINFProps;
823 PropertyMap ipzvpdVINIProps;
824 using InterfaceMap = std::map<std::string, PropertyMap>;
825 InterfaceMap interfaces;
826 using ObjectMap = std::map<sdbusplus::message::object_path, InterfaceMap>;
827 ObjectMap object;
828#endif
B. J. Wyman681b2a32021-04-20 22:31:22 +0000829 log<level::DEBUG>(
830 fmt::format("updateInventory() inventoryPath: {}", inventoryPath)
831 .c_str());
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500832
833 if (present)
834 {
835 // TODO: non-IBM inventory updates?
836
Chanh Nguyenc12c53b2021-04-06 17:24:47 +0700837#if IBM_VPD
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500838 try
839 {
840 ccin = pmbusIntf->readString(CCIN, Type::HwmonDeviceDebug);
841 assetProps.emplace(MODEL_PROP, ccin);
Adriana Kobylak572a9052021-03-30 15:58:07 +0000842 modelName = ccin;
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500843 }
Patrick Williamsc1d4de52021-10-06 12:45:57 -0500844 catch (const ReadFailure& e)
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500845 {
B. J. Wyman681b2a32021-04-20 22:31:22 +0000846 // Ignore the read failure, let pmbus code indicate failure,
847 // path...
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500848 // TODO - ibm918
849 // https://github.com/openbmc/docs/blob/master/designs/vpd-collection.md
850 // The BMC must log errors if any of the VPD cannot be properly
851 // parsed or fails ECC checks.
852 }
853
854 try
855 {
856 pn = pmbusIntf->readString(PART_NUMBER, Type::HwmonDeviceDebug);
857 assetProps.emplace(PN_PROP, pn);
858 }
Patrick Williamsc1d4de52021-10-06 12:45:57 -0500859 catch (const ReadFailure& e)
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500860 {
B. J. Wyman681b2a32021-04-20 22:31:22 +0000861 // Ignore the read failure, let pmbus code indicate failure,
862 // path...
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500863 }
864
865 try
866 {
867 fn = pmbusIntf->readString(FRU_NUMBER, Type::HwmonDeviceDebug);
Brandon Wymana169b0f2021-12-07 20:18:06 +0000868 assetProps.emplace(SPARE_PN_PROP, fn);
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500869 }
Patrick Williamsc1d4de52021-10-06 12:45:57 -0500870 catch (const ReadFailure& e)
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500871 {
B. J. Wyman681b2a32021-04-20 22:31:22 +0000872 // Ignore the read failure, let pmbus code indicate failure,
873 // path...
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500874 }
875
876 try
877 {
878 header =
879 pmbusIntf->readString(SERIAL_HEADER, Type::HwmonDeviceDebug);
880 sn = pmbusIntf->readString(SERIAL_NUMBER, Type::HwmonDeviceDebug);
Mike Capps1cb0f132022-03-14 11:40:30 -0400881 assetProps.emplace(SN_PROP, header + sn);
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500882 }
Patrick Williamsc1d4de52021-10-06 12:45:57 -0500883 catch (const ReadFailure& e)
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500884 {
B. J. Wyman681b2a32021-04-20 22:31:22 +0000885 // Ignore the read failure, let pmbus code indicate failure,
886 // path...
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500887 }
888
889 try
890 {
Brandon Wymanc9efe412020-10-09 15:42:50 -0500891 fwVersion =
892 pmbusIntf->readString(FW_VERSION, Type::HwmonDeviceDebug);
893 versionProps.emplace(VERSION_PROP, fwVersion);
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500894 }
Patrick Williamsc1d4de52021-10-06 12:45:57 -0500895 catch (const ReadFailure& e)
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500896 {
B. J. Wyman681b2a32021-04-20 22:31:22 +0000897 // Ignore the read failure, let pmbus code indicate failure,
898 // path...
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500899 }
900
901 ipzvpdVINIProps.emplace("CC",
902 std::vector<uint8_t>(ccin.begin(), ccin.end()));
903 ipzvpdVINIProps.emplace("PN",
904 std::vector<uint8_t>(pn.begin(), pn.end()));
905 ipzvpdVINIProps.emplace("FN",
906 std::vector<uint8_t>(fn.begin(), fn.end()));
Brandon Wyman33d492f2022-03-23 20:45:17 +0000907 std::string header_sn = header + sn;
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500908 ipzvpdVINIProps.emplace(
909 "SN", std::vector<uint8_t>(header_sn.begin(), header_sn.end()));
910 std::string description = "IBM PS";
911 ipzvpdVINIProps.emplace(
912 "DR", std::vector<uint8_t>(description.begin(), description.end()));
913
Ben Tynerf8d8c462022-01-27 16:09:45 -0600914 // Populate the VINI Resource Type (RT) keyword
915 ipzvpdVINIProps.emplace("RT", std::vector<uint8_t>{'V', 'I', 'N', 'I'});
916
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500917 // Update the Resource Identifier (RI) keyword
918 // 2 byte FRC: 0x0003
919 // 2 byte RID: 0x1000, 0x1001...
920 std::uint8_t num = std::stoul(
921 inventoryPath.substr(inventoryPath.size() - 1, 1), nullptr, 0);
922 std::vector<uint8_t> ri{0x00, 0x03, 0x10, num};
923 ipzvpdDINFProps.emplace("RI", ri);
924
925 // Fill in the FRU Label (FL) keyword.
926 std::string fl = "E";
927 fl.push_back(inventoryPath.back());
928 fl.resize(FL_KW_SIZE, ' ');
929 ipzvpdDINFProps.emplace("FL",
930 std::vector<uint8_t>(fl.begin(), fl.end()));
931
Ben Tynerf8d8c462022-01-27 16:09:45 -0600932 // Populate the DINF Resource Type (RT) keyword
933 ipzvpdDINFProps.emplace("RT", std::vector<uint8_t>{'D', 'I', 'N', 'F'});
934
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500935 interfaces.emplace(ASSET_IFACE, std::move(assetProps));
936 interfaces.emplace(VERSION_IFACE, std::move(versionProps));
937 interfaces.emplace(DINF_IFACE, std::move(ipzvpdDINFProps));
938 interfaces.emplace(VINI_IFACE, std::move(ipzvpdVINIProps));
939
George Liu070c1bc2020-10-12 11:28:01 +0800940 // Update the Functional
941 operProps.emplace(FUNCTIONAL_PROP, present);
942 interfaces.emplace(OPERATIONAL_STATE_IFACE, std::move(operProps));
943
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500944 auto path = inventoryPath.substr(strlen(INVENTORY_OBJ_PATH));
945 object.emplace(path, std::move(interfaces));
946
947 try
948 {
949 auto service =
950 util::getService(INVENTORY_OBJ_PATH, INVENTORY_MGR_IFACE, bus);
951
952 if (service.empty())
953 {
954 log<level::ERR>("Unable to get inventory manager service");
955 return;
956 }
957
958 auto method =
959 bus.new_method_call(service.c_str(), INVENTORY_OBJ_PATH,
960 INVENTORY_MGR_IFACE, "Notify");
961
962 method.append(std::move(object));
963
964 auto reply = bus.call(method);
965 }
Patrick Williamsc1d4de52021-10-06 12:45:57 -0500966 catch (const std::exception& e)
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500967 {
Jay Meyer6a3fd2c2020-08-25 16:37:16 -0500968 log<level::ERR>(
969 std::string(e.what() + std::string(" PATH=") + inventoryPath)
970 .c_str());
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500971 }
972#endif
973 }
974}
975
Brandon Wymanae35ac52022-05-23 22:33:40 +0000976auto PowerSupply::getMaxPowerOut() const
977{
978 using namespace phosphor::pmbus;
979
980 auto maxPowerOut = 0;
981
982 if (present)
983 {
984 try
985 {
986 // Read max_power_out, should be direct format
987 auto maxPowerOutStr =
988 pmbusIntf->readString(MFR_POUT_MAX, Type::HwmonDeviceDebug);
989 log<level::INFO>(fmt::format("{} MFR_POUT_MAX read {}", shortName,
990 maxPowerOutStr)
991 .c_str());
992 maxPowerOut = std::stod(maxPowerOutStr);
993 }
994 catch (const std::exception& e)
995 {
996 log<level::ERR>(fmt::format("{} MFR_POUT_MAX read error: {}",
997 shortName, e.what())
998 .c_str());
999 }
1000 }
1001
1002 return maxPowerOut;
1003}
1004
Brandon Wymanc3324422022-03-24 20:30:57 +00001005void PowerSupply::setupInputHistory()
1006{
1007 if (bindPath.string().find("ibm-cffps") != std::string::npos)
1008 {
Brandon Wymanae35ac52022-05-23 22:33:40 +00001009 auto maxPowerOut = getMaxPowerOut();
1010
1011 if (maxPowerOut != phosphor::pmbus::IBM_CFFPS_1400W)
Brandon Wymanc3324422022-03-24 20:30:57 +00001012 {
Brandon Wymanae35ac52022-05-23 22:33:40 +00001013 // Do not enable input history for power supplies that are missing
1014 if (present)
1015 {
1016 inputHistorySupported = true;
1017 log<level::INFO>(
1018 fmt::format("{} INPUT_HISTORY enabled", shortName).c_str());
1019
1020 std::string name{fmt::format("{}_input_power", shortName)};
1021
1022 historyObjectPath =
1023 std::string{INPUT_HISTORY_SENSOR_ROOT} + '/' + name;
1024
1025 // If the power supply was present, we created the
1026 // recordManager. If it then went missing, the recordManager is
1027 // still there. If it then is reinserted, we should be able to
1028 // use the recordManager that was allocated when it was
1029 // initially present.
1030 if (!recordManager)
1031 {
1032 recordManager = std::make_unique<history::RecordManager>(
1033 INPUT_HISTORY_MAX_RECORDS);
1034 }
1035
1036 if (!average)
1037 {
1038 auto avgPath =
1039 historyObjectPath + '/' + history::Average::name;
1040 average = std::make_unique<history::Average>(bus, avgPath);
1041 log<level::DEBUG>(
1042 fmt::format("{} avgPath: {}", shortName, avgPath)
1043 .c_str());
1044 }
1045
1046 if (!maximum)
1047 {
1048 auto maxPath =
1049 historyObjectPath + '/' + history::Maximum::name;
1050 maximum = std::make_unique<history::Maximum>(bus, maxPath);
1051 log<level::DEBUG>(
1052 fmt::format("{} maxPath: {}", shortName, maxPath)
1053 .c_str());
1054 }
1055
1056 log<level::DEBUG>(fmt::format("{} historyObjectPath: {}",
1057 shortName, historyObjectPath)
1058 .c_str());
1059 }
1060 }
1061 else
1062 {
Brandon Wymanc3324422022-03-24 20:30:57 +00001063 log<level::INFO>(
Brandon Wymanae35ac52022-05-23 22:33:40 +00001064 fmt::format("{} INPUT_HISTORY DISABLED. max_power_out: {}",
1065 shortName, maxPowerOut)
1066 .c_str());
1067 inputHistorySupported = false;
Brandon Wymanc3324422022-03-24 20:30:57 +00001068 }
1069 }
1070 else
1071 {
1072 inputHistorySupported = false;
1073 }
1074}
1075
1076void PowerSupply::updateHistory()
1077{
1078 if (!recordManager)
1079 {
1080 // Not enabled
1081 return;
1082 }
1083
1084 if (!present)
1085 {
1086 // Cannot read when not present
1087 return;
1088 }
1089
1090 // Read just the most recent average/max record
1091 auto data =
1092 pmbusIntf->readBinary(INPUT_HISTORY, pmbus::Type::HwmonDeviceDebug,
1093 history::RecordManager::RAW_RECORD_SIZE);
1094
Brandon Wymanae35ac52022-05-23 22:33:40 +00001095 // Update D-Bus only if something changed (a new record ID, or cleared
1096 // out)
Brandon Wymanc3324422022-03-24 20:30:57 +00001097 auto changed = recordManager->add(data);
1098 if (changed)
1099 {
1100 average->values(std::move(recordManager->getAverageRecords()));
1101 maximum->values(std::move(recordManager->getMaximumRecords()));
1102 }
1103}
1104
Adriana Kobylak4175ffb2021-08-02 14:51:05 +00001105void PowerSupply::getInputVoltage(double& actualInputVoltage,
1106 int& inputVoltage) const
1107{
1108 using namespace phosphor::pmbus;
1109
1110 actualInputVoltage = in_input::VIN_VOLTAGE_0;
1111 inputVoltage = in_input::VIN_VOLTAGE_0;
1112
1113 if (present)
1114 {
1115 try
1116 {
1117 // Read input voltage in millivolts
1118 auto inputVoltageStr = pmbusIntf->readString(READ_VIN, Type::Hwmon);
1119
1120 // Convert to volts
1121 actualInputVoltage = std::stod(inputVoltageStr) / 1000;
1122
1123 // Calculate the voltage based on voltage thresholds
1124 if (actualInputVoltage < in_input::VIN_VOLTAGE_MIN)
1125 {
1126 inputVoltage = in_input::VIN_VOLTAGE_0;
1127 }
1128 else if (actualInputVoltage < in_input::VIN_VOLTAGE_110_THRESHOLD)
1129 {
1130 inputVoltage = in_input::VIN_VOLTAGE_110;
1131 }
1132 else
1133 {
1134 inputVoltage = in_input::VIN_VOLTAGE_220;
1135 }
1136 }
1137 catch (const std::exception& e)
1138 {
1139 log<level::ERR>(
Brandon Wyman321a6152022-03-19 00:11:44 +00001140 fmt::format("{} READ_VIN read error: {}", shortName, e.what())
1141 .c_str());
Adriana Kobylak4175ffb2021-08-02 14:51:05 +00001142 }
1143 }
1144}
1145
Matt Spinler0975eaf2022-02-14 15:38:30 -06001146void PowerSupply::checkAvailability()
1147{
1148 bool origAvailability = available;
1149 available = present && !hasInputFault() && !hasVINUVFault() &&
1150 !hasPSKillFault() && !hasIoutOCFault();
1151
1152 if (origAvailability != available)
1153 {
1154 auto invpath = inventoryPath.substr(strlen(INVENTORY_OBJ_PATH));
1155 phosphor::power::psu::setAvailable(bus, invpath, available);
Matt Spinlerca1e9ea2022-02-18 14:03:08 -06001156
1157 // Check if the health rollup needs to change based on the
1158 // new availability value.
1159 phosphor::power::psu::handleChassisHealthRollup(bus, inventoryPath,
1160 !available);
Matt Spinler0975eaf2022-02-14 15:38:30 -06001161 }
1162}
1163
Brandon Wyman3f1242f2020-01-28 13:11:25 -06001164} // namespace phosphor::power::psu