[coreboot] r713 - in coreboot-v3: . arch/x86 include/arch/x86/amd/k8 include/device mainboard/gigabyte mainboard/gigabyte/m57sli northbridge/amd/k8 southbridge southbridge/nvidia southbridge/nvidia/mcp55

svn at coreboot.org svn at coreboot.org
Sat Aug 2 05:34:05 CEST 2008


Author: stuge
Date: 2008-08-02 05:34:05 +0200 (Sat, 02 Aug 2008)
New Revision: 713

Added:
   coreboot-v3/include/arch/x86/amd/k8/raminit.h
   coreboot-v3/include/arch/x86/amd/k8/sysconf.h
   coreboot-v3/northbridge/amd/k8/Makefile
   coreboot-v3/northbridge/amd/k8/pci
   coreboot-v3/northbridge/amd/k8/raminit.c
   coreboot-v3/southbridge/nvidia/
   coreboot-v3/southbridge/nvidia/mcp55/
   coreboot-v3/southbridge/nvidia/mcp55/Makefile
   coreboot-v3/southbridge/nvidia/mcp55/dts
   coreboot-v3/southbridge/nvidia/mcp55/mcp55.c
   coreboot-v3/southbridge/nvidia/mcp55/mcp55.h
   coreboot-v3/southbridge/nvidia/mcp55/stage1.c
Modified:
   coreboot-v3/Kconfig
   coreboot-v3/arch/x86/Makefile
   coreboot-v3/include/arch/x86/amd/k8/k8.h
   coreboot-v3/include/device/pci_def.h
   coreboot-v3/include/device/pci_ids.h
   coreboot-v3/mainboard/gigabyte/Kconfig
   coreboot-v3/mainboard/gigabyte/m57sli/dts
Log:
v3: k8/m57sli wip1

This is not nearly complete, but just the current state of my tree.

k8/raminit.c does not compile at all. Lots of fixes are still needed to bring
it working into v3. I've gone through about 1/8 of the file, it errors out on
line 576 now.

The mcp55 files are in a very early state and also do not compile for me, so
I've disabled them by commenting out the select in mainboard/gigabyte/Kconfig.

Once northbridge/amd/k8/raminit.c builds, k8_ops needs to be added, then we
may actually see the first v3 k8 build. :)

Signed-off-by: Peter Stuge <peter at stuge.se>
Acked-by: Ronald G. Minnich <rminnich at gmail.com>


Modified: coreboot-v3/Kconfig
===================================================================
--- coreboot-v3/Kconfig	2008-08-02 03:29:02 UTC (rev 712)
+++ coreboot-v3/Kconfig	2008-08-02 03:34:05 UTC (rev 713)
@@ -72,6 +72,8 @@
 # Northbridges:
 config NORTHBRIDGE_AMD_GEODELX
 	boolean
+config NORTHBRIDGE_AMD_K8
+	boolean
 config NORTHBRIDGE_INTEL_I440BXEMULATION
 	boolean
 
@@ -80,6 +82,8 @@
 	boolean
 config SOUTHBRIDGE_INTEL_I82371EB
 	boolean
+config SOUTHBRIDGE_NVIDIA_MCP55
+	boolean
 
 # Super I/Os:
 config SUPERIO_WINBOND_W83627HF

Modified: coreboot-v3/arch/x86/Makefile
===================================================================
--- coreboot-v3/arch/x86/Makefile	2008-08-02 03:29:02 UTC (rev 712)
+++ coreboot-v3/arch/x86/Makefile	2008-08-02 03:34:05 UTC (rev 713)
@@ -248,10 +248,13 @@
 	$(Q)printf "  AS      $(subst $(shell pwd)/,,$(@))\n"
 	$(Q)$(AS) $(obj)/arch/x86/stage0_asm.s -o $@
 
+
+# NOTE HACK. Stefan will fix this :-)
 $(obj)/arch/x86/amd/stage0.o: $(src)/arch/x86/amd/stage0.S
 	$(Q)mkdir -p $(dir $@)
 	$(Q)printf "  CC      $(subst $(shell pwd)/,,$(@))\n"
 	$(Q)$(CC) -E $(COREBOOTINCLUDE) $< \
+		-I $(src)/include/arch/x86/amd/k8 \
 		-o $(obj)/arch/x86/stage0_asm.s -DBOOTBLK=0x1f00 \
 		-DRESRVED=0xf0 -DDATE=\"`date +%Y/%m/%d`\"
 	$(Q)printf "  AS      $(subst $(shell pwd)/,,$(@))\n"

Modified: coreboot-v3/include/arch/x86/amd/k8/k8.h
===================================================================
--- coreboot-v3/include/arch/x86/amd/k8/k8.h	2008-08-02 03:29:02 UTC (rev 712)
+++ coreboot-v3/include/arch/x86/amd/k8/k8.h	2008-08-02 03:34:05 UTC (rev 713)
@@ -54,3 +54,231 @@
 #define TOP_MEM_MASK_KB			(TOP_MEM_MASK >> 10)
 
 
+/* Definitions of various K8 registers */
+/* Function 0 */
+#define HT_TRANSACTION_CONTROL 0x68
+#define  HTTC_DIS_RD_B_P            (1 << 0)
+#define  HTTC_DIS_RD_DW_P           (1 << 1)
+#define  HTTC_DIS_WR_B_P            (1 << 2)
+#define  HTTC_DIS_WR_DW_P           (1 << 3)
+#define  HTTC_DIS_MTS               (1 << 4)
+#define  HTTC_CPU1_EN               (1 << 5)
+#define  HTTC_CPU_REQ_PASS_PW       (1 << 6)
+#define  HTTC_CPU_RD_RSP_PASS_PW    (1 << 7)
+#define  HTTC_DIS_P_MEM_C           (1 << 8)
+#define  HTTC_DIS_RMT_MEM_C         (1 << 9)
+#define  HTTC_DIS_FILL_P            (1 << 10)
+#define  HTTC_RSP_PASS_PW           (1 << 11)
+#define  HTTC_CHG_ISOC_TO_ORD       (1 << 12)
+#define  HTTC_BUF_REL_PRI_SHIFT     13
+#define  HTTC_BUF_REL_PRI_MASK      3
+#define   HTTC_BUF_REL_PRI_64       0
+#define   HTTC_BUF_REL_PRI_16       1
+#define   HTTC_BUF_REL_PRI_8        2
+#define   HTTC_BUF_REL_PRI_2        3
+#define  HTTC_LIMIT_CLDT_CFG        (1 << 15)
+#define  HTTC_LINT_EN               (1 << 16)
+#define  HTTC_APIC_EXT_BRD_CST      (1 << 17)
+#define  HTTC_APIC_EXT_ID           (1 << 18)
+#define  HTTC_APIC_EXT_SPUR         (1 << 19)
+#define  HTTC_SEQ_ID_SRC_NODE_EN    (1 << 20)
+#define  HTTC_DS_NP_REQ_LIMIT_SHIFT 21
+#define  HTTC_DS_NP_REQ_LIMIT_MASK  3
+#define   HTTC_DS_NP_REQ_LIMIT_NONE 0
+#define   HTTC_DS_NP_REQ_LIMIT_1    1
+#define   HTTC_DS_NP_REQ_LIMIT_4    2
+#define   HTTC_DS_NP_REQ_LIMIT_8    3
+#define  HTTC_MED_PRI_BYP_CNT_SHIFT 24
+#define  HTTC_MED_PRI_BYP_CNT_MASK  3
+#define  HTTC_HI_PRI_BYP_CNT_SHIFT  26
+#define  HTTC_HI_PRI_BYP_CNT_MASK   3
+
+
+/* Function 1 */
+#define PCI_IO_BASE0       0xc0
+#define PCI_IO_BASE1       0xc8
+#define PCI_IO_BASE2       0xd0
+#define PCI_IO_BASE3       0xd8
+#define PCI_IO_BASE_VGA_EN (1 << 4)
+#define PCI_IO_BASE_NO_ISA (1 << 5)
+
+
+/* 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_Burst2Opt     (1<<5)
+#define	 DCL_DramInit	   (1<<8)
+#define	 DCL_DualDIMMen    (1<<9)
+#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	 DCL_DisInRcvrs    (1<<24)
+#define	 DCL_BypMax_SHIFT  25
+#define	 DCL_En2T          (1<<28)
+#define	 DCL_UpperCSMap    (1<<29)
+	
+#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	      (1 << 0)
+#define	 NBCAP_MP	      (1 << 1)
+#define	 NBCAP_BIG_MP	      (1 << 2)
+#define	 NBCAP_ECC	      (1 << 3)
+#define	 NBCAP_CHIPKILL_ECC   (1 << 4)
+#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	      (1 << 8)

Added: coreboot-v3/include/arch/x86/amd/k8/raminit.h
===================================================================
--- coreboot-v3/include/arch/x86/amd/k8/raminit.h	                        (rev 0)
+++ coreboot-v3/include/arch/x86/amd/k8/raminit.h	2008-08-02 03:34:05 UTC (rev 713)
@@ -0,0 +1,16 @@
+#ifndef RAMINIT_H
+#define RAMINIT_H
+
+#include <device/device.h>
+
+#define NODE_NUMS 8
+
+#define DIMM_SOCKETS 4
+struct mem_controller {
+	unsigned node_id;
+	struct device *f0, *f1, *f2, *f3;
+	u16 channel0[DIMM_SOCKETS];
+	u16 channel1[DIMM_SOCKETS];
+};
+
+#endif /* RAMINIT_H */

Added: coreboot-v3/include/arch/x86/amd/k8/sysconf.h
===================================================================
--- coreboot-v3/include/arch/x86/amd/k8/sysconf.h	                        (rev 0)
+++ coreboot-v3/include/arch/x86/amd/k8/sysconf.h	2008-08-02 03:34:05 UTC (rev 713)
@@ -0,0 +1,47 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2006 Advanced Micro Devices, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+ */
+#ifndef AMD_K8_SYSCONF_H
+#define AMD_K8_SYSCONF_H
+
+#define HC_POSSIBLE_NUM 8
+
+struct amdk8_sysconf_t {
+	//ht
+	unsigned nodes;
+	unsigned hc_possible_num;
+	unsigned pci1234[HC_POSSIBLE_NUM];
+	unsigned hcdn[HC_POSSIBLE_NUM];
+	unsigned hcid[HC_POSSIBLE_NUM]; //record ht chain type
+	unsigned sbdn;
+	unsigned sblk;
+
+	unsigned hcdn_reg[4]; // it will be used by get_sblk_pci1234
+
+	int enabled_apic_ext_id;
+	unsigned lift_bsp_apicid;
+	int apicid_offset;
+
+	void *mb; // pointer for mb releated struct
+	
+};
+
+extern struct amdk8_sysconf_t sysconf;
+
+#endif

Modified: coreboot-v3/include/device/pci_def.h
===================================================================
--- coreboot-v3/include/device/pci_def.h	2008-08-02 03:29:02 UTC (rev 712)
+++ coreboot-v3/include/device/pci_def.h	2008-08-02 03:34:05 UTC (rev 713)
@@ -481,5 +481,6 @@
 #define PCI_SLOT(devfn)		(((devfn) >> 3) & 0x1f)
 #define PCI_FUNC(devfn)		((devfn) & 0x07)
 #define PCI_BDF(bus,dev,func)	((bus) << 16 | (dev) << 11 | (func) << 8)
+#define PCI_ADDR(bus,dev,func,where) (PCI_BDF((bus),(dev),(func)) << 4 | (where & 0xfff))
 
 #endif /* DEVICE_PCI_DEF_H */

Modified: coreboot-v3/include/device/pci_ids.h
===================================================================
--- coreboot-v3/include/device/pci_ids.h	2008-08-02 03:29:02 UTC (rev 712)
+++ coreboot-v3/include/device/pci_ids.h	2008-08-02 03:34:05 UTC (rev 713)
@@ -155,4 +155,33 @@
 #define PCI_VENDOR_ID_CIRRUS			0x1013
 #define PCI_DEVICE_ID_CIRRUS_5446		0x00b8	/* Used by QEMU */
 
+#define PCI_VENDIR_ID_NVIDIA                    0x10de
+/*
+0360MCP55 LPC Bridge
+0361MCP55 LPC Bridge
+0362MCP55 LPC Bridge
+0363MCP55 LPC Bridge
+0364MCP55 LPC Bridge
+0365MCP55 LPC Bridge
+0366MCP55 LPC Bridge
+0367MCP55 LPC Bridge
+0368MCP55 SMBus
+0369MCP55 Memory Controller
+036aMCP55 Memory Controller
+036bMCP55 SMU
+036cMCP55 USB Controller
+036dMCP55 USB Controller
+036eMCP55 IDE
+0370MCP55 PCI bridge
+0371MCP55 High Definition Audio
+0372MCP55 Ethernet
+0373MCP55 Ethernet
+0374MCP55 PCI Express bridge
+0375MCP55 PCI Express bridge
+0376MCP55 PCI Express bridge
+0377MCP55 PCI Express bridge
+0378MCP55 PCI Express bridge
+037aMCP55 Memory Controller
+*/
+#define PCI_DEVICE_ID_NVIDIA_MCP55_PCIBRIDGE 0x370
 #endif /* DEVICE_PCI_IDS_H */

Modified: coreboot-v3/mainboard/gigabyte/Kconfig
===================================================================
--- coreboot-v3/mainboard/gigabyte/Kconfig	2008-08-02 03:29:02 UTC (rev 712)
+++ coreboot-v3/mainboard/gigabyte/Kconfig	2008-08-02 03:34:05 UTC (rev 713)
@@ -28,6 +28,9 @@
 	select ARCH_X86
 	select OPTION_TABLE
 	select CPU_AMD_K8
+	select NORTHBRIDGE_AMD_K8
+#	select SOUTHBRIDGE_NVIDIA_MCP55
+	select SUPERIO_ITE_IT8716F
 	help
 	  Gigabyte M57SLI
 

Modified: coreboot-v3/mainboard/gigabyte/m57sli/dts
===================================================================
--- coreboot-v3/mainboard/gigabyte/m57sli/dts	2008-08-02 03:29:02 UTC (rev 712)
+++ coreboot-v3/mainboard/gigabyte/m57sli/dts	2008-08-02 03:34:05 UTC (rev 713)
@@ -25,5 +25,8 @@
 	apic at 0 {
 	};
 	domain at 0 {
+		pci at 18,0 {
+			/config/("northbridge/amd/k8/pci");
+		};
 	};
 };

Added: coreboot-v3/northbridge/amd/k8/Makefile
===================================================================
--- coreboot-v3/northbridge/amd/k8/Makefile	                        (rev 0)
+++ coreboot-v3/northbridge/amd/k8/Makefile	2008-08-02 03:34:05 UTC (rev 713)
@@ -0,0 +1,26 @@
+##
+## This file is part of the coreboot project.
+##
+## Copyright (C) 2007 coresystems GmbH
+## (Written by Stefan Reinauer <stepan at coresystems.de> for coresystems GmbH)
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, write to the Free Software
+## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+##
+
+ifeq ($(CONFIG_NORTHBRIDGE_AMD_K8),y)
+
+STAGE2_CHIPSET_OBJ += $(obj)/northbridge/amd/k8/raminit.o
+
+endif

Added: coreboot-v3/northbridge/amd/k8/pci
===================================================================
--- coreboot-v3/northbridge/amd/k8/pci	                        (rev 0)
+++ coreboot-v3/northbridge/amd/k8/pci	2008-08-02 03:34:05 UTC (rev 713)
@@ -0,0 +1,23 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2008 Ronald G. Minnich <rminnich at gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+ */
+
+{
+	device_operations = "k8_ops";
+};

