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