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 <sdbusplus/bus/match.hpp>
10 #include <sdeventplus/clock.hpp>
11 #include <sdeventplus/event.hpp>
12 #include <sdeventplus/utility/timer.hpp>
13 
14 namespace witherspoon
15 {
16 namespace power
17 {
18 namespace psu
19 {
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     witherspoon::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 Callback for inventory property changes
267      *
268      * Process change of Present property for power supply.
269      *
270      * @param[in]  msg - Data associated with Present change signal
271      *
272      */
273     void inventoryChanged(sdbusplus::message::message& msg);
274 
275     /**
276      * Updates the presence status by querying D-Bus
277      *
278      * The D-Bus inventory properties for this power supply will be read to
279      * determine if the power supply is present or not and update this
280      * objects present member variable to reflect current status.
281      */
282     void updatePresence();
283 
284     /**
285      * @brief Updates the poweredOn status by querying D-Bus
286      *
287      * The D-Bus property for the system power state will be read to
288      * determine if the system is powered on or not.
289      */
290     void updatePowerState();
291 
292     /**
293      * @brief Callback for power state property changes
294      *
295      * Process changes to the powered on stat property for the system.
296      *
297      * @param[in] msg - Data associated with the power state signal
298      */
299     void powerStateChanged(sdbusplus::message::message& msg);
300 
301     /**
302      * @brief Wrapper for PMBus::read() and adding metadata
303      *
304      * @param[out] nv - NamesValues instance to store cmd string and value
305      * @param[in] cmd - String for the command to read data from.
306      * @param[in] type - The type of file to read the command from.
307      */
308     void captureCmd(util::NamesValues& nv, const std::string& cmd,
309                     witherspoon::pmbus::Type type);
310 
311     /**
312      * @brief Checks for input voltage faults and logs error if needed.
313      *
314      * Check for voltage input under voltage fault (VIN_UV_FAULT) and/or
315      * input fault or warning (INPUT_FAULT), and logs appropriate error(s).
316      *
317      * @param[in] statusWord  - 2 byte STATUS_WORD value read from sysfs
318      */
319     void checkInputFault(const uint16_t statusWord);
320 
321     /**
322      * @brief Checks for power good negated or unit is off in wrong state
323      *
324      * @param[in] statusWord  - 2 byte STATUS_WORD value read from sysfs
325      */
326     void checkPGOrUnitOffFault(const uint16_t statusWord);
327 
328     /**
329      * @brief Checks for output current over current fault.
330      *
331      * IOUT_OC_FAULT is checked, if on, appropriate error is logged.
332      *
333      * @param[in] statusWord  - 2 byte STATUS_WORD value read from sysfs
334      */
335     void checkCurrentOutOverCurrentFault(const uint16_t statusWord);
336 
337     /**
338      * @brief Checks for output overvoltage fault.
339      *
340      * VOUT_OV_FAULT is checked, if on, appropriate error is logged.
341      *
342      * @param[in] statusWord  - 2 byte STATUS_WORD value read from sysfs
343      */
344     void checkOutputOvervoltageFault(const uint16_t statusWord);
345 
346     /**
347      * @brief Checks for a fan fault or warning condition.
348      *
349      * The high byte of STATUS_WORD is checked to see if the "FAN FAULT OR
350      * WARNING" bit is turned on. If it is on, log an error.
351      *
352      * @param[in] statusWord - 2 byte STATUS_WORD value read from sysfs
353      */
354     void checkFanFault(const uint16_t statusWord);
355 
356     /**
357      * @brief Checks for a temperature fault or warning condition.
358      *
359      * The low byte of STATUS_WORD is checked to see if the "TEMPERATURE
360      * FAULT OR WARNING" bit is turned on. If it is on, log an error,
361      * call out the power supply indicating the fault/warning condition.
362      *
363      * @parma[in] statusWord - 2 byte STATUS_WORD value read from sysfs
364      */
365     void checkTemperatureFault(const uint16_t statusWord);
366 
367     /**
368      * @brief Adds properties to the inventory.
369      *
370      * Reads the values from the device and writes them to the
371      * associated power supply D-Bus inventory object.
372      *
373      * This needs to be done on startup, and each time the presence
374      * state changes.
375      *
376      * Properties added:
377      * - Serial Number
378      * - Part Number
379      * - CCIN (Customer Card Identification Number) - added as the Model
380      * - Firmware version
381      */
382     void updateInventory();
383 
384     /**
385      * @brief Toggles the GPIO to sync power supply input history readings
386      *
387      * This GPIO is connected to all supplies.  This will clear the
388      * previous readings out of the supplies and restart them both at the
389      * same time zero and at record ID 0.  The supplies will return 0
390      * bytes of data for the input history command right after this until
391      * a new entry shows up.
392      *
393      * This will cause the code to delete all previous history data and
394      * start fresh.
395      */
396     void syncHistory();
397 
398     /**
399      * @brief Reads the most recent input history record from the power
400      *        supply and updates the average and maximum properties in
401      *        D-Bus if there is a new reading available.
402      *
403      * This will still run every time analyze() is called so code can
404      * post new data as soon as possible and the timestamp will more
405      * accurately reflect the correct time.
406      *
407      * D-Bus is only updated if there is a change and the oldest record
408      * will be pruned if the property already contains the max number of
409      * records.
410      */
411     void updateHistory();
412 };
413 
414 } // namespace psu
415 } // namespace power
416 } // namespace witherspoon
417