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