1 #pragma once
2 #include "average.hpp"
3 #include "device.hpp"
4 #include "maximum.hpp"
5 #include "names_values.hpp"
6 #include "pmbus.hpp"
7 #include "record_manager.hpp"
8 
9 #include <nlohmann/json.hpp>
10 #include <sdbusplus/bus/match.hpp>
11 #include <sdeventplus/clock.hpp>
12 #include <sdeventplus/event.hpp>
13 #include <sdeventplus/utility/timer.hpp>
14 
15 namespace phosphor
16 {
17 namespace power
18 {
19 namespace psu
20 {
21 namespace sdbusRule = sdbusplus::bus::match::rules;
22 
23 constexpr auto FAULT_COUNT = 3;
24 
25 /**
26  * @class PowerSupply
27  * Represents a PMBus power supply device.
28  */
29 class PowerSupply : public Device
30 {
31   public:
32     PowerSupply() = delete;
33     PowerSupply(const PowerSupply&) = delete;
34     PowerSupply(PowerSupply&&) = default;
35     PowerSupply& operator=(const PowerSupply&) = default;
36     PowerSupply& operator=(PowerSupply&&) = default;
37     ~PowerSupply() = default;
38 
39     /**
40      * Constructor
41      *
42      * @param[in] name - the device name
43      * @param[in] inst - the device instance
44      * @param[in] objpath - the path to monitor
45      * @param[in] invpath - the inventory path to use
46      * @param[in] bus - D-Bus bus object
47      * @param[in] e - event object
48      * @param[in] t - time to allow power supply to assert PG#
49      * @param[in] p - time to allow power supply presence state to
50      *                settle/deglitch and allow for application of power
51      *                prior to fault checking
52      */
53     PowerSupply(const std::string& name, size_t inst,
54                 const std::string& objpath, const std::string& invpath,
55                 sdbusplus::bus::bus& bus, const sdeventplus::Event& e,
56                 std::chrono::seconds& t, std::chrono::seconds& p);
57 
58     /**
59      * Power supply specific function to analyze for faults/errors.
60      *
61      * Various PMBus status bits will be checked for fault conditions.
62      * If a certain fault bits are on, the appropriate error will be
63      * committed.
64      */
65     void analyze() override;
66 
67     /**
68      * Write PMBus CLEAR_FAULTS
69      *
70      * This function will be called in various situations in order to clear
71      * any fault status bits that may have been set, in order to start over
72      * with a clean state. Presence changes and power state changes will
73      * want to clear any faults logged.
74      */
75     void clearFaults() override;
76 
77     /**
78      * Mark error for specified callout and message as resolved.
79      *
80      * @param[in] callout - The callout to be resolved (inventory path)
81      * @parma[in] message - The message for the fault to be resolved
82      */
83     void resolveError(const std::string& callout, const std::string& message);
84 
85     /**
86      * Enables making the input power history available on D-Bus
87      *
88      * @param[in] objectPath - the D-Bus object path to use
89      * @param[in] maxRecords - the number of history records to keep
90      * @param[in] syncGPIOPath - The gpiochip device path to use for
91      *                           sending the sync command
92      * @paramp[in] syncGPIONum - the GPIO number for the sync command
93      */
94     void enableHistory(const std::string& objectPath, size_t numRecords,
95                        const std::string& syncGPIOPath, size_t syncGPIONum);
96 
97   private:
98     /**
99      * The path to use for reading various PMBus bits/words.
100      */
101     std::string monitorPath;
102 
103     /**
104      * @brief Pointer to the PMBus interface
105      *
106      * Used to read out of or write to the /sysfs tree(s) containing files
107      * that a device driver monitors the PMBus interface to the power
108      * supplies.
109      */
110     phosphor::pmbus::PMBus pmbusIntf;
111 
112     /**
113      * @brief D-Bus path to use for this power supply's inventory status.
114      */
115     std::string inventoryPath;
116 
117     /** @brief Connection for sdbusplus bus */
118     sdbusplus::bus::bus& bus;
119 
120     /** @brief True if the power supply is present. */
121     bool present = false;
122 
123     /** @brief Used to subscribe to D-Bus property changes for Present */
124     std::unique_ptr<sdbusplus::bus::match_t> presentMatch;
125 
126     /**
127      * @brief Interval for setting present to true.
128      *
129      * The amount of time to wait from not present to present change before
130      * updating the internal present indicator. Allows person servicing
131      * the power supply some time to plug in the cable.
132      */
133     std::chrono::seconds presentInterval;
134 
135     /**
136      * @brief Timer used to delay setting the internal present state.
137      *
138      * The timer used to do the callback after the present property has
139      * changed.
140      */
141     sdeventplus::utility::Timer<sdeventplus::ClockId::Monotonic> presentTimer;
142 
143     /** @brief True if a fault has already been found and not cleared */
144     bool faultFound = false;
145 
146     /** @brief True if the power is on. */
147     bool powerOn = false;
148 
149     /**
150      * @brief Equal to FAULT_COUNT if power on fault has been
151      * detected.
152      */
153     size_t powerOnFault = 0;
154 
155     /**
156      * @brief Interval to setting powerOn to true.
157      *
158      * The amount of time to wait from power state on to setting the
159      * internal powerOn state to true. The amount of time the power supply
160      * is allowed to delay setting DGood/PG#.
161      */
162     std::chrono::seconds powerOnInterval;
163 
164     /**
165      * @brief Timer used to delay setting the internal powerOn state.
166      *
167      * The timer used to do the callback after the power state has been on
168      * long enough.
169      */
170     sdeventplus::utility::Timer<sdeventplus::ClockId::Monotonic> powerOnTimer;
171 
172     /** @brief Used to subscribe to D-Bus power on state changes */
173     std::unique_ptr<sdbusplus::bus::match_t> powerOnMatch;
174 
175     /** @brief Indicates that a read failure has occurred.
176      *
177      * @details This will be incremented each time a read failure is
178      *          encountered. If it is incremented to FAULT_COUNT, an error
179      *          will be logged.
180      */
181     size_t readFail = 0;
182 
183     /** @brief Has a PMBus read failure already been logged? */
184     bool readFailLogged = false;
185 
186     /**
187      * @brief Indicates an input fault or warning if equal to FAULT_COUNT.
188      *
189      * @details This is the "INPUT FAULT OR WARNING" bit in the high byte,
190      *          or the VIN_UV_FAULT bit in the low byte in the STATUS_WORD
191      *          command response. If either of those bits are on, this will
192      *          be incremented.
193      */
194     size_t inputFault = 0;
195 
196     /**
197      * @brief Indicates output over current fault if equal to FAULT_COUNT
198      *
199      * @details This is incremented when the "IOUT_OC_FAULT" bit in the low
200      *          byte from the STATUS_WORD command response is on.
201      */
202     size_t outputOCFault = 0;
203 
204     /**
205      * @brief Indicates output overvoltage fault if equal to FAULT_COUNT.
206      *
207      * @details This is incremented when the "VOUT_OV_FAULT" bit in the
208      *          STATUS_WORD command response is on.
209      */
210     size_t outputOVFault = 0;
211 
212     /**
213      * @brief Indicates a fan fault or warning condition was detected if
214      *        equal to FAULT_COUNT.
215      *
216      * @details This is incremented when the 'FAN_FAULT' bit in the
217      *          STATUS_WORD command response is on.
218      */
219     size_t fanFault = 0;
220 
221     /**
222      * @brief Indicates a temperature fault or warn condition was detected
223      *        if equal to FAULT_COUNT.
224      *
225      * @details This is incremented when the 'TEMPERATURE_FAULT_WARN' bit
226      *          in the STATUS_WORD command response is on, or if the
227      *          'OT_FAULT' bit in the STATUS_TEMPERATURE command response
228      *          is on.
229      */
230     size_t temperatureFault = 0;
231 
232     /**
233      * @brief Class that manages the input power history records.
234      */
235     std::unique_ptr<history::RecordManager> recordManager;
236 
237     /**
238      * @brief The D-Bus object for the average input power history
239      */
240     std::unique_ptr<history::Average> average;
241 
242     /**
243      * @brief The D-Bus object for the maximum input power history
244      */
245     std::unique_ptr<history::Maximum> maximum;
246 
247     /**
248      * @brief The base D-Bus object path to use for the average
249      *        and maximum objects.
250      */
251     std::string historyObjectPath;
252 
253     /**
254      * @brief The GPIO device path to use for sending the 'sync'
255      *        command to the PS.
256      */
257     std::string syncGPIODevPath;
258 
259     /**
260      * @brief The GPIO number to use for sending the 'sync'
261      *        command to the PS.
262      */
263     size_t syncGPIONumber = 0;
264 
265     /**
266      * @brief The type of the power supply inventory pmbus access.
267      */
268     phosphor::pmbus::Type inventoryPMBusAccessType =
269         phosphor::pmbus::Type::Base;
270 
271     /**
272      * @brief The JSON from the parsed power supply FRU JSON File.
273      */
274     nlohmann::json fruJson;
275 
276     /**
277      * @brief get the power supply access type from the JSON file.
278      *
279      */
280     void getAccessType();
281 
282     /**
283      * @brief Callback for inventory property changes
284      *
285      * Process change of Present property for power supply.
286      *
287      * @param[in]  msg - Data associated with Present change signal
288      *
289      */
290     void inventoryChanged(sdbusplus::message::message& msg);
291 
292     /**
293      * Updates the presence status by querying D-Bus
294      *
295      * The D-Bus inventory properties for this power supply will be read to
296      * determine if the power supply is present or not and update this
297      * objects present member variable to reflect current status.
298      */
299     void updatePresence();
300 
301     /**
302      * @brief Updates the poweredOn status by querying D-Bus
303      *
304      * The D-Bus property for the system power state will be read to
305      * determine if the system is powered on or not.
306      */
307     void updatePowerState();
308 
309     /**
310      * @brief Callback for power state property changes
311      *
312      * Process changes to the powered on stat property for the system.
313      *
314      * @param[in] msg - Data associated with the power state signal
315      */
316     void powerStateChanged(sdbusplus::message::message& msg);
317 
318     /**
319      * @brief Wrapper for PMBus::read() and adding metadata
320      *
321      * @param[out] nv - NamesValues instance to store cmd string and value
322      * @param[in] cmd - String for the command to read data from.
323      * @param[in] type - The type of file to read the command from.
324      */
325     void captureCmd(util::NamesValues& nv, const std::string& cmd,
326                     phosphor::pmbus::Type type);
327 
328     /**
329      * @brief Checks for input voltage faults and logs error if needed.
330      *
331      * Check for voltage input under voltage fault (VIN_UV_FAULT) and/or
332      * input fault or warning (INPUT_FAULT), and logs appropriate error(s).
333      *
334      * @param[in] statusWord  - 2 byte STATUS_WORD value read from sysfs
335      */
336     void checkInputFault(const uint16_t statusWord);
337 
338     /**
339      * @brief Checks for power good negated or unit is off in wrong state
340      *
341      * @param[in] statusWord  - 2 byte STATUS_WORD value read from sysfs
342      */
343     void checkPGOrUnitOffFault(const uint16_t statusWord);
344 
345     /**
346      * @brief Checks for output current over current fault.
347      *
348      * IOUT_OC_FAULT is checked, if on, appropriate error is logged.
349      *
350      * @param[in] statusWord  - 2 byte STATUS_WORD value read from sysfs
351      */
352     void checkCurrentOutOverCurrentFault(const uint16_t statusWord);
353 
354     /**
355      * @brief Checks for output overvoltage fault.
356      *
357      * VOUT_OV_FAULT is checked, if on, appropriate error is logged.
358      *
359      * @param[in] statusWord  - 2 byte STATUS_WORD value read from sysfs
360      */
361     void checkOutputOvervoltageFault(const uint16_t statusWord);
362 
363     /**
364      * @brief Checks for a fan fault or warning condition.
365      *
366      * The high byte of STATUS_WORD is checked to see if the "FAN FAULT OR
367      * WARNING" bit is turned on. If it is on, log an error.
368      *
369      * @param[in] statusWord - 2 byte STATUS_WORD value read from sysfs
370      */
371     void checkFanFault(const uint16_t statusWord);
372 
373     /**
374      * @brief Checks for a temperature fault or warning condition.
375      *
376      * The low byte of STATUS_WORD is checked to see if the "TEMPERATURE
377      * FAULT OR WARNING" bit is turned on. If it is on, log an error,
378      * call out the power supply indicating the fault/warning condition.
379      *
380      * @parma[in] statusWord - 2 byte STATUS_WORD value read from sysfs
381      */
382     void checkTemperatureFault(const uint16_t statusWord);
383 
384     /**
385      * @brief Adds properties to the inventory.
386      *
387      * Reads the values from the device and writes them to the
388      * associated power supply D-Bus inventory object.
389      *
390      * This needs to be done on startup, and each time the presence
391      * state changes.
392      *
393      * Properties added:
394      * - Serial Number
395      * - Part Number
396      * - Manufacturer
397      * - Model
398      */
399     void updateInventory();
400 
401     /**
402      * @brief Toggles the GPIO to sync power supply input history readings
403      *
404      * This GPIO is connected to all supplies.  This will clear the
405      * previous readings out of the supplies and restart them both at the
406      * same time zero and at record ID 0.  The supplies will return 0
407      * bytes of data for the input history command right after this until
408      * a new entry shows up.
409      *
410      * This will cause the code to delete all previous history data and
411      * start fresh.
412      */
413     void syncHistory();
414 
415     /**
416      * @brief Reads the most recent input history record from the power
417      *        supply and updates the average and maximum properties in
418      *        D-Bus if there is a new reading available.
419      *
420      * This will still run every time analyze() is called so code can
421      * post new data as soon as possible and the timestamp will more
422      * accurately reflect the correct time.
423      *
424      * D-Bus is only updated if there is a change and the oldest record
425      * will be pruned if the property already contains the max number of
426      * records.
427      */
428     void updateHistory();
429 };
430 
431 } // namespace psu
432 } // namespace power
433 } // namespace phosphor
434