diff --git a/src/northbridge/intel/i855/Kconfig b/src/northbridge/intel/i855/Kconfig index 3d3443a2c9..f5c2890e91 100644 --- a/src/northbridge/intel/i855/Kconfig +++ b/src/northbridge/intel/i855/Kconfig @@ -1,3 +1,33 @@ config NORTHBRIDGE_INTEL_I855 bool + select HAVE_DEBUG_RAM_SETUP +choice + prompt "Onboard graphics" + default I855_VIDEO_MB_8MB + depends on NORTHBRIDGE_INTEL_I855 + +config I855_VIDEO_MB_OFF + bool "Disabled, 0KB" +config I855_VIDEO_MB_1MB + bool "Enabled, 1MB" +config I855_VIDEO_MB_4MB + bool "Enabled, 4MB" +config I855_VIDEO_MB_8MB + bool "Enabled, 8MB" +config I855_VIDEO_MB_16MB + bool "Enabled, 16MB" +config I855_VIDEO_MB_32MB + bool "Enabled, 32MB" + +endchoice + +config VIDEO_MB + int + default 0 if I855_VIDEO_MB_OFF + default 1 if I855_VIDEO_MB_1MB + default 4 if I855_VIDEO_MB_4MB + default 8 if I855_VIDEO_MB_8MB + default 16 if I855_VIDEO_MB_16MB + default 32 if I855_VIDEO_MB_32MB + depends on NORTHBRIDGE_INTEL_I855 diff --git a/src/northbridge/intel/i855/northbridge.c b/src/northbridge/intel/i855/northbridge.c index 77d1564672..220f7220ce 100644 --- a/src/northbridge/intel/i855/northbridge.c +++ b/src/northbridge/intel/i855/northbridge.c @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -32,6 +33,26 @@ #include #include "chip.h" +static void northbridge_init(device_t dev) +{ + printk(BIOS_SPEW, "Northbridge init\n"); +} + +static struct device_operations northbridge_operations = { + .read_resources = pci_dev_read_resources, + .set_resources = pci_dev_set_resources, + .enable_resources = pci_dev_enable_resources, + .init = northbridge_init, + .enable = 0, + .ops_pci = 0, +}; + +static const struct pci_driver northbridge_driver __pci_driver = { + .ops = &northbridge_operations, + .vendor = PCI_VENDOR_ID_INTEL, + .device = 0x3580, +}; + static void ram_resource(device_t dev, unsigned long index, unsigned long basek, unsigned long sizek) { diff --git a/src/northbridge/intel/i855/raminit.c b/src/northbridge/intel/i855/raminit.c index 386eda10bb..e611d8e62b 100644 --- a/src/northbridge/intel/i855/raminit.c +++ b/src/northbridge/intel/i855/raminit.c @@ -18,12 +18,478 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ +#include +#include #include +#include #include +#include "i855.h" + +#define VALIDATE_DIMM_COMPATIBILITY + +/* Debugging macros. */ +#if CONFIG_DEBUG_RAM_SETUP +#define PRINTK_DEBUG(x...) printk(BIOS_DEBUG, x) +#define DUMPNORTH() dump_pci_device(NORTHBRIDGE_MMC) +#else +#define PRINTK_DEBUG(x...) +#define DUMPNORTH() +#endif + +#define delay() udelay(200) -#define dumpnorth() dump_pci_device(PCI_DEV(0, 0, 1)) #define VG85X_MODE (SDRAM_BURST_4 | SDRAM_BURST_INTERLEAVED | SDRAM_CAS_2_5) +/* DRC[10:8] - Refresh Mode Select (RMS). + * 0x0 for Refresh Disabled (Self Refresh) + * 0x1 for Refresh interval 15.6 us for 133MHz + * 0x2 for Refresh interval 7.8 us for 133MHz + * 0x7 for Refresh interval 64 Clocks. (Fast Refresh Mode) + */ +#define RAM_COMMAND_REFRESH 0x1 + +/* DRC[6:4] - SDRAM Mode Select (SMS). */ +#define RAM_COMMAND_SELF_REFRESH 0x0 +#define RAM_COMMAND_NOP 0x1 +#define RAM_COMMAND_PRECHARGE 0x2 +#define RAM_COMMAND_MRS 0x3 +#define RAM_COMMAND_EMRS 0x4 +#define RAM_COMMAND_CBR 0x6 +#define RAM_COMMAND_NORMAL 0x7 + +/* DRC[29] - Initialization Complete (IC). */ +#define RAM_COMMAND_IC 0x1 + +struct dimm_size { + unsigned int side1; + unsigned int side2; +}; + +/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ +/* DEFINITIONS */ +/**********************************************************************************/ + +static const uint32_t refresh_frequency[] = { + /* Relative frequency (array value) of each E7501 Refresh Mode Select + * (RMS) value (array index) + * 0 == least frequent refresh (longest interval between refreshes) + * [0] disabled -> 0 + * [1] 15.6 usec -> 2 + * [2] 7.8 usec -> 3 + * [3] 64 usec -> 1 + * [4] reserved -> 0 + * [5] reserved -> 0 + * [6] reserved -> 0 + * [7] 64 clocks -> 4 + */ + 0, 2, 3, 1, 0, 0, 0, 4 +}; + +static const uint32_t refresh_rate_map[] = { + /* Map the JEDEC spd refresh rates (array index) to i855 Refresh Mode + * Select values (array value) + * These are all the rates defined by JESD21-C Appendix D, Rev. 1.0 + * The i855 supports only 15.6 us (1), 7.8 us (2) and + * 64 clock (481 ns) (7) refresh. + * [0] == 15.625 us -> 15.6 us + * [1] == 3.9 us -> 481 ns + * [2] == 7.8 us -> 7.8 us + * [3] == 31.3 us -> 15.6 us + * [4] == 62.5 us -> 15.6 us + * [5] == 125 us -> 15.6 us + */ + 1, 7, 2, 1, 1, 1 +}; + +#define MAX_SPD_REFRESH_RATE ((sizeof(refresh_rate_map) / sizeof(uint32_t)) - 1) + +/*----------------------------------------------------------------------------- +SPD functions. +-----------------------------------------------------------------------------*/ + +static void die_on_spd_error(int spd_return_value) +{ + if (spd_return_value < 0) + PRINTK_DEBUG("Error reading SPD info: got %d\n", spd_return_value); +/* + if (spd_return_value < 0) + die("Error reading SPD info\n"); +*/ +} + +//---------------------------------------------------------------------------------- +// Function: sdram_spd_get_page_size +// Parameters: dimm_socket_address - SMBus address of DIMM socket to interrogate +// Return Value: struct dimm_size - log2(page size) for each side of the DIMM. +// Description: Calculate the page size for each physical bank of the DIMM: +// log2(page size) = (# columns) + log2(data width) +// +// NOTE: page size is the total number of data bits in a row. +// +static struct dimm_size sdram_spd_get_page_size(uint16_t dimm_socket_address) +{ + uint16_t module_data_width; + int value; + struct dimm_size pgsz; + + pgsz.side1 = 0; + pgsz.side2 = 0; + + // Side 1 + value = spd_read_byte(dimm_socket_address, SPD_NUM_COLUMNS); + die_on_spd_error(value); + + pgsz.side1 = value & 0xf; // # columns in bank 1 + + /* Get the module data width and convert it to a power of two */ + value = spd_read_byte(dimm_socket_address, SPD_MODULE_DATA_WIDTH_MSB); + die_on_spd_error(value); + + module_data_width = (value & 0xff) << 8; + + value = spd_read_byte(dimm_socket_address, SPD_MODULE_DATA_WIDTH_LSB); + die_on_spd_error(value); + + module_data_width |= (value & 0xff); + + pgsz.side1 += log2(module_data_width); + + /* side two */ + value = spd_read_byte(dimm_socket_address, SPD_NUM_DIMM_BANKS); + die_on_spd_error(value); + +/* + if (value > 2) + die("Bad SPD value\n"); +*/ + if (value > 2) + PRINTK_DEBUG("Bad SPD value\n"); + + if (value == 2) { + pgsz.side2 = pgsz.side1; // Assume symmetric banks until we know differently + value = spd_read_byte(dimm_socket_address, SPD_NUM_COLUMNS); + die_on_spd_error(value); + + if ((value & 0xf0) != 0) { + // Asymmetric banks + pgsz.side2 -= value & 0xf; /* Subtract out columns on side 1 */ + pgsz.side2 += (value >> 4) & 0xf; /* Add in columns on side 2 */ + } + } + + return pgsz; +} + +//---------------------------------------------------------------------------------- +// Function: sdram_spd_get_width +// Parameters: dimm_socket_address - SMBus address of DIMM socket to interrogate +// Return Value: dimm_size - width in bits of each DIMM side's DRAMs. +// Description: Read the width in bits of each DIMM side's DRAMs via SPD. +// (i.e. 4, 8, 16) +// +static struct dimm_size sdram_spd_get_width(uint16_t dimm_socket_address) +{ + int value; + struct dimm_size width; + + width.side1 = 0; + width.side2 = 0; + + value = spd_read_byte(dimm_socket_address, SPD_PRIMARY_SDRAM_WIDTH); + die_on_spd_error(value); + + width.side1 = value & 0x7f; // Mask off bank 2 flag + + if (value & 0x80) { + width.side2 = width.side1 << 1; // Bank 2 exists and is double-width + } else { + // If bank 2 exists, it's the same width as bank 1 + value = spd_read_byte(dimm_socket_address, SPD_NUM_DIMM_BANKS); + die_on_spd_error(value); + +#ifdef ROMCC_IF_BUG_FIXED + if (value == 2) + width.side2 = width.side1; +#else + switch (value) { + case 2: + width.side2 = width.side1; + break; + + default: + break; + } +#endif + } + + return width; +} + +//---------------------------------------------------------------------------------- +// Function: spd_get_dimm_size +// Parameters: dimm_socket_address - SMBus address of DIMM socket to interrogate +// Return Value: dimm_size - log2(number of bits) for each side of the DIMM +// Description: Calculate the log base 2 size in bits of both DIMM sides. +// log2(# bits) = (# columns) + log2(data width) + +// (# rows) + log2(banks per SDRAM) +// +// Note that it might be easier to use SPD byte 31 here, it has the +// DIMM size as a multiple of 4MB. The way we do it now we can size +// both sides of an asymmetric dimm. +// +static struct dimm_size spd_get_dimm_size(unsigned dimm) +{ + int value; + + // Start with log2(page size) + struct dimm_size sz = sdram_spd_get_page_size(dimm); + + if (sz.side1 > 0) { + value = spd_read_byte(dimm, SPD_NUM_ROWS); + die_on_spd_error(value); + + sz.side1 += value & 0xf; + + if (sz.side2 > 0) { + // Double-sided DIMM + if (value & 0xF0) + sz.side2 += value >> 4; // Asymmetric + else + sz.side2 += value; // Symmetric + } + + value = spd_read_byte(dimm, SPD_NUM_BANKS_PER_SDRAM); + die_on_spd_error(value); + + value = log2(value); + sz.side1 += value; + if (sz.side2 > 0) + sz.side2 += value; + } + + return sz; +} + +//---------------------------------------------------------------------------------- +// Function: spd_get_supported_dimms +// Parameters: ctrl - PCI addresses of memory controller functions, and +// SMBus addresses of DIMM slots on the mainboard +// Return Value: uint8_t - a bitmask indicating which sockets contain a compatible DIMM. +// Description: Scan for compatible DIMMs. +// +static uint8_t spd_get_supported_dimms(const struct mem_controller *ctrl) +{ + int i; + uint8_t dimm_mask = 0; + + for (i = 0; i < DIMM_SOCKETS; i++) { + uint16_t dimm = ctrl->channel0[i]; + +#ifdef VALIDATE_DIMM_COMPATIBILITY + struct dimm_size page_size; + struct dimm_size sdram_width; +#endif + int spd_value; + + if (dimm == 0) + continue; // No such socket on this mainboard + + if (spd_read_byte(dimm, SPD_MEMORY_TYPE) != SPD_MEMORY_TYPE_SDRAM_DDR) + continue; + +#ifdef VALIDATE_DIMM_COMPATIBILITY + if ((spd_value = spd_read_byte(dimm, SPD_MODULE_VOLTAGE)) != SPD_VOLTAGE_SSTL2) { + PRINTK_DEBUG("Skipping DIMM with unsupported voltage: %02x\n", spd_value); + continue; // Unsupported voltage + } + +/* + // E7501 does not support unregistered DIMMs + spd_value = spd_read_byte(dimm, SPD_MODULE_ATTRIBUTES); + if (!(spd_value & MODULE_REGISTERED) || (spd_value < 0)) { + PRINTK_DEBUG("Skipping unregistered DIMM: %02x\n", spd_value); + continue; + } +*/ + + page_size = sdram_spd_get_page_size(dimm); + sdram_width = sdram_spd_get_width(dimm); + + // Validate DIMM page size + // The i855 only supports page sizes of 4, 8, 16 KB per channel + // NOTE: 4 KB = 32 Kb = 2^15 + // 16 KB = 128 Kb = 2^17 + + if ((page_size.side1 < 15) || (page_size.side1 > 17)) { + PRINTK_DEBUG("Skipping DIMM with unsupported page size: %d\n", page_size.side1); + continue; + } + + // If DIMM is double-sided, verify side2 page size + if (page_size.side2 != 0) { + if ((page_size.side2 < 15) || (page_size.side2 > 17)) { + PRINTK_DEBUG("Skipping DIMM with unsupported page size: %d\n", page_size.side2); + continue; + } + } + // Validate SDRAM width + // The i855 only supports x8 and x16 devices + if ((sdram_width.side1 != 8) && (sdram_width.side1 != 16)) { + PRINTK_DEBUG("Skipping DIMM with unsupported width: %d\n", sdram_width.side2); + continue; + } + + // If DIMM is double-sided, verify side2 width + if (sdram_width.side2 != 0) { + if ((sdram_width.side2 != 8) + && (sdram_width.side2 != 16)) { + PRINTK_DEBUG("Skipping DIMM with unsupported width: %d\n", sdram_width.side2); + continue; + } + } +#endif + // Made it through all the checks, this DIMM is usable + dimm_mask |= (1 << i); + } + + return dimm_mask; +} + +/*----------------------------------------------------------------------------- +DIMM-initialization functions. +-----------------------------------------------------------------------------*/ +static void do_ram_command(uint8_t command, uint16_t jedec_mode_bits) +{ + int i; + u32 reg32; + uint8_t dimm_start_32M_multiple = 0; + uint16_t i855_mode_bits = jedec_mode_bits; + + /* Configure the RAM command. */ + reg32 = pci_read_config32(NORTHBRIDGE_MMC, DRC); + reg32 &= ~(7 << 4); + reg32 |= (command << 4); + PRINTK_DEBUG(" Sending RAM command 0x%08x\n", reg32); + pci_write_config32(NORTHBRIDGE_MMC, DRC, reg32); + + // RAM_COMMAND_NORMAL is an exception. + // It affects only the memory controller and does not need to be "sent" to the DIMMs. + + if (command != RAM_COMMAND_NORMAL) { + + // Send the command to all DIMMs by accessing a memory location within each + // NOTE: for mode select commands, some of the location address bits + // are part of the command + + // Map JEDEC mode bits to i855 + if (command == RAM_COMMAND_MRS || command == RAM_COMMAND_EMRS) { + /* Host address lines [13:3] map to DIMM address lines [11, 9:0] */ + i855_mode_bits = ((jedec_mode_bits & 0x800) << (13 - 11)) | ((jedec_mode_bits & 0x3ff) << (12 - 9)); + } + + for (i = 0; i < (DIMM_SOCKETS * 2); ++i) { + uint8_t dimm_end_32M_multiple = pci_read_config8(NORTHBRIDGE_MMC, DRB + i); + if (dimm_end_32M_multiple > dimm_start_32M_multiple) { + + uint32_t dimm_start_address = dimm_start_32M_multiple << 25; + PRINTK_DEBUG(" Sending RAM command to 0x%08x\n", dimm_start_address + i855_mode_bits); + read32(dimm_start_address + i855_mode_bits); + + // Set the start of the next DIMM + dimm_start_32M_multiple = dimm_end_32M_multiple; + } + } + } +} + +static void set_initialize_complete(const struct mem_controller *ctrl) +{ + uint32_t drc_reg; + + drc_reg = pci_read_config32(NORTHBRIDGE_MMC, DRC); + drc_reg |= (1 << 29); + pci_write_config32(NORTHBRIDGE_MMC, DRC, drc_reg); +} + +static void sdram_enable(int controllers, const struct mem_controller *ctrl) +{ + int i; + + print_debug("Ram enable 1\n"); + delay(); + delay(); + + /* NOP command */ + PRINTK_DEBUG(" NOP\n"); + do_ram_command(RAM_COMMAND_NOP, 0); + delay(); + delay(); + delay(); + + /* Pre-charge all banks (at least 200 us after NOP) */ + PRINTK_DEBUG(" Pre-charging all banks\n"); + do_ram_command(RAM_COMMAND_PRECHARGE, 0); + delay(); + delay(); + delay(); + + print_debug("Ram enable 4\n"); + do_ram_command(RAM_COMMAND_EMRS, SDRAM_EXTMODE_DLL_ENABLE); + delay(); + delay(); + delay(); + + print_debug("Ram enable 5\n"); + do_ram_command(RAM_COMMAND_MRS, VG85X_MODE | SDRAM_MODE_DLL_RESET); + + print_debug("Ram enable 6\n"); + do_ram_command(RAM_COMMAND_PRECHARGE, 0); + delay(); + delay(); + delay(); + + /* 8 CBR refreshes (Auto Refresh) */ + PRINTK_DEBUG(" 8 CBR refreshes\n"); + for(i = 0; i < 8; i++) { + do_ram_command(RAM_COMMAND_CBR, 0); + delay(); + delay(); + delay(); + } + + print_debug("Ram enable 8\n"); + do_ram_command(RAM_COMMAND_MRS, VG85X_MODE | SDRAM_MODE_NORMAL); + + /* Set GME-M Mode Select bits back to NORMAL operation mode */ + PRINTK_DEBUG(" Normal operation mode\n"); + do_ram_command(RAM_COMMAND_NORMAL, 0); + delay(); + delay(); + delay(); + + print_debug("Ram enable 9\n"); + set_initialize_complete(ctrl); + + delay(); + delay(); + delay(); + delay(); + delay(); + + print_debug("After configuration:\n"); + /* dump_pci_devices(); */ + + /* + print_debug("\n\n***** RAM TEST *****\n"); + ram_check(0, 0xa0000); + ram_check(0x100000, 0x40000000); + */ +} + +/*----------------------------------------------------------------------------- +DIMM-independant configuration functions. +-----------------------------------------------------------------------------*/ + /** * Set only what I need until it works, then make it figure things out on boot * assumes only one dimm is populated @@ -37,253 +503,479 @@ static void sdram_set_registers(const struct mem_controller *ctrl) */ } -static void spd_set_row_attributes(const struct mem_controller *ctrl) +static void spd_set_row_attributes(const struct mem_controller *ctrl, uint8_t dimm_mask) { - uint16_t dra_reg; + int i; + uint16_t row_attributes = 0; - dra_reg = 0x7733; - pci_write_config16(ctrl->d0, 0x50, dra_reg); + for (i = 0; i < DIMM_SOCKETS; i++) { + uint16_t dimm = ctrl->channel0[i]; + struct dimm_size page_size; + struct dimm_size sdram_width; + + if (!(dimm_mask & (1 << i))) { + row_attributes |= 0x77 << (i << 3); + continue; // This DIMM not usable + } + + // Get the relevant parameters via SPD + page_size = sdram_spd_get_page_size(dimm); + sdram_width = sdram_spd_get_width(dimm); + + // Update the DRAM Row Attributes. + // Page size is encoded as log2(page size in bits) - log2(2 KB) or 4 KB == 1, 8 KB == 3, 16KB == 3 + // NOTE: 2 KB = 16 Kb = 2^14 + row_attributes |= (page_size.side1 - 14) << (i << 3); // Side 1 of each DIMM is an EVEN row + + if (sdram_width.side2 > 0) + row_attributes |= (page_size.side2 - 14) << ((i << 3) + 4); // Side 2 is ODD + else + row_attributes |= 7 << ((i << 3) + 4); + /* go to the next DIMM */ + } + + PRINTK_DEBUG("DRA: %04x\n", row_attributes); + + /* Write the new row attributes register */ + pci_write_config16(NORTHBRIDGE_MMC, DRA, row_attributes); } -static void spd_set_dram_controller_mode(const struct mem_controller *ctrl) +static void spd_set_dram_controller_mode(const struct mem_controller *ctrl, uint8_t dimm_mask) { - uint32_t drc_reg; + int i; - /* drc_reg = 0x00009101; */ - drc_reg = 0x00009901; - pci_write_config32(ctrl->d0, 0x70, drc_reg); + // Initial settings + u32 controller_mode = pci_read_config32(NORTHBRIDGE_MMC, DRC); + u32 system_refresh_mode = (controller_mode >> 7) & 7; + + controller_mode |= (1 << 20); // ECC + controller_mode |= (1 << 15); // RAS lockout + controller_mode |= (1 << 12); // Address Tri-state enable (ADRTRIEN), FIXME: how is this detected????? + controller_mode |= (2 << 10); // FIXME: Undocumented, really needed????? + + for (i = 0; i < DIMM_SOCKETS; i++) { + uint16_t dimm = ctrl->channel0[i]; + uint32_t dimm_refresh_mode; + int value; + u8 tRCD, tRP; + + if (!(dimm_mask & (1 << i))) { + continue; // This DIMM not usable + } + + // Disable ECC mode if any one of the DIMMs does not support ECC + value = spd_read_byte(dimm, SPD_DIMM_CONFIG_TYPE); + die_on_spd_error(value); + if (value != ERROR_SCHEME_ECC) + controller_mode &= ~(3 << 20); + + value = spd_read_byte(dimm, SPD_REFRESH); + die_on_spd_error(value); + value &= 0x7f; // Mask off self-refresh bit + if (value > MAX_SPD_REFRESH_RATE) { + print_err("unsupported refresh rate\n"); + continue; + } + // Get the appropriate i855 refresh mode for this DIMM + dimm_refresh_mode = refresh_rate_map[value]; + if (dimm_refresh_mode > 7) { + print_err("unsupported refresh rate\n"); + continue; + } + // If this DIMM requires more frequent refresh than others, + // update the system setting + if (refresh_frequency[dimm_refresh_mode] > + refresh_frequency[system_refresh_mode]) + system_refresh_mode = dimm_refresh_mode; + + /* FIXME: is this correct? */ + tRCD = spd_read_byte(dimm, SPD_tRCD); + tRP = spd_read_byte(dimm, SPD_tRP); + if (tRCD != tRP) { + PRINTK_DEBUG(" Disabling RAS lockouk due to tRCD (%d) != tRP (%d)\n", tRCD, tRP); + controller_mode &= ~(1 << 15); + } + + /* go to the next DIMM */ + } + + controller_mode &= ~(7 << 7); + controller_mode |= (system_refresh_mode << 7); + PRINTK_DEBUG("DRC: %08x\n", controller_mode); + + pci_write_config32(NORTHBRIDGE_MMC, DRC, controller_mode); } -static void spd_set_dram_timing(const struct mem_controller *ctrl) +static void spd_set_dram_timing(const struct mem_controller *ctrl, uint8_t dimm_mask) { - uint32_t drt_reg; + int i; + u32 dram_timing; - drt_reg = 0x2a004405; - pci_write_config32(ctrl->d0, 0x60, drt_reg); + // CAS# latency bitmasks in SPD_ACCEPTABLE_CAS_LATENCIES format + // NOTE: i82822 supports only 2.0 and 2.5 + uint32_t system_compatible_cas_latencies = SPD_CAS_LATENCY_2_0 | SPD_CAS_LATENCY_2_5; + uint8_t slowest_row_precharge = 0; + uint8_t slowest_ras_cas_delay = 0; + uint8_t slowest_active_to_precharge_delay = 0; + + for (i = 0; i < DIMM_SOCKETS; i++) { + uint16_t dimm = ctrl->channel0[i]; + int value; + uint32_t current_cas_latency; + uint32_t dimm_compatible_cas_latencies; + if (!(dimm_mask & (1 << i))) + continue; // This DIMM not usable + + value = spd_read_byte(dimm, SPD_ACCEPTABLE_CAS_LATENCIES); + PRINTK_DEBUG("SPD_ACCEPTABLE_CAS_LATENCIES: %d\n", value); + die_on_spd_error(value); + + dimm_compatible_cas_latencies = value & 0x7f; // Start with all supported by DIMM + PRINTK_DEBUG("dimm_compatible_cas_latencies #1: %d\n", dimm_compatible_cas_latencies); + + current_cas_latency = 1 << log2(dimm_compatible_cas_latencies); // Max supported by DIMM + PRINTK_DEBUG("current_cas_latency: %d\n", current_cas_latency); + + // Can we support the highest CAS# latency? + value = spd_read_byte(dimm, SPD_MIN_CYCLE_TIME_AT_CAS_MAX); + die_on_spd_error(value); + PRINTK_DEBUG("SPD_MIN_CYCLE_TIME_AT_CAS_MAX: %d.%d\n", value >> 4, value & 0xf); + + // NOTE: At 133 MHz, 1 clock == 7.52 ns + if (value > 0x75) { + // Our bus is too fast for this CAS# latency + // Remove it from the bitmask of those supported by the DIMM that are compatible + dimm_compatible_cas_latencies &= ~current_cas_latency; + PRINTK_DEBUG("dimm_compatible_cas_latencies #2: %d\n", dimm_compatible_cas_latencies); + } + // Can we support the next-highest CAS# latency (max - 0.5)? + + current_cas_latency >>= 1; + if (current_cas_latency != 0) { + value = spd_read_byte(dimm, SPD_SDRAM_CYCLE_TIME_2ND); + die_on_spd_error(value); + PRINTK_DEBUG("SPD_SDRAM_CYCLE_TIME_2ND: %d.%d\n", value >> 4, value & 0xf); + if (value > 0x75) { + dimm_compatible_cas_latencies &= ~current_cas_latency; + PRINTK_DEBUG("dimm_compatible_cas_latencies #2: %d\n", dimm_compatible_cas_latencies); + } + } + // Can we support the next-highest CAS# latency (max - 1.0)? + current_cas_latency >>= 1; + if (current_cas_latency != 0) { + value = spd_read_byte(dimm, SPD_SDRAM_CYCLE_TIME_3RD); + PRINTK_DEBUG("SPD_SDRAM_CYCLE_TIME_3RD: %d.%d\n", value >> 4, value & 0xf); + die_on_spd_error(value); + if (value > 0x75) { + dimm_compatible_cas_latencies &= ~current_cas_latency; + PRINTK_DEBUG("dimm_compatible_cas_latencies #2: %d\n", dimm_compatible_cas_latencies); + } + } + // Restrict the system to CAS# latencies compatible with this DIMM + system_compatible_cas_latencies &= dimm_compatible_cas_latencies; + + value = spd_read_byte(dimm, SPD_MIN_ROW_PRECHARGE_TIME); + die_on_spd_error(value); + if (value > slowest_row_precharge) + slowest_row_precharge = value; + + value = spd_read_byte(dimm, SPD_MIN_RAS_TO_CAS_DELAY); + die_on_spd_error(value); + if (value > slowest_ras_cas_delay) + slowest_ras_cas_delay = value; + + value = spd_read_byte(dimm, SPD_MIN_ACTIVE_TO_PRECHARGE_DELAY); + die_on_spd_error(value); + if (value > slowest_active_to_precharge_delay) + slowest_active_to_precharge_delay = value; + + /* go to the next DIMM */ + } + PRINTK_DEBUG("CAS latency: %d\n", system_compatible_cas_latencies); + + dram_timing = pci_read_config32(NORTHBRIDGE_MMC, DRT); + dram_timing &= ~(DRT_CAS_MASK | DRT_TRP_MASK | DRT_RCD_MASK); + PRINTK_DEBUG("DRT: %08x\n", dram_timing); + + if (system_compatible_cas_latencies & SPD_CAS_LATENCY_2_0) { + dram_timing |= DRT_CAS_2_0; + } else if (system_compatible_cas_latencies & SPD_CAS_LATENCY_2_5) { + dram_timing |= DRT_CAS_2_5; + } else + die("No CAS# latencies compatible with all DIMMs!!\n"); + + uint32_t current_cas_latency = dram_timing & DRT_CAS_MASK; + + /* tRP */ + + PRINTK_DEBUG("slowest_row_precharge: %d.%d\n", slowest_row_precharge >> 2, slowest_row_precharge & 0x3); + // i855 supports only 2, 3 or 4 clocks for tRP + if (slowest_row_precharge > ((30 << 2))) + die("unsupported DIMM tRP"); // > 30.0 ns: 5 or more clocks + else if (slowest_row_precharge > ((22 << 2) | (2 << 0))) + dram_timing |= DRT_TRP_4; // > 22.5 ns: 4 or more clocks + else if (slowest_row_precharge > (15 << 2)) + dram_timing |= DRT_TRP_3; // > 15.0 ns: 3 clocks + else + dram_timing |= DRT_TRP_2; // <= 15.0 ns: 2 clocks + + /* tRCD */ + + PRINTK_DEBUG("slowest_ras_cas_delay: %d.%d\n", slowest_ras_cas_delay >> 2, slowest_ras_cas_delay & 0x3); + // i855 supports only 2, 3 or 4 clocks for tRCD + if (slowest_ras_cas_delay > ((30 << 2))) + die("unsupported DIMM tRCD"); // > 30.0 ns: 5 or more clocks + else if (slowest_ras_cas_delay > ((22 << 2) | (2 << 0))) + dram_timing |= DRT_RCD_4; // > 22.5 ns: 4 or more clocks + else if (slowest_ras_cas_delay > (15 << 2)) + dram_timing |= DRT_RCD_3; // > 15.0 ns: 3 clocks + else + dram_timing |= DRT_RCD_2; // <= 15.0 ns: 2 clocks + + /* tRAS, min */ + + PRINTK_DEBUG("slowest_active_to_precharge_delay: %d\n", slowest_active_to_precharge_delay); + // i855 supports only 5, 6, 7 or 8 clocks for tRAS + // 5 clocks ~= 37.6 ns, 6 clocks ~= 45.1 ns, 7 clocks ~= 52.6 ns, 8 clocks ~= 60.1 ns + if (slowest_active_to_precharge_delay > 60) + die("unsupported DIMM tRAS"); // > 52 ns: 8 or more clocks + else if (slowest_active_to_precharge_delay > 52) + dram_timing |= DRT_TRAS_MIN_8; // 46-52 ns: 7 clocks + else if (slowest_active_to_precharge_delay > 45) + dram_timing |= DRT_TRAS_MIN_7; // 46-52 ns: 7 clocks + else if (slowest_active_to_precharge_delay > 37) + dram_timing |= DRT_TRAS_MIN_6; // 38-45 ns: 6 clocks + else + dram_timing |= DRT_TRAS_MIN_5; // < 38 ns: 5 clocks + + /* FIXME: guess work starts here... + * + * Intel refers to DQ turn-arround values for back to calculate the values, + * but i have no idea what this means + */ + + /* + * Back to Back Read-Write command spaceing (DDR, different Rows/Bank) + */ + /* Set to a 3 clock back to back read to write turn around. + * 2 is a good delay if the CAS latency is 2.0 */ + dram_timing &= ~(3 << 28); + if (current_cas_latency == DRT_CAS_2_0) + dram_timing |= (2 << 28); // 2 clocks + else + dram_timing |= (1 << 28); // 3 clocks + + /* + * Back to Back Read-Write command spaceing (DDR, same or different Rows/Bank) + */ + dram_timing &= ~(3 << 26); + if (current_cas_latency == DRT_CAS_2_0) + dram_timing |= (2 << 26); // 5 clocks + else + dram_timing |= (1 << 26); // 6 clocks + + /* + * Back To Back Read-Read commands spacing (DDR, different Rows): + */ + dram_timing &= ~(1 << 25); + dram_timing |= (1 << 25); // 3 clocks + + PRINTK_DEBUG("DRT: %08x\n", dram_timing); + pci_write_config32(NORTHBRIDGE_MMC, DRT, dram_timing); } -static void spd_set_dram_size(const struct mem_controller *ctrl) +static void spd_set_dram_size(const struct mem_controller *ctrl, uint8_t dimm_mask) { - uint32_t drb_reg; + int i; + int total_dram = 0; + uint32_t drb_reg = 0; - drb_reg = 0x20202010; - pci_write_config32(ctrl->d0, 0x40, drb_reg); + for (i = 0; i < DIMM_SOCKETS; i++) { + uint16_t dimm = ctrl->channel0[i]; + struct dimm_size sz; + + if (!(dimm_mask & (1 << i))) { + /* fill values even for not present DIMMs */ + drb_reg |= (total_dram << (i * 16)); + drb_reg |= (total_dram << ((i * 16) + 8)); + + continue; // This DIMM not usable + } + sz = spd_get_dimm_size(dimm); + + total_dram += (1 << (sz.side1 - 28)); + drb_reg |= (total_dram << (i * 16)); + + total_dram += (1 << (sz.side2 - 28)); + drb_reg |= (total_dram << ((i * 16) + 8)); + } + PRINTK_DEBUG("DRB: %08x\n", drb_reg); + pci_write_config32(NORTHBRIDGE_MMC, DRB, drb_reg); } + static void spd_set_dram_pwr_management(const struct mem_controller *ctrl) { uint32_t pwrmg_reg; pwrmg_reg = 0x10f10430; - pci_write_config32(ctrl->d0, 0x68, pwrmg_reg); + pci_write_config32(NORTHBRIDGE_MMC, PWRMG, pwrmg_reg); } static void spd_set_dram_throttle_control(const struct mem_controller *ctrl) { - uint32_t dtc_reg; + uint32_t dtc_reg = 0; - dtc_reg = 0x300aa2ff; - pci_write_config32(ctrl->d0, 0xa0, dtc_reg); + /* DDR SDRAM Throttle Mode (TMODE): + * 0011 = Both Rank and GMCH Thermal Sensor based throttling is enabled. When the external SO- + * DIMM Thermal Sensor is Tripped DDR SDRAM Throttling begins based on the setting in RTT + */ + dtc_reg |= (3 << 28); + + /* Read Counter Based Power Throttle Control (RCTC): + * 0 = 85% + */ + dtc_reg |= (0 << 24); + + /* Write Counter Based Power Throttle Control (WCTC): + * 0 = 85% + */ + dtc_reg |= (0 << 20); + + /* Read Thermal Based Power Throttle Control (RTTC): + * 0xA = 20% + */ + dtc_reg |= (0xA << 16); + + /* Write Thermal Based Power Throttle Control (WTTC): + * 0xA = 20% + */ + dtc_reg |= (0xA << 12); + + /* Counter Based Throttle Lock (CTLOCK): */ + dtc_reg |= (0 << 11); + + /* Thermal Throttle Lock (TTLOCK): */ + dtc_reg |= (0 << 10); + + /* Thermal Power Throttle Control fields Enable: */ + dtc_reg |= (1 << 9); + + /* High Priority Stream Throttling Enable: */ + dtc_reg |= (0 << 8); + + /* Global DDR SDRAM Sampling Window (GDSW): */ + dtc_reg |= 0xff; + PRINTK_DEBUG("DTC: %08x\n", dtc_reg); + pci_write_config32(NORTHBRIDGE_MMC, DTC, dtc_reg); } -#define delay() udelay(200) +static void spd_update(const struct mem_controller *ctrl, u8 reg, u32 new_value) +{ + u32 value1 = pci_read_config32(ctrl->d0, reg); + pci_write_config32(ctrl->d0, reg, new_value); + u32 value2 = pci_read_config32(ctrl->d0, reg); + PRINTK_DEBUG("update reg %02x, old: %08x, new: %08x, read back: %08x\n", reg, value1, new_value, value2); +} /* if ram still doesn't work do this function */ static void spd_set_undocumented_registers(const struct mem_controller *ctrl) { - /* 0:0.0 */ - /* - pci_write_config32(PCI_DEV(0, 0, 0), 0x10, 0xe0000008); - pci_write_config32(PCI_DEV(0, 0, 0), 0x2c, 0x35808086); - pci_write_config32(PCI_DEV(0, 0, 0), 0x48, 0xfec10000); - pci_write_config32(PCI_DEV(0, 0, 0), 0x50, 0x00440100); + spd_update(ctrl, 0x74, 0x00000001); + spd_update(ctrl, 0x78, 0x001fe974); + spd_update(ctrl, 0x80, 0x00af0039); + spd_update(ctrl, 0x84, 0x0000033c); + spd_update(ctrl, 0x88, 0x00000010); - pci_write_config32(PCI_DEV(0, 0, 0), 0x58, 0x11111000); + spd_update(ctrl, 0xc0, 0x00000003); +} - pci_write_config16(PCI_DEV(0, 0, 0), 0x52, 0x0002); - */ - pci_write_config16(PCI_DEV(0, 0, 0), 0x52, 0x0044); - /* - pci_write_config16(PCI_DEV(0, 0, 0), 0x52, 0x0000); - */ - pci_write_config32(PCI_DEV(0, 0, 0), 0x58, 0x33333000); - pci_write_config32(PCI_DEV(0, 0, 0), 0x5c, 0x33333333); - /* - pci_write_config32(PCI_DEV(0, 0, 0), 0x60, 0x0000390a); - pci_write_config32(PCI_DEV(0, 0, 0), 0x74, 0x02006056); - pci_write_config32(PCI_DEV(0, 0, 0), 0x78, 0x00800001); - */ - pci_write_config32(PCI_DEV(0, 0, 0), 0xa8, 0x00000001); +static void northbridge_set_registers(void) +{ + u16 value; + int video_memory = 0; - pci_write_config32(PCI_DEV(0, 0, 0), 0xbc, 0x00001020); - /* - pci_write_config32(PCI_DEV(0, 0, 0), 0xfc, 0x00000109); - */ + printk(BIOS_DEBUG, "Setting initial Northbridge registers....\n"); - /* 0:0.1 */ - pci_write_config32(ctrl->d0, 0x74, 0x00000001); - pci_write_config32(ctrl->d0, 0x78, 0x001fe974); - pci_write_config32(ctrl->d0, 0x80, 0x00af0039); - pci_write_config32(ctrl->d0, 0x84, 0x0000033c); - pci_write_config32(ctrl->d0, 0x88, 0x00000010); - pci_write_config32(ctrl->d0, 0x98, 0xde5a868c); - pci_write_config32(ctrl->d0, 0x9c, 0x404e0046); - pci_write_config32(ctrl->d0, 0xa8, 0x00020e1a); - pci_write_config32(ctrl->d0, 0xb4, 0x0044cdac); - pci_write_config32(ctrl->d0, 0xb8, 0x000055d4); - pci_write_config32(ctrl->d0, 0xbc, 0x024acd38); - pci_write_config32(ctrl->d0, 0xc0, 0x00000003); + /* Set the value for Fixed DRAM Hole Control Register */ + pci_write_config8(NORTHBRIDGE, FDHC, 0x00); - /* 0:0.3 */ - /* - pci_write_config32(PCI_DEV(0, 0, 3), 0x2c, 0x35858086); - pci_write_config32(PCI_DEV(0, 0, 3), 0x44, 0x11110000); - pci_write_config32(PCI_DEV(0, 0, 3), 0x48, 0x09614a3c); - pci_write_config32(PCI_DEV(0, 0, 3), 0x4c, 0x4b09604a); - pci_write_config32(PCI_DEV(0, 0, 3), 0x50, 0x00000962); - pci_write_config32(PCI_DEV(0, 0, 3), 0x5c, 0x0b023331); - pci_write_config32(PCI_DEV(0, 0, 3), 0x6c, 0x0000402e); - pci_write_config32(PCI_DEV(0, 0, 3), 0x78, 0xe7c70f7f); - pci_write_config32(PCI_DEV(0, 0, 3), 0x7c, 0x0284007f); - pci_write_config32(PCI_DEV(0, 0, 3), 0x84, 0x000000ef); - */ + /* Set the value for Programable Attribute Map Registers + * Ideally, this should be R/W for as many ranges as possible. + */ + pci_write_config8(NORTHBRIDGE, PAM0, 0x30); + pci_write_config8(NORTHBRIDGE, PAM1, 0x33); + pci_write_config8(NORTHBRIDGE, PAM2, 0x33); + pci_write_config8(NORTHBRIDGE, PAM3, 0x33); + pci_write_config8(NORTHBRIDGE, PAM4, 0x33); + pci_write_config8(NORTHBRIDGE, PAM5, 0x33); + pci_write_config8(NORTHBRIDGE, PAM6, 0x33); - /* - pci_write_config16(PCI_DEV(0, 0, 3), 0xc0, 0x0200); - pci_write_config16(PCI_DEV(0, 0, 3), 0xc0, 0x0400); - */ + /* Set the value for System Management RAM Control Register */ + pci_write_config8(NORTHBRIDGE, SMRAM, 0x02); - /* - pci_write_config32(PCI_DEV(0, 0, 3), 0xc4, 0x00000000); - pci_write_config32(PCI_DEV(0, 0, 3), 0xd8, 0xff00c308); - pci_write_config32(PCI_DEV(0, 0, 3), 0xdc, 0x00000025); - pci_write_config32(PCI_DEV(0, 0, 3), 0xe0, 0x001f002a); - pci_write_config32(PCI_DEV(0, 0, 3), 0xe4, 0x009f0098); - pci_write_config32(PCI_DEV(0, 0, 3), 0xec, 0x00000400); - pci_write_config32(PCI_DEV(0, 0, 3), 0xf0, 0xc0000000); - */ + /* Set the value for GMCH Control Register #1 */ + switch (CONFIG_VIDEO_MB) { + case 1: /* 1M of memory */ + video_memory = 0x1; + break; + case 4: /* 4M of memory */ + video_memory = 0x2; + break; + case 8: /* 8M of memory */ + video_memory = 0x3; + break; + case 16: /* 16M of memory */ + video_memory = 0x4; + break; + case 32: /* 32M of memory */ + video_memory = 0x5; + break; + default: /* No memory */ + pci_write_config16(NORTHBRIDGE, GMC, pci_read_config16(NORTHBRIDGE, GMC) | 1); + video_memory = 0x0; + } + + value = pci_read_config16(NORTHBRIDGE, GGC); + value |= video_memory << 4; + if (video_memory == 0) { + value &= ~(1 < 1); + } else + value |= (1 < 1); + pci_write_config16(NORTHBRIDGE, GGC, value); + + /* AGPCMD: disable AGP, Data-Rate: 1x */ + pci_write_config32(NORTHBRIDGE, AGPCMD, 0x00000001); + + pci_write_config8(NORTHBRIDGE, AMTT, 0x20); + pci_write_config8(NORTHBRIDGE, LPTT, 0x10); + + printk(BIOS_DEBUG, "Initial Northbridge registers have been set.\n"); } static void sdram_set_spd_registers(const struct mem_controller *ctrl) { - spd_set_row_attributes(ctrl); - spd_set_dram_controller_mode(ctrl); - spd_set_dram_timing(ctrl); - spd_set_dram_size(ctrl); - spd_set_dram_pwr_management(ctrl); - spd_set_dram_throttle_control(ctrl); - spd_set_undocumented_registers(ctrl); -} + uint8_t dimm_mask; -static void ram_command(const struct mem_controller *ctrl, - uint8_t command, - uint32_t addr) -{ - uint32_t drc_reg; + PRINTK_DEBUG("Reading SPD data...\n"); - drc_reg = pci_read_config32(ctrl->d0, 0x70); - drc_reg &= ~(7 << 4); - drc_reg |= (command << 4); - pci_write_config8(ctrl->d0, 0x70, drc_reg); - read32(addr); -} + dimm_mask = spd_get_supported_dimms(ctrl); -static void ram_command_mrs(const struct mem_controller *ctrl, - uint8_t command, - uint32_t mode, - uint32_t addr) -{ - uint32_t drc_reg; - uint32_t adjusted_mode; + if (dimm_mask == 0) { + print_debug("No usable memory for this controller\n"); + } else { + PRINTK_DEBUG("DIMM MASK: %02x\n", dimm_mask); - drc_reg = pci_read_config32(ctrl->d0, 0x70); - drc_reg &= ~(7 << 4); - drc_reg |= (command << 4); - pci_write_config8(ctrl->d0, 0x70, drc_reg); - /* Host address lines [13:3] map to DIMM address lines [11, 9:0] */ - adjusted_mode = ((mode & 0x800) << (13 - 11)) | ((mode & 0x3ff) << (12 - 9)); - print_debug("Setting mode: "); - print_debug_hex32(adjusted_mode + addr); - print_debug("\n"); - read32(adjusted_mode + addr); -} - -static void set_initialize_complete(const struct mem_controller *ctrl) -{ - uint32_t drc_reg; - - drc_reg = pci_read_config32(ctrl->d0, 0x70); - drc_reg |= (1 << 29); - pci_write_config32(ctrl->d0, 0x70, drc_reg); -} - -static void sdram_enable(int controllers, const struct mem_controller *ctrl) -{ - int i; - uint32_t rank1 = (1 << 30) / 2; - print_debug("Ram enable 1\n"); - delay(); - delay(); - - print_debug("Ram enable 2\n"); - ram_command(ctrl, 1, 0); - ram_command(ctrl, 1, rank1); - delay(); - delay(); - - print_debug("Ram enable 3\n"); - ram_command(ctrl, 2, 0); - ram_command(ctrl, 2, rank1); - delay(); - delay(); - - print_debug("Ram enable 4\n"); - ram_command_mrs(ctrl, 4, SDRAM_EXTMODE_DLL_ENABLE, 0); - ram_command_mrs(ctrl, 4, SDRAM_EXTMODE_DLL_ENABLE, rank1); - delay(); - delay(); - - print_debug("Ram enable 5\n"); - ram_command_mrs(ctrl, 3, VG85X_MODE | SDRAM_MODE_DLL_RESET, 0); - ram_command_mrs(ctrl, 3, VG85X_MODE | SDRAM_MODE_DLL_RESET, rank1); - - print_debug("Ram enable 6\n"); - ram_command(ctrl, 2, 0); - ram_command(ctrl, 2, rank1); - delay(); - delay(); - - print_debug("Ram enable 7\n"); - for(i = 0; i < 8; i++) { - ram_command(ctrl, 6, 0); - ram_command(ctrl, 6, rank1); - delay(); - delay(); + spd_set_row_attributes(ctrl, dimm_mask); + spd_set_dram_controller_mode(ctrl, dimm_mask); + spd_set_dram_timing(ctrl, dimm_mask); + spd_set_dram_size(ctrl, dimm_mask); + spd_set_dram_pwr_management(ctrl); + spd_set_dram_throttle_control(ctrl); + spd_set_undocumented_registers(ctrl); } - print_debug("Ram enable 8\n"); - ram_command_mrs(ctrl, 3, VG85X_MODE | SDRAM_MODE_NORMAL, 0); - ram_command_mrs(ctrl, 3, VG85X_MODE | SDRAM_MODE_NORMAL, rank1); - - print_debug("Ram enable 9\n"); - ram_command(ctrl, 7, 0); - ram_command(ctrl, 7, rank1); - delay(); - delay(); - - print_debug("Ram enable 9\n"); - set_initialize_complete(ctrl); - - delay(); - delay(); - delay(); - - print_debug("After configuration:\n"); - /* dump_pci_devices(); */ - - /* - print_debug("\n\n***** RAM TEST *****\n"); - ram_check(0, 0xa0000); - ram_check(0x100000, 0x40000000); - */ + /* Setup Initial Northbridge Registers */ + northbridge_set_registers(); } + diff --git a/src/northbridge/intel/i855/raminit.h b/src/northbridge/intel/i855/raminit.h index dbd0be6927..1f1b34d14b 100644 --- a/src/northbridge/intel/i855/raminit.h +++ b/src/northbridge/intel/i855/raminit.h @@ -18,11 +18,19 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#ifndef RAMINIT_H -#define RAMINIT_H +#ifndef NORTHBRIDGE_INTEL_I855_RAMINIT_H +#define NORTHBRIDGE_INTEL_I855_RAMINIT_H +/* i855 Northbridge PCI device */ +#define NORTHBRIDGE PCI_DEV(0, 0, 0) +#define NORTHBRIDGE_MMC PCI_DEV(0, 0, 1) + +/* The i855 supports max. 2 dual-sided SO-DIMMs. */ #define DIMM_SOCKETS 2 +/* DIMM0 is at 0x50, DIMM1 is at 0x51. */ +#define DIMM_SPD_BASE 0x50 + struct mem_controller { device_t d0; uint16_t channel0[DIMM_SOCKETS]; @@ -31,4 +39,4 @@ struct mem_controller { void sdram_initialize(int controllers, const struct mem_controller *ctrl); -#endif /* RAMINIT_H */ +#endif /* NORTHBRIDGE_INTEL_I855_RAMINIT_H */