blob: ec97108ffe445b5f1dfa3e692e6ed1e4762843f9 [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{
Shawn McCarney16275832024-06-27 10:14:11 -0500124 return (hasPgoodFaultStatusVout(device, services, additionalData) ||
125 hasPgoodFaultGPIO(device, services, gpioValues, additionalData) ||
126 hasPgoodFaultOutputVoltage(device, services, additionalData));
Shawn McCarney24956592024-02-19 18:58:57 -0600127}
128
129bool Rail::hasPgoodFaultStatusVout(
130 PowerSequencerDevice& device, Services& services,
131 std::map<std::string, std::string>& additionalData)
132{
133 bool hasFault{false};
134
Shawn McCarney16275832024-06-27 10:14:11 -0500135 // If rail is present and we are checking the value of STATUS_VOUT
136 if (isPresent(services) && checkStatusVout)
Shawn McCarney24956592024-02-19 18:58:57 -0600137 {
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));
Shawn McCarney16275832024-06-27 10:14:11 -0500150 storePgoodFaultDebugData(device, services, additionalData);
Shawn McCarney24956592024-02-19 18:58:57 -0600151 }
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
Shawn McCarney16275832024-06-27 10:14:11 -0500163bool Rail::hasPgoodFaultGPIO(PowerSequencerDevice& device, Services& services,
Shawn McCarney24956592024-02-19 18:58:57 -0600164 const std::vector<int>& gpioValues,
165 std::map<std::string, std::string>& additionalData)
166{
167 bool hasFault{false};
168
Shawn McCarney16275832024-06-27 10:14:11 -0500169 // If rail is present and a GPIO is defined for checking pgood status
170 if (isPresent(services) && gpio)
Shawn McCarney24956592024-02-19 18:58:57 -0600171 {
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));
Shawn McCarney16275832024-06-27 10:14:11 -0500192 storePgoodFaultDebugData(device, services, additionalData);
Shawn McCarney24956592024-02-19 18:58:57 -0600193 }
194 }
195
196 return hasFault;
197}
198
199bool Rail::hasPgoodFaultOutputVoltage(
200 PowerSequencerDevice& device, Services& services,
201 std::map<std::string, std::string>& additionalData)
202{
203 bool hasFault{false};
204
Shawn McCarney16275832024-06-27 10:14:11 -0500205 // If rail is present and we are comparing output voltage to UV limit
206 if (isPresent(services) && compareVoltageToLimit)
Shawn McCarney24956592024-02-19 18:58:57 -0600207 {
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));
Shawn McCarney16275832024-06-27 10:14:11 -0500222 storePgoodFaultDebugData(device, services, additionalData);
Shawn McCarney24956592024-02-19 18:58:57 -0600223 }
224 }
225
226 return hasFault;
227}
228
Shawn McCarney16275832024-06-27 10:14:11 -0500229void 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
Shawn McCarney24956592024-02-19 18:58:57 -0600238void Rail::storePgoodFaultDebugData(
239 PowerSequencerDevice& device, Services& services,
240 std::map<std::string, std::string>& additionalData)
241{
Shawn McCarney16275832024-06-27 10:14:11 -0500242 services.logErrorMsg(std::format("Pgood fault detected in rail {}", name));
Shawn McCarney24956592024-02-19 18:58:57 -0600243 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