Clean up SPDAccessor, split out classes into their own files, add comments, update coding style
This commit is contained in:
parent
38732a8fb4
commit
560ff5ab33
19 changed files with 1028 additions and 752 deletions
11
OpenRGB.pro
11
OpenRGB.pro
|
|
@ -123,6 +123,7 @@ INCLUDEPATH +=
|
||||||
KeyboardLayoutManager/ \
|
KeyboardLayoutManager/ \
|
||||||
RGBController/ \
|
RGBController/ \
|
||||||
qt/ \
|
qt/ \
|
||||||
|
SPDAccessor/ \
|
||||||
SuspendResume/
|
SuspendResume/
|
||||||
|
|
||||||
HEADERS += \
|
HEADERS += \
|
||||||
|
|
@ -208,7 +209,11 @@ SOURCES +=
|
||||||
PluginManager.cpp \
|
PluginManager.cpp \
|
||||||
ProfileManager.cpp \
|
ProfileManager.cpp \
|
||||||
ResourceManager.cpp \
|
ResourceManager.cpp \
|
||||||
SPDAccessor.cpp \
|
SPDAccessor/DDR4DirectAccessor.cpp \
|
||||||
|
SPDAccessor/DDR5DirectAccessor.cpp \
|
||||||
|
SPDAccessor/SPDAccessor.cpp \
|
||||||
|
SPDAccessor/SPDDetector.cpp \
|
||||||
|
SPDAccessor/SPDWrapper.cpp \
|
||||||
SettingsManager.cpp \
|
SettingsManager.cpp \
|
||||||
i2c_smbus/i2c_smbus.cpp \
|
i2c_smbus/i2c_smbus.cpp \
|
||||||
i2c_tools/i2c_tools.cpp \
|
i2c_tools/i2c_tools.cpp \
|
||||||
|
|
@ -504,6 +509,8 @@ contains(QMAKE_PLATFORM, linux) {
|
||||||
dependencies/NVFC/nvapi.h \
|
dependencies/NVFC/nvapi.h \
|
||||||
i2c_smbus/i2c_smbus_linux.h \
|
i2c_smbus/i2c_smbus_linux.h \
|
||||||
AutoStart/AutoStart-Linux.h \
|
AutoStart/AutoStart-Linux.h \
|
||||||
|
SPDAccessor/EE1004Accessor_Linux.h \
|
||||||
|
SPDAccessor/SPD5118Accessor_Linux.h \
|
||||||
SuspendResume/SuspendResume_Linux_FreeBSD.h \
|
SuspendResume/SuspendResume_Linux_FreeBSD.h \
|
||||||
|
|
||||||
INCLUDEPATH += \
|
INCLUDEPATH += \
|
||||||
|
|
@ -558,6 +565,8 @@ contains(QMAKE_PLATFORM, linux) {
|
||||||
scsiapi/scsiapi_linux.c \
|
scsiapi/scsiapi_linux.c \
|
||||||
serial_port/find_usb_serial_port_linux.cpp \
|
serial_port/find_usb_serial_port_linux.cpp \
|
||||||
AutoStart/AutoStart-Linux.cpp \
|
AutoStart/AutoStart-Linux.cpp \
|
||||||
|
SPDAccessor/EE1004Accessor_Linux.cpp \
|
||||||
|
SPDAccessor/SPD5118Accessor_Linux.cpp \
|
||||||
SuspendResume/SuspendResume_Linux_FreeBSD.cpp \
|
SuspendResume/SuspendResume_Linux_FreeBSD.cpp \
|
||||||
|
|
||||||
#-------------------------------------------------------------------------------------------#
|
#-------------------------------------------------------------------------------------------#
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@
|
||||||
#include <thread>
|
#include <thread>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include "SPDAccessor.h"
|
#include "SPDWrapper.h"
|
||||||
#include "hidapi_wrapper.h"
|
#include "hidapi_wrapper.h"
|
||||||
#include "i2c_smbus.h"
|
#include "i2c_smbus.h"
|
||||||
#include "filesystem.h"
|
#include "filesystem.h"
|
||||||
|
|
|
||||||
527
SPDAccessor.cpp
527
SPDAccessor.cpp
|
|
@ -1,527 +0,0 @@
|
||||||
/*---------------------------------------------------------*\
|
|
||||||
| SPDAccessor.cpp |
|
|
||||||
| |
|
|
||||||
| Access to SPD information on various DIMMs |
|
|
||||||
| |
|
|
||||||
| Milan Cermak (krysmanta) 09 Nov 2024 |
|
|
||||||
| |
|
|
||||||
| This file is part of the OpenRGB project |
|
|
||||||
| SPDX-License-Identifier: GPL-2.0-only |
|
|
||||||
\*---------------------------------------------------------*/
|
|
||||||
|
|
||||||
#include "LogManager.h"
|
|
||||||
#include "SPDAccessor.h"
|
|
||||||
#include "filesystem.h"
|
|
||||||
|
|
||||||
using namespace std::chrono_literals;
|
|
||||||
|
|
||||||
const char *spd_memory_type_name[] =
|
|
||||||
{
|
|
||||||
"Reserved",
|
|
||||||
"FPM",
|
|
||||||
"EDO",
|
|
||||||
"Nibble",
|
|
||||||
"SDR",
|
|
||||||
"Multiplex ROM",
|
|
||||||
"DDR",
|
|
||||||
"DDR",
|
|
||||||
"DDR2",
|
|
||||||
"FB",
|
|
||||||
"FB Probe",
|
|
||||||
"DDR3",
|
|
||||||
"DDR4",
|
|
||||||
"Reserved",
|
|
||||||
"DDR4e",
|
|
||||||
"LPDDR3",
|
|
||||||
"LPDDR4",
|
|
||||||
"LPDDR4X",
|
|
||||||
"DDR5",
|
|
||||||
"LPDDR5"
|
|
||||||
};
|
|
||||||
|
|
||||||
SPDDetector::SPDDetector(i2c_smbus_interface *bus, uint8_t address, SPDMemoryType mem_type = SPD_RESERVED)
|
|
||||||
: bus(bus), address(address), mem_type(mem_type), valid(false)
|
|
||||||
{
|
|
||||||
detect_memory_type();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool SPDDetector::is_valid() const
|
|
||||||
{
|
|
||||||
return valid;
|
|
||||||
}
|
|
||||||
|
|
||||||
SPDMemoryType SPDDetector::memory_type() const
|
|
||||||
{
|
|
||||||
return mem_type;
|
|
||||||
}
|
|
||||||
|
|
||||||
void SPDDetector::detect_memory_type()
|
|
||||||
{
|
|
||||||
SPDAccessor *accessor;
|
|
||||||
|
|
||||||
LOG_DEBUG("Probing DRAM on address 0x%02x", address);
|
|
||||||
|
|
||||||
#ifdef __linux__
|
|
||||||
if(EE1004Accessor::isAvailable(bus, address))
|
|
||||||
{
|
|
||||||
accessor = new EE1004Accessor(bus, address);
|
|
||||||
}
|
|
||||||
else if(SPD5118Accessor::isAvailable(bus, address))
|
|
||||||
{
|
|
||||||
accessor = new SPD5118Accessor(bus, address);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
#endif
|
|
||||||
if((mem_type == SPD_RESERVED || mem_type == SPD_DDR4_SDRAM || mem_type == SPD_DDR4E_SDRAM ||
|
|
||||||
mem_type == SPD_LPDDR4_SDRAM || mem_type == SPD_LPDDR4X_SDRAM) &&
|
|
||||||
DDR4DirectAccessor::isAvailable(bus, address))
|
|
||||||
{
|
|
||||||
accessor = new DDR4DirectAccessor(bus, address);
|
|
||||||
}
|
|
||||||
else if((mem_type == SPD_RESERVED || mem_type == SPD_DDR5_SDRAM || mem_type == SPD_LPDDR5_SDRAM) &&
|
|
||||||
DDR5DirectAccessor::isAvailable(bus, address))
|
|
||||||
{
|
|
||||||
accessor = new DDR5DirectAccessor(bus, address);
|
|
||||||
}
|
|
||||||
else if(mem_type == SPD_RESERVED)
|
|
||||||
{
|
|
||||||
// Probe the SPD directly - probably an older system than DDR4
|
|
||||||
LOG_TRACE("Probing memory type older than DDR4");
|
|
||||||
int value = bus->i2c_smbus_read_byte_data(address, 0x02);
|
|
||||||
if(value < 0)
|
|
||||||
{
|
|
||||||
valid = false;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
mem_type = (SPDMemoryType) value;
|
|
||||||
// We are only interested in DDR4 and DDR5 systems
|
|
||||||
valid = (mem_type == SPD_DDR4_SDRAM || mem_type == SPD_DDR4E_SDRAM ||
|
|
||||||
mem_type == SPD_LPDDR4_SDRAM || mem_type == SPD_LPDDR4X_SDRAM ||
|
|
||||||
mem_type == SPD_DDR5_SDRAM || mem_type == SPD_LPDDR5_SDRAM);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
valid = false;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
valid = true;
|
|
||||||
mem_type = accessor->memory_type();
|
|
||||||
delete accessor;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t SPDDetector::spd_address() const
|
|
||||||
{
|
|
||||||
return this->address;
|
|
||||||
}
|
|
||||||
|
|
||||||
i2c_smbus_interface *SPDDetector::smbus() const
|
|
||||||
{
|
|
||||||
return this->bus;
|
|
||||||
}
|
|
||||||
|
|
||||||
SPDWrapper::SPDWrapper(const SPDWrapper &wrapper)
|
|
||||||
{
|
|
||||||
if(wrapper.accessor != nullptr)
|
|
||||||
{
|
|
||||||
this->accessor = wrapper.accessor->copy();
|
|
||||||
}
|
|
||||||
this->address = wrapper.address;
|
|
||||||
this->mem_type = wrapper.mem_type;
|
|
||||||
}
|
|
||||||
|
|
||||||
SPDWrapper::SPDWrapper(const SPDDetector &detector)
|
|
||||||
{
|
|
||||||
this->address = detector.spd_address();
|
|
||||||
this->mem_type = detector.memory_type();
|
|
||||||
|
|
||||||
// Allocate a new accessor
|
|
||||||
this->accessor = SPDAccessor::for_memory_type(this->mem_type, detector.smbus(), this->address);
|
|
||||||
}
|
|
||||||
|
|
||||||
SPDWrapper::~SPDWrapper()
|
|
||||||
{
|
|
||||||
delete accessor;
|
|
||||||
}
|
|
||||||
|
|
||||||
SPDMemoryType SPDWrapper::memory_type()
|
|
||||||
{
|
|
||||||
return mem_type;
|
|
||||||
}
|
|
||||||
|
|
||||||
int SPDWrapper::index()
|
|
||||||
{
|
|
||||||
return this->address - 0x50;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint16_t SPDWrapper::jedec_id()
|
|
||||||
{
|
|
||||||
if(accessor == nullptr)
|
|
||||||
{
|
|
||||||
return 0x0000;
|
|
||||||
}
|
|
||||||
return accessor->jedec_id();
|
|
||||||
}
|
|
||||||
|
|
||||||
/*-------------------------------------------------------------------------*\
|
|
||||||
| Helper functions for easier collection handling. |
|
|
||||||
\*-------------------------------------------------------------------------*/
|
|
||||||
|
|
||||||
bool is_jedec_in_slots(std::vector<SPDWrapper> &slots, uint16_t jedec_id)
|
|
||||||
{
|
|
||||||
for(SPDWrapper &slot : slots)
|
|
||||||
{
|
|
||||||
if(slot.jedec_id() == jedec_id)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<SPDWrapper*> slots_with_jedec(std::vector<SPDWrapper> &slots, uint16_t jedec_id)
|
|
||||||
{
|
|
||||||
std::vector<SPDWrapper*> matching_slots;
|
|
||||||
|
|
||||||
for(SPDWrapper &slot : slots)
|
|
||||||
{
|
|
||||||
if(slot.jedec_id() == jedec_id)
|
|
||||||
{
|
|
||||||
matching_slots.push_back(&slot);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return matching_slots;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*-------------------------------------------------------------------------*\
|
|
||||||
| Internal implementation for specific memory type. |
|
|
||||||
\*-------------------------------------------------------------------------*/
|
|
||||||
|
|
||||||
SPDAccessor::SPDAccessor(i2c_smbus_interface *bus, uint8_t spd_addr)
|
|
||||||
{
|
|
||||||
this->bus = bus;
|
|
||||||
this->address = spd_addr;
|
|
||||||
}
|
|
||||||
|
|
||||||
SPDAccessor::~SPDAccessor()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
SPDAccessor *SPDAccessor::for_memory_type(SPDMemoryType type, i2c_smbus_interface *bus, uint8_t spd_addr)
|
|
||||||
{
|
|
||||||
if(type == SPD_DDR4_SDRAM)
|
|
||||||
{
|
|
||||||
#ifdef __linux__
|
|
||||||
if(EE1004Accessor::isAvailable(bus, spd_addr))
|
|
||||||
{
|
|
||||||
return new EE1004Accessor(bus, spd_addr);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
return new DDR4DirectAccessor(bus, spd_addr);
|
|
||||||
}
|
|
||||||
if(type == SPD_DDR5_SDRAM)
|
|
||||||
{
|
|
||||||
#ifdef __linux__
|
|
||||||
if(SPD5118Accessor::isAvailable(bus, spd_addr))
|
|
||||||
{
|
|
||||||
return new SPD5118Accessor(bus, spd_addr);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
return new DDR5DirectAccessor(bus, spd_addr);
|
|
||||||
}
|
|
||||||
|
|
||||||
return nullptr;
|
|
||||||
};
|
|
||||||
|
|
||||||
DDR4Accessor::DDR4Accessor(i2c_smbus_interface *bus, uint8_t spd_addr)
|
|
||||||
: SPDAccessor(bus, spd_addr)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
DDR4Accessor::~DDR4Accessor()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
SPDMemoryType DDR4Accessor::memory_type()
|
|
||||||
{
|
|
||||||
return (SPDMemoryType)(this->at(0x02));
|
|
||||||
}
|
|
||||||
|
|
||||||
uint16_t DDR4Accessor::jedec_id()
|
|
||||||
{
|
|
||||||
return (this->at(0x140) << 8) + (this->at(0x141) & 0x7f) - 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
DDR5Accessor::DDR5Accessor(i2c_smbus_interface *bus, uint8_t spd_addr)
|
|
||||||
: SPDAccessor(bus, spd_addr)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
DDR5Accessor::~DDR5Accessor()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
SPDMemoryType DDR5Accessor::memory_type()
|
|
||||||
{
|
|
||||||
return (SPDMemoryType)(this->at(0x02));
|
|
||||||
}
|
|
||||||
|
|
||||||
uint16_t DDR5Accessor::jedec_id()
|
|
||||||
{
|
|
||||||
return (this->at(0x200) << 8) + (this->at(0x201) & 0x7f) - 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
DDR4DirectAccessor::DDR4DirectAccessor(i2c_smbus_interface *bus, uint8_t spd_addr)
|
|
||||||
: DDR4Accessor(bus, spd_addr)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
DDR4DirectAccessor::~DDR4DirectAccessor()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
bool DDR4DirectAccessor::isAvailable(i2c_smbus_interface *bus, uint8_t spd_addr)
|
|
||||||
{
|
|
||||||
int value = bus->i2c_smbus_write_quick(0x36, 0x00);
|
|
||||||
if(value < 0)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Do page switch
|
|
||||||
bus->i2c_smbus_write_byte_data(0x36, 0x00, 0xFF);
|
|
||||||
std::this_thread::sleep_for(SPD_IO_DELAY);
|
|
||||||
|
|
||||||
value = bus->i2c_smbus_read_byte_data(spd_addr, 0x00);
|
|
||||||
return (value == 0x23);
|
|
||||||
}
|
|
||||||
|
|
||||||
SPDAccessor *DDR4DirectAccessor::copy()
|
|
||||||
{
|
|
||||||
return new DDR4DirectAccessor(bus, address);
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t DDR4DirectAccessor::at(uint16_t addr)
|
|
||||||
{
|
|
||||||
if(addr >= SPD_DDR4_EEPROM_LENGTH)
|
|
||||||
{
|
|
||||||
//throw OutOfBoundsError(addr);
|
|
||||||
return 0xFF;
|
|
||||||
}
|
|
||||||
set_page(addr >> SPD_DDR4_EEPROM_PAGE_SHIFT);
|
|
||||||
uint8_t offset = (uint8_t)(addr & SPD_DDR4_EEPROM_PAGE_MASK);
|
|
||||||
uint32_t value = bus->i2c_smbus_read_byte_data(address, offset);
|
|
||||||
std::this_thread::sleep_for(SPD_IO_DELAY);
|
|
||||||
return (uint8_t)value;
|
|
||||||
}
|
|
||||||
|
|
||||||
void DDR4DirectAccessor::set_page(uint8_t page)
|
|
||||||
{
|
|
||||||
if(current_page != page)
|
|
||||||
{
|
|
||||||
bus->i2c_smbus_write_byte_data(0x36 + page, 0x00, 0xFF);
|
|
||||||
current_page = page;
|
|
||||||
std::this_thread::sleep_for(SPD_IO_DELAY);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef __linux__
|
|
||||||
const char *EE1004Accessor::SPD_EE1004_PATH = "/sys/bus/i2c/drivers/ee1004/%u-%04x/eeprom";
|
|
||||||
|
|
||||||
EE1004Accessor::EE1004Accessor(i2c_smbus_interface *bus, uint8_t spd_addr)
|
|
||||||
: DDR4Accessor(bus, spd_addr), valid(false)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
EE1004Accessor::~EE1004Accessor()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
bool EE1004Accessor::isAvailable(i2c_smbus_interface *bus, uint8_t spd_addr)
|
|
||||||
{
|
|
||||||
int size = snprintf(nullptr, 0, SPD_EE1004_PATH, bus->port_id, spd_addr);
|
|
||||||
char *path = new char[size+1];
|
|
||||||
snprintf(path, size+1, SPD_EE1004_PATH, bus->port_id, spd_addr);
|
|
||||||
bool result = std::filesystem::exists(path);
|
|
||||||
delete[] path;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
SPDAccessor *EE1004Accessor::copy()
|
|
||||||
{
|
|
||||||
EE1004Accessor *access = new EE1004Accessor(bus, address);
|
|
||||||
memcpy(access->dump, this->dump, sizeof(this->dump));
|
|
||||||
access->valid = this->valid;
|
|
||||||
return access;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t EE1004Accessor::at(uint16_t addr)
|
|
||||||
{
|
|
||||||
if(!valid)
|
|
||||||
{
|
|
||||||
readEeprom();
|
|
||||||
}
|
|
||||||
return dump[addr];
|
|
||||||
}
|
|
||||||
|
|
||||||
void EE1004Accessor::readEeprom()
|
|
||||||
{
|
|
||||||
int size = snprintf(nullptr, 0, SPD_EE1004_PATH, bus->port_id, address);
|
|
||||||
char *filename = new char[size+1];
|
|
||||||
snprintf(filename, size+1, SPD_EE1004_PATH, bus->port_id, address);
|
|
||||||
|
|
||||||
std::ifstream eeprom_file(filename, std::ios::in | std::ios::binary);
|
|
||||||
if(eeprom_file)
|
|
||||||
{
|
|
||||||
eeprom_file.read((char*)dump, sizeof(dump));
|
|
||||||
eeprom_file.close();
|
|
||||||
}
|
|
||||||
delete[] filename;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
DDR5DirectAccessor::DDR5DirectAccessor(i2c_smbus_interface *bus, uint8_t spd_addr)
|
|
||||||
: DDR5Accessor(bus, spd_addr)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
DDR5DirectAccessor::~DDR5DirectAccessor()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
bool DDR5DirectAccessor::isAvailable(i2c_smbus_interface *bus, uint8_t spd_addr)
|
|
||||||
{
|
|
||||||
bool retry = true;
|
|
||||||
|
|
||||||
while(true)
|
|
||||||
{
|
|
||||||
std::this_thread::sleep_for(SPD_IO_DELAY);
|
|
||||||
int ddr5Magic = bus->i2c_smbus_read_byte_data(spd_addr, 0x00);
|
|
||||||
std::this_thread::sleep_for(SPD_IO_DELAY);
|
|
||||||
int ddr5Sensor = bus->i2c_smbus_read_byte_data(spd_addr, 0x01);
|
|
||||||
std::this_thread::sleep_for(SPD_IO_DELAY);
|
|
||||||
|
|
||||||
if(ddr5Magic < 0 || ddr5Sensor < 0)
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
LOG_TRACE("SPD Hub Magic: 0x%02x 0x%02x", ddr5Magic, ddr5Sensor);
|
|
||||||
|
|
||||||
if(ddr5Magic == 0x51 && (ddr5Sensor & 0xEF) == 0x08)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
int page = bus->i2c_smbus_read_byte_data(spd_addr, SPD_DDR5_MREG_VIRTUAL_PAGE);
|
|
||||||
std::this_thread::sleep_for(SPD_IO_DELAY);
|
|
||||||
|
|
||||||
LOG_TRACE("SPD Page: 0x%02x", page);
|
|
||||||
if(page < 0)
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
else if(retry && page > 0 && page < (SPD_DDR5_EEPROM_LENGTH >> SPD_DDR5_EEPROM_PAGE_SHIFT))
|
|
||||||
{
|
|
||||||
// This still might be a DDR5 module, just the page is off
|
|
||||||
bus->i2c_smbus_write_byte_data(spd_addr, SPD_DDR5_MREG_VIRTUAL_PAGE, 0);
|
|
||||||
std::this_thread::sleep_for(SPD_IO_DELAY);
|
|
||||||
retry = false;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
SPDAccessor *DDR5DirectAccessor::copy()
|
|
||||||
{
|
|
||||||
DDR5DirectAccessor *access = new DDR5DirectAccessor(bus, address);
|
|
||||||
access->current_page = this->current_page;
|
|
||||||
return access;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t DDR5DirectAccessor::at(uint16_t addr)
|
|
||||||
{
|
|
||||||
if(addr >= SPD_DDR5_EEPROM_LENGTH)
|
|
||||||
{
|
|
||||||
//throw OutOfBoundsError(addr);
|
|
||||||
return 0xFF;
|
|
||||||
}
|
|
||||||
set_page(addr >> SPD_DDR5_EEPROM_PAGE_SHIFT);
|
|
||||||
uint8_t offset = (uint8_t)(addr & SPD_DDR5_EEPROM_PAGE_MASK) | 0x80;
|
|
||||||
uint32_t value = bus->i2c_smbus_read_byte_data(address, offset);
|
|
||||||
std::this_thread::sleep_for(SPD_IO_DELAY);
|
|
||||||
return (uint8_t)value;
|
|
||||||
}
|
|
||||||
|
|
||||||
void DDR5DirectAccessor::set_page(uint8_t page)
|
|
||||||
{
|
|
||||||
if(current_page != page)
|
|
||||||
{
|
|
||||||
bus->i2c_smbus_write_byte_data(address, SPD_DDR5_MREG_VIRTUAL_PAGE, page);
|
|
||||||
current_page = page;
|
|
||||||
std::this_thread::sleep_for(SPD_IO_DELAY);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef __linux__
|
|
||||||
const char *SPD5118Accessor::SPD_SPD5118_PATH = "/sys/bus/i2c/drivers/spd5118/%u-%04x/eeprom";
|
|
||||||
|
|
||||||
SPD5118Accessor::SPD5118Accessor(i2c_smbus_interface *bus, uint8_t spd_addr)
|
|
||||||
: DDR5Accessor(bus, spd_addr), valid(false)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
SPD5118Accessor::~SPD5118Accessor()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
bool SPD5118Accessor::isAvailable(i2c_smbus_interface *bus, uint8_t spd_addr)
|
|
||||||
{
|
|
||||||
int size = snprintf(nullptr, 0, SPD_SPD5118_PATH, bus->port_id, spd_addr);
|
|
||||||
char *path = new char[size+1];
|
|
||||||
snprintf(path, size+1, SPD_SPD5118_PATH, bus->port_id, spd_addr);
|
|
||||||
bool result = std::filesystem::exists(path);
|
|
||||||
delete[] path;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
SPDAccessor *SPD5118Accessor::copy()
|
|
||||||
{
|
|
||||||
SPD5118Accessor *access = new SPD5118Accessor(bus, address);
|
|
||||||
memcpy(access->dump, this->dump, sizeof(this->dump));
|
|
||||||
access->valid = this->valid;
|
|
||||||
return access;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t SPD5118Accessor::at(uint16_t addr)
|
|
||||||
{
|
|
||||||
if(!valid)
|
|
||||||
{
|
|
||||||
readEeprom();
|
|
||||||
}
|
|
||||||
return dump[addr];
|
|
||||||
}
|
|
||||||
|
|
||||||
void SPD5118Accessor::readEeprom()
|
|
||||||
{
|
|
||||||
int size = snprintf(nullptr, 0, SPD_SPD5118_PATH, bus->port_id, address);
|
|
||||||
char *filename = new char[size+1];
|
|
||||||
snprintf(filename, size+1, SPD_SPD5118_PATH, bus->port_id, address);
|
|
||||||
|
|
||||||
std::ifstream eeprom_file(filename, std::ios::in | std::ios::binary);
|
|
||||||
if(eeprom_file)
|
|
||||||
{
|
|
||||||
eeprom_file.read((char*)dump, sizeof(dump));
|
|
||||||
eeprom_file.close();
|
|
||||||
}
|
|
||||||
delete[] filename;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
223
SPDAccessor.h
223
SPDAccessor.h
|
|
@ -1,223 +0,0 @@
|
||||||
/*---------------------------------------------------------*\
|
|
||||||
| SPDAccessor.h |
|
|
||||||
| |
|
|
||||||
| Access to SPD information on various DIMMs |
|
|
||||||
| |
|
|
||||||
| This file is part of the OpenRGB project |
|
|
||||||
| SPDX-License-Identifier: GPL-2.0-only |
|
|
||||||
\*---------------------------------------------------------*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <vector>
|
|
||||||
#include "i2c_smbus.h"
|
|
||||||
|
|
||||||
typedef enum
|
|
||||||
{
|
|
||||||
JEDEC_KINGSTON = 0x0117,
|
|
||||||
JEDEC_CORSAIR = 0x021d,
|
|
||||||
JEDEC_ADATA = 0x044a,
|
|
||||||
JEDEC_GSKILL = 0x044c,
|
|
||||||
JEDEC_TEAMGROUP = 0x046e,
|
|
||||||
JEDEC_MUSHKIN = 0x8313,
|
|
||||||
JEDEC_GIGABYTE = 0x8971,
|
|
||||||
JEDEC_THERMALTAKE = 0x8a41
|
|
||||||
} JedecIdentifier;
|
|
||||||
|
|
||||||
typedef enum
|
|
||||||
{
|
|
||||||
SPD_RESERVED = 0,
|
|
||||||
SPD_FPM_DRAM = 1,
|
|
||||||
SPD_EDO = 2,
|
|
||||||
SPD_NIBBLE = 3,
|
|
||||||
SPD_SDR_SDRAM = 4,
|
|
||||||
SPD_MUX_ROM = 5,
|
|
||||||
SPD_DDR_SGRAM = 6,
|
|
||||||
SPD_DDR_SDRAM = 7,
|
|
||||||
SPD_DDR2_SDRAM = 8,
|
|
||||||
SPD_FB_DIMM = 9,
|
|
||||||
SPD_FB_PROBE = 10,
|
|
||||||
SPD_DDR3_SDRAM = 11,
|
|
||||||
SPD_DDR4_SDRAM = 12,
|
|
||||||
SPD_RESERVED2 = 13,
|
|
||||||
SPD_DDR4E_SDRAM = 14,
|
|
||||||
SPD_LPDDR3_SDRAM = 15,
|
|
||||||
SPD_LPDDR4_SDRAM = 16,
|
|
||||||
SPD_LPDDR4X_SDRAM = 17,
|
|
||||||
SPD_DDR5_SDRAM = 18,
|
|
||||||
SPD_LPDDR5_SDRAM = 19
|
|
||||||
} SPDMemoryType;
|
|
||||||
|
|
||||||
#define SPD_IO_DELAY 1ms
|
|
||||||
|
|
||||||
extern const char *spd_memory_type_name[];
|
|
||||||
|
|
||||||
class SPDDetector
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
SPDDetector(i2c_smbus_interface *bus, uint8_t address, SPDMemoryType mem_type);
|
|
||||||
|
|
||||||
bool is_valid() const;
|
|
||||||
SPDMemoryType memory_type() const;
|
|
||||||
uint8_t spd_address() const;
|
|
||||||
i2c_smbus_interface *smbus() const;
|
|
||||||
|
|
||||||
private:
|
|
||||||
i2c_smbus_interface *bus;
|
|
||||||
uint8_t address;
|
|
||||||
SPDMemoryType mem_type;
|
|
||||||
bool valid;
|
|
||||||
|
|
||||||
void detect_memory_type();
|
|
||||||
};
|
|
||||||
|
|
||||||
class SPDAccessor
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
SPDAccessor(i2c_smbus_interface *bus, uint8_t address);
|
|
||||||
virtual ~SPDAccessor();
|
|
||||||
|
|
||||||
static SPDAccessor *for_memory_type(SPDMemoryType type, i2c_smbus_interface *bus, uint8_t address);
|
|
||||||
|
|
||||||
virtual SPDMemoryType memory_type() = 0;
|
|
||||||
virtual uint16_t jedec_id() = 0;
|
|
||||||
|
|
||||||
virtual SPDAccessor *copy() = 0;
|
|
||||||
|
|
||||||
virtual uint8_t at(uint16_t addr) = 0;
|
|
||||||
|
|
||||||
protected:
|
|
||||||
i2c_smbus_interface *bus;
|
|
||||||
uint8_t address;
|
|
||||||
};
|
|
||||||
|
|
||||||
class SPDWrapper
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
SPDWrapper(const SPDWrapper &wrapper);
|
|
||||||
SPDWrapper(const SPDDetector &detector);
|
|
||||||
~SPDWrapper();
|
|
||||||
|
|
||||||
SPDMemoryType memory_type();
|
|
||||||
int index();
|
|
||||||
uint16_t jedec_id();
|
|
||||||
|
|
||||||
private:
|
|
||||||
SPDAccessor *accessor = nullptr;
|
|
||||||
uint8_t address;
|
|
||||||
SPDMemoryType mem_type;
|
|
||||||
};
|
|
||||||
|
|
||||||
/*-------------------------------------------------------------------------*\
|
|
||||||
| Internal implementation for specific memory type. |
|
|
||||||
\*-------------------------------------------------------------------------*/
|
|
||||||
|
|
||||||
class DDR4Accessor : public SPDAccessor
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
DDR4Accessor(i2c_smbus_interface *bus, uint8_t address);
|
|
||||||
virtual ~DDR4Accessor();
|
|
||||||
virtual SPDMemoryType memory_type();
|
|
||||||
virtual uint16_t jedec_id();
|
|
||||||
};
|
|
||||||
|
|
||||||
class DDR4DirectAccessor : public DDR4Accessor
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
DDR4DirectAccessor(i2c_smbus_interface *bus, uint8_t address);
|
|
||||||
virtual ~DDR4DirectAccessor();
|
|
||||||
|
|
||||||
static bool isAvailable(i2c_smbus_interface *bus, uint8_t address);
|
|
||||||
|
|
||||||
virtual SPDAccessor *copy();
|
|
||||||
virtual uint8_t at(uint16_t addr);
|
|
||||||
|
|
||||||
private:
|
|
||||||
uint8_t current_page = 0xFF;
|
|
||||||
static const uint16_t SPD_DDR4_EEPROM_LENGTH = 512;
|
|
||||||
static const uint8_t SPD_DDR4_EEPROM_PAGE_SHIFT = 8;
|
|
||||||
static const uint8_t SPD_DDR4_EEPROM_PAGE_MASK = 0xFF;
|
|
||||||
|
|
||||||
void set_page(uint8_t page);
|
|
||||||
};
|
|
||||||
|
|
||||||
#ifdef __linux__
|
|
||||||
class EE1004Accessor : public DDR4Accessor
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
EE1004Accessor(i2c_smbus_interface *bus, uint8_t address);
|
|
||||||
virtual ~EE1004Accessor();
|
|
||||||
|
|
||||||
static bool isAvailable(i2c_smbus_interface *bus, uint8_t address);
|
|
||||||
|
|
||||||
virtual SPDAccessor *copy();
|
|
||||||
virtual uint8_t at(uint16_t addr);
|
|
||||||
|
|
||||||
private:
|
|
||||||
static const char *SPD_EE1004_PATH;
|
|
||||||
|
|
||||||
uint8_t dump[512];
|
|
||||||
bool valid;
|
|
||||||
|
|
||||||
void readEeprom();
|
|
||||||
};
|
|
||||||
#endif
|
|
||||||
|
|
||||||
class DDR5Accessor : public SPDAccessor
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
DDR5Accessor(i2c_smbus_interface *bus, uint8_t address);
|
|
||||||
virtual ~DDR5Accessor();
|
|
||||||
virtual SPDMemoryType memory_type();
|
|
||||||
virtual uint16_t jedec_id();
|
|
||||||
};
|
|
||||||
|
|
||||||
class DDR5DirectAccessor : public DDR5Accessor
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
DDR5DirectAccessor(i2c_smbus_interface *bus, uint8_t address);
|
|
||||||
virtual ~DDR5DirectAccessor();
|
|
||||||
|
|
||||||
static bool isAvailable(i2c_smbus_interface *bus, uint8_t address);
|
|
||||||
|
|
||||||
virtual SPDAccessor *copy();
|
|
||||||
virtual uint8_t at(uint16_t addr);
|
|
||||||
|
|
||||||
private:
|
|
||||||
uint8_t current_page = 0xFF;
|
|
||||||
static const uint16_t SPD_DDR5_EEPROM_LENGTH = 2048;
|
|
||||||
static const uint8_t SPD_DDR5_EEPROM_PAGE_SHIFT = 7;
|
|
||||||
static const uint8_t SPD_DDR5_EEPROM_PAGE_MASK = 0x7F;
|
|
||||||
static const uint8_t SPD_DDR5_MREG_VIRTUAL_PAGE = 0x0B;
|
|
||||||
|
|
||||||
void set_page(uint8_t page);
|
|
||||||
};
|
|
||||||
|
|
||||||
#ifdef __linux__
|
|
||||||
class SPD5118Accessor : public DDR5Accessor
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
SPD5118Accessor(i2c_smbus_interface *bus, uint8_t address);
|
|
||||||
virtual ~SPD5118Accessor();
|
|
||||||
|
|
||||||
static bool isAvailable(i2c_smbus_interface *bus, uint8_t address);
|
|
||||||
|
|
||||||
virtual SPDAccessor *copy();
|
|
||||||
virtual uint8_t at(uint16_t addr);
|
|
||||||
|
|
||||||
private:
|
|
||||||
static const char *SPD_SPD5118_PATH;
|
|
||||||
|
|
||||||
uint8_t dump[2048];
|
|
||||||
bool valid;
|
|
||||||
|
|
||||||
void readEeprom();
|
|
||||||
};
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/*-------------------------------------------------------------------------*\
|
|
||||||
| Helper functions for easier collection handling. |
|
|
||||||
\*-------------------------------------------------------------------------*/
|
|
||||||
|
|
||||||
bool is_jedec_in_slots(std::vector<SPDWrapper> &slots, uint16_t jedec_id);
|
|
||||||
std::vector<SPDWrapper*> slots_with_jedec(std::vector<SPDWrapper> &slots, uint16_t jedec_id);
|
|
||||||
103
SPDAccessor/DDR4DirectAccessor.cpp
Normal file
103
SPDAccessor/DDR4DirectAccessor.cpp
Normal file
|
|
@ -0,0 +1,103 @@
|
||||||
|
/*---------------------------------------------------------*\
|
||||||
|
| DDR4DirectAccessor.cpp |
|
||||||
|
| |
|
||||||
|
| DDR4 SPD accessor implementation using direct i2c |
|
||||||
|
| |
|
||||||
|
| This file is part of the OpenRGB project |
|
||||||
|
| SPDX-License-Identifier: GPL-2.0-only |
|
||||||
|
\*---------------------------------------------------------*/
|
||||||
|
|
||||||
|
#include "DDR4DirectAccessor.h"
|
||||||
|
|
||||||
|
using namespace std::chrono_literals;
|
||||||
|
|
||||||
|
DDR4DirectAccessor::DDR4DirectAccessor(i2c_smbus_interface *bus, uint8_t spd_addr)
|
||||||
|
: DDR4Accessor(bus, spd_addr)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
DDR4DirectAccessor::~DDR4DirectAccessor()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DDR4DirectAccessor::isAvailable(i2c_smbus_interface *bus, uint8_t spd_addr)
|
||||||
|
{
|
||||||
|
/*-----------------------------------------------------*\
|
||||||
|
| Perform i2c quick transfer to test if i2c address |
|
||||||
|
| responds |
|
||||||
|
\*-----------------------------------------------------*/
|
||||||
|
int value = bus->i2c_smbus_write_quick(0x36, 0x00);
|
||||||
|
if(value < 0)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*-----------------------------------------------------*\
|
||||||
|
| Select low page |
|
||||||
|
\*-----------------------------------------------------*/
|
||||||
|
bus->i2c_smbus_write_byte_data(0x36, 0x00, 0xFF);
|
||||||
|
|
||||||
|
std::this_thread::sleep_for(SPD_IO_DELAY);
|
||||||
|
|
||||||
|
/*-----------------------------------------------------*\
|
||||||
|
| Read value at address 0 in SPD device |
|
||||||
|
\*-----------------------------------------------------*/
|
||||||
|
value = bus->i2c_smbus_read_byte_data(spd_addr, 0x00);
|
||||||
|
|
||||||
|
/*-----------------------------------------------------*\
|
||||||
|
| DDR4 is available if value is 0x23 |
|
||||||
|
\*-----------------------------------------------------*/
|
||||||
|
return(value == 0x23);
|
||||||
|
}
|
||||||
|
|
||||||
|
SPDAccessor *DDR4DirectAccessor::copy()
|
||||||
|
{
|
||||||
|
return new DDR4DirectAccessor(bus, address);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t DDR4DirectAccessor::at(uint16_t addr)
|
||||||
|
{
|
||||||
|
/*-----------------------------------------------------*\
|
||||||
|
| Ensure address is valid |
|
||||||
|
\*-----------------------------------------------------*/
|
||||||
|
if(addr >= SPD_DDR4_EEPROM_LENGTH)
|
||||||
|
{
|
||||||
|
return 0xFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*-----------------------------------------------------*\
|
||||||
|
| Switch to the page containing address |
|
||||||
|
\*-----------------------------------------------------*/
|
||||||
|
set_page(addr >> SPD_DDR4_EEPROM_PAGE_SHIFT);
|
||||||
|
|
||||||
|
/*-----------------------------------------------------*\
|
||||||
|
| Calculate offset |
|
||||||
|
\*-----------------------------------------------------*/
|
||||||
|
uint8_t offset = (uint8_t)(addr & SPD_DDR4_EEPROM_PAGE_MASK);
|
||||||
|
|
||||||
|
/*-----------------------------------------------------*\
|
||||||
|
| Read value at address |
|
||||||
|
\*-----------------------------------------------------*/
|
||||||
|
uint32_t value = bus->i2c_smbus_read_byte_data(address, offset);
|
||||||
|
|
||||||
|
std::this_thread::sleep_for(SPD_IO_DELAY);
|
||||||
|
|
||||||
|
/*-----------------------------------------------------*\
|
||||||
|
| Return value |
|
||||||
|
\*-----------------------------------------------------*/
|
||||||
|
return((uint8_t)value);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DDR4DirectAccessor::set_page(uint8_t page)
|
||||||
|
{
|
||||||
|
/*-----------------------------------------------------*\
|
||||||
|
| Switch page if not already active |
|
||||||
|
\*-----------------------------------------------------*/
|
||||||
|
if(current_page != page)
|
||||||
|
{
|
||||||
|
bus->i2c_smbus_write_byte_data(0x36 + page, 0x00, 0xFF);
|
||||||
|
current_page = page;
|
||||||
|
|
||||||
|
std::this_thread::sleep_for(SPD_IO_DELAY);
|
||||||
|
}
|
||||||
|
}
|
||||||
32
SPDAccessor/DDR4DirectAccessor.h
Normal file
32
SPDAccessor/DDR4DirectAccessor.h
Normal file
|
|
@ -0,0 +1,32 @@
|
||||||
|
/*---------------------------------------------------------*\
|
||||||
|
| DDR4DirectAccessor.h |
|
||||||
|
| |
|
||||||
|
| DDR4 SPD accessor implementation using direct i2c |
|
||||||
|
| |
|
||||||
|
| This file is part of the OpenRGB project |
|
||||||
|
| SPDX-License-Identifier: GPL-2.0-only |
|
||||||
|
\*---------------------------------------------------------*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "SPDAccessor.h"
|
||||||
|
|
||||||
|
class DDR4DirectAccessor : public DDR4Accessor
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
DDR4DirectAccessor(i2c_smbus_interface *bus, uint8_t address);
|
||||||
|
virtual ~DDR4DirectAccessor();
|
||||||
|
|
||||||
|
static bool isAvailable(i2c_smbus_interface *bus, uint8_t address);
|
||||||
|
|
||||||
|
virtual SPDAccessor * copy();
|
||||||
|
virtual uint8_t at(uint16_t addr);
|
||||||
|
|
||||||
|
private:
|
||||||
|
uint8_t current_page = 0xFF;
|
||||||
|
static const uint16_t SPD_DDR4_EEPROM_LENGTH = 512;
|
||||||
|
static const uint8_t SPD_DDR4_EEPROM_PAGE_SHIFT = 8;
|
||||||
|
static const uint8_t SPD_DDR4_EEPROM_PAGE_MASK = 0xFF;
|
||||||
|
|
||||||
|
void set_page(uint8_t page);
|
||||||
|
};
|
||||||
122
SPDAccessor/DDR5DirectAccessor.cpp
Normal file
122
SPDAccessor/DDR5DirectAccessor.cpp
Normal file
|
|
@ -0,0 +1,122 @@
|
||||||
|
/*---------------------------------------------------------*\
|
||||||
|
| DDR5DirectAccessor.cpp |
|
||||||
|
| |
|
||||||
|
| DDR5 SPD accessor implementation using direct i2c |
|
||||||
|
| |
|
||||||
|
| This file is part of the OpenRGB project |
|
||||||
|
| SPDX-License-Identifier: GPL-2.0-only |
|
||||||
|
\*---------------------------------------------------------*/
|
||||||
|
|
||||||
|
#include "DDR5DirectAccessor.h"
|
||||||
|
#include "LogManager.h"
|
||||||
|
|
||||||
|
using namespace std::chrono_literals;
|
||||||
|
|
||||||
|
DDR5DirectAccessor::DDR5DirectAccessor(i2c_smbus_interface *bus, uint8_t spd_addr)
|
||||||
|
: DDR5Accessor(bus, spd_addr)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
DDR5DirectAccessor::~DDR5DirectAccessor()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DDR5DirectAccessor::isAvailable(i2c_smbus_interface *bus, uint8_t spd_addr)
|
||||||
|
{
|
||||||
|
bool retry = true;
|
||||||
|
|
||||||
|
while(true)
|
||||||
|
{
|
||||||
|
std::this_thread::sleep_for(SPD_IO_DELAY);
|
||||||
|
int ddr5Magic = bus->i2c_smbus_read_byte_data(spd_addr, 0x00);
|
||||||
|
std::this_thread::sleep_for(SPD_IO_DELAY);
|
||||||
|
int ddr5Sensor = bus->i2c_smbus_read_byte_data(spd_addr, 0x01);
|
||||||
|
std::this_thread::sleep_for(SPD_IO_DELAY);
|
||||||
|
|
||||||
|
if(ddr5Magic < 0 || ddr5Sensor < 0)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG_TRACE("SPD Hub Magic: 0x%02x 0x%02x", ddr5Magic, ddr5Sensor);
|
||||||
|
|
||||||
|
if(ddr5Magic == 0x51 && (ddr5Sensor & 0xEF) == 0x08)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
int page = bus->i2c_smbus_read_byte_data(spd_addr, SPD_DDR5_MREG_VIRTUAL_PAGE);
|
||||||
|
std::this_thread::sleep_for(SPD_IO_DELAY);
|
||||||
|
|
||||||
|
LOG_TRACE("SPD Page: 0x%02x", page);
|
||||||
|
if(page < 0)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else if(retry && page > 0 && page < (SPD_DDR5_EEPROM_LENGTH >> SPD_DDR5_EEPROM_PAGE_SHIFT))
|
||||||
|
{
|
||||||
|
// This still might be a DDR5 module, just the page is off
|
||||||
|
bus->i2c_smbus_write_byte_data(spd_addr, SPD_DDR5_MREG_VIRTUAL_PAGE, 0);
|
||||||
|
std::this_thread::sleep_for(SPD_IO_DELAY);
|
||||||
|
retry = false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
SPDAccessor *DDR5DirectAccessor::copy()
|
||||||
|
{
|
||||||
|
DDR5DirectAccessor *access = new DDR5DirectAccessor(bus, address);
|
||||||
|
access->current_page = this->current_page;
|
||||||
|
return access;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t DDR5DirectAccessor::at(uint16_t addr)
|
||||||
|
{
|
||||||
|
/*-----------------------------------------------------*\
|
||||||
|
| Ensure address is valid |
|
||||||
|
\*-----------------------------------------------------*/
|
||||||
|
if(addr >= SPD_DDR5_EEPROM_LENGTH)
|
||||||
|
{
|
||||||
|
return 0xFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*-----------------------------------------------------*\
|
||||||
|
| Switch to the page containing address |
|
||||||
|
\*-----------------------------------------------------*/
|
||||||
|
set_page(addr >> SPD_DDR5_EEPROM_PAGE_SHIFT);
|
||||||
|
|
||||||
|
/*-----------------------------------------------------*\
|
||||||
|
| Calculate offset |
|
||||||
|
\*-----------------------------------------------------*/
|
||||||
|
uint8_t offset = (uint8_t)(addr & SPD_DDR5_EEPROM_PAGE_MASK) | 0x80;
|
||||||
|
|
||||||
|
/*-----------------------------------------------------*\
|
||||||
|
| Read value at address |
|
||||||
|
\*-----------------------------------------------------*/
|
||||||
|
uint32_t value = bus->i2c_smbus_read_byte_data(address, offset);
|
||||||
|
|
||||||
|
std::this_thread::sleep_for(SPD_IO_DELAY);
|
||||||
|
|
||||||
|
/*-----------------------------------------------------*\
|
||||||
|
| Return value |
|
||||||
|
\*-----------------------------------------------------*/
|
||||||
|
return((uint8_t)value);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DDR5DirectAccessor::set_page(uint8_t page)
|
||||||
|
{
|
||||||
|
/*-----------------------------------------------------*\
|
||||||
|
| Switch page if not already active |
|
||||||
|
\*-----------------------------------------------------*/
|
||||||
|
if(current_page != page)
|
||||||
|
{
|
||||||
|
bus->i2c_smbus_write_byte_data(address, SPD_DDR5_MREG_VIRTUAL_PAGE, page);
|
||||||
|
current_page = page;
|
||||||
|
std::this_thread::sleep_for(SPD_IO_DELAY);
|
||||||
|
}
|
||||||
|
}
|
||||||
33
SPDAccessor/DDR5DirectAccessor.h
Normal file
33
SPDAccessor/DDR5DirectAccessor.h
Normal file
|
|
@ -0,0 +1,33 @@
|
||||||
|
/*---------------------------------------------------------*\
|
||||||
|
| DDR5DirectAccessor.h |
|
||||||
|
| |
|
||||||
|
| DDR5 SPD accessor implementation using direct i2c |
|
||||||
|
| |
|
||||||
|
| This file is part of the OpenRGB project |
|
||||||
|
| SPDX-License-Identifier: GPL-2.0-only |
|
||||||
|
\*---------------------------------------------------------*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "SPDAccessor.h"
|
||||||
|
|
||||||
|
class DDR5DirectAccessor : public DDR5Accessor
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
DDR5DirectAccessor(i2c_smbus_interface *bus, uint8_t address);
|
||||||
|
virtual ~DDR5DirectAccessor();
|
||||||
|
|
||||||
|
static bool isAvailable(i2c_smbus_interface *bus, uint8_t address);
|
||||||
|
|
||||||
|
virtual SPDAccessor * copy();
|
||||||
|
virtual uint8_t at(uint16_t addr);
|
||||||
|
|
||||||
|
private:
|
||||||
|
uint8_t current_page = 0xFF;
|
||||||
|
static const uint16_t SPD_DDR5_EEPROM_LENGTH = 2048;
|
||||||
|
static const uint8_t SPD_DDR5_EEPROM_PAGE_SHIFT = 7;
|
||||||
|
static const uint8_t SPD_DDR5_EEPROM_PAGE_MASK = 0x7F;
|
||||||
|
static const uint8_t SPD_DDR5_MREG_VIRTUAL_PAGE = 0x0B;
|
||||||
|
|
||||||
|
void set_page(uint8_t page);
|
||||||
|
};
|
||||||
66
SPDAccessor/EE1004Accessor_Linux.cpp
Normal file
66
SPDAccessor/EE1004Accessor_Linux.cpp
Normal file
|
|
@ -0,0 +1,66 @@
|
||||||
|
/*---------------------------------------------------------*\
|
||||||
|
| EE1004Accessor_Linux.cpp |
|
||||||
|
| |
|
||||||
|
| SPD accessor implementation using e1004 driver on Linux |
|
||||||
|
| |
|
||||||
|
| This file is part of the OpenRGB project |
|
||||||
|
| SPDX-License-Identifier: GPL-2.0-only |
|
||||||
|
\*---------------------------------------------------------*/
|
||||||
|
|
||||||
|
#include <cstring>
|
||||||
|
#include <fstream>
|
||||||
|
#include "EE1004Accessor_Linux.h"
|
||||||
|
#include "filesystem.h"
|
||||||
|
|
||||||
|
const char *EE1004Accessor::SPD_EE1004_PATH = "/sys/bus/i2c/drivers/ee1004/%u-%04x/eeprom";
|
||||||
|
|
||||||
|
EE1004Accessor::EE1004Accessor(i2c_smbus_interface *bus, uint8_t spd_addr)
|
||||||
|
: DDR4Accessor(bus, spd_addr), valid(false)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
EE1004Accessor::~EE1004Accessor()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
bool EE1004Accessor::isAvailable(i2c_smbus_interface *bus, uint8_t spd_addr)
|
||||||
|
{
|
||||||
|
int size = snprintf(nullptr, 0, SPD_EE1004_PATH, bus->port_id, spd_addr);
|
||||||
|
char *path = new char[size+1];
|
||||||
|
snprintf(path, size+1, SPD_EE1004_PATH, bus->port_id, spd_addr);
|
||||||
|
bool result = std::filesystem::exists(path);
|
||||||
|
delete[] path;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
SPDAccessor *EE1004Accessor::copy()
|
||||||
|
{
|
||||||
|
EE1004Accessor *access = new EE1004Accessor(bus, address);
|
||||||
|
memcpy(access->dump, this->dump, sizeof(this->dump));
|
||||||
|
access->valid = this->valid;
|
||||||
|
return access;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t EE1004Accessor::at(uint16_t addr)
|
||||||
|
{
|
||||||
|
if(!valid)
|
||||||
|
{
|
||||||
|
readEEPROM();
|
||||||
|
}
|
||||||
|
return dump[addr];
|
||||||
|
}
|
||||||
|
|
||||||
|
void EE1004Accessor::readEEPROM()
|
||||||
|
{
|
||||||
|
int size = snprintf(nullptr, 0, SPD_EE1004_PATH, bus->port_id, address);
|
||||||
|
char *filename = new char[size+1];
|
||||||
|
snprintf(filename, size+1, SPD_EE1004_PATH, bus->port_id, address);
|
||||||
|
|
||||||
|
std::ifstream eeprom_file(filename, std::ios::in | std::ios::binary);
|
||||||
|
if(eeprom_file)
|
||||||
|
{
|
||||||
|
eeprom_file.read((char*)dump, sizeof(dump));
|
||||||
|
eeprom_file.close();
|
||||||
|
}
|
||||||
|
delete[] filename;
|
||||||
|
}
|
||||||
32
SPDAccessor/EE1004Accessor_Linux.h
Normal file
32
SPDAccessor/EE1004Accessor_Linux.h
Normal file
|
|
@ -0,0 +1,32 @@
|
||||||
|
/*---------------------------------------------------------*\
|
||||||
|
| EE1004Accessor_Linux.h |
|
||||||
|
| |
|
||||||
|
| SPD accessor implementation using e1004 driver on Linux |
|
||||||
|
| |
|
||||||
|
| This file is part of the OpenRGB project |
|
||||||
|
| SPDX-License-Identifier: GPL-2.0-only |
|
||||||
|
\*---------------------------------------------------------*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "SPDAccessor.h"
|
||||||
|
|
||||||
|
class EE1004Accessor : public DDR4Accessor
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
EE1004Accessor(i2c_smbus_interface *bus, uint8_t address);
|
||||||
|
virtual ~EE1004Accessor();
|
||||||
|
|
||||||
|
static bool isAvailable(i2c_smbus_interface *bus, uint8_t address);
|
||||||
|
|
||||||
|
virtual SPDAccessor *copy();
|
||||||
|
virtual uint8_t at(uint16_t addr);
|
||||||
|
|
||||||
|
private:
|
||||||
|
static const char *SPD_EE1004_PATH;
|
||||||
|
|
||||||
|
uint8_t dump[512];
|
||||||
|
bool valid;
|
||||||
|
|
||||||
|
void readEEPROM();
|
||||||
|
};
|
||||||
67
SPDAccessor/SPD5118Accessor_Linux.cpp
Normal file
67
SPDAccessor/SPD5118Accessor_Linux.cpp
Normal file
|
|
@ -0,0 +1,67 @@
|
||||||
|
/*---------------------------------------------------------*\
|
||||||
|
| SPD5118Accessor_Linux.cpp |
|
||||||
|
| |
|
||||||
|
| DDR5 SPD accessor implementation using spd5118 driver |
|
||||||
|
| on Linux |
|
||||||
|
| |
|
||||||
|
| This file is part of the OpenRGB project |
|
||||||
|
| SPDX-License-Identifier: GPL-2.0-only |
|
||||||
|
\*---------------------------------------------------------*/
|
||||||
|
|
||||||
|
#include <cstring>
|
||||||
|
#include <fstream>
|
||||||
|
#include "SPD5118Accessor_Linux.h"
|
||||||
|
#include "filesystem.h"
|
||||||
|
|
||||||
|
const char *SPD5118Accessor::SPD_SPD5118_PATH = "/sys/bus/i2c/drivers/spd5118/%u-%04x/eeprom";
|
||||||
|
|
||||||
|
SPD5118Accessor::SPD5118Accessor(i2c_smbus_interface *bus, uint8_t spd_addr)
|
||||||
|
: DDR5Accessor(bus, spd_addr), valid(false)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
SPD5118Accessor::~SPD5118Accessor()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SPD5118Accessor::isAvailable(i2c_smbus_interface *bus, uint8_t spd_addr)
|
||||||
|
{
|
||||||
|
int size = snprintf(nullptr, 0, SPD_SPD5118_PATH, bus->port_id, spd_addr);
|
||||||
|
char *path = new char[size+1];
|
||||||
|
snprintf(path, size+1, SPD_SPD5118_PATH, bus->port_id, spd_addr);
|
||||||
|
bool result = std::filesystem::exists(path);
|
||||||
|
delete[] path;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
SPDAccessor *SPD5118Accessor::copy()
|
||||||
|
{
|
||||||
|
SPD5118Accessor *access = new SPD5118Accessor(bus, address);
|
||||||
|
memcpy(access->dump, this->dump, sizeof(this->dump));
|
||||||
|
access->valid = this->valid;
|
||||||
|
return access;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t SPD5118Accessor::at(uint16_t addr)
|
||||||
|
{
|
||||||
|
if(!valid)
|
||||||
|
{
|
||||||
|
readEEPROM();
|
||||||
|
}
|
||||||
|
return dump[addr];
|
||||||
|
}
|
||||||
|
|
||||||
|
void SPD5118Accessor::readEEPROM()
|
||||||
|
{
|
||||||
|
int size = snprintf(nullptr, 0, SPD_SPD5118_PATH, bus->port_id, address);
|
||||||
|
char *filename = new char[size+1];
|
||||||
|
snprintf(filename, size+1, SPD_SPD5118_PATH, bus->port_id, address);
|
||||||
|
|
||||||
|
std::ifstream eeprom_file(filename, std::ios::in | std::ios::binary);
|
||||||
|
if(eeprom_file)
|
||||||
|
{
|
||||||
|
eeprom_file.read((char*)dump, sizeof(dump));
|
||||||
|
eeprom_file.close();
|
||||||
|
}
|
||||||
|
delete[] filename;
|
||||||
|
}
|
||||||
33
SPDAccessor/SPD5118Accessor_Linux.h
Normal file
33
SPDAccessor/SPD5118Accessor_Linux.h
Normal file
|
|
@ -0,0 +1,33 @@
|
||||||
|
/*---------------------------------------------------------*\
|
||||||
|
| SPD5118Accessor_Linux.h |
|
||||||
|
| |
|
||||||
|
| DDR5 SPD accessor implementation using spd5118 driver |
|
||||||
|
| on Linux |
|
||||||
|
| |
|
||||||
|
| This file is part of the OpenRGB project |
|
||||||
|
| SPDX-License-Identifier: GPL-2.0-only |
|
||||||
|
\*---------------------------------------------------------*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "SPDAccessor.h"
|
||||||
|
|
||||||
|
class SPD5118Accessor : public DDR5Accessor
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
SPD5118Accessor(i2c_smbus_interface *bus, uint8_t address);
|
||||||
|
virtual ~SPD5118Accessor();
|
||||||
|
|
||||||
|
static bool isAvailable(i2c_smbus_interface *bus, uint8_t address);
|
||||||
|
|
||||||
|
virtual SPDAccessor *copy();
|
||||||
|
virtual uint8_t at(uint16_t addr);
|
||||||
|
|
||||||
|
private:
|
||||||
|
static const char *SPD_SPD5118_PATH;
|
||||||
|
|
||||||
|
uint8_t dump[2048];
|
||||||
|
bool valid;
|
||||||
|
|
||||||
|
void readEEPROM();
|
||||||
|
};
|
||||||
131
SPDAccessor/SPDAccessor.cpp
Normal file
131
SPDAccessor/SPDAccessor.cpp
Normal file
|
|
@ -0,0 +1,131 @@
|
||||||
|
/*---------------------------------------------------------*\
|
||||||
|
| SPDAccessor.cpp |
|
||||||
|
| |
|
||||||
|
| Access to SPD information on various DIMMs |
|
||||||
|
| |
|
||||||
|
| Milan Cermak (krysmanta) 09 Nov 2024 |
|
||||||
|
| |
|
||||||
|
| This file is part of the OpenRGB project |
|
||||||
|
| SPDX-License-Identifier: GPL-2.0-only |
|
||||||
|
\*---------------------------------------------------------*/
|
||||||
|
|
||||||
|
#include "DDR4DirectAccessor.h"
|
||||||
|
#include "DDR5DirectAccessor.h"
|
||||||
|
#include "LogManager.h"
|
||||||
|
#include "SPDAccessor.h"
|
||||||
|
|
||||||
|
#ifdef __linux__
|
||||||
|
#include "EE1004Accessor_Linux.h"
|
||||||
|
#include "SPD5118Accessor_Linux.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
using namespace std::chrono_literals;
|
||||||
|
|
||||||
|
const char *spd_memory_type_name[] =
|
||||||
|
{
|
||||||
|
"Reserved",
|
||||||
|
"FPM",
|
||||||
|
"EDO",
|
||||||
|
"Nibble",
|
||||||
|
"SDR",
|
||||||
|
"Multiplex ROM",
|
||||||
|
"DDR",
|
||||||
|
"DDR",
|
||||||
|
"DDR2",
|
||||||
|
"FB",
|
||||||
|
"FB Probe",
|
||||||
|
"DDR3",
|
||||||
|
"DDR4",
|
||||||
|
"Reserved",
|
||||||
|
"DDR4e",
|
||||||
|
"LPDDR3",
|
||||||
|
"LPDDR4",
|
||||||
|
"LPDDR4X",
|
||||||
|
"DDR5",
|
||||||
|
"LPDDR5"
|
||||||
|
};
|
||||||
|
|
||||||
|
SPDAccessor::SPDAccessor(i2c_smbus_interface *bus, uint8_t spd_addr)
|
||||||
|
{
|
||||||
|
this->bus = bus;
|
||||||
|
this->address = spd_addr;
|
||||||
|
}
|
||||||
|
|
||||||
|
SPDAccessor::~SPDAccessor()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
SPDAccessor *SPDAccessor::for_memory_type(SPDMemoryType type, i2c_smbus_interface *bus, uint8_t spd_addr)
|
||||||
|
{
|
||||||
|
/*-----------------------------------------------------*\
|
||||||
|
| DDR4 can use DDR4DirectAccessor or EE1004Accessor |
|
||||||
|
\*-----------------------------------------------------*/
|
||||||
|
if(type == SPD_DDR4_SDRAM)
|
||||||
|
{
|
||||||
|
#ifdef __linux__
|
||||||
|
if(EE1004Accessor::isAvailable(bus, spd_addr))
|
||||||
|
{
|
||||||
|
return(new EE1004Accessor(bus, spd_addr));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
return(new DDR4DirectAccessor(bus, spd_addr));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*-----------------------------------------------------*\
|
||||||
|
| DDR5 can use DDR5DirectAccessor or SPD5118Accessor |
|
||||||
|
\*-----------------------------------------------------*/
|
||||||
|
if(type == SPD_DDR5_SDRAM)
|
||||||
|
{
|
||||||
|
#ifdef __linux__
|
||||||
|
if(SPD5118Accessor::isAvailable(bus, spd_addr))
|
||||||
|
{
|
||||||
|
return(new SPD5118Accessor(bus, spd_addr));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
return(new DDR5DirectAccessor(bus, spd_addr));
|
||||||
|
}
|
||||||
|
|
||||||
|
return(nullptr);
|
||||||
|
};
|
||||||
|
|
||||||
|
/*---------------------------------------------------------*\
|
||||||
|
| Internal implementation for specific memory type. |
|
||||||
|
\*---------------------------------------------------------*/
|
||||||
|
|
||||||
|
DDR4Accessor::DDR4Accessor(i2c_smbus_interface *bus, uint8_t spd_addr)
|
||||||
|
: SPDAccessor(bus, spd_addr)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
DDR4Accessor::~DDR4Accessor()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
SPDMemoryType DDR4Accessor::memory_type()
|
||||||
|
{
|
||||||
|
return((SPDMemoryType)(this->at(0x02)));
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t DDR4Accessor::jedec_id()
|
||||||
|
{
|
||||||
|
return((this->at(0x140) << 8) + (this->at(0x141) & 0x7f) - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
DDR5Accessor::DDR5Accessor(i2c_smbus_interface *bus, uint8_t spd_addr)
|
||||||
|
: SPDAccessor(bus, spd_addr)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
DDR5Accessor::~DDR5Accessor()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
SPDMemoryType DDR5Accessor::memory_type()
|
||||||
|
{
|
||||||
|
return((SPDMemoryType)(this->at(0x02)));
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t DDR5Accessor::jedec_id()
|
||||||
|
{
|
||||||
|
return((this->at(0x200) << 8) + (this->at(0x201) & 0x7f) - 1);
|
||||||
|
}
|
||||||
54
SPDAccessor/SPDAccessor.h
Normal file
54
SPDAccessor/SPDAccessor.h
Normal file
|
|
@ -0,0 +1,54 @@
|
||||||
|
/*---------------------------------------------------------*\
|
||||||
|
| SPDAccessor.h |
|
||||||
|
| |
|
||||||
|
| Access to SPD information on various DIMMs |
|
||||||
|
| |
|
||||||
|
| This file is part of the OpenRGB project |
|
||||||
|
| SPDX-License-Identifier: GPL-2.0-only |
|
||||||
|
\*---------------------------------------------------------*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "SPDCommon.h"
|
||||||
|
|
||||||
|
class SPDAccessor
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
SPDAccessor(i2c_smbus_interface *bus, uint8_t address);
|
||||||
|
virtual ~SPDAccessor();
|
||||||
|
|
||||||
|
static SPDAccessor *for_memory_type(SPDMemoryType type, i2c_smbus_interface *bus, uint8_t address);
|
||||||
|
|
||||||
|
virtual SPDMemoryType memory_type() = 0;
|
||||||
|
virtual uint16_t jedec_id() = 0;
|
||||||
|
|
||||||
|
virtual SPDAccessor *copy() = 0;
|
||||||
|
|
||||||
|
virtual uint8_t at(uint16_t addr) = 0;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
i2c_smbus_interface *bus;
|
||||||
|
uint8_t address;
|
||||||
|
};
|
||||||
|
|
||||||
|
/*---------------------------------------------------------*\
|
||||||
|
| Internal implementation for specific memory type. |
|
||||||
|
\*---------------------------------------------------------*/
|
||||||
|
|
||||||
|
class DDR4Accessor : public SPDAccessor
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
DDR4Accessor(i2c_smbus_interface *bus, uint8_t address);
|
||||||
|
virtual ~DDR4Accessor();
|
||||||
|
virtual SPDMemoryType memory_type();
|
||||||
|
virtual uint16_t jedec_id();
|
||||||
|
};
|
||||||
|
|
||||||
|
class DDR5Accessor : public SPDAccessor
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
DDR5Accessor(i2c_smbus_interface *bus, uint8_t address);
|
||||||
|
virtual ~DDR5Accessor();
|
||||||
|
virtual SPDMemoryType memory_type();
|
||||||
|
virtual uint16_t jedec_id();
|
||||||
|
};
|
||||||
53
SPDAccessor/SPDCommon.h
Normal file
53
SPDAccessor/SPDCommon.h
Normal file
|
|
@ -0,0 +1,53 @@
|
||||||
|
/*---------------------------------------------------------*\
|
||||||
|
| SPDCommon.h |
|
||||||
|
| |
|
||||||
|
| Common definitions for SPD |
|
||||||
|
| |
|
||||||
|
| This file is part of the OpenRGB project |
|
||||||
|
| SPDX-License-Identifier: GPL-2.0-only |
|
||||||
|
\*---------------------------------------------------------*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include "i2c_smbus.h"
|
||||||
|
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
JEDEC_KINGSTON = 0x0117,
|
||||||
|
JEDEC_CORSAIR = 0x021D,
|
||||||
|
JEDEC_ADATA = 0x044A,
|
||||||
|
JEDEC_GSKILL = 0x044C,
|
||||||
|
JEDEC_TEAMGROUP = 0x046E,
|
||||||
|
JEDEC_MUSHKIN = 0x8313,
|
||||||
|
JEDEC_GIGABYTE = 0x8971,
|
||||||
|
JEDEC_THERMALTAKE = 0x8A41
|
||||||
|
} JedecIdentifier;
|
||||||
|
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
SPD_RESERVED = 0,
|
||||||
|
SPD_FPM_DRAM = 1,
|
||||||
|
SPD_EDO = 2,
|
||||||
|
SPD_NIBBLE = 3,
|
||||||
|
SPD_SDR_SDRAM = 4,
|
||||||
|
SPD_MUX_ROM = 5,
|
||||||
|
SPD_DDR_SGRAM = 6,
|
||||||
|
SPD_DDR_SDRAM = 7,
|
||||||
|
SPD_DDR2_SDRAM = 8,
|
||||||
|
SPD_FB_DIMM = 9,
|
||||||
|
SPD_FB_PROBE = 10,
|
||||||
|
SPD_DDR3_SDRAM = 11,
|
||||||
|
SPD_DDR4_SDRAM = 12,
|
||||||
|
SPD_RESERVED2 = 13,
|
||||||
|
SPD_DDR4E_SDRAM = 14,
|
||||||
|
SPD_LPDDR3_SDRAM = 15,
|
||||||
|
SPD_LPDDR4_SDRAM = 16,
|
||||||
|
SPD_LPDDR4X_SDRAM = 17,
|
||||||
|
SPD_DDR5_SDRAM = 18,
|
||||||
|
SPD_LPDDR5_SDRAM = 19
|
||||||
|
} SPDMemoryType;
|
||||||
|
|
||||||
|
#define SPD_IO_DELAY 1ms
|
||||||
|
|
||||||
|
extern const char *spd_memory_type_name[];
|
||||||
137
SPDAccessor/SPDDetector.cpp
Normal file
137
SPDAccessor/SPDDetector.cpp
Normal file
|
|
@ -0,0 +1,137 @@
|
||||||
|
/*---------------------------------------------------------*\
|
||||||
|
| SPDDetector.cpp |
|
||||||
|
| |
|
||||||
|
| Detector for DRAM modules using SPD information |
|
||||||
|
| |
|
||||||
|
| This file is part of the OpenRGB project |
|
||||||
|
| SPDX-License-Identifier: GPL-2.0-only |
|
||||||
|
\*---------------------------------------------------------*/
|
||||||
|
|
||||||
|
#include "DDR4DirectAccessor.h"
|
||||||
|
#include "DDR5DirectAccessor.h"
|
||||||
|
#include "LogManager.h"
|
||||||
|
#include "SPDDetector.h"
|
||||||
|
|
||||||
|
#ifdef __linux__
|
||||||
|
#include "EE1004Accessor_Linux.h"
|
||||||
|
#include "SPD5118Accessor_Linux.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
SPDDetector::SPDDetector(i2c_smbus_interface *bus, uint8_t address, SPDMemoryType mem_type = SPD_RESERVED)
|
||||||
|
: bus(bus), address(address), mem_type(mem_type), valid(false)
|
||||||
|
{
|
||||||
|
detect_memory_type();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SPDDetector::is_valid() const
|
||||||
|
{
|
||||||
|
return(valid);
|
||||||
|
}
|
||||||
|
|
||||||
|
SPDMemoryType SPDDetector::memory_type() const
|
||||||
|
{
|
||||||
|
return(mem_type);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SPDDetector::detect_memory_type()
|
||||||
|
{
|
||||||
|
SPDAccessor *accessor;
|
||||||
|
|
||||||
|
LOG_DEBUG("Probing DRAM on address 0x%02x", address);
|
||||||
|
|
||||||
|
/*---------------------------------------------------------*\
|
||||||
|
| On Linux, attempt to use the ee1004 or spd5118 drivers to |
|
||||||
|
| access SPD on DDR4 and DDR5, respectively |
|
||||||
|
\*---------------------------------------------------------*/
|
||||||
|
#ifdef __linux__
|
||||||
|
if(EE1004Accessor::isAvailable(bus, address))
|
||||||
|
{
|
||||||
|
accessor = new EE1004Accessor(bus, address);
|
||||||
|
}
|
||||||
|
else if(SPD5118Accessor::isAvailable(bus, address))
|
||||||
|
{
|
||||||
|
accessor = new SPD5118Accessor(bus, address);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
#endif
|
||||||
|
/*---------------------------------------------------------*\
|
||||||
|
| Otherwise, access the SPD using a direct accessor using |
|
||||||
|
| i2c for DDR4 and DDR5 |
|
||||||
|
\*---------------------------------------------------------*/
|
||||||
|
if((mem_type == SPD_RESERVED
|
||||||
|
|| mem_type == SPD_DDR4_SDRAM
|
||||||
|
|| mem_type == SPD_DDR4E_SDRAM
|
||||||
|
|| mem_type == SPD_LPDDR4_SDRAM
|
||||||
|
|| mem_type == SPD_LPDDR4X_SDRAM)
|
||||||
|
&& DDR4DirectAccessor::isAvailable(bus, address))
|
||||||
|
{
|
||||||
|
accessor = new DDR4DirectAccessor(bus, address);
|
||||||
|
}
|
||||||
|
else if((mem_type == SPD_RESERVED
|
||||||
|
|| mem_type == SPD_DDR5_SDRAM
|
||||||
|
|| mem_type == SPD_LPDDR5_SDRAM)
|
||||||
|
&& DDR5DirectAccessor::isAvailable(bus, address))
|
||||||
|
{
|
||||||
|
accessor = new DDR5DirectAccessor(bus, address);
|
||||||
|
}
|
||||||
|
/*---------------------------------------------------------*\
|
||||||
|
| Otherwise, probe the SPD directly using i2c, probably an |
|
||||||
|
| older system than DDR4 |
|
||||||
|
\*---------------------------------------------------------*/
|
||||||
|
else if(mem_type == SPD_RESERVED)
|
||||||
|
{
|
||||||
|
LOG_TRACE("Probing memory type older than DDR4");
|
||||||
|
|
||||||
|
int value = bus->i2c_smbus_read_byte_data(address, 0x02);
|
||||||
|
|
||||||
|
if(value < 0)
|
||||||
|
{
|
||||||
|
valid = false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mem_type = (SPDMemoryType)value;
|
||||||
|
|
||||||
|
/*-------------------------------------------------*\
|
||||||
|
| We are only interested in DDR4 and DDR5 systems |
|
||||||
|
\*-------------------------------------------------*/
|
||||||
|
valid = (mem_type == SPD_DDR4_SDRAM
|
||||||
|
|| mem_type == SPD_DDR4E_SDRAM
|
||||||
|
|| mem_type == SPD_LPDDR4_SDRAM
|
||||||
|
|| mem_type == SPD_LPDDR4X_SDRAM
|
||||||
|
|| mem_type == SPD_DDR5_SDRAM
|
||||||
|
|| mem_type == SPD_LPDDR5_SDRAM);
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
/*---------------------------------------------------------*\
|
||||||
|
| If memory type could not be determined, detection failed |
|
||||||
|
\*---------------------------------------------------------*/
|
||||||
|
else
|
||||||
|
{
|
||||||
|
valid = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*---------------------------------------------------------*\
|
||||||
|
| If an accessor was created, save the memory type |
|
||||||
|
\*---------------------------------------------------------*/
|
||||||
|
valid = true;
|
||||||
|
mem_type = accessor->memory_type();
|
||||||
|
|
||||||
|
/*---------------------------------------------------------*\
|
||||||
|
| Delete the accessor |
|
||||||
|
\*---------------------------------------------------------*/
|
||||||
|
delete accessor;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t SPDDetector::spd_address() const
|
||||||
|
{
|
||||||
|
return(this->address);
|
||||||
|
}
|
||||||
|
|
||||||
|
i2c_smbus_interface *SPDDetector::smbus() const
|
||||||
|
{
|
||||||
|
return(this->bus);
|
||||||
|
}
|
||||||
32
SPDAccessor/SPDDetector.h
Normal file
32
SPDAccessor/SPDDetector.h
Normal file
|
|
@ -0,0 +1,32 @@
|
||||||
|
/*---------------------------------------------------------*\
|
||||||
|
| SPDDetector.h |
|
||||||
|
| |
|
||||||
|
| Detector for DRAM modules using SPD information |
|
||||||
|
| |
|
||||||
|
| This file is part of the OpenRGB project |
|
||||||
|
| SPDX-License-Identifier: GPL-2.0-only |
|
||||||
|
\*---------------------------------------------------------*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "SPDAccessor.h"
|
||||||
|
#include "SPDCommon.h"
|
||||||
|
|
||||||
|
class SPDDetector
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
SPDDetector(i2c_smbus_interface *bus, uint8_t address, SPDMemoryType mem_type);
|
||||||
|
|
||||||
|
bool is_valid() const;
|
||||||
|
SPDMemoryType memory_type() const;
|
||||||
|
uint8_t spd_address() const;
|
||||||
|
i2c_smbus_interface *smbus() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
i2c_smbus_interface *bus;
|
||||||
|
uint8_t address;
|
||||||
|
SPDMemoryType mem_type;
|
||||||
|
bool valid;
|
||||||
|
|
||||||
|
void detect_memory_type();
|
||||||
|
};
|
||||||
84
SPDAccessor/SPDWrapper.cpp
Normal file
84
SPDAccessor/SPDWrapper.cpp
Normal file
|
|
@ -0,0 +1,84 @@
|
||||||
|
/*---------------------------------------------------------*\
|
||||||
|
| SPDWrapper.cpp |
|
||||||
|
| |
|
||||||
|
| Wrapper for DRAM modules using SPD information |
|
||||||
|
| |
|
||||||
|
| This file is part of the OpenRGB project |
|
||||||
|
| SPDX-License-Identifier: GPL-2.0-only |
|
||||||
|
\*---------------------------------------------------------*/
|
||||||
|
|
||||||
|
#include "SPDWrapper.h"
|
||||||
|
|
||||||
|
SPDWrapper::SPDWrapper(const SPDWrapper &wrapper)
|
||||||
|
{
|
||||||
|
if(wrapper.accessor != nullptr)
|
||||||
|
{
|
||||||
|
this->accessor = wrapper.accessor->copy();
|
||||||
|
}
|
||||||
|
this->address = wrapper.address;
|
||||||
|
this->mem_type = wrapper.mem_type;
|
||||||
|
}
|
||||||
|
|
||||||
|
SPDWrapper::SPDWrapper(const SPDDetector &detector)
|
||||||
|
{
|
||||||
|
this->address = detector.spd_address();
|
||||||
|
this->mem_type = detector.memory_type();
|
||||||
|
|
||||||
|
// Allocate a new accessor
|
||||||
|
this->accessor = SPDAccessor::for_memory_type(this->mem_type, detector.smbus(), this->address);
|
||||||
|
}
|
||||||
|
|
||||||
|
SPDWrapper::~SPDWrapper()
|
||||||
|
{
|
||||||
|
delete accessor;
|
||||||
|
}
|
||||||
|
|
||||||
|
SPDMemoryType SPDWrapper::memory_type()
|
||||||
|
{
|
||||||
|
return mem_type;
|
||||||
|
}
|
||||||
|
|
||||||
|
int SPDWrapper::index()
|
||||||
|
{
|
||||||
|
return this->address - 0x50;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t SPDWrapper::jedec_id()
|
||||||
|
{
|
||||||
|
if(accessor == nullptr)
|
||||||
|
{
|
||||||
|
return 0x0000;
|
||||||
|
}
|
||||||
|
return accessor->jedec_id();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------------*\
|
||||||
|
| Helper functions for easier collection handling. |
|
||||||
|
\*-------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
bool is_jedec_in_slots(std::vector<SPDWrapper> &slots, uint16_t jedec_id)
|
||||||
|
{
|
||||||
|
for(SPDWrapper &slot : slots)
|
||||||
|
{
|
||||||
|
if(slot.jedec_id() == jedec_id)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<SPDWrapper*> slots_with_jedec(std::vector<SPDWrapper> &slots, uint16_t jedec_id)
|
||||||
|
{
|
||||||
|
std::vector<SPDWrapper*> matching_slots;
|
||||||
|
|
||||||
|
for(SPDWrapper &slot : slots)
|
||||||
|
{
|
||||||
|
if(slot.jedec_id() == jedec_id)
|
||||||
|
{
|
||||||
|
matching_slots.push_back(&slot);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return matching_slots;
|
||||||
|
}
|
||||||
38
SPDAccessor/SPDWrapper.h
Normal file
38
SPDAccessor/SPDWrapper.h
Normal file
|
|
@ -0,0 +1,38 @@
|
||||||
|
/*---------------------------------------------------------*\
|
||||||
|
| SPDWrapper.h |
|
||||||
|
| |
|
||||||
|
| Wrapper for DRAM modules using SPD information |
|
||||||
|
| |
|
||||||
|
| This file is part of the OpenRGB project |
|
||||||
|
| SPDX-License-Identifier: GPL-2.0-only |
|
||||||
|
\*---------------------------------------------------------*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "SPDAccessor.h"
|
||||||
|
#include "SPDCommon.h"
|
||||||
|
#include "SPDDetector.h"
|
||||||
|
|
||||||
|
class SPDWrapper
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
SPDWrapper(const SPDWrapper &wrapper);
|
||||||
|
SPDWrapper(const SPDDetector &detector);
|
||||||
|
~SPDWrapper();
|
||||||
|
|
||||||
|
SPDMemoryType memory_type();
|
||||||
|
int index();
|
||||||
|
uint16_t jedec_id();
|
||||||
|
|
||||||
|
private:
|
||||||
|
SPDAccessor *accessor = nullptr;
|
||||||
|
uint8_t address;
|
||||||
|
SPDMemoryType mem_type;
|
||||||
|
};
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------------*\
|
||||||
|
| Helper functions for easier collection handling. |
|
||||||
|
\*-------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
bool is_jedec_in_slots(std::vector<SPDWrapper> &slots, uint16_t jedec_id);
|
||||||
|
std::vector<SPDWrapper*> slots_with_jedec(std::vector<SPDWrapper> &slots, uint16_t jedec_id);
|
||||||
Loading…
Add table
Add a link
Reference in a new issue