/* // 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include 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 dimmDataTable; const boost::container::flat_map 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 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 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 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 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 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 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 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> 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> 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(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 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& conn, size_t retries) { conn->async_method_call( [conn, retries, this](boost::system::error_code ec, const std::variant& state) { if (ec) { if (retries != 0U) { auto timer = std::make_shared( 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(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& matches, sdbusplus::bus_t& connection, std::function&& 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& 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= 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(memReg | (index[i] & 0x40) | (index[i] & 0x3F)); wrBuf[1] = static_cast((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 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(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(curOut[0] & 0xFF); wrBuf[1] = static_cast((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(curOut[i] & 0xFF); wrBuf[1] = static_cast((curOut[i] >> 8) & 0xFF); wLen = 2; } else { wrBuf[0] = static_cast(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(curOut[0] & 0xFF); wrBuf[1] = static_cast((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(curOut[i] & 0xFF); wrBuf[1] = static_cast((curOut[i] >> 8) & 0xFF); wLen = 2; } else { wrBuf[0] = static_cast(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::quiet_NaN(), std::numeric_limits::quiet_NaN(), std::numeric_limits::quiet_NaN(), std::numeric_limits::quiet_NaN(), std::numeric_limits::quiet_NaN(), std::numeric_limits::quiet_NaN()}, {std::numeric_limits::quiet_NaN(), std::numeric_limits::quiet_NaN(), std::numeric_limits::quiet_NaN(), std::numeric_limits::quiet_NaN(), std::numeric_limits::quiet_NaN(), std::numeric_limits::quiet_NaN()}, {std::numeric_limits::quiet_NaN(), std::numeric_limits::quiet_NaN(), std::numeric_limits::quiet_NaN(), std::numeric_limits::quiet_NaN(), std::numeric_limits::quiet_NaN(), std::numeric_limits::quiet_NaN()}, {std::numeric_limits::quiet_NaN(), std::numeric_limits::quiet_NaN(), std::numeric_limits::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::quiet_NaN(), std::numeric_limits::quiet_NaN(), std::numeric_limits::quiet_NaN(), std::numeric_limits::quiet_NaN(), std::numeric_limits::quiet_NaN(), std::numeric_limits::quiet_NaN()}, {std::numeric_limits::quiet_NaN(), std::numeric_limits::quiet_NaN(), std::numeric_limits::quiet_NaN(), std::numeric_limits::quiet_NaN(), std::numeric_limits::quiet_NaN(), std::numeric_limits::quiet_NaN()}, {std::numeric_limits::quiet_NaN(), std::numeric_limits::quiet_NaN(), std::numeric_limits::quiet_NaN(), std::numeric_limits::quiet_NaN(), std::numeric_limits::quiet_NaN(), std::numeric_limits::quiet_NaN()}, {std::numeric_limits::quiet_NaN(), std::numeric_limits::quiet_NaN(), std::numeric_limits::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 weakRef = weak_from_this(); std::shared_ptr 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(); } }); }