Fixes for Tyan s2880

Eric W. Biederman ebiederman at lnxi.com
Thu Jul 31 23:25:00 CEST 2003


YhLu <YhLu at tyan.com> writes:

> Eric,
> 
> Great. Then there should be no big issue on opteron any more.
> 
> Can you send to the new raminit.c to me now?

Here is my most recent snapshot, which is slightly newer
than what I have in the public CVS tree.

I think I need to scrub all of memory on the B3 stepping before
I start using it.  I have not tested anything that is C0 specific yet.

Eric

#include <cpu/k8/mtrr.h>
#include "raminit.h"

/* Function 2 */
#define DRAM_CSBASE	   0x40
#define DRAM_CSMASK	   0x60
#define DRAM_BANK_ADDR_MAP 0x80
#define DRAM_TIMING_LOW	   0x88
#define	 DTL_TCL_SHIFT	   0
#define	 DTL_TCL_MASK	   0x7
#define	  DTL_CL_2	   1
#define	  DTL_CL_3	   2
#define	  DTL_CL_2_5	   5
#define	 DTL_TRC_SHIFT	   4
#define	 DTL_TRC_MASK	   0xf
#define	  DTL_TRC_BASE	   7
#define	  DTL_TRC_MIN	   7
#define	  DTL_TRC_MAX	   22
#define	 DTL_TRFC_SHIFT	   8
#define	 DTL_TRFC_MASK	   0xf
#define	  DTL_TRFC_BASE	   9
#define	  DTL_TRFC_MIN	   9
#define	  DTL_TRFC_MAX	   24
#define	 DTL_TRCD_SHIFT	   12
#define	 DTL_TRCD_MASK	   0x7
#define	  DTL_TRCD_BASE	   0
#define	  DTL_TRCD_MIN	   2
#define	  DTL_TRCD_MAX	   6
#define	 DTL_TRRD_SHIFT	   16
#define	 DTL_TRRD_MASK	   0x7
#define	  DTL_TRRD_BASE	   0
#define	  DTL_TRRD_MIN	   2
#define	  DTL_TRRD_MAX	   4
#define	 DTL_TRAS_SHIFT	   20
#define	 DTL_TRAS_MASK	   0xf
#define	  DTL_TRAS_BASE	   0
#define	  DTL_TRAS_MIN	   5
#define	  DTL_TRAS_MAX	   15
#define	 DTL_TRP_SHIFT	   24
#define	 DTL_TRP_MASK	   0x7
#define	  DTL_TRP_BASE	   0
#define	  DTL_TRP_MIN	   2
#define	  DTL_TRP_MAX	   6
#define	 DTL_TWR_SHIFT	   28
#define	 DTL_TWR_MASK	   0x1
#define	  DTL_TWR_BASE	   2
#define	  DTL_TWR_MIN	   2
#define	  DTL_TWR_MAX	   3
#define DRAM_TIMING_HIGH   0x8c
#define	 DTH_TWTR_SHIFT	   0
#define	 DTH_TWTR_MASK	   0x1
#define	  DTH_TWTR_BASE	   1
#define	  DTH_TWTR_MIN	   1
#define	  DTH_TWTR_MAX	   2
#define	 DTH_TRWT_SHIFT	   4
#define	 DTH_TRWT_MASK	   0x7
#define	  DTH_TRWT_BASE	   1
#define	  DTH_TRWT_MIN	   1
#define	  DTH_TRWT_MAX	   6
#define	 DTH_TREF_SHIFT	   8
#define	 DTH_TREF_MASK	   0x1f
#define	  DTH_TREF_100MHZ_4K 0x00
#define	  DTH_TREF_133MHZ_4K 0x01
#define	  DTH_TREF_166MHZ_4K 0x02
#define	  DTH_TREF_200MHZ_4K 0x03
#define	  DTH_TREF_100MHZ_8K 0x08
#define	  DTH_TREF_133MHZ_8K 0x09
#define	  DTH_TREF_166MHZ_8K 0x0A
#define	  DTH_TREF_200MHZ_8K 0x0B
#define	 DTH_TWCL_SHIFT	    20
#define	 DTH_TWCL_MASK	    0x7
#define	  DTH_TWCL_BASE	    1
#define	  DTH_TWCL_MIN	    1
#define	  DTH_TWCL_MAX	    2
#define DRAM_CONFIG_LOW	   0x90
#define	 DCL_DLL_Disable   (1<<0)
#define	 DCL_D_DRV	   (1<<1)
#define	 DCL_QFC_EN	   (1<<2)
#define	 DCL_DisDqsHys	   (1<<3)
#define	 DCL_DramInit	   (1<<8)
#define	 DCL_DramEnable	   (1<<10)
#define	 DCL_MemClrStatus  (1<<11)
#define	 DCL_ESR	   (1<<12)
#define	 DCL_SRS	   (1<<13)
#define	 DCL_128BitEn	   (1<<16)
#define	 DCL_DimmEccEn	   (1<<17)
#define	 DCL_UnBufDimm	   (1<<18)
#define	 DCL_32ByteEn	   (1<<19)
#define	 DCL_x4DIMM_SHIFT  20
#define DRAM_CONFIG_HIGH   0x94
#define	 DCH_ASYNC_LAT_SHIFT  0
#define	 DCH_ASYNC_LAT_MASK   0xf
#define	  DCH_ASYNC_LAT_BASE  0
#define	  DCH_ASYNC_LAT_MIN   0
#define	  DCH_ASYNC_LAT_MAX   15
#define	 DCH_RDPREAMBLE_SHIFT 8
#define	 DCH_RDPREAMBLE_MASK  0xf
#define	  DCH_RDPREAMBLE_BASE ((2<<1)+0) /* 2.0 ns */
#define	  DCH_RDPREAMBLE_MIN  ((2<<1)+0) /* 2.0 ns */
#define	  DCH_RDPREAMBLE_MAX  ((9<<1)+1) /* 9.5 ns */
#define	 DCH_IDLE_LIMIT_SHIFT 16
#define	 DCH_IDLE_LIMIT_MASK  0x7
#define	  DCH_IDLE_LIMIT_0    0
#define	  DCH_IDLE_LIMIT_4    1
#define	  DCH_IDLE_LIMIT_8    2
#define	  DCH_IDLE_LIMIT_16   3
#define	  DCH_IDLE_LIMIT_32   4
#define	  DCH_IDLE_LIMIT_64   5
#define	  DCH_IDLE_LIMIT_128  6
#define	  DCH_IDLE_LIMIT_256  7
#define	 DCH_DYN_IDLE_CTR_EN (1 << 19)
#define	 DCH_MEMCLK_SHIFT     20
#define	 DCH_MEMCLK_MASK      0x7
#define	  DCH_MEMCLK_100MHZ   0
#define	  DCH_MEMCLK_133MHZ   2
#define	  DCH_MEMCLK_166MHZ   5
#define	  DCH_MEMCLK_200MHZ   7
#define	 DCH_MEMCLK_VALID     (1 << 25)
#define	 DCH_MEMCLK_EN0	      (1 << 26) 
#define	 DCH_MEMCLK_EN1	      (1 << 27) 
#define	 DCH_MEMCLK_EN2	      (1 << 28) 
#define	 DCH_MEMCLK_EN3	      (1 << 29) 

/* Function 3 */
#define MCA_NB_CONFIG      0x44
#define   MNC_ECC_EN       (1 << 22)
#define   MNC_CHIPKILL_EN  (1 << 23)
#define SCRUB_CONTROL	   0x58
#define	  SCRUB_NONE	    0
#define	  SCRUB_40ns	    1
#define	  SCRUB_80ns	    2
#define	  SCRUB_160ns	    3
#define	  SCRUB_320ns	    4
#define	  SCRUB_640ns	    5
#define	  SCRUB_1_28us	    6
#define	  SCRUB_2_56us	    7
#define	  SCRUB_5_12us	    8
#define	  SCRUB_10_2us	    9
#define	  SCRUB_20_5us	   10
#define	  SCRUB_41_0us	   11
#define	  SCRUB_81_9us	   12
#define	  SCRUB_163_8us	   13
#define	  SCRUB_327_7us	   14
#define	  SCRUB_655_4us	   15
#define	  SCRUB_1_31ms	   16
#define	  SCRUB_2_62ms	   17
#define	  SCRUB_5_24ms	   18 
#define	  SCRUB_10_49ms	   19
#define	  SCRUB_20_97ms	   20
#define	  SCRUB_42ms	   21
#define	  SCRUB_84ms	   22
#define	 SC_DRAM_SCRUB_RATE_SHFIT  0
#define	 SC_DRAM_SCRUB_RATE_MASK   0x1f
#define	 SC_L2_SCRUB_RATE_SHIFT	   8
#define	 SC_L2_SCRUB_RATE_MASK	   0x1f
#define	 SC_L1D_SCRUB_RATE_SHIFT   16
#define	 SC_L1D_SCRUB_RATE_MASK	   0x1f
#define SCRUB_ADDR_LOW	   0x5C
#define SCRUB_ADDR_HIGH	   0x60
#define NORTHBRIDGE_CAP	   0xE8
#define	 NBCAP_128Bit	      0x0001
#define	 NBCAP_MP	      0x0002
#define	 NBCAP_BIG_MP	      0x0004
#define	 NBCAP_ECC	      0x0004
#define	 NBCAP_CHIPKILL_ECC   0x0010
#define	 NBCAP_MEMCLK_SHIFT   5
#define	 NBCAP_MEMCLK_MASK    3
#define	 NBCAP_MEMCLK_100MHZ  3
#define	 NBCAP_MEMCLK_133MHZ  2
#define	 NBCAP_MEMCLK_166MHZ  1
#define	 NBCAP_MEMCLK_200MHZ  0
#define	 NBCAP_MEMCTRL	      0x0100


static void setup_resource_map(const unsigned int *register_values, int max)
{
	int i;
	print_debug("setting up resource map....\r\n");
	for(i = 0; i < max; i += 3) {
		device_t dev;
		unsigned where;
		unsigned long reg;
#if 0
		print_debug_hex32(register_values[i]);
		print_debug(" <-");
		print_debug_hex32(register_values[i+2]);
		print_debug("\r\n");
#endif
		dev = register_values[i] & ~0xff;
		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);
#if 0
		reg = pci_read_config32(register_values[i]);
		reg &= register_values[i+1];
		reg |= register_values[i+2] & ~register_values[i+1];
		pci_write_config32(register_values[i], reg);
#endif
	}
	print_debug("done.\r\n");
}

