mirror of
https://github.com/CalcProgrammer1/OpenRGB.git
synced 2025-12-23 23:37:48 -05:00
Implement common DRAM SPD module with EE1004 and SPD5118 driver support to support detecting DRAM modules using SPD information.
This commit is contained in:
@@ -22,7 +22,6 @@
|
||||
#define FURY_BASE_ADDR_DDR4 0x58
|
||||
#define FURY_BASE_ADDR_DDR5 0x60
|
||||
#define FURY_DELAY std::chrono::milliseconds(10)
|
||||
#define FURY_MAX_SLOTS 8
|
||||
#define FURY_LEDS_PER_DIMM_DDR4 10
|
||||
#define FURY_LEDS_PER_DIMM_DDR5 12
|
||||
#define FURY_MAX_MODE_COLORS 10
|
||||
|
||||
@@ -27,132 +27,6 @@ typedef enum
|
||||
RESULT_ERROR = 2
|
||||
} TestResult;
|
||||
|
||||
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;
|
||||
|
||||
// DDR5 SPD hub detection
|
||||
TestResult TestForSPDHub(i2c_smbus_interface *bus, int spd_address, int &mem_type)
|
||||
{
|
||||
int ddr5Magic = bus->i2c_smbus_read_byte_data(spd_address, 0x00);
|
||||
int ddr5Sensor = bus->i2c_smbus_read_byte_data(spd_address, 0x01);
|
||||
std::this_thread::sleep_for(1ms);
|
||||
|
||||
if(ddr5Magic < 0 || ddr5Sensor < 0)
|
||||
{
|
||||
return RESULT_ERROR;
|
||||
}
|
||||
|
||||
if(ddr5Magic == 0x51 && (ddr5Sensor & 0xEF) == 0x08)
|
||||
{
|
||||
// These values are invalid for any other memory type
|
||||
mem_type = SPD_DDR5_SDRAM;
|
||||
return RESULT_PASS;
|
||||
}
|
||||
return RESULT_FAIL;
|
||||
}
|
||||
|
||||
TestResult TestSPDForKingston(i2c_smbus_interface *bus, SPDMemoryType &fury_type, std::vector<int> &slots)
|
||||
{
|
||||
int mem_type = -1;
|
||||
|
||||
for(int slot_index = 0; slot_index < FURY_MAX_SLOTS; slot_index++)
|
||||
{
|
||||
int spd_address = 0x50 + slot_index;
|
||||
|
||||
TestResult result = TestForSPDHub(bus, spd_address, mem_type);
|
||||
if(result == RESULT_ERROR)
|
||||
{
|
||||
LOG_TRACE("[%s] SPD Hub check [0x%02X] failed. No device.",
|
||||
FURY_CONTROLLER_NAME, spd_address);
|
||||
continue;
|
||||
}
|
||||
|
||||
if(mem_type != SPD_DDR4_SDRAM && mem_type != SPD_DDR5_SDRAM)
|
||||
{
|
||||
// Get memory type from SPD for DDR4 or older
|
||||
bus->i2c_smbus_write_byte_data(0x36, 0x00, 0xFF);
|
||||
std::this_thread::sleep_for(1ms);
|
||||
mem_type = bus->i2c_smbus_read_byte_data(spd_address, 0x02);
|
||||
}
|
||||
|
||||
if(mem_type != SPD_DDR4_SDRAM && mem_type != SPD_DDR5_SDRAM)
|
||||
{
|
||||
LOG_TRACE("[%s] SPD check [0x%02X] - wrong memory type => %02X",
|
||||
FURY_CONTROLLER_NAME, spd_address, mem_type);
|
||||
continue;
|
||||
}
|
||||
fury_type = static_cast<SPDMemoryType>(mem_type);
|
||||
|
||||
LOG_TRACE("[%s] SPD check [0x%02X]: memory type => %02X, ",
|
||||
FURY_CONTROLLER_NAME, spd_address, mem_type);
|
||||
// Switch SPD page
|
||||
if(mem_type == SPD_DDR4_SDRAM)
|
||||
{
|
||||
bus->i2c_smbus_write_byte_data(0x37, 0x00, 0xFF);
|
||||
}
|
||||
else
|
||||
{
|
||||
bus->i2c_smbus_write_byte_data(spd_address, 0x0B, 4);
|
||||
}
|
||||
std::this_thread::sleep_for(1ms);
|
||||
|
||||
// Get ManufacturerID
|
||||
int vendorHi, vendorLo;
|
||||
if(mem_type == SPD_DDR4_SDRAM)
|
||||
{
|
||||
vendorHi = bus->i2c_smbus_read_byte_data(spd_address, 0x40);
|
||||
vendorLo = bus->i2c_smbus_read_byte_data(spd_address, 0x41);
|
||||
}
|
||||
else
|
||||
{
|
||||
vendorHi = bus->i2c_smbus_read_byte_data(spd_address, 0x80);
|
||||
vendorLo = bus->i2c_smbus_read_byte_data(spd_address, 0x81);
|
||||
}
|
||||
std::this_thread::sleep_for(1ms);
|
||||
|
||||
LOG_DEBUG("[%s] SPD check [0x%02X]: vendorHi => %02X, vendorLo => %02X, ",
|
||||
FURY_CONTROLLER_NAME, spd_address, vendorHi, vendorLo);
|
||||
|
||||
// Switch SPD page back to 0
|
||||
if(mem_type == SPD_DDR4_SDRAM)
|
||||
{
|
||||
bus->i2c_smbus_write_byte_data(0x36, 0x00, 0xFF);
|
||||
}
|
||||
else
|
||||
{
|
||||
bus->i2c_smbus_write_byte_data(spd_address, 0x0B, 0);
|
||||
}
|
||||
std::this_thread::sleep_for(1ms);
|
||||
|
||||
if(vendorHi == 0x01 && vendorLo == 0x98)
|
||||
{
|
||||
slots.push_back(slot_index);
|
||||
}
|
||||
}
|
||||
return RESULT_PASS;
|
||||
}
|
||||
|
||||
bool TestDDR4Models(char code)
|
||||
{
|
||||
return (code == FURY_MODEL_BEAST_WHITE_DDR4 ||
|
||||
@@ -173,10 +47,15 @@ TestResult TestForFurySignature(i2c_smbus_interface *bus, unsigned int slot_addr
|
||||
char test_str[] = "FURY";
|
||||
int res;
|
||||
|
||||
LOG_DEBUG("[%s] looking at 0x%02X",
|
||||
FURY_CONTROLLER_NAME, slot_addr);
|
||||
|
||||
// Start transaction
|
||||
res = bus->i2c_smbus_write_byte_data(slot_addr, FURY_REG_APPLY, FURY_BEGIN_TRNSFER);
|
||||
if(res < 0)
|
||||
{
|
||||
LOG_DEBUG("[%s] DIMM not present at 0x%02X",
|
||||
FURY_CONTROLLER_NAME, slot_addr);
|
||||
return RESULT_ERROR;
|
||||
}
|
||||
|
||||
@@ -240,6 +119,41 @@ TestResult TestForFurySignature(i2c_smbus_interface *bus, unsigned int slot_addr
|
||||
return passed ? RESULT_PASS : RESULT_FAIL;
|
||||
}
|
||||
|
||||
void DetectKingstonFuryDRAMControllers(i2c_smbus_interface* bus, std::vector<SPDWrapper*> &slots,
|
||||
uint8_t fury_base_addr, bool (*modelChecker)(char), std::vector<int> &fury_slots)
|
||||
{
|
||||
// Are these the Kingston Fury DRAMs
|
||||
for(SPDWrapper *slot : slots)
|
||||
{
|
||||
TestResult result;
|
||||
int retries = 0;
|
||||
|
||||
result = RESULT_ERROR;
|
||||
while(retries < 3 && result == RESULT_ERROR)
|
||||
{
|
||||
result = TestForFurySignature(bus, fury_base_addr + slot->index(), modelChecker);
|
||||
if(result == RESULT_PASS)
|
||||
{
|
||||
break;
|
||||
}
|
||||
if(result == RESULT_ERROR)
|
||||
{
|
||||
// I/O error - wait for a bit and retry
|
||||
retries++;
|
||||
std::this_thread::sleep_for(FURY_DELAY);
|
||||
}
|
||||
}
|
||||
|
||||
// RAM module successfully detected in the slot 'slot_index'
|
||||
if(result == RESULT_PASS)
|
||||
{
|
||||
LOG_DEBUG("[%s] detected at slot index %d",
|
||||
FURY_CONTROLLER_NAME, slot->index());
|
||||
fury_slots.push_back(slot->index());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/******************************************************************************************\
|
||||
* *
|
||||
* DetectKingstonFuryDRAMControllers *
|
||||
@@ -248,82 +162,39 @@ TestResult TestForFurySignature(i2c_smbus_interface *bus, unsigned int slot_addr
|
||||
* *
|
||||
\******************************************************************************************/
|
||||
|
||||
void DetectKingstonFuryDRAMControllers(std::vector<i2c_smbus_interface*> &busses)
|
||||
void DetectKingstonFuryDDR4Controllers(i2c_smbus_interface* bus, std::vector<SPDWrapper*> &slots)
|
||||
{
|
||||
SPDMemoryType fury_type = SPD_RESERVED;
|
||||
std::vector<int> fury_slots;
|
||||
|
||||
for(unsigned int bus = 0; bus < busses.size(); bus++)
|
||||
DetectKingstonFuryDRAMControllers(bus, slots, FURY_BASE_ADDR_DDR4, TestDDR4Models, fury_slots);
|
||||
|
||||
if(!fury_slots.empty())
|
||||
{
|
||||
IF_DRAM_SMBUS(busses[bus]->pci_vendor, busses[bus]->pci_device)
|
||||
{
|
||||
TestResult result;
|
||||
std::vector<int> occupied_slots, fury_slots;
|
||||
int fury_base_addr;
|
||||
bool (*modelChecker)(char);
|
||||
|
||||
// Do we have Kingston DRAMs?
|
||||
result = TestSPDForKingston(busses[bus], fury_type, occupied_slots);
|
||||
if(result != RESULT_PASS || (fury_type != SPD_DDR4_SDRAM && fury_type != SPD_DDR5_SDRAM))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if(fury_type == SPD_DDR4_SDRAM)
|
||||
{
|
||||
fury_base_addr = FURY_BASE_ADDR_DDR4;
|
||||
modelChecker = TestDDR4Models;
|
||||
}
|
||||
else
|
||||
{
|
||||
fury_base_addr = FURY_BASE_ADDR_DDR5;
|
||||
modelChecker = TestDDR5Models;
|
||||
}
|
||||
|
||||
// Are these the Kingston Fury DRAMs
|
||||
for(int slot_index : occupied_slots)
|
||||
{
|
||||
int retries = 0;
|
||||
result = RESULT_ERROR;
|
||||
|
||||
while(retries < 3 && result == RESULT_ERROR)
|
||||
{
|
||||
result = TestForFurySignature(busses[bus],
|
||||
fury_base_addr + slot_index, modelChecker);
|
||||
if(result == RESULT_PASS)
|
||||
{
|
||||
break;
|
||||
}
|
||||
if(result == RESULT_ERROR)
|
||||
{
|
||||
// I/O error - wait for a bit and retry
|
||||
retries++;
|
||||
std::this_thread::sleep_for(100ms);
|
||||
}
|
||||
}
|
||||
|
||||
// RAM module successfully detected in the slot 'slot_index'
|
||||
if(result == RESULT_PASS)
|
||||
{
|
||||
LOG_DEBUG("[%s] detected at slot index %d",
|
||||
FURY_CONTROLLER_NAME, slot_index);
|
||||
fury_slots.push_back(slot_index);
|
||||
}
|
||||
}
|
||||
|
||||
if(!fury_slots.empty())
|
||||
{
|
||||
unsigned char base_addr =
|
||||
(fury_type == SPD_DDR4_SDRAM) ? FURY_BASE_ADDR_DDR4 : FURY_BASE_ADDR_DDR5;
|
||||
KingstonFuryDRAMController* controller =
|
||||
new KingstonFuryDRAMController(busses[bus], base_addr, fury_slots);
|
||||
RGBController_KingstonFuryDRAM* rgb_controller =
|
||||
new RGBController_KingstonFuryDRAM(controller);
|
||||
rgb_controller->name =
|
||||
(fury_type == SPD_DDR4_SDRAM) ? "Kingston Fury DDR4 RGB" : "Kingston Fury DDR5 RGB";
|
||||
ResourceManager::get()->RegisterRGBController(rgb_controller);
|
||||
}
|
||||
}
|
||||
KingstonFuryDRAMController* controller =
|
||||
new KingstonFuryDRAMController(bus, FURY_BASE_ADDR_DDR4, fury_slots);
|
||||
RGBController_KingstonFuryDRAM* rgb_controller =
|
||||
new RGBController_KingstonFuryDRAM(controller);
|
||||
rgb_controller->name = "Kingston Fury DDR4 RGB";
|
||||
ResourceManager::get()->RegisterRGBController(rgb_controller);
|
||||
}
|
||||
}
|
||||
|
||||
REGISTER_I2C_DETECTOR("Kingston Fury DDR4/5 DRAM", DetectKingstonFuryDRAMControllers);
|
||||
void DetectKingstonFuryDDR5Controllers(i2c_smbus_interface* bus, std::vector<SPDWrapper*> &slots)
|
||||
{
|
||||
std::vector<int> fury_slots;
|
||||
|
||||
DetectKingstonFuryDRAMControllers(bus, slots, FURY_BASE_ADDR_DDR5, TestDDR5Models, fury_slots);
|
||||
|
||||
if(!fury_slots.empty())
|
||||
{
|
||||
KingstonFuryDRAMController* controller =
|
||||
new KingstonFuryDRAMController(bus, FURY_BASE_ADDR_DDR5, fury_slots);
|
||||
RGBController_KingstonFuryDRAM* rgb_controller =
|
||||
new RGBController_KingstonFuryDRAM(controller);
|
||||
rgb_controller->name = "Kingston Fury DDR5 RGB";
|
||||
ResourceManager::get()->RegisterRGBController(rgb_controller);
|
||||
}
|
||||
}
|
||||
|
||||
REGISTER_I2C_DIMM_DETECTOR("Kingston Fury DDR4 DRAM", DetectKingstonFuryDDR4Controllers, JEDEC_KINGSTON, SPD_DDR4_SDRAM);
|
||||
REGISTER_I2C_DIMM_DETECTOR("Kingston Fury DDR5 DRAM", DetectKingstonFuryDDR5Controllers, JEDEC_KINGSTON, SPD_DDR5_SDRAM);
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
|
||||
#define REGISTER_DETECTOR(name, func) static DeviceDetector device_detector_obj_##func(name, func)
|
||||
#define REGISTER_I2C_DETECTOR(name, func) static I2CDeviceDetector device_detector_obj_##func(name, func)
|
||||
#define REGISTER_I2C_DIMM_DETECTOR(name, func, jedec_id, dimm_type) static I2CDIMMDeviceDetector device_detector_obj_##func(name, func, jedec_id, dimm_type)
|
||||
#define REGISTER_I2C_PCI_DETECTOR(name, func, ven, dev, subven, subdev, addr) static I2CPCIDeviceDetector device_detector_obj_##ven##dev##subven##subdev##addr##func(name, func, ven, dev, subven, subdev, addr)
|
||||
#define REGISTER_I2C_BUS_DETECTOR(func) static I2CBusDetector device_detector_obj_##func(func)
|
||||
#define REGISTER_HID_DETECTOR(name, func, vid, pid) static HIDDeviceDetector device_detector_obj_##vid##pid(name, func, vid, pid, HID_INTERFACE_ANY, HID_USAGE_PAGE_ANY, HID_USAGE_ANY)
|
||||
@@ -29,6 +30,7 @@
|
||||
#define REGISTER_PRE_DETECTION_HOOK(func) static PreDetectionHook device_detector_obj_##func(func)
|
||||
|
||||
#define REGISTER_DYNAMIC_I2C_DETECTOR(name, func) I2CDeviceDetector device_detector_obj_##func(name, func)
|
||||
#define REGISTER_DYNAMIC_I2C_DIMM_DETECTOR(name, func, jedec_id, dimm_type) I2CDIMMDeviceDetector device_detector_obj_##func(name, func, jedec_id, dimm_type)
|
||||
#define REGISTER_DYNAMIC_I2C_PCI_DETECTOR(name, func, ven, dev, subven, subdev, addr) I2CPCIDeviceDetector device_detector_obj_##ven##dev##subven##subdev##addr##func(name, func, ven, dev, subven, subdev, addr)
|
||||
#define REGISTER_DYNAMIC_I2C_BUS_DETECTOR(func) I2CBusDetector device_detector_obj_##func(func)
|
||||
#define REGISTER_DYNAMIC_HID_DETECTOR(name, func, vid, pid) HIDDeviceDetector device_detector_obj_##vid##pid(name, func, vid, pid, HID_INTERFACE_ANY, HID_USAGE_PAGE_ANY, HID_USAGE_ANY)
|
||||
|
||||
@@ -33,6 +33,15 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
class I2CDIMMDeviceDetector
|
||||
{
|
||||
public:
|
||||
I2CDIMMDeviceDetector(std::string name, I2CDIMMDeviceDetectorFunction detector, uint16_t jedec_id, uint8_t dimm_type)
|
||||
{
|
||||
ResourceManager::get()->RegisterI2CDIMMDeviceDetector(name, detector, jedec_id, dimm_type);
|
||||
}
|
||||
};
|
||||
|
||||
class I2CPCIDeviceDetector
|
||||
{
|
||||
public:
|
||||
|
||||
@@ -208,6 +208,7 @@ SOURCES +=
|
||||
PluginManager.cpp \
|
||||
ProfileManager.cpp \
|
||||
ResourceManager.cpp \
|
||||
SPDAccessor.cpp \
|
||||
SettingsManager.cpp \
|
||||
i2c_smbus/i2c_smbus.cpp \
|
||||
i2c_tools/i2c_tools.cpp \
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
#include <string>
|
||||
#include <hidapi.h>
|
||||
#include "cli.h"
|
||||
#include "pci_ids/pci_ids.h"
|
||||
#include "ResourceManager.h"
|
||||
#include "ProfileManager.h"
|
||||
#include "LogManager.h"
|
||||
@@ -271,6 +272,18 @@ void ResourceManager::RegisterI2CDeviceDetector(std::string name, I2CDeviceDetec
|
||||
i2c_device_detectors.push_back(detector);
|
||||
}
|
||||
|
||||
void ResourceManager::RegisterI2CDIMMDeviceDetector(std::string name, I2CDIMMDeviceDetectorFunction detector, uint16_t jedec_id, uint8_t dimm_type)
|
||||
{
|
||||
I2CDIMMDeviceDetectorBlock block;
|
||||
|
||||
block.name = name;
|
||||
block.function = detector;
|
||||
block.jedec_id = jedec_id;
|
||||
block.dimm_type = dimm_type;
|
||||
|
||||
i2c_dimm_device_detectors.push_back(block);
|
||||
}
|
||||
|
||||
void ResourceManager::RegisterI2CPCIDeviceDetector(std::string name, I2CPCIDeviceDetectorFunction detector, uint16_t ven_id, uint16_t dev_id, uint16_t subven_id, uint16_t subdev_id, uint8_t i2c_addr)
|
||||
{
|
||||
I2CPCIDeviceDetectorBlock block;
|
||||
@@ -1009,7 +1022,7 @@ void ResourceManager::DetectDevicesThreadFunction()
|
||||
current_hid_device = current_hid_device->next;
|
||||
}
|
||||
|
||||
percent_denominator = (float)(i2c_device_detectors.size() + i2c_pci_device_detectors.size() + device_detectors.size()) + (float)hid_device_count;
|
||||
percent_denominator = (float)(i2c_device_detectors.size() + i2c_dimm_device_detectors.size() + i2c_pci_device_detectors.size() + device_detectors.size()) + (float)hid_device_count;
|
||||
|
||||
/*-------------------------------------------------*\
|
||||
| Start at 0% detection progress |
|
||||
@@ -1105,6 +1118,70 @@ void ResourceManager::DetectDevicesThreadFunction()
|
||||
detection_percent = (unsigned int)(percent * 100.0f);
|
||||
}
|
||||
|
||||
/*-------------------------------------------------*\
|
||||
| Detect i2c DIMM modules |
|
||||
\*-------------------------------------------------*/
|
||||
LOG_INFO("------------------------------------------------------");
|
||||
LOG_INFO("| Detecting I2C DIMM modules |");
|
||||
LOG_INFO("------------------------------------------------------");
|
||||
for(unsigned int bus = 0; bus < busses.size(); bus++)
|
||||
{
|
||||
IF_DRAM_SMBUS(busses[bus]->pci_vendor, busses[bus]->pci_device)
|
||||
{
|
||||
std::vector<SPDWrapper> slots;
|
||||
SPDMemoryType dimm_type = SPD_RESERVED;
|
||||
|
||||
for(uint8_t spd_addr = 0x50; spd_addr < 0x58; spd_addr++)
|
||||
{
|
||||
SPDDetector spd(busses[bus], spd_addr, dimm_type);
|
||||
if(spd.is_valid())
|
||||
{
|
||||
SPDWrapper accessor(spd);
|
||||
dimm_type = spd.memory_type();
|
||||
LOG_INFO("Detected occupied slot %d, bus %d, type %s", spd_addr - 0x50 + 1, bus, spd_memory_type_name[dimm_type]);
|
||||
LOG_DEBUG("Jedec ID: 0x%04x", accessor.jedec_id());
|
||||
slots.push_back(accessor);
|
||||
}
|
||||
}
|
||||
|
||||
for(unsigned int i2c_detector_idx = 0; i2c_detector_idx < i2c_dimm_device_detectors.size() && detection_is_required.load(); i2c_detector_idx++)
|
||||
{
|
||||
if(i2c_dimm_device_detectors[i2c_detector_idx].dimm_type == dimm_type &&
|
||||
is_jedec_in_slots(slots, i2c_dimm_device_detectors[i2c_detector_idx].jedec_id))
|
||||
{
|
||||
detection_string = i2c_dimm_device_detectors[i2c_detector_idx].name.c_str();
|
||||
|
||||
/*-------------------------------------------------*\
|
||||
| Check if this detector is enabled |
|
||||
\*-------------------------------------------------*/
|
||||
bool this_device_enabled = true;
|
||||
if(detector_settings.contains("detectors") && detector_settings["detectors"].contains(detection_string))
|
||||
{
|
||||
this_device_enabled = detector_settings["detectors"][detection_string];
|
||||
}
|
||||
|
||||
LOG_DEBUG("[%s] is %s", detection_string, ((this_device_enabled == true) ? "enabled" : "disabled"));
|
||||
if(this_device_enabled)
|
||||
{
|
||||
DetectionProgressChanged();
|
||||
|
||||
std::vector<SPDWrapper*> matching_slots = slots_with_jedec(slots, i2c_dimm_device_detectors[i2c_detector_idx].jedec_id);
|
||||
i2c_dimm_device_detectors[i2c_detector_idx].function(busses[bus], matching_slots);
|
||||
}
|
||||
|
||||
LOG_TRACE("[%s] detection end", detection_string);
|
||||
}
|
||||
|
||||
/*-------------------------------------------------*\
|
||||
| Update detection percent |
|
||||
\*-------------------------------------------------*/
|
||||
percent = ((float)i2c_detector_idx + 1.0f) / percent_denominator;
|
||||
|
||||
detection_percent = (unsigned int)(percent * 100.0f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*-------------------------------------------------*\
|
||||
| Detect i2c PCI devices |
|
||||
\*-------------------------------------------------*/
|
||||
|
||||
@@ -18,6 +18,8 @@
|
||||
#include <functional>
|
||||
#include <thread>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "SPDAccessor.h"
|
||||
#include "hidapi_wrapper.h"
|
||||
#include "i2c_smbus.h"
|
||||
#include "filesystem.h"
|
||||
@@ -38,6 +40,7 @@ class SettingsManager;
|
||||
typedef std::function<bool()> I2CBusDetectorFunction;
|
||||
typedef std::function<void()> DeviceDetectorFunction;
|
||||
typedef std::function<void(std::vector<i2c_smbus_interface*>&)> I2CDeviceDetectorFunction;
|
||||
typedef std::function<void(i2c_smbus_interface*, std::vector<SPDWrapper*>&)> I2CDIMMDeviceDetectorFunction;
|
||||
typedef std::function<void(i2c_smbus_interface*, uint8_t, const std::string&)> I2CPCIDeviceDetectorFunction;
|
||||
typedef std::function<void(hid_device_info*, const std::string&)> HIDDeviceDetectorFunction;
|
||||
typedef std::function<void(hidapi_wrapper wrapper, hid_device_info*, const std::string&)> HIDWrappedDeviceDetectorFunction;
|
||||
@@ -80,6 +83,14 @@ typedef struct
|
||||
uint8_t i2c_addr;
|
||||
} I2CPCIDeviceDetectorBlock;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
std::string name;
|
||||
I2CDIMMDeviceDetectorFunction function;
|
||||
uint16_t jedec_id;
|
||||
uint8_t dimm_type;
|
||||
} I2CDIMMDeviceDetectorBlock;
|
||||
|
||||
typedef void (*DeviceListChangeCallback)(void *);
|
||||
typedef void (*DetectionProgressCallback)(void *);
|
||||
typedef void (*DetectionStartCallback)(void *);
|
||||
@@ -144,6 +155,7 @@ public:
|
||||
void RegisterI2CBusDetector (I2CBusDetectorFunction detector);
|
||||
void RegisterDeviceDetector (std::string name, DeviceDetectorFunction detector);
|
||||
void RegisterI2CDeviceDetector (std::string name, I2CDeviceDetectorFunction detector);
|
||||
void RegisterI2CDIMMDeviceDetector (std::string name, I2CDIMMDeviceDetectorFunction detector, uint16_t jedec_id, uint8_t dimm_type);
|
||||
void RegisterI2CPCIDeviceDetector (std::string name, I2CPCIDeviceDetectorFunction detector, uint16_t ven_id, uint16_t dev_id, uint16_t subven_id, uint16_t subdev_id, uint8_t i2c_addr);
|
||||
void RegisterHIDDeviceDetector (std::string name,
|
||||
HIDDeviceDetectorFunction detector,
|
||||
@@ -289,6 +301,7 @@ private:
|
||||
std::vector<I2CBusDetectorFunction> i2c_bus_detectors;
|
||||
std::vector<I2CDeviceDetectorFunction> i2c_device_detectors;
|
||||
std::vector<std::string> i2c_device_detector_strings;
|
||||
std::vector<I2CDIMMDeviceDetectorBlock> i2c_dimm_device_detectors;
|
||||
std::vector<I2CPCIDeviceDetectorBlock> i2c_pci_device_detectors;
|
||||
std::vector<HIDDeviceDetectorBlock> hid_device_detectors;
|
||||
std::vector<HIDWrappedDeviceDetectorBlock> hid_wrapped_device_detectors;
|
||||
|
||||
519
SPDAccessor.cpp
Normal file
519
SPDAccessor.cpp
Normal file
@@ -0,0 +1,519 @@
|
||||
/*---------------------------------------------------------*\
|
||||
| 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;
|
||||
valid = true;
|
||||
}
|
||||
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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
int ddr5Magic = bus->i2c_smbus_read_byte_data(spd_addr, 0x00);
|
||||
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
Normal file
223
SPDAccessor.h
Normal file
@@ -0,0 +1,223 @@
|
||||
/*---------------------------------------------------------*\
|
||||
| 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;
|
||||
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);
|
||||
Reference in New Issue
Block a user