blob: 0a5c9cce324af51e564d3f6175b8218e55688acb [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::pmbus;
125 using namespace phosphor::power::util;
George Liu690e7802019-08-23 11:04:01 +0800126 fruJson = loadJSONFromFile(PSU_JSON_PATH);
127 if (fruJson == nullptr)
128 {
129 log<level::ERR>("InternalFailure when parsing the JSON file");
130 return;
131 }
132
133 auto type = fruJson.at("inventoryPMBusAccessType");
134
135 if (type == "Hwmon")
136 {
137 inventoryPMBusAccessType = Type::Hwmon;
138 }
139 else if (type == "DeviceDebug")
140 {
141 inventoryPMBusAccessType = Type::DeviceDebug;
142 }
143 else if (type == "Debug")
144 {
145 inventoryPMBusAccessType = Type::Debug;
146 }
147 else if (type == "HwmonDeviceDebug")
148 {
149 inventoryPMBusAccessType = Type::HwmonDeviceDebug;
150 }
151 else
152 {
153 inventoryPMBusAccessType = Type::Base;
154 }
155}
156
Brandon Wymana1e96342017-09-25 16:47:44 -0500157void PowerSupply::captureCmd(util::NamesValues& nv, const std::string& cmd,
Lei YUab093322019-10-09 16:43:22 +0800158 phosphor::pmbus::Type type)
Brandon Wymana1e96342017-09-25 16:47:44 -0500159{
160 if (pmbusIntf.exists(cmd, type))
161 {
162 try
163 {
164 auto val = pmbusIntf.read(cmd, type);
165 nv.add(cmd, val);
166 }
167 catch (std::exception& e)
168 {
Joseph Reynolds5b485962018-05-17 16:05:06 -0500169 log<level::INFO>("Unable to capture metadata",
170 entry("CMD=%s", cmd.c_str()));
Brandon Wymana1e96342017-09-25 16:47:44 -0500171 }
172 }
173}
Brandon Wyman431fbe42017-08-18 16:22:09 -0500174
Brandon Wyman1db9a9e2017-07-26 18:50:22 -0500175void PowerSupply::analyze()
176{
Lei YUab093322019-10-09 16:43:22 +0800177 using namespace phosphor::pmbus;
Brandon Wyman442035f2017-08-08 15:58:45 -0500178
179 try
180 {
Brandon Wyman10295542017-08-09 18:20:44 -0500181 if (present)
Brandon Wyman442035f2017-08-08 15:58:45 -0500182 {
Brandon Wyman764c7972017-08-22 17:05:36 -0500183 std::uint16_t statusWord = 0;
Brandon Wyman764c7972017-08-22 17:05:36 -0500184
185 // Read the 2 byte STATUS_WORD value to check for faults.
186 statusWord = pmbusIntf.read(STATUS_WORD, Type::Debug);
Brandon Wymane4af9802017-11-13 15:58:33 -0600187 readFail = 0;
Brandon Wyman764c7972017-08-22 17:05:36 -0500188
Brandon Wyman603cc002017-08-28 18:17:58 -0500189 checkInputFault(statusWord);
Brandon Wyman764c7972017-08-22 17:05:36 -0500190
Brandon Wyman654eb6e2018-02-14 14:55:47 -0600191 if (powerOn && (inputFault == 0) && !faultFound)
Brandon Wyman764c7972017-08-22 17:05:36 -0500192 {
Brandon Wyman12661f12017-08-31 15:28:21 -0500193 checkFanFault(statusWord);
Brandon Wyman875b3632017-09-13 18:46:03 -0500194 checkTemperatureFault(statusWord);
Brandon Wymancfa032b2017-09-25 17:37:50 -0500195 checkOutputOvervoltageFault(statusWord);
196 checkCurrentOutOverCurrentFault(statusWord);
197 checkPGOrUnitOffFault(statusWord);
Brandon Wyman442035f2017-08-08 15:58:45 -0500198 }
Matt Spinlereb169fd2018-01-18 14:19:08 -0600199
200 updateHistory();
Brandon Wyman442035f2017-08-08 15:58:45 -0500201 }
202 }
203 catch (ReadFailure& e)
204 {
Brandon Wymane4af9802017-11-13 15:58:33 -0600205 if (readFail < FAULT_COUNT)
206 {
207 readFail++;
208 }
209
210 if (!readFailLogged && readFail >= FAULT_COUNT)
Brandon Wyman442035f2017-08-08 15:58:45 -0500211 {
212 commit<ReadFailure>();
213 readFailLogged = true;
Brandon Wyman442035f2017-08-08 15:58:45 -0500214 }
215 }
216
Brandon Wyman1db9a9e2017-07-26 18:50:22 -0500217 return;
218}
219
Brandon Wyman10295542017-08-09 18:20:44 -0500220void PowerSupply::inventoryChanged(sdbusplus::message::message& msg)
221{
222 std::string msgSensor;
223 std::map<std::string, sdbusplus::message::variant<uint32_t, bool>> msgData;
224 msg.read(msgSensor, msgData);
225
226 // Check if it was the Present property that changed.
227 auto valPropMap = msgData.find(PRESENT_PROP);
228 if (valPropMap != msgData.end())
229 {
Brandon Wyman2ef48cf2017-11-21 15:43:54 -0600230 if (sdbusplus::message::variant_ns::get<bool>(valPropMap->second))
Brandon Wyman10295542017-08-09 18:20:44 -0500231 {
Brandon Wyman6ccce0b2017-10-26 15:13:10 -0500232 clearFaults();
William A. Kennington III1a0c9172018-10-18 17:57:49 -0700233 presentTimer.restartOnce(presentInterval);
Brandon Wyman590fc282017-11-01 18:22:25 -0500234 }
235 else
236 {
Brandon Wyman2ef48cf2017-11-21 15:43:54 -0600237 present = false;
William A. Kennington III1a0c9172018-10-18 17:57:49 -0700238 presentTimer.setEnabled(false);
Matt Spinler234ce0d2018-01-04 15:06:57 -0600239
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500240 // Clear out the now outdated inventory properties
Matt Spinler234ce0d2018-01-04 15:06:57 -0600241 updateInventory();
Brandon Wyman10295542017-08-09 18:20:44 -0500242 }
243 }
244
245 return;
246}
247
248void PowerSupply::updatePresence()
249{
250 // Use getProperty utility function to get presence status.
Brandon Wyman10295542017-08-09 18:20:44 -0500251 std::string service = "xyz.openbmc_project.Inventory.Manager";
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500252 util::getProperty(INVENTORY_IFACE, PRESENT_PROP, inventoryPath, service,
253 bus, this->present);
Brandon Wyman10295542017-08-09 18:20:44 -0500254}
255
Brandon Wyman431fbe42017-08-18 16:22:09 -0500256void PowerSupply::powerStateChanged(sdbusplus::message::message& msg)
257{
258 int32_t state = 0;
259 std::string msgSensor;
William A. Kennington IIIac9d5c32018-11-12 14:58:39 -0800260 std::map<std::string, sdbusplus::message::variant<int32_t>> msgData;
Brandon Wyman431fbe42017-08-18 16:22:09 -0500261 msg.read(msgSensor, msgData);
262
263 // Check if it was the Present property that changed.
264 auto valPropMap = msgData.find("state");
265 if (valPropMap != msgData.end())
266 {
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500267 state =
268 sdbusplus::message::variant_ns::get<int32_t>(valPropMap->second);
Brandon Wyman431fbe42017-08-18 16:22:09 -0500269
270 // Power is on when state=1. Set the fault logged variables to false
271 // and start the power on timer when the state changes to 1.
272 if (state)
273 {
Brandon Wyman6ccce0b2017-10-26 15:13:10 -0500274 clearFaults();
William A. Kennington III1a0c9172018-10-18 17:57:49 -0700275 powerOnTimer.restartOnce(powerOnInterval);
Brandon Wyman431fbe42017-08-18 16:22:09 -0500276 }
277 else
278 {
William A. Kennington III1a0c9172018-10-18 17:57:49 -0700279 powerOnTimer.setEnabled(false);
Brandon Wyman431fbe42017-08-18 16:22:09 -0500280 powerOn = false;
281 }
282 }
Brandon Wyman431fbe42017-08-18 16:22:09 -0500283}
284
285void PowerSupply::updatePowerState()
286{
287 // When state = 1, system is powered on
288 int32_t state = 0;
289
290 try
291 {
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500292 auto service = util::getService(POWER_OBJ_PATH, POWER_IFACE, bus);
Brandon Wyman431fbe42017-08-18 16:22:09 -0500293
294 // Use getProperty utility function to get power state.
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500295 util::getProperty<int32_t>(POWER_IFACE, "state", POWER_OBJ_PATH,
296 service, bus, state);
Brandon Wyman431fbe42017-08-18 16:22:09 -0500297
298 if (state)
299 {
300 powerOn = true;
301 }
302 else
303 {
304 powerOn = false;
305 }
306 }
307 catch (std::exception& e)
308 {
309 log<level::INFO>("Failed to get power state. Assuming it is off.");
310 powerOn = false;
311 }
Brandon Wyman431fbe42017-08-18 16:22:09 -0500312}
313
Brandon Wyman603cc002017-08-28 18:17:58 -0500314void PowerSupply::checkInputFault(const uint16_t statusWord)
315{
Lei YUab093322019-10-09 16:43:22 +0800316 using namespace phosphor::pmbus;
Brandon Wyman603cc002017-08-28 18:17:58 -0500317
Brandon Wymana3c675c2017-11-14 14:54:54 -0600318 if ((inputFault < FAULT_COUNT) &&
319 ((statusWord & status_word::INPUT_FAULT_WARN) ||
320 (statusWord & status_word::VIN_UV_FAULT)))
Brandon Wyman603cc002017-08-28 18:17:58 -0500321 {
Brandon Wymanad708242018-01-19 17:28:35 -0600322 if (inputFault == 0)
323 {
324 log<level::INFO>("INPUT or VIN_UV fault",
325 entry("STATUS_WORD=0x%04X", statusWord));
326 }
327
Brandon Wymana3c675c2017-11-14 14:54:54 -0600328 inputFault++;
Brandon Wyman603cc002017-08-28 18:17:58 -0500329 }
330 else
331 {
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500332 if ((inputFault > 0) && !(statusWord & status_word::INPUT_FAULT_WARN) &&
Brandon Wymand20686a2017-11-01 17:45:23 -0500333 !(statusWord & status_word::VIN_UV_FAULT))
Brandon Wyman603cc002017-08-28 18:17:58 -0500334 {
Brandon Wymana3c675c2017-11-14 14:54:54 -0600335 inputFault = 0;
Brandon Wyman3343e822017-11-03 16:54:11 -0500336 faultFound = false;
Brandon Wyman7502f6c2018-02-12 20:20:39 -0600337 // When an input fault occurs, the power supply cannot be on.
338 // However, the check for the case where the power supply should be
339 // on will stop when there is a fault found.
340 // Clear the powerOnFault when the inputFault is cleared to reset
341 // the powerOnFault de-glitching.
342 powerOnFault = 0;
Brandon Wyman69591bd2017-11-01 18:07:23 -0500343
Brandon Wyman603cc002017-08-28 18:17:58 -0500344 log<level::INFO>("INPUT_FAULT_WARN cleared",
Brandon Wymana3c675c2017-11-14 14:54:54 -0600345 entry("POWERSUPPLY=%s", inventoryPath.c_str()));
Brandon Wyman69591bd2017-11-01 18:07:23 -0500346
Brandon Wyman08b05712017-11-30 17:53:56 -0600347 resolveError(inventoryPath,
348 std::string(PowerSupplyInputFault::errName));
349
Brandon Wyman69591bd2017-11-01 18:07:23 -0500350 if (powerOn)
351 {
352 // The power supply will not be immediately powered on after
353 // the input power is restored.
354 powerOn = false;
355 // Start up the timer that will set the state to indicate we
356 // are ready for the powered on fault checks.
William A. Kennington III1a0c9172018-10-18 17:57:49 -0700357 powerOnTimer.restartOnce(powerOnInterval);
Brandon Wyman69591bd2017-11-01 18:07:23 -0500358 }
Brandon Wyman603cc002017-08-28 18:17:58 -0500359 }
360 }
Brandon Wymana3c675c2017-11-14 14:54:54 -0600361
362 if (!faultFound && (inputFault >= FAULT_COUNT))
363 {
Brandon Wymanad708242018-01-19 17:28:35 -0600364 // If the power is on, report the fault in an error log entry.
365 if (powerOn)
366 {
367 util::NamesValues nv;
368 nv.add("STATUS_WORD", statusWord);
369 captureCmd(nv, STATUS_INPUT, Type::Debug);
Brandon Wymana3c675c2017-11-14 14:54:54 -0600370
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500371 using metadata =
372 org::open_power::Witherspoon::Fault::PowerSupplyInputFault;
Brandon Wymana3c675c2017-11-14 14:54:54 -0600373
Brandon Wymanad708242018-01-19 17:28:35 -0600374 report<PowerSupplyInputFault>(
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500375 metadata::RAW_STATUS(nv.get().c_str()),
376 metadata::CALLOUT_INVENTORY_PATH(inventoryPath.c_str()));
Brandon Wymanad708242018-01-19 17:28:35 -0600377
378 faultFound = true;
379 }
Brandon Wymana3c675c2017-11-14 14:54:54 -0600380 }
Brandon Wyman603cc002017-08-28 18:17:58 -0500381}
382
383void PowerSupply::checkPGOrUnitOffFault(const uint16_t statusWord)
384{
Lei YUab093322019-10-09 16:43:22 +0800385 using namespace phosphor::pmbus;
Brandon Wyman603cc002017-08-28 18:17:58 -0500386
Brandon Wyman593d24f2017-10-13 18:15:23 -0500387 if (powerOnFault < FAULT_COUNT)
Brandon Wyman603cc002017-08-28 18:17:58 -0500388 {
Brandon Wyman593d24f2017-10-13 18:15:23 -0500389 // Check PG# and UNIT_IS_OFF
390 if ((statusWord & status_word::POWER_GOOD_NEGATED) ||
391 (statusWord & status_word::UNIT_IS_OFF))
392 {
393 log<level::INFO>("PGOOD or UNIT_IS_OFF bit bad",
394 entry("STATUS_WORD=0x%04X", statusWord));
395 powerOnFault++;
396 }
397 else
398 {
399 if (powerOnFault > 0)
400 {
401 log<level::INFO>("PGOOD and UNIT_IS_OFF bits good");
402 powerOnFault = 0;
403 }
404 }
Brandon Wyman603cc002017-08-28 18:17:58 -0500405
Brandon Wymane2fc7aa2017-11-13 17:37:10 -0600406 if (!faultFound && (powerOnFault >= FAULT_COUNT))
Brandon Wyman593d24f2017-10-13 18:15:23 -0500407 {
Brandon Wyman3343e822017-11-03 16:54:11 -0500408 faultFound = true;
409
Brandon Wyman593d24f2017-10-13 18:15:23 -0500410 util::NamesValues nv;
411 nv.add("STATUS_WORD", statusWord);
412 captureCmd(nv, STATUS_INPUT, Type::Debug);
413 auto status0Vout = pmbusIntf.insertPageNum(STATUS_VOUT, 0);
414 captureCmd(nv, status0Vout, Type::Debug);
415 captureCmd(nv, STATUS_IOUT, Type::Debug);
416 captureCmd(nv, STATUS_MFR, Type::Debug);
Brandon Wyman603cc002017-08-28 18:17:58 -0500417
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500418 using metadata =
419 org::open_power::Witherspoon::Fault::PowerSupplyShouldBeOn;
Brandon Wyman603cc002017-08-28 18:17:58 -0500420
Brandon Wyman593d24f2017-10-13 18:15:23 -0500421 // A power supply is OFF (or pgood low) but should be on.
422 report<PowerSupplyShouldBeOn>(
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500423 metadata::RAW_STATUS(nv.get().c_str()),
424 metadata::CALLOUT_INVENTORY_PATH(inventoryPath.c_str()));
Brandon Wyman593d24f2017-10-13 18:15:23 -0500425 }
Brandon Wyman603cc002017-08-28 18:17:58 -0500426 }
Brandon Wyman603cc002017-08-28 18:17:58 -0500427}
428
429void PowerSupply::checkCurrentOutOverCurrentFault(const uint16_t statusWord)
430{
Lei YUab093322019-10-09 16:43:22 +0800431 using namespace phosphor::pmbus;
Brandon Wyman603cc002017-08-28 18:17:58 -0500432
Brandon Wymandd61be42017-11-07 18:38:54 -0600433 if (outputOCFault < FAULT_COUNT)
Brandon Wyman603cc002017-08-28 18:17:58 -0500434 {
Brandon Wymandd61be42017-11-07 18:38:54 -0600435 // Check for an output overcurrent fault.
436 if ((statusWord & status_word::IOUT_OC_FAULT))
437 {
438 outputOCFault++;
439 }
440 else
441 {
442 if (outputOCFault > 0)
443 {
444 outputOCFault = 0;
445 }
446 }
Brandon Wyman603cc002017-08-28 18:17:58 -0500447
Brandon Wymane2fc7aa2017-11-13 17:37:10 -0600448 if (!faultFound && (outputOCFault >= FAULT_COUNT))
Brandon Wymandd61be42017-11-07 18:38:54 -0600449 {
450 util::NamesValues nv;
451 nv.add("STATUS_WORD", statusWord);
452 captureCmd(nv, STATUS_INPUT, Type::Debug);
453 auto status0Vout = pmbusIntf.insertPageNum(STATUS_VOUT, 0);
454 captureCmd(nv, status0Vout, Type::Debug);
455 captureCmd(nv, STATUS_IOUT, Type::Debug);
456 captureCmd(nv, STATUS_MFR, Type::Debug);
Brandon Wyman603cc002017-08-28 18:17:58 -0500457
Brandon Wymandd61be42017-11-07 18:38:54 -0600458 using metadata = org::open_power::Witherspoon::Fault::
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500459 PowerSupplyOutputOvercurrent;
Brandon Wyman603cc002017-08-28 18:17:58 -0500460
Brandon Wymandd61be42017-11-07 18:38:54 -0600461 report<PowerSupplyOutputOvercurrent>(
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500462 metadata::RAW_STATUS(nv.get().c_str()),
463 metadata::CALLOUT_INVENTORY_PATH(inventoryPath.c_str()));
Brandon Wymandd61be42017-11-07 18:38:54 -0600464
465 faultFound = true;
466 }
Brandon Wyman603cc002017-08-28 18:17:58 -0500467 }
468}
469
Brandon Wymanab05c072017-08-30 18:26:41 -0500470void PowerSupply::checkOutputOvervoltageFault(const uint16_t statusWord)
471{
Lei YUab093322019-10-09 16:43:22 +0800472 using namespace phosphor::pmbus;
Brandon Wymanab05c072017-08-30 18:26:41 -0500473
Brandon Wyman2ab319b2017-11-08 17:34:59 -0600474 if (outputOVFault < FAULT_COUNT)
Brandon Wymanab05c072017-08-30 18:26:41 -0500475 {
Brandon Wyman2ab319b2017-11-08 17:34:59 -0600476 // Check for an output overvoltage fault.
477 if (statusWord & status_word::VOUT_OV_FAULT)
478 {
479 outputOVFault++;
480 }
481 else
482 {
483 if (outputOVFault > 0)
484 {
485 outputOVFault = 0;
486 }
487 }
Brandon Wymanab05c072017-08-30 18:26:41 -0500488
Brandon Wymane2fc7aa2017-11-13 17:37:10 -0600489 if (!faultFound && (outputOVFault >= FAULT_COUNT))
Brandon Wyman2ab319b2017-11-08 17:34:59 -0600490 {
491 util::NamesValues nv;
492 nv.add("STATUS_WORD", statusWord);
493 captureCmd(nv, STATUS_INPUT, Type::Debug);
494 auto status0Vout = pmbusIntf.insertPageNum(STATUS_VOUT, 0);
495 captureCmd(nv, status0Vout, Type::Debug);
496 captureCmd(nv, STATUS_IOUT, Type::Debug);
497 captureCmd(nv, STATUS_MFR, Type::Debug);
Brandon Wymanab05c072017-08-30 18:26:41 -0500498
Brandon Wyman2ab319b2017-11-08 17:34:59 -0600499 using metadata = org::open_power::Witherspoon::Fault::
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500500 PowerSupplyOutputOvervoltage;
Brandon Wymanab05c072017-08-30 18:26:41 -0500501
Brandon Wyman2ab319b2017-11-08 17:34:59 -0600502 report<PowerSupplyOutputOvervoltage>(
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500503 metadata::RAW_STATUS(nv.get().c_str()),
504 metadata::CALLOUT_INVENTORY_PATH(inventoryPath.c_str()));
Brandon Wyman2ab319b2017-11-08 17:34:59 -0600505
506 faultFound = true;
507 }
Brandon Wymanab05c072017-08-30 18:26:41 -0500508 }
509}
510
Brandon Wyman12661f12017-08-31 15:28:21 -0500511void PowerSupply::checkFanFault(const uint16_t statusWord)
512{
Lei YUab093322019-10-09 16:43:22 +0800513 using namespace phosphor::pmbus;
Brandon Wyman12661f12017-08-31 15:28:21 -0500514
Brandon Wymanba255532017-11-08 17:44:10 -0600515 if (fanFault < FAULT_COUNT)
Brandon Wyman12661f12017-08-31 15:28:21 -0500516 {
Brandon Wymanba255532017-11-08 17:44:10 -0600517 // Check for a fan fault or warning condition
518 if (statusWord & status_word::FAN_FAULT)
519 {
520 fanFault++;
521 }
522 else
523 {
524 if (fanFault > 0)
525 {
526 fanFault = 0;
527 }
528 }
Brandon Wyman12661f12017-08-31 15:28:21 -0500529
Brandon Wymane2fc7aa2017-11-13 17:37:10 -0600530 if (!faultFound && (fanFault >= FAULT_COUNT))
Brandon Wymanba255532017-11-08 17:44:10 -0600531 {
532 util::NamesValues nv;
533 nv.add("STATUS_WORD", statusWord);
534 captureCmd(nv, STATUS_MFR, Type::Debug);
535 captureCmd(nv, STATUS_TEMPERATURE, Type::Debug);
536 captureCmd(nv, STATUS_FANS_1_2, Type::Debug);
Brandon Wyman12661f12017-08-31 15:28:21 -0500537
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500538 using metadata =
539 org::open_power::Witherspoon::Fault::PowerSupplyFanFault;
Brandon Wyman12661f12017-08-31 15:28:21 -0500540
Brandon Wymanba255532017-11-08 17:44:10 -0600541 report<PowerSupplyFanFault>(
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500542 metadata::RAW_STATUS(nv.get().c_str()),
543 metadata::CALLOUT_INVENTORY_PATH(inventoryPath.c_str()));
Brandon Wymanba255532017-11-08 17:44:10 -0600544
545 faultFound = true;
546 }
Brandon Wyman12661f12017-08-31 15:28:21 -0500547 }
548}
549
Brandon Wyman875b3632017-09-13 18:46:03 -0500550void PowerSupply::checkTemperatureFault(const uint16_t statusWord)
551{
Lei YUab093322019-10-09 16:43:22 +0800552 using namespace phosphor::pmbus;
Brandon Wyman875b3632017-09-13 18:46:03 -0500553
554 // Due to how the PMBus core device driver sends a clear faults command
555 // the bit in STATUS_WORD will likely be cleared when we attempt to examine
556 // it for a Thermal Fault or Warning. So, check the STATUS_WORD and the
557 // STATUS_TEMPERATURE bits. If either indicates a fault, proceed with
558 // logging the over-temperature condition.
559 std::uint8_t statusTemperature = 0;
560 statusTemperature = pmbusIntf.read(STATUS_TEMPERATURE, Type::Debug);
Brandon Wyman50044ea2017-11-08 17:58:56 -0600561 if (temperatureFault < FAULT_COUNT)
Brandon Wyman875b3632017-09-13 18:46:03 -0500562 {
Brandon Wyman50044ea2017-11-08 17:58:56 -0600563 if ((statusWord & status_word::TEMPERATURE_FAULT_WARN) ||
564 (statusTemperature & status_temperature::OT_FAULT))
565 {
566 temperatureFault++;
567 }
568 else
569 {
570 if (temperatureFault > 0)
571 {
572 temperatureFault = 0;
573 }
574 }
Brandon Wyman875b3632017-09-13 18:46:03 -0500575
Brandon Wymane2fc7aa2017-11-13 17:37:10 -0600576 if (!faultFound && (temperatureFault >= FAULT_COUNT))
Brandon Wyman50044ea2017-11-08 17:58:56 -0600577 {
578 // The power supply has had an over-temperature condition.
579 // This may not result in a shutdown if experienced for a short
580 // duration.
581 // This should not occur under normal conditions.
582 // The power supply may be faulty, or the paired supply may be
583 // putting out less current.
584 // Capture command responses with potentially relevant information,
585 // and call out the power supply reporting the condition.
586 util::NamesValues nv;
587 nv.add("STATUS_WORD", statusWord);
588 captureCmd(nv, STATUS_MFR, Type::Debug);
589 captureCmd(nv, STATUS_IOUT, Type::Debug);
590 nv.add("STATUS_TEMPERATURE", statusTemperature);
591 captureCmd(nv, STATUS_FANS_1_2, Type::Debug);
Brandon Wyman875b3632017-09-13 18:46:03 -0500592
Brandon Wyman50044ea2017-11-08 17:58:56 -0600593 using metadata = org::open_power::Witherspoon::Fault::
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500594 PowerSupplyTemperatureFault;
Brandon Wyman875b3632017-09-13 18:46:03 -0500595
Brandon Wyman50044ea2017-11-08 17:58:56 -0600596 report<PowerSupplyTemperatureFault>(
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500597 metadata::RAW_STATUS(nv.get().c_str()),
598 metadata::CALLOUT_INVENTORY_PATH(inventoryPath.c_str()));
Brandon Wyman50044ea2017-11-08 17:58:56 -0600599
600 faultFound = true;
601 }
Brandon Wyman875b3632017-09-13 18:46:03 -0500602 }
603}
604
Brandon Wyman1db9a9e2017-07-26 18:50:22 -0500605void PowerSupply::clearFaults()
606{
Brandon Wymane4af9802017-11-13 15:58:33 -0600607 readFail = 0;
Brandon Wyman6ccce0b2017-10-26 15:13:10 -0500608 readFailLogged = false;
Brandon Wymana3c675c2017-11-14 14:54:54 -0600609 inputFault = 0;
Brandon Wyman6ccce0b2017-10-26 15:13:10 -0500610 powerOnFault = 0;
Brandon Wymandd61be42017-11-07 18:38:54 -0600611 outputOCFault = 0;
Brandon Wyman2ab319b2017-11-08 17:34:59 -0600612 outputOVFault = 0;
Brandon Wymanba255532017-11-08 17:44:10 -0600613 fanFault = 0;
Brandon Wyman50044ea2017-11-08 17:58:56 -0600614 temperatureFault = 0;
Brandon Wyman3343e822017-11-03 16:54:11 -0500615 faultFound = false;
Brandon Wyman6ccce0b2017-10-26 15:13:10 -0500616
Brandon Wyman1db9a9e2017-07-26 18:50:22 -0500617 return;
618}
619
Brandon Wyman43ce2082017-11-30 17:24:01 -0600620void PowerSupply::resolveError(const std::string& callout,
621 const std::string& message)
622{
Brandon Wyman01741f12017-12-01 17:22:08 -0600623 using EndpointList = std::vector<std::string>;
624
625 try
626 {
627 auto path = callout + "/fault";
628 // Get the service name from the mapper for the fault callout
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500629 auto service = util::getService(path, ASSOCIATION_IFACE, bus);
Brandon Wyman01741f12017-12-01 17:22:08 -0600630
631 // Use getProperty utility function to get log entries (endpoints)
632 EndpointList logEntries;
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500633 util::getProperty(ASSOCIATION_IFACE, ENDPOINTS_PROP, path, service, bus,
634 logEntries);
Brandon Wyman01741f12017-12-01 17:22:08 -0600635
636 // It is possible that all such entries for this callout have since
637 // been deleted.
638 if (logEntries.empty())
639 {
640 return;
641 }
642
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500643 auto logEntryService =
644 util::getService(logEntries[0], LOGGING_IFACE, bus);
Brandon Wyman01741f12017-12-01 17:22:08 -0600645 if (logEntryService.empty())
646 {
647 return;
648 }
649
650 // go through each log entry that matches this callout path
651 std::string logMessage;
652 for (const auto& logEntry : logEntries)
653 {
654 // Check to see if this logEntry has a message that matches.
Matt Spinler589e8722018-01-04 15:24:49 -0600655 util::getProperty(LOGGING_IFACE, MESSAGE_PROP, logEntry,
Brandon Wyman01741f12017-12-01 17:22:08 -0600656 logEntryService, bus, logMessage);
657
658 if (message == logMessage)
659 {
660 // Log entry matches call out and message, set Resolved to true
661 bool resolved = true;
Matt Spinler589e8722018-01-04 15:24:49 -0600662 util::setProperty(LOGGING_IFACE, RESOLVED_PROP, logEntry,
Brandon Wyman01741f12017-12-01 17:22:08 -0600663 logEntryService, bus, resolved);
664 }
Brandon Wyman01741f12017-12-01 17:22:08 -0600665 }
Brandon Wyman01741f12017-12-01 17:22:08 -0600666 }
667 catch (std::exception& e)
668 {
669 log<level::INFO>("Failed to resolve error",
670 entry("CALLOUT=%s", callout.c_str()),
Matt Spinler0d09f292018-01-22 14:51:26 -0600671 entry("ERROR=%s", message.c_str()));
Brandon Wyman01741f12017-12-01 17:22:08 -0600672 }
Brandon Wyman43ce2082017-11-30 17:24:01 -0600673}
674
Matt Spinler234ce0d2018-01-04 15:06:57 -0600675void PowerSupply::updateInventory()
676{
Lei YUab093322019-10-09 16:43:22 +0800677 using namespace phosphor::pmbus;
Matt Spinler018a7bc2018-01-04 15:36:41 -0600678 using namespace sdbusplus::message;
679
Matt Spinler018a7bc2018-01-04 15:36:41 -0600680 // Build the object map and send it to the inventory
681 using Properties = std::map<std::string, variant<std::string>>;
682 using Interfaces = std::map<std::string, Properties>;
683 using Object = std::map<object_path, Interfaces>;
684 Properties assetProps;
685 Properties versionProps;
686 Interfaces interfaces;
687 Object object;
688
George Liu690e7802019-08-23 11:04:01 +0800689 // If any of these accesses fail, the fields will just be
690 // blank in the inventory. Leave logging ReadFailure errors
691 // to analyze() as it runs continuously and will most
692 // likely hit and threshold them first anyway. The
693 // readString() function will do the tracing of the failing
694 // path so this code doesn't need to.
695 for (const auto& fru : fruJson.at("fruConfigs"))
696 {
697 if (fru.at("interface") == ASSET_IFACE)
698 {
699 try
700 {
701 assetProps.emplace(
702 fru.at("propertyName"),
703 present ? pmbusIntf.readString(fru.at("fileName"),
704 inventoryPMBusAccessType)
705 : "");
706 }
707 catch (ReadFailure& e)
708 {
709 }
710 }
711 else if (fru.at("interface") == VERSION_IFACE)
712 {
713 try
714 {
715 versionProps.emplace(
716 fru.at("propertyName"),
717 present ? pmbusIntf.readString(fru.at("fileName"),
718 inventoryPMBusAccessType)
719 : "");
720 }
721 catch (const std::exception& e)
722 {
723 }
724 }
725 }
Matt Spinler018a7bc2018-01-04 15:36:41 -0600726
George Liu690e7802019-08-23 11:04:01 +0800727 interfaces.emplace(ASSET_IFACE, std::move(assetProps));
Matt Spinler018a7bc2018-01-04 15:36:41 -0600728 interfaces.emplace(VERSION_IFACE, std::move(versionProps));
729
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500730 // For Notify(), just send the relative path of the inventory
731 // object so remove the INVENTORY_OBJ_PATH prefix
Matt Spinler018a7bc2018-01-04 15:36:41 -0600732 auto path = inventoryPath.substr(strlen(INVENTORY_OBJ_PATH));
733
734 object.emplace(path, std::move(interfaces));
735
736 try
737 {
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500738 auto service =
739 util::getService(INVENTORY_OBJ_PATH, INVENTORY_MGR_IFACE, bus);
Matt Spinler018a7bc2018-01-04 15:36:41 -0600740
741 if (service.empty())
742 {
743 log<level::ERR>("Unable to get inventory manager service");
744 return;
745 }
746
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500747 auto method = bus.new_method_call(service.c_str(), INVENTORY_OBJ_PATH,
748 INVENTORY_MGR_IFACE, "Notify");
Matt Spinler018a7bc2018-01-04 15:36:41 -0600749
750 method.append(std::move(object));
751
752 auto reply = bus.call(method);
Matt Spinler018a7bc2018-01-04 15:36:41 -0600753
754 // TODO: openbmc/openbmc#2756
755 // Calling Notify() with an enumerated property crashes inventory
756 // manager, so let it default to Unknown and now set it to the
757 // right value.
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500758 auto purpose =
759 version::convertForMessage(version::Version::VersionPurpose::Other);
Matt Spinler018a7bc2018-01-04 15:36:41 -0600760
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500761 util::setProperty(VERSION_IFACE, VERSION_PURPOSE_PROP, inventoryPath,
762 service, bus, purpose);
Matt Spinler018a7bc2018-01-04 15:36:41 -0600763 }
764 catch (std::exception& e)
765 {
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500766 log<level::ERR>(e.what(), entry("PATH=%s", inventoryPath.c_str()));
Matt Spinler018a7bc2018-01-04 15:36:41 -0600767 }
Matt Spinler234ce0d2018-01-04 15:06:57 -0600768}
769
Matt Spinlerd734e652018-01-18 14:31:15 -0600770void PowerSupply::syncHistory()
771{
Lei YUab093322019-10-09 16:43:22 +0800772 using namespace phosphor::gpio;
Matt Spinlerd734e652018-01-18 14:31:15 -0600773
774 if (syncGPIODevPath.empty())
775 {
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500776 // Sync not implemented
Matt Spinlerd734e652018-01-18 14:31:15 -0600777 return;
778 }
779
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500780 GPIO gpio{syncGPIODevPath, static_cast<gpioNum_t>(syncGPIONumber),
Matt Spinlerd734e652018-01-18 14:31:15 -0600781 Direction::output};
782
783 try
784 {
785 gpio.set(Value::low);
786
787 std::this_thread::sleep_for(std::chrono::milliseconds{5});
788
789 gpio.set(Value::high);
790
791 recordManager->clear();
792 }
793 catch (std::exception& e)
794 {
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500795 // Do nothing. There would already be a journal entry.
Matt Spinlerd734e652018-01-18 14:31:15 -0600796 }
797}
798
Matt Spinler82384142018-01-18 14:15:03 -0600799void PowerSupply::enableHistory(const std::string& objectPath,
800 size_t numRecords,
801 const std::string& syncGPIOPath,
802 size_t syncGPIONum)
803{
804 historyObjectPath = objectPath;
805 syncGPIODevPath = syncGPIOPath;
806 syncGPIONumber = syncGPIONum;
807
808 recordManager = std::make_unique<history::RecordManager>(numRecords);
809
810 auto avgPath = historyObjectPath + '/' + history::Average::name;
811 auto maxPath = historyObjectPath + '/' + history::Maximum::name;
812
813 average = std::make_unique<history::Average>(bus, avgPath);
814
815 maximum = std::make_unique<history::Maximum>(bus, maxPath);
816}
817
Matt Spinlereb169fd2018-01-18 14:19:08 -0600818void PowerSupply::updateHistory()
819{
820 if (!recordManager)
821 {
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500822 // Not enabled
Matt Spinlereb169fd2018-01-18 14:19:08 -0600823 return;
824 }
825
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500826 // Read just the most recent average/max record
827 auto data =
828 pmbusIntf.readBinary(INPUT_HISTORY, pmbus::Type::HwmonDeviceDebug,
829 history::RecordManager::RAW_RECORD_SIZE);
Matt Spinlereb169fd2018-01-18 14:19:08 -0600830
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500831 // Update D-Bus only if something changed (a new record ID, or cleared out)
Matt Spinlereb169fd2018-01-18 14:19:08 -0600832 auto changed = recordManager->add(data);
833 if (changed)
834 {
835 average->values(std::move(recordManager->getAverageRecords()));
836 maximum->values(std::move(recordManager->getMaximumRecords()));
837 }
838}
839
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500840} // namespace psu
841} // namespace power
Lei YUab093322019-10-09 16:43:22 +0800842} // namespace phosphor