blob: b26c96b2f7a46d264a554f20abce40e0bae70db4 [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"
Matt Spinlerd734e652018-01-18 14:31:15 -060022#include "gpio.hpp"
Brandon Wyman10295542017-08-09 18:20:44 -050023#include "names_values.hpp"
Brandon Wyman24e422f2017-07-25 19:40:14 -050024#include "power_supply.hpp"
Brandon Wyman442035f2017-08-08 15:58:45 -050025#include "pmbus.hpp"
26#include "utility.hpp"
27
Brandon Wyman1db9a9e2017-07-26 18:50:22 -050028namespace witherspoon
Brandon Wyman24e422f2017-07-25 19:40:14 -050029{
30namespace power
31{
32namespace psu
33{
34
Matt Spinler589e8722018-01-04 15:24:49 -060035using namespace phosphor::logging;
36using namespace sdbusplus::org::open_power::Witherspoon::Fault::Error;
37using namespace sdbusplus::xyz::openbmc_project::Common::Device::Error;
Matt Spinler018a7bc2018-01-04 15:36:41 -060038namespace version = sdbusplus::xyz::openbmc_project::Software::server;
Matt Spinler589e8722018-01-04 15:24:49 -060039
40constexpr auto ASSOCIATION_IFACE = "org.openbmc.Association";
41constexpr auto LOGGING_IFACE = "xyz.openbmc_project.Logging.Entry";
42constexpr auto INVENTORY_IFACE = "xyz.openbmc_project.Inventory.Item";
43constexpr auto POWER_IFACE = "org.openbmc.control.Power";
Matt Spinler018a7bc2018-01-04 15:36:41 -060044constexpr auto INVENTORY_MGR_IFACE = "xyz.openbmc_project.Inventory.Manager";
45constexpr auto ASSET_IFACE = "xyz.openbmc_project.Inventory.Decorator.Asset";
46constexpr auto VERSION_IFACE = "xyz.openbmc_project.Software.Version";
Matt Spinler589e8722018-01-04 15:24:49 -060047
48constexpr auto ENDPOINTS_PROP = "endpoints";
49constexpr auto MESSAGE_PROP = "Message";
50constexpr auto RESOLVED_PROP = "Resolved";
Brandon Wyman10295542017-08-09 18:20:44 -050051constexpr auto PRESENT_PROP = "Present";
Matt Spinler018a7bc2018-01-04 15:36:41 -060052constexpr auto SN_PROP = "SerialNumber";
53constexpr auto PN_PROP = "PartNumber";
54constexpr auto MODEL_PROP = "Model";
55constexpr auto VERSION_PROP = "Version";
56constexpr auto VERSION_PURPOSE_PROP = "Purpose";
Matt Spinler589e8722018-01-04 15:24:49 -060057
58constexpr auto INVENTORY_OBJ_PATH = "/xyz/openbmc_project/inventory";
Brandon Wyman431fbe42017-08-18 16:22:09 -050059constexpr auto POWER_OBJ_PATH = "/org/openbmc/control/power0";
Brandon Wyman10295542017-08-09 18:20:44 -050060
Matt Spinler018a7bc2018-01-04 15:36:41 -060061constexpr auto SERIAL_NUMBER = "serial_number";
62constexpr auto PART_NUMBER = "part_number";
63constexpr auto FW_VERSION = "fw_version";
64constexpr auto CCIN = "ccin";
Matt Spinlereb169fd2018-01-18 14:19:08 -060065constexpr auto INPUT_HISTORY = "input_history";
Matt Spinler018a7bc2018-01-04 15:36:41 -060066
Brandon Wyman10295542017-08-09 18:20:44 -050067PowerSupply::PowerSupply(const std::string& name, size_t inst,
Brandon Wyman431fbe42017-08-18 16:22:09 -050068 const std::string& objpath,
69 const std::string& invpath,
70 sdbusplus::bus::bus& bus,
71 event::Event& e,
Brandon Wyman590fc282017-11-01 18:22:25 -050072 std::chrono::seconds& t,
73 std::chrono::seconds& p)
Brandon Wyman431fbe42017-08-18 16:22:09 -050074 : Device(name, inst), monitorPath(objpath), pmbusIntf(objpath),
Brandon Wyman50bb85d2017-11-01 18:36:00 -050075 inventoryPath(INVENTORY_OBJ_PATH + invpath), bus(bus), event(e),
76 presentInterval(p),
Brandon Wyman590fc282017-11-01 18:22:25 -050077 presentTimer(e, [this]()
78 {
Brandon Wyman2877add2017-11-10 17:44:19 -060079 // The hwmon path may have changed.
80 pmbusIntf.findHwmonDir();
Brandon Wyman590fc282017-11-01 18:22:25 -050081 this->present = true;
Matt Spinler234ce0d2018-01-04 15:06:57 -060082
Matt Spinlerd734e652018-01-18 14:31:15 -060083 // Sync the INPUT_HISTORY data for all PSs
84 syncHistory();
85
Matt Spinler234ce0d2018-01-04 15:06:57 -060086 // Update the inventory for the new device
87 updateInventory();
Brandon Wyman590fc282017-11-01 18:22:25 -050088 }),
89 powerOnInterval(t),
Brandon Wyman431fbe42017-08-18 16:22:09 -050090 powerOnTimer(e, [this]()
91 {
92 this->powerOn = true;
93 })
Brandon Wyman10295542017-08-09 18:20:44 -050094{
Brandon Wyman10295542017-08-09 18:20:44 -050095 using namespace sdbusplus::bus;
Brandon Wyman10295542017-08-09 18:20:44 -050096 presentMatch = std::make_unique<match_t>(bus,
97 match::rules::propertiesChanged(
Brandon Wyman50bb85d2017-11-01 18:36:00 -050098 inventoryPath,
Matt Spinler589e8722018-01-04 15:24:49 -060099 INVENTORY_IFACE),
Brandon Wyman10295542017-08-09 18:20:44 -0500100 [this](auto& msg)
Brandon Wyman431fbe42017-08-18 16:22:09 -0500101 {
102 this->inventoryChanged(msg);
103 });
104 // Get initial presence state.
Brandon Wyman253dc9b2017-08-12 13:45:52 -0500105 updatePresence();
Brandon Wyman431fbe42017-08-18 16:22:09 -0500106
Matt Spinler234ce0d2018-01-04 15:06:57 -0600107 // Write the SN, PN, etc to the inventory
108 updateInventory();
109
Brandon Wyman431fbe42017-08-18 16:22:09 -0500110 // Subscribe to power state changes
111 powerOnMatch = std::make_unique<match_t>(bus,
112 match::rules::propertiesChanged(
113 POWER_OBJ_PATH,
Matt Spinler589e8722018-01-04 15:24:49 -0600114 POWER_IFACE),
Brandon Wyman431fbe42017-08-18 16:22:09 -0500115 [this](auto& msg)
116 {
117 this->powerStateChanged(msg);
118 });
119 // Get initial power state.
120 updatePowerState();
Brandon Wyman10295542017-08-09 18:20:44 -0500121}
Brandon Wyman442035f2017-08-08 15:58:45 -0500122
Brandon Wymana1e96342017-09-25 16:47:44 -0500123void PowerSupply::captureCmd(util::NamesValues& nv, const std::string& cmd,
124 witherspoon::pmbus::Type type)
125{
126 if (pmbusIntf.exists(cmd, type))
127 {
128 try
129 {
130 auto val = pmbusIntf.read(cmd, type);
131 nv.add(cmd, val);
132 }
133 catch (std::exception& e)
134 {
135 log<level::INFO>("Unable to capture metadata", entry("CMD=%s",
136 cmd));
137 }
138 }
139}
Brandon Wyman431fbe42017-08-18 16:22:09 -0500140
Brandon Wyman1db9a9e2017-07-26 18:50:22 -0500141void PowerSupply::analyze()
142{
Brandon Wyman442035f2017-08-08 15:58:45 -0500143 using namespace witherspoon::pmbus;
144
145 try
146 {
Brandon Wyman10295542017-08-09 18:20:44 -0500147 if (present)
Brandon Wyman442035f2017-08-08 15:58:45 -0500148 {
Brandon Wyman764c7972017-08-22 17:05:36 -0500149 std::uint16_t statusWord = 0;
Brandon Wyman764c7972017-08-22 17:05:36 -0500150
151 // Read the 2 byte STATUS_WORD value to check for faults.
152 statusWord = pmbusIntf.read(STATUS_WORD, Type::Debug);
Brandon Wymane4af9802017-11-13 15:58:33 -0600153 readFail = 0;
Brandon Wyman764c7972017-08-22 17:05:36 -0500154
Brandon Wyman603cc002017-08-28 18:17:58 -0500155 checkInputFault(statusWord);
Brandon Wyman764c7972017-08-22 17:05:36 -0500156
Brandon Wyman654eb6e2018-02-14 14:55:47 -0600157 if (powerOn && (inputFault == 0) && !faultFound)
Brandon Wyman764c7972017-08-22 17:05:36 -0500158 {
Brandon Wyman12661f12017-08-31 15:28:21 -0500159 checkFanFault(statusWord);
Brandon Wyman875b3632017-09-13 18:46:03 -0500160 checkTemperatureFault(statusWord);
Brandon Wymancfa032b2017-09-25 17:37:50 -0500161 checkOutputOvervoltageFault(statusWord);
162 checkCurrentOutOverCurrentFault(statusWord);
163 checkPGOrUnitOffFault(statusWord);
Brandon Wyman442035f2017-08-08 15:58:45 -0500164 }
Matt Spinlereb169fd2018-01-18 14:19:08 -0600165
166 updateHistory();
Brandon Wyman442035f2017-08-08 15:58:45 -0500167 }
168 }
169 catch (ReadFailure& e)
170 {
Brandon Wymane4af9802017-11-13 15:58:33 -0600171 if (readFail < FAULT_COUNT)
172 {
173 readFail++;
174 }
175
176 if (!readFailLogged && readFail >= FAULT_COUNT)
Brandon Wyman442035f2017-08-08 15:58:45 -0500177 {
178 commit<ReadFailure>();
179 readFailLogged = true;
Brandon Wyman442035f2017-08-08 15:58:45 -0500180 }
181 }
182
Brandon Wyman1db9a9e2017-07-26 18:50:22 -0500183 return;
184}
185
Brandon Wyman10295542017-08-09 18:20:44 -0500186void PowerSupply::inventoryChanged(sdbusplus::message::message& msg)
187{
188 std::string msgSensor;
189 std::map<std::string, sdbusplus::message::variant<uint32_t, bool>> msgData;
190 msg.read(msgSensor, msgData);
191
192 // Check if it was the Present property that changed.
193 auto valPropMap = msgData.find(PRESENT_PROP);
194 if (valPropMap != msgData.end())
195 {
Brandon Wyman2ef48cf2017-11-21 15:43:54 -0600196 if (sdbusplus::message::variant_ns::get<bool>(valPropMap->second))
Brandon Wyman10295542017-08-09 18:20:44 -0500197 {
Brandon Wyman6ccce0b2017-10-26 15:13:10 -0500198 clearFaults();
Brandon Wyman590fc282017-11-01 18:22:25 -0500199 presentTimer.start(presentInterval, Timer::TimerType::oneshot);
200 }
201 else
202 {
Brandon Wyman2ef48cf2017-11-21 15:43:54 -0600203 present = false;
Brandon Wyman590fc282017-11-01 18:22:25 -0500204 presentTimer.stop();
Matt Spinler234ce0d2018-01-04 15:06:57 -0600205
206 //Clear out the now outdated inventory properties
207 updateInventory();
Brandon Wyman10295542017-08-09 18:20:44 -0500208 }
209 }
210
211 return;
212}
213
214void PowerSupply::updatePresence()
215{
216 // Use getProperty utility function to get presence status.
Brandon Wyman10295542017-08-09 18:20:44 -0500217 std::string service = "xyz.openbmc_project.Inventory.Manager";
Matt Spinler589e8722018-01-04 15:24:49 -0600218 util::getProperty(INVENTORY_IFACE, PRESENT_PROP, inventoryPath,
Brandon Wyman50bb85d2017-11-01 18:36:00 -0500219 service, bus, this->present);
Brandon Wyman10295542017-08-09 18:20:44 -0500220}
221
Brandon Wyman431fbe42017-08-18 16:22:09 -0500222void PowerSupply::powerStateChanged(sdbusplus::message::message& msg)
223{
224 int32_t state = 0;
225 std::string msgSensor;
226 std::map<std::string, sdbusplus::message::variant<int32_t, int32_t>>
227 msgData;
228 msg.read(msgSensor, msgData);
229
230 // Check if it was the Present property that changed.
231 auto valPropMap = msgData.find("state");
232 if (valPropMap != msgData.end())
233 {
234 state = sdbusplus::message::variant_ns::get<int32_t>(valPropMap->second);
235
236 // Power is on when state=1. Set the fault logged variables to false
237 // and start the power on timer when the state changes to 1.
238 if (state)
239 {
Brandon Wyman6ccce0b2017-10-26 15:13:10 -0500240 clearFaults();
Brandon Wyman431fbe42017-08-18 16:22:09 -0500241 powerOnTimer.start(powerOnInterval, Timer::TimerType::oneshot);
242 }
243 else
244 {
245 powerOnTimer.stop();
246 powerOn = false;
247 }
248 }
249
250}
251
252void PowerSupply::updatePowerState()
253{
254 // When state = 1, system is powered on
255 int32_t state = 0;
256
257 try
258 {
259 auto service = util::getService(POWER_OBJ_PATH,
Matt Spinler589e8722018-01-04 15:24:49 -0600260 POWER_IFACE,
Brandon Wyman431fbe42017-08-18 16:22:09 -0500261 bus);
262
263 // Use getProperty utility function to get power state.
Matt Spinler589e8722018-01-04 15:24:49 -0600264 util::getProperty<int32_t>(POWER_IFACE,
Brandon Wyman431fbe42017-08-18 16:22:09 -0500265 "state",
266 POWER_OBJ_PATH,
267 service,
268 bus,
269 state);
270
271 if (state)
272 {
273 powerOn = true;
274 }
275 else
276 {
277 powerOn = false;
278 }
279 }
280 catch (std::exception& e)
281 {
282 log<level::INFO>("Failed to get power state. Assuming it is off.");
283 powerOn = false;
284 }
285
286}
287
Brandon Wyman603cc002017-08-28 18:17:58 -0500288void PowerSupply::checkInputFault(const uint16_t statusWord)
289{
290 using namespace witherspoon::pmbus;
291
Brandon Wymana3c675c2017-11-14 14:54:54 -0600292 if ((inputFault < FAULT_COUNT) &&
293 ((statusWord & status_word::INPUT_FAULT_WARN) ||
294 (statusWord & status_word::VIN_UV_FAULT)))
Brandon Wyman603cc002017-08-28 18:17:58 -0500295 {
Brandon Wymanad708242018-01-19 17:28:35 -0600296 if (inputFault == 0)
297 {
298 log<level::INFO>("INPUT or VIN_UV fault",
299 entry("STATUS_WORD=0x%04X", statusWord));
300 }
301
Brandon Wymana3c675c2017-11-14 14:54:54 -0600302 inputFault++;
Brandon Wyman603cc002017-08-28 18:17:58 -0500303 }
304 else
305 {
Brandon Wymana3c675c2017-11-14 14:54:54 -0600306 if ((inputFault > 0) &&
Brandon Wymand20686a2017-11-01 17:45:23 -0500307 !(statusWord & status_word::INPUT_FAULT_WARN) &&
308 !(statusWord & status_word::VIN_UV_FAULT))
Brandon Wyman603cc002017-08-28 18:17:58 -0500309 {
Brandon Wymana3c675c2017-11-14 14:54:54 -0600310 inputFault = 0;
Brandon Wyman3343e822017-11-03 16:54:11 -0500311 faultFound = false;
Brandon Wyman7502f6c2018-02-12 20:20:39 -0600312 // When an input fault occurs, the power supply cannot be on.
313 // However, the check for the case where the power supply should be
314 // on will stop when there is a fault found.
315 // Clear the powerOnFault when the inputFault is cleared to reset
316 // the powerOnFault de-glitching.
317 powerOnFault = 0;
Brandon Wyman69591bd2017-11-01 18:07:23 -0500318
Brandon Wyman603cc002017-08-28 18:17:58 -0500319 log<level::INFO>("INPUT_FAULT_WARN cleared",
Brandon Wymana3c675c2017-11-14 14:54:54 -0600320 entry("POWERSUPPLY=%s", inventoryPath.c_str()));
Brandon Wyman69591bd2017-11-01 18:07:23 -0500321
Brandon Wyman08b05712017-11-30 17:53:56 -0600322 resolveError(inventoryPath,
323 std::string(PowerSupplyInputFault::errName));
324
Brandon Wyman69591bd2017-11-01 18:07:23 -0500325 if (powerOn)
326 {
327 // The power supply will not be immediately powered on after
328 // the input power is restored.
329 powerOn = false;
330 // Start up the timer that will set the state to indicate we
331 // are ready for the powered on fault checks.
332 powerOnTimer.start(powerOnInterval, Timer::TimerType::oneshot);
333 }
Brandon Wyman603cc002017-08-28 18:17:58 -0500334 }
335 }
Brandon Wymana3c675c2017-11-14 14:54:54 -0600336
337 if (!faultFound && (inputFault >= FAULT_COUNT))
338 {
Brandon Wymanad708242018-01-19 17:28:35 -0600339 // If the power is on, report the fault in an error log entry.
340 if (powerOn)
341 {
342 util::NamesValues nv;
343 nv.add("STATUS_WORD", statusWord);
344 captureCmd(nv, STATUS_INPUT, Type::Debug);
Brandon Wymana3c675c2017-11-14 14:54:54 -0600345
Brandon Wymanad708242018-01-19 17:28:35 -0600346 using metadata = org::open_power::Witherspoon::Fault::
347 PowerSupplyInputFault;
Brandon Wymana3c675c2017-11-14 14:54:54 -0600348
Brandon Wymanad708242018-01-19 17:28:35 -0600349 report<PowerSupplyInputFault>(
350 metadata::RAW_STATUS(nv.get().c_str()),
351 metadata::CALLOUT_INVENTORY_PATH(inventoryPath.c_str()));
352
353 faultFound = true;
354 }
Brandon Wymana3c675c2017-11-14 14:54:54 -0600355 }
356
Brandon Wyman603cc002017-08-28 18:17:58 -0500357}
358
359void PowerSupply::checkPGOrUnitOffFault(const uint16_t statusWord)
360{
361 using namespace witherspoon::pmbus;
362
Brandon Wyman593d24f2017-10-13 18:15:23 -0500363 if (powerOnFault < FAULT_COUNT)
Brandon Wyman603cc002017-08-28 18:17:58 -0500364 {
Brandon Wyman593d24f2017-10-13 18:15:23 -0500365 // Check PG# and UNIT_IS_OFF
366 if ((statusWord & status_word::POWER_GOOD_NEGATED) ||
367 (statusWord & status_word::UNIT_IS_OFF))
368 {
369 log<level::INFO>("PGOOD or UNIT_IS_OFF bit bad",
370 entry("STATUS_WORD=0x%04X", statusWord));
371 powerOnFault++;
372 }
373 else
374 {
375 if (powerOnFault > 0)
376 {
377 log<level::INFO>("PGOOD and UNIT_IS_OFF bits good");
378 powerOnFault = 0;
379 }
380 }
Brandon Wyman603cc002017-08-28 18:17:58 -0500381
Brandon Wymane2fc7aa2017-11-13 17:37:10 -0600382 if (!faultFound && (powerOnFault >= FAULT_COUNT))
Brandon Wyman593d24f2017-10-13 18:15:23 -0500383 {
Brandon Wyman3343e822017-11-03 16:54:11 -0500384 faultFound = true;
385
Brandon Wyman593d24f2017-10-13 18:15:23 -0500386 util::NamesValues nv;
387 nv.add("STATUS_WORD", statusWord);
388 captureCmd(nv, STATUS_INPUT, Type::Debug);
389 auto status0Vout = pmbusIntf.insertPageNum(STATUS_VOUT, 0);
390 captureCmd(nv, status0Vout, Type::Debug);
391 captureCmd(nv, STATUS_IOUT, Type::Debug);
392 captureCmd(nv, STATUS_MFR, Type::Debug);
Brandon Wyman603cc002017-08-28 18:17:58 -0500393
Brandon Wyman593d24f2017-10-13 18:15:23 -0500394 using metadata = org::open_power::Witherspoon::Fault::
395 PowerSupplyShouldBeOn;
Brandon Wyman603cc002017-08-28 18:17:58 -0500396
Brandon Wyman593d24f2017-10-13 18:15:23 -0500397 // A power supply is OFF (or pgood low) but should be on.
398 report<PowerSupplyShouldBeOn>(
399 metadata::RAW_STATUS(nv.get().c_str()),
400 metadata::CALLOUT_INVENTORY_PATH(
401 inventoryPath.c_str()));
402 }
Brandon Wyman603cc002017-08-28 18:17:58 -0500403 }
404
405}
406
407void PowerSupply::checkCurrentOutOverCurrentFault(const uint16_t statusWord)
408{
409 using namespace witherspoon::pmbus;
410
Brandon Wymandd61be42017-11-07 18:38:54 -0600411 if (outputOCFault < FAULT_COUNT)
Brandon Wyman603cc002017-08-28 18:17:58 -0500412 {
Brandon Wymandd61be42017-11-07 18:38:54 -0600413 // Check for an output overcurrent fault.
414 if ((statusWord & status_word::IOUT_OC_FAULT))
415 {
416 outputOCFault++;
417 }
418 else
419 {
420 if (outputOCFault > 0)
421 {
422 outputOCFault = 0;
423 }
424 }
Brandon Wyman603cc002017-08-28 18:17:58 -0500425
Brandon Wymane2fc7aa2017-11-13 17:37:10 -0600426 if (!faultFound && (outputOCFault >= FAULT_COUNT))
Brandon Wymandd61be42017-11-07 18:38:54 -0600427 {
428 util::NamesValues nv;
429 nv.add("STATUS_WORD", statusWord);
430 captureCmd(nv, STATUS_INPUT, Type::Debug);
431 auto status0Vout = pmbusIntf.insertPageNum(STATUS_VOUT, 0);
432 captureCmd(nv, status0Vout, Type::Debug);
433 captureCmd(nv, STATUS_IOUT, Type::Debug);
434 captureCmd(nv, STATUS_MFR, Type::Debug);
Brandon Wyman603cc002017-08-28 18:17:58 -0500435
Brandon Wymandd61be42017-11-07 18:38:54 -0600436 using metadata = org::open_power::Witherspoon::Fault::
437 PowerSupplyOutputOvercurrent;
Brandon Wyman603cc002017-08-28 18:17:58 -0500438
Brandon Wymandd61be42017-11-07 18:38:54 -0600439 report<PowerSupplyOutputOvercurrent>(
440 metadata::RAW_STATUS(nv.get().c_str()),
441 metadata::CALLOUT_INVENTORY_PATH(inventoryPath.c_str()));
442
443 faultFound = true;
444 }
Brandon Wyman603cc002017-08-28 18:17:58 -0500445 }
446}
447
Brandon Wymanab05c072017-08-30 18:26:41 -0500448void PowerSupply::checkOutputOvervoltageFault(const uint16_t statusWord)
449{
450 using namespace witherspoon::pmbus;
451
Brandon Wyman2ab319b2017-11-08 17:34:59 -0600452 if (outputOVFault < FAULT_COUNT)
Brandon Wymanab05c072017-08-30 18:26:41 -0500453 {
Brandon Wyman2ab319b2017-11-08 17:34:59 -0600454 // Check for an output overvoltage fault.
455 if (statusWord & status_word::VOUT_OV_FAULT)
456 {
457 outputOVFault++;
458 }
459 else
460 {
461 if (outputOVFault > 0)
462 {
463 outputOVFault = 0;
464 }
465 }
Brandon Wymanab05c072017-08-30 18:26:41 -0500466
Brandon Wymane2fc7aa2017-11-13 17:37:10 -0600467 if (!faultFound && (outputOVFault >= FAULT_COUNT))
Brandon Wyman2ab319b2017-11-08 17:34:59 -0600468 {
469 util::NamesValues nv;
470 nv.add("STATUS_WORD", statusWord);
471 captureCmd(nv, STATUS_INPUT, Type::Debug);
472 auto status0Vout = pmbusIntf.insertPageNum(STATUS_VOUT, 0);
473 captureCmd(nv, status0Vout, Type::Debug);
474 captureCmd(nv, STATUS_IOUT, Type::Debug);
475 captureCmd(nv, STATUS_MFR, Type::Debug);
Brandon Wymanab05c072017-08-30 18:26:41 -0500476
Brandon Wyman2ab319b2017-11-08 17:34:59 -0600477 using metadata = org::open_power::Witherspoon::Fault::
478 PowerSupplyOutputOvervoltage;
Brandon Wymanab05c072017-08-30 18:26:41 -0500479
Brandon Wyman2ab319b2017-11-08 17:34:59 -0600480 report<PowerSupplyOutputOvervoltage>(
481 metadata::RAW_STATUS(nv.get().c_str()),
482 metadata::CALLOUT_INVENTORY_PATH(inventoryPath.c_str()));
483
484 faultFound = true;
485 }
Brandon Wymanab05c072017-08-30 18:26:41 -0500486 }
487}
488
Brandon Wyman12661f12017-08-31 15:28:21 -0500489void PowerSupply::checkFanFault(const uint16_t statusWord)
490{
491 using namespace witherspoon::pmbus;
492
Brandon Wymanba255532017-11-08 17:44:10 -0600493 if (fanFault < FAULT_COUNT)
Brandon Wyman12661f12017-08-31 15:28:21 -0500494 {
Brandon Wymanba255532017-11-08 17:44:10 -0600495 // Check for a fan fault or warning condition
496 if (statusWord & status_word::FAN_FAULT)
497 {
498 fanFault++;
499 }
500 else
501 {
502 if (fanFault > 0)
503 {
504 fanFault = 0;
505 }
506 }
Brandon Wyman12661f12017-08-31 15:28:21 -0500507
Brandon Wymane2fc7aa2017-11-13 17:37:10 -0600508 if (!faultFound && (fanFault >= FAULT_COUNT))
Brandon Wymanba255532017-11-08 17:44:10 -0600509 {
510 util::NamesValues nv;
511 nv.add("STATUS_WORD", statusWord);
512 captureCmd(nv, STATUS_MFR, Type::Debug);
513 captureCmd(nv, STATUS_TEMPERATURE, Type::Debug);
514 captureCmd(nv, STATUS_FANS_1_2, Type::Debug);
Brandon Wyman12661f12017-08-31 15:28:21 -0500515
Brandon Wymanba255532017-11-08 17:44:10 -0600516 using metadata = org::open_power::Witherspoon::Fault::
517 PowerSupplyFanFault;
Brandon Wyman12661f12017-08-31 15:28:21 -0500518
Brandon Wymanba255532017-11-08 17:44:10 -0600519 report<PowerSupplyFanFault>(
520 metadata::RAW_STATUS(nv.get().c_str()),
521 metadata::CALLOUT_INVENTORY_PATH(inventoryPath.c_str()));
522
523 faultFound = true;
524 }
Brandon Wyman12661f12017-08-31 15:28:21 -0500525 }
526}
527
Brandon Wyman875b3632017-09-13 18:46:03 -0500528void PowerSupply::checkTemperatureFault(const uint16_t statusWord)
529{
530 using namespace witherspoon::pmbus;
531
532 // Due to how the PMBus core device driver sends a clear faults command
533 // the bit in STATUS_WORD will likely be cleared when we attempt to examine
534 // it for a Thermal Fault or Warning. So, check the STATUS_WORD and the
535 // STATUS_TEMPERATURE bits. If either indicates a fault, proceed with
536 // logging the over-temperature condition.
537 std::uint8_t statusTemperature = 0;
538 statusTemperature = pmbusIntf.read(STATUS_TEMPERATURE, Type::Debug);
Brandon Wyman50044ea2017-11-08 17:58:56 -0600539 if (temperatureFault < FAULT_COUNT)
Brandon Wyman875b3632017-09-13 18:46:03 -0500540 {
Brandon Wyman50044ea2017-11-08 17:58:56 -0600541 if ((statusWord & status_word::TEMPERATURE_FAULT_WARN) ||
542 (statusTemperature & status_temperature::OT_FAULT))
543 {
544 temperatureFault++;
545 }
546 else
547 {
548 if (temperatureFault > 0)
549 {
550 temperatureFault = 0;
551 }
552 }
Brandon Wyman875b3632017-09-13 18:46:03 -0500553
Brandon Wymane2fc7aa2017-11-13 17:37:10 -0600554 if (!faultFound && (temperatureFault >= FAULT_COUNT))
Brandon Wyman50044ea2017-11-08 17:58:56 -0600555 {
556 // The power supply has had an over-temperature condition.
557 // This may not result in a shutdown if experienced for a short
558 // duration.
559 // This should not occur under normal conditions.
560 // The power supply may be faulty, or the paired supply may be
561 // putting out less current.
562 // Capture command responses with potentially relevant information,
563 // and call out the power supply reporting the condition.
564 util::NamesValues nv;
565 nv.add("STATUS_WORD", statusWord);
566 captureCmd(nv, STATUS_MFR, Type::Debug);
567 captureCmd(nv, STATUS_IOUT, Type::Debug);
568 nv.add("STATUS_TEMPERATURE", statusTemperature);
569 captureCmd(nv, STATUS_FANS_1_2, Type::Debug);
Brandon Wyman875b3632017-09-13 18:46:03 -0500570
Brandon Wyman50044ea2017-11-08 17:58:56 -0600571 using metadata = org::open_power::Witherspoon::Fault::
572 PowerSupplyTemperatureFault;
Brandon Wyman875b3632017-09-13 18:46:03 -0500573
Brandon Wyman50044ea2017-11-08 17:58:56 -0600574 report<PowerSupplyTemperatureFault>(
575 metadata::RAW_STATUS(nv.get().c_str()),
576 metadata::CALLOUT_INVENTORY_PATH(inventoryPath.c_str()));
577
578 faultFound = true;
579 }
Brandon Wyman875b3632017-09-13 18:46:03 -0500580 }
581}
582
Brandon Wyman1db9a9e2017-07-26 18:50:22 -0500583void PowerSupply::clearFaults()
584{
Brandon Wymane4af9802017-11-13 15:58:33 -0600585 readFail = 0;
Brandon Wyman6ccce0b2017-10-26 15:13:10 -0500586 readFailLogged = false;
Brandon Wymana3c675c2017-11-14 14:54:54 -0600587 inputFault = 0;
Brandon Wyman6ccce0b2017-10-26 15:13:10 -0500588 powerOnFault = 0;
Brandon Wymandd61be42017-11-07 18:38:54 -0600589 outputOCFault = 0;
Brandon Wyman2ab319b2017-11-08 17:34:59 -0600590 outputOVFault = 0;
Brandon Wymanba255532017-11-08 17:44:10 -0600591 fanFault = 0;
Brandon Wyman50044ea2017-11-08 17:58:56 -0600592 temperatureFault = 0;
Brandon Wyman3343e822017-11-03 16:54:11 -0500593 faultFound = false;
Brandon Wyman6ccce0b2017-10-26 15:13:10 -0500594
Brandon Wyman1db9a9e2017-07-26 18:50:22 -0500595 return;
596}
597
Brandon Wyman43ce2082017-11-30 17:24:01 -0600598void PowerSupply::resolveError(const std::string& callout,
599 const std::string& message)
600{
Brandon Wyman01741f12017-12-01 17:22:08 -0600601 using EndpointList = std::vector<std::string>;
602
603 try
604 {
605 auto path = callout + "/fault";
606 // Get the service name from the mapper for the fault callout
607 auto service = util::getService(path,
608 ASSOCIATION_IFACE,
609 bus);
610
611 // Use getProperty utility function to get log entries (endpoints)
612 EndpointList logEntries;
Matt Spinler589e8722018-01-04 15:24:49 -0600613 util::getProperty(ASSOCIATION_IFACE, ENDPOINTS_PROP, path, service,
Brandon Wyman01741f12017-12-01 17:22:08 -0600614 bus, logEntries);
615
616 // It is possible that all such entries for this callout have since
617 // been deleted.
618 if (logEntries.empty())
619 {
620 return;
621 }
622
623 auto logEntryService = util::getService(logEntries[0], LOGGING_IFACE,
624 bus);
625 if (logEntryService.empty())
626 {
627 return;
628 }
629
630 // go through each log entry that matches this callout path
631 std::string logMessage;
632 for (const auto& logEntry : logEntries)
633 {
634 // Check to see if this logEntry has a message that matches.
Matt Spinler589e8722018-01-04 15:24:49 -0600635 util::getProperty(LOGGING_IFACE, MESSAGE_PROP, logEntry,
Brandon Wyman01741f12017-12-01 17:22:08 -0600636 logEntryService, bus, logMessage);
637
638 if (message == logMessage)
639 {
640 // Log entry matches call out and message, set Resolved to true
641 bool resolved = true;
Matt Spinler589e8722018-01-04 15:24:49 -0600642 util::setProperty(LOGGING_IFACE, RESOLVED_PROP, logEntry,
Brandon Wyman01741f12017-12-01 17:22:08 -0600643 logEntryService, bus, resolved);
644 }
645
646 }
647
648 }
649 catch (std::exception& e)
650 {
651 log<level::INFO>("Failed to resolve error",
652 entry("CALLOUT=%s", callout.c_str()),
Matt Spinler0d09f292018-01-22 14:51:26 -0600653 entry("ERROR=%s", message.c_str()));
Brandon Wyman01741f12017-12-01 17:22:08 -0600654 }
655
Brandon Wyman43ce2082017-11-30 17:24:01 -0600656}
657
Matt Spinler234ce0d2018-01-04 15:06:57 -0600658void PowerSupply::updateInventory()
659{
Matt Spinler018a7bc2018-01-04 15:36:41 -0600660 using namespace witherspoon::pmbus;
661 using namespace sdbusplus::message;
662
663 // If any of these accesses fail, the fields will just be
664 // blank in the inventory. Leave logging ReadFailure errors
665 // to analyze() as it runs continuously and will most
666 // likely hit and threshold them first anyway. The
667 // readString() function will do the tracing of the failing
668 // path so this code doesn't need to.
669 std::string pn;
670 std::string sn;
671 std::string ccin;
672 std::string version;
673
674 if (present)
675 {
676 try
677 {
678 sn = pmbusIntf.readString(SERIAL_NUMBER, Type::HwmonDeviceDebug);
679 }
680 catch (ReadFailure& e) { }
681
682 try
683 {
684 pn = pmbusIntf.readString(PART_NUMBER, Type::HwmonDeviceDebug);
685 }
686 catch (ReadFailure& e) { }
687
688 try
689 {
690 ccin = pmbusIntf.readString(CCIN, Type::HwmonDeviceDebug);
691 }
692 catch (ReadFailure& e) { }
693
694 try
695 {
696 version = pmbusIntf.readString(FW_VERSION, Type::HwmonDeviceDebug);
697 }
698 catch (ReadFailure& e) { }
699 }
700
701 // Build the object map and send it to the inventory
702 using Properties = std::map<std::string, variant<std::string>>;
703 using Interfaces = std::map<std::string, Properties>;
704 using Object = std::map<object_path, Interfaces>;
705 Properties assetProps;
706 Properties versionProps;
707 Interfaces interfaces;
708 Object object;
709
710 assetProps.emplace(SN_PROP, sn);
711 assetProps.emplace(PN_PROP, pn);
712 assetProps.emplace(MODEL_PROP, ccin);
713 interfaces.emplace(ASSET_IFACE, std::move(assetProps));
714
715 versionProps.emplace(VERSION_PROP, version);
716 interfaces.emplace(VERSION_IFACE, std::move(versionProps));
717
718 //For Notify(), just send the relative path of the inventory
719 //object so remove the INVENTORY_OBJ_PATH prefix
720 auto path = inventoryPath.substr(strlen(INVENTORY_OBJ_PATH));
721
722 object.emplace(path, std::move(interfaces));
723
724 try
725 {
726 auto service = util::getService(
727 INVENTORY_OBJ_PATH,
728 INVENTORY_MGR_IFACE,
729 bus);
730
731 if (service.empty())
732 {
733 log<level::ERR>("Unable to get inventory manager service");
734 return;
735 }
736
737 auto method = bus.new_method_call(
738 service.c_str(),
739 INVENTORY_OBJ_PATH,
740 INVENTORY_MGR_IFACE,
741 "Notify");
742
743 method.append(std::move(object));
744
745 auto reply = bus.call(method);
746 if (reply.is_method_error())
747 {
748 log<level::ERR>(
749 "Unable to update power supply inventory properties",
750 entry("PATH=%s", path.c_str()));
751 }
752
753 // TODO: openbmc/openbmc#2756
754 // Calling Notify() with an enumerated property crashes inventory
755 // manager, so let it default to Unknown and now set it to the
756 // right value.
757 auto purpose = version::convertForMessage(
758 version::Version::VersionPurpose::Other);
759
760 util::setProperty(
761 VERSION_IFACE,
762 VERSION_PURPOSE_PROP,
763 inventoryPath,
764 service,
765 bus,
766 purpose);
767 }
768 catch (std::exception& e)
769 {
770 log<level::ERR>(
771 e.what(),
772 entry("PATH=%s", inventoryPath));
773 }
Matt Spinler234ce0d2018-01-04 15:06:57 -0600774}
775
Matt Spinlerd734e652018-01-18 14:31:15 -0600776void PowerSupply::syncHistory()
777{
778 using namespace witherspoon::gpio;
779
780 if (syncGPIODevPath.empty())
781 {
782 //Sync not implemented
783 return;
784 }
785
786 GPIO gpio{syncGPIODevPath,
787 static_cast<gpioNum_t>(syncGPIONumber),
788 Direction::output};
789
790 try
791 {
792 gpio.set(Value::low);
793
794 std::this_thread::sleep_for(std::chrono::milliseconds{5});
795
796 gpio.set(Value::high);
797
798 recordManager->clear();
799 }
800 catch (std::exception& e)
801 {
802 //Do nothing. There would already be a journal entry.
803 }
804}
805
Matt Spinler82384142018-01-18 14:15:03 -0600806void PowerSupply::enableHistory(const std::string& objectPath,
807 size_t numRecords,
808 const std::string& syncGPIOPath,
809 size_t syncGPIONum)
810{
811 historyObjectPath = objectPath;
812 syncGPIODevPath = syncGPIOPath;
813 syncGPIONumber = syncGPIONum;
814
815 recordManager = std::make_unique<history::RecordManager>(numRecords);
816
817 auto avgPath = historyObjectPath + '/' + history::Average::name;
818 auto maxPath = historyObjectPath + '/' + history::Maximum::name;
819
820 average = std::make_unique<history::Average>(bus, avgPath);
821
822 maximum = std::make_unique<history::Maximum>(bus, maxPath);
823}
824
Matt Spinlereb169fd2018-01-18 14:19:08 -0600825void PowerSupply::updateHistory()
826{
827 if (!recordManager)
828 {
829 //Not enabled
830 return;
831 }
832
833 //Read just the most recent average/max record
834 auto data = pmbusIntf.readBinary(
835 INPUT_HISTORY,
836 pmbus::Type::HwmonDeviceDebug,
837 history::RecordManager::RAW_RECORD_SIZE);
838
839 //Update D-Bus only if something changed (a new record ID, or cleared out)
840 auto changed = recordManager->add(data);
841 if (changed)
842 {
843 average->values(std::move(recordManager->getAverageRecords()));
844 maximum->values(std::move(recordManager->getMaximumRecords()));
845 }
846}
847
Brandon Wyman24e422f2017-07-25 19:40:14 -0500848}
849}
850}