dimm-spd-reader: fix PMIC type detection from SPD

Add support for identifying PMIC unknown state as 0xFF.

Update PMIC SPD detection flow to read SPD registers 0x200, 0x204, and 0x208. Check bit7 to determine whether the PMIC is installed, and use SPD bit[3:0] as the PMIC type.

Also update the IPMI error handling path to return completion code 0x07 for command 0x6c when PMIC detection fails or the PMIC type is unknown.
This commit is contained in:
Your Name
2026-04-27 10:01:06 +08:00
parent 182a0516ac
commit 0cc0c96893
6 changed files with 697 additions and 68 deletions
@@ -217,8 +217,14 @@ void updateField(const std::string& dimmName, FieldType field, T newValues[], si
} else {
std::cout << "Invalid size for status array!" << std::endl;
}
break;
case ERROR:
if (size == STATUS_ERROR_NUM) {
std::copy(newValues, newValues + size, std::begin(it->second.error));
} else {
std::cout << "Invalid size for error array!" << std::endl;
}
break;
default:
std::cout << "Unknown field type!" << std::endl;
break;
@@ -456,10 +462,10 @@ int DIMMSpdReader::getPmicType(uint8_t cpuid, uint8_t rank)
uint8_t index[] = {0xc8, 0xcc, 0xd0};
uint16_t wLen = 2;
//Jim debug uint8_t wrBuf[] = {0, 0};
uint8_t wrBuf[] = {0, 0, 0}; //Jim debug
uint8_t wrBuf[] = {0, 0, 0};
uint8_t rdBuf[] = {0};
uint16_t rLen = 1;
uint8_t typeVal = 0;
int spd = NUM_TO_SPD(rank);
const uint8_t memReg = 0x80;
@@ -484,12 +490,16 @@ int DIMMSpdReader::getPmicType(uint8_t cpuid, uint8_t rank)
{
return -1;
}
if((rdBuf[0] & 0xf) == 0x6)
{
pmicType[cpuid][rank] = PMIC5030;
break;
}
//SPD 0xc8, 0xcc, 0xd0 is Device Types
//Check DeviceType[7] = 1 is Installed
if((rdBuf[0] & 0x80) != 0)
{
//read DeviceType Bits[3:0]
typeVal = rdBuf[0] & 0xf;
pmicType[cpuid][rank] = (typeVal <= PMIC5030) ? static_cast<PMICType>(typeVal) : Unknown;
break;
}
}
if(pmicType[cpuid][rank] == PMIC5030)
@@ -499,8 +509,6 @@ int DIMMSpdReader::getPmicType(uint8_t cpuid, uint8_t rank)
gotType[cpuid][rank] = true;
//Jim debug+S
//----------------------------------------------------------------------------
//A. 1B Write R30 = 0x84 // 1 byte format, set 2 byte mode
//B. 2B Read R0030 //check 2 byte mode
@@ -516,7 +524,7 @@ int DIMMSpdReader::getPmicType(uint8_t cpuid, uint8_t rank)
int pmic = NUM_TO_PMIC(rank);
std::string i3cPmicName = I3C_ADDR(cpuid) + to_hex(pmic);
if constexpr (debug)
{
std::cout << "is PMIC5030"<< std::endl;
@@ -561,8 +569,8 @@ int DIMMSpdReader::getPmicType(uint8_t cpuid, uint8_t rank)
if constexpr (debug)
{
std::cout << "Is 2 Byte mode" << std::endl;
}
}
}
}
// It was originally 2 bytes mode
else
{
@@ -571,7 +579,7 @@ int DIMMSpdReader::getPmicType(uint8_t cpuid, uint8_t rank)
wLen = 3;
rLen = 0;
ret = i3cWriteRead(i3cPmicName, wrBuf, wLen, rdBuf, rLen);
std::this_thread::sleep_for(std::chrono::milliseconds(9));
//C2-2. 2B Read R30 = 0x84
wrBuf[0] = 0x30; wrBuf[1] = 0x00;
@@ -592,8 +600,9 @@ int DIMMSpdReader::getPmicType(uint8_t cpuid, uint8_t rank)
}
}
//Jim debug+E
if constexpr (debug)
{
std::cout << "pmicType[" << (int)cpuid << "][" << (int)rank << "] = "<< (int)pmicType[cpuid][rank] << std::endl;
@@ -736,10 +745,10 @@ int DIMMSpdReader::getDIMMRegsVol(uint8_t cpuid, uint8_t rank, DimmData& dimmDat
int index = (pmicType[cpuid][rank] == PMIC5030) ? 6 : 4;
for(int i=0; i<index; i++) {
//Write R30
rLen = 0; //Jim debug, For Renesas requirements, I3C write commands cannot be directly read; reading requires a separate I3C command.
rLen = 0; //For Renesas requirements, I3C write commands cannot be directly read; reading requires a separate I3C command.
if(twoByteMode[cpuid][rank])
{
adcValue = 0x4; //Jim debug, PMIC5030 fixed the 2 byte mode + 1ms
adcValue = 0x4; //PMIC5030 fixed the 2 byte mode + 1ms
wrBuf[2] = adcValue | adcSel[i];
wLen = 3;
}
@@ -754,14 +763,12 @@ int DIMMSpdReader::getDIMMRegsVol(uint8_t cpuid, uint8_t rank, DimmData& dimmDat
{
return -1;
}
//Jim debug std::this_thread::sleep_for(std::chrono::milliseconds(adcDelay[cpuid][rank]));
//Jim debug+S
// For Renesas requirements, the JESD spec PMIC5030 needs a 9ms delay.
if(pmicType[cpuid][rank] == PMIC5030)
std::this_thread::sleep_for(std::chrono::milliseconds(9));
else
std::this_thread::sleep_for(std::chrono::milliseconds(adcDelay[cpuid][rank]));
//Jim debug+E
wLen = twoByteMode[cpuid][rank] ? 2 : 1;
rLen = 1;
@@ -808,6 +815,147 @@ static double calStep31CurPower(int index, const uint8_t (&temp)[8])
return result;
}
int DIMMSpdReader::checkADCAccuracyStepSize(uint8_t cpuid, uint8_t rank, DimmData& dimmData, const uint8_t* TargetData)
{
int ret = -1;
if(cpuid >= MAX_CPU_ID || rank >= MAX_DIMM_RANK) return ret;
if constexpr (debug)
{
std::cout << "Start to check ADC Accuracy Step Size."<< std::endl;
}
uint16_t wLen;
uint16_t rLen;
uint8_t rdBuf[] = {0};
uint8_t wrBuf[3] = {0, 0, 0};
uint16_t ThresholdSel[6] = {0x1C, 0x1D, 0x1E, 0x1F, 0x11A, 0x11B}; //OUTPUT_HIGH_CURRENT_CONSUMPTION_WARNING_THRESHOLD
int pmic = NUM_TO_PMIC(rank);
std::string i3cPmicName = I3C_ADDR(cpuid) + to_hex(pmic);
// 1. Read R32
rLen = 1;
wLen = 2;
wrBuf[0] = 0x32; //address_Low
wrBuf[1] = 0; //address_High
ret = i3cWriteRead(i3cPmicName, wrBuf, wLen, rdBuf, rLen);
if (ret < 0)
{
return -1;
}
if((rdBuf[0] & 0x3) != 1)
{
dimmData.error[0] |= 1;
}
// 2. Read ThresholdSel {0x1C, 0x1D, 0x1E, 0x1F, 0x11A, 0x11B}
rLen = 1;
wLen = 2;
for(int i=0; i<6; i++) {
wrBuf[0] = static_cast<uint8_t>(ThresholdSel[i] & 0xFF); //address_Low
wrBuf[1] = static_cast<uint8_t>((ThresholdSel[i]>>8) & 0xFF); //address_High
ret = i3cWriteRead(i3cPmicName, wrBuf, wLen, rdBuf, rLen);
if (ret < 0)
{
return -1;
}
if(rdBuf[0] != TargetData[i])
{
dimmData.error[0] |= 1<<(i+1);
}
}
return ret;
}
int DIMMSpdReader::setADCAccuracyStepSize(uint8_t cpuid, uint8_t rank, DimmData& dimmData, uint8_t R32)
{
int ret = -1;
if(cpuid >= MAX_CPU_ID || rank >= MAX_DIMM_RANK) return ret;
if constexpr (debug)
{
std::cout << "Start to set Pmic5030 ADC Accuracy Step Size."<< std::endl;
}
uint16_t wLen;
uint16_t rLen;
uint8_t rdBuf[] = {0};
uint8_t wrBuf[3] = {0, 0, 0};
uint16_t ThresholdSel[6] = {0x1C, 0x1D, 0x1E, 0x1F, 0x11A, 0x11B}; //OUTPUT_HIGH_CURRENT_CONSUMPTION_WARNING_THRESHOLD
uint8_t ThresholdData[6] = {0, 0, 0, 0, 0, 0};
uint8_t TargetData[6] = {0, 0, 0, 0, 0, 0};
uint8_t adcAcc[] = {0x32, 0, 0};
uint32_t calcData = 0;
constexpr int PMIC_CHANNEL_SWD_INDEX = 3;
constexpr uint8_t PMIC_MAX_CUR_7500MA_REG_VAL = 0xF0; // SWA, SWB, SWC, SWE, SWF
constexpr uint8_t PMIC_MAX_CUR_5000MA_REG_VAL = 0xA0; // SWD
int pmic = NUM_TO_PMIC(rank);
std::string i3cPmicName = I3C_ADDR(cpuid) + to_hex(pmic);
// 1. Read ThresholdSel {0x1C, 0x1D, 0x1E, 0x1F, 0x11A, 0x11B}
rLen = 1;
wLen = 2;
for(int i=0; i<6; i++) {
wrBuf[0] = static_cast<uint8_t>(ThresholdSel[i] & 0xFF); //address_Low
wrBuf[1] = static_cast<uint8_t>((ThresholdSel[i]>>8) & 0xFF); //address_High
ret = i3cWriteRead(i3cPmicName, wrBuf, wLen, &ThresholdData[i], rLen);
if (ret < 0)
{
return -1;
}
}
// 2. Write R32[1:0] = 01b
adcAcc[2] = (R32&0xFC)|0x1;
wLen = 3;
rLen = 0;
ret = i3cWriteRead(i3cPmicName, adcAcc, wLen, rdBuf, rLen);
if (ret < 0)
{
//std::cerr << " read word data failed. \n";
return -1;
}
// 3. Write ThresholdData
for(int i=0; i<6; i++) {
rLen = 0;
wLen = 3;
wrBuf[0] = static_cast<uint8_t>(ThresholdSel[i] & 0xFF); //address_Low
wrBuf[1] = static_cast<uint8_t>((ThresholdSel[i]>>8) & 0xFF); //address_High
//data
calcData = (ThresholdData[i] * 4); // 125/31.25 = 4
if (i == PMIC_CHANNEL_SWD_INDEX) {
// SWD Maximum current is 5000mA
TargetData[i] = (calcData > PMIC_MAX_CUR_5000MA_REG_VAL) ? PMIC_MAX_CUR_5000MA_REG_VAL : static_cast<uint8_t>(calcData);
}
else {
// SWA, SWB, SWC, SWE, SWF Maximum current is 7500mA
TargetData[i] = (calcData > PMIC_MAX_CUR_7500MA_REG_VAL) ? PMIC_MAX_CUR_7500MA_REG_VAL : static_cast<uint8_t>(calcData);
}
wrBuf[2] = TargetData[i];
ret = i3cWriteRead(i3cPmicName, wrBuf, wLen, rdBuf, rLen);
if (ret < 0)
{
return -1;
}
}
ret = checkADCAccuracyStepSize(cpuid, rank, dimmData, TargetData);
if (ret < 0)
{
//std::cerr << " set ADCAccuracy Step Size failed. \n";
return -1;
}
return ret;
}
int DIMMSpdReader::getDIMMRegsCurAndPow(uint8_t cpuid, uint8_t rank, DimmData& dimmData)
{
@@ -828,8 +976,7 @@ int DIMMSpdReader::getDIMMRegsCurAndPow(uint8_t cpuid, uint8_t rank, DimmData& d
uint8_t adcSelect[] = {0x1b, 0, 0};
uint16_t curOut[8];
uint8_t temp[8];
uint8_t rdBuf[] = {0, 0, 0, 0, 0, 0, 0, 0}; //Jim debug, For Renesas requirements, I3C command: Continuously read R100~R107
//Jim debug uint8_t rdBuf[] = {0};
uint8_t rdBuf[] = {0, 0, 0, 0, 0, 0, 0, 0}; //For Renesas requirements, I3C command: Continuously read R100~R107
uint8_t wrBuf[] = {0, 0};
int pmic = NUM_TO_PMIC(rank);
@@ -846,6 +993,20 @@ int DIMMSpdReader::getDIMMRegsCurAndPow(uint8_t cpuid, uint8_t rank, DimmData& d
return -1;
}
if((rdBuf[0] & 0x3) == 1) accStep31 = true;
else
{
if(twoByteMode[cpuid][rank])
{
//if two Byte Mode + PMIC5030, R32[1:0] set 01b
ret = setADCAccuracyStepSize(cpuid, rank, dimmData, rdBuf[0]);
accStep31 = true;
if (ret < 0)
{
//std::cerr << " set ADCAccuracy Step Size failed. \n";
return -1;
}
}
}
if constexpr (debug)
{
std::cout << "accStep31: " << (int)accStep31 << std::endl;
@@ -886,7 +1047,7 @@ int DIMMSpdReader::getDIMMRegsCurAndPow(uint8_t cpuid, uint8_t rank, DimmData& d
adcSelect[1] = rdBuf[0] & 0xbf; //Read current
wLen = 2;
}
rLen = 0; //Jim debug, For Renesas requirements, I3C write commands cannot be directly read; reading requires a separate I3C command.
rLen = 0; //For Renesas requirements, I3C write commands cannot be directly read; reading requires a separate I3C command.
ret = i3cWriteRead(i3cPmicName, adcSelect, wLen, rdBuf, rLen);
if (ret < 0)
{
@@ -916,13 +1077,6 @@ int DIMMSpdReader::getDIMMRegsCurAndPow(uint8_t cpuid, uint8_t rank, DimmData& d
{
return -1;
}
else
{
for(int i=0; i<6; i++)
{
dimmData.current[i] = calStep31CurPower(i, temp);
}
}
}
ret = i3cWriteRead(i3cPmicName, wrBuf, wLen, rdBuf, rLen);
@@ -963,10 +1117,10 @@ int DIMMSpdReader::getDIMMRegsCurAndPow(uint8_t cpuid, uint8_t rank, DimmData& d
}
dimmData.current[i] = rdBuf[0] * 125.0 / 1000.0;
}
}
//start to read power
if(twoByteMode[cpuid][rank])
@@ -979,7 +1133,7 @@ int DIMMSpdReader::getDIMMRegsCurAndPow(uint8_t cpuid, uint8_t rank, DimmData& d
adcSelect[1] |= 0x40;
wLen = 2;
}
rLen = 0; //Jim debug, For Renesas requirements, I3C write commands cannot be directly read; reading requires a separate I3C command.
rLen = 0; //For Renesas requirements, I3C write commands cannot be directly read; reading requires a separate I3C command.
ret = i3cWriteRead(i3cPmicName, adcSelect, wLen, rdBuf, rLen);
if (ret < 0)
{
@@ -1009,13 +1163,6 @@ int DIMMSpdReader::getDIMMRegsCurAndPow(uint8_t cpuid, uint8_t rank, DimmData& d
{
return -1;
}
else
{
for(int i=0; i<6; i++)
{
dimmData.current[i] = calStep31CurPower(i, temp);
}
}
}
ret = i3cWriteRead(i3cPmicName, wrBuf, wLen, rdBuf, rLen);
@@ -1026,7 +1173,7 @@ int DIMMSpdReader::getDIMMRegsCurAndPow(uint8_t cpuid, uint8_t rank, DimmData& d
for(int i=0; i<index; i++) {
temp[i] = rdBuf[i];
}
for(int i=0; i<6; i++)
{
dimmData.power[i] = calStep31CurPower(i, temp);
@@ -1056,7 +1203,7 @@ int DIMMSpdReader::getDIMMRegsCurAndPow(uint8_t cpuid, uint8_t rank, DimmData& d
return -1;
}
dimmData.power[i] = rdBuf[0] * 125.0 / 1000.0;
}
}
@@ -1097,6 +1244,7 @@ void DIMMSpdReader::init()
for(int i=0; i<MAX_DIMM_RANK; i++) {
DimmData dData = {
{0, 0, 0, 0, 0, 0, 0, 0},
{0},
{std::numeric_limits<double>::quiet_NaN(), std::numeric_limits<double>::quiet_NaN(),
std::numeric_limits<double>::quiet_NaN(), std::numeric_limits<double>::quiet_NaN(),
std::numeric_limits<double>::quiet_NaN(), std::numeric_limits<double>::quiet_NaN()},
@@ -1113,7 +1261,7 @@ void DIMMSpdReader::init()
dimmDataTable.insert_or_assign(dimmName, std::move(dData));
pmicType[cpuid][i] = PMIC5010;
pmicType[cpuid][i] = Unknown;
gotType[cpuid][i] = false;
twoByteMode[cpuid][i] = false;
}
@@ -1131,21 +1279,19 @@ void DIMMSpdReader::readDimmInfo()
for(int cpuid=0; cpuid<MAX_CPU_ID; cpuid++) {
for(int i=0; i<MAX_DIMM_RANK; i++) {
DimmData dData = {
{0, 0, 0, 0, 0, 0, 0, 0},
{std::numeric_limits<double>::quiet_NaN(), std::numeric_limits<double>::quiet_NaN(),
std::numeric_limits<double>::quiet_NaN(), std::numeric_limits<double>::quiet_NaN(),
std::numeric_limits<double>::quiet_NaN(), std::numeric_limits<double>::quiet_NaN()},
{std::numeric_limits<double>::quiet_NaN(), std::numeric_limits<double>::quiet_NaN(),
std::numeric_limits<double>::quiet_NaN(), std::numeric_limits<double>::quiet_NaN(),
std::numeric_limits<double>::quiet_NaN(), std::numeric_limits<double>::quiet_NaN()},
{std::numeric_limits<double>::quiet_NaN(), std::numeric_limits<double>::quiet_NaN(),
std::numeric_limits<double>::quiet_NaN(), std::numeric_limits<double>::quiet_NaN(),
std::numeric_limits<double>::quiet_NaN(), std::numeric_limits<double>::quiet_NaN()},
{std::numeric_limits<double>::quiet_NaN(), std::numeric_limits<double>::quiet_NaN(),
std::numeric_limits<double>::quiet_NaN()}};
std::string dimmName = "DIMM_CPU" + std::string(1, '0' + cpuid) + "_" + std::string(1, 'A' + i);
DimmData dData = dimmDataTable[dimmName];
for (int j = 0; j < STATUS_TEMP_NUM; j++) {
dData.temp[j] = std::numeric_limits<double>::quiet_NaN();
}
for (int j = 0; j < STATUS_PMIC_RAIL_NUM; j++) {
dData.voltage[j] = std::numeric_limits<double>::quiet_NaN();
dData.current[j] = std::numeric_limits<double>::quiet_NaN();
dData.power[j] = std::numeric_limits<double>::quiet_NaN();
}
for (int j = 0; j < STATUS_REG_NUM; j++) {
dData.reg[j] = 0;
}
int index = cpuid * 4 + i / 3;
if(index != lastIndex) {
@@ -1211,6 +1357,7 @@ void DIMMSpdReader::readDimmInfo()
updateField(dimmName, CURRENT, dData.current, STATUS_PMIC_RAIL_NUM);
updateField(dimmName, POWER, dData.power, STATUS_PMIC_RAIL_NUM);
updateField(dimmName, ERROR, dData.error, STATUS_ERROR_NUM);
}
#endif
}
@@ -1266,6 +1413,11 @@ void DIMMSpdReader::updateDbus()
dimm_statusReg_inf[data.first]->set_property("0x0a", data.second.reg[6]);
dimm_statusReg_inf[data.first]->set_property("0x0b", data.second.reg[7]);
}
if(dimm_errCode_inf.find(data.first) != dimm_errCode_inf.end()) {
dimm_errCode_inf[data.first]->set_property("0x32", data.second.error[0]);
}
}
}
@@ -1320,6 +1472,9 @@ void DIMMSpdReader::updateToDimmInfoFile()
js[data.first]["Status_Reg"]["0x09"] = data.second.reg[5];
js[data.first]["Status_Reg"]["0x0a"] = data.second.reg[6];
js[data.first]["Status_Reg"]["0x0b"] = data.second.reg[7];
//update error code
js[data.first]["Error_Code"]["0x32"] = data.second.error[0];
}
jsonOutputFile << js.dump(4);
@@ -1355,7 +1510,7 @@ void DIMMSpdReader::setupMatches(sdbusplus::bus_t& connection)
if (ret != 0) {
std::cout << "Failed to modprobe dw-i3c-master module" << std::endl;
return;
}
}
std::this_thread::sleep_for(std::chrono::milliseconds(500));
//switch dimm i3c to bmc
std::cout << "start to Read" << std::endl;
@@ -16,28 +16,33 @@
#define STATUS_TEMP_NUM 3
#define STATUS_PMIC_RAIL_NUM 6
#define STATUS_REG_NUM 8
#define STATUS_ERROR_NUM 1
extern std::unordered_map<std::string, std::shared_ptr<sdbusplus::asio::dbus_interface>> dimm_temp_inf;
extern std::unordered_map<std::string, std::shared_ptr<sdbusplus::asio::dbus_interface>> dimm_voltage_inf;
extern std::unordered_map<std::string, std::shared_ptr<sdbusplus::asio::dbus_interface>> dimm_current_inf;
extern std::unordered_map<std::string, std::shared_ptr<sdbusplus::asio::dbus_interface>> dimm_power_inf;
extern std::unordered_map<std::string, std::shared_ptr<sdbusplus::asio::dbus_interface>> dimm_statusReg_inf;
extern std::unordered_map<std::string, std::shared_ptr<sdbusplus::asio::dbus_interface>> dimm_errCode_inf;
enum FieldType {
VOLTAGE,
CURRENT,
POWER,
TEMP,
STATUS
STATUS,
ERROR
};
struct DimmData
{
uint8_t reg[STATUS_REG_NUM];
uint8_t error[STATUS_ERROR_NUM];
double voltage[STATUS_PMIC_RAIL_NUM];
double current[STATUS_PMIC_RAIL_NUM];
double power[STATUS_PMIC_RAIL_NUM];
double temp[STATUS_TEMP_NUM];
};
struct DimmI3CAddr
@@ -53,7 +58,8 @@ enum PMICType {
PMIC5020,
PMIC5120,
PMIC5200,
PMIC5030
PMIC5030,
Unknown = 0xFF
};
struct DIMMSpdReader : std::enable_shared_from_this<DIMMSpdReader>
@@ -81,6 +87,8 @@ struct DIMMSpdReader : std::enable_shared_from_this<DIMMSpdReader>
void getdimmI3cSwitchState(const std::shared_ptr<sdbusplus::asio::connection>& conn, size_t retries = 2);
int getPmicType(uint8_t cpuid, uint8_t rank);
int getPmicStatusRegs(uint8_t cpuid, uint8_t rank, DimmData& dimmData);
int setADCAccuracyStepSize(uint8_t cpuid, uint8_t rank, DimmData& dimmData, uint8_t R32);
int checkADCAccuracyStepSize(uint8_t cpuid, uint8_t rank, DimmData& dimmData, const uint8_t* ThresholdData);
private:
std::vector<sdbusplus::bus::match_t> matches;
@@ -59,6 +59,8 @@ std::unordered_map<std::string, std::shared_ptr<sdbusplus::asio::dbus_interface>
std::unordered_map<std::string, std::shared_ptr<sdbusplus::asio::dbus_interface>> dimm_current_inf;
std::unordered_map<std::string, std::shared_ptr<sdbusplus::asio::dbus_interface>> dimm_power_inf;
std::unordered_map<std::string, std::shared_ptr<sdbusplus::asio::dbus_interface>> dimm_statusReg_inf;
std::unordered_map<std::string, std::shared_ptr<sdbusplus::asio::dbus_interface>> dimm_errCode_inf;
int main()
{
@@ -177,6 +179,13 @@ int main()
dimm_statusReg_inf[dimm]->register_property(
"0x0b", uint8_t(0), sdbusplus::asio::PropertyPermission::readWrite);
dimm_statusReg_inf[dimm]->initialize();
dimm_errCode_inf[dimm] = objectServer.add_interface(interfacePath,
"xyz.openbmc_project.Dimm.ErrCode");
dimm_errCode_inf[dimm]->register_property(
"0x32", uint8_t(0), sdbusplus::asio::PropertyPermission::readWrite);
dimm_errCode_inf[dimm]->initialize();
}
double pollRate = 0.1;