1 #pragma once
2 
3 #include "power_supply.hpp"
4 #include "types.hpp"
5 #include "utility.hpp"
6 
7 #include <phosphor-logging/log.hpp>
8 #include <sdbusplus/bus/match.hpp>
9 #include <sdbusplus/server/manager.hpp>
10 #include <sdbusplus/server/object.hpp>
11 #include <sdeventplus/event.hpp>
12 #include <sdeventplus/utility/timer.hpp>
13 #include <xyz/openbmc_project/State/Decorator/PowerSystemInputs/server.hpp>
14 
15 struct sys_properties
16 {
17     int powerSupplyCount;
18     std::vector<uint64_t> inputVoltage;
19     bool powerConfigFullLoad;
20 };
21 
22 using namespace phosphor::power::psu;
23 using namespace phosphor::logging;
24 
25 namespace phosphor::power::manager
26 {
27 
28 using PowerSystemInputsInterface = sdbusplus::xyz::openbmc_project::State::
29     Decorator::server::PowerSystemInputs;
30 using PowerSystemInputsObject =
31     sdbusplus::server::object_t<PowerSystemInputsInterface>;
32 
33 // Validation timeout. Allow 10s to detect if new EM interfaces show up in D-Bus
34 // before performing the validation.
35 constexpr auto validationTimeout = std::chrono::seconds(10);
36 
37 /**
38  * @class PowerSystemInputs
39  * @brief A concrete implementation for the PowerSystemInputs interface.
40  */
41 class PowerSystemInputs : public PowerSystemInputsObject
42 {
43   public:
44     PowerSystemInputs(sdbusplus::bus::bus& bus, const std::string& path) :
45         PowerSystemInputsObject(bus, path.c_str())
46     {}
47 };
48 
49 /**
50  * @class PSUManager
51  *
52  * This class will create an object used to manage and monitor a list of power
53  * supply devices.
54  */
55 class PSUManager
56 {
57   public:
58     PSUManager() = delete;
59     ~PSUManager() = default;
60     PSUManager(const PSUManager&) = delete;
61     PSUManager& operator=(const PSUManager&) = delete;
62     PSUManager(PSUManager&&) = delete;
63     PSUManager& operator=(PSUManager&&) = delete;
64 
65     /**
66      * Constructor to read configuration from D-Bus.
67      *
68      * @param[in] bus - D-Bus bus object
69      * @param[in] e - event object
70      */
71     PSUManager(sdbusplus::bus::bus& bus, const sdeventplus::Event& e);
72 
73     /**
74      * Get PSU properties from D-Bus, use that to build a power supply
75      * object.
76      *
77      * @param[in] properties - A map of property names and values
78      *
79      */
80     void getPSUProperties(util::DbusPropertyMap& properties);
81 
82     /**
83      * Get PSU configuration from D-Bus
84      */
85     void getPSUConfiguration();
86 
87     /**
88      * @brief Initialize the system properties from the Supported Configuration
89      *        D-Bus object provided by Entity Manager.
90      */
91     void getSystemProperties();
92 
93     /**
94      * Initializes the manager.
95      *
96      * Get current BMC state, ...
97      */
98     void initialize()
99     {
100         // When state = 1, system is powered on
101         int32_t state = 0;
102 
103         try
104         {
105             // Use getProperty utility function to get power state.
106             util::getProperty<int32_t>(POWER_IFACE, "state", POWER_OBJ_PATH,
107                                        powerService, bus, state);
108 
109             if (state)
110             {
111                 powerOn = true;
112                 validationTimer->restartOnce(validationTimeout);
113             }
114             else
115             {
116                 powerOn = false;
117                 runValidateConfig = true;
118             }
119         }
120         catch (const std::exception& e)
121         {
122             log<level::INFO>("Failed to get power state. Assuming it is off.");
123             powerOn = false;
124             runValidateConfig = true;
125         }
126 
127         onOffConfig(phosphor::pmbus::ON_OFF_CONFIG_CONTROL_PIN_ONLY);
128         clearFaults();
129         updateInventory();
130         setPowerConfigGPIO();
131     }
132 
133     /**
134      * Starts the timer to start monitoring the list of devices.
135      */
136     int run()
137     {
138         return timer->get_event().loop();
139     }
140 
141     /**
142      * Write PMBus ON_OFF_CONFIG
143      *
144      * This function will be called to cause the PMBus device driver to send the
145      * ON_OFF_CONFIG command. Takes one byte of data.
146      */
147     void onOffConfig(const uint8_t data)
148     {
149         for (auto& psu : psus)
150         {
151             psu->onOffConfig(data);
152         }
153     }
154 
155     /**
156      * This function will be called in various situations in order to clear
157      * any fault status bits that may have been set, in order to start over
158      * with a clean state. Presence changes and power state changes will want
159      * to clear any faults logged.
160      */
161     void clearFaults()
162     {
163         setPowerSupplyError("");
164         for (auto& psu : psus)
165         {
166             psu->clearFaults();
167         }
168     }
169 
170   private:
171     /**
172      * The D-Bus object
173      */
174     sdbusplus::bus::bus& bus;
175 
176     /**
177      * The timer that runs to periodically check the power supplies.
178      */
179     std::unique_ptr<
180         sdeventplus::utility::Timer<sdeventplus::ClockId::Monotonic>>
181         timer;
182 
183     /**
184      * The timer that performs power supply validation as the entity manager
185      * interfaces show up in d-bus.
186      */
187     std::unique_ptr<
188         sdeventplus::utility::Timer<sdeventplus::ClockId::Monotonic>>
189         validationTimer;
190 
191     /**
192      * Let power control/sequencer application know of PSU error(s).
193      *
194      * @param[in] psuErrorString - string for power supply error
195      */
196     void setPowerSupplyError(const std::string& psuErrorString);
197 
198     /**
199      * Create an error
200      *
201      * @param[in] faultName - 'name' message for the BMC error log entry
202      * @param[in,out] additionalData - The AdditionalData property for the error
203      */
204     void createError(const std::string& faultName,
205                      std::map<std::string, std::string>& additionalData);
206 
207     /**
208      * Analyze the status of each of the power supplies.
209      *
210      * Log errors for faults, when and where appropriate.
211      */
212     void analyze();
213 
214     /** @brief True if the power is on. */
215     bool powerOn = false;
216 
217     /** @brief True if an error for a brownout has already been logged. */
218     bool brownoutLogged = false;
219 
220     /** @brief Used as part of subscribing to power on state changes*/
221     std::string powerService;
222 
223     /** @brief Used to subscribe to D-Bus power on state changes */
224     std::unique_ptr<sdbusplus::bus::match_t> powerOnMatch;
225 
226     /** @brief Used to subscribe to D-Bus power supply presence changes */
227     std::vector<std::unique_ptr<sdbusplus::bus::match_t>> presenceMatches;
228 
229     /** @brief Used to subscribe to Entity Manager interfaces added */
230     std::unique_ptr<sdbusplus::bus::match_t> entityManagerIfacesAddedMatch;
231 
232     /**
233      * @brief Callback for power state property changes
234      *
235      * Process changes to the powered on state property for the system.
236      *
237      * @param[in] msg - Data associated with the power state signal
238      */
239     void powerStateChanged(sdbusplus::message::message& msg);
240 
241     /**
242      * @brief Callback for inventory property changes
243      *
244      * Process change of the Present property for power supply.
245      *
246      * @param[in]  msg - Data associated with the Present change signal
247      **/
248     void presenceChanged(sdbusplus::message::message& msg);
249 
250     /**
251      * @brief Callback for entity-manager interface added
252      *
253      * Process the information from the supported configuration and or IBM CFFPS
254      * Connector interface being added.
255      *
256      * @param[in] msg - Data associated with the interfaces added signal
257      */
258     void entityManagerIfaceAdded(sdbusplus::message::message& msg);
259 
260     /**
261      * @brief Adds properties to the inventory.
262      *
263      * Reads the values from the devices and writes them to the associated
264      * power supply D-Bus inventory objects.
265      *
266      * This needs to be done on startup, and each time the presence state
267      * changes.
268      */
269     void updateInventory()
270     {
271         for (auto& psu : psus)
272         {
273             psu->updateInventory();
274         }
275     }
276 
277     /**
278      * @brief Helper function to populate the system properties
279      *
280      * @param[in] properties - A map of property names and values
281      */
282     void populateSysProperties(const util::DbusPropertyMap& properties);
283 
284     /**
285      * @brief Perform power supply configuration validation.
286      * @details Validates if the existing power supply properties are a
287      * supported configuration, and acts on its findings such as logging errors.
288      */
289     void validateConfig();
290 
291     /**
292      * @brief Flag to indicate if the validateConfig() function should be run.
293      * Set to false once the configuration has been validated to avoid running
294      * multiple times due to interfaces added signal. Set to true during power
295      * off to trigger the validation on power on.
296      */
297     bool runValidateConfig = true;
298 
299     /**
300      * @brief Check that all PSUs have the same model name and that the system
301      * has the required number of PSUs present as specified in the Supported
302      * Configuration interface.
303      *
304      * @param[out] additionalData - Contains debug information on why the check
305      *             might have failed. Can be used to fill in error logs.
306      * @return true if all the required PSUs are present, false otherwise.
307      */
308     bool hasRequiredPSUs(std::map<std::string, std::string>& additionalData);
309 
310     /**
311      * @brief Helper function to validate that all PSUs have the same model name
312      *
313      * @param[out] model - The model name. Empty if there is a mismatch.
314      * @param[out] additionalData - If there is a mismatch, it contains debug
315      *             information such as the mismatched model name.
316      * @return true if all the PSUs have the same model name, false otherwise.
317      */
318     bool validateModelName(std::string& model,
319                            std::map<std::string, std::string>& additionalData);
320 
321     /**
322      * @brief Set the power-config-full-load GPIO depending on the EM full load
323      *        property value.
324      */
325     void setPowerConfigGPIO();
326 
327     /**
328      * @brief Indicate that the system is in a brownout condition by creating an
329      * error log and setting the PowerSystemInputs status property to Fault.
330      *
331      * @param[in] additionalData - Contains debug information on the number of
332      *            PSUs in fault state or not present.
333      */
334     void setBrownout(std::map<std::string, std::string>& additionalData);
335 
336     /**
337      * @brief Indicate that the system is no longer in a brownout condition by
338      * setting the PowerSystemInputs status property to Good.
339      */
340     void clearBrownout();
341 
342     /**
343      * @brief Map of supported PSU configurations that include the model name
344      * and their properties.
345      */
346     std::map<std::string, sys_properties> supportedConfigs;
347 
348     /**
349      * @brief The vector for power supplies.
350      */
351     std::vector<std::unique_ptr<PowerSupply>> psus;
352 
353     /**
354      * @brief The libgpiod object for setting the power supply config
355      */
356     std::unique_ptr<GPIOInterfaceBase> powerConfigGPIO = nullptr;
357 
358     /**
359      * @brief PowerSystemInputs object
360      */
361     PowerSystemInputs powerSystemInputs;
362 
363     /**
364      * @brief Implement the org.freedesktop.DBus.ObjectManager interface
365      */
366     sdbusplus::server::manager_t objectManager;
367 };
368 
369 } // namespace phosphor::power::manager
370