blob: f5b4735b72e29f1cc99566e546b0a2ca6dae8c3a [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 McCarney5a8aefe2025-11-13 14:38:53 -060019#include "chassis.hpp"
Shawn McCarney1f8b1102024-06-21 18:57:46 -050020#include "config_file_parser.hpp"
21#include "format_utils.hpp"
Jim Wright539b6082021-08-02 14:50:23 -050022#include "types.hpp"
Shawn McCarney1f8b1102024-06-21 18:57:46 -050023#include "utility.hpp"
Jim Wright539b6082021-08-02 14:50:23 -050024
Jim Wright539b6082021-08-02 14:50:23 -050025#include <exception>
Shawn McCarney1f8b1102024-06-21 18:57:46 -050026#include <format>
27#include <functional>
Shawn McCarney1f8b1102024-06-21 18:57:46 -050028#include <span>
29#include <stdexcept>
30#include <thread>
31#include <utility>
Jim Wright539b6082021-08-02 14:50:23 -050032
33namespace phosphor::power::sequencer
Jim Wright10eb00f2021-07-21 12:10:38 -050034{
Jim Wright539b6082021-08-02 14:50:23 -050035
Shawn McCarney1f8b1102024-06-21 18:57:46 -050036const std::string powerOnTimeoutError =
37 "xyz.openbmc_project.Power.Error.PowerOnTimeout";
38
39const std::string powerOffTimeoutError =
40 "xyz.openbmc_project.Power.Error.PowerOffTimeout";
41
42const std::string shutdownError = "xyz.openbmc_project.Power.Error.Shutdown";
Jim Wright7a5dd992021-08-31 16:56:52 -050043
Patrick Williams7354ce62022-07-22 19:26:56 -050044PowerControl::PowerControl(sdbusplus::bus_t& bus,
Jim Wright539b6082021-08-02 14:50:23 -050045 const sdeventplus::Event& event) :
Patrick Williamsf5402192024-08-16 15:20:53 -040046 PowerObject{bus, POWER_OBJ_PATH, PowerObject::action::defer_emit}, bus{bus},
47 services{bus},
Jim Wright1b639532022-11-19 17:41:33 -060048 pgoodWaitTimer{event, std::bind(&PowerControl::onFailureCallback, this)},
Jim Wrightb4ad95d2022-03-08 17:24:01 -060049 powerOnAllowedTime{std::chrono::steady_clock::now() + minimumColdStartTime},
Jim Wrightccea2d22021-12-10 14:10:46 -060050 timer{event, std::bind(&PowerControl::pollPgood, this), pollInterval}
Jim Wright539b6082021-08-02 14:50:23 -050051{
52 // Obtain dbus service name
53 bus.request_name(POWER_IFACE);
Jim Wright2d99bf72021-11-19 11:18:12 -060054
Shawn McCarney1f8b1102024-06-21 18:57:46 -050055 compatSysTypesFinder = std::make_unique<util::CompatibleSystemTypesFinder>(
56 bus, std::bind_front(&PowerControl::compatibleSystemTypesFound, this));
57
Jim Wright7a5dd992021-08-31 16:56:52 -050058 setUpGpio();
Jim Wright10eb00f2021-07-21 12:10:38 -050059}
Jim Wright539b6082021-08-02 14:50:23 -050060
Jim Wright22318a32021-08-27 15:56:09 -050061int PowerControl::getPgood() const
62{
63 return pgood;
64}
65
66int PowerControl::getPgoodTimeout() const
67{
68 return timeout.count();
69}
70
71int PowerControl::getState() const
72{
73 return state;
74}
75
Jim Wright1b639532022-11-19 17:41:33 -060076void PowerControl::onFailureCallback()
77{
Shawn McCarney1f8b1102024-06-21 18:57:46 -050078 services.logInfoMsg("After onFailure wait");
Jim Wright1b639532022-11-19 17:41:33 -060079
80 onFailure(false);
81
82 // Power good has failed, call for chassis hard power off
83 auto method = bus.new_method_call(util::SYSTEMD_SERVICE, util::SYSTEMD_ROOT,
84 util::SYSTEMD_INTERFACE, "StartUnit");
85 method.append(util::POWEROFF_TARGET);
86 method.append("replace");
87 bus.call_noreply(method);
88}
89
Shawn McCarney1f8b1102024-06-21 18:57:46 -050090void PowerControl::onFailure(bool wasTimeOut)
Jim Wright1b639532022-11-19 17:41:33 -060091{
Shawn McCarney1f8b1102024-06-21 18:57:46 -050092 std::string error;
93 std::map<std::string, std::string> additionalData{};
94
Shawn McCarney5a8aefe2025-11-13 14:38:53 -060095 // Check if pgood fault occurred on one of the rails being monitored
96 error = findPgoodFault(additionalData);
Shawn McCarney1f8b1102024-06-21 18:57:46 -050097
98 // If fault was not isolated to a voltage rail, select a more generic error
99 if (error.empty())
100 {
101 if (!powerSupplyError.empty())
102 {
103 error = powerSupplyError;
104 }
105 else if (wasTimeOut)
106 {
107 error = powerOnTimeoutError;
108 }
109 else
110 {
111 error = shutdownError;
112 }
113 }
114
115 services.logError(error, Entry::Level::Critical, additionalData);
116
117 if (!wasTimeOut)
118 {
119 services.createBMCDump();
120 }
Jim Wright1b639532022-11-19 17:41:33 -0600121}
122
Shawn McCarney5a8aefe2025-11-13 14:38:53 -0600123std::string PowerControl::findPgoodFault(
124 std::map<std::string, std::string>& additionalData)
125{
126 // Note: This code is temporary. It will be replaced when additional
127 // multi-chassis support is implementated in this application.
128 std::string error{};
129 if (system)
130 {
131 try
132 {
133 for (auto& chassis : system->getChassis())
134 {
135 for (auto& powerSequencer : chassis->getPowerSequencers())
136 {
137 error = powerSequencer->findPgoodFault(
138 services, powerSupplyError, additionalData);
139 if (!error.empty())
140 {
141 return error;
142 }
143 }
144 }
145 }
146 catch (const std::exception& e)
147 {
148 services.logErrorMsg(e.what());
149 additionalData.emplace("ERROR", e.what());
150 }
151 }
152 return error;
153}
154
Jim Wright539b6082021-08-02 14:50:23 -0500155void PowerControl::pollPgood()
Jim Wright7a5dd992021-08-31 16:56:52 -0500156{
157 if (inStateTransition)
158 {
Jim Wright9d7d95c2021-11-16 11:32:23 -0600159 // In transition between power on and off, check for timeout
Jim Wright7a5dd992021-08-31 16:56:52 -0500160 const auto now = std::chrono::steady_clock::now();
161 if (now > pgoodTimeoutTime)
162 {
Shawn McCarney1f8b1102024-06-21 18:57:46 -0500163 services.logErrorMsg(std::format(
164 "Power state transition timeout, state: {}", state));
Jim Wright7a5dd992021-08-31 16:56:52 -0500165 inStateTransition = false;
Jim Wright4e25df52021-11-17 09:38:00 -0600166
Jim Wright930458c2022-01-24 14:37:27 -0600167 if (state)
Jim Wright4e25df52021-11-17 09:38:00 -0600168 {
Jim Wright930458c2022-01-24 14:37:27 -0600169 // Time out powering on
Jim Wright1b639532022-11-19 17:41:33 -0600170 onFailure(true);
Jim Wright4e25df52021-11-17 09:38:00 -0600171 }
Jim Wright930458c2022-01-24 14:37:27 -0600172 else
Jim Wright4e25df52021-11-17 09:38:00 -0600173 {
Jim Wright930458c2022-01-24 14:37:27 -0600174 // Time out powering off
175 std::map<std::string, std::string> additionalData{};
Shawn McCarney1f8b1102024-06-21 18:57:46 -0500176 services.logError(powerOffTimeoutError, Entry::Level::Critical,
177 additionalData);
Jim Wright4e25df52021-11-17 09:38:00 -0600178 }
179
Jim Wright48752622022-02-28 20:37:53 -0600180 failureFound = true;
Jim Wright7a5dd992021-08-31 16:56:52 -0500181 return;
182 }
183 }
184
185 int pgoodState = pgoodLine.get_value();
186 if (pgoodState != pgood)
187 {
Jim Wright9d7d95c2021-11-16 11:32:23 -0600188 // Power good has changed since last read
Jim Wright7a5dd992021-08-31 16:56:52 -0500189 pgood = pgoodState;
190 if (pgoodState == 0)
191 {
192 emitPowerLostSignal();
193 }
194 else
195 {
196 emitPowerGoodSignal();
Jim Wright48752622022-02-28 20:37:53 -0600197 // Clear any errors on the transition to power on
Jim Wrightabd64b92022-02-11 09:56:56 -0600198 powerSupplyError.clear();
Jim Wright48752622022-02-28 20:37:53 -0600199 failureFound = false;
Jim Wright7a5dd992021-08-31 16:56:52 -0500200 }
201 emitPropertyChangedSignal("pgood");
202 }
203 if (pgoodState == state)
204 {
Jim Wright9d7d95c2021-11-16 11:32:23 -0600205 // Power good matches requested state
Jim Wright7a5dd992021-08-31 16:56:52 -0500206 inStateTransition = false;
207 }
Jim Wright48752622022-02-28 20:37:53 -0600208 else if (!inStateTransition && (pgoodState == 0) && !failureFound)
Jim Wright9d7d95c2021-11-16 11:32:23 -0600209 {
210 // Not in power off state, not changing state, and power good is off
Shawn McCarney1f8b1102024-06-21 18:57:46 -0500211 services.logErrorMsg("Chassis pgood failure");
Jim Wright1b639532022-11-19 17:41:33 -0600212 pgoodWaitTimer.restartOnce(std::chrono::seconds(7));
Jim Wright48752622022-02-28 20:37:53 -0600213 failureFound = true;
Jim Wright9d7d95c2021-11-16 11:32:23 -0600214 }
Jim Wright7a5dd992021-08-31 16:56:52 -0500215}
Jim Wright539b6082021-08-02 14:50:23 -0500216
Jim Wright22318a32021-08-27 15:56:09 -0500217void PowerControl::setPgoodTimeout(int t)
218{
219 if (timeout.count() != t)
220 {
221 timeout = std::chrono::seconds(t);
222 emitPropertyChangedSignal("pgood_timeout");
223 }
224}
225
Jim Wrightccea2d22021-12-10 14:10:46 -0600226void PowerControl::setPowerSupplyError(const std::string& error)
227{
228 powerSupplyError = error;
229}
230
Jim Wright22318a32021-08-27 15:56:09 -0500231void PowerControl::setState(int s)
232{
233 if (state == s)
234 {
Shawn McCarney1f8b1102024-06-21 18:57:46 -0500235 services.logInfoMsg(
236 std::format("Power already at requested state: {}", state));
Jim Wright22318a32021-08-27 15:56:09 -0500237 return;
238 }
Jim Wright209690b2021-11-11 14:46:42 -0600239 if (s == 0)
240 {
241 // Wait for two seconds when powering down. This is to allow host and
242 // other BMC applications time to complete power off processing
243 std::this_thread::sleep_for(std::chrono::seconds(2));
244 }
Jim Wrightb4ad95d2022-03-08 17:24:01 -0600245 else
246 {
247 // If minimum power off time has not passed, wait
248 if (powerOnAllowedTime > std::chrono::steady_clock::now())
249 {
Shawn McCarney1f8b1102024-06-21 18:57:46 -0500250 services.logInfoMsg(std::format(
251 "Waiting {} seconds until power on allowed",
252 std::chrono::duration_cast<std::chrono::seconds>(
253 powerOnAllowedTime - std::chrono::steady_clock::now())
254 .count()));
Jim Wrightb4ad95d2022-03-08 17:24:01 -0600255 }
256 std::this_thread::sleep_until(powerOnAllowedTime);
257 }
Jim Wright22318a32021-08-27 15:56:09 -0500258
Shawn McCarney1f8b1102024-06-21 18:57:46 -0500259 services.logInfoMsg(std::format("setState: {}", s));
260 services.logInfoMsg(std::format("Powering chassis {}", (s ? "on" : "off")));
Jim Wright7a5dd992021-08-31 16:56:52 -0500261 powerControlLine.request(
262 {"phosphor-power-control", gpiod::line_request::DIRECTION_OUTPUT, 0});
263 powerControlLine.set_value(s);
264 powerControlLine.release();
265
Jim Wrightb4ad95d2022-03-08 17:24:01 -0600266 if (s == 0)
267 {
268 // Set a minimum amount of time to wait before next power on
Patrick Williamsf5402192024-08-16 15:20:53 -0400269 powerOnAllowedTime =
270 std::chrono::steady_clock::now() + minimumPowerOffTime;
Jim Wrightb4ad95d2022-03-08 17:24:01 -0600271 }
272
Jim Wright7a5dd992021-08-31 16:56:52 -0500273 pgoodTimeoutTime = std::chrono::steady_clock::now() + timeout;
274 inStateTransition = true;
Jim Wright22318a32021-08-27 15:56:09 -0500275 state = s;
276 emitPropertyChangedSignal("state");
277}
278
Shawn McCarney1f8b1102024-06-21 18:57:46 -0500279void PowerControl::compatibleSystemTypesFound(
280 const std::vector<std::string>& types)
Jim Wright2d99bf72021-11-19 11:18:12 -0600281{
Shawn McCarney1f8b1102024-06-21 18:57:46 -0500282 // If we don't already have compatible system types
283 if (compatibleSystemTypes.empty())
Jim Wright2d99bf72021-11-19 11:18:12 -0600284 {
Shawn McCarney1f8b1102024-06-21 18:57:46 -0500285 std::string typesStr = format_utils::toString(std::span{types});
286 services.logInfoMsg(
287 std::format("Compatible system types found: {}", typesStr));
Jim Wright2d99bf72021-11-19 11:18:12 -0600288
Shawn McCarney1f8b1102024-06-21 18:57:46 -0500289 // Store compatible system types
290 compatibleSystemTypes = types;
Jim Wrighteae2d612022-12-23 13:14:17 -0600291
Shawn McCarney5a8aefe2025-11-13 14:38:53 -0600292 // Load config file that matches one of the compatible system types
293 loadConfigFile();
Jim Wright2d99bf72021-11-19 11:18:12 -0600294 }
295}
296
Jim Wright7a5dd992021-08-31 16:56:52 -0500297void PowerControl::setUpGpio()
298{
Jim Wright2d99bf72021-11-19 11:18:12 -0600299 const std::string powerControlLineName = "power-chassis-control";
300 const std::string pgoodLineName = "power-chassis-good";
301
Jim Wright7a5dd992021-08-31 16:56:52 -0500302 pgoodLine = gpiod::find_line(pgoodLineName);
303 if (!pgoodLine)
304 {
Jim Wright209690b2021-11-11 14:46:42 -0600305 std::string errorString{"GPIO line name not found: " + pgoodLineName};
Shawn McCarney1f8b1102024-06-21 18:57:46 -0500306 services.logErrorMsg(errorString);
Jim Wright7a5dd992021-08-31 16:56:52 -0500307 throw std::runtime_error(errorString);
308 }
309 powerControlLine = gpiod::find_line(powerControlLineName);
310 if (!powerControlLine)
311 {
Patrick Williamsf5402192024-08-16 15:20:53 -0400312 std::string errorString{
313 "GPIO line name not found: " + powerControlLineName};
Shawn McCarney1f8b1102024-06-21 18:57:46 -0500314 services.logErrorMsg(errorString);
Jim Wright7a5dd992021-08-31 16:56:52 -0500315 throw std::runtime_error(errorString);
316 }
317
318 pgoodLine.request(
319 {"phosphor-power-control", gpiod::line_request::DIRECTION_INPUT, 0});
320 int pgoodState = pgoodLine.get_value();
321 pgood = pgoodState;
322 state = pgoodState;
Shawn McCarney1f8b1102024-06-21 18:57:46 -0500323 services.logInfoMsg(std::format("Pgood state: {}", pgoodState));
324}
325
Shawn McCarney5a8aefe2025-11-13 14:38:53 -0600326void PowerControl::loadConfigFile()
Shawn McCarney1f8b1102024-06-21 18:57:46 -0500327{
Shawn McCarney5a8aefe2025-11-13 14:38:53 -0600328 try
Shawn McCarney1f8b1102024-06-21 18:57:46 -0500329 {
Shawn McCarney1f8b1102024-06-21 18:57:46 -0500330 std::filesystem::path configFile = findConfigFile();
331 if (!configFile.empty())
332 {
Shawn McCarney5a8aefe2025-11-13 14:38:53 -0600333 std::vector<std::unique_ptr<Chassis>> chassis =
334 config_file_parser::parse(configFile, services);
335 system = std::make_unique<System>(std::move(chassis));
Shawn McCarney1f8b1102024-06-21 18:57:46 -0500336 }
337 }
Shawn McCarney5a8aefe2025-11-13 14:38:53 -0600338 catch (const std::exception& e)
339 {
340 services.logErrorMsg(std::format(
341 "Unable to parse JSON configuration file: {}", e.what()));
342 }
Shawn McCarney1f8b1102024-06-21 18:57:46 -0500343}
344
345std::filesystem::path PowerControl::findConfigFile()
346{
347 // Find config file for current system based on compatible system types
348 std::filesystem::path configFile;
349 if (!compatibleSystemTypes.empty())
350 {
351 try
352 {
353 configFile = config_file_parser::find(compatibleSystemTypes);
354 if (!configFile.empty())
355 {
356 services.logInfoMsg(std::format(
357 "JSON configuration file found: {}", configFile.string()));
358 }
359 }
360 catch (const std::exception& e)
361 {
362 services.logErrorMsg(std::format(
363 "Unable to find JSON configuration file: {}", e.what()));
364 }
365 }
366 return configFile;
367}
368
Jim Wright539b6082021-08-02 14:50:23 -0500369} // namespace phosphor::power::sequencer