/** * 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