blob: 518f48328401909d87bc7af8799a5a42173d1155 [file] [log] [blame]
Jim Wright1553cd92021-03-31 16:11:59 -05001/**
2 * Copyright © 2021 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 */
16
Jim Wright7945dd22021-04-06 16:55:15 -050017#include "ucd90320_monitor.hpp"
18
Jim Wright56ae78e2021-12-01 14:46:15 -060019#include "utility.hpp"
Jim Wright7945dd22021-04-06 16:55:15 -050020
Jim Wright56ae78e2021-12-01 14:46:15 -060021#include <fmt/format.h>
22#include <fmt/ranges.h>
23
Jim Wrightd8a86172021-12-08 11:38:26 -060024#include <nlohmann/json.hpp>
Jim Wright56ae78e2021-12-01 14:46:15 -060025#include <phosphor-logging/log.hpp>
26#include <sdbusplus/bus.hpp>
Jim Wright71a14132022-01-28 09:46:46 -060027#include <xyz/openbmc_project/Common/Device/error.hpp>
Jim Wright56ae78e2021-12-01 14:46:15 -060028
Jim Wrightd8a86172021-12-08 11:38:26 -060029#include <fstream>
Jim Wright56ae78e2021-12-01 14:46:15 -060030#include <map>
Jim Wright213ffe92022-06-03 08:54:30 -050031#include <span>
Jim Wright7945dd22021-04-06 16:55:15 -050032#include <string>
33
34namespace phosphor::power::sequencer
Jim Wright1553cd92021-03-31 16:11:59 -050035{
Jim Wright7945dd22021-04-06 16:55:15 -050036
Jim Wrightd8a86172021-12-08 11:38:26 -060037using json = nlohmann::json;
Jim Wright71a14132022-01-28 09:46:46 -060038using namespace pmbus;
Jim Wright56ae78e2021-12-01 14:46:15 -060039using namespace phosphor::logging;
40using namespace phosphor::power;
41
42const std::string compatibleInterface =
43 "xyz.openbmc_project.Configuration.IBMCompatibleSystem";
44const std::string compatibleNamesProperty = "Names";
45
Jim Wright71a14132022-01-28 09:46:46 -060046namespace device_error = sdbusplus::xyz::openbmc_project::Common::Device::Error;
47
Jim Wright7945dd22021-04-06 16:55:15 -050048UCD90320Monitor::UCD90320Monitor(sdbusplus::bus::bus& bus, std::uint8_t i2cBus,
49 std::uint16_t i2cAddress) :
Jim Wright930458c2022-01-24 14:37:27 -060050 PowerSequencerMonitor(bus),
51 match{bus,
52 sdbusplus::bus::match::rules::interfacesAdded() +
53 sdbusplus::bus::match::rules::sender(
54 "xyz.openbmc_project.EntityManager"),
55 std::bind(&UCD90320Monitor::interfacesAddedHandler, this,
56 std::placeholders::_1)},
Jim Wright56ae78e2021-12-01 14:46:15 -060057 pmbusInterface{
58 fmt::format("/sys/bus/i2c/devices/{}-{:04x}", i2cBus, i2cAddress)
59 .c_str(),
60 "ucd9000", 0}
61
62{
63 // Use the compatible system types information, if already available, to
64 // load the configuration file
65 findCompatibleSystemTypes();
Jim Wright1553cd92021-03-31 16:11:59 -050066}
Jim Wright56ae78e2021-12-01 14:46:15 -060067
68void UCD90320Monitor::findCompatibleSystemTypes()
69{
70 try
71 {
72 auto subTree = util::getSubTree(bus, "/xyz/openbmc_project/inventory",
73 compatibleInterface, 0);
74
75 auto objectIt = subTree.cbegin();
76 if (objectIt != subTree.cend())
77 {
78 const auto& objPath = objectIt->first;
79
80 // Get the first service name
81 auto serviceIt = objectIt->second.cbegin();
82 if (serviceIt != objectIt->second.cend())
83 {
84 std::string service = serviceIt->first;
85 if (!service.empty())
86 {
87 std::vector<std::string> compatibleSystemTypes;
88
89 // Get compatible system types property value
90 util::getProperty(compatibleInterface,
91 compatibleNamesProperty, objPath, service,
92 bus, compatibleSystemTypes);
93
94 log<level::DEBUG>(
95 fmt::format("Found compatible systems: {}",
96 compatibleSystemTypes)
97 .c_str());
98 // Use compatible systems information to find config file
Jim Wrightd8a86172021-12-08 11:38:26 -060099 findConfigFile(compatibleSystemTypes);
Jim Wright56ae78e2021-12-01 14:46:15 -0600100 }
101 }
102 }
103 }
104 catch (const std::exception&)
105 {
106 // Compatible system types information is not available.
107 }
108}
109
Jim Wrightd8a86172021-12-08 11:38:26 -0600110void UCD90320Monitor::findConfigFile(
111 const std::vector<std::string>& compatibleSystemTypes)
112{
113 // Expected config file path name:
114 // /usr/share/phosphor-power-sequencer/UCD90320Monitor_<systemType>.json
115
116 // Add possible file names based on compatible system types (if any)
117 for (const std::string& systemType : compatibleSystemTypes)
118 {
119 // Check if file exists
120 std::filesystem::path pathName{
121 "/usr/share/phosphor-power-sequencer/UCD90320Monitor_" +
122 systemType + ".json"};
123 if (std::filesystem::exists(pathName))
124 {
125 log<level::INFO>(
126 fmt::format("Config file path: {}", pathName.string()).c_str());
127 parseConfigFile(pathName);
128 break;
129 }
130 }
131}
132
Jim Wright56ae78e2021-12-01 14:46:15 -0600133void UCD90320Monitor::interfacesAddedHandler(sdbusplus::message::message& msg)
134{
Jim Wrightd8a86172021-12-08 11:38:26 -0600135 // Only continue if message is valid and rails / pins have not already been
136 // found
137 if (!msg || !rails.empty())
Jim Wright56ae78e2021-12-01 14:46:15 -0600138 {
139 return;
140 }
141
142 try
143 {
144 // Read the dbus message
145 sdbusplus::message::object_path objPath;
146 std::map<std::string,
147 std::map<std::string, std::variant<std::vector<std::string>>>>
148 interfaces;
149 msg.read(objPath, interfaces);
150
151 // Find the compatible interface, if present
152 auto itIntf = interfaces.find(compatibleInterface);
153 if (itIntf != interfaces.cend())
154 {
155 // Find the Names property of the compatible interface, if present
156 auto itProp = itIntf->second.find(compatibleNamesProperty);
157 if (itProp != itIntf->second.cend())
158 {
159 // Get value of Names property
160 const auto& propValue = std::get<0>(itProp->second);
161 if (!propValue.empty())
162 {
163 log<level::INFO>(
164 fmt::format(
165 "InterfacesAdded for compatible systems: {}",
166 propValue)
167 .c_str());
168
169 // Use compatible systems information to find config file
Jim Wrightd8a86172021-12-08 11:38:26 -0600170 findConfigFile(propValue);
Jim Wright56ae78e2021-12-01 14:46:15 -0600171 }
172 }
173 }
174 }
175 catch (const std::exception&)
176 {
177 // Error trying to read interfacesAdded message.
178 }
179}
Jim Wright7945dd22021-04-06 16:55:15 -0500180
Jim Wrightd8a86172021-12-08 11:38:26 -0600181void UCD90320Monitor::parseConfigFile(const std::filesystem::path& pathName)
182{
183 try
184 {
185 std::ifstream file{pathName};
186 json rootElement = json::parse(file);
187
188 // Parse rail information from config file
189 auto railsIterator = rootElement.find("rails");
190 if (railsIterator != rootElement.end())
191 {
192 for (const auto& railElement : *railsIterator)
193 {
194 std::string rail = railElement.get<std::string>();
195 rails.emplace_back(std::move(rail));
196 }
197 }
198 else
199 {
200 log<level::ERR>(
201 fmt::format("No rails found in configuration file: {}",
202 pathName.string())
203 .c_str());
204 }
205 log<level::DEBUG>(fmt::format("Found rails: {}", rails).c_str());
206
207 // Parse pin information from config file
208 auto pinsIterator = rootElement.find("pins");
209 if (pinsIterator != rootElement.end())
210 {
211 for (const auto& pinElement : *pinsIterator)
212 {
213 auto nameIterator = pinElement.find("name");
214 auto lineIterator = pinElement.find("line");
215
216 if (nameIterator != pinElement.end() &&
217 lineIterator != pinElement.end())
218 {
219 std::string name = (*nameIterator).get<std::string>();
Jim Wrightd8fc0682022-01-11 15:36:00 -0600220 unsigned int line = (*lineIterator).get<unsigned int>();
Jim Wrightd8a86172021-12-08 11:38:26 -0600221
222 Pin pin;
223 pin.name = name;
224 pin.line = line;
225 pins.emplace_back(std::move(pin));
226 }
227 else
228 {
229 log<level::ERR>(
230 fmt::format(
231 "No name or line found within pin in configuration file: {}",
232 pathName.string())
233 .c_str());
234 }
235 }
236 }
237 else
238 {
239 log<level::ERR>(
240 fmt::format("No pins found in configuration file: {}",
241 pathName.string())
242 .c_str());
243 }
244 log<level::DEBUG>(
Jim Wright213ffe92022-06-03 08:54:30 -0500245 fmt::format("Found number of pins: {}", pins.size()).c_str());
Jim Wrightd8a86172021-12-08 11:38:26 -0600246 }
247 catch (const std::exception& e)
248 {
249 // Log error message in journal
250 log<level::ERR>(std::string("Exception parsing configuration file: " +
251 std::string(e.what()))
252 .c_str());
253 }
254}
255
Jim Wright71a14132022-01-28 09:46:46 -0600256void UCD90320Monitor::onFailure(bool timeout,
257 const std::string& powerSupplyError)
258{
Jim Wrightf6f0da92022-02-28 21:08:33 -0600259 std::string message;
Jim Wright71a14132022-01-28 09:46:46 -0600260 std::map<std::string, std::string> additionalData{};
Jim Wright71a14132022-01-28 09:46:46 -0600261
262 try
263 {
Jim Wright3accffe2022-03-10 17:19:42 -0600264 onFailureCheckRails(message, additionalData, powerSupplyError);
Jim Wright3accffe2022-03-10 17:19:42 -0600265 onFailureCheckPins(message, additionalData);
Jim Wright71a14132022-01-28 09:46:46 -0600266 }
267 catch (device_error::ReadFailure& e)
268 {
Jim Wrightf6f0da92022-02-28 21:08:33 -0600269 log<level::ERR>(
270 fmt::format("ReadFailure when collecting metadata, error {}",
271 e.what())
272 .c_str());
Jim Wright71a14132022-01-28 09:46:46 -0600273 }
Jim Wrightf6f0da92022-02-28 21:08:33 -0600274
275 if (message.empty())
276 {
277 // Could not isolate, but we know something failed, so issue a timeout
278 // or generic power good error
279 message = timeout ? "xyz.openbmc_project.Power.Error.PowerOnTimeout"
280 : "xyz.openbmc_project.Power.Error.Shutdown";
281 }
282 logError(message, additionalData);
Jim Wright213ffe92022-06-03 08:54:30 -0500283 if (!timeout)
284 {
285 createBmcDump();
286 }
Jim Wright71a14132022-01-28 09:46:46 -0600287}
288
Jim Wright3accffe2022-03-10 17:19:42 -0600289void UCD90320Monitor::onFailureCheckPins(
290 std::string& message, std::map<std::string, std::string>& additionalData)
291{
292 // Setup a list of all the GPIOs on the chip
293 gpiod::chip chip{"ucd90320", gpiod::chip::OPEN_BY_LABEL};
294 log<level::INFO>(fmt::format("GPIO chip name: {}", chip.name()).c_str());
295 log<level::INFO>(fmt::format("GPIO chip label: {}", chip.label()).c_str());
296 unsigned int numberLines = chip.num_lines();
297 log<level::INFO>(
298 fmt::format("GPIO chip number of lines: {}", numberLines).c_str());
299
300 // Workaround libgpiod bulk line maximum by getting values from individual
301 // lines
302 std::vector<int> values;
Jim Wright213ffe92022-06-03 08:54:30 -0500303 try
Jim Wright3accffe2022-03-10 17:19:42 -0600304 {
Jim Wright213ffe92022-06-03 08:54:30 -0500305 for (unsigned int offset = 0; offset < numberLines; ++offset)
306 {
307 gpiod::line line = chip.get_line(offset);
308 line.request({"phosphor-power-control",
309 gpiod::line_request::DIRECTION_INPUT, 0});
310 values.push_back(line.get_value());
311 line.release();
312 }
313 }
314 catch (const std::exception& e)
315 {
316 log<level::ERR>(
317 fmt::format("Error reading device GPIOs, error {}", e.what())
318 .c_str());
319 additionalData.emplace("GPIO_ERROR", e.what());
Jim Wright3accffe2022-03-10 17:19:42 -0600320 }
321
Jim Wright213ffe92022-06-03 08:54:30 -0500322 // Add GPIO values to additional data, device has 84 GPIO pins so that value
323 // is expected
324 if (numberLines == 84)
325 {
326 log<level::INFO>(fmt::format("MAR01-24 GPIO values: {}",
327 std::span{values}.subspan(0, 24))
328 .c_str());
329 additionalData.emplace(
330 "MAR01_24_GPIO_VALUES",
331 fmt::format("{}", std::span{values}.subspan(0, 24)));
332 log<level::INFO>(fmt::format("EN1-32 GPIO values: {}",
333 std::span{values}.subspan(24, 32))
334 .c_str());
335 additionalData.emplace(
336 "EN1_32_GPIO_VALUES",
337 fmt::format("{}", std::span{values}.subspan(24, 32)));
338 log<level::INFO>(fmt::format("LGP01-16 GPIO values: {}",
339 std::span{values}.subspan(56, 16))
340 .c_str());
341 additionalData.emplace(
342 "LGP01_16_GPIO_VALUES",
343 fmt::format("{}", std::span{values}.subspan(56, 16)));
344 log<level::INFO>(fmt::format("DMON1-8 GPIO values: {}",
345 std::span{values}.subspan(72, 8))
346 .c_str());
347 additionalData.emplace(
348 "DMON1_8_GPIO_VALUES",
349 fmt::format("{}", std::span{values}.subspan(72, 8)));
350 log<level::INFO>(fmt::format("GPIO1-4 GPIO values: {}",
351 std::span{values}.subspan(80, 4))
352 .c_str());
353 additionalData.emplace(
354 "GPIO1_4_GPIO_VALUES",
355 fmt::format("{}", std::span{values}.subspan(80, 4)));
356 }
357 else
358 {
359 log<level::INFO>(fmt::format("GPIO values: {}", values).c_str());
360 additionalData.emplace("GPIO_VALUES", fmt::format("{}", values));
361 }
Jim Wright3accffe2022-03-10 17:19:42 -0600362
363 // Only check GPIOs if no rail fail was found
364 if (message.empty())
365 {
366 for (size_t pin = 0; pin < pins.size(); ++pin)
367 {
368 unsigned int line = pins[pin].line;
369 if (line < values.size())
370 {
371 int value = values[line];
372 if (value == 0)
373 {
374 additionalData.emplace("INPUT_NUM",
375 fmt::format("{}", line));
376 additionalData.emplace("INPUT_NAME", pins[pin].name);
Jim Wright3accffe2022-03-10 17:19:42 -0600377 message =
378 "xyz.openbmc_project.Power.Error.PowerSequencerPGOODFault";
379 return;
380 }
381 }
382 }
383 }
384}
385
386void UCD90320Monitor::onFailureCheckRails(
387 std::string& message, std::map<std::string, std::string>& additionalData,
388 const std::string& powerSupplyError)
389{
390 auto statusWord = readStatusWord();
391 additionalData.emplace("STATUS_WORD", fmt::format("{:#06x}", statusWord));
392 try
393 {
394 additionalData.emplace("MFR_STATUS",
Jim Wright213ffe92022-06-03 08:54:30 -0500395 fmt::format("{:#014x}", readMFRStatus()));
Jim Wright3accffe2022-03-10 17:19:42 -0600396 }
397 catch (device_error::ReadFailure& e)
398 {
399 log<level::ERR>(
400 fmt::format("ReadFailure when collecting MFR_STATUS, error {}",
401 e.what())
402 .c_str());
403 }
404
405 // The status_word register has a summary bit to tell us if each page even
406 // needs to be checked
407 if (statusWord & status_word::VOUT_FAULT)
408 {
Jim Wright286bc702022-05-02 11:01:46 -0500409 constexpr size_t numberPages = 32;
Jim Wright3accffe2022-03-10 17:19:42 -0600410 for (size_t page = 0; page < numberPages; page++)
411 {
412 auto statusVout = pmbusInterface.insertPageNum(STATUS_VOUT, page);
Jim Wright2a054922022-04-13 09:39:27 -0500413 if (pmbusInterface.exists(statusVout, Type::Debug))
Jim Wright3accffe2022-03-10 17:19:42 -0600414 {
Jim Wright2a054922022-04-13 09:39:27 -0500415 uint8_t vout = pmbusInterface.read(statusVout, Type::Debug);
Jim Wright3accffe2022-03-10 17:19:42 -0600416
Jim Wright2a054922022-04-13 09:39:27 -0500417 if (vout)
418 {
Jim Wright213ffe92022-06-03 08:54:30 -0500419 // If any bits are on log them, though some are just
420 // warnings so they won't cause errors
Jim Wright2a054922022-04-13 09:39:27 -0500421 log<level::INFO>(
Jim Wright213ffe92022-06-03 08:54:30 -0500422 fmt::format("{}, value: {:#04x}", statusVout, vout)
Jim Wright2a054922022-04-13 09:39:27 -0500423 .c_str());
Jim Wright3accffe2022-03-10 17:19:42 -0600424
Jim Wright213ffe92022-06-03 08:54:30 -0500425 // Log errors if any non-warning bits on
426 if (vout & ~status_vout::WARNING_MASK)
Jim Wright2a054922022-04-13 09:39:27 -0500427 {
Jim Wright213ffe92022-06-03 08:54:30 -0500428 additionalData.emplace(
429 fmt::format("STATUS{}_VOUT", page),
430 fmt::format("{:#04x}", vout));
Jim Wright2a054922022-04-13 09:39:27 -0500431
Jim Wright213ffe92022-06-03 08:54:30 -0500432 // Base the callouts on the first vout failure found
433 if (message.empty())
434 {
435 if (page < rails.size())
436 {
437 additionalData.emplace("RAIL_NAME",
438 rails[page]);
439 }
440
441 // Use power supply error if set and 12v rail has
442 // failed, else use voltage error
443 message =
444 ((page == 0) && !powerSupplyError.empty())
445 ? powerSupplyError
446 : "xyz.openbmc_project.Power.Error.PowerSequencerVoltageFault";
447 }
448 }
Jim Wright2a054922022-04-13 09:39:27 -0500449 }
Jim Wright3accffe2022-03-10 17:19:42 -0600450 }
451 }
452 }
453}
454
Jim Wright71a14132022-01-28 09:46:46 -0600455uint16_t UCD90320Monitor::readStatusWord()
456{
457 return pmbusInterface.read(STATUS_WORD, Type::Debug);
458}
459
Jim Wright213ffe92022-06-03 08:54:30 -0500460uint64_t UCD90320Monitor::readMFRStatus()
Jim Wright71a14132022-01-28 09:46:46 -0600461{
462 const std::string mfrStatus = "mfr_status";
463 return pmbusInterface.read(mfrStatus, Type::HwmonDeviceDebug);
464}
465
Jim Wright7945dd22021-04-06 16:55:15 -0500466} // namespace phosphor::power::sequencer