summaryrefslogtreecommitdiff
path: root/meta-openbmc-mods/meta-common/recipes-phosphor/sensors/dbus-sensors/0010-Add-support-for-Get-PMBUS-Readings-method.patch
diff options
context:
space:
mode:
Diffstat (limited to 'meta-openbmc-mods/meta-common/recipes-phosphor/sensors/dbus-sensors/0010-Add-support-for-Get-PMBUS-Readings-method.patch')
-rw-r--r--meta-openbmc-mods/meta-common/recipes-phosphor/sensors/dbus-sensors/0010-Add-support-for-Get-PMBUS-Readings-method.patch457
1 files changed, 457 insertions, 0 deletions
diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/sensors/dbus-sensors/0010-Add-support-for-Get-PMBUS-Readings-method.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/sensors/dbus-sensors/0010-Add-support-for-Get-PMBUS-Readings-method.patch
new file mode 100644
index 000000000..d305ef008
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-phosphor/sensors/dbus-sensors/0010-Add-support-for-Get-PMBUS-Readings-method.patch
@@ -0,0 +1,457 @@
+From 5c2fbc31b076a32d0291e74046a576de852ac90c Mon Sep 17 00:00:00 2001
+From: Arun Lal K M <arun.lal@intel.com>
+Date: Thu, 6 Jan 2022 20:30:09 +0000
+Subject: [PATCH] Add support for 'Get PMBUS Readings' method.
+
+VR sensor is currently read in the following way:
+BMC gives read command, and ME proxy forward it to VR sensor.
+
+In 'Get PMBUS Readings' method BMC reads the data from ME.
+This is used in platforms where ME cannot proxy forward PMBUS command.
+
+Command:
+0x2E 0xF5 0x57 0x01 0x00 0x<ID> 0x0F 0x00
+
+0x2E is net function
+0xF5 is corresponding to 'Get PMBUS Readings'
+0x57 0x01 0x00 is Intel manufacturers ID
+ID is the ID of senssor in ME (not actual sensor address)
+0x0F is PMBUS-enabled Device Index
+0x00 is reserved byte
+
+New configuration parameters in Baseboard.json:
+1) DeviceIndex: ID of the sensor in ME.
+2) ReadMethod: use "ReadME" for 'Get PMBUS Readings'.
+3) Register: Register to read in the response byte array.
+ For example,
+ Registers 1 = Temperature
+ Registers 2 = Voltage
+ Registers 3 = Current
+
+Note:
+This is not a complete replacement for the old method, we are adding
+one more way to read these sensors. The old implementation is still
+present, as in, new code with old configuration file
+(xx_Baseboard.json) will work (using the old method of reading the
+sensor).
+
+References:
+1) Intelligent Power Node Manager External Interface Specification
+ Using IPMI.
+2) PMBus-Specification.
+
+Tested:
+Case 1: Using proxy (Backward compatibility testing)
+Sample configuration in Baseboard.json:
+{
+ "Address": "0xXX",
+ "Class": "MpsBridgeTemp",
+ "Name": "XX VR Temp",
+ "Thresholds": [...],
+ "Type": "IpmbSensor"
+}
+
+Give command 'ipmitool sdr elist'
+Response:
+CPU1 PVCCD VR Te | 31h | ok | 0.1 | 37 degrees C
+CPU2 PVCCD VR Te | 36h | ok | 0.1 | 37 degrees C
+CPU1 PVCCFA EHV | 32h | ok | 0.1 | 34 degrees C
+CPU2 PVCCFA EHV | 37h | ok | 0.1 | 33 degrees C
+CPU1 VCCIN VR Te | 34h | ok | 0.1 | 48 degrees C
+CPU2 VCCIN VR Te | 39h | ok | 0.1 | 58 degrees C
+
+Case 2: Using 'Get PMBUS Readings' method
+Sample configuration in Baseboard.json:
+{
+ "Address": "0xXX",
+ "DeviceIndex": "0xXX",
+ "Class": "MpsBridgeTemp",
+ "Register": 1,
+ "ReadMethod": "ReadME",
+ "Name": "XX VR Temp",
+ "Thresholds": [...],
+ "Type": "IpmbSensor"
+}
+
+Give command 'ipmitool sdr elist'
+Response:
+CPU1 PVCCD VR Te | 31h | ok | 0.1 | 41 degrees C
+CPU2 PVCCD VR Te | 36h | ok | 0.1 | 43 degrees C
+CPU1 PVCCFA EHV | 32h | ok | 0.1 | 37 degrees C
+CPU2 PVCCFA EHV | 37h | ok | 0.1 | 37 degrees C
+CPU1 VCCIN VR Te | 34h | ok | 0.1 | 60 degrees C
+CPU2 VCCIN VR Te | 39h | ok | 0.1 | 56 degrees C
+
+Signed-off-by: Arun Lal K M <arun.lal@intel.com>
+---
+ include/IpmbSensor.hpp | 51 ++++++++++
+ src/IpmbSensor.cpp | 217 ++++++++++++++++++++++++++++++++++-------
+ 2 files changed, 233 insertions(+), 35 deletions(-)
+
+diff --git a/include/IpmbSensor.hpp b/include/IpmbSensor.hpp
+index 18d10c1..2a89251 100644
+--- a/include/IpmbSensor.hpp
++++ b/include/IpmbSensor.hpp
+@@ -43,6 +43,51 @@ namespace sensor
+ {
+ constexpr uint8_t netFn = 0x04;
+ constexpr uint8_t getSensorReading = 0x2d;
++constexpr uint8_t manufacturerId[3] = {0x57, 0x01, 0x00};
++
++namespace read_me
++{
++/**
++ * Refernce:
++ * Intelligent Power Node Manager External Interface Specification
++ * getPmbusReadings = Get PMBUS Readings (F5h)
++ *
++ * bytesForTimestamp and bytesForManufacturerId are decoded from
++ * response bytes for Get PMBUS Readings.
++ */
++constexpr uint8_t getPmbusReadings = 0xF5;
++constexpr uint8_t bytesForTimestamp = 4;
++constexpr uint8_t bytesForManufacturerId = 3;
++
++constexpr size_t fixedOffset = bytesForTimestamp + bytesForManufacturerId;
++
++void getRawData(uint8_t registerToRead, const std::vector<uint8_t>& input,
++ std::vector<uint8_t>& result)
++{
++ if (input.size() < 3)
++ {
++ return;
++ }
++
++ /* Every register is two bytes*/
++ size_t offset = fixedOffset + (registerToRead * 2);
++ if (input.size() <= (offset + 1))
++ {
++ return;
++ }
++
++ result.reserve(5);
++
++ // ID
++ result.emplace_back(input[0]);
++ result.emplace_back(input[1]);
++ result.emplace_back(input[2]);
++
++ // Value in registerToRead
++ result.emplace_back(input[offset]);
++ result.emplace_back(input[offset + 1]);
++}
++} // namespace read_me
+
+ static bool isValid(const std::vector<uint8_t>& data)
+ {
+@@ -91,6 +136,7 @@ struct IpmbSensor : public Sensor
+ void loadDefaults(void);
+ void runInitCmd(void);
+ bool processReading(const std::vector<uint8_t>& data, double& resp);
++ void setReadMethod(const SensorBaseConfigMap& sensorBaseConfig);
+
+ IpmbType type;
+ IpmbSubType subType;
+@@ -102,6 +148,9 @@ struct IpmbSensor : public Sensor
+ uint8_t deviceAddress;
+ uint8_t errorCount;
+ uint8_t hostSMbusIndex;
++ uint8_t registerToRead = 0;
++ bool isReadMe = false;
++ uint8_t deviceIndex = 0;
+ std::vector<uint8_t> commandData;
+ std::optional<uint8_t> initCommand;
+ std::vector<uint8_t> initData;
+@@ -112,4 +161,6 @@ struct IpmbSensor : public Sensor
+ private:
+ sdbusplus::asio::object_server& objectServer;
+ boost::asio::deadline_timer waitTimer;
++
++ void getMeCommand();
+ };
+diff --git a/src/IpmbSensor.cpp b/src/IpmbSensor.cpp
+index eedf21e..75f74b5 100644
+--- a/src/IpmbSensor.cpp
++++ b/src/IpmbSensor.cpp
+@@ -150,6 +150,39 @@ void IpmbSensor::runInitCmd()
+ }
+ }
+
++/**
++ * Refernce:
++ * Intelligent Power Node Manager External Interface Specification
++ */
++void IpmbSensor::getMeCommand()
++{
++ /*
++ * Byte 1, 2, 3 = Manufacturer ID.
++ */
++ commandData.emplace_back(ipmi::sensor::manufacturerId[0]);
++ commandData.emplace_back(ipmi::sensor::manufacturerId[1]);
++ commandData.emplace_back(ipmi::sensor::manufacturerId[2]);
++
++ /*
++ * Byte 4 = Device Index.
++ */
++ commandData.emplace_back(deviceIndex);
++
++ /*
++ * Byte 5 = History index.
++ * bit 0 to 3 = History index. Supported value: 0Fh to retrieve
++ * current samples.
++ * bit 4 to 7 = Page number – used only for devices which support
++ * pages.
++ */
++ commandData.emplace_back(0x0F);
++
++ /*
++ * Byte 6 = First Register Offset.
++ */
++ commandData.emplace_back(0x00);
++}
++
+ void IpmbSensor::loadDefaults()
+ {
+ if (type == IpmbType::meSensor)
+@@ -164,28 +197,44 @@ void IpmbSensor::loadDefaults()
+ {
+ commandAddress = meAddress;
+ netfn = ipmi::me_bridge::netFn;
+- command = ipmi::me_bridge::sendRawPmbus;
+- initCommand = ipmi::me_bridge::sendRawPmbus;
+- // pmbus read temp
+- commandData = {0x57, 0x01, 0x00, 0x16, hostSMbusIndex,
+- deviceAddress, 0x00, 0x00, 0x00, 0x00,
+- 0x01, 0x02, 0x8d};
+- // goto page 0
+- initData = {0x57, 0x01, 0x00, 0x14, hostSMbusIndex,
+- deviceAddress, 0x00, 0x00, 0x00, 0x00,
+- 0x02, 0x00, 0x00, 0x00};
+ readingFormat = ReadingFormat::linearElevenBit;
++ if (isReadMe)
++ {
++ command = ipmi::sensor::read_me::getPmbusReadings;
++ getMeCommand();
++ }
++ else
++ {
++ command = ipmi::me_bridge::sendRawPmbus;
++ initCommand = ipmi::me_bridge::sendRawPmbus;
++ // pmbus read temp
++ commandData = {0x57, 0x01, 0x00, 0x16, hostSMbusIndex,
++ deviceAddress, 0x00, 0x00, 0x00, 0x00,
++ 0x01, 0x02, 0x8d};
++ // goto page 0
++ initData = {0x57, 0x01, 0x00, 0x14, hostSMbusIndex,
++ deviceAddress, 0x00, 0x00, 0x00, 0x00,
++ 0x02, 0x00, 0x00, 0x00};
++ }
+ }
+ else if (type == IpmbType::IR38363VR)
+ {
+ commandAddress = meAddress;
+ netfn = ipmi::me_bridge::netFn;
+- command = ipmi::me_bridge::sendRawPmbus;
+- // pmbus read temp
+- commandData = {0x57, 0x01, 0x00, 0x16, hostSMbusIndex,
+- deviceAddress, 00, 0x00, 0x00, 0x00,
+- 0x01, 0x02, 0x8D};
+ readingFormat = ReadingFormat::elevenBitShift;
++ if (isReadMe)
++ {
++ command = ipmi::sensor::read_me::getPmbusReadings;
++ getMeCommand();
++ }
++ else
++ {
++ command = ipmi::me_bridge::sendRawPmbus;
++ // pmbus read temp
++ commandData = {0x57, 0x01, 0x00, 0x16, hostSMbusIndex,
++ deviceAddress, 00, 0x00, 0x00, 0x00,
++ 0x01, 0x02, 0x8D};
++ }
+ }
+ else if (type == IpmbType::ADM1278HSC)
+ {
+@@ -194,20 +243,28 @@ void IpmbSensor::loadDefaults()
+ {
+ case IpmbSubType::temp:
+ case IpmbSubType::curr:
+- uint8_t snsNum;
+- if (subType == IpmbSubType::temp)
++ netfn = ipmi::me_bridge::netFn;
++ readingFormat = ReadingFormat::elevenBit;
++ if (isReadMe)
+ {
+- snsNum = 0x8d;
++ command = ipmi::sensor::read_me::getPmbusReadings;
++ getMeCommand();
+ }
+ else
+ {
+- snsNum = 0x8c;
++ uint8_t snsNum;
++ if (subType == IpmbSubType::temp)
++ {
++ snsNum = 0x8d;
++ }
++ else
++ {
++ snsNum = 0x8c;
++ }
++ command = ipmi::me_bridge::sendRawPmbus;
++ commandData = {0x57, 0x01, 0x00, 0x86, deviceAddress,
++ 0x00, 0x00, 0x01, 0x02, snsNum};
+ }
+- netfn = ipmi::me_bridge::netFn;
+- command = ipmi::me_bridge::sendRawPmbus;
+- commandData = {0x57, 0x01, 0x00, 0x86, deviceAddress,
+- 0x00, 0x00, 0x01, 0x02, snsNum};
+- readingFormat = ReadingFormat::elevenBit;
+ break;
+ case IpmbSubType::power:
+ case IpmbSubType::volt:
+@@ -224,17 +281,25 @@ void IpmbSensor::loadDefaults()
+ {
+ commandAddress = meAddress;
+ netfn = ipmi::me_bridge::netFn;
+- command = ipmi::me_bridge::sendRawPmbus;
+- initCommand = ipmi::me_bridge::sendRawPmbus;
+- // pmbus read temp
+- commandData = {0x57, 0x01, 0x00, 0x16, hostSMbusIndex,
+- deviceAddress, 0x00, 0x00, 0x00, 0x00,
+- 0x01, 0x02, 0x8d};
+- // goto page 0
+- initData = {0x57, 0x01, 0x00, 0x14, hostSMbusIndex,
+- deviceAddress, 0x00, 0x00, 0x00, 0x00,
+- 0x02, 0x00, 0x00, 0x00};
+ readingFormat = ReadingFormat::byte3;
++ if (isReadMe)
++ {
++ command = ipmi::sensor::read_me::getPmbusReadings;
++ getMeCommand();
++ }
++ else
++ {
++ command = ipmi::me_bridge::sendRawPmbus;
++ initCommand = ipmi::me_bridge::sendRawPmbus;
++ // pmbus read temp
++ commandData = {0x57, 0x01, 0x00, 0x16, hostSMbusIndex,
++ deviceAddress, 0x00, 0x00, 0x00, 0x00,
++ 0x01, 0x02, 0x8d};
++ // goto page 0
++ initData = {0x57, 0x01, 0x00, 0x14, hostSMbusIndex,
++ deviceAddress, 0x00, 0x00, 0x00, 0x00,
++ 0x02, 0x00, 0x00, 0x00};
++ }
+ }
+ else
+ {
+@@ -362,7 +427,19 @@ void IpmbSensor::read(void)
+ read();
+ return;
+ }
+- const std::vector<uint8_t>& data = std::get<5>(response);
++
++ std::vector<uint8_t> data;
++
++ if (isReadMe)
++ {
++ ipmi::sensor::read_me::getRawData(
++ registerToRead, std::get<5>(response), data);
++ }
++ else
++ {
++ data = std::get<5>(response);
++ }
++
+ if constexpr (debug)
+ {
+ std::cout << name << ": ";
+@@ -408,6 +485,74 @@ void IpmbSensor::read(void)
+ "sendRequest", commandAddress, netfn, lun, command, commandData);
+ });
+ }
++
++void IpmbSensor::setReadMethod(const SensorBaseConfigMap& sensorBaseConfig)
++{
++ /*
++ * Some sensor can be read in two ways
++ * 1) Using proxy: BMC read command is proxy forward by ME
++ * to sensor. 2) Using 'Get PMBUS Readings': ME responds to
++ * BMC with sensor data.
++ *
++ * By default we assume the method is 1. And if ReadMethod
++ * == "ReadME" we switch to method 2.
++ */
++ auto readMethod = sensorBaseConfig.find("ReadMethod");
++ if (readMethod == sensorBaseConfig.end())
++ {
++ std::cerr << "'ReadMethod' not found, defaulting to "
++ "proxy method of reading sensor\n";
++ return;
++ }
++
++ if (std::visit(VariantToStringVisitor(), readMethod->second) != "ReadME")
++ {
++ std::cerr << "'ReadMethod' != 'ReadME', defaulting to "
++ "proxy method of reading sensor\n";
++ return;
++ }
++
++ /*
++ * In 'Get PMBUS Readings' the response containt a
++ * set of registers from the sensor. And different
++ * values such as temperature power voltage will be
++ * mapped to different registers.
++ */
++ auto registerToReadConfig = sensorBaseConfig.find("Register");
++ if (registerToReadConfig == sensorBaseConfig.end())
++ {
++ std::cerr << "'Register' not found, defaulting to "
++ "proxy method of reading sensor\n";
++ return;
++ }
++
++ registerToRead =
++ std::visit(VariantToUnsignedIntVisitor(), registerToReadConfig->second);
++
++ /*
++ * In 'Get PMBUS Readings' since ME is
++ * responding with the sensor data we need
++ * to use the address for sensor in ME, this
++ * is different from the actual sensor
++ * address.
++ */
++ auto deviceIndexConfig = sensorBaseConfig.find("SensorMeAddress");
++ if (deviceIndexConfig == sensorBaseConfig.end())
++ {
++ std::cerr << "'SensorMeAddress' not found, defaulting to "
++ "proxy method of reading sensor\n";
++ return;
++ }
++
++ deviceIndex =
++ std::visit(VariantToUnsignedIntVisitor(), deviceIndexConfig->second);
++
++ /*
++ * We found all parameters to use 'Get PMBUS Readings'
++ * method.
++ */
++ isReadMe = true;
++}
+ void createSensors(
+ boost::asio::io_service& io, sdbusplus::asio::object_server& objectServer,
+ boost::container::flat_map<std::string, std::unique_ptr<IpmbSensor>>&
+@@ -485,6 +630,8 @@ void createSensors(
+ std::move(sensorThresholds), deviceAddress,
+ hostSMbusIndex, pollRate, sensorTypeName);
+
++ sensor->setReadMethod(entry.second);
++
+ /* Initialize scale and offset value */
+ sensor->scaleVal = 1;
+ sensor->offsetVal = 0;
+--
+2.17.1
+