ipmbsensor: add support for an Ampere SMPro on an IPMB
On ADLINK systems, the SMPro can be reached via the MMC (Module
Management Controller), which is a second BMC. It sits on an IPMB
bus.
Add support for reading power, voltage, current and temperature values
from the SMPro.
Tested: built ipmbsensor and ran new ipmb unit tests.
Change-Id: Ib9862486a18f77fb58d3acd59de7686750029b56
Signed-off-by: Rebecca Cran <rebecca@bsdio.com>
diff --git a/src/IpmbSensor.cpp b/src/IpmbSensor.cpp
index 30531bf..fbc4b7d 100644
--- a/src/IpmbSensor.cpp
+++ b/src/IpmbSensor.cpp
@@ -249,6 +249,33 @@
0x02, 0x00, 0x00, 0x00};
readingFormat = ReadingFormat::byte3;
}
+ else if (type == IpmbType::SMPro)
+ {
+ // This is an Ampere SMPro reachable via a BMC. For example,
+ // this architecture is used on ADLINK Ampere Altra systems.
+ // See the Ampere Family SoC BMC Interface Specification at
+ // https://amperecomputing.com/customer-connect/products/altra-family-software---firmware
+ // for details of the sensors.
+ commandAddress = 0;
+ netfn = 0x30;
+ command = 0x31;
+ commandData = {0x9e, deviceAddress};
+ switch (subType)
+ {
+ case IpmbSubType::temp:
+ readingFormat = ReadingFormat::nineBit;
+ break;
+ case IpmbSubType::power:
+ readingFormat = ReadingFormat::tenBit;
+ break;
+ case IpmbSubType::curr:
+ case IpmbSubType::volt:
+ readingFormat = ReadingFormat::fifteenBit;
+ break;
+ default:
+ throw std::runtime_error("Invalid sensor type");
+ }
+ }
else
{
throw std::runtime_error("Invalid sensor type");
@@ -296,6 +323,51 @@
resp = data[3];
return true;
}
+ case (ReadingFormat::nineBit):
+ case (ReadingFormat::tenBit):
+ case (ReadingFormat::fifteenBit):
+ {
+ if (data.size() != 2)
+ {
+ if (errCount == 0U)
+ {
+ std::cerr << "Invalid data length returned\n";
+ }
+ return false;
+ }
+
+ // From the Altra Family SoC BMC Interface Specification:
+ // 0xFFFF – This sensor data is either missing or is not supported
+ // by the device.
+ if ((data[0] == 0xff) && (data[1] == 0xff))
+ {
+ return false;
+ }
+
+ if (readingFormat == ReadingFormat::nineBit)
+ {
+ int16_t value = data[0];
+ if ((data[1] & 0x1) != 0)
+ {
+ // Sign extend to 16 bits
+ value |= 0xFF00;
+ }
+ resp = value;
+ }
+ else if (readingFormat == ReadingFormat::tenBit)
+ {
+ uint16_t value = ((data[1] & 0x3) << 8) + data[0];
+ resp = value;
+ }
+ else if (readingFormat == ReadingFormat::fifteenBit)
+ {
+ uint16_t value = ((data[1] & 0x7F) << 8) + data[0];
+ // Convert mV to V
+ resp = value / 1000.0;
+ }
+
+ return true;
+ }
case (ReadingFormat::elevenBit):
{
if (data.size() < 5)
@@ -464,6 +536,10 @@
{
type = IpmbType::meSensor;
}
+ else if (sensorClass == "SMPro")
+ {
+ type = IpmbType::SMPro;
+ }
else
{
std::cerr << "Invalid class " << sensorClass << "\n";
diff --git a/src/IpmbSensor.hpp b/src/IpmbSensor.hpp
index d22e36d..953e44a 100644
--- a/src/IpmbSensor.hpp
+++ b/src/IpmbSensor.hpp
@@ -20,7 +20,8 @@
PXE1410CVR,
IR38363VR,
ADM1278HSC,
- mpsVR
+ mpsVR,
+ SMPro
};
enum class IpmbSubType
@@ -37,9 +38,12 @@
{
byte0,
byte3,
+ nineBit,
+ tenBit,
elevenBit,
elevenBitShift,
- linearElevenBit
+ linearElevenBit,
+ fifteenBit
};
namespace ipmi
diff --git a/tests/test_IpmbSensor.cpp b/tests/test_IpmbSensor.cpp
index 3168993..5ba7521 100644
--- a/tests/test_IpmbSensor.cpp
+++ b/tests/test_IpmbSensor.cpp
@@ -20,4 +20,272 @@
responseValue, errCount));
EXPECT_EQ(responseValue, 42.0);
}
+
+TEST(IPMBSensor, NineBitValidPositive)
+{
+ std::vector<uint8_t> data;
+ data.push_back(0x2a);
+ data.push_back(0x00);
+
+ double responseValue = 0.0;
+ size_t errCount = 0;
+ EXPECT_TRUE(IpmbSensor::processReading(ReadingFormat::nineBit, 0, data,
+ responseValue, errCount));
+ EXPECT_EQ(responseValue, 42.0);
+ EXPECT_EQ(errCount, 0);
+}
+
+TEST(IPMBSensor, NineBitValidNegative)
+{
+ std::vector<uint8_t> data;
+ data.push_back(0x9c);
+ data.push_back(0x01);
+
+ double responseValue = 0.0;
+ size_t errCount = 0;
+ EXPECT_TRUE(IpmbSensor::processReading(ReadingFormat::nineBit, 0, data,
+ responseValue, errCount));
+ EXPECT_EQ(responseValue, -100.0);
+ EXPECT_EQ(errCount, 0);
+}
+
+TEST(IPMBSensor, NineBitMin)
+{
+ std::vector<uint8_t> data;
+ data.push_back(0x01);
+ data.push_back(0x01);
+
+ double responseValue = 0.0;
+ size_t errCount = 0;
+ EXPECT_TRUE(IpmbSensor::processReading(ReadingFormat::nineBit, 0, data,
+ responseValue, errCount));
+ EXPECT_EQ(responseValue, -255.0);
+ EXPECT_EQ(errCount, 0);
+}
+
+// The Altra Family SoC BMC Interface Specification says the maximum 9-bit value
+// is 256, but that can't be represented in 9 bits, so test the max as 255.
+TEST(IPMBSensor, NineBitMax)
+{
+ std::vector<uint8_t> data;
+ data.push_back(0xff);
+ data.push_back(0x00);
+
+ double responseValue = 0.0;
+ size_t errCount = 0;
+ EXPECT_TRUE(IpmbSensor::processReading(ReadingFormat::nineBit, 0, data,
+ responseValue, errCount));
+ EXPECT_EQ(responseValue, 255.0);
+ EXPECT_EQ(errCount, 0);
+}
+
+TEST(IPMBSensor, NineBitTooShort)
+{
+ std::vector<uint8_t> data;
+ data.push_back(0x00);
+
+ double responseValue = 0.0;
+ size_t errCount = 0;
+ EXPECT_FALSE(IpmbSensor::processReading(ReadingFormat::nineBit, 0, data,
+ responseValue, errCount));
+}
+
+TEST(IPMBSensor, NineBitTooLong)
+{
+ std::vector<uint8_t> data;
+ data.push_back(0x00);
+ data.push_back(0x00);
+ data.push_back(0x00);
+
+ double responseValue = 0.0;
+ size_t errCount = 0;
+ EXPECT_FALSE(IpmbSensor::processReading(ReadingFormat::nineBit, 0, data,
+ responseValue, errCount));
+}
+
+TEST(IPMBSensor, NineBitInvalid)
+{
+ std::vector<uint8_t> data;
+ data.push_back(0xff);
+ data.push_back(0xff);
+
+ double responseValue = 0.0;
+ size_t errCount = 0;
+ EXPECT_FALSE(IpmbSensor::processReading(ReadingFormat::nineBit, 0, data,
+ responseValue, errCount));
+}
+
+TEST(IPMBSensor, TenBitValid1)
+{
+ std::vector<uint8_t> data;
+ data.push_back(0x08);
+ data.push_back(0x00);
+
+ double responseValue = 0.0;
+ size_t errCount = 0;
+ EXPECT_TRUE(IpmbSensor::processReading(ReadingFormat::tenBit, 0, data,
+ responseValue, errCount));
+ EXPECT_EQ(responseValue, 8.0);
+ EXPECT_EQ(errCount, 0);
+}
+
+TEST(IPMBSensor, TenBitValid2)
+{
+ std::vector<uint8_t> data;
+ data.push_back(0x30);
+ data.push_back(0x02);
+
+ double responseValue = 0.0;
+ size_t errCount = 0;
+ EXPECT_TRUE(IpmbSensor::processReading(ReadingFormat::tenBit, 0, data,
+ responseValue, errCount));
+
+ EXPECT_EQ(responseValue, 560.0);
+ EXPECT_EQ(errCount, 0);
+}
+
+TEST(IPMBSensor, TenBitMin)
+{
+ std::vector<uint8_t> data;
+ data.push_back(0x00);
+ data.push_back(0x00);
+
+ double responseValue = 0.0;
+ size_t errCount = 0;
+ EXPECT_TRUE(IpmbSensor::processReading(ReadingFormat::tenBit, 0, data,
+ responseValue, errCount));
+
+ EXPECT_EQ(responseValue, 0.0);
+ EXPECT_EQ(errCount, 0);
+}
+
+TEST(IPMBSensor, TenBitValidMax)
+{
+ std::vector<uint8_t> data;
+ data.push_back(0xff);
+ data.push_back(0x03);
+
+ double responseValue = 0.0;
+ size_t errCount = 0;
+ EXPECT_TRUE(IpmbSensor::processReading(ReadingFormat::tenBit, 0, data,
+ responseValue, errCount));
+
+ EXPECT_EQ(responseValue, 1023.0);
+ EXPECT_EQ(errCount, 0);
+}
+
+TEST(IPMBSensor, TenBitTooShort)
+{
+ std::vector<uint8_t> data;
+ data.push_back(0xff);
+
+ double responseValue = 0.0;
+ size_t errCount = 0;
+ EXPECT_FALSE(IpmbSensor::processReading(ReadingFormat::tenBit, 0, data,
+ responseValue, errCount));
+}
+
+TEST(IPMBSensor, TenBitTooLong)
+{
+ std::vector<uint8_t> data;
+ data.push_back(0x00);
+ data.push_back(0x00);
+ data.push_back(0x00);
+
+ double responseValue = 0.0;
+ size_t errCount = 0;
+ EXPECT_FALSE(IpmbSensor::processReading(ReadingFormat::tenBit, 0, data,
+ responseValue, errCount));
+}
+
+TEST(IPMBSensor, TenBitInvalid)
+{
+ std::vector<uint8_t> data;
+ data.push_back(0xff);
+ data.push_back(0xff);
+
+ double responseValue = 0.0;
+ size_t errCount = 0;
+ EXPECT_FALSE(IpmbSensor::processReading(ReadingFormat::tenBit, 0, data,
+ responseValue, errCount));
+}
+
+TEST(IPMBSensor, FifteenBitValid1)
+{
+ std::vector<uint8_t> data;
+ data.push_back(0xda);
+ data.push_back(0x02);
+
+ double responseValue = 0.0;
+ size_t errCount = 0;
+ EXPECT_TRUE(IpmbSensor::processReading(ReadingFormat::fifteenBit, 0, data,
+ responseValue, errCount));
+ EXPECT_EQ(responseValue, 0.730);
+ EXPECT_EQ(errCount, 0);
+}
+
+TEST(IPMBSensor, FifteenBitMin)
+{
+ std::vector<uint8_t> data;
+ data.push_back(0x00);
+ data.push_back(0x00);
+
+ double responseValue = 0.0;
+ size_t errCount = 0;
+ EXPECT_TRUE(IpmbSensor::processReading(ReadingFormat::fifteenBit, 0, data,
+ responseValue, errCount));
+ EXPECT_EQ(responseValue, 0.0);
+ EXPECT_EQ(errCount, 0);
+}
+
+TEST(IPMBSensor, FifteenBitMax)
+{
+ std::vector<uint8_t> data;
+ data.push_back(0xff);
+ data.push_back(0x7f);
+
+ double responseValue = 0.0;
+ size_t errCount = 0;
+ EXPECT_TRUE(IpmbSensor::processReading(ReadingFormat::fifteenBit, 0, data,
+ responseValue, errCount));
+ EXPECT_EQ(responseValue, 32.767);
+ EXPECT_EQ(errCount, 0);
+}
+
+TEST(IPMBSensor, FifteenBitTooShort)
+{
+ std::vector<uint8_t> data;
+ data.push_back(0xff);
+
+ double responseValue = 0.0;
+ size_t errCount = 0;
+ EXPECT_FALSE(IpmbSensor::processReading(ReadingFormat::fifteenBit, 0, data,
+ responseValue, errCount));
+}
+
+TEST(IPMBSensor, FifteenBitTooLong)
+{
+ std::vector<uint8_t> data;
+ data.push_back(0x00);
+ data.push_back(0x00);
+ data.push_back(0x00);
+
+ double responseValue = 0.0;
+ size_t errCount = 0;
+ EXPECT_FALSE(IpmbSensor::processReading(ReadingFormat::fifteenBit, 0, data,
+ responseValue, errCount));
+}
+
+TEST(IPMBSensor, FifteenBitInvalid)
+{
+ std::vector<uint8_t> data;
+ data.push_back(0xff);
+ data.push_back(0xff);
+
+ double responseValue = 0.0;
+ size_t errCount = 0;
+ EXPECT_FALSE(IpmbSensor::processReading(ReadingFormat::fifteenBit, 0, data,
+ responseValue, errCount));
+}
+
} // namespace