static void setup_default_resource_map(void)
{
	static const unsigned int register_values[] = {
	/* Careful set limit registers before base registers which contain the enables */
	/* DRAM Limit i Registers
	 * F1:0x44 i = 0
	 * F1:0x4C i = 1
	 * F1:0x54 i = 2
	 * F1:0x5C i = 3
	 * F1:0x64 i = 4
	 * F1:0x6C i = 5
	 * F1:0x74 i = 6
	 * F1:0x7C i = 7
	 * [ 2: 0] Destination Node ID
	 *	   000 = Node 0
	 *	   001 = Node 1
	 *	   010 = Node 2
	 *	   011 = Node 3
	 *	   100 = Node 4
	 *	   101 = Node 5
	 *	   110 = Node 6
	 *	   111 = Node 7
	 * [ 7: 3] Reserved
	 * [10: 8] Interleave select
	 *	   specifies the values of A[14:12] to use with interleave enable.
	 * [15:11] Reserved
	 * [31:16] DRAM Limit Address i Bits 39-24
	 *	   This field defines the upper address bits of a 40 bit  address
	 *	   that define the end of the DRAM region.
	 */
	PCI_ADDR(0, 0x18, 1, 0x44), 0x0000f8f8, 0x00000000,
	PCI_ADDR(0, 0x18, 1, 0x4C), 0x0000f8f8, 0x00000001,
	PCI_ADDR(0, 0x18, 1, 0x54), 0x0000f8f8, 0x00000002,
	PCI_ADDR(0, 0x18, 1, 0x5C), 0x0000f8f8, 0x00000003,
	PCI_ADDR(0, 0x18, 1, 0x64), 0x0000f8f8, 0x00000004,
	PCI_ADDR(0, 0x18, 1, 0x6C), 0x0000f8f8, 0x00000005,
	PCI_ADDR(0, 0x18, 1, 0x74), 0x0000f8f8, 0x00000006,
	PCI_ADDR(0, 0x18, 1, 0x7C), 0x0000f8f8, 0x00000007,
	/* DRAM Base i Registers
	 * F1:0x40 i = 0
	 * F1:0x48 i = 1
	 * F1:0x50 i = 2
	 * F1:0x58 i = 3
	 * F1:0x60 i = 4
	 * F1:0x68 i = 5
	 * F1:0x70 i = 6
	 * F1:0x78 i = 7
	 * [ 0: 0] Read Enable
	 *	   0 = Reads Disabled
	 *	   1 = Reads Enabled
	 * [ 1: 1] Write Enable
	 *	   0 = Writes Disabled
	 *	   1 = Writes Enabled
	 * [ 7: 2] Reserved
	 * [10: 8] Interleave Enable
	 *	   000 = No interleave
	 *	   001 = Interleave on A[12] (2 nodes)
	 *	   010 = reserved
	 *	   011 = Interleave on A[12] and A[14] (4 nodes)
	 *	   100 = reserved
	 *	   101 = reserved
	 *	   110 = reserved
	 *	   111 = Interleve on A[12] and A[13] and A[14] (8 nodes)
	 * [15:11] Reserved
	 * [13:16] DRAM Base Address i Bits 39-24
	 *	   This field defines the upper address bits of a 40-bit address
	 *	   that define the start of the DRAM region.
	 */
	PCI_ADDR(0, 0x18, 1, 0x40), 0x0000f8fc, 0x00000000,
	PCI_ADDR(0, 0x18, 1, 0x48), 0x0000f8fc, 0x00000000,
	PCI_ADDR(0, 0x18, 1, 0x50), 0x0000f8fc, 0x00000000,
	PCI_ADDR(0, 0x18, 1, 0x58), 0x0000f8fc, 0x00000000,
	PCI_ADDR(0, 0x18, 1, 0x60), 0x0000f8fc, 0x00000000,
	PCI_ADDR(0, 0x18, 1, 0x68), 0x0000f8fc, 0x00000000,
	PCI_ADDR(0, 0x18, 1, 0x70), 0x0000f8fc, 0x00000000,
	PCI_ADDR(0, 0x18, 1, 0x78), 0x0000f8fc, 0x00000000,

	/* Memory-Mapped I/O Limit i Registers
	 * F1:0x84 i = 0
	 * F1:0x8C i = 1
	 * F1:0x94 i = 2
	 * F1:0x9C i = 3
	 * F1:0xA4 i = 4
	 * F1:0xAC i = 5
	 * F1:0xB4 i = 6
	 * F1:0xBC i = 7
	 * [ 2: 0] Destination Node ID
	 *	   000 = Node 0
	 *	   001 = Node 1
	 *	   010 = Node 2
	 *	   011 = Node 3
	 *	   100 = Node 4
	 *	   101 = Node 5
	 *	   110 = Node 6
	 *	   111 = Node 7
	 * [ 3: 3] Reserved
	 * [ 5: 4] Destination Link ID
	 *	   00 = Link 0
	 *	   01 = Link 1
	 *	   10 = Link 2
	 *	   11 = Reserved
	 * [ 6: 6] Reserved
	 * [ 7: 7] Non-Posted
	 *	   0 = CPU writes may be posted
	 *	   1 = CPU writes must be non-posted
	 * [31: 8] Memory-Mapped I/O Limit Address i (39-16)
	 *	   This field defines the upp adddress bits of a 40-bit address that
	 *	   defines the end of a memory-mapped I/O region n
	 */
	PCI_ADDR(0, 0x18, 1, 0x84), 0x00000048, 0x00000000,
	PCI_ADDR(0, 0x18, 1, 0x8C), 0x00000048, 0x00000000,
	PCI_ADDR(0, 0x18, 1, 0x94), 0x00000048, 0x00000000,
	PCI_ADDR(0, 0x18, 1, 0x9C), 0x00000048, 0x00000000,
	PCI_ADDR(0, 0x18, 1, 0xA4), 0x00000048, 0x00000000,
	PCI_ADDR(0, 0x18, 1, 0xAC), 0x00000048, 0x00000000,
	PCI_ADDR(0, 0x18, 1, 0xB4), 0x00000048, 0x00000000,
	PCI_ADDR(0, 0x18, 1, 0xBC), 0x00000048, 0x00ffff00,

	/* Memory-Mapped I/O Base i Registers
	 * F1:0x80 i = 0
	 * F1:0x88 i = 1
	 * F1:0x90 i = 2
	 * F1:0x98 i = 3
	 * F1:0xA0 i = 4
	 * F1:0xA8 i = 5
	 * F1:0xB0 i = 6
	 * F1:0xB8 i = 7
	 * [ 0: 0] Read Enable
	 *	   0 = Reads disabled
	 *	   1 = Reads Enabled
	 * [ 1: 1] Write Enable
	 *	   0 = Writes disabled
	 *	   1 = Writes Enabled
	 * [ 2: 2] Cpu Disable
	 *	   0 = Cpu can use this I/O range
	 *	   1 = Cpu requests do not use this I/O range
	 * [ 3: 3] Lock
	 *	   0 = base/limit registers i are read/write
	 *	   1 = base/limit registers i are read-only
	 * [ 7: 4] Reserved
	 * [31: 8] Memory-Mapped I/O Base Address i (39-16)
	 *	   This field defines the upper address bits of a 40bit address 
	 *	   that defines the start of memory-mapped I/O region i
	 */
	PCI_ADDR(0, 0x18, 1, 0x80), 0x000000f0, 0x00000000,
	PCI_ADDR(0, 0x18, 1, 0x88), 0x000000f0, 0x00000000,
	PCI_ADDR(0, 0x18, 1, 0x90), 0x000000f0, 0x00000000,
	PCI_ADDR(0, 0x18, 1, 0x98), 0x000000f0, 0x00000000,
	PCI_ADDR(0, 0x18, 1, 0xA0), 0x000000f0, 0x00000000,
	PCI_ADDR(0, 0x18, 1, 0xA8), 0x000000f0, 0x00000000,
	PCI_ADDR(0, 0x18, 1, 0xB0), 0x000000f0, 0x00000000,
	PCI_ADDR(0, 0x18, 1, 0xB8), 0x000000f0, 0x00fc0003,

	/* PCI I/O Limit i Registers
	 * F1:0xC4 i = 0
	 * F1:0xCC i = 1
	 * F1:0xD4 i = 2
	 * F1:0xDC i = 3
	 * [ 2: 0] Destination Node ID
	 *	   000 = Node 0
	 *	   001 = Node 1
	 *	   010 = Node 2
	 *	   011 = Node 3
	 *	   100 = Node 4
	 *	   101 = Node 5
	 *	   110 = Node 6
	 *	   111 = Node 7
	 * [ 3: 3] Reserved
	 * [ 5: 4] Destination Link ID
	 *	   00 = Link 0
	 *	   01 = Link 1
	 *	   10 = Link 2
	 *	   11 = reserved
	 * [11: 6] Reserved
	 * [24:12] PCI I/O Limit Address i
	 *	   This field defines the end of PCI I/O region n
	 * [31:25] Reserved
	 */
	PCI_ADDR(0, 0x18, 1, 0xC4), 0xFE000FC8, 0x01fff000,
	PCI_ADDR(0, 0x18, 1, 0xCC), 0xFE000FC8, 0x00000000,
	PCI_ADDR(0, 0x18, 1, 0xD4), 0xFE000FC8, 0x00000000,
	PCI_ADDR(0, 0x18, 1, 0xDC), 0xFE000FC8, 0x00000000,

	/* PCI I/O Base i Registers
	 * F1:0xC0 i = 0
	 * F1:0xC8 i = 1
	 * F1:0xD0 i = 2
	 * F1:0xD8 i = 3
	 * [ 0: 0] Read Enable
	 *	   0 = Reads Disabled
	 *	   1 = Reads Enabled
	 * [ 1: 1] Write Enable
	 *	   0 = Writes Disabled
	 *	   1 = Writes Enabled
	 * [ 3: 2] Reserved
	 * [ 4: 4] VGA Enable
	 *	   0 = VGA matches Disabled
	 *	   1 = matches all address < 64K and where A[9:0] is in the 
	 *	       range 3B0-3BB or 3C0-3DF independen of the base & limit registers
	 * [ 5: 5] ISA Enable
	 *	   0 = ISA matches Disabled
	 *	   1 = Blocks address < 64K and in the last 768 bytes of eack 1K block
	 *	       from matching agains this base/limit pair
	 * [11: 6] Reserved
	 * [24:12] PCI I/O Base i
	 *	   This field defines the start of PCI I/O region n 
	 * [31:25] Reserved
	 */
	PCI_ADDR(0, 0x18, 1, 0xC0), 0xFE000FCC, 0x00000003,
	PCI_ADDR(0, 0x18, 1, 0xC8), 0xFE000FCC, 0x00000000,
	PCI_ADDR(0, 0x18, 1, 0xD0), 0xFE000FCC, 0x00000000,
	PCI_ADDR(0, 0x18, 1, 0xD8), 0xFE000FCC, 0x00000000,

	/* Config Base and Limit i Registers
	 * F1:0xE0 i = 0
	 * F1:0xE4 i = 1
	 * F1:0xE8 i = 2
	 * F1:0xEC i = 3
	 * [ 0: 0] Read Enable
	 *	   0 = Reads Disabled
	 *	   1 = Reads Enabled
	 * [ 1: 1] Write Enable
	 *	   0 = Writes Disabled
	 *	   1 = Writes Enabled
	 * [ 2: 2] Device Number Compare Enable
	 *	   0 = The ranges are based on bus number
	 *	   1 = The ranges are ranges of devices on bus 0
	 * [ 3: 3] Reserved
	 * [ 6: 4] Destination Node
	 *	   000 = Node 0
	 *	   001 = Node 1
	 *	   010 = Node 2
	 *	   011 = Node 3
	 *	   100 = Node 4
	 *	   101 = Node 5
	 *	   110 = Node 6
	 *	   111 = Node 7
	 * [ 7: 7] Reserved
	 * [ 9: 8] Destination Link
	 *	   00 = Link 0
	 *	   01 = Link 1
	 *	   10 = Link 2
	 *	   11 - Reserved
	 * [15:10] Reserved
	 * [23:16] Bus Number Base i
	 *	   This field defines the lowest bus number in configuration region i
	 * [31:24] Bus Number Limit i
	 *	   This field defines the highest bus number in configuration regin i
	 */
	PCI_ADDR(0, 0x18, 1, 0xE0), 0x0000FC88, 0xff000003,
	PCI_ADDR(0, 0x18, 1, 0xE4), 0x0000FC88, 0x00000000,
	PCI_ADDR(0, 0x18, 1, 0xE8), 0x0000FC88, 0x00000000,
	PCI_ADDR(0, 0x18, 1, 0xEC), 0x0000FC88, 0x00000000,
	};
	int max;
	max = sizeof(register_values)/sizeof(register_values[0]);
	setup_resource_map(register_values, max);
}

