blob: cedc1845417fce46b7c6acda5848f7eb907fc2e5 [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
Brandon Wyman510acaa2020-11-05 18:32:04 -060022PowerSupply::PowerSupply(sdbusplus::bus::bus& bus, const std::string& invpath,
23 std::uint8_t i2cbus, std::uint16_t i2caddr) :
24 bus(bus),
25 inventoryPath(invpath)
26{
27 if (inventoryPath.empty())
28 {
29 throw std::invalid_argument{"Invalid empty inventoryPath"};
30 }
31
32 // Setup the functions to call when the D-Bus inventory path for the
33 // Present property changes.
34 presentMatch = std::make_unique<sdbusplus::bus::match_t>(
35 bus,
36 sdbusplus::bus::match::rules::propertiesChanged(inventoryPath,
37 INVENTORY_IFACE),
38 [this](auto& msg) { this->inventoryChanged(msg); });
39
40 presentAddedMatch = std::make_unique<sdbusplus::bus::match_t>(
41 bus,
42 sdbusplus::bus::match::rules::interfacesAdded() +
43 sdbusplus::bus::match::rules::argNpath(0, inventoryPath),
44 [this](auto& msg) { this->inventoryAdded(msg); });
45
46 std::ostringstream ss;
47 ss << std::hex << std::setw(4) << std::setfill('0') << i2caddr;
48 std::string addrStr = ss.str();
49 pmbusIntf = phosphor::pmbus::createPMBus(i2cbus, addrStr);
50
51 // Get the current state of the Present property.
52 updatePresence();
53}
54
Brandon Wymanaed1f752019-11-25 18:10:52 -060055void PowerSupply::updatePresence()
56{
57 try
58 {
Brandon Wyman3f1242f2020-01-28 13:11:25 -060059 present = getPresence(bus, inventoryPath);
Brandon Wymanaed1f752019-11-25 18:10:52 -060060 }
61 catch (const sdbusplus::exception::SdBusError& e)
62 {
63 // Relying on property change or interface added to retry.
64 // Log an informational trace to the journal.
Brandon Wymandf13c3a2020-12-15 14:25:22 -060065 log<level::INFO>(
66 fmt::format("D-Bus property {} access failure exception",
67 inventoryPath)
68 .c_str());
Brandon Wymanaed1f752019-11-25 18:10:52 -060069 }
70}
71
Brandon Wyman3f1242f2020-01-28 13:11:25 -060072void PowerSupply::analyze()
73{
74 using namespace phosphor::pmbus;
75
Brandon Wymanf65c4062020-08-19 13:15:53 -050076 if ((present) && (readFail < LOG_LIMIT))
Brandon Wyman3f1242f2020-01-28 13:11:25 -060077 {
78 try
79 {
Brandon Wymanfed0ba22020-09-26 20:02:51 -050080 statusWord = pmbusIntf->read(STATUS_WORD, Type::Debug);
Brandon Wymanf65c4062020-08-19 13:15:53 -050081 // Read worked, reset the fail count.
82 readFail = 0;
Brandon Wyman3f1242f2020-01-28 13:11:25 -060083
84 if (statusWord)
85 {
Jay Meyer10d94052020-11-30 14:41:21 -060086 statusMFR = pmbusIntf->read(STATUS_MFR, Type::Debug);
Brandon Wyman3f1242f2020-01-28 13:11:25 -060087 if (statusWord & status_word::INPUT_FAULT_WARN)
88 {
89 if (!inputFault)
90 {
Jay Meyer10d94052020-11-30 14:41:21 -060091 log<level::INFO>(fmt::format("INPUT fault: "
92 "status word = {:#04x}, "
93 "MFR fault = {:#02x}",
94 statusWord, statusMFR)
95 .c_str());
Brandon Wyman3f1242f2020-01-28 13:11:25 -060096 }
97
98 faultFound = true;
99 inputFault = true;
100 }
101
102 if (statusWord & status_word::MFR_SPECIFIC_FAULT)
103 {
104 if (!mfrFault)
105 {
Jay Meyer10d94052020-11-30 14:41:21 -0600106 log<level::ERR>(fmt::format("MFR fault: "
107 "status word = {:#04x} "
108 "MFR fault = {:#02x}",
109 statusWord, statusMFR)
110 .c_str());
Brandon Wyman3f1242f2020-01-28 13:11:25 -0600111 }
112 faultFound = true;
113 mfrFault = true;
114 }
115
116 if (statusWord & status_word::VIN_UV_FAULT)
117 {
118 if (!vinUVFault)
119 {
Jay Meyer10d94052020-11-30 14:41:21 -0600120 log<level::INFO>(fmt::format("VIN_UV fault: "
121 "status word = {:#04x}, "
122 "MFR fault = {:#02x}",
123 statusWord, statusMFR)
124 .c_str());
Brandon Wyman3f1242f2020-01-28 13:11:25 -0600125 }
126
127 faultFound = true;
128 vinUVFault = true;
129 }
130 }
131 else
132 {
133 faultFound = false;
134 inputFault = false;
135 mfrFault = false;
136 vinUVFault = false;
137 }
138 }
139 catch (ReadFailure& e)
140 {
Brandon Wymanf65c4062020-08-19 13:15:53 -0500141 readFail++;
Brandon Wyman3f1242f2020-01-28 13:11:25 -0600142 phosphor::logging::commit<ReadFailure>();
143 }
144 }
145}
146
Brandon Wyman59a35792020-06-04 12:37:40 -0500147void PowerSupply::onOffConfig(uint8_t data)
148{
149 using namespace phosphor::pmbus;
150
151 if (present)
152 {
153 log<level::INFO>("ON_OFF_CONFIG write", entry("DATA=0x%02X", data));
154 try
155 {
156 std::vector<uint8_t> configData{data};
157 pmbusIntf->writeBinary(ON_OFF_CONFIG, configData,
158 Type::HwmonDeviceDebug);
159 }
160 catch (...)
161 {
162 // The underlying code in writeBinary will log a message to the
163 // journal if the write fails. If the ON_OFF_CONFIG is not setup as
164 // desired, later fault detection and analysis code should catch any
165 // of the fall out. We should not need to terminate the application
166 // if this write fails.
167 }
168 }
169}
170
Brandon Wyman3c208462020-05-13 16:25:58 -0500171void PowerSupply::clearFaults()
172{
Brandon Wyman5474c912021-02-23 14:39:43 -0600173 faultLogged = false;
Brandon Wyman3c208462020-05-13 16:25:58 -0500174 // The PMBus device driver does not allow for writing CLEAR_FAULTS
175 // directly. However, the pmbus hwmon device driver code will send a
176 // CLEAR_FAULTS after reading from any of the hwmon "files" in sysfs, so
177 // reading in1_input should result in clearing the fault bits in
178 // STATUS_BYTE/STATUS_WORD.
179 // I do not care what the return value is.
Brandon Wyman11151532020-11-10 13:45:57 -0600180 if (present)
Brandon Wyman3c208462020-05-13 16:25:58 -0500181 {
Brandon Wyman9564e942020-11-10 14:01:42 -0600182 faultFound = false;
183 inputFault = false;
184 mfrFault = false;
Jay Meyer10d94052020-11-30 14:41:21 -0600185 statusMFR = 0;
Brandon Wyman9564e942020-11-10 14:01:42 -0600186 vinUVFault = false;
187 readFail = 0;
Brandon Wyman9564e942020-11-10 14:01:42 -0600188
Brandon Wyman11151532020-11-10 13:45:57 -0600189 try
190 {
191 static_cast<void>(
192 pmbusIntf->read("in1_input", phosphor::pmbus::Type::Hwmon));
193 }
194 catch (ReadFailure& e)
195 {
196 // Since I do not care what the return value is, I really do not
197 // care much if it gets a ReadFailure either. However, this should
198 // not prevent the application from continuing to run, so catching
199 // the read failure.
200 }
Brandon Wyman3c208462020-05-13 16:25:58 -0500201 }
202}
203
Brandon Wymanaed1f752019-11-25 18:10:52 -0600204void PowerSupply::inventoryChanged(sdbusplus::message::message& msg)
205{
206 std::string msgSensor;
Patrick Williamsabe49412020-05-13 17:59:47 -0500207 std::map<std::string, std::variant<uint32_t, bool>> msgData;
Brandon Wymanaed1f752019-11-25 18:10:52 -0600208 msg.read(msgSensor, msgData);
209
210 // Check if it was the Present property that changed.
211 auto valPropMap = msgData.find(PRESENT_PROP);
212 if (valPropMap != msgData.end())
213 {
214 if (std::get<bool>(valPropMap->second))
215 {
216 present = true;
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500217 // TODO: Immediately trying to read or write the "files" causes read
218 // or write failures.
219 using namespace std::chrono_literals;
220 std::this_thread::sleep_for(20ms);
Brandon Wyman9564e942020-11-10 14:01:42 -0600221 pmbusIntf->findHwmonDir();
Brandon Wyman59a35792020-06-04 12:37:40 -0500222 onOffConfig(phosphor::pmbus::ON_OFF_CONFIG_CONTROL_PIN_ONLY);
Brandon Wymanaed1f752019-11-25 18:10:52 -0600223 clearFaults();
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500224 updateInventory();
Brandon Wymanaed1f752019-11-25 18:10:52 -0600225 }
226 else
227 {
228 present = false;
229
230 // Clear out the now outdated inventory properties
231 updateInventory();
232 }
233 }
234}
235
Brandon Wyman9a507db2021-02-25 16:15:22 -0600236void PowerSupply::inventoryAdded(sdbusplus::message::message& msg)
237{
238 sdbusplus::message::object_path path;
239 msg.read(path);
240 // Make sure the signal is for the PSU inventory path
241 if (path == inventoryPath)
242 {
243 std::map<std::string, std::map<std::string, std::variant<bool>>>
244 interfaces;
245 // Get map of interfaces and their properties
246 msg.read(interfaces);
247
248 auto properties = interfaces.find(INVENTORY_IFACE);
249 if (properties != interfaces.end())
250 {
251 auto property = properties->second.find(PRESENT_PROP);
252 if (property != properties->second.end())
253 {
254 present = std::get<bool>(property->second);
255
256 log<level::INFO>(fmt::format("Power Supply {} Present {}",
257 inventoryPath, present)
258 .c_str());
259
260 updateInventory();
261 }
262 }
263 }
264}
265
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500266void PowerSupply::updateInventory()
267{
268 using namespace phosphor::pmbus;
269
270#ifdef IBM_VPD
271 std::string ccin;
272 std::string pn;
273 std::string fn;
274 std::string header;
275 std::string sn;
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500276 using PropertyMap =
George Liu070c1bc2020-10-12 11:28:01 +0800277 std::map<std::string,
278 std::variant<std::string, std::vector<uint8_t>, bool>>;
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500279 PropertyMap assetProps;
George Liu070c1bc2020-10-12 11:28:01 +0800280 PropertyMap operProps;
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500281 PropertyMap versionProps;
282 PropertyMap ipzvpdDINFProps;
283 PropertyMap ipzvpdVINIProps;
284 using InterfaceMap = std::map<std::string, PropertyMap>;
285 InterfaceMap interfaces;
286 using ObjectMap = std::map<sdbusplus::message::object_path, InterfaceMap>;
287 ObjectMap object;
288#endif
289
290 if (present)
291 {
292 // TODO: non-IBM inventory updates?
293
294#ifdef IBM_VPD
295 try
296 {
297 ccin = pmbusIntf->readString(CCIN, Type::HwmonDeviceDebug);
298 assetProps.emplace(MODEL_PROP, ccin);
299 }
300 catch (ReadFailure& e)
301 {
302 // Ignore the read failure, let pmbus code indicate failure, path...
303 // TODO - ibm918
304 // https://github.com/openbmc/docs/blob/master/designs/vpd-collection.md
305 // The BMC must log errors if any of the VPD cannot be properly
306 // parsed or fails ECC checks.
307 }
308
309 try
310 {
311 pn = pmbusIntf->readString(PART_NUMBER, Type::HwmonDeviceDebug);
312 assetProps.emplace(PN_PROP, pn);
313 }
314 catch (ReadFailure& e)
315 {
316 // Ignore the read failure, let pmbus code indicate failure, path...
317 }
318
319 try
320 {
321 fn = pmbusIntf->readString(FRU_NUMBER, Type::HwmonDeviceDebug);
322 }
323 catch (ReadFailure& e)
324 {
325 // Ignore the read failure, let pmbus code indicate failure, path...
326 }
327
328 try
329 {
330 header =
331 pmbusIntf->readString(SERIAL_HEADER, Type::HwmonDeviceDebug);
332 sn = pmbusIntf->readString(SERIAL_NUMBER, Type::HwmonDeviceDebug);
333 assetProps.emplace(SN_PROP, sn);
334 }
335 catch (ReadFailure& e)
336 {
337 // Ignore the read failure, let pmbus code indicate failure, path...
338 }
339
340 try
341 {
Brandon Wymanc9efe412020-10-09 15:42:50 -0500342 fwVersion =
343 pmbusIntf->readString(FW_VERSION, Type::HwmonDeviceDebug);
344 versionProps.emplace(VERSION_PROP, fwVersion);
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500345 }
346 catch (ReadFailure& e)
347 {
348 // Ignore the read failure, let pmbus code indicate failure, path...
349 }
350
351 ipzvpdVINIProps.emplace("CC",
352 std::vector<uint8_t>(ccin.begin(), ccin.end()));
353 ipzvpdVINIProps.emplace("PN",
354 std::vector<uint8_t>(pn.begin(), pn.end()));
355 ipzvpdVINIProps.emplace("FN",
356 std::vector<uint8_t>(fn.begin(), fn.end()));
357 std::string header_sn = header + sn + '\0';
358 ipzvpdVINIProps.emplace(
359 "SN", std::vector<uint8_t>(header_sn.begin(), header_sn.end()));
360 std::string description = "IBM PS";
361 ipzvpdVINIProps.emplace(
362 "DR", std::vector<uint8_t>(description.begin(), description.end()));
363
364 // Update the Resource Identifier (RI) keyword
365 // 2 byte FRC: 0x0003
366 // 2 byte RID: 0x1000, 0x1001...
367 std::uint8_t num = std::stoul(
368 inventoryPath.substr(inventoryPath.size() - 1, 1), nullptr, 0);
369 std::vector<uint8_t> ri{0x00, 0x03, 0x10, num};
370 ipzvpdDINFProps.emplace("RI", ri);
371
372 // Fill in the FRU Label (FL) keyword.
373 std::string fl = "E";
374 fl.push_back(inventoryPath.back());
375 fl.resize(FL_KW_SIZE, ' ');
376 ipzvpdDINFProps.emplace("FL",
377 std::vector<uint8_t>(fl.begin(), fl.end()));
378
379 interfaces.emplace(ASSET_IFACE, std::move(assetProps));
380 interfaces.emplace(VERSION_IFACE, std::move(versionProps));
381 interfaces.emplace(DINF_IFACE, std::move(ipzvpdDINFProps));
382 interfaces.emplace(VINI_IFACE, std::move(ipzvpdVINIProps));
383
George Liu070c1bc2020-10-12 11:28:01 +0800384 // Update the Functional
385 operProps.emplace(FUNCTIONAL_PROP, present);
386 interfaces.emplace(OPERATIONAL_STATE_IFACE, std::move(operProps));
387
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500388 auto path = inventoryPath.substr(strlen(INVENTORY_OBJ_PATH));
389 object.emplace(path, std::move(interfaces));
390
391 try
392 {
393 auto service =
394 util::getService(INVENTORY_OBJ_PATH, INVENTORY_MGR_IFACE, bus);
395
396 if (service.empty())
397 {
398 log<level::ERR>("Unable to get inventory manager service");
399 return;
400 }
401
402 auto method =
403 bus.new_method_call(service.c_str(), INVENTORY_OBJ_PATH,
404 INVENTORY_MGR_IFACE, "Notify");
405
406 method.append(std::move(object));
407
408 auto reply = bus.call(method);
409 }
410 catch (std::exception& e)
411 {
Jay Meyer6a3fd2c2020-08-25 16:37:16 -0500412 log<level::ERR>(
413 std::string(e.what() + std::string(" PATH=") + inventoryPath)
414 .c_str());
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500415 }
416#endif
417 }
418}
419
Brandon Wyman3f1242f2020-01-28 13:11:25 -0600420} // namespace phosphor::power::psu