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<GPIO> gpio{GPIO{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 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 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 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 = device.findPgoodFault(services, powerSupplyError, 186 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 = device.findPgoodFault(services, powerSupplyError, 240 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