blob: 9543afbf26a8ac074ab14b24ece15e9a55e4e712 [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,
46 sdbusplus::bus::bus& bus, const sdeventplus::Event& e,
47 std::chrono::seconds& t, std::chrono::seconds& p) :
48 Device(name, inst),
49 monitorPath(objpath), pmbusIntf(objpath),
50 inventoryPath(INVENTORY_OBJ_PATH + invpath), bus(bus), presentInterval(p),
51 presentTimer(e, std::bind([this]() {
52 // The hwmon path may have changed.
53 pmbusIntf.findHwmonDir();
54 this->present = true;
Matt Spinler234ce0d2018-01-04 15:06:57 -060055
Matt Spinlerf0f02b92018-10-25 16:12:43 -050056 // Sync the INPUT_HISTORY data for all PSs
57 syncHistory();
Matt Spinlerd734e652018-01-18 14:31:15 -060058
Matt Spinlerf0f02b92018-10-25 16:12:43 -050059 // Update the inventory for the new device
60 updateInventory();
61 })),
62 powerOnInterval(t),
63 powerOnTimer(e, std::bind([this]() { this->powerOn = true; }))
Brandon Wyman10295542017-08-09 18:20:44 -050064{
George Liu690e7802019-08-23 11:04:01 +080065 getAccessType();
66
Brandon Wyman10295542017-08-09 18:20:44 -050067 using namespace sdbusplus::bus;
Lei YUab093322019-10-09 16:43:22 +080068 using namespace phosphor::pmbus;
Aatir Manzur817f8a72019-08-07 07:42:15 -050069 std::uint16_t statusWord = 0;
70 try
71 {
72 // Read the 2 byte STATUS_WORD value to check for faults.
73 statusWord = pmbusIntf.read(STATUS_WORD, Type::Debug);
74 if (!((statusWord & status_word::INPUT_FAULT_WARN) ||
75 (statusWord & status_word::VIN_UV_FAULT)))
76 {
77 resolveError(inventoryPath,
78 std::string(PowerSupplyInputFault::errName));
79 }
80 }
81 catch (ReadFailure& e)
82 {
83 log<level::INFO>("Unable to read the 2 byte STATUS_WORD value to check "
84 "for power-supply input faults.");
85 }
Matt Spinlerf0f02b92018-10-25 16:12:43 -050086 presentMatch = std::make_unique<match_t>(
87 bus, match::rules::propertiesChanged(inventoryPath, INVENTORY_IFACE),
88 [this](auto& msg) { this->inventoryChanged(msg); });
Brandon Wyman431fbe42017-08-18 16:22:09 -050089 // Get initial presence state.
Brandon Wyman253dc9b2017-08-12 13:45:52 -050090 updatePresence();
Brandon Wyman431fbe42017-08-18 16:22:09 -050091
Matt Spinler234ce0d2018-01-04 15:06:57 -060092 // Write the SN, PN, etc to the inventory
93 updateInventory();
94
Brandon Wyman431fbe42017-08-18 16:22:09 -050095 // Subscribe to power state changes
Matt Spinlerf0f02b92018-10-25 16:12:43 -050096 powerOnMatch = std::make_unique<match_t>(
97 bus, match::rules::propertiesChanged(POWER_OBJ_PATH, POWER_IFACE),
98 [this](auto& msg) { this->powerStateChanged(msg); });
Brandon Wyman431fbe42017-08-18 16:22:09 -050099 // Get initial power state.
100 updatePowerState();
Brandon Wyman10295542017-08-09 18:20:44 -0500101}
Brandon Wyman442035f2017-08-08 15:58:45 -0500102
George Liu690e7802019-08-23 11:04:01 +0800103void PowerSupply::getAccessType()
104{
Lei YUab093322019-10-09 16:43:22 +0800105 using namespace phosphor::power::util;
George Liu690e7802019-08-23 11:04:01 +0800106 fruJson = loadJSONFromFile(PSU_JSON_PATH);
107 if (fruJson == nullptr)
108 {
109 log<level::ERR>("InternalFailure when parsing the JSON file");
110 return;
111 }
Lei YU40705462019-10-09 17:07:11 +0800112 inventoryPMBusAccessType = getPMBusAccessType(fruJson);
George Liu690e7802019-08-23 11:04:01 +0800113}
114
Brandon Wymana1e96342017-09-25 16:47:44 -0500115void PowerSupply::captureCmd(util::NamesValues& nv, const std::string& cmd,
Lei YUab093322019-10-09 16:43:22 +0800116 phosphor::pmbus::Type type)
Brandon Wymana1e96342017-09-25 16:47:44 -0500117{
118 if (pmbusIntf.exists(cmd, type))
119 {
120 try
121 {
122 auto val = pmbusIntf.read(cmd, type);
123 nv.add(cmd, val);
124 }
125 catch (std::exception& e)
126 {
Joseph Reynolds5b485962018-05-17 16:05:06 -0500127 log<level::INFO>("Unable to capture metadata",
128 entry("CMD=%s", cmd.c_str()));
Brandon Wymana1e96342017-09-25 16:47:44 -0500129 }
130 }
131}
Brandon Wyman431fbe42017-08-18 16:22:09 -0500132
Brandon Wyman1db9a9e2017-07-26 18:50:22 -0500133void PowerSupply::analyze()
134{
Lei YUab093322019-10-09 16:43:22 +0800135 using namespace phosphor::pmbus;
Brandon Wyman442035f2017-08-08 15:58:45 -0500136
137 try
138 {
Brandon Wyman10295542017-08-09 18:20:44 -0500139 if (present)
Brandon Wyman442035f2017-08-08 15:58:45 -0500140 {
Brandon Wyman764c7972017-08-22 17:05:36 -0500141 std::uint16_t statusWord = 0;
Brandon Wyman764c7972017-08-22 17:05:36 -0500142
143 // Read the 2 byte STATUS_WORD value to check for faults.
144 statusWord = pmbusIntf.read(STATUS_WORD, Type::Debug);
Brandon Wymane4af9802017-11-13 15:58:33 -0600145 readFail = 0;
Brandon Wyman764c7972017-08-22 17:05:36 -0500146
Brandon Wyman603cc002017-08-28 18:17:58 -0500147 checkInputFault(statusWord);
Brandon Wyman764c7972017-08-22 17:05:36 -0500148
Brandon Wyman654eb6e2018-02-14 14:55:47 -0600149 if (powerOn && (inputFault == 0) && !faultFound)
Brandon Wyman764c7972017-08-22 17:05:36 -0500150 {
Brandon Wyman12661f12017-08-31 15:28:21 -0500151 checkFanFault(statusWord);
Brandon Wyman875b3632017-09-13 18:46:03 -0500152 checkTemperatureFault(statusWord);
Brandon Wymancfa032b2017-09-25 17:37:50 -0500153 checkOutputOvervoltageFault(statusWord);
154 checkCurrentOutOverCurrentFault(statusWord);
155 checkPGOrUnitOffFault(statusWord);
Brandon Wyman442035f2017-08-08 15:58:45 -0500156 }
Matt Spinlereb169fd2018-01-18 14:19:08 -0600157
158 updateHistory();
Brandon Wyman442035f2017-08-08 15:58:45 -0500159 }
160 }
161 catch (ReadFailure& e)
162 {
Brandon Wymane4af9802017-11-13 15:58:33 -0600163 if (readFail < FAULT_COUNT)
164 {
165 readFail++;
166 }
167
168 if (!readFailLogged && readFail >= FAULT_COUNT)
Brandon Wyman442035f2017-08-08 15:58:45 -0500169 {
170 commit<ReadFailure>();
171 readFailLogged = true;
Brandon Wyman442035f2017-08-08 15:58:45 -0500172 }
173 }
174
Brandon Wyman1db9a9e2017-07-26 18:50:22 -0500175 return;
176}
177
Brandon Wyman10295542017-08-09 18:20:44 -0500178void PowerSupply::inventoryChanged(sdbusplus::message::message& msg)
179{
180 std::string msgSensor;
Patrick Williamsabe49412020-05-13 17:59:47 -0500181 std::map<std::string, std::variant<uint32_t, bool>> msgData;
Brandon Wyman10295542017-08-09 18:20:44 -0500182 msg.read(msgSensor, msgData);
183
184 // Check if it was the Present property that changed.
185 auto valPropMap = msgData.find(PRESENT_PROP);
186 if (valPropMap != msgData.end())
187 {
Patrick Williams365d61c2020-05-13 12:23:08 -0500188 if (std::get<bool>(valPropMap->second))
Brandon Wyman10295542017-08-09 18:20:44 -0500189 {
Brandon Wyman6ccce0b2017-10-26 15:13:10 -0500190 clearFaults();
William A. Kennington III1a0c9172018-10-18 17:57:49 -0700191 presentTimer.restartOnce(presentInterval);
Brandon Wyman590fc282017-11-01 18:22:25 -0500192 }
193 else
194 {
Brandon Wyman2ef48cf2017-11-21 15:43:54 -0600195 present = false;
William A. Kennington III1a0c9172018-10-18 17:57:49 -0700196 presentTimer.setEnabled(false);
Matt Spinler234ce0d2018-01-04 15:06:57 -0600197
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500198 // Clear out the now outdated inventory properties
Matt Spinler234ce0d2018-01-04 15:06:57 -0600199 updateInventory();
Brandon Wyman10295542017-08-09 18:20:44 -0500200 }
201 }
202
203 return;
204}
205
206void PowerSupply::updatePresence()
207{
208 // Use getProperty utility function to get presence status.
Brandon Wyman10295542017-08-09 18:20:44 -0500209 std::string service = "xyz.openbmc_project.Inventory.Manager";
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500210 util::getProperty(INVENTORY_IFACE, PRESENT_PROP, inventoryPath, service,
211 bus, this->present);
Brandon Wyman10295542017-08-09 18:20:44 -0500212}
213
Brandon Wyman431fbe42017-08-18 16:22:09 -0500214void PowerSupply::powerStateChanged(sdbusplus::message::message& msg)
215{
216 int32_t state = 0;
217 std::string msgSensor;
Patrick Williamsabe49412020-05-13 17:59:47 -0500218 std::map<std::string, std::variant<int32_t>> msgData;
Brandon Wyman431fbe42017-08-18 16:22:09 -0500219 msg.read(msgSensor, msgData);
220
221 // Check if it was the Present property that changed.
222 auto valPropMap = msgData.find("state");
223 if (valPropMap != msgData.end())
224 {
Patrick Williams365d61c2020-05-13 12:23:08 -0500225 state = std::get<int32_t>(valPropMap->second);
Brandon Wyman431fbe42017-08-18 16:22:09 -0500226
227 // Power is on when state=1. Set the fault logged variables to false
228 // and start the power on timer when the state changes to 1.
229 if (state)
230 {
Brandon Wyman6ccce0b2017-10-26 15:13:10 -0500231 clearFaults();
William A. Kennington III1a0c9172018-10-18 17:57:49 -0700232 powerOnTimer.restartOnce(powerOnInterval);
Brandon Wyman431fbe42017-08-18 16:22:09 -0500233 }
234 else
235 {
William A. Kennington III1a0c9172018-10-18 17:57:49 -0700236 powerOnTimer.setEnabled(false);
Brandon Wyman431fbe42017-08-18 16:22:09 -0500237 powerOn = false;
238 }
239 }
Brandon Wyman431fbe42017-08-18 16:22:09 -0500240}
241
242void PowerSupply::updatePowerState()
243{
Lei YUcfc040c2019-10-29 17:10:26 +0800244 powerOn = util::isPoweredOn(bus);
Brandon Wyman431fbe42017-08-18 16:22:09 -0500245}
246
Brandon Wyman603cc002017-08-28 18:17:58 -0500247void PowerSupply::checkInputFault(const uint16_t statusWord)
248{
Lei YUab093322019-10-09 16:43:22 +0800249 using namespace phosphor::pmbus;
Brandon Wyman603cc002017-08-28 18:17:58 -0500250
Brandon Wymana3c675c2017-11-14 14:54:54 -0600251 if ((inputFault < FAULT_COUNT) &&
252 ((statusWord & status_word::INPUT_FAULT_WARN) ||
253 (statusWord & status_word::VIN_UV_FAULT)))
Brandon Wyman603cc002017-08-28 18:17:58 -0500254 {
Brandon Wymanad708242018-01-19 17:28:35 -0600255 if (inputFault == 0)
256 {
257 log<level::INFO>("INPUT or VIN_UV fault",
258 entry("STATUS_WORD=0x%04X", statusWord));
259 }
260
Brandon Wymana3c675c2017-11-14 14:54:54 -0600261 inputFault++;
Brandon Wyman603cc002017-08-28 18:17:58 -0500262 }
263 else
264 {
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500265 if ((inputFault > 0) && !(statusWord & status_word::INPUT_FAULT_WARN) &&
Brandon Wymand20686a2017-11-01 17:45:23 -0500266 !(statusWord & status_word::VIN_UV_FAULT))
Brandon Wyman603cc002017-08-28 18:17:58 -0500267 {
Brandon Wymana3c675c2017-11-14 14:54:54 -0600268 inputFault = 0;
Brandon Wyman3343e822017-11-03 16:54:11 -0500269 faultFound = false;
Brandon Wyman7502f6c2018-02-12 20:20:39 -0600270 // When an input fault occurs, the power supply cannot be on.
271 // However, the check for the case where the power supply should be
272 // on will stop when there is a fault found.
273 // Clear the powerOnFault when the inputFault is cleared to reset
274 // the powerOnFault de-glitching.
275 powerOnFault = 0;
Brandon Wyman69591bd2017-11-01 18:07:23 -0500276
Brandon Wyman603cc002017-08-28 18:17:58 -0500277 log<level::INFO>("INPUT_FAULT_WARN cleared",
Brandon Wymana3c675c2017-11-14 14:54:54 -0600278 entry("POWERSUPPLY=%s", inventoryPath.c_str()));
Brandon Wyman69591bd2017-11-01 18:07:23 -0500279
Brandon Wyman08b05712017-11-30 17:53:56 -0600280 resolveError(inventoryPath,
281 std::string(PowerSupplyInputFault::errName));
282
Brandon Wyman69591bd2017-11-01 18:07:23 -0500283 if (powerOn)
284 {
285 // The power supply will not be immediately powered on after
286 // the input power is restored.
287 powerOn = false;
288 // Start up the timer that will set the state to indicate we
289 // are ready for the powered on fault checks.
William A. Kennington III1a0c9172018-10-18 17:57:49 -0700290 powerOnTimer.restartOnce(powerOnInterval);
Brandon Wyman69591bd2017-11-01 18:07:23 -0500291 }
Brandon Wyman603cc002017-08-28 18:17:58 -0500292 }
293 }
Brandon Wymana3c675c2017-11-14 14:54:54 -0600294
295 if (!faultFound && (inputFault >= FAULT_COUNT))
296 {
Brandon Wymanad708242018-01-19 17:28:35 -0600297 // If the power is on, report the fault in an error log entry.
298 if (powerOn)
299 {
300 util::NamesValues nv;
301 nv.add("STATUS_WORD", statusWord);
302 captureCmd(nv, STATUS_INPUT, Type::Debug);
Brandon Wymana3c675c2017-11-14 14:54:54 -0600303
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500304 using metadata =
305 org::open_power::Witherspoon::Fault::PowerSupplyInputFault;
Brandon Wymana3c675c2017-11-14 14:54:54 -0600306
Brandon Wymanad708242018-01-19 17:28:35 -0600307 report<PowerSupplyInputFault>(
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500308 metadata::RAW_STATUS(nv.get().c_str()),
309 metadata::CALLOUT_INVENTORY_PATH(inventoryPath.c_str()));
Brandon Wymanad708242018-01-19 17:28:35 -0600310
311 faultFound = true;
312 }
Brandon Wymana3c675c2017-11-14 14:54:54 -0600313 }
Brandon Wyman603cc002017-08-28 18:17:58 -0500314}
315
316void PowerSupply::checkPGOrUnitOffFault(const uint16_t statusWord)
317{
Lei YUab093322019-10-09 16:43:22 +0800318 using namespace phosphor::pmbus;
Brandon Wyman603cc002017-08-28 18:17:58 -0500319
Brandon Wyman593d24f2017-10-13 18:15:23 -0500320 if (powerOnFault < FAULT_COUNT)
Brandon Wyman603cc002017-08-28 18:17:58 -0500321 {
Brandon Wyman593d24f2017-10-13 18:15:23 -0500322 // Check PG# and UNIT_IS_OFF
323 if ((statusWord & status_word::POWER_GOOD_NEGATED) ||
324 (statusWord & status_word::UNIT_IS_OFF))
325 {
326 log<level::INFO>("PGOOD or UNIT_IS_OFF bit bad",
327 entry("STATUS_WORD=0x%04X", statusWord));
328 powerOnFault++;
329 }
330 else
331 {
332 if (powerOnFault > 0)
333 {
334 log<level::INFO>("PGOOD and UNIT_IS_OFF bits good");
335 powerOnFault = 0;
336 }
337 }
Brandon Wyman603cc002017-08-28 18:17:58 -0500338
Brandon Wymane2fc7aa2017-11-13 17:37:10 -0600339 if (!faultFound && (powerOnFault >= FAULT_COUNT))
Brandon Wyman593d24f2017-10-13 18:15:23 -0500340 {
Brandon Wyman3343e822017-11-03 16:54:11 -0500341 faultFound = true;
342
Brandon Wyman593d24f2017-10-13 18:15:23 -0500343 util::NamesValues nv;
344 nv.add("STATUS_WORD", statusWord);
345 captureCmd(nv, STATUS_INPUT, Type::Debug);
346 auto status0Vout = pmbusIntf.insertPageNum(STATUS_VOUT, 0);
347 captureCmd(nv, status0Vout, Type::Debug);
348 captureCmd(nv, STATUS_IOUT, Type::Debug);
349 captureCmd(nv, STATUS_MFR, Type::Debug);
Brandon Wyman603cc002017-08-28 18:17:58 -0500350
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500351 using metadata =
352 org::open_power::Witherspoon::Fault::PowerSupplyShouldBeOn;
Brandon Wyman603cc002017-08-28 18:17:58 -0500353
Brandon Wyman593d24f2017-10-13 18:15:23 -0500354 // A power supply is OFF (or pgood low) but should be on.
355 report<PowerSupplyShouldBeOn>(
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500356 metadata::RAW_STATUS(nv.get().c_str()),
357 metadata::CALLOUT_INVENTORY_PATH(inventoryPath.c_str()));
Brandon Wyman593d24f2017-10-13 18:15:23 -0500358 }
Brandon Wyman603cc002017-08-28 18:17:58 -0500359 }
Brandon Wyman603cc002017-08-28 18:17:58 -0500360}
361
362void PowerSupply::checkCurrentOutOverCurrentFault(const uint16_t statusWord)
363{
Lei YUab093322019-10-09 16:43:22 +0800364 using namespace phosphor::pmbus;
Brandon Wyman603cc002017-08-28 18:17:58 -0500365
Brandon Wymandd61be42017-11-07 18:38:54 -0600366 if (outputOCFault < FAULT_COUNT)
Brandon Wyman603cc002017-08-28 18:17:58 -0500367 {
Brandon Wymandd61be42017-11-07 18:38:54 -0600368 // Check for an output overcurrent fault.
369 if ((statusWord & status_word::IOUT_OC_FAULT))
370 {
371 outputOCFault++;
372 }
373 else
374 {
375 if (outputOCFault > 0)
376 {
377 outputOCFault = 0;
378 }
379 }
Brandon Wyman603cc002017-08-28 18:17:58 -0500380
Brandon Wymane2fc7aa2017-11-13 17:37:10 -0600381 if (!faultFound && (outputOCFault >= FAULT_COUNT))
Brandon Wymandd61be42017-11-07 18:38:54 -0600382 {
383 util::NamesValues nv;
384 nv.add("STATUS_WORD", statusWord);
385 captureCmd(nv, STATUS_INPUT, Type::Debug);
386 auto status0Vout = pmbusIntf.insertPageNum(STATUS_VOUT, 0);
387 captureCmd(nv, status0Vout, Type::Debug);
388 captureCmd(nv, STATUS_IOUT, Type::Debug);
389 captureCmd(nv, STATUS_MFR, Type::Debug);
Brandon Wyman603cc002017-08-28 18:17:58 -0500390
Brandon Wymandd61be42017-11-07 18:38:54 -0600391 using metadata = org::open_power::Witherspoon::Fault::
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500392 PowerSupplyOutputOvercurrent;
Brandon Wyman603cc002017-08-28 18:17:58 -0500393
Brandon Wymandd61be42017-11-07 18:38:54 -0600394 report<PowerSupplyOutputOvercurrent>(
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500395 metadata::RAW_STATUS(nv.get().c_str()),
396 metadata::CALLOUT_INVENTORY_PATH(inventoryPath.c_str()));
Brandon Wymandd61be42017-11-07 18:38:54 -0600397
398 faultFound = true;
399 }
Brandon Wyman603cc002017-08-28 18:17:58 -0500400 }
401}
402
Brandon Wymanab05c072017-08-30 18:26:41 -0500403void PowerSupply::checkOutputOvervoltageFault(const uint16_t statusWord)
404{
Lei YUab093322019-10-09 16:43:22 +0800405 using namespace phosphor::pmbus;
Brandon Wymanab05c072017-08-30 18:26:41 -0500406
Brandon Wyman2ab319b2017-11-08 17:34:59 -0600407 if (outputOVFault < FAULT_COUNT)
Brandon Wymanab05c072017-08-30 18:26:41 -0500408 {
Brandon Wyman2ab319b2017-11-08 17:34:59 -0600409 // Check for an output overvoltage fault.
410 if (statusWord & status_word::VOUT_OV_FAULT)
411 {
412 outputOVFault++;
413 }
414 else
415 {
416 if (outputOVFault > 0)
417 {
418 outputOVFault = 0;
419 }
420 }
Brandon Wymanab05c072017-08-30 18:26:41 -0500421
Brandon Wymane2fc7aa2017-11-13 17:37:10 -0600422 if (!faultFound && (outputOVFault >= FAULT_COUNT))
Brandon Wyman2ab319b2017-11-08 17:34:59 -0600423 {
424 util::NamesValues nv;
425 nv.add("STATUS_WORD", statusWord);
426 captureCmd(nv, STATUS_INPUT, Type::Debug);
427 auto status0Vout = pmbusIntf.insertPageNum(STATUS_VOUT, 0);
428 captureCmd(nv, status0Vout, Type::Debug);
429 captureCmd(nv, STATUS_IOUT, Type::Debug);
430 captureCmd(nv, STATUS_MFR, Type::Debug);
Brandon Wymanab05c072017-08-30 18:26:41 -0500431
Brandon Wyman2ab319b2017-11-08 17:34:59 -0600432 using metadata = org::open_power::Witherspoon::Fault::
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500433 PowerSupplyOutputOvervoltage;
Brandon Wymanab05c072017-08-30 18:26:41 -0500434
Brandon Wyman2ab319b2017-11-08 17:34:59 -0600435 report<PowerSupplyOutputOvervoltage>(
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500436 metadata::RAW_STATUS(nv.get().c_str()),
437 metadata::CALLOUT_INVENTORY_PATH(inventoryPath.c_str()));
Brandon Wyman2ab319b2017-11-08 17:34:59 -0600438
439 faultFound = true;
440 }
Brandon Wymanab05c072017-08-30 18:26:41 -0500441 }
442}
443
Brandon Wyman12661f12017-08-31 15:28:21 -0500444void PowerSupply::checkFanFault(const uint16_t statusWord)
445{
Lei YUab093322019-10-09 16:43:22 +0800446 using namespace phosphor::pmbus;
Brandon Wyman12661f12017-08-31 15:28:21 -0500447
Brandon Wymanba255532017-11-08 17:44:10 -0600448 if (fanFault < FAULT_COUNT)
Brandon Wyman12661f12017-08-31 15:28:21 -0500449 {
Brandon Wymanba255532017-11-08 17:44:10 -0600450 // Check for a fan fault or warning condition
451 if (statusWord & status_word::FAN_FAULT)
452 {
453 fanFault++;
454 }
455 else
456 {
457 if (fanFault > 0)
458 {
459 fanFault = 0;
460 }
461 }
Brandon Wyman12661f12017-08-31 15:28:21 -0500462
Brandon Wymane2fc7aa2017-11-13 17:37:10 -0600463 if (!faultFound && (fanFault >= FAULT_COUNT))
Brandon Wymanba255532017-11-08 17:44:10 -0600464 {
465 util::NamesValues nv;
466 nv.add("STATUS_WORD", statusWord);
467 captureCmd(nv, STATUS_MFR, Type::Debug);
468 captureCmd(nv, STATUS_TEMPERATURE, Type::Debug);
469 captureCmd(nv, STATUS_FANS_1_2, Type::Debug);
Brandon Wyman12661f12017-08-31 15:28:21 -0500470
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500471 using metadata =
472 org::open_power::Witherspoon::Fault::PowerSupplyFanFault;
Brandon Wyman12661f12017-08-31 15:28:21 -0500473
Brandon Wymanba255532017-11-08 17:44:10 -0600474 report<PowerSupplyFanFault>(
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500475 metadata::RAW_STATUS(nv.get().c_str()),
476 metadata::CALLOUT_INVENTORY_PATH(inventoryPath.c_str()));
Brandon Wymanba255532017-11-08 17:44:10 -0600477
478 faultFound = true;
479 }
Brandon Wyman12661f12017-08-31 15:28:21 -0500480 }
481}
482
Brandon Wyman875b3632017-09-13 18:46:03 -0500483void PowerSupply::checkTemperatureFault(const uint16_t statusWord)
484{
Lei YUab093322019-10-09 16:43:22 +0800485 using namespace phosphor::pmbus;
Brandon Wyman875b3632017-09-13 18:46:03 -0500486
487 // Due to how the PMBus core device driver sends a clear faults command
488 // the bit in STATUS_WORD will likely be cleared when we attempt to examine
489 // it for a Thermal Fault or Warning. So, check the STATUS_WORD and the
490 // STATUS_TEMPERATURE bits. If either indicates a fault, proceed with
491 // logging the over-temperature condition.
492 std::uint8_t statusTemperature = 0;
493 statusTemperature = pmbusIntf.read(STATUS_TEMPERATURE, Type::Debug);
Brandon Wyman50044ea2017-11-08 17:58:56 -0600494 if (temperatureFault < FAULT_COUNT)
Brandon Wyman875b3632017-09-13 18:46:03 -0500495 {
Brandon Wyman50044ea2017-11-08 17:58:56 -0600496 if ((statusWord & status_word::TEMPERATURE_FAULT_WARN) ||
497 (statusTemperature & status_temperature::OT_FAULT))
498 {
499 temperatureFault++;
500 }
501 else
502 {
503 if (temperatureFault > 0)
504 {
505 temperatureFault = 0;
506 }
507 }
Brandon Wyman875b3632017-09-13 18:46:03 -0500508
Brandon Wymane2fc7aa2017-11-13 17:37:10 -0600509 if (!faultFound && (temperatureFault >= FAULT_COUNT))
Brandon Wyman50044ea2017-11-08 17:58:56 -0600510 {
511 // The power supply has had an over-temperature condition.
512 // This may not result in a shutdown if experienced for a short
513 // duration.
514 // This should not occur under normal conditions.
515 // The power supply may be faulty, or the paired supply may be
516 // putting out less current.
517 // Capture command responses with potentially relevant information,
518 // and call out the power supply reporting the condition.
519 util::NamesValues nv;
520 nv.add("STATUS_WORD", statusWord);
521 captureCmd(nv, STATUS_MFR, Type::Debug);
522 captureCmd(nv, STATUS_IOUT, Type::Debug);
523 nv.add("STATUS_TEMPERATURE", statusTemperature);
524 captureCmd(nv, STATUS_FANS_1_2, Type::Debug);
Brandon Wyman875b3632017-09-13 18:46:03 -0500525
Brandon Wyman50044ea2017-11-08 17:58:56 -0600526 using metadata = org::open_power::Witherspoon::Fault::
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500527 PowerSupplyTemperatureFault;
Brandon Wyman875b3632017-09-13 18:46:03 -0500528
Brandon Wyman50044ea2017-11-08 17:58:56 -0600529 report<PowerSupplyTemperatureFault>(
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500530 metadata::RAW_STATUS(nv.get().c_str()),
531 metadata::CALLOUT_INVENTORY_PATH(inventoryPath.c_str()));
Brandon Wyman50044ea2017-11-08 17:58:56 -0600532
533 faultFound = true;
534 }
Brandon Wyman875b3632017-09-13 18:46:03 -0500535 }
536}
537
Brandon Wyman1db9a9e2017-07-26 18:50:22 -0500538void PowerSupply::clearFaults()
539{
Brandon Wymane4af9802017-11-13 15:58:33 -0600540 readFail = 0;
Brandon Wyman6ccce0b2017-10-26 15:13:10 -0500541 readFailLogged = false;
Brandon Wymana3c675c2017-11-14 14:54:54 -0600542 inputFault = 0;
Brandon Wyman6ccce0b2017-10-26 15:13:10 -0500543 powerOnFault = 0;
Brandon Wymandd61be42017-11-07 18:38:54 -0600544 outputOCFault = 0;
Brandon Wyman2ab319b2017-11-08 17:34:59 -0600545 outputOVFault = 0;
Brandon Wymanba255532017-11-08 17:44:10 -0600546 fanFault = 0;
Brandon Wyman50044ea2017-11-08 17:58:56 -0600547 temperatureFault = 0;
Brandon Wyman3343e822017-11-03 16:54:11 -0500548 faultFound = false;
Brandon Wyman6ccce0b2017-10-26 15:13:10 -0500549
Brandon Wyman1db9a9e2017-07-26 18:50:22 -0500550 return;
551}
552
Brandon Wyman43ce2082017-11-30 17:24:01 -0600553void PowerSupply::resolveError(const std::string& callout,
554 const std::string& message)
555{
Brandon Wyman01741f12017-12-01 17:22:08 -0600556 using EndpointList = std::vector<std::string>;
557
558 try
559 {
560 auto path = callout + "/fault";
561 // Get the service name from the mapper for the fault callout
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500562 auto service = util::getService(path, ASSOCIATION_IFACE, bus);
Brandon Wyman01741f12017-12-01 17:22:08 -0600563
564 // Use getProperty utility function to get log entries (endpoints)
565 EndpointList logEntries;
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500566 util::getProperty(ASSOCIATION_IFACE, ENDPOINTS_PROP, path, service, bus,
567 logEntries);
Brandon Wyman01741f12017-12-01 17:22:08 -0600568
569 // It is possible that all such entries for this callout have since
570 // been deleted.
571 if (logEntries.empty())
572 {
573 return;
574 }
575
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500576 auto logEntryService =
577 util::getService(logEntries[0], LOGGING_IFACE, bus);
Brandon Wyman01741f12017-12-01 17:22:08 -0600578 if (logEntryService.empty())
579 {
580 return;
581 }
582
583 // go through each log entry that matches this callout path
584 std::string logMessage;
585 for (const auto& logEntry : logEntries)
586 {
587 // Check to see if this logEntry has a message that matches.
Matt Spinler589e8722018-01-04 15:24:49 -0600588 util::getProperty(LOGGING_IFACE, MESSAGE_PROP, logEntry,
Brandon Wyman01741f12017-12-01 17:22:08 -0600589 logEntryService, bus, logMessage);
590
591 if (message == logMessage)
592 {
593 // Log entry matches call out and message, set Resolved to true
594 bool resolved = true;
Matt Spinler589e8722018-01-04 15:24:49 -0600595 util::setProperty(LOGGING_IFACE, RESOLVED_PROP, logEntry,
Brandon Wyman01741f12017-12-01 17:22:08 -0600596 logEntryService, bus, resolved);
597 }
Brandon Wyman01741f12017-12-01 17:22:08 -0600598 }
Brandon Wyman01741f12017-12-01 17:22:08 -0600599 }
600 catch (std::exception& e)
601 {
602 log<level::INFO>("Failed to resolve error",
603 entry("CALLOUT=%s", callout.c_str()),
Matt Spinler0d09f292018-01-22 14:51:26 -0600604 entry("ERROR=%s", message.c_str()));
Brandon Wyman01741f12017-12-01 17:22:08 -0600605 }
Brandon Wyman43ce2082017-11-30 17:24:01 -0600606}
607
Matt Spinler234ce0d2018-01-04 15:06:57 -0600608void PowerSupply::updateInventory()
609{
Lei YUab093322019-10-09 16:43:22 +0800610 using namespace phosphor::pmbus;
Matt Spinler018a7bc2018-01-04 15:36:41 -0600611 using namespace sdbusplus::message;
612
Matt Spinler018a7bc2018-01-04 15:36:41 -0600613 // Build the object map and send it to the inventory
Shawn McCarney6687f122020-06-02 16:49:24 -0500614 using Properties = std::map<std::string, std::variant<std::string>>;
Matt Spinler018a7bc2018-01-04 15:36:41 -0600615 using Interfaces = std::map<std::string, Properties>;
616 using Object = std::map<object_path, Interfaces>;
617 Properties assetProps;
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 }
639 catch (ReadFailure& e)
640 {
641 }
642 }
George Liu690e7802019-08-23 11:04:01 +0800643 }
Matt Spinler018a7bc2018-01-04 15:36:41 -0600644
George Liu690e7802019-08-23 11:04:01 +0800645 interfaces.emplace(ASSET_IFACE, std::move(assetProps));
Matt Spinler018a7bc2018-01-04 15:36:41 -0600646
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500647 // For Notify(), just send the relative path of the inventory
648 // object so remove the INVENTORY_OBJ_PATH prefix
Matt Spinler018a7bc2018-01-04 15:36:41 -0600649 auto path = inventoryPath.substr(strlen(INVENTORY_OBJ_PATH));
650
651 object.emplace(path, std::move(interfaces));
652
653 try
654 {
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500655 auto service =
656 util::getService(INVENTORY_OBJ_PATH, INVENTORY_MGR_IFACE, bus);
Matt Spinler018a7bc2018-01-04 15:36:41 -0600657
658 if (service.empty())
659 {
660 log<level::ERR>("Unable to get inventory manager service");
661 return;
662 }
663
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500664 auto method = bus.new_method_call(service.c_str(), INVENTORY_OBJ_PATH,
665 INVENTORY_MGR_IFACE, "Notify");
Matt Spinler018a7bc2018-01-04 15:36:41 -0600666
667 method.append(std::move(object));
668
669 auto reply = bus.call(method);
Matt Spinler018a7bc2018-01-04 15:36:41 -0600670 }
671 catch (std::exception& e)
672 {
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500673 log<level::ERR>(e.what(), entry("PATH=%s", inventoryPath.c_str()));
Matt Spinler018a7bc2018-01-04 15:36:41 -0600674 }
Matt Spinler234ce0d2018-01-04 15:06:57 -0600675}
676
Matt Spinlerd734e652018-01-18 14:31:15 -0600677void PowerSupply::syncHistory()
678{
Lei YUab093322019-10-09 16:43:22 +0800679 using namespace phosphor::gpio;
Matt Spinlerd734e652018-01-18 14:31:15 -0600680
681 if (syncGPIODevPath.empty())
682 {
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500683 // Sync not implemented
Matt Spinlerd734e652018-01-18 14:31:15 -0600684 return;
685 }
686
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500687 GPIO gpio{syncGPIODevPath, static_cast<gpioNum_t>(syncGPIONumber),
Matt Spinlerd734e652018-01-18 14:31:15 -0600688 Direction::output};
689
690 try
691 {
692 gpio.set(Value::low);
693
694 std::this_thread::sleep_for(std::chrono::milliseconds{5});
695
696 gpio.set(Value::high);
697
698 recordManager->clear();
699 }
700 catch (std::exception& e)
701 {
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500702 // Do nothing. There would already be a journal entry.
Matt Spinlerd734e652018-01-18 14:31:15 -0600703 }
704}
705
Matt Spinler82384142018-01-18 14:15:03 -0600706void PowerSupply::enableHistory(const std::string& objectPath,
707 size_t numRecords,
708 const std::string& syncGPIOPath,
709 size_t syncGPIONum)
710{
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
734 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 {
742 average->values(std::move(recordManager->getAverageRecords()));
743 maximum->values(std::move(recordManager->getMaximumRecords()));
744 }
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