blob: ff98457956369df4fd695050630ae0347f2df842 [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 */
William A. Kennington III1a0c9172018-10-18 17:57:49 -070016#include <functional>
Brandon Wyman442035f2017-08-08 15:58:45 -050017#include <phosphor-logging/log.hpp>
18#include <phosphor-logging/elog.hpp>
Brandon Wymane0eb45c2017-10-06 12:58:42 -050019#include <org/open_power/Witherspoon/Fault/error.hpp>
Matt Spinlerceacf942017-10-05 13:55:02 -050020#include <xyz/openbmc_project/Common/Device/error.hpp>
Matt Spinler018a7bc2018-01-04 15:36:41 -060021#include <xyz/openbmc_project/Software/Version/server.hpp>
Brandon Wyman442035f2017-08-08 15:58:45 -050022#include "elog-errors.hpp"
Matt Spinlerd734e652018-01-18 14:31:15 -060023#include "gpio.hpp"
Brandon Wyman10295542017-08-09 18:20:44 -050024#include "names_values.hpp"
Brandon Wyman24e422f2017-07-25 19:40:14 -050025#include "power_supply.hpp"
Brandon Wyman442035f2017-08-08 15:58:45 -050026#include "pmbus.hpp"
27#include "utility.hpp"
28
Brandon Wyman1db9a9e2017-07-26 18:50:22 -050029namespace witherspoon
Brandon Wyman24e422f2017-07-25 19:40:14 -050030{
31namespace power
32{
33namespace psu
34{
35
Matt Spinler589e8722018-01-04 15:24:49 -060036using namespace phosphor::logging;
37using namespace sdbusplus::org::open_power::Witherspoon::Fault::Error;
38using namespace sdbusplus::xyz::openbmc_project::Common::Device::Error;
Matt Spinler018a7bc2018-01-04 15:36:41 -060039namespace version = sdbusplus::xyz::openbmc_project::Software::server;
Matt Spinler589e8722018-01-04 15:24:49 -060040
41constexpr auto ASSOCIATION_IFACE = "org.openbmc.Association";
42constexpr auto LOGGING_IFACE = "xyz.openbmc_project.Logging.Entry";
43constexpr auto INVENTORY_IFACE = "xyz.openbmc_project.Inventory.Item";
44constexpr auto POWER_IFACE = "org.openbmc.control.Power";
Matt Spinler018a7bc2018-01-04 15:36:41 -060045constexpr auto INVENTORY_MGR_IFACE = "xyz.openbmc_project.Inventory.Manager";
46constexpr auto ASSET_IFACE = "xyz.openbmc_project.Inventory.Decorator.Asset";
47constexpr auto VERSION_IFACE = "xyz.openbmc_project.Software.Version";
Matt Spinler589e8722018-01-04 15:24:49 -060048
49constexpr auto ENDPOINTS_PROP = "endpoints";
50constexpr auto MESSAGE_PROP = "Message";
51constexpr auto RESOLVED_PROP = "Resolved";
Brandon Wyman10295542017-08-09 18:20:44 -050052constexpr auto PRESENT_PROP = "Present";
Matt Spinler018a7bc2018-01-04 15:36:41 -060053constexpr auto SN_PROP = "SerialNumber";
54constexpr auto PN_PROP = "PartNumber";
55constexpr auto MODEL_PROP = "Model";
56constexpr auto VERSION_PROP = "Version";
57constexpr auto VERSION_PURPOSE_PROP = "Purpose";
Matt Spinler589e8722018-01-04 15:24:49 -060058
59constexpr auto INVENTORY_OBJ_PATH = "/xyz/openbmc_project/inventory";
Brandon Wyman431fbe42017-08-18 16:22:09 -050060constexpr auto POWER_OBJ_PATH = "/org/openbmc/control/power0";
Brandon Wyman10295542017-08-09 18:20:44 -050061
Matt Spinler018a7bc2018-01-04 15:36:41 -060062constexpr auto SERIAL_NUMBER = "serial_number";
63constexpr auto PART_NUMBER = "part_number";
64constexpr auto FW_VERSION = "fw_version";
65constexpr auto CCIN = "ccin";
Matt Spinlereb169fd2018-01-18 14:19:08 -060066constexpr auto INPUT_HISTORY = "input_history";
Matt Spinler018a7bc2018-01-04 15:36:41 -060067
Brandon Wyman10295542017-08-09 18:20:44 -050068PowerSupply::PowerSupply(const std::string& name, size_t inst,
Brandon Wyman431fbe42017-08-18 16:22:09 -050069 const std::string& objpath,
70 const std::string& invpath,
71 sdbusplus::bus::bus& bus,
William A. Kennington IIIe5a8b472018-10-18 00:40:04 -070072 const sdeventplus::Event& e,
Brandon Wyman590fc282017-11-01 18:22:25 -050073 std::chrono::seconds& t,
74 std::chrono::seconds& p)
Brandon Wyman431fbe42017-08-18 16:22:09 -050075 : Device(name, inst), monitorPath(objpath), pmbusIntf(objpath),
William A. Kennington IIIe5a8b472018-10-18 00:40:04 -070076 inventoryPath(INVENTORY_OBJ_PATH + invpath), bus(bus),
Brandon Wyman50bb85d2017-11-01 18:36:00 -050077 presentInterval(p),
William A. Kennington III1a0c9172018-10-18 17:57:49 -070078 presentTimer(e, std::bind([this]()
Brandon Wyman590fc282017-11-01 18:22:25 -050079 {
Brandon Wyman2877add2017-11-10 17:44:19 -060080 // The hwmon path may have changed.
81 pmbusIntf.findHwmonDir();
Brandon Wyman590fc282017-11-01 18:22:25 -050082 this->present = true;
Matt Spinler234ce0d2018-01-04 15:06:57 -060083
Matt Spinlerd734e652018-01-18 14:31:15 -060084 // Sync the INPUT_HISTORY data for all PSs
85 syncHistory();
86
Matt Spinler234ce0d2018-01-04 15:06:57 -060087 // Update the inventory for the new device
88 updateInventory();
William A. Kennington III1a0c9172018-10-18 17:57:49 -070089 })),
Brandon Wyman590fc282017-11-01 18:22:25 -050090 powerOnInterval(t),
William A. Kennington III1a0c9172018-10-18 17:57:49 -070091 powerOnTimer(e, std::bind([this]()
Brandon Wyman431fbe42017-08-18 16:22:09 -050092 {
93 this->powerOn = true;
William A. Kennington III1a0c9172018-10-18 17:57:49 -070094 }))
Brandon Wyman10295542017-08-09 18:20:44 -050095{
Brandon Wyman10295542017-08-09 18:20:44 -050096 using namespace sdbusplus::bus;
Brandon Wyman10295542017-08-09 18:20:44 -050097 presentMatch = std::make_unique<match_t>(bus,
98 match::rules::propertiesChanged(
Brandon Wyman50bb85d2017-11-01 18:36:00 -050099 inventoryPath,
Matt Spinler589e8722018-01-04 15:24:49 -0600100 INVENTORY_IFACE),
Brandon Wyman10295542017-08-09 18:20:44 -0500101 [this](auto& msg)
Brandon Wyman431fbe42017-08-18 16:22:09 -0500102 {
103 this->inventoryChanged(msg);
104 });
105 // Get initial presence state.
Brandon Wyman253dc9b2017-08-12 13:45:52 -0500106 updatePresence();
Brandon Wyman431fbe42017-08-18 16:22:09 -0500107
Matt Spinler234ce0d2018-01-04 15:06:57 -0600108 // Write the SN, PN, etc to the inventory
109 updateInventory();
110
Brandon Wyman431fbe42017-08-18 16:22:09 -0500111 // Subscribe to power state changes
112 powerOnMatch = std::make_unique<match_t>(bus,
113 match::rules::propertiesChanged(
114 POWER_OBJ_PATH,
Matt Spinler589e8722018-01-04 15:24:49 -0600115 POWER_IFACE),
Brandon Wyman431fbe42017-08-18 16:22:09 -0500116 [this](auto& msg)
117 {
118 this->powerStateChanged(msg);
119 });
120 // Get initial power state.
121 updatePowerState();
Brandon Wyman10295542017-08-09 18:20:44 -0500122}
Brandon Wyman442035f2017-08-08 15:58:45 -0500123
Brandon Wymana1e96342017-09-25 16:47:44 -0500124void PowerSupply::captureCmd(util::NamesValues& nv, const std::string& cmd,
125 witherspoon::pmbus::Type type)
126{
127 if (pmbusIntf.exists(cmd, type))
128 {
129 try
130 {
131 auto val = pmbusIntf.read(cmd, type);
132 nv.add(cmd, val);
133 }
134 catch (std::exception& e)
135 {
Joseph Reynolds5b485962018-05-17 16:05:06 -0500136 log<level::INFO>("Unable to capture metadata",
137 entry("CMD=%s", cmd.c_str()));
Brandon Wymana1e96342017-09-25 16:47:44 -0500138 }
139 }
140}
Brandon Wyman431fbe42017-08-18 16:22:09 -0500141
Brandon Wyman1db9a9e2017-07-26 18:50:22 -0500142void PowerSupply::analyze()
143{
Brandon Wyman442035f2017-08-08 15:58:45 -0500144 using namespace witherspoon::pmbus;
145
146 try
147 {
Brandon Wyman10295542017-08-09 18:20:44 -0500148 if (present)
Brandon Wyman442035f2017-08-08 15:58:45 -0500149 {
Brandon Wyman764c7972017-08-22 17:05:36 -0500150 std::uint16_t statusWord = 0;
Brandon Wyman764c7972017-08-22 17:05:36 -0500151
152 // Read the 2 byte STATUS_WORD value to check for faults.
153 statusWord = pmbusIntf.read(STATUS_WORD, Type::Debug);
Brandon Wymane4af9802017-11-13 15:58:33 -0600154 readFail = 0;
Brandon Wyman764c7972017-08-22 17:05:36 -0500155
Brandon Wyman603cc002017-08-28 18:17:58 -0500156 checkInputFault(statusWord);
Brandon Wyman764c7972017-08-22 17:05:36 -0500157
Brandon Wyman654eb6e2018-02-14 14:55:47 -0600158 if (powerOn && (inputFault == 0) && !faultFound)
Brandon Wyman764c7972017-08-22 17:05:36 -0500159 {
Brandon Wyman12661f12017-08-31 15:28:21 -0500160 checkFanFault(statusWord);
Brandon Wyman875b3632017-09-13 18:46:03 -0500161 checkTemperatureFault(statusWord);
Brandon Wymancfa032b2017-09-25 17:37:50 -0500162 checkOutputOvervoltageFault(statusWord);
163 checkCurrentOutOverCurrentFault(statusWord);
164 checkPGOrUnitOffFault(statusWord);
Brandon Wyman442035f2017-08-08 15:58:45 -0500165 }
Matt Spinlereb169fd2018-01-18 14:19:08 -0600166
167 updateHistory();
Brandon Wyman442035f2017-08-08 15:58:45 -0500168 }
169 }
170 catch (ReadFailure& e)
171 {
Brandon Wymane4af9802017-11-13 15:58:33 -0600172 if (readFail < FAULT_COUNT)
173 {
174 readFail++;
175 }
176
177 if (!readFailLogged && readFail >= FAULT_COUNT)
Brandon Wyman442035f2017-08-08 15:58:45 -0500178 {
179 commit<ReadFailure>();
180 readFailLogged = true;
Brandon Wyman442035f2017-08-08 15:58:45 -0500181 }
182 }
183
Brandon Wyman1db9a9e2017-07-26 18:50:22 -0500184 return;
185}
186
Brandon Wyman10295542017-08-09 18:20:44 -0500187void PowerSupply::inventoryChanged(sdbusplus::message::message& msg)
188{
189 std::string msgSensor;
190 std::map<std::string, sdbusplus::message::variant<uint32_t, bool>> msgData;
191 msg.read(msgSensor, msgData);
192
193 // Check if it was the Present property that changed.
194 auto valPropMap = msgData.find(PRESENT_PROP);
195 if (valPropMap != msgData.end())
196 {
Brandon Wyman2ef48cf2017-11-21 15:43:54 -0600197 if (sdbusplus::message::variant_ns::get<bool>(valPropMap->second))
Brandon Wyman10295542017-08-09 18:20:44 -0500198 {
Brandon Wyman6ccce0b2017-10-26 15:13:10 -0500199 clearFaults();
William A. Kennington III1a0c9172018-10-18 17:57:49 -0700200 presentTimer.restartOnce(presentInterval);
Brandon Wyman590fc282017-11-01 18:22:25 -0500201 }
202 else
203 {
Brandon Wyman2ef48cf2017-11-21 15:43:54 -0600204 present = false;
William A. Kennington III1a0c9172018-10-18 17:57:49 -0700205 presentTimer.setEnabled(false);
Matt Spinler234ce0d2018-01-04 15:06:57 -0600206
207 //Clear out the now outdated inventory properties
208 updateInventory();
Brandon Wyman10295542017-08-09 18:20:44 -0500209 }
210 }
211
212 return;
213}
214
215void PowerSupply::updatePresence()
216{
217 // Use getProperty utility function to get presence status.
Brandon Wyman10295542017-08-09 18:20:44 -0500218 std::string service = "xyz.openbmc_project.Inventory.Manager";
Matt Spinler589e8722018-01-04 15:24:49 -0600219 util::getProperty(INVENTORY_IFACE, PRESENT_PROP, inventoryPath,
Brandon Wyman50bb85d2017-11-01 18:36:00 -0500220 service, bus, this->present);
Brandon Wyman10295542017-08-09 18:20:44 -0500221}
222
Brandon Wyman431fbe42017-08-18 16:22:09 -0500223void PowerSupply::powerStateChanged(sdbusplus::message::message& msg)
224{
225 int32_t state = 0;
226 std::string msgSensor;
227 std::map<std::string, sdbusplus::message::variant<int32_t, int32_t>>
228 msgData;
229 msg.read(msgSensor, msgData);
230
231 // Check if it was the Present property that changed.
232 auto valPropMap = msgData.find("state");
233 if (valPropMap != msgData.end())
234 {
235 state = sdbusplus::message::variant_ns::get<int32_t>(valPropMap->second);
236
237 // Power is on when state=1. Set the fault logged variables to false
238 // and start the power on timer when the state changes to 1.
239 if (state)
240 {
Brandon Wyman6ccce0b2017-10-26 15:13:10 -0500241 clearFaults();
William A. Kennington III1a0c9172018-10-18 17:57:49 -0700242 powerOnTimer.restartOnce(powerOnInterval);
Brandon Wyman431fbe42017-08-18 16:22:09 -0500243 }
244 else
245 {
William A. Kennington III1a0c9172018-10-18 17:57:49 -0700246 powerOnTimer.setEnabled(false);
Brandon Wyman431fbe42017-08-18 16:22:09 -0500247 powerOn = false;
248 }
249 }
250
251}
252
253void PowerSupply::updatePowerState()
254{
255 // When state = 1, system is powered on
256 int32_t state = 0;
257
258 try
259 {
260 auto service = util::getService(POWER_OBJ_PATH,
Matt Spinler589e8722018-01-04 15:24:49 -0600261 POWER_IFACE,
Brandon Wyman431fbe42017-08-18 16:22:09 -0500262 bus);
263
264 // Use getProperty utility function to get power state.
Matt Spinler589e8722018-01-04 15:24:49 -0600265 util::getProperty<int32_t>(POWER_IFACE,
Brandon Wyman431fbe42017-08-18 16:22:09 -0500266 "state",
267 POWER_OBJ_PATH,
268 service,
269 bus,
270 state);
271
272 if (state)
273 {
274 powerOn = true;
275 }
276 else
277 {
278 powerOn = false;
279 }
280 }
281 catch (std::exception& e)
282 {
283 log<level::INFO>("Failed to get power state. Assuming it is off.");
284 powerOn = false;
285 }
286
287}
288
Brandon Wyman603cc002017-08-28 18:17:58 -0500289void PowerSupply::checkInputFault(const uint16_t statusWord)
290{
291 using namespace witherspoon::pmbus;
292
Brandon Wymana3c675c2017-11-14 14:54:54 -0600293 if ((inputFault < FAULT_COUNT) &&
294 ((statusWord & status_word::INPUT_FAULT_WARN) ||
295 (statusWord & status_word::VIN_UV_FAULT)))
Brandon Wyman603cc002017-08-28 18:17:58 -0500296 {
Brandon Wymanad708242018-01-19 17:28:35 -0600297 if (inputFault == 0)
298 {
299 log<level::INFO>("INPUT or VIN_UV fault",
300 entry("STATUS_WORD=0x%04X", statusWord));
301 }
302
Brandon Wymana3c675c2017-11-14 14:54:54 -0600303 inputFault++;
Brandon Wyman603cc002017-08-28 18:17:58 -0500304 }
305 else
306 {
Brandon Wymana3c675c2017-11-14 14:54:54 -0600307 if ((inputFault > 0) &&
Brandon Wymand20686a2017-11-01 17:45:23 -0500308 !(statusWord & status_word::INPUT_FAULT_WARN) &&
309 !(statusWord & status_word::VIN_UV_FAULT))
Brandon Wyman603cc002017-08-28 18:17:58 -0500310 {
Brandon Wymana3c675c2017-11-14 14:54:54 -0600311 inputFault = 0;
Brandon Wyman3343e822017-11-03 16:54:11 -0500312 faultFound = false;
Brandon Wyman7502f6c2018-02-12 20:20:39 -0600313 // When an input fault occurs, the power supply cannot be on.
314 // However, the check for the case where the power supply should be
315 // on will stop when there is a fault found.
316 // Clear the powerOnFault when the inputFault is cleared to reset
317 // the powerOnFault de-glitching.
318 powerOnFault = 0;
Brandon Wyman69591bd2017-11-01 18:07:23 -0500319
Brandon Wyman603cc002017-08-28 18:17:58 -0500320 log<level::INFO>("INPUT_FAULT_WARN cleared",
Brandon Wymana3c675c2017-11-14 14:54:54 -0600321 entry("POWERSUPPLY=%s", inventoryPath.c_str()));
Brandon Wyman69591bd2017-11-01 18:07:23 -0500322
Brandon Wyman08b05712017-11-30 17:53:56 -0600323 resolveError(inventoryPath,
324 std::string(PowerSupplyInputFault::errName));
325
Brandon Wyman69591bd2017-11-01 18:07:23 -0500326 if (powerOn)
327 {
328 // The power supply will not be immediately powered on after
329 // the input power is restored.
330 powerOn = false;
331 // Start up the timer that will set the state to indicate we
332 // are ready for the powered on fault checks.
William A. Kennington III1a0c9172018-10-18 17:57:49 -0700333 powerOnTimer.restartOnce(powerOnInterval);
Brandon Wyman69591bd2017-11-01 18:07:23 -0500334 }
Brandon Wyman603cc002017-08-28 18:17:58 -0500335 }
336 }
Brandon Wymana3c675c2017-11-14 14:54:54 -0600337
338 if (!faultFound && (inputFault >= FAULT_COUNT))
339 {
Brandon Wymanad708242018-01-19 17:28:35 -0600340 // If the power is on, report the fault in an error log entry.
341 if (powerOn)
342 {
343 util::NamesValues nv;
344 nv.add("STATUS_WORD", statusWord);
345 captureCmd(nv, STATUS_INPUT, Type::Debug);
Brandon Wymana3c675c2017-11-14 14:54:54 -0600346
Brandon Wymanad708242018-01-19 17:28:35 -0600347 using metadata = org::open_power::Witherspoon::Fault::
348 PowerSupplyInputFault;
Brandon Wymana3c675c2017-11-14 14:54:54 -0600349
Brandon Wymanad708242018-01-19 17:28:35 -0600350 report<PowerSupplyInputFault>(
351 metadata::RAW_STATUS(nv.get().c_str()),
352 metadata::CALLOUT_INVENTORY_PATH(inventoryPath.c_str()));
353
354 faultFound = true;
355 }
Brandon Wymana3c675c2017-11-14 14:54:54 -0600356 }
357
Brandon Wyman603cc002017-08-28 18:17:58 -0500358}
359
360void PowerSupply::checkPGOrUnitOffFault(const uint16_t statusWord)
361{
362 using namespace witherspoon::pmbus;
363
Brandon Wyman593d24f2017-10-13 18:15:23 -0500364 if (powerOnFault < FAULT_COUNT)
Brandon Wyman603cc002017-08-28 18:17:58 -0500365 {
Brandon Wyman593d24f2017-10-13 18:15:23 -0500366 // Check PG# and UNIT_IS_OFF
367 if ((statusWord & status_word::POWER_GOOD_NEGATED) ||
368 (statusWord & status_word::UNIT_IS_OFF))
369 {
370 log<level::INFO>("PGOOD or UNIT_IS_OFF bit bad",
371 entry("STATUS_WORD=0x%04X", statusWord));
372 powerOnFault++;
373 }
374 else
375 {
376 if (powerOnFault > 0)
377 {
378 log<level::INFO>("PGOOD and UNIT_IS_OFF bits good");
379 powerOnFault = 0;
380 }
381 }
Brandon Wyman603cc002017-08-28 18:17:58 -0500382
Brandon Wymane2fc7aa2017-11-13 17:37:10 -0600383 if (!faultFound && (powerOnFault >= FAULT_COUNT))
Brandon Wyman593d24f2017-10-13 18:15:23 -0500384 {
Brandon Wyman3343e822017-11-03 16:54:11 -0500385 faultFound = true;
386
Brandon Wyman593d24f2017-10-13 18:15:23 -0500387 util::NamesValues nv;
388 nv.add("STATUS_WORD", statusWord);
389 captureCmd(nv, STATUS_INPUT, Type::Debug);
390 auto status0Vout = pmbusIntf.insertPageNum(STATUS_VOUT, 0);
391 captureCmd(nv, status0Vout, Type::Debug);
392 captureCmd(nv, STATUS_IOUT, Type::Debug);
393 captureCmd(nv, STATUS_MFR, Type::Debug);
Brandon Wyman603cc002017-08-28 18:17:58 -0500394
Brandon Wyman593d24f2017-10-13 18:15:23 -0500395 using metadata = org::open_power::Witherspoon::Fault::
396 PowerSupplyShouldBeOn;
Brandon Wyman603cc002017-08-28 18:17:58 -0500397
Brandon Wyman593d24f2017-10-13 18:15:23 -0500398 // A power supply is OFF (or pgood low) but should be on.
399 report<PowerSupplyShouldBeOn>(
400 metadata::RAW_STATUS(nv.get().c_str()),
401 metadata::CALLOUT_INVENTORY_PATH(
402 inventoryPath.c_str()));
403 }
Brandon Wyman603cc002017-08-28 18:17:58 -0500404 }
405
406}
407
408void PowerSupply::checkCurrentOutOverCurrentFault(const uint16_t statusWord)
409{
410 using namespace witherspoon::pmbus;
411
Brandon Wymandd61be42017-11-07 18:38:54 -0600412 if (outputOCFault < FAULT_COUNT)
Brandon Wyman603cc002017-08-28 18:17:58 -0500413 {
Brandon Wymandd61be42017-11-07 18:38:54 -0600414 // Check for an output overcurrent fault.
415 if ((statusWord & status_word::IOUT_OC_FAULT))
416 {
417 outputOCFault++;
418 }
419 else
420 {
421 if (outputOCFault > 0)
422 {
423 outputOCFault = 0;
424 }
425 }
Brandon Wyman603cc002017-08-28 18:17:58 -0500426
Brandon Wymane2fc7aa2017-11-13 17:37:10 -0600427 if (!faultFound && (outputOCFault >= FAULT_COUNT))
Brandon Wymandd61be42017-11-07 18:38:54 -0600428 {
429 util::NamesValues nv;
430 nv.add("STATUS_WORD", statusWord);
431 captureCmd(nv, STATUS_INPUT, Type::Debug);
432 auto status0Vout = pmbusIntf.insertPageNum(STATUS_VOUT, 0);
433 captureCmd(nv, status0Vout, Type::Debug);
434 captureCmd(nv, STATUS_IOUT, Type::Debug);
435 captureCmd(nv, STATUS_MFR, Type::Debug);
Brandon Wyman603cc002017-08-28 18:17:58 -0500436
Brandon Wymandd61be42017-11-07 18:38:54 -0600437 using metadata = org::open_power::Witherspoon::Fault::
438 PowerSupplyOutputOvercurrent;
Brandon Wyman603cc002017-08-28 18:17:58 -0500439
Brandon Wymandd61be42017-11-07 18:38:54 -0600440 report<PowerSupplyOutputOvercurrent>(
441 metadata::RAW_STATUS(nv.get().c_str()),
442 metadata::CALLOUT_INVENTORY_PATH(inventoryPath.c_str()));
443
444 faultFound = true;
445 }
Brandon Wyman603cc002017-08-28 18:17:58 -0500446 }
447}
448
Brandon Wymanab05c072017-08-30 18:26:41 -0500449void PowerSupply::checkOutputOvervoltageFault(const uint16_t statusWord)
450{
451 using namespace witherspoon::pmbus;
452
Brandon Wyman2ab319b2017-11-08 17:34:59 -0600453 if (outputOVFault < FAULT_COUNT)
Brandon Wymanab05c072017-08-30 18:26:41 -0500454 {
Brandon Wyman2ab319b2017-11-08 17:34:59 -0600455 // Check for an output overvoltage fault.
456 if (statusWord & status_word::VOUT_OV_FAULT)
457 {
458 outputOVFault++;
459 }
460 else
461 {
462 if (outputOVFault > 0)
463 {
464 outputOVFault = 0;
465 }
466 }
Brandon Wymanab05c072017-08-30 18:26:41 -0500467
Brandon Wymane2fc7aa2017-11-13 17:37:10 -0600468 if (!faultFound && (outputOVFault >= FAULT_COUNT))
Brandon Wyman2ab319b2017-11-08 17:34:59 -0600469 {
470 util::NamesValues nv;
471 nv.add("STATUS_WORD", statusWord);
472 captureCmd(nv, STATUS_INPUT, Type::Debug);
473 auto status0Vout = pmbusIntf.insertPageNum(STATUS_VOUT, 0);
474 captureCmd(nv, status0Vout, Type::Debug);
475 captureCmd(nv, STATUS_IOUT, Type::Debug);
476 captureCmd(nv, STATUS_MFR, Type::Debug);
Brandon Wymanab05c072017-08-30 18:26:41 -0500477
Brandon Wyman2ab319b2017-11-08 17:34:59 -0600478 using metadata = org::open_power::Witherspoon::Fault::
479 PowerSupplyOutputOvervoltage;
Brandon Wymanab05c072017-08-30 18:26:41 -0500480
Brandon Wyman2ab319b2017-11-08 17:34:59 -0600481 report<PowerSupplyOutputOvervoltage>(
482 metadata::RAW_STATUS(nv.get().c_str()),
483 metadata::CALLOUT_INVENTORY_PATH(inventoryPath.c_str()));
484
485 faultFound = true;
486 }
Brandon Wymanab05c072017-08-30 18:26:41 -0500487 }
488}
489
Brandon Wyman12661f12017-08-31 15:28:21 -0500490void PowerSupply::checkFanFault(const uint16_t statusWord)
491{
492 using namespace witherspoon::pmbus;
493
Brandon Wymanba255532017-11-08 17:44:10 -0600494 if (fanFault < FAULT_COUNT)
Brandon Wyman12661f12017-08-31 15:28:21 -0500495 {
Brandon Wymanba255532017-11-08 17:44:10 -0600496 // Check for a fan fault or warning condition
497 if (statusWord & status_word::FAN_FAULT)
498 {
499 fanFault++;
500 }
501 else
502 {
503 if (fanFault > 0)
504 {
505 fanFault = 0;
506 }
507 }
Brandon Wyman12661f12017-08-31 15:28:21 -0500508
Brandon Wymane2fc7aa2017-11-13 17:37:10 -0600509 if (!faultFound && (fanFault >= FAULT_COUNT))
Brandon Wymanba255532017-11-08 17:44:10 -0600510 {
511 util::NamesValues nv;
512 nv.add("STATUS_WORD", statusWord);
513 captureCmd(nv, STATUS_MFR, Type::Debug);
514 captureCmd(nv, STATUS_TEMPERATURE, Type::Debug);
515 captureCmd(nv, STATUS_FANS_1_2, Type::Debug);
Brandon Wyman12661f12017-08-31 15:28:21 -0500516
Brandon Wymanba255532017-11-08 17:44:10 -0600517 using metadata = org::open_power::Witherspoon::Fault::
518 PowerSupplyFanFault;
Brandon Wyman12661f12017-08-31 15:28:21 -0500519
Brandon Wymanba255532017-11-08 17:44:10 -0600520 report<PowerSupplyFanFault>(
521 metadata::RAW_STATUS(nv.get().c_str()),
522 metadata::CALLOUT_INVENTORY_PATH(inventoryPath.c_str()));
523
524 faultFound = true;
525 }
Brandon Wyman12661f12017-08-31 15:28:21 -0500526 }
527}
528
Brandon Wyman875b3632017-09-13 18:46:03 -0500529void PowerSupply::checkTemperatureFault(const uint16_t statusWord)
530{
531 using namespace witherspoon::pmbus;
532
533 // Due to how the PMBus core device driver sends a clear faults command
534 // the bit in STATUS_WORD will likely be cleared when we attempt to examine
535 // it for a Thermal Fault or Warning. So, check the STATUS_WORD and the
536 // STATUS_TEMPERATURE bits. If either indicates a fault, proceed with
537 // logging the over-temperature condition.
538 std::uint8_t statusTemperature = 0;
539 statusTemperature = pmbusIntf.read(STATUS_TEMPERATURE, Type::Debug);
Brandon Wyman50044ea2017-11-08 17:58:56 -0600540 if (temperatureFault < FAULT_COUNT)
Brandon Wyman875b3632017-09-13 18:46:03 -0500541 {
Brandon Wyman50044ea2017-11-08 17:58:56 -0600542 if ((statusWord & status_word::TEMPERATURE_FAULT_WARN) ||
543 (statusTemperature & status_temperature::OT_FAULT))
544 {
545 temperatureFault++;
546 }
547 else
548 {
549 if (temperatureFault > 0)
550 {
551 temperatureFault = 0;
552 }
553 }
Brandon Wyman875b3632017-09-13 18:46:03 -0500554
Brandon Wymane2fc7aa2017-11-13 17:37:10 -0600555 if (!faultFound && (temperatureFault >= FAULT_COUNT))
Brandon Wyman50044ea2017-11-08 17:58:56 -0600556 {
557 // The power supply has had an over-temperature condition.
558 // This may not result in a shutdown if experienced for a short
559 // duration.
560 // This should not occur under normal conditions.
561 // The power supply may be faulty, or the paired supply may be
562 // putting out less current.
563 // Capture command responses with potentially relevant information,
564 // and call out the power supply reporting the condition.
565 util::NamesValues nv;
566 nv.add("STATUS_WORD", statusWord);
567 captureCmd(nv, STATUS_MFR, Type::Debug);
568 captureCmd(nv, STATUS_IOUT, Type::Debug);
569 nv.add("STATUS_TEMPERATURE", statusTemperature);
570 captureCmd(nv, STATUS_FANS_1_2, Type::Debug);
Brandon Wyman875b3632017-09-13 18:46:03 -0500571
Brandon Wyman50044ea2017-11-08 17:58:56 -0600572 using metadata = org::open_power::Witherspoon::Fault::
573 PowerSupplyTemperatureFault;
Brandon Wyman875b3632017-09-13 18:46:03 -0500574
Brandon Wyman50044ea2017-11-08 17:58:56 -0600575 report<PowerSupplyTemperatureFault>(
576 metadata::RAW_STATUS(nv.get().c_str()),
577 metadata::CALLOUT_INVENTORY_PATH(inventoryPath.c_str()));
578
579 faultFound = true;
580 }
Brandon Wyman875b3632017-09-13 18:46:03 -0500581 }
582}
583
Brandon Wyman1db9a9e2017-07-26 18:50:22 -0500584void PowerSupply::clearFaults()
585{
Brandon Wymane4af9802017-11-13 15:58:33 -0600586 readFail = 0;
Brandon Wyman6ccce0b2017-10-26 15:13:10 -0500587 readFailLogged = false;
Brandon Wymana3c675c2017-11-14 14:54:54 -0600588 inputFault = 0;
Brandon Wyman6ccce0b2017-10-26 15:13:10 -0500589 powerOnFault = 0;
Brandon Wymandd61be42017-11-07 18:38:54 -0600590 outputOCFault = 0;
Brandon Wyman2ab319b2017-11-08 17:34:59 -0600591 outputOVFault = 0;
Brandon Wymanba255532017-11-08 17:44:10 -0600592 fanFault = 0;
Brandon Wyman50044ea2017-11-08 17:58:56 -0600593 temperatureFault = 0;
Brandon Wyman3343e822017-11-03 16:54:11 -0500594 faultFound = false;
Brandon Wyman6ccce0b2017-10-26 15:13:10 -0500595
Brandon Wyman1db9a9e2017-07-26 18:50:22 -0500596 return;
597}
598
Brandon Wyman43ce2082017-11-30 17:24:01 -0600599void PowerSupply::resolveError(const std::string& callout,
600 const std::string& message)
601{
Brandon Wyman01741f12017-12-01 17:22:08 -0600602 using EndpointList = std::vector<std::string>;
603
604 try
605 {
606 auto path = callout + "/fault";
607 // Get the service name from the mapper for the fault callout
608 auto service = util::getService(path,
609 ASSOCIATION_IFACE,
610 bus);
611
612 // Use getProperty utility function to get log entries (endpoints)
613 EndpointList logEntries;
Matt Spinler589e8722018-01-04 15:24:49 -0600614 util::getProperty(ASSOCIATION_IFACE, ENDPOINTS_PROP, path, service,
Brandon Wyman01741f12017-12-01 17:22:08 -0600615 bus, logEntries);
616
617 // It is possible that all such entries for this callout have since
618 // been deleted.
619 if (logEntries.empty())
620 {
621 return;
622 }
623
624 auto logEntryService = util::getService(logEntries[0], LOGGING_IFACE,
625 bus);
626 if (logEntryService.empty())
627 {
628 return;
629 }
630
631 // go through each log entry that matches this callout path
632 std::string logMessage;
633 for (const auto& logEntry : logEntries)
634 {
635 // Check to see if this logEntry has a message that matches.
Matt Spinler589e8722018-01-04 15:24:49 -0600636 util::getProperty(LOGGING_IFACE, MESSAGE_PROP, logEntry,
Brandon Wyman01741f12017-12-01 17:22:08 -0600637 logEntryService, bus, logMessage);
638
639 if (message == logMessage)
640 {
641 // Log entry matches call out and message, set Resolved to true
642 bool resolved = true;
Matt Spinler589e8722018-01-04 15:24:49 -0600643 util::setProperty(LOGGING_IFACE, RESOLVED_PROP, logEntry,
Brandon Wyman01741f12017-12-01 17:22:08 -0600644 logEntryService, bus, resolved);
645 }
646
647 }
648
649 }
650 catch (std::exception& e)
651 {
652 log<level::INFO>("Failed to resolve error",
653 entry("CALLOUT=%s", callout.c_str()),
Matt Spinler0d09f292018-01-22 14:51:26 -0600654 entry("ERROR=%s", message.c_str()));
Brandon Wyman01741f12017-12-01 17:22:08 -0600655 }
656
Brandon Wyman43ce2082017-11-30 17:24:01 -0600657}
658
Matt Spinler234ce0d2018-01-04 15:06:57 -0600659void PowerSupply::updateInventory()
660{
Matt Spinler018a7bc2018-01-04 15:36:41 -0600661 using namespace witherspoon::pmbus;
662 using namespace sdbusplus::message;
663
664 // If any of these accesses fail, the fields will just be
665 // blank in the inventory. Leave logging ReadFailure errors
666 // to analyze() as it runs continuously and will most
667 // likely hit and threshold them first anyway. The
668 // readString() function will do the tracing of the failing
669 // path so this code doesn't need to.
670 std::string pn;
671 std::string sn;
672 std::string ccin;
673 std::string version;
674
675 if (present)
676 {
677 try
678 {
679 sn = pmbusIntf.readString(SERIAL_NUMBER, Type::HwmonDeviceDebug);
680 }
681 catch (ReadFailure& e) { }
682
683 try
684 {
685 pn = pmbusIntf.readString(PART_NUMBER, Type::HwmonDeviceDebug);
686 }
687 catch (ReadFailure& e) { }
688
689 try
690 {
691 ccin = pmbusIntf.readString(CCIN, Type::HwmonDeviceDebug);
692 }
693 catch (ReadFailure& e) { }
694
695 try
696 {
697 version = pmbusIntf.readString(FW_VERSION, Type::HwmonDeviceDebug);
698 }
699 catch (ReadFailure& e) { }
700 }
701
702 // Build the object map and send it to the inventory
703 using Properties = std::map<std::string, variant<std::string>>;
704 using Interfaces = std::map<std::string, Properties>;
705 using Object = std::map<object_path, Interfaces>;
706 Properties assetProps;
707 Properties versionProps;
708 Interfaces interfaces;
709 Object object;
710
711 assetProps.emplace(SN_PROP, sn);
712 assetProps.emplace(PN_PROP, pn);
713 assetProps.emplace(MODEL_PROP, ccin);
714 interfaces.emplace(ASSET_IFACE, std::move(assetProps));
715
716 versionProps.emplace(VERSION_PROP, version);
717 interfaces.emplace(VERSION_IFACE, std::move(versionProps));
718
719 //For Notify(), just send the relative path of the inventory
720 //object so remove the INVENTORY_OBJ_PATH prefix
721 auto path = inventoryPath.substr(strlen(INVENTORY_OBJ_PATH));
722
723 object.emplace(path, std::move(interfaces));
724
725 try
726 {
727 auto service = util::getService(
728 INVENTORY_OBJ_PATH,
729 INVENTORY_MGR_IFACE,
730 bus);
731
732 if (service.empty())
733 {
734 log<level::ERR>("Unable to get inventory manager service");
735 return;
736 }
737
738 auto method = bus.new_method_call(
739 service.c_str(),
740 INVENTORY_OBJ_PATH,
741 INVENTORY_MGR_IFACE,
742 "Notify");
743
744 method.append(std::move(object));
745
746 auto reply = bus.call(method);
747 if (reply.is_method_error())
748 {
749 log<level::ERR>(
750 "Unable to update power supply inventory properties",
751 entry("PATH=%s", path.c_str()));
752 }
753
754 // TODO: openbmc/openbmc#2756
755 // Calling Notify() with an enumerated property crashes inventory
756 // manager, so let it default to Unknown and now set it to the
757 // right value.
758 auto purpose = version::convertForMessage(
759 version::Version::VersionPurpose::Other);
760
761 util::setProperty(
762 VERSION_IFACE,
763 VERSION_PURPOSE_PROP,
764 inventoryPath,
765 service,
766 bus,
767 purpose);
768 }
769 catch (std::exception& e)
770 {
771 log<level::ERR>(
772 e.what(),
Joseph Reynolds5b485962018-05-17 16:05:06 -0500773 entry("PATH=%s", inventoryPath.c_str()));
Matt Spinler018a7bc2018-01-04 15:36:41 -0600774 }
Matt Spinler234ce0d2018-01-04 15:06:57 -0600775}
776
Matt Spinlerd734e652018-01-18 14:31:15 -0600777void PowerSupply::syncHistory()
778{
779 using namespace witherspoon::gpio;
780
781 if (syncGPIODevPath.empty())
782 {
783 //Sync not implemented
784 return;
785 }
786
787 GPIO gpio{syncGPIODevPath,
788 static_cast<gpioNum_t>(syncGPIONumber),
789 Direction::output};
790
791 try
792 {
793 gpio.set(Value::low);
794
795 std::this_thread::sleep_for(std::chrono::milliseconds{5});
796
797 gpio.set(Value::high);
798
799 recordManager->clear();
800 }
801 catch (std::exception& e)
802 {
803 //Do nothing. There would already be a journal entry.
804 }
805}
806
Matt Spinler82384142018-01-18 14:15:03 -0600807void PowerSupply::enableHistory(const std::string& objectPath,
808 size_t numRecords,
809 const std::string& syncGPIOPath,
810 size_t syncGPIONum)
811{
812 historyObjectPath = objectPath;
813 syncGPIODevPath = syncGPIOPath;
814 syncGPIONumber = syncGPIONum;
815
816 recordManager = std::make_unique<history::RecordManager>(numRecords);
817
818 auto avgPath = historyObjectPath + '/' + history::Average::name;
819 auto maxPath = historyObjectPath + '/' + history::Maximum::name;
820
821 average = std::make_unique<history::Average>(bus, avgPath);
822
823 maximum = std::make_unique<history::Maximum>(bus, maxPath);
824}
825
Matt Spinlereb169fd2018-01-18 14:19:08 -0600826void PowerSupply::updateHistory()
827{
828 if (!recordManager)
829 {
830 //Not enabled
831 return;
832 }
833
834 //Read just the most recent average/max record
835 auto data = pmbusIntf.readBinary(
836 INPUT_HISTORY,
837 pmbus::Type::HwmonDeviceDebug,
838 history::RecordManager::RAW_RECORD_SIZE);
839
840 //Update D-Bus only if something changed (a new record ID, or cleared out)
841 auto changed = recordManager->add(data);
842 if (changed)
843 {
844 average->values(std::move(recordManager->getAverageRecords()));
845 maximum->values(std::move(recordManager->getMaximumRecords()));
846 }
847}
848
Brandon Wyman24e422f2017-07-25 19:40:14 -0500849}
850}
851}