1 #pragma once
2 
3 #include "pmbus.hpp"
4 #include "types.hpp"
5 #include "util.hpp"
6 #include "utility.hpp"
7 
8 #include <gpiod.hpp>
9 #include <sdbusplus/bus/match.hpp>
10 
11 #include <filesystem>
12 #include <stdexcept>
13 
14 namespace phosphor::power::psu
15 {
16 
17 #if IBM_VPD
18 // PMBus device driver "file name" to read for CCIN value.
19 constexpr auto CCIN = "ccin";
20 constexpr auto PART_NUMBER = "part_number";
21 constexpr auto FRU_NUMBER = "fru";
22 constexpr auto SERIAL_HEADER = "header";
23 constexpr auto SERIAL_NUMBER = "serial_number";
24 constexpr auto FW_VERSION = "fw_version";
25 
26 // The D-Bus property name to update with the CCIN value.
27 constexpr auto MODEL_PROP = "Model";
28 constexpr auto PN_PROP = "PartNumber";
29 constexpr auto SN_PROP = "SerialNumber";
30 constexpr auto VERSION_PROP = "Version";
31 
32 // ipzVPD Keyword sizes
33 static constexpr auto FL_KW_SIZE = 20;
34 #endif
35 
36 constexpr auto LOG_LIMIT = 3;
37 
38 /**
39  * @class PowerSupply
40  * Represents a PMBus power supply device.
41  */
42 class PowerSupply
43 {
44   public:
45     PowerSupply() = delete;
46     PowerSupply(const PowerSupply&) = delete;
47     PowerSupply(PowerSupply&&) = delete;
48     PowerSupply& operator=(const PowerSupply&) = delete;
49     PowerSupply& operator=(PowerSupply&&) = delete;
50     ~PowerSupply() = default;
51 
52     /**
53      * @param[in] invpath - String for inventory path to use
54      * @param[in] i2cbus - The bus number this power supply is on
55      * @param[in] i2caddr - The 16-bit I2C address of the power supply
56      * @param[in] gpioLineName - The gpio-line-name to read for presence. See
57      * https://github.com/openbmc/docs/blob/master/designs/device-tree-gpio-naming.md
58      */
59     PowerSupply(sdbusplus::bus::bus& bus, const std::string& invpath,
60                 std::uint8_t i2cbus, const std::uint16_t i2caddr,
61                 const std::string& gpioLineName);
62 
63     phosphor::pmbus::PMBusBase& getPMBus()
64     {
65         return *pmbusIntf;
66     }
67 
68     GPIOInterface* getPresenceGPIO()
69     {
70         return presenceGPIO.get();
71     }
72 
73     /**
74      * Power supply specific function to analyze for faults/errors.
75      *
76      * Various PMBus status bits will be checked for fault conditions.
77      * If a certain fault bits are on, the appropriate error will be
78      * committed.
79      */
80     void analyze();
81 
82     /**
83      * Write PMBus ON_OFF_CONFIG
84      *
85      * This function will be called to cause the PMBus device driver to send the
86      * ON_OFF_CONFIG command. Takes one byte of data.
87      *
88      * @param[in] data - The ON_OFF_CONFIG data byte mask.
89      */
90     void onOffConfig(uint8_t data);
91 
92     /**
93      * Write PMBus CLEAR_FAULTS
94      *
95      * This function will be called in various situations in order to clear
96      * any fault status bits that may have been set, in order to start over
97      * with a clean state. Presence changes and power state changes will
98      * want to clear any faults logged.
99      */
100     void clearFaults();
101 
102     /**
103      * @brief Adds properties to the inventory.
104      *
105      * Reads the values from the device and writes them to the
106      * associated power supply D-Bus inventory object.
107      *
108      * This needs to be done on startup, and each time the presence
109      * state changes.
110      *
111      * Properties added:
112      * - Serial Number
113      * - Part Number
114      * - CCIN (Customer Card Identification Number) - added as the Model
115      * - Firmware version
116      */
117     void updateInventory();
118 
119     /**
120      * @brief Accessor function to indicate present status
121      */
122     bool isPresent() const
123     {
124         return present;
125     }
126 
127     /**
128      * @brief Returns the last read value from STATUS_WORD.
129      */
130     uint64_t getStatusWord() const
131     {
132         return statusWord;
133     }
134 
135     /**
136      * @brief Returns the last read value from STATUS_MFR.
137      */
138     uint64_t getMFRFault() const
139     {
140         return statusMFR;
141     }
142 
143     /**
144      * @brief Returns true if a fault was found.
145      */
146     bool isFaulted() const
147     {
148         return (faultFound || hasCommFault());
149     }
150 
151     /**
152      * @brief Return whether a fault has been logged for this power supply
153      */
154     bool isFaultLogged() const
155     {
156         return faultLogged;
157     }
158 
159     /**
160      * @brief Called when a fault for this power supply has been logged.
161      */
162     void setFaultLogged()
163     {
164         faultLogged = true;
165     }
166 
167     /**
168      * @brief Returns true if INPUT fault occurred.
169      */
170     bool hasInputFault() const
171     {
172         return inputFault;
173     }
174 
175     /**
176      * @brief Returns true if MFRSPECIFIC occurred.
177      */
178     bool hasMFRFault() const
179     {
180         return mfrFault;
181     }
182 
183     /**
184      * @brief Returns true if VIN_UV_FAULT occurred.
185      */
186     bool hasVINUVFault() const
187     {
188         return vinUVFault;
189     }
190 
191     /**
192      * @brief Returns the device path
193      *
194      * This can be used for error call outs.
195      * Example: /sys/bus/i2c/devices/3-0068
196      */
197     const std::string getDevicePath() const
198     {
199         return pmbusIntf->path();
200     }
201 
202     /**
203      * @brief Returns this power supplies inventory path.
204      *
205      * This can be used for error call outs.
206      * Example:
207      * /xyz/openbmc_project/inventory/system/chassis/motherboard/powersupply1
208      */
209     const std::string& getInventoryPath() const
210     {
211         return inventoryPath;
212     }
213 
214     /**
215      * @brief Returns the firmware revision version read from the power supply
216      */
217     const std::string& getFWVersion() const
218     {
219         return fwVersion;
220     }
221 
222     /**
223      * @brief Returns the model name of the power supply
224      */
225     const std::string& getModelName() const
226     {
227         return modelName;
228     }
229 
230     /** @brief Returns true if the number of failed reads exceeds limit
231      * TODO: or CML bit on.
232      */
233     bool hasCommFault() const
234     {
235         return readFail >= LOG_LIMIT;
236     }
237 
238   private:
239     /** @brief systemd bus member */
240     sdbusplus::bus::bus& bus;
241 
242     /** @brief Will be updated to the latest/lastvalue read from STATUS_WORD.*/
243     uint64_t statusWord = 0;
244 
245     /** @brief Will be updated to the latest/lastvalue read from STATUS_MFR.*/
246     uint64_t statusMFR = 0;
247 
248     /** @brief True if a fault has already been found and not cleared */
249     bool faultFound = false;
250 
251     /** @brief True if an error for a fault has already been logged. */
252     bool faultLogged = false;
253 
254     /** @brief True if bit 5 of STATUS_WORD high byte is on. */
255     bool inputFault = false;
256 
257     /** @brief True if bit 4 of STATUS_WORD high byte is on. */
258     bool mfrFault = false;
259 
260     /** @brief True if bit 3 of STATUS_WORD low byte is on. */
261     bool vinUVFault = false;
262 
263     /** @brief Count of the number of read failures. */
264     size_t readFail = 0;
265 
266     /**
267      * @brief D-Bus path to use for this power supply's inventory status.
268      **/
269     std::string inventoryPath;
270 
271     /**
272      * @brief The libgpiod object for monitoring PSU presence
273      */
274     std::unique_ptr<GPIOInterface> presenceGPIO = nullptr;
275 
276     /** @brief True if the power supply is present. */
277     bool present = false;
278 
279     /** @brief Power supply model name. */
280     std::string modelName;
281 
282     /** @brief D-Bus match variable used to subscribe to Present property
283      * changes.
284      **/
285     std::unique_ptr<sdbusplus::bus::match_t> presentMatch;
286 
287     /** @brief D-Bus match variable used to subscribe for Present property
288      * interface added.
289      */
290     std::unique_ptr<sdbusplus::bus::match_t> presentAddedMatch;
291 
292     /**
293      * @brief Pointer to the PMBus interface
294      *
295      * Used to read or write to/from PMBus power supply devices.
296      */
297     std::unique_ptr<phosphor::pmbus::PMBusBase> pmbusIntf = nullptr;
298 
299     /** @brief Stored copy of the firmware version/revision string */
300     std::string fwVersion;
301 
302     /**
303      * @brief The file system path used for binding the device driver.
304      */
305     const std::filesystem::path bindPath;
306 
307     /* @brief The string to pass in for binding the device driver. */
308     std::string bindDevice;
309 
310     /**
311      * @brief Binds or unbinds the power supply device driver
312      *
313      * Called when a presence change is detected to either bind the device
314      * driver for the power supply when it is installed, or unbind the device
315      * driver when the power supply is removed.
316      *
317      * Writes <device> to <path>/bind (or unbind)
318      *
319      * @param present - when true, will bind the device driver
320      *                  when false, will unbind the device driver
321      */
322     void bindOrUnbindDriver(bool present);
323 
324     /**
325      *  @brief Updates the presence status by querying D-Bus
326      *
327      * The D-Bus inventory properties for this power supply will be read to
328      * determine if the power supply is present or not and update this
329      * object's present member variable to reflect current status.
330      **/
331     void updatePresence();
332 
333     /**
334      * @brief Updates the power supply presence by reading the GPIO line.
335      */
336     void updatePresenceGPIO();
337 
338     /**
339      * @brief Callback for inventory property changes
340      *
341      * Process change of Present property for power supply.
342      *
343      * This is used if we are watching the D-Bus properties instead of reading
344      * the GPIO presence line ourselves.
345      *
346      * @param[in]  msg - Data associated with Present change signal
347      **/
348     void inventoryChanged(sdbusplus::message::message& msg);
349 
350     /**
351      * @brief Callback for inventory property added.
352      *
353      * Process add of the interface with the Present property for power supply.
354      *
355      * This is used if we are watching the D-Bus properties instead of reading
356      * the GPIO presence line ourselves.
357      *
358      * @param[in]  msg - Data associated with Present add signal
359      **/
360     void inventoryAdded(sdbusplus::message::message& msg);
361 };
362 
363 } // namespace phosphor::power::psu
364