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