blob: ab1caf9bd83f14258b15187302ab090a40a14faa [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 Wyman3f1242f2020-01-28 13:11:25 -06008#include <xyz/openbmc_project/Common/Device/error.hpp>
9
Brandon Wyman1d7a7df2020-03-26 10:14:05 -050010#include <chrono> // sleep_for()
11#include <cstdint> // uint8_t...
12#include <thread> // sleep_for()
13
Brandon Wyman3f1242f2020-01-28 13:11:25 -060014namespace phosphor::power::psu
Brandon Wymanaed1f752019-11-25 18:10:52 -060015{
16
17using namespace phosphor::logging;
Brandon Wyman3f1242f2020-01-28 13:11:25 -060018using namespace sdbusplus::xyz::openbmc_project::Common::Device::Error;
Brandon Wymanaed1f752019-11-25 18:10:52 -060019
20void PowerSupply::updatePresence()
21{
22 try
23 {
Brandon Wyman3f1242f2020-01-28 13:11:25 -060024 present = getPresence(bus, inventoryPath);
Brandon Wymanaed1f752019-11-25 18:10:52 -060025 }
26 catch (const sdbusplus::exception::SdBusError& e)
27 {
28 // Relying on property change or interface added to retry.
29 // Log an informational trace to the journal.
30 log<level::INFO>("D-Bus property access failure exception");
31 }
32}
33
Brandon Wyman3f1242f2020-01-28 13:11:25 -060034void PowerSupply::analyze()
35{
36 using namespace phosphor::pmbus;
37
38 if (present)
39 {
40 try
41 {
Brandon Wymanfed0ba22020-09-26 20:02:51 -050042 statusWord = pmbusIntf->read(STATUS_WORD, Type::Debug);
Brandon Wyman3f1242f2020-01-28 13:11:25 -060043
44 if (statusWord)
45 {
46 if (statusWord & status_word::INPUT_FAULT_WARN)
47 {
48 if (!inputFault)
49 {
50 log<level::INFO>(
51 "INPUT fault",
52 entry("STATUS_WORD=0x%04X",
53 static_cast<uint16_t>(statusWord)));
54 }
55
56 faultFound = true;
57 inputFault = true;
58 }
59
60 if (statusWord & status_word::MFR_SPECIFIC_FAULT)
61 {
62 if (!mfrFault)
63 {
64 log<level::INFO>(
65 "MFRSPECIFIC fault",
66 entry("STATUS_WORD=0x%04X",
67 static_cast<uint16_t>(statusWord)));
68 }
69 faultFound = true;
70 mfrFault = true;
71 }
72
73 if (statusWord & status_word::VIN_UV_FAULT)
74 {
75 if (!vinUVFault)
76 {
77 log<level::INFO>(
78 "VIN_UV fault",
79 entry("STATUS_WORD=0x%04X",
80 static_cast<uint16_t>(statusWord)));
81 }
82
83 faultFound = true;
84 vinUVFault = true;
85 }
86 }
87 else
88 {
89 faultFound = false;
90 inputFault = false;
91 mfrFault = false;
92 vinUVFault = false;
93 }
94 }
95 catch (ReadFailure& e)
96 {
97 phosphor::logging::commit<ReadFailure>();
98 }
99 }
100}
101
Brandon Wyman59a35792020-06-04 12:37:40 -0500102void PowerSupply::onOffConfig(uint8_t data)
103{
104 using namespace phosphor::pmbus;
105
106 if (present)
107 {
108 log<level::INFO>("ON_OFF_CONFIG write", entry("DATA=0x%02X", data));
109 try
110 {
111 std::vector<uint8_t> configData{data};
112 pmbusIntf->writeBinary(ON_OFF_CONFIG, configData,
113 Type::HwmonDeviceDebug);
114 }
115 catch (...)
116 {
117 // The underlying code in writeBinary will log a message to the
118 // journal if the write fails. If the ON_OFF_CONFIG is not setup as
119 // desired, later fault detection and analysis code should catch any
120 // of the fall out. We should not need to terminate the application
121 // if this write fails.
122 }
123 }
124}
125
Brandon Wyman3c208462020-05-13 16:25:58 -0500126void PowerSupply::clearFaults()
127{
128 faultFound = false;
129 inputFault = false;
130 mfrFault = false;
131 vinUVFault = false;
Brandon Wymanb76ab242020-09-16 18:06:06 -0500132 faultLogged = false;
Brandon Wyman3c208462020-05-13 16:25:58 -0500133
134 // The PMBus device driver does not allow for writing CLEAR_FAULTS
135 // directly. However, the pmbus hwmon device driver code will send a
136 // CLEAR_FAULTS after reading from any of the hwmon "files" in sysfs, so
137 // reading in1_input should result in clearing the fault bits in
138 // STATUS_BYTE/STATUS_WORD.
139 // I do not care what the return value is.
140 try
141 {
142 static_cast<void>(
143 pmbusIntf->read("in1_input", phosphor::pmbus::Type::Hwmon));
144 }
145 catch (ReadFailure& e)
146 {
147 // Since I do not care what the return value is, I really do not
148 // care much if it gets a ReadFailure either. However, this should not
149 // prevent the application from continuing to run, so catching the read
150 // failure.
151 }
152}
153
Brandon Wymanaed1f752019-11-25 18:10:52 -0600154void PowerSupply::inventoryChanged(sdbusplus::message::message& msg)
155{
156 std::string msgSensor;
Patrick Williamsabe49412020-05-13 17:59:47 -0500157 std::map<std::string, std::variant<uint32_t, bool>> msgData;
Brandon Wymanaed1f752019-11-25 18:10:52 -0600158 msg.read(msgSensor, msgData);
159
160 // Check if it was the Present property that changed.
161 auto valPropMap = msgData.find(PRESENT_PROP);
162 if (valPropMap != msgData.end())
163 {
164 if (std::get<bool>(valPropMap->second))
165 {
166 present = true;
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500167 // TODO: Immediately trying to read or write the "files" causes read
168 // or write failures.
169 using namespace std::chrono_literals;
170 std::this_thread::sleep_for(20ms);
Brandon Wyman59a35792020-06-04 12:37:40 -0500171 onOffConfig(phosphor::pmbus::ON_OFF_CONFIG_CONTROL_PIN_ONLY);
Brandon Wymanaed1f752019-11-25 18:10:52 -0600172 clearFaults();
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500173 updateInventory();
Brandon Wymanaed1f752019-11-25 18:10:52 -0600174 }
175 else
176 {
177 present = false;
178
179 // Clear out the now outdated inventory properties
180 updateInventory();
181 }
182 }
183}
184
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500185void PowerSupply::updateInventory()
186{
187 using namespace phosphor::pmbus;
188
189#ifdef IBM_VPD
190 std::string ccin;
191 std::string pn;
192 std::string fn;
193 std::string header;
194 std::string sn;
195 std::string version;
196 using PropertyMap =
197 std::map<std::string, std::variant<std::string, std::vector<uint8_t>>>;
198 PropertyMap assetProps;
199 PropertyMap versionProps;
200 PropertyMap ipzvpdDINFProps;
201 PropertyMap ipzvpdVINIProps;
202 using InterfaceMap = std::map<std::string, PropertyMap>;
203 InterfaceMap interfaces;
204 using ObjectMap = std::map<sdbusplus::message::object_path, InterfaceMap>;
205 ObjectMap object;
206#endif
207
208 if (present)
209 {
210 // TODO: non-IBM inventory updates?
211
212#ifdef IBM_VPD
213 try
214 {
215 ccin = pmbusIntf->readString(CCIN, Type::HwmonDeviceDebug);
216 assetProps.emplace(MODEL_PROP, ccin);
217 }
218 catch (ReadFailure& e)
219 {
220 // Ignore the read failure, let pmbus code indicate failure, path...
221 // TODO - ibm918
222 // https://github.com/openbmc/docs/blob/master/designs/vpd-collection.md
223 // The BMC must log errors if any of the VPD cannot be properly
224 // parsed or fails ECC checks.
225 }
226
227 try
228 {
229 pn = pmbusIntf->readString(PART_NUMBER, Type::HwmonDeviceDebug);
230 assetProps.emplace(PN_PROP, pn);
231 }
232 catch (ReadFailure& e)
233 {
234 // Ignore the read failure, let pmbus code indicate failure, path...
235 }
236
237 try
238 {
239 fn = pmbusIntf->readString(FRU_NUMBER, Type::HwmonDeviceDebug);
240 }
241 catch (ReadFailure& e)
242 {
243 // Ignore the read failure, let pmbus code indicate failure, path...
244 }
245
246 try
247 {
248 header =
249 pmbusIntf->readString(SERIAL_HEADER, Type::HwmonDeviceDebug);
250 sn = pmbusIntf->readString(SERIAL_NUMBER, Type::HwmonDeviceDebug);
251 assetProps.emplace(SN_PROP, sn);
252 }
253 catch (ReadFailure& e)
254 {
255 // Ignore the read failure, let pmbus code indicate failure, path...
256 }
257
258 try
259 {
260 version = pmbusIntf->readString(FW_VERSION, Type::HwmonDeviceDebug);
261 versionProps.emplace(VERSION_PROP, version);
262 }
263 catch (ReadFailure& e)
264 {
265 // Ignore the read failure, let pmbus code indicate failure, path...
266 }
267
268 ipzvpdVINIProps.emplace("CC",
269 std::vector<uint8_t>(ccin.begin(), ccin.end()));
270 ipzvpdVINIProps.emplace("PN",
271 std::vector<uint8_t>(pn.begin(), pn.end()));
272 ipzvpdVINIProps.emplace("FN",
273 std::vector<uint8_t>(fn.begin(), fn.end()));
274 std::string header_sn = header + sn + '\0';
275 ipzvpdVINIProps.emplace(
276 "SN", std::vector<uint8_t>(header_sn.begin(), header_sn.end()));
277 std::string description = "IBM PS";
278 ipzvpdVINIProps.emplace(
279 "DR", std::vector<uint8_t>(description.begin(), description.end()));
280
281 // Update the Resource Identifier (RI) keyword
282 // 2 byte FRC: 0x0003
283 // 2 byte RID: 0x1000, 0x1001...
284 std::uint8_t num = std::stoul(
285 inventoryPath.substr(inventoryPath.size() - 1, 1), nullptr, 0);
286 std::vector<uint8_t> ri{0x00, 0x03, 0x10, num};
287 ipzvpdDINFProps.emplace("RI", ri);
288
289 // Fill in the FRU Label (FL) keyword.
290 std::string fl = "E";
291 fl.push_back(inventoryPath.back());
292 fl.resize(FL_KW_SIZE, ' ');
293 ipzvpdDINFProps.emplace("FL",
294 std::vector<uint8_t>(fl.begin(), fl.end()));
295
296 interfaces.emplace(ASSET_IFACE, std::move(assetProps));
297 interfaces.emplace(VERSION_IFACE, std::move(versionProps));
298 interfaces.emplace(DINF_IFACE, std::move(ipzvpdDINFProps));
299 interfaces.emplace(VINI_IFACE, std::move(ipzvpdVINIProps));
300
301 auto path = inventoryPath.substr(strlen(INVENTORY_OBJ_PATH));
302 object.emplace(path, std::move(interfaces));
303
304 try
305 {
306 auto service =
307 util::getService(INVENTORY_OBJ_PATH, INVENTORY_MGR_IFACE, bus);
308
309 if (service.empty())
310 {
311 log<level::ERR>("Unable to get inventory manager service");
312 return;
313 }
314
315 auto method =
316 bus.new_method_call(service.c_str(), INVENTORY_OBJ_PATH,
317 INVENTORY_MGR_IFACE, "Notify");
318
319 method.append(std::move(object));
320
321 auto reply = bus.call(method);
322 }
323 catch (std::exception& e)
324 {
Jay Meyer6a3fd2c2020-08-25 16:37:16 -0500325 log<level::ERR>(
326 std::string(e.what() + std::string(" PATH=") + inventoryPath)
327 .c_str());
Brandon Wyman1d7a7df2020-03-26 10:14:05 -0500328 }
329#endif
330 }
331}
332
Brandon Wyman3f1242f2020-01-28 13:11:25 -0600333} // namespace phosphor::power::psu