blob: 9e8abba9355da6404766f825f62191ce7d5b2e1d [file] [log] [blame]
/**
* 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;
}
}
}