1 /**
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 #include "power_sequencer_device.hpp"
21
22 #include <exception>
23 #include <format>
24
25 namespace phosphor::power::sequencer
26 {
27 namespace status_vout = phosphor::pmbus::status_vout;
28
isPresent(Services & services)29 bool 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
getStatusWord(PowerSequencerDevice & device)53 uint16_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
getStatusVout(PowerSequencerDevice & device)70 uint8_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
getReadVout(PowerSequencerDevice & device)87 double 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
getVoutUVFaultLimit(PowerSequencerDevice & device)103 double 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
hasPgoodFault(PowerSequencerDevice & device,Services & services,const std::vector<int> & gpioValues,std::map<std::string,std::string> & additionalData)120 bool Rail::hasPgoodFault(PowerSequencerDevice& device, Services& services,
121 const std::vector<int>& gpioValues,
122 std::map<std::string, std::string>& additionalData)
123 {
124 return (hasPgoodFaultStatusVout(device, services, additionalData) ||
125 hasPgoodFaultGPIO(device, services, gpioValues, additionalData) ||
126 hasPgoodFaultOutputVoltage(device, services, additionalData));
127 }
128
hasPgoodFaultStatusVout(PowerSequencerDevice & device,Services & services,std::map<std::string,std::string> & additionalData)129 bool Rail::hasPgoodFaultStatusVout(
130 PowerSequencerDevice& device, Services& services,
131 std::map<std::string, std::string>& additionalData)
132 {
133 bool hasFault{false};
134
135 // If rail is present and we are checking the value of STATUS_VOUT
136 if (isPresent(services) && checkStatusVout)
137 {
138 // Read STATUS_VOUT value from device
139 uint8_t statusVout = getStatusVout(device);
140
141 // Check if fault (non-warning) bits are set in value
142 if (statusVout & ~status_vout::WARNING_MASK)
143 {
144 hasFault = true;
145 services.logErrorMsg(std::format(
146 "Rail {} has fault bits set in STATUS_VOUT: {:#04x}", name,
147 statusVout));
148 additionalData.emplace("STATUS_VOUT",
149 std::format("{:#04x}", statusVout));
150 storePgoodFaultDebugData(device, services, additionalData);
151 }
152 else if (statusVout != 0)
153 {
154 services.logInfoMsg(std::format(
155 "Rail {} has warning bits set in STATUS_VOUT: {:#04x}", name,
156 statusVout));
157 }
158 }
159
160 return hasFault;
161 }
162
hasPgoodFaultGPIO(PowerSequencerDevice & device,Services & services,const std::vector<int> & gpioValues,std::map<std::string,std::string> & additionalData)163 bool Rail::hasPgoodFaultGPIO(PowerSequencerDevice& device, Services& services,
164 const std::vector<int>& gpioValues,
165 std::map<std::string, std::string>& additionalData)
166 {
167 bool hasFault{false};
168
169 // If rail is present and a GPIO is defined for checking pgood status
170 if (isPresent(services) && gpio)
171 {
172 // Get GPIO value
173 unsigned int line = gpio->line;
174 bool activeLow = gpio->activeLow;
175 if (line >= gpioValues.size())
176 {
177 throw std::runtime_error{std::format(
178 "Invalid GPIO line offset {} for rail {}: Device only has {} GPIO values",
179 line, name, gpioValues.size())};
180 }
181 int value = gpioValues[line];
182
183 // Check if value indicates pgood signal is not active
184 if ((activeLow && (value == 1)) || (!activeLow && (value == 0)))
185 {
186 hasFault = true;
187 services.logErrorMsg(std::format(
188 "Rail {} pgood GPIO line offset {} has inactive value {}", name,
189 line, value));
190 additionalData.emplace("GPIO_LINE", std::format("{}", line));
191 additionalData.emplace("GPIO_VALUE", std::format("{}", value));
192 storePgoodFaultDebugData(device, services, additionalData);
193 }
194 }
195
196 return hasFault;
197 }
198
hasPgoodFaultOutputVoltage(PowerSequencerDevice & device,Services & services,std::map<std::string,std::string> & additionalData)199 bool Rail::hasPgoodFaultOutputVoltage(
200 PowerSequencerDevice& device, Services& services,
201 std::map<std::string, std::string>& additionalData)
202 {
203 bool hasFault{false};
204
205 // If rail is present and we are comparing output voltage to UV limit
206 if (isPresent(services) && compareVoltageToLimit)
207 {
208 // Read output voltage and UV fault limit values from device
209 double vout = getReadVout(device);
210 double uvLimit = getVoutUVFaultLimit(device);
211
212 // If output voltage is at or below UV fault limit
213 if (vout <= uvLimit)
214 {
215 hasFault = true;
216 services.logErrorMsg(std::format(
217 "Rail {} output voltage {}V is <= UV fault limit {}V", name,
218 vout, uvLimit));
219 additionalData.emplace("READ_VOUT", std::format("{}", vout));
220 additionalData.emplace("VOUT_UV_FAULT_LIMIT",
221 std::format("{}", uvLimit));
222 storePgoodFaultDebugData(device, services, additionalData);
223 }
224 }
225
226 return hasFault;
227 }
228
verifyHasPage()229 void Rail::verifyHasPage()
230 {
231 if (!page)
232 {
233 throw std::runtime_error{
234 std::format("No PAGE number defined for rail {}", name)};
235 }
236 }
237
storePgoodFaultDebugData(PowerSequencerDevice & device,Services & services,std::map<std::string,std::string> & additionalData)238 void Rail::storePgoodFaultDebugData(
239 PowerSequencerDevice& device, Services& services,
240 std::map<std::string, std::string>& additionalData)
241 {
242 services.logErrorMsg(std::format("Pgood fault detected in rail {}", name));
243 additionalData.emplace("RAIL_NAME", name);
244 if (page)
245 {
246 try
247 {
248 uint16_t statusWord = getStatusWord(device);
249 services.logInfoMsg(
250 std::format("Rail {} STATUS_WORD: {:#06x}", name, statusWord));
251 additionalData.emplace("STATUS_WORD",
252 std::format("{:#06x}", statusWord));
253 }
254 catch (...)
255 {
256 // Ignore error; don't interrupt pgood fault handling
257 }
258 }
259 }
260
261 } // namespace phosphor::power::sequencer
262