blob: ba33124ff82a5a7bafbd72ac00cc711590aa91aa [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 }
Jason M. Bills94ce8eb2019-09-30 10:13:25 -0700629 // We're done with the previous power state for the restore policy, so store
630 // the current state
631 savePowerState(powerState);
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700632}
633
634static void powerRestorePolicyDelay(int delay)
635{
636 // Async events may call this twice, but we only want to run once
637 static bool delayStarted = false;
638 if (delayStarted)
639 {
640 return;
641 }
642 delayStarted = true;
643 // Calculate the delay from now to meet the requested delay
644 // Subtract the approximate uboot time
645 static constexpr const int ubootSeconds = 20;
646 delay -= ubootSeconds;
647 // Subtract the time since boot
648 struct sysinfo info = {};
649 if (sysinfo(&info) == 0)
650 {
651 delay -= info.uptime;
652 }
653 // 0 is the minimum delay
654 delay = std::max(delay, 0);
655
656 static boost::asio::steady_timer powerRestorePolicyTimer(io);
657 powerRestorePolicyTimer.expires_after(std::chrono::seconds(delay));
658 std::cerr << "Power restore delay of " << delay << " seconds started\n";
659 powerRestorePolicyTimer.async_wait([](const boost::system::error_code ec) {
660 if (ec)
661 {
662 // operation_aborted is expected if timer is canceled before
663 // completion.
664 if (ec != boost::asio::error::operation_aborted)
665 {
666 std::cerr << "power restore policy async_wait failed: "
667 << ec.message() << "\n";
668 }
669 return;
670 }
671 // Get Power Restore Policy
672 // In case PowerRestorePolicy is not available, set a match for it
673 static std::unique_ptr<sdbusplus::bus::match::match>
674 powerRestorePolicyMatch = std::make_unique<
675 sdbusplus::bus::match::match>(
676 *conn,
677 "type='signal',interface='org.freedesktop.DBus.Properties',"
678 "member='PropertiesChanged',arg0namespace='xyz.openbmc_"
679 "project.Control.Power.RestorePolicy'",
680 [](sdbusplus::message::message& msg) {
681 std::string interfaceName;
682 boost::container::flat_map<std::string,
683 std::variant<std::string>>
684 propertiesChanged;
685 std::string policy;
686 try
687 {
688 msg.read(interfaceName, propertiesChanged);
689 policy = std::get<std::string>(
690 propertiesChanged.begin()->second);
691 }
692 catch (std::exception& e)
693 {
694 std::cerr
695 << "Unable to read power restore policy value\n";
696 powerRestorePolicyMatch.reset();
697 return;
698 }
699 invokePowerRestorePolicy(policy);
700 powerRestorePolicyMatch.reset();
701 });
702
703 // Check if it's already on DBus
704 conn->async_method_call(
705 [](boost::system::error_code ec,
706 const std::variant<std::string>& policyProperty) {
707 if (ec)
708 {
709 return;
710 }
711 powerRestorePolicyMatch.reset();
712 const std::string* policy =
713 std::get_if<std::string>(&policyProperty);
714 if (policy == nullptr)
715 {
716 std::cerr << "Unable to read power restore policy value\n";
717 return;
718 }
719 invokePowerRestorePolicy(*policy);
720 },
721 "xyz.openbmc_project.Settings",
722 "/xyz/openbmc_project/control/host0/power_restore_policy",
723 "org.freedesktop.DBus.Properties", "Get",
724 "xyz.openbmc_project.Control.Power.RestorePolicy",
725 "PowerRestorePolicy");
726 });
727}
728
729static void powerRestorePolicyStart()
730{
731 std::cerr << "Power restore policy started\n";
732 powerRestorePolicyLog();
733
734 // Get the desired delay time
735 // In case PowerRestoreDelay is not available, set a match for it
736 static std::unique_ptr<sdbusplus::bus::match::match>
737 powerRestoreDelayMatch = std::make_unique<sdbusplus::bus::match::match>(
738 *conn,
739 "type='signal',interface='org.freedesktop.DBus.Properties',member='"
740 "PropertiesChanged',arg0namespace='xyz.openbmc_project.Control."
741 "Power.RestoreDelay'",
742 [](sdbusplus::message::message& msg) {
743 std::string interfaceName;
744 boost::container::flat_map<std::string, std::variant<uint16_t>>
745 propertiesChanged;
746 int delay = 0;
747 try
748 {
749 msg.read(interfaceName, propertiesChanged);
750 delay =
751 std::get<uint16_t>(propertiesChanged.begin()->second);
752 }
753 catch (std::exception& e)
754 {
755 std::cerr << "Unable to read power restore delay value\n";
756 powerRestoreDelayMatch.reset();
757 return;
758 }
759 powerRestorePolicyDelay(delay);
760 powerRestoreDelayMatch.reset();
761 });
762
763 // Check if it's already on DBus
764 conn->async_method_call(
765 [](boost::system::error_code ec,
766 const std::variant<uint16_t>& delayProperty) {
767 if (ec)
768 {
769 return;
770 }
771 powerRestoreDelayMatch.reset();
772 const uint16_t* delay = std::get_if<uint16_t>(&delayProperty);
773 if (delay == nullptr)
774 {
775 std::cerr << "Unable to read power restore delay value\n";
776 return;
777 }
778 powerRestorePolicyDelay(*delay);
779 },
780 "xyz.openbmc_project.Settings",
781 "/xyz/openbmc_project/control/power_restore_delay",
782 "org.freedesktop.DBus.Properties", "Get",
783 "xyz.openbmc_project.Control.Power.RestoreDelay", "PowerRestoreDelay");
784}
785
786static void powerRestorePolicyCheck()
787{
788 // In case ACBoot is not available, set a match for it
789 static std::unique_ptr<sdbusplus::bus::match::match> acBootMatch =
790 std::make_unique<sdbusplus::bus::match::match>(
791 *conn,
792 "type='signal',interface='org.freedesktop.DBus.Properties',member='"
793 "PropertiesChanged',arg0namespace='xyz.openbmc_project.Common."
794 "ACBoot'",
795 [](sdbusplus::message::message& msg) {
796 std::string interfaceName;
797 boost::container::flat_map<std::string,
798 std::variant<std::string>>
799 propertiesChanged;
800 std::string acBoot;
801 try
802 {
803 msg.read(interfaceName, propertiesChanged);
804 acBoot = std::get<std::string>(
805 propertiesChanged.begin()->second);
806 }
807 catch (std::exception& e)
808 {
809 std::cerr << "Unable to read AC Boot status\n";
810 acBootMatch.reset();
811 return;
812 }
813 if (acBoot == "Unknown")
814 {
815 return;
816 }
817 if (acBoot == "True")
818 {
819 // Start the Power Restore policy
820 powerRestorePolicyStart();
821 }
822 acBootMatch.reset();
823 });
824
825 // Check if it's already on DBus
826 conn->async_method_call(
827 [](boost::system::error_code ec,
828 const std::variant<std::string>& acBootProperty) {
829 if (ec)
830 {
831 return;
832 }
833 const std::string* acBoot =
834 std::get_if<std::string>(&acBootProperty);
835 if (acBoot == nullptr)
836 {
837 std::cerr << "Unable to read AC Boot status\n";
838 return;
839 }
840 if (*acBoot == "Unknown")
841 {
842 return;
843 }
844 if (*acBoot == "True")
845 {
846 // Start the Power Restore policy
847 powerRestorePolicyStart();
848 }
849 acBootMatch.reset();
850 },
851 "xyz.openbmc_project.Settings",
852 "/xyz/openbmc_project/control/host0/ac_boot",
853 "org.freedesktop.DBus.Properties", "Get",
854 "xyz.openbmc_project.Common.ACBoot", "ACBoot");
855}
856
857static bool requestGPIOEvents(
858 const std::string& name, const std::function<void()>& handler,
859 gpiod::line& gpioLine,
860 boost::asio::posix::stream_descriptor& gpioEventDescriptor)
861{
862 // Find the GPIO line
863 gpioLine = gpiod::find_line(name);
864 if (!gpioLine)
865 {
866 std::cerr << "Failed to find the " << name << " line\n";
867 return false;
868 }
869
870 try
871 {
872 gpioLine.request(
873 {"power-control", gpiod::line_request::EVENT_BOTH_EDGES});
874 }
875 catch (std::exception&)
876 {
877 std::cerr << "Failed to request events for " << name << "\n";
878 return false;
879 }
880
881 int gpioLineFd = gpioLine.event_get_fd();
882 if (gpioLineFd < 0)
883 {
884 std::cerr << "Failed to get " << name << " fd\n";
885 return false;
886 }
887
888 gpioEventDescriptor.assign(gpioLineFd);
889
890 gpioEventDescriptor.async_wait(
891 boost::asio::posix::stream_descriptor::wait_read,
892 [&name, handler](const boost::system::error_code ec) {
893 if (ec)
894 {
895 std::cerr << name << " fd handler error: " << ec.message()
896 << "\n";
897 // TODO: throw here to force power-control to restart?
898 return;
899 }
900 handler();
901 });
902 return true;
903}
904
905static bool setGPIOOutput(const std::string& name, const int value,
906 gpiod::line& gpioLine)
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 // Request GPIO output to specified value
917 try
918 {
919 gpioLine.request({__FUNCTION__, gpiod::line_request::DIRECTION_OUTPUT},
920 value);
921 }
922 catch (std::exception&)
923 {
924 std::cerr << "Failed to request " << name << " output\n";
925 return false;
926 }
927
928 std::cerr << name << " set to " << std::to_string(value) << "\n";
929 return true;
930}
931
932static int setMaskedGPIOOutputForMs(gpiod::line& maskedGPIOLine,
933 const std::string& name, const int value,
934 const int durationMs)
935{
936 // Set the masked GPIO line to the specified value
937 maskedGPIOLine.set_value(value);
938 std::cerr << name << " set to " << std::to_string(value) << "\n";
939 gpioAssertTimer.expires_after(std::chrono::milliseconds(durationMs));
940 gpioAssertTimer.async_wait(
941 [maskedGPIOLine, value, name](const boost::system::error_code ec) {
942 // Set the masked GPIO line back to the opposite value
943 maskedGPIOLine.set_value(!value);
944 std::cerr << name << " released\n";
945 if (ec)
946 {
947 // operation_aborted is expected if timer is canceled before
948 // completion.
949 if (ec != boost::asio::error::operation_aborted)
950 {
951 std::cerr << name << " async_wait failed: " + ec.message()
952 << "\n";
953 }
954 }
955 });
956 return 0;
957}
958
959static int setGPIOOutputForMs(const std::string& name, const int value,
960 const int durationMs)
961{
962 // If the requested GPIO is masked, use the mask line to set the output
Vijay Khemka0dc7d4c2019-10-22 12:30:17 -0700963 if (powerButtonMask && name == power_control::powerOutName)
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700964 {
965 return setMaskedGPIOOutputForMs(powerButtonMask, name, value,
966 durationMs);
967 }
Vijay Khemka0dc7d4c2019-10-22 12:30:17 -0700968 if (resetButtonMask && name == power_control::resetOutName)
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700969 {
970 return setMaskedGPIOOutputForMs(resetButtonMask, name, value,
971 durationMs);
972 }
973
974 // No mask set, so request and set the GPIO normally
975 gpiod::line gpioLine;
976 if (!setGPIOOutput(name, value, gpioLine))
977 {
978 return -1;
979 }
980 gpioAssertTimer.expires_after(std::chrono::milliseconds(durationMs));
981 gpioAssertTimer.async_wait(
Vijay Khemka0dc7d4c2019-10-22 12:30:17 -0700982 [gpioLine, value, name](const boost::system::error_code ec) {
983 // Set the GPIO line back to the opposite value
984 gpioLine.set_value(!value);
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700985 std::cerr << name << " released\n";
986 if (ec)
987 {
988 // operation_aborted is expected if timer is canceled before
989 // completion.
990 if (ec != boost::asio::error::operation_aborted)
991 {
992 std::cerr << name << " async_wait failed: " << ec.message()
993 << "\n";
994 }
995 }
996 });
997 return 0;
998}
999
1000static void powerOn()
1001{
Vijay Khemka0dc7d4c2019-10-22 12:30:17 -07001002 setGPIOOutputForMs(power_control::powerOutName, 0, powerPulseTimeMs);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001003}
1004
1005static void gracefulPowerOff()
1006{
Vijay Khemka0dc7d4c2019-10-22 12:30:17 -07001007 setGPIOOutputForMs(power_control::powerOutName, 0, powerPulseTimeMs);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001008}
1009
1010static void forcePowerOff()
1011{
Vijay Khemka0dc7d4c2019-10-22 12:30:17 -07001012 if (setGPIOOutputForMs(power_control::powerOutName, 0,
1013 forceOffPulseTimeMs) < 0)
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001014 {
1015 return;
1016 }
1017
1018 // If the force off timer expires, then the PCH power-button override
1019 // failed, so attempt the Unconditional Powerdown SMBus command.
1020 gpioAssertTimer.async_wait([](const boost::system::error_code ec) {
1021 if (ec)
1022 {
1023 // operation_aborted is expected if timer is canceled before
1024 // completion.
1025 if (ec != boost::asio::error::operation_aborted)
1026 {
1027 std::cerr << "Force power off async_wait failed: "
1028 << ec.message() << "\n";
1029 }
1030 return;
1031 }
1032 std::cerr << "PCH Power-button override failed. Issuing Unconditional "
1033 "Powerdown SMBus command.\n";
1034 const static constexpr size_t pchDevBusAddress = 3;
1035 const static constexpr size_t pchDevSlaveAddress = 0x44;
1036 const static constexpr size_t pchCmdReg = 0;
1037 const static constexpr size_t pchPowerDownCmd = 0x02;
1038 if (i2cSet(pchDevBusAddress, pchDevSlaveAddress, pchCmdReg,
1039 pchPowerDownCmd) < 0)
1040 {
1041 std::cerr << "Unconditional Powerdown command failed! Not sure "
1042 "what to do now.\n";
1043 }
1044 });
1045}
1046
1047static void reset()
1048{
Vijay Khemka0dc7d4c2019-10-22 12:30:17 -07001049 setGPIOOutputForMs(power_control::resetOutName, 0, resetPulseTimeMs);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001050}
1051
1052static void gracefulPowerOffTimerStart()
1053{
1054 std::cerr << "Graceful power-off timer started\n";
1055 gracefulPowerOffTimer.expires_after(
1056 std::chrono::milliseconds(gracefulPowerOffTimeMs));
1057 gracefulPowerOffTimer.async_wait([](const boost::system::error_code ec) {
1058 if (ec)
1059 {
1060 // operation_aborted is expected if timer is canceled before
1061 // completion.
1062 if (ec != boost::asio::error::operation_aborted)
1063 {
1064 std::cerr << "Graceful power-off async_wait failed: "
1065 << ec.message() << "\n";
1066 }
1067 std::cerr << "Graceful power-off timer canceled\n";
1068 return;
1069 }
1070 std::cerr << "Graceful power-off timer completed\n";
1071 sendPowerControlEvent(Event::gracefulPowerOffTimerExpired);
1072 });
1073}
1074
1075static void powerCycleTimerStart()
1076{
1077 std::cerr << "Power-cycle timer started\n";
1078 powerCycleTimer.expires_after(std::chrono::milliseconds(powerCycleTimeMs));
1079 powerCycleTimer.async_wait([](const boost::system::error_code ec) {
1080 if (ec)
1081 {
1082 // operation_aborted is expected if timer is canceled before
1083 // completion.
1084 if (ec != boost::asio::error::operation_aborted)
1085 {
1086 std::cerr << "Power-cycle async_wait failed: " << ec.message()
1087 << "\n";
1088 }
1089 std::cerr << "Power-cycle timer canceled\n";
1090 return;
1091 }
1092 std::cerr << "Power-cycle timer completed\n";
1093 sendPowerControlEvent(Event::powerCycleTimerExpired);
1094 });
1095}
1096
1097static void psPowerOKWatchdogTimerStart()
1098{
1099 std::cerr << "power supply power OK watchdog timer started\n";
1100 psPowerOKWatchdogTimer.expires_after(
1101 std::chrono::milliseconds(psPowerOKWatchdogTimeMs));
1102 psPowerOKWatchdogTimer.async_wait(
1103 [](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
1111 << "power supply power OK watchdog async_wait failed: "
1112 << ec.message() << "\n";
1113 }
1114 std::cerr << "power supply power OK watchdog timer canceled\n";
1115 return;
1116 }
1117 std::cerr << "power supply power OK watchdog timer expired\n";
1118 sendPowerControlEvent(Event::psPowerOKWatchdogTimerExpired);
1119 });
1120}
1121
Jason M. Billse9a9e2d2019-08-08 14:01:19 -07001122static void warmResetCheckTimerStart()
1123{
1124 std::cerr << "Warm reset check timer started\n";
1125 warmResetCheckTimer.expires_after(
1126 std::chrono::milliseconds(warmResetCheckTimeMs));
1127 warmResetCheckTimer.async_wait([](const boost::system::error_code ec) {
1128 if (ec)
1129 {
1130 // operation_aborted is expected if timer is canceled before
1131 // completion.
1132 if (ec != boost::asio::error::operation_aborted)
1133 {
1134 std::cerr << "Warm reset check async_wait failed: "
1135 << ec.message() << "\n";
1136 }
1137 std::cerr << "Warm reset check timer canceled\n";
1138 return;
1139 }
1140 std::cerr << "Warm reset check timer completed\n";
1141 sendPowerControlEvent(Event::warmResetDetected);
1142 });
1143}
1144
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001145static void pohCounterTimerStart()
1146{
1147 std::cerr << "POH timer started\n";
1148 // Set the time-out as 1 hour, to align with POH command in ipmid
1149 pohCounterTimer.expires_after(std::chrono::hours(1));
1150 pohCounterTimer.async_wait([](const boost::system::error_code& ec) {
1151 if (ec)
1152 {
1153 // operation_aborted is expected if timer is canceled before
1154 // completion.
1155 if (ec != boost::asio::error::operation_aborted)
1156 {
1157 std::cerr << "POH timer async_wait failed: " << ec.message()
1158 << "\n";
1159 }
1160 std::cerr << "POH timer canceled\n";
1161 return;
1162 }
1163
1164 if (getHostState(powerState) !=
1165 "xyz.openbmc_project.State.Host.HostState.Running")
1166 {
1167 return;
1168 }
1169
1170 conn->async_method_call(
1171 [](boost::system::error_code ec,
1172 const std::variant<uint32_t>& pohCounterProperty) {
1173 if (ec)
1174 {
1175 std::cerr << "error to get poh counter\n";
1176 return;
1177 }
1178 const uint32_t* pohCounter =
1179 std::get_if<uint32_t>(&pohCounterProperty);
1180 if (pohCounter == nullptr)
1181 {
1182 std::cerr << "unable to read poh counter\n";
1183 return;
1184 }
1185
1186 conn->async_method_call(
1187 [](boost::system::error_code ec) {
1188 if (ec)
1189 {
1190 std::cerr << "failed to set poh counter\n";
1191 }
1192 },
1193 "xyz.openbmc_project.Settings",
1194 "/xyz/openbmc_project/state/chassis0",
1195 "org.freedesktop.DBus.Properties", "Set",
1196 "xyz.openbmc_project.State.PowerOnHours", "POHCounter",
1197 std::variant<uint32_t>(*pohCounter + 1));
1198 },
1199 "xyz.openbmc_project.Settings",
1200 "/xyz/openbmc_project/state/chassis0",
1201 "org.freedesktop.DBus.Properties", "Get",
1202 "xyz.openbmc_project.State.PowerOnHours", "POHCounter");
1203
1204 pohCounterTimerStart();
1205 });
1206}
1207
1208static void currentHostStateMonitor()
1209{
1210 static auto match = sdbusplus::bus::match::match(
1211 *conn,
1212 "type='signal',member='PropertiesChanged', "
1213 "interface='org.freedesktop.DBus.Properties', "
1214 "arg0namespace='xyz.openbmc_project.State.Host'",
1215 [](sdbusplus::message::message& message) {
1216 std::string intfName;
1217 std::map<std::string, std::variant<std::string>> properties;
1218
1219 message.read(intfName, properties);
1220
1221 std::variant<std::string> currentHostState;
1222
1223 try
1224 {
1225 currentHostState = properties.at("CurrentHostState");
1226 }
1227 catch (const std::out_of_range& e)
1228 {
1229 std::cerr << "Error in finding CurrentHostState property\n";
1230
1231 return;
1232 }
1233
1234 if (std::get<std::string>(currentHostState) ==
1235 "xyz.openbmc_project.State.Host.HostState.Running")
1236 {
1237 pohCounterTimerStart();
1238 }
1239 else
1240 {
1241 pohCounterTimer.cancel();
1242 }
1243 });
1244}
1245
1246static void sioPowerGoodWatchdogTimerStart()
1247{
1248 std::cerr << "SIO power good watchdog timer started\n";
1249 sioPowerGoodWatchdogTimer.expires_after(
1250 std::chrono::milliseconds(sioPowerGoodWatchdogTimeMs));
1251 sioPowerGoodWatchdogTimer.async_wait(
1252 [](const boost::system::error_code ec) {
1253 if (ec)
1254 {
1255 // operation_aborted is expected if timer is canceled before
1256 // completion.
1257 if (ec != boost::asio::error::operation_aborted)
1258 {
1259 std::cerr << "SIO power good watchdog async_wait failed: "
1260 << ec.message() << "\n";
1261 }
1262 std::cerr << "SIO power good watchdog timer canceled\n";
1263 return;
1264 }
1265 std::cerr << "SIO power good watchdog timer completed\n";
1266 sendPowerControlEvent(Event::sioPowerGoodWatchdogTimerExpired);
1267 });
1268}
1269
1270static void powerStateOn(const Event event)
1271{
1272 logEvent(__FUNCTION__, event);
1273 switch (event)
1274 {
1275 case Event::psPowerOKDeAssert:
1276 setPowerState(PowerState::off);
1277 // DC power is unexpectedly lost, beep
1278 beep(beepPowerFail);
1279 break;
1280 case Event::sioS5Assert:
1281 setPowerState(PowerState::transitionToOff);
1282 setRestartCause(RestartCause::softReset);
1283 break;
Jason M. Billse9a9e2d2019-08-08 14:01:19 -07001284 case Event::postCompleteDeAssert:
1285 setPowerState(PowerState::checkForWarmReset);
1286 setRestartCause(RestartCause::softReset);
1287 warmResetCheckTimerStart();
1288 break;
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001289 case Event::powerButtonPressed:
1290 setPowerState(PowerState::gracefulTransitionToOff);
1291 gracefulPowerOffTimerStart();
1292 break;
Jason M. Billse9a9e2d2019-08-08 14:01:19 -07001293 case Event::resetButtonPressed:
1294 setPowerState(PowerState::checkForWarmReset);
1295 warmResetCheckTimerStart();
1296 break;
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001297 case Event::powerOffRequest:
1298 setPowerState(PowerState::transitionToOff);
1299 forcePowerOff();
1300 break;
1301 case Event::gracefulPowerOffRequest:
1302 setPowerState(PowerState::gracefulTransitionToOff);
1303 gracefulPowerOffTimerStart();
1304 gracefulPowerOff();
1305 break;
1306 case Event::powerCycleRequest:
1307 setPowerState(PowerState::transitionToCycleOff);
1308 forcePowerOff();
1309 break;
1310 case Event::gracefulPowerCycleRequest:
1311 setPowerState(PowerState::gracefulTransitionToCycleOff);
1312 gracefulPowerOffTimerStart();
1313 gracefulPowerOff();
1314 break;
1315 case Event::resetRequest:
1316 reset();
1317 break;
1318 default:
1319 std::cerr << "No action taken.\n";
1320 break;
1321 }
1322}
1323
1324static void powerStateWaitForPSPowerOK(const Event event)
1325{
1326 logEvent(__FUNCTION__, event);
1327 switch (event)
1328 {
1329 case Event::psPowerOKAssert:
1330 // Cancel any GPIO assertions held during the transition
1331 gpioAssertTimer.cancel();
1332 psPowerOKWatchdogTimer.cancel();
1333 sioPowerGoodWatchdogTimerStart();
1334 setPowerState(PowerState::waitForSIOPowerGood);
1335 break;
1336 case Event::psPowerOKWatchdogTimerExpired:
1337 setPowerState(PowerState::failedTransitionToOn);
Jason M. Bills6c2ad362019-08-01 16:53:35 -07001338 psPowerOKFailedLog();
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001339 break;
Vijay Khemka0eef6b62019-10-22 12:22:52 -07001340 case Event::sioPowerGoodAssert:
1341 psPowerOKWatchdogTimer.cancel();
1342 setPowerState(PowerState::on);
1343 break;
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001344 default:
1345 std::cerr << "No action taken.\n";
1346 break;
1347 }
1348}
1349
1350static void powerStateWaitForSIOPowerGood(const Event event)
1351{
1352 logEvent(__FUNCTION__, event);
1353 switch (event)
1354 {
1355 case Event::sioPowerGoodAssert:
1356 sioPowerGoodWatchdogTimer.cancel();
1357 setPowerState(PowerState::on);
1358 break;
1359 case Event::sioPowerGoodWatchdogTimerExpired:
1360 setPowerState(PowerState::failedTransitionToOn);
Jason M. Bills6c2ad362019-08-01 16:53:35 -07001361 systemPowerGoodFailedLog();
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001362 forcePowerOff();
1363 break;
1364 default:
1365 std::cerr << "No action taken.\n";
1366 break;
1367 }
1368}
1369
1370static void powerStateFailedTransitionToOn(const Event event)
1371{
1372 logEvent(__FUNCTION__, event);
1373 switch (event)
1374 {
1375 case Event::psPowerOKAssert:
1376 // We're in a failure state, so don't allow the system to turn on
1377 // without a user request
1378 forcePowerOff();
1379 break;
1380 case Event::psPowerOKDeAssert:
1381 // Cancel any GPIO assertions held during the transition
1382 gpioAssertTimer.cancel();
1383 break;
1384 case Event::powerButtonPressed:
1385 psPowerOKWatchdogTimerStart();
1386 setPowerState(PowerState::waitForPSPowerOK);
1387 break;
1388 case Event::powerOnRequest:
1389 psPowerOKWatchdogTimerStart();
1390 setPowerState(PowerState::waitForPSPowerOK);
1391 powerOn();
1392 break;
1393 default:
1394 std::cerr << "No action taken.\n";
1395 break;
1396 }
1397}
1398
1399static void powerStateOff(const Event event)
1400{
1401 logEvent(__FUNCTION__, event);
1402 switch (event)
1403 {
1404 case Event::psPowerOKAssert:
1405 setPowerState(PowerState::waitForSIOPowerGood);
1406 break;
1407 case Event::sioS5DeAssert:
1408 setPowerState(PowerState::waitForPSPowerOK);
1409 break;
1410 case Event::powerButtonPressed:
1411 psPowerOKWatchdogTimerStart();
1412 setPowerState(PowerState::waitForPSPowerOK);
1413 break;
1414 case Event::powerOnRequest:
1415 psPowerOKWatchdogTimerStart();
1416 setPowerState(PowerState::waitForPSPowerOK);
1417 powerOn();
1418 break;
1419 default:
1420 std::cerr << "No action taken.\n";
1421 break;
1422 }
1423}
1424
1425static void powerStateTransitionToOff(const Event event)
1426{
1427 logEvent(__FUNCTION__, event);
1428 switch (event)
1429 {
1430 case Event::psPowerOKDeAssert:
1431 // Cancel any GPIO assertions held during the transition
1432 gpioAssertTimer.cancel();
1433 setPowerState(PowerState::off);
1434 break;
1435 default:
1436 std::cerr << "No action taken.\n";
1437 break;
1438 }
1439}
1440
1441static void powerStateGracefulTransitionToOff(const Event event)
1442{
1443 logEvent(__FUNCTION__, event);
1444 switch (event)
1445 {
1446 case Event::psPowerOKDeAssert:
1447 gracefulPowerOffTimer.cancel();
1448 setPowerState(PowerState::off);
1449 break;
1450 case Event::gracefulPowerOffTimerExpired:
1451 setPowerState(PowerState::on);
1452 break;
1453 default:
1454 std::cerr << "No action taken.\n";
1455 break;
1456 }
1457}
1458
1459static void powerStateCycleOff(const Event event)
1460{
1461 logEvent(__FUNCTION__, event);
1462 switch (event)
1463 {
1464 case Event::powerCycleTimerExpired:
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 powerStateTransitionToCycleOff(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::cycleOff);
1484 powerCycleTimerStart();
1485 break;
1486 default:
1487 std::cerr << "No action taken.\n";
1488 break;
1489 }
1490}
1491
1492static void powerStateGracefulTransitionToCycleOff(const Event event)
1493{
1494 logEvent(__FUNCTION__, event);
1495 switch (event)
1496 {
1497 case Event::psPowerOKDeAssert:
1498 gracefulPowerOffTimer.cancel();
1499 setPowerState(PowerState::cycleOff);
1500 powerCycleTimerStart();
1501 break;
1502 case Event::gracefulPowerOffTimerExpired:
1503 setPowerState(PowerState::on);
1504 break;
1505 default:
1506 std::cerr << "No action taken.\n";
1507 break;
1508 }
1509}
1510
Jason M. Billse9a9e2d2019-08-08 14:01:19 -07001511static void powerStateCheckForWarmReset(const Event event)
1512{
1513 logEvent(__FUNCTION__, event);
1514 switch (event)
1515 {
1516 case Event::sioS5Assert:
1517 warmResetCheckTimer.cancel();
1518 setPowerState(PowerState::transitionToOff);
1519 break;
1520 case Event::warmResetDetected:
1521 setPowerState(PowerState::on);
1522 break;
1523 default:
1524 std::cerr << "No action taken.\n";
1525 break;
1526 }
1527}
1528
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001529static void psPowerOKHandler()
1530{
1531 gpiod::line_event gpioLineEvent = psPowerOKLine.event_read();
1532
1533 Event powerControlEvent =
1534 gpioLineEvent.event_type == gpiod::line_event::RISING_EDGE
1535 ? Event::psPowerOKAssert
1536 : Event::psPowerOKDeAssert;
1537
1538 sendPowerControlEvent(powerControlEvent);
1539 psPowerOKEvent.async_wait(
1540 boost::asio::posix::stream_descriptor::wait_read,
1541 [](const boost::system::error_code ec) {
1542 if (ec)
1543 {
1544 std::cerr << "power supply power OK handler error: "
1545 << ec.message() << "\n";
1546 return;
1547 }
1548 psPowerOKHandler();
1549 });
1550}
1551
1552static void sioPowerGoodHandler()
1553{
1554 gpiod::line_event gpioLineEvent = sioPowerGoodLine.event_read();
1555
1556 Event powerControlEvent =
1557 gpioLineEvent.event_type == gpiod::line_event::RISING_EDGE
1558 ? Event::sioPowerGoodAssert
1559 : Event::sioPowerGoodDeAssert;
1560
1561 sendPowerControlEvent(powerControlEvent);
1562 sioPowerGoodEvent.async_wait(
1563 boost::asio::posix::stream_descriptor::wait_read,
1564 [](const boost::system::error_code ec) {
1565 if (ec)
1566 {
1567 std::cerr << "SIO power good handler error: " << ec.message()
1568 << "\n";
1569 return;
1570 }
1571 sioPowerGoodHandler();
1572 });
1573}
1574
1575static void sioOnControlHandler()
1576{
1577 gpiod::line_event gpioLineEvent = sioOnControlLine.event_read();
1578
1579 bool sioOnControl =
1580 gpioLineEvent.event_type == gpiod::line_event::RISING_EDGE;
1581 std::cerr << "SIO_ONCONTROL value changed: " << sioOnControl << "\n";
1582 sioOnControlEvent.async_wait(
1583 boost::asio::posix::stream_descriptor::wait_read,
1584 [](const boost::system::error_code ec) {
1585 if (ec)
1586 {
1587 std::cerr << "SIO ONCONTROL handler error: " << ec.message()
1588 << "\n";
1589 return;
1590 }
1591 sioOnControlHandler();
1592 });
1593}
1594
1595static void sioS5Handler()
1596{
1597 gpiod::line_event gpioLineEvent = sioS5Line.event_read();
1598
1599 Event powerControlEvent =
1600 gpioLineEvent.event_type == gpiod::line_event::FALLING_EDGE
1601 ? Event::sioS5Assert
1602 : Event::sioS5DeAssert;
1603
1604 sendPowerControlEvent(powerControlEvent);
1605 sioS5Event.async_wait(boost::asio::posix::stream_descriptor::wait_read,
1606 [](const boost::system::error_code ec) {
1607 if (ec)
1608 {
1609 std::cerr << "SIO S5 handler error: "
1610 << ec.message() << "\n";
1611 return;
1612 }
1613 sioS5Handler();
1614 });
1615}
1616
1617static void powerButtonHandler()
1618{
1619 gpiod::line_event gpioLineEvent = powerButtonLine.event_read();
1620
1621 if (gpioLineEvent.event_type == gpiod::line_event::FALLING_EDGE)
1622 {
1623 powerButtonPressLog();
1624 powerButtonIface->set_property("ButtonPressed", true);
1625 if (!powerButtonMask)
1626 {
1627 sendPowerControlEvent(Event::powerButtonPressed);
1628 setRestartCause(RestartCause::powerButton);
1629 }
1630 else
1631 {
1632 std::cerr << "power button press masked\n";
1633 }
1634 }
1635 else if (gpioLineEvent.event_type == gpiod::line_event::RISING_EDGE)
1636 {
1637 powerButtonIface->set_property("ButtonPressed", false);
1638 }
1639 powerButtonEvent.async_wait(
1640 boost::asio::posix::stream_descriptor::wait_read,
1641 [](const boost::system::error_code ec) {
1642 if (ec)
1643 {
1644 std::cerr << "power button handler error: " << ec.message()
1645 << "\n";
1646 return;
1647 }
1648 powerButtonHandler();
1649 });
1650}
1651
1652static void resetButtonHandler()
1653{
1654 gpiod::line_event gpioLineEvent = resetButtonLine.event_read();
1655
1656 if (gpioLineEvent.event_type == gpiod::line_event::FALLING_EDGE)
1657 {
1658 resetButtonPressLog();
1659 resetButtonIface->set_property("ButtonPressed", true);
1660 if (!resetButtonMask)
1661 {
Jason M. Billse9a9e2d2019-08-08 14:01:19 -07001662 sendPowerControlEvent(Event::resetButtonPressed);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001663 setRestartCause(RestartCause::resetButton);
1664 }
1665 else
1666 {
1667 std::cerr << "reset button press masked\n";
1668 }
1669 }
1670 else if (gpioLineEvent.event_type == gpiod::line_event::RISING_EDGE)
1671 {
1672 resetButtonIface->set_property("ButtonPressed", false);
1673 }
1674 resetButtonEvent.async_wait(
1675 boost::asio::posix::stream_descriptor::wait_read,
1676 [](const boost::system::error_code ec) {
1677 if (ec)
1678 {
1679 std::cerr << "reset button handler error: " << ec.message()
1680 << "\n";
1681 return;
1682 }
1683 resetButtonHandler();
1684 });
1685}
1686
1687static void nmiSetEnablePorperty(bool value)
1688{
1689 conn->async_method_call(
1690 [](boost::system::error_code ec) {
1691 if (ec)
1692 {
1693 std::cerr << "failed to set NMI source\n";
1694 }
1695 },
1696 "xyz.openbmc_project.Settings", "/com/intel/control/NMISource",
1697 "org.freedesktop.DBus.Properties", "Set", "com.intel.Control.NMISource",
1698 "Enabled", std::variant<bool>{value});
1699}
1700
1701static void nmiReset(void)
1702{
1703 static constexpr const uint8_t value = 1;
1704 const static constexpr int nmiOutPulseTimeMs = 200;
1705
1706 std::cerr << "NMI out action \n";
1707 nmiOutLine.set_value(value);
1708 std::cerr << nmiOutName << " set to " << std::to_string(value) << "\n";
1709 gpioAssertTimer.expires_after(std::chrono::milliseconds(nmiOutPulseTimeMs));
1710 gpioAssertTimer.async_wait([](const boost::system::error_code ec) {
1711 // restore the NMI_OUT GPIO line back to the opposite value
1712 nmiOutLine.set_value(!value);
1713 std::cerr << nmiOutName << " released\n";
1714 if (ec)
1715 {
1716 // operation_aborted is expected if timer is canceled before
1717 // completion.
1718 if (ec != boost::asio::error::operation_aborted)
1719 {
1720 std::cerr << nmiOutName << " async_wait failed: " + ec.message()
1721 << "\n";
1722 }
1723 }
1724 });
1725 // log to redfish
1726 nmiDiagIntLog();
1727 std::cerr << "NMI out action completed\n";
1728 // reset Enable Property
1729 nmiSetEnablePorperty(false);
1730}
1731
1732static void nmiSourcePropertyMonitor(void)
1733{
1734 std::cerr << " NMI Source Property Monitor \n";
1735
1736 static std::unique_ptr<sdbusplus::bus::match::match> nmiSourceMatch =
1737 std::make_unique<sdbusplus::bus::match::match>(
1738 *conn,
1739 "type='signal',interface='org.freedesktop.DBus.Properties',"
1740 "member='PropertiesChanged',arg0namespace='com.intel.Control."
1741 "NMISource'",
1742 [](sdbusplus::message::message& msg) {
1743 std::string interfaceName;
1744 boost::container::flat_map<std::string,
1745 std::variant<bool, std::string>>
1746 propertiesChanged;
1747 std::string state;
1748 bool value = true;
1749 try
1750 {
1751 msg.read(interfaceName, propertiesChanged);
1752 if (propertiesChanged.begin()->first == "Enabled")
1753 {
1754 value =
1755 std::get<bool>(propertiesChanged.begin()->second);
1756 std::cerr
1757 << " NMI Enabled propertiesChanged value: " << value
1758 << "\n";
1759 nmiEnabled = value;
1760 if (nmiEnabled)
1761 {
1762 nmiReset();
1763 }
1764 }
1765 }
1766 catch (std::exception& e)
1767 {
1768 std::cerr << "Unable to read NMI source\n";
1769 return;
1770 }
1771 });
1772}
1773
1774static void setNmiSource()
1775{
1776 conn->async_method_call(
1777 [](boost::system::error_code ec) {
1778 if (ec)
1779 {
1780 std::cerr << "failed to set NMI source\n";
1781 }
1782 },
1783 "xyz.openbmc_project.Settings", "/com/intel/control/NMISource",
1784 "org.freedesktop.DBus.Properties", "Set", "com.intel.Control.NMISource",
1785 "BMCSource",
1786 std::variant<std::string>{
1787 "com.intel.Control.NMISource.BMCSourceSignal.FpBtn"});
1788 // set Enable Property
1789 nmiSetEnablePorperty(true);
1790}
1791
1792static void nmiButtonHandler()
1793{
1794 gpiod::line_event gpioLineEvent = nmiButtonLine.event_read();
1795
1796 if (gpioLineEvent.event_type == gpiod::line_event::FALLING_EDGE)
1797 {
1798 nmiButtonPressLog();
1799 nmiButtonIface->set_property("ButtonPressed", true);
1800 if (nmiButtonMasked)
1801 {
1802 std::cerr << "NMI button press masked\n";
1803 }
1804 else
1805 {
1806 setNmiSource();
1807 }
1808 }
1809 else if (gpioLineEvent.event_type == gpiod::line_event::RISING_EDGE)
1810 {
1811 nmiButtonIface->set_property("ButtonPressed", false);
1812 }
1813 nmiButtonEvent.async_wait(boost::asio::posix::stream_descriptor::wait_read,
1814 [](const boost::system::error_code ec) {
1815 if (ec)
1816 {
1817 std::cerr << "NMI button handler error: "
1818 << ec.message() << "\n";
1819 return;
1820 }
1821 nmiButtonHandler();
1822 });
1823}
1824
1825static void idButtonHandler()
1826{
1827 gpiod::line_event gpioLineEvent = idButtonLine.event_read();
1828
1829 if (gpioLineEvent.event_type == gpiod::line_event::FALLING_EDGE)
1830 {
1831 idButtonIface->set_property("ButtonPressed", true);
1832 }
1833 else if (gpioLineEvent.event_type == gpiod::line_event::RISING_EDGE)
1834 {
1835 idButtonIface->set_property("ButtonPressed", false);
1836 }
1837 idButtonEvent.async_wait(boost::asio::posix::stream_descriptor::wait_read,
1838 [](const boost::system::error_code& ec) {
1839 if (ec)
1840 {
1841 std::cerr << "ID button handler error: "
1842 << ec.message() << "\n";
1843 return;
1844 }
1845 idButtonHandler();
1846 });
1847}
1848
1849static void postCompleteHandler()
1850{
1851 gpiod::line_event gpioLineEvent = postCompleteLine.event_read();
1852
1853 bool postComplete =
1854 gpioLineEvent.event_type == gpiod::line_event::FALLING_EDGE;
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001855 if (postComplete)
1856 {
Jason M. Billse9a9e2d2019-08-08 14:01:19 -07001857 sendPowerControlEvent(Event::postCompleteAssert);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001858 osIface->set_property("OperatingSystemState", std::string("Standby"));
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001859 }
1860 else
1861 {
Jason M. Billse9a9e2d2019-08-08 14:01:19 -07001862 sendPowerControlEvent(Event::postCompleteDeAssert);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001863 osIface->set_property("OperatingSystemState", std::string("Inactive"));
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001864 }
1865 postCompleteEvent.async_wait(
1866 boost::asio::posix::stream_descriptor::wait_read,
1867 [](const boost::system::error_code ec) {
1868 if (ec)
1869 {
1870 std::cerr << "POST complete handler error: " << ec.message()
1871 << "\n";
1872 return;
1873 }
1874 postCompleteHandler();
1875 });
1876}
1877} // namespace power_control
1878
1879int main(int argc, char* argv[])
1880{
1881 std::cerr << "Start Chassis power control service...\n";
1882 power_control::conn =
1883 std::make_shared<sdbusplus::asio::connection>(power_control::io);
1884
1885 // Request all the dbus names
1886 power_control::conn->request_name("xyz.openbmc_project.State.Host");
1887 power_control::conn->request_name("xyz.openbmc_project.State.Chassis");
1888 power_control::conn->request_name(
1889 "xyz.openbmc_project.State.OperatingSystem");
1890 power_control::conn->request_name("xyz.openbmc_project.Chassis.Buttons");
Chen Yugang174ec662019-08-19 19:58:49 +08001891 power_control::conn->request_name("xyz.openbmc_project.Control.Host.NMI");
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001892
1893 // Request PS_PWROK GPIO events
1894 if (!power_control::requestGPIOEvents(
1895 "PS_PWROK", power_control::psPowerOKHandler,
1896 power_control::psPowerOKLine, power_control::psPowerOKEvent))
1897 {
1898 return -1;
1899 }
1900
1901 // Request SIO_POWER_GOOD GPIO events
1902 if (!power_control::requestGPIOEvents(
1903 "SIO_POWER_GOOD", power_control::sioPowerGoodHandler,
1904 power_control::sioPowerGoodLine, power_control::sioPowerGoodEvent))
1905 {
1906 return -1;
1907 }
1908
1909 // Request SIO_ONCONTROL GPIO events
1910 if (!power_control::requestGPIOEvents(
1911 "SIO_ONCONTROL", power_control::sioOnControlHandler,
1912 power_control::sioOnControlLine, power_control::sioOnControlEvent))
1913 {
1914 return -1;
1915 }
1916
1917 // Request SIO_S5 GPIO events
1918 if (!power_control::requestGPIOEvents("SIO_S5", power_control::sioS5Handler,
1919 power_control::sioS5Line,
1920 power_control::sioS5Event))
1921 {
1922 return -1;
1923 }
1924
1925 // Request POWER_BUTTON GPIO events
1926 if (!power_control::requestGPIOEvents(
1927 "POWER_BUTTON", power_control::powerButtonHandler,
1928 power_control::powerButtonLine, power_control::powerButtonEvent))
1929 {
1930 return -1;
1931 }
1932
1933 // Request RESET_BUTTON GPIO events
1934 if (!power_control::requestGPIOEvents(
1935 "RESET_BUTTON", power_control::resetButtonHandler,
1936 power_control::resetButtonLine, power_control::resetButtonEvent))
1937 {
1938 return -1;
1939 }
1940
1941 // Request NMI_BUTTON GPIO events
Vijay Khemka33a532d2019-11-14 16:50:35 -08001942 power_control::requestGPIOEvents(
1943 "NMI_BUTTON", power_control::nmiButtonHandler,
1944 power_control::nmiButtonLine, power_control::nmiButtonEvent);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001945
1946 // Request ID_BUTTON GPIO events
Vijay Khemka33a532d2019-11-14 16:50:35 -08001947 power_control::requestGPIOEvents(
1948 "ID_BUTTON", power_control::idButtonHandler,
1949 power_control::idButtonLine, power_control::idButtonEvent);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001950
1951 // Request POST_COMPLETE GPIO events
1952 if (!power_control::requestGPIOEvents(
1953 "POST_COMPLETE", power_control::postCompleteHandler,
1954 power_control::postCompleteLine, power_control::postCompleteEvent))
1955 {
1956 return -1;
1957 }
1958
1959 // initialize NMI_OUT GPIO.
Vijay Khemka33a532d2019-11-14 16:50:35 -08001960 power_control::setGPIOOutput(power_control::nmiOutName, 0,
1961 power_control::nmiOutLine);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001962
Vijay Khemka0dc7d4c2019-10-22 12:30:17 -07001963 // Initialize POWER_OUT and RESET_OUT GPIO.
1964 gpiod::line line;
1965 if (!power_control::setGPIOOutput(power_control::powerOutName, 1, line))
1966 {
1967 return -1;
1968 }
1969
1970 if (!power_control::setGPIOOutput(power_control::resetOutName, 1, line))
1971 {
1972 return -1;
1973 }
1974
1975 // Release line
1976 line.reset();
1977
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001978 // Initialize the power state
1979 power_control::powerState = power_control::PowerState::off;
1980 // Check power good
1981 if (power_control::psPowerOKLine.get_value() > 0)
1982 {
1983 power_control::powerState = power_control::PowerState::on;
1984 }
1985
1986 // Initialize the power state storage
1987 if (power_control::initializePowerStateStorage() < 0)
1988 {
1989 return -1;
1990 }
1991
1992 // Check if we need to start the Power Restore policy
1993 power_control::powerRestorePolicyCheck();
1994
Vijay Khemka33a532d2019-11-14 16:50:35 -08001995 if (power_control::nmiOutLine)
1996 power_control::nmiSourcePropertyMonitor();
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001997
1998 std::cerr << "Initializing power state. ";
1999 power_control::logStateTransition(power_control::powerState);
2000
2001 // Power Control Service
2002 sdbusplus::asio::object_server hostServer =
2003 sdbusplus::asio::object_server(power_control::conn);
2004
2005 // Power Control Interface
2006 power_control::hostIface = hostServer.add_interface(
2007 "/xyz/openbmc_project/state/host0", "xyz.openbmc_project.State.Host");
2008
2009 power_control::hostIface->register_property(
2010 "RequestedHostTransition",
2011 std::string("xyz.openbmc_project.State.Host.Transition.Off"),
2012 [](const std::string& requested, std::string& resp) {
2013 if (requested == "xyz.openbmc_project.State.Host.Transition.Off")
2014 {
2015 sendPowerControlEvent(
2016 power_control::Event::gracefulPowerOffRequest);
2017 }
2018 else if (requested ==
2019 "xyz.openbmc_project.State.Host.Transition.On")
2020 {
2021 sendPowerControlEvent(power_control::Event::powerOnRequest);
2022 setRestartCause(power_control::RestartCause::command);
2023 }
2024 else if (requested ==
2025 "xyz.openbmc_project.State.Host.Transition.Reboot")
2026 {
2027 sendPowerControlEvent(
2028 power_control::Event::gracefulPowerCycleRequest);
2029 setRestartCause(power_control::RestartCause::command);
2030 }
2031 else
2032 {
2033 std::cerr << "Unrecognized host state transition request.\n";
2034 throw std::invalid_argument("Unrecognized Transition Request");
2035 return 0;
2036 }
2037 resp = requested;
2038 return 1;
2039 });
2040 power_control::hostIface->register_property(
2041 "CurrentHostState",
2042 std::string(power_control::getHostState(power_control::powerState)));
2043
2044 power_control::currentHostStateMonitor();
2045
2046 power_control::hostIface->initialize();
2047
2048 // Chassis Control Service
2049 sdbusplus::asio::object_server chassisServer =
2050 sdbusplus::asio::object_server(power_control::conn);
2051
2052 // Chassis Control Interface
2053 power_control::chassisIface =
2054 chassisServer.add_interface("/xyz/openbmc_project/state/chassis0",
2055 "xyz.openbmc_project.State.Chassis");
2056
2057 power_control::chassisIface->register_property(
2058 "RequestedPowerTransition",
2059 std::string("xyz.openbmc_project.State.Chassis.Transition.Off"),
2060 [](const std::string& requested, std::string& resp) {
2061 if (requested == "xyz.openbmc_project.State.Chassis.Transition.Off")
2062 {
2063 sendPowerControlEvent(power_control::Event::powerOffRequest);
2064 }
2065 else if (requested ==
2066 "xyz.openbmc_project.State.Chassis.Transition.On")
2067 {
2068 sendPowerControlEvent(power_control::Event::powerOnRequest);
2069 setRestartCause(power_control::RestartCause::command);
2070 }
2071 else if (requested ==
2072 "xyz.openbmc_project.State.Chassis.Transition.PowerCycle")
2073 {
2074 sendPowerControlEvent(power_control::Event::powerCycleRequest);
2075 setRestartCause(power_control::RestartCause::command);
2076 }
2077 else if (requested ==
2078 "xyz.openbmc_project.State.Chassis.Transition.Reset")
2079 {
2080 setRestartCause(power_control::RestartCause::command);
2081 sendPowerControlEvent(power_control::Event::resetRequest);
2082 }
2083 else
2084 {
2085 std::cerr << "Unrecognized chassis state transition request.\n";
2086 throw std::invalid_argument("Unrecognized Transition Request");
2087 return 0;
2088 }
2089 resp = requested;
2090 return 1;
2091 });
2092 power_control::chassisIface->register_property(
2093 "CurrentPowerState",
2094 std::string(power_control::getChassisState(power_control::powerState)));
2095 power_control::chassisIface->register_property(
2096 "LastStateChangeTime", power_control::getCurrentTimeMs());
2097
2098 power_control::chassisIface->initialize();
2099
2100 // Buttons Service
2101 sdbusplus::asio::object_server buttonsServer =
2102 sdbusplus::asio::object_server(power_control::conn);
2103
2104 // Power Button Interface
2105 power_control::powerButtonIface = buttonsServer.add_interface(
2106 "/xyz/openbmc_project/chassis/buttons/power",
2107 "xyz.openbmc_project.Chassis.Buttons");
2108
2109 power_control::powerButtonIface->register_property(
2110 "ButtonMasked", false, [](const bool requested, bool& current) {
2111 if (requested)
2112 {
2113 if (power_control::powerButtonMask)
2114 {
2115 return 1;
2116 }
2117 if (!power_control::setGPIOOutput(
Vijay Khemka0dc7d4c2019-10-22 12:30:17 -07002118 power_control::powerOutName, 1,
2119 power_control::powerButtonMask))
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002120 {
2121 throw std::runtime_error("Failed to request GPIO");
2122 return 0;
2123 }
2124 std::cerr << "Power Button Masked.\n";
2125 }
2126 else
2127 {
2128 if (!power_control::powerButtonMask)
2129 {
2130 return 1;
2131 }
2132 std::cerr << "Power Button Un-masked\n";
2133 power_control::powerButtonMask.reset();
2134 }
2135 // Update the mask setting
2136 current = requested;
2137 return 1;
2138 });
2139
2140 // Check power button state
2141 bool powerButtonPressed = power_control::powerButtonLine.get_value() == 0;
2142 power_control::powerButtonIface->register_property("ButtonPressed",
2143 powerButtonPressed);
2144
2145 power_control::powerButtonIface->initialize();
2146
2147 // Reset Button Interface
2148 power_control::resetButtonIface = buttonsServer.add_interface(
2149 "/xyz/openbmc_project/chassis/buttons/reset",
2150 "xyz.openbmc_project.Chassis.Buttons");
2151
2152 power_control::resetButtonIface->register_property(
2153 "ButtonMasked", false, [](const bool requested, bool& current) {
2154 if (requested)
2155 {
2156 if (power_control::resetButtonMask)
2157 {
2158 return 1;
2159 }
2160 if (!power_control::setGPIOOutput(
Vijay Khemka0dc7d4c2019-10-22 12:30:17 -07002161 power_control::resetOutName, 1,
2162 power_control::resetButtonMask))
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002163 {
2164 throw std::runtime_error("Failed to request GPIO");
2165 return 0;
2166 }
2167 std::cerr << "Reset Button Masked.\n";
2168 }
2169 else
2170 {
2171 if (!power_control::resetButtonMask)
2172 {
2173 return 1;
2174 }
2175 std::cerr << "Reset Button Un-masked\n";
2176 power_control::resetButtonMask.reset();
2177 }
2178 // Update the mask setting
2179 current = requested;
2180 return 1;
2181 });
2182
2183 // Check reset button state
2184 bool resetButtonPressed = power_control::resetButtonLine.get_value() == 0;
2185 power_control::resetButtonIface->register_property("ButtonPressed",
2186 resetButtonPressed);
2187
2188 power_control::resetButtonIface->initialize();
2189
Vijay Khemka33a532d2019-11-14 16:50:35 -08002190 if (power_control::nmiButtonLine)
2191 {
2192 // NMI Button Interface
2193 power_control::nmiButtonIface = buttonsServer.add_interface(
2194 "/xyz/openbmc_project/chassis/buttons/nmi",
2195 "xyz.openbmc_project.Chassis.Buttons");
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002196
Vijay Khemka33a532d2019-11-14 16:50:35 -08002197 power_control::nmiButtonIface->register_property(
2198 "ButtonMasked", false, [](const bool requested, bool& current) {
2199 if (power_control::nmiButtonMasked == requested)
2200 {
2201 // NMI button mask is already set as requested, so no change
2202 return 1;
2203 }
2204 if (requested)
2205 {
2206 std::cerr << "NMI Button Masked.\n";
2207 power_control::nmiButtonMasked = true;
2208 }
2209 else
2210 {
2211 std::cerr << "NMI Button Un-masked.\n";
2212 power_control::nmiButtonMasked = false;
2213 }
2214 // Update the mask setting
2215 current = power_control::nmiButtonMasked;
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002216 return 1;
Vijay Khemka33a532d2019-11-14 16:50:35 -08002217 });
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002218
Vijay Khemka33a532d2019-11-14 16:50:35 -08002219 // Check NMI button state
2220 bool nmiButtonPressed = power_control::nmiButtonLine.get_value() == 0;
2221 power_control::nmiButtonIface->register_property("ButtonPressed",
2222 nmiButtonPressed);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002223
Vijay Khemka33a532d2019-11-14 16:50:35 -08002224 power_control::nmiButtonIface->initialize();
2225 }
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002226
Vijay Khemka33a532d2019-11-14 16:50:35 -08002227 if (power_control::nmiOutLine)
2228 {
2229 // NMI out Service
2230 sdbusplus::asio::object_server nmiOutServer =
2231 sdbusplus::asio::object_server(power_control::conn);
Chen Yugang174ec662019-08-19 19:58:49 +08002232
Vijay Khemka33a532d2019-11-14 16:50:35 -08002233 // NMI out Interface
2234 power_control::nmiOutIface =
2235 nmiOutServer.add_interface("/xyz/openbmc_project/control/host0/nmi",
2236 "xyz.openbmc_project.Control.Host.NMI");
2237 power_control::nmiOutIface->register_method("NMI",
2238 power_control::nmiReset);
2239 power_control::nmiOutIface->initialize();
2240 }
Chen Yugang174ec662019-08-19 19:58:49 +08002241
Vijay Khemka33a532d2019-11-14 16:50:35 -08002242 if (power_control::idButtonLine)
2243 {
2244 // ID Button Interface
2245 power_control::idButtonIface = buttonsServer.add_interface(
2246 "/xyz/openbmc_project/chassis/buttons/id",
2247 "xyz.openbmc_project.Chassis.Buttons");
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002248
Vijay Khemka33a532d2019-11-14 16:50:35 -08002249 // Check ID button state
2250 bool idButtonPressed = power_control::idButtonLine.get_value() == 0;
2251 power_control::idButtonIface->register_property("ButtonPressed",
2252 idButtonPressed);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002253
Vijay Khemka33a532d2019-11-14 16:50:35 -08002254 power_control::idButtonIface->initialize();
2255 }
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002256
2257 // OS State Service
2258 sdbusplus::asio::object_server osServer =
2259 sdbusplus::asio::object_server(power_control::conn);
2260
2261 // OS State Interface
2262 power_control::osIface = osServer.add_interface(
2263 "/xyz/openbmc_project/state/os",
2264 "xyz.openbmc_project.State.OperatingSystem.Status");
2265
2266 // Get the initial OS state based on POST complete
2267 // 0: Asserted, OS state is "Standby" (ready to boot)
2268 // 1: De-Asserted, OS state is "Inactive"
2269 std::string osState = power_control::postCompleteLine.get_value() > 0
2270 ? "Inactive"
2271 : "Standby";
2272
2273 power_control::osIface->register_property("OperatingSystemState",
2274 std::string(osState));
2275
2276 power_control::osIface->initialize();
2277
2278 power_control::io.run();
2279
2280 return 0;
2281}