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