blob: ed17096e00307f4faa274dd624193f74de459ad0 [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 {
Chris Cainc567dc82022-04-01 15:09:17 -0500331 log<level::ERR>(
332 fmt::format(
333 "PowerMode::sendModeChange: SET_MODE FAILED with status={}",
334 status)
335 .c_str());
Chris Cain36f9cde2021-11-22 11:18:21 -0600336 }
337 }
338 else
339 {
340 log<level::ERR>(
341 fmt::format(
342 "PowerMode::sendModeChange: Unable to set power mode to {}",
343 newMode)
344 .c_str());
345 status = CmdStatus::FAILURE;
346 }
347
348 return status;
349}
350
351void PowerMode::ipsChanged(sdbusplus::message::message& msg)
352{
Chris Cain1d51da22021-09-21 14:13:41 -0500353 bool parmsChanged = false;
354 std::string interface;
355 std::map<std::string, std::variant<bool, uint8_t, uint64_t>>
356 ipsProperties{};
357 msg.read(interface, ipsProperties);
358
Chris Cain1be43372021-12-09 19:29:37 -0600359 // Read persisted values
360 bool ipsEnabled;
361 uint8_t enterUtil, exitUtil;
362 uint16_t enterTime, exitTime;
363 getIPSParms(ipsEnabled, enterUtil, enterTime, exitUtil, exitTime);
364
365 // Check for any changed data
Chris Cain1d51da22021-09-21 14:13:41 -0500366 auto ipsEntry = ipsProperties.find(IPS_ENABLED_PROP);
367 if (ipsEntry != ipsProperties.end())
368 {
Chris Cain1be43372021-12-09 19:29:37 -0600369 ipsEnabled = std::get<bool>(ipsEntry->second);
Chris Cain1d51da22021-09-21 14:13:41 -0500370 log<level::INFO>(
371 fmt::format("Idle Power Saver change: Enabled={}", ipsEnabled)
372 .c_str());
373 parmsChanged = true;
374 }
375 ipsEntry = ipsProperties.find(IPS_ENTER_UTIL);
376 if (ipsEntry != ipsProperties.end())
377 {
Chris Cain1be43372021-12-09 19:29:37 -0600378 enterUtil = std::get<uint8_t>(ipsEntry->second);
Chris Cain1d51da22021-09-21 14:13:41 -0500379 log<level::INFO>(
380 fmt::format("Idle Power Saver change: Enter Util={}%", enterUtil)
381 .c_str());
382 parmsChanged = true;
383 }
384 ipsEntry = ipsProperties.find(IPS_ENTER_TIME);
385 if (ipsEntry != ipsProperties.end())
386 {
387 std::chrono::milliseconds ms(std::get<uint64_t>(ipsEntry->second));
Chris Cain1be43372021-12-09 19:29:37 -0600388 enterTime =
Chris Cain1d51da22021-09-21 14:13:41 -0500389 std::chrono::duration_cast<std::chrono::seconds>(ms).count();
390 log<level::INFO>(
391 fmt::format("Idle Power Saver change: Enter Time={}sec", enterTime)
392 .c_str());
393 parmsChanged = true;
394 }
395 ipsEntry = ipsProperties.find(IPS_EXIT_UTIL);
396 if (ipsEntry != ipsProperties.end())
397 {
Chris Cain1be43372021-12-09 19:29:37 -0600398 exitUtil = std::get<uint8_t>(ipsEntry->second);
Chris Cain1d51da22021-09-21 14:13:41 -0500399 log<level::INFO>(
400 fmt::format("Idle Power Saver change: Exit Util={}%", exitUtil)
401 .c_str());
402 parmsChanged = true;
403 }
404 ipsEntry = ipsProperties.find(IPS_EXIT_TIME);
405 if (ipsEntry != ipsProperties.end())
406 {
407 std::chrono::milliseconds ms(std::get<uint64_t>(ipsEntry->second));
Chris Cain1be43372021-12-09 19:29:37 -0600408 exitTime = std::chrono::duration_cast<std::chrono::seconds>(ms).count();
Chris Cain1d51da22021-09-21 14:13:41 -0500409 log<level::INFO>(
410 fmt::format("Idle Power Saver change: Exit Time={}sec", exitTime)
411 .c_str());
412 parmsChanged = true;
413 }
414
415 if (parmsChanged)
416 {
Chris Caincde7bea2022-01-28 15:54:24 -0600417 if (exitUtil == 0)
418 {
419 // Setting the exitUtil to 0 will force restoring the default IPS
420 // parmeters (0 is not valid exit utilization)
421 log<level::INFO>(
422 "Idle Power Saver Exit Utilization is 0%. Restoring default parameters");
423 // Read the default IPS parameters, write persistent file and update
424 // DBus
425 useDefaultIPSParms();
426 }
427 else
428 {
429 // Update persistant data with new DBus values
430 persistedData.updateIPS(ipsEnabled, enterUtil, enterTime, exitUtil,
431 exitTime);
432 }
Chris Cain1be43372021-12-09 19:29:37 -0600433
434 // Trigger IPS data to get sent to the OCC
Chris Cain36f9cde2021-11-22 11:18:21 -0600435 sendIpsData();
Chris Cain1d51da22021-09-21 14:13:41 -0500436 }
437
438 return;
439}
440
Chris Cain1be43372021-12-09 19:29:37 -0600441/** @brief Get the Idle Power Saver properties from persisted data
442 * @return true if IPS parameters were read
Chris Cain36f9cde2021-11-22 11:18:21 -0600443 */
Chris Cain1be43372021-12-09 19:29:37 -0600444bool PowerMode::getIPSParms(bool& ipsEnabled, uint8_t& enterUtil,
445 uint16_t& enterTime, uint8_t& exitUtil,
446 uint16_t& exitTime)
Chris Cain36f9cde2021-11-22 11:18:21 -0600447{
Chris Cain36f9cde2021-11-22 11:18:21 -0600448 // Defaults:
Chris Cain1be43372021-12-09 19:29:37 -0600449 ipsEnabled = true; // Enabled
450 enterUtil = 8; // Enter Utilization (8%)
451 enterTime = 240; // Enter Delay Time (240s)
452 exitUtil = 12; // Exit Utilization (12%)
453 exitTime = 10; // Exit Delay Time (10s)
Chris Cain36f9cde2021-11-22 11:18:21 -0600454
Chris Cain1be43372021-12-09 19:29:37 -0600455 if (!persistedData.getIPS(ipsEnabled, enterUtil, enterTime, exitUtil,
456 exitTime))
457 {
458 // Persistent data not initialized, read defaults and update DBus
459 if (!initPersistentData())
460 {
461 // Unable to read defaults from entity manager yet
462 return false;
463 }
Chris Cain36f9cde2021-11-22 11:18:21 -0600464
Chris Cain1be43372021-12-09 19:29:37 -0600465 persistedData.getIPS(ipsEnabled, enterUtil, enterTime, exitUtil,
466 exitTime);
Chris Cain36f9cde2021-11-22 11:18:21 -0600467 }
468
469 if (enterUtil > exitUtil)
470 {
471 log<level::ERR>(
472 fmt::format(
473 "ERROR: Idle Power Saver Enter Utilization ({}%) is > Exit Utilization ({}%) - using Exit for both",
474 enterUtil, exitUtil)
475 .c_str());
476 enterUtil = exitUtil;
477 }
478
Chris Cain1be43372021-12-09 19:29:37 -0600479 return true;
480}
481
482// Set the Idle Power Saver data on DBus
483bool PowerMode::updateDbusIPS(const bool enabled, const uint8_t enterUtil,
484 const uint16_t enterTime, const uint8_t exitUtil,
485 const uint16_t exitTime)
486{
487 // true = skip update signal
488 IpsInterface::setPropertyByName(IPS_ENABLED_PROP, enabled, true);
489 IpsInterface::setPropertyByName(IPS_ENTER_UTIL, enterUtil, true);
490 // Convert time from seconds to ms
491 uint64_t msTime = enterTime * 1000;
492 IpsInterface::setPropertyByName(IPS_ENTER_TIME, msTime, true);
493 IpsInterface::setPropertyByName(IPS_EXIT_UTIL, exitUtil, true);
494 msTime = exitTime * 1000;
495 IpsInterface::setPropertyByName(IPS_EXIT_TIME, msTime, true);
496
497 return true;
Chris Cain36f9cde2021-11-22 11:18:21 -0600498}
499
500// Send Idle Power Saver config data to the master OCC
501CmdStatus PowerMode::sendIpsData()
502{
Chris Cain6fa848a2022-01-24 14:54:38 -0600503 if (!masterActive || !masterOccSet)
Chris Cain36f9cde2021-11-22 11:18:21 -0600504 {
505 // Nothing to do
506 return CmdStatus::SUCCESS;
507 }
508
509 if (!isPowerVM())
510 {
511 // Idle Power Saver data is only supported on PowerVM systems
512 log<level::DEBUG>(
513 "PowerMode::sendIpsData: SET_CFG_DATA[IPS] does not get sent on non-PowerVM systems");
514 return CmdStatus::SUCCESS;
515 }
516
Chris Cain1be43372021-12-09 19:29:37 -0600517 bool ipsEnabled;
Chris Cain36f9cde2021-11-22 11:18:21 -0600518 uint8_t enterUtil, exitUtil;
519 uint16_t enterTime, exitTime;
Chris Cain1be43372021-12-09 19:29:37 -0600520 getIPSParms(ipsEnabled, enterUtil, enterTime, exitUtil, exitTime);
Chris Cain36f9cde2021-11-22 11:18:21 -0600521
522 log<level::INFO>(
523 fmt::format(
524 "Idle Power Saver Parameters: enabled:{}, enter:{}%/{}s, exit:{}%/{}s",
525 ipsEnabled, enterUtil, enterTime, exitUtil, exitTime)
526 .c_str());
527
528 std::vector<std::uint8_t> cmd, rsp;
529 cmd.reserve(12);
530 cmd.push_back(uint8_t(CmdType::SET_CONFIG_DATA));
531 cmd.push_back(0x00); // Data Length (2 bytes)
532 cmd.push_back(0x09); //
533 cmd.push_back(0x11); // Config Format: IPS Settings
534 cmd.push_back(0x00); // Version
535 cmd.push_back(ipsEnabled ? 1 : 0); // IPS Enable
536 cmd.push_back(enterTime >> 8); // Enter Delay Time
537 cmd.push_back(enterTime & 0xFF); //
538 cmd.push_back(enterUtil); // Enter Utilization
539 cmd.push_back(exitTime >> 8); // Exit Delay Time
540 cmd.push_back(exitTime & 0xFF); //
541 cmd.push_back(exitUtil); // Exit Utilization
542 log<level::INFO>(fmt::format("PowerMode::sendIpsData: SET_CFG_DATA[IPS] "
543 "command to OCC{} ({} bytes)",
544 occInstance, cmd.size())
545 .c_str());
Chris Cain1be43372021-12-09 19:29:37 -0600546 CmdStatus status = occCmd->send(cmd, rsp);
Chris Cain36f9cde2021-11-22 11:18:21 -0600547 if (status == CmdStatus::SUCCESS)
548 {
549 if (rsp.size() == 5)
550 {
551 if (RspStatus::SUCCESS != RspStatus(rsp[2]))
552 {
553 log<level::ERR>(
554 fmt::format(
555 "PowerMode::sendIpsData: SET_CFG_DATA[IPS] failed with status 0x{:02X}",
556 rsp[2])
557 .c_str());
558 dump_hex(rsp);
559 status = CmdStatus::FAILURE;
560 }
561 }
562 else
563 {
564 log<level::ERR>(
565 "PowerMode::sendIpsData: INVALID SET_CFG_DATA[IPS] response");
566 dump_hex(rsp);
567 status = CmdStatus::FAILURE;
568 }
569 }
570 else
571 {
Chris Cainc567dc82022-04-01 15:09:17 -0500572 log<level::ERR>(
573 fmt::format(
574 "PowerMode::sendIpsData: SET_CFG_DATA[IPS] with status={}",
575 status)
576 .c_str());
Chris Cain36f9cde2021-11-22 11:18:21 -0600577 }
578
579 return status;
580}
581
Chris Caincde7bea2022-01-28 15:54:24 -0600582// Print the current values
Chris Cain1be43372021-12-09 19:29:37 -0600583void OccPersistData::print()
Chris Cain36f9cde2021-11-22 11:18:21 -0600584{
Chris Cain1be43372021-12-09 19:29:37 -0600585 if (modeData.modeInitialized)
586 {
587 log<level::INFO>(
588 fmt::format(
589 "OccPersistData: Mode: 0x{:02X}, OEM Mode Data: {} (0x{:04X})",
590 modeData.mode, modeData.oemModeData, modeData.oemModeData)
591 .c_str());
592 }
593 if (modeData.ipsInitialized)
594 {
595 log<level::INFO>(
596 fmt::format(
597 "OccPersistData: IPS enabled:{}, enter:{}%/{}s, exit:{}%/{}s",
598 modeData.ipsEnabled, modeData.ipsEnterUtil,
599 modeData.ipsEnterTime, modeData.ipsExitUtil,
600 modeData.ipsExitTime)
601 .c_str());
602 }
Chris Cain36f9cde2021-11-22 11:18:21 -0600603}
604
605// Saves the OEM mode data in the filesystem using cereal.
606void OccPersistData::save()
607{
608 std::filesystem::path opath =
Chris Cain1be43372021-12-09 19:29:37 -0600609 std::filesystem::path{OCC_CONTROL_PERSIST_PATH} / powerModeFilename;
Chris Cain36f9cde2021-11-22 11:18:21 -0600610
611 if (!std::filesystem::exists(opath.parent_path()))
612 {
613 std::filesystem::create_directory(opath.parent_path());
614 }
615
616 log<level::DEBUG>(
Chris Cain1be43372021-12-09 19:29:37 -0600617 fmt::format(
618 "OccPersistData::save: Writing Power Mode persisted data to {}",
619 opath.c_str())
Chris Cain36f9cde2021-11-22 11:18:21 -0600620 .c_str());
Chris Caincde7bea2022-01-28 15:54:24 -0600621 // print();
Chris Cain36f9cde2021-11-22 11:18:21 -0600622
623 std::ofstream stream{opath.c_str()};
624 cereal::JSONOutputArchive oarchive{stream};
625
Chris Cain1be43372021-12-09 19:29:37 -0600626 oarchive(modeData);
Chris Cain36f9cde2021-11-22 11:18:21 -0600627}
628
629// Loads the OEM mode data in the filesystem using cereal.
630void OccPersistData::load()
631{
632
633 std::filesystem::path ipath =
Chris Cain1be43372021-12-09 19:29:37 -0600634 std::filesystem::path{OCC_CONTROL_PERSIST_PATH} / powerModeFilename;
Chris Cain36f9cde2021-11-22 11:18:21 -0600635
636 if (!std::filesystem::exists(ipath))
637 {
Chris Cain1be43372021-12-09 19:29:37 -0600638 modeData.modeInitialized = false;
639 modeData.ipsInitialized = false;
Chris Cain36f9cde2021-11-22 11:18:21 -0600640 return;
641 }
642
643 log<level::DEBUG>(
Chris Cain1be43372021-12-09 19:29:37 -0600644 fmt::format(
645 "OccPersistData::load: Reading Power Mode persisted data from {}",
646 ipath.c_str())
Chris Cain36f9cde2021-11-22 11:18:21 -0600647 .c_str());
648 try
649 {
650 std::ifstream stream{ipath.c_str()};
651 cereal::JSONInputArchive iarchive(stream);
Chris Cain1be43372021-12-09 19:29:37 -0600652 iarchive(modeData);
Chris Cain36f9cde2021-11-22 11:18:21 -0600653 }
654 catch (const std::exception& e)
655 {
656 auto error = errno;
657 log<level::ERR>(
658 fmt::format("OccPersistData::load: failed to read {}, errno={}",
659 ipath.c_str(), error)
660 .c_str());
Chris Cain1be43372021-12-09 19:29:37 -0600661 modeData.modeInitialized = false;
662 modeData.ipsInitialized = false;
Chris Cain36f9cde2021-11-22 11:18:21 -0600663 }
664
Chris Caincde7bea2022-01-28 15:54:24 -0600665 // print();
Chris Cain36f9cde2021-11-22 11:18:21 -0600666}
667
Chris Cain1be43372021-12-09 19:29:37 -0600668// Called when PowerModeProperties defaults are available on DBus
669void PowerMode::defaultsReady(sdbusplus::message::message& msg)
Chris Cain36f9cde2021-11-22 11:18:21 -0600670{
Chris Cain1be43372021-12-09 19:29:37 -0600671 std::map<std::string, std::variant<std::string>> properties{};
672 std::string interface;
673 msg.read(interface, properties);
Chris Cain36f9cde2021-11-22 11:18:21 -0600674
Chris Cain1be43372021-12-09 19:29:37 -0600675 // If persistent data exists, then don't need to read defaults
676 if ((!persistedData.modeAvailable()) || (!persistedData.ipsAvailable()))
Chris Cain36f9cde2021-11-22 11:18:21 -0600677 {
Chris Cain1be43372021-12-09 19:29:37 -0600678 log<level::INFO>(
679 fmt::format(
680 "Default PowerModeProperties are now available (persistent modeAvail={}, ipsAvail={})",
681 persistedData.modeAvailable() ? 'y' : 'n',
682 persistedData.modeAvailable() ? 'y' : 'n')
683 .c_str());
684
685 // Read default power mode defaults and update DBus
686 initPersistentData();
687 }
688}
689
690// Get the default power mode from DBus and return true if success
691bool PowerMode::getDefaultMode(SysPwrMode& defaultMode)
692{
693 try
694 {
695 auto& bus = utils::getBus();
696 std::string path = "/";
697 std::string service =
698 utils::getServiceUsingSubTree(PMODE_DEFAULT_INTERFACE, path);
699 auto method =
700 bus.new_method_call(service.c_str(), path.c_str(),
701 "org.freedesktop.DBus.Properties", "Get");
702 method.append(PMODE_DEFAULT_INTERFACE, "PowerMode");
703 auto reply = bus.call(method);
704
705 std::variant<std::string> stateEntryValue;
706 reply.read(stateEntryValue);
707 auto propVal = std::get<std::string>(stateEntryValue);
708
709 const std::string fullModeString =
710 PMODE_INTERFACE + ".PowerMode."s + propVal;
711 defaultMode = powermode::convertStringToMode(fullModeString);
712 if (!VALID_POWER_MODE_SETTING(defaultMode))
713 {
714 log<level::ERR>(
715 fmt::format(
716 "PowerMode::getDefaultMode: Invalid default power mode found: {}",
717 defaultMode)
718 .c_str());
719 // If default was read but not valid, use Max Performance
720 defaultMode = SysPwrMode::MAX_PERF;
721 return true;
722 }
723 }
724 catch (const sdbusplus::exception::exception& e)
725 {
726 log<level::ERR>(
727 fmt::format("Unable to read Default Power Mode: {}", e.what())
728 .c_str());
729 return false;
Chris Cain36f9cde2021-11-22 11:18:21 -0600730 }
731
Chris Cain1be43372021-12-09 19:29:37 -0600732 return true;
733}
Chris Cain36f9cde2021-11-22 11:18:21 -0600734
Chris Cain1be43372021-12-09 19:29:37 -0600735/* Get the default Idle Power Saver properties and return true if successful */
736bool PowerMode::getDefaultIPSParms(bool& ipsEnabled, uint8_t& enterUtil,
737 uint16_t& enterTime, uint8_t& exitUtil,
738 uint16_t& exitTime)
739{
740 // Defaults:
741 ipsEnabled = true; // Enabled
742 enterUtil = 8; // Enter Utilization (8%)
743 enterTime = 240; // Enter Delay Time (240s)
744 exitUtil = 12; // Exit Utilization (12%)
745 exitTime = 10; // Exit Delay Time (10s)
746
747 std::map<std::string, std::variant<bool, uint8_t, uint16_t, uint64_t>>
748 ipsProperties{};
749
750 // Get all IPS properties from DBus
751 try
752 {
753 auto& bus = utils::getBus();
754 std::string path = "/";
755 std::string service =
756 utils::getServiceUsingSubTree(PMODE_DEFAULT_INTERFACE, path);
757 auto method =
758 bus.new_method_call(service.c_str(), path.c_str(),
759 "org.freedesktop.DBus.Properties", "GetAll");
760 method.append(PMODE_DEFAULT_INTERFACE);
761 auto reply = bus.call(method);
762 reply.read(ipsProperties);
763 }
764 catch (const sdbusplus::exception::exception& e)
765 {
766 log<level::ERR>(
767 fmt::format(
768 "Unable to read Default Idle Power Saver parameters so it will be disabled: {}",
769 e.what())
770 .c_str());
771 return false;
772 }
773
774 auto ipsEntry = ipsProperties.find("IdlePowerSaverEnabled");
775 if (ipsEntry != ipsProperties.end())
776 {
777 ipsEnabled = std::get<bool>(ipsEntry->second);
778 }
779 else
780 {
781 log<level::ERR>(
782 "PowerMode::getDefaultIPSParms could not find property: IdlePowerSaverEnabled");
783 }
784
785 ipsEntry = ipsProperties.find("EnterUtilizationPercent");
786 if (ipsEntry != ipsProperties.end())
787 {
788 enterUtil = std::get<uint64_t>(ipsEntry->second);
789 }
790 else
791 {
792 log<level::ERR>(
793 "PowerMode::getDefaultIPSParms could not find property: EnterUtilizationPercent");
794 }
795
796 ipsEntry = ipsProperties.find("EnterUtilizationDwellTime");
797 if (ipsEntry != ipsProperties.end())
798 {
799 enterTime = std::get<uint64_t>(ipsEntry->second);
800 }
801 else
802 {
803 log<level::ERR>(
804 "PowerMode::getDefaultIPSParms could not find property: EnterUtilizationDwellTime");
805 }
806
807 ipsEntry = ipsProperties.find("ExitUtilizationPercent");
808 if (ipsEntry != ipsProperties.end())
809 {
810 exitUtil = std::get<uint64_t>(ipsEntry->second);
811 }
812 else
813 {
814 log<level::ERR>(
815 "PowerMode::getDefaultIPSParms could not find property: ExitUtilizationPercent");
816 }
817
818 ipsEntry = ipsProperties.find("ExitUtilizationDwellTime");
819 if (ipsEntry != ipsProperties.end())
820 {
821 exitTime = std::get<uint64_t>(ipsEntry->second);
822 }
823 else
824 {
825 log<level::ERR>(
826 "PowerMode::getDefaultIPSParms could not find property: ExitUtilizationDwellTime");
827 }
828
829 if (enterUtil > exitUtil)
830 {
831 log<level::ERR>(
832 fmt::format(
833 "ERROR: Default Idle Power Saver Enter Utilization ({}%) is > Exit Utilization ({}%) - using Exit for both",
834 enterUtil, exitUtil)
835 .c_str());
836 enterUtil = exitUtil;
837 }
838
839 return true;
Chris Cain36f9cde2021-11-22 11:18:21 -0600840}
841
Chris Caincde7bea2022-01-28 15:54:24 -0600842/* Read default IPS parameters, save them to the persistent file and update
843 DBus. Return true if successful */
844bool PowerMode::useDefaultIPSParms()
845{
846 // Read the default IPS parameters
847 bool ipsEnabled;
848 uint8_t enterUtil, exitUtil;
849 uint16_t enterTime, exitTime;
850 if (!getDefaultIPSParms(ipsEnabled, enterUtil, enterTime, exitUtil,
851 exitTime))
852 {
853 // Unable to read defaults
854 return false;
855 }
856 log<level::INFO>(
857 fmt::format(
858 "PowerMode::useDefaultIPSParms: Using default IPS parms: Enabled: {}, EnterUtil: {}%, EnterTime: {}s, ExitUtil: {}%, ExitTime: {}s",
859 ipsEnabled, enterUtil, enterTime, exitUtil, exitTime)
860 .c_str());
861
862 // Save IPS parms to the persistent file
863 persistedData.updateIPS(ipsEnabled, enterUtil, enterTime, exitUtil,
864 exitTime);
865
866 // Write IPS parms to DBus
867 return updateDbusIPS(ipsEnabled, enterUtil, enterTime, exitUtil, exitTime);
868}
869
Chris Cain78e86012021-03-04 16:15:31 -0600870} // namespace powermode
871
872} // namespace occ
873
874} // namespace open_power