blob: d2d3e9c76c7aec7f0fda350f31e1aa211f409fc9 [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 */
Matt Spinlerf0f02b92018-10-25 16:12:43 -050016#include "power_supply.hpp"
17
Brandon Wyman442035f2017-08-08 15:58:45 -050018#include "elog-errors.hpp"
Matt Spinlerd734e652018-01-18 14:31:15 -060019#include "gpio.hpp"
Brandon Wyman10295542017-08-09 18:20:44 -050020#include "names_values.hpp"
Brandon Wyman442035f2017-08-08 15:58:45 -050021#include "pmbus.hpp"
22#include "utility.hpp"
23
Matt Spinlerf0f02b92018-10-25 16:12:43 -050024#include <functional>
25#include <org/open_power/Witherspoon/Fault/error.hpp>
26#include <phosphor-logging/elog.hpp>
27#include <phosphor-logging/log.hpp>
28#include <xyz/openbmc_project/Common/Device/error.hpp>
29#include <xyz/openbmc_project/Software/Version/server.hpp>
30
Brandon Wyman1db9a9e2017-07-26 18:50:22 -050031namespace witherspoon
Brandon Wyman24e422f2017-07-25 19:40:14 -050032{
33namespace power
34{
35namespace psu
36{
37
Matt Spinler589e8722018-01-04 15:24:49 -060038using namespace phosphor::logging;
39using namespace sdbusplus::org::open_power::Witherspoon::Fault::Error;
40using namespace sdbusplus::xyz::openbmc_project::Common::Device::Error;
Matt Spinler018a7bc2018-01-04 15:36:41 -060041namespace version = sdbusplus::xyz::openbmc_project::Software::server;
Matt Spinler589e8722018-01-04 15:24:49 -060042
Matt Spinler5b2964f2019-07-24 10:34:31 -050043constexpr auto ASSOCIATION_IFACE = "xyz.openbmc_project.Association";
Matt Spinler589e8722018-01-04 15:24:49 -060044constexpr auto LOGGING_IFACE = "xyz.openbmc_project.Logging.Entry";
45constexpr auto INVENTORY_IFACE = "xyz.openbmc_project.Inventory.Item";
46constexpr auto POWER_IFACE = "org.openbmc.control.Power";
Matt Spinler018a7bc2018-01-04 15:36:41 -060047constexpr auto INVENTORY_MGR_IFACE = "xyz.openbmc_project.Inventory.Manager";
48constexpr auto ASSET_IFACE = "xyz.openbmc_project.Inventory.Decorator.Asset";
49constexpr auto VERSION_IFACE = "xyz.openbmc_project.Software.Version";
Matt Spinler589e8722018-01-04 15:24:49 -060050
51constexpr auto ENDPOINTS_PROP = "endpoints";
52constexpr auto MESSAGE_PROP = "Message";
53constexpr auto RESOLVED_PROP = "Resolved";
Brandon Wyman10295542017-08-09 18:20:44 -050054constexpr auto PRESENT_PROP = "Present";
Matt Spinler018a7bc2018-01-04 15:36:41 -060055constexpr auto SN_PROP = "SerialNumber";
56constexpr auto PN_PROP = "PartNumber";
57constexpr auto MODEL_PROP = "Model";
58constexpr auto VERSION_PROP = "Version";
59constexpr auto VERSION_PURPOSE_PROP = "Purpose";
Matt Spinler589e8722018-01-04 15:24:49 -060060
61constexpr auto INVENTORY_OBJ_PATH = "/xyz/openbmc_project/inventory";
Brandon Wyman431fbe42017-08-18 16:22:09 -050062constexpr auto POWER_OBJ_PATH = "/org/openbmc/control/power0";
Brandon Wyman10295542017-08-09 18:20:44 -050063
Matt Spinler018a7bc2018-01-04 15:36:41 -060064constexpr auto SERIAL_NUMBER = "serial_number";
65constexpr auto PART_NUMBER = "part_number";
66constexpr auto FW_VERSION = "fw_version";
67constexpr auto CCIN = "ccin";
Matt Spinlereb169fd2018-01-18 14:19:08 -060068constexpr auto INPUT_HISTORY = "input_history";
Matt Spinler018a7bc2018-01-04 15:36:41 -060069
Brandon Wyman10295542017-08-09 18:20:44 -050070PowerSupply::PowerSupply(const std::string& name, size_t inst,
Matt Spinlerf0f02b92018-10-25 16:12:43 -050071 const std::string& objpath, const std::string& invpath,
72 sdbusplus::bus::bus& bus, const sdeventplus::Event& e,
73 std::chrono::seconds& t, std::chrono::seconds& p) :
74 Device(name, inst),
75 monitorPath(objpath), pmbusIntf(objpath),
76 inventoryPath(INVENTORY_OBJ_PATH + invpath), bus(bus), presentInterval(p),
77 presentTimer(e, std::bind([this]() {
78 // The hwmon path may have changed.
79 pmbusIntf.findHwmonDir();
80 this->present = true;
Matt Spinler234ce0d2018-01-04 15:06:57 -060081
Matt Spinlerf0f02b92018-10-25 16:12:43 -050082 // Sync the INPUT_HISTORY data for all PSs
83 syncHistory();
Matt Spinlerd734e652018-01-18 14:31:15 -060084
Matt Spinlerf0f02b92018-10-25 16:12:43 -050085 // Update the inventory for the new device
86 updateInventory();
87 })),
88 powerOnInterval(t),
89 powerOnTimer(e, std::bind([this]() { this->powerOn = true; }))
Brandon Wyman10295542017-08-09 18:20:44 -050090{
Brandon Wyman10295542017-08-09 18:20:44 -050091 using namespace sdbusplus::bus;
Aatir Manzur817f8a72019-08-07 07:42:15 -050092 using namespace witherspoon::pmbus;
93 std::uint16_t statusWord = 0;
94 try
95 {
96 // Read the 2 byte STATUS_WORD value to check for faults.
97 statusWord = pmbusIntf.read(STATUS_WORD, Type::Debug);
98 if (!((statusWord & status_word::INPUT_FAULT_WARN) ||
99 (statusWord & status_word::VIN_UV_FAULT)))
100 {
101 resolveError(inventoryPath,
102 std::string(PowerSupplyInputFault::errName));
103 }
104 }
105 catch (ReadFailure& e)
106 {
107 log<level::INFO>("Unable to read the 2 byte STATUS_WORD value to check "
108 "for power-supply input faults.");
109 }
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500110 presentMatch = std::make_unique<match_t>(
111 bus, match::rules::propertiesChanged(inventoryPath, INVENTORY_IFACE),
112 [this](auto& msg) { this->inventoryChanged(msg); });
Brandon Wyman431fbe42017-08-18 16:22:09 -0500113 // Get initial presence state.
Brandon Wyman253dc9b2017-08-12 13:45:52 -0500114 updatePresence();
Brandon Wyman431fbe42017-08-18 16:22:09 -0500115
Matt Spinler234ce0d2018-01-04 15:06:57 -0600116 // Write the SN, PN, etc to the inventory
117 updateInventory();
118
Brandon Wyman431fbe42017-08-18 16:22:09 -0500119 // Subscribe to power state changes
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500120 powerOnMatch = std::make_unique<match_t>(
121 bus, match::rules::propertiesChanged(POWER_OBJ_PATH, POWER_IFACE),
122 [this](auto& msg) { this->powerStateChanged(msg); });
Brandon Wyman431fbe42017-08-18 16:22:09 -0500123 // Get initial power state.
124 updatePowerState();
Brandon Wyman10295542017-08-09 18:20:44 -0500125}
Brandon Wyman442035f2017-08-08 15:58:45 -0500126
Brandon Wymana1e96342017-09-25 16:47:44 -0500127void PowerSupply::captureCmd(util::NamesValues& nv, const std::string& cmd,
128 witherspoon::pmbus::Type type)
129{
130 if (pmbusIntf.exists(cmd, type))
131 {
132 try
133 {
134 auto val = pmbusIntf.read(cmd, type);
135 nv.add(cmd, val);
136 }
137 catch (std::exception& e)
138 {
Joseph Reynolds5b485962018-05-17 16:05:06 -0500139 log<level::INFO>("Unable to capture metadata",
140 entry("CMD=%s", cmd.c_str()));
Brandon Wymana1e96342017-09-25 16:47:44 -0500141 }
142 }
143}
Brandon Wyman431fbe42017-08-18 16:22:09 -0500144
Brandon Wyman1db9a9e2017-07-26 18:50:22 -0500145void PowerSupply::analyze()
146{
Brandon Wyman442035f2017-08-08 15:58:45 -0500147 using namespace witherspoon::pmbus;
148
149 try
150 {
Brandon Wyman10295542017-08-09 18:20:44 -0500151 if (present)
Brandon Wyman442035f2017-08-08 15:58:45 -0500152 {
Brandon Wyman764c7972017-08-22 17:05:36 -0500153 std::uint16_t statusWord = 0;
Brandon Wyman764c7972017-08-22 17:05:36 -0500154
155 // Read the 2 byte STATUS_WORD value to check for faults.
156 statusWord = pmbusIntf.read(STATUS_WORD, Type::Debug);
Brandon Wymane4af9802017-11-13 15:58:33 -0600157 readFail = 0;
Brandon Wyman764c7972017-08-22 17:05:36 -0500158
Brandon Wyman603cc002017-08-28 18:17:58 -0500159 checkInputFault(statusWord);
Brandon Wyman764c7972017-08-22 17:05:36 -0500160
Brandon Wyman654eb6e2018-02-14 14:55:47 -0600161 if (powerOn && (inputFault == 0) && !faultFound)
Brandon Wyman764c7972017-08-22 17:05:36 -0500162 {
Brandon Wyman12661f12017-08-31 15:28:21 -0500163 checkFanFault(statusWord);
Brandon Wyman875b3632017-09-13 18:46:03 -0500164 checkTemperatureFault(statusWord);
Brandon Wymancfa032b2017-09-25 17:37:50 -0500165 checkOutputOvervoltageFault(statusWord);
166 checkCurrentOutOverCurrentFault(statusWord);
167 checkPGOrUnitOffFault(statusWord);
Brandon Wyman442035f2017-08-08 15:58:45 -0500168 }
Matt Spinlereb169fd2018-01-18 14:19:08 -0600169
170 updateHistory();
Brandon Wyman442035f2017-08-08 15:58:45 -0500171 }
172 }
173 catch (ReadFailure& e)
174 {
Brandon Wymane4af9802017-11-13 15:58:33 -0600175 if (readFail < FAULT_COUNT)
176 {
177 readFail++;
178 }
179
180 if (!readFailLogged && readFail >= FAULT_COUNT)
Brandon Wyman442035f2017-08-08 15:58:45 -0500181 {
182 commit<ReadFailure>();
183 readFailLogged = true;
Brandon Wyman442035f2017-08-08 15:58:45 -0500184 }
185 }
186
Brandon Wyman1db9a9e2017-07-26 18:50:22 -0500187 return;
188}
189
Brandon Wyman10295542017-08-09 18:20:44 -0500190void PowerSupply::inventoryChanged(sdbusplus::message::message& msg)
191{
192 std::string msgSensor;
Patrick Williamsd7778fb2020-05-13 18:02:03 -0500193 std::map<std::string, std::variant<uint32_t, bool>> msgData;
Brandon Wyman10295542017-08-09 18:20:44 -0500194 msg.read(msgSensor, msgData);
195
196 // Check if it was the Present property that changed.
197 auto valPropMap = msgData.find(PRESENT_PROP);
198 if (valPropMap != msgData.end())
199 {
Patrick Williams5124fb32020-05-13 11:48:23 -0500200 if (std::get<bool>(valPropMap->second))
Brandon Wyman10295542017-08-09 18:20:44 -0500201 {
Brandon Wyman6ccce0b2017-10-26 15:13:10 -0500202 clearFaults();
William A. Kennington III1a0c9172018-10-18 17:57:49 -0700203 presentTimer.restartOnce(presentInterval);
Brandon Wyman590fc282017-11-01 18:22:25 -0500204 }
205 else
206 {
Brandon Wyman2ef48cf2017-11-21 15:43:54 -0600207 present = false;
William A. Kennington III1a0c9172018-10-18 17:57:49 -0700208 presentTimer.setEnabled(false);
Matt Spinler234ce0d2018-01-04 15:06:57 -0600209
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500210 // Clear out the now outdated inventory properties
Matt Spinler234ce0d2018-01-04 15:06:57 -0600211 updateInventory();
Brandon Wyman10295542017-08-09 18:20:44 -0500212 }
213 }
214
215 return;
216}
217
218void PowerSupply::updatePresence()
219{
220 // Use getProperty utility function to get presence status.
Brandon Wyman10295542017-08-09 18:20:44 -0500221 std::string service = "xyz.openbmc_project.Inventory.Manager";
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500222 util::getProperty(INVENTORY_IFACE, PRESENT_PROP, inventoryPath, service,
223 bus, this->present);
Brandon Wyman10295542017-08-09 18:20:44 -0500224}
225
Brandon Wyman431fbe42017-08-18 16:22:09 -0500226void PowerSupply::powerStateChanged(sdbusplus::message::message& msg)
227{
228 int32_t state = 0;
229 std::string msgSensor;
Patrick Williamsd7778fb2020-05-13 18:02:03 -0500230 std::map<std::string, std::variant<int32_t>> msgData;
Brandon Wyman431fbe42017-08-18 16:22:09 -0500231 msg.read(msgSensor, msgData);
232
233 // Check if it was the Present property that changed.
234 auto valPropMap = msgData.find("state");
235 if (valPropMap != msgData.end())
236 {
Patrick Williams5124fb32020-05-13 11:48:23 -0500237 state = std::get<int32_t>(valPropMap->second);
Brandon Wyman431fbe42017-08-18 16:22:09 -0500238
239 // Power is on when state=1. Set the fault logged variables to false
240 // and start the power on timer when the state changes to 1.
241 if (state)
242 {
Brandon Wyman6ccce0b2017-10-26 15:13:10 -0500243 clearFaults();
William A. Kennington III1a0c9172018-10-18 17:57:49 -0700244 powerOnTimer.restartOnce(powerOnInterval);
Brandon Wyman431fbe42017-08-18 16:22:09 -0500245 }
246 else
247 {
William A. Kennington III1a0c9172018-10-18 17:57:49 -0700248 powerOnTimer.setEnabled(false);
Brandon Wyman431fbe42017-08-18 16:22:09 -0500249 powerOn = false;
250 }
251 }
Brandon Wyman431fbe42017-08-18 16:22:09 -0500252}
253
254void PowerSupply::updatePowerState()
255{
256 // When state = 1, system is powered on
257 int32_t state = 0;
258
259 try
260 {
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500261 auto service = util::getService(POWER_OBJ_PATH, POWER_IFACE, bus);
Brandon Wyman431fbe42017-08-18 16:22:09 -0500262
263 // Use getProperty utility function to get power state.
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500264 util::getProperty<int32_t>(POWER_IFACE, "state", POWER_OBJ_PATH,
265 service, bus, state);
Brandon Wyman431fbe42017-08-18 16:22:09 -0500266
267 if (state)
268 {
269 powerOn = true;
270 }
271 else
272 {
273 powerOn = false;
274 }
275 }
276 catch (std::exception& e)
277 {
278 log<level::INFO>("Failed to get power state. Assuming it is off.");
279 powerOn = false;
280 }
Brandon Wyman431fbe42017-08-18 16:22:09 -0500281}
282
Brandon Wyman603cc002017-08-28 18:17:58 -0500283void PowerSupply::checkInputFault(const uint16_t statusWord)
284{
285 using namespace witherspoon::pmbus;
286
Brandon Wymana3c675c2017-11-14 14:54:54 -0600287 if ((inputFault < FAULT_COUNT) &&
288 ((statusWord & status_word::INPUT_FAULT_WARN) ||
289 (statusWord & status_word::VIN_UV_FAULT)))
Brandon Wyman603cc002017-08-28 18:17:58 -0500290 {
Brandon Wymanad708242018-01-19 17:28:35 -0600291 if (inputFault == 0)
292 {
293 log<level::INFO>("INPUT or VIN_UV fault",
294 entry("STATUS_WORD=0x%04X", statusWord));
295 }
296
Brandon Wymana3c675c2017-11-14 14:54:54 -0600297 inputFault++;
Brandon Wyman603cc002017-08-28 18:17:58 -0500298 }
299 else
300 {
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500301 if ((inputFault > 0) && !(statusWord & status_word::INPUT_FAULT_WARN) &&
Brandon Wymand20686a2017-11-01 17:45:23 -0500302 !(statusWord & status_word::VIN_UV_FAULT))
Brandon Wyman603cc002017-08-28 18:17:58 -0500303 {
Brandon Wymana3c675c2017-11-14 14:54:54 -0600304 inputFault = 0;
Brandon Wyman3343e822017-11-03 16:54:11 -0500305 faultFound = false;
Brandon Wyman7502f6c2018-02-12 20:20:39 -0600306 // When an input fault occurs, the power supply cannot be on.
307 // However, the check for the case where the power supply should be
308 // on will stop when there is a fault found.
309 // Clear the powerOnFault when the inputFault is cleared to reset
310 // the powerOnFault de-glitching.
311 powerOnFault = 0;
Brandon Wyman69591bd2017-11-01 18:07:23 -0500312
Brandon Wyman603cc002017-08-28 18:17:58 -0500313 log<level::INFO>("INPUT_FAULT_WARN cleared",
Brandon Wymana3c675c2017-11-14 14:54:54 -0600314 entry("POWERSUPPLY=%s", inventoryPath.c_str()));
Brandon Wyman69591bd2017-11-01 18:07:23 -0500315
Brandon Wyman08b05712017-11-30 17:53:56 -0600316 resolveError(inventoryPath,
317 std::string(PowerSupplyInputFault::errName));
318
Brandon Wyman69591bd2017-11-01 18:07:23 -0500319 if (powerOn)
320 {
321 // The power supply will not be immediately powered on after
322 // the input power is restored.
323 powerOn = false;
324 // Start up the timer that will set the state to indicate we
325 // are ready for the powered on fault checks.
William A. Kennington III1a0c9172018-10-18 17:57:49 -0700326 powerOnTimer.restartOnce(powerOnInterval);
Brandon Wyman69591bd2017-11-01 18:07:23 -0500327 }
Brandon Wyman603cc002017-08-28 18:17:58 -0500328 }
329 }
Brandon Wymana3c675c2017-11-14 14:54:54 -0600330
331 if (!faultFound && (inputFault >= FAULT_COUNT))
332 {
Brandon Wymanad708242018-01-19 17:28:35 -0600333 // If the power is on, report the fault in an error log entry.
334 if (powerOn)
335 {
336 util::NamesValues nv;
337 nv.add("STATUS_WORD", statusWord);
338 captureCmd(nv, STATUS_INPUT, Type::Debug);
Brandon Wymana3c675c2017-11-14 14:54:54 -0600339
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500340 using metadata =
341 org::open_power::Witherspoon::Fault::PowerSupplyInputFault;
Brandon Wymana3c675c2017-11-14 14:54:54 -0600342
Brandon Wymanad708242018-01-19 17:28:35 -0600343 report<PowerSupplyInputFault>(
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500344 metadata::RAW_STATUS(nv.get().c_str()),
345 metadata::CALLOUT_INVENTORY_PATH(inventoryPath.c_str()));
Brandon Wymanad708242018-01-19 17:28:35 -0600346
347 faultFound = true;
348 }
Brandon Wymana3c675c2017-11-14 14:54:54 -0600349 }
Brandon Wyman603cc002017-08-28 18:17:58 -0500350}
351
352void PowerSupply::checkPGOrUnitOffFault(const uint16_t statusWord)
353{
354 using namespace witherspoon::pmbus;
355
Brandon Wyman593d24f2017-10-13 18:15:23 -0500356 if (powerOnFault < FAULT_COUNT)
Brandon Wyman603cc002017-08-28 18:17:58 -0500357 {
Brandon Wyman593d24f2017-10-13 18:15:23 -0500358 // Check PG# and UNIT_IS_OFF
359 if ((statusWord & status_word::POWER_GOOD_NEGATED) ||
360 (statusWord & status_word::UNIT_IS_OFF))
361 {
362 log<level::INFO>("PGOOD or UNIT_IS_OFF bit bad",
363 entry("STATUS_WORD=0x%04X", statusWord));
364 powerOnFault++;
365 }
366 else
367 {
368 if (powerOnFault > 0)
369 {
370 log<level::INFO>("PGOOD and UNIT_IS_OFF bits good");
371 powerOnFault = 0;
372 }
373 }
Brandon Wyman603cc002017-08-28 18:17:58 -0500374
Brandon Wymane2fc7aa2017-11-13 17:37:10 -0600375 if (!faultFound && (powerOnFault >= FAULT_COUNT))
Brandon Wyman593d24f2017-10-13 18:15:23 -0500376 {
Brandon Wyman3343e822017-11-03 16:54:11 -0500377 faultFound = true;
378
Brandon Wyman593d24f2017-10-13 18:15:23 -0500379 util::NamesValues nv;
380 nv.add("STATUS_WORD", statusWord);
381 captureCmd(nv, STATUS_INPUT, Type::Debug);
382 auto status0Vout = pmbusIntf.insertPageNum(STATUS_VOUT, 0);
383 captureCmd(nv, status0Vout, Type::Debug);
384 captureCmd(nv, STATUS_IOUT, Type::Debug);
385 captureCmd(nv, STATUS_MFR, Type::Debug);
Brandon Wyman603cc002017-08-28 18:17:58 -0500386
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500387 using metadata =
388 org::open_power::Witherspoon::Fault::PowerSupplyShouldBeOn;
Brandon Wyman603cc002017-08-28 18:17:58 -0500389
Brandon Wyman593d24f2017-10-13 18:15:23 -0500390 // A power supply is OFF (or pgood low) but should be on.
391 report<PowerSupplyShouldBeOn>(
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500392 metadata::RAW_STATUS(nv.get().c_str()),
393 metadata::CALLOUT_INVENTORY_PATH(inventoryPath.c_str()));
Brandon Wyman593d24f2017-10-13 18:15:23 -0500394 }
Brandon Wyman603cc002017-08-28 18:17:58 -0500395 }
Brandon Wyman603cc002017-08-28 18:17:58 -0500396}
397
398void PowerSupply::checkCurrentOutOverCurrentFault(const uint16_t statusWord)
399{
400 using namespace witherspoon::pmbus;
401
Brandon Wymandd61be42017-11-07 18:38:54 -0600402 if (outputOCFault < FAULT_COUNT)
Brandon Wyman603cc002017-08-28 18:17:58 -0500403 {
Brandon Wymandd61be42017-11-07 18:38:54 -0600404 // Check for an output overcurrent fault.
405 if ((statusWord & status_word::IOUT_OC_FAULT))
406 {
407 outputOCFault++;
408 }
409 else
410 {
411 if (outputOCFault > 0)
412 {
413 outputOCFault = 0;
414 }
415 }
Brandon Wyman603cc002017-08-28 18:17:58 -0500416
Brandon Wymane2fc7aa2017-11-13 17:37:10 -0600417 if (!faultFound && (outputOCFault >= FAULT_COUNT))
Brandon Wymandd61be42017-11-07 18:38:54 -0600418 {
419 util::NamesValues nv;
420 nv.add("STATUS_WORD", statusWord);
421 captureCmd(nv, STATUS_INPUT, Type::Debug);
422 auto status0Vout = pmbusIntf.insertPageNum(STATUS_VOUT, 0);
423 captureCmd(nv, status0Vout, Type::Debug);
424 captureCmd(nv, STATUS_IOUT, Type::Debug);
425 captureCmd(nv, STATUS_MFR, Type::Debug);
Brandon Wyman603cc002017-08-28 18:17:58 -0500426
Brandon Wymandd61be42017-11-07 18:38:54 -0600427 using metadata = org::open_power::Witherspoon::Fault::
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500428 PowerSupplyOutputOvercurrent;
Brandon Wyman603cc002017-08-28 18:17:58 -0500429
Brandon Wymandd61be42017-11-07 18:38:54 -0600430 report<PowerSupplyOutputOvercurrent>(
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500431 metadata::RAW_STATUS(nv.get().c_str()),
432 metadata::CALLOUT_INVENTORY_PATH(inventoryPath.c_str()));
Brandon Wymandd61be42017-11-07 18:38:54 -0600433
434 faultFound = true;
435 }
Brandon Wyman603cc002017-08-28 18:17:58 -0500436 }
437}
438
Brandon Wymanab05c072017-08-30 18:26:41 -0500439void PowerSupply::checkOutputOvervoltageFault(const uint16_t statusWord)
440{
441 using namespace witherspoon::pmbus;
442
Brandon Wyman2ab319b2017-11-08 17:34:59 -0600443 if (outputOVFault < FAULT_COUNT)
Brandon Wymanab05c072017-08-30 18:26:41 -0500444 {
Brandon Wyman2ab319b2017-11-08 17:34:59 -0600445 // Check for an output overvoltage fault.
446 if (statusWord & status_word::VOUT_OV_FAULT)
447 {
448 outputOVFault++;
449 }
450 else
451 {
452 if (outputOVFault > 0)
453 {
454 outputOVFault = 0;
455 }
456 }
Brandon Wymanab05c072017-08-30 18:26:41 -0500457
Brandon Wymane2fc7aa2017-11-13 17:37:10 -0600458 if (!faultFound && (outputOVFault >= FAULT_COUNT))
Brandon Wyman2ab319b2017-11-08 17:34:59 -0600459 {
460 util::NamesValues nv;
461 nv.add("STATUS_WORD", statusWord);
462 captureCmd(nv, STATUS_INPUT, Type::Debug);
463 auto status0Vout = pmbusIntf.insertPageNum(STATUS_VOUT, 0);
464 captureCmd(nv, status0Vout, Type::Debug);
465 captureCmd(nv, STATUS_IOUT, Type::Debug);
466 captureCmd(nv, STATUS_MFR, Type::Debug);
Brandon Wymanab05c072017-08-30 18:26:41 -0500467
Brandon Wyman2ab319b2017-11-08 17:34:59 -0600468 using metadata = org::open_power::Witherspoon::Fault::
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500469 PowerSupplyOutputOvervoltage;
Brandon Wymanab05c072017-08-30 18:26:41 -0500470
Brandon Wyman2ab319b2017-11-08 17:34:59 -0600471 report<PowerSupplyOutputOvervoltage>(
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500472 metadata::RAW_STATUS(nv.get().c_str()),
473 metadata::CALLOUT_INVENTORY_PATH(inventoryPath.c_str()));
Brandon Wyman2ab319b2017-11-08 17:34:59 -0600474
475 faultFound = true;
476 }
Brandon Wymanab05c072017-08-30 18:26:41 -0500477 }
478}
479
Brandon Wyman12661f12017-08-31 15:28:21 -0500480void PowerSupply::checkFanFault(const uint16_t statusWord)
481{
482 using namespace witherspoon::pmbus;
483
Brandon Wymanba255532017-11-08 17:44:10 -0600484 if (fanFault < FAULT_COUNT)
Brandon Wyman12661f12017-08-31 15:28:21 -0500485 {
Brandon Wymanba255532017-11-08 17:44:10 -0600486 // Check for a fan fault or warning condition
487 if (statusWord & status_word::FAN_FAULT)
488 {
489 fanFault++;
490 }
491 else
492 {
493 if (fanFault > 0)
494 {
495 fanFault = 0;
496 }
497 }
Brandon Wyman12661f12017-08-31 15:28:21 -0500498
Brandon Wymane2fc7aa2017-11-13 17:37:10 -0600499 if (!faultFound && (fanFault >= FAULT_COUNT))
Brandon Wymanba255532017-11-08 17:44:10 -0600500 {
501 util::NamesValues nv;
502 nv.add("STATUS_WORD", statusWord);
503 captureCmd(nv, STATUS_MFR, Type::Debug);
504 captureCmd(nv, STATUS_TEMPERATURE, Type::Debug);
505 captureCmd(nv, STATUS_FANS_1_2, Type::Debug);
Brandon Wyman12661f12017-08-31 15:28:21 -0500506
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500507 using metadata =
508 org::open_power::Witherspoon::Fault::PowerSupplyFanFault;
Brandon Wyman12661f12017-08-31 15:28:21 -0500509
Brandon Wymanba255532017-11-08 17:44:10 -0600510 report<PowerSupplyFanFault>(
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500511 metadata::RAW_STATUS(nv.get().c_str()),
512 metadata::CALLOUT_INVENTORY_PATH(inventoryPath.c_str()));
Brandon Wymanba255532017-11-08 17:44:10 -0600513
514 faultFound = true;
515 }
Brandon Wyman12661f12017-08-31 15:28:21 -0500516 }
517}
518
Brandon Wyman875b3632017-09-13 18:46:03 -0500519void PowerSupply::checkTemperatureFault(const uint16_t statusWord)
520{
521 using namespace witherspoon::pmbus;
522
523 // Due to how the PMBus core device driver sends a clear faults command
524 // the bit in STATUS_WORD will likely be cleared when we attempt to examine
525 // it for a Thermal Fault or Warning. So, check the STATUS_WORD and the
526 // STATUS_TEMPERATURE bits. If either indicates a fault, proceed with
527 // logging the over-temperature condition.
528 std::uint8_t statusTemperature = 0;
529 statusTemperature = pmbusIntf.read(STATUS_TEMPERATURE, Type::Debug);
Brandon Wyman50044ea2017-11-08 17:58:56 -0600530 if (temperatureFault < FAULT_COUNT)
Brandon Wyman875b3632017-09-13 18:46:03 -0500531 {
Brandon Wyman50044ea2017-11-08 17:58:56 -0600532 if ((statusWord & status_word::TEMPERATURE_FAULT_WARN) ||
533 (statusTemperature & status_temperature::OT_FAULT))
534 {
535 temperatureFault++;
536 }
537 else
538 {
539 if (temperatureFault > 0)
540 {
541 temperatureFault = 0;
542 }
543 }
Brandon Wyman875b3632017-09-13 18:46:03 -0500544
Brandon Wymane2fc7aa2017-11-13 17:37:10 -0600545 if (!faultFound && (temperatureFault >= FAULT_COUNT))
Brandon Wyman50044ea2017-11-08 17:58:56 -0600546 {
547 // The power supply has had an over-temperature condition.
548 // This may not result in a shutdown if experienced for a short
549 // duration.
550 // This should not occur under normal conditions.
551 // The power supply may be faulty, or the paired supply may be
552 // putting out less current.
553 // Capture command responses with potentially relevant information,
554 // and call out the power supply reporting the condition.
555 util::NamesValues nv;
556 nv.add("STATUS_WORD", statusWord);
557 captureCmd(nv, STATUS_MFR, Type::Debug);
558 captureCmd(nv, STATUS_IOUT, Type::Debug);
559 nv.add("STATUS_TEMPERATURE", statusTemperature);
560 captureCmd(nv, STATUS_FANS_1_2, Type::Debug);
Brandon Wyman875b3632017-09-13 18:46:03 -0500561
Brandon Wyman50044ea2017-11-08 17:58:56 -0600562 using metadata = org::open_power::Witherspoon::Fault::
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500563 PowerSupplyTemperatureFault;
Brandon Wyman875b3632017-09-13 18:46:03 -0500564
Brandon Wyman50044ea2017-11-08 17:58:56 -0600565 report<PowerSupplyTemperatureFault>(
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500566 metadata::RAW_STATUS(nv.get().c_str()),
567 metadata::CALLOUT_INVENTORY_PATH(inventoryPath.c_str()));
Brandon Wyman50044ea2017-11-08 17:58:56 -0600568
569 faultFound = true;
570 }
Brandon Wyman875b3632017-09-13 18:46:03 -0500571 }
572}
573
Brandon Wyman1db9a9e2017-07-26 18:50:22 -0500574void PowerSupply::clearFaults()
575{
Brandon Wymane4af9802017-11-13 15:58:33 -0600576 readFail = 0;
Brandon Wyman6ccce0b2017-10-26 15:13:10 -0500577 readFailLogged = false;
Brandon Wymana3c675c2017-11-14 14:54:54 -0600578 inputFault = 0;
Brandon Wyman6ccce0b2017-10-26 15:13:10 -0500579 powerOnFault = 0;
Brandon Wymandd61be42017-11-07 18:38:54 -0600580 outputOCFault = 0;
Brandon Wyman2ab319b2017-11-08 17:34:59 -0600581 outputOVFault = 0;
Brandon Wymanba255532017-11-08 17:44:10 -0600582 fanFault = 0;
Brandon Wyman50044ea2017-11-08 17:58:56 -0600583 temperatureFault = 0;
Brandon Wyman3343e822017-11-03 16:54:11 -0500584 faultFound = false;
Brandon Wyman6ccce0b2017-10-26 15:13:10 -0500585
Brandon Wyman1db9a9e2017-07-26 18:50:22 -0500586 return;
587}
588
Brandon Wyman43ce2082017-11-30 17:24:01 -0600589void PowerSupply::resolveError(const std::string& callout,
590 const std::string& message)
591{
Brandon Wyman01741f12017-12-01 17:22:08 -0600592 using EndpointList = std::vector<std::string>;
593
594 try
595 {
596 auto path = callout + "/fault";
597 // Get the service name from the mapper for the fault callout
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500598 auto service = util::getService(path, ASSOCIATION_IFACE, bus);
Brandon Wyman01741f12017-12-01 17:22:08 -0600599
600 // Use getProperty utility function to get log entries (endpoints)
601 EndpointList logEntries;
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500602 util::getProperty(ASSOCIATION_IFACE, ENDPOINTS_PROP, path, service, bus,
603 logEntries);
Brandon Wyman01741f12017-12-01 17:22:08 -0600604
605 // It is possible that all such entries for this callout have since
606 // been deleted.
607 if (logEntries.empty())
608 {
609 return;
610 }
611
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500612 auto logEntryService =
613 util::getService(logEntries[0], LOGGING_IFACE, bus);
Brandon Wyman01741f12017-12-01 17:22:08 -0600614 if (logEntryService.empty())
615 {
616 return;
617 }
618
619 // go through each log entry that matches this callout path
620 std::string logMessage;
621 for (const auto& logEntry : logEntries)
622 {
623 // Check to see if this logEntry has a message that matches.
Matt Spinler589e8722018-01-04 15:24:49 -0600624 util::getProperty(LOGGING_IFACE, MESSAGE_PROP, logEntry,
Brandon Wyman01741f12017-12-01 17:22:08 -0600625 logEntryService, bus, logMessage);
626
627 if (message == logMessage)
628 {
629 // Log entry matches call out and message, set Resolved to true
630 bool resolved = true;
Matt Spinler589e8722018-01-04 15:24:49 -0600631 util::setProperty(LOGGING_IFACE, RESOLVED_PROP, logEntry,
Brandon Wyman01741f12017-12-01 17:22:08 -0600632 logEntryService, bus, resolved);
633 }
Brandon Wyman01741f12017-12-01 17:22:08 -0600634 }
Brandon Wyman01741f12017-12-01 17:22:08 -0600635 }
636 catch (std::exception& e)
637 {
638 log<level::INFO>("Failed to resolve error",
639 entry("CALLOUT=%s", callout.c_str()),
Matt Spinler0d09f292018-01-22 14:51:26 -0600640 entry("ERROR=%s", message.c_str()));
Brandon Wyman01741f12017-12-01 17:22:08 -0600641 }
Brandon Wyman43ce2082017-11-30 17:24:01 -0600642}
643
Matt Spinler234ce0d2018-01-04 15:06:57 -0600644void PowerSupply::updateInventory()
645{
Matt Spinler018a7bc2018-01-04 15:36:41 -0600646 using namespace witherspoon::pmbus;
647 using namespace sdbusplus::message;
648
649 // If any of these accesses fail, the fields will just be
650 // blank in the inventory. Leave logging ReadFailure errors
651 // to analyze() as it runs continuously and will most
652 // likely hit and threshold them first anyway. The
653 // readString() function will do the tracing of the failing
654 // path so this code doesn't need to.
655 std::string pn;
656 std::string sn;
657 std::string ccin;
658 std::string version;
659
660 if (present)
661 {
662 try
663 {
664 sn = pmbusIntf.readString(SERIAL_NUMBER, Type::HwmonDeviceDebug);
665 }
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500666 catch (ReadFailure& e)
667 {
668 }
Matt Spinler018a7bc2018-01-04 15:36:41 -0600669
670 try
671 {
672 pn = pmbusIntf.readString(PART_NUMBER, Type::HwmonDeviceDebug);
673 }
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500674 catch (ReadFailure& e)
675 {
676 }
Matt Spinler018a7bc2018-01-04 15:36:41 -0600677
678 try
679 {
680 ccin = pmbusIntf.readString(CCIN, Type::HwmonDeviceDebug);
681 }
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500682 catch (ReadFailure& e)
683 {
684 }
Matt Spinler018a7bc2018-01-04 15:36:41 -0600685
686 try
687 {
688 version = pmbusIntf.readString(FW_VERSION, Type::HwmonDeviceDebug);
689 }
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500690 catch (ReadFailure& e)
691 {
692 }
Matt Spinler018a7bc2018-01-04 15:36:41 -0600693 }
694
695 // Build the object map and send it to the inventory
696 using Properties = std::map<std::string, variant<std::string>>;
697 using Interfaces = std::map<std::string, Properties>;
698 using Object = std::map<object_path, Interfaces>;
699 Properties assetProps;
700 Properties versionProps;
701 Interfaces interfaces;
702 Object object;
703
704 assetProps.emplace(SN_PROP, sn);
705 assetProps.emplace(PN_PROP, pn);
706 assetProps.emplace(MODEL_PROP, ccin);
707 interfaces.emplace(ASSET_IFACE, std::move(assetProps));
708
709 versionProps.emplace(VERSION_PROP, version);
710 interfaces.emplace(VERSION_IFACE, std::move(versionProps));
711
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500712 // For Notify(), just send the relative path of the inventory
713 // object so remove the INVENTORY_OBJ_PATH prefix
Matt Spinler018a7bc2018-01-04 15:36:41 -0600714 auto path = inventoryPath.substr(strlen(INVENTORY_OBJ_PATH));
715
716 object.emplace(path, std::move(interfaces));
717
718 try
719 {
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500720 auto service =
721 util::getService(INVENTORY_OBJ_PATH, INVENTORY_MGR_IFACE, bus);
Matt Spinler018a7bc2018-01-04 15:36:41 -0600722
723 if (service.empty())
724 {
725 log<level::ERR>("Unable to get inventory manager service");
726 return;
727 }
728
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500729 auto method = bus.new_method_call(service.c_str(), INVENTORY_OBJ_PATH,
730 INVENTORY_MGR_IFACE, "Notify");
Matt Spinler018a7bc2018-01-04 15:36:41 -0600731
732 method.append(std::move(object));
733
734 auto reply = bus.call(method);
Matt Spinler018a7bc2018-01-04 15:36:41 -0600735
736 // TODO: openbmc/openbmc#2756
737 // Calling Notify() with an enumerated property crashes inventory
738 // manager, so let it default to Unknown and now set it to the
739 // right value.
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500740 auto purpose =
741 version::convertForMessage(version::Version::VersionPurpose::Other);
Matt Spinler018a7bc2018-01-04 15:36:41 -0600742
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500743 util::setProperty(VERSION_IFACE, VERSION_PURPOSE_PROP, inventoryPath,
744 service, bus, purpose);
Matt Spinler018a7bc2018-01-04 15:36:41 -0600745 }
746 catch (std::exception& e)
747 {
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500748 log<level::ERR>(e.what(), entry("PATH=%s", inventoryPath.c_str()));
Matt Spinler018a7bc2018-01-04 15:36:41 -0600749 }
Matt Spinler234ce0d2018-01-04 15:06:57 -0600750}
751
Matt Spinlerd734e652018-01-18 14:31:15 -0600752void PowerSupply::syncHistory()
753{
754 using namespace witherspoon::gpio;
755
756 if (syncGPIODevPath.empty())
757 {
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500758 // Sync not implemented
Matt Spinlerd734e652018-01-18 14:31:15 -0600759 return;
760 }
761
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500762 GPIO gpio{syncGPIODevPath, static_cast<gpioNum_t>(syncGPIONumber),
Matt Spinlerd734e652018-01-18 14:31:15 -0600763 Direction::output};
764
765 try
766 {
767 gpio.set(Value::low);
768
769 std::this_thread::sleep_for(std::chrono::milliseconds{5});
770
771 gpio.set(Value::high);
772
773 recordManager->clear();
774 }
775 catch (std::exception& e)
776 {
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500777 // Do nothing. There would already be a journal entry.
Matt Spinlerd734e652018-01-18 14:31:15 -0600778 }
779}
780
Matt Spinler82384142018-01-18 14:15:03 -0600781void PowerSupply::enableHistory(const std::string& objectPath,
782 size_t numRecords,
783 const std::string& syncGPIOPath,
784 size_t syncGPIONum)
785{
786 historyObjectPath = objectPath;
787 syncGPIODevPath = syncGPIOPath;
788 syncGPIONumber = syncGPIONum;
789
790 recordManager = std::make_unique<history::RecordManager>(numRecords);
791
792 auto avgPath = historyObjectPath + '/' + history::Average::name;
793 auto maxPath = historyObjectPath + '/' + history::Maximum::name;
794
795 average = std::make_unique<history::Average>(bus, avgPath);
796
797 maximum = std::make_unique<history::Maximum>(bus, maxPath);
798}
799
Matt Spinlereb169fd2018-01-18 14:19:08 -0600800void PowerSupply::updateHistory()
801{
802 if (!recordManager)
803 {
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500804 // Not enabled
Matt Spinlereb169fd2018-01-18 14:19:08 -0600805 return;
806 }
807
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500808 // Read just the most recent average/max record
809 auto data =
810 pmbusIntf.readBinary(INPUT_HISTORY, pmbus::Type::HwmonDeviceDebug,
811 history::RecordManager::RAW_RECORD_SIZE);
Matt Spinlereb169fd2018-01-18 14:19:08 -0600812
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500813 // Update D-Bus only if something changed (a new record ID, or cleared out)
Matt Spinlereb169fd2018-01-18 14:19:08 -0600814 auto changed = recordManager->add(data);
815 if (changed)
816 {
817 average->values(std::move(recordManager->getAverageRecords()));
818 maximum->values(std::move(recordManager->getMaximumRecords()));
819 }
820}
821
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500822} // namespace psu
823} // namespace power
824} // namespace witherspoon