blob: 6ebf3a9394586c0e87b787a8b0f2fd7004db67d8 [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>
Matt Spinlerf0f02b92018-10-25 16:12:43 -050028#include <phosphor-logging/log.hpp>
29#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
Brandon Wyman10295542017-08-09 18:20:44 -050044PowerSupply::PowerSupply(const std::string& name, size_t inst,
Matt Spinlerf0f02b92018-10-25 16:12:43 -050045 const std::string& objpath, const std::string& invpath,
Patrick Williams7354ce62022-07-22 19:26:56 -050046 sdbusplus::bus_t& bus, const sdeventplus::Event& e,
Matt Spinlerf0f02b92018-10-25 16:12:43 -050047 std::chrono::seconds& t, std::chrono::seconds& p) :
Patrick Williamsf5402192024-08-16 15:20:53 -040048 Device(name, inst), monitorPath(objpath), pmbusIntf(objpath),
Matt Spinlerf0f02b92018-10-25 16:12:43 -050049 inventoryPath(INVENTORY_OBJ_PATH + invpath), bus(bus), presentInterval(p),
50 presentTimer(e, std::bind([this]() {
Patrick Williamsf5402192024-08-16 15:20:53 -040051 // The hwmon path may have changed.
52 pmbusIntf.findHwmonDir();
53 this->present = true;
Matt Spinler234ce0d2018-01-04 15:06:57 -060054
Patrick Williamsf5402192024-08-16 15:20:53 -040055 // Sync the INPUT_HISTORY data for all PSs
56 syncHistory();
Matt Spinlerd734e652018-01-18 14:31:15 -060057
Patrick Williamsf5402192024-08-16 15:20:53 -040058 // Update the inventory for the new device
59 updateInventory();
60 })),
Matt Spinlerf0f02b92018-10-25 16:12:43 -050061 powerOnInterval(t),
62 powerOnTimer(e, std::bind([this]() { this->powerOn = true; }))
Brandon Wyman10295542017-08-09 18:20:44 -050063{
George Liu690e7802019-08-23 11:04:01 +080064 getAccessType();
65
Brandon Wyman10295542017-08-09 18:20:44 -050066 using namespace sdbusplus::bus;
Lei YUab093322019-10-09 16:43:22 +080067 using namespace phosphor::pmbus;
Aatir Manzur817f8a72019-08-07 07:42:15 -050068 std::uint16_t statusWord = 0;
69 try
70 {
71 // Read the 2 byte STATUS_WORD value to check for faults.
72 statusWord = pmbusIntf.read(STATUS_WORD, Type::Debug);
73 if (!((statusWord & status_word::INPUT_FAULT_WARN) ||
74 (statusWord & status_word::VIN_UV_FAULT)))
75 {
76 resolveError(inventoryPath,
77 std::string(PowerSupplyInputFault::errName));
78 }
79 }
Patrick Williamsc1d4de52021-10-06 12:45:57 -050080 catch (const ReadFailure& e)
Aatir Manzur817f8a72019-08-07 07:42:15 -050081 {
82 log<level::INFO>("Unable to read the 2 byte STATUS_WORD value to check "
83 "for power-supply input faults.");
84 }
Matt Spinlerf0f02b92018-10-25 16:12:43 -050085 presentMatch = std::make_unique<match_t>(
86 bus, match::rules::propertiesChanged(inventoryPath, INVENTORY_IFACE),
87 [this](auto& msg) { this->inventoryChanged(msg); });
Brandon Wyman431fbe42017-08-18 16:22:09 -050088 // Get initial presence state.
Brandon Wyman253dc9b2017-08-12 13:45:52 -050089 updatePresence();
Brandon Wyman431fbe42017-08-18 16:22:09 -050090
Matt Spinler234ce0d2018-01-04 15:06:57 -060091 // Write the SN, PN, etc to the inventory
92 updateInventory();
93
Brandon Wyman431fbe42017-08-18 16:22:09 -050094 // Subscribe to power state changes
Matt Spinlerf0f02b92018-10-25 16:12:43 -050095 powerOnMatch = std::make_unique<match_t>(
96 bus, match::rules::propertiesChanged(POWER_OBJ_PATH, POWER_IFACE),
97 [this](auto& msg) { this->powerStateChanged(msg); });
Brandon Wyman431fbe42017-08-18 16:22:09 -050098 // Get initial power state.
99 updatePowerState();
Brandon Wyman10295542017-08-09 18:20:44 -0500100}
Brandon Wyman442035f2017-08-08 15:58:45 -0500101
George Liu690e7802019-08-23 11:04:01 +0800102void PowerSupply::getAccessType()
103{
Lei YUab093322019-10-09 16:43:22 +0800104 using namespace phosphor::power::util;
George Liu690e7802019-08-23 11:04:01 +0800105 fruJson = loadJSONFromFile(PSU_JSON_PATH);
106 if (fruJson == nullptr)
107 {
108 log<level::ERR>("InternalFailure when parsing the JSON file");
109 return;
110 }
Lei YU40705462019-10-09 17:07:11 +0800111 inventoryPMBusAccessType = getPMBusAccessType(fruJson);
George Liu690e7802019-08-23 11:04:01 +0800112}
113
Brandon Wymana1e96342017-09-25 16:47:44 -0500114void PowerSupply::captureCmd(util::NamesValues& nv, const std::string& cmd,
Lei YUab093322019-10-09 16:43:22 +0800115 phosphor::pmbus::Type type)
Brandon Wymana1e96342017-09-25 16:47:44 -0500116{
117 if (pmbusIntf.exists(cmd, type))
118 {
119 try
120 {
121 auto val = pmbusIntf.read(cmd, type);
122 nv.add(cmd, val);
123 }
Patrick Williamsc1d4de52021-10-06 12:45:57 -0500124 catch (const std::exception& e)
Brandon Wymana1e96342017-09-25 16:47:44 -0500125 {
Joseph Reynolds5b485962018-05-17 16:05:06 -0500126 log<level::INFO>("Unable to capture metadata",
127 entry("CMD=%s", cmd.c_str()));
Brandon Wymana1e96342017-09-25 16:47:44 -0500128 }
129 }
130}
Brandon Wyman431fbe42017-08-18 16:22:09 -0500131
Brandon Wyman1db9a9e2017-07-26 18:50:22 -0500132void PowerSupply::analyze()
133{
Lei YUab093322019-10-09 16:43:22 +0800134 using namespace phosphor::pmbus;
Brandon Wyman442035f2017-08-08 15:58:45 -0500135
136 try
137 {
Brandon Wyman10295542017-08-09 18:20:44 -0500138 if (present)
Brandon Wyman442035f2017-08-08 15:58:45 -0500139 {
Brandon Wyman764c7972017-08-22 17:05:36 -0500140 std::uint16_t statusWord = 0;
Brandon Wyman764c7972017-08-22 17:05:36 -0500141
142 // Read the 2 byte STATUS_WORD value to check for faults.
143 statusWord = pmbusIntf.read(STATUS_WORD, Type::Debug);
Brandon Wymane4af9802017-11-13 15:58:33 -0600144 readFail = 0;
Brandon Wyman764c7972017-08-22 17:05:36 -0500145
Brandon Wyman603cc002017-08-28 18:17:58 -0500146 checkInputFault(statusWord);
Brandon Wyman764c7972017-08-22 17:05:36 -0500147
Brandon Wyman654eb6e2018-02-14 14:55:47 -0600148 if (powerOn && (inputFault == 0) && !faultFound)
Brandon Wyman764c7972017-08-22 17:05:36 -0500149 {
Brandon Wyman12661f12017-08-31 15:28:21 -0500150 checkFanFault(statusWord);
Brandon Wyman875b3632017-09-13 18:46:03 -0500151 checkTemperatureFault(statusWord);
Brandon Wymancfa032b2017-09-25 17:37:50 -0500152 checkOutputOvervoltageFault(statusWord);
153 checkCurrentOutOverCurrentFault(statusWord);
154 checkPGOrUnitOffFault(statusWord);
Brandon Wyman442035f2017-08-08 15:58:45 -0500155 }
Matt Spinlereb169fd2018-01-18 14:19:08 -0600156
157 updateHistory();
Brandon Wyman442035f2017-08-08 15:58:45 -0500158 }
159 }
Patrick Williamsc1d4de52021-10-06 12:45:57 -0500160 catch (const ReadFailure& e)
Brandon Wyman442035f2017-08-08 15:58:45 -0500161 {
Brandon Wymane4af9802017-11-13 15:58:33 -0600162 if (readFail < FAULT_COUNT)
163 {
164 readFail++;
165 }
166
167 if (!readFailLogged && readFail >= FAULT_COUNT)
Brandon Wyman442035f2017-08-08 15:58:45 -0500168 {
169 commit<ReadFailure>();
170 readFailLogged = true;
Brandon Wyman442035f2017-08-08 15:58:45 -0500171 }
172 }
173
Brandon Wyman1db9a9e2017-07-26 18:50:22 -0500174 return;
175}
176
Patrick Williams7354ce62022-07-22 19:26:56 -0500177void PowerSupply::inventoryChanged(sdbusplus::message_t& msg)
Brandon Wyman10295542017-08-09 18:20:44 -0500178{
179 std::string msgSensor;
Patrick Williamsabe49412020-05-13 17:59:47 -0500180 std::map<std::string, std::variant<uint32_t, bool>> msgData;
Brandon Wyman10295542017-08-09 18:20:44 -0500181 msg.read(msgSensor, msgData);
182
183 // Check if it was the Present property that changed.
184 auto valPropMap = msgData.find(PRESENT_PROP);
185 if (valPropMap != msgData.end())
186 {
Patrick Williams365d61c2020-05-13 12:23:08 -0500187 if (std::get<bool>(valPropMap->second))
Brandon Wyman10295542017-08-09 18:20:44 -0500188 {
Brandon Wyman6ccce0b2017-10-26 15:13:10 -0500189 clearFaults();
William A. Kennington III1a0c9172018-10-18 17:57:49 -0700190 presentTimer.restartOnce(presentInterval);
Brandon Wyman590fc282017-11-01 18:22:25 -0500191 }
192 else
193 {
Brandon Wyman2ef48cf2017-11-21 15:43:54 -0600194 present = false;
William A. Kennington III1a0c9172018-10-18 17:57:49 -0700195 presentTimer.setEnabled(false);
Matt Spinler234ce0d2018-01-04 15:06:57 -0600196
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500197 // Clear out the now outdated inventory properties
Matt Spinler234ce0d2018-01-04 15:06:57 -0600198 updateInventory();
Brandon Wyman10295542017-08-09 18:20:44 -0500199 }
200 }
201
202 return;
203}
204
205void PowerSupply::updatePresence()
206{
207 // Use getProperty utility function to get presence status.
Brandon Wyman10295542017-08-09 18:20:44 -0500208 std::string service = "xyz.openbmc_project.Inventory.Manager";
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500209 util::getProperty(INVENTORY_IFACE, PRESENT_PROP, inventoryPath, service,
210 bus, this->present);
Brandon Wyman10295542017-08-09 18:20:44 -0500211}
212
Patrick Williams7354ce62022-07-22 19:26:56 -0500213void PowerSupply::powerStateChanged(sdbusplus::message_t& msg)
Brandon Wyman431fbe42017-08-18 16:22:09 -0500214{
215 int32_t state = 0;
216 std::string msgSensor;
Patrick Williamsabe49412020-05-13 17:59:47 -0500217 std::map<std::string, std::variant<int32_t>> msgData;
Brandon Wyman431fbe42017-08-18 16:22:09 -0500218 msg.read(msgSensor, msgData);
219
220 // Check if it was the Present property that changed.
221 auto valPropMap = msgData.find("state");
222 if (valPropMap != msgData.end())
223 {
Patrick Williams365d61c2020-05-13 12:23:08 -0500224 state = std::get<int32_t>(valPropMap->second);
Brandon Wyman431fbe42017-08-18 16:22:09 -0500225
226 // Power is on when state=1. Set the fault logged variables to false
227 // and start the power on timer when the state changes to 1.
228 if (state)
229 {
Brandon Wyman6ccce0b2017-10-26 15:13:10 -0500230 clearFaults();
William A. Kennington III1a0c9172018-10-18 17:57:49 -0700231 powerOnTimer.restartOnce(powerOnInterval);
Brandon Wyman431fbe42017-08-18 16:22:09 -0500232 }
233 else
234 {
William A. Kennington III1a0c9172018-10-18 17:57:49 -0700235 powerOnTimer.setEnabled(false);
Brandon Wyman431fbe42017-08-18 16:22:09 -0500236 powerOn = false;
237 }
238 }
Brandon Wyman431fbe42017-08-18 16:22:09 -0500239}
240
241void PowerSupply::updatePowerState()
242{
Lei YUcfc040c2019-10-29 17:10:26 +0800243 powerOn = util::isPoweredOn(bus);
Brandon Wyman431fbe42017-08-18 16:22:09 -0500244}
245
Brandon Wyman603cc002017-08-28 18:17:58 -0500246void PowerSupply::checkInputFault(const uint16_t statusWord)
247{
Lei YUab093322019-10-09 16:43:22 +0800248 using namespace phosphor::pmbus;
Brandon Wyman603cc002017-08-28 18:17:58 -0500249
Brandon Wymana3c675c2017-11-14 14:54:54 -0600250 if ((inputFault < FAULT_COUNT) &&
251 ((statusWord & status_word::INPUT_FAULT_WARN) ||
252 (statusWord & status_word::VIN_UV_FAULT)))
Brandon Wyman603cc002017-08-28 18:17:58 -0500253 {
Brandon Wymanad708242018-01-19 17:28:35 -0600254 if (inputFault == 0)
255 {
256 log<level::INFO>("INPUT or VIN_UV fault",
257 entry("STATUS_WORD=0x%04X", statusWord));
258 }
259
Brandon Wymana3c675c2017-11-14 14:54:54 -0600260 inputFault++;
Brandon Wyman603cc002017-08-28 18:17:58 -0500261 }
262 else
263 {
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500264 if ((inputFault > 0) && !(statusWord & status_word::INPUT_FAULT_WARN) &&
Brandon Wymand20686a2017-11-01 17:45:23 -0500265 !(statusWord & status_word::VIN_UV_FAULT))
Brandon Wyman603cc002017-08-28 18:17:58 -0500266 {
Brandon Wymana3c675c2017-11-14 14:54:54 -0600267 inputFault = 0;
Brandon Wyman3343e822017-11-03 16:54:11 -0500268 faultFound = false;
Brandon Wyman7502f6c2018-02-12 20:20:39 -0600269 // When an input fault occurs, the power supply cannot be on.
270 // However, the check for the case where the power supply should be
271 // on will stop when there is a fault found.
272 // Clear the powerOnFault when the inputFault is cleared to reset
273 // the powerOnFault de-glitching.
274 powerOnFault = 0;
Brandon Wyman69591bd2017-11-01 18:07:23 -0500275
Brandon Wyman603cc002017-08-28 18:17:58 -0500276 log<level::INFO>("INPUT_FAULT_WARN cleared",
Brandon Wymana3c675c2017-11-14 14:54:54 -0600277 entry("POWERSUPPLY=%s", inventoryPath.c_str()));
Brandon Wyman69591bd2017-11-01 18:07:23 -0500278
Brandon Wyman08b05712017-11-30 17:53:56 -0600279 resolveError(inventoryPath,
280 std::string(PowerSupplyInputFault::errName));
281
Brandon Wyman69591bd2017-11-01 18:07:23 -0500282 if (powerOn)
283 {
284 // The power supply will not be immediately powered on after
285 // the input power is restored.
286 powerOn = false;
287 // Start up the timer that will set the state to indicate we
288 // are ready for the powered on fault checks.
William A. Kennington III1a0c9172018-10-18 17:57:49 -0700289 powerOnTimer.restartOnce(powerOnInterval);
Brandon Wyman69591bd2017-11-01 18:07:23 -0500290 }
Brandon Wyman603cc002017-08-28 18:17:58 -0500291 }
292 }
Brandon Wymana3c675c2017-11-14 14:54:54 -0600293
294 if (!faultFound && (inputFault >= FAULT_COUNT))
295 {
Brandon Wymanad708242018-01-19 17:28:35 -0600296 // If the power is on, report the fault in an error log entry.
297 if (powerOn)
298 {
299 util::NamesValues nv;
300 nv.add("STATUS_WORD", statusWord);
301 captureCmd(nv, STATUS_INPUT, Type::Debug);
Brandon Wymana3c675c2017-11-14 14:54:54 -0600302
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500303 using metadata =
304 org::open_power::Witherspoon::Fault::PowerSupplyInputFault;
Brandon Wymana3c675c2017-11-14 14:54:54 -0600305
Brandon Wymanad708242018-01-19 17:28:35 -0600306 report<PowerSupplyInputFault>(
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500307 metadata::RAW_STATUS(nv.get().c_str()),
308 metadata::CALLOUT_INVENTORY_PATH(inventoryPath.c_str()));
Brandon Wymanad708242018-01-19 17:28:35 -0600309
310 faultFound = true;
311 }
Brandon Wymana3c675c2017-11-14 14:54:54 -0600312 }
Brandon Wyman603cc002017-08-28 18:17:58 -0500313}
314
315void PowerSupply::checkPGOrUnitOffFault(const uint16_t statusWord)
316{
Lei YUab093322019-10-09 16:43:22 +0800317 using namespace phosphor::pmbus;
Brandon Wyman603cc002017-08-28 18:17:58 -0500318
Brandon Wyman593d24f2017-10-13 18:15:23 -0500319 if (powerOnFault < FAULT_COUNT)
Brandon Wyman603cc002017-08-28 18:17:58 -0500320 {
Brandon Wyman593d24f2017-10-13 18:15:23 -0500321 // Check PG# and UNIT_IS_OFF
322 if ((statusWord & status_word::POWER_GOOD_NEGATED) ||
323 (statusWord & status_word::UNIT_IS_OFF))
324 {
325 log<level::INFO>("PGOOD or UNIT_IS_OFF bit bad",
326 entry("STATUS_WORD=0x%04X", statusWord));
327 powerOnFault++;
328 }
329 else
330 {
331 if (powerOnFault > 0)
332 {
333 log<level::INFO>("PGOOD and UNIT_IS_OFF bits good");
334 powerOnFault = 0;
335 }
336 }
Brandon Wyman603cc002017-08-28 18:17:58 -0500337
Brandon Wymane2fc7aa2017-11-13 17:37:10 -0600338 if (!faultFound && (powerOnFault >= FAULT_COUNT))
Brandon Wyman593d24f2017-10-13 18:15:23 -0500339 {
Brandon Wyman3343e822017-11-03 16:54:11 -0500340 faultFound = true;
341
Brandon Wyman593d24f2017-10-13 18:15:23 -0500342 util::NamesValues nv;
343 nv.add("STATUS_WORD", statusWord);
344 captureCmd(nv, STATUS_INPUT, Type::Debug);
345 auto status0Vout = pmbusIntf.insertPageNum(STATUS_VOUT, 0);
346 captureCmd(nv, status0Vout, Type::Debug);
347 captureCmd(nv, STATUS_IOUT, Type::Debug);
348 captureCmd(nv, STATUS_MFR, Type::Debug);
Brandon Wyman603cc002017-08-28 18:17:58 -0500349
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500350 using metadata =
351 org::open_power::Witherspoon::Fault::PowerSupplyShouldBeOn;
Brandon Wyman603cc002017-08-28 18:17:58 -0500352
Brandon Wyman593d24f2017-10-13 18:15:23 -0500353 // A power supply is OFF (or pgood low) but should be on.
354 report<PowerSupplyShouldBeOn>(
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500355 metadata::RAW_STATUS(nv.get().c_str()),
356 metadata::CALLOUT_INVENTORY_PATH(inventoryPath.c_str()));
Brandon Wyman593d24f2017-10-13 18:15:23 -0500357 }
Brandon Wyman603cc002017-08-28 18:17:58 -0500358 }
Brandon Wyman603cc002017-08-28 18:17:58 -0500359}
360
361void PowerSupply::checkCurrentOutOverCurrentFault(const uint16_t statusWord)
362{
Lei YUab093322019-10-09 16:43:22 +0800363 using namespace phosphor::pmbus;
Brandon Wyman603cc002017-08-28 18:17:58 -0500364
Brandon Wymandd61be42017-11-07 18:38:54 -0600365 if (outputOCFault < FAULT_COUNT)
Brandon Wyman603cc002017-08-28 18:17:58 -0500366 {
Brandon Wymandd61be42017-11-07 18:38:54 -0600367 // Check for an output overcurrent fault.
368 if ((statusWord & status_word::IOUT_OC_FAULT))
369 {
370 outputOCFault++;
371 }
372 else
373 {
374 if (outputOCFault > 0)
375 {
376 outputOCFault = 0;
377 }
378 }
Brandon Wyman603cc002017-08-28 18:17:58 -0500379
Brandon Wymane2fc7aa2017-11-13 17:37:10 -0600380 if (!faultFound && (outputOCFault >= FAULT_COUNT))
Brandon Wymandd61be42017-11-07 18:38:54 -0600381 {
382 util::NamesValues nv;
383 nv.add("STATUS_WORD", statusWord);
384 captureCmd(nv, STATUS_INPUT, Type::Debug);
385 auto status0Vout = pmbusIntf.insertPageNum(STATUS_VOUT, 0);
386 captureCmd(nv, status0Vout, Type::Debug);
387 captureCmd(nv, STATUS_IOUT, Type::Debug);
388 captureCmd(nv, STATUS_MFR, Type::Debug);
Brandon Wyman603cc002017-08-28 18:17:58 -0500389
Brandon Wymandd61be42017-11-07 18:38:54 -0600390 using metadata = org::open_power::Witherspoon::Fault::
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500391 PowerSupplyOutputOvercurrent;
Brandon Wyman603cc002017-08-28 18:17:58 -0500392
Brandon Wymandd61be42017-11-07 18:38:54 -0600393 report<PowerSupplyOutputOvercurrent>(
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500394 metadata::RAW_STATUS(nv.get().c_str()),
395 metadata::CALLOUT_INVENTORY_PATH(inventoryPath.c_str()));
Brandon Wymandd61be42017-11-07 18:38:54 -0600396
397 faultFound = true;
398 }
Brandon Wyman603cc002017-08-28 18:17:58 -0500399 }
400}
401
Brandon Wymanab05c072017-08-30 18:26:41 -0500402void PowerSupply::checkOutputOvervoltageFault(const uint16_t statusWord)
403{
Lei YUab093322019-10-09 16:43:22 +0800404 using namespace phosphor::pmbus;
Brandon Wymanab05c072017-08-30 18:26:41 -0500405
Brandon Wyman2ab319b2017-11-08 17:34:59 -0600406 if (outputOVFault < FAULT_COUNT)
Brandon Wymanab05c072017-08-30 18:26:41 -0500407 {
Brandon Wyman2ab319b2017-11-08 17:34:59 -0600408 // Check for an output overvoltage fault.
409 if (statusWord & status_word::VOUT_OV_FAULT)
410 {
411 outputOVFault++;
412 }
413 else
414 {
415 if (outputOVFault > 0)
416 {
417 outputOVFault = 0;
418 }
419 }
Brandon Wymanab05c072017-08-30 18:26:41 -0500420
Brandon Wymane2fc7aa2017-11-13 17:37:10 -0600421 if (!faultFound && (outputOVFault >= FAULT_COUNT))
Brandon Wyman2ab319b2017-11-08 17:34:59 -0600422 {
423 util::NamesValues nv;
424 nv.add("STATUS_WORD", statusWord);
425 captureCmd(nv, STATUS_INPUT, Type::Debug);
426 auto status0Vout = pmbusIntf.insertPageNum(STATUS_VOUT, 0);
427 captureCmd(nv, status0Vout, Type::Debug);
428 captureCmd(nv, STATUS_IOUT, Type::Debug);
429 captureCmd(nv, STATUS_MFR, Type::Debug);
Brandon Wymanab05c072017-08-30 18:26:41 -0500430
Brandon Wyman2ab319b2017-11-08 17:34:59 -0600431 using metadata = org::open_power::Witherspoon::Fault::
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500432 PowerSupplyOutputOvervoltage;
Brandon Wymanab05c072017-08-30 18:26:41 -0500433
Brandon Wyman2ab319b2017-11-08 17:34:59 -0600434 report<PowerSupplyOutputOvervoltage>(
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500435 metadata::RAW_STATUS(nv.get().c_str()),
436 metadata::CALLOUT_INVENTORY_PATH(inventoryPath.c_str()));
Brandon Wyman2ab319b2017-11-08 17:34:59 -0600437
438 faultFound = true;
439 }
Brandon Wymanab05c072017-08-30 18:26:41 -0500440 }
441}
442
Brandon Wyman12661f12017-08-31 15:28:21 -0500443void PowerSupply::checkFanFault(const uint16_t statusWord)
444{
Lei YUab093322019-10-09 16:43:22 +0800445 using namespace phosphor::pmbus;
Brandon Wyman12661f12017-08-31 15:28:21 -0500446
Brandon Wymanba255532017-11-08 17:44:10 -0600447 if (fanFault < FAULT_COUNT)
Brandon Wyman12661f12017-08-31 15:28:21 -0500448 {
Brandon Wymanba255532017-11-08 17:44:10 -0600449 // Check for a fan fault or warning condition
450 if (statusWord & status_word::FAN_FAULT)
451 {
452 fanFault++;
453 }
454 else
455 {
456 if (fanFault > 0)
457 {
458 fanFault = 0;
459 }
460 }
Brandon Wyman12661f12017-08-31 15:28:21 -0500461
Brandon Wymane2fc7aa2017-11-13 17:37:10 -0600462 if (!faultFound && (fanFault >= FAULT_COUNT))
Brandon Wymanba255532017-11-08 17:44:10 -0600463 {
464 util::NamesValues nv;
465 nv.add("STATUS_WORD", statusWord);
466 captureCmd(nv, STATUS_MFR, Type::Debug);
467 captureCmd(nv, STATUS_TEMPERATURE, Type::Debug);
468 captureCmd(nv, STATUS_FANS_1_2, Type::Debug);
Brandon Wyman12661f12017-08-31 15:28:21 -0500469
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500470 using metadata =
471 org::open_power::Witherspoon::Fault::PowerSupplyFanFault;
Brandon Wyman12661f12017-08-31 15:28:21 -0500472
Brandon Wymanba255532017-11-08 17:44:10 -0600473 report<PowerSupplyFanFault>(
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500474 metadata::RAW_STATUS(nv.get().c_str()),
475 metadata::CALLOUT_INVENTORY_PATH(inventoryPath.c_str()));
Brandon Wymanba255532017-11-08 17:44:10 -0600476
477 faultFound = true;
478 }
Brandon Wyman12661f12017-08-31 15:28:21 -0500479 }
480}
481
Brandon Wyman875b3632017-09-13 18:46:03 -0500482void PowerSupply::checkTemperatureFault(const uint16_t statusWord)
483{
Lei YUab093322019-10-09 16:43:22 +0800484 using namespace phosphor::pmbus;
Brandon Wyman875b3632017-09-13 18:46:03 -0500485
486 // Due to how the PMBus core device driver sends a clear faults command
487 // the bit in STATUS_WORD will likely be cleared when we attempt to examine
488 // it for a Thermal Fault or Warning. So, check the STATUS_WORD and the
489 // STATUS_TEMPERATURE bits. If either indicates a fault, proceed with
490 // logging the over-temperature condition.
491 std::uint8_t statusTemperature = 0;
492 statusTemperature = pmbusIntf.read(STATUS_TEMPERATURE, Type::Debug);
Brandon Wyman50044ea2017-11-08 17:58:56 -0600493 if (temperatureFault < FAULT_COUNT)
Brandon Wyman875b3632017-09-13 18:46:03 -0500494 {
Brandon Wyman50044ea2017-11-08 17:58:56 -0600495 if ((statusWord & status_word::TEMPERATURE_FAULT_WARN) ||
496 (statusTemperature & status_temperature::OT_FAULT))
497 {
498 temperatureFault++;
499 }
500 else
501 {
502 if (temperatureFault > 0)
503 {
504 temperatureFault = 0;
505 }
506 }
Brandon Wyman875b3632017-09-13 18:46:03 -0500507
Brandon Wymane2fc7aa2017-11-13 17:37:10 -0600508 if (!faultFound && (temperatureFault >= FAULT_COUNT))
Brandon Wyman50044ea2017-11-08 17:58:56 -0600509 {
510 // The power supply has had an over-temperature condition.
511 // This may not result in a shutdown if experienced for a short
512 // duration.
513 // This should not occur under normal conditions.
514 // The power supply may be faulty, or the paired supply may be
515 // putting out less current.
516 // Capture command responses with potentially relevant information,
517 // and call out the power supply reporting the condition.
518 util::NamesValues nv;
519 nv.add("STATUS_WORD", statusWord);
520 captureCmd(nv, STATUS_MFR, Type::Debug);
521 captureCmd(nv, STATUS_IOUT, Type::Debug);
522 nv.add("STATUS_TEMPERATURE", statusTemperature);
523 captureCmd(nv, STATUS_FANS_1_2, Type::Debug);
Brandon Wyman875b3632017-09-13 18:46:03 -0500524
Brandon Wyman50044ea2017-11-08 17:58:56 -0600525 using metadata = org::open_power::Witherspoon::Fault::
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500526 PowerSupplyTemperatureFault;
Brandon Wyman875b3632017-09-13 18:46:03 -0500527
Brandon Wyman50044ea2017-11-08 17:58:56 -0600528 report<PowerSupplyTemperatureFault>(
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500529 metadata::RAW_STATUS(nv.get().c_str()),
530 metadata::CALLOUT_INVENTORY_PATH(inventoryPath.c_str()));
Brandon Wyman50044ea2017-11-08 17:58:56 -0600531
532 faultFound = true;
533 }
Brandon Wyman875b3632017-09-13 18:46:03 -0500534 }
535}
536
Brandon Wyman1db9a9e2017-07-26 18:50:22 -0500537void PowerSupply::clearFaults()
538{
Brandon Wymane4af9802017-11-13 15:58:33 -0600539 readFail = 0;
Brandon Wyman6ccce0b2017-10-26 15:13:10 -0500540 readFailLogged = false;
Brandon Wymana3c675c2017-11-14 14:54:54 -0600541 inputFault = 0;
Brandon Wyman6ccce0b2017-10-26 15:13:10 -0500542 powerOnFault = 0;
Brandon Wymandd61be42017-11-07 18:38:54 -0600543 outputOCFault = 0;
Brandon Wyman2ab319b2017-11-08 17:34:59 -0600544 outputOVFault = 0;
Brandon Wymanba255532017-11-08 17:44:10 -0600545 fanFault = 0;
Brandon Wyman50044ea2017-11-08 17:58:56 -0600546 temperatureFault = 0;
Brandon Wyman3343e822017-11-03 16:54:11 -0500547 faultFound = false;
Brandon Wyman6ccce0b2017-10-26 15:13:10 -0500548
Brandon Wyman1db9a9e2017-07-26 18:50:22 -0500549 return;
550}
551
Brandon Wyman43ce2082017-11-30 17:24:01 -0600552void PowerSupply::resolveError(const std::string& callout,
553 const std::string& message)
554{
Brandon Wyman01741f12017-12-01 17:22:08 -0600555 using EndpointList = std::vector<std::string>;
556
557 try
558 {
559 auto path = callout + "/fault";
560 // Get the service name from the mapper for the fault callout
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500561 auto service = util::getService(path, ASSOCIATION_IFACE, bus);
Brandon Wyman01741f12017-12-01 17:22:08 -0600562
563 // Use getProperty utility function to get log entries (endpoints)
564 EndpointList logEntries;
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500565 util::getProperty(ASSOCIATION_IFACE, ENDPOINTS_PROP, path, service, bus,
566 logEntries);
Brandon Wyman01741f12017-12-01 17:22:08 -0600567
568 // It is possible that all such entries for this callout have since
569 // been deleted.
570 if (logEntries.empty())
571 {
572 return;
573 }
574
Patrick Williamsf5402192024-08-16 15:20:53 -0400575 auto logEntryService =
576 util::getService(logEntries[0], LOGGING_IFACE, bus);
Brandon Wyman01741f12017-12-01 17:22:08 -0600577 if (logEntryService.empty())
578 {
579 return;
580 }
581
582 // go through each log entry that matches this callout path
583 std::string logMessage;
584 for (const auto& logEntry : logEntries)
585 {
586 // Check to see if this logEntry has a message that matches.
Matt Spinler589e8722018-01-04 15:24:49 -0600587 util::getProperty(LOGGING_IFACE, MESSAGE_PROP, logEntry,
Brandon Wyman01741f12017-12-01 17:22:08 -0600588 logEntryService, bus, logMessage);
589
590 if (message == logMessage)
591 {
592 // Log entry matches call out and message, set Resolved to true
593 bool resolved = true;
Matt Spinler589e8722018-01-04 15:24:49 -0600594 util::setProperty(LOGGING_IFACE, RESOLVED_PROP, logEntry,
Brandon Wyman01741f12017-12-01 17:22:08 -0600595 logEntryService, bus, resolved);
596 }
Brandon Wyman01741f12017-12-01 17:22:08 -0600597 }
Brandon Wyman01741f12017-12-01 17:22:08 -0600598 }
Patrick Williamsc1d4de52021-10-06 12:45:57 -0500599 catch (const std::exception& e)
Brandon Wyman01741f12017-12-01 17:22:08 -0600600 {
601 log<level::INFO>("Failed to resolve error",
602 entry("CALLOUT=%s", callout.c_str()),
Matt Spinler0d09f292018-01-22 14:51:26 -0600603 entry("ERROR=%s", message.c_str()));
Brandon Wyman01741f12017-12-01 17:22:08 -0600604 }
Brandon Wyman43ce2082017-11-30 17:24:01 -0600605}
606
Matt Spinler234ce0d2018-01-04 15:06:57 -0600607void PowerSupply::updateInventory()
608{
Lei YUab093322019-10-09 16:43:22 +0800609 using namespace phosphor::pmbus;
Matt Spinler018a7bc2018-01-04 15:36:41 -0600610 using namespace sdbusplus::message;
611
Matt Spinler018a7bc2018-01-04 15:36:41 -0600612 // Build the object map and send it to the inventory
George Liu070c1bc2020-10-12 11:28:01 +0800613 using Properties = std::map<std::string, std::variant<std::string, bool>>;
Matt Spinler018a7bc2018-01-04 15:36:41 -0600614 using Interfaces = std::map<std::string, Properties>;
615 using Object = std::map<object_path, Interfaces>;
616 Properties assetProps;
George Liu070c1bc2020-10-12 11:28:01 +0800617 Properties operProps;
Matt Spinler018a7bc2018-01-04 15:36:41 -0600618 Interfaces interfaces;
619 Object object;
620
George Liu690e7802019-08-23 11:04:01 +0800621 // If any of these accesses fail, the fields will just be
622 // blank in the inventory. Leave logging ReadFailure errors
623 // to analyze() as it runs continuously and will most
624 // likely hit and threshold them first anyway. The
625 // readString() function will do the tracing of the failing
626 // path so this code doesn't need to.
627 for (const auto& fru : fruJson.at("fruConfigs"))
628 {
629 if (fru.at("interface") == ASSET_IFACE)
630 {
631 try
632 {
633 assetProps.emplace(
634 fru.at("propertyName"),
635 present ? pmbusIntf.readString(fru.at("fileName"),
636 inventoryPMBusAccessType)
637 : "");
638 }
Patrick Williamsc1d4de52021-10-06 12:45:57 -0500639 catch (const ReadFailure& e)
Adriana Kobylak0c9a33d2021-09-13 18:05:09 +0000640 {}
George Liu690e7802019-08-23 11:04:01 +0800641 }
George Liu690e7802019-08-23 11:04:01 +0800642 }
Matt Spinler018a7bc2018-01-04 15:36:41 -0600643
George Liu070c1bc2020-10-12 11:28:01 +0800644 operProps.emplace(FUNCTIONAL_PROP, present);
George Liu690e7802019-08-23 11:04:01 +0800645 interfaces.emplace(ASSET_IFACE, std::move(assetProps));
George Liu070c1bc2020-10-12 11:28:01 +0800646 interfaces.emplace(OPERATIONAL_STATE_IFACE, std::move(operProps));
Matt Spinler018a7bc2018-01-04 15:36:41 -0600647
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500648 // For Notify(), just send the relative path of the inventory
649 // object so remove the INVENTORY_OBJ_PATH prefix
Matt Spinler018a7bc2018-01-04 15:36:41 -0600650 auto path = inventoryPath.substr(strlen(INVENTORY_OBJ_PATH));
651
652 object.emplace(path, std::move(interfaces));
653
654 try
655 {
Patrick Williamsf5402192024-08-16 15:20:53 -0400656 auto service =
657 util::getService(INVENTORY_OBJ_PATH, INVENTORY_MGR_IFACE, bus);
Matt Spinler018a7bc2018-01-04 15:36:41 -0600658
659 if (service.empty())
660 {
661 log<level::ERR>("Unable to get inventory manager service");
662 return;
663 }
664
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500665 auto method = bus.new_method_call(service.c_str(), INVENTORY_OBJ_PATH,
666 INVENTORY_MGR_IFACE, "Notify");
Matt Spinler018a7bc2018-01-04 15:36:41 -0600667
668 method.append(std::move(object));
669
670 auto reply = bus.call(method);
Matt Spinler018a7bc2018-01-04 15:36:41 -0600671 }
Patrick Williamsc1d4de52021-10-06 12:45:57 -0500672 catch (const std::exception& e)
Matt Spinler018a7bc2018-01-04 15:36:41 -0600673 {
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500674 log<level::ERR>(e.what(), entry("PATH=%s", inventoryPath.c_str()));
Matt Spinler018a7bc2018-01-04 15:36:41 -0600675 }
Matt Spinler234ce0d2018-01-04 15:06:57 -0600676}
677
Matt Spinlerd734e652018-01-18 14:31:15 -0600678void PowerSupply::syncHistory()
679{
Lei YUab093322019-10-09 16:43:22 +0800680 using namespace phosphor::gpio;
Matt Spinlerd734e652018-01-18 14:31:15 -0600681
682 if (syncGPIODevPath.empty())
683 {
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500684 // Sync not implemented
Matt Spinlerd734e652018-01-18 14:31:15 -0600685 return;
686 }
687
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500688 GPIO gpio{syncGPIODevPath, static_cast<gpioNum_t>(syncGPIONumber),
Matt Spinlerd734e652018-01-18 14:31:15 -0600689 Direction::output};
690
691 try
692 {
693 gpio.set(Value::low);
694
695 std::this_thread::sleep_for(std::chrono::milliseconds{5});
696
697 gpio.set(Value::high);
698
699 recordManager->clear();
700 }
Patrick Williamsc1d4de52021-10-06 12:45:57 -0500701 catch (const std::exception& e)
Matt Spinlerd734e652018-01-18 14:31:15 -0600702 {
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500703 // Do nothing. There would already be a journal entry.
Matt Spinlerd734e652018-01-18 14:31:15 -0600704 }
705}
706
Patrick Williamsf5402192024-08-16 15:20:53 -0400707void PowerSupply::enableHistory(
708 const std::string& objectPath, size_t numRecords,
709 const std::string& syncGPIOPath, size_t syncGPIONum)
Matt Spinler82384142018-01-18 14:15:03 -0600710{
711 historyObjectPath = objectPath;
712 syncGPIODevPath = syncGPIOPath;
713 syncGPIONumber = syncGPIONum;
714
715 recordManager = std::make_unique<history::RecordManager>(numRecords);
716
717 auto avgPath = historyObjectPath + '/' + history::Average::name;
718 auto maxPath = historyObjectPath + '/' + history::Maximum::name;
719
720 average = std::make_unique<history::Average>(bus, avgPath);
721
722 maximum = std::make_unique<history::Maximum>(bus, maxPath);
723}
724
Matt Spinlereb169fd2018-01-18 14:19:08 -0600725void PowerSupply::updateHistory()
726{
727 if (!recordManager)
728 {
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500729 // Not enabled
Matt Spinlereb169fd2018-01-18 14:19:08 -0600730 return;
731 }
732
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500733 // Read just the most recent average/max record
Patrick Williamsf5402192024-08-16 15:20:53 -0400734 auto data =
735 pmbusIntf.readBinary(INPUT_HISTORY, pmbus::Type::HwmonDeviceDebug,
736 history::RecordManager::RAW_RECORD_SIZE);
Matt Spinlereb169fd2018-01-18 14:19:08 -0600737
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500738 // Update D-Bus only if something changed (a new record ID, or cleared out)
Matt Spinlereb169fd2018-01-18 14:19:08 -0600739 auto changed = recordManager->add(data);
740 if (changed)
741 {
Patrick Williams41a6b9f2023-05-31 19:54:59 -0500742 average->values(recordManager->getAverageRecords());
743 maximum->values(recordManager->getMaximumRecords());
Matt Spinlereb169fd2018-01-18 14:19:08 -0600744 }
745}
746
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500747} // namespace psu
748} // namespace power
Lei YUab093322019-10-09 16:43:22 +0800749} // namespace phosphor