xref: /openbmc/phosphor-power/phosphor-power-sequencer/src/rail.cpp (revision 16275831b63e2df21eed140b26810f9591b00c35)
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"
20f47a7a72SShawn 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 
isPresent(Services & services)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 
getStatusWord(PowerSequencerDevice & device)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 
getStatusVout(PowerSequencerDevice & device)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 
getReadVout(PowerSequencerDevice & device)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 
getVoutUVFaultLimit(PowerSequencerDevice & device)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 
hasPgoodFault(PowerSequencerDevice & device,Services & services,const std::vector<int> & gpioValues,std::map<std::string,std::string> & additionalData)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 {
124*16275831SShawn McCarney     return (hasPgoodFaultStatusVout(device, services, additionalData) ||
125*16275831SShawn McCarney             hasPgoodFaultGPIO(device, services, gpioValues, additionalData) ||
126*16275831SShawn McCarney             hasPgoodFaultOutputVoltage(device, services, additionalData));
12724956598SShawn McCarney }
12824956598SShawn McCarney 
hasPgoodFaultStatusVout(PowerSequencerDevice & device,Services & services,std::map<std::string,std::string> & additionalData)12924956598SShawn McCarney bool Rail::hasPgoodFaultStatusVout(
13024956598SShawn McCarney     PowerSequencerDevice& device, Services& services,
13124956598SShawn McCarney     std::map<std::string, std::string>& additionalData)
13224956598SShawn McCarney {
13324956598SShawn McCarney     bool hasFault{false};
13424956598SShawn McCarney 
135*16275831SShawn McCarney     // If rail is present and we are checking the value of STATUS_VOUT
136*16275831SShawn McCarney     if (isPresent(services) && checkStatusVout)
13724956598SShawn McCarney     {
13824956598SShawn McCarney         // Read STATUS_VOUT value from device
13924956598SShawn McCarney         uint8_t statusVout = getStatusVout(device);
14024956598SShawn McCarney 
14124956598SShawn McCarney         // Check if fault (non-warning) bits are set in value
14224956598SShawn McCarney         if (statusVout & ~status_vout::WARNING_MASK)
14324956598SShawn McCarney         {
14424956598SShawn McCarney             hasFault = true;
14524956598SShawn McCarney             services.logErrorMsg(std::format(
14624956598SShawn McCarney                 "Rail {} has fault bits set in STATUS_VOUT: {:#04x}", name,
14724956598SShawn McCarney                 statusVout));
14824956598SShawn McCarney             additionalData.emplace("STATUS_VOUT",
14924956598SShawn McCarney                                    std::format("{:#04x}", statusVout));
150*16275831SShawn McCarney             storePgoodFaultDebugData(device, services, additionalData);
15124956598SShawn McCarney         }
15224956598SShawn McCarney         else if (statusVout != 0)
15324956598SShawn McCarney         {
15424956598SShawn McCarney             services.logInfoMsg(std::format(
15524956598SShawn McCarney                 "Rail {} has warning bits set in STATUS_VOUT: {:#04x}", name,
15624956598SShawn McCarney                 statusVout));
15724956598SShawn McCarney         }
15824956598SShawn McCarney     }
15924956598SShawn McCarney 
16024956598SShawn McCarney     return hasFault;
16124956598SShawn McCarney }
16224956598SShawn McCarney 
hasPgoodFaultGPIO(PowerSequencerDevice & device,Services & services,const std::vector<int> & gpioValues,std::map<std::string,std::string> & additionalData)163*16275831SShawn McCarney bool Rail::hasPgoodFaultGPIO(PowerSequencerDevice& device, Services& services,
16424956598SShawn McCarney                              const std::vector<int>& gpioValues,
16524956598SShawn McCarney                              std::map<std::string, std::string>& additionalData)
16624956598SShawn McCarney {
16724956598SShawn McCarney     bool hasFault{false};
16824956598SShawn McCarney 
169*16275831SShawn McCarney     // If rail is present and a GPIO is defined for checking pgood status
170*16275831SShawn McCarney     if (isPresent(services) && gpio)
17124956598SShawn McCarney     {
17224956598SShawn McCarney         // Get GPIO value
17324956598SShawn McCarney         unsigned int line = gpio->line;
17424956598SShawn McCarney         bool activeLow = gpio->activeLow;
17524956598SShawn McCarney         if (line >= gpioValues.size())
17624956598SShawn McCarney         {
17724956598SShawn McCarney             throw std::runtime_error{std::format(
17824956598SShawn McCarney                 "Invalid GPIO line offset {} for rail {}: Device only has {} GPIO values",
17924956598SShawn McCarney                 line, name, gpioValues.size())};
18024956598SShawn McCarney         }
18124956598SShawn McCarney         int value = gpioValues[line];
18224956598SShawn McCarney 
18324956598SShawn McCarney         // Check if value indicates pgood signal is not active
18424956598SShawn McCarney         if ((activeLow && (value == 1)) || (!activeLow && (value == 0)))
18524956598SShawn McCarney         {
18624956598SShawn McCarney             hasFault = true;
18724956598SShawn McCarney             services.logErrorMsg(std::format(
18824956598SShawn McCarney                 "Rail {} pgood GPIO line offset {} has inactive value {}", name,
18924956598SShawn McCarney                 line, value));
19024956598SShawn McCarney             additionalData.emplace("GPIO_LINE", std::format("{}", line));
19124956598SShawn McCarney             additionalData.emplace("GPIO_VALUE", std::format("{}", value));
192*16275831SShawn McCarney             storePgoodFaultDebugData(device, services, additionalData);
19324956598SShawn McCarney         }
19424956598SShawn McCarney     }
19524956598SShawn McCarney 
19624956598SShawn McCarney     return hasFault;
19724956598SShawn McCarney }
19824956598SShawn McCarney 
hasPgoodFaultOutputVoltage(PowerSequencerDevice & device,Services & services,std::map<std::string,std::string> & additionalData)19924956598SShawn McCarney bool Rail::hasPgoodFaultOutputVoltage(
20024956598SShawn McCarney     PowerSequencerDevice& device, Services& services,
20124956598SShawn McCarney     std::map<std::string, std::string>& additionalData)
20224956598SShawn McCarney {
20324956598SShawn McCarney     bool hasFault{false};
20424956598SShawn McCarney 
205*16275831SShawn McCarney     // If rail is present and we are comparing output voltage to UV limit
206*16275831SShawn McCarney     if (isPresent(services) && compareVoltageToLimit)
20724956598SShawn McCarney     {
20824956598SShawn McCarney         // Read output voltage and UV fault limit values from device
20924956598SShawn McCarney         double vout = getReadVout(device);
21024956598SShawn McCarney         double uvLimit = getVoutUVFaultLimit(device);
21124956598SShawn McCarney 
21224956598SShawn McCarney         // If output voltage is at or below UV fault limit
21324956598SShawn McCarney         if (vout <= uvLimit)
21424956598SShawn McCarney         {
21524956598SShawn McCarney             hasFault = true;
21624956598SShawn McCarney             services.logErrorMsg(std::format(
21724956598SShawn McCarney                 "Rail {} output voltage {}V is <= UV fault limit {}V", name,
21824956598SShawn McCarney                 vout, uvLimit));
21924956598SShawn McCarney             additionalData.emplace("READ_VOUT", std::format("{}", vout));
22024956598SShawn McCarney             additionalData.emplace("VOUT_UV_FAULT_LIMIT",
22124956598SShawn McCarney                                    std::format("{}", uvLimit));
222*16275831SShawn McCarney             storePgoodFaultDebugData(device, services, additionalData);
22324956598SShawn McCarney         }
22424956598SShawn McCarney     }
22524956598SShawn McCarney 
22624956598SShawn McCarney     return hasFault;
22724956598SShawn McCarney }
22824956598SShawn McCarney 
verifyHasPage()229*16275831SShawn McCarney void Rail::verifyHasPage()
230*16275831SShawn McCarney {
231*16275831SShawn McCarney     if (!page)
232*16275831SShawn McCarney     {
233*16275831SShawn McCarney         throw std::runtime_error{
234*16275831SShawn McCarney             std::format("No PAGE number defined for rail {}", name)};
235*16275831SShawn McCarney     }
236*16275831SShawn McCarney }
237*16275831SShawn McCarney 
storePgoodFaultDebugData(PowerSequencerDevice & device,Services & services,std::map<std::string,std::string> & additionalData)23824956598SShawn McCarney void Rail::storePgoodFaultDebugData(
23924956598SShawn McCarney     PowerSequencerDevice& device, Services& services,
24024956598SShawn McCarney     std::map<std::string, std::string>& additionalData)
24124956598SShawn McCarney {
242*16275831SShawn McCarney     services.logErrorMsg(std::format("Pgood fault detected in rail {}", name));
24324956598SShawn McCarney     additionalData.emplace("RAIL_NAME", name);
24424956598SShawn McCarney     if (page)
24524956598SShawn McCarney     {
24624956598SShawn McCarney         try
24724956598SShawn McCarney         {
24824956598SShawn McCarney             uint16_t statusWord = getStatusWord(device);
24924956598SShawn McCarney             services.logInfoMsg(
25024956598SShawn McCarney                 std::format("Rail {} STATUS_WORD: {:#06x}", name, statusWord));
25124956598SShawn McCarney             additionalData.emplace("STATUS_WORD",
25224956598SShawn McCarney                                    std::format("{:#06x}", statusWord));
25324956598SShawn McCarney         }
25424956598SShawn McCarney         catch (...)
25524956598SShawn McCarney         {
25624956598SShawn McCarney             // Ignore error; don't interrupt pgood fault handling
25724956598SShawn McCarney         }
25824956598SShawn McCarney     }
25924956598SShawn McCarney }
26024956598SShawn McCarney 
26124956598SShawn McCarney } // namespace phosphor::power::sequencer
262