mirror of
https://github.com/fail0verflow/switch-coreboot.git
synced 2025-05-04 01:39:18 -04:00
Signed-off-by: Ronald G. Minnich <rminnich@gmail.com> Acked-by: Peter Stuge <peter@stuge.se> git-svn-id: svn://coreboot.org/repository/coreboot-v3@722 f3766cd6-281f-0410-b1cd-43a5c92072e9
2351 lines
65 KiB
C
2351 lines
65 KiB
C
/*
|
|
* 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 <macros.h>
|
|
#include <spd.h>
|
|
#include <cpu.h>
|
|
#include <msr.h>
|
|
#include <amd/k8/k8.h>
|
|
#include <amd/k8/sysconf.h>
|
|
#include <device/pci.h>
|
|
#include <mc146818rtc.h>
|
|
#include <lib.h>
|
|
|
|
#ifndef QRANK_DIMM_SUPPORT
|
|
#define QRANK_DIMM_SUPPORT 0
|
|
#endif
|
|
|
|
#ifndef HW_MEM_HOLE_SIZEK
|
|
#define HW_MEM_HOLE_SIZEK 64*1024*1024
|
|
#endif
|
|
|
|
|
|
/* now THIS is a kludge but ... it will do for now until we work this out. */
|
|
extern const struct pci_bus_operations pci_cf8_conf1;
|
|
static void hard_reset(void);
|
|
|
|
#define pci_read_config32(bus, dev, where) pci_cf8_conf1.read32(NULL, bus, dev, where)
|
|
#define pci_write_config32(bus, dev, where, what) pci_cf8_conf1.write32(NULL, bus, dev, where, what)
|
|
|
|
|
|
static int controller_present(const struct mem_controller *ctrl)
|
|
{
|
|
return pci_read_config32(0, ctrl->f0, 0) == 0x11001022;
|
|
}
|
|
|
|
static void sdram_set_registers(const struct mem_controller *ctrl)
|
|
{
|
|
static struct rmap rm[] = {
|
|
/* 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.
|
|
*/
|
|
PCM(0, 0x18, 1, 0x44, 0x0000f8f8, 0x00000000),
|
|
PCM(0, 0x18, 1, 0x4C, 0x0000f8f8, 0x00000001),
|
|
PCM(0, 0x18, 1, 0x54, 0x0000f8f8, 0x00000002),
|
|
PCM(0, 0x18, 1, 0x5C, 0x0000f8f8, 0x00000003),
|
|
PCM(0, 0x18, 1, 0x64, 0x0000f8f8, 0x00000004),
|
|
PCM(0, 0x18, 1, 0x6C, 0x0000f8f8, 0x00000005),
|
|
PCM(0, 0x18, 1, 0x74, 0x0000f8f8, 0x00000006),
|
|
PCM(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.
|
|
*/
|
|
PCM(0, 0x18, 1, 0x40, 0x0000f8fc, 0x00000000),
|
|
PCM(0, 0x18, 1, 0x48, 0x0000f8fc, 0x00000000),
|
|
PCM(0, 0x18, 1, 0x50, 0x0000f8fc, 0x00000000),
|
|
PCM(0, 0x18, 1, 0x58, 0x0000f8fc, 0x00000000),
|
|
PCM(0, 0x18, 1, 0x60, 0x0000f8fc, 0x00000000),
|
|
PCM(0, 0x18, 1, 0x68, 0x0000f8fc, 0x00000000),
|
|
PCM(0, 0x18, 1, 0x70, 0x0000f8fc, 0x00000000),
|
|
PCM(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.
|
|
*/
|
|
PCM(0, 0x18, 2, 0x40, 0x001f01fe, 0x00000000),
|
|
PCM(0, 0x18, 2, 0x44, 0x001f01fe, 0x00000000),
|
|
PCM(0, 0x18, 2, 0x48, 0x001f01fe, 0x00000000),
|
|
PCM(0, 0x18, 2, 0x4C, 0x001f01fe, 0x00000000),
|
|
PCM(0, 0x18, 2, 0x50, 0x001f01fe, 0x00000000),
|
|
PCM(0, 0x18, 2, 0x54, 0x001f01fe, 0x00000000),
|
|
PCM(0, 0x18, 2, 0x58, 0x001f01fe, 0x00000000),
|
|
PCM(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
|
|
*
|
|
*/
|
|
PCM(0, 0x18, 2, 0x60, 0xC01f01ff, 0x00000000),
|
|
PCM(0, 0x18, 2, 0x64, 0xC01f01ff, 0x00000000),
|
|
PCM(0, 0x18, 2, 0x68, 0xC01f01ff, 0x00000000),
|
|
PCM(0, 0x18, 2, 0x6C, 0xC01f01ff, 0x00000000),
|
|
PCM(0, 0x18, 2, 0x70, 0xC01f01ff, 0x00000000),
|
|
PCM(0, 0x18, 2, 0x74, 0xC01f01ff, 0x00000000),
|
|
PCM(0, 0x18, 2, 0x78, 0xC01f01ff, 0x00000000),
|
|
PCM(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]
|
|
*/
|
|
PCM(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
|
|
*/
|
|
PCM(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
|
|
*/
|
|
PCM(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
|
|
*/
|
|
PCM(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
|
|
*/
|
|
PCM(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
|
|
*/
|
|
PCM(0, 0x18, 2, 0x98, 0xfc00ffff, 0x00000000),
|
|
/* MCA NB Status Low reg */
|
|
PCM(0, 0x18, 3, 0x48, 0x00f00000, 0x00000000),
|
|
/* MCA NB Status high reg */
|
|
PCM(0, 0x18, 3, 0x4c, 0x01801e8c, 0x00000000),
|
|
/* MCA NB address Low reg */
|
|
PCM(0, 0x18, 3, 0x50, 0x00000007, 0x00000000),
|
|
/* MCA NB address high reg */
|
|
PCM(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
|
|
*/
|
|
PCM(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
|
|
*/
|
|
PCM(0, 0x18, 3, 0x5C, 0x0000003e, 0x00000000),
|
|
/* DRAM Scrub Address High Register
|
|
* F3:0x60
|
|
* [ 7: 0] DRAM Scrubb Address 39-32
|
|
* [31: 8] Reserved
|
|
*/
|
|
PCM(0, 0x18, 3, 0x60, 0xffffff00, 0x00000000),
|
|
};
|
|
int i;
|
|
int max;
|
|
|
|
if (!controller_present(ctrl)) {
|
|
printk(BIOS_DEBUG, "No memory controller present\n");
|
|
return;
|
|
}
|
|
printk(BIOS_SPEW, "setting up CPU 0x%x northbridge registers ", ctrl->node_id);
|
|
max = sizeof(rm)/sizeof(rm[0]);
|
|
setup_resource_map(rm, max);
|
|
printk(BIOS_SPEW, "done.\n");
|
|
}
|
|
|
|
|
|
static void hw_enable_ecc(const struct mem_controller *ctrl)
|
|
{
|
|
u32 dcl, nbcap, opt = 1;
|
|
nbcap = pci_read_config32(0, ctrl->f3, NORTHBRIDGE_CAP);
|
|
dcl = pci_read_config32(0, ctrl->f2, DRAM_CONFIG_LOW);
|
|
dcl &= ~DCL_DimmEccEn;
|
|
if (nbcap & NBCAP_ECC) {
|
|
dcl |= DCL_DimmEccEn;
|
|
}
|
|
if (get_option(&opt, "ECC_memory") || opt) {
|
|
dcl &= ~DCL_DimmEccEn;
|
|
}
|
|
pci_write_config32(0, ctrl->f2, DRAM_CONFIG_LOW, dcl);
|
|
|
|
}
|
|
|
|
static int is_dual_channel(const struct mem_controller *ctrl)
|
|
{
|
|
u32 dcl;
|
|
dcl = pci_read_config32(0, 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(0, 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(0, ctrl->f2, DRAM_CONFIG_LOW);
|
|
return !(dcl & DCL_UnBufDimm);
|
|
}
|
|
|
|
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\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(0, ctrl->f2, DRAM_CSBASE + (((index << 1)+0)<<2), base0);
|
|
pci_write_config32(0, ctrl->f2, DRAM_CSBASE + (((index << 1)+1)<<2), base1);
|
|
#if QRANK_DIMM_SUPPORT == 1
|
|
if(sz.rank == 4) {
|
|
pci_write_config32(0, ctrl->f2, DRAM_CSBASE + (((index << 1)+4)<<2), base0);
|
|
pci_write_config32(0, ctrl->f2, DRAM_CSBASE + (((index << 1)+5)<<2), base1);
|
|
}
|
|
#endif
|
|
|
|
/* Enable the memory clocks for this DIMM */
|
|
if (base0) {
|
|
dch = pci_read_config32(0, 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(0, 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(0, 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(0, 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;
|
|
u32 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_BDF(0, 0x18, 1); device <= PCI_BDF(0, 0x1f, 1); device += PCI_BDF(0, 1, 0)) {
|
|
pci_write_config32(0, device, limit_reg, limit);
|
|
pci_write_config32(0, 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. */
|
|
printk(BIOS_SPEW, "RAM: 0x%x KB\n", tom_k);
|
|
|
|
/* Now set top of memory */
|
|
struct msr 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(0, 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(0, 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\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\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(0, ctrl->f2, DRAM_CSBASE + (index << 2));
|
|
/* Is it enabled? */
|
|
if (!(value & 1)) {
|
|
continue;
|
|
}
|
|
pci_write_config32(0, ctrl->f2, DRAM_CSBASE + (index << 2), csbase);
|
|
pci_write_config32(0, 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 candidate */
|
|
unsigned index, candidate;
|
|
u32 csbase, csmask;
|
|
unsigned size;
|
|
csbase = 0;
|
|
candidate = 0;
|
|
for(index = 0; index < 8; index++) {
|
|
u32 value;
|
|
value = pci_read_config32(0, 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 candidate */
|
|
csbase = value;
|
|
candidate = index;
|
|
}
|
|
/* See if I have found a new candidate */
|
|
if (csbase == 0) {
|
|
break;
|
|
}
|
|
|
|
/* Remember the dimm size */
|
|
size = csbase >> 21;
|
|
|
|
/* Remember I have used this register */
|
|
tom |= (1 << (candidate + 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(0, ctrl->f2, DRAM_CSBASE + (candidate << 2), csbase);
|
|
/* Write the new mask register */
|
|
pci_write_config32(0, ctrl->f2, DRAM_CSMASK + (candidate << 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(0, ctrl->f1, 0x40 + index);
|
|
/* Only look at the limit if the base is enabled */
|
|
if ((base & 3) == 3) {
|
|
limit = pci_read_config32(0, 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;
|
|
int opt = 1;
|
|
|
|
if (get_option(&opt, "interleave_chip_selects") || opt) {
|
|
tom_k = interleave_chip_selects(ctrl);
|
|
} else {
|
|
printk(BIOS_DEBUG, "Interleaving disabled\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(0, ctrl->f2, DRAM_CSBASE + (((index << 1)+0)<<2), 0);
|
|
pci_write_config32(0, 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(0, 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(0, ctrl->f2, DRAM_CONFIG_LOW, dcl);
|
|
if (is_registered(ctrl)) {
|
|
printk(BIOS_DEBUG, "Registered\n");
|
|
} else {
|
|
printk(BIOS_DEBUG, "Unbuffered\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(0, 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\n");
|
|
u32 dcl;
|
|
dcl = pci_read_config32(0, ctrl->f2, DRAM_CONFIG_LOW);
|
|
dcl &= ~DCL_32ByteEn;
|
|
dcl |= DCL_128BitEn;
|
|
pci_write_config32(0, 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\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\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\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\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");
|
|
}
|
|
printk(BIOS_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;
|
|
u32 max_mem_clk_index = 0;
|
|
|
|
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(0, ctrl->f3, NORTHBRIDGE_CAP);
|
|
|
|
min_cycle_time = min_cycle_times[(value >> NBCAP_MEMCLK_SHIFT) & NBCAP_MEMCLK_MASK];
|
|
|
|
get_option(&max_mem_clk_index, "max_mem_clk");
|
|
bios_cycle_time = min_cycle_times[max_mem_clk_index];
|
|
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(0, 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(0, 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(0, ctrl->f2, DRAM_TIMING_LOW);
|
|
value &= ~(DTL_TCL_MASK << DTL_TCL_SHIFT);
|
|
value |= latencies[min_latency - 2] << DTL_TCL_SHIFT;
|
|
pci_write_config32(0, 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(0, 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(0, 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(0, 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(0, 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(0, 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(0, 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(0, 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(0, 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(0, 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(0, 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(0, 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(0, 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(0, 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(0, 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(0, ctrl->f2, DRAM_TIMING_HIGH);
|
|
dth &= ~(DTH_TREF_MASK << DTH_TREF_SHIFT);
|
|
dth |= (param->dch_tref4k << DTH_TREF_SHIFT);
|
|
pci_write_config32(0, 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(0, 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(0, 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(0, ctrl->f2, DRAM_CONFIG_LOW);
|
|
dcl &= ~dimm;
|
|
if (value == 4) {
|
|
dcl |= dimm;
|
|
}
|
|
pci_write_config32(0, 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(0, ctrl->f2, DRAM_CONFIG_LOW);
|
|
dcl &= ~DCL_DimmEccEn;
|
|
pci_write_config32(0, 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(0, 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(0, 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(0, 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(0, 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\n");
|
|
}
|
|
|
|
dth = pci_read_config32(0, ctrl->f2, DRAM_TIMING_HIGH);
|
|
dth &= ~(DTH_TRWT_MASK << DTH_TRWT_SHIFT);
|
|
dth |= ((clocks - DTH_TRWT_BASE) << DTH_TRWT_SHIFT);
|
|
pci_write_config32(0, 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(0, ctrl->f2, DRAM_TIMING_HIGH);
|
|
dth &= ~(DTH_TWCL_MASK << DTH_TWCL_SHIFT);
|
|
dth |= ((clocks - DTH_TWCL_BASE) << DTH_TWCL_SHIFT);
|
|
pci_write_config32(0, 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(0, 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(0, 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(0, 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(0, 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(0, 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(0, 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\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\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 */
|
|
printk(BIOS_ERR, "SPD error - reset\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;
|
|
u32 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(0, ctrl[0].f1, 0x40 + (ii << 3));
|
|
if ((base & ((1<<1)|(1<<0))) != ((1<<1)|(1<<0))) {
|
|
continue;
|
|
}
|
|
limit = pci_read_config32(0, ctrl[0].f1, 0x44 + (ii << 3));
|
|
for(j = 0; j < controllers; j++) {
|
|
pci_write_config32(0, ctrl[j].f1, 0x44 + (ii << 3), limit + (carry_over << 2));
|
|
pci_write_config32(0, ctrl[j].f1, 0x40 + (ii << 3), base + (carry_over << 2));
|
|
}
|
|
}
|
|
limit = pci_read_config32(0, ctrl[0].f1, 0x44 + (i << 3));
|
|
for(j = 0; j < controllers; j++) {
|
|
pci_write_config32(0, ctrl[j].f1, 0x44 + (i << 3), limit + (carry_over << 2));
|
|
}
|
|
dev = ctrl[i].f1;
|
|
base = pci_read_config32(0, 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(0, 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(0, 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;
|
|
#warning "fix HW_MEM_HOLE_SIZE_AUTO_INC 1"
|
|
#define HW_MEM_HOLE_SIZE_AUTO_INC 1
|
|
#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(0, 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(0, ctrl[0].f1, 0x40 + (i << 3));
|
|
if ((base & ((1<<1)|(1<<0))) != ((1<<1)|(1<<0))) {
|
|
continue;
|
|
}
|
|
limit = pci_read_config32(0, 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\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(0, 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(0, ctrl[i].f2, DRAM_CONFIG_HIGH, dch);
|
|
}
|
|
else {
|
|
/* Disable dram receivers */
|
|
u32 dcl;
|
|
dcl = pci_read_config32(0, ctrl[i].f2, DRAM_CONFIG_LOW);
|
|
dcl |= DCL_DisInRcvrs;
|
|
pci_write_config32(0, 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(0, ctrl[i].f2, DRAM_CONFIG_HIGH);
|
|
if (!(dch & DCH_MEMCLK_VALID)) {
|
|
continue;
|
|
}
|
|
|
|
/* Toggle DisDqsHys to get it working */
|
|
dcl = pci_read_config32(0, ctrl[i].f2, DRAM_CONFIG_LOW);
|
|
if (dcl & DCL_DimmEccEn) {
|
|
u32 mnc;
|
|
printk(BIOS_SPEW, "ECC enabled\n");
|
|
mnc = pci_read_config32(0, ctrl[i].f3, MCA_NB_CONFIG);
|
|
mnc |= MNC_ECC_EN;
|
|
if (dcl & DCL_128BitEn) {
|
|
mnc |= MNC_CHIPKILL_EN;
|
|
}
|
|
pci_write_config32(0, ctrl[i].f3, MCA_NB_CONFIG, mnc);
|
|
}
|
|
dcl |= DCL_DisDqsHys;
|
|
pci_write_config32(0, 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(0, 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(0, 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(0, 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\n");
|
|
continue;
|
|
}
|
|
|
|
if (!is_cpu_pre_c0()) {
|
|
/* Wait until it is safe to touch memory */
|
|
dcl &= ~(DCL_MemClrStatus | DCL_DramEnable);
|
|
pci_write_config32(0, ctrl[i].f2, DRAM_CONFIG_LOW, dcl);
|
|
do {
|
|
dcl = pci_read_config32(0, ctrl[i].f2, DRAM_CONFIG_LOW);
|
|
} while(((dcl & DCL_MemClrStatus) == 0) || ((dcl & DCL_DramEnable) == 0) );
|
|
}
|
|
|
|
printk(BIOS_DEBUG, " done\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_BDF(0, 0x18+i, 0);
|
|
ctrl->f1 = PCI_BDF(0, 0x18+i, 1);
|
|
ctrl->f2 = PCI_BDF(0, 0x18+i, 2);
|
|
ctrl->f3 = PCI_BDF(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];
|
|
}
|
|
}
|
|
}
|