diff --git a/OpenRGB.pro b/OpenRGB.pro index 3d938d61..9807859a 100644 --- a/OpenRGB.pro +++ b/OpenRGB.pro @@ -123,6 +123,7 @@ INCLUDEPATH += KeyboardLayoutManager/ \ RGBController/ \ qt/ \ + SPDAccessor/ \ SuspendResume/ HEADERS += \ @@ -208,7 +209,11 @@ SOURCES += PluginManager.cpp \ ProfileManager.cpp \ ResourceManager.cpp \ - SPDAccessor.cpp \ + SPDAccessor/DDR4DirectAccessor.cpp \ + SPDAccessor/DDR5DirectAccessor.cpp \ + SPDAccessor/SPDAccessor.cpp \ + SPDAccessor/SPDDetector.cpp \ + SPDAccessor/SPDWrapper.cpp \ SettingsManager.cpp \ i2c_smbus/i2c_smbus.cpp \ i2c_tools/i2c_tools.cpp \ @@ -504,6 +509,8 @@ contains(QMAKE_PLATFORM, linux) { dependencies/NVFC/nvapi.h \ i2c_smbus/i2c_smbus_linux.h \ AutoStart/AutoStart-Linux.h \ + SPDAccessor/EE1004Accessor_Linux.h \ + SPDAccessor/SPD5118Accessor_Linux.h \ SuspendResume/SuspendResume_Linux_FreeBSD.h \ INCLUDEPATH += \ @@ -558,6 +565,8 @@ contains(QMAKE_PLATFORM, linux) { scsiapi/scsiapi_linux.c \ serial_port/find_usb_serial_port_linux.cpp \ AutoStart/AutoStart-Linux.cpp \ + SPDAccessor/EE1004Accessor_Linux.cpp \ + SPDAccessor/SPD5118Accessor_Linux.cpp \ SuspendResume/SuspendResume_Linux_FreeBSD.cpp \ #-------------------------------------------------------------------------------------------# diff --git a/ResourceManager.h b/ResourceManager.h index 3b2956c6..df4ac6ff 100644 --- a/ResourceManager.h +++ b/ResourceManager.h @@ -19,7 +19,7 @@ #include #include #include -#include "SPDAccessor.h" +#include "SPDWrapper.h" #include "hidapi_wrapper.h" #include "i2c_smbus.h" #include "filesystem.h" diff --git a/SPDAccessor.cpp b/SPDAccessor.cpp deleted file mode 100644 index e400e60e..00000000 --- a/SPDAccessor.cpp +++ /dev/null @@ -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 &slots, uint16_t jedec_id) -{ - for(SPDWrapper &slot : slots) - { - if(slot.jedec_id() == jedec_id) - { - return true; - } - } - return false; -} - -std::vector slots_with_jedec(std::vector &slots, uint16_t jedec_id) -{ - std::vector 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 diff --git a/SPDAccessor.h b/SPDAccessor.h deleted file mode 100644 index 29399aca..00000000 --- a/SPDAccessor.h +++ /dev/null @@ -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 -#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 &slots, uint16_t jedec_id); -std::vector slots_with_jedec(std::vector &slots, uint16_t jedec_id); diff --git a/SPDAccessor/DDR4DirectAccessor.cpp b/SPDAccessor/DDR4DirectAccessor.cpp new file mode 100644 index 00000000..585db6aa --- /dev/null +++ b/SPDAccessor/DDR4DirectAccessor.cpp @@ -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); + } +} diff --git a/SPDAccessor/DDR4DirectAccessor.h b/SPDAccessor/DDR4DirectAccessor.h new file mode 100644 index 00000000..f429ceff --- /dev/null +++ b/SPDAccessor/DDR4DirectAccessor.h @@ -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); +}; diff --git a/SPDAccessor/DDR5DirectAccessor.cpp b/SPDAccessor/DDR5DirectAccessor.cpp new file mode 100644 index 00000000..341e2df7 --- /dev/null +++ b/SPDAccessor/DDR5DirectAccessor.cpp @@ -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); + } +} diff --git a/SPDAccessor/DDR5DirectAccessor.h b/SPDAccessor/DDR5DirectAccessor.h new file mode 100644 index 00000000..0f3e4732 --- /dev/null +++ b/SPDAccessor/DDR5DirectAccessor.h @@ -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); +}; diff --git a/SPDAccessor/EE1004Accessor_Linux.cpp b/SPDAccessor/EE1004Accessor_Linux.cpp new file mode 100644 index 00000000..af314048 --- /dev/null +++ b/SPDAccessor/EE1004Accessor_Linux.cpp @@ -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 +#include +#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; +} diff --git a/SPDAccessor/EE1004Accessor_Linux.h b/SPDAccessor/EE1004Accessor_Linux.h new file mode 100644 index 00000000..7347cc2f --- /dev/null +++ b/SPDAccessor/EE1004Accessor_Linux.h @@ -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(); +}; diff --git a/SPDAccessor/SPD5118Accessor_Linux.cpp b/SPDAccessor/SPD5118Accessor_Linux.cpp new file mode 100644 index 00000000..5fc44d0e --- /dev/null +++ b/SPDAccessor/SPD5118Accessor_Linux.cpp @@ -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 +#include +#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; +} diff --git a/SPDAccessor/SPD5118Accessor_Linux.h b/SPDAccessor/SPD5118Accessor_Linux.h new file mode 100644 index 00000000..93fca3a4 --- /dev/null +++ b/SPDAccessor/SPD5118Accessor_Linux.h @@ -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(); +}; diff --git a/SPDAccessor/SPDAccessor.cpp b/SPDAccessor/SPDAccessor.cpp new file mode 100644 index 00000000..d56c981c --- /dev/null +++ b/SPDAccessor/SPDAccessor.cpp @@ -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); +} diff --git a/SPDAccessor/SPDAccessor.h b/SPDAccessor/SPDAccessor.h new file mode 100644 index 00000000..47135398 --- /dev/null +++ b/SPDAccessor/SPDAccessor.h @@ -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(); +}; diff --git a/SPDAccessor/SPDCommon.h b/SPDAccessor/SPDCommon.h new file mode 100644 index 00000000..66c14704 --- /dev/null +++ b/SPDAccessor/SPDCommon.h @@ -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 +#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[]; diff --git a/SPDAccessor/SPDDetector.cpp b/SPDAccessor/SPDDetector.cpp new file mode 100644 index 00000000..9d1b72b2 --- /dev/null +++ b/SPDAccessor/SPDDetector.cpp @@ -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); +} diff --git a/SPDAccessor/SPDDetector.h b/SPDAccessor/SPDDetector.h new file mode 100644 index 00000000..f3cebbbd --- /dev/null +++ b/SPDAccessor/SPDDetector.h @@ -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(); +}; diff --git a/SPDAccessor/SPDWrapper.cpp b/SPDAccessor/SPDWrapper.cpp new file mode 100644 index 00000000..07e3cd01 --- /dev/null +++ b/SPDAccessor/SPDWrapper.cpp @@ -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 &slots, uint16_t jedec_id) +{ + for(SPDWrapper &slot : slots) + { + if(slot.jedec_id() == jedec_id) + { + return true; + } + } + return false; +} + +std::vector slots_with_jedec(std::vector &slots, uint16_t jedec_id) +{ + std::vector matching_slots; + + for(SPDWrapper &slot : slots) + { + if(slot.jedec_id() == jedec_id) + { + matching_slots.push_back(&slot); + } + } + + return matching_slots; +} diff --git a/SPDAccessor/SPDWrapper.h b/SPDAccessor/SPDWrapper.h new file mode 100644 index 00000000..f6a30afe --- /dev/null +++ b/SPDAccessor/SPDWrapper.h @@ -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 &slots, uint16_t jedec_id); +std::vector slots_with_jedec(std::vector &slots, uint16_t jedec_id);