blob: 981435fae4e531da166dfd565561797c572791c9 [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 <functional>
28#include <org/open_power/Witherspoon/Fault/error.hpp>
Matt Spinlerf0f02b92018-10-25 16:12:43 -050029#include <phosphor-logging/log.hpp>
30#include <xyz/openbmc_project/Common/Device/error.hpp>
31#include <xyz/openbmc_project/Software/Version/server.hpp>
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;
Matt Spinler018a7bc2018-01-04 15:36:41 -060043namespace version = sdbusplus::xyz::openbmc_project::Software::server;
Matt Spinler589e8722018-01-04 15:24:49 -060044
Brandon Wyman10295542017-08-09 18:20:44 -050045PowerSupply::PowerSupply(const std::string& name, size_t inst,
Matt Spinlerf0f02b92018-10-25 16:12:43 -050046 const std::string& objpath, const std::string& invpath,
47 sdbusplus::bus::bus& bus, const sdeventplus::Event& e,
48 std::chrono::seconds& t, std::chrono::seconds& p) :
49 Device(name, inst),
50 monitorPath(objpath), pmbusIntf(objpath),
51 inventoryPath(INVENTORY_OBJ_PATH + invpath), bus(bus), presentInterval(p),
52 presentTimer(e, std::bind([this]() {
53 // The hwmon path may have changed.
54 pmbusIntf.findHwmonDir();
55 this->present = true;
Matt Spinler234ce0d2018-01-04 15:06:57 -060056
Matt Spinlerf0f02b92018-10-25 16:12:43 -050057 // Sync the INPUT_HISTORY data for all PSs
58 syncHistory();
Matt Spinlerd734e652018-01-18 14:31:15 -060059
Matt Spinlerf0f02b92018-10-25 16:12:43 -050060 // Update the inventory for the new device
61 updateInventory();
62 })),
63 powerOnInterval(t),
64 powerOnTimer(e, std::bind([this]() { this->powerOn = true; }))
Brandon Wyman10295542017-08-09 18:20:44 -050065{
George Liu690e7802019-08-23 11:04:01 +080066 getAccessType();
67
Brandon Wyman10295542017-08-09 18:20:44 -050068 using namespace sdbusplus::bus;
Lei YUab093322019-10-09 16:43:22 +080069 using namespace phosphor::pmbus;
Aatir Manzur817f8a72019-08-07 07:42:15 -050070 std::uint16_t statusWord = 0;
71 try
72 {
73 // Read the 2 byte STATUS_WORD value to check for faults.
74 statusWord = pmbusIntf.read(STATUS_WORD, Type::Debug);
75 if (!((statusWord & status_word::INPUT_FAULT_WARN) ||
76 (statusWord & status_word::VIN_UV_FAULT)))
77 {
78 resolveError(inventoryPath,
79 std::string(PowerSupplyInputFault::errName));
80 }
81 }
82 catch (ReadFailure& e)
83 {
84 log<level::INFO>("Unable to read the 2 byte STATUS_WORD value to check "
85 "for power-supply input faults.");
86 }
Matt Spinlerf0f02b92018-10-25 16:12:43 -050087 presentMatch = std::make_unique<match_t>(
88 bus, match::rules::propertiesChanged(inventoryPath, INVENTORY_IFACE),
89 [this](auto& msg) { this->inventoryChanged(msg); });
Brandon Wyman431fbe42017-08-18 16:22:09 -050090 // Get initial presence state.
Brandon Wyman253dc9b2017-08-12 13:45:52 -050091 updatePresence();
Brandon Wyman431fbe42017-08-18 16:22:09 -050092
Matt Spinler234ce0d2018-01-04 15:06:57 -060093 // Write the SN, PN, etc to the inventory
94 updateInventory();
95
Brandon Wyman431fbe42017-08-18 16:22:09 -050096 // Subscribe to power state changes
Matt Spinlerf0f02b92018-10-25 16:12:43 -050097 powerOnMatch = std::make_unique<match_t>(
98 bus, match::rules::propertiesChanged(POWER_OBJ_PATH, POWER_IFACE),
99 [this](auto& msg) { this->powerStateChanged(msg); });
Brandon Wyman431fbe42017-08-18 16:22:09 -0500100 // Get initial power state.
101 updatePowerState();
Brandon Wyman10295542017-08-09 18:20:44 -0500102}
Brandon Wyman442035f2017-08-08 15:58:45 -0500103
George Liu690e7802019-08-23 11:04:01 +0800104void PowerSupply::getAccessType()
105{
Lei YUab093322019-10-09 16:43:22 +0800106 using namespace phosphor::power::util;
George Liu690e7802019-08-23 11:04:01 +0800107 fruJson = loadJSONFromFile(PSU_JSON_PATH);
108 if (fruJson == nullptr)
109 {
110 log<level::ERR>("InternalFailure when parsing the JSON file");
111 return;
112 }
Lei YU40705462019-10-09 17:07:11 +0800113 inventoryPMBusAccessType = getPMBusAccessType(fruJson);
George Liu690e7802019-08-23 11:04:01 +0800114}
115
Brandon Wymana1e96342017-09-25 16:47:44 -0500116void PowerSupply::captureCmd(util::NamesValues& nv, const std::string& cmd,
Lei YUab093322019-10-09 16:43:22 +0800117 phosphor::pmbus::Type type)
Brandon Wymana1e96342017-09-25 16:47:44 -0500118{
119 if (pmbusIntf.exists(cmd, type))
120 {
121 try
122 {
123 auto val = pmbusIntf.read(cmd, type);
124 nv.add(cmd, val);
125 }
126 catch (std::exception& e)
127 {
Joseph Reynolds5b485962018-05-17 16:05:06 -0500128 log<level::INFO>("Unable to capture metadata",
129 entry("CMD=%s", cmd.c_str()));
Brandon Wymana1e96342017-09-25 16:47:44 -0500130 }
131 }
132}
Brandon Wyman431fbe42017-08-18 16:22:09 -0500133
Brandon Wyman1db9a9e2017-07-26 18:50:22 -0500134void PowerSupply::analyze()
135{
Lei YUab093322019-10-09 16:43:22 +0800136 using namespace phosphor::pmbus;
Brandon Wyman442035f2017-08-08 15:58:45 -0500137
138 try
139 {
Brandon Wyman10295542017-08-09 18:20:44 -0500140 if (present)
Brandon Wyman442035f2017-08-08 15:58:45 -0500141 {
Brandon Wyman764c7972017-08-22 17:05:36 -0500142 std::uint16_t statusWord = 0;
Brandon Wyman764c7972017-08-22 17:05:36 -0500143
144 // Read the 2 byte STATUS_WORD value to check for faults.
145 statusWord = pmbusIntf.read(STATUS_WORD, Type::Debug);
Brandon Wymane4af9802017-11-13 15:58:33 -0600146 readFail = 0;
Brandon Wyman764c7972017-08-22 17:05:36 -0500147
Brandon Wyman603cc002017-08-28 18:17:58 -0500148 checkInputFault(statusWord);
Brandon Wyman764c7972017-08-22 17:05:36 -0500149
Brandon Wyman654eb6e2018-02-14 14:55:47 -0600150 if (powerOn && (inputFault == 0) && !faultFound)
Brandon Wyman764c7972017-08-22 17:05:36 -0500151 {
Brandon Wyman12661f12017-08-31 15:28:21 -0500152 checkFanFault(statusWord);
Brandon Wyman875b3632017-09-13 18:46:03 -0500153 checkTemperatureFault(statusWord);
Brandon Wymancfa032b2017-09-25 17:37:50 -0500154 checkOutputOvervoltageFault(statusWord);
155 checkCurrentOutOverCurrentFault(statusWord);
156 checkPGOrUnitOffFault(statusWord);
Brandon Wyman442035f2017-08-08 15:58:45 -0500157 }
Matt Spinlereb169fd2018-01-18 14:19:08 -0600158
159 updateHistory();
Brandon Wyman442035f2017-08-08 15:58:45 -0500160 }
161 }
162 catch (ReadFailure& e)
163 {
Brandon Wymane4af9802017-11-13 15:58:33 -0600164 if (readFail < FAULT_COUNT)
165 {
166 readFail++;
167 }
168
169 if (!readFailLogged && readFail >= FAULT_COUNT)
Brandon Wyman442035f2017-08-08 15:58:45 -0500170 {
171 commit<ReadFailure>();
172 readFailLogged = true;
Brandon Wyman442035f2017-08-08 15:58:45 -0500173 }
174 }
175
Brandon Wyman1db9a9e2017-07-26 18:50:22 -0500176 return;
177}
178
Brandon Wyman10295542017-08-09 18:20:44 -0500179void PowerSupply::inventoryChanged(sdbusplus::message::message& msg)
180{
181 std::string msgSensor;
182 std::map<std::string, sdbusplus::message::variant<uint32_t, bool>> msgData;
183 msg.read(msgSensor, msgData);
184
185 // Check if it was the Present property that changed.
186 auto valPropMap = msgData.find(PRESENT_PROP);
187 if (valPropMap != msgData.end())
188 {
Brandon Wyman2ef48cf2017-11-21 15:43:54 -0600189 if (sdbusplus::message::variant_ns::get<bool>(valPropMap->second))
Brandon Wyman10295542017-08-09 18:20:44 -0500190 {
Brandon Wyman6ccce0b2017-10-26 15:13:10 -0500191 clearFaults();
William A. Kennington III1a0c9172018-10-18 17:57:49 -0700192 presentTimer.restartOnce(presentInterval);
Brandon Wyman590fc282017-11-01 18:22:25 -0500193 }
194 else
195 {
Brandon Wyman2ef48cf2017-11-21 15:43:54 -0600196 present = false;
William A. Kennington III1a0c9172018-10-18 17:57:49 -0700197 presentTimer.setEnabled(false);
Matt Spinler234ce0d2018-01-04 15:06:57 -0600198
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500199 // Clear out the now outdated inventory properties
Matt Spinler234ce0d2018-01-04 15:06:57 -0600200 updateInventory();
Brandon Wyman10295542017-08-09 18:20:44 -0500201 }
202 }
203
204 return;
205}
206
207void PowerSupply::updatePresence()
208{
209 // Use getProperty utility function to get presence status.
Brandon Wyman10295542017-08-09 18:20:44 -0500210 std::string service = "xyz.openbmc_project.Inventory.Manager";
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500211 util::getProperty(INVENTORY_IFACE, PRESENT_PROP, inventoryPath, service,
212 bus, this->present);
Brandon Wyman10295542017-08-09 18:20:44 -0500213}
214
Brandon Wyman431fbe42017-08-18 16:22:09 -0500215void PowerSupply::powerStateChanged(sdbusplus::message::message& msg)
216{
217 int32_t state = 0;
218 std::string msgSensor;
William A. Kennington IIIac9d5c32018-11-12 14:58:39 -0800219 std::map<std::string, sdbusplus::message::variant<int32_t>> msgData;
Brandon Wyman431fbe42017-08-18 16:22:09 -0500220 msg.read(msgSensor, msgData);
221
222 // Check if it was the Present property that changed.
223 auto valPropMap = msgData.find("state");
224 if (valPropMap != msgData.end())
225 {
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500226 state =
227 sdbusplus::message::variant_ns::get<int32_t>(valPropMap->second);
Brandon Wyman431fbe42017-08-18 16:22:09 -0500228
229 // Power is on when state=1. Set the fault logged variables to false
230 // and start the power on timer when the state changes to 1.
231 if (state)
232 {
Brandon Wyman6ccce0b2017-10-26 15:13:10 -0500233 clearFaults();
William A. Kennington III1a0c9172018-10-18 17:57:49 -0700234 powerOnTimer.restartOnce(powerOnInterval);
Brandon Wyman431fbe42017-08-18 16:22:09 -0500235 }
236 else
237 {
William A. Kennington III1a0c9172018-10-18 17:57:49 -0700238 powerOnTimer.setEnabled(false);
Brandon Wyman431fbe42017-08-18 16:22:09 -0500239 powerOn = false;
240 }
241 }
Brandon Wyman431fbe42017-08-18 16:22:09 -0500242}
243
244void PowerSupply::updatePowerState()
245{
Lei YUcfc040c2019-10-29 17:10:26 +0800246 powerOn = util::isPoweredOn(bus);
Brandon Wyman431fbe42017-08-18 16:22:09 -0500247}
248
Brandon Wyman603cc002017-08-28 18:17:58 -0500249void PowerSupply::checkInputFault(const uint16_t statusWord)
250{
Lei YUab093322019-10-09 16:43:22 +0800251 using namespace phosphor::pmbus;
Brandon Wyman603cc002017-08-28 18:17:58 -0500252
Brandon Wymana3c675c2017-11-14 14:54:54 -0600253 if ((inputFault < FAULT_COUNT) &&
254 ((statusWord & status_word::INPUT_FAULT_WARN) ||
255 (statusWord & status_word::VIN_UV_FAULT)))
Brandon Wyman603cc002017-08-28 18:17:58 -0500256 {
Brandon Wymanad708242018-01-19 17:28:35 -0600257 if (inputFault == 0)
258 {
259 log<level::INFO>("INPUT or VIN_UV fault",
260 entry("STATUS_WORD=0x%04X", statusWord));
261 }
262
Brandon Wymana3c675c2017-11-14 14:54:54 -0600263 inputFault++;
Brandon Wyman603cc002017-08-28 18:17:58 -0500264 }
265 else
266 {
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500267 if ((inputFault > 0) && !(statusWord & status_word::INPUT_FAULT_WARN) &&
Brandon Wymand20686a2017-11-01 17:45:23 -0500268 !(statusWord & status_word::VIN_UV_FAULT))
Brandon Wyman603cc002017-08-28 18:17:58 -0500269 {
Brandon Wymana3c675c2017-11-14 14:54:54 -0600270 inputFault = 0;
Brandon Wyman3343e822017-11-03 16:54:11 -0500271 faultFound = false;
Brandon Wyman7502f6c2018-02-12 20:20:39 -0600272 // When an input fault occurs, the power supply cannot be on.
273 // However, the check for the case where the power supply should be
274 // on will stop when there is a fault found.
275 // Clear the powerOnFault when the inputFault is cleared to reset
276 // the powerOnFault de-glitching.
277 powerOnFault = 0;
Brandon Wyman69591bd2017-11-01 18:07:23 -0500278
Brandon Wyman603cc002017-08-28 18:17:58 -0500279 log<level::INFO>("INPUT_FAULT_WARN cleared",
Brandon Wymana3c675c2017-11-14 14:54:54 -0600280 entry("POWERSUPPLY=%s", inventoryPath.c_str()));
Brandon Wyman69591bd2017-11-01 18:07:23 -0500281
Brandon Wyman08b05712017-11-30 17:53:56 -0600282 resolveError(inventoryPath,
283 std::string(PowerSupplyInputFault::errName));
284
Brandon Wyman69591bd2017-11-01 18:07:23 -0500285 if (powerOn)
286 {
287 // The power supply will not be immediately powered on after
288 // the input power is restored.
289 powerOn = false;
290 // Start up the timer that will set the state to indicate we
291 // are ready for the powered on fault checks.
William A. Kennington III1a0c9172018-10-18 17:57:49 -0700292 powerOnTimer.restartOnce(powerOnInterval);
Brandon Wyman69591bd2017-11-01 18:07:23 -0500293 }
Brandon Wyman603cc002017-08-28 18:17:58 -0500294 }
295 }
Brandon Wymana3c675c2017-11-14 14:54:54 -0600296
297 if (!faultFound && (inputFault >= FAULT_COUNT))
298 {
Brandon Wymanad708242018-01-19 17:28:35 -0600299 // If the power is on, report the fault in an error log entry.
300 if (powerOn)
301 {
302 util::NamesValues nv;
303 nv.add("STATUS_WORD", statusWord);
304 captureCmd(nv, STATUS_INPUT, Type::Debug);
Brandon Wymana3c675c2017-11-14 14:54:54 -0600305
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500306 using metadata =
307 org::open_power::Witherspoon::Fault::PowerSupplyInputFault;
Brandon Wymana3c675c2017-11-14 14:54:54 -0600308
Brandon Wymanad708242018-01-19 17:28:35 -0600309 report<PowerSupplyInputFault>(
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500310 metadata::RAW_STATUS(nv.get().c_str()),
311 metadata::CALLOUT_INVENTORY_PATH(inventoryPath.c_str()));
Brandon Wymanad708242018-01-19 17:28:35 -0600312
313 faultFound = true;
314 }
Brandon Wymana3c675c2017-11-14 14:54:54 -0600315 }
Brandon Wyman603cc002017-08-28 18:17:58 -0500316}
317
318void PowerSupply::checkPGOrUnitOffFault(const uint16_t statusWord)
319{
Lei YUab093322019-10-09 16:43:22 +0800320 using namespace phosphor::pmbus;
Brandon Wyman603cc002017-08-28 18:17:58 -0500321
Brandon Wyman593d24f2017-10-13 18:15:23 -0500322 if (powerOnFault < FAULT_COUNT)
Brandon Wyman603cc002017-08-28 18:17:58 -0500323 {
Brandon Wyman593d24f2017-10-13 18:15:23 -0500324 // Check PG# and UNIT_IS_OFF
325 if ((statusWord & status_word::POWER_GOOD_NEGATED) ||
326 (statusWord & status_word::UNIT_IS_OFF))
327 {
328 log<level::INFO>("PGOOD or UNIT_IS_OFF bit bad",
329 entry("STATUS_WORD=0x%04X", statusWord));
330 powerOnFault++;
331 }
332 else
333 {
334 if (powerOnFault > 0)
335 {
336 log<level::INFO>("PGOOD and UNIT_IS_OFF bits good");
337 powerOnFault = 0;
338 }
339 }
Brandon Wyman603cc002017-08-28 18:17:58 -0500340
Brandon Wymane2fc7aa2017-11-13 17:37:10 -0600341 if (!faultFound && (powerOnFault >= FAULT_COUNT))
Brandon Wyman593d24f2017-10-13 18:15:23 -0500342 {
Brandon Wyman3343e822017-11-03 16:54:11 -0500343 faultFound = true;
344
Brandon Wyman593d24f2017-10-13 18:15:23 -0500345 util::NamesValues nv;
346 nv.add("STATUS_WORD", statusWord);
347 captureCmd(nv, STATUS_INPUT, Type::Debug);
348 auto status0Vout = pmbusIntf.insertPageNum(STATUS_VOUT, 0);
349 captureCmd(nv, status0Vout, Type::Debug);
350 captureCmd(nv, STATUS_IOUT, Type::Debug);
351 captureCmd(nv, STATUS_MFR, Type::Debug);
Brandon Wyman603cc002017-08-28 18:17:58 -0500352
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500353 using metadata =
354 org::open_power::Witherspoon::Fault::PowerSupplyShouldBeOn;
Brandon Wyman603cc002017-08-28 18:17:58 -0500355
Brandon Wyman593d24f2017-10-13 18:15:23 -0500356 // A power supply is OFF (or pgood low) but should be on.
357 report<PowerSupplyShouldBeOn>(
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500358 metadata::RAW_STATUS(nv.get().c_str()),
359 metadata::CALLOUT_INVENTORY_PATH(inventoryPath.c_str()));
Brandon Wyman593d24f2017-10-13 18:15:23 -0500360 }
Brandon Wyman603cc002017-08-28 18:17:58 -0500361 }
Brandon Wyman603cc002017-08-28 18:17:58 -0500362}
363
364void PowerSupply::checkCurrentOutOverCurrentFault(const uint16_t statusWord)
365{
Lei YUab093322019-10-09 16:43:22 +0800366 using namespace phosphor::pmbus;
Brandon Wyman603cc002017-08-28 18:17:58 -0500367
Brandon Wymandd61be42017-11-07 18:38:54 -0600368 if (outputOCFault < FAULT_COUNT)
Brandon Wyman603cc002017-08-28 18:17:58 -0500369 {
Brandon Wymandd61be42017-11-07 18:38:54 -0600370 // Check for an output overcurrent fault.
371 if ((statusWord & status_word::IOUT_OC_FAULT))
372 {
373 outputOCFault++;
374 }
375 else
376 {
377 if (outputOCFault > 0)
378 {
379 outputOCFault = 0;
380 }
381 }
Brandon Wyman603cc002017-08-28 18:17:58 -0500382
Brandon Wymane2fc7aa2017-11-13 17:37:10 -0600383 if (!faultFound && (outputOCFault >= FAULT_COUNT))
Brandon Wymandd61be42017-11-07 18:38:54 -0600384 {
385 util::NamesValues nv;
386 nv.add("STATUS_WORD", statusWord);
387 captureCmd(nv, STATUS_INPUT, Type::Debug);
388 auto status0Vout = pmbusIntf.insertPageNum(STATUS_VOUT, 0);
389 captureCmd(nv, status0Vout, Type::Debug);
390 captureCmd(nv, STATUS_IOUT, Type::Debug);
391 captureCmd(nv, STATUS_MFR, Type::Debug);
Brandon Wyman603cc002017-08-28 18:17:58 -0500392
Brandon Wymandd61be42017-11-07 18:38:54 -0600393 using metadata = org::open_power::Witherspoon::Fault::
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500394 PowerSupplyOutputOvercurrent;
Brandon Wyman603cc002017-08-28 18:17:58 -0500395
Brandon Wymandd61be42017-11-07 18:38:54 -0600396 report<PowerSupplyOutputOvercurrent>(
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500397 metadata::RAW_STATUS(nv.get().c_str()),
398 metadata::CALLOUT_INVENTORY_PATH(inventoryPath.c_str()));
Brandon Wymandd61be42017-11-07 18:38:54 -0600399
400 faultFound = true;
401 }
Brandon Wyman603cc002017-08-28 18:17:58 -0500402 }
403}
404
Brandon Wymanab05c072017-08-30 18:26:41 -0500405void PowerSupply::checkOutputOvervoltageFault(const uint16_t statusWord)
406{
Lei YUab093322019-10-09 16:43:22 +0800407 using namespace phosphor::pmbus;
Brandon Wymanab05c072017-08-30 18:26:41 -0500408
Brandon Wyman2ab319b2017-11-08 17:34:59 -0600409 if (outputOVFault < FAULT_COUNT)
Brandon Wymanab05c072017-08-30 18:26:41 -0500410 {
Brandon Wyman2ab319b2017-11-08 17:34:59 -0600411 // Check for an output overvoltage fault.
412 if (statusWord & status_word::VOUT_OV_FAULT)
413 {
414 outputOVFault++;
415 }
416 else
417 {
418 if (outputOVFault > 0)
419 {
420 outputOVFault = 0;
421 }
422 }
Brandon Wymanab05c072017-08-30 18:26:41 -0500423
Brandon Wymane2fc7aa2017-11-13 17:37:10 -0600424 if (!faultFound && (outputOVFault >= FAULT_COUNT))
Brandon Wyman2ab319b2017-11-08 17:34:59 -0600425 {
426 util::NamesValues nv;
427 nv.add("STATUS_WORD", statusWord);
428 captureCmd(nv, STATUS_INPUT, Type::Debug);
429 auto status0Vout = pmbusIntf.insertPageNum(STATUS_VOUT, 0);
430 captureCmd(nv, status0Vout, Type::Debug);
431 captureCmd(nv, STATUS_IOUT, Type::Debug);
432 captureCmd(nv, STATUS_MFR, Type::Debug);
Brandon Wymanab05c072017-08-30 18:26:41 -0500433
Brandon Wyman2ab319b2017-11-08 17:34:59 -0600434 using metadata = org::open_power::Witherspoon::Fault::
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500435 PowerSupplyOutputOvervoltage;
Brandon Wymanab05c072017-08-30 18:26:41 -0500436
Brandon Wyman2ab319b2017-11-08 17:34:59 -0600437 report<PowerSupplyOutputOvervoltage>(
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500438 metadata::RAW_STATUS(nv.get().c_str()),
439 metadata::CALLOUT_INVENTORY_PATH(inventoryPath.c_str()));
Brandon Wyman2ab319b2017-11-08 17:34:59 -0600440
441 faultFound = true;
442 }
Brandon Wymanab05c072017-08-30 18:26:41 -0500443 }
444}
445
Brandon Wyman12661f12017-08-31 15:28:21 -0500446void PowerSupply::checkFanFault(const uint16_t statusWord)
447{
Lei YUab093322019-10-09 16:43:22 +0800448 using namespace phosphor::pmbus;
Brandon Wyman12661f12017-08-31 15:28:21 -0500449
Brandon Wymanba255532017-11-08 17:44:10 -0600450 if (fanFault < FAULT_COUNT)
Brandon Wyman12661f12017-08-31 15:28:21 -0500451 {
Brandon Wymanba255532017-11-08 17:44:10 -0600452 // Check for a fan fault or warning condition
453 if (statusWord & status_word::FAN_FAULT)
454 {
455 fanFault++;
456 }
457 else
458 {
459 if (fanFault > 0)
460 {
461 fanFault = 0;
462 }
463 }
Brandon Wyman12661f12017-08-31 15:28:21 -0500464
Brandon Wymane2fc7aa2017-11-13 17:37:10 -0600465 if (!faultFound && (fanFault >= FAULT_COUNT))
Brandon Wymanba255532017-11-08 17:44:10 -0600466 {
467 util::NamesValues nv;
468 nv.add("STATUS_WORD", statusWord);
469 captureCmd(nv, STATUS_MFR, Type::Debug);
470 captureCmd(nv, STATUS_TEMPERATURE, Type::Debug);
471 captureCmd(nv, STATUS_FANS_1_2, Type::Debug);
Brandon Wyman12661f12017-08-31 15:28:21 -0500472
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500473 using metadata =
474 org::open_power::Witherspoon::Fault::PowerSupplyFanFault;
Brandon Wyman12661f12017-08-31 15:28:21 -0500475
Brandon Wymanba255532017-11-08 17:44:10 -0600476 report<PowerSupplyFanFault>(
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500477 metadata::RAW_STATUS(nv.get().c_str()),
478 metadata::CALLOUT_INVENTORY_PATH(inventoryPath.c_str()));
Brandon Wymanba255532017-11-08 17:44:10 -0600479
480 faultFound = true;
481 }
Brandon Wyman12661f12017-08-31 15:28:21 -0500482 }
483}
484
Brandon Wyman875b3632017-09-13 18:46:03 -0500485void PowerSupply::checkTemperatureFault(const uint16_t statusWord)
486{
Lei YUab093322019-10-09 16:43:22 +0800487 using namespace phosphor::pmbus;
Brandon Wyman875b3632017-09-13 18:46:03 -0500488
489 // Due to how the PMBus core device driver sends a clear faults command
490 // the bit in STATUS_WORD will likely be cleared when we attempt to examine
491 // it for a Thermal Fault or Warning. So, check the STATUS_WORD and the
492 // STATUS_TEMPERATURE bits. If either indicates a fault, proceed with
493 // logging the over-temperature condition.
494 std::uint8_t statusTemperature = 0;
495 statusTemperature = pmbusIntf.read(STATUS_TEMPERATURE, Type::Debug);
Brandon Wyman50044ea2017-11-08 17:58:56 -0600496 if (temperatureFault < FAULT_COUNT)
Brandon Wyman875b3632017-09-13 18:46:03 -0500497 {
Brandon Wyman50044ea2017-11-08 17:58:56 -0600498 if ((statusWord & status_word::TEMPERATURE_FAULT_WARN) ||
499 (statusTemperature & status_temperature::OT_FAULT))
500 {
501 temperatureFault++;
502 }
503 else
504 {
505 if (temperatureFault > 0)
506 {
507 temperatureFault = 0;
508 }
509 }
Brandon Wyman875b3632017-09-13 18:46:03 -0500510
Brandon Wymane2fc7aa2017-11-13 17:37:10 -0600511 if (!faultFound && (temperatureFault >= FAULT_COUNT))
Brandon Wyman50044ea2017-11-08 17:58:56 -0600512 {
513 // The power supply has had an over-temperature condition.
514 // This may not result in a shutdown if experienced for a short
515 // duration.
516 // This should not occur under normal conditions.
517 // The power supply may be faulty, or the paired supply may be
518 // putting out less current.
519 // Capture command responses with potentially relevant information,
520 // and call out the power supply reporting the condition.
521 util::NamesValues nv;
522 nv.add("STATUS_WORD", statusWord);
523 captureCmd(nv, STATUS_MFR, Type::Debug);
524 captureCmd(nv, STATUS_IOUT, Type::Debug);
525 nv.add("STATUS_TEMPERATURE", statusTemperature);
526 captureCmd(nv, STATUS_FANS_1_2, Type::Debug);
Brandon Wyman875b3632017-09-13 18:46:03 -0500527
Brandon Wyman50044ea2017-11-08 17:58:56 -0600528 using metadata = org::open_power::Witherspoon::Fault::
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500529 PowerSupplyTemperatureFault;
Brandon Wyman875b3632017-09-13 18:46:03 -0500530
Brandon Wyman50044ea2017-11-08 17:58:56 -0600531 report<PowerSupplyTemperatureFault>(
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500532 metadata::RAW_STATUS(nv.get().c_str()),
533 metadata::CALLOUT_INVENTORY_PATH(inventoryPath.c_str()));
Brandon Wyman50044ea2017-11-08 17:58:56 -0600534
535 faultFound = true;
536 }
Brandon Wyman875b3632017-09-13 18:46:03 -0500537 }
538}
539
Brandon Wyman1db9a9e2017-07-26 18:50:22 -0500540void PowerSupply::clearFaults()
541{
Brandon Wymane4af9802017-11-13 15:58:33 -0600542 readFail = 0;
Brandon Wyman6ccce0b2017-10-26 15:13:10 -0500543 readFailLogged = false;
Brandon Wymana3c675c2017-11-14 14:54:54 -0600544 inputFault = 0;
Brandon Wyman6ccce0b2017-10-26 15:13:10 -0500545 powerOnFault = 0;
Brandon Wymandd61be42017-11-07 18:38:54 -0600546 outputOCFault = 0;
Brandon Wyman2ab319b2017-11-08 17:34:59 -0600547 outputOVFault = 0;
Brandon Wymanba255532017-11-08 17:44:10 -0600548 fanFault = 0;
Brandon Wyman50044ea2017-11-08 17:58:56 -0600549 temperatureFault = 0;
Brandon Wyman3343e822017-11-03 16:54:11 -0500550 faultFound = false;
Brandon Wyman6ccce0b2017-10-26 15:13:10 -0500551
Brandon Wyman1db9a9e2017-07-26 18:50:22 -0500552 return;
553}
554
Brandon Wyman43ce2082017-11-30 17:24:01 -0600555void PowerSupply::resolveError(const std::string& callout,
556 const std::string& message)
557{
Brandon Wyman01741f12017-12-01 17:22:08 -0600558 using EndpointList = std::vector<std::string>;
559
560 try
561 {
562 auto path = callout + "/fault";
563 // Get the service name from the mapper for the fault callout
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500564 auto service = util::getService(path, ASSOCIATION_IFACE, bus);
Brandon Wyman01741f12017-12-01 17:22:08 -0600565
566 // Use getProperty utility function to get log entries (endpoints)
567 EndpointList logEntries;
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500568 util::getProperty(ASSOCIATION_IFACE, ENDPOINTS_PROP, path, service, bus,
569 logEntries);
Brandon Wyman01741f12017-12-01 17:22:08 -0600570
571 // It is possible that all such entries for this callout have since
572 // been deleted.
573 if (logEntries.empty())
574 {
575 return;
576 }
577
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500578 auto logEntryService =
579 util::getService(logEntries[0], LOGGING_IFACE, bus);
Brandon Wyman01741f12017-12-01 17:22:08 -0600580 if (logEntryService.empty())
581 {
582 return;
583 }
584
585 // go through each log entry that matches this callout path
586 std::string logMessage;
587 for (const auto& logEntry : logEntries)
588 {
589 // Check to see if this logEntry has a message that matches.
Matt Spinler589e8722018-01-04 15:24:49 -0600590 util::getProperty(LOGGING_IFACE, MESSAGE_PROP, logEntry,
Brandon Wyman01741f12017-12-01 17:22:08 -0600591 logEntryService, bus, logMessage);
592
593 if (message == logMessage)
594 {
595 // Log entry matches call out and message, set Resolved to true
596 bool resolved = true;
Matt Spinler589e8722018-01-04 15:24:49 -0600597 util::setProperty(LOGGING_IFACE, RESOLVED_PROP, logEntry,
Brandon Wyman01741f12017-12-01 17:22:08 -0600598 logEntryService, bus, resolved);
599 }
Brandon Wyman01741f12017-12-01 17:22:08 -0600600 }
Brandon Wyman01741f12017-12-01 17:22:08 -0600601 }
602 catch (std::exception& e)
603 {
604 log<level::INFO>("Failed to resolve error",
605 entry("CALLOUT=%s", callout.c_str()),
Matt Spinler0d09f292018-01-22 14:51:26 -0600606 entry("ERROR=%s", message.c_str()));
Brandon Wyman01741f12017-12-01 17:22:08 -0600607 }
Brandon Wyman43ce2082017-11-30 17:24:01 -0600608}
609
Matt Spinler234ce0d2018-01-04 15:06:57 -0600610void PowerSupply::updateInventory()
611{
Lei YUab093322019-10-09 16:43:22 +0800612 using namespace phosphor::pmbus;
Matt Spinler018a7bc2018-01-04 15:36:41 -0600613 using namespace sdbusplus::message;
614
Matt Spinler018a7bc2018-01-04 15:36:41 -0600615 // Build the object map and send it to the inventory
616 using Properties = std::map<std::string, variant<std::string>>;
617 using Interfaces = std::map<std::string, Properties>;
618 using Object = std::map<object_path, Interfaces>;
619 Properties assetProps;
620 Properties versionProps;
621 Interfaces interfaces;
622 Object object;
623
George Liu690e7802019-08-23 11:04:01 +0800624 // If any of these accesses fail, the fields will just be
625 // blank in the inventory. Leave logging ReadFailure errors
626 // to analyze() as it runs continuously and will most
627 // likely hit and threshold them first anyway. The
628 // readString() function will do the tracing of the failing
629 // path so this code doesn't need to.
630 for (const auto& fru : fruJson.at("fruConfigs"))
631 {
632 if (fru.at("interface") == ASSET_IFACE)
633 {
634 try
635 {
636 assetProps.emplace(
637 fru.at("propertyName"),
638 present ? pmbusIntf.readString(fru.at("fileName"),
639 inventoryPMBusAccessType)
640 : "");
641 }
642 catch (ReadFailure& e)
643 {
644 }
645 }
646 else if (fru.at("interface") == VERSION_IFACE)
647 {
648 try
649 {
650 versionProps.emplace(
651 fru.at("propertyName"),
652 present ? pmbusIntf.readString(fru.at("fileName"),
653 inventoryPMBusAccessType)
654 : "");
655 }
656 catch (const std::exception& e)
657 {
658 }
659 }
660 }
Matt Spinler018a7bc2018-01-04 15:36:41 -0600661
George Liu690e7802019-08-23 11:04:01 +0800662 interfaces.emplace(ASSET_IFACE, std::move(assetProps));
Matt Spinler018a7bc2018-01-04 15:36:41 -0600663 interfaces.emplace(VERSION_IFACE, std::move(versionProps));
664
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500665 // For Notify(), just send the relative path of the inventory
666 // object so remove the INVENTORY_OBJ_PATH prefix
Matt Spinler018a7bc2018-01-04 15:36:41 -0600667 auto path = inventoryPath.substr(strlen(INVENTORY_OBJ_PATH));
668
669 object.emplace(path, std::move(interfaces));
670
671 try
672 {
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500673 auto service =
674 util::getService(INVENTORY_OBJ_PATH, INVENTORY_MGR_IFACE, bus);
Matt Spinler018a7bc2018-01-04 15:36:41 -0600675
676 if (service.empty())
677 {
678 log<level::ERR>("Unable to get inventory manager service");
679 return;
680 }
681
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500682 auto method = bus.new_method_call(service.c_str(), INVENTORY_OBJ_PATH,
683 INVENTORY_MGR_IFACE, "Notify");
Matt Spinler018a7bc2018-01-04 15:36:41 -0600684
685 method.append(std::move(object));
686
687 auto reply = bus.call(method);
Matt Spinler018a7bc2018-01-04 15:36:41 -0600688
689 // TODO: openbmc/openbmc#2756
690 // Calling Notify() with an enumerated property crashes inventory
691 // manager, so let it default to Unknown and now set it to the
692 // right value.
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500693 auto purpose =
694 version::convertForMessage(version::Version::VersionPurpose::Other);
Matt Spinler018a7bc2018-01-04 15:36:41 -0600695
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500696 util::setProperty(VERSION_IFACE, VERSION_PURPOSE_PROP, inventoryPath,
697 service, bus, purpose);
Matt Spinler018a7bc2018-01-04 15:36:41 -0600698 }
699 catch (std::exception& e)
700 {
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500701 log<level::ERR>(e.what(), entry("PATH=%s", inventoryPath.c_str()));
Matt Spinler018a7bc2018-01-04 15:36:41 -0600702 }
Matt Spinler234ce0d2018-01-04 15:06:57 -0600703}
704
Matt Spinlerd734e652018-01-18 14:31:15 -0600705void PowerSupply::syncHistory()
706{
Lei YUab093322019-10-09 16:43:22 +0800707 using namespace phosphor::gpio;
Matt Spinlerd734e652018-01-18 14:31:15 -0600708
709 if (syncGPIODevPath.empty())
710 {
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500711 // Sync not implemented
Matt Spinlerd734e652018-01-18 14:31:15 -0600712 return;
713 }
714
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500715 GPIO gpio{syncGPIODevPath, static_cast<gpioNum_t>(syncGPIONumber),
Matt Spinlerd734e652018-01-18 14:31:15 -0600716 Direction::output};
717
718 try
719 {
720 gpio.set(Value::low);
721
722 std::this_thread::sleep_for(std::chrono::milliseconds{5});
723
724 gpio.set(Value::high);
725
726 recordManager->clear();
727 }
728 catch (std::exception& e)
729 {
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500730 // Do nothing. There would already be a journal entry.
Matt Spinlerd734e652018-01-18 14:31:15 -0600731 }
732}
733
Matt Spinler82384142018-01-18 14:15:03 -0600734void PowerSupply::enableHistory(const std::string& objectPath,
735 size_t numRecords,
736 const std::string& syncGPIOPath,
737 size_t syncGPIONum)
738{
739 historyObjectPath = objectPath;
740 syncGPIODevPath = syncGPIOPath;
741 syncGPIONumber = syncGPIONum;
742
743 recordManager = std::make_unique<history::RecordManager>(numRecords);
744
745 auto avgPath = historyObjectPath + '/' + history::Average::name;
746 auto maxPath = historyObjectPath + '/' + history::Maximum::name;
747
748 average = std::make_unique<history::Average>(bus, avgPath);
749
750 maximum = std::make_unique<history::Maximum>(bus, maxPath);
751}
752
Matt Spinlereb169fd2018-01-18 14:19:08 -0600753void PowerSupply::updateHistory()
754{
755 if (!recordManager)
756 {
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500757 // Not enabled
Matt Spinlereb169fd2018-01-18 14:19:08 -0600758 return;
759 }
760
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500761 // Read just the most recent average/max record
762 auto data =
763 pmbusIntf.readBinary(INPUT_HISTORY, pmbus::Type::HwmonDeviceDebug,
764 history::RecordManager::RAW_RECORD_SIZE);
Matt Spinlereb169fd2018-01-18 14:19:08 -0600765
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500766 // Update D-Bus only if something changed (a new record ID, or cleared out)
Matt Spinlereb169fd2018-01-18 14:19:08 -0600767 auto changed = recordManager->add(data);
768 if (changed)
769 {
770 average->values(std::move(recordManager->getAverageRecords()));
771 maximum->values(std::move(recordManager->getMaximumRecords()));
772 }
773}
774
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500775} // namespace psu
776} // namespace power
Lei YUab093322019-10-09 16:43:22 +0800777} // namespace phosphor