- Commit a working spd based memory initialization routine
[coreboot.git] / src / northbridge / amd / amdk8 / raminit.c
index 34d3ab1fa62d692d435977906a5bff39b3370ddd..abfa058821403cbd38dfde543bb76472bec0dd2b 100644 (file)
 #include <cpu/k8/mtrr.h>
-#define MEMORY_SUSE_SOLO  1 /* SuSE Solo configuration */
-#define MEMORY_LNXI_SOLO  2 /* LNXI Solo configuration */
-#define MEMORY_LNXI_HDAMA 3 /* LNXI HDAMA configuration */
+#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 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
 
-#ifndef MEMORY_CONFIG
-#define MEMORY_CONFIG MEMORY_SUSE_SOLO
-#endif
 
 static void setup_resource_map(const unsigned int *register_values, int max)
 {
@@ -297,7 +458,7 @@ static void setup_default_resource_map(void)
        setup_resource_map(register_values, max);
 }
 
-static void sdram_set_registers(void)
+static void sdram_set_registers(const struct mem_controller *ctrl)
 {
        static const unsigned int register_values[] = {
 
@@ -328,18 +489,8 @@ static void sdram_set_registers(void)
         *         This field defines the upper address bits of a 40 bit  address
         *         that define the end of the DRAM region.
         */
-#if MEMORY_CONFIG == MEMORY_LNXI_SOLO
-       PCI_ADDR(0, 0x18, 1, 0x44), 0x0000f8f8, 0x003f0000,
-       PCI_ADDR(0, 0x18, 1, 0x4C), 0x0000f8f8, 0x00000001,
-#endif
-#if MEMORY_CONFIG == MEMORY_SUSE_SOLO
-       PCI_ADDR(0, 0x18, 1, 0x44), 0x0000f8f8, 0x001f0000,
+       PCI_ADDR(0, 0x18, 1, 0x44), 0x0000f8f8, 0x00000000,
        PCI_ADDR(0, 0x18, 1, 0x4C), 0x0000f8f8, 0x00000001,
-#endif
-#if MEMORY_CONFIG == MEMORY_LNXI_HDAMA
-       PCI_ADDR(0, 0x18, 1, 0x44), 0x0000f8f8, 0x003f0000,
-       PCI_ADDR(0, 0x18, 1, 0x4C), 0x0000f8f8, 0x007f0001,
-#endif
        PCI_ADDR(0, 0x18, 1, 0x54), 0x0000f8f8, 0x00000002,
        PCI_ADDR(0, 0x18, 1, 0x5C), 0x0000f8f8, 0x00000003,
        PCI_ADDR(0, 0x18, 1, 0x64), 0x0000f8f8, 0x00000004,
@@ -376,265 +527,14 @@ static void sdram_set_registers(void)
         *         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, 0x00000003,
-#if MEMORY_CONFIG == MEMORY_LNXI_SOLO
-       PCI_ADDR(0, 0x18, 1, 0x48), 0x0000f8fc, 0x00400000,
-       PCI_ADDR(0, 0x18, 1, 0x50), 0x0000f8fc, 0x00400000,
-       PCI_ADDR(0, 0x18, 1, 0x58), 0x0000f8fc, 0x00400000,
-       PCI_ADDR(0, 0x18, 1, 0x60), 0x0000f8fc, 0x00400000,
-       PCI_ADDR(0, 0x18, 1, 0x68), 0x0000f8fc, 0x00400000,
-       PCI_ADDR(0, 0x18, 1, 0x70), 0x0000f8fc, 0x00400000,
-       PCI_ADDR(0, 0x18, 1, 0x78), 0x0000f8fc, 0x00400000,
-#endif
-#if MEMORY_CONFIG == MEMORY_SUSE_SOLO
-       PCI_ADDR(0, 0x18, 1, 0x48), 0x0000f8fc, 0x00200000,
-       PCI_ADDR(0, 0x18, 1, 0x50), 0x0000f8fc, 0x00200000,
-       PCI_ADDR(0, 0x18, 1, 0x58), 0x0000f8fc, 0x00200000,
-       PCI_ADDR(0, 0x18, 1, 0x60), 0x0000f8fc, 0x00200000,
-       PCI_ADDR(0, 0x18, 1, 0x68), 0x0000f8fc, 0x00200000,
-       PCI_ADDR(0, 0x18, 1, 0x70), 0x0000f8fc, 0x00200000,
-       PCI_ADDR(0, 0x18, 1, 0x78), 0x0000f8fc, 0x00200000,
-#endif
-#if MEMORY_CONFIG == MEMORY_LNXI_HDAMA
-       PCI_ADDR(0, 0x18, 1, 0x48), 0x0000f8fc, 0x00400003,
-       PCI_ADDR(0, 0x18, 1, 0x50), 0x0000f8fc, 0x00800000,
-       PCI_ADDR(0, 0x18, 1, 0x58), 0x0000f8fc, 0x00800000,
-       PCI_ADDR(0, 0x18, 1, 0x60), 0x0000f8fc, 0x00800000,
-       PCI_ADDR(0, 0x18, 1, 0x68), 0x0000f8fc, 0x00800000,
-       PCI_ADDR(0, 0x18, 1, 0x70), 0x0000f8fc, 0x00800000,
-       PCI_ADDR(0, 0x18, 1, 0x78), 0x0000f8fc, 0x00800000,
-       PCI_ADDR(0, 0x18, 1, 0x78), 0x0000f8fc, 0x00800000,
-#endif
-
-       /* 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
-        */
-#if (MEMORY_CONFIG == MEMORY_LNXI_SOLO) || (MEMORY_CONFIG == MEMORY_SUSE_SOLO)
-       PCI_ADDR(0, 0x18, 1, 0x84), 0x00000048, 0x00e1ff00,
-       PCI_ADDR(0, 0x18, 1, 0x8C), 0x00000048, 0x00dfff00,
-       PCI_ADDR(0, 0x18, 1, 0x94), 0x00000048, 0x00e3ff00,
-       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, 0x00000b00,
-       PCI_ADDR(0, 0x18, 1, 0xBC), 0x00000048, 0x00fe0b00,
-#endif
-#if MEMORY_CONFIG == MEMORY_LNXI_HDAMA
-       PCI_ADDR(0, 0x18, 1, 0x84), 0x00000048, 0x00fe2f00,
-       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, 0x00fec000,
-       PCI_ADDR(0, 0x18, 1, 0xAC), 0x00000048, 0x0000b000,
-       PCI_ADDR(0, 0x18, 1, 0xB4), 0x00000048, 0x00000000,
-       PCI_ADDR(0, 0x18, 1, 0xBC), 0x00000048, 0x00000000,
-#endif
-
-       /* 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
-        */
-#if (MEMORY_CONFIG == MEMORY_LNXI_SOLO) || (MEMORY_CONFIG == MEMORY_SUSE_SOLO)
-       PCI_ADDR(0, 0x18, 1, 0x80), 0x000000f0, 0x00e00003,
-       PCI_ADDR(0, 0x18, 1, 0x88), 0x000000f0, 0x00d80003,
-       PCI_ADDR(0, 0x18, 1, 0x90), 0x000000f0, 0x00e20003,
-       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, 0x00000a03,
-#endif
-#if MEMORY_CONFIG == MEMORY_LNXI_SOLO
-       PCI_ADDR(0, 0x18, 1, 0xB8), 0x000000f0, 0x00400003,
-#endif
-#if MEMORY_CONFIG == MEMORY_SUSE_SOLO
-       PCI_ADDR(0, 0x18, 1, 0xB8), 0x000000f0, 0x00200003,
-#endif
-#if MEMORY_CONFIG == MEMORY_LNXI_HDAMA
-       PCI_ADDR(0, 0x18, 1, 0x80), 0x000000f0, 0x00fc0003,
-       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, 0x00fec00e,
-       PCI_ADDR(0, 0x18, 1, 0xA8), 0x000000f0, 0x00000a03,
-       PCI_ADDR(0, 0x18, 1, 0xB0), 0x000000f0, 0x00000000,
-       PCI_ADDR(0, 0x18, 1, 0xB8), 0x000000f0, 0x00000000,
-#endif
-
-       /* 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
-        */
-#if (MEMORY_CONFIG == MEMORY_LNXI_SOLO) || (MEMORY_CONFIG == MEMORY_SUSE_SOLO)
-       PCI_ADDR(0, 0x18, 1, 0xC4), 0xFE000FC8, 0x0000d000,
-       PCI_ADDR(0, 0x18, 1, 0xCC), 0xFE000FC8, 0x000ff000,
-       PCI_ADDR(0, 0x18, 1, 0xD4), 0xFE000FC8, 0x00000000,
-       PCI_ADDR(0, 0x18, 1, 0xDC), 0xFE000FC8, 0x00000000,
-#endif
-#if MEMORY_CONFIG == MEMORY_LNXI_HDAMA
-       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,
-#endif
-
-       /* 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
-        */
-#if (MEMORY_CONFIG == MEMORY_LNXI_SOLO) || (MEMORY_CONFIG == MEMORY_SUSE_SOLO)
-       PCI_ADDR(0, 0x18, 1, 0xC0), 0xFE000FCC, 0x0000d003,
-       PCI_ADDR(0, 0x18, 1, 0xC8), 0xFE000FCC, 0x00001013,
-       PCI_ADDR(0, 0x18, 1, 0xD0), 0xFE000FCC, 0x00000000,
-       PCI_ADDR(0, 0x18, 1, 0xD8), 0xFE000FCC, 0x00000000,
-#endif
-#if MEMORY_CONFIG == MEMORY_LNXI_HDAMA
-       PCI_ADDR(0, 0x18, 1, 0xC0), 0xFE000FCC, 0x00000033,
-       PCI_ADDR(0, 0x18, 1, 0xC8), 0xFE000FCC, 0x00000000,
-       PCI_ADDR(0, 0x18, 1, 0xD0), 0xFE000FCC, 0x00000000,
-       PCI_ADDR(0, 0x18, 1, 0xD8), 0xFE000FCC, 0x00000000,
-#endif
-
-       /* 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
-        */
-#if (MEMORY_CONFIG == MEMORY_LNXI_SOLO) || (MEMORY_CONFIG == MEMORY_SUSE_SOLO)
-       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,
-#endif
-#if MEMORY_CONFIG == MEMORY_LNXI_HDAMA
-       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,
-#endif
+       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
@@ -657,24 +557,10 @@ static void sdram_set_registers(void)
         *         address that define the memory address space.  These
         *         bits decode 32-MByte blocks of memory.
         */
-#if MEMORY_CONFIG == MEMORY_LNXI_SOLO
        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,
-#endif
-#if MEMORY_CONFIG == MEMORY_SUSE_SOLO
-       PCI_ADDR(0, 0x18, 2, 0x40), 0x001f01fe, 0x00000001,
-       PCI_ADDR(0, 0x18, 2, 0x44), 0x001f01fe, 0x00800001,
-       PCI_ADDR(0, 0x18, 2, 0x48), 0x001f01fe, 0x01000001,
-       PCI_ADDR(0, 0x18, 2, 0x4C), 0x001f01fe, 0x01800001,
-#endif
-#if MEMORY_CONFIG == MEMORY_LNXI_HDAMA
-       PCI_ADDR(0, 0x18, 2, 0x40), 0x001f01fe, 0x00000001,
-       PCI_ADDR(0, 0x18, 2, 0x44), 0x001f01fe, 0x00001001,
-       PCI_ADDR(0, 0x18, 2, 0x48), 0x001f01fe, 0x00000000,
-       PCI_ADDR(0, 0x18, 2, 0x4C), 0x001f01fe, 0x00000000,
-#endif
        PCI_ADDR(0, 0x18, 2, 0x50), 0x001f01fe, 0x00000000,
        PCI_ADDR(0, 0x18, 2, 0x54), 0x001f01fe, 0x00000000,
        PCI_ADDR(0, 0x18, 2, 0x58), 0x001f01fe, 0x00000000,
@@ -698,24 +584,10 @@ static void sdram_set_registers(void)
         * [31:30] Reserved
         * 
         */
-#if MEMORY_CONFIG == MEMORY_LNXI_SOLO
        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,
-#endif
-#if MEMORY_CONFIG == MEMORY_SUSE_SOLO
-       PCI_ADDR(0, 0x18, 2, 0x60), 0xC01f01ff, 0x0060fe00,
-       PCI_ADDR(0, 0x18, 2, 0x64), 0xC01f01ff, 0x0060fe00,
-       PCI_ADDR(0, 0x18, 2, 0x68), 0xC01f01ff, 0x0060fe00,
-       PCI_ADDR(0, 0x18, 2, 0x6C), 0xC01f01ff, 0x0060fe00,
-#endif
-#if MEMORY_CONFIG == MEMORY_LNXI_HDAMA
-       PCI_ADDR(0, 0x18, 2, 0x60), 0xC01f01ff, 0x03e0ee00,
-       PCI_ADDR(0, 0x18, 2, 0x64), 0xC01f01ff, 0x03e0ee00,
-       PCI_ADDR(0, 0x18, 2, 0x68), 0xC01f01ff, 0x00000000,
-       PCI_ADDR(0, 0x18, 2, 0x6C), 0xC01f01ff, 0x00000000,
-#endif
        PCI_ADDR(0, 0x18, 2, 0x70), 0xC01f01ff, 0x00000000,
        PCI_ADDR(0, 0x18, 2, 0x74), 0xC01f01ff, 0x00000000,
        PCI_ADDR(0, 0x18, 2, 0x78), 0xC01f01ff, 0x00000000,
@@ -740,15 +612,7 @@ static void sdram_set_registers(void)
         * [11:11] Reserved
         * [31:15]
         */
-#if MEMORY_CONFIG == MEMORY_LNXI_SOLO
        PCI_ADDR(0, 0x18, 2, 0x80), 0xffff8888, 0x00000000,
-#endif
-#if MEMORY_CONFIG == MEMORY_SUSE_SOLO
-       PCI_ADDR(0, 0x18, 2, 0x80), 0xffff8888, 0x00000022,
-#endif
-#if MEMORY_CONFIG == MEMORY_LNXI_HDAMA
-       PCI_ADDR(0, 0x18, 2, 0x80), 0xffff8888, 0x00000003,
-#endif
        /* DRAM Timing Low Register
         * F2:0x88
         * [ 2: 0] Tcl (Cas# Latency, Cas# to read-data-valid)
@@ -813,19 +677,14 @@ static void sdram_set_registers(void)
         *         1 = 3 bus clocks
         * [31:29] Reserved
         */
-#if (MEMORY_CONFIG == MEMORY_LNXI_SOLO) || (MEMORY_CONFIG == MEMORY_SUSE_SOLO)
-       PCI_ADDR(0, 0x18, 2, 0x88), 0xe8088008, 0x03623125,
-#endif
-#if MEMORY_CONFIG == MEMORY_LNXI_HDAMA
-       PCI_ADDR(0, 0x18, 2, 0x88), 0xe8088008, 0x13723335,
-#endif
+       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] Trwf (Read to Write Delay)
+        * [ 6: 4] Trwt (Read to Write Delay)
         *         000 = 1 bus clocks
         *         001 = 2 bus clocks
         *         010 = 3 bus clocks
@@ -839,24 +698,18 @@ static void sdram_set_registers(void)
         *         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
         */
-#if MEMORY_CONFIG == MEMORY_LNXI_SOLO
-       PCI_ADDR(0, 0x18, 2, 0x8c), 0xff8fe08e, 0x00000930,
-#endif
-#if MEMORY_CONFIG == MEMORY_SUSE_SOLO
-       PCI_ADDR(0, 0x18, 2, 0x8c), 0xff8fe08e, 0x00000130,
-#endif
-#if MEMORY_CONFIG == MEMORY_LNXI_HDAMA
-       PCI_ADDR(0, 0x18, 2, 0x8c), 0xff8fe08e, 0x00100a20,
-#endif
+       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
@@ -927,24 +780,13 @@ static void sdram_set_registers(void)
         *         111 = Oldest entry in DCQ can be bypassed 7 times
         * [31:28] Reserved
         */
-#if (MEMORY_CONFIG == MEMORY_LNXI_SOLO) || (MEMORY_CONFIG == MEMORY_SUSE_SOLO)
        PCI_ADDR(0, 0x18, 2, 0x90), 0xf0000000, 
        (4 << 25)|(0 << 24)| 
        (0 << 23)|(0 << 22)|(0 << 21)|(0 << 20)| 
-       (1 << 19)|(1 << 18)|(0 << 17)|(0 << 16)| 
+       (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),
-#endif
-#if MEMORY_CONFIG == MEMORY_LNXI_HDAMA
-       PCI_ADDR(0, 0x18, 2, 0x90), 0xf0000000, 
-       (4 << 25)|(0 << 24)|
-       (0 << 23)|(0 << 22)|(0 << 21)|(0 << 20)|
-       (0 << 19)|(0 << 18)|(0 << 17)|(1 << 16)|
-       (2 << 14)|(0 << 13)|(0 << 12)|
-       (0 << 11)|(0 << 10)|(0 << 9)|(0 << 8)|
-       (0 << 3) |(0 << 1) |(0 << 0),
-#endif
        /* DRAM Config High Register
         * F2:0x94
         * [ 0: 3] Maximum Asynchronous Latency
@@ -1009,15 +851,9 @@ static void sdram_set_registers(void)
         *         1 = Enabled
         * [31:30] Reserved
         */
-#if MEMORY_CONFIG == MEMORY_LNXI_SOLO
-       PCI_ADDR(0, 0x18, 2, 0x94), 0xc180f0f0, 0x0e2b0a05,
-#endif
-#if MEMORY_CONFIG == MEMORY_SUSE_SOLO
-       PCI_ADDR(0, 0x18, 2, 0x94), 0xc180f0f0, 0x0e2b0a06,
-#endif
-#if MEMORY_CONFIG == MEMORY_LNXI_HDAMA
-       PCI_ADDR(0, 0x18, 2, 0x94), 0xc180f0f0, 0x065b0b08,
-#endif
+       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
@@ -1086,6 +922,9 @@ static void sdram_set_registers(void)
        };
        int i;
        int max;
