1 /** 2 * Copyright © 2024 IBM Corporation 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 #include "standard_device.hpp" 18 19 #include "format_utils.hpp" 20 21 #include <exception> 22 #include <format> 23 #include <span> 24 #include <stdexcept> 25 26 namespace phosphor::power::sequencer 27 { 28 29 std::string StandardDevice::findPgoodFault( 30 Services& services, const std::string& powerSupplyError, 31 std::map<std::string, std::string>& additionalData) 32 { 33 std::string error{}; 34 try 35 { 36 prepareForPgoodFaultDetection(services); 37 38 // Get all GPIO values (if possible) from device. They may be slow to 39 // obtain, so obtain them once and then pass values to each Rail object. 40 std::vector<int> gpioValues = getGPIOValuesIfPossible(services); 41 42 // Try to find a voltage rail where a pgood fault occurred 43 Rail* rail = findRailWithPgoodFault(services, gpioValues, 44 additionalData); 45 if (rail != nullptr) 46 { 47 services.logErrorMsg(std::format( 48 "Pgood fault found in rail monitored by device {}", name)); 49 50 // If this is a PSU rail and a PSU error was previously detected 51 if (rail->isPowerSupplyRail() && !powerSupplyError.empty()) 52 { 53 // Return power supply error as root cause 54 error = powerSupplyError; 55 } 56 else 57 { 58 // Return pgood fault as root cause 59 error = 60 "xyz.openbmc_project.Power.Error.PowerSequencerVoltageFault"; 61 } 62 63 storePgoodFaultDebugData(services, gpioValues, additionalData); 64 } 65 } 66 catch (const std::exception& e) 67 { 68 throw std::runtime_error{std::format( 69 "Unable to determine if a pgood fault occurred in device {}: {}", 70 name, e.what())}; 71 } 72 return error; 73 } 74 75 std::vector<int> StandardDevice::getGPIOValuesIfPossible(Services& services) 76 { 77 std::vector<int> values{}; 78 try 79 { 80 values = getGPIOValues(services); 81 } 82 catch (...) 83 {} 84 return values; 85 } 86 87 Rail* StandardDevice::findRailWithPgoodFault( 88 Services& services, const std::vector<int>& gpioValues, 89 std::map<std::string, std::string>& additionalData) 90 { 91 // Look for the first rail in the power on sequence with a pgood fault based 92 // on STATUS_VOUT. This is usually the most accurate method. For example, 93 // if a pgood fault occurs, the power sequencer device may automatically 94 // shut off related rails. Ideally the device will only set fault bits in 95 // STATUS_VOUT for the rail with the pgood fault. However, all the related 96 // rails will likely appear to be faulted by the other methods. 97 for (std::unique_ptr<Rail>& rail : rails) 98 { 99 if (rail->hasPgoodFaultStatusVout(*this, services, additionalData)) 100 { 101 return rail.get(); 102 } 103 } 104 105 // Look for the first rail in the power on sequence with a pgood fault based 106 // on either a GPIO or the output voltage. Both methods check if the rail 107 // is powered off. If a pgood fault occurs during the power on sequence, 108 // the power sequencer device may stop powering on rails. As a result, all 109 // rails after the faulted one in the sequence may also be powered off. 110 for (std::unique_ptr<Rail>& rail : rails) 111 { 112 if (rail->hasPgoodFaultGPIO(*this, services, gpioValues, 113 additionalData) || 114 rail->hasPgoodFaultOutputVoltage(*this, services, additionalData)) 115 { 116 return rail.get(); 117 } 118 } 119 120 // No rail with pgood fault found 121 return nullptr; 122 } 123 124 void StandardDevice::storePgoodFaultDebugData( 125 Services& services, const std::vector<int>& gpioValues, 126 std::map<std::string, std::string>& additionalData) 127 { 128 try 129 { 130 additionalData.emplace("DEVICE_NAME", name); 131 storeGPIOValues(services, gpioValues, additionalData); 132 } 133 catch (...) 134 {} 135 } 136 137 void StandardDevice::storeGPIOValues( 138 Services& services, const std::vector<int>& values, 139 std::map<std::string, std::string>& additionalData) 140 { 141 if (!values.empty()) 142 { 143 std::string valuesStr = format_utils::toString(std::span(values)); 144 services.logInfoMsg( 145 std::format("Device {} GPIO values: {}", name, valuesStr)); 146 additionalData.emplace("GPIO_VALUES", valuesStr); 147 } 148 } 149 150 } // namespace phosphor::power::sequencer 151