/** * Copyright © 2024 IBM Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "rail.hpp" #include "pmbus.hpp" #include "power_sequencer_device.hpp" #include #include namespace phosphor::power::sequencer { namespace status_vout = phosphor::pmbus::status_vout; bool Rail::isPresent(Services& services) { // Initially assume rail is present bool present{true}; // If presence data member contains an inventory path to check if (presence) { const std::string& inventoryPath = *presence; try { present = services.isPresent(inventoryPath); } catch (const std::exception& e) { throw std::runtime_error{std::format( "Unable to determine presence of rail {} using inventory path {}: {}", name, inventoryPath, e.what())}; } } return present; } uint16_t Rail::getStatusWord(PowerSequencerDevice& device) { uint16_t value{0}; try { verifyHasPage(); value = device.getStatusWord(*page); } catch (const std::exception& e) { throw std::runtime_error{ std::format("Unable to read STATUS_WORD value for rail {}: {}", name, e.what())}; } return value; } uint8_t Rail::getStatusVout(PowerSequencerDevice& device) { uint8_t value{0}; try { verifyHasPage(); value = device.getStatusVout(*page); } catch (const std::exception& e) { throw std::runtime_error{ std::format("Unable to read STATUS_VOUT value for rail {}: {}", name, e.what())}; } return value; } double Rail::getReadVout(PowerSequencerDevice& device) { double value{0.0}; try { verifyHasPage(); value = device.getReadVout(*page); } catch (const std::exception& e) { throw std::runtime_error{std::format( "Unable to read READ_VOUT value for rail {}: {}", name, e.what())}; } return value; } double Rail::getVoutUVFaultLimit(PowerSequencerDevice& device) { double value{0.0}; try { verifyHasPage(); value = device.getVoutUVFaultLimit(*page); } catch (const std::exception& e) { throw std::runtime_error{std::format( "Unable to read VOUT_UV_FAULT_LIMIT value for rail {}: {}", name, e.what())}; } return value; } bool Rail::hasPgoodFault(PowerSequencerDevice& device, Services& services, const std::vector& gpioValues, std::map& additionalData) { return (hasPgoodFaultStatusVout(device, services, additionalData) || hasPgoodFaultGPIO(device, services, gpioValues, additionalData) || hasPgoodFaultOutputVoltage(device, services, additionalData)); } bool Rail::hasPgoodFaultStatusVout( PowerSequencerDevice& device, Services& services, std::map& additionalData) { bool hasFault{false}; // If rail is present and we are checking the value of STATUS_VOUT if (isPresent(services) && checkStatusVout) { // Read STATUS_VOUT value from device uint8_t statusVout = getStatusVout(device); // Check if fault (non-warning) bits are set in value if (statusVout & ~status_vout::WARNING_MASK) { hasFault = true; services.logErrorMsg(std::format( "Rail {} has fault bits set in STATUS_VOUT: {:#04x}", name, statusVout)); additionalData.emplace("STATUS_VOUT", std::format("{:#04x}", statusVout)); storePgoodFaultDebugData(device, services, additionalData); } else if (statusVout != 0) { services.logInfoMsg(std::format( "Rail {} has warning bits set in STATUS_VOUT: {:#04x}", name, statusVout)); } } return hasFault; } bool Rail::hasPgoodFaultGPIO(PowerSequencerDevice& device, Services& services, const std::vector& gpioValues, std::map& additionalData) { bool hasFault{false}; // If rail is present and a GPIO is defined for checking pgood status if (isPresent(services) && gpio) { // Get GPIO value unsigned int line = gpio->line; bool activeLow = gpio->activeLow; if (line >= gpioValues.size()) { throw std::runtime_error{std::format( "Invalid GPIO line offset {} for rail {}: Device only has {} GPIO values", line, name, gpioValues.size())}; } int value = gpioValues[line]; // Check if value indicates pgood signal is not active if ((activeLow && (value == 1)) || (!activeLow && (value == 0))) { hasFault = true; services.logErrorMsg(std::format( "Rail {} pgood GPIO line offset {} has inactive value {}", name, line, value)); additionalData.emplace("GPIO_LINE", std::format("{}", line)); additionalData.emplace("GPIO_VALUE", std::format("{}", value)); storePgoodFaultDebugData(device, services, additionalData); } } return hasFault; } bool Rail::hasPgoodFaultOutputVoltage( PowerSequencerDevice& device, Services& services, std::map& additionalData) { bool hasFault{false}; // If rail is present and we are comparing output voltage to UV limit if (isPresent(services) && compareVoltageToLimit) { // Read output voltage and UV fault limit values from device double vout = getReadVout(device); double uvLimit = getVoutUVFaultLimit(device); // If output voltage is at or below UV fault limit if (vout <= uvLimit) { hasFault = true; services.logErrorMsg(std::format( "Rail {} output voltage {}V is <= UV fault limit {}V", name, vout, uvLimit)); additionalData.emplace("READ_VOUT", std::format("{}", vout)); additionalData.emplace("VOUT_UV_FAULT_LIMIT", std::format("{}", uvLimit)); storePgoodFaultDebugData(device, services, additionalData); } } return hasFault; } void Rail::verifyHasPage() { if (!page) { throw std::runtime_error{ std::format("No PAGE number defined for rail {}", name)}; } } void Rail::storePgoodFaultDebugData( PowerSequencerDevice& device, Services& services, std::map& additionalData) { services.logErrorMsg(std::format("Pgood fault detected in rail {}", name)); additionalData.emplace("RAIL_NAME", name); if (page) { try { uint16_t statusWord = getStatusWord(device); services.logInfoMsg( std::format("Rail {} STATUS_WORD: {:#06x}", name, statusWord)); additionalData.emplace("STATUS_WORD", std::format("{:#06x}", statusWord)); } catch (...) { // Ignore error; don't interrupt pgood fault handling } } } } // namespace phosphor::power::sequencer