/*
* This file is part of the coreboot project.
*
- * Copyright (C) 2007 Uwe Hermann <uwe@hermann-uwe.de>
+ * Copyright (C) 2007-2008 Uwe Hermann <uwe@hermann-uwe.de>
+ * Copyright (C) 2010 Keith Hui <buurin@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
*/
#include <spd.h>
-#include <sdram_mode.h>
#include <delay.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <arch/io.h>
+#include <arch/romcc_io.h>
+#include <device/pci_def.h>
+#include <console/console.h>
#include "i440bx.h"
+#include "raminit.h"
/*-----------------------------------------------------------------------------
Macros and definitions.
-----------------------------------------------------------------------------*/
-/* Uncomment this to enable debugging output. */
-#define DEBUG_RAM_SETUP 1
+#define NB PCI_DEV(0, 0, 0)
/* Debugging macros. */
-#if defined(DEBUG_RAM_SETUP)
-#define PRINT_DEBUG(x) print_debug(x)
-#define PRINT_DEBUG_HEX8(x) print_debug_hex8(x)
-#define PRINT_DEBUG_HEX16(x) print_debug_hex16(x)
-#define PRINT_DEBUG_HEX32(x) print_debug_hex32(x)
-#define DUMPNORTH() dump_pci_device(PCI_DEV(0, 0, 0))
+#if CONFIG_DEBUG_RAM_SETUP
+#define PRINT_DEBUG(x...) printk(BIOS_DEBUG, x)
+#define DUMPNORTH() dump_pci_device(NB)
#else
-#define PRINT_DEBUG(x)
-#define PRINT_DEBUG_HEX8(x)
-#define PRINT_DEBUG_HEX16(x)
-#define PRINT_DEBUG_HEX32(x)
+#define PRINT_DEBUG(x...)
#define DUMPNORTH()
#endif
};
/* Table format: register, bitmask, value. */
-static const long register_values[] = {
+static const u8 register_values[] = {
/* NBXCFG - NBX Configuration Register
* 0x50 - 0x53
*
* 0 = A7# is sampled asserted (i.e., 0)
* [01:00] Reserved
*/
- // TODO
NBXCFG + 0, 0x00, 0x0c,
- // NBXCFG + 1, 0x00, 0xa0,
+ // TODO: Bit 15 should be 0 for multiprocessor boards
NBXCFG + 1, 0x00, 0x80,
NBXCFG + 2, 0x00, 0x00,
NBXCFG + 3, 0x00, 0xff,
*
* [7:6] Reserved
* [5:5] Module Mode Configuration (MMCONFIG)
- * TODO
+ * The combination of SDRAMPWR and this bit (which is set by an
+ * external strapping option) determine how CKE works.
+ * SDRAMPWR MMCONFIG
+ * 0 0 = 3 DIMM, CKE0[5:0] driven
+ * X 1 = 3 DIMM, CKE0 only
+ * 1 0 = 4 DIMM, GCKE only
* [4:3] DRAM Type (DT)
* 00 = EDO
* 01 = SDRAM
* 10 = Write Only (Writes to DRAM, reads to memory mapped I/O space)
* 11 = Read/Write (all access goes to DRAM)
*/
- // TODO
- PAM0, 0x00, 0x00,
- PAM1, 0x00, 0x00,
- PAM2, 0x00, 0x00,
- PAM3, 0x00, 0x00,
- PAM4, 0x00, 0x00,
- PAM5, 0x00, 0x00,
- PAM6, 0x00, 0x00,
+
+ /*
+ * Map all legacy regions to RAM (read/write). This is required if
+ * you want to use the RAM area from 768 KB - 1 MB. If the PAM
+ * registers are not set here appropriately, the RAM in that region
+ * will not be accessible, thus a RAM check of it will also fail.
+ *
+ * TODO: This was set in sdram_set_spd_registers().
+ * Test if it still works when set here.
+ */
+ PAM0, 0x00, 0x30,
+ PAM1, 0x00, 0x33,
+ PAM2, 0x00, 0x33,
+ PAM3, 0x00, 0x33,
+ PAM4, 0x00, 0x33,
+ PAM5, 0x00, 0x33,
+ PAM6, 0x00, 0x33,
/* DRB[0:7] - DRAM Row Boundary Registers
* 0x60 - 0x67
*
* An array of 8 byte registers, which hold the ending memory address
- * assigned to each pair of DIMMs, in 8MB granularity.
+ * assigned to each pair of DIMMs, in 8MB granularity.
*
* 0x60 DRB0 = Total memory in row0 (in 8 MB)
* 0x61 DRB1 = Total memory in row0+1 (in 8 MB)
* Sets the row page size for SDRAM. For EDO memory, the page
* size is fixed at 2 KB.
*
- * [15:0] Page Size (PS)
- * TODO
+ * Bits[1:0] Page Size
+ * 00 2 KB
+ * 01 4 KB
+ * 10 8 KB
+ * 11 Reserved
+ *
+ * RPS bits Corresponding DRB register
+ * [01:00] DRB[0], row 0
+ * [03:02] DRB[1], row 1
+ * [05:04] DRB[2], row 2
+ * [07:06] DRB[3], row 3
+ * [09:08] DRB[4], row 4
+ * [11:10] DRB[5], row 5
+ * [13:12] DRB[6], row 6
+ * [15:14] DRB[7], row 7
*/
- // TODO
- RPS + 0, 0x00, 0x00,
- RPS + 1, 0x00, 0x00,
+ /* Power on defaults to 2KB. Will be set later. */
+ // RPS + 0, 0x00, 0x00,
+ // RPS + 1, 0x00, 0x00,
/* SDRAMC - SDRAM Control Register
* 0x76 - 0x77
* [09:08] Idle/Pipeline DRAM Leadoff Timing (IPDLT)
* 00 = Illegal
* 01 = Add a clock delay to the lead-off clock count
- * 10 = Illegal
- * 11 = Illegal
+ * 1x = Illegal
* [07:05] SDRAM Mode Select (SMS)
* 000 = Normal SDRAM Operation (default)
* 001 = NOP Command Enable
* 0 = 3 clocks of RAS# precharge
* 1 = 2 clocks of RAS# precharge
*/
- SDRAMC + 0, 0x00, 0x00,
- SDRAMC + 0, 0x00, 0x00,
+#if CONFIG_SDRAMPWR_4DIMM
+ SDRAMC + 0, 0x00, 0x10, /* The board has 4 DIMM slots. */
+#else
+ SDRAMC + 0, 0x00, 0x00, /* The board has 3 DIMM slots. */
+#endif
+ SDRAMC + 1, 0x00, 0x00,
/* PGPOL - Paging Policy Register
* 0x78 - 0x79
*
* [15:08] Banks per Row (BPR)
- * TODO
+ * Each bit in this field corresponds to one row of the memory
+ * array. Bit 15 corresponds to row 7 while bit 8 corresponds
+ * to row 0. Bits for empty rows are "don't care".
* 0 = 2 banks
* 1 = 4 banks
* [07:05] Reserved
* 0111 = 32 clocks
* 1xxx = Infinite (pages are not closed for idle condition)
*/
- // TODO
PGPOL + 0, 0x00, 0x00,
PGPOL + 1, 0x00, 0xff,
/* Enable normal refresh and the gated clock. */
// TODO: Only do this later?
// PMCR, 0x00, 0x14,
- // PMCR, 0x00, 0x10,
PMCR, 0x00, 0x00,
+
+ /* Enable SCRR.SRRAEN and let BX choose the SRR. */
+ SCRR + 1, 0x00, 0x10,
};
/*-----------------------------------------------------------------------------
/**
* Send the specified RAM command to all DIMMs.
*
- * @param Memory controller
- * @param TODO
- * @param TODO
+ * @param command The RAM command to send to the DIMM(s).
*/
-static void do_ram_command(const struct mem_controller *ctrl,
- uint32_t command, uint32_t addr_offset)
+static void do_ram_command(u32 command)
{
- int i;
- uint16_t reg;
-
- /* TODO: Support for multiple DIMMs. */
+ int i, caslatency;
+ u8 dimm_start, dimm_end;
+ u16 reg16;
+ u32 addr, addr_offset;
/* Configure the RAM command. */
- reg = pci_read_config16(ctrl->d0, SDRAMC);
- reg &= 0xff1f; /* Clear bits 7-5. */
- reg |= (uint16_t) (command << 5);
- pci_write_config16(ctrl->d0, SDRAMC, reg);
-
- /* RAM_COMMAND_NORMAL affects only the memory controller and
- doesn't need to be "sent" to the DIMMs. */
- /* if (command == RAM_COMMAND_NORMAL) return; */
-
- PRINT_DEBUG(" Sending RAM command 0x");
- PRINT_DEBUG_HEX16(reg);
- PRINT_DEBUG(" to 0x");
- PRINT_DEBUG_HEX32(0 + addr_offset); // FIXME
- PRINT_DEBUG("\r\n");
-
- /* Read from (DIMM start address + addr_offset). */
- read32(0 + addr_offset); // FIXME
+ reg16 = pci_read_config16(NB, SDRAMC);
+ reg16 &= 0xff1f; /* Clear bits 7-5. */
+ reg16 |= (u16) (command << 5); /* Write command into bits 7-5. */
+ pci_write_config16(NB, SDRAMC, reg16);
+
+ /*
+ * RAM_COMMAND_NORMAL affects only the memory controller and
+ * doesn't need to be "sent" to the DIMMs.
+ */
+ if (command == RAM_COMMAND_NORMAL)
+ return;
+
+ /* Send the RAM command to each row of memory. */
+ dimm_start = 0;
+ for (i = 0; i < (DIMM_SOCKETS * 2); i++) {
+ addr_offset = 0;
+ caslatency = 3; /* TODO: Dynamically get CAS latency later. */
+ if (command == RAM_COMMAND_MRS) {
+ /*
+ * MAA[12:11,9:0] must be inverted when sent to DIMM
+ * 2 or 3 (no inversion if sent to DIMM 0 or 1).
+ */
+ if ((i >= 0 && i <= 3) && caslatency == 3)
+ addr_offset = 0x1d0;
+ if ((i >= 4 && i <= 7) && caslatency == 3)
+ addr_offset = 0x1e28;
+ if ((i >= 0 && i <= 3) && caslatency == 2)
+ addr_offset = 0x150;
+ if ((i >= 4 && i <= 7) && caslatency == 2)
+ addr_offset = 0x1ea8;
+ }
+
+ dimm_end = pci_read_config8(NB, DRB + i);
+
+ addr = (dimm_start * 8 * 1024 * 1024) + addr_offset;
+ if (dimm_end > dimm_start) {
+#if 0
+ PRINT_DEBUG(" Sending RAM command 0x%04x to 0x%08x\n",
+ reg16, addr);
+#endif
+
+ read32(addr);
+ }
+
+ /* Set the start of the next DIMM. */
+ dimm_start = dimm_end;
+ }
+}
+
+static void set_dram_buffer_strength(void)
+{
+ /* To give some breathing room for romcc,
+ * mbsc0 doubles as drb
+ * mbsc1 doubles as drb1
+ * mbfs0 doubles as i and reg
+ */
+ uint8_t mbsc0,mbsc1,mbsc3,mbsc4,mbfs0,mbfs2,fsb;
+
+ /* Tally how many rows between rows 0-3 and rows 4-7 are populated.
+ * This determines how to program MBFS and MBSC.
+ */
+ uint8_t dimm03 = 0;
+ uint8_t dimm47 = 0;
+
+ mbsc0 = 0;
+ for (mbfs0 = DRB0; mbfs0 <= DRB7; mbfs0++) {
+ mbsc1 = pci_read_config8(NB, mbfs0);
+ if (mbsc0 != mbsc1) {
+ if (mbfs0 <= DRB3) {
+ dimm03++;
+ } else {
+ dimm47++;
+ }
+ mbsc0 = mbsc1;
+ }
+ }
+
+ /* Algorithm bitmap for programming MBSC[39:0] and MBFS[23:0].
+ *
+ * The 440BX datasheet says buffer frequency is independent from bus
+ * frequency and mismatch both ways are possible. This is how it is
+ * programmed in the ASUS P2B-LS mainboard.
+ *
+ * There are four main conditions to check when programming DRAM buffer
+ * frequency and strength:
+ *
+ * a: >2 rows populated across DIMM0,1
+ * b: >2 rows populated across DIMM2,3
+ * c: >4 rows populated across all DIMM slots
+ * and either one of:
+ * 1: NBXCFG[13] strapped as 100MHz, or
+ * 6: NBXCFG[13] strapped as 66MHz
+ *
+ * CKE0/FENA ----------------------------------------------------------+
+ * CKE1/GCKE -------------------[ MBFS ]------------------------+|
+ * DQMA/CASA[764320]# ----------[ 0 = 66MHz ]-----------------------+||
+ * DQMB1/CASB1# ----------------[ 1 = 100MHz ]----------------------+|||
+ * DQMB5/CASB5# ---------------------------------------------------+||||
+ * DQMA1/CASA1# --------------------------------------------------+|||||
+ * DQMA5/CASA5# -------------------------------------------------+||||||
+ * CSA0-5#,CSB0-5# ----------------------------------------++++++|||||||
+ * CSA6#/CKE2# -------------------------------------------+|||||||||||||
+ * CSB6#/CKE4# ------------------------------------------+||||||||||||||
+ * CSA7#/CKE3# -----------------------------------------+|||||||||||||||
+ * CSB7#/CKE5# ----------------------------------------+||||||||||||||||
+ * MECC[7:0] #2/#1 (100MHz) -------------------------++|||||||||||||||||
+ * MD[63:0] #2/#1 (100MHz) ------------------------++|||||||||||||||||||
+ * MAB[12:11,9:0]#,MAB[13,10],WEB#,SRASB#,SCASB# -+|||||||||||||||||||||
+ * MAA[13:0],WEA#,SRASA#,SCASA# -----------------+||||||||||||||||||||||
+ * Reserved ------------------------------------+|||||||||||||||||||||||
+ * ||||||||||||||||||||||||
+ * 3 32 21 10 0 * 2 21 10 0
+ * 9876543210987654321098765432109876543210 * 321098765432109876543210
+ * a 10------------------------1010---------- * -1---------------11----- a
+ *!a 11------------------------1111---------- * -0---------------00----- !a
+ * b --10--------------------------1010------ * --1----------------11--- b
+ *!b --11--------------------------1111------ * --0----------------00--- !b
+ * c ----------------------------------1100-- * ----------------------1- c
+ *!c ----------------------------------1011-- * ----------------------0- !c
+ * 1 ----1010101000000000000000------------00 * ---11111111111111----1-0 1
+ * 6 ----000000000000000000000010101010----00 * ---1111111111111100000-0 6
+ * | | | | | | | | | | ||||||| | | | | | |
+ * | | | | | | | | | | ||||||| | | | | | +- CKE0/FENA
+ * | | | | | | | | | | ||||||| | | | | +--- CKE1/GCKE
+ * | | | | | | | | | | ||||||| | | | +----- DQMA/CASA[764320]#
+ * | | | | | | | | | | ||||||| | | +------- DQMB1/CASB1#
+ * | | | | | | | | | | ||||||| | +--------- DQMB5/CASB5#
+ * | | | | | | | | | | ||||||| +----------- DQMA1/CASA1#
+ * | | | | | | | | | | ||||||+------------- DQMA5/CASA5#
+ * | | | | | | | | | | ++++++-------------- CSA0-5#,CSB0-5# [ 0=1x;1=2x ]
+ * | | | | | | | | | +--------------------- CSA6#/CKE2#
+ * | | | | | | | | +---[ MBSC ]------ CSB6#/CKE4#
+ * | | | | | | | +-----[ 00 = 1x ]------ CSA7#/CKE3#
+ * | | | | | | +-------[ 01 invalid ]------ CSB7#/CKE5#
+ * | | | | | +---------[ 10 = 2x ]------ MECC[7:0] #1 (2x)
+ * | | | | +-----------[ 11 = 3x ]------ MECC[7:0] #2 (2x)
+ * | | | +--------------------------------- MD[63:0] #1 (2x)
+ * | | +----------------------------------- MD[63:0] #2 (2x)
+ * | +------------------------------------- MAB[12:11,9:0]#,MAB[13,10],WEB#,SRASB#,SCASB#
+ * +--------------------------------------- MAA[13:0],WEA#,SRASA#,SCASA#
+ * MBSC[47:40] and MBFS[23] are reserved.
+ *
+ * This algorithm is checked against the ASUS P2B-LS (which has
+ * 4 DIMM slots) factory BIOS.
+ * Therefore it assumes a board with 4 slots, and will need testing
+ * on boards with 3 DIMM slots.
+ */
+
+ mbsc0 = 0x80;
+ mbsc1 = 0x2a;
+ mbfs2 = 0x1f;
+ if (pci_read_config8(NB, NBXCFG + 1) & 0x30) {
+ fsb = 66;
+ mbsc3 = 0x00;
+ mbsc4 = 0x00;
+ mbfs0 = 0x80;
+ } else {
+ fsb = 100;
+ mbsc3 = 0xa0;
+ mbsc4 = 0x0a;
+ mbfs0 = 0x84;
+ }
+
+ if (dimm03 > 2) {
+ mbsc4 = mbsc4 | 0x80;
+ mbsc1 = mbsc1 | 0x28;
+ mbfs2 = mbfs2 | 0x40;
+ mbfs0 = mbfs0 | 0x60;
+ } else {
+ mbsc4 = mbsc4 | 0xc0;
+ if (fsb == 100) {
+ mbsc1 = mbsc1 | 0x3c;
+ }
+ }
+ if (dimm47 > 2) {
+ mbsc4 = mbsc4 | 0x20;
+ mbsc1 = mbsc1 | 0x02;
+ mbsc0 = mbsc0 | 0x80;
+ mbfs2 = mbfs2 | 0x20;
+ mbfs0 = mbfs0 | 0x18;
+ } else {
+ mbsc4 = mbsc4 | 0x30;
+ if (fsb == 100) {
+ mbsc1 = mbsc1 | 0x03;
+ mbsc0 = mbsc0 | 0xc0;
+ }
+ }
+ if ((dimm03 + dimm47) > 4) {
+ mbsc0 = mbsc0 | 0x30;
+ mbfs0 = mbfs0 | 0x02;
+ } else {
+ mbsc0 = mbsc0 | 0x2c;
+ }
+
+ pci_write_config8(NB, MBSC + 0, mbsc0);
+ pci_write_config8(NB, MBSC + 1, mbsc1);
+ pci_write_config8(NB, MBSC + 2, 0x00);
+ pci_write_config8(NB, MBSC + 3, mbsc3);
+ pci_write_config8(NB, MBSC + 4, mbsc4);
+ pci_write_config8(NB, MBFS + 0, mbfs0);
+ pci_write_config8(NB, MBFS + 1, 0xff);
+ pci_write_config8(NB, MBFS + 2, mbfs2);
}
/*-----------------------------------------------------------------------------
DIMM-independant configuration functions.
-----------------------------------------------------------------------------*/
-/**
- * TODO.
- *
- * @param Memory controller
- */
-static void spd_enable_refresh(const struct mem_controller *ctrl)
+static void spd_enable_refresh(void)
{
int i, value;
uint8_t reg;
- reg = pci_read_config8(ctrl->d0, DRAMC);
+ reg = pci_read_config8(NB, DRAMC);
for (i = 0; i < DIMM_SOCKETS; i++) {
- value = spd_read_byte(ctrl->channel0[i], SPD_REFRESH);
+ value = spd_read_byte(DIMM0 + i, SPD_REFRESH);
if (value < 0)
continue;
reg = (reg & 0xf8) | refresh_rate_map[(value & 0x7f)];
- PRINT_DEBUG(" Enabling refresh (DRAMC = 0x");
- PRINT_DEBUG_HEX8(reg);
- PRINT_DEBUG(") for DIMM ");
- PRINT_DEBUG_HEX8(i);
- PRINT_DEBUG("\r\n");
+ PRINT_DEBUG(" Enabling refresh (DRAMC = 0x%02x) for DIMM %02x\n", reg, i);
}
- pci_write_config8(ctrl->d0, DRAMC, reg);
+ pci_write_config8(NB, DRAMC, reg);
}
/*-----------------------------------------------------------------------------
Public interface.
-----------------------------------------------------------------------------*/
-/**
- * TODO.
- *
- * @param Memory controller
- */
-static void sdram_set_registers(const struct mem_controller *ctrl)
+void sdram_set_registers(void)
{
int i, max;
uint8_t reg;
- PRINT_DEBUG("Northbridge prior to SDRAM init:\r\n");
+ PRINT_DEBUG("Northbridge prior to SDRAM init:\n");
DUMPNORTH();
- max = sizeof(register_values) / sizeof(register_values[0]);
+ max = ARRAY_SIZE(register_values);
/* Set registers as specified in the register_values[] array. */
for (i = 0; i < max; i += 3) {
- reg = pci_read_config8(ctrl->d0, register_values[i]);
+ reg = pci_read_config8(NB, register_values[i]);
reg &= register_values[i + 1];
reg |= register_values[i + 2] & ~(register_values[i + 1]);
- pci_write_config8(ctrl->d0, register_values[i], reg);
-
- PRINT_DEBUG(" Set register 0x");
- PRINT_DEBUG_HEX8(register_values[i]);
- PRINT_DEBUG(" to 0x");
- PRINT_DEBUG_HEX8(reg);
- PRINT_DEBUG("\r\n");
+ pci_write_config8(NB, register_values[i], reg);
+#if 0
+ PRINT_DEBUG(" Set register 0x%02x to 0x%02x\n",
+ register_values[i], reg);
+#endif
}
}
-/**
- * TODO.
- *
- * @param Memory controller
- */
-static void sdram_set_spd_registers(const struct mem_controller *ctrl)
+struct dimm_size {
+ u32 side1;
+ u32 side2;
+};
+
+static struct dimm_size spd_get_dimm_size(unsigned int device)
{
- /* TODO: Don't hardcode the values here, get info via SPD. */
+ struct dimm_size sz;
+ int i, module_density, dimm_banks;
+ sz.side1 = 0;
+ module_density = spd_read_byte(device, SPD_DENSITY_OF_EACH_ROW_ON_MODULE);
+ dimm_banks = spd_read_byte(device, SPD_NUM_DIMM_BANKS);
+
+ /* Find the size of side1. */
+ /* Find the larger value. The larger value is always side1. */
+ for (i = 512; i >= 0; i >>= 1) {
+ if ((module_density & i) == i) {
+ sz.side1 = i;
+ break;
+ }
+ }
- /* Map all legacy regions to RAM (read/write). This is required if
- * you want to use the RAM area from 768 KB - 1 MB. If the PAM
- * registers are not set here appropriately, the RAM in that region
- * will not be accessible, thus a RAM check of it will also fail.
+ /* Set to 0 in case it's single sided. */
+ sz.side2 = 0;
+
+ /* Test if it's a dual-sided DIMM. */
+ if (dimm_banks > 1) {
+ /* Test if there's a second value. If so it's asymmetrical. */
+ if (module_density != i) {
+ /*
+ * Find second value, picking up where we left off.
+ * i >>= 1 done initially to make sure we don't get
+ * the same value again.
+ */
+ for (i >>= 1; i >= 0; i >>= 1) {
+ if (module_density == (sz.side1 | i)) {
+ sz.side2 = i;
+ break;
+ }
+ }
+ /* If not, it's symmetrical. */
+ } else {
+ sz.side2 = sz.side1;
+ }
+ }
+
+ /*
+ * SPD byte 31 is the memory size divided by 4 so we
+ * need to muliply by 4 to get the total size.
+ */
+ sz.side1 *= 4;
+ sz.side2 *= 4;
+
+ /* It is possible to partially use larger then supported
+ * modules by setting them to a supported size.
*/
- pci_write_config8(ctrl->d0, PAM0, 0x30);
- pci_write_config8(ctrl->d0, PAM1, 0x33);
- pci_write_config8(ctrl->d0, PAM2, 0x33);
- pci_write_config8(ctrl->d0, PAM3, 0x33);
- pci_write_config8(ctrl->d0, PAM4, 0x33);
- pci_write_config8(ctrl->d0, PAM5, 0x33);
- pci_write_config8(ctrl->d0, PAM6, 0x33);
-
- /* TODO: Set DRB0-DRB7. */
- /* Currently this is hardcoded to one 64 MB DIMM in slot 0. */
- pci_write_config8(ctrl->d0, DRB0, 0x08);
- pci_write_config8(ctrl->d0, DRB1, 0x08);
- pci_write_config8(ctrl->d0, DRB2, 0x08);
- pci_write_config8(ctrl->d0, DRB3, 0x08);
- pci_write_config8(ctrl->d0, DRB4, 0x08);
- pci_write_config8(ctrl->d0, DRB5, 0x08);
- pci_write_config8(ctrl->d0, DRB6, 0x08);
- pci_write_config8(ctrl->d0, DRB7, 0x08);
-
- /* TODO: Set DRAMC. Don't enable refresh for now. */
- pci_write_config8(ctrl->d0, DRAMC, 0x08);
-
- /* TODO: Set RPS. */
- pci_write_config16(ctrl->d0, RPS, 0x0001);
-
- /* TODO: Set SDRAMC. */
- // pci_write_config16(ctrl->d0, SDRAMC, 0x010f); // FIXME?
- pci_write_config16(ctrl->d0, SDRAMC, 0x0003); // FIXME?
-
- /* TODO: Set PGPOL. */
- // pci_write_config16(ctrl->d0, PGPOL, 0x0107);
- pci_write_config16(ctrl->d0, PGPOL, 0x0123);
-
- /* TODO: Set NBXCFG. */
- // pci_write_config32(ctrl->d0, NBXCFG, 0x0100220c); // FIXME?
- pci_write_config32(ctrl->d0, NBXCFG, 0xff00800c);
+ if(sz.side1 > 128) {
+ PRINT_DEBUG("Side1 was %dMB but only 128MB will be used.\n",
+ sz.side1);
+ sz.side1 = 128;
+
+ if(sz.side2 > 128) {
+ PRINT_DEBUG("Side2 was %dMB but only 128MB will be used.\n",
+ sz.side2);
+ sz.side2 = 128;
+ }
+ }
+
+ return sz;
+}
+/*
+ * Sets DRAM attributes one DIMM at a time, based on SPD data.
+ * Northbridge settings that are set: NBXCFG[31:24], DRB0-DRB7, RPS, DRAMC.
+ */
+static void set_dram_row_attributes(void)
+{
+ int i, dra, drb, col, width, value, rps;
+ u8 bpr; /* Top 8 bits of PGPOL */
+ u8 nbxecc = 0; /* NBXCFG[31:24] */
+ u8 edo, sd, regsd; /* EDO, SDRAM, registered SDRAM */
+
+ edo = 0;
+ sd = 0;
+ regsd = 1;
+ rps = 0;
+ drb = 0;
+ bpr = 0;
+
+ for (i = 0; i < DIMM_SOCKETS; i++) {
+ unsigned int device;
+ device = DIMM0 + i;
+ bpr >>= 2;
+ nbxecc >>= 2;
+
+ /* First check if a DIMM is actually present. */
+ value = spd_read_byte(device, SPD_MEMORY_TYPE);
+ /* This is 440BX! We do EDO too! */
+ if (value == SPD_MEMORY_TYPE_EDO
+ || value == SPD_MEMORY_TYPE_SDRAM) {
+
+ if (value == SPD_MEMORY_TYPE_EDO) {
+ edo = 1;
+ } else if (value == SPD_MEMORY_TYPE_SDRAM) {
+ sd = 1;
+ }
+ PRINT_DEBUG("Found DIMM in slot %d\n", i);
+
+ if (edo && sd) {
+ print_err("Mixing EDO/SDRAM unsupported!\n");
+ die("HALT\n");
+ }
+
+ /* "DRA" is our RPS for the two rows on this DIMM. */
+ dra = 0;
+
+ /* Columns */
+ col = spd_read_byte(device, SPD_NUM_COLUMNS);
+
+ /*
+ * Is this an ECC DIMM? Actually will be a 2 if so.
+ * TODO: Other register than NBXCFG also needs this
+ * ECC information.
+ */
+ value = spd_read_byte(device, SPD_DIMM_CONFIG_TYPE);
+
+ /* Data width */
+ width = spd_read_byte(device, SPD_MODULE_DATA_WIDTH_LSB);
+
+ /* Exclude error checking data width from page size calculations */
+ if (value) {
+ value = spd_read_byte(device,
+ SPD_ERROR_CHECKING_SDRAM_WIDTH);
+ width -= value;
+ /* ### ECC */
+ /* Clear top 2 bits to help set up NBXCFG. */
+ nbxecc &= 0x3f;
+ } else {
+ /* Without ECC, top 2 bits should be 11. */
+ nbxecc |= 0xc0;
+ }
+
+ /* If any installed DIMM is *not* registered, this system cannot be
+ * configured for registered SDRAM.
+ * By registered, only the address and control lines need to be, which
+ * we can tell by reading SPD byte 21, bit 1.
+ */
+ value = spd_read_byte(device, SPD_MODULE_ATTRIBUTES);
+
+ PRINT_DEBUG("DIMM is ");
+ if ((value & MODULE_REGISTERED) == 0) {
+ regsd = 0;
+ PRINT_DEBUG("not ");
+ }
+ PRINT_DEBUG("registered\n");
+
+ /* Calculate page size in bits. */
+ value = ((1 << col) * width);
+
+ /* Convert to KB. */
+ dra = (value >> 13);
+
+ /* Number of banks of DIMM (single or double sided). */
+ value = spd_read_byte(device, SPD_NUM_DIMM_BANKS);
+
+ /* Once we have dra, col is done and can be reused.
+ * So it's reused for number of banks.
+ */
+ col = spd_read_byte(device, SPD_NUM_BANKS_PER_SDRAM);
+
+ if (value == 1) {
+ /*
+ * Second bank of 1-bank DIMMs "doesn't have
+ * ECC" - or anything.
+ */
+ if (dra == 2) {
+ dra = 0x0; /* 2KB */
+ } else if (dra == 4) {
+ dra = 0x1; /* 4KB */
+ } else if (dra == 8) {
+ dra = 0x2; /* 8KB */
+ } else if (dra >= 16) {
+ /* Page sizes larger than supported are
+ * set to 8KB to use module partially.
+ */
+ PRINT_DEBUG("Page size forced to 8KB.\n");
+ dra = 0x2; /* 8KB */
+ } else {
+ dra = -1;
+ }
+ /*
+ * Sets a flag in PGPOL[BPR] if this DIMM has
+ * 4 banks per row.
+ */
+ if (col == 4)
+ bpr |= 0x40;
+ } else if (value == 2) {
+ if (dra == 2) {
+ dra = 0x0; /* 2KB */
+ } else if (dra == 4) {
+ dra = 0x05; /* 4KB */
+ } else if (dra == 8) {
+ dra = 0x0a; /* 8KB */
+ } else if (dra >= 16) {
+ /* Ditto */
+ PRINT_DEBUG("Page size forced to 8KB.\n");
+ dra = 0x0a; /* 8KB */
+ } else {
+ dra = -1;
+ }
+ /* Ditto */
+ if (col == 4)
+ bpr |= 0xc0;
+ } else {
+ print_err("# of banks of DIMM unsupported!\n");
+ die("HALT\n");
+ }
+ if (dra == -1) {
+ print_err("Page size not supported\n");
+ die("HALT\n");
+ }
+
+ /*
+ * 440BX supports asymmetrical dual-sided DIMMs,
+ * but can't handle DIMMs smaller than 8MB per
+ * side.
+ */
+ struct dimm_size sz = spd_get_dimm_size(device);
+ if ((sz.side1 < 8)) {
+ print_err("DIMMs smaller than 8MB per side\n"
+ "are not supported on this NB.\n");
+ die("HALT\n");
+ }
+
+ /* Divide size by 8 to set up the DRB registers. */
+ drb += (sz.side1 / 8);
+
+ /*
+ * Build the DRB for the next row in MSB so it gets
+ * placed in DRB[n+1] where it belongs when written
+ * as a 16-bit word.
+ */
+ drb &= 0xff;
+ drb |= (drb + (sz.side2 / 8)) << 8;
+ } else {
+#if 0
+ PRINT_DEBUG("No DIMM found in slot %d\n", i);
+#endif
+
+ /* If there's no DIMM in the slot, set dra to 0x00. */
+ dra = 0x00;
+ /* Still have to propagate DRB over. */
+ drb &= 0xff;
+ drb |= (drb << 8);
+ }
+
+ pci_write_config16(NB, DRB + (2 * i), drb);
+#if 0
+ PRINT_DEBUG("DRB has been set to 0x%04x\n", drb);
+#endif
+
+ /* Brings the upper DRB back down to be base for
+ * DRB calculations for the next two rows.
+ */
+ drb >>= 8;
+
+ rps |= (dra & 0x0f) << (i * 4);
+ }
+
+ /* Set paging policy register. */
+ pci_write_config8(NB, PGPOL + 1, bpr);
+ PRINT_DEBUG("PGPOL[BPR] has been set to 0x%02x\n", bpr);
+
+ /* Set DRAM row page size register. */
+ pci_write_config16(NB, RPS, rps);
+ PRINT_DEBUG("RPS has been set to 0x%04x\n", rps);
+
+ /* ### ECC */
+ pci_write_config8(NB, NBXCFG + 3, nbxecc);
+ PRINT_DEBUG("NBXECC[31:24] has been set to 0x%02x\n", nbxecc);
+
+ /* Set DRAMC[4:3] to proper memory type (EDO/SDRAM/Registered SDRAM). */
+
+ /* i will be used to set DRAMC[4:3]. */
+ if (regsd && sd) {
+ i = 0x10; // Registered SDRAM
+ } else if (sd) {
+ i = 0x08; // SDRAM
+ } else {
+ i = 0; // EDO
+ }
+
+ value = pci_read_config8(NB, DRAMC) & 0xe7;
+ value |= i;
+ pci_write_config8(NB, DRAMC, value);
+ PRINT_DEBUG("DRAMC has been set to 0x%02x\n", value);
+}
+
+void sdram_set_spd_registers(void)
+{
+ /* Setup DRAM row boundary registers and other attributes. */
+ set_dram_row_attributes();
+
+ /* Setup DRAM buffer strength. */
+ set_dram_buffer_strength();
/* TODO: Set PMCR? */
- // pci_write_config8(ctrl->d0, PMCR, 0x14);
- pci_write_config8(ctrl->d0, PMCR, 0x10);
-
- /* TODO? */
- pci_write_config8(ctrl->d0, PCI_LATENCY_TIMER, 0x40);
- pci_write_config8(ctrl->d0, DRAMT, 0x03);
- pci_write_config8(ctrl->d0, MBSC, 0x03);
- pci_write_config8(ctrl->d0, SCRR, 0x38);
+ // pci_write_config8(NB, PMCR, 0x14);
+ pci_write_config8(NB, PMCR, 0x10);
+
+ /* TODO: This is for EDO memory only. */
+ pci_write_config8(NB, DRAMT, 0x03);
}
-/**
- * Enable SDRAM.
- *
- * @param Number of controllers
- * @param Memory controller
- */
-static void sdram_enable(int controllers, const struct mem_controller *ctrl)
+void sdram_enable(void)
{
int i;
udelay(200);
/* 1. Apply NOP. Wait 200 clock cycles (200us should do). */
- PRINT_DEBUG("RAM Enable 1: Apply NOP\r\n");
- do_ram_command(ctrl, RAM_COMMAND_NOP, 0);
+ PRINT_DEBUG("RAM Enable 1: Apply NOP\n");
+ do_ram_command(RAM_COMMAND_NOP);
udelay(200);
/* 2. Precharge all. Wait tRP. */
- PRINT_DEBUG("RAM Enable 2: Precharge all\r\n");
- do_ram_command(ctrl, RAM_COMMAND_PRECHARGE, 0);
+ PRINT_DEBUG("RAM Enable 2: Precharge all\n");
+ do_ram_command(RAM_COMMAND_PRECHARGE);
udelay(1);
/* 3. Perform 8 refresh cycles. Wait tRC each time. */
- PRINT_DEBUG("RAM Enable 3: CBR\r\n");
+ PRINT_DEBUG("RAM Enable 3: CBR\n");
for (i = 0; i < 8; i++) {
- do_ram_command(ctrl, RAM_COMMAND_CBR, 0);
+ do_ram_command(RAM_COMMAND_CBR);
udelay(1);
}
/* 4. Mode register set. Wait two memory cycles. */
- PRINT_DEBUG("RAM Enable 4: Mode register set\r\n");
- do_ram_command(ctrl, RAM_COMMAND_MRS, 0x1d0);
+ PRINT_DEBUG("RAM Enable 4: Mode register set\n");
+ do_ram_command(RAM_COMMAND_MRS);
udelay(2);
/* 5. Normal operation. */
- PRINT_DEBUG("RAM Enable 5: Normal operation\r\n");
- do_ram_command(ctrl, RAM_COMMAND_NORMAL, 0);
+ PRINT_DEBUG("RAM Enable 5: Normal operation\n");
+ do_ram_command(RAM_COMMAND_NORMAL);
udelay(1);
/* 6. Finally enable refresh. */
- PRINT_DEBUG("RAM Enable 6: Enable refresh\r\n");
- // pci_write_config8(ctrl->d0, PMCR, 0x10);
- spd_enable_refresh(ctrl);
+ PRINT_DEBUG("RAM Enable 6: Enable refresh\n");
+ // pci_write_config8(NB, PMCR, 0x10);
+ spd_enable_refresh();
udelay(1);
- PRINT_DEBUG("Northbridge following SDRAM init:\r\n");
+ PRINT_DEBUG("Northbridge following SDRAM init:\n");
DUMPNORTH();
}