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 faultFound = false; 132 inputFault = false; 133 mfrFault = false; 134 vinUVFault = false; 135 readFail = 0; 136 faultLogged = false; 137 138 // The PMBus device driver does not allow for writing CLEAR_FAULTS 139 // directly. However, the pmbus hwmon device driver code will send a 140 // CLEAR_FAULTS after reading from any of the hwmon "files" in sysfs, so 141 // reading in1_input should result in clearing the fault bits in 142 // STATUS_BYTE/STATUS_WORD. 143 // I do not care what the return value is. 144 try 145 { 146 static_cast<void>( 147 pmbusIntf->read("in1_input", phosphor::pmbus::Type::Hwmon)); 148 } 149 catch (ReadFailure& e) 150 { 151 // Since I do not care what the return value is, I really do not 152 // care much if it gets a ReadFailure either. However, this should not 153 // prevent the application from continuing to run, so catching the read 154 // failure. 155 } 156 } 157 158 void PowerSupply::inventoryChanged(sdbusplus::message::message& msg) 159 { 160 std::string msgSensor; 161 std::map<std::string, std::variant<uint32_t, bool>> msgData; 162 msg.read(msgSensor, msgData); 163 164 // Check if it was the Present property that changed. 165 auto valPropMap = msgData.find(PRESENT_PROP); 166 if (valPropMap != msgData.end()) 167 { 168 if (std::get<bool>(valPropMap->second)) 169 { 170 present = true; 171 // TODO: Immediately trying to read or write the "files" causes read 172 // or write failures. 173 using namespace std::chrono_literals; 174 std::this_thread::sleep_for(20ms); 175 onOffConfig(phosphor::pmbus::ON_OFF_CONFIG_CONTROL_PIN_ONLY); 176 clearFaults(); 177 updateInventory(); 178 } 179 else 180 { 181 present = false; 182 183 // Clear out the now outdated inventory properties 184 updateInventory(); 185 } 186 } 187 } 188 189 void PowerSupply::updateInventory() 190 { 191 using namespace phosphor::pmbus; 192 193 #ifdef IBM_VPD 194 std::string ccin; 195 std::string pn; 196 std::string fn; 197 std::string header; 198 std::string sn; 199 using PropertyMap = 200 std::map<std::string, 201 std::variant<std::string, std::vector<uint8_t>, bool>>; 202 PropertyMap assetProps; 203 PropertyMap operProps; 204 PropertyMap versionProps; 205 PropertyMap ipzvpdDINFProps; 206 PropertyMap ipzvpdVINIProps; 207 using InterfaceMap = std::map<std::string, PropertyMap>; 208 InterfaceMap interfaces; 209 using ObjectMap = std::map<sdbusplus::message::object_path, InterfaceMap>; 210 ObjectMap object; 211 #endif 212 213 if (present) 214 { 215 // TODO: non-IBM inventory updates? 216 217 #ifdef IBM_VPD 218 try 219 { 220 ccin = pmbusIntf->readString(CCIN, Type::HwmonDeviceDebug); 221 assetProps.emplace(MODEL_PROP, ccin); 222 } 223 catch (ReadFailure& e) 224 { 225 // Ignore the read failure, let pmbus code indicate failure, path... 226 // TODO - ibm918 227 // https://github.com/openbmc/docs/blob/master/designs/vpd-collection.md 228 // The BMC must log errors if any of the VPD cannot be properly 229 // parsed or fails ECC checks. 230 } 231 232 try 233 { 234 pn = pmbusIntf->readString(PART_NUMBER, Type::HwmonDeviceDebug); 235 assetProps.emplace(PN_PROP, pn); 236 } 237 catch (ReadFailure& e) 238 { 239 // Ignore the read failure, let pmbus code indicate failure, path... 240 } 241 242 try 243 { 244 fn = pmbusIntf->readString(FRU_NUMBER, Type::HwmonDeviceDebug); 245 } 246 catch (ReadFailure& e) 247 { 248 // Ignore the read failure, let pmbus code indicate failure, path... 249 } 250 251 try 252 { 253 header = 254 pmbusIntf->readString(SERIAL_HEADER, Type::HwmonDeviceDebug); 255 sn = pmbusIntf->readString(SERIAL_NUMBER, Type::HwmonDeviceDebug); 256 assetProps.emplace(SN_PROP, sn); 257 } 258 catch (ReadFailure& e) 259 { 260 // Ignore the read failure, let pmbus code indicate failure, path... 261 } 262 263 try 264 { 265 fwVersion = 266 pmbusIntf->readString(FW_VERSION, Type::HwmonDeviceDebug); 267 versionProps.emplace(VERSION_PROP, fwVersion); 268 } 269 catch (ReadFailure& e) 270 { 271 // Ignore the read failure, let pmbus code indicate failure, path... 272 } 273 274 ipzvpdVINIProps.emplace("CC", 275 std::vector<uint8_t>(ccin.begin(), ccin.end())); 276 ipzvpdVINIProps.emplace("PN", 277 std::vector<uint8_t>(pn.begin(), pn.end())); 278 ipzvpdVINIProps.emplace("FN", 279 std::vector<uint8_t>(fn.begin(), fn.end())); 280 std::string header_sn = header + sn + '\0'; 281 ipzvpdVINIProps.emplace( 282 "SN", std::vector<uint8_t>(header_sn.begin(), header_sn.end())); 283 std::string description = "IBM PS"; 284 ipzvpdVINIProps.emplace( 285 "DR", std::vector<uint8_t>(description.begin(), description.end())); 286 287 // Update the Resource Identifier (RI) keyword 288 // 2 byte FRC: 0x0003 289 // 2 byte RID: 0x1000, 0x1001... 290 std::uint8_t num = std::stoul( 291 inventoryPath.substr(inventoryPath.size() - 1, 1), nullptr, 0); 292 std::vector<uint8_t> ri{0x00, 0x03, 0x10, num}; 293 ipzvpdDINFProps.emplace("RI", ri); 294 295 // Fill in the FRU Label (FL) keyword. 296 std::string fl = "E"; 297 fl.push_back(inventoryPath.back()); 298 fl.resize(FL_KW_SIZE, ' '); 299 ipzvpdDINFProps.emplace("FL", 300 std::vector<uint8_t>(fl.begin(), fl.end())); 301 302 interfaces.emplace(ASSET_IFACE, std::move(assetProps)); 303 interfaces.emplace(VERSION_IFACE, std::move(versionProps)); 304 interfaces.emplace(DINF_IFACE, std::move(ipzvpdDINFProps)); 305 interfaces.emplace(VINI_IFACE, std::move(ipzvpdVINIProps)); 306 307 // Update the Functional 308 operProps.emplace(FUNCTIONAL_PROP, present); 309 interfaces.emplace(OPERATIONAL_STATE_IFACE, std::move(operProps)); 310 311 auto path = inventoryPath.substr(strlen(INVENTORY_OBJ_PATH)); 312 object.emplace(path, std::move(interfaces)); 313 314 try 315 { 316 auto service = 317 util::getService(INVENTORY_OBJ_PATH, INVENTORY_MGR_IFACE, bus); 318 319 if (service.empty()) 320 { 321 log<level::ERR>("Unable to get inventory manager service"); 322 return; 323 } 324 325 auto method = 326 bus.new_method_call(service.c_str(), INVENTORY_OBJ_PATH, 327 INVENTORY_MGR_IFACE, "Notify"); 328 329 method.append(std::move(object)); 330 331 auto reply = bus.call(method); 332 } 333 catch (std::exception& e) 334 { 335 log<level::ERR>( 336 std::string(e.what() + std::string(" PATH=") + inventoryPath) 337 .c_str()); 338 } 339 #endif 340 } 341 } 342 343 } // namespace phosphor::power::psu 344