blob: 373da01b0b743084077154b6eaa8a857c009d264 [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 <org/open_power/Witherspoon/Fault/error.hpp>
25#include <phosphor-logging/elog.hpp>
26#include <phosphor-logging/log.hpp>
27#include <xyz/openbmc_project/Common/Device/error.hpp>
28#include <xyz/openbmc_project/Software/Version/server.hpp>
29
Patrick Williams2c4fbc42020-06-26 15:33:11 -050030#include <functional>
31
Brandon Wyman1db9a9e2017-07-26 18:50:22 -050032namespace witherspoon
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 SN_PROP = "SerialNumber";
57constexpr auto PN_PROP = "PartNumber";
58constexpr auto MODEL_PROP = "Model";
59constexpr auto VERSION_PROP = "Version";
60constexpr auto VERSION_PURPOSE_PROP = "Purpose";
Matt Spinler589e8722018-01-04 15:24:49 -060061
62constexpr auto INVENTORY_OBJ_PATH = "/xyz/openbmc_project/inventory";
Brandon Wyman431fbe42017-08-18 16:22:09 -050063constexpr auto POWER_OBJ_PATH = "/org/openbmc/control/power0";
Brandon Wyman10295542017-08-09 18:20:44 -050064
Matt Spinler018a7bc2018-01-04 15:36:41 -060065constexpr auto SERIAL_NUMBER = "serial_number";
66constexpr auto PART_NUMBER = "part_number";
67constexpr auto FW_VERSION = "fw_version";
68constexpr auto CCIN = "ccin";
Matt Spinlereb169fd2018-01-18 14:19:08 -060069constexpr auto INPUT_HISTORY = "input_history";
Matt Spinler018a7bc2018-01-04 15:36:41 -060070
Brandon Wyman10295542017-08-09 18:20:44 -050071PowerSupply::PowerSupply(const std::string& name, size_t inst,
Matt Spinlerf0f02b92018-10-25 16:12:43 -050072 const std::string& objpath, const std::string& invpath,
Patrick Williams1426a102022-07-22 19:26:55 -050073 sdbusplus::bus_t& bus, const sdeventplus::Event& e,
Matt Spinlerf0f02b92018-10-25 16:12:43 -050074 std::chrono::seconds& t, std::chrono::seconds& p) :
75 Device(name, inst),
76 monitorPath(objpath), pmbusIntf(objpath),
77 inventoryPath(INVENTORY_OBJ_PATH + invpath), bus(bus), presentInterval(p),
78 presentTimer(e, std::bind([this]() {
79 // The hwmon path may have changed.
80 pmbusIntf.findHwmonDir();
81 this->present = true;
Matt Spinler234ce0d2018-01-04 15:06:57 -060082
Matt Spinlerf0f02b92018-10-25 16:12:43 -050083 // Sync the INPUT_HISTORY data for all PSs
84 syncHistory();
Matt Spinlerd734e652018-01-18 14:31:15 -060085
Matt Spinlerf0f02b92018-10-25 16:12:43 -050086 // Update the inventory for the new device
87 updateInventory();
88 })),
89 powerOnInterval(t),
90 powerOnTimer(e, std::bind([this]() { this->powerOn = true; }))
Brandon Wyman10295542017-08-09 18:20:44 -050091{
Brandon Wyman10295542017-08-09 18:20:44 -050092 using namespace sdbusplus::bus;
Aatir Manzur817f8a72019-08-07 07:42:15 -050093 using namespace witherspoon::pmbus;
94 std::uint16_t statusWord = 0;
95 try
96 {
97 // Read the 2 byte STATUS_WORD value to check for faults.
98 statusWord = pmbusIntf.read(STATUS_WORD, Type::Debug);
99 if (!((statusWord & status_word::INPUT_FAULT_WARN) ||
100 (statusWord & status_word::VIN_UV_FAULT)))
101 {
102 resolveError(inventoryPath,
103 std::string(PowerSupplyInputFault::errName));
104 }
105 }
106 catch (ReadFailure& e)
107 {
108 log<level::INFO>("Unable to read the 2 byte STATUS_WORD value to check "
109 "for power-supply input faults.");
110 }
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500111 presentMatch = std::make_unique<match_t>(
112 bus, match::rules::propertiesChanged(inventoryPath, INVENTORY_IFACE),
113 [this](auto& msg) { this->inventoryChanged(msg); });
Brandon Wyman431fbe42017-08-18 16:22:09 -0500114 // Get initial presence state.
Brandon Wyman253dc9b2017-08-12 13:45:52 -0500115 updatePresence();
Brandon Wyman431fbe42017-08-18 16:22:09 -0500116
Matt Spinler234ce0d2018-01-04 15:06:57 -0600117 // Write the SN, PN, etc to the inventory
118 updateInventory();
119
Brandon Wyman431fbe42017-08-18 16:22:09 -0500120 // Subscribe to power state changes
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500121 powerOnMatch = std::make_unique<match_t>(
122 bus, match::rules::propertiesChanged(POWER_OBJ_PATH, POWER_IFACE),
123 [this](auto& msg) { this->powerStateChanged(msg); });
Brandon Wyman431fbe42017-08-18 16:22:09 -0500124 // Get initial power state.
125 updatePowerState();
Brandon Wyman10295542017-08-09 18:20:44 -0500126}
Brandon Wyman442035f2017-08-08 15:58:45 -0500127
Brandon Wymana1e96342017-09-25 16:47:44 -0500128void PowerSupply::captureCmd(util::NamesValues& nv, const std::string& cmd,
129 witherspoon::pmbus::Type type)
130{
131 if (pmbusIntf.exists(cmd, type))
132 {
133 try
134 {
135 auto val = pmbusIntf.read(cmd, type);
136 nv.add(cmd, val);
137 }
138 catch (std::exception& e)
139 {
Joseph Reynolds5b485962018-05-17 16:05:06 -0500140 log<level::INFO>("Unable to capture metadata",
141 entry("CMD=%s", cmd.c_str()));
Brandon Wymana1e96342017-09-25 16:47:44 -0500142 }
143 }
144}
Brandon Wyman431fbe42017-08-18 16:22:09 -0500145
Brandon Wyman1db9a9e2017-07-26 18:50:22 -0500146void PowerSupply::analyze()
147{
Brandon Wyman442035f2017-08-08 15:58:45 -0500148 using namespace witherspoon::pmbus;
149
150 try
151 {
Brandon Wyman10295542017-08-09 18:20:44 -0500152 if (present)
Brandon Wyman442035f2017-08-08 15:58:45 -0500153 {
Brandon Wyman764c7972017-08-22 17:05:36 -0500154 std::uint16_t statusWord = 0;
Brandon Wyman764c7972017-08-22 17:05:36 -0500155
156 // Read the 2 byte STATUS_WORD value to check for faults.
157 statusWord = pmbusIntf.read(STATUS_WORD, Type::Debug);
Brandon Wymane4af9802017-11-13 15:58:33 -0600158 readFail = 0;
Brandon Wyman764c7972017-08-22 17:05:36 -0500159
Brandon Wyman603cc002017-08-28 18:17:58 -0500160 checkInputFault(statusWord);
Brandon Wyman764c7972017-08-22 17:05:36 -0500161
Brandon Wyman654eb6e2018-02-14 14:55:47 -0600162 if (powerOn && (inputFault == 0) && !faultFound)
Brandon Wyman764c7972017-08-22 17:05:36 -0500163 {
Brandon Wyman12661f12017-08-31 15:28:21 -0500164 checkFanFault(statusWord);
Brandon Wyman875b3632017-09-13 18:46:03 -0500165 checkTemperatureFault(statusWord);
Brandon Wymancfa032b2017-09-25 17:37:50 -0500166 checkOutputOvervoltageFault(statusWord);
167 checkCurrentOutOverCurrentFault(statusWord);
168 checkPGOrUnitOffFault(statusWord);
Brandon Wyman442035f2017-08-08 15:58:45 -0500169 }
Matt Spinlereb169fd2018-01-18 14:19:08 -0600170
171 updateHistory();
Brandon Wyman442035f2017-08-08 15:58:45 -0500172 }
173 }
174 catch (ReadFailure& e)
175 {
Brandon Wymane4af9802017-11-13 15:58:33 -0600176 if (readFail < FAULT_COUNT)
177 {
178 readFail++;
179 }
180
181 if (!readFailLogged && readFail >= FAULT_COUNT)
Brandon Wyman442035f2017-08-08 15:58:45 -0500182 {
183 commit<ReadFailure>();
184 readFailLogged = true;
Brandon Wyman442035f2017-08-08 15:58:45 -0500185 }
186 }
187
Brandon Wyman1db9a9e2017-07-26 18:50:22 -0500188 return;
189}
190
Patrick Williams1426a102022-07-22 19:26:55 -0500191void PowerSupply::inventoryChanged(sdbusplus::message_t& msg)
Brandon Wyman10295542017-08-09 18:20:44 -0500192{
193 std::string msgSensor;
Patrick Williamsd7778fb2020-05-13 18:02:03 -0500194 std::map<std::string, std::variant<uint32_t, bool>> msgData;
Brandon Wyman10295542017-08-09 18:20:44 -0500195 msg.read(msgSensor, msgData);
196
197 // Check if it was the Present property that changed.
198 auto valPropMap = msgData.find(PRESENT_PROP);
199 if (valPropMap != msgData.end())
200 {
Patrick Williams5124fb32020-05-13 11:48:23 -0500201 if (std::get<bool>(valPropMap->second))
Brandon Wyman10295542017-08-09 18:20:44 -0500202 {
Brandon Wyman6ccce0b2017-10-26 15:13:10 -0500203 clearFaults();
William A. Kennington III1a0c9172018-10-18 17:57:49 -0700204 presentTimer.restartOnce(presentInterval);
Brandon Wyman590fc282017-11-01 18:22:25 -0500205 }
206 else
207 {
Brandon Wyman2ef48cf2017-11-21 15:43:54 -0600208 present = false;
William A. Kennington III1a0c9172018-10-18 17:57:49 -0700209 presentTimer.setEnabled(false);
Matt Spinler234ce0d2018-01-04 15:06:57 -0600210
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500211 // Clear out the now outdated inventory properties
Matt Spinler234ce0d2018-01-04 15:06:57 -0600212 updateInventory();
Brandon Wyman10295542017-08-09 18:20:44 -0500213 }
214 }
215
216 return;
217}
218
219void PowerSupply::updatePresence()
220{
221 // Use getProperty utility function to get presence status.
Brandon Wyman10295542017-08-09 18:20:44 -0500222 std::string service = "xyz.openbmc_project.Inventory.Manager";
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500223 util::getProperty(INVENTORY_IFACE, PRESENT_PROP, inventoryPath, service,
224 bus, this->present);
Brandon Wyman10295542017-08-09 18:20:44 -0500225}
226
Patrick Williams1426a102022-07-22 19:26:55 -0500227void PowerSupply::powerStateChanged(sdbusplus::message_t& msg)
Brandon Wyman431fbe42017-08-18 16:22:09 -0500228{
229 int32_t state = 0;
230 std::string msgSensor;
Patrick Williamsd7778fb2020-05-13 18:02:03 -0500231 std::map<std::string, std::variant<int32_t>> msgData;
Brandon Wyman431fbe42017-08-18 16:22:09 -0500232 msg.read(msgSensor, msgData);
233
234 // Check if it was the Present property that changed.
235 auto valPropMap = msgData.find("state");
236 if (valPropMap != msgData.end())
237 {
Patrick Williams5124fb32020-05-13 11:48:23 -0500238 state = std::get<int32_t>(valPropMap->second);
Brandon Wyman431fbe42017-08-18 16:22:09 -0500239
240 // Power is on when state=1. Set the fault logged variables to false
241 // and start the power on timer when the state changes to 1.
242 if (state)
243 {
Brandon Wyman6ccce0b2017-10-26 15:13:10 -0500244 clearFaults();
William A. Kennington III1a0c9172018-10-18 17:57:49 -0700245 powerOnTimer.restartOnce(powerOnInterval);
Brandon Wyman431fbe42017-08-18 16:22:09 -0500246 }
247 else
248 {
William A. Kennington III1a0c9172018-10-18 17:57:49 -0700249 powerOnTimer.setEnabled(false);
Brandon Wyman431fbe42017-08-18 16:22:09 -0500250 powerOn = false;
251 }
252 }
Brandon Wyman431fbe42017-08-18 16:22:09 -0500253}
254
255void PowerSupply::updatePowerState()
256{
257 // When state = 1, system is powered on
258 int32_t state = 0;
259
260 try
261 {
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500262 auto service = util::getService(POWER_OBJ_PATH, POWER_IFACE, bus);
Brandon Wyman431fbe42017-08-18 16:22:09 -0500263
264 // Use getProperty utility function to get power state.
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500265 util::getProperty<int32_t>(POWER_IFACE, "state", POWER_OBJ_PATH,
266 service, bus, state);
Brandon Wyman431fbe42017-08-18 16:22:09 -0500267
268 if (state)
269 {
270 powerOn = true;
271 }
272 else
273 {
274 powerOn = false;
275 }
276 }
277 catch (std::exception& e)
278 {
279 log<level::INFO>("Failed to get power state. Assuming it is off.");
280 powerOn = false;
281 }
Brandon Wyman431fbe42017-08-18 16:22:09 -0500282}
283
Brandon Wyman603cc002017-08-28 18:17:58 -0500284void PowerSupply::checkInputFault(const uint16_t statusWord)
285{
286 using namespace witherspoon::pmbus;
287
Brandon Wymana3c675c2017-11-14 14:54:54 -0600288 if ((inputFault < FAULT_COUNT) &&
289 ((statusWord & status_word::INPUT_FAULT_WARN) ||
290 (statusWord & status_word::VIN_UV_FAULT)))
Brandon Wyman603cc002017-08-28 18:17:58 -0500291 {
Brandon Wymanad708242018-01-19 17:28:35 -0600292 if (inputFault == 0)
293 {
294 log<level::INFO>("INPUT or VIN_UV fault",
295 entry("STATUS_WORD=0x%04X", statusWord));
296 }
297
Brandon Wymana3c675c2017-11-14 14:54:54 -0600298 inputFault++;
Brandon Wyman603cc002017-08-28 18:17:58 -0500299 }
300 else
301 {
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500302 if ((inputFault > 0) && !(statusWord & status_word::INPUT_FAULT_WARN) &&
Brandon Wymand20686a2017-11-01 17:45:23 -0500303 !(statusWord & status_word::VIN_UV_FAULT))
Brandon Wyman603cc002017-08-28 18:17:58 -0500304 {
Brandon Wymana3c675c2017-11-14 14:54:54 -0600305 inputFault = 0;
Brandon Wyman3343e822017-11-03 16:54:11 -0500306 faultFound = false;
Brandon Wyman7502f6c2018-02-12 20:20:39 -0600307 // When an input fault occurs, the power supply cannot be on.
308 // However, the check for the case where the power supply should be
309 // on will stop when there is a fault found.
310 // Clear the powerOnFault when the inputFault is cleared to reset
311 // the powerOnFault de-glitching.
312 powerOnFault = 0;
Brandon Wyman69591bd2017-11-01 18:07:23 -0500313
Brandon Wyman603cc002017-08-28 18:17:58 -0500314 log<level::INFO>("INPUT_FAULT_WARN cleared",
Brandon Wymana3c675c2017-11-14 14:54:54 -0600315 entry("POWERSUPPLY=%s", inventoryPath.c_str()));
Brandon Wyman69591bd2017-11-01 18:07:23 -0500316
Brandon Wyman08b05712017-11-30 17:53:56 -0600317 resolveError(inventoryPath,
318 std::string(PowerSupplyInputFault::errName));
319
Brandon Wyman69591bd2017-11-01 18:07:23 -0500320 if (powerOn)
321 {
322 // The power supply will not be immediately powered on after
323 // the input power is restored.
324 powerOn = false;
325 // Start up the timer that will set the state to indicate we
326 // are ready for the powered on fault checks.
William A. Kennington III1a0c9172018-10-18 17:57:49 -0700327 powerOnTimer.restartOnce(powerOnInterval);
Brandon Wyman69591bd2017-11-01 18:07:23 -0500328 }
Brandon Wyman603cc002017-08-28 18:17:58 -0500329 }
330 }
Brandon Wymana3c675c2017-11-14 14:54:54 -0600331
332 if (!faultFound && (inputFault >= FAULT_COUNT))
333 {
Brandon Wymanad708242018-01-19 17:28:35 -0600334 // If the power is on, report the fault in an error log entry.
335 if (powerOn)
336 {
337 util::NamesValues nv;
338 nv.add("STATUS_WORD", statusWord);
339 captureCmd(nv, STATUS_INPUT, Type::Debug);
Brandon Wymana3c675c2017-11-14 14:54:54 -0600340
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500341 using metadata =
342 org::open_power::Witherspoon::Fault::PowerSupplyInputFault;
Brandon Wymana3c675c2017-11-14 14:54:54 -0600343
Brandon Wymanad708242018-01-19 17:28:35 -0600344 report<PowerSupplyInputFault>(
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500345 metadata::RAW_STATUS(nv.get().c_str()),
346 metadata::CALLOUT_INVENTORY_PATH(inventoryPath.c_str()));
Brandon Wymanad708242018-01-19 17:28:35 -0600347
348 faultFound = true;
349 }
Brandon Wymana3c675c2017-11-14 14:54:54 -0600350 }
Brandon Wyman603cc002017-08-28 18:17:58 -0500351}
352
353void PowerSupply::checkPGOrUnitOffFault(const uint16_t statusWord)
354{
355 using namespace witherspoon::pmbus;
356
Brandon Wyman593d24f2017-10-13 18:15:23 -0500357 if (powerOnFault < FAULT_COUNT)
Brandon Wyman603cc002017-08-28 18:17:58 -0500358 {
Brandon Wyman593d24f2017-10-13 18:15:23 -0500359 // Check PG# and UNIT_IS_OFF
360 if ((statusWord & status_word::POWER_GOOD_NEGATED) ||
361 (statusWord & status_word::UNIT_IS_OFF))
362 {
363 log<level::INFO>("PGOOD or UNIT_IS_OFF bit bad",
364 entry("STATUS_WORD=0x%04X", statusWord));
365 powerOnFault++;
366 }
367 else
368 {
369 if (powerOnFault > 0)
370 {
371 log<level::INFO>("PGOOD and UNIT_IS_OFF bits good");
372 powerOnFault = 0;
373 }
374 }
Brandon Wyman603cc002017-08-28 18:17:58 -0500375
Brandon Wymane2fc7aa2017-11-13 17:37:10 -0600376 if (!faultFound && (powerOnFault >= FAULT_COUNT))
Brandon Wyman593d24f2017-10-13 18:15:23 -0500377 {
Brandon Wyman3343e822017-11-03 16:54:11 -0500378 faultFound = true;
379
Brandon Wyman593d24f2017-10-13 18:15:23 -0500380 util::NamesValues nv;
381 nv.add("STATUS_WORD", statusWord);
382 captureCmd(nv, STATUS_INPUT, Type::Debug);
383 auto status0Vout = pmbusIntf.insertPageNum(STATUS_VOUT, 0);
384 captureCmd(nv, status0Vout, Type::Debug);
385 captureCmd(nv, STATUS_IOUT, Type::Debug);
386 captureCmd(nv, STATUS_MFR, Type::Debug);
Brandon Wyman603cc002017-08-28 18:17:58 -0500387
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500388 using metadata =
389 org::open_power::Witherspoon::Fault::PowerSupplyShouldBeOn;
Brandon Wyman603cc002017-08-28 18:17:58 -0500390
Brandon Wyman593d24f2017-10-13 18:15:23 -0500391 // A power supply is OFF (or pgood low) but should be on.
392 report<PowerSupplyShouldBeOn>(
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500393 metadata::RAW_STATUS(nv.get().c_str()),
394 metadata::CALLOUT_INVENTORY_PATH(inventoryPath.c_str()));
Brandon Wyman593d24f2017-10-13 18:15:23 -0500395 }
Brandon Wyman603cc002017-08-28 18:17:58 -0500396 }
Brandon Wyman603cc002017-08-28 18:17:58 -0500397}
398
399void PowerSupply::checkCurrentOutOverCurrentFault(const uint16_t statusWord)
400{
401 using namespace witherspoon::pmbus;
402
Brandon Wymandd61be42017-11-07 18:38:54 -0600403 if (outputOCFault < FAULT_COUNT)
Brandon Wyman603cc002017-08-28 18:17:58 -0500404 {
Brandon Wymandd61be42017-11-07 18:38:54 -0600405 // Check for an output overcurrent fault.
406 if ((statusWord & status_word::IOUT_OC_FAULT))
407 {
408 outputOCFault++;
409 }
410 else
411 {
412 if (outputOCFault > 0)
413 {
414 outputOCFault = 0;
415 }
416 }
Brandon Wyman603cc002017-08-28 18:17:58 -0500417
Brandon Wymane2fc7aa2017-11-13 17:37:10 -0600418 if (!faultFound && (outputOCFault >= FAULT_COUNT))
Brandon Wymandd61be42017-11-07 18:38:54 -0600419 {
420 util::NamesValues nv;
421 nv.add("STATUS_WORD", statusWord);
422 captureCmd(nv, STATUS_INPUT, Type::Debug);
423 auto status0Vout = pmbusIntf.insertPageNum(STATUS_VOUT, 0);
424 captureCmd(nv, status0Vout, Type::Debug);
425 captureCmd(nv, STATUS_IOUT, Type::Debug);
426 captureCmd(nv, STATUS_MFR, Type::Debug);
Brandon Wyman603cc002017-08-28 18:17:58 -0500427
Brandon Wymandd61be42017-11-07 18:38:54 -0600428 using metadata = org::open_power::Witherspoon::Fault::
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500429 PowerSupplyOutputOvercurrent;
Brandon Wyman603cc002017-08-28 18:17:58 -0500430
Brandon Wymandd61be42017-11-07 18:38:54 -0600431 report<PowerSupplyOutputOvercurrent>(
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500432 metadata::RAW_STATUS(nv.get().c_str()),
433 metadata::CALLOUT_INVENTORY_PATH(inventoryPath.c_str()));
Brandon Wymandd61be42017-11-07 18:38:54 -0600434
435 faultFound = true;
436 }
Brandon Wyman603cc002017-08-28 18:17:58 -0500437 }
438}
439
Brandon Wymanab05c072017-08-30 18:26:41 -0500440void PowerSupply::checkOutputOvervoltageFault(const uint16_t statusWord)
441{
442 using namespace witherspoon::pmbus;
443
Brandon Wyman2ab319b2017-11-08 17:34:59 -0600444 if (outputOVFault < FAULT_COUNT)
Brandon Wymanab05c072017-08-30 18:26:41 -0500445 {
Brandon Wyman2ab319b2017-11-08 17:34:59 -0600446 // Check for an output overvoltage fault.
447 if (statusWord & status_word::VOUT_OV_FAULT)
448 {
449 outputOVFault++;
450 }
451 else
452 {
453 if (outputOVFault > 0)
454 {
455 outputOVFault = 0;
456 }
457 }
Brandon Wymanab05c072017-08-30 18:26:41 -0500458
Brandon Wymane2fc7aa2017-11-13 17:37:10 -0600459 if (!faultFound && (outputOVFault >= FAULT_COUNT))
Brandon Wyman2ab319b2017-11-08 17:34:59 -0600460 {
461 util::NamesValues nv;
462 nv.add("STATUS_WORD", statusWord);
463 captureCmd(nv, STATUS_INPUT, Type::Debug);
464 auto status0Vout = pmbusIntf.insertPageNum(STATUS_VOUT, 0);
465 captureCmd(nv, status0Vout, Type::Debug);
466 captureCmd(nv, STATUS_IOUT, Type::Debug);
467 captureCmd(nv, STATUS_MFR, Type::Debug);
Brandon Wymanab05c072017-08-30 18:26:41 -0500468
Brandon Wyman2ab319b2017-11-08 17:34:59 -0600469 using metadata = org::open_power::Witherspoon::Fault::
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500470 PowerSupplyOutputOvervoltage;
Brandon Wymanab05c072017-08-30 18:26:41 -0500471
Brandon Wyman2ab319b2017-11-08 17:34:59 -0600472 report<PowerSupplyOutputOvervoltage>(
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500473 metadata::RAW_STATUS(nv.get().c_str()),
474 metadata::CALLOUT_INVENTORY_PATH(inventoryPath.c_str()));
Brandon Wyman2ab319b2017-11-08 17:34:59 -0600475
476 faultFound = true;
477 }
Brandon Wymanab05c072017-08-30 18:26:41 -0500478 }
479}
480
Brandon Wyman12661f12017-08-31 15:28:21 -0500481void PowerSupply::checkFanFault(const uint16_t statusWord)
482{
483 using namespace witherspoon::pmbus;
484
Brandon Wymanba255532017-11-08 17:44:10 -0600485 if (fanFault < FAULT_COUNT)
Brandon Wyman12661f12017-08-31 15:28:21 -0500486 {
Brandon Wymanba255532017-11-08 17:44:10 -0600487 // Check for a fan fault or warning condition
488 if (statusWord & status_word::FAN_FAULT)
489 {
490 fanFault++;
491 }
492 else
493 {
494 if (fanFault > 0)
495 {
496 fanFault = 0;
497 }
498 }
Brandon Wyman12661f12017-08-31 15:28:21 -0500499
Brandon Wymane2fc7aa2017-11-13 17:37:10 -0600500 if (!faultFound && (fanFault >= FAULT_COUNT))
Brandon Wymanba255532017-11-08 17:44:10 -0600501 {
502 util::NamesValues nv;
503 nv.add("STATUS_WORD", statusWord);
504 captureCmd(nv, STATUS_MFR, Type::Debug);
505 captureCmd(nv, STATUS_TEMPERATURE, Type::Debug);
506 captureCmd(nv, STATUS_FANS_1_2, Type::Debug);
Brandon Wyman12661f12017-08-31 15:28:21 -0500507
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500508 using metadata =
509 org::open_power::Witherspoon::Fault::PowerSupplyFanFault;
Brandon Wyman12661f12017-08-31 15:28:21 -0500510
Brandon Wymanba255532017-11-08 17:44:10 -0600511 report<PowerSupplyFanFault>(
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500512 metadata::RAW_STATUS(nv.get().c_str()),
513 metadata::CALLOUT_INVENTORY_PATH(inventoryPath.c_str()));
Brandon Wymanba255532017-11-08 17:44:10 -0600514
515 faultFound = true;
516 }
Brandon Wyman12661f12017-08-31 15:28:21 -0500517 }
518}
519
Brandon Wyman875b3632017-09-13 18:46:03 -0500520void PowerSupply::checkTemperatureFault(const uint16_t statusWord)
521{
522 using namespace witherspoon::pmbus;
523
524 // Due to how the PMBus core device driver sends a clear faults command
525 // the bit in STATUS_WORD will likely be cleared when we attempt to examine
526 // it for a Thermal Fault or Warning. So, check the STATUS_WORD and the
527 // STATUS_TEMPERATURE bits. If either indicates a fault, proceed with
528 // logging the over-temperature condition.
529 std::uint8_t statusTemperature = 0;
530 statusTemperature = pmbusIntf.read(STATUS_TEMPERATURE, Type::Debug);
Brandon Wyman50044ea2017-11-08 17:58:56 -0600531 if (temperatureFault < FAULT_COUNT)
Brandon Wyman875b3632017-09-13 18:46:03 -0500532 {
Brandon Wyman50044ea2017-11-08 17:58:56 -0600533 if ((statusWord & status_word::TEMPERATURE_FAULT_WARN) ||
534 (statusTemperature & status_temperature::OT_FAULT))
535 {
536 temperatureFault++;
537 }
538 else
539 {
540 if (temperatureFault > 0)
541 {
542 temperatureFault = 0;
543 }
544 }
Brandon Wyman875b3632017-09-13 18:46:03 -0500545
Brandon Wymane2fc7aa2017-11-13 17:37:10 -0600546 if (!faultFound && (temperatureFault >= FAULT_COUNT))
Brandon Wyman50044ea2017-11-08 17:58:56 -0600547 {
548 // The power supply has had an over-temperature condition.
549 // This may not result in a shutdown if experienced for a short
550 // duration.
551 // This should not occur under normal conditions.
552 // The power supply may be faulty, or the paired supply may be
553 // putting out less current.
554 // Capture command responses with potentially relevant information,
555 // and call out the power supply reporting the condition.
556 util::NamesValues nv;
557 nv.add("STATUS_WORD", statusWord);
558 captureCmd(nv, STATUS_MFR, Type::Debug);
559 captureCmd(nv, STATUS_IOUT, Type::Debug);
560 nv.add("STATUS_TEMPERATURE", statusTemperature);
561 captureCmd(nv, STATUS_FANS_1_2, Type::Debug);
Brandon Wyman875b3632017-09-13 18:46:03 -0500562
Brandon Wyman50044ea2017-11-08 17:58:56 -0600563 using metadata = org::open_power::Witherspoon::Fault::
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500564 PowerSupplyTemperatureFault;
Brandon Wyman875b3632017-09-13 18:46:03 -0500565
Brandon Wyman50044ea2017-11-08 17:58:56 -0600566 report<PowerSupplyTemperatureFault>(
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500567 metadata::RAW_STATUS(nv.get().c_str()),
568 metadata::CALLOUT_INVENTORY_PATH(inventoryPath.c_str()));
Brandon Wyman50044ea2017-11-08 17:58:56 -0600569
570 faultFound = true;
571 }
Brandon Wyman875b3632017-09-13 18:46:03 -0500572 }
573}
574
Brandon Wyman1db9a9e2017-07-26 18:50:22 -0500575void PowerSupply::clearFaults()
576{
Brandon Wymane4af9802017-11-13 15:58:33 -0600577 readFail = 0;
Brandon Wyman6ccce0b2017-10-26 15:13:10 -0500578 readFailLogged = false;
Brandon Wymana3c675c2017-11-14 14:54:54 -0600579 inputFault = 0;
Brandon Wyman6ccce0b2017-10-26 15:13:10 -0500580 powerOnFault = 0;
Brandon Wymandd61be42017-11-07 18:38:54 -0600581 outputOCFault = 0;
Brandon Wyman2ab319b2017-11-08 17:34:59 -0600582 outputOVFault = 0;
Brandon Wymanba255532017-11-08 17:44:10 -0600583 fanFault = 0;
Brandon Wyman50044ea2017-11-08 17:58:56 -0600584 temperatureFault = 0;
Brandon Wyman3343e822017-11-03 16:54:11 -0500585 faultFound = false;
Brandon Wyman6ccce0b2017-10-26 15:13:10 -0500586
Brandon Wyman1db9a9e2017-07-26 18:50:22 -0500587 return;
588}
589
Brandon Wyman43ce2082017-11-30 17:24:01 -0600590void PowerSupply::resolveError(const std::string& callout,
591 const std::string& message)
592{
Brandon Wyman01741f12017-12-01 17:22:08 -0600593 using EndpointList = std::vector<std::string>;
594
595 try
596 {
597 auto path = callout + "/fault";
598 // Get the service name from the mapper for the fault callout
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500599 auto service = util::getService(path, ASSOCIATION_IFACE, bus);
Brandon Wyman01741f12017-12-01 17:22:08 -0600600
601 // Use getProperty utility function to get log entries (endpoints)
602 EndpointList logEntries;
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500603 util::getProperty(ASSOCIATION_IFACE, ENDPOINTS_PROP, path, service, bus,
604 logEntries);
Brandon Wyman01741f12017-12-01 17:22:08 -0600605
606 // It is possible that all such entries for this callout have since
607 // been deleted.
608 if (logEntries.empty())
609 {
610 return;
611 }
612
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500613 auto logEntryService =
614 util::getService(logEntries[0], LOGGING_IFACE, bus);
Brandon Wyman01741f12017-12-01 17:22:08 -0600615 if (logEntryService.empty())
616 {
617 return;
618 }
619
620 // go through each log entry that matches this callout path
621 std::string logMessage;
622 for (const auto& logEntry : logEntries)
623 {
624 // Check to see if this logEntry has a message that matches.
Matt Spinler589e8722018-01-04 15:24:49 -0600625 util::getProperty(LOGGING_IFACE, MESSAGE_PROP, logEntry,
Brandon Wyman01741f12017-12-01 17:22:08 -0600626 logEntryService, bus, logMessage);
627
628 if (message == logMessage)
629 {
630 // Log entry matches call out and message, set Resolved to true
631 bool resolved = true;
Matt Spinler589e8722018-01-04 15:24:49 -0600632 util::setProperty(LOGGING_IFACE, RESOLVED_PROP, logEntry,
Brandon Wyman01741f12017-12-01 17:22:08 -0600633 logEntryService, bus, resolved);
634 }
Brandon Wyman01741f12017-12-01 17:22:08 -0600635 }
Brandon Wyman01741f12017-12-01 17:22:08 -0600636 }
637 catch (std::exception& e)
638 {
639 log<level::INFO>("Failed to resolve error",
640 entry("CALLOUT=%s", callout.c_str()),
Matt Spinler0d09f292018-01-22 14:51:26 -0600641 entry("ERROR=%s", message.c_str()));
Brandon Wyman01741f12017-12-01 17:22:08 -0600642 }
Brandon Wyman43ce2082017-11-30 17:24:01 -0600643}
644
Matt Spinler234ce0d2018-01-04 15:06:57 -0600645void PowerSupply::updateInventory()
646{
Matt Spinler018a7bc2018-01-04 15:36:41 -0600647 using namespace witherspoon::pmbus;
648 using namespace sdbusplus::message;
649
650 // If any of these accesses fail, the fields will just be
651 // blank in the inventory. Leave logging ReadFailure errors
652 // to analyze() as it runs continuously and will most
653 // likely hit and threshold them first anyway. The
654 // readString() function will do the tracing of the failing
655 // path so this code doesn't need to.
656 std::string pn;
657 std::string sn;
658 std::string ccin;
659 std::string version;
660
661 if (present)
662 {
663 try
664 {
665 sn = pmbusIntf.readString(SERIAL_NUMBER, Type::HwmonDeviceDebug);
666 }
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500667 catch (ReadFailure& e)
Patrick Williams2c4fbc42020-06-26 15:33:11 -0500668 {}
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)
Patrick Williams2c4fbc42020-06-26 15:33:11 -0500675 {}
Matt Spinler018a7bc2018-01-04 15:36:41 -0600676
677 try
678 {
679 ccin = pmbusIntf.readString(CCIN, Type::HwmonDeviceDebug);
680 }
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500681 catch (ReadFailure& e)
Patrick Williams2c4fbc42020-06-26 15:33:11 -0500682 {}
Matt Spinler018a7bc2018-01-04 15:36:41 -0600683
684 try
685 {
686 version = pmbusIntf.readString(FW_VERSION, Type::HwmonDeviceDebug);
687 }
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500688 catch (ReadFailure& e)
Patrick Williams2c4fbc42020-06-26 15:33:11 -0500689 {}
Matt Spinler018a7bc2018-01-04 15:36:41 -0600690 }
691
692 // Build the object map and send it to the inventory
Patrick Williams2c4fbc42020-06-26 15:33:11 -0500693 using Properties = std::map<std::string, std::variant<std::string>>;
Matt Spinler018a7bc2018-01-04 15:36:41 -0600694 using Interfaces = std::map<std::string, Properties>;
695 using Object = std::map<object_path, Interfaces>;
696 Properties assetProps;
697 Properties versionProps;
698 Interfaces interfaces;
699 Object object;
700
701 assetProps.emplace(SN_PROP, sn);
702 assetProps.emplace(PN_PROP, pn);
703 assetProps.emplace(MODEL_PROP, ccin);
704 interfaces.emplace(ASSET_IFACE, std::move(assetProps));
705
706 versionProps.emplace(VERSION_PROP, version);
707 interfaces.emplace(VERSION_IFACE, std::move(versionProps));
708
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500709 // For Notify(), just send the relative path of the inventory
710 // object so remove the INVENTORY_OBJ_PATH prefix
Matt Spinler018a7bc2018-01-04 15:36:41 -0600711 auto path = inventoryPath.substr(strlen(INVENTORY_OBJ_PATH));
712
713 object.emplace(path, std::move(interfaces));
714
715 try
716 {
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500717 auto service =
718 util::getService(INVENTORY_OBJ_PATH, INVENTORY_MGR_IFACE, bus);
Matt Spinler018a7bc2018-01-04 15:36:41 -0600719
720 if (service.empty())
721 {
722 log<level::ERR>("Unable to get inventory manager service");
723 return;
724 }
725
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500726 auto method = bus.new_method_call(service.c_str(), INVENTORY_OBJ_PATH,
727 INVENTORY_MGR_IFACE, "Notify");
Matt Spinler018a7bc2018-01-04 15:36:41 -0600728
729 method.append(std::move(object));
730
731 auto reply = bus.call(method);
Matt Spinler018a7bc2018-01-04 15:36:41 -0600732
733 // TODO: openbmc/openbmc#2756
734 // Calling Notify() with an enumerated property crashes inventory
735 // manager, so let it default to Unknown and now set it to the
736 // right value.
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500737 auto purpose =
738 version::convertForMessage(version::Version::VersionPurpose::Other);
Matt Spinler018a7bc2018-01-04 15:36:41 -0600739
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500740 util::setProperty(VERSION_IFACE, VERSION_PURPOSE_PROP, inventoryPath,
741 service, bus, purpose);
Matt Spinler018a7bc2018-01-04 15:36:41 -0600742 }
743 catch (std::exception& e)
744 {
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500745 log<level::ERR>(e.what(), entry("PATH=%s", inventoryPath.c_str()));
Matt Spinler018a7bc2018-01-04 15:36:41 -0600746 }
Matt Spinler234ce0d2018-01-04 15:06:57 -0600747}
748
Matt Spinlerd734e652018-01-18 14:31:15 -0600749void PowerSupply::syncHistory()
750{
751 using namespace witherspoon::gpio;
752
753 if (syncGPIODevPath.empty())
754 {
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500755 // Sync not implemented
Matt Spinlerd734e652018-01-18 14:31:15 -0600756 return;
757 }
758
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500759 GPIO gpio{syncGPIODevPath, static_cast<gpioNum_t>(syncGPIONumber),
Matt Spinlerd734e652018-01-18 14:31:15 -0600760 Direction::output};
761
762 try
763 {
764 gpio.set(Value::low);
765
766 std::this_thread::sleep_for(std::chrono::milliseconds{5});
767
768 gpio.set(Value::high);
769
770 recordManager->clear();
771 }
772 catch (std::exception& e)
773 {
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500774 // Do nothing. There would already be a journal entry.
Matt Spinlerd734e652018-01-18 14:31:15 -0600775 }
776}
777
Matt Spinler82384142018-01-18 14:15:03 -0600778void PowerSupply::enableHistory(const std::string& objectPath,
779 size_t numRecords,
780 const std::string& syncGPIOPath,
781 size_t syncGPIONum)
782{
783 historyObjectPath = objectPath;
784 syncGPIODevPath = syncGPIOPath;
785 syncGPIONumber = syncGPIONum;
786
787 recordManager = std::make_unique<history::RecordManager>(numRecords);
788
789 auto avgPath = historyObjectPath + '/' + history::Average::name;
790 auto maxPath = historyObjectPath + '/' + history::Maximum::name;
791
792 average = std::make_unique<history::Average>(bus, avgPath);
793
794 maximum = std::make_unique<history::Maximum>(bus, maxPath);
795}
796
Matt Spinlereb169fd2018-01-18 14:19:08 -0600797void PowerSupply::updateHistory()
798{
799 if (!recordManager)
800 {
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500801 // Not enabled
Matt Spinlereb169fd2018-01-18 14:19:08 -0600802 return;
803 }
804
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500805 // Read just the most recent average/max record
806 auto data =
807 pmbusIntf.readBinary(INPUT_HISTORY, pmbus::Type::HwmonDeviceDebug,
808 history::RecordManager::RAW_RECORD_SIZE);
Matt Spinlereb169fd2018-01-18 14:19:08 -0600809
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500810 // Update D-Bus only if something changed (a new record ID, or cleared out)
Matt Spinlereb169fd2018-01-18 14:19:08 -0600811 auto changed = recordManager->add(data);
812 if (changed)
813 {
814 average->values(std::move(recordManager->getAverageRecords()));
815 maximum->values(std::move(recordManager->getMaximumRecords()));
816 }
817}
818
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500819} // namespace psu
820} // namespace power
821} // namespace witherspoon