xref: /openbmc/phosphor-power/phosphor-power-sequencer/src/rail.cpp (revision f47a7a725ccafff2ae0e57dea065e966e32906ba)
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