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