Keith Hui has uploaded this change for review.

View Change

nb/intel/i440bx: Support running memory at CL2 latency

Since time immemorial i440bx CAS latency has been hard coded at CL3.
This change adds code to check all installed memory modules for CL2
support as indicated by SPD, and programs the system for CL2 if
supported.

Some restructuring is required to break down functional silos holding
various parts of RAM init logic preventing operational data sharing
such as CAS latency support.

Change-Id: I59a03932a227dfe36d0657941e3468e66025282c
Signed-off-by: Keith Hui <buurin@gmail.com>
---
M src/northbridge/intel/i440bx/raminit.c
1 file changed, 254 insertions(+), 154 deletions(-)

git pull ssh://review.coreboot.org:29418/coreboot refs/changes/88/73888/1
diff --git a/src/northbridge/intel/i440bx/raminit.c b/src/northbridge/intel/i440bx/raminit.c
index 80ccaed..36b80dd 100644
--- a/src/northbridge/intel/i440bx/raminit.c
+++ b/src/northbridge/intel/i440bx/raminit.c
@@ -33,19 +33,8 @@
#define RAM_COMMAND_MRS 0x3
#define RAM_COMMAND_CBR 0x4

-/* Map the JEDEC SPD refresh rates (array index) to 440BX refresh rates as
- * defined in DRAMC[2:0].
- *
- * [0] == Normal 15.625 us -> 15.6 us
- * [1] == Reduced(.25X) 3.9 us -> 7.8 ns
- * [2] == Reduced(.5X) 7.8 us -> 7.8 us
- * [3] == Extended(2x) 31.3 us -> 31.2 us
- * [4] == Extended(4x) 62.5 us -> 62.4 us
- * [5] == Extended(8x) 125 us -> 124.8 us
- */
-static const uint32_t refresh_rate_map[] = {
- 1, 5, 5, 2, 3, 4
-};
+/* SDRAMC[2] - CAS Latency (CL). */
+#define RAM_LATENCY_CL2 0x4

/* Table format: register, value. */
static const u8 register_values[] = {
@@ -350,16 +339,16 @@
*/
static void do_ram_command(u32 command)
{
- int i, caslatency;
+ int row;
u8 dimm_start, dimm_end;
u16 reg16;
void *addr;
- u32 addr_offset;
+ u32 addr_offset = 0;

/* Configure the RAM command. */
reg16 = pci_read_config16(NB, SDRAMC);
- reg16 &= 0xff1f; /* Clear bits 7-5. */
- reg16 |= (u16)(command << 5); /* Write command into bits 7-5. */
+ reg16 &= 0xff1f;
+ reg16 |= (u16)(command << 5); /* Replace bits 7-5 with command. */
pci_write_config16(NB, SDRAMC, reg16);

/*
@@ -371,33 +360,44 @@

/* 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. */
+ for (row = 0; row < (DIMM_SOCKETS * 2); row++) {
+ /*
+ * When programming the memory modules, this is how the lowest memory address
+ * lines need to be driven while memory controller is doing Mode Register Set,
+ * accomplished by "reading" at a calculated offset from
+ * the beginning of the row's memory range.
+ *
+ * B: Memory address bits to be inverted for rows 4-7 serviced by MAB[*]
+ *
+ * 1 10 0 *
+ * 32109876543210 *
+ * -##-##########--- B MAB[*]#
+ * 000000001L1010--- A MAA[*]
+ * || || ||| || |
+ * || || ||| |+++-- Memory is 64 bits (8 bytes) wide
+ * || || ||+++----- Burst length (4)
+ * || || |+-------- Wrap type (interleave)
+ * || |+++--------- Latency (2 or 3)
+ * |++++++------------ Must be zero
+ * +------------------ MA?[13] documented here but not used nor inverted
+ */
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;
+ addr_offset = 0x0150;
+ /* Get CAS latency previously programmed here. */
+ if ((reg16 & RAM_LATENCY_CL2) == 0)
+ addr_offset |= 0x80;
+ if (row >= 4)
+ addr_offset ^= 0xdff8;
}

- dimm_end = pci_read_config8(NB, DRB + i);
+ dimm_end = pci_read_config8(NB, DRB + row);

addr = (void *)((dimm_start * 8 * 1024 * 1024) + addr_offset);
if (dimm_end > dimm_start) {
read32(addr);
+ /* Set the start of the next DIMM. */
+ dimm_start = dimm_end;
}
-
- /* Set the start of the next DIMM. */
- dimm_start = dimm_end;
}
}

