124956598SShawn McCarney /** 224956598SShawn McCarney * Copyright © 2024 IBM Corporation 324956598SShawn McCarney * 424956598SShawn McCarney * Licensed under the Apache License, Version 2.0 (the "License"); 524956598SShawn McCarney * you may not use this file except in compliance with the License. 624956598SShawn McCarney * You may obtain a copy of the License at 724956598SShawn McCarney * 824956598SShawn McCarney * http://www.apache.org/licenses/LICENSE-2.0 924956598SShawn McCarney * 1024956598SShawn McCarney * Unless required by applicable law or agreed to in writing, software 1124956598SShawn McCarney * distributed under the License is distributed on an "AS IS" BASIS, 1224956598SShawn McCarney * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1324956598SShawn McCarney * See the License for the specific language governing permissions and 1424956598SShawn McCarney * limitations under the License. 1524956598SShawn McCarney */ 1624956598SShawn McCarney 1724956598SShawn McCarney #include "rail.hpp" 1824956598SShawn McCarney 1924956598SShawn McCarney #include "pmbus.hpp" 20*f47a7a72SShawn McCarney #include "power_sequencer_device.hpp" 2124956598SShawn McCarney 2224956598SShawn McCarney #include <exception> 2324956598SShawn McCarney #include <format> 2424956598SShawn McCarney 2524956598SShawn McCarney namespace phosphor::power::sequencer 2624956598SShawn McCarney { 2724956598SShawn McCarney namespace status_vout = phosphor::pmbus::status_vout; 2824956598SShawn McCarney 2924956598SShawn McCarney bool Rail::isPresent(Services& services) 3024956598SShawn McCarney { 3124956598SShawn McCarney // Initially assume rail is present 3224956598SShawn McCarney bool present{true}; 3324956598SShawn McCarney 3424956598SShawn McCarney // If presence data member contains an inventory path to check 3524956598SShawn McCarney if (presence) 3624956598SShawn McCarney { 3724956598SShawn McCarney const std::string& inventoryPath = *presence; 3824956598SShawn McCarney try 3924956598SShawn McCarney { 4024956598SShawn McCarney present = services.isPresent(inventoryPath); 4124956598SShawn McCarney } 4224956598SShawn McCarney catch (const std::exception& e) 4324956598SShawn McCarney { 4424956598SShawn McCarney throw std::runtime_error{std::format( 4524956598SShawn McCarney "Unable to determine presence of rail {} using inventory path {}: {}", 4624956598SShawn McCarney name, inventoryPath, e.what())}; 4724956598SShawn McCarney } 4824956598SShawn McCarney } 4924956598SShawn McCarney 5024956598SShawn McCarney return present; 5124956598SShawn McCarney } 5224956598SShawn McCarney 5324956598SShawn McCarney uint16_t Rail::getStatusWord(PowerSequencerDevice& device) 5424956598SShawn McCarney { 5524956598SShawn McCarney uint16_t value{0}; 5624956598SShawn McCarney try 5724956598SShawn McCarney { 5824956598SShawn McCarney verifyHasPage(); 5924956598SShawn McCarney value = device.getStatusWord(*page); 6024956598SShawn McCarney } 6124956598SShawn McCarney catch (const std::exception& e) 6224956598SShawn McCarney { 6324956598SShawn McCarney throw std::runtime_error{ 6424956598SShawn McCarney std::format("Unable to read STATUS_WORD value for rail {}: {}", 6524956598SShawn McCarney name, e.what())}; 6624956598SShawn McCarney } 6724956598SShawn McCarney return value; 6824956598SShawn McCarney } 6924956598SShawn McCarney 7024956598SShawn McCarney uint8_t Rail::getStatusVout(PowerSequencerDevice& device) 7124956598SShawn McCarney { 7224956598SShawn McCarney uint8_t value{0}; 7324956598SShawn McCarney try 7424956598SShawn McCarney { 7524956598SShawn McCarney verifyHasPage(); 7624956598SShawn McCarney value = device.getStatusVout(*page); 7724956598SShawn McCarney } 7824956598SShawn McCarney catch (const std::exception& e) 7924956598SShawn McCarney { 8024956598SShawn McCarney throw std::runtime_error{ 8124956598SShawn McCarney std::format("Unable to read STATUS_VOUT value for rail {}: {}", 8224956598SShawn McCarney name, e.what())}; 8324956598SShawn McCarney } 8424956598SShawn McCarney return value; 8524956598SShawn McCarney } 8624956598SShawn McCarney 8724956598SShawn McCarney double Rail::getReadVout(PowerSequencerDevice& device) 8824956598SShawn McCarney { 8924956598SShawn McCarney double value{0.0}; 9024956598SShawn McCarney try 9124956598SShawn McCarney { 9224956598SShawn McCarney verifyHasPage(); 9324956598SShawn McCarney value = device.getReadVout(*page); 9424956598SShawn McCarney } 9524956598SShawn McCarney catch (const std::exception& e) 9624956598SShawn McCarney { 9724956598SShawn McCarney throw std::runtime_error{std::format( 9824956598SShawn McCarney "Unable to read READ_VOUT value for rail {}: {}", name, e.what())}; 9924956598SShawn McCarney } 10024956598SShawn McCarney return value; 10124956598SShawn McCarney } 10224956598SShawn McCarney 10324956598SShawn McCarney double Rail::getVoutUVFaultLimit(PowerSequencerDevice& device) 10424956598SShawn McCarney { 10524956598SShawn McCarney double value{0.0}; 10624956598SShawn McCarney try 10724956598SShawn McCarney { 10824956598SShawn McCarney verifyHasPage(); 10924956598SShawn McCarney value = device.getVoutUVFaultLimit(*page); 11024956598SShawn McCarney } 11124956598SShawn McCarney catch (const std::exception& e) 11224956598SShawn McCarney { 11324956598SShawn McCarney throw std::runtime_error{std::format( 11424956598SShawn McCarney "Unable to read VOUT_UV_FAULT_LIMIT value for rail {}: {}", name, 11524956598SShawn McCarney e.what())}; 11624956598SShawn McCarney } 11724956598SShawn McCarney return value; 11824956598SShawn McCarney } 11924956598SShawn McCarney 12024956598SShawn McCarney bool Rail::hasPgoodFault(PowerSequencerDevice& device, Services& services, 12124956598SShawn McCarney const std::vector<int>& gpioValues, 12224956598SShawn McCarney std::map<std::string, std::string>& additionalData) 12324956598SShawn McCarney { 12424956598SShawn McCarney // If rail is not present, return false and don't check anything else 12524956598SShawn McCarney if (!isPresent(services)) 12624956598SShawn McCarney { 12724956598SShawn McCarney services.logInfoMsg(std::format("Rail {} is not present", name)); 12824956598SShawn McCarney return false; 12924956598SShawn McCarney } 13024956598SShawn McCarney 13124956598SShawn McCarney // Check if STATUS_VOUT indicates a pgood fault occurred 13224956598SShawn McCarney bool hasFault = hasPgoodFaultStatusVout(device, services, additionalData); 13324956598SShawn McCarney 13424956598SShawn McCarney // Check if a GPIO value indicates a pgood fault occurred 13524956598SShawn McCarney if (!hasFault) 13624956598SShawn McCarney { 13724956598SShawn McCarney hasFault = hasPgoodFaultGPIO(services, gpioValues, additionalData); 13824956598SShawn McCarney } 13924956598SShawn McCarney 14024956598SShawn McCarney // Check if output voltage is below UV limit indicating pgood fault occurred 14124956598SShawn McCarney if (!hasFault) 14224956598SShawn McCarney { 14324956598SShawn McCarney hasFault = hasPgoodFaultOutputVoltage(device, services, additionalData); 14424956598SShawn McCarney } 14524956598SShawn McCarney 14624956598SShawn McCarney // If fault detected, store debug data in additional data map 14724956598SShawn McCarney if (hasFault) 14824956598SShawn McCarney { 14924956598SShawn McCarney services.logErrorMsg( 15024956598SShawn McCarney std::format("Pgood fault detected in rail {}", name)); 15124956598SShawn McCarney storePgoodFaultDebugData(device, services, additionalData); 15224956598SShawn McCarney } 15324956598SShawn McCarney 15424956598SShawn McCarney return hasFault; 15524956598SShawn McCarney } 15624956598SShawn McCarney 15724956598SShawn McCarney void Rail::verifyHasPage() 15824956598SShawn McCarney { 15924956598SShawn McCarney if (!page) 16024956598SShawn McCarney { 16124956598SShawn McCarney throw std::runtime_error{ 16224956598SShawn McCarney std::format("No PAGE number defined for rail {}", name)}; 16324956598SShawn McCarney } 16424956598SShawn McCarney } 16524956598SShawn McCarney 16624956598SShawn McCarney bool Rail::hasPgoodFaultStatusVout( 16724956598SShawn McCarney PowerSequencerDevice& device, Services& services, 16824956598SShawn McCarney std::map<std::string, std::string>& additionalData) 16924956598SShawn McCarney { 17024956598SShawn McCarney bool hasFault{false}; 17124956598SShawn McCarney 17224956598SShawn McCarney // If we are checking the value of STATUS_VOUT for the rail 17324956598SShawn McCarney if (checkStatusVout) 17424956598SShawn McCarney { 17524956598SShawn McCarney // Read STATUS_VOUT value from device 17624956598SShawn McCarney uint8_t statusVout = getStatusVout(device); 17724956598SShawn McCarney 17824956598SShawn McCarney // Check if fault (non-warning) bits are set in value 17924956598SShawn McCarney if (statusVout & ~status_vout::WARNING_MASK) 18024956598SShawn McCarney { 18124956598SShawn McCarney hasFault = true; 18224956598SShawn McCarney services.logErrorMsg(std::format( 18324956598SShawn McCarney "Rail {} has fault bits set in STATUS_VOUT: {:#04x}", name, 18424956598SShawn McCarney statusVout)); 18524956598SShawn McCarney additionalData.emplace("STATUS_VOUT", 18624956598SShawn McCarney std::format("{:#04x}", statusVout)); 18724956598SShawn McCarney } 18824956598SShawn McCarney else if (statusVout != 0) 18924956598SShawn McCarney { 19024956598SShawn McCarney services.logInfoMsg(std::format( 19124956598SShawn McCarney "Rail {} has warning bits set in STATUS_VOUT: {:#04x}", name, 19224956598SShawn McCarney statusVout)); 19324956598SShawn McCarney } 19424956598SShawn McCarney } 19524956598SShawn McCarney 19624956598SShawn McCarney return hasFault; 19724956598SShawn McCarney } 19824956598SShawn McCarney 19924956598SShawn McCarney bool Rail::hasPgoodFaultGPIO(Services& services, 20024956598SShawn McCarney const std::vector<int>& gpioValues, 20124956598SShawn McCarney std::map<std::string, std::string>& additionalData) 20224956598SShawn McCarney { 20324956598SShawn McCarney bool hasFault{false}; 20424956598SShawn McCarney 20524956598SShawn McCarney // If a GPIO is defined for checking pgood status 20624956598SShawn McCarney if (gpio) 20724956598SShawn McCarney { 20824956598SShawn McCarney // Get GPIO value 20924956598SShawn McCarney unsigned int line = gpio->line; 21024956598SShawn McCarney bool activeLow = gpio->activeLow; 21124956598SShawn McCarney if (line >= gpioValues.size()) 21224956598SShawn McCarney { 21324956598SShawn McCarney throw std::runtime_error{std::format( 21424956598SShawn McCarney "Invalid GPIO line offset {} for rail {}: Device only has {} GPIO values", 21524956598SShawn McCarney line, name, gpioValues.size())}; 21624956598SShawn McCarney } 21724956598SShawn McCarney int value = gpioValues[line]; 21824956598SShawn McCarney 21924956598SShawn McCarney // Check if value indicates pgood signal is not active 22024956598SShawn McCarney if ((activeLow && (value == 1)) || (!activeLow && (value == 0))) 22124956598SShawn McCarney { 22224956598SShawn McCarney hasFault = true; 22324956598SShawn McCarney services.logErrorMsg(std::format( 22424956598SShawn McCarney "Rail {} pgood GPIO line offset {} has inactive value {}", name, 22524956598SShawn McCarney line, value)); 22624956598SShawn McCarney additionalData.emplace("GPIO_LINE", std::format("{}", line)); 22724956598SShawn McCarney additionalData.emplace("GPIO_VALUE", std::format("{}", value)); 22824956598SShawn McCarney } 22924956598SShawn McCarney } 23024956598SShawn McCarney 23124956598SShawn McCarney return hasFault; 23224956598SShawn McCarney } 23324956598SShawn McCarney 23424956598SShawn McCarney bool Rail::hasPgoodFaultOutputVoltage( 23524956598SShawn McCarney PowerSequencerDevice& device, Services& services, 23624956598SShawn McCarney std::map<std::string, std::string>& additionalData) 23724956598SShawn McCarney { 23824956598SShawn McCarney bool hasFault{false}; 23924956598SShawn McCarney 24024956598SShawn McCarney // If we are comparing output voltage to UV limit to check pgood status 24124956598SShawn McCarney if (compareVoltageToLimit) 24224956598SShawn McCarney { 24324956598SShawn McCarney // Read output voltage and UV fault limit values from device 24424956598SShawn McCarney double vout = getReadVout(device); 24524956598SShawn McCarney double uvLimit = getVoutUVFaultLimit(device); 24624956598SShawn McCarney 24724956598SShawn McCarney // If output voltage is at or below UV fault limit 24824956598SShawn McCarney if (vout <= uvLimit) 24924956598SShawn McCarney { 25024956598SShawn McCarney hasFault = true; 25124956598SShawn McCarney services.logErrorMsg(std::format( 25224956598SShawn McCarney "Rail {} output voltage {}V is <= UV fault limit {}V", name, 25324956598SShawn McCarney vout, uvLimit)); 25424956598SShawn McCarney additionalData.emplace("READ_VOUT", std::format("{}", vout)); 25524956598SShawn McCarney additionalData.emplace("VOUT_UV_FAULT_LIMIT", 25624956598SShawn McCarney std::format("{}", uvLimit)); 25724956598SShawn McCarney } 25824956598SShawn McCarney } 25924956598SShawn McCarney 26024956598SShawn McCarney return hasFault; 26124956598SShawn McCarney } 26224956598SShawn McCarney 26324956598SShawn McCarney void Rail::storePgoodFaultDebugData( 26424956598SShawn McCarney PowerSequencerDevice& device, Services& services, 26524956598SShawn McCarney std::map<std::string, std::string>& additionalData) 26624956598SShawn McCarney { 26724956598SShawn McCarney additionalData.emplace("RAIL_NAME", name); 26824956598SShawn McCarney if (page) 26924956598SShawn McCarney { 27024956598SShawn McCarney try 27124956598SShawn McCarney { 27224956598SShawn McCarney uint16_t statusWord = getStatusWord(device); 27324956598SShawn McCarney services.logInfoMsg( 27424956598SShawn McCarney std::format("Rail {} STATUS_WORD: {:#06x}", name, statusWord)); 27524956598SShawn McCarney additionalData.emplace("STATUS_WORD", 27624956598SShawn McCarney std::format("{:#06x}", statusWord)); 27724956598SShawn McCarney } 27824956598SShawn McCarney catch (...) 27924956598SShawn McCarney { 28024956598SShawn McCarney // Ignore error; don't interrupt pgood fault handling 28124956598SShawn McCarney } 28224956598SShawn McCarney } 28324956598SShawn McCarney } 28424956598SShawn McCarney 28524956598SShawn McCarney } // namespace phosphor::power::sequencer 286