summaryrefslogtreecommitdiff
path: root/src/northbridge/intel/sch/raminit.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/northbridge/intel/sch/raminit.c')
-rw-r--r--src/northbridge/intel/sch/raminit.c331
1 files changed, 331 insertions, 0 deletions
diff --git a/src/northbridge/intel/sch/raminit.c b/src/northbridge/intel/sch/raminit.c
new file mode 100644
index 0000000000..d2e8af8970
--- /dev/null
+++ b/src/northbridge/intel/sch/raminit.c
@@ -0,0 +1,331 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2009-2010 iWave Systems
+ *
+ * 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; version 2 of
+ * the License.
+ *
+ * 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 <string.h>
+#include <cpu/x86/mtrr.h>
+#include <cpu/x86/cache.h>
+#include <spd.h>
+#include "raminit.h"
+#include "sch.h"
+#define DEBUG_RAM_SETUP
+#define SOFTSTRSP(base, off) *((volatile u8 *)((base) + (off)))
+
+/* Debugging macros. */
+#if defined(DEBUG_RAM_SETUP)
+#define PRINTK_DEBUG(x...) printk(BIOS_DEBUG, x)
+#else
+#define PRINTK_DEBUG(x...)
+#endif
+
+#define BOOT_MODE_RESUME 1
+#define BOOT_MODE_NORMAL 0
+
+#include "port_access.c"
+
+static void detect_fsb(struct sys_info *sysinfo)
+{
+ u32 reg32;
+ reg32 = sch_port_access_read(5, 3, 4);
+ if (reg32 & BIT(3)) {
+ sysinfo->fsb_frequency = 533;
+ } else {
+ sysinfo->fsb_frequency = 400;
+ }
+}
+
+static u32 detect_softstrap_base(void)
+{
+ u32 reg32, base_addr;
+ reg32 = sch_port_access_read(4, 0x71, 2);
+ reg32 &= 0x700;
+ reg32 = reg32 >> 7;
+ switch (reg32) {
+ case 7:
+ base_addr = 0xFFFB0000;
+ break;
+ case 6:
+ base_addr = 0xFFFC0000;
+ break;
+ case 5:
+ base_addr = 0xFFFD0000;
+ break;
+ case 4:
+ base_addr = 0xFFFE0000;
+ break;
+ }
+ return base_addr;
+}
+
+static void detect_softstraps(struct sys_info *sysinfo)
+{
+ u8 reg8, temp;
+ u32 sbase = detect_softstrap_base();
+
+ reg8 = SOFTSTRSP(sbase, 0x87f2);
+ sysinfo->ranks = reg8;
+ if (reg8 == 0) {
+ sysinfo->ram_param_source = RAM_PARAM_SOURCE_SPD;
+ /* FIXME: implement SPD reading */
+ die("no support for reading DIMM config from SPD yet!");
+ return;
+ } else {
+ sysinfo->ram_param_source = RAM_PARAM_SOURCE_SOFTSTRAP;
+ /*Timings from soft strap */
+ reg8 = SOFTSTRSP(sbase, 0x87f0);
+ temp = reg8 & 0x30;
+ temp = temp >> 4;
+ sysinfo->cl = temp;
+ temp = reg8 & 0x0c;
+ temp = temp >> 2;
+ sysinfo->trcd = temp;
+ temp = reg8 & 0x03;
+ sysinfo->trp = temp;
+
+ /*Geometry from Softstrap */
+ reg8 = SOFTSTRSP(sbase, 0x87f1);
+
+ temp = reg8 & 0x06;
+ temp = temp >> 1;
+ sysinfo->device_density = temp;
+
+ temp = reg8 & 0x01;
+ sysinfo->data_width = temp;
+
+ /*Refresh rate default 7.8us */
+ sysinfo->refresh = 3;
+ }
+}
+
+static void program_sch_dram_data(struct sys_info *sysinfo)
+{
+ u32 reg32;
+
+ /* Program DRP DRAM Rank Population and Interface Register
+ * as per data in sysinfo SCH port 1 register 0 .. 0XFF
+ */
+ reg32 = sch_port_access_read(SCH_MSG_DUNIT_PORT, SCH_MSG_DUNIT_REG_DRP, 4);
+ reg32 &= ~(DRP_FIELDS); /* Clear all DRP fields we'll change */
+ /* Rank0 Device Width, Density, Enable */
+ reg32 |= (sysinfo->data_width) | ((sysinfo->device_density) << 1) | (1 << 3);
+ /* Rank1 Device Width, Density, Enable */
+ reg32 |= (sysinfo->data_width << 4) | ((sysinfo->device_density) << 5) | (1 << 7);
+ sch_port_access_write(SCH_MSG_DUNIT_PORT, SCH_MSG_DUNIT_REG_DRP, 4, reg32);
+
+ /*
+ Program DTR DRAM Timing Register as per data in sysinfo SCH port 1 register 1
+ tRD_dly = 2 (15:13 = 010b)
+ 0X3F
+ */
+ reg32 = sch_port_access_read(SCH_MSG_DUNIT_PORT, SCH_MSG_DUNIT_REG_DTR, 4);
+ reg32 &= ~(DTR_FIELDS); /* Clear all DTR fields we'll change */
+
+ reg32 = (sysinfo->trp);
+ reg32 |= (sysinfo->trcd) << 2;
+ reg32 |= (sysinfo->cl) << 4;
+ reg32 |= 0X4000; /* tRD_dly = 2 (15:13 = 010b) */
+ sch_port_access_write(SCH_MSG_DUNIT_PORT, SCH_MSG_DUNIT_REG_DTR, 4, reg32);
+
+
+ /* DCO DRAM Controller Operation Register as per data in sysinfo SCH port 1 register 2 0XF */
+ reg32 = sch_port_access_read(SCH_MSG_DUNIT_PORT, SCH_MSG_DUNIT_REG_DCO, 4);
+ reg32 &= ~(DCO_FIELDS); /*Clear all DTR fields we'll change */
+
+ if (sysinfo->fsb_frequency == 533) {
+ reg32 |= 1;
+ } else {
+ reg32 &= ~(BIT(0));
+ }
+ reg32 = 0x006911c; // FIXME ?
+
+ sch_port_access_write(SCH_MSG_DUNIT_PORT, SCH_MSG_DUNIT_REG_DCO, 4, reg32);
+}
+
+static void program_dll_config(struct sys_info *sysinfo)
+{
+ if (sysinfo->fsb_frequency == 533) {
+ sch_port_access_write(SCH_MSG_DUNIT_PORT, 0x21, 4, 0x46464646);
+ sch_port_access_write(SCH_MSG_DUNIT_PORT, 0x22, 4, 0x46464646);
+ } else {
+ sch_port_access_write(SCH_MSG_DUNIT_PORT, 0x21, 4, 0x58585858);
+ sch_port_access_write(SCH_MSG_DUNIT_PORT, 0x22, 4, 0x58585858);
+ }
+ sch_port_access_write(SCH_MSG_DUNIT_PORT, 0x23, 4, 0x2222);
+ if (sysinfo->fsb_frequency == 533) {
+ sch_port_access_write(SCH_MSG_DUNIT_PORT, 0x20, 4, 0x993B);
+ } else {
+ sch_port_access_write(SCH_MSG_DUNIT_PORT, 0x20, 4, 0xCC3B);
+ }
+}
+
+static void do_jedec_init(struct sys_info *sysinfo)
+{
+ u32 reg32, rank, cmd, temp, num_ranks;
+ /* Performs JEDEC memory initializattion for all memory rows */
+ /* Set CKE0/1 low */
+ reg32 = sch_port_access_read(SCH_MSG_DUNIT_PORT, SCH_MSG_DUNIT_REG_DRP, 4);
+ reg32 |= DRP_CKE_DIS;
+ sch_port_access_write(SCH_MSG_DUNIT_PORT, SCH_MSG_DUNIT_REG_DRP, 4, reg32);
+ reg32 = sch_port_access_read(SCH_MSG_DUNIT_PORT, SCH_MSG_DUNIT_REG_DRP, 4);
+ rank = 0;
+ num_ranks = sysinfo->ranks;
+ do {
+
+ /* Start clocks */
+ reg32 =
+ sch_port_access_read(SCH_MSG_DUNIT_PORT, SCH_MSG_DUNIT_REG_DRP, 4);
+ reg32 &= ~(DRP_SCK_DIS); /* Enable all SCK/SCKB by def. */
+ sch_port_access_write(1, SCH_MSG_DUNIT_REG_DRP, 4, reg32);
+ /* Program miscellaneous SCH registers on rank 0 initialization */
+ reg32 = sch_port_access_read(SCH_MSG_DUNIT_PORT, SCH_MSG_DUNIT_REG_DRP, 4);
+ if (rank == 0) {
+ program_dll_config(sysinfo);
+
+ }
+ printk(BIOS_DEBUG, "Setting up RAM \n");
+ /*
+ Wait 200us
+ reg32 = inb(ACPI_BASE + 8); PM1 Timer
+ reg32 &=0xFFFFFF;
+ reg32 +=0x2EE;
+ do
+ {
+ reg32 = inb(ACPI_BASE + 8);PM1 Timer
+ reg32 &= 0xFFFFFF;
+ }while (reg32 < 0x2EE); */
+ /* Apply NOP */
+ cmd = rank;
+ cmd |= SCH_DRAMINIT_CMD_NOP;
+ sch_port_access_write_ram_cmd(SCH_OPCODE_DRAMINIT, SCH_MSG_DUNIT_PORT, 0, cmd);
+ /* Set CKE=high */
+ reg32 = sch_port_access_read(SCH_MSG_DUNIT_PORT, SCH_MSG_DUNIT_REG_DRP, 4);
+ reg32 &= 0xFFFF9FFF; /* Clear both the CKE static disables */
+ sch_port_access_write(SCH_MSG_DUNIT_PORT, SCH_MSG_DUNIT_REG_DRP, 4, reg32);
+ /* Wait 400ns (not needed when executing from flash)
+ Precharge all
+ */
+ reg32 = sch_port_access_read(SCH_MSG_DUNIT_PORT, SCH_MSG_DUNIT_REG_DRP, 4);
+ cmd = rank;
+ cmd |= SCH_DRAMINIT_CMD_PALL;
+ sch_port_access_write_ram_cmd(SCH_OPCODE_DRAMINIT, SCH_MSG_DUNIT_PORT, 0, cmd);
+
+ /*EMRS(2); High temp self refresh=disabled, partial array self refresh=full */
+ cmd = rank;
+ cmd |= SCH_DRAMINIT_CMD_EMRS2;
+ sch_port_access_write_ram_cmd(SCH_OPCODE_DRAMINIT, SCH_MSG_DUNIT_PORT, 0, cmd);
+ /*EMRS(3) (no command) */
+ cmd = rank;
+ cmd |= SCH_DRAMINIT_CMD_EMRS3;
+ sch_port_access_write_ram_cmd(SCH_OPCODE_DRAMINIT, SCH_MSG_DUNIT_PORT, 0, cmd);
+ /*EMRS(1); Enable DLL (Leave all bits in the command at 0) */
+ cmd = rank;
+ cmd |= SCH_DRAMINIT_CMD_EMRS1;
+ sch_port_access_write_ram_cmd(SCH_OPCODE_DRAMINIT, SCH_MSG_DUNIT_PORT, 0, cmd);
+ /*MRS; Reset DLL (Set memory address bit 8) */
+ cmd = rank;
+ cmd |= SCH_DRAMINIT_CMD_MRS;
+ cmd |= (SCH_JEDEC_DLLRESET << SCH_DRAMINIT_ADDR_OFFSET);
+ sch_port_access_write_ram_cmd(SCH_OPCODE_DRAMINIT, SCH_MSG_DUNIT_PORT, 0, cmd);
+ /*Precharge all */
+ cmd = rank;
+ cmd |= SCH_DRAMINIT_CMD_PALL;
+ sch_port_access_write_ram_cmd(SCH_OPCODE_DRAMINIT, SCH_MSG_DUNIT_PORT, 0, cmd);
+ /*Issue 2 auto-refresh commands */
+ cmd = rank;
+ cmd |= SCH_DRAMINIT_CMD_AREF;
+ sch_port_access_write_ram_cmd(SCH_OPCODE_DRAMINIT, SCH_MSG_DUNIT_PORT, 0, cmd);
+ sch_port_access_write_ram_cmd(SCH_OPCODE_DRAMINIT, SCH_MSG_DUNIT_PORT, 0, cmd);
+ /*MRS command including tCL, tWR, burst length (always 4) */
+ cmd = rank;
+ cmd |= (SCH_DRAMINIT_CMD_MRS + JEDEC_STATIC_PARAM); /*Static param */
+ temp = sysinfo->cl;
+ temp += TCL_LOW; /*Adjust for the TCL base */
+ temp = temp << ((SCH_JEDEC_CL_OFFSET + SCH_DRAMINIT_ADDR_OFFSET)); /*Ready the CAS latency */
+ cmd |= temp;
+ sch_port_access_write_ram_cmd(SCH_OPCODE_DRAMINIT, SCH_MSG_DUNIT_PORT, 0, cmd);
+ /* Wait 200 clocks (max of 1us, so no need to delay)
+ Issue EMRS(1):OCD default
+ */
+ cmd = rank;
+ cmd |= SCH_DRAMINIT_CMD_EMRS1;
+ cmd |= (SCH_JEDEC_OCD_DEFAULT << SCH_DRAMINIT_ADDR_OFFSET);
+ sch_port_access_write_ram_cmd(SCH_OPCODE_DRAMINIT, SCH_MSG_DUNIT_PORT, 0, cmd);
+ /*Issue EMRS(1): OCD cal. mode exit. */
+ cmd = rank;
+ cmd |= SCH_DRAMINIT_CMD_EMRS1;
+ cmd |= (SCH_JEDEC_DQS_DIS << SCH_DRAMINIT_ADDR_OFFSET);
+ sch_port_access_write_ram_cmd(SCH_OPCODE_DRAMINIT, SCH_MSG_DUNIT_PORT, 0, cmd);
+ rank += SCH_DRAMINIT_RANK_MASK;
+ num_ranks--;
+ } while (num_ranks);
+}
+
+/**
+ * @param boot_mode: 0 = normal, 1 = resume
+ */
+
+void sdram_initialize(int boot_mode)
+{
+ struct sys_info sysinfo;
+ u32 reg32;
+
+ printk(BIOS_DEBUG, "Setting up RAM controller.\n");
+
+ memset(&sysinfo, 0, sizeof(sysinfo));
+
+
+ detect_fsb(&sysinfo);
+ detect_softstraps(&sysinfo);
+
+ program_sch_dram_data(&sysinfo);
+
+ /* cold boot */
+ if (boot_mode == BOOT_MODE_NORMAL) {
+ do_jedec_init(&sysinfo);
+ } else {
+ program_dll_config(&sysinfo);
+ }
+
+ /* raminit complete */
+ reg32 = sch_port_access_read(SCH_MSG_DUNIT_PORT, SCH_MSG_DUNIT_REG_DCO, 4);
+ reg32 |= DCO_IC;
+ reg32 |= ((sysinfo.refresh) << 2);
+ reg32 = 0x006919c;
+ sch_port_access_write(SCH_MSG_DUNIT_PORT, SCH_MSG_DUNIT_REG_DCO, 4, reg32);
+
+ /* setting up TOM */
+ reg32 = 0x10000000;
+ reg32 = reg32 >> sysinfo.data_width;
+ reg32 = reg32 << sysinfo.device_density;
+ reg32 = reg32 << sysinfo.ranks;
+ reg32 = 0x40000000;
+ sch_port_access_write(2, 8, 4, reg32);
+
+ /* resume mode */
+ if (boot_mode == BOOT_MODE_RESUME) {
+ sch_port_access_write_ram_cmd(SCH_OPCODE_WAKEFULLON,
+ SCH_MSG_DUNIT_PORT, 0, 0);
+ }
+
+ sch_port_access_write(2, 0, 4, 0x98);
+ sch_port_access_write(2, 3, 4, 0x7);
+ sch_port_access_write(3, 2, 4, 0x408);
+ sch_port_access_write(4, 0x71, 4, 0x600);
+}
+