0cc0c96893
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.
1521 lines
46 KiB
C++
Executable File
1521 lines
46 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;
|
|
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;
|
|
}
|
|
} 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;
|
|
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;
|
|
|
|
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;
|
|
}
|
|
|
|
//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)
|
|
{
|
|
twoByteMode[cpuid][rank] = true;
|
|
}
|
|
|
|
gotType[cpuid][rank] = true;
|
|
|
|
//----------------------------------------------------------------------------
|
|
//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;
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
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; //For Renesas requirements, I3C write commands cannot be directly read; reading requires a separate I3C command.
|
|
if(twoByteMode[cpuid][rank])
|
|
{
|
|
adcValue = 0x4; //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;
|
|
}
|
|
|
|
// 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]));
|
|
|
|
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::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)
|
|
{
|
|
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}; //For Renesas requirements, I3C command: Continuously read R100~R107
|
|
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;
|
|
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;
|
|
}
|
|
}
|
|
|
|
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; //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;
|
|
}
|
|
}
|
|
|
|
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; //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;
|
|
}
|
|
}
|
|
|
|
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, ®[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},
|
|
{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] = Unknown;
|
|
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++) {
|
|
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) {
|
|
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);
|
|
updateField(dimmName, ERROR, dData.error, STATUS_ERROR_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]);
|
|
}
|
|
|
|
if(dimm_errCode_inf.find(data.first) != dimm_errCode_inf.end()) {
|
|
dimm_errCode_inf[data.first]->set_property("0x32", data.second.error[0]);
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
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];
|
|
|
|
//update error code
|
|
js[data.first]["Error_Code"]["0x32"] = data.second.error[0];
|
|
}
|
|
|
|
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();
|
|
}
|
|
});
|
|
}
|