power-utils: Implement isReadyToUpdate
Check other PSUs' status word and status vout, return false when the
status has input/output fault.
Tested: Verify on Witherspoon that the update is not started and log
Unable to update PSU when other PSU has input/ouput fault
when the other PSU has input/output error;
And the update continues when the other PSU has no errors.
Signed-off-by: Lei YU <mine260309@gmail.com>
Change-Id: Ia2a4a23a43c18a417b8a85fbd5339f487984e689
diff --git a/tools/power-utils/updater.cpp b/tools/power-utils/updater.cpp
index deab95f..e26d1b0 100644
--- a/tools/power-utils/updater.cpp
+++ b/tools/power-utils/updater.cpp
@@ -17,6 +17,7 @@
#include "updater.hpp"
+#include "pmbus.hpp"
#include "types.hpp"
#include "utility.hpp"
@@ -146,16 +147,85 @@
bool Updater::isReadyToUpdate()
{
+ using namespace phosphor::pmbus;
+
// Pre-condition for updating PSU:
// * Host is powered off
- // * All other PSUs are having AC input
- // * All other PSUs are having standby output
- if (util::isPoweredOn(bus))
+ // * At least one other PSU is present
+ // * All other PSUs that are present are having AC input and DC standby
+ // output
+
+ if (util::isPoweredOn(bus, true))
{
+ log<level::WARNING>("Unable to update PSU when host is on");
return false;
}
- // TODO
- return true;
+
+ bool hasOtherPresent = false;
+ auto paths = util::getPSUInventoryPaths(bus);
+ for (const auto& p : paths)
+ {
+ if (p == psuInventoryPath)
+ {
+ // Skip check for itself
+ continue;
+ }
+
+ // Check PSU present
+ bool present = false;
+ try
+ {
+ auto service = util::getService(p, INVENTORY_IFACE, bus);
+ util::getProperty(INVENTORY_IFACE, PRESENT_PROP, psuInventoryPath,
+ service, bus, present);
+ }
+ catch (const std::exception& e)
+ {
+ log<level::ERR>("Failed to get present property",
+ entry("PSU=%s", p.c_str()));
+ }
+ if (!present)
+ {
+ log<level::WARNING>("PSU not present", entry("PSU=%s", p.c_str()));
+ continue;
+ }
+ hasOtherPresent = true;
+
+ // Typically the driver is still bound here, so it is possible to
+ // directly read the debugfs to get the status.
+ try
+ {
+ auto devPath = internal::getDevicePath(p);
+ PMBus pmbus(devPath);
+ uint16_t statusWord = pmbus.read(STATUS_WORD, Type::Debug);
+ auto status0Vout = pmbus.insertPageNum(STATUS_VOUT, 0);
+ uint8_t voutStatus = pmbus.read(status0Vout, Type::Debug);
+ if ((statusWord & status_word::VOUT_FAULT) ||
+ (statusWord & status_word::INPUT_FAULT_WARN) ||
+ (statusWord & status_word::VIN_UV_FAULT) ||
+ // For ibm-cffps PSUs, the MFR (0x80)'s OV (bit 2) and VAUX
+ // (bit 6) fault map to OV_FAULT, and UV (bit 3) fault maps to
+ // UV_FAULT in vout status.
+ (voutStatus & status_vout::UV_FAULT) ||
+ (voutStatus & status_vout::OV_FAULT))
+ {
+ log<level::WARNING>(
+ "Unable to update PSU when other PSU has input/ouput fault",
+ entry("PSU=%s", p.c_str()),
+ entry("STATUS_WORD=0x%04x", statusWord),
+ entry("VOUT_BYTE=0x%02x", voutStatus));
+ return false;
+ }
+ }
+ catch (const std::exception& ex)
+ {
+ // If error occurs on accessing the debugfs, it means something went
+ // wrong, e.g. PSU is not present, and it's not ready to update.
+ log<level::ERR>(ex.what());
+ return false;
+ }
+ }
+ return hasOtherPresent;
}
int Updater::doUpdate()