blob: 9e8b1b473abd34554a4d8b4fbc3ed385a3737e8f [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},
Jim Wrightccea2d22021-12-10 14:10:46 -060047 bus{bus}, 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)},
53 timer{event, std::bind(&PowerControl::pollPgood, this), pollInterval}
Jim Wright539b6082021-08-02 14:50:23 -050054{
55 // Obtain dbus service name
56 bus.request_name(POWER_IFACE);
Jim Wright2d99bf72021-11-19 11:18:12 -060057
Jim Wright2d99bf72021-11-19 11:18:12 -060058 setUpDevice();
Jim Wright7a5dd992021-08-31 16:56:52 -050059 setUpGpio();
Jim Wright10eb00f2021-07-21 12:10:38 -050060}
Jim Wright539b6082021-08-02 14:50:23 -050061
Jim Wright2d99bf72021-11-19 11:18:12 -060062void PowerControl::getDeviceProperties(util::DbusPropertyMap& properties)
63{
64 uint64_t* i2cBus = nullptr;
65 uint64_t* i2cAddress = nullptr;
66 std::string* name = nullptr;
67
68 for (const auto& property : properties)
69 {
70 try
71 {
72 if (property.first == busPropertyName)
73 {
74 i2cBus = std::get_if<uint64_t>(&properties[busPropertyName]);
75 }
76 else if (property.first == addressPropertyName)
77 {
78 i2cAddress =
79 std::get_if<uint64_t>(&properties[addressPropertyName]);
80 }
81 else if (property.first == namePropertyName)
82 {
83 name = std::get_if<std::string>(&properties[namePropertyName]);
84 }
85 }
86 catch (std::exception& e)
87 {}
88 }
89
90 if (i2cBus && i2cAddress && name && !name->empty())
91 {
Jim Wright7945dd22021-04-06 16:55:15 -050092 log<level::DEBUG>(
Jim Wright2d99bf72021-11-19 11:18:12 -060093 fmt::format(
94 "Found power sequencer device properties, name: {}, bus: {} addr: {:#02x} ",
95 *name, *i2cBus, *i2cAddress)
96 .c_str());
97 // Create device object
Jim Wright7945dd22021-04-06 16:55:15 -050098 device = std::make_unique<UCD90320Monitor>(bus, *i2cBus, *i2cAddress);
Jim Wrightccea2d22021-12-10 14:10:46 -060099 deviceFound = true;
Jim Wright2d99bf72021-11-19 11:18:12 -0600100 }
101}
102
Jim Wright22318a32021-08-27 15:56:09 -0500103int PowerControl::getPgood() const
104{
105 return pgood;
106}
107
108int PowerControl::getPgoodTimeout() const
109{
110 return timeout.count();
111}
112
113int PowerControl::getState() const
114{
115 return state;
116}
117
Jim Wright2d99bf72021-11-19 11:18:12 -0600118void PowerControl::interfacesAddedHandler(sdbusplus::message::message& msg)
119{
Jim Wrightccea2d22021-12-10 14:10:46 -0600120 // Only continue if message is valid and device has not already been found
121 if (!msg || deviceFound)
Jim Wright2d99bf72021-11-19 11:18:12 -0600122 {
123 return;
124 }
125
126 try
127 {
128 // Read the dbus message
129 sdbusplus::message::object_path objPath;
130 std::map<std::string, std::map<std::string, util::DbusVariant>>
131 interfaces;
132 msg.read(objPath, interfaces);
133
134 // Find the device interface, if present
135 auto itIntf = interfaces.find(interfaceName);
136 if (itIntf != interfaces.cend())
137 {
138 log<level::INFO>(
139 fmt::format("InterfacesAdded for: {}", interfaceName).c_str());
140 getDeviceProperties(itIntf->second);
141 }
142 }
143 catch (const std::exception&)
144 {
145 // Error trying to read interfacesAdded message.
146 }
147}
148
Jim Wright539b6082021-08-02 14:50:23 -0500149void PowerControl::pollPgood()
Jim Wright7a5dd992021-08-31 16:56:52 -0500150{
151 if (inStateTransition)
152 {
Jim Wright9d7d95c2021-11-16 11:32:23 -0600153 // In transition between power on and off, check for timeout
Jim Wright7a5dd992021-08-31 16:56:52 -0500154 const auto now = std::chrono::steady_clock::now();
155 if (now > pgoodTimeoutTime)
156 {
157 log<level::ERR>("ERROR PowerControl: Pgood poll timeout");
158 inStateTransition = false;
Jim Wright4e25df52021-11-17 09:38:00 -0600159
160 try
161 {
162 auto method = bus.new_method_call(
163 "xyz.openbmc_project.Logging",
164 "/xyz/openbmc_project/logging",
165 "xyz.openbmc_project.Logging.Create", "Create");
166
167 std::map<std::string, std::string> additionalData;
168 // Add PID to AdditionalData
169 additionalData.emplace("_PID", std::to_string(getpid()));
170
171 method.append(
172 state ? "xyz.openbmc_project.Power.Error.PowerOnTimeout"
173 : "xyz.openbmc_project.Power.Error.PowerOffTimeout",
174 sdbusplus::xyz::openbmc_project::Logging::server::Entry::
175 Level::Critical,
176 additionalData);
177 bus.call_noreply(method);
178 }
179 catch (const std::exception& e)
180 {
181 log<level::ERR>(
182 fmt::format(
183 "Unable to log timeout error, state: {}, error {}",
184 state, e.what())
185 .c_str());
186 }
187
Jim Wright7a5dd992021-08-31 16:56:52 -0500188 return;
189 }
190 }
191
192 int pgoodState = pgoodLine.get_value();
193 if (pgoodState != pgood)
194 {
Jim Wright9d7d95c2021-11-16 11:32:23 -0600195 // Power good has changed since last read
Jim Wright7a5dd992021-08-31 16:56:52 -0500196 pgood = pgoodState;
197 if (pgoodState == 0)
198 {
199 emitPowerLostSignal();
200 }
201 else
202 {
203 emitPowerGoodSignal();
204 }
205 emitPropertyChangedSignal("pgood");
206 }
207 if (pgoodState == state)
208 {
Jim Wright9d7d95c2021-11-16 11:32:23 -0600209 // Power good matches requested state
Jim Wright7a5dd992021-08-31 16:56:52 -0500210 inStateTransition = false;
211 }
Jim Wright9d7d95c2021-11-16 11:32:23 -0600212 else if (!inStateTransition && (pgoodState == 0))
213 {
214 // Not in power off state, not changing state, and power good is off
215 // Power good has failed, call for chassis hard power off
216 log<level::ERR>("Chassis pgood failure");
217
218 auto method =
219 bus.new_method_call(util::SYSTEMD_SERVICE, util::SYSTEMD_ROOT,
220 util::SYSTEMD_INTERFACE, "StartUnit");
221 method.append(util::POWEROFF_TARGET);
222 method.append("replace");
223 bus.call_noreply(method);
224 }
Jim Wright7a5dd992021-08-31 16:56:52 -0500225}
Jim Wright539b6082021-08-02 14:50:23 -0500226
Jim Wright22318a32021-08-27 15:56:09 -0500227void PowerControl::setPgoodTimeout(int t)
228{
229 if (timeout.count() != t)
230 {
231 timeout = std::chrono::seconds(t);
232 emitPropertyChangedSignal("pgood_timeout");
233 }
234}
235
Jim Wrightccea2d22021-12-10 14:10:46 -0600236void PowerControl::setPowerSupplyError(const std::string& error)
237{
238 powerSupplyError = error;
239}
240
Jim Wright22318a32021-08-27 15:56:09 -0500241void PowerControl::setState(int s)
242{
243 if (state == s)
244 {
245 log<level::INFO>(
246 fmt::format("Power already at requested state: {}", state).c_str());
247 return;
248 }
Jim Wright209690b2021-11-11 14:46:42 -0600249 if (s == 0)
250 {
251 // Wait for two seconds when powering down. This is to allow host and
252 // other BMC applications time to complete power off processing
253 std::this_thread::sleep_for(std::chrono::seconds(2));
254 }
Jim Wright22318a32021-08-27 15:56:09 -0500255
256 log<level::INFO>(fmt::format("setState: {}", s).c_str());
Jim Wright7a5dd992021-08-31 16:56:52 -0500257 powerControlLine.request(
258 {"phosphor-power-control", gpiod::line_request::DIRECTION_OUTPUT, 0});
259 powerControlLine.set_value(s);
260 powerControlLine.release();
261
262 pgoodTimeoutTime = std::chrono::steady_clock::now() + timeout;
263 inStateTransition = true;
Jim Wright22318a32021-08-27 15:56:09 -0500264 state = s;
265 emitPropertyChangedSignal("state");
266}
267
Jim Wright2d99bf72021-11-19 11:18:12 -0600268void PowerControl::setUpDevice()
269{
270 try
271 {
272 auto objects = util::getSubTree(bus, "/", interfaceName, 0);
273
274 // Search for matching interface in returned objects
275 for (const auto& [path, services] : objects)
276 {
277 auto service = services.begin()->first;
278
279 if (path.empty() || service.empty())
280 {
281 continue;
282 }
283
284 // Get the properties for the device interface
285 auto properties =
286 util::getAllProperties(bus, path, interfaceName, service);
287
288 getDeviceProperties(properties);
289 }
290 }
291 catch (const std::exception& e)
292 {
293 // Interface or property not found. Let the Interfaces Added callback
294 // process the information once the interfaces are added to D-Bus.
295 }
296}
297
Jim Wright7a5dd992021-08-31 16:56:52 -0500298void PowerControl::setUpGpio()
299{
Jim Wright2d99bf72021-11-19 11:18:12 -0600300 const std::string powerControlLineName = "power-chassis-control";
301 const std::string pgoodLineName = "power-chassis-good";
302
Jim Wright7a5dd992021-08-31 16:56:52 -0500303 pgoodLine = gpiod::find_line(pgoodLineName);
304 if (!pgoodLine)
305 {
Jim Wright209690b2021-11-11 14:46:42 -0600306 std::string errorString{"GPIO line name not found: " + pgoodLineName};
Jim Wright7a5dd992021-08-31 16:56:52 -0500307 log<level::ERR>(errorString.c_str());
308 report<
309 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure>();
310 throw std::runtime_error(errorString);
311 }
312 powerControlLine = gpiod::find_line(powerControlLineName);
313 if (!powerControlLine)
314 {
Jim Wright209690b2021-11-11 14:46:42 -0600315 std::string errorString{"GPIO line name not found: " +
316 powerControlLineName};
Jim Wright7a5dd992021-08-31 16:56:52 -0500317 log<level::ERR>(errorString.c_str());
318 report<
319 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure>();
320 throw std::runtime_error(errorString);
321 }
322
323 pgoodLine.request(
324 {"phosphor-power-control", gpiod::line_request::DIRECTION_INPUT, 0});
325 int pgoodState = pgoodLine.get_value();
326 pgood = pgoodState;
327 state = pgoodState;
328 log<level::INFO>(fmt::format("Pgood state: {}", pgoodState).c_str());
329}
330
Jim Wright539b6082021-08-02 14:50:23 -0500331} // namespace phosphor::power::sequencer