+#if 1
+       memreset_setup(ctrl);
+#endif
        print_debug("setting up CPU0 northbridge registers\r\n");
        max = sizeof(register_values)/sizeof(register_values[0]);
        for(i = 0; i < max; i += 3) {
@@ -1098,7 +937,7 @@ static void sdram_set_registers(void)
                print_debug_hex32(register_values[i+2]);
                print_debug("\r\n");
 #endif
-               dev = register_values[i] & ~0xff;
+               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];
@@ -1116,14 +955,44 @@ static void sdram_set_registers(void)
 }
 
 
-struct dimm_size {
-       unsigned long side1;
-       unsigned long side2;
-};
-static struct dimm_size spd_get_dimm_size(unsigned device)
+static int is_dual_channel(const struct mem_controller *ctrl)
 {
-       /* Calculate the log base 2 size of a DIMM in bits */
-       struct dimm_size sz;
+       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.
+        */
+       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;
@@ -1133,56 +1002,54 @@ static struct dimm_size spd_get_dimm_size(unsigned device)
         * sides of an assymetric dimm.
         */
        value = smbus_read_byte(device, 3);     /* rows */
-       if (value < 0) return sz;
+       if (value < 0) goto out;
        sz.side1 += value & 0xf;
 
        value = smbus_read_byte(device, 4);     /* columns */
-       if (value < 0) return sz;
+       if (value < 0) goto out;
        sz.side1 += value & 0xf;
 
        value = smbus_read_byte(device, 17);    /* banks */
-       if (value < 0) return sz;
+       if (value < 0) goto out;
        sz.side1 += log2(value & 0xff);
 
-       /* Get the module data widht and convert it to a power of two */
+       /* Get the module data width and convert it to a power of two */
        value = smbus_read_byte(device, 7);     /* (high byte) */
-       if (value < 0) return sz;
+       if (value < 0) goto out;
        value &= 0xff;
        value <<= 8;
        
        low = smbus_read_byte(device, 6);       /* (low byte) */
-       if (low < 0) return sz;
+       if (low < 0) goto out;
        value = value | (low & 0xff);
        sz.side1 += log2(value);
 
        /* side 2 */
        value = smbus_read_byte(device, 5);     /* number of physical banks */
-       if (value <= 1) return sz;
+       if (value <= 1) goto out;
 
        /* Start with the symmetrical case */
        sz.side2 = sz.side1;
 
        value = smbus_read_byte(device, 3);     /* rows */
-       if (value < 0) return sz;
-       if ((value & 0xf0) == 0) return sz;     /* If symmetrical we are done */
+       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 = smbus_read_byte(device, 4);     /* columns */
-       if (value < 0) return sz;
+       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 */
-       return sz;
-}
 
