blob: c10fdf77928f982fb2e8641e5b59dfcf0e842b61 [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>
30#include <xyz/openbmc_project/Software/Version/server.hpp>
31
Brandon Wymand1bc4ce2019-12-13 14:20:34 -060032#include <functional>
33
Lei YUab093322019-10-09 16:43:22 +080034namespace phosphor
Brandon Wyman24e422f2017-07-25 19:40:14 -050035{
36namespace power
37{
38namespace psu
39{
40
Matt Spinler589e8722018-01-04 15:24:49 -060041using namespace phosphor::logging;
42using namespace sdbusplus::org::open_power::Witherspoon::Fault::Error;
43using namespace sdbusplus::xyz::openbmc_project::Common::Device::Error;
Matt Spinler018a7bc2018-01-04 15:36:41 -060044namespace version = sdbusplus::xyz::openbmc_project::Software::server;
Matt Spinler589e8722018-01-04 15:24:49 -060045
Brandon Wyman10295542017-08-09 18:20:44 -050046PowerSupply::PowerSupply(const std::string& name, size_t inst,
Matt Spinlerf0f02b92018-10-25 16:12:43 -050047 const std::string& objpath, const std::string& invpath,
48 sdbusplus::bus::bus& bus, const sdeventplus::Event& e,
49 std::chrono::seconds& t, std::chrono::seconds& p) :
50 Device(name, inst),
51 monitorPath(objpath), pmbusIntf(objpath),
52 inventoryPath(INVENTORY_OBJ_PATH + invpath), bus(bus), presentInterval(p),
53 presentTimer(e, std::bind([this]() {
54 // The hwmon path may have changed.
55 pmbusIntf.findHwmonDir();
56 this->present = true;
Matt Spinler234ce0d2018-01-04 15:06:57 -060057
Matt Spinlerf0f02b92018-10-25 16:12:43 -050058 // Sync the INPUT_HISTORY data for all PSs
59 syncHistory();
Matt Spinlerd734e652018-01-18 14:31:15 -060060
Matt Spinlerf0f02b92018-10-25 16:12:43 -050061 // Update the inventory for the new device
62 updateInventory();
63 })),
64 powerOnInterval(t),
65 powerOnTimer(e, std::bind([this]() { this->powerOn = true; }))
Brandon Wyman10295542017-08-09 18:20:44 -050066{
George Liu690e7802019-08-23 11:04:01 +080067 getAccessType();
68
Brandon Wyman10295542017-08-09 18:20:44 -050069 using namespace sdbusplus::bus;
Lei YUab093322019-10-09 16:43:22 +080070 using namespace phosphor::pmbus;
Aatir Manzur817f8a72019-08-07 07:42:15 -050071 std::uint16_t statusWord = 0;
72 try
73 {
74 // Read the 2 byte STATUS_WORD value to check for faults.
75 statusWord = pmbusIntf.read(STATUS_WORD, Type::Debug);
76 if (!((statusWord & status_word::INPUT_FAULT_WARN) ||
77 (statusWord & status_word::VIN_UV_FAULT)))
78 {
79 resolveError(inventoryPath,
80 std::string(PowerSupplyInputFault::errName));
81 }
82 }
83 catch (ReadFailure& e)
84 {
85 log<level::INFO>("Unable to read the 2 byte STATUS_WORD value to check "
86 "for power-supply input faults.");
87 }
Matt Spinlerf0f02b92018-10-25 16:12:43 -050088 presentMatch = std::make_unique<match_t>(
89 bus, match::rules::propertiesChanged(inventoryPath, INVENTORY_IFACE),
90 [this](auto& msg) { this->inventoryChanged(msg); });
Brandon Wyman431fbe42017-08-18 16:22:09 -050091 // Get initial presence state.
Brandon Wyman253dc9b2017-08-12 13:45:52 -050092 updatePresence();
Brandon Wyman431fbe42017-08-18 16:22:09 -050093
Matt Spinler234ce0d2018-01-04 15:06:57 -060094 // Write the SN, PN, etc to the inventory
95 updateInventory();
96
Brandon Wyman431fbe42017-08-18 16:22:09 -050097 // Subscribe to power state changes
Matt Spinlerf0f02b92018-10-25 16:12:43 -050098 powerOnMatch = std::make_unique<match_t>(
99 bus, match::rules::propertiesChanged(POWER_OBJ_PATH, POWER_IFACE),
100 [this](auto& msg) { this->powerStateChanged(msg); });
Brandon Wyman431fbe42017-08-18 16:22:09 -0500101 // Get initial power state.
102 updatePowerState();
Brandon Wyman10295542017-08-09 18:20:44 -0500103}
Brandon Wyman442035f2017-08-08 15:58:45 -0500104
George Liu690e7802019-08-23 11:04:01 +0800105void PowerSupply::getAccessType()
106{
Lei YUab093322019-10-09 16:43:22 +0800107 using namespace phosphor::power::util;
George Liu690e7802019-08-23 11:04:01 +0800108 fruJson = loadJSONFromFile(PSU_JSON_PATH);
109 if (fruJson == nullptr)
110 {
111 log<level::ERR>("InternalFailure when parsing the JSON file");
112 return;
113 }
Lei YU40705462019-10-09 17:07:11 +0800114 inventoryPMBusAccessType = getPMBusAccessType(fruJson);
George Liu690e7802019-08-23 11:04:01 +0800115}
116
Brandon Wymana1e96342017-09-25 16:47:44 -0500117void PowerSupply::captureCmd(util::NamesValues& nv, const std::string& cmd,
Lei YUab093322019-10-09 16:43:22 +0800118 phosphor::pmbus::Type type)
Brandon Wymana1e96342017-09-25 16:47:44 -0500119{
120 if (pmbusIntf.exists(cmd, type))
121 {
122 try
123 {
124 auto val = pmbusIntf.read(cmd, type);
125 nv.add(cmd, val);
126 }
127 catch (std::exception& e)
128 {
Joseph Reynolds5b485962018-05-17 16:05:06 -0500129 log<level::INFO>("Unable to capture metadata",
130 entry("CMD=%s", cmd.c_str()));
Brandon Wymana1e96342017-09-25 16:47:44 -0500131 }
132 }
133}
Brandon Wyman431fbe42017-08-18 16:22:09 -0500134
Brandon Wyman1db9a9e2017-07-26 18:50:22 -0500135void PowerSupply::analyze()
136{
Lei YUab093322019-10-09 16:43:22 +0800137 using namespace phosphor::pmbus;
Brandon Wyman442035f2017-08-08 15:58:45 -0500138
139 try
140 {
Brandon Wyman10295542017-08-09 18:20:44 -0500141 if (present)
Brandon Wyman442035f2017-08-08 15:58:45 -0500142 {
Brandon Wyman764c7972017-08-22 17:05:36 -0500143 std::uint16_t statusWord = 0;
Brandon Wyman764c7972017-08-22 17:05:36 -0500144
145 // Read the 2 byte STATUS_WORD value to check for faults.
146 statusWord = pmbusIntf.read(STATUS_WORD, Type::Debug);
Brandon Wymane4af9802017-11-13 15:58:33 -0600147 readFail = 0;
Brandon Wyman764c7972017-08-22 17:05:36 -0500148
Brandon Wyman603cc002017-08-28 18:17:58 -0500149 checkInputFault(statusWord);
Brandon Wyman764c7972017-08-22 17:05:36 -0500150
Brandon Wyman654eb6e2018-02-14 14:55:47 -0600151 if (powerOn && (inputFault == 0) && !faultFound)
Brandon Wyman764c7972017-08-22 17:05:36 -0500152 {
Brandon Wyman12661f12017-08-31 15:28:21 -0500153 checkFanFault(statusWord);
Brandon Wyman875b3632017-09-13 18:46:03 -0500154 checkTemperatureFault(statusWord);
Brandon Wymancfa032b2017-09-25 17:37:50 -0500155 checkOutputOvervoltageFault(statusWord);
156 checkCurrentOutOverCurrentFault(statusWord);
157 checkPGOrUnitOffFault(statusWord);
Brandon Wyman442035f2017-08-08 15:58:45 -0500158 }
Matt Spinlereb169fd2018-01-18 14:19:08 -0600159
160 updateHistory();
Brandon Wyman442035f2017-08-08 15:58:45 -0500161 }
162 }
163 catch (ReadFailure& e)
164 {
Brandon Wymane4af9802017-11-13 15:58:33 -0600165 if (readFail < FAULT_COUNT)
166 {
167 readFail++;
168 }
169
170 if (!readFailLogged && readFail >= FAULT_COUNT)
Brandon Wyman442035f2017-08-08 15:58:45 -0500171 {
172 commit<ReadFailure>();
173 readFailLogged = true;
Brandon Wyman442035f2017-08-08 15:58:45 -0500174 }
175 }
176
Brandon Wyman1db9a9e2017-07-26 18:50:22 -0500177 return;
178}
179
Brandon Wyman10295542017-08-09 18:20:44 -0500180void PowerSupply::inventoryChanged(sdbusplus::message::message& msg)
181{
182 std::string msgSensor;
183 std::map<std::string, sdbusplus::message::variant<uint32_t, bool>> msgData;
184 msg.read(msgSensor, msgData);
185
186 // Check if it was the Present property that changed.
187 auto valPropMap = msgData.find(PRESENT_PROP);
188 if (valPropMap != msgData.end())
189 {
Brandon Wyman2ef48cf2017-11-21 15:43:54 -0600190 if (sdbusplus::message::variant_ns::get<bool>(valPropMap->second))
Brandon Wyman10295542017-08-09 18:20:44 -0500191 {
Brandon Wyman6ccce0b2017-10-26 15:13:10 -0500192 clearFaults();
William A. Kennington III1a0c9172018-10-18 17:57:49 -0700193 presentTimer.restartOnce(presentInterval);
Brandon Wyman590fc282017-11-01 18:22:25 -0500194 }
195 else
196 {
Brandon Wyman2ef48cf2017-11-21 15:43:54 -0600197 present = false;
William A. Kennington III1a0c9172018-10-18 17:57:49 -0700198 presentTimer.setEnabled(false);
Matt Spinler234ce0d2018-01-04 15:06:57 -0600199
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500200 // Clear out the now outdated inventory properties
Matt Spinler234ce0d2018-01-04 15:06:57 -0600201 updateInventory();
Brandon Wyman10295542017-08-09 18:20:44 -0500202 }
203 }
204
205 return;
206}
207
208void PowerSupply::updatePresence()
209{
210 // Use getProperty utility function to get presence status.
Brandon Wyman10295542017-08-09 18:20:44 -0500211 std::string service = "xyz.openbmc_project.Inventory.Manager";
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500212 util::getProperty(INVENTORY_IFACE, PRESENT_PROP, inventoryPath, service,
213 bus, this->present);
Brandon Wyman10295542017-08-09 18:20:44 -0500214}
215
Brandon Wyman431fbe42017-08-18 16:22:09 -0500216void PowerSupply::powerStateChanged(sdbusplus::message::message& msg)
217{
218 int32_t state = 0;
219 std::string msgSensor;
William A. Kennington IIIac9d5c32018-11-12 14:58:39 -0800220 std::map<std::string, sdbusplus::message::variant<int32_t>> msgData;
Brandon Wyman431fbe42017-08-18 16:22:09 -0500221 msg.read(msgSensor, msgData);
222
223 // Check if it was the Present property that changed.
224 auto valPropMap = msgData.find("state");
225 if (valPropMap != msgData.end())
226 {
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500227 state =
228 sdbusplus::message::variant_ns::get<int32_t>(valPropMap->second);
Brandon Wyman431fbe42017-08-18 16:22:09 -0500229
230 // Power is on when state=1. Set the fault logged variables to false
231 // and start the power on timer when the state changes to 1.
232 if (state)
233 {
Brandon Wyman6ccce0b2017-10-26 15:13:10 -0500234 clearFaults();
William A. Kennington III1a0c9172018-10-18 17:57:49 -0700235 powerOnTimer.restartOnce(powerOnInterval);
Brandon Wyman431fbe42017-08-18 16:22:09 -0500236 }
237 else
238 {
William A. Kennington III1a0c9172018-10-18 17:57:49 -0700239 powerOnTimer.setEnabled(false);
Brandon Wyman431fbe42017-08-18 16:22:09 -0500240 powerOn = false;
241 }
242 }
Brandon Wyman431fbe42017-08-18 16:22:09 -0500243}
244
245void PowerSupply::updatePowerState()
246{
Lei YUcfc040c2019-10-29 17:10:26 +0800247 powerOn = util::isPoweredOn(bus);
Brandon Wyman431fbe42017-08-18 16:22:09 -0500248}
249
Brandon Wyman603cc002017-08-28 18:17:58 -0500250void PowerSupply::checkInputFault(const uint16_t statusWord)
251{
Lei YUab093322019-10-09 16:43:22 +0800252 using namespace phosphor::pmbus;
Brandon Wyman603cc002017-08-28 18:17:58 -0500253
Brandon Wymana3c675c2017-11-14 14:54:54 -0600254 if ((inputFault < FAULT_COUNT) &&
255 ((statusWord & status_word::INPUT_FAULT_WARN) ||
256 (statusWord & status_word::VIN_UV_FAULT)))
Brandon Wyman603cc002017-08-28 18:17:58 -0500257 {
Brandon Wymanad708242018-01-19 17:28:35 -0600258 if (inputFault == 0)
259 {
260 log<level::INFO>("INPUT or VIN_UV fault",
261 entry("STATUS_WORD=0x%04X", statusWord));
262 }
263
Brandon Wymana3c675c2017-11-14 14:54:54 -0600264 inputFault++;
Brandon Wyman603cc002017-08-28 18:17:58 -0500265 }
266 else
267 {
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500268 if ((inputFault > 0) && !(statusWord & status_word::INPUT_FAULT_WARN) &&
Brandon Wymand20686a2017-11-01 17:45:23 -0500269 !(statusWord & status_word::VIN_UV_FAULT))
Brandon Wyman603cc002017-08-28 18:17:58 -0500270 {
Brandon Wymana3c675c2017-11-14 14:54:54 -0600271 inputFault = 0;
Brandon Wyman3343e822017-11-03 16:54:11 -0500272 faultFound = false;
Brandon Wyman7502f6c2018-02-12 20:20:39 -0600273 // When an input fault occurs, the power supply cannot be on.
274 // However, the check for the case where the power supply should be
275 // on will stop when there is a fault found.
276 // Clear the powerOnFault when the inputFault is cleared to reset
277 // the powerOnFault de-glitching.
278 powerOnFault = 0;
Brandon Wyman69591bd2017-11-01 18:07:23 -0500279
Brandon Wyman603cc002017-08-28 18:17:58 -0500280 log<level::INFO>("INPUT_FAULT_WARN cleared",
Brandon Wymana3c675c2017-11-14 14:54:54 -0600281 entry("POWERSUPPLY=%s", inventoryPath.c_str()));
Brandon Wyman69591bd2017-11-01 18:07:23 -0500282
Brandon Wyman08b05712017-11-30 17:53:56 -0600283 resolveError(inventoryPath,
284 std::string(PowerSupplyInputFault::errName));
285
Brandon Wyman69591bd2017-11-01 18:07:23 -0500286 if (powerOn)
287 {
288 // The power supply will not be immediately powered on after
289 // the input power is restored.
290 powerOn = false;
291 // Start up the timer that will set the state to indicate we
292 // are ready for the powered on fault checks.
William A. Kennington III1a0c9172018-10-18 17:57:49 -0700293 powerOnTimer.restartOnce(powerOnInterval);
Brandon Wyman69591bd2017-11-01 18:07:23 -0500294 }
Brandon Wyman603cc002017-08-28 18:17:58 -0500295 }
296 }
Brandon Wymana3c675c2017-11-14 14:54:54 -0600297
298 if (!faultFound && (inputFault >= FAULT_COUNT))
299 {
Brandon Wymanad708242018-01-19 17:28:35 -0600300 // If the power is on, report the fault in an error log entry.
301 if (powerOn)
302 {
303 util::NamesValues nv;
304 nv.add("STATUS_WORD", statusWord);
305 captureCmd(nv, STATUS_INPUT, Type::Debug);
Brandon Wymana3c675c2017-11-14 14:54:54 -0600306
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500307 using metadata =
308 org::open_power::Witherspoon::Fault::PowerSupplyInputFault;
Brandon Wymana3c675c2017-11-14 14:54:54 -0600309
Brandon Wymanad708242018-01-19 17:28:35 -0600310 report<PowerSupplyInputFault>(
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500311 metadata::RAW_STATUS(nv.get().c_str()),
312 metadata::CALLOUT_INVENTORY_PATH(inventoryPath.c_str()));
Brandon Wymanad708242018-01-19 17:28:35 -0600313
314 faultFound = true;
315 }
Brandon Wymana3c675c2017-11-14 14:54:54 -0600316 }
Brandon Wyman603cc002017-08-28 18:17:58 -0500317}
318
319void PowerSupply::checkPGOrUnitOffFault(const uint16_t statusWord)
320{
Lei YUab093322019-10-09 16:43:22 +0800321 using namespace phosphor::pmbus;
Brandon Wyman603cc002017-08-28 18:17:58 -0500322
Brandon Wyman593d24f2017-10-13 18:15:23 -0500323 if (powerOnFault < FAULT_COUNT)
Brandon Wyman603cc002017-08-28 18:17:58 -0500324 {
Brandon Wyman593d24f2017-10-13 18:15:23 -0500325 // Check PG# and UNIT_IS_OFF
326 if ((statusWord & status_word::POWER_GOOD_NEGATED) ||
327 (statusWord & status_word::UNIT_IS_OFF))
328 {
329 log<level::INFO>("PGOOD or UNIT_IS_OFF bit bad",
330 entry("STATUS_WORD=0x%04X", statusWord));
331 powerOnFault++;
332 }
333 else
334 {
335 if (powerOnFault > 0)
336 {
337 log<level::INFO>("PGOOD and UNIT_IS_OFF bits good");
338 powerOnFault = 0;
339 }
340 }
Brandon Wyman603cc002017-08-28 18:17:58 -0500341
Brandon Wymane2fc7aa2017-11-13 17:37:10 -0600342 if (!faultFound && (powerOnFault >= FAULT_COUNT))
Brandon Wyman593d24f2017-10-13 18:15:23 -0500343 {
Brandon Wyman3343e822017-11-03 16:54:11 -0500344 faultFound = true;
345
Brandon Wyman593d24f2017-10-13 18:15:23 -0500346 util::NamesValues nv;
347 nv.add("STATUS_WORD", statusWord);
348 captureCmd(nv, STATUS_INPUT, Type::Debug);
349 auto status0Vout = pmbusIntf.insertPageNum(STATUS_VOUT, 0);
350 captureCmd(nv, status0Vout, Type::Debug);
351 captureCmd(nv, STATUS_IOUT, Type::Debug);
352 captureCmd(nv, STATUS_MFR, Type::Debug);
Brandon Wyman603cc002017-08-28 18:17:58 -0500353
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500354 using metadata =
355 org::open_power::Witherspoon::Fault::PowerSupplyShouldBeOn;
Brandon Wyman603cc002017-08-28 18:17:58 -0500356
Brandon Wyman593d24f2017-10-13 18:15:23 -0500357 // A power supply is OFF (or pgood low) but should be on.
358 report<PowerSupplyShouldBeOn>(
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500359 metadata::RAW_STATUS(nv.get().c_str()),
360 metadata::CALLOUT_INVENTORY_PATH(inventoryPath.c_str()));
Brandon Wyman593d24f2017-10-13 18:15:23 -0500361 }
Brandon Wyman603cc002017-08-28 18:17:58 -0500362 }
Brandon Wyman603cc002017-08-28 18:17:58 -0500363}
364
365void PowerSupply::checkCurrentOutOverCurrentFault(const uint16_t statusWord)
366{
Lei YUab093322019-10-09 16:43:22 +0800367 using namespace phosphor::pmbus;
Brandon Wyman603cc002017-08-28 18:17:58 -0500368
Brandon Wymandd61be42017-11-07 18:38:54 -0600369 if (outputOCFault < FAULT_COUNT)
Brandon Wyman603cc002017-08-28 18:17:58 -0500370 {
Brandon Wymandd61be42017-11-07 18:38:54 -0600371 // Check for an output overcurrent fault.
372 if ((statusWord & status_word::IOUT_OC_FAULT))
373 {
374 outputOCFault++;
375 }
376 else
377 {
378 if (outputOCFault > 0)
379 {
380 outputOCFault = 0;
381 }
382 }
Brandon Wyman603cc002017-08-28 18:17:58 -0500383
Brandon Wymane2fc7aa2017-11-13 17:37:10 -0600384 if (!faultFound && (outputOCFault >= FAULT_COUNT))
Brandon Wymandd61be42017-11-07 18:38:54 -0600385 {
386 util::NamesValues nv;
387 nv.add("STATUS_WORD", statusWord);
388 captureCmd(nv, STATUS_INPUT, Type::Debug);
389 auto status0Vout = pmbusIntf.insertPageNum(STATUS_VOUT, 0);
390 captureCmd(nv, status0Vout, Type::Debug);
391 captureCmd(nv, STATUS_IOUT, Type::Debug);
392 captureCmd(nv, STATUS_MFR, Type::Debug);
Brandon Wyman603cc002017-08-28 18:17:58 -0500393
Brandon Wymandd61be42017-11-07 18:38:54 -0600394 using metadata = org::open_power::Witherspoon::Fault::
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500395 PowerSupplyOutputOvercurrent;
Brandon Wyman603cc002017-08-28 18:17:58 -0500396
Brandon Wymandd61be42017-11-07 18:38:54 -0600397 report<PowerSupplyOutputOvercurrent>(
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500398 metadata::RAW_STATUS(nv.get().c_str()),
399 metadata::CALLOUT_INVENTORY_PATH(inventoryPath.c_str()));
Brandon Wymandd61be42017-11-07 18:38:54 -0600400
401 faultFound = true;
402 }
Brandon Wyman603cc002017-08-28 18:17:58 -0500403 }
404}
405
Brandon Wymanab05c072017-08-30 18:26:41 -0500406void PowerSupply::checkOutputOvervoltageFault(const uint16_t statusWord)
407{
Lei YUab093322019-10-09 16:43:22 +0800408 using namespace phosphor::pmbus;
Brandon Wymanab05c072017-08-30 18:26:41 -0500409
Brandon Wyman2ab319b2017-11-08 17:34:59 -0600410 if (outputOVFault < FAULT_COUNT)
Brandon Wymanab05c072017-08-30 18:26:41 -0500411 {
Brandon Wyman2ab319b2017-11-08 17:34:59 -0600412 // Check for an output overvoltage fault.
413 if (statusWord & status_word::VOUT_OV_FAULT)
414 {
415 outputOVFault++;
416 }
417 else
418 {
419 if (outputOVFault > 0)
420 {
421 outputOVFault = 0;
422 }
423 }
Brandon Wymanab05c072017-08-30 18:26:41 -0500424
Brandon Wymane2fc7aa2017-11-13 17:37:10 -0600425 if (!faultFound && (outputOVFault >= FAULT_COUNT))
Brandon Wyman2ab319b2017-11-08 17:34:59 -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 Wymanab05c072017-08-30 18:26:41 -0500434
Brandon Wyman2ab319b2017-11-08 17:34:59 -0600435 using metadata = org::open_power::Witherspoon::Fault::
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500436 PowerSupplyOutputOvervoltage;
Brandon Wymanab05c072017-08-30 18:26:41 -0500437
Brandon Wyman2ab319b2017-11-08 17:34:59 -0600438 report<PowerSupplyOutputOvervoltage>(
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500439 metadata::RAW_STATUS(nv.get().c_str()),
440 metadata::CALLOUT_INVENTORY_PATH(inventoryPath.c_str()));
Brandon Wyman2ab319b2017-11-08 17:34:59 -0600441
442 faultFound = true;
443 }
Brandon Wymanab05c072017-08-30 18:26:41 -0500444 }
445}
446
Brandon Wyman12661f12017-08-31 15:28:21 -0500447void PowerSupply::checkFanFault(const uint16_t statusWord)
448{
Lei YUab093322019-10-09 16:43:22 +0800449 using namespace phosphor::pmbus;
Brandon Wyman12661f12017-08-31 15:28:21 -0500450
Brandon Wymanba255532017-11-08 17:44:10 -0600451 if (fanFault < FAULT_COUNT)
Brandon Wyman12661f12017-08-31 15:28:21 -0500452 {
Brandon Wymanba255532017-11-08 17:44:10 -0600453 // Check for a fan fault or warning condition
454 if (statusWord & status_word::FAN_FAULT)
455 {
456 fanFault++;
457 }
458 else
459 {
460 if (fanFault > 0)
461 {
462 fanFault = 0;
463 }
464 }
Brandon Wyman12661f12017-08-31 15:28:21 -0500465
Brandon Wymane2fc7aa2017-11-13 17:37:10 -0600466 if (!faultFound && (fanFault >= FAULT_COUNT))
Brandon Wymanba255532017-11-08 17:44:10 -0600467 {
468 util::NamesValues nv;
469 nv.add("STATUS_WORD", statusWord);
470 captureCmd(nv, STATUS_MFR, Type::Debug);
471 captureCmd(nv, STATUS_TEMPERATURE, Type::Debug);
472 captureCmd(nv, STATUS_FANS_1_2, Type::Debug);
Brandon Wyman12661f12017-08-31 15:28:21 -0500473
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500474 using metadata =
475 org::open_power::Witherspoon::Fault::PowerSupplyFanFault;
Brandon Wyman12661f12017-08-31 15:28:21 -0500476
Brandon Wymanba255532017-11-08 17:44:10 -0600477 report<PowerSupplyFanFault>(
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500478 metadata::RAW_STATUS(nv.get().c_str()),
479 metadata::CALLOUT_INVENTORY_PATH(inventoryPath.c_str()));
Brandon Wymanba255532017-11-08 17:44:10 -0600480
481 faultFound = true;
482 }
Brandon Wyman12661f12017-08-31 15:28:21 -0500483 }
484}
485
Brandon Wyman875b3632017-09-13 18:46:03 -0500486void PowerSupply::checkTemperatureFault(const uint16_t statusWord)
487{
Lei YUab093322019-10-09 16:43:22 +0800488 using namespace phosphor::pmbus;
Brandon Wyman875b3632017-09-13 18:46:03 -0500489
490 // Due to how the PMBus core device driver sends a clear faults command
491 // the bit in STATUS_WORD will likely be cleared when we attempt to examine
492 // it for a Thermal Fault or Warning. So, check the STATUS_WORD and the
493 // STATUS_TEMPERATURE bits. If either indicates a fault, proceed with
494 // logging the over-temperature condition.
495 std::uint8_t statusTemperature = 0;
496 statusTemperature = pmbusIntf.read(STATUS_TEMPERATURE, Type::Debug);
Brandon Wyman50044ea2017-11-08 17:58:56 -0600497 if (temperatureFault < FAULT_COUNT)
Brandon Wyman875b3632017-09-13 18:46:03 -0500498 {
Brandon Wyman50044ea2017-11-08 17:58:56 -0600499 if ((statusWord & status_word::TEMPERATURE_FAULT_WARN) ||
500 (statusTemperature & status_temperature::OT_FAULT))
501 {
502 temperatureFault++;
503 }
504 else
505 {
506 if (temperatureFault > 0)
507 {
508 temperatureFault = 0;
509 }
510 }
Brandon Wyman875b3632017-09-13 18:46:03 -0500511
Brandon Wymane2fc7aa2017-11-13 17:37:10 -0600512 if (!faultFound && (temperatureFault >= FAULT_COUNT))
Brandon Wyman50044ea2017-11-08 17:58:56 -0600513 {
514 // The power supply has had an over-temperature condition.
515 // This may not result in a shutdown if experienced for a short
516 // duration.
517 // This should not occur under normal conditions.
518 // The power supply may be faulty, or the paired supply may be
519 // putting out less current.
520 // Capture command responses with potentially relevant information,
521 // and call out the power supply reporting the condition.
522 util::NamesValues nv;
523 nv.add("STATUS_WORD", statusWord);
524 captureCmd(nv, STATUS_MFR, Type::Debug);
525 captureCmd(nv, STATUS_IOUT, Type::Debug);
526 nv.add("STATUS_TEMPERATURE", statusTemperature);
527 captureCmd(nv, STATUS_FANS_1_2, Type::Debug);
Brandon Wyman875b3632017-09-13 18:46:03 -0500528
Brandon Wyman50044ea2017-11-08 17:58:56 -0600529 using metadata = org::open_power::Witherspoon::Fault::
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500530 PowerSupplyTemperatureFault;
Brandon Wyman875b3632017-09-13 18:46:03 -0500531
Brandon Wyman50044ea2017-11-08 17:58:56 -0600532 report<PowerSupplyTemperatureFault>(
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500533 metadata::RAW_STATUS(nv.get().c_str()),
534 metadata::CALLOUT_INVENTORY_PATH(inventoryPath.c_str()));
Brandon Wyman50044ea2017-11-08 17:58:56 -0600535
536 faultFound = true;
537 }
Brandon Wyman875b3632017-09-13 18:46:03 -0500538 }
539}
540
Brandon Wyman1db9a9e2017-07-26 18:50:22 -0500541void PowerSupply::clearFaults()
542{
Brandon Wymane4af9802017-11-13 15:58:33 -0600543 readFail = 0;
Brandon Wyman6ccce0b2017-10-26 15:13:10 -0500544 readFailLogged = false;
Brandon Wymana3c675c2017-11-14 14:54:54 -0600545 inputFault = 0;
Brandon Wyman6ccce0b2017-10-26 15:13:10 -0500546 powerOnFault = 0;
Brandon Wymandd61be42017-11-07 18:38:54 -0600547 outputOCFault = 0;
Brandon Wyman2ab319b2017-11-08 17:34:59 -0600548 outputOVFault = 0;
Brandon Wymanba255532017-11-08 17:44:10 -0600549 fanFault = 0;
Brandon Wyman50044ea2017-11-08 17:58:56 -0600550 temperatureFault = 0;
Brandon Wyman3343e822017-11-03 16:54:11 -0500551 faultFound = false;
Brandon Wyman6ccce0b2017-10-26 15:13:10 -0500552
Brandon Wyman1db9a9e2017-07-26 18:50:22 -0500553 return;
554}
555
Brandon Wyman43ce2082017-11-30 17:24:01 -0600556void PowerSupply::resolveError(const std::string& callout,
557 const std::string& message)
558{
Brandon Wyman01741f12017-12-01 17:22:08 -0600559 using EndpointList = std::vector<std::string>;
560
561 try
562 {
563 auto path = callout + "/fault";
564 // Get the service name from the mapper for the fault callout
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500565 auto service = util::getService(path, ASSOCIATION_IFACE, bus);
Brandon Wyman01741f12017-12-01 17:22:08 -0600566
567 // Use getProperty utility function to get log entries (endpoints)
568 EndpointList logEntries;
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500569 util::getProperty(ASSOCIATION_IFACE, ENDPOINTS_PROP, path, service, bus,
570 logEntries);
Brandon Wyman01741f12017-12-01 17:22:08 -0600571
572 // It is possible that all such entries for this callout have since
573 // been deleted.
574 if (logEntries.empty())
575 {
576 return;
577 }
578
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500579 auto logEntryService =
580 util::getService(logEntries[0], LOGGING_IFACE, bus);
Brandon Wyman01741f12017-12-01 17:22:08 -0600581 if (logEntryService.empty())
582 {
583 return;
584 }
585
586 // go through each log entry that matches this callout path
587 std::string logMessage;
588 for (const auto& logEntry : logEntries)
589 {
590 // Check to see if this logEntry has a message that matches.
Matt Spinler589e8722018-01-04 15:24:49 -0600591 util::getProperty(LOGGING_IFACE, MESSAGE_PROP, logEntry,
Brandon Wyman01741f12017-12-01 17:22:08 -0600592 logEntryService, bus, logMessage);
593
594 if (message == logMessage)
595 {
596 // Log entry matches call out and message, set Resolved to true
597 bool resolved = true;
Matt Spinler589e8722018-01-04 15:24:49 -0600598 util::setProperty(LOGGING_IFACE, RESOLVED_PROP, logEntry,
Brandon Wyman01741f12017-12-01 17:22:08 -0600599 logEntryService, bus, resolved);
600 }
Brandon Wyman01741f12017-12-01 17:22:08 -0600601 }
Brandon Wyman01741f12017-12-01 17:22:08 -0600602 }
603 catch (std::exception& e)
604 {
605 log<level::INFO>("Failed to resolve error",
606 entry("CALLOUT=%s", callout.c_str()),
Matt Spinler0d09f292018-01-22 14:51:26 -0600607 entry("ERROR=%s", message.c_str()));
Brandon Wyman01741f12017-12-01 17:22:08 -0600608 }
Brandon Wyman43ce2082017-11-30 17:24:01 -0600609}
610
Matt Spinler234ce0d2018-01-04 15:06:57 -0600611void PowerSupply::updateInventory()
612{
Lei YUab093322019-10-09 16:43:22 +0800613 using namespace phosphor::pmbus;
Matt Spinler018a7bc2018-01-04 15:36:41 -0600614 using namespace sdbusplus::message;
615
Matt Spinler018a7bc2018-01-04 15:36:41 -0600616 // Build the object map and send it to the inventory
617 using Properties = std::map<std::string, variant<std::string>>;
618 using Interfaces = std::map<std::string, Properties>;
619 using Object = std::map<object_path, Interfaces>;
620 Properties assetProps;
621 Properties versionProps;
622 Interfaces interfaces;
623 Object object;
624
George Liu690e7802019-08-23 11:04:01 +0800625 // If any of these accesses fail, the fields will just be
626 // blank in the inventory. Leave logging ReadFailure errors
627 // to analyze() as it runs continuously and will most
628 // likely hit and threshold them first anyway. The
629 // readString() function will do the tracing of the failing
630 // path so this code doesn't need to.
631 for (const auto& fru : fruJson.at("fruConfigs"))
632 {
633 if (fru.at("interface") == ASSET_IFACE)
634 {
635 try
636 {
637 assetProps.emplace(
638 fru.at("propertyName"),
639 present ? pmbusIntf.readString(fru.at("fileName"),
640 inventoryPMBusAccessType)
641 : "");
642 }
643 catch (ReadFailure& e)
644 {
645 }
646 }
647 else if (fru.at("interface") == VERSION_IFACE)
648 {
649 try
650 {
651 versionProps.emplace(
652 fru.at("propertyName"),
653 present ? pmbusIntf.readString(fru.at("fileName"),
654 inventoryPMBusAccessType)
655 : "");
656 }
657 catch (const std::exception& e)
658 {
659 }
660 }
661 }
Matt Spinler018a7bc2018-01-04 15:36:41 -0600662
George Liu690e7802019-08-23 11:04:01 +0800663 interfaces.emplace(ASSET_IFACE, std::move(assetProps));
Matt Spinler018a7bc2018-01-04 15:36:41 -0600664 interfaces.emplace(VERSION_IFACE, std::move(versionProps));
665
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500666 // For Notify(), just send the relative path of the inventory
667 // object so remove the INVENTORY_OBJ_PATH prefix
Matt Spinler018a7bc2018-01-04 15:36:41 -0600668 auto path = inventoryPath.substr(strlen(INVENTORY_OBJ_PATH));
669
670 object.emplace(path, std::move(interfaces));
671
672 try
673 {
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500674 auto service =
675 util::getService(INVENTORY_OBJ_PATH, INVENTORY_MGR_IFACE, bus);
Matt Spinler018a7bc2018-01-04 15:36:41 -0600676
677 if (service.empty())
678 {
679 log<level::ERR>("Unable to get inventory manager service");
680 return;
681 }
682
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500683 auto method = bus.new_method_call(service.c_str(), INVENTORY_OBJ_PATH,
684 INVENTORY_MGR_IFACE, "Notify");
Matt Spinler018a7bc2018-01-04 15:36:41 -0600685
686 method.append(std::move(object));
687
688 auto reply = bus.call(method);
Matt Spinler018a7bc2018-01-04 15:36:41 -0600689
690 // TODO: openbmc/openbmc#2756
691 // Calling Notify() with an enumerated property crashes inventory
692 // manager, so let it default to Unknown and now set it to the
693 // right value.
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500694 auto purpose =
695 version::convertForMessage(version::Version::VersionPurpose::Other);
Matt Spinler018a7bc2018-01-04 15:36:41 -0600696
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500697 util::setProperty(VERSION_IFACE, VERSION_PURPOSE_PROP, inventoryPath,
698 service, bus, purpose);
Matt Spinler018a7bc2018-01-04 15:36:41 -0600699 }
700 catch (std::exception& e)
701 {
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500702 log<level::ERR>(e.what(), entry("PATH=%s", inventoryPath.c_str()));
Matt Spinler018a7bc2018-01-04 15:36:41 -0600703 }
Matt Spinler234ce0d2018-01-04 15:06:57 -0600704}
705
Matt Spinlerd734e652018-01-18 14:31:15 -0600706void PowerSupply::syncHistory()
707{
Lei YUab093322019-10-09 16:43:22 +0800708 using namespace phosphor::gpio;
Matt Spinlerd734e652018-01-18 14:31:15 -0600709
710 if (syncGPIODevPath.empty())
711 {
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500712 // Sync not implemented
Matt Spinlerd734e652018-01-18 14:31:15 -0600713 return;
714 }
715
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500716 GPIO gpio{syncGPIODevPath, static_cast<gpioNum_t>(syncGPIONumber),
Matt Spinlerd734e652018-01-18 14:31:15 -0600717 Direction::output};
718
719 try
720 {
721 gpio.set(Value::low);
722
723 std::this_thread::sleep_for(std::chrono::milliseconds{5});
724
725 gpio.set(Value::high);
726
727 recordManager->clear();
728 }
729 catch (std::exception& e)
730 {
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500731 // Do nothing. There would already be a journal entry.
Matt Spinlerd734e652018-01-18 14:31:15 -0600732 }
733}
734
Matt Spinler82384142018-01-18 14:15:03 -0600735void PowerSupply::enableHistory(const std::string& objectPath,
736 size_t numRecords,
737 const std::string& syncGPIOPath,
738 size_t syncGPIONum)
739{
740 historyObjectPath = objectPath;
741 syncGPIODevPath = syncGPIOPath;
742 syncGPIONumber = syncGPIONum;
743
744 recordManager = std::make_unique<history::RecordManager>(numRecords);
745
746 auto avgPath = historyObjectPath + '/' + history::Average::name;
747 auto maxPath = historyObjectPath + '/' + history::Maximum::name;
748
749 average = std::make_unique<history::Average>(bus, avgPath);
750
751 maximum = std::make_unique<history::Maximum>(bus, maxPath);
752}
753
Matt Spinlereb169fd2018-01-18 14:19:08 -0600754void PowerSupply::updateHistory()
755{
756 if (!recordManager)
757 {
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500758 // Not enabled
Matt Spinlereb169fd2018-01-18 14:19:08 -0600759 return;
760 }
761
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500762 // Read just the most recent average/max record
763 auto data =
764 pmbusIntf.readBinary(INPUT_HISTORY, pmbus::Type::HwmonDeviceDebug,
765 history::RecordManager::RAW_RECORD_SIZE);
Matt Spinlereb169fd2018-01-18 14:19:08 -0600766
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500767 // Update D-Bus only if something changed (a new record ID, or cleared out)
Matt Spinlereb169fd2018-01-18 14:19:08 -0600768 auto changed = recordManager->add(data);
769 if (changed)
770 {
771 average->values(std::move(recordManager->getAverageRecords()));
772 maximum->values(std::move(recordManager->getMaximumRecords()));
773 }
774}
775
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500776} // namespace psu
777} // namespace power
Lei YUab093322019-10-09 16:43:22 +0800778} // namespace phosphor