blob: e9b4c830f775c6f8114a87a28ca84ceff64dba56 [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
Patrick Williams7354ce62022-07-22 19:26:56 -050033PowerSupply::PowerSupply(sdbusplus::bus_t& 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 Williams7354ce62022-07-22 19:26:56 -0500146 catch (const sdbusplus::exception_t& 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 {
Brandon Wyman6d469fd2022-06-15 16:58:21 +0000404 if (pgoodFault < PGOOD_DEGLITCH_LIMIT)
Brandon Wyman993b5542021-12-21 22:55:16 +0000405 {
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
Patrick Williams7354ce62022-07-22 19:26:56 -0500742void PowerSupply::inventoryChanged(sdbusplus::message_t& msg)
Brandon Wymanaed1f752019-11-25 18:10:52 -0600743{
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
Patrick Williams7354ce62022-07-22 19:26:56 -0500775void PowerSupply::inventoryAdded(sdbusplus::message_t& msg)
Brandon Wyman9a507db2021-02-25 16:15:22 -0600776{
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 Wyman8393f462022-06-28 16:06:46 +0000806auto PowerSupply::readVPDValue(const std::string& vpdName,
807 const phosphor::pmbus::Type& type,
808 const std::size_t& vpdSize)
809{
810 std::string vpdValue;
811
812 try
813 {
814 vpdValue = pmbusIntf->readString(vpdName, type);
815 }
816 catch (const ReadFailure& e)
817 {
818 // Ignore the read failure, let pmbus code indicate failure,
819 // path...
820 // TODO - ibm918
821 // https://github.com/openbmc/docs/blob/master/designs/vpd-collection.md
822 // The BMC must log errors if any of the VPD cannot be properly
823 // parsed or fails ECC checks.
824 }
825
826 if (vpdValue.size() != vpdSize)
827 {
828 log<level::INFO>(fmt::format("{} {} resize needed. size: {}", shortName,
829 vpdName, vpdValue.size())
830 .c_str());
831 vpdValue.resize(vpdSize, ' ');
832 }
833
834 return vpdValue;
835}
836
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500837void PowerSupply::updateInventory()
838{
839 using namespace phosphor::pmbus;
840
Chanh Nguyenc12c53b2021-04-06 17:24:47 +0700841#if IBM_VPD
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500842 std::string pn;
843 std::string fn;
844 std::string header;
845 std::string sn;
Brandon Wyman8393f462022-06-28 16:06:46 +0000846 // The IBM power supply splits the full serial number into two parts.
847 // Each part is 6 bytes long, which should match up with SN_KW_SIZE.
848 const auto HEADER_SIZE = 6;
849 const auto SERIAL_SIZE = 6;
850 // The IBM PSU firmware version size is a bit complicated. It was originally
851 // 1-byte, per command. It was later expanded to 2-bytes per command, then
852 // up to 8-bytes per command. The device driver only reads up to 2 bytes per
853 // command, but combines all three of the 2-byte reads, or all 4 of the
854 // 1-byte reads into one string. So, the maximum size expected is 6 bytes.
855 const auto VERSION_SIZE = 6;
856
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500857 using PropertyMap =
George Liu070c1bc2020-10-12 11:28:01 +0800858 std::map<std::string,
859 std::variant<std::string, std::vector<uint8_t>, bool>>;
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500860 PropertyMap assetProps;
George Liu070c1bc2020-10-12 11:28:01 +0800861 PropertyMap operProps;
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500862 PropertyMap versionProps;
863 PropertyMap ipzvpdDINFProps;
864 PropertyMap ipzvpdVINIProps;
865 using InterfaceMap = std::map<std::string, PropertyMap>;
866 InterfaceMap interfaces;
867 using ObjectMap = std::map<sdbusplus::message::object_path, InterfaceMap>;
868 ObjectMap object;
869#endif
B. J. Wyman681b2a32021-04-20 22:31:22 +0000870 log<level::DEBUG>(
871 fmt::format("updateInventory() inventoryPath: {}", inventoryPath)
872 .c_str());
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500873
874 if (present)
875 {
876 // TODO: non-IBM inventory updates?
877
Chanh Nguyenc12c53b2021-04-06 17:24:47 +0700878#if IBM_VPD
Brandon Wyman8393f462022-06-28 16:06:46 +0000879 modelName = readVPDValue(CCIN, Type::HwmonDeviceDebug, CC_KW_SIZE);
880 assetProps.emplace(MODEL_PROP, modelName);
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500881
Brandon Wyman8393f462022-06-28 16:06:46 +0000882 pn = readVPDValue(PART_NUMBER, Type::HwmonDeviceDebug, PN_KW_SIZE);
883 assetProps.emplace(PN_PROP, pn);
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500884
Brandon Wyman8393f462022-06-28 16:06:46 +0000885 fn = readVPDValue(FRU_NUMBER, Type::HwmonDeviceDebug, FN_KW_SIZE);
886 assetProps.emplace(SPARE_PN_PROP, fn);
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500887
Brandon Wyman8393f462022-06-28 16:06:46 +0000888 header =
889 readVPDValue(SERIAL_HEADER, Type::HwmonDeviceDebug, HEADER_SIZE);
890 sn = readVPDValue(SERIAL_NUMBER, Type::HwmonDeviceDebug, SERIAL_SIZE);
891 assetProps.emplace(SN_PROP, header + sn);
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500892
Brandon Wyman8393f462022-06-28 16:06:46 +0000893 fwVersion =
894 readVPDValue(FW_VERSION, Type::HwmonDeviceDebug, VERSION_SIZE);
895 versionProps.emplace(VERSION_PROP, fwVersion);
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500896
Brandon Wyman8393f462022-06-28 16:06:46 +0000897 ipzvpdVINIProps.emplace(
898 "CC", std::vector<uint8_t>(modelName.begin(), modelName.end()));
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500899 ipzvpdVINIProps.emplace("PN",
900 std::vector<uint8_t>(pn.begin(), pn.end()));
901 ipzvpdVINIProps.emplace("FN",
902 std::vector<uint8_t>(fn.begin(), fn.end()));
Brandon Wyman33d492f2022-03-23 20:45:17 +0000903 std::string header_sn = header + sn;
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500904 ipzvpdVINIProps.emplace(
905 "SN", std::vector<uint8_t>(header_sn.begin(), header_sn.end()));
906 std::string description = "IBM PS";
907 ipzvpdVINIProps.emplace(
908 "DR", std::vector<uint8_t>(description.begin(), description.end()));
909
Ben Tynerf8d8c462022-01-27 16:09:45 -0600910 // Populate the VINI Resource Type (RT) keyword
911 ipzvpdVINIProps.emplace("RT", std::vector<uint8_t>{'V', 'I', 'N', 'I'});
912
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500913 // Update the Resource Identifier (RI) keyword
914 // 2 byte FRC: 0x0003
915 // 2 byte RID: 0x1000, 0x1001...
916 std::uint8_t num = std::stoul(
917 inventoryPath.substr(inventoryPath.size() - 1, 1), nullptr, 0);
918 std::vector<uint8_t> ri{0x00, 0x03, 0x10, num};
919 ipzvpdDINFProps.emplace("RI", ri);
920
921 // Fill in the FRU Label (FL) keyword.
922 std::string fl = "E";
923 fl.push_back(inventoryPath.back());
924 fl.resize(FL_KW_SIZE, ' ');
925 ipzvpdDINFProps.emplace("FL",
926 std::vector<uint8_t>(fl.begin(), fl.end()));
927
Ben Tynerf8d8c462022-01-27 16:09:45 -0600928 // Populate the DINF Resource Type (RT) keyword
929 ipzvpdDINFProps.emplace("RT", std::vector<uint8_t>{'D', 'I', 'N', 'F'});
930
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500931 interfaces.emplace(ASSET_IFACE, std::move(assetProps));
932 interfaces.emplace(VERSION_IFACE, std::move(versionProps));
933 interfaces.emplace(DINF_IFACE, std::move(ipzvpdDINFProps));
934 interfaces.emplace(VINI_IFACE, std::move(ipzvpdVINIProps));
935
George Liu070c1bc2020-10-12 11:28:01 +0800936 // Update the Functional
937 operProps.emplace(FUNCTIONAL_PROP, present);
938 interfaces.emplace(OPERATIONAL_STATE_IFACE, std::move(operProps));
939
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500940 auto path = inventoryPath.substr(strlen(INVENTORY_OBJ_PATH));
941 object.emplace(path, std::move(interfaces));
942
943 try
944 {
945 auto service =
946 util::getService(INVENTORY_OBJ_PATH, INVENTORY_MGR_IFACE, bus);
947
948 if (service.empty())
949 {
950 log<level::ERR>("Unable to get inventory manager service");
951 return;
952 }
953
954 auto method =
955 bus.new_method_call(service.c_str(), INVENTORY_OBJ_PATH,
956 INVENTORY_MGR_IFACE, "Notify");
957
958 method.append(std::move(object));
959
960 auto reply = bus.call(method);
961 }
Patrick Williamsc1d4de52021-10-06 12:45:57 -0500962 catch (const std::exception& e)
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500963 {
Jay Meyer6a3fd2c2020-08-25 16:37:16 -0500964 log<level::ERR>(
965 std::string(e.what() + std::string(" PATH=") + inventoryPath)
966 .c_str());
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500967 }
968#endif
969 }
970}
971
Brandon Wymanae35ac52022-05-23 22:33:40 +0000972auto PowerSupply::getMaxPowerOut() const
973{
974 using namespace phosphor::pmbus;
975
976 auto maxPowerOut = 0;
977
978 if (present)
979 {
980 try
981 {
982 // Read max_power_out, should be direct format
983 auto maxPowerOutStr =
984 pmbusIntf->readString(MFR_POUT_MAX, Type::HwmonDeviceDebug);
985 log<level::INFO>(fmt::format("{} MFR_POUT_MAX read {}", shortName,
986 maxPowerOutStr)
987 .c_str());
988 maxPowerOut = std::stod(maxPowerOutStr);
989 }
990 catch (const std::exception& e)
991 {
992 log<level::ERR>(fmt::format("{} MFR_POUT_MAX read error: {}",
993 shortName, e.what())
994 .c_str());
995 }
996 }
997
998 return maxPowerOut;
999}
1000
Brandon Wymanc3324422022-03-24 20:30:57 +00001001void PowerSupply::setupInputHistory()
1002{
1003 if (bindPath.string().find("ibm-cffps") != std::string::npos)
1004 {
Brandon Wymanae35ac52022-05-23 22:33:40 +00001005 auto maxPowerOut = getMaxPowerOut();
1006
1007 if (maxPowerOut != phosphor::pmbus::IBM_CFFPS_1400W)
Brandon Wymanc3324422022-03-24 20:30:57 +00001008 {
Brandon Wymanae35ac52022-05-23 22:33:40 +00001009 // Do not enable input history for power supplies that are missing
1010 if (present)
1011 {
1012 inputHistorySupported = true;
1013 log<level::INFO>(
1014 fmt::format("{} INPUT_HISTORY enabled", shortName).c_str());
1015
1016 std::string name{fmt::format("{}_input_power", shortName)};
1017
1018 historyObjectPath =
1019 std::string{INPUT_HISTORY_SENSOR_ROOT} + '/' + name;
1020
1021 // If the power supply was present, we created the
1022 // recordManager. If it then went missing, the recordManager is
1023 // still there. If it then is reinserted, we should be able to
1024 // use the recordManager that was allocated when it was
1025 // initially present.
1026 if (!recordManager)
1027 {
1028 recordManager = std::make_unique<history::RecordManager>(
1029 INPUT_HISTORY_MAX_RECORDS);
1030 }
1031
1032 if (!average)
1033 {
1034 auto avgPath =
1035 historyObjectPath + '/' + history::Average::name;
1036 average = std::make_unique<history::Average>(bus, avgPath);
1037 log<level::DEBUG>(
1038 fmt::format("{} avgPath: {}", shortName, avgPath)
1039 .c_str());
1040 }
1041
1042 if (!maximum)
1043 {
1044 auto maxPath =
1045 historyObjectPath + '/' + history::Maximum::name;
1046 maximum = std::make_unique<history::Maximum>(bus, maxPath);
1047 log<level::DEBUG>(
1048 fmt::format("{} maxPath: {}", shortName, maxPath)
1049 .c_str());
1050 }
1051
1052 log<level::DEBUG>(fmt::format("{} historyObjectPath: {}",
1053 shortName, historyObjectPath)
1054 .c_str());
1055 }
1056 }
1057 else
1058 {
Brandon Wymanc3324422022-03-24 20:30:57 +00001059 log<level::INFO>(
Brandon Wymanae35ac52022-05-23 22:33:40 +00001060 fmt::format("{} INPUT_HISTORY DISABLED. max_power_out: {}",
1061 shortName, maxPowerOut)
1062 .c_str());
1063 inputHistorySupported = false;
Brandon Wymanc3324422022-03-24 20:30:57 +00001064 }
1065 }
1066 else
1067 {
1068 inputHistorySupported = false;
1069 }
1070}
1071
1072void PowerSupply::updateHistory()
1073{
1074 if (!recordManager)
1075 {
1076 // Not enabled
1077 return;
1078 }
1079
1080 if (!present)
1081 {
1082 // Cannot read when not present
1083 return;
1084 }
1085
1086 // Read just the most recent average/max record
1087 auto data =
1088 pmbusIntf->readBinary(INPUT_HISTORY, pmbus::Type::HwmonDeviceDebug,
1089 history::RecordManager::RAW_RECORD_SIZE);
1090
Brandon Wymanae35ac52022-05-23 22:33:40 +00001091 // Update D-Bus only if something changed (a new record ID, or cleared
1092 // out)
Brandon Wymanc3324422022-03-24 20:30:57 +00001093 auto changed = recordManager->add(data);
1094 if (changed)
1095 {
1096 average->values(std::move(recordManager->getAverageRecords()));
1097 maximum->values(std::move(recordManager->getMaximumRecords()));
1098 }
1099}
1100
Adriana Kobylak4175ffb2021-08-02 14:51:05 +00001101void PowerSupply::getInputVoltage(double& actualInputVoltage,
1102 int& inputVoltage) const
1103{
1104 using namespace phosphor::pmbus;
1105
1106 actualInputVoltage = in_input::VIN_VOLTAGE_0;
1107 inputVoltage = in_input::VIN_VOLTAGE_0;
1108
1109 if (present)
1110 {
1111 try
1112 {
1113 // Read input voltage in millivolts
1114 auto inputVoltageStr = pmbusIntf->readString(READ_VIN, Type::Hwmon);
1115
1116 // Convert to volts
1117 actualInputVoltage = std::stod(inputVoltageStr) / 1000;
1118
1119 // Calculate the voltage based on voltage thresholds
1120 if (actualInputVoltage < in_input::VIN_VOLTAGE_MIN)
1121 {
1122 inputVoltage = in_input::VIN_VOLTAGE_0;
1123 }
1124 else if (actualInputVoltage < in_input::VIN_VOLTAGE_110_THRESHOLD)
1125 {
1126 inputVoltage = in_input::VIN_VOLTAGE_110;
1127 }
1128 else
1129 {
1130 inputVoltage = in_input::VIN_VOLTAGE_220;
1131 }
1132 }
1133 catch (const std::exception& e)
1134 {
1135 log<level::ERR>(
Brandon Wyman321a6152022-03-19 00:11:44 +00001136 fmt::format("{} READ_VIN read error: {}", shortName, e.what())
1137 .c_str());
Adriana Kobylak4175ffb2021-08-02 14:51:05 +00001138 }
1139 }
1140}
1141
Matt Spinler0975eaf2022-02-14 15:38:30 -06001142void PowerSupply::checkAvailability()
1143{
1144 bool origAvailability = available;
1145 available = present && !hasInputFault() && !hasVINUVFault() &&
1146 !hasPSKillFault() && !hasIoutOCFault();
1147
1148 if (origAvailability != available)
1149 {
1150 auto invpath = inventoryPath.substr(strlen(INVENTORY_OBJ_PATH));
1151 phosphor::power::psu::setAvailable(bus, invpath, available);
Matt Spinlerca1e9ea2022-02-18 14:03:08 -06001152
1153 // Check if the health rollup needs to change based on the
1154 // new availability value.
1155 phosphor::power::psu::handleChassisHealthRollup(bus, inventoryPath,
1156 !available);
Matt Spinler0975eaf2022-02-14 15:38:30 -06001157 }
1158}
1159
Brandon Wyman3f1242f2020-01-28 13:11:25 -06001160} // namespace phosphor::power::psu