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@stuge.se Acked-by: Ronald G. Minnich rminnich@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@0 { }; domain@0 { + pci@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@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@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@lnxi.com for Linux Networx) + * Copyright (C) 2004 YingHai Lu + * Copyright (C) 2007 Ronald G. Minnich rminnich@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@lnxi.com for Linux Networx) + * Copyright (C) 2004 YingHai Lu + * Copyright (C) 2007 Ronald G. Minnich rminnich@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@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@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@tyan.com for Tyan Computer. + * Copyright (C) 2006,2007 AMD + * Written by Yinghai Lu yinghai.lu@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@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@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; + +} + + +