blob: b4b993d502877f96a5da0d31198ab02d5095f66c [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 Wrighteae2d612022-12-23 13:14:17 -060020#include "ucd90160_monitor.hpp"
Jim Wright7945dd22021-04-06 16:55:15 -050021#include "ucd90320_monitor.hpp"
Jim Wright539b6082021-08-02 14:50:23 -050022
Jim Wrightb4ad95d2022-03-08 17:24:01 -060023#include <fmt/chrono.h>
Jim Wright22318a32021-08-27 15:56:09 -050024#include <fmt/format.h>
Jim Wrighteae2d612022-12-23 13:14:17 -060025#include <fmt/ranges.h>
Jim Wright22318a32021-08-27 15:56:09 -050026
Jim Wright7a5dd992021-08-31 16:56:52 -050027#include <phosphor-logging/elog-errors.hpp>
28#include <phosphor-logging/elog.hpp>
Jim Wright539b6082021-08-02 14:50:23 -050029#include <phosphor-logging/log.hpp>
Jim Wright7a5dd992021-08-31 16:56:52 -050030#include <xyz/openbmc_project/Common/error.hpp>
Jim Wright539b6082021-08-02 14:50:23 -050031
Jim Wright539b6082021-08-02 14:50:23 -050032#include <exception>
Jim Wrighteae2d612022-12-23 13:14:17 -060033#include <vector>
Jim Wright539b6082021-08-02 14:50:23 -050034
35using namespace phosphor::logging;
36
37namespace phosphor::power::sequencer
Jim Wright10eb00f2021-07-21 12:10:38 -050038{
Jim Wright539b6082021-08-02 14:50:23 -050039
Jim Wrighteae2d612022-12-23 13:14:17 -060040const std::vector<std::string>
41 interfaceNames({"xyz.openbmc_project.Configuration.UCD90160",
42 "xyz.openbmc_project.Configuration.UCD90320"});
Jim Wright2d99bf72021-11-19 11:18:12 -060043const std::string addressPropertyName = "Address";
44const std::string busPropertyName = "Bus";
45const std::string namePropertyName = "Name";
Jim Wrighteae2d612022-12-23 13:14:17 -060046const std::string typePropertyName = "Type";
Jim Wright7a5dd992021-08-31 16:56:52 -050047
Patrick Williams7354ce62022-07-22 19:26:56 -050048PowerControl::PowerControl(sdbusplus::bus_t& bus,
Jim Wright539b6082021-08-02 14:50:23 -050049 const sdeventplus::Event& event) :
Patrick Williams3fa31a72022-04-05 21:22:58 -050050 PowerObject{bus, POWER_OBJ_PATH, PowerObject::action::defer_emit},
Jim Wright930458c2022-01-24 14:37:27 -060051 bus{bus}, device{std::make_unique<PowerSequencerMonitor>(bus)},
52 match{bus,
53 sdbusplus::bus::match::rules::interfacesAdded() +
54 sdbusplus::bus::match::rules::sender(
55 "xyz.openbmc_project.EntityManager"),
56 std::bind(&PowerControl::interfacesAddedHandler, this,
57 std::placeholders::_1)},
Jim Wright1b639532022-11-19 17:41:33 -060058 pgoodWaitTimer{event, std::bind(&PowerControl::onFailureCallback, this)},
Jim Wrightb4ad95d2022-03-08 17:24:01 -060059 powerOnAllowedTime{std::chrono::steady_clock::now() + minimumColdStartTime},
Jim Wrightccea2d22021-12-10 14:10:46 -060060 timer{event, std::bind(&PowerControl::pollPgood, this), pollInterval}
Jim Wright539b6082021-08-02 14:50:23 -050061{
62 // Obtain dbus service name
63 bus.request_name(POWER_IFACE);
Jim Wright2d99bf72021-11-19 11:18:12 -060064
Jim Wright2d99bf72021-11-19 11:18:12 -060065 setUpDevice();
Jim Wright7a5dd992021-08-31 16:56:52 -050066 setUpGpio();
Jim Wright10eb00f2021-07-21 12:10:38 -050067}
Jim Wright539b6082021-08-02 14:50:23 -050068
Jim Wrighteae2d612022-12-23 13:14:17 -060069void PowerControl::getDeviceProperties(const util::DbusPropertyMap& properties)
Jim Wright2d99bf72021-11-19 11:18:12 -060070{
Jim Wrighteae2d612022-12-23 13:14:17 -060071 uint64_t i2cBus{0};
72 uint64_t i2cAddress{0};
73 std::string name;
74 std::string type;
Jim Wright2d99bf72021-11-19 11:18:12 -060075
Jim Wrighteae2d612022-12-23 13:14:17 -060076 for (const auto& [property, value] : properties)
Jim Wright2d99bf72021-11-19 11:18:12 -060077 {
78 try
79 {
Jim Wrighteae2d612022-12-23 13:14:17 -060080 if (property == busPropertyName)
Jim Wright2d99bf72021-11-19 11:18:12 -060081 {
Jim Wrighteae2d612022-12-23 13:14:17 -060082 i2cBus = std::get<uint64_t>(value);
Jim Wright2d99bf72021-11-19 11:18:12 -060083 }
Jim Wrighteae2d612022-12-23 13:14:17 -060084 else if (property == addressPropertyName)
Jim Wright2d99bf72021-11-19 11:18:12 -060085 {
Jim Wrighteae2d612022-12-23 13:14:17 -060086 i2cAddress = std::get<uint64_t>(value);
Jim Wright2d99bf72021-11-19 11:18:12 -060087 }
Jim Wrighteae2d612022-12-23 13:14:17 -060088 else if (property == namePropertyName)
Jim Wright2d99bf72021-11-19 11:18:12 -060089 {
Jim Wrighteae2d612022-12-23 13:14:17 -060090 name = std::get<std::string>(value);
91 }
92 else if (property == typePropertyName)
93 {
94 type = std::get<std::string>(value);
Jim Wright2d99bf72021-11-19 11:18:12 -060095 }
96 }
Jim Wrighteae2d612022-12-23 13:14:17 -060097 catch (const std::exception& e)
98 {
99 log<level::INFO>(
100 fmt::format("Error getting device properties, error: {}",
101 e.what())
102 .c_str());
103 }
Jim Wright2d99bf72021-11-19 11:18:12 -0600104 }
105
Jim Wrighteae2d612022-12-23 13:14:17 -0600106 log<level::INFO>(
107 fmt::format(
108 "Found power sequencer device properties, name: {}, type: {}, bus: {} addr: {:#02x} ",
109 name, type, i2cBus, i2cAddress)
110 .c_str());
111
112 // Create device object
113 if (type == "UCD90320")
Jim Wright2d99bf72021-11-19 11:18:12 -0600114 {
Jim Wrighteae2d612022-12-23 13:14:17 -0600115 device = std::make_unique<UCD90320Monitor>(bus, i2cBus, i2cAddress);
116 deviceFound = true;
117 }
118 else if (type == "UCD90160")
119 {
120 device = std::make_unique<UCD90160Monitor>(bus, i2cBus, i2cAddress);
Jim Wrightccea2d22021-12-10 14:10:46 -0600121 deviceFound = true;
Jim Wright2d99bf72021-11-19 11:18:12 -0600122 }
123}
124
Jim Wright22318a32021-08-27 15:56:09 -0500125int PowerControl::getPgood() const
126{
127 return pgood;
128}
129
130int PowerControl::getPgoodTimeout() const
131{
132 return timeout.count();
133}
134
135int PowerControl::getState() const
136{
137 return state;
138}
139
Jim Wrighteae2d612022-12-23 13:14:17 -0600140void PowerControl::interfacesAddedHandler(sdbusplus::message_t& message)
Jim Wright2d99bf72021-11-19 11:18:12 -0600141{
Jim Wrightccea2d22021-12-10 14:10:46 -0600142 // Only continue if message is valid and device has not already been found
Jim Wrighteae2d612022-12-23 13:14:17 -0600143 if (!message || deviceFound)
Jim Wright2d99bf72021-11-19 11:18:12 -0600144 {
145 return;
146 }
147
148 try
149 {
150 // Read the dbus message
Jim Wrighteae2d612022-12-23 13:14:17 -0600151 sdbusplus::message::object_path path;
Jim Wright2d99bf72021-11-19 11:18:12 -0600152 std::map<std::string, std::map<std::string, util::DbusVariant>>
153 interfaces;
Jim Wrighteae2d612022-12-23 13:14:17 -0600154 message.read(path, interfaces);
Jim Wright2d99bf72021-11-19 11:18:12 -0600155
Jim Wrighteae2d612022-12-23 13:14:17 -0600156 for (const auto& [interface, properties] : interfaces)
Jim Wright2d99bf72021-11-19 11:18:12 -0600157 {
Jim Wrighteae2d612022-12-23 13:14:17 -0600158 log<level::DEBUG>(
159 fmt::format(
160 "Interfaces added handler found path: {}, interface: {}",
161 path.str, interface)
162 .c_str());
163
164 // Find the device interface, if present
165 for (const auto& interfaceName : interfaceNames)
166 {
167 if (interface == interfaceName)
168 {
169 log<level::INFO>(
170 fmt::format(
171 "Interfaces added handler matched interface name: {}",
172 interfaceName)
173 .c_str());
174 getDeviceProperties(properties);
175 }
176 }
Jim Wright2d99bf72021-11-19 11:18:12 -0600177 }
178 }
Jim Wrighteae2d612022-12-23 13:14:17 -0600179 catch (const std::exception& e)
Jim Wright2d99bf72021-11-19 11:18:12 -0600180 {
181 // Error trying to read interfacesAdded message.
Jim Wrighteae2d612022-12-23 13:14:17 -0600182 log<level::INFO>(
183 fmt::format(
184 "Error trying to read interfacesAdded message, error: {}",
185 e.what())
186 .c_str());
Jim Wright2d99bf72021-11-19 11:18:12 -0600187 }
188}
189
Jim Wright1b639532022-11-19 17:41:33 -0600190void PowerControl::onFailureCallback()
191{
192 log<level::INFO>("After onFailure wait");
193
194 onFailure(false);
195
196 // Power good has failed, call for chassis hard power off
197 auto method = bus.new_method_call(util::SYSTEMD_SERVICE, util::SYSTEMD_ROOT,
198 util::SYSTEMD_INTERFACE, "StartUnit");
199 method.append(util::POWEROFF_TARGET);
200 method.append("replace");
201 bus.call_noreply(method);
202}
203
204void PowerControl::onFailure(bool timeout)
205{
206 // Call device on failure
207 device->onFailure(timeout, powerSupplyError);
208}
209
Jim Wright539b6082021-08-02 14:50:23 -0500210void PowerControl::pollPgood()
Jim Wright7a5dd992021-08-31 16:56:52 -0500211{
212 if (inStateTransition)
213 {
Jim Wright9d7d95c2021-11-16 11:32:23 -0600214 // In transition between power on and off, check for timeout
Jim Wright7a5dd992021-08-31 16:56:52 -0500215 const auto now = std::chrono::steady_clock::now();
216 if (now > pgoodTimeoutTime)
217 {
Jim Wright213ffe92022-06-03 08:54:30 -0500218 log<level::ERR>(
219 fmt::format("Power state transition timeout, state: {}", state)
220 .c_str());
Jim Wright7a5dd992021-08-31 16:56:52 -0500221 inStateTransition = false;
Jim Wright4e25df52021-11-17 09:38:00 -0600222
Jim Wright930458c2022-01-24 14:37:27 -0600223 if (state)
Jim Wright4e25df52021-11-17 09:38:00 -0600224 {
Jim Wright930458c2022-01-24 14:37:27 -0600225 // Time out powering on
Jim Wright1b639532022-11-19 17:41:33 -0600226 onFailure(true);
Jim Wright4e25df52021-11-17 09:38:00 -0600227 }
Jim Wright930458c2022-01-24 14:37:27 -0600228 else
Jim Wright4e25df52021-11-17 09:38:00 -0600229 {
Jim Wright930458c2022-01-24 14:37:27 -0600230 // Time out powering off
231 std::map<std::string, std::string> additionalData{};
232 device->logError(
233 "xyz.openbmc_project.Power.Error.PowerOffTimeout",
234 additionalData);
Jim Wright4e25df52021-11-17 09:38:00 -0600235 }
236
Jim Wright48752622022-02-28 20:37:53 -0600237 failureFound = true;
Jim Wright7a5dd992021-08-31 16:56:52 -0500238 return;
239 }
240 }
241
242 int pgoodState = pgoodLine.get_value();
243 if (pgoodState != pgood)
244 {
Jim Wright9d7d95c2021-11-16 11:32:23 -0600245 // Power good has changed since last read
Jim Wright7a5dd992021-08-31 16:56:52 -0500246 pgood = pgoodState;
247 if (pgoodState == 0)
248 {
249 emitPowerLostSignal();
250 }
251 else
252 {
253 emitPowerGoodSignal();
Jim Wright48752622022-02-28 20:37:53 -0600254 // Clear any errors on the transition to power on
Jim Wrightabd64b92022-02-11 09:56:56 -0600255 powerSupplyError.clear();
Jim Wright48752622022-02-28 20:37:53 -0600256 failureFound = false;
Jim Wright7a5dd992021-08-31 16:56:52 -0500257 }
258 emitPropertyChangedSignal("pgood");
259 }
260 if (pgoodState == state)
261 {
Jim Wright9d7d95c2021-11-16 11:32:23 -0600262 // Power good matches requested state
Jim Wright7a5dd992021-08-31 16:56:52 -0500263 inStateTransition = false;
264 }
Jim Wright48752622022-02-28 20:37:53 -0600265 else if (!inStateTransition && (pgoodState == 0) && !failureFound)
Jim Wright9d7d95c2021-11-16 11:32:23 -0600266 {
267 // Not in power off state, not changing state, and power good is off
Jim Wright213ffe92022-06-03 08:54:30 -0500268 log<level::ERR>("Chassis pgood failure");
Jim Wright1b639532022-11-19 17:41:33 -0600269 pgoodWaitTimer.restartOnce(std::chrono::seconds(7));
Jim Wright48752622022-02-28 20:37:53 -0600270 failureFound = true;
Jim Wright9d7d95c2021-11-16 11:32:23 -0600271 }
Jim Wright7a5dd992021-08-31 16:56:52 -0500272}
Jim Wright539b6082021-08-02 14:50:23 -0500273
Jim Wright22318a32021-08-27 15:56:09 -0500274void PowerControl::setPgoodTimeout(int t)
275{
276 if (timeout.count() != t)
277 {
278 timeout = std::chrono::seconds(t);
279 emitPropertyChangedSignal("pgood_timeout");
280 }
281}
282
Jim Wrightccea2d22021-12-10 14:10:46 -0600283void PowerControl::setPowerSupplyError(const std::string& error)
284{
285 powerSupplyError = error;
286}
287
Jim Wright22318a32021-08-27 15:56:09 -0500288void PowerControl::setState(int s)
289{
290 if (state == s)
291 {
292 log<level::INFO>(
293 fmt::format("Power already at requested state: {}", state).c_str());
294 return;
295 }
Jim Wright209690b2021-11-11 14:46:42 -0600296 if (s == 0)
297 {
298 // Wait for two seconds when powering down. This is to allow host and
299 // other BMC applications time to complete power off processing
300 std::this_thread::sleep_for(std::chrono::seconds(2));
301 }
Jim Wrightb4ad95d2022-03-08 17:24:01 -0600302 else
303 {
304 // If minimum power off time has not passed, wait
305 if (powerOnAllowedTime > std::chrono::steady_clock::now())
306 {
307 log<level::INFO>(
308 fmt::format(
309 "Waiting {} seconds until power on allowed",
310 std::chrono::duration_cast<std::chrono::seconds>(
311 powerOnAllowedTime - std::chrono::steady_clock::now())
312 .count())
313 .c_str());
314 }
315 std::this_thread::sleep_until(powerOnAllowedTime);
316 }
Jim Wright22318a32021-08-27 15:56:09 -0500317
318 log<level::INFO>(fmt::format("setState: {}", s).c_str());
Jim Wright7a5dd992021-08-31 16:56:52 -0500319 powerControlLine.request(
320 {"phosphor-power-control", gpiod::line_request::DIRECTION_OUTPUT, 0});
321 powerControlLine.set_value(s);
322 powerControlLine.release();
323
Jim Wrightb4ad95d2022-03-08 17:24:01 -0600324 if (s == 0)
325 {
326 // Set a minimum amount of time to wait before next power on
Patrick Williams48781ae2023-05-10 07:50:50 -0500327 powerOnAllowedTime = std::chrono::steady_clock::now() +
328 minimumPowerOffTime;
Jim Wrightb4ad95d2022-03-08 17:24:01 -0600329 }
330
Jim Wright7a5dd992021-08-31 16:56:52 -0500331 pgoodTimeoutTime = std::chrono::steady_clock::now() + timeout;
332 inStateTransition = true;
Jim Wright22318a32021-08-27 15:56:09 -0500333 state = s;
334 emitPropertyChangedSignal("state");
335}
336
Jim Wright2d99bf72021-11-19 11:18:12 -0600337void PowerControl::setUpDevice()
338{
339 try
340 {
Jim Wrighteae2d612022-12-23 13:14:17 -0600341 // Check if device information is already available
342 auto objects = util::getSubTree(bus, "/", interfaceNames, 0);
Jim Wright2d99bf72021-11-19 11:18:12 -0600343
344 // Search for matching interface in returned objects
345 for (const auto& [path, services] : objects)
346 {
Jim Wrighteae2d612022-12-23 13:14:17 -0600347 log<level::DEBUG>(
348 fmt::format("Found path: {}, services: {}", path, services)
349 .c_str());
350 for (const auto& [service, interfaces] : services)
Jim Wright2d99bf72021-11-19 11:18:12 -0600351 {
Jim Wrighteae2d612022-12-23 13:14:17 -0600352 log<level::DEBUG>(
353 fmt::format("Found service: {}, interfaces: {}", service,
354 interfaces)
355 .c_str());
356 for (const auto& interface : interfaces)
357 {
358 log<level::DEBUG>(
359 fmt::format("Found interface: {}", interface).c_str());
360 // Get the properties for the device interface
361 auto properties =
362 util::getAllProperties(bus, path, interface, service);
363
364 getDeviceProperties(properties);
365 }
Jim Wright2d99bf72021-11-19 11:18:12 -0600366 }
Jim Wright2d99bf72021-11-19 11:18:12 -0600367 }
368 }
Jim Wrighteae2d612022-12-23 13:14:17 -0600369 catch (const std::exception& e)
Jim Wright2d99bf72021-11-19 11:18:12 -0600370 {
Jim Wrighteae2d612022-12-23 13:14:17 -0600371 // Interface or property not found. Let the Interfaces Added
372 // callback process the information once the interfaces are added to
373 // D-Bus.
374 log<level::DEBUG>(
375 fmt::format("Error setting up device, error: {}", e.what())
376 .c_str());
Jim Wright2d99bf72021-11-19 11:18:12 -0600377 }
378}
379
Jim Wright7a5dd992021-08-31 16:56:52 -0500380void PowerControl::setUpGpio()
381{
Jim Wright2d99bf72021-11-19 11:18:12 -0600382 const std::string powerControlLineName = "power-chassis-control";
383 const std::string pgoodLineName = "power-chassis-good";
384
Jim Wright7a5dd992021-08-31 16:56:52 -0500385 pgoodLine = gpiod::find_line(pgoodLineName);
386 if (!pgoodLine)
387 {
Jim Wright209690b2021-11-11 14:46:42 -0600388 std::string errorString{"GPIO line name not found: " + pgoodLineName};
Jim Wright7a5dd992021-08-31 16:56:52 -0500389 log<level::ERR>(errorString.c_str());
390 report<
391 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure>();
392 throw std::runtime_error(errorString);
393 }
394 powerControlLine = gpiod::find_line(powerControlLineName);
395 if (!powerControlLine)
396 {
Jim Wright209690b2021-11-11 14:46:42 -0600397 std::string errorString{"GPIO line name not found: " +
398 powerControlLineName};
Jim Wright7a5dd992021-08-31 16:56:52 -0500399 log<level::ERR>(errorString.c_str());
400 report<
401 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure>();
402 throw std::runtime_error(errorString);
403 }
404
405 pgoodLine.request(
406 {"phosphor-power-control", gpiod::line_request::DIRECTION_INPUT, 0});
407 int pgoodState = pgoodLine.get_value();
408 pgood = pgoodState;
409 state = pgoodState;
410 log<level::INFO>(fmt::format("Pgood state: {}", pgoodState).c_str());
411}
412
Jim Wright539b6082021-08-02 14:50:23 -0500413} // namespace phosphor::power::sequencer