Added: coreboot-v3/northbridge/amd/k8/raminit.c
===================================================================
--- coreboot-v3/northbridge/amd/k8/raminit.c	                        (rev 0)
+++ coreboot-v3/northbridge/amd/k8/raminit.c	2008-08-02 03:34:05 UTC (rev 713)
@@ -0,0 +1,4756 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2002 Linux Networx
+ * (Written by Eric Biederman <ebiederman at lnxi.com> for Linux Networx)
+ * Copyright (C) 2004 YingHai Lu
+ * Copyright (C) 2007 Ronald G. Minnich <rminnich at gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA, 02110-1301 USA
+ */
+/*	This should be done by Eric
+	2004.11 yhlu add 4 rank DIMM support
+	2004.12 yhlu add D0 support
+	2005.02 yhlu add E0 memory hole support
+*/
+/* not yet 
+#if K8_REV_F_SUPPORT == 1
+        #include "raminit_f.c"
+#else
+ */
+
+#include <console.h>
+#include <mtrr.h>
+#include <amd/k8/raminit.h>
+#include <amd/k8/k8.h>
+#include <amd/k8/sysconf.h>
+#include <device/pci.h>
+
+#ifndef QRANK_DIMM_SUPPORT
+#define QRANK_DIMM_SUPPORT 0
+#endif
+
+static void hard_reset(void);
+
+static void setup_resource_map(const unsigned int *register_values, int max)
+{
+	printk(BIOS_DEBUG, "setting up resource map....");
+/*
+	int i;
+	for(i = 0; i < max; i += 3) {
+		struct device *dev;
+		unsigned where;
+		unsigned long reg;
+		printk(BIOS_DEBUG, "%08x <- %08x\r\n", register_values[i], register_values[i+2]);
+		dev = register_values[i] & ~0xfff;
+		where = register_values[i] & 0xfff;
+		reg = pci_read_config32(dev, where);
+		reg &= register_values[i+1];
+		reg |= register_values[i+2];
+		pci_write_config32(dev, where, reg);
+	}
+*/
+	printk(BIOS_DEBUG, "done.\r\n");
+}
+
+static int controller_present(const struct mem_controller *ctrl)
+{
+        return pci_read_config32(ctrl->f0, 0) == 0x11001022;
+}
+
+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,
+	/* MCA NB Status Low reg */
+	PCI_ADDR(0, 0x18, 3, 0x48), 0x00f00000, 0x00000000,
+	/* MCA NB Status high reg */
+	PCI_ADDR(0, 0x18, 3, 0x4c), 0x01801e8c, 0x00000000,
+	/* MCA NB address Low reg */
+	PCI_ADDR(0, 0x18, 3, 0x50), 0x00000007, 0x00000000,
+	/* MCA NB address high reg */
+	PCI_ADDR(0, 0x18, 3, 0x54), 0xffffff00, 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,
+	};
+	int i;
+	int max;
+
+        if (!controller_present(ctrl)) {
+                printk(BIOS_DEBUG, "No memory controller present\r\n");
+                return;
+        }
+	printk(BIOS_SPEW, "setting up CPU 0x%x northbridge registers ", ctrl->node_id);
+	max = sizeof(register_values)/sizeof(register_values[0]);
+	for(i = 0; i < max; i += 3) {
+		struct device *dev;
+		unsigned where;
+		unsigned long reg;
+                printk(BIOS_DEBUG, "%08x <- %08x\r\n", register_values[i], register_values[i+2]);
+		dev = (register_values[i] & ~0xfff) - PCI_BDF(0, 0x18, 0) + ctrl->f0;
+		where = register_values[i] & 0xfff;
+		reg = pci_read_config32(dev, where);
+		reg &= register_values[i+1];
+		reg |= register_values[i+2];
+		pci_write_config32(dev, where, reg);
+	}
+	printk(BIOS_SPEW, "done.\r\n");
+}
+
+
+static void hw_enable_ecc(const struct mem_controller *ctrl)
+{
+	u32 dcl, nbcap;
+	nbcap = pci_read_config32(ctrl->f3, NORTHBRIDGE_CAP);
+	dcl = pci_read_config32(ctrl->f2, DRAM_CONFIG_LOW);
+	dcl &= ~DCL_DimmEccEn;
+	if (nbcap & NBCAP_ECC) {
+		dcl |= DCL_DimmEccEn;
+	}
+	if (read_option(CMOS_VSTART_ECC_memory, CMOS_VLEN_ECC_memory, 1) == 0) {
+		dcl &= ~DCL_DimmEccEn;
+	}
+	pci_write_config32(ctrl->f2, DRAM_CONFIG_LOW, dcl);
+	
+}
+
+static int is_dual_channel(const struct mem_controller *ctrl)
+{
+	u32 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 Socket 939 based Athlon64 have dual channel capability,
+	 * too, so we need a better test for Opterons
+	 */
+#warning "FIXME: Implement a better test for Opterons"
+	u32 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.
+	 */
+	u32 dcl;
+	dcl = pci_read_config32(ctrl->f2, DRAM_CONFIG_LOW);
+	return !(dcl & DCL_UnBufDimm);
+}
+
+struct dimm_size {
+	unsigned long side1;
+	unsigned long side2;
+	unsigned long rows;
+	unsigned long col;
+#if QRANK_DIMM_SUPPORT == 1
+	unsigned long rank;
+#endif
+};
+
+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;
+	sz.rows = 0;
+	sz.col = 0;
+#if QRANK_DIMM_SUPPORT == 1
+	sz.rank = 0;
+#endif
+
+	/* 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 hw_err;
+	if ((value & 0xf) == 0) goto val_err;
+	sz.side1 += value & 0xf;
+	sz.rows = value & 0xf;
+
+	value = spd_read_byte(device, 4);	/* columns */
+	if (value < 0) goto hw_err;
+	if ((value & 0xf) == 0) goto val_err;
+	sz.side1 += value & 0xf;
+	sz.col = value & 0xf;
+
+	value = spd_read_byte(device, 17);	/* banks */
+	if (value < 0) goto hw_err;
+	if ((value & 0xff) == 0) goto val_err;
+	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 hw_err;
+	value &= 0xff;
+	value <<= 8;
+	
+	low = spd_read_byte(device, 6);	/* (low byte) */
+	if (low < 0) goto hw_err;
+	value = value | (low & 0xff);
+	if ((value != 72) && (value != 64)) goto val_err;
+	sz.side1 += log2(value);
+
+	/* side 2 */
+	value = spd_read_byte(device, 5);	/* number of physical banks */
+	if (value < 0) goto hw_err;
+	if (value == 1) goto out;
+	if ((value != 2) && (value != 4 )) {
+		goto val_err;
+	}
+#if QRANK_DIMM_SUPPORT == 1
+	sz.rank = value;
+#endif
+
+	/* Start with the symmetrical case */
+	sz.side2 = sz.side1;
+
+	value = spd_read_byte(device, 3);	/* rows */
+	if (value < 0) goto hw_err;
+	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 hw_err;
+	if ((value & 0xff) == 0) goto val_err;
+	sz.side2 -= (value & 0x0f);		/* Subtract out columns on side 1 */
+	sz.side2 += ((value >> 4) & 0x0f);	/* Add in columsn on side 2 */
+
+	goto out;
+
+ val_err:
+	die("Bad SPD value\r\n");
+	/* If an hw_error occurs report that I have no memory */
+hw_err:
+	sz.side1 = 0;
+	sz.side2 = 0;
+	sz.rows = 0;
+	sz.col = 0;
+#if QRANK_DIMM_SUPPORT == 1
+	sz.rank = 0;
+#endif
+ out:
+	return sz;
+}
+
+
+static void set_dimm_size(const struct mem_controller *ctrl, struct dimm_size sz, unsigned index)
+{
+	u32 base0, base1;
+	u32 dch;
+
+	if (sz.side1 != sz.side2) {
+		sz.side2 = 0;
+	}
+	
+	/* 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)) {
+		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);
+#if QRANK_DIMM_SUPPORT == 1
+	if(sz.rank == 4) {
+		pci_write_config32(ctrl->f2, DRAM_CSBASE + (((index << 1)+4)<<2), base0);
+		pci_write_config32(ctrl->f2, DRAM_CSBASE + (((index << 1)+5)<<2), base1);
+	}
+#endif
+
+	/* Enable the memory clocks for this DIMM */
+	if (base0) {
+		dch = pci_read_config32(ctrl->f2, DRAM_CONFIG_HIGH);
+		dch |= DCH_MEMCLK_EN0 << index;
+#if QRANK_DIMM_SUPPORT == 1
+		if(sz.rank == 4) {
+			dch |= DCH_MEMCLK_EN0 << (index + 2);
+		}
+#endif
+		pci_write_config32(ctrl->f2, DRAM_CONFIG_HIGH, dch);
+	}
+}
+
+static void set_dimm_map(const struct mem_controller *ctrl, struct dimm_size sz, unsigned index)
+{
+	static const unsigned cs_map_aa[] = {
+		/* (row=12, col=8)(14, 12) ---> (0, 0) (2, 4) */
+		0, 1, 3, 6, 0,
+		0, 2, 4, 7, 9,
+		0, 0, 5, 8,10,
+	};
+
+	u32 map;
+	u32 dch;
+
+	map = pci_read_config32(ctrl->f2, DRAM_BANK_ADDR_MAP);
+	map &= ~(0xf << (index * 4));
+#if QRANK_DIMM_SUPPORT == 1
+        if(sz.rank == 4) {
+                map &= ~(0xf << ( (index + 2) * 4));
+        }
+#endif
+
+
+	/* Make certain side1 of the dimm is at least 32MB */
+	if (sz.side1 >= (25 +3)) {
+		if(is_cpu_pre_d0()) {
+			map |= (sz.side1 - (25 + 3)) << (index *4);
+#if QRANK_DIMM_SUPPORT == 1
+	                if(sz.rank == 4) {
+         	              map |= (sz.side1 - (25 + 3)) << ( (index + 2) * 4);
+               		}
+#endif
+		}
+		else {
+			map |= cs_map_aa[(sz.rows - 12) * 5 + (sz.col - 8) ] << (index*4);
+#if QRANK_DIMM_SUPPORT == 1
+		        if(sz.rank == 4) {
+                	       map |=  cs_map_aa[(sz.rows - 12) * 5 + (sz.col - 8) ] << ( (index + 2) * 4);
+               		}
+#endif
+		}
+	}
+
+	pci_write_config32(ctrl->f2, DRAM_BANK_ADDR_MAP, map);
+	
+}
+
+static long spd_set_ram_size(const struct mem_controller *ctrl, long dimm_mask)
+{
+	int i;
+	
+	for(i = 0; i < DIMM_SOCKETS; i++) {
+		struct dimm_size sz;
+		if (!(dimm_mask & (1 << i))) {
+			continue;
+		}
+		sz = spd_get_dimm_size(ctrl->channel0[i]);
+		if (sz.side1 == 0) {
+			return -1; /* Report SPD error */
+		}
+		set_dimm_size(ctrl, sz, i);
+		set_dimm_map (ctrl, sz, i);
+	}
+	return dimm_mask;
+}
+
+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;
+	struct device *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, unsigned hole_startk)
+{
+	/* Error if I don't have memory */
+	if (!tom_k) {
+		die("No memory?");
+	}
+
+	/* Report the amount of memory. */
+	print_spew("RAM: 0x");
+	print_spew_hex32(tom_k);
+	print_spew(" KB\r\n");
+
+	/* Now set top of memory */
+	msr_t msr;
+	if(tom_k > (4*1024*1024)) {
+		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) {
+#if HW_MEM_HOLE_SIZEK != 0
+                if(hole_startk != 0) {
+                        tom_k = hole_startk;
+                } else
+#endif
+                tom_k = 0x3f0000;
+	}
+	msr.lo = (tom_k & 0x003fffff) << 10;
+	msr.hi = (tom_k & 0xffc00000) >> 22;
+	wrmsr(TOP_MEM, msr);
+}
+
+static unsigned long interleave_chip_selects(const struct mem_controller *ctrl)
+{
+	/* 35 - 25 */
+	static const u8 csbase_low_shift[] = { 
+	/* 32MB */	(13 - 4),
+	/* 64MB */	(14 - 4),
+	/* 128MB */	(14 - 4), 
+	/* 256MB */	(15 - 4),
+	/* 512MB */	(15 - 4),
+	/* 1GB */	(16 - 4),
+	/* 2GB */	(16 - 4), 
+	};
+
+        static const u8 csbase_low_d0_shift[] = {
+        /* 32MB */      (13 - 4),
+        /* 64MB */      (14 - 4),
+        /* 128MB */     (14 - 4),
+	/* 128MB */     (15 - 4),
+        /* 256MB */     (15 - 4),
+        /* 512MB */     (15 - 4),
+        /* 256MB */     (16 - 4),
+        /* 512MB */     (16 - 4),
+        /* 1GB */       (16 - 4),
+	/* 1GB */       (17 - 4),
+        /* 2GB */       (17 - 4),
+        };
+
+	/* cs_base_high is not changed */
+
+	u32 csbase_inc;
+	int chip_selects, index;
+	int bits;
+	unsigned common_size;
+	unsigned common_cs_mode;
+	u32 csbase, csmask;
+
+	/* See if all of the memory chip selects are the same size
+	 * and if so count them.
+	 */
+	chip_selects = 0;
+	common_size = 0;
+	common_cs_mode = 0;
+	for(index = 0; index < 8; index++) {
+		unsigned size;
+		unsigned cs_mode;
+		u32 value;
+		
+		value = pci_read_config32(ctrl->f2, DRAM_CSBASE + (index << 2));
+		
+		/* Is it enabled? */
+		if (!(value & 1)) {
+			continue;
+		}
+		chip_selects++;
+		size = value >> 21;
+		if (common_size == 0) {
+			common_size = size;
+		}
+		/* The size differed fail */
+		if (common_size != size) {
+			return 0;
+		}
+
+		value = pci_read_config32(ctrl->f2, DRAM_BANK_ADDR_MAP);
+                cs_mode =( value >> ((index>>1)*4)) & 0xf;
+                if(cs_mode == 0 ) continue;
+                if(common_cs_mode == 0) {
+                	common_cs_mode = cs_mode;
+                }
+                /* The size differed fail */
+                if(common_cs_mode != cs_mode) {
+                        return 0;
+                }
+	}
+
+	/* Chip selects can only be interleaved when there is
+	 * more than one and their is a power of two of them.
+	 */
+	bits = log2(chip_selects);
+	if (((1 << bits) != chip_selects) || (bits < 1) || (bits > 3)) {
+		return 0;
+	}
+
+	/* Find the bits of csbase that we need to interleave on */
+	if(is_cpu_pre_d0()){
+		csbase_inc = 1 << csbase_low_shift[common_cs_mode];
+		if(is_dual_channel(ctrl)) {
+                /* Also we run out of address mask bits if we try and interleave 8 4GB dimms */
+	                if ((bits == 3) && (common_size == (1 << (32 - 3)))) {
+//     		                printk(BIOS_DEBUG, "8 4GB chip selects cannot be interleaved\r\n");
+	                        return 0;
+                	}  
+			csbase_inc <<=1;
+		}
+	}
+	else {
+		csbase_inc = 1 << csbase_low_d0_shift[common_cs_mode];
+		if(is_dual_channel(ctrl)) {
+	                if( (bits==3) && (common_cs_mode > 8)) {
+//        	                printk(BIOS_DEBUG, "8 cs_mode>8 chip selects cannot be interleaved\r\n");
+        	                return 0;
+			}
+			csbase_inc <<=1;
+                }   
+	}
+
+	/* Compute the initial values for csbase and csbask. 
+	 * In csbase just set the enable bit and the base to zero.
+	 * In csmask set the mask bits for the size and page level interleave.
+	 */
+	csbase = 0 | 1;
+	csmask = (((common_size  << bits) - 1) << 21);
+	csmask |= 0xfe00 & ~((csbase_inc << bits) - csbase_inc);
+	for(index = 0; index < 8; index++) {
+		u32 value;
+
+		value = pci_read_config32(ctrl->f2, DRAM_CSBASE + (index << 2));
+		/* Is it enabled? */
+		if (!(value & 1)) {
+			continue;
+		}
+		pci_write_config32(ctrl->f2, DRAM_CSBASE + (index << 2), csbase);
+		pci_write_config32(ctrl->f2, DRAM_CSMASK + (index << 2), csmask);
+		csbase += csbase_inc;
+	}
+	
+	printk(BIOS_SPEW, "Interleaved\n");
+
+	/* Return the memory size in K */
+	return common_size << (15 + bits);
+}
+
+static unsigned long order_chip_selects(const struct mem_controller *ctrl)
+{
+	unsigned long tom;
+
+	/* Remember which registers we have used in the high 8 bits of tom */
+	tom = 0;
+	for(;;) {
+		/* Find the largest remaining canidate */
+		unsigned index, canidate;
+		u32 csbase, csmask;
+		unsigned size;
+		csbase = 0;
+		canidate = 0;
+		for(index = 0; index < 8; index++) {
+			u32 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 */
+
+		/* 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);
+		
+	}
+	/* Return the memory size in K */
+	return (tom & ~0xff000000) << 15;
+}
+
+unsigned long memory_end_k(const struct mem_controller *ctrl, int max_node_id)
+{
+	unsigned node_id;
+	unsigned end_k;
+	/* Find the last memory address used */
+	end_k = 0;
+	for(node_id = 0; node_id < max_node_id; node_id++) {
+		u32 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);
+			end_k = ((limit + 0x00010000) & 0xffff0000) >> 2;
+		}
+	}
+	return end_k;
+}
+
+static void order_dimms(const struct mem_controller *ctrl)
+{
+	unsigned long tom_k, base_k;
+
+	if (read_option(CMOS_VSTART_interleave_chip_selects, CMOS_VLEN_interleave_chip_selects, 1) != 0) {
+		tom_k = interleave_chip_selects(ctrl);
+	} else {
+		printk(BIOS_DEBUG, "Interleaving disabled\r\n");
+		tom_k = 0;
+	}
+	if (!tom_k) {
+		tom_k = order_chip_selects(ctrl);
+	}
+	/* Compute the memory base address */
+	base_k = memory_end_k(ctrl, ctrl->node_id);
+	tom_k += base_k;
+	route_dram_accesses(ctrl, base_k, tom_k);
+	set_top_mem(tom_k, 0);
+}
+
+static long disable_dimm(const struct mem_controller *ctrl, unsigned index, long dimm_mask)
+{
+	printk(BIOS_DEBUG, "disabling dimm 0x%x\n", index); 
+	pci_write_config32(ctrl->f2, DRAM_CSBASE + (((index << 1)+0)<<2), 0);
+	pci_write_config32(ctrl->f2, DRAM_CSBASE + (((index << 1)+1)<<2), 0);
+	dimm_mask &= ~(1 << index);
+	return dimm_mask;
+}
+
+static long spd_handle_unbuffered_dimms(const struct mem_controller *ctrl, long dimm_mask)
+{
+	int i;
+	int registered;
+	int unbuffered;
+	int has_dualch = is_opteron(ctrl);
+	u32 dcl;
+	unbuffered = 0;
+	registered = 0;
+	for(i = 0; (i < DIMM_SOCKETS); i++) {
+		int value;
+		if (!(dimm_mask & (1 << i))) {
+			continue;
+		}
+		value = spd_read_byte(ctrl->channel0[i], 21);
+		if (value < 0) {
+			return -1;
+		}
+		/* 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");
+	}
+
+	dcl = pci_read_config32(ctrl->f2, DRAM_CONFIG_LOW);
+	dcl &= ~DCL_UnBufDimm;
+	if (unbuffered) {
+		if ((has_dualch) && (!is_cpu_pre_d0())) {
+			dcl |= DCL_UnBufDimm; /* set DCL_DualDIMMen too? */
+			
+			/* set DCL_En2T if you have non-equal DDR mem types! */
+			
+			if ((cpuid_eax(1) & 0x30) == 0x30) {
+				/* CS[7:4] is copy of CS[3:0], should be set for 939 socket */
+				dcl |= DCL_UpperCSMap;
+			}
+		} else {
+			dcl |= DCL_UnBufDimm;
+		}
+	}
+	pci_write_config32(ctrl->f2, DRAM_CONFIG_LOW, dcl);
+	if (is_registered(ctrl)) {
+		printk(BIOS_DEBUG, "Registered\r\n");
+	} else {
+		printk(BIOS_DEBUG, "Unbuffered\r\n");
+	}
+	return dimm_mask;
+}
+
+static unsigned int spd_detect_dimms(const struct mem_controller *ctrl)
+{
+	unsigned dimm_mask;
+	int i;
+	dimm_mask = 0;
+	for(i = 0; i < DIMM_SOCKETS; i++) {
+		int byte;
+		unsigned device;
+		device = ctrl->channel0[i];
+		if (device) {
+			byte = spd_read_byte(ctrl->channel0[i], 2);  /* Type */
+			if (byte == 7) {
+				dimm_mask |= (1 << i);
+			}
+		}
+		device = ctrl->channel1[i];
+		if (device) {
+			byte = spd_read_byte(ctrl->channel1[i], 2);
+			if (byte == 7) {
+				dimm_mask |= (1 << (i + DIMM_SOCKETS));
+			}
+		}
+	}
+	return dimm_mask;
+}
+
+static long spd_enable_2channels(const struct mem_controller *ctrl, long dimm_mask)
+{
+	int i;
+	u32 nbcap;
+	/* SPD addresses to verify are identical */
+	static const u8 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) */
+	};
+	/* If the dimms are not in pairs do not do dual channels */
+	if ((dimm_mask & ((1 << DIMM_SOCKETS) - 1)) !=
+		((dimm_mask >> DIMM_SOCKETS) & ((1 << DIMM_SOCKETS) - 1))) { 
+		goto single_channel;
+	}
+	/* If the cpu is not capable of doing dual channels don't do dual channels */
+	nbcap = pci_read_config32(ctrl->f3, NORTHBRIDGE_CAP);
+	if (!(nbcap & NBCAP_128Bit)) {
+		goto single_channel;
+	}
+	for(i = 0; (i < 4) && (ctrl->channel0[i]); i++) {
+		unsigned device0, device1;
+		int value0, value1;
+		int j;
+		/* If I don't have a dimm skip this one */
+		if (!(dimm_mask & (1 << i))) {
+			continue;
+		}
+		device0 = ctrl->channel0[i];
+		device1 = ctrl->channel1[i];
+		for(j = 0; j < sizeof(addresses)/sizeof(addresses[0]); j++) {
+			unsigned addr;
+			addr = addresses[j];
+			value0 = spd_read_byte(device0, addr);
+			if (value0 < 0) {
+				return -1;
+			}
+			value1 = spd_read_byte(device1, addr);
+			if (value1 < 0) {
+				return -1;
+			}
+			if (value0 != value1) {
+				goto single_channel;
+			}
+		}
+	}
+	printk(BIOS_SPEW, "Enabling dual channel memory\r\n");
+	u32 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);
+	return dimm_mask;
+ single_channel:
+	dimm_mask &= ~((1 << (DIMM_SOCKETS *2)) - (1 << DIMM_SOCKETS));
+	return dimm_mask;
+}
+
+struct mem_param {
+	u8 cycle_time;
+	u8 divisor; /* In 1/2 ns increments */
+	u8 tRC;
+	u8 tRFC;
+	u32 dch_memclk;
+	u16 dch_tref4k, dch_tref8k;
+	u8	 dtl_twr;
+	u8	 dtl_twtr;
+	u8  dtl_trwt[3][3]; /* first index is CAS_LAT 2/2.5/3 and 128/registered64/64 */
+ 	u8	 rdpreamble[4]; /* 0 is for registered, 1 for 1-2 DIMMS, 2 and 3 for 3 or 4 unreg dimm slots */
+	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,
+			.dtl_twtr   = 1,
+			.dtl_trwt   = { { 2, 2, 3 }, { 3, 3, 4 }, { 3, 3, 4 }},
+			.rdpreamble = { ((9 << 1) + 0), ((9 << 1) + 0), ((9 << 1) + 0), ((9 << 1) + 0) }
+		},
+		{
+			.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,
+			.dtl_twtr   = 1,
+			.dtl_trwt   = { { 2, 2, 3 }, { 3, 3, 4 }, { 3, 3, 4 }},
+			.rdpreamble = { ((8 << 1) + 0), ((7 << 1) + 0), ((7 << 1) + 1), ((7 << 1) + 0) }
+		},
+		{
+			.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,
+			.dtl_twtr   = 1,
+			.dtl_trwt   = { { 3, 2, 3 }, { 3, 3, 4 }, { 4, 3, 4 }},
+			.rdpreamble = { ((7 << 1) + 1), ((6 << 1) + 0), ((6 << 1) + 1), ((6 << 1) + 0) }
+		},
+		{
+			.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,
+			.dtl_twtr   = 2,
+			.dtl_trwt   = { { 0, 2, 3 }, { 3, 3, 4 }, { 3, 3, 4 }},
+			.rdpreamble = { ((7 << 1) + 0), ((5 << 1) + 0), ((5 << 1) + 1), ((5 << 1) + 1) }
+		},
+		{
+			.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");
+	}
+	print_spew(param->name);
+#ifdef DRAM_MIN_CYCLE_TIME
+	printk(BIOS_DEBUG, param->name);
+#endif
+	return param;
+}
+
+struct spd_set_memclk_result {
+	const struct mem_param *param;
+	long dimm_mask;
+};
+static struct spd_set_memclk_result spd_set_memclk(const struct mem_controller *ctrl, long dimm_mask)
+{
+	/* Compute the minimum cycle time for these dimms */
+	struct spd_set_memclk_result result;
+	unsigned min_cycle_time, min_latency, bios_cycle_time;
+	int i;
+	u32 value;
+
+	static const u8 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];
+	bios_cycle_time = min_cycle_times[
+		read_option(CMOS_VSTART_max_mem_clock, CMOS_VLEN_max_mem_clock, 0)];
+	if (bios_cycle_time > min_cycle_time) {
+		min_cycle_time = bios_cycle_time;
+	}
+	min_latency = 2;
+
+	/* Compute the least latency with the fastest clock supported
+	 * by both the memory controller and the dimms.
+	 */
+	for(i = 0; i < DIMM_SOCKETS; i++) {
+		int new_cycle_time, new_latency;
+		int index;
+		int latencies;
+		int latency;
+
+		if (!(dimm_mask & (1 << i))) {
+			continue;
+		}
+
+		/* 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) {
+				goto hw_error;
+			}
+
+			/* 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;
+		}
+	}
+	/* 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;
+		if (!(dimm_mask & (1 << i))) {
+			continue;
+		}
+		latencies = spd_read_byte(ctrl->channel0[i], 18);
+		if (latencies < 0) goto hw_error;
+		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]);
+		if (value < 0) goto hw_error;
+		
+		/* 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:
+		dimm_mask = disable_dimm(ctrl, i, dimm_mask);
+	}
+#if 0
+//down speed for full load 4 rank support
+#if QRANK_DIMM_SUPPORT
+	if(dimm_mask == (3|(3<<DIMM_SOCKETS)) ) {
+		int ranks = 4;
+	        for(i = 0; (i < 4) && (ctrl->channel0[i]); i++) {
+			int val;
+                	if (!(dimm_mask & (1 << i))) {
+                        	continue;
+	                }
+        	        val = spd_read_byte(ctrl->channel0[i], 5);
+			if(val!=ranks) {
+				ranks = val;
+				break;
+			}
+		}
+		if(ranks==4) {
+			if(min_cycle_time <= 0x50 ) {
+				min_cycle_time = 0x60;
+			}
+		}
+		
+	}
+#endif
+#endif
+	/* Now that I know the minimum cycle time lookup the memory parameters */
+	result.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);
+#if 0
+	/* Improves DQS centering by correcting for case when core speed multiplier and MEMCLK speed 
+	 * result in odd clock divisor, by selecting the next lowest memory speed, required only at DDR400 
+	 * and higher speeds with certain DIMM loadings ---- cheating???*/
+	if(!is_cpu_pre_e0()) {
+		if(min_cycle_time==0x50) {
+			value |= 1<<31;
+		}
+	}
+#endif
+
+	value |= result.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);
+	
+	result.dimm_mask = dimm_mask;
+	return result;
+ hw_error:
+	result.param = (const struct mem_param *)0;
+	result.dimm_mask = -1;
+	return result;
+}
+
+
+static int update_dimm_Trc(const struct mem_controller *ctrl, const struct mem_param *param, int i)
+{
+	unsigned clocks, old_clocks;
+	u32 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 0;
+	}
+
+	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 1;
+}
+
+static int update_dimm_Trfc(const struct mem_controller *ctrl, const struct mem_param *param, int i)
+{
+	unsigned clocks, old_clocks;
+	u32 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 0;
+	}
+	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 1;
+}
+
+
+static int update_dimm_Trcd(const struct mem_controller *ctrl, const struct mem_param *param, int i)
+{
+	unsigned clocks, old_clocks;
+	u32 dtl;
+	int value;
+	value = spd_read_byte(ctrl->channel0[i], 29);
+	if (value < 0) return -1;
+	clocks = (value + (param->divisor << 1) -1)/(param->divisor << 1);
+	if (clocks < DTL_TRCD_MIN) {
+		clocks = DTL_TRCD_MIN;
+	}
+	if (clocks > DTL_TRCD_MAX) {
+		return 0;
+	}
+	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 1;
+}
+
+static int update_dimm_Trrd(const struct mem_controller *ctrl, const struct mem_param *param, int i)
+{
+	unsigned clocks, old_clocks;
+	u32 dtl;
+	int value;
+	value = spd_read_byte(ctrl->channel0[i], 28);
+	if (value < 0) return -1;
+	clocks = (value + (param->divisor << 1) -1)/(param->divisor << 1);
+	if (clocks < DTL_TRRD_MIN) {
+		clocks = DTL_TRRD_MIN;
+	}
+	if (clocks > DTL_TRRD_MAX) {
+		return 0;
+	}
+	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 1;
+}
+
+static int update_dimm_Tras(const struct mem_controller *ctrl, const struct mem_param *param, int i)
+{
+	unsigned clocks, old_clocks;
+	u32 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 0;
+	}
+	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 1;
+}
+
+static int update_dimm_Trp(const struct mem_controller *ctrl, const struct mem_param *param, int i)
+{
+	unsigned clocks, old_clocks;
+	u32 dtl;
+	int value;
+	value = spd_read_byte(ctrl->channel0[i], 27);
+	if (value < 0) return -1;
+	clocks = (value + (param->divisor << 1) - 1)/(param->divisor << 1);
+	if (clocks < DTL_TRP_MIN) {
+		clocks = DTL_TRP_MIN;
+	}
+	if (clocks > DTL_TRP_MAX) {
+		return 0;
+	}
+	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 1;
+}
+
+static void set_Twr(const struct mem_controller *ctrl, const struct mem_param *param)
+{
+	u32 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)
+{
+	u32 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)
+{
+	u32 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 1;
+}
+
+
+static int update_dimm_x4(const struct mem_controller *ctrl, const struct mem_param *param, int i)
+{
+	u32 dcl;
+	int value;
+#if QRANK_DIMM_SUPPORT == 1
+	int rank;
+#endif
+	int dimm;
+	value = spd_read_byte(ctrl->channel0[i], 13);
+	if (value < 0) {
+		return -1;
+	}
+
+#if QRANK_DIMM_SUPPORT == 1
+	rank = spd_read_byte(ctrl->channel0[i], 5);       /* number of physical banks */
+	if (rank < 0) {
+		return -1;	
+	}
+#endif
+
+	dimm = 1<<(DCL_x4DIMM_SHIFT+i);
+#if QRANK_DIMM_SUPPORT == 1
+	if(rank==4) {
+		dimm |= 1<<(DCL_x4DIMM_SHIFT+i+2);
+	}
+#endif
+	dcl = pci_read_config32(ctrl->f2, DRAM_CONFIG_LOW);
+	dcl &= ~dimm;
+	if (value == 4) {
+		dcl |= dimm;
+	}
+	pci_write_config32(ctrl->f2, DRAM_CONFIG_LOW, dcl);
+	return 1;
+}
+
+static int update_dimm_ecc(const struct mem_controller *ctrl, const struct mem_param *param, int i)
+{
+	u32 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 1;
+}
+
+static int count_dimms(const struct mem_controller *ctrl)
+{
+	int dimms;
+	unsigned index;
+	dimms = 0;
+	for(index = 0; index < 8; index += 2) {
+		u32 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)
+{
+	u32 dth;
+
+	dth = pci_read_config32(ctrl->f2, DRAM_TIMING_HIGH);
+	dth &= ~(DTH_TWTR_MASK << DTH_TWTR_SHIFT);
+	dth |= ((param->dtl_twtr - 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)
+{
+	u32 dth, dtl;
+	unsigned latency;
+	unsigned clocks;
+	int lat, mtype;
+
+	clocks = 0;
+	dtl = pci_read_config32(ctrl->f2, DRAM_TIMING_LOW);
+	latency = (dtl >> DTL_TCL_SHIFT) & DTL_TCL_MASK;
+
+	if (is_opteron(ctrl)) {
+		mtype = 0; /* dual channel */
+	} else if (is_registered(ctrl)) {
+		mtype = 1; /* registered 64bit interface */
+	} else {
+		mtype = 2; /* unbuffered 64bit interface */
+	}
+
+	switch (latency) {
+		case DTL_CL_2:
+			lat = 0;
+			break;
+		case DTL_CL_2_5:
+			lat = 1;
+			break;
+		case DTL_CL_3:
+			lat = 2;
+			break;
+		default:
+			die("Unknown LAT for Trwt");
+	}
+
+	clocks = param->dtl_trwt[lat][mtype];
+	if ((clocks < DTH_TRWT_MIN) || (clocks > DTH_TRWT_MAX)) {
+		die("Unknown Trwt\r\n");
+	}
+	
+	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# */
+	u32 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)
+{
+	u32 dch;
+	unsigned rdpreamble;
+	int slots, i;
+
+	slots = 0;
+
+	for(i = 0; i < 4; i++) {
+		if (ctrl->channel0[i]) {
+			slots += 1;
+		}
+	}
+
+	/* map to index to param.rdpreamble array */
+	if (is_registered(ctrl)) {
+		i = 0;
+	} else if (slots < 3) {
+		i = 1;
+	} else if (slots == 3) {
+		i = 2;
+	} else if (slots == 4) {
+		i = 3;
+	} else {
+		die("Unknown rdpreamble for this nr of slots");
+	}
+
+	dch = pci_read_config32(ctrl->f2, DRAM_CONFIG_HIGH);
+	dch &= ~(DCH_RDPREAMBLE_MASK << DCH_RDPREAMBLE_SHIFT);
+	rdpreamble = param->rdpreamble[i];
+
+	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)
+{
+	u32 dch;
+	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)
+{
+	u32 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 long spd_set_dram_timing(const struct mem_controller *ctrl, const struct mem_param *param, long dimm_mask)
+{
+	int i;
+	
+	init_Tref(ctrl, param);
+	for(i = 0; i < DIMM_SOCKETS; i++) {
+		int rc;
+		if (!(dimm_mask & (1 << i))) {
+			continue;
+		}
+		/* DRAM Timing Low Register */
+		if ((rc = update_dimm_Trc (ctrl, param, i)) <= 0) goto dimm_err;
+		if ((rc = update_dimm_Trfc(ctrl, param, i)) <= 0) goto dimm_err;
+		if ((rc = update_dimm_Trcd(ctrl, param, i)) <= 0) goto dimm_err;
+		if ((rc = update_dimm_Trrd(ctrl, param, i)) <= 0) goto dimm_err;
+		if ((rc = update_dimm_Tras(ctrl, param, i)) <= 0) goto dimm_err;
+		if ((rc = update_dimm_Trp (ctrl, param, i)) <= 0) goto dimm_err;
+
+		/* DRAM Timing High Register */
+		if ((rc = update_dimm_Tref(ctrl, param, i)) <= 0) goto dimm_err;
+	
+
+		/* DRAM Config Low */
+		if ((rc = update_dimm_x4 (ctrl, param, i)) <= 0) goto dimm_err;
+		if ((rc = update_dimm_ecc(ctrl, param, i)) <= 0) goto dimm_err;
+		continue;
+	dimm_err:
+		if (rc < 0) {
+			return -1;
+		}
+		dimm_mask = disable_dimm(ctrl, i, dimm_mask);
+	}
+	/* 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);
+	return dimm_mask;
+}
+
+static void sdram_set_spd_registers(const struct mem_controller *ctrl, struct sys_info *sysinfo) 
+{
+	struct spd_set_memclk_result result;
+	const struct mem_param *param;
+	long dimm_mask;
+	if (!controller_present(ctrl)) {
+		printk(BIOS_DEBUG, "No memory controller present\r\n");
+		return;
+	}
+	hw_enable_ecc(ctrl);
+	activate_spd_rom(ctrl);
+	dimm_mask = spd_detect_dimms(ctrl);
+	if (!(dimm_mask & ((1 << DIMM_SOCKETS) - 1))) {
+		printk(BIOS_DEBUG, "No memory for this cpu\r\n");
+		return;
+	}
+	dimm_mask = spd_enable_2channels(ctrl, dimm_mask);        
+	if (dimm_mask < 0) 
+		goto hw_spd_err;
+	dimm_mask = spd_set_ram_size(ctrl , dimm_mask);           
+	if (dimm_mask < 0) 
+		goto hw_spd_err;
+	dimm_mask = spd_handle_unbuffered_dimms(ctrl, dimm_mask); 
+	if (dimm_mask < 0) 
+		goto hw_spd_err;
+	result = spd_set_memclk(ctrl, dimm_mask);
+	param     = result.param;
+	dimm_mask = result.dimm_mask;
+	if (dimm_mask < 0) 
+		goto hw_spd_err;
+	dimm_mask = spd_set_dram_timing(ctrl, param , dimm_mask);
+	if (dimm_mask < 0)
+		goto hw_spd_err;
+	order_dimms(ctrl);
+	return;
+ hw_spd_err:
+	/* Unrecoverable error reading SPD data */
+	print_err("SPD error - reset\r\n");
+	hard_reset();
+	return;
+}
+
+#if HW_MEM_HOLE_SIZEK != 0
+static u32 hoist_memory(int controllers, const struct mem_controller *ctrl,unsigned hole_startk, int i)
+{
+        int ii;
+        u32 carry_over;
+        struct device *dev;
+        u32 base, limit;
+        u32 basek;
+        u32 hoist;
+        int j;
+
+        carry_over = (4*1024*1024) - hole_startk;
+
+        for(ii=controllers - 1;ii>i;ii--) {
+                base  = pci_read_config32(ctrl[0].f1, 0x40 + (ii << 3));
+                if ((base & ((1<<1)|(1<<0))) != ((1<<1)|(1<<0))) {
+                        continue;
+                }
+		limit = pci_read_config32(ctrl[0].f1, 0x44 + (ii << 3));
+                for(j = 0; j < controllers; j++) {
+                        pci_write_config32(ctrl[j].f1, 0x44 + (ii << 3), limit + (carry_over << 2));
+                        pci_write_config32(ctrl[j].f1, 0x40 + (ii << 3), base + (carry_over << 2));
+                }
+        }
+        limit = pci_read_config32(ctrl[0].f1, 0x44 + (i << 3));
+        for(j = 0; j < controllers; j++) {
+                pci_write_config32(ctrl[j].f1, 0x44 + (i << 3), limit + (carry_over << 2));
+        }
+        dev = ctrl[i].f1;
+        base  = pci_read_config32(dev, 0x40 + (i << 3));
+        basek  = (base & 0xffff0000) >> 2;
+        if(basek == hole_startk) {
+                //don't need set memhole here, because hole off set will be 0, overflow
+                //so need to change base reg instead, new basek will be 4*1024*1024
+                base &= 0x0000ffff;
+                base |= (4*1024*1024)<<2;
+                for(j = 0; j < controllers; j++) {
+                        pci_write_config32(ctrl[j].f1, 0x40 + (i<<3), base);
+                }
+        }
+	else {
+        	hoist = /* hole start address */
+                	((hole_startk << 10) & 0xff000000) +
+	                /* hole address to memory controller address */
+        	        (((basek + carry_over) >> 6) & 0x0000ff00) +
+                	/* enable */
+	                1;
+	        pci_write_config32(dev, 0xf0, hoist);
+	}
+
+        return carry_over;
+}
+
+static void set_hw_mem_hole(int controllers, const struct mem_controller *ctrl)
+{
+
+        u32 hole_startk;
+        int i;
+
+        hole_startk = 4*1024*1024 - HW_MEM_HOLE_SIZEK;
+
+#if HW_MEM_HOLE_SIZE_AUTO_INC == 1 
+	/* We need to double check if hole_startk is valid.
+	 * If it is equal to the dram base address in K (base_k), 
+	 * we need to decrease it.
+	 */
+        u32 basek_pri;
+        for(i=0; i<controllers; i++) {
+                        u32 base;
+                        unsigned base_k;
+                        base  = pci_read_config32(ctrl[0].f1, 0x40 + (i << 3));
+                        if ((base & ((1<<1)|(1<<0))) != ((1<<1)|(1<<0))) {
+                                continue;
+                        }
+                        base_k = (base & 0xffff0000) >> 2;
+                        if(base_k == hole_startk) {
+				/* decrease memory hole startk to make sure it is
+				 * in the middle of the previous node 
+				 */
+                                hole_startk -= (base_k - basek_pri)>>1; 
+                                break; /* only one hole */
+                        }
+                        basek_pri = base_k;
+        }
+
+#endif
+        /* Find node number that needs the memory hole configured */
+        for(i=0; i<controllers; i++) {
+                        u32 base, limit;
+                        unsigned base_k, limit_k;
+                        base  = pci_read_config32(ctrl[0].f1, 0x40 + (i << 3));
+                        if ((base & ((1<<1)|(1<<0))) != ((1<<1)|(1<<0))) {
+                                continue;
+                        }
+                        limit = pci_read_config32(ctrl[0].f1, 0x44 + (i << 3));
+                        base_k = (base & 0xffff0000) >> 2;
+                        limit_k = ((limit + 0x00010000) & 0xffff0000) >> 2;
+			if ((base_k <= hole_startk) && (limit_k > hole_startk)) {
+                                unsigned end_k;
+                                hoist_memory(controllers, ctrl, hole_startk, i);
+                                end_k = memory_end_k(ctrl, controllers);
+                                set_top_mem(end_k, hole_startk);
+                                break; /* only one hole */
+                        }
+        }
+
+}
+
+#endif
+
+#define TIMEOUT_LOOPS 300000
+static void sdram_enable(int controllers, const struct mem_controller *ctrl, struct sys_info *sysinfo)
+{
+	int i;
+
+	/* Error if I don't have memory */
+	if (memory_end_k(ctrl, controllers) == 0) {
+		die("No memory\r\n");
+	}
+
+	/* Before enabling memory start the memory clocks */
+	for(i = 0; i < controllers; i++) {
+		u32 dch;
+		if (!controller_present(ctrl + i))
+			continue;
+		dch = pci_read_config32(ctrl[i].f2, DRAM_CONFIG_HIGH);
+		if (dch & (DCH_MEMCLK_EN0|DCH_MEMCLK_EN1|DCH_MEMCLK_EN2|DCH_MEMCLK_EN3)) {
+			dch |= DCH_MEMCLK_VALID;
+			pci_write_config32(ctrl[i].f2, DRAM_CONFIG_HIGH, dch);
+		}
+		else {
+			/* Disable dram receivers */
+			u32 dcl;
+			dcl = pci_read_config32(ctrl[i].f2, DRAM_CONFIG_LOW);
+			dcl |= DCL_DisInRcvrs;
+			pci_write_config32(ctrl[i].f2, DRAM_CONFIG_LOW, dcl);
+		}
+	}
+
+	/* And if necessary toggle the the reset on the dimms by hand */
+	memreset(controllers, ctrl);
+
+	/* We need to wait a mimmium of 20 MEMCLKS to enable the  InitDram */
+
+	for(i = 0; i < controllers; i++) {
+		u32 dcl, dch;
+		if (!controller_present(ctrl + i))
+			continue;
+		/* Skip everything if I don't have any memory on this controller */
+		dch = pci_read_config32(ctrl[i].f2, DRAM_CONFIG_HIGH);
+		if (!(dch & DCH_MEMCLK_VALID)) {
+			continue;
+		}
+
+		/* Toggle DisDqsHys to get it working */
+		dcl = pci_read_config32(ctrl[i].f2, DRAM_CONFIG_LOW);
+		if (dcl & DCL_DimmEccEn) {
+			u32 mnc;
+			print_spew("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++) {
+		u32 dcl, dch;
+		if (!controller_present(ctrl + i))
+			continue;
+		/* Skip everything if I don't have any memory on this controller */
+		dch = pci_read_config32(ctrl[i].f2, DRAM_CONFIG_HIGH);
+		if (!(dch & DCH_MEMCLK_VALID)) {
+			continue;
+		}
+
+		printk(BIOS_DEBUG, "Initializing memory: ");
+
+		int loops = 0;
+		do {
+			dcl = pci_read_config32(ctrl[i].f2, DRAM_CONFIG_LOW);
+			loops += 1;
+			if ((loops & 1023) == 0) {
+				printk(BIOS_DEBUG, ".");
+			}
+		} while(((dcl & DCL_DramInit) != 0) && (loops < TIMEOUT_LOOPS));
+		if (loops >= TIMEOUT_LOOPS) {
+			printk(BIOS_DEBUG, " failed\r\n");
+			continue;
+		}
+
+		if (!is_cpu_pre_c0()) {
+			/* Wait until it is safe to touch memory */
+			dcl &= ~(DCL_MemClrStatus | DCL_DramEnable);
+			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) || ((dcl & DCL_DramEnable) == 0) );
+		}
+
+		printk(BIOS_DEBUG, " done\r\n");
+	}
+
+#if HW_MEM_HOLE_SIZEK != 0
+         // init hw mem hole here
+        /* DramHoleValid bit only can be set after MemClrStatus is set by Hardware */
+	if(!is_cpu_pre_e0())
+	        set_hw_mem_hole(controllers, ctrl);
+#endif
+
+	//FIXME add enable node interleaving here -- yhlu
+	/*needed?
+		1. check how many nodes we have , if not all has ram installed get out
+		2. check cs_base lo is 0, node 0 f2 0x40,,,,, if any one is not using lo is CS_BASE, get out
+		3. check if other node is the same as node 0 about f2 0x40,,,,, otherwise get out
+		4. if all ready enable node_interleaving in f1 0x40..... of every node
+		5. for node interleaving we need to set mem hole to every node ( need recalcute hole offset in f0 for every node)
+	*/
+
+}
+
+static void set_sysinfo_in_ram(unsigned val)
+{
+}
+
+static void fill_mem_ctrl(int controllers, struct mem_controller *ctrl_a, const u16 *spd_addr)
+{
+        int i;
+        int j;
+        struct mem_controller *ctrl;
+        for(i=0;i<controllers; i++) {
+                ctrl = &ctrl_a[i];
+                ctrl->node_id = i;
+                ctrl->f0 = PCI_DEV(0, 0x18+i, 0);
+                ctrl->f1 = PCI_DEV(0, 0x18+i, 1);
+                ctrl->f2 = PCI_DEV(0, 0x18+i, 2);
+                ctrl->f3 = PCI_DEV(0, 0x18+i, 3);
+
+                if(spd_addr == (void *)0) continue;
+
+                for(j=0;j<DIMM_SOCKETS;j++) {
+                        ctrl->channel0[j] = spd_addr[(i*2+0)*DIMM_SOCKETS + j];
+                        ctrl->channel1[j] = spd_addr[(i*2+1)*DIMM_SOCKETS + j];
+                }
+        }
+}
+#endif
+
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2002 Linux Networx
+ * (Written by Eric Biederman <ebiederman at lnxi.com> for Linux Networx)
+ * Copyright (C) 2004 YingHai Lu
+ * Copyright (C) 2007 Ronald G. Minnich <rminnich at gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA, 02110-1301 USA
+ */
+/*	This should be done by Eric
+	2004.11 yhlu add 4 rank DIMM support
+	2004.12 yhlu add D0 support
+	2005.02 yhlu add E0 memory hole support
+*/
+/* not yet 
+#if K8_REV_F_SUPPORT == 1
+        #include "raminit_f.c"
+#else
+ */
+
+#include <mem.h>
+#include <cache.h>
+#include <mtrr.h>
+#include "raminit.h"
+#include "k8.h"
+#include "sysconf.h"
+
+#ifndef QRANK_DIMM_SUPPORT
+#define QRANK_DIMM_SUPPORT 0
+#endif
+
+static void hard_reset(void);
+
+static void setup_resource_map(const unsigned int *register_values, int max)
+{
+	int i;
+	printk(BIOS_DEBUG, "setting up resource map....");
+	for(i = 0; i < max; i += 3) {
+		struct device *dev;
+		unsigned where;
+		unsigned long reg;
+		printk(BIOS_DEBUG, "%08x <- %08x\r\n", register_values[i], register_values[i+2]);
+		dev = register_values[i] & ~0xfff;
+		where = register_values[i] & 0xfff;
+		reg = pci_read_config32(dev, where);
+		reg &= register_values[i+1];
+		reg |= register_values[i+2];
+		pci_write_config32(dev, where, reg);
+	}
+	printk(BIOS_DEBUG, "done.\r\n");
+}
+
+static int controller_present(const struct mem_controller *ctrl)
+{
+        return pci_read_config32(ctrl->f0, 0) == 0x11001022;
+}
+
+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,
+	/* MCA NB Status Low reg */
+	PCI_ADDR(0, 0x18, 3, 0x48), 0x00f00000, 0x00000000,
+	/* MCA NB Status high reg */
+	PCI_ADDR(0, 0x18, 3, 0x4c), 0x01801e8c, 0x00000000,
+	/* MCA NB address Low reg */
+	PCI_ADDR(0, 0x18, 3, 0x50), 0x00000007, 0x00000000,
+	/* MCA NB address high reg */
+	PCI_ADDR(0, 0x18, 3, 0x54), 0xffffff00, 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,
+	};
+	int i;
+	int max;
+
+        if (!controller_present(ctrl)) {
+                printk(BIOS_DEBUG, "No memory controller present\r\n");
+                return;
+        }
+	printk(BIOS_SPEW, "setting up CPU 0x%x northbridge registers ", ctrl->node_id);
+	max = sizeof(register_values)/sizeof(register_values[0]);
+/*
+	for(i = 0; i < max; i += 3) {
+		struct device *dev;
+		unsigned where;
+		unsigned long reg;
+                printk(BIOS_DEBUG, "%08x <- %08x\r\n", register_values[i], register_values[i+2]);
+		dev = (register_values[i] & ~0xfff) - PCI_DEV(0, 0x18, 0) + ctrl->f0;
+		where = register_values[i] & 0xfff;
+		reg = pci_read_config32(dev, where);
+		reg &= register_values[i+1];
+		reg |= register_values[i+2];
+		pci_write_config32(dev, where, reg);
+	}
+*/
+	printk(BIOS_SPEW, "done.\r\n");
+}
+
+
+static void hw_enable_ecc(const struct mem_controller *ctrl)
+{
+	u32 dcl, nbcap;
+	nbcap = pci_read_config32(ctrl->f3, NORTHBRIDGE_CAP);
+	dcl = pci_read_config32(ctrl->f2, DRAM_CONFIG_LOW);
+	dcl &= ~DCL_DimmEccEn;
+	if (nbcap & NBCAP_ECC) {
+		dcl |= DCL_DimmEccEn;
+	}
+	if (read_option(CMOS_VSTART_ECC_memory, CMOS_VLEN_ECC_memory, 1) == 0) {
+		dcl &= ~DCL_DimmEccEn;
+	}
+	pci_write_config32(ctrl->f2, DRAM_CONFIG_LOW, dcl);
+	
+}
+
+static int is_dual_channel(const struct mem_controller *ctrl)
+{
+	u32 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 Socket 939 based Athlon64 have dual channel capability,
+	 * too, so we need a better test for Opterons
+	 */
+#warning "FIXME: Implement a better test for Opterons"
+	u32 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.
+	 */
+	u32 dcl;
+	dcl = pci_read_config32(ctrl->f2, DRAM_CONFIG_LOW);
+	return !(dcl & DCL_UnBufDimm);
+}
+
+struct dimm_size {
+	unsigned long side1;
+	unsigned long side2;
+	unsigned long rows;
+	unsigned long col;
+#if QRANK_DIMM_SUPPORT == 1
+	unsigned long rank;
+#endif
+};
+
+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;
+	sz.rows = 0;
+	sz.col = 0;
+#if QRANK_DIMM_SUPPORT == 1
+	sz.rank = 0;
+#endif
+
+	/* 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 hw_err;
+	if ((value & 0xf) == 0) goto val_err;
+	sz.side1 += value & 0xf;
+	sz.rows = value & 0xf;
+
+	value = spd_read_byte(device, 4);	/* columns */
+	if (value < 0) goto hw_err;
+	if ((value & 0xf) == 0) goto val_err;
+	sz.side1 += value & 0xf;
+	sz.col = value & 0xf;
+
+	value = spd_read_byte(device, 17);	/* banks */
+	if (value < 0) goto hw_err;
+	if ((value & 0xff) == 0) goto val_err;
+	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 hw_err;
+	value &= 0xff;
+	value <<= 8;
+	
+	low = spd_read_byte(device, 6);	/* (low byte) */
+	if (low < 0) goto hw_err;
+	value = value | (low & 0xff);
+	if ((value != 72) && (value != 64)) goto val_err;
+	sz.side1 += log2(value);
+
+	/* side 2 */
+	value = spd_read_byte(device, 5);	/* number of physical banks */
+	if (value < 0) goto hw_err;
+	if (value == 1) goto out;
+	if ((value != 2) && (value != 4 )) {
+		goto val_err;
+	}
+#if QRANK_DIMM_SUPPORT == 1
+	sz.rank = value;
+#endif
+
+	/* Start with the symmetrical case */
+	sz.side2 = sz.side1;
+
+	value = spd_read_byte(device, 3);	/* rows */
+	if (value < 0) goto hw_err;
+	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 hw_err;
+	if ((value & 0xff) == 0) goto val_err;
+	sz.side2 -= (value & 0x0f);		/* Subtract out columns on side 1 */
+	sz.side2 += ((value >> 4) & 0x0f);	/* Add in columsn on side 2 */
+
+	goto out;
+
+ val_err:
+	die("Bad SPD value\r\n");
+	/* If an hw_error occurs report that I have no memory */
+hw_err:
+	sz.side1 = 0;
+	sz.side2 = 0;
+	sz.rows = 0;
+	sz.col = 0;
+#if QRANK_DIMM_SUPPORT == 1
+	sz.rank = 0;
+#endif
+ out:
+	return sz;
+}
+
+
+static void set_dimm_size(const struct mem_controller *ctrl, struct dimm_size sz, unsigned index)
+{
+	u32 base0, base1;
+	u32 dch;
+
+	if (sz.side1 != sz.side2) {
+		sz.side2 = 0;
+	}
+	
+	/* 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)) {
+		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);
+#if QRANK_DIMM_SUPPORT == 1
+	if(sz.rank == 4) {
+		pci_write_config32(ctrl->f2, DRAM_CSBASE + (((index << 1)+4)<<2), base0);
+		pci_write_config32(ctrl->f2, DRAM_CSBASE + (((index << 1)+5)<<2), base1);
+	}
+#endif
+
+	/* Enable the memory clocks for this DIMM */
+	if (base0) {
+		dch = pci_read_config32(ctrl->f2, DRAM_CONFIG_HIGH);
+		dch |= DCH_MEMCLK_EN0 << index;
+#if QRANK_DIMM_SUPPORT == 1
+		if(sz.rank == 4) {
+			dch |= DCH_MEMCLK_EN0 << (index + 2);
+		}
+#endif
+		pci_write_config32(ctrl->f2, DRAM_CONFIG_HIGH, dch);
+	}
+}
+
+static void set_dimm_map(const struct mem_controller *ctrl, struct dimm_size sz, unsigned index)
+{
+	static const unsigned cs_map_aa[] = {
+		/* (row=12, col=8)(14, 12) ---> (0, 0) (2, 4) */
+		0, 1, 3, 6, 0,
+		0, 2, 4, 7, 9,
+		0, 0, 5, 8,10,
+	};
+
+	u32 map;
+	u32 dch;
+
+	map = pci_read_config32(ctrl->f2, DRAM_BANK_ADDR_MAP);
+	map &= ~(0xf << (index * 4));
+#if QRANK_DIMM_SUPPORT == 1
+        if(sz.rank == 4) {
+                map &= ~(0xf << ( (index + 2) * 4));
+        }
+#endif
+
+
+	/* Make certain side1 of the dimm is at least 32MB */
+	if (sz.side1 >= (25 +3)) {
+		if(is_cpu_pre_d0()) {
+			map |= (sz.side1 - (25 + 3)) << (index *4);
+#if QRANK_DIMM_SUPPORT == 1
+	                if(sz.rank == 4) {
+         	              map |= (sz.side1 - (25 + 3)) << ( (index + 2) * 4);
+               		}
+#endif
+		}
+		else {
+			map |= cs_map_aa[(sz.rows - 12) * 5 + (sz.col - 8) ] << (index*4);
+#if QRANK_DIMM_SUPPORT == 1
+		        if(sz.rank == 4) {
+                	       map |=  cs_map_aa[(sz.rows - 12) * 5 + (sz.col - 8) ] << ( (index + 2) * 4);
+               		}
+#endif
+		}
+	}
+
+	pci_write_config32(ctrl->f2, DRAM_BANK_ADDR_MAP, map);
+	
+}
+
+static long spd_set_ram_size(const struct mem_controller *ctrl, long dimm_mask)
+{
+	int i;
+	
+	for(i = 0; i < DIMM_SOCKETS; i++) {
+		struct dimm_size sz;
+		if (!(dimm_mask & (1 << i))) {
+			continue;
+		}
+		sz = spd_get_dimm_size(ctrl->channel0[i]);
+		if (sz.side1 == 0) {
+			return -1; /* Report SPD error */
+		}
+		set_dimm_size(ctrl, sz, i);
+		set_dimm_map (ctrl, sz, i);
+	}
+	return dimm_mask;
+}
+
+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;
+	struct device *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, unsigned hole_startk)
+{
+	/* Error if I don't have memory */
+	if (!tom_k) {
+		die("No memory?");
+	}
+
+	/* Report the amount of memory. */
+	print_spew("RAM: 0x");
+	print_spew_hex32(tom_k);
+	print_spew(" KB\r\n");
+
+	/* Now set top of memory */
+	msr_t msr;
+	if(tom_k > (4*1024*1024)) {
+		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) {
+#if HW_MEM_HOLE_SIZEK != 0
+                if(hole_startk != 0) {
+                        tom_k = hole_startk;
+                } else
+#endif
+                tom_k = 0x3f0000;
+	}
+	msr.lo = (tom_k & 0x003fffff) << 10;
+	msr.hi = (tom_k & 0xffc00000) >> 22;
+	wrmsr(TOP_MEM, msr);
+}
+
+static unsigned long interleave_chip_selects(const struct mem_controller *ctrl)
+{
+	/* 35 - 25 */
+	static const u8 csbase_low_shift[] = { 
+	/* 32MB */	(13 - 4),
+	/* 64MB */	(14 - 4),
+	/* 128MB */	(14 - 4), 
+	/* 256MB */	(15 - 4),
+	/* 512MB */	(15 - 4),
+	/* 1GB */	(16 - 4),
+	/* 2GB */	(16 - 4), 
+	};
+
+        static const u8 csbase_low_d0_shift[] = {
+        /* 32MB */      (13 - 4),
+        /* 64MB */      (14 - 4),
+        /* 128MB */     (14 - 4),
+	/* 128MB */     (15 - 4),
+        /* 256MB */     (15 - 4),
+        /* 512MB */     (15 - 4),
+        /* 256MB */     (16 - 4),
+        /* 512MB */     (16 - 4),
+        /* 1GB */       (16 - 4),
+	/* 1GB */       (17 - 4),
+        /* 2GB */       (17 - 4),
+        };
+
+	/* cs_base_high is not changed */
+
+	u32 csbase_inc;
+	int chip_selects, index;
+	int bits;
+	unsigned common_size;
+	unsigned common_cs_mode;
+	u32 csbase, csmask;
+
+	/* See if all of the memory chip selects are the same size
+	 * and if so count them.
+	 */
+	chip_selects = 0;
+	common_size = 0;
+	common_cs_mode = 0;
+	for(index = 0; index < 8; index++) {
+		unsigned size;
+		unsigned cs_mode;
+		u32 value;
+		
+		value = pci_read_config32(ctrl->f2, DRAM_CSBASE + (index << 2));
+		
+		/* Is it enabled? */
+		if (!(value & 1)) {
+			continue;
+		}
+		chip_selects++;
+		size = value >> 21;
+		if (common_size == 0) {
+			common_size = size;
+		}
+		/* The size differed fail */
+		if (common_size != size) {
+			return 0;
+		}
+
+		value = pci_read_config32(ctrl->f2, DRAM_BANK_ADDR_MAP);
+                cs_mode =( value >> ((index>>1)*4)) & 0xf;
+                if(cs_mode == 0 ) continue;
+                if(common_cs_mode == 0) {
+                	common_cs_mode = cs_mode;
+                }
+                /* The size differed fail */
+                if(common_cs_mode != cs_mode) {
+                        return 0;
+                }
+	}
+
+	/* Chip selects can only be interleaved when there is
+	 * more than one and their is a power of two of them.
+	 */
+	bits = log2(chip_selects);
+	if (((1 << bits) != chip_selects) || (bits < 1) || (bits > 3)) {
+		return 0;
+	}
+
+	/* Find the bits of csbase that we need to interleave on */
+	if(is_cpu_pre_d0()){
+		csbase_inc = 1 << csbase_low_shift[common_cs_mode];
+		if(is_dual_channel(ctrl)) {
+                /* Also we run out of address mask bits if we try and interleave 8 4GB dimms */
+	                if ((bits == 3) && (common_size == (1 << (32 - 3)))) {
+//     		                printk(BIOS_DEBUG, "8 4GB chip selects cannot be interleaved\r\n");
+	                        return 0;
+                	}  
+			csbase_inc <<=1;
+		}
+	}
+	else {
+		csbase_inc = 1 << csbase_low_d0_shift[common_cs_mode];
+		if(is_dual_channel(ctrl)) {
+	                if( (bits==3) && (common_cs_mode > 8)) {
+//        	                printk(BIOS_DEBUG, "8 cs_mode>8 chip selects cannot be interleaved\r\n");
+        	                return 0;
+			}
+			csbase_inc <<=1;
+                }   
+	}
+
+	/* Compute the initial values for csbase and csbask. 
+	 * In csbase just set the enable bit and the base to zero.
+	 * In csmask set the mask bits for the size and page level interleave.
+	 */
+	csbase = 0 | 1;
+	csmask = (((common_size  << bits) - 1) << 21);
+	csmask |= 0xfe00 & ~((csbase_inc << bits) - csbase_inc);
+	for(index = 0; index < 8; index++) {
+		u32 value;
+
+		value = pci_read_config32(ctrl->f2, DRAM_CSBASE + (index << 2));
+		/* Is it enabled? */
+		if (!(value & 1)) {
+			continue;
+		}
+		pci_write_config32(ctrl->f2, DRAM_CSBASE + (index << 2), csbase);
+		pci_write_config32(ctrl->f2, DRAM_CSMASK + (index << 2), csmask);
+		csbase += csbase_inc;
+	}
+	
+	printk(BIOS_SPEW, "Interleaved\n");
+
+	/* Return the memory size in K */
+	return common_size << (15 + bits);
+}
+
+static unsigned long order_chip_selects(const struct mem_controller *ctrl)
+{
+	unsigned long tom;
+
+	/* Remember which registers we have used in the high 8 bits of tom */
+	tom = 0;
+	for(;;) {
+		/* Find the largest remaining canidate */
+		unsigned index, canidate;
+		u32 csbase, csmask;
+		unsigned size;
+		csbase = 0;
+		canidate = 0;
+		for(index = 0; index < 8; index++) {
+			u32 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 */
+
+		/* 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);
+		
+	}
+	/* Return the memory size in K */
+	return (tom & ~0xff000000) << 15;
+}
+
+unsigned long memory_end_k(const struct mem_controller *ctrl, int max_node_id)
+{
+	unsigned node_id;
+	unsigned end_k;
+	/* Find the last memory address used */
+	end_k = 0;
+	for(node_id = 0; node_id < max_node_id; node_id++) {
+		u32 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);
+			end_k = ((limit + 0x00010000) & 0xffff0000) >> 2;
+		}
+	}
+	return end_k;
+}
+
+static void order_dimms(const struct mem_controller *ctrl)
+{
+	unsigned long tom_k, base_k;
+
+	if (read_option(CMOS_VSTART_interleave_chip_selects, CMOS_VLEN_interleave_chip_selects, 1) != 0) {
+		tom_k = interleave_chip_selects(ctrl);
+	} else {
+		printk(BIOS_DEBUG, "Interleaving disabled\r\n");
+		tom_k = 0;
+	}
+	if (!tom_k) {
+		tom_k = order_chip_selects(ctrl);
+	}
+	/* Compute the memory base address */
+	base_k = memory_end_k(ctrl, ctrl->node_id);
+	tom_k += base_k;
+	route_dram_accesses(ctrl, base_k, tom_k);
+	set_top_mem(tom_k, 0);
+}
+
+static long disable_dimm(const struct mem_controller *ctrl, unsigned index, long dimm_mask)
+{
+	printk(BIOS_DEBUG, "disabling dimm 0x%x\n", index); 
+	pci_write_config32(ctrl->f2, DRAM_CSBASE + (((index << 1)+0)<<2), 0);
+	pci_write_config32(ctrl->f2, DRAM_CSBASE + (((index << 1)+1)<<2), 0);
+	dimm_mask &= ~(1 << index);
+	return dimm_mask;
+}
+
+static long spd_handle_unbuffered_dimms(const struct mem_controller *ctrl, long dimm_mask)
+{
+	int i;
+	int registered;
+	int unbuffered;
+	int has_dualch = is_opteron(ctrl);
+	u32 dcl;
+	unbuffered = 0;
+	registered = 0;
+	for(i = 0; (i < DIMM_SOCKETS); i++) {
+		int value;
+		if (!(dimm_mask & (1 << i))) {
+			continue;
+		}
+		value = spd_read_byte(ctrl->channel0[i], 21);
+		if (value < 0) {
+			return -1;
+		}
+		/* 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");
+	}
+
+	dcl = pci_read_config32(ctrl->f2, DRAM_CONFIG_LOW);
+	dcl &= ~DCL_UnBufDimm;
+	if (unbuffered) {
+		if ((has_dualch) && (!is_cpu_pre_d0())) {
+			dcl |= DCL_UnBufDimm; /* set DCL_DualDIMMen too? */
+			
+			/* set DCL_En2T if you have non-equal DDR mem types! */
+			
+			if ((cpuid_eax(1) & 0x30) == 0x30) {
+				/* CS[7:4] is copy of CS[3:0], should be set for 939 socket */
+				dcl |= DCL_UpperCSMap;
+			}
+		} else {
+			dcl |= DCL_UnBufDimm;
+		}
+	}
+	pci_write_config32(ctrl->f2, DRAM_CONFIG_LOW, dcl);
+	if (is_registered(ctrl)) {
+		printk(BIOS_DEBUG, "Registered\r\n");
+	} else {
+		printk(BIOS_DEBUG, "Unbuffered\r\n");
+	}
+	return dimm_mask;
+}
+
+static unsigned int spd_detect_dimms(const struct mem_controller *ctrl)
+{
+	unsigned dimm_mask;
+	int i;
+	dimm_mask = 0;
+	for(i = 0; i < DIMM_SOCKETS; i++) {
+		int byte;
+		unsigned device;
+		device = ctrl->channel0[i];
+		if (device) {
+			byte = spd_read_byte(ctrl->channel0[i], 2);  /* Type */
+			if (byte == 7) {
+				dimm_mask |= (1 << i);
+			}
+		}
+		device = ctrl->channel1[i];
+		if (device) {
+			byte = spd_read_byte(ctrl->channel1[i], 2);
+			if (byte == 7) {
+				dimm_mask |= (1 << (i + DIMM_SOCKETS));
+			}
+		}
+	}
+	return dimm_mask;
+}
+
+static long spd_enable_2channels(const struct mem_controller *ctrl, long dimm_mask)
+{
+	int i;
+	u32 nbcap;
+	/* SPD addresses to verify are identical */
+	static const u8 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) */
+	};
+	/* If the dimms are not in pairs do not do dual channels */
+	if ((dimm_mask & ((1 << DIMM_SOCKETS) - 1)) !=
+		((dimm_mask >> DIMM_SOCKETS) & ((1 << DIMM_SOCKETS) - 1))) { 
+		goto single_channel;
+	}
+	/* If the cpu is not capable of doing dual channels don't do dual channels */
+	nbcap = pci_read_config32(ctrl->f3, NORTHBRIDGE_CAP);
+	if (!(nbcap & NBCAP_128Bit)) {
+		goto single_channel;
+	}
+	for(i = 0; (i < 4) && (ctrl->channel0[i]); i++) {
+		unsigned device0, device1;
+		int value0, value1;
+		int j;
+		/* If I don't have a dimm skip this one */
+		if (!(dimm_mask & (1 << i))) {
+			continue;
+		}
+		device0 = ctrl->channel0[i];
+		device1 = ctrl->channel1[i];
+		for(j = 0; j < sizeof(addresses)/sizeof(addresses[0]); j++) {
+			unsigned addr;
+			addr = addresses[j];
+			value0 = spd_read_byte(device0, addr);
+			if (value0 < 0) {
+				return -1;
+			}
+			value1 = spd_read_byte(device1, addr);
+			if (value1 < 0) {
+				return -1;
+			}
+			if (value0 != value1) {
+				goto single_channel;
+			}
+		}
+	}
+	printk(BIOS_SPEW, "Enabling dual channel memory\r\n");
+	u32 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);
+	return dimm_mask;
+ single_channel:
+	dimm_mask &= ~((1 << (DIMM_SOCKETS *2)) - (1 << DIMM_SOCKETS));
+	return dimm_mask;
+}
+
+struct mem_param {
+	u8 cycle_time;
+	u8 divisor; /* In 1/2 ns increments */
+	u8 tRC;
+	u8 tRFC;
+	u32 dch_memclk;
+	u16 dch_tref4k, dch_tref8k;
+	u8	 dtl_twr;
+	u8	 dtl_twtr;
+	u8  dtl_trwt[3][3]; /* first index is CAS_LAT 2/2.5/3 and 128/registered64/64 */
+ 	u8	 rdpreamble[4]; /* 0 is for registered, 1 for 1-2 DIMMS, 2 and 3 for 3 or 4 unreg dimm slots */
+	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,
+			.dtl_twtr   = 1,
+			.dtl_trwt   = { { 2, 2, 3 }, { 3, 3, 4 }, { 3, 3, 4 }},
+			.rdpreamble = { ((9 << 1) + 0), ((9 << 1) + 0), ((9 << 1) + 0), ((9 << 1) + 0) }
+		},
+		{
+			.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,
+			.dtl_twtr   = 1,
+			.dtl_trwt   = { { 2, 2, 3 }, { 3, 3, 4 }, { 3, 3, 4 }},
+			.rdpreamble = { ((8 << 1) + 0), ((7 << 1) + 0), ((7 << 1) + 1), ((7 << 1) + 0) }
+		},
+		{
+			.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,
+			.dtl_twtr   = 1,
+			.dtl_trwt   = { { 3, 2, 3 }, { 3, 3, 4 }, { 4, 3, 4 }},
+			.rdpreamble = { ((7 << 1) + 1), ((6 << 1) + 0), ((6 << 1) + 1), ((6 << 1) + 0) }
+		},
+		{
+			.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,
+			.dtl_twtr   = 2,
+			.dtl_trwt   = { { 0, 2, 3 }, { 3, 3, 4 }, { 3, 3, 4 }},
+			.rdpreamble = { ((7 << 1) + 0), ((5 << 1) + 0), ((5 << 1) + 1), ((5 << 1) + 1) }
+		},
+		{
+			.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");
+	}
+	print_spew(param->name);
+#ifdef DRAM_MIN_CYCLE_TIME
+	printk(BIOS_DEBUG, param->name);
+#endif
+	return param;
+}
+
+struct spd_set_memclk_result {
+	const struct mem_param *param;
+	long dimm_mask;
+};
+static struct spd_set_memclk_result spd_set_memclk(const struct mem_controller *ctrl, long dimm_mask)
+{
+	/* Compute the minimum cycle time for these dimms */
+	struct spd_set_memclk_result result;
+	unsigned min_cycle_time, min_latency, bios_cycle_time;
+	int i;
+	u32 value;
+
+	static const u8 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];
+	bios_cycle_time = min_cycle_times[
+		read_option(CMOS_VSTART_max_mem_clock, CMOS_VLEN_max_mem_clock, 0)];
+	if (bios_cycle_time > min_cycle_time) {
+		min_cycle_time = bios_cycle_time;
+	}
+	min_latency = 2;
+
+	/* Compute the least latency with the fastest clock supported
+	 * by both the memory controller and the dimms.
+	 */
+	for(i = 0; i < DIMM_SOCKETS; i++) {
+		int new_cycle_time, new_latency;
+		int index;
+		int latencies;
+		int latency;
+
+		if (!(dimm_mask & (1 << i))) {
+			continue;
+		}
+
+		/* 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) {
+				goto hw_error;
+			}
+
+			/* 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;
+		}
+	}
+	/* 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;
+		if (!(dimm_mask & (1 << i))) {
+			continue;
+		}
+		latencies = spd_read_byte(ctrl->channel0[i], 18);
+		if (latencies < 0) goto hw_error;
+		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]);
+		if (value < 0) goto hw_error;
+		
+		/* 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:
+		dimm_mask = disable_dimm(ctrl, i, dimm_mask);
+	}
+#if 0
+//down speed for full load 4 rank support
+#if QRANK_DIMM_SUPPORT
+	if(dimm_mask == (3|(3<<DIMM_SOCKETS)) ) {
+		int ranks = 4;
+	        for(i = 0; (i < 4) && (ctrl->channel0[i]); i++) {
+			int val;
+                	if (!(dimm_mask & (1 << i))) {
+                        	continue;
+	                }
+        	        val = spd_read_byte(ctrl->channel0[i], 5);
+			if(val!=ranks) {
+				ranks = val;
+				break;
+			}
+		}
+		if(ranks==4) {
+			if(min_cycle_time <= 0x50 ) {
+				min_cycle_time = 0x60;
+			}
+		}
+		
+	}
+#endif
+#endif
+	/* Now that I know the minimum cycle time lookup the memory parameters */
+	result.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);
+#if 0
+	/* Improves DQS centering by correcting for case when core speed multiplier and MEMCLK speed 
+	 * result in odd clock divisor, by selecting the next lowest memory speed, required only at DDR400 
+	 * and higher speeds with certain DIMM loadings ---- cheating???*/
+	if(!is_cpu_pre_e0()) {
+		if(min_cycle_time==0x50) {
+			value |= 1<<31;
+		}
+	}
+#endif
+
+	value |= result.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);
+	
+	result.dimm_mask = dimm_mask;
+	return result;
+ hw_error:
+	result.param = (const struct mem_param *)0;
+	result.dimm_mask = -1;
+	return result;
+}
+
+
+static int update_dimm_Trc(const struct mem_controller *ctrl, const struct mem_param *param, int i)
+{
+	unsigned clocks, old_clocks;
+	u32 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 0;
+	}
+
+	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 1;
+}
+
+static int update_dimm_Trfc(const struct mem_controller *ctrl, const struct mem_param *param, int i)
+{
+	unsigned clocks, old_clocks;
+	u32 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 0;
+	}
+	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 1;
+}
+
+
+static int update_dimm_Trcd(const struct mem_controller *ctrl, const struct mem_param *param, int i)
+{
+	unsigned clocks, old_clocks;
+	u32 dtl;
+	int value;
+	value = spd_read_byte(ctrl->channel0[i], 29);
+	if (value < 0) return -1;
+	clocks = (value + (param->divisor << 1) -1)/(param->divisor << 1);
+	if (clocks < DTL_TRCD_MIN) {
+		clocks = DTL_TRCD_MIN;
+	}
+	if (clocks > DTL_TRCD_MAX) {
+		return 0;
+	}
+	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 1;
+}
+
+static int update_dimm_Trrd(const struct mem_controller *ctrl, const struct mem_param *param, int i)
+{
+	unsigned clocks, old_clocks;
+	u32 dtl;
+	int value;
+	value = spd_read_byte(ctrl->channel0[i], 28);
+	if (value < 0) return -1;
+	clocks = (value + (param->divisor << 1) -1)/(param->divisor << 1);
+	if (clocks < DTL_TRRD_MIN) {
+		clocks = DTL_TRRD_MIN;
+	}
+	if (clocks > DTL_TRRD_MAX) {
+		return 0;
+	}
+	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 1;
+}
+
+static int update_dimm_Tras(const struct mem_controller *ctrl, const struct mem_param *param, int i)
+{
+	unsigned clocks, old_clocks;
+	u32 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 0;
+	}
+	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 1;
+}
+
+static int update_dimm_Trp(const struct mem_controller *ctrl, const struct mem_param *param, int i)
+{
+	unsigned clocks, old_clocks;
+	u32 dtl;
+	int value;
+	value = spd_read_byte(ctrl->channel0[i], 27);
+	if (value < 0) return -1;
+	clocks = (value + (param->divisor << 1) - 1)/(param->divisor << 1);
+	if (clocks < DTL_TRP_MIN) {
+		clocks = DTL_TRP_MIN;
+	}
+	if (clocks > DTL_TRP_MAX) {
+		return 0;
+	}
+	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 1;
+}
+
+static void set_Twr(const struct mem_controller *ctrl, const struct mem_param *param)
+{
+	u32 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)
+{
+	u32 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)
+{
+	u32 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 1;
+}
+
+
+static int update_dimm_x4(const struct mem_controller *ctrl, const struct mem_param *param, int i)
+{
+	u32 dcl;
+	int value;
+#if QRANK_DIMM_SUPPORT == 1
+	int rank;
+#endif
+	int dimm;
+	value = spd_read_byte(ctrl->channel0[i], 13);
+	if (value < 0) {
+		return -1;
+	}
+
+#if QRANK_DIMM_SUPPORT == 1
+	rank = spd_read_byte(ctrl->channel0[i], 5);       /* number of physical banks */
+	if (rank < 0) {
+		return -1;	
+	}
+#endif
+
+	dimm = 1<<(DCL_x4DIMM_SHIFT+i);
+#if QRANK_DIMM_SUPPORT == 1
+	if(rank==4) {
+		dimm |= 1<<(DCL_x4DIMM_SHIFT+i+2);
+	}
+#endif
+	dcl = pci_read_config32(ctrl->f2, DRAM_CONFIG_LOW);
+	dcl &= ~dimm;
+	if (value == 4) {
+		dcl |= dimm;
+	}
+	pci_write_config32(ctrl->f2, DRAM_CONFIG_LOW, dcl);
+	return 1;
+}
+
+static int update_dimm_ecc(const struct mem_controller *ctrl, const struct mem_param *param, int i)
+{
+	u32 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 1;
+}
+
+static int count_dimms(const struct mem_controller *ctrl)
+{
+	int dimms;
+	unsigned index;
+	dimms = 0;
+	for(index = 0; index < 8; index += 2) {
+		u32 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)
+{
+	u32 dth;
+
+	dth = pci_read_config32(ctrl->f2, DRAM_TIMING_HIGH);
+	dth &= ~(DTH_TWTR_MASK << DTH_TWTR_SHIFT);
+	dth |= ((param->dtl_twtr - 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)
+{
+	u32 dth, dtl;
+	unsigned latency;
+	unsigned clocks;
+	int lat, mtype;
+
+	clocks = 0;
+	dtl = pci_read_config32(ctrl->f2, DRAM_TIMING_LOW);
+	latency = (dtl >> DTL_TCL_SHIFT) & DTL_TCL_MASK;
+
+	if (is_opteron(ctrl)) {
+		mtype = 0; /* dual channel */
+	} else if (is_registered(ctrl)) {
+		mtype = 1; /* registered 64bit interface */
+	} else {
+		mtype = 2; /* unbuffered 64bit interface */
+	}
+
+	switch (latency) {
+		case DTL_CL_2:
+			lat = 0;
+			break;
+		case DTL_CL_2_5:
+			lat = 1;
+			break;
+		case DTL_CL_3:
+			lat = 2;
+			break;
+		default:
+			die("Unknown LAT for Trwt");
+	}
+
+	clocks = param->dtl_trwt[lat][mtype];
+	if ((clocks < DTH_TRWT_MIN) || (clocks > DTH_TRWT_MAX)) {
+		die("Unknown Trwt\r\n");
+	}
+	
+	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# */
+	u32 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)
+{
+	u32 dch;
+	unsigned rdpreamble;
+	int slots, i;
+
+	slots = 0;
+
+	for(i = 0; i < 4; i++) {
+		if (ctrl->channel0[i]) {
+			slots += 1;
+		}
+	}
+
+	/* map to index to param.rdpreamble array */
+	if (is_registered(ctrl)) {
+		i = 0;
+	} else if (slots < 3) {
+		i = 1;
+	} else if (slots == 3) {
+		i = 2;
+	} else if (slots == 4) {
+		i = 3;
+	} else {
+		die("Unknown rdpreamble for this nr of slots");
+	}
+
+	dch = pci_read_config32(ctrl->f2, DRAM_CONFIG_HIGH);
+	dch &= ~(DCH_RDPREAMBLE_MASK << DCH_RDPREAMBLE_SHIFT);
+	rdpreamble = param->rdpreamble[i];
+
+	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)
+{
+	u32 dch;
+	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)
+{
+	u32 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 long spd_set_dram_timing(const struct mem_controller *ctrl, const struct mem_param *param, long dimm_mask)
+{
+	int i;
+	
+	init_Tref(ctrl, param);
+	for(i = 0; i < DIMM_SOCKETS; i++) {
+		int rc;
+		if (!(dimm_mask & (1 << i))) {
+			continue;
+		}
+		/* DRAM Timing Low Register */
+		if ((rc = update_dimm_Trc (ctrl, param, i)) <= 0) goto dimm_err;
+		if ((rc = update_dimm_Trfc(ctrl, param, i)) <= 0) goto dimm_err;
+		if ((rc = update_dimm_Trcd(ctrl, param, i)) <= 0) goto dimm_err;
+		if ((rc = update_dimm_Trrd(ctrl, param, i)) <= 0) goto dimm_err;
+		if ((rc = update_dimm_Tras(ctrl, param, i)) <= 0) goto dimm_err;
+		if ((rc = update_dimm_Trp (ctrl, param, i)) <= 0) goto dimm_err;
+
+		/* DRAM Timing High Register */
+		if ((rc = update_dimm_Tref(ctrl, param, i)) <= 0) goto dimm_err;
+	
+
+		/* DRAM Config Low */
+		if ((rc = update_dimm_x4 (ctrl, param, i)) <= 0) goto dimm_err;
+		if ((rc = update_dimm_ecc(ctrl, param, i)) <= 0) goto dimm_err;
+		continue;
+	dimm_err:
+		if (rc < 0) {
+			return -1;
+		}
+		dimm_mask = disable_dimm(ctrl, i, dimm_mask);
+	}
+	/* 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);
+	return dimm_mask;
+}
+
+static void sdram_set_spd_registers(const struct mem_controller *ctrl, struct sys_info *sysinfo) 
+{
+	struct spd_set_memclk_result result;
+	const struct mem_param *param;
+	long dimm_mask;
+	if (!controller_present(ctrl)) {
+		printk(BIOS_DEBUG, "No memory controller present\r\n");
+		return;
+	}
+	hw_enable_ecc(ctrl);
+	activate_spd_rom(ctrl);
+	dimm_mask = spd_detect_dimms(ctrl);
+	if (!(dimm_mask & ((1 << DIMM_SOCKETS) - 1))) {
+		printk(BIOS_DEBUG, "No memory for this cpu\r\n");
+		return;
+	}
+	dimm_mask = spd_enable_2channels(ctrl, dimm_mask);        
+	if (dimm_mask < 0) 
+		goto hw_spd_err;
+	dimm_mask = spd_set_ram_size(ctrl , dimm_mask);           
+	if (dimm_mask < 0) 
+		goto hw_spd_err;
+	dimm_mask = spd_handle_unbuffered_dimms(ctrl, dimm_mask); 
+	if (dimm_mask < 0) 
+		goto hw_spd_err;
+	result = spd_set_memclk(ctrl, dimm_mask);
+	param     = result.param;
+	dimm_mask = result.dimm_mask;
+	if (dimm_mask < 0) 
+		goto hw_spd_err;
+	dimm_mask = spd_set_dram_timing(ctrl, param , dimm_mask);
+	if (dimm_mask < 0)
+		goto hw_spd_err;
+	order_dimms(ctrl);
+	return;
+ hw_spd_err:
+	/* Unrecoverable error reading SPD data */
+	print_err("SPD error - reset\r\n");
+	hard_reset();
+	return;
+}
+
+#if HW_MEM_HOLE_SIZEK != 0
+static u32 hoist_memory(int controllers, const struct mem_controller *ctrl,unsigned hole_startk, int i)
+{
+        int ii;
+        u32 carry_over;
+        struct device *dev;
+        u32 base, limit;
+        u32 basek;
+        u32 hoist;
+        int j;
+
+        carry_over = (4*1024*1024) - hole_startk;
+
+        for(ii=controllers - 1;ii>i;ii--) {
+                base  = pci_read_config32(ctrl[0].f1, 0x40 + (ii << 3));
+                if ((base & ((1<<1)|(1<<0))) != ((1<<1)|(1<<0))) {
+                        continue;
+                }
+		limit = pci_read_config32(ctrl[0].f1, 0x44 + (ii << 3));
+                for(j = 0; j < controllers; j++) {
+                        pci_write_config32(ctrl[j].f1, 0x44 + (ii << 3), limit + (carry_over << 2));
+                        pci_write_config32(ctrl[j].f1, 0x40 + (ii << 3), base + (carry_over << 2));
+                }
+        }
+        limit = pci_read_config32(ctrl[0].f1, 0x44 + (i << 3));
+        for(j = 0; j < controllers; j++) {
+                pci_write_config32(ctrl[j].f1, 0x44 + (i << 3), limit + (carry_over << 2));
+        }
+        dev = ctrl[i].f1;
+        base  = pci_read_config32(dev, 0x40 + (i << 3));
+        basek  = (base & 0xffff0000) >> 2;
+        if(basek == hole_startk) {
+                //don't need set memhole here, because hole off set will be 0, overflow
+                //so need to change base reg instead, new basek will be 4*1024*1024
+                base &= 0x0000ffff;
+                base |= (4*1024*1024)<<2;
+                for(j = 0; j < controllers; j++) {
+                        pci_write_config32(ctrl[j].f1, 0x40 + (i<<3), base);
+                }
+        }
+	else {
+        	hoist = /* hole start address */
+                	((hole_startk << 10) & 0xff000000) +
+	                /* hole address to memory controller address */
+        	        (((basek + carry_over) >> 6) & 0x0000ff00) +
+                	/* enable */
+	                1;
+	        pci_write_config32(dev, 0xf0, hoist);
+	}
+
+        return carry_over;
+}
+
+static void set_hw_mem_hole(int controllers, const struct mem_controller *ctrl)
+{
+
+        u32 hole_startk;
+        int i;
+
+        hole_startk = 4*1024*1024 - HW_MEM_HOLE_SIZEK;
+
+#if HW_MEM_HOLE_SIZE_AUTO_INC == 1 
+	/* We need to double check if hole_startk is valid.
+	 * If it is equal to the dram base address in K (base_k), 
+	 * we need to decrease it.
+	 */
+        u32 basek_pri;
+        for(i=0; i<controllers; i++) {
+                        u32 base;
+                        unsigned base_k;
+                        base  = pci_read_config32(ctrl[0].f1, 0x40 + (i << 3));
+                        if ((base & ((1<<1)|(1<<0))) != ((1<<1)|(1<<0))) {
+                                continue;
+                        }
+                        base_k = (base & 0xffff0000) >> 2;
+                        if(base_k == hole_startk) {
+				/* decrease memory hole startk to make sure it is
+				 * in the middle of the previous node 
+				 */
+                                hole_startk -= (base_k - basek_pri)>>1; 
+                                break; /* only one hole */
+                        }
+                        basek_pri = base_k;
+        }
+
+#endif
+        /* Find node number that needs the memory hole configured */
+        for(i=0; i<controllers; i++) {
+                        u32 base, limit;
+                        unsigned base_k, limit_k;
+                        base  = pci_read_config32(ctrl[0].f1, 0x40 + (i << 3));
+                        if ((base & ((1<<1)|(1<<0))) != ((1<<1)|(1<<0))) {
+                                continue;
+                        }
+                        limit = pci_read_config32(ctrl[0].f1, 0x44 + (i << 3));
+                        base_k = (base & 0xffff0000) >> 2;
+                        limit_k = ((limit + 0x00010000) & 0xffff0000) >> 2;
+			if ((base_k <= hole_startk) && (limit_k > hole_startk)) {
+                                unsigned end_k;
+                                hoist_memory(controllers, ctrl, hole_startk, i);
+                                end_k = memory_end_k(ctrl, controllers);
+                                set_top_mem(end_k, hole_startk);
+                                break; /* only one hole */
+                        }
+        }
+
+}
+
+#endif
+
+#define TIMEOUT_LOOPS 300000
+static void sdram_enable(int controllers, const struct mem_controller *ctrl, struct sys_info *sysinfo)
+{
+	int i;
+
+	/* Error if I don't have memory */
+	if (memory_end_k(ctrl, controllers) == 0) {
+		die("No memory\r\n");
+	}
+
+	/* Before enabling memory start the memory clocks */
+	for(i = 0; i < controllers; i++) {
+		u32 dch;
+		if (!controller_present(ctrl + i))
+			continue;
+		dch = pci_read_config32(ctrl[i].f2, DRAM_CONFIG_HIGH);
+		if (dch & (DCH_MEMCLK_EN0|DCH_MEMCLK_EN1|DCH_MEMCLK_EN2|DCH_MEMCLK_EN3)) {
+			dch |= DCH_MEMCLK_VALID;
+			pci_write_config32(ctrl[i].f2, DRAM_CONFIG_HIGH, dch);
+		}
+		else {
+			/* Disable dram receivers */
+			u32 dcl;
+			dcl = pci_read_config32(ctrl[i].f2, DRAM_CONFIG_LOW);
+			dcl |= DCL_DisInRcvrs;
+			pci_write_config32(ctrl[i].f2, DRAM_CONFIG_LOW, dcl);
+		}
+	}
+
+	/* And if necessary toggle the the reset on the dimms by hand */
+	memreset(controllers, ctrl);
+
+	/* We need to wait a mimmium of 20 MEMCLKS to enable the  InitDram */
+
+	for(i = 0; i < controllers; i++) {
+		u32 dcl, dch;
+		if (!controller_present(ctrl + i))
+			continue;
+		/* Skip everything if I don't have any memory on this controller */
+		dch = pci_read_config32(ctrl[i].f2, DRAM_CONFIG_HIGH);
+		if (!(dch & DCH_MEMCLK_VALID)) {
+			continue;
+		}
+
+		/* Toggle DisDqsHys to get it working */
+		dcl = pci_read_config32(ctrl[i].f2, DRAM_CONFIG_LOW);
+		if (dcl & DCL_DimmEccEn) {
+			u32 mnc;
+			print_spew("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++) {
+		u32 dcl, dch;
+		if (!controller_present(ctrl + i))
+			continue;
+		/* Skip everything if I don't have any memory on this controller */
+		dch = pci_read_config32(ctrl[i].f2, DRAM_CONFIG_HIGH);
+		if (!(dch & DCH_MEMCLK_VALID)) {
+			continue;
+		}
+
+		printk(BIOS_DEBUG, "Initializing memory: ");
+
+		int loops = 0;
+		do {
+			dcl = pci_read_config32(ctrl[i].f2, DRAM_CONFIG_LOW);
+			loops += 1;
+			if ((loops & 1023) == 0) {
+				printk(BIOS_DEBUG, ".");
+			}
+		} while(((dcl & DCL_DramInit) != 0) && (loops < TIMEOUT_LOOPS));
+		if (loops >= TIMEOUT_LOOPS) {
+			printk(BIOS_DEBUG, " failed\r\n");
+			continue;
+		}
+
+		if (!is_cpu_pre_c0()) {
+			/* Wait until it is safe to touch memory */
+			dcl &= ~(DCL_MemClrStatus | DCL_DramEnable);
+			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) || ((dcl & DCL_DramEnable) == 0) );
+		}
+
+		printk(BIOS_DEBUG, " done\r\n");
+	}
+
+#if HW_MEM_HOLE_SIZEK != 0
+         // init hw mem hole here
+        /* DramHoleValid bit only can be set after MemClrStatus is set by Hardware */
+	if(!is_cpu_pre_e0())
+	        set_hw_mem_hole(controllers, ctrl);
+#endif
+
+	//FIXME add enable node interleaving here -- yhlu
+	/*needed?
+		1. check how many nodes we have , if not all has ram installed get out
+		2. check cs_base lo is 0, node 0 f2 0x40,,,,, if any one is not using lo is CS_BASE, get out
+		3. check if other node is the same as node 0 about f2 0x40,,,,, otherwise get out
+		4. if all ready enable node_interleaving in f1 0x40..... of every node
+		5. for node interleaving we need to set mem hole to every node ( need recalcute hole offset in f0 for every node)
+	*/
+
+}
+
+static void set_sysinfo_in_ram(unsigned val)
+{
+}
+
+static void fill_mem_ctrl(int controllers, struct mem_controller *ctrl_a, const u16 *spd_addr)
+{
+        int i;
+        int j;
+        struct mem_controller *ctrl;
+        for(i=0;i<controllers; i++) {
+                ctrl = &ctrl_a[i];
+                ctrl->node_id = i;
+                ctrl->f0 = PCI_DEV(0, 0x18+i, 0);
+                ctrl->f1 = PCI_DEV(0, 0x18+i, 1);
+                ctrl->f2 = PCI_DEV(0, 0x18+i, 2);
+                ctrl->f3 = PCI_DEV(0, 0x18+i, 3);
+
+                if(spd_addr == (void *)0) continue;
+
+                for(j=0;j<DIMM_SOCKETS;j++) {
+                        ctrl->channel0[j] = spd_addr[(i*2+0)*DIMM_SOCKETS + j];
+                        ctrl->channel1[j] = spd_addr[(i*2+1)*DIMM_SOCKETS + j];
+                }
+        }
+}
+#endif
+

Added: coreboot-v3/southbridge/nvidia/mcp55/Makefile
===================================================================
--- coreboot-v3/southbridge/nvidia/mcp55/Makefile	                        (rev 0)
+++ coreboot-v3/southbridge/nvidia/mcp55/Makefile	2008-08-02 03:34:05 UTC (rev 713)
@@ -0,0 +1,32 @@
+##
+## This file is part of the coreboot project.
+##
+## Copyright (C) 2007 coresystems GmbH
+## (Written by Stefan Reinauer <stepan at coresystems.de> for coresystems GmbH)
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, write to the Free Software
+## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+##
+
+ifeq ($(CONFIG_SOUTHBRIDGE_NVIDIA_MCP55),y)
+
+STAGE2_CHIPSET_OBJ += $(obj)/southbridge/nvidia/mcp55/mcp55.o
+
+ifeq ($(CONFIG_PIRQ_TABLE),y)
+STAGE2_CHIPSET_OBJ += $(obj)/southbridge/nvidia/mcp55/irq_tables.o
+endif
+
+STAGE0_CHIPSET_OBJ += $(obj)/southbridge/nvidia/mcp55/stage1.o
+
+endif

Added: coreboot-v3/southbridge/nvidia/mcp55/dts
===================================================================
--- coreboot-v3/southbridge/nvidia/mcp55/dts	                        (rev 0)
+++ coreboot-v3/southbridge/nvidia/mcp55/dts	2008-08-02 03:34:05 UTC (rev 713)
@@ -0,0 +1,30 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2007 YingHai Lu
+ * Copyright (C) 2008 Ronald G. Minnich <rminnich at gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+ */
+
+{
+	ide0_enable = "0";
+	ide1_enable = "0";
+	sata0_enable = "0";
+	sata1_enable = "0";
+	mac_eeprom_smbus = "0";
+	mac_eeprom_addr "0";
+};
+

Added: coreboot-v3/southbridge/nvidia/mcp55/mcp55.c
===================================================================
--- coreboot-v3/southbridge/nvidia/mcp55/mcp55.c	                        (rev 0)
+++ coreboot-v3/southbridge/nvidia/mcp55/mcp55.c	2008-08-02 03:34:05 UTC (rev 713)
@@ -0,0 +1,257 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2004 Tyan Computer
+ * Written by Yinghai Lu <yhlu at tyan.com> for Tyan Computer.
+ * Copyright (C) 2006,2007 AMD
+ * Written by Yinghai Lu <yinghai.lu at amd.com> for AMD.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+ */
+
+#include <console/console.h>
+
+#include <arch/io.h>
+
+#include <device/device.h>
+#include <device/pci.h>
+#include <device/pci_ids.h>
+#include <device/pci_ops.h>
+#include "mcp55.h"
+
+static uint32_t final_reg;
+
+static device_t find_lpc_dev( device_t dev,  unsigned devfn)
+{
+
+	device_t lpc_dev;
+
+	lpc_dev = dev_find_slot(dev->bus->secondary, devfn);
+
+	if ( !lpc_dev ) return lpc_dev;
+
+	if ((lpc_dev->vendor != PCI_VENDOR_ID_NVIDIA) || (
+		(lpc_dev->device < PCI_DEVICE_ID_NVIDIA_MCP55_LPC) ||
+		(lpc_dev->device > PCI_DEVICE_ID_NVIDIA_MCP55_PRO)
+		) ) {
+			uint32_t id;
+			id = pci_read_config32(lpc_dev, PCI_VENDOR_ID);
+			if ( (id < (PCI_VENDOR_ID_NVIDIA | (PCI_DEVICE_ID_NVIDIA_MCP55_LPC << 16))) ||
+				(id > (PCI_VENDOR_ID_NVIDIA | (PCI_DEVICE_ID_NVIDIA_MCP55_PRO << 16)))
+				) {
+				lpc_dev = 0;
+			}
+	}
+
+	return lpc_dev;
+}
+
+static void mcp55_enable(device_t dev)
+{
+	device_t lpc_dev = 0;
+	device_t sm_dev = 0;
+	unsigned index = 0;
+	unsigned index2 = 0;
+	uint32_t reg_old, reg;
+	uint8_t byte;
+	unsigned deviceid;
+	unsigned vendorid;
+
+	struct southbridge_nvidia_mcp55_config *conf;
+	conf = dev->chip_info;
+	int i;
+
+	unsigned devfn;
+
+	if(dev->device==0x0000) {
+		vendorid = pci_read_config32(dev, PCI_VENDOR_ID);
+		deviceid = (vendorid>>16) & 0xffff;
+//		vendorid &= 0xffff;
+	} else {
+//		vendorid = dev->vendor;
+		deviceid = dev->device;
+	}
+
+	devfn = (dev->path.u.pci.devfn) & ~7;
+	switch(deviceid) {
+		case PCI_DEVICE_ID_NVIDIA_MCP55_HT:
+			return;
+
+		case PCI_DEVICE_ID_NVIDIA_MCP55_SM2://?
+			index = 16;
+			break;
+		case PCI_DEVICE_ID_NVIDIA_MCP55_USB:
+			devfn -= (1<<3);
+			index = 8;
+			break;
+		case PCI_DEVICE_ID_NVIDIA_MCP55_USB2:
+			devfn -= (1<<3);
+			index = 20;
+			break;
+		case PCI_DEVICE_ID_NVIDIA_MCP55_NIC: //two
+		case PCI_DEVICE_ID_NVIDIA_MCP55_NIC_BRIDGE://two
+			devfn -= (7<<3);
+			index = 10;
+			for(i=0;i<2;i++) {
+				lpc_dev = find_lpc_dev(dev, devfn - (i<<3));
+				if(!lpc_dev) continue;
+				index -= i;
+				devfn -= (i<<3);
+				break;
+			}
+			break;
+		case PCI_DEVICE_ID_NVIDIA_MCP55_AZA:
+			devfn -= (5<<3);
+			index = 11;
+			break;
+		case PCI_DEVICE_ID_NVIDIA_MCP55_IDE:
+			devfn -= (3<<3);
+			index = 14;
+			break;
+		case PCI_DEVICE_ID_NVIDIA_MCP55_SATA0: //three
+		case PCI_DEVICE_ID_NVIDIA_MCP55_SATA1: //three
+			devfn -= (4<<3);
+			index = 22;
+			i = (dev->path.u.pci.devfn) & 7;
+			if(i>0) {
+				index -= (i+3);
+			}
+			break;
+		case PCI_DEVICE_ID_NVIDIA_MCP55_PCI:
+			devfn -= (5<<3);
+			index = 15;
+			break;
+		case PCI_DEVICE_ID_NVIDIA_MCP55_PCIE_A:
+			devfn -= (0x9<<3);  // to LPC
+			index2 = 9;
+			break;
+		case PCI_DEVICE_ID_NVIDIA_MCP55_PCIE_B_C: //two
+			devfn -= (0xa<<3);  // to LPC
+			index2 = 8;
+			for(i=0;i<2;i++) {
+				lpc_dev = find_lpc_dev(dev, devfn - (i<<3));
+				if(!lpc_dev) continue;
+				index2 -= i;
+				devfn -= (i<<3);
+				break;
+			}
+			break;
+		case PCI_DEVICE_ID_NVIDIA_MCP55_PCIE_D:
+			devfn -= (0xc<<3);  // to LPC
+			index2 = 6;
+			break;
+		case PCI_DEVICE_ID_NVIDIA_MCP55_PCIE_E:
+			devfn -= (0xd<<3);  // to LPC
+			index2 = 5;
+			break;
+		case PCI_DEVICE_ID_NVIDIA_MCP55_PCIE_F:
+			devfn -= (0xe<<3);  // to LPC
+			index2 = 4;
+			break;
+		default:
+			index = 0;
+	}
+
+	if(!lpc_dev)
+		lpc_dev = find_lpc_dev(dev, devfn);
+
+	if ( !lpc_dev )	return;
+
+	if(index2!=0) {
+		sm_dev = dev_find_slot(dev->bus->secondary, devfn + 1);
+		if(!sm_dev) return;
+
+		if ( sm_dev ) {
+			reg_old = reg =  pci_read_config32(sm_dev, 0xe4);
+
+			if (!dev->enabled) { //disable it
+				reg |= (1<<index2);
+			}
+
+			if (reg != reg_old) {
+				pci_write_config32(sm_dev, 0xe4, reg);
+			}
+		}
+
+		index2 = 0;
+		return;
+	}
+
+
+	if ( index == 0) {  // for LPC
+
+		// expose ioapic base
+		byte = pci_read_config8(lpc_dev, 0x74);
+		byte |= ((1<<1)); // expose the BAR
+		pci_write_config8(dev, 0x74, byte);
+
+		// expose trap base
+		byte = pci_read_config8(lpc_dev, 0xdd);
+		byte |= ((1<<0)|(1<<3)); // expose the BAR and enable write
+		pci_write_config8(dev, 0xdd, byte);
+
+		return;
+
+	}
+
+	if( index == 16) {
+		sm_dev = dev_find_slot(dev->bus->secondary, devfn + 1);
+		if(!sm_dev) return;
+
+		final_reg = pci_read_config32(sm_dev, 0xe8);
+		final_reg &= ~((1<<16)|(1<<8)|(1<<20)|(1<<14)|(1<<22)|(1<<18)|(1<<17)|(1<<15)|(1<<11)|(1<<10)|(1<<9));
+		pci_write_config32(sm_dev, 0xe8, final_reg); //enable all at first
+#if 0
+		reg_old = reg = pci_read_config32(sm_dev, 0xe4);
+//		reg |= (1<<0);
+		reg &= ~(0x3f<<4);
+		if (reg != reg_old) {
+			printk_debug("mcp55.c pcie enabled\n");
+			pci_write_config32(sm_dev, 0xe4, reg);
+		}
+#endif
+	}
+
+	if (!dev->enabled) {
+		final_reg |= (1 << index);// disable it
+		//The reason for using final_reg, if diable func 1, the func 2 will be func 1 so We need disable them one time.
+	}
+
+	if(index == 9 ) { //NIC1 is the final, We need update final reg to 0xe8
+		sm_dev = dev_find_slot(dev->bus->secondary, devfn + 1);
+		if(!sm_dev) return;
+		reg_old = pci_read_config32(sm_dev, 0xe8);
+		if (final_reg != reg_old) {
+			pci_write_config32(sm_dev, 0xe8, final_reg);
+		}
+
+	}
+
+
+}
+
+struct device_operations nvidia_ops = {
+	.id = {.type = DEVICE_ID_PCI,
+		.u = {.pci = {.vendor = PCI_VENDOR_ID_NVIDIA,
+			      .device = PCI_DEVICE_ID_NVIDIA_MCP55_PCIBRIDGE}}},
+	.constructor			= default_device_constructor,
+	.phase3_scan			= scan_static_bus,
+	.phase4_read_resources		= pci_dev_read_resources,
+	.phase4_set_resources		= pci_dev_set_resources,
+	.phase5_enable_resources	= mcp55_enable,
+	.phase6_init			= NULL,
+};
+
+

Added: coreboot-v3/southbridge/nvidia/mcp55/mcp55.h
===================================================================
--- coreboot-v3/southbridge/nvidia/mcp55/mcp55.h	                        (rev 0)
+++ coreboot-v3/southbridge/nvidia/mcp55/mcp55.h	2008-08-02 03:34:05 UTC (rev 713)
@@ -0,0 +1,27 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2007 AMD
+ * Written by Yinghai Lu <yinghai.lu at amd.com> for AMD.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+ */
+
+#ifndef MCP55_H
+#define MCP55_H
+
+void mcp55_enable(device_t dev);
+
+#endif /* MCP55_H */

Added: coreboot-v3/southbridge/nvidia/mcp55/stage1.c
===================================================================
--- coreboot-v3/southbridge/nvidia/mcp55/stage1.c	                        (rev 0)
+++ coreboot-v3/southbridge/nvidia/mcp55/stage1.c	2008-08-02 03:34:05 UTC (rev 713)
@@ -0,0 +1,427 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2006 AMD
+ * Written by Yinghai Lu <yinghai.lu at amd.com> for AMD.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+ */
+
+static int set_ht_link_mcp55(u8 ht_c_num)
+{
+	unsigned vendorid = 0x10de;
+	unsigned val = 0x01610109;
+	/* Nvidia mcp55 hardcode, hw can not set it automatically */
+	return set_ht_link_buffer_counts_chain(ht_c_num, vendorid, val);
+}
+
+static void setup_ss_table(unsigned index, unsigned where, unsigned control, const unsigned int *register_values, int max)
+{
+	int i;
+
+	unsigned val;
+
+	val = inl(control);
+	val &= 0xfffffffe;
+	outl(val, control);
+
+	outl(0, index); //index
+	for(i = 0; i < max; i++) {
+		unsigned long reg;
+		reg = register_values[i];
+		outl(reg, where);
+	}
+
+	val = inl(control);
+	val |= 1;
+	outl(val, control);
+
+}
+
+/* SIZE 0x100 */
+#define ANACTRL_IO_BASE	0x2800
+#define ANACTRL_REG_POS	0x68
+
+/* SIZE 0x100 */
+#define SYSCTRL_IO_BASE	0x2400
+#define SYSCTRL_REG_POS	0x64
+
+/* SIZE 0x100 */
+#define ACPICTRL_IO_BASE	0x2000
+#define ACPICTRL_REG_POS	0x60
+
+/*
+	16 1 1 1 1 8 :0
+	16 0 4 0 0 8 :1
+	16 0 4 2 2 4 :2
+	 4 4 4 4 4 8 :3
+	 8 8 4 0 0 8 :4
+	 8 0 4 4 4 8 :5
+*/
+
+#ifndef MCP55_PCI_E_X_0
+	#define MCP55_PCI_E_X_0	4
+#endif
+#ifndef MCP55_PCI_E_X_1
+	#define MCP55_PCI_E_X_1	4
+#endif
+#ifndef MCP55_PCI_E_X_2
+	#define MCP55_PCI_E_X_2	4
+#endif
+#ifndef MCP55_PCI_E_X_3
+	#define MCP55_PCI_E_X_3	4
+#endif
+
+#ifndef MCP55_USE_NIC
+	#define MCP55_USE_NIC	0
+#endif
+
+#ifndef MCP55_USE_AZA
+	#define MCP55_USE_AZA	0
+#endif
+
+#define MCP55_CHIP_REV	3
+
+static void mcp55_early_set_port(unsigned mcp55_num, unsigned *busn, unsigned *devn, unsigned *io_base)
+{
+
+	static const unsigned int ctrl_devport_conf[] = {
+		PCI_ADDR(0, 1, 1, ANACTRL_REG_POS), ~(0x0000ff00), ANACTRL_IO_BASE,
+		PCI_ADDR(0, 1, 1, SYSCTRL_REG_POS), ~(0x0000ff00), SYSCTRL_IO_BASE,
+		PCI_ADDR(0, 1, 1, ACPICTRL_REG_POS), ~(0x0000ff00), ACPICTRL_IO_BASE,
+	};
+
+	int j;
+	for(j = 0; j < mcp55_num; j++ ) {
+		setup_resource_map_offset(ctrl_devport_conf,
+			sizeof(ctrl_devport_conf)/sizeof(ctrl_devport_conf[0]),
+			PCI_DEV(busn[j], devn[j], 0) , io_base[j]);
+	}
+}
+
+static void mcp55_early_clear_port(unsigned mcp55_num, unsigned *busn, unsigned *devn, unsigned *io_base)
+{
+
+	static const unsigned int ctrl_devport_conf_clear[] = {
+		PCI_ADDR(0, 1, 1, ANACTRL_REG_POS), ~(0x0000ff00), 0,
+		PCI_ADDR(0, 1, 1, SYSCTRL_REG_POS), ~(0x0000ff00), 0,
+		PCI_ADDR(0, 1, 1, ACPICTRL_REG_POS), ~(0x0000ff00), 0,
+	};
+
+	int j;
+	for(j = 0; j < mcp55_num; j++ ) {
+		setup_resource_map_offset(ctrl_devport_conf_clear,
+			sizeof(ctrl_devport_conf_clear)/sizeof(ctrl_devport_conf_clear[0]),
+			PCI_DEV(busn[j], devn[j], 0) , io_base[j]);
+	}
+
+
+}
+static void delayx(u8 value) {
+#if 1
+	int i;
+	for(i=0;i<0x8000;i++) {
+		outb(value, 0x80);
+	}
+#endif
+}
+
+static void mcp55_early_pcie_setup(unsigned busnx, unsigned devnx, unsigned anactrl_io_base, unsigned pci_e_x)
+{
+	u32 tgio_ctrl;
+	u32 pll_ctrl;
+	u32 dword;
+	int i;
+	device_t dev;
+	dev = PCI_DEV(busnx, devnx+1, 1);
+	dword = pci_read_config32(dev, 0xe4);
+	dword |= 0x3f0; // disable it at first
+	pci_write_config32(dev, 0xe4, dword);
+
+	for(i=0; i<3; i++) {
+		tgio_ctrl = inl(anactrl_io_base + 0xcc);
+		tgio_ctrl &= ~(3<<9);
+		tgio_ctrl |= (i<<9);
+		outl(tgio_ctrl, anactrl_io_base + 0xcc);
+		pll_ctrl = inl(anactrl_io_base + 0x30);
+		pll_ctrl |= (1<<31);
+		outl(pll_ctrl, anactrl_io_base + 0x30);
+		do {
+			pll_ctrl = inl(anactrl_io_base + 0x30);
+		} while (!(pll_ctrl & 1));
+	}
+	tgio_ctrl = inl(anactrl_io_base + 0xcc);
+	tgio_ctrl &= ~((7<<4)|(1<<8));
+	tgio_ctrl |= (pci_e_x<<4)|(1<<8);
+	outl(tgio_ctrl, anactrl_io_base + 0xcc);
+
+//	wait 100us
+	delayx(1);
+
+	dword = pci_read_config32(dev, 0xe4);
+	dword &= ~(0x3f0); // enable
+	pci_write_config32(dev, 0xe4, dword);
+
+//	need to wait 100ms
+	delayx(1000);
+}
+
+static void mcp55_early_setup(unsigned mcp55_num, unsigned *busn, unsigned *devn, unsigned *io_base, unsigned *pci_e_x)
+{
+
+    static const unsigned int ctrl_conf_1[] = {
+	RES_PORT_IO_32, ACPICTRL_IO_BASE + 0x10, 0x0007ffff, 0xff78000,
+	RES_PORT_IO_32, ACPICTRL_IO_BASE + 0xa4, 0xffedffff, 0x0012000,
+	RES_PORT_IO_32, ACPICTRL_IO_BASE + 0xac, 0xfffffdff, 0x0000200,
+	RES_PORT_IO_32, ACPICTRL_IO_BASE + 0xb4, 0xfffffffd, 0x0000002,
+
+	RES_PORT_IO_32, ANACTRL_IO_BASE + 0x24, 0xc0f0f08f, 0x26020230,
+	RES_PORT_IO_32, ANACTRL_IO_BASE + 0x34, 0x00000000, 0x22222222,
+	RES_PORT_IO_32, ANACTRL_IO_BASE + 0x08, 0x7FFFFFFF, 0x00000000,
+	RES_PORT_IO_32, ANACTRL_IO_BASE + 0x2C, 0x7FFFFFFF, 0x80000000,
+	RES_PORT_IO_32, ANACTRL_IO_BASE + 0xCC, 0xFFFFF9FF, 0x00000000,
+	RES_PORT_IO_32, ANACTRL_IO_BASE + 0x30, 0x8FFFFFFF, 0x40000000,
+	RES_PORT_IO_32, ANACTRL_IO_BASE + 0xCC, 0xFFFFF9FF, 0x00000200,
+	RES_PORT_IO_32, ANACTRL_IO_BASE + 0x30, 0x8FFFFFFF, 0x40000000,
+	RES_PORT_IO_32, ANACTRL_IO_BASE + 0xCC, 0xFFFFF9FF, 0x00000400,
+	RES_PORT_IO_32, ANACTRL_IO_BASE + 0x30, 0x8FFFFFFF, 0x40000000,
+	RES_PORT_IO_32, ANACTRL_IO_BASE + 0x74, 0xFFFF0FF5, 0x0000F000,
+	RES_PORT_IO_32, ANACTRL_IO_BASE + 0x78, 0xFF00FF00, 0x00100010,
+	RES_PORT_IO_32, ANACTRL_IO_BASE + 0x7C, 0xFF0FF0FF, 0x00500500,
+	RES_PORT_IO_32, ANACTRL_IO_BASE + 0x80, 0xFFFFFFE7, 0x00000000,
+	RES_PORT_IO_32, ANACTRL_IO_BASE + 0x60, 0xFFCFFFFF, 0x00300000,
+	RES_PORT_IO_32, ANACTRL_IO_BASE + 0x90, 0xFFFF00FF, 0x0000FF00,
+	RES_PORT_IO_32, ANACTRL_IO_BASE + 0x9C, 0xFF00FFFF, 0x00070000,
+
+	RES_PCI_IO, PCI_ADDR(0, 0, 0, 0x40), 0x00000000, 0xCB8410DE,
+	RES_PCI_IO, PCI_ADDR(0, 0, 0, 0x48), 0xFFFFDCED, 0x00002002,
+	RES_PCI_IO, PCI_ADDR(0, 0, 0, 0x78), 0xFFFFFF8E, 0x00000011,
+	RES_PCI_IO, PCI_ADDR(0, 0, 0, 0x80), 0xFFFF0000, 0x00009923,
+	RES_PCI_IO, PCI_ADDR(0, 0, 0, 0x88), 0xFFFFFFFE, 0x00000000,
+	RES_PCI_IO, PCI_ADDR(0, 0, 0, 0x8C), 0xFFFF0000, 0x0000007F,
+	RES_PCI_IO, PCI_ADDR(0, 0, 0, 0xDC), 0xFFFEFFFF, 0x00010000,
+
+	RES_PCI_IO, PCI_ADDR(0, 1, 0, 0x40), 0x00000000, 0xCB8410DE,
+	RES_PCI_IO, PCI_ADDR(0, 1, 0, 0x74), 0xFFFFFF7B, 0x00000084,
+	RES_PCI_IO, PCI_ADDR(0, 1, 0, 0xF8), 0xFFFFFFCF, 0x00000010,
+
+	RES_PCI_IO, PCI_ADDR(0, 1, 1, 0xC4), 0xFFFFFFFE, 0x00000001,
+	RES_PCI_IO, PCI_ADDR(0, 1, 1, 0xF0), 0x7FFFFFFD, 0x00000002,
+	RES_PCI_IO, PCI_ADDR(0, 1, 1, 0xF8), 0xFFFFFFCF, 0x00000010,
+
+	RES_PCI_IO, PCI_ADDR(0, 8, 0, 0x40), 0x00000000, 0xCB8410DE,
+	RES_PCI_IO, PCI_ADDR(0, 8, 0, 0x68), 0xFFFFFF00, 0x000000FF,
+	RES_PCI_IO, PCI_ADDR(0, 8, 0, 0xF8), 0xFFFFFFBF, 0x00000040,//Enable bridge mode
+
+	RES_PCI_IO, PCI_ADDR(0, 9, 0, 0x40), 0x00000000, 0xCB8410DE,
+	RES_PCI_IO, PCI_ADDR(0, 9, 0, 0x68), 0xFFFFFF00, 0x000000FF,
+	RES_PCI_IO, PCI_ADDR(0, 9, 0, 0xF8), 0xFFFFFFBF, 0x00000040,//Enable bridge mode
+    };
+
+    static const unsigned int ctrl_conf_1_1[] = {
+	RES_PCI_IO, PCI_ADDR(0, 5, 0, 0x40), 0x00000000, 0xCB8410DE,
+	RES_PCI_IO, PCI_ADDR(0, 5, 0, 0x50), 0xFFFFFFFC, 0x00000003,
+	RES_PCI_IO, PCI_ADDR(0, 5, 0, 0x64), 0xFFFFFFFE, 0x00000001,
+	RES_PCI_IO, PCI_ADDR(0, 5, 0, 0x70), 0xFFF0FFFF, 0x00040000,
+	RES_PCI_IO, PCI_ADDR(0, 5, 0, 0xAC), 0xFFFFF0FF, 0x00000100,
+	RES_PCI_IO, PCI_ADDR(0, 5, 0, 0x7C), 0xFFFFFFEF, 0x00000000,
+	RES_PCI_IO, PCI_ADDR(0, 5, 0, 0xC8), 0xFF00FF00, 0x000A000A,
+	RES_PCI_IO, PCI_ADDR(0, 5, 0, 0xD0), 0xF0FFFFFF, 0x03000000,
+	RES_PCI_IO, PCI_ADDR(0, 5, 0, 0xE0), 0xF0FFFFFF, 0x03000000,
+    };
+
+
+    static const unsigned int ctrl_conf_mcp55_only[] = {
+	RES_PCI_IO, PCI_ADDR(0, 1, 1, 0x40), 0x00000000, 0xCB8410DE,
+	RES_PCI_IO, PCI_ADDR(0, 1, 1, 0xE0), 0xFFFFFEFF, 0x00000000,
+	RES_PCI_IO, PCI_ADDR(0, 1, 1, 0xE4), 0xFFFFFFFB, 0x00000000,
+	RES_PCI_IO, PCI_ADDR(0, 1, 1, 0xE8), 0xFFA9C8FF, 0x00003000,
+
+	RES_PCI_IO, PCI_ADDR(0, 4, 0, 0x40), 0x00000000, 0xCB8410DE,
+	RES_PCI_IO, PCI_ADDR(0, 4, 0, 0xF8), 0xFFFFFFCF, 0x00000010,
+
+	RES_PCI_IO, PCI_ADDR(0, 2, 0, 0x40), 0x00000000, 0xCB8410DE,
+
+	RES_PCI_IO, PCI_ADDR(0, 2, 1, 0x40), 0x00000000, 0xCB8410DE,
+	RES_PCI_IO, PCI_ADDR(0, 2, 1, 0x64), 0xF87FFFFF, 0x05000000,
+	RES_PCI_IO, PCI_ADDR(0, 2, 1, 0x78), 0xFFC07FFF, 0x00360000,
+	RES_PCI_IO, PCI_ADDR(0, 2, 1, 0x68), 0xFE00D03F, 0x013F2C00,
+	RES_PCI_IO, PCI_ADDR(0, 2, 1, 0x70), 0xFFF7FFFF, 0x00080000,
+	RES_PCI_IO, PCI_ADDR(0, 2, 1, 0x7C), 0xFFFFF00F, 0x00000570,
+	RES_PCI_IO, PCI_ADDR(0, 2, 1, 0xF8), 0xFFFFFFCF, 0x00000010,
+
+	RES_PCI_IO, PCI_ADDR(0, 6, 0, 0x04), 0xFFFFFEFB, 0x00000104,
+	RES_PCI_IO, PCI_ADDR(0, 6, 0, 0x3C), 0xF5FFFFFF, 0x0A000000,
+	RES_PCI_IO, PCI_ADDR(0, 6, 0, 0x40), 0x00C8FFFF, 0x07330000,
+	RES_PCI_IO, PCI_ADDR(0, 6, 0, 0x48), 0xFFFFFFF8, 0x00000005,
+	RES_PCI_IO, PCI_ADDR(0, 6, 0, 0x4C), 0xFE02FFFF, 0x004C0000,
+	RES_PCI_IO, PCI_ADDR(0, 6, 0, 0x74), 0xFFFFFFC0, 0x00000000,
+	RES_PCI_IO, PCI_ADDR(0, 6, 0, 0xC0), 0x00000000, 0xCB8410DE,
+	RES_PCI_IO, PCI_ADDR(0, 6, 0, 0xC4), 0xFFFFFFF8, 0x00000007,
+
+	RES_PCI_IO, PCI_ADDR(0, 1, 0, 0x78), 0xC0FFFFFF, 0x19000000,
+
+#if MCP55_USE_AZA == 1
+	RES_PCI_IO, PCI_ADDR(0, 6, 1, 0x40), 0x00000000, 0xCB8410DE,
+
+//	RES_PCI_IO, PCI_ADDR(0, 1, 1, 0xE4), ~(1<<14), 1<<14,
+#endif
+// play a while with GPIO in MCP55
+#ifdef MCP55_MB_SETUP
+	MCP55_MB_SETUP
+#endif
+
+#if MCP55_USE_AZA == 1
+	RES_PORT_IO_8, SYSCTRL_IO_BASE + 0xc0+ 21, ~(3<<2), (2<<2),
+	RES_PORT_IO_8, SYSCTRL_IO_BASE + 0xc0+ 22, ~(3<<2), (2<<2),
+	RES_PORT_IO_8, SYSCTRL_IO_BASE + 0xc0+ 46, ~(3<<2), (2<<2),
+#endif
+
+
+    };
+
+    static const unsigned int ctrl_conf_master_only[] = {
+
+	RES_PORT_IO_32, ACPICTRL_IO_BASE + 0x80, 0xEFFFFFF, 0x01000000,
+
+	//Master MCP55 ????YHLU
+	RES_PORT_IO_8, SYSCTRL_IO_BASE + 0xc0+ 0, ~(3<<2), (0<<2),
+
+    };
+
+    static const unsigned int ctrl_conf_2[] = {
+	/* I didn't put pcie related stuff here */
+
+	RES_PCI_IO, PCI_ADDR(0, 0, 0, 0x74), 0xFFFFF00F, 0x000009D0,
+	RES_PCI_IO, PCI_ADDR(0, 1, 0, 0x74), 0xFFFF7FFF, 0x00008000,
+
+	RES_PORT_IO_32, SYSCTRL_IO_BASE + 0x48, 0xFFFEFFFF, 0x00010000,
+
+	RES_PORT_IO_32, ANACTRL_IO_BASE + 0x60, 0xFFFFFF00, 0x00000012,
+
+
+#if MCP55_USE_NIC == 1
+	RES_PCI_IO, PCI_ADDR(0, 1, 1, 0xe4), ~((1<<22)|(1<<20)), (1<<22)|(1<<20),
+
+	RES_PORT_IO_8, SYSCTRL_IO_BASE + 0xc0+ 4, ~(0xff),  ((0<<4)|(1<<2)|(0<<0)),
+	RES_PORT_IO_8, SYSCTRL_IO_BASE + 0xc0+ 4, ~(0xff),  ((0<<4)|(1<<2)|(1<<0)),
+#endif
+
+    };
+
+
+	int j, i;
+
+	for(j=0; j<mcp55_num; j++) {
+		mcp55_early_pcie_setup(busn[j], devn[j], io_base[j] + ANACTRL_IO_BASE, pci_e_x[j]);
+
+		setup_resource_map_x_offset(ctrl_conf_1, sizeof(ctrl_conf_1)/sizeof(ctrl_conf_1[0]),
+				PCI_DEV(busn[j], devn[j], 0), io_base[j]);
+		for(i=0; i<3; i++) { // three SATA
+			setup_resource_map_x_offset(ctrl_conf_1_1, sizeof(ctrl_conf_1_1)/sizeof(ctrl_conf_1_1[0]),
+				PCI_DEV(busn[j], devn[j], i), io_base[j]);
+		}
+		if(busn[j] == 0) {
+			setup_resource_map_x_offset(ctrl_conf_mcp55_only, sizeof(ctrl_conf_mcp55_only)/sizeof(ctrl_conf_mcp55_only[0]),
+				PCI_DEV(busn[j], devn[j], 0), io_base[j]);
+		}
+
+		if( (busn[j] == 0) && (mcp55_num>1) ) {
+			setup_resource_map_x_offset(ctrl_conf_master_only, sizeof(ctrl_conf_master_only)/sizeof(ctrl_conf_master_only[0]),
+				PCI_DEV(busn[j], devn[j], 0), io_base[j]);
+		}
+
+		setup_resource_map_x_offset(ctrl_conf_2, sizeof(ctrl_conf_2)/sizeof(ctrl_conf_2[0]),
+				PCI_DEV(busn[j], devn[j], 0), io_base[j]);
+
+	}
+
+#if 0
+	for(j=0; j< mcp55_num; j++) {
+		// PCI-E (XSPLL) SS table 0x40, x044, 0x48
+		// SATA  (SPPLL) SS table 0xb0, 0xb4, 0xb8
+		// CPU   (PPLL)  SS table 0xc0, 0xc4, 0xc8
+		setup_ss_table(io_base[j] + ANACTRL_IO_BASE+0x40, io_base[j] + ANACTRL_IO_BASE+0x44,
+			io_base[j] + ANACTRL_IO_BASE+0x48, pcie_ss_tbl, 64);
+		setup_ss_table(io_base[j] + ANACTRL_IO_BASE+0xb0, io_base[j] + ANACTRL_IO_BASE+0xb4,
+			io_base[j] + ANACTRL_IO_BASE+0xb8, sata_ss_tbl, 64);
+		setup_ss_table(io_base[j] + ANACTRL_IO_BASE+0xc0, io_base[j] + ANACTRL_IO_BASE+0xc4,
+			io_base[j] + ANACTRL_IO_BASE+0xc8, cpu_ss_tbl, 64);
+	}
+#endif
+
+}
+
+#ifndef HT_CHAIN_NUM_MAX
+
+#define HT_CHAIN_NUM_MAX	4
+#define HT_CHAIN_BUSN_D	0x40
+#define HT_CHAIN_IOBASE_D	0x4000
+
+#endif
+
+static int mcp55_early_setup_x(void)
+{
+	/*find out how many mcp55 we have */
+	unsigned busn[HT_CHAIN_NUM_MAX];
+	unsigned devn[HT_CHAIN_NUM_MAX];
+	unsigned io_base[HT_CHAIN_NUM_MAX];
+	/*
+		FIXME: May have problem if there is different MCP55 HTX card with different PCI_E lane allocation
+		Need to use same trick about pci1234 to verify node/link connection
+	*/
+	unsigned pci_e_x[HT_CHAIN_NUM_MAX] = {MCP55_PCI_E_X_0, MCP55_PCI_E_X_1, MCP55_PCI_E_X_2, MCP55_PCI_E_X_3 };
+	int mcp55_num = 0;
+	unsigned busnx;
+	unsigned devnx;
+	int ht_c_index,j;
+
+	/* FIXME: multi pci segment handling */
+
+	/* Any system that only have IO55 without MCP55? */
+	for(ht_c_index = 0; ht_c_index<HT_CHAIN_NUM_MAX; ht_c_index++) {
+		busnx = ht_c_index * HT_CHAIN_BUSN_D;
+		for(devnx=0;devnx<0x20;devnx++) {
+			u32 id;
+			device_t dev;
+			dev = PCI_DEV(busnx, devnx, 0);
+			id = pci_read_config32(dev, PCI_VENDOR_ID);
+			if(id == 0x036910de) {
+				busn[mcp55_num] = busnx;
+				devn[mcp55_num] = devnx;
+				io_base[mcp55_num] = ht_c_index * HT_CHAIN_IOBASE_D; // we may have ht chain other than MCP55
+				mcp55_num++;
+				if(mcp55_num == MCP55_NUM) goto out;
+				break; // only one MCP55 on one chain
+			}
+		}
+	}
+
+out:
+	print_debug("mcp55_num:"); print_debug_hex8(mcp55_num); print_debug("\r\n");
+
+	mcp55_early_set_port(mcp55_num, busn, devn, io_base);
+	mcp55_early_setup(mcp55_num, busn, devn, io_base, pci_e_x);
+
+	mcp55_early_clear_port(mcp55_num, busn, devn, io_base);
+
+//	set_ht_link_mcp55(HT_CHAIN_NUM_MAX);
+
+	return 0;
+
+}
+
+
+





More information about the coreboot mailing list