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