blob: 5bfbfb337196870f67da95e0e4709e3b63f780ed [file] [log] [blame]
/**
* Copyright © 2024 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 "standard_device.hpp"
#include "format_utils.hpp"
#include <exception>
#include <format>
#include <span>
#include <stdexcept>
namespace phosphor::power::sequencer
{
std::string StandardDevice::findPgoodFault(
Services& services, const std::string& powerSupplyError,
std::map<std::string, std::string>& additionalData)
{
std::string error{};
try
{
prepareForPgoodFaultDetection(services);
// Get all GPIO values (if possible) from device. They may be slow to
// obtain, so obtain them once and then pass values to each Rail object.
std::vector<int> gpioValues = getGPIOValuesIfPossible(services);
// Try to find a voltage rail where a pgood fault occurred
Rail* rail = findRailWithPgoodFault(services, gpioValues,
additionalData);
if (rail != nullptr)
{
services.logErrorMsg(std::format(
"Pgood fault found in rail monitored by device {}", name));
// If this is a PSU rail and a PSU error was previously detected
if (rail->isPowerSupplyRail() && !powerSupplyError.empty())
{
// Return power supply error as root cause
error = powerSupplyError;
}
else
{
// Return pgood fault as root cause
error =
"xyz.openbmc_project.Power.Error.PowerSequencerVoltageFault";
}
storePgoodFaultDebugData(services, gpioValues, additionalData);
}
}
catch (const std::exception& e)
{
throw std::runtime_error{std::format(
"Unable to determine if a pgood fault occurred in device {}: {}",
name, e.what())};
}
return error;
}
std::vector<int> StandardDevice::getGPIOValuesIfPossible(Services& services)
{
std::vector<int> values{};
try
{
values = getGPIOValues(services);
}
catch (...)
{}
return values;
}
Rail* StandardDevice::findRailWithPgoodFault(
Services& services, const std::vector<int>& gpioValues,
std::map<std::string, std::string>& additionalData)
{
// Look for the first rail in the power on sequence with a pgood fault based
// on STATUS_VOUT. This is usually the most accurate method. For example,
// if a pgood fault occurs, the power sequencer device may automatically
// shut off related rails. Ideally the device will only set fault bits in
// STATUS_VOUT for the rail with the pgood fault. However, all the related
// rails will likely appear to be faulted by the other methods.
for (std::unique_ptr<Rail>& rail : rails)
{
if (rail->hasPgoodFaultStatusVout(*this, services, additionalData))
{
return rail.get();
}
}
// Look for the first rail in the power on sequence with a pgood fault based
// on either a GPIO or the output voltage. Both methods check if the rail
// is powered off. If a pgood fault occurs during the power on sequence,
// the power sequencer device may stop powering on rails. As a result, all
// rails after the faulted one in the sequence may also be powered off.
for (std::unique_ptr<Rail>& rail : rails)
{
if (rail->hasPgoodFaultGPIO(*this, services, gpioValues,
additionalData) ||
rail->hasPgoodFaultOutputVoltage(*this, services, additionalData))
{
return rail.get();
}
}
// No rail with pgood fault found
return nullptr;
}
void StandardDevice::storePgoodFaultDebugData(
Services& services, const std::vector<int>& gpioValues,
std::map<std::string, std::string>& additionalData)
{
try
{
additionalData.emplace("DEVICE_NAME", name);
storeGPIOValues(services, gpioValues, additionalData);
}
catch (...)
{}
}
void StandardDevice::storeGPIOValues(
Services& services, const std::vector<int>& values,
std::map<std::string, std::string>& additionalData)
{
if (!values.empty())
{
std::string valuesStr = format_utils::toString(std::span(values));
services.logInfoMsg(
std::format("Device {} GPIO values: {}", name, valuesStr));
additionalData.emplace("GPIO_VALUES", valuesStr);
}
}
} // namespace phosphor::power::sequencer