psu-ng: Add code to set ON_OFF_CONFIG
Add code that sends the appropriate ON_OFF_CONFIG mask for the power
supply power on operation.
Tested:
Ran gtest using x86sdk
Used simulator to change ON_OFF_CONFIG value, restarted service,
verified ON_OFF_CONFIG changed.
Used i2cget on hardware to change ON_OFF_CONFIG, restarted service,
verified ON_OFF_CONFIG changed.
Signed-off-by: Brandon Wyman <bjwyman@gmail.com>
Change-Id: Ifd9d63fcd2e86a62b7358e08958b5e2dbd21db9f
diff --git a/phosphor-power-supply/power_supply.cpp b/phosphor-power-supply/power_supply.cpp
index 37e5666..8c80ef3 100644
--- a/phosphor-power-supply/power_supply.cpp
+++ b/phosphor-power-supply/power_supply.cpp
@@ -93,6 +93,30 @@
}
}
+void PowerSupply::onOffConfig(uint8_t data)
+{
+ using namespace phosphor::pmbus;
+
+ if (present)
+ {
+ log<level::INFO>("ON_OFF_CONFIG write", entry("DATA=0x%02X", data));
+ try
+ {
+ std::vector<uint8_t> configData{data};
+ pmbusIntf->writeBinary(ON_OFF_CONFIG, configData,
+ Type::HwmonDeviceDebug);
+ }
+ catch (...)
+ {
+ // The underlying code in writeBinary will log a message to the
+ // journal if the write fails. If the ON_OFF_CONFIG is not setup as
+ // desired, later fault detection and analysis code should catch any
+ // of the fall out. We should not need to terminate the application
+ // if this write fails.
+ }
+ }
+}
+
void PowerSupply::clearFaults()
{
faultFound = false;
@@ -133,6 +157,7 @@
if (std::get<bool>(valPropMap->second))
{
present = true;
+ onOffConfig(phosphor::pmbus::ON_OFF_CONFIG_CONTROL_PIN_ONLY);
clearFaults();
}
else
diff --git a/phosphor-power-supply/power_supply.hpp b/phosphor-power-supply/power_supply.hpp
index 707a745..487fa53 100644
--- a/phosphor-power-supply/power_supply.hpp
+++ b/phosphor-power-supply/power_supply.hpp
@@ -65,6 +65,16 @@
void analyze();
/**
+ * Write PMBus ON_OFF_CONFIG
+ *
+ * This function will be called to cause the PMBus device driver to send the
+ * ON_OFF_CONFIG command. Takes one byte of data.
+ *
+ * @param[in] data - The ON_OFF_CONFIG data byte mask.
+ */
+ void onOffConfig(uint8_t data);
+
+ /**
* Write PMBus CLEAR_FAULTS
*
* This function will be called in various situations in order to clear
diff --git a/phosphor-power-supply/psu_manager.hpp b/phosphor-power-supply/psu_manager.hpp
index a3bcae0..a9a0bd5 100644
--- a/phosphor-power-supply/psu_manager.hpp
+++ b/phosphor-power-supply/psu_manager.hpp
@@ -87,6 +87,7 @@
powerOn = false;
}
+ onOffConfig(phosphor::pmbus::ON_OFF_CONFIG_CONTROL_PIN_ONLY);
clearFaults();
updateInventory();
}
@@ -100,6 +101,20 @@
}
/**
+ * Write PMBus ON_OFF_CONFIG
+ *
+ * This function will be called to cause the PMBus device driver to send the
+ * ON_OFF_CONFIG command. Takes one byte of data.
+ */
+ void onOffConfig(const uint8_t data)
+ {
+ for (auto& psu : psus)
+ {
+ psu->onOffConfig(data);
+ }
+ }
+
+ /**
* This function will be called in various situations in order to clear
* any fault status bits that may have been set, in order to start over
* with a clean state. Presence changes and power state changes will want
diff --git a/phosphor-power-supply/test/mock.hpp b/phosphor-power-supply/test/mock.hpp
index 0c64e00..c4c2c9d 100644
--- a/phosphor-power-supply/test/mock.hpp
+++ b/phosphor-power-supply/test/mock.hpp
@@ -17,6 +17,9 @@
MOCK_METHOD(uint64_t, read, (const std::string& name, Type type),
(override));
+ MOCK_METHOD(void, writeBinary,
+ (const std::string& name, std::vector<uint8_t> data, Type type),
+ (override));
};
} // namespace pmbus
diff --git a/phosphor-power-supply/test/power_supply_tests.cpp b/phosphor-power-supply/test/power_supply_tests.cpp
index c6d4814..5eb4b3e 100644
--- a/phosphor-power-supply/test/power_supply_tests.cpp
+++ b/phosphor-power-supply/test/power_supply_tests.cpp
@@ -11,8 +11,11 @@
using namespace phosphor::pmbus;
using ::testing::_;
+using ::testing::Args;
using ::testing::Assign;
using ::testing::DoAll;
+using ::testing::ElementsAre;
+using ::testing::NotNull;
using ::testing::Return;
using ::testing::StrEq;
@@ -148,6 +151,46 @@
// TODO: ReadFailure
}
+TEST_F(PowerSupplyTests, OnOffConfig)
+{
+ auto bus = sdbusplus::bus::new_default();
+ uint8_t data = 0x15;
+
+ // Test where PSU is NOT present
+ try
+ {
+ EXPECT_CALL(mockedUtil, getPresence(_, StrEq(PSUInventoryPath)))
+ .Times(1)
+ .WillOnce(Return(false));
+ PowerSupply psu{bus, PSUInventoryPath, 4, "0069"};
+ MockedPMBus& mockPMBus = static_cast<MockedPMBus&>(psu.getPMBus());
+ // If it is not present, I should not be trying to write to it.
+ EXPECT_CALL(mockPMBus, writeBinary(_, _, _)).Times(0);
+ psu.onOffConfig(data);
+ }
+ catch (...)
+ {
+ }
+
+ // Test where PSU is present
+ try
+ {
+ EXPECT_CALL(mockedUtil, getPresence(_, StrEq(PSUInventoryPath)))
+ .Times(1)
+ .WillOnce(Return(true)); // present
+ PowerSupply psu{bus, PSUInventoryPath, 5, "006a"};
+ MockedPMBus& mockPMBus = static_cast<MockedPMBus&>(psu.getPMBus());
+ // TODO: ???should I check the filename?
+ EXPECT_CALL(mockPMBus,
+ writeBinary(_, ElementsAre(0x15), Type::HwmonDeviceDebug))
+ .Times(1);
+ psu.onOffConfig(data);
+ }
+ catch (...)
+ {
+ }
+}
+
TEST_F(PowerSupplyTests, ClearFaults)
{
auto bus = sdbusplus::bus::new_default();
diff --git a/pmbus.cpp b/pmbus.cpp
index d8b1804..b2fe2fd 100644
--- a/pmbus.cpp
+++ b/pmbus.cpp
@@ -303,6 +303,40 @@
}
}
+void PMBus::writeBinary(const std::string& name, std::vector<uint8_t> data,
+ Type type)
+{
+ std::ofstream file;
+ fs::path path = getPath(type);
+
+ path /= name;
+
+ file.exceptions(std::ofstream::failbit | std::ofstream::badbit |
+ std::ofstream::eofbit);
+
+ try
+ {
+ // I need to specify binary mode when I construct the ofstream
+ file.open(path, std::ios::out | std::ios_base::binary);
+ log<level::DEBUG>("Write data to sysfs file",
+ entry("FILENAME=%s", path.c_str()));
+ file.write(reinterpret_cast<const char*>(&data[0]), data.size());
+ }
+ catch (const std::exception& e)
+ {
+ auto rc = errno;
+
+ log<level::ERR>("Failed to write binary data to sysfs file",
+ entry("FILENAME=%s", path.c_str()));
+
+ using metadata = xyz::openbmc_project::Common::Device::WriteFailure;
+
+ elog<WriteFailure>(
+ metadata::CALLOUT_ERRNO(rc),
+ metadata::CALLOUT_DEVICE_PATH(fs::canonical(basePath).c_str()));
+ }
+}
+
void PMBus::findHwmonDir()
{
fs::path path{basePath};
diff --git a/pmbus.hpp b/pmbus.hpp
index 5e252b2..123e4fd 100644
--- a/pmbus.hpp
+++ b/pmbus.hpp
@@ -101,6 +101,24 @@
constexpr auto OT_FAULT = 0x80;
} // namespace status_temperature
+constexpr auto ON_OFF_CONFIG = "on_off_config";
+
+// From PMBus Specification Part II Revsion 1.2:
+// The ON_OFF_CONFIG command configures the combination of CONTROL pin input
+// and serial bus commands needed to turn the unit on and off. This includes how
+// the unit responds when power is applied.
+// Bits [7:5] - 000 - Reserved
+// Bit 4 - 1 - Unit does not power up until commanded by the CONTROL pin and
+// OPERATION command (as programmed in bits [3:0]).
+// Bit 3 - 0 - Unit ignores the on/off portion of the OPERATION command from
+// serial bus.
+// Bit 2 - 1 - Unit requires the CONTROL pin to be asserted to start the unit.
+// Bit 1 - 0 - Polarity of the CONTROL pin. Active low (Pull pin low to start
+// the unit).
+// Bit 0 - 1 - Turn off the output and stop transferring energy to the output as
+// fast as possible.
+constexpr auto ON_OFF_CONFIG_CONTROL_PIN_ONLY = 0x15;
+
/**
* Where the access should be done
*/
@@ -124,6 +142,8 @@
virtual ~PMBusBase() = default;
virtual uint64_t read(const std::string& name, Type type) = 0;
+ virtual void writeBinary(const std::string& name, std::vector<uint8_t> data,
+ Type type) = 0;
};
/**
@@ -275,6 +295,16 @@
void write(const std::string& name, int value, Type type);
/**
+ * Writes binary data to a file in sysfs.
+ *
+ * @param[in] name - path concatenated to basePath to write
+ * @param[in] data - The data to write to the file
+ * @param[in] type - Path type
+ */
+ void writeBinary(const std::string& name, std::vector<uint8_t> data,
+ Type type) override;
+
+ /**
* Returns the sysfs base path of this device
*/
inline const auto& path() const