blob: fd8679d83fd1e812fdcdbbd5ad3e499c7002854b [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 {
198 // Read the default IPS parameters
199 bool ipsEnabled;
200 uint8_t enterUtil, exitUtil;
201 uint16_t enterTime, exitTime;
202 if (!getDefaultIPSParms(ipsEnabled, enterUtil, enterTime, exitUtil,
203 exitTime))
204 {
205 // Unable to read defaults
206 return false;
207 }
208 log<level::INFO>(
209 fmt::format(
210 "PowerMode::initPersistentData: Using default IPS parms: Enabled: {}, EnterUtil: {}%, EnterTime: {}s, ExitUtil: {}%, ExitTime: {}s",
211 ipsEnabled, enterUtil, enterTime, exitUtil, exitTime)
212 .c_str());
213
214 // Save IPS
215 persistedData.updateIPS(ipsEnabled, enterUtil, enterTime, exitUtil,
216 exitTime);
217
218 // Write default IPS data to DBus
219 updateDbusIPS(ipsEnabled, enterUtil, enterTime, exitUtil, exitTime);
220 }
221 return true;
222}
223
224// Get the requested power mode and return true if successful
225bool PowerMode::getMode(SysPwrMode& currentMode, uint16_t& oemModeData)
226{
227 currentMode = SysPwrMode::NO_CHANGE;
228 oemModeData = 0;
229
230 if (!persistedData.getMode(currentMode, oemModeData))
231 {
232 // Persistent data not initialized, read defaults and update DBus
233 if (!initPersistentData())
234 {
235 // Unable to read defaults from entity manager yet
236 return false;
237 }
238 return persistedData.getMode(currentMode, oemModeData);
239 }
240
241 return true;
Chris Cain36f9cde2021-11-22 11:18:21 -0600242}
243
244// Set the power mode on DBus
245bool PowerMode::updateDbusMode(const SysPwrMode newMode)
246{
Chris Cain36f9cde2021-11-22 11:18:21 -0600247 if (!VALID_POWER_MODE_SETTING(newMode) &&
248 !VALID_OEM_POWER_MODE_SETTING(newMode))
249 {
250 log<level::ERR>(
251 fmt::format(
252 "PowerMode::updateDbusMode - Requested power mode not supported: {}",
253 newMode)
254 .c_str());
255 return false;
256 }
257
Chris Cain1be43372021-12-09 19:29:37 -0600258 // Convert mode for DBus
259 ModeInterface::PowerMode dBusMode;
Chris Cain36f9cde2021-11-22 11:18:21 -0600260 switch (newMode)
261 {
262 case SysPwrMode::STATIC:
Chris Cain1be43372021-12-09 19:29:37 -0600263 dBusMode = Mode::PowerMode::Static;
Chris Cain36f9cde2021-11-22 11:18:21 -0600264 break;
265 case SysPwrMode::POWER_SAVING:
Chris Cain1be43372021-12-09 19:29:37 -0600266 dBusMode = Mode::PowerMode::PowerSaving;
Chris Cain36f9cde2021-11-22 11:18:21 -0600267 break;
268 case SysPwrMode::MAX_PERF:
Chris Cain1be43372021-12-09 19:29:37 -0600269 dBusMode = Mode::PowerMode::MaximumPerformance;
Chris Cain36f9cde2021-11-22 11:18:21 -0600270 break;
271 default:
Chris Cain1be43372021-12-09 19:29:37 -0600272 dBusMode = Mode::PowerMode::OEM;
Chris Cain36f9cde2021-11-22 11:18:21 -0600273 }
274
Chris Cain1be43372021-12-09 19:29:37 -0600275 // true = skip update signal
276 ModeInterface::setPropertyByName(POWER_MODE_PROP, dBusMode, true);
Chris Cain36f9cde2021-11-22 11:18:21 -0600277
278 return true;
279}
280
281// Send mode change request to the master OCC
282CmdStatus PowerMode::sendModeChange()
283{
Chris Cain6fa848a2022-01-24 14:54:38 -0600284 CmdStatus status;
Chris Cain36f9cde2021-11-22 11:18:21 -0600285
Chris Cain6fa848a2022-01-24 14:54:38 -0600286 if (!masterActive || !masterOccSet)
Chris Cain36f9cde2021-11-22 11:18:21 -0600287 {
288 // Nothing to do
Chris Cain6fa848a2022-01-24 14:54:38 -0600289 log<level::DEBUG>("PowerMode::sendModeChange: OCC master not active");
Chris Cain36f9cde2021-11-22 11:18:21 -0600290 return CmdStatus::SUCCESS;
291 }
292
293 if (!isPowerVM())
294 {
295 // Mode change is only supported on PowerVM systems
296 log<level::DEBUG>(
297 "PowerMode::sendModeChange: MODE CHANGE does not get sent on non-PowerVM systems");
298 return CmdStatus::SUCCESS;
299 }
300
Chris Cain1be43372021-12-09 19:29:37 -0600301 SysPwrMode newMode;
302 uint16_t oemModeData = 0;
303 getMode(newMode, oemModeData);
Chris Cain36f9cde2021-11-22 11:18:21 -0600304
305 if (VALID_POWER_MODE_SETTING(newMode) ||
306 VALID_OEM_POWER_MODE_SETTING(newMode))
307 {
308 std::vector<std::uint8_t> cmd, rsp;
309 cmd.reserve(9);
310 cmd.push_back(uint8_t(CmdType::SET_MODE_AND_STATE));
311 cmd.push_back(0x00); // Data Length (2 bytes)
312 cmd.push_back(0x06);
313 cmd.push_back(0x30); // Data (Version)
314 cmd.push_back(uint8_t(OccState::NO_CHANGE));
315 cmd.push_back(uint8_t(newMode));
Chris Cain1be43372021-12-09 19:29:37 -0600316 cmd.push_back(oemModeData >> 8); // Mode Data (Freq Point)
317 cmd.push_back(oemModeData & 0xFF); //
318 cmd.push_back(0x00); // reserved
Chris Cain36f9cde2021-11-22 11:18:21 -0600319 log<level::INFO>(
320 fmt::format(
321 "PowerMode::sendModeChange: SET_MODE({},{}) command to OCC{} ({} bytes)",
Chris Cain1be43372021-12-09 19:29:37 -0600322 newMode, oemModeData, occInstance, cmd.size())
Chris Cain36f9cde2021-11-22 11:18:21 -0600323 .c_str());
Chris Cain6fa848a2022-01-24 14:54:38 -0600324 status = occCmd->send(cmd, rsp);
Chris Cain36f9cde2021-11-22 11:18:21 -0600325 if (status == CmdStatus::SUCCESS)
326 {
327 if (rsp.size() == 5)
328 {
329 if (RspStatus::SUCCESS != RspStatus(rsp[2]))
330 {
331 log<level::ERR>(
332 fmt::format(
333 "PowerMode::sendModeChange: SET MODE failed with status 0x{:02X}",
334 rsp[2])
335 .c_str());
336 dump_hex(rsp);
337 status = CmdStatus::FAILURE;
338 }
339 }
340 else
341 {
342 log<level::ERR>(
343 "PowerMode::sendModeChange: INVALID SET MODE response");
344 dump_hex(rsp);
345 status = CmdStatus::FAILURE;
346 }
347 }
348 else
349 {
350 if (status == CmdStatus::OPEN_FAILURE)
351 {
352 // OCC not active yet
353 status = CmdStatus::SUCCESS;
354 }
355 else
356 {
357 log<level::ERR>("PowerMode::sendModeChange: SET_MODE FAILED!");
358 }
359 }
360 }
361 else
362 {
363 log<level::ERR>(
364 fmt::format(
365 "PowerMode::sendModeChange: Unable to set power mode to {}",
366 newMode)
367 .c_str());
368 status = CmdStatus::FAILURE;
369 }
370
371 return status;
372}
373
374void PowerMode::ipsChanged(sdbusplus::message::message& msg)
375{
Chris Cain6fa848a2022-01-24 14:54:38 -0600376 if (!masterActive || !masterOccSet)
Chris Cain1d51da22021-09-21 14:13:41 -0500377 {
378 // Nothing to do
379 return;
380 }
381
382 bool parmsChanged = false;
383 std::string interface;
384 std::map<std::string, std::variant<bool, uint8_t, uint64_t>>
385 ipsProperties{};
386 msg.read(interface, ipsProperties);
387
Chris Cain1be43372021-12-09 19:29:37 -0600388 // Read persisted values
389 bool ipsEnabled;
390 uint8_t enterUtil, exitUtil;
391 uint16_t enterTime, exitTime;
392 getIPSParms(ipsEnabled, enterUtil, enterTime, exitUtil, exitTime);
393
394 // Check for any changed data
Chris Cain1d51da22021-09-21 14:13:41 -0500395 auto ipsEntry = ipsProperties.find(IPS_ENABLED_PROP);
396 if (ipsEntry != ipsProperties.end())
397 {
Chris Cain1be43372021-12-09 19:29:37 -0600398 ipsEnabled = std::get<bool>(ipsEntry->second);
Chris Cain1d51da22021-09-21 14:13:41 -0500399 log<level::INFO>(
400 fmt::format("Idle Power Saver change: Enabled={}", ipsEnabled)
401 .c_str());
402 parmsChanged = true;
403 }
404 ipsEntry = ipsProperties.find(IPS_ENTER_UTIL);
405 if (ipsEntry != ipsProperties.end())
406 {
Chris Cain1be43372021-12-09 19:29:37 -0600407 enterUtil = std::get<uint8_t>(ipsEntry->second);
Chris Cain1d51da22021-09-21 14:13:41 -0500408 log<level::INFO>(
409 fmt::format("Idle Power Saver change: Enter Util={}%", enterUtil)
410 .c_str());
411 parmsChanged = true;
412 }
413 ipsEntry = ipsProperties.find(IPS_ENTER_TIME);
414 if (ipsEntry != ipsProperties.end())
415 {
416 std::chrono::milliseconds ms(std::get<uint64_t>(ipsEntry->second));
Chris Cain1be43372021-12-09 19:29:37 -0600417 enterTime =
Chris Cain1d51da22021-09-21 14:13:41 -0500418 std::chrono::duration_cast<std::chrono::seconds>(ms).count();
419 log<level::INFO>(
420 fmt::format("Idle Power Saver change: Enter Time={}sec", enterTime)
421 .c_str());
422 parmsChanged = true;
423 }
424 ipsEntry = ipsProperties.find(IPS_EXIT_UTIL);
425 if (ipsEntry != ipsProperties.end())
426 {
Chris Cain1be43372021-12-09 19:29:37 -0600427 exitUtil = std::get<uint8_t>(ipsEntry->second);
Chris Cain1d51da22021-09-21 14:13:41 -0500428 log<level::INFO>(
429 fmt::format("Idle Power Saver change: Exit Util={}%", exitUtil)
430 .c_str());
431 parmsChanged = true;
432 }
433 ipsEntry = ipsProperties.find(IPS_EXIT_TIME);
434 if (ipsEntry != ipsProperties.end())
435 {
436 std::chrono::milliseconds ms(std::get<uint64_t>(ipsEntry->second));
Chris Cain1be43372021-12-09 19:29:37 -0600437 exitTime = std::chrono::duration_cast<std::chrono::seconds>(ms).count();
Chris Cain1d51da22021-09-21 14:13:41 -0500438 log<level::INFO>(
439 fmt::format("Idle Power Saver change: Exit Time={}sec", exitTime)
440 .c_str());
441 parmsChanged = true;
442 }
443
444 if (parmsChanged)
445 {
Chris Cain1be43372021-12-09 19:29:37 -0600446 // Update persistant data with new DBus values
447 persistedData.updateIPS(ipsEnabled, enterUtil, enterTime, exitUtil,
448 exitTime);
449
450 // Trigger IPS data to get sent to the OCC
Chris Cain36f9cde2021-11-22 11:18:21 -0600451 sendIpsData();
Chris Cain1d51da22021-09-21 14:13:41 -0500452 }
453
454 return;
455}
456
Chris Cain1be43372021-12-09 19:29:37 -0600457/** @brief Get the Idle Power Saver properties from persisted data
458 * @return true if IPS parameters were read
Chris Cain36f9cde2021-11-22 11:18:21 -0600459 */
Chris Cain1be43372021-12-09 19:29:37 -0600460bool PowerMode::getIPSParms(bool& ipsEnabled, uint8_t& enterUtil,
461 uint16_t& enterTime, uint8_t& exitUtil,
462 uint16_t& exitTime)
Chris Cain36f9cde2021-11-22 11:18:21 -0600463{
Chris Cain36f9cde2021-11-22 11:18:21 -0600464 // Defaults:
Chris Cain1be43372021-12-09 19:29:37 -0600465 ipsEnabled = true; // Enabled
466 enterUtil = 8; // Enter Utilization (8%)
467 enterTime = 240; // Enter Delay Time (240s)
468 exitUtil = 12; // Exit Utilization (12%)
469 exitTime = 10; // Exit Delay Time (10s)
Chris Cain36f9cde2021-11-22 11:18:21 -0600470
Chris Cain1be43372021-12-09 19:29:37 -0600471 if (!persistedData.getIPS(ipsEnabled, enterUtil, enterTime, exitUtil,
472 exitTime))
473 {
474 // Persistent data not initialized, read defaults and update DBus
475 if (!initPersistentData())
476 {
477 // Unable to read defaults from entity manager yet
478 return false;
479 }
Chris Cain36f9cde2021-11-22 11:18:21 -0600480
Chris Cain1be43372021-12-09 19:29:37 -0600481 persistedData.getIPS(ipsEnabled, enterUtil, enterTime, exitUtil,
482 exitTime);
Chris Cain36f9cde2021-11-22 11:18:21 -0600483 }
484
485 if (enterUtil > exitUtil)
486 {
487 log<level::ERR>(
488 fmt::format(
489 "ERROR: Idle Power Saver Enter Utilization ({}%) is > Exit Utilization ({}%) - using Exit for both",
490 enterUtil, exitUtil)
491 .c_str());
492 enterUtil = exitUtil;
493 }
494
Chris Cain1be43372021-12-09 19:29:37 -0600495 return true;
496}
497
498// Set the Idle Power Saver data on DBus
499bool PowerMode::updateDbusIPS(const bool enabled, const uint8_t enterUtil,
500 const uint16_t enterTime, const uint8_t exitUtil,
501 const uint16_t exitTime)
502{
503 // true = skip update signal
504 IpsInterface::setPropertyByName(IPS_ENABLED_PROP, enabled, true);
505 IpsInterface::setPropertyByName(IPS_ENTER_UTIL, enterUtil, true);
506 // Convert time from seconds to ms
507 uint64_t msTime = enterTime * 1000;
508 IpsInterface::setPropertyByName(IPS_ENTER_TIME, msTime, true);
509 IpsInterface::setPropertyByName(IPS_EXIT_UTIL, exitUtil, true);
510 msTime = exitTime * 1000;
511 IpsInterface::setPropertyByName(IPS_EXIT_TIME, msTime, true);
512
513 return true;
Chris Cain36f9cde2021-11-22 11:18:21 -0600514}
515
516// Send Idle Power Saver config data to the master OCC
517CmdStatus PowerMode::sendIpsData()
518{
Chris Cain6fa848a2022-01-24 14:54:38 -0600519 if (!masterActive || !masterOccSet)
Chris Cain36f9cde2021-11-22 11:18:21 -0600520 {
521 // Nothing to do
522 return CmdStatus::SUCCESS;
523 }
524
525 if (!isPowerVM())
526 {
527 // Idle Power Saver data is only supported on PowerVM systems
528 log<level::DEBUG>(
529 "PowerMode::sendIpsData: SET_CFG_DATA[IPS] does not get sent on non-PowerVM systems");
530 return CmdStatus::SUCCESS;
531 }
532
Chris Cain1be43372021-12-09 19:29:37 -0600533 bool ipsEnabled;
Chris Cain36f9cde2021-11-22 11:18:21 -0600534 uint8_t enterUtil, exitUtil;
535 uint16_t enterTime, exitTime;
Chris Cain1be43372021-12-09 19:29:37 -0600536 getIPSParms(ipsEnabled, enterUtil, enterTime, exitUtil, exitTime);
Chris Cain36f9cde2021-11-22 11:18:21 -0600537
538 log<level::INFO>(
539 fmt::format(
540 "Idle Power Saver Parameters: enabled:{}, enter:{}%/{}s, exit:{}%/{}s",
541 ipsEnabled, enterUtil, enterTime, exitUtil, exitTime)
542 .c_str());
543
544 std::vector<std::uint8_t> cmd, rsp;
545 cmd.reserve(12);
546 cmd.push_back(uint8_t(CmdType::SET_CONFIG_DATA));
547 cmd.push_back(0x00); // Data Length (2 bytes)
548 cmd.push_back(0x09); //
549 cmd.push_back(0x11); // Config Format: IPS Settings
550 cmd.push_back(0x00); // Version
551 cmd.push_back(ipsEnabled ? 1 : 0); // IPS Enable
552 cmd.push_back(enterTime >> 8); // Enter Delay Time
553 cmd.push_back(enterTime & 0xFF); //
554 cmd.push_back(enterUtil); // Enter Utilization
555 cmd.push_back(exitTime >> 8); // Exit Delay Time
556 cmd.push_back(exitTime & 0xFF); //
557 cmd.push_back(exitUtil); // Exit Utilization
558 log<level::INFO>(fmt::format("PowerMode::sendIpsData: SET_CFG_DATA[IPS] "
559 "command to OCC{} ({} bytes)",
560 occInstance, cmd.size())
561 .c_str());
Chris Cain1be43372021-12-09 19:29:37 -0600562 CmdStatus status = occCmd->send(cmd, rsp);
Chris Cain36f9cde2021-11-22 11:18:21 -0600563 if (status == CmdStatus::SUCCESS)
564 {
565 if (rsp.size() == 5)
566 {
567 if (RspStatus::SUCCESS != RspStatus(rsp[2]))
568 {
569 log<level::ERR>(
570 fmt::format(
571 "PowerMode::sendIpsData: SET_CFG_DATA[IPS] failed with status 0x{:02X}",
572 rsp[2])
573 .c_str());
574 dump_hex(rsp);
575 status = CmdStatus::FAILURE;
576 }
577 }
578 else
579 {
580 log<level::ERR>(
581 "PowerMode::sendIpsData: INVALID SET_CFG_DATA[IPS] response");
582 dump_hex(rsp);
583 status = CmdStatus::FAILURE;
584 }
585 }
586 else
587 {
588 if (status == CmdStatus::OPEN_FAILURE)
589 {
590 // OCC not active yet
591 status = CmdStatus::SUCCESS;
592 }
593 else
594 {
595 log<level::ERR>(
596 "PowerMode::sendIpsData: SET_CFG_DATA[IPS] FAILED!");
597 }
598 }
599
600 return status;
601}
602
Chris Cain1be43372021-12-09 19:29:37 -0600603void OccPersistData::print()
Chris Cain36f9cde2021-11-22 11:18:21 -0600604{
Chris Cain1be43372021-12-09 19:29:37 -0600605 if (modeData.modeInitialized)
606 {
607 log<level::INFO>(
608 fmt::format(
609 "OccPersistData: Mode: 0x{:02X}, OEM Mode Data: {} (0x{:04X})",
610 modeData.mode, modeData.oemModeData, modeData.oemModeData)
611 .c_str());
612 }
613 if (modeData.ipsInitialized)
614 {
615 log<level::INFO>(
616 fmt::format(
617 "OccPersistData: IPS enabled:{}, enter:{}%/{}s, exit:{}%/{}s",
618 modeData.ipsEnabled, modeData.ipsEnterUtil,
619 modeData.ipsEnterTime, modeData.ipsExitUtil,
620 modeData.ipsExitTime)
621 .c_str());
622 }
Chris Cain36f9cde2021-11-22 11:18:21 -0600623}
624
625// Saves the OEM mode data in the filesystem using cereal.
626void OccPersistData::save()
627{
628 std::filesystem::path opath =
Chris Cain1be43372021-12-09 19:29:37 -0600629 std::filesystem::path{OCC_CONTROL_PERSIST_PATH} / powerModeFilename;
Chris Cain36f9cde2021-11-22 11:18:21 -0600630
631 if (!std::filesystem::exists(opath.parent_path()))
632 {
633 std::filesystem::create_directory(opath.parent_path());
634 }
635
636 log<level::DEBUG>(
Chris Cain1be43372021-12-09 19:29:37 -0600637 fmt::format(
638 "OccPersistData::save: Writing Power Mode persisted data to {}",
639 opath.c_str())
Chris Cain36f9cde2021-11-22 11:18:21 -0600640 .c_str());
641 print();
642
643 std::ofstream stream{opath.c_str()};
644 cereal::JSONOutputArchive oarchive{stream};
645
Chris Cain1be43372021-12-09 19:29:37 -0600646 oarchive(modeData);
Chris Cain36f9cde2021-11-22 11:18:21 -0600647}
648
649// Loads the OEM mode data in the filesystem using cereal.
650void OccPersistData::load()
651{
652
653 std::filesystem::path ipath =
Chris Cain1be43372021-12-09 19:29:37 -0600654 std::filesystem::path{OCC_CONTROL_PERSIST_PATH} / powerModeFilename;
Chris Cain36f9cde2021-11-22 11:18:21 -0600655
656 if (!std::filesystem::exists(ipath))
657 {
Chris Cain1be43372021-12-09 19:29:37 -0600658 modeData.modeInitialized = false;
659 modeData.ipsInitialized = false;
Chris Cain36f9cde2021-11-22 11:18:21 -0600660 return;
661 }
662
663 log<level::DEBUG>(
Chris Cain1be43372021-12-09 19:29:37 -0600664 fmt::format(
665 "OccPersistData::load: Reading Power Mode persisted data from {}",
666 ipath.c_str())
Chris Cain36f9cde2021-11-22 11:18:21 -0600667 .c_str());
668 try
669 {
670 std::ifstream stream{ipath.c_str()};
671 cereal::JSONInputArchive iarchive(stream);
Chris Cain1be43372021-12-09 19:29:37 -0600672 iarchive(modeData);
Chris Cain36f9cde2021-11-22 11:18:21 -0600673 }
674 catch (const std::exception& e)
675 {
676 auto error = errno;
677 log<level::ERR>(
678 fmt::format("OccPersistData::load: failed to read {}, errno={}",
679 ipath.c_str(), error)
680 .c_str());
Chris Cain1be43372021-12-09 19:29:37 -0600681 modeData.modeInitialized = false;
682 modeData.ipsInitialized = false;
Chris Cain36f9cde2021-11-22 11:18:21 -0600683 }
684
685 print();
686}
687
Chris Cain1be43372021-12-09 19:29:37 -0600688// Called when PowerModeProperties defaults are available on DBus
689void PowerMode::defaultsReady(sdbusplus::message::message& msg)
Chris Cain36f9cde2021-11-22 11:18:21 -0600690{
Chris Cain1be43372021-12-09 19:29:37 -0600691 std::map<std::string, std::variant<std::string>> properties{};
692 std::string interface;
693 msg.read(interface, properties);
Chris Cain36f9cde2021-11-22 11:18:21 -0600694
Chris Cain1be43372021-12-09 19:29:37 -0600695 // If persistent data exists, then don't need to read defaults
696 if ((!persistedData.modeAvailable()) || (!persistedData.ipsAvailable()))
Chris Cain36f9cde2021-11-22 11:18:21 -0600697 {
Chris Cain1be43372021-12-09 19:29:37 -0600698 log<level::INFO>(
699 fmt::format(
700 "Default PowerModeProperties are now available (persistent modeAvail={}, ipsAvail={})",
701 persistedData.modeAvailable() ? 'y' : 'n',
702 persistedData.modeAvailable() ? 'y' : 'n')
703 .c_str());
704
705 // Read default power mode defaults and update DBus
706 initPersistentData();
707 }
708}
709
710// Get the default power mode from DBus and return true if success
711bool PowerMode::getDefaultMode(SysPwrMode& defaultMode)
712{
713 try
714 {
715 auto& bus = utils::getBus();
716 std::string path = "/";
717 std::string service =
718 utils::getServiceUsingSubTree(PMODE_DEFAULT_INTERFACE, path);
719 auto method =
720 bus.new_method_call(service.c_str(), path.c_str(),
721 "org.freedesktop.DBus.Properties", "Get");
722 method.append(PMODE_DEFAULT_INTERFACE, "PowerMode");
723 auto reply = bus.call(method);
724
725 std::variant<std::string> stateEntryValue;
726 reply.read(stateEntryValue);
727 auto propVal = std::get<std::string>(stateEntryValue);
728
729 const std::string fullModeString =
730 PMODE_INTERFACE + ".PowerMode."s + propVal;
731 defaultMode = powermode::convertStringToMode(fullModeString);
732 if (!VALID_POWER_MODE_SETTING(defaultMode))
733 {
734 log<level::ERR>(
735 fmt::format(
736 "PowerMode::getDefaultMode: Invalid default power mode found: {}",
737 defaultMode)
738 .c_str());
739 // If default was read but not valid, use Max Performance
740 defaultMode = SysPwrMode::MAX_PERF;
741 return true;
742 }
743 }
744 catch (const sdbusplus::exception::exception& e)
745 {
746 log<level::ERR>(
747 fmt::format("Unable to read Default Power Mode: {}", e.what())
748 .c_str());
749 return false;
Chris Cain36f9cde2021-11-22 11:18:21 -0600750 }
751
Chris Cain1be43372021-12-09 19:29:37 -0600752 return true;
753}
Chris Cain36f9cde2021-11-22 11:18:21 -0600754
Chris Cain1be43372021-12-09 19:29:37 -0600755/* Get the default Idle Power Saver properties and return true if successful */
756bool PowerMode::getDefaultIPSParms(bool& ipsEnabled, uint8_t& enterUtil,
757 uint16_t& enterTime, uint8_t& exitUtil,
758 uint16_t& exitTime)
759{
760 // Defaults:
761 ipsEnabled = true; // Enabled
762 enterUtil = 8; // Enter Utilization (8%)
763 enterTime = 240; // Enter Delay Time (240s)
764 exitUtil = 12; // Exit Utilization (12%)
765 exitTime = 10; // Exit Delay Time (10s)
766
767 std::map<std::string, std::variant<bool, uint8_t, uint16_t, uint64_t>>
768 ipsProperties{};
769
770 // Get all IPS properties from DBus
771 try
772 {
773 auto& bus = utils::getBus();
774 std::string path = "/";
775 std::string service =
776 utils::getServiceUsingSubTree(PMODE_DEFAULT_INTERFACE, path);
777 auto method =
778 bus.new_method_call(service.c_str(), path.c_str(),
779 "org.freedesktop.DBus.Properties", "GetAll");
780 method.append(PMODE_DEFAULT_INTERFACE);
781 auto reply = bus.call(method);
782 reply.read(ipsProperties);
783 }
784 catch (const sdbusplus::exception::exception& e)
785 {
786 log<level::ERR>(
787 fmt::format(
788 "Unable to read Default Idle Power Saver parameters so it will be disabled: {}",
789 e.what())
790 .c_str());
791 return false;
792 }
793
794 auto ipsEntry = ipsProperties.find("IdlePowerSaverEnabled");
795 if (ipsEntry != ipsProperties.end())
796 {
797 ipsEnabled = std::get<bool>(ipsEntry->second);
798 }
799 else
800 {
801 log<level::ERR>(
802 "PowerMode::getDefaultIPSParms could not find property: IdlePowerSaverEnabled");
803 }
804
805 ipsEntry = ipsProperties.find("EnterUtilizationPercent");
806 if (ipsEntry != ipsProperties.end())
807 {
808 enterUtil = std::get<uint64_t>(ipsEntry->second);
809 }
810 else
811 {
812 log<level::ERR>(
813 "PowerMode::getDefaultIPSParms could not find property: EnterUtilizationPercent");
814 }
815
816 ipsEntry = ipsProperties.find("EnterUtilizationDwellTime");
817 if (ipsEntry != ipsProperties.end())
818 {
819 enterTime = std::get<uint64_t>(ipsEntry->second);
820 }
821 else
822 {
823 log<level::ERR>(
824 "PowerMode::getDefaultIPSParms could not find property: EnterUtilizationDwellTime");
825 }
826
827 ipsEntry = ipsProperties.find("ExitUtilizationPercent");
828 if (ipsEntry != ipsProperties.end())
829 {
830 exitUtil = std::get<uint64_t>(ipsEntry->second);
831 }
832 else
833 {
834 log<level::ERR>(
835 "PowerMode::getDefaultIPSParms could not find property: ExitUtilizationPercent");
836 }
837
838 ipsEntry = ipsProperties.find("ExitUtilizationDwellTime");
839 if (ipsEntry != ipsProperties.end())
840 {
841 exitTime = std::get<uint64_t>(ipsEntry->second);
842 }
843 else
844 {
845 log<level::ERR>(
846 "PowerMode::getDefaultIPSParms could not find property: ExitUtilizationDwellTime");
847 }
848
849 if (enterUtil > exitUtil)
850 {
851 log<level::ERR>(
852 fmt::format(
853 "ERROR: Default Idle Power Saver Enter Utilization ({}%) is > Exit Utilization ({}%) - using Exit for both",
854 enterUtil, exitUtil)
855 .c_str());
856 enterUtil = exitUtil;
857 }
858
859 return true;
Chris Cain36f9cde2021-11-22 11:18:21 -0600860}
861
Chris Cain78e86012021-03-04 16:15:31 -0600862} // namespace powermode
863
864} // namespace occ
865
866} // namespace open_power