1 /**
2 * Copyright © 2017 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 #include "config.h"
17
18 #include "power_supply.hpp"
19
20 #include "elog-errors.hpp"
21 #include "gpio.hpp"
22 #include "names_values.hpp"
23 #include "pmbus.hpp"
24 #include "types.hpp"
25 #include "utility.hpp"
26
27 #include <org/open_power/Witherspoon/Fault/error.hpp>
28 #include <phosphor-logging/log.hpp>
29 #include <xyz/openbmc_project/Common/Device/error.hpp>
30
31 #include <functional>
32
33 namespace phosphor
34 {
35 namespace power
36 {
37 namespace psu
38 {
39
40 using namespace phosphor::logging;
41 using namespace sdbusplus::org::open_power::Witherspoon::Fault::Error;
42 using namespace sdbusplus::xyz::openbmc_project::Common::Device::Error;
43
PowerSupply(const std::string & name,size_t inst,const std::string & objpath,const std::string & invpath,sdbusplus::bus_t & bus,const sdeventplus::Event & e,std::chrono::seconds & t,std::chrono::seconds & p)44 PowerSupply::PowerSupply(const std::string& name, size_t inst,
45 const std::string& objpath, const std::string& invpath,
46 sdbusplus::bus_t& bus, const sdeventplus::Event& e,
47 std::chrono::seconds& t, std::chrono::seconds& p) :
48 Device(name, inst), monitorPath(objpath), pmbusIntf(objpath),
49 inventoryPath(INVENTORY_OBJ_PATH + invpath), bus(bus), presentInterval(p),
50 presentTimer(e, std::bind([this]() {
51 // The hwmon path may have changed.
52 pmbusIntf.findHwmonDir();
53 this->present = true;
54
55 // Sync the INPUT_HISTORY data for all PSs
56 syncHistory();
57
58 // Update the inventory for the new device
59 updateInventory();
60 })),
61 powerOnInterval(t),
__anon78e5afee0202() 62 powerOnTimer(e, std::bind([this]() { this->powerOn = true; }))
63 {
64 getAccessType();
65
66 using namespace sdbusplus::bus;
67 using namespace phosphor::pmbus;
68 std::uint16_t statusWord = 0;
69 try
70 {
71 // Read the 2 byte STATUS_WORD value to check for faults.
72 statusWord = pmbusIntf.read(STATUS_WORD, Type::Debug);
73 if (!((statusWord & status_word::INPUT_FAULT_WARN) ||
74 (statusWord & status_word::VIN_UV_FAULT)))
75 {
76 resolveError(inventoryPath,
77 std::string(PowerSupplyInputFault::errName));
78 }
79 }
80 catch (const ReadFailure& e)
81 {
82 log<level::INFO>("Unable to read the 2 byte STATUS_WORD value to check "
83 "for power-supply input faults.");
84 }
85 presentMatch = std::make_unique<match_t>(
86 bus, match::rules::propertiesChanged(inventoryPath, INVENTORY_IFACE),
__anon78e5afee0302(auto& msg) 87 [this](auto& msg) { this->inventoryChanged(msg); });
88 // Get initial presence state.
89 updatePresence();
90
91 // Write the SN, PN, etc to the inventory
92 updateInventory();
93
94 // Subscribe to power state changes
95 powerOnMatch = std::make_unique<match_t>(
96 bus, match::rules::propertiesChanged(POWER_OBJ_PATH, POWER_IFACE),
__anon78e5afee0402(auto& msg) 97 [this](auto& msg) { this->powerStateChanged(msg); });
98 // Get initial power state.
99 updatePowerState();
100 }
101
getAccessType()102 void PowerSupply::getAccessType()
103 {
104 using namespace phosphor::power::util;
105 fruJson = loadJSONFromFile(PSU_JSON_PATH);
106 if (fruJson == nullptr)
107 {
108 log<level::ERR>("InternalFailure when parsing the JSON file");
109 return;
110 }
111 inventoryPMBusAccessType = getPMBusAccessType(fruJson);
112 }
113
captureCmd(util::NamesValues & nv,const std::string & cmd,phosphor::pmbus::Type type)114 void PowerSupply::captureCmd(util::NamesValues& nv, const std::string& cmd,
115 phosphor::pmbus::Type type)
116 {
117 if (pmbusIntf.exists(cmd, type))
118 {
119 try
120 {
121 auto val = pmbusIntf.read(cmd, type);
122 nv.add(cmd, val);
123 }
124 catch (const std::exception& e)
125 {
126 log<level::INFO>("Unable to capture metadata",
127 entry("CMD=%s", cmd.c_str()));
128 }
129 }
130 }
131
analyze()132 void PowerSupply::analyze()
133 {
134 using namespace phosphor::pmbus;
135
136 try
137 {
138 if (present)
139 {
140 std::uint16_t statusWord = 0;
141
142 // Read the 2 byte STATUS_WORD value to check for faults.
143 statusWord = pmbusIntf.read(STATUS_WORD, Type::Debug);
144 readFail = 0;
145
146 checkInputFault(statusWord);
147
148 if (powerOn && (inputFault == 0) && !faultFound)
149 {
150 checkFanFault(statusWord);
151 checkTemperatureFault(statusWord);
152 checkOutputOvervoltageFault(statusWord);
153 checkCurrentOutOverCurrentFault(statusWord);
154 checkPGOrUnitOffFault(statusWord);
155 }
156
157 updateHistory();
158 }
159 }
160 catch (const ReadFailure& e)
161 {
162 if (readFail < FAULT_COUNT)
163 {
164 readFail++;
165 }
166
167 if (!readFailLogged && readFail >= FAULT_COUNT)
168 {
169 commit<ReadFailure>();
170 readFailLogged = true;
171 }
172 }
173
174 return;
175 }
176
inventoryChanged(sdbusplus::message_t & msg)177 void PowerSupply::inventoryChanged(sdbusplus::message_t& msg)
178 {
179 std::string msgSensor;
180 std::map<std::string, std::variant<uint32_t, bool>> msgData;
181 msg.read(msgSensor, msgData);
182
183 // Check if it was the Present property that changed.
184 auto valPropMap = msgData.find(PRESENT_PROP);
185 if (valPropMap != msgData.end())
186 {
187 if (std::get<bool>(valPropMap->second))
188 {
189 clearFaults();
190 presentTimer.restartOnce(presentInterval);
191 }
192 else
193 {
194 present = false;
195 presentTimer.setEnabled(false);
196
197 // Clear out the now outdated inventory properties
198 updateInventory();
199 }
200 }
201
202 return;
203 }
204
updatePresence()205 void PowerSupply::updatePresence()
206 {
207 // Use getProperty utility function to get presence status.
208 std::string service = "xyz.openbmc_project.Inventory.Manager";
209 util::getProperty(INVENTORY_IFACE, PRESENT_PROP, inventoryPath, service,
210 bus, this->present);
211 }
212
powerStateChanged(sdbusplus::message_t & msg)213 void PowerSupply::powerStateChanged(sdbusplus::message_t& msg)
214 {
215 int32_t state = 0;
216 std::string msgSensor;
217 std::map<std::string, std::variant<int32_t>> msgData;
218 msg.read(msgSensor, msgData);
219
220 // Check if it was the Present property that changed.
221 auto valPropMap = msgData.find("state");
222 if (valPropMap != msgData.end())
223 {
224 state = std::get<int32_t>(valPropMap->second);
225
226 // Power is on when state=1. Set the fault logged variables to false
227 // and start the power on timer when the state changes to 1.
228 if (state)
229 {
230 clearFaults();
231 powerOnTimer.restartOnce(powerOnInterval);
232 }
233 else
234 {
235 powerOnTimer.setEnabled(false);
236 powerOn = false;
237 }
238 }
239 }
240
updatePowerState()241 void PowerSupply::updatePowerState()
242 {
243 powerOn = util::isPoweredOn(bus);
244 }
245
checkInputFault(const uint16_t statusWord)246 void PowerSupply::checkInputFault(const uint16_t statusWord)
247 {
248 using namespace phosphor::pmbus;
249
250 if ((inputFault < FAULT_COUNT) &&
251 ((statusWord & status_word::INPUT_FAULT_WARN) ||
252 (statusWord & status_word::VIN_UV_FAULT)))
253 {
254 if (inputFault == 0)
255 {
256 log<level::INFO>("INPUT or VIN_UV fault",
257 entry("STATUS_WORD=0x%04X", statusWord));
258 }
259
260 inputFault++;
261 }
262 else
263 {
264 if ((inputFault > 0) && !(statusWord & status_word::INPUT_FAULT_WARN) &&
265 !(statusWord & status_word::VIN_UV_FAULT))
266 {
267 inputFault = 0;
268 faultFound = false;
269 // When an input fault occurs, the power supply cannot be on.
270 // However, the check for the case where the power supply should be
271 // on will stop when there is a fault found.
272 // Clear the powerOnFault when the inputFault is cleared to reset
273 // the powerOnFault de-glitching.
274 powerOnFault = 0;
275
276 log<level::INFO>("INPUT_FAULT_WARN cleared",
277 entry("POWERSUPPLY=%s", inventoryPath.c_str()));
278
279 resolveError(inventoryPath,
280 std::string(PowerSupplyInputFault::errName));
281
282 if (powerOn)
283 {
284 // The power supply will not be immediately powered on after
285 // the input power is restored.
286 powerOn = false;
287 // Start up the timer that will set the state to indicate we
288 // are ready for the powered on fault checks.
289 powerOnTimer.restartOnce(powerOnInterval);
290 }
291 }
292 }
293
294 if (!faultFound && (inputFault >= FAULT_COUNT))
295 {
296 // If the power is on, report the fault in an error log entry.
297 if (powerOn)
298 {
299 util::NamesValues nv;
300 nv.add("STATUS_WORD", statusWord);
301 captureCmd(nv, STATUS_INPUT, Type::Debug);
302
303 using metadata =
304 org::open_power::Witherspoon::Fault::PowerSupplyInputFault;
305
306 report<PowerSupplyInputFault>(
307 metadata::RAW_STATUS(nv.get().c_str()),
308 metadata::CALLOUT_INVENTORY_PATH(inventoryPath.c_str()));
309
310 faultFound = true;
311 }
312 }
313 }
314
checkPGOrUnitOffFault(const uint16_t statusWord)315 void PowerSupply::checkPGOrUnitOffFault(const uint16_t statusWord)
316 {
317 using namespace phosphor::pmbus;
318
319 if (powerOnFault < FAULT_COUNT)
320 {
321 // Check PG# and UNIT_IS_OFF
322 if ((statusWord & status_word::POWER_GOOD_NEGATED) ||
323 (statusWord & status_word::UNIT_IS_OFF))
324 {
325 log<level::INFO>("PGOOD or UNIT_IS_OFF bit bad",
326 entry("STATUS_WORD=0x%04X", statusWord));
327 powerOnFault++;
328 }
329 else
330 {
331 if (powerOnFault > 0)
332 {
333 log<level::INFO>("PGOOD and UNIT_IS_OFF bits good");
334 powerOnFault = 0;
335 }
336 }
337
338 if (!faultFound && (powerOnFault >= FAULT_COUNT))
339 {
340 faultFound = true;
341
342 util::NamesValues nv;
343 nv.add("STATUS_WORD", statusWord);
344 captureCmd(nv, STATUS_INPUT, Type::Debug);
345 auto status0Vout = pmbusIntf.insertPageNum(STATUS_VOUT, 0);
346 captureCmd(nv, status0Vout, Type::Debug);
347 captureCmd(nv, STATUS_IOUT, Type::Debug);
348 captureCmd(nv, STATUS_MFR, Type::Debug);
349
350 using metadata =
351 org::open_power::Witherspoon::Fault::PowerSupplyShouldBeOn;
352
353 // A power supply is OFF (or pgood low) but should be on.
354 report<PowerSupplyShouldBeOn>(
355 metadata::RAW_STATUS(nv.get().c_str()),
356 metadata::CALLOUT_INVENTORY_PATH(inventoryPath.c_str()));
357 }
358 }
359 }
360
checkCurrentOutOverCurrentFault(const uint16_t statusWord)361 void PowerSupply::checkCurrentOutOverCurrentFault(const uint16_t statusWord)
362 {
363 using namespace phosphor::pmbus;
364
365 if (outputOCFault < FAULT_COUNT)
366 {
367 // Check for an output overcurrent fault.
368 if ((statusWord & status_word::IOUT_OC_FAULT))
369 {
370 outputOCFault++;
371 }
372 else
373 {
374 if (outputOCFault > 0)
375 {
376 outputOCFault = 0;
377 }
378 }
379
380 if (!faultFound && (outputOCFault >= FAULT_COUNT))
381 {
382 util::NamesValues nv;
383 nv.add("STATUS_WORD", statusWord);
384 captureCmd(nv, STATUS_INPUT, Type::Debug);
385 auto status0Vout = pmbusIntf.insertPageNum(STATUS_VOUT, 0);
386 captureCmd(nv, status0Vout, Type::Debug);
387 captureCmd(nv, STATUS_IOUT, Type::Debug);
388 captureCmd(nv, STATUS_MFR, Type::Debug);
389
390 using metadata = org::open_power::Witherspoon::Fault::
391 PowerSupplyOutputOvercurrent;
392
393 report<PowerSupplyOutputOvercurrent>(
394 metadata::RAW_STATUS(nv.get().c_str()),
395 metadata::CALLOUT_INVENTORY_PATH(inventoryPath.c_str()));
396
397 faultFound = true;
398 }
399 }
400 }
401
checkOutputOvervoltageFault(const uint16_t statusWord)402 void PowerSupply::checkOutputOvervoltageFault(const uint16_t statusWord)
403 {
404 using namespace phosphor::pmbus;
405
406 if (outputOVFault < FAULT_COUNT)
407 {
408 // Check for an output overvoltage fault.
409 if (statusWord & status_word::VOUT_OV_FAULT)
410 {
411 outputOVFault++;
412 }
413 else
414 {
415 if (outputOVFault > 0)
416 {
417 outputOVFault = 0;
418 }
419 }
420
421 if (!faultFound && (outputOVFault >= FAULT_COUNT))
422 {
423 util::NamesValues nv;
424 nv.add("STATUS_WORD", statusWord);
425 captureCmd(nv, STATUS_INPUT, Type::Debug);
426 auto status0Vout = pmbusIntf.insertPageNum(STATUS_VOUT, 0);
427 captureCmd(nv, status0Vout, Type::Debug);
428 captureCmd(nv, STATUS_IOUT, Type::Debug);
429 captureCmd(nv, STATUS_MFR, Type::Debug);
430
431 using metadata = org::open_power::Witherspoon::Fault::
432 PowerSupplyOutputOvervoltage;
433
434 report<PowerSupplyOutputOvervoltage>(
435 metadata::RAW_STATUS(nv.get().c_str()),
436 metadata::CALLOUT_INVENTORY_PATH(inventoryPath.c_str()));
437
438 faultFound = true;
439 }
440 }
441 }
442
checkFanFault(const uint16_t statusWord)443 void PowerSupply::checkFanFault(const uint16_t statusWord)
444 {
445 using namespace phosphor::pmbus;
446
447 if (fanFault < FAULT_COUNT)
448 {
449 // Check for a fan fault or warning condition
450 if (statusWord & status_word::FAN_FAULT)
451 {
452 fanFault++;
453 }
454 else
455 {
456 if (fanFault > 0)
457 {
458 fanFault = 0;
459 }
460 }
461
462 if (!faultFound && (fanFault >= FAULT_COUNT))
463 {
464 util::NamesValues nv;
465 nv.add("STATUS_WORD", statusWord);
466 captureCmd(nv, STATUS_MFR, Type::Debug);
467 captureCmd(nv, STATUS_TEMPERATURE, Type::Debug);
468 captureCmd(nv, STATUS_FANS_1_2, Type::Debug);
469
470 using metadata =
471 org::open_power::Witherspoon::Fault::PowerSupplyFanFault;
472
473 report<PowerSupplyFanFault>(
474 metadata::RAW_STATUS(nv.get().c_str()),
475 metadata::CALLOUT_INVENTORY_PATH(inventoryPath.c_str()));
476
477 faultFound = true;
478 }
479 }
480 }
481
checkTemperatureFault(const uint16_t statusWord)482 void PowerSupply::checkTemperatureFault(const uint16_t statusWord)
483 {
484 using namespace phosphor::pmbus;
485
486 // Due to how the PMBus core device driver sends a clear faults command
487 // the bit in STATUS_WORD will likely be cleared when we attempt to examine
488 // it for a Thermal Fault or Warning. So, check the STATUS_WORD and the
489 // STATUS_TEMPERATURE bits. If either indicates a fault, proceed with
490 // logging the over-temperature condition.
491 std::uint8_t statusTemperature = 0;
492 statusTemperature = pmbusIntf.read(STATUS_TEMPERATURE, Type::Debug);
493 if (temperatureFault < FAULT_COUNT)
494 {
495 if ((statusWord & status_word::TEMPERATURE_FAULT_WARN) ||
496 (statusTemperature & status_temperature::OT_FAULT))
497 {
498 temperatureFault++;
499 }
500 else
501 {
502 if (temperatureFault > 0)
503 {
504 temperatureFault = 0;
505 }
506 }
507
508 if (!faultFound && (temperatureFault >= FAULT_COUNT))
509 {
510 // The power supply has had an over-temperature condition.
511 // This may not result in a shutdown if experienced for a short
512 // duration.
513 // This should not occur under normal conditions.
514 // The power supply may be faulty, or the paired supply may be
515 // putting out less current.
516 // Capture command responses with potentially relevant information,
517 // and call out the power supply reporting the condition.
518 util::NamesValues nv;
519 nv.add("STATUS_WORD", statusWord);
520 captureCmd(nv, STATUS_MFR, Type::Debug);
521 captureCmd(nv, STATUS_IOUT, Type::Debug);
522 nv.add("STATUS_TEMPERATURE", statusTemperature);
523 captureCmd(nv, STATUS_FANS_1_2, Type::Debug);
524
525 using metadata = org::open_power::Witherspoon::Fault::
526 PowerSupplyTemperatureFault;
527
528 report<PowerSupplyTemperatureFault>(
529 metadata::RAW_STATUS(nv.get().c_str()),
530 metadata::CALLOUT_INVENTORY_PATH(inventoryPath.c_str()));
531
532 faultFound = true;
533 }
534 }
535 }
536
clearFaults()537 void PowerSupply::clearFaults()
538 {
539 readFail = 0;
540 readFailLogged = false;
541 inputFault = 0;
542 powerOnFault = 0;
543 outputOCFault = 0;
544 outputOVFault = 0;
545 fanFault = 0;
546 temperatureFault = 0;
547 faultFound = false;
548
549 return;
550 }
551
resolveError(const std::string & callout,const std::string & message)552 void PowerSupply::resolveError(const std::string& callout,
553 const std::string& message)
554 {
555 using EndpointList = std::vector<std::string>;
556
557 try
558 {
559 auto path = callout + "/fault";
560 // Get the service name from the mapper for the fault callout
561 auto service = util::getService(path, ASSOCIATION_IFACE, bus);
562
563 // Use getProperty utility function to get log entries (endpoints)
564 EndpointList logEntries;
565 util::getProperty(ASSOCIATION_IFACE, ENDPOINTS_PROP, path, service, bus,
566 logEntries);
567
568 // It is possible that all such entries for this callout have since
569 // been deleted.
570 if (logEntries.empty())
571 {
572 return;
573 }
574
575 auto logEntryService =
576 util::getService(logEntries[0], LOGGING_IFACE, bus);
577 if (logEntryService.empty())
578 {
579 return;
580 }
581
582 // go through each log entry that matches this callout path
583 std::string logMessage;
584 for (const auto& logEntry : logEntries)
585 {
586 // Check to see if this logEntry has a message that matches.
587 util::getProperty(LOGGING_IFACE, MESSAGE_PROP, logEntry,
588 logEntryService, bus, logMessage);
589
590 if (message == logMessage)
591 {
592 // Log entry matches call out and message, set Resolved to true
593 bool resolved = true;
594 util::setProperty(LOGGING_IFACE, RESOLVED_PROP, logEntry,
595 logEntryService, bus, resolved);
596 }
597 }
598 }
599 catch (const std::exception& e)
600 {
601 log<level::INFO>("Failed to resolve error",
602 entry("CALLOUT=%s", callout.c_str()),
603 entry("ERROR=%s", message.c_str()));
604 }
605 }
606
updateInventory()607 void PowerSupply::updateInventory()
608 {
609 using namespace phosphor::pmbus;
610 using namespace sdbusplus::message;
611
612 // Build the object map and send it to the inventory
613 using Properties = std::map<std::string, std::variant<std::string, bool>>;
614 using Interfaces = std::map<std::string, Properties>;
615 using Object = std::map<object_path, Interfaces>;
616 Properties assetProps;
617 Properties operProps;
618 Interfaces interfaces;
619 Object object;
620
621 // If any of these accesses fail, the fields will just be
622 // blank in the inventory. Leave logging ReadFailure errors
623 // to analyze() as it runs continuously and will most
624 // likely hit and threshold them first anyway. The
625 // readString() function will do the tracing of the failing
626 // path so this code doesn't need to.
627 for (const auto& fru : fruJson.at("fruConfigs"))
628 {
629 if (fru.at("interface") == ASSET_IFACE)
630 {
631 try
632 {
633 assetProps.emplace(
634 fru.at("propertyName"),
635 present ? pmbusIntf.readString(fru.at("fileName"),
636 inventoryPMBusAccessType)
637 : "");
638 }
639 catch (const ReadFailure& e)
640 {}
641 }
642 }
643
644 operProps.emplace(FUNCTIONAL_PROP, present);
645 interfaces.emplace(ASSET_IFACE, std::move(assetProps));
646 interfaces.emplace(OPERATIONAL_STATE_IFACE, std::move(operProps));
647
648 // For Notify(), just send the relative path of the inventory
649 // object so remove the INVENTORY_OBJ_PATH prefix
650 auto path = inventoryPath.substr(strlen(INVENTORY_OBJ_PATH));
651
652 object.emplace(path, std::move(interfaces));
653
654 try
655 {
656 auto service =
657 util::getService(INVENTORY_OBJ_PATH, INVENTORY_MGR_IFACE, bus);
658
659 if (service.empty())
660 {
661 log<level::ERR>("Unable to get inventory manager service");
662 return;
663 }
664
665 auto method = bus.new_method_call(service.c_str(), INVENTORY_OBJ_PATH,
666 INVENTORY_MGR_IFACE, "Notify");
667
668 method.append(std::move(object));
669
670 auto reply = bus.call(method);
671 }
672 catch (const std::exception& e)
673 {
674 log<level::ERR>(e.what(), entry("PATH=%s", inventoryPath.c_str()));
675 }
676 }
677
syncHistory()678 void PowerSupply::syncHistory()
679 {
680 using namespace phosphor::gpio;
681
682 if (syncGPIODevPath.empty())
683 {
684 // Sync not implemented
685 return;
686 }
687
688 GPIO gpio{syncGPIODevPath, static_cast<gpioNum_t>(syncGPIONumber),
689 Direction::output};
690
691 try
692 {
693 gpio.set(Value::low);
694
695 std::this_thread::sleep_for(std::chrono::milliseconds{5});
696
697 gpio.set(Value::high);
698
699 recordManager->clear();
700 }
701 catch (const std::exception& e)
702 {
703 // Do nothing. There would already be a journal entry.
704 }
705 }
706
enableHistory(const std::string & objectPath,size_t numRecords,const std::string & syncGPIOPath,size_t syncGPIONum)707 void PowerSupply::enableHistory(
708 const std::string& objectPath, size_t numRecords,
709 const std::string& syncGPIOPath, size_t syncGPIONum)
710 {
711 historyObjectPath = objectPath;
712 syncGPIODevPath = syncGPIOPath;
713 syncGPIONumber = syncGPIONum;
714
715 recordManager = std::make_unique<history::RecordManager>(numRecords);
716
717 auto avgPath = historyObjectPath + '/' + history::Average::name;
718 auto maxPath = historyObjectPath + '/' + history::Maximum::name;
719
720 average = std::make_unique<history::Average>(bus, avgPath);
721
722 maximum = std::make_unique<history::Maximum>(bus, maxPath);
723 }
724
updateHistory()725 void PowerSupply::updateHistory()
726 {
727 if (!recordManager)
728 {
729 // Not enabled
730 return;
731 }
732
733 // Read just the most recent average/max record
734 auto data =
735 pmbusIntf.readBinary(INPUT_HISTORY, pmbus::Type::HwmonDeviceDebug,
736 history::RecordManager::RAW_RECORD_SIZE);
737
738 // Update D-Bus only if something changed (a new record ID, or cleared out)
739 auto changed = recordManager->add(data);
740 if (changed)
741 {
742 average->values(recordManager->getAverageRecords());
743 maximum->values(recordManager->getMaximumRecords());
744 }
745 }
746
747 } // namespace psu
748 } // namespace power
749 } // namespace phosphor
750