-static unsigned spd_to_dimm(unsigned device)
-{
-       return (device - SMBUS_MEM_DEVICE_START);
+ out:
+       return sz;
 }
 
-static void set_dimm_size(struct dimm_size sz, unsigned index)
+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 1
        print_debug("set_dimm_size: (");
@@ -1196,7 +1063,7 @@ static void set_dimm_size(struct dimm_size sz, unsigned index)
        if (sz.side1 != sz.side2) {
                sz.side2 = 0;
        }
-       map = pci_read_config32(PCI_DEV(0, 0x18, 2), 0x80);
+       map = pci_read_config32(ctrl->f2, DRAM_BANK_ADDR_MAP);
        map &= ~(0xf << (index + 4));
 
        /* For each base register.
@@ -1208,35 +1075,66 @@ static void set_dimm_size(struct dimm_size sz, unsigned index)
        base0 = base1 = 0;
 
        /* Make certain side1 of the dimm is at least 32MB */
-       if (sz.side1 >= (25 + 3)) {
-               base0 = (1 << ((sz.side1 - (25 + 3)) + 21)) | 1;
+       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(PCI_DEV(0, 0x18, 2), 0x40 + (((index << 1)+0)<<2), base0);
-       pci_write_config32(PCI_DEV(0, 0x18, 2), 0x40 + (((index << 1)+1)<<2), base1);
-       pci_write_config32(PCI_DEV(0, 0x18, 2), 0x80, map);
+       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(void)
+static void spd_set_ram_size(const struct mem_controller *ctrl)
 {
-       unsigned device;
-       for(device = SMBUS_MEM_DEVICE_START; 
-               device <= SMBUS_MEM_DEVICE_END;
-               device += SMBUS_MEM_DEVICE_INC) 
-       {
+       int i;
+       
+       for(i = 0; (i < 4) && (ctrl->channel0[i]); i++) {
                struct dimm_size sz;
-               sz = spd_get_dimm_size(device);
-               set_dimm_size(sz, spd_to_dimm(device));
+               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)
+{
+#warning "FIXME this is hardcoded for one cpu"
+       unsigned node_id;
+       unsigned link_id;
+       unsigned limit;
+       node_id = 0;
+       link_id = 0;
+       /* Route the addresses to node 0 */
+       limit = (limit_k << 2);
+       limit &= 0xffff0000;
+       limit -= 0x00010000;
+       pci_write_config32(ctrl->f1, 0x44, limit | (0 << 7) | (link_id << 4) | (node_id << 0));
+       pci_write_config32(ctrl->f1, 0x40, (base_k << 2) | (0 << 8) | (1<<1) | (1<<0));
+}
+
 static void set_top_mem(unsigned tom_k)
 {
        /* Error if I don't have memory */
@@ -1250,16 +1148,16 @@ static void set_top_mem(unsigned tom_k)
        wrmsr(TOP_MEM, msr);
 
 #if 1
-       /* And report the amount of memory.  (I run out of registers if i don't) */
+       /* And report the amount of memory. */
        print_debug("RAM: 0x");
        print_debug_hex32(tom_k);
        print_debug(" KB\r\n");
 #endif
 }
 
-static void order_dimms(void)
+static void order_dimms(const struct mem_controller *ctrl)
 {
-       unsigned long tom;
+       unsigned long tom, tom_k;
 
        /* Remember which registers we have used in the high 8 bits of tom */
        tom = 0;
@@ -1272,7 +1170,7 @@ static void order_dimms(void)
                canidate = 0;
                for(index = 0; index < 8; index++) {
                        uint32_t value;
-                       value = pci_read_config32(PCI_DEV(0, 0x18, 2), 0x40 + (index << 2));
+                       value = pci_read_config32(ctrl->f2, DRAM_CSBASE + (index << 2));
 
                        /* Is it enabled? */
                        if (!(value & 1)) {
@@ -1312,72 +1210,986 @@ static void order_dimms(void)
                /* 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(PCI_DEV(0, 0x18, 2), 0x40 + (canidate << 2), csbase);
+               pci_write_config32(ctrl->f2, DRAM_CSBASE + (canidate << 2), csbase);
                /* Write the new mask register */
-               pci_write_config32(PCI_DEV(0, 0x18, 2), 0x60 + (canidate << 2), csmask);
+               pci_write_config32(ctrl->f2, DRAM_CSMASK + (canidate << 2), csmask);
                
        }
-       set_top_mem((tom & ~0xff000000) << 15);
+       tom_k = (tom & ~0xff000000) << 15;
+#if 1
+       print_debug("tom: ");
+       print_debug_hex32(tom);
+       print_debug(" tom_k: ");
+       print_debug_hex32(tom_k);
+       print_debug("\r\n");
+#endif
+       route_dram_accesses(ctrl, 0, 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 = smbus_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 1
+       if (is_registered(ctrl)) {
+               print_debug("Registered\r\n");
+       } else {
+               print_debug("Unbuffered\r\n");
+       }
+#endif
 }
 
-static void spd_set_dram_timing(void)
+static void spd_enable_2channels(const struct mem_controller *ctrl)
 {
+       int i;
+       uint32_t nbcap;
+       /* SMBUS 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 */
+               12,     /* Refresh Interval */
+               13,     /* SDRAM Width */
+               15,     /* Back-to-Back Random Column Access */
+               16,     /* Burst Lengths */
+               17,     /* Logical Banks */
+               18,     /* Supported CAS Latencies */
+               23,     /* Cycle time at CAS Latnecy (CLX - 0.5) */
+               26,     /* Cycle time at CAS Latnecy (CLX - 1.0) */
+               27,     /* tRP Row precharge time */
+               29,     /* tRCD RAS to CAS */
+               30,     /* tRAS Activate to Precharge */
+               31,     /* Module Bank Density */
+               33,     /* Address and Command Hold Time After Clock */
+       };
+       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 = smbus_read_byte(device0, addr);
+                       if (value0 < 0) {
+                               break;
+                       }
+                       value1 = smbus_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 1
+       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 = smbus_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 = smbus_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 = smbus_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 = smbus_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 1
+       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;
 }
 
-#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_DimmEcEn      (1<<17)
+
+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 = smbus_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 = smbus_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 void spd_set_ecc_mode(void)
+static int update_dimm_Trcd(const struct mem_controller *ctrl, const struct mem_param *param, int i)
 {
-       unsigned long dcl;
-       dcl = pci_read_config32(PCI_DEV(0, 0x18, 2), DRAM_CONFIG_LOW);
-       /* Until I know what is going on disable ECC support */
-       dcl &= ~DCL_DimmEcEn;
-       pci_write_config32(PCI_DEV(0, 0x18, 2), DRAM_CONFIG_LOW, dcl);
+       unsigned clocks, old_clocks;
+       uint32_t dtl;
+       int value;
+       value = smbus_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 = smbus_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 void sdram_set_spd_registers(void) 
+
+static int update_dimm_Tras(const struct mem_controller *ctrl, const struct mem_param *param, int i)
 {
-       spd_set_ram_size();
-       spd_set_dram_timing();
-       spd_set_ecc_mode();
-       order_dimms();
+       unsigned clocks, old_clocks;
+       uint32_t dtl;
+       int value;
+       value = smbus_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 = smbus_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 1
+       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 = smbus_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 = smbus_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 = smbus_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(void)
+static void sdram_enable(const struct mem_controller *ctrl)
 {
-       unsigned long dcl;
+       uint32_t dcl, dch;
+
+       /* Before enabling memory start the memory clocks */
+       dch = pci_read_config32(ctrl->f2, DRAM_CONFIG_HIGH);
+       dch |= DCH_MEMCLK_VALID;
+       pci_write_config32(ctrl->f2, DRAM_CONFIG_HIGH, dch);
+
+       /* And if necessary toggle the the reset on the dimms by hand */
+       memreset(ctrl);
 
        /* Toggle DisDqsHys to get it working */
-       dcl = pci_read_config32(PCI_DEV(0, 0x18, 2), DRAM_CONFIG_LOW);
+       dcl = pci_read_config32(ctrl->f2, DRAM_CONFIG_LOW);
        print_debug("dcl: ");
        print_debug_hex32(dcl);
        print_debug("\r\n");
+
+#warning "FIXME set the ECC type to perform"
+#warning "FIXME initialize the scrub registers"
+#if 1
+       if (dcl & DCL_DimmEccEn) {
+               print_debug("ECC enabled\r\n");
+       }
+#endif
        dcl |= DCL_DisDqsHys;
-       pci_write_config32(PCI_DEV(0, 0x18, 2), DRAM_CONFIG_LOW, dcl);
+       pci_write_config32(ctrl->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(PCI_DEV(0, 0x18, 2), DRAM_CONFIG_LOW, dcl);
+       pci_write_config32(ctrl->f2, DRAM_CONFIG_LOW, dcl);
        
        print_debug("Initializing memory: ");
        int loops = 0;
        do {
-               dcl = pci_read_config32(PCI_DEV(0, 0x18, 2), DRAM_CONFIG_LOW);
+               dcl = pci_read_config32(ctrl->f2, DRAM_CONFIG_LOW);
                loops += 1;
                if ((loops & 1023) == 0) {
                        print_debug(".");
@@ -1390,25 +2202,28 @@ static void sdram_enable(void)
        }
 
 #if 0
-       print_debug("Clearing memory: ");
-       loops = 0;
-       do {
-               dcl = pci_read_config32(PCI_DEV(0, 0x18, 2), DRAM_CONFIG_LOW);
-               loops += 1;
-               if ((loops & 1023) == 0) {
-                       print_debug(" ");
-                       print_debug_hex32(loops);
+
+       if (dcl & DCL_DimmEccEn) {
+               print_debug("Clearing memory: ");
+               loops = 0;
+               dcl &= ~DCL_MemClrStatus;
+               pci_write_config32(ctrl->f2, DRAM_CONFIG_LOW, dcl);
+               
+               do {
+                       dcl = pci_read_config32(ctrl->f2, DRAM_CONFIG_LOW);
+                       loops += 1;
+                       if ((loops & 1023) == 0) {
+                               print_debug(" ");
+                               print_debug_hex32(loops);
+                       }
+               } while(((dcl & DCL_MemClrStatus) == 0) && (loops < TIMEOUT_LOOPS));
+               if (loops >= TIMEOUT_LOOPS) {
+                       print_debug("failed\r\n");
+               } else {
+                       print_debug("done\r\n");
                }
-       } while(((dcl & DCL_MemClrStatus) == 0) && (loops < TIMEOUT_LOOPS));
-       if (loops >= TIMEOUT_LOOPS) {
-               print_debug("failed\r\n");
-       } else {
-               print_debug("done\r\n");
+               pci_write_config32(ctrl->f3, SCRUB_ADDR_LOW, 0);
+               pci_write_config32(ctrl->f3, SCRUB_ADDR_HIGH, 0);
        }
 #endif
 }
-
-static void sdram_first_normal_reference(void) {}
-static void sdram_enable_refresh(void) {}
-static void sdram_special_finishup(void) {}
-