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