| /** |
| * Copyright © 2017 IBM Corporation |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| #include <phosphor-logging/log.hpp> |
| #include <xyz/openbmc_project/Power/Fault/error.hpp> |
| #include "config.h" |
| #include "elog-errors.hpp" |
| #include "pgood_monitor.hpp" |
| #include "utility.hpp" |
| |
| namespace witherspoon |
| { |
| namespace power |
| { |
| |
| constexpr auto POWER_OBJ_PATH = "/org/openbmc/control/power0"; |
| constexpr auto POWER_INTERFACE = "org.openbmc.control.Power"; |
| |
| using namespace phosphor::logging; |
| using namespace sdbusplus::xyz::openbmc_project::Power::Fault::Error; |
| |
| bool PGOODMonitor::pgoodPending() |
| { |
| bool pending = false; |
| int32_t state = 0; |
| int32_t pgood = 0; |
| |
| auto service = util::getService(POWER_OBJ_PATH, |
| POWER_INTERFACE, |
| bus); |
| |
| util::getProperty<int32_t>(POWER_INTERFACE, |
| "pgood", |
| POWER_OBJ_PATH, |
| service, |
| bus, |
| pgood); |
| |
| //When state = 1, system was switched on |
| util::getProperty<int32_t>(POWER_INTERFACE, |
| "state", |
| POWER_OBJ_PATH, |
| service, |
| bus, |
| state); |
| |
| //On but no PGOOD |
| if (state && !pgood) |
| { |
| pending = true; |
| } |
| |
| return pending; |
| } |
| |
| |
| void PGOODMonitor::exitEventLoop() |
| { |
| auto r = sd_event_exit(event.get(), EXIT_SUCCESS); |
| if (r < 0) |
| { |
| log<level::ERR>("sd_event_exit failed", |
| entry("RC = %d", r)); |
| } |
| } |
| |
| void PGOODMonitor::analyze() |
| { |
| //Timer callback. |
| //The timer expired before it was stopped. |
| //If PGOOD is still pending (it should be), |
| //then there is a real failure. |
| |
| if (pgoodPending()) |
| { |
| #ifdef UCD90160_DEVICE_ACCESS |
| device->onFailure(); |
| #endif |
| report<PowerOnFailure>(); |
| } |
| |
| //The pgood-wait service (with a longer timeout) |
| //will handle powering off the system. |
| |
| exitEventLoop(); |
| } |
| |
| void PGOODMonitor::propertyChanged() |
| { |
| //Multiple properties could have changed here. |
| //Keep things simple and just recheck the important ones. |
| if (!pgoodPending()) |
| { |
| //PGOOD is on, or system is off, so we are done. |
| timer.stop(); |
| exitEventLoop(); |
| } |
| } |
| |
| void PGOODMonitor::startListening() |
| { |
| match = std::make_unique<sdbusplus::bus::match_t>( |
| bus, |
| sdbusplus::bus::match::rules::propertiesChanged( |
| POWER_OBJ_PATH, |
| POWER_INTERFACE), |
| [this](auto& msg){this->propertyChanged();}); |
| } |
| |
| int PGOODMonitor::run() |
| { |
| try |
| { |
| startListening(); |
| |
| //If PGOOD came up before we got here, we're done. |
| //Otherwise if PGOOD doesn't get asserted before |
| //the timer expires, it's a failure. |
| if (!pgoodPending()) |
| { |
| return EXIT_SUCCESS; |
| } |
| |
| timer.start(interval); |
| |
| auto r = sd_event_loop(event.get()); |
| if (r < 0) |
| { |
| log<level::ERR>("sd_event_loop() failed", |
| entry("ERROR=%d", r)); |
| } |
| } |
| catch (std::exception& e) |
| { |
| log<level::ERR>(e.what()); |
| log<level::ERR>("Unexpected failure prevented PGOOD checking"); |
| } |
| |
| //Letting the service fail won't help anything, so don't do it. |
| return EXIT_SUCCESS; |
| } |
| |
| |
| } |
| } |