blob: 63697f4099d858e4e65fd3b3f6c8d0ac64dca909 [file] [log] [blame]
Brandon Wyman1d7a7df2020-03-26 10:14:05 -05001#include "config.h"
2
Brandon Wymanaed1f752019-11-25 18:10:52 -06003#include "power_supply.hpp"
4
5#include "types.hpp"
Brandon Wyman3f1242f2020-01-28 13:11:25 -06006#include "util.hpp"
Brandon Wymanaed1f752019-11-25 18:10:52 -06007
Brandon Wymandf13c3a2020-12-15 14:25:22 -06008#include <fmt/format.h>
9
Brandon Wyman3f1242f2020-01-28 13:11:25 -060010#include <xyz/openbmc_project/Common/Device/error.hpp>
11
Brandon Wyman1d7a7df2020-03-26 10:14:05 -050012#include <chrono> // sleep_for()
13#include <cstdint> // uint8_t...
14#include <thread> // sleep_for()
15
Brandon Wyman3f1242f2020-01-28 13:11:25 -060016namespace phosphor::power::psu
Brandon Wymanaed1f752019-11-25 18:10:52 -060017{
18
19using namespace phosphor::logging;
Brandon Wyman3f1242f2020-01-28 13:11:25 -060020using namespace sdbusplus::xyz::openbmc_project::Common::Device::Error;
Brandon Wymanaed1f752019-11-25 18:10:52 -060021
22void PowerSupply::updatePresence()
23{
24 try
25 {
Brandon Wyman3f1242f2020-01-28 13:11:25 -060026 present = getPresence(bus, inventoryPath);
Brandon Wymanaed1f752019-11-25 18:10:52 -060027 }
28 catch (const sdbusplus::exception::SdBusError& e)
29 {
30 // Relying on property change or interface added to retry.
31 // Log an informational trace to the journal.
Brandon Wymandf13c3a2020-12-15 14:25:22 -060032 log<level::INFO>(
33 fmt::format("D-Bus property {} access failure exception",
34 inventoryPath)
35 .c_str());
Brandon Wymanaed1f752019-11-25 18:10:52 -060036 }
37}
38
Brandon Wyman3f1242f2020-01-28 13:11:25 -060039void PowerSupply::analyze()
40{
41 using namespace phosphor::pmbus;
42
Brandon Wymanf65c4062020-08-19 13:15:53 -050043 if ((present) && (readFail < LOG_LIMIT))
Brandon Wyman3f1242f2020-01-28 13:11:25 -060044 {
45 try
46 {
Brandon Wymanfed0ba22020-09-26 20:02:51 -050047 statusWord = pmbusIntf->read(STATUS_WORD, Type::Debug);
Brandon Wymanf65c4062020-08-19 13:15:53 -050048 // Read worked, reset the fail count.
49 readFail = 0;
Brandon Wyman3f1242f2020-01-28 13:11:25 -060050
51 if (statusWord)
52 {
Jay Meyer10d94052020-11-30 14:41:21 -060053 statusMFR = pmbusIntf->read(STATUS_MFR, Type::Debug);
Brandon Wyman3f1242f2020-01-28 13:11:25 -060054 if (statusWord & status_word::INPUT_FAULT_WARN)
55 {
56 if (!inputFault)
57 {
Jay Meyer10d94052020-11-30 14:41:21 -060058 log<level::INFO>(fmt::format("INPUT fault: "
59 "status word = {:#04x}, "
60 "MFR fault = {:#02x}",
61 statusWord, statusMFR)
62 .c_str());
Brandon Wyman3f1242f2020-01-28 13:11:25 -060063 }
64
65 faultFound = true;
66 inputFault = true;
67 }
68
69 if (statusWord & status_word::MFR_SPECIFIC_FAULT)
70 {
71 if (!mfrFault)
72 {
Jay Meyer10d94052020-11-30 14:41:21 -060073 log<level::ERR>(fmt::format("MFR fault: "
74 "status word = {:#04x} "
75 "MFR fault = {:#02x}",
76 statusWord, statusMFR)
77 .c_str());
Brandon Wyman3f1242f2020-01-28 13:11:25 -060078 }
79 faultFound = true;
80 mfrFault = true;
81 }
82
83 if (statusWord & status_word::VIN_UV_FAULT)
84 {
85 if (!vinUVFault)
86 {
Jay Meyer10d94052020-11-30 14:41:21 -060087 log<level::INFO>(fmt::format("VIN_UV fault: "
88 "status word = {:#04x}, "
89 "MFR fault = {:#02x}",
90 statusWord, statusMFR)
91 .c_str());
Brandon Wyman3f1242f2020-01-28 13:11:25 -060092 }
93
94 faultFound = true;
95 vinUVFault = true;
96 }
97 }
98 else
99 {
100 faultFound = false;
101 inputFault = false;
102 mfrFault = false;
103 vinUVFault = false;
104 }
105 }
106 catch (ReadFailure& e)
107 {
Brandon Wymanf65c4062020-08-19 13:15:53 -0500108 readFail++;
Brandon Wyman3f1242f2020-01-28 13:11:25 -0600109 phosphor::logging::commit<ReadFailure>();
110 }
111 }
112}
113
Brandon Wyman59a35792020-06-04 12:37:40 -0500114void PowerSupply::onOffConfig(uint8_t data)
115{
116 using namespace phosphor::pmbus;
117
118 if (present)
119 {
120 log<level::INFO>("ON_OFF_CONFIG write", entry("DATA=0x%02X", data));
121 try
122 {
123 std::vector<uint8_t> configData{data};
124 pmbusIntf->writeBinary(ON_OFF_CONFIG, configData,
125 Type::HwmonDeviceDebug);
126 }
127 catch (...)
128 {
129 // The underlying code in writeBinary will log a message to the
130 // journal if the write fails. If the ON_OFF_CONFIG is not setup as
131 // desired, later fault detection and analysis code should catch any
132 // of the fall out. We should not need to terminate the application
133 // if this write fails.
134 }
135 }
136}
137
Brandon Wyman3c208462020-05-13 16:25:58 -0500138void PowerSupply::clearFaults()
139{
Brandon Wyman5474c912021-02-23 14:39:43 -0600140 faultLogged = false;
Brandon Wyman3c208462020-05-13 16:25:58 -0500141 // The PMBus device driver does not allow for writing CLEAR_FAULTS
142 // directly. However, the pmbus hwmon device driver code will send a
143 // CLEAR_FAULTS after reading from any of the hwmon "files" in sysfs, so
144 // reading in1_input should result in clearing the fault bits in
145 // STATUS_BYTE/STATUS_WORD.
146 // I do not care what the return value is.
Brandon Wyman11151532020-11-10 13:45:57 -0600147 if (present)
Brandon Wyman3c208462020-05-13 16:25:58 -0500148 {
Brandon Wyman9564e942020-11-10 14:01:42 -0600149 faultFound = false;
150 inputFault = false;
151 mfrFault = false;
Jay Meyer10d94052020-11-30 14:41:21 -0600152 statusMFR = 0;
Brandon Wyman9564e942020-11-10 14:01:42 -0600153 vinUVFault = false;
154 readFail = 0;
Brandon Wyman9564e942020-11-10 14:01:42 -0600155
Brandon Wyman11151532020-11-10 13:45:57 -0600156 try
157 {
158 static_cast<void>(
159 pmbusIntf->read("in1_input", phosphor::pmbus::Type::Hwmon));
160 }
161 catch (ReadFailure& e)
162 {
163 // Since I do not care what the return value is, I really do not
164 // care much if it gets a ReadFailure either. However, this should
165 // not prevent the application from continuing to run, so catching
166 // the read failure.
167 }
Brandon Wyman3c208462020-05-13 16:25:58 -0500168 }
169}
170
Brandon Wymanaed1f752019-11-25 18:10:52 -0600171void PowerSupply::inventoryChanged(sdbusplus::message::message& msg)
172{
173 std::string msgSensor;
Patrick Williamsabe49412020-05-13 17:59:47 -0500174 std::map<std::string, std::variant<uint32_t, bool>> msgData;
Brandon Wymanaed1f752019-11-25 18:10:52 -0600175 msg.read(msgSensor, msgData);
176
177 // Check if it was the Present property that changed.
178 auto valPropMap = msgData.find(PRESENT_PROP);
179 if (valPropMap != msgData.end())
180 {
181 if (std::get<bool>(valPropMap->second))
182 {
183 present = true;
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500184 // TODO: Immediately trying to read or write the "files" causes read
185 // or write failures.
186 using namespace std::chrono_literals;
187 std::this_thread::sleep_for(20ms);
Brandon Wyman9564e942020-11-10 14:01:42 -0600188 pmbusIntf->findHwmonDir();
Brandon Wyman59a35792020-06-04 12:37:40 -0500189 onOffConfig(phosphor::pmbus::ON_OFF_CONFIG_CONTROL_PIN_ONLY);
Brandon Wymanaed1f752019-11-25 18:10:52 -0600190 clearFaults();
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500191 updateInventory();
Brandon Wymanaed1f752019-11-25 18:10:52 -0600192 }
193 else
194 {
195 present = false;
196
197 // Clear out the now outdated inventory properties
198 updateInventory();
199 }
200 }
201}
202
Brandon Wyman9a507db2021-02-25 16:15:22 -0600203void PowerSupply::inventoryAdded(sdbusplus::message::message& msg)
204{
205 sdbusplus::message::object_path path;
206 msg.read(path);
207 // Make sure the signal is for the PSU inventory path
208 if (path == inventoryPath)
209 {
210 std::map<std::string, std::map<std::string, std::variant<bool>>>
211 interfaces;
212 // Get map of interfaces and their properties
213 msg.read(interfaces);
214
215 auto properties = interfaces.find(INVENTORY_IFACE);
216 if (properties != interfaces.end())
217 {
218 auto property = properties->second.find(PRESENT_PROP);
219 if (property != properties->second.end())
220 {
221 present = std::get<bool>(property->second);
222
223 log<level::INFO>(fmt::format("Power Supply {} Present {}",
224 inventoryPath, present)
225 .c_str());
226
227 updateInventory();
228 }
229 }
230 }
231}
232
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500233void PowerSupply::updateInventory()
234{
235 using namespace phosphor::pmbus;
236
237#ifdef IBM_VPD
238 std::string ccin;
239 std::string pn;
240 std::string fn;
241 std::string header;
242 std::string sn;
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500243 using PropertyMap =
George Liu070c1bc2020-10-12 11:28:01 +0800244 std::map<std::string,
245 std::variant<std::string, std::vector<uint8_t>, bool>>;
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500246 PropertyMap assetProps;
George Liu070c1bc2020-10-12 11:28:01 +0800247 PropertyMap operProps;
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500248 PropertyMap versionProps;
249 PropertyMap ipzvpdDINFProps;
250 PropertyMap ipzvpdVINIProps;
251 using InterfaceMap = std::map<std::string, PropertyMap>;
252 InterfaceMap interfaces;
253 using ObjectMap = std::map<sdbusplus::message::object_path, InterfaceMap>;
254 ObjectMap object;
255#endif
256
257 if (present)
258 {
259 // TODO: non-IBM inventory updates?
260
261#ifdef IBM_VPD
262 try
263 {
264 ccin = pmbusIntf->readString(CCIN, Type::HwmonDeviceDebug);
265 assetProps.emplace(MODEL_PROP, ccin);
266 }
267 catch (ReadFailure& e)
268 {
269 // Ignore the read failure, let pmbus code indicate failure, path...
270 // TODO - ibm918
271 // https://github.com/openbmc/docs/blob/master/designs/vpd-collection.md
272 // The BMC must log errors if any of the VPD cannot be properly
273 // parsed or fails ECC checks.
274 }
275
276 try
277 {
278 pn = pmbusIntf->readString(PART_NUMBER, Type::HwmonDeviceDebug);
279 assetProps.emplace(PN_PROP, pn);
280 }
281 catch (ReadFailure& e)
282 {
283 // Ignore the read failure, let pmbus code indicate failure, path...
284 }
285
286 try
287 {
288 fn = pmbusIntf->readString(FRU_NUMBER, Type::HwmonDeviceDebug);
289 }
290 catch (ReadFailure& e)
291 {
292 // Ignore the read failure, let pmbus code indicate failure, path...
293 }
294
295 try
296 {
297 header =
298 pmbusIntf->readString(SERIAL_HEADER, Type::HwmonDeviceDebug);
299 sn = pmbusIntf->readString(SERIAL_NUMBER, Type::HwmonDeviceDebug);
300 assetProps.emplace(SN_PROP, sn);
301 }
302 catch (ReadFailure& e)
303 {
304 // Ignore the read failure, let pmbus code indicate failure, path...
305 }
306
307 try
308 {
Brandon Wymanc9efe412020-10-09 15:42:50 -0500309 fwVersion =
310 pmbusIntf->readString(FW_VERSION, Type::HwmonDeviceDebug);
311 versionProps.emplace(VERSION_PROP, fwVersion);
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500312 }
313 catch (ReadFailure& e)
314 {
315 // Ignore the read failure, let pmbus code indicate failure, path...
316 }
317
318 ipzvpdVINIProps.emplace("CC",
319 std::vector<uint8_t>(ccin.begin(), ccin.end()));
320 ipzvpdVINIProps.emplace("PN",
321 std::vector<uint8_t>(pn.begin(), pn.end()));
322 ipzvpdVINIProps.emplace("FN",
323 std::vector<uint8_t>(fn.begin(), fn.end()));
324 std::string header_sn = header + sn + '\0';
325 ipzvpdVINIProps.emplace(
326 "SN", std::vector<uint8_t>(header_sn.begin(), header_sn.end()));
327 std::string description = "IBM PS";
328 ipzvpdVINIProps.emplace(
329 "DR", std::vector<uint8_t>(description.begin(), description.end()));
330
331 // Update the Resource Identifier (RI) keyword
332 // 2 byte FRC: 0x0003
333 // 2 byte RID: 0x1000, 0x1001...
334 std::uint8_t num = std::stoul(
335 inventoryPath.substr(inventoryPath.size() - 1, 1), nullptr, 0);
336 std::vector<uint8_t> ri{0x00, 0x03, 0x10, num};
337 ipzvpdDINFProps.emplace("RI", ri);
338
339 // Fill in the FRU Label (FL) keyword.
340 std::string fl = "E";
341 fl.push_back(inventoryPath.back());
342 fl.resize(FL_KW_SIZE, ' ');
343 ipzvpdDINFProps.emplace("FL",
344 std::vector<uint8_t>(fl.begin(), fl.end()));
345
346 interfaces.emplace(ASSET_IFACE, std::move(assetProps));
347 interfaces.emplace(VERSION_IFACE, std::move(versionProps));
348 interfaces.emplace(DINF_IFACE, std::move(ipzvpdDINFProps));
349 interfaces.emplace(VINI_IFACE, std::move(ipzvpdVINIProps));
350
George Liu070c1bc2020-10-12 11:28:01 +0800351 // Update the Functional
352 operProps.emplace(FUNCTIONAL_PROP, present);
353 interfaces.emplace(OPERATIONAL_STATE_IFACE, std::move(operProps));
354
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500355 auto path = inventoryPath.substr(strlen(INVENTORY_OBJ_PATH));
356 object.emplace(path, std::move(interfaces));
357
358 try
359 {
360 auto service =
361 util::getService(INVENTORY_OBJ_PATH, INVENTORY_MGR_IFACE, bus);
362
363 if (service.empty())
364 {
365 log<level::ERR>("Unable to get inventory manager service");
366 return;
367 }
368
369 auto method =
370 bus.new_method_call(service.c_str(), INVENTORY_OBJ_PATH,
371 INVENTORY_MGR_IFACE, "Notify");
372
373 method.append(std::move(object));
374
375 auto reply = bus.call(method);
376 }
377 catch (std::exception& e)
378 {
Jay Meyer6a3fd2c2020-08-25 16:37:16 -0500379 log<level::ERR>(
380 std::string(e.what() + std::string(" PATH=") + inventoryPath)
381 .c_str());
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500382 }
383#endif
384 }
385}
386
Brandon Wyman3f1242f2020-01-28 13:11:25 -0600387} // namespace phosphor::power::psu