On 03/02/2010 11:19 PM, Keith Hui wrote:
Hi all,
This thing is now ready for more exposure. Scratch my previous "patch" - this is my first real deal.
- Adds Asus P2B-LS mainboard
- Adds RAM detection for i440bx (based on i82830 code). We're no longer
hard coded for 64MB on one row!
- Adds a proper Slot 1 cpu under src/cpu/intel/slot_1. It's a stub
copied from slot_2 but addresses a few FIXMEs. My P2B-LS code refers to this.
- Adds microcode for Intel Tualatin CPUs, cpuid 6B1 and 6B4.* Actually
loading them is pending.
Signed-off-by: Keith Hui <buurin@gmail.com mailto:buurin@gmail.com>
Enjoy.
Keith
- Microcodes for all Intel CPUs can be downloaded from Intel -
downloadcenter.intel.com http://downloadcenter.intel.com. So TODO for me is to add all microcode updates from Klamath to Tualatin as the Asus P2B family, with the right mods and/or adapter, can run anything in between that can fit either Slot 1 or Socket 370.
I would not worry about the microcode updates right now. CAR for Intel 6bx is coming real soon and the microcode updates will be included :-)
See other comments below.
Index: src/northbridge/intel/i440bx/raminit.c =================================================================== --- src/northbridge/intel/i440bx/raminit.c (revision 5184) +++ src/northbridge/intel/i440bx/raminit.c (working copy) @@ -2,6 +2,7 @@ * This file is part of the coreboot project. * * Copyright (C) 2007-2008 Uwe Hermann uwe@hermann-uwe.de + * Copyright (C) 2010 Keith Hui buurin@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 @@ -23,6 +24,7 @@ #include <delay.h> #include <stdlib.h> #include "i440bx.h" +#include "raminit.h"
/*----------------------------------------------------------------------------- Macros and definitions. @@ -65,12 +67,24 @@ * [4] == Extended(4x) 62.5 us -> 62.4 us * [5] == Extended(8x) 125 us -> 124.8 us */ -static const uint32_t refresh_rate_map[] = { - 1, 5, 5, 2, 3, 4 +static const unsigned int refresh_rate_map[] = { + 1, 1, 1, 2, 3, 4 };
/* Table format: register, bitmask, value. */ static const long register_values[] = { + /* MLT - Master Latency Timer Register + * 0x0d + * + * [07:03] Master Latency Timer Count Value for PCI Bus Access. + * MLT is an 8-bit register that controls the amount of + * time the 82443BX, as a PCI bus master, can burst data + * on the PCI Bus. The default value of MLT is 00h and + * disables this function. For example, if the MLT is + * programmed to 18h, then the value is 24 PCI clocks. + * [02:00] Reserved + */ + MLT, 0x00, 0x40, /* NBXCFG - NBX Configuration Register * 0x50 - 0x53 * @@ -188,14 +202,21 @@ * 10 = Write Only (Writes to DRAM, reads to memory mapped I/O space) * 11 = Read/Write (all access goes to DRAM) */ - // TODO - PAM0, 0x00, 0x00, - PAM1, 0x00, 0x00, - PAM2, 0x00, 0x00, - PAM3, 0x00, 0x00, - PAM4, 0x00, 0x00, - PAM5, 0x00, 0x00, - PAM6, 0x00, 0x00, + /* Map all legacy regions to RAM (read/write). This is required if + * you want to use the RAM area from 768 KB - 1 MB. If the PAM + * registers are not set here appropriately, the RAM in that region + * will not be accessible, thus a RAM check of it will also fail. + + * TODO: This was set in sdram_set_spd_registers() + * Test if it still works when set here + */ + PAM0, 0x00, 0x30, + PAM1, 0x00, 0x33, + PAM2, 0x00, 0x33, + PAM3, 0x00, 0x33, + PAM4, 0x00, 0x33, + PAM5, 0x00, 0x33, + PAM6, 0x00, 0x33,
/* DRB[0:7] - DRAM Row Boundary Registers * 0x60 - 0x67 @@ -343,6 +364,8 @@ // PMCR, 0x00, 0x14, // PMCR, 0x00, 0x10, PMCR, 0x00, 0x00, + /* Enable SCRR.SRRAEN and let BX choose the SRR */ + SCRR+1, 0x00, 0x10, };
/*----------------------------------------------------------------------------- @@ -396,7 +419,7 @@
dimm_end = pci_read_config8(NB, DRB + i);
- addr = (dimm_start * 8 * 1024 * 1024) + addr_offset; + addr = (dimm_start * 8 * 1048576) + addr_offset; if (dimm_end > dimm_start) { #if 0 PRINT_DEBUG(" Sending RAM command 0x"); @@ -414,6 +437,22 @@ } }
+static void set_dram_buffer_strength(void) +{ + /* TODO: This needs to be set according to the DRAM tech + * (x8, x16, or x32). Argh, Intel provides no docs on this! + * Currently, it needs to be pulled from the output of + * lspci -xxx Rx92 + Relevant registers: + * MBSC + * MBFS, BUFFC + */ + + pci_write_config8(NB, MBSC, 0x03); + +} + +
/*----------------------------------------------------------------------------- DIMM-independant configuration functions.
-----------------------------------------------------------------------------*/ @@ -461,71 +500,305 @@ reg &= register_values[i + 1]; reg |= register_values[i + 2] & ~(register_values[i + 1]); pci_write_config8(NB, register_values[i], reg); - +#if 0 PRINT_DEBUG(" Set register 0x"); PRINT_DEBUG_HEX8(register_values[i]); PRINT_DEBUG(" to 0x"); PRINT_DEBUG_HEX8(reg); PRINT_DEBUG("\r\n"); +#endif } }
-static void sdram_set_spd_registers(void) +/* Copied from i82830 northbridge code */ +struct dimm_size { + unsigned long side1; + unsigned long side2; +}; + +static struct dimm_size spd_get_dimm_size(unsigned device) { - /* TODO: Don't hardcode the values here, get info via SPD. */ + struct dimm_size sz; + int i, module_density, dimm_banks; + sz.side1 = 0; + module_density = spd_read_byte(device, SPD_DENSITY_OF_EACH_ROW_ON_MODULE); + dimm_banks = spd_read_byte(device, SPD_NUM_DIMM_BANKS);
- /* Map all legacy regions to RAM (read/write). This is required if - * you want to use the RAM area from 768 KB - 1 MB. If the PAM - * registers are not set here appropriately, the RAM in that region - * will not be accessible, thus a RAM check of it will also fail. + /* Find the size of side1. */ + /* Find the larger value. The larger value is always side1. */ + for (i = 512; i >= 0; i >>= 1) { + if ((module_density & i) == i) { + sz.side1 = i; + break; + } + } + + /* Set to 0 in case it's single sided. */ + sz.side2 = 0; + + /* Test if it's a dual-sided DIMM. */ + if (dimm_banks > 1) { + /* Test to see if there's a second value, if so it's asymmetrical. */ + if (module_density != i) { + /* Find the second value, picking up where we left off. */ + /* i >>= 1 done initially to make sure we don't get the same value again. */ + for (i >>= 1; i >= 0; i >>= 1) { + if (module_density == (sz.side1 | i)) { + sz.side2 = i; + break; + } + } + /* If not, it's symmetrical */ + } else { + sz.side2 = sz.side1; + } + } + + /* SPD byte 31 is the memory size divided by 4 so we + * need to muliply by 4 to get the total size. */ - pci_write_config8(NB, PAM0, 0x30); - pci_write_config8(NB, PAM1, 0x33); - pci_write_config8(NB, PAM2, 0x33); - pci_write_config8(NB, PAM3, 0x33); - pci_write_config8(NB, PAM4, 0x33); - pci_write_config8(NB, PAM5, 0x33); - pci_write_config8(NB, PAM6, 0x33); + sz.side1 *= 4; + sz.side2 *= 4; + return sz; +} +/* + Sets DRAM attributes one DIMM at a time, based on SPD data + Northbridge settings that got set here: + + NBXCFG[31:24] + DRB0-DRB7 + RPS + DRAMC + */ +static void set_dram_row_attributes(void)
**********You are setting alot more than just dra here, I would rename this function something like sdram_setup_registers().
+{ + int i, dra, drb, col, width, value, rps, edosd, ecc, nbxecc; + u8 bpr; // Top 8 bits of PGPOL + + edosd = 0; + rps = 0; + drb = 0; + bpr = 0; + nbxecc=0xff; + + for (i = 0; i < DIMM_SOCKETS; i++) { + unsigned device; + device = DIMM_SPD_BASE + i; + bpr >>= 2;
- /* TODO: Set DRB0-DRB7. */ - /* Currently this is hardcoded to one 64 MB DIMM in slot 0. */ - pci_write_config8(NB, DRB0, 0x08); - pci_write_config8(NB, DRB1, 0x08); - pci_write_config8(NB, DRB2, 0x08); - pci_write_config8(NB, DRB3, 0x08); - pci_write_config8(NB, DRB4, 0x08); - pci_write_config8(NB, DRB5, 0x08); - pci_write_config8(NB, DRB6, 0x08); - pci_write_config8(NB, DRB7, 0x08); + /* First check if a DIMM is actually present. */ + value = spd_read_byte(device, SPD_MEMORY_TYPE); + /* This is BX! We do EDO too! */ + if (value == SPD_MEMORY_TYPE_EDO || value == SPD_MEMORY_TYPE_SDRAM) {
- /* TODO: Set DRAMC. Don't enable refresh for now. */ - pci_write_config8(NB, DRAMC, 0x08); + PRINT_DEBUG("Found "); + if (value == SPD_MEMORY_TYPE_EDO) { + edosd |= 0x02; + } else if (value == SPD_MEMORY_TYPE_SDRAM) { + edosd = edosd | 0x04; + } + PRINT_DEBUG("DIMM in slot "); + PRINT_DEBUG_HEX8(i); + PRINT_DEBUG("\r\n");
- /* TODO: Set RPS. Needs to be fixed for multiple DIMM support. */ - pci_write_config16(NB, RPS, 0x0001); + if (edosd == 0x06) { + print_err("Mixing EDO/SDRAM not supported\r\n"); + die("HALT\r\n"); + } + + /* "DRA" is our RPS for the two rows on this DIMM */ + dra = 0;
+ /* columns */ + col = spd_read_byte(device, SPD_NUM_COLUMNS); + + /* Is this an ECC DIMM? (Actually this will be a 2 if so) */ + /* TODO: Other register than NBXCFG also needs this ECC information */ + ecc = spd_read_byte(device, SPD_DIMM_CONFIG_TYPE); + + /* data width */ + width = spd_read_byte(device, SPD_MODULE_DATA_WIDTH_LSB); + + /* Exclude error checking data width from page size calculations */ + if (ecc) { + value = spd_read_byte(device, SPD_ERROR_CHECKING_SDRAM_WIDTH); + width -= value; + /* ### ECC */ + /* Top 2 bits are clear to help set up NBXCFG */ + ecc &= 0x3f; + } else { + /* Without ECC, these top 2 bits should be 11 */ + ecc |= 0xc0; + } + + /* calculate page size in bits */ + value = ((1 << col) * width); + + /* convert to Kilobytes */ + dra = (value >> 13); + + /* # of banks of DIMM (single or double sided) */ + value = spd_read_byte(device, SPD_NUM_DIMM_BANKS); + + /* Once we have dra, col is done and can be reused, + * So it's reused for number of banks + */ + col = spd_read_byte(device, SPD_NUM_BANKS_PER_SDRAM); + + if (value == 1) { + /* Second bank of 1-bank DIMMs "doesn't have ECC" - or anything */ + ecc |=0x80; + if (dra == 2) { + dra = 0x0; /* 2KB */ + } else if (dra == 4) { + dra = 0x1; /* 4KB */ + } else if (dra == 8) { + dra = 0x2; /* 8KB */ + } else { + dra = -1; + } + /* Sets a flag in PGPOL[BPR] if this DIMM has 4 banks per row */ + if (col == 4) { + bpr |= 0x40; + } + } else if (value == 2) { + if (dra == 2) { + dra = 0x0; /* 2KB */ + } else if (dra == 4) { + dra = 0x05; /* 4KB */ + } else if (dra == 8) { + dra = 0x0a; /* 8KB */ + } else { + dra = -1; + } + /* Ditto */ + if (col == 4) { + bpr |= 0xc0; + } + } else { + print_err("# of banks of DIMM not supported\r\n"); + die("HALT\r\n"); + } + if (dra == -1) { + print_err("Page size not supported\r\n"); + die("HALT\r\n"); + } + + /* The 440BX supports asymmetrical dual-sided dimms (I can't test though) + * but can't handle DIMMs smaller than 8MB per + * side or larger than 128MB per side. + */ + struct dimm_size sz = spd_get_dimm_size(device); + if ((sz.side1 < 8)) { + print_err("DIMMs smaller than 8MB per side\r\nare not supported on this northbridge\r\n"); + die("HALT\r\n"); + } + if ((sz.side1 > 128)) { + print_err ("DIMMs larger than 128MB per side\r\nare not supported on this northbridge\r\n"); + die("HALT\r\n"); + } + /* - End Memory compatibility checks - */ + + /* We need to divide size by 8 to set up the + * DRB registers. + */ + drb += (sz.side1 / 8); + /* Builds the DRB for the next row in MSB so it gets placed in DRB[n+1] + * where it belongs when written as a 16-bit word. + */ + drb &= 0xff; + drb |= (drb + (sz.side2 / 8)) << 8; + + } else { +#if 0 + PRINT_DEBUG("No DIMM found in slot "); + PRINT_DEBUG_HEX8(i); + PRINT_DEBUG("\r\n"); +#endif + + /* If there's no DIMM in the slot, set dra value to 0x00. */ + dra = 0x00; + ecc = 0xc0; + /* Still have to propagate DRB over */ + drb &= 0xff; + drb |= (drb << 8); + } + + pci_write_config16(NB, DRB+(2*i), drb); +#if 0 + PRINT_DEBUG("DRB has been set to 0x"); + PRINT_DEBUG_HEX16(drb); + PRINT_DEBUG("\r\n"); +#endif + + /* Brings the upper DRB back down to be base for + * DRB calculations for the next two rows. + */ + drb >>= 8; + + rps |= (dra & 0x0f) << (i*4); + nbxecc = (nbxecc >> 2) | (ecc & 0xc0); + } + /* Set Paging Policy Register */ + pci_write_config8(NB, PGPOL+1, bpr); + PRINT_DEBUG("PGPOL[BPR] has been set to 0x"); + PRINT_DEBUG_HEX8(bpr); + PRINT_DEBUG("\r\n"); + /* Set DRAM Row Page Size Register */ + pci_write_config16(NB, RPS, rps); + PRINT_DEBUG("RPS has been set to 0x"); + PRINT_DEBUG_HEX16(rps); + PRINT_DEBUG("\r\n"); + /* ### ECC */ + pci_write_config8(NB, NBXCFG+3, nbxecc); + PRINT_DEBUG("NBXECC[31:24] has been set to 0x"); + PRINT_DEBUG_HEX8(nbxecc); + PRINT_DEBUG("\r\n"); + + /* Set DRAMC[4:3] to proper memory type (EDO/SDRAM) + * TODO: Account for registered SDRAM + */ + edosd &= 0x07; + if (edosd & 0x02) { + edosd |= 0x00; + } else if (edosd & 0x04) { + edosd |= 0x08; + } + edosd &= 0x18; + /* edosd by now has been transformed to the value needed for DRAMC[4:3] */ + value = pci_read_config8(NB, DRAMC) & 0xe7; + value |= edosd; + pci_write_config8(NB, DRAMC, value); + PRINT_DEBUG("DRAMC has been set to 0x"); + PRINT_DEBUG_HEX8(value); + PRINT_DEBUG("\r\n"); + +} + +static void sdram_set_spd_registers(void) +{ + /* Setup DRAM Row Boundary Registers and other attributes */ + set_dram_row_attributes(); + /* TODO: Set SDRAMC. */ pci_write_config16(NB, SDRAMC, 0x0010); /* SDRAMPWR=1: 4 DIMM config */
- /* TODO: Set PGPOL. */ - // pci_write_config16(NB, PGPOL, 0x0107); - pci_write_config16(NB, PGPOL, 0x0123); - - /* TODO: Set NBXCFG. */ - // pci_write_config32(NB, NBXCFG, 0x0100220c); // FIXME? - pci_write_config32(NB, NBXCFG, 0xff00800c); - + /* TODO */ + set_dram_buffer_strength(); + /* TODO: Set PMCR? */ // pci_write_config8(NB, PMCR, 0x14); pci_write_config8(NB, PMCR, 0x10);
/* TODO? */ - pci_write_config8(NB, PCI_LATENCY_TIMER, 0x40); pci_write_config8(NB, DRAMT, 0x03); - pci_write_config8(NB, MBSC, 0x03); - pci_write_config8(NB, SCRR, 0x38); }
+static void sdram_set_timing(unsigned int mhz) { + /* TODO */ + +} + static void sdram_enable(void) { int i;
************I also noticed you did not use the memory initialize each row/side code from the i830. That code is extremely important for multiple memory sticks. Besides that everything else looks really good, great work!