blob: 02fe7797cb27c3001488526335c6a20a3c4cde60 [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);
Adriana Kobylak572a9052021-03-30 15:58:07 +0000299 modelName = ccin;
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500300 }
301 catch (ReadFailure& e)
302 {
303 // Ignore the read failure, let pmbus code indicate failure, path...
304 // TODO - ibm918
305 // https://github.com/openbmc/docs/blob/master/designs/vpd-collection.md
306 // The BMC must log errors if any of the VPD cannot be properly
307 // parsed or fails ECC checks.
308 }
309
310 try
311 {
312 pn = pmbusIntf->readString(PART_NUMBER, Type::HwmonDeviceDebug);
313 assetProps.emplace(PN_PROP, pn);
314 }
315 catch (ReadFailure& e)
316 {
317 // Ignore the read failure, let pmbus code indicate failure, path...
318 }
319
320 try
321 {
322 fn = pmbusIntf->readString(FRU_NUMBER, Type::HwmonDeviceDebug);
323 }
324 catch (ReadFailure& e)
325 {
326 // Ignore the read failure, let pmbus code indicate failure, path...
327 }
328
329 try
330 {
331 header =
332 pmbusIntf->readString(SERIAL_HEADER, Type::HwmonDeviceDebug);
333 sn = pmbusIntf->readString(SERIAL_NUMBER, Type::HwmonDeviceDebug);
334 assetProps.emplace(SN_PROP, sn);
335 }
336 catch (ReadFailure& e)
337 {
338 // Ignore the read failure, let pmbus code indicate failure, path...
339 }
340
341 try
342 {
Brandon Wymanc9efe412020-10-09 15:42:50 -0500343 fwVersion =
344 pmbusIntf->readString(FW_VERSION, Type::HwmonDeviceDebug);
345 versionProps.emplace(VERSION_PROP, fwVersion);
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500346 }
347 catch (ReadFailure& e)
348 {
349 // Ignore the read failure, let pmbus code indicate failure, path...
350 }
351
352 ipzvpdVINIProps.emplace("CC",
353 std::vector<uint8_t>(ccin.begin(), ccin.end()));
354 ipzvpdVINIProps.emplace("PN",
355 std::vector<uint8_t>(pn.begin(), pn.end()));
356 ipzvpdVINIProps.emplace("FN",
357 std::vector<uint8_t>(fn.begin(), fn.end()));
358 std::string header_sn = header + sn + '\0';
359 ipzvpdVINIProps.emplace(
360 "SN", std::vector<uint8_t>(header_sn.begin(), header_sn.end()));
361 std::string description = "IBM PS";
362 ipzvpdVINIProps.emplace(
363 "DR", std::vector<uint8_t>(description.begin(), description.end()));
364
365 // Update the Resource Identifier (RI) keyword
366 // 2 byte FRC: 0x0003
367 // 2 byte RID: 0x1000, 0x1001...
368 std::uint8_t num = std::stoul(
369 inventoryPath.substr(inventoryPath.size() - 1, 1), nullptr, 0);
370 std::vector<uint8_t> ri{0x00, 0x03, 0x10, num};
371 ipzvpdDINFProps.emplace("RI", ri);
372
373 // Fill in the FRU Label (FL) keyword.
374 std::string fl = "E";
375 fl.push_back(inventoryPath.back());
376 fl.resize(FL_KW_SIZE, ' ');
377 ipzvpdDINFProps.emplace("FL",
378 std::vector<uint8_t>(fl.begin(), fl.end()));
379
380 interfaces.emplace(ASSET_IFACE, std::move(assetProps));
381 interfaces.emplace(VERSION_IFACE, std::move(versionProps));
382 interfaces.emplace(DINF_IFACE, std::move(ipzvpdDINFProps));
383 interfaces.emplace(VINI_IFACE, std::move(ipzvpdVINIProps));
384
George Liu070c1bc2020-10-12 11:28:01 +0800385 // Update the Functional
386 operProps.emplace(FUNCTIONAL_PROP, present);
387 interfaces.emplace(OPERATIONAL_STATE_IFACE, std::move(operProps));
388
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500389 auto path = inventoryPath.substr(strlen(INVENTORY_OBJ_PATH));
390 object.emplace(path, std::move(interfaces));
391
392 try
393 {
394 auto service =
395 util::getService(INVENTORY_OBJ_PATH, INVENTORY_MGR_IFACE, bus);
396
397 if (service.empty())
398 {
399 log<level::ERR>("Unable to get inventory manager service");
400 return;
401 }
402
403 auto method =
404 bus.new_method_call(service.c_str(), INVENTORY_OBJ_PATH,
405 INVENTORY_MGR_IFACE, "Notify");
406
407 method.append(std::move(object));
408
409 auto reply = bus.call(method);
410 }
411 catch (std::exception& e)
412 {
Jay Meyer6a3fd2c2020-08-25 16:37:16 -0500413 log<level::ERR>(
414 std::string(e.what() + std::string(" PATH=") + inventoryPath)
415 .c_str());
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500416 }
417#endif
418 }
419}
420
Brandon Wyman3f1242f2020-01-28 13:11:25 -0600421} // namespace phosphor::power::psu