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 return (hasPgoodFaultStatusVout(device, services, additionalData) || 125 hasPgoodFaultGPIO(device, services, gpioValues, additionalData) || 126 hasPgoodFaultOutputVoltage(device, services, additionalData)); 127 } 128 129 bool Rail::hasPgoodFaultStatusVout( 130 PowerSequencerDevice& device, Services& services, 131 std::map<std::string, std::string>& additionalData) 132 { 133 bool hasFault{false}; 134 135 // If rail is present and we are checking the value of STATUS_VOUT 136 if (isPresent(services) && checkStatusVout) 137 { 138 // Read STATUS_VOUT value from device 139 uint8_t statusVout = getStatusVout(device); 140 141 // Check if fault (non-warning) bits are set in value 142 if (statusVout & ~status_vout::WARNING_MASK) 143 { 144 hasFault = true; 145 services.logErrorMsg(std::format( 146 "Rail {} has fault bits set in STATUS_VOUT: {:#04x}", name, 147 statusVout)); 148 additionalData.emplace("STATUS_VOUT", 149 std::format("{:#04x}", statusVout)); 150 storePgoodFaultDebugData(device, services, additionalData); 151 } 152 else if (statusVout != 0) 153 { 154 services.logInfoMsg(std::format( 155 "Rail {} has warning bits set in STATUS_VOUT: {:#04x}", name, 156 statusVout)); 157 } 158 } 159 160 return hasFault; 161 } 162 163 bool Rail::hasPgoodFaultGPIO(PowerSequencerDevice& device, Services& services, 164 const std::vector<int>& gpioValues, 165 std::map<std::string, std::string>& additionalData) 166 { 167 bool hasFault{false}; 168 169 // If rail is present and a GPIO is defined for checking pgood status 170 if (isPresent(services) && gpio) 171 { 172 // Get GPIO value 173 unsigned int line = gpio->line; 174 bool activeLow = gpio->activeLow; 175 if (line >= gpioValues.size()) 176 { 177 throw std::runtime_error{std::format( 178 "Invalid GPIO line offset {} for rail {}: Device only has {} GPIO values", 179 line, name, gpioValues.size())}; 180 } 181 int value = gpioValues[line]; 182 183 // Check if value indicates pgood signal is not active 184 if ((activeLow && (value == 1)) || (!activeLow && (value == 0))) 185 { 186 hasFault = true; 187 services.logErrorMsg(std::format( 188 "Rail {} pgood GPIO line offset {} has inactive value {}", name, 189 line, value)); 190 additionalData.emplace("GPIO_LINE", std::format("{}", line)); 191 additionalData.emplace("GPIO_VALUE", std::format("{}", value)); 192 storePgoodFaultDebugData(device, services, additionalData); 193 } 194 } 195 196 return hasFault; 197 } 198 199 bool Rail::hasPgoodFaultOutputVoltage( 200 PowerSequencerDevice& device, Services& services, 201 std::map<std::string, std::string>& additionalData) 202 { 203 bool hasFault{false}; 204 205 // If rail is present and we are comparing output voltage to UV limit 206 if (isPresent(services) && compareVoltageToLimit) 207 { 208 // Read output voltage and UV fault limit values from device 209 double vout = getReadVout(device); 210 double uvLimit = getVoutUVFaultLimit(device); 211 212 // If output voltage is at or below UV fault limit 213 if (vout <= uvLimit) 214 { 215 hasFault = true; 216 services.logErrorMsg(std::format( 217 "Rail {} output voltage {}V is <= UV fault limit {}V", name, 218 vout, uvLimit)); 219 additionalData.emplace("READ_VOUT", std::format("{}", vout)); 220 additionalData.emplace("VOUT_UV_FAULT_LIMIT", 221 std::format("{}", uvLimit)); 222 storePgoodFaultDebugData(device, services, additionalData); 223 } 224 } 225 226 return hasFault; 227 } 228 229 void Rail::verifyHasPage() 230 { 231 if (!page) 232 { 233 throw std::runtime_error{ 234 std::format("No PAGE number defined for rail {}", name)}; 235 } 236 } 237 238 void Rail::storePgoodFaultDebugData( 239 PowerSequencerDevice& device, Services& services, 240 std::map<std::string, std::string>& additionalData) 241 { 242 services.logErrorMsg(std::format("Pgood fault detected in rail {}", name)); 243 additionalData.emplace("RAIL_NAME", name); 244 if (page) 245 { 246 try 247 { 248 uint16_t statusWord = getStatusWord(device); 249 services.logInfoMsg( 250 std::format("Rail {} STATUS_WORD: {:#06x}", name, statusWord)); 251 additionalData.emplace("STATUS_WORD", 252 std::format("{:#06x}", statusWord)); 253 } 254 catch (...) 255 { 256 // Ignore error; don't interrupt pgood fault handling 257 } 258 } 259 } 260 261 } // namespace phosphor::power::sequencer 262