blob: 838078e7354bcd8aa74b7896b261ceec37fbee80 [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 Wright22318a32021-08-27 15:56:09 -050022#include <fmt/format.h>
23
Jim Wright7a5dd992021-08-31 16:56:52 -050024#include <phosphor-logging/elog-errors.hpp>
25#include <phosphor-logging/elog.hpp>
Jim Wright539b6082021-08-02 14:50:23 -050026#include <phosphor-logging/log.hpp>
Jim Wright7a5dd992021-08-31 16:56:52 -050027#include <xyz/openbmc_project/Common/error.hpp>
Jim Wright539b6082021-08-02 14:50:23 -050028
Jim Wright539b6082021-08-02 14:50:23 -050029#include <exception>
Jim Wright7a5dd992021-08-31 16:56:52 -050030#include <string>
Jim Wright539b6082021-08-02 14:50:23 -050031
32using namespace phosphor::logging;
33
34namespace phosphor::power::sequencer
Jim Wright10eb00f2021-07-21 12:10:38 -050035{
Jim Wright539b6082021-08-02 14:50:23 -050036
Jim Wright2d99bf72021-11-19 11:18:12 -060037const std::string interfaceName = "xyz.openbmc_project.Configuration.UCD90320";
38const std::string addressPropertyName = "Address";
39const std::string busPropertyName = "Bus";
40const std::string namePropertyName = "Name";
Jim Wright7a5dd992021-08-31 16:56:52 -050041
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},
Jim Wright930458c2022-01-24 14:37:27 -060045 bus{bus}, device{std::make_unique<PowerSequencerMonitor>(bus)},
46 match{bus,
47 sdbusplus::bus::match::rules::interfacesAdded() +
48 sdbusplus::bus::match::rules::sender(
49 "xyz.openbmc_project.EntityManager"),
50 std::bind(&PowerControl::interfacesAddedHandler, this,
51 std::placeholders::_1)},
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
Jim Wright2d99bf72021-11-19 11:18:12 -060057 setUpDevice();
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 Wright2d99bf72021-11-19 11:18:12 -060061void PowerControl::getDeviceProperties(util::DbusPropertyMap& properties)
62{
63 uint64_t* i2cBus = nullptr;
64 uint64_t* i2cAddress = nullptr;
65 std::string* name = nullptr;
66
67 for (const auto& property : properties)
68 {
69 try
70 {
71 if (property.first == busPropertyName)
72 {
73 i2cBus = std::get_if<uint64_t>(&properties[busPropertyName]);
74 }
75 else if (property.first == addressPropertyName)
76 {
77 i2cAddress =
78 std::get_if<uint64_t>(&properties[addressPropertyName]);
79 }
80 else if (property.first == namePropertyName)
81 {
82 name = std::get_if<std::string>(&properties[namePropertyName]);
83 }
84 }
85 catch (std::exception& e)
86 {}
87 }
88
89 if (i2cBus && i2cAddress && name && !name->empty())
90 {
Jim Wright7945dd22021-04-06 16:55:15 -050091 log<level::DEBUG>(
Jim Wright2d99bf72021-11-19 11:18:12 -060092 fmt::format(
93 "Found power sequencer device properties, name: {}, bus: {} addr: {:#02x} ",
94 *name, *i2cBus, *i2cAddress)
95 .c_str());
96 // Create device object
Jim Wright7945dd22021-04-06 16:55:15 -050097 device = std::make_unique<UCD90320Monitor>(bus, *i2cBus, *i2cAddress);
Jim Wrightccea2d22021-12-10 14:10:46 -060098 deviceFound = true;
Jim Wright2d99bf72021-11-19 11:18:12 -060099 }
100}
101
Jim Wright22318a32021-08-27 15:56:09 -0500102int PowerControl::getPgood() const
103{
104 return pgood;
105}
106
107int PowerControl::getPgoodTimeout() const
108{
109 return timeout.count();
110}
111
112int PowerControl::getState() const
113{
114 return state;
115}
116
Jim Wright2d99bf72021-11-19 11:18:12 -0600117void PowerControl::interfacesAddedHandler(sdbusplus::message::message& msg)
118{
Jim Wrightccea2d22021-12-10 14:10:46 -0600119 // Only continue if message is valid and device has not already been found
120 if (!msg || deviceFound)
Jim Wright2d99bf72021-11-19 11:18:12 -0600121 {
122 return;
123 }
124
125 try
126 {
127 // Read the dbus message
128 sdbusplus::message::object_path objPath;
129 std::map<std::string, std::map<std::string, util::DbusVariant>>
130 interfaces;
131 msg.read(objPath, interfaces);
132
133 // Find the device interface, if present
134 auto itIntf = interfaces.find(interfaceName);
135 if (itIntf != interfaces.cend())
136 {
137 log<level::INFO>(
138 fmt::format("InterfacesAdded for: {}", interfaceName).c_str());
139 getDeviceProperties(itIntf->second);
140 }
141 }
142 catch (const std::exception&)
143 {
144 // Error trying to read interfacesAdded message.
145 }
146}
147
Jim Wright539b6082021-08-02 14:50:23 -0500148void PowerControl::pollPgood()
Jim Wright7a5dd992021-08-31 16:56:52 -0500149{
150 if (inStateTransition)
151 {
Jim Wright9d7d95c2021-11-16 11:32:23 -0600152 // In transition between power on and off, check for timeout
Jim Wright7a5dd992021-08-31 16:56:52 -0500153 const auto now = std::chrono::steady_clock::now();
154 if (now > pgoodTimeoutTime)
155 {
156 log<level::ERR>("ERROR PowerControl: Pgood poll timeout");
157 inStateTransition = false;
Jim Wright4e25df52021-11-17 09:38:00 -0600158
Jim Wright930458c2022-01-24 14:37:27 -0600159 if (state)
Jim Wright4e25df52021-11-17 09:38:00 -0600160 {
Jim Wright930458c2022-01-24 14:37:27 -0600161 // Time out powering on
162 device->onFailure(true, powerSupplyError);
Jim Wright4e25df52021-11-17 09:38:00 -0600163 }
Jim Wright930458c2022-01-24 14:37:27 -0600164 else
Jim Wright4e25df52021-11-17 09:38:00 -0600165 {
Jim Wright930458c2022-01-24 14:37:27 -0600166 // Time out powering off
167 std::map<std::string, std::string> additionalData{};
168 device->logError(
169 "xyz.openbmc_project.Power.Error.PowerOffTimeout",
170 additionalData);
Jim Wright4e25df52021-11-17 09:38:00 -0600171 }
172
Jim Wright7a5dd992021-08-31 16:56:52 -0500173 return;
174 }
175 }
176
177 int pgoodState = pgoodLine.get_value();
178 if (pgoodState != pgood)
179 {
Jim Wright9d7d95c2021-11-16 11:32:23 -0600180 // Power good has changed since last read
Jim Wright7a5dd992021-08-31 16:56:52 -0500181 pgood = pgoodState;
182 if (pgoodState == 0)
183 {
184 emitPowerLostSignal();
185 }
186 else
187 {
188 emitPowerGoodSignal();
Jim Wrightabd64b92022-02-11 09:56:56 -0600189 // Clear any power supply error on the transition to power on
190 powerSupplyError.clear();
Jim Wright7a5dd992021-08-31 16:56:52 -0500191 }
192 emitPropertyChangedSignal("pgood");
193 }
194 if (pgoodState == state)
195 {
Jim Wright9d7d95c2021-11-16 11:32:23 -0600196 // Power good matches requested state
Jim Wright7a5dd992021-08-31 16:56:52 -0500197 inStateTransition = false;
198 }
Jim Wright9d7d95c2021-11-16 11:32:23 -0600199 else if (!inStateTransition && (pgoodState == 0))
200 {
201 // Not in power off state, not changing state, and power good is off
Jim Wright930458c2022-01-24 14:37:27 -0600202 device->onFailure(false, powerSupplyError);
Jim Wright9d7d95c2021-11-16 11:32:23 -0600203 // Power good has failed, call for chassis hard power off
204 log<level::ERR>("Chassis pgood failure");
205
206 auto method =
207 bus.new_method_call(util::SYSTEMD_SERVICE, util::SYSTEMD_ROOT,
208 util::SYSTEMD_INTERFACE, "StartUnit");
209 method.append(util::POWEROFF_TARGET);
210 method.append("replace");
211 bus.call_noreply(method);
212 }
Jim Wright7a5dd992021-08-31 16:56:52 -0500213}
Jim Wright539b6082021-08-02 14:50:23 -0500214
Jim Wright22318a32021-08-27 15:56:09 -0500215void PowerControl::setPgoodTimeout(int t)
216{
217 if (timeout.count() != t)
218 {
219 timeout = std::chrono::seconds(t);
220 emitPropertyChangedSignal("pgood_timeout");
221 }
222}
223
Jim Wrightccea2d22021-12-10 14:10:46 -0600224void PowerControl::setPowerSupplyError(const std::string& error)
225{
226 powerSupplyError = error;
227}
228
Jim Wright22318a32021-08-27 15:56:09 -0500229void PowerControl::setState(int s)
230{
231 if (state == s)
232 {
233 log<level::INFO>(
234 fmt::format("Power already at requested state: {}", state).c_str());
235 return;
236 }
Jim Wright209690b2021-11-11 14:46:42 -0600237 if (s == 0)
238 {
239 // Wait for two seconds when powering down. This is to allow host and
240 // other BMC applications time to complete power off processing
241 std::this_thread::sleep_for(std::chrono::seconds(2));
242 }
Jim Wright22318a32021-08-27 15:56:09 -0500243
244 log<level::INFO>(fmt::format("setState: {}", s).c_str());
Jim Wright7a5dd992021-08-31 16:56:52 -0500245 powerControlLine.request(
246 {"phosphor-power-control", gpiod::line_request::DIRECTION_OUTPUT, 0});
247 powerControlLine.set_value(s);
248 powerControlLine.release();
249
250 pgoodTimeoutTime = std::chrono::steady_clock::now() + timeout;
251 inStateTransition = true;
Jim Wright22318a32021-08-27 15:56:09 -0500252 state = s;
253 emitPropertyChangedSignal("state");
254}
255
Jim Wright2d99bf72021-11-19 11:18:12 -0600256void PowerControl::setUpDevice()
257{
258 try
259 {
260 auto objects = util::getSubTree(bus, "/", interfaceName, 0);
261
262 // Search for matching interface in returned objects
263 for (const auto& [path, services] : objects)
264 {
265 auto service = services.begin()->first;
266
267 if (path.empty() || service.empty())
268 {
269 continue;
270 }
271
272 // Get the properties for the device interface
273 auto properties =
274 util::getAllProperties(bus, path, interfaceName, service);
275
276 getDeviceProperties(properties);
277 }
278 }
279 catch (const std::exception& e)
280 {
281 // Interface or property not found. Let the Interfaces Added callback
282 // process the information once the interfaces are added to D-Bus.
283 }
284}
285
Jim Wright7a5dd992021-08-31 16:56:52 -0500286void PowerControl::setUpGpio()
287{
Jim Wright2d99bf72021-11-19 11:18:12 -0600288 const std::string powerControlLineName = "power-chassis-control";
289 const std::string pgoodLineName = "power-chassis-good";
290
Jim Wright7a5dd992021-08-31 16:56:52 -0500291 pgoodLine = gpiod::find_line(pgoodLineName);
292 if (!pgoodLine)
293 {
Jim Wright209690b2021-11-11 14:46:42 -0600294 std::string errorString{"GPIO line name not found: " + pgoodLineName};
Jim Wright7a5dd992021-08-31 16:56:52 -0500295 log<level::ERR>(errorString.c_str());
296 report<
297 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure>();
298 throw std::runtime_error(errorString);
299 }
300 powerControlLine = gpiod::find_line(powerControlLineName);
301 if (!powerControlLine)
302 {
Jim Wright209690b2021-11-11 14:46:42 -0600303 std::string errorString{"GPIO line name not found: " +
304 powerControlLineName};
Jim Wright7a5dd992021-08-31 16:56:52 -0500305 log<level::ERR>(errorString.c_str());
306 report<
307 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure>();
308 throw std::runtime_error(errorString);
309 }
310
311 pgoodLine.request(
312 {"phosphor-power-control", gpiod::line_request::DIRECTION_INPUT, 0});
313 int pgoodState = pgoodLine.get_value();
314 pgood = pgoodState;
315 state = pgoodState;
316 log<level::INFO>(fmt::format("Pgood state: {}", pgoodState).c_str());
317}
318
Jim Wright539b6082021-08-02 14:50:23 -0500319} // namespace phosphor::power::sequencer