blob: 88422bf4016dde527469b4873df768a68305681f [file] [log] [blame]
Chris Cain36f9cde2021-11-22 11:18:21 -06001#include "powermode.hpp"
2
Sheldon Baileyea2b22e2022-04-04 12:24:46 -05003#include <fcntl.h>
Sheldon Baileyea2b22e2022-04-04 12:24:46 -05004#include <sys/ioctl.h>
Chris Cain78e86012021-03-04 16:15:31 -06005
Patrick Williams48002492024-02-13 21:43:32 -06006#include <format>
7
Chris Cain52cce032023-02-09 15:38:31 -06008#ifdef POWERVM_CHECK
Chris Cain36f9cde2021-11-22 11:18:21 -06009#include <com/ibm/Host/Target/server.hpp>
Chris Cain52cce032023-02-09 15:38:31 -060010#endif
Sheldon Baileyea2b22e2022-04-04 12:24:46 -050011#include <org/open_power/OCC/Device/error.hpp>
Ben Tyner3576d652022-05-22 18:05:53 -050012#include <phosphor-logging/elog-errors.hpp>
Chris Cain78e86012021-03-04 16:15:31 -060013#include <phosphor-logging/log.hpp>
Sheldon Baileyea2b22e2022-04-04 12:24:46 -050014#include <xyz/openbmc_project/Common/error.hpp>
Chris Cain78e86012021-03-04 16:15:31 -060015#include <xyz/openbmc_project/Control/Power/Mode/server.hpp>
16
George Liub5ca1012021-09-10 12:53:11 +080017#include <cassert>
Chris Cain36f9cde2021-11-22 11:18:21 -060018#include <fstream>
George Liub5ca1012021-09-10 12:53:11 +080019#include <regex>
20
Chris Cain78e86012021-03-04 16:15:31 -060021namespace open_power
22{
23namespace occ
24{
25namespace powermode
26{
27
28using namespace phosphor::logging;
Chris Cain1be43372021-12-09 19:29:37 -060029using namespace std::literals::string_literals;
Sheldon Baileyea2b22e2022-04-04 12:24:46 -050030using namespace sdbusplus::org::open_power::OCC::Device::Error;
Chris Cain1be43372021-12-09 19:29:37 -060031
Chris Cain78e86012021-03-04 16:15:31 -060032using Mode = sdbusplus::xyz::openbmc_project::Control::Power::server::Mode;
33
Ben Tyner3576d652022-05-22 18:05:53 -050034using NotAllowed = sdbusplus::xyz::openbmc_project::Common::Error::NotAllowed;
35using Reason = xyz::openbmc_project::Common::NotAllowed::REASON;
36
Chris Cain6fa848a2022-01-24 14:54:38 -060037// Set the Master OCC
Chris Cain1be43372021-12-09 19:29:37 -060038void PowerMode::setMasterOcc(const std::string& masterOccPath)
Chris Cain6fa848a2022-01-24 14:54:38 -060039{
Chris Cain1be43372021-12-09 19:29:37 -060040 if (masterOccSet)
41 {
42 if (masterOccPath != path)
43 {
44 log<level::ERR>(
Patrick Williams48002492024-02-13 21:43:32 -060045 std::format(
Chris Cain1be43372021-12-09 19:29:37 -060046 "PowerMode::setMasterOcc: Master changed (was OCC{}, {})",
47 occInstance, masterOccPath)
48 .c_str());
49 if (occCmd)
50 {
51 occCmd.reset();
52 }
53 }
54 }
55 path = masterOccPath;
Chris Cain6fa848a2022-01-24 14:54:38 -060056 occInstance = path.back() - '0';
Patrick Williams48002492024-02-13 21:43:32 -060057 log<level::DEBUG>(std::format("PowerMode::setMasterOcc(OCC{}, {})",
Chris Cain6fa848a2022-01-24 14:54:38 -060058 occInstance, path.c_str())
59 .c_str());
Chris Cain1be43372021-12-09 19:29:37 -060060 if (!occCmd)
61 {
62 occCmd = std::make_unique<open_power::occ::OccCommand>(occInstance,
63 path.c_str());
64 }
Chris Cain6fa848a2022-01-24 14:54:38 -060065 masterOccSet = true;
66};
67
Chris Cain36f9cde2021-11-22 11:18:21 -060068// Called when DBus power mode gets changed
Patrick Williamsaf408082022-07-22 19:26:54 -050069void PowerMode::modeChanged(sdbusplus::message_t& msg)
Chris Cain78e86012021-03-04 16:15:31 -060070{
Chris Cain78e86012021-03-04 16:15:31 -060071 std::map<std::string, std::variant<std::string>> properties{};
72 std::string interface;
73 std::string propVal;
74 msg.read(interface, properties);
75 const auto modeEntry = properties.find(POWER_MODE_PROP);
76 if (modeEntry != properties.end())
77 {
78 auto modeEntryValue = modeEntry->second;
79 propVal = std::get<std::string>(modeEntryValue);
Chris Cain6fa848a2022-01-24 14:54:38 -060080 SysPwrMode newMode = convertStringToMode(propVal);
Ben Tyner3576d652022-05-22 18:05:53 -050081
82 // If mode set was requested via direct dbus property change then we
83 // need to see if mode set is locked and ignore the request. We should
84 // not get to this path since the property change method should also be
85 // checking the mode lock status.
86 if (persistedData.getModeLock())
87 {
88 // Fix up the mode property since we are going to ignore this
89 // set mode request.
90 log<level::ERR>(
91 "PowerMode::modeChanged: mode property changed while locked");
92 SysPwrMode currentMode;
93 uint16_t oemModeData;
94 getMode(currentMode, oemModeData);
95 updateDbusMode(currentMode);
96 return;
97 }
98
Chris Cain36f9cde2021-11-22 11:18:21 -060099 if (newMode != SysPwrMode::NO_CHANGE)
Chris Cain78e86012021-03-04 16:15:31 -0600100 {
Chris Cain1be43372021-12-09 19:29:37 -0600101 // Update persisted data with new mode
102 persistedData.updateMode(newMode, 0);
Chris Cain36f9cde2021-11-22 11:18:21 -0600103
Chris Cain78e86012021-03-04 16:15:31 -0600104 log<level::INFO>(
Patrick Williams48002492024-02-13 21:43:32 -0600105 std::format("DBus Power Mode Changed: {}", propVal).c_str());
Chris Cain78e86012021-03-04 16:15:31 -0600106
Chris Cain36f9cde2021-11-22 11:18:21 -0600107 // Send mode change to OCC
108 sendModeChange();
109 }
110 }
111}
112
Ben Tyner3576d652022-05-22 18:05:53 -0500113// Set the state of power mode lock. Writing persistent data via dbus method.
114bool PowerMode::powerModeLock()
115{
116 log<level::INFO>("PowerMode::powerModeLock: locking mode change");
117 persistedData.updateModeLock(true); // write persistent data
118 return true;
119}
120
121// Get the state of power mode. Reading persistent data via dbus method.
122bool PowerMode::powerModeLockStatus()
123{
124 bool status = persistedData.getModeLock(); // read persistent data
Patrick Williams48002492024-02-13 21:43:32 -0600125 log<level::INFO>(std::format("PowerMode::powerModeLockStatus: {}",
Ben Tyner3576d652022-05-22 18:05:53 -0500126 status ? "locked" : "unlocked")
127 .c_str());
128 return status;
129}
130
Chris Cain36f9cde2021-11-22 11:18:21 -0600131// Called from OCC PassThrough interface (via CE login / BMC command line)
Chris Cain1be43372021-12-09 19:29:37 -0600132bool PowerMode::setMode(const SysPwrMode newMode, const uint16_t oemModeData)
Chris Cain36f9cde2021-11-22 11:18:21 -0600133{
Ben Tyner3576d652022-05-22 18:05:53 -0500134 if (persistedData.getModeLock())
135 {
136 log<level::INFO>("PowerMode::setMode: mode change blocked");
137 return false;
138 }
139
Chris Cain36f9cde2021-11-22 11:18:21 -0600140 if (updateDbusMode(newMode) == false)
141 {
142 // Unsupported mode
143 return false;
144 }
145
Chris Cain1be43372021-12-09 19:29:37 -0600146 // Save mode
147 persistedData.updateMode(newMode, oemModeData);
Chris Cain36f9cde2021-11-22 11:18:21 -0600148
Chris Cain1be43372021-12-09 19:29:37 -0600149 // Send mode change to OCC
150 if (sendModeChange() != CmdStatus::SUCCESS)
151 {
152 // Mode change failed
153 return false;
Chris Cain78e86012021-03-04 16:15:31 -0600154 }
155
Chris Cain36f9cde2021-11-22 11:18:21 -0600156 return true;
Chris Cain78e86012021-03-04 16:15:31 -0600157}
158
159// Convert PowerMode string to OCC SysPwrMode
Chris Cain36f9cde2021-11-22 11:18:21 -0600160// Returns NO_CHANGE if OEM or unsupported mode
Chris Cain78e86012021-03-04 16:15:31 -0600161SysPwrMode convertStringToMode(const std::string& i_modeString)
162{
163 SysPwrMode pmode = SysPwrMode::NO_CHANGE;
164
165 Mode::PowerMode mode = Mode::convertPowerModeFromString(i_modeString);
166 if (mode == Mode::PowerMode::MaximumPerformance)
167 {
168 pmode = SysPwrMode::MAX_PERF;
169 }
170 else if (mode == Mode::PowerMode::PowerSaving)
171 {
172 pmode = SysPwrMode::POWER_SAVING;
173 }
174 else if (mode == Mode::PowerMode::Static)
175 {
Chris Cain36f9cde2021-11-22 11:18:21 -0600176 pmode = SysPwrMode::STATIC;
Chris Cain78e86012021-03-04 16:15:31 -0600177 }
Chris Cain2ff28862024-01-31 10:21:13 -0600178 else if (mode == Mode::PowerMode::EfficiencyFavorPower)
179 {
180 pmode = SysPwrMode::EFF_FAVOR_POWER;
181 }
182 else if (mode == Mode::PowerMode::EfficiencyFavorPerformance)
183 {
184 pmode = SysPwrMode::EFF_FAVOR_PERF;
185 }
186 else if (mode == Mode::PowerMode::BalancedPerformance)
187 {
188 pmode = SysPwrMode::BALANCED_PERF;
189 }
Chris Cain78e86012021-03-04 16:15:31 -0600190 else
191 {
Chris Cain36f9cde2021-11-22 11:18:21 -0600192 if (mode != Mode::PowerMode::OEM)
193 {
194 log<level::ERR>(
Patrick Williams48002492024-02-13 21:43:32 -0600195 std::format(
Chris Cain36f9cde2021-11-22 11:18:21 -0600196 "convertStringToMode: Invalid Power Mode specified: {}",
197 i_modeString)
198 .c_str());
199 }
Chris Cain78e86012021-03-04 16:15:31 -0600200 }
201
202 return pmode;
203}
204
Chris Cain36f9cde2021-11-22 11:18:21 -0600205// Check if Hypervisor target is PowerVM
206bool isPowerVM()
Chris Cain1d51da22021-09-21 14:13:41 -0500207{
Chris Cain52cce032023-02-09 15:38:31 -0600208 bool powerVmTarget = true;
209#ifdef POWERVM_CHECK
Chris Cain36f9cde2021-11-22 11:18:21 -0600210 namespace Hyper = sdbusplus::com::ibm::Host::server;
211 constexpr auto HYPE_PATH = "/com/ibm/host0/hypervisor";
212 constexpr auto HYPE_INTERFACE = "com.ibm.Host.Target";
213 constexpr auto HYPE_PROP = "Target";
214
Chris Cain36f9cde2021-11-22 11:18:21 -0600215 // This will throw exception on failure
216 auto& bus = utils::getBus();
217 auto service = utils::getService(HYPE_PATH, HYPE_INTERFACE);
218 auto method = bus.new_method_call(service.c_str(), HYPE_PATH,
219 "org.freedesktop.DBus.Properties", "Get");
220 method.append(HYPE_INTERFACE, HYPE_PROP);
221 auto reply = bus.call(method);
222
223 std::variant<std::string> hyperEntryValue;
224 reply.read(hyperEntryValue);
225 auto propVal = std::get<std::string>(hyperEntryValue);
226 if (Hyper::Target::convertHypervisorFromString(propVal) ==
227 Hyper::Target::Hypervisor::PowerVM)
228 {
229 powerVmTarget = true;
230 }
231
232 log<level::DEBUG>(
Patrick Williams48002492024-02-13 21:43:32 -0600233 std::format("isPowerVM returning {}", powerVmTarget).c_str());
Chris Cain52cce032023-02-09 15:38:31 -0600234#endif
Chris Cain36f9cde2021-11-22 11:18:21 -0600235
236 return powerVmTarget;
237}
238
Chris Cain1be43372021-12-09 19:29:37 -0600239// Initialize persistent data and return true if successful
240bool PowerMode::initPersistentData()
Chris Cain36f9cde2021-11-22 11:18:21 -0600241{
Chris Cain1be43372021-12-09 19:29:37 -0600242 if (!persistedData.modeAvailable())
Chris Cain36f9cde2021-11-22 11:18:21 -0600243 {
Chris Cain1be43372021-12-09 19:29:37 -0600244 // Read the default mode
245 SysPwrMode currentMode;
246 if (!getDefaultMode(currentMode))
247 {
248 // Unable to read defaults
249 return false;
250 }
251 log<level::INFO>(
Patrick Williams48002492024-02-13 21:43:32 -0600252 std::format("PowerMode::initPersistentData: Using default mode: {}",
Chris Cain1be43372021-12-09 19:29:37 -0600253 currentMode)
Chris Cain36f9cde2021-11-22 11:18:21 -0600254 .c_str());
Chris Cain1be43372021-12-09 19:29:37 -0600255
256 // Save default mode as current mode
257 persistedData.updateMode(currentMode, 0);
258
259 // Write default mode to DBus
260 updateDbusMode(currentMode);
Chris Cain36f9cde2021-11-22 11:18:21 -0600261 }
262
Chris Cain1be43372021-12-09 19:29:37 -0600263 if (!persistedData.ipsAvailable())
264 {
Chris Caincde7bea2022-01-28 15:54:24 -0600265 // Read the default IPS parameters, write persistent file and update
266 // DBus
267 return useDefaultIPSParms();
Chris Cain1be43372021-12-09 19:29:37 -0600268 }
269 return true;
270}
271
272// Get the requested power mode and return true if successful
273bool PowerMode::getMode(SysPwrMode& currentMode, uint16_t& oemModeData)
274{
275 currentMode = SysPwrMode::NO_CHANGE;
276 oemModeData = 0;
277
278 if (!persistedData.getMode(currentMode, oemModeData))
279 {
280 // Persistent data not initialized, read defaults and update DBus
281 if (!initPersistentData())
282 {
283 // Unable to read defaults from entity manager yet
284 return false;
285 }
286 return persistedData.getMode(currentMode, oemModeData);
287 }
288
289 return true;
Chris Cain36f9cde2021-11-22 11:18:21 -0600290}
291
292// Set the power mode on DBus
293bool PowerMode::updateDbusMode(const SysPwrMode newMode)
294{
Chris Cain36f9cde2021-11-22 11:18:21 -0600295 if (!VALID_POWER_MODE_SETTING(newMode) &&
296 !VALID_OEM_POWER_MODE_SETTING(newMode))
297 {
298 log<level::ERR>(
Patrick Williams48002492024-02-13 21:43:32 -0600299 std::format(
Chris Cain36f9cde2021-11-22 11:18:21 -0600300 "PowerMode::updateDbusMode - Requested power mode not supported: {}",
301 newMode)
302 .c_str());
303 return false;
304 }
305
Chris Cain1be43372021-12-09 19:29:37 -0600306 // Convert mode for DBus
307 ModeInterface::PowerMode dBusMode;
Chris Cain36f9cde2021-11-22 11:18:21 -0600308 switch (newMode)
309 {
310 case SysPwrMode::STATIC:
Chris Cain1be43372021-12-09 19:29:37 -0600311 dBusMode = Mode::PowerMode::Static;
Chris Cain36f9cde2021-11-22 11:18:21 -0600312 break;
313 case SysPwrMode::POWER_SAVING:
Chris Cain1be43372021-12-09 19:29:37 -0600314 dBusMode = Mode::PowerMode::PowerSaving;
Chris Cain36f9cde2021-11-22 11:18:21 -0600315 break;
316 case SysPwrMode::MAX_PERF:
Chris Cain1be43372021-12-09 19:29:37 -0600317 dBusMode = Mode::PowerMode::MaximumPerformance;
Chris Cain36f9cde2021-11-22 11:18:21 -0600318 break;
Chris Cain2ff28862024-01-31 10:21:13 -0600319 case SysPwrMode::EFF_FAVOR_POWER:
320 dBusMode = Mode::PowerMode::EfficiencyFavorPower;
321 break;
322 case SysPwrMode::EFF_FAVOR_PERF:
323 dBusMode = Mode::PowerMode::EfficiencyFavorPerformance;
324 break;
325 case SysPwrMode::BALANCED_PERF:
326 dBusMode = Mode::PowerMode::BalancedPerformance;
327 break;
Chris Cain36f9cde2021-11-22 11:18:21 -0600328 default:
Chris Cain1be43372021-12-09 19:29:37 -0600329 dBusMode = Mode::PowerMode::OEM;
Chris Cain36f9cde2021-11-22 11:18:21 -0600330 }
331
Chris Cain1be43372021-12-09 19:29:37 -0600332 // true = skip update signal
333 ModeInterface::setPropertyByName(POWER_MODE_PROP, dBusMode, true);
Chris Cain36f9cde2021-11-22 11:18:21 -0600334
335 return true;
336}
337
338// Send mode change request to the master OCC
339CmdStatus PowerMode::sendModeChange()
340{
Chris Cain6fa848a2022-01-24 14:54:38 -0600341 CmdStatus status;
Chris Cain36f9cde2021-11-22 11:18:21 -0600342
Chris Cain6fa848a2022-01-24 14:54:38 -0600343 if (!masterActive || !masterOccSet)
Chris Cain36f9cde2021-11-22 11:18:21 -0600344 {
345 // Nothing to do
Chris Cain6fa848a2022-01-24 14:54:38 -0600346 log<level::DEBUG>("PowerMode::sendModeChange: OCC master not active");
Chris Cain36f9cde2021-11-22 11:18:21 -0600347 return CmdStatus::SUCCESS;
348 }
349
350 if (!isPowerVM())
351 {
352 // Mode change is only supported on PowerVM systems
353 log<level::DEBUG>(
354 "PowerMode::sendModeChange: MODE CHANGE does not get sent on non-PowerVM systems");
355 return CmdStatus::SUCCESS;
356 }
357
Chris Cain1be43372021-12-09 19:29:37 -0600358 SysPwrMode newMode;
359 uint16_t oemModeData = 0;
360 getMode(newMode, oemModeData);
Chris Cain36f9cde2021-11-22 11:18:21 -0600361
362 if (VALID_POWER_MODE_SETTING(newMode) ||
363 VALID_OEM_POWER_MODE_SETTING(newMode))
364 {
365 std::vector<std::uint8_t> cmd, rsp;
366 cmd.reserve(9);
367 cmd.push_back(uint8_t(CmdType::SET_MODE_AND_STATE));
368 cmd.push_back(0x00); // Data Length (2 bytes)
369 cmd.push_back(0x06);
370 cmd.push_back(0x30); // Data (Version)
371 cmd.push_back(uint8_t(OccState::NO_CHANGE));
372 cmd.push_back(uint8_t(newMode));
Chris Cain1be43372021-12-09 19:29:37 -0600373 cmd.push_back(oemModeData >> 8); // Mode Data (Freq Point)
374 cmd.push_back(oemModeData & 0xFF); //
375 cmd.push_back(0x00); // reserved
Chris Cain36f9cde2021-11-22 11:18:21 -0600376 log<level::INFO>(
Patrick Williams48002492024-02-13 21:43:32 -0600377 std::format(
Chris Cain36f9cde2021-11-22 11:18:21 -0600378 "PowerMode::sendModeChange: SET_MODE({},{}) command to OCC{} ({} bytes)",
Chris Cain1be43372021-12-09 19:29:37 -0600379 newMode, oemModeData, occInstance, cmd.size())
Chris Cain36f9cde2021-11-22 11:18:21 -0600380 .c_str());
Chris Cain6fa848a2022-01-24 14:54:38 -0600381 status = occCmd->send(cmd, rsp);
Chris Cain36f9cde2021-11-22 11:18:21 -0600382 if (status == CmdStatus::SUCCESS)
383 {
384 if (rsp.size() == 5)
385 {
386 if (RspStatus::SUCCESS != RspStatus(rsp[2]))
387 {
388 log<level::ERR>(
Patrick Williams48002492024-02-13 21:43:32 -0600389 std::format(
Chris Cain36f9cde2021-11-22 11:18:21 -0600390 "PowerMode::sendModeChange: SET MODE failed with status 0x{:02X}",
391 rsp[2])
392 .c_str());
393 dump_hex(rsp);
394 status = CmdStatus::FAILURE;
395 }
396 }
397 else
398 {
399 log<level::ERR>(
400 "PowerMode::sendModeChange: INVALID SET MODE response");
401 dump_hex(rsp);
402 status = CmdStatus::FAILURE;
403 }
404 }
405 else
406 {
Chris Cainc567dc82022-04-01 15:09:17 -0500407 log<level::ERR>(
Patrick Williams48002492024-02-13 21:43:32 -0600408 std::format(
Chris Cainc567dc82022-04-01 15:09:17 -0500409 "PowerMode::sendModeChange: SET_MODE FAILED with status={}",
410 status)
411 .c_str());
Chris Cain36f9cde2021-11-22 11:18:21 -0600412 }
413 }
414 else
415 {
416 log<level::ERR>(
Patrick Williams48002492024-02-13 21:43:32 -0600417 std::format(
Chris Cain36f9cde2021-11-22 11:18:21 -0600418 "PowerMode::sendModeChange: Unable to set power mode to {}",
419 newMode)
420 .c_str());
421 status = CmdStatus::FAILURE;
422 }
423
424 return status;
425}
426
Patrick Williamsaf408082022-07-22 19:26:54 -0500427void PowerMode::ipsChanged(sdbusplus::message_t& msg)
Chris Cain36f9cde2021-11-22 11:18:21 -0600428{
Chris Cain1d51da22021-09-21 14:13:41 -0500429 bool parmsChanged = false;
430 std::string interface;
431 std::map<std::string, std::variant<bool, uint8_t, uint64_t>>
432 ipsProperties{};
433 msg.read(interface, ipsProperties);
434
Chris Cain1be43372021-12-09 19:29:37 -0600435 // Read persisted values
436 bool ipsEnabled;
437 uint8_t enterUtil, exitUtil;
438 uint16_t enterTime, exitTime;
439 getIPSParms(ipsEnabled, enterUtil, enterTime, exitUtil, exitTime);
440
441 // Check for any changed data
Chris Cain1d51da22021-09-21 14:13:41 -0500442 auto ipsEntry = ipsProperties.find(IPS_ENABLED_PROP);
443 if (ipsEntry != ipsProperties.end())
444 {
Chris Cain1be43372021-12-09 19:29:37 -0600445 ipsEnabled = std::get<bool>(ipsEntry->second);
Chris Cain1d51da22021-09-21 14:13:41 -0500446 log<level::INFO>(
Patrick Williams48002492024-02-13 21:43:32 -0600447 std::format("Idle Power Saver change: Enabled={}", ipsEnabled)
Chris Cain1d51da22021-09-21 14:13:41 -0500448 .c_str());
449 parmsChanged = true;
450 }
451 ipsEntry = ipsProperties.find(IPS_ENTER_UTIL);
452 if (ipsEntry != ipsProperties.end())
453 {
Chris Cain1be43372021-12-09 19:29:37 -0600454 enterUtil = std::get<uint8_t>(ipsEntry->second);
Chris Cain1d51da22021-09-21 14:13:41 -0500455 log<level::INFO>(
Patrick Williams48002492024-02-13 21:43:32 -0600456 std::format("Idle Power Saver change: Enter Util={}%", enterUtil)
Chris Cain1d51da22021-09-21 14:13:41 -0500457 .c_str());
458 parmsChanged = true;
459 }
460 ipsEntry = ipsProperties.find(IPS_ENTER_TIME);
461 if (ipsEntry != ipsProperties.end())
462 {
463 std::chrono::milliseconds ms(std::get<uint64_t>(ipsEntry->second));
Chris Cain1be43372021-12-09 19:29:37 -0600464 enterTime =
Chris Cain1d51da22021-09-21 14:13:41 -0500465 std::chrono::duration_cast<std::chrono::seconds>(ms).count();
466 log<level::INFO>(
Patrick Williams48002492024-02-13 21:43:32 -0600467 std::format("Idle Power Saver change: Enter Time={}sec", enterTime)
Chris Cain1d51da22021-09-21 14:13:41 -0500468 .c_str());
469 parmsChanged = true;
470 }
471 ipsEntry = ipsProperties.find(IPS_EXIT_UTIL);
472 if (ipsEntry != ipsProperties.end())
473 {
Chris Cain1be43372021-12-09 19:29:37 -0600474 exitUtil = std::get<uint8_t>(ipsEntry->second);
Chris Cain1d51da22021-09-21 14:13:41 -0500475 log<level::INFO>(
Patrick Williams48002492024-02-13 21:43:32 -0600476 std::format("Idle Power Saver change: Exit Util={}%", exitUtil)
Chris Cain1d51da22021-09-21 14:13:41 -0500477 .c_str());
478 parmsChanged = true;
479 }
480 ipsEntry = ipsProperties.find(IPS_EXIT_TIME);
481 if (ipsEntry != ipsProperties.end())
482 {
483 std::chrono::milliseconds ms(std::get<uint64_t>(ipsEntry->second));
Chris Cain1be43372021-12-09 19:29:37 -0600484 exitTime = std::chrono::duration_cast<std::chrono::seconds>(ms).count();
Chris Cain1d51da22021-09-21 14:13:41 -0500485 log<level::INFO>(
Patrick Williams48002492024-02-13 21:43:32 -0600486 std::format("Idle Power Saver change: Exit Time={}sec", exitTime)
Chris Cain1d51da22021-09-21 14:13:41 -0500487 .c_str());
488 parmsChanged = true;
489 }
490
491 if (parmsChanged)
492 {
Chris Caincde7bea2022-01-28 15:54:24 -0600493 if (exitUtil == 0)
494 {
495 // Setting the exitUtil to 0 will force restoring the default IPS
496 // parmeters (0 is not valid exit utilization)
497 log<level::INFO>(
498 "Idle Power Saver Exit Utilization is 0%. Restoring default parameters");
499 // Read the default IPS parameters, write persistent file and update
500 // DBus
501 useDefaultIPSParms();
502 }
503 else
504 {
505 // Update persistant data with new DBus values
506 persistedData.updateIPS(ipsEnabled, enterUtil, enterTime, exitUtil,
507 exitTime);
508 }
Chris Cain1be43372021-12-09 19:29:37 -0600509
510 // Trigger IPS data to get sent to the OCC
Chris Cain36f9cde2021-11-22 11:18:21 -0600511 sendIpsData();
Chris Cain1d51da22021-09-21 14:13:41 -0500512 }
513
514 return;
515}
516
Chris Cain1be43372021-12-09 19:29:37 -0600517/** @brief Get the Idle Power Saver properties from persisted data
518 * @return true if IPS parameters were read
Chris Cain36f9cde2021-11-22 11:18:21 -0600519 */
Chris Cain1be43372021-12-09 19:29:37 -0600520bool PowerMode::getIPSParms(bool& ipsEnabled, uint8_t& enterUtil,
521 uint16_t& enterTime, uint8_t& exitUtil,
522 uint16_t& exitTime)
Chris Cain36f9cde2021-11-22 11:18:21 -0600523{
Chris Cain36f9cde2021-11-22 11:18:21 -0600524 // Defaults:
Chris Cain1be43372021-12-09 19:29:37 -0600525 ipsEnabled = true; // Enabled
526 enterUtil = 8; // Enter Utilization (8%)
527 enterTime = 240; // Enter Delay Time (240s)
528 exitUtil = 12; // Exit Utilization (12%)
529 exitTime = 10; // Exit Delay Time (10s)
Chris Cain36f9cde2021-11-22 11:18:21 -0600530
Chris Cain1be43372021-12-09 19:29:37 -0600531 if (!persistedData.getIPS(ipsEnabled, enterUtil, enterTime, exitUtil,
532 exitTime))
533 {
534 // Persistent data not initialized, read defaults and update DBus
535 if (!initPersistentData())
536 {
537 // Unable to read defaults from entity manager yet
538 return false;
539 }
Chris Cain36f9cde2021-11-22 11:18:21 -0600540
Chris Cain1be43372021-12-09 19:29:37 -0600541 persistedData.getIPS(ipsEnabled, enterUtil, enterTime, exitUtil,
542 exitTime);
Chris Cain36f9cde2021-11-22 11:18:21 -0600543 }
544
545 if (enterUtil > exitUtil)
546 {
547 log<level::ERR>(
Patrick Williams48002492024-02-13 21:43:32 -0600548 std::format(
Chris Cain36f9cde2021-11-22 11:18:21 -0600549 "ERROR: Idle Power Saver Enter Utilization ({}%) is > Exit Utilization ({}%) - using Exit for both",
550 enterUtil, exitUtil)
551 .c_str());
552 enterUtil = exitUtil;
553 }
554
Chris Cain1be43372021-12-09 19:29:37 -0600555 return true;
556}
557
558// Set the Idle Power Saver data on DBus
559bool PowerMode::updateDbusIPS(const bool enabled, const uint8_t enterUtil,
560 const uint16_t enterTime, const uint8_t exitUtil,
561 const uint16_t exitTime)
562{
563 // true = skip update signal
564 IpsInterface::setPropertyByName(IPS_ENABLED_PROP, enabled, true);
565 IpsInterface::setPropertyByName(IPS_ENTER_UTIL, enterUtil, true);
566 // Convert time from seconds to ms
567 uint64_t msTime = enterTime * 1000;
568 IpsInterface::setPropertyByName(IPS_ENTER_TIME, msTime, true);
569 IpsInterface::setPropertyByName(IPS_EXIT_UTIL, exitUtil, true);
570 msTime = exitTime * 1000;
571 IpsInterface::setPropertyByName(IPS_EXIT_TIME, msTime, true);
572
573 return true;
Chris Cain36f9cde2021-11-22 11:18:21 -0600574}
575
576// Send Idle Power Saver config data to the master OCC
577CmdStatus PowerMode::sendIpsData()
578{
Chris Cain6fa848a2022-01-24 14:54:38 -0600579 if (!masterActive || !masterOccSet)
Chris Cain36f9cde2021-11-22 11:18:21 -0600580 {
581 // Nothing to do
582 return CmdStatus::SUCCESS;
583 }
584
585 if (!isPowerVM())
586 {
587 // Idle Power Saver data is only supported on PowerVM systems
588 log<level::DEBUG>(
589 "PowerMode::sendIpsData: SET_CFG_DATA[IPS] does not get sent on non-PowerVM systems");
590 return CmdStatus::SUCCESS;
591 }
592
Chris Cain1be43372021-12-09 19:29:37 -0600593 bool ipsEnabled;
Chris Cain36f9cde2021-11-22 11:18:21 -0600594 uint8_t enterUtil, exitUtil;
595 uint16_t enterTime, exitTime;
Chris Cain1be43372021-12-09 19:29:37 -0600596 getIPSParms(ipsEnabled, enterUtil, enterTime, exitUtil, exitTime);
Chris Cain36f9cde2021-11-22 11:18:21 -0600597
598 log<level::INFO>(
Patrick Williams48002492024-02-13 21:43:32 -0600599 std::format(
Chris Cain36f9cde2021-11-22 11:18:21 -0600600 "Idle Power Saver Parameters: enabled:{}, enter:{}%/{}s, exit:{}%/{}s",
601 ipsEnabled, enterUtil, enterTime, exitUtil, exitTime)
602 .c_str());
603
604 std::vector<std::uint8_t> cmd, rsp;
605 cmd.reserve(12);
606 cmd.push_back(uint8_t(CmdType::SET_CONFIG_DATA));
607 cmd.push_back(0x00); // Data Length (2 bytes)
608 cmd.push_back(0x09); //
609 cmd.push_back(0x11); // Config Format: IPS Settings
610 cmd.push_back(0x00); // Version
611 cmd.push_back(ipsEnabled ? 1 : 0); // IPS Enable
612 cmd.push_back(enterTime >> 8); // Enter Delay Time
613 cmd.push_back(enterTime & 0xFF); //
614 cmd.push_back(enterUtil); // Enter Utilization
615 cmd.push_back(exitTime >> 8); // Exit Delay Time
616 cmd.push_back(exitTime & 0xFF); //
617 cmd.push_back(exitUtil); // Exit Utilization
Patrick Williams48002492024-02-13 21:43:32 -0600618 log<level::INFO>(std::format("PowerMode::sendIpsData: SET_CFG_DATA[IPS] "
Chris Cain36f9cde2021-11-22 11:18:21 -0600619 "command to OCC{} ({} bytes)",
620 occInstance, cmd.size())
621 .c_str());
Chris Cain1be43372021-12-09 19:29:37 -0600622 CmdStatus status = occCmd->send(cmd, rsp);
Chris Cain36f9cde2021-11-22 11:18:21 -0600623 if (status == CmdStatus::SUCCESS)
624 {
625 if (rsp.size() == 5)
626 {
627 if (RspStatus::SUCCESS != RspStatus(rsp[2]))
628 {
629 log<level::ERR>(
Patrick Williams48002492024-02-13 21:43:32 -0600630 std::format(
Chris Cain36f9cde2021-11-22 11:18:21 -0600631 "PowerMode::sendIpsData: SET_CFG_DATA[IPS] failed with status 0x{:02X}",
632 rsp[2])
633 .c_str());
634 dump_hex(rsp);
635 status = CmdStatus::FAILURE;
636 }
637 }
638 else
639 {
640 log<level::ERR>(
641 "PowerMode::sendIpsData: INVALID SET_CFG_DATA[IPS] response");
642 dump_hex(rsp);
643 status = CmdStatus::FAILURE;
644 }
645 }
646 else
647 {
Chris Cainc567dc82022-04-01 15:09:17 -0500648 log<level::ERR>(
Patrick Williams48002492024-02-13 21:43:32 -0600649 std::format(
Chris Cainc567dc82022-04-01 15:09:17 -0500650 "PowerMode::sendIpsData: SET_CFG_DATA[IPS] with status={}",
651 status)
652 .c_str());
Chris Cain36f9cde2021-11-22 11:18:21 -0600653 }
654
655 return status;
656}
657
Chris Caincde7bea2022-01-28 15:54:24 -0600658// Print the current values
Chris Cain1be43372021-12-09 19:29:37 -0600659void OccPersistData::print()
Chris Cain36f9cde2021-11-22 11:18:21 -0600660{
Chris Cain1be43372021-12-09 19:29:37 -0600661 if (modeData.modeInitialized)
662 {
663 log<level::INFO>(
Patrick Williams48002492024-02-13 21:43:32 -0600664 std::format(
Ben Tyner3576d652022-05-22 18:05:53 -0500665 "OccPersistData: Mode: 0x{:02X}, OEM Mode Data: {} (0x{:04X} Locked{})",
666 modeData.mode, modeData.oemModeData, modeData.oemModeData,
667 modeData.modeLocked)
Chris Cain1be43372021-12-09 19:29:37 -0600668 .c_str());
669 }
670 if (modeData.ipsInitialized)
671 {
672 log<level::INFO>(
Patrick Williams48002492024-02-13 21:43:32 -0600673 std::format(
Chris Cain1be43372021-12-09 19:29:37 -0600674 "OccPersistData: IPS enabled:{}, enter:{}%/{}s, exit:{}%/{}s",
675 modeData.ipsEnabled, modeData.ipsEnterUtil,
676 modeData.ipsEnterTime, modeData.ipsExitUtil,
677 modeData.ipsExitTime)
678 .c_str());
679 }
Chris Cain36f9cde2021-11-22 11:18:21 -0600680}
681
682// Saves the OEM mode data in the filesystem using cereal.
683void OccPersistData::save()
684{
685 std::filesystem::path opath =
Chris Cain1be43372021-12-09 19:29:37 -0600686 std::filesystem::path{OCC_CONTROL_PERSIST_PATH} / powerModeFilename;
Chris Cain36f9cde2021-11-22 11:18:21 -0600687
688 if (!std::filesystem::exists(opath.parent_path()))
689 {
690 std::filesystem::create_directory(opath.parent_path());
691 }
692
693 log<level::DEBUG>(
Patrick Williams48002492024-02-13 21:43:32 -0600694 std::format(
Chris Cain1be43372021-12-09 19:29:37 -0600695 "OccPersistData::save: Writing Power Mode persisted data to {}",
696 opath.c_str())
Chris Cain36f9cde2021-11-22 11:18:21 -0600697 .c_str());
Chris Caincde7bea2022-01-28 15:54:24 -0600698 // print();
Chris Cain36f9cde2021-11-22 11:18:21 -0600699
700 std::ofstream stream{opath.c_str()};
701 cereal::JSONOutputArchive oarchive{stream};
702
Chris Cain1be43372021-12-09 19:29:37 -0600703 oarchive(modeData);
Chris Cain36f9cde2021-11-22 11:18:21 -0600704}
705
706// Loads the OEM mode data in the filesystem using cereal.
707void OccPersistData::load()
708{
Chris Cain36f9cde2021-11-22 11:18:21 -0600709 std::filesystem::path ipath =
Chris Cain1be43372021-12-09 19:29:37 -0600710 std::filesystem::path{OCC_CONTROL_PERSIST_PATH} / powerModeFilename;
Chris Cain36f9cde2021-11-22 11:18:21 -0600711
712 if (!std::filesystem::exists(ipath))
713 {
Chris Cain1be43372021-12-09 19:29:37 -0600714 modeData.modeInitialized = false;
715 modeData.ipsInitialized = false;
Chris Cain36f9cde2021-11-22 11:18:21 -0600716 return;
717 }
718
719 log<level::DEBUG>(
Patrick Williams48002492024-02-13 21:43:32 -0600720 std::format(
Chris Cain1be43372021-12-09 19:29:37 -0600721 "OccPersistData::load: Reading Power Mode persisted data from {}",
722 ipath.c_str())
Chris Cain36f9cde2021-11-22 11:18:21 -0600723 .c_str());
724 try
725 {
726 std::ifstream stream{ipath.c_str()};
727 cereal::JSONInputArchive iarchive(stream);
Chris Cain1be43372021-12-09 19:29:37 -0600728 iarchive(modeData);
Chris Cain36f9cde2021-11-22 11:18:21 -0600729 }
730 catch (const std::exception& e)
731 {
732 auto error = errno;
733 log<level::ERR>(
Patrick Williams48002492024-02-13 21:43:32 -0600734 std::format("OccPersistData::load: failed to read {}, errno={}",
Chris Cain36f9cde2021-11-22 11:18:21 -0600735 ipath.c_str(), error)
736 .c_str());
Chris Cain1be43372021-12-09 19:29:37 -0600737 modeData.modeInitialized = false;
738 modeData.ipsInitialized = false;
Chris Cain36f9cde2021-11-22 11:18:21 -0600739 }
740
Chris Caincde7bea2022-01-28 15:54:24 -0600741 // print();
Chris Cain36f9cde2021-11-22 11:18:21 -0600742}
743
Chris Cain1be43372021-12-09 19:29:37 -0600744// Called when PowerModeProperties defaults are available on DBus
Patrick Williamsaf408082022-07-22 19:26:54 -0500745void PowerMode::defaultsReady(sdbusplus::message_t& msg)
Chris Cain36f9cde2021-11-22 11:18:21 -0600746{
Chris Cain1be43372021-12-09 19:29:37 -0600747 std::map<std::string, std::variant<std::string>> properties{};
748 std::string interface;
749 msg.read(interface, properties);
Chris Cain36f9cde2021-11-22 11:18:21 -0600750
Chris Cain1be43372021-12-09 19:29:37 -0600751 // If persistent data exists, then don't need to read defaults
752 if ((!persistedData.modeAvailable()) || (!persistedData.ipsAvailable()))
Chris Cain36f9cde2021-11-22 11:18:21 -0600753 {
Chris Cain1be43372021-12-09 19:29:37 -0600754 log<level::INFO>(
Patrick Williams48002492024-02-13 21:43:32 -0600755 std::format(
Chris Cain1be43372021-12-09 19:29:37 -0600756 "Default PowerModeProperties are now available (persistent modeAvail={}, ipsAvail={})",
757 persistedData.modeAvailable() ? 'y' : 'n',
758 persistedData.modeAvailable() ? 'y' : 'n')
759 .c_str());
760
761 // Read default power mode defaults and update DBus
762 initPersistentData();
763 }
764}
765
766// Get the default power mode from DBus and return true if success
767bool PowerMode::getDefaultMode(SysPwrMode& defaultMode)
768{
769 try
770 {
771 auto& bus = utils::getBus();
772 std::string path = "/";
773 std::string service =
774 utils::getServiceUsingSubTree(PMODE_DEFAULT_INTERFACE, path);
Patrick Williamsa49c9872023-05-10 07:50:35 -0500775 auto method = bus.new_method_call(service.c_str(), path.c_str(),
776 "org.freedesktop.DBus.Properties",
777 "Get");
Chris Cain1be43372021-12-09 19:29:37 -0600778 method.append(PMODE_DEFAULT_INTERFACE, "PowerMode");
779 auto reply = bus.call(method);
780
781 std::variant<std::string> stateEntryValue;
782 reply.read(stateEntryValue);
783 auto propVal = std::get<std::string>(stateEntryValue);
784
Patrick Williamsa49c9872023-05-10 07:50:35 -0500785 const std::string fullModeString = PMODE_INTERFACE + ".PowerMode."s +
786 propVal;
Chris Cain1be43372021-12-09 19:29:37 -0600787 defaultMode = powermode::convertStringToMode(fullModeString);
788 if (!VALID_POWER_MODE_SETTING(defaultMode))
789 {
790 log<level::ERR>(
Patrick Williams48002492024-02-13 21:43:32 -0600791 std::format(
Chris Cain1be43372021-12-09 19:29:37 -0600792 "PowerMode::getDefaultMode: Invalid default power mode found: {}",
793 defaultMode)
794 .c_str());
795 // If default was read but not valid, use Max Performance
796 defaultMode = SysPwrMode::MAX_PERF;
797 return true;
798 }
799 }
Patrick Williamsaf408082022-07-22 19:26:54 -0500800 catch (const sdbusplus::exception_t& e)
Chris Cain1be43372021-12-09 19:29:37 -0600801 {
802 log<level::ERR>(
Patrick Williams48002492024-02-13 21:43:32 -0600803 std::format("Unable to read Default Power Mode: {}", e.what())
Chris Cain1be43372021-12-09 19:29:37 -0600804 .c_str());
805 return false;
Chris Cain36f9cde2021-11-22 11:18:21 -0600806 }
807
Chris Cain1be43372021-12-09 19:29:37 -0600808 return true;
809}
Chris Cain36f9cde2021-11-22 11:18:21 -0600810
Chris Cain1be43372021-12-09 19:29:37 -0600811/* Get the default Idle Power Saver properties and return true if successful */
812bool PowerMode::getDefaultIPSParms(bool& ipsEnabled, uint8_t& enterUtil,
813 uint16_t& enterTime, uint8_t& exitUtil,
814 uint16_t& exitTime)
815{
816 // Defaults:
817 ipsEnabled = true; // Enabled
818 enterUtil = 8; // Enter Utilization (8%)
819 enterTime = 240; // Enter Delay Time (240s)
820 exitUtil = 12; // Exit Utilization (12%)
821 exitTime = 10; // Exit Delay Time (10s)
822
823 std::map<std::string, std::variant<bool, uint8_t, uint16_t, uint64_t>>
824 ipsProperties{};
825
826 // Get all IPS properties from DBus
827 try
828 {
829 auto& bus = utils::getBus();
830 std::string path = "/";
831 std::string service =
832 utils::getServiceUsingSubTree(PMODE_DEFAULT_INTERFACE, path);
Patrick Williamsa49c9872023-05-10 07:50:35 -0500833 auto method = bus.new_method_call(service.c_str(), path.c_str(),
834 "org.freedesktop.DBus.Properties",
835 "GetAll");
Chris Cain1be43372021-12-09 19:29:37 -0600836 method.append(PMODE_DEFAULT_INTERFACE);
837 auto reply = bus.call(method);
838 reply.read(ipsProperties);
839 }
Patrick Williamsaf408082022-07-22 19:26:54 -0500840 catch (const sdbusplus::exception_t& e)
Chris Cain1be43372021-12-09 19:29:37 -0600841 {
842 log<level::ERR>(
Patrick Williams48002492024-02-13 21:43:32 -0600843 std::format(
Chris Cain1be43372021-12-09 19:29:37 -0600844 "Unable to read Default Idle Power Saver parameters so it will be disabled: {}",
845 e.what())
846 .c_str());
847 return false;
848 }
849
850 auto ipsEntry = ipsProperties.find("IdlePowerSaverEnabled");
851 if (ipsEntry != ipsProperties.end())
852 {
853 ipsEnabled = std::get<bool>(ipsEntry->second);
854 }
855 else
856 {
857 log<level::ERR>(
858 "PowerMode::getDefaultIPSParms could not find property: IdlePowerSaverEnabled");
859 }
860
861 ipsEntry = ipsProperties.find("EnterUtilizationPercent");
862 if (ipsEntry != ipsProperties.end())
863 {
864 enterUtil = std::get<uint64_t>(ipsEntry->second);
865 }
866 else
867 {
868 log<level::ERR>(
869 "PowerMode::getDefaultIPSParms could not find property: EnterUtilizationPercent");
870 }
871
872 ipsEntry = ipsProperties.find("EnterUtilizationDwellTime");
873 if (ipsEntry != ipsProperties.end())
874 {
875 enterTime = std::get<uint64_t>(ipsEntry->second);
876 }
877 else
878 {
879 log<level::ERR>(
880 "PowerMode::getDefaultIPSParms could not find property: EnterUtilizationDwellTime");
881 }
882
883 ipsEntry = ipsProperties.find("ExitUtilizationPercent");
884 if (ipsEntry != ipsProperties.end())
885 {
886 exitUtil = std::get<uint64_t>(ipsEntry->second);
887 }
888 else
889 {
890 log<level::ERR>(
891 "PowerMode::getDefaultIPSParms could not find property: ExitUtilizationPercent");
892 }
893
894 ipsEntry = ipsProperties.find("ExitUtilizationDwellTime");
895 if (ipsEntry != ipsProperties.end())
896 {
897 exitTime = std::get<uint64_t>(ipsEntry->second);
898 }
899 else
900 {
901 log<level::ERR>(
902 "PowerMode::getDefaultIPSParms could not find property: ExitUtilizationDwellTime");
903 }
904
905 if (enterUtil > exitUtil)
906 {
907 log<level::ERR>(
Patrick Williams48002492024-02-13 21:43:32 -0600908 std::format(
Chris Cain1be43372021-12-09 19:29:37 -0600909 "ERROR: Default Idle Power Saver Enter Utilization ({}%) is > Exit Utilization ({}%) - using Exit for both",
910 enterUtil, exitUtil)
911 .c_str());
912 enterUtil = exitUtil;
913 }
914
915 return true;
Chris Cain36f9cde2021-11-22 11:18:21 -0600916}
917
Chris Caincde7bea2022-01-28 15:54:24 -0600918/* Read default IPS parameters, save them to the persistent file and update
919 DBus. Return true if successful */
920bool PowerMode::useDefaultIPSParms()
921{
922 // Read the default IPS parameters
923 bool ipsEnabled;
924 uint8_t enterUtil, exitUtil;
925 uint16_t enterTime, exitTime;
926 if (!getDefaultIPSParms(ipsEnabled, enterUtil, enterTime, exitUtil,
927 exitTime))
928 {
929 // Unable to read defaults
930 return false;
931 }
932 log<level::INFO>(
Patrick Williams48002492024-02-13 21:43:32 -0600933 std::format(
Chris Caincde7bea2022-01-28 15:54:24 -0600934 "PowerMode::useDefaultIPSParms: Using default IPS parms: Enabled: {}, EnterUtil: {}%, EnterTime: {}s, ExitUtil: {}%, ExitTime: {}s",
935 ipsEnabled, enterUtil, enterTime, exitUtil, exitTime)
936 .c_str());
937
938 // Save IPS parms to the persistent file
939 persistedData.updateIPS(ipsEnabled, enterUtil, enterTime, exitUtil,
940 exitTime);
941
942 // Write IPS parms to DBus
943 return updateDbusIPS(ipsEnabled, enterUtil, enterTime, exitUtil, exitTime);
944}
945
Sheldon Baileyea2b22e2022-04-04 12:24:46 -0500946#ifdef POWER10
947
948// Starts to watch for IPS active state changes.
949bool PowerMode::openIpsFile()
950{
951 bool rc = true;
952 fd = open(ipsStatusFile.c_str(), O_RDONLY | O_NONBLOCK);
953 const int open_errno = errno;
954 if (fd < 0)
955 {
Patrick Williams48002492024-02-13 21:43:32 -0600956 log<level::ERR>(std::format("openIpsFile Error({})={} : File={}",
Sheldon Baileyea2b22e2022-04-04 12:24:46 -0500957 open_errno, strerror(open_errno),
958 ipsStatusFile.c_str())
959 .c_str());
960
961 close(fd);
962
963 using namespace sdbusplus::org::open_power::OCC::Device::Error;
964 report<OpenFailure>(
965 phosphor::logging::org::open_power::OCC::Device::OpenFailure::
966 CALLOUT_ERRNO(open_errno),
967 phosphor::logging::org::open_power::OCC::Device::OpenFailure::
968 CALLOUT_DEVICE_PATH(ipsStatusFile.c_str()));
969
970 // We are no longer watching the error
971 active(false);
972
973 watching = false;
974 rc = false;
975 // NOTE: this will leave the system not reporting IPS active state to
976 // Fan Controls, Until an APP reload, or IPL and we will attempt again.
977 }
978 return rc;
979}
980
981// Starts to watch for IPS active state changes.
982void PowerMode::addIpsWatch(bool poll)
983{
984 // open file and register callback on file if we are not currently watching,
985 // and if poll=true, and if we are the master.
986 if ((!watching) && poll)
987 {
988 // Open the file
989 if (openIpsFile())
990 {
991 // register the callback handler which sets 'watching'
992 registerIpsStatusCallBack();
993 }
994 }
995}
996
997// Stops watching for IPS active state changes.
998void PowerMode::removeIpsWatch()
999{
1000 // NOTE: we want to remove event, close file, and IPS active false no
1001 // matter what the 'watching' flags is set to.
1002
1003 // We are no longer watching the error
1004 active(false);
1005
1006 watching = false;
1007
1008 // Close file
1009 close(fd);
1010
1011 // clears sourcePtr in the event source.
1012 eventSource.reset();
1013}
1014
1015// Attaches the FD to event loop and registers the callback handler
1016void PowerMode::registerIpsStatusCallBack()
1017{
1018 decltype(eventSource.get()) sourcePtr = nullptr;
1019
1020 auto r = sd_event_add_io(event.get(), &sourcePtr, fd, EPOLLPRI | EPOLLERR,
1021 ipsStatusCallBack, this);
1022 if (r < 0)
1023 {
Patrick Williams48002492024-02-13 21:43:32 -06001024 log<level::ERR>(std::format("sd_event_add_io: Error({})={} : File={}",
Sheldon Baileyea2b22e2022-04-04 12:24:46 -05001025 r, strerror(-r), ipsStatusFile.c_str())
1026 .c_str());
1027
1028 using InternalFailure =
1029 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
1030 report<InternalFailure>();
1031
1032 removeIpsWatch();
1033 // NOTE: this will leave the system not reporting IPS active state to
1034 // Fan Controls, Until an APP reload, or IPL and we will attempt again.
1035 }
1036 else
1037 {
1038 // puts sourcePtr in the event source.
1039 eventSource.reset(sourcePtr);
1040 // Set we are watching the error
1041 watching = true;
1042 }
1043}
1044
1045// Static function to redirect to non static analyze event function to be
1046// able to read file and push onto dBus.
1047int PowerMode::ipsStatusCallBack(sd_event_source* /*es*/, int /*fd*/,
1048 uint32_t /*revents*/, void* userData)
1049{
1050 auto pmode = static_cast<PowerMode*>(userData);
1051 pmode->analyzeIpsEvent();
1052 return 0;
1053}
1054
1055// Function to Read SysFs file change on IPS state and push on dBus.
1056void PowerMode::analyzeIpsEvent()
1057{
1058 // Need to seek to START, else the poll returns immediately telling
1059 // there is data to be read. if not done this floods the system.
1060 auto r = lseek(fd, 0, SEEK_SET);
1061 const int open_errno = errno;
1062 if (r < 0)
1063 {
1064 // NOTE: upon file access error we can not just re-open file, we have to
1065 // remove and add to watch.
1066 removeIpsWatch();
1067 addIpsWatch(true);
1068 }
1069
1070 // if we are 'watching' that is the file seek, or the re-open passed.. we
1071 // can read the data
1072 if (watching)
1073 {
1074 // This file gets created when polling OCCs. A value or length of 0 is
1075 // deemed success. That means we would disable IPS active on dbus.
1076 char data;
1077 bool ipsState = false;
1078 const auto len = read(fd, &data, sizeof(data));
1079 const int readErrno = errno;
1080 if (len <= 0)
1081 {
1082 removeIpsWatch();
1083
1084 log<level::ERR>(
Patrick Williams48002492024-02-13 21:43:32 -06001085 std::format("IPS state Read Error({})={} : File={} : len={}",
Sheldon Baileyea2b22e2022-04-04 12:24:46 -05001086 readErrno, strerror(readErrno),
1087 ipsStatusFile.c_str(), len)
1088 .c_str());
1089
1090 report<ReadFailure>(
1091 phosphor::logging::org::open_power::OCC::Device::ReadFailure::
1092 CALLOUT_ERRNO(readErrno),
1093 phosphor::logging::org::open_power::OCC::Device::ReadFailure::
1094 CALLOUT_DEVICE_PATH(ipsStatusFile.c_str()));
1095
1096 // NOTE: this will leave the system not reporting IPS active state
1097 // to Fan Controls, Until an APP reload, or IPL and we will attempt
1098 // again.
1099 }
1100 else
1101 {
1102 // Data returned in ASCII.
1103 // convert to integer. atoi()
1104 // from OCC_P10_FW_Interfaces spec
1105 // Bit 6: IPS active 1 indicates enabled.
1106 // Bit 7: IPS enabled. 1 indicates enabled.
1107 // mask off bit 6 --> & 0x02
1108 // Shift left one bit and store as bool. >> 1
1109 ipsState = static_cast<bool>(((atoi(&data)) & 0x2) >> 1);
1110 }
1111
1112 // This will only set IPS active dbus if different than current.
1113 active(ipsState);
1114 }
1115 else
1116 {
1117 removeIpsWatch();
1118
1119 // If the Retry did not get to "watching = true" we already have an
1120 // error log, just post trace.
Patrick Williams48002492024-02-13 21:43:32 -06001121 log<level::ERR>(std::format("Retry on File seek Error({})={} : File={}",
Sheldon Baileyea2b22e2022-04-04 12:24:46 -05001122 open_errno, strerror(open_errno),
1123 ipsStatusFile.c_str())
1124 .c_str());
1125
1126 // NOTE: this will leave the system not reporting IPS active state to
1127 // Fan Controls, Until an APP reload, or IPL and we will attempt again.
1128 }
1129
1130 return;
1131}
Ben Tyner3576d652022-05-22 18:05:53 -05001132
1133// overrides read/write to powerMode dbus property.
1134Mode::PowerMode PowerMode::powerMode(Mode::PowerMode value)
1135{
1136 if (persistedData.getModeLock())
1137 {
1138 log<level::INFO>("PowerMode::powerMode: mode property change blocked");
1139 elog<NotAllowed>(xyz::openbmc_project::Common::NotAllowed::REASON(
1140 "mode change not allowed due to lock"));
1141 return value;
1142 }
1143 else
1144 {
1145 return Mode::powerMode(value);
1146 }
1147}
Sheldon Baileyea2b22e2022-04-04 12:24:46 -05001148#endif
1149
Sheldon Bailey31a2f132022-05-20 11:31:52 -05001150/* Set dbus property to SAFE mode(true) or clear(false) only if different */
1151void PowerMode::updateDbusSafeMode(const bool safeModeReq)
1152{
1153 log<level::DEBUG>(
Patrick Williams48002492024-02-13 21:43:32 -06001154 std::format("PowerMode:updateDbusSafeMode: Update dbus state ({})",
Sheldon Bailey31a2f132022-05-20 11:31:52 -05001155 safeModeReq)
1156 .c_str());
1157
1158 // Note; this function checks and only updates if different.
1159 Mode::safeMode(safeModeReq);
1160}
1161
Chris Cain78e86012021-03-04 16:15:31 -06001162} // namespace powermode
1163
1164} // namespace occ
1165
1166} // namespace open_power