diff options
author | Richard Smith <smithbone@gmail.com> | 2006-07-24 04:25:47 +0000 |
---|---|---|
committer | Richard Smith <smithbone@gmail.com> | 2006-07-24 04:25:47 +0000 |
commit | cb8eab482ff09ec256456312ef2d6e7710123551 (patch) | |
tree | 7bc1297911194e564b967efba4a03c4dde5f7a13 /src/northbridge/intel | |
parent | 4788effb045ae1f71d89c78a0b16a93d5ba79e89 (diff) | |
download | coreboot-cb8eab482ff09ec256456312ef2d6e7710123551.tar.xz |
add framework for i440bx chipset
add support for NSC pc87351 SuperIO
add Bitworks/IMS manboard config
This is a very basic framework for the i440bx chipset and the
Bitworks IMS board that uses it. Most things are
structure only.
Known issues:
- SMbus reads to the RAM SPD come back
all zero.
- dump_spd_registers() is commented out since it breaks with
the default setting of generic_dump_spd.c where it wants
2 memory controllers.
git-svn-id: svn://svn.coreboot.org/coreboot/trunk@2347 2b7e53f0-3cfb-0310-b3e9-8179ed1497e1
Diffstat (limited to 'src/northbridge/intel')
-rw-r--r-- | src/northbridge/intel/i440bx/Config.lb | 2 | ||||
-rw-r--r-- | src/northbridge/intel/i440bx/chip.h | 5 | ||||
-rw-r--r-- | src/northbridge/intel/i440bx/northbridge.c | 186 | ||||
-rw-r--r-- | src/northbridge/intel/i440bx/northbridge.h | 6 | ||||
-rw-r--r-- | src/northbridge/intel/i440bx/raminit.c | 399 | ||||
-rw-r--r-- | src/northbridge/intel/i440bx/raminit.h | 11 |
6 files changed, 609 insertions, 0 deletions
diff --git a/src/northbridge/intel/i440bx/Config.lb b/src/northbridge/intel/i440bx/Config.lb new file mode 100644 index 0000000000..4a0c2c8658 --- /dev/null +++ b/src/northbridge/intel/i440bx/Config.lb @@ -0,0 +1,2 @@ +config chip.h +object northbridge.o diff --git a/src/northbridge/intel/i440bx/chip.h b/src/northbridge/intel/i440bx/chip.h new file mode 100644 index 0000000000..b0b3b72b4a --- /dev/null +++ b/src/northbridge/intel/i440bx/chip.h @@ -0,0 +1,5 @@ +struct northbridge_intel_i440bx_config +{ +}; + +extern struct chip_operations northbridge_intel_i440bx_ops; diff --git a/src/northbridge/intel/i440bx/northbridge.c b/src/northbridge/intel/i440bx/northbridge.c new file mode 100644 index 0000000000..ed262c24b7 --- /dev/null +++ b/src/northbridge/intel/i440bx/northbridge.c @@ -0,0 +1,186 @@ +#include <console/console.h> +#include <arch/io.h> +#include <stdint.h> +#include <device/device.h> +#include <device/pci.h> +#include <device/pci_ids.h> +#include <stdlib.h> +#include <string.h> +#include <bitops.h> +#include "chip.h" +#include "northbridge.h" + +/* +*/ +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 struct pci_driver northbridge_driver __pci_driver = { + .ops = &northbridge_operations, + .vendor = PCI_VENDOR_ID_INTEL, + .device = 0x7190, +}; + + + +#define BRIDGE_IO_MASK (IORESOURCE_IO | IORESOURCE_MEM) + +static void pci_domain_read_resources(device_t dev) +{ + struct resource *resource; + + /* Initialize the system wide io space constraints */ + resource = new_resource(dev, IOINDEX_SUBTRACTIVE(0,0)); + resource->limit = 0xffffUL; + resource->flags = IORESOURCE_IO | IORESOURCE_SUBTRACTIVE | IORESOURCE_ASSIGNED; + + /* Initialize the system wide memory resources constraints */ + resource = new_resource(dev, IOINDEX_SUBTRACTIVE(1,0)); + resource->limit = 0xffffffffULL; + resource->flags = IORESOURCE_MEM | IORESOURCE_SUBTRACTIVE | IORESOURCE_ASSIGNED; +} + +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; +} + +static void pci_domain_set_resources(device_t dev) +{ + static const uint8_t ramregs[] = { + 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x56, 0x57 + }; + 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; + unsigned char rambits; + int i, idx; + + for(rambits = 0, i = 0; i < sizeof(ramregs)/sizeof(ramregs[0]); i++) { + unsigned char reg; + reg = pci_read_config8(mc_dev, ramregs[i]); + /* these are ENDING addresses, not sizes. + * if there is memory in this slot, then reg will be > rambits. + * So we just take the max, that gives us total. + * We take the highest one to cover for once and future linuxbios + * bugs. We warn about bugs. + */ + if (reg > rambits) + rambits = reg; + if (reg < rambits) + printk_err("ERROR! register 0x%x is not set!\n", + ramregs[i]); + } + printk_debug("I would set ram size to 0x%x Kbytes\n", (rambits)*8*1024); + tomk = rambits*8*1024; + /* Compute the top of Low memory */ + tolmk = pci_tolm >> 10; + if (tolmk >= tomk) { + /* The PCI hole does does not overlap the memory. + */ + tolmk = tomk; + } + /* Report the memory regions */ + idx = 10; + ram_resource(dev, idx++, 0, tolmk); + } + assign_resources(&dev->link[0]); +} + +static unsigned int pci_domain_scan_bus(device_t dev, unsigned int max) +{ + max = pci_scan_bus(&dev->link[0], PCI_DEVFN(0, 0), 0xff, max); + return max; +} + +static struct device_operations pci_domain_ops = { + .read_resources = pci_domain_read_resources, + .set_resources = pci_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_i440bx_ops = { + CHIP_NAME("Intel 440bx Northbridge") + .enable_dev = enable_dev, +}; diff --git a/src/northbridge/intel/i440bx/northbridge.h b/src/northbridge/intel/i440bx/northbridge.h new file mode 100644 index 0000000000..3e569bb31b --- /dev/null +++ b/src/northbridge/intel/i440bx/northbridge.h @@ -0,0 +1,6 @@ +#ifndef NORTHBRIDGE_INTEL_440BX_H +#define NORTHBRIDGE_INTEL_440BX_H + +extern unsigned int i440bx_scan_root_bus(device_t root, unsigned int max); + +#endif /* NORTHBRIDGE_INTEL_440BX_H */ diff --git a/src/northbridge/intel/i440bx/raminit.c b/src/northbridge/intel/i440bx/raminit.c new file mode 100644 index 0000000000..737f2bf458 --- /dev/null +++ b/src/northbridge/intel/i440bx/raminit.c @@ -0,0 +1,399 @@ +#include <cpu/x86/mtrr.h> +#include "raminit.h" + +/* +This software and ancillary information (herein called SOFTWARE ) +called LinuxBIOS is made available under the terms described +here. The SOFTWARE has been approved for release with associated +LA-CC Number 00-34 . Unless otherwise indicated, this SOFTWARE has +been authored by an employee or employees of the University of +California, operator of the Los Alamos National Laboratory under +Contract No. W-7405-ENG-36 with the U.S. Department of Energy. The +U.S. Government has rights to use, reproduce, and distribute this +SOFTWARE. The public may copy, distribute, prepare derivative works +and publicly display this SOFTWARE without charge, provided that this +Notice and any statement of authorship are reproduced on all copies. +Neither the Government nor the University makes any warranty, express +or implied, or assumes any liability or responsibility for the use of +this SOFTWARE. If SOFTWARE is modified to produce derivative works, +such modified SOFTWARE should be clearly marked, so as not to confuse +it with the version available from LANL. + */ +/* Copyright 2000, Ron Minnich, Advanced Computing Lab, LANL + * rminnich@lanl.gov + */ +/* + * 11/26/02 - kevinh@ispiri.com - The existing comments implied that + * this didn't work yet. Therefore, I've updated it so that it works + * correctly - at least on my VIA epia motherboard. 64MB DIMM in slot 0. + */ + +/* Added automatic detection of first equipped bank and its MA mapping type. + * (Rest of configuration is done in C) + * 5/19/03 by SONE Takeshi <ts1@tsn.or.jp> + */ +/* converted to C 9/2003 Ron Minnich */ + +/* Modified for the i440bx Richard Smith 01/2005 */ + +/* Set to 1 if your DIMMs are PC133 Note that I'm assuming CPU's FSB + * frequency is 133MHz. If your CPU runs at another bus speed, you + * might need to change some of register values. + */ +#ifndef DIMM_PC133 +#define DIMM_PC133 0 +#endif + +// Set to 1 if your DIMMs are CL=2 +#ifndef DIMM_CL2 +#define DIMM_CL2 0 +#endif + +void dimms_read(unsigned long x) +{ + uint8_t c; + unsigned long eax; + volatile unsigned long y; + eax = x; + for(c = 0; c < 6; c++) { + y = * (volatile unsigned long *) eax; + eax += 0x10000000; + } +} + +void dimms_write(int x) +{ + uint8_t c; + unsigned long eax = x; + for(c = 0; c < 6; c++) { + *(volatile unsigned long *) eax = 0; + eax += 0x10000000; + } +} + + + +#ifdef DEBUG_SETNORTHB +void setnorthb(device_t north, uint8_t reg, uint8_t val) +{ + print_debug("setnorth: reg "); + print_debug_hex8(reg); + print_debug(" to "); + print_debug_hex8(val); + print_debug("\r\n"); + pci_write_config8(north, reg, val); +} +#else +#define setnorthb pci_write_config8 +#endif + +void +dumpnorth(device_t north) +{ + unsigned int r, c; + for(r = 0; ; r += 16) { + print_debug_hex8(r); + print_debug(":"); + for(c = 0; c < 16; c++) { + print_debug_hex8(pci_read_config8(north, r+c)); + print_debug(" "); + } + print_debug("\r\n"); + if (r >= 240) + break; + } +} + +static void sdram_set_registers(const struct mem_controller *ctrl) +{ + device_t north = (device_t) 0; + uint8_t c, r; + + print_err("vt8601 init starting\r\n"); + north = pci_locate_device(PCI_ID(0x1106, 0x8601), 0); + north = 0; + print_debug_hex32(north); + print_debug(" is the north\n"); + print_debug_hex16(pci_read_config16(north, 0)); + print_debug(" "); + print_debug_hex16(pci_read_config16(north, 2)); + print_debug("\r\n"); + + /* All we are doing now is setting initial known-good values that will + * be revised later as we read SPD + */ + // memory clk enable. We are not using ECC + pci_write_config8(north,0x78, 0x01); + print_debug_hex8(pci_read_config8(north, 0x78)); + // dram control, see the book. +#if DIMM_PC133 + pci_write_config8(north,0x68, 0x52); +#else + pci_write_config8(north,0x68, 0x42); +#endif + // dram control, see the book. + pci_write_config8(north,0x6B, 0x0c); + // Initial setting, 256MB in each bank, will be rewritten later. + pci_write_config8(north,0x5A, 0x20); + print_debug_hex8(pci_read_config8(north, 0x5a)); + pci_write_config8(north,0x5B, 0x40); + pci_write_config8(north,0x5C, 0x60); + pci_write_config8(north,0x5D, 0x80); + pci_write_config8(north,0x5E, 0xA0); + pci_write_config8(north,0x5F, 0xC0); + // It seems we have to take care of these 2 registers as if + // they are bank 6 and 7. + pci_write_config8(north,0x56, 0xC0); + pci_write_config8(north,0x57, 0xC0); + + // SDRAM in all banks + pci_write_config8(north,0x60, 0x3F); + // DRAM timing. I'm suspicious of this + // This is for all banks, 64 is 0,1. 65 is 2,3. 66 is 4,5. + // ras precharge 4T, RAS pulse 5T + // cas2 is 0xd6, cas3 is 0xe6 + // we're also backing off write pulse width to 2T, so result is 0xee +#if DIMM_CL2 + pci_write_config8(north,0x64, 0xd4); + pci_write_config8(north,0x65, 0xd4); + pci_write_config8(north,0x66, 0xd4); +#else // CL=3 + pci_write_config8(north,0x64, 0xe4); + pci_write_config8(north,0x65, 0xe4); + pci_write_config8(north,0x66, 0xe4); +#endif + + // dram frequency select. + // enable 4K pages for 64M dram. +#if DIMM_PC133 + pci_write_config8(north,0x69, 0x3c); +#else + pci_write_config8(north,0x69, 0xac); +#endif + + /* IMPORTANT -- disable refresh counter */ + // refresh counter, disabled. + pci_write_config8(north,0x6A, 0x00); + + + // clkenable configuration. kevinh FIXME - add precharge + pci_write_config8(north,0x6C, 0x00); + // dram read latch delay of 1 ns, MD drive 8 mA, + // high drive strength on MA[2: 13], we#, cas#, ras# + // As per Cindy Lee, set to 0x37, not 0x57 + pci_write_config8(north,0x6D, 0x7f); +} + +/* slot is the dram slot. Return size of side0 in lower 16-bit, + * side1 in upper 16-bit, in units of 8MB */ +static unsigned long +spd_module_size(unsigned char slot) +{ + /* for all the DRAMS, see if they are there and get the size of each + * module. This is just a very early first cut at sizing. + */ + /* we may run out of registers ... */ + unsigned int banks, rows, cols, reg; + unsigned int value = 0; + /* unsigned int module = ((0x50 + slot) << 1) + 1; */ + unsigned int module = 0x50 + slot; + /* is the module there? if byte 2 is not 4, then we'll assume it + * is useless. + */ + print_info("Slot "); + print_info_hex8(slot); + if (smbus_read_byte(module, 2) != 4) { + print_info(" is empty\r\n"); + return 0; + } + print_info(" is SDRAM "); + + banks = smbus_read_byte(module, 17); + /* we're going to assume symmetric banks. Sorry. */ + cols = smbus_read_byte(module, 4) & 0xf; + rows = smbus_read_byte(module, 3) & 0xf; + /* grand total. You have rows+cols addressing, * times of banks, times + * width of data in bytes */ + /* Width is assumed to be 64 bits == 8 bytes */ + value = (1 << (cols + rows)) * banks * 8; + print_info_hex32(value); + print_info(" bytes "); + /* Return in 8MB units */ + value >>= 23; + + /* We should have single or double side */ + if (smbus_read_byte(module, 5) == 2) { + print_info("x2"); + value = (value << 16) | value; + } + print_info("\r\n"); + return value; + +} + +static int +spd_num_chips(unsigned char slot) +{ +/* unsigned int module = ((0x50 + slot) << 1) + 1; */ + unsigned int module = 0x50 + slot; + unsigned int width; + + width = smbus_read_byte(module, 13); + if (width == 0) + width = 8; + return 64 / width; +} + +static void sdram_set_spd_registers(const struct mem_controller *ctrl) +{ +#define T133 7 + unsigned char Trp = 1, Tras = 1, casl = 2, val; + unsigned char timing = 0xe4; + /* read Trp */ + val = smbus_read_byte(0x50, 27); + if (val < 2*T133) + Trp = 1; + val = smbus_read_byte(0x50, 30); + if (val < 5*T133) + Tras = 0; + val = smbus_read_byte(0x50, 18); + if (val < 8) + casl = 1; + if (val < 4) + casl = 0; + + val = (Trp << 7) | (Tras << 6) | (casl << 4) | 4; + + print_debug_hex8(val); print_debug(" is the computed timing\n"); + /* don't set it. Experience shows that this screwy chipset should just + * be run with the most conservative timing. + * pci_write_config8(0, 0x64, val); + */ +} + +static void set_ma_mapping(device_t north, int slot, int type) +{ + unsigned char reg, val; + int shift; + + reg = 0x58 + slot/2; + if (slot%2 >= 1) + shift = 0; + else + shift = 4; + + val = pci_read_config8(north, reg); + val &= ~(0xf << shift); + val |= type << shift; + pci_write_config8(north, reg, val); +} + + +static void sdram_enable(int controllers, const struct mem_controller *ctrl) +{ + unsigned char i; + static const uint8_t ramregs[] = { + 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x56, 0x57 + }; + device_t north = 0; + uint32_t size, base, slot, ma; + /* begin to initialize*/ + // I forget why we need this, but we do + dimms_write(0xa55a5aa5); + + /* set NOP*/ + pci_write_config8(north,0x6C, 0x01); + print_debug("NOP\r\n"); + /* wait 200us*/ + // You need to do the memory reference. That causes the nop cycle. + dimms_read(0); + udelay(400); + print_debug("PRECHARGE\r\n"); + /* set precharge */ + pci_write_config8(north,0x6C, 0x02); + print_debug("DUMMY READS\r\n"); + /* dummy reads*/ + dimms_read(0); + udelay(200); + print_debug("CBR\r\n"); + /* set CBR*/ + pci_write_config8(north,0x6C, 0x04); + + /* do 8 reads and wait >100us between each - from via*/ + dimms_read(0); + udelay(200); + dimms_read(0); + udelay(200); + dimms_read(0); + udelay(200); + dimms_read(0); + udelay(200); + dimms_read(0); + udelay(200); + dimms_read(0); + udelay(200); + dimms_read(0); + udelay(200); + dimms_read(0); + udelay(200); + print_debug("MRS\r\n"); + /* set MRS*/ + pci_write_config8(north,0x6c, 0x03); +#if DIMM_CL2 + dimms_read(0x150); +#else // CL=3 + dimms_read(0x1d0); +#endif + udelay(200); + print_debug("NORMAL\r\n"); + /* set to normal mode */ + pci_write_config8(north,0x6C, 0x08); + + dimms_write(0x55aa55aa); + dimms_read(0); + udelay(200); + print_debug("set ref. rate\r\n"); + // Set the refresh rate. +#if DIMM_PC133 + pci_write_config8(north,0x6A, 0x86); +#else + pci_write_config8(north,0x6A, 0x65); +#endif + print_debug("enable multi-page open\r\n"); + // enable multi-page open + pci_write_config8(north,0x6B, 0x0d); + + base = 0; + for(slot = 0; slot < 4; slot++) { + size = spd_module_size(slot); + /* side 0 */ + base += size & 0xffff; + pci_write_config8(north, ramregs[2*slot], base); + /* side 1 */ + base += size >> 16; + if (base > 0xff) + base = 0xff; + pci_write_config8(north, ramregs[2*slot + 1], base); + + if (!size) + continue; + + /* Calculate the value of MA mapping type register, + * based on size of SDRAM chips. */ + size = (size & 0xffff) << (3 + 3); + /* convert module size to be in Mbits */ + size /= spd_num_chips(slot); + print_debug_hex16(size); + print_debug(" is the chip size\r\n"); + if (size < 64) + ma = 0; + if (size < 256) + ma = 8; + else + ma = 0xe; + print_debug_hex16(ma); + print_debug(" is the MA type\r\n"); + set_ma_mapping(north, slot, ma); + } + print_err("vt8601 done\r\n"); +} diff --git a/src/northbridge/intel/i440bx/raminit.h b/src/northbridge/intel/i440bx/raminit.h new file mode 100644 index 0000000000..6e40683066 --- /dev/null +++ b/src/northbridge/intel/i440bx/raminit.h @@ -0,0 +1,11 @@ +#ifndef RAMINIT_H +#define RAMINIT_H + +#define DIMM_SOCKETS 4 +struct mem_controller { + device_t d0; + uint16_t channel0[DIMM_SOCKETS]; +}; + + +#endif /* RAMINIT_H */ |