1 #include "config.h" 2 3 #include "power_supply.hpp" 4 5 #include "types.hpp" 6 #include "util.hpp" 7 8 #include <fmt/format.h> 9 10 #include <xyz/openbmc_project/Common/Device/error.hpp> 11 12 #include <chrono> // sleep_for() 13 #include <cstdint> // uint8_t... 14 #include <thread> // sleep_for() 15 16 namespace phosphor::power::psu 17 { 18 19 using namespace phosphor::logging; 20 using namespace sdbusplus::xyz::openbmc_project::Common::Device::Error; 21 22 void PowerSupply::updatePresence() 23 { 24 try 25 { 26 present = getPresence(bus, inventoryPath); 27 } 28 catch (const sdbusplus::exception::SdBusError& e) 29 { 30 // Relying on property change or interface added to retry. 31 // Log an informational trace to the journal. 32 log<level::INFO>( 33 fmt::format("D-Bus property {} access failure exception", 34 inventoryPath) 35 .c_str()); 36 } 37 } 38 39 void PowerSupply::analyze() 40 { 41 using namespace phosphor::pmbus; 42 43 if ((present) && (readFail < LOG_LIMIT)) 44 { 45 try 46 { 47 statusWord = pmbusIntf->read(STATUS_WORD, Type::Debug); 48 // Read worked, reset the fail count. 49 readFail = 0; 50 51 if (statusWord) 52 { 53 if (statusWord & status_word::INPUT_FAULT_WARN) 54 { 55 if (!inputFault) 56 { 57 log<level::INFO>( 58 "INPUT fault", 59 entry("STATUS_WORD=0x%04X", 60 static_cast<uint16_t>(statusWord))); 61 } 62 63 faultFound = true; 64 inputFault = true; 65 } 66 67 if (statusWord & status_word::MFR_SPECIFIC_FAULT) 68 { 69 if (!mfrFault) 70 { 71 log<level::INFO>( 72 "MFRSPECIFIC fault", 73 entry("STATUS_WORD=0x%04X", 74 static_cast<uint16_t>(statusWord))); 75 } 76 faultFound = true; 77 mfrFault = true; 78 } 79 80 if (statusWord & status_word::VIN_UV_FAULT) 81 { 82 if (!vinUVFault) 83 { 84 log<level::INFO>( 85 "VIN_UV fault", 86 entry("STATUS_WORD=0x%04X", 87 static_cast<uint16_t>(statusWord))); 88 } 89 90 faultFound = true; 91 vinUVFault = true; 92 } 93 } 94 else 95 { 96 faultFound = false; 97 inputFault = false; 98 mfrFault = false; 99 vinUVFault = false; 100 } 101 } 102 catch (ReadFailure& e) 103 { 104 readFail++; 105 phosphor::logging::commit<ReadFailure>(); 106 } 107 } 108 } 109 110 void PowerSupply::onOffConfig(uint8_t data) 111 { 112 using namespace phosphor::pmbus; 113 114 if (present) 115 { 116 log<level::INFO>("ON_OFF_CONFIG write", entry("DATA=0x%02X", data)); 117 try 118 { 119 std::vector<uint8_t> configData{data}; 120 pmbusIntf->writeBinary(ON_OFF_CONFIG, configData, 121 Type::HwmonDeviceDebug); 122 } 123 catch (...) 124 { 125 // The underlying code in writeBinary will log a message to the 126 // journal if the write fails. If the ON_OFF_CONFIG is not setup as 127 // desired, later fault detection and analysis code should catch any 128 // of the fall out. We should not need to terminate the application 129 // if this write fails. 130 } 131 } 132 } 133 134 void PowerSupply::clearFaults() 135 { 136 // The PMBus device driver does not allow for writing CLEAR_FAULTS 137 // directly. However, the pmbus hwmon device driver code will send a 138 // CLEAR_FAULTS after reading from any of the hwmon "files" in sysfs, so 139 // reading in1_input should result in clearing the fault bits in 140 // STATUS_BYTE/STATUS_WORD. 141 // I do not care what the return value is. 142 if (present) 143 { 144 faultFound = false; 145 inputFault = false; 146 mfrFault = false; 147 vinUVFault = false; 148 readFail = 0; 149 faultLogged = false; 150 151 try 152 { 153 static_cast<void>( 154 pmbusIntf->read("in1_input", phosphor::pmbus::Type::Hwmon)); 155 } 156 catch (ReadFailure& e) 157 { 158 // Since I do not care what the return value is, I really do not 159 // care much if it gets a ReadFailure either. However, this should 160 // not prevent the application from continuing to run, so catching 161 // the read failure. 162 } 163 } 164 } 165 166 void PowerSupply::inventoryChanged(sdbusplus::message::message& msg) 167 { 168 std::string msgSensor; 169 std::map<std::string, std::variant<uint32_t, bool>> msgData; 170 msg.read(msgSensor, msgData); 171 172 // Check if it was the Present property that changed. 173 auto valPropMap = msgData.find(PRESENT_PROP); 174 if (valPropMap != msgData.end()) 175 { 176 if (std::get<bool>(valPropMap->second)) 177 { 178 present = true; 179 // TODO: Immediately trying to read or write the "files" causes read 180 // or write failures. 181 using namespace std::chrono_literals; 182 std::this_thread::sleep_for(20ms); 183 pmbusIntf->findHwmonDir(); 184 onOffConfig(phosphor::pmbus::ON_OFF_CONFIG_CONTROL_PIN_ONLY); 185 clearFaults(); 186 updateInventory(); 187 } 188 else 189 { 190 present = false; 191 192 // Clear out the now outdated inventory properties 193 updateInventory(); 194 } 195 } 196 } 197 198 void PowerSupply::updateInventory() 199 { 200 using namespace phosphor::pmbus; 201 202 #ifdef IBM_VPD 203 std::string ccin; 204 std::string pn; 205 std::string fn; 206 std::string header; 207 std::string sn; 208 using PropertyMap = 209 std::map<std::string, 210 std::variant<std::string, std::vector<uint8_t>, bool>>; 211 PropertyMap assetProps; 212 PropertyMap operProps; 213 PropertyMap versionProps; 214 PropertyMap ipzvpdDINFProps; 215 PropertyMap ipzvpdVINIProps; 216 using InterfaceMap = std::map<std::string, PropertyMap>; 217 InterfaceMap interfaces; 218 using ObjectMap = std::map<sdbusplus::message::object_path, InterfaceMap>; 219 ObjectMap object; 220 #endif 221 222 if (present) 223 { 224 // TODO: non-IBM inventory updates? 225 226 #ifdef IBM_VPD 227 try 228 { 229 ccin = pmbusIntf->readString(CCIN, Type::HwmonDeviceDebug); 230 assetProps.emplace(MODEL_PROP, ccin); 231 } 232 catch (ReadFailure& e) 233 { 234 // Ignore the read failure, let pmbus code indicate failure, path... 235 // TODO - ibm918 236 // https://github.com/openbmc/docs/blob/master/designs/vpd-collection.md 237 // The BMC must log errors if any of the VPD cannot be properly 238 // parsed or fails ECC checks. 239 } 240 241 try 242 { 243 pn = pmbusIntf->readString(PART_NUMBER, Type::HwmonDeviceDebug); 244 assetProps.emplace(PN_PROP, pn); 245 } 246 catch (ReadFailure& e) 247 { 248 // Ignore the read failure, let pmbus code indicate failure, path... 249 } 250 251 try 252 { 253 fn = pmbusIntf->readString(FRU_NUMBER, Type::HwmonDeviceDebug); 254 } 255 catch (ReadFailure& e) 256 { 257 // Ignore the read failure, let pmbus code indicate failure, path... 258 } 259 260 try 261 { 262 header = 263 pmbusIntf->readString(SERIAL_HEADER, Type::HwmonDeviceDebug); 264 sn = pmbusIntf->readString(SERIAL_NUMBER, Type::HwmonDeviceDebug); 265 assetProps.emplace(SN_PROP, sn); 266 } 267 catch (ReadFailure& e) 268 { 269 // Ignore the read failure, let pmbus code indicate failure, path... 270 } 271 272 try 273 { 274 fwVersion = 275 pmbusIntf->readString(FW_VERSION, Type::HwmonDeviceDebug); 276 versionProps.emplace(VERSION_PROP, fwVersion); 277 } 278 catch (ReadFailure& e) 279 { 280 // Ignore the read failure, let pmbus code indicate failure, path... 281 } 282 283 ipzvpdVINIProps.emplace("CC", 284 std::vector<uint8_t>(ccin.begin(), ccin.end())); 285 ipzvpdVINIProps.emplace("PN", 286 std::vector<uint8_t>(pn.begin(), pn.end())); 287 ipzvpdVINIProps.emplace("FN", 288 std::vector<uint8_t>(fn.begin(), fn.end())); 289 std::string header_sn = header + sn + '\0'; 290 ipzvpdVINIProps.emplace( 291 "SN", std::vector<uint8_t>(header_sn.begin(), header_sn.end())); 292 std::string description = "IBM PS"; 293 ipzvpdVINIProps.emplace( 294 "DR", std::vector<uint8_t>(description.begin(), description.end())); 295 296 // Update the Resource Identifier (RI) keyword 297 // 2 byte FRC: 0x0003 298 // 2 byte RID: 0x1000, 0x1001... 299 std::uint8_t num = std::stoul( 300 inventoryPath.substr(inventoryPath.size() - 1, 1), nullptr, 0); 301 std::vector<uint8_t> ri{0x00, 0x03, 0x10, num}; 302 ipzvpdDINFProps.emplace("RI", ri); 303 304 // Fill in the FRU Label (FL) keyword. 305 std::string fl = "E"; 306 fl.push_back(inventoryPath.back()); 307 fl.resize(FL_KW_SIZE, ' '); 308 ipzvpdDINFProps.emplace("FL", 309 std::vector<uint8_t>(fl.begin(), fl.end())); 310 311 interfaces.emplace(ASSET_IFACE, std::move(assetProps)); 312 interfaces.emplace(VERSION_IFACE, std::move(versionProps)); 313 interfaces.emplace(DINF_IFACE, std::move(ipzvpdDINFProps)); 314 interfaces.emplace(VINI_IFACE, std::move(ipzvpdVINIProps)); 315 316 // Update the Functional 317 operProps.emplace(FUNCTIONAL_PROP, present); 318 interfaces.emplace(OPERATIONAL_STATE_IFACE, std::move(operProps)); 319 320 auto path = inventoryPath.substr(strlen(INVENTORY_OBJ_PATH)); 321 object.emplace(path, std::move(interfaces)); 322 323 try 324 { 325 auto service = 326 util::getService(INVENTORY_OBJ_PATH, INVENTORY_MGR_IFACE, bus); 327 328 if (service.empty()) 329 { 330 log<level::ERR>("Unable to get inventory manager service"); 331 return; 332 } 333 334 auto method = 335 bus.new_method_call(service.c_str(), INVENTORY_OBJ_PATH, 336 INVENTORY_MGR_IFACE, "Notify"); 337 338 method.append(std::move(object)); 339 340 auto reply = bus.call(method); 341 } 342 catch (std::exception& e) 343 { 344 log<level::ERR>( 345 std::string(e.what() + std::string(" PATH=") + inventoryPath) 346 .c_str()); 347 } 348 #endif 349 } 350 } 351 352 } // namespace phosphor::power::psu 353