static void sdram_set_registers(const struct mem_controller *ctrl)
{
	static const unsigned int register_values[] = {

	/* Careful set limit registers before base registers which contain the enables */
	/* DRAM Limit i Registers
	 * F1:0x44 i = 0
	 * F1:0x4C i = 1
	 * F1:0x54 i = 2
	 * F1:0x5C i = 3
	 * F1:0x64 i = 4
	 * F1:0x6C i = 5
	 * F1:0x74 i = 6
	 * F1:0x7C i = 7
	 * [ 2: 0] Destination Node ID
	 *	   000 = Node 0
	 *	   001 = Node 1
	 *	   010 = Node 2
	 *	   011 = Node 3
	 *	   100 = Node 4
	 *	   101 = Node 5
	 *	   110 = Node 6
	 *	   111 = Node 7
	 * [ 7: 3] Reserved
	 * [10: 8] Interleave select
	 *	   specifies the values of A[14:12] to use with interleave enable.
	 * [15:11] Reserved
	 * [31:16] DRAM Limit Address i Bits 39-24
	 *	   This field defines the upper address bits of a 40 bit  address
	 *	   that define the end of the DRAM region.
	 */
	PCI_ADDR(0, 0x18, 1, 0x44), 0x0000f8f8, 0x00000000,
	PCI_ADDR(0, 0x18, 1, 0x4C), 0x0000f8f8, 0x00000001,
	PCI_ADDR(0, 0x18, 1, 0x54), 0x0000f8f8, 0x00000002,
	PCI_ADDR(0, 0x18, 1, 0x5C), 0x0000f8f8, 0x00000003,
	PCI_ADDR(0, 0x18, 1, 0x64), 0x0000f8f8, 0x00000004,
	PCI_ADDR(0, 0x18, 1, 0x6C), 0x0000f8f8, 0x00000005,
	PCI_ADDR(0, 0x18, 1, 0x74), 0x0000f8f8, 0x00000006,
	PCI_ADDR(0, 0x18, 1, 0x7C), 0x0000f8f8, 0x00000007,
	/* DRAM Base i Registers
	 * F1:0x40 i = 0
	 * F1:0x48 i = 1
	 * F1:0x50 i = 2
	 * F1:0x58 i = 3
	 * F1:0x60 i = 4
	 * F1:0x68 i = 5
	 * F1:0x70 i = 6
	 * F1:0x78 i = 7
	 * [ 0: 0] Read Enable
	 *	   0 = Reads Disabled
	 *	   1 = Reads Enabled
	 * [ 1: 1] Write Enable
	 *	   0 = Writes Disabled
	 *	   1 = Writes Enabled
	 * [ 7: 2] Reserved
	 * [10: 8] Interleave Enable
	 *	   000 = No interleave
	 *	   001 = Interleave on A[12] (2 nodes)
	 *	   010 = reserved
	 *	   011 = Interleave on A[12] and A[14] (4 nodes)
	 *	   100 = reserved
	 *	   101 = reserved
	 *	   110 = reserved
	 *	   111 = Interleve on A[12] and A[13] and A[14] (8 nodes)
	 * [15:11] Reserved
	 * [13:16] DRAM Base Address i Bits 39-24
	 *	   This field defines the upper address bits of a 40-bit address
	 *	   that define the start of the DRAM region.
	 */
	PCI_ADDR(0, 0x18, 1, 0x40), 0x0000f8fc, 0x00000000,
	PCI_ADDR(0, 0x18, 1, 0x48), 0x0000f8fc, 0x00000000,
	PCI_ADDR(0, 0x18, 1, 0x50), 0x0000f8fc, 0x00000000,
	PCI_ADDR(0, 0x18, 1, 0x58), 0x0000f8fc, 0x00000000,
	PCI_ADDR(0, 0x18, 1, 0x60), 0x0000f8fc, 0x00000000,
	PCI_ADDR(0, 0x18, 1, 0x68), 0x0000f8fc, 0x00000000,
	PCI_ADDR(0, 0x18, 1, 0x70), 0x0000f8fc, 0x00000000,
	PCI_ADDR(0, 0x18, 1, 0x78), 0x0000f8fc, 0x00000000,

	/* DRAM CS Base Address i Registers
	 * F2:0x40 i = 0
	 * F2:0x44 i = 1
	 * F2:0x48 i = 2
	 * F2:0x4C i = 3
	 * F2:0x50 i = 4
	 * F2:0x54 i = 5
	 * F2:0x58 i = 6
	 * F2:0x5C i = 7
	 * [ 0: 0] Chip-Select Bank Enable
	 *	   0 = Bank Disabled
	 *	   1 = Bank Enabled
	 * [ 8: 1] Reserved
	 * [15: 9] Base Address (19-13)
	 *	   An optimization used when all DIMM are the same size...
	 * [20:16] Reserved
	 * [31:21] Base Address (35-25)
	 *	   This field defines the top 11 addresses bit of a 40-bit
	 *	   address that define the memory address space.  These
	 *	   bits decode 32-MByte blocks of memory.
	 */
	PCI_ADDR(0, 0x18, 2, 0x40), 0x001f01fe, 0x00000000,
	PCI_ADDR(0, 0x18, 2, 0x44), 0x001f01fe, 0x00000000,
	PCI_ADDR(0, 0x18, 2, 0x48), 0x001f01fe, 0x00000000,
	PCI_ADDR(0, 0x18, 2, 0x4C), 0x001f01fe, 0x00000000,
	PCI_ADDR(0, 0x18, 2, 0x50), 0x001f01fe, 0x00000000,
	PCI_ADDR(0, 0x18, 2, 0x54), 0x001f01fe, 0x00000000,
	PCI_ADDR(0, 0x18, 2, 0x58), 0x001f01fe, 0x00000000,
	PCI_ADDR(0, 0x18, 2, 0x5C), 0x001f01fe, 0x00000000,
	/* DRAM CS Mask Address i Registers
	 * F2:0x60 i = 0
	 * F2:0x64 i = 1
	 * F2:0x68 i = 2
	 * F2:0x6C i = 3
	 * F2:0x70 i = 4
	 * F2:0x74 i = 5
	 * F2:0x78 i = 6
	 * F2:0x7C i = 7
	 * Select bits to exclude from comparison with the DRAM Base address register.
	 * [ 8: 0] Reserved
	 * [15: 9] Address Mask (19-13)
	 *	   Address to be excluded from the optimized case
	 * [20:16] Reserved
	 * [29:21] Address Mask (33-25)
	 *	   The bits with an address mask of 1 are excluded from address comparison
	 * [31:30] Reserved
	 * 
	 */
	PCI_ADDR(0, 0x18, 2, 0x60), 0xC01f01ff, 0x00000000,
	PCI_ADDR(0, 0x18, 2, 0x64), 0xC01f01ff, 0x00000000,
	PCI_ADDR(0, 0x18, 2, 0x68), 0xC01f01ff, 0x00000000,
	PCI_ADDR(0, 0x18, 2, 0x6C), 0xC01f01ff, 0x00000000,
	PCI_ADDR(0, 0x18, 2, 0x70), 0xC01f01ff, 0x00000000,
	PCI_ADDR(0, 0x18, 2, 0x74), 0xC01f01ff, 0x00000000,
	PCI_ADDR(0, 0x18, 2, 0x78), 0xC01f01ff, 0x00000000,
	PCI_ADDR(0, 0x18, 2, 0x7C), 0xC01f01ff, 0x00000000,
	/* DRAM Bank Address Mapping Register
	 * F2:0x80
	 * Specify the memory module size
	 * [ 2: 0] CS1/0 
	 * [ 6: 4] CS3/2
	 * [10: 8] CS5/4
	 * [14:12] CS7/6
	 *	   000 = 32Mbyte  (Rows = 12 & Col =  8)
	 *	   001 = 64Mbyte  (Rows = 12 & Col =  9)
	 *	   010 = 128Mbyte (Rows = 13 & Col =  9)|(Rows = 12 & Col = 10)
	 *	   011 = 256Mbyte (Rows = 13 & Col = 10)|(Rows = 12 & Col = 11)
	 *	   100 = 512Mbyte (Rows = 13 & Col = 11)|(Rows = 14 & Col = 10)
	 *	   101 = 1Gbyte	  (Rows = 14 & Col = 11)|(Rows = 13 & Col = 12)
	 *	   110 = 2Gbyte	  (Rows = 14 & Col = 12)
	 *	   111 = reserved 
	 * [ 3: 3] Reserved
	 * [ 7: 7] Reserved
	 * [11:11] Reserved
	 * [31:15]
	 */
	PCI_ADDR(0, 0x18, 2, 0x80), 0xffff8888, 0x00000000,
	/* DRAM Timing Low Register
	 * F2:0x88
	 * [ 2: 0] Tcl (Cas# Latency, Cas# to read-data-valid)
	 *	   000 = reserved
	 *	   001 = CL 2
	 *	   010 = CL 3
	 *	   011 = reserved
	 *	   100 = reserved
	 *	   101 = CL 2.5
	 *	   110 = reserved
	 *	   111 = reserved
	 * [ 3: 3] Reserved
	 * [ 7: 4] Trc (Row Cycle Time, Ras#-active to Ras#-active/bank auto refresh)
	 *	   0000 =  7 bus clocks
	 *	   0001 =  8 bus clocks
	 *	   ...
	 *	   1110 = 21 bus clocks
	 *	   1111 = 22 bus clocks
	 * [11: 8] Trfc (Row refresh Cycle time, Auto-refresh-active to RAS#-active or RAS#auto-refresh)
	 *	   0000 = 9 bus clocks
	 *	   0010 = 10 bus clocks
	 *	   ....
	 *	   1110 = 23 bus clocks
	 *	   1111 = 24 bus clocks
	 * [14:12] Trcd (Ras#-active to Case#-read/write Delay)
	 *	   000 = reserved
	 *	   001 = reserved
	 *	   010 = 2 bus clocks
	 *	   011 = 3 bus clocks
	 *	   100 = 4 bus clocks
	 *	   101 = 5 bus clocks
	 *	   110 = 6 bus clocks
	 *	   111 = reserved
	 * [15:15] Reserved
	 * [18:16] Trrd (Ras# to Ras# Delay)
	 *	   000 = reserved
	 *	   001 = reserved
	 *	   010 = 2 bus clocks
	 *	   011 = 3 bus clocks
	 *	   100 = 4 bus clocks
	 *	   101 = reserved
	 *	   110 = reserved
	 *	   111 = reserved
	 * [19:19] Reserved
	 * [23:20] Tras (Minmum Ras# Active Time)
	 *	   0000 to 0100 = reserved
	 *	   0101 = 5 bus clocks
	 *	   ...
	 *	   1111 = 15 bus clocks
	 * [26:24] Trp (Row Precharge Time)
	 *	   000 = reserved
	 *	   001 = reserved
	 *	   010 = 2 bus clocks
	 *	   011 = 3 bus clocks
	 *	   100 = 4 bus clocks
	 *	   101 = 5 bus clocks
	 *	   110 = 6 bus clocks
	 *	   111 = reserved
	 * [27:27] Reserved
	 * [28:28] Twr (Write Recovery Time)
	 *	   0 = 2 bus clocks
	 *	   1 = 3 bus clocks
	 * [31:29] Reserved
	 */
	PCI_ADDR(0, 0x18, 2, 0x88), 0xe8088008, 0x02522001 /* 0x03623125 */ ,
	/* DRAM Timing High Register
	 * F2:0x8C
	 * [ 0: 0] Twtr (Write to Read Delay)
	 *	   0 = 1 bus Clocks
	 *	   1 = 2 bus Clocks
	 * [ 3: 1] Reserved
	 * [ 6: 4] Trwt (Read to Write Delay)
	 *	   000 = 1 bus clocks
	 *	   001 = 2 bus clocks
	 *	   010 = 3 bus clocks
	 *	   011 = 4 bus clocks
	 *	   100 = 5 bus clocks
	 *	   101 = 6 bus clocks
	 *	   110 = reserved
	 *	   111 = reserved
	 * [ 7: 7] Reserved
	 * [12: 8] Tref (Refresh Rate)
	 *	   00000 = 100Mhz 4K rows
	 *	   00001 = 133Mhz 4K rows
	 *	   00010 = 166Mhz 4K rows
	 *	   00011 = 200Mhz 4K rows
	 *	   01000 = 100Mhz 8K/16K rows
	 *	   01001 = 133Mhz 8K/16K rows
	 *	   01010 = 166Mhz 8K/16K rows
	 *	   01011 = 200Mhz 8K/16K rows
	 * [19:13] Reserved
	 * [22:20] Twcl (Write CAS Latency)
	 *	   000 = 1 Mem clock after CAS# (Unbuffered Dimms)
	 *	   001 = 2 Mem clocks after CAS# (Registered Dimms)
	 * [31:23] Reserved
	 */
	PCI_ADDR(0, 0x18, 2, 0x8c), 0xff8fe08e, (0 << 20)|(0 << 8)|(0 << 4)|(0 << 0),
	/* DRAM Config Low Register
	 * F2:0x90
	 * [ 0: 0] DLL Disable
	 *	   0 = Enabled
	 *	   1 = Disabled
	 * [ 1: 1] D_DRV
	 *	   0 = Normal Drive
	 *	   1 = Weak Drive
	 * [ 2: 2] QFC_EN
	 *	   0 = Disabled
	 *	   1 = Enabled
	 * [ 3: 3] Disable DQS Hystersis  (FIXME handle this one carefully)
	 *	   0 = Enable DQS input filter 
	 *	   1 = Disable DQS input filtering 
	 * [ 7: 4] Reserved
	 * [ 8: 8] DRAM_Init
	 *	   0 = Initialization done or not yet started.
	 *	   1 = Initiate DRAM intialization sequence
	 * [ 9: 9] SO-Dimm Enable
	 *	   0 = Do nothing
	 *	   1 = SO-Dimms present
	 * [10:10] DramEnable
	 *	   0 = DRAM not enabled
	 *	   1 = DRAM initialized and enabled
	 * [11:11] Memory Clear Status
	 *	   0 = Memory Clear function has not completed
	 *	   1 = Memory Clear function has completed
	 * [12:12] Exit Self-Refresh
	 *	   0 = Exit from self-refresh done or not yet started
	 *	   1 = DRAM exiting from self refresh
	 * [13:13] Self-Refresh Status
	 *	   0 = Normal Operation
	 *	   1 = Self-refresh mode active
	 * [15:14] Read/Write Queue Bypass Count
	 *	   00 = 2
	 *	   01 = 4
	 *	   10 = 8
	 *	   11 = 16
	 * [16:16] 128-bit/64-Bit
	 *	   0 = 64bit Interface to DRAM
	 *	   1 = 128bit Interface to DRAM
	 * [17:17] DIMM ECC Enable
	 *	   0 = Some DIMMs do not have ECC
	 *	   1 = ALL DIMMS have ECC bits
	 * [18:18] UnBuffered DIMMs
	 *	   0 = Buffered DIMMS
	 *	   1 = Unbuffered DIMMS
	 * [19:19] Enable 32-Byte Granularity
	 *	   0 = Optimize for 64byte bursts
	 *	   1 = Optimize for 32byte bursts
	 * [20:20] DIMM 0 is x4
	 * [21:21] DIMM 1 is x4
	 * [22:22] DIMM 2 is x4
	 * [23:23] DIMM 3 is x4
	 *	   0 = DIMM is not x4
	 *	   1 = x4 DIMM present
	 * [24:24] Disable DRAM Receivers
	 *	   0 = Receivers enabled
	 *	   1 = Receivers disabled
	 * [27:25] Bypass Max
	 *	   000 = Arbiters chois is always respected
	 *	   001 = Oldest entry in DCQ can be bypassed 1 time
	 *	   010 = Oldest entry in DCQ can be bypassed 2 times
	 *	   011 = Oldest entry in DCQ can be bypassed 3 times
	 *	   100 = Oldest entry in DCQ can be bypassed 4 times
	 *	   101 = Oldest entry in DCQ can be bypassed 5 times
	 *	   110 = Oldest entry in DCQ can be bypassed 6 times
	 *	   111 = Oldest entry in DCQ can be bypassed 7 times
	 * [31:28] Reserved
	 */
	PCI_ADDR(0, 0x18, 2, 0x90), 0xf0000000, 
	(4 << 25)|(0 << 24)| 
	(0 << 23)|(0 << 22)|(0 << 21)|(0 << 20)| 
	(1 << 19)|(0 << 18)|(1 << 17)|(0 << 16)| 
	(2 << 14)|(0 << 13)|(0 << 12)| 
	(0 << 11)|(0 << 10)|(0 << 9)|(0 << 8)| 
	(0 << 3) |(0 << 1) |(0 << 0),
	/* DRAM Config High Register
	 * F2:0x94
	 * [ 0: 3] Maximum Asynchronous Latency
	 *	   0000 = 0 ns
	 *	   ...
	 *	   1111 = 15 ns
	 * [ 7: 4] Reserved
	 * [11: 8] Read Preamble
	 *	   0000 = 2.0 ns
	 *	   0001 = 2.5 ns
	 *	   0010 = 3.0 ns
	 *	   0011 = 3.5 ns
	 *	   0100 = 4.0 ns
	 *	   0101 = 4.5 ns
	 *	   0110 = 5.0 ns
	 *	   0111 = 5.5 ns
	 *	   1000 = 6.0 ns
	 *	   1001 = 6.5 ns
	 *	   1010 = 7.0 ns
	 *	   1011 = 7.5 ns
	 *	   1100 = 8.0 ns
	 *	   1101 = 8.5 ns
	 *	   1110 = 9.0 ns
	 *	   1111 = 9.5 ns
	 * [15:12] Reserved
	 * [18:16] Idle Cycle Limit
	 *	   000 = 0 cycles
	 *	   001 = 4 cycles
	 *	   010 = 8 cycles
	 *	   011 = 16 cycles
	 *	   100 = 32 cycles
	 *	   101 = 64 cycles
	 *	   110 = 128 cycles
	 *	   111 = 256 cycles
	 * [19:19] Dynamic Idle Cycle Center Enable
	 *	   0 = Use Idle Cycle Limit
	 *	   1 = Generate a dynamic Idle cycle limit
	 * [22:20] DRAM MEMCLK Frequency
	 *	   000 = 100Mhz
	 *	   001 = reserved
	 *	   010 = 133Mhz
	 *	   011 = reserved
	 *	   100 = reserved
	 *	   101 = 166Mhz
	 *	   110 = reserved
	 *	   111 = reserved
	 * [24:23] Reserved
	 * [25:25] Memory Clock Ratio Valid (FIXME carefully enable memclk)
	 *	   0 = Disable MemClks
	 *	   1 = Enable MemClks
	 * [26:26] Memory Clock 0 Enable
	 *	   0 = Disabled
	 *	   1 = Enabled
	 * [27:27] Memory Clock 1 Enable
	 *	   0 = Disabled
	 *	   1 = Enabled
	 * [28:28] Memory Clock 2 Enable
	 *	   0 = Disabled
	 *	   1 = Enabled
	 * [29:29] Memory Clock 3 Enable
	 *	   0 = Disabled
	 *	   1 = Enabled
	 * [31:30] Reserved
	 */
	PCI_ADDR(0, 0x18, 2, 0x94), 0xc180f0f0,
	(0 << 29)|(0 << 28)|(0 << 27)|(0 << 26)|(0 << 25)|
	(0 << 20)|(0 << 19)|(DCH_IDLE_LIMIT_16 << 16)|(0 << 8)|(0 << 0),
	/* DRAM Delay Line Register
	 * F2:0x98
	 * Adjust the skew of the input DQS strobe relative to DATA
	 * [15: 0] Reserved
	 * [23:16] Delay Line Adjust
	 *	   Adjusts the DLL derived PDL delay by one or more delay stages
	 *	   in either the faster or slower direction.
	 * [24:24} Adjust Slower
	 *	   0 = Do Nothing
	 *	   1 = Adj is used to increase the PDL delay
	 * [25:25] Adjust Faster
	 *	   0 = Do Nothing
	 *	   1 = Adj is used to decrease the PDL delay
	 * [31:26] Reserved
	 */
	PCI_ADDR(0, 0x18, 2, 0x98), 0xfc00ffff, 0x00000000,
	/* DRAM Scrub Control Register
	 * F3:0x58
	 * [ 4: 0] DRAM Scrube Rate
	 * [ 7: 5] reserved
	 * [12: 8] L2 Scrub Rate
	 * [15:13] reserved
	 * [20:16] Dcache Scrub
	 * [31:21] reserved
	 *	   Scrub Rates
	 *	   00000 = Do not scrub
	 *	   00001 =  40.00 ns
	 *	   00010 =  80.00 ns
	 *	   00011 = 160.00 ns
	 *	   00100 = 320.00 ns
	 *	   00101 = 640.00 ns
	 *	   00110 =   1.28 us
	 *	   00111 =   2.56 us
	 *	   01000 =   5.12 us
	 *	   01001 =  10.20 us
	 *	   01011 =  41.00 us
	 *	   01100 =  81.90 us
	 *	   01101 = 163.80 us
	 *	   01110 = 327.70 us
	 *	   01111 = 655.40 us
	 *	   10000 =   1.31 ms
	 *	   10001 =   2.62 ms
	 *	   10010 =   5.24 ms
	 *	   10011 =  10.49 ms
	 *	   10100 =  20.97 ms
	 *	   10101 =  42.00 ms
	 *	   10110 =  84.00 ms
	 *	   All Others = Reserved
	 */
	PCI_ADDR(0, 0x18, 3, 0x58), 0xffe0e0e0, 0x00000000,
	/* DRAM Scrub Address Low Register
	 * F3:0x5C
	 * [ 0: 0] DRAM Scrubber Redirect Enable
	 *	   0 = Do nothing
	 *	   1 = Scrubber Corrects errors found in normal operation
	 * [ 5: 1] Reserved
	 * [31: 6] DRAM Scrub Address 31-6
	 */
	PCI_ADDR(0, 0x18, 3, 0x5C), 0x0000003e, 0x00000000,
	/* DRAM Scrub Address High Register
	 * F3:0x60
	 * [ 7: 0] DRAM Scrubb Address 39-32
	 * [31: 8] Reserved
	 */
	PCI_ADDR(0, 0x18, 3, 0x60), 0xffffff00, 0x00000000,
#if 0
//BY LYH  add IOMMU 64M APERTURE
	PCI_ADDR(0, 0x18, 3, 0x94), 0xffff8000, 0x00000f70,
	PCI_ADDR(0, 0x18, 3, 0x90), 0xffffff80, 0x00000002,
	PCI_ADDR(0, 0x18, 3, 0x98), 0x0000000f, 0x00068300,

//BY LYH END
#endif
	};
	int i;
	int max;
	print_debug("setting up CPU");
	print_debug_hex8(ctrl->node_id);
	print_debug(" northbridge registers\r\n");
	max = sizeof(register_values)/sizeof(register_values[0]);
	for(i = 0; i < max; i += 3) {
		device_t dev;
		unsigned where;
		unsigned long reg;
#if 0
		print_debug_hex32(register_values[i]);
		print_debug(" <-");
		print_debug_hex32(register_values[i+2]);
		print_debug("\r\n");
#endif
		dev = (register_values[i] & ~0xff) - PCI_DEV(0, 0x18, 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);
#if 0

		reg = pci_read_config32(register_values[i]);
		reg &= register_values[i+1];
		reg |= register_values[i+2];
		pci_write_config32(register_values[i], reg);
#endif
	}
	print_debug("done.\r\n");
}


