blob: aa47598d412384ccfafd06a0f68796163b22cd1c [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>
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 Wright2d99bf72021-11-19 11:18:12 -060039const std::string interfaceName = "xyz.openbmc_project.Configuration.UCD90320";
40const std::string addressPropertyName = "Address";
41const std::string busPropertyName = "Bus";
42const std::string namePropertyName = "Name";
Jim Wright7a5dd992021-08-31 16:56:52 -050043
Jim Wright539b6082021-08-02 14:50:23 -050044PowerControl::PowerControl(sdbusplus::bus::bus& bus,
45 const sdeventplus::Event& event) :
Jim Wright22318a32021-08-27 15:56:09 -050046 PowerObject{bus, POWER_OBJ_PATH, true},
47 bus{bus}, timer{event, std::bind(&PowerControl::pollPgood, this),
48 pollInterval}
Jim Wright539b6082021-08-02 14:50:23 -050049{
50 // Obtain dbus service name
51 bus.request_name(POWER_IFACE);
Jim Wright2d99bf72021-11-19 11:18:12 -060052
53 // Subscribe to D-Bus interfacesAdded signal from Entity Manager. This
54 // notifies us if the interface becomes available later.
55 match = std::make_unique<sdbusplus::bus::match_t>(
56 bus,
57 sdbusplus::bus::match::rules::interfacesAdded() +
58 sdbusplus::bus::match::rules::sender(
59 "xyz.openbmc_project.EntityManager"),
60 std::bind(&PowerControl::interfacesAddedHandler, this,
61 std::placeholders::_1));
62 setUpDevice();
Jim Wright7a5dd992021-08-31 16:56:52 -050063 setUpGpio();
Jim Wright10eb00f2021-07-21 12:10:38 -050064}
Jim Wright539b6082021-08-02 14:50:23 -050065
Jim Wright2d99bf72021-11-19 11:18:12 -060066void PowerControl::getDeviceProperties(util::DbusPropertyMap& properties)
67{
68 uint64_t* i2cBus = nullptr;
69 uint64_t* i2cAddress = nullptr;
70 std::string* name = nullptr;
71
72 for (const auto& property : properties)
73 {
74 try
75 {
76 if (property.first == busPropertyName)
77 {
78 i2cBus = std::get_if<uint64_t>(&properties[busPropertyName]);
79 }
80 else if (property.first == addressPropertyName)
81 {
82 i2cAddress =
83 std::get_if<uint64_t>(&properties[addressPropertyName]);
84 }
85 else if (property.first == namePropertyName)
86 {
87 name = std::get_if<std::string>(&properties[namePropertyName]);
88 }
89 }
90 catch (std::exception& e)
91 {}
92 }
93
94 if (i2cBus && i2cAddress && name && !name->empty())
95 {
Jim Wright7945dd22021-04-06 16:55:15 -050096 log<level::DEBUG>(
Jim Wright2d99bf72021-11-19 11:18:12 -060097 fmt::format(
98 "Found power sequencer device properties, name: {}, bus: {} addr: {:#02x} ",
99 *name, *i2cBus, *i2cAddress)
100 .c_str());
101 // Create device object
Jim Wright7945dd22021-04-06 16:55:15 -0500102 device = std::make_unique<UCD90320Monitor>(bus, *i2cBus, *i2cAddress);
Jim Wright2d99bf72021-11-19 11:18:12 -0600103 }
104}
105
Jim Wright22318a32021-08-27 15:56:09 -0500106int PowerControl::getPgood() const
107{
108 return pgood;
109}
110
111int PowerControl::getPgoodTimeout() const
112{
113 return timeout.count();
114}
115
116int PowerControl::getState() const
117{
118 return state;
119}
120
Jim Wright2d99bf72021-11-19 11:18:12 -0600121void PowerControl::interfacesAddedHandler(sdbusplus::message::message& msg)
122{
123 // Verify message is valid
124 if (!msg)
125 {
126 return;
127 }
128
129 try
130 {
131 // Read the dbus message
132 sdbusplus::message::object_path objPath;
133 std::map<std::string, std::map<std::string, util::DbusVariant>>
134 interfaces;
135 msg.read(objPath, interfaces);
136
137 // Find the device interface, if present
138 auto itIntf = interfaces.find(interfaceName);
139 if (itIntf != interfaces.cend())
140 {
141 log<level::INFO>(
142 fmt::format("InterfacesAdded for: {}", interfaceName).c_str());
143 getDeviceProperties(itIntf->second);
144 }
145 }
146 catch (const std::exception&)
147 {
148 // Error trying to read interfacesAdded message.
149 }
150}
151
Jim Wright539b6082021-08-02 14:50:23 -0500152void PowerControl::pollPgood()
Jim Wright7a5dd992021-08-31 16:56:52 -0500153{
154 if (inStateTransition)
155 {
Jim Wright9d7d95c2021-11-16 11:32:23 -0600156 // In transition between power on and off, check for timeout
Jim Wright7a5dd992021-08-31 16:56:52 -0500157 const auto now = std::chrono::steady_clock::now();
158 if (now > pgoodTimeoutTime)
159 {
160 log<level::ERR>("ERROR PowerControl: Pgood poll timeout");
161 inStateTransition = false;
Jim Wright4e25df52021-11-17 09:38:00 -0600162
163 try
164 {
165 auto method = bus.new_method_call(
166 "xyz.openbmc_project.Logging",
167 "/xyz/openbmc_project/logging",
168 "xyz.openbmc_project.Logging.Create", "Create");
169
170 std::map<std::string, std::string> additionalData;
171 // Add PID to AdditionalData
172 additionalData.emplace("_PID", std::to_string(getpid()));
173
174 method.append(
175 state ? "xyz.openbmc_project.Power.Error.PowerOnTimeout"
176 : "xyz.openbmc_project.Power.Error.PowerOffTimeout",
177 sdbusplus::xyz::openbmc_project::Logging::server::Entry::
178 Level::Critical,
179 additionalData);
180 bus.call_noreply(method);
181 }
182 catch (const std::exception& e)
183 {
184 log<level::ERR>(
185 fmt::format(
186 "Unable to log timeout error, state: {}, error {}",
187 state, e.what())
188 .c_str());
189 }
190
Jim Wright7a5dd992021-08-31 16:56:52 -0500191 return;
192 }
193 }
194
195 int pgoodState = pgoodLine.get_value();
196 if (pgoodState != pgood)
197 {
Jim Wright9d7d95c2021-11-16 11:32:23 -0600198 // Power good has changed since last read
Jim Wright7a5dd992021-08-31 16:56:52 -0500199 pgood = pgoodState;
200 if (pgoodState == 0)
201 {
202 emitPowerLostSignal();
203 }
204 else
205 {
206 emitPowerGoodSignal();
207 }
208 emitPropertyChangedSignal("pgood");
209 }
210 if (pgoodState == state)
211 {
Jim Wright9d7d95c2021-11-16 11:32:23 -0600212 // Power good matches requested state
Jim Wright7a5dd992021-08-31 16:56:52 -0500213 inStateTransition = false;
214 }
Jim Wright9d7d95c2021-11-16 11:32:23 -0600215 else if (!inStateTransition && (pgoodState == 0))
216 {
217 // Not in power off state, not changing state, and power good is off
218 // Power good has failed, call for chassis hard power off
219 log<level::ERR>("Chassis pgood failure");
220
221 auto method =
222 bus.new_method_call(util::SYSTEMD_SERVICE, util::SYSTEMD_ROOT,
223 util::SYSTEMD_INTERFACE, "StartUnit");
224 method.append(util::POWEROFF_TARGET);
225 method.append("replace");
226 bus.call_noreply(method);
227 }
Jim Wright7a5dd992021-08-31 16:56:52 -0500228}
Jim Wright539b6082021-08-02 14:50:23 -0500229
Jim Wright22318a32021-08-27 15:56:09 -0500230void PowerControl::setPgoodTimeout(int t)
231{
232 if (timeout.count() != t)
233 {
234 timeout = std::chrono::seconds(t);
235 emitPropertyChangedSignal("pgood_timeout");
236 }
237}
238
239void PowerControl::setState(int s)
240{
241 if (state == s)
242 {
243 log<level::INFO>(
244 fmt::format("Power already at requested state: {}", state).c_str());
245 return;
246 }
Jim Wright209690b2021-11-11 14:46:42 -0600247 if (s == 0)
248 {
249 // Wait for two seconds when powering down. This is to allow host and
250 // other BMC applications time to complete power off processing
251 std::this_thread::sleep_for(std::chrono::seconds(2));
252 }
Jim Wright22318a32021-08-27 15:56:09 -0500253
254 log<level::INFO>(fmt::format("setState: {}", s).c_str());
Jim Wright7a5dd992021-08-31 16:56:52 -0500255 powerControlLine.request(
256 {"phosphor-power-control", gpiod::line_request::DIRECTION_OUTPUT, 0});
257 powerControlLine.set_value(s);
258 powerControlLine.release();
259
260 pgoodTimeoutTime = std::chrono::steady_clock::now() + timeout;
261 inStateTransition = true;
Jim Wright22318a32021-08-27 15:56:09 -0500262 state = s;
263 emitPropertyChangedSignal("state");
264}
265
Jim Wright2d99bf72021-11-19 11:18:12 -0600266void PowerControl::setUpDevice()
267{
268 try
269 {
270 auto objects = util::getSubTree(bus, "/", interfaceName, 0);
271
272 // Search for matching interface in returned objects
273 for (const auto& [path, services] : objects)
274 {
275 auto service = services.begin()->first;
276
277 if (path.empty() || service.empty())
278 {
279 continue;
280 }
281
282 // Get the properties for the device interface
283 auto properties =
284 util::getAllProperties(bus, path, interfaceName, service);
285
286 getDeviceProperties(properties);
287 }
288 }
289 catch (const std::exception& e)
290 {
291 // Interface or property not found. Let the Interfaces Added callback
292 // process the information once the interfaces are added to D-Bus.
293 }
294}
295
Jim Wright7a5dd992021-08-31 16:56:52 -0500296void PowerControl::setUpGpio()
297{
Jim Wright2d99bf72021-11-19 11:18:12 -0600298 const std::string powerControlLineName = "power-chassis-control";
299 const std::string pgoodLineName = "power-chassis-good";
300
Jim Wright7a5dd992021-08-31 16:56:52 -0500301 pgoodLine = gpiod::find_line(pgoodLineName);
302 if (!pgoodLine)
303 {
Jim Wright209690b2021-11-11 14:46:42 -0600304 std::string errorString{"GPIO line name not found: " + pgoodLineName};
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 powerControlLine = gpiod::find_line(powerControlLineName);
311 if (!powerControlLine)
312 {
Jim Wright209690b2021-11-11 14:46:42 -0600313 std::string errorString{"GPIO line name not found: " +
314 powerControlLineName};
Jim Wright7a5dd992021-08-31 16:56:52 -0500315 log<level::ERR>(errorString.c_str());
316 report<
317 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure>();
318 throw std::runtime_error(errorString);
319 }
320
321 pgoodLine.request(
322 {"phosphor-power-control", gpiod::line_request::DIRECTION_INPUT, 0});
323 int pgoodState = pgoodLine.get_value();
324 pgood = pgoodState;
325 state = pgoodState;
326 log<level::INFO>(fmt::format("Pgood state: {}", pgoodState).c_str());
327}
328
Jim Wright539b6082021-08-02 14:50:23 -0500329} // namespace phosphor::power::sequencer