blob: 6e9fa58521e031488fa804f13f3fe1b2aeeea9a6 [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"
20
Jim Wright22318a32021-08-27 15:56:09 -050021#include <fmt/format.h>
Jim Wright4e25df52021-11-17 09:38:00 -060022#include <sys/types.h>
23#include <unistd.h>
Jim Wright22318a32021-08-27 15:56:09 -050024
Jim Wright7a5dd992021-08-31 16:56:52 -050025#include <phosphor-logging/elog-errors.hpp>
26#include <phosphor-logging/elog.hpp>
Jim Wright539b6082021-08-02 14:50:23 -050027#include <phosphor-logging/log.hpp>
Jim Wright7a5dd992021-08-31 16:56:52 -050028#include <xyz/openbmc_project/Common/error.hpp>
Jim Wright539b6082021-08-02 14:50:23 -050029
Jim Wright539b6082021-08-02 14:50:23 -050030#include <exception>
Jim Wright7a5dd992021-08-31 16:56:52 -050031#include <string>
Jim Wright539b6082021-08-02 14:50:23 -050032
33using namespace phosphor::logging;
34
35namespace phosphor::power::sequencer
Jim Wright10eb00f2021-07-21 12:10:38 -050036{
Jim Wright539b6082021-08-02 14:50:23 -050037
Jim Wright2d99bf72021-11-19 11:18:12 -060038const std::string interfaceName = "xyz.openbmc_project.Configuration.UCD90320";
39const std::string addressPropertyName = "Address";
40const std::string busPropertyName = "Bus";
41const std::string namePropertyName = "Name";
Jim Wright7a5dd992021-08-31 16:56:52 -050042
Jim Wright539b6082021-08-02 14:50:23 -050043PowerControl::PowerControl(sdbusplus::bus::bus& bus,
44 const sdeventplus::Event& event) :
Jim Wright22318a32021-08-27 15:56:09 -050045 PowerObject{bus, POWER_OBJ_PATH, true},
46 bus{bus}, timer{event, std::bind(&PowerControl::pollPgood, this),
47 pollInterval}
Jim Wright539b6082021-08-02 14:50:23 -050048{
49 // Obtain dbus service name
50 bus.request_name(POWER_IFACE);
Jim Wright2d99bf72021-11-19 11:18:12 -060051
52 // Subscribe to D-Bus interfacesAdded signal from Entity Manager. This
53 // notifies us if the interface becomes available later.
54 match = std::make_unique<sdbusplus::bus::match_t>(
55 bus,
56 sdbusplus::bus::match::rules::interfacesAdded() +
57 sdbusplus::bus::match::rules::sender(
58 "xyz.openbmc_project.EntityManager"),
59 std::bind(&PowerControl::interfacesAddedHandler, this,
60 std::placeholders::_1));
61 setUpDevice();
Jim Wright7a5dd992021-08-31 16:56:52 -050062 setUpGpio();
Jim Wright10eb00f2021-07-21 12:10:38 -050063}
Jim Wright539b6082021-08-02 14:50:23 -050064
Jim Wright2d99bf72021-11-19 11:18:12 -060065void PowerControl::getDeviceProperties(util::DbusPropertyMap& properties)
66{
67 uint64_t* i2cBus = nullptr;
68 uint64_t* i2cAddress = nullptr;
69 std::string* name = nullptr;
70
71 for (const auto& property : properties)
72 {
73 try
74 {
75 if (property.first == busPropertyName)
76 {
77 i2cBus = std::get_if<uint64_t>(&properties[busPropertyName]);
78 }
79 else if (property.first == addressPropertyName)
80 {
81 i2cAddress =
82 std::get_if<uint64_t>(&properties[addressPropertyName]);
83 }
84 else if (property.first == namePropertyName)
85 {
86 name = std::get_if<std::string>(&properties[namePropertyName]);
87 }
88 }
89 catch (std::exception& e)
90 {}
91 }
92
93 if (i2cBus && i2cAddress && name && !name->empty())
94 {
95 log<level::INFO>(
96 fmt::format(
97 "Found power sequencer device properties, name: {}, bus: {} addr: {:#02x} ",
98 *name, *i2cBus, *i2cAddress)
99 .c_str());
100 // Create device object
101 }
102}
103
Jim Wright22318a32021-08-27 15:56:09 -0500104int PowerControl::getPgood() const
105{
106 return pgood;
107}
108
109int PowerControl::getPgoodTimeout() const
110{
111 return timeout.count();
112}
113
114int PowerControl::getState() const
115{
116 return state;
117}
118
Jim Wright2d99bf72021-11-19 11:18:12 -0600119void PowerControl::interfacesAddedHandler(sdbusplus::message::message& msg)
120{
121 // Verify message is valid
122 if (!msg)
123 {
124 return;
125 }
126
127 try
128 {
129 // Read the dbus message
130 sdbusplus::message::object_path objPath;
131 std::map<std::string, std::map<std::string, util::DbusVariant>>
132 interfaces;
133 msg.read(objPath, interfaces);
134
135 // Find the device interface, if present
136 auto itIntf = interfaces.find(interfaceName);
137 if (itIntf != interfaces.cend())
138 {
139 log<level::INFO>(
140 fmt::format("InterfacesAdded for: {}", interfaceName).c_str());
141 getDeviceProperties(itIntf->second);
142 }
143 }
144 catch (const std::exception&)
145 {
146 // Error trying to read interfacesAdded message.
147 }
148}
149
Jim Wright539b6082021-08-02 14:50:23 -0500150void PowerControl::pollPgood()
Jim Wright7a5dd992021-08-31 16:56:52 -0500151{
152 if (inStateTransition)
153 {
Jim Wright9d7d95c2021-11-16 11:32:23 -0600154 // In transition between power on and off, check for timeout
Jim Wright7a5dd992021-08-31 16:56:52 -0500155 const auto now = std::chrono::steady_clock::now();
156 if (now > pgoodTimeoutTime)
157 {
158 log<level::ERR>("ERROR PowerControl: Pgood poll timeout");
159 inStateTransition = false;
Jim Wright4e25df52021-11-17 09:38:00 -0600160
161 try
162 {
163 auto method = bus.new_method_call(
164 "xyz.openbmc_project.Logging",
165 "/xyz/openbmc_project/logging",
166 "xyz.openbmc_project.Logging.Create", "Create");
167
168 std::map<std::string, std::string> additionalData;
169 // Add PID to AdditionalData
170 additionalData.emplace("_PID", std::to_string(getpid()));
171
172 method.append(
173 state ? "xyz.openbmc_project.Power.Error.PowerOnTimeout"
174 : "xyz.openbmc_project.Power.Error.PowerOffTimeout",
175 sdbusplus::xyz::openbmc_project::Logging::server::Entry::
176 Level::Critical,
177 additionalData);
178 bus.call_noreply(method);
179 }
180 catch (const std::exception& e)
181 {
182 log<level::ERR>(
183 fmt::format(
184 "Unable to log timeout error, state: {}, error {}",
185 state, e.what())
186 .c_str());
187 }
188
Jim Wright7a5dd992021-08-31 16:56:52 -0500189 return;
190 }
191 }
192
193 int pgoodState = pgoodLine.get_value();
194 if (pgoodState != pgood)
195 {
Jim Wright9d7d95c2021-11-16 11:32:23 -0600196 // Power good has changed since last read
Jim Wright7a5dd992021-08-31 16:56:52 -0500197 pgood = pgoodState;
198 if (pgoodState == 0)
199 {
200 emitPowerLostSignal();
201 }
202 else
203 {
204 emitPowerGoodSignal();
205 }
206 emitPropertyChangedSignal("pgood");
207 }
208 if (pgoodState == state)
209 {
Jim Wright9d7d95c2021-11-16 11:32:23 -0600210 // Power good matches requested state
Jim Wright7a5dd992021-08-31 16:56:52 -0500211 inStateTransition = false;
212 }
Jim Wright9d7d95c2021-11-16 11:32:23 -0600213 else if (!inStateTransition && (pgoodState == 0))
214 {
215 // Not in power off state, not changing state, and power good is off
216 // Power good has failed, call for chassis hard power off
217 log<level::ERR>("Chassis pgood failure");
218
219 auto method =
220 bus.new_method_call(util::SYSTEMD_SERVICE, util::SYSTEMD_ROOT,
221 util::SYSTEMD_INTERFACE, "StartUnit");
222 method.append(util::POWEROFF_TARGET);
223 method.append("replace");
224 bus.call_noreply(method);
225 }
Jim Wright7a5dd992021-08-31 16:56:52 -0500226}
Jim Wright539b6082021-08-02 14:50:23 -0500227
Jim Wright22318a32021-08-27 15:56:09 -0500228void PowerControl::setPgoodTimeout(int t)
229{
230 if (timeout.count() != t)
231 {
232 timeout = std::chrono::seconds(t);
233 emitPropertyChangedSignal("pgood_timeout");
234 }
235}
236
237void PowerControl::setState(int s)
238{
239 if (state == s)
240 {
241 log<level::INFO>(
242 fmt::format("Power already at requested state: {}", state).c_str());
243 return;
244 }
Jim Wright209690b2021-11-11 14:46:42 -0600245 if (s == 0)
246 {
247 // Wait for two seconds when powering down. This is to allow host and
248 // other BMC applications time to complete power off processing
249 std::this_thread::sleep_for(std::chrono::seconds(2));
250 }
Jim Wright22318a32021-08-27 15:56:09 -0500251
252 log<level::INFO>(fmt::format("setState: {}", s).c_str());
Jim Wright7a5dd992021-08-31 16:56:52 -0500253 powerControlLine.request(
254 {"phosphor-power-control", gpiod::line_request::DIRECTION_OUTPUT, 0});
255 powerControlLine.set_value(s);
256 powerControlLine.release();
257
258 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
Jim Wright2d99bf72021-11-19 11:18:12 -0600264void PowerControl::setUpDevice()
265{
266 try
267 {
268 auto objects = util::getSubTree(bus, "/", interfaceName, 0);
269
270 // Search for matching interface in returned objects
271 for (const auto& [path, services] : objects)
272 {
273 auto service = services.begin()->first;
274
275 if (path.empty() || service.empty())
276 {
277 continue;
278 }
279
280 // Get the properties for the device interface
281 auto properties =
282 util::getAllProperties(bus, path, interfaceName, service);
283
284 getDeviceProperties(properties);
285 }
286 }
287 catch (const std::exception& e)
288 {
289 // Interface or property not found. Let the Interfaces Added callback
290 // process the information once the interfaces are added to D-Bus.
291 }
292}
293
Jim Wright7a5dd992021-08-31 16:56:52 -0500294void PowerControl::setUpGpio()
295{
Jim Wright2d99bf72021-11-19 11:18:12 -0600296 const std::string powerControlLineName = "power-chassis-control";
297 const std::string pgoodLineName = "power-chassis-good";
298
Jim Wright7a5dd992021-08-31 16:56:52 -0500299 pgoodLine = gpiod::find_line(pgoodLineName);
300 if (!pgoodLine)
301 {
Jim Wright209690b2021-11-11 14:46:42 -0600302 std::string errorString{"GPIO line name not found: " + pgoodLineName};
Jim Wright7a5dd992021-08-31 16:56:52 -0500303 log<level::ERR>(errorString.c_str());
304 report<
305 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure>();
306 throw std::runtime_error(errorString);
307 }
308 powerControlLine = gpiod::find_line(powerControlLineName);
309 if (!powerControlLine)
310 {
Jim Wright209690b2021-11-11 14:46:42 -0600311 std::string errorString{"GPIO line name not found: " +
312 powerControlLineName};
Jim Wright7a5dd992021-08-31 16:56:52 -0500313 log<level::ERR>(errorString.c_str());
314 report<
315 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure>();
316 throw std::runtime_error(errorString);
317 }
318
319 pgoodLine.request(
320 {"phosphor-power-control", gpiod::line_request::DIRECTION_INPUT, 0});
321 int pgoodState = pgoodLine.get_value();
322 pgood = pgoodState;
323 state = pgoodState;
324 log<level::INFO>(fmt::format("Pgood state: {}", pgoodState).c_str());
325}
326
Jim Wright539b6082021-08-02 14:50:23 -0500327} // namespace phosphor::power::sequencer