@@ -618,29 +618,46 @@
DIMM-independent configuration functions.
-----------------------------------------------------------------------------*/

+/* Map the JEDEC SPD refresh rates (array index) to 440BX refresh rates as
+ * defined in DRAMC[2:0].
+ *
+ * [0] == Normal 15.625 us -> 15.6 us
+ * [1] == Reduced(.25X) 3.9 us -> 249.6 us
+ * [2] == Reduced(.5X) 7.8 us -> 249.6 us
+ * [3] == Extended(2x) 31.3 us -> 31.2 us
+ * [4] == Extended(4x) 62.5 us -> 62.4 us
+ * [5] == Extended(8x) 125 us -> 124.8 us
+ */
+static const uint32_t refresh_rate_map[] = {
+ 1, 5, 5, 2, 3, 4
+};
+static const u8 interim_refresh_rate_map[] = {
+ 3, 1, 2, 4, 5, 6
+};
static void spd_enable_refresh(void)
{
- int i, value;
- uint8_t reg;

- reg = pci_read_config8(NB, DRAMC);
+ int i, value;
+ u8 reg = 9;
+

for (i = 0; i < DIMM_SOCKETS; i++) {
value = smbus_read_byte(DIMM0 + i, SPD_REFRESH);
if (value < 0)
continue;
- reg = (reg & 0xf8) | refresh_rate_map[(value & 0x7f)];

- PRINT_DEBUG(" Enabling refresh (DRAMC = 0x%02x) for DIMM %02x\n", reg, i);
+ if (reg > interim_refresh_rate_map[(value & 0x7f)])
+ reg = interim_refresh_rate_map[(value & 0x7f)];
+
}

+ reg = (reg & 0xf8) | refresh_rate_map[(value & 0x7f)];
+
+ PRINT_DEBUG(" Enabling refresh (DRAMC = 0x%02x) for DIMM %02x\n", reg, i);
+ reg |= (pci_read_config8(NB, DRAMC) & 0xf8);
pci_write_config8(NB, DRAMC, reg);
}

-/*-----------------------------------------------------------------------------
-Public interface.
------------------------------------------------------------------------------*/
-
static void sdram_set_registers(void)
{
int i, max;
@@ -660,17 +677,15 @@
u32 side2;
};

-static struct dimm_size spd_get_dimm_size(unsigned int device)
+static struct dimm_size spd_get_dimm_size(u8 module_density, u8 dimm_banks)
{
struct dimm_size sz;
- int i, module_density, dimm_banks;
+ int i;
sz.side1 = 0;
- module_density = smbus_read_byte(device, SPD_DENSITY_OF_EACH_ROW_ON_MODULE);
- dimm_banks = smbus_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) {
+ for (i = 128; i >= 0; i >>= 1) {
if ((module_density & i) == i) {
sz.side1 = i;
break;
@@ -713,13 +728,13 @@
* modules by setting them to a supported size.
*/
if (sz.side1 > 128) {
- PRINT_DEBUG("Side1 was %dMB but only 128MB will be used.\n",
- sz.side1);
+ PRINT_DEBUG("Side%d was %dMB but only 128MB will be used.\n",
+ 1, sz.side1);
sz.side1 = 128;

if (sz.side2 > 128) {
- PRINT_DEBUG("Side2 was %dMB but only 128MB will be used.\n",
- sz.side2);
+ PRINT_DEBUG("Side%d was %dMB but only 128MB will be used.\n",
+ 2, sz.side2);
sz.side2 = 128;
}
}
@@ -728,71 +743,126 @@
}
/*
* Sets DRAM attributes one DIMM at a time, based on SPD data.
- * Northbridge settings that are set: NBXCFG[31:24], DRB0-DRB7, RPS, DRAMC.
+ * Northbridge registers programmed:
+ * NBXCFG[31:24], DRB0-DRB7, PGPOL, RPS, DRAMC, SDRAMC.
*/
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] */
+ int i, j, status, page_size, width, value;
u8 edo, sd, regsd; /* EDO, SDRAM, registered SDRAM */

+ /* BX registers built up as DIMMs are examined. */
+ u8 bpr = 0; /* Top 8 bits of PGPOL */
+ u8 nbxecc = 0; /* NBXCFG[31:24] */
+ u8 sdramc = RAM_LATENCY_CL2; /* Assume CAS latency 2 initially */
+ u16 rps = 0;
+ u16 drb = 0;
+
+ /* SPD of current DIMM. Only first 36 bytes needed */
+ u8 spd[36];
+
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;
+
+ /*
+ * Shift down settings for rows previously looked at.
+ * These registers still have bits allocated for 8 rows of memory,
+ * therefore this loop has to run the full 4 times, despite below.
+ */
bpr >>= 2;
nbxecc >>= 2;
+ rps >>= 4;

- /* First check if a DIMM is actually present. */
- value = smbus_read_byte(device, SPD_MEMORY_TYPE);
+ /*
+ * If board is not set for 4 DIMM, don't probe 4th
+ * DIMM slot which will not be there.
+ */
+ if (!CONFIG(SDRAMPWR_4DIMM) && i == 3) {
+ status = -1;
+ } else {
+ PRINT_DEBUG("DIMM %d: %02x", i, device);
+ for (j = 0; j < sizeof(spd); j++) {
+ /*
+ * First check if a DIMM is actually present.
+ * If present, read the first 36 bytes where all the
+ * important info are. This is to save heap space -
+ * at this point we don't have much to work with.
+ *
+ * If DEBUG_RAM_SETUP is enabled, dump the contents
+ * on the console along the way.
+ */
+ if ((j & 0xf) == 0) {
+ PRINT_DEBUG("\n%02x: ", j);
+ }
+ status = smbus_read_byte(device, j);
+ if (status < 0) {
+ PRINT_DEBUG("bad device\n");
+ break;
+ }
+ spd[j] = status & 0xff;
+ PRINT_DEBUG("%02x ", spd[j]);
+ }
+ /* Dump the rest of the programmed SPD. */
+ if (CONFIG(DEBUG_RAM_SETUP) && status >= 0) {
+ for (; j < spd[SPD_NUM_MANUFACTURER_BYTES]; j++) {
+ if ((j & 0xf) == 0) {
+ printk(BIOS_DEBUG, "\n%02x: ", j);
+ }
+ printk(BIOS_DEBUG, "%02x ",
+ smbus_read_byte(device, j) & 0xff);
+ }
+ printk(BIOS_DEBUG, "\n");
+ }
+ }
+
+ value = spd[SPD_MEMORY_TYPE];
/* This is 440BX! We do EDO too! */
- if (value == SPD_MEMORY_TYPE_EDO
- || value == SPD_MEMORY_TYPE_SDRAM) {
+ if (status >= 0 && (value == SPD_MEMORY_TYPE_EDO
+ || value == SPD_MEMORY_TYPE_SDRAM)) {

+ PRINT_DEBUG("DIMM is ");
if (value == SPD_MEMORY_TYPE_EDO) {
edo = 1;
+ PRINT_DEBUG("EDO ");
} else if (value == SPD_MEMORY_TYPE_SDRAM) {
sd = 1;
+ PRINT_DEBUG("SD");
}
- PRINT_DEBUG("Found DIMM in slot %d\n", i);
+ PRINT_DEBUG("RAM\n");

if (edo && sd) {
die_with_post_code(POST_RAM_FAILURE,
"Mixing EDO/SDRAM unsupported!\n");
}

- /* "DRA" is our RPS for the two rows on this DIMM. */
- dra = 0;
-
- /* Columns */
- col = smbus_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.
+ * Revert to CAS latency 3 if any module does
+ * not support CL2.
+ * TODO: Check bytes 23 and 24 and current
+ * CPU speed that TClk and Tac at CL2 can be met.
*/
- value = smbus_read_byte(device, SPD_DIMM_CONFIG_TYPE);
+ if (!(spd[SPD_ACCEPTABLE_CAS_LATENCIES] & 0x02)) {
+ sdramc = 0;
+ PRINT_DEBUG("DIMM does not support CL2\n");
+ }

/* Data width */
- width = smbus_read_byte(device, SPD_MODULE_DATA_WIDTH_LSB);
-
+ width = spd[SPD_MODULE_DATA_WIDTH_LSB];
/* Exclude error checking data width from page size calculations */
- if (value) {
- value = smbus_read_byte(device,
- SPD_ERROR_CHECKING_SDRAM_WIDTH);
- width -= value;
- /* ### ECC */
- /* Clear top 2 bits to help set up NBXCFG. */
- nbxecc &= 0x3f;
- } else {
+ width -= spd[SPD_ERROR_CHECKING_SDRAM_WIDTH];
+
+ /*
+ * Is this an ECC DIMM?
+ * TODO: Other register than NBXCFG also needs this
+ * ECC information.
+ * TODO: Verify correctness with parity&non-ECC RAM
+ */
+ if (spd[SPD_DIMM_CONFIG_TYPE] != ERROR_SCHEME_ECC) {
/* Without ECC, top 2 bits should be 11. */
nbxecc |= 0xc0;
}
@@ -802,77 +872,67 @@
* By registered, only the address and control lines need to be, which
* we can tell by reading SPD byte 21, bit 1.
*/
- value = smbus_read_byte(device, SPD_MODULE_ATTRIBUTES);
-
PRINT_DEBUG("DIMM is ");
- if ((value & MODULE_REGISTERED) == 0) {
+ if ((spd[SPD_MODULE_ATTRIBUTES] & MODULE_REGISTERED) == 0) {
regsd = 0;
PRINT_DEBUG("not ");
}
PRINT_DEBUG("registered\n");

/* Calculate page size in bits. */
- value = ((1 << col) * width);
+ value = (1 << spd[SPD_NUM_COLUMNS]) * width;

/* Convert to KB. */
- dra = (value >> 13);
+ page_size = (value >> 13);

- /* Number of banks of DIMM (single or double sided). */
- value = smbus_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 = smbus_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
+ /* Number of rows of DIMM (single or double sided). */
+ switch (spd[SPD_NUM_DIMM_BANKS]) {
+ case 1:
+ if (page_size == 2) {
+ /* 2KB page size */
+ } else if (page_size == 4) {
+ rps |= 0x1 << 12; /* 4KB page size */
+ } else if (page_size == 8) {
+ rps |= 0x2 << 12; /* 8KB page size */
+ } else if (page_size >= 16) {
+ /* Page sizes larger than 8KB are
* set to 8KB to use module partially.
*/
+ rps |= 0x2 << 12;
PRINT_DEBUG("Page size forced to 8KB.\n");
- dra = 0x2; /* 8KB */
} else {
- dra = -1;
+ page_size = -1;
}
/*
* Sets a flag in PGPOL[BPR] if this DIMM has
* 4 banks per row.
*/
- if (col == 4)
+ if (spd[SPD_NUM_BANKS_PER_SDRAM] == 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) {
+ break;
+ case 2:
+ if (page_size == 2) {
+ /* 2KB */
+ } else if (page_size == 4) {
+ rps |= 0x5 << 12; /* 4KB */
+ } else if (page_size == 8) {
+ rps |= 0xa << 12; /* 8KB */
+ } else if (page_size >= 16) {
/* Ditto */
+ rps |= 0xa << 12;
PRINT_DEBUG("Page size forced to 8KB.\n");
- dra = 0x0a; /* 8KB */
} else {
- dra = -1;
+ page_size = -1;
}
/* Ditto */
- if (col == 4)
+ if (spd[SPD_NUM_BANKS_PER_SDRAM] == 4)
bpr |= 0xc0;
- } else {
+ break;
+ default:
die_with_post_code(POST_RAM_FAILURE,
"# of banks of DIMM unsupported!\n");
}
- if (dra == -1) {
+ if (page_size == -1) {
die_with_post_code(POST_RAM_FAILURE,
"Page size not supported!\n");
}
@@ -882,7 +942,9 @@
* but can't handle DIMMs smaller than 8MB per
* side.
*/
- struct dimm_size sz = spd_get_dimm_size(device);
+ struct dimm_size sz = spd_get_dimm_size(
+ spd[SPD_DENSITY_OF_EACH_ROW_ON_MODULE],
+ spd[SPD_NUM_DIMM_BANKS]);
if ((sz.side1 < 8)) {
die_with_post_code(POST_RAM_FAILURE,
"DIMMs smaller than 8MB per side "
@@ -900,9 +962,10 @@
drb &= 0xff;
drb |= (drb + (sz.side2 / 8)) << 8;
} else {
- /* If there's no DIMM in the slot, set dra to 0x00. */
- dra = 0x00;
- /* Still have to propagate DRB over. */
+ /*
+ * If there's no DIMM in the slot, RPS can be
+ * skipped, but still have to propagate DRB over.
+ */
drb &= 0xff;
drb |= (drb << 8);
}
@@ -913,25 +976,45 @@
* 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 idle timer to 8T for CL2, 10T for CL3.
+ * TODO: Confirm if this is the right thing to do.
+ */
+ value = pci_read_config8(NB, PGPOL) & 0xf0; /* Preserve bits [7:4] */
+ value |= (sdramc & RAM_LATENCY_CL2) ? 0x03 : 0x04;
+ value |= (bpr << 8);
+ pci_write_config16(NB, PGPOL, value);
+ PRINT_DEBUG("%s has been set to 0x%04x\n", "PGPOL", value);

/* Set DRAM row page size register. */
pci_write_config16(NB, RPS, rps);
- PRINT_DEBUG("RPS has been set to 0x%04x\n", rps);
+ PRINT_DEBUG("%s has been set to 0x%04x\n", "RPS", rps);

/* ### ECC */
pci_write_config8(NB, NBXCFG + 3, nbxecc);
- PRINT_DEBUG("NBXECC[31:24] has been set to 0x%02x\n", nbxecc);
+ PRINT_DEBUG("%s has been set to 0x%02x\n", "NBXCFG[31:24]", nbxecc);
+ /*
+ * Enable ECC with hardware scrubbing if entire memory array is ECC-capable.
+ * TODO: Allow user to opt out via nvram option.
+ */
+ if (nbxecc == 0) {
+ value = pci_read_config16(NB, NBXCFG);
+ value |= 3 << 7; /* DDIM = ECC with hardware scrubbing */
+ pci_write_config16(NB, NBXCFG, value);
+ PRINT_DEBUG("%s has been set to 0x%04x\n", "NBXCFG[15:00]", value);
+ }
+
+ /* Set SDRAMC based on CAS latency. */
+ /* ### Set SRCD and SRP to 2T if CL2 FOR TESTING ONLY */
+ if (sdramc & RAM_LATENCY_CL2) sdramc |= 0x03;
+ sdramc |= pci_read_config8(NB, SDRAMC);
+ pci_write_config8(NB, SDRAMC, sdramc);
+ PRINT_DEBUG("%s has been set to 0x%02x\n", "SDRAMC[7:0]", sdramc);

/* 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) {
@@ -943,16 +1026,7 @@
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);
-}
-
-static 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();
+ PRINT_DEBUG("%s has been set to 0x%02x\n", "DRAMC", value);
}

static void sdram_enable(void)
@@ -1001,18 +1075,25 @@

/* Implemented under mainboard. */
void __weak enable_spd(void) { }
-void __weak disable_spd(void) { }
+
+/* Public interface. */

void sdram_initialize(int s3resume)
{
timestamp_add_now(TS_INITRAM_START);
enable_spd();

- dump_spd_registers();
sdram_set_registers();
- sdram_set_spd_registers();
+ /* Setup DRAM row boundary registers and other attributes. */
+ set_dram_row_attributes();
+ /* Setup DRAM buffer strength. */
+ set_dram_buffer_strength();
sdram_enable();

- disable_spd();
+ /* Clear any errors reported during raminit. */
+ pci_write_config32(NB, EAP, 0x3);
+ pci_write_config8(NB, ERRSTS, 0x11);
+ pci_write_config8(NB, ERRSTS + 1, 0x1f);
+
timestamp_add_now(TS_INITRAM_END);
}

To view, visit change 73888. To unsubscribe, or for help writing mail filters, visit settings.

Gerrit-Project: coreboot
Gerrit-Branch: master
Gerrit-Change-Id: I59a03932a227dfe36d0657941e3468e66025282c
Gerrit-Change-Number: 73888
Gerrit-PatchSet: 1
Gerrit-Owner: Keith Hui <buurin@gmail.com>
Gerrit-MessageType: newchange