blob: 266b7b3440ddc7ee1b63286738f3600f17c262d6 [file] [log] [blame]
Shawn McCarney472101c2024-04-17 16:31:09 -05001/**
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 "mock_services.hpp"
18#include "rail.hpp"
Shawn McCarneyfc3f31f2024-04-23 17:02:44 -050019#include "services.hpp"
Shawn McCarney472101c2024-04-17 16:31:09 -050020#include "standard_device.hpp"
21
22#include <cstdint>
23#include <map>
24#include <memory>
25#include <optional>
26#include <string>
27#include <utility>
28#include <vector>
29
30#include <gmock/gmock.h>
31#include <gtest/gtest.h>
32
33using namespace phosphor::power::sequencer;
34
35using ::testing::Return;
36using ::testing::Throw;
37
38/**
39 * @class StandardDeviceImpl
40 *
41 * Concrete subclass of the StandardDevice abstract class.
42 *
43 * This subclass is required for two reasons:
44 * - StandardDevice has some pure virtual methods so it cannot be instantiated.
45 * - The pure virtual methods provide the PMBus and GPIO information. Mocking
46 * these makes it possible to test the pgood fault detection algorithm.
47 *
48 * This class is not intended to be used outside of this file. It is
49 * implementation detail for testing the the StandardDevice class.
50 */
51class StandardDeviceImpl : public StandardDevice
52{
53 public:
54 // Specify which compiler-generated methods we want
55 StandardDeviceImpl() = delete;
56 StandardDeviceImpl(const StandardDeviceImpl&) = delete;
57 StandardDeviceImpl(StandardDeviceImpl&&) = delete;
58 StandardDeviceImpl& operator=(const StandardDeviceImpl&) = delete;
59 StandardDeviceImpl& operator=(StandardDeviceImpl&&) = delete;
60 virtual ~StandardDeviceImpl() = default;
61
62 // Constructor just calls StandardDevice constructor
63 explicit StandardDeviceImpl(const std::string& name,
64 std::vector<std::unique_ptr<Rail>> rails) :
65 StandardDevice(name, std::move(rails))
66 {}
67
68 // Mock pure virtual methods
Shawn McCarneyfc3f31f2024-04-23 17:02:44 -050069 MOCK_METHOD(std::vector<int>, getGPIOValues, (Services & services),
70 (override));
Shawn McCarney472101c2024-04-17 16:31:09 -050071 MOCK_METHOD(uint16_t, getStatusWord, (uint8_t page), (override));
72 MOCK_METHOD(uint8_t, getStatusVout, (uint8_t page), (override));
73 MOCK_METHOD(double, getReadVout, (uint8_t page), (override));
74 MOCK_METHOD(double, getVoutUVFaultLimit, (uint8_t page), (override));
Shawn McCarney16275832024-06-27 10:14:11 -050075
76 // Override empty implementation with mock so we can verify it is called
77 MOCK_METHOD(void, prepareForPgoodFaultDetection, (Services & services),
78 (override));
Shawn McCarney472101c2024-04-17 16:31:09 -050079};
80
81/**
82 * Creates a Rail object that checks for a pgood fault using STATUS_VOUT.
83 *
84 * @param name Unique name for the rail
85 * @param isPowerSupplyRail Specifies whether the rail is produced by a
86 power supply
87 * @param pageNum PMBus PAGE number of the rail
88 * @return Rail object
89 */
Patrick Williamsf5402192024-08-16 15:20:53 -040090std::unique_ptr<Rail> createRailStatusVout(
91 const std::string& name, bool isPowerSupplyRail, uint8_t pageNum)
Shawn McCarney472101c2024-04-17 16:31:09 -050092{
93 std::optional<std::string> presence{};
94 std::optional<uint8_t> page{pageNum};
95 bool checkStatusVout{true};
96 bool compareVoltageToLimit{false};
97 std::optional<GPIO> gpio{};
98 return std::make_unique<Rail>(name, presence, page, isPowerSupplyRail,
99 checkStatusVout, compareVoltageToLimit, gpio);
100}
101
102/**
103 * Creates a Rail object that checks for a pgood fault using a GPIO.
104 *
105 * @param name Unique name for the rail
106 * @param isPowerSupplyRail Specifies whether the rail is produced by a
107 power supply
108 * @param gpio GPIO line to read to determine the pgood status of the rail
109 * @return Rail object
110 */
Patrick Williamsf5402192024-08-16 15:20:53 -0400111std::unique_ptr<Rail> createRailGPIO(
112 const std::string& name, bool isPowerSupplyRail, unsigned int gpioLine)
Shawn McCarney472101c2024-04-17 16:31:09 -0500113{
114 std::optional<std::string> presence{};
115 std::optional<uint8_t> page{};
116 bool checkStatusVout{false};
117 bool compareVoltageToLimit{false};
118 bool activeLow{false};
119 std::optional<GPIO> gpio{GPIO{gpioLine, activeLow}};
120 return std::make_unique<Rail>(name, presence, page, isPowerSupplyRail,
121 checkStatusVout, compareVoltageToLimit, gpio);
122}
123
Shawn McCarney16275832024-06-27 10:14:11 -0500124/**
125 * Creates a Rail object that checks for a pgood fault using output voltage.
126 *
127 * @param name Unique name for the rail
128 * @param isPowerSupplyRail Specifies whether the rail is produced by a
129 power supply
130 * @param pageNum PMBus PAGE number of the rail
131 * @return Rail object
132 */
Patrick Williamsf5402192024-08-16 15:20:53 -0400133std::unique_ptr<Rail> createRailOutputVoltage(
134 const std::string& name, bool isPowerSupplyRail, uint8_t pageNum)
Shawn McCarney16275832024-06-27 10:14:11 -0500135{
136 std::optional<std::string> presence{};
137 std::optional<uint8_t> page{pageNum};
138 bool checkStatusVout{false};
139 bool compareVoltageToLimit{true};
140 std::optional<GPIO> gpio{};
141 return std::make_unique<Rail>(name, presence, page, isPowerSupplyRail,
142 checkStatusVout, compareVoltageToLimit, gpio);
143}
144
Shawn McCarney472101c2024-04-17 16:31:09 -0500145TEST(StandardDeviceTests, Constructor)
146{
147 // Empty vector of rails
148 {
149 std::vector<std::unique_ptr<Rail>> rails{};
150 StandardDeviceImpl device{"xyz_pseq", std::move(rails)};
151
152 EXPECT_EQ(device.getName(), "xyz_pseq");
153 EXPECT_TRUE(device.getRails().empty());
154 }
155
156 // Non-empty vector of rails
157 {
158 std::vector<std::unique_ptr<Rail>> rails{};
159 rails.emplace_back(createRailGPIO("PSU", true, 3));
Shawn McCarney16275832024-06-27 10:14:11 -0500160 rails.emplace_back(createRailOutputVoltage("VDD", false, 5));
Shawn McCarney472101c2024-04-17 16:31:09 -0500161 rails.emplace_back(createRailStatusVout("VIO", false, 7));
162 StandardDeviceImpl device{"abc_pseq", std::move(rails)};
163
164 EXPECT_EQ(device.getName(), "abc_pseq");
165 EXPECT_EQ(device.getRails().size(), 3);
166 EXPECT_EQ(device.getRails()[0]->getName(), "PSU");
167 EXPECT_EQ(device.getRails()[1]->getName(), "VDD");
168 EXPECT_EQ(device.getRails()[2]->getName(), "VIO");
169 }
170}
171
172TEST(StandardDeviceTests, GetName)
173{
174 std::vector<std::unique_ptr<Rail>> rails{};
175 StandardDeviceImpl device{"xyz_pseq", std::move(rails)};
176
177 EXPECT_EQ(device.getName(), "xyz_pseq");
178}
179
180TEST(StandardDeviceTests, GetRails)
181{
182 // Empty vector of rails
183 {
184 std::vector<std::unique_ptr<Rail>> rails{};
185 StandardDeviceImpl device{"xyz_pseq", std::move(rails)};
186
187 EXPECT_TRUE(device.getRails().empty());
188 }
189
190 // Non-empty vector of rails
191 {
192 std::vector<std::unique_ptr<Rail>> rails{};
193 rails.emplace_back(createRailGPIO("PSU", true, 3));
Shawn McCarney16275832024-06-27 10:14:11 -0500194 rails.emplace_back(createRailOutputVoltage("VDD", false, 5));
Shawn McCarney472101c2024-04-17 16:31:09 -0500195 rails.emplace_back(createRailStatusVout("VIO", false, 7));
196 StandardDeviceImpl device{"abc_pseq", std::move(rails)};
197
198 EXPECT_EQ(device.getRails().size(), 3);
199 EXPECT_EQ(device.getRails()[0]->getName(), "PSU");
200 EXPECT_EQ(device.getRails()[1]->getName(), "VDD");
201 EXPECT_EQ(device.getRails()[2]->getName(), "VIO");
202 }
203}
204
205TEST(StandardDeviceTests, FindPgoodFault)
206{
207 // No rail has a pgood fault
208 {
209 std::vector<std::unique_ptr<Rail>> rails{};
210 rails.emplace_back(createRailGPIO("PSU", true, 2));
Shawn McCarney16275832024-06-27 10:14:11 -0500211 rails.emplace_back(createRailOutputVoltage("VDD", false, 5));
Shawn McCarney472101c2024-04-17 16:31:09 -0500212 rails.emplace_back(createRailStatusVout("VIO", false, 7));
213 StandardDeviceImpl device{"abc_pseq", std::move(rails)};
214
Shawn McCarney16275832024-06-27 10:14:11 -0500215 EXPECT_CALL(device, prepareForPgoodFaultDetection).Times(1);
Shawn McCarney472101c2024-04-17 16:31:09 -0500216 std::vector<int> gpioValues{1, 1, 1};
217 EXPECT_CALL(device, getGPIOValues)
218 .Times(1)
219 .WillOnce(Return(gpioValues));
Shawn McCarney16275832024-06-27 10:14:11 -0500220 EXPECT_CALL(device, getReadVout(5)).Times(1).WillOnce(Return(1.2));
221 EXPECT_CALL(device, getVoutUVFaultLimit(5))
222 .Times(1)
223 .WillOnce(Return(1.1));
Shawn McCarney472101c2024-04-17 16:31:09 -0500224 EXPECT_CALL(device, getStatusVout(7)).Times(1).WillOnce(Return(0x00));
225
226 MockServices services{};
227
228 std::string powerSupplyError{};
229 std::map<std::string, std::string> additionalData{};
Patrick Williamsf5402192024-08-16 15:20:53 -0400230 std::string error =
231 device.findPgoodFault(services, powerSupplyError, additionalData);
Shawn McCarney472101c2024-04-17 16:31:09 -0500232 EXPECT_TRUE(error.empty());
233 EXPECT_EQ(additionalData.size(), 0);
234 }
235
Shawn McCarney16275832024-06-27 10:14:11 -0500236 // First rail has a pgood fault detected via GPIO
Shawn McCarney472101c2024-04-17 16:31:09 -0500237 // Is a PSU rail: No PSU error specified
238 {
239 std::vector<std::unique_ptr<Rail>> rails{};
240 rails.emplace_back(createRailGPIO("PSU", true, 2));
Shawn McCarney16275832024-06-27 10:14:11 -0500241 rails.emplace_back(createRailOutputVoltage("VDD", false, 5));
Shawn McCarney472101c2024-04-17 16:31:09 -0500242 rails.emplace_back(createRailStatusVout("VIO", false, 7));
243 StandardDeviceImpl device{"abc_pseq", std::move(rails)};
244
Shawn McCarney16275832024-06-27 10:14:11 -0500245 EXPECT_CALL(device, prepareForPgoodFaultDetection).Times(1);
Shawn McCarney472101c2024-04-17 16:31:09 -0500246 std::vector<int> gpioValues{1, 1, 0};
247 EXPECT_CALL(device, getGPIOValues)
248 .Times(1)
249 .WillOnce(Return(gpioValues));
Shawn McCarney16275832024-06-27 10:14:11 -0500250 EXPECT_CALL(device, getReadVout(5)).Times(0);
251 EXPECT_CALL(device, getVoutUVFaultLimit(5)).Times(0);
252 EXPECT_CALL(device, getStatusVout(7)).Times(1).WillOnce(Return(0x00));
Shawn McCarney472101c2024-04-17 16:31:09 -0500253
254 MockServices services{};
255 EXPECT_CALL(services,
256 logInfoMsg("Device abc_pseq GPIO values: [1, 1, 0]"))
257 .Times(1);
258 EXPECT_CALL(
259 services,
260 logErrorMsg(
261 "Pgood fault found in rail monitored by device abc_pseq"))
262 .Times(1);
263 EXPECT_CALL(services, logErrorMsg("Pgood fault detected in rail PSU"))
264 .Times(1);
265 EXPECT_CALL(
266 services,
267 logErrorMsg(
268 "Rail PSU pgood GPIO line offset 2 has inactive value 0"))
269 .Times(1);
270
271 std::string powerSupplyError{};
272 std::map<std::string, std::string> additionalData{};
Patrick Williamsf5402192024-08-16 15:20:53 -0400273 std::string error =
274 device.findPgoodFault(services, powerSupplyError, additionalData);
Shawn McCarney472101c2024-04-17 16:31:09 -0500275 EXPECT_EQ(error,
276 "xyz.openbmc_project.Power.Error.PowerSequencerVoltageFault");
277 EXPECT_EQ(additionalData.size(), 5);
278 EXPECT_EQ(additionalData["DEVICE_NAME"], "abc_pseq");
279 EXPECT_EQ(additionalData["GPIO_VALUES"], "[1, 1, 0]");
280 EXPECT_EQ(additionalData["RAIL_NAME"], "PSU");
281 EXPECT_EQ(additionalData["GPIO_LINE"], "2");
282 EXPECT_EQ(additionalData["GPIO_VALUE"], "0");
283 }
284
Shawn McCarney16275832024-06-27 10:14:11 -0500285 // First rail has a pgood fault detected via GPIO
Shawn McCarney472101c2024-04-17 16:31:09 -0500286 // Is a PSU rail: PSU error specified
287 {
288 std::vector<std::unique_ptr<Rail>> rails{};
289 rails.emplace_back(createRailGPIO("PSU", true, 2));
Shawn McCarney16275832024-06-27 10:14:11 -0500290 rails.emplace_back(createRailOutputVoltage("VDD", false, 5));
Shawn McCarney472101c2024-04-17 16:31:09 -0500291 rails.emplace_back(createRailStatusVout("VIO", false, 7));
292 StandardDeviceImpl device{"abc_pseq", std::move(rails)};
293
Shawn McCarney16275832024-06-27 10:14:11 -0500294 EXPECT_CALL(device, prepareForPgoodFaultDetection).Times(1);
Shawn McCarney472101c2024-04-17 16:31:09 -0500295 std::vector<int> gpioValues{1, 1, 0};
296 EXPECT_CALL(device, getGPIOValues)
297 .Times(1)
298 .WillOnce(Return(gpioValues));
Shawn McCarney16275832024-06-27 10:14:11 -0500299 EXPECT_CALL(device, getReadVout(5)).Times(0);
300 EXPECT_CALL(device, getVoutUVFaultLimit(5)).Times(0);
301 EXPECT_CALL(device, getStatusVout(7)).Times(1).WillOnce(Return(0x00));
Shawn McCarney472101c2024-04-17 16:31:09 -0500302
303 MockServices services{};
304 EXPECT_CALL(services,
305 logInfoMsg("Device abc_pseq GPIO values: [1, 1, 0]"))
306 .Times(1);
307 EXPECT_CALL(
308 services,
309 logErrorMsg(
310 "Pgood fault found in rail monitored by device abc_pseq"))
311 .Times(1);
312 EXPECT_CALL(services, logErrorMsg("Pgood fault detected in rail PSU"))
313 .Times(1);
314 EXPECT_CALL(
315 services,
316 logErrorMsg(
317 "Rail PSU pgood GPIO line offset 2 has inactive value 0"))
318 .Times(1);
319
320 std::string powerSupplyError{"Undervoltage fault: PSU1"};
321 std::map<std::string, std::string> additionalData{};
Patrick Williamsf5402192024-08-16 15:20:53 -0400322 std::string error =
323 device.findPgoodFault(services, powerSupplyError, additionalData);
Shawn McCarney472101c2024-04-17 16:31:09 -0500324 EXPECT_EQ(error, powerSupplyError);
325 EXPECT_EQ(additionalData.size(), 5);
326 EXPECT_EQ(additionalData["DEVICE_NAME"], "abc_pseq");
327 EXPECT_EQ(additionalData["GPIO_VALUES"], "[1, 1, 0]");
328 EXPECT_EQ(additionalData["RAIL_NAME"], "PSU");
329 EXPECT_EQ(additionalData["GPIO_LINE"], "2");
330 EXPECT_EQ(additionalData["GPIO_VALUE"], "0");
331 }
332
Shawn McCarney16275832024-06-27 10:14:11 -0500333 // Second rail has a pgood fault detected via output voltage
Shawn McCarney472101c2024-04-17 16:31:09 -0500334 // Not a PSU rail: PSU error specified
335 {
336 std::vector<std::unique_ptr<Rail>> rails{};
337 rails.emplace_back(createRailGPIO("PSU", true, 2));
Shawn McCarney16275832024-06-27 10:14:11 -0500338 rails.emplace_back(createRailOutputVoltage("VDD", false, 5));
Shawn McCarney472101c2024-04-17 16:31:09 -0500339 rails.emplace_back(createRailStatusVout("VIO", false, 7));
340 StandardDeviceImpl device{"abc_pseq", std::move(rails)};
341
Shawn McCarney16275832024-06-27 10:14:11 -0500342 EXPECT_CALL(device, prepareForPgoodFaultDetection).Times(1);
Shawn McCarney472101c2024-04-17 16:31:09 -0500343 std::vector<int> gpioValues{1, 1, 1};
344 EXPECT_CALL(device, getGPIOValues)
345 .Times(1)
346 .WillOnce(Return(gpioValues));
Shawn McCarney16275832024-06-27 10:14:11 -0500347 EXPECT_CALL(device, getReadVout(5)).Times(1).WillOnce(Return(1.1));
348 EXPECT_CALL(device, getVoutUVFaultLimit(5))
349 .Times(1)
350 .WillOnce(Return(1.2));
351 EXPECT_CALL(device, getStatusVout(7)).Times(1).WillOnce(Return(0x00));
Shawn McCarney472101c2024-04-17 16:31:09 -0500352 EXPECT_CALL(device, getStatusWord(5)).Times(1).WillOnce(Return(0xbeef));
353
354 MockServices services{};
355 EXPECT_CALL(services,
356 logInfoMsg("Device abc_pseq GPIO values: [1, 1, 1]"))
357 .Times(1);
358 EXPECT_CALL(
359 services,
360 logErrorMsg(
361 "Pgood fault found in rail monitored by device abc_pseq"))
362 .Times(1);
363 EXPECT_CALL(services, logInfoMsg("Rail VDD STATUS_WORD: 0xbeef"))
364 .Times(1);
365 EXPECT_CALL(services, logErrorMsg("Pgood fault detected in rail VDD"))
366 .Times(1);
367 EXPECT_CALL(
368 services,
Shawn McCarney16275832024-06-27 10:14:11 -0500369 logErrorMsg(
370 "Rail VDD output voltage 1.1V is <= UV fault limit 1.2V"))
Shawn McCarney472101c2024-04-17 16:31:09 -0500371 .Times(1);
372
373 std::string powerSupplyError{"Undervoltage fault: PSU1"};
374 std::map<std::string, std::string> additionalData{};
Patrick Williamsf5402192024-08-16 15:20:53 -0400375 std::string error =
376 device.findPgoodFault(services, powerSupplyError, additionalData);
Shawn McCarney472101c2024-04-17 16:31:09 -0500377 EXPECT_EQ(error,
378 "xyz.openbmc_project.Power.Error.PowerSequencerVoltageFault");
Shawn McCarney16275832024-06-27 10:14:11 -0500379 EXPECT_EQ(additionalData.size(), 6);
Shawn McCarney472101c2024-04-17 16:31:09 -0500380 EXPECT_EQ(additionalData["DEVICE_NAME"], "abc_pseq");
381 EXPECT_EQ(additionalData["GPIO_VALUES"], "[1, 1, 1]");
382 EXPECT_EQ(additionalData["RAIL_NAME"], "VDD");
Shawn McCarney16275832024-06-27 10:14:11 -0500383 EXPECT_EQ(additionalData["READ_VOUT"], "1.1");
384 EXPECT_EQ(additionalData["VOUT_UV_FAULT_LIMIT"], "1.2");
Shawn McCarney472101c2024-04-17 16:31:09 -0500385 EXPECT_EQ(additionalData["STATUS_WORD"], "0xbeef");
386 }
387
Shawn McCarney16275832024-06-27 10:14:11 -0500388 // Third rail has a pgood fault detected via STATUS_VOUT
Shawn McCarney472101c2024-04-17 16:31:09 -0500389 // Device returns 0 GPIO values
390 // Does not halt pgood fault detection because GPIO values not used by rails
391 {
392 std::vector<std::unique_ptr<Rail>> rails{};
393 rails.emplace_back(createRailStatusVout("PSU", true, 3));
Shawn McCarney16275832024-06-27 10:14:11 -0500394 rails.emplace_back(createRailOutputVoltage("VDD", false, 5));
Shawn McCarney472101c2024-04-17 16:31:09 -0500395 rails.emplace_back(createRailStatusVout("VIO", false, 7));
396 StandardDeviceImpl device{"abc_pseq", std::move(rails)};
397
Shawn McCarney16275832024-06-27 10:14:11 -0500398 EXPECT_CALL(device, prepareForPgoodFaultDetection).Times(1);
Shawn McCarney472101c2024-04-17 16:31:09 -0500399 std::vector<int> gpioValues{};
400 EXPECT_CALL(device, getGPIOValues)
401 .Times(1)
402 .WillOnce(Return(gpioValues));
403 EXPECT_CALL(device, getStatusVout(3)).Times(1).WillOnce(Return(0x00));
Shawn McCarney16275832024-06-27 10:14:11 -0500404 EXPECT_CALL(device, getReadVout(5)).Times(0);
405 EXPECT_CALL(device, getVoutUVFaultLimit(5)).Times(0);
Shawn McCarney472101c2024-04-17 16:31:09 -0500406 EXPECT_CALL(device, getStatusVout(7)).Times(1).WillOnce(Return(0x11));
407 EXPECT_CALL(device, getStatusWord(7)).Times(1).WillOnce(Return(0xbeef));
408
409 MockServices services{};
410 EXPECT_CALL(
411 services,
412 logErrorMsg(
413 "Pgood fault found in rail monitored by device abc_pseq"))
414 .Times(1);
415 EXPECT_CALL(services, logInfoMsg("Rail VIO STATUS_WORD: 0xbeef"))
416 .Times(1);
417 EXPECT_CALL(services, logErrorMsg("Pgood fault detected in rail VIO"))
418 .Times(1);
419 EXPECT_CALL(
420 services,
421 logErrorMsg("Rail VIO has fault bits set in STATUS_VOUT: 0x11"))
422 .Times(1);
423
424 std::string powerSupplyError{};
425 std::map<std::string, std::string> additionalData{};
Patrick Williamsf5402192024-08-16 15:20:53 -0400426 std::string error =
427 device.findPgoodFault(services, powerSupplyError, additionalData);
Shawn McCarney472101c2024-04-17 16:31:09 -0500428 EXPECT_EQ(error,
429 "xyz.openbmc_project.Power.Error.PowerSequencerVoltageFault");
430 EXPECT_EQ(additionalData.size(), 4);
431 EXPECT_EQ(additionalData["DEVICE_NAME"], "abc_pseq");
432 EXPECT_EQ(additionalData["RAIL_NAME"], "VIO");
433 EXPECT_EQ(additionalData["STATUS_VOUT"], "0x11");
434 EXPECT_EQ(additionalData["STATUS_WORD"], "0xbeef");
435 }
436
Shawn McCarney16275832024-06-27 10:14:11 -0500437 // Third rail has a pgood fault detected via STATUS_VOUT
Shawn McCarney472101c2024-04-17 16:31:09 -0500438 // Exception occurs trying to obtain GPIO values from device
439 // Does not halt pgood fault detection because GPIO values not used by rails
440 {
441 std::vector<std::unique_ptr<Rail>> rails{};
442 rails.emplace_back(createRailStatusVout("PSU", true, 3));
Shawn McCarney16275832024-06-27 10:14:11 -0500443 rails.emplace_back(createRailOutputVoltage("VDD", false, 5));
Shawn McCarney472101c2024-04-17 16:31:09 -0500444 rails.emplace_back(createRailStatusVout("VIO", false, 7));
445 StandardDeviceImpl device{"abc_pseq", std::move(rails)};
446
Shawn McCarney16275832024-06-27 10:14:11 -0500447 EXPECT_CALL(device, prepareForPgoodFaultDetection).Times(1);
Shawn McCarney472101c2024-04-17 16:31:09 -0500448 EXPECT_CALL(device, getGPIOValues)
449 .Times(1)
450 .WillOnce(Throw(std::runtime_error{"Unable to acquire GPIO line"}));
451 EXPECT_CALL(device, getStatusVout(3)).Times(1).WillOnce(Return(0x00));
Shawn McCarney16275832024-06-27 10:14:11 -0500452 EXPECT_CALL(device, getReadVout(5)).Times(0);
453 EXPECT_CALL(device, getVoutUVFaultLimit(5)).Times(0);
Shawn McCarney472101c2024-04-17 16:31:09 -0500454 EXPECT_CALL(device, getStatusVout(7)).Times(1).WillOnce(Return(0x11));
455 EXPECT_CALL(device, getStatusWord(7)).Times(1).WillOnce(Return(0xbeef));
456
457 MockServices services{};
458 EXPECT_CALL(
459 services,
460 logErrorMsg(
461 "Pgood fault found in rail monitored by device abc_pseq"))
462 .Times(1);
463 EXPECT_CALL(services, logInfoMsg("Rail VIO STATUS_WORD: 0xbeef"))
464 .Times(1);
465 EXPECT_CALL(services, logErrorMsg("Pgood fault detected in rail VIO"))
466 .Times(1);
467 EXPECT_CALL(
468 services,
469 logErrorMsg("Rail VIO has fault bits set in STATUS_VOUT: 0x11"))
470 .Times(1);
471
472 std::string powerSupplyError{};
473 std::map<std::string, std::string> additionalData{};
Patrick Williamsf5402192024-08-16 15:20:53 -0400474 std::string error =
475 device.findPgoodFault(services, powerSupplyError, additionalData);
Shawn McCarney472101c2024-04-17 16:31:09 -0500476 EXPECT_EQ(error,
477 "xyz.openbmc_project.Power.Error.PowerSequencerVoltageFault");
478 EXPECT_EQ(additionalData.size(), 4);
479 EXPECT_EQ(additionalData["DEVICE_NAME"], "abc_pseq");
480 EXPECT_EQ(additionalData["RAIL_NAME"], "VIO");
481 EXPECT_EQ(additionalData["STATUS_VOUT"], "0x11");
482 EXPECT_EQ(additionalData["STATUS_WORD"], "0xbeef");
483 }
484
Shawn McCarney16275832024-06-27 10:14:11 -0500485 // All three rails appear to have a pgood fault. Verify third rail is
486 // selected, even though it is last in the power on sequence, because it is
487 // checked using STATUS_VOUT. That check happens before the other checks.
488 {
489 std::vector<std::unique_ptr<Rail>> rails{};
490 rails.emplace_back(createRailGPIO("PSU", true, 2));
491 rails.emplace_back(createRailGPIO("VDD", false, 1));
492 rails.emplace_back(createRailStatusVout("VIO", false, 7));
493 StandardDeviceImpl device{"abc_pseq", std::move(rails)};
494
495 EXPECT_CALL(device, prepareForPgoodFaultDetection).Times(1);
496 std::vector<int> gpioValues{0, 0, 0};
497 EXPECT_CALL(device, getGPIOValues)
498 .Times(1)
499 .WillOnce(Return(gpioValues));
500 EXPECT_CALL(device, getStatusVout(7)).Times(1).WillOnce(Return(0x11));
501 EXPECT_CALL(device, getStatusWord(7)).Times(1).WillOnce(Return(0xbeef));
502
503 MockServices services{};
504 EXPECT_CALL(services,
505 logInfoMsg("Device abc_pseq GPIO values: [0, 0, 0]"))
506 .Times(1);
507 EXPECT_CALL(
508 services,
509 logErrorMsg(
510 "Pgood fault found in rail monitored by device abc_pseq"))
511 .Times(1);
512 EXPECT_CALL(services, logInfoMsg("Rail VIO STATUS_WORD: 0xbeef"))
513 .Times(1);
514 EXPECT_CALL(services, logErrorMsg("Pgood fault detected in rail VIO"))
515 .Times(1);
516 EXPECT_CALL(
517 services,
518 logErrorMsg("Rail VIO has fault bits set in STATUS_VOUT: 0x11"))
519 .Times(1);
520
521 std::string powerSupplyError{};
522 std::map<std::string, std::string> additionalData{};
Patrick Williamsf5402192024-08-16 15:20:53 -0400523 std::string error =
524 device.findPgoodFault(services, powerSupplyError, additionalData);
Shawn McCarney16275832024-06-27 10:14:11 -0500525 EXPECT_EQ(error,
526 "xyz.openbmc_project.Power.Error.PowerSequencerVoltageFault");
527 EXPECT_EQ(additionalData.size(), 5);
528 EXPECT_EQ(additionalData["DEVICE_NAME"], "abc_pseq");
529 EXPECT_EQ(additionalData["GPIO_VALUES"], "[0, 0, 0]");
530 EXPECT_EQ(additionalData["RAIL_NAME"], "VIO");
531 EXPECT_EQ(additionalData["STATUS_VOUT"], "0x11");
532 EXPECT_EQ(additionalData["STATUS_WORD"], "0xbeef");
533 }
534
535 // Two rails appear to have a pgood fault. One is found via output voltage
536 // and one is found via a GPIO. Verify the first rail in the sequence with
537 // a fault is selected.
538 {
539 std::vector<std::unique_ptr<Rail>> rails{};
540 rails.emplace_back(createRailStatusVout("VIO", false, 7));
541 rails.emplace_back(createRailOutputVoltage("VDD", false, 5));
542 rails.emplace_back(createRailGPIO("PSU", true, 2));
543 StandardDeviceImpl device{"abc_pseq", std::move(rails)};
544
545 EXPECT_CALL(device, prepareForPgoodFaultDetection).Times(1);
546 std::vector<int> gpioValues{1, 1, 0};
547 EXPECT_CALL(device, getGPIOValues)
548 .Times(1)
549 .WillOnce(Return(gpioValues));
550 EXPECT_CALL(device, getStatusVout(7)).Times(1).WillOnce(Return(0x00));
551 EXPECT_CALL(device, getReadVout(5)).Times(1).WillOnce(Return(1.1));
552 EXPECT_CALL(device, getVoutUVFaultLimit(5))
553 .Times(1)
554 .WillOnce(Return(1.2));
555 EXPECT_CALL(device, getStatusWord(5)).Times(1).WillOnce(Return(0xbeef));
556
557 MockServices services{};
558 EXPECT_CALL(services,
559 logInfoMsg("Device abc_pseq GPIO values: [1, 1, 0]"))
560 .Times(1);
561 EXPECT_CALL(
562 services,
563 logErrorMsg(
564 "Pgood fault found in rail monitored by device abc_pseq"))
565 .Times(1);
566 EXPECT_CALL(services, logInfoMsg("Rail VDD STATUS_WORD: 0xbeef"))
567 .Times(1);
568 EXPECT_CALL(services, logErrorMsg("Pgood fault detected in rail VDD"))
569 .Times(1);
570 EXPECT_CALL(
571 services,
572 logErrorMsg(
573 "Rail VDD output voltage 1.1V is <= UV fault limit 1.2V"))
574 .Times(1);
575
576 std::string powerSupplyError{};
577 std::map<std::string, std::string> additionalData{};
Patrick Williamsf5402192024-08-16 15:20:53 -0400578 std::string error =
579 device.findPgoodFault(services, powerSupplyError, additionalData);
Shawn McCarney16275832024-06-27 10:14:11 -0500580 EXPECT_EQ(error,
581 "xyz.openbmc_project.Power.Error.PowerSequencerVoltageFault");
582 EXPECT_EQ(additionalData.size(), 6);
583 EXPECT_EQ(additionalData["DEVICE_NAME"], "abc_pseq");
584 EXPECT_EQ(additionalData["GPIO_VALUES"], "[1, 1, 0]");
585 EXPECT_EQ(additionalData["RAIL_NAME"], "VDD");
586 EXPECT_EQ(additionalData["READ_VOUT"], "1.1");
587 EXPECT_EQ(additionalData["VOUT_UV_FAULT_LIMIT"], "1.2");
588 EXPECT_EQ(additionalData["STATUS_WORD"], "0xbeef");
589 }
590
Shawn McCarney472101c2024-04-17 16:31:09 -0500591 // Exception is thrown during pgood fault detection
592 {
593 std::vector<std::unique_ptr<Rail>> rails{};
594 rails.emplace_back(createRailGPIO("PSU", true, 2));
Shawn McCarney16275832024-06-27 10:14:11 -0500595 rails.emplace_back(createRailOutputVoltage("VDD", false, 5));
Shawn McCarney472101c2024-04-17 16:31:09 -0500596 rails.emplace_back(createRailStatusVout("VIO", false, 7));
597 StandardDeviceImpl device{"abc_pseq", std::move(rails)};
598
Shawn McCarney16275832024-06-27 10:14:11 -0500599 EXPECT_CALL(device, prepareForPgoodFaultDetection).Times(1);
Shawn McCarney472101c2024-04-17 16:31:09 -0500600 std::vector<int> gpioValues{1, 1, 1};
601 EXPECT_CALL(device, getGPIOValues)
602 .Times(1)
603 .WillOnce(Return(gpioValues));
Shawn McCarney16275832024-06-27 10:14:11 -0500604 EXPECT_CALL(device, getReadVout(5)).Times(0);
605 EXPECT_CALL(device, getVoutUVFaultLimit(5)).Times(0);
606 EXPECT_CALL(device, getStatusVout(7))
Shawn McCarney472101c2024-04-17 16:31:09 -0500607 .Times(1)
608 .WillOnce(Throw(std::runtime_error{"File does not exist"}));
Shawn McCarney472101c2024-04-17 16:31:09 -0500609
610 MockServices services{};
611
612 std::string powerSupplyError{};
613 std::map<std::string, std::string> additionalData{};
614 try
615 {
616 device.findPgoodFault(services, powerSupplyError, additionalData);
617 ADD_FAILURE() << "Should not have reached this line.";
618 }
619 catch (const std::exception& e)
620 {
621 EXPECT_STREQ(
622 e.what(),
623 "Unable to determine if a pgood fault occurred in device abc_pseq: "
Shawn McCarney16275832024-06-27 10:14:11 -0500624 "Unable to read STATUS_VOUT value for rail VIO: "
Shawn McCarney472101c2024-04-17 16:31:09 -0500625 "File does not exist");
626 }
627 }
628}