blob: 297d20ba9bf250d952bf6fb3e8fd37f88f13a955 [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 Wrightc91eed02022-07-22 11:27:06 -050019#include "types.hpp"
Jim Wright56ae78e2021-12-01 14:46:15 -060020#include "utility.hpp"
Jim Wright7945dd22021-04-06 16:55:15 -050021
Jim Wright56ae78e2021-12-01 14:46:15 -060022#include <fmt/format.h>
23#include <fmt/ranges.h>
24
Jim Wrightd8a86172021-12-08 11:38:26 -060025#include <nlohmann/json.hpp>
Jim Wright56ae78e2021-12-01 14:46:15 -060026#include <phosphor-logging/log.hpp>
27#include <sdbusplus/bus.hpp>
Jim Wright71a14132022-01-28 09:46:46 -060028#include <xyz/openbmc_project/Common/Device/error.hpp>
Jim Wright56ae78e2021-12-01 14:46:15 -060029
Jim Wright056bc192022-06-06 17:46:34 -050030#include <chrono>
Jim Wrightd8a86172021-12-08 11:38:26 -060031#include <fstream>
Jim Wright56ae78e2021-12-01 14:46:15 -060032#include <map>
Jim Wright213ffe92022-06-03 08:54:30 -050033#include <span>
Jim Wright7945dd22021-04-06 16:55:15 -050034#include <string>
35
36namespace phosphor::power::sequencer
Jim Wright1553cd92021-03-31 16:11:59 -050037{
Jim Wright7945dd22021-04-06 16:55:15 -050038
Jim Wrightd8a86172021-12-08 11:38:26 -060039using json = nlohmann::json;
Jim Wright71a14132022-01-28 09:46:46 -060040using namespace pmbus;
Jim Wright56ae78e2021-12-01 14:46:15 -060041using namespace phosphor::logging;
42using namespace phosphor::power;
43
44const std::string compatibleInterface =
45 "xyz.openbmc_project.Configuration.IBMCompatibleSystem";
46const std::string compatibleNamesProperty = "Names";
47
Jim Wright71a14132022-01-28 09:46:46 -060048namespace device_error = sdbusplus::xyz::openbmc_project::Common::Device::Error;
49
Patrick Williams7354ce62022-07-22 19:26:56 -050050UCD90320Monitor::UCD90320Monitor(sdbusplus::bus_t& bus, std::uint8_t i2cBus,
Jim Wright7945dd22021-04-06 16:55:15 -050051 std::uint16_t i2cAddress) :
Jim Wright930458c2022-01-24 14:37:27 -060052 PowerSequencerMonitor(bus),
53 match{bus,
54 sdbusplus::bus::match::rules::interfacesAdded() +
55 sdbusplus::bus::match::rules::sender(
56 "xyz.openbmc_project.EntityManager"),
57 std::bind(&UCD90320Monitor::interfacesAddedHandler, this,
58 std::placeholders::_1)},
Jim Wright56ae78e2021-12-01 14:46:15 -060059 pmbusInterface{
60 fmt::format("/sys/bus/i2c/devices/{}-{:04x}", i2cBus, i2cAddress)
61 .c_str(),
62 "ucd9000", 0}
63
64{
65 // Use the compatible system types information, if already available, to
66 // load the configuration file
67 findCompatibleSystemTypes();
Jim Wright1553cd92021-03-31 16:11:59 -050068}
Jim Wright56ae78e2021-12-01 14:46:15 -060069
70void UCD90320Monitor::findCompatibleSystemTypes()
71{
72 try
73 {
74 auto subTree = util::getSubTree(bus, "/xyz/openbmc_project/inventory",
75 compatibleInterface, 0);
76
77 auto objectIt = subTree.cbegin();
78 if (objectIt != subTree.cend())
79 {
80 const auto& objPath = objectIt->first;
81
82 // Get the first service name
83 auto serviceIt = objectIt->second.cbegin();
84 if (serviceIt != objectIt->second.cend())
85 {
86 std::string service = serviceIt->first;
87 if (!service.empty())
88 {
89 std::vector<std::string> compatibleSystemTypes;
90
91 // Get compatible system types property value
92 util::getProperty(compatibleInterface,
93 compatibleNamesProperty, objPath, service,
94 bus, compatibleSystemTypes);
95
96 log<level::DEBUG>(
97 fmt::format("Found compatible systems: {}",
98 compatibleSystemTypes)
99 .c_str());
100 // Use compatible systems information to find config file
Jim Wrightd8a86172021-12-08 11:38:26 -0600101 findConfigFile(compatibleSystemTypes);
Jim Wright56ae78e2021-12-01 14:46:15 -0600102 }
103 }
104 }
105 }
106 catch (const std::exception&)
107 {
108 // Compatible system types information is not available.
109 }
110}
111
Jim Wrightd8a86172021-12-08 11:38:26 -0600112void UCD90320Monitor::findConfigFile(
113 const std::vector<std::string>& compatibleSystemTypes)
114{
115 // Expected config file path name:
116 // /usr/share/phosphor-power-sequencer/UCD90320Monitor_<systemType>.json
117
118 // Add possible file names based on compatible system types (if any)
119 for (const std::string& systemType : compatibleSystemTypes)
120 {
121 // Check if file exists
122 std::filesystem::path pathName{
123 "/usr/share/phosphor-power-sequencer/UCD90320Monitor_" +
124 systemType + ".json"};
125 if (std::filesystem::exists(pathName))
126 {
127 log<level::INFO>(
128 fmt::format("Config file path: {}", pathName.string()).c_str());
129 parseConfigFile(pathName);
130 break;
131 }
132 }
133}
134
Patrick Williams7354ce62022-07-22 19:26:56 -0500135void UCD90320Monitor::interfacesAddedHandler(sdbusplus::message_t& msg)
Jim Wright56ae78e2021-12-01 14:46:15 -0600136{
Jim Wrightd8a86172021-12-08 11:38:26 -0600137 // Only continue if message is valid and rails / pins have not already been
138 // found
139 if (!msg || !rails.empty())
Jim Wright56ae78e2021-12-01 14:46:15 -0600140 {
141 return;
142 }
143
144 try
145 {
146 // Read the dbus message
147 sdbusplus::message::object_path objPath;
148 std::map<std::string,
149 std::map<std::string, std::variant<std::vector<std::string>>>>
150 interfaces;
151 msg.read(objPath, interfaces);
152
153 // Find the compatible interface, if present
154 auto itIntf = interfaces.find(compatibleInterface);
155 if (itIntf != interfaces.cend())
156 {
157 // Find the Names property of the compatible interface, if present
158 auto itProp = itIntf->second.find(compatibleNamesProperty);
159 if (itProp != itIntf->second.cend())
160 {
161 // Get value of Names property
162 const auto& propValue = std::get<0>(itProp->second);
163 if (!propValue.empty())
164 {
165 log<level::INFO>(
166 fmt::format(
167 "InterfacesAdded for compatible systems: {}",
168 propValue)
169 .c_str());
170
171 // Use compatible systems information to find config file
Jim Wrightd8a86172021-12-08 11:38:26 -0600172 findConfigFile(propValue);
Jim Wright56ae78e2021-12-01 14:46:15 -0600173 }
174 }
175 }
176 }
177 catch (const std::exception&)
178 {
179 // Error trying to read interfacesAdded message.
180 }
181}
Jim Wright7945dd22021-04-06 16:55:15 -0500182
Jim Wrightc91eed02022-07-22 11:27:06 -0500183bool UCD90320Monitor::isPresent(const std::string& inventoryPath)
184{
185 // Empty path indicates no presence check is needed
186 if (inventoryPath.empty())
187 {
188 return true;
189 }
190
191 // Get presence from D-Bus interface/property
192 try
193 {
194 bool present{true};
195 util::getProperty(INVENTORY_IFACE, PRESENT_PROP, inventoryPath,
196 INVENTORY_MGR_IFACE, bus, present);
197 log<level::INFO>(
198 fmt::format("Presence, path: {}, value: {}", inventoryPath, present)
199 .c_str());
200 return present;
201 }
202 catch (const std::exception& e)
203 {
204 log<level::INFO>(
205 fmt::format("Error getting presence property, path: {}, error: {}",
206 inventoryPath, e.what())
207 .c_str());
208 return false;
209 }
210}
211
Jim Wrightd8a86172021-12-08 11:38:26 -0600212void UCD90320Monitor::parseConfigFile(const std::filesystem::path& pathName)
213{
214 try
215 {
216 std::ifstream file{pathName};
217 json rootElement = json::parse(file);
218
219 // Parse rail information from config file
220 auto railsIterator = rootElement.find("rails");
221 if (railsIterator != rootElement.end())
222 {
223 for (const auto& railElement : *railsIterator)
224 {
Jim Wrightc91eed02022-07-22 11:27:06 -0500225 auto nameIterator = railElement.find("name");
226
227 if (nameIterator != railElement.end())
228 {
229 Rail rail;
230 rail.name = (*nameIterator).get<std::string>();
231
232 // Presence element is optional
233 auto presenceIterator = railElement.find("presence");
234 if (presenceIterator != railElement.end())
235 {
236 rail.presence = (*presenceIterator).get<std::string>();
237 }
238
239 log<level::DEBUG>(
240 fmt::format("Adding rail, name: {}, presence: {}",
241 rail.name, rail.presence)
242 .c_str());
243 rails.emplace_back(std::move(rail));
244 }
245 else
246 {
247 log<level::ERR>(
248 fmt::format(
249 "No name found within rail in configuration file: {}",
250 pathName.string())
251 .c_str());
252 }
Jim Wrightd8a86172021-12-08 11:38:26 -0600253 }
254 }
255 else
256 {
257 log<level::ERR>(
258 fmt::format("No rails found in configuration file: {}",
259 pathName.string())
260 .c_str());
261 }
Jim Wrightc91eed02022-07-22 11:27:06 -0500262 log<level::DEBUG>(
263 fmt::format("Found number of rails: {}", rails.size()).c_str());
Jim Wrightd8a86172021-12-08 11:38:26 -0600264
265 // Parse pin information from config file
266 auto pinsIterator = rootElement.find("pins");
267 if (pinsIterator != rootElement.end())
268 {
269 for (const auto& pinElement : *pinsIterator)
270 {
271 auto nameIterator = pinElement.find("name");
272 auto lineIterator = pinElement.find("line");
273
274 if (nameIterator != pinElement.end() &&
275 lineIterator != pinElement.end())
276 {
Jim Wrightd8a86172021-12-08 11:38:26 -0600277 Pin pin;
Jim Wrightc91eed02022-07-22 11:27:06 -0500278 pin.name = (*nameIterator).get<std::string>();
279 pin.line = (*lineIterator).get<unsigned int>();
280
281 // Presence element is optional
282 auto presenceIterator = pinElement.find("presence");
283 if (presenceIterator != pinElement.end())
284 {
285 pin.presence = (*presenceIterator).get<std::string>();
286 }
287
288 log<level::DEBUG>(
289 fmt::format(
290 "Adding pin, name: {}, line: {}, presence: {}",
291 pin.name, pin.line, pin.presence)
292 .c_str());
Jim Wrightd8a86172021-12-08 11:38:26 -0600293 pins.emplace_back(std::move(pin));
294 }
295 else
296 {
297 log<level::ERR>(
298 fmt::format(
299 "No name or line found within pin in configuration file: {}",
300 pathName.string())
301 .c_str());
302 }
303 }
304 }
305 else
306 {
307 log<level::ERR>(
308 fmt::format("No pins found in configuration file: {}",
309 pathName.string())
310 .c_str());
311 }
312 log<level::DEBUG>(
Jim Wright213ffe92022-06-03 08:54:30 -0500313 fmt::format("Found number of pins: {}", pins.size()).c_str());
Jim Wrightd8a86172021-12-08 11:38:26 -0600314 }
315 catch (const std::exception& e)
316 {
Jim Wrightc91eed02022-07-22 11:27:06 -0500317 log<level::ERR>(
318 fmt::format("Error parsing configuration file, error: {}", e.what())
319 .c_str());
Jim Wrightd8a86172021-12-08 11:38:26 -0600320 }
321}
322
Jim Wright71a14132022-01-28 09:46:46 -0600323void UCD90320Monitor::onFailure(bool timeout,
324 const std::string& powerSupplyError)
325{
Jim Wright08fcb9f2022-07-21 09:48:29 -0500326 // Wait before reading device data. This is to allow the
Jim Wright056bc192022-06-06 17:46:34 -0500327 // power supplies and other hardware time to complete failure processing.
Jim Wright08fcb9f2022-07-21 09:48:29 -0500328 std::this_thread::sleep_for(std::chrono::seconds(7));
Jim Wright056bc192022-06-06 17:46:34 -0500329
Jim Wrightf6f0da92022-02-28 21:08:33 -0600330 std::string message;
Jim Wright71a14132022-01-28 09:46:46 -0600331 std::map<std::string, std::string> additionalData{};
Jim Wright71a14132022-01-28 09:46:46 -0600332
333 try
334 {
Jim Wright3accffe2022-03-10 17:19:42 -0600335 onFailureCheckRails(message, additionalData, powerSupplyError);
Jim Wright3accffe2022-03-10 17:19:42 -0600336 onFailureCheckPins(message, additionalData);
Jim Wright71a14132022-01-28 09:46:46 -0600337 }
Jim Wrightc91eed02022-07-22 11:27:06 -0500338 catch (const std::exception& e)
Jim Wright71a14132022-01-28 09:46:46 -0600339 {
Jim Wrightf6f0da92022-02-28 21:08:33 -0600340 log<level::ERR>(
Jim Wrightc91eed02022-07-22 11:27:06 -0500341 fmt::format("Error when collecting metadata, error: {}", e.what())
Jim Wrightf6f0da92022-02-28 21:08:33 -0600342 .c_str());
Jim Wrightc91eed02022-07-22 11:27:06 -0500343 additionalData.emplace("ERROR", e.what());
Jim Wright71a14132022-01-28 09:46:46 -0600344 }
Jim Wrightf6f0da92022-02-28 21:08:33 -0600345
346 if (message.empty())
347 {
348 // Could not isolate, but we know something failed, so issue a timeout
349 // or generic power good error
350 message = timeout ? "xyz.openbmc_project.Power.Error.PowerOnTimeout"
351 : "xyz.openbmc_project.Power.Error.Shutdown";
352 }
353 logError(message, additionalData);
Jim Wright213ffe92022-06-03 08:54:30 -0500354 if (!timeout)
355 {
356 createBmcDump();
357 }
Jim Wright71a14132022-01-28 09:46:46 -0600358}
359
Jim Wright3accffe2022-03-10 17:19:42 -0600360void UCD90320Monitor::onFailureCheckPins(
361 std::string& message, std::map<std::string, std::string>& additionalData)
362{
363 // Setup a list of all the GPIOs on the chip
364 gpiod::chip chip{"ucd90320", gpiod::chip::OPEN_BY_LABEL};
365 log<level::INFO>(fmt::format("GPIO chip name: {}", chip.name()).c_str());
366 log<level::INFO>(fmt::format("GPIO chip label: {}", chip.label()).c_str());
367 unsigned int numberLines = chip.num_lines();
368 log<level::INFO>(
369 fmt::format("GPIO chip number of lines: {}", numberLines).c_str());
370
371 // Workaround libgpiod bulk line maximum by getting values from individual
372 // lines
373 std::vector<int> values;
Jim Wright213ffe92022-06-03 08:54:30 -0500374 try
Jim Wright3accffe2022-03-10 17:19:42 -0600375 {
Jim Wright213ffe92022-06-03 08:54:30 -0500376 for (unsigned int offset = 0; offset < numberLines; ++offset)
377 {
378 gpiod::line line = chip.get_line(offset);
379 line.request({"phosphor-power-control",
380 gpiod::line_request::DIRECTION_INPUT, 0});
381 values.push_back(line.get_value());
382 line.release();
383 }
384 }
385 catch (const std::exception& e)
386 {
387 log<level::ERR>(
Jim Wrightc91eed02022-07-22 11:27:06 -0500388 fmt::format("Error reading device GPIOs, error: {}", e.what())
Jim Wright213ffe92022-06-03 08:54:30 -0500389 .c_str());
390 additionalData.emplace("GPIO_ERROR", e.what());
Jim Wright3accffe2022-03-10 17:19:42 -0600391 }
392
Jim Wright213ffe92022-06-03 08:54:30 -0500393 // Add GPIO values to additional data, device has 84 GPIO pins so that value
394 // is expected
Jim Wrightc91eed02022-07-22 11:27:06 -0500395 if (numberLines == 84 && values.size() >= 84)
Jim Wright213ffe92022-06-03 08:54:30 -0500396 {
397 log<level::INFO>(fmt::format("MAR01-24 GPIO values: {}",
398 std::span{values}.subspan(0, 24))
399 .c_str());
400 additionalData.emplace(
401 "MAR01_24_GPIO_VALUES",
402 fmt::format("{}", std::span{values}.subspan(0, 24)));
403 log<level::INFO>(fmt::format("EN1-32 GPIO values: {}",
404 std::span{values}.subspan(24, 32))
405 .c_str());
406 additionalData.emplace(
407 "EN1_32_GPIO_VALUES",
408 fmt::format("{}", std::span{values}.subspan(24, 32)));
409 log<level::INFO>(fmt::format("LGP01-16 GPIO values: {}",
410 std::span{values}.subspan(56, 16))
411 .c_str());
412 additionalData.emplace(
413 "LGP01_16_GPIO_VALUES",
414 fmt::format("{}", std::span{values}.subspan(56, 16)));
415 log<level::INFO>(fmt::format("DMON1-8 GPIO values: {}",
416 std::span{values}.subspan(72, 8))
417 .c_str());
418 additionalData.emplace(
419 "DMON1_8_GPIO_VALUES",
420 fmt::format("{}", std::span{values}.subspan(72, 8)));
421 log<level::INFO>(fmt::format("GPIO1-4 GPIO values: {}",
422 std::span{values}.subspan(80, 4))
423 .c_str());
424 additionalData.emplace(
425 "GPIO1_4_GPIO_VALUES",
426 fmt::format("{}", std::span{values}.subspan(80, 4)));
427 }
428 else
429 {
430 log<level::INFO>(fmt::format("GPIO values: {}", values).c_str());
431 additionalData.emplace("GPIO_VALUES", fmt::format("{}", values));
432 }
Jim Wright3accffe2022-03-10 17:19:42 -0600433
434 // Only check GPIOs if no rail fail was found
435 if (message.empty())
436 {
437 for (size_t pin = 0; pin < pins.size(); ++pin)
438 {
439 unsigned int line = pins[pin].line;
440 if (line < values.size())
441 {
442 int value = values[line];
Jim Wrightc91eed02022-07-22 11:27:06 -0500443
444 if ((value == 0) && isPresent(pins[pin].presence))
Jim Wright3accffe2022-03-10 17:19:42 -0600445 {
446 additionalData.emplace("INPUT_NUM",
447 fmt::format("{}", line));
448 additionalData.emplace("INPUT_NAME", pins[pin].name);
Jim Wright3accffe2022-03-10 17:19:42 -0600449 message =
450 "xyz.openbmc_project.Power.Error.PowerSequencerPGOODFault";
451 return;
452 }
453 }
454 }
455 }
456}
457
458void UCD90320Monitor::onFailureCheckRails(
459 std::string& message, std::map<std::string, std::string>& additionalData,
460 const std::string& powerSupplyError)
461{
462 auto statusWord = readStatusWord();
463 additionalData.emplace("STATUS_WORD", fmt::format("{:#06x}", statusWord));
464 try
465 {
466 additionalData.emplace("MFR_STATUS",
Jim Wright213ffe92022-06-03 08:54:30 -0500467 fmt::format("{:#014x}", readMFRStatus()));
Jim Wright3accffe2022-03-10 17:19:42 -0600468 }
Jim Wrightc91eed02022-07-22 11:27:06 -0500469 catch (const std::exception& e)
Jim Wright3accffe2022-03-10 17:19:42 -0600470 {
471 log<level::ERR>(
Jim Wrightc91eed02022-07-22 11:27:06 -0500472 fmt::format("Error when collecting MFR_STATUS, error: {}", e.what())
Jim Wright3accffe2022-03-10 17:19:42 -0600473 .c_str());
Jim Wrightc91eed02022-07-22 11:27:06 -0500474 additionalData.emplace("ERROR", e.what());
Jim Wright3accffe2022-03-10 17:19:42 -0600475 }
476
477 // The status_word register has a summary bit to tell us if each page even
478 // needs to be checked
479 if (statusWord & status_word::VOUT_FAULT)
480 {
Jim Wright286bc702022-05-02 11:01:46 -0500481 constexpr size_t numberPages = 32;
Jim Wright3accffe2022-03-10 17:19:42 -0600482 for (size_t page = 0; page < numberPages; page++)
483 {
484 auto statusVout = pmbusInterface.insertPageNum(STATUS_VOUT, page);
Jim Wright2a054922022-04-13 09:39:27 -0500485 if (pmbusInterface.exists(statusVout, Type::Debug))
Jim Wright3accffe2022-03-10 17:19:42 -0600486 {
Jim Wright2a054922022-04-13 09:39:27 -0500487 uint8_t vout = pmbusInterface.read(statusVout, Type::Debug);
Jim Wright3accffe2022-03-10 17:19:42 -0600488
Jim Wright2a054922022-04-13 09:39:27 -0500489 if (vout)
490 {
Jim Wright213ffe92022-06-03 08:54:30 -0500491 // If any bits are on log them, though some are just
492 // warnings so they won't cause errors
Jim Wright2a054922022-04-13 09:39:27 -0500493 log<level::INFO>(
Jim Wright213ffe92022-06-03 08:54:30 -0500494 fmt::format("{}, value: {:#04x}", statusVout, vout)
Jim Wright2a054922022-04-13 09:39:27 -0500495 .c_str());
Jim Wright3accffe2022-03-10 17:19:42 -0600496
Jim Wright213ffe92022-06-03 08:54:30 -0500497 // Log errors if any non-warning bits on
498 if (vout & ~status_vout::WARNING_MASK)
Jim Wright2a054922022-04-13 09:39:27 -0500499 {
Jim Wright213ffe92022-06-03 08:54:30 -0500500 additionalData.emplace(
501 fmt::format("STATUS{}_VOUT", page),
502 fmt::format("{:#04x}", vout));
Jim Wright2a054922022-04-13 09:39:27 -0500503
Jim Wrightc91eed02022-07-22 11:27:06 -0500504 // Base the callouts on the first present vout failure
505 // found
506 if (message.empty() && (page < rails.size()) &&
507 isPresent(rails[page].presence))
Jim Wright213ffe92022-06-03 08:54:30 -0500508 {
Jim Wrightc91eed02022-07-22 11:27:06 -0500509 additionalData.emplace("RAIL_NAME",
510 rails[page].name);
Jim Wright213ffe92022-06-03 08:54:30 -0500511
512 // Use power supply error if set and 12v rail has
513 // failed, else use voltage error
514 message =
515 ((page == 0) && !powerSupplyError.empty())
516 ? powerSupplyError
517 : "xyz.openbmc_project.Power.Error.PowerSequencerVoltageFault";
518 }
519 }
Jim Wright2a054922022-04-13 09:39:27 -0500520 }
Jim Wright3accffe2022-03-10 17:19:42 -0600521 }
522 }
523 }
Jim Wright056bc192022-06-06 17:46:34 -0500524 // If no vout failure found, but power supply error is set, use power supply
525 // error
526 if (message.empty())
527 {
528 message = powerSupplyError;
529 }
Jim Wright3accffe2022-03-10 17:19:42 -0600530}
531
Jim Wright71a14132022-01-28 09:46:46 -0600532uint16_t UCD90320Monitor::readStatusWord()
533{
534 return pmbusInterface.read(STATUS_WORD, Type::Debug);
535}
536
Jim Wright213ffe92022-06-03 08:54:30 -0500537uint64_t UCD90320Monitor::readMFRStatus()
Jim Wright71a14132022-01-28 09:46:46 -0600538{
539 const std::string mfrStatus = "mfr_status";
540 return pmbusInterface.read(mfrStatus, Type::HwmonDeviceDebug);
541}
542
Jim Wright7945dd22021-04-06 16:55:15 -0500543} // namespace phosphor::power::sequencer