Author: eswierk Date: 2008-09-24 17:06:34 +0200 (Wed, 24 Sep 2008) New Revision: 3600
Added: trunk/coreboot-v2/src/northbridge/intel/i3100/ep80579.h trunk/coreboot-v2/src/northbridge/intel/i3100/raminit_ep80579.c trunk/coreboot-v2/src/northbridge/intel/i3100/raminit_ep80579.h Log: Support for the memory controller and PCIe interface of the Intel EP80579 Integrated Processor (codename "Tolapai"). The memory controller code supports only 64-bit-wide DIMMs with x8 devices and ECC. It has been tested on a development board using a single Micron MT9HTF6472PY-667D2 DIMM. Your mileage will definitely vary with other DIMMs.
Signed-off-by: Ed Swierk eswierk@arastra.com Acked-by: Joseph Smith joe@settoplinux.org
Added: trunk/coreboot-v2/src/northbridge/intel/i3100/ep80579.h =================================================================== --- trunk/coreboot-v2/src/northbridge/intel/i3100/ep80579.h (rev 0) +++ trunk/coreboot-v2/src/northbridge/intel/i3100/ep80579.h 2008-09-24 15:06:34 UTC (rev 3600) @@ -0,0 +1,64 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2008 Arastra, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 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 + * + */ + +#ifndef NORTHBRIDGE_INTEL_I3100_EP80579_H +#define NORTHBRIDGE_INTEL_I3100_EP80579_H + +#define SMRBASE 0x14 +#define MCHCFG0 0x50 +#define FDHC 0x58 +#define PAM 0x59 +#define DRB 0x60 +#define DRT1 0x64 +#define DRA 0x70 +#define DRT0 0x78 +#define DRC 0x7c +#define ECCDIAG 0x84 +#define SDRC 0x88 +#define CKDIS 0x8c +#define CKEDIS 0x8d +#define DEVPRES 0x9c +#define DEVPRES_D0F0 (1 << 0) +#define DEVPRES_D1F0 (1 << 1) +#define DEVPRES_D2F0 (1 << 2) +#define DEVPRES_D3F0 (1 << 3) +#define DEVPRES_D4F0 (1 << 4) +#define DEVPRES_D10F0 (1 << 5) +#define EXSMRC 0x9d +#define SMRAM 0x9e +#define EXSMRAMC 0x9f +#define DDR2ODTC 0xb0 +#define TOLM 0xc4 +#define REMAPBASE 0xc6 +#define REMAPLIMIT 0xc8 +#define REMAPOFFSET 0xca +#define TOM 0xcc +#define HECBASE 0xce +#define DEVPRES1 0xf4 + +#define DCALCSR 0x040 +#define DCALADDR 0x044 +#define DCALDATA 0x048 +#define MBCSR 0x140 +#define MBADDR 0x144 +#define MBDATA 0x148 +#define DDRIOMC2 0x268 + +#endif
Added: trunk/coreboot-v2/src/northbridge/intel/i3100/raminit_ep80579.c =================================================================== --- trunk/coreboot-v2/src/northbridge/intel/i3100/raminit_ep80579.c (rev 0) +++ trunk/coreboot-v2/src/northbridge/intel/i3100/raminit_ep80579.c 2008-09-24 15:06:34 UTC (rev 3600) @@ -0,0 +1,795 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2005 Eric W. Biederman and Tom Zimmerman + * Copyright (C) 2008 Arastra, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 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 <cpu/x86/mem.h> +#include <cpu/x86/mtrr.h> +#include <cpu/x86/cache.h> +#include "raminit_ep80579.h" +#include "ep80579.h" + +#define BAR 0x90000000 + +static void sdram_set_registers(const struct mem_controller *ctrl) +{ + static const u32 register_values[] = { + PCI_ADDR(0, 0x00, 0, CKDIS), 0xffff0000, 0x0000ffff, + PCI_ADDR(0, 0x00, 0, DEVPRES), 0x00000000, 0x07420801 | DEVPRES_CONFIG, + PCI_ADDR(0, 0x00, 0, PAM-1), 0xcccccc7f, 0x33333000, + PCI_ADDR(0, 0x00, 0, PAM+3), 0xcccccccc, 0x33333333, + PCI_ADDR(0, 0x00, 0, DEVPRES1), 0xffffffff, 0x0040003a, + PCI_ADDR(0, 0x00, 0, SMRBASE), 0x00000fff, BAR | 0, + }; + int i; + int max; + + for (i = 0; i < ARRAY_SIZE(register_values); i += 3) { + device_t dev; + u32 where; + u32 reg; + dev = (register_values[i] & ~0xff) - PCI_DEV(0, 0x00, 0) + ctrl->f0; + where = register_values[i] & 0xff; + reg = pci_read_config32(dev, where); + reg &= register_values[i+1]; + reg |= register_values[i+2]; + pci_write_config32(dev, where, reg); + } +} + +struct dimm_size { + u32 side1; + u32 side2; +}; + +static struct dimm_size spd_get_dimm_size(u16 device) +{ + /* Calculate the log base 2 size of a DIMM in bits */ + struct dimm_size sz; + int value, low, ddr2; + sz.side1 = 0; + sz.side2 = 0; + + /* Note it might be easier to use byte 31 here, it has the DIMM size as + * a multiple of 4MB. The way we do it now we can size both + * sides of an assymetric dimm. + */ + value = spd_read_byte(device, SPD_NUM_ROWS); + if (value < 0) goto hw_err; + if ((value & 0xf) == 0) goto val_err; + sz.side1 += value & 0xf; + + value = spd_read_byte(device, SPD_NUM_COLUMNS); + if (value < 0) goto hw_err; + if ((value & 0xf) == 0) goto val_err; + sz.side1 += value & 0xf; + + value = spd_read_byte(device, SPD_NUM_BANKS_PER_SDRAM); + if (value < 0) goto hw_err; + if ((value & 0xff) == 0) goto val_err; + sz.side1 += log2(value & 0xff); + + /* Get the module data width and convert it to a power of two */ + value = spd_read_byte(device, SPD_MODULE_DATA_WIDTH_MSB); + if (value < 0) goto hw_err; + value &= 0xff; + value <<= 8; + + low = spd_read_byte(device, SPD_MODULE_DATA_WIDTH_LSB); + if (low < 0) goto hw_err; + value = value | (low & 0xff); + if ((value != 72) && (value != 64)) goto val_err; + sz.side1 += log2(value); + + /* side 2 */ + value = spd_read_byte(device, SPD_NUM_DIMM_BANKS); + + if (value < 0) goto hw_err; + value &= 7; + value++; + if (value == 1) goto out; + if (value != 2) goto val_err; + + /* Start with the symmetrical case */ + sz.side2 = sz.side1; + + value = spd_read_byte(device, SPD_NUM_ROWS); + if (value < 0) goto hw_err; + if ((value & 0xf0) == 0) goto out; /* If symmetrical we are done */ + sz.side2 -= (value & 0x0f); /* Subtract out rows on side 1 */ + sz.side2 += ((value >> 4) & 0x0f); /* Add in rows on side 2 */ + + value = spd_read_byte(device, SPD_NUM_COLUMNS); + if (value < 0) goto hw_err; + if ((value & 0xff) == 0) goto val_err; + sz.side2 -= (value & 0x0f); /* Subtract out columns on side 1 */ + sz.side2 += ((value >> 4) & 0x0f); /* Add in columns on side 2 */ + goto out; + + val_err: + die("Bad SPD value\r\n"); + /* If an hw_error occurs report that I have no memory */ + hw_err: + sz.side1 = 0; + sz.side2 = 0; + out: + print_debug("dimm "); + print_debug_hex8(device); + print_debug(" size = "); + print_debug_hex8(sz.side1); + print_debug("."); + print_debug_hex8(sz.side2); + print_debug("\r\n"); + return sz; + +} + +static long spd_set_ram_size(const struct mem_controller *ctrl, u8 dimm_mask) +{ + int i; + int cum; + + for (i = cum = 0; i < DIMM_SOCKETS; i++) { + struct dimm_size sz; + if (dimm_mask & (1 << i)) { + sz = spd_get_dimm_size(ctrl->channel0[i]); + if (sz.side1 < 29) { + return -1; /* Report SPD error */ + } + /* convert bits to multiples of 64MB */ + sz.side1 -= 29; + cum += (1 << sz.side1); + pci_write_config8(ctrl->f0, DRB + (i*2), cum); + pci_write_config8(ctrl->f0, DRB+1 + (i*2), cum); + if (spd_read_byte(ctrl->channel0[i], SPD_NUM_DIMM_BANKS) & 0x1) { + cum <<= 1; + } + } + else { + pci_write_config8(ctrl->f0, DRB + (i*2), cum); + pci_write_config8(ctrl->f0, DRB+1 + (i*2), cum); + } + } + print_debug("DRB = "); + print_debug_hex32(pci_read_config32(ctrl->f0, DRB)); + print_debug("\r\n"); + + cum >>= 1; + /* set TOM top of memory */ + pci_write_config16(ctrl->f0, TOM, cum); + print_debug("TOM = "); + print_debug_hex16(cum); + print_debug("\r\n"); + /* set TOLM top of low memory */ + if (cum > 0x18) { + cum = 0x18; + } + cum <<= 11; + pci_write_config16(ctrl->f0, TOLM, cum); + print_debug("TOLM = "); + print_debug_hex16(cum); + print_debug("\r\n"); + return 0; +} + + +static u8 spd_detect_dimms(const struct mem_controller *ctrl) +{ + u8 dimm_mask = 0; + int i; + for (i = 0; i < DIMM_SOCKETS; i++) { + int byte; + u16 device; + device = ctrl->channel0[i]; + if (device) { + byte = spd_read_byte(device, SPD_MEMORY_TYPE); + print_debug("spd "); + print_debug_hex8(device); + print_debug(" = "); + print_debug_hex8(byte); + print_debug("\r\n"); + if (byte == 8) { + dimm_mask |= (1 << i); + } + } + } + return dimm_mask; +} + + +static int spd_set_row_attributes(const struct mem_controller *ctrl, + u8 dimm_mask) +{ + int value; + int i; + + for (i = 0; i < DIMM_SOCKETS; i++) { + u32 dra = 0; + int reg = 0; + + if (!(dimm_mask & (1 << i))) { + continue; + } + + value = spd_read_byte(ctrl->channel0[i], SPD_NUM_ROWS); + if (value < 0) die("Bad SPD data\r\n"); + if ((value & 0xf) == 0) die("Invalid # of rows\r\n"); + dra |= (((value-13) & 0x7) << 23); + dra |= (((value-13) & 0x7) << 29); + reg += value & 0xf; + + value = spd_read_byte(ctrl->channel0[i], SPD_NUM_COLUMNS); + if (value < 0) die("Bad SPD data\r\n"); + if ((value & 0xf) == 0) die("Invalid # of columns\r\n"); + dra |= (((value-10) & 0x7) << 20); + dra |= (((value-10) & 0x7) << 26); + reg += value & 0xf; + + value = spd_read_byte(ctrl->channel0[i], SPD_NUM_BANKS_PER_SDRAM); + if (value < 0) die("Bad SPD data\r\n"); + if ((value & 0xff) == 0) die("Invalid # of banks\r\n"); + reg += log2(value & 0xff); + + print_debug("dimm "); + print_debug_hex8(i); + print_debug(" reg = "); + print_debug_hex8(reg); + print_debug("\r\n"); + + /* set device density */ + dra |= ((31-reg)); + dra |= ((31-reg) << 6); + + /* set device width (x8) */ + dra |= (1 << 4); + dra |= (1 << 10); + + /* set device type (registered) */ + dra |= (1 << 14); + + /* set number of ranks (0=single, 1=dual) */ + value = spd_read_byte(ctrl->channel0[i], SPD_NUM_DIMM_BANKS); + dra |= ((value & 0x1) << 17); + + print_debug("DRA"); + print_debug_hex8(i); + print_debug(" = "); + print_debug_hex32(dra); + print_debug("\r\n"); + + pci_write_config32(ctrl->f0, DRA + (i*4), dra); + } + return 0; +} + + +static u32 spd_set_drt_attributes(const struct mem_controller *ctrl, + u8 dimm_mask, u32 drc) +{ + int i; + u32 val, val1; + u32 cl; + u32 trc = 0; + u32 trfc = 0; + u32 tras = 0; + u32 trtp = 0; + u32 twtr = 0; + int index = drc & 0x00000003; + int ci; + static const u8 latencies[] = { /* 533, 800, 400, 667 */ + 0x10, 0x60, 0x10, 0x20 }; + static const u32 drt0[] = { /* 533, 800, 400, 667 */ + 0x24240002, 0x24360002, 0x24220002, 0x24360002 }; + static const u32 drt1[] = { /* 533, 800, 400, 667 */ + 0x00400000, 0x00900000, 0x00200000, 0x00700000 }; + static const u32 magic[] = { /* 533, 800, 400, 667 */ + 0x007b8221, 0x00b94331, 0x005ca1a1, 0x009a62b1 }; + static const u32 mrs[] = { /* 533, 800, 400, 667 */ + 0x07020000, 0x0b020000, 0x05020000, 0x09020000 }; + static const int cycle[] = { /* 533, 800, 400, 667 */ + 15, 10, 20, 12 }; /* cycle time in 1/4 ns units */ + static const int byte40rem[] = { + 0, 1, 2, 2, 3, 3, 0, 0 }; /* byte 40 remainder in 1/4 ns units */ + + /* CAS latency in cycles */ + val = latencies[index]; + for (i = 0; i < DIMM_SOCKETS; i++) { + if (!(dimm_mask & (1 << i))) + continue; + val &= spd_read_byte(ctrl->channel0[i], SPD_ACCEPTABLE_CAS_LATENCIES); + } + if (val & 0x10) + cl = 4; + else if (val & 0x20) + cl = 5; + else if (val & 0x40) + cl = 6; + else + die("CAS latency mismatch\r\n"); + print_debug("cl = "); + print_debug_hex8(cl); + print_debug("\r\n"); + + ci = cycle[index]; + + /* Trc, Trfc in cycles */ + for (i = 0; i < DIMM_SOCKETS; i++) { + if (!(dimm_mask & (1 << i))) + continue; + val1 = spd_read_byte(ctrl->channel0[i], SPD_BYTE_41_42_EXTENSION); + val = spd_read_byte(ctrl->channel0[i], SPD_MIN_ACT_TO_ACT_AUTO_REFRESH); + val <<= 2; /* convert to 1/4 ns */ + val += byte40rem[(val1 >> 4) & 0x7]; + val = (val + ci - 1) / ci + 1; /* convert to cycles */ + if (trc < val) + trc = val; + val = spd_read_byte(ctrl->channel0[i], SPD_MIN_AUTO_REFRESH_TO_ACT); + val <<= 2; /* convert to 1/4 ns */ + if (val1 & 0x01) + val += 1024; + val += byte40rem[(val1 >> 1) & 0x7]; + val = (val + ci - 1) / ci; /* convert to cycles */ + if (trfc < val) + trfc = val; + } + print_debug("trc = "); + print_debug_hex8(trc); + print_debug("\r\n"); + print_debug("trfc = "); + print_debug_hex8(trfc); + print_debug("\r\n"); + + /* Tras, Trtp, Twtr in cycles */ + for (i = 0; i < DIMM_SOCKETS; i++) { + if (!(dimm_mask & (1 << i))) + continue; + val = spd_read_byte(ctrl->channel0[i], SPD_MIN_ACTIVE_TO_PRECHARGE_DELAY); + val <<= 2; /* convert to 1/4 ns */ + val = (val + ci - 1) / ci; /* convert to cycles */ + if (tras < val) + tras = val; + val = spd_read_byte(ctrl->channel0[i], SPD_INT_READ_TO_PRECHARGE_DELAY); + val = (val + ci - 1) / ci; /* convert to cycles */ + if (trtp < val) + trtp = val; + val = spd_read_byte(ctrl->channel0[i], SPD_INT_WRITE_TO_READ_DELAY); + val = (val + ci - 1) / ci; /* convert to cycles */ + if (twtr < val) + twtr = val; + } + print_debug("tras = "); + print_debug_hex8(tras); + print_debug("\r\n"); + print_debug("trtp = "); + print_debug_hex8(trtp); + print_debug("\r\n"); + print_debug("twtr = "); + print_debug_hex8(twtr); + print_debug("\r\n"); + + val = (drt0[index] | ((trc - 11) << 12) | ((cl - 3) << 9) + | ((cl - 3) << 6) | ((cl - 3) << 3)); + print_debug("drt0 = "); + print_debug_hex32(val); + print_debug("\r\n"); + pci_write_config32(ctrl->f0, DRT0, val); + + val = (drt1[index] | ((tras - 8) << 28) | ((trtp - 2) << 25) + | (twtr << 15)); + print_debug("drt1 = "); + print_debug_hex32(val); + print_debug("\r\n"); + pci_write_config32(ctrl->f0, DRT1, val); + + val = (magic[index]); + print_debug("magic = "); + print_debug_hex32(val); + print_debug("\r\n"); + pci_write_config32(PCI_DEV(0, 0x08, 0), 0xcc, val); + + val = (mrs[index] | (cl << 20)); + print_debug("mrs = "); + print_debug_hex32(val); + print_debug("\r\n"); + return val; +} + +static int spd_set_dram_controller_mode(const struct mem_controller *ctrl, + u8 dimm_mask) +{ + int value; + int drc = 0; + int i; + msr_t msr; + u8 cycle = 0x25; + + for (i = 0; i < DIMM_SOCKETS; i++) { + if (!(dimm_mask & (1 << i))) + continue; + if ((spd_read_byte(ctrl->channel0[i], SPD_MODULE_DATA_WIDTH_LSB) & 0xf0) != 0x40) + die("ERROR: Only 64-bit DIMMs supported\r\n"); + if (!(spd_read_byte(ctrl->channel0[i], SPD_DIMM_CONFIG_TYPE) & 0x02)) + die("ERROR: Only ECC DIMMs supported\r\n"); + if (spd_read_byte(ctrl->channel0[i], SPD_PRIMARY_SDRAM_WIDTH) != 0x08) + die("ERROR: Only x8 DIMMs supported\r\n"); + + value = spd_read_byte(ctrl->channel0[i], SPD_MIN_CYCLE_TIME_AT_CAS_MAX); + if (value > cycle) + cycle = value; + } + print_debug("cycle = "); + print_debug_hex8(cycle); + print_debug("\r\n"); + + drc |= (1 << 20); /* enable ECC */ + drc |= (3 << 30); /* enable CKE on each DIMM */ + drc |= (1 << 4); /* enable CKE globally */ + + /* TODO check: */ + /* set front side bus speed */ + msr = rdmsr(0xcd); /* returns 0 on Pentium M 90nm */ + print_debug("msr 0xcd = "); + print_debug_hex32(msr.hi); + print_debug_hex32(msr.lo); + print_debug("\r\n"); + + /* TODO check that this msr really indicates fsb speed! */ + if (msr.lo & 0x07) { + print_info("533 MHz FSB\r\n"); + if (cycle <= 0x25) { + drc |= 0x5; + print_info("400 MHz DDR\r\n"); + } else if (cycle <= 0x30) { + drc |= 0x7; + print_info("333 MHz DDR\r\n"); + } else if (cycle <= 0x3d) { + drc |= 0x4; + print_info("266 MHz DDR\r\n"); + } else { + drc |= 0x2; + print_info("200 MHz DDR\r\n"); + } + } + else { + print_info("400 MHz FSB\r\n"); + if (cycle <= 0x30) { + drc |= 0x7; + print_info("333 MHz DDR\r\n"); + } else if (cycle <= 0x3d) { + drc |= 0x0; + print_info("266 MHz DDR\r\n"); + } else { + drc |= 0x2; + print_info("200 MHz DDR\r\n"); + } + } + + print_debug("DRC = "); + print_debug_hex32(drc); + print_debug("\r\n"); + + return drc; +} + +static void sdram_set_spd_registers(const struct mem_controller *ctrl) +{ + u8 dimm_mask; + int i; + + /* Test if we can read the SPD */ + dimm_mask = spd_detect_dimms(ctrl); + if (!(dimm_mask & ((1 << DIMM_SOCKETS) - 1))) { + print_err("No memory for this cpu\r\n"); + return; + } + return; +} + +static void set_on_dimm_termination_enable(const struct mem_controller *ctrl) +{ + u8 c1,c2; + u32 dimm,i; + u32 data32; + u32 t4; + + /* Set up northbridge values */ + /* ODT enable */ + pci_write_config32(ctrl->f0, SDRC, 0xa0002c30); + + c1 = pci_read_config8(ctrl->f0, DRB); + c2 = pci_read_config8(ctrl->f0, DRB+2); + if (c1 == c2) { + /* 1 single-rank DIMM */ + data32 = 0x00000010; + } + else { + /* 2 single-rank DIMMs or 1 double-rank DIMM */ + data32 = 0x00002010; + } + + print_debug("ODT Value = "); + print_debug_hex32(data32); + print_debug("\r\n"); + + pci_write_config32(ctrl->f0, DDR2ODTC, data32); + + for (i = 0; i < 2; i++) { + print_debug("ODT CS"); + print_debug_hex8(i); + print_debug("\r\n"); + + write32(BAR+DCALADDR, 0x0b840001); + write32(BAR+DCALCSR, 0x80000003 | ((i+1)<<21)); + data32 = read32(BAR+DCALCSR); + while (data32 & 0x80000000) + data32 = read32(BAR+DCALCSR); + } +} + + +static void dump_dcal_regs(void) +{ + int i; + for (i = 0x0; i < 0x2a0; i += 4) { + if ((i % 16) == 0) { + print_debug("\r\n"); + print_debug_hex16(i); + print_debug(": "); + } + print_debug_hex32(read32(BAR+i)); + print_debug(" "); + } + print_debug("\r\n"); +} + + +static void sdram_enable(int controllers, const struct mem_controller *ctrl) +{ + int i; + int cs; + long mask; + u32 drc; + u32 data32; + u32 mode_reg; + msr_t msr; + u16 data16; + + mask = spd_detect_dimms(ctrl); + print_debug("Starting SDRAM Enable\r\n"); + + /* Set DRAM type and Front Side Bus frequency */ + drc = spd_set_dram_controller_mode(ctrl, mask); + if (drc == 0) { + die("Error calculating DRC\r\n"); + } + data32 = drc & ~(3 << 20); /* clear ECC mode */ + data32 = data32 | (3 << 5); /* temp turn off ODT */ + /* Set DRAM controller mode */ + pci_write_config32(ctrl->f0, DRC, data32); + + /* Turn the clocks on */ + pci_write_config16(ctrl->f0, CKDIS, 0x0000); + + /* Program row size */ + spd_set_ram_size(ctrl, mask); + + /* Program row attributes */ + spd_set_row_attributes(ctrl, mask); + + /* Program timing values */ + mode_reg = spd_set_drt_attributes(ctrl, mask, drc); + + dump_dcal_regs(); + + /* Apply NOP */ + for (cs = 0; cs < 2; cs++) { + print_debug("NOP CS"); + print_debug_hex8(cs); + print_debug("\r\n"); + udelay(16); + write32(BAR+DCALCSR, (0x00000000 | ((cs+1)<<21))); + write32(BAR+DCALCSR, (0x80000000 | ((cs+1)<<21))); + data32 = read32(BAR+DCALCSR); + while (data32 & 0x80000000) + data32 = read32(BAR+DCALCSR); + } + + /* Apply NOP */ + udelay(16); + for (cs = 0; cs < 2; cs++) { + print_debug("NOP CS"); + print_debug_hex8(cs); + print_debug("\r\n"); + write32(BAR + DCALCSR, (0x80000000 | ((cs+1)<<21))); + data32 = read32(BAR+DCALCSR); + while (data32 & 0x80000000) + data32 = read32(BAR+DCALCSR); + } + + /* Precharge all banks */ + udelay(16); + for (cs = 0; cs < 2; cs++) { + print_debug("Precharge CS"); + print_debug_hex8(cs); + print_debug("\r\n"); + write32(BAR+DCALADDR, 0x04000000); + write32(BAR+DCALCSR, (0x80000002 | ((cs+1)<<21))); + data32 = read32(BAR+DCALCSR); + while (data32 & 0x80000000) + data32 = read32(BAR+DCALCSR); + } + + /* EMRS: Enable DLLs, set OCD calibration mode to default */ + udelay(16); + for (cs = 0; cs < 2; cs++) { + print_debug("EMRS CS"); + print_debug_hex8(cs); + print_debug("\r\n"); + write32(BAR+DCALADDR, 0x0b840001); + write32(BAR+DCALCSR, (0x80000003 | ((cs+1)<<21))); + data32 = read32(BAR+DCALCSR); + while (data32 & 0x80000000) + data32 = read32(BAR+DCALCSR); + } + /* MRS: Reset DLLs */ + udelay(16); + for (cs = 0; cs < 2; cs++) { + print_debug("MRS CS"); + print_debug_hex8(cs); + print_debug("\r\n"); + write32(BAR+DCALADDR, mode_reg); + write32(BAR+DCALCSR, (0x80000003 | ((cs+1)<<21))); + data32 = read32(BAR+DCALCSR); + while (data32 & 0x80000000) + data32 = read32(BAR+DCALCSR); + } + + /* Precharge all banks */ + udelay(48); + for (cs = 0; cs < 2; cs++) { + print_debug("Precharge CS"); + print_debug_hex8(cs); + print_debug("\r\n"); + write32(BAR+DCALADDR, 0x04000000); + write32(BAR+DCALCSR, (0x80000002 | ((cs+1)<<21))); + data32 = read32(BAR+DCALCSR); + while (data32 & 0x80000000) + data32 = read32(BAR+DCALCSR); + } + + /* Do 2 refreshes */ + for (i = 0; i < 2; i++) { + udelay(16); + for (cs = 0; cs < 2; cs++) { + print_debug("Refresh CS"); + print_debug_hex8(cs); + print_debug("\r\n"); + write32(BAR+DCALCSR, (0x80000001 | ((cs+1)<<21))); + data32 = read32(BAR+DCALCSR); + while (data32 & 0x80000000) + data32 = read32(BAR+DCALCSR); + } + } + + /* MRS: Set DLLs to normal */ + udelay(16); + for (cs = 0; cs < 2; cs++) { + print_debug("MRS CS"); + print_debug_hex8(cs); + print_debug("\r\n"); + write32(BAR+DCALADDR, (mode_reg & ~(1<<24))); + write32(BAR+DCALCSR, (0x80000003 | ((cs+1)<<21))); + data32 = read32(BAR+DCALCSR); + while (data32 & 0x80000000) + data32 = read32(BAR+DCALCSR); + } + + /* EMRS: Enable DLLs */ + udelay(16); + for (cs = 0; cs < 2; cs++) { + print_debug("EMRS CS"); + print_debug_hex8(cs); + print_debug("\r\n"); + write32(BAR+DCALADDR, 0x0b840001); + write32(BAR+DCALCSR, (0x80000003 | ((cs+1)<<21))); + data32 = read32(BAR+DCALCSR); + while (data32 & 0x80000000) + data32 = read32(BAR+DCALCSR); + } + + udelay(16); + /* No command */ + write32(BAR+DCALCSR, 0x0000000f); + + write32(BAR, 0x00100000); + + /* Enable on-DIMM termination */ + set_on_dimm_termination_enable(ctrl); + + dump_dcal_regs(); + + /* Receive enable calibration */ + udelay(16); + for (cs = 0; cs < 1; cs++) { + print_debug("receive enable calibration CS"); + print_debug_hex8(cs); + print_debug("\r\n"); + write32(BAR+DCALCSR, (0x8000000c | ((cs+1)<<21))); + data32 = read32(BAR+DCALCSR); + while (data32 & 0x80000000) + data32 = read32(BAR+DCALCSR); + } + + dump_dcal_regs(); + + /* Adjust RCOMP */ + data32 = read32(BAR+DDRIOMC2); + data32 &= ~(0xf << 16); + data32 |= (0xb << 16); + write32(BAR+DDRIOMC2, data32); + + dump_dcal_regs(); + + data32 = drc & ~(3 << 20); /* clear ECC mode */ + pci_write_config32(ctrl->f0, DRC, data32); + write32(BAR+DCALCSR, 0x0008000f); + + /* Clear memory and init ECC */ + for (cs = 0; cs < 2; cs++) { + if (!(mask & (1<<cs))) + continue; + print_debug("clear memory CS"); + print_debug_hex8(cs); + print_debug("\r\n"); + write32(BAR+MBCSR, 0xa00000f0 | ((cs+1)<<20) | (0<<16)); + data32 = read32(BAR+MBCSR); + while (data32 & 0x80000000) + data32 = read32(BAR+MBCSR); + if (data32 & 0x40000000) + print_debug("failed!\r\n"); + } + + /* Clear read/write FIFO pointers */ + print_debug("clear read/write fifo pointers\r\n"); + write32(BAR+DDRIOMC2, read32(BAR+DDRIOMC2) | (1<<15)); + udelay(16); + write32(BAR+DDRIOMC2, read32(BAR+DDRIOMC2) & ~(1<<15)); + udelay(16); + + dump_dcal_regs(); + + print_debug("Done\r\n"); + + /* Set initialization complete */ + drc |= (1 << 29); + drc |= (3 << 30); + data32 = drc & ~(3 << 20); /* clear ECC mode */ + pci_write_config32(ctrl->f0, DRC, data32); + + /* Set the ECC mode */ + pci_write_config32(ctrl->f0, DRC, drc); + + /* The memory is now set up--use it */ + cache_lbmem(MTRR_TYPE_WRBACK); +} + +static inline int memory_initialized(void) +{ + return pci_read_config32(PCI_DEV(0, 0x00, 0), DRC) & (1 << 29); +}
Added: trunk/coreboot-v2/src/northbridge/intel/i3100/raminit_ep80579.h =================================================================== --- trunk/coreboot-v2/src/northbridge/intel/i3100/raminit_ep80579.h (rev 0) +++ trunk/coreboot-v2/src/northbridge/intel/i3100/raminit_ep80579.h 2008-09-24 15:06:34 UTC (rev 3600) @@ -0,0 +1,31 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2008 Arastra, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 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 + * + */ + +#ifndef NORTHBRIDGE_INTEL_I3100_RAMINIT_EP80579_H +#define NORTHBRIDGE_INTEL_I3100_RAMINIT_EP80579_H + +#define DIMM_SOCKETS 2 +struct mem_controller { + u32 node_id; + device_t f0; + u16 channel0[DIMM_SOCKETS]; +}; + +#endif