Publish LumaOps source

This commit is contained in:
LumaOps release export
2026-09-03 01:18:36 +02:00
commit 7f1c0e5f71
2363 changed files with 501543 additions and 0 deletions
+94
View File
@@ -0,0 +1,94 @@
/*---------------------------------------------------------*\
| DDR4DirectAccessor.cpp |
| |
| DDR4 SPD accessor implementation using direct i2c |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#include <chrono>
#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)
{
/*-----------------------------------------------------*\
| Select low page |
\*-----------------------------------------------------*/
bus->i2c_smbus_write_byte(0x36, 0x00);
std::this_thread::sleep_for(SPD_IO_DELAY);
/*-----------------------------------------------------*\
| Read value at address 0 in SPD device |
\*-----------------------------------------------------*/
s32 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
View 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-or-later |
\*---------------------------------------------------------*/
#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
View 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-or-later |
\*---------------------------------------------------------*/
#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("[DDR5DirectAccessor] 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("[DDR5DirectAccessor] 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
View 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-or-later |
\*---------------------------------------------------------*/
#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);
};
+72
View File
@@ -0,0 +1,72 @@
/*---------------------------------------------------------*\
| 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-or-later |
\*---------------------------------------------------------*/
#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->bus_id, spd_addr);
char *path = new char[size+1];
snprintf(path, size+1, SPD_EE1004_PATH, bus->bus_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();
}
// Prevent indexing out of bounds
if(addr >= sizeof(dump))
{
return 0xFF;
}
return dump[addr];
}
void EE1004Accessor::readEEPROM()
{
int size = snprintf(nullptr, 0, SPD_EE1004_PATH, bus->bus_id, address);
char *filename = new char[size+1];
snprintf(filename, size+1, SPD_EE1004_PATH, bus->bus_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
View 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-or-later |
\*---------------------------------------------------------*/
#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();
};
+73
View File
@@ -0,0 +1,73 @@
/*---------------------------------------------------------*\
| 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-or-later |
\*---------------------------------------------------------*/
#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->bus_id, spd_addr);
char *path = new char[size+1];
snprintf(path, size+1, SPD_SPD5118_PATH, bus->bus_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();
}
// Prevent indexing out of bounds
if(addr >= sizeof(dump))
{
return 0xFF;
}
return dump[addr];
}
void SPD5118Accessor::readEEPROM()
{
int size = snprintf(nullptr, 0, SPD_SPD5118_PATH, bus->bus_id, address);
char *filename = new char[size+1];
snprintf(filename, size+1, SPD_SPD5118_PATH, bus->bus_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
View 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-or-later |
\*---------------------------------------------------------*/
#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();
};
+218
View File
@@ -0,0 +1,218 @@
/*---------------------------------------------------------*\
| 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-or-later |
\*---------------------------------------------------------*/
#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;
/*---------------------------------------------------------*\
| Sources for define values: |
| - https://en.wikipedia.org/wiki/Serial_presence_detect |
| - JEDEC DDR5 Serial Presence Detect (SPD): Table of |
| contents |
\*---------------------------------------------------------*/
#define BASIC_MEMORY_TYPE_ADDR (0x02)
#define DDR4_JEDEC_ID_ADDR (0x140)
#define DDR4_PART_NR_START (0x149)
#define DDR4_PART_NR_END (0x15C)
#define DDR4_PART_NR_LEN (DDR4_PART_NR_END - DDR4_PART_NR_START + 1)
#define DDR4_MANUF_SPECIFIC_START (0x161)
#define DDR4_MANUF_SPECIFIC_END (0x17D)
#define DDR4_MANUF_SPECIFIC_LEN (DDR4_MANUF_SPECIFIC_END - DDR4_MANUF_SPECIFIC_START + 1)
#define DDR5_JEDEC_ID_ADDR (0x200)
#define DDR5_PART_NR_START (0x209)
#define DDR5_PART_NR_END (0x226)
#define DDR5_PART_NR_LEN (DDR5_PART_NR_END - DDR5_PART_NR_START + 1)
#define DDR5_MANUF_SPECIFIC_START (0x22B)
#define DDR5_MANUF_SPECIFIC_END (0x27F)
#define DDR5_MANUF_SPECIFIC_LEN (DDR5_MANUF_SPECIFIC_END - DDR5_MANUF_SPECIFIC_START + 1)
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);
};
std::string SPDAccessor::read_part_nr_at(uint16_t address, std::size_t len)
{
std::string part_number;
for(uint16_t i = 0; i < (uint16_t)len; i++)
{
uint16_t spd_addr = address + i;
part_number += (char)this->at(spd_addr);
}
/*-----------------------------------------------------*\
| Find the true end of string and truncate it to that |
| point. Part number should be padded with 0x20 |
| (space) for DDR4 (Source: Wikipedia). |
| It may be padded with 0x00 (Source: real-life tests |
| on DDR5 memory). |
| Note: To prevent infinite loop, end_of_string_idx |
| MUST be signed. |
\*-----------------------------------------------------*/
std::size_t end_of_string_idx = part_number.length();
for(; end_of_string_idx > 0; end_of_string_idx--)
{
if((part_number[end_of_string_idx - 1] != '\0')
&& (part_number[end_of_string_idx - 1] != ' '))
{
break;
}
}
part_number = part_number.substr(0, end_of_string_idx + 1);
return part_number;
}
/*---------------------------------------------------------*\
| 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(BASIC_MEMORY_TYPE_ADDR)));
}
uint16_t DDR4Accessor::jedec_id()
{
return((this->at(DDR4_JEDEC_ID_ADDR) << 8) + (this->at(DDR4_JEDEC_ID_ADDR+1) & 0x7f) - 1);
}
std::string DDR4Accessor::part_number()
{
return this->read_part_nr_at(DDR4_PART_NR_START, DDR4_PART_NR_LEN);
}
uint8_t DDR4Accessor::manufacturer_data(uint16_t index)
{
if(index > DDR4_MANUF_SPECIFIC_LEN-1)
{
return 0;
}
return this->at(DDR4_MANUF_SPECIFIC_START + index);
}
DDR5Accessor::DDR5Accessor(i2c_smbus_interface *bus, uint8_t spd_addr)
: SPDAccessor(bus, spd_addr)
{
}
DDR5Accessor::~DDR5Accessor()
{
}
SPDMemoryType DDR5Accessor::memory_type()
{
return((SPDMemoryType)(this->at(BASIC_MEMORY_TYPE_ADDR)));
}
uint16_t DDR5Accessor::jedec_id()
{
return((this->at(DDR5_JEDEC_ID_ADDR) << 8) + (this->at(DDR5_JEDEC_ID_ADDR+1) & 0x7f) - 1);
}
std::string DDR5Accessor::part_number()
{
return this->read_part_nr_at(DDR5_PART_NR_START, DDR5_PART_NR_LEN);
}
uint8_t DDR5Accessor::manufacturer_data(uint16_t index)
{
if(index > DDR5_MANUF_SPECIFIC_LEN-1)
{
return 0;
}
return this->at(DDR5_MANUF_SPECIFIC_START + index);
}
+66
View File
@@ -0,0 +1,66 @@
/*---------------------------------------------------------*\
| SPDAccessor.h |
| |
| Access to SPD information on various DIMMs |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#pragma once
#include "SPDCommon.h"
#include <cstddef>
#include <cstdint>
#include <string>
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 std::string part_number() = 0;
virtual uint8_t manufacturer_data(uint16_t index) = 0;
virtual SPDAccessor *copy() = 0;
virtual uint8_t at(uint16_t addr) = 0;
protected:
i2c_smbus_interface *bus;
uint8_t address;
std::string read_part_nr_at(uint16_t address, std::size_t len);
};
/*---------------------------------------------------------*\
| 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();
virtual std::string part_number();
virtual uint8_t manufacturer_data(uint16_t index);
};
class DDR5Accessor : public SPDAccessor
{
public:
DDR5Accessor(i2c_smbus_interface *bus, uint8_t address);
virtual ~DDR5Accessor();
virtual SPDMemoryType memory_type();
virtual uint16_t jedec_id();
virtual std::string part_number();
virtual uint8_t manufacturer_data(uint16_t index);
};
+56
View File
@@ -0,0 +1,56 @@
/*---------------------------------------------------------*\
| SPDCommon.h |
| |
| Common definitions for SPD |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#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_KINGSTON_2 = 0x300F,
JEDEC_KINGSTON_3 = 0x3011,
JEDEC_MUSHKIN = 0x8313,
JEDEC_GIGABYTE = 0x8971,
JEDEC_THERMALTAKE = 0x8A41,
JEDEC_PATRIOT = 0x8501
} 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[];
+140
View File
@@ -0,0 +1,140 @@
/*---------------------------------------------------------*\
| SPDDetector.cpp |
| |
| Detector for DRAM modules using SPD information |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#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;
/*---------------------------------------------------------*\
| 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))
{
LOG_DEBUG("[SPDDetector] Probing DRAM using EE1004 Accessor on bus %d, address 0x%02x", bus->bus_id, address);
accessor = new EE1004Accessor(bus, address);
}
else if(SPD5118Accessor::isAvailable(bus, address))
{
LOG_DEBUG("[SPDDetector] Probing DRAM using SPD5118 Accessor on bus %d, address 0x%02x", bus->bus_id, 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))
{
LOG_DEBUG("[SPDDetector] Probing DRAM using DDR4 Direct Accessor on bus %d, address 0x%02x", bus->bus_id, 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))
{
LOG_DEBUG("[SPDDetector] Probing DRAM using DDR5 Direct Accessor on bus %d, address 0x%02x", bus->bus_id, 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_DEBUG("[SPDDetector] Probing DRAM older than DDR4 on bus %d, address 0x%02x", bus->bus_id, address);
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
{
LOG_DEBUG("[SPDDetector] Memory type could not be determined for bus %d, address 0x%02x", bus->bus_id, address);
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
View 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-or-later |
\*---------------------------------------------------------*/
#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();
};
+141
View File
@@ -0,0 +1,141 @@
/*---------------------------------------------------------*\
| SPDWrapper.cpp |
| |
| Wrapper for DRAM modules using SPD information |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#include "SPDWrapper.h"
SPDWrapper::SPDWrapper(const SPDWrapper &wrapper)
{
if(wrapper.accessor != nullptr)
{
this->accessor = wrapper.accessor->copy();
}
this->addr = wrapper.addr;
this->mem_type = wrapper.mem_type;
/*-----------------------------------------------------*\
| Read the JEDEC ID and cache its value |
| This saves a significant amount of time over reading |
| the JEDEC ID each time it is accessed |
\*-----------------------------------------------------*/
if(accessor == nullptr)
{
jedec_id_val = 0x0000;
}
else
{
jedec_id_val = accessor->jedec_id();
}
}
SPDWrapper::SPDWrapper(const SPDDetector &detector)
{
this->addr = 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->addr);
/*-----------------------------------------------------*\
| Read the JEDEC ID and cache its value |
| This saves a significant amount of time over reading |
| the JEDEC ID each time it is accessed |
\*-----------------------------------------------------*/
if(accessor == nullptr)
{
jedec_id_val = 0x0000;
}
else
{
jedec_id_val = accessor->jedec_id();
}
}
SPDWrapper::~SPDWrapper()
{
delete accessor;
}
SPDMemoryType SPDWrapper::memory_type()
{
return mem_type;
}
uint8_t SPDWrapper::address()
{
return this->addr;
}
int SPDWrapper::index()
{
return this->addr - 0x50;
}
uint16_t SPDWrapper::jedec_id()
{
return jedec_id_val;
}
std::string SPDWrapper::part_number()
{
if(accessor == nullptr)
{
return std::string();
}
return accessor->part_number();
}
uint8_t SPDWrapper::manufacturer_data(uint16_t index)
{
if(accessor == nullptr)
{
return 0x00;
}
return accessor->manufacturer_data(index);
}
/*---------------------------------------------------------*\
| Helper functions for easier collection handling. |
\*---------------------------------------------------------*/
bool is_jedec_in_slots(std::vector<SPDWrapper> &slots, uint16_t jedec_id)
{
/*-----------------------------------------------------*\
| Search through all SPD slots to see if any have the |
| desired 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;
/*-----------------------------------------------------*\
| Search through all SPD slots and build a list of all |
| slots matching the desired JEDEC ID |
\*-----------------------------------------------------*/
for(SPDWrapper &slot : slots)
{
if(slot.jedec_id() == jedec_id)
{
matching_slots.push_back(&slot);
}
}
return matching_slots;
}
+42
View File
@@ -0,0 +1,42 @@
/*---------------------------------------------------------*\
| SPDWrapper.h |
| |
| Wrapper for DRAM modules using SPD information |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#pragma once
#include "SPDAccessor.h"
#include "SPDCommon.h"
#include "SPDDetector.h"
class SPDWrapper
{
public:
SPDWrapper(const SPDWrapper &wrapper);
SPDWrapper(const SPDDetector &detector);
~SPDWrapper();
uint8_t address();
SPDMemoryType memory_type();
int index();
uint16_t jedec_id();
std::string part_number();
uint8_t manufacturer_data(uint16_t index);
private:
SPDAccessor *accessor = nullptr;
uint8_t addr;
uint16_t jedec_id_val;
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);