blob: f8c86e303eeb5200519813138d6f4a91c5821b4e [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 */
Brandon Wyman442035f2017-08-08 15:58:45 -050016#include <phosphor-logging/log.hpp>
17#include <phosphor-logging/elog.hpp>
Brandon Wymane0eb45c2017-10-06 12:58:42 -050018#include <org/open_power/Witherspoon/Fault/error.hpp>
Matt Spinlerceacf942017-10-05 13:55:02 -050019#include <xyz/openbmc_project/Common/Device/error.hpp>
Matt Spinler018a7bc2018-01-04 15:36:41 -060020#include <xyz/openbmc_project/Software/Version/server.hpp>
Brandon Wyman442035f2017-08-08 15:58:45 -050021#include "elog-errors.hpp"
Brandon Wyman10295542017-08-09 18:20:44 -050022#include "names_values.hpp"
Brandon Wyman24e422f2017-07-25 19:40:14 -050023#include "power_supply.hpp"
Brandon Wyman442035f2017-08-08 15:58:45 -050024#include "pmbus.hpp"
25#include "utility.hpp"
26
Brandon Wyman1db9a9e2017-07-26 18:50:22 -050027namespace witherspoon
Brandon Wyman24e422f2017-07-25 19:40:14 -050028{
29namespace power
30{
31namespace psu
32{
33
Matt Spinler589e8722018-01-04 15:24:49 -060034using namespace phosphor::logging;
35using namespace sdbusplus::org::open_power::Witherspoon::Fault::Error;
36using namespace sdbusplus::xyz::openbmc_project::Common::Device::Error;
Matt Spinler018a7bc2018-01-04 15:36:41 -060037namespace version = sdbusplus::xyz::openbmc_project::Software::server;
Matt Spinler589e8722018-01-04 15:24:49 -060038
39constexpr auto ASSOCIATION_IFACE = "org.openbmc.Association";
40constexpr auto LOGGING_IFACE = "xyz.openbmc_project.Logging.Entry";
41constexpr auto INVENTORY_IFACE = "xyz.openbmc_project.Inventory.Item";
42constexpr auto POWER_IFACE = "org.openbmc.control.Power";
Matt Spinler018a7bc2018-01-04 15:36:41 -060043constexpr auto INVENTORY_MGR_IFACE = "xyz.openbmc_project.Inventory.Manager";
44constexpr auto ASSET_IFACE = "xyz.openbmc_project.Inventory.Decorator.Asset";
45constexpr auto VERSION_IFACE = "xyz.openbmc_project.Software.Version";
Matt Spinler589e8722018-01-04 15:24:49 -060046
47constexpr auto ENDPOINTS_PROP = "endpoints";
48constexpr auto MESSAGE_PROP = "Message";
49constexpr auto RESOLVED_PROP = "Resolved";
Brandon Wyman10295542017-08-09 18:20:44 -050050constexpr auto PRESENT_PROP = "Present";
Matt Spinler018a7bc2018-01-04 15:36:41 -060051constexpr auto SN_PROP = "SerialNumber";
52constexpr auto PN_PROP = "PartNumber";
53constexpr auto MODEL_PROP = "Model";
54constexpr auto VERSION_PROP = "Version";
55constexpr auto VERSION_PURPOSE_PROP = "Purpose";
Matt Spinler589e8722018-01-04 15:24:49 -060056
57constexpr auto INVENTORY_OBJ_PATH = "/xyz/openbmc_project/inventory";
Brandon Wyman431fbe42017-08-18 16:22:09 -050058constexpr auto POWER_OBJ_PATH = "/org/openbmc/control/power0";
Brandon Wyman10295542017-08-09 18:20:44 -050059
Matt Spinler018a7bc2018-01-04 15:36:41 -060060constexpr auto SERIAL_NUMBER = "serial_number";
61constexpr auto PART_NUMBER = "part_number";
62constexpr auto FW_VERSION = "fw_version";
63constexpr auto CCIN = "ccin";
64
Brandon Wyman10295542017-08-09 18:20:44 -050065PowerSupply::PowerSupply(const std::string& name, size_t inst,
Brandon Wyman431fbe42017-08-18 16:22:09 -050066 const std::string& objpath,
67 const std::string& invpath,
68 sdbusplus::bus::bus& bus,
69 event::Event& e,
Brandon Wyman590fc282017-11-01 18:22:25 -050070 std::chrono::seconds& t,
71 std::chrono::seconds& p)
Brandon Wyman431fbe42017-08-18 16:22:09 -050072 : Device(name, inst), monitorPath(objpath), pmbusIntf(objpath),
Brandon Wyman50bb85d2017-11-01 18:36:00 -050073 inventoryPath(INVENTORY_OBJ_PATH + invpath), bus(bus), event(e),
74 presentInterval(p),
Brandon Wyman590fc282017-11-01 18:22:25 -050075 presentTimer(e, [this]()
76 {
Brandon Wyman2877add2017-11-10 17:44:19 -060077 // The hwmon path may have changed.
78 pmbusIntf.findHwmonDir();
Brandon Wyman590fc282017-11-01 18:22:25 -050079 this->present = true;
Matt Spinler234ce0d2018-01-04 15:06:57 -060080
81 // Update the inventory for the new device
82 updateInventory();
Brandon Wyman590fc282017-11-01 18:22:25 -050083 }),
84 powerOnInterval(t),
Brandon Wyman431fbe42017-08-18 16:22:09 -050085 powerOnTimer(e, [this]()
86 {
87 this->powerOn = true;
88 })
Brandon Wyman10295542017-08-09 18:20:44 -050089{
Brandon Wyman10295542017-08-09 18:20:44 -050090 using namespace sdbusplus::bus;
Brandon Wyman10295542017-08-09 18:20:44 -050091 presentMatch = std::make_unique<match_t>(bus,
92 match::rules::propertiesChanged(
Brandon Wyman50bb85d2017-11-01 18:36:00 -050093 inventoryPath,
Matt Spinler589e8722018-01-04 15:24:49 -060094 INVENTORY_IFACE),
Brandon Wyman10295542017-08-09 18:20:44 -050095 [this](auto& msg)
Brandon Wyman431fbe42017-08-18 16:22:09 -050096 {
97 this->inventoryChanged(msg);
98 });
99 // Get initial presence state.
Brandon Wyman253dc9b2017-08-12 13:45:52 -0500100 updatePresence();
Brandon Wyman431fbe42017-08-18 16:22:09 -0500101
Matt Spinler234ce0d2018-01-04 15:06:57 -0600102 // Write the SN, PN, etc to the inventory
103 updateInventory();
104
Brandon Wyman431fbe42017-08-18 16:22:09 -0500105 // Subscribe to power state changes
106 powerOnMatch = std::make_unique<match_t>(bus,
107 match::rules::propertiesChanged(
108 POWER_OBJ_PATH,
Matt Spinler589e8722018-01-04 15:24:49 -0600109 POWER_IFACE),
Brandon Wyman431fbe42017-08-18 16:22:09 -0500110 [this](auto& msg)
111 {
112 this->powerStateChanged(msg);
113 });
114 // Get initial power state.
115 updatePowerState();
Brandon Wyman10295542017-08-09 18:20:44 -0500116}
Brandon Wyman442035f2017-08-08 15:58:45 -0500117
Brandon Wymana1e96342017-09-25 16:47:44 -0500118void PowerSupply::captureCmd(util::NamesValues& nv, const std::string& cmd,
119 witherspoon::pmbus::Type type)
120{
121 if (pmbusIntf.exists(cmd, type))
122 {
123 try
124 {
125 auto val = pmbusIntf.read(cmd, type);
126 nv.add(cmd, val);
127 }
128 catch (std::exception& e)
129 {
130 log<level::INFO>("Unable to capture metadata", entry("CMD=%s",
131 cmd));
132 }
133 }
134}
Brandon Wyman431fbe42017-08-18 16:22:09 -0500135
Brandon Wyman1db9a9e2017-07-26 18:50:22 -0500136void PowerSupply::analyze()
137{
Brandon Wyman442035f2017-08-08 15:58:45 -0500138 using namespace witherspoon::pmbus;
139
140 try
141 {
Brandon Wyman10295542017-08-09 18:20:44 -0500142 if (present)
Brandon Wyman442035f2017-08-08 15:58:45 -0500143 {
Brandon Wyman764c7972017-08-22 17:05:36 -0500144 std::uint16_t statusWord = 0;
Brandon Wyman764c7972017-08-22 17:05:36 -0500145
146 // Read the 2 byte STATUS_WORD value to check for faults.
147 statusWord = pmbusIntf.read(STATUS_WORD, Type::Debug);
Brandon Wymane4af9802017-11-13 15:58:33 -0600148 readFail = 0;
Brandon Wyman764c7972017-08-22 17:05:36 -0500149
Brandon Wyman603cc002017-08-28 18:17:58 -0500150 checkInputFault(statusWord);
Brandon Wyman764c7972017-08-22 17:05:36 -0500151
Brandon Wyman3343e822017-11-03 16:54:11 -0500152 if (powerOn && !faultFound)
Brandon Wyman764c7972017-08-22 17:05:36 -0500153 {
Brandon Wyman12661f12017-08-31 15:28:21 -0500154 checkFanFault(statusWord);
Brandon Wyman875b3632017-09-13 18:46:03 -0500155 checkTemperatureFault(statusWord);
Brandon Wymancfa032b2017-09-25 17:37:50 -0500156 checkOutputOvervoltageFault(statusWord);
157 checkCurrentOutOverCurrentFault(statusWord);
158 checkPGOrUnitOffFault(statusWord);
Brandon Wyman442035f2017-08-08 15:58:45 -0500159 }
160 }
161 }
162 catch (ReadFailure& e)
163 {
Brandon Wymane4af9802017-11-13 15:58:33 -0600164 if (readFail < FAULT_COUNT)
165 {
166 readFail++;
167 }
168
169 if (!readFailLogged && readFail >= FAULT_COUNT)
Brandon Wyman442035f2017-08-08 15:58:45 -0500170 {
171 commit<ReadFailure>();
172 readFailLogged = true;
Brandon Wyman442035f2017-08-08 15:58:45 -0500173 }
174 }
175
Brandon Wyman1db9a9e2017-07-26 18:50:22 -0500176 return;
177}
178
Brandon Wyman10295542017-08-09 18:20:44 -0500179void PowerSupply::inventoryChanged(sdbusplus::message::message& msg)
180{
181 std::string msgSensor;
182 std::map<std::string, sdbusplus::message::variant<uint32_t, bool>> msgData;
183 msg.read(msgSensor, msgData);
184
185 // Check if it was the Present property that changed.
186 auto valPropMap = msgData.find(PRESENT_PROP);
187 if (valPropMap != msgData.end())
188 {
Brandon Wyman2ef48cf2017-11-21 15:43:54 -0600189 if (sdbusplus::message::variant_ns::get<bool>(valPropMap->second))
Brandon Wyman10295542017-08-09 18:20:44 -0500190 {
Brandon Wyman6ccce0b2017-10-26 15:13:10 -0500191 clearFaults();
Brandon Wyman590fc282017-11-01 18:22:25 -0500192 presentTimer.start(presentInterval, Timer::TimerType::oneshot);
193 }
194 else
195 {
Brandon Wyman2ef48cf2017-11-21 15:43:54 -0600196 present = false;
Brandon Wyman590fc282017-11-01 18:22:25 -0500197 presentTimer.stop();
Matt Spinler234ce0d2018-01-04 15:06:57 -0600198
199 //Clear out the now outdated inventory properties
200 updateInventory();
Brandon Wyman10295542017-08-09 18:20:44 -0500201 }
202 }
203
204 return;
205}
206
207void PowerSupply::updatePresence()
208{
209 // Use getProperty utility function to get presence status.
Brandon Wyman10295542017-08-09 18:20:44 -0500210 std::string service = "xyz.openbmc_project.Inventory.Manager";
Matt Spinler589e8722018-01-04 15:24:49 -0600211 util::getProperty(INVENTORY_IFACE, PRESENT_PROP, inventoryPath,
Brandon Wyman50bb85d2017-11-01 18:36:00 -0500212 service, bus, this->present);
Brandon Wyman10295542017-08-09 18:20:44 -0500213}
214
Brandon Wyman431fbe42017-08-18 16:22:09 -0500215void PowerSupply::powerStateChanged(sdbusplus::message::message& msg)
216{
217 int32_t state = 0;
218 std::string msgSensor;
219 std::map<std::string, sdbusplus::message::variant<int32_t, int32_t>>
220 msgData;
221 msg.read(msgSensor, msgData);
222
223 // Check if it was the Present property that changed.
224 auto valPropMap = msgData.find("state");
225 if (valPropMap != msgData.end())
226 {
227 state = sdbusplus::message::variant_ns::get<int32_t>(valPropMap->second);
228
229 // Power is on when state=1. Set the fault logged variables to false
230 // and start the power on timer when the state changes to 1.
231 if (state)
232 {
Brandon Wyman6ccce0b2017-10-26 15:13:10 -0500233 clearFaults();
Brandon Wyman431fbe42017-08-18 16:22:09 -0500234 powerOnTimer.start(powerOnInterval, Timer::TimerType::oneshot);
235 }
236 else
237 {
238 powerOnTimer.stop();
239 powerOn = false;
240 }
241 }
242
243}
244
245void PowerSupply::updatePowerState()
246{
247 // When state = 1, system is powered on
248 int32_t state = 0;
249
250 try
251 {
252 auto service = util::getService(POWER_OBJ_PATH,
Matt Spinler589e8722018-01-04 15:24:49 -0600253 POWER_IFACE,
Brandon Wyman431fbe42017-08-18 16:22:09 -0500254 bus);
255
256 // Use getProperty utility function to get power state.
Matt Spinler589e8722018-01-04 15:24:49 -0600257 util::getProperty<int32_t>(POWER_IFACE,
Brandon Wyman431fbe42017-08-18 16:22:09 -0500258 "state",
259 POWER_OBJ_PATH,
260 service,
261 bus,
262 state);
263
264 if (state)
265 {
266 powerOn = true;
267 }
268 else
269 {
270 powerOn = false;
271 }
272 }
273 catch (std::exception& e)
274 {
275 log<level::INFO>("Failed to get power state. Assuming it is off.");
276 powerOn = false;
277 }
278
279}
280
Brandon Wyman603cc002017-08-28 18:17:58 -0500281void PowerSupply::checkInputFault(const uint16_t statusWord)
282{
283 using namespace witherspoon::pmbus;
284
Brandon Wymana3c675c2017-11-14 14:54:54 -0600285 if ((inputFault < FAULT_COUNT) &&
286 ((statusWord & status_word::INPUT_FAULT_WARN) ||
287 (statusWord & status_word::VIN_UV_FAULT)))
Brandon Wyman603cc002017-08-28 18:17:58 -0500288 {
Brandon Wymana3c675c2017-11-14 14:54:54 -0600289 inputFault++;
Brandon Wyman603cc002017-08-28 18:17:58 -0500290 }
291 else
292 {
Brandon Wymana3c675c2017-11-14 14:54:54 -0600293 if ((inputFault > 0) &&
Brandon Wymand20686a2017-11-01 17:45:23 -0500294 !(statusWord & status_word::INPUT_FAULT_WARN) &&
295 !(statusWord & status_word::VIN_UV_FAULT))
Brandon Wyman603cc002017-08-28 18:17:58 -0500296 {
Brandon Wymana3c675c2017-11-14 14:54:54 -0600297 inputFault = 0;
Brandon Wyman3343e822017-11-03 16:54:11 -0500298 faultFound = false;
Brandon Wyman69591bd2017-11-01 18:07:23 -0500299
Brandon Wyman603cc002017-08-28 18:17:58 -0500300 log<level::INFO>("INPUT_FAULT_WARN cleared",
Brandon Wymana3c675c2017-11-14 14:54:54 -0600301 entry("POWERSUPPLY=%s", inventoryPath.c_str()));
Brandon Wyman69591bd2017-11-01 18:07:23 -0500302
Brandon Wyman08b05712017-11-30 17:53:56 -0600303 resolveError(inventoryPath,
304 std::string(PowerSupplyInputFault::errName));
305
Brandon Wyman69591bd2017-11-01 18:07:23 -0500306 if (powerOn)
307 {
308 // The power supply will not be immediately powered on after
309 // the input power is restored.
310 powerOn = false;
311 // Start up the timer that will set the state to indicate we
312 // are ready for the powered on fault checks.
313 powerOnTimer.start(powerOnInterval, Timer::TimerType::oneshot);
314 }
Brandon Wyman603cc002017-08-28 18:17:58 -0500315 }
316 }
Brandon Wymana3c675c2017-11-14 14:54:54 -0600317
318 if (!faultFound && (inputFault >= FAULT_COUNT))
319 {
320 util::NamesValues nv;
321 nv.add("STATUS_WORD", statusWord);
322 captureCmd(nv, STATUS_INPUT, Type::Debug);
323
324 using metadata = org::open_power::Witherspoon::Fault::
325 PowerSupplyInputFault;
326
327 report<PowerSupplyInputFault>(
328 metadata::RAW_STATUS(nv.get().c_str()),
329 metadata::CALLOUT_INVENTORY_PATH(inventoryPath.c_str()));
330 faultFound = true;
331 }
332
Brandon Wyman603cc002017-08-28 18:17:58 -0500333}
334
335void PowerSupply::checkPGOrUnitOffFault(const uint16_t statusWord)
336{
337 using namespace witherspoon::pmbus;
338
Brandon Wyman593d24f2017-10-13 18:15:23 -0500339 if (powerOnFault < FAULT_COUNT)
Brandon Wyman603cc002017-08-28 18:17:58 -0500340 {
Brandon Wyman593d24f2017-10-13 18:15:23 -0500341 // Check PG# and UNIT_IS_OFF
342 if ((statusWord & status_word::POWER_GOOD_NEGATED) ||
343 (statusWord & status_word::UNIT_IS_OFF))
344 {
345 log<level::INFO>("PGOOD or UNIT_IS_OFF bit bad",
346 entry("STATUS_WORD=0x%04X", statusWord));
347 powerOnFault++;
348 }
349 else
350 {
351 if (powerOnFault > 0)
352 {
353 log<level::INFO>("PGOOD and UNIT_IS_OFF bits good");
354 powerOnFault = 0;
355 }
356 }
Brandon Wyman603cc002017-08-28 18:17:58 -0500357
Brandon Wymane2fc7aa2017-11-13 17:37:10 -0600358 if (!faultFound && (powerOnFault >= FAULT_COUNT))
Brandon Wyman593d24f2017-10-13 18:15:23 -0500359 {
Brandon Wyman3343e822017-11-03 16:54:11 -0500360 faultFound = true;
361
Brandon Wyman593d24f2017-10-13 18:15:23 -0500362 util::NamesValues nv;
363 nv.add("STATUS_WORD", statusWord);
364 captureCmd(nv, STATUS_INPUT, Type::Debug);
365 auto status0Vout = pmbusIntf.insertPageNum(STATUS_VOUT, 0);
366 captureCmd(nv, status0Vout, Type::Debug);
367 captureCmd(nv, STATUS_IOUT, Type::Debug);
368 captureCmd(nv, STATUS_MFR, Type::Debug);
Brandon Wyman603cc002017-08-28 18:17:58 -0500369
Brandon Wyman593d24f2017-10-13 18:15:23 -0500370 using metadata = org::open_power::Witherspoon::Fault::
371 PowerSupplyShouldBeOn;
Brandon Wyman603cc002017-08-28 18:17:58 -0500372
Brandon Wyman593d24f2017-10-13 18:15:23 -0500373 // A power supply is OFF (or pgood low) but should be on.
374 report<PowerSupplyShouldBeOn>(
375 metadata::RAW_STATUS(nv.get().c_str()),
376 metadata::CALLOUT_INVENTORY_PATH(
377 inventoryPath.c_str()));
378 }
Brandon Wyman603cc002017-08-28 18:17:58 -0500379 }
380
381}
382
383void PowerSupply::checkCurrentOutOverCurrentFault(const uint16_t statusWord)
384{
385 using namespace witherspoon::pmbus;
386
Brandon Wymandd61be42017-11-07 18:38:54 -0600387 if (outputOCFault < FAULT_COUNT)
Brandon Wyman603cc002017-08-28 18:17:58 -0500388 {
Brandon Wymandd61be42017-11-07 18:38:54 -0600389 // Check for an output overcurrent fault.
390 if ((statusWord & status_word::IOUT_OC_FAULT))
391 {
392 outputOCFault++;
393 }
394 else
395 {
396 if (outputOCFault > 0)
397 {
398 outputOCFault = 0;
399 }
400 }
Brandon Wyman603cc002017-08-28 18:17:58 -0500401
Brandon Wymane2fc7aa2017-11-13 17:37:10 -0600402 if (!faultFound && (outputOCFault >= FAULT_COUNT))
Brandon Wymandd61be42017-11-07 18:38:54 -0600403 {
404 util::NamesValues nv;
405 nv.add("STATUS_WORD", statusWord);
406 captureCmd(nv, STATUS_INPUT, Type::Debug);
407 auto status0Vout = pmbusIntf.insertPageNum(STATUS_VOUT, 0);
408 captureCmd(nv, status0Vout, Type::Debug);
409 captureCmd(nv, STATUS_IOUT, Type::Debug);
410 captureCmd(nv, STATUS_MFR, Type::Debug);
Brandon Wyman603cc002017-08-28 18:17:58 -0500411
Brandon Wymandd61be42017-11-07 18:38:54 -0600412 using metadata = org::open_power::Witherspoon::Fault::
413 PowerSupplyOutputOvercurrent;
Brandon Wyman603cc002017-08-28 18:17:58 -0500414
Brandon Wymandd61be42017-11-07 18:38:54 -0600415 report<PowerSupplyOutputOvercurrent>(
416 metadata::RAW_STATUS(nv.get().c_str()),
417 metadata::CALLOUT_INVENTORY_PATH(inventoryPath.c_str()));
418
419 faultFound = true;
420 }
Brandon Wyman603cc002017-08-28 18:17:58 -0500421 }
422}
423
Brandon Wymanab05c072017-08-30 18:26:41 -0500424void PowerSupply::checkOutputOvervoltageFault(const uint16_t statusWord)
425{
426 using namespace witherspoon::pmbus;
427
Brandon Wyman2ab319b2017-11-08 17:34:59 -0600428 if (outputOVFault < FAULT_COUNT)
Brandon Wymanab05c072017-08-30 18:26:41 -0500429 {
Brandon Wyman2ab319b2017-11-08 17:34:59 -0600430 // Check for an output overvoltage fault.
431 if (statusWord & status_word::VOUT_OV_FAULT)
432 {
433 outputOVFault++;
434 }
435 else
436 {
437 if (outputOVFault > 0)
438 {
439 outputOVFault = 0;
440 }
441 }
Brandon Wymanab05c072017-08-30 18:26:41 -0500442
Brandon Wymane2fc7aa2017-11-13 17:37:10 -0600443 if (!faultFound && (outputOVFault >= FAULT_COUNT))
Brandon Wyman2ab319b2017-11-08 17:34:59 -0600444 {
445 util::NamesValues nv;
446 nv.add("STATUS_WORD", statusWord);
447 captureCmd(nv, STATUS_INPUT, Type::Debug);
448 auto status0Vout = pmbusIntf.insertPageNum(STATUS_VOUT, 0);
449 captureCmd(nv, status0Vout, Type::Debug);
450 captureCmd(nv, STATUS_IOUT, Type::Debug);
451 captureCmd(nv, STATUS_MFR, Type::Debug);
Brandon Wymanab05c072017-08-30 18:26:41 -0500452
Brandon Wyman2ab319b2017-11-08 17:34:59 -0600453 using metadata = org::open_power::Witherspoon::Fault::
454 PowerSupplyOutputOvervoltage;
Brandon Wymanab05c072017-08-30 18:26:41 -0500455
Brandon Wyman2ab319b2017-11-08 17:34:59 -0600456 report<PowerSupplyOutputOvervoltage>(
457 metadata::RAW_STATUS(nv.get().c_str()),
458 metadata::CALLOUT_INVENTORY_PATH(inventoryPath.c_str()));
459
460 faultFound = true;
461 }
Brandon Wymanab05c072017-08-30 18:26:41 -0500462 }
463}
464
Brandon Wyman12661f12017-08-31 15:28:21 -0500465void PowerSupply::checkFanFault(const uint16_t statusWord)
466{
467 using namespace witherspoon::pmbus;
468
Brandon Wymanba255532017-11-08 17:44:10 -0600469 if (fanFault < FAULT_COUNT)
Brandon Wyman12661f12017-08-31 15:28:21 -0500470 {
Brandon Wymanba255532017-11-08 17:44:10 -0600471 // Check for a fan fault or warning condition
472 if (statusWord & status_word::FAN_FAULT)
473 {
474 fanFault++;
475 }
476 else
477 {
478 if (fanFault > 0)
479 {
480 fanFault = 0;
481 }
482 }
Brandon Wyman12661f12017-08-31 15:28:21 -0500483
Brandon Wymane2fc7aa2017-11-13 17:37:10 -0600484 if (!faultFound && (fanFault >= FAULT_COUNT))
Brandon Wymanba255532017-11-08 17:44:10 -0600485 {
486 util::NamesValues nv;
487 nv.add("STATUS_WORD", statusWord);
488 captureCmd(nv, STATUS_MFR, Type::Debug);
489 captureCmd(nv, STATUS_TEMPERATURE, Type::Debug);
490 captureCmd(nv, STATUS_FANS_1_2, Type::Debug);
Brandon Wyman12661f12017-08-31 15:28:21 -0500491
Brandon Wymanba255532017-11-08 17:44:10 -0600492 using metadata = org::open_power::Witherspoon::Fault::
493 PowerSupplyFanFault;
Brandon Wyman12661f12017-08-31 15:28:21 -0500494
Brandon Wymanba255532017-11-08 17:44:10 -0600495 report<PowerSupplyFanFault>(
496 metadata::RAW_STATUS(nv.get().c_str()),
497 metadata::CALLOUT_INVENTORY_PATH(inventoryPath.c_str()));
498
499 faultFound = true;
500 }
Brandon Wyman12661f12017-08-31 15:28:21 -0500501 }
502}
503
Brandon Wyman875b3632017-09-13 18:46:03 -0500504void PowerSupply::checkTemperatureFault(const uint16_t statusWord)
505{
506 using namespace witherspoon::pmbus;
507
508 // Due to how the PMBus core device driver sends a clear faults command
509 // the bit in STATUS_WORD will likely be cleared when we attempt to examine
510 // it for a Thermal Fault or Warning. So, check the STATUS_WORD and the
511 // STATUS_TEMPERATURE bits. If either indicates a fault, proceed with
512 // logging the over-temperature condition.
513 std::uint8_t statusTemperature = 0;
514 statusTemperature = pmbusIntf.read(STATUS_TEMPERATURE, Type::Debug);
Brandon Wyman50044ea2017-11-08 17:58:56 -0600515 if (temperatureFault < FAULT_COUNT)
Brandon Wyman875b3632017-09-13 18:46:03 -0500516 {
Brandon Wyman50044ea2017-11-08 17:58:56 -0600517 if ((statusWord & status_word::TEMPERATURE_FAULT_WARN) ||
518 (statusTemperature & status_temperature::OT_FAULT))
519 {
520 temperatureFault++;
521 }
522 else
523 {
524 if (temperatureFault > 0)
525 {
526 temperatureFault = 0;
527 }
528 }
Brandon Wyman875b3632017-09-13 18:46:03 -0500529
Brandon Wymane2fc7aa2017-11-13 17:37:10 -0600530 if (!faultFound && (temperatureFault >= FAULT_COUNT))
Brandon Wyman50044ea2017-11-08 17:58:56 -0600531 {
532 // The power supply has had an over-temperature condition.
533 // This may not result in a shutdown if experienced for a short
534 // duration.
535 // This should not occur under normal conditions.
536 // The power supply may be faulty, or the paired supply may be
537 // putting out less current.
538 // Capture command responses with potentially relevant information,
539 // and call out the power supply reporting the condition.
540 util::NamesValues nv;
541 nv.add("STATUS_WORD", statusWord);
542 captureCmd(nv, STATUS_MFR, Type::Debug);
543 captureCmd(nv, STATUS_IOUT, Type::Debug);
544 nv.add("STATUS_TEMPERATURE", statusTemperature);
545 captureCmd(nv, STATUS_FANS_1_2, Type::Debug);
Brandon Wyman875b3632017-09-13 18:46:03 -0500546
Brandon Wyman50044ea2017-11-08 17:58:56 -0600547 using metadata = org::open_power::Witherspoon::Fault::
548 PowerSupplyTemperatureFault;
Brandon Wyman875b3632017-09-13 18:46:03 -0500549
Brandon Wyman50044ea2017-11-08 17:58:56 -0600550 report<PowerSupplyTemperatureFault>(
551 metadata::RAW_STATUS(nv.get().c_str()),
552 metadata::CALLOUT_INVENTORY_PATH(inventoryPath.c_str()));
553
554 faultFound = true;
555 }
Brandon Wyman875b3632017-09-13 18:46:03 -0500556 }
557}
558
Brandon Wyman1db9a9e2017-07-26 18:50:22 -0500559void PowerSupply::clearFaults()
560{
Brandon Wymane4af9802017-11-13 15:58:33 -0600561 readFail = 0;
Brandon Wyman6ccce0b2017-10-26 15:13:10 -0500562 readFailLogged = false;
Brandon Wymana3c675c2017-11-14 14:54:54 -0600563 inputFault = 0;
Brandon Wyman6ccce0b2017-10-26 15:13:10 -0500564 powerOnFault = 0;
Brandon Wymandd61be42017-11-07 18:38:54 -0600565 outputOCFault = 0;
Brandon Wyman2ab319b2017-11-08 17:34:59 -0600566 outputOVFault = 0;
Brandon Wymanba255532017-11-08 17:44:10 -0600567 fanFault = 0;
Brandon Wyman50044ea2017-11-08 17:58:56 -0600568 temperatureFault = 0;
Brandon Wyman3343e822017-11-03 16:54:11 -0500569 faultFound = false;
Brandon Wyman6ccce0b2017-10-26 15:13:10 -0500570
Brandon Wyman1db9a9e2017-07-26 18:50:22 -0500571 return;
572}
573
Brandon Wyman43ce2082017-11-30 17:24:01 -0600574void PowerSupply::resolveError(const std::string& callout,
575 const std::string& message)
576{
Brandon Wyman01741f12017-12-01 17:22:08 -0600577 using EndpointList = std::vector<std::string>;
578
579 try
580 {
581 auto path = callout + "/fault";
582 // Get the service name from the mapper for the fault callout
583 auto service = util::getService(path,
584 ASSOCIATION_IFACE,
585 bus);
586
587 // Use getProperty utility function to get log entries (endpoints)
588 EndpointList logEntries;
Matt Spinler589e8722018-01-04 15:24:49 -0600589 util::getProperty(ASSOCIATION_IFACE, ENDPOINTS_PROP, path, service,
Brandon Wyman01741f12017-12-01 17:22:08 -0600590 bus, logEntries);
591
592 // It is possible that all such entries for this callout have since
593 // been deleted.
594 if (logEntries.empty())
595 {
596 return;
597 }
598
599 auto logEntryService = util::getService(logEntries[0], LOGGING_IFACE,
600 bus);
601 if (logEntryService.empty())
602 {
603 return;
604 }
605
606 // go through each log entry that matches this callout path
607 std::string logMessage;
608 for (const auto& logEntry : logEntries)
609 {
610 // Check to see if this logEntry has a message that matches.
Matt Spinler589e8722018-01-04 15:24:49 -0600611 util::getProperty(LOGGING_IFACE, MESSAGE_PROP, logEntry,
Brandon Wyman01741f12017-12-01 17:22:08 -0600612 logEntryService, bus, logMessage);
613
614 if (message == logMessage)
615 {
616 // Log entry matches call out and message, set Resolved to true
617 bool resolved = true;
Matt Spinler589e8722018-01-04 15:24:49 -0600618 util::setProperty(LOGGING_IFACE, RESOLVED_PROP, logEntry,
Brandon Wyman01741f12017-12-01 17:22:08 -0600619 logEntryService, bus, resolved);
620 }
621
622 }
623
624 }
625 catch (std::exception& e)
626 {
627 log<level::INFO>("Failed to resolve error",
628 entry("CALLOUT=%s", callout.c_str()),
Matt Spinler0d09f292018-01-22 14:51:26 -0600629 entry("ERROR=%s", message.c_str()));
Brandon Wyman01741f12017-12-01 17:22:08 -0600630 }
631
Brandon Wyman43ce2082017-11-30 17:24:01 -0600632}
633
Matt Spinler234ce0d2018-01-04 15:06:57 -0600634void PowerSupply::updateInventory()
635{
Matt Spinler018a7bc2018-01-04 15:36:41 -0600636 using namespace witherspoon::pmbus;
637 using namespace sdbusplus::message;
638
639 // If any of these accesses fail, the fields will just be
640 // blank in the inventory. Leave logging ReadFailure errors
641 // to analyze() as it runs continuously and will most
642 // likely hit and threshold them first anyway. The
643 // readString() function will do the tracing of the failing
644 // path so this code doesn't need to.
645 std::string pn;
646 std::string sn;
647 std::string ccin;
648 std::string version;
649
650 if (present)
651 {
652 try
653 {
654 sn = pmbusIntf.readString(SERIAL_NUMBER, Type::HwmonDeviceDebug);
655 }
656 catch (ReadFailure& e) { }
657
658 try
659 {
660 pn = pmbusIntf.readString(PART_NUMBER, Type::HwmonDeviceDebug);
661 }
662 catch (ReadFailure& e) { }
663
664 try
665 {
666 ccin = pmbusIntf.readString(CCIN, Type::HwmonDeviceDebug);
667 }
668 catch (ReadFailure& e) { }
669
670 try
671 {
672 version = pmbusIntf.readString(FW_VERSION, Type::HwmonDeviceDebug);
673 }
674 catch (ReadFailure& e) { }
675 }
676
677 // Build the object map and send it to the inventory
678 using Properties = std::map<std::string, variant<std::string>>;
679 using Interfaces = std::map<std::string, Properties>;
680 using Object = std::map<object_path, Interfaces>;
681 Properties assetProps;
682 Properties versionProps;
683 Interfaces interfaces;
684 Object object;
685
686 assetProps.emplace(SN_PROP, sn);
687 assetProps.emplace(PN_PROP, pn);
688 assetProps.emplace(MODEL_PROP, ccin);
689 interfaces.emplace(ASSET_IFACE, std::move(assetProps));
690
691 versionProps.emplace(VERSION_PROP, version);
692 interfaces.emplace(VERSION_IFACE, std::move(versionProps));
693
694 //For Notify(), just send the relative path of the inventory
695 //object so remove the INVENTORY_OBJ_PATH prefix
696 auto path = inventoryPath.substr(strlen(INVENTORY_OBJ_PATH));
697
698 object.emplace(path, std::move(interfaces));
699
700 try
701 {
702 auto service = util::getService(
703 INVENTORY_OBJ_PATH,
704 INVENTORY_MGR_IFACE,
705 bus);
706
707 if (service.empty())
708 {
709 log<level::ERR>("Unable to get inventory manager service");
710 return;
711 }
712
713 auto method = bus.new_method_call(
714 service.c_str(),
715 INVENTORY_OBJ_PATH,
716 INVENTORY_MGR_IFACE,
717 "Notify");
718
719 method.append(std::move(object));
720
721 auto reply = bus.call(method);
722 if (reply.is_method_error())
723 {
724 log<level::ERR>(
725 "Unable to update power supply inventory properties",
726 entry("PATH=%s", path.c_str()));
727 }
728
729 // TODO: openbmc/openbmc#2756
730 // Calling Notify() with an enumerated property crashes inventory
731 // manager, so let it default to Unknown and now set it to the
732 // right value.
733 auto purpose = version::convertForMessage(
734 version::Version::VersionPurpose::Other);
735
736 util::setProperty(
737 VERSION_IFACE,
738 VERSION_PURPOSE_PROP,
739 inventoryPath,
740 service,
741 bus,
742 purpose);
743 }
744 catch (std::exception& e)
745 {
746 log<level::ERR>(
747 e.what(),
748 entry("PATH=%s", inventoryPath));
749 }
Matt Spinler234ce0d2018-01-04 15:06:57 -0600750}
751
Brandon Wyman24e422f2017-07-25 19:40:14 -0500752}
753}
754}