[PATCH] PPC4xx: Add 440SP(e) DDR2 SPD DIMM support

This patch adds support for the DDR2 controller used on the
440SP and 440SPe. It is tested on the Katmai (440SPe) eval
board and works fine with the following DIMM modules:

- Corsair CM2X512-5400C4 (512MByte per DIMM)
- Kingston ValueRAM KVR667D2N5/512 (512MByte per DIMM)
- Kingston ValueRAM KVR667D2N5K2/2G (1GByte per DIMM)

This patch also adds the nice functionality to dynamically
create the TLB entries for the SDRAM (tlb.c). So we should
never run into such problems with wrong (too short) TLB
initialization again on these platforms.

Signed-off-by: Stefan Roese <sr@denx.de>
This commit is contained in:
Stefan Roese 2007-02-20 10:43:34 +01:00
parent 36d830c983
commit 4037ed3b63
3 changed files with 3109 additions and 42 deletions

2759
cpu/ppc4xx/44x_spd_ddr2.c Normal file

File diff suppressed because it is too large Load Diff

184
cpu/ppc4xx/tlb.c Normal file
View File

@ -0,0 +1,184 @@
/*
* (C) Copyright 2007
* Stefan Roese, DENX Software Engineering, sr@denx.de.
*
* See file CREDITS for list of people who contributed to this
* project.
*
* 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., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*/
#include <common.h>
#if defined(CONFIG_440)
#include <ppc4xx.h>
#include <ppc440.h>
#include <asm/io.h>
#include <asm/mmu.h>
typedef struct region {
unsigned long base;
unsigned long size;
unsigned long tlb_word2_i_value;
} region_t;
static int add_tlb_entry(unsigned long base_addr,
unsigned long tlb_word0_size_value,
unsigned long tlb_word2_i_value)
{
int i;
unsigned long tlb_word0_value;
unsigned long tlb_word1_value;
unsigned long tlb_word2_value;
/* First, find the index of a TLB entry not being used */
for (i=0; i<PPC4XX_TLB_SIZE; i++) {
tlb_word0_value = mftlb1(i);
if ((tlb_word0_value & TLB_WORD0_V_MASK) == TLB_WORD0_V_DISABLE)
break;
}
if (i >= PPC4XX_TLB_SIZE)
return -1;
/* Second, create the TLB entry */
tlb_word0_value = TLB_WORD0_EPN_ENCODE(base_addr) | TLB_WORD0_V_ENABLE |
TLB_WORD0_TS_0 | tlb_word0_size_value;
tlb_word1_value = TLB_WORD1_RPN_ENCODE(base_addr) | TLB_WORD1_ERPN_ENCODE(0);
tlb_word2_value = TLB_WORD2_U0_DISABLE | TLB_WORD2_U1_DISABLE |
TLB_WORD2_U2_DISABLE | TLB_WORD2_U3_DISABLE |
TLB_WORD2_W_DISABLE | tlb_word2_i_value |
TLB_WORD2_M_DISABLE | TLB_WORD2_G_DISABLE |
TLB_WORD2_E_DISABLE | TLB_WORD2_UX_ENABLE |
TLB_WORD2_UW_ENABLE | TLB_WORD2_UR_ENABLE |
TLB_WORD2_SX_ENABLE | TLB_WORD2_SW_ENABLE |
TLB_WORD2_SR_ENABLE;
/* Wait for all memory accesses to complete */
sync();
/* Third, add the TLB entries */
mttlb1(i, tlb_word0_value);
mttlb2(i, tlb_word1_value);
mttlb3(i, tlb_word2_value);
/* Execute an ISYNC instruction so that the new TLB entry takes effect */
asm("isync");
return 0;
}
static void program_tlb_addr(unsigned long base_addr, unsigned long mem_size,
unsigned long tlb_word2_i_value)
{
int rc;
int tlb_i;
tlb_i = tlb_word2_i_value;
while (mem_size != 0) {
rc = 0;
/* Add the TLB entries in to map the region. */
if (((base_addr & TLB_256MB_ALIGN_MASK) == base_addr) &&
(mem_size >= TLB_256MB_SIZE)) {
/* Add a 256MB TLB entry */
if ((rc = add_tlb_entry(base_addr, TLB_WORD0_SIZE_256MB, tlb_i)) == 0) {
mem_size -= TLB_256MB_SIZE;
base_addr += TLB_256MB_SIZE;
}
} else if (((base_addr & TLB_16MB_ALIGN_MASK) == base_addr) &&
(mem_size >= TLB_16MB_SIZE)) {
/* Add a 16MB TLB entry */
if ((rc = add_tlb_entry(base_addr, TLB_WORD0_SIZE_16MB, tlb_i)) == 0) {
mem_size -= TLB_16MB_SIZE;
base_addr += TLB_16MB_SIZE;
}
} else if (((base_addr & TLB_1MB_ALIGN_MASK) == base_addr) &&
(mem_size >= TLB_1MB_SIZE)) {
/* Add a 1MB TLB entry */
if ((rc = add_tlb_entry(base_addr, TLB_WORD0_SIZE_1MB, tlb_i)) == 0) {
mem_size -= TLB_1MB_SIZE;
base_addr += TLB_1MB_SIZE;
}
} else if (((base_addr & TLB_256KB_ALIGN_MASK) == base_addr) &&
(mem_size >= TLB_256KB_SIZE)) {
/* Add a 256KB TLB entry */
if ((rc = add_tlb_entry(base_addr, TLB_WORD0_SIZE_256KB, tlb_i)) == 0) {
mem_size -= TLB_256KB_SIZE;
base_addr += TLB_256KB_SIZE;
}
} else if (((base_addr & TLB_64KB_ALIGN_MASK) == base_addr) &&
(mem_size >= TLB_64KB_SIZE)) {
/* Add a 64KB TLB entry */
if ((rc = add_tlb_entry(base_addr, TLB_WORD0_SIZE_64KB, tlb_i)) == 0) {
mem_size -= TLB_64KB_SIZE;
base_addr += TLB_64KB_SIZE;
}
} else if (((base_addr & TLB_16KB_ALIGN_MASK) == base_addr) &&
(mem_size >= TLB_16KB_SIZE)) {
/* Add a 16KB TLB entry */
if ((rc = add_tlb_entry(base_addr, TLB_WORD0_SIZE_16KB, tlb_i)) == 0) {
mem_size -= TLB_16KB_SIZE;
base_addr += TLB_16KB_SIZE;
}
} else if (((base_addr & TLB_4KB_ALIGN_MASK) == base_addr) &&
(mem_size >= TLB_4KB_SIZE)) {
/* Add a 4KB TLB entry */
if ((rc = add_tlb_entry(base_addr, TLB_WORD0_SIZE_4KB, tlb_i)) == 0) {
mem_size -= TLB_4KB_SIZE;
base_addr += TLB_4KB_SIZE;
}
} else if (((base_addr & TLB_1KB_ALIGN_MASK) == base_addr) &&
(mem_size >= TLB_1KB_SIZE)) {
/* Add a 1KB TLB entry */
if ((rc = add_tlb_entry(base_addr, TLB_WORD0_SIZE_1KB, tlb_i)) == 0) {
mem_size -= TLB_1KB_SIZE;
base_addr += TLB_1KB_SIZE;
}
} else {
printf("ERROR: no TLB size exists for the base address 0x%0X.\n",
base_addr);
}
if (rc != 0)
printf("ERROR: no TLB entries available for the base addr 0x%0X.\n",
base_addr);
}
return;
}
/*
* Program one (or multiple) TLB entries for one memory region
*
* Common usage for boards with SDRAM DIMM modules to dynamically
* configure the TLB's for the SDRAM
*/
void program_tlb(u32 start, u32 size)
{
region_t region_array;
region_array.base = start;
region_array.size = size;
region_array.tlb_word2_i_value = TLB_WORD2_I_ENABLE; /* disable cache (for now) */
/* Call the routine to add in the tlb entries for the memory regions */
program_tlb_addr(region_array.base, region_array.size,
region_array.tlb_word2_i_value);
return;
}
#endif /* CONFIG_440 */

