blob: 661f7e9a553c0fe4d30bac9fc39099fd85a11c40 [file] [log] [blame]
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001/*
2// Copyright (c) 2018-2019 Intel 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#include "i2c.hpp"
17
18#include <sys/sysinfo.h>
19#include <systemd/sd-journal.h>
20
21#include <boost/asio/posix/stream_descriptor.hpp>
22#include <boost/container/flat_map.hpp>
Jason M. Bills7d4aaac2019-09-19 14:03:44 -070023#include <boost/container/flat_set.hpp>
Ed Tanousf61ca6f2019-08-15 15:09:05 -070024#include <filesystem>
25#include <fstream>
26#include <gpiod.hpp>
27#include <iostream>
28#include <sdbusplus/asio/object_server.hpp>
29#include <string_view>
30
31namespace power_control
32{
33static boost::asio::io_service io;
34std::shared_ptr<sdbusplus::asio::connection> conn;
35static std::shared_ptr<sdbusplus::asio::dbus_interface> hostIface;
36static std::shared_ptr<sdbusplus::asio::dbus_interface> chassisIface;
37static std::shared_ptr<sdbusplus::asio::dbus_interface> powerButtonIface;
38static std::shared_ptr<sdbusplus::asio::dbus_interface> resetButtonIface;
39static std::shared_ptr<sdbusplus::asio::dbus_interface> nmiButtonIface;
40static std::shared_ptr<sdbusplus::asio::dbus_interface> osIface;
41static std::shared_ptr<sdbusplus::asio::dbus_interface> idButtonIface;
Chen Yugang174ec662019-08-19 19:58:49 +080042static std::shared_ptr<sdbusplus::asio::dbus_interface> nmiOutIface;
Jason M. Bills7d4aaac2019-09-19 14:03:44 -070043static std::shared_ptr<sdbusplus::asio::dbus_interface> restartCauseIface;
Ed Tanousf61ca6f2019-08-15 15:09:05 -070044
45static gpiod::line powerButtonMask;
46static gpiod::line resetButtonMask;
47static bool nmiButtonMasked = false;
Ed Tanousf61ca6f2019-08-15 15:09:05 -070048
49const static constexpr int powerPulseTimeMs = 200;
50const static constexpr int forceOffPulseTimeMs = 15000;
51const static constexpr int resetPulseTimeMs = 500;
52const static constexpr int powerCycleTimeMs = 1000;
53const static constexpr int sioPowerGoodWatchdogTimeMs = 1000;
54const static constexpr int psPowerOKWatchdogTimeMs = 8000;
55const static constexpr int gracefulPowerOffTimeMs = 60000;
Jason M. Billse9a9e2d2019-08-08 14:01:19 -070056const static constexpr int warmResetCheckTimeMs = 500;
Ed Tanousf61ca6f2019-08-15 15:09:05 -070057const static constexpr int buttonMaskTimeMs = 60000;
58const static constexpr int powerOffSaveTimeMs = 7000;
59
60const static std::filesystem::path powerControlDir = "/var/lib/power-control";
61const static constexpr std::string_view powerStateFile = "power-state";
62
63static bool nmiEnabled = true;
64static constexpr const char* nmiOutName = "NMI_OUT";
Vijay Khemka0dc7d4c2019-10-22 12:30:17 -070065static constexpr const char* powerOutName = "POWER_OUT";
66static constexpr const char* resetOutName = "RESET_OUT";
Ed Tanousf61ca6f2019-08-15 15:09:05 -070067
68// Timers
69// Time holding GPIOs asserted
70static boost::asio::steady_timer gpioAssertTimer(io);
71// Time between off and on during a power cycle
72static boost::asio::steady_timer powerCycleTimer(io);
73// Time OS gracefully powering off
74static boost::asio::steady_timer gracefulPowerOffTimer(io);
Jason M. Billse9a9e2d2019-08-08 14:01:19 -070075// Time the warm reset check
76static boost::asio::steady_timer warmResetCheckTimer(io);
Ed Tanousf61ca6f2019-08-15 15:09:05 -070077// Time power supply power OK assertion on power-on
78static boost::asio::steady_timer psPowerOKWatchdogTimer(io);
79// Time SIO power good assertion on power-on
80static boost::asio::steady_timer sioPowerGoodWatchdogTimer(io);
81// Time power-off state save for power loss tracking
82static boost::asio::steady_timer powerStateSaveTimer(io);
83// POH timer
84static boost::asio::steady_timer pohCounterTimer(io);
Jason M. Bills7d4aaac2019-09-19 14:03:44 -070085// Time when to allow restart cause updates
86static boost::asio::steady_timer restartCauseTimer(io);
Ed Tanousf61ca6f2019-08-15 15:09:05 -070087
88// GPIO Lines and Event Descriptors
89static gpiod::line psPowerOKLine;
90static boost::asio::posix::stream_descriptor psPowerOKEvent(io);
91static gpiod::line sioPowerGoodLine;
92static boost::asio::posix::stream_descriptor sioPowerGoodEvent(io);
93static gpiod::line sioOnControlLine;
94static boost::asio::posix::stream_descriptor sioOnControlEvent(io);
95static gpiod::line sioS5Line;
96static boost::asio::posix::stream_descriptor sioS5Event(io);
97static gpiod::line powerButtonLine;
98static boost::asio::posix::stream_descriptor powerButtonEvent(io);
99static gpiod::line resetButtonLine;
100static boost::asio::posix::stream_descriptor resetButtonEvent(io);
101static gpiod::line nmiButtonLine;
102static boost::asio::posix::stream_descriptor nmiButtonEvent(io);
103static gpiod::line idButtonLine;
104static boost::asio::posix::stream_descriptor idButtonEvent(io);
105static gpiod::line postCompleteLine;
106static boost::asio::posix::stream_descriptor postCompleteEvent(io);
107static gpiod::line nmiOutLine;
108
109static constexpr uint8_t beepPowerFail = 8;
110
111static void beep(const uint8_t& beepPriority)
112{
113 std::cerr << "Beep with priority: " << (unsigned)beepPriority << "\n";
114
115 conn->async_method_call(
116 [](boost::system::error_code ec) {
117 if (ec)
118 {
119 std::cerr << "beep returned error with "
120 "async_method_call (ec = "
121 << ec << ")\n";
122 return;
123 }
124 },
125 "xyz.openbmc_project.BeepCode", "/xyz/openbmc_project/BeepCode",
126 "xyz.openbmc_project.BeepCode", "Beep", uint8_t(beepPriority));
127}
128
129enum class PowerState
130{
131 on,
132 waitForPSPowerOK,
133 waitForSIOPowerGood,
134 failedTransitionToOn,
135 off,
136 transitionToOff,
137 gracefulTransitionToOff,
138 cycleOff,
139 transitionToCycleOff,
140 gracefulTransitionToCycleOff,
Jason M. Billse9a9e2d2019-08-08 14:01:19 -0700141 checkForWarmReset,
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700142};
143static PowerState powerState;
144static std::string getPowerStateName(PowerState state)
145{
146 switch (state)
147 {
148 case PowerState::on:
149 return "On";
150 break;
151 case PowerState::waitForPSPowerOK:
152 return "Wait for Power Supply Power OK";
153 break;
154 case PowerState::waitForSIOPowerGood:
155 return "Wait for SIO Power Good";
156 break;
157 case PowerState::failedTransitionToOn:
158 return "Failed Transition to On";
159 break;
160 case PowerState::off:
161 return "Off";
162 break;
163 case PowerState::transitionToOff:
164 return "Transition to Off";
165 break;
166 case PowerState::gracefulTransitionToOff:
167 return "Graceful Transition to Off";
168 break;
169 case PowerState::cycleOff:
170 return "Power Cycle Off";
171 break;
172 case PowerState::transitionToCycleOff:
173 return "Transition to Power Cycle Off";
174 break;
175 case PowerState::gracefulTransitionToCycleOff:
176 return "Graceful Transition to Power Cycle Off";
177 break;
Jason M. Billse9a9e2d2019-08-08 14:01:19 -0700178 case PowerState::checkForWarmReset:
179 return "Check for Warm Reset";
180 break;
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700181 default:
182 return "unknown state: " + std::to_string(static_cast<int>(state));
183 break;
184 }
185}
186static void logStateTransition(const PowerState state)
187{
188 std::cerr << "Moving to \"" << getPowerStateName(state) << "\" state.\n";
189}
190
191enum class Event
192{
193 psPowerOKAssert,
194 psPowerOKDeAssert,
195 sioPowerGoodAssert,
196 sioPowerGoodDeAssert,
197 sioS5Assert,
198 sioS5DeAssert,
Jason M. Billse9a9e2d2019-08-08 14:01:19 -0700199 postCompleteAssert,
200 postCompleteDeAssert,
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700201 powerButtonPressed,
Jason M. Billse9a9e2d2019-08-08 14:01:19 -0700202 resetButtonPressed,
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700203 powerCycleTimerExpired,
204 psPowerOKWatchdogTimerExpired,
205 sioPowerGoodWatchdogTimerExpired,
206 gracefulPowerOffTimerExpired,
207 powerOnRequest,
208 powerOffRequest,
209 powerCycleRequest,
210 resetRequest,
211 gracefulPowerOffRequest,
212 gracefulPowerCycleRequest,
Jason M. Billse9a9e2d2019-08-08 14:01:19 -0700213 warmResetDetected,
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700214};
215static std::string getEventName(Event event)
216{
217 switch (event)
218 {
219 case Event::psPowerOKAssert:
220 return "power supply power OK assert";
221 break;
222 case Event::psPowerOKDeAssert:
223 return "power supply power OK de-assert";
224 break;
225 case Event::sioPowerGoodAssert:
226 return "SIO power good assert";
227 break;
228 case Event::sioPowerGoodDeAssert:
229 return "SIO power good de-assert";
230 break;
231 case Event::sioS5Assert:
232 return "SIO S5 assert";
233 break;
234 case Event::sioS5DeAssert:
235 return "SIO S5 de-assert";
236 break;
Jason M. Billse9a9e2d2019-08-08 14:01:19 -0700237 case Event::postCompleteAssert:
238 return "POST Complete assert";
239 break;
240 case Event::postCompleteDeAssert:
241 return "POST Complete de-assert";
242 break;
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700243 case Event::powerButtonPressed:
244 return "power button pressed";
245 break;
Jason M. Billse9a9e2d2019-08-08 14:01:19 -0700246 case Event::resetButtonPressed:
247 return "reset button pressed";
248 break;
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700249 case Event::powerCycleTimerExpired:
250 return "power cycle timer expired";
251 break;
252 case Event::psPowerOKWatchdogTimerExpired:
253 return "power supply power OK watchdog timer expired";
254 break;
255 case Event::sioPowerGoodWatchdogTimerExpired:
256 return "SIO power good watchdog timer expired";
257 break;
258 case Event::gracefulPowerOffTimerExpired:
259 return "graceful power-off timer expired";
260 break;
261 case Event::powerOnRequest:
262 return "power-on request";
263 break;
264 case Event::powerOffRequest:
265 return "power-off request";
266 break;
267 case Event::powerCycleRequest:
268 return "power-cycle request";
269 break;
270 case Event::resetRequest:
271 return "reset request";
272 break;
273 case Event::gracefulPowerOffRequest:
274 return "graceful power-off request";
275 break;
276 case Event::gracefulPowerCycleRequest:
277 return "graceful power-cycle request";
278 break;
Jason M. Billse9a9e2d2019-08-08 14:01:19 -0700279 case Event::warmResetDetected:
280 return "warm reset detected";
281 break;
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700282 default:
283 return "unknown event: " + std::to_string(static_cast<int>(event));
284 break;
285 }
286}
287static void logEvent(const std::string_view stateHandler, const Event event)
288{
289 std::cerr << stateHandler << ": " << getEventName(event)
290 << " event received.\n";
291}
292
293// Power state handlers
294static void powerStateOn(const Event event);
295static void powerStateWaitForPSPowerOK(const Event event);
296static void powerStateWaitForSIOPowerGood(const Event event);
297static void powerStateFailedTransitionToOn(const Event event);
298static void powerStateOff(const Event event);
299static void powerStateTransitionToOff(const Event event);
300static void powerStateGracefulTransitionToOff(const Event event);
301static void powerStateCycleOff(const Event event);
302static void powerStateTransitionToCycleOff(const Event event);
303static void powerStateGracefulTransitionToCycleOff(const Event event);
Jason M. Billse9a9e2d2019-08-08 14:01:19 -0700304static void powerStateCheckForWarmReset(const Event event);
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700305
306static std::function<void(const Event)> getPowerStateHandler(PowerState state)
307{
308 switch (state)
309 {
310 case PowerState::on:
311 return powerStateOn;
312 break;
313 case PowerState::waitForPSPowerOK:
314 return powerStateWaitForPSPowerOK;
315 break;
316 case PowerState::waitForSIOPowerGood:
317 return powerStateWaitForSIOPowerGood;
318 break;
319 case PowerState::failedTransitionToOn:
320 return powerStateFailedTransitionToOn;
321 break;
322 case PowerState::off:
323 return powerStateOff;
324 break;
325 case PowerState::transitionToOff:
326 return powerStateTransitionToOff;
327 break;
328 case PowerState::gracefulTransitionToOff:
329 return powerStateGracefulTransitionToOff;
330 break;
331 case PowerState::cycleOff:
332 return powerStateCycleOff;
333 break;
334 case PowerState::transitionToCycleOff:
335 return powerStateTransitionToCycleOff;
336 break;
337 case PowerState::gracefulTransitionToCycleOff:
338 return powerStateGracefulTransitionToCycleOff;
339 break;
Jason M. Billse9a9e2d2019-08-08 14:01:19 -0700340 case PowerState::checkForWarmReset:
341 return powerStateCheckForWarmReset;
342 break;
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700343 default:
344 return std::function<void(const Event)>{};
345 break;
346 }
347};
348
349static void sendPowerControlEvent(const Event event)
350{
351 std::function<void(const Event)> handler = getPowerStateHandler(powerState);
352 if (handler == nullptr)
353 {
354 std::cerr << "Failed to find handler for power state: "
355 << static_cast<int>(powerState) << "\n";
356 return;
357 }
358 handler(event);
359}
360
361static uint64_t getCurrentTimeMs()
362{
363 struct timespec time = {};
364
365 if (clock_gettime(CLOCK_REALTIME, &time) < 0)
366 {
367 return 0;
368 }
369 uint64_t currentTimeMs = static_cast<uint64_t>(time.tv_sec) * 1000;
370 currentTimeMs += static_cast<uint64_t>(time.tv_nsec) / 1000 / 1000;
371
372 return currentTimeMs;
373}
374
375static constexpr std::string_view getHostState(const PowerState state)
376{
377 switch (state)
378 {
379 case PowerState::on:
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700380 case PowerState::gracefulTransitionToOff:
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700381 case PowerState::gracefulTransitionToCycleOff:
382 return "xyz.openbmc_project.State.Host.HostState.Running";
383 break;
384 case PowerState::waitForPSPowerOK:
385 case PowerState::waitForSIOPowerGood:
386 case PowerState::failedTransitionToOn:
387 case PowerState::off:
Jason M. Billse9a9e2d2019-08-08 14:01:19 -0700388 case PowerState::transitionToOff:
389 case PowerState::transitionToCycleOff:
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700390 case PowerState::cycleOff:
Jason M. Billse9a9e2d2019-08-08 14:01:19 -0700391 case PowerState::checkForWarmReset:
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700392 return "xyz.openbmc_project.State.Host.HostState.Off";
393 break;
394 default:
395 return "";
396 break;
397 }
398};
399static constexpr std::string_view getChassisState(const PowerState state)
400{
401 switch (state)
402 {
403 case PowerState::on:
404 case PowerState::transitionToOff:
405 case PowerState::gracefulTransitionToOff:
406 case PowerState::transitionToCycleOff:
407 case PowerState::gracefulTransitionToCycleOff:
Jason M. Billse9a9e2d2019-08-08 14:01:19 -0700408 case PowerState::checkForWarmReset:
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700409 return "xyz.openbmc_project.State.Chassis.PowerState.On";
410 break;
411 case PowerState::waitForPSPowerOK:
412 case PowerState::waitForSIOPowerGood:
413 case PowerState::failedTransitionToOn:
414 case PowerState::off:
415 case PowerState::cycleOff:
416 return "xyz.openbmc_project.State.Chassis.PowerState.Off";
417 break;
418 default:
419 return "";
420 break;
421 }
422};
423static void savePowerState(const PowerState state)
424{
425 powerStateSaveTimer.expires_after(
426 std::chrono::milliseconds(powerOffSaveTimeMs));
427 powerStateSaveTimer.async_wait([state](const boost::system::error_code ec) {
428 if (ec)
429 {
430 // operation_aborted is expected if timer is canceled before
431 // completion.
432 if (ec != boost::asio::error::operation_aborted)
433 {
434 std::cerr << "Power-state save async_wait failed: "
435 << ec.message() << "\n";
436 }
437 return;
438 }
439 std::ofstream powerStateStream(powerControlDir / powerStateFile);
440 powerStateStream << getChassisState(state);
441 });
442}
443static void setPowerState(const PowerState state)
444{
445 powerState = state;
446 logStateTransition(state);
447
448 hostIface->set_property("CurrentHostState",
449 std::string(getHostState(powerState)));
450
451 chassisIface->set_property("CurrentPowerState",
452 std::string(getChassisState(powerState)));
453 chassisIface->set_property("LastStateChangeTime", getCurrentTimeMs());
454
455 // Save the power state for the restore policy
456 savePowerState(state);
457}
458
459enum class RestartCause
460{
461 command,
462 resetButton,
463 powerButton,
Jason M. Bills7d4aaac2019-09-19 14:03:44 -0700464 watchdog,
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700465 powerPolicyOn,
466 powerPolicyRestore,
467 softReset,
468};
Jason M. Bills7d4aaac2019-09-19 14:03:44 -0700469static boost::container::flat_set<RestartCause> causeSet;
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700470static std::string getRestartCause(RestartCause cause)
471{
472 switch (cause)
473 {
474 case RestartCause::command:
475 return "xyz.openbmc_project.State.Host.RestartCause.IpmiCommand";
476 break;
477 case RestartCause::resetButton:
478 return "xyz.openbmc_project.State.Host.RestartCause.ResetButton";
479 break;
480 case RestartCause::powerButton:
481 return "xyz.openbmc_project.State.Host.RestartCause.PowerButton";
482 break;
Jason M. Bills7d4aaac2019-09-19 14:03:44 -0700483 case RestartCause::watchdog:
484 return "xyz.openbmc_project.State.Host.RestartCause.WatchdogTimer";
485 break;
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700486 case RestartCause::powerPolicyOn:
487 return "xyz.openbmc_project.State.Host.RestartCause."
488 "PowerPolicyAlwaysOn";
489 break;
490 case RestartCause::powerPolicyRestore:
491 return "xyz.openbmc_project.State.Host.RestartCause."
492 "PowerPolicyPreviousState";
493 break;
494 case RestartCause::softReset:
495 return "xyz.openbmc_project.State.Host.RestartCause.SoftReset";
496 break;
497 default:
498 return "xyz.openbmc_project.State.Host.RestartCause.Unknown";
499 break;
500 }
501}
Jason M. Bills7d4aaac2019-09-19 14:03:44 -0700502static void addRestartCause(const RestartCause cause)
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700503{
Jason M. Bills7d4aaac2019-09-19 14:03:44 -0700504 // Add this to the set of causes for this restart
505 causeSet.insert(cause);
506}
507static void clearRestartCause()
508{
509 // Clear the set for the next restart
510 causeSet.clear();
511}
512static void setRestartCauseProperty(const std::string& cause)
513{
514 std::cerr << "RestartCause set to " << cause << "\n";
515 restartCauseIface->set_property("RestartCause", cause);
516}
517static void setRestartCause()
518{
519 // Determine the actual restart cause based on the set of causes
520 std::string restartCause =
521 "xyz.openbmc_project.State.Host.RestartCause.Unknown";
522 if (causeSet.contains(RestartCause::watchdog))
523 {
524 restartCause = getRestartCause(RestartCause::watchdog);
525 }
526 else if (causeSet.contains(RestartCause::command))
527 {
528 restartCause = getRestartCause(RestartCause::command);
529 }
530 else if (causeSet.contains(RestartCause::resetButton))
531 {
532 restartCause = getRestartCause(RestartCause::resetButton);
533 }
534 else if (causeSet.contains(RestartCause::powerButton))
535 {
536 restartCause = getRestartCause(RestartCause::powerButton);
537 }
538 else if (causeSet.contains(RestartCause::powerPolicyOn))
539 {
540 restartCause = getRestartCause(RestartCause::powerPolicyOn);
541 }
542 else if (causeSet.contains(RestartCause::powerPolicyRestore))
543 {
544 restartCause = getRestartCause(RestartCause::powerPolicyRestore);
545 }
546 else if (causeSet.contains(RestartCause::softReset))
547 {
548 restartCause = getRestartCause(RestartCause::softReset);
549 }
550
551 setRestartCauseProperty(restartCause);
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700552}
553
Jason M. Bills6c2ad362019-08-01 16:53:35 -0700554static void systemPowerGoodFailedLog()
555{
556 sd_journal_send(
557 "MESSAGE=PowerControl: system power good failed to assert (VR failure)",
558 "PRIORITY=%i", LOG_INFO, "REDFISH_MESSAGE_ID=%s",
559 "OpenBMC.0.1.SystemPowerGoodFailed", "REDFISH_MESSAGE_ARGS=%d",
560 sioPowerGoodWatchdogTimeMs, NULL);
561}
562
563static void psPowerOKFailedLog()
564{
565 sd_journal_send(
566 "MESSAGE=PowerControl: power supply power good failed to assert",
567 "PRIORITY=%i", LOG_INFO, "REDFISH_MESSAGE_ID=%s",
568 "OpenBMC.0.1.PowerSupplyPowerGoodFailed", "REDFISH_MESSAGE_ARGS=%d",
569 psPowerOKWatchdogTimeMs, NULL);
570}
571
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700572static void powerRestorePolicyLog()
573{
574 sd_journal_send("MESSAGE=PowerControl: power restore policy applied",
575 "PRIORITY=%i", LOG_INFO, "REDFISH_MESSAGE_ID=%s",
576 "OpenBMC.0.1.PowerRestorePolicyApplied", NULL);
577}
578
579static void powerButtonPressLog()
580{
581 sd_journal_send("MESSAGE=PowerControl: power button pressed", "PRIORITY=%i",
582 LOG_INFO, "REDFISH_MESSAGE_ID=%s",
583 "OpenBMC.0.1.PowerButtonPressed", NULL);
584}
585
586static void resetButtonPressLog()
587{
588 sd_journal_send("MESSAGE=PowerControl: reset button pressed", "PRIORITY=%i",
589 LOG_INFO, "REDFISH_MESSAGE_ID=%s",
590 "OpenBMC.0.1.ResetButtonPressed", NULL);
591}
592
593static void nmiButtonPressLog()
594{
595 sd_journal_send("MESSAGE=PowerControl: NMI button pressed", "PRIORITY=%i",
596 LOG_INFO, "REDFISH_MESSAGE_ID=%s",
597 "OpenBMC.0.1.NMIButtonPressed", NULL);
598}
599
600static void nmiDiagIntLog()
601{
602 sd_journal_send("MESSAGE=PowerControl: NMI Diagnostic Interrupt",
603 "PRIORITY=%i", LOG_INFO, "REDFISH_MESSAGE_ID=%s",
604 "OpenBMC.0.1.NMIDiagnosticInterrupt", NULL);
605}
606
607static int initializePowerStateStorage()
608{
609 // create the power control directory if it doesn't exist
610 std::error_code ec;
611 if (!(std::filesystem::create_directories(powerControlDir, ec)))
612 {
613 if (ec.value() != 0)
614 {
615 std::cerr << "failed to create " << powerControlDir << ": "
616 << ec.message() << "\n";
617 return -1;
618 }
619 }
620 // Create the power state file if it doesn't exist
621 if (!std::filesystem::exists(powerControlDir / powerStateFile))
622 {
623 std::ofstream powerStateStream(powerControlDir / powerStateFile);
624 powerStateStream << getChassisState(powerState);
625 }
626 return 0;
627}
628
629static bool wasPowerDropped()
630{
631 std::ifstream powerStateStream(powerControlDir / powerStateFile);
632 if (!powerStateStream.is_open())
633 {
634 std::cerr << "Failed to open power state file\n";
635 return false;
636 }
637
638 std::string state;
639 std::getline(powerStateStream, state);
640 return state == "xyz.openbmc_project.State.Chassis.PowerState.On";
641}
642
643static void invokePowerRestorePolicy(const std::string& policy)
644{
645 // Async events may call this twice, but we only want to run once
646 static bool policyInvoked = false;
647 if (policyInvoked)
648 {
649 return;
650 }
651 policyInvoked = true;
652
653 std::cerr << "Power restore delay expired, invoking " << policy << "\n";
654 if (policy ==
655 "xyz.openbmc_project.Control.Power.RestorePolicy.Policy.AlwaysOn")
656 {
657 sendPowerControlEvent(Event::powerOnRequest);
Jason M. Bills7d4aaac2019-09-19 14:03:44 -0700658 setRestartCauseProperty(getRestartCause(RestartCause::powerPolicyOn));
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700659 }
660 else if (policy == "xyz.openbmc_project.Control.Power.RestorePolicy."
661 "Policy.Restore")
662 {
663 if (wasPowerDropped())
664 {
665 std::cerr << "Power was dropped, restoring Host On state\n";
666 sendPowerControlEvent(Event::powerOnRequest);
Jason M. Bills7d4aaac2019-09-19 14:03:44 -0700667 setRestartCauseProperty(
668 getRestartCause(RestartCause::powerPolicyRestore));
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700669 }
670 else
671 {
672 std::cerr << "No power drop, restoring Host Off state\n";
673 }
674 }
Jason M. Bills94ce8eb2019-09-30 10:13:25 -0700675 // We're done with the previous power state for the restore policy, so store
676 // the current state
677 savePowerState(powerState);
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700678}
679
680static void powerRestorePolicyDelay(int delay)
681{
682 // Async events may call this twice, but we only want to run once
683 static bool delayStarted = false;
684 if (delayStarted)
685 {
686 return;
687 }
688 delayStarted = true;
689 // Calculate the delay from now to meet the requested delay
690 // Subtract the approximate uboot time
691 static constexpr const int ubootSeconds = 20;
692 delay -= ubootSeconds;
693 // Subtract the time since boot
694 struct sysinfo info = {};
695 if (sysinfo(&info) == 0)
696 {
697 delay -= info.uptime;
698 }
699 // 0 is the minimum delay
700 delay = std::max(delay, 0);
701
702 static boost::asio::steady_timer powerRestorePolicyTimer(io);
703 powerRestorePolicyTimer.expires_after(std::chrono::seconds(delay));
704 std::cerr << "Power restore delay of " << delay << " seconds started\n";
705 powerRestorePolicyTimer.async_wait([](const boost::system::error_code ec) {
706 if (ec)
707 {
708 // operation_aborted is expected if timer is canceled before
709 // completion.
710 if (ec != boost::asio::error::operation_aborted)
711 {
712 std::cerr << "power restore policy async_wait failed: "
713 << ec.message() << "\n";
714 }
715 return;
716 }
717 // Get Power Restore Policy
718 // In case PowerRestorePolicy is not available, set a match for it
719 static std::unique_ptr<sdbusplus::bus::match::match>
720 powerRestorePolicyMatch = std::make_unique<
721 sdbusplus::bus::match::match>(
722 *conn,
723 "type='signal',interface='org.freedesktop.DBus.Properties',"
724 "member='PropertiesChanged',arg0namespace='xyz.openbmc_"
725 "project.Control.Power.RestorePolicy'",
726 [](sdbusplus::message::message& msg) {
727 std::string interfaceName;
728 boost::container::flat_map<std::string,
729 std::variant<std::string>>
730 propertiesChanged;
731 std::string policy;
732 try
733 {
734 msg.read(interfaceName, propertiesChanged);
735 policy = std::get<std::string>(
736 propertiesChanged.begin()->second);
737 }
738 catch (std::exception& e)
739 {
740 std::cerr
741 << "Unable to read power restore policy value\n";
742 powerRestorePolicyMatch.reset();
743 return;
744 }
745 invokePowerRestorePolicy(policy);
746 powerRestorePolicyMatch.reset();
747 });
748
749 // Check if it's already on DBus
750 conn->async_method_call(
751 [](boost::system::error_code ec,
752 const std::variant<std::string>& policyProperty) {
753 if (ec)
754 {
755 return;
756 }
757 powerRestorePolicyMatch.reset();
758 const std::string* policy =
759 std::get_if<std::string>(&policyProperty);
760 if (policy == nullptr)
761 {
762 std::cerr << "Unable to read power restore policy value\n";
763 return;
764 }
765 invokePowerRestorePolicy(*policy);
766 },
767 "xyz.openbmc_project.Settings",
768 "/xyz/openbmc_project/control/host0/power_restore_policy",
769 "org.freedesktop.DBus.Properties", "Get",
770 "xyz.openbmc_project.Control.Power.RestorePolicy",
771 "PowerRestorePolicy");
772 });
773}
774
775static void powerRestorePolicyStart()
776{
777 std::cerr << "Power restore policy started\n";
778 powerRestorePolicyLog();
779
780 // Get the desired delay time
781 // In case PowerRestoreDelay is not available, set a match for it
782 static std::unique_ptr<sdbusplus::bus::match::match>
783 powerRestoreDelayMatch = std::make_unique<sdbusplus::bus::match::match>(
784 *conn,
785 "type='signal',interface='org.freedesktop.DBus.Properties',member='"
786 "PropertiesChanged',arg0namespace='xyz.openbmc_project.Control."
787 "Power.RestoreDelay'",
788 [](sdbusplus::message::message& msg) {
789 std::string interfaceName;
790 boost::container::flat_map<std::string, std::variant<uint16_t>>
791 propertiesChanged;
792 int delay = 0;
793 try
794 {
795 msg.read(interfaceName, propertiesChanged);
796 delay =
797 std::get<uint16_t>(propertiesChanged.begin()->second);
798 }
799 catch (std::exception& e)
800 {
801 std::cerr << "Unable to read power restore delay value\n";
802 powerRestoreDelayMatch.reset();
803 return;
804 }
805 powerRestorePolicyDelay(delay);
806 powerRestoreDelayMatch.reset();
807 });
808
809 // Check if it's already on DBus
810 conn->async_method_call(
811 [](boost::system::error_code ec,
812 const std::variant<uint16_t>& delayProperty) {
813 if (ec)
814 {
815 return;
816 }
817 powerRestoreDelayMatch.reset();
818 const uint16_t* delay = std::get_if<uint16_t>(&delayProperty);
819 if (delay == nullptr)
820 {
821 std::cerr << "Unable to read power restore delay value\n";
822 return;
823 }
824 powerRestorePolicyDelay(*delay);
825 },
826 "xyz.openbmc_project.Settings",
827 "/xyz/openbmc_project/control/power_restore_delay",
828 "org.freedesktop.DBus.Properties", "Get",
829 "xyz.openbmc_project.Control.Power.RestoreDelay", "PowerRestoreDelay");
830}
831
832static void powerRestorePolicyCheck()
833{
834 // In case ACBoot is not available, set a match for it
835 static std::unique_ptr<sdbusplus::bus::match::match> acBootMatch =
836 std::make_unique<sdbusplus::bus::match::match>(
837 *conn,
838 "type='signal',interface='org.freedesktop.DBus.Properties',member='"
839 "PropertiesChanged',arg0namespace='xyz.openbmc_project.Common."
840 "ACBoot'",
841 [](sdbusplus::message::message& msg) {
842 std::string interfaceName;
843 boost::container::flat_map<std::string,
844 std::variant<std::string>>
845 propertiesChanged;
846 std::string acBoot;
847 try
848 {
849 msg.read(interfaceName, propertiesChanged);
850 acBoot = std::get<std::string>(
851 propertiesChanged.begin()->second);
852 }
853 catch (std::exception& e)
854 {
855 std::cerr << "Unable to read AC Boot status\n";
856 acBootMatch.reset();
857 return;
858 }
859 if (acBoot == "Unknown")
860 {
861 return;
862 }
863 if (acBoot == "True")
864 {
865 // Start the Power Restore policy
866 powerRestorePolicyStart();
867 }
868 acBootMatch.reset();
869 });
870
871 // Check if it's already on DBus
872 conn->async_method_call(
873 [](boost::system::error_code ec,
874 const std::variant<std::string>& acBootProperty) {
875 if (ec)
876 {
877 return;
878 }
879 const std::string* acBoot =
880 std::get_if<std::string>(&acBootProperty);
881 if (acBoot == nullptr)
882 {
883 std::cerr << "Unable to read AC Boot status\n";
884 return;
885 }
886 if (*acBoot == "Unknown")
887 {
888 return;
889 }
890 if (*acBoot == "True")
891 {
892 // Start the Power Restore policy
893 powerRestorePolicyStart();
894 }
895 acBootMatch.reset();
896 },
897 "xyz.openbmc_project.Settings",
898 "/xyz/openbmc_project/control/host0/ac_boot",
899 "org.freedesktop.DBus.Properties", "Get",
900 "xyz.openbmc_project.Common.ACBoot", "ACBoot");
901}
902
903static bool requestGPIOEvents(
904 const std::string& name, const std::function<void()>& handler,
905 gpiod::line& gpioLine,
906 boost::asio::posix::stream_descriptor& gpioEventDescriptor)
907{
908 // Find the GPIO line
909 gpioLine = gpiod::find_line(name);
910 if (!gpioLine)
911 {
912 std::cerr << "Failed to find the " << name << " line\n";
913 return false;
914 }
915
916 try
917 {
918 gpioLine.request(
919 {"power-control", gpiod::line_request::EVENT_BOTH_EDGES});
920 }
921 catch (std::exception&)
922 {
923 std::cerr << "Failed to request events for " << name << "\n";
924 return false;
925 }
926
927 int gpioLineFd = gpioLine.event_get_fd();
928 if (gpioLineFd < 0)
929 {
930 std::cerr << "Failed to get " << name << " fd\n";
931 return false;
932 }
933
934 gpioEventDescriptor.assign(gpioLineFd);
935
936 gpioEventDescriptor.async_wait(
937 boost::asio::posix::stream_descriptor::wait_read,
938 [&name, handler](const boost::system::error_code ec) {
939 if (ec)
940 {
941 std::cerr << name << " fd handler error: " << ec.message()
942 << "\n";
943 // TODO: throw here to force power-control to restart?
944 return;
945 }
946 handler();
947 });
948 return true;
949}
950
951static bool setGPIOOutput(const std::string& name, const int value,
952 gpiod::line& gpioLine)
953{
954 // Find the GPIO line
955 gpioLine = gpiod::find_line(name);
956 if (!gpioLine)
957 {
958 std::cerr << "Failed to find the " << name << " line.\n";
959 return false;
960 }
961
962 // Request GPIO output to specified value
963 try
964 {
965 gpioLine.request({__FUNCTION__, gpiod::line_request::DIRECTION_OUTPUT},
966 value);
967 }
968 catch (std::exception&)
969 {
970 std::cerr << "Failed to request " << name << " output\n";
971 return false;
972 }
973
974 std::cerr << name << " set to " << std::to_string(value) << "\n";
975 return true;
976}
977
978static int setMaskedGPIOOutputForMs(gpiod::line& maskedGPIOLine,
979 const std::string& name, const int value,
980 const int durationMs)
981{
982 // Set the masked GPIO line to the specified value
983 maskedGPIOLine.set_value(value);
984 std::cerr << name << " set to " << std::to_string(value) << "\n";
985 gpioAssertTimer.expires_after(std::chrono::milliseconds(durationMs));
986 gpioAssertTimer.async_wait(
987 [maskedGPIOLine, value, name](const boost::system::error_code ec) {
988 // Set the masked GPIO line back to the opposite value
989 maskedGPIOLine.set_value(!value);
990 std::cerr << name << " released\n";
991 if (ec)
992 {
993 // operation_aborted is expected if timer is canceled before
994 // completion.
995 if (ec != boost::asio::error::operation_aborted)
996 {
997 std::cerr << name << " async_wait failed: " + ec.message()
998 << "\n";
999 }
1000 }
1001 });
1002 return 0;
1003}
1004
1005static int setGPIOOutputForMs(const std::string& name, const int value,
1006 const int durationMs)
1007{
1008 // If the requested GPIO is masked, use the mask line to set the output
Vijay Khemka0dc7d4c2019-10-22 12:30:17 -07001009 if (powerButtonMask && name == power_control::powerOutName)
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001010 {
1011 return setMaskedGPIOOutputForMs(powerButtonMask, name, value,
1012 durationMs);
1013 }
Vijay Khemka0dc7d4c2019-10-22 12:30:17 -07001014 if (resetButtonMask && name == power_control::resetOutName)
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001015 {
1016 return setMaskedGPIOOutputForMs(resetButtonMask, name, value,
1017 durationMs);
1018 }
1019
1020 // No mask set, so request and set the GPIO normally
1021 gpiod::line gpioLine;
1022 if (!setGPIOOutput(name, value, gpioLine))
1023 {
1024 return -1;
1025 }
1026 gpioAssertTimer.expires_after(std::chrono::milliseconds(durationMs));
1027 gpioAssertTimer.async_wait(
Vijay Khemka0dc7d4c2019-10-22 12:30:17 -07001028 [gpioLine, value, name](const boost::system::error_code ec) {
1029 // Set the GPIO line back to the opposite value
1030 gpioLine.set_value(!value);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001031 std::cerr << name << " released\n";
1032 if (ec)
1033 {
1034 // operation_aborted is expected if timer is canceled before
1035 // completion.
1036 if (ec != boost::asio::error::operation_aborted)
1037 {
1038 std::cerr << name << " async_wait failed: " << ec.message()
1039 << "\n";
1040 }
1041 }
1042 });
1043 return 0;
1044}
1045
1046static void powerOn()
1047{
Vijay Khemka0dc7d4c2019-10-22 12:30:17 -07001048 setGPIOOutputForMs(power_control::powerOutName, 0, powerPulseTimeMs);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001049}
1050
1051static void gracefulPowerOff()
1052{
Vijay Khemka0dc7d4c2019-10-22 12:30:17 -07001053 setGPIOOutputForMs(power_control::powerOutName, 0, powerPulseTimeMs);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001054}
1055
1056static void forcePowerOff()
1057{
Vijay Khemka0dc7d4c2019-10-22 12:30:17 -07001058 if (setGPIOOutputForMs(power_control::powerOutName, 0,
1059 forceOffPulseTimeMs) < 0)
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001060 {
1061 return;
1062 }
1063
1064 // If the force off timer expires, then the PCH power-button override
1065 // failed, so attempt the Unconditional Powerdown SMBus command.
1066 gpioAssertTimer.async_wait([](const boost::system::error_code ec) {
1067 if (ec)
1068 {
1069 // operation_aborted is expected if timer is canceled before
1070 // completion.
1071 if (ec != boost::asio::error::operation_aborted)
1072 {
1073 std::cerr << "Force power off async_wait failed: "
1074 << ec.message() << "\n";
1075 }
1076 return;
1077 }
1078 std::cerr << "PCH Power-button override failed. Issuing Unconditional "
1079 "Powerdown SMBus command.\n";
1080 const static constexpr size_t pchDevBusAddress = 3;
1081 const static constexpr size_t pchDevSlaveAddress = 0x44;
1082 const static constexpr size_t pchCmdReg = 0;
1083 const static constexpr size_t pchPowerDownCmd = 0x02;
1084 if (i2cSet(pchDevBusAddress, pchDevSlaveAddress, pchCmdReg,
1085 pchPowerDownCmd) < 0)
1086 {
1087 std::cerr << "Unconditional Powerdown command failed! Not sure "
1088 "what to do now.\n";
1089 }
1090 });
1091}
1092
1093static void reset()
1094{
Vijay Khemka0dc7d4c2019-10-22 12:30:17 -07001095 setGPIOOutputForMs(power_control::resetOutName, 0, resetPulseTimeMs);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001096}
1097
1098static void gracefulPowerOffTimerStart()
1099{
1100 std::cerr << "Graceful power-off timer started\n";
1101 gracefulPowerOffTimer.expires_after(
1102 std::chrono::milliseconds(gracefulPowerOffTimeMs));
1103 gracefulPowerOffTimer.async_wait([](const boost::system::error_code ec) {
1104 if (ec)
1105 {
1106 // operation_aborted is expected if timer is canceled before
1107 // completion.
1108 if (ec != boost::asio::error::operation_aborted)
1109 {
1110 std::cerr << "Graceful power-off async_wait failed: "
1111 << ec.message() << "\n";
1112 }
1113 std::cerr << "Graceful power-off timer canceled\n";
1114 return;
1115 }
1116 std::cerr << "Graceful power-off timer completed\n";
1117 sendPowerControlEvent(Event::gracefulPowerOffTimerExpired);
1118 });
1119}
1120
1121static void powerCycleTimerStart()
1122{
1123 std::cerr << "Power-cycle timer started\n";
1124 powerCycleTimer.expires_after(std::chrono::milliseconds(powerCycleTimeMs));
1125 powerCycleTimer.async_wait([](const boost::system::error_code ec) {
1126 if (ec)
1127 {
1128 // operation_aborted is expected if timer is canceled before
1129 // completion.
1130 if (ec != boost::asio::error::operation_aborted)
1131 {
1132 std::cerr << "Power-cycle async_wait failed: " << ec.message()
1133 << "\n";
1134 }
1135 std::cerr << "Power-cycle timer canceled\n";
1136 return;
1137 }
1138 std::cerr << "Power-cycle timer completed\n";
1139 sendPowerControlEvent(Event::powerCycleTimerExpired);
1140 });
1141}
1142
1143static void psPowerOKWatchdogTimerStart()
1144{
1145 std::cerr << "power supply power OK watchdog timer started\n";
1146 psPowerOKWatchdogTimer.expires_after(
1147 std::chrono::milliseconds(psPowerOKWatchdogTimeMs));
1148 psPowerOKWatchdogTimer.async_wait(
1149 [](const boost::system::error_code ec) {
1150 if (ec)
1151 {
1152 // operation_aborted is expected if timer is canceled before
1153 // completion.
1154 if (ec != boost::asio::error::operation_aborted)
1155 {
1156 std::cerr
1157 << "power supply power OK watchdog async_wait failed: "
1158 << ec.message() << "\n";
1159 }
1160 std::cerr << "power supply power OK watchdog timer canceled\n";
1161 return;
1162 }
1163 std::cerr << "power supply power OK watchdog timer expired\n";
1164 sendPowerControlEvent(Event::psPowerOKWatchdogTimerExpired);
1165 });
1166}
1167
Jason M. Billse9a9e2d2019-08-08 14:01:19 -07001168static void warmResetCheckTimerStart()
1169{
1170 std::cerr << "Warm reset check timer started\n";
1171 warmResetCheckTimer.expires_after(
1172 std::chrono::milliseconds(warmResetCheckTimeMs));
1173 warmResetCheckTimer.async_wait([](const boost::system::error_code ec) {
1174 if (ec)
1175 {
1176 // operation_aborted is expected if timer is canceled before
1177 // completion.
1178 if (ec != boost::asio::error::operation_aborted)
1179 {
1180 std::cerr << "Warm reset check async_wait failed: "
1181 << ec.message() << "\n";
1182 }
1183 std::cerr << "Warm reset check timer canceled\n";
1184 return;
1185 }
1186 std::cerr << "Warm reset check timer completed\n";
1187 sendPowerControlEvent(Event::warmResetDetected);
1188 });
1189}
1190
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001191static void pohCounterTimerStart()
1192{
1193 std::cerr << "POH timer started\n";
1194 // Set the time-out as 1 hour, to align with POH command in ipmid
1195 pohCounterTimer.expires_after(std::chrono::hours(1));
1196 pohCounterTimer.async_wait([](const boost::system::error_code& ec) {
1197 if (ec)
1198 {
1199 // operation_aborted is expected if timer is canceled before
1200 // completion.
1201 if (ec != boost::asio::error::operation_aborted)
1202 {
1203 std::cerr << "POH timer async_wait failed: " << ec.message()
1204 << "\n";
1205 }
1206 std::cerr << "POH timer canceled\n";
1207 return;
1208 }
1209
1210 if (getHostState(powerState) !=
1211 "xyz.openbmc_project.State.Host.HostState.Running")
1212 {
1213 return;
1214 }
1215
1216 conn->async_method_call(
1217 [](boost::system::error_code ec,
1218 const std::variant<uint32_t>& pohCounterProperty) {
1219 if (ec)
1220 {
1221 std::cerr << "error to get poh counter\n";
1222 return;
1223 }
1224 const uint32_t* pohCounter =
1225 std::get_if<uint32_t>(&pohCounterProperty);
1226 if (pohCounter == nullptr)
1227 {
1228 std::cerr << "unable to read poh counter\n";
1229 return;
1230 }
1231
1232 conn->async_method_call(
1233 [](boost::system::error_code ec) {
1234 if (ec)
1235 {
1236 std::cerr << "failed to set poh counter\n";
1237 }
1238 },
1239 "xyz.openbmc_project.Settings",
1240 "/xyz/openbmc_project/state/chassis0",
1241 "org.freedesktop.DBus.Properties", "Set",
1242 "xyz.openbmc_project.State.PowerOnHours", "POHCounter",
1243 std::variant<uint32_t>(*pohCounter + 1));
1244 },
1245 "xyz.openbmc_project.Settings",
1246 "/xyz/openbmc_project/state/chassis0",
1247 "org.freedesktop.DBus.Properties", "Get",
1248 "xyz.openbmc_project.State.PowerOnHours", "POHCounter");
1249
1250 pohCounterTimerStart();
1251 });
1252}
1253
1254static void currentHostStateMonitor()
1255{
1256 static auto match = sdbusplus::bus::match::match(
1257 *conn,
1258 "type='signal',member='PropertiesChanged', "
1259 "interface='org.freedesktop.DBus.Properties', "
1260 "arg0namespace='xyz.openbmc_project.State.Host'",
1261 [](sdbusplus::message::message& message) {
1262 std::string intfName;
1263 std::map<std::string, std::variant<std::string>> properties;
1264
1265 message.read(intfName, properties);
1266
1267 std::variant<std::string> currentHostState;
1268
1269 try
1270 {
1271 currentHostState = properties.at("CurrentHostState");
1272 }
1273 catch (const std::out_of_range& e)
1274 {
1275 std::cerr << "Error in finding CurrentHostState property\n";
1276
1277 return;
1278 }
1279
1280 if (std::get<std::string>(currentHostState) ==
1281 "xyz.openbmc_project.State.Host.HostState.Running")
1282 {
1283 pohCounterTimerStart();
Jason M. Bills7d4aaac2019-09-19 14:03:44 -07001284 // Clear the restart cause set for the next restart
1285 clearRestartCause();
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001286 }
1287 else
1288 {
1289 pohCounterTimer.cancel();
Jason M. Bills7d4aaac2019-09-19 14:03:44 -07001290 // Set the restart cause set for this restart
1291 setRestartCause();
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001292 }
1293 });
1294}
1295
1296static void sioPowerGoodWatchdogTimerStart()
1297{
1298 std::cerr << "SIO power good watchdog timer started\n";
1299 sioPowerGoodWatchdogTimer.expires_after(
1300 std::chrono::milliseconds(sioPowerGoodWatchdogTimeMs));
1301 sioPowerGoodWatchdogTimer.async_wait(
1302 [](const boost::system::error_code ec) {
1303 if (ec)
1304 {
1305 // operation_aborted is expected if timer is canceled before
1306 // completion.
1307 if (ec != boost::asio::error::operation_aborted)
1308 {
1309 std::cerr << "SIO power good watchdog async_wait failed: "
1310 << ec.message() << "\n";
1311 }
1312 std::cerr << "SIO power good watchdog timer canceled\n";
1313 return;
1314 }
1315 std::cerr << "SIO power good watchdog timer completed\n";
1316 sendPowerControlEvent(Event::sioPowerGoodWatchdogTimerExpired);
1317 });
1318}
1319
1320static void powerStateOn(const Event event)
1321{
1322 logEvent(__FUNCTION__, event);
1323 switch (event)
1324 {
1325 case Event::psPowerOKDeAssert:
1326 setPowerState(PowerState::off);
1327 // DC power is unexpectedly lost, beep
1328 beep(beepPowerFail);
1329 break;
1330 case Event::sioS5Assert:
1331 setPowerState(PowerState::transitionToOff);
Jason M. Bills7d4aaac2019-09-19 14:03:44 -07001332 addRestartCause(RestartCause::softReset);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001333 break;
Jason M. Billse9a9e2d2019-08-08 14:01:19 -07001334 case Event::postCompleteDeAssert:
1335 setPowerState(PowerState::checkForWarmReset);
Jason M. Bills7d4aaac2019-09-19 14:03:44 -07001336 addRestartCause(RestartCause::softReset);
Jason M. Billse9a9e2d2019-08-08 14:01:19 -07001337 warmResetCheckTimerStart();
1338 break;
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001339 case Event::powerButtonPressed:
1340 setPowerState(PowerState::gracefulTransitionToOff);
1341 gracefulPowerOffTimerStart();
1342 break;
Jason M. Billse9a9e2d2019-08-08 14:01:19 -07001343 case Event::resetButtonPressed:
1344 setPowerState(PowerState::checkForWarmReset);
1345 warmResetCheckTimerStart();
1346 break;
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001347 case Event::powerOffRequest:
1348 setPowerState(PowerState::transitionToOff);
1349 forcePowerOff();
1350 break;
1351 case Event::gracefulPowerOffRequest:
1352 setPowerState(PowerState::gracefulTransitionToOff);
1353 gracefulPowerOffTimerStart();
1354 gracefulPowerOff();
1355 break;
1356 case Event::powerCycleRequest:
1357 setPowerState(PowerState::transitionToCycleOff);
1358 forcePowerOff();
1359 break;
1360 case Event::gracefulPowerCycleRequest:
1361 setPowerState(PowerState::gracefulTransitionToCycleOff);
1362 gracefulPowerOffTimerStart();
1363 gracefulPowerOff();
1364 break;
1365 case Event::resetRequest:
1366 reset();
1367 break;
1368 default:
1369 std::cerr << "No action taken.\n";
1370 break;
1371 }
1372}
1373
1374static void powerStateWaitForPSPowerOK(const Event event)
1375{
1376 logEvent(__FUNCTION__, event);
1377 switch (event)
1378 {
1379 case Event::psPowerOKAssert:
1380 // Cancel any GPIO assertions held during the transition
1381 gpioAssertTimer.cancel();
1382 psPowerOKWatchdogTimer.cancel();
1383 sioPowerGoodWatchdogTimerStart();
1384 setPowerState(PowerState::waitForSIOPowerGood);
1385 break;
1386 case Event::psPowerOKWatchdogTimerExpired:
1387 setPowerState(PowerState::failedTransitionToOn);
Jason M. Bills6c2ad362019-08-01 16:53:35 -07001388 psPowerOKFailedLog();
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001389 break;
Vijay Khemka0eef6b62019-10-22 12:22:52 -07001390 case Event::sioPowerGoodAssert:
1391 psPowerOKWatchdogTimer.cancel();
1392 setPowerState(PowerState::on);
1393 break;
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001394 default:
1395 std::cerr << "No action taken.\n";
1396 break;
1397 }
1398}
1399
1400static void powerStateWaitForSIOPowerGood(const Event event)
1401{
1402 logEvent(__FUNCTION__, event);
1403 switch (event)
1404 {
1405 case Event::sioPowerGoodAssert:
1406 sioPowerGoodWatchdogTimer.cancel();
1407 setPowerState(PowerState::on);
1408 break;
1409 case Event::sioPowerGoodWatchdogTimerExpired:
1410 setPowerState(PowerState::failedTransitionToOn);
Jason M. Bills6c2ad362019-08-01 16:53:35 -07001411 systemPowerGoodFailedLog();
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001412 forcePowerOff();
1413 break;
1414 default:
1415 std::cerr << "No action taken.\n";
1416 break;
1417 }
1418}
1419
1420static void powerStateFailedTransitionToOn(const Event event)
1421{
1422 logEvent(__FUNCTION__, event);
1423 switch (event)
1424 {
1425 case Event::psPowerOKAssert:
1426 // We're in a failure state, so don't allow the system to turn on
1427 // without a user request
1428 forcePowerOff();
1429 break;
1430 case Event::psPowerOKDeAssert:
1431 // Cancel any GPIO assertions held during the transition
1432 gpioAssertTimer.cancel();
1433 break;
1434 case Event::powerButtonPressed:
1435 psPowerOKWatchdogTimerStart();
1436 setPowerState(PowerState::waitForPSPowerOK);
1437 break;
1438 case Event::powerOnRequest:
1439 psPowerOKWatchdogTimerStart();
1440 setPowerState(PowerState::waitForPSPowerOK);
1441 powerOn();
1442 break;
1443 default:
1444 std::cerr << "No action taken.\n";
1445 break;
1446 }
1447}
1448
1449static void powerStateOff(const Event event)
1450{
1451 logEvent(__FUNCTION__, event);
1452 switch (event)
1453 {
1454 case Event::psPowerOKAssert:
1455 setPowerState(PowerState::waitForSIOPowerGood);
1456 break;
1457 case Event::sioS5DeAssert:
1458 setPowerState(PowerState::waitForPSPowerOK);
1459 break;
1460 case Event::powerButtonPressed:
1461 psPowerOKWatchdogTimerStart();
1462 setPowerState(PowerState::waitForPSPowerOK);
1463 break;
1464 case Event::powerOnRequest:
1465 psPowerOKWatchdogTimerStart();
1466 setPowerState(PowerState::waitForPSPowerOK);
1467 powerOn();
1468 break;
1469 default:
1470 std::cerr << "No action taken.\n";
1471 break;
1472 }
1473}
1474
1475static void powerStateTransitionToOff(const Event event)
1476{
1477 logEvent(__FUNCTION__, event);
1478 switch (event)
1479 {
1480 case Event::psPowerOKDeAssert:
1481 // Cancel any GPIO assertions held during the transition
1482 gpioAssertTimer.cancel();
1483 setPowerState(PowerState::off);
1484 break;
1485 default:
1486 std::cerr << "No action taken.\n";
1487 break;
1488 }
1489}
1490
1491static void powerStateGracefulTransitionToOff(const Event event)
1492{
1493 logEvent(__FUNCTION__, event);
1494 switch (event)
1495 {
1496 case Event::psPowerOKDeAssert:
1497 gracefulPowerOffTimer.cancel();
1498 setPowerState(PowerState::off);
1499 break;
1500 case Event::gracefulPowerOffTimerExpired:
1501 setPowerState(PowerState::on);
1502 break;
1503 default:
1504 std::cerr << "No action taken.\n";
1505 break;
1506 }
1507}
1508
1509static void powerStateCycleOff(const Event event)
1510{
1511 logEvent(__FUNCTION__, event);
1512 switch (event)
1513 {
1514 case Event::powerCycleTimerExpired:
1515 psPowerOKWatchdogTimerStart();
1516 setPowerState(PowerState::waitForPSPowerOK);
1517 powerOn();
1518 break;
1519 default:
1520 std::cerr << "No action taken.\n";
1521 break;
1522 }
1523}
1524
1525static void powerStateTransitionToCycleOff(const Event event)
1526{
1527 logEvent(__FUNCTION__, event);
1528 switch (event)
1529 {
1530 case Event::psPowerOKDeAssert:
1531 // Cancel any GPIO assertions held during the transition
1532 gpioAssertTimer.cancel();
1533 setPowerState(PowerState::cycleOff);
1534 powerCycleTimerStart();
1535 break;
1536 default:
1537 std::cerr << "No action taken.\n";
1538 break;
1539 }
1540}
1541
1542static void powerStateGracefulTransitionToCycleOff(const Event event)
1543{
1544 logEvent(__FUNCTION__, event);
1545 switch (event)
1546 {
1547 case Event::psPowerOKDeAssert:
1548 gracefulPowerOffTimer.cancel();
1549 setPowerState(PowerState::cycleOff);
1550 powerCycleTimerStart();
1551 break;
1552 case Event::gracefulPowerOffTimerExpired:
1553 setPowerState(PowerState::on);
1554 break;
1555 default:
1556 std::cerr << "No action taken.\n";
1557 break;
1558 }
1559}
1560
Jason M. Billse9a9e2d2019-08-08 14:01:19 -07001561static void powerStateCheckForWarmReset(const Event event)
1562{
1563 logEvent(__FUNCTION__, event);
1564 switch (event)
1565 {
1566 case Event::sioS5Assert:
1567 warmResetCheckTimer.cancel();
1568 setPowerState(PowerState::transitionToOff);
1569 break;
1570 case Event::warmResetDetected:
1571 setPowerState(PowerState::on);
1572 break;
1573 default:
1574 std::cerr << "No action taken.\n";
1575 break;
1576 }
1577}
1578
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001579static void psPowerOKHandler()
1580{
1581 gpiod::line_event gpioLineEvent = psPowerOKLine.event_read();
1582
1583 Event powerControlEvent =
1584 gpioLineEvent.event_type == gpiod::line_event::RISING_EDGE
1585 ? Event::psPowerOKAssert
1586 : Event::psPowerOKDeAssert;
1587
1588 sendPowerControlEvent(powerControlEvent);
1589 psPowerOKEvent.async_wait(
1590 boost::asio::posix::stream_descriptor::wait_read,
1591 [](const boost::system::error_code ec) {
1592 if (ec)
1593 {
1594 std::cerr << "power supply power OK handler error: "
1595 << ec.message() << "\n";
1596 return;
1597 }
1598 psPowerOKHandler();
1599 });
1600}
1601
1602static void sioPowerGoodHandler()
1603{
1604 gpiod::line_event gpioLineEvent = sioPowerGoodLine.event_read();
1605
1606 Event powerControlEvent =
1607 gpioLineEvent.event_type == gpiod::line_event::RISING_EDGE
1608 ? Event::sioPowerGoodAssert
1609 : Event::sioPowerGoodDeAssert;
1610
1611 sendPowerControlEvent(powerControlEvent);
1612 sioPowerGoodEvent.async_wait(
1613 boost::asio::posix::stream_descriptor::wait_read,
1614 [](const boost::system::error_code ec) {
1615 if (ec)
1616 {
1617 std::cerr << "SIO power good handler error: " << ec.message()
1618 << "\n";
1619 return;
1620 }
1621 sioPowerGoodHandler();
1622 });
1623}
1624
1625static void sioOnControlHandler()
1626{
1627 gpiod::line_event gpioLineEvent = sioOnControlLine.event_read();
1628
1629 bool sioOnControl =
1630 gpioLineEvent.event_type == gpiod::line_event::RISING_EDGE;
1631 std::cerr << "SIO_ONCONTROL value changed: " << sioOnControl << "\n";
1632 sioOnControlEvent.async_wait(
1633 boost::asio::posix::stream_descriptor::wait_read,
1634 [](const boost::system::error_code ec) {
1635 if (ec)
1636 {
1637 std::cerr << "SIO ONCONTROL handler error: " << ec.message()
1638 << "\n";
1639 return;
1640 }
1641 sioOnControlHandler();
1642 });
1643}
1644
1645static void sioS5Handler()
1646{
1647 gpiod::line_event gpioLineEvent = sioS5Line.event_read();
1648
1649 Event powerControlEvent =
1650 gpioLineEvent.event_type == gpiod::line_event::FALLING_EDGE
1651 ? Event::sioS5Assert
1652 : Event::sioS5DeAssert;
1653
1654 sendPowerControlEvent(powerControlEvent);
1655 sioS5Event.async_wait(boost::asio::posix::stream_descriptor::wait_read,
1656 [](const boost::system::error_code ec) {
1657 if (ec)
1658 {
1659 std::cerr << "SIO S5 handler error: "
1660 << ec.message() << "\n";
1661 return;
1662 }
1663 sioS5Handler();
1664 });
1665}
1666
1667static void powerButtonHandler()
1668{
1669 gpiod::line_event gpioLineEvent = powerButtonLine.event_read();
1670
1671 if (gpioLineEvent.event_type == gpiod::line_event::FALLING_EDGE)
1672 {
1673 powerButtonPressLog();
1674 powerButtonIface->set_property("ButtonPressed", true);
1675 if (!powerButtonMask)
1676 {
1677 sendPowerControlEvent(Event::powerButtonPressed);
Jason M. Bills7d4aaac2019-09-19 14:03:44 -07001678 addRestartCause(RestartCause::powerButton);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001679 }
1680 else
1681 {
1682 std::cerr << "power button press masked\n";
1683 }
1684 }
1685 else if (gpioLineEvent.event_type == gpiod::line_event::RISING_EDGE)
1686 {
1687 powerButtonIface->set_property("ButtonPressed", false);
1688 }
1689 powerButtonEvent.async_wait(
1690 boost::asio::posix::stream_descriptor::wait_read,
1691 [](const boost::system::error_code ec) {
1692 if (ec)
1693 {
1694 std::cerr << "power button handler error: " << ec.message()
1695 << "\n";
1696 return;
1697 }
1698 powerButtonHandler();
1699 });
1700}
1701
1702static void resetButtonHandler()
1703{
1704 gpiod::line_event gpioLineEvent = resetButtonLine.event_read();
1705
1706 if (gpioLineEvent.event_type == gpiod::line_event::FALLING_EDGE)
1707 {
1708 resetButtonPressLog();
1709 resetButtonIface->set_property("ButtonPressed", true);
1710 if (!resetButtonMask)
1711 {
Jason M. Billse9a9e2d2019-08-08 14:01:19 -07001712 sendPowerControlEvent(Event::resetButtonPressed);
Jason M. Bills7d4aaac2019-09-19 14:03:44 -07001713 addRestartCause(RestartCause::resetButton);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001714 }
1715 else
1716 {
1717 std::cerr << "reset button press masked\n";
1718 }
1719 }
1720 else if (gpioLineEvent.event_type == gpiod::line_event::RISING_EDGE)
1721 {
1722 resetButtonIface->set_property("ButtonPressed", false);
1723 }
1724 resetButtonEvent.async_wait(
1725 boost::asio::posix::stream_descriptor::wait_read,
1726 [](const boost::system::error_code ec) {
1727 if (ec)
1728 {
1729 std::cerr << "reset button handler error: " << ec.message()
1730 << "\n";
1731 return;
1732 }
1733 resetButtonHandler();
1734 });
1735}
1736
1737static void nmiSetEnablePorperty(bool value)
1738{
1739 conn->async_method_call(
1740 [](boost::system::error_code ec) {
1741 if (ec)
1742 {
1743 std::cerr << "failed to set NMI source\n";
1744 }
1745 },
1746 "xyz.openbmc_project.Settings", "/com/intel/control/NMISource",
1747 "org.freedesktop.DBus.Properties", "Set", "com.intel.Control.NMISource",
1748 "Enabled", std::variant<bool>{value});
1749}
1750
1751static void nmiReset(void)
1752{
1753 static constexpr const uint8_t value = 1;
1754 const static constexpr int nmiOutPulseTimeMs = 200;
1755
1756 std::cerr << "NMI out action \n";
1757 nmiOutLine.set_value(value);
1758 std::cerr << nmiOutName << " set to " << std::to_string(value) << "\n";
1759 gpioAssertTimer.expires_after(std::chrono::milliseconds(nmiOutPulseTimeMs));
1760 gpioAssertTimer.async_wait([](const boost::system::error_code ec) {
1761 // restore the NMI_OUT GPIO line back to the opposite value
1762 nmiOutLine.set_value(!value);
1763 std::cerr << nmiOutName << " released\n";
1764 if (ec)
1765 {
1766 // operation_aborted is expected if timer is canceled before
1767 // completion.
1768 if (ec != boost::asio::error::operation_aborted)
1769 {
1770 std::cerr << nmiOutName << " async_wait failed: " + ec.message()
1771 << "\n";
1772 }
1773 }
1774 });
1775 // log to redfish
1776 nmiDiagIntLog();
1777 std::cerr << "NMI out action completed\n";
1778 // reset Enable Property
1779 nmiSetEnablePorperty(false);
1780}
1781
1782static void nmiSourcePropertyMonitor(void)
1783{
1784 std::cerr << " NMI Source Property Monitor \n";
1785
1786 static std::unique_ptr<sdbusplus::bus::match::match> nmiSourceMatch =
1787 std::make_unique<sdbusplus::bus::match::match>(
1788 *conn,
1789 "type='signal',interface='org.freedesktop.DBus.Properties',"
1790 "member='PropertiesChanged',arg0namespace='com.intel.Control."
1791 "NMISource'",
1792 [](sdbusplus::message::message& msg) {
1793 std::string interfaceName;
1794 boost::container::flat_map<std::string,
1795 std::variant<bool, std::string>>
1796 propertiesChanged;
1797 std::string state;
1798 bool value = true;
1799 try
1800 {
1801 msg.read(interfaceName, propertiesChanged);
1802 if (propertiesChanged.begin()->first == "Enabled")
1803 {
1804 value =
1805 std::get<bool>(propertiesChanged.begin()->second);
1806 std::cerr
1807 << " NMI Enabled propertiesChanged value: " << value
1808 << "\n";
1809 nmiEnabled = value;
1810 if (nmiEnabled)
1811 {
1812 nmiReset();
1813 }
1814 }
1815 }
1816 catch (std::exception& e)
1817 {
1818 std::cerr << "Unable to read NMI source\n";
1819 return;
1820 }
1821 });
1822}
1823
1824static void setNmiSource()
1825{
1826 conn->async_method_call(
1827 [](boost::system::error_code ec) {
1828 if (ec)
1829 {
1830 std::cerr << "failed to set NMI source\n";
1831 }
1832 },
1833 "xyz.openbmc_project.Settings", "/com/intel/control/NMISource",
1834 "org.freedesktop.DBus.Properties", "Set", "com.intel.Control.NMISource",
1835 "BMCSource",
1836 std::variant<std::string>{
1837 "com.intel.Control.NMISource.BMCSourceSignal.FpBtn"});
1838 // set Enable Property
1839 nmiSetEnablePorperty(true);
1840}
1841
1842static void nmiButtonHandler()
1843{
1844 gpiod::line_event gpioLineEvent = nmiButtonLine.event_read();
1845
1846 if (gpioLineEvent.event_type == gpiod::line_event::FALLING_EDGE)
1847 {
1848 nmiButtonPressLog();
1849 nmiButtonIface->set_property("ButtonPressed", true);
1850 if (nmiButtonMasked)
1851 {
1852 std::cerr << "NMI button press masked\n";
1853 }
1854 else
1855 {
1856 setNmiSource();
1857 }
1858 }
1859 else if (gpioLineEvent.event_type == gpiod::line_event::RISING_EDGE)
1860 {
1861 nmiButtonIface->set_property("ButtonPressed", false);
1862 }
1863 nmiButtonEvent.async_wait(boost::asio::posix::stream_descriptor::wait_read,
1864 [](const boost::system::error_code ec) {
1865 if (ec)
1866 {
1867 std::cerr << "NMI button handler error: "
1868 << ec.message() << "\n";
1869 return;
1870 }
1871 nmiButtonHandler();
1872 });
1873}
1874
1875static void idButtonHandler()
1876{
1877 gpiod::line_event gpioLineEvent = idButtonLine.event_read();
1878
1879 if (gpioLineEvent.event_type == gpiod::line_event::FALLING_EDGE)
1880 {
1881 idButtonIface->set_property("ButtonPressed", true);
1882 }
1883 else if (gpioLineEvent.event_type == gpiod::line_event::RISING_EDGE)
1884 {
1885 idButtonIface->set_property("ButtonPressed", false);
1886 }
1887 idButtonEvent.async_wait(boost::asio::posix::stream_descriptor::wait_read,
1888 [](const boost::system::error_code& ec) {
1889 if (ec)
1890 {
1891 std::cerr << "ID button handler error: "
1892 << ec.message() << "\n";
1893 return;
1894 }
1895 idButtonHandler();
1896 });
1897}
1898
1899static void postCompleteHandler()
1900{
1901 gpiod::line_event gpioLineEvent = postCompleteLine.event_read();
1902
1903 bool postComplete =
1904 gpioLineEvent.event_type == gpiod::line_event::FALLING_EDGE;
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001905 if (postComplete)
1906 {
Jason M. Billse9a9e2d2019-08-08 14:01:19 -07001907 sendPowerControlEvent(Event::postCompleteAssert);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001908 osIface->set_property("OperatingSystemState", std::string("Standby"));
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001909 }
1910 else
1911 {
Jason M. Billse9a9e2d2019-08-08 14:01:19 -07001912 sendPowerControlEvent(Event::postCompleteDeAssert);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001913 osIface->set_property("OperatingSystemState", std::string("Inactive"));
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001914 }
1915 postCompleteEvent.async_wait(
1916 boost::asio::posix::stream_descriptor::wait_read,
1917 [](const boost::system::error_code ec) {
1918 if (ec)
1919 {
1920 std::cerr << "POST complete handler error: " << ec.message()
1921 << "\n";
1922 return;
1923 }
1924 postCompleteHandler();
1925 });
1926}
1927} // namespace power_control
1928
1929int main(int argc, char* argv[])
1930{
1931 std::cerr << "Start Chassis power control service...\n";
1932 power_control::conn =
1933 std::make_shared<sdbusplus::asio::connection>(power_control::io);
1934
1935 // Request all the dbus names
1936 power_control::conn->request_name("xyz.openbmc_project.State.Host");
1937 power_control::conn->request_name("xyz.openbmc_project.State.Chassis");
1938 power_control::conn->request_name(
1939 "xyz.openbmc_project.State.OperatingSystem");
1940 power_control::conn->request_name("xyz.openbmc_project.Chassis.Buttons");
Chen Yugang174ec662019-08-19 19:58:49 +08001941 power_control::conn->request_name("xyz.openbmc_project.Control.Host.NMI");
Jason M. Bills7d4aaac2019-09-19 14:03:44 -07001942 power_control::conn->request_name(
1943 "xyz.openbmc_project.Control.Host.RestartCause");
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001944
1945 // Request PS_PWROK GPIO events
1946 if (!power_control::requestGPIOEvents(
1947 "PS_PWROK", power_control::psPowerOKHandler,
1948 power_control::psPowerOKLine, power_control::psPowerOKEvent))
1949 {
1950 return -1;
1951 }
1952
1953 // Request SIO_POWER_GOOD GPIO events
1954 if (!power_control::requestGPIOEvents(
1955 "SIO_POWER_GOOD", power_control::sioPowerGoodHandler,
1956 power_control::sioPowerGoodLine, power_control::sioPowerGoodEvent))
1957 {
1958 return -1;
1959 }
1960
1961 // Request SIO_ONCONTROL GPIO events
1962 if (!power_control::requestGPIOEvents(
1963 "SIO_ONCONTROL", power_control::sioOnControlHandler,
1964 power_control::sioOnControlLine, power_control::sioOnControlEvent))
1965 {
1966 return -1;
1967 }
1968
1969 // Request SIO_S5 GPIO events
1970 if (!power_control::requestGPIOEvents("SIO_S5", power_control::sioS5Handler,
1971 power_control::sioS5Line,
1972 power_control::sioS5Event))
1973 {
1974 return -1;
1975 }
1976
1977 // Request POWER_BUTTON GPIO events
1978 if (!power_control::requestGPIOEvents(
1979 "POWER_BUTTON", power_control::powerButtonHandler,
1980 power_control::powerButtonLine, power_control::powerButtonEvent))
1981 {
1982 return -1;
1983 }
1984
1985 // Request RESET_BUTTON GPIO events
1986 if (!power_control::requestGPIOEvents(
1987 "RESET_BUTTON", power_control::resetButtonHandler,
1988 power_control::resetButtonLine, power_control::resetButtonEvent))
1989 {
1990 return -1;
1991 }
1992
1993 // Request NMI_BUTTON GPIO events
Vijay Khemka33a532d2019-11-14 16:50:35 -08001994 power_control::requestGPIOEvents(
1995 "NMI_BUTTON", power_control::nmiButtonHandler,
1996 power_control::nmiButtonLine, power_control::nmiButtonEvent);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001997
1998 // Request ID_BUTTON GPIO events
Vijay Khemka33a532d2019-11-14 16:50:35 -08001999 power_control::requestGPIOEvents(
2000 "ID_BUTTON", power_control::idButtonHandler,
2001 power_control::idButtonLine, power_control::idButtonEvent);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002002
2003 // Request POST_COMPLETE GPIO events
2004 if (!power_control::requestGPIOEvents(
2005 "POST_COMPLETE", power_control::postCompleteHandler,
2006 power_control::postCompleteLine, power_control::postCompleteEvent))
2007 {
2008 return -1;
2009 }
2010
2011 // initialize NMI_OUT GPIO.
Vijay Khemka33a532d2019-11-14 16:50:35 -08002012 power_control::setGPIOOutput(power_control::nmiOutName, 0,
2013 power_control::nmiOutLine);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002014
Vijay Khemka0dc7d4c2019-10-22 12:30:17 -07002015 // Initialize POWER_OUT and RESET_OUT GPIO.
2016 gpiod::line line;
2017 if (!power_control::setGPIOOutput(power_control::powerOutName, 1, line))
2018 {
2019 return -1;
2020 }
2021
2022 if (!power_control::setGPIOOutput(power_control::resetOutName, 1, line))
2023 {
2024 return -1;
2025 }
2026
2027 // Release line
2028 line.reset();
2029
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002030 // Initialize the power state
2031 power_control::powerState = power_control::PowerState::off;
2032 // Check power good
2033 if (power_control::psPowerOKLine.get_value() > 0)
2034 {
2035 power_control::powerState = power_control::PowerState::on;
2036 }
2037
2038 // Initialize the power state storage
2039 if (power_control::initializePowerStateStorage() < 0)
2040 {
2041 return -1;
2042 }
2043
2044 // Check if we need to start the Power Restore policy
2045 power_control::powerRestorePolicyCheck();
2046
Vijay Khemka33a532d2019-11-14 16:50:35 -08002047 if (power_control::nmiOutLine)
2048 power_control::nmiSourcePropertyMonitor();
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002049
2050 std::cerr << "Initializing power state. ";
2051 power_control::logStateTransition(power_control::powerState);
2052
2053 // Power Control Service
2054 sdbusplus::asio::object_server hostServer =
2055 sdbusplus::asio::object_server(power_control::conn);
2056
2057 // Power Control Interface
2058 power_control::hostIface = hostServer.add_interface(
2059 "/xyz/openbmc_project/state/host0", "xyz.openbmc_project.State.Host");
2060
2061 power_control::hostIface->register_property(
2062 "RequestedHostTransition",
2063 std::string("xyz.openbmc_project.State.Host.Transition.Off"),
2064 [](const std::string& requested, std::string& resp) {
2065 if (requested == "xyz.openbmc_project.State.Host.Transition.Off")
2066 {
2067 sendPowerControlEvent(
2068 power_control::Event::gracefulPowerOffRequest);
Jason M. Bills7d4aaac2019-09-19 14:03:44 -07002069 addRestartCause(power_control::RestartCause::command);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002070 }
2071 else if (requested ==
2072 "xyz.openbmc_project.State.Host.Transition.On")
2073 {
2074 sendPowerControlEvent(power_control::Event::powerOnRequest);
Jason M. Bills7d4aaac2019-09-19 14:03:44 -07002075 addRestartCause(power_control::RestartCause::command);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002076 }
2077 else if (requested ==
2078 "xyz.openbmc_project.State.Host.Transition.Reboot")
2079 {
2080 sendPowerControlEvent(
2081 power_control::Event::gracefulPowerCycleRequest);
Jason M. Bills7d4aaac2019-09-19 14:03:44 -07002082 addRestartCause(power_control::RestartCause::command);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002083 }
2084 else
2085 {
2086 std::cerr << "Unrecognized host state transition request.\n";
2087 throw std::invalid_argument("Unrecognized Transition Request");
2088 return 0;
2089 }
2090 resp = requested;
2091 return 1;
2092 });
2093 power_control::hostIface->register_property(
2094 "CurrentHostState",
2095 std::string(power_control::getHostState(power_control::powerState)));
2096
2097 power_control::currentHostStateMonitor();
2098
2099 power_control::hostIface->initialize();
2100
2101 // Chassis Control Service
2102 sdbusplus::asio::object_server chassisServer =
2103 sdbusplus::asio::object_server(power_control::conn);
2104
2105 // Chassis Control Interface
2106 power_control::chassisIface =
2107 chassisServer.add_interface("/xyz/openbmc_project/state/chassis0",
2108 "xyz.openbmc_project.State.Chassis");
2109
2110 power_control::chassisIface->register_property(
2111 "RequestedPowerTransition",
2112 std::string("xyz.openbmc_project.State.Chassis.Transition.Off"),
2113 [](const std::string& requested, std::string& resp) {
2114 if (requested == "xyz.openbmc_project.State.Chassis.Transition.Off")
2115 {
2116 sendPowerControlEvent(power_control::Event::powerOffRequest);
Jason M. Bills7d4aaac2019-09-19 14:03:44 -07002117 addRestartCause(power_control::RestartCause::command);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002118 }
2119 else if (requested ==
2120 "xyz.openbmc_project.State.Chassis.Transition.On")
2121 {
2122 sendPowerControlEvent(power_control::Event::powerOnRequest);
Jason M. Bills7d4aaac2019-09-19 14:03:44 -07002123 addRestartCause(power_control::RestartCause::command);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002124 }
2125 else if (requested ==
2126 "xyz.openbmc_project.State.Chassis.Transition.PowerCycle")
2127 {
2128 sendPowerControlEvent(power_control::Event::powerCycleRequest);
Jason M. Bills7d4aaac2019-09-19 14:03:44 -07002129 addRestartCause(power_control::RestartCause::command);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002130 }
2131 else if (requested ==
2132 "xyz.openbmc_project.State.Chassis.Transition.Reset")
2133 {
Jason M. Bills7d4aaac2019-09-19 14:03:44 -07002134 addRestartCause(power_control::RestartCause::command);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002135 sendPowerControlEvent(power_control::Event::resetRequest);
2136 }
2137 else
2138 {
2139 std::cerr << "Unrecognized chassis state transition request.\n";
2140 throw std::invalid_argument("Unrecognized Transition Request");
2141 return 0;
2142 }
2143 resp = requested;
2144 return 1;
2145 });
2146 power_control::chassisIface->register_property(
2147 "CurrentPowerState",
2148 std::string(power_control::getChassisState(power_control::powerState)));
2149 power_control::chassisIface->register_property(
2150 "LastStateChangeTime", power_control::getCurrentTimeMs());
2151
2152 power_control::chassisIface->initialize();
2153
2154 // Buttons Service
2155 sdbusplus::asio::object_server buttonsServer =
2156 sdbusplus::asio::object_server(power_control::conn);
2157
2158 // Power Button Interface
2159 power_control::powerButtonIface = buttonsServer.add_interface(
2160 "/xyz/openbmc_project/chassis/buttons/power",
2161 "xyz.openbmc_project.Chassis.Buttons");
2162
2163 power_control::powerButtonIface->register_property(
2164 "ButtonMasked", false, [](const bool requested, bool& current) {
2165 if (requested)
2166 {
2167 if (power_control::powerButtonMask)
2168 {
2169 return 1;
2170 }
2171 if (!power_control::setGPIOOutput(
Vijay Khemka0dc7d4c2019-10-22 12:30:17 -07002172 power_control::powerOutName, 1,
2173 power_control::powerButtonMask))
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002174 {
2175 throw std::runtime_error("Failed to request GPIO");
2176 return 0;
2177 }
2178 std::cerr << "Power Button Masked.\n";
2179 }
2180 else
2181 {
2182 if (!power_control::powerButtonMask)
2183 {
2184 return 1;
2185 }
2186 std::cerr << "Power Button Un-masked\n";
2187 power_control::powerButtonMask.reset();
2188 }
2189 // Update the mask setting
2190 current = requested;
2191 return 1;
2192 });
2193
2194 // Check power button state
2195 bool powerButtonPressed = power_control::powerButtonLine.get_value() == 0;
2196 power_control::powerButtonIface->register_property("ButtonPressed",
2197 powerButtonPressed);
2198
2199 power_control::powerButtonIface->initialize();
2200
2201 // Reset Button Interface
2202 power_control::resetButtonIface = buttonsServer.add_interface(
2203 "/xyz/openbmc_project/chassis/buttons/reset",
2204 "xyz.openbmc_project.Chassis.Buttons");
2205
2206 power_control::resetButtonIface->register_property(
2207 "ButtonMasked", false, [](const bool requested, bool& current) {
2208 if (requested)
2209 {
2210 if (power_control::resetButtonMask)
2211 {
2212 return 1;
2213 }
2214 if (!power_control::setGPIOOutput(
Vijay Khemka0dc7d4c2019-10-22 12:30:17 -07002215 power_control::resetOutName, 1,
2216 power_control::resetButtonMask))
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002217 {
2218 throw std::runtime_error("Failed to request GPIO");
2219 return 0;
2220 }
2221 std::cerr << "Reset Button Masked.\n";
2222 }
2223 else
2224 {
2225 if (!power_control::resetButtonMask)
2226 {
2227 return 1;
2228 }
2229 std::cerr << "Reset Button Un-masked\n";
2230 power_control::resetButtonMask.reset();
2231 }
2232 // Update the mask setting
2233 current = requested;
2234 return 1;
2235 });
2236
2237 // Check reset button state
2238 bool resetButtonPressed = power_control::resetButtonLine.get_value() == 0;
2239 power_control::resetButtonIface->register_property("ButtonPressed",
2240 resetButtonPressed);
2241
2242 power_control::resetButtonIface->initialize();
2243
Vijay Khemka33a532d2019-11-14 16:50:35 -08002244 if (power_control::nmiButtonLine)
2245 {
2246 // NMI Button Interface
2247 power_control::nmiButtonIface = buttonsServer.add_interface(
2248 "/xyz/openbmc_project/chassis/buttons/nmi",
2249 "xyz.openbmc_project.Chassis.Buttons");
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002250
Vijay Khemka33a532d2019-11-14 16:50:35 -08002251 power_control::nmiButtonIface->register_property(
2252 "ButtonMasked", false, [](const bool requested, bool& current) {
2253 if (power_control::nmiButtonMasked == requested)
2254 {
2255 // NMI button mask is already set as requested, so no change
2256 return 1;
2257 }
2258 if (requested)
2259 {
2260 std::cerr << "NMI Button Masked.\n";
2261 power_control::nmiButtonMasked = true;
2262 }
2263 else
2264 {
2265 std::cerr << "NMI Button Un-masked.\n";
2266 power_control::nmiButtonMasked = false;
2267 }
2268 // Update the mask setting
2269 current = power_control::nmiButtonMasked;
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002270 return 1;
Vijay Khemka33a532d2019-11-14 16:50:35 -08002271 });
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002272
Vijay Khemka33a532d2019-11-14 16:50:35 -08002273 // Check NMI button state
2274 bool nmiButtonPressed = power_control::nmiButtonLine.get_value() == 0;
2275 power_control::nmiButtonIface->register_property("ButtonPressed",
2276 nmiButtonPressed);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002277
Vijay Khemka33a532d2019-11-14 16:50:35 -08002278 power_control::nmiButtonIface->initialize();
2279 }
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002280
Vijay Khemka33a532d2019-11-14 16:50:35 -08002281 if (power_control::nmiOutLine)
2282 {
2283 // NMI out Service
2284 sdbusplus::asio::object_server nmiOutServer =
2285 sdbusplus::asio::object_server(power_control::conn);
Chen Yugang174ec662019-08-19 19:58:49 +08002286
Vijay Khemka33a532d2019-11-14 16:50:35 -08002287 // NMI out Interface
2288 power_control::nmiOutIface =
2289 nmiOutServer.add_interface("/xyz/openbmc_project/control/host0/nmi",
2290 "xyz.openbmc_project.Control.Host.NMI");
2291 power_control::nmiOutIface->register_method("NMI",
2292 power_control::nmiReset);
2293 power_control::nmiOutIface->initialize();
2294 }
Chen Yugang174ec662019-08-19 19:58:49 +08002295
Vijay Khemka33a532d2019-11-14 16:50:35 -08002296 if (power_control::idButtonLine)
2297 {
2298 // ID Button Interface
2299 power_control::idButtonIface = buttonsServer.add_interface(
2300 "/xyz/openbmc_project/chassis/buttons/id",
2301 "xyz.openbmc_project.Chassis.Buttons");
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002302
Vijay Khemka33a532d2019-11-14 16:50:35 -08002303 // Check ID button state
2304 bool idButtonPressed = power_control::idButtonLine.get_value() == 0;
2305 power_control::idButtonIface->register_property("ButtonPressed",
2306 idButtonPressed);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002307
Vijay Khemka33a532d2019-11-14 16:50:35 -08002308 power_control::idButtonIface->initialize();
2309 }
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002310
Chen Yugang605e9c72019-10-10 14:36:44 +08002311 // set Chassis CapabilitiesFlags Property with 0x06: front panel(0x2) and
2312 // NMI out(0x4)
2313 power_control::conn->async_method_call(
2314 [](boost::system::error_code ec) {
2315 if (ec)
2316 {
2317 std::cerr << "failed to set Chassis CapabilitiesFlags\n";
2318 }
2319 },
2320 "xyz.openbmc_project.Settings",
2321 "/xyz/openbmc_project/control/chassis_capabilities_config",
2322 "org.freedesktop.DBus.Properties", "Set",
2323 "xyz.openbmc_project.Control.ChassisCapabilities", "CapabilitiesFlags",
2324 std::variant<uint8_t>(0x06));
2325
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002326 // OS State Service
2327 sdbusplus::asio::object_server osServer =
2328 sdbusplus::asio::object_server(power_control::conn);
2329
2330 // OS State Interface
2331 power_control::osIface = osServer.add_interface(
2332 "/xyz/openbmc_project/state/os",
2333 "xyz.openbmc_project.State.OperatingSystem.Status");
2334
2335 // Get the initial OS state based on POST complete
2336 // 0: Asserted, OS state is "Standby" (ready to boot)
2337 // 1: De-Asserted, OS state is "Inactive"
2338 std::string osState = power_control::postCompleteLine.get_value() > 0
2339 ? "Inactive"
2340 : "Standby";
2341
2342 power_control::osIface->register_property("OperatingSystemState",
2343 std::string(osState));
2344
2345 power_control::osIface->initialize();
2346
Jason M. Bills7d4aaac2019-09-19 14:03:44 -07002347 // Restart Cause Service
2348 sdbusplus::asio::object_server restartCauseServer =
2349 sdbusplus::asio::object_server(power_control::conn);
2350
2351 // Restart Cause Interface
2352 power_control::restartCauseIface = restartCauseServer.add_interface(
2353 "/xyz/openbmc_project/control/host0/restart_cause",
2354 "xyz.openbmc_project.Control.Host.RestartCause");
2355
2356 power_control::restartCauseIface->register_property(
2357 "RestartCause",
2358 std::string("xyz.openbmc_project.State.Host.RestartCause.Unknown"));
2359
2360 power_control::restartCauseIface->register_property(
2361 "RequestedRestartCause",
2362 std::string("xyz.openbmc_project.State.Host.RestartCause.Unknown"),
2363 [](const std::string& requested, std::string& resp) {
2364 if (requested ==
2365 "xyz.openbmc_project.State.Host.RestartCause.WatchdogTimer")
2366 {
2367 power_control::addRestartCause(
2368 power_control::RestartCause::watchdog);
2369 }
2370 else
2371 {
2372 throw std::invalid_argument(
2373 "Unrecognized RestartCause Request");
2374 return 0;
2375 }
2376
2377 std::cerr << "RestartCause requested: " << requested << "\n";
2378 resp = requested;
2379 return 1;
2380 });
2381
2382 power_control::restartCauseIface->initialize();
2383
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002384 power_control::io.run();
2385
2386 return 0;
2387}