static int is_dual_channel(const struct mem_controller *ctrl)
{
	uint32_t dcl;
	dcl = pci_read_config32(ctrl->f2, DRAM_CONFIG_LOW);
	return dcl & DCL_128BitEn;
}

static int is_opteron(const struct mem_controller *ctrl)
{
	/* Test to see if I am an Opteron.  
	 * FIXME Testing dual channel capability is correct for now
	 * but a beter test is probably required.
	 */
#warning "FIXME implement a better test for opterons"
	uint32_t nbcap;
	nbcap = pci_read_config32(ctrl->f3, NORTHBRIDGE_CAP);
	return !!(nbcap & NBCAP_128Bit);
}

static int is_registered(const struct mem_controller *ctrl)
{
	/* Test to see if we are dealing with registered SDRAM.
	 * If we are not registered we are unbuffered.
	 * This function must be called after spd_handle_unbuffered_dimms.
	 */
	uint32_t dcl;
	dcl = pci_read_config32(ctrl->f2, DRAM_CONFIG_LOW);
	return !(dcl & DCL_UnBufDimm);
}

struct dimm_size {
	unsigned long side1;
	unsigned long side2;
};

static struct dimm_size spd_get_dimm_size(unsigned device)
{
	/* Calculate the log base 2 size of a DIMM in bits */
	struct dimm_size sz;
	int value, low;
	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, 3);	/* rows */
	if (value < 0) goto out;
	sz.side1 += value & 0xf;

	value = spd_read_byte(device, 4);	/* columns */
	if (value < 0) goto out;
	sz.side1 += value & 0xf;

	value = spd_read_byte(device, 17);	/* banks */
	if (value < 0) goto out;
	sz.side1 += log2(value & 0xff);

	/* Get the module data width and convert it to a power of two */
	value = spd_read_byte(device, 7);	/* (high byte) */
	if (value < 0) goto out;
	value &= 0xff;
	value <<= 8;
	
	low = spd_read_byte(device, 6);	/* (low byte) */
	if (low < 0) goto out;
	value = value | (low & 0xff);
	sz.side1 += log2(value);

	/* side 2 */
	value = spd_read_byte(device, 5);	/* number of physical banks */
	if (value <= 1) goto out;

	/* Start with the symmetrical case */
	sz.side2 = sz.side1;

	value = spd_read_byte(device, 3);	/* rows */
	if (value < 0) goto out;
	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, 4);	/* columns */
	if (value < 0) goto out;
	sz.side2 -= (value & 0x0f);		/* Subtract out columns on side 1 */
	sz.side2 += ((value >> 4) & 0x0f);	/* Add in columsn on side 2 */

 out:
	return sz;
}