View File

@ -335,41 +335,6 @@ extern int write_bat(ppc_bat_t bat, unsigned long upper, unsigned long lower);
* instructions.
*/
#define TLB_LO 1
#define TLB_HI 0
#define TLB_DATA TLB_LO
#define TLB_TAG TLB_HI
/* Tag portion */
#define TLB_EPN_MASK 0xFFFFFC00 /* Effective Page Number */
#define TLB_PAGESZ_MASK 0x00000380
#define TLB_PAGESZ(x) (((x) & 0x7) << 7)
#define PAGESZ_1K 0
#define PAGESZ_4K 1
#define PAGESZ_16K 2
#define PAGESZ_64K 3
#define PAGESZ_256K 4
#define PAGESZ_1M 5
#define PAGESZ_4M 6
#define PAGESZ_16M 7
#define TLB_VALID 0x00000040 /* Entry is valid */
/* Data portion */
#define TLB_RPN_MASK 0xFFFFFC00 /* Real Page Number */
#define TLB_PERM_MASK 0x00000300
#define TLB_EX 0x00000200 /* Instruction execution allowed */
#define TLB_WR 0x00000100 /* Writes permitted */
#define TLB_ZSEL_MASK 0x000000F0
#define TLB_ZSEL(x) (((x) & 0xF) << 4)
#define TLB_ATTR_MASK 0x0000000F
#define TLB_W 0x00000008 /* Caching is write-through */
#define TLB_I 0x00000004 /* Caching is inhibited */
#define TLB_M 0x00000002 /* Memory is coherent */
#define TLB_G 0x00000001 /* Memory is guarded from prefetch */
/*
* e500 support
*/
@ -482,7 +447,162 @@ extern int write_bat(ppc_bat_t bat, unsigned long upper, unsigned long lower);
#define LAWAR_SIZE_16G (LAWAR_SIZE_BASE+23)
#define LAWAR_SIZE_32G (LAWAR_SIZE_BASE+24)
#ifdef CONFIG_440SPE
#ifdef CONFIG_440
/* General */
#define TLB_VALID 0x00000200
/* Supported page sizes */
#define SZ_1K 0x00000000
#define SZ_4K 0x00000010
#define SZ_16K 0x00000020
#define SZ_64K 0x00000030
#define SZ_256K 0x00000040
#define SZ_1M 0x00000050
#define SZ_16M 0x00000070
#define SZ_256M 0x00000090
/* Storage attributes */
#define SA_W 0x00000800 /* Write-through */
#define SA_I 0x00000400 /* Caching inhibited */
#define SA_M 0x00000200 /* Memory coherence */
#define SA_G 0x00000100 /* Guarded */
#define SA_E 0x00000080 /* Endian */
/* Access control */
#define AC_X 0x00000024 /* Execute */
#define AC_W 0x00000012 /* Write */
#define AC_R 0x00000009 /* Read */
/* Some handy macros */
#define EPN(e) ((e) & 0xfffffc00)
#define TLB0(epn,sz) ((EPN((epn)) | (sz) | TLB_VALID ))
#define TLB1(rpn,erpn) (((rpn) & 0xfffffc00) | (erpn))
#define TLB2(a) ((a) & 0x00000fbf)
#define tlbtab_start\
mflr r1 ;\
bl 0f ;
#define tlbtab_end\
.long 0, 0, 0 ;\
0: mflr r0 ;\
mtlr r1 ;\
blr ;
#define tlbentry(epn,sz,rpn,erpn,attr)\
.long TLB0(epn,sz),TLB1(rpn,erpn),TLB2(attr)
/*----------------------------------------------------------------------------+
| TLB specific defines.
+----------------------------------------------------------------------------*/
#define TLB_256MB_ALIGN_MASK 0xF0000000
#define TLB_16MB_ALIGN_MASK 0xFF000000
#define TLB_1MB_ALIGN_MASK 0xFFF00000
#define TLB_256KB_ALIGN_MASK 0xFFFC0000
#define TLB_64KB_ALIGN_MASK 0xFFFF0000
#define TLB_16KB_ALIGN_MASK 0xFFFFC000
#define TLB_4KB_ALIGN_MASK 0xFFFFF000
#define TLB_1KB_ALIGN_MASK 0xFFFFFC00
#define TLB_256MB_SIZE 0x10000000
#define TLB_16MB_SIZE 0x01000000
#define TLB_1MB_SIZE 0x00100000
#define TLB_256KB_SIZE 0x00040000
#define TLB_64KB_SIZE 0x00010000
#define TLB_16KB_SIZE 0x00004000
#define TLB_4KB_SIZE 0x00001000
#define TLB_1KB_SIZE 0x00000400
#define TLB_WORD0_EPN_MASK 0xFFFFFC00
#define TLB_WORD0_EPN_ENCODE(n) (((unsigned long)(n))&0xFFFFFC00)
#define TLB_WORD0_EPN_DECODE(n) (((unsigned long)(n))&0xFFFFFC00)
#define TLB_WORD0_V_MASK 0x00000200
#define TLB_WORD0_V_ENABLE 0x00000200
#define TLB_WORD0_V_DISABLE 0x00000000
#define TLB_WORD0_TS_MASK 0x00000100
#define TLB_WORD0_TS_1 0x00000100
#define TLB_WORD0_TS_0 0x00000000
#define TLB_WORD0_SIZE_MASK 0x000000F0
#define TLB_WORD0_SIZE_1KB 0x00000000
#define TLB_WORD0_SIZE_4KB 0x00000010
#define TLB_WORD0_SIZE_16KB 0x00000020
#define TLB_WORD0_SIZE_64KB 0x00000030
#define TLB_WORD0_SIZE_256KB 0x00000040
#define TLB_WORD0_SIZE_1MB 0x00000050
#define TLB_WORD0_SIZE_16MB 0x00000070
#define TLB_WORD0_SIZE_256MB 0x00000090
#define TLB_WORD0_TPAR_MASK 0x0000000F
#define TLB_WORD0_TPAR_ENCODE(n) ((((unsigned long)(n))&0x0F)<<0)
#define TLB_WORD0_TPAR_DECODE(n) ((((unsigned long)(n))>>0)&0x0F)
#define TLB_WORD1_RPN_MASK 0xFFFFFC00
#define TLB_WORD1_RPN_ENCODE(n) (((unsigned long)(n))&0xFFFFFC00)
#define TLB_WORD1_RPN_DECODE(n) (((unsigned long)(n))&0xFFFFFC00)
#define TLB_WORD1_PAR1_MASK 0x00000300
#define TLB_WORD1_PAR1_ENCODE(n) ((((unsigned long)(n))&0x03)<<8)
#define TLB_WORD1_PAR1_DECODE(n) ((((unsigned long)(n))>>8)&0x03)
#define TLB_WORD1_PAR1_0 0x00000000
#define TLB_WORD1_PAR1_1 0x00000100
#define TLB_WORD1_PAR1_2 0x00000200
#define TLB_WORD1_PAR1_3 0x00000300
#define TLB_WORD1_ERPN_MASK 0x0000000F
#define TLB_WORD1_ERPN_ENCODE(n) ((((unsigned long)(n))&0x0F)<<0)
#define TLB_WORD1_ERPN_DECODE(n) ((((unsigned long)(n))>>0)&0x0F)
#define TLB_WORD2_PAR2_MASK 0xC0000000
#define TLB_WORD2_PAR2_ENCODE(n) ((((unsigned long)(n))&0x03)<<30)
#define TLB_WORD2_PAR2_DECODE(n) ((((unsigned long)(n))>>30)&0x03)
#define TLB_WORD2_PAR2_0 0x00000000
#define TLB_WORD2_PAR2_1 0x40000000
#define TLB_WORD2_PAR2_2 0x80000000
#define TLB_WORD2_PAR2_3 0xC0000000
#define TLB_WORD2_U0_MASK 0x00008000
#define TLB_WORD2_U0_ENABLE 0x00008000
#define TLB_WORD2_U0_DISABLE 0x00000000
#define TLB_WORD2_U1_MASK 0x00004000
#define TLB_WORD2_U1_ENABLE 0x00004000
#define TLB_WORD2_U1_DISABLE 0x00000000
#define TLB_WORD2_U2_MASK 0x00002000
#define TLB_WORD2_U2_ENABLE 0x00002000
#define TLB_WORD2_U2_DISABLE 0x00000000
#define TLB_WORD2_U3_MASK 0x00001000
#define TLB_WORD2_U3_ENABLE 0x00001000
#define TLB_WORD2_U3_DISABLE 0x00000000
#define TLB_WORD2_W_MASK 0x00000800
#define TLB_WORD2_W_ENABLE 0x00000800
#define TLB_WORD2_W_DISABLE 0x00000000
#define TLB_WORD2_I_MASK 0x00000400
#define TLB_WORD2_I_ENABLE 0x00000400
#define TLB_WORD2_I_DISABLE 0x00000000
#define TLB_WORD2_M_MASK 0x00000200
#define TLB_WORD2_M_ENABLE 0x00000200
#define TLB_WORD2_M_DISABLE 0x00000000
#define TLB_WORD2_G_MASK 0x00000100
#define TLB_WORD2_G_ENABLE 0x00000100
#define TLB_WORD2_G_DISABLE 0x00000000
#define TLB_WORD2_E_MASK 0x00000080
#define TLB_WORD2_E_ENABLE 0x00000080
#define TLB_WORD2_E_DISABLE 0x00000000
#define TLB_WORD2_UX_MASK 0x00000020
#define TLB_WORD2_UX_ENABLE 0x00000020
#define TLB_WORD2_UX_DISABLE 0x00000000
#define TLB_WORD2_UW_MASK 0x00000010
#define TLB_WORD2_UW_ENABLE 0x00000010
#define TLB_WORD2_UW_DISABLE 0x00000000
#define TLB_WORD2_UR_MASK 0x00000008
#define TLB_WORD2_UR_ENABLE 0x00000008
#define TLB_WORD2_UR_DISABLE 0x00000000
#define TLB_WORD2_SX_MASK 0x00000004
#define TLB_WORD2_SX_ENABLE 0x00000004
#define TLB_WORD2_SX_DISABLE 0x00000000
#define TLB_WORD2_SW_MASK 0x00000002
#define TLB_WORD2_SW_ENABLE 0x00000002
#define TLB_WORD2_SW_DISABLE 0x00000000
#define TLB_WORD2_SR_MASK 0x00000001
#define TLB_WORD2_SR_ENABLE 0x00000001
#define TLB_WORD2_SR_DISABLE 0x00000000
/*----------------------------------------------------------------------------+
| Following instructions are not available in Book E mode of the GNU assembler.
+----------------------------------------------------------------------------*/
@ -516,11 +636,15 @@ extern int write_bat(ppc_bat_t bat, unsigned long upper, unsigned long lower);
#define MBAR_INST .long 0x7c000000|\
(854<<1)
/*----------------------------------------------------------------------------+
| Following instruction is not available in PPC405 mode of the GNU assembler.
+----------------------------------------------------------------------------*/
#define TLBRE(rt,ra,ws) .long 0x7c000000|\
(rt<<21)|(ra<<16)|(ws<<11)|(946<<1)
#ifndef __ASSEMBLY__
/* Prototypes */
void mttlb1(unsigned long index, unsigned long value);
void mttlb2(unsigned long index, unsigned long value);
void mttlb3(unsigned long index, unsigned long value);
unsigned long mftlb1(unsigned long index);
unsigned long mftlb2(unsigned long index);
unsigned long mftlb3(unsigned long index);
#endif /* __ASSEMBLY__ */
#endif
#endif /* CONFIG_440 */
#endif /* _PPC_MMU_H_ */