blob: aa6a8a3d06e3be30e10e4c9d7388eec7bd4690fe [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();
189 }
190 emitPropertyChangedSignal("pgood");
191 }
192 if (pgoodState == state)
193 {
Jim Wright9d7d95c2021-11-16 11:32:23 -0600194 // Power good matches requested state
Jim Wright7a5dd992021-08-31 16:56:52 -0500195 inStateTransition = false;
196 }
Jim Wright9d7d95c2021-11-16 11:32:23 -0600197 else if (!inStateTransition && (pgoodState == 0))
198 {
199 // Not in power off state, not changing state, and power good is off
Jim Wright930458c2022-01-24 14:37:27 -0600200 device->onFailure(false, powerSupplyError);
Jim Wright9d7d95c2021-11-16 11:32:23 -0600201 // Power good has failed, call for chassis hard power off
202 log<level::ERR>("Chassis pgood failure");
203
204 auto method =
205 bus.new_method_call(util::SYSTEMD_SERVICE, util::SYSTEMD_ROOT,
206 util::SYSTEMD_INTERFACE, "StartUnit");
207 method.append(util::POWEROFF_TARGET);
208 method.append("replace");
209 bus.call_noreply(method);
210 }
Jim Wright7a5dd992021-08-31 16:56:52 -0500211}
Jim Wright539b6082021-08-02 14:50:23 -0500212
Jim Wright22318a32021-08-27 15:56:09 -0500213void PowerControl::setPgoodTimeout(int t)
214{
215 if (timeout.count() != t)
216 {
217 timeout = std::chrono::seconds(t);
218 emitPropertyChangedSignal("pgood_timeout");
219 }
220}
221
Jim Wrightccea2d22021-12-10 14:10:46 -0600222void PowerControl::setPowerSupplyError(const std::string& error)
223{
224 powerSupplyError = error;
225}
226
Jim Wright22318a32021-08-27 15:56:09 -0500227void PowerControl::setState(int s)
228{
229 if (state == s)
230 {
231 log<level::INFO>(
232 fmt::format("Power already at requested state: {}", state).c_str());
233 return;
234 }
Jim Wright209690b2021-11-11 14:46:42 -0600235 if (s == 0)
236 {
237 // Wait for two seconds when powering down. This is to allow host and
238 // other BMC applications time to complete power off processing
239 std::this_thread::sleep_for(std::chrono::seconds(2));
240 }
Jim Wright22318a32021-08-27 15:56:09 -0500241
242 log<level::INFO>(fmt::format("setState: {}", s).c_str());
Jim Wright7a5dd992021-08-31 16:56:52 -0500243 powerControlLine.request(
244 {"phosphor-power-control", gpiod::line_request::DIRECTION_OUTPUT, 0});
245 powerControlLine.set_value(s);
246 powerControlLine.release();
247
248 pgoodTimeoutTime = std::chrono::steady_clock::now() + timeout;
249 inStateTransition = true;
Jim Wright22318a32021-08-27 15:56:09 -0500250 state = s;
251 emitPropertyChangedSignal("state");
252}
253
Jim Wright2d99bf72021-11-19 11:18:12 -0600254void PowerControl::setUpDevice()
255{
256 try
257 {
258 auto objects = util::getSubTree(bus, "/", interfaceName, 0);
259
260 // Search for matching interface in returned objects
261 for (const auto& [path, services] : objects)
262 {
263 auto service = services.begin()->first;
264
265 if (path.empty() || service.empty())
266 {
267 continue;
268 }
269
270 // Get the properties for the device interface
271 auto properties =
272 util::getAllProperties(bus, path, interfaceName, service);
273
274 getDeviceProperties(properties);
275 }
276 }
277 catch (const std::exception& e)
278 {
279 // Interface or property not found. Let the Interfaces Added callback
280 // process the information once the interfaces are added to D-Bus.
281 }
282}
283
Jim Wright7a5dd992021-08-31 16:56:52 -0500284void PowerControl::setUpGpio()
285{
Jim Wright2d99bf72021-11-19 11:18:12 -0600286 const std::string powerControlLineName = "power-chassis-control";
287 const std::string pgoodLineName = "power-chassis-good";
288
Jim Wright7a5dd992021-08-31 16:56:52 -0500289 pgoodLine = gpiod::find_line(pgoodLineName);
290 if (!pgoodLine)
291 {
Jim Wright209690b2021-11-11 14:46:42 -0600292 std::string errorString{"GPIO line name not found: " + pgoodLineName};
Jim Wright7a5dd992021-08-31 16:56:52 -0500293 log<level::ERR>(errorString.c_str());
294 report<
295 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure>();
296 throw std::runtime_error(errorString);
297 }
298 powerControlLine = gpiod::find_line(powerControlLineName);
299 if (!powerControlLine)
300 {
Jim Wright209690b2021-11-11 14:46:42 -0600301 std::string errorString{"GPIO line name not found: " +
302 powerControlLineName};
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
309 pgoodLine.request(
310 {"phosphor-power-control", gpiod::line_request::DIRECTION_INPUT, 0});
311 int pgoodState = pgoodLine.get_value();
312 pgood = pgoodState;
313 state = pgoodState;
314 log<level::INFO>(fmt::format("Pgood state: {}", pgoodState).c_str());
315}
316
Jim Wright539b6082021-08-02 14:50:23 -0500317} // namespace phosphor::power::sequencer