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