/*---------------------------------------------------------*\ | 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_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_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); }; /*---------------------------------------------------------*\ | 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); } 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); } 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); }