aboutsummaryrefslogtreecommitdiff
path: root/src/northbridge/via/cn700/raminit.c
diff options
context:
space:
mode:
authorCorey Osgood <corey.osgood@gmail.com>2008-02-21 00:56:14 +0000
committerStefan Reinauer <stepan@openbios.org>2008-02-21 00:56:14 +0000
commitbd3f93e33038be965c971a6974d580f55934b854 (patch)
tree52287af10f52f09c81e19ce64c95b8a9973474df /src/northbridge/via/cn700/raminit.c
parentf327d9f9540971518e1661e1f50d30ffa6b74173 (diff)
Add support for the Via CN700 with a C7 CPU and DDR2 RAM. Only a single DIMM is
working for now, and more work is needed for it to be fully dynamic. However, just about any 128MB-512MB DIMM should work. Signed-off-by: Corey Osgood <corey.osgood@gmail.com> Acked-by: Stefan Reinauer <stepan@coresystems.de> git-svn-id: svn://svn.coreboot.org/coreboot/trunk@3113 2b7e53f0-3cfb-0310-b3e9-8179ed1497e1
Diffstat (limited to 'src/northbridge/via/cn700/raminit.c')
-rw-r--r--src/northbridge/via/cn700/raminit.c373
1 files changed, 373 insertions, 0 deletions
diff --git a/src/northbridge/via/cn700/raminit.c b/src/northbridge/via/cn700/raminit.c
new file mode 100644
index 0000000000..a9efd4e4d3
--- /dev/null
+++ b/src/northbridge/via/cn700/raminit.c
@@ -0,0 +1,373 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2007 Corey Osgood <corey.osgood@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
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <spd.h>
+#include <sdram_mode.h>
+#include <delay.h>
+#include "cn700.h"
+
+//#define DEBUG_RAM_SETUP 1
+
+#ifdef DEBUG_RAM_SETUP
+#define PRINT_DEBUG_MEM(x) print_debug(x)
+#define PRINT_DEBUG_MEM_HEX8(x) print_debug_hex8(x)
+#define PRINT_DEBUG_MEM_HEX16(x) print_debug_hex16(x)
+#define PRINT_DEBUG_MEM_HEX32(x) print_debug_hex32(x)
+#define DUMPNORTH() dump_pci_device(PCI_DEV(0, 0, 0))
+#else
+#define PRINT_DEBUG_MEM(x)
+#define PRINT_DEBUG_MEM_HEX8(x)
+#define PRINT_DEBUG_MEM_HEX16(x)
+#define PRINT_DEBUG_MEM_HEX32(x)
+#define DUMPNORTH()
+#endif
+
+static void do_ram_command(device_t dev, u8 command, u32 addr_offset)
+{
+ u8 reg;
+
+ /* TODO: Support for multiple DIMMs. */
+
+ 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");
+
+ read32(0 + 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
+ * should not be messed around with. These are too complex to explain short of
+ * copying the datasheets into the comments, but most of these values are from
+ * the BIOS Porting Guide, so they should work on any board. If they don't,
+ * try the values from your factory BIOS.
+ *
+ * TODO: Changing the DRAM frequency doesn't work (hard lockup)
+ *
+ * @param dev The northbridge's CPU Host Interface (D0F2)
+ */
+static void c7_cpu_setup(device_t dev)
+{
+ /* Host bus interface registers (D0F2 0x50-0x67) */
+ /* Request phase control */
+ pci_write_config8(dev, 0x50, 0x88);
+ /* CPU Interface Control */
+ pci_write_config8(dev, 0x51, 0x7a);
+ pci_write_config8(dev, 0x52, 0x6f);
+ /* Arbitration */
+ pci_write_config8(dev, 0x53, 0x88);
+ /* Miscellaneous Control */
+ pci_write_config8(dev, 0x54, 0x10);
+ pci_write_config8(dev, 0x55, 0x16);
+ /* Write Policy */
+ pci_write_config8(dev, 0x56, 0x01);
+ /* Miscellaneous Control */
+ /* DRAM Operating Frequency (Bits 7:5)
+ * 000 : 100MHz 001 : 133MHz
+ * 010 : 166MHz 011 : 200MHz
+ * 100 : 266MHz 101 : 333MHz
+ * 110/111 : Reserved */
+ //pci_write_config8(dev, 0x57, 0x60);//set 200MHz dram clock
+ /* CPU Miscellaneous Control */
+ pci_write_config8(dev, 0x59, 0x60);
+ /* Write Policy */
+ pci_write_config8(dev, 0x5d, 0xb2);
+ /* Bandwidth Timer */
+ pci_write_config8(dev, 0x5e, 0x88);
+ /* CPU Miscellaneous Control */
+ pci_write_config8(dev, 0x5f, 0xc7);
+
+ /* Line DRDY# Timing Control */
+ pci_write_config8(dev, 0x60, 0xff);
+ pci_write_config8(dev, 0x61, 0xff);
+ pci_write_config8(dev, 0x62, 0x0f);
+ /* QW DRDY# Timing Control */
+ pci_write_config8(dev, 0x63, 0xff);
+ pci_write_config8(dev, 0x64, 0xff);
+ pci_write_config8(dev, 0x65, 0x0f);
+ /* Read Line Burst DRDY# Timing Control */
+ pci_write_config8(dev, 0x66, 0xff);
+ pci_write_config8(dev, 0x67, 0x70);
+
+ /* Host Bus IO Circuit (See datasheet) */
+ /* Host Address Pullup/down Driving */
+ pci_write_config8(dev, 0x70, 0x33);
+ pci_write_config8(dev, 0x71, 0x00);
+ pci_write_config8(dev, 0x72, 0x33);
+ pci_write_config8(dev, 0x73, 0x00);
+ /* Miscellaneous Control */
+ pci_write_config8(dev, 0x74, 0x00);
+ /* AGTL+ I/O Circuit */
+ pci_write_config8(dev, 0x75, 0x28);
+ /* AGTL+ Compensation Status */
+ pci_write_config8(dev, 0x76, 0x74);
+ /* AGTL+ Auto Compensation Offest */
+ pci_write_config8(dev, 0x77, 0x00);
+ /* Host FSB CKG Control */
+ pci_write_config8(dev, 0x78, 0x0a);
+ /* Address/Address Clock Output Delay Control */
+ pci_write_config8(dev, 0x79, 0xaa);
+ /* Address Strobe Input Delay Control */
+ pci_write_config8(dev, 0x7a, 0x24);
+ /* Address CKG Rising/Falling Time Control */
+ pci_write_config8(dev, 0x7b, 0x00);
+ /* Address CKG Clock Rising/Falling Time Control */
+ pci_write_config8(dev, 0x7c, 0x00);
+ /* Undefined (can't remember why I did this) */
+ pci_write_config8(dev, 0x7d, 0x6d);
+}
+
+/**
+ * Set up various ram and other control registers statically. Some of these may
+ * not be needed, other should be done with spd info, but that's a project for
+ * the future
+ */
+static void sdram_set_registers(const struct mem_controller *ctrl)
+{
+ /* DQ/DQS Strength Control */
+ pci_write_config8(ctrl->d0f3, 0xd0, 0x88);
+ pci_write_config8(ctrl->d0f3, 0xd1, 0x8b);
+ pci_write_config8(ctrl->d0f3, 0xd2, 0x89);
+ /* SMM and APIC Decoding */
+ pci_write_config8(ctrl->d0f3, 0x86, 0x2d);
+
+ /* Driving selection */
+ /* DQ / DQS ODT Driving and Range Select */
+ pci_write_config8(ctrl->d0f3, 0xd5, 0x8a);
+ /* Memory Pads Driving and Range Select */
+ pci_write_config8(ctrl->d0f3, 0xd6, 0xaa);
+ /* DRAM Driving – Group DQS */
+ pci_write_config8(ctrl->d0f3, 0xe0, 0xee);
+ /* DRAM Driving – Group DQ (DQ, MPD, DQM) */
+ pci_write_config8(ctrl->d0f3, 0xe2, 0xac);//ba
+ /* DRAM Driving – Group CS */
+ pci_write_config8(ctrl->d0f3, 0xe4, 0x66);
+ /* DRAM Driving – Group MA */
+ pci_write_config8(ctrl->d0f3, 0xe8, 0x86);
+ /* DRAM Driving – Group MCLK */
+ pci_write_config8(ctrl->d0f3, 0xe6, 0xaa);
+
+ /* ODT (some are set with driving select above) */
+ /* Memory Pad ODT Pullup / Pulldown Control */
+ pci_write_config8(ctrl->d0f3, 0xd4, 0x0a);
+ /* Memory Ranks ODT Lookup Table */
+ pci_write_config8(ctrl->d0f3, 0xd8, 0x00);//was 1
+ /* Compensation Control */
+ pci_write_config8(ctrl->d0f3, 0xd3, 0x89);//enable auto compensation
+
+ /* MCLKO Phase Control */
+ pci_write_config8(ctrl->d0f3, 0x91, 0x02);
+ /* CS/CKE Clock Phase Control */
+ pci_write_config8(ctrl->d0f3, 0x92, 0x06);
+ /* SCMD/MA Clock Phase Control */
+ pci_write_config8(ctrl->d0f3, 0x93, 0x07);
+ /* Channel A DQS Input Capture Range Control */
+ pci_write_config8(ctrl->d0f3, 0x78, 0x83);
+ /* DQS Input Capture Range Control */
+ /* Set in accordance with the BIOS update note */
+ pci_write_config8(ctrl->d0f3, 0x7a, 0x00);
+ /* DQS Input Delay Offset Control */
+ pci_write_config8(ctrl->d0f3, 0x7c, 0x00);
+ /* SDRAM ODT Control */
+ pci_write_config8(ctrl->d0f3, 0xda, 0x80);
+ /* DQ/DQS CKG Output Delay Control - I */
+ pci_write_config8(ctrl->d0f3, 0xdc, 0xff);
+ /* DQ/DQS CKG Output Delay Control - II */
+ pci_write_config8(ctrl->d0f3, 0xdd, 0xff);
+ /* DQS / DQ CKG Duty Cycle Control */
+ pci_write_config8(ctrl->d0f3, 0xec, 0x88);
+ /* MCLK Output Duty Control */
+ pci_write_config8(ctrl->d0f3, 0xee, 0x00);
+ pci_write_config8(ctrl->d0f3, 0xed, 0x10);
+ /* DQS CKG Input Delay Control */
+ pci_write_config8(ctrl->d0f3, 0xef, 0x10);
+
+ pci_write_config8(ctrl->d0f3, 0x77, 0x9d);
+ pci_write_config8(ctrl->d0f3, 0x79, 0x83);
+ pci_write_config16(ctrl->d0f3, 0x88, 0x0020);
+
+ pci_write_config8(ctrl->d0f4, 0xa7, 0x80);
+
+ /* VLink Control */
+ pci_write_config8(ctrl->d0f7, 0xb0, 0x05);
+ pci_write_config8(ctrl->d0f7, 0xb1, 0x01);
+
+ /* Memory base */
+ pci_write_config16(ctrl->d1f0, 0x20, 0xfb00);
+ /* Memory limit */
+ pci_write_config16(ctrl->d1f0, 0x22, 0xfcf0);
+ /* Prefetch memory base */
+ pci_write_config16(ctrl->d1f0, 0x24, 0xf400);
+ /* Prefetch memory limit */
+ pci_write_config16(ctrl->d1f0, 0x26, 0xf7f0);
+ /* PCI to PCI bridge control */
+ pci_write_config16(ctrl->d1f0, 0x3e, 0x0008);
+
+ /* CPU to PCI flow control 1 */
+ pci_write_config8(ctrl->d1f0, 0x40, 0x83);
+ pci_write_config8(ctrl->d1f0, 0x41, 0xc3);//clear reset error, set to 43
+ pci_write_config8(ctrl->d1f0, 0x42, 0xe2);
+ pci_write_config8(ctrl->d1f0, 0x43, 0x44);
+ pci_write_config8(ctrl->d1f0, 0x44, 0x34);
+ pci_write_config8(ctrl->d1f0, 0x45, 0x72);
+
+ /* Disable cross bank/multi page mode */
+ pci_write_config8(ctrl->d0f3, DDR_PAGE_CTL, 0x80);
+ pci_write_config8(ctrl->d0f3, DRAM_REFRESH_COUNTER, 0x00);
+
+ /* Set WR=5 and RFC */
+ pci_write_config8(ctrl->d0f3, 0x61, 0xc7);
+ /* Set CAS=5 */
+ pci_write_config8(ctrl->d0f3, 0x62, 0xaf);
+ pci_write_config8(ctrl->d0f3, 0x63, 0xca);
+ /* Set to DDR2 sdram, BL=8 (0xc8, 0xc0 for bl=4) */
+ pci_write_config8(ctrl->d0f3, 0x6c, 0xc8);
+ /* Allow manual dll reset */
+ pci_write_config8(ctrl->d0f3, 0x6b, 0x10);
+
+ pci_write_config8(ctrl->d0f3, 0x6e, 0x89);
+ 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);
+
+ /* BA0-2 Selection. Don't mess with */
+ pci_write_config8(ctrl->d0f3, 0x52, 0x33);
+ pci_write_config8(ctrl->d0f3, 0x53, 0x3f);
+
+ /* Disable bank interleaving. This feature seems useless anyways */
+ pci_write_config32(ctrl->d0f3, 0x58, 0x00000000);
+ pci_write_config8(ctrl->d0f3, 0x88, 0x08);
+
+ /* Some DQS control stuffs */
+ pci_write_config8(ctrl->d0f3, 0x74, 0x04);
+ pci_write_config8(ctrl->d0f3, 0x75, 0x04);
+ pci_write_config8(ctrl->d0f3, 0x76, 0x00);
+}
+
+/**
+ * Set up dram size according to spd data. Eventually, DRAM timings should be
+ * done in a similar manner.
+ *
+ * @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],
+ 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 */
+}
+
+static void sdram_enable(device_t dev)
+{
+ int i;
+
+ /* 1. Apply NOP. */
+ PRINT_DEBUG_MEM("RAM Enable 1: Apply NOP\r\n");
+ do_ram_command(dev, 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);
+
+ /* 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
+
+ /* 4. Precharge all again. */
+ PRINT_DEBUG_MEM("RAM Enable 2: Precharge all\r\n");
+ do_ram_command(dev, 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++) {
+ udelay(100);
+ read32(0);
+ }
+
+ /* 6. Mode register set. */
+ PRINT_DEBUG_MEM("RAM Enable 4: Mode register set\r\n");
+ //safe value for now, BL=8, WR=5, CAS=5
+ /* (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);
+
+ /* 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
+
+ /* 8. Normal operation */
+ PRINT_DEBUG_MEM("RAM Enable 5: Normal operation\r\n");
+ do_ram_command(dev, 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);
+
+ /* 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);
+
+ /* 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);
+}