1 #pragma once
2 
3 #include "config.h"
4 
5 #ifdef POWER10
6 #include "occ_command.hpp"
7 
8 #include <cereal/archives/json.hpp>
9 #include <cereal/cereal.hpp>
10 #include <cereal/types/string.hpp>
11 #include <cereal/types/tuple.hpp>
12 #include <cereal/types/vector.hpp>
13 #include <sdbusplus/bus.hpp>
14 #include <sdbusplus/bus/match.hpp>
15 #include <xyz/openbmc_project/Control/Power/IdlePowerSaver/server.hpp>
16 #include <xyz/openbmc_project/Control/Power/Mode/server.hpp>
17 
18 #include <filesystem>
19 
20 namespace open_power
21 {
22 namespace occ
23 {
24 
25 class Manager;
26 
27 namespace powermode
28 {
29 namespace Base = sdbusplus::xyz::openbmc_project::Control::Power::server;
30 using ModeInterface = sdbusplus::server::object_t<Base::Mode>;
31 using IpsInterface = sdbusplus::server::object_t<Base::IdlePowerSaver>;
32 using namespace std::literals::string_literals;
33 
34 constexpr auto PMODE_PATH = "/xyz/openbmc_project/control/host0/power_mode";
35 constexpr auto PMODE_INTERFACE = "xyz.openbmc_project.Control.Power.Mode";
36 constexpr auto POWER_MODE_PROP = "PowerMode";
37 constexpr auto POWER_SAFE_MODE_PROP = "SafeMode";
38 
39 constexpr auto PIPS_PATH = "/xyz/openbmc_project/control/host0/power_ips";
40 constexpr auto PIPS_INTERFACE =
41     "xyz.openbmc_project.Control.Power.IdlePowerSaver";
42 constexpr auto IPS_ACTIVE_PROP = "Active";
43 constexpr auto IPS_ENABLED_PROP = "Enabled";
44 constexpr auto IPS_ENTER_UTIL = "EnterUtilizationPercent";
45 constexpr auto IPS_ENTER_TIME = "EnterDwellTime";
46 constexpr auto IPS_EXIT_UTIL = "ExitUtilizationPercent";
47 constexpr auto IPS_EXIT_TIME = "ExitDwellTime";
48 
49 const auto PMODE_DEFAULT_INTERFACE =
50     "xyz.openbmc_project.Configuration.PowerModeProperties"s;
51 
52 /** @brief Query the current Hypervisor target
53  * @return true if the current Hypervisor target is PowerVM
54  */
55 bool isPowerVM();
56 
57 /** @brief Convert power mode string to OCC SysPwrMode value
58  *
59  * @param[in] i_modeString - power mode string
60  *
61  * @return  SysPwrMode or SysPwrMode::NO_CHANGE if not found
62  */
63 SysPwrMode convertStringToMode(const std::string& i_modeString);
64 
65 struct PowerModeData
66 {
67     bool modeInitialized = false;
68     SysPwrMode mode = SysPwrMode::NO_CHANGE;
69     uint16_t oemModeData = 0x0000;
70     bool ipsInitialized = false;
71     bool ipsEnabled = true;
72     uint8_t ipsEnterUtil = 0;
73     uint16_t ipsEnterTime = 0;
74     uint8_t ipsExitUtil = 0;
75     uint16_t ipsExitTime = 0;
76     bool modeLocked = false;
77 
78     /** @brief Function specifying data to archive for cereal.
79      */
80     template <class Archive>
serializeopen_power::occ::powermode::PowerModeData81     void serialize(Archive& archive)
82     {
83         archive(modeInitialized, mode, oemModeData, ipsInitialized, ipsEnabled,
84                 ipsEnterUtil, ipsEnterTime, ipsExitUtil, ipsExitTime,
85                 modeLocked);
86     }
87 };
88 
89 /** @class OccPersistData
90  *  @brief Provides persistent container to store data for OCC
91  *
92  * Data is stored via cereal
93  */
94 class OccPersistData
95 {
96   public:
97     ~OccPersistData() = default;
98     OccPersistData(const OccPersistData&) = default;
99     OccPersistData& operator=(const OccPersistData&) = default;
100     OccPersistData(OccPersistData&&) = default;
101     OccPersistData& operator=(OccPersistData&&) = default;
102 
103     /** @brief Loads any saved power mode data */
OccPersistData()104     OccPersistData()
105     {
106         load();
107     }
108 
109     /** @brief Save Power Mode data to persistent file
110      *
111      *  @param[in] newMode - desired System Power Mode
112      *  @param[in] oemModeData - data required by some OEM Power Modes
113      */
updateMode(const SysPwrMode newMode,const uint16_t oemModeData)114     void updateMode(const SysPwrMode newMode, const uint16_t oemModeData)
115     {
116         modeData.mode = newMode;
117         modeData.oemModeData = oemModeData;
118         modeData.modeInitialized = true;
119         save();
120     }
121 
122     /** @brief Save Power Mode Lock value to persistent file
123      *
124      *  @param[in] modeLock - desired System Power Mode Lock
125      */
updateModeLock(const bool modeLock)126     void updateModeLock(const bool modeLock)
127     {
128         modeData.modeLocked = modeLock;
129         save();
130     }
131     /** @brief Write Idle Power Saver parameters to persistent file
132      *
133      *  @param[in] enabled - Idle Power Save status (true = enabled)
134      *  @param[in] enterUtil - IPS Enter Utilization (%)
135      *  @param[in] enterTime - IPS Enter Time (seconds)
136      *  @param[in] exitUtil - IPS Exit Utilization (%)
137      *  @param[in] exitTime - IPS Exit Time (seconds)
138      */
updateIPS(const bool enabled,const uint8_t enterUtil,const uint16_t enterTime,const uint8_t exitUtil,const uint16_t exitTime)139     void updateIPS(const bool enabled, const uint8_t enterUtil,
140                    const uint16_t enterTime, const uint8_t exitUtil,
141                    const uint16_t exitTime)
142     {
143         modeData.ipsEnabled = enabled;
144         modeData.ipsEnterUtil = enterUtil;
145         modeData.ipsEnterTime = enterTime;
146         modeData.ipsExitUtil = exitUtil;
147         modeData.ipsExitTime = exitTime;
148         modeData.ipsInitialized = true;
149         save();
150     }
151 
152     /** @brief Return the Power Mode and mode data
153      *
154      *  @param[out] mode - current system power mode
155      *  @param[out] oemModeData - frequency data for some OEM mode
156      *
157      *  @returns true if mode was available
158      */
getMode(SysPwrMode & mode,uint16_t & oemModeData) const159     bool getMode(SysPwrMode& mode, uint16_t& oemModeData) const
160     {
161         if (modeData.modeInitialized)
162         {
163             mode = modeData.mode;
164             oemModeData = modeData.oemModeData;
165         }
166         return modeData.modeInitialized;
167     }
168 
169     /** @brief Get the Idle Power Saver properties from DBus
170      *
171      *  @param[out] enabled - Idle Power Save status (true = enabled)
172      *  @param[out] enterUtil - IPS Enter Utilization (%)
173      *  @param[out] enterTime - IPS Enter Time (seconds)
174      *  @param[out] exitUtil - IPS Exit Utilization (%)
175      *  @param[out] exitTime - IPS Exit Time (seconds)
176      *
177      * @return true if parameters were read successfully
178      */
getIPS(bool & enabled,uint8_t & enterUtil,uint16_t & enterTime,uint8_t & exitUtil,uint16_t & exitTime)179     bool getIPS(bool& enabled, uint8_t& enterUtil, uint16_t& enterTime,
180                 uint8_t& exitUtil, uint16_t& exitTime)
181     {
182         if (!modeData.ipsInitialized)
183         {
184             return false;
185         }
186 
187         enabled = modeData.ipsEnabled;
188         enterUtil = modeData.ipsEnterUtil;
189         enterTime = modeData.ipsEnterTime;
190         exitUtil = modeData.ipsExitUtil;
191         exitTime = modeData.ipsExitTime;
192         return true;
193     }
194 
195     /** @brief Return persisted mode lock */
getModeLock()196     bool getModeLock()
197     {
198         return modeData.modeLocked;
199     }
200 
201     /** @brief Return true if the power mode is available */
modeAvailable()202     bool modeAvailable()
203     {
204         return (modeData.modeInitialized);
205     }
206 
207     /** @brief Return true if the IPS data is available */
ipsAvailable()208     bool ipsAvailable()
209     {
210         return (modeData.ipsInitialized);
211     }
212 
213     /** @brief Saves the Power Mode data in the filesystem using cereal. */
214     void save();
215 
216     /** @brief Trace the Power Mode and IPS parameters. */
217     void print();
218 
219     /** @brief Invalidate the persisted mode */
invalidateMode()220     void invalidateMode()
221     {
222         modeData.modeInitialized = false;
223     }
224 
225   private:
226     /** @brief Power Mode data filename to store persistent data */
227     static constexpr auto powerModeFilename = "powerModeData";
228 
229     /** @brief Power Mode data object to be persisted */
230     PowerModeData modeData;
231 
232     /** @brief Loads the OEM mode data in the filesystem using cereal. */
233     void load();
234 };
235 
236 /** @class PowerMode
237  *  @brief Monitors for changes to the power mode and notifies occ
238  *
239  *  The customer power mode is provided to the OCC by host TMGT when the occ
240  *  first goes active or is reset.  This code is responsible for sending
241  *  the power mode to the OCC if the mode is changed while the occ is active.
242  */
243 
244 class PowerMode : public ModeInterface
245 {
246   public:
247     /** @brief PowerMode object to inform occ of changes to mode
248      *
249      * This object will monitor for changes to the power mode setting.
250      * If a change is detected, and the occ is active, then this object will
251      * notify the OCC of the change.
252      *
253      * @param[in] managerRef - manager object reference
254      * @param[in] modePath - Power Mode dbus path
255      * @param[in] ipsPath - Idle Power Saver dbus path
256      */
257     explicit PowerMode(const Manager& managerRef, const char* modePath,
258                        const char* ipsPath, EventPtr& event);
259 
260     /** @brief Initialize the persistent data with default values
261      *
262      * @return true if initialization completed
263      */
264     bool initPersistentData();
265 
266     /** @brief Set the power mode lock (dbus method)
267      *
268      * @return true if successful
269      */
270     bool powerModeLock();
271 
272     /** @brief Get the power mode lock status (dbus method)
273      *
274      * @return true if locked
275      */
276     bool powerModeLockStatus();
277 
278     /** @brief Set the current power mode property
279      *
280      * @param[in] newMode     - desired system power mode
281      * @param[in] oemModeData - data required by some OEM Power Modes
282      *
283      * @return true if mode accepted
284      */
285     bool setMode(const SysPwrMode newMode, const uint16_t oemModeData);
286 
287     /** @brief Send mode change command to the master OCC
288      *  @return SUCCESS on success
289      */
290     CmdStatus sendModeChange();
291 
292     /** @brief Send Idle Power Saver config data to the master OCC
293      *  @return SUCCESS on success
294      */
295     CmdStatus sendIpsData();
296 
297     /** @brief Set the master OCC path
298      *
299      * @param[in]  occPath - hwmon path for master OCC
300      */
301     void setMasterOcc(const std::string& occPath);
302 
303     /** @brief Notify object of master OCC state.  If not acitve, no
304      * commands will be sent to the master OCC
305      *
306      * @param[in]  isActive - true when master OCC is active
307      */
setMasterActive(const bool isActive=true)308     void setMasterActive(const bool isActive = true)
309     {
310         masterActive = isActive;
311     };
312 
313     /** @brief Starts to monitor for IPS active state change conditions
314      *
315      *  @param[in] poll - Indicates whether or not the IPS state file should
316      *                    actually be read for changes.
317      */
318     void addIpsWatch(bool poll = true);
319 
320     /** @brief Removes IPS active watch */
321     void removeIpsWatch();
322 
323     /** @brief Set dbus property to SAFE Mode(true) or clear SAFE Mode(false)*/
324     void updateDbusSafeMode(const bool safeMode);
325 
326     /** @brief override the set/get MODE function
327      *
328      *  @param[in] value - Intended value
329      *
330      *  @return          - the value or Updated value of the property
331      */
332     Base::Mode::PowerMode powerMode(Base::Mode::PowerMode value) override;
333 
334     /** @brief Determine if the supplied mode is valid for the system
335      *
336      *  @param[in] mode  - potential mode
337      *
338      *  @return          - true if the mode is valid
339      */
340     bool isValidMode(const SysPwrMode mode);
341 
342     /** @brief If IPS is supported, set flag indicating need to send IPS data */
needToSendIPS()343     void needToSendIPS()
344     {
345         if (ipsObject)
346         {
347             needToSendIpsData = true;
348         }
349     }
350 
351   private:
352     /** @brief OCC manager object */
353     const Manager& manager;
354 
355     /** @brief Pass-through occ path on the bus */
356     std::string path;
357 
358     /** @brief OCC instance number */
359     int occInstance;
360 
361     /** @brief Object to send commands to the OCC */
362     std::unique_ptr<open_power::occ::OccCommand> occCmd;
363 
364     /** @brief Used to subscribe to dbus IPS property changes **/
365     sdbusplus::bus::match_t ipsMatch;
366 
367     /** @brief Used to subscribe to dbus defaults property changes **/
368     sdbusplus::bus::match_t defaultsUpdateMatch;
369 
370     OccPersistData persistedData;
371 
372     /** @brief True when the master OCC has been established **/
373     bool masterOccSet;
374 
375     /** @brief True when the master OCC is active **/
376     bool masterActive;
377 
378     /** @brief True when the ecoModes are supported for this system **/
379     bool ecoModeSupport = false;
380 
381     /** @brief List of customer supported power modes **/
382     std::set<SysPwrMode> customerModeList = {
383         SysPwrMode::STATIC, SysPwrMode::POWER_SAVING, SysPwrMode::MAX_PERF};
384 
385     /** @brief List of OEM supported power modes **/
386     std::set<SysPwrMode> oemModeList = {SysPwrMode::SFP, SysPwrMode::FFO,
387                                         SysPwrMode::MAX_FREQ};
388 
389     /** @brief IPS status data filename to read */
390     const fs::path ipsStatusFile =
391         std::filesystem::path{OCC_HWMON_PATH} /
392         std::filesystem::path{OCC_MASTER_NAME} / "occ_ips_status";
393 
394     /** @brief Current state of error watching */
395     bool watching = false;
396 
397     /** @brief Set at IPS object creation and cleared after sending IPS data */
398     bool needToSendIpsData = false;
399 
400     /** @brief Object path for IPS on DBUS */
401     const char* ipsObjectPath;
402 
403     /** @brief IPS DBUS Object */
404     std::unique_ptr<IpsInterface> ipsObject;
405 
406     /** @brief register for the callback from the POLL IPS changed event */
407     void registerIpsStatusCallBack();
408 
409     /** @brief Get the current power mode property
410      *
411      * @param[out] currentMode - current system power mode
412      * @param[out] oemModeData - frequency data for some OEM mode
413      *
414      * @return true if data read successfully
415      */
416     bool getMode(SysPwrMode& currentMode, uint16_t& oemModeData);
417 
418     /** @brief Update the power mode property on DBus
419      *
420      * @param[in]  newMode - desired power mode
421      *
422      * @return true on success
423      */
424     bool updateDbusMode(const SysPwrMode newMode);
425 
426     /** @brief Callback for IPS setting changes
427      *
428      * Process change and inform OCC
429      *
430      * @param[in]  msg - Data associated with IPS change signal
431      *
432      */
433     void ipsChanged(sdbusplus::message_t& msg);
434 
435     /** @brief Get the Idle Power Saver properties
436      *
437      *  @param[out] enabled - Idle Power Save status (true = enabled)
438      *  @param[out] enterUtil - IPS Enter Utilization (%)
439      *  @param[out] enterTime - IPS Enter Time (seconds)
440      *  @param[out] exitUtil - IPS Exit Utilization (%)
441      *  @param[out] exitTime - IPS Exit Time (seconds)
442      *
443      * @return true if data read successfully
444      */
445     bool getIPSParms(bool& enabled, uint8_t& enterUtil, uint16_t& enterTime,
446                      uint8_t& exitUtil, uint16_t& exitTime);
447 
448     /** Update the Idle Power Saver data on DBus
449      *
450      *  @param[in] enabled - Idle Power Save status (true = enabled)
451      *  @param[in] enterUtil - IPS Enter Utilization (%)
452      *  @param[in] enterTime - IPS Enter Time (seconds)
453      *  @param[in] exitUtil - IPS Exit Utilization (%)
454      *  @param[in] exitTime - IPS Exit Time (seconds)
455      *
456      *  @return true if parameters were set successfully
457      */
458     bool updateDbusIPS(const bool enabled, const uint8_t enterUtil,
459                        const uint16_t enterTime, const uint8_t exitUtil,
460                        const uint16_t exitTime);
461 
462     /** @brief Callback for entity manager default changes
463      *
464      * Called when PowerModeProperties defaults are available
465      */
466     void defaultsReady(sdbusplus::message_t& msg);
467 
468     /** @brief Get the default power mode property for this system type
469      *
470      * @param[out] defaultMode - default system power mode
471      *
472      * @return true if data read successfully
473      */
474     bool getDefaultMode(SysPwrMode& defaultMode);
475 
476     /** @brief Get the default Idle Power Saver properties for this system type
477      *
478      *  @param[out] enabled - Idle Power Save status (true = enabled)
479      *  @param[out] enterUtil - IPS Enter Utilization (%)
480      *  @param[out] enterTime - IPS Enter Time (seconds)
481      *  @param[out] exitUtil - IPS Exit Utilization (%)
482      *  @param[out] exitTime - IPS Exit Time (seconds)
483      *
484      *  @return true if parameters were read successfully
485      */
486     bool getDefaultIPSParms(bool& enabled, uint8_t& enterUtil,
487                             uint16_t& enterTime, uint8_t& exitUtil,
488                             uint16_t& exitTime);
489 
490     /** @brief Read the default Idle Power Saver parameters and save them to the
491      * DBUS so they will get used
492      *
493      * @return true if restore was successful
494      */
495     bool useDefaultIPSParms();
496 
497     /** @brief Read the supported power modes from entity-manager and update
498      * AllowedPowerModes on dbus
499      *
500      * @return true if data was found/updated
501      */
502     bool getSupportedModes();
503 
504     /** @brief Create IPS DBUS Object */
505     void createIpsObject();
506 
507     /** @brief Remove IPS DBUS Object */
508     void removeIpsObject();
509 
510     /** @brief callback for the POLL IPS changed event
511      *
512      *  @param[in] es       - Populated event source
513      *  @param[in] fd       - Associated File descriptor
514      *  @param[in] revents  - Type of event
515      *  @param[in] userData - User data that was passed during registration
516      */
517     static int ipsStatusCallBack(sd_event_source* es, int fd, uint32_t revents,
518                                  void* userData);
519 
520     /** @brief Opens the IPS file and populates fd */
521     bool openIpsFile();
522 
523     /** @brief sd_event wrapped in unique_ptr */
524     EventPtr& event;
525 
526     /** @brief event source wrapped in unique_ptr */
527     EventSourcePtr eventSource;
528 
529     /** @brief When the ips status event is received, analyzes it */
530     virtual void analyzeIpsEvent();
531 
532   protected:
533     /** @brief File descriptor to watch for errors */
534     int fd = -1;
535 };
536 
537 } // namespace powermode
538 
539 } // namespace occ
540 
541 } // namespace open_power
542 #endif
543