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 "rail.hpp" 18 19 #include "pmbus.hpp" 20 #include "power_sequencer_device.hpp" 21 22 #include <exception> 23 #include <format> 24 25 namespace phosphor::power::sequencer 26 { 27 namespace status_vout = phosphor::pmbus::status_vout; 28 29 bool Rail::isPresent(Services& services) 30 { 31 // Initially assume rail is present 32 bool present{true}; 33 34 // If presence data member contains an inventory path to check 35 if (presence) 36 { 37 const std::string& inventoryPath = *presence; 38 try 39 { 40 present = services.isPresent(inventoryPath); 41 } 42 catch (const std::exception& e) 43 { 44 throw std::runtime_error{std::format( 45 "Unable to determine presence of rail {} using inventory path {}: {}", 46 name, inventoryPath, e.what())}; 47 } 48 } 49 50 return present; 51 } 52 53 uint16_t Rail::getStatusWord(PowerSequencerDevice& device) 54 { 55 uint16_t value{0}; 56 try 57 { 58 verifyHasPage(); 59 value = device.getStatusWord(*page); 60 } 61 catch (const std::exception& e) 62 { 63 throw std::runtime_error{ 64 std::format("Unable to read STATUS_WORD value for rail {}: {}", 65 name, e.what())}; 66 } 67 return value; 68 } 69 70 uint8_t Rail::getStatusVout(PowerSequencerDevice& device) 71 { 72 uint8_t value{0}; 73 try 74 { 75 verifyHasPage(); 76 value = device.getStatusVout(*page); 77 } 78 catch (const std::exception& e) 79 { 80 throw std::runtime_error{ 81 std::format("Unable to read STATUS_VOUT value for rail {}: {}", 82 name, e.what())}; 83 } 84 return value; 85 } 86 87 double Rail::getReadVout(PowerSequencerDevice& device) 88 { 89 double value{0.0}; 90 try 91 { 92 verifyHasPage(); 93 value = device.getReadVout(*page); 94 } 95 catch (const std::exception& e) 96 { 97 throw std::runtime_error{std::format( 98 "Unable to read READ_VOUT value for rail {}: {}", name, e.what())}; 99 } 100 return value; 101 } 102 103 double Rail::getVoutUVFaultLimit(PowerSequencerDevice& device) 104 { 105 double value{0.0}; 106 try 107 { 108 verifyHasPage(); 109 value = device.getVoutUVFaultLimit(*page); 110 } 111 catch (const std::exception& e) 112 { 113 throw std::runtime_error{std::format( 114 "Unable to read VOUT_UV_FAULT_LIMIT value for rail {}: {}", name, 115 e.what())}; 116 } 117 return value; 118 } 119 120 bool Rail::hasPgoodFault(PowerSequencerDevice& device, Services& services, 121 const std::vector<int>& gpioValues, 122 std::map<std::string, std::string>& additionalData) 123 { 124 // If rail is not present, return false and don't check anything else 125 if (!isPresent(services)) 126 { 127 services.logInfoMsg(std::format("Rail {} is not present", name)); 128 return false; 129 } 130 131 // Check if STATUS_VOUT indicates a pgood fault occurred 132 bool hasFault = hasPgoodFaultStatusVout(device, services, additionalData); 133 134 // Check if a GPIO value indicates a pgood fault occurred 135 if (!hasFault) 136 { 137 hasFault = hasPgoodFaultGPIO(services, gpioValues, additionalData); 138 } 139 140 // Check if output voltage is below UV limit indicating pgood fault occurred 141 if (!hasFault) 142 { 143 hasFault = hasPgoodFaultOutputVoltage(device, services, additionalData); 144 } 145 146 // If fault detected, store debug data in additional data map 147 if (hasFault) 148 { 149 services.logErrorMsg( 150 std::format("Pgood fault detected in rail {}", name)); 151 storePgoodFaultDebugData(device, services, additionalData); 152 } 153 154 return hasFault; 155 } 156 157 void Rail::verifyHasPage() 158 { 159 if (!page) 160 { 161 throw std::runtime_error{ 162 std::format("No PAGE number defined for rail {}", name)}; 163 } 164 } 165 166 bool Rail::hasPgoodFaultStatusVout( 167 PowerSequencerDevice& device, Services& services, 168 std::map<std::string, std::string>& additionalData) 169 { 170 bool hasFault{false}; 171 172 // If we are checking the value of STATUS_VOUT for the rail 173 if (checkStatusVout) 174 { 175 // Read STATUS_VOUT value from device 176 uint8_t statusVout = getStatusVout(device); 177 178 // Check if fault (non-warning) bits are set in value 179 if (statusVout & ~status_vout::WARNING_MASK) 180 { 181 hasFault = true; 182 services.logErrorMsg(std::format( 183 "Rail {} has fault bits set in STATUS_VOUT: {:#04x}", name, 184 statusVout)); 185 additionalData.emplace("STATUS_VOUT", 186 std::format("{:#04x}", statusVout)); 187 } 188 else if (statusVout != 0) 189 { 190 services.logInfoMsg(std::format( 191 "Rail {} has warning bits set in STATUS_VOUT: {:#04x}", name, 192 statusVout)); 193 } 194 } 195 196 return hasFault; 197 } 198 199 bool Rail::hasPgoodFaultGPIO(Services& services, 200 const std::vector<int>& gpioValues, 201 std::map<std::string, std::string>& additionalData) 202 { 203 bool hasFault{false}; 204 205 // If a GPIO is defined for checking pgood status 206 if (gpio) 207 { 208 // Get GPIO value 209 unsigned int line = gpio->line; 210 bool activeLow = gpio->activeLow; 211 if (line >= gpioValues.size()) 212 { 213 throw std::runtime_error{std::format( 214 "Invalid GPIO line offset {} for rail {}: Device only has {} GPIO values", 215 line, name, gpioValues.size())}; 216 } 217 int value = gpioValues[line]; 218 219 // Check if value indicates pgood signal is not active 220 if ((activeLow && (value == 1)) || (!activeLow && (value == 0))) 221 { 222 hasFault = true; 223 services.logErrorMsg(std::format( 224 "Rail {} pgood GPIO line offset {} has inactive value {}", name, 225 line, value)); 226 additionalData.emplace("GPIO_LINE", std::format("{}", line)); 227 additionalData.emplace("GPIO_VALUE", std::format("{}", value)); 228 } 229 } 230 231 return hasFault; 232 } 233 234 bool Rail::hasPgoodFaultOutputVoltage( 235 PowerSequencerDevice& device, Services& services, 236 std::map<std::string, std::string>& additionalData) 237 { 238 bool hasFault{false}; 239 240 // If we are comparing output voltage to UV limit to check pgood status 241 if (compareVoltageToLimit) 242 { 243 // Read output voltage and UV fault limit values from device 244 double vout = getReadVout(device); 245 double uvLimit = getVoutUVFaultLimit(device); 246 247 // If output voltage is at or below UV fault limit 248 if (vout <= uvLimit) 249 { 250 hasFault = true; 251 services.logErrorMsg(std::format( 252 "Rail {} output voltage {}V is <= UV fault limit {}V", name, 253 vout, uvLimit)); 254 additionalData.emplace("READ_VOUT", std::format("{}", vout)); 255 additionalData.emplace("VOUT_UV_FAULT_LIMIT", 256 std::format("{}", uvLimit)); 257 } 258 } 259 260 return hasFault; 261 } 262 263 void Rail::storePgoodFaultDebugData( 264 PowerSequencerDevice& device, Services& services, 265 std::map<std::string, std::string>& additionalData) 266 { 267 additionalData.emplace("RAIL_NAME", name); 268 if (page) 269 { 270 try 271 { 272 uint16_t statusWord = getStatusWord(device); 273 services.logInfoMsg( 274 std::format("Rail {} STATUS_WORD: {:#06x}", name, statusWord)); 275 additionalData.emplace("STATUS_WORD", 276 std::format("{:#06x}", statusWord)); 277 } 278 catch (...) 279 { 280 // Ignore error; don't interrupt pgood fault handling 281 } 282 } 283 } 284 285 } // namespace phosphor::power::sequencer 286