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