blob: cc92ff4686b1a7f38f9c2ac6e5c75da3ce7c44e9 [file] [log] [blame]
Jim Wright10eb00f2021-07-21 12:10:38 -05001/**
2 * Copyright © 2021 IBM Corporation
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
Jim Wright539b6082021-08-02 14:50:23 -050017#include "power_control.hpp"
18
Shawn McCarney1f8b1102024-06-21 18:57:46 -050019#include "config_file_parser.hpp"
20#include "format_utils.hpp"
Jim Wright539b6082021-08-02 14:50:23 -050021#include "types.hpp"
Shawn McCarney1f8b1102024-06-21 18:57:46 -050022#include "ucd90160_device.hpp"
23#include "ucd90320_device.hpp"
24#include "utility.hpp"
Jim Wright539b6082021-08-02 14:50:23 -050025
Jim Wright539b6082021-08-02 14:50:23 -050026#include <exception>
Shawn McCarney1f8b1102024-06-21 18:57:46 -050027#include <format>
28#include <functional>
29#include <map>
30#include <span>
31#include <stdexcept>
32#include <thread>
33#include <utility>
Jim Wright539b6082021-08-02 14:50:23 -050034
35namespace phosphor::power::sequencer
Jim Wright10eb00f2021-07-21 12:10:38 -050036{
Jim Wright539b6082021-08-02 14:50:23 -050037
Shawn McCarney1f8b1102024-06-21 18:57:46 -050038const std::string powerOnTimeoutError =
39 "xyz.openbmc_project.Power.Error.PowerOnTimeout";
40
41const std::string powerOffTimeoutError =
42 "xyz.openbmc_project.Power.Error.PowerOffTimeout";
43
44const std::string shutdownError = "xyz.openbmc_project.Power.Error.Shutdown";
Jim Wright7a5dd992021-08-31 16:56:52 -050045
Patrick Williams7354ce62022-07-22 19:26:56 -050046PowerControl::PowerControl(sdbusplus::bus_t& bus,
Jim Wright539b6082021-08-02 14:50:23 -050047 const sdeventplus::Event& event) :
Patrick Williams3fa31a72022-04-05 21:22:58 -050048 PowerObject{bus, POWER_OBJ_PATH, PowerObject::action::defer_emit},
Shawn McCarney1f8b1102024-06-21 18:57:46 -050049 bus{bus}, services{bus},
Jim Wright1b639532022-11-19 17:41:33 -060050 pgoodWaitTimer{event, std::bind(&PowerControl::onFailureCallback, this)},
Jim Wrightb4ad95d2022-03-08 17:24:01 -060051 powerOnAllowedTime{std::chrono::steady_clock::now() + minimumColdStartTime},
Jim Wrightccea2d22021-12-10 14:10:46 -060052 timer{event, std::bind(&PowerControl::pollPgood, this), pollInterval}
Jim Wright539b6082021-08-02 14:50:23 -050053{
54 // Obtain dbus service name
55 bus.request_name(POWER_IFACE);
Jim Wright2d99bf72021-11-19 11:18:12 -060056
Shawn McCarney1f8b1102024-06-21 18:57:46 -050057 compatSysTypesFinder = std::make_unique<util::CompatibleSystemTypesFinder>(
58 bus, std::bind_front(&PowerControl::compatibleSystemTypesFound, this));
59
60 deviceFinder = std::make_unique<DeviceFinder>(
61 bus, std::bind_front(&PowerControl::deviceFound, this));
62
Jim Wright7a5dd992021-08-31 16:56:52 -050063 setUpGpio();
Jim Wright10eb00f2021-07-21 12:10:38 -050064}
Jim Wright539b6082021-08-02 14:50:23 -050065
Jim Wright22318a32021-08-27 15:56:09 -050066int PowerControl::getPgood() const
67{
68 return pgood;
69}
70
71int PowerControl::getPgoodTimeout() const
72{
73 return timeout.count();
74}
75
76int PowerControl::getState() const
77{
78 return state;
79}
80
Jim Wright1b639532022-11-19 17:41:33 -060081void PowerControl::onFailureCallback()
82{
Shawn McCarney1f8b1102024-06-21 18:57:46 -050083 services.logInfoMsg("After onFailure wait");
Jim Wright1b639532022-11-19 17:41:33 -060084
85 onFailure(false);
86
87 // Power good has failed, call for chassis hard power off
88 auto method = bus.new_method_call(util::SYSTEMD_SERVICE, util::SYSTEMD_ROOT,
89 util::SYSTEMD_INTERFACE, "StartUnit");
90 method.append(util::POWEROFF_TARGET);
91 method.append("replace");
92 bus.call_noreply(method);
93}
94
Shawn McCarney1f8b1102024-06-21 18:57:46 -050095void PowerControl::onFailure(bool wasTimeOut)
Jim Wright1b639532022-11-19 17:41:33 -060096{
Shawn McCarney1f8b1102024-06-21 18:57:46 -050097 std::string error;
98 std::map<std::string, std::string> additionalData{};
99
100 // Check if pgood fault occurred on rail monitored by power sequencer device
101 if (device)
102 {
103 try
104 {
105 error = device->findPgoodFault(services, powerSupplyError,
106 additionalData);
107 }
108 catch (const std::exception& e)
109 {
110 services.logErrorMsg(e.what());
111 additionalData.emplace("ERROR", e.what());
112 }
113 }
114
115 // If fault was not isolated to a voltage rail, select a more generic error
116 if (error.empty())
117 {
118 if (!powerSupplyError.empty())
119 {
120 error = powerSupplyError;
121 }
122 else if (wasTimeOut)
123 {
124 error = powerOnTimeoutError;
125 }
126 else
127 {
128 error = shutdownError;
129 }
130 }
131
132 services.logError(error, Entry::Level::Critical, additionalData);
133
134 if (!wasTimeOut)
135 {
136 services.createBMCDump();
137 }
Jim Wright1b639532022-11-19 17:41:33 -0600138}
139
Jim Wright539b6082021-08-02 14:50:23 -0500140void PowerControl::pollPgood()
Jim Wright7a5dd992021-08-31 16:56:52 -0500141{
142 if (inStateTransition)
143 {
Jim Wright9d7d95c2021-11-16 11:32:23 -0600144 // In transition between power on and off, check for timeout
Jim Wright7a5dd992021-08-31 16:56:52 -0500145 const auto now = std::chrono::steady_clock::now();
146 if (now > pgoodTimeoutTime)
147 {
Shawn McCarney1f8b1102024-06-21 18:57:46 -0500148 services.logErrorMsg(std::format(
149 "Power state transition timeout, state: {}", state));
Jim Wright7a5dd992021-08-31 16:56:52 -0500150 inStateTransition = false;
Jim Wright4e25df52021-11-17 09:38:00 -0600151
Jim Wright930458c2022-01-24 14:37:27 -0600152 if (state)
Jim Wright4e25df52021-11-17 09:38:00 -0600153 {
Jim Wright930458c2022-01-24 14:37:27 -0600154 // Time out powering on
Jim Wright1b639532022-11-19 17:41:33 -0600155 onFailure(true);
Jim Wright4e25df52021-11-17 09:38:00 -0600156 }
Jim Wright930458c2022-01-24 14:37:27 -0600157 else
Jim Wright4e25df52021-11-17 09:38:00 -0600158 {
Jim Wright930458c2022-01-24 14:37:27 -0600159 // Time out powering off
160 std::map<std::string, std::string> additionalData{};
Shawn McCarney1f8b1102024-06-21 18:57:46 -0500161 services.logError(powerOffTimeoutError, Entry::Level::Critical,
162 additionalData);
Jim Wright4e25df52021-11-17 09:38:00 -0600163 }
164
Jim Wright48752622022-02-28 20:37:53 -0600165 failureFound = true;
Jim Wright7a5dd992021-08-31 16:56:52 -0500166 return;
167 }
168 }
169
170 int pgoodState = pgoodLine.get_value();
171 if (pgoodState != pgood)
172 {
Jim Wright9d7d95c2021-11-16 11:32:23 -0600173 // Power good has changed since last read
Jim Wright7a5dd992021-08-31 16:56:52 -0500174 pgood = pgoodState;
175 if (pgoodState == 0)
176 {
177 emitPowerLostSignal();
178 }
179 else
180 {
181 emitPowerGoodSignal();
Jim Wright48752622022-02-28 20:37:53 -0600182 // Clear any errors on the transition to power on
Jim Wrightabd64b92022-02-11 09:56:56 -0600183 powerSupplyError.clear();
Jim Wright48752622022-02-28 20:37:53 -0600184 failureFound = false;
Jim Wright7a5dd992021-08-31 16:56:52 -0500185 }
186 emitPropertyChangedSignal("pgood");
187 }
188 if (pgoodState == state)
189 {
Jim Wright9d7d95c2021-11-16 11:32:23 -0600190 // Power good matches requested state
Jim Wright7a5dd992021-08-31 16:56:52 -0500191 inStateTransition = false;
192 }
Jim Wright48752622022-02-28 20:37:53 -0600193 else if (!inStateTransition && (pgoodState == 0) && !failureFound)
Jim Wright9d7d95c2021-11-16 11:32:23 -0600194 {
195 // Not in power off state, not changing state, and power good is off
Shawn McCarney1f8b1102024-06-21 18:57:46 -0500196 services.logErrorMsg("Chassis pgood failure");
Jim Wright1b639532022-11-19 17:41:33 -0600197 pgoodWaitTimer.restartOnce(std::chrono::seconds(7));
Jim Wright48752622022-02-28 20:37:53 -0600198 failureFound = true;
Jim Wright9d7d95c2021-11-16 11:32:23 -0600199 }
Jim Wright7a5dd992021-08-31 16:56:52 -0500200}
Jim Wright539b6082021-08-02 14:50:23 -0500201
Jim Wright22318a32021-08-27 15:56:09 -0500202void PowerControl::setPgoodTimeout(int t)
203{
204 if (timeout.count() != t)
205 {
206 timeout = std::chrono::seconds(t);
207 emitPropertyChangedSignal("pgood_timeout");
208 }
209}
210
Jim Wrightccea2d22021-12-10 14:10:46 -0600211void PowerControl::setPowerSupplyError(const std::string& error)
212{
213 powerSupplyError = error;
214}
215
Jim Wright22318a32021-08-27 15:56:09 -0500216void PowerControl::setState(int s)
217{
218 if (state == s)
219 {
Shawn McCarney1f8b1102024-06-21 18:57:46 -0500220 services.logInfoMsg(
221 std::format("Power already at requested state: {}", state));
Jim Wright22318a32021-08-27 15:56:09 -0500222 return;
223 }
Jim Wright209690b2021-11-11 14:46:42 -0600224 if (s == 0)
225 {
226 // Wait for two seconds when powering down. This is to allow host and
227 // other BMC applications time to complete power off processing
228 std::this_thread::sleep_for(std::chrono::seconds(2));
229 }
Jim Wrightb4ad95d2022-03-08 17:24:01 -0600230 else
231 {
232 // If minimum power off time has not passed, wait
233 if (powerOnAllowedTime > std::chrono::steady_clock::now())
234 {
Shawn McCarney1f8b1102024-06-21 18:57:46 -0500235 services.logInfoMsg(std::format(
236 "Waiting {} seconds until power on allowed",
237 std::chrono::duration_cast<std::chrono::seconds>(
238 powerOnAllowedTime - std::chrono::steady_clock::now())
239 .count()));
Jim Wrightb4ad95d2022-03-08 17:24:01 -0600240 }
241 std::this_thread::sleep_until(powerOnAllowedTime);
242 }
Jim Wright22318a32021-08-27 15:56:09 -0500243
Shawn McCarney1f8b1102024-06-21 18:57:46 -0500244 services.logInfoMsg(std::format("setState: {}", s));
245 services.logInfoMsg(std::format("Powering chassis {}", (s ? "on" : "off")));
Jim Wright7a5dd992021-08-31 16:56:52 -0500246 powerControlLine.request(
247 {"phosphor-power-control", gpiod::line_request::DIRECTION_OUTPUT, 0});
248 powerControlLine.set_value(s);
249 powerControlLine.release();
250
Jim Wrightb4ad95d2022-03-08 17:24:01 -0600251 if (s == 0)
252 {
253 // Set a minimum amount of time to wait before next power on
Patrick Williams48781ae2023-05-10 07:50:50 -0500254 powerOnAllowedTime = std::chrono::steady_clock::now() +
255 minimumPowerOffTime;
Jim Wrightb4ad95d2022-03-08 17:24:01 -0600256 }
257
Jim Wright7a5dd992021-08-31 16:56:52 -0500258 pgoodTimeoutTime = std::chrono::steady_clock::now() + timeout;
259 inStateTransition = true;
Jim Wright22318a32021-08-27 15:56:09 -0500260 state = s;
261 emitPropertyChangedSignal("state");
262}
263
Shawn McCarney1f8b1102024-06-21 18:57:46 -0500264void PowerControl::compatibleSystemTypesFound(
265 const std::vector<std::string>& types)
Jim Wright2d99bf72021-11-19 11:18:12 -0600266{
Shawn McCarney1f8b1102024-06-21 18:57:46 -0500267 // If we don't already have compatible system types
268 if (compatibleSystemTypes.empty())
Jim Wright2d99bf72021-11-19 11:18:12 -0600269 {
Shawn McCarney1f8b1102024-06-21 18:57:46 -0500270 std::string typesStr = format_utils::toString(std::span{types});
271 services.logInfoMsg(
272 std::format("Compatible system types found: {}", typesStr));
Jim Wright2d99bf72021-11-19 11:18:12 -0600273
Shawn McCarney1f8b1102024-06-21 18:57:46 -0500274 // Store compatible system types
275 compatibleSystemTypes = types;
Jim Wrighteae2d612022-12-23 13:14:17 -0600276
Shawn McCarney1f8b1102024-06-21 18:57:46 -0500277 // Load config file and create device object if possible
278 loadConfigFileAndCreateDevice();
Jim Wright2d99bf72021-11-19 11:18:12 -0600279 }
Shawn McCarney1f8b1102024-06-21 18:57:46 -0500280}
281
282void PowerControl::deviceFound(const DeviceProperties& properties)
283{
284 // If we don't already have device properties
285 if (!deviceProperties)
Jim Wright2d99bf72021-11-19 11:18:12 -0600286 {
Shawn McCarney1f8b1102024-06-21 18:57:46 -0500287 services.logInfoMsg(std::format(
288 "Power sequencer device found: type={}, name={}, bus={:d}, address={:#02x}",
289 properties.type, properties.name, properties.bus,
290 properties.address));
291
292 // Store device properties
293 deviceProperties = properties;
294
295 // Load config file and create device object if possible
296 loadConfigFileAndCreateDevice();
Jim Wright2d99bf72021-11-19 11:18:12 -0600297 }
298}
299
Jim Wright7a5dd992021-08-31 16:56:52 -0500300void PowerControl::setUpGpio()
301{
Jim Wright2d99bf72021-11-19 11:18:12 -0600302 const std::string powerControlLineName = "power-chassis-control";
303 const std::string pgoodLineName = "power-chassis-good";
304
Jim Wright7a5dd992021-08-31 16:56:52 -0500305 pgoodLine = gpiod::find_line(pgoodLineName);
306 if (!pgoodLine)
307 {
Jim Wright209690b2021-11-11 14:46:42 -0600308 std::string errorString{"GPIO line name not found: " + pgoodLineName};
Shawn McCarney1f8b1102024-06-21 18:57:46 -0500309 services.logErrorMsg(errorString);
Jim Wright7a5dd992021-08-31 16:56:52 -0500310 throw std::runtime_error(errorString);
311 }
312 powerControlLine = gpiod::find_line(powerControlLineName);
313 if (!powerControlLine)
314 {
Jim Wright209690b2021-11-11 14:46:42 -0600315 std::string errorString{"GPIO line name not found: " +
316 powerControlLineName};
Shawn McCarney1f8b1102024-06-21 18:57:46 -0500317 services.logErrorMsg(errorString);
Jim Wright7a5dd992021-08-31 16:56:52 -0500318 throw std::runtime_error(errorString);
319 }
320
321 pgoodLine.request(
322 {"phosphor-power-control", gpiod::line_request::DIRECTION_INPUT, 0});
323 int pgoodState = pgoodLine.get_value();
324 pgood = pgoodState;
325 state = pgoodState;
Shawn McCarney1f8b1102024-06-21 18:57:46 -0500326 services.logInfoMsg(std::format("Pgood state: {}", pgoodState));
327}
328
329void PowerControl::loadConfigFileAndCreateDevice()
330{
331 // If compatible system types and device properties have been found
332 if (!compatibleSystemTypes.empty() && deviceProperties)
333 {
334 // Find the JSON configuration file
335 std::filesystem::path configFile = findConfigFile();
336 if (!configFile.empty())
337 {
338 // Parse the JSON configuration file
339 std::vector<std::unique_ptr<Rail>> rails;
340 if (parseConfigFile(configFile, rails))
341 {
342 // Create the power sequencer device object
343 createDevice(std::move(rails));
344 }
345 }
346 }
347}
348
349std::filesystem::path PowerControl::findConfigFile()
350{
351 // Find config file for current system based on compatible system types
352 std::filesystem::path configFile;
353 if (!compatibleSystemTypes.empty())
354 {
355 try
356 {
357 configFile = config_file_parser::find(compatibleSystemTypes);
358 if (!configFile.empty())
359 {
360 services.logInfoMsg(std::format(
361 "JSON configuration file found: {}", configFile.string()));
362 }
363 }
364 catch (const std::exception& e)
365 {
366 services.logErrorMsg(std::format(
367 "Unable to find JSON configuration file: {}", e.what()));
368 }
369 }
370 return configFile;
371}
372
373bool PowerControl::parseConfigFile(const std::filesystem::path& configFile,
374 std::vector<std::unique_ptr<Rail>>& rails)
375{
376 // Parse JSON configuration file
377 bool wasParsed{false};
378 try
379 {
380 rails = config_file_parser::parse(configFile);
381 wasParsed = true;
382 }
383 catch (const std::exception& e)
384 {
385 services.logErrorMsg(std::format(
386 "Unable to parse JSON configuration file: {}", e.what()));
387 }
388 return wasParsed;
389}
390
391void PowerControl::createDevice(std::vector<std::unique_ptr<Rail>> rails)
392{
393 // Create power sequencer device based on device properties
394 if (deviceProperties)
395 {
396 try
397 {
398 if (deviceProperties->type == UCD90160Device::deviceName)
399 {
400 device = std::make_unique<UCD90160Device>(
401 std::move(rails), services, deviceProperties->bus,
402 deviceProperties->address);
403 }
404 else if (deviceProperties->type == UCD90320Device::deviceName)
405 {
406 device = std::make_unique<UCD90320Device>(
407 std::move(rails), services, deviceProperties->bus,
408 deviceProperties->address);
409 }
410 else
411 {
412 throw std::runtime_error{std::format(
413 "Unsupported device type: {}", deviceProperties->type)};
414 }
415 services.logInfoMsg(std::format(
416 "Power sequencer device created: {}", device->getName()));
417 }
418 catch (const std::exception& e)
419 {
420 services.logErrorMsg(
421 std::format("Unable to create device object: {}", e.what()));
422 }
423 }
Jim Wright7a5dd992021-08-31 16:56:52 -0500424}
425
Jim Wright539b6082021-08-02 14:50:23 -0500426} // namespace phosphor::power::sequencer