blob: 981a9576619963058e37a6376f83e51722053684 [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
19#include "types.hpp"
Jim Wright9d7d95c2021-11-16 11:32:23 -060020#include "utility.hpp"
Jim Wright539b6082021-08-02 14:50:23 -050021
Jim Wright22318a32021-08-27 15:56:09 -050022#include <fmt/format.h>
Jim Wright4e25df52021-11-17 09:38:00 -060023#include <sys/types.h>
24#include <unistd.h>
Jim Wright22318a32021-08-27 15:56:09 -050025
Jim Wright7a5dd992021-08-31 16:56:52 -050026#include <phosphor-logging/elog-errors.hpp>
27#include <phosphor-logging/elog.hpp>
Jim Wright539b6082021-08-02 14:50:23 -050028#include <phosphor-logging/log.hpp>
Jim Wright7a5dd992021-08-31 16:56:52 -050029#include <xyz/openbmc_project/Common/error.hpp>
Jim Wright539b6082021-08-02 14:50:23 -050030
Jim Wright539b6082021-08-02 14:50:23 -050031#include <exception>
Jim Wright7a5dd992021-08-31 16:56:52 -050032#include <string>
Jim Wright539b6082021-08-02 14:50:23 -050033
34using namespace phosphor::logging;
35
36namespace phosphor::power::sequencer
Jim Wright10eb00f2021-07-21 12:10:38 -050037{
Jim Wright539b6082021-08-02 14:50:23 -050038
Jim Wright7a5dd992021-08-31 16:56:52 -050039const std::string powerControlLineName = "power-chassis-control";
40const std::string pgoodLineName = "power-chassis-good";
41
Jim Wright539b6082021-08-02 14:50:23 -050042PowerControl::PowerControl(sdbusplus::bus::bus& bus,
43 const sdeventplus::Event& event) :
Jim Wright22318a32021-08-27 15:56:09 -050044 PowerObject{bus, POWER_OBJ_PATH, true},
45 bus{bus}, timer{event, std::bind(&PowerControl::pollPgood, this),
46 pollInterval}
Jim Wright539b6082021-08-02 14:50:23 -050047{
48 // Obtain dbus service name
49 bus.request_name(POWER_IFACE);
Jim Wright7a5dd992021-08-31 16:56:52 -050050 setUpGpio();
Jim Wright10eb00f2021-07-21 12:10:38 -050051}
Jim Wright539b6082021-08-02 14:50:23 -050052
Jim Wright22318a32021-08-27 15:56:09 -050053int PowerControl::getPgood() const
54{
55 return pgood;
56}
57
58int PowerControl::getPgoodTimeout() const
59{
60 return timeout.count();
61}
62
63int PowerControl::getState() const
64{
65 return state;
66}
67
Jim Wright539b6082021-08-02 14:50:23 -050068void PowerControl::pollPgood()
Jim Wright7a5dd992021-08-31 16:56:52 -050069{
70 if (inStateTransition)
71 {
Jim Wright9d7d95c2021-11-16 11:32:23 -060072 // In transition between power on and off, check for timeout
Jim Wright7a5dd992021-08-31 16:56:52 -050073 const auto now = std::chrono::steady_clock::now();
74 if (now > pgoodTimeoutTime)
75 {
76 log<level::ERR>("ERROR PowerControl: Pgood poll timeout");
77 inStateTransition = false;
Jim Wright4e25df52021-11-17 09:38:00 -060078
79 try
80 {
81 auto method = bus.new_method_call(
82 "xyz.openbmc_project.Logging",
83 "/xyz/openbmc_project/logging",
84 "xyz.openbmc_project.Logging.Create", "Create");
85
86 std::map<std::string, std::string> additionalData;
87 // Add PID to AdditionalData
88 additionalData.emplace("_PID", std::to_string(getpid()));
89
90 method.append(
91 state ? "xyz.openbmc_project.Power.Error.PowerOnTimeout"
92 : "xyz.openbmc_project.Power.Error.PowerOffTimeout",
93 sdbusplus::xyz::openbmc_project::Logging::server::Entry::
94 Level::Critical,
95 additionalData);
96 bus.call_noreply(method);
97 }
98 catch (const std::exception& e)
99 {
100 log<level::ERR>(
101 fmt::format(
102 "Unable to log timeout error, state: {}, error {}",
103 state, e.what())
104 .c_str());
105 }
106
Jim Wright7a5dd992021-08-31 16:56:52 -0500107 return;
108 }
109 }
110
111 int pgoodState = pgoodLine.get_value();
112 if (pgoodState != pgood)
113 {
Jim Wright9d7d95c2021-11-16 11:32:23 -0600114 // Power good has changed since last read
Jim Wright7a5dd992021-08-31 16:56:52 -0500115 pgood = pgoodState;
116 if (pgoodState == 0)
117 {
118 emitPowerLostSignal();
119 }
120 else
121 {
122 emitPowerGoodSignal();
123 }
124 emitPropertyChangedSignal("pgood");
125 }
126 if (pgoodState == state)
127 {
Jim Wright9d7d95c2021-11-16 11:32:23 -0600128 // Power good matches requested state
Jim Wright7a5dd992021-08-31 16:56:52 -0500129 inStateTransition = false;
130 }
Jim Wright9d7d95c2021-11-16 11:32:23 -0600131 else if (!inStateTransition && (pgoodState == 0))
132 {
133 // Not in power off state, not changing state, and power good is off
134 // Power good has failed, call for chassis hard power off
135 log<level::ERR>("Chassis pgood failure");
136
137 auto method =
138 bus.new_method_call(util::SYSTEMD_SERVICE, util::SYSTEMD_ROOT,
139 util::SYSTEMD_INTERFACE, "StartUnit");
140 method.append(util::POWEROFF_TARGET);
141 method.append("replace");
142 bus.call_noreply(method);
143 }
Jim Wright7a5dd992021-08-31 16:56:52 -0500144}
Jim Wright539b6082021-08-02 14:50:23 -0500145
Jim Wright22318a32021-08-27 15:56:09 -0500146void PowerControl::setPgoodTimeout(int t)
147{
148 if (timeout.count() != t)
149 {
150 timeout = std::chrono::seconds(t);
151 emitPropertyChangedSignal("pgood_timeout");
152 }
153}
154
155void PowerControl::setState(int s)
156{
157 if (state == s)
158 {
159 log<level::INFO>(
160 fmt::format("Power already at requested state: {}", state).c_str());
161 return;
162 }
Jim Wright209690b2021-11-11 14:46:42 -0600163 if (s == 0)
164 {
165 // Wait for two seconds when powering down. This is to allow host and
166 // other BMC applications time to complete power off processing
167 std::this_thread::sleep_for(std::chrono::seconds(2));
168 }
Jim Wright22318a32021-08-27 15:56:09 -0500169
170 log<level::INFO>(fmt::format("setState: {}", s).c_str());
Jim Wright7a5dd992021-08-31 16:56:52 -0500171 powerControlLine.request(
172 {"phosphor-power-control", gpiod::line_request::DIRECTION_OUTPUT, 0});
173 powerControlLine.set_value(s);
174 powerControlLine.release();
175
176 pgoodTimeoutTime = std::chrono::steady_clock::now() + timeout;
177 inStateTransition = true;
Jim Wright22318a32021-08-27 15:56:09 -0500178 state = s;
179 emitPropertyChangedSignal("state");
180}
181
Jim Wright7a5dd992021-08-31 16:56:52 -0500182void PowerControl::setUpGpio()
183{
184 pgoodLine = gpiod::find_line(pgoodLineName);
185 if (!pgoodLine)
186 {
Jim Wright209690b2021-11-11 14:46:42 -0600187 std::string errorString{"GPIO line name not found: " + pgoodLineName};
Jim Wright7a5dd992021-08-31 16:56:52 -0500188 log<level::ERR>(errorString.c_str());
189 report<
190 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure>();
191 throw std::runtime_error(errorString);
192 }
193 powerControlLine = gpiod::find_line(powerControlLineName);
194 if (!powerControlLine)
195 {
Jim Wright209690b2021-11-11 14:46:42 -0600196 std::string errorString{"GPIO line name not found: " +
197 powerControlLineName};
Jim Wright7a5dd992021-08-31 16:56:52 -0500198 log<level::ERR>(errorString.c_str());
199 report<
200 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure>();
201 throw std::runtime_error(errorString);
202 }
203
204 pgoodLine.request(
205 {"phosphor-power-control", gpiod::line_request::DIRECTION_INPUT, 0});
206 int pgoodState = pgoodLine.get_value();
207 pgood = pgoodState;
208 state = pgoodState;
209 log<level::INFO>(fmt::format("Pgood state: {}", pgoodState).c_str());
210}
211
Jim Wright539b6082021-08-02 14:50:23 -0500212} // namespace phosphor::power::sequencer