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