| Shawn McCarney | 2495659 | 2024-02-19 18:58:57 -0600 | [diff] [blame] | 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" | 
| Shawn McCarney | f47a7a7 | 2024-04-18 16:59:33 -0500 | [diff] [blame] | 20 | #include "power_sequencer_device.hpp" | 
| Shawn McCarney | 2495659 | 2024-02-19 18:58:57 -0600 | [diff] [blame] | 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 | { | 
| Shawn McCarney | 1627583 | 2024-06-27 10:14:11 -0500 | [diff] [blame] | 124 | return (hasPgoodFaultStatusVout(device, services, additionalData) || | 
|  | 125 | hasPgoodFaultGPIO(device, services, gpioValues, additionalData) || | 
|  | 126 | hasPgoodFaultOutputVoltage(device, services, additionalData)); | 
| Shawn McCarney | 2495659 | 2024-02-19 18:58:57 -0600 | [diff] [blame] | 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 |  | 
| Shawn McCarney | 1627583 | 2024-06-27 10:14:11 -0500 | [diff] [blame] | 135 | // If rail is present and we are checking the value of STATUS_VOUT | 
|  | 136 | if (isPresent(services) && checkStatusVout) | 
| Shawn McCarney | 2495659 | 2024-02-19 18:58:57 -0600 | [diff] [blame] | 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)); | 
| Shawn McCarney | 1627583 | 2024-06-27 10:14:11 -0500 | [diff] [blame] | 150 | storePgoodFaultDebugData(device, services, additionalData); | 
| Shawn McCarney | 2495659 | 2024-02-19 18:58:57 -0600 | [diff] [blame] | 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 |  | 
| Shawn McCarney | 1627583 | 2024-06-27 10:14:11 -0500 | [diff] [blame] | 163 | bool Rail::hasPgoodFaultGPIO(PowerSequencerDevice& device, Services& services, | 
| Shawn McCarney | 2495659 | 2024-02-19 18:58:57 -0600 | [diff] [blame] | 164 | const std::vector<int>& gpioValues, | 
|  | 165 | std::map<std::string, std::string>& additionalData) | 
|  | 166 | { | 
|  | 167 | bool hasFault{false}; | 
|  | 168 |  | 
| Shawn McCarney | 1627583 | 2024-06-27 10:14:11 -0500 | [diff] [blame] | 169 | // If rail is present and a GPIO is defined for checking pgood status | 
|  | 170 | if (isPresent(services) && gpio) | 
| Shawn McCarney | 2495659 | 2024-02-19 18:58:57 -0600 | [diff] [blame] | 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)); | 
| Shawn McCarney | 1627583 | 2024-06-27 10:14:11 -0500 | [diff] [blame] | 192 | storePgoodFaultDebugData(device, services, additionalData); | 
| Shawn McCarney | 2495659 | 2024-02-19 18:58:57 -0600 | [diff] [blame] | 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 |  | 
| Shawn McCarney | 1627583 | 2024-06-27 10:14:11 -0500 | [diff] [blame] | 205 | // If rail is present and we are comparing output voltage to UV limit | 
|  | 206 | if (isPresent(services) && compareVoltageToLimit) | 
| Shawn McCarney | 2495659 | 2024-02-19 18:58:57 -0600 | [diff] [blame] | 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)); | 
| Shawn McCarney | 1627583 | 2024-06-27 10:14:11 -0500 | [diff] [blame] | 222 | storePgoodFaultDebugData(device, services, additionalData); | 
| Shawn McCarney | 2495659 | 2024-02-19 18:58:57 -0600 | [diff] [blame] | 223 | } | 
|  | 224 | } | 
|  | 225 |  | 
|  | 226 | return hasFault; | 
|  | 227 | } | 
|  | 228 |  | 
| Shawn McCarney | 1627583 | 2024-06-27 10:14:11 -0500 | [diff] [blame] | 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 |  | 
| Shawn McCarney | 2495659 | 2024-02-19 18:58:57 -0600 | [diff] [blame] | 238 | void Rail::storePgoodFaultDebugData( | 
|  | 239 | PowerSequencerDevice& device, Services& services, | 
|  | 240 | std::map<std::string, std::string>& additionalData) | 
|  | 241 | { | 
| Shawn McCarney | 1627583 | 2024-06-27 10:14:11 -0500 | [diff] [blame] | 242 | services.logErrorMsg(std::format("Pgood fault detected in rail {}", name)); | 
| Shawn McCarney | 2495659 | 2024-02-19 18:58:57 -0600 | [diff] [blame] | 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 |