blob: 5578491b220711017bcd917972d11be78ceb94ed [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
Patrick Williams7354ce62022-07-22 19:26:56 -050043PowerControl::PowerControl(sdbusplus::bus_t& bus,
Jim Wright539b6082021-08-02 14:50:23 -050044 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 Wright1b639532022-11-19 17:41:33 -060053 pgoodWaitTimer{event, std::bind(&PowerControl::onFailureCallback, this)},
Jim Wrightb4ad95d2022-03-08 17:24:01 -060054 powerOnAllowedTime{std::chrono::steady_clock::now() + minimumColdStartTime},
Jim Wrightccea2d22021-12-10 14:10:46 -060055 timer{event, std::bind(&PowerControl::pollPgood, this), pollInterval}
Jim Wright539b6082021-08-02 14:50:23 -050056{
57 // Obtain dbus service name
58 bus.request_name(POWER_IFACE);
Jim Wright2d99bf72021-11-19 11:18:12 -060059
Jim Wright2d99bf72021-11-19 11:18:12 -060060 setUpDevice();
Jim Wright7a5dd992021-08-31 16:56:52 -050061 setUpGpio();
Jim Wright10eb00f2021-07-21 12:10:38 -050062}
Jim Wright539b6082021-08-02 14:50:23 -050063
Jim Wright2d99bf72021-11-19 11:18:12 -060064void PowerControl::getDeviceProperties(util::DbusPropertyMap& properties)
65{
66 uint64_t* i2cBus = nullptr;
67 uint64_t* i2cAddress = nullptr;
68 std::string* name = nullptr;
69
70 for (const auto& property : properties)
71 {
72 try
73 {
74 if (property.first == busPropertyName)
75 {
76 i2cBus = std::get_if<uint64_t>(&properties[busPropertyName]);
77 }
78 else if (property.first == addressPropertyName)
79 {
80 i2cAddress =
81 std::get_if<uint64_t>(&properties[addressPropertyName]);
82 }
83 else if (property.first == namePropertyName)
84 {
85 name = std::get_if<std::string>(&properties[namePropertyName]);
86 }
87 }
Jim Wrightc91eed02022-07-22 11:27:06 -050088 catch (const std::exception&)
Jim Wright2d99bf72021-11-19 11:18:12 -060089 {}
90 }
91
92 if (i2cBus && i2cAddress && name && !name->empty())
93 {
Jim Wright7945dd22021-04-06 16:55:15 -050094 log<level::DEBUG>(
Jim Wright2d99bf72021-11-19 11:18:12 -060095 fmt::format(
96 "Found power sequencer device properties, name: {}, bus: {} addr: {:#02x} ",
97 *name, *i2cBus, *i2cAddress)
98 .c_str());
99 // Create device object
Jim Wright7945dd22021-04-06 16:55:15 -0500100 device = std::make_unique<UCD90320Monitor>(bus, *i2cBus, *i2cAddress);
Jim Wrightccea2d22021-12-10 14:10:46 -0600101 deviceFound = true;
Jim Wright2d99bf72021-11-19 11:18:12 -0600102 }
103}
104
Jim Wright22318a32021-08-27 15:56:09 -0500105int PowerControl::getPgood() const
106{
107 return pgood;
108}
109
110int PowerControl::getPgoodTimeout() const
111{
112 return timeout.count();
113}
114
115int PowerControl::getState() const
116{
117 return state;
118}
119
Patrick Williams7354ce62022-07-22 19:26:56 -0500120void PowerControl::interfacesAddedHandler(sdbusplus::message_t& msg)
Jim Wright2d99bf72021-11-19 11:18:12 -0600121{
Jim Wrightccea2d22021-12-10 14:10:46 -0600122 // Only continue if message is valid and device has not already been found
123 if (!msg || deviceFound)
Jim Wright2d99bf72021-11-19 11:18:12 -0600124 {
125 return;
126 }
127
128 try
129 {
130 // Read the dbus message
131 sdbusplus::message::object_path objPath;
132 std::map<std::string, std::map<std::string, util::DbusVariant>>
133 interfaces;
134 msg.read(objPath, interfaces);
135
136 // Find the device interface, if present
137 auto itIntf = interfaces.find(interfaceName);
138 if (itIntf != interfaces.cend())
139 {
140 log<level::INFO>(
141 fmt::format("InterfacesAdded for: {}", interfaceName).c_str());
142 getDeviceProperties(itIntf->second);
143 }
144 }
145 catch (const std::exception&)
146 {
147 // Error trying to read interfacesAdded message.
148 }
149}
150
Jim Wright1b639532022-11-19 17:41:33 -0600151void PowerControl::onFailureCallback()
152{
153 log<level::INFO>("After onFailure wait");
154
155 onFailure(false);
156
157 // Power good has failed, call for chassis hard power off
158 auto method = bus.new_method_call(util::SYSTEMD_SERVICE, util::SYSTEMD_ROOT,
159 util::SYSTEMD_INTERFACE, "StartUnit");
160 method.append(util::POWEROFF_TARGET);
161 method.append("replace");
162 bus.call_noreply(method);
163}
164
165void PowerControl::onFailure(bool timeout)
166{
167 // Call device on failure
168 device->onFailure(timeout, powerSupplyError);
169}
170
Jim Wright539b6082021-08-02 14:50:23 -0500171void PowerControl::pollPgood()
Jim Wright7a5dd992021-08-31 16:56:52 -0500172{
173 if (inStateTransition)
174 {
Jim Wright9d7d95c2021-11-16 11:32:23 -0600175 // In transition between power on and off, check for timeout
Jim Wright7a5dd992021-08-31 16:56:52 -0500176 const auto now = std::chrono::steady_clock::now();
177 if (now > pgoodTimeoutTime)
178 {
Jim Wright213ffe92022-06-03 08:54:30 -0500179 log<level::ERR>(
180 fmt::format("Power state transition timeout, state: {}", state)
181 .c_str());
Jim Wright7a5dd992021-08-31 16:56:52 -0500182 inStateTransition = false;
Jim Wright4e25df52021-11-17 09:38:00 -0600183
Jim Wright930458c2022-01-24 14:37:27 -0600184 if (state)
Jim Wright4e25df52021-11-17 09:38:00 -0600185 {
Jim Wright930458c2022-01-24 14:37:27 -0600186 // Time out powering on
Jim Wright1b639532022-11-19 17:41:33 -0600187 onFailure(true);
Jim Wright4e25df52021-11-17 09:38:00 -0600188 }
Jim Wright930458c2022-01-24 14:37:27 -0600189 else
Jim Wright4e25df52021-11-17 09:38:00 -0600190 {
Jim Wright930458c2022-01-24 14:37:27 -0600191 // Time out powering off
192 std::map<std::string, std::string> additionalData{};
193 device->logError(
194 "xyz.openbmc_project.Power.Error.PowerOffTimeout",
195 additionalData);
Jim Wright4e25df52021-11-17 09:38:00 -0600196 }
197
Jim Wright48752622022-02-28 20:37:53 -0600198 failureFound = true;
Jim Wright7a5dd992021-08-31 16:56:52 -0500199 return;
200 }
201 }
202
203 int pgoodState = pgoodLine.get_value();
204 if (pgoodState != pgood)
205 {
Jim Wright9d7d95c2021-11-16 11:32:23 -0600206 // Power good has changed since last read
Jim Wright7a5dd992021-08-31 16:56:52 -0500207 pgood = pgoodState;
208 if (pgoodState == 0)
209 {
210 emitPowerLostSignal();
211 }
212 else
213 {
214 emitPowerGoodSignal();
Jim Wright48752622022-02-28 20:37:53 -0600215 // Clear any errors on the transition to power on
Jim Wrightabd64b92022-02-11 09:56:56 -0600216 powerSupplyError.clear();
Jim Wright48752622022-02-28 20:37:53 -0600217 failureFound = false;
Jim Wright7a5dd992021-08-31 16:56:52 -0500218 }
219 emitPropertyChangedSignal("pgood");
220 }
221 if (pgoodState == state)
222 {
Jim Wright9d7d95c2021-11-16 11:32:23 -0600223 // Power good matches requested state
Jim Wright7a5dd992021-08-31 16:56:52 -0500224 inStateTransition = false;
225 }
Jim Wright48752622022-02-28 20:37:53 -0600226 else if (!inStateTransition && (pgoodState == 0) && !failureFound)
Jim Wright9d7d95c2021-11-16 11:32:23 -0600227 {
228 // Not in power off state, not changing state, and power good is off
Jim Wright213ffe92022-06-03 08:54:30 -0500229 log<level::ERR>("Chassis pgood failure");
Jim Wright1b639532022-11-19 17:41:33 -0600230 pgoodWaitTimer.restartOnce(std::chrono::seconds(7));
Jim Wright48752622022-02-28 20:37:53 -0600231 failureFound = true;
Jim Wright9d7d95c2021-11-16 11:32:23 -0600232 }
Jim Wright7a5dd992021-08-31 16:56:52 -0500233}
Jim Wright539b6082021-08-02 14:50:23 -0500234
Jim Wright22318a32021-08-27 15:56:09 -0500235void PowerControl::setPgoodTimeout(int t)
236{
237 if (timeout.count() != t)
238 {
239 timeout = std::chrono::seconds(t);
240 emitPropertyChangedSignal("pgood_timeout");
241 }
242}
243
Jim Wrightccea2d22021-12-10 14:10:46 -0600244void PowerControl::setPowerSupplyError(const std::string& error)
245{
246 powerSupplyError = error;
247}
248
Jim Wright22318a32021-08-27 15:56:09 -0500249void PowerControl::setState(int s)
250{
251 if (state == s)
252 {
253 log<level::INFO>(
254 fmt::format("Power already at requested state: {}", state).c_str());
255 return;
256 }
Jim Wright209690b2021-11-11 14:46:42 -0600257 if (s == 0)
258 {
259 // Wait for two seconds when powering down. This is to allow host and
260 // other BMC applications time to complete power off processing
261 std::this_thread::sleep_for(std::chrono::seconds(2));
262 }
Jim Wrightb4ad95d2022-03-08 17:24:01 -0600263 else
264 {
265 // If minimum power off time has not passed, wait
266 if (powerOnAllowedTime > std::chrono::steady_clock::now())
267 {
268 log<level::INFO>(
269 fmt::format(
270 "Waiting {} seconds until power on allowed",
271 std::chrono::duration_cast<std::chrono::seconds>(
272 powerOnAllowedTime - std::chrono::steady_clock::now())
273 .count())
274 .c_str());
275 }
276 std::this_thread::sleep_until(powerOnAllowedTime);
277 }
Jim Wright22318a32021-08-27 15:56:09 -0500278
279 log<level::INFO>(fmt::format("setState: {}", s).c_str());
Jim Wright7a5dd992021-08-31 16:56:52 -0500280 powerControlLine.request(
281 {"phosphor-power-control", gpiod::line_request::DIRECTION_OUTPUT, 0});
282 powerControlLine.set_value(s);
283 powerControlLine.release();
284
Jim Wrightb4ad95d2022-03-08 17:24:01 -0600285 if (s == 0)
286 {
287 // Set a minimum amount of time to wait before next power on
288 powerOnAllowedTime =
289 std::chrono::steady_clock::now() + minimumPowerOffTime;
290 }
291
Jim Wright7a5dd992021-08-31 16:56:52 -0500292 pgoodTimeoutTime = std::chrono::steady_clock::now() + timeout;
293 inStateTransition = true;
Jim Wright22318a32021-08-27 15:56:09 -0500294 state = s;
295 emitPropertyChangedSignal("state");
296}
297
Jim Wright2d99bf72021-11-19 11:18:12 -0600298void PowerControl::setUpDevice()
299{
300 try
301 {
302 auto objects = util::getSubTree(bus, "/", interfaceName, 0);
303
304 // Search for matching interface in returned objects
305 for (const auto& [path, services] : objects)
306 {
307 auto service = services.begin()->first;
308
309 if (path.empty() || service.empty())
310 {
311 continue;
312 }
313
314 // Get the properties for the device interface
315 auto properties =
316 util::getAllProperties(bus, path, interfaceName, service);
317
318 getDeviceProperties(properties);
319 }
320 }
Jim Wrightc91eed02022-07-22 11:27:06 -0500321 catch (const std::exception&)
Jim Wright2d99bf72021-11-19 11:18:12 -0600322 {
323 // Interface or property not found. Let the Interfaces Added callback
324 // process the information once the interfaces are added to D-Bus.
325 }
326}
327
Jim Wright7a5dd992021-08-31 16:56:52 -0500328void PowerControl::setUpGpio()
329{
Jim Wright2d99bf72021-11-19 11:18:12 -0600330 const std::string powerControlLineName = "power-chassis-control";
331 const std::string pgoodLineName = "power-chassis-good";
332
Jim Wright7a5dd992021-08-31 16:56:52 -0500333 pgoodLine = gpiod::find_line(pgoodLineName);
334 if (!pgoodLine)
335 {
Jim Wright209690b2021-11-11 14:46:42 -0600336 std::string errorString{"GPIO line name not found: " + pgoodLineName};
Jim Wright7a5dd992021-08-31 16:56:52 -0500337 log<level::ERR>(errorString.c_str());
338 report<
339 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure>();
340 throw std::runtime_error(errorString);
341 }
342 powerControlLine = gpiod::find_line(powerControlLineName);
343 if (!powerControlLine)
344 {
Jim Wright209690b2021-11-11 14:46:42 -0600345 std::string errorString{"GPIO line name not found: " +
346 powerControlLineName};
Jim Wright7a5dd992021-08-31 16:56:52 -0500347 log<level::ERR>(errorString.c_str());
348 report<
349 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure>();
350 throw std::runtime_error(errorString);
351 }
352
353 pgoodLine.request(
354 {"phosphor-power-control", gpiod::line_request::DIRECTION_INPUT, 0});
355 int pgoodState = pgoodLine.get_value();
356 pgood = pgoodState;
357 state = pgoodState;
358 log<level::INFO>(fmt::format("Pgood state: {}", pgoodState).c_str());
359}
360
Jim Wright539b6082021-08-02 14:50:23 -0500361} // namespace phosphor::power::sequencer