blob: 7355b9a13ab8a6c585a9fd5214ae98cf6b16e2b0 [file] [log] [blame]
Shawn McCarney24956592024-02-19 18:58:57 -06001/**
2 * Copyright © 2024 IBM Corporation
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include "rail.hpp"
18
19#include "pmbus.hpp"
20
21#include <exception>
22#include <format>
23
24namespace phosphor::power::sequencer
25{
26namespace status_vout = phosphor::pmbus::status_vout;
27
28bool Rail::isPresent(Services& services)
29{
30 // Initially assume rail is present
31 bool present{true};
32
33 // If presence data member contains an inventory path to check
34 if (presence)
35 {
36 const std::string& inventoryPath = *presence;
37 try
38 {
39 present = services.isPresent(inventoryPath);
40 }
41 catch (const std::exception& e)
42 {
43 throw std::runtime_error{std::format(
44 "Unable to determine presence of rail {} using inventory path {}: {}",
45 name, inventoryPath, e.what())};
46 }
47 }
48
49 return present;
50}
51
52uint16_t Rail::getStatusWord(PowerSequencerDevice& device)
53{
54 uint16_t value{0};
55 try
56 {
57 verifyHasPage();
58 value = device.getStatusWord(*page);
59 }
60 catch (const std::exception& e)
61 {
62 throw std::runtime_error{
63 std::format("Unable to read STATUS_WORD value for rail {}: {}",
64 name, e.what())};
65 }
66 return value;
67}
68
69uint8_t Rail::getStatusVout(PowerSequencerDevice& device)
70{
71 uint8_t value{0};
72 try
73 {
74 verifyHasPage();
75 value = device.getStatusVout(*page);
76 }
77 catch (const std::exception& e)
78 {
79 throw std::runtime_error{
80 std::format("Unable to read STATUS_VOUT value for rail {}: {}",
81 name, e.what())};
82 }
83 return value;
84}
85
86double Rail::getReadVout(PowerSequencerDevice& device)
87{
88 double value{0.0};
89 try
90 {
91 verifyHasPage();
92 value = device.getReadVout(*page);
93 }
94 catch (const std::exception& e)
95 {
96 throw std::runtime_error{std::format(
97 "Unable to read READ_VOUT value for rail {}: {}", name, e.what())};
98 }
99 return value;
100}
101
102double Rail::getVoutUVFaultLimit(PowerSequencerDevice& device)
103{
104 double value{0.0};
105 try
106 {
107 verifyHasPage();
108 value = device.getVoutUVFaultLimit(*page);
109 }
110 catch (const std::exception& e)
111 {
112 throw std::runtime_error{std::format(
113 "Unable to read VOUT_UV_FAULT_LIMIT value for rail {}: {}", name,
114 e.what())};
115 }
116 return value;
117}
118
119bool Rail::hasPgoodFault(PowerSequencerDevice& device, Services& services,
120 const std::vector<int>& gpioValues,
121 std::map<std::string, std::string>& additionalData)
122{
123 // If rail is not present, return false and don't check anything else
124 if (!isPresent(services))
125 {
126 services.logInfoMsg(std::format("Rail {} is not present", name));
127 return false;
128 }
129
130 // Check if STATUS_VOUT indicates a pgood fault occurred
131 bool hasFault = hasPgoodFaultStatusVout(device, services, additionalData);
132
133 // Check if a GPIO value indicates a pgood fault occurred
134 if (!hasFault)
135 {
136 hasFault = hasPgoodFaultGPIO(services, gpioValues, additionalData);
137 }
138
139 // Check if output voltage is below UV limit indicating pgood fault occurred
140 if (!hasFault)
141 {
142 hasFault = hasPgoodFaultOutputVoltage(device, services, additionalData);
143 }
144
145 // If fault detected, store debug data in additional data map
146 if (hasFault)
147 {
148 services.logErrorMsg(
149 std::format("Pgood fault detected in rail {}", name));
150 storePgoodFaultDebugData(device, services, additionalData);
151 }
152
153 return hasFault;
154}
155
156void Rail::verifyHasPage()
157{
158 if (!page)
159 {
160 throw std::runtime_error{
161 std::format("No PAGE number defined for rail {}", name)};
162 }
163}
164
165bool Rail::hasPgoodFaultStatusVout(
166 PowerSequencerDevice& device, Services& services,
167 std::map<std::string, std::string>& additionalData)
168{
169 bool hasFault{false};
170
171 // If we are checking the value of STATUS_VOUT for the rail
172 if (checkStatusVout)
173 {
174 // Read STATUS_VOUT value from device
175 uint8_t statusVout = getStatusVout(device);
176
177 // Check if fault (non-warning) bits are set in value
178 if (statusVout & ~status_vout::WARNING_MASK)
179 {
180 hasFault = true;
181 services.logErrorMsg(std::format(
182 "Rail {} has fault bits set in STATUS_VOUT: {:#04x}", name,
183 statusVout));
184 additionalData.emplace("STATUS_VOUT",
185 std::format("{:#04x}", statusVout));
186 }
187 else if (statusVout != 0)
188 {
189 services.logInfoMsg(std::format(
190 "Rail {} has warning bits set in STATUS_VOUT: {:#04x}", name,
191 statusVout));
192 }
193 }
194
195 return hasFault;
196}
197
198bool Rail::hasPgoodFaultGPIO(Services& services,
199 const std::vector<int>& gpioValues,
200 std::map<std::string, std::string>& additionalData)
201{
202 bool hasFault{false};
203
204 // If a GPIO is defined for checking pgood status
205 if (gpio)
206 {
207 // Get GPIO value
208 unsigned int line = gpio->line;
209 bool activeLow = gpio->activeLow;
210 if (line >= gpioValues.size())
211 {
212 throw std::runtime_error{std::format(
213 "Invalid GPIO line offset {} for rail {}: Device only has {} GPIO values",
214 line, name, gpioValues.size())};
215 }
216 int value = gpioValues[line];
217
218 // Check if value indicates pgood signal is not active
219 if ((activeLow && (value == 1)) || (!activeLow && (value == 0)))
220 {
221 hasFault = true;
222 services.logErrorMsg(std::format(
223 "Rail {} pgood GPIO line offset {} has inactive value {}", name,
224 line, value));
225 additionalData.emplace("GPIO_LINE", std::format("{}", line));
226 additionalData.emplace("GPIO_VALUE", std::format("{}", value));
227 }
228 }
229
230 return hasFault;
231}
232
233bool Rail::hasPgoodFaultOutputVoltage(
234 PowerSequencerDevice& device, Services& services,
235 std::map<std::string, std::string>& additionalData)
236{
237 bool hasFault{false};
238
239 // If we are comparing output voltage to UV limit to check pgood status
240 if (compareVoltageToLimit)
241 {
242 // Read output voltage and UV fault limit values from device
243 double vout = getReadVout(device);
244 double uvLimit = getVoutUVFaultLimit(device);
245
246 // If output voltage is at or below UV fault limit
247 if (vout <= uvLimit)
248 {
249 hasFault = true;
250 services.logErrorMsg(std::format(
251 "Rail {} output voltage {}V is <= UV fault limit {}V", name,
252 vout, uvLimit));
253 additionalData.emplace("READ_VOUT", std::format("{}", vout));
254 additionalData.emplace("VOUT_UV_FAULT_LIMIT",
255 std::format("{}", uvLimit));
256 }
257 }
258
259 return hasFault;
260}
261
262void Rail::storePgoodFaultDebugData(
263 PowerSequencerDevice& device, Services& services,
264 std::map<std::string, std::string>& additionalData)
265{
266 additionalData.emplace("RAIL_NAME", name);
267 if (page)
268 {
269 try
270 {
271 uint16_t statusWord = getStatusWord(device);
272 services.logInfoMsg(
273 std::format("Rail {} STATUS_WORD: {:#06x}", name, statusWord));
274 additionalData.emplace("STATUS_WORD",
275 std::format("{:#06x}", statusWord));
276 }
277 catch (...)
278 {
279 // Ignore error; don't interrupt pgood fault handling
280 }
281 }
282}
283
284} // namespace phosphor::power::sequencer