blob: 50d647a6317c615c651be15e729038b182acdc68 [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";
Matt Spinlereb169fd2018-01-18 14:19:08 -060064constexpr auto INPUT_HISTORY = "input_history";
Matt Spinler018a7bc2018-01-04 15:36:41 -060065
Brandon Wyman10295542017-08-09 18:20:44 -050066PowerSupply::PowerSupply(const std::string& name, size_t inst,
Brandon Wyman431fbe42017-08-18 16:22:09 -050067 const std::string& objpath,
68 const std::string& invpath,
69 sdbusplus::bus::bus& bus,
70 event::Event& e,
Brandon Wyman590fc282017-11-01 18:22:25 -050071 std::chrono::seconds& t,
72 std::chrono::seconds& p)
Brandon Wyman431fbe42017-08-18 16:22:09 -050073 : Device(name, inst), monitorPath(objpath), pmbusIntf(objpath),
Brandon Wyman50bb85d2017-11-01 18:36:00 -050074 inventoryPath(INVENTORY_OBJ_PATH + invpath), bus(bus), event(e),
75 presentInterval(p),
Brandon Wyman590fc282017-11-01 18:22:25 -050076 presentTimer(e, [this]()
77 {
Brandon Wyman2877add2017-11-10 17:44:19 -060078 // The hwmon path may have changed.
79 pmbusIntf.findHwmonDir();
Brandon Wyman590fc282017-11-01 18:22:25 -050080 this->present = true;
Matt Spinler234ce0d2018-01-04 15:06:57 -060081
82 // Update the inventory for the new device
83 updateInventory();
Brandon Wyman590fc282017-11-01 18:22:25 -050084 }),
85 powerOnInterval(t),
Brandon Wyman431fbe42017-08-18 16:22:09 -050086 powerOnTimer(e, [this]()
87 {
88 this->powerOn = true;
89 })
Brandon Wyman10295542017-08-09 18:20:44 -050090{
Brandon Wyman10295542017-08-09 18:20:44 -050091 using namespace sdbusplus::bus;
Brandon Wyman10295542017-08-09 18:20:44 -050092 presentMatch = std::make_unique<match_t>(bus,
93 match::rules::propertiesChanged(
Brandon Wyman50bb85d2017-11-01 18:36:00 -050094 inventoryPath,
Matt Spinler589e8722018-01-04 15:24:49 -060095 INVENTORY_IFACE),
Brandon Wyman10295542017-08-09 18:20:44 -050096 [this](auto& msg)
Brandon Wyman431fbe42017-08-18 16:22:09 -050097 {
98 this->inventoryChanged(msg);
99 });
100 // Get initial presence state.
Brandon Wyman253dc9b2017-08-12 13:45:52 -0500101 updatePresence();
Brandon Wyman431fbe42017-08-18 16:22:09 -0500102
Matt Spinler234ce0d2018-01-04 15:06:57 -0600103 // Write the SN, PN, etc to the inventory
104 updateInventory();
105
Brandon Wyman431fbe42017-08-18 16:22:09 -0500106 // Subscribe to power state changes
107 powerOnMatch = std::make_unique<match_t>(bus,
108 match::rules::propertiesChanged(
109 POWER_OBJ_PATH,
Matt Spinler589e8722018-01-04 15:24:49 -0600110 POWER_IFACE),
Brandon Wyman431fbe42017-08-18 16:22:09 -0500111 [this](auto& msg)
112 {
113 this->powerStateChanged(msg);
114 });
115 // Get initial power state.
116 updatePowerState();
Brandon Wyman10295542017-08-09 18:20:44 -0500117}
Brandon Wyman442035f2017-08-08 15:58:45 -0500118
Brandon Wymana1e96342017-09-25 16:47:44 -0500119void PowerSupply::captureCmd(util::NamesValues& nv, const std::string& cmd,
120 witherspoon::pmbus::Type type)
121{
122 if (pmbusIntf.exists(cmd, type))
123 {
124 try
125 {
126 auto val = pmbusIntf.read(cmd, type);
127 nv.add(cmd, val);
128 }
129 catch (std::exception& e)
130 {
131 log<level::INFO>("Unable to capture metadata", entry("CMD=%s",
132 cmd));
133 }
134 }
135}
Brandon Wyman431fbe42017-08-18 16:22:09 -0500136
Brandon Wyman1db9a9e2017-07-26 18:50:22 -0500137void PowerSupply::analyze()
138{
Brandon Wyman442035f2017-08-08 15:58:45 -0500139 using namespace witherspoon::pmbus;
140
141 try
142 {
Brandon Wyman10295542017-08-09 18:20:44 -0500143 if (present)
Brandon Wyman442035f2017-08-08 15:58:45 -0500144 {
Brandon Wyman764c7972017-08-22 17:05:36 -0500145 std::uint16_t statusWord = 0;
Brandon Wyman764c7972017-08-22 17:05:36 -0500146
147 // Read the 2 byte STATUS_WORD value to check for faults.
148 statusWord = pmbusIntf.read(STATUS_WORD, Type::Debug);
Brandon Wymane4af9802017-11-13 15:58:33 -0600149 readFail = 0;
Brandon Wyman764c7972017-08-22 17:05:36 -0500150
Brandon Wyman603cc002017-08-28 18:17:58 -0500151 checkInputFault(statusWord);
Brandon Wyman764c7972017-08-22 17:05:36 -0500152
Brandon Wyman3343e822017-11-03 16:54:11 -0500153 if (powerOn && !faultFound)
Brandon Wyman764c7972017-08-22 17:05:36 -0500154 {
Brandon Wyman12661f12017-08-31 15:28:21 -0500155 checkFanFault(statusWord);
Brandon Wyman875b3632017-09-13 18:46:03 -0500156 checkTemperatureFault(statusWord);
Brandon Wymancfa032b2017-09-25 17:37:50 -0500157 checkOutputOvervoltageFault(statusWord);
158 checkCurrentOutOverCurrentFault(statusWord);
159 checkPGOrUnitOffFault(statusWord);
Brandon Wyman442035f2017-08-08 15:58:45 -0500160 }
Matt Spinlereb169fd2018-01-18 14:19:08 -0600161
162 updateHistory();
Brandon Wyman442035f2017-08-08 15:58:45 -0500163 }
164 }
165 catch (ReadFailure& e)
166 {
Brandon Wymane4af9802017-11-13 15:58:33 -0600167 if (readFail < FAULT_COUNT)
168 {
169 readFail++;
170 }
171
172 if (!readFailLogged && readFail >= FAULT_COUNT)
Brandon Wyman442035f2017-08-08 15:58:45 -0500173 {
174 commit<ReadFailure>();
175 readFailLogged = true;
Brandon Wyman442035f2017-08-08 15:58:45 -0500176 }
177 }
178
Brandon Wyman1db9a9e2017-07-26 18:50:22 -0500179 return;
180}
181
Brandon Wyman10295542017-08-09 18:20:44 -0500182void PowerSupply::inventoryChanged(sdbusplus::message::message& msg)
183{
184 std::string msgSensor;
185 std::map<std::string, sdbusplus::message::variant<uint32_t, bool>> msgData;
186 msg.read(msgSensor, msgData);
187
188 // Check if it was the Present property that changed.
189 auto valPropMap = msgData.find(PRESENT_PROP);
190 if (valPropMap != msgData.end())
191 {
Brandon Wyman2ef48cf2017-11-21 15:43:54 -0600192 if (sdbusplus::message::variant_ns::get<bool>(valPropMap->second))
Brandon Wyman10295542017-08-09 18:20:44 -0500193 {
Brandon Wyman6ccce0b2017-10-26 15:13:10 -0500194 clearFaults();
Brandon Wyman590fc282017-11-01 18:22:25 -0500195 presentTimer.start(presentInterval, Timer::TimerType::oneshot);
196 }
197 else
198 {
Brandon Wyman2ef48cf2017-11-21 15:43:54 -0600199 present = false;
Brandon Wyman590fc282017-11-01 18:22:25 -0500200 presentTimer.stop();
Matt Spinler234ce0d2018-01-04 15:06:57 -0600201
202 //Clear out the now outdated inventory properties
203 updateInventory();
Brandon Wyman10295542017-08-09 18:20:44 -0500204 }
205 }
206
207 return;
208}
209
210void PowerSupply::updatePresence()
211{
212 // Use getProperty utility function to get presence status.
Brandon Wyman10295542017-08-09 18:20:44 -0500213 std::string service = "xyz.openbmc_project.Inventory.Manager";
Matt Spinler589e8722018-01-04 15:24:49 -0600214 util::getProperty(INVENTORY_IFACE, PRESENT_PROP, inventoryPath,
Brandon Wyman50bb85d2017-11-01 18:36:00 -0500215 service, bus, this->present);
Brandon Wyman10295542017-08-09 18:20:44 -0500216}
217
Brandon Wyman431fbe42017-08-18 16:22:09 -0500218void PowerSupply::powerStateChanged(sdbusplus::message::message& msg)
219{
220 int32_t state = 0;
221 std::string msgSensor;
222 std::map<std::string, sdbusplus::message::variant<int32_t, int32_t>>
223 msgData;
224 msg.read(msgSensor, msgData);
225
226 // Check if it was the Present property that changed.
227 auto valPropMap = msgData.find("state");
228 if (valPropMap != msgData.end())
229 {
230 state = sdbusplus::message::variant_ns::get<int32_t>(valPropMap->second);
231
232 // Power is on when state=1. Set the fault logged variables to false
233 // and start the power on timer when the state changes to 1.
234 if (state)
235 {
Brandon Wyman6ccce0b2017-10-26 15:13:10 -0500236 clearFaults();
Brandon Wyman431fbe42017-08-18 16:22:09 -0500237 powerOnTimer.start(powerOnInterval, Timer::TimerType::oneshot);
238 }
239 else
240 {
241 powerOnTimer.stop();
242 powerOn = false;
243 }
244 }
245
246}
247
248void PowerSupply::updatePowerState()
249{
250 // When state = 1, system is powered on
251 int32_t state = 0;
252
253 try
254 {
255 auto service = util::getService(POWER_OBJ_PATH,
Matt Spinler589e8722018-01-04 15:24:49 -0600256 POWER_IFACE,
Brandon Wyman431fbe42017-08-18 16:22:09 -0500257 bus);
258
259 // Use getProperty utility function to get power state.
Matt Spinler589e8722018-01-04 15:24:49 -0600260 util::getProperty<int32_t>(POWER_IFACE,
Brandon Wyman431fbe42017-08-18 16:22:09 -0500261 "state",
262 POWER_OBJ_PATH,
263 service,
264 bus,
265 state);
266
267 if (state)
268 {
269 powerOn = true;
270 }
271 else
272 {
273 powerOn = false;
274 }
275 }
276 catch (std::exception& e)
277 {
278 log<level::INFO>("Failed to get power state. Assuming it is off.");
279 powerOn = false;
280 }
281
282}
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 Wymana3c675c2017-11-14 14:54:54 -0600292 inputFault++;
Brandon Wyman603cc002017-08-28 18:17:58 -0500293 }
294 else
295 {
Brandon Wymana3c675c2017-11-14 14:54:54 -0600296 if ((inputFault > 0) &&
Brandon Wymand20686a2017-11-01 17:45:23 -0500297 !(statusWord & status_word::INPUT_FAULT_WARN) &&
298 !(statusWord & status_word::VIN_UV_FAULT))
Brandon Wyman603cc002017-08-28 18:17:58 -0500299 {
Brandon Wymana3c675c2017-11-14 14:54:54 -0600300 inputFault = 0;
Brandon Wyman3343e822017-11-03 16:54:11 -0500301 faultFound = false;
Brandon Wyman69591bd2017-11-01 18:07:23 -0500302
Brandon Wyman603cc002017-08-28 18:17:58 -0500303 log<level::INFO>("INPUT_FAULT_WARN cleared",
Brandon Wymana3c675c2017-11-14 14:54:54 -0600304 entry("POWERSUPPLY=%s", inventoryPath.c_str()));
Brandon Wyman69591bd2017-11-01 18:07:23 -0500305
Brandon Wyman08b05712017-11-30 17:53:56 -0600306 resolveError(inventoryPath,
307 std::string(PowerSupplyInputFault::errName));
308
Brandon Wyman69591bd2017-11-01 18:07:23 -0500309 if (powerOn)
310 {
311 // The power supply will not be immediately powered on after
312 // the input power is restored.
313 powerOn = false;
314 // Start up the timer that will set the state to indicate we
315 // are ready for the powered on fault checks.
316 powerOnTimer.start(powerOnInterval, Timer::TimerType::oneshot);
317 }
Brandon Wyman603cc002017-08-28 18:17:58 -0500318 }
319 }
Brandon Wymana3c675c2017-11-14 14:54:54 -0600320
321 if (!faultFound && (inputFault >= FAULT_COUNT))
322 {
323 util::NamesValues nv;
324 nv.add("STATUS_WORD", statusWord);
325 captureCmd(nv, STATUS_INPUT, Type::Debug);
326
327 using metadata = org::open_power::Witherspoon::Fault::
328 PowerSupplyInputFault;
329
330 report<PowerSupplyInputFault>(
331 metadata::RAW_STATUS(nv.get().c_str()),
332 metadata::CALLOUT_INVENTORY_PATH(inventoryPath.c_str()));
333 faultFound = true;
334 }
335
Brandon Wyman603cc002017-08-28 18:17:58 -0500336}
337
338void PowerSupply::checkPGOrUnitOffFault(const uint16_t statusWord)
339{
340 using namespace witherspoon::pmbus;
341
Brandon Wyman593d24f2017-10-13 18:15:23 -0500342 if (powerOnFault < FAULT_COUNT)
Brandon Wyman603cc002017-08-28 18:17:58 -0500343 {
Brandon Wyman593d24f2017-10-13 18:15:23 -0500344 // Check PG# and UNIT_IS_OFF
345 if ((statusWord & status_word::POWER_GOOD_NEGATED) ||
346 (statusWord & status_word::UNIT_IS_OFF))
347 {
348 log<level::INFO>("PGOOD or UNIT_IS_OFF bit bad",
349 entry("STATUS_WORD=0x%04X", statusWord));
350 powerOnFault++;
351 }
352 else
353 {
354 if (powerOnFault > 0)
355 {
356 log<level::INFO>("PGOOD and UNIT_IS_OFF bits good");
357 powerOnFault = 0;
358 }
359 }
Brandon Wyman603cc002017-08-28 18:17:58 -0500360
Brandon Wymane2fc7aa2017-11-13 17:37:10 -0600361 if (!faultFound && (powerOnFault >= FAULT_COUNT))
Brandon Wyman593d24f2017-10-13 18:15:23 -0500362 {
Brandon Wyman3343e822017-11-03 16:54:11 -0500363 faultFound = true;
364
Brandon Wyman593d24f2017-10-13 18:15:23 -0500365 util::NamesValues nv;
366 nv.add("STATUS_WORD", statusWord);
367 captureCmd(nv, STATUS_INPUT, Type::Debug);
368 auto status0Vout = pmbusIntf.insertPageNum(STATUS_VOUT, 0);
369 captureCmd(nv, status0Vout, Type::Debug);
370 captureCmd(nv, STATUS_IOUT, Type::Debug);
371 captureCmd(nv, STATUS_MFR, Type::Debug);
Brandon Wyman603cc002017-08-28 18:17:58 -0500372
Brandon Wyman593d24f2017-10-13 18:15:23 -0500373 using metadata = org::open_power::Witherspoon::Fault::
374 PowerSupplyShouldBeOn;
Brandon Wyman603cc002017-08-28 18:17:58 -0500375
Brandon Wyman593d24f2017-10-13 18:15:23 -0500376 // A power supply is OFF (or pgood low) but should be on.
377 report<PowerSupplyShouldBeOn>(
378 metadata::RAW_STATUS(nv.get().c_str()),
379 metadata::CALLOUT_INVENTORY_PATH(
380 inventoryPath.c_str()));
381 }
Brandon Wyman603cc002017-08-28 18:17:58 -0500382 }
383
384}
385
386void PowerSupply::checkCurrentOutOverCurrentFault(const uint16_t statusWord)
387{
388 using namespace witherspoon::pmbus;
389
Brandon Wymandd61be42017-11-07 18:38:54 -0600390 if (outputOCFault < FAULT_COUNT)
Brandon Wyman603cc002017-08-28 18:17:58 -0500391 {
Brandon Wymandd61be42017-11-07 18:38:54 -0600392 // Check for an output overcurrent fault.
393 if ((statusWord & status_word::IOUT_OC_FAULT))
394 {
395 outputOCFault++;
396 }
397 else
398 {
399 if (outputOCFault > 0)
400 {
401 outputOCFault = 0;
402 }
403 }
Brandon Wyman603cc002017-08-28 18:17:58 -0500404
Brandon Wymane2fc7aa2017-11-13 17:37:10 -0600405 if (!faultFound && (outputOCFault >= FAULT_COUNT))
Brandon Wymandd61be42017-11-07 18:38:54 -0600406 {
407 util::NamesValues nv;
408 nv.add("STATUS_WORD", statusWord);
409 captureCmd(nv, STATUS_INPUT, Type::Debug);
410 auto status0Vout = pmbusIntf.insertPageNum(STATUS_VOUT, 0);
411 captureCmd(nv, status0Vout, Type::Debug);
412 captureCmd(nv, STATUS_IOUT, Type::Debug);
413 captureCmd(nv, STATUS_MFR, Type::Debug);
Brandon Wyman603cc002017-08-28 18:17:58 -0500414
Brandon Wymandd61be42017-11-07 18:38:54 -0600415 using metadata = org::open_power::Witherspoon::Fault::
416 PowerSupplyOutputOvercurrent;
Brandon Wyman603cc002017-08-28 18:17:58 -0500417
Brandon Wymandd61be42017-11-07 18:38:54 -0600418 report<PowerSupplyOutputOvercurrent>(
419 metadata::RAW_STATUS(nv.get().c_str()),
420 metadata::CALLOUT_INVENTORY_PATH(inventoryPath.c_str()));
421
422 faultFound = true;
423 }
Brandon Wyman603cc002017-08-28 18:17:58 -0500424 }
425}
426
Brandon Wymanab05c072017-08-30 18:26:41 -0500427void PowerSupply::checkOutputOvervoltageFault(const uint16_t statusWord)
428{
429 using namespace witherspoon::pmbus;
430
Brandon Wyman2ab319b2017-11-08 17:34:59 -0600431 if (outputOVFault < FAULT_COUNT)
Brandon Wymanab05c072017-08-30 18:26:41 -0500432 {
Brandon Wyman2ab319b2017-11-08 17:34:59 -0600433 // Check for an output overvoltage fault.
434 if (statusWord & status_word::VOUT_OV_FAULT)
435 {
436 outputOVFault++;
437 }
438 else
439 {
440 if (outputOVFault > 0)
441 {
442 outputOVFault = 0;
443 }
444 }
Brandon Wymanab05c072017-08-30 18:26:41 -0500445
Brandon Wymane2fc7aa2017-11-13 17:37:10 -0600446 if (!faultFound && (outputOVFault >= FAULT_COUNT))
Brandon Wyman2ab319b2017-11-08 17:34:59 -0600447 {
448 util::NamesValues nv;
449 nv.add("STATUS_WORD", statusWord);
450 captureCmd(nv, STATUS_INPUT, Type::Debug);
451 auto status0Vout = pmbusIntf.insertPageNum(STATUS_VOUT, 0);
452 captureCmd(nv, status0Vout, Type::Debug);
453 captureCmd(nv, STATUS_IOUT, Type::Debug);
454 captureCmd(nv, STATUS_MFR, Type::Debug);
Brandon Wymanab05c072017-08-30 18:26:41 -0500455
Brandon Wyman2ab319b2017-11-08 17:34:59 -0600456 using metadata = org::open_power::Witherspoon::Fault::
457 PowerSupplyOutputOvervoltage;
Brandon Wymanab05c072017-08-30 18:26:41 -0500458
Brandon Wyman2ab319b2017-11-08 17:34:59 -0600459 report<PowerSupplyOutputOvervoltage>(
460 metadata::RAW_STATUS(nv.get().c_str()),
461 metadata::CALLOUT_INVENTORY_PATH(inventoryPath.c_str()));
462
463 faultFound = true;
464 }
Brandon Wymanab05c072017-08-30 18:26:41 -0500465 }
466}
467
Brandon Wyman12661f12017-08-31 15:28:21 -0500468void PowerSupply::checkFanFault(const uint16_t statusWord)
469{
470 using namespace witherspoon::pmbus;
471
Brandon Wymanba255532017-11-08 17:44:10 -0600472 if (fanFault < FAULT_COUNT)
Brandon Wyman12661f12017-08-31 15:28:21 -0500473 {
Brandon Wymanba255532017-11-08 17:44:10 -0600474 // Check for a fan fault or warning condition
475 if (statusWord & status_word::FAN_FAULT)
476 {
477 fanFault++;
478 }
479 else
480 {
481 if (fanFault > 0)
482 {
483 fanFault = 0;
484 }
485 }
Brandon Wyman12661f12017-08-31 15:28:21 -0500486
Brandon Wymane2fc7aa2017-11-13 17:37:10 -0600487 if (!faultFound && (fanFault >= FAULT_COUNT))
Brandon Wymanba255532017-11-08 17:44:10 -0600488 {
489 util::NamesValues nv;
490 nv.add("STATUS_WORD", statusWord);
491 captureCmd(nv, STATUS_MFR, Type::Debug);
492 captureCmd(nv, STATUS_TEMPERATURE, Type::Debug);
493 captureCmd(nv, STATUS_FANS_1_2, Type::Debug);
Brandon Wyman12661f12017-08-31 15:28:21 -0500494
Brandon Wymanba255532017-11-08 17:44:10 -0600495 using metadata = org::open_power::Witherspoon::Fault::
496 PowerSupplyFanFault;
Brandon Wyman12661f12017-08-31 15:28:21 -0500497
Brandon Wymanba255532017-11-08 17:44:10 -0600498 report<PowerSupplyFanFault>(
499 metadata::RAW_STATUS(nv.get().c_str()),
500 metadata::CALLOUT_INVENTORY_PATH(inventoryPath.c_str()));
501
502 faultFound = true;
503 }
Brandon Wyman12661f12017-08-31 15:28:21 -0500504 }
505}
506
Brandon Wyman875b3632017-09-13 18:46:03 -0500507void PowerSupply::checkTemperatureFault(const uint16_t statusWord)
508{
509 using namespace witherspoon::pmbus;
510
511 // Due to how the PMBus core device driver sends a clear faults command
512 // the bit in STATUS_WORD will likely be cleared when we attempt to examine
513 // it for a Thermal Fault or Warning. So, check the STATUS_WORD and the
514 // STATUS_TEMPERATURE bits. If either indicates a fault, proceed with
515 // logging the over-temperature condition.
516 std::uint8_t statusTemperature = 0;
517 statusTemperature = pmbusIntf.read(STATUS_TEMPERATURE, Type::Debug);
Brandon Wyman50044ea2017-11-08 17:58:56 -0600518 if (temperatureFault < FAULT_COUNT)
Brandon Wyman875b3632017-09-13 18:46:03 -0500519 {
Brandon Wyman50044ea2017-11-08 17:58:56 -0600520 if ((statusWord & status_word::TEMPERATURE_FAULT_WARN) ||
521 (statusTemperature & status_temperature::OT_FAULT))
522 {
523 temperatureFault++;
524 }
525 else
526 {
527 if (temperatureFault > 0)
528 {
529 temperatureFault = 0;
530 }
531 }
Brandon Wyman875b3632017-09-13 18:46:03 -0500532
Brandon Wymane2fc7aa2017-11-13 17:37:10 -0600533 if (!faultFound && (temperatureFault >= FAULT_COUNT))
Brandon Wyman50044ea2017-11-08 17:58:56 -0600534 {
535 // The power supply has had an over-temperature condition.
536 // This may not result in a shutdown if experienced for a short
537 // duration.
538 // This should not occur under normal conditions.
539 // The power supply may be faulty, or the paired supply may be
540 // putting out less current.
541 // Capture command responses with potentially relevant information,
542 // and call out the power supply reporting the condition.
543 util::NamesValues nv;
544 nv.add("STATUS_WORD", statusWord);
545 captureCmd(nv, STATUS_MFR, Type::Debug);
546 captureCmd(nv, STATUS_IOUT, Type::Debug);
547 nv.add("STATUS_TEMPERATURE", statusTemperature);
548 captureCmd(nv, STATUS_FANS_1_2, Type::Debug);
Brandon Wyman875b3632017-09-13 18:46:03 -0500549
Brandon Wyman50044ea2017-11-08 17:58:56 -0600550 using metadata = org::open_power::Witherspoon::Fault::
551 PowerSupplyTemperatureFault;
Brandon Wyman875b3632017-09-13 18:46:03 -0500552
Brandon Wyman50044ea2017-11-08 17:58:56 -0600553 report<PowerSupplyTemperatureFault>(
554 metadata::RAW_STATUS(nv.get().c_str()),
555 metadata::CALLOUT_INVENTORY_PATH(inventoryPath.c_str()));
556
557 faultFound = true;
558 }
Brandon Wyman875b3632017-09-13 18:46:03 -0500559 }
560}
561
Brandon Wyman1db9a9e2017-07-26 18:50:22 -0500562void PowerSupply::clearFaults()
563{
Brandon Wymane4af9802017-11-13 15:58:33 -0600564 readFail = 0;
Brandon Wyman6ccce0b2017-10-26 15:13:10 -0500565 readFailLogged = false;
Brandon Wymana3c675c2017-11-14 14:54:54 -0600566 inputFault = 0;
Brandon Wyman6ccce0b2017-10-26 15:13:10 -0500567 powerOnFault = 0;
Brandon Wymandd61be42017-11-07 18:38:54 -0600568 outputOCFault = 0;
Brandon Wyman2ab319b2017-11-08 17:34:59 -0600569 outputOVFault = 0;
Brandon Wymanba255532017-11-08 17:44:10 -0600570 fanFault = 0;
Brandon Wyman50044ea2017-11-08 17:58:56 -0600571 temperatureFault = 0;
Brandon Wyman3343e822017-11-03 16:54:11 -0500572 faultFound = false;
Brandon Wyman6ccce0b2017-10-26 15:13:10 -0500573
Brandon Wyman1db9a9e2017-07-26 18:50:22 -0500574 return;
575}
576
Brandon Wyman43ce2082017-11-30 17:24:01 -0600577void PowerSupply::resolveError(const std::string& callout,
578 const std::string& message)
579{
Brandon Wyman01741f12017-12-01 17:22:08 -0600580 using EndpointList = std::vector<std::string>;
581
582 try
583 {
584 auto path = callout + "/fault";
585 // Get the service name from the mapper for the fault callout
586 auto service = util::getService(path,
587 ASSOCIATION_IFACE,
588 bus);
589
590 // Use getProperty utility function to get log entries (endpoints)
591 EndpointList logEntries;
Matt Spinler589e8722018-01-04 15:24:49 -0600592 util::getProperty(ASSOCIATION_IFACE, ENDPOINTS_PROP, path, service,
Brandon Wyman01741f12017-12-01 17:22:08 -0600593 bus, logEntries);
594
595 // It is possible that all such entries for this callout have since
596 // been deleted.
597 if (logEntries.empty())
598 {
599 return;
600 }
601
602 auto logEntryService = util::getService(logEntries[0], LOGGING_IFACE,
603 bus);
604 if (logEntryService.empty())
605 {
606 return;
607 }
608
609 // go through each log entry that matches this callout path
610 std::string logMessage;
611 for (const auto& logEntry : logEntries)
612 {
613 // Check to see if this logEntry has a message that matches.
Matt Spinler589e8722018-01-04 15:24:49 -0600614 util::getProperty(LOGGING_IFACE, MESSAGE_PROP, logEntry,
Brandon Wyman01741f12017-12-01 17:22:08 -0600615 logEntryService, bus, logMessage);
616
617 if (message == logMessage)
618 {
619 // Log entry matches call out and message, set Resolved to true
620 bool resolved = true;
Matt Spinler589e8722018-01-04 15:24:49 -0600621 util::setProperty(LOGGING_IFACE, RESOLVED_PROP, logEntry,
Brandon Wyman01741f12017-12-01 17:22:08 -0600622 logEntryService, bus, resolved);
623 }
624
625 }
626
627 }
628 catch (std::exception& e)
629 {
630 log<level::INFO>("Failed to resolve error",
631 entry("CALLOUT=%s", callout.c_str()),
Matt Spinler0d09f292018-01-22 14:51:26 -0600632 entry("ERROR=%s", message.c_str()));
Brandon Wyman01741f12017-12-01 17:22:08 -0600633 }
634
Brandon Wyman43ce2082017-11-30 17:24:01 -0600635}
636
Matt Spinler234ce0d2018-01-04 15:06:57 -0600637void PowerSupply::updateInventory()
638{
Matt Spinler018a7bc2018-01-04 15:36:41 -0600639 using namespace witherspoon::pmbus;
640 using namespace sdbusplus::message;
641
642 // If any of these accesses fail, the fields will just be
643 // blank in the inventory. Leave logging ReadFailure errors
644 // to analyze() as it runs continuously and will most
645 // likely hit and threshold them first anyway. The
646 // readString() function will do the tracing of the failing
647 // path so this code doesn't need to.
648 std::string pn;
649 std::string sn;
650 std::string ccin;
651 std::string version;
652
653 if (present)
654 {
655 try
656 {
657 sn = pmbusIntf.readString(SERIAL_NUMBER, Type::HwmonDeviceDebug);
658 }
659 catch (ReadFailure& e) { }
660
661 try
662 {
663 pn = pmbusIntf.readString(PART_NUMBER, Type::HwmonDeviceDebug);
664 }
665 catch (ReadFailure& e) { }
666
667 try
668 {
669 ccin = pmbusIntf.readString(CCIN, Type::HwmonDeviceDebug);
670 }
671 catch (ReadFailure& e) { }
672
673 try
674 {
675 version = pmbusIntf.readString(FW_VERSION, Type::HwmonDeviceDebug);
676 }
677 catch (ReadFailure& e) { }
678 }
679
680 // Build the object map and send it to the inventory
681 using Properties = std::map<std::string, variant<std::string>>;
682 using Interfaces = std::map<std::string, Properties>;
683 using Object = std::map<object_path, Interfaces>;
684 Properties assetProps;
685 Properties versionProps;
686 Interfaces interfaces;
687 Object object;
688
689 assetProps.emplace(SN_PROP, sn);
690 assetProps.emplace(PN_PROP, pn);
691 assetProps.emplace(MODEL_PROP, ccin);
692 interfaces.emplace(ASSET_IFACE, std::move(assetProps));
693
694 versionProps.emplace(VERSION_PROP, version);
695 interfaces.emplace(VERSION_IFACE, std::move(versionProps));
696
697 //For Notify(), just send the relative path of the inventory
698 //object so remove the INVENTORY_OBJ_PATH prefix
699 auto path = inventoryPath.substr(strlen(INVENTORY_OBJ_PATH));
700
701 object.emplace(path, std::move(interfaces));
702
703 try
704 {
705 auto service = util::getService(
706 INVENTORY_OBJ_PATH,
707 INVENTORY_MGR_IFACE,
708 bus);
709
710 if (service.empty())
711 {
712 log<level::ERR>("Unable to get inventory manager service");
713 return;
714 }
715
716 auto method = bus.new_method_call(
717 service.c_str(),
718 INVENTORY_OBJ_PATH,
719 INVENTORY_MGR_IFACE,
720 "Notify");
721
722 method.append(std::move(object));
723
724 auto reply = bus.call(method);
725 if (reply.is_method_error())
726 {
727 log<level::ERR>(
728 "Unable to update power supply inventory properties",
729 entry("PATH=%s", path.c_str()));
730 }
731
732 // TODO: openbmc/openbmc#2756
733 // Calling Notify() with an enumerated property crashes inventory
734 // manager, so let it default to Unknown and now set it to the
735 // right value.
736 auto purpose = version::convertForMessage(
737 version::Version::VersionPurpose::Other);
738
739 util::setProperty(
740 VERSION_IFACE,
741 VERSION_PURPOSE_PROP,
742 inventoryPath,
743 service,
744 bus,
745 purpose);
746 }
747 catch (std::exception& e)
748 {
749 log<level::ERR>(
750 e.what(),
751 entry("PATH=%s", inventoryPath));
752 }
Matt Spinler234ce0d2018-01-04 15:06:57 -0600753}
754
Matt Spinler82384142018-01-18 14:15:03 -0600755void PowerSupply::enableHistory(const std::string& objectPath,
756 size_t numRecords,
757 const std::string& syncGPIOPath,
758 size_t syncGPIONum)
759{
760 historyObjectPath = objectPath;
761 syncGPIODevPath = syncGPIOPath;
762 syncGPIONumber = syncGPIONum;
763
764 recordManager = std::make_unique<history::RecordManager>(numRecords);
765
766 auto avgPath = historyObjectPath + '/' + history::Average::name;
767 auto maxPath = historyObjectPath + '/' + history::Maximum::name;
768
769 average = std::make_unique<history::Average>(bus, avgPath);
770
771 maximum = std::make_unique<history::Maximum>(bus, maxPath);
772}
773
Matt Spinlereb169fd2018-01-18 14:19:08 -0600774void PowerSupply::updateHistory()
775{
776 if (!recordManager)
777 {
778 //Not enabled
779 return;
780 }
781
782 //Read just the most recent average/max record
783 auto data = pmbusIntf.readBinary(
784 INPUT_HISTORY,
785 pmbus::Type::HwmonDeviceDebug,
786 history::RecordManager::RAW_RECORD_SIZE);
787
788 //Update D-Bus only if something changed (a new record ID, or cleared out)
789 auto changed = recordManager->add(data);
790 if (changed)
791 {
792 average->values(std::move(recordManager->getAverageRecords()));
793 maximum->values(std::move(recordManager->getMaximumRecords()));
794 }
795}
796
Brandon Wyman24e422f2017-07-25 19:40:14 -0500797}
798}
799}