1 /**
2  * Copyright © 2024 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 #pragma once
17 
18 #include "pmbus.hpp"
19 #include "rail.hpp"
20 #include "services.hpp"
21 #include "standard_device.hpp"
22 
23 #include <stddef.h> // for size_t
24 
25 #include <cstdint>
26 #include <map>
27 #include <memory>
28 #include <optional>
29 #include <string>
30 #include <utility>
31 #include <vector>
32 
33 namespace phosphor::power::sequencer
34 {
35 
36 /**
37  * @class PMBusDriverDevice
38  *
39  * StandardDevice sub-class for power sequencer devices that are bound to a
40  * PMBus device driver.
41  */
42 class PMBusDriverDevice : public StandardDevice
43 {
44   public:
45     // Specify which compiler-generated methods we want
46     PMBusDriverDevice() = delete;
47     PMBusDriverDevice(const PMBusDriverDevice&) = delete;
48     PMBusDriverDevice(PMBusDriverDevice&&) = delete;
49     PMBusDriverDevice& operator=(const PMBusDriverDevice&) = delete;
50     PMBusDriverDevice& operator=(PMBusDriverDevice&&) = delete;
51     virtual ~PMBusDriverDevice() = default;
52 
53     /**
54      * Constructor.
55      *
56      * @param name Device name
57      * @param rails Voltage rails that are enabled and monitored by this device
58      * @param services System services like hardware presence and the journal
59      * @param bus I2C bus for the device
60      * @param address I2C address for the device
61      * @param driverName Device driver name
62      * @param instance Chip instance number
63      */
PMBusDriverDevice(const std::string & name,std::vector<std::unique_ptr<Rail>> rails,Services & services,uint8_t bus,uint16_t address,const std::string & driverName="",size_t instance=0)64     explicit PMBusDriverDevice(const std::string& name,
65                                std::vector<std::unique_ptr<Rail>> rails,
66                                Services& services, uint8_t bus,
67                                uint16_t address,
68                                const std::string& driverName = "",
69                                size_t instance = 0) :
70         StandardDevice(name, std::move(rails)),
71         bus{bus}, address{address}, driverName{driverName}, instance{instance}
72     {
73         pmbusInterface = services.createPMBus(bus, address, driverName,
74                                               instance);
75     }
76 
77     /**
78      * Returns the I2C bus for the device.
79      *
80      * @return I2C bus
81      */
getBus() const82     uint8_t getBus() const
83     {
84         return bus;
85     }
86 
87     /**
88      * Returns the I2C address for the device.
89      *
90      * @return I2C address
91      */
getAddress() const92     uint16_t getAddress() const
93     {
94         return address;
95     }
96 
97     /**
98      * Returns the device driver name.
99      *
100      * @return driver name
101      */
getDriverName() const102     const std::string& getDriverName() const
103     {
104         return driverName;
105     }
106 
107     /**
108      * Returns the chip instance number.
109      *
110      * @return chip instance
111      */
getInstance() const112     size_t getInstance() const
113     {
114         return instance;
115     }
116 
117     /**
118      * Returns interface to the PMBus information that is provided by the device
119      * driver in sysfs.
120      *
121      * @return PMBus interface object
122      */
getPMBusInterface()123     pmbus::PMBusBase& getPMBusInterface()
124     {
125         return *pmbusInterface;
126     }
127 
128     /** @copydoc PowerSequencerDevice::getGPIOValues() */
129     virtual std::vector<int> getGPIOValues(Services& services) override;
130 
131     /** @copydoc PowerSequencerDevice::getStatusWord() */
132     virtual uint16_t getStatusWord(uint8_t page) override;
133 
134     /** @copydoc PowerSequencerDevice::getStatusVout() */
135     virtual uint8_t getStatusVout(uint8_t page) override;
136 
137     /** @copydoc PowerSequencerDevice::getReadVout() */
138     virtual double getReadVout(uint8_t page) override;
139 
140     /** @copydoc PowerSequencerDevice::getVoutUVFaultLimit() */
141     virtual double getVoutUVFaultLimit(uint8_t page) override;
142 
143     /**
144      * Returns map from PMBus PAGE numbers to sysfs hwmon file numbers.
145      *
146      * Throws an exception if an error occurs trying to build the map.
147      *
148      * @return page to file number map
149      */
getPageToFileNumberMap()150     const std::map<uint8_t, unsigned int>& getPageToFileNumberMap()
151     {
152         if (pageToFileNumber.empty())
153         {
154             buildPageToFileNumberMap();
155         }
156         return pageToFileNumber;
157     }
158 
159     /**
160      * Returns the hwmon file number that corresponds to the specified PMBus
161      * PAGE number.
162      *
163      * Throws an exception if a file number was not found for the specified PAGE
164      * number.
165      *
166      * @param page PMBus PAGE number
167      * @return hwmon file number
168      */
169     unsigned int getFileNumber(uint8_t page);
170 
171   protected:
172     /** @copydoc StandardDevice::prepareForPgoodFaultDetection() */
prepareForPgoodFaultDetection(Services & services)173     virtual void prepareForPgoodFaultDetection(Services& services) override
174     {
175         // Rebuild PMBus PAGE to hwmon file number map
176         buildPageToFileNumberMap();
177 
178         // Call parent class method to do any actions defined there
179         StandardDevice::prepareForPgoodFaultDetection(services);
180     }
181 
182     /**
183      * Build mapping from PMBus PAGE numbers to the hwmon file numbers in
184      * sysfs.
185      *
186      * hwmon file names have the format:
187      *   <type><number>_<item>
188      *
189      * The <number> is not the PMBus PAGE number.  The PMBus PAGE is determined
190      * by reading the contents of the <type><number>_label file.
191      *
192      * If the map is not empty, it is cleared and rebuilt.  This is necessary
193      * over time because power devices may have been added or removed.
194      *
195      * Throws an exception if an error occurs trying to build the map.
196      */
197     virtual void buildPageToFileNumberMap();
198 
199     /**
200      * Returns whether the specified sysfs hwmon file is a voltage label file.
201      *
202      * If it is a label file, the hwmon file number is obtained from the file
203      * name and returned.
204      *
205      * @param fileName file within the sysfs hwmon directory
206      * @param fileNumber the hwmon file number is stored in this output
207      *                   parameter if this is a label file
208      * @return true if specified file is a voltage label file, false otherwise
209      */
210     virtual bool isLabelFile(const std::string& fileName,
211                              unsigned int& fileNumber);
212 
213     /**
214      * Reads the specified voltage label file to obtain the associated PMBus
215      * PAGE number.
216      *
217      * The returned optional variable will have no value if the PMBus PAGE
218      * number could not be obtained due to an error.
219      *
220      * @param fileName voltage label file within the sysfs hwmon directory
221      * @return PMBus page number
222      */
223     virtual std::optional<uint8_t>
224         readPageFromLabelFile(const std::string& fileName);
225 
226     /**
227      * I2C bus for the device.
228      */
229     uint8_t bus;
230 
231     /**
232      * I2C address for the device.
233      */
234     uint16_t address;
235 
236     /**
237      * Device driver name.
238      */
239     std::string driverName;
240 
241     /**
242      * Chip instance number.
243      */
244     size_t instance;
245 
246     /**
247      * Interface to the PMBus information that is provided by the device driver
248      * in sysfs.
249      */
250     std::unique_ptr<pmbus::PMBusBase> pmbusInterface;
251 
252     /**
253      * Map from PMBus PAGE numbers to sysfs hwmon file numbers.
254      */
255     std::map<uint8_t, unsigned int> pageToFileNumber;
256 };
257 
258 } // namespace phosphor::power::sequencer
259