xref: /openbmc/phosphor-power/phosphor-power-sequencer/test/ucd90x_device_tests.cpp (revision 7b7a5632c594d60f4620ca14379a766a56faf846)
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_pmbus.hpp"
18 #include "mock_services.hpp"
19 #include "pmbus.hpp"
20 #include "rail.hpp"
21 #include "services.hpp"
22 #include "ucd90x_device.hpp"
23 
24 #include <cstdint>
25 #include <exception>
26 #include <map>
27 #include <memory>
28 #include <optional>
29 #include <string>
30 #include <utility>
31 #include <vector>
32 
33 #include <gmock/gmock.h>
34 #include <gtest/gtest.h>
35 
36 using namespace phosphor::power::sequencer;
37 using namespace phosphor::pmbus;
38 
39 using ::testing::Return;
40 using ::testing::Throw;
41 
42 /**
43  * Creates a Rail object that checks for a pgood fault using a GPIO.
44  *
45  * @param name Unique name for the rail
46  * @param gpio GPIO line to read to determine the pgood status of the rail
47  * @return Rail object
48  */
49 static std::unique_ptr<Rail> createRail(const std::string& name,
50                                         unsigned int gpioLine)
51 {
52     std::optional<std::string> presence{};
53     std::optional<uint8_t> page{};
54     bool isPowerSupplyRail{false};
55     bool checkStatusVout{false};
56     bool compareVoltageToLimit{false};
57     bool activeLow{false};
58     std::optional<PgoodGPIO> gpio{PgoodGPIO{gpioLine, activeLow}};
59     return std::make_unique<Rail>(name, presence, page, isPowerSupplyRail,
60                                   checkStatusVout, compareVoltageToLimit, gpio);
61 }
62 
63 TEST(UCD90xDeviceTests, Constructor)
64 {
65     MockServices services;
66 
67     std::string name{"ucd90320"};
68     uint8_t bus{3};
69     uint16_t address{0x72};
70     std::string powerControlGPIOName{"power-chassis-control"};
71     std::string powerGoodGPIOName{"power-chassis-good"};
72     std::vector<std::unique_ptr<Rail>> rails;
73     rails.emplace_back(createRail("VDD", 5));
74     rails.emplace_back(createRail("VIO", 7));
75     UCD90xDevice device{name,
76                         bus,
77                         address,
78                         powerControlGPIOName,
79                         powerGoodGPIOName,
80                         std::move(rails),
81                         services};
82 
83     EXPECT_EQ(device.getName(), name);
84     EXPECT_EQ(device.getBus(), bus);
85     EXPECT_EQ(device.getAddress(), address);
86     EXPECT_EQ(device.getPowerControlGPIOName(), powerControlGPIOName);
87     EXPECT_EQ(device.getPowerGoodGPIOName(), powerGoodGPIOName);
88     EXPECT_EQ(device.getRails().size(), 2);
89     EXPECT_EQ(device.getRails()[0]->getName(), "VDD");
90     EXPECT_EQ(device.getRails()[1]->getName(), "VIO");
91     EXPECT_EQ(device.getDriverName(), "ucd9000");
92     EXPECT_EQ(device.getInstance(), 0);
93     EXPECT_NE(&(device.getPMBusInterface()), nullptr);
94 }
95 
96 TEST(UCD90xDeviceTests, GetMfrStatus)
97 {
98     // Test where works
99     {
100         MockServices services;
101 
102         std::string name{"ucd90320"};
103         uint8_t bus{3};
104         uint16_t address{0x72};
105         std::string powerControlGPIOName{"power-chassis-control"};
106         std::string powerGoodGPIOName{"power-chassis-good"};
107         std::vector<std::unique_ptr<Rail>> rails;
108         UCD90xDevice device{name,
109                             bus,
110                             address,
111                             powerControlGPIOName,
112                             powerGoodGPIOName,
113                             std::move(rails),
114                             services};
115 
116         MockPMBus& pmbus = static_cast<MockPMBus&>(device.getPMBusInterface());
117         uint64_t mfrStatus{0x123456789abcull};
118         EXPECT_CALL(pmbus, read("mfr_status", Type::HwmonDeviceDebug, true))
119             .Times(1)
120             .WillOnce(Return(mfrStatus));
121 
122         EXPECT_EQ(device.getMfrStatus(), mfrStatus);
123     }
124 
125     // Test where fails with exception
126     {
127         MockServices services;
128 
129         std::string name{"ucd90320"};
130         uint8_t bus{3};
131         uint16_t address{0x72};
132         std::string powerControlGPIOName{"power-chassis-control"};
133         std::string powerGoodGPIOName{"power-chassis-good"};
134         std::vector<std::unique_ptr<Rail>> rails;
135         UCD90xDevice device{name,
136                             bus,
137                             address,
138                             powerControlGPIOName,
139                             powerGoodGPIOName,
140                             std::move(rails),
141                             services};
142 
143         MockPMBus& pmbus = static_cast<MockPMBus&>(device.getPMBusInterface());
144         EXPECT_CALL(pmbus, read("mfr_status", Type::HwmonDeviceDebug, true))
145             .Times(1)
146             .WillOnce(Throw(std::runtime_error{"File does not exist"}));
147 
148         try
149         {
150             device.getMfrStatus();
151             ADD_FAILURE() << "Should not have reached this line.";
152         }
153         catch (const std::exception& e)
154         {
155             EXPECT_STREQ(e.what(),
156                          "Unable to read MFR_STATUS for device ucd90320: "
157                          "File does not exist");
158         }
159     }
160 }
161 
162 TEST(UCD90xDeviceTests, StorePgoodFaultDebugData)
163 {
164     // This is a protected method and cannot be called directly from a gtest.
165     // Call findPgoodFault() which calls storePgoodFaultDebugData().
166 
167     // Test where works
168     {
169         MockServices services;
170         std::vector<int> gpioValues{1, 1, 0};
171         EXPECT_CALL(services, getGPIOValues("ucd90320"))
172             .Times(1)
173             .WillOnce(Return(gpioValues));
174         EXPECT_CALL(services,
175                     logInfoMsg("Device ucd90320 GPIO values: [1, 1, 0]"))
176             .Times(1);
177         EXPECT_CALL(services,
178                     logInfoMsg("Device ucd90320 MFR_STATUS: 0x123456789abc"))
179             .Times(1);
180         EXPECT_CALL(
181             services,
182             logErrorMsg(
183                 "Pgood fault found in rail monitored by device ucd90320"))
184             .Times(1);
185         EXPECT_CALL(services, logErrorMsg("Pgood fault detected in rail VDD"))
186             .Times(1);
187         EXPECT_CALL(
188             services,
189             logErrorMsg(
190                 "Rail VDD pgood GPIO line offset 2 has inactive value 0"))
191             .Times(1);
192 
193         std::string name{"ucd90320"};
194         uint8_t bus{3};
195         uint16_t address{0x72};
196         std::string powerControlGPIOName{"power-chassis-control"};
197         std::string powerGoodGPIOName{"power-chassis-good"};
198         std::vector<std::unique_ptr<Rail>> rails;
199         rails.emplace_back(createRail("VDD", 2));
200         UCD90xDevice device{name,
201                             bus,
202                             address,
203                             powerControlGPIOName,
204                             powerGoodGPIOName,
205                             std::move(rails),
206                             services};
207 
208         MockPMBus& pmbus = static_cast<MockPMBus&>(device.getPMBusInterface());
209         EXPECT_CALL(pmbus, getPath(Type::Hwmon))
210             .Times(1)
211             .WillOnce(Return("/tmp"));
212         EXPECT_CALL(pmbus, read("mfr_status", Type::HwmonDeviceDebug, true))
213             .Times(1)
214             .WillOnce(Return(0x123456789abcull));
215 
216         // Call findPgoodFault() which calls storePgoodFaultDebugData()
217         std::string powerSupplyError{};
218         std::map<std::string, std::string> additionalData{};
219         std::string error =
220             device.findPgoodFault(services, powerSupplyError, additionalData);
221         EXPECT_EQ(error,
222                   "xyz.openbmc_project.Power.Error.PowerSequencerVoltageFault");
223         EXPECT_EQ(additionalData.size(), 6);
224         EXPECT_EQ(additionalData["MFR_STATUS"], "0x123456789abc");
225         EXPECT_EQ(additionalData["DEVICE_NAME"], "ucd90320");
226         EXPECT_EQ(additionalData["GPIO_VALUES"], "[1, 1, 0]");
227         EXPECT_EQ(additionalData["RAIL_NAME"], "VDD");
228         EXPECT_EQ(additionalData["GPIO_LINE"], "2");
229         EXPECT_EQ(additionalData["GPIO_VALUE"], "0");
230     }
231 
232     // Test where exception thrown trying to get MFR_STATUS
233     {
234         MockServices services;
235         std::vector<int> gpioValues{1, 1, 0};
236         EXPECT_CALL(services, getGPIOValues("ucd90320"))
237             .Times(1)
238             .WillOnce(Return(gpioValues));
239         EXPECT_CALL(services,
240                     logInfoMsg("Device ucd90320 GPIO values: [1, 1, 0]"))
241             .Times(1);
242         EXPECT_CALL(
243             services,
244             logErrorMsg(
245                 "Pgood fault found in rail monitored by device ucd90320"))
246             .Times(1);
247         EXPECT_CALL(services, logErrorMsg("Pgood fault detected in rail VDD"))
248             .Times(1);
249         EXPECT_CALL(
250             services,
251             logErrorMsg(
252                 "Rail VDD pgood GPIO line offset 2 has inactive value 0"))
253             .Times(1);
254 
255         std::string name{"ucd90320"};
256         uint8_t bus{3};
257         uint16_t address{0x72};
258         std::string powerControlGPIOName{"power-chassis-control"};
259         std::string powerGoodGPIOName{"power-chassis-good"};
260         std::vector<std::unique_ptr<Rail>> rails;
261         rails.emplace_back(createRail("VDD", 2));
262         UCD90xDevice device{name,
263                             bus,
264                             address,
265                             powerControlGPIOName,
266                             powerGoodGPIOName,
267                             std::move(rails),
268                             services};
269 
270         MockPMBus& pmbus = static_cast<MockPMBus&>(device.getPMBusInterface());
271         EXPECT_CALL(pmbus, getPath(Type::Hwmon))
272             .Times(1)
273             .WillOnce(Return("/tmp"));
274         EXPECT_CALL(pmbus, read("mfr_status", Type::HwmonDeviceDebug, true))
275             .Times(1)
276             .WillOnce(Throw(std::runtime_error{"File does not exist"}));
277 
278         // Call findPgoodFault() which calls storePgoodFaultDebugData()
279         std::string powerSupplyError{};
280         std::map<std::string, std::string> additionalData{};
281         std::string error =
282             device.findPgoodFault(services, powerSupplyError, additionalData);
283         EXPECT_EQ(error,
284                   "xyz.openbmc_project.Power.Error.PowerSequencerVoltageFault");
285         EXPECT_EQ(additionalData.size(), 5);
286         EXPECT_EQ(additionalData["DEVICE_NAME"], "ucd90320");
287         EXPECT_EQ(additionalData["GPIO_VALUES"], "[1, 1, 0]");
288         EXPECT_EQ(additionalData["RAIL_NAME"], "VDD");
289         EXPECT_EQ(additionalData["GPIO_LINE"], "2");
290         EXPECT_EQ(additionalData["GPIO_VALUE"], "0");
291     }
292 }
293