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