Files
2026-04-23 17:48:19 +08:00

1366 lines
42 KiB
C++
Executable File

/*
// Copyright (c) 2019 Intel Corporation
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
*/
#include "dimmSpdReader.hpp"
#include "i3cdev.h"
#include <gpiod.hpp>
#include <phosphor-logging/lg2.hpp>
#include <stdint.h>
#include <chrono>
#include <cmath>
#include <functional>
#include <limits>
#include <memory>
#include <numeric>
#include <string>
#include <vector>
#include <filesystem>
#include <fstream>
#include <iostream>
#include <ctime>
#include <sstream>
#include <iomanip>
#include <unistd.h>
#include <nlohmann/json.hpp>
static constexpr bool debug = false;
#define PECI_MBX_INDEX_DDR_DIMM_TEMP 0x0E
constexpr auto dimmSensorInterface = "xyz.openbmc_project.Sensor.dimmLED";
constexpr auto dimmSensorPath = "/xyz/openbmc_project/sensors/temperature/";
constexpr auto HostMiscDbusName = "xyz.openbmc_project.Host.Misc.Manager";
constexpr auto platformStatePath = "/xyz/openbmc_project/misc/platform_state";
constexpr auto platformStateInterface = "xyz.openbmc_project.State.Host.Misc";
constexpr auto postCompleteProperty = "PostComplete";
static constexpr const char* dimmInfoPath = "/var/log/dimmInfo.json";
static constexpr const char* dimmInfoTempPath = "/usr/share/dimm-spd-reader/dimmInfo_template.json";
static int lastIndex = -1;
#define NUM_TO_SPD(n) (((n) % 3) * 2)
#define NUM_TO_PMIC(n) (((n) % 3) * 2 + 8)
#define I3C_ADDR(cpuid) ("/dev/i3c-" + std::to_string(cpuid) + "-3c00000000")
#define I3C_TS0_ADDR(cpuid) ("/dev/i3c-" + std::to_string(cpuid) + "-3c00000001")
#define I3C_TS1_ADDR(cpuid) ("/dev/i3c-" + std::to_string(cpuid) + "-3c00000003")
static boost::container::flat_map<std::string, DimmData> dimmDataTable;
const boost::container::flat_map<std::string, bool> cfgCpu0DimmABC = {
{"I3C_SPD_BMC_MUX0_SEL", false},
{"I3C_SPD_BMC_MUX0_EN", false},
{"I3C_SPD_BMC_MUX1_SEL", true},
{"I3C_SPD_BMC_MUX1_EN", true}};
const boost::container::flat_map<std::string, bool> cfgCpu0DimmDEF = {
{"I3C_SPD_BMC_MUX0_SEL", true},
{"I3C_SPD_BMC_MUX0_EN", false},
{"I3C_SPD_BMC_MUX1_SEL", true},
{"I3C_SPD_BMC_MUX1_EN", true}};
const boost::container::flat_map<std::string, bool> cfgCpu0DimmGHI = {
{"I3C_SPD_BMC_MUX0_SEL", true},
{"I3C_SPD_BMC_MUX0_EN", true},
{"I3C_SPD_BMC_MUX1_SEL", false},
{"I3C_SPD_BMC_MUX1_EN", false}};
const boost::container::flat_map<std::string, bool> cfgCpu0DimmJKL = {
{"I3C_SPD_BMC_MUX0_SEL", true},
{"I3C_SPD_BMC_MUX0_EN", true},
{"I3C_SPD_BMC_MUX1_SEL", true},
{"I3C_SPD_BMC_MUX1_EN", false}};
const boost::container::flat_map<std::string, bool> cfgCpu1DimmABC = {
{"I3C_SPD_BMC_MUX2_SEL", false},
{"I3C_SPD_BMC_MUX2_EN", false},
{"I3C_SPD_BMC_MUX3_SEL", true},
{"I3C_SPD_BMC_MUX3_EN", true}};
const boost::container::flat_map<std::string, bool> cfgCpu1DimmDEF = {
{"I3C_SPD_BMC_MUX2_SEL", true},
{"I3C_SPD_BMC_MUX2_EN", false},
{"I3C_SPD_BMC_MUX3_SEL", true},
{"I3C_SPD_BMC_MUX3_EN", true}};
const boost::container::flat_map<std::string, bool> cfgCpu1DimmGHI = {
{"I3C_SPD_BMC_MUX2_SEL", true},
{"I3C_SPD_BMC_MUX2_EN", true},
{"I3C_SPD_BMC_MUX3_SEL", false},
{"I3C_SPD_BMC_MUX3_EN", false}};
const boost::container::flat_map<std::string, bool> cfgCpu1DimmJKL = {
{"I3C_SPD_BMC_MUX2_SEL", true},
{"I3C_SPD_BMC_MUX2_EN", true},
{"I3C_SPD_BMC_MUX3_SEL", true},
{"I3C_SPD_BMC_MUX3_EN", false}};
static std::vector<boost::container::flat_map<std::string, bool>> cfgGPIO = {
cfgCpu0DimmABC,
cfgCpu0DimmDEF,
cfgCpu0DimmGHI,
cfgCpu0DimmJKL,
cfgCpu1DimmABC,
cfgCpu1DimmDEF,
cfgCpu1DimmGHI,
cfgCpu1DimmJKL
};
static inline std::string to_hex(int num) {
std::stringstream ss;
ss << std::hex << num;
return ss.str();
}
static inline std::string get_current_timestamp() {
auto now = std::chrono::system_clock::now();
std::time_t now_time = std::chrono::system_clock::to_time_t(now);
std::tm tm = *std::localtime(&now_time);
std::stringstream ss;
ss << std::put_time(&tm, "%Y-%m-%d %H:%M:%S"); // 格式化为 "YYYY-MM-DD HH:MM:SS"
return ss.str();
}
static bool getDbusMsgState(sdbusplus::message_t& msg, bool& value)
{
std::string pltStateInterface;
std::string event;
boost::container::flat_map<std::string, std::variant<bool>>
propertiesChanged;
try
{
msg.read(pltStateInterface, propertiesChanged);
if (propertiesChanged.empty())
{
return false;
}
std::string property = propertiesChanged.begin()->first;
if (property.empty() || property != "dimmI3cSwitch")
{
return false;
}
value = std::get<bool>(propertiesChanged.begin()->second);
return true;
}
catch (const std::exception& e)
{
std::cerr << "exception while reading dbus property dimmI3cSwitch" << "\n";
return false;
}
}
static int getADCUpdateTime(int value) {
static const int updateTimes[] = {1, 2, 4, 8};
if (value >= 0 && value < 4) {
return updateTimes[value];
} else {
return 8;
}
}
template <typename T>
void updateField(const std::string& dimmName, FieldType field, T newValues[], size_t size) {
auto it = dimmDataTable.find(dimmName);
if (it != dimmDataTable.end()) {
switch (field) {
case VOLTAGE:
if (size == STATUS_PMIC_RAIL_NUM) {
std::copy(newValues, newValues + size, std::begin(it->second.voltage));
} else {
std::cout << "Invalid size for voltage array!" << std::endl;
}
break;
case CURRENT:
if (size == STATUS_PMIC_RAIL_NUM) {
std::copy(newValues, newValues + size, std::begin(it->second.current));
} else {
std::cout << "Invalid size for current array!" << std::endl;
}
break;
case POWER:
if (size == STATUS_PMIC_RAIL_NUM) {
std::copy(newValues, newValues + size, std::begin(it->second.power));
} else {
std::cout << "Invalid size for power array!" << std::endl;
}
break;
case TEMP:
if (size == STATUS_TEMP_NUM) {
std::copy(newValues, newValues + size, std::begin(it->second.temp));
} else {
std::cout << "Invalid size for temp array!" << std::endl;
}
break;
case STATUS:
if (size == STATUS_REG_NUM) {
std::copy(newValues, newValues + size, std::begin(it->second.reg));
} else {
std::cout << "Invalid size for status array!" << std::endl;
}
break;
default:
std::cout << "Unknown field type!" << std::endl;
break;
}
} else {
std::cout << "DIMM not found!" << std::endl;
}
}
void DIMMSpdReader::getdimmI3cSwitchState(const std::shared_ptr<sdbusplus::asio::connection>& conn,
size_t retries)
{
conn->async_method_call(
[conn, retries, this](boost::system::error_code ec,
const std::variant<bool>& state) {
if (ec)
{
if (retries != 0U)
{
auto timer = std::make_shared<boost::asio::steady_timer>(
conn->get_io_context());
timer->expires_after(std::chrono::seconds(15));
timer->async_wait(
[timer, conn, retries, this](boost::system::error_code) {
this->getdimmI3cSwitchState(conn, retries - 1);
});
return;
}
std::cerr << "error getting dimm I3cSwitchState" << ec.message() << "\n";
return;
}
bool i3cSwitchState = std::get<bool>(state);
std::cout << "i3cSwitch state: " << (int)i3cSwitchState << std::endl;
if(i3cSwitchState)
{
int ret = system("modprobe dw-i3c-master");
if (ret != 0) {
std::cout << "Failed to modprobe dw-i3c-master module" << std::endl;
return;
}
std::this_thread::sleep_for(std::chrono::milliseconds(500));
std::cout << "Start dimm spd reader" << std::endl;
this->startRead();
}
},
HostMiscDbusName, platformStatePath, "org.freedesktop.DBus.Properties", "Get",
platformStateInterface, "dimmI3cSwitch");
}
void DIMMSpdReader::dimmI3cSwitchMatcher(
std::vector<sdbusplus::bus::match_t>& matches, sdbusplus::bus_t& connection,
std::function<void(bool)>&& onMatch)
{
auto pulseEventMatcherCallback =
[onMatch{std::move(onMatch)}, this](sdbusplus::message_t& msg) {
bool value = false;
if (!getDbusMsgState(msg, value))
{
return;
}
onMatch(value);
};
matches.emplace_back(
connection,
"type='signal',interface='org.freedesktop.DBus.Properties',member='"
"PropertiesChanged',arg0='" +
std::string(platformStateInterface) + "'",
std::move(pulseEventMatcherCallback));
}
static bool setGPIOOutput(const std::string& name, const int value,
gpiod::line& gpioLine)
{
// Find the GPIO line
gpioLine = gpiod::find_line(name);
if (!gpioLine)
{
lg2::error("Failed to find the {GPIO_NAME} line", "GPIO_NAME", name);
return false;
}
// Request GPIO output to specified value
try
{
gpioLine.request({"dimm-spd-reader", gpiod::line_request::DIRECTION_OUTPUT, {}},
value);
}
catch (const std::exception& e)
{
lg2::error("Failed to request {GPIO_NAME} output: {ERROR}", "GPIO_NAME",
name, "ERROR", e);
return false;
}
lg2::debug("{GPIO_NAME} set to {GPIO_VALUE}", "GPIO_NAME", name,
"GPIO_VALUE", value);
return true;
}
static void setGPIOToSelectDimm(const boost::container::flat_map<std::string, bool>& cfgMap)
{
for(auto cfg : cfgMap)
{
gpiod::line line;
if (!setGPIOOutput(cfg.first, (int)cfg.second, line))
{
std::cerr << "Setting I3CSwitch GPIO failed \n";
return;
}
}
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
static int i3cWriteRead(std::string& i3cDev, uint8_t wBuf[], uint16_t wLen,
uint8_t rBuf[], uint16_t rLen)
{
struct i3c_ioc_priv_xfer xfers[2];
int nxfers = 0;
if constexpr (debug)
{
std::cout << "i3c data to write:" << std::endl;
for(int i=0; i<wLen; i++)
{
std::cout << "wBuf[" << i << "]: 0x" << std::hex << (int)wBuf[i] << std::dec << " ";
}
std::cout << std::endl;
}
int fd = open(i3cDev.c_str(), O_RDWR);
if (fd < 0)
{
std::cerr << " unable to open i3c device " << i3cDev << "\n";
goto err;
}
memset(xfers, 0, sizeof(xfers));
if (wLen)
{
xfers[nxfers].len = wLen;
xfers[nxfers].rnw = 0;
xfers[nxfers].data = (uintptr_t)wBuf;
nxfers++;
}
if (rLen)
{
xfers[nxfers].len = rLen;
xfers[nxfers].rnw = 1;
xfers[nxfers].data = (uintptr_t)rBuf;
nxfers++;
}
if (ioctl(fd, I3C_IOC_PRIV_XFER(nxfers), xfers) < 0) {
if constexpr (debug)
{
std::cerr << "i3c transfer failed "<< "\n";
}
goto err;
}
for (int i = 0; i < nxfers; i++) {
if (xfers[i].rnw) {
memcpy((void *)rBuf, (void *)(uintptr_t)xfers[i].data, xfers[i].len * sizeof(uint8_t));
}
}
if constexpr (debug)
{
std::cout << "i3c received data:\n";
for (int i = 0; i < (int)rLen; i++)
{
std::cout << "rBuf[" << i << "]: 0x" << std::hex << (int)rBuf[i] << std::dec << " ";
}
std::cout << "\n";
}
close(fd);
return 0;
err:
close(fd);
return -1;
}
void DIMMSpdReader::startRead()
{
waitTimer.expires_after(std::chrono::milliseconds(sensorPollMs));
waitTimer.async_wait([this](const boost::system::error_code& ec) {
if (ec == boost::asio::error::operation_aborted)
{
return; // we're being cancelled
}
// read timer error
if (ec)
{
std::cerr << "timer error\n";
return;
}
readDimmInfo();
updateDbus();
updateToDimmInfoFile();
if(regCounter == 0)
{
regCounter = 6000; //read status registers per 10 minutes.
}
else
{
regCounter--;
}
if(pmicCounter == 0)
{
pmicCounter = 300; //read status registers per 30s.
}
else
{
pmicCounter--;
}
startRead();
});
}
int DIMMSpdReader::getPmicType(uint8_t cpuid, uint8_t rank)
{
int ret = -1;
if(cpuid >= MAX_CPU_ID || rank >= MAX_DIMM_RANK) return ret;
if(gotType[cpuid][rank]) return 0;
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 rdBuf[] = {0};
uint16_t rLen = 1;
int spd = NUM_TO_SPD(rank);
const uint8_t memReg = 0x80;
std::string i3cSpdName = I3C_ADDR(cpuid) + to_hex(spd);
wrBuf[0] = 0xb;
ret = i3cWriteRead(i3cSpdName, wrBuf, wLen, rdBuf, rLen);
if (ret < 0)
{
return -1;
}
size_t size = sizeof(index) / sizeof(index[0]);
for(size_t i = 0; i < size; i++)
{
wrBuf[0] = static_cast<uint8_t>(memReg | (index[i] & 0x40) | (index[i] & 0x3F));
wrBuf[1] = static_cast<uint8_t>((index[i] >> 7) & 0x0F);
ret = i3cWriteRead(i3cSpdName, wrBuf, wLen, rdBuf, rLen);
if (ret < 0)
{
return -1;
}
if((rdBuf[0] & 0xf) == 0x6)
{
pmicType[cpuid][rank] = PMIC5030;
break;
}
}
if(pmicType[cpuid][rank] == PMIC5030)
{
twoByteMode[cpuid][rank] = true;
}
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
// if(R0030 == 0x84)
// 2 byte mode + 1ms delay: R30 success
// Else // Original 2 byte mode
// C. 2B Write R0030 = 0x84
// D. 2B Read R0030
// if(R0030 == 0x84) //check 2 byte mode
// 2 byte mode + 1ms delay: R30 success
if (pmicType[cpuid][rank] == PMIC5030)
{
int pmic = NUM_TO_PMIC(rank);
std::string i3cPmicName = I3C_ADDR(cpuid) + to_hex(pmic);
if constexpr (debug)
{
std::cout << "is PMIC5030"<< std::endl;
}
// 1. 1B Write R30 = 0x84
wLen = 2;
rLen = 0;
wrBuf[0] = 0x30; wrBuf[1] = 0x84;
rdBuf[0] = 0x00;
ret = i3cWriteRead(i3cPmicName, wrBuf, wLen, rdBuf, rLen);
if (ret < 0)
{
return -1;
}
if constexpr (debug)
{
std::cout << "first 1B Write R30 = 0x84" << std::endl;
}
std::this_thread::sleep_for(std::chrono::milliseconds(9));
// 2. 2B Read R0030
wLen = 2;
rLen = 1;
wrBuf[0] = 0x30; wrBuf[1] = 0x00;
rdBuf[0] = 0x00;
ret = i3cWriteRead(i3cPmicName, wrBuf, wLen, rdBuf, rLen);
if (ret < 0)
{
return -1;
}
if constexpr (debug)
{
std::cout << "2B Read R0030 = 0x" << std::hex << (int)rdBuf[0] << std::endl;
}
// Successful switch to 2 Byte mode
if(rdBuf[0] == 0x84)
{
if constexpr (debug)
{
std::cout << "Is 2 Byte mode" << std::endl;
}
}
// It was originally 2 bytes mode
else
{
//C2-2. 2B Write R30 = 0x84
wrBuf[0] = 0x30; wrBuf[1] = 0x00; wrBuf[2] = 0x84;
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;
wLen = 2;
rLen = 1;
ret = i3cWriteRead(i3cPmicName, wrBuf, wLen, rdBuf, rLen);
if constexpr (debug)
{
std::cout << "2B Read R0030 = " << std::hex << (int)rdBuf[0] << std::endl;
}
//Force the use of 1-byte mode
if(rdBuf[0] != 0x84)
{
twoByteMode[cpuid][rank] = false;
std::cout << "Force the use of 1-byte mode" << std::endl;
}
}
}
//Jim debug+E
if constexpr (debug)
{
std::cout << "pmicType[" << (int)cpuid << "][" << (int)rank << "] = "<< (int)pmicType[cpuid][rank] << std::endl;
std::cout << "gotType[" << (int)cpuid << "][" << (int)rank << "] = "<< (int)gotType[cpuid][rank] << std::endl;
}
return ret;
}
int DIMMSpdReader::getDIMMRegsTemp(uint8_t cpuid, uint8_t rank, DimmData& dimmData)
{
int ret = -1;
if(cpuid >= MAX_CPU_ID || rank >= MAX_DIMM_RANK) return ret;
if constexpr (debug)
{
std::cout << "Start to get DIMM temperature "<< std::endl;
}
uint8_t wrBuf[] = {0x31};
uint16_t wLen = 1;
uint8_t rdBuf[] = {0, 0};
uint16_t rLen = 2;
uint16_t integerPart;
uint16_t fractionalPart;
int spd = NUM_TO_SPD(rank);
#if 0
std::string i3cSpdName = I3C_ADDR(cpuid) + to_hex(spd);
ret = i3cWriteRead(i3cSpdName, wrBuf, wLen, rdBuf, rLen);
if (ret < 0)
{
return -1;
}
integerPart = ((rdBuf[0] & 0xf0) >> 4) | ((rdBuf[1] & 0x0f) << 4);
fractionalPart = (rdBuf[0] & 0x0C) >> 2;
// check if a negative reading
if (rdBuf[1] & 0x10)
{
uint16_t tmp = (integerPart << 2) | fractionalPart;
tmp = ((~tmp) & 0x3ff) + 1;
fractionalPart = tmp & 0x3;
integerPart = tmp >> 2;
dimmData.temp[2] = -1.0 * (integerPart + fractionalPart * 0.25);
}
else
{
dimmData.temp[2] = integerPart + fractionalPart * 0.25;
}
#endif
std::string i3cTS0Name = I3C_TS0_ADDR(cpuid) + to_hex(spd);
memset(rdBuf, 0, sizeof(rdBuf));
ret = i3cWriteRead(i3cTS0Name, wrBuf, wLen, rdBuf, rLen);
if (ret < 0)
{
return -1;
}
integerPart = ((rdBuf[0] & 0xf0) >> 4) | ((rdBuf[1] & 0x0f) << 4);
fractionalPart = (rdBuf[0] & 0x0C) >> 2;
// check if a negative reading
if (rdBuf[1] & 0x10)
{
uint16_t tmp = (integerPart << 2) | fractionalPart;
tmp = ((~tmp) & 0x3ff) + 1;
fractionalPart = tmp & 0x3;
integerPart = tmp >> 2;
dimmData.temp[0] = -1.0 * (integerPart + fractionalPart * 0.25);
}
else
{
dimmData.temp[0] = integerPart + fractionalPart * 0.25;
}
std::string i3cTS1Name = I3C_TS1_ADDR(cpuid) + to_hex(spd);
memset(rdBuf, 0, sizeof(rdBuf));
ret = i3cWriteRead(i3cTS1Name, wrBuf, wLen, rdBuf, rLen);
if (ret < 0)
{
return -1;
}
integerPart = ((rdBuf[0] & 0xf0) >> 4) | ((rdBuf[1] & 0x0f) << 4);
fractionalPart = (rdBuf[0] & 0x0C) >> 2;
// check if a negative reading
if (rdBuf[1] & 0x10)
{
uint16_t tmp = (integerPart << 2) | fractionalPart;
tmp = ((~tmp) & 0x3ff) + 1;
fractionalPart = tmp & 0x3;
integerPart = tmp >> 2;
dimmData.temp[1] = -1.0 * (integerPart + fractionalPart * 0.25);
}
else
{
dimmData.temp[1] = integerPart + fractionalPart * 0.25;
}
return 0;
}
int DIMMSpdReader::getDIMMRegsVol(uint8_t cpuid, uint8_t rank, DimmData& dimmData)
{
int ret = -1;
if(cpuid >= MAX_CPU_ID || rank >= MAX_DIMM_RANK) return ret;
if constexpr (debug)
{
std::cout << "Start to get DIMM voltage "<< std::endl;
std::cout << "pmicType[" << (int)cpuid << "][" << (int)rank << "] = "<< (int)pmicType[cpuid][rank] << std::endl;
}
uint16_t wLen;
uint16_t rLen;
uint8_t adcSel[6] = {0x80, 0x88, 0x90, 0x98, 0xa0, 0xd0};
uint8_t adcOut[] = {0x31, 0};
uint8_t rdBuf[] = {0};
uint8_t wrBuf[3] = {0x30, 0, 0};
uint8_t adcValue = 0;
int pmic = NUM_TO_PMIC(rank);
std::string i3cPmicName = I3C_ADDR(cpuid) + to_hex(pmic);
rLen = 1;
wLen = twoByteMode[cpuid][rank] ? 2 : 1;
ret = i3cWriteRead(i3cPmicName, wrBuf, wLen, &adcValue, rLen);
if (ret < 0)
{
return -1;
}
adcValue &= 0x7;
int adcValue10 = (int)(adcValue & 0x3);
adcDelay[cpuid][rank] = getADCUpdateTime(adcValue10);
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.
if(twoByteMode[cpuid][rank])
{
adcValue = 0x4; //Jim debug, PMIC5030 fixed the 2 byte mode + 1ms
wrBuf[2] = adcValue | adcSel[i];
wLen = 3;
}
else
{
wrBuf[1] = adcValue | adcSel[i];
wLen = 2;
}
ret = i3cWriteRead(i3cPmicName, wrBuf, wLen, rdBuf, rLen);
if (ret < 0)
{
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;
rdBuf[0] = 0;
ret = i3cWriteRead(i3cPmicName, adcOut, wLen, rdBuf, rLen);
if (ret < 0)
{
return -1;
}
dimmData.voltage[i] = rdBuf[0] * 15.0 / 1000.0;
}
return 0;
}
static double calStep31CurPower(int index, const uint8_t (&temp)[8])
{
if (index < 0 || index > 5)
return 0.0;
uint8_t reg;
uint8_t shift;
if(index < 4)
{
reg = temp[0];
shift = 6 - index * 2;
}
else
{
reg = temp[1];
shift = 6 - (index - 4) * 2;
}
uint8_t msb = (reg >> shift) & 0x03;
uint8_t lsb = temp[2 + index];
uint16_t raw10 = (static_cast<uint16_t>(msb) << 8) | lsb;
if constexpr (debug)
{
std::cout << "calStep31CurPower raw10: " << (int)raw10 << std::endl;
}
double result = std::round(raw10 * 31.25) / 1000.0;
return result;
}
int DIMMSpdReader::getDIMMRegsCurAndPow(uint8_t cpuid, uint8_t rank, DimmData& dimmData)
{
int ret = -1;
if(cpuid >= MAX_CPU_ID || rank >= MAX_DIMM_RANK) return ret;
if constexpr (debug)
{
std::cout << "Start to get DIMM current and power "<< std::endl;
std::cout << "pmicType[" << (int)cpuid << "][" << (int)rank << "] = "<< (int)pmicType[cpuid][rank] << std::endl;
}
int index = 0;
uint16_t wLen;
uint16_t rLen = 1;
bool accStep31 = false;
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 wrBuf[] = {0, 0};
int pmic = NUM_TO_PMIC(rank);
std::string i3cPmicName = I3C_ADDR(cpuid) + to_hex(pmic);
if(pmicType[cpuid][rank] == PMIC5030)
{
uint8_t adcAcc[] = {0x32, 0};
wLen = twoByteMode[cpuid][rank] ? 2 : 1;
ret = i3cWriteRead(i3cPmicName, adcAcc, wLen, rdBuf, rLen);
if (ret < 0)
{
//std::cerr << " read word data failed. \n";
return -1;
}
if((rdBuf[0] & 0x3) == 1) accStep31 = true;
if constexpr (debug)
{
std::cout << "accStep31: " << (int)accStep31 << std::endl;
}
}
if(accStep31)
{
index = 8;
for(int i=0; i<index; i++)
{
curOut[i] = 0x100 + i;
}
}
else
{
index = 4;
for(int i=0; i<index; i++)
{
curOut[i] = 0xc + i;
}
}
wLen = twoByteMode[cpuid][rank] ? 2 : 1;
ret = i3cWriteRead(i3cPmicName, adcSelect, wLen, rdBuf, rLen);
if (ret < 0)
{
return -1;
}
if(twoByteMode[cpuid][rank])
{
adcSelect[2] = rdBuf[0] & 0xbf; //Read current
wLen = 3;
}
else
{
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.
ret = i3cWriteRead(i3cPmicName, adcSelect, wLen, rdBuf, rLen);
if (ret < 0)
{
return -1;
}
std::this_thread::sleep_for(std::chrono::milliseconds(adcDelay[cpuid][rank]));
if(accStep31)
{
rLen = 8;
memset(rdBuf, 0, sizeof(rdBuf));
if(twoByteMode[cpuid][rank])
{
wrBuf[0] = static_cast<uint8_t>(curOut[0] & 0xFF);
wrBuf[1] = static_cast<uint8_t>((curOut[0] >> 8) & 0xFF);
wLen = 2;
}
else
{
if constexpr (debug)
{
std::cout << "twoByteMode[" << (int)cpuid << "][" << (int)rank << "] = "<< (int)twoByteMode[cpuid][rank] << std::endl;
}
if(pmicType[cpuid][rank] == PMIC5030)
{
return -1;
}
else
{
for(int i=0; i<6; i++)
{
dimmData.current[i] = calStep31CurPower(i, temp);
}
}
}
ret = i3cWriteRead(i3cPmicName, wrBuf, wLen, rdBuf, rLen);
if (ret < 0)
{
return -1;
}
for(int i=0; i<index; i++) {
temp[i] = rdBuf[i];
}
for(int i=0; i<6; i++)
{
dimmData.current[i] = calStep31CurPower(i, temp);
}
}
else
{
for(int i=0; i<index; i++) {
rLen = 1;
rdBuf[0] = 0;
if(twoByteMode[cpuid][rank])
{
wrBuf[0] = static_cast<uint8_t>(curOut[i] & 0xFF);
wrBuf[1] = static_cast<uint8_t>((curOut[i] >> 8) & 0xFF);
wLen = 2;
}
else
{
wrBuf[0] = static_cast<uint8_t>(curOut[i]);
wLen = 1;
}
ret = i3cWriteRead(i3cPmicName, wrBuf, wLen, rdBuf, rLen);
if (ret < 0)
{
return -1;
}
dimmData.current[i] = rdBuf[0] * 125.0 / 1000.0;
}
}
//start to read power
if(twoByteMode[cpuid][rank])
{
adcSelect[2] |= 0x40;
wLen = 3;
}
else
{
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.
ret = i3cWriteRead(i3cPmicName, adcSelect, wLen, rdBuf, rLen);
if (ret < 0)
{
return -1;
}
std::this_thread::sleep_for(std::chrono::milliseconds(adcDelay[cpuid][rank]));
// For Renesas requirements, I3C command: Continuously read R100~R107
if(accStep31)
{
rLen = 8;
memset(rdBuf, 0, sizeof(rdBuf));
if(twoByteMode[cpuid][rank])
{
wrBuf[0] = static_cast<uint8_t>(curOut[0] & 0xFF);
wrBuf[1] = static_cast<uint8_t>((curOut[0] >> 8) & 0xFF);
wLen = 2;
}
else
{
if constexpr (debug)
{
std::cout << "twoByteMode[" << (int)cpuid << "][" << (int)rank << "] = "<< (int)twoByteMode[cpuid][rank] << std::endl;
}
if(pmicType[cpuid][rank] == PMIC5030)
{
return -1;
}
else
{
for(int i=0; i<6; i++)
{
dimmData.current[i] = calStep31CurPower(i, temp);
}
}
}
ret = i3cWriteRead(i3cPmicName, wrBuf, wLen, rdBuf, rLen);
if (ret < 0)
{
return -1;
}
for(int i=0; i<index; i++) {
temp[i] = rdBuf[i];
}
for(int i=0; i<6; i++)
{
dimmData.power[i] = calStep31CurPower(i, temp);
}
}
else
{
for(int i=0; i<index; i++)
{
rLen = 1;
rdBuf[0] = 0;
if(twoByteMode[cpuid][rank])
{
wrBuf[0] = static_cast<uint8_t>(curOut[i] & 0xFF);
wrBuf[1] = static_cast<uint8_t>((curOut[i] >> 8) & 0xFF);
wLen = 2;
}
else
{
wrBuf[0] = static_cast<uint8_t>(curOut[i]);
wLen = 1;
}
ret = i3cWriteRead(i3cPmicName, wrBuf, wLen, rdBuf, rLen);
if (ret < 0)
{
return -1;
}
dimmData.power[i] = rdBuf[0] * 125.0 / 1000.0;
}
}
return 0;
}
int DIMMSpdReader::getPmicStatusRegs(uint8_t cpuid, uint8_t rank, DimmData& dimmData)
{
int ret = -1;
if(cpuid >= MAX_CPU_ID || rank >= MAX_DIMM_RANK) return ret;
uint16_t wLen = 1;
uint16_t rLen = 1;
uint8_t reg[] = {0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb};
uint8_t rdBuf[] = {0};
int pmic = NUM_TO_PMIC(rank);
std::string i3cPmicName = I3C_ADDR(cpuid) + to_hex(pmic);
size_t index = sizeof(reg) / sizeof(reg[0]);
for(size_t i=0; i<index; i++) {
ret = i3cWriteRead(i3cPmicName, &reg[i], wLen, rdBuf, rLen);
if (ret < 0)
{
return -1;
}
dimmData.reg[i] = rdBuf[0];
std::this_thread::sleep_for(std::chrono::milliseconds(2));
}
return 0;
}
void DIMMSpdReader::init()
{
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);
dimmDataTable.insert_or_assign(dimmName, std::move(dData));
pmicType[cpuid][i] = PMIC5010;
gotType[cpuid][i] = false;
twoByteMode[cpuid][i] = false;
}
}
regCounter = 0;
pmicCounter = 0;
updateDbus();
updateToDimmInfoFile();
}
void DIMMSpdReader::readDimmInfo()
{
int ret = -1;
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);
int index = cpuid * 4 + i / 3;
if(index != lastIndex) {
setGPIOToSelectDimm(cfgGPIO[index]);
lastIndex = index;
}
ret = getPmicType(cpuid, i);
if (ret < 0)
{
if constexpr (debug)
{
std::cerr << " get cpu" << cpuid << " rank" << i << " PMIC Type failed. \n";
}
}
ret = getDIMMRegsTemp(cpuid, i, dData);
if (ret < 0)
{
if constexpr (debug)
{
std::cerr << " read cpu" << cpuid << " rank" << i << " dimm temp failed. \n";
}
}
updateField(dimmName, TEMP, dData.temp, STATUS_TEMP_NUM);
#if 1
if(regCounter == 0)
{
ret = getPmicStatusRegs(cpuid, i, dData);
if (ret < 0)
{
if constexpr (debug)
{
std::cerr << " read cpu" << cpuid << " rank" << i << " status registers failed. \n";
}
}
updateField(dimmName, STATUS, dData.reg, STATUS_REG_NUM);
}
if(pmicCounter == 0)
{
ret = getDIMMRegsVol(cpuid, i, dData);
if (ret < 0)
{
if constexpr (debug)
{
std::cerr << " read cpu" << cpuid << " rank" << i << " dimm voltage failed. \n";
}
}
updateField(dimmName, VOLTAGE, dData.voltage, STATUS_PMIC_RAIL_NUM);
ret = getDIMMRegsCurAndPow(cpuid, i, dData);
if (ret < 0)
{
if constexpr (debug)
{
std::cerr << " read cpu" << cpuid << " rank" << i << " dimm current and power failed. \n";
}
}
updateField(dimmName, CURRENT, dData.current, STATUS_PMIC_RAIL_NUM);
updateField(dimmName, POWER, dData.power, STATUS_PMIC_RAIL_NUM);
}
#endif
}
}
return;
}
void DIMMSpdReader::updateDbus()
{
for(auto data : dimmDataTable) {
if(dimm_temp_inf.find(data.first) != dimm_temp_inf.end()) {
dimm_temp_inf[data.first]->set_property("TS0", data.second.temp[0]);
dimm_temp_inf[data.first]->set_property("TS1", data.second.temp[1]);
dimm_temp_inf[data.first]->set_property("SPD_TS", data.second.temp[2]);
}
if(dimm_voltage_inf.find(data.first) != dimm_voltage_inf.end()) {
dimm_voltage_inf[data.first]->set_property("SWA", data.second.voltage[0]);
dimm_voltage_inf[data.first]->set_property("SWB", data.second.voltage[1]);
dimm_voltage_inf[data.first]->set_property("SWC", data.second.voltage[2]);
dimm_voltage_inf[data.first]->set_property("SWD", data.second.voltage[3]);
dimm_voltage_inf[data.first]->set_property("SWE", data.second.voltage[4]);
dimm_voltage_inf[data.first]->set_property("SWF", data.second.voltage[5]);
}
if(dimm_current_inf.find(data.first) != dimm_current_inf.end()) {
dimm_current_inf[data.first]->set_property("SWA", data.second.current[0]);
dimm_current_inf[data.first]->set_property("SWB", data.second.current[1]);
dimm_current_inf[data.first]->set_property("SWC", data.second.current[2]);
dimm_current_inf[data.first]->set_property("SWD", data.second.current[3]);
dimm_current_inf[data.first]->set_property("SWE", data.second.current[4]);
dimm_current_inf[data.first]->set_property("SWF", data.second.current[5]);
}
if(dimm_power_inf.find(data.first) != dimm_power_inf.end()) {
dimm_power_inf[data.first]->set_property("SWA", data.second.power[0]);
dimm_power_inf[data.first]->set_property("SWB", data.second.power[1]);
dimm_power_inf[data.first]->set_property("SWC", data.second.power[2]);
dimm_power_inf[data.first]->set_property("SWD", data.second.power[3]);
dimm_power_inf[data.first]->set_property("SWE", data.second.power[4]);
dimm_power_inf[data.first]->set_property("SWF", data.second.power[5]);
}
if(dimm_statusReg_inf.find(data.first) != dimm_statusReg_inf.end()) {
dimm_statusReg_inf[data.first]->set_property("0x04", data.second.reg[0]);
dimm_statusReg_inf[data.first]->set_property("0x05", data.second.reg[1]);
dimm_statusReg_inf[data.first]->set_property("0x06", data.second.reg[2]);
dimm_statusReg_inf[data.first]->set_property("0x07", data.second.reg[3]);
dimm_statusReg_inf[data.first]->set_property("0x08", data.second.reg[4]);
dimm_statusReg_inf[data.first]->set_property("0x09", data.second.reg[5]);
dimm_statusReg_inf[data.first]->set_property("0x0a", data.second.reg[6]);
dimm_statusReg_inf[data.first]->set_property("0x0b", data.second.reg[7]);
}
}
}
void DIMMSpdReader::updateToDimmInfoFile()
{
const std::filesystem::path outputPath(dimmInfoPath);
std::ofstream jsonOutputFile(outputPath);
if (!jsonOutputFile.is_open()) {
std::cout << "Failed to open dimmInfo json file" << std::endl;
return;
}
nlohmann::json js;
std::string timestamp = get_current_timestamp();
js["Current Time"] = timestamp;
//std::cout << "timestamp: " << timestamp << std::endl;
for(auto data : dimmDataTable) {
//update temperature
js[data.first]["Temperature_DegreesC"]["TS0"] = data.second.temp[0];
js[data.first]["Temperature_DegreesC"]["TS1"] = data.second.temp[1];
js[data.first]["Temperature_DegreesC"]["SPD_TS"] = data.second.temp[2];
//update voltage
js[data.first]["Voltage_Volt"]["SWA"] = data.second.voltage[0];
js[data.first]["Voltage_Volt"]["SWB"] = data.second.voltage[1];
js[data.first]["Voltage_Volt"]["SWC"] = data.second.voltage[2];
js[data.first]["Voltage_Volt"]["SWD"] = data.second.voltage[3];
js[data.first]["Voltage_Volt"]["SWE"] = data.second.voltage[4];
js[data.first]["Voltage_Volt"]["SWF"] = data.second.voltage[5];
//update current
js[data.first]["Current_Ampere"]["SWA"] = data.second.current[0];
js[data.first]["Current_Ampere"]["SWB"] = data.second.current[1];
js[data.first]["Current_Ampere"]["SWC"] = data.second.current[2];
js[data.first]["Current_Ampere"]["SWD"] = data.second.current[3];
js[data.first]["Current_Ampere"]["SWE"] = data.second.current[4];
js[data.first]["Current_Ampere"]["SWF"] = data.second.current[5];
//update power
js[data.first]["Power_Watt"]["SWA"] = data.second.power[0];
js[data.first]["Power_Watt"]["SWB"] = data.second.power[1];
js[data.first]["Power_Watt"]["SWC"] = data.second.power[2];
js[data.first]["Power_Watt"]["SWD"] = data.second.power[3];
js[data.first]["Power_Watt"]["SWE"] = data.second.power[4];
js[data.first]["Power_Watt"]["SWF"] = data.second.power[5];
//update status registers
js[data.first]["Status_Reg"]["0x04"] = data.second.reg[0];
js[data.first]["Status_Reg"]["0x05"] = data.second.reg[1];
js[data.first]["Status_Reg"]["0x06"] = data.second.reg[2];
js[data.first]["Status_Reg"]["0x07"] = data.second.reg[3];
js[data.first]["Status_Reg"]["0x08"] = data.second.reg[4];
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];
}
jsonOutputFile << js.dump(4);
return;
}
void DIMMSpdReader::setupMatches(sdbusplus::bus_t& connection)
{
std::weak_ptr<DIMMSpdReader> weakRef = weak_from_this();
std::shared_ptr<DIMMSpdReader> sharedRef = weakRef.lock();
dimmI3cSwitchMatcher(matches, connection, [sharedRef](bool state)
{
if (!sharedRef)
{
return;
}
if (!state)
{
//switch dimm i3c to bios
sharedRef->waitTimer.cancel();
sharedRef->init();
std::cout << "waitTimer canceled" << std::endl;
std::this_thread::sleep_for(std::chrono::milliseconds(500));
int ret = system("rmmod dw-i3c-master");
if (ret != 0) {
std::cout << "Failed to rmmod dw-i3c-master module" << std::endl;
}
std::this_thread::sleep_for(std::chrono::milliseconds(200));
}
else
{
int ret = system("modprobe dw-i3c-master");
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;
sharedRef->startRead();
}
});
}