diff --git a/src/northbridge/intel/Kconfig b/src/northbridge/intel/Kconfig index eae68f1e37..983aca86d1 100644 --- a/src/northbridge/intel/Kconfig +++ b/src/northbridge/intel/Kconfig @@ -3,6 +3,7 @@ source src/northbridge/intel/e7520/Kconfig source src/northbridge/intel/e7525/Kconfig source src/northbridge/intel/i3100/Kconfig source src/northbridge/intel/i440bx/Kconfig +source src/northbridge/intel/i440lx/Kconfig source src/northbridge/intel/i82810/Kconfig source src/northbridge/intel/i82830/Kconfig source src/northbridge/intel/i855gme/Kconfig diff --git a/src/northbridge/intel/Makefile.inc b/src/northbridge/intel/Makefile.inc index d89a28f770..fa04dd20d5 100644 --- a/src/northbridge/intel/Makefile.inc +++ b/src/northbridge/intel/Makefile.inc @@ -3,6 +3,7 @@ subdirs-$(CONFIG_NORTHBRIDGE_INTEL_E7520) += e7520 subdirs-$(CONFIG_NORTHBRIDGE_INTEL_E7525) += e7525 subdirs-$(CONFIG_NORTHBRIDGE_INTEL_I3100) += i3100 subdirs-$(CONFIG_NORTHBRIDGE_INTEL_I440BX) += i440bx +subdirs-$(CONFIG_NORTHBRIDGE_INTEL_I440LX) += i440lx subdirs-$(CONFIG_NORTHBRIDGE_INTEL_I82810) += i82810 subdirs-$(CONFIG_NORTHBRIDGE_INTEL_I82830) += i82830 subdirs-$(CONFIG_NORTHBRIDGE_INTEL_I855GME) += i855gme diff --git a/src/northbridge/intel/i440lx/Config.lb b/src/northbridge/intel/i440lx/Config.lb new file mode 100644 index 0000000000..fb23caaa93 --- /dev/null +++ b/src/northbridge/intel/i440lx/Config.lb @@ -0,0 +1,27 @@ +## +## This file is part of the coreboot project. +## +## Copyright (C) 2007 Uwe Hermann +## +## 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 +## + +uses CONFIG_WRITE_HIGH_TABLES + +config chip.h + +driver northbridge.o + +default CONFIG_WRITE_HIGH_TABLES=1 diff --git a/src/northbridge/intel/i440lx/Kconfig b/src/northbridge/intel/i440lx/Kconfig new file mode 100644 index 0000000000..2dd58c63e1 --- /dev/null +++ b/src/northbridge/intel/i440lx/Kconfig @@ -0,0 +1,24 @@ +## +## This file is part of the coreboot project. +## +## Copyright (C) 2009 Uwe Hermann +## +## 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 +## + +config NORTHBRIDGE_INTEL_I440LX + bool + select HAVE_HIGH_TABLES + diff --git a/src/northbridge/intel/i440lx/Makefile.inc b/src/northbridge/intel/i440lx/Makefile.inc new file mode 100644 index 0000000000..f4ef7d49c2 --- /dev/null +++ b/src/northbridge/intel/i440lx/Makefile.inc @@ -0,0 +1,22 @@ +## +## This file is part of the coreboot project. +## +## Copyright (C) 2009 Uwe Hermann +## +## 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 +## + +driver-y += northbridge.o + diff --git a/src/northbridge/intel/i440lx/chip.h b/src/northbridge/intel/i440lx/chip.h new file mode 100644 index 0000000000..8d800f8cac --- /dev/null +++ b/src/northbridge/intel/i440lx/chip.h @@ -0,0 +1,25 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2009 Maciej Pijanka + * + * 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 + */ + +struct northbridge_intel_i440lx_config +{ +}; + +extern struct chip_operations northbridge_intel_i440lx_ops; diff --git a/src/northbridge/intel/i440lx/i440lx.h b/src/northbridge/intel/i440lx/i440lx.h new file mode 100644 index 0000000000..9b870410b5 --- /dev/null +++ b/src/northbridge/intel/i440lx/i440lx.h @@ -0,0 +1,73 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2006 Uwe Hermann + * Copyright (C) 2009 Maciej Pijanka + * + * 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 + */ + +/* + * Datasheet: + * - Name: Intel 440LX AGPset: 82443LX Host Bridge/Controller + * - PDF: 29056402.pdf + */ + +/* + * Host-to-PCI Bridge Registers. + * The values in parenthesis are the default values as per datasheet. + * Any addresses between 0x00 and 0xff not listed below are either + * + * i didnt listed every register that IS implemented, just usefull ones + * -- Maciej `agaran` Pijanka + * + * Reserved or Intel Reserved and should not be touched. + */ +#define APBASE 0x34 /* Aperture Base Address (0x00000008) */ +#define PACCFG 0x50 /* 440LX PAC Configuration Register (0s00_s000_0000_0s00b) */ +#define DBC 0x53 /* DRAM Row Type Register (0x83) */ +#define DRT 0x55 /* DRAM Row Type Register (0x0000) */ +#define DRAMC 0x57 /* DRAM Control (0x01) */ +#define DRAMT 0x58 /* DRAM Timing (0x00) */ +#define PAM 0x59 /* Programmable Attribute Map, 7 registers (0x00). */ +#define DRB 0x60 /* DRAM Row Boundary, 8 registers (0x01). */ +#define FDHC 0x68 /* Fixed SDRAM Hole Control (0x00). */ +#define DRAMXC 0x6A /* Dram Extended Control Register (0x0000) */ +#define MBSC 0x6C /* Memory Buffer Strength Control: (0x55555555) */ + +#define SMRAM 0x72 /* System Management RAM Control (0x02). */ +#define ERRCMD 0x90 /* Error Command Register (0x80). */ +#define ERRSTS0 0x91 /* Error Status (0x0000). */ +#define ERRSTS1 0x92 /* Error Status (0x0000). */ +// TODO: AGP stuff. + +/* For convenience: */ +#define DRB0 0x60 +#define DRB1 0x61 +#define DRB2 0x62 +#define DRB3 0x63 +#define DRB4 0x64 +#define DRB5 0x65 +#define DRB6 0x66 +#define DRB7 0x67 + +#define PAM0 0x59 +#define PAM1 0x5a +#define PAM2 0x5b +#define PAM3 0x5c +#define PAM4 0x5d +#define PAM5 0x5e +#define PAM6 0x5f + diff --git a/src/northbridge/intel/i440lx/northbridge.c b/src/northbridge/intel/i440lx/northbridge.c new file mode 100644 index 0000000000..41cc43bbc4 --- /dev/null +++ b/src/northbridge/intel/i440lx/northbridge.c @@ -0,0 +1,192 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2007 Uwe Hermann + * Copyright (C) 2009 Maciej Pijanka + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "chip.h" +#include "northbridge.h" +#include "i440lx.h" + +/* This code is mostly same as 440BX created by Uwe Hermann, + * i done only very minor changes like renamed functions to 440lx etc + * Maciej + */ + +/* TODO: + * - maybe this could print Northbridge i440LX Init? + */ +static void northbridge_init(device_t dev) +{ + printk_spew("Northbridge Init\n"); +} + +static struct device_operations northbridge_operations = { + .read_resources = pci_dev_read_resources, + .set_resources = pci_dev_set_resources, + .enable_resources = pci_dev_enable_resources, + .init = northbridge_init, + .enable = 0, + .ops_pci = 0, +}; + +static const struct pci_driver northbridge_driver __pci_driver = { + .ops = &northbridge_operations, + .vendor = PCI_VENDOR_ID_INTEL, + .device = 0x7180, +}; + +static void ram_resource(device_t dev, unsigned long index, + unsigned long basek, unsigned long sizek) +{ + struct resource *resource; + + if (!sizek) { + return; + } + resource = new_resource(dev, index); + resource->base = ((resource_t)basek) << 10; + resource->size = ((resource_t)sizek) << 10; + resource->flags = IORESOURCE_MEM | IORESOURCE_CACHEABLE | \ + IORESOURCE_FIXED | IORESOURCE_STORED | IORESOURCE_ASSIGNED; +} + +static void tolm_test(void *gp, struct device *dev, struct resource *new) +{ + struct resource **best_p = gp; + struct resource *best; + best = *best_p; + if (!best || (best->base > new->base)) { + best = new; + } + *best_p = best; +} + +static uint32_t find_pci_tolm(struct bus *bus) +{ + struct resource *min; + uint32_t tolm; + min = 0; + search_bus_resources(bus, IORESOURCE_MEM, IORESOURCE_MEM, tolm_test, &min); + tolm = 0xffffffffUL; + if (min && tolm > min->base) { + tolm = min->base; + } + return tolm; +} + +#if CONFIG_WRITE_HIGH_TABLES==1 +#define HIGH_TABLES_SIZE 64 // maximum size of high tables in KB +extern uint64_t high_tables_base, high_tables_size; +#endif + +static void i440lx_domain_set_resources(device_t dev) +{ + device_t mc_dev; + uint32_t pci_tolm; + + pci_tolm = find_pci_tolm(&dev->link[0]); + mc_dev = dev->link[0].children; + if (mc_dev) { + unsigned long tomk, tolmk; + int idx; + + /* Figure out which areas are/should be occupied by RAM. The + * value of the highest DRB denotes the end of the physical + * memory (in units of 8MB). + */ + tomk = ((unsigned long)pci_read_config8(mc_dev, DRB7)); + + /* Convert to KB. */ + tomk *= (8 * 1024); + + printk_debug("Setting RAM size to %lu MB\n", tomk / 1024); + + /* Compute the top of low memory. */ + tolmk = pci_tolm / 1024; + + if (tolmk >= tomk) { + /* The PCI hole does not overlap the memory. */ + tolmk = tomk; + } + + /* Report the memory regions. */ + idx = 10; + ram_resource(dev, idx++, 0, 640); + ram_resource(dev, idx++, 768, tolmk - 768); + +#if CONFIG_WRITE_HIGH_TABLES==1 + /* Leave some space for ACPI, PIRQ and MP tables */ + high_tables_base = (tomk - HIGH_TABLES_SIZE) * 1024; + high_tables_size = HIGH_TABLES_SIZE * 1024; +#endif + } + assign_resources(&dev->link[0]); +} + +static struct device_operations pci_domain_ops = { + .read_resources = pci_domain_read_resources, + .set_resources = i440lx_domain_set_resources, + .enable_resources = enable_childrens_resources, + .init = 0, + .scan_bus = pci_domain_scan_bus, +}; + +static void cpu_bus_init(device_t dev) +{ + initialize_cpus(&dev->link[0]); +} + +static void cpu_bus_noop(device_t dev) +{ +} + +static struct device_operations cpu_bus_ops = { + .read_resources = cpu_bus_noop, + .set_resources = cpu_bus_noop, + .enable_resources = cpu_bus_noop, + .init = cpu_bus_init, + .scan_bus = 0, +}; + +static void enable_dev(struct device *dev) +{ + /* Set the operations if it is a special bus type */ + if (dev->path.type == DEVICE_PATH_PCI_DOMAIN) { + dev->ops = &pci_domain_ops; + pci_set_method(dev); + } + else if (dev->path.type == DEVICE_PATH_APIC_CLUSTER) { + dev->ops = &cpu_bus_ops; + } +} + +struct chip_operations northbridge_intel_i440lx_ops = { + CHIP_NAME("Intel 82443LX (440LX) Northbridge") + .enable_dev = enable_dev, +}; diff --git a/src/northbridge/intel/i440lx/northbridge.h b/src/northbridge/intel/i440lx/northbridge.h new file mode 100644 index 0000000000..cdc7d3379e --- /dev/null +++ b/src/northbridge/intel/i440lx/northbridge.h @@ -0,0 +1,26 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2009 Maciej Pijanka + * + * 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 + */ + +#ifndef NORTHBRIDGE_INTEL_440LX_H +#define NORTHBRIDGE_INTEL_440LX_H + +extern unsigned int i440lx_scan_root_bus(device_t root, unsigned int max); + +#endif /* NORTHBRIDGE_INTEL_440LX_H */ diff --git a/src/northbridge/intel/i440lx/raminit.c b/src/northbridge/intel/i440lx/raminit.c new file mode 100644 index 0000000000..4c171ef92d --- /dev/null +++ b/src/northbridge/intel/i440lx/raminit.c @@ -0,0 +1,455 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2007-2008 Uwe Hermann + * Copyright (C) 2009 Maciej Pijanka + * + * 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 +#include +#include +#include +#include "i440lx.h" + +/*----------------------------------------------------------------------------- +Macros and definitions. +-----------------------------------------------------------------------------*/ + +/* Uncomment this to enable debugging output. */ + +/* Debugging macros. */ +#if defined(DEBUG_RAM_SETUP) +#define PRINT_DEBUG(x) print_debug(x) +#define PRINT_DEBUG_HEX8(x) print_debug_hex8(x) +#define PRINT_DEBUG_HEX16(x) print_debug_hex16(x) +#define PRINT_DEBUG_HEX32(x) print_debug_hex32(x) +#define DUMPNORTH() dump_pci_device(PCI_DEV(0, 0, 0)) +#else +#define PRINT_DEBUG(x) +#define PRINT_DEBUG_HEX8(x) +#define PRINT_DEBUG_HEX16(x) +#define PRINT_DEBUG_HEX32(x) +#define DUMPNORTH() +#endif + +#define NB PCI_DEV(0, 0, 0) + +/* DRAMXC[7:5] - DRAM extended control register (SMS). */ +#define RAM_COMMAND_NORMAL 0x0 +#define RAM_COMMAND_NOP 0x1 // (NOPCE) +#define RAM_COMMAND_PRECHARGE 0x2 // ABPCE +#define RAM_COMMAND_MRS 0x3 // MRSCE +#define RAM_COMMAND_CBR 0x4 // CBRC +// rest are reserved + +/* Table format: register, bitmask, value. */ +static const long register_values[] = { + // ~0x02 == bit 9 + // 0x04 == bit 10 + // BASE is 0x8A but we dont want bit 9 or 10 have ENABLED so 0x8C + PACCFG + 1, 0x38, 0x8c, + + DBC, 0x00, 0xC3, + + DRT, 0x00, 0xFF, + DRT+1, 0x00, 0xFF, + + DRAMC, 0x00, 0x00, /* disable refresh for now. */ + DRAMT, 0x00, 0x00, + + PAM0, 0x00, 0x30, // everything is a mem + PAM1, 0x00, 0x33, + PAM2, 0x00, 0x33, + PAM3, 0x00, 0x33, + PAM4, 0x00, 0x33, + PAM5, 0x00, 0x33, + PAM6, 0x00, 0x33, + + /* Set the DRBs to zero for now, this will be fixed later. */ + DRB0, 0x00, 0x00, + DRB1, 0x00, 0x00, + DRB2, 0x00, 0x00, + DRB3, 0x00, 0x00, + DRB4, 0x00, 0x00, + DRB5, 0x00, 0x00, + DRB6, 0x00, 0x00, + DRB7, 0x00, 0x00, + + /* No memory holes. */ + FDHC, 0x00, 0x00, +}; + +/*----------------------------------------------------------------------------- +SDRAM configuration functions. +-----------------------------------------------------------------------------*/ + +/** + * Send the specified RAM command to all DIMMs. + * + * @param command The RAM command to send to the DIMM(s). + */ +static void do_ram_command(u32 command) +{ + int i, caslatency; + u8 dimm_start, dimm_end; + u16 reg16; + u32 addr, addr_offset; + + /* Configure the RAM command. */ + reg16 = pci_read_config16(NB, DRAMXC); + reg16 &= 0xff1f; /* Clear bits 7-5. */ + reg16 |= (u16) (command << 5); /* Write command into bits 7-5. */ + pci_write_config16(NB, DRAMXC, reg16); + + /* + * RAM_COMMAND_NORMAL affects only the memory controller and + * doesn't need to be "sent" to the DIMMs. + */ + if (command == RAM_COMMAND_NORMAL) + return; + + /* 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. */ + + /* before translation it is + * + * M[02:00] Burst Length + * M[03:03] Burst Type + * M[06:04] Cas Latency + * 000 - Reserved + * 001 - Reserved + * 010 - CAS 2 + * 011 - CAS 3 + * 100 - Reserved + * 101 - Reserved + * 110 - Reserved + * 111 - Reserved + * M[08:07] Op Mode + * Must Be 00b (Defined mode) + * M[09:09] Write Burst Mode + * 0 - Programmed burst length + * 1 - Single location access + * M[11:10] Reserved + * write 0 to ensure compatibility with.... + */ + + /* seems constructed value will be right shifted by 3 bit, thus constructed value + * must be left shifted by 3 + * so possible formula is (caslatency <<4)|(burst_type << 1)|(burst length) + * then << 3 shift to compensate shift in Memory Controller + */ + if (command == RAM_COMMAND_MRS) { + if (caslatency == 3) + addr_offset = 0x1d0; + if (caslatency == 2) + addr_offset = 0x150; + } + + dimm_end = pci_read_config8(NB, DRB + i); + + addr = (dimm_start * 8 * 1024 * 1024) + addr_offset; + if (dimm_end > dimm_start) { +#if 0 + PRINT_DEBUG(" Sending RAM command 0x"); + PRINT_DEBUG_HEX16(reg16); + PRINT_DEBUG(" to 0x"); + PRINT_DEBUG_HEX32(addr); + PRINT_DEBUG("\r\n"); +#endif + + read32(addr); + } + + /* Set the start of the next DIMM. */ + dimm_start = dimm_end; + } +} + +/*----------------------------------------------------------------------------- +DIMM-independant configuration functions. +-----------------------------------------------------------------------------*/ + +static void spd_enable_refresh(void) +{ + uint8_t reg; + + reg = pci_read_config8(NB, DRAMC); + + /* this chipset offer only two choices regarding refresh + * refresh disabled, or refresh normal + */ + + pci_write_config8(NB, DRAMC, reg | 0x01); + reg = pci_read_config8(NB, DRAMC); + + PRINT_DEBUG("spd_enable_refresh: dramc = 0x"); + PRINT_DEBUG_HEX8(reg); + PRINT_DEBUG("\r\n"); +} + +/*----------------------------------------------------------------------------- +Public interface. +-----------------------------------------------------------------------------*/ + +static void northbridge_init(void) +{ + uint32_t reg32; + + reg32 = pci_read_config32(NB, APBASE); + reg32 &= 0xe8000000U; + pci_write_config32(NB, APBASE, reg32); + + #ifdef DEBUG_RAM_SETUP + /* + * apbase dont get set still, no idea what i have doing wrong yet, + * i am almost sure that somehow i set it by mistake once, but can't + * repeat that. + */ + reg32 = pci_read_config32(NB, APBASE); + PRINT_DEBUG("APBASE "); + PRINT_DEBUG_HEX32(reg32); + PRINT_DEBUG("\r\n"); + #endif +} + + +/** + * This routine sets RAM controller inside northbridge to known state + * + */ +static void sdram_set_registers(void) +{ + int i, max; + + /* nice banner with FSB shown? do we have + * any standart policy about such things? + */ +#if 0 + uint16_t reg16; + reg16 = pci_read_config16(NB, PACCFG); + printk_debug("i82443LX Host Freq: 6%C MHz\r\n", (reg16 & 0x4000) ? '0' : '6'); +#endif + + PRINT_DEBUG("Northbridge prior to SDRAM init:\r\n"); + DUMPNORTH(); + + northbridge_init(); + + max = ARRAY_SIZE(register_values); + + /* Set registers as specified in the register_values[] array. */ + for (i = 0; i < max; i += 3) { + uint8_t reg,tmp; + reg = pci_read_config8(NB, register_values[i]); + reg &= register_values[i + 1]; + reg |= register_values[i + 2] & ~(register_values[i + 1]); + pci_write_config8(NB, register_values[i], reg); + + /* + * i am not sure if that is needed, but was usefull + * for me to confirm what got written + */ +#ifdef DEBUG_RAM_SETUP + PRINT_DEBUG(" Set register 0x"); + PRINT_DEBUG_HEX8(register_values[i]); + PRINT_DEBUG(" to 0x"); + PRINT_DEBUG_HEX8(reg); + tmp = pci_read_config8(NB, register_values[i]); + PRINT_DEBUG(" readed 0x"); + PRINT_DEBUG_HEX8(tmp); + if (tmp == reg) { + PRINT_DEBUG(" OK "); + } else { + PRINT_DEBUG(" FAIL "); + } + PRINT_DEBUG("\r\n"); +#endif + } + + PRINT_DEBUG("Northbridge atexit sdram set registers\r\n"); + DUMPNORTH(); +} + + +static void sdram_set_spd_registers(void) +{ + int i; + u16 memsize = 0; + + for (i = 0; i < DIMM_SOCKETS; i++) { + uint16_t ds = 0; // dimm size + int j; + /* this code skips second bank on each socket (no idea how to fix it now) + */ + + PRINT_DEBUG("DIMM"); + PRINT_DEBUG_HEX8(i); + PRINT_DEBUG(" rows: "); + PRINT_DEBUG_HEX8(spd_read_byte(DIMM_SPD_BASE + i, SPD_NUM_DIMM_BANKS) & 0xFF); + PRINT_DEBUG(" rowsize: "); + PRINT_DEBUG_HEX8(spd_read_byte(DIMM_SPD_BASE + i, SPD_DENSITY_OF_EACH_ROW_ON_MODULE) & 0xFF); + PRINT_DEBUG(" modulesize: "); + + j = spd_read_byte(DIMM_SPD_BASE + i, SPD_NUM_DIMM_BANKS); + if (j < 0) + j = 0; + else + ds = j; + + j = spd_read_byte(DIMM_SPD_BASE + i, SPD_DENSITY_OF_EACH_ROW_ON_MODULE); + + if (j < 0) + j = 0; + else + ds = ds * (j >> 1); // convert from 4MB to 8MB units in place + + + /* This is more or less crude hack + * allowing to run this target under qemu (even if that is not really + * same hardware emulated), + * probably some kconfig expert option should be added to enable/disable + * this nicelly + */ +#if 0 + if (ds == 0 && memsize == 0) + ds = 0x8; +#endif + + + // todo: support for bank with not equal sizes as per jedec standart? + + /* + * because density is reported in units of 4Mbyte + * and rows in device are just value, + * and for setting registers we need value in 8Mbyte units + */ + + PRINT_DEBUG_HEX16(ds); + PRINT_DEBUG("\r\n"); + + memsize += ds; + + pci_write_config8(NB, DRB + (2*i), memsize); + pci_write_config8(NB, DRB + (2*i) + 1, memsize); + if (ds > 0) { + /* i have no idea why pci_read_config16 not work for + * me correctly here + */ + ds = pci_read_config8(NB, DRT+1); + ds <<=8; + ds |= pci_read_config8(NB, DRT); + + PRINT_DEBUG("DRT "); + PRINT_DEBUG_HEX16(ds); + + ds &= ~(0x01 << (4 * i)); + + PRINT_DEBUG(" "); + PRINT_DEBUG_HEX16(ds); + PRINT_DEBUG("\r\n"); + + /* + * modify DRT register if current row isn't empty + * code assume its SDRAM plugged (should check if its sdram or EDO, + * edo would have 0x00 as constand instead 0x10 for SDRAM + * also this code is buggy because ignores second row of each dimm socket + */ + + /* and as above write_config16 not work here too) + * pci_write_config16(NB, DRT, j); + */ + + pci_write_config8(NB, DRT+1, ds >> 8); + pci_write_config8(NB, DRT, ds & 0xFF); + } + } + +#if 0 + PRINT_DEBUG("Mem: 0x"); + PRINT_DEBUG_HEX16(memsize * 8); + PRINT_DEBUG(" MB\r\n"); + + if (memsize == 0) { + /* maybe we should use some nice die/hlt sequence with printing on console + * that no memory found, get lost, i can't run? + * maybe such event_handler can be commonly defined routine to decrease + * code duplication? + */ + PRINT_DEBUG("No memory detected via SPD\r\n"); + PRINT_DEBUG("Reverting to hardcoded 64M single side dimm in first bank\r\n"); + } +#endif + + /* Set DRAMC. Don't enable refresh for now. */ + pci_write_config8(NB, DRAMC, 0x00); + + /* Cas latency 3, and other shouldbe properly from spd too */ + pci_write_config8(NB, DRAMT, 0xAC); + + /* TODO? */ + pci_write_config8(NB, PCI_LATENCY_TIMER, 0x40); + + // set drive strength + pci_write_config32(NB, MBSC, 0x00000000); +} + +static void sdram_enable(void) +{ + int i; + + /* 0. Wait until power/voltages and clocks are stable (200us). */ + udelay(200); + + /* 1. Apply NOP. Wait 200 clock cycles (clock might be 60 or 66 Mhz). */ + PRINT_DEBUG("RAM Enable 1: Apply NOP\r\n"); + do_ram_command(RAM_COMMAND_NOP); + udelay(200); + + /* 2. Precharge all. Wait tRP. */ + PRINT_DEBUG("RAM Enable 2: Precharge all\r\n"); + do_ram_command(RAM_COMMAND_PRECHARGE); + udelay(1); + + /* 3. Perform 8 refresh cycles. Wait tRC each time. */ + PRINT_DEBUG("RAM Enable 3: CBR\r\n"); + for (i = 0; i < 8; i++) { + do_ram_command(RAM_COMMAND_CBR); + udelay(1); + } + + /* 4. Mode register set. Wait two memory cycles. */ + PRINT_DEBUG("RAM Enable 4: Mode register set\r\n"); + do_ram_command(RAM_COMMAND_MRS); + udelay(2); + + /* 5. Normal operation. */ + PRINT_DEBUG("RAM Enable 5: Normal operation\r\n"); + do_ram_command(RAM_COMMAND_NORMAL); + udelay(1); + + /* 6. Finally enable refresh. */ + PRINT_DEBUG("RAM Enable 6: Enable refresh\r\n"); + pci_write_config8(NB, DRAMC, 0x01); + spd_enable_refresh(); + udelay(1); + + PRINT_DEBUG("Northbridge following SDRAM init:\r\n"); +} + diff --git a/src/northbridge/intel/i440lx/raminit.h b/src/northbridge/intel/i440lx/raminit.h new file mode 100644 index 0000000000..37331f4374 --- /dev/null +++ b/src/northbridge/intel/i440lx/raminit.h @@ -0,0 +1,31 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2007 Uwe Hermann + * Copyright (C) 2009 Maciej Pijanka + * + * 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 + */ + +#ifndef RAMINIT_H +#define RAMINIT_H + +/* The 440LX supports up to four (single- or double-sided) DIMMs. */ +#define DIMM_SOCKETS 4 + +/* DIMMs 1-4 are at 0x50, 0x51, 0x52, 0x53. */ +#define DIMM_SPD_BASE 0x50 + +#endif /* RAMINIT_H */