blob: 7db356595e2c8f2a93037dec45df45dd880aa5d0 [file] [log] [blame]
Chris Cain36f9cde2021-11-22 11:18:21 -06001#include "powermode.hpp"
2
Chris Cain78e86012021-03-04 16:15:31 -06003#include <fmt/core.h>
4
Chris Cain36f9cde2021-11-22 11:18:21 -06005#include <com/ibm/Host/Target/server.hpp>
Chris Cain78e86012021-03-04 16:15:31 -06006#include <phosphor-logging/log.hpp>
Chris Cain78e86012021-03-04 16:15:31 -06007#include <xyz/openbmc_project/Control/Power/Mode/server.hpp>
8
George Liub5ca1012021-09-10 12:53:11 +08009#include <cassert>
Chris Cain36f9cde2021-11-22 11:18:21 -060010#include <fstream>
George Liub5ca1012021-09-10 12:53:11 +080011#include <regex>
12
Chris Cain78e86012021-03-04 16:15:31 -060013namespace open_power
14{
15namespace occ
16{
17namespace powermode
18{
19
20using namespace phosphor::logging;
Chris Cain1be43372021-12-09 19:29:37 -060021using namespace std::literals::string_literals;
22
Chris Cain78e86012021-03-04 16:15:31 -060023using Mode = sdbusplus::xyz::openbmc_project::Control::Power::server::Mode;
24
Chris Cain6fa848a2022-01-24 14:54:38 -060025// Set the Master OCC
Chris Cain1be43372021-12-09 19:29:37 -060026void PowerMode::setMasterOcc(const std::string& masterOccPath)
Chris Cain6fa848a2022-01-24 14:54:38 -060027{
Chris Cain1be43372021-12-09 19:29:37 -060028 if (masterOccSet)
29 {
30 if (masterOccPath != path)
31 {
32 log<level::ERR>(
33 fmt::format(
34 "PowerMode::setMasterOcc: Master changed (was OCC{}, {})",
35 occInstance, masterOccPath)
36 .c_str());
37 if (occCmd)
38 {
39 occCmd.reset();
40 }
41 }
42 }
43 path = masterOccPath;
Chris Cain6fa848a2022-01-24 14:54:38 -060044 occInstance = path.back() - '0';
45 log<level::DEBUG>(fmt::format("PowerMode::setMasterOcc(OCC{}, {})",
46 occInstance, path.c_str())
47 .c_str());
Chris Cain1be43372021-12-09 19:29:37 -060048 if (!occCmd)
49 {
50 occCmd = std::make_unique<open_power::occ::OccCommand>(occInstance,
51 path.c_str());
52 }
Chris Cain6fa848a2022-01-24 14:54:38 -060053 masterOccSet = true;
54};
55
Chris Cain36f9cde2021-11-22 11:18:21 -060056// Called when DBus power mode gets changed
Chris Cain78e86012021-03-04 16:15:31 -060057void PowerMode::modeChanged(sdbusplus::message::message& msg)
58{
Chris Cain78e86012021-03-04 16:15:31 -060059 std::map<std::string, std::variant<std::string>> properties{};
60 std::string interface;
61 std::string propVal;
62 msg.read(interface, properties);
63 const auto modeEntry = properties.find(POWER_MODE_PROP);
64 if (modeEntry != properties.end())
65 {
66 auto modeEntryValue = modeEntry->second;
67 propVal = std::get<std::string>(modeEntryValue);
Chris Cain6fa848a2022-01-24 14:54:38 -060068 SysPwrMode newMode = convertStringToMode(propVal);
Chris Cain36f9cde2021-11-22 11:18:21 -060069 if (newMode != SysPwrMode::NO_CHANGE)
Chris Cain78e86012021-03-04 16:15:31 -060070 {
Chris Cain1be43372021-12-09 19:29:37 -060071 // Update persisted data with new mode
72 persistedData.updateMode(newMode, 0);
Chris Cain36f9cde2021-11-22 11:18:21 -060073
Chris Cain78e86012021-03-04 16:15:31 -060074 log<level::INFO>(
Chris Cain1be43372021-12-09 19:29:37 -060075 fmt::format("DBus Power Mode Changed: {}", propVal).c_str());
Chris Cain78e86012021-03-04 16:15:31 -060076
Chris Cain36f9cde2021-11-22 11:18:21 -060077 // Send mode change to OCC
78 sendModeChange();
79 }
80 }
81}
82
83// Called from OCC PassThrough interface (via CE login / BMC command line)
Chris Cain1be43372021-12-09 19:29:37 -060084bool PowerMode::setMode(const SysPwrMode newMode, const uint16_t oemModeData)
Chris Cain36f9cde2021-11-22 11:18:21 -060085{
86 if (updateDbusMode(newMode) == false)
87 {
88 // Unsupported mode
89 return false;
90 }
91
Chris Cain1be43372021-12-09 19:29:37 -060092 // Save mode
93 persistedData.updateMode(newMode, oemModeData);
Chris Cain36f9cde2021-11-22 11:18:21 -060094
Chris Cain1be43372021-12-09 19:29:37 -060095 // Send mode change to OCC
96 if (sendModeChange() != CmdStatus::SUCCESS)
97 {
98 // Mode change failed
99 return false;
Chris Cain78e86012021-03-04 16:15:31 -0600100 }
101
Chris Cain36f9cde2021-11-22 11:18:21 -0600102 return true;
Chris Cain78e86012021-03-04 16:15:31 -0600103}
104
105// Convert PowerMode string to OCC SysPwrMode
Chris Cain36f9cde2021-11-22 11:18:21 -0600106// Returns NO_CHANGE if OEM or unsupported mode
Chris Cain78e86012021-03-04 16:15:31 -0600107SysPwrMode convertStringToMode(const std::string& i_modeString)
108{
109 SysPwrMode pmode = SysPwrMode::NO_CHANGE;
110
111 Mode::PowerMode mode = Mode::convertPowerModeFromString(i_modeString);
112 if (mode == Mode::PowerMode::MaximumPerformance)
113 {
114 pmode = SysPwrMode::MAX_PERF;
115 }
116 else if (mode == Mode::PowerMode::PowerSaving)
117 {
118 pmode = SysPwrMode::POWER_SAVING;
119 }
120 else if (mode == Mode::PowerMode::Static)
121 {
Chris Cain36f9cde2021-11-22 11:18:21 -0600122 pmode = SysPwrMode::STATIC;
Chris Cain78e86012021-03-04 16:15:31 -0600123 }
124 else
125 {
Chris Cain36f9cde2021-11-22 11:18:21 -0600126 if (mode != Mode::PowerMode::OEM)
127 {
128 log<level::ERR>(
129 fmt::format(
130 "convertStringToMode: Invalid Power Mode specified: {}",
131 i_modeString)
132 .c_str());
133 }
Chris Cain78e86012021-03-04 16:15:31 -0600134 }
135
136 return pmode;
137}
138
Chris Cain36f9cde2021-11-22 11:18:21 -0600139// Check if Hypervisor target is PowerVM
140bool isPowerVM()
Chris Cain1d51da22021-09-21 14:13:41 -0500141{
Chris Cain36f9cde2021-11-22 11:18:21 -0600142 namespace Hyper = sdbusplus::com::ibm::Host::server;
143 constexpr auto HYPE_PATH = "/com/ibm/host0/hypervisor";
144 constexpr auto HYPE_INTERFACE = "com.ibm.Host.Target";
145 constexpr auto HYPE_PROP = "Target";
146
147 bool powerVmTarget = false;
148
149 // This will throw exception on failure
150 auto& bus = utils::getBus();
151 auto service = utils::getService(HYPE_PATH, HYPE_INTERFACE);
152 auto method = bus.new_method_call(service.c_str(), HYPE_PATH,
153 "org.freedesktop.DBus.Properties", "Get");
154 method.append(HYPE_INTERFACE, HYPE_PROP);
155 auto reply = bus.call(method);
156
157 std::variant<std::string> hyperEntryValue;
158 reply.read(hyperEntryValue);
159 auto propVal = std::get<std::string>(hyperEntryValue);
160 if (Hyper::Target::convertHypervisorFromString(propVal) ==
161 Hyper::Target::Hypervisor::PowerVM)
162 {
163 powerVmTarget = true;
164 }
165
166 log<level::DEBUG>(
167 fmt::format("isPowerVM returning {}", powerVmTarget).c_str());
168
169 return powerVmTarget;
170}
171
Chris Cain1be43372021-12-09 19:29:37 -0600172// Initialize persistent data and return true if successful
173bool PowerMode::initPersistentData()
Chris Cain36f9cde2021-11-22 11:18:21 -0600174{
Chris Cain1be43372021-12-09 19:29:37 -0600175 if (!persistedData.modeAvailable())
Chris Cain36f9cde2021-11-22 11:18:21 -0600176 {
Chris Cain1be43372021-12-09 19:29:37 -0600177 // Read the default mode
178 SysPwrMode currentMode;
179 if (!getDefaultMode(currentMode))
180 {
181 // Unable to read defaults
182 return false;
183 }
184 log<level::INFO>(
185 fmt::format("PowerMode::initPersistentData: Using default mode: {}",
186 currentMode)
Chris Cain36f9cde2021-11-22 11:18:21 -0600187 .c_str());
Chris Cain1be43372021-12-09 19:29:37 -0600188
189 // Save default mode as current mode
190 persistedData.updateMode(currentMode, 0);
191
192 // Write default mode to DBus
193 updateDbusMode(currentMode);
Chris Cain36f9cde2021-11-22 11:18:21 -0600194 }
195
Chris Cain1be43372021-12-09 19:29:37 -0600196 if (!persistedData.ipsAvailable())
197 {
Chris Caincde7bea2022-01-28 15:54:24 -0600198 // Read the default IPS parameters, write persistent file and update
199 // DBus
200 return useDefaultIPSParms();
Chris Cain1be43372021-12-09 19:29:37 -0600201 }
202 return true;
203}
204
205// Get the requested power mode and return true if successful
206bool PowerMode::getMode(SysPwrMode& currentMode, uint16_t& oemModeData)
207{
208 currentMode = SysPwrMode::NO_CHANGE;
209 oemModeData = 0;
210
211 if (!persistedData.getMode(currentMode, oemModeData))
212 {
213 // Persistent data not initialized, read defaults and update DBus
214 if (!initPersistentData())
215 {
216 // Unable to read defaults from entity manager yet
217 return false;
218 }
219 return persistedData.getMode(currentMode, oemModeData);
220 }
221
222 return true;
Chris Cain36f9cde2021-11-22 11:18:21 -0600223}
224
225// Set the power mode on DBus
226bool PowerMode::updateDbusMode(const SysPwrMode newMode)
227{
Chris Cain36f9cde2021-11-22 11:18:21 -0600228 if (!VALID_POWER_MODE_SETTING(newMode) &&
229 !VALID_OEM_POWER_MODE_SETTING(newMode))
230 {
231 log<level::ERR>(
232 fmt::format(
233 "PowerMode::updateDbusMode - Requested power mode not supported: {}",
234 newMode)
235 .c_str());
236 return false;
237 }
238
Chris Cain1be43372021-12-09 19:29:37 -0600239 // Convert mode for DBus
240 ModeInterface::PowerMode dBusMode;
Chris Cain36f9cde2021-11-22 11:18:21 -0600241 switch (newMode)
242 {
243 case SysPwrMode::STATIC:
Chris Cain1be43372021-12-09 19:29:37 -0600244 dBusMode = Mode::PowerMode::Static;
Chris Cain36f9cde2021-11-22 11:18:21 -0600245 break;
246 case SysPwrMode::POWER_SAVING:
Chris Cain1be43372021-12-09 19:29:37 -0600247 dBusMode = Mode::PowerMode::PowerSaving;
Chris Cain36f9cde2021-11-22 11:18:21 -0600248 break;
249 case SysPwrMode::MAX_PERF:
Chris Cain1be43372021-12-09 19:29:37 -0600250 dBusMode = Mode::PowerMode::MaximumPerformance;
Chris Cain36f9cde2021-11-22 11:18:21 -0600251 break;
252 default:
Chris Cain1be43372021-12-09 19:29:37 -0600253 dBusMode = Mode::PowerMode::OEM;
Chris Cain36f9cde2021-11-22 11:18:21 -0600254 }
255
Chris Cain1be43372021-12-09 19:29:37 -0600256 // true = skip update signal
257 ModeInterface::setPropertyByName(POWER_MODE_PROP, dBusMode, true);
Chris Cain36f9cde2021-11-22 11:18:21 -0600258
259 return true;
260}
261
262// Send mode change request to the master OCC
263CmdStatus PowerMode::sendModeChange()
264{
Chris Cain6fa848a2022-01-24 14:54:38 -0600265 CmdStatus status;
Chris Cain36f9cde2021-11-22 11:18:21 -0600266
Chris Cain6fa848a2022-01-24 14:54:38 -0600267 if (!masterActive || !masterOccSet)
Chris Cain36f9cde2021-11-22 11:18:21 -0600268 {
269 // Nothing to do
Chris Cain6fa848a2022-01-24 14:54:38 -0600270 log<level::DEBUG>("PowerMode::sendModeChange: OCC master not active");
Chris Cain36f9cde2021-11-22 11:18:21 -0600271 return CmdStatus::SUCCESS;
272 }
273
274 if (!isPowerVM())
275 {
276 // Mode change is only supported on PowerVM systems
277 log<level::DEBUG>(
278 "PowerMode::sendModeChange: MODE CHANGE does not get sent on non-PowerVM systems");
279 return CmdStatus::SUCCESS;
280 }
281
Chris Cain1be43372021-12-09 19:29:37 -0600282 SysPwrMode newMode;
283 uint16_t oemModeData = 0;
284 getMode(newMode, oemModeData);
Chris Cain36f9cde2021-11-22 11:18:21 -0600285
286 if (VALID_POWER_MODE_SETTING(newMode) ||
287 VALID_OEM_POWER_MODE_SETTING(newMode))
288 {
289 std::vector<std::uint8_t> cmd, rsp;
290 cmd.reserve(9);
291 cmd.push_back(uint8_t(CmdType::SET_MODE_AND_STATE));
292 cmd.push_back(0x00); // Data Length (2 bytes)
293 cmd.push_back(0x06);
294 cmd.push_back(0x30); // Data (Version)
295 cmd.push_back(uint8_t(OccState::NO_CHANGE));
296 cmd.push_back(uint8_t(newMode));
Chris Cain1be43372021-12-09 19:29:37 -0600297 cmd.push_back(oemModeData >> 8); // Mode Data (Freq Point)
298 cmd.push_back(oemModeData & 0xFF); //
299 cmd.push_back(0x00); // reserved
Chris Cain36f9cde2021-11-22 11:18:21 -0600300 log<level::INFO>(
301 fmt::format(
302 "PowerMode::sendModeChange: SET_MODE({},{}) command to OCC{} ({} bytes)",
Chris Cain1be43372021-12-09 19:29:37 -0600303 newMode, oemModeData, occInstance, cmd.size())
Chris Cain36f9cde2021-11-22 11:18:21 -0600304 .c_str());
Chris Cain6fa848a2022-01-24 14:54:38 -0600305 status = occCmd->send(cmd, rsp);
Chris Cain36f9cde2021-11-22 11:18:21 -0600306 if (status == CmdStatus::SUCCESS)
307 {
308 if (rsp.size() == 5)
309 {
310 if (RspStatus::SUCCESS != RspStatus(rsp[2]))
311 {
312 log<level::ERR>(
313 fmt::format(
314 "PowerMode::sendModeChange: SET MODE failed with status 0x{:02X}",
315 rsp[2])
316 .c_str());
317 dump_hex(rsp);
318 status = CmdStatus::FAILURE;
319 }
320 }
321 else
322 {
323 log<level::ERR>(
324 "PowerMode::sendModeChange: INVALID SET MODE response");
325 dump_hex(rsp);
326 status = CmdStatus::FAILURE;
327 }
328 }
329 else
330 {
331 if (status == CmdStatus::OPEN_FAILURE)
332 {
333 // OCC not active yet
334 status = CmdStatus::SUCCESS;
335 }
336 else
337 {
338 log<level::ERR>("PowerMode::sendModeChange: SET_MODE FAILED!");
339 }
340 }
341 }
342 else
343 {
344 log<level::ERR>(
345 fmt::format(
346 "PowerMode::sendModeChange: Unable to set power mode to {}",
347 newMode)
348 .c_str());
349 status = CmdStatus::FAILURE;
350 }
351
352 return status;
353}
354
355void PowerMode::ipsChanged(sdbusplus::message::message& msg)
356{
Chris Cain1d51da22021-09-21 14:13:41 -0500357 bool parmsChanged = false;
358 std::string interface;
359 std::map<std::string, std::variant<bool, uint8_t, uint64_t>>
360 ipsProperties{};
361 msg.read(interface, ipsProperties);
362
Chris Cain1be43372021-12-09 19:29:37 -0600363 // Read persisted values
364 bool ipsEnabled;
365 uint8_t enterUtil, exitUtil;
366 uint16_t enterTime, exitTime;
367 getIPSParms(ipsEnabled, enterUtil, enterTime, exitUtil, exitTime);
368
369 // Check for any changed data
Chris Cain1d51da22021-09-21 14:13:41 -0500370 auto ipsEntry = ipsProperties.find(IPS_ENABLED_PROP);
371 if (ipsEntry != ipsProperties.end())
372 {
Chris Cain1be43372021-12-09 19:29:37 -0600373 ipsEnabled = std::get<bool>(ipsEntry->second);
Chris Cain1d51da22021-09-21 14:13:41 -0500374 log<level::INFO>(
375 fmt::format("Idle Power Saver change: Enabled={}", ipsEnabled)
376 .c_str());
377 parmsChanged = true;
378 }
379 ipsEntry = ipsProperties.find(IPS_ENTER_UTIL);
380 if (ipsEntry != ipsProperties.end())
381 {
Chris Cain1be43372021-12-09 19:29:37 -0600382 enterUtil = std::get<uint8_t>(ipsEntry->second);
Chris Cain1d51da22021-09-21 14:13:41 -0500383 log<level::INFO>(
384 fmt::format("Idle Power Saver change: Enter Util={}%", enterUtil)
385 .c_str());
386 parmsChanged = true;
387 }
388 ipsEntry = ipsProperties.find(IPS_ENTER_TIME);
389 if (ipsEntry != ipsProperties.end())
390 {
391 std::chrono::milliseconds ms(std::get<uint64_t>(ipsEntry->second));
Chris Cain1be43372021-12-09 19:29:37 -0600392 enterTime =
Chris Cain1d51da22021-09-21 14:13:41 -0500393 std::chrono::duration_cast<std::chrono::seconds>(ms).count();
394 log<level::INFO>(
395 fmt::format("Idle Power Saver change: Enter Time={}sec", enterTime)
396 .c_str());
397 parmsChanged = true;
398 }
399 ipsEntry = ipsProperties.find(IPS_EXIT_UTIL);
400 if (ipsEntry != ipsProperties.end())
401 {
Chris Cain1be43372021-12-09 19:29:37 -0600402 exitUtil = std::get<uint8_t>(ipsEntry->second);
Chris Cain1d51da22021-09-21 14:13:41 -0500403 log<level::INFO>(
404 fmt::format("Idle Power Saver change: Exit Util={}%", exitUtil)
405 .c_str());
406 parmsChanged = true;
407 }
408 ipsEntry = ipsProperties.find(IPS_EXIT_TIME);
409 if (ipsEntry != ipsProperties.end())
410 {
411 std::chrono::milliseconds ms(std::get<uint64_t>(ipsEntry->second));
Chris Cain1be43372021-12-09 19:29:37 -0600412 exitTime = std::chrono::duration_cast<std::chrono::seconds>(ms).count();
Chris Cain1d51da22021-09-21 14:13:41 -0500413 log<level::INFO>(
414 fmt::format("Idle Power Saver change: Exit Time={}sec", exitTime)
415 .c_str());
416 parmsChanged = true;
417 }
418
419 if (parmsChanged)
420 {
Chris Caincde7bea2022-01-28 15:54:24 -0600421 if (exitUtil == 0)
422 {
423 // Setting the exitUtil to 0 will force restoring the default IPS
424 // parmeters (0 is not valid exit utilization)
425 log<level::INFO>(
426 "Idle Power Saver Exit Utilization is 0%. Restoring default parameters");
427 // Read the default IPS parameters, write persistent file and update
428 // DBus
429 useDefaultIPSParms();
430 }
431 else
432 {
433 // Update persistant data with new DBus values
434 persistedData.updateIPS(ipsEnabled, enterUtil, enterTime, exitUtil,
435 exitTime);
436 }
Chris Cain1be43372021-12-09 19:29:37 -0600437
438 // Trigger IPS data to get sent to the OCC
Chris Cain36f9cde2021-11-22 11:18:21 -0600439 sendIpsData();
Chris Cain1d51da22021-09-21 14:13:41 -0500440 }
441
442 return;
443}
444
Chris Cain1be43372021-12-09 19:29:37 -0600445/** @brief Get the Idle Power Saver properties from persisted data
446 * @return true if IPS parameters were read
Chris Cain36f9cde2021-11-22 11:18:21 -0600447 */
Chris Cain1be43372021-12-09 19:29:37 -0600448bool PowerMode::getIPSParms(bool& ipsEnabled, uint8_t& enterUtil,
449 uint16_t& enterTime, uint8_t& exitUtil,
450 uint16_t& exitTime)
Chris Cain36f9cde2021-11-22 11:18:21 -0600451{
Chris Cain36f9cde2021-11-22 11:18:21 -0600452 // Defaults:
Chris Cain1be43372021-12-09 19:29:37 -0600453 ipsEnabled = true; // Enabled
454 enterUtil = 8; // Enter Utilization (8%)
455 enterTime = 240; // Enter Delay Time (240s)
456 exitUtil = 12; // Exit Utilization (12%)
457 exitTime = 10; // Exit Delay Time (10s)
Chris Cain36f9cde2021-11-22 11:18:21 -0600458
Chris Cain1be43372021-12-09 19:29:37 -0600459 if (!persistedData.getIPS(ipsEnabled, enterUtil, enterTime, exitUtil,
460 exitTime))
461 {
462 // Persistent data not initialized, read defaults and update DBus
463 if (!initPersistentData())
464 {
465 // Unable to read defaults from entity manager yet
466 return false;
467 }
Chris Cain36f9cde2021-11-22 11:18:21 -0600468
Chris Cain1be43372021-12-09 19:29:37 -0600469 persistedData.getIPS(ipsEnabled, enterUtil, enterTime, exitUtil,
470 exitTime);
Chris Cain36f9cde2021-11-22 11:18:21 -0600471 }
472
473 if (enterUtil > exitUtil)
474 {
475 log<level::ERR>(
476 fmt::format(
477 "ERROR: Idle Power Saver Enter Utilization ({}%) is > Exit Utilization ({}%) - using Exit for both",
478 enterUtil, exitUtil)
479 .c_str());
480 enterUtil = exitUtil;
481 }
482
Chris Cain1be43372021-12-09 19:29:37 -0600483 return true;
484}
485
486// Set the Idle Power Saver data on DBus
487bool PowerMode::updateDbusIPS(const bool enabled, const uint8_t enterUtil,
488 const uint16_t enterTime, const uint8_t exitUtil,
489 const uint16_t exitTime)
490{
491 // true = skip update signal
492 IpsInterface::setPropertyByName(IPS_ENABLED_PROP, enabled, true);
493 IpsInterface::setPropertyByName(IPS_ENTER_UTIL, enterUtil, true);
494 // Convert time from seconds to ms
495 uint64_t msTime = enterTime * 1000;
496 IpsInterface::setPropertyByName(IPS_ENTER_TIME, msTime, true);
497 IpsInterface::setPropertyByName(IPS_EXIT_UTIL, exitUtil, true);
498 msTime = exitTime * 1000;
499 IpsInterface::setPropertyByName(IPS_EXIT_TIME, msTime, true);
500
501 return true;
Chris Cain36f9cde2021-11-22 11:18:21 -0600502}
503
504// Send Idle Power Saver config data to the master OCC
505CmdStatus PowerMode::sendIpsData()
506{
Chris Cain6fa848a2022-01-24 14:54:38 -0600507 if (!masterActive || !masterOccSet)
Chris Cain36f9cde2021-11-22 11:18:21 -0600508 {
509 // Nothing to do
510 return CmdStatus::SUCCESS;
511 }
512
513 if (!isPowerVM())
514 {
515 // Idle Power Saver data is only supported on PowerVM systems
516 log<level::DEBUG>(
517 "PowerMode::sendIpsData: SET_CFG_DATA[IPS] does not get sent on non-PowerVM systems");
518 return CmdStatus::SUCCESS;
519 }
520
Chris Cain1be43372021-12-09 19:29:37 -0600521 bool ipsEnabled;
Chris Cain36f9cde2021-11-22 11:18:21 -0600522 uint8_t enterUtil, exitUtil;
523 uint16_t enterTime, exitTime;
Chris Cain1be43372021-12-09 19:29:37 -0600524 getIPSParms(ipsEnabled, enterUtil, enterTime, exitUtil, exitTime);
Chris Cain36f9cde2021-11-22 11:18:21 -0600525
526 log<level::INFO>(
527 fmt::format(
528 "Idle Power Saver Parameters: enabled:{}, enter:{}%/{}s, exit:{}%/{}s",
529 ipsEnabled, enterUtil, enterTime, exitUtil, exitTime)
530 .c_str());
531
532 std::vector<std::uint8_t> cmd, rsp;
533 cmd.reserve(12);
534 cmd.push_back(uint8_t(CmdType::SET_CONFIG_DATA));
535 cmd.push_back(0x00); // Data Length (2 bytes)
536 cmd.push_back(0x09); //
537 cmd.push_back(0x11); // Config Format: IPS Settings
538 cmd.push_back(0x00); // Version
539 cmd.push_back(ipsEnabled ? 1 : 0); // IPS Enable
540 cmd.push_back(enterTime >> 8); // Enter Delay Time
541 cmd.push_back(enterTime & 0xFF); //
542 cmd.push_back(enterUtil); // Enter Utilization
543 cmd.push_back(exitTime >> 8); // Exit Delay Time
544 cmd.push_back(exitTime & 0xFF); //
545 cmd.push_back(exitUtil); // Exit Utilization
546 log<level::INFO>(fmt::format("PowerMode::sendIpsData: SET_CFG_DATA[IPS] "
547 "command to OCC{} ({} bytes)",
548 occInstance, cmd.size())
549 .c_str());
Chris Cain1be43372021-12-09 19:29:37 -0600550 CmdStatus status = occCmd->send(cmd, rsp);
Chris Cain36f9cde2021-11-22 11:18:21 -0600551 if (status == CmdStatus::SUCCESS)
552 {
553 if (rsp.size() == 5)
554 {
555 if (RspStatus::SUCCESS != RspStatus(rsp[2]))
556 {
557 log<level::ERR>(
558 fmt::format(
559 "PowerMode::sendIpsData: SET_CFG_DATA[IPS] failed with status 0x{:02X}",
560 rsp[2])
561 .c_str());
562 dump_hex(rsp);
563 status = CmdStatus::FAILURE;
564 }
565 }
566 else
567 {
568 log<level::ERR>(
569 "PowerMode::sendIpsData: INVALID SET_CFG_DATA[IPS] response");
570 dump_hex(rsp);
571 status = CmdStatus::FAILURE;
572 }
573 }
574 else
575 {
576 if (status == CmdStatus::OPEN_FAILURE)
577 {
578 // OCC not active yet
579 status = CmdStatus::SUCCESS;
580 }
581 else
582 {
583 log<level::ERR>(
584 "PowerMode::sendIpsData: SET_CFG_DATA[IPS] FAILED!");
585 }
586 }
587
588 return status;
589}
590
Chris Caincde7bea2022-01-28 15:54:24 -0600591// Print the current values
Chris Cain1be43372021-12-09 19:29:37 -0600592void OccPersistData::print()
Chris Cain36f9cde2021-11-22 11:18:21 -0600593{
Chris Cain1be43372021-12-09 19:29:37 -0600594 if (modeData.modeInitialized)
595 {
596 log<level::INFO>(
597 fmt::format(
598 "OccPersistData: Mode: 0x{:02X}, OEM Mode Data: {} (0x{:04X})",
599 modeData.mode, modeData.oemModeData, modeData.oemModeData)
600 .c_str());
601 }
602 if (modeData.ipsInitialized)
603 {
604 log<level::INFO>(
605 fmt::format(
606 "OccPersistData: IPS enabled:{}, enter:{}%/{}s, exit:{}%/{}s",
607 modeData.ipsEnabled, modeData.ipsEnterUtil,
608 modeData.ipsEnterTime, modeData.ipsExitUtil,
609 modeData.ipsExitTime)
610 .c_str());
611 }
Chris Cain36f9cde2021-11-22 11:18:21 -0600612}
613
614// Saves the OEM mode data in the filesystem using cereal.
615void OccPersistData::save()
616{
617 std::filesystem::path opath =
Chris Cain1be43372021-12-09 19:29:37 -0600618 std::filesystem::path{OCC_CONTROL_PERSIST_PATH} / powerModeFilename;
Chris Cain36f9cde2021-11-22 11:18:21 -0600619
620 if (!std::filesystem::exists(opath.parent_path()))
621 {
622 std::filesystem::create_directory(opath.parent_path());
623 }
624
625 log<level::DEBUG>(
Chris Cain1be43372021-12-09 19:29:37 -0600626 fmt::format(
627 "OccPersistData::save: Writing Power Mode persisted data to {}",
628 opath.c_str())
Chris Cain36f9cde2021-11-22 11:18:21 -0600629 .c_str());
Chris Caincde7bea2022-01-28 15:54:24 -0600630 // print();
Chris Cain36f9cde2021-11-22 11:18:21 -0600631
632 std::ofstream stream{opath.c_str()};
633 cereal::JSONOutputArchive oarchive{stream};
634
Chris Cain1be43372021-12-09 19:29:37 -0600635 oarchive(modeData);
Chris Cain36f9cde2021-11-22 11:18:21 -0600636}
637
638// Loads the OEM mode data in the filesystem using cereal.
639void OccPersistData::load()
640{
641
642 std::filesystem::path ipath =
Chris Cain1be43372021-12-09 19:29:37 -0600643 std::filesystem::path{OCC_CONTROL_PERSIST_PATH} / powerModeFilename;
Chris Cain36f9cde2021-11-22 11:18:21 -0600644
645 if (!std::filesystem::exists(ipath))
646 {
Chris Cain1be43372021-12-09 19:29:37 -0600647 modeData.modeInitialized = false;
648 modeData.ipsInitialized = false;
Chris Cain36f9cde2021-11-22 11:18:21 -0600649 return;
650 }
651
652 log<level::DEBUG>(
Chris Cain1be43372021-12-09 19:29:37 -0600653 fmt::format(
654 "OccPersistData::load: Reading Power Mode persisted data from {}",
655 ipath.c_str())
Chris Cain36f9cde2021-11-22 11:18:21 -0600656 .c_str());
657 try
658 {
659 std::ifstream stream{ipath.c_str()};
660 cereal::JSONInputArchive iarchive(stream);
Chris Cain1be43372021-12-09 19:29:37 -0600661 iarchive(modeData);
Chris Cain36f9cde2021-11-22 11:18:21 -0600662 }
663 catch (const std::exception& e)
664 {
665 auto error = errno;
666 log<level::ERR>(
667 fmt::format("OccPersistData::load: failed to read {}, errno={}",
668 ipath.c_str(), error)
669 .c_str());
Chris Cain1be43372021-12-09 19:29:37 -0600670 modeData.modeInitialized = false;
671 modeData.ipsInitialized = false;
Chris Cain36f9cde2021-11-22 11:18:21 -0600672 }
673
Chris Caincde7bea2022-01-28 15:54:24 -0600674 // print();
Chris Cain36f9cde2021-11-22 11:18:21 -0600675}
676
Chris Cain1be43372021-12-09 19:29:37 -0600677// Called when PowerModeProperties defaults are available on DBus
678void PowerMode::defaultsReady(sdbusplus::message::message& msg)
Chris Cain36f9cde2021-11-22 11:18:21 -0600679{
Chris Cain1be43372021-12-09 19:29:37 -0600680 std::map<std::string, std::variant<std::string>> properties{};
681 std::string interface;
682 msg.read(interface, properties);
Chris Cain36f9cde2021-11-22 11:18:21 -0600683
Chris Cain1be43372021-12-09 19:29:37 -0600684 // If persistent data exists, then don't need to read defaults
685 if ((!persistedData.modeAvailable()) || (!persistedData.ipsAvailable()))
Chris Cain36f9cde2021-11-22 11:18:21 -0600686 {
Chris Cain1be43372021-12-09 19:29:37 -0600687 log<level::INFO>(
688 fmt::format(
689 "Default PowerModeProperties are now available (persistent modeAvail={}, ipsAvail={})",
690 persistedData.modeAvailable() ? 'y' : 'n',
691 persistedData.modeAvailable() ? 'y' : 'n')
692 .c_str());
693
694 // Read default power mode defaults and update DBus
695 initPersistentData();
696 }
697}
698
699// Get the default power mode from DBus and return true if success
700bool PowerMode::getDefaultMode(SysPwrMode& defaultMode)
701{
702 try
703 {
704 auto& bus = utils::getBus();
705 std::string path = "/";
706 std::string service =
707 utils::getServiceUsingSubTree(PMODE_DEFAULT_INTERFACE, path);
708 auto method =
709 bus.new_method_call(service.c_str(), path.c_str(),
710 "org.freedesktop.DBus.Properties", "Get");
711 method.append(PMODE_DEFAULT_INTERFACE, "PowerMode");
712 auto reply = bus.call(method);
713
714 std::variant<std::string> stateEntryValue;
715 reply.read(stateEntryValue);
716 auto propVal = std::get<std::string>(stateEntryValue);
717
718 const std::string fullModeString =
719 PMODE_INTERFACE + ".PowerMode."s + propVal;
720 defaultMode = powermode::convertStringToMode(fullModeString);
721 if (!VALID_POWER_MODE_SETTING(defaultMode))
722 {
723 log<level::ERR>(
724 fmt::format(
725 "PowerMode::getDefaultMode: Invalid default power mode found: {}",
726 defaultMode)
727 .c_str());
728 // If default was read but not valid, use Max Performance
729 defaultMode = SysPwrMode::MAX_PERF;
730 return true;
731 }
732 }
733 catch (const sdbusplus::exception::exception& e)
734 {
735 log<level::ERR>(
736 fmt::format("Unable to read Default Power Mode: {}", e.what())
737 .c_str());
738 return false;
Chris Cain36f9cde2021-11-22 11:18:21 -0600739 }
740
Chris Cain1be43372021-12-09 19:29:37 -0600741 return true;
742}
Chris Cain36f9cde2021-11-22 11:18:21 -0600743
Chris Cain1be43372021-12-09 19:29:37 -0600744/* Get the default Idle Power Saver properties and return true if successful */
745bool PowerMode::getDefaultIPSParms(bool& ipsEnabled, uint8_t& enterUtil,
746 uint16_t& enterTime, uint8_t& exitUtil,
747 uint16_t& exitTime)
748{
749 // Defaults:
750 ipsEnabled = true; // Enabled
751 enterUtil = 8; // Enter Utilization (8%)
752 enterTime = 240; // Enter Delay Time (240s)
753 exitUtil = 12; // Exit Utilization (12%)
754 exitTime = 10; // Exit Delay Time (10s)
755
756 std::map<std::string, std::variant<bool, uint8_t, uint16_t, uint64_t>>
757 ipsProperties{};
758
759 // Get all IPS properties from DBus
760 try
761 {
762 auto& bus = utils::getBus();
763 std::string path = "/";
764 std::string service =
765 utils::getServiceUsingSubTree(PMODE_DEFAULT_INTERFACE, path);
766 auto method =
767 bus.new_method_call(service.c_str(), path.c_str(),
768 "org.freedesktop.DBus.Properties", "GetAll");
769 method.append(PMODE_DEFAULT_INTERFACE);
770 auto reply = bus.call(method);
771 reply.read(ipsProperties);
772 }
773 catch (const sdbusplus::exception::exception& e)
774 {
775 log<level::ERR>(
776 fmt::format(
777 "Unable to read Default Idle Power Saver parameters so it will be disabled: {}",
778 e.what())
779 .c_str());
780 return false;
781 }
782
783 auto ipsEntry = ipsProperties.find("IdlePowerSaverEnabled");
784 if (ipsEntry != ipsProperties.end())
785 {
786 ipsEnabled = std::get<bool>(ipsEntry->second);
787 }
788 else
789 {
790 log<level::ERR>(
791 "PowerMode::getDefaultIPSParms could not find property: IdlePowerSaverEnabled");
792 }
793
794 ipsEntry = ipsProperties.find("EnterUtilizationPercent");
795 if (ipsEntry != ipsProperties.end())
796 {
797 enterUtil = std::get<uint64_t>(ipsEntry->second);
798 }
799 else
800 {
801 log<level::ERR>(
802 "PowerMode::getDefaultIPSParms could not find property: EnterUtilizationPercent");
803 }
804
805 ipsEntry = ipsProperties.find("EnterUtilizationDwellTime");
806 if (ipsEntry != ipsProperties.end())
807 {
808 enterTime = std::get<uint64_t>(ipsEntry->second);
809 }
810 else
811 {
812 log<level::ERR>(
813 "PowerMode::getDefaultIPSParms could not find property: EnterUtilizationDwellTime");
814 }
815
816 ipsEntry = ipsProperties.find("ExitUtilizationPercent");
817 if (ipsEntry != ipsProperties.end())
818 {
819 exitUtil = std::get<uint64_t>(ipsEntry->second);
820 }
821 else
822 {
823 log<level::ERR>(
824 "PowerMode::getDefaultIPSParms could not find property: ExitUtilizationPercent");
825 }
826
827 ipsEntry = ipsProperties.find("ExitUtilizationDwellTime");
828 if (ipsEntry != ipsProperties.end())
829 {
830 exitTime = std::get<uint64_t>(ipsEntry->second);
831 }
832 else
833 {
834 log<level::ERR>(
835 "PowerMode::getDefaultIPSParms could not find property: ExitUtilizationDwellTime");
836 }
837
838 if (enterUtil > exitUtil)
839 {
840 log<level::ERR>(
841 fmt::format(
842 "ERROR: Default Idle Power Saver Enter Utilization ({}%) is > Exit Utilization ({}%) - using Exit for both",
843 enterUtil, exitUtil)
844 .c_str());
845 enterUtil = exitUtil;
846 }
847
848 return true;
Chris Cain36f9cde2021-11-22 11:18:21 -0600849}
850
Chris Caincde7bea2022-01-28 15:54:24 -0600851/* Read default IPS parameters, save them to the persistent file and update
852 DBus. Return true if successful */
853bool PowerMode::useDefaultIPSParms()
854{
855 // Read the default IPS parameters
856 bool ipsEnabled;
857 uint8_t enterUtil, exitUtil;
858 uint16_t enterTime, exitTime;
859 if (!getDefaultIPSParms(ipsEnabled, enterUtil, enterTime, exitUtil,
860 exitTime))
861 {
862 // Unable to read defaults
863 return false;
864 }
865 log<level::INFO>(
866 fmt::format(
867 "PowerMode::useDefaultIPSParms: Using default IPS parms: Enabled: {}, EnterUtil: {}%, EnterTime: {}s, ExitUtil: {}%, ExitTime: {}s",
868 ipsEnabled, enterUtil, enterTime, exitUtil, exitTime)
869 .c_str());
870
871 // Save IPS parms to the persistent file
872 persistedData.updateIPS(ipsEnabled, enterUtil, enterTime, exitUtil,
873 exitTime);
874
875 // Write IPS parms to DBus
876 return updateDbusIPS(ipsEnabled, enterUtil, enterTime, exitUtil, exitTime);
877}
878
Chris Cain78e86012021-03-04 16:15:31 -0600879} // namespace powermode
880
881} // namespace occ
882
883} // namespace open_power