static void set_dimm_size(const struct mem_controller *ctrl, struct dimm_size sz, unsigned index)
{
	uint32_t base0, base1, map;
	uint32_t dch;

#if 0
	print_debug("set_dimm_size: (");
	print_debug_hex32(sz.side1);
	print_debug_char(',');
	print_debug_hex32(sz.side2);
	print_debug_char(',');
	print_debug_hex32(index);
	print_debug(")\r\n");
#endif
	if (sz.side1 != sz.side2) {
		sz.side2 = 0;
	}
	map = pci_read_config32(ctrl->f2, DRAM_BANK_ADDR_MAP);
	map &= ~(0xf << (index + 4));

	/* For each base register.
	 * Place the dimm size in 32 MB quantities in the bits 31 - 21.
	 * The initialize dimm size is in bits.
	 * Set the base enable bit0.
	 */
	
	base0 = base1 = 0;

	/* Make certain side1 of the dimm is at least 32MB */
	if (sz.side1 >= (25 +3)) {
		map |= (sz.side1 - (25 + 3)) << (index *4);
		base0 = (1 << ((sz.side1 - (25 + 3)) + 21)) | 1;
	}
	/* Make certain side2 of the dimm is at least 32MB */
	if (sz.side2 >= (25 + 3)) {
		base1 = (1 << ((sz.side2 - (25 + 3)) + 21)) | 1;
	}

	/* Double the size if we are using dual channel memory */
	if (is_dual_channel(ctrl)) {
		base0 = (base0 << 1) | (base0 & 1);
		base1 = (base1 << 1) | (base1 & 1);
	}

	/* Clear the reserved bits */
	base0 &= ~0x001ffffe;
	base1 &= ~0x001ffffe;

	/* Set the appropriate DIMM base address register */
	pci_write_config32(ctrl->f2, DRAM_CSBASE + (((index << 1)+0)<<2), base0);
	pci_write_config32(ctrl->f2, DRAM_CSBASE + (((index << 1)+1)<<2), base1);
	pci_write_config32(ctrl->f2, DRAM_BANK_ADDR_MAP, map);
	
	/* Enable the memory clocks for this DIMM */
	if (base0) {
		dch = pci_read_config32(ctrl->f2, DRAM_CONFIG_HIGH);
		dch |= DCH_MEMCLK_EN0 << index;
		pci_write_config32(ctrl->f2, DRAM_CONFIG_HIGH, dch);
	}
}

static void spd_set_ram_size(const struct mem_controller *ctrl)
{
	int i;
	
	for(i = 0; (i < 4) && (ctrl->channel0[i]); i++) {
		struct dimm_size sz;
		sz = spd_get_dimm_size(ctrl->channel0[i]);
		set_dimm_size(ctrl, sz, i);
	}
}

static void route_dram_accesses(const struct mem_controller *ctrl,
	unsigned long base_k, unsigned long limit_k)
{
	/* Route the addresses to the controller node */
	unsigned node_id;
	unsigned limit;
	unsigned base;
	unsigned index;
	unsigned limit_reg, base_reg;
	device_t device;

	node_id = ctrl->node_id;
	index = (node_id << 3);
	limit = (limit_k << 2);
	limit &= 0xffff0000;
	limit -= 0x00010000;
	limit |= ( 0 << 8) | (node_id << 0);
	base = (base_k << 2);
	base &= 0xffff0000;
	base |= (0 << 8) | (1<<1) | (1<<0);

	limit_reg = 0x44 + index;
	base_reg = 0x40 + index;
	for(device = PCI_DEV(0, 0x18, 1); device <= PCI_DEV(0, 0x1f, 1); device += PCI_DEV(0, 1, 0)) {
		pci_write_config32(device, limit_reg, limit);
		pci_write_config32(device, base_reg, base);
	}
}

static void set_top_mem(unsigned tom_k)
{
	/* Error if I don't have memory */
	if (!tom_k) {
		die("No memory");
	}

#if 1
	/* Report the amount of memory. */
	print_debug("RAM: 0x");
	print_debug_hex32(tom_k);
	print_debug(" KB\r\n");
#endif

	/* Now set top of memory */
	msr_t msr;
	msr.lo = (tom_k & 0x003fffff) << 10;
	msr.hi = (tom_k & 0xffc00000) >> 22;
	wrmsr(TOP_MEM2, msr);

	/* Leave a 64M hole between TOP_MEM and TOP_MEM2
	 * so I can see my rom chip and other I/O devices.
	 */
	if (tom_k >= 0x003f0000) {
		tom_k = 0x3f0000;
	}
	msr.lo = (tom_k & 0x003fffff) << 10;
	msr.hi = (tom_k & 0xffc00000) >> 22;
	wrmsr(TOP_MEM, msr);
}

static void order_dimms(const struct mem_controller *ctrl)
{
	unsigned long tom, tom_k, base_k;
	unsigned node_id;

	/* Compute the memory base address address */
	base_k = 0;
	/* Remember which registers we have used in the high 8 bits of tom */
	tom = base_k >> 15;
	for(;;) {
		/* Find the largest remaining canidate */
		unsigned index, canidate;
		uint32_t csbase, csmask;
		unsigned size;
		csbase = 0;
		canidate = 0;
		for(index = 0; index < 8; index++) {
			uint32_t value;
			value = pci_read_config32(ctrl->f2, DRAM_CSBASE + (index << 2));

			/* Is it enabled? */
			if (!(value & 1)) {
				continue;
			}
			
			/* Is it greater? */
			if (value <= csbase) {
				continue;
			}
			
			/* Has it already been selected */
			if (tom & (1 << (index + 24))) {
				continue;
			}
			/* I have a new canidate */
			csbase = value;
			canidate = index;
		}
		/* See if I have found a new canidate */
		if (csbase == 0) {
			break;
		}

		/* Remember the dimm size */
		size = csbase >> 21;

		/* Remember I have used this register */
		tom |= (1 << (canidate + 24));

		/* Recompute the cs base register value */
		csbase = (tom << 21) | 1;

		/* Increment the top of memory */
		tom += size;

		/* Compute the memory mask */
		csmask = ((size -1) << 21);
		csmask |= 0xfe00;		/* For now don't optimize */
#warning "Don't forget to optimize the DIMM size"

		/* Write the new base register */
		pci_write_config32(ctrl->f2, DRAM_CSBASE + (canidate << 2), csbase);
		/* Write the new mask register */
		pci_write_config32(ctrl->f2, DRAM_CSMASK + (canidate << 2), csmask);
		
	}
	tom_k = (tom & ~0xff000000) << 15;

	/* Compute the memory base address */
	base_k = 0;
	for(node_id = 0; node_id < ctrl->node_id; node_id++) {
		uint32_t limit, base;
		unsigned index;
		index = node_id << 3;
		base = pci_read_config32(ctrl->f1, 0x40 + index);
		/* Only look at the limit if the base is enabled */
		if ((base & 3) == 3) {
			limit = pci_read_config32(ctrl->f1, 0x44 + index);
			base_k = ((limit + 0x00010000) & 0xffff0000) >> 2;
		}
	}
	tom_k += base_k;
#if 0
	print_debug("tom: ");
	print_debug_hex32(tom);
	print_debug(" base_k: ");
	print_debug_hex32(base_k);
	print_debug(" tom_k: ");
	print_debug_hex32(tom_k);
	print_debug("\r\n");
#endif
	route_dram_accesses(ctrl, base_k, tom_k);
	set_top_mem(tom_k);
}

static void disable_dimm(const struct mem_controller *ctrl, unsigned index)
{
	print_debug("disabling dimm"); 
	print_debug_hex8(index); 
	print_debug("\r\n");
	pci_write_config32(ctrl->f2, DRAM_CSBASE + (((index << 1)+0)<<2), 0);
	pci_write_config32(ctrl->f2, DRAM_CSBASE + (((index << 1)+1)<<2), 0);
}


static void spd_handle_unbuffered_dimms(const struct mem_controller *ctrl)
{
	int i;
	int registered;
	int unbuffered;
	uint32_t dcl;
	unbuffered = 0;
	registered = 0;
	for(i = 0; (i < 4) && (ctrl->channel0[i]); i++) {
		int value;
		value = spd_read_byte(ctrl->channel0[i], 21);
		if (value < 0) {
			disable_dimm(ctrl, i);
			continue;
		}
		/* Registered dimm ? */
		if (value & (1 << 1)) {
			registered = 1;
		} 
		/* Otherwise it must be an unbuffered dimm */
		else {
			unbuffered = 1;
		}
	}
	if (unbuffered && registered) {
		die("Mixed buffered and registered dimms not supported");
	}
	if (unbuffered && is_opteron(ctrl)) {
		die("Unbuffered Dimms not supported on Opteron");
	}

	dcl = pci_read_config32(ctrl->f2, DRAM_CONFIG_LOW);
	dcl &= ~DCL_UnBufDimm;
	if (unbuffered) {
		dcl |= DCL_UnBufDimm;
	}
	pci_write_config32(ctrl->f2, DRAM_CONFIG_LOW, dcl);
#if 0
	if (is_registered(ctrl)) {
		print_debug("Registered\r\n");
	} else {
		print_debug("Unbuffered\r\n");
	}
#endif
}

static void spd_enable_2channels(const struct mem_controller *ctrl)
{
	int i;
	uint32_t nbcap;
	/* SPD addresses to verify are identical */
#warning "FINISHME review and see if these are the bytes I need"
	/* FINISHME review and see if these are the bytes I need */
	static const unsigned addresses[] = {
		2,	/* Type should be DDR SDRAM */
		3,	/* *Row addresses */
		4,	/* *Column addresses */
		5,	/* *Physical Banks */
		6,	/* *Module Data Width low */
		7,	/* *Module Data Width high */
		9,	/* *Cycle time at highest CAS Latency CL=X */
		11,	/* *SDRAM Type */
		13,	/* *SDRAM Width */
		17,	/* *Logical Banks */
		18,	/* *Supported CAS Latencies */
		21,	/* *SDRAM Module Attributes */
		23,	/* *Cycle time at CAS Latnecy (CLX - 0.5) */
		26,	/* *Cycle time at CAS Latnecy (CLX - 1.0) */
		27,	/* *tRP Row precharge time */
		28,	/* *Minimum Row Active to Row Active Delay (tRRD) */
		29,	/* *tRCD RAS to CAS */
		30,	/* *tRAS Activate to Precharge */
		41,	/* *Minimum Active to Active/Auto Refresh Time(Trc) */
		42,	/* *Minimum Auto Refresh Command Time(Trfc) */
	};
	nbcap = pci_read_config32(ctrl->f3, NORTHBRIDGE_CAP);
	if (!(nbcap & NBCAP_128Bit)) {
		return;
	}
	for(i = 0; (i < 4) && (ctrl->channel0[i]); i++) {
		unsigned device0, device1;
		int value0, value1;
		int j;
		device0 = ctrl->channel0[i];
		device1 = ctrl->channel1[i];
		if (!device1)
			return;
		for(j = 0; j < sizeof(addresses)/sizeof(addresses[0]); j++) {
			unsigned addr;
			addr = addresses[j];
			value0 = spd_read_byte(device0, addr);
			if (value0 < 0) {
				break;
			}
			value1 = spd_read_byte(device1, addr);
			if (value1 < 0) {
				return;
			}
			if (value0 != value1) {
				return;
			}
		}
	}
	print_debug("Enabling dual channel memory\r\n");
	uint32_t dcl;
	dcl = pci_read_config32(ctrl->f2, DRAM_CONFIG_LOW);
	dcl &= ~DCL_32ByteEn;
	dcl |= DCL_128BitEn;
	pci_write_config32(ctrl->f2, DRAM_CONFIG_LOW, dcl);
}

struct mem_param {
	uint8_t cycle_time;
	uint8_t divisor; /* In 1/2 ns increments */
	uint8_t tRC;
	uint8_t tRFC;
	uint32_t dch_memclk;
	uint16_t dch_tref4k, dch_tref8k;
	uint8_t	 dtl_twr;
	char name[9];
};

static const struct mem_param *get_mem_param(unsigned min_cycle_time)
{
	static const struct mem_param speed[] = {
		{
			.name	    = "100Mhz\r\n",
			.cycle_time = 0xa0,
			.divisor    = (10 <<1),
			.tRC	    = 0x46,
			.tRFC	    = 0x50,
			.dch_memclk = DCH_MEMCLK_100MHZ << DCH_MEMCLK_SHIFT,
			.dch_tref4k = DTH_TREF_100MHZ_4K,
			.dch_tref8k = DTH_TREF_100MHZ_8K,
			.dtl_twr    = 2,
		},
		{
			.name	    = "133Mhz\r\n",
			.cycle_time = 0x75,
			.divisor    = (7<<1)+1,
			.tRC	    = 0x41,
			.tRFC	    = 0x4B,
			.dch_memclk = DCH_MEMCLK_133MHZ << DCH_MEMCLK_SHIFT,
			.dch_tref4k = DTH_TREF_133MHZ_4K,
			.dch_tref8k = DTH_TREF_133MHZ_8K,
			.dtl_twr    = 2,
		},
		{
			.name	    = "166Mhz\r\n",
			.cycle_time = 0x60,
			.divisor    = (6<<1),
			.tRC	    = 0x3C,
			.tRFC	    = 0x48,
			.dch_memclk = DCH_MEMCLK_166MHZ << DCH_MEMCLK_SHIFT,
			.dch_tref4k = DTH_TREF_166MHZ_4K,
			.dch_tref8k = DTH_TREF_166MHZ_8K,
			.dtl_twr    = 3,
		},
		{
			.name	    = "200Mhz\r\n",
			.cycle_time = 0x50,
			.divisor    = (5<<1),
			.tRC	    = 0x37,
			.tRFC	    = 0x46,
			.dch_memclk = DCH_MEMCLK_200MHZ << DCH_MEMCLK_SHIFT,
			.dch_tref4k = DTH_TREF_200MHZ_4K,
			.dch_tref8k = DTH_TREF_200MHZ_8K,
			.dtl_twr    = 3,
		},
		{
			.cycle_time = 0x00,
		},
	};
	const struct mem_param *param;
	for(param = &speed[0]; param->cycle_time ; param++) {
		if (min_cycle_time > (param+1)->cycle_time) {
			break;
		}
	}
	if (!param->cycle_time) {
		die("min_cycle_time to low");
	}
#if 1
	print_debug(param->name);
#endif
	return param;
}

static const struct mem_param *spd_set_memclk(const struct mem_controller *ctrl)
{
	/* Compute the minimum cycle time for these dimms */
	const struct mem_param *param;
	unsigned min_cycle_time, min_latency;
	int i;
	uint32_t value;

	static const int latency_indicies[] = { 26, 23, 9 };
	static const unsigned char min_cycle_times[] = {
		[NBCAP_MEMCLK_200MHZ] = 0x50, /* 5ns */
		[NBCAP_MEMCLK_166MHZ] = 0x60, /* 6ns */
		[NBCAP_MEMCLK_133MHZ] = 0x75, /* 7.5ns */
		[NBCAP_MEMCLK_100MHZ] = 0xa0, /* 10ns */
	};


	value = pci_read_config32(ctrl->f3, NORTHBRIDGE_CAP);
	min_cycle_time = min_cycle_times[(value >> NBCAP_MEMCLK_SHIFT) & NBCAP_MEMCLK_MASK];
	min_latency = 2;

#if 0
	print_debug("min_cycle_time: "); 
	print_debug_hex8(min_cycle_time); 
	print_debug(" min_latency: ");
	print_debug_hex8(min_latency);
	print_debug("\r\n");
#endif

	/* Compute the least latency with the fastest clock supported
	 * by both the memory controller and the dimms.
	 */
	for(i = 0; (i < 4) && (ctrl->channel0[i]); i++) {
		int new_cycle_time, new_latency;
		int index;
		int latencies;
		int latency;

		/* First find the supported CAS latencies
		 * Byte 18 for DDR SDRAM is interpreted:
		 * bit 0 == CAS Latency = 1.0
		 * bit 1 == CAS Latency = 1.5
		 * bit 2 == CAS Latency = 2.0
		 * bit 3 == CAS Latency = 2.5
		 * bit 4 == CAS Latency = 3.0
		 * bit 5 == CAS Latency = 3.5
		 * bit 6 == TBD
		 * bit 7 == TBD
		 */
		new_cycle_time = 0xa0;
		new_latency = 5;

		latencies = spd_read_byte(ctrl->channel0[i], 18);
		if (latencies <= 0) continue;

		/* Compute the lowest cas latency supported */
		latency = log2(latencies) -2;

		/* Loop through and find a fast clock with a low latency */
		for(index = 0; index < 3; index++, latency++) {
			int value;
			if ((latency < 2) || (latency > 4) ||
				(!(latencies & (1 << latency)))) {
				continue;
			}
			value = spd_read_byte(ctrl->channel0[i], latency_indicies[index]);
			if (value < 0) {
				continue;
			}

			/* Only increase the latency if we decreas the clock */
			if ((value >= min_cycle_time) && (value < new_cycle_time)) {
				new_cycle_time = value;
				new_latency = latency;
			}
		}
		if (new_latency > 4){
			continue;
		}
		/* Does min_latency need to be increased? */
		if (new_cycle_time > min_cycle_time) {
			min_cycle_time = new_cycle_time;
		}
		/* Does min_cycle_time need to be increased? */
		if (new_latency > min_latency) {
			min_latency = new_latency;
		}
#if 0
		print_debug("i: ");
		print_debug_hex8(i);
		print_debug(" min_cycle_time: "); 
		print_debug_hex8(min_cycle_time); 
		print_debug(" min_latency: ");
		print_debug_hex8(min_latency);
		print_debug("\r\n");
#endif
	}
	/* Make a second pass through the dimms and disable
	 * any that cannot support the selected memclk and cas latency.
	 */
	
	for(i = 0; (i < 4) && (ctrl->channel0[i]); i++) {
		int latencies;
		int latency;
		int index;
		int value;
		int dimm;
		latencies = spd_read_byte(ctrl->channel0[i], 18);
		if (latencies <= 0) {
			goto dimm_err;
		}

		/* Compute the lowest cas latency supported */
		latency = log2(latencies) -2;

		/* Walk through searching for the selected latency */
		for(index = 0; index < 3; index++, latency++) {
			if (!(latencies & (1 << latency))) {
				continue;
			}
			if (latency == min_latency)
				break;
		}
		/* If I can't find the latency or my index is bad error */
		if ((latency != min_latency) || (index >= 3)) {
			goto dimm_err;
		}
		
		/* Read the min_cycle_time for this latency */
		value = spd_read_byte(ctrl->channel0[i], latency_indicies[index]);
		
		/* All is good if the selected clock speed 
		 * is what I need or slower.
		 */
		if (value <= min_cycle_time) {
			continue;
		}
		/* Otherwise I have an error, disable the dimm */
	dimm_err:
		disable_dimm(ctrl, i);
	}
#if 0
	print_debug("min_cycle_time: "); 
	print_debug_hex8(min_cycle_time); 
	print_debug(" min_latency: ");
	print_debug_hex8(min_latency);
	print_debug("\r\n");
#endif
	/* Now that I know the minimum cycle time lookup the memory parameters */
	param = get_mem_param(min_cycle_time);

	/* Update DRAM Config High with our selected memory speed */
	value = pci_read_config32(ctrl->f2, DRAM_CONFIG_HIGH);
	value &= ~(DCH_MEMCLK_MASK << DCH_MEMCLK_SHIFT);
	value |= param->dch_memclk;
	pci_write_config32(ctrl->f2, DRAM_CONFIG_HIGH, value);

	static const unsigned latencies[] = { DTL_CL_2, DTL_CL_2_5, DTL_CL_3 };
	/* Update DRAM Timing Low with our selected cas latency */
	value = pci_read_config32(ctrl->f2, DRAM_TIMING_LOW);
	value &= ~(DTL_TCL_MASK << DTL_TCL_SHIFT);
	value |= latencies[min_latency - 2] << DTL_TCL_SHIFT;
	pci_write_config32(ctrl->f2, DRAM_TIMING_LOW, value);
	
	return param;
}


static int update_dimm_Trc(const struct mem_controller *ctrl, const struct mem_param *param, int i)
{
	unsigned clocks, old_clocks;
	uint32_t dtl;
	int value;
	value = spd_read_byte(ctrl->channel0[i], 41);
	if (value < 0) return -1;
	if ((value == 0) || (value == 0xff)) {
		value = param->tRC;
	}
	clocks = ((value << 1) + param->divisor - 1)/param->divisor;
	if (clocks < DTL_TRC_MIN) {
		clocks = DTL_TRC_MIN;
	}
	if (clocks > DTL_TRC_MAX) {
		return -1;
	}

	dtl = pci_read_config32(ctrl->f2, DRAM_TIMING_LOW);
	old_clocks = ((dtl >> DTL_TRC_SHIFT) & DTL_TRC_MASK) + DTL_TRC_BASE;
	if (old_clocks > clocks) {
		clocks = old_clocks;
	}
	dtl &= ~(DTL_TRC_MASK << DTL_TRC_SHIFT);
	dtl |=	((clocks - DTL_TRC_BASE) << DTL_TRC_SHIFT);
	pci_write_config32(ctrl->f2, DRAM_TIMING_LOW, dtl);
	return 0;
}

static int update_dimm_Trfc(const struct mem_controller *ctrl, const struct mem_param *param, int i)
{
	unsigned clocks, old_clocks;
	uint32_t dtl;
	int value;
	value = spd_read_byte(ctrl->channel0[i], 42);
	if (value < 0) return -1;
	if ((value == 0) || (value == 0xff)) {
		value = param->tRFC;
	}
	clocks = ((value << 1) + param->divisor - 1)/param->divisor;
	if (clocks < DTL_TRFC_MIN) {
		clocks = DTL_TRFC_MIN;
	}
	if (clocks > DTL_TRFC_MAX) {
		return -1;
	}
	dtl = pci_read_config32(ctrl->f2, DRAM_TIMING_LOW);
	old_clocks = ((dtl >> DTL_TRFC_SHIFT) & DTL_TRFC_MASK) + DTL_TRFC_BASE;
	if (old_clocks > clocks) {
		clocks = old_clocks;
	}
	dtl &= ~(DTL_TRFC_MASK << DTL_TRFC_SHIFT);
	dtl |= ((clocks - DTL_TRFC_BASE) << DTL_TRFC_SHIFT);
	pci_write_config32(ctrl->f2, DRAM_TIMING_LOW, dtl);
	return 0;
}


static int update_dimm_Trcd(const struct mem_controller *ctrl, const struct mem_param *param, int i)
{
	unsigned clocks, old_clocks;
	uint32_t dtl;
	int value;
	value = spd_read_byte(ctrl->channel0[i], 29);
	if (value < 0) return -1;
#if 0
	clocks = (value + (param->divisor << 1) -1)/(param->divisor << 1);
#else
	clocks = (value + ((param->divisor & 0xff) << 1) -1)/((param->divisor & 0xff) << 1);
#endif
	if (clocks < DTL_TRCD_MIN) {
		clocks = DTL_TRCD_MIN;
	}
	if (clocks > DTL_TRCD_MAX) {
		return -1;
	}
	dtl = pci_read_config32(ctrl->f2, DRAM_TIMING_LOW);
	old_clocks = ((dtl >> DTL_TRCD_SHIFT) & DTL_TRCD_MASK) + DTL_TRCD_BASE;
	if (old_clocks > clocks) {
		clocks = old_clocks;
	}
	dtl &= ~(DTL_TRCD_MASK << DTL_TRCD_SHIFT);
	dtl |= ((clocks - DTL_TRCD_BASE) << DTL_TRCD_SHIFT);
	pci_write_config32(ctrl->f2, DRAM_TIMING_LOW, dtl);
	return 0;
}

static int update_dimm_Trrd(const struct mem_controller *ctrl, const struct mem_param *param, int i)
{
	unsigned clocks, old_clocks;
	uint32_t dtl;
	int value;
	value = spd_read_byte(ctrl->channel0[i], 28);
	if (value < 0) return -1;
	clocks = (value + ((param->divisor & 0xff) << 1) -1)/((param->divisor & 0xff) << 1);
	if (clocks < DTL_TRRD_MIN) {
		clocks = DTL_TRRD_MIN;
	}
	if (clocks > DTL_TRRD_MAX) {
		return -1;
	}
	dtl = pci_read_config32(ctrl->f2, DRAM_TIMING_LOW);
	old_clocks = ((dtl >> DTL_TRRD_SHIFT) & DTL_TRRD_MASK) + DTL_TRRD_BASE;
	if (old_clocks > clocks) {
		clocks = old_clocks;
	}
	dtl &= ~(DTL_TRRD_MASK << DTL_TRRD_SHIFT);
	dtl |= ((clocks - DTL_TRRD_BASE) << DTL_TRRD_SHIFT);
	pci_write_config32(ctrl->f2, DRAM_TIMING_LOW, dtl);
	return 0;
}

static int update_dimm_Tras(const struct mem_controller *ctrl, const struct mem_param *param, int i)
{
	unsigned clocks, old_clocks;
	uint32_t dtl;
	int value;
	value = spd_read_byte(ctrl->channel0[i], 30);
	if (value < 0) return -1;
	clocks = ((value << 1) + param->divisor - 1)/param->divisor;
	if (clocks < DTL_TRAS_MIN) {
		clocks = DTL_TRAS_MIN;
	}
	if (clocks > DTL_TRAS_MAX) {
		return -1;
	}
	dtl = pci_read_config32(ctrl->f2, DRAM_TIMING_LOW);
	old_clocks = ((dtl >> DTL_TRAS_SHIFT) & DTL_TRAS_MASK) + DTL_TRAS_BASE;
	if (old_clocks > clocks) {
		clocks = old_clocks;
	}
	dtl &= ~(DTL_TRAS_MASK << DTL_TRAS_SHIFT);
	dtl |= ((clocks - DTL_TRAS_BASE) << DTL_TRAS_SHIFT);
	pci_write_config32(ctrl->f2, DRAM_TIMING_LOW, dtl);
	return 0;
}

static int update_dimm_Trp(const struct mem_controller *ctrl, const struct mem_param *param, int i)
{
	unsigned clocks, old_clocks;
	uint32_t dtl;
	int value;
	value = spd_read_byte(ctrl->channel0[i], 27);
	if (value < 0) return -1;
#if 0
	clocks = (value + (param->divisor << 1) - 1)/(param->divisor << 1);
#else
	clocks = (value + ((param->divisor & 0xff) << 1) - 1)/((param->divisor & 0xff) << 1);
#endif
#if 0
	print_debug("Trp: ");
	print_debug_hex8(clocks);
	print_debug(" spd value: ");
	print_debug_hex8(value);
	print_debug(" divisor: ");
	print_debug_hex8(param->divisor);
	print_debug("\r\n");
#endif
	if (clocks < DTL_TRP_MIN) {
		clocks = DTL_TRP_MIN;
	}
	if (clocks > DTL_TRP_MAX) {
		return -1;
	}
	dtl = pci_read_config32(ctrl->f2, DRAM_TIMING_LOW);
	old_clocks = ((dtl >> DTL_TRP_SHIFT) & DTL_TRP_MASK) + DTL_TRP_BASE;
	if (old_clocks > clocks) {
		clocks = old_clocks;
	}
	dtl &= ~(DTL_TRP_MASK << DTL_TRP_SHIFT);
	dtl |= ((clocks - DTL_TRP_BASE) << DTL_TRP_SHIFT);
	pci_write_config32(ctrl->f2, DRAM_TIMING_LOW, dtl);
	return 0;
}

static void set_Twr(const struct mem_controller *ctrl, const struct mem_param *param)
{
	uint32_t dtl;
	dtl = pci_read_config32(ctrl->f2, DRAM_TIMING_LOW);
	dtl &= ~(DTL_TWR_MASK << DTL_TWR_SHIFT);
	dtl |= (param->dtl_twr - DTL_TWR_BASE) << DTL_TWR_SHIFT;
	pci_write_config32(ctrl->f2, DRAM_TIMING_LOW, dtl);
}


static void init_Tref(const struct mem_controller *ctrl, const struct mem_param *param)
{
	uint32_t dth;
	dth = pci_read_config32(ctrl->f2, DRAM_TIMING_HIGH);
	dth &= ~(DTH_TREF_MASK << DTH_TREF_SHIFT);
	dth |= (param->dch_tref4k << DTH_TREF_SHIFT);
	pci_write_config32(ctrl->f2, DRAM_TIMING_HIGH, dth);
}

static int update_dimm_Tref(const struct mem_controller *ctrl, const struct mem_param *param, int i)
{
	uint32_t dth;
	int value;
	unsigned tref, old_tref;
	value = spd_read_byte(ctrl->channel0[i], 3);
	if (value < 0) return -1;
	value &= 0xf;

	tref = param->dch_tref8k;
	if (value == 12) {
		tref = param->dch_tref4k;
	}

	dth = pci_read_config32(ctrl->f2, DRAM_TIMING_HIGH);
	old_tref = (dth >> DTH_TREF_SHIFT) & DTH_TREF_MASK;
	if ((value == 12) && (old_tref == param->dch_tref4k)) {
		tref = param->dch_tref4k;
	} else {
		tref = param->dch_tref8k;
	}
	dth &= ~(DTH_TREF_MASK << DTH_TREF_SHIFT);
	dth |= (tref << DTH_TREF_SHIFT);
	pci_write_config32(ctrl->f2, DRAM_TIMING_HIGH, dth);
	return 0;
}


static int update_dimm_x4(const struct mem_controller *ctrl, const struct mem_param *param, int i)
{
	uint32_t dcl;
	int value;
	int dimm;
	value = spd_read_byte(ctrl->channel0[i], 13);
	if (value < 0) {
		return -1;
	}
	dimm = i;
	dimm += DCL_x4DIMM_SHIFT;
	dcl = pci_read_config32(ctrl->f2, DRAM_CONFIG_LOW);
	dcl &= ~(1 << dimm);
	if (value == 4) {
		dcl |= (1 << dimm);
	}
	pci_write_config32(ctrl->f2, DRAM_CONFIG_LOW, dcl);
	return 0;
}

static int update_dimm_ecc(const struct mem_controller *ctrl, const struct mem_param *param, int i)
{
	uint32_t dcl;
	int value;
	value = spd_read_byte(ctrl->channel0[i], 11);
	if (value < 0) {
		return -1;
	}
	if (value != 2) {
		dcl = pci_read_config32(ctrl->f2, DRAM_CONFIG_LOW);
		dcl &= ~DCL_DimmEccEn;
		pci_write_config32(ctrl->f2, DRAM_CONFIG_LOW, dcl);
	}
	return 0;
}

static int count_dimms(const struct mem_controller *ctrl)
{
	int dimms;
	unsigned index;
	dimms = 0;
	for(index = 0; index < 8; index += 2) {
		uint32_t csbase;
		csbase = pci_read_config32(ctrl->f2, (DRAM_CSBASE + index << 2));
		if (csbase & 1) {
			dimms += 1;
		}
	}
	return dimms;
}

static void set_Twtr(const struct mem_controller *ctrl, const struct mem_param *param)
{
	uint32_t dth;
	unsigned clocks;
	clocks = 1; /* AMD says hard code this */
	dth = pci_read_config32(ctrl->f2, DRAM_TIMING_HIGH);
	dth &= ~(DTH_TWTR_MASK << DTH_TWTR_SHIFT);
	dth |= ((clocks - DTH_TWTR_BASE) << DTH_TWTR_SHIFT);
	pci_write_config32(ctrl->f2, DRAM_TIMING_HIGH, dth);
}

static void set_Trwt(const struct mem_controller *ctrl, const struct mem_param *param)
{
	uint32_t dth, dtl;
	unsigned divisor;
	unsigned latency;
	unsigned clocks;

	clocks = 0;
	dtl = pci_read_config32(ctrl->f2, DRAM_TIMING_LOW);
	latency = (dtl >> DTL_TCL_SHIFT) & DTL_TCL_MASK;
	divisor = param->divisor;

	if (is_opteron(ctrl)) {
		if (latency == DTL_CL_2) {
			if (divisor == ((6 << 0) + 0)) {
				/* 166Mhz */
				clocks = 3;
			}
			else if (divisor > ((6 << 0)+0)) {
				/* 100Mhz && 133Mhz */
				clocks = 2;
			}
		}
		else if (latency == DTL_CL_2_5) {
			clocks = 3;
		}
		else if (latency == DTL_CL_3) {
			if (divisor == ((6 << 0)+0)) {
				/* 166Mhz */
				clocks = 4;
			}
			else if (divisor > ((6 << 0)+0)) {
				/* 100Mhz && 133Mhz */
				clocks = 3;
			}
		}
	}
	else /* Athlon64 */ {
		if (is_registered(ctrl)) {
			if (latency == DTL_CL_2) {
				clocks = 2;
			}
			else if (latency == DTL_CL_2_5) {
				clocks = 3;
			}
			else if (latency == DTL_CL_3) {
				clocks = 3;
			}
		}
		else /* Unbuffered */{
			if (latency == DTL_CL_2) {
				clocks = 3;
			}
			else if (latency == DTL_CL_2_5) {
				clocks = 4;
			}
			else if (latency == DTL_CL_3) {
				clocks = 4;
			}
		}
	}
	if ((clocks < DTH_TRWT_MIN) || (clocks > DTH_TRWT_MAX)) {
		die("Unknown Trwt");
	}
	
	dth = pci_read_config32(ctrl->f2, DRAM_TIMING_HIGH);
	dth &= ~(DTH_TRWT_MASK << DTH_TRWT_SHIFT);
	dth |= ((clocks - DTH_TRWT_BASE) << DTH_TRWT_SHIFT);
	pci_write_config32(ctrl->f2, DRAM_TIMING_HIGH, dth);
	return;
}

static void set_Twcl(const struct mem_controller *ctrl, const struct mem_param *param)
{
	/* Memory Clocks after CAS# */
	uint32_t dth;
	unsigned clocks;
	if (is_registered(ctrl)) {
		clocks = 2;
	} else {
		clocks = 1;
	}
	dth = pci_read_config32(ctrl->f2, DRAM_TIMING_HIGH);
	dth &= ~(DTH_TWCL_MASK << DTH_TWCL_SHIFT);
	dth |= ((clocks - DTH_TWCL_BASE) << DTH_TWCL_SHIFT);
	pci_write_config32(ctrl->f2, DRAM_TIMING_HIGH, dth);
}


static void set_read_preamble(const struct mem_controller *ctrl, const struct mem_param *param)
{
	uint32_t dch;
	unsigned divisor;
	unsigned rdpreamble;
	divisor = param->divisor;
	dch = pci_read_config32(ctrl->f2, DRAM_CONFIG_HIGH);
	dch &= ~(DCH_RDPREAMBLE_MASK << DCH_RDPREAMBLE_SHIFT);
	rdpreamble = 0;
	if (is_registered(ctrl)) {
		if (divisor == ((10 << 1)+0)) {
			/* 100Mhz, 9ns */
			rdpreamble = ((9 << 1)+ 0);
		}
		else if (divisor == ((7 << 1)+1)) {
			/* 133Mhz, 8ns */
			rdpreamble = ((8 << 1)+0);
		}
		else if (divisor == ((6 << 1)+0)) {
			/* 166Mhz, 7.5ns */
			rdpreamble = ((7 << 1)+1);
		}
	}
	else {
		int slots;
		int i;
		slots = 0;
		for(i = 0; i < 4; i++) {
			if (ctrl->channel0[i]) {
				slots += 1;
			}
		}
		if (divisor == ((10 << 1)+0)) {
			/* 100Mhz */
			if (slots <= 2) {
				/* 9ns */
				rdpreamble = ((9 << 1)+0);
			} else {
				/* 14ns */
				rdpreamble = ((14 << 1)+0);
			}
		}
		else if (divisor == ((7 << 1)+1)) {
			/* 133Mhz */
			if (slots <= 2) {
				/* 7ns */
				rdpreamble = ((7 << 1)+0);
			} else {
				/* 11 ns */
				rdpreamble = ((11 << 1)+0);
			}
		}
		else if (divisor == ((6 << 1)+0)) {
			/* 166Mhz */
			if (slots <= 2) {
				/* 6ns */
				rdpreamble = ((7 << 1)+0);
			} else {
				/* 9ns */
				rdpreamble = ((9 << 1)+0);
			}
		}
		else if (divisor == ((5 << 1)+0)) {
			/* 200Mhz */
			if (slots <= 2) {
				/* 5ns */
				rdpreamble = ((5 << 1)+0);
			} else {
				/* 7ns */
				rdpreamble = ((7 << 1)+0);
			}
		}
	}
	if ((rdpreamble < DCH_RDPREAMBLE_MIN) || (rdpreamble > DCH_RDPREAMBLE_MAX)) {
		die("Unknown rdpreamble");
	}
	dch |= (rdpreamble - DCH_RDPREAMBLE_BASE) << DCH_RDPREAMBLE_SHIFT;
	pci_write_config32(ctrl->f2, DRAM_CONFIG_HIGH, dch);
}

static void set_max_async_latency(const struct mem_controller *ctrl, const struct mem_param *param)
{
	uint32_t dch;
	int i;
	unsigned async_lat;
	int dimms;

	dimms = count_dimms(ctrl);

	dch = pci_read_config32(ctrl->f2, DRAM_CONFIG_HIGH);
	dch &= ~(DCH_ASYNC_LAT_MASK << DCH_ASYNC_LAT_SHIFT);
	async_lat = 0;
	if (is_registered(ctrl)) {
		if (dimms == 4) {
			/* 9ns */
			async_lat = 9;
		} 
		else {
			/* 8ns */
			async_lat = 8;
		}
	}
	else {
		if (dimms > 3) {
			die("Too many unbuffered dimms");
		}
		else if (dimms == 3) {
			/* 7ns */
			async_lat = 7;
		}
		else {
			/* 6ns */
			async_lat = 6;
		}
	}
	dch |= ((async_lat - DCH_ASYNC_LAT_BASE) << DCH_ASYNC_LAT_SHIFT);
	pci_write_config32(ctrl->f2, DRAM_CONFIG_HIGH, dch);
}

static void set_idle_cycle_limit(const struct mem_controller *ctrl, const struct mem_param *param)
{
	uint32_t dch;
	/* AMD says to Hardcode this */
	dch = pci_read_config32(ctrl->f2, DRAM_CONFIG_HIGH);
	dch &= ~(DCH_IDLE_LIMIT_MASK << DCH_IDLE_LIMIT_SHIFT);
	dch |= DCH_IDLE_LIMIT_16 << DCH_IDLE_LIMIT_SHIFT;
	dch |= DCH_DYN_IDLE_CTR_EN;
	pci_write_config32(ctrl->f2, DRAM_CONFIG_HIGH, dch);
}

static void spd_set_dram_timing(const struct mem_controller *ctrl, const struct mem_param *param)
{
	int dimms;
	int i;
	init_Tref(ctrl, param);
	for(i = 0; (i < 4) && ctrl->channel0[i]; i++) {
		int rc;
		/* DRAM Timing Low Register */
		if (update_dimm_Trc (ctrl, param, i) < 0) goto dimm_err;
		if (update_dimm_Trfc(ctrl, param, i) < 0) goto dimm_err;
		if (update_dimm_Trcd(ctrl, param, i) < 0) goto dimm_err;
		if (update_dimm_Trrd(ctrl, param, i) < 0) goto dimm_err;
		if (update_dimm_Tras(ctrl, param, i) < 0) goto dimm_err;
		if (update_dimm_Trp (ctrl, param, i) < 0) goto dimm_err;

		/* DRAM Timing High Register */
		if (update_dimm_Tref(ctrl, param, i) < 0) goto dimm_err;

		/* DRAM Config Low */
		if (update_dimm_x4 (ctrl, param, i) < 0) goto dimm_err;
		if (update_dimm_ecc(ctrl, param, i) < 0) goto dimm_err;
		continue;
	dimm_err:
		disable_dimm(ctrl, i);
		
	}
	/* DRAM Timing Low Register */
	set_Twr(ctrl, param);

	/* DRAM Timing High Register */
	set_Twtr(ctrl, param);
	set_Trwt(ctrl, param);
	set_Twcl(ctrl, param);

	/* DRAM Config High */
	set_read_preamble(ctrl, param);
	set_max_async_latency(ctrl, param);
	set_idle_cycle_limit(ctrl, param);
}

static void sdram_set_spd_registers(const struct mem_controller *ctrl) 
{
	const struct mem_param *param;
	spd_enable_2channels(ctrl);
	spd_set_ram_size(ctrl);
	spd_handle_unbuffered_dimms(ctrl);
	param = spd_set_memclk(ctrl);
	spd_set_dram_timing(ctrl, param);
	order_dimms(ctrl);
}

#define TIMEOUT_LOOPS 300000
static void sdram_enable(int controllers, const struct mem_controller *ctrl)
{
	int i;

	/* Before enabling memory start the memory clocks */
	for(i = 0; i < controllers; i++) {
		uint32_t dch;
		dch = pci_read_config32(ctrl[i].f2, DRAM_CONFIG_HIGH);
		dch |= DCH_MEMCLK_VALID;
		pci_write_config32(ctrl[i].f2, DRAM_CONFIG_HIGH, dch);
	}

	/* And if necessary toggle the the reset on the dimms by hand */
	memreset(controllers, ctrl);

	for(i = 0; i < controllers; i++) {
		uint32_t dcl;
		/* Toggle DisDqsHys to get it working */
		dcl = pci_read_config32(ctrl[i].f2, DRAM_CONFIG_LOW);
#if 0
		print_debug("dcl: ");
		print_debug_hex32(dcl);
		print_debug("\r\n");
#endif
		if (dcl & DCL_DimmEccEn) {
			uint32_t mnc;
			print_debug("ECC enabled\r\n");
			mnc = pci_read_config32(ctrl[i].f3, MCA_NB_CONFIG);
			mnc |= MNC_ECC_EN;
			if (dcl & DCL_128BitEn) {
				mnc |= MNC_CHIPKILL_EN;
			}
			pci_write_config32(ctrl[i].f3, MCA_NB_CONFIG, mnc);
		}
		dcl |= DCL_DisDqsHys;
		pci_write_config32(ctrl[i].f2, DRAM_CONFIG_LOW, dcl);
		dcl &= ~DCL_DisDqsHys;
		dcl &= ~DCL_DLL_Disable;
		dcl &= ~DCL_D_DRV;
		dcl &= ~DCL_QFC_EN;
		dcl |= DCL_DramInit;
		pci_write_config32(ctrl[i].f2, DRAM_CONFIG_LOW, dcl);

	}
	for(i = 0; i < controllers; i++) {
		uint32_t dcl;
		print_debug("Initializing memory: ");
		int loops = 0;
		do {
			dcl = pci_read_config32(ctrl[i].f2, DRAM_CONFIG_LOW);
			loops += 1;
			if ((loops & 1023) == 0) {
				print_debug(".");
			}
		} while(((dcl & DCL_DramInit) != 0) && (loops < TIMEOUT_LOOPS));
		if (loops >= TIMEOUT_LOOPS) {
			print_debug(" failed\r\n");
		} else {
			print_debug(" done\r\n");
		}
		if (dcl & DCL_DimmEccEn) {
			print_debug("Clearing memory: ");
			if (is_cpu_pre_c0()) {
				/* Kick the memory scrubber into high gear and scrub everything */
				uint32_t base, last_scrub_k, scrub_k;
				/* First make certain the scrubber is disabled */
				pci_write_config32(ctrl[i].f3, SCRUB_CONTROL,
					(SCRUB_NONE << 16) | (SCRUB_NONE << 8) | (SCRUB_NONE << 0));
				/* Set the scrub base address */
				base = pci_read_config32(ctrl[i].f1, 0x40 + (ctrl[i].node_id << 3));
				base &= 0xffff0000;
				pci_write_config32(ctrl[i].f3, SCRUB_ADDR_LOW,  base << 8);
				pci_write_config32(ctrl[i].f3, SCRUB_ADDR_HIGH, base >> 24);
				/* Enable DRAM scrubbing at full speed */
				pci_write_config32(ctrl[i].f3, SCRUB_CONTROL,
					(SCRUB_84ms << 16) | (SCRUB_84ms << 8) | (SCRUB_40ns << 0));
				scrub_k = 0;
				/* Wait until the DRAM Scrubber gets past 0k */
				while(scrub_k == 0) {
					scrub_k = pci_read_config32(ctrl[i].f3, SCRUB_ADDR_LOW) >> 10;
					scrub_k |= pci_read_config32(ctrl[i].f3, SCRUB_ADDR_HIGH) << 22;
				}
				/* Wait until the DRAM Scrubber loops */
				do {
					last_scrub_k = scrub_k;
					scrub_k = pci_read_config32(ctrl[i].f3, SCRUB_ADDR_LOW) >> 10;
					scrub_k |= pci_read_config32(ctrl[i].f3, SCRUB_ADDR_HIGH) << 22;
				} while(last_scrub_k <= scrub_k);
				/* Now stop the scrubber */
				pci_write_config32(ctrl[i].f3, SCRUB_CONTROL,
					(SCRUB_NONE << 16) | (SCRUB_NONE << 8) | (SCRUB_NONE << 0));
			}
			else {
				/* Wait until the automatic ram scrubber is finished */
				dcl &= ~DCL_MemClrStatus;
				pci_write_config32(ctrl[i].f2, DRAM_CONFIG_LOW, dcl);
				do {
					dcl = pci_read_config32(ctrl[i].f2, DRAM_CONFIG_LOW);
				} while((dcl & DCL_MemClrStatus) == 0);
			}
			print_debug("done\r\n");
			uint32_t base;
			/* Disable scrubbing so it is safe to set the scrub address */
			pci_write_config32(ctrl[i].f3, SCRUB_CONTROL,
				(SCRUB_NONE << 16) | (SCRUB_NONE << 8) | (SCRUB_NONE << 0));

			/* Find the Srub base address for this cpu */
			base = pci_read_config32(ctrl[i].f1, 0x40 + (ctrl[i].node_id << 3));
			base &= 0xffff0000;

			/* Set the scrub base address registers */
			pci_write_config32(ctrl[i].f3, SCRUB_ADDR_LOW, base << 8);
			pci_write_config32(ctrl[i].f3, SCRUB_ADDR_HIGH, base >> 24);

			/* Enable scrubbing at the lowest possible rate */
			pci_write_config32(ctrl[i].f3, SCRUB_CONTROL, 
				(SCRUB_84ms << 16) | (SCRUB_84ms << 8) | (SCRUB_84ms << 0));
		}
	}
}




More information about the coreboot mailing list