It isn't quite complete, but there's a couple fairly important bits in
there. I'm trying to reconstruct a lot of the work I've done in the last few
months from memory, so bear with me ;)
Squash a major bug in the previously committed cn700 patch (spd data only shifted 1 bit instead of 4), add Urbez Santana Roma's fixes for the ram setup data, and add the start of dynamic ram setup. This is an intermediate patch, there will be another to finish the dynamic ram setup, but the fix is very necessary. Note that this is not build tested.
Signed-off-by: Corey Osgood <[EMAIL PROTECTED]>
Index: src/include/spd.h
===================================================================
--- src/include/spd.h (revision 3113)
+++ src/include/spd.h (working copy)
@@ -73,6 +73,9 @@
#define SPD_CMD_SIGNAL_INPUT_HOLD_TIME 33 /* Command and address signal input hold time */
#define SPD_DATA_SIGNAL_INPUT_SETUP_TIME 34 /* Data signal input setup time */
#define SPD_DATA_SIGNAL_INPUT_HOLD_TIME 35 /* Data signal input hold time */
+#define SPD_WRITE_RECOVERY_TIME 36 /* Write Recovery Time (tWR) */
+#define SPD_WRITE_TO_READ_CMD_DELAY 37 /* Internal write to read command delay (tWTR) */
+#define SPD_READ_TO_PRECHARGE_CMD_DELAY 38 /* Internal read to precharge command delay (tRTP) */
#define SPD_SPD_DATA_REVISION_CODE 62 /* SPD data revision code */
#define SPD_CHECKSUM_FOR_BYTES_0_TO_62 63 /* Checksum for bytes 0-62 */
#define SPD_MANUFACTURER_JEDEC_ID_CODE 64 /* Manufacturer's JEDEC ID code, per EIA/JEP106 (bytes 64-71) */
@@ -92,6 +95,9 @@
#define SPD_tRAS SPD_MIN_ACTIVE_TO_PRECHARGE_DELAY
#define SPD_BANK_DENSITY SPD_DENSITY_OF_EACH_ROW_ON_MODULE
#define SPD_ADDRESS_CMD_HOLD SPD_CMD_SIGNAL_INPUT_HOLD_TIME
+#define SPD_tWR SPD_WRITE_RECOVERY_TIME
+#define SPD_tWTR SPD_WRITE_TO_READ_CMD_DELAY
+#define SPD_tRTP SPD_READ_TO_PRECHARGE_CMD_DELAY
#define SPD_tRC 41 /* SDRAM Device Minimum Active to Active/Auto Refresh Time (tRC) */
#define SPD_tRFC 42 /* SDRAM Device Minimum Auto Refresh to Active/Auto Refresh (tRFC) */
Index: src/northbridge/via/cn700/raminit.c
===================================================================
--- src/northbridge/via/cn700/raminit.c (revision 3113)
+++ src/northbridge/via/cn700/raminit.c (working copy)
@@ -39,27 +39,24 @@
#define DUMPNORTH()
#endif
-static void do_ram_command(device_t dev, u8 command, u32 addr_offset)
+static void do_ram_command(const struct mem_controller *ctrl, u8 command, u32 addr_offset)
{
- u8 reg;
+ u8 reg8;
+ int i;
- /* TODO: Support for multiple DIMMs. */
+ reg8 = pci_read_config8(ctrl->d0f3, DRAM_MISC_CTL);
+ reg8 &= 0xf8;
+ reg8 |= command;
+ pci_write_config8(ctrl->d0f3, DRAM_MISC_CTL, reg8);
- reg = pci_read_config8(dev, DRAM_MISC_CTL);
- reg &= 0xf8; /* Clear bits 2-0. */
- reg |= command;
- pci_write_config8(dev, DRAM_MISC_CTL, reg);
-
- PRINT_DEBUG_MEM(" Sending RAM command 0x");
- PRINT_DEBUG_MEM_HEX8(reg);
- PRINT_DEBUG_MEM(" to 0x");
- PRINT_DEBUG_MEM_HEX32(0 + addr_offset);
- PRINT_DEBUG_MEM("\r\n");
-
+ /* NOTE: Dual-sided ready */
read32(0 + addr_offset);
+ for(i = 0; i < (ARRAY_SIZE(ctrl->channel0) * 2); i++) {
+ reg8 = pci_read_config8(ctrl->d0f3, 0x40 + i);
+ if(reg8) read32((reg8 << 26) + addr_offset);
+ }
}
-
/**
* Configure the bus between the cpu and the northbridge. This might be able to
* be moved to post-ram code in the future. For the most part, these registers
@@ -255,11 +252,6 @@
pci_write_config8(ctrl->d0f3, 0x67, 0x50);
pci_write_config8(ctrl->d0f3, 0x65, 0xd9);
- /* Only enable bank 1, for now */
- /* TODO: Multiple, dynamically controlled bank enables */
- pci_write_config8(ctrl->d0f3, 0x54, 0x80);
- pci_write_config8(ctrl->d0f3, 0x55, 0x00);
-
/* Set to 2T, MA Map type 1.
* TODO: Needs to become dynamic */
pci_write_config16(ctrl->d0f3, 0x50, 0x0020);
@@ -268,7 +260,7 @@
pci_write_config8(ctrl->d0f3, 0x52, 0x33);
pci_write_config8(ctrl->d0f3, 0x53, 0x3f);
- /* Disable bank interleaving. This feature seems useless anyways */
+ /* Disable bank interleaving. */
pci_write_config32(ctrl->d0f3, 0x58, 0x00000000);
pci_write_config8(ctrl->d0f3, 0x88, 0x08);
@@ -279,62 +271,226 @@
}
/**
+ * Check the detected timing against timings supported by the cn700, and
+ * adjust as necessary.
+ *
+ * @param val The value to be checked.
+ * @param min Minimum possible value.
+ * @param max Maximum possible value.
+ * @return val The checked and/or modified value.
+ */
+static int check_timing(int val, int min, int max)
+{
+ if(val < min)
+ val = min;
+ if(val > max)
+ val = max;
+ return val;
+}
+
+/* Table to transform the ram speed register value into a MHz value */
+static int ram_speeds[6] = {100, 133, 166, 200, 266, 333};
+/* Transform spd byte 40's tRFC value into a decimal */
+static int byte_40_tRFC[6] = {0, 25, 33, 50, 66, 75 };
+
+/**
* Set up dram size according to spd data. Eventually, DRAM timings should be
* done in a similar manner.
*
+ * NOTE: Via datasheets contradict themselves. Some places specify 8 ranks (4 banks),
+ * other places (on the same page) say 4 ranks. This code should be able to handle
+ * either situation.
+ *
+ * TODO: Double sided dimm support, the old code was broken. This is why odd-numbered
+ * ranks are ignored.
+ *
* @param ctrl The northbridge devices and spd addresses.
*/
static void sdram_set_spd_registers(const struct mem_controller *ctrl)
{
- u8 spd_data, spd_data2;
-
- /* DRAM Bank Size */
- spd_data = spd_read_byte(ctrl->channel0[0],
+ u16 spd_data;
+ u8 reg8;
+ int i, j, ram_cycle;
+
+ /* ram_cycle is the cycle time of the ram, in ns */
+ ram_cycle = (pci_read_config8(ctrl->d0f2, 0x57) >> 5);
+ ram_cycle = 1000 / ram_cycle[ram_speed];
+
+ for(i = 0; i < ARRAY_SIZE(ctrl->channel0); i++)
+ {
+ /* DRAM Bank Size */
+ spd_data = spd_read_byte(ctrl->channel0[i],
SPD_DENSITY_OF_EACH_ROW_ON_MODULE);
- /* I know this seems weird. Blame JEDEC/Via. */
- if(spd_data >= 0x10)
- spd_data = spd_data >> 1;
- else
- spd_data = spd_data << 1;
- /* Check for double sided dimm and adjust size accordingly */
- spd_data2 = spd_read_byte(ctrl->channel0[0], SPD_NUM_BANKS_PER_SDRAM);
- /* There should be 4 banks on a single sided dimm,
- * or 8 on a dual sided one */
- spd_data = spd_data * (spd_data2 / 4);
- pci_write_config8(ctrl->d0f3, 0x40, spd_data);
- /* TODO: The rest of the DIMMs */
+ /* If there's no ram, just continue to the next bank */
+ if(spd_data == 0x00 || spd_data == 0xff)
+ continue;
+
+ /* I know this seems weird. Blame JEDEC/Via. */
+ if(spd_data >= 0x10)
+ spd_data = spd_data >> 4;
+ else
+ spd_data = spd_data << 4;
+
+ /* 0x40-0x47 are top addresses, 0x48-0x4f are base addressses */
+ for(j = 1, reg8 = 0; !reg8 && j < (i * 2); j++) {
+ /* Search for the top of the previous populated rank */
+ reg8 = pci_read_config8(ctrl->d0f3, 0x49 + (i * 2) - j));
+ }
+ pci_write_config8(ctrl->d0f3, 0x49 + (i * 2), reg8);
+ pci_write_config8(ctrl->d0f3, 0x40 + (i * 2), spd_data + reg8);
+
+ /* Just a mockup, needs a detection routine */
+ /* if(dual sided) {
+ pci_write_config8(ctrl->d0f3, 0x49 + (i * 2) + 1,
+ spd_data + reg8);
+ pci_write_config8(ctrl->d0f3, 0x40 + (i * 2) + 1,
+ (spd_data * 2) + reg8);
+ } */
+
+ /* Enable the rank, and map it to the virtual rank of the same number. */
+ reg8 = (1 << 8) | ((i * 2) << 4);
+ /* if(dual sided) reg8 |= (1 << 4) | ((i * 2) + 1); */
+ pci_write_config8(ctrl->d0f3, 0x54 + i, reg8);
+
+ /* tWR (bits 7:6) */
+ /* JEDEC spec allows for decimal values, but coreboot doesn't.
+ * Convert the decimal value to an int (done more below). */
+ reg8 = spd_read_byte(ctrl->channel0[i], SPD_tWR);
+ spd_data = (((reg8 & ~0x3) >> 2) * 100) | ((reg8 & 0x3) * 25);
+ spd_data = spd_data / (ram_cycle * 100);
+ spd_data = check_timing(spd_data, 2, 5);
+ reg8 = pci_read_config8(ctrl->d0f3, 0x61);
+ if((spd_data - 2) > (reg8 >> 6) {
+ reg8 &= ~(0x3 << 6);
+ reg8 |= ((spd_data - 2) << 6);
+ }
+
+ /* tRFC */
+ spd_data = (spd_read_byte(ctrl->channel0[i], SPD_tRFC) * 100);
+ j = spd_read_byte(ctrl->channel0[i], 40);
+ if(j & 1)
+ spd_data += (256 * 100);
+ j = (j >> 1) & 0x7;
+ spd_data |= byte_40_tRFC[j];
+ spd_data = spd_data / (ram_cycle * 100);
+ spd_data = check_timing(spd_data, 8, 71);
+ if((spd_data - 8) > (reg8 & 0x3f)) {
+ reg8 &= ~0x3f;
+ reg8 |= (spd_data - 8);
+ }
+ pci_write_config8(ctrl->d0f3, 0x61, reg8);
+
+ /* tRAS */
+ reg8 = pci_read_config8(ctrl->d0f3, 0x62);
+ spd_data = spd_read_byte(ctrl->channel0[i], SPD_tRAS);
+ spd_data = spd_data / ram_cycle;
+ spd_data = check_timing(spd_data, 5, 20);
+ if((spd_data - 5) > (reg8 >> 4)) {
+ reg8 &= 0xf;
+ reg8 |= ((spd_data - 5) << 4);
+ }
+
+ /* CAS Latency */
+ spd_data = spd_read_byte(ctrl->channel0,
+ SPD_ACCEPTABLE_CAS_LATENCIES);
+ for(j = 2; !((spd_data << j) & 1); j++) { }
+ /* j should now be the CAS latency,
+ * in T for the module's rated speed */
+ j = check_timing(j, 2, 5);
+ if((j - 2) > (reg8 & 0x7)) {
+ reg8 &= ~0x7;
+ reg8 |= j;
+ }
+ pci_write_config8(ctrl->d0f3, 0x62, reg8);
+
+ /* tRRD */
+ reg8 = pci_read_config8(ctrl->d0f3, 0x63);
+ j = spd_read_byte(ctrl->channel0[i], SPD_tRRD);
+ spd_data = ((j >> 2) * 100) | ((j & 0x3) * 25);
+ spd_data = spd_data / (ram_cycle * 100);
+ spd_data = check_timing(spd_data, 2, 5);
+ if((spd_data - 2) > (reg8 >> 6)) {
+ reg8 &= 0x3f;
+ reg8 |= (spd_data - 2) << 6;
+ }
+
+ /* tRTP */
+ j = spd_read_byte(ctrl->channel0[i], SPD_tRTP);
+ spd_data = ((j >> 2) * 100) | ((j & 0x3) * 25);
+ spd_data = spd_data / (ram_cycle * 100);
+ spd_data = check_timing(spd_data, 2, 3);
+ if((spd_data - 2) > ((reg8 >> 3) & 0x1)) {
+ reg8 |= 0x8;
+ }
+
+ /* tWTR */
+ j = spd_read_byte(ctrl->channel0[i], SPD_tWTR);
+ spd_data = ((j >> 2) * 100) | ((j & 0x3) * 25);
+ spd_data = spd_data / (ram_cycle * 100);
+ spd_data = check_timing(spd_data, 2, 3);
+ if((spd_data - 2) > ((reg8 >> 1) & 0x1)) {
+ reg8 |= 0x2;
+ }
+ pci_write_config8(ctrl->d0f3, 0x63, reg8);
+
+ /* tRCD */
+ reg8 = pci_read_config8(ctrl->d0f3, 0x64);
+ j = spd_read_byte(ctrl->channel0[i], SPD_tRCD);
+ spd_data = ((j >> 2) * 100) | ((j & 0x3) * 25);
+ spd_data = spd_data / (ram_cycle * 100);
+ spd_data = check_timing(spd_data, 2, 5);
+ if((spd_data - 2) > (reg8 >> 6)) {
+ reg8 &= 0x3f;
+ reg8 |= (spd_data - 2) << 6;
+ }
+
+ /* CKE Minimum Pulse */
+ reg8 |= 1 << 4; /* 0 = 2T, 1 = 3T */
+
+ /* tRP (cn700 datasheet calls this tPR) */
+ j = spd_read_byte(ctrl->channel0[i], SPD_tRP);
+ spd_data = ((j >> 2) * 100) | ((j & 0x3) * 25);
+ spd_data = spd_data / (ram_cycle * 100);
+ spd_data = check_timing(spd_data, 2, 5);
+ if((spd_data - 2) > ((reg8 >> 2) & 0x3)) {
+ reg8 &= ~(0x3 << 2);
+ reg8 |= (spd_data - 2) << 2;
+ }
+
+ /* Exit powerdown/active powerdown to any command delay */
+ reg8 |= 0x1; /* 0 = 2T, 1 = 3T */
+
+ } /* for() loop */
}
-static void sdram_enable(device_t dev)
+static void sdram_enable(const struct mem_controller *ctrl)
{
int i;
/* 1. Apply NOP. */
PRINT_DEBUG_MEM("RAM Enable 1: Apply NOP\r\n");
- do_ram_command(dev, RAM_COMMAND_NOP, 0);
+ do_ram_command(ctrl, RAM_COMMAND_NOP, 0);
udelay(200);
/* 2. Precharge all. */
PRINT_DEBUG_MEM("RAM Enable 2: Precharge all\r\n");
- do_ram_command(dev, RAM_COMMAND_PRECHARGE, 0);
+ do_ram_command(ctrl, RAM_COMMAND_PRECHARGE, 0);
/* 3. Mode register set. */
PRINT_DEBUG_MEM("RAM Enable 4: Mode register set\r\n");
- do_ram_command(dev, RAM_COMMAND_MRS, 0x2000);//enable dll
- do_ram_command(dev, RAM_COMMAND_MRS, 0x800);//reset dll
+ do_ram_command(ctrl, RAM_COMMAND_MRS, 0x2000);//enable dll
+ do_ram_command(ctrl, RAM_COMMAND_MRS, 0x800);//reset dll
/* 4. Precharge all again. */
PRINT_DEBUG_MEM("RAM Enable 2: Precharge all\r\n");
- do_ram_command(dev, RAM_COMMAND_PRECHARGE, 0);
+ do_ram_command(ctrl, RAM_COMMAND_PRECHARGE, 0);
/* 5. Perform 8 refresh cycles. Wait tRC each time. */
PRINT_DEBUG_MEM("RAM Enable 3: CBR\r\n");
- do_ram_command(dev, RAM_COMMAND_CBR, 0);
- /* First read is actually done by do_ram_command */
for(i = 0; i < 7; i++) {
+ do_ram_command(ctrl, RAM_COMMAND_CBR, 0);
udelay(100);
- read32(0);
}
/* 6. Mode register set. */
@@ -343,31 +499,34 @@
/* (E)MRS values are from the BPG. No direct explanation is given, but
* they should somehow conform to the JEDEC DDR2 SDRAM Specification
* (JESD79-2C). */
- do_ram_command(dev, RAM_COMMAND_MRS, 0x0022d8);
+ do_ram_command(ctrl, RAM_COMMAND_MRS, 0x1022d8);
/* 7. Mode register set. */
PRINT_DEBUG_MEM("RAM Enable 4: Mode register set\r\n");
- do_ram_command(dev, RAM_COMMAND_MRS, 0x21c20);//default OCD calibration
- do_ram_command(dev, RAM_COMMAND_MRS, 0x20020);//exit calibration mode
+ do_ram_command(ctrl, RAM_COMMAND_MRS, 0x121c20);//default OCD
calibration
+ do_ram_command(ctrl, RAM_COMMAND_MRS, 0x120020);//exit
calibration mode
/* 8. Normal operation */
PRINT_DEBUG_MEM("RAM Enable 5: Normal operation\r\n");
- do_ram_command(dev, RAM_COMMAND_NORMAL, 0);
+ do_ram_command(ctrl, RAM_COMMAND_NORMAL, 0);
/* Enable multipage mode. */
- pci_write_config8(dev, DDR_PAGE_CTL, 0x83);
- /* Enable refresh. */
- pci_write_config8(dev, DRAM_REFRESH_COUNTER, 0x32);
+ pci_write_config8(ctrl->d0f3, DDR_PAGE_CTL, 0x83);
+ /* Enable refresh.
+ * TODO: Needs to be dynamic */
+ pci_write_config8(ctrl->d0f3, DRAM_REFRESH_COUNTER, 0x32);
/* DQS Tuning: testing on a couple different boards has shown this is
* static, or close enough that it can be. Which is good, because the
* tuning function used too many registers. */
- pci_write_config8(dev, CH_A_DQS_OUTPUT_DELAY, 0x00);
- pci_write_config8(dev, CH_A_MD_OUTPUT_DELAY, 0x03);
+ pci_write_config8(ctrl->d0f3, CH_A_DQS_OUTPUT_DELAY, 0x00);
+ pci_write_config8(ctrl->d0f3, CH_A_MD_OUTPUT_DELAY, 0x03);
+#if CONFIG_VGA_MB
/* Enable VGA device with no memory, add memory later. We need this
* here to enable the actual device, otherwise it won't show up until
* later and LB will have a fit. */
- pci_write_config16(dev, 0xa0, (1 << 15));
- pci_write_config16(dev, 0xa4, 0x0010);
+ pci_write_config16(ctrl->d0f3, 0xa0, (1 << 15));
+ pci_write_config16(ctrl->d0f3, 0xa4, 0x0010);
+#endif
}
--
coreboot mailing list
[email protected]
http://www.coreboot.org/mailman/listinfo/coreboot