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