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 "ucd90320_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  */
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 
61 TEST(UCD90320DeviceTests, 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     UCD90320Device device{std::move(rails), services, bus, address};
71 
72     EXPECT_EQ(device.getName(), "UCD90320");
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 
83 TEST(UCD90320DeviceTests, 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, 1, 1, 0, 0, 1, 0, 0, 1, // MAR01-12
92             1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, // MAR13-24
93             1, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0, 1, // EN1-12
94             1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, // EN13-24
95             1, 1, 0, 0, 1, 1, 1, 0,             // EN25-32
96             1, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0, 1, // LGP01-12
97             1, 1, 0, 0,                         // LGP13-16
98             1, 0, 0, 1, 1, 1, 0, 0,             // DMON1-8
99             1, 0, 0, 1                          // GPIO1-4
100         };
101 
102         MockServices services;
103         EXPECT_CALL(services, getGPIOValues("ucd90320"))
104             .Times(1)
105             .WillOnce(Return(gpioValues));
106         EXPECT_CALL(services, logInfoMsg("Device UCD90320 GPIO values:"))
107             .Times(1);
108         EXPECT_CALL(services, logInfoMsg("MAR01-24: ["
109                                          "1, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0, 1, "
110                                          "1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0]"))
111             .Times(1);
112         EXPECT_CALL(services, logInfoMsg("EN1-32: ["
113                                          "1, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0, 1, "
114                                          "1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, "
115                                          "1, 1, 0, 0, 1, 1, 1, 0]"))
116             .Times(1);
117         EXPECT_CALL(services, logInfoMsg("LGP01-16: ["
118                                          "1, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0, 1, "
119                                          "1, 1, 0, 0]"))
120             .Times(1);
121         EXPECT_CALL(services, logInfoMsg("DMON1-8: ["
122                                          "1, 0, 0, 1, 1, 1, 0, 0]"))
123             .Times(1);
124         EXPECT_CALL(services, logInfoMsg("GPIO1-4: ["
125                                          "1, 0, 0, 1]"))
126             .Times(1);
127         EXPECT_CALL(services,
128                     logInfoMsg("Device UCD90320 MFR_STATUS: 0x123456789abc"))
129             .Times(1);
130         EXPECT_CALL(
131             services,
132             logErrorMsg(
133                 "Pgood fault found in rail monitored by device UCD90320"))
134             .Times(1);
135         EXPECT_CALL(services, logErrorMsg("Pgood fault detected in rail VDD"))
136             .Times(1);
137         EXPECT_CALL(
138             services,
139             logErrorMsg(
140                 "Rail VDD pgood GPIO line offset 2 has inactive value 0"))
141             .Times(1);
142 
143         std::vector<std::unique_ptr<Rail>> rails;
144         rails.emplace_back(createRail("VDD", 2));
145         uint8_t bus{3};
146         uint16_t address{0x72};
147         UCD90320Device device{std::move(rails), services, bus, address};
148 
149         MockPMBus& pmbus = static_cast<MockPMBus&>(device.getPMBusInterface());
150         EXPECT_CALL(pmbus, getPath(Type::Hwmon))
151             .Times(1)
152             .WillOnce(Return("/tmp"));
153         EXPECT_CALL(pmbus, read("mfr_status", Type::HwmonDeviceDebug, true))
154             .Times(1)
155             .WillOnce(Return(0x123456789abcull));
156 
157         // Call findPgoodFault() which calls storeGPIOValues()
158         std::string powerSupplyError{};
159         std::map<std::string, std::string> additionalData{};
160         std::string error = device.findPgoodFault(services, powerSupplyError,
161                                                   additionalData);
162         EXPECT_EQ(error,
163                   "xyz.openbmc_project.Power.Error.PowerSequencerVoltageFault");
164         EXPECT_EQ(additionalData.size(), 10);
165         EXPECT_EQ(additionalData["MFR_STATUS"], "0x123456789abc");
166         EXPECT_EQ(additionalData["DEVICE_NAME"], "UCD90320");
167         EXPECT_EQ(additionalData["MAR01_24_GPIO_VALUES"],
168                   "[1, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0, 1, "
169                   "1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0]");
170         EXPECT_EQ(additionalData["EN1_32_GPIO_VALUES"],
171                   "[1, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0, 1, "
172                   "1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, "
173                   "1, 1, 0, 0, 1, 1, 1, 0]");
174         EXPECT_EQ(additionalData["LGP01_16_GPIO_VALUES"],
175                   "[1, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0, 1, "
176                   "1, 1, 0, 0]");
177         EXPECT_EQ(additionalData["DMON1_8_GPIO_VALUES"],
178                   "[1, 0, 0, 1, 1, 1, 0, 0]");
179         EXPECT_EQ(additionalData["GPIO1_4_GPIO_VALUES"], "[1, 0, 0, 1]");
180         EXPECT_EQ(additionalData["RAIL_NAME"], "VDD");
181         EXPECT_EQ(additionalData["GPIO_LINE"], "2");
182         EXPECT_EQ(additionalData["GPIO_VALUE"], "0");
183     }
184 
185     // Test where there are the wrong number of GPIOs (83 instead of 84)
186     {
187         std::vector<int> gpioValues{
188             1, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0, 1, // MAR01-12
189             1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, // MAR13-24
190             1, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0, 1, // EN1-12
191             1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, // EN13-24
192             1, 1, 0, 0, 1, 1, 1, 0,             // EN25-32
193             1, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0, 1, // LGP01-12
194             1, 1, 0, 0,                         // LGP13-16
195             1, 0, 0, 1, 1, 1, 0, 0,             // DMON1-8
196             1, 0, 0                             // GPIO1-4 (1 missing)
197         };
198 
199         MockServices services;
200         EXPECT_CALL(services, getGPIOValues("ucd90320"))
201             .Times(1)
202             .WillOnce(Return(gpioValues));
203         EXPECT_CALL(services, logInfoMsg("Device UCD90320 GPIO values: ["
204                                          "1, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0, 1, "
205                                          "1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, "
206                                          "1, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0, 1, "
207                                          "1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, "
208                                          "1, 1, 0, 0, 1, 1, 1, 0, "
209                                          "1, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0, 1, "
210                                          "1, 1, 0, 0, "
211                                          "1, 0, 0, 1, 1, 1, 0, 0, "
212                                          "1, 0, 0]"))
213             .Times(1);
214         EXPECT_CALL(services,
215                     logInfoMsg("Device UCD90320 MFR_STATUS: 0x123456789abc"))
216             .Times(1);
217         EXPECT_CALL(
218             services,
219             logErrorMsg(
220                 "Pgood fault found in rail monitored by device UCD90320"))
221             .Times(1);
222         EXPECT_CALL(services, logErrorMsg("Pgood fault detected in rail VDD"))
223             .Times(1);
224         EXPECT_CALL(
225             services,
226             logErrorMsg(
227                 "Rail VDD pgood GPIO line offset 2 has inactive value 0"))
228             .Times(1);
229 
230         std::vector<std::unique_ptr<Rail>> rails;
231         rails.emplace_back(createRail("VDD", 2));
232         uint8_t bus{3};
233         uint16_t address{0x72};
234         UCD90320Device device{std::move(rails), services, bus, address};
235 
236         MockPMBus& pmbus = static_cast<MockPMBus&>(device.getPMBusInterface());
237         EXPECT_CALL(pmbus, getPath(Type::Hwmon))
238             .Times(1)
239             .WillOnce(Return("/tmp"));
240         EXPECT_CALL(pmbus, read("mfr_status", Type::HwmonDeviceDebug, true))
241             .Times(1)
242             .WillOnce(Return(0x123456789abcull));
243 
244         // Call findPgoodFault() which calls storeGPIOValues()
245         std::string powerSupplyError{};
246         std::map<std::string, std::string> additionalData{};
247         std::string error = device.findPgoodFault(services, powerSupplyError,
248                                                   additionalData);
249         EXPECT_EQ(error,
250                   "xyz.openbmc_project.Power.Error.PowerSequencerVoltageFault");
251         EXPECT_EQ(additionalData.size(), 6);
252         EXPECT_EQ(additionalData["MFR_STATUS"], "0x123456789abc");
253         EXPECT_EQ(additionalData["DEVICE_NAME"], "UCD90320");
254         EXPECT_EQ(additionalData["GPIO_VALUES"],
255                   "[1, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0, 1, "
256                   "1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, "
257                   "1, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0, 1, "
258                   "1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, "
259                   "1, 1, 0, 0, 1, 1, 1, 0, "
260                   "1, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0, 1, "
261                   "1, 1, 0, 0, "
262                   "1, 0, 0, 1, 1, 1, 0, 0, "
263                   "1, 0, 0]");
264         EXPECT_EQ(additionalData["RAIL_NAME"], "VDD");
265         EXPECT_EQ(additionalData["GPIO_LINE"], "2");
266         EXPECT_EQ(additionalData["GPIO_VALUE"], "0");
267     }
268 }
269