blob: 5988ea92ae76c85831562aa8b4c2bb94b3b7d995 [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
43constexpr auto ASSOCIATION_IFACE = "org.openbmc.Association";
44constexpr 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;
Matt Spinlerf0f02b92018-10-25 16:12:43 -050092 presentMatch = std::make_unique<match_t>(
93 bus, match::rules::propertiesChanged(inventoryPath, INVENTORY_IFACE),
94 [this](auto& msg) { this->inventoryChanged(msg); });
Brandon Wyman431fbe42017-08-18 16:22:09 -050095 // Get initial presence state.
Brandon Wyman253dc9b2017-08-12 13:45:52 -050096 updatePresence();
Brandon Wyman431fbe42017-08-18 16:22:09 -050097
Matt Spinler234ce0d2018-01-04 15:06:57 -060098 // Write the SN, PN, etc to the inventory
99 updateInventory();
100
Brandon Wyman431fbe42017-08-18 16:22:09 -0500101 // Subscribe to power state changes
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500102 powerOnMatch = std::make_unique<match_t>(
103 bus, match::rules::propertiesChanged(POWER_OBJ_PATH, POWER_IFACE),
104 [this](auto& msg) { this->powerStateChanged(msg); });
Brandon Wyman431fbe42017-08-18 16:22:09 -0500105 // Get initial power state.
106 updatePowerState();
Brandon Wyman10295542017-08-09 18:20:44 -0500107}
Brandon Wyman442035f2017-08-08 15:58:45 -0500108
Brandon Wymana1e96342017-09-25 16:47:44 -0500109void PowerSupply::captureCmd(util::NamesValues& nv, const std::string& cmd,
110 witherspoon::pmbus::Type type)
111{
112 if (pmbusIntf.exists(cmd, type))
113 {
114 try
115 {
116 auto val = pmbusIntf.read(cmd, type);
117 nv.add(cmd, val);
118 }
119 catch (std::exception& e)
120 {
Joseph Reynolds5b485962018-05-17 16:05:06 -0500121 log<level::INFO>("Unable to capture metadata",
122 entry("CMD=%s", cmd.c_str()));
Brandon Wymana1e96342017-09-25 16:47:44 -0500123 }
124 }
125}
Brandon Wyman431fbe42017-08-18 16:22:09 -0500126
Brandon Wyman1db9a9e2017-07-26 18:50:22 -0500127void PowerSupply::analyze()
128{
Brandon Wyman442035f2017-08-08 15:58:45 -0500129 using namespace witherspoon::pmbus;
130
131 try
132 {
Brandon Wyman10295542017-08-09 18:20:44 -0500133 if (present)
Brandon Wyman442035f2017-08-08 15:58:45 -0500134 {
Brandon Wyman764c7972017-08-22 17:05:36 -0500135 std::uint16_t statusWord = 0;
Brandon Wyman764c7972017-08-22 17:05:36 -0500136
137 // Read the 2 byte STATUS_WORD value to check for faults.
138 statusWord = pmbusIntf.read(STATUS_WORD, Type::Debug);
Brandon Wymane4af9802017-11-13 15:58:33 -0600139 readFail = 0;
Brandon Wyman764c7972017-08-22 17:05:36 -0500140
Brandon Wyman603cc002017-08-28 18:17:58 -0500141 checkInputFault(statusWord);
Brandon Wyman764c7972017-08-22 17:05:36 -0500142
Brandon Wyman654eb6e2018-02-14 14:55:47 -0600143 if (powerOn && (inputFault == 0) && !faultFound)
Brandon Wyman764c7972017-08-22 17:05:36 -0500144 {
Brandon Wyman12661f12017-08-31 15:28:21 -0500145 checkFanFault(statusWord);
Brandon Wyman875b3632017-09-13 18:46:03 -0500146 checkTemperatureFault(statusWord);
Brandon Wymancfa032b2017-09-25 17:37:50 -0500147 checkOutputOvervoltageFault(statusWord);
148 checkCurrentOutOverCurrentFault(statusWord);
149 checkPGOrUnitOffFault(statusWord);
Brandon Wyman442035f2017-08-08 15:58:45 -0500150 }
Matt Spinlereb169fd2018-01-18 14:19:08 -0600151
152 updateHistory();
Brandon Wyman442035f2017-08-08 15:58:45 -0500153 }
154 }
155 catch (ReadFailure& e)
156 {
Brandon Wymane4af9802017-11-13 15:58:33 -0600157 if (readFail < FAULT_COUNT)
158 {
159 readFail++;
160 }
161
162 if (!readFailLogged && readFail >= FAULT_COUNT)
Brandon Wyman442035f2017-08-08 15:58:45 -0500163 {
164 commit<ReadFailure>();
165 readFailLogged = true;
Brandon Wyman442035f2017-08-08 15:58:45 -0500166 }
167 }
168
Brandon Wyman1db9a9e2017-07-26 18:50:22 -0500169 return;
170}
171
Brandon Wyman10295542017-08-09 18:20:44 -0500172void PowerSupply::inventoryChanged(sdbusplus::message::message& msg)
173{
174 std::string msgSensor;
175 std::map<std::string, sdbusplus::message::variant<uint32_t, bool>> msgData;
176 msg.read(msgSensor, msgData);
177
178 // Check if it was the Present property that changed.
179 auto valPropMap = msgData.find(PRESENT_PROP);
180 if (valPropMap != msgData.end())
181 {
Brandon Wyman2ef48cf2017-11-21 15:43:54 -0600182 if (sdbusplus::message::variant_ns::get<bool>(valPropMap->second))
Brandon Wyman10295542017-08-09 18:20:44 -0500183 {
Brandon Wyman6ccce0b2017-10-26 15:13:10 -0500184 clearFaults();
William A. Kennington III1a0c9172018-10-18 17:57:49 -0700185 presentTimer.restartOnce(presentInterval);
Brandon Wyman590fc282017-11-01 18:22:25 -0500186 }
187 else
188 {
Brandon Wyman2ef48cf2017-11-21 15:43:54 -0600189 present = false;
William A. Kennington III1a0c9172018-10-18 17:57:49 -0700190 presentTimer.setEnabled(false);
Matt Spinler234ce0d2018-01-04 15:06:57 -0600191
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500192 // Clear out the now outdated inventory properties
Matt Spinler234ce0d2018-01-04 15:06:57 -0600193 updateInventory();
Brandon Wyman10295542017-08-09 18:20:44 -0500194 }
195 }
196
197 return;
198}
199
200void PowerSupply::updatePresence()
201{
202 // Use getProperty utility function to get presence status.
Brandon Wyman10295542017-08-09 18:20:44 -0500203 std::string service = "xyz.openbmc_project.Inventory.Manager";
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500204 util::getProperty(INVENTORY_IFACE, PRESENT_PROP, inventoryPath, service,
205 bus, this->present);
Brandon Wyman10295542017-08-09 18:20:44 -0500206}
207
Brandon Wyman431fbe42017-08-18 16:22:09 -0500208void PowerSupply::powerStateChanged(sdbusplus::message::message& msg)
209{
210 int32_t state = 0;
211 std::string msgSensor;
212 std::map<std::string, sdbusplus::message::variant<int32_t, int32_t>>
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500213 msgData;
Brandon Wyman431fbe42017-08-18 16:22:09 -0500214 msg.read(msgSensor, msgData);
215
216 // Check if it was the Present property that changed.
217 auto valPropMap = msgData.find("state");
218 if (valPropMap != msgData.end())
219 {
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500220 state =
221 sdbusplus::message::variant_ns::get<int32_t>(valPropMap->second);
Brandon Wyman431fbe42017-08-18 16:22:09 -0500222
223 // Power is on when state=1. Set the fault logged variables to false
224 // and start the power on timer when the state changes to 1.
225 if (state)
226 {
Brandon Wyman6ccce0b2017-10-26 15:13:10 -0500227 clearFaults();
William A. Kennington III1a0c9172018-10-18 17:57:49 -0700228 powerOnTimer.restartOnce(powerOnInterval);
Brandon Wyman431fbe42017-08-18 16:22:09 -0500229 }
230 else
231 {
William A. Kennington III1a0c9172018-10-18 17:57:49 -0700232 powerOnTimer.setEnabled(false);
Brandon Wyman431fbe42017-08-18 16:22:09 -0500233 powerOn = false;
234 }
235 }
Brandon Wyman431fbe42017-08-18 16:22:09 -0500236}
237
238void PowerSupply::updatePowerState()
239{
240 // When state = 1, system is powered on
241 int32_t state = 0;
242
243 try
244 {
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500245 auto service = util::getService(POWER_OBJ_PATH, POWER_IFACE, bus);
Brandon Wyman431fbe42017-08-18 16:22:09 -0500246
247 // Use getProperty utility function to get power state.
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500248 util::getProperty<int32_t>(POWER_IFACE, "state", POWER_OBJ_PATH,
249 service, bus, state);
Brandon Wyman431fbe42017-08-18 16:22:09 -0500250
251 if (state)
252 {
253 powerOn = true;
254 }
255 else
256 {
257 powerOn = false;
258 }
259 }
260 catch (std::exception& e)
261 {
262 log<level::INFO>("Failed to get power state. Assuming it is off.");
263 powerOn = false;
264 }
Brandon Wyman431fbe42017-08-18 16:22:09 -0500265}
266
Brandon Wyman603cc002017-08-28 18:17:58 -0500267void PowerSupply::checkInputFault(const uint16_t statusWord)
268{
269 using namespace witherspoon::pmbus;
270
Brandon Wymana3c675c2017-11-14 14:54:54 -0600271 if ((inputFault < FAULT_COUNT) &&
272 ((statusWord & status_word::INPUT_FAULT_WARN) ||
273 (statusWord & status_word::VIN_UV_FAULT)))
Brandon Wyman603cc002017-08-28 18:17:58 -0500274 {
Brandon Wymanad708242018-01-19 17:28:35 -0600275 if (inputFault == 0)
276 {
277 log<level::INFO>("INPUT or VIN_UV fault",
278 entry("STATUS_WORD=0x%04X", statusWord));
279 }
280
Brandon Wymana3c675c2017-11-14 14:54:54 -0600281 inputFault++;
Brandon Wyman603cc002017-08-28 18:17:58 -0500282 }
283 else
284 {
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500285 if ((inputFault > 0) && !(statusWord & status_word::INPUT_FAULT_WARN) &&
Brandon Wymand20686a2017-11-01 17:45:23 -0500286 !(statusWord & status_word::VIN_UV_FAULT))
Brandon Wyman603cc002017-08-28 18:17:58 -0500287 {
Brandon Wymana3c675c2017-11-14 14:54:54 -0600288 inputFault = 0;
Brandon Wyman3343e822017-11-03 16:54:11 -0500289 faultFound = false;
Brandon Wyman7502f6c2018-02-12 20:20:39 -0600290 // When an input fault occurs, the power supply cannot be on.
291 // However, the check for the case where the power supply should be
292 // on will stop when there is a fault found.
293 // Clear the powerOnFault when the inputFault is cleared to reset
294 // the powerOnFault de-glitching.
295 powerOnFault = 0;
Brandon Wyman69591bd2017-11-01 18:07:23 -0500296
Brandon Wyman603cc002017-08-28 18:17:58 -0500297 log<level::INFO>("INPUT_FAULT_WARN cleared",
Brandon Wymana3c675c2017-11-14 14:54:54 -0600298 entry("POWERSUPPLY=%s", inventoryPath.c_str()));
Brandon Wyman69591bd2017-11-01 18:07:23 -0500299
Brandon Wyman08b05712017-11-30 17:53:56 -0600300 resolveError(inventoryPath,
301 std::string(PowerSupplyInputFault::errName));
302
Brandon Wyman69591bd2017-11-01 18:07:23 -0500303 if (powerOn)
304 {
305 // The power supply will not be immediately powered on after
306 // the input power is restored.
307 powerOn = false;
308 // Start up the timer that will set the state to indicate we
309 // are ready for the powered on fault checks.
William A. Kennington III1a0c9172018-10-18 17:57:49 -0700310 powerOnTimer.restartOnce(powerOnInterval);
Brandon Wyman69591bd2017-11-01 18:07:23 -0500311 }
Brandon Wyman603cc002017-08-28 18:17:58 -0500312 }
313 }
Brandon Wymana3c675c2017-11-14 14:54:54 -0600314
315 if (!faultFound && (inputFault >= FAULT_COUNT))
316 {
Brandon Wymanad708242018-01-19 17:28:35 -0600317 // If the power is on, report the fault in an error log entry.
318 if (powerOn)
319 {
320 util::NamesValues nv;
321 nv.add("STATUS_WORD", statusWord);
322 captureCmd(nv, STATUS_INPUT, Type::Debug);
Brandon Wymana3c675c2017-11-14 14:54:54 -0600323
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500324 using metadata =
325 org::open_power::Witherspoon::Fault::PowerSupplyInputFault;
Brandon Wymana3c675c2017-11-14 14:54:54 -0600326
Brandon Wymanad708242018-01-19 17:28:35 -0600327 report<PowerSupplyInputFault>(
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500328 metadata::RAW_STATUS(nv.get().c_str()),
329 metadata::CALLOUT_INVENTORY_PATH(inventoryPath.c_str()));
Brandon Wymanad708242018-01-19 17:28:35 -0600330
331 faultFound = true;
332 }
Brandon Wymana3c675c2017-11-14 14:54:54 -0600333 }
Brandon Wyman603cc002017-08-28 18:17:58 -0500334}
335
336void PowerSupply::checkPGOrUnitOffFault(const uint16_t statusWord)
337{
338 using namespace witherspoon::pmbus;
339
Brandon Wyman593d24f2017-10-13 18:15:23 -0500340 if (powerOnFault < FAULT_COUNT)
Brandon Wyman603cc002017-08-28 18:17:58 -0500341 {
Brandon Wyman593d24f2017-10-13 18:15:23 -0500342 // Check PG# and UNIT_IS_OFF
343 if ((statusWord & status_word::POWER_GOOD_NEGATED) ||
344 (statusWord & status_word::UNIT_IS_OFF))
345 {
346 log<level::INFO>("PGOOD or UNIT_IS_OFF bit bad",
347 entry("STATUS_WORD=0x%04X", statusWord));
348 powerOnFault++;
349 }
350 else
351 {
352 if (powerOnFault > 0)
353 {
354 log<level::INFO>("PGOOD and UNIT_IS_OFF bits good");
355 powerOnFault = 0;
356 }
357 }
Brandon Wyman603cc002017-08-28 18:17:58 -0500358
Brandon Wymane2fc7aa2017-11-13 17:37:10 -0600359 if (!faultFound && (powerOnFault >= FAULT_COUNT))
Brandon Wyman593d24f2017-10-13 18:15:23 -0500360 {
Brandon Wyman3343e822017-11-03 16:54:11 -0500361 faultFound = true;
362
Brandon Wyman593d24f2017-10-13 18:15:23 -0500363 util::NamesValues nv;
364 nv.add("STATUS_WORD", statusWord);
365 captureCmd(nv, STATUS_INPUT, Type::Debug);
366 auto status0Vout = pmbusIntf.insertPageNum(STATUS_VOUT, 0);
367 captureCmd(nv, status0Vout, Type::Debug);
368 captureCmd(nv, STATUS_IOUT, Type::Debug);
369 captureCmd(nv, STATUS_MFR, Type::Debug);
Brandon Wyman603cc002017-08-28 18:17:58 -0500370
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500371 using metadata =
372 org::open_power::Witherspoon::Fault::PowerSupplyShouldBeOn;
Brandon Wyman603cc002017-08-28 18:17:58 -0500373
Brandon Wyman593d24f2017-10-13 18:15:23 -0500374 // A power supply is OFF (or pgood low) but should be on.
375 report<PowerSupplyShouldBeOn>(
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500376 metadata::RAW_STATUS(nv.get().c_str()),
377 metadata::CALLOUT_INVENTORY_PATH(inventoryPath.c_str()));
Brandon Wyman593d24f2017-10-13 18:15:23 -0500378 }
Brandon Wyman603cc002017-08-28 18:17:58 -0500379 }
Brandon Wyman603cc002017-08-28 18:17:58 -0500380}
381
382void PowerSupply::checkCurrentOutOverCurrentFault(const uint16_t statusWord)
383{
384 using namespace witherspoon::pmbus;
385
Brandon Wymandd61be42017-11-07 18:38:54 -0600386 if (outputOCFault < FAULT_COUNT)
Brandon Wyman603cc002017-08-28 18:17:58 -0500387 {
Brandon Wymandd61be42017-11-07 18:38:54 -0600388 // Check for an output overcurrent fault.
389 if ((statusWord & status_word::IOUT_OC_FAULT))
390 {
391 outputOCFault++;
392 }
393 else
394 {
395 if (outputOCFault > 0)
396 {
397 outputOCFault = 0;
398 }
399 }
Brandon Wyman603cc002017-08-28 18:17:58 -0500400
Brandon Wymane2fc7aa2017-11-13 17:37:10 -0600401 if (!faultFound && (outputOCFault >= FAULT_COUNT))
Brandon Wymandd61be42017-11-07 18:38:54 -0600402 {
403 util::NamesValues nv;
404 nv.add("STATUS_WORD", statusWord);
405 captureCmd(nv, STATUS_INPUT, Type::Debug);
406 auto status0Vout = pmbusIntf.insertPageNum(STATUS_VOUT, 0);
407 captureCmd(nv, status0Vout, Type::Debug);
408 captureCmd(nv, STATUS_IOUT, Type::Debug);
409 captureCmd(nv, STATUS_MFR, Type::Debug);
Brandon Wyman603cc002017-08-28 18:17:58 -0500410
Brandon Wymandd61be42017-11-07 18:38:54 -0600411 using metadata = org::open_power::Witherspoon::Fault::
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500412 PowerSupplyOutputOvercurrent;
Brandon Wyman603cc002017-08-28 18:17:58 -0500413
Brandon Wymandd61be42017-11-07 18:38:54 -0600414 report<PowerSupplyOutputOvercurrent>(
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500415 metadata::RAW_STATUS(nv.get().c_str()),
416 metadata::CALLOUT_INVENTORY_PATH(inventoryPath.c_str()));
Brandon Wymandd61be42017-11-07 18:38:54 -0600417
418 faultFound = true;
419 }
Brandon Wyman603cc002017-08-28 18:17:58 -0500420 }
421}
422
Brandon Wymanab05c072017-08-30 18:26:41 -0500423void PowerSupply::checkOutputOvervoltageFault(const uint16_t statusWord)
424{
425 using namespace witherspoon::pmbus;
426
Brandon Wyman2ab319b2017-11-08 17:34:59 -0600427 if (outputOVFault < FAULT_COUNT)
Brandon Wymanab05c072017-08-30 18:26:41 -0500428 {
Brandon Wyman2ab319b2017-11-08 17:34:59 -0600429 // Check for an output overvoltage fault.
430 if (statusWord & status_word::VOUT_OV_FAULT)
431 {
432 outputOVFault++;
433 }
434 else
435 {
436 if (outputOVFault > 0)
437 {
438 outputOVFault = 0;
439 }
440 }
Brandon Wymanab05c072017-08-30 18:26:41 -0500441
Brandon Wymane2fc7aa2017-11-13 17:37:10 -0600442 if (!faultFound && (outputOVFault >= FAULT_COUNT))
Brandon Wyman2ab319b2017-11-08 17:34:59 -0600443 {
444 util::NamesValues nv;
445 nv.add("STATUS_WORD", statusWord);
446 captureCmd(nv, STATUS_INPUT, Type::Debug);
447 auto status0Vout = pmbusIntf.insertPageNum(STATUS_VOUT, 0);
448 captureCmd(nv, status0Vout, Type::Debug);
449 captureCmd(nv, STATUS_IOUT, Type::Debug);
450 captureCmd(nv, STATUS_MFR, Type::Debug);
Brandon Wymanab05c072017-08-30 18:26:41 -0500451
Brandon Wyman2ab319b2017-11-08 17:34:59 -0600452 using metadata = org::open_power::Witherspoon::Fault::
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500453 PowerSupplyOutputOvervoltage;
Brandon Wymanab05c072017-08-30 18:26:41 -0500454
Brandon Wyman2ab319b2017-11-08 17:34:59 -0600455 report<PowerSupplyOutputOvervoltage>(
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500456 metadata::RAW_STATUS(nv.get().c_str()),
457 metadata::CALLOUT_INVENTORY_PATH(inventoryPath.c_str()));
Brandon Wyman2ab319b2017-11-08 17:34:59 -0600458
459 faultFound = true;
460 }
Brandon Wymanab05c072017-08-30 18:26:41 -0500461 }
462}
463
Brandon Wyman12661f12017-08-31 15:28:21 -0500464void PowerSupply::checkFanFault(const uint16_t statusWord)
465{
466 using namespace witherspoon::pmbus;
467
Brandon Wymanba255532017-11-08 17:44:10 -0600468 if (fanFault < FAULT_COUNT)
Brandon Wyman12661f12017-08-31 15:28:21 -0500469 {
Brandon Wymanba255532017-11-08 17:44:10 -0600470 // Check for a fan fault or warning condition
471 if (statusWord & status_word::FAN_FAULT)
472 {
473 fanFault++;
474 }
475 else
476 {
477 if (fanFault > 0)
478 {
479 fanFault = 0;
480 }
481 }
Brandon Wyman12661f12017-08-31 15:28:21 -0500482
Brandon Wymane2fc7aa2017-11-13 17:37:10 -0600483 if (!faultFound && (fanFault >= FAULT_COUNT))
Brandon Wymanba255532017-11-08 17:44:10 -0600484 {
485 util::NamesValues nv;
486 nv.add("STATUS_WORD", statusWord);
487 captureCmd(nv, STATUS_MFR, Type::Debug);
488 captureCmd(nv, STATUS_TEMPERATURE, Type::Debug);
489 captureCmd(nv, STATUS_FANS_1_2, Type::Debug);
Brandon Wyman12661f12017-08-31 15:28:21 -0500490
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500491 using metadata =
492 org::open_power::Witherspoon::Fault::PowerSupplyFanFault;
Brandon Wyman12661f12017-08-31 15:28:21 -0500493
Brandon Wymanba255532017-11-08 17:44:10 -0600494 report<PowerSupplyFanFault>(
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500495 metadata::RAW_STATUS(nv.get().c_str()),
496 metadata::CALLOUT_INVENTORY_PATH(inventoryPath.c_str()));
Brandon Wymanba255532017-11-08 17:44:10 -0600497
498 faultFound = true;
499 }
Brandon Wyman12661f12017-08-31 15:28:21 -0500500 }
501}
502
Brandon Wyman875b3632017-09-13 18:46:03 -0500503void PowerSupply::checkTemperatureFault(const uint16_t statusWord)
504{
505 using namespace witherspoon::pmbus;
506
507 // Due to how the PMBus core device driver sends a clear faults command
508 // the bit in STATUS_WORD will likely be cleared when we attempt to examine
509 // it for a Thermal Fault or Warning. So, check the STATUS_WORD and the
510 // STATUS_TEMPERATURE bits. If either indicates a fault, proceed with
511 // logging the over-temperature condition.
512 std::uint8_t statusTemperature = 0;
513 statusTemperature = pmbusIntf.read(STATUS_TEMPERATURE, Type::Debug);
Brandon Wyman50044ea2017-11-08 17:58:56 -0600514 if (temperatureFault < FAULT_COUNT)
Brandon Wyman875b3632017-09-13 18:46:03 -0500515 {
Brandon Wyman50044ea2017-11-08 17:58:56 -0600516 if ((statusWord & status_word::TEMPERATURE_FAULT_WARN) ||
517 (statusTemperature & status_temperature::OT_FAULT))
518 {
519 temperatureFault++;
520 }
521 else
522 {
523 if (temperatureFault > 0)
524 {
525 temperatureFault = 0;
526 }
527 }
Brandon Wyman875b3632017-09-13 18:46:03 -0500528
Brandon Wymane2fc7aa2017-11-13 17:37:10 -0600529 if (!faultFound && (temperatureFault >= FAULT_COUNT))
Brandon Wyman50044ea2017-11-08 17:58:56 -0600530 {
531 // The power supply has had an over-temperature condition.
532 // This may not result in a shutdown if experienced for a short
533 // duration.
534 // This should not occur under normal conditions.
535 // The power supply may be faulty, or the paired supply may be
536 // putting out less current.
537 // Capture command responses with potentially relevant information,
538 // and call out the power supply reporting the condition.
539 util::NamesValues nv;
540 nv.add("STATUS_WORD", statusWord);
541 captureCmd(nv, STATUS_MFR, Type::Debug);
542 captureCmd(nv, STATUS_IOUT, Type::Debug);
543 nv.add("STATUS_TEMPERATURE", statusTemperature);
544 captureCmd(nv, STATUS_FANS_1_2, Type::Debug);
Brandon Wyman875b3632017-09-13 18:46:03 -0500545
Brandon Wyman50044ea2017-11-08 17:58:56 -0600546 using metadata = org::open_power::Witherspoon::Fault::
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500547 PowerSupplyTemperatureFault;
Brandon Wyman875b3632017-09-13 18:46:03 -0500548
Brandon Wyman50044ea2017-11-08 17:58:56 -0600549 report<PowerSupplyTemperatureFault>(
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500550 metadata::RAW_STATUS(nv.get().c_str()),
551 metadata::CALLOUT_INVENTORY_PATH(inventoryPath.c_str()));
Brandon Wyman50044ea2017-11-08 17:58:56 -0600552
553 faultFound = true;
554 }
Brandon Wyman875b3632017-09-13 18:46:03 -0500555 }
556}
557
Brandon Wyman1db9a9e2017-07-26 18:50:22 -0500558void PowerSupply::clearFaults()
559{
Brandon Wymane4af9802017-11-13 15:58:33 -0600560 readFail = 0;
Brandon Wyman6ccce0b2017-10-26 15:13:10 -0500561 readFailLogged = false;
Brandon Wymana3c675c2017-11-14 14:54:54 -0600562 inputFault = 0;
Brandon Wyman6ccce0b2017-10-26 15:13:10 -0500563 powerOnFault = 0;
Brandon Wymandd61be42017-11-07 18:38:54 -0600564 outputOCFault = 0;
Brandon Wyman2ab319b2017-11-08 17:34:59 -0600565 outputOVFault = 0;
Brandon Wymanba255532017-11-08 17:44:10 -0600566 fanFault = 0;
Brandon Wyman50044ea2017-11-08 17:58:56 -0600567 temperatureFault = 0;
Brandon Wyman3343e822017-11-03 16:54:11 -0500568 faultFound = false;
Brandon Wyman6ccce0b2017-10-26 15:13:10 -0500569
Brandon Wyman1db9a9e2017-07-26 18:50:22 -0500570 return;
571}
572
Brandon Wyman43ce2082017-11-30 17:24:01 -0600573void PowerSupply::resolveError(const std::string& callout,
574 const std::string& message)
575{
Brandon Wyman01741f12017-12-01 17:22:08 -0600576 using EndpointList = std::vector<std::string>;
577
578 try
579 {
580 auto path = callout + "/fault";
581 // Get the service name from the mapper for the fault callout
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500582 auto service = util::getService(path, ASSOCIATION_IFACE, bus);
Brandon Wyman01741f12017-12-01 17:22:08 -0600583
584 // Use getProperty utility function to get log entries (endpoints)
585 EndpointList logEntries;
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500586 util::getProperty(ASSOCIATION_IFACE, ENDPOINTS_PROP, path, service, bus,
587 logEntries);
Brandon Wyman01741f12017-12-01 17:22:08 -0600588
589 // It is possible that all such entries for this callout have since
590 // been deleted.
591 if (logEntries.empty())
592 {
593 return;
594 }
595
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500596 auto logEntryService =
597 util::getService(logEntries[0], LOGGING_IFACE, bus);
Brandon Wyman01741f12017-12-01 17:22:08 -0600598 if (logEntryService.empty())
599 {
600 return;
601 }
602
603 // go through each log entry that matches this callout path
604 std::string logMessage;
605 for (const auto& logEntry : logEntries)
606 {
607 // Check to see if this logEntry has a message that matches.
Matt Spinler589e8722018-01-04 15:24:49 -0600608 util::getProperty(LOGGING_IFACE, MESSAGE_PROP, logEntry,
Brandon Wyman01741f12017-12-01 17:22:08 -0600609 logEntryService, bus, logMessage);
610
611 if (message == logMessage)
612 {
613 // Log entry matches call out and message, set Resolved to true
614 bool resolved = true;
Matt Spinler589e8722018-01-04 15:24:49 -0600615 util::setProperty(LOGGING_IFACE, RESOLVED_PROP, logEntry,
Brandon Wyman01741f12017-12-01 17:22:08 -0600616 logEntryService, bus, resolved);
617 }
Brandon Wyman01741f12017-12-01 17:22:08 -0600618 }
Brandon Wyman01741f12017-12-01 17:22:08 -0600619 }
620 catch (std::exception& e)
621 {
622 log<level::INFO>("Failed to resolve error",
623 entry("CALLOUT=%s", callout.c_str()),
Matt Spinler0d09f292018-01-22 14:51:26 -0600624 entry("ERROR=%s", message.c_str()));
Brandon Wyman01741f12017-12-01 17:22:08 -0600625 }
Brandon Wyman43ce2082017-11-30 17:24:01 -0600626}
627
Matt Spinler234ce0d2018-01-04 15:06:57 -0600628void PowerSupply::updateInventory()
629{
Matt Spinler018a7bc2018-01-04 15:36:41 -0600630 using namespace witherspoon::pmbus;
631 using namespace sdbusplus::message;
632
633 // If any of these accesses fail, the fields will just be
634 // blank in the inventory. Leave logging ReadFailure errors
635 // to analyze() as it runs continuously and will most
636 // likely hit and threshold them first anyway. The
637 // readString() function will do the tracing of the failing
638 // path so this code doesn't need to.
639 std::string pn;
640 std::string sn;
641 std::string ccin;
642 std::string version;
643
644 if (present)
645 {
646 try
647 {
648 sn = pmbusIntf.readString(SERIAL_NUMBER, Type::HwmonDeviceDebug);
649 }
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500650 catch (ReadFailure& e)
651 {
652 }
Matt Spinler018a7bc2018-01-04 15:36:41 -0600653
654 try
655 {
656 pn = pmbusIntf.readString(PART_NUMBER, Type::HwmonDeviceDebug);
657 }
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500658 catch (ReadFailure& e)
659 {
660 }
Matt Spinler018a7bc2018-01-04 15:36:41 -0600661
662 try
663 {
664 ccin = pmbusIntf.readString(CCIN, 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 version = pmbusIntf.readString(FW_VERSION, 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
679 // Build the object map and send it to the inventory
680 using Properties = std::map<std::string, variant<std::string>>;
681 using Interfaces = std::map<std::string, Properties>;
682 using Object = std::map<object_path, Interfaces>;
683 Properties assetProps;
684 Properties versionProps;
685 Interfaces interfaces;
686 Object object;
687
688 assetProps.emplace(SN_PROP, sn);
689 assetProps.emplace(PN_PROP, pn);
690 assetProps.emplace(MODEL_PROP, ccin);
691 interfaces.emplace(ASSET_IFACE, std::move(assetProps));
692
693 versionProps.emplace(VERSION_PROP, version);
694 interfaces.emplace(VERSION_IFACE, std::move(versionProps));
695
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500696 // For Notify(), just send the relative path of the inventory
697 // object so remove the INVENTORY_OBJ_PATH prefix
Matt Spinler018a7bc2018-01-04 15:36:41 -0600698 auto path = inventoryPath.substr(strlen(INVENTORY_OBJ_PATH));
699
700 object.emplace(path, std::move(interfaces));
701
702 try
703 {
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500704 auto service =
705 util::getService(INVENTORY_OBJ_PATH, INVENTORY_MGR_IFACE, bus);
Matt Spinler018a7bc2018-01-04 15:36:41 -0600706
707 if (service.empty())
708 {
709 log<level::ERR>("Unable to get inventory manager service");
710 return;
711 }
712
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500713 auto method = bus.new_method_call(service.c_str(), INVENTORY_OBJ_PATH,
714 INVENTORY_MGR_IFACE, "Notify");
Matt Spinler018a7bc2018-01-04 15:36:41 -0600715
716 method.append(std::move(object));
717
718 auto reply = bus.call(method);
719 if (reply.is_method_error())
720 {
721 log<level::ERR>(
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500722 "Unable to update power supply inventory properties",
723 entry("PATH=%s", path.c_str()));
Matt Spinler018a7bc2018-01-04 15:36:41 -0600724 }
725
726 // TODO: openbmc/openbmc#2756
727 // Calling Notify() with an enumerated property crashes inventory
728 // manager, so let it default to Unknown and now set it to the
729 // right value.
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500730 auto purpose =
731 version::convertForMessage(version::Version::VersionPurpose::Other);
Matt Spinler018a7bc2018-01-04 15:36:41 -0600732
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500733 util::setProperty(VERSION_IFACE, VERSION_PURPOSE_PROP, inventoryPath,
734 service, bus, purpose);
Matt Spinler018a7bc2018-01-04 15:36:41 -0600735 }
736 catch (std::exception& e)
737 {
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500738 log<level::ERR>(e.what(), entry("PATH=%s", inventoryPath.c_str()));
Matt Spinler018a7bc2018-01-04 15:36:41 -0600739 }
Matt Spinler234ce0d2018-01-04 15:06:57 -0600740}
741
Matt Spinlerd734e652018-01-18 14:31:15 -0600742void PowerSupply::syncHistory()
743{
744 using namespace witherspoon::gpio;
745
746 if (syncGPIODevPath.empty())
747 {
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500748 // Sync not implemented
Matt Spinlerd734e652018-01-18 14:31:15 -0600749 return;
750 }
751
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500752 GPIO gpio{syncGPIODevPath, static_cast<gpioNum_t>(syncGPIONumber),
Matt Spinlerd734e652018-01-18 14:31:15 -0600753 Direction::output};
754
755 try
756 {
757 gpio.set(Value::low);
758
759 std::this_thread::sleep_for(std::chrono::milliseconds{5});
760
761 gpio.set(Value::high);
762
763 recordManager->clear();
764 }
765 catch (std::exception& e)
766 {
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500767 // Do nothing. There would already be a journal entry.
Matt Spinlerd734e652018-01-18 14:31:15 -0600768 }
769}
770
Matt Spinler82384142018-01-18 14:15:03 -0600771void PowerSupply::enableHistory(const std::string& objectPath,
772 size_t numRecords,
773 const std::string& syncGPIOPath,
774 size_t syncGPIONum)
775{
776 historyObjectPath = objectPath;
777 syncGPIODevPath = syncGPIOPath;
778 syncGPIONumber = syncGPIONum;
779
780 recordManager = std::make_unique<history::RecordManager>(numRecords);
781
782 auto avgPath = historyObjectPath + '/' + history::Average::name;
783 auto maxPath = historyObjectPath + '/' + history::Maximum::name;
784
785 average = std::make_unique<history::Average>(bus, avgPath);
786
787 maximum = std::make_unique<history::Maximum>(bus, maxPath);
788}
789
Matt Spinlereb169fd2018-01-18 14:19:08 -0600790void PowerSupply::updateHistory()
791{
792 if (!recordManager)
793 {
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500794 // Not enabled
Matt Spinlereb169fd2018-01-18 14:19:08 -0600795 return;
796 }
797
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500798 // Read just the most recent average/max record
799 auto data =
800 pmbusIntf.readBinary(INPUT_HISTORY, pmbus::Type::HwmonDeviceDebug,
801 history::RecordManager::RAW_RECORD_SIZE);
Matt Spinlereb169fd2018-01-18 14:19:08 -0600802
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500803 // Update D-Bus only if something changed (a new record ID, or cleared out)
Matt Spinlereb169fd2018-01-18 14:19:08 -0600804 auto changed = recordManager->add(data);
805 if (changed)
806 {
807 average->values(std::move(recordManager->getAverageRecords()));
808 maximum->values(std::move(recordManager->getMaximumRecords()));
809 }
810}
811
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500812} // namespace psu
813} // namespace power
814} // namespace witherspoon