blob: 0094ac692351788acad26b03ca3a8be3758131a5 [file] [log] [blame]
Brandon Wyman24e422f2017-07-25 19:40:14 -05001/**
2 * Copyright © 2017 IBM Corporation
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
George Liu690e7802019-08-23 11:04:01 +080016#include "config.h"
17
Matt Spinlerf0f02b92018-10-25 16:12:43 -050018#include "power_supply.hpp"
19
Brandon Wyman442035f2017-08-08 15:58:45 -050020#include "elog-errors.hpp"
Matt Spinlerd734e652018-01-18 14:31:15 -060021#include "gpio.hpp"
Brandon Wyman10295542017-08-09 18:20:44 -050022#include "names_values.hpp"
Brandon Wyman442035f2017-08-08 15:58:45 -050023#include "pmbus.hpp"
Lei YUcfc040c2019-10-29 17:10:26 +080024#include "types.hpp"
Brandon Wyman442035f2017-08-08 15:58:45 -050025#include "utility.hpp"
26
Matt Spinlerf0f02b92018-10-25 16:12:43 -050027#include <org/open_power/Witherspoon/Fault/error.hpp>
Anwaar Hadi1f0193f2025-05-13 21:05:12 +000028#include <phosphor-logging/lg2.hpp>
Matt Spinlerf0f02b92018-10-25 16:12:43 -050029#include <xyz/openbmc_project/Common/Device/error.hpp>
Matt Spinlerf0f02b92018-10-25 16:12:43 -050030
Brandon Wymand1bc4ce2019-12-13 14:20:34 -060031#include <functional>
32
Lei YUab093322019-10-09 16:43:22 +080033namespace phosphor
Brandon Wyman24e422f2017-07-25 19:40:14 -050034{
35namespace power
36{
37namespace psu
38{
39
Matt Spinler589e8722018-01-04 15:24:49 -060040using namespace phosphor::logging;
41using namespace sdbusplus::org::open_power::Witherspoon::Fault::Error;
42using namespace sdbusplus::xyz::openbmc_project::Common::Device::Error;
43
Jayanth Othayothf9886b92024-12-07 01:26:08 -060044#ifdef __clang__
45#pragma clang diagnostic push
46#pragma clang diagnostic ignored "-Wpessimizing-move"
47#endif
Brandon Wyman10295542017-08-09 18:20:44 -050048PowerSupply::PowerSupply(const std::string& name, size_t inst,
Matt Spinlerf0f02b92018-10-25 16:12:43 -050049 const std::string& objpath, const std::string& invpath,
Patrick Williams7354ce62022-07-22 19:26:56 -050050 sdbusplus::bus_t& bus, const sdeventplus::Event& e,
Matt Spinlerf0f02b92018-10-25 16:12:43 -050051 std::chrono::seconds& t, std::chrono::seconds& p) :
Patrick Williamsf5402192024-08-16 15:20:53 -040052 Device(name, inst), monitorPath(objpath), pmbusIntf(objpath),
Matt Spinlerf0f02b92018-10-25 16:12:43 -050053 inventoryPath(INVENTORY_OBJ_PATH + invpath), bus(bus), presentInterval(p),
54 presentTimer(e, std::bind([this]() {
Patrick Williamsf5402192024-08-16 15:20:53 -040055 // The hwmon path may have changed.
56 pmbusIntf.findHwmonDir();
57 this->present = true;
Matt Spinler234ce0d2018-01-04 15:06:57 -060058
Patrick Williamsf5402192024-08-16 15:20:53 -040059 // Sync the INPUT_HISTORY data for all PSs
60 syncHistory();
Matt Spinlerd734e652018-01-18 14:31:15 -060061
Patrick Williamsf5402192024-08-16 15:20:53 -040062 // Update the inventory for the new device
63 updateInventory();
64 })),
Matt Spinlerf0f02b92018-10-25 16:12:43 -050065 powerOnInterval(t),
66 powerOnTimer(e, std::bind([this]() { this->powerOn = true; }))
Brandon Wyman10295542017-08-09 18:20:44 -050067{
George Liu690e7802019-08-23 11:04:01 +080068 getAccessType();
69
Brandon Wyman10295542017-08-09 18:20:44 -050070 using namespace sdbusplus::bus;
Lei YUab093322019-10-09 16:43:22 +080071 using namespace phosphor::pmbus;
Aatir Manzur817f8a72019-08-07 07:42:15 -050072 std::uint16_t statusWord = 0;
73 try
74 {
75 // Read the 2 byte STATUS_WORD value to check for faults.
76 statusWord = pmbusIntf.read(STATUS_WORD, Type::Debug);
77 if (!((statusWord & status_word::INPUT_FAULT_WARN) ||
78 (statusWord & status_word::VIN_UV_FAULT)))
79 {
80 resolveError(inventoryPath,
81 std::string(PowerSupplyInputFault::errName));
82 }
83 }
Patrick Williamsc1d4de52021-10-06 12:45:57 -050084 catch (const ReadFailure& e)
Aatir Manzur817f8a72019-08-07 07:42:15 -050085 {
Anwaar Hadi1f0193f2025-05-13 21:05:12 +000086 lg2::info("Unable to read the 2 byte STATUS_WORD value to check "
87 "for power-supply input faults.");
Aatir Manzur817f8a72019-08-07 07:42:15 -050088 }
Matt Spinlerf0f02b92018-10-25 16:12:43 -050089 presentMatch = std::make_unique<match_t>(
90 bus, match::rules::propertiesChanged(inventoryPath, INVENTORY_IFACE),
91 [this](auto& msg) { this->inventoryChanged(msg); });
Brandon Wyman431fbe42017-08-18 16:22:09 -050092 // Get initial presence state.
Brandon Wyman253dc9b2017-08-12 13:45:52 -050093 updatePresence();
Brandon Wyman431fbe42017-08-18 16:22:09 -050094
Matt Spinler234ce0d2018-01-04 15:06:57 -060095 // Write the SN, PN, etc to the inventory
96 updateInventory();
97
Brandon Wyman431fbe42017-08-18 16:22:09 -050098 // Subscribe to power state changes
Matt Spinlerf0f02b92018-10-25 16:12:43 -050099 powerOnMatch = std::make_unique<match_t>(
100 bus, match::rules::propertiesChanged(POWER_OBJ_PATH, POWER_IFACE),
101 [this](auto& msg) { this->powerStateChanged(msg); });
Brandon Wyman431fbe42017-08-18 16:22:09 -0500102 // Get initial power state.
103 updatePowerState();
Brandon Wyman10295542017-08-09 18:20:44 -0500104}
Jayanth Othayothf9886b92024-12-07 01:26:08 -0600105#ifdef __clang__
106#pragma clang diagnostic pop
107#endif
Brandon Wyman442035f2017-08-08 15:58:45 -0500108
George Liu690e7802019-08-23 11:04:01 +0800109void PowerSupply::getAccessType()
110{
Lei YUab093322019-10-09 16:43:22 +0800111 using namespace phosphor::power::util;
George Liu690e7802019-08-23 11:04:01 +0800112 fruJson = loadJSONFromFile(PSU_JSON_PATH);
113 if (fruJson == nullptr)
114 {
Anwaar Hadi1f0193f2025-05-13 21:05:12 +0000115 lg2::error("InternalFailure when parsing the JSON file");
George Liu690e7802019-08-23 11:04:01 +0800116 return;
117 }
Lei YU40705462019-10-09 17:07:11 +0800118 inventoryPMBusAccessType = getPMBusAccessType(fruJson);
George Liu690e7802019-08-23 11:04:01 +0800119}
120
Brandon Wymana1e96342017-09-25 16:47:44 -0500121void PowerSupply::captureCmd(util::NamesValues& nv, const std::string& cmd,
Lei YUab093322019-10-09 16:43:22 +0800122 phosphor::pmbus::Type type)
Brandon Wymana1e96342017-09-25 16:47:44 -0500123{
124 if (pmbusIntf.exists(cmd, type))
125 {
126 try
127 {
128 auto val = pmbusIntf.read(cmd, type);
129 nv.add(cmd, val);
130 }
Patrick Williamsc1d4de52021-10-06 12:45:57 -0500131 catch (const std::exception& e)
Brandon Wymana1e96342017-09-25 16:47:44 -0500132 {
Anwaar Hadi1f0193f2025-05-13 21:05:12 +0000133 lg2::info("Unable to capture metadata, CMD={CMD}", "CMD", cmd);
Brandon Wymana1e96342017-09-25 16:47:44 -0500134 }
135 }
136}
Brandon Wyman431fbe42017-08-18 16:22:09 -0500137
Brandon Wyman1db9a9e2017-07-26 18:50:22 -0500138void PowerSupply::analyze()
139{
Lei YUab093322019-10-09 16:43:22 +0800140 using namespace phosphor::pmbus;
Brandon Wyman442035f2017-08-08 15:58:45 -0500141
142 try
143 {
Brandon Wyman10295542017-08-09 18:20:44 -0500144 if (present)
Brandon Wyman442035f2017-08-08 15:58:45 -0500145 {
Brandon Wyman764c7972017-08-22 17:05:36 -0500146 std::uint16_t statusWord = 0;
Brandon Wyman764c7972017-08-22 17:05:36 -0500147
148 // Read the 2 byte STATUS_WORD value to check for faults.
149 statusWord = pmbusIntf.read(STATUS_WORD, Type::Debug);
Brandon Wymane4af9802017-11-13 15:58:33 -0600150 readFail = 0;
Brandon Wyman764c7972017-08-22 17:05:36 -0500151
Brandon Wyman603cc002017-08-28 18:17:58 -0500152 checkInputFault(statusWord);
Brandon Wyman764c7972017-08-22 17:05:36 -0500153
Brandon Wyman654eb6e2018-02-14 14:55:47 -0600154 if (powerOn && (inputFault == 0) && !faultFound)
Brandon Wyman764c7972017-08-22 17:05:36 -0500155 {
Brandon Wyman12661f12017-08-31 15:28:21 -0500156 checkFanFault(statusWord);
Brandon Wyman875b3632017-09-13 18:46:03 -0500157 checkTemperatureFault(statusWord);
Brandon Wymancfa032b2017-09-25 17:37:50 -0500158 checkOutputOvervoltageFault(statusWord);
159 checkCurrentOutOverCurrentFault(statusWord);
160 checkPGOrUnitOffFault(statusWord);
Brandon Wyman442035f2017-08-08 15:58:45 -0500161 }
Matt Spinlereb169fd2018-01-18 14:19:08 -0600162
163 updateHistory();
Brandon Wyman442035f2017-08-08 15:58:45 -0500164 }
165 }
Patrick Williamsc1d4de52021-10-06 12:45:57 -0500166 catch (const ReadFailure& e)
Brandon Wyman442035f2017-08-08 15:58:45 -0500167 {
Brandon Wymane4af9802017-11-13 15:58:33 -0600168 if (readFail < FAULT_COUNT)
169 {
170 readFail++;
171 }
172
173 if (!readFailLogged && readFail >= FAULT_COUNT)
Brandon Wyman442035f2017-08-08 15:58:45 -0500174 {
175 commit<ReadFailure>();
176 readFailLogged = true;
Brandon Wyman442035f2017-08-08 15:58:45 -0500177 }
178 }
179
Brandon Wyman1db9a9e2017-07-26 18:50:22 -0500180 return;
181}
182
Patrick Williams7354ce62022-07-22 19:26:56 -0500183void PowerSupply::inventoryChanged(sdbusplus::message_t& msg)
Brandon Wyman10295542017-08-09 18:20:44 -0500184{
185 std::string msgSensor;
Patrick Williamsabe49412020-05-13 17:59:47 -0500186 std::map<std::string, std::variant<uint32_t, bool>> msgData;
Brandon Wyman10295542017-08-09 18:20:44 -0500187 msg.read(msgSensor, msgData);
188
189 // Check if it was the Present property that changed.
190 auto valPropMap = msgData.find(PRESENT_PROP);
191 if (valPropMap != msgData.end())
192 {
Patrick Williams365d61c2020-05-13 12:23:08 -0500193 if (std::get<bool>(valPropMap->second))
Brandon Wyman10295542017-08-09 18:20:44 -0500194 {
Brandon Wyman6ccce0b2017-10-26 15:13:10 -0500195 clearFaults();
William A. Kennington III1a0c9172018-10-18 17:57:49 -0700196 presentTimer.restartOnce(presentInterval);
Brandon Wyman590fc282017-11-01 18:22:25 -0500197 }
198 else
199 {
Brandon Wyman2ef48cf2017-11-21 15:43:54 -0600200 present = false;
William A. Kennington III1a0c9172018-10-18 17:57:49 -0700201 presentTimer.setEnabled(false);
Matt Spinler234ce0d2018-01-04 15:06:57 -0600202
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500203 // Clear out the now outdated inventory properties
Matt Spinler234ce0d2018-01-04 15:06:57 -0600204 updateInventory();
Brandon Wyman10295542017-08-09 18:20:44 -0500205 }
206 }
207
208 return;
209}
210
211void PowerSupply::updatePresence()
212{
213 // Use getProperty utility function to get presence status.
Brandon Wyman10295542017-08-09 18:20:44 -0500214 std::string service = "xyz.openbmc_project.Inventory.Manager";
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500215 util::getProperty(INVENTORY_IFACE, PRESENT_PROP, inventoryPath, service,
216 bus, this->present);
Brandon Wyman10295542017-08-09 18:20:44 -0500217}
218
Patrick Williams7354ce62022-07-22 19:26:56 -0500219void PowerSupply::powerStateChanged(sdbusplus::message_t& msg)
Brandon Wyman431fbe42017-08-18 16:22:09 -0500220{
221 int32_t state = 0;
222 std::string msgSensor;
Patrick Williamsabe49412020-05-13 17:59:47 -0500223 std::map<std::string, std::variant<int32_t>> msgData;
Brandon Wyman431fbe42017-08-18 16:22:09 -0500224 msg.read(msgSensor, msgData);
225
226 // Check if it was the Present property that changed.
227 auto valPropMap = msgData.find("state");
228 if (valPropMap != msgData.end())
229 {
Patrick Williams365d61c2020-05-13 12:23:08 -0500230 state = std::get<int32_t>(valPropMap->second);
Brandon Wyman431fbe42017-08-18 16:22:09 -0500231
232 // Power is on when state=1. Set the fault logged variables to false
233 // and start the power on timer when the state changes to 1.
234 if (state)
235 {
Brandon Wyman6ccce0b2017-10-26 15:13:10 -0500236 clearFaults();
William A. Kennington III1a0c9172018-10-18 17:57:49 -0700237 powerOnTimer.restartOnce(powerOnInterval);
Brandon Wyman431fbe42017-08-18 16:22:09 -0500238 }
239 else
240 {
William A. Kennington III1a0c9172018-10-18 17:57:49 -0700241 powerOnTimer.setEnabled(false);
Brandon Wyman431fbe42017-08-18 16:22:09 -0500242 powerOn = false;
243 }
244 }
Brandon Wyman431fbe42017-08-18 16:22:09 -0500245}
246
247void PowerSupply::updatePowerState()
248{
Lei YUcfc040c2019-10-29 17:10:26 +0800249 powerOn = util::isPoweredOn(bus);
Brandon Wyman431fbe42017-08-18 16:22:09 -0500250}
251
Brandon Wyman603cc002017-08-28 18:17:58 -0500252void PowerSupply::checkInputFault(const uint16_t statusWord)
253{
Lei YUab093322019-10-09 16:43:22 +0800254 using namespace phosphor::pmbus;
Brandon Wyman603cc002017-08-28 18:17:58 -0500255
Brandon Wymana3c675c2017-11-14 14:54:54 -0600256 if ((inputFault < FAULT_COUNT) &&
257 ((statusWord & status_word::INPUT_FAULT_WARN) ||
258 (statusWord & status_word::VIN_UV_FAULT)))
Brandon Wyman603cc002017-08-28 18:17:58 -0500259 {
Brandon Wymanad708242018-01-19 17:28:35 -0600260 if (inputFault == 0)
261 {
Anwaar Hadi1f0193f2025-05-13 21:05:12 +0000262 lg2::info("INPUT or VIN_UV fault, STATUS_WORD={STATUS_WORD}",
263 "STATUS_WORD", lg2::hex | lg2::field16, statusWord);
Brandon Wymanad708242018-01-19 17:28:35 -0600264 }
265
Brandon Wymana3c675c2017-11-14 14:54:54 -0600266 inputFault++;
Brandon Wyman603cc002017-08-28 18:17:58 -0500267 }
268 else
269 {
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500270 if ((inputFault > 0) && !(statusWord & status_word::INPUT_FAULT_WARN) &&
Brandon Wymand20686a2017-11-01 17:45:23 -0500271 !(statusWord & status_word::VIN_UV_FAULT))
Brandon Wyman603cc002017-08-28 18:17:58 -0500272 {
Brandon Wymana3c675c2017-11-14 14:54:54 -0600273 inputFault = 0;
Brandon Wyman3343e822017-11-03 16:54:11 -0500274 faultFound = false;
Brandon Wyman7502f6c2018-02-12 20:20:39 -0600275 // When an input fault occurs, the power supply cannot be on.
276 // However, the check for the case where the power supply should be
277 // on will stop when there is a fault found.
278 // Clear the powerOnFault when the inputFault is cleared to reset
279 // the powerOnFault de-glitching.
280 powerOnFault = 0;
Brandon Wyman69591bd2017-11-01 18:07:23 -0500281
Anwaar Hadi1f0193f2025-05-13 21:05:12 +0000282 lg2::info("INPUT_FAULT_WARN cleared, POWERSUPPLY={POWERSUPPLY}",
283 "POWERSUPPLY", inventoryPath);
Brandon Wyman69591bd2017-11-01 18:07:23 -0500284
Brandon Wyman08b05712017-11-30 17:53:56 -0600285 resolveError(inventoryPath,
286 std::string(PowerSupplyInputFault::errName));
287
Brandon Wyman69591bd2017-11-01 18:07:23 -0500288 if (powerOn)
289 {
290 // The power supply will not be immediately powered on after
291 // the input power is restored.
292 powerOn = false;
293 // Start up the timer that will set the state to indicate we
294 // are ready for the powered on fault checks.
William A. Kennington III1a0c9172018-10-18 17:57:49 -0700295 powerOnTimer.restartOnce(powerOnInterval);
Brandon Wyman69591bd2017-11-01 18:07:23 -0500296 }
Brandon Wyman603cc002017-08-28 18:17:58 -0500297 }
298 }
Brandon Wymana3c675c2017-11-14 14:54:54 -0600299
300 if (!faultFound && (inputFault >= FAULT_COUNT))
301 {
Brandon Wymanad708242018-01-19 17:28:35 -0600302 // If the power is on, report the fault in an error log entry.
303 if (powerOn)
304 {
305 util::NamesValues nv;
306 nv.add("STATUS_WORD", statusWord);
307 captureCmd(nv, STATUS_INPUT, Type::Debug);
Brandon Wymana3c675c2017-11-14 14:54:54 -0600308
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500309 using metadata =
310 org::open_power::Witherspoon::Fault::PowerSupplyInputFault;
Brandon Wymana3c675c2017-11-14 14:54:54 -0600311
Brandon Wymanad708242018-01-19 17:28:35 -0600312 report<PowerSupplyInputFault>(
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500313 metadata::RAW_STATUS(nv.get().c_str()),
314 metadata::CALLOUT_INVENTORY_PATH(inventoryPath.c_str()));
Brandon Wymanad708242018-01-19 17:28:35 -0600315
316 faultFound = true;
317 }
Brandon Wymana3c675c2017-11-14 14:54:54 -0600318 }
Brandon Wyman603cc002017-08-28 18:17:58 -0500319}
320
321void PowerSupply::checkPGOrUnitOffFault(const uint16_t statusWord)
322{
Lei YUab093322019-10-09 16:43:22 +0800323 using namespace phosphor::pmbus;
Brandon Wyman603cc002017-08-28 18:17:58 -0500324
Brandon Wyman593d24f2017-10-13 18:15:23 -0500325 if (powerOnFault < FAULT_COUNT)
Brandon Wyman603cc002017-08-28 18:17:58 -0500326 {
Brandon Wyman593d24f2017-10-13 18:15:23 -0500327 // Check PG# and UNIT_IS_OFF
328 if ((statusWord & status_word::POWER_GOOD_NEGATED) ||
329 (statusWord & status_word::UNIT_IS_OFF))
330 {
Anwaar Hadi1f0193f2025-05-13 21:05:12 +0000331 lg2::info("PGOOD or UNIT_IS_OFF bit bad, STATUS_WORD={STATUS_WORD}",
332 "STATUS_WORD", lg2::hex | lg2::field16, statusWord);
Brandon Wyman593d24f2017-10-13 18:15:23 -0500333 powerOnFault++;
334 }
335 else
336 {
337 if (powerOnFault > 0)
338 {
Anwaar Hadi1f0193f2025-05-13 21:05:12 +0000339 lg2::info("PGOOD and UNIT_IS_OFF bits good");
Brandon Wyman593d24f2017-10-13 18:15:23 -0500340 powerOnFault = 0;
341 }
342 }
Brandon Wyman603cc002017-08-28 18:17:58 -0500343
Brandon Wymane2fc7aa2017-11-13 17:37:10 -0600344 if (!faultFound && (powerOnFault >= FAULT_COUNT))
Brandon Wyman593d24f2017-10-13 18:15:23 -0500345 {
Brandon Wyman3343e822017-11-03 16:54:11 -0500346 faultFound = true;
347
Brandon Wyman593d24f2017-10-13 18:15:23 -0500348 util::NamesValues nv;
349 nv.add("STATUS_WORD", statusWord);
350 captureCmd(nv, STATUS_INPUT, Type::Debug);
351 auto status0Vout = pmbusIntf.insertPageNum(STATUS_VOUT, 0);
352 captureCmd(nv, status0Vout, Type::Debug);
353 captureCmd(nv, STATUS_IOUT, Type::Debug);
354 captureCmd(nv, STATUS_MFR, Type::Debug);
Brandon Wyman603cc002017-08-28 18:17:58 -0500355
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500356 using metadata =
357 org::open_power::Witherspoon::Fault::PowerSupplyShouldBeOn;
Brandon Wyman603cc002017-08-28 18:17:58 -0500358
Brandon Wyman593d24f2017-10-13 18:15:23 -0500359 // A power supply is OFF (or pgood low) but should be on.
360 report<PowerSupplyShouldBeOn>(
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500361 metadata::RAW_STATUS(nv.get().c_str()),
362 metadata::CALLOUT_INVENTORY_PATH(inventoryPath.c_str()));
Brandon Wyman593d24f2017-10-13 18:15:23 -0500363 }
Brandon Wyman603cc002017-08-28 18:17:58 -0500364 }
Brandon Wyman603cc002017-08-28 18:17:58 -0500365}
366
367void PowerSupply::checkCurrentOutOverCurrentFault(const uint16_t statusWord)
368{
Lei YUab093322019-10-09 16:43:22 +0800369 using namespace phosphor::pmbus;
Brandon Wyman603cc002017-08-28 18:17:58 -0500370
Brandon Wymandd61be42017-11-07 18:38:54 -0600371 if (outputOCFault < FAULT_COUNT)
Brandon Wyman603cc002017-08-28 18:17:58 -0500372 {
Brandon Wymandd61be42017-11-07 18:38:54 -0600373 // Check for an output overcurrent fault.
374 if ((statusWord & status_word::IOUT_OC_FAULT))
375 {
376 outputOCFault++;
377 }
378 else
379 {
380 if (outputOCFault > 0)
381 {
382 outputOCFault = 0;
383 }
384 }
Brandon Wyman603cc002017-08-28 18:17:58 -0500385
Brandon Wymane2fc7aa2017-11-13 17:37:10 -0600386 if (!faultFound && (outputOCFault >= FAULT_COUNT))
Brandon Wymandd61be42017-11-07 18:38:54 -0600387 {
388 util::NamesValues nv;
389 nv.add("STATUS_WORD", statusWord);
390 captureCmd(nv, STATUS_INPUT, Type::Debug);
391 auto status0Vout = pmbusIntf.insertPageNum(STATUS_VOUT, 0);
392 captureCmd(nv, status0Vout, Type::Debug);
393 captureCmd(nv, STATUS_IOUT, Type::Debug);
394 captureCmd(nv, STATUS_MFR, Type::Debug);
Brandon Wyman603cc002017-08-28 18:17:58 -0500395
Brandon Wymandd61be42017-11-07 18:38:54 -0600396 using metadata = org::open_power::Witherspoon::Fault::
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500397 PowerSupplyOutputOvercurrent;
Brandon Wyman603cc002017-08-28 18:17:58 -0500398
Brandon Wymandd61be42017-11-07 18:38:54 -0600399 report<PowerSupplyOutputOvercurrent>(
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500400 metadata::RAW_STATUS(nv.get().c_str()),
401 metadata::CALLOUT_INVENTORY_PATH(inventoryPath.c_str()));
Brandon Wymandd61be42017-11-07 18:38:54 -0600402
403 faultFound = true;
404 }
Brandon Wyman603cc002017-08-28 18:17:58 -0500405 }
406}
407
Brandon Wymanab05c072017-08-30 18:26:41 -0500408void PowerSupply::checkOutputOvervoltageFault(const uint16_t statusWord)
409{
Lei YUab093322019-10-09 16:43:22 +0800410 using namespace phosphor::pmbus;
Brandon Wymanab05c072017-08-30 18:26:41 -0500411
Brandon Wyman2ab319b2017-11-08 17:34:59 -0600412 if (outputOVFault < FAULT_COUNT)
Brandon Wymanab05c072017-08-30 18:26:41 -0500413 {
Brandon Wyman2ab319b2017-11-08 17:34:59 -0600414 // Check for an output overvoltage fault.
415 if (statusWord & status_word::VOUT_OV_FAULT)
416 {
417 outputOVFault++;
418 }
419 else
420 {
421 if (outputOVFault > 0)
422 {
423 outputOVFault = 0;
424 }
425 }
Brandon Wymanab05c072017-08-30 18:26:41 -0500426
Brandon Wymane2fc7aa2017-11-13 17:37:10 -0600427 if (!faultFound && (outputOVFault >= FAULT_COUNT))
Brandon Wyman2ab319b2017-11-08 17:34:59 -0600428 {
429 util::NamesValues nv;
430 nv.add("STATUS_WORD", statusWord);
431 captureCmd(nv, STATUS_INPUT, Type::Debug);
432 auto status0Vout = pmbusIntf.insertPageNum(STATUS_VOUT, 0);
433 captureCmd(nv, status0Vout, Type::Debug);
434 captureCmd(nv, STATUS_IOUT, Type::Debug);
435 captureCmd(nv, STATUS_MFR, Type::Debug);
Brandon Wymanab05c072017-08-30 18:26:41 -0500436
Brandon Wyman2ab319b2017-11-08 17:34:59 -0600437 using metadata = org::open_power::Witherspoon::Fault::
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500438 PowerSupplyOutputOvervoltage;
Brandon Wymanab05c072017-08-30 18:26:41 -0500439
Brandon Wyman2ab319b2017-11-08 17:34:59 -0600440 report<PowerSupplyOutputOvervoltage>(
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500441 metadata::RAW_STATUS(nv.get().c_str()),
442 metadata::CALLOUT_INVENTORY_PATH(inventoryPath.c_str()));
Brandon Wyman2ab319b2017-11-08 17:34:59 -0600443
444 faultFound = true;
445 }
Brandon Wymanab05c072017-08-30 18:26:41 -0500446 }
447}
448
Brandon Wyman12661f12017-08-31 15:28:21 -0500449void PowerSupply::checkFanFault(const uint16_t statusWord)
450{
Lei YUab093322019-10-09 16:43:22 +0800451 using namespace phosphor::pmbus;
Brandon Wyman12661f12017-08-31 15:28:21 -0500452
Brandon Wymanba255532017-11-08 17:44:10 -0600453 if (fanFault < FAULT_COUNT)
Brandon Wyman12661f12017-08-31 15:28:21 -0500454 {
Brandon Wymanba255532017-11-08 17:44:10 -0600455 // Check for a fan fault or warning condition
456 if (statusWord & status_word::FAN_FAULT)
457 {
458 fanFault++;
459 }
460 else
461 {
462 if (fanFault > 0)
463 {
464 fanFault = 0;
465 }
466 }
Brandon Wyman12661f12017-08-31 15:28:21 -0500467
Brandon Wymane2fc7aa2017-11-13 17:37:10 -0600468 if (!faultFound && (fanFault >= FAULT_COUNT))
Brandon Wymanba255532017-11-08 17:44:10 -0600469 {
470 util::NamesValues nv;
471 nv.add("STATUS_WORD", statusWord);
472 captureCmd(nv, STATUS_MFR, Type::Debug);
473 captureCmd(nv, STATUS_TEMPERATURE, Type::Debug);
474 captureCmd(nv, STATUS_FANS_1_2, Type::Debug);
Brandon Wyman12661f12017-08-31 15:28:21 -0500475
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500476 using metadata =
477 org::open_power::Witherspoon::Fault::PowerSupplyFanFault;
Brandon Wyman12661f12017-08-31 15:28:21 -0500478
Brandon Wymanba255532017-11-08 17:44:10 -0600479 report<PowerSupplyFanFault>(
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500480 metadata::RAW_STATUS(nv.get().c_str()),
481 metadata::CALLOUT_INVENTORY_PATH(inventoryPath.c_str()));
Brandon Wymanba255532017-11-08 17:44:10 -0600482
483 faultFound = true;
484 }
Brandon Wyman12661f12017-08-31 15:28:21 -0500485 }
486}
487
Brandon Wyman875b3632017-09-13 18:46:03 -0500488void PowerSupply::checkTemperatureFault(const uint16_t statusWord)
489{
Lei YUab093322019-10-09 16:43:22 +0800490 using namespace phosphor::pmbus;
Brandon Wyman875b3632017-09-13 18:46:03 -0500491
492 // Due to how the PMBus core device driver sends a clear faults command
493 // the bit in STATUS_WORD will likely be cleared when we attempt to examine
494 // it for a Thermal Fault or Warning. So, check the STATUS_WORD and the
495 // STATUS_TEMPERATURE bits. If either indicates a fault, proceed with
496 // logging the over-temperature condition.
497 std::uint8_t statusTemperature = 0;
498 statusTemperature = pmbusIntf.read(STATUS_TEMPERATURE, Type::Debug);
Brandon Wyman50044ea2017-11-08 17:58:56 -0600499 if (temperatureFault < FAULT_COUNT)
Brandon Wyman875b3632017-09-13 18:46:03 -0500500 {
Brandon Wyman50044ea2017-11-08 17:58:56 -0600501 if ((statusWord & status_word::TEMPERATURE_FAULT_WARN) ||
502 (statusTemperature & status_temperature::OT_FAULT))
503 {
504 temperatureFault++;
505 }
506 else
507 {
508 if (temperatureFault > 0)
509 {
510 temperatureFault = 0;
511 }
512 }
Brandon Wyman875b3632017-09-13 18:46:03 -0500513
Brandon Wymane2fc7aa2017-11-13 17:37:10 -0600514 if (!faultFound && (temperatureFault >= FAULT_COUNT))
Brandon Wyman50044ea2017-11-08 17:58:56 -0600515 {
516 // The power supply has had an over-temperature condition.
517 // This may not result in a shutdown if experienced for a short
518 // duration.
519 // This should not occur under normal conditions.
520 // The power supply may be faulty, or the paired supply may be
521 // putting out less current.
522 // Capture command responses with potentially relevant information,
523 // and call out the power supply reporting the condition.
524 util::NamesValues nv;
525 nv.add("STATUS_WORD", statusWord);
526 captureCmd(nv, STATUS_MFR, Type::Debug);
527 captureCmd(nv, STATUS_IOUT, Type::Debug);
528 nv.add("STATUS_TEMPERATURE", statusTemperature);
529 captureCmd(nv, STATUS_FANS_1_2, Type::Debug);
Brandon Wyman875b3632017-09-13 18:46:03 -0500530
Brandon Wyman50044ea2017-11-08 17:58:56 -0600531 using metadata = org::open_power::Witherspoon::Fault::
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500532 PowerSupplyTemperatureFault;
Brandon Wyman875b3632017-09-13 18:46:03 -0500533
Brandon Wyman50044ea2017-11-08 17:58:56 -0600534 report<PowerSupplyTemperatureFault>(
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500535 metadata::RAW_STATUS(nv.get().c_str()),
536 metadata::CALLOUT_INVENTORY_PATH(inventoryPath.c_str()));
Brandon Wyman50044ea2017-11-08 17:58:56 -0600537
538 faultFound = true;
539 }
Brandon Wyman875b3632017-09-13 18:46:03 -0500540 }
541}
542
Brandon Wyman1db9a9e2017-07-26 18:50:22 -0500543void PowerSupply::clearFaults()
544{
Brandon Wymane4af9802017-11-13 15:58:33 -0600545 readFail = 0;
Brandon Wyman6ccce0b2017-10-26 15:13:10 -0500546 readFailLogged = false;
Brandon Wymana3c675c2017-11-14 14:54:54 -0600547 inputFault = 0;
Brandon Wyman6ccce0b2017-10-26 15:13:10 -0500548 powerOnFault = 0;
Brandon Wymandd61be42017-11-07 18:38:54 -0600549 outputOCFault = 0;
Brandon Wyman2ab319b2017-11-08 17:34:59 -0600550 outputOVFault = 0;
Brandon Wymanba255532017-11-08 17:44:10 -0600551 fanFault = 0;
Brandon Wyman50044ea2017-11-08 17:58:56 -0600552 temperatureFault = 0;
Brandon Wyman3343e822017-11-03 16:54:11 -0500553 faultFound = false;
Brandon Wyman6ccce0b2017-10-26 15:13:10 -0500554
Brandon Wyman1db9a9e2017-07-26 18:50:22 -0500555 return;
556}
557
Brandon Wyman43ce2082017-11-30 17:24:01 -0600558void PowerSupply::resolveError(const std::string& callout,
559 const std::string& message)
560{
Brandon Wyman01741f12017-12-01 17:22:08 -0600561 using EndpointList = std::vector<std::string>;
562
563 try
564 {
565 auto path = callout + "/fault";
566 // Get the service name from the mapper for the fault callout
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500567 auto service = util::getService(path, ASSOCIATION_IFACE, bus);
Brandon Wyman01741f12017-12-01 17:22:08 -0600568
569 // Use getProperty utility function to get log entries (endpoints)
570 EndpointList logEntries;
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500571 util::getProperty(ASSOCIATION_IFACE, ENDPOINTS_PROP, path, service, bus,
572 logEntries);
Brandon Wyman01741f12017-12-01 17:22:08 -0600573
574 // It is possible that all such entries for this callout have since
575 // been deleted.
576 if (logEntries.empty())
577 {
578 return;
579 }
580
Patrick Williamsf5402192024-08-16 15:20:53 -0400581 auto logEntryService =
582 util::getService(logEntries[0], LOGGING_IFACE, bus);
Brandon Wyman01741f12017-12-01 17:22:08 -0600583 if (logEntryService.empty())
584 {
585 return;
586 }
587
588 // go through each log entry that matches this callout path
589 std::string logMessage;
590 for (const auto& logEntry : logEntries)
591 {
592 // Check to see if this logEntry has a message that matches.
Matt Spinler589e8722018-01-04 15:24:49 -0600593 util::getProperty(LOGGING_IFACE, MESSAGE_PROP, logEntry,
Brandon Wyman01741f12017-12-01 17:22:08 -0600594 logEntryService, bus, logMessage);
595
596 if (message == logMessage)
597 {
598 // Log entry matches call out and message, set Resolved to true
599 bool resolved = true;
Matt Spinler589e8722018-01-04 15:24:49 -0600600 util::setProperty(LOGGING_IFACE, RESOLVED_PROP, logEntry,
Brandon Wyman01741f12017-12-01 17:22:08 -0600601 logEntryService, bus, resolved);
602 }
Brandon Wyman01741f12017-12-01 17:22:08 -0600603 }
Brandon Wyman01741f12017-12-01 17:22:08 -0600604 }
Patrick Williamsc1d4de52021-10-06 12:45:57 -0500605 catch (const std::exception& e)
Brandon Wyman01741f12017-12-01 17:22:08 -0600606 {
Anwaar Hadi1f0193f2025-05-13 21:05:12 +0000607 lg2::info("Failed to resolve error, CALLOUT={CALLOUT}, ERROR={ERROR}",
608 "CALLOUT", callout, "ERROR", message);
Brandon Wyman01741f12017-12-01 17:22:08 -0600609 }
Brandon Wyman43ce2082017-11-30 17:24:01 -0600610}
611
Matt Spinler234ce0d2018-01-04 15:06:57 -0600612void PowerSupply::updateInventory()
613{
Lei YUab093322019-10-09 16:43:22 +0800614 using namespace phosphor::pmbus;
Matt Spinler018a7bc2018-01-04 15:36:41 -0600615 using namespace sdbusplus::message;
616
Matt Spinler018a7bc2018-01-04 15:36:41 -0600617 // Build the object map and send it to the inventory
George Liu070c1bc2020-10-12 11:28:01 +0800618 using Properties = std::map<std::string, std::variant<std::string, bool>>;
Matt Spinler018a7bc2018-01-04 15:36:41 -0600619 using Interfaces = std::map<std::string, Properties>;
620 using Object = std::map<object_path, Interfaces>;
621 Properties assetProps;
George Liu070c1bc2020-10-12 11:28:01 +0800622 Properties operProps;
Matt Spinler018a7bc2018-01-04 15:36:41 -0600623 Interfaces interfaces;
624 Object object;
625
George Liu690e7802019-08-23 11:04:01 +0800626 // If any of these accesses fail, the fields will just be
627 // blank in the inventory. Leave logging ReadFailure errors
628 // to analyze() as it runs continuously and will most
629 // likely hit and threshold them first anyway. The
630 // readString() function will do the tracing of the failing
631 // path so this code doesn't need to.
632 for (const auto& fru : fruJson.at("fruConfigs"))
633 {
634 if (fru.at("interface") == ASSET_IFACE)
635 {
636 try
637 {
638 assetProps.emplace(
639 fru.at("propertyName"),
640 present ? pmbusIntf.readString(fru.at("fileName"),
641 inventoryPMBusAccessType)
642 : "");
643 }
Patrick Williamsc1d4de52021-10-06 12:45:57 -0500644 catch (const ReadFailure& e)
Adriana Kobylak0c9a33d2021-09-13 18:05:09 +0000645 {}
George Liu690e7802019-08-23 11:04:01 +0800646 }
George Liu690e7802019-08-23 11:04:01 +0800647 }
Matt Spinler018a7bc2018-01-04 15:36:41 -0600648
George Liu070c1bc2020-10-12 11:28:01 +0800649 operProps.emplace(FUNCTIONAL_PROP, present);
George Liu690e7802019-08-23 11:04:01 +0800650 interfaces.emplace(ASSET_IFACE, std::move(assetProps));
George Liu070c1bc2020-10-12 11:28:01 +0800651 interfaces.emplace(OPERATIONAL_STATE_IFACE, std::move(operProps));
Matt Spinler018a7bc2018-01-04 15:36:41 -0600652
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500653 // For Notify(), just send the relative path of the inventory
654 // object so remove the INVENTORY_OBJ_PATH prefix
Matt Spinler018a7bc2018-01-04 15:36:41 -0600655 auto path = inventoryPath.substr(strlen(INVENTORY_OBJ_PATH));
656
657 object.emplace(path, std::move(interfaces));
658
659 try
660 {
Patrick Williamsf5402192024-08-16 15:20:53 -0400661 auto service =
662 util::getService(INVENTORY_OBJ_PATH, INVENTORY_MGR_IFACE, bus);
Matt Spinler018a7bc2018-01-04 15:36:41 -0600663
664 if (service.empty())
665 {
Anwaar Hadi1f0193f2025-05-13 21:05:12 +0000666 lg2::error("Unable to get inventory manager service");
Matt Spinler018a7bc2018-01-04 15:36:41 -0600667 return;
668 }
669
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500670 auto method = bus.new_method_call(service.c_str(), INVENTORY_OBJ_PATH,
671 INVENTORY_MGR_IFACE, "Notify");
Matt Spinler018a7bc2018-01-04 15:36:41 -0600672
673 method.append(std::move(object));
674
675 auto reply = bus.call(method);
Matt Spinler018a7bc2018-01-04 15:36:41 -0600676 }
Patrick Williamsc1d4de52021-10-06 12:45:57 -0500677 catch (const std::exception& e)
Matt Spinler018a7bc2018-01-04 15:36:41 -0600678 {
Anwaar Hadi1f0193f2025-05-13 21:05:12 +0000679 lg2::error("Exception in updateInventory: {ERROR}, PATH={PATH}",
680 "ERROR", e, "PATH", inventoryPath);
Matt Spinler018a7bc2018-01-04 15:36:41 -0600681 }
Matt Spinler234ce0d2018-01-04 15:06:57 -0600682}
683
Matt Spinlerd734e652018-01-18 14:31:15 -0600684void PowerSupply::syncHistory()
685{
Lei YUab093322019-10-09 16:43:22 +0800686 using namespace phosphor::gpio;
Matt Spinlerd734e652018-01-18 14:31:15 -0600687
688 if (syncGPIODevPath.empty())
689 {
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500690 // Sync not implemented
Matt Spinlerd734e652018-01-18 14:31:15 -0600691 return;
692 }
693
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500694 GPIO gpio{syncGPIODevPath, static_cast<gpioNum_t>(syncGPIONumber),
Matt Spinlerd734e652018-01-18 14:31:15 -0600695 Direction::output};
696
697 try
698 {
699 gpio.set(Value::low);
700
701 std::this_thread::sleep_for(std::chrono::milliseconds{5});
702
703 gpio.set(Value::high);
704
705 recordManager->clear();
706 }
Patrick Williamsc1d4de52021-10-06 12:45:57 -0500707 catch (const std::exception& e)
Matt Spinlerd734e652018-01-18 14:31:15 -0600708 {
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500709 // Do nothing. There would already be a journal entry.
Matt Spinlerd734e652018-01-18 14:31:15 -0600710 }
711}
712
Patrick Williamsf5402192024-08-16 15:20:53 -0400713void PowerSupply::enableHistory(
714 const std::string& objectPath, size_t numRecords,
715 const std::string& syncGPIOPath, size_t syncGPIONum)
Matt Spinler82384142018-01-18 14:15:03 -0600716{
717 historyObjectPath = objectPath;
718 syncGPIODevPath = syncGPIOPath;
719 syncGPIONumber = syncGPIONum;
720
721 recordManager = std::make_unique<history::RecordManager>(numRecords);
722
723 auto avgPath = historyObjectPath + '/' + history::Average::name;
724 auto maxPath = historyObjectPath + '/' + history::Maximum::name;
725
726 average = std::make_unique<history::Average>(bus, avgPath);
727
728 maximum = std::make_unique<history::Maximum>(bus, maxPath);
729}
730
Matt Spinlereb169fd2018-01-18 14:19:08 -0600731void PowerSupply::updateHistory()
732{
733 if (!recordManager)
734 {
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500735 // Not enabled
Matt Spinlereb169fd2018-01-18 14:19:08 -0600736 return;
737 }
738
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500739 // Read just the most recent average/max record
Patrick Williamsf5402192024-08-16 15:20:53 -0400740 auto data =
741 pmbusIntf.readBinary(INPUT_HISTORY, pmbus::Type::HwmonDeviceDebug,
742 history::RecordManager::RAW_RECORD_SIZE);
Matt Spinlereb169fd2018-01-18 14:19:08 -0600743
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500744 // Update D-Bus only if something changed (a new record ID, or cleared out)
Matt Spinlereb169fd2018-01-18 14:19:08 -0600745 auto changed = recordManager->add(data);
746 if (changed)
747 {
Patrick Williams41a6b9f2023-05-31 19:54:59 -0500748 average->values(recordManager->getAverageRecords());
749 maximum->values(recordManager->getMaximumRecords());
Matt Spinlereb169fd2018-01-18 14:19:08 -0600750 }
751}
752
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500753} // namespace psu
754} // namespace power
Lei YUab093322019-10-09 16:43:22 +0800755} // namespace phosphor