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 */
createRail(const std::string & name,unsigned int gpioLine)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<GPIO> gpio{GPIO{gpioLine, activeLow}};
59 return std::make_unique<Rail>(name, presence, page, isPowerSupplyRail,
60 checkStatusVout, compareVoltageToLimit, gpio);
61 }
62
TEST(UCD90xDeviceTests,Constructor)63 TEST(UCD90xDeviceTests, Constructor)
64 {
65 MockServices services;
66
67 std::string name{"ucd90320"};
68 std::vector<std::unique_ptr<Rail>> rails;
69 rails.emplace_back(createRail("VDD", 5));
70 rails.emplace_back(createRail("VIO", 7));
71 uint8_t bus{3};
72 uint16_t address{0x72};
73 UCD90xDevice device{name, std::move(rails), services, bus, address};
74
75 EXPECT_EQ(device.getName(), name);
76 EXPECT_EQ(device.getRails().size(), 2);
77 EXPECT_EQ(device.getRails()[0]->getName(), "VDD");
78 EXPECT_EQ(device.getRails()[1]->getName(), "VIO");
79 EXPECT_EQ(device.getBus(), bus);
80 EXPECT_EQ(device.getAddress(), address);
81 EXPECT_EQ(device.getDriverName(), "ucd9000");
82 EXPECT_EQ(device.getInstance(), 0);
83 EXPECT_NE(&(device.getPMBusInterface()), nullptr);
84 }
85
TEST(UCD90xDeviceTests,GetMfrStatus)86 TEST(UCD90xDeviceTests, GetMfrStatus)
87 {
88 // Test where works
89 {
90 MockServices services;
91
92 std::string name{"ucd90320"};
93 std::vector<std::unique_ptr<Rail>> rails;
94 uint8_t bus{3};
95 uint16_t address{0x72};
96 UCD90xDevice device{name, std::move(rails), services, bus, address};
97
98 MockPMBus& pmbus = static_cast<MockPMBus&>(device.getPMBusInterface());
99 uint64_t mfrStatus{0x123456789abcull};
100 EXPECT_CALL(pmbus, read("mfr_status", Type::HwmonDeviceDebug, true))
101 .Times(1)
102 .WillOnce(Return(mfrStatus));
103
104 EXPECT_EQ(device.getMfrStatus(), mfrStatus);
105 }
106
107 // Test where fails with exception
108 {
109 MockServices services;
110
111 std::string name{"ucd90320"};
112 std::vector<std::unique_ptr<Rail>> rails;
113 uint8_t bus{3};
114 uint16_t address{0x72};
115 UCD90xDevice device{name, std::move(rails), services, bus, address};
116
117 MockPMBus& pmbus = static_cast<MockPMBus&>(device.getPMBusInterface());
118 EXPECT_CALL(pmbus, read("mfr_status", Type::HwmonDeviceDebug, true))
119 .Times(1)
120 .WillOnce(Throw(std::runtime_error{"File does not exist"}));
121
122 try
123 {
124 device.getMfrStatus();
125 ADD_FAILURE() << "Should not have reached this line.";
126 }
127 catch (const std::exception& e)
128 {
129 EXPECT_STREQ(e.what(),
130 "Unable to read MFR_STATUS for device ucd90320: "
131 "File does not exist");
132 }
133 }
134 }
135
TEST(UCD90xDeviceTests,StorePgoodFaultDebugData)136 TEST(UCD90xDeviceTests, StorePgoodFaultDebugData)
137 {
138 // This is a protected method and cannot be called directly from a gtest.
139 // Call findPgoodFault() which calls storePgoodFaultDebugData().
140
141 // Test where works
142 {
143 MockServices services;
144 std::vector<int> gpioValues{1, 1, 0};
145 EXPECT_CALL(services, getGPIOValues("ucd90320"))
146 .Times(1)
147 .WillOnce(Return(gpioValues));
148 EXPECT_CALL(services,
149 logInfoMsg("Device ucd90320 GPIO values: [1, 1, 0]"))
150 .Times(1);
151 EXPECT_CALL(services,
152 logInfoMsg("Device ucd90320 MFR_STATUS: 0x123456789abc"))
153 .Times(1);
154 EXPECT_CALL(
155 services,
156 logErrorMsg(
157 "Pgood fault found in rail monitored by device ucd90320"))
158 .Times(1);
159 EXPECT_CALL(services, logErrorMsg("Pgood fault detected in rail VDD"))
160 .Times(1);
161 EXPECT_CALL(
162 services,
163 logErrorMsg(
164 "Rail VDD pgood GPIO line offset 2 has inactive value 0"))
165 .Times(1);
166
167 std::string name{"ucd90320"};
168 std::vector<std::unique_ptr<Rail>> rails;
169 rails.emplace_back(createRail("VDD", 2));
170 uint8_t bus{3};
171 uint16_t address{0x72};
172 UCD90xDevice device{name, std::move(rails), services, bus, address};
173
174 MockPMBus& pmbus = static_cast<MockPMBus&>(device.getPMBusInterface());
175 EXPECT_CALL(pmbus, getPath(Type::Hwmon))
176 .Times(1)
177 .WillOnce(Return("/tmp"));
178 EXPECT_CALL(pmbus, read("mfr_status", Type::HwmonDeviceDebug, true))
179 .Times(1)
180 .WillOnce(Return(0x123456789abcull));
181
182 // Call findPgoodFault() which calls storePgoodFaultDebugData()
183 std::string powerSupplyError{};
184 std::map<std::string, std::string> additionalData{};
185 std::string error =
186 device.findPgoodFault(services, powerSupplyError, additionalData);
187 EXPECT_EQ(error,
188 "xyz.openbmc_project.Power.Error.PowerSequencerVoltageFault");
189 EXPECT_EQ(additionalData.size(), 6);
190 EXPECT_EQ(additionalData["MFR_STATUS"], "0x123456789abc");
191 EXPECT_EQ(additionalData["DEVICE_NAME"], "ucd90320");
192 EXPECT_EQ(additionalData["GPIO_VALUES"], "[1, 1, 0]");
193 EXPECT_EQ(additionalData["RAIL_NAME"], "VDD");
194 EXPECT_EQ(additionalData["GPIO_LINE"], "2");
195 EXPECT_EQ(additionalData["GPIO_VALUE"], "0");
196 }
197
198 // Test where exception thrown trying to get MFR_STATUS
199 {
200 MockServices services;
201 std::vector<int> gpioValues{1, 1, 0};
202 EXPECT_CALL(services, getGPIOValues("ucd90320"))
203 .Times(1)
204 .WillOnce(Return(gpioValues));
205 EXPECT_CALL(services,
206 logInfoMsg("Device ucd90320 GPIO values: [1, 1, 0]"))
207 .Times(1);
208 EXPECT_CALL(
209 services,
210 logErrorMsg(
211 "Pgood fault found in rail monitored by device ucd90320"))
212 .Times(1);
213 EXPECT_CALL(services, logErrorMsg("Pgood fault detected in rail VDD"))
214 .Times(1);
215 EXPECT_CALL(
216 services,
217 logErrorMsg(
218 "Rail VDD pgood GPIO line offset 2 has inactive value 0"))
219 .Times(1);
220
221 std::string name{"ucd90320"};
222 std::vector<std::unique_ptr<Rail>> rails;
223 rails.emplace_back(createRail("VDD", 2));
224 uint8_t bus{3};
225 uint16_t address{0x72};
226 UCD90xDevice device{name, std::move(rails), services, bus, address};
227
228 MockPMBus& pmbus = static_cast<MockPMBus&>(device.getPMBusInterface());
229 EXPECT_CALL(pmbus, getPath(Type::Hwmon))
230 .Times(1)
231 .WillOnce(Return("/tmp"));
232 EXPECT_CALL(pmbus, read("mfr_status", Type::HwmonDeviceDebug, true))
233 .Times(1)
234 .WillOnce(Throw(std::runtime_error{"File does not exist"}));
235
236 // Call findPgoodFault() which calls storePgoodFaultDebugData()
237 std::string powerSupplyError{};
238 std::map<std::string, std::string> additionalData{};
239 std::string error =
240 device.findPgoodFault(services, powerSupplyError, additionalData);
241 EXPECT_EQ(error,
242 "xyz.openbmc_project.Power.Error.PowerSequencerVoltageFault");
243 EXPECT_EQ(additionalData.size(), 5);
244 EXPECT_EQ(additionalData["DEVICE_NAME"], "ucd90320");
245 EXPECT_EQ(additionalData["GPIO_VALUES"], "[1, 1, 0]");
246 EXPECT_EQ(additionalData["RAIL_NAME"], "VDD");
247 EXPECT_EQ(additionalData["GPIO_LINE"], "2");
248 EXPECT_EQ(additionalData["GPIO_VALUE"], "0");
249 }
250 }
251