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 "mock_gpio.hpp"
18 #include "mock_services.hpp"
19 #include "rail.hpp"
20 #include "services.hpp"
21 #include "standard_device.hpp"
22
23 #include <cstdint>
24 #include <map>
25 #include <memory>
26 #include <optional>
27 #include <string>
28 #include <utility>
29 #include <vector>
30
31 #include <gmock/gmock.h>
32 #include <gtest/gtest.h>
33
34 using namespace phosphor::power::sequencer;
35
36 using ::testing::Return;
37 using ::testing::Throw;
38
39 /**
40 * @class StandardDeviceImpl
41 *
42 * Concrete subclass of the StandardDevice abstract class.
43 *
44 * This subclass is required for two reasons:
45 * - StandardDevice has some pure virtual methods so it cannot be instantiated.
46 * - The pure virtual methods provide the PMBus and GPIO information. Mocking
47 * these makes it possible to test the pgood fault detection algorithm.
48 *
49 * This class is not intended to be used outside of this file. It is
50 * implementation detail for testing the the StandardDevice class.
51 */
52 class StandardDeviceImpl : public StandardDevice
53 {
54 public:
55 // Specify which compiler-generated methods we want
56 StandardDeviceImpl() = delete;
57 StandardDeviceImpl(const StandardDeviceImpl&) = delete;
58 StandardDeviceImpl(StandardDeviceImpl&&) = delete;
59 StandardDeviceImpl& operator=(const StandardDeviceImpl&) = delete;
60 StandardDeviceImpl& operator=(StandardDeviceImpl&&) = delete;
61 virtual ~StandardDeviceImpl() = default;
62
63 // Constructor just calls StandardDevice constructor
StandardDeviceImpl(const std::string & name,uint8_t bus,uint16_t address,const std::string & powerControlGPIOName,const std::string & powerGoodGPIOName,std::vector<std::unique_ptr<Rail>> rails,Services & services)64 explicit StandardDeviceImpl(
65 const std::string& name, uint8_t bus, uint16_t address,
66 const std::string& powerControlGPIOName,
67 const std::string& powerGoodGPIOName,
68 std::vector<std::unique_ptr<Rail>> rails, Services& services) :
69 StandardDevice(name, bus, address, powerControlGPIOName,
70 powerGoodGPIOName, std::move(rails), services)
71 {}
72
73 // Mock pure virtual methods
74 MOCK_METHOD(std::vector<int>, getGPIOValues, (Services & services),
75 (override));
76 MOCK_METHOD(uint16_t, getStatusWord, (uint8_t page), (override));
77 MOCK_METHOD(uint8_t, getStatusVout, (uint8_t page), (override));
78 MOCK_METHOD(double, getReadVout, (uint8_t page), (override));
79 MOCK_METHOD(double, getVoutUVFaultLimit, (uint8_t page), (override));
80
81 // Override empty implementation with mock so we can verify it is called
82 MOCK_METHOD(void, prepareForPgoodFaultDetection, (Services & services),
83 (override));
84 };
85
86 /**
87 * Creates a Rail object that checks for a pgood fault using STATUS_VOUT.
88 *
89 * @param name Unique name for the rail
90 * @param isPowerSupplyRail Specifies whether the rail is produced by a
91 power supply
92 * @param pageNum PMBus PAGE number of the rail
93 * @return Rail object
94 */
createRailStatusVout(const std::string & name,bool isPowerSupplyRail,uint8_t pageNum)95 std::unique_ptr<Rail> createRailStatusVout(
96 const std::string& name, bool isPowerSupplyRail, uint8_t pageNum)
97 {
98 std::optional<std::string> presence{};
99 std::optional<uint8_t> page{pageNum};
100 bool checkStatusVout{true};
101 bool compareVoltageToLimit{false};
102 std::optional<PgoodGPIO> gpio{};
103 return std::make_unique<Rail>(name, presence, page, isPowerSupplyRail,
104 checkStatusVout, compareVoltageToLimit, gpio);
105 }
106
107 /**
108 * Creates a Rail object that checks for a pgood fault using a GPIO.
109 *
110 * @param name Unique name for the rail
111 * @param isPowerSupplyRail Specifies whether the rail is produced by a
112 power supply
113 * @param gpio GPIO line to read to determine the pgood status of the rail
114 * @return Rail object
115 */
createRailGPIO(const std::string & name,bool isPowerSupplyRail,unsigned int gpioLine)116 std::unique_ptr<Rail> createRailGPIO(
117 const std::string& name, bool isPowerSupplyRail, unsigned int gpioLine)
118 {
119 std::optional<std::string> presence{};
120 std::optional<uint8_t> page{};
121 bool checkStatusVout{false};
122 bool compareVoltageToLimit{false};
123 bool activeLow{false};
124 std::optional<PgoodGPIO> gpio{PgoodGPIO{gpioLine, activeLow}};
125 return std::make_unique<Rail>(name, presence, page, isPowerSupplyRail,
126 checkStatusVout, compareVoltageToLimit, gpio);
127 }
128
129 /**
130 * Creates a Rail object that checks for a pgood fault using output voltage.
131 *
132 * @param name Unique name for the rail
133 * @param isPowerSupplyRail Specifies whether the rail is produced by a
134 power supply
135 * @param pageNum PMBus PAGE number of the rail
136 * @return Rail object
137 */
createRailOutputVoltage(const std::string & name,bool isPowerSupplyRail,uint8_t pageNum)138 std::unique_ptr<Rail> createRailOutputVoltage(
139 const std::string& name, bool isPowerSupplyRail, uint8_t pageNum)
140 {
141 std::optional<std::string> presence{};
142 std::optional<uint8_t> page{pageNum};
143 bool checkStatusVout{false};
144 bool compareVoltageToLimit{true};
145 std::optional<PgoodGPIO> gpio{};
146 return std::make_unique<Rail>(name, presence, page, isPowerSupplyRail,
147 checkStatusVout, compareVoltageToLimit, gpio);
148 }
149
TEST(StandardDeviceTests,Constructor)150 TEST(StandardDeviceTests, Constructor)
151 {
152 // Test where works: Empty vector of rails
153 {
154 std::string name{"xyz_pseq"};
155 uint8_t bus{3};
156 uint16_t address{0x72};
157 std::string powerControlGPIOName{"power-chassis-control"};
158 std::string powerGoodGPIOName{"power-chassis-good"};
159 std::vector<std::unique_ptr<Rail>> rails{};
160 MockServices services;
161 StandardDeviceImpl device{
162 name,
163 bus,
164 address,
165 powerControlGPIOName,
166 powerGoodGPIOName,
167 std::move(rails),
168 services};
169
170 EXPECT_EQ(device.getName(), name);
171 EXPECT_EQ(device.getBus(), bus);
172 EXPECT_EQ(device.getAddress(), address);
173 EXPECT_EQ(device.getPowerControlGPIOName(), powerControlGPIOName);
174 EXPECT_EQ(device.getPowerGoodGPIOName(), powerGoodGPIOName);
175 EXPECT_TRUE(device.getRails().empty());
176 }
177
178 // Test where works: Non-empty vector of rails
179 {
180 std::string name{"abc_pseq"};
181 uint8_t bus{0};
182 uint16_t address{0x23};
183 std::string powerControlGPIOName{"power-chassis-control"};
184 std::string powerGoodGPIOName{"power-chassis-good"};
185 std::vector<std::unique_ptr<Rail>> rails{};
186 rails.emplace_back(createRailGPIO("PSU", true, 3));
187 rails.emplace_back(createRailOutputVoltage("VDD", false, 5));
188 rails.emplace_back(createRailStatusVout("VIO", false, 7));
189 MockServices services;
190 StandardDeviceImpl device{
191 name,
192 bus,
193 address,
194 powerControlGPIOName,
195 powerGoodGPIOName,
196 std::move(rails),
197 services};
198
199 EXPECT_EQ(device.getName(), name);
200 EXPECT_EQ(device.getBus(), bus);
201 EXPECT_EQ(device.getAddress(), address);
202 EXPECT_EQ(device.getPowerControlGPIOName(), powerControlGPIOName);
203 EXPECT_EQ(device.getPowerGoodGPIOName(), powerGoodGPIOName);
204 EXPECT_EQ(device.getRails().size(), 3);
205 EXPECT_EQ(device.getRails()[0]->getName(), "PSU");
206 EXPECT_EQ(device.getRails()[1]->getName(), "VDD");
207 EXPECT_EQ(device.getRails()[2]->getName(), "VIO");
208 }
209 }
210
TEST(StandardDeviceTests,GetName)211 TEST(StandardDeviceTests, GetName)
212 {
213 std::string name{"xyz_pseq"};
214 uint8_t bus{0};
215 uint16_t address{0x23};
216 std::string powerControlGPIOName{"power-chassis-control"};
217 std::string powerGoodGPIOName{"power-chassis-good"};
218 std::vector<std::unique_ptr<Rail>> rails{};
219 MockServices services;
220 StandardDeviceImpl device{
221 name,
222 bus,
223 address,
224 powerControlGPIOName,
225 powerGoodGPIOName,
226 std::move(rails),
227 services};
228
229 EXPECT_EQ(device.getName(), name);
230 }
231
TEST(StandardDeviceTests,GetBus)232 TEST(StandardDeviceTests, GetBus)
233 {
234 std::string name{"abc_pseq"};
235 uint8_t bus{1};
236 uint16_t address{0x23};
237 std::string powerControlGPIOName{"power-chassis-control"};
238 std::string powerGoodGPIOName{"power-chassis-good"};
239 std::vector<std::unique_ptr<Rail>> rails{};
240 MockServices services;
241 StandardDeviceImpl device{
242 name,
243 bus,
244 address,
245 powerControlGPIOName,
246 powerGoodGPIOName,
247 std::move(rails),
248 services};
249
250 EXPECT_EQ(device.getBus(), bus);
251 }
252
TEST(StandardDeviceTests,GetAddress)253 TEST(StandardDeviceTests, GetAddress)
254 {
255 std::string name{"abc_pseq"};
256 uint8_t bus{1};
257 uint16_t address{0x24};
258 std::string powerControlGPIOName{"power-chassis-control"};
259 std::string powerGoodGPIOName{"power-chassis-good"};
260 std::vector<std::unique_ptr<Rail>> rails{};
261 MockServices services;
262 StandardDeviceImpl device{
263 name,
264 bus,
265 address,
266 powerControlGPIOName,
267 powerGoodGPIOName,
268 std::move(rails),
269 services};
270
271 EXPECT_EQ(device.getAddress(), address);
272 }
273
TEST(StandardDeviceTests,GetPowerControlGPIOName)274 TEST(StandardDeviceTests, GetPowerControlGPIOName)
275 {
276 std::string name{"xyz_pseq"};
277 uint8_t bus{0};
278 uint16_t address{0x23};
279 std::string powerControlGPIOName{"power-on"};
280 std::string powerGoodGPIOName{"chassis-pgood"};
281 std::vector<std::unique_ptr<Rail>> rails{};
282 MockServices services;
283 StandardDeviceImpl device{
284 name,
285 bus,
286 address,
287 powerControlGPIOName,
288 powerGoodGPIOName,
289 std::move(rails),
290 services};
291
292 EXPECT_EQ(device.getPowerControlGPIOName(), powerControlGPIOName);
293 }
294
TEST(StandardDeviceTests,GetPowerGoodGPIOName)295 TEST(StandardDeviceTests, GetPowerGoodGPIOName)
296 {
297 std::string name{"xyz_pseq"};
298 uint8_t bus{0};
299 uint16_t address{0x23};
300 std::string powerControlGPIOName{"power-on"};
301 std::string powerGoodGPIOName{"chassis-pgood"};
302 std::vector<std::unique_ptr<Rail>> rails{};
303 MockServices services;
304 StandardDeviceImpl device{
305 name,
306 bus,
307 address,
308 powerControlGPIOName,
309 powerGoodGPIOName,
310 std::move(rails),
311 services};
312
313 EXPECT_EQ(device.getPowerGoodGPIOName(), powerGoodGPIOName);
314 }
315
TEST(StandardDeviceTests,GetRails)316 TEST(StandardDeviceTests, GetRails)
317 {
318 // Empty vector of rails
319 {
320 std::string name{"xyz_pseq"};
321 uint8_t bus{0};
322 uint16_t address{0x23};
323 std::string powerControlGPIOName{"power-chassis-control"};
324 std::string powerGoodGPIOName{"power-chassis-good"};
325 std::vector<std::unique_ptr<Rail>> rails{};
326 MockServices services;
327 StandardDeviceImpl device{
328 name,
329 bus,
330 address,
331 powerControlGPIOName,
332 powerGoodGPIOName,
333 std::move(rails),
334 services};
335
336 EXPECT_TRUE(device.getRails().empty());
337 }
338
339 // Non-empty vector of rails
340 {
341 std::string name{"abc_pseq"};
342 uint8_t bus{0};
343 uint16_t address{0x23};
344 std::string powerControlGPIOName{"power-chassis-control"};
345 std::string powerGoodGPIOName{"power-chassis-good"};
346 std::vector<std::unique_ptr<Rail>> rails{};
347 rails.emplace_back(createRailGPIO("PSU", true, 3));
348 rails.emplace_back(createRailOutputVoltage("VDD", false, 5));
349 rails.emplace_back(createRailStatusVout("VIO", false, 7));
350 MockServices services;
351 StandardDeviceImpl device{
352 name,
353 bus,
354 address,
355 powerControlGPIOName,
356 powerGoodGPIOName,
357 std::move(rails),
358 services};
359
360 EXPECT_EQ(device.getRails().size(), 3);
361 EXPECT_EQ(device.getRails()[0]->getName(), "PSU");
362 EXPECT_EQ(device.getRails()[1]->getName(), "VDD");
363 EXPECT_EQ(device.getRails()[2]->getName(), "VIO");
364 }
365 }
366
TEST(StandardDeviceTests,GetPowerControlGPIO)367 TEST(StandardDeviceTests, GetPowerControlGPIO)
368 {
369 std::string name{"xyz_pseq"};
370 uint8_t bus{0};
371 uint16_t address{0x23};
372 std::string powerControlGPIOName{"power-on"};
373 std::string powerGoodGPIOName{"chassis-pgood"};
374 std::vector<std::unique_ptr<Rail>> rails{};
375 MockServices services;
376 StandardDeviceImpl device{
377 name,
378 bus,
379 address,
380 powerControlGPIOName,
381 powerGoodGPIOName,
382 std::move(rails),
383 services};
384
385 MockGPIO& gpio = static_cast<MockGPIO&>(device.getPowerControlGPIO());
386 EXPECT_CALL(gpio, setValue(1)).Times(1);
387 device.powerOn();
388 }
389
TEST(StandardDeviceTests,GetPowerGoodGPIO)390 TEST(StandardDeviceTests, GetPowerGoodGPIO)
391 {
392 std::string name{"xyz_pseq"};
393 uint8_t bus{0};
394 uint16_t address{0x23};
395 std::string powerControlGPIOName{"power-on"};
396 std::string powerGoodGPIOName{"chassis-pgood"};
397 std::vector<std::unique_ptr<Rail>> rails{};
398 MockServices services;
399 StandardDeviceImpl device{
400 name,
401 bus,
402 address,
403 powerControlGPIOName,
404 powerGoodGPIOName,
405 std::move(rails),
406 services};
407
408 MockGPIO& gpio = static_cast<MockGPIO&>(device.getPowerGoodGPIO());
409 EXPECT_CALL(gpio, getValue()).Times(1).WillOnce(Return(0));
410 EXPECT_FALSE(device.getPowerGood());
411 }
412
TEST(StandardDeviceTests,PowerOn)413 TEST(StandardDeviceTests, PowerOn)
414 {
415 // Test where works
416 {
417 std::string name{"xyz_pseq"};
418 uint8_t bus{0};
419 uint16_t address{0x23};
420 std::string powerControlGPIOName{"power-on"};
421 std::string powerGoodGPIOName{"chassis-pgood"};
422 std::vector<std::unique_ptr<Rail>> rails{};
423 MockServices services;
424 StandardDeviceImpl device{
425 name,
426 bus,
427 address,
428 powerControlGPIOName,
429 powerGoodGPIOName,
430 std::move(rails),
431 services};
432
433 MockGPIO& gpio = static_cast<MockGPIO&>(device.getPowerControlGPIO());
434 EXPECT_CALL(gpio, requestWrite(1)).Times(1);
435 EXPECT_CALL(gpio, setValue(1)).Times(1);
436 EXPECT_CALL(gpio, release()).Times(1);
437 device.powerOn();
438 }
439
440 // Test where fails with exception
441 try
442 {
443 std::string name{"xyz_pseq"};
444 uint8_t bus{0};
445 uint16_t address{0x23};
446 std::string powerControlGPIOName{"power-on"};
447 std::string powerGoodGPIOName{"chassis-pgood"};
448 std::vector<std::unique_ptr<Rail>> rails{};
449 MockServices services;
450 StandardDeviceImpl device{
451 name,
452 bus,
453 address,
454 powerControlGPIOName,
455 powerGoodGPIOName,
456 std::move(rails),
457 services};
458
459 MockGPIO& gpio = static_cast<MockGPIO&>(device.getPowerControlGPIO());
460 EXPECT_CALL(gpio, requestWrite(1))
461 .Times(1)
462 .WillOnce(Throw(std::runtime_error{"Unable to write GPIO"}));
463 device.powerOn();
464 ADD_FAILURE() << "Should not have reached this line.";
465 }
466 catch (const std::exception& e)
467 {
468 EXPECT_STREQ(e.what(), "Unable to write GPIO");
469 }
470 }
471
TEST(StandardDeviceTests,PowerOff)472 TEST(StandardDeviceTests, PowerOff)
473 {
474 // Test where works
475 {
476 std::string name{"xyz_pseq"};
477 uint8_t bus{0};
478 uint16_t address{0x23};
479 std::string powerControlGPIOName{"power-on"};
480 std::string powerGoodGPIOName{"chassis-pgood"};
481 std::vector<std::unique_ptr<Rail>> rails{};
482 MockServices services;
483 StandardDeviceImpl device{
484 name,
485 bus,
486 address,
487 powerControlGPIOName,
488 powerGoodGPIOName,
489 std::move(rails),
490 services};
491
492 MockGPIO& gpio = static_cast<MockGPIO&>(device.getPowerControlGPIO());
493 EXPECT_CALL(gpio, requestWrite(0)).Times(1);
494 EXPECT_CALL(gpio, setValue(0)).Times(1);
495 EXPECT_CALL(gpio, release()).Times(1);
496 device.powerOff();
497 }
498
499 // Test where fails with exception
500 try
501 {
502 std::string name{"xyz_pseq"};
503 uint8_t bus{0};
504 uint16_t address{0x23};
505 std::string powerControlGPIOName{"power-on"};
506 std::string powerGoodGPIOName{"chassis-pgood"};
507 std::vector<std::unique_ptr<Rail>> rails{};
508 MockServices services;
509 StandardDeviceImpl device{
510 name,
511 bus,
512 address,
513 powerControlGPIOName,
514 powerGoodGPIOName,
515 std::move(rails),
516 services};
517
518 MockGPIO& gpio = static_cast<MockGPIO&>(device.getPowerControlGPIO());
519 EXPECT_CALL(gpio, requestWrite(0)).Times(1);
520 EXPECT_CALL(gpio, setValue(0))
521 .Times(1)
522 .WillOnce(Throw(std::runtime_error{"Unable to write GPIO"}));
523 device.powerOff();
524 ADD_FAILURE() << "Should not have reached this line.";
525 }
526 catch (const std::exception& e)
527 {
528 EXPECT_STREQ(e.what(), "Unable to write GPIO");
529 }
530 }
531
TEST(StandardDeviceTests,GetPowerGood)532 TEST(StandardDeviceTests, GetPowerGood)
533 {
534 // Test where works: Value is false
535 {
536 std::string name{"xyz_pseq"};
537 uint8_t bus{0};
538 uint16_t address{0x23};
539 std::string powerControlGPIOName{"power-on"};
540 std::string powerGoodGPIOName{"chassis-pgood"};
541 std::vector<std::unique_ptr<Rail>> rails{};
542 MockServices services;
543 StandardDeviceImpl device{
544 name,
545 bus,
546 address,
547 powerControlGPIOName,
548 powerGoodGPIOName,
549 std::move(rails),
550 services};
551
552 MockGPIO& gpio = static_cast<MockGPIO&>(device.getPowerGoodGPIO());
553 EXPECT_CALL(gpio, getValue()).Times(1).WillOnce(Return(0));
554 EXPECT_FALSE(device.getPowerGood());
555 }
556
557 // Test where works: Value is true
558 {
559 std::string name{"xyz_pseq"};
560 uint8_t bus{0};
561 uint16_t address{0x23};
562 std::string powerControlGPIOName{"power-on"};
563 std::string powerGoodGPIOName{"chassis-pgood"};
564 std::vector<std::unique_ptr<Rail>> rails{};
565 MockServices services;
566 StandardDeviceImpl device{
567 name,
568 bus,
569 address,
570 powerControlGPIOName,
571 powerGoodGPIOName,
572 std::move(rails),
573 services};
574
575 MockGPIO& gpio = static_cast<MockGPIO&>(device.getPowerGoodGPIO());
576 EXPECT_CALL(gpio, getValue()).Times(1).WillOnce(Return(1));
577 EXPECT_TRUE(device.getPowerGood());
578 }
579
580 // Test where fails with exception
581 try
582 {
583 std::string name{"xyz_pseq"};
584 uint8_t bus{0};
585 uint16_t address{0x23};
586 std::string powerControlGPIOName{"power-on"};
587 std::string powerGoodGPIOName{"chassis-pgood"};
588 std::vector<std::unique_ptr<Rail>> rails{};
589 MockServices services;
590 StandardDeviceImpl device{
591 name,
592 bus,
593 address,
594 powerControlGPIOName,
595 powerGoodGPIOName,
596 std::move(rails),
597 services};
598
599 MockGPIO& gpio = static_cast<MockGPIO&>(device.getPowerGoodGPIO());
600 EXPECT_CALL(gpio, getValue())
601 .Times(1)
602 .WillOnce(Throw(std::runtime_error{"Unable to read GPIO"}));
603 device.getPowerGood();
604 ADD_FAILURE() << "Should not have reached this line.";
605 }
606 catch (const std::exception& e)
607 {
608 EXPECT_STREQ(e.what(), "Unable to read GPIO");
609 }
610 }
611
TEST(StandardDeviceTests,FindPgoodFault)612 TEST(StandardDeviceTests, FindPgoodFault)
613 {
614 // No rail has a pgood fault
615 {
616 std::string name{"abc_pseq"};
617 uint8_t bus{0};
618 uint16_t address{0x23};
619 std::string powerControlGPIOName{"power-chassis-control"};
620 std::string powerGoodGPIOName{"power-chassis-good"};
621 std::vector<std::unique_ptr<Rail>> rails{};
622 rails.emplace_back(createRailGPIO("PSU", true, 2));
623 rails.emplace_back(createRailOutputVoltage("VDD", false, 5));
624 rails.emplace_back(createRailStatusVout("VIO", false, 7));
625 MockServices services;
626 StandardDeviceImpl device{
627 name,
628 bus,
629 address,
630 powerControlGPIOName,
631 powerGoodGPIOName,
632 std::move(rails),
633 services};
634
635 EXPECT_CALL(device, prepareForPgoodFaultDetection).Times(1);
636 std::vector<int> gpioValues{1, 1, 1};
637 EXPECT_CALL(device, getGPIOValues)
638 .Times(1)
639 .WillOnce(Return(gpioValues));
640 EXPECT_CALL(device, getReadVout(5)).Times(1).WillOnce(Return(1.2));
641 EXPECT_CALL(device, getVoutUVFaultLimit(5))
642 .Times(1)
643 .WillOnce(Return(1.1));
644 EXPECT_CALL(device, getStatusVout(7)).Times(1).WillOnce(Return(0x00));
645
646 std::string powerSupplyError{};
647 std::map<std::string, std::string> additionalData{};
648 std::string error =
649 device.findPgoodFault(services, powerSupplyError, additionalData);
650 EXPECT_TRUE(error.empty());
651 EXPECT_EQ(additionalData.size(), 0);
652 }
653
654 // First rail has a pgood fault detected via GPIO
655 // Is a PSU rail: No PSU error specified
656 {
657 std::string name{"abc_pseq"};
658 uint8_t bus{0};
659 uint16_t address{0x23};
660 std::string powerControlGPIOName{"power-chassis-control"};
661 std::string powerGoodGPIOName{"power-chassis-good"};
662 std::vector<std::unique_ptr<Rail>> rails{};
663 rails.emplace_back(createRailGPIO("PSU", true, 2));
664 rails.emplace_back(createRailOutputVoltage("VDD", false, 5));
665 rails.emplace_back(createRailStatusVout("VIO", false, 7));
666 MockServices services;
667 StandardDeviceImpl device{
668 name,
669 bus,
670 address,
671 powerControlGPIOName,
672 powerGoodGPIOName,
673 std::move(rails),
674 services};
675
676 EXPECT_CALL(device, prepareForPgoodFaultDetection).Times(1);
677 std::vector<int> gpioValues{1, 1, 0};
678 EXPECT_CALL(device, getGPIOValues)
679 .Times(1)
680 .WillOnce(Return(gpioValues));
681 EXPECT_CALL(device, getReadVout(5)).Times(0);
682 EXPECT_CALL(device, getVoutUVFaultLimit(5)).Times(0);
683 EXPECT_CALL(device, getStatusVout(7)).Times(1).WillOnce(Return(0x00));
684
685 EXPECT_CALL(services,
686 logInfoMsg("Device abc_pseq GPIO values: [1, 1, 0]"))
687 .Times(1);
688 EXPECT_CALL(
689 services,
690 logErrorMsg(
691 "Pgood fault found in rail monitored by device abc_pseq"))
692 .Times(1);
693 EXPECT_CALL(services, logErrorMsg("Pgood fault detected in rail PSU"))
694 .Times(1);
695 EXPECT_CALL(
696 services,
697 logErrorMsg(
698 "Rail PSU pgood GPIO line offset 2 has inactive value 0"))
699 .Times(1);
700
701 std::string powerSupplyError{};
702 std::map<std::string, std::string> additionalData{};
703 std::string error =
704 device.findPgoodFault(services, powerSupplyError, additionalData);
705 EXPECT_EQ(error,
706 "xyz.openbmc_project.Power.Error.PowerSequencerVoltageFault");
707 EXPECT_EQ(additionalData.size(), 5);
708 EXPECT_EQ(additionalData["DEVICE_NAME"], "abc_pseq");
709 EXPECT_EQ(additionalData["GPIO_VALUES"], "[1, 1, 0]");
710 EXPECT_EQ(additionalData["RAIL_NAME"], "PSU");
711 EXPECT_EQ(additionalData["GPIO_LINE"], "2");
712 EXPECT_EQ(additionalData["GPIO_VALUE"], "0");
713 }
714
715 // First rail has a pgood fault detected via GPIO
716 // Is a PSU rail: PSU error specified
717 {
718 std::string name{"abc_pseq"};
719 uint8_t bus{0};
720 uint16_t address{0x23};
721 std::string powerControlGPIOName{"power-chassis-control"};
722 std::string powerGoodGPIOName{"power-chassis-good"};
723 std::vector<std::unique_ptr<Rail>> rails{};
724 rails.emplace_back(createRailGPIO("PSU", true, 2));
725 rails.emplace_back(createRailOutputVoltage("VDD", false, 5));
726 rails.emplace_back(createRailStatusVout("VIO", false, 7));
727 MockServices services;
728 StandardDeviceImpl device{
729 name,
730 bus,
731 address,
732 powerControlGPIOName,
733 powerGoodGPIOName,
734 std::move(rails),
735 services};
736
737 EXPECT_CALL(device, prepareForPgoodFaultDetection).Times(1);
738 std::vector<int> gpioValues{1, 1, 0};
739 EXPECT_CALL(device, getGPIOValues)
740 .Times(1)
741 .WillOnce(Return(gpioValues));
742 EXPECT_CALL(device, getReadVout(5)).Times(0);
743 EXPECT_CALL(device, getVoutUVFaultLimit(5)).Times(0);
744 EXPECT_CALL(device, getStatusVout(7)).Times(1).WillOnce(Return(0x00));
745
746 EXPECT_CALL(services,
747 logInfoMsg("Device abc_pseq GPIO values: [1, 1, 0]"))
748 .Times(1);
749 EXPECT_CALL(
750 services,
751 logErrorMsg(
752 "Pgood fault found in rail monitored by device abc_pseq"))
753 .Times(1);
754 EXPECT_CALL(services, logErrorMsg("Pgood fault detected in rail PSU"))
755 .Times(1);
756 EXPECT_CALL(
757 services,
758 logErrorMsg(
759 "Rail PSU pgood GPIO line offset 2 has inactive value 0"))
760 .Times(1);
761
762 std::string powerSupplyError{"Undervoltage fault: PSU1"};
763 std::map<std::string, std::string> additionalData{};
764 std::string error =
765 device.findPgoodFault(services, powerSupplyError, additionalData);
766 EXPECT_EQ(error, powerSupplyError);
767 EXPECT_EQ(additionalData.size(), 5);
768 EXPECT_EQ(additionalData["DEVICE_NAME"], "abc_pseq");
769 EXPECT_EQ(additionalData["GPIO_VALUES"], "[1, 1, 0]");
770 EXPECT_EQ(additionalData["RAIL_NAME"], "PSU");
771 EXPECT_EQ(additionalData["GPIO_LINE"], "2");
772 EXPECT_EQ(additionalData["GPIO_VALUE"], "0");
773 }
774
775 // Second rail has a pgood fault detected via output voltage
776 // Not a PSU rail: PSU error specified
777 {
778 std::string name{"abc_pseq"};
779 uint8_t bus{0};
780 uint16_t address{0x23};
781 std::string powerControlGPIOName{"power-chassis-control"};
782 std::string powerGoodGPIOName{"power-chassis-good"};
783 std::vector<std::unique_ptr<Rail>> rails{};
784 rails.emplace_back(createRailGPIO("PSU", true, 2));
785 rails.emplace_back(createRailOutputVoltage("VDD", false, 5));
786 rails.emplace_back(createRailStatusVout("VIO", false, 7));
787 MockServices services;
788 StandardDeviceImpl device{
789 name,
790 bus,
791 address,
792 powerControlGPIOName,
793 powerGoodGPIOName,
794 std::move(rails),
795 services};
796
797 EXPECT_CALL(device, prepareForPgoodFaultDetection).Times(1);
798 std::vector<int> gpioValues{1, 1, 1};
799 EXPECT_CALL(device, getGPIOValues)
800 .Times(1)
801 .WillOnce(Return(gpioValues));
802 EXPECT_CALL(device, getReadVout(5)).Times(1).WillOnce(Return(1.1));
803 EXPECT_CALL(device, getVoutUVFaultLimit(5))
804 .Times(1)
805 .WillOnce(Return(1.2));
806 EXPECT_CALL(device, getStatusVout(7)).Times(1).WillOnce(Return(0x00));
807 EXPECT_CALL(device, getStatusWord(5)).Times(1).WillOnce(Return(0xbeef));
808
809 EXPECT_CALL(services,
810 logInfoMsg("Device abc_pseq GPIO values: [1, 1, 1]"))
811 .Times(1);
812 EXPECT_CALL(
813 services,
814 logErrorMsg(
815 "Pgood fault found in rail monitored by device abc_pseq"))
816 .Times(1);
817 EXPECT_CALL(services, logInfoMsg("Rail VDD STATUS_WORD: 0xbeef"))
818 .Times(1);
819 EXPECT_CALL(services, logErrorMsg("Pgood fault detected in rail VDD"))
820 .Times(1);
821 EXPECT_CALL(
822 services,
823 logErrorMsg(
824 "Rail VDD output voltage 1.1V is <= UV fault limit 1.2V"))
825 .Times(1);
826
827 std::string powerSupplyError{"Undervoltage fault: PSU1"};
828 std::map<std::string, std::string> additionalData{};
829 std::string error =
830 device.findPgoodFault(services, powerSupplyError, additionalData);
831 EXPECT_EQ(error,
832 "xyz.openbmc_project.Power.Error.PowerSequencerVoltageFault");
833 EXPECT_EQ(additionalData.size(), 6);
834 EXPECT_EQ(additionalData["DEVICE_NAME"], "abc_pseq");
835 EXPECT_EQ(additionalData["GPIO_VALUES"], "[1, 1, 1]");
836 EXPECT_EQ(additionalData["RAIL_NAME"], "VDD");
837 EXPECT_EQ(additionalData["READ_VOUT"], "1.1");
838 EXPECT_EQ(additionalData["VOUT_UV_FAULT_LIMIT"], "1.2");
839 EXPECT_EQ(additionalData["STATUS_WORD"], "0xbeef");
840 }
841
842 // Third rail has a pgood fault detected via STATUS_VOUT
843 // Device returns 0 GPIO values
844 // Does not halt pgood fault detection because GPIO values not used by rails
845 {
846 std::string name{"abc_pseq"};
847 uint8_t bus{0};
848 uint16_t address{0x23};
849 std::string powerControlGPIOName{"power-chassis-control"};
850 std::string powerGoodGPIOName{"power-chassis-good"};
851 std::vector<std::unique_ptr<Rail>> rails{};
852 rails.emplace_back(createRailStatusVout("PSU", true, 3));
853 rails.emplace_back(createRailOutputVoltage("VDD", false, 5));
854 rails.emplace_back(createRailStatusVout("VIO", false, 7));
855 MockServices services;
856 StandardDeviceImpl device{
857 name,
858 bus,
859 address,
860 powerControlGPIOName,
861 powerGoodGPIOName,
862 std::move(rails),
863 services};
864
865 EXPECT_CALL(device, prepareForPgoodFaultDetection).Times(1);
866 std::vector<int> gpioValues{};
867 EXPECT_CALL(device, getGPIOValues)
868 .Times(1)
869 .WillOnce(Return(gpioValues));
870 EXPECT_CALL(device, getStatusVout(3)).Times(1).WillOnce(Return(0x00));
871 EXPECT_CALL(device, getReadVout(5)).Times(0);
872 EXPECT_CALL(device, getVoutUVFaultLimit(5)).Times(0);
873 EXPECT_CALL(device, getStatusVout(7)).Times(1).WillOnce(Return(0x11));
874 EXPECT_CALL(device, getStatusWord(7)).Times(1).WillOnce(Return(0xbeef));
875
876 EXPECT_CALL(
877 services,
878 logErrorMsg(
879 "Pgood fault found in rail monitored by device abc_pseq"))
880 .Times(1);
881 EXPECT_CALL(services, logInfoMsg("Rail VIO STATUS_WORD: 0xbeef"))
882 .Times(1);
883 EXPECT_CALL(services, logErrorMsg("Pgood fault detected in rail VIO"))
884 .Times(1);
885 EXPECT_CALL(
886 services,
887 logErrorMsg("Rail VIO has fault bits set in STATUS_VOUT: 0x11"))
888 .Times(1);
889
890 std::string powerSupplyError{};
891 std::map<std::string, std::string> additionalData{};
892 std::string error =
893 device.findPgoodFault(services, powerSupplyError, additionalData);
894 EXPECT_EQ(error,
895 "xyz.openbmc_project.Power.Error.PowerSequencerVoltageFault");
896 EXPECT_EQ(additionalData.size(), 4);
897 EXPECT_EQ(additionalData["DEVICE_NAME"], "abc_pseq");
898 EXPECT_EQ(additionalData["RAIL_NAME"], "VIO");
899 EXPECT_EQ(additionalData["STATUS_VOUT"], "0x11");
900 EXPECT_EQ(additionalData["STATUS_WORD"], "0xbeef");
901 }
902
903 // Third rail has a pgood fault detected via STATUS_VOUT
904 // Exception occurs trying to obtain GPIO values from device
905 // Does not halt pgood fault detection because GPIO values not used by rails
906 {
907 std::string name{"abc_pseq"};
908 uint8_t bus{0};
909 uint16_t address{0x23};
910 std::string powerControlGPIOName{"power-chassis-control"};
911 std::string powerGoodGPIOName{"power-chassis-good"};
912 std::vector<std::unique_ptr<Rail>> rails{};
913 rails.emplace_back(createRailStatusVout("PSU", true, 3));
914 rails.emplace_back(createRailOutputVoltage("VDD", false, 5));
915 rails.emplace_back(createRailStatusVout("VIO", false, 7));
916 MockServices services;
917 StandardDeviceImpl device{
918 name,
919 bus,
920 address,
921 powerControlGPIOName,
922 powerGoodGPIOName,
923 std::move(rails),
924 services};
925
926 EXPECT_CALL(device, prepareForPgoodFaultDetection).Times(1);
927 EXPECT_CALL(device, getGPIOValues)
928 .Times(1)
929 .WillOnce(Throw(std::runtime_error{"Unable to acquire GPIO line"}));
930 EXPECT_CALL(device, getStatusVout(3)).Times(1).WillOnce(Return(0x00));
931 EXPECT_CALL(device, getReadVout(5)).Times(0);
932 EXPECT_CALL(device, getVoutUVFaultLimit(5)).Times(0);
933 EXPECT_CALL(device, getStatusVout(7)).Times(1).WillOnce(Return(0x11));
934 EXPECT_CALL(device, getStatusWord(7)).Times(1).WillOnce(Return(0xbeef));
935
936 EXPECT_CALL(
937 services,
938 logErrorMsg(
939 "Pgood fault found in rail monitored by device abc_pseq"))
940 .Times(1);
941 EXPECT_CALL(services, logInfoMsg("Rail VIO STATUS_WORD: 0xbeef"))
942 .Times(1);
943 EXPECT_CALL(services, logErrorMsg("Pgood fault detected in rail VIO"))
944 .Times(1);
945 EXPECT_CALL(
946 services,
947 logErrorMsg("Rail VIO has fault bits set in STATUS_VOUT: 0x11"))
948 .Times(1);
949
950 std::string powerSupplyError{};
951 std::map<std::string, std::string> additionalData{};
952 std::string error =
953 device.findPgoodFault(services, powerSupplyError, additionalData);
954 EXPECT_EQ(error,
955 "xyz.openbmc_project.Power.Error.PowerSequencerVoltageFault");
956 EXPECT_EQ(additionalData.size(), 4);
957 EXPECT_EQ(additionalData["DEVICE_NAME"], "abc_pseq");
958 EXPECT_EQ(additionalData["RAIL_NAME"], "VIO");
959 EXPECT_EQ(additionalData["STATUS_VOUT"], "0x11");
960 EXPECT_EQ(additionalData["STATUS_WORD"], "0xbeef");
961 }
962
963 // All three rails appear to have a pgood fault. Verify third rail is
964 // selected, even though it is last in the power on sequence, because it is
965 // checked using STATUS_VOUT. That check happens before the other checks.
966 {
967 std::string name{"abc_pseq"};
968 uint8_t bus{0};
969 uint16_t address{0x23};
970 std::string powerControlGPIOName{"power-chassis-control"};
971 std::string powerGoodGPIOName{"power-chassis-good"};
972 std::vector<std::unique_ptr<Rail>> rails{};
973 rails.emplace_back(createRailGPIO("PSU", true, 2));
974 rails.emplace_back(createRailGPIO("VDD", false, 1));
975 rails.emplace_back(createRailStatusVout("VIO", false, 7));
976 MockServices services;
977 StandardDeviceImpl device{
978 name,
979 bus,
980 address,
981 powerControlGPIOName,
982 powerGoodGPIOName,
983 std::move(rails),
984 services};
985
986 EXPECT_CALL(device, prepareForPgoodFaultDetection).Times(1);
987 std::vector<int> gpioValues{0, 0, 0};
988 EXPECT_CALL(device, getGPIOValues)
989 .Times(1)
990 .WillOnce(Return(gpioValues));
991 EXPECT_CALL(device, getStatusVout(7)).Times(1).WillOnce(Return(0x11));
992 EXPECT_CALL(device, getStatusWord(7)).Times(1).WillOnce(Return(0xbeef));
993
994 EXPECT_CALL(services,
995 logInfoMsg("Device abc_pseq GPIO values: [0, 0, 0]"))
996 .Times(1);
997 EXPECT_CALL(
998 services,
999 logErrorMsg(
1000 "Pgood fault found in rail monitored by device abc_pseq"))
1001 .Times(1);
1002 EXPECT_CALL(services, logInfoMsg("Rail VIO STATUS_WORD: 0xbeef"))
1003 .Times(1);
1004 EXPECT_CALL(services, logErrorMsg("Pgood fault detected in rail VIO"))
1005 .Times(1);
1006 EXPECT_CALL(
1007 services,
1008 logErrorMsg("Rail VIO has fault bits set in STATUS_VOUT: 0x11"))
1009 .Times(1);
1010
1011 std::string powerSupplyError{};
1012 std::map<std::string, std::string> additionalData{};
1013 std::string error =
1014 device.findPgoodFault(services, powerSupplyError, additionalData);
1015 EXPECT_EQ(error,
1016 "xyz.openbmc_project.Power.Error.PowerSequencerVoltageFault");
1017 EXPECT_EQ(additionalData.size(), 5);
1018 EXPECT_EQ(additionalData["DEVICE_NAME"], "abc_pseq");
1019 EXPECT_EQ(additionalData["GPIO_VALUES"], "[0, 0, 0]");
1020 EXPECT_EQ(additionalData["RAIL_NAME"], "VIO");
1021 EXPECT_EQ(additionalData["STATUS_VOUT"], "0x11");
1022 EXPECT_EQ(additionalData["STATUS_WORD"], "0xbeef");
1023 }
1024
1025 // Two rails appear to have a pgood fault. One is found via output voltage
1026 // and one is found via a GPIO. Verify the first rail in the sequence with
1027 // a fault is selected.
1028 {
1029 std::string name{"abc_pseq"};
1030 uint8_t bus{0};
1031 uint16_t address{0x23};
1032 std::string powerControlGPIOName{"power-chassis-control"};
1033 std::string powerGoodGPIOName{"power-chassis-good"};
1034 std::vector<std::unique_ptr<Rail>> rails{};
1035 rails.emplace_back(createRailStatusVout("VIO", false, 7));
1036 rails.emplace_back(createRailOutputVoltage("VDD", false, 5));
1037 rails.emplace_back(createRailGPIO("PSU", true, 2));
1038 MockServices services;
1039 StandardDeviceImpl device{
1040 name,
1041 bus,
1042 address,
1043 powerControlGPIOName,
1044 powerGoodGPIOName,
1045 std::move(rails),
1046 services};
1047
1048 EXPECT_CALL(device, prepareForPgoodFaultDetection).Times(1);
1049 std::vector<int> gpioValues{1, 1, 0};
1050 EXPECT_CALL(device, getGPIOValues)
1051 .Times(1)
1052 .WillOnce(Return(gpioValues));
1053 EXPECT_CALL(device, getStatusVout(7)).Times(1).WillOnce(Return(0x00));
1054 EXPECT_CALL(device, getReadVout(5)).Times(1).WillOnce(Return(1.1));
1055 EXPECT_CALL(device, getVoutUVFaultLimit(5))
1056 .Times(1)
1057 .WillOnce(Return(1.2));
1058 EXPECT_CALL(device, getStatusWord(5)).Times(1).WillOnce(Return(0xbeef));
1059
1060 EXPECT_CALL(services,
1061 logInfoMsg("Device abc_pseq GPIO values: [1, 1, 0]"))
1062 .Times(1);
1063 EXPECT_CALL(
1064 services,
1065 logErrorMsg(
1066 "Pgood fault found in rail monitored by device abc_pseq"))
1067 .Times(1);
1068 EXPECT_CALL(services, logInfoMsg("Rail VDD STATUS_WORD: 0xbeef"))
1069 .Times(1);
1070 EXPECT_CALL(services, logErrorMsg("Pgood fault detected in rail VDD"))
1071 .Times(1);
1072 EXPECT_CALL(
1073 services,
1074 logErrorMsg(
1075 "Rail VDD output voltage 1.1V is <= UV fault limit 1.2V"))
1076 .Times(1);
1077
1078 std::string powerSupplyError{};
1079 std::map<std::string, std::string> additionalData{};
1080 std::string error =
1081 device.findPgoodFault(services, powerSupplyError, additionalData);
1082 EXPECT_EQ(error,
1083 "xyz.openbmc_project.Power.Error.PowerSequencerVoltageFault");
1084 EXPECT_EQ(additionalData.size(), 6);
1085 EXPECT_EQ(additionalData["DEVICE_NAME"], "abc_pseq");
1086 EXPECT_EQ(additionalData["GPIO_VALUES"], "[1, 1, 0]");
1087 EXPECT_EQ(additionalData["RAIL_NAME"], "VDD");
1088 EXPECT_EQ(additionalData["READ_VOUT"], "1.1");
1089 EXPECT_EQ(additionalData["VOUT_UV_FAULT_LIMIT"], "1.2");
1090 EXPECT_EQ(additionalData["STATUS_WORD"], "0xbeef");
1091 }
1092
1093 // Exception is thrown during pgood fault detection
1094 {
1095 std::string name{"abc_pseq"};
1096 uint8_t bus{0};
1097 uint16_t address{0x23};
1098 std::string powerControlGPIOName{"power-chassis-control"};
1099 std::string powerGoodGPIOName{"power-chassis-good"};
1100 std::vector<std::unique_ptr<Rail>> rails{};
1101 rails.emplace_back(createRailGPIO("PSU", true, 2));
1102 rails.emplace_back(createRailOutputVoltage("VDD", false, 5));
1103 rails.emplace_back(createRailStatusVout("VIO", false, 7));
1104 MockServices services;
1105 StandardDeviceImpl device{
1106 name,
1107 bus,
1108 address,
1109 powerControlGPIOName,
1110 powerGoodGPIOName,
1111 std::move(rails),
1112 services};
1113
1114 EXPECT_CALL(device, prepareForPgoodFaultDetection).Times(1);
1115 std::vector<int> gpioValues{1, 1, 1};
1116 EXPECT_CALL(device, getGPIOValues)
1117 .Times(1)
1118 .WillOnce(Return(gpioValues));
1119 EXPECT_CALL(device, getReadVout(5)).Times(0);
1120 EXPECT_CALL(device, getVoutUVFaultLimit(5)).Times(0);
1121 EXPECT_CALL(device, getStatusVout(7))
1122 .Times(1)
1123 .WillOnce(Throw(std::runtime_error{"File does not exist"}));
1124
1125 std::string powerSupplyError{};
1126 std::map<std::string, std::string> additionalData{};
1127 try
1128 {
1129 device.findPgoodFault(services, powerSupplyError, additionalData);
1130 ADD_FAILURE() << "Should not have reached this line.";
1131 }
1132 catch (const std::exception& e)
1133 {
1134 EXPECT_STREQ(
1135 e.what(),
1136 "Unable to determine if a pgood fault occurred in device abc_pseq: "
1137 "Unable to read STATUS_VOUT value for rail VIO: "
1138 "File does not exist");
1139 }
1140 }
1141 }
1142