blob: 62067ef86030478cf8f6ceb3252c63e4a981faa4 [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"
24#include "utility.hpp"
25
Matt Spinlerf0f02b92018-10-25 16:12:43 -050026#include <functional>
27#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>
30#include <xyz/openbmc_project/Software/Version/server.hpp>
31
Lei YUab093322019-10-09 16:43:22 +080032namespace phosphor
Brandon Wyman24e422f2017-07-25 19:40:14 -050033{
34namespace power
35{
36namespace psu
37{
38
Matt Spinler589e8722018-01-04 15:24:49 -060039using namespace phosphor::logging;
40using namespace sdbusplus::org::open_power::Witherspoon::Fault::Error;
41using namespace sdbusplus::xyz::openbmc_project::Common::Device::Error;
Matt Spinler018a7bc2018-01-04 15:36:41 -060042namespace version = sdbusplus::xyz::openbmc_project::Software::server;
Matt Spinler589e8722018-01-04 15:24:49 -060043
Matt Spinler5b2964f2019-07-24 10:34:31 -050044constexpr auto ASSOCIATION_IFACE = "xyz.openbmc_project.Association";
Matt Spinler589e8722018-01-04 15:24:49 -060045constexpr auto LOGGING_IFACE = "xyz.openbmc_project.Logging.Entry";
46constexpr auto INVENTORY_IFACE = "xyz.openbmc_project.Inventory.Item";
47constexpr auto POWER_IFACE = "org.openbmc.control.Power";
Matt Spinler018a7bc2018-01-04 15:36:41 -060048constexpr auto INVENTORY_MGR_IFACE = "xyz.openbmc_project.Inventory.Manager";
49constexpr auto ASSET_IFACE = "xyz.openbmc_project.Inventory.Decorator.Asset";
50constexpr auto VERSION_IFACE = "xyz.openbmc_project.Software.Version";
Matt Spinler589e8722018-01-04 15:24:49 -060051
52constexpr auto ENDPOINTS_PROP = "endpoints";
53constexpr auto MESSAGE_PROP = "Message";
54constexpr auto RESOLVED_PROP = "Resolved";
Brandon Wyman10295542017-08-09 18:20:44 -050055constexpr auto PRESENT_PROP = "Present";
Matt Spinler018a7bc2018-01-04 15:36:41 -060056constexpr auto VERSION_PURPOSE_PROP = "Purpose";
Matt Spinler589e8722018-01-04 15:24:49 -060057
58constexpr auto INVENTORY_OBJ_PATH = "/xyz/openbmc_project/inventory";
Brandon Wyman431fbe42017-08-18 16:22:09 -050059constexpr auto POWER_OBJ_PATH = "/org/openbmc/control/power0";
Brandon Wyman10295542017-08-09 18:20:44 -050060
Matt Spinlereb169fd2018-01-18 14:19:08 -060061constexpr auto INPUT_HISTORY = "input_history";
Matt Spinler018a7bc2018-01-04 15:36:41 -060062
Brandon Wyman10295542017-08-09 18:20:44 -050063PowerSupply::PowerSupply(const std::string& name, size_t inst,
Matt Spinlerf0f02b92018-10-25 16:12:43 -050064 const std::string& objpath, const std::string& invpath,
65 sdbusplus::bus::bus& bus, const sdeventplus::Event& e,
66 std::chrono::seconds& t, std::chrono::seconds& p) :
67 Device(name, inst),
68 monitorPath(objpath), pmbusIntf(objpath),
69 inventoryPath(INVENTORY_OBJ_PATH + invpath), bus(bus), presentInterval(p),
70 presentTimer(e, std::bind([this]() {
71 // The hwmon path may have changed.
72 pmbusIntf.findHwmonDir();
73 this->present = true;
Matt Spinler234ce0d2018-01-04 15:06:57 -060074
Matt Spinlerf0f02b92018-10-25 16:12:43 -050075 // Sync the INPUT_HISTORY data for all PSs
76 syncHistory();
Matt Spinlerd734e652018-01-18 14:31:15 -060077
Matt Spinlerf0f02b92018-10-25 16:12:43 -050078 // Update the inventory for the new device
79 updateInventory();
80 })),
81 powerOnInterval(t),
82 powerOnTimer(e, std::bind([this]() { this->powerOn = true; }))
Brandon Wyman10295542017-08-09 18:20:44 -050083{
George Liu690e7802019-08-23 11:04:01 +080084 getAccessType();
85
Brandon Wyman10295542017-08-09 18:20:44 -050086 using namespace sdbusplus::bus;
Lei YUab093322019-10-09 16:43:22 +080087 using namespace phosphor::pmbus;
Aatir Manzur817f8a72019-08-07 07:42:15 -050088 std::uint16_t statusWord = 0;
89 try
90 {
91 // Read the 2 byte STATUS_WORD value to check for faults.
92 statusWord = pmbusIntf.read(STATUS_WORD, Type::Debug);
93 if (!((statusWord & status_word::INPUT_FAULT_WARN) ||
94 (statusWord & status_word::VIN_UV_FAULT)))
95 {
96 resolveError(inventoryPath,
97 std::string(PowerSupplyInputFault::errName));
98 }
99 }
100 catch (ReadFailure& e)
101 {
102 log<level::INFO>("Unable to read the 2 byte STATUS_WORD value to check "
103 "for power-supply input faults.");
104 }
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500105 presentMatch = std::make_unique<match_t>(
106 bus, match::rules::propertiesChanged(inventoryPath, INVENTORY_IFACE),
107 [this](auto& msg) { this->inventoryChanged(msg); });
Brandon Wyman431fbe42017-08-18 16:22:09 -0500108 // Get initial presence state.
Brandon Wyman253dc9b2017-08-12 13:45:52 -0500109 updatePresence();
Brandon Wyman431fbe42017-08-18 16:22:09 -0500110
Matt Spinler234ce0d2018-01-04 15:06:57 -0600111 // Write the SN, PN, etc to the inventory
112 updateInventory();
113
Brandon Wyman431fbe42017-08-18 16:22:09 -0500114 // Subscribe to power state changes
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500115 powerOnMatch = std::make_unique<match_t>(
116 bus, match::rules::propertiesChanged(POWER_OBJ_PATH, POWER_IFACE),
117 [this](auto& msg) { this->powerStateChanged(msg); });
Brandon Wyman431fbe42017-08-18 16:22:09 -0500118 // Get initial power state.
119 updatePowerState();
Brandon Wyman10295542017-08-09 18:20:44 -0500120}
Brandon Wyman442035f2017-08-08 15:58:45 -0500121
George Liu690e7802019-08-23 11:04:01 +0800122void PowerSupply::getAccessType()
123{
Lei YUab093322019-10-09 16:43:22 +0800124 using namespace phosphor::power::util;
George Liu690e7802019-08-23 11:04:01 +0800125 fruJson = loadJSONFromFile(PSU_JSON_PATH);
126 if (fruJson == nullptr)
127 {
128 log<level::ERR>("InternalFailure when parsing the JSON file");
129 return;
130 }
Lei YU40705462019-10-09 17:07:11 +0800131 inventoryPMBusAccessType = getPMBusAccessType(fruJson);
George Liu690e7802019-08-23 11:04:01 +0800132}
133
Brandon Wymana1e96342017-09-25 16:47:44 -0500134void PowerSupply::captureCmd(util::NamesValues& nv, const std::string& cmd,
Lei YUab093322019-10-09 16:43:22 +0800135 phosphor::pmbus::Type type)
Brandon Wymana1e96342017-09-25 16:47:44 -0500136{
137 if (pmbusIntf.exists(cmd, type))
138 {
139 try
140 {
141 auto val = pmbusIntf.read(cmd, type);
142 nv.add(cmd, val);
143 }
144 catch (std::exception& e)
145 {
Joseph Reynolds5b485962018-05-17 16:05:06 -0500146 log<level::INFO>("Unable to capture metadata",
147 entry("CMD=%s", cmd.c_str()));
Brandon Wymana1e96342017-09-25 16:47:44 -0500148 }
149 }
150}
Brandon Wyman431fbe42017-08-18 16:22:09 -0500151
Brandon Wyman1db9a9e2017-07-26 18:50:22 -0500152void PowerSupply::analyze()
153{
Lei YUab093322019-10-09 16:43:22 +0800154 using namespace phosphor::pmbus;
Brandon Wyman442035f2017-08-08 15:58:45 -0500155
156 try
157 {
Brandon Wyman10295542017-08-09 18:20:44 -0500158 if (present)
Brandon Wyman442035f2017-08-08 15:58:45 -0500159 {
Brandon Wyman764c7972017-08-22 17:05:36 -0500160 std::uint16_t statusWord = 0;
Brandon Wyman764c7972017-08-22 17:05:36 -0500161
162 // Read the 2 byte STATUS_WORD value to check for faults.
163 statusWord = pmbusIntf.read(STATUS_WORD, Type::Debug);
Brandon Wymane4af9802017-11-13 15:58:33 -0600164 readFail = 0;
Brandon Wyman764c7972017-08-22 17:05:36 -0500165
Brandon Wyman603cc002017-08-28 18:17:58 -0500166 checkInputFault(statusWord);
Brandon Wyman764c7972017-08-22 17:05:36 -0500167
Brandon Wyman654eb6e2018-02-14 14:55:47 -0600168 if (powerOn && (inputFault == 0) && !faultFound)
Brandon Wyman764c7972017-08-22 17:05:36 -0500169 {
Brandon Wyman12661f12017-08-31 15:28:21 -0500170 checkFanFault(statusWord);
Brandon Wyman875b3632017-09-13 18:46:03 -0500171 checkTemperatureFault(statusWord);
Brandon Wymancfa032b2017-09-25 17:37:50 -0500172 checkOutputOvervoltageFault(statusWord);
173 checkCurrentOutOverCurrentFault(statusWord);
174 checkPGOrUnitOffFault(statusWord);
Brandon Wyman442035f2017-08-08 15:58:45 -0500175 }
Matt Spinlereb169fd2018-01-18 14:19:08 -0600176
177 updateHistory();
Brandon Wyman442035f2017-08-08 15:58:45 -0500178 }
179 }
180 catch (ReadFailure& e)
181 {
Brandon Wymane4af9802017-11-13 15:58:33 -0600182 if (readFail < FAULT_COUNT)
183 {
184 readFail++;
185 }
186
187 if (!readFailLogged && readFail >= FAULT_COUNT)
Brandon Wyman442035f2017-08-08 15:58:45 -0500188 {
189 commit<ReadFailure>();
190 readFailLogged = true;
Brandon Wyman442035f2017-08-08 15:58:45 -0500191 }
192 }
193
Brandon Wyman1db9a9e2017-07-26 18:50:22 -0500194 return;
195}
196
Brandon Wyman10295542017-08-09 18:20:44 -0500197void PowerSupply::inventoryChanged(sdbusplus::message::message& msg)
198{
199 std::string msgSensor;
200 std::map<std::string, sdbusplus::message::variant<uint32_t, bool>> msgData;
201 msg.read(msgSensor, msgData);
202
203 // Check if it was the Present property that changed.
204 auto valPropMap = msgData.find(PRESENT_PROP);
205 if (valPropMap != msgData.end())
206 {
Brandon Wyman2ef48cf2017-11-21 15:43:54 -0600207 if (sdbusplus::message::variant_ns::get<bool>(valPropMap->second))
Brandon Wyman10295542017-08-09 18:20:44 -0500208 {
Brandon Wyman6ccce0b2017-10-26 15:13:10 -0500209 clearFaults();
William A. Kennington III1a0c9172018-10-18 17:57:49 -0700210 presentTimer.restartOnce(presentInterval);
Brandon Wyman590fc282017-11-01 18:22:25 -0500211 }
212 else
213 {
Brandon Wyman2ef48cf2017-11-21 15:43:54 -0600214 present = false;
William A. Kennington III1a0c9172018-10-18 17:57:49 -0700215 presentTimer.setEnabled(false);
Matt Spinler234ce0d2018-01-04 15:06:57 -0600216
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500217 // Clear out the now outdated inventory properties
Matt Spinler234ce0d2018-01-04 15:06:57 -0600218 updateInventory();
Brandon Wyman10295542017-08-09 18:20:44 -0500219 }
220 }
221
222 return;
223}
224
225void PowerSupply::updatePresence()
226{
227 // Use getProperty utility function to get presence status.
Brandon Wyman10295542017-08-09 18:20:44 -0500228 std::string service = "xyz.openbmc_project.Inventory.Manager";
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500229 util::getProperty(INVENTORY_IFACE, PRESENT_PROP, inventoryPath, service,
230 bus, this->present);
Brandon Wyman10295542017-08-09 18:20:44 -0500231}
232
Brandon Wyman431fbe42017-08-18 16:22:09 -0500233void PowerSupply::powerStateChanged(sdbusplus::message::message& msg)
234{
235 int32_t state = 0;
236 std::string msgSensor;
William A. Kennington IIIac9d5c32018-11-12 14:58:39 -0800237 std::map<std::string, sdbusplus::message::variant<int32_t>> msgData;
Brandon Wyman431fbe42017-08-18 16:22:09 -0500238 msg.read(msgSensor, msgData);
239
240 // Check if it was the Present property that changed.
241 auto valPropMap = msgData.find("state");
242 if (valPropMap != msgData.end())
243 {
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500244 state =
245 sdbusplus::message::variant_ns::get<int32_t>(valPropMap->second);
Brandon Wyman431fbe42017-08-18 16:22:09 -0500246
247 // Power is on when state=1. Set the fault logged variables to false
248 // and start the power on timer when the state changes to 1.
249 if (state)
250 {
Brandon Wyman6ccce0b2017-10-26 15:13:10 -0500251 clearFaults();
William A. Kennington III1a0c9172018-10-18 17:57:49 -0700252 powerOnTimer.restartOnce(powerOnInterval);
Brandon Wyman431fbe42017-08-18 16:22:09 -0500253 }
254 else
255 {
William A. Kennington III1a0c9172018-10-18 17:57:49 -0700256 powerOnTimer.setEnabled(false);
Brandon Wyman431fbe42017-08-18 16:22:09 -0500257 powerOn = false;
258 }
259 }
Brandon Wyman431fbe42017-08-18 16:22:09 -0500260}
261
262void PowerSupply::updatePowerState()
263{
264 // When state = 1, system is powered on
265 int32_t state = 0;
266
267 try
268 {
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500269 auto service = util::getService(POWER_OBJ_PATH, POWER_IFACE, bus);
Brandon Wyman431fbe42017-08-18 16:22:09 -0500270
271 // Use getProperty utility function to get power state.
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500272 util::getProperty<int32_t>(POWER_IFACE, "state", POWER_OBJ_PATH,
273 service, bus, state);
Brandon Wyman431fbe42017-08-18 16:22:09 -0500274
275 if (state)
276 {
277 powerOn = true;
278 }
279 else
280 {
281 powerOn = false;
282 }
283 }
284 catch (std::exception& e)
285 {
286 log<level::INFO>("Failed to get power state. Assuming it is off.");
287 powerOn = false;
288 }
Brandon Wyman431fbe42017-08-18 16:22:09 -0500289}
290
Brandon Wyman603cc002017-08-28 18:17:58 -0500291void PowerSupply::checkInputFault(const uint16_t statusWord)
292{
Lei YUab093322019-10-09 16:43:22 +0800293 using namespace phosphor::pmbus;
Brandon Wyman603cc002017-08-28 18:17:58 -0500294
Brandon Wymana3c675c2017-11-14 14:54:54 -0600295 if ((inputFault < FAULT_COUNT) &&
296 ((statusWord & status_word::INPUT_FAULT_WARN) ||
297 (statusWord & status_word::VIN_UV_FAULT)))
Brandon Wyman603cc002017-08-28 18:17:58 -0500298 {
Brandon Wymanad708242018-01-19 17:28:35 -0600299 if (inputFault == 0)
300 {
301 log<level::INFO>("INPUT or VIN_UV fault",
302 entry("STATUS_WORD=0x%04X", statusWord));
303 }
304
Brandon Wymana3c675c2017-11-14 14:54:54 -0600305 inputFault++;
Brandon Wyman603cc002017-08-28 18:17:58 -0500306 }
307 else
308 {
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500309 if ((inputFault > 0) && !(statusWord & status_word::INPUT_FAULT_WARN) &&
Brandon Wymand20686a2017-11-01 17:45:23 -0500310 !(statusWord & status_word::VIN_UV_FAULT))
Brandon Wyman603cc002017-08-28 18:17:58 -0500311 {
Brandon Wymana3c675c2017-11-14 14:54:54 -0600312 inputFault = 0;
Brandon Wyman3343e822017-11-03 16:54:11 -0500313 faultFound = false;
Brandon Wyman7502f6c2018-02-12 20:20:39 -0600314 // When an input fault occurs, the power supply cannot be on.
315 // However, the check for the case where the power supply should be
316 // on will stop when there is a fault found.
317 // Clear the powerOnFault when the inputFault is cleared to reset
318 // the powerOnFault de-glitching.
319 powerOnFault = 0;
Brandon Wyman69591bd2017-11-01 18:07:23 -0500320
Brandon Wyman603cc002017-08-28 18:17:58 -0500321 log<level::INFO>("INPUT_FAULT_WARN cleared",
Brandon Wymana3c675c2017-11-14 14:54:54 -0600322 entry("POWERSUPPLY=%s", inventoryPath.c_str()));
Brandon Wyman69591bd2017-11-01 18:07:23 -0500323
Brandon Wyman08b05712017-11-30 17:53:56 -0600324 resolveError(inventoryPath,
325 std::string(PowerSupplyInputFault::errName));
326
Brandon Wyman69591bd2017-11-01 18:07:23 -0500327 if (powerOn)
328 {
329 // The power supply will not be immediately powered on after
330 // the input power is restored.
331 powerOn = false;
332 // Start up the timer that will set the state to indicate we
333 // are ready for the powered on fault checks.
William A. Kennington III1a0c9172018-10-18 17:57:49 -0700334 powerOnTimer.restartOnce(powerOnInterval);
Brandon Wyman69591bd2017-11-01 18:07:23 -0500335 }
Brandon Wyman603cc002017-08-28 18:17:58 -0500336 }
337 }
Brandon Wymana3c675c2017-11-14 14:54:54 -0600338
339 if (!faultFound && (inputFault >= FAULT_COUNT))
340 {
Brandon Wymanad708242018-01-19 17:28:35 -0600341 // If the power is on, report the fault in an error log entry.
342 if (powerOn)
343 {
344 util::NamesValues nv;
345 nv.add("STATUS_WORD", statusWord);
346 captureCmd(nv, STATUS_INPUT, Type::Debug);
Brandon Wymana3c675c2017-11-14 14:54:54 -0600347
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500348 using metadata =
349 org::open_power::Witherspoon::Fault::PowerSupplyInputFault;
Brandon Wymana3c675c2017-11-14 14:54:54 -0600350
Brandon Wymanad708242018-01-19 17:28:35 -0600351 report<PowerSupplyInputFault>(
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500352 metadata::RAW_STATUS(nv.get().c_str()),
353 metadata::CALLOUT_INVENTORY_PATH(inventoryPath.c_str()));
Brandon Wymanad708242018-01-19 17:28:35 -0600354
355 faultFound = true;
356 }
Brandon Wymana3c675c2017-11-14 14:54:54 -0600357 }
Brandon Wyman603cc002017-08-28 18:17:58 -0500358}
359
360void PowerSupply::checkPGOrUnitOffFault(const uint16_t statusWord)
361{
Lei YUab093322019-10-09 16:43:22 +0800362 using namespace phosphor::pmbus;
Brandon Wyman603cc002017-08-28 18:17:58 -0500363
Brandon Wyman593d24f2017-10-13 18:15:23 -0500364 if (powerOnFault < FAULT_COUNT)
Brandon Wyman603cc002017-08-28 18:17:58 -0500365 {
Brandon Wyman593d24f2017-10-13 18:15:23 -0500366 // Check PG# and UNIT_IS_OFF
367 if ((statusWord & status_word::POWER_GOOD_NEGATED) ||
368 (statusWord & status_word::UNIT_IS_OFF))
369 {
370 log<level::INFO>("PGOOD or UNIT_IS_OFF bit bad",
371 entry("STATUS_WORD=0x%04X", statusWord));
372 powerOnFault++;
373 }
374 else
375 {
376 if (powerOnFault > 0)
377 {
378 log<level::INFO>("PGOOD and UNIT_IS_OFF bits good");
379 powerOnFault = 0;
380 }
381 }
Brandon Wyman603cc002017-08-28 18:17:58 -0500382
Brandon Wymane2fc7aa2017-11-13 17:37:10 -0600383 if (!faultFound && (powerOnFault >= FAULT_COUNT))
Brandon Wyman593d24f2017-10-13 18:15:23 -0500384 {
Brandon Wyman3343e822017-11-03 16:54:11 -0500385 faultFound = true;
386
Brandon Wyman593d24f2017-10-13 18:15:23 -0500387 util::NamesValues nv;
388 nv.add("STATUS_WORD", statusWord);
389 captureCmd(nv, STATUS_INPUT, Type::Debug);
390 auto status0Vout = pmbusIntf.insertPageNum(STATUS_VOUT, 0);
391 captureCmd(nv, status0Vout, Type::Debug);
392 captureCmd(nv, STATUS_IOUT, Type::Debug);
393 captureCmd(nv, STATUS_MFR, Type::Debug);
Brandon Wyman603cc002017-08-28 18:17:58 -0500394
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500395 using metadata =
396 org::open_power::Witherspoon::Fault::PowerSupplyShouldBeOn;
Brandon Wyman603cc002017-08-28 18:17:58 -0500397
Brandon Wyman593d24f2017-10-13 18:15:23 -0500398 // A power supply is OFF (or pgood low) but should be on.
399 report<PowerSupplyShouldBeOn>(
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500400 metadata::RAW_STATUS(nv.get().c_str()),
401 metadata::CALLOUT_INVENTORY_PATH(inventoryPath.c_str()));
Brandon Wyman593d24f2017-10-13 18:15:23 -0500402 }
Brandon Wyman603cc002017-08-28 18:17:58 -0500403 }
Brandon Wyman603cc002017-08-28 18:17:58 -0500404}
405
406void PowerSupply::checkCurrentOutOverCurrentFault(const uint16_t statusWord)
407{
Lei YUab093322019-10-09 16:43:22 +0800408 using namespace phosphor::pmbus;
Brandon Wyman603cc002017-08-28 18:17:58 -0500409
Brandon Wymandd61be42017-11-07 18:38:54 -0600410 if (outputOCFault < FAULT_COUNT)
Brandon Wyman603cc002017-08-28 18:17:58 -0500411 {
Brandon Wymandd61be42017-11-07 18:38:54 -0600412 // Check for an output overcurrent fault.
413 if ((statusWord & status_word::IOUT_OC_FAULT))
414 {
415 outputOCFault++;
416 }
417 else
418 {
419 if (outputOCFault > 0)
420 {
421 outputOCFault = 0;
422 }
423 }
Brandon Wyman603cc002017-08-28 18:17:58 -0500424
Brandon Wymane2fc7aa2017-11-13 17:37:10 -0600425 if (!faultFound && (outputOCFault >= FAULT_COUNT))
Brandon Wymandd61be42017-11-07 18:38:54 -0600426 {
427 util::NamesValues nv;
428 nv.add("STATUS_WORD", statusWord);
429 captureCmd(nv, STATUS_INPUT, Type::Debug);
430 auto status0Vout = pmbusIntf.insertPageNum(STATUS_VOUT, 0);
431 captureCmd(nv, status0Vout, Type::Debug);
432 captureCmd(nv, STATUS_IOUT, Type::Debug);
433 captureCmd(nv, STATUS_MFR, Type::Debug);
Brandon Wyman603cc002017-08-28 18:17:58 -0500434
Brandon Wymandd61be42017-11-07 18:38:54 -0600435 using metadata = org::open_power::Witherspoon::Fault::
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500436 PowerSupplyOutputOvercurrent;
Brandon Wyman603cc002017-08-28 18:17:58 -0500437
Brandon Wymandd61be42017-11-07 18:38:54 -0600438 report<PowerSupplyOutputOvercurrent>(
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500439 metadata::RAW_STATUS(nv.get().c_str()),
440 metadata::CALLOUT_INVENTORY_PATH(inventoryPath.c_str()));
Brandon Wymandd61be42017-11-07 18:38:54 -0600441
442 faultFound = true;
443 }
Brandon Wyman603cc002017-08-28 18:17:58 -0500444 }
445}
446
Brandon Wymanab05c072017-08-30 18:26:41 -0500447void PowerSupply::checkOutputOvervoltageFault(const uint16_t statusWord)
448{
Lei YUab093322019-10-09 16:43:22 +0800449 using namespace phosphor::pmbus;
Brandon Wymanab05c072017-08-30 18:26:41 -0500450
Brandon Wyman2ab319b2017-11-08 17:34:59 -0600451 if (outputOVFault < FAULT_COUNT)
Brandon Wymanab05c072017-08-30 18:26:41 -0500452 {
Brandon Wyman2ab319b2017-11-08 17:34:59 -0600453 // Check for an output overvoltage fault.
454 if (statusWord & status_word::VOUT_OV_FAULT)
455 {
456 outputOVFault++;
457 }
458 else
459 {
460 if (outputOVFault > 0)
461 {
462 outputOVFault = 0;
463 }
464 }
Brandon Wymanab05c072017-08-30 18:26:41 -0500465
Brandon Wymane2fc7aa2017-11-13 17:37:10 -0600466 if (!faultFound && (outputOVFault >= FAULT_COUNT))
Brandon Wyman2ab319b2017-11-08 17:34:59 -0600467 {
468 util::NamesValues nv;
469 nv.add("STATUS_WORD", statusWord);
470 captureCmd(nv, STATUS_INPUT, Type::Debug);
471 auto status0Vout = pmbusIntf.insertPageNum(STATUS_VOUT, 0);
472 captureCmd(nv, status0Vout, Type::Debug);
473 captureCmd(nv, STATUS_IOUT, Type::Debug);
474 captureCmd(nv, STATUS_MFR, Type::Debug);
Brandon Wymanab05c072017-08-30 18:26:41 -0500475
Brandon Wyman2ab319b2017-11-08 17:34:59 -0600476 using metadata = org::open_power::Witherspoon::Fault::
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500477 PowerSupplyOutputOvervoltage;
Brandon Wymanab05c072017-08-30 18:26:41 -0500478
Brandon Wyman2ab319b2017-11-08 17:34:59 -0600479 report<PowerSupplyOutputOvervoltage>(
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500480 metadata::RAW_STATUS(nv.get().c_str()),
481 metadata::CALLOUT_INVENTORY_PATH(inventoryPath.c_str()));
Brandon Wyman2ab319b2017-11-08 17:34:59 -0600482
483 faultFound = true;
484 }
Brandon Wymanab05c072017-08-30 18:26:41 -0500485 }
486}
487
Brandon Wyman12661f12017-08-31 15:28:21 -0500488void PowerSupply::checkFanFault(const uint16_t statusWord)
489{
Lei YUab093322019-10-09 16:43:22 +0800490 using namespace phosphor::pmbus;
Brandon Wyman12661f12017-08-31 15:28:21 -0500491
Brandon Wymanba255532017-11-08 17:44:10 -0600492 if (fanFault < FAULT_COUNT)
Brandon Wyman12661f12017-08-31 15:28:21 -0500493 {
Brandon Wymanba255532017-11-08 17:44:10 -0600494 // Check for a fan fault or warning condition
495 if (statusWord & status_word::FAN_FAULT)
496 {
497 fanFault++;
498 }
499 else
500 {
501 if (fanFault > 0)
502 {
503 fanFault = 0;
504 }
505 }
Brandon Wyman12661f12017-08-31 15:28:21 -0500506
Brandon Wymane2fc7aa2017-11-13 17:37:10 -0600507 if (!faultFound && (fanFault >= FAULT_COUNT))
Brandon Wymanba255532017-11-08 17:44:10 -0600508 {
509 util::NamesValues nv;
510 nv.add("STATUS_WORD", statusWord);
511 captureCmd(nv, STATUS_MFR, Type::Debug);
512 captureCmd(nv, STATUS_TEMPERATURE, Type::Debug);
513 captureCmd(nv, STATUS_FANS_1_2, Type::Debug);
Brandon Wyman12661f12017-08-31 15:28:21 -0500514
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500515 using metadata =
516 org::open_power::Witherspoon::Fault::PowerSupplyFanFault;
Brandon Wyman12661f12017-08-31 15:28:21 -0500517
Brandon Wymanba255532017-11-08 17:44:10 -0600518 report<PowerSupplyFanFault>(
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500519 metadata::RAW_STATUS(nv.get().c_str()),
520 metadata::CALLOUT_INVENTORY_PATH(inventoryPath.c_str()));
Brandon Wymanba255532017-11-08 17:44:10 -0600521
522 faultFound = true;
523 }
Brandon Wyman12661f12017-08-31 15:28:21 -0500524 }
525}
526
Brandon Wyman875b3632017-09-13 18:46:03 -0500527void PowerSupply::checkTemperatureFault(const uint16_t statusWord)
528{
Lei YUab093322019-10-09 16:43:22 +0800529 using namespace phosphor::pmbus;
Brandon Wyman875b3632017-09-13 18:46:03 -0500530
531 // Due to how the PMBus core device driver sends a clear faults command
532 // the bit in STATUS_WORD will likely be cleared when we attempt to examine
533 // it for a Thermal Fault or Warning. So, check the STATUS_WORD and the
534 // STATUS_TEMPERATURE bits. If either indicates a fault, proceed with
535 // logging the over-temperature condition.
536 std::uint8_t statusTemperature = 0;
537 statusTemperature = pmbusIntf.read(STATUS_TEMPERATURE, Type::Debug);
Brandon Wyman50044ea2017-11-08 17:58:56 -0600538 if (temperatureFault < FAULT_COUNT)
Brandon Wyman875b3632017-09-13 18:46:03 -0500539 {
Brandon Wyman50044ea2017-11-08 17:58:56 -0600540 if ((statusWord & status_word::TEMPERATURE_FAULT_WARN) ||
541 (statusTemperature & status_temperature::OT_FAULT))
542 {
543 temperatureFault++;
544 }
545 else
546 {
547 if (temperatureFault > 0)
548 {
549 temperatureFault = 0;
550 }
551 }
Brandon Wyman875b3632017-09-13 18:46:03 -0500552
Brandon Wymane2fc7aa2017-11-13 17:37:10 -0600553 if (!faultFound && (temperatureFault >= FAULT_COUNT))
Brandon Wyman50044ea2017-11-08 17:58:56 -0600554 {
555 // The power supply has had an over-temperature condition.
556 // This may not result in a shutdown if experienced for a short
557 // duration.
558 // This should not occur under normal conditions.
559 // The power supply may be faulty, or the paired supply may be
560 // putting out less current.
561 // Capture command responses with potentially relevant information,
562 // and call out the power supply reporting the condition.
563 util::NamesValues nv;
564 nv.add("STATUS_WORD", statusWord);
565 captureCmd(nv, STATUS_MFR, Type::Debug);
566 captureCmd(nv, STATUS_IOUT, Type::Debug);
567 nv.add("STATUS_TEMPERATURE", statusTemperature);
568 captureCmd(nv, STATUS_FANS_1_2, Type::Debug);
Brandon Wyman875b3632017-09-13 18:46:03 -0500569
Brandon Wyman50044ea2017-11-08 17:58:56 -0600570 using metadata = org::open_power::Witherspoon::Fault::
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500571 PowerSupplyTemperatureFault;
Brandon Wyman875b3632017-09-13 18:46:03 -0500572
Brandon Wyman50044ea2017-11-08 17:58:56 -0600573 report<PowerSupplyTemperatureFault>(
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500574 metadata::RAW_STATUS(nv.get().c_str()),
575 metadata::CALLOUT_INVENTORY_PATH(inventoryPath.c_str()));
Brandon Wyman50044ea2017-11-08 17:58:56 -0600576
577 faultFound = true;
578 }
Brandon Wyman875b3632017-09-13 18:46:03 -0500579 }
580}
581
Brandon Wyman1db9a9e2017-07-26 18:50:22 -0500582void PowerSupply::clearFaults()
583{
Brandon Wymane4af9802017-11-13 15:58:33 -0600584 readFail = 0;
Brandon Wyman6ccce0b2017-10-26 15:13:10 -0500585 readFailLogged = false;
Brandon Wymana3c675c2017-11-14 14:54:54 -0600586 inputFault = 0;
Brandon Wyman6ccce0b2017-10-26 15:13:10 -0500587 powerOnFault = 0;
Brandon Wymandd61be42017-11-07 18:38:54 -0600588 outputOCFault = 0;
Brandon Wyman2ab319b2017-11-08 17:34:59 -0600589 outputOVFault = 0;
Brandon Wymanba255532017-11-08 17:44:10 -0600590 fanFault = 0;
Brandon Wyman50044ea2017-11-08 17:58:56 -0600591 temperatureFault = 0;
Brandon Wyman3343e822017-11-03 16:54:11 -0500592 faultFound = false;
Brandon Wyman6ccce0b2017-10-26 15:13:10 -0500593
Brandon Wyman1db9a9e2017-07-26 18:50:22 -0500594 return;
595}
596
Brandon Wyman43ce2082017-11-30 17:24:01 -0600597void PowerSupply::resolveError(const std::string& callout,
598 const std::string& message)
599{
Brandon Wyman01741f12017-12-01 17:22:08 -0600600 using EndpointList = std::vector<std::string>;
601
602 try
603 {
604 auto path = callout + "/fault";
605 // Get the service name from the mapper for the fault callout
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500606 auto service = util::getService(path, ASSOCIATION_IFACE, bus);
Brandon Wyman01741f12017-12-01 17:22:08 -0600607
608 // Use getProperty utility function to get log entries (endpoints)
609 EndpointList logEntries;
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500610 util::getProperty(ASSOCIATION_IFACE, ENDPOINTS_PROP, path, service, bus,
611 logEntries);
Brandon Wyman01741f12017-12-01 17:22:08 -0600612
613 // It is possible that all such entries for this callout have since
614 // been deleted.
615 if (logEntries.empty())
616 {
617 return;
618 }
619
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500620 auto logEntryService =
621 util::getService(logEntries[0], LOGGING_IFACE, bus);
Brandon Wyman01741f12017-12-01 17:22:08 -0600622 if (logEntryService.empty())
623 {
624 return;
625 }
626
627 // go through each log entry that matches this callout path
628 std::string logMessage;
629 for (const auto& logEntry : logEntries)
630 {
631 // Check to see if this logEntry has a message that matches.
Matt Spinler589e8722018-01-04 15:24:49 -0600632 util::getProperty(LOGGING_IFACE, MESSAGE_PROP, logEntry,
Brandon Wyman01741f12017-12-01 17:22:08 -0600633 logEntryService, bus, logMessage);
634
635 if (message == logMessage)
636 {
637 // Log entry matches call out and message, set Resolved to true
638 bool resolved = true;
Matt Spinler589e8722018-01-04 15:24:49 -0600639 util::setProperty(LOGGING_IFACE, RESOLVED_PROP, logEntry,
Brandon Wyman01741f12017-12-01 17:22:08 -0600640 logEntryService, bus, resolved);
641 }
Brandon Wyman01741f12017-12-01 17:22:08 -0600642 }
Brandon Wyman01741f12017-12-01 17:22:08 -0600643 }
644 catch (std::exception& e)
645 {
646 log<level::INFO>("Failed to resolve error",
647 entry("CALLOUT=%s", callout.c_str()),
Matt Spinler0d09f292018-01-22 14:51:26 -0600648 entry("ERROR=%s", message.c_str()));
Brandon Wyman01741f12017-12-01 17:22:08 -0600649 }
Brandon Wyman43ce2082017-11-30 17:24:01 -0600650}
651
Matt Spinler234ce0d2018-01-04 15:06:57 -0600652void PowerSupply::updateInventory()
653{
Lei YUab093322019-10-09 16:43:22 +0800654 using namespace phosphor::pmbus;
Matt Spinler018a7bc2018-01-04 15:36:41 -0600655 using namespace sdbusplus::message;
656
Matt Spinler018a7bc2018-01-04 15:36:41 -0600657 // Build the object map and send it to the inventory
658 using Properties = std::map<std::string, variant<std::string>>;
659 using Interfaces = std::map<std::string, Properties>;
660 using Object = std::map<object_path, Interfaces>;
661 Properties assetProps;
662 Properties versionProps;
663 Interfaces interfaces;
664 Object object;
665
George Liu690e7802019-08-23 11:04:01 +0800666 // If any of these accesses fail, the fields will just be
667 // blank in the inventory. Leave logging ReadFailure errors
668 // to analyze() as it runs continuously and will most
669 // likely hit and threshold them first anyway. The
670 // readString() function will do the tracing of the failing
671 // path so this code doesn't need to.
672 for (const auto& fru : fruJson.at("fruConfigs"))
673 {
674 if (fru.at("interface") == ASSET_IFACE)
675 {
676 try
677 {
678 assetProps.emplace(
679 fru.at("propertyName"),
680 present ? pmbusIntf.readString(fru.at("fileName"),
681 inventoryPMBusAccessType)
682 : "");
683 }
684 catch (ReadFailure& e)
685 {
686 }
687 }
688 else if (fru.at("interface") == VERSION_IFACE)
689 {
690 try
691 {
692 versionProps.emplace(
693 fru.at("propertyName"),
694 present ? pmbusIntf.readString(fru.at("fileName"),
695 inventoryPMBusAccessType)
696 : "");
697 }
698 catch (const std::exception& e)
699 {
700 }
701 }
702 }
Matt Spinler018a7bc2018-01-04 15:36:41 -0600703
George Liu690e7802019-08-23 11:04:01 +0800704 interfaces.emplace(ASSET_IFACE, std::move(assetProps));
Matt Spinler018a7bc2018-01-04 15:36:41 -0600705 interfaces.emplace(VERSION_IFACE, std::move(versionProps));
706
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500707 // For Notify(), just send the relative path of the inventory
708 // object so remove the INVENTORY_OBJ_PATH prefix
Matt Spinler018a7bc2018-01-04 15:36:41 -0600709 auto path = inventoryPath.substr(strlen(INVENTORY_OBJ_PATH));
710
711 object.emplace(path, std::move(interfaces));
712
713 try
714 {
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500715 auto service =
716 util::getService(INVENTORY_OBJ_PATH, INVENTORY_MGR_IFACE, bus);
Matt Spinler018a7bc2018-01-04 15:36:41 -0600717
718 if (service.empty())
719 {
720 log<level::ERR>("Unable to get inventory manager service");
721 return;
722 }
723
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500724 auto method = bus.new_method_call(service.c_str(), INVENTORY_OBJ_PATH,
725 INVENTORY_MGR_IFACE, "Notify");
Matt Spinler018a7bc2018-01-04 15:36:41 -0600726
727 method.append(std::move(object));
728
729 auto reply = bus.call(method);
Matt Spinler018a7bc2018-01-04 15:36:41 -0600730
731 // TODO: openbmc/openbmc#2756
732 // Calling Notify() with an enumerated property crashes inventory
733 // manager, so let it default to Unknown and now set it to the
734 // right value.
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500735 auto purpose =
736 version::convertForMessage(version::Version::VersionPurpose::Other);
Matt Spinler018a7bc2018-01-04 15:36:41 -0600737
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500738 util::setProperty(VERSION_IFACE, VERSION_PURPOSE_PROP, inventoryPath,
739 service, bus, purpose);
Matt Spinler018a7bc2018-01-04 15:36:41 -0600740 }
741 catch (std::exception& e)
742 {
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500743 log<level::ERR>(e.what(), entry("PATH=%s", inventoryPath.c_str()));
Matt Spinler018a7bc2018-01-04 15:36:41 -0600744 }
Matt Spinler234ce0d2018-01-04 15:06:57 -0600745}
746
Matt Spinlerd734e652018-01-18 14:31:15 -0600747void PowerSupply::syncHistory()
748{
Lei YUab093322019-10-09 16:43:22 +0800749 using namespace phosphor::gpio;
Matt Spinlerd734e652018-01-18 14:31:15 -0600750
751 if (syncGPIODevPath.empty())
752 {
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500753 // Sync not implemented
Matt Spinlerd734e652018-01-18 14:31:15 -0600754 return;
755 }
756
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500757 GPIO gpio{syncGPIODevPath, static_cast<gpioNum_t>(syncGPIONumber),
Matt Spinlerd734e652018-01-18 14:31:15 -0600758 Direction::output};
759
760 try
761 {
762 gpio.set(Value::low);
763
764 std::this_thread::sleep_for(std::chrono::milliseconds{5});
765
766 gpio.set(Value::high);
767
768 recordManager->clear();
769 }
770 catch (std::exception& e)
771 {
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500772 // Do nothing. There would already be a journal entry.
Matt Spinlerd734e652018-01-18 14:31:15 -0600773 }
774}
775
Matt Spinler82384142018-01-18 14:15:03 -0600776void PowerSupply::enableHistory(const std::string& objectPath,
777 size_t numRecords,
778 const std::string& syncGPIOPath,
779 size_t syncGPIONum)
780{
781 historyObjectPath = objectPath;
782 syncGPIODevPath = syncGPIOPath;
783 syncGPIONumber = syncGPIONum;
784
785 recordManager = std::make_unique<history::RecordManager>(numRecords);
786
787 auto avgPath = historyObjectPath + '/' + history::Average::name;
788 auto maxPath = historyObjectPath + '/' + history::Maximum::name;
789
790 average = std::make_unique<history::Average>(bus, avgPath);
791
792 maximum = std::make_unique<history::Maximum>(bus, maxPath);
793}
794
Matt Spinlereb169fd2018-01-18 14:19:08 -0600795void PowerSupply::updateHistory()
796{
797 if (!recordManager)
798 {
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500799 // Not enabled
Matt Spinlereb169fd2018-01-18 14:19:08 -0600800 return;
801 }
802
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500803 // Read just the most recent average/max record
804 auto data =
805 pmbusIntf.readBinary(INPUT_HISTORY, pmbus::Type::HwmonDeviceDebug,
806 history::RecordManager::RAW_RECORD_SIZE);
Matt Spinlereb169fd2018-01-18 14:19:08 -0600807
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500808 // Update D-Bus only if something changed (a new record ID, or cleared out)
Matt Spinlereb169fd2018-01-18 14:19:08 -0600809 auto changed = recordManager->add(data);
810 if (changed)
811 {
812 average->values(std::move(recordManager->getAverageRecords()));
813 maximum->values(std::move(recordManager->getMaximumRecords()));
814 }
815}
816
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500817} // namespace psu
818} // namespace power
Lei YUab093322019-10-09 16:43:22 +0800819} // namespace phosphor