// SPDX-License-Identifier: GPL-2.0 // Copyright (C) 2004-2023 Sam Demeulemeester // // ------------------------ // // Platform-specific code for Intel Haswell CPUs (HSW) // #include "cpuinfo.h" #include "memctrl.h" #include "msr.h" #include "pci.h" #include "imc.h" #define HSW_MMR_BASE_REG 0x48 #define HSW_REG_MAIN_CHAN0 0x5004 #define HSW_REG_MAIN_CHAN1 0x5008 #define HSW_REG_MCH_CFG 0x5E04 #define HSW_REG_TIMING_CAS 0x4014 #define HSW_REG_TIMING_RCD 0x4000 void get_imc_config_intel_hsw(void) { uint32_t mmio_reg, mch_cfg, offset; uint32_t reg0, reg1; float cpu_ratio, dram_ratio; uintptr_t *ptr; imc.type = "DDR3"; imc.tCL_dec = 0; // Get Memory Mapped Register Base Address (Enable MMIO if needed) mmio_reg = pci_config_read32(0, 0, 0, HSW_MMR_BASE_REG); if (!(mmio_reg & 0x1)) { pci_config_write32( 0, 0, 0, HSW_MMR_BASE_REG, mmio_reg | 1); mmio_reg = pci_config_read32(0, 0, 0, HSW_MMR_BASE_REG); if (!(mmio_reg & 0x1)) return; } mmio_reg &= 0xFFFFC000; // Get DRAM Ratio ptr = (uintptr_t*)((uintptr_t)mmio_reg + HSW_REG_MCH_CFG); mch_cfg = *ptr & 0xFFFF; if ((mch_cfg >> 8) & 1) { dram_ratio = (float)(*ptr & 0x1F) * (100.0f / 100.0f); } else { dram_ratio = (float)(*ptr & 0x1F) * (133.34f / 100.0f); } // Get CPU Ratio rdmsr(MSR_IA32_PLATFORM_INFO, reg0, reg1); cpu_ratio = (float)((reg0 >> 8) & 0xFF); if (!cpu_ratio) return; // Compute DRAM Frequency imc.freq = ((clks_per_msec / 1000) / cpu_ratio) * dram_ratio * 2; if (imc.freq < 350 || imc.freq > 5000) { imc.freq = 0; return; } // Get Main Memory Controller Register for both channels ptr = (uintptr_t*)((uintptr_t)mmio_reg + HSW_REG_MAIN_CHAN0); reg0 = *ptr & 0xFFFF; ptr = (uintptr_t*)((uintptr_t)mmio_reg + HSW_REG_MAIN_CHAN1); reg1 = *ptr & 0xFFFF; // Populate IMC width imc.width = (reg0 && reg1) ? 128 : 64; // Define offset (ie: which channel is really used) offset = reg0 ? 0x0000 : 0x4000; // CAS Latency (tCAS) ptr = (uintptr_t*)((uintptr_t)mmio_reg + offset + HSW_REG_TIMING_CAS); imc.tCL = *ptr & 0x1F; // RAS-To-CAS (tRCD) ptr = (uintptr_t*)((uintptr_t)mmio_reg + offset + HSW_REG_TIMING_RCD); imc.tRCD = *ptr & 0x1F; // RAS Precharge (tRP) imc.tRP = (*ptr >> 5) & 0x1F; // RAS Active to precharge (tRAS) imc.tRAS = (*ptr >> 10) & 0x3F; }