blob: 91021a4c71a73f9b0a5a46bd87da5d7da08b17a1 [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 Wright7945dd22021-04-06 16:55:15 -050020#include "ucd90320_monitor.hpp"
Jim Wright539b6082021-08-02 14:50:23 -050021
Jim Wrightb4ad95d2022-03-08 17:24:01 -060022#include <fmt/chrono.h>
Jim Wright22318a32021-08-27 15:56:09 -050023#include <fmt/format.h>
24
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) :
Patrick Williams3fa31a72022-04-05 21:22:58 -050045 PowerObject{bus, POWER_OBJ_PATH, PowerObject::action::defer_emit},
Jim Wright930458c2022-01-24 14:37:27 -060046 bus{bus}, device{std::make_unique<PowerSequencerMonitor>(bus)},
47 match{bus,
48 sdbusplus::bus::match::rules::interfacesAdded() +
49 sdbusplus::bus::match::rules::sender(
50 "xyz.openbmc_project.EntityManager"),
51 std::bind(&PowerControl::interfacesAddedHandler, this,
52 std::placeholders::_1)},
Jim Wrightb4ad95d2022-03-08 17:24:01 -060053 powerOnAllowedTime{std::chrono::steady_clock::now() + minimumColdStartTime},
Jim Wrightccea2d22021-12-10 14:10:46 -060054 timer{event, std::bind(&PowerControl::pollPgood, this), pollInterval}
Jim Wright539b6082021-08-02 14:50:23 -050055{
56 // Obtain dbus service name
57 bus.request_name(POWER_IFACE);
Jim Wright2d99bf72021-11-19 11:18:12 -060058
Jim Wright2d99bf72021-11-19 11:18:12 -060059 setUpDevice();
Jim Wright7a5dd992021-08-31 16:56:52 -050060 setUpGpio();
Jim Wright10eb00f2021-07-21 12:10:38 -050061}
Jim Wright539b6082021-08-02 14:50:23 -050062
Jim Wright2d99bf72021-11-19 11:18:12 -060063void PowerControl::getDeviceProperties(util::DbusPropertyMap& properties)
64{
65 uint64_t* i2cBus = nullptr;
66 uint64_t* i2cAddress = nullptr;
67 std::string* name = nullptr;
68
69 for (const auto& property : properties)
70 {
71 try
72 {
73 if (property.first == busPropertyName)
74 {
75 i2cBus = std::get_if<uint64_t>(&properties[busPropertyName]);
76 }
77 else if (property.first == addressPropertyName)
78 {
79 i2cAddress =
80 std::get_if<uint64_t>(&properties[addressPropertyName]);
81 }
82 else if (property.first == namePropertyName)
83 {
84 name = std::get_if<std::string>(&properties[namePropertyName]);
85 }
86 }
Jim Wrightc91eed02022-07-22 11:27:06 -050087 catch (const std::exception&)
Jim Wright2d99bf72021-11-19 11:18:12 -060088 {}
89 }
90
91 if (i2cBus && i2cAddress && name && !name->empty())
92 {
Jim Wright7945dd22021-04-06 16:55:15 -050093 log<level::DEBUG>(
Jim Wright2d99bf72021-11-19 11:18:12 -060094 fmt::format(
95 "Found power sequencer device properties, name: {}, bus: {} addr: {:#02x} ",
96 *name, *i2cBus, *i2cAddress)
97 .c_str());
98 // Create device object
Jim Wright7945dd22021-04-06 16:55:15 -050099 device = std::make_unique<UCD90320Monitor>(bus, *i2cBus, *i2cAddress);
Jim Wrightccea2d22021-12-10 14:10:46 -0600100 deviceFound = true;
Jim Wright2d99bf72021-11-19 11:18:12 -0600101 }
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{
Jim Wrightccea2d22021-12-10 14:10:46 -0600121 // Only continue if message is valid and device has not already been found
122 if (!msg || deviceFound)
Jim Wright2d99bf72021-11-19 11:18:12 -0600123 {
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 {
Jim Wright213ffe92022-06-03 08:54:30 -0500158 log<level::ERR>(
159 fmt::format("Power state transition timeout, state: {}", state)
160 .c_str());
Jim Wright7a5dd992021-08-31 16:56:52 -0500161 inStateTransition = false;
Jim Wright4e25df52021-11-17 09:38:00 -0600162
Jim Wright930458c2022-01-24 14:37:27 -0600163 if (state)
Jim Wright4e25df52021-11-17 09:38:00 -0600164 {
Jim Wright930458c2022-01-24 14:37:27 -0600165 // Time out powering on
166 device->onFailure(true, powerSupplyError);
Jim Wright4e25df52021-11-17 09:38:00 -0600167 }
Jim Wright930458c2022-01-24 14:37:27 -0600168 else
Jim Wright4e25df52021-11-17 09:38:00 -0600169 {
Jim Wright930458c2022-01-24 14:37:27 -0600170 // Time out powering off
171 std::map<std::string, std::string> additionalData{};
172 device->logError(
173 "xyz.openbmc_project.Power.Error.PowerOffTimeout",
174 additionalData);
Jim Wright4e25df52021-11-17 09:38:00 -0600175 }
176
Jim Wright48752622022-02-28 20:37:53 -0600177 failureFound = true;
Jim Wright7a5dd992021-08-31 16:56:52 -0500178 return;
179 }
180 }
181
182 int pgoodState = pgoodLine.get_value();
183 if (pgoodState != pgood)
184 {
Jim Wright9d7d95c2021-11-16 11:32:23 -0600185 // Power good has changed since last read
Jim Wright7a5dd992021-08-31 16:56:52 -0500186 pgood = pgoodState;
187 if (pgoodState == 0)
188 {
189 emitPowerLostSignal();
190 }
191 else
192 {
193 emitPowerGoodSignal();
Jim Wright48752622022-02-28 20:37:53 -0600194 // Clear any errors on the transition to power on
Jim Wrightabd64b92022-02-11 09:56:56 -0600195 powerSupplyError.clear();
Jim Wright48752622022-02-28 20:37:53 -0600196 failureFound = false;
Jim Wright7a5dd992021-08-31 16:56:52 -0500197 }
198 emitPropertyChangedSignal("pgood");
199 }
200 if (pgoodState == state)
201 {
Jim Wright9d7d95c2021-11-16 11:32:23 -0600202 // Power good matches requested state
Jim Wright7a5dd992021-08-31 16:56:52 -0500203 inStateTransition = false;
204 }
Jim Wright48752622022-02-28 20:37:53 -0600205 else if (!inStateTransition && (pgoodState == 0) && !failureFound)
Jim Wright9d7d95c2021-11-16 11:32:23 -0600206 {
207 // Not in power off state, not changing state, and power good is off
Jim Wright213ffe92022-06-03 08:54:30 -0500208 log<level::ERR>("Chassis pgood failure");
Jim Wright930458c2022-01-24 14:37:27 -0600209 device->onFailure(false, powerSupplyError);
Jim Wright48752622022-02-28 20:37:53 -0600210 failureFound = true;
Jim Wright9d7d95c2021-11-16 11:32:23 -0600211
Jim Wright213ffe92022-06-03 08:54:30 -0500212 // Power good has failed, call for chassis hard power off
Jim Wright9d7d95c2021-11-16 11:32:23 -0600213 auto method =
214 bus.new_method_call(util::SYSTEMD_SERVICE, util::SYSTEMD_ROOT,
215 util::SYSTEMD_INTERFACE, "StartUnit");
216 method.append(util::POWEROFF_TARGET);
217 method.append("replace");
218 bus.call_noreply(method);
219 }
Jim Wright7a5dd992021-08-31 16:56:52 -0500220}
Jim Wright539b6082021-08-02 14:50:23 -0500221
Jim Wright22318a32021-08-27 15:56:09 -0500222void PowerControl::setPgoodTimeout(int t)
223{
224 if (timeout.count() != t)
225 {
226 timeout = std::chrono::seconds(t);
227 emitPropertyChangedSignal("pgood_timeout");
228 }
229}
230
Jim Wrightccea2d22021-12-10 14:10:46 -0600231void PowerControl::setPowerSupplyError(const std::string& error)
232{
233 powerSupplyError = error;
234}
235
Jim Wright22318a32021-08-27 15:56:09 -0500236void PowerControl::setState(int s)
237{
238 if (state == s)
239 {
240 log<level::INFO>(
241 fmt::format("Power already at requested state: {}", state).c_str());
242 return;
243 }
Jim Wright209690b2021-11-11 14:46:42 -0600244 if (s == 0)
245 {
246 // Wait for two seconds when powering down. This is to allow host and
247 // other BMC applications time to complete power off processing
248 std::this_thread::sleep_for(std::chrono::seconds(2));
249 }
Jim Wrightb4ad95d2022-03-08 17:24:01 -0600250 else
251 {
252 // If minimum power off time has not passed, wait
253 if (powerOnAllowedTime > std::chrono::steady_clock::now())
254 {
255 log<level::INFO>(
256 fmt::format(
257 "Waiting {} seconds until power on allowed",
258 std::chrono::duration_cast<std::chrono::seconds>(
259 powerOnAllowedTime - std::chrono::steady_clock::now())
260 .count())
261 .c_str());
262 }
263 std::this_thread::sleep_until(powerOnAllowedTime);
264 }
Jim Wright22318a32021-08-27 15:56:09 -0500265
266 log<level::INFO>(fmt::format("setState: {}", s).c_str());
Jim Wright7a5dd992021-08-31 16:56:52 -0500267 powerControlLine.request(
268 {"phosphor-power-control", gpiod::line_request::DIRECTION_OUTPUT, 0});
269 powerControlLine.set_value(s);
270 powerControlLine.release();
271
Jim Wrightb4ad95d2022-03-08 17:24:01 -0600272 if (s == 0)
273 {
274 // Set a minimum amount of time to wait before next power on
275 powerOnAllowedTime =
276 std::chrono::steady_clock::now() + minimumPowerOffTime;
277 }
278
Jim Wright7a5dd992021-08-31 16:56:52 -0500279 pgoodTimeoutTime = std::chrono::steady_clock::now() + timeout;
280 inStateTransition = true;
Jim Wright22318a32021-08-27 15:56:09 -0500281 state = s;
282 emitPropertyChangedSignal("state");
283}
284
Jim Wright2d99bf72021-11-19 11:18:12 -0600285void PowerControl::setUpDevice()
286{
287 try
288 {
289 auto objects = util::getSubTree(bus, "/", interfaceName, 0);
290
291 // Search for matching interface in returned objects
292 for (const auto& [path, services] : objects)
293 {
294 auto service = services.begin()->first;
295
296 if (path.empty() || service.empty())
297 {
298 continue;
299 }
300
301 // Get the properties for the device interface
302 auto properties =
303 util::getAllProperties(bus, path, interfaceName, service);
304
305 getDeviceProperties(properties);
306 }
307 }
Jim Wrightc91eed02022-07-22 11:27:06 -0500308 catch (const std::exception&)
Jim Wright2d99bf72021-11-19 11:18:12 -0600309 {
310 // Interface or property not found. Let the Interfaces Added callback
311 // process the information once the interfaces are added to D-Bus.
312 }
313}
314
Jim Wright7a5dd992021-08-31 16:56:52 -0500315void PowerControl::setUpGpio()
316{
Jim Wright2d99bf72021-11-19 11:18:12 -0600317 const std::string powerControlLineName = "power-chassis-control";
318 const std::string pgoodLineName = "power-chassis-good";
319
Jim Wright7a5dd992021-08-31 16:56:52 -0500320 pgoodLine = gpiod::find_line(pgoodLineName);
321 if (!pgoodLine)
322 {
Jim Wright209690b2021-11-11 14:46:42 -0600323 std::string errorString{"GPIO line name not found: " + pgoodLineName};
Jim Wright7a5dd992021-08-31 16:56:52 -0500324 log<level::ERR>(errorString.c_str());
325 report<
326 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure>();
327 throw std::runtime_error(errorString);
328 }
329 powerControlLine = gpiod::find_line(powerControlLineName);
330 if (!powerControlLine)
331 {
Jim Wright209690b2021-11-11 14:46:42 -0600332 std::string errorString{"GPIO line name not found: " +
333 powerControlLineName};
Jim Wright7a5dd992021-08-31 16:56:52 -0500334 log<level::ERR>(errorString.c_str());
335 report<
336 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure>();
337 throw std::runtime_error(errorString);
338 }
339
340 pgoodLine.request(
341 {"phosphor-power-control", gpiod::line_request::DIRECTION_INPUT, 0});
342 int pgoodState = pgoodLine.get_value();
343 pgood = pgoodState;
344 state = pgoodState;
345 log<level::INFO>(fmt::format("Pgood state: {}", pgoodState).c_str());
346}
347
Jim Wright539b6082021-08-02 14:50:23 -0500348} // namespace phosphor::power::sequencer