diff options
author | Stefan Roese <sr@denx.de> | 2010-04-15 16:07:28 +0200 |
---|---|---|
committer | Wolfgang Denk <wd@denx.de> | 2010-04-21 23:42:38 +0200 |
commit | a47a12becf66f02a56da91c161e2edb625e9f20c (patch) | |
tree | 6efae7137d26d1e610c5fd56b7aaa3c043ad2b71 /arch/powerpc/cpu | |
parent | 254ab7bd464657600aba69d840406f9358f3e116 (diff) | |
download | u-boot-imx-a47a12becf66f02a56da91c161e2edb625e9f20c.zip u-boot-imx-a47a12becf66f02a56da91c161e2edb625e9f20c.tar.gz u-boot-imx-a47a12becf66f02a56da91c161e2edb625e9f20c.tar.bz2 |
Move arch/ppc to arch/powerpc
As discussed on the list, move "arch/ppc" to "arch/powerpc" to
better match the Linux directory structure.
Please note that this patch also changes the "ppc" target in
MAKEALL to "powerpc" to match this new infrastructure. But "ppc"
is kept as an alias for now, to not break compatibility with
scripts using this name.
Signed-off-by: Stefan Roese <sr@denx.de>
Acked-by: Wolfgang Denk <wd@denx.de>
Acked-by: Detlev Zundel <dzu@denx.de>
Acked-by: Kim Phillips <kim.phillips@freescale.com>
Cc: Peter Tyser <ptyser@xes-inc.com>
Cc: Anatolij Gustschin <agust@denx.de>
Diffstat (limited to 'arch/powerpc/cpu')
261 files changed, 82264 insertions, 0 deletions
diff --git a/arch/powerpc/cpu/74xx_7xx/Makefile b/arch/powerpc/cpu/74xx_7xx/Makefile new file mode 100644 index 0000000..fe905f3 --- /dev/null +++ b/arch/powerpc/cpu/74xx_7xx/Makefile @@ -0,0 +1,51 @@ +# +# (C) Copyright 2006 +# Wolfgang Denk, DENX Software Engineering, wd@denx.de. +# +# (C) Copyright 2001 +# Josh Huber <huber@mclx.com>, Mission Critical Linux, Inc. +# +# 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 $(TOPDIR)/config.mk + +LIB = $(obj)lib$(CPU).a + +START = start.o +SOBJS = cache.o kgdb.o io.o +COBJS = traps.o cpu.o cpu_init.o speed.o interrupts.o + +SRCS := $(START:.o=.S) $(SOBJS:.o=.S) $(COBJS:.o=.c) +OBJS := $(addprefix $(obj),$(SOBJS) $(COBJS)) +START := $(addprefix $(obj),$(START)) + +all: $(obj).depend $(START) $(LIB) + +$(LIB): $(OBJS) + $(AR) $(ARFLAGS) $@ $(OBJS) + +######################################################################### + +# defines $(obj).depend target +include $(SRCTREE)/rules.mk + +sinclude $(obj).depend + +######################################################################### diff --git a/arch/powerpc/cpu/74xx_7xx/cache.S b/arch/powerpc/cpu/74xx_7xx/cache.S new file mode 100644 index 0000000..66c7298 --- /dev/null +++ b/arch/powerpc/cpu/74xx_7xx/cache.S @@ -0,0 +1,404 @@ +#include <config.h> +#include <74xx_7xx.h> +#include <version.h> + +#include <ppc_asm.tmpl> +#include <ppc_defs.h> + +#include <asm/cache.h> +#include <asm/mmu.h> + +#ifndef CACHE_LINE_SIZE +# define CACHE_LINE_SIZE L1_CACHE_BYTES +#endif + +#if CACHE_LINE_SIZE == 128 +#define LG_CACHE_LINE_SIZE 7 +#elif CACHE_LINE_SIZE == 32 +#define LG_CACHE_LINE_SIZE 5 +#elif CACHE_LINE_SIZE == 16 +#define LG_CACHE_LINE_SIZE 4 +#elif CACHE_LINE_SIZE == 8 +#define LG_CACHE_LINE_SIZE 3 +#else +# error "Invalid cache line size!" +#endif + +/* + * Invalidate L1 instruction cache. + */ +_GLOBAL(invalidate_l1_instruction_cache) + mfspr r3,PVR + rlwinm r3,r3,16,16,31 + cmpi 0,r3,1 + beqlr /* for 601, do nothing */ + /* 603/604 processor - use invalidate-all bit in HID0 */ + mfspr r3,HID0 + ori r3,r3,HID0_ICFI + mtspr HID0,r3 + isync + blr + +/* + * Invalidate L1 data cache. + */ +_GLOBAL(invalidate_l1_data_cache) + mfspr r3,HID0 + ori r3,r3,HID0_DCFI + mtspr HID0,r3 + isync + blr + +/* + * Flush data cache. + */ +_GLOBAL(flush_dcache) + lis r3,0 + lis r5,CACHE_LINE_SIZE +flush: + cmp 0,1,r3,r5 + bge done + lwz r5,0(r3) + lis r5,CACHE_LINE_SIZE + addi r3,r3,0x4 + b flush +done: + blr +/* + * Write any modified data cache blocks out to memory + * and invalidate the corresponding instruction cache blocks. + * This is a no-op on the 601. + * + * flush_icache_range(unsigned long start, unsigned long stop) + */ +_GLOBAL(flush_icache_range) + mfspr r5,PVR + rlwinm r5,r5,16,16,31 + cmpi 0,r5,1 + beqlr /* for 601, do nothing */ + li r5,CACHE_LINE_SIZE-1 + andc r3,r3,r5 + subf r4,r3,r4 + add r4,r4,r5 + srwi. r4,r4,LG_CACHE_LINE_SIZE + beqlr + mtctr r4 + mr r6,r3 +1: dcbst 0,r3 + addi r3,r3,CACHE_LINE_SIZE + bdnz 1b + sync /* wait for dcbst's to get to ram */ + mtctr r4 +2: icbi 0,r6 + addi r6,r6,CACHE_LINE_SIZE + bdnz 2b + sync /* additional sync needed on g4 */ + isync + blr +/* + * Write any modified data cache blocks out to memory. + * Does not invalidate the corresponding cache lines (especially for + * any corresponding instruction cache). + * + * clean_dcache_range(unsigned long start, unsigned long stop) + */ +_GLOBAL(clean_dcache_range) + li r5,CACHE_LINE_SIZE-1 + andc r3,r3,r5 /* align r3 down to cache line */ + subf r4,r3,r4 /* r4 = offset of stop from start of cache line */ + add r4,r4,r5 /* r4 += cache_line_size-1 */ + srwi. r4,r4,LG_CACHE_LINE_SIZE /* r4 = number of cache lines to flush */ + beqlr /* if r4 == 0 return */ + mtctr r4 /* ctr = r4 */ + + sync +1: dcbst 0,r3 + addi r3,r3,CACHE_LINE_SIZE + bdnz 1b + sync /* wait for dcbst's to get to ram */ + blr + +/* + * Write any modified data cache blocks out to memory + * and invalidate the corresponding instruction cache blocks. + * + * flush_dcache_range(unsigned long start, unsigned long stop) + */ +_GLOBAL(flush_dcache_range) + li r5,CACHE_LINE_SIZE-1 + andc r3,r3,r5 + subf r4,r3,r4 + add r4,r4,r5 + srwi. r4,r4,LG_CACHE_LINE_SIZE + beqlr + mtctr r4 + + sync +1: dcbf 0,r3 + addi r3,r3,CACHE_LINE_SIZE + bdnz 1b + sync /* wait for dcbf's to get to ram */ + blr + +/* + * Like above, but invalidate the D-cache. This is used by the 8xx + * to invalidate the cache so the PPC core doesn't get stale data + * from the CPM (no cache snooping here :-). + * + * invalidate_dcache_range(unsigned long start, unsigned long stop) + */ +_GLOBAL(invalidate_dcache_range) + li r5,CACHE_LINE_SIZE-1 + andc r3,r3,r5 + subf r4,r3,r4 + add r4,r4,r5 + srwi. r4,r4,LG_CACHE_LINE_SIZE + beqlr + mtctr r4 + + sync +1: dcbi 0,r3 + addi r3,r3,CACHE_LINE_SIZE + bdnz 1b + sync /* wait for dcbi's to get to ram */ + blr + +/* + * Flush a particular page from the data cache to RAM. + * Note: this is necessary because the instruction cache does *not* + * snoop from the data cache. + * This is a no-op on the 601 which has a unified cache. + * + * void __flush_page_to_ram(void *page) + */ +_GLOBAL(__flush_page_to_ram) + mfspr r5,PVR + rlwinm r5,r5,16,16,31 + cmpi 0,r5,1 + beqlr /* for 601, do nothing */ + rlwinm r3,r3,0,0,19 /* Get page base address */ + li r4,4096/CACHE_LINE_SIZE /* Number of lines in a page */ + mtctr r4 + mr r6,r3 +0: dcbst 0,r3 /* Write line to ram */ + addi r3,r3,CACHE_LINE_SIZE + bdnz 0b + sync + mtctr r4 +1: icbi 0,r6 + addi r6,r6,CACHE_LINE_SIZE + bdnz 1b + sync + isync + blr + +/* + * Flush a particular page from the instruction cache. + * Note: this is necessary because the instruction cache does *not* + * snoop from the data cache. + * This is a no-op on the 601 which has a unified cache. + * + * void __flush_icache_page(void *page) + */ +_GLOBAL(__flush_icache_page) + mfspr r5,PVR + rlwinm r5,r5,16,16,31 + cmpi 0,r5,1 + beqlr /* for 601, do nothing */ + li r4,4096/CACHE_LINE_SIZE /* Number of lines in a page */ + mtctr r4 +1: icbi 0,r3 + addi r3,r3,CACHE_LINE_SIZE + bdnz 1b + sync + isync + blr + +/* + * Clear a page using the dcbz instruction, which doesn't cause any + * memory traffic (except to write out any cache lines which get + * displaced). This only works on cacheable memory. + */ +_GLOBAL(clear_page) + li r0,4096/CACHE_LINE_SIZE + mtctr r0 +1: dcbz 0,r3 + addi r3,r3,CACHE_LINE_SIZE + bdnz 1b + blr + +/* + * Enable L1 Instruction cache + */ +_GLOBAL(icache_enable) + mfspr r3, HID0 + li r5, HID0_ICFI|HID0_ILOCK + andc r3, r3, r5 + ori r3, r3, HID0_ICE + ori r5, r3, HID0_ICFI + mtspr HID0, r5 + mtspr HID0, r3 + isync + blr + +/* + * Disable L1 Instruction cache + */ +_GLOBAL(icache_disable) + mflr r4 + bl invalidate_l1_instruction_cache /* uses r3 */ + sync + mtlr r4 + mfspr r3, HID0 + li r5, 0 + ori r5, r5, HID0_ICE + andc r3, r3, r5 + mtspr HID0, r3 + isync + blr + +/* + * Is instruction cache enabled? + */ +_GLOBAL(icache_status) + mfspr r3, HID0 + andi. r3, r3, HID0_ICE + blr + + +_GLOBAL(l1dcache_enable) + mfspr r3, HID0 + li r5, HID0_DCFI|HID0_DLOCK + andc r3, r3, r5 + mtspr HID0, r3 /* no invalidate, unlock */ + ori r3, r3, HID0_DCE + ori r5, r3, HID0_DCFI + mtspr HID0, r5 /* enable + invalidate */ + mtspr HID0, r3 /* enable */ + sync + blr + +/* + * Enable data cache(s) - L1 and optionally L2 + * Calls l2cache_enable. LR saved in r5 + */ +_GLOBAL(dcache_enable) + mfspr r3, HID0 + li r5, HID0_DCFI|HID0_DLOCK + andc r3, r3, r5 + mtspr HID0, r3 /* no invalidate, unlock */ + ori r3, r3, HID0_DCE + ori r5, r3, HID0_DCFI + mtspr HID0, r5 /* enable + invalidate */ + mtspr HID0, r3 /* enable */ + sync +#ifdef CONFIG_SYS_L2 + mflr r5 + bl l2cache_enable /* uses r3 and r4 */ + sync + mtlr r5 +#endif + blr + + +/* + * Disable data cache(s) - L1 and optionally L2 + * Calls flush_dcache and l2cache_disable_no_flush. + * LR saved in r4 + */ +_GLOBAL(dcache_disable) + mflr r4 /* save link register */ + bl flush_dcache /* uses r3 and r5 */ + sync + mfspr r3, HID0 + li r5, HID0_DCFI|HID0_DLOCK + andc r3, r3, r5 + mtspr HID0, r3 /* no invalidate, unlock */ + li r5, HID0_DCE|HID0_DCFI + andc r3, r3, r5 /* no enable, no invalidate */ + mtspr HID0, r3 + sync +#ifdef CONFIG_SYS_L2 + bl l2cache_disable_no_flush /* uses r3 */ +#endif + mtlr r4 /* restore link register */ + blr + +/* + * Is data cache enabled? + */ +_GLOBAL(dcache_status) + mfspr r3, HID0 + andi. r3, r3, HID0_DCE + blr + +/* + * Invalidate L2 cache using L2I and polling L2IP or L2I + */ +_GLOBAL(l2cache_invalidate) + sync + mfspr r3, l2cr + oris r3, r3, L2CR_L2I@h + sync + mtspr l2cr, r3 + sync + mfspr r3, PVR + sync + rlwinm r3, r3, 16,16,31 + cmpli 0,r3,0x8000 /* 7451, 7441 */ + beq 0,inv_7450 + cmpli 0,r3,0x8001 /* 7455, 7445 */ + beq 0,inv_7450 + cmpli 0,r3,0x8002 /* 7457, 7447 */ + beq 0,inv_7450 + cmpli 0,r3,0x8003 /* 7447A */ + beq 0,inv_7450 + cmpli 0,r3,0x8004 /* 7448 */ + beq 0,inv_7450 +invl2: + mfspr r3, l2cr + andi. r3, r3, L2CR_L2IP + bne invl2 + /* turn off the global invalidate bit */ + mfspr r3, l2cr + rlwinm r3, r3, 0, 11, 9 + sync + mtspr l2cr, r3 + sync + blr +inv_7450: + mfspr r3, l2cr + andis. r3, r3, L2CR_L2I@h + bne inv_7450 + blr + +/* + * Enable L2 cache + * Calls l2cache_invalidate. LR is saved in r4 + */ +_GLOBAL(l2cache_enable) + mflr r4 /* save link register */ + bl l2cache_invalidate /* uses r3 */ + sync + lis r3, L2_ENABLE@h + ori r3, r3, L2_ENABLE@l + mtspr l2cr, r3 + isync + mtlr r4 /* restore link register */ + blr + +/* + * Disable L2 cache + * Calls flush_dcache. LR is saved in r4 + */ +_GLOBAL(l2cache_disable) + mflr r4 /* save link register */ + bl flush_dcache /* uses r3 and r5 */ + sync + mtlr r4 /* restore link register */ +l2cache_disable_no_flush: /* provide way to disable L2 w/o flushing */ + lis r3, L2_INIT@h + ori r3, r3, L2_INIT@l + mtspr l2cr, r3 + isync + blr diff --git a/arch/powerpc/cpu/74xx_7xx/config.mk b/arch/powerpc/cpu/74xx_7xx/config.mk new file mode 100644 index 0000000..df1f6ac --- /dev/null +++ b/arch/powerpc/cpu/74xx_7xx/config.mk @@ -0,0 +1,26 @@ +# +# (C) Copyright 2001 +# Josh Huber <huber@mclx.com>, Mission Critical Linux, Inc. +# +# 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 +# + +PLATFORM_RELFLAGS += -fPIC -meabi + +PLATFORM_CPPFLAGS += -DCONFIG_74xx_7xx -ffixed-r2 -mstring diff --git a/arch/powerpc/cpu/74xx_7xx/cpu.c b/arch/powerpc/cpu/74xx_7xx/cpu.c new file mode 100644 index 0000000..3c17277 --- /dev/null +++ b/arch/powerpc/cpu/74xx_7xx/cpu.c @@ -0,0 +1,320 @@ +/* + * (C) Copyright 2001 + * Josh Huber <huber@mclx.com>, Mission Critical Linux, Inc. + * + * 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 + */ + +/* + * cpu.c + * + * CPU specific code + * + * written or collected and sometimes rewritten by + * Magnus Damm <damm@bitsmart.com> + * + * minor modifications by + * Wolfgang Denk <wd@denx.de> + * + * more modifications by + * Josh Huber <huber@mclx.com> + * added support for the 74xx series of cpus + * added support for the 7xx series of cpus + * made the code a little less hard-coded, and more auto-detectish + */ + +#include <common.h> +#include <command.h> +#include <74xx_7xx.h> +#include <asm/cache.h> + +#if defined(CONFIG_OF_LIBFDT) +#include <libfdt.h> +#include <fdt_support.h> +#endif + +#ifdef CONFIG_AMIGAONEG3SE +#include "../board/MAI/AmigaOneG3SE/via686.h" +#include "../board/MAI/AmigaOneG3SE/memio.h" +#endif + +DECLARE_GLOBAL_DATA_PTR; + +cpu_t +get_cpu_type(void) +{ + uint pvr = get_pvr(); + cpu_t type; + + type = CPU_UNKNOWN; + + switch (PVR_VER(pvr)) { + case 0x000c: + type = CPU_7400; + break; + case 0x0008: + type = CPU_750; + + if (((pvr >> 8) & 0xff) == 0x01) { + type = CPU_750CX; /* old CX (80100 and 8010x?)*/ + } else if (((pvr >> 8) & 0xff) == 0x22) { + type = CPU_750CX; /* CX (82201,82202) and CXe (82214) */ + } else if (((pvr >> 8) & 0xff) == 0x33) { + type = CPU_750CX; /* CXe (83311) */ + } else if (((pvr >> 12) & 0xF) == 0x3) { + type = CPU_755; + } + break; + + case 0x7000: + type = CPU_750FX; + break; + + case 0x7002: + type = CPU_750GX; + break; + + case 0x800C: + type = CPU_7410; + break; + + case 0x8000: + type = CPU_7450; + break; + + case 0x8001: + type = CPU_7455; + break; + + case 0x8002: + type = CPU_7457; + break; + + case 0x8003: + type = CPU_7447A; + break; + + case 0x8004: + type = CPU_7448; + break; + + default: + break; + } + + return type; +} + +/* ------------------------------------------------------------------------- */ + +#if !defined(CONFIG_BAB7xx) +int checkcpu (void) +{ + uint type = get_cpu_type(); + uint pvr = get_pvr(); + ulong clock = gd->cpu_clk; + char buf[32]; + char *str; + + puts ("CPU: "); + + switch (type) { + case CPU_750CX: + printf ("750CX%s v%d.%d", (pvr&0xf0)?"e":"", + (pvr>>8) & 0xf, + pvr & 0xf); + goto PR_CLK; + + case CPU_750: + str = "750"; + break; + + case CPU_750FX: + str = "750FX"; + break; + + case CPU_750GX: + str = "750GX"; + break; + + case CPU_755: + str = "755"; + break; + + case CPU_7400: + str = "MPC7400"; + break; + + case CPU_7410: + str = "MPC7410"; + break; + + case CPU_7447A: + str = "MPC7447A"; + break; + + case CPU_7448: + str = "MPC7448"; + break; + + case CPU_7450: + str = "MPC7450"; + break; + + case CPU_7455: + str = "MPC7455"; + break; + + case CPU_7457: + str = "MPC7457"; + break; + + default: + printf("Unknown CPU -- PVR: 0x%08x\n", pvr); + return -1; + } + + printf ("%s v%d.%d", str, (pvr >> 8) & 0xFF, pvr & 0xFF); +PR_CLK: + printf (" @ %s MHz\n", strmhz(buf, clock)); + + return (0); +} +#endif +/* these two functions are unimplemented currently [josh] */ + +/* -------------------------------------------------------------------- */ +/* L1 i-cache */ + +int +checkicache(void) +{ + return 0; /* XXX */ +} + +/* -------------------------------------------------------------------- */ +/* L1 d-cache */ + +int +checkdcache(void) +{ + return 0; /* XXX */ +} + +/* -------------------------------------------------------------------- */ + +static inline void +soft_restart(unsigned long addr) +{ + /* SRR0 has system reset vector, SRR1 has default MSR value */ + /* rfi restores MSR from SRR1 and sets the PC to the SRR0 value */ + + __asm__ __volatile__ ("mtspr 26, %0" :: "r" (addr)); + __asm__ __volatile__ ("li 4, (1 << 6)" ::: "r4"); + __asm__ __volatile__ ("mtspr 27, 4"); + __asm__ __volatile__ ("rfi"); + + while(1); /* not reached */ +} + + +#if !defined(CONFIG_PCIPPC2) && \ + !defined(CONFIG_BAB7xx) && \ + !defined(CONFIG_ELPPC) && \ + !defined(CONFIG_PPMC7XX) +/* no generic way to do board reset. simply call soft_reset. */ +void +do_reset (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]) +{ + ulong addr; + /* flush and disable I/D cache */ + __asm__ __volatile__ ("mfspr 3, 1008" ::: "r3"); + __asm__ __volatile__ ("ori 5, 5, 0xcc00" ::: "r5"); + __asm__ __volatile__ ("ori 4, 3, 0xc00" ::: "r4"); + __asm__ __volatile__ ("andc 5, 3, 5" ::: "r5"); + __asm__ __volatile__ ("sync"); + __asm__ __volatile__ ("mtspr 1008, 4"); + __asm__ __volatile__ ("isync"); + __asm__ __volatile__ ("sync"); + __asm__ __volatile__ ("mtspr 1008, 5"); + __asm__ __volatile__ ("isync"); + __asm__ __volatile__ ("sync"); + +#ifdef CONFIG_SYS_RESET_ADDRESS + addr = CONFIG_SYS_RESET_ADDRESS; +#else + /* + * note: when CONFIG_SYS_MONITOR_BASE points to a RAM address, + * CONFIG_SYS_MONITOR_BASE - sizeof (ulong) is usually a valid + * address. Better pick an address known to be invalid on your + * system and assign it to CONFIG_SYS_RESET_ADDRESS. + */ + addr = CONFIG_SYS_MONITOR_BASE - sizeof (ulong); +#endif + soft_restart(addr); + while(1); /* not reached */ +} +#endif + +/* ------------------------------------------------------------------------- */ + +/* + * For the 7400 the TB clock runs at 1/4 the cpu bus speed. + */ +#if defined(CONFIG_AMIGAONEG3SE) || defined(CONFIG_SYS_CONFIG_BUS_CLK) +unsigned long get_tbclk(void) +{ + return (gd->bus_clk / 4); +} +#else /* ! CONFIG_AMIGAONEG3SE and !CONFIG_SYS_CONFIG_BUS_CLK*/ + +unsigned long get_tbclk (void) +{ + return CONFIG_SYS_BUS_HZ / 4; +} +#endif /* CONFIG_AMIGAONEG3SE or CONFIG_SYS_CONFIG_BUS_CLK*/ +/* ------------------------------------------------------------------------- */ +#if defined(CONFIG_WATCHDOG) +#if !defined(CONFIG_PCIPPC2) && !defined(CONFIG_BAB7xx) +void +watchdog_reset(void) +{ + +} +#endif /* !CONFIG_PCIPPC2 && !CONFIG_BAB7xx */ +#endif /* CONFIG_WATCHDOG */ + +/* ------------------------------------------------------------------------- */ + +#ifdef CONFIG_OF_LIBFDT +void ft_cpu_setup(void *blob, bd_t *bd) +{ + do_fixup_by_prop_u32(blob, "device_type", "cpu", 4, + "timebase-frequency", bd->bi_busfreq / 4, 1); + do_fixup_by_prop_u32(blob, "device_type", "cpu", 4, + "bus-frequency", bd->bi_busfreq, 1); + do_fixup_by_prop_u32(blob, "device_type", "cpu", 4, + "clock-frequency", bd->bi_intfreq, 1); + + fdt_fixup_memory(blob, (u64)bd->bi_memstart, (u64)bd->bi_memsize); + + fdt_fixup_ethernet(blob); +} +#endif +/* ------------------------------------------------------------------------- */ diff --git a/arch/powerpc/cpu/74xx_7xx/cpu_init.c b/arch/powerpc/cpu/74xx_7xx/cpu_init.c new file mode 100644 index 0000000..1dd1b2c --- /dev/null +++ b/arch/powerpc/cpu/74xx_7xx/cpu_init.c @@ -0,0 +1,63 @@ +/* + * (C) Copyright 2001 + * Josh Huber <huber@mclx.com>, Mission Critical Linux, Inc. + * + * 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 + */ + +/* + * cpu_init.c - low level cpu init + * + * there's really nothing going on here yet. future work area? + */ + +#include <common.h> +#include <74xx_7xx.h> + +/* + * Breath some life into the CPU... + * + * there's basically nothing to do here since the memory controller + * isn't on the CPU in this case. + */ +void +cpu_init_f (void) +{ + switch (get_cpu_type()) { + case CPU_7450: + case CPU_7455: + case CPU_7457: + case CPU_7447A: + case CPU_7448: + /* enable the timebase bit in HID0 */ + set_hid0(get_hid0() | 0x4000000); + break; + default: + /* do nothing */ + break; + } +} + +/* + * initialize higher level parts of CPU like timers + */ +int cpu_init_r (void) +{ + return (0); +} diff --git a/arch/powerpc/cpu/74xx_7xx/interrupts.c b/arch/powerpc/cpu/74xx_7xx/interrupts.c new file mode 100644 index 0000000..0ea1aec --- /dev/null +++ b/arch/powerpc/cpu/74xx_7xx/interrupts.c @@ -0,0 +1,105 @@ +/* + * (C) Copyright 2001 + * Josh Huber <huber@mclx.com>, Mission Critical Linux, Inc. + * + * 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 + */ + +/* + * interrupts.c - just enough support for the decrementer/timer + */ + +#include <common.h> +#include <mpc8xx.h> +#include <mpc8xx_irq.h> +#include <asm/processor.h> +#include <commproc.h> +#include <command.h> + +int interrupt_init_cpu (unsigned *decrementer_count) +{ +#if defined(DEBUG) && !defined(CONFIG_AMIGAONEG3SE) + printf("interrupt_init: GT main cause reg: %08x:%08x\n", + GTREGREAD(LOW_INTERRUPT_CAUSE_REGISTER), + GTREGREAD(HIGH_INTERRUPT_CAUSE_REGISTER)); + printf("interrupt_init: ethernet cause regs: %08x %08x %08x\n", + GTREGREAD(ETHERNET0_INTERRUPT_CAUSE_REGISTER), + GTREGREAD(ETHERNET1_INTERRUPT_CAUSE_REGISTER), + GTREGREAD(ETHERNET2_INTERRUPT_CAUSE_REGISTER)); + printf("interrupt_init: ethernet mask regs: %08x %08x %08x\n", + GTREGREAD(ETHERNET0_INTERRUPT_MASK_REGISTER), + GTREGREAD(ETHERNET1_INTERRUPT_MASK_REGISTER), + GTREGREAD(ETHERNET2_INTERRUPT_MASK_REGISTER)); + puts("interrupt_init: setting decrementer_count\n"); +#endif + *decrementer_count = get_tbclk() / CONFIG_SYS_HZ; + + return (0); +} + +/****************************************************************************/ + +/* + * Handle external interrupts + */ +void +external_interrupt(struct pt_regs *regs) +{ + puts("external_interrupt (oops!)\n"); +} + +volatile ulong timestamp = 0; + +/* + * timer_interrupt - gets called when the decrementer overflows, + * with interrupts disabled. + * Trivial implementation - no need to be really accurate. + */ +void +timer_interrupt_cpu (struct pt_regs *regs) +{ + /* nothing to do here */ + return; +} + +/****************************************************************************/ + +/* + * Install and free a interrupt handler. + */ + +void +irq_install_handler(int vec, interrupt_handler_t *handler, void *arg) +{ + +} + +void +irq_free_handler(int vec) +{ + +} + +/****************************************************************************/ + +void +do_irqinfo(cmd_tbl_t *cmdtp, bd_t *bd, int flag, int argc, char *argv[]) +{ + puts("IRQ related functions are unimplemented currently.\n"); +} diff --git a/arch/powerpc/cpu/74xx_7xx/io.S b/arch/powerpc/cpu/74xx_7xx/io.S new file mode 100644 index 0000000..af2e6d1 --- /dev/null +++ b/arch/powerpc/cpu/74xx_7xx/io.S @@ -0,0 +1,128 @@ +/* + * Copyright (C) 1998 Dan Malek <dmalek@jlc.net> + * Copyright (C) 1999 Magnus Damm <kieraypc01.p.y.kie.era.ericsson.se> + * Copyright (C) 2001 Sysgo Real-Time Solutions, GmbH <www.elinos.com> + * Andreas Heppel <aheppel@sysgo.de> + * Copyright (C) 2002 Wolfgang Denk <wd@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 <config.h> +#include <ppc_asm.tmpl> + +/* ------------------------------------------------------------------------------- */ +/* Function: in8 */ +/* Description: Input 8 bits */ +/* ------------------------------------------------------------------------------- */ + .globl in8 +in8: + lbz r3,0(r3) + sync + blr + +/* ------------------------------------------------------------------------------- */ +/* Function: in16 */ +/* Description: Input 16 bits */ +/* ------------------------------------------------------------------------------- */ + .globl in16 +in16: + lhz r3,0(r3) + sync + blr + +/* ------------------------------------------------------------------------------- */ +/* Function: in16r */ +/* Description: Input 16 bits and byte reverse */ +/* ------------------------------------------------------------------------------- */ + .globl in16r +in16r: + lhbrx r3,0,r3 + sync + blr + +/* ------------------------------------------------------------------------------- */ +/* Function: in32 */ +/* Description: Input 32 bits */ +/* ------------------------------------------------------------------------------- */ + .globl in32 +in32: + lwz 3,0(3) + sync + blr + +/* ------------------------------------------------------------------------------- */ +/* Function: in32r */ +/* Description: Input 32 bits and byte reverse */ +/* ------------------------------------------------------------------------------- */ + .globl in32r +in32r: + lwbrx r3,0,r3 + sync + blr + +/* ------------------------------------------------------------------------------- */ +/* Function: out8 */ +/* Description: Output 8 bits */ +/* ------------------------------------------------------------------------------- */ + .globl out8 +out8: + stb r4,0(r3) + sync + blr + +/* ------------------------------------------------------------------------------- */ +/* Function: out16 */ +/* Description: Output 16 bits */ +/* ------------------------------------------------------------------------------- */ + .globl out16 +out16: + sth r4,0(r3) + sync + blr + +/* ------------------------------------------------------------------------------- */ +/* Function: out16r */ +/* Description: Byte reverse and output 16 bits */ +/* ------------------------------------------------------------------------------- */ + .globl out16r +out16r: + sthbrx r4,0,r3 + sync + blr + +/* ------------------------------------------------------------------------------- */ +/* Function: out32 */ +/* Description: Output 32 bits */ +/* ------------------------------------------------------------------------------- */ + .globl out32 +out32: + stw r4,0(r3) + sync + blr + +/* ------------------------------------------------------------------------------- */ +/* Function: out32r */ +/* Description: Byte reverse and output 32 bits */ +/* ------------------------------------------------------------------------------- */ + .globl out32r +out32r: + stwbrx r4,0,r3 + sync + blr diff --git a/arch/powerpc/cpu/74xx_7xx/kgdb.S b/arch/powerpc/cpu/74xx_7xx/kgdb.S new file mode 100644 index 0000000..ad487cd --- /dev/null +++ b/arch/powerpc/cpu/74xx_7xx/kgdb.S @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2000 Murray Jensen <Murray.Jensen@cmst.csiro.au> + * + * 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-1307USA + */ + +#include <config.h> +#include <command.h> +#include <74xx_7xx.h> +#include <version.h> + +#include <ppc_asm.tmpl> +#include <ppc_defs.h> + +#include <asm/cache.h> +#include <asm/mmu.h> + +#if defined(CONFIG_CMD_KGDB) + + /* + * cache flushing routines for kgdb + */ + + .globl kgdb_flush_cache_all +kgdb_flush_cache_all: + lis r3,0 + addis r4,r0,0x0040 +kgdb_flush_loop: + lwz r5,0(r3) + addi r3,r3,CONFIG_SYS_CACHELINE_SIZE + cmp 0,0,r3,r4 + bne kgdb_flush_loop + SYNC + mfspr r3,1008 + ori r3,r3,0x8800 + mtspr 1008,r3 + sync + blr + + .globl kgdb_flush_cache_range +kgdb_flush_cache_range: + li r5,CONFIG_SYS_CACHELINE_SIZE-1 + andc r3,r3,r5 + subf r4,r3,r4 + add r4,r4,r5 + srwi. r4,r4,CONFIG_SYS_CACHELINE_SHIFT + beqlr + mtctr r4 + mr r6,r3 +1: dcbst 0,r3 + addi r3,r3,CONFIG_SYS_CACHELINE_SIZE + bdnz 1b + sync /* wait for dcbst's to get to ram */ + mtctr r4 +2: icbi 0,r6 + addi r6,r6,CONFIG_SYS_CACHELINE_SIZE + bdnz 2b + SYNC + blr + +#endif diff --git a/arch/powerpc/cpu/74xx_7xx/speed.c b/arch/powerpc/cpu/74xx_7xx/speed.c new file mode 100644 index 0000000..f2fdcd5 --- /dev/null +++ b/arch/powerpc/cpu/74xx_7xx/speed.c @@ -0,0 +1,185 @@ +/* + * (C) Copyright 2000-2002 + * Wolfgang Denk, DENX Software Engineering, wd@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> +#include <74xx_7xx.h> +#include <asm/processor.h> + +#ifdef CONFIG_AMIGAONEG3SE +#include "../board/MAI/AmigaOneG3SE/via686.h" +#endif + +DECLARE_GLOBAL_DATA_PTR; + +extern unsigned long get_board_bus_clk (void); + +static const int hid1_multipliers_x_10[] = { + 25, /* 0000 - 2.5x */ + 75, /* 0001 - 7.5x */ + 70, /* 0010 - 7x */ + 10, /* 0011 - bypass */ + 20, /* 0100 - 2x */ + 65, /* 0101 - 6.5x */ + 100, /* 0110 - 10x */ + 45, /* 0111 - 4.5x */ + 30, /* 1000 - 3x */ + 55, /* 1001 - 5.5x */ + 40, /* 1010 - 4x */ + 50, /* 1011 - 5x */ + 80, /* 1100 - 8x */ + 60, /* 1101 - 6x */ + 35, /* 1110 - 3.5x */ + 0 /* 1111 - off */ +}; + +/* PLL_CFG[0:4] table for cpu 7448/7447A/7455/7457 */ +static const int hid1_74xx_multipliers_x_10[] = { + 115, /* 00000 - 11.5x */ + 170, /* 00001 - 17x */ + 75, /* 00010 - 7.5x */ + 150, /* 00011 - 15x */ + 70, /* 00100 - 7x */ + 180, /* 00101 - 18x */ + 10, /* 00110 - bypass */ + 200, /* 00111 - 20x */ + 20, /* 01000 - 2x */ + 210, /* 01001 - 21x */ + 65, /* 01010 - 6.5x */ + 130, /* 01011 - 13x */ + 85, /* 01100 - 8.5x */ + 240, /* 01101 - 24x */ + 95, /* 01110 - 9.5x */ + 90, /* 01111 - 9x */ + 30, /* 10000 - 3x */ + 105, /* 10001 - 10.5x */ + 55, /* 10010 - 5.5x */ + 110, /* 10011 - 11x */ + 40, /* 10100 - 4x */ + 100, /* 10101 - 10x */ + 50, /* 10110 - 5x */ + 120, /* 10111 - 12x */ + 80, /* 11000 - 8x */ + 140, /* 11001 - 14x */ + 60, /* 11010 - 6x */ + 160, /* 11011 - 16x */ + 135, /* 11100 - 13.5x */ + 280, /* 11101 - 28x */ + 0, /* 11110 - off */ + 125 /* 11111 - 12.5x */ +}; + +static const int hid1_fx_multipliers_x_10[] = { + 00, /* 0000 - off */ + 00, /* 0001 - off */ + 10, /* 0010 - bypass */ + 10, /* 0011 - bypass */ + 20, /* 0100 - 2x */ + 25, /* 0101 - 2.5x */ + 30, /* 0110 - 3x */ + 35, /* 0111 - 3.5x */ + 40, /* 1000 - 4x */ + 45, /* 1001 - 4.5x */ + 50, /* 1010 - 5x */ + 55, /* 1011 - 5.5x */ + 60, /* 1100 - 6x */ + 65, /* 1101 - 6.5x */ + 70, /* 1110 - 7x */ + 75, /* 1111 - 7.5 */ + 80, /* 10000 - 8x */ + 85, /* 10001 - 8.5x */ + 90, /* 10010 - 9x */ + 95, /* 10011 - 9.5x */ + 100, /* 10100 - 10x */ + 110, /* 10101 - 11x */ + 120, /* 10110 - 12x */ +}; + + +/* ------------------------------------------------------------------------- */ + +/* + * Measure CPU clock speed (core clock GCLK1, GCLK2) + * + * (Approx. GCLK frequency in Hz) + */ + +int get_clocks (void) +{ + ulong clock = 0; + +#ifdef CONFIG_SYS_BUS_CLK + gd->bus_clk = CONFIG_SYS_BUS_CLK; /* bus clock is a fixed frequency */ +#else + gd->bus_clk = get_board_bus_clk (); /* bus clock is configurable */ +#endif + + /* calculate the clock frequency based upon the CPU type */ + switch (get_cpu_type()) { + case CPU_7447A: + case CPU_7448: + case CPU_7455: + case CPU_7457: + /* + * Make sure division is done before multiplication to prevent 32-bit + * arithmetic overflows which will cause a negative number + */ + clock = (gd->bus_clk / 10) * + hid1_74xx_multipliers_x_10[(get_hid1 () >> 12) & 0x1F]; + break; + + case CPU_750GX: + case CPU_750FX: + clock = (gd->bus_clk / 10) * + hid1_fx_multipliers_x_10[get_hid1 () >> 27]; + break; + + case CPU_7450: + case CPU_740: + case CPU_740P: + case CPU_745: + case CPU_750CX: + case CPU_750: + case CPU_750P: + case CPU_755: + case CPU_7400: + case CPU_7410: + /* + * Make sure division is done before multiplication to prevent 32-bit + * arithmetic overflows which will cause a negative number + */ + clock = (gd->bus_clk / 10) * + hid1_multipliers_x_10[get_hid1 () >> 28]; + break; + + case CPU_UNKNOWN: + printf ("get_gclk_freq(): unknown CPU type\n"); + clock = 0; + return (1); + } + + gd->cpu_clk = clock; + + return (0); +} + +/* ------------------------------------------------------------------------- */ diff --git a/arch/powerpc/cpu/74xx_7xx/start.S b/arch/powerpc/cpu/74xx_7xx/start.S new file mode 100644 index 0000000..88fdf88 --- /dev/null +++ b/arch/powerpc/cpu/74xx_7xx/start.S @@ -0,0 +1,874 @@ +/* + * Copyright (C) 1998 Dan Malek <dmalek@jlc.net> + * Copyright (C) 1999 Magnus Damm <kieraypc01.p.y.kie.era.ericsson.se> + * Copyright (C) 2000,2001,2002 Wolfgang Denk <wd@denx.de> + * Copyright (C) 2001 Josh Huber <huber@mclx.com> + * + * 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 + */ + +/* U-Boot - Startup Code for PowerPC based Embedded Boards + * + * + * The processor starts at 0xfff00100 and the code is executed + * from flash. The code is organized to be at an other address + * in memory, but as long we don't jump around before relocating. + * board_init lies at a quite high address and when the cpu has + * jumped there, everything is ok. + */ +#include <config.h> +#include <74xx_7xx.h> +#include <timestamp.h> +#include <version.h> + +#include <ppc_asm.tmpl> +#include <ppc_defs.h> + +#include <asm/cache.h> +#include <asm/mmu.h> + +#if !defined(CONFIG_DB64360) && \ + !defined(CONFIG_DB64460) && \ + !defined(CONFIG_CPCI750) && \ + !defined(CONFIG_P3Mx) +#include <galileo/gt64260R.h> +#endif + +#ifndef CONFIG_IDENT_STRING +#define CONFIG_IDENT_STRING "" +#endif + +/* We don't want the MMU yet. +*/ +#undef MSR_KERNEL +/* Machine Check and Recoverable Interr. */ +#define MSR_KERNEL ( MSR_ME | MSR_RI ) + +/* + * Set up GOT: Global Offset Table + * + * Use r12 to access the GOT + */ + START_GOT + GOT_ENTRY(_GOT2_TABLE_) + GOT_ENTRY(_FIXUP_TABLE_) + + GOT_ENTRY(_start) + GOT_ENTRY(_start_of_vectors) + GOT_ENTRY(_end_of_vectors) + GOT_ENTRY(transfer_to_handler) + + GOT_ENTRY(__init_end) + GOT_ENTRY(_end) + GOT_ENTRY(__bss_start) + END_GOT + +/* + * r3 - 1st arg to board_init(): IMMP pointer + * r4 - 2nd arg to board_init(): boot flag + */ + .text + .long 0x27051956 /* U-Boot Magic Number */ + .globl version_string +version_string: + .ascii U_BOOT_VERSION + .ascii " (", U_BOOT_DATE, " - ", U_BOOT_TIME, ")" + .ascii CONFIG_IDENT_STRING, "\0" + + . = EXC_OFF_SYS_RESET + .globl _start +_start: + li r21, BOOTFLAG_COLD /* Normal Power-On: Boot from FLASH */ + b boot_cold + sync + + . = EXC_OFF_SYS_RESET + 0x10 + + .globl _start_warm +_start_warm: + li r21, BOOTFLAG_WARM /* Software reboot */ + b boot_warm + sync + + /* the boot code is located below the exception table */ + + .globl _start_of_vectors +_start_of_vectors: + +/* Machine check */ + STD_EXCEPTION(0x200, MachineCheck, MachineCheckException) + +/* Data Storage exception. "Never" generated on the 860. */ + STD_EXCEPTION(0x300, DataStorage, UnknownException) + +/* Instruction Storage exception. "Never" generated on the 860. */ + STD_EXCEPTION(0x400, InstStorage, UnknownException) + +/* External Interrupt exception. */ + STD_EXCEPTION(0x500, ExtInterrupt, external_interrupt) + +/* Alignment exception. */ + . = 0x600 +Alignment: + EXCEPTION_PROLOG(SRR0, SRR1) + mfspr r4,DAR + stw r4,_DAR(r21) + mfspr r5,DSISR + stw r5,_DSISR(r21) + addi r3,r1,STACK_FRAME_OVERHEAD + EXC_XFER_TEMPLATE(Alignment, AlignmentException, MSR_KERNEL, COPY_EE) + +/* Program check exception */ + . = 0x700 +ProgramCheck: + EXCEPTION_PROLOG(SRR0, SRR1) + addi r3,r1,STACK_FRAME_OVERHEAD + EXC_XFER_TEMPLATE(ProgramCheck, ProgramCheckException, + MSR_KERNEL, COPY_EE) + + /* No FPU on MPC8xx. This exception is not supposed to happen. + */ + STD_EXCEPTION(0x800, FPUnavailable, UnknownException) + + /* I guess we could implement decrementer, and may have + * to someday for timekeeping. + */ + STD_EXCEPTION(0x900, Decrementer, timer_interrupt) + STD_EXCEPTION(0xa00, Trap_0a, UnknownException) + STD_EXCEPTION(0xb00, Trap_0b, UnknownException) + STD_EXCEPTION(0xc00, SystemCall, UnknownException) + STD_EXCEPTION(0xd00, SingleStep, UnknownException) + + STD_EXCEPTION(0xe00, Trap_0e, UnknownException) + STD_EXCEPTION(0xf00, Trap_0f, UnknownException) + + /* + * On the MPC8xx, this is a software emulation interrupt. It + * occurs for all unimplemented and illegal instructions. + */ + STD_EXCEPTION(0x1000, SoftEmu, SoftEmuException) + + STD_EXCEPTION(0x1100, InstructionTLBMiss, UnknownException) + STD_EXCEPTION(0x1200, DataTLBMiss, UnknownException) + STD_EXCEPTION(0x1300, InstructionTLBError, UnknownException) + STD_EXCEPTION(0x1400, DataTLBError, UnknownException) + + STD_EXCEPTION(0x1500, Reserved5, UnknownException) + STD_EXCEPTION(0x1600, Reserved6, UnknownException) + STD_EXCEPTION(0x1700, Reserved7, UnknownException) + STD_EXCEPTION(0x1800, Reserved8, UnknownException) + STD_EXCEPTION(0x1900, Reserved9, UnknownException) + STD_EXCEPTION(0x1a00, ReservedA, UnknownException) + STD_EXCEPTION(0x1b00, ReservedB, UnknownException) + + STD_EXCEPTION(0x1c00, DataBreakpoint, UnknownException) + STD_EXCEPTION(0x1d00, InstructionBreakpoint, UnknownException) + STD_EXCEPTION(0x1e00, PeripheralBreakpoint, UnknownException) + STD_EXCEPTION(0x1f00, DevPortBreakpoint, UnknownException) + + .globl _end_of_vectors +_end_of_vectors: + + . = 0x2000 + +boot_cold: +boot_warm: + /* disable everything */ + li r0, 0 + mtspr HID0, r0 + sync + mtmsr 0 + bl invalidate_bats + sync + +#ifdef CONFIG_SYS_L2 + /* init the L2 cache */ + addis r3, r0, L2_INIT@h + ori r3, r3, L2_INIT@l + sync + mtspr l2cr, r3 +#endif +#if defined(CONFIG_ALTIVEC) && defined(CONFIG_74xx) + .long 0x7e00066c + /* + * dssall instruction, gas doesn't have it yet + * ...for altivec, data stream stop all this probably + * isn't needed unless we warm (software) reboot U-Boot + */ +#endif + +#ifdef CONFIG_SYS_L2 + /* invalidate the L2 cache */ + bl l2cache_invalidate + sync +#endif +#ifdef CONFIG_SYS_BOARD_ASM_INIT + /* do early init */ + bl board_asm_init +#endif + + /* + * Calculate absolute address in FLASH and jump there + *------------------------------------------------------*/ + lis r3, CONFIG_SYS_MONITOR_BASE@h + ori r3, r3, CONFIG_SYS_MONITOR_BASE@l + addi r3, r3, in_flash - _start + EXC_OFF_SYS_RESET + mtlr r3 + blr + +in_flash: + /* let the C-code set up the rest */ + /* */ + /* Be careful to keep code relocatable ! */ + /*------------------------------------------------------*/ + + /* perform low-level init */ + /* sdram init, galileo init, etc */ + /* r3: NHR bit from HID0 */ + + /* setup the bats */ + bl setup_bats + sync + + /* + * Cache must be enabled here for stack-in-cache trick. + * This means we need to enable the BATS. + * This means: + * 1) for the EVB, original gt regs need to be mapped + * 2) need to have an IBAT for the 0xf region, + * we are running there! + * Cache should be turned on after BATs, since by default + * everything is write-through. + * The init-mem BAT can be reused after reloc. The old + * gt-regs BAT can be reused after board_init_f calls + * board_early_init_f (EVB only). + */ +#if !defined(CONFIG_BAB7xx) && !defined(CONFIG_ELPPC) && !defined(CONFIG_P3Mx) + /* enable address translation */ + bl enable_addr_trans + sync + + /* enable and invalidate the data cache */ + bl l1dcache_enable + sync +#endif +#ifdef CONFIG_SYS_INIT_RAM_LOCK + bl lock_ram_in_cache + sync +#endif + + /* set up the stack pointer in our newly created + * cache-ram (r1) */ + lis r1, (CONFIG_SYS_INIT_RAM_ADDR + CONFIG_SYS_GBL_DATA_OFFSET)@h + ori r1, r1, (CONFIG_SYS_INIT_RAM_ADDR + CONFIG_SYS_GBL_DATA_OFFSET)@l + + li r0, 0 /* Make room for stack frame header and */ + stwu r0, -4(r1) /* clear final stack frame so that */ + stwu r0, -4(r1) /* stack backtraces terminate cleanly */ + + GET_GOT /* initialize GOT access */ + + /* run low-level CPU init code (from Flash) */ + bl cpu_init_f + sync + + mr r3, r21 + + /* r3: BOOTFLAG */ + /* run 1st part of board init code (from Flash) */ + bl board_init_f + sync + + /* NOTREACHED */ + + .globl invalidate_bats +invalidate_bats: + /* invalidate BATs */ + mtspr IBAT0U, r0 + mtspr IBAT1U, r0 + mtspr IBAT2U, r0 + mtspr IBAT3U, r0 +#ifdef CONFIG_HIGH_BATS + mtspr IBAT4U, r0 + mtspr IBAT5U, r0 + mtspr IBAT6U, r0 + mtspr IBAT7U, r0 +#endif + isync + mtspr DBAT0U, r0 + mtspr DBAT1U, r0 + mtspr DBAT2U, r0 + mtspr DBAT3U, r0 +#ifdef CONFIG_HIGH_BATS + mtspr DBAT4U, r0 + mtspr DBAT5U, r0 + mtspr DBAT6U, r0 + mtspr DBAT7U, r0 +#endif + isync + sync + blr + + /* setup_bats - set them up to some initial state */ + .globl setup_bats +setup_bats: + addis r0, r0, 0x0000 + + /* IBAT 0 */ + addis r4, r0, CONFIG_SYS_IBAT0L@h + ori r4, r4, CONFIG_SYS_IBAT0L@l + addis r3, r0, CONFIG_SYS_IBAT0U@h + ori r3, r3, CONFIG_SYS_IBAT0U@l + mtspr IBAT0L, r4 + mtspr IBAT0U, r3 + isync + + /* DBAT 0 */ + addis r4, r0, CONFIG_SYS_DBAT0L@h + ori r4, r4, CONFIG_SYS_DBAT0L@l + addis r3, r0, CONFIG_SYS_DBAT0U@h + ori r3, r3, CONFIG_SYS_DBAT0U@l + mtspr DBAT0L, r4 + mtspr DBAT0U, r3 + isync + + /* IBAT 1 */ + addis r4, r0, CONFIG_SYS_IBAT1L@h + ori r4, r4, CONFIG_SYS_IBAT1L@l + addis r3, r0, CONFIG_SYS_IBAT1U@h + ori r3, r3, CONFIG_SYS_IBAT1U@l + mtspr IBAT1L, r4 + mtspr IBAT1U, r3 + isync + + /* DBAT 1 */ + addis r4, r0, CONFIG_SYS_DBAT1L@h + ori r4, r4, CONFIG_SYS_DBAT1L@l + addis r3, r0, CONFIG_SYS_DBAT1U@h + ori r3, r3, CONFIG_SYS_DBAT1U@l + mtspr DBAT1L, r4 + mtspr DBAT1U, r3 + isync + + /* IBAT 2 */ + addis r4, r0, CONFIG_SYS_IBAT2L@h + ori r4, r4, CONFIG_SYS_IBAT2L@l + addis r3, r0, CONFIG_SYS_IBAT2U@h + ori r3, r3, CONFIG_SYS_IBAT2U@l + mtspr IBAT2L, r4 + mtspr IBAT2U, r3 + isync + + /* DBAT 2 */ + addis r4, r0, CONFIG_SYS_DBAT2L@h + ori r4, r4, CONFIG_SYS_DBAT2L@l + addis r3, r0, CONFIG_SYS_DBAT2U@h + ori r3, r3, CONFIG_SYS_DBAT2U@l + mtspr DBAT2L, r4 + mtspr DBAT2U, r3 + isync + + /* IBAT 3 */ + addis r4, r0, CONFIG_SYS_IBAT3L@h + ori r4, r4, CONFIG_SYS_IBAT3L@l + addis r3, r0, CONFIG_SYS_IBAT3U@h + ori r3, r3, CONFIG_SYS_IBAT3U@l + mtspr IBAT3L, r4 + mtspr IBAT3U, r3 + isync + + /* DBAT 3 */ + addis r4, r0, CONFIG_SYS_DBAT3L@h + ori r4, r4, CONFIG_SYS_DBAT3L@l + addis r3, r0, CONFIG_SYS_DBAT3U@h + ori r3, r3, CONFIG_SYS_DBAT3U@l + mtspr DBAT3L, r4 + mtspr DBAT3U, r3 + isync + +#ifdef CONFIG_HIGH_BATS + /* IBAT 4 */ + addis r4, r0, CONFIG_SYS_IBAT4L@h + ori r4, r4, CONFIG_SYS_IBAT4L@l + addis r3, r0, CONFIG_SYS_IBAT4U@h + ori r3, r3, CONFIG_SYS_IBAT4U@l + mtspr IBAT4L, r4 + mtspr IBAT4U, r3 + isync + + /* DBAT 4 */ + addis r4, r0, CONFIG_SYS_DBAT4L@h + ori r4, r4, CONFIG_SYS_DBAT4L@l + addis r3, r0, CONFIG_SYS_DBAT4U@h + ori r3, r3, CONFIG_SYS_DBAT4U@l + mtspr DBAT4L, r4 + mtspr DBAT4U, r3 + isync + + /* IBAT 5 */ + addis r4, r0, CONFIG_SYS_IBAT5L@h + ori r4, r4, CONFIG_SYS_IBAT5L@l + addis r3, r0, CONFIG_SYS_IBAT5U@h + ori r3, r3, CONFIG_SYS_IBAT5U@l + mtspr IBAT5L, r4 + mtspr IBAT5U, r3 + isync + + /* DBAT 5 */ + addis r4, r0, CONFIG_SYS_DBAT5L@h + ori r4, r4, CONFIG_SYS_DBAT5L@l + addis r3, r0, CONFIG_SYS_DBAT5U@h + ori r3, r3, CONFIG_SYS_DBAT5U@l + mtspr DBAT5L, r4 + mtspr DBAT5U, r3 + isync + + /* IBAT 6 */ + addis r4, r0, CONFIG_SYS_IBAT6L@h + ori r4, r4, CONFIG_SYS_IBAT6L@l + addis r3, r0, CONFIG_SYS_IBAT6U@h + ori r3, r3, CONFIG_SYS_IBAT6U@l + mtspr IBAT6L, r4 + mtspr IBAT6U, r3 + isync + + /* DBAT 6 */ + addis r4, r0, CONFIG_SYS_DBAT6L@h + ori r4, r4, CONFIG_SYS_DBAT6L@l + addis r3, r0, CONFIG_SYS_DBAT6U@h + ori r3, r3, CONFIG_SYS_DBAT6U@l + mtspr DBAT6L, r4 + mtspr DBAT6U, r3 + isync + + /* IBAT 7 */ + addis r4, r0, CONFIG_SYS_IBAT7L@h + ori r4, r4, CONFIG_SYS_IBAT7L@l + addis r3, r0, CONFIG_SYS_IBAT7U@h + ori r3, r3, CONFIG_SYS_IBAT7U@l + mtspr IBAT7L, r4 + mtspr IBAT7U, r3 + isync + + /* DBAT 7 */ + addis r4, r0, CONFIG_SYS_DBAT7L@h + ori r4, r4, CONFIG_SYS_DBAT7L@l + addis r3, r0, CONFIG_SYS_DBAT7U@h + ori r3, r3, CONFIG_SYS_DBAT7U@l + mtspr DBAT7L, r4 + mtspr DBAT7U, r3 + isync +#endif + + /* bats are done, now invalidate the TLBs */ + + addis r3, 0, 0x0000 + addis r5, 0, 0x4 /* upper bound of 0x00040000 for 7400/750 */ + + isync + +tlblp: + tlbie r3 + sync + addi r3, r3, 0x1000 + cmp 0, 0, r3, r5 + blt tlblp + + blr + + .globl enable_addr_trans +enable_addr_trans: + /* enable address translation */ + mfmsr r5 + ori r5, r5, (MSR_IR | MSR_DR) + mtmsr r5 + isync + blr + + .globl disable_addr_trans +disable_addr_trans: + /* disable address translation */ + mflr r4 + mfmsr r3 + andi. r0, r3, (MSR_IR | MSR_DR) + beqlr + andc r3, r3, r0 + mtspr SRR0, r4 + mtspr SRR1, r3 + rfi + +/* + * This code finishes saving the registers to the exception frame + * and jumps to the appropriate handler for the exception. + * Register r21 is pointer into trap frame, r1 has new stack pointer. + */ + .globl transfer_to_handler +transfer_to_handler: + stw r22,_NIP(r21) + lis r22,MSR_POW@h + andc r23,r23,r22 + stw r23,_MSR(r21) + SAVE_GPR(7, r21) + SAVE_4GPRS(8, r21) + SAVE_8GPRS(12, r21) + SAVE_8GPRS(24, r21) + mflr r23 + andi. r24,r23,0x3f00 /* get vector offset */ + stw r24,TRAP(r21) + li r22,0 + stw r22,RESULT(r21) + mtspr SPRG2,r22 /* r1 is now kernel sp */ + lwz r24,0(r23) /* virtual address of handler */ + lwz r23,4(r23) /* where to go when done */ + mtspr SRR0,r24 + mtspr SRR1,r20 + mtlr r23 + SYNC + rfi /* jump to handler, enable MMU */ + +int_return: + mfmsr r28 /* Disable interrupts */ + li r4,0 + ori r4,r4,MSR_EE + andc r28,r28,r4 + SYNC /* Some chip revs need this... */ + mtmsr r28 + SYNC + lwz r2,_CTR(r1) + lwz r0,_LINK(r1) + mtctr r2 + mtlr r0 + lwz r2,_XER(r1) + lwz r0,_CCR(r1) + mtspr XER,r2 + mtcrf 0xFF,r0 + REST_10GPRS(3, r1) + REST_10GPRS(13, r1) + REST_8GPRS(23, r1) + REST_GPR(31, r1) + lwz r2,_NIP(r1) /* Restore environment */ + lwz r0,_MSR(r1) + mtspr SRR0,r2 + mtspr SRR1,r0 + lwz r0,GPR0(r1) + lwz r2,GPR2(r1) + lwz r1,GPR1(r1) + SYNC + rfi + + .globl dc_read +dc_read: + blr + + .globl get_pvr +get_pvr: + mfspr r3, PVR + blr + +/*-----------------------------------------------------------------------*/ +/* + * void relocate_code (addr_sp, gd, addr_moni) + * + * This "function" does not return, instead it continues in RAM + * after relocating the monitor code. + * + * r3 = dest + * r4 = src + * r5 = length in bytes + * r6 = cachelinesize + */ + .globl relocate_code +relocate_code: + mr r1, r3 /* Set new stack pointer */ + mr r9, r4 /* Save copy of Global Data pointer */ + mr r10, r5 /* Save copy of Destination Address */ + + GET_GOT + mr r3, r5 /* Destination Address */ + lis r4, CONFIG_SYS_MONITOR_BASE@h /* Source Address */ + ori r4, r4, CONFIG_SYS_MONITOR_BASE@l + lwz r5, GOT(__init_end) + sub r5, r5, r4 + li r6, CONFIG_SYS_CACHELINE_SIZE /* Cache Line Size */ + + /* + * Fix GOT pointer: + * + * New GOT-PTR = (old GOT-PTR - CONFIG_SYS_MONITOR_BASE) + Destination Address + * + * Offset: + */ + sub r15, r10, r4 + + /* First our own GOT */ + add r12, r12, r15 + /* then the one used by the C code */ + add r30, r30, r15 + + /* + * Now relocate code + */ +#ifdef CONFIG_ECC + bl board_relocate_rom + sync + mr r3, r10 /* Destination Address */ + lis r4, CONFIG_SYS_MONITOR_BASE@h /* Source Address */ + ori r4, r4, CONFIG_SYS_MONITOR_BASE@l + lwz r5, GOT(__init_end) + sub r5, r5, r4 + li r6, CONFIG_SYS_CACHELINE_SIZE /* Cache Line Size */ +#else + cmplw cr1,r3,r4 + addi r0,r5,3 + srwi. r0,r0,2 + beq cr1,4f /* In place copy is not necessary */ + beq 7f /* Protect against 0 count */ + mtctr r0 + bge cr1,2f + + la r8,-4(r4) + la r7,-4(r3) +1: lwzu r0,4(r8) + stwu r0,4(r7) + bdnz 1b + b 4f + +2: slwi r0,r0,2 + add r8,r4,r0 + add r7,r3,r0 +3: lwzu r0,-4(r8) + stwu r0,-4(r7) + bdnz 3b +#endif +/* + * Now flush the cache: note that we must start from a cache aligned + * address. Otherwise we might miss one cache line. + */ +4: cmpwi r6,0 + add r5,r3,r5 + beq 7f /* Always flush prefetch queue in any case */ + subi r0,r6,1 + andc r3,r3,r0 + mr r4,r3 +5: dcbst 0,r4 + add r4,r4,r6 + cmplw r4,r5 + blt 5b + sync /* Wait for all dcbst to complete on bus */ + mr r4,r3 +6: icbi 0,r4 + add r4,r4,r6 + cmplw r4,r5 + blt 6b +7: sync /* Wait for all icbi to complete on bus */ + isync + +/* + * We are done. Do not return, instead branch to second part of board + * initialization, now running from RAM. + */ + addi r0, r10, in_ram - _start + EXC_OFF_SYS_RESET + mtlr r0 + blr + +in_ram: +#ifdef CONFIG_ECC + bl board_init_ecc +#endif + /* + * Relocation Function, r12 point to got2+0x8000 + * + * Adjust got2 pointers, no need to check for 0, this code + * already puts a few entries in the table. + */ + li r0,__got2_entries@sectoff@l + la r3,GOT(_GOT2_TABLE_) + lwz r11,GOT(_GOT2_TABLE_) + mtctr r0 + sub r11,r3,r11 + addi r3,r3,-4 +1: lwzu r0,4(r3) + cmpwi r0,0 + beq- 2f + add r0,r0,r11 + stw r0,0(r3) +2: bdnz 1b + + /* + * Now adjust the fixups and the pointers to the fixups + * in case we need to move ourselves again. + */ + li r0,__fixup_entries@sectoff@l + lwz r3,GOT(_FIXUP_TABLE_) + cmpwi r0,0 + mtctr r0 + addi r3,r3,-4 + beq 4f +3: lwzu r4,4(r3) + lwzux r0,r4,r11 + add r0,r0,r11 + stw r10,0(r3) + stw r0,0(r4) + bdnz 3b +4: +/* clear_bss: */ + /* + * Now clear BSS segment + */ + lwz r3,GOT(__bss_start) + lwz r4,GOT(_end) + + cmplw 0, r3, r4 + beq 6f + + li r0, 0 +5: + stw r0, 0(r3) + addi r3, r3, 4 + cmplw 0, r3, r4 + bne 5b +6: + mr r3, r10 /* Destination Address */ +#if defined(CONFIG_AMIGAONEG3SE) || \ + defined(CONFIG_DB64360) || \ + defined(CONFIG_DB64460) || \ + defined(CONFIG_CPCI750) || \ + defined(CONFIG_PPMC7XX) || \ + defined(CONFIG_P3Mx) + mr r4, r9 /* Use RAM copy of the global data */ +#endif + bl after_reloc + + /* not reached - end relocate_code */ +/*-----------------------------------------------------------------------*/ + + /* + * Copy exception vector code to low memory + * + * r3: dest_addr + * r7: source address, r8: end address, r9: target address + */ + .globl trap_init +trap_init: + mflr r4 /* save link register */ + GET_GOT + lwz r7, GOT(_start) + lwz r8, GOT(_end_of_vectors) + + li r9, 0x100 /* reset vector always at 0x100 */ + + cmplw 0, r7, r8 + bgelr /* return if r7>=r8 - just in case */ +1: + lwz r0, 0(r7) + stw r0, 0(r9) + addi r7, r7, 4 + addi r9, r9, 4 + cmplw 0, r7, r8 + bne 1b + + /* + * relocate `hdlr' and `int_return' entries + */ + li r7, .L_MachineCheck - _start + EXC_OFF_SYS_RESET + li r8, Alignment - _start + EXC_OFF_SYS_RESET +2: + bl trap_reloc + addi r7, r7, 0x100 /* next exception vector */ + cmplw 0, r7, r8 + blt 2b + + li r7, .L_Alignment - _start + EXC_OFF_SYS_RESET + bl trap_reloc + + li r7, .L_ProgramCheck - _start + EXC_OFF_SYS_RESET + bl trap_reloc + + li r7, .L_FPUnavailable - _start + EXC_OFF_SYS_RESET + li r8, SystemCall - _start + EXC_OFF_SYS_RESET +3: + bl trap_reloc + addi r7, r7, 0x100 /* next exception vector */ + cmplw 0, r7, r8 + blt 3b + + li r7, .L_SingleStep - _start + EXC_OFF_SYS_RESET + li r8, _end_of_vectors - _start + EXC_OFF_SYS_RESET +4: + bl trap_reloc + addi r7, r7, 0x100 /* next exception vector */ + cmplw 0, r7, r8 + blt 4b + + /* enable execptions from RAM vectors */ + mfmsr r7 + li r8,MSR_IP + andc r7,r7,r8 + mtmsr r7 + + mtlr r4 /* restore link register */ + blr + +#ifdef CONFIG_SYS_INIT_RAM_LOCK +lock_ram_in_cache: + /* Allocate Initial RAM in data cache. + */ + lis r3, (CONFIG_SYS_INIT_RAM_ADDR & ~31)@h + ori r3, r3, (CONFIG_SYS_INIT_RAM_ADDR & ~31)@l + li r4, ((CONFIG_SYS_INIT_RAM_END & ~31) + \ + (CONFIG_SYS_INIT_RAM_ADDR & 31) + 31) / 32 + mtctr r4 +1: + dcbz r0, r3 + addi r3, r3, 32 + bdnz 1b + + /* Lock the data cache */ + mfspr r0, HID0 + ori r0, r0, 0x1000 + sync + mtspr HID0, r0 + sync + blr + +.globl unlock_ram_in_cache +unlock_ram_in_cache: + /* invalidate the INIT_RAM section */ + lis r3, (CONFIG_SYS_INIT_RAM_ADDR & ~31)@h + ori r3, r3, (CONFIG_SYS_INIT_RAM_ADDR & ~31)@l + li r4, ((CONFIG_SYS_INIT_RAM_END & ~31) + \ + (CONFIG_SYS_INIT_RAM_ADDR & 31) + 31) / 32 + mtctr r4 +1: icbi r0, r3 + addi r3, r3, 32 + bdnz 1b + sync /* Wait for all icbi to complete on bus */ + isync + + /* Unlock the data cache and invalidate it */ + mfspr r0, HID0 + li r3,0x1000 + andc r0,r0,r3 + li r3,0x0400 + or r0,r0,r3 + sync + mtspr HID0, r0 + sync + blr +#endif diff --git a/arch/powerpc/cpu/74xx_7xx/traps.c b/arch/powerpc/cpu/74xx_7xx/traps.c new file mode 100644 index 0000000..5073b05 --- /dev/null +++ b/arch/powerpc/cpu/74xx_7xx/traps.c @@ -0,0 +1,252 @@ +/* + * linux/arch/powerpc/kernel/traps.c + * + * Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org) + * + * Modified by Cort Dougan (cort@cs.nmt.edu) + * and Paul Mackerras (paulus@cs.anu.edu.au) + * + * (C) Copyright 2000 + * Wolfgang Denk, DENX Software Engineering, wd@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 + */ + +/* + * This file handles the architecture-dependent parts of hardware exceptions + */ + +#include <common.h> +#include <command.h> +#include <kgdb.h> +#include <asm/processor.h> + +#ifdef CONFIG_AMIGAONEG3SE +DECLARE_GLOBAL_DATA_PTR; +#endif + +/* Returns 0 if exception not found and fixup otherwise. */ +extern unsigned long search_exception_table(unsigned long); + +/* THIS NEEDS CHANGING to use the board info structure. +*/ +#ifdef CONFIG_AMIGAONEG3SE +#define END_OF_MEM (gd->bd->bi_memstart + gd->bd->bi_memsize) +#else +#define END_OF_MEM 0x02000000 +#endif + +/* + * Trap & Exception support + */ + +void +print_backtrace(unsigned long *sp) +{ + int cnt = 0; + unsigned long i; + + printf("Call backtrace: "); + while (sp) { + if ((uint)sp > END_OF_MEM) + break; + + i = sp[1]; + if (cnt++ % 7 == 0) + printf("\n"); + printf("%08lX ", i); + if (cnt > 32) break; + sp = (unsigned long *)*sp; + } + printf("\n"); +} + +void +show_regs(struct pt_regs * regs) +{ + int i; + + printf("NIP: %08lX XER: %08lX LR: %08lX REGS:" + " %p TRAP: %04lx DAR: %08lX\n", + regs->nip, regs->xer, regs->link, regs, regs->trap, regs->dar); + printf("MSR: %08lx EE: %01x PR: %01x FP:" + " %01x ME: %01x IR/DR: %01x%01x\n", + regs->msr, regs->msr&MSR_EE ? 1 : 0, regs->msr&MSR_PR ? 1 : 0, + regs->msr & MSR_FP ? 1 : 0,regs->msr&MSR_ME ? 1 : 0, + regs->msr&MSR_IR ? 1 : 0, + regs->msr&MSR_DR ? 1 : 0); + + printf("\n"); + for (i = 0; i < 32; i++) { + if ((i % 8) == 0) + { + printf("GPR%02d: ", i); + } + + printf("%08lX ", regs->gpr[i]); + if ((i % 8) == 7) + { + printf("\n"); + } + } +} + + +void +_exception(int signr, struct pt_regs *regs) +{ + show_regs(regs); + print_backtrace((unsigned long *)regs->gpr[1]); + panic("Exception in kernel pc %lx signal %d",regs->nip,signr); +} + +void +MachineCheckException(struct pt_regs *regs) +{ + unsigned long fixup; + + /* Probing PCI using config cycles cause this exception + * when a device is not present. Catch it and return to + * the PCI exception handler. + */ + if ((fixup = search_exception_table(regs->nip)) != 0) { + regs->nip = fixup; + return; + } + +#if defined(CONFIG_CMD_KGDB) + if (debugger_exception_handler && (*debugger_exception_handler)(regs)) + return; +#endif + + printf("Machine check in kernel mode.\n"); + printf("Caused by (from msr): "); + printf("regs %p ",regs); + switch( regs->msr & 0x000F0000) { + case (0x80000000>>12): + printf("Machine check signal - probably due to mm fault\n" + "with mmu off\n"); + break; + case (0x80000000>>13): + printf("Transfer error ack signal\n"); + break; + case (0x80000000>>14): + printf("Data parity signal\n"); + break; + case (0x80000000>>15): + printf("Address parity signal\n"); + break; + default: + printf("Unknown values in msr\n"); + } + show_regs(regs); + print_backtrace((unsigned long *)regs->gpr[1]); + panic("machine check"); +} + +void +AlignmentException(struct pt_regs *regs) +{ +#if defined(CONFIG_CMD_KGDB) + if (debugger_exception_handler && (*debugger_exception_handler)(regs)) + return; +#endif + show_regs(regs); + print_backtrace((unsigned long *)regs->gpr[1]); + panic("Alignment Exception"); +} + +void +ProgramCheckException(struct pt_regs *regs) +{ + unsigned char *p = regs ? (unsigned char *)(regs->nip) : NULL; + int i, j; + +#if defined(CONFIG_CMD_KGDB) + if (debugger_exception_handler && (*debugger_exception_handler)(regs)) + return; +#endif + show_regs(regs); + + p = (unsigned char *) ((unsigned long)p & 0xFFFFFFE0); + p -= 32; + for (i = 0; i < 256; i+=16) { + printf("%08x: ", (unsigned int)p+i); + for (j = 0; j < 16; j++) { + printf("%02x ", p[i+j]); + } + printf("\n"); + } + + print_backtrace((unsigned long *)regs->gpr[1]); + panic("Program Check Exception"); +} + +void +SoftEmuException(struct pt_regs *regs) +{ +#if defined(CONFIG_CMD_KGDB) + if (debugger_exception_handler && (*debugger_exception_handler)(regs)) + return; +#endif + show_regs(regs); + print_backtrace((unsigned long *)regs->gpr[1]); + panic("Software Emulation Exception"); +} + + +void +UnknownException(struct pt_regs *regs) +{ +#if defined(CONFIG_CMD_KGDB) + if (debugger_exception_handler && (*debugger_exception_handler)(regs)) + return; +#endif + printf("Bad trap at PC: %lx, SR: %lx, vector=%lx\n", + regs->nip, regs->msr, regs->trap); + _exception(0, regs); +} + +/* Probe an address by reading. If not present, return -1, otherwise + * return 0. + */ +int +addr_probe(uint *addr) +{ +#if 0 + int retval; + + __asm__ __volatile__( \ + "1: lwz %0,0(%1)\n" \ + " eieio\n" \ + " li %0,0\n" \ + "2:\n" \ + ".section .fixup,\"ax\"\n" \ + "3: li %0,-1\n" \ + " b 2b\n" \ + ".section __ex_table,\"a\"\n" \ + " .align 2\n" \ + " .long 1b,3b\n" \ + ".text" \ + : "=r" (retval) : "r"(addr)); + + return (retval); +#endif + return 0; +} diff --git a/arch/powerpc/cpu/mpc512x/Makefile b/arch/powerpc/cpu/mpc512x/Makefile new file mode 100644 index 0000000..1719c66 --- /dev/null +++ b/arch/powerpc/cpu/mpc512x/Makefile @@ -0,0 +1,63 @@ +# +# (C) Copyright 2007-2009 DENX Software Engineering +# +# 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 $(TOPDIR)/config.mk + +$(shell mkdir -p $(OBJTREE)/board/freescale/common) + +LIB = $(obj)lib$(CPU).a + +START = start.o +COBJS-y := cpu.o +COBJS-y += traps.o +COBJS-y += cpu_init.o +COBJS-y += fixed_sdram.o +COBJS-y += i2c.o +COBJS-y += interrupts.o +COBJS-y += iopin.o +COBJS-y += serial.o +COBJS-y += speed.o +COBJS-${CONFIG_FSL_DIU_FB} += diu.o +COBJS-${CONFIG_FSL_DIU_FB} += ../../../../board/freescale/common/fsl_diu_fb.o +COBJS-${CONFIG_FSL_DIU_FB} += ../../../../board/freescale/common/fsl_logo_bmp.o +COBJS-${CONFIG_CMD_IDE} += ide.o +COBJS-${CONFIG_IIM} += iim.o +COBJS-$(CONFIG_PCI) += pci.o + +COBJS := $(COBJS-y) +SRCS := $(START:.o=.S) $(SOBJS:.o=.S) $(COBJS:.o=.c) +OBJS := $(addprefix $(obj),$(SOBJS) $(COBJS)) +START := $(addprefix $(obj),$(START)) + +all: $(obj).depend $(START) $(LIB) + +$(LIB): $(OBJS) + $(AR) $(ARFLAGS) $@ $(OBJS) + +######################################################################### + +# defines $(obj).depend target +include $(SRCTREE)/rules.mk + +sinclude $(obj).depend + +######################################################################### diff --git a/arch/powerpc/cpu/mpc512x/asm-offsets.h b/arch/powerpc/cpu/mpc512x/asm-offsets.h new file mode 100644 index 0000000..957d4be2 --- /dev/null +++ b/arch/powerpc/cpu/mpc512x/asm-offsets.h @@ -0,0 +1,15 @@ +/* + * needed for arch/powerpc/cpu/mpc512x/start.S + * + * These should be auto-generated + */ +#define LPCS0AW 0x0024 +#define SRAMBAR 0x00C4 +#define SWCRR 0x0904 +#define LPC_OFFSET 0x10000 +#define CS0_CONFIG 0x00000 +#define CS_CTRL 0x00020 +#define CS_CTRL_ME 0x01000000 /* CS Master Enable bit */ + +#define EXC_OFF_SYS_RESET 0x0100 +#define _START_OFFSET EXC_OFF_SYS_RESET diff --git a/arch/powerpc/cpu/mpc512x/config.mk b/arch/powerpc/cpu/mpc512x/config.mk new file mode 100644 index 0000000..b29edb1 --- /dev/null +++ b/arch/powerpc/cpu/mpc512x/config.mk @@ -0,0 +1,29 @@ +# +# (C) Copyright 2007-2009 DENX Software Engineering +# +# 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 +# +PLATFORM_RELFLAGS += -fPIC -meabi + +PLATFORM_CPPFLAGS += -DCONFIG_MPC512X -DCONFIG_E300 \ + -ffixed-r2 -msoft-float -mcpu=603e + +# Use default linker script. +# A board port can override this setting in board/*/config.mk +LDSCRIPT := $(SRCTREE)/arch/powerpc/cpu/mpc512x/u-boot.lds diff --git a/arch/powerpc/cpu/mpc512x/cpu.c b/arch/powerpc/cpu/mpc512x/cpu.c new file mode 100644 index 0000000..09cbd20 --- /dev/null +++ b/arch/powerpc/cpu/mpc512x/cpu.c @@ -0,0 +1,216 @@ +/* + * (C) Copyright 2007-2010 DENX Software Engineering + * Copyright (C) 2004-2006 Freescale Semiconductor, Inc. + * + * 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 + */ + +/* + * CPU specific code for the MPC512x family. + * + * Derived from the MPC83xx code. + */ + +#include <common.h> +#include <command.h> +#include <net.h> +#include <netdev.h> +#include <asm/processor.h> +#include <asm/io.h> + +#if defined(CONFIG_OF_LIBFDT) +#include <fdt_support.h> +#endif + +DECLARE_GLOBAL_DATA_PTR; + +int checkcpu (void) +{ + volatile immap_t *immr = (immap_t *) CONFIG_SYS_IMMR; + ulong clock = gd->cpu_clk; + u32 pvr = get_pvr (); + u32 spridr = in_be32(&immr->sysconf.spridr); + char buf1[32], buf2[32]; + + puts ("CPU: "); + + switch (spridr & 0xffff0000) { + case SPR_5121E: + puts ("MPC5121e "); + break; + default: + printf ("Unknown part ID %08x ", spridr & 0xffff0000); + } + printf ("rev. %d.%d, Core ", SVR_MJREV (spridr), SVR_MNREV (spridr)); + + switch (pvr & 0xffff0000) { + case PVR_E300C4: + puts ("e300c4 "); + break; + default: + puts ("unknown "); + } + printf ("at %s MHz, CSB at %s MHz (RSR=0x%04lx)\n", + strmhz(buf1, clock), + strmhz(buf2, gd->csb_clk), + gd->reset_status & 0xffff); + return 0; +} + + +int +do_reset (cmd_tbl_t * cmdtp, int flag, int argc, char *argv[]) +{ + ulong msr; + volatile immap_t *immap = (immap_t *) CONFIG_SYS_IMMR; + + /* Interrupts and MMU off */ + __asm__ __volatile__ ("mfmsr %0":"=r" (msr):); + + msr &= ~( MSR_EE | MSR_IR | MSR_DR); + __asm__ __volatile__ ("mtmsr %0"::"r" (msr)); + + /* + * Enable Reset Control Reg - "RSTE" is the magic word that let us go + */ + out_be32(&immap->reset.rpr, 0x52535445); + + /* Verify Reset Control Reg is enabled */ + while (!(in_be32(&immap->reset.rcer) & RCER_CRE)) + ; + + printf ("Resetting the board.\n"); + udelay(200); + + /* Perform reset */ + out_be32(&immap->reset.rcr, RCR_SWHR); + + /* Unreached... */ + return 1; +} + + +/* + * Get timebase clock frequency (like cpu_clk in Hz) + */ +unsigned long get_tbclk (void) +{ + ulong tbclk; + + tbclk = (gd->bus_clk + 3L) / 4L; + + return tbclk; +} + + +#if defined(CONFIG_WATCHDOG) +void watchdog_reset (void) +{ + int re_enable = disable_interrupts (); + + /* Reset watchdog */ + volatile immap_t *immr = (immap_t *) CONFIG_SYS_IMMR; + out_be32(&immr->wdt.swsrr, 0x556c); + out_be32(&immr->wdt.swsrr, 0xaa39); + + if (re_enable) + enable_interrupts (); +} +#endif + +#ifdef CONFIG_OF_LIBFDT + +#ifdef CONFIG_OF_SUPPORT_OLD_DEVICE_TREES +/* + * fdt setup for old device trees + * fix up + * cpu clocks + * soc clocks + * ethernet addresses + */ +static void old_ft_cpu_setup(void *blob, bd_t *bd) +{ + /* + * avoid fixing up by path because that + * produces scary error messages + */ + uchar enetaddr[6]; + + /* + * old device trees have ethernet nodes with + * device_type = "network" + */ + eth_getenv_enetaddr("ethaddr", enetaddr); + do_fixup_by_prop(blob, "device_type", "network", 8, + "local-mac-address", enetaddr, 6, 0); + do_fixup_by_prop(blob, "device_type", "network", 8, + "address", enetaddr, 6, 0); + /* + * old device trees have soc nodes with + * device_type = "soc" + */ + do_fixup_by_prop_u32(blob, "device_type", "soc", 4, + "bus-frequency", bd->bi_ipsfreq, 0); +} +#endif + +static void ft_clock_setup(void *blob, bd_t *bd) +{ + char *cpu_path = "/cpus/" OF_CPU; + + /* + * fixup cpu clocks using path + */ + do_fixup_by_path_u32(blob, cpu_path, + "timebase-frequency", OF_TBCLK, 1); + do_fixup_by_path_u32(blob, cpu_path, + "bus-frequency", bd->bi_busfreq, 1); + do_fixup_by_path_u32(blob, cpu_path, + "clock-frequency", bd->bi_intfreq, 1); + /* + * fixup soc clocks using compatible + */ + do_fixup_by_compat_u32(blob, OF_SOC_COMPAT, + "bus-frequency", bd->bi_ipsfreq, 1); +} + +void ft_cpu_setup(void *blob, bd_t *bd) +{ +#ifdef CONFIG_OF_SUPPORT_OLD_DEVICE_TREES + old_ft_cpu_setup(blob, bd); +#endif + ft_clock_setup(blob, bd); +#ifdef CONFIG_HAS_ETH0 + fdt_fixup_ethernet(blob); +#endif + fdt_fixup_memory(blob, (u64)bd->bi_memstart, (u64)bd->bi_memsize); +} +#endif + +#ifdef CONFIG_MPC512x_FEC +/* Default initializations for FEC controllers. To override, + * create a board-specific function called: + * int board_eth_init(bd_t *bis) + */ + +int cpu_eth_init(bd_t *bis) +{ + return mpc512x_fec_initialize(bis); +} +#endif diff --git a/arch/powerpc/cpu/mpc512x/cpu_init.c b/arch/powerpc/cpu/mpc512x/cpu_init.c new file mode 100644 index 0000000..fe6beaf --- /dev/null +++ b/arch/powerpc/cpu/mpc512x/cpu_init.c @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2004-2006 Freescale Semiconductor, Inc. + * Copyright (C) 2007-2009 DENX Software Engineering + * + * 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 + * + * Derived from the MPC83xx code. + * + */ + +#include <common.h> +#include <asm/io.h> +#include <asm/processor.h> + +DECLARE_GLOBAL_DATA_PTR; + +/* + * Set up the memory map, initialize registers, + */ +void cpu_init_f (volatile immap_t * im) +{ + u32 ips_div; + + /* Pointer is writable since we allocated a register for it */ + gd = (gd_t *) (CONFIG_SYS_INIT_RAM_ADDR + CONFIG_SYS_GBL_DATA_OFFSET); + + /* Clear initial global data */ + memset ((void *) gd, 0, sizeof (gd_t)); + + /* system performance tweaking */ + +#ifdef CONFIG_SYS_ACR_PIPE_DEP + /* Arbiter pipeline depth */ + out_be32(&im->arbiter.acr, + (im->arbiter.acr & ~ACR_PIPE_DEP) | + (CONFIG_SYS_ACR_PIPE_DEP << ACR_PIPE_DEP_SHIFT) + ); +#endif + +#ifdef CONFIG_SYS_ACR_RPTCNT + /* Arbiter repeat count */ + out_be32(im->arbiter.acr, + (im->arbiter.acr & ~(ACR_RPTCNT)) | + (CONFIG_SYS_ACR_RPTCNT << ACR_RPTCNT_SHIFT) + ); +#endif + + /* RSR - Reset Status Register - clear all status */ + gd->reset_status = im->reset.rsr; + out_be32(&im->reset.rsr, ~RSR_RES); + + /* + * RMR - Reset Mode Register - enable checkstop reset + */ + out_be32(&im->reset.rmr, RMR_CSRE & (1 << RMR_CSRE_SHIFT)); + + /* Set IPS-CSB divider: IPS = 1/2 CSB */ + ips_div = in_be32(&im->clk.scfr[0]); + ips_div &= ~(SCFR1_IPS_DIV_MASK); + ips_div |= SCFR1_IPS_DIV << SCFR1_IPS_DIV_SHIFT; + out_be32(&im->clk.scfr[0], ips_div); + + /* + * Enable Time Base/Decrementer + * + * NOTICE: TB needs to be enabled as early as possible in order to + * have udelay() working; if not enabled, usually leads to a hang, like + * during FLASH chip identification etc. + */ + setbits_be32(&im->sysconf.spcr, SPCR_TBEN); +} + +int cpu_init_r (void) +{ + return 0; +} diff --git a/arch/powerpc/cpu/mpc512x/diu.c b/arch/powerpc/cpu/mpc512x/diu.c new file mode 100644 index 0000000..9361161 --- /dev/null +++ b/arch/powerpc/cpu/mpc512x/diu.c @@ -0,0 +1,188 @@ +/* + * Copyright 2008 Freescale Semiconductor, Inc. + * York Sun <yorksun@freescale.com> + * + * FSL DIU Framebuffer driver + * + * 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> +#include <command.h> +#include <asm/io.h> + +#include "../../../../board/freescale/common/fsl_diu_fb.h" + +#if defined(CONFIG_VIDEO) || defined(CONFIG_CFB_CONSOLE) +#include <stdio_dev.h> +#include <video_fb.h> +#endif + +#ifdef CONFIG_FSL_DIU_LOGO_BMP +extern unsigned int FSL_Logo_BMP[]; +#else +#define FSL_Logo_BMP NULL +#endif + +static int xres, yres; + +void diu_set_pixel_clock(unsigned int pixclock) +{ + volatile immap_t *immap = (immap_t *)CONFIG_SYS_IMMR; + volatile clk512x_t *clk = &immap->clk; + volatile unsigned int *clkdvdr = &clk->scfr[0]; + unsigned long speed_ccb, temp, pixval; + + speed_ccb = get_bus_freq(0) * 4; + temp = 1000000000/pixclock; + temp *= 1000; + pixval = speed_ccb / temp; + debug("DIU pixval = %lu\n", pixval); + + /* Modify PXCLK in GUTS CLKDVDR */ + debug("DIU: Current value of CLKDVDR = 0x%08x\n", in_be32(clkdvdr)); + temp = in_be32(clkdvdr) & 0xFFFFFF00; + out_be32(clkdvdr, temp | (pixval & 0xFF)); + debug("DIU: Modified value of CLKDVDR = 0x%08x\n", in_be32(clkdvdr)); +} + +char *valid_bmp(char *addr) +{ + unsigned long h_addr; + + h_addr = simple_strtoul(addr, NULL, 16); + if (h_addr < CONFIG_SYS_FLASH_BASE || + h_addr >= (CONFIG_SYS_FLASH_BASE + CONFIG_SYS_FLASH_SIZE - 1)) { + printf("bmp addr %lx is not a valid flash address\n", h_addr); + return 0; + } else if ((*(char *)(h_addr) != 'B') || (*(char *)(h_addr+1) != 'M')) { + printf("bmp addr is not a bmp\n"); + return 0; + } else + return (char *)h_addr; +} + +int mpc5121_diu_init(void) +{ + unsigned int pixel_format; + char *bmp = NULL; + char *bmp_env; + + xres = 1024; + yres = 768; + pixel_format = 0x88883316; + + debug("mpc5121_diu_init\n"); + bmp_env = getenv("diu_bmp_addr"); + if (bmp_env) { + bmp = valid_bmp(bmp_env); + } + if (!bmp) + bmp = (char *)FSL_Logo_BMP; + return fsl_diu_init(xres, pixel_format, 0, (unsigned char *)bmp); +} + +int mpc5121diu_init_show_bmp(cmd_tbl_t *cmdtp, + int flag, int argc, char *argv[]) +{ + unsigned int addr; + + if (argc < 2) { + cmd_usage(cmdtp); + return 1; + } + + if (!strncmp(argv[1], "init", 4)) { +#if defined(CONFIG_VIDEO) || defined(CONFIG_CFB_CONSOLE) + fsl_diu_clear_screen(); + drv_video_init(); +#else + return mpc5121_diu_init(); +#endif + } else { + addr = simple_strtoul(argv[1], NULL, 16); + fsl_diu_clear_screen(); + fsl_diu_display_bmp((unsigned char *)addr, 0, 0, 0); + } + + return 0; +} + +U_BOOT_CMD( + diufb, CONFIG_SYS_MAXARGS, 1, mpc5121diu_init_show_bmp, + "Init or Display BMP file", + "init\n - initialize DIU\n" + "addr\n - display bmp at address 'addr'" + ); + + +#if defined(CONFIG_VIDEO) || defined(CONFIG_CFB_CONSOLE) + +/* + * The Graphic Device + */ +GraphicDevice ctfb; +void *video_hw_init(void) +{ + GraphicDevice *pGD = (GraphicDevice *) &ctfb; + struct fb_info *info; + + if (mpc5121_diu_init() < 0) + return NULL; + + /* fill in Graphic device struct */ + sprintf(pGD->modeIdent, "%dx%dx%d %dkHz %dHz", + xres, yres, 32, 64, 60); + + pGD->frameAdrs = (unsigned int)fsl_fb_open(&info); + pGD->winSizeX = xres; + pGD->winSizeY = yres - info->logo_height; + pGD->plnSizeX = pGD->winSizeX; + pGD->plnSizeY = pGD->winSizeY; + + pGD->gdfBytesPP = 4; + pGD->gdfIndex = GDF_32BIT_X888RGB; + + pGD->isaBase = 0; + pGD->pciBase = 0; + pGD->memSize = info->screen_size - info->logo_size; + + /* Cursor Start Address */ + pGD->dprBase = 0; + pGD->vprBase = 0; + pGD->cprBase = 0; + + return (void *)pGD; +} + +/** + * Set the LUT + * + * @index: color number + * @r: red + * @b: blue + * @g: green + */ +void video_set_lut + (unsigned int index, unsigned char r, unsigned char g, unsigned char b) +{ + return; +} + +#endif /* defined(CONFIG_VIDEO) || defined(CONFIG_CFB_CONSOLE) */ diff --git a/arch/powerpc/cpu/mpc512x/fixed_sdram.c b/arch/powerpc/cpu/mpc512x/fixed_sdram.c new file mode 100644 index 0000000..442b5fc --- /dev/null +++ b/arch/powerpc/cpu/mpc512x/fixed_sdram.c @@ -0,0 +1,152 @@ +/* + * (C) Copyright 2007-2009 DENX Software Engineering + * + * 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> +#include <asm/io.h> +#include <asm/mpc512x.h> + +/* + * MDDRC Config Runtime Settings + */ +ddr512x_config_t default_mddrc_config = { + .ddr_sys_config = CONFIG_SYS_MDDRC_SYS_CFG, + .ddr_time_config0 = CONFIG_SYS_MDDRC_TIME_CFG0, + .ddr_time_config1 = CONFIG_SYS_MDDRC_TIME_CFG1, + .ddr_time_config2 = CONFIG_SYS_MDDRC_TIME_CFG2, +}; + +u32 default_init_seq[] = { + CONFIG_SYS_DDRCMD_NOP, + CONFIG_SYS_DDRCMD_NOP, + CONFIG_SYS_DDRCMD_NOP, + CONFIG_SYS_DDRCMD_NOP, + CONFIG_SYS_DDRCMD_NOP, + CONFIG_SYS_DDRCMD_NOP, + CONFIG_SYS_DDRCMD_NOP, + CONFIG_SYS_DDRCMD_NOP, + CONFIG_SYS_DDRCMD_NOP, + CONFIG_SYS_DDRCMD_NOP, + CONFIG_SYS_DDRCMD_PCHG_ALL, + CONFIG_SYS_DDRCMD_NOP, + CONFIG_SYS_DDRCMD_RFSH, + CONFIG_SYS_DDRCMD_NOP, + CONFIG_SYS_DDRCMD_RFSH, + CONFIG_SYS_DDRCMD_NOP, + CONFIG_SYS_MICRON_INIT_DEV_OP, + CONFIG_SYS_DDRCMD_NOP, + CONFIG_SYS_DDRCMD_EM2, + CONFIG_SYS_DDRCMD_NOP, + CONFIG_SYS_DDRCMD_PCHG_ALL, + CONFIG_SYS_DDRCMD_EM2, + CONFIG_SYS_DDRCMD_EM3, + CONFIG_SYS_DDRCMD_EN_DLL, + CONFIG_SYS_MICRON_INIT_DEV_OP, + CONFIG_SYS_DDRCMD_PCHG_ALL, + CONFIG_SYS_DDRCMD_RFSH, + CONFIG_SYS_MICRON_INIT_DEV_OP, + CONFIG_SYS_DDRCMD_OCD_DEFAULT, + CONFIG_SYS_DDRCMD_PCHG_ALL, + CONFIG_SYS_DDRCMD_NOP +}; + +/* + * fixed sdram init: + * The board doesn't use memory modules that have serial presence + * detect or similar mechanism for discovery of the DRAM settings + */ +long int fixed_sdram(ddr512x_config_t *mddrc_config, + u32 *dram_init_seq, int seq_sz) +{ + volatile immap_t *im = (immap_t *)CONFIG_SYS_IMMR; + u32 msize = CONFIG_SYS_DDR_SIZE * 1024 * 1024; + u32 msize_log2 = __ilog2(msize); + u32 i; + + /* take default settings and init sequence if necessary */ + if (mddrc_config == NULL) + mddrc_config = &default_mddrc_config; + if (dram_init_seq == NULL) { + dram_init_seq = default_init_seq; + seq_sz = sizeof(default_init_seq)/sizeof(u32); + } + + /* Initialize IO Control */ + out_be32(&im->io_ctrl.io_control_mem, IOCTRL_MUX_DDR); + + /* Initialize DDR Local Window */ + out_be32(&im->sysconf.ddrlaw.bar, CONFIG_SYS_DDR_BASE & 0xFFFFF000); + out_be32(&im->sysconf.ddrlaw.ar, msize_log2 - 1); + sync_law(&im->sysconf.ddrlaw.ar); + + /* DDR Enable */ + out_be32(&im->mddrc.ddr_sys_config, MDDRC_SYS_CFG_EN); + + /* Initialize DDR Priority Manager */ + out_be32(&im->mddrc.prioman_config1, CONFIG_SYS_MDDRCGRP_PM_CFG1); + out_be32(&im->mddrc.prioman_config2, CONFIG_SYS_MDDRCGRP_PM_CFG2); + out_be32(&im->mddrc.hiprio_config, CONFIG_SYS_MDDRCGRP_HIPRIO_CFG); + out_be32(&im->mddrc.lut_table0_main_upper, CONFIG_SYS_MDDRCGRP_LUT0_MU); + out_be32(&im->mddrc.lut_table0_main_lower, CONFIG_SYS_MDDRCGRP_LUT0_ML); + out_be32(&im->mddrc.lut_table1_main_upper, CONFIG_SYS_MDDRCGRP_LUT1_MU); + out_be32(&im->mddrc.lut_table1_main_lower, CONFIG_SYS_MDDRCGRP_LUT1_ML); + out_be32(&im->mddrc.lut_table2_main_upper, CONFIG_SYS_MDDRCGRP_LUT2_MU); + out_be32(&im->mddrc.lut_table2_main_lower, CONFIG_SYS_MDDRCGRP_LUT2_ML); + out_be32(&im->mddrc.lut_table3_main_upper, CONFIG_SYS_MDDRCGRP_LUT3_MU); + out_be32(&im->mddrc.lut_table3_main_lower, CONFIG_SYS_MDDRCGRP_LUT3_ML); + out_be32(&im->mddrc.lut_table4_main_upper, CONFIG_SYS_MDDRCGRP_LUT4_MU); + out_be32(&im->mddrc.lut_table4_main_lower, CONFIG_SYS_MDDRCGRP_LUT4_ML); + out_be32(&im->mddrc.lut_table0_alternate_upper, CONFIG_SYS_MDDRCGRP_LUT0_AU); + out_be32(&im->mddrc.lut_table0_alternate_lower, CONFIG_SYS_MDDRCGRP_LUT0_AL); + out_be32(&im->mddrc.lut_table1_alternate_upper, CONFIG_SYS_MDDRCGRP_LUT1_AU); + out_be32(&im->mddrc.lut_table1_alternate_lower, CONFIG_SYS_MDDRCGRP_LUT1_AL); + out_be32(&im->mddrc.lut_table2_alternate_upper, CONFIG_SYS_MDDRCGRP_LUT2_AU); + out_be32(&im->mddrc.lut_table2_alternate_lower, CONFIG_SYS_MDDRCGRP_LUT2_AL); + out_be32(&im->mddrc.lut_table3_alternate_upper, CONFIG_SYS_MDDRCGRP_LUT3_AU); + out_be32(&im->mddrc.lut_table3_alternate_lower, CONFIG_SYS_MDDRCGRP_LUT3_AL); + out_be32(&im->mddrc.lut_table4_alternate_upper, CONFIG_SYS_MDDRCGRP_LUT4_AU); + out_be32(&im->mddrc.lut_table4_alternate_lower, CONFIG_SYS_MDDRCGRP_LUT4_AL); + + /* + * Initialize MDDRC + * put MDDRC in CMD mode and + * set the max time between refreshes to 0 during init process + */ + out_be32(&im->mddrc.ddr_sys_config, + mddrc_config->ddr_sys_config | MDDRC_SYS_CFG_CMD_MASK); + out_be32(&im->mddrc.ddr_time_config0, + mddrc_config->ddr_time_config0 & MDDRC_REFRESH_ZERO_MASK); + out_be32(&im->mddrc.ddr_time_config1, + mddrc_config->ddr_time_config1); + out_be32(&im->mddrc.ddr_time_config2, + mddrc_config->ddr_time_config2); + + /* Initialize DDR with either default or supplied init sequence */ + for (i = 0; i < seq_sz; i++) + out_be32(&im->mddrc.ddr_command, dram_init_seq[i]); + + /* Start MDDRC */ + out_be32(&im->mddrc.ddr_time_config0, mddrc_config->ddr_time_config0); + out_be32(&im->mddrc.ddr_sys_config, mddrc_config->ddr_sys_config); + + return msize; +} diff --git a/arch/powerpc/cpu/mpc512x/i2c.c b/arch/powerpc/cpu/mpc512x/i2c.c new file mode 100644 index 0000000..e2d9097 --- /dev/null +++ b/arch/powerpc/cpu/mpc512x/i2c.c @@ -0,0 +1,403 @@ +/* + * (C) Copyright 2003 - 2009 + * Wolfgang Denk, DENX Software Engineering, wd@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 + * + * Based on the MPC5xxx code. + */ + +#include <common.h> +#include <asm/io.h> + +DECLARE_GLOBAL_DATA_PTR; + +#ifdef CONFIG_HARD_I2C + +#include <i2c.h> + +/* by default set I2C bus 0 active */ +static unsigned int bus_num __attribute__ ((section (".data"))) = 0; + +#define I2C_TIMEOUT 100 +#define I2C_RETRIES 3 + +struct mpc512x_i2c_tap { + int scl2tap; + int tap2tap; +}; + +static int mpc_reg_in(volatile u32 *reg); +static void mpc_reg_out(volatile u32 *reg, int val, int mask); +static int wait_for_bb(void); +static int wait_for_pin(int *status); +static int do_address(uchar chip, char rdwr_flag); +static int send_bytes(uchar chip, char *buf, int len); +static int receive_bytes(uchar chip, char *buf, int len); +static int mpc_get_fdr(int); + +static int mpc_reg_in (volatile u32 *reg) +{ + int ret = in_be32(reg) >> 24; + + return ret; +} + +static void mpc_reg_out (volatile u32 *reg, int val, int mask) +{ + if (!mask) { + out_be32(reg, val << 24); + } else { + clrsetbits_be32(reg, mask << 24, (val & mask) << 24); + } +} + +static int wait_for_bb (void) +{ + volatile immap_t *im = (immap_t *) CONFIG_SYS_IMMR; + volatile i2c512x_dev_t *regs = &im->i2c.dev[bus_num]; + int timeout = I2C_TIMEOUT; + int status; + + status = mpc_reg_in (®s->msr); + + while (timeout-- && (status & I2C_BB)) { + volatile int temp; + mpc_reg_out (®s->mcr, I2C_STA, I2C_STA); + temp = mpc_reg_in (®s->mdr); + mpc_reg_out (®s->mcr, 0, I2C_STA); + mpc_reg_out (®s->mcr, 0, 0); + mpc_reg_out (®s->mcr, I2C_EN, 0); + + udelay (1000); + status = mpc_reg_in (®s->msr); + } + + return (status & I2C_BB); +} + +static int wait_for_pin (int *status) +{ + volatile immap_t *im = (immap_t *) CONFIG_SYS_IMMR; + volatile i2c512x_dev_t *regs = &im->i2c.dev[bus_num]; + int timeout = I2C_TIMEOUT; + + *status = mpc_reg_in (®s->msr); + + while (timeout-- && !(*status & I2C_IF)) { + udelay (1000); + *status = mpc_reg_in (®s->msr); + } + + if (!(*status & I2C_IF)) { + return -1; + } + + mpc_reg_out (®s->msr, 0, I2C_IF); + + return 0; +} + +static int do_address (uchar chip, char rdwr_flag) +{ + volatile immap_t *im = (immap_t *) CONFIG_SYS_IMMR; + volatile i2c512x_dev_t *regs = &im->i2c.dev[bus_num]; + int status; + + chip <<= 1; + + if (rdwr_flag) { + chip |= 1; + } + + mpc_reg_out (®s->mcr, I2C_TX, I2C_TX); + mpc_reg_out (®s->mdr, chip, 0); + + if (wait_for_pin (&status)) { + return -2; + } + + if (status & I2C_RXAK) { + return -3; + } + + return 0; +} + +static int send_bytes (uchar chip, char *buf, int len) +{ + volatile immap_t *im = (immap_t *) CONFIG_SYS_IMMR; + volatile i2c512x_dev_t *regs = &im->i2c.dev[bus_num]; + int wrcount; + int status; + + for (wrcount = 0; wrcount < len; ++wrcount) { + + mpc_reg_out (®s->mdr, buf[wrcount], 0); + + if (wait_for_pin (&status)) { + break; + } + + if (status & I2C_RXAK) { + break; + } + + } + + return !(wrcount == len); +} + +static int receive_bytes (uchar chip, char *buf, int len) +{ + volatile immap_t *im = (immap_t *) CONFIG_SYS_IMMR; + volatile i2c512x_dev_t *regs = &im->i2c.dev[bus_num]; + int dummy = 1; + int rdcount = 0; + int status; + int i; + + mpc_reg_out (®s->mcr, 0, I2C_TX); + + for (i = 0; i < len; ++i) { + buf[rdcount] = mpc_reg_in (®s->mdr); + + if (dummy) { + dummy = 0; + } else { + rdcount++; + } + + if (wait_for_pin (&status)) { + return -4; + } + } + + mpc_reg_out (®s->mcr, I2C_TXAK, I2C_TXAK); + buf[rdcount++] = mpc_reg_in (®s->mdr); + + if (wait_for_pin (&status)) { + return -5; + } + + mpc_reg_out (®s->mcr, 0, I2C_TXAK); + + return 0; +} + +/**************** I2C API ****************/ + +void i2c_init (int speed, int saddr) +{ + volatile immap_t *im = (immap_t *) CONFIG_SYS_IMMR; + int i; + + for (i = 0; i < I2C_BUS_CNT; i++){ + volatile i2c512x_dev_t *regs = &im->i2c.dev[i]; + + mpc_reg_out (®s->mcr, 0, 0); + + /* Set clock */ + mpc_reg_out (®s->mfdr, mpc_get_fdr (speed), 0); + mpc_reg_out (®s->madr, saddr << 1, 0); + + /* Enable module */ + mpc_reg_out (®s->mcr, I2C_EN, I2C_INIT_MASK); + mpc_reg_out (®s->msr, 0, I2C_IF); + } + + /* Disable interrupts */ + out_be32(&im->i2c.icr, 0); + + /* Turn off filters */ + out_be32(&im->i2c.mifr, 0); +} + +static int mpc_get_fdr (int speed) +{ + static int fdr = -1; + + if (fdr == -1) { + ulong best_speed = 0; + ulong divider; + ulong ips, scl; + ulong bestmatch = 0xffffffffUL; + int best_i = 0, best_j = 0, i, j; + int SCL_Tap[] = { 9, 10, 12, 15, 5, 6, 7, 8}; + struct mpc512x_i2c_tap scltap[] = { + {4, 1}, + {4, 2}, + {6, 4}, + {6, 8}, + {14, 16}, + {30, 32}, + {62, 64}, + {126, 128} + }; + + ips = gd->ips_clk; + for (i = 7; i >= 0; i--) { + for (j = 7; j >= 0; j--) { + scl = 2 * (scltap[j].scl2tap + + (SCL_Tap[i] - 1) * scltap[j].tap2tap + + 2); + if (ips <= speed*scl) { + if ((speed*scl - ips) < bestmatch) { + bestmatch = speed*scl - ips; + best_i = i; + best_j = j; + best_speed = ips/scl; + } + } + } + } + divider = (best_i & 3) | ((best_i & 4) << 3) | (best_j << 2); + if (gd->flags & GD_FLG_RELOC) { + fdr = divider; + } else { + debug("%ld kHz, \n", best_speed / 1000); + return divider; + } + } + + return fdr; +} + +int i2c_probe (uchar chip) +{ + volatile immap_t *im = (immap_t *) CONFIG_SYS_IMMR; + volatile i2c512x_dev_t *regs = &im->i2c.dev[bus_num]; + int i; + + for (i = 0; i < I2C_RETRIES; i++) { + mpc_reg_out (®s->mcr, I2C_STA, I2C_STA); + + if (! do_address (chip, 0)) { + mpc_reg_out (®s->mcr, 0, I2C_STA); + udelay (500); + break; + } + + mpc_reg_out (®s->mcr, 0, I2C_STA); + udelay (500); + } + + return (i == I2C_RETRIES); +} + +int i2c_read (uchar chip, uint addr, int alen, uchar *buf, int len) +{ + volatile immap_t *im = (immap_t *) CONFIG_SYS_IMMR; + volatile i2c512x_dev_t *regs = &im->i2c.dev[bus_num]; + char xaddr[4]; + int ret = -1; + + xaddr[0] = (addr >> 24) & 0xFF; + xaddr[1] = (addr >> 16) & 0xFF; + xaddr[2] = (addr >> 8) & 0xFF; + xaddr[3] = addr & 0xFF; + + if (wait_for_bb ()) { + printf ("i2c_read: bus is busy\n"); + goto Done; + } + + mpc_reg_out (®s->mcr, I2C_STA, I2C_STA); + if (do_address (chip, 0)) { + printf ("i2c_read: failed to address chip\n"); + goto Done; + } + + if (send_bytes (chip, &xaddr[4-alen], alen)) { + printf ("i2c_read: send_bytes failed\n"); + goto Done; + } + + mpc_reg_out (®s->mcr, I2C_RSTA, I2C_RSTA); + if (do_address (chip, 1)) { + printf ("i2c_read: failed to address chip\n"); + goto Done; + } + + if (receive_bytes (chip, (char *)buf, len)) { + printf ("i2c_read: receive_bytes failed\n"); + goto Done; + } + + ret = 0; +Done: + mpc_reg_out (®s->mcr, 0, I2C_STA); + return ret; +} + +int i2c_write (uchar chip, uint addr, int alen, uchar *buf, int len) +{ + volatile immap_t *im = (immap_t *) CONFIG_SYS_IMMR; + volatile i2c512x_dev_t *regs = &im->i2c.dev[bus_num]; + char xaddr[4]; + int ret = -1; + + xaddr[0] = (addr >> 24) & 0xFF; + xaddr[1] = (addr >> 16) & 0xFF; + xaddr[2] = (addr >> 8) & 0xFF; + xaddr[3] = addr & 0xFF; + + if (wait_for_bb ()) { + printf ("i2c_write: bus is busy\n"); + goto Done; + } + + mpc_reg_out (®s->mcr, I2C_STA, I2C_STA); + if (do_address (chip, 0)) { + printf ("i2c_write: failed to address chip\n"); + goto Done; + } + + if (send_bytes (chip, &xaddr[4-alen], alen)) { + printf ("i2c_write: send_bytes failed\n"); + goto Done; + } + + if (send_bytes (chip, (char *)buf, len)) { + printf ("i2c_write: send_bytes failed\n"); + goto Done; + } + + ret = 0; +Done: + mpc_reg_out (®s->mcr, 0, I2C_STA); + return ret; +} + +int i2c_set_bus_num (unsigned int bus) +{ + if (bus >= I2C_BUS_CNT) { + return -1; + } + bus_num = bus; + + return 0; +} + +unsigned int i2c_get_bus_num (void) +{ + return bus_num; +} + +#endif /* CONFIG_HARD_I2C */ diff --git a/arch/powerpc/cpu/mpc512x/ide.c b/arch/powerpc/cpu/mpc512x/ide.c new file mode 100644 index 0000000..dd6b2f4 --- /dev/null +++ b/arch/powerpc/cpu/mpc512x/ide.c @@ -0,0 +1,128 @@ +/* + * (C) Copyright 2007-2009 DENX Software Engineering + * + * 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> +#include <command.h> +#include <asm/io.h> +#include <asm/processor.h> + +DECLARE_GLOBAL_DATA_PTR; + +#if defined(CONFIG_IDE_RESET) + +void ide_set_reset (int idereset) +{ + volatile immap_t *im = (immap_t *) CONFIG_SYS_IMMR; + debug ("ide_set_reset(%d)\n", idereset); + + if (idereset) { + out_be32(&im->pata.pata_ata_control, 0); + } else { + out_be32(&im->pata.pata_ata_control, FSL_ATA_CTRL_ATA_RST_B); + } + udelay(100); +} + +void init_ide_reset (void) +{ + debug ("init_ide_reset\n"); + + /* + * Clear the reset bit to reset the interface + * cf. RefMan MPC5121EE: 28.4.1 Resetting the ATA Bus + */ + ide_set_reset(1); + + /* Assert the reset bit to enable the interface */ + ide_set_reset(0); + +} + +#define CALC_TIMING(t) (t + period - 1) / period + +int ide_preinit (void) +{ + volatile immap_t *im = (immap_t *) CONFIG_SYS_IMMR; + long t; + const struct { + short t0; + short t1; + short t2_8; + short t2_16; + short t2i; + short t4; + short t9; + short tA; + } pio_specs = { + .t0 = 600, + .t1 = 70, + .t2_8 = 290, + .t2_16 = 165, + .t2i = 0, + .t4 = 30, + .t9 = 20, + .tA = 50, + }; + union { + u32 config; + struct { + u8 field1; + u8 field2; + u8 field3; + u8 field4; + }bytes; + } cfg; + + debug ("IDE preinit using PATA peripheral at IMMR-ADDR %08x\n", + (u32)&im->pata); + + /* Set the reset bit to 1 to enable the interface */ + ide_set_reset(0); + + /* Init timings : we use PIO mode 0 timings */ + t = 1000000000 / gd->ips_clk; /* period in ns */ + cfg.bytes.field1 = 3; + cfg.bytes.field2 = 3; + cfg.bytes.field3 = (pio_specs.t1 + t) / t; + cfg.bytes.field4 = (pio_specs.t2_8 + t) / t; + + out_be32(&im->pata.pata_time1, cfg.config); + + cfg.bytes.field1 = (pio_specs.t2_8 + t) / t; + cfg.bytes.field2 = (pio_specs.tA + t) / t + 2; + cfg.bytes.field3 = 1; + cfg.bytes.field4 = (pio_specs.t4 + t) / t; + + out_be32(&im->pata.pata_time2, cfg.config); + + cfg.config = in_be32(&im->pata.pata_time3); + cfg.bytes.field1 = (pio_specs.t9 + t) / t; + + out_be32(&im->pata.pata_time3, cfg.config); + + debug ("PATA preinit complete.\n"); + + return 0; +} + +#endif /* defined(CONFIG_IDE_RESET) */ diff --git a/arch/powerpc/cpu/mpc512x/iim.c b/arch/powerpc/cpu/mpc512x/iim.c new file mode 100644 index 0000000..8f2eb37 --- /dev/null +++ b/arch/powerpc/cpu/mpc512x/iim.c @@ -0,0 +1,394 @@ +/* + * Copyright 2008 Silicon Turnkey Express, Inc. + * Martha Marx <mmarx@silicontkx.com> + * + * ADS5121 IIM (Fusebox) Interface + * + * 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> +#include <command.h> +#include <asm/io.h> + +#ifdef CONFIG_CMD_FUSE + +DECLARE_GLOBAL_DATA_PTR; + +static char cur_bank = '1'; + +char *iim_err_msg(u32 err) +{ + static char *IIM_errs[] = { + "Parity Error in cache", + "Explicit Sense Cycle Error", + "Write to Locked Register Error", + "Read Protect Error", + "Override Protect Error", + "Write Protect Error"}; + + int i; + + if (!err) + return ""; + for (i = 1; i < 8; i++) + if (err & (1 << i)) + printf("IIM - %s\n", IIM_errs[i-1]); + return ""; +} + +int in_range(int n, int min, int max, char *err, char *usg) +{ + if (n > max || n < min) { + printf(err); + printf("Usage:\n%s\n", usg); + return 0; + } + return 1; +} + +int ads5121_fuse_read(int bank, int fstart, int num) +{ + iim512x_t *iim = &((immap_t *) CONFIG_SYS_IMMR)->iim; + u32 *iim_fb, dummy; + int f, ctr; + + out_be32(&iim->err, in_be32(&iim->err)); + if (bank == 0) + iim_fb = (u32 *)&(iim->fbac0); + else + iim_fb = (u32 *)&(iim->fbac1); +/* try a read to see if Read Protect is set */ + dummy = in_be32(&iim_fb[0]); + if (in_be32(&iim->err) & IIM_ERR_RPE) { + printf("\tRead protect fuse is set\n"); + out_be32(&iim->err, IIM_ERR_RPE); + return 0; + } + printf("Reading Bank %d cache\n", bank); + for (f = fstart, ctr = 0; num > 0; ctr++, num--, f++) { + if (ctr % 4 == 0) + printf("F%2d:", f); + printf("\t%#04x", (u8)(iim_fb[f])); + if (ctr % 4 == 3) + printf("\n"); + } + if (ctr % 4 != 0) + printf("\n"); +} + +int ads5121_fuse_override(int bank, int f, u8 val) +{ + iim512x_t *iim = &((immap_t *) CONFIG_SYS_IMMR)->iim; + u32 *iim_fb; + u32 iim_stat; + int i; + + out_be32(&iim->err, in_be32(&iim->err)); + if (bank == 0) + iim_fb = (u32 *)&(iim->fbac0); + else + iim_fb = (u32 *)&(iim->fbac1); +/* try a read to see if Read Protect is set */ + iim_stat = in_be32(&iim_fb[0]); + if (in_be32(&iim->err) & IIM_ERR_RPE) { + printf("Read protect fuse is set on bank %d;" + "Override protect may also be set\n", bank); + printf("An attempt will be made to override\n"); + out_be32(&iim->err, IIM_ERR_RPE); + } + if (iim_stat & IIM_FBAC_FBOP) { + printf("Override protect fuse is set on bank %d\n", bank); + return 1; + } + if (f > IIM_FMAX) /* reset the entire bank */ + for (i = 0; i < IIM_FMAX + 1; i++) + out_be32(&iim_fb[i], 0); + else + out_be32(&iim_fb[f], val); + return 0; +} + +int ads5121_fuse_prog(cmd_tbl_t *cmdtp, int bank, char *fuseno_bitno) +{ + iim512x_t *iim = &((immap_t *) CONFIG_SYS_IMMR)->iim; + int f, i, bitno; + u32 stat, err; + + f = simple_strtol(fuseno_bitno, NULL, 10); + if (f == 0 && fuseno_bitno[0] != '0') + f = -1; + if (!in_range(f, 0, IIM_FMAX, + "<frow> must be between 0-31\n\n", cmdtp->usage)) + return 1; + bitno = -1; + for (i = 0; i < 6; i++) { + if (fuseno_bitno[i] == '_') { + bitno = simple_strtol(&(fuseno_bitno[i+1]), NULL, 10); + if (bitno == 0 && fuseno_bitno[i+1] != '0') + bitno = -1; + break; + } + } + if (!in_range(bitno, 0, 7, "Bit number ranges from 0-7\n" + "Example of <frow_bitno>: \"18_4\" sets bit 4 of row 18\n", + cmdtp->usage)) + return 1; + out_be32(&iim->err, in_be32(&iim->err)); + out_be32(&iim->prg_p, IIM_PRG_P_SET); + out_be32(&iim->ua, IIM_SET_UA(bank, f)); + out_be32(&iim->la, IIM_SET_LA(f, bitno)); +#ifdef DEBUG + printf("Programming disabled with DEBUG defined \n"); + printf(""Set up to pro + printf("iim.ua = %x; iim.la = %x\n", iim->ua, iim->la); +#else + out_be32(&iim->fctl, IIM_FCTL_PROG_PULSE | IIM_FCTL_PROG); + do + udelay(20); + while ((stat = in_be32(&iim->stat)) & IIM_STAT_BUSY); + out_be32(&iim->prg_p, 0); + err = in_be32(&iim->err); + if (stat & IIM_STAT_PRGD) { + if (!(err & (IIM_ERR_WPE | IIM_ERR_WPE))) { + printf("Fuse is successfully set"); + if (err) + printf(" - however there are other errors"); + printf("\n"); + } + iim->stat = 0; + } + if (err) { + iim_err_msg(err); + out_be32(&iim->err, in_be32(&iim->err)); + } +#endif +} + +int ads5121_fuse_sense(int bank, int fstart, int num) +{ + iim512x_t *iim = &((immap_t *) CONFIG_SYS_IMMR)->iim; + u32 iim_fbac; + u32 stat, err, err_hold = 0; + int f, ctr; + + out_be32(&iim->err, in_be32(&iim->err)); + if (bank == 0) + iim_fbac = in_be32(&iim->fbac0); + else + iim_fbac = in_be32(&iim->fbac1); + if (iim_fbac & IIM_FBAC_FBESP) { + printf("\tSense Protect disallows this operation\n"); + out_be32(&iim->err, IIM_FBAC_FBESP); + return 1; + } + err = in_be32(&iim->err); + if (err) { + iim_err_msg(err); + err_hold |= err; + } + if (err & IIM_ERR_RPE) + printf("\tRead protect fuse is set; " + "Sense Protect may be set but will be attempted\n"); + if (err) + out_be32(&iim->err, err); + printf("Sensing fuse(s) on Bank %d\n", bank); + for (f = fstart, ctr = 0; num > 0; ctr++, f++, num--) { + out_be32(&iim->ua, IIM_SET_UA(bank, f)); + out_be32(&iim->la, IIM_SET_LA(f, 0)); + out_be32(&iim->fctl, IIM_FCTL_ESNS_N); + do + udelay(20); + while ((stat = in_be32(&iim->stat)) & IIM_STAT_BUSY); + err = in_be32(&iim->err); + if (err & IIM_ERR_SNSE) { + iim_err_msg(err); + out_be32(&iim->err, IIM_ERR_SNSE); + return 1; + } + if (stat & IIM_STAT_SNSD) { + out_be32(&iim->stat, 0); + if (ctr % 4 == 0) + printf("F%2d:", f); + printf("\t%#04x", (u8)iim->sdat); + if (ctr % 4 == 3) + printf("\n"); + } + if (err) { + err_hold |= err; + out_be32(&iim->err, err); + } + } + if (ctr % 4 != 0) + printf("\n"); + if (err_hold) + iim_err_msg(err_hold); + + return 0; +} + +int ads5121_fuse_stat(int bank) +{ + iim512x_t *iim = &((immap_t *) CONFIG_SYS_IMMR)->iim; + u32 iim_fbac; + u32 err; + + out_be32(&iim->err, in_be32(&iim->err)); + if (bank == 0) + iim_fbac = in_be32(&iim->fbac0); + else + iim_fbac = in_be32(&iim->fbac1); + err = in_be32(&iim->err); + if (err) + iim_err_msg(err); + if (err & IIM_ERR_RPE || iim_fbac & IIM_FBAC_FBRP) { + if (iim_fbac == 0) + printf("Since protection settings can't be read - " + "try sensing fuse row 0;\n"); + return 0; + } + if (iim_fbac & IIM_PROTECTION) + printf("Protection Fuses Bank %d = %#04x:\n", bank, iim_fbac); + else if (!(err & IIM_ERR_RPE)) + printf("No Protection fuses are set\n"); + if (iim_fbac & IIM_FBAC_FBWP) + printf("\tWrite Protect fuse is set\n"); + if (iim_fbac & IIM_FBAC_FBOP) + printf("\tOverride Protect fuse is set\n"); + if (iim_fbac & IIM_FBAC_FBESP) + printf("\tSense Protect Fuse is set\n"); + out_be32(&iim->err, in_be32(&iim->err)); + + return 0; +} + +int do_ads5121_fuse(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]) +{ + int frow, n, v, bank; + + if (cur_bank == '0') + bank = 0; + else + bank = 1; + + switch (argc) { + case 0: + case 1: + printf("Usage:\n%s\n", cmdtp->usage); + return 1; + case 2: + if (strncmp(argv[1], "stat", 4) == 0) + return ads5121_fuse_stat(bank); + if (strncmp(argv[1], "read", 4) == 0) + return ads5121_fuse_read(bank, 0, IIM_FMAX + 1); + if (strncmp(argv[1], "sense", 5) == 0) + return ads5121_fuse_sense(bank, 0, IIM_FMAX + 1); + if (strncmp(argv[1], "ovride", 6) == 0) + return ads5121_fuse_override(bank, IIM_FMAX + 1, 0); + if (strncmp(argv[1], "bank", 4) == 0) { + printf("Active Fuse Bank is %c\n", cur_bank); + return 0; + } + printf("Usage:\n%s\n", cmdtp->usage); + return 1; + case 3: + if (strncmp(argv[1], "bank", 4) == 0) { + if (argv[2][0] == '0') + cur_bank = '0'; + else if (argv[2][0] == '1') + cur_bank = '1'; + else { + printf("Usage:\n%s\n", cmdtp->usage); + return 1; + } + + printf("Setting Active Fuse Bank to %c\n", cur_bank); + return 0; + } + if (strncmp(argv[1], "prog", 4) == 0) + return ads5121_fuse_prog(cmdtp, bank, argv[2]); + + frow = (int)simple_strtol(argv[2], NULL, 10); + if (frow == 0 && argv[2][0] != '0') + frow = -1; + if (!in_range(frow, 0, IIM_FMAX, + "<frow> must be between 0-31\n\n", cmdtp->usage)) + return 1; + if (strncmp(argv[1], "read", 4) == 0) + return ads5121_fuse_read(bank, frow, 1); + if (strncmp(argv[1], "ovride", 6) == 0) + return ads5121_fuse_override(bank, frow, 0); + if (strncmp(argv[1], "sense", 5) == 0) + return ads5121_fuse_sense(bank, frow, 1); + printf("Usage:\n%s\n", cmdtp->usage); + return 1; + case 4: + frow = (int)simple_strtol(argv[2], NULL, 10); + if (frow == 0 && argv[2][0] != '0') + frow = -1; + if (!in_range(frow, 0, IIM_FMAX, + "<frow> must be between 0-31\n\n", cmdtp->usage)) + return 1; + if (strncmp(argv[1], "read", 4) == 0) { + n = (int)simple_strtol(argv[3], NULL, 10); + if (!in_range(frow + n, frow + 1, IIM_FMAX + 1, + "<frow>+<n> must be between 1-32\n\n", + cmdtp->usage)) + return 1; + return ads5121_fuse_read(bank, frow, n); + } + if (strncmp(argv[1], "ovride", 6) == 0) { + v = (int)simple_strtol(argv[3], NULL, 10); + return ads5121_fuse_override(bank, frow, v); + } + if (strncmp(argv[1], "sense", 5) == 0) { + n = (int)simple_strtol(argv[3], NULL, 10); + if (!in_range(frow + n, frow + 1, IIM_FMAX + 1, + "<frow>+<n> must be between 1-32\n\n", + cmdtp->usage)) + return 1; + return ads5121_fuse_sense(bank, frow, n); + } + printf("Usage:\n%s\n", cmdtp->usage); + return 1; + default: /* at least 5 args */ + printf("Usage:\n%s\n", cmdtp->usage); + return 1; + } +} + +U_BOOT_CMD( + fuse, CONFIG_SYS_MAXARGS, 0, do_ads5121_fuse, + " - Read, Sense, Override or Program Fuses\n", + "bank <n> - sets active Fuse Bank to 0 or 1\n" + " no args shows current active bank\n" + "fuse stat - print active fuse bank's protection status\n" + "fuse read [<frow> [<n>]] - print <n> fuse rows starting at <frow>\n" + " no args to print entire bank's fuses\n" + "fuse ovride [<frow> [<v>]]- override fuses at <frow> with <v>\n" + " no <v> defaults to 0 for the row\n" + " no args resets entire bank to 0\n" + " NOTE - settings persist until hard reset\n" + "fuse sense [<frow>] - senses current fuse at <frow>\n" + " no args for entire bank\n" + "fuse prog <frow_bit> - program fuse at row <frow>, bit <_bit>\n" + " <frow> is 0-31, <bit> is 0-7; eg. 13_2 \n" + " WARNING - this is permanent" +); +#endif /* CONFIG_CMD_FUSE */ diff --git a/arch/powerpc/cpu/mpc512x/interrupts.c b/arch/powerpc/cpu/mpc512x/interrupts.c new file mode 100644 index 0000000..ef7c773 --- /dev/null +++ b/arch/powerpc/cpu/mpc512x/interrupts.c @@ -0,0 +1,61 @@ +/* + * (C) Copyright 2000-2007 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * Copyright 2004 Freescale Semiconductor, Inc. + * + * 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 + * + * Derived from the MPC83xx code. + */ + +#include <common.h> + +DECLARE_GLOBAL_DATA_PTR; + +struct irq_action { + interrupt_handler_t *handler; + void *arg; + ulong count; +}; + +int interrupt_init_cpu (unsigned *decrementer_count) +{ + *decrementer_count = get_tbclk () / CONFIG_SYS_HZ; + + return 0; +} + +/* + * Install and free an interrupt handler. + */ +void +irq_install_handler (int irq, interrupt_handler_t * handler, void *arg) +{ +} + +void irq_free_handler (int irq) +{ +} + +void timer_interrupt_cpu (struct pt_regs *regs) +{ + /* nothing to do here */ + return; +} diff --git a/arch/powerpc/cpu/mpc512x/iopin.c b/arch/powerpc/cpu/mpc512x/iopin.c new file mode 100644 index 0000000..be20947 --- /dev/null +++ b/arch/powerpc/cpu/mpc512x/iopin.c @@ -0,0 +1,49 @@ +/* + * (C) Copyright 2008 + * Martha J Marx, Silicon Turnkey Express, mmarx@silicontkx.com + * mpc512x I/O pin/pad initialization for the ADS5121 board + * 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> +#include <linux/types.h> +#include <asm/io.h> + +void iopin_initialize(iopin_t *ioregs_init, int len) +{ + short i, j, p; + u32 *reg; + immap_t *im = (immap_t *)CONFIG_SYS_IMMR; + + reg = (u32 *)&(im->io_ctrl); + + if (sizeof(ioregs_init) == 0) + return; + + for (i = 0; i < len; i++) { + for (p = 0, j = ioregs_init[i].p_offset / sizeof(u_long); + p < ioregs_init[i].nr_pins; p++, j++) { + if (ioregs_init[i].bit_or) + setbits_be32(reg + j, ioregs_init[i].val); + else + out_be32 (reg + j, ioregs_init[i].val); + } + } + return; +} diff --git a/arch/powerpc/cpu/mpc512x/pci.c b/arch/powerpc/cpu/mpc512x/pci.c new file mode 100644 index 0000000..141db8b --- /dev/null +++ b/arch/powerpc/cpu/mpc512x/pci.c @@ -0,0 +1,227 @@ +/* + * Copyright (C) 2009-2010 DENX Software Engineering <wd@denx.de> + * Copyright (C) Freescale Semiconductor, Inc. 2006, 2007. + * + * 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> + +#include <asm/io.h> +#include <asm/mmu.h> +#include <asm/global_data.h> +#include <pci.h> +#if defined(CONFIG_OF_LIBFDT) +#include <libfdt.h> +#include <fdt_support.h> +#endif + +DECLARE_GLOBAL_DATA_PTR; + +/* System RAM mapped to PCI space */ +#define CONFIG_PCI_SYS_MEM_BUS CONFIG_SYS_SDRAM_BASE +#define CONFIG_PCI_SYS_MEM_PHYS CONFIG_SYS_SDRAM_BASE + +static struct pci_controller pci_hose; + + +/************************************************************************** + * pci_init_board() + * + */ +void +pci_init_board(void) +{ + volatile immap_t *im = (immap_t *) CONFIG_SYS_IMMR; + volatile law512x_t *pci_law; + volatile pot512x_t *pci_pot; + volatile pcictrl512x_t *pci_ctrl; + volatile pciconf512x_t *pci_conf; + u16 reg16; + u32 reg32; + u32 dev; + int i; + struct pci_controller *hose; + + /* Set PCI divider for 33MHz */ + reg32 = in_be32(&im->clk.scfr[0]); + reg32 &= ~(SCFR1_PCI_DIV_MASK); + reg32 |= SCFR1_PCI_DIV << SCFR1_PCI_DIV_SHIFT; + out_be32(&im->clk.scfr[0], reg32); + + clrsetbits_be32(&im->clk.scfr[0], + SCFR1_PCI_DIV_MASK, + SCFR1_PCI_DIV << SCFR1_PCI_DIV_SHIFT + ); + + pci_law = im->sysconf.pcilaw; + pci_pot = im->ios.pot; + pci_ctrl = &im->pci_ctrl; + pci_conf = &im->pci_conf; + + hose = &pci_hose; + + /* + * Release PCI RST Output signal + */ + out_be32(&pci_ctrl->gcr, 0); + udelay(2000); + out_be32(&pci_ctrl->gcr, 1); + + /* We need to wait at least a 1sec based on PCI specs */ + for (i = 0; i < 1000; i++) + udelay(1000); + + /* + * Configure PCI Local Access Windows + */ + out_be32(&pci_law[0].bar, CONFIG_SYS_PCI_MEM_PHYS & LAWBAR_BAR); + out_be32(&pci_law[0].ar, LAWAR_EN | LAWAR_SIZE_512M); + + out_be32(&pci_law[1].bar, CONFIG_SYS_PCI_IO_PHYS & LAWBAR_BAR); + out_be32(&pci_law[1].ar, LAWAR_EN | LAWAR_SIZE_16M); + + /* + * Configure PCI Outbound Translation Windows + */ + + /* PCI mem space - prefetch */ + out_be32(&pci_pot[0].potar, + (CONFIG_SYS_PCI_MEM_BASE >> 12) & POTAR_TA_MASK); + out_be32(&pci_pot[0].pobar, + (CONFIG_SYS_PCI_MEM_PHYS >> 12) & POBAR_BA_MASK); + out_be32(&pci_pot[0].pocmr, + POCMR_EN | POCMR_PRE | POCMR_CM_256M); + + /* PCI IO space */ + out_be32(&pci_pot[1].potar, + (CONFIG_SYS_PCI_IO_BASE >> 12) & POTAR_TA_MASK); + out_be32(&pci_pot[1].pobar, + (CONFIG_SYS_PCI_IO_PHYS >> 12) & POBAR_BA_MASK); + out_be32(&pci_pot[1].pocmr, + POCMR_EN | POCMR_IO | POCMR_CM_16M); + + /* PCI mmio - non-prefetch mem space */ + out_be32(&pci_pot[2].potar, + (CONFIG_SYS_PCI_MMIO_BASE >> 12) & POTAR_TA_MASK); + out_be32(&pci_pot[2].pobar, + (CONFIG_SYS_PCI_MMIO_PHYS >> 12) & POBAR_BA_MASK); + out_be32(&pci_pot[2].pocmr, + POCMR_EN | POCMR_CM_256M); + + /* + * Configure PCI Inbound Translation Windows + */ + + /* we need RAM mapped to PCI space for the devices to + * access main memory */ + out_be32(&pci_ctrl[0].pitar1, 0x0); + out_be32(&pci_ctrl[0].pibar1, 0x0); + out_be32(&pci_ctrl[0].piebar1, 0x0); + out_be32(&pci_ctrl[0].piwar1, + PIWAR_EN | PIWAR_PF | PIWAR_RTT_SNOOP | + PIWAR_WTT_SNOOP | (__ilog2(gd->ram_size) - 1)); + + hose->first_busno = 0; + hose->last_busno = 0xff; + + /* PCI memory prefetch space */ + pci_set_region(hose->regions + 0, + CONFIG_SYS_PCI_MEM_BASE, + CONFIG_SYS_PCI_MEM_PHYS, + CONFIG_SYS_PCI_MEM_SIZE, + PCI_REGION_MEM|PCI_REGION_PREFETCH); + + /* PCI memory space */ + pci_set_region(hose->regions + 1, + CONFIG_SYS_PCI_MMIO_BASE, + CONFIG_SYS_PCI_MMIO_PHYS, + CONFIG_SYS_PCI_MMIO_SIZE, + PCI_REGION_MEM); + + /* PCI IO space */ + pci_set_region(hose->regions + 2, + CONFIG_SYS_PCI_IO_BASE, + CONFIG_SYS_PCI_IO_PHYS, + CONFIG_SYS_PCI_IO_SIZE, + PCI_REGION_IO); + + /* System memory space */ + pci_set_region(hose->regions + 3, + CONFIG_PCI_SYS_MEM_BUS, + CONFIG_PCI_SYS_MEM_PHYS, + gd->ram_size, + PCI_REGION_MEM | PCI_REGION_SYS_MEMORY); + + hose->region_count = 4; + + pci_setup_indirect(hose, + (CONFIG_SYS_IMMR + 0x8300), + (CONFIG_SYS_IMMR + 0x8304)); + + pci_register_hose(hose); + + /* + * Write to Command register + */ + reg16 = 0xff; + dev = PCI_BDF(hose->first_busno, 0, 0); + pci_hose_read_config_word(hose, dev, PCI_COMMAND, ®16); + reg16 |= PCI_COMMAND_SERR | PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY; + pci_hose_write_config_word(hose, dev, PCI_COMMAND, reg16); + + /* + * Clear non-reserved bits in status register. + */ + pci_hose_write_config_word(hose, dev, PCI_STATUS, 0xffff); + pci_hose_write_config_byte(hose, dev, PCI_LATENCY_TIMER, 0x80); + pci_hose_write_config_byte(hose, dev, PCI_CACHE_LINE_SIZE, 0x08); + +#ifdef CONFIG_PCI_SCAN_SHOW + printf("PCI: Bus Dev VenId DevId Class Int\n"); +#endif + /* + * Hose scan. + */ + hose->last_busno = pci_hose_scan(hose); +} + +#if defined(CONFIG_OF_LIBFDT) +void ft_pci_setup(void *blob, bd_t *bd) +{ + int nodeoffset; + int tmp[2]; + const char *path; + + nodeoffset = fdt_path_offset(blob, "/aliases"); + if (nodeoffset >= 0) { + path = fdt_getprop(blob, nodeoffset, "pci", NULL); + if (path) { + tmp[0] = cpu_to_be32(pci_hose.first_busno); + tmp[1] = cpu_to_be32(pci_hose.last_busno); + do_fixup_by_path(blob, path, "bus-range", + &tmp, sizeof(tmp), 1); + + tmp[0] = cpu_to_be32(gd->pci_clk); + do_fixup_by_path(blob, path, "clock-frequency", + &tmp, sizeof(tmp[0]), 1); + } + } +} +#endif /* CONFIG_OF_LIBFDT */ diff --git a/arch/powerpc/cpu/mpc512x/serial.c b/arch/powerpc/cpu/mpc512x/serial.c new file mode 100644 index 0000000..ec2f41b --- /dev/null +++ b/arch/powerpc/cpu/mpc512x/serial.c @@ -0,0 +1,193 @@ +/* + * (C) Copyright 2000 - 2010 + * Wolfgang Denk, DENX Software Engineering, wd@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 + * + * Based ont the MPC5200 PSC driver. + * Adapted for MPC512x by Jan Wrobel <wrr@semihalf.com> + */ + +/* + * Minimal serial functions needed to use one of the PSC ports + * as serial console interface. + */ + +#include <common.h> +#include <asm/io.h> +#include <asm/processor.h> + +DECLARE_GLOBAL_DATA_PTR; + +#if defined(CONFIG_PSC_CONSOLE) + +static void fifo_init (volatile psc512x_t *psc) +{ + volatile immap_t *im = (immap_t *) CONFIG_SYS_IMMR; + + /* reset Rx & Tx fifo slice */ + out_be32(&psc->rfcmd, PSC_FIFO_RESET_SLICE); + out_be32(&psc->tfcmd, PSC_FIFO_RESET_SLICE); + + /* disable Tx & Rx FIFO interrupts */ + out_be32(&psc->rfintmask, 0); + out_be32(&psc->tfintmask, 0); + + out_be32(&psc->tfsize, CONSOLE_FIFO_TX_SIZE | (CONSOLE_FIFO_TX_ADDR << 16)); + out_be32(&psc->rfsize, CONSOLE_FIFO_RX_SIZE | (CONSOLE_FIFO_RX_ADDR << 16)); + + /* enable Tx & Rx FIFO slice */ + out_be32(&psc->rfcmd, PSC_FIFO_ENABLE_SLICE); + out_be32(&psc->tfcmd, PSC_FIFO_ENABLE_SLICE); + + out_be32(&im->fifoc.fifoc_cmd, FIFOC_DISABLE_CLOCK_GATE); + __asm__ volatile ("sync"); +} + +void serial_setbrg(void) +{ + volatile immap_t *im = (immap_t *) CONFIG_SYS_IMMR; + volatile psc512x_t *psc = (psc512x_t *) &im->psc[CONFIG_PSC_CONSOLE]; + unsigned long baseclk, div; + + /* calculate dividor for setting PSC CTUR and CTLR registers */ + baseclk = (gd->ips_clk + 8) / 16; + div = (baseclk + (gd->baudrate / 2)) / gd->baudrate; + + out_8(&psc->ctur, (div >> 8) & 0xff); + out_8(&psc->ctlr, div & 0xff); /* set baudrate */ +} + +int serial_init(void) +{ + volatile immap_t *im = (immap_t *) CONFIG_SYS_IMMR; + volatile psc512x_t *psc = (psc512x_t *) &im->psc[CONFIG_PSC_CONSOLE]; + + fifo_init (psc); + + /* set MR register to point to MR1 */ + out_8(&psc->command, PSC_SEL_MODE_REG_1); + + /* disable Tx/Rx */ + out_8(&psc->command, PSC_TX_DISABLE | PSC_RX_DISABLE); + + /* choose the prescaler by 16 for the Tx/Rx clock generation */ + out_be16(&psc->psc_clock_select, 0xdd00); + + /* switch to UART mode */ + out_be32(&psc->sicr, 0); + + /* mode register points to mr1 */ + /* configure parity, bit length and so on in mode register 1*/ + out_8(&psc->mode, PSC_MODE_8_BITS | PSC_MODE_PARNONE); + /* now, mode register points to mr2 */ + out_8(&psc->mode, PSC_MODE_1_STOPBIT); + + /* set baudrate */ + serial_setbrg(); + + /* disable all interrupts */ + out_be16(&psc->psc_imr, 0); + + /* reset and enable Rx/Tx */ + out_8(&psc->command, PSC_RST_RX); + out_8(&psc->command, PSC_RST_TX); + out_8(&psc->command, PSC_RX_ENABLE | PSC_TX_ENABLE); + + return 0; +} + +void serial_putc (const char c) +{ + volatile immap_t *im = (immap_t *)CONFIG_SYS_IMMR; + volatile psc512x_t *psc = (psc512x_t *) &im->psc[CONFIG_PSC_CONSOLE]; + + if (c == '\n') + serial_putc ('\r'); + + /* Wait for last character to go. */ + while (!(in_be16(&psc->psc_status) & PSC_SR_TXEMP)) + ; + + out_8(&psc->tfdata_8, c); +} + +void serial_putc_raw (const char c) +{ + volatile immap_t *im = (immap_t *) CONFIG_SYS_IMMR; + volatile psc512x_t *psc = (psc512x_t *) &im->psc[CONFIG_PSC_CONSOLE]; + + /* Wait for last character to go. */ + while (!(in_be16(&psc->psc_status) & PSC_SR_TXEMP)) + ; + + out_8(&psc->tfdata_8, c); +} + + +void serial_puts (const char *s) +{ + while (*s) { + serial_putc (*s++); + } +} + +int serial_getc (void) +{ + volatile immap_t *im = (immap_t *) CONFIG_SYS_IMMR; + volatile psc512x_t *psc = (psc512x_t *) &im->psc[CONFIG_PSC_CONSOLE]; + + /* Wait for a character to arrive. */ + while (in_be32(&psc->rfstat) & PSC_FIFO_EMPTY) + ; + + return in_8(&psc->rfdata_8); +} + +int serial_tstc (void) +{ + volatile immap_t *im = (immap_t *) CONFIG_SYS_IMMR; + volatile psc512x_t *psc = (psc512x_t *) &im->psc[CONFIG_PSC_CONSOLE]; + + return !(in_be32(&psc->rfstat) & PSC_FIFO_EMPTY); +} + +void serial_setrts(int s) +{ + volatile immap_t *im = (immap_t *) CONFIG_SYS_IMMR; + volatile psc512x_t *psc = (psc512x_t *) &im->psc[CONFIG_PSC_CONSOLE]; + + if (s) { + /* Assert RTS (become LOW) */ + out_8(&psc->op1, 0x1); + } + else { + /* Negate RTS (become HIGH) */ + out_8(&psc->op0, 0x1); + } +} + +int serial_getcts(void) +{ + volatile immap_t *im = (immap_t *) CONFIG_SYS_IMMR; + volatile psc512x_t *psc = (psc512x_t *) &im->psc[CONFIG_PSC_CONSOLE]; + + return (in_8(&psc->ip) & 0x1) ? 0 : 1; +} +#endif /* CONFIG_PSC_CONSOLE */ diff --git a/arch/powerpc/cpu/mpc512x/speed.c b/arch/powerpc/cpu/mpc512x/speed.c new file mode 100644 index 0000000..ce8d094 --- /dev/null +++ b/arch/powerpc/cpu/mpc512x/speed.c @@ -0,0 +1,156 @@ +/* + * (C) Copyright 2000-2009 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * Copyright (C) 2004-2006 Freescale Semiconductor, Inc. + * + * 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 + * + * Based on the MPC83xx code. + */ + +#include <common.h> +#include <command.h> +#include <asm/io.h> +#include <asm/processor.h> + +DECLARE_GLOBAL_DATA_PTR; + +static int spmf_mult[] = { + 68, 1, 12, 16, + 20, 24, 28, 32, + 36, 40, 44, 48, + 52, 56, 60, 64 +}; + +static int cpmf_mult[][2] = { + {0, 1}, {0, 1}, /* 0 and 1 are not valid */ + {1, 1}, {3, 2}, + {2, 1}, {5, 2}, + {3, 1}, {7, 2}, + {0, 1}, {0, 1}, /* and all above 7 are not valid too */ + {0, 1}, {0, 1}, + {0, 1}, {0, 1}, + {0, 1}, {0, 1} +}; + +static int sys_dividors[][2] = { + {2, 1}, {5, 2}, {3, 1}, {7, 2}, {4, 1}, + {9, 2}, {5, 1}, {7, 1}, {6, 1}, {8, 1}, + {9, 1}, {11, 1}, {10, 1}, {12, 1}, {13, 1}, + {15, 1}, {14, 1}, {16, 1}, {17, 1}, {19, 1}, + {18, 1}, {20, 1}, {21, 1}, {23, 1}, {22, 1}, + {24, 1}, {25, 1}, {27, 1}, {26, 1}, {28, 1}, + {29, 1}, {31, 1}, {30, 1}, {32, 1}, {33, 1} +}; + +int get_clocks (void) +{ + volatile immap_t *im = (immap_t *) CONFIG_SYS_IMMR; + u8 spmf; + u8 cpmf; + u8 sys_div; + u8 ips_div; + u8 pci_div; + u32 ref_clk = CONFIG_SYS_MPC512X_CLKIN; + u32 spll; + u32 sys_clk; + u32 core_clk; + u32 csb_clk; + u32 ips_clk; + u32 pci_clk; + u32 reg; + + reg = in_be32(&im->sysconf.immrbar); + if ((reg & IMMRBAR_BASE_ADDR) != (u32) im) + return -1; + + reg = in_be32(&im->clk.spmr); + spmf = (reg & SPMR_SPMF) >> SPMR_SPMF_SHIFT; + spll = ref_clk * spmf_mult[spmf]; + + reg = in_be32(&im->clk.scfr[1]); + sys_div = (reg & SCFR2_SYS_DIV) >> SCFR2_SYS_DIV_SHIFT; + sys_clk = (spll * sys_dividors[sys_div][1]) / sys_dividors[sys_div][0]; + + csb_clk = sys_clk / 2; + + reg = in_be32(&im->clk.spmr); + cpmf = (reg & SPMR_CPMF) >> SPMR_CPMF_SHIFT; + core_clk = (csb_clk * cpmf_mult[cpmf][0]) / cpmf_mult[cpmf][1]; + + reg = in_be32(&im->clk.scfr[0]); + ips_div = (reg & SCFR1_IPS_DIV_MASK) >> SCFR1_IPS_DIV_SHIFT; + if (ips_div != 0) { + ips_clk = csb_clk / ips_div; + } else { + /* in case we cannot get a sane IPS divisor, fail gracefully */ + ips_clk = 0; + } + + reg = in_be32(&im->clk.scfr[0]); + pci_div = (reg & SCFR1_PCI_DIV_MASK) >> SCFR1_PCI_DIV_SHIFT; + if (pci_div != 0) { + pci_clk = csb_clk / pci_div; + } else { + /* in case we cannot get a sane IPS divisor, fail gracefully */ + pci_clk = 333333; + } + + gd->ips_clk = ips_clk; + gd->pci_clk = pci_clk; + gd->csb_clk = csb_clk; + gd->cpu_clk = core_clk; + gd->bus_clk = csb_clk; + return 0; + +} + +/******************************************** + * get_bus_freq + * return system bus freq in Hz + *********************************************/ +ulong get_bus_freq (ulong dummy) +{ + return gd->csb_clk; +} + +int do_clocks (cmd_tbl_t * cmdtp, int flag, int argc, char *argv[]) +{ + char buf[32]; + + printf("Clock configuration:\n"); + printf(" CPU: %-4s MHz\n", strmhz(buf, gd->cpu_clk)); + printf(" Coherent System Bus: %-4s MHz\n", strmhz(buf, gd->csb_clk)); + printf(" IPS Bus: %-4s MHz\n", strmhz(buf, gd->ips_clk)); + printf(" PCI: %-4s MHz\n", strmhz(buf, gd->pci_clk)); + printf(" DDR: %-4s MHz\n", strmhz(buf, 2*gd->csb_clk)); + return 0; +} + +U_BOOT_CMD(clocks, 1, 0, do_clocks, + "print clock configuration", + " clocks" +); + +int prt_mpc512x_clks (void) +{ + do_clocks (NULL, 0, 0, NULL); + return (0); +} diff --git a/arch/powerpc/cpu/mpc512x/start.S b/arch/powerpc/cpu/mpc512x/start.S new file mode 100644 index 0000000..d26b617 --- /dev/null +++ b/arch/powerpc/cpu/mpc512x/start.S @@ -0,0 +1,711 @@ +/* + * Copyright (C) 1998 Dan Malek <dmalek@jlc.net> + * Copyright (C) 1999 Magnus Damm <kieraypc01.p.y.kie.era.ericsson.se> + * Copyright (C) 2000-2009 Wolfgang Denk <wd@denx.de> + * Copyright Freescale Semiconductor, Inc. 2004, 2006. + * + * 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 + * + * Based on the MPC83xx code. + */ + +/* + * U-Boot - Startup Code for MPC512x based Embedded Boards + */ + +#include <config.h> +#include <timestamp.h> +#include <version.h> + +#define CONFIG_521X 1 /* needed for Linux kernel header files*/ + +#include <asm/immap_512x.h> +#include "asm-offsets.h" + +#include <ppc_asm.tmpl> +#include <ppc_defs.h> + +#include <asm/cache.h> +#include <asm/mmu.h> + +#ifndef CONFIG_IDENT_STRING +#define CONFIG_IDENT_STRING "MPC512X" +#endif + +/* + * Floating Point enable, Machine Check and Recoverable Interr. + */ +#undef MSR_KERNEL +#ifdef DEBUG +#define MSR_KERNEL (MSR_FP|MSR_RI) +#else +#define MSR_KERNEL (MSR_FP|MSR_ME|MSR_RI) +#endif + +/* Macros for manipulating CSx_START/STOP */ +#define START_REG(start) ((start) >> 16) +#define STOP_REG(start, size) (((start) + (size) - 1) >> 16) + +/* + * Set up GOT: Global Offset Table + * + * Use r12 to access the GOT + */ + START_GOT + GOT_ENTRY(_GOT2_TABLE_) + GOT_ENTRY(_FIXUP_TABLE_) + + GOT_ENTRY(_start) + GOT_ENTRY(_start_of_vectors) + GOT_ENTRY(_end_of_vectors) + GOT_ENTRY(transfer_to_handler) + + GOT_ENTRY(__init_end) + GOT_ENTRY(_end) + GOT_ENTRY(__bss_start) + END_GOT + +/* + * Magic number and version string + */ + .long 0x27051956 /* U-Boot Magic Number */ + .globl version_string +version_string: + .ascii U_BOOT_VERSION + .ascii " (", U_BOOT_DATE, " - ", U_BOOT_TIME, ")" + .ascii " ", CONFIG_IDENT_STRING, "\0" + +/* + * Vector Table + */ + .text + . = EXC_OFF_SYS_RESET + + .globl _start + /* Start from here after reset/power on */ +_start: + li r21, BOOTFLAG_COLD /* Normal Power-On: Boot from FLASH */ + b boot_cold + + .globl _start_of_vectors +_start_of_vectors: + +/* Machine check */ + STD_EXCEPTION(0x200, MachineCheck, MachineCheckException) + +/* Data Storage exception. */ + STD_EXCEPTION(0x300, DataStorage, UnknownException) + +/* Instruction Storage exception. */ + STD_EXCEPTION(0x400, InstStorage, UnknownException) + +/* External Interrupt exception. */ + STD_EXCEPTION(0x500, ExtInterrupt, UnknownException) + +/* Alignment exception. */ + . = 0x600 +Alignment: + EXCEPTION_PROLOG(SRR0, SRR1) + mfspr r4,DAR + stw r4,_DAR(r21) + mfspr r5,DSISR + stw r5,_DSISR(r21) + addi r3,r1,STACK_FRAME_OVERHEAD + EXC_XFER_TEMPLATE(Alignment, AlignmentException, MSR_KERNEL, COPY_EE) + +/* Program check exception */ + . = 0x700 +ProgramCheck: + EXCEPTION_PROLOG(SRR0, SRR1) + addi r3,r1,STACK_FRAME_OVERHEAD + EXC_XFER_TEMPLATE(ProgramCheck, ProgramCheckException, + MSR_KERNEL, COPY_EE) + +/* Floating Point Unit unavailable exception */ + STD_EXCEPTION(0x800, FPUnavailable, UnknownException) + +/* Decrementer */ + STD_EXCEPTION(0x900, Decrementer, timer_interrupt) + +/* Critical interrupt */ + STD_EXCEPTION(0xa00, Critical, UnknownException) + +/* System Call */ + STD_EXCEPTION(0xc00, SystemCall, UnknownException) + +/* Trace interrupt */ + STD_EXCEPTION(0xd00, Trace, UnknownException) + +/* Performance Monitor interrupt */ + STD_EXCEPTION(0xf00, PerfMon, UnknownException) + +/* Intruction Translation Miss */ + STD_EXCEPTION(0x1000, InstructionTLBMiss, UnknownException) + +/* Data Load Translation Miss */ + STD_EXCEPTION(0x1100, DataLoadTLBMiss, UnknownException) + +/* Data Store Translation Miss */ + STD_EXCEPTION(0x1200, DataStoreTLBMiss, UnknownException) + +/* Instruction Address Breakpoint */ + STD_EXCEPTION(0x1300, InstructionAddrBreakpoint, DebugException) + +/* System Management interrupt */ + STD_EXCEPTION(0x1400, SystemMgmtInterrupt, UnknownException) + + .globl _end_of_vectors +_end_of_vectors: + + . = 0x3000 +boot_cold: + /* Save msr contents */ + mfmsr r5 + + /* Set IMMR area to our preferred location */ + lis r4, CONFIG_DEFAULT_IMMR@h + lis r3, CONFIG_SYS_IMMR@h + ori r3, r3, CONFIG_SYS_IMMR@l + stw r3, IMMRBAR(r4) + mtspr MBAR, r3 /* IMMRBAR is mirrored into the MBAR SPR (311) */ + + /* Initialise the machine */ + bl cpu_early_init + + /* + * Set up Local Access Windows: + * + * 1) Boot/CS0 (boot FLASH) + * 2) On-chip SRAM (initial stack purposes) + */ + + /* Boot CS/CS0 window range */ + lis r3, CONFIG_SYS_IMMR@h + ori r3, r3, CONFIG_SYS_IMMR@l + + lis r4, START_REG(CONFIG_SYS_FLASH_BASE) + ori r4, r4, STOP_REG(CONFIG_SYS_FLASH_BASE, CONFIG_SYS_FLASH_SIZE) + stw r4, LPCS0AW(r3) + + /* + * The SRAM window has a fixed size (256K), so only the start address + * is necessary + */ + lis r4, START_REG(CONFIG_SYS_SRAM_BASE) & 0xff00 + stw r4, SRAMBAR(r3) + + /* + * According to MPC5121e RM, configuring local access windows should + * be followed by a dummy read of the config register that was + * modified last and an isync + */ + lwz r4, SRAMBAR(r3) + isync + + /* + * Set configuration of the Boot/CS0, the SRAM window does not have a + * config register so no params can be set for it + */ + lis r3, (CONFIG_SYS_IMMR + LPC_OFFSET)@h + ori r3, r3, (CONFIG_SYS_IMMR + LPC_OFFSET)@l + + lis r4, CONFIG_SYS_CS0_CFG@h + ori r4, r4, CONFIG_SYS_CS0_CFG@l + stw r4, CS0_CONFIG(r3) + + /* Master enable all CS's */ + lis r4, CS_CTRL_ME@h + ori r4, r4, CS_CTRL_ME@l + stw r4, CS_CTRL(r3) + + lis r4, (CONFIG_SYS_MONITOR_BASE)@h + ori r4, r4, (CONFIG_SYS_MONITOR_BASE)@l + addi r5, r4, in_flash - _start + EXC_OFF_SYS_RESET + mtlr r5 + blr + +in_flash: + lis r1, (CONFIG_SYS_INIT_RAM_ADDR + CONFIG_SYS_GBL_DATA_OFFSET)@h + ori r1, r1, (CONFIG_SYS_INIT_RAM_ADDR + CONFIG_SYS_GBL_DATA_OFFSET)@l + + li r0, 0 /* Make room for stack frame header and */ + stwu r0, -4(r1) /* clear final stack frame so that */ + stwu r0, -4(r1) /* stack backtraces terminate cleanly */ + + /* let the C-code set up the rest */ + /* */ + /* Be careful to keep code relocatable & stack humble */ + /*------------------------------------------------------*/ + + GET_GOT /* initialize GOT access */ + + /* r3: IMMR */ + lis r3, CONFIG_SYS_IMMR@h + /* run low-level CPU init code (in Flash) */ + bl cpu_init_f + + /* r3: BOOTFLAG */ + mr r3, r21 + /* run 1st part of board init code (in Flash) */ + bl board_init_f + + /* NOTREACHED - board_init_f() does not return */ + +/* + * This code finishes saving the registers to the exception frame + * and jumps to the appropriate handler for the exception. + * Register r21 is pointer into trap frame, r1 has new stack pointer. + */ + .globl transfer_to_handler +transfer_to_handler: + stw r22,_NIP(r21) + lis r22,MSR_POW@h + andc r23,r23,r22 + stw r23,_MSR(r21) + SAVE_GPR(7, r21) + SAVE_4GPRS(8, r21) + SAVE_8GPRS(12, r21) + SAVE_8GPRS(24, r21) + mflr r23 + andi. r24,r23,0x3f00 /* get vector offset */ + stw r24,TRAP(r21) + li r22,0 + stw r22,RESULT(r21) + lwz r24,0(r23) /* virtual address of handler */ + lwz r23,4(r23) /* where to go when done */ + mtspr SRR0,r24 + mtspr SRR1,r20 + mtlr r23 + SYNC + rfi /* jump to handler, enable MMU */ + +int_return: + mfmsr r28 /* Disable interrupts */ + li r4,0 + ori r4,r4,MSR_EE + andc r28,r28,r4 + SYNC /* Some chip revs need this... */ + mtmsr r28 + SYNC + lwz r2,_CTR(r1) + lwz r0,_LINK(r1) + mtctr r2 + mtlr r0 + lwz r2,_XER(r1) + lwz r0,_CCR(r1) + mtspr XER,r2 + mtcrf 0xFF,r0 + REST_10GPRS(3, r1) + REST_10GPRS(13, r1) + REST_8GPRS(23, r1) + REST_GPR(31, r1) + lwz r2,_NIP(r1) /* Restore environment */ + lwz r0,_MSR(r1) + mtspr SRR0,r2 + mtspr SRR1,r0 + lwz r0,GPR0(r1) + lwz r2,GPR2(r1) + lwz r1,GPR1(r1) + SYNC + rfi + +/* + * This code initialises the machine, it expects original MSR contents to be in r5. + */ +cpu_early_init: + /* Initialize machine status; enable machine check interrupt */ + /*-----------------------------------------------------------*/ + + li r3, MSR_KERNEL /* Set ME and RI flags */ + rlwimi r3, r5, 0, 25, 25 /* preserve IP bit */ +#ifdef DEBUG + rlwimi r3, r5, 0, 21, 22 /* debugger might set SE, BE bits */ +#endif + mtmsr r3 + SYNC + mtspr SRR1, r3 /* Mirror current MSR state in SRR1 */ + + lis r3, CONFIG_SYS_IMMR@h + +#if defined(CONFIG_WATCHDOG) + /* Initialise the watchdog and reset it */ + /*--------------------------------------*/ + lis r4, CONFIG_SYS_WATCHDOG_VALUE + ori r4, r4, (SWCRR_SWEN | SWCRR_SWRI | SWCRR_SWPR) + stw r4, SWCRR(r3) + + /* reset */ + li r4, 0x556C + sth r4, SWSRR@l(r3) + li r4, 0x0 + ori r4, r4, 0xAA39 + sth r4, SWSRR@l(r3) +#else + /* Disable the watchdog */ + /*----------------------*/ + lwz r4, SWCRR(r3) + /* + * Check to see if it's enabled for disabling: once disabled by s/w + * it's not possible to re-enable it + */ + andi. r4, r4, 0x4 + beq 1f + xor r4, r4, r4 + stw r4, SWCRR(r3) +1: +#endif /* CONFIG_WATCHDOG */ + + /* Initialize the Hardware Implementation-dependent Registers */ + /* HID0 also contains cache control */ + /*------------------------------------------------------*/ + lis r3, CONFIG_SYS_HID0_INIT@h + ori r3, r3, CONFIG_SYS_HID0_INIT@l + SYNC + mtspr HID0, r3 + + lis r3, CONFIG_SYS_HID0_FINAL@h + ori r3, r3, CONFIG_SYS_HID0_FINAL@l + SYNC + mtspr HID0, r3 + + lis r3, CONFIG_SYS_HID2@h + ori r3, r3, CONFIG_SYS_HID2@l + SYNC + mtspr HID2, r3 + sync + blr + + +/* Cache functions. + * + * Note: requires that all cache bits in + * HID0 are in the low half word. + */ + .globl icache_enable +icache_enable: + mfspr r3, HID0 + ori r3, r3, HID0_ICE + lis r4, 0 + ori r4, r4, HID0_ILOCK + andc r3, r3, r4 + ori r4, r3, HID0_ICFI + isync + mtspr HID0, r4 /* sets enable and invalidate, clears lock */ + isync + mtspr HID0, r3 /* clears invalidate */ + blr + + .globl icache_disable +icache_disable: + mfspr r3, HID0 + lis r4, 0 + ori r4, r4, HID0_ICE|HID0_ILOCK + andc r3, r3, r4 + ori r4, r3, HID0_ICFI + isync + mtspr HID0, r4 /* sets invalidate, clears enable and lock*/ + isync + mtspr HID0, r3 /* clears invalidate */ + blr + + .globl icache_status +icache_status: + mfspr r3, HID0 + rlwinm r3, r3, (31 - HID0_ICE_SHIFT + 1), 31, 31 + blr + + .globl dcache_enable +dcache_enable: + mfspr r3, HID0 + li r5, HID0_DCFI|HID0_DLOCK + andc r3, r3, r5 + mtspr HID0, r3 /* no invalidate, unlock */ + ori r3, r3, HID0_DCE + ori r5, r3, HID0_DCFI + mtspr HID0, r5 /* enable + invalidate */ + mtspr HID0, r3 /* enable */ + sync + blr + + .globl dcache_disable +dcache_disable: + mfspr r3, HID0 + lis r4, 0 + ori r4, r4, HID0_DCE|HID0_DLOCK + andc r3, r3, r4 + ori r4, r3, HID0_DCI + sync + mtspr HID0, r4 /* sets invalidate, clears enable and lock */ + sync + mtspr HID0, r3 /* clears invalidate */ + blr + + .globl dcache_status +dcache_status: + mfspr r3, HID0 + rlwinm r3, r3, (31 - HID0_DCE_SHIFT + 1), 31, 31 + blr + + .globl get_pvr +get_pvr: + mfspr r3, PVR + blr + +/*-------------------------------------------------------------------*/ + +/* + * void relocate_code (addr_sp, gd, addr_moni) + * + * This "function" does not return, instead it continues in RAM + * after relocating the monitor code. + * + * r3 = dest + * r4 = src + * r5 = length in bytes + * r6 = cachelinesize + */ + .globl relocate_code +relocate_code: + mr r1, r3 /* Set new stack pointer */ + mr r9, r4 /* Save copy of Global Data pointer */ + mr r10, r5 /* Save copy of Destination Address */ + + GET_GOT + mr r3, r5 /* Destination Address */ + lis r4, CONFIG_SYS_MONITOR_BASE@h /* Source Address */ + ori r4, r4, CONFIG_SYS_MONITOR_BASE@l + lwz r5, GOT(__init_end) + sub r5, r5, r4 + li r6, CONFIG_SYS_CACHELINE_SIZE /* Cache Line Size */ + + /* + * Fix GOT pointer: + * + * New GOT-PTR = (old GOT-PTR - CONFIG_SYS_MONITOR_BASE) + * + Destination Address + * + * Offset: + */ + sub r15, r10, r4 + + /* First our own GOT */ + add r12, r12, r15 + /* then the one used by the C code */ + add r30, r30, r15 + + /* + * Now relocate code + */ + cmplw cr1,r3,r4 + addi r0,r5,3 + srwi. r0,r0,2 + beq cr1,4f /* In place copy is not necessary */ + beq 7f /* Protect against 0 count */ + mtctr r0 + bge cr1,2f + la r8,-4(r4) + la r7,-4(r3) + + /* copy */ +1: lwzu r0,4(r8) + stwu r0,4(r7) + bdnz 1b + + addi r0,r5,3 + srwi. r0,r0,2 + mtctr r0 + la r8,-4(r4) + la r7,-4(r3) + + /* and compare */ +20: lwzu r20,4(r8) + lwzu r21,4(r7) + xor. r22, r20, r21 + bne 30f + bdnz 20b + b 4f + + /* compare failed */ +30: li r3, 0 + blr + +2: slwi r0,r0,2 /* re copy in reverse order ... y do we needed it? */ + add r8,r4,r0 + add r7,r3,r0 +3: lwzu r0,-4(r8) + stwu r0,-4(r7) + bdnz 3b + +/* + * Now flush the cache: note that we must start from a cache aligned + * address. Otherwise we might miss one cache line. + */ +4: cmpwi r6,0 + add r5,r3,r5 + beq 7f /* Always flush prefetch queue in any case */ + subi r0,r6,1 + andc r3,r3,r0 + mr r4,r3 +5: dcbst 0,r4 + add r4,r4,r6 + cmplw r4,r5 + blt 5b + sync /* Wait for all dcbst to complete on bus */ + mr r4,r3 +6: icbi 0,r4 + add r4,r4,r6 + cmplw r4,r5 + blt 6b +7: sync /* Wait for all icbi to complete on bus */ + isync + +/* + * We are done. Do not return, instead branch to second part of board + * initialization, now running from RAM. + */ + addi r0, r10, in_ram - _start + EXC_OFF_SYS_RESET + mtlr r0 + blr + +in_ram: + /* + * Relocation Function, r12 point to got2+0x8000 + * + * Adjust got2 pointers, no need to check for 0, this code + * already puts a few entries in the table. + */ + li r0,__got2_entries@sectoff@l + la r3,GOT(_GOT2_TABLE_) + lwz r11,GOT(_GOT2_TABLE_) + mtctr r0 + sub r11,r3,r11 + addi r3,r3,-4 +1: lwzu r0,4(r3) + cmpwi r0,0 + beq- 2f + add r0,r0,r11 + stw r0,0(r3) +2: bdnz 1b + + /* + * Now adjust the fixups and the pointers to the fixups + * in case we need to move ourselves again. + */ + li r0,__fixup_entries@sectoff@l + lwz r3,GOT(_FIXUP_TABLE_) + cmpwi r0,0 + mtctr r0 + addi r3,r3,-4 + beq 4f +3: lwzu r4,4(r3) + lwzux r0,r4,r11 + add r0,r0,r11 + stw r10,0(r3) + stw r0,0(r4) + bdnz 3b +4: +clear_bss: + /* + * Now clear BSS segment + */ + lwz r3,GOT(__bss_start) + lwz r4,GOT(_end) + + cmplw 0, r3, r4 + beq 6f + + li r0, 0 +5: + stw r0, 0(r3) + addi r3, r3, 4 + cmplw 0, r3, r4 + bne 5b +6: + mr r3, r9 /* Global Data pointer */ + mr r4, r10 /* Destination Address */ + bl board_init_r + + /* + * Copy exception vector code to low memory + * + * r3: dest_addr + * r7: source address, r8: end address, r9: target address + */ + .globl trap_init +trap_init: + mflr r4 /* save link register */ + GET_GOT + lwz r7, GOT(_start) + lwz r8, GOT(_end_of_vectors) + + li r9, 0x100 /* reset vector at 0x100 */ + + cmplw 0, r7, r8 + bgelr /* return if r7>=r8 - just in case */ +1: + lwz r0, 0(r7) + stw r0, 0(r9) + addi r7, r7, 4 + addi r9, r9, 4 + cmplw 0, r7, r8 + bne 1b + + /* + * relocate `hdlr' and `int_return' entries + */ + li r7, .L_MachineCheck - _start + EXC_OFF_SYS_RESET + li r8, Alignment - _start + EXC_OFF_SYS_RESET +2: + bl trap_reloc + addi r7, r7, 0x100 /* next exception vector */ + cmplw 0, r7, r8 + blt 2b + + li r7, .L_Alignment - _start + EXC_OFF_SYS_RESET + bl trap_reloc + + li r7, .L_ProgramCheck - _start + EXC_OFF_SYS_RESET + bl trap_reloc + + li r7, .L_FPUnavailable - _start + EXC_OFF_SYS_RESET + li r8, SystemCall - _start + EXC_OFF_SYS_RESET +3: + bl trap_reloc + addi r7, r7, 0x100 /* next exception vector */ + cmplw 0, r7, r8 + blt 3b + + li r7, .L_Trace - _start + EXC_OFF_SYS_RESET + li r8, _end_of_vectors - _start + EXC_OFF_SYS_RESET +4: + bl trap_reloc + addi r7, r7, 0x100 /* next exception vector */ + cmplw 0, r7, r8 + blt 4b + + mfmsr r3 /* now that the vectors have */ + lis r7, MSR_IP@h /* relocated into low memory */ + ori r7, r7, MSR_IP@l /* MSR[IP] can be turned off */ + andc r3, r3, r7 /* (if it was on) */ + SYNC /* Some chip revs need this... */ + mtmsr r3 + SYNC + + mtlr r4 /* restore link register */ + blr diff --git a/arch/powerpc/cpu/mpc512x/traps.c b/arch/powerpc/cpu/mpc512x/traps.c new file mode 100644 index 0000000..786f4a5 --- /dev/null +++ b/arch/powerpc/cpu/mpc512x/traps.c @@ -0,0 +1,212 @@ +/* + * (C) Copyright 2000 - 2007 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org) + * + * 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 + * + * Derived from the MPC83xx code. + */ + +/* + * This file handles the architecture-dependent parts of hardware + * exceptions + */ + +#include <common.h> +#include <kgdb.h> +#include <asm/processor.h> + +DECLARE_GLOBAL_DATA_PTR; + +extern unsigned long search_exception_table(unsigned long); + +/* + * End of addressable memory. This may be less than the actual + * amount of memory on the system if we're unable to keep all + * the memory mapped in. + */ +extern ulong get_effective_memsize(void); +#define END_OF_MEM (gd->bd->bi_memstart + get_effective_memsize()) + +/* + * Trap & Exception support + */ + +void +print_backtrace (unsigned long *sp) +{ + int cnt = 0; + unsigned long i; + + puts ("Call backtrace: "); + while (sp) { + if ((uint)sp > END_OF_MEM) + break; + + i = sp[1]; + if (cnt++ % 7 == 0) + putc ('\n'); + printf ("%08lX ", i); + if (cnt > 32) break; + sp = (unsigned long *) *sp; + } + putc ('\n'); +} + +void show_regs (struct pt_regs * regs) +{ + int i; + + printf ("NIP: %08lX XER: %08lX LR: %08lX REGS: %p TRAP: %04lx DAR: %08lX\n", + regs->nip, regs->xer, regs->link, regs, regs->trap, regs->dar); + printf ("MSR: %08lx EE: %01x PR: %01x FP: %01x ME: %01x IR/DR: %01x%01x\n", + regs->msr, regs->msr & MSR_EE ? 1 : 0, regs->msr & MSR_PR ? 1 : 0, + regs->msr & MSR_FP ? 1 : 0,regs->msr & MSR_ME ? 1 : 0, + regs->msr & MSR_IR ? 1 : 0, + regs->msr & MSR_DR ? 1 : 0); + + putc ('\n'); + for (i = 0; i < 32; i++) { + if ((i % 8) == 0) { + printf ("GPR%02d: ", i); + } + + printf ("%08lX ", regs->gpr[i]); + if ((i % 8) == 7) { + putc ('\n'); + } + } +} + + +void +_exception (int signr, struct pt_regs *regs) +{ + show_regs (regs); + print_backtrace ((unsigned long *)regs->gpr[1]); + panic ("Exception at pc %lx signal %d", regs->nip,signr); +} + + +void +MachineCheckException (struct pt_regs *regs) +{ + unsigned long fixup; + + if ((fixup = search_exception_table (regs->nip)) != 0) { + regs->nip = fixup; + return; + } + +#ifdef CONFIG_CMD_KGDB + if (debugger_exception_handler && (*debugger_exception_handler)(regs)) + return; +#endif + + puts ("Machine check.\nCaused by (from msr): "); + printf ("regs %p ",regs); + switch (regs->msr & 0x00FF0000) { + case (0x80000000 >> 10): + puts ("Instruction cache parity signal\n"); + break; + case (0x80000000 >> 11): + puts ("Data cache parity signal\n"); + break; + case (0x80000000 >> 12): + puts ("Machine check signal\n"); + break; + case (0x80000000 >> 13): + puts ("Transfer error ack signal\n"); + break; + case (0x80000000 >> 14): + puts ("Data parity signal\n"); + break; + case (0x80000000 >> 15): + puts ("Address parity signal\n"); + break; + default: + puts ("Unknown values in msr\n"); + } + show_regs (regs); + print_backtrace ((unsigned long *)regs->gpr[1]); + + panic ("machine check"); +} + +void +AlignmentException (struct pt_regs *regs) +{ +#ifdef CONFIG_CMD_KGDB + if (debugger_exception_handler && (*debugger_exception_handler)(regs)) + return; +#endif + show_regs (regs); + print_backtrace ((unsigned long *)regs->gpr[1]); + panic ("Alignment Exception"); +} + +void +ProgramCheckException (struct pt_regs *regs) +{ +#ifdef CONFIG_CMD_KGDB + if (debugger_exception_handler && (*debugger_exception_handler)(regs)) + return; +#endif + show_regs (regs); + print_backtrace ((unsigned long *)regs->gpr[1]); + panic ("Program Check Exception"); +} + +void +SoftEmuException (struct pt_regs *regs) +{ +#ifdef CONFIG_CMD_KGDB + if (debugger_exception_handler && (*debugger_exception_handler)(regs)) + return; +#endif + show_regs (regs); + print_backtrace ((unsigned long *)regs->gpr[1]); + panic ("Software Emulation Exception"); +} + + +void +UnknownException (struct pt_regs *regs) +{ +#ifdef CONFIG_CMD_KGDB + if (debugger_exception_handler && (*debugger_exception_handler)(regs)) + return; +#endif + printf ("Bad trap at PC: %lx, SR: %lx, vector=%lx\n", + regs->nip, regs->msr, regs->trap); + _exception (0, regs); +} + +#ifdef CONFIG_CMD_BEDBUG +extern void do_bedbug_breakpoint (struct pt_regs *); +#endif + +void +DebugException (struct pt_regs *regs) +{ + printf ("Debugger trap at @ %lx\n", regs->nip ); + show_regs (regs); +#ifdef CONFIG_CMD_BEDBUG + do_bedbug_breakpoint (regs); +#endif +} diff --git a/arch/powerpc/cpu/mpc512x/u-boot.lds b/arch/powerpc/cpu/mpc512x/u-boot.lds new file mode 100644 index 0000000..c716799 --- /dev/null +++ b/arch/powerpc/cpu/mpc512x/u-boot.lds @@ -0,0 +1,120 @@ +/* + * (C) Copyright 2007 DENX Software Engineering. + * + * 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 + */ + +OUTPUT_ARCH(powerpc) +SECTIONS +{ + /* Read-only sections, merged into text segment: */ + . = + SIZEOF_HEADERS; + .interp : { *(.interp) } + .hash : { *(.hash) } + .dynsym : { *(.dynsym) } + .dynstr : { *(.dynstr) } + .rel.text : { *(.rel.text) } + .rela.text : { *(.rela.text) } + .rel.data : { *(.rel.data) } + .rela.data : { *(.rela.data) } + .rel.rodata : { *(.rel.rodata) } + .rela.rodata : { *(.rela.rodata) } + .rel.got : { *(.rel.got) } + .rela.got : { *(.rela.got) } + .rel.ctors : { *(.rel.ctors) } + .rela.ctors : { *(.rela.ctors) } + .rel.dtors : { *(.rel.dtors) } + .rela.dtors : { *(.rela.dtors) } + .rel.bss : { *(.rel.bss) } + .rela.bss : { *(.rela.bss) } + .rel.plt : { *(.rel.plt) } + .rela.plt : { *(.rela.plt) } + .init : { *(.init) } + .plt : { *(.plt) } + .text : + { + arch/powerpc/cpu/mpc512x/start.o (.text) + *(.text) + *(.got1) + . = ALIGN(16); + *(.eh_frame) + *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.rodata*))) + } + .fini : { *(.fini) } =0 + .ctors : { *(.ctors) } + .dtors : { *(.dtors) } + + /* Read-write section, merged into data segment: */ + . = (. + 0x0FFF) & 0xFFFFF000; + _erotext = .; + PROVIDE (erotext = .); + .reloc : + { + *(.got) + _GOT2_TABLE_ = .; + *(.got2) + _FIXUP_TABLE_ = .; + *(.fixup) + } + __got2_entries = (_FIXUP_TABLE_ - _GOT2_TABLE_) >> 2; + __fixup_entries = (. - _FIXUP_TABLE_) >> 2; + + .data : + { + *(.data) + *(.data1) + *(.sdata) + *(.sdata2) + *(.dynamic) + CONSTRUCTORS + } + _edata = .; + PROVIDE (edata = .); + + . = .; + __u_boot_cmd_start = .; + .u_boot_cmd : { *(.u_boot_cmd) } + __u_boot_cmd_end = .; + + + . = .; + __start___ex_table = .; + __ex_table : { *(__ex_table) } + __stop___ex_table = .; + + . = ALIGN(4096); + __init_begin = .; + .text.init : { *(.text.init) } + .data.init : { *(.data.init) } + . = ALIGN(4096); + __init_end = .; + + __bss_start = .; + .bss (NOLOAD) : + { + *(.sbss) *(.scommon) + *(.dynbss) + *(.bss) + *(COMMON) + . = ALIGN(4); + } + _end = . ; + PROVIDE (end = .); +} +ENTRY(_start) diff --git a/arch/powerpc/cpu/mpc5xx/Makefile b/arch/powerpc/cpu/mpc5xx/Makefile new file mode 100644 index 0000000..80c5320 --- /dev/null +++ b/arch/powerpc/cpu/mpc5xx/Makefile @@ -0,0 +1,59 @@ +# +# (C) Copyright 2006 +# Wolfgang Denk, DENX Software Engineering, wd@denx.de. +# +# (C) Copyright 2003 +# Martin Winistoerfer, martinwinistoerfer@gmx.ch. +# +# 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 +# + +# +# File: arch/powerpc/cpu/mpc5xx/Makefile +# +# Discription: Makefile to build mpc5xx cpu configuration. +# Will include top config.mk which itselfs +# uses the definitions made in arch/powerpc/cpu/mpc5xx/config.mk +# + + +include $(TOPDIR)/config.mk + +LIB = $(obj)lib$(CPU).a + +START = start.o +COBJS = serial.o cpu.o cpu_init.o interrupts.o traps.o speed.o spi.o + +SRCS := $(START:.o=.S) $(SOBJS:.o=.S) $(COBJS:.o=.c) +OBJS := $(addprefix $(obj),$(SOBJS) $(COBJS)) +START := $(addprefix $(obj),$(START)) + +all: $(obj).depend $(START) $(LIB) + +$(LIB): $(OBJS) + $(AR) $(ARFLAGS) $@ $(OBJS) + +######################################################################### + +# defines $(obj).depend target +include $(SRCTREE)/rules.mk + +sinclude $(obj).depend + +######################################################################### diff --git a/arch/powerpc/cpu/mpc5xx/config.mk b/arch/powerpc/cpu/mpc5xx/config.mk new file mode 100644 index 0000000..5f9285d --- /dev/null +++ b/arch/powerpc/cpu/mpc5xx/config.mk @@ -0,0 +1,36 @@ +# +# (C) Copyright 2003 +# Martin Winistoerfer, martinwinistoerfer@gmx.ch. +# +# 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 +# + +# +# File: config.mk +# +# Discription: compiler flags and make definitions +# + + +PLATFORM_RELFLAGS += -fPIC -meabi + +PLATFORM_CPPFLAGS += -DCONFIG_5xx -ffixed-r2 -mpowerpc -msoft-float + +# Use default linker script. Board port can override in board/*/config.mk +LDSCRIPT := $(SRCTREE)/arch/powerpc/cpu/mpc5xx/u-boot.lds diff --git a/arch/powerpc/cpu/mpc5xx/cpu.c b/arch/powerpc/cpu/mpc5xx/cpu.c new file mode 100644 index 0000000..7fffebc --- /dev/null +++ b/arch/powerpc/cpu/mpc5xx/cpu.c @@ -0,0 +1,171 @@ +/* + * (C) Copyright 2003 + * Martin Winistoerfer, martinwinistoerfer@gmx.ch. + * + * 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, + */ + +/* + * File: cpu.c + * + * Discription: Some cpu specific function for watchdog, + * cpu version test, clock setting ... + * + */ + + +#include <common.h> +#include <watchdog.h> +#include <command.h> +#include <mpc5xx.h> + +DECLARE_GLOBAL_DATA_PTR; + +#if (defined(CONFIG_MPC555)) +# define ID_STR "MPC555/556" + +/* + * Check version of cpu with Processor Version Register (PVR) + */ +static int check_cpu_version (long clock, uint pvr, uint immr) +{ + char buf[32]; + /* The highest 16 bits should be 0x0002 for a MPC555/556 */ + if ((pvr >> 16) == 0x0002) { + printf (" " ID_STR " Version %x", (pvr >> 16)); + printf (" at %s MHz:", strmhz (buf, clock)); + } else { + printf ("Not supported cpu version"); + return -1; + } + return 0; +} +#endif /* CONFIG_MPC555 */ + + +/* + * Check version of mpc5xx + */ +int checkcpu (void) +{ + ulong clock = gd->cpu_clk; + uint immr = get_immr (0); /* Return full IMMR contents */ + uint pvr = get_pvr (); /* Retrieve PVR register */ + + puts ("CPU: "); + + return check_cpu_version (clock, pvr, immr); +} + +/* + * Called by macro WATCHDOG_RESET + */ +#if defined(CONFIG_WATCHDOG) +void watchdog_reset (void) +{ + int re_enable = disable_interrupts (); + + reset_5xx_watchdog ((immap_t *) CONFIG_SYS_IMMR); + if (re_enable) + enable_interrupts (); +} + +/* + * Will clear software reset + */ +void reset_5xx_watchdog (volatile immap_t * immr) +{ + /* Use the MPC5xx Internal Watchdog */ + immr->im_siu_conf.sc_swsr = 0x556c; /* Prevent SW time-out */ + immr->im_siu_conf.sc_swsr = 0xaa39; +} + +#endif /* CONFIG_WATCHDOG */ + + +/* + * Get timebase clock frequency + */ +unsigned long get_tbclk (void) +{ + volatile immap_t *immr = (volatile immap_t *) CONFIG_SYS_IMMR; + ulong oscclk, factor; + + if (immr->im_clkrst.car_sccr & SCCR_TBS) { + return (gd->cpu_clk / 16); + } + + factor = (((CONFIG_SYS_PLPRCR) & PLPRCR_MF_MSK) >> PLPRCR_MF_SHIFT) + 1; + + oscclk = gd->cpu_clk / factor; + + if ((immr->im_clkrst.car_sccr & SCCR_RTSEL) == 0 || factor > 2) { + return (oscclk / 4); + } + return (oscclk / 16); +} + +void dcache_enable (void) +{ + return; +} + +void dcache_disable (void) +{ + return; +} + +int dcache_status (void) +{ + return 0; /* always off */ +} + +/* + * Reset board + */ +int do_reset (cmd_tbl_t * cmdtp, int flag, int argc, char *argv[]) +{ +#if defined(CONFIG_PATI) + volatile ulong *addr = (ulong *) CONFIG_SYS_RESET_ADDRESS; + *addr = 1; +#else + ulong addr; + + /* Interrupts off, enable reset */ + __asm__ volatile (" mtspr 81, %r0 \n\t" + " mfmsr %r3 \n\t" + " rlwinm %r31,%r3,0,25,23\n\t" + " mtmsr %r31 \n\t"); + /* + * Trying to execute the next instruction at a non-existing address + * should cause a machine check, resulting in reset + */ +#ifdef CONFIG_SYS_RESET_ADDRESS + addr = CONFIG_SYS_RESET_ADDRESS; +#else + /* + * note: when CONFIG_SYS_MONITOR_BASE points to a RAM address, CONFIG_SYS_MONITOR_BASE * - sizeof (ulong) is usually a valid address. Better pick an address + * known to be invalid on your system and assign it to CONFIG_SYS_RESET_ADDRESS. + * "(ulong)-1" used to be a good choice for many systems... + */ + addr = CONFIG_SYS_MONITOR_BASE - sizeof (ulong); +#endif + ((void (*) (void)) addr) (); +#endif /* #if defined(CONFIG_PATI) */ + return 1; +} diff --git a/arch/powerpc/cpu/mpc5xx/cpu_init.c b/arch/powerpc/cpu/mpc5xx/cpu_init.c new file mode 100644 index 0000000..cb4bf84 --- /dev/null +++ b/arch/powerpc/cpu/mpc5xx/cpu_init.c @@ -0,0 +1,123 @@ +/* + * (C) Copyright 2003 Martin Winistoerfer, martinwinistoerfer@gmx.ch. + * + * 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, + */ + +/* + * File: cpu_init.c + * + * Discription: Contains initialisation functions to setup + * the cpu properly + * + */ + +#include <common.h> +#include <mpc5xx.h> +#include <watchdog.h> + +/* + * Setup essential cpu registers to run + */ +void cpu_init_f (volatile immap_t * immr) +{ + volatile memctl5xx_t *memctl = &immr->im_memctl; + ulong reg; + + /* SYPCR - contains watchdog control. This will enable watchdog */ + /* if CONFIG_WATCHDOG is set */ + immr->im_siu_conf.sc_sypcr = CONFIG_SYS_SYPCR; + +#if defined(CONFIG_WATCHDOG) + reset_5xx_watchdog (immr); +#endif + + /* SIUMCR - contains debug pin configuration */ + immr->im_siu_conf.sc_siumcr |= CONFIG_SYS_SIUMCR; + + /* Initialize timebase. Unlock TBSCRK */ + immr->im_sitk.sitk_tbscrk = KAPWR_KEY; + immr->im_sit.sit_tbscr = CONFIG_SYS_TBSCR; + + /* Full IMB bus speed */ + immr->im_uimb.uimb_umcr = CONFIG_SYS_UMCR; + + /* Time base and decrementer will be enables (TBE) */ + /* in init_timebase() in time.c called from board_init_f(). */ + + /* Initialize the PIT. Unlock PISCRK */ + immr->im_sitk.sitk_piscrk = KAPWR_KEY; + immr->im_sit.sit_piscr = CONFIG_SYS_PISCR; + +#if !defined(CONFIG_PATI) + /* PATI sest PLL in start.S */ + /* PLL (CPU clock) settings */ + immr->im_clkrstk.cark_plprcrk = KAPWR_KEY; + + /* If CONFIG_SYS_PLPRCR (set in the various *_config.h files) tries to + * set the MF field, then just copy CONFIG_SYS_PLPRCR over car_plprcr, + * otherwise OR in CONFIG_SYS_PLPRCR so we do not change the currentMF + * field value. + */ +#if ((CONFIG_SYS_PLPRCR & PLPRCR_MF_MSK) != 0) + reg = CONFIG_SYS_PLPRCR; /* reset control bits */ +#else + reg = immr->im_clkrst.car_plprcr; + reg &= PLPRCR_MF_MSK; /* isolate MF field */ + reg |= CONFIG_SYS_PLPRCR; /* reset control bits */ +#endif + immr->im_clkrst.car_plprcr = reg; + +#endif /* !defined(CONFIG_PATI) */ + + /* System integration timers. CONFIG_SYS_MASK has EBDF configuration */ + immr->im_clkrstk.cark_sccrk = KAPWR_KEY; + reg = immr->im_clkrst.car_sccr; + reg &= SCCR_MASK; + reg |= CONFIG_SYS_SCCR; + immr->im_clkrst.car_sccr = reg; + + /* Memory Controller */ + memctl->memc_br0 = CONFIG_SYS_BR0_PRELIM; + memctl->memc_or0 = CONFIG_SYS_OR0_PRELIM; + +#if (defined(CONFIG_SYS_OR1_PRELIM) && defined(CONFIG_SYS_BR1_PRELIM)) + memctl->memc_or1 = CONFIG_SYS_OR1_PRELIM; + memctl->memc_br1 = CONFIG_SYS_BR1_PRELIM; +#endif + +#if defined(CONFIG_SYS_OR2_PRELIM) && defined(CONFIG_SYS_BR2_PRELIM) + memctl->memc_or2 = CONFIG_SYS_OR2_PRELIM; + memctl->memc_br2 = CONFIG_SYS_BR2_PRELIM; +#endif + +#if defined(CONFIG_SYS_OR3_PRELIM) && defined(CONFIG_SYS_BR3_PRELIM) + memctl->memc_or3 = CONFIG_SYS_OR3_PRELIM; + memctl->memc_br3 = CONFIG_SYS_BR3_PRELIM; +#endif + +} + +/* + * Initialize higher level parts of cpu + */ +int cpu_init_r (void) +{ + /* Nothing to do at the moment */ + return (0); +} diff --git a/arch/powerpc/cpu/mpc5xx/interrupts.c b/arch/powerpc/cpu/mpc5xx/interrupts.c new file mode 100644 index 0000000..167543f --- /dev/null +++ b/arch/powerpc/cpu/mpc5xx/interrupts.c @@ -0,0 +1,207 @@ +/* + * (C) Copyright 2000-2002 Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * (C) Copyright 2003 Martin Winistoerfer, martinwinistoerfer@gmx.ch. + * + * 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, + */ + +/* + * File: interrupt.c + * + * Discription: Contains interrupt routines needed by U-Boot + * + */ + +#include <common.h> +#include <command.h> +#include <mpc5xx.h> +#include <asm/processor.h> + +#if defined(CONFIG_PATI) +/* PATI uses IRQs for PCI doorbell */ +#undef NR_IRQS +#define NR_IRQS 16 +#endif + +struct interrupt_action { + interrupt_handler_t *handler; + void *arg; + int count; +}; + +static struct interrupt_action irq_vecs[NR_IRQS]; + +/* + * Initialise interrupts + */ + +int interrupt_init_cpu (ulong *decrementer_count) +{ + volatile immap_t *immr = (immap_t *) CONFIG_SYS_IMMR; + int vec; + + /* Decrementer used here for status led */ + *decrementer_count = get_tbclk () / CONFIG_SYS_HZ; + + /* Disable all interrupts */ + immr->im_siu_conf.sc_simask = 0; + for (vec=0; vec<NR_IRQS; vec++) { + irq_vecs[vec].handler = NULL; + irq_vecs[vec].arg = NULL; + irq_vecs[vec].count = 0; + } + + return (0); +} + +/* + * Handle external interrupts + */ +void external_interrupt (struct pt_regs *regs) +{ + volatile immap_t *immr = (immap_t *) CONFIG_SYS_IMMR; + int irq; + ulong simask, newmask; + ulong vec, v_bit; + + /* + * read the SIVEC register and shift the bits down + * to get the irq number + */ + vec = immr->im_siu_conf.sc_sivec; + irq = vec >> 26; + v_bit = 0x80000000UL >> irq; + + /* + * Read Interrupt Mask Register and Mask Interrupts + */ + simask = immr->im_siu_conf.sc_simask; + newmask = simask & (~(0xFFFF0000 >> irq)); + immr->im_siu_conf.sc_simask = newmask; + + if (!(irq & 0x1)) { /* External Interrupt ? */ + ulong siel; + + /* + * Read Interrupt Edge/Level Register + */ + siel = immr->im_siu_conf.sc_siel; + + if (siel & v_bit) { /* edge triggered interrupt ? */ + /* + * Rewrite SIPEND Register to clear interrupt + */ + immr->im_siu_conf.sc_sipend = v_bit; + } + } + + if (irq_vecs[irq].handler != NULL) { + irq_vecs[irq].handler (irq_vecs[irq].arg); + } else { + printf ("\nBogus External Interrupt IRQ %d Vector %ld\n", + irq, vec); + /* turn off the bogus interrupt to avoid it from now */ + simask &= ~v_bit; + } + /* + * Re-Enable old Interrupt Mask + */ + immr->im_siu_conf.sc_simask = simask; +} + +/* + * Install and free an interrupt handler + */ +void irq_install_handler (int vec, interrupt_handler_t * handler, + void *arg) +{ + volatile immap_t *immr = (immap_t *) CONFIG_SYS_IMMR; + /* SIU interrupt */ + if (irq_vecs[vec].handler != NULL) { + printf ("SIU interrupt %d 0x%x\n", + vec, + (uint) handler); + } + irq_vecs[vec].handler = handler; + irq_vecs[vec].arg = arg; + immr->im_siu_conf.sc_simask |= 1 << (31 - vec); +#if 0 + printf ("Install SIU interrupt for vector %d ==> %p\n", + vec, handler); +#endif +} + +void irq_free_handler (int vec) +{ + volatile immap_t *immr = (immap_t *) CONFIG_SYS_IMMR; + /* SIU interrupt */ +#if 0 + printf ("Free CPM interrupt for vector %d\n", + vec); +#endif + immr->im_siu_conf.sc_simask &= ~(1 << (31 - vec)); + irq_vecs[vec].handler = NULL; + irq_vecs[vec].arg = NULL; +} + +/* + * Timer interrupt - gets called when bit 0 of DEC changes from + * 0. Decrementer is enabled with bit TBE in TBSCR. + */ +void timer_interrupt_cpu (struct pt_regs *regs) +{ + volatile immap_t *immr = (immap_t *) CONFIG_SYS_IMMR; + +#if 0 + printf ("*** Timer Interrupt *** "); +#endif + /* Reset Timer Status Bit and Timers Interrupt Status */ + immr->im_clkrstk.cark_plprcrk = KAPWR_KEY; + __asm__ ("nop"); + immr->im_clkrst.car_plprcr |= PLPRCR_TEXPS | PLPRCR_TMIST; + + return; +} + +#if defined(CONFIG_CMD_IRQ) +/******************************************************************************* + * + * irqinfo - print information about IRQs + * + */ +int do_irqinfo(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]) +{ + int vec; + + printf ("\nInterrupt-Information:\n"); + printf ("Nr Routine Arg Count\n"); + + for (vec=0; vec<NR_IRQS; vec++) { + if (irq_vecs[vec].handler != NULL) { + printf ("%02d %08lx %08lx %d\n", + vec, + (ulong)irq_vecs[vec].handler, + (ulong)irq_vecs[vec].arg, + irq_vecs[vec].count); + } + } + return 0; +} + + +#endif diff --git a/arch/powerpc/cpu/mpc5xx/serial.c b/arch/powerpc/cpu/mpc5xx/serial.c new file mode 100644 index 0000000..88c6db8 --- /dev/null +++ b/arch/powerpc/cpu/mpc5xx/serial.c @@ -0,0 +1,170 @@ +/* + * (C) Copyright 2003 + * Martin Winistoerfer, martinwinistoerfer@gmx.ch. + * + * 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, + */ + +/* + * File: serial.c + * + * Discription: Serial interface driver for SCI1 and SCI2. + * Since this code will be called from ROM use + * only non-static local variables. + * + */ + +#include <common.h> +#include <watchdog.h> +#include <command.h> +#include <mpc5xx.h> + +DECLARE_GLOBAL_DATA_PTR; + +/* + * Local function prototypes + */ + +static int ready_to_send(void); + +/* + * Minimal global serial functions needed to use one of the SCI modules. + */ + +int serial_init (void) +{ + volatile immap_t *immr = (immap_t *)CONFIG_SYS_IMMR; + + serial_setbrg(); + +#if defined(CONFIG_5xx_CONS_SCI1) + /* 10-Bit, 1 start bit, 8 data bit, no parity, 1 stop bit */ + immr->im_qsmcm.qsmcm_scc1r1 = SCI_M_10; + immr->im_qsmcm.qsmcm_scc1r1 = SCI_TE | SCI_RE; +#else + immr->im_qsmcm.qsmcm_scc2r1 = SCI_M_10; + immr->im_qsmcm.qsmcm_scc2r1 = SCI_TE | SCI_RE; +#endif + return 0; +} + +void serial_putc(const char c) +{ + volatile immap_t *immr = (immap_t *)CONFIG_SYS_IMMR; + + /* Test for completition */ + if(ready_to_send()) { +#if defined(CONFIG_5xx_CONS_SCI1) + immr->im_qsmcm.qsmcm_sc1dr = (short)c; +#else + immr->im_qsmcm.qsmcm_sc2dr = (short)c; +#endif + if(c == '\n') { + if(ready_to_send()); +#if defined(CONFIG_5xx_CONS_SCI1) + immr->im_qsmcm.qsmcm_sc1dr = (short)'\r'; +#else + immr->im_qsmcm.qsmcm_sc2dr = (short)'\r'; +#endif + } + } +} + +int serial_getc(void) +{ + volatile immap_t *immr = (immap_t *)CONFIG_SYS_IMMR; + volatile short status; + unsigned char tmp; + + /* New data ? */ + do { +#if defined(CONFIG_5xx_CONS_SCI1) + status = immr->im_qsmcm.qsmcm_sc1sr; +#else + status = immr->im_qsmcm.qsmcm_sc2sr; +#endif + +#if defined(CONFIG_WATCHDOG) + reset_5xx_watchdog (immr); +#endif + } while ((status & SCI_RDRF) == 0); + + /* Read data */ +#if defined(CONFIG_5xx_CONS_SCI1) + tmp = (unsigned char)(immr->im_qsmcm.qsmcm_sc1dr & SCI_SCXDR_MK); +#else + tmp = (unsigned char)( immr->im_qsmcm.qsmcm_sc2dr & SCI_SCXDR_MK); +#endif + return tmp; +} + +int serial_tstc() +{ + volatile immap_t *immr = (immap_t *)CONFIG_SYS_IMMR; + short status; + + /* New data character ? */ +#if defined(CONFIG_5xx_CONS_SCI1) + status = immr->im_qsmcm.qsmcm_sc1sr; +#else + status = immr->im_qsmcm.qsmcm_sc2sr; +#endif + return (status & SCI_RDRF); +} + +void serial_setbrg (void) +{ + volatile immap_t *immr = (immap_t *)CONFIG_SYS_IMMR; + short scxbr; + + /* Set baudrate */ + scxbr = (gd->cpu_clk / (32 * gd->baudrate)); +#if defined(CONFIG_5xx_CONS_SCI1) + immr->im_qsmcm.qsmcm_scc1r0 = (scxbr & SCI_SCXBR_MK); +#else + immr->im_qsmcm.qsmcm_scc2r0 = (scxbr & SCI_SCXBR_MK); +#endif +} + +void serial_puts (const char *s) +{ + while (*s) { + serial_putc(*s); + ++s; + } +} + +int ready_to_send(void) +{ + volatile immap_t *immr = (immap_t *)CONFIG_SYS_IMMR; + volatile short status; + + do { +#if defined(CONFIG_5xx_CONS_SCI1) + status = immr->im_qsmcm.qsmcm_sc1sr; +#else + status = immr->im_qsmcm.qsmcm_sc2sr; +#endif + +#if defined(CONFIG_WATCHDOG) + reset_5xx_watchdog (immr); +#endif + } while ((status & SCI_TDRE) == 0); + return 1; + +} diff --git a/arch/powerpc/cpu/mpc5xx/speed.c b/arch/powerpc/cpu/mpc5xx/speed.c new file mode 100644 index 0000000..ea5c1de --- /dev/null +++ b/arch/powerpc/cpu/mpc5xx/speed.c @@ -0,0 +1,67 @@ +/* + * (C) Copyright 2003 + * Martin Winistoerfer, martinwinistoerfer@gmx.ch. + * + * 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, + */ + +/* + * File: speed.c + * + * Discription: Provides cpu speed calculation + * + */ + +#include <common.h> +#include <mpc5xx.h> +#include <asm/processor.h> + +DECLARE_GLOBAL_DATA_PTR; + +/* + * Get cpu and bus clock + */ +int get_clocks (void) +{ + volatile immap_t *immr = (immap_t *) CONFIG_SYS_IMMR; + +#ifndef CONFIG_5xx_GCLK_FREQ + uint divf = (immr->im_clkrst.car_plprcr & PLPRCR_DIVF_MSK); + uint mf = ((immr->im_clkrst.car_plprcr & PLPRCR_MF_MSK) >> PLPRCR_MF_SHIFT); + ulong vcoout; + + vcoout = (CONFIG_SYS_OSC_CLK / (divf + 1)) * (mf + 1) * 2; + if(immr->im_clkrst.car_plprcr & PLPRCR_CSRC_MSK) { + gd->cpu_clk = vcoout / (2^(((immr->im_clkrst.car_sccr & SCCR_DFNL_MSK) >> SCCR_DFNL_SHIFT) + 1)); + } else { + gd->cpu_clk = vcoout / (2^(immr->im_clkrst.car_sccr & SCCR_DFNH_MSK)); + } + +#else /* CONFIG_5xx_GCLK_FREQ */ + gd->bus_clk = CONFIG_5xx_GCLK_FREQ; +#endif /* CONFIG_5xx_GCLK_FREQ */ + + if ((immr->im_clkrst.car_sccr & SCCR_EBDF11) == 0) { + /* No Bus Divider active */ + gd->bus_clk = gd->cpu_clk; + } else { + /* CLKOUT is GCLK / 2 */ + gd->bus_clk = gd->cpu_clk / 2; + } + return (0); +} diff --git a/arch/powerpc/cpu/mpc5xx/spi.c b/arch/powerpc/cpu/mpc5xx/spi.c new file mode 100644 index 0000000..3ca15ea --- /dev/null +++ b/arch/powerpc/cpu/mpc5xx/spi.c @@ -0,0 +1,412 @@ +/* + * Copyright (c) 2001 Navin Boppuri / Prashant Patel + * <nboppuri@trinetcommunication.com>, + * <pmpatel@trinetcommunication.com> + * Copyright (c) 2001 Gerd Mennchen <Gerd.Mennchen@icn.siemens.de> + * Copyright (c) 2001 Wolfgang Denk, DENX Software Engineering, <wd@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 + */ + +/* + * MPC5xx CPM SPI interface. + * + * Parts of this code are probably not portable and/or specific to + * the board which I used for the tests. Please send fixes/complaints + * to wd@denx.de + * + * Ported to MPC5xx + * Copyright (c) 2003 Denis Peter, MPL AG Switzerland, d.petr@mpl.ch. + */ + +#include <common.h> +#include <mpc5xx.h> +#include <asm/5xx_immap.h> +#include <linux/ctype.h> +#include <malloc.h> +#include <post.h> +#include <net.h> + +#if defined(CONFIG_SPI) + +#undef DEBUG + +#define SPI_EEPROM_WREN 0x06 +#define SPI_EEPROM_RDSR 0x05 +#define SPI_EEPROM_READ 0x03 +#define SPI_EEPROM_WRITE 0x02 + + +#ifdef DEBUG + +#define DPRINT(a) printf a; +/* ----------------------------------------------- + * Helper functions to peek into tx and rx buffers + * ----------------------------------------------- */ +static const char * const hex_digit = "0123456789ABCDEF"; + +static char quickhex (int i) +{ + return hex_digit[i]; +} + +static void memdump (void *pv, int num) +{ + int i; + unsigned char *pc = (unsigned char *) pv; + + for (i = 0; i < num; i++) + printf ("%c%c ", quickhex (pc[i] >> 4), quickhex (pc[i] & 0x0f)); + printf ("\t"); + for (i = 0; i < num; i++) + printf ("%c", isprint (pc[i]) ? pc[i] : '.'); + printf ("\n"); +} +#else /* !DEBUG */ + +#define DPRINT(a) + +#endif /* DEBUG */ + +/* ------------------- + * Function prototypes + * ------------------- */ +void spi_init (void); + +ssize_t spi_read (uchar *, int, uchar *, int); +ssize_t spi_write (uchar *, int, uchar *, int); +ssize_t spi_xfer (size_t); + + +/* ************************************************************************** + * + * Function: spi_init_f + * + * Description: Init SPI-Controller (ROM part) + * + * return: --- + * + * *********************************************************************** */ + +void spi_init_f (void) +{ + int i; + + volatile immap_t *immr; + volatile qsmcm5xx_t *qsmcm; + + immr = (immap_t *) CONFIG_SYS_IMMR; + qsmcm = (qsmcm5xx_t *)&immr->im_qsmcm; + + qsmcm->qsmcm_qsmcr = 0; /* all accesses enabled */ + qsmcm->qsmcm_qspi_il = 0; /* lowest IRQ */ + + /* -------------------------------------------- + * GPIO or per. Function + * PQSPAR[00] = 0 reserved + * PQSPAR[01] = 1 [0x4000] -> PERI: (SPICS3) + * PQSPAR[02] = 0 [0x0000] -> GPIO + * PQSPAR[03] = 0 [0x0000] -> GPIO + * PQSPAR[04] = 1 [0x0800] -> PERI: (SPICS0) + * PQSPAR[05] = 0 reseved + * PQSPAR[06] = 1 [0x0200] -> PERI: (SPIMOSI) + * PQSPAR[07] = 1 [0x0100] -> PERI: (SPIMISO) + * -------------------------------------------- */ + qsmcm->qsmcm_pqspar = 0x3 | (CONFIG_SYS_SPI_CS_USED << 3); + + /* -------------------------------------------- + * DDRQS[00] = 0 reserved + * DDRQS[01] = 1 [0x0040] -> SPICS3 Output + * DDRQS[02] = 0 [0x0000] -> GPIO Output + * DDRQS[03] = 0 [0x0000] -> GPIO Output + * DDRQS[04] = 1 [0x0008] -> SPICS0 Output + * DDRQS[05] = 1 [0x0004] -> SPICLK Output + * DDRQS[06] = 1 [0x0002] -> SPIMOSI Output + * DDRQS[07] = 0 [0x0001] -> SPIMISO Input + * -------------------------------------------- */ + qsmcm->qsmcm_ddrqs = 0x7E; + /* -------------------------------------------- + * Base state for used SPI CS pins, if base = 0 active must be 1 + * PORTQS[00] = 0 reserved + * PORTQS[01] = 0 reserved + * PORTQS[02] = 0 reserved + * PORTQS[03] = 0 reserved + * PORTQS[04] = 0 [0x0000] RxD2 + * PORTQS[05] = 1 [0x0400] TxD2 + * PORTQS[06] = 0 [0x0000] RxD1 + * PORTQS[07] = 1 [0x0100] TxD1 + * PORTQS[08] = 0 reserved + * PORTQS[09] = 0 [0x0000] -> SPICS3 Base Output + * PORTQS[10] = 0 [0x0000] -> SPICS2 Base Output + * PORTQS[11] = 0 [0x0000] -> SPICS1 Base Output + * PORTQS[12] = 0 [0x0000] -> SPICS0 Base Output + * PORTQS[13] = 0 [0x0004] -> SPICLK Output + * PORTQS[14] = 0 [0x0002] -> SPIMOSI Output + * PORTQS[15] = 0 [0x0001] -> SPIMISO Input + * -------------------------------------------- */ + qsmcm->qsmcm_portqs |= (CONFIG_SYS_SPI_CS_BASE << 3); + /* -------------------------------------------- + * Controll Register 0 + * SPCR0[00] = 1 (0x8000) Master + * SPCR0[01] = 0 (0x0000) Wired-Or + * SPCR0[2..5] = (0x2000) Bits per transfer (default 8) + * SPCR0[06] = 0 (0x0000) Normal polarity + * SPCR0[07] = 0 (0x0000) Normal Clock Phase + * SPCR0[08..15] = 14 1.4MHz + */ + qsmcm->qsmcm_spcr0=0xA00E; + /* -------------------------------------------- + * Controll Register 1 + * SPCR1[00] = 0 (0x0000) QSPI enabled + * SPCR1[1..7] = (0x7F00) Delay before Transfer + * SPCR1[8..15] = (0x0000) Delay After transfer (204.8usec@40MHz) + */ + qsmcm->qsmcm_spcr1=0x7F00; + /* -------------------------------------------- + * Controll Register 2 + * SPCR2[00] = 0 (0x0000) SPI IRQs Disabeld + * SPCR2[01] = 0 (0x0000) No Wrap around + * SPCR2[02] = 0 (0x0000) Wrap to 0 + * SPCR2[3..7] = (0x0000) End Queue pointer = 0 + * SPCR2[8..10] = 0 (0x0000) reserved + * SPCR2[11..15] = 0 (0x0000) NewQueue Address = 0 + */ + qsmcm->qsmcm_spcr2=0x0000; + /* -------------------------------------------- + * Controll Register 3 + * SPCR3[00..04] = 0 (0x0000) reserved + * SPCR3[05] = 0 (0x0000) Feedback disabled + * SPCR3[06] = 0 (0x0000) IRQ on HALTA & MODF disabled + * SPCR3[07] = 0 (0x0000) Not halted + */ + qsmcm->qsmcm_spcr3=0x00; + /* -------------------------------------------- + * SPSR (Controll Register 3) Read only/ reset Flags 08,09,10 + * SPCR3[08] = 1 (0x80) QSPI finished + * SPCR3[09] = 1 (0x40) Mode Fault Flag + * SPCR3[10] = 1 (0x20) HALTA + * SPCR3[11..15] = 0 (0x0000) Last executed command + */ + qsmcm->qsmcm_spsr=0xE0; + /*------------------------------------------- + * Setup RAM + */ + for(i=0;i<32;i++) { + qsmcm->qsmcm_recram[i]=0x0000; + qsmcm->qsmcm_tranram[i]=0x0000; + qsmcm->qsmcm_comdram[i]=0x00; + } + return; +} + +/* ************************************************************************** + * + * Function: spi_init_r + * Dummy, all initializations have been done in spi_init_r + * *********************************************************************** */ +void spi_init_r (void) +{ + return; + +} + +/**************************************************************************** + * Function: spi_write + **************************************************************************** */ +ssize_t short_spi_write (uchar *addr, int alen, uchar *buffer, int len) +{ + int i,dlen; + volatile immap_t *immr; + volatile qsmcm5xx_t *qsmcm; + + immr = (immap_t *) CONFIG_SYS_IMMR; + qsmcm = (qsmcm5xx_t *)&immr->im_qsmcm; + for(i=0;i<32;i++) { + qsmcm->qsmcm_recram[i]=0x0000; + qsmcm->qsmcm_tranram[i]=0x0000; + qsmcm->qsmcm_comdram[i]=0x00; + } + qsmcm->qsmcm_tranram[0] = SPI_EEPROM_WREN; /* write enable */ + spi_xfer(1); + i=0; + qsmcm->qsmcm_tranram[i++] = SPI_EEPROM_WRITE; /* WRITE memory array */ + qsmcm->qsmcm_tranram[i++] = addr[0]; + qsmcm->qsmcm_tranram[i++] = addr[1]; + + for(dlen=0;dlen<len;dlen++) { + qsmcm->qsmcm_tranram[i+dlen] = buffer[dlen]; /* WRITE memory array */ + } + /* transmit it */ + spi_xfer(i+dlen); + /* ignore received data */ + for (i = 0; i < 1000; i++) { + qsmcm->qsmcm_tranram[0] = SPI_EEPROM_RDSR; /* read status */ + qsmcm->qsmcm_tranram[1] = 0; + spi_xfer(2); + if (!(qsmcm->qsmcm_recram[1] & 1)) { + break; + } + udelay(1000); + } + if (i >= 1000) { + printf ("*** spi_write: Time out while writing!\n"); + } + return len; +} + +#define TRANSFER_LEN 16 + +ssize_t spi_write (uchar *addr, int alen, uchar *buffer, int len) +{ + int index,i,newlen; + uchar newaddr[2]; + int curraddr; + + curraddr=(addr[alen-2]<<8)+addr[alen-1]; + i=len; + index=0; + do { + newaddr[1]=(curraddr & 0xff); + newaddr[0]=((curraddr>>8) & 0xff); + if(i>TRANSFER_LEN) { + newlen=TRANSFER_LEN; + i-=TRANSFER_LEN; + } + else { + newlen=i; + i=0; + } + short_spi_write (newaddr, 2, &buffer[index], newlen); + index+=newlen; + curraddr+=newlen; + }while(i); + return (len); +} + +/**************************************************************************** + * Function: spi_read + **************************************************************************** */ +ssize_t short_spi_read (uchar *addr, int alen, uchar *buffer, int len) +{ + int i; + volatile immap_t *immr; + volatile qsmcm5xx_t *qsmcm; + + immr = (immap_t *) CONFIG_SYS_IMMR; + qsmcm = (qsmcm5xx_t *)&immr->im_qsmcm; + + for(i=0;i<32;i++) { + qsmcm->qsmcm_recram[i]=0x0000; + qsmcm->qsmcm_tranram[i]=0x0000; + qsmcm->qsmcm_comdram[i]=0x00; + } + i=0; + qsmcm->qsmcm_tranram[i++] = (SPI_EEPROM_READ); /* READ memory array */ + qsmcm->qsmcm_tranram[i++] = addr[0] & 0xff; + qsmcm->qsmcm_tranram[i++] = addr[1] & 0xff; + spi_xfer(3 + len); + for(i=0;i<len;i++) { + *buffer++=(char)qsmcm->qsmcm_recram[i+3]; + } + return len; +} + +ssize_t spi_read (uchar *addr, int alen, uchar *buffer, int len) +{ + int index,i,newlen; + uchar newaddr[2]; + int curraddr; + + curraddr=(addr[alen-2]<<8)+addr[alen-1]; + i=len; + index=0; + do { + newaddr[1]=(curraddr & 0xff); + newaddr[0]=((curraddr>>8) & 0xff); + if(i>TRANSFER_LEN) { + newlen=TRANSFER_LEN; + i-=TRANSFER_LEN; + } + else { + newlen=i; + i=0; + } + short_spi_read (newaddr, 2, &buffer[index], newlen); + index+=newlen; + curraddr+=newlen; + }while(i); + return (len); +} + +/**************************************************************************** + * Function: spi_xfer + **************************************************************************** */ +ssize_t spi_xfer (size_t count) +{ + volatile immap_t *immr; + volatile qsmcm5xx_t *qsmcm; + int i; + int tm; + ushort status; + immr = (immap_t *) CONFIG_SYS_IMMR; + qsmcm = (qsmcm5xx_t *)&immr->im_qsmcm; + DPRINT (("*** spi_xfer entered count %d***\n",count)); + + /* Set CS for device */ + for(i=0;i<(count-1);i++) + qsmcm->qsmcm_comdram[i] = 0x80 | CONFIG_SYS_SPI_CS_ACT; /* CS3 is connected to the SPI EEPROM */ + + qsmcm->qsmcm_comdram[i] = CONFIG_SYS_SPI_CS_ACT; /* CS3 is connected to the SPI EEPROM */ + qsmcm->qsmcm_spcr2=((count-1)&0x1F)<<8; + + DPRINT (("*** spi_xfer: Bytes to be xferred: %d ***\n", count)); + + qsmcm->qsmcm_spsr=0xE0; /* clear all flags */ + + /* start spi transfer */ + DPRINT (("*** spi_xfer: Performing transfer ...\n")); + qsmcm->qsmcm_spcr1 |= 0x8000; /* Start transmit */ + + /* -------------------------------- + * Wait for SPI transmit to get out + * or time out (1 second = 1000 ms) + * -------------------------------- */ + for (tm=0; tm<1000; ++tm) { + status=qsmcm->qsmcm_spcr1; + if((status & 0x8000)==0) + break; + udelay (1000); + } + if (tm >= 1000) { + printf ("*** spi_xfer: Time out while xferring to/from SPI!\n"); + } +#ifdef DEBUG + printf ("\nspi_xfer: txbuf after xfer\n"); + memdump ((void *) qsmcm->qsmcm_tranram, 32); /* dump of txbuf before transmit */ + printf ("spi_xfer: rxbuf after xfer\n"); + memdump ((void *) qsmcm->qsmcm_recram, 32); /* dump of rxbuf after transmit */ + printf ("\nspi_xfer: commbuf after xfer\n"); + memdump ((void *) qsmcm->qsmcm_comdram, 32); /* dump of txbuf before transmit */ + printf ("\n"); +#endif + + return count; +} + +#endif /* CONFIG_SPI */ diff --git a/arch/powerpc/cpu/mpc5xx/start.S b/arch/powerpc/cpu/mpc5xx/start.S new file mode 100644 index 0000000..0af879e --- /dev/null +++ b/arch/powerpc/cpu/mpc5xx/start.S @@ -0,0 +1,576 @@ +/* + * Copyright (C) 1998 Dan Malek <dmalek@jlc.net> + * Copyright (C) 1999 Magnus Damm <kieraypc01.p.y.kie.era.ericsson.se> + * Copyright (C) 2000, 2001, 2002 Wolfgang Denk <wd@denx.de> + * Copyright (C) 2003 Martin Winistoerfer, martinwinistoerfer@gmx.ch. + * + * 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 + */ + +/* + * File: start.S + * + * Discription: startup code + * + */ + +#include <config.h> +#include <mpc5xx.h> +#include <timestamp.h> +#include <version.h> + +#define CONFIG_5xx 1 /* needed for Linux kernel header files */ +#define _LINUX_CONFIG_H 1 /* avoid reading Linux autoconf.h file */ + +#include <ppc_asm.tmpl> +#include <ppc_defs.h> + +#include <linux/config.h> +#include <asm/processor.h> + +#ifndef CONFIG_IDENT_STRING +#define CONFIG_IDENT_STRING "" +#endif + +/* We don't have a MMU. +*/ +#undef MSR_KERNEL +#define MSR_KERNEL ( MSR_ME | MSR_RI ) /* Machine Check and Recoverable Interr. */ + +/* + * Set up GOT: Global Offset Table + * + * Use r12 to access the GOT + */ + START_GOT + GOT_ENTRY(_GOT2_TABLE_) + GOT_ENTRY(_FIXUP_TABLE_) + + GOT_ENTRY(_start) + GOT_ENTRY(_start_of_vectors) + GOT_ENTRY(_end_of_vectors) + GOT_ENTRY(transfer_to_handler) + + GOT_ENTRY(__init_end) + GOT_ENTRY(_end) + GOT_ENTRY(__bss_start) + END_GOT + +/* + * r3 - 1st arg to board_init(): IMMP pointer + * r4 - 2nd arg to board_init(): boot flag + */ + .text + .long 0x27051956 /* U-Boot Magic Number */ + .globl version_string +version_string: + .ascii U_BOOT_VERSION + .ascii " (", U_BOOT_DATE, " - ", U_BOOT_TIME, ")" + .ascii CONFIG_IDENT_STRING, "\0" + + . = EXC_OFF_SYS_RESET + .globl _start +_start: + mfspr r3, 638 + li r4, CONFIG_SYS_ISB /* Set ISB bit */ + or r3, r3, r4 + mtspr 638, r3 + li r21, BOOTFLAG_COLD /* Normal Power-On: Boot from FLASH */ + b boot_cold + + . = EXC_OFF_SYS_RESET + 0x20 + + .globl _start_warm +_start_warm: + li r21, BOOTFLAG_WARM /* Software reboot */ + b boot_warm + +boot_cold: +boot_warm: + + /* Initialize machine status; enable machine check interrupt */ + /*----------------------------------------------------------------------*/ + li r3, MSR_KERNEL /* Set ME, RI flags */ + mtmsr r3 + mtspr SRR1, r3 /* Make SRR1 match MSR */ + + /* Initialize debug port registers */ + /*----------------------------------------------------------------------*/ + xor r0, r0, r0 /* Clear R0 */ + mtspr LCTRL1, r0 /* Initialize debug port regs */ + mtspr LCTRL2, r0 + mtspr COUNTA, r0 + mtspr COUNTB, r0 + +#if defined(CONFIG_PATI) + /* the external flash access on PATI fails if programming the PLL to 40MHz. + * Copy the PLL programming code to the internal RAM and execute it + *----------------------------------------------------------------------*/ + lis r3, CONFIG_SYS_MONITOR_BASE@h + ori r3, r3, CONFIG_SYS_MONITOR_BASE@l + addi r3, r3, pll_prog_code_start - _start + EXC_OFF_SYS_RESET + + lis r4, CONFIG_SYS_INIT_RAM_ADDR@h + ori r4, r4, CONFIG_SYS_INIT_RAM_ADDR@l + mtlr r4 + addis r5,0,0x0 + ori r5,r5,((pll_prog_code_end - pll_prog_code_start) >>2) + mtctr r5 + addi r3, r3, -4 + addi r4, r4, -4 +0: + lwzu r0,4(r3) + stwu r0,4(r4) + bdnz 0b /* copy loop */ + blrl +#endif + + /* + * Calculate absolute address in FLASH and jump there + *----------------------------------------------------------------------*/ + + lis r3, CONFIG_SYS_MONITOR_BASE@h + ori r3, r3, CONFIG_SYS_MONITOR_BASE@l + addi r3, r3, in_flash - _start + EXC_OFF_SYS_RESET + mtlr r3 + blr + +in_flash: + + /* Initialize some SPRs that are hard to access from C */ + /*----------------------------------------------------------------------*/ + + lis r3, CONFIG_SYS_IMMR@h /* Pass IMMR as arg1 to C routine */ + lis r2, CONFIG_SYS_INIT_SP_ADDR@h + ori r1, r2, CONFIG_SYS_INIT_SP_ADDR@l /* Set up the stack in internal SRAM */ + /* Note: R0 is still 0 here */ + stwu r0, -4(r1) /* Clear final stack frame so that */ + stwu r0, -4(r1) /* stack backtraces terminate cleanly */ + + /* + * Disable serialized ifetch and show cycles + * (i.e. set processor to normal mode) for maximum + * performance. + */ + + li r2, 0x0007 + mtspr ICTRL, r2 + + /* Set up debug mode entry */ + + lis r2, CONFIG_SYS_DER@h + ori r2, r2, CONFIG_SYS_DER@l + mtspr DER, r2 + + /* Let the C-code set up the rest */ + /* */ + /* Be careful to keep code relocatable ! */ + /*----------------------------------------------------------------------*/ + + GET_GOT /* initialize GOT access */ + + /* r3: IMMR */ + bl cpu_init_f /* run low-level CPU init code (from Flash) */ + + mr r3, r21 + /* r3: BOOTFLAG */ + bl board_init_f /* run 1st part of board init code (from Flash) */ + + + .globl _start_of_vectors +_start_of_vectors: + +/* Machine check */ + STD_EXCEPTION(0x200, MachineCheck, MachineCheckException) + +/* Data Storage exception. "Never" generated on the 860. */ + STD_EXCEPTION(0x300, DataStorage, UnknownException) + +/* Instruction Storage exception. "Never" generated on the 860. */ + STD_EXCEPTION(0x400, InstStorage, UnknownException) + +/* External Interrupt exception. */ + STD_EXCEPTION(0x500, ExtInterrupt, external_interrupt) + +/* Alignment exception. */ + . = 0x600 +Alignment: + EXCEPTION_PROLOG(SRR0, SRR1) + mfspr r4,DAR + stw r4,_DAR(r21) + mfspr r5,DSISR + stw r5,_DSISR(r21) + addi r3,r1,STACK_FRAME_OVERHEAD + EXC_XFER_TEMPLATE(Alignment, AlignmentException, MSR_KERNEL, COPY_EE) + +/* Program check exception */ + . = 0x700 +ProgramCheck: + EXCEPTION_PROLOG(SRR0, SRR1) + addi r3,r1,STACK_FRAME_OVERHEAD + EXC_XFER_TEMPLATE(ProgramCheck, ProgramCheckException, + MSR_KERNEL, COPY_EE) + + /* FPU on MPC5xx available. We will use it later. + */ + STD_EXCEPTION(0x800, FPUnavailable, UnknownException) + + /* I guess we could implement decrementer, and may have + * to someday for timekeeping. + */ + STD_EXCEPTION(0x900, Decrementer, timer_interrupt) + STD_EXCEPTION(0xa00, Trap_0a, UnknownException) + STD_EXCEPTION(0xb00, Trap_0b, UnknownException) + STD_EXCEPTION(0xc00, SystemCall, UnknownException) + STD_EXCEPTION(0xd00, SingleStep, UnknownException) + + STD_EXCEPTION(0xe00, Trap_0e, UnknownException) + STD_EXCEPTION(0xf00, Trap_0f, UnknownException) + + /* On the MPC8xx, this is a software emulation interrupt. It occurs + * for all unimplemented and illegal instructions. + */ + STD_EXCEPTION(0x1000, SoftEmu, SoftEmuException) + STD_EXCEPTION(0x1100, InstructionTLBMiss, UnknownException) + STD_EXCEPTION(0x1200, DataTLBMiss, UnknownException) + STD_EXCEPTION(0x1300, InstructionTLBError, UnknownException) + STD_EXCEPTION(0x1400, DataTLBError, UnknownException) + + STD_EXCEPTION(0x1500, Reserved5, UnknownException) + STD_EXCEPTION(0x1600, Reserved6, UnknownException) + STD_EXCEPTION(0x1700, Reserved7, UnknownException) + STD_EXCEPTION(0x1800, Reserved8, UnknownException) + STD_EXCEPTION(0x1900, Reserved9, UnknownException) + STD_EXCEPTION(0x1a00, ReservedA, UnknownException) + STD_EXCEPTION(0x1b00, ReservedB, UnknownException) + + STD_EXCEPTION(0x1c00, DataBreakpoint, UnknownException) + STD_EXCEPTION(0x1d00, InstructionBreakpoint, DebugException) + STD_EXCEPTION(0x1e00, PeripheralBreakpoint, UnknownException) + STD_EXCEPTION(0x1f00, DevPortBreakpoint, UnknownException) + + + .globl _end_of_vectors +_end_of_vectors: + + + . = 0x2000 + +/* + * This code finishes saving the registers to the exception frame + * and jumps to the appropriate handler for the exception. + * Register r21 is pointer into trap frame, r1 has new stack pointer. + */ + .globl transfer_to_handler +transfer_to_handler: + stw r22,_NIP(r21) + lis r22,MSR_POW@h + andc r23,r23,r22 + stw r23,_MSR(r21) + SAVE_GPR(7, r21) + SAVE_4GPRS(8, r21) + SAVE_8GPRS(12, r21) + SAVE_8GPRS(24, r21) + mflr r23 + andi. r24,r23,0x3f00 /* get vector offset */ + stw r24,TRAP(r21) + li r22,0 + stw r22,RESULT(r21) + mtspr SPRG2,r22 /* r1 is now kernel sp */ + lwz r24,0(r23) /* virtual address of handler */ + lwz r23,4(r23) /* where to go when done */ + mtspr SRR0,r24 + mtspr SRR1,r20 + mtlr r23 + SYNC + rfi /* jump to handler, enable MMU */ + +int_return: + mfmsr r28 /* Disable interrupts */ + li r4,0 + ori r4,r4,MSR_EE + andc r28,r28,r4 + SYNC /* Some chip revs need this... */ + mtmsr r28 + SYNC + lwz r2,_CTR(r1) + lwz r0,_LINK(r1) + mtctr r2 + mtlr r0 + lwz r2,_XER(r1) + lwz r0,_CCR(r1) + mtspr XER,r2 + mtcrf 0xFF,r0 + REST_10GPRS(3, r1) + REST_10GPRS(13, r1) + REST_8GPRS(23, r1) + REST_GPR(31, r1) + lwz r2,_NIP(r1) /* Restore environment */ + lwz r0,_MSR(r1) + mtspr SRR0,r2 + mtspr SRR1,r0 + lwz r0,GPR0(r1) + lwz r2,GPR2(r1) + lwz r1,GPR1(r1) + SYNC + rfi + + +/* + * unsigned int get_immr (unsigned int mask) + * + * return (mask ? (IMMR & mask) : IMMR); + */ + .globl get_immr +get_immr: + mr r4,r3 /* save mask */ + mfspr r3, IMMR /* IMMR */ + cmpwi 0,r4,0 /* mask != 0 ? */ + beq 4f + and r3,r3,r4 /* IMMR & mask */ +4: + blr + + .globl get_pvr +get_pvr: + mfspr r3, PVR + blr + + +/*------------------------------------------------------------------------------*/ + +/* + * void relocate_code (addr_sp, gd, addr_moni) + * + * This "function" does not return, instead it continues in RAM + * after relocating the monitor code. + * + * r3 = dest + * r4 = src + * r5 = length in bytes + * r6 = cachelinesize + */ + .globl relocate_code +relocate_code: + mr r1, r3 /* Set new stack pointer in SRAM */ + mr r9, r4 /* Save copy of global data pointer in SRAM */ + mr r10, r5 /* Save copy of monitor destination Address in SRAM */ + + GET_GOT + mr r3, r5 /* Destination Address */ + lis r4, CONFIG_SYS_MONITOR_BASE@h /* Source Address */ + ori r4, r4, CONFIG_SYS_MONITOR_BASE@l + lwz r5, GOT(__init_end) + sub r5, r5, r4 + + /* + * Fix GOT pointer: + * + * New GOT-PTR = (old GOT-PTR - CONFIG_SYS_MONITOR_BASE) + Destination Address + * + * Offset: + */ + sub r15, r10, r4 + + /* First our own GOT */ + add r12, r12, r15 + /* the the one used by the C code */ + add r30, r30, r15 + + /* + * Now relocate code + */ + + cmplw cr1,r3,r4 + addi r0,r5,3 + srwi. r0,r0,2 + beq cr1,4f /* In place copy is not necessary */ + beq 4f /* Protect against 0 count */ + mtctr r0 + bge cr1,2f + + la r8,-4(r4) + la r7,-4(r3) +1: lwzu r0,4(r8) + stwu r0,4(r7) + bdnz 1b + b 4f + +2: slwi r0,r0,2 + add r8,r4,r0 + add r7,r3,r0 +3: lwzu r0,-4(r8) + stwu r0,-4(r7) + bdnz 3b + +4: sync + isync + +/* + * We are done. Do not return, instead branch to second part of board + * initialization, now running from RAM. + */ + + addi r0, r10, in_ram - _start + EXC_OFF_SYS_RESET + mtlr r0 + blr + +in_ram: + + /* + * Relocation Function, r12 point to got2+0x8000 + * + * Adjust got2 pointers, no need to check for 0, this code + * already puts a few entries in the table. + */ + li r0,__got2_entries@sectoff@l + la r3,GOT(_GOT2_TABLE_) + lwz r11,GOT(_GOT2_TABLE_) + mtctr r0 + sub r11,r3,r11 + addi r3,r3,-4 +1: lwzu r0,4(r3) + cmpwi r0,0 + beq- 2f + add r0,r0,r11 + stw r0,0(r3) +2: bdnz 1b + + /* + * Now adjust the fixups and the pointers to the fixups + * in case we need to move ourselves again. + */ + li r0,__fixup_entries@sectoff@l + lwz r3,GOT(_FIXUP_TABLE_) + cmpwi r0,0 + mtctr r0 + addi r3,r3,-4 + beq 4f +3: lwzu r4,4(r3) + lwzux r0,r4,r11 + add r0,r0,r11 + stw r10,0(r3) + stw r0,0(r4) + bdnz 3b +4: +clear_bss: + /* + * Now clear BSS segment + */ + lwz r3,GOT(__bss_start) + lwz r4,GOT(_end) + cmplw 0, r3, r4 + beq 6f + + li r0, 0 +5: + stw r0, 0(r3) + addi r3, r3, 4 + cmplw 0, r3, r4 + bne 5b +6: + + mr r3, r9 /* Global Data pointer */ + mr r4, r10 /* Destination Address */ + bl board_init_r + + /* + * Copy exception vector code to low memory + * + * r3: dest_addr + * r7: source address, r8: end address, r9: target address + */ + .globl trap_init +trap_init: + mflr r4 /* save link register */ + GET_GOT + lwz r7, GOT(_start) + lwz r8, GOT(_end_of_vectors) + + li r9, 0x100 /* reset vector always at 0x100 */ + + cmplw 0, r7, r8 + bgelr /* return if r7>=r8 - just in case */ +1: + lwz r0, 0(r7) + stw r0, 0(r9) + addi r7, r7, 4 + addi r9, r9, 4 + cmplw 0, r7, r8 + bne 1b + + /* + * relocate `hdlr' and `int_return' entries + */ + li r7, .L_MachineCheck - _start + EXC_OFF_SYS_RESET + li r8, Alignment - _start + EXC_OFF_SYS_RESET +2: + bl trap_reloc + addi r7, r7, 0x100 /* next exception vector */ + cmplw 0, r7, r8 + blt 2b + + li r7, .L_Alignment - _start + EXC_OFF_SYS_RESET + bl trap_reloc + + li r7, .L_ProgramCheck - _start + EXC_OFF_SYS_RESET + bl trap_reloc + + li r7, .L_FPUnavailable - _start + EXC_OFF_SYS_RESET + li r8, SystemCall - _start + EXC_OFF_SYS_RESET +3: + bl trap_reloc + addi r7, r7, 0x100 /* next exception vector */ + cmplw 0, r7, r8 + blt 3b + + li r7, .L_SingleStep - _start + EXC_OFF_SYS_RESET + li r8, _end_of_vectors - _start + EXC_OFF_SYS_RESET +4: + bl trap_reloc + addi r7, r7, 0x100 /* next exception vector */ + cmplw 0, r7, r8 + blt 4b + + mtlr r4 /* restore link register */ + blr + +#if defined(CONFIG_PATI) +/* Program the PLL */ +pll_prog_code_start: + lis r4, (CONFIG_SYS_IMMR + 0x002fc384)@h + ori r4, r4, (CONFIG_SYS_IMMR + 0x002fc384)@l + lis r3, (0x55ccaa33)@h + ori r3, r3, (0x55ccaa33)@l + stw r3, 0(r4) + lis r4, (CONFIG_SYS_IMMR + 0x002fc284)@h + ori r4, r4, (CONFIG_SYS_IMMR + 0x002fc284)@l + lis r3, CONFIG_SYS_PLPRCR@h + ori r3, r3, CONFIG_SYS_PLPRCR@l + stw r3, 0(r4) + addis r3,0,0x0 + ori r3,r3,0xA000 + mtctr r3 +..spinlp: + bdnz ..spinlp /* spin loop */ + blr +pll_prog_code_end: + nop + blr +#endif diff --git a/arch/powerpc/cpu/mpc5xx/traps.c b/arch/powerpc/cpu/mpc5xx/traps.c new file mode 100644 index 0000000..e3ce11b --- /dev/null +++ b/arch/powerpc/cpu/mpc5xx/traps.c @@ -0,0 +1,227 @@ +/* + * linux/arch/powerpc/kernel/traps.c + * + * Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org) + * + * Modified by Cort Dougan (cort@cs.nmt.edu) + * and Paul Mackerras (paulus@cs.anu.edu.au) + * + * (C) Copyright 2000 + * Wolfgang Denk, DENX Software Engineering, wd@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 + */ + +/* + * This file handles the architecture-dependent parts of hardware exceptions + */ + +#include <common.h> +#include <command.h> +#include <kgdb.h> +#include <asm/processor.h> + +#if defined(CONFIG_CMD_BEDBUG) +extern void do_bedbug_breakpoint(struct pt_regs *); +#endif + +/* Returns 0 if exception not found and fixup otherwise. */ +extern unsigned long search_exception_table(unsigned long); + +/* THIS NEEDS CHANGING to use the board info structure. +*/ +#define END_OF_MEM 0x0001000 + + +/* + * Print stack backtrace + */ +void print_backtrace(unsigned long *sp) +{ + int cnt = 0; + unsigned long i; + + printf("Call backtrace: "); + while (sp) { + if ((uint)sp > END_OF_MEM) + break; + + i = sp[1]; + if (cnt++ % 7 == 0) + printf("\n"); + printf("%08lX ", i); + if (cnt > 32) break; + sp = (unsigned long *)*sp; + } + printf("\n"); +} + +/* + * Print current registers + */ +void show_regs(struct pt_regs * regs) +{ + int i; + printf("NIP: %08lX XER: %08lX LR: %08lX REGS: %p TRAP: %04lx DAR: %08lX\n", + regs->nip, regs->xer, regs->link, regs, regs->trap, regs->dar); + printf("MSR: %08lx EE: %01x PR: %01x FP: %01x ME: %01x IR/DR: %01x%01x\n", + regs->msr, regs->msr&MSR_EE ? 1 : 0, regs->msr&MSR_PR ? 1 : 0, + regs->msr & MSR_FP ? 1 : 0,regs->msr&MSR_ME ? 1 : 0, + regs->msr&MSR_IR ? 1 : 0, + regs->msr&MSR_DR ? 1 : 0); + + printf("\n"); + for (i = 0; i < 32; i++) { + if ((i % 8) == 0) + { + printf("GPR%02d: ", i); + } + + printf("%08lX ", regs->gpr[i]); + if ((i % 8) == 7) + { + printf("\n"); + } + } +} + + +/* + * General exception handler routine + */ +void _exception(int signr, struct pt_regs *regs) +{ + show_regs(regs); + print_backtrace((unsigned long *)regs->gpr[1]); + panic("Exception in kernel pc %lx signal %d",regs->nip,signr); +} + +/* + * Machine check exception handler routine + */ +void MachineCheckException(struct pt_regs *regs) +{ + unsigned long fixup; + + /* Probing PCI using config cycles cause this exception + * when a device is not present. Catch it and return to + * the PCI exception handler. + */ + if ((fixup = search_exception_table(regs->nip)) != 0) { + regs->nip = fixup; + return; + } + +#if defined(CONFIG_CMD_KGDB) + if (debugger_exception_handler && (*debugger_exception_handler)(regs)) + return; +#endif + + printf("Machine check in kernel mode.\n"); + printf("Caused by (from msr): "); + printf("regs %p ",regs); + switch( regs->msr & 0x000F0000) { + case (0x80000000>>12): + printf("Machine check signal\n"); + break; + case (0x80000000>>13): + printf("Transfer error ack signal\n"); + break; + case (0x80000000>>14): + printf("Data parity signal\n"); + break; + case (0x80000000>>15): + printf("Address parity signal\n"); + break; + default: + printf("Unknown values in msr\n"); + } + show_regs(regs); + print_backtrace((unsigned long *)regs->gpr[1]); + panic("machine check"); +} + +/* + * Alignment exception handler routine + */ +void AlignmentException(struct pt_regs *regs) +{ +#if defined(CONFIG_CMD_KGDB) + if (debugger_exception_handler && (*debugger_exception_handler)(regs)) + return; +#endif + show_regs(regs); + print_backtrace((unsigned long *)regs->gpr[1]); + panic("Alignment Exception"); +} + +/* + * Program check exception handler routine + */ +void ProgramCheckException(struct pt_regs *regs) +{ +#if defined(CONFIG_CMD_KGDB) + if (debugger_exception_handler && (*debugger_exception_handler)(regs)) + return; +#endif + show_regs(regs); + print_backtrace((unsigned long *)regs->gpr[1]); + panic("Program Check Exception"); +} + +/* + * Software emulation exception handler routine + */ +void SoftEmuException(struct pt_regs *regs) +{ +#if defined(CONFIG_CMD_KGDB) + if (debugger_exception_handler && (*debugger_exception_handler)(regs)) + return; +#endif + show_regs(regs); + print_backtrace((unsigned long *)regs->gpr[1]); + panic("Software Emulation Exception"); +} + + +/* + * Unknown exception handler routine + */ +void UnknownException(struct pt_regs *regs) +{ +#if defined(CONFIG_CMD_KGDB) + if (debugger_exception_handler && (*debugger_exception_handler)(regs)) + return; +#endif + printf("Bad trap at PC: %lx, SR: %lx, vector=%lx\n", + regs->nip, regs->msr, regs->trap); + _exception(0, regs); +} + +/* + * Debug exception handler routine + */ +void DebugException(struct pt_regs *regs) +{ + printf("Debugger trap at @ %lx\n", regs->nip ); + show_regs(regs); +#if defined(CONFIG_CMD_BEDBUG) + do_bedbug_breakpoint( regs ); +#endif +} diff --git a/arch/powerpc/cpu/mpc5xx/u-boot.lds b/arch/powerpc/cpu/mpc5xx/u-boot.lds new file mode 100644 index 0000000..d5e5dc1 --- /dev/null +++ b/arch/powerpc/cpu/mpc5xx/u-boot.lds @@ -0,0 +1,137 @@ +/* + * (C) Copyright 2001 Wolfgang Denk, DENX Software Engineering, wd@denx.de + * (C) Copyright 2003 Martin Winistoerfer, martinwinistoerfer@gmx.ch + * + * 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 + */ + +OUTPUT_ARCH(powerpc) +/* Do we need any of these for elf? + __DYNAMIC = 0; */ +SECTIONS +{ + /* Read-only sections, merged into text segment: */ + . = + SIZEOF_HEADERS; + .interp : { *(.interp) } + .hash : { *(.hash) } + .dynsym : { *(.dynsym) } + .dynstr : { *(.dynstr) } + .rel.text : { *(.rel.text) } + .rela.text : { *(.rela.text) } + .rel.data : { *(.rel.data) } + .rela.data : { *(.rela.data) } + .rel.rodata : { *(.rel.rodata) } + .rela.rodata : { *(.rela.rodata) } + .rel.got : { *(.rel.got) } + .rela.got : { *(.rela.got) } + .rel.ctors : { *(.rel.ctors) } + .rela.ctors : { *(.rela.ctors) } + .rel.dtors : { *(.rel.dtors) } + .rela.dtors : { *(.rela.dtors) } + .rel.bss : { *(.rel.bss) } + .rela.bss : { *(.rela.bss) } + .rel.plt : { *(.rel.plt) } + .rela.plt : { *(.rela.plt) } + .init : { *(.init) } + .plt : { *(.plt) } + .text : + { + /* WARNING - the following is hand-optimized to fit within */ + /* the sector layout of our flash chips! XXX FIXME XXX */ + + arch/powerpc/cpu/mpc5xx/start.o (.text) + + *(.text) + *(.got1) + } + _etext = .; + PROVIDE (etext = .); + .rodata : + { + *(.eh_frame) + *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.rodata*))) + } + .fini : { *(.fini) } =0 + .ctors : { *(.ctors) } + .dtors : { *(.dtors) } + + /* Read-write section, merged into data segment: */ + . = (. + 0x00FF) & 0xFFFFFF00; + _erotext = .; + PROVIDE (erotext = .); + .reloc : + { + *(.got) + _GOT2_TABLE_ = .; + *(.got2) + _FIXUP_TABLE_ = .; + *(.fixup) + } + __got2_entries = (_FIXUP_TABLE_ - _GOT2_TABLE_) >>2; + __fixup_entries = (. - _FIXUP_TABLE_)>>2; + + .data : + { + *(.data) + *(.data1) + *(.sdata) + *(.sdata2) + *(.dynamic) + CONSTRUCTORS + } + _edata = .; + PROVIDE (edata = .); + + . = .; + __u_boot_cmd_start = .; + .u_boot_cmd : { *(.u_boot_cmd) } + __u_boot_cmd_end = .; + + + . = .; + __start___ex_table = .; + __ex_table : { *(__ex_table) } + __stop___ex_table = .; + + . = ALIGN(256); + __init_begin = .; + .text.init : { *(.text.init) } + .data.init : { *(.data.init) } + . = ALIGN(256); + __init_end = .; + + __bss_start = .; + .bss (NOLOAD) : + { + *(.sbss) *(.scommon) + *(.dynbss) + *(.bss) + *(COMMON) + . = ALIGN(4); + } + + _end = . ; + PROVIDE (end = .); +/* . = env_start; + .ppcenv : + { + common/env_embedded.o (.ppcenv) + } +*/ +} diff --git a/arch/powerpc/cpu/mpc5xxx/Makefile b/arch/powerpc/cpu/mpc5xxx/Makefile new file mode 100644 index 0000000..0ee0611 --- /dev/null +++ b/arch/powerpc/cpu/mpc5xxx/Makefile @@ -0,0 +1,49 @@ +# +# (C) Copyright 2003-2006 +# Wolfgang Denk, DENX Software Engineering, wd@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 $(TOPDIR)/config.mk + +LIB = $(obj)lib$(CPU).a + +START = start.o +SOBJS = io.o firmware_sc_task_bestcomm.impl.o +COBJS = i2c.o traps.o cpu.o cpu_init.o ide.o interrupts.o \ + loadtask.o pci_mpc5200.o serial.o speed.o usb_ohci.o usb.o + +SRCS := $(START:.o=.S) $(SOBJS:.o=.S) $(COBJS:.o=.c) +OBJS := $(addprefix $(obj),$(SOBJS) $(COBJS)) +START := $(addprefix $(obj),$(START)) + +all: $(obj).depend $(START) $(LIB) + +$(LIB): $(OBJS) + $(AR) $(ARFLAGS) $@ $(OBJS) + +######################################################################### + +# defines $(obj).depend target +include $(SRCTREE)/rules.mk + +sinclude $(obj).depend + +######################################################################### diff --git a/arch/powerpc/cpu/mpc5xxx/config.mk b/arch/powerpc/cpu/mpc5xxx/config.mk new file mode 100644 index 0000000..7ef8a47 --- /dev/null +++ b/arch/powerpc/cpu/mpc5xxx/config.mk @@ -0,0 +1,30 @@ +# +# (C) Copyright 2003 +# Wolfgang Denk, DENX Software Engineering, wd@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 +# + +PLATFORM_RELFLAGS += -fPIC -meabi + +PLATFORM_CPPFLAGS += -DCONFIG_MPC5xxx -ffixed-r2 \ + -mstring -mcpu=603e -mmultiple + +# Use default linker script. Board port can override in board/*/config.mk +LDSCRIPT := $(SRCTREE)/arch/powerpc/cpu/mpc5xxx/u-boot.lds diff --git a/arch/powerpc/cpu/mpc5xxx/cpu.c b/arch/powerpc/cpu/mpc5xxx/cpu.c new file mode 100644 index 0000000..b20234d --- /dev/null +++ b/arch/powerpc/cpu/mpc5xxx/cpu.c @@ -0,0 +1,205 @@ +/* + * (C) Copyright 2000-2010 + * Wolfgang Denk, DENX Software Engineering, wd@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 + */ + +/* + * CPU specific code for the MPC5xxx CPUs + */ + +#include <common.h> +#include <watchdog.h> +#include <command.h> +#include <net.h> +#include <mpc5xxx.h> +#include <netdev.h> +#include <asm/io.h> +#include <asm/processor.h> + +#if defined(CONFIG_OF_LIBFDT) +#include <libfdt.h> +#include <libfdt_env.h> +#include <fdt_support.h> +#endif + +#if defined(CONFIG_OF_IDE_FIXUP) +#include <ide.h> +#endif + +DECLARE_GLOBAL_DATA_PTR; + +int checkcpu (void) +{ + ulong clock = gd->cpu_clk; + char buf[32]; + uint svr, pvr; + + puts ("CPU: "); + + svr = get_svr(); + pvr = get_pvr(); + + switch (pvr) { + case PVR_5200: + printf("MPC5200"); + break; + case PVR_5200B: + printf("MPC5200B"); + break; + default: + printf("Unknown MPC5xxx"); + break; + } + + printf (" v%d.%d, Core v%d.%d", SVR_MJREV (svr), SVR_MNREV (svr), + PVR_MAJ(pvr), PVR_MIN(pvr)); + printf (" at %s MHz\n", strmhz (buf, clock)); + return 0; +} + +/* ------------------------------------------------------------------------- */ + +int +do_reset (cmd_tbl_t * cmdtp, int flag, int argc, char *argv[]) +{ + ulong msr; + /* Interrupts and MMU off */ + __asm__ __volatile__ ("mfmsr %0":"=r" (msr):); + + msr &= ~(MSR_ME | MSR_EE | MSR_IR | MSR_DR); + __asm__ __volatile__ ("mtmsr %0"::"r" (msr)); + + /* Charge the watchdog timer */ + *(vu_long *)(MPC5XXX_GPT0_COUNTER) = 0x0001000f; + *(vu_long *)(MPC5XXX_GPT0_ENABLE) = 0x9004; /* wden|ce|timer_ms */ + while(1); + + return 1; + +} + +/* ------------------------------------------------------------------------- */ + +/* + * Get timebase clock frequency (like cpu_clk in Hz) + * + */ +unsigned long get_tbclk (void) +{ + ulong tbclk; + + tbclk = (gd->bus_clk + 3L) / 4L; + + return (tbclk); +} + +/* ------------------------------------------------------------------------- */ + +#if defined(CONFIG_OF_LIBFDT) && defined (CONFIG_OF_BOARD_SETUP) +void ft_cpu_setup(void *blob, bd_t *bd) +{ + int div = in_8((void*)CONFIG_SYS_MBAR + 0x204) & 0x0020 ? 8 : 4; + char * cpu_path = "/cpus/" OF_CPU; +#ifdef CONFIG_MPC5xxx_FEC + uchar enetaddr[6]; + char * eth_path = "/" OF_SOC "/ethernet@3000"; +#endif + + do_fixup_by_path_u32(blob, cpu_path, "timebase-frequency", OF_TBCLK, 1); + do_fixup_by_path_u32(blob, cpu_path, "bus-frequency", bd->bi_busfreq, 1); + do_fixup_by_path_u32(blob, cpu_path, "clock-frequency", bd->bi_intfreq, 1); + do_fixup_by_path_u32(blob, "/" OF_SOC, "bus-frequency", bd->bi_ipbfreq, 1); + do_fixup_by_path_u32(blob, "/" OF_SOC, "system-frequency", + bd->bi_busfreq*div, 1); +#ifdef CONFIG_MPC5xxx_FEC + eth_getenv_enetaddr("ethaddr", enetaddr); + do_fixup_by_path(blob, eth_path, "mac-address", enetaddr, 6, 0); + do_fixup_by_path(blob, eth_path, "local-mac-address", enetaddr, 6, 0); +#endif +#if defined(CONFIG_OF_IDE_FIXUP) + if (!ide_device_present(0)) { + /* NO CF card detected -> delete ata node in DTS */ + int nodeoffset = 0; + char nodename[] = "/soc5200@f0000000/ata@3a00"; + + nodeoffset = fdt_path_offset(blob, nodename); + if (nodeoffset >= 0) { + fdt_del_node(blob, nodeoffset); + } else { + printf("%s: cannot find %s node err:%s\n", + __func__, nodename, fdt_strerror(nodeoffset)); + } + } + +#endif + fdt_fixup_memory(blob, (u64)bd->bi_memstart, (u64)bd->bi_memsize); +} +#endif + +#ifdef CONFIG_BOOTCOUNT_LIMIT + +void bootcount_store (ulong a) +{ + volatile ulong *save_addr = (volatile ulong *) (MPC5XXX_CDM_BRDCRMB); + + *save_addr = (BOOTCOUNT_MAGIC & 0xffff0000) | a; +} + +ulong bootcount_load (void) +{ + volatile ulong *save_addr = (volatile ulong *) (MPC5XXX_CDM_BRDCRMB); + + if ((*save_addr & 0xffff0000) != (BOOTCOUNT_MAGIC & 0xffff0000)) + return 0; + else + return (*save_addr & 0x0000ffff); +} +#endif /* CONFIG_BOOTCOUNT_LIMIT */ + +#ifdef CONFIG_MPC5xxx_FEC +/* Default initializations for FEC controllers. To override, + * create a board-specific function called: + * int board_eth_init(bd_t *bis) + */ + +int cpu_eth_init(bd_t *bis) +{ + return mpc5xxx_fec_initialize(bis); +} +#endif + +#if defined(CONFIG_WATCHDOG) +void watchdog_reset(void) +{ + int re_enable = disable_interrupts(); + reset_5xxx_watchdog(); + if (re_enable) enable_interrupts(); +} + +void reset_5xxx_watchdog(void) +{ + volatile struct mpc5xxx_gpt *gpt0 = + (struct mpc5xxx_gpt *) MPC5XXX_GPT; + + /* Trigger TIMER_0 by writing A5 to OCPW */ + clrsetbits_be32(&gpt0->emsr, 0xff000000, 0xa5000000); +} +#endif /* CONFIG_WATCHDOG */ diff --git a/arch/powerpc/cpu/mpc5xxx/cpu_init.c b/arch/powerpc/cpu/mpc5xxx/cpu_init.c new file mode 100644 index 0000000..9daf375 --- /dev/null +++ b/arch/powerpc/cpu/mpc5xxx/cpu_init.c @@ -0,0 +1,233 @@ +/* + * (C) Copyright 2000-2010 + * Wolfgang Denk, DENX Software Engineering, wd@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> +#include <mpc5xxx.h> +#include <asm/io.h> +#include <watchdog.h> + +DECLARE_GLOBAL_DATA_PTR; + +/* + * Breath some life into the CPU... + * + * Set up the memory map, + * initialize a bunch of registers. + */ +void cpu_init_f (void) +{ + volatile struct mpc5xxx_mmap_ctl *mm = + (struct mpc5xxx_mmap_ctl *) CONFIG_SYS_MBAR; + volatile struct mpc5xxx_lpb *lpb = + (struct mpc5xxx_lpb *) MPC5XXX_LPB; + volatile struct mpc5xxx_gpio *gpio = + (struct mpc5xxx_gpio *) MPC5XXX_GPIO; + volatile struct mpc5xxx_xlb *xlb = + (struct mpc5xxx_xlb *) MPC5XXX_XLBARB; +#if defined(CONFIG_SYS_IPBCLK_EQUALS_XLBCLK) + volatile struct mpc5xxx_cdm *cdm = + (struct mpc5xxx_cdm *) MPC5XXX_CDM; +#endif /* CONFIG_SYS_IPBCLK_EQUALS_XLBCLK */ +#if defined(CONFIG_WATCHDOG) + volatile struct mpc5xxx_gpt *gpt0 = + (struct mpc5xxx_gpt *) MPC5XXX_GPT; +#endif /* CONFIG_WATCHDOG */ + unsigned long addecr = (1 << 25); /* Boot_CS */ + /* Pointer is writable since we allocated a register for it */ + gd = (gd_t *) (CONFIG_SYS_INIT_RAM_ADDR + CONFIG_SYS_GBL_DATA_OFFSET); + + /* Clear initial global data */ + memset ((void *) gd, 0, sizeof (gd_t)); + + /* + * Memory Controller: configure chip selects and enable them + */ +#if defined(CONFIG_SYS_BOOTCS_START) && defined(CONFIG_SYS_BOOTCS_SIZE) + out_be32(&mm->boot_start, START_REG(CONFIG_SYS_BOOTCS_START)); + out_be32(&mm->boot_stop, STOP_REG(CONFIG_SYS_BOOTCS_START, + CONFIG_SYS_BOOTCS_SIZE)); +#endif +#if defined(CONFIG_SYS_BOOTCS_CFG) + out_be32(&lpb->cs0_cfg, CONFIG_SYS_BOOTCS_CFG); +#endif + +#if defined(CONFIG_SYS_CS0_START) && defined(CONFIG_SYS_CS0_SIZE) + out_be32(&mm->cs0_start, START_REG(CONFIG_SYS_CS0_START)); + out_be32(&mm->cs0_stop, STOP_REG(CONFIG_SYS_CS0_START, + CONFIG_SYS_CS0_SIZE)); + /* CS0 and BOOT_CS cannot be enabled at once. */ + /* addecr |= (1 << 16); */ +#endif +#if defined(CONFIG_SYS_CS0_CFG) + out_be32(&lpb->cs0_cfg, CONFIG_SYS_CS0_CFG); +#endif + +#if defined(CONFIG_SYS_CS1_START) && defined(CONFIG_SYS_CS1_SIZE) + out_be32(&mm->cs1_start, START_REG(CONFIG_SYS_CS1_START)); + out_be32(&mm->cs1_stop, STOP_REG(CONFIG_SYS_CS1_START, + CONFIG_SYS_CS1_SIZE)); + addecr |= (1 << 17); +#endif +#if defined(CONFIG_SYS_CS1_CFG) + out_be32(&lpb->cs1_cfg, CONFIG_SYS_CS1_CFG); +#endif + +#if defined(CONFIG_SYS_CS2_START) && defined(CONFIG_SYS_CS2_SIZE) + out_be32(&mm->cs2_start, START_REG(CONFIG_SYS_CS2_START)); + out_be32(&mm->cs2_stop, STOP_REG(CONFIG_SYS_CS2_START, + CONFIG_SYS_CS2_SIZE)); + addecr |= (1 << 18); +#endif +#if defined(CONFIG_SYS_CS2_CFG) + out_be32(&lpb->cs2_cfg, CONFIG_SYS_CS2_CFG); +#endif + +#if defined(CONFIG_SYS_CS3_START) && defined(CONFIG_SYS_CS3_SIZE) + out_be32(&mm->cs3_start, START_REG(CONFIG_SYS_CS3_START)); + out_be32(&mm->cs3_stop, STOP_REG(CONFIG_SYS_CS3_START, + CONFIG_SYS_CS3_SIZE)); + addecr |= (1 << 19); +#endif +#if defined(CONFIG_SYS_CS3_CFG) + out_be32(&lpb->cs3_cfg, CONFIG_SYS_CS3_CFG); +#endif + +#if defined(CONFIG_SYS_CS4_START) && defined(CONFIG_SYS_CS4_SIZE) + out_be32(&mm->cs4_start, START_REG(CONFIG_SYS_CS4_START)); + out_be32(&mm->cs4_stop, STOP_REG(CONFIG_SYS_CS4_START, + CONFIG_SYS_CS4_SIZE)); + addecr |= (1 << 20); +#endif +#if defined(CONFIG_SYS_CS4_CFG) + out_be32(&lpb->cs4_cfg, CONFIG_SYS_CS4_CFG); +#endif + +#if defined(CONFIG_SYS_CS5_START) && defined(CONFIG_SYS_CS5_SIZE) + out_be32(&mm->cs5_start, START_REG(CONFIG_SYS_CS5_START)); + out_be32(&mm->cs5_stop, STOP_REG(CONFIG_SYS_CS5_START, + CONFIG_SYS_CS5_SIZE)); + addecr |= (1 << 21); +#endif +#if defined(CONFIG_SYS_CS5_CFG) + out_be32(&lpb->cs5_cfg, CONFIG_SYS_CS5_CFG); +#endif + + addecr |= 1; +#if defined(CONFIG_SYS_CS6_START) && defined(CONFIG_SYS_CS6_SIZE) + out_be32(&mm->cs6_start, START_REG(CONFIG_SYS_CS6_START)); + out_be32(&mm->cs6_stop, STOP_REG(CONFIG_SYS_CS6_START, + CONFIG_SYS_CS6_SIZE)); + addecr |= (1 << 26); +#endif +#if defined(CONFIG_SYS_CS6_CFG) + out_be32(&lpb->cs6_cfg, CONFIG_SYS_CS6_CFG); +#endif + +#if defined(CONFIG_SYS_CS7_START) && defined(CONFIG_SYS_CS7_SIZE) + out_be32(&mm->cs7_start, START_REG(CONFIG_SYS_CS7_START)); + out_be32(&mm->cs7_stop, STOP_REG(CONFIG_SYS_CS7_START, + CONFIG_SYS_CS7_SIZE)); + addecr |= (1 << 27); +#endif +#if defined(CONFIG_SYS_CS7_CFG) + out_be32(&lpb->cs7_cfg, CONFIG_SYS_CS7_CFG); +#endif + +#if defined(CONFIG_SYS_CS_BURST) + out_be32(&lpb->cs_burst, CONFIG_SYS_CS_BURST); +#endif +#if defined(CONFIG_SYS_CS_DEADCYCLE) + out_be32(&lpb->cs_deadcycle, CONFIG_SYS_CS_DEADCYCLE); +#endif + + /* Enable chip selects */ + out_be32(&mm->ipbi_ws_ctrl, addecr); + out_be32(&lpb->cs_ctrl, (1 << 24)); + + /* Setup pin multiplexing */ +#if defined(CONFIG_SYS_GPS_PORT_CONFIG) + out_be32(&gpio->port_config, CONFIG_SYS_GPS_PORT_CONFIG); +#endif + + /* enable timebase */ + setbits_be32(&xlb->config, (1 << 13)); + + /* Enable snooping for RAM */ + setbits_be32(&xlb->config, (1 << 15)); + out_be32(&xlb->snoop_window, CONFIG_SYS_SDRAM_BASE | 0x1d); + +#if defined(CONFIG_SYS_IPBCLK_EQUALS_XLBCLK) + /* Motorola reports IPB should better run at 133 MHz. */ + setbits_be32(&mm->ipbi_ws_ctrl, 1); + /* pci_clk_sel = 0x02, ipb_clk_sel = 0x00; */ + addecr = in_be32(&cdm->cfg); + addecr &= ~0x103; +# if defined(CONFIG_SYS_PCICLK_EQUALS_IPBCLK_DIV2) + /* pci_clk_sel = 0x01 -> IPB_CLK/2 */ + addecr |= 0x01; +# else + /* pci_clk_sel = 0x02 -> XLB_CLK/4 = IPB_CLK/4 */ + addecr |= 0x02; +# endif /* CONFIG_SYS_PCICLK_EQUALS_IPBCLK_DIV2 */ + out_be32(&cdm->cfg, addecr); +#endif /* CONFIG_SYS_IPBCLK_EQUALS_XLBCLK */ + /* Configure the XLB Arbiter */ + out_be32(&xlb->master_pri_enable, 0xff); + out_be32(&xlb->master_priority, 0x11111111); + +#if defined(CONFIG_SYS_XLB_PIPELINING) + /* Enable piplining */ + clrbits_be32(&xlb->config, (1 << 31)); +#endif + +#if defined(CONFIG_WATCHDOG) + /* Charge the watchdog timer - prescaler = 64k, count = 64k*/ + out_be32(&gpt0->cir, 0x0000ffff); + out_be32(&gpt0->emsr, 0x9004); /* wden|ce|timer_ms */ + + reset_5xxx_watchdog(); +#endif /* CONFIG_WATCHDOG */ +} + +/* + * initialize higher level parts of CPU like time base and timers + */ +int cpu_init_r (void) +{ + volatile struct mpc5xxx_intr *intr = + (struct mpc5xxx_intr *) MPC5XXX_ICTL; + + /* mask all interrupts */ + out_be32(&intr->per_mask, 0xffffff00); + setbits_be32(&intr->main_mask, 0x0001ffff); + clrbits_be32(&intr->ctrl, 0x00000f00); + /* route critical ints to normal ints */ + setbits_be32(&intr->ctrl, 0x00000001); + +#if defined(CONFIG_CMD_NET) && defined(CONFIG_MPC5xxx_FEC) + /* load FEC microcode */ + loadtask(0, 2); +#endif + + return (0); +} diff --git a/arch/powerpc/cpu/mpc5xxx/firmware_sc_task_bestcomm.impl.S b/arch/powerpc/cpu/mpc5xxx/firmware_sc_task_bestcomm.impl.S new file mode 100644 index 0000000..00c2312 --- /dev/null +++ b/arch/powerpc/cpu/mpc5xxx/firmware_sc_task_bestcomm.impl.S @@ -0,0 +1,359 @@ +/* + * Copyright (C) 2001, Software Center, Motorola China. + * + * This file contains microcode for the FEC controller of the MPC5200 CPU. + */ + +#include <config.h> + +/* sas/sccg, gas target */ +.section smartdmaInitData,"aw",@progbits /* Initialized data for task variables */ +.section smartdmaTaskTable,"aw",@progbits /* Task tables */ +.align 9 +.globl taskTable +taskTable: +.globl scEthernetRecv_Entry +scEthernetRecv_Entry: /* Task 0 */ +.long scEthernetRecv_TDT - taskTable /* Task 0 Descriptor Table */ +.long scEthernetRecv_TDT - taskTable + 0x000000a4 +.long scEthernetRecv_VarTab - taskTable /* Task 0 Variable Table */ +.long scEthernetRecv_FDT - taskTable + 0x03 /* Task 0 Function Descriptor Table & Flags */ +.long 0x00000000 +.long 0x00000000 +.long scEthernetRecv_CSave - taskTable /* Task 0 context save space */ +.long CONFIG_SYS_MBAR +.globl scEthernetXmit_Entry +scEthernetXmit_Entry: /* Task 1 */ +.long scEthernetXmit_TDT - taskTable /* Task 1 Descriptor Table */ +.long scEthernetXmit_TDT - taskTable + 0x000000d0 +.long scEthernetXmit_VarTab - taskTable /* Task 1 Variable Table */ +.long scEthernetXmit_FDT - taskTable + 0x03 /* Task 1 Function Descriptor Table & Flags */ +.long 0x00000000 +.long 0x00000000 +.long scEthernetXmit_CSave - taskTable /* Task 1 context save space */ +.long CONFIG_SYS_MBAR + + +.globl scEthernetRecv_TDT +scEthernetRecv_TDT: /* Task 0 Descriptor Table */ +.long 0xc4c50000 /* 0000: LCDEXT: idx0 = var9 + var10; idx0 once var0; idx0 += inc0 */ +.long 0x84c5e000 /* 0004: LCD: idx1 = var9 + var11; ; idx1 += inc0 */ +.long 0x10001f08 /* 0008: DRD1A: var7 = idx1; FN=0 MORE init=0 WS=0 RS=0 */ +.long 0x10000380 /* 000C: DRD1A: var0 = *idx0; FN=0 MORE init=0 WS=0 RS=0 */ +.long 0x00000f88 /* 0010: DRD1A: var3 = *idx1; FN=0 init=0 WS=0 RS=0 */ +.long 0x81980000 /* 0014: LCD: idx0 = var3; idx0 once var0; idx0 += inc0 */ +.long 0x10000780 /* 0018: DRD1A: var1 = *idx0; FN=0 MORE init=0 WS=0 RS=0 */ +.long 0x60000000 /* 001C: DRD2A: EU0=0 EU1=0 EU2=0 EU3=0 EXT init=0 WS=0 RS=0 */ +.long 0x010cf04c /* 0020: DRD2B1: var4 = EU3(); EU3(var1,var12) */ +.long 0x82180349 /* 0024: LCD: idx0 = var4; idx0 != var13; idx0 += inc1 */ +.long 0x81c68004 /* 0028: LCD: idx1 = var3 + var13 + 4; idx1 once var0; idx1 += inc0 */ +.long 0x70000000 /* 002C: DRD2A: EU0=0 EU1=0 EU2=0 EU3=0 EXT MORE init=0 WS=0 RS=0 */ +.long 0x018cf04e /* 0030: DRD2B1: var6 = EU3(); EU3(var1,var14) */ +.long 0x70000000 /* 0034: DRD2A: EU0=0 EU1=0 EU2=0 EU3=0 EXT MORE init=0 WS=0 RS=0 */ +.long 0x020cf04f /* 0038: DRD2B1: var8 = EU3(); EU3(var1,var15) */ +.long 0x00000b88 /* 003C: DRD1A: var2 = *idx1; FN=0 init=0 WS=0 RS=0 */ +.long 0x8000d184 /* 0040: LCDEXT: idx1 = 0xf0003184; ; */ +.long 0xc6990452 /* 0044: LCDEXT: idx2 = var13; idx2 < var17; idx2 += inc2 */ +.long 0x81486010 /* 0048: LCD: idx3 = var2 + var16; ; idx3 += inc2 */ +.long 0x006acf88 /* 004C: DRD1A: *idx3 = *idx1; FN=0 init=3 WS=1 RS=1 */ +.long 0x8000d184 /* 0050: LCDEXT: idx1 = 0xf0003184; ; */ +.long 0x86810492 /* 0054: LCD: idx2 = var13, idx3 = var2; idx2 < var18; idx2 += inc2, idx3 += inc2 */ +.long 0x006acf88 /* 0058: DRD1A: *idx3 = *idx1; FN=0 init=3 WS=1 RS=1 */ +.long 0x8000d184 /* 005C: LCDEXT: idx1 = 0xf0003184; ; */ +.long 0x868184d2 /* 0060: LCD: idx2 = var13, idx3 = var3; idx2 < var19; idx2 += inc2, idx3 += inc2 */ +.long 0x000acf88 /* 0064: DRD1A: *idx3 = *idx1; FN=0 init=0 WS=1 RS=1 */ +.long 0xc318839b /* 0068: LCDEXT: idx1 = var6; idx1 == var14; idx1 += inc3 */ +.long 0x80190000 /* 006C: LCD: idx2 = var0; idx2 once var0; idx2 += inc0 */ +.long 0x04008468 /* 0070: DRD1A: idx1 = var13; FN=0 INT init=0 WS=0 RS=0 */ +.long 0xc4038358 /* 0074: LCDEXT: idx1 = var8, idx2 = var7; idx1 == var13; idx1 += inc3, idx2 += inc0 */ +.long 0x81c50000 /* 0078: LCD: idx3 = var3 + var10; idx3 once var0; idx3 += inc0 */ +.long 0x1000cb18 /* 007C: DRD1A: *idx2 = idx3; FN=0 MORE init=0 WS=0 RS=0 */ +.long 0x00000f18 /* 0080: DRD1A: var3 = idx3; FN=0 init=0 WS=0 RS=0 */ +.long 0xc4188364 /* 0084: LCDEXT: idx1 = var8; idx1 > var13; idx1 += inc4 */ +.long 0x83990000 /* 0088: LCD: idx2 = var7; idx2 once var0; idx2 += inc0 */ +.long 0x10000c00 /* 008C: DRD1A: var3 = var0; FN=0 MORE init=0 WS=0 RS=0 */ +.long 0x0000c800 /* 0090: DRD1A: *idx2 = var0; FN=0 init=0 WS=0 RS=0 */ +.long 0x81988000 /* 0094: LCD: idx1 = var3; idx1 once var0; idx1 += inc0 */ +.long 0x10000788 /* 0098: DRD1A: var1 = *idx1; FN=0 MORE init=0 WS=0 RS=0 */ +.long 0x60000000 /* 009C: DRD2A: EU0=0 EU1=0 EU2=0 EU3=0 EXT init=0 WS=0 RS=0 */ +.long 0x080cf04c /* 00A0: DRD2B1: idx0 = EU3(); EU3(var1,var12) */ +.long 0x000001f8 /* 00A4(:0): NOP */ + + +.globl scEthernetXmit_TDT +scEthernetXmit_TDT: /* Task 1 Descriptor Table */ +.long 0x80024800 /* 0000: LCDEXT: idx0 = 0xf0008800; ; */ +.long 0x85c60004 /* 0004: LCD: idx1 = var11 + var12 + 4; idx1 once var0; idx1 += inc0 */ +.long 0x10002308 /* 0008: DRD1A: var8 = idx1; FN=0 MORE init=0 WS=0 RS=0 */ +.long 0x10000f88 /* 000C: DRD1A: var3 = *idx1; FN=0 MORE init=0 WS=0 RS=0 */ +.long 0x00000380 /* 0010: DRD1A: var0 = *idx0; FN=0 init=0 WS=0 RS=0 */ +.long 0x81980000 /* 0014: LCD: idx0 = var3; idx0 once var0; idx0 += inc0 */ +.long 0x10000780 /* 0018: DRD1A: var1 = *idx0; FN=0 MORE init=0 WS=0 RS=0 */ +.long 0x60000000 /* 001C: DRD2A: EU0=0 EU1=0 EU2=0 EU3=0 EXT init=0 WS=0 RS=0 */ +.long 0x024cf04d /* 0020: DRD2B1: var9 = EU3(); EU3(var1,var13) */ +.long 0x84980309 /* 0024: LCD: idx0 = var9; idx0 != var12; idx0 += inc1 */ +.long 0xc0004003 /* 0028: LCDEXT: idx1 = 0x00000003; ; */ +.long 0x81c60004 /* 002C: LCD: idx2 = var3 + var12 + 4; idx2 once var0; idx2 += inc0 */ +.long 0x70000000 /* 0030: DRD2A: EU0=0 EU1=0 EU2=0 EU3=0 EXT MORE init=0 WS=0 RS=0 */ +.long 0x010cf04e /* 0034: DRD2B1: var4 = EU3(); EU3(var1,var14) */ +.long 0x70000000 /* 0038: DRD2A: EU0=0 EU1=0 EU2=0 EU3=0 EXT MORE init=0 WS=0 RS=0 */ +.long 0x014cf04f /* 003C: DRD2B1: var5 = EU3(); EU3(var1,var15) */ +.long 0x70000000 /* 0040: DRD2A: EU0=0 EU1=0 EU2=0 EU3=0 EXT MORE init=0 WS=0 RS=0 */ +.long 0x028cf050 /* 0044: DRD2B1: var10 = EU3(); EU3(var1,var16) */ +.long 0x70000000 /* 0048: DRD2A: EU0=0 EU1=0 EU2=0 EU3=0 EXT MORE init=0 WS=0 RS=0 */ +.long 0x018cf051 /* 004C: DRD2B1: var6 = EU3(); EU3(var1,var17) */ +.long 0x10000b90 /* 0050: DRD1A: var2 = *idx2; FN=0 MORE init=0 WS=0 RS=0 */ +.long 0x60000000 /* 0054: DRD2A: EU0=0 EU1=0 EU2=0 EU3=0 EXT init=0 WS=0 RS=0 */ +.long 0x01ccf0a1 /* 0058: DRD2B1: var7 = EU3(); EU3(var2,idx1) */ +.long 0xc2988312 /* 005C: LCDEXT: idx1 = var5; idx1 > var12; idx1 += inc2 */ +.long 0x83490000 /* 0060: LCD: idx2 = var6 + var18; idx2 once var0; idx2 += inc0 */ +.long 0x00001b10 /* 0064: DRD1A: var6 = idx2; FN=0 init=0 WS=0 RS=0 */ +.long 0x8000d1a4 /* 0068: LCDEXT: idx1 = 0xf00031a4; ; */ +.long 0x8301031c /* 006C: LCD: idx2 = var6, idx3 = var2; idx2 > var12; idx2 += inc3, idx3 += inc4 */ +.long 0x008ac798 /* 0070: DRD1A: *idx1 = *idx3; FN=0 init=4 WS=1 RS=1 */ +.long 0x8000d1a4 /* 0074: LCDEXT: idx1 = 0xf00031a4; ; */ +.long 0xc1430000 /* 0078: LCDEXT: idx2 = var2 + var6; idx2 once var0; idx2 += inc0 */ +.long 0x82998312 /* 007C: LCD: idx3 = var5; idx3 > var12; idx3 += inc2 */ +.long 0x088ac790 /* 0080: DRD1A: *idx1 = *idx2; FN=0 TFD init=4 WS=1 RS=1 */ +.long 0x81988000 /* 0084: LCD: idx1 = var3; idx1 once var0; idx1 += inc0 */ +.long 0x60000001 /* 0088: DRD2A: EU0=0 EU1=0 EU2=0 EU3=1 EXT init=0 WS=0 RS=0 */ +.long 0x0c4cfc4d /* 008C: DRD2B1: *idx1 = EU3(); EU3(*idx1,var13) */ +.long 0xc21883ad /* 0090: LCDEXT: idx1 = var4; idx1 == var14; idx1 += inc5 */ +.long 0x80190000 /* 0094: LCD: idx2 = var0; idx2 once var0; idx2 += inc0 */ +.long 0x04008460 /* 0098: DRD1A: idx1 = var12; FN=0 INT init=0 WS=0 RS=0 */ +.long 0xc4052305 /* 009C: LCDEXT: idx1 = var8, idx2 = var10; idx2 == var12; idx1 += inc0, idx2 += inc5 */ +.long 0x81c98000 /* 00A0: LCD: idx3 = var3 + var19; idx3 once var0; idx3 += inc0 */ +.long 0x1000c718 /* 00A4: DRD1A: *idx1 = idx3; FN=0 MORE init=0 WS=0 RS=0 */ +.long 0x00000f18 /* 00A8: DRD1A: var3 = idx3; FN=0 init=0 WS=0 RS=0 */ +.long 0xc4188000 /* 00AC: LCDEXT: idx1 = var8; idx1 once var0; idx1 += inc0 */ +.long 0x85190312 /* 00B0: LCD: idx2 = var10; idx2 > var12; idx2 += inc2 */ +.long 0x10000c00 /* 00B4: DRD1A: var3 = var0; FN=0 MORE init=0 WS=0 RS=0 */ +.long 0x1000c400 /* 00B8: DRD1A: *idx1 = var0; FN=0 MORE init=0 WS=0 RS=0 */ +.long 0x00008860 /* 00BC: DRD1A: idx2 = var12; FN=0 init=0 WS=0 RS=0 */ +.long 0x81988000 /* 00C0: LCD: idx1 = var3; idx1 once var0; idx1 += inc0 */ +.long 0x10000788 /* 00C4: DRD1A: var1 = *idx1; FN=0 MORE init=0 WS=0 RS=0 */ +.long 0x60000000 /* 00C8: DRD2A: EU0=0 EU1=0 EU2=0 EU3=0 EXT init=0 WS=0 RS=0 */ +.long 0x080cf04d /* 00CC: DRD2B1: idx0 = EU3(); EU3(var1,var13) */ +.long 0x000001f8 /* 00D0(:0): NOP */ + +.align 8 + +.globl scEthernetRecv_VarTab +scEthernetRecv_VarTab: /* Task 0 Variable Table */ +.long 0x00000000 /* var[0] */ +.long 0x00000000 /* var[1] */ +.long 0x00000000 /* var[2] */ +.long 0x00000000 /* var[3] */ +.long 0x00000000 /* var[4] */ +.long 0x00000000 /* var[5] */ +.long 0x00000000 /* var[6] */ +.long 0x00000000 /* var[7] */ +.long 0x00000000 /* var[8] */ +.long (CONFIG_SYS_MBAR + 0x8800) /* var[9] */ +.long 0x00000008 /* var[10] */ +.long 0x0000000c /* var[11] */ +.long 0x80000000 /* var[12] */ +.long 0x00000000 /* var[13] */ +.long 0x10000000 /* var[14] */ +.long 0x20000000 /* var[15] */ +.long 0x000005e4 /* var[16] */ +.long 0x0000000e /* var[17] */ +.long 0x000005e0 /* var[18] */ +.long 0x00000004 /* var[19] */ +.long 0x00000000 /* var[20] */ +.long 0x00000000 /* var[21] */ +.long 0x00000000 /* var[22] */ +.long 0x00000000 /* var[23] */ +.long 0x00000000 /* inc[0] */ +.long 0x60000000 /* inc[1] */ +.long 0x20000001 /* inc[2] */ +.long 0x80000000 /* inc[3] */ +.long 0x40000000 /* inc[4] */ +.long 0x00000000 /* inc[5] */ +.long 0x00000000 /* inc[6] */ +.long 0x00000000 /* inc[7] */ + +.align 8 + +.globl scEthernetXmit_VarTab +scEthernetXmit_VarTab: /* Task 1 Variable Table */ +.long 0x00000000 /* var[0] */ +.long 0x00000000 /* var[1] */ +.long 0x00000000 /* var[2] */ +.long 0x00000000 /* var[3] */ +.long 0x00000000 /* var[4] */ +.long 0x00000000 /* var[5] */ +.long 0x00000000 /* var[6] */ +.long 0x00000000 /* var[7] */ +.long 0x00000000 /* var[8] */ +.long 0x00000000 /* var[9] */ +.long 0x00000000 /* var[10] */ +.long (CONFIG_SYS_MBAR + 0x8800) /* var[11] */ +.long 0x00000000 /* var[12] */ +.long 0x80000000 /* var[13] */ +.long 0x10000000 /* var[14] */ +.long 0x08000000 /* var[15] */ +.long 0x20000000 /* var[16] */ +.long 0x0000ffff /* var[17] */ +.long 0xffffffff /* var[18] */ +.long 0x00000008 /* var[19] */ +.long 0x00000000 /* var[20] */ +.long 0x00000000 /* var[21] */ +.long 0x00000000 /* var[22] */ +.long 0x00000000 /* var[23] */ +.long 0x00000000 /* inc[0] */ +.long 0x60000000 /* inc[1] */ +.long 0x40000000 /* inc[2] */ +.long 0x4000ffff /* inc[3] */ +.long 0xe0000001 /* inc[4] */ +.long 0x80000000 /* inc[5] */ +.long 0x00000000 /* inc[6] */ +.long 0x00000000 /* inc[7] */ + +.align 8 + +.globl scEthernetRecv_FDT +scEthernetRecv_FDT: /* Task 0 Function Descriptor Table */ +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x21800000 /* and(), EU# 3 */ +.long 0x21400000 /* andn(), EU# 3 */ +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 + +.align 8 + +.globl scEthernetXmit_FDT +scEthernetXmit_FDT: /* Task 1 Function Descriptor Table */ +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x21800000 /* and(), EU# 3 */ +.long 0x21400000 /* andn(), EU# 3 */ +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 + + +.globl scEthernetRecv_CSave +scEthernetRecv_CSave: /* Task 0 context save space */ +.space 128, 0x0 + + +.globl scEthernetXmit_CSave +scEthernetXmit_CSave: /* Task 1 context save space */ +.space 128, 0x0 diff --git a/arch/powerpc/cpu/mpc5xxx/i2c.c b/arch/powerpc/cpu/mpc5xxx/i2c.c new file mode 100644 index 0000000..4f7f716 --- /dev/null +++ b/arch/powerpc/cpu/mpc5xxx/i2c.c @@ -0,0 +1,442 @@ +/* + * (C) Copyright 2003 + * Wolfgang Denk, DENX Software Engineering, wd@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> + +DECLARE_GLOBAL_DATA_PTR; + +#ifdef CONFIG_HARD_I2C + +#include <mpc5xxx.h> +#include <i2c.h> + +#if (CONFIG_SYS_I2C_MODULE == 2) +#define I2C_BASE MPC5XXX_I2C2 +#elif (CONFIG_SYS_I2C_MODULE == 1) +#define I2C_BASE MPC5XXX_I2C1 +#else +#error CONFIG_SYS_I2C_MODULE is not properly configured +#endif + +#define I2C_TIMEOUT 6667 +#define I2C_RETRIES 3 + +struct mpc5xxx_i2c_tap { + int scl2tap; + int tap2tap; +}; + +static int mpc_reg_in (volatile u32 *reg); +static void mpc_reg_out (volatile u32 *reg, int val, int mask); +static int wait_for_bb (void); +static int wait_for_pin (int *status); +static int do_address (uchar chip, char rdwr_flag); +static int send_bytes (uchar chip, char *buf, int len); +static int receive_bytes (uchar chip, char *buf, int len); +static int mpc_get_fdr (int); + +static int mpc_reg_in(volatile u32 *reg) +{ + int ret = *reg >> 24; + __asm__ __volatile__ ("eieio"); + return ret; +} + +static void mpc_reg_out(volatile u32 *reg, int val, int mask) +{ + int tmp; + + if (!mask) { + *reg = val << 24; + } else { + tmp = mpc_reg_in(reg); + *reg = ((tmp & ~mask) | (val & mask)) << 24; + } + __asm__ __volatile__ ("eieio"); + + return; +} + +static int wait_for_bb(void) +{ + struct mpc5xxx_i2c *regs = (struct mpc5xxx_i2c *)I2C_BASE; + int timeout = I2C_TIMEOUT; + int status; + + status = mpc_reg_in(®s->msr); + + while (timeout-- && (status & I2C_BB)) { +#if 1 + volatile int temp; + mpc_reg_out(®s->mcr, I2C_STA, I2C_STA); + temp = mpc_reg_in(®s->mdr); + mpc_reg_out(®s->mcr, 0, I2C_STA); + mpc_reg_out(®s->mcr, 0, 0); + mpc_reg_out(®s->mcr, I2C_EN, 0); +#endif + udelay(15); + status = mpc_reg_in(®s->msr); + } + + return (status & I2C_BB); +} + +static int wait_for_pin(int *status) +{ + struct mpc5xxx_i2c *regs = (struct mpc5xxx_i2c *)I2C_BASE; + int timeout = I2C_TIMEOUT; + + *status = mpc_reg_in(®s->msr); + + while (timeout-- && !(*status & I2C_IF)) { + udelay(15); + *status = mpc_reg_in(®s->msr); + } + + if (!(*status & I2C_IF)) { + return -1; + } + + mpc_reg_out(®s->msr, 0, I2C_IF); + + return 0; +} + +static int do_address(uchar chip, char rdwr_flag) +{ + struct mpc5xxx_i2c *regs = (struct mpc5xxx_i2c *)I2C_BASE; + int status; + + chip <<= 1; + + if (rdwr_flag) { + chip |= 1; + } + + mpc_reg_out(®s->mcr, I2C_TX, I2C_TX); + mpc_reg_out(®s->mdr, chip, 0); + + if (wait_for_pin(&status)) { + return -2; + } + + if (status & I2C_RXAK) { + return -3; + } + + return 0; +} + +static int send_bytes(uchar chip, char *buf, int len) +{ + struct mpc5xxx_i2c *regs = (struct mpc5xxx_i2c *)I2C_BASE; + int wrcount; + int status; + + for (wrcount = 0; wrcount < len; ++wrcount) { + + mpc_reg_out(®s->mdr, buf[wrcount], 0); + + if (wait_for_pin(&status)) { + break; + } + + if (status & I2C_RXAK) { + break; + } + + } + + return !(wrcount == len); +} + +static int receive_bytes(uchar chip, char *buf, int len) +{ + struct mpc5xxx_i2c *regs = (struct mpc5xxx_i2c *)I2C_BASE; + int dummy = 1; + int rdcount = 0; + int status; + int i; + + mpc_reg_out(®s->mcr, 0, I2C_TX); + + for (i = 0; i < len; ++i) { + buf[rdcount] = mpc_reg_in(®s->mdr); + + if (dummy) { + dummy = 0; + } else { + rdcount++; + } + + + if (wait_for_pin(&status)) { + return -4; + } + } + + mpc_reg_out(®s->mcr, I2C_TXAK, I2C_TXAK); + buf[rdcount++] = mpc_reg_in(®s->mdr); + + if (wait_for_pin(&status)) { + return -5; + } + + mpc_reg_out(®s->mcr, 0, I2C_TXAK); + + return 0; +} + +#if defined(CONFIG_SYS_I2C_INIT_MPC5XXX) + +#define FDR510(x) (u8) (((x & 0x20) >> 3) | (x & 0x3)) +#define FDR432(x) (u8) ((x & 0x1C) >> 2) +/* + * Reset any i2c devices that may have been interrupted during a system reset. + * Normally this would be accomplished by clocking the line until SCL and SDA + * are released and then sending a start condtiion (From an Atmel datasheet). + * There is no direct access to the i2c pins so instead create start commands + * through the i2c interface. Send a start command then delay for the SDA Hold + * time, repeat this by disabling/enabling the bus a total of 9 times. + */ +static void send_reset(void) +{ + struct mpc5xxx_i2c *regs = (struct mpc5xxx_i2c *)I2C_BASE; + int i; + u32 delay; + u8 fdr; + int SDA_Tap[] = { 3, 3, 4, 4, 1, 1, 2, 2}; + struct mpc5xxx_i2c_tap scltap[] = { + {4, 1}, + {4, 2}, + {6, 4}, + {6, 8}, + {14, 16}, + {30, 32}, + {62, 64}, + {126, 128} + }; + + fdr = (u8)mpc_reg_in(®s->mfdr); + + delay = scltap[FDR432(fdr)].scl2tap + ((SDA_Tap[FDR510(fdr)] - 1) * \ + scltap[FDR432(fdr)].tap2tap) + 3; + + for (i = 0; i < 9; i++) { + mpc_reg_out(®s->mcr, I2C_EN|I2C_STA|I2C_TX, I2C_INIT_MASK); + udelay(delay); + mpc_reg_out(®s->mcr, 0, I2C_INIT_MASK); + udelay(delay); + } + + mpc_reg_out(®s->mcr, I2C_EN, I2C_INIT_MASK); +} +#endif /* CONFIG_SYS_I2c_INIT_MPC5XXX */ + +/**************** I2C API ****************/ + +void i2c_init(int speed, int saddr) +{ + struct mpc5xxx_i2c *regs = (struct mpc5xxx_i2c *)I2C_BASE; + + mpc_reg_out(®s->mcr, 0, 0); + mpc_reg_out(®s->madr, saddr << 1, 0); + + /* Set clock + */ + mpc_reg_out(®s->mfdr, mpc_get_fdr(speed), 0); + + /* Enable module + */ + mpc_reg_out(®s->mcr, I2C_EN, I2C_INIT_MASK); + mpc_reg_out(®s->msr, 0, I2C_IF); + +#if defined(CONFIG_SYS_I2C_INIT_MPC5XXX) + send_reset(); +#endif + return; +} + +static int mpc_get_fdr(int speed) +{ + static int fdr = -1; + + if (fdr == -1) { + ulong best_speed = 0; + ulong divider; + ulong ipb, scl; + ulong bestmatch = 0xffffffffUL; + int best_i = 0, best_j = 0, i, j; + int SCL_Tap[] = { 9, 10, 12, 15, 5, 6, 7, 8}; + struct mpc5xxx_i2c_tap scltap[] = { + {4, 1}, + {4, 2}, + {6, 4}, + {6, 8}, + {14, 16}, + {30, 32}, + {62, 64}, + {126, 128} + }; + + ipb = gd->ipb_clk; + for (i = 7; i >= 0; i--) { + for (j = 7; j >= 0; j--) { + scl = 2 * (scltap[j].scl2tap + + (SCL_Tap[i] - 1) * scltap[j].tap2tap + 2); + if (ipb <= speed*scl) { + if ((speed*scl - ipb) < bestmatch) { + bestmatch = speed*scl - ipb; + best_i = i; + best_j = j; + best_speed = ipb/scl; + } + } + } + } + divider = (best_i & 3) | ((best_i & 4) << 3) | (best_j << 2); + if (gd->flags & GD_FLG_RELOC) { + fdr = divider; + } else { + if (gd->have_console) + printf("%ld kHz, ", best_speed / 1000); + return divider; + } + } + + return fdr; +} + +int i2c_probe(uchar chip) +{ + struct mpc5xxx_i2c *regs = (struct mpc5xxx_i2c *)I2C_BASE; + int i; + + for (i = 0; i < I2C_RETRIES; i++) { + mpc_reg_out(®s->mcr, I2C_STA, I2C_STA); + + if (! do_address(chip, 0)) { + mpc_reg_out(®s->mcr, 0, I2C_STA); + udelay(500); + break; + } + + mpc_reg_out(®s->mcr, 0, I2C_STA); + udelay(500); + } + + return (i == I2C_RETRIES); +} + +int i2c_read(uchar chip, uint addr, int alen, uchar *buf, int len) +{ + char xaddr[4]; + struct mpc5xxx_i2c * regs = (struct mpc5xxx_i2c *)I2C_BASE; + int ret = -1; + + xaddr[0] = (addr >> 24) & 0xFF; + xaddr[1] = (addr >> 16) & 0xFF; + xaddr[2] = (addr >> 8) & 0xFF; + xaddr[3] = addr & 0xFF; + + if (wait_for_bb()) { + if (gd->have_console) + printf("i2c_read: bus is busy\n"); + goto Done; + } + + mpc_reg_out(®s->mcr, I2C_STA, I2C_STA); + if (do_address(chip, 0)) { + if (gd->have_console) + printf("i2c_read: failed to address chip\n"); + goto Done; + } + + if (send_bytes(chip, &xaddr[4-alen], alen)) { + if (gd->have_console) + printf("i2c_read: send_bytes failed\n"); + goto Done; + } + + mpc_reg_out(®s->mcr, I2C_RSTA, I2C_RSTA); + if (do_address(chip, 1)) { + if (gd->have_console) + printf("i2c_read: failed to address chip\n"); + goto Done; + } + + if (receive_bytes(chip, (char *)buf, len)) { + if (gd->have_console) + printf("i2c_read: receive_bytes failed\n"); + goto Done; + } + + ret = 0; +Done: + mpc_reg_out(®s->mcr, 0, I2C_STA); + return ret; +} + +int i2c_write(uchar chip, uint addr, int alen, uchar *buf, int len) +{ + char xaddr[4]; + struct mpc5xxx_i2c *regs = (struct mpc5xxx_i2c *)I2C_BASE; + int ret = -1; + + xaddr[0] = (addr >> 24) & 0xFF; + xaddr[1] = (addr >> 16) & 0xFF; + xaddr[2] = (addr >> 8) & 0xFF; + xaddr[3] = addr & 0xFF; + + if (wait_for_bb()) { + if (gd->have_console) + printf("i2c_write: bus is busy\n"); + goto Done; + } + + mpc_reg_out(®s->mcr, I2C_STA, I2C_STA); + if (do_address(chip, 0)) { + if (gd->have_console) + printf("i2c_write: failed to address chip\n"); + goto Done; + } + + if (send_bytes(chip, &xaddr[4-alen], alen)) { + if (gd->have_console) + printf("i2c_write: send_bytes failed\n"); + goto Done; + } + + if (send_bytes(chip, (char *)buf, len)) { + if (gd->have_console) + printf("i2c_write: send_bytes failed\n"); + goto Done; + } + + ret = 0; +Done: + mpc_reg_out(®s->mcr, 0, I2C_STA); + return ret; +} + +#endif /* CONFIG_HARD_I2C */ diff --git a/arch/powerpc/cpu/mpc5xxx/ide.c b/arch/powerpc/cpu/mpc5xxx/ide.c new file mode 100644 index 0000000..d337abb --- /dev/null +++ b/arch/powerpc/cpu/mpc5xxx/ide.c @@ -0,0 +1,99 @@ +/* + * (C) Copyright 2004 + * Pierre AUBERT, Staubli Faverges, <p.aubert@staubli.com> + * + * 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 + * + * Init is derived from Linux code. + */ +#include <common.h> + +#if defined(CONFIG_CMD_IDE) +#include <mpc5xxx.h> + +DECLARE_GLOBAL_DATA_PTR; + +#define CALC_TIMING(t) (t + period - 1) / period + +#ifdef CONFIG_IDE_RESET +extern void init_ide_reset (void); +#endif + +int ide_preinit (void) +{ + long period, t0, t1, t2_8, t2_16, t4, ta; + vu_long reg; + struct mpc5xxx_sdma *psdma = (struct mpc5xxx_sdma *) MPC5XXX_SDMA; + + reg = *(vu_long *) MPC5XXX_GPS_PORT_CONFIG; +#if defined(CONFIG_SYS_ATA_CS_ON_I2C2) + /* ATA cs0/1 on i2c2 clk/io */ + reg = (reg & ~0x03000000ul) | 0x02000000ul; +#elif defined(CONFIG_SYS_ATA_CS_ON_TIMER01) + /* ATA cs0/1 on Timer 0/1 */ + reg = (reg & ~0x03000000ul) | 0x03000000ul; +#else + /* ATA cs0/1 on Local Plus cs4/5 */ + reg = (reg & ~0x03000000ul) | 0x01000000ul; +#endif /* CONFIG_TOTAL5200 */ + *(vu_long *) MPC5XXX_GPS_PORT_CONFIG = reg; + + /* All sample codes do that... */ + *(vu_long *) MPC5XXX_ATA_SHARE_COUNT = 0; + +#if defined(CONFIG_UC101) + /* Configure and reset host */ + *(vu_long *) MPC5XXX_ATA_HOST_CONFIG = + MPC5xxx_ATA_HOSTCONF_SMR | MPC5xxx_ATA_HOSTCONF_FR; + udelay (10); + *(vu_long *) MPC5XXX_ATA_HOST_CONFIG = 0; +#else + /* Configure and reset host */ + *(vu_long *) MPC5XXX_ATA_HOST_CONFIG = MPC5xxx_ATA_HOSTCONF_IORDY | + MPC5xxx_ATA_HOSTCONF_SMR | MPC5xxx_ATA_HOSTCONF_FR; + udelay (10); + *(vu_long *) MPC5XXX_ATA_HOST_CONFIG = MPC5xxx_ATA_HOSTCONF_IORDY; +#endif + + /* Disable prefetch on Commbus */ + psdma->PtdCntrl |= 1; + + /* Init timings : we use PIO mode 0 timings */ + period = 1000000000 / gd->ipb_clk; /* period in ns */ + + t0 = CALC_TIMING (600); + t2_8 = CALC_TIMING (290); + t2_16 = CALC_TIMING (165); + reg = (t0 << 24) | (t2_8 << 16) | (t2_16 << 8); + *(vu_long *) MPC5XXX_ATA_PIO1 = reg; + + t4 = CALC_TIMING (30); + t1 = CALC_TIMING (70); + ta = CALC_TIMING (35); + reg = (t4 << 24) | (t1 << 16) | (ta << 8); + + *(vu_long *) MPC5XXX_ATA_PIO2 = reg; + +#ifdef CONFIG_IDE_RESET + init_ide_reset (); +#endif /* CONFIG_IDE_RESET */ + + return (0); +} +#endif diff --git a/arch/powerpc/cpu/mpc5xxx/interrupts.c b/arch/powerpc/cpu/mpc5xxx/interrupts.c new file mode 100644 index 0000000..16eee3a --- /dev/null +++ b/arch/powerpc/cpu/mpc5xxx/interrupts.c @@ -0,0 +1,346 @@ +/* + * (C) Copyright 2006 + * Detlev Zundel, DENX Software Engineering, dzu@denx.de + * + * (C) Copyright -2003 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * (C) Copyright 2001 + * Josh Huber <huber@mclx.com>, Mission Critical Linux, Inc. + * + * 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 + */ + +/* this section was ripped out of arch/powerpc/syslib/mpc52xx_pic.c in the + * Linux 2.6 source with the following copyright. + * + * Based on (well, mostly copied from) the code from the 2.4 kernel by + * Dale Farnsworth <dfarnsworth@mvista.com> and Kent Borg. + * + * Copyright (C) 2004 Sylvain Munaut <tnt@246tNt.com> + * Copyright (C) 2003 Montavista Software, Inc + */ + +#include <common.h> +#include <asm/processor.h> +#include <asm/io.h> +#include <command.h> + +struct irq_action { + interrupt_handler_t *handler; + void *arg; + ulong count; +}; + +static struct irq_action irq_handlers[NR_IRQS]; + +static struct mpc5xxx_intr *intr; +static struct mpc5xxx_sdma *sdma; + +static void mpc5xxx_ic_disable(unsigned int irq) +{ + u32 val; + + if (irq == MPC5XXX_IRQ0) { + val = in_be32(&intr->ctrl); + val &= ~(1 << 11); + out_be32(&intr->ctrl, val); + } else if (irq < MPC5XXX_IRQ1) { + BUG(); + } else if (irq <= MPC5XXX_IRQ3) { + val = in_be32(&intr->ctrl); + val &= ~(1 << (10 - (irq - MPC5XXX_IRQ1))); + out_be32(&intr->ctrl, val); + } else if (irq < MPC5XXX_SDMA_IRQ_BASE) { + val = in_be32(&intr->main_mask); + val |= 1 << (16 - (irq - MPC5XXX_MAIN_IRQ_BASE)); + out_be32(&intr->main_mask, val); + } else if (irq < MPC5XXX_PERP_IRQ_BASE) { + val = in_be32(&sdma->IntMask); + val |= 1 << (irq - MPC5XXX_SDMA_IRQ_BASE); + out_be32(&sdma->IntMask, val); + } else { + val = in_be32(&intr->per_mask); + val |= 1 << (31 - (irq - MPC5XXX_PERP_IRQ_BASE)); + out_be32(&intr->per_mask, val); + } +} + +static void mpc5xxx_ic_enable(unsigned int irq) +{ + u32 val; + + if (irq == MPC5XXX_IRQ0) { + val = in_be32(&intr->ctrl); + val |= 1 << 11; + out_be32(&intr->ctrl, val); + } else if (irq < MPC5XXX_IRQ1) { + BUG(); + } else if (irq <= MPC5XXX_IRQ3) { + val = in_be32(&intr->ctrl); + val |= 1 << (10 - (irq - MPC5XXX_IRQ1)); + out_be32(&intr->ctrl, val); + } else if (irq < MPC5XXX_SDMA_IRQ_BASE) { + val = in_be32(&intr->main_mask); + val &= ~(1 << (16 - (irq - MPC5XXX_MAIN_IRQ_BASE))); + out_be32(&intr->main_mask, val); + } else if (irq < MPC5XXX_PERP_IRQ_BASE) { + val = in_be32(&sdma->IntMask); + val &= ~(1 << (irq - MPC5XXX_SDMA_IRQ_BASE)); + out_be32(&sdma->IntMask, val); + } else { + val = in_be32(&intr->per_mask); + val &= ~(1 << (31 - (irq - MPC5XXX_PERP_IRQ_BASE))); + out_be32(&intr->per_mask, val); + } +} + +static void mpc5xxx_ic_ack(unsigned int irq) +{ + u32 val; + + /* + * Only some irqs are reset here, others in interrupting hardware. + */ + + switch (irq) { + case MPC5XXX_IRQ0: + val = in_be32(&intr->ctrl); + val |= 0x08000000; + out_be32(&intr->ctrl, val); + break; + case MPC5XXX_CCS_IRQ: + val = in_be32(&intr->enc_status); + val |= 0x00000400; + out_be32(&intr->enc_status, val); + break; + case MPC5XXX_IRQ1: + val = in_be32(&intr->ctrl); + val |= 0x04000000; + out_be32(&intr->ctrl, val); + break; + case MPC5XXX_IRQ2: + val = in_be32(&intr->ctrl); + val |= 0x02000000; + out_be32(&intr->ctrl, val); + break; + case MPC5XXX_IRQ3: + val = in_be32(&intr->ctrl); + val |= 0x01000000; + out_be32(&intr->ctrl, val); + break; + default: + if (irq >= MPC5XXX_SDMA_IRQ_BASE + && irq < (MPC5XXX_SDMA_IRQ_BASE + MPC5XXX_SDMA_IRQ_NUM)) { + out_be32(&sdma->IntPend, + 1 << (irq - MPC5XXX_SDMA_IRQ_BASE)); + } + break; + } +} + +static void mpc5xxx_ic_disable_and_ack(unsigned int irq) +{ + mpc5xxx_ic_disable(irq); + mpc5xxx_ic_ack(irq); +} + +static void mpc5xxx_ic_end(unsigned int irq) +{ + mpc5xxx_ic_enable(irq); +} + +void mpc5xxx_init_irq(void) +{ + u32 intr_ctrl; + + /* Remap the necessary zones */ + intr = (struct mpc5xxx_intr *)(MPC5XXX_ICTL); + sdma = (struct mpc5xxx_sdma *)(MPC5XXX_SDMA); + + /* Disable all interrupt sources. */ + out_be32(&sdma->IntPend, 0xffffffff); /* 1 means clear pending */ + out_be32(&sdma->IntMask, 0xffffffff); /* 1 means disabled */ + out_be32(&intr->per_mask, 0x7ffffc00); /* 1 means disabled */ + out_be32(&intr->main_mask, 0x00010fff); /* 1 means disabled */ + intr_ctrl = in_be32(&intr->ctrl); + intr_ctrl |= 0x0f000000 | /* clear IRQ 0-3 */ + 0x00ff0000 | /* IRQ 0-3 level sensitive low active */ + 0x00001000 | /* MEE master external enable */ + 0x00000000 | /* 0 means disable IRQ 0-3 */ + 0x00000001; /* CEb route critical normally */ + out_be32(&intr->ctrl, intr_ctrl); + + /* Zero a bunch of the priority settings. */ + out_be32(&intr->per_pri1, 0); + out_be32(&intr->per_pri2, 0); + out_be32(&intr->per_pri3, 0); + out_be32(&intr->main_pri1, 0); + out_be32(&intr->main_pri2, 0); +} + +int mpc5xxx_get_irq(struct pt_regs *regs) +{ + u32 status; + int irq = -1; + + status = in_be32(&intr->enc_status); + + if (status & 0x00000400) { /* critical */ + irq = (status >> 8) & 0x3; + if (irq == 2) /* high priority peripheral */ + goto peripheral; + irq += MPC5XXX_CRIT_IRQ_BASE; + } else if (status & 0x00200000) { /* main */ + irq = (status >> 16) & 0x1f; + if (irq == 4) /* low priority peripheral */ + goto peripheral; + irq += MPC5XXX_MAIN_IRQ_BASE; + } else if (status & 0x20000000) { /* peripheral */ + peripheral: + irq = (status >> 24) & 0x1f; + if (irq == 0) { /* bestcomm */ + status = in_be32(&sdma->IntPend); + irq = ffs(status) + MPC5XXX_SDMA_IRQ_BASE - 1; + } else + irq += MPC5XXX_PERP_IRQ_BASE; + } + + return irq; +} + +/****************************************************************************/ + +int interrupt_init_cpu(ulong * decrementer_count) +{ + *decrementer_count = get_tbclk() / CONFIG_SYS_HZ; + + mpc5xxx_init_irq(); + + return (0); +} + +/****************************************************************************/ + +/* + * Handle external interrupts + */ +void external_interrupt(struct pt_regs *regs) +{ + int irq, unmask = 1; + + irq = mpc5xxx_get_irq(regs); + + mpc5xxx_ic_disable_and_ack(irq); + + enable_interrupts(); + + if (irq_handlers[irq].handler != NULL) + (*irq_handlers[irq].handler) (irq_handlers[irq].arg); + else { + printf("\nBogus External Interrupt IRQ %d\n", irq); + /* + * turn off the bogus interrupt, otherwise it + * might repeat forever + */ + unmask = 0; + } + + if (unmask) + mpc5xxx_ic_end(irq); +} + +void timer_interrupt_cpu(struct pt_regs *regs) +{ + /* nothing to do here */ + return; +} + +/****************************************************************************/ + +/* + * Install and free a interrupt handler. + */ + +void irq_install_handler(int irq, interrupt_handler_t * handler, void *arg) +{ + if (irq < 0 || irq >= NR_IRQS) { + printf("irq_install_handler: bad irq number %d\n", irq); + return; + } + + if (irq_handlers[irq].handler != NULL) + printf("irq_install_handler: 0x%08lx replacing 0x%08lx\n", + (ulong) handler, (ulong) irq_handlers[irq].handler); + + irq_handlers[irq].handler = handler; + irq_handlers[irq].arg = arg; + + mpc5xxx_ic_enable(irq); +} + +void irq_free_handler(int irq) +{ + if (irq < 0 || irq >= NR_IRQS) { + printf("irq_free_handler: bad irq number %d\n", irq); + return; + } + + mpc5xxx_ic_disable(irq); + + irq_handlers[irq].handler = NULL; + irq_handlers[irq].arg = NULL; +} + +/****************************************************************************/ + +#if defined(CONFIG_CMD_IRQ) +void do_irqinfo(cmd_tbl_t * cmdtp, bd_t * bd, int flag, int argc, char *argv[]) +{ + int irq, re_enable; + u32 intr_ctrl; + char *irq_config[] = { "level sensitive, active high", + "edge sensitive, rising active edge", + "edge sensitive, falling active edge", + "level sensitive, active low" + }; + + re_enable = disable_interrupts(); + + intr_ctrl = in_be32(&intr->ctrl); + printf("Interrupt configuration:\n"); + + for (irq = 0; irq <= 3; irq++) { + printf("IRQ%d: %s\n", irq, + irq_config[(intr_ctrl >> (22 - 2 * irq)) & 0x3]); + } + + puts("\nInterrupt-Information:\n" "Nr Routine Arg Count\n"); + + for (irq = 0; irq < NR_IRQS; irq++) + if (irq_handlers[irq].handler != NULL) + printf("%02d %08lx %08lx %ld\n", irq, + (ulong) irq_handlers[irq].handler, + (ulong) irq_handlers[irq].arg, + irq_handlers[irq].count); + + if (re_enable) + enable_interrupts(); +} +#endif diff --git a/arch/powerpc/cpu/mpc5xxx/io.S b/arch/powerpc/cpu/mpc5xxx/io.S new file mode 100644 index 0000000..2178a26 --- /dev/null +++ b/arch/powerpc/cpu/mpc5xxx/io.S @@ -0,0 +1,128 @@ +/* + * Copyright (C) 1998 Dan Malek <dmalek@jlc.net> + * Copyright (C) 1999 Magnus Damm <kieraypc01.p.y.kie.era.ericsson.se> + * Copyright (C) 2001 Sysgo Real-Time Solutions, GmbH <www.elinos.com> + * Andreas Heppel <aheppel@sysgo.de> + * Copyright (C) 2003 Wolfgang Denk <wd@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 <config.h> +#include <ppc_asm.tmpl> + +/* ------------------------------------------------------------------------------- */ +/* Function: in8 */ +/* Description: Input 8 bits */ +/* ------------------------------------------------------------------------------- */ + .globl in8 +in8: + lbz r3,0(r3) + sync + blr + +/* ------------------------------------------------------------------------------- */ +/* Function: in16 */ +/* Description: Input 16 bits */ +/* ------------------------------------------------------------------------------- */ + .globl in16 +in16: + lhz r3,0(r3) + sync + blr + +/* ------------------------------------------------------------------------------- */ +/* Function: in16r */ +/* Description: Input 16 bits and byte reverse */ +/* ------------------------------------------------------------------------------- */ + .globl in16r +in16r: + lhbrx r3,0,r3 + sync + blr + +/* ------------------------------------------------------------------------------- */ +/* Function: in32 */ +/* Description: Input 32 bits */ +/* ------------------------------------------------------------------------------- */ + .globl in32 +in32: + lwz 3,0(3) + sync + blr + +/* ------------------------------------------------------------------------------- */ +/* Function: in32r */ +/* Description: Input 32 bits and byte reverse */ +/* ------------------------------------------------------------------------------- */ + .globl in32r +in32r: + lwbrx r3,0,r3 + sync + blr + +/* ------------------------------------------------------------------------------- */ +/* Function: out8 */ +/* Description: Output 8 bits */ +/* ------------------------------------------------------------------------------- */ + .globl out8 +out8: + stb r4,0(r3) + sync + blr + +/* ------------------------------------------------------------------------------- */ +/* Function: out16 */ +/* Description: Output 16 bits */ +/* ------------------------------------------------------------------------------- */ + .globl out16 +out16: + sth r4,0(r3) + sync + blr + +/* ------------------------------------------------------------------------------- */ +/* Function: out16r */ +/* Description: Byte reverse and output 16 bits */ +/* ------------------------------------------------------------------------------- */ + .globl out16r +out16r: + sthbrx r4,0,r3 + sync + blr + +/* ------------------------------------------------------------------------------- */ +/* Function: out32 */ +/* Description: Output 32 bits */ +/* ------------------------------------------------------------------------------- */ + .globl out32 +out32: + stw r4,0(r3) + sync + blr + +/* ------------------------------------------------------------------------------- */ +/* Function: out32r */ +/* Description: Byte reverse and output 32 bits */ +/* ------------------------------------------------------------------------------- */ + .globl out32r +out32r: + stwbrx r4,0,r3 + sync + blr diff --git a/arch/powerpc/cpu/mpc5xxx/loadtask.c b/arch/powerpc/cpu/mpc5xxx/loadtask.c new file mode 100644 index 0000000..47e7b59 --- /dev/null +++ b/arch/powerpc/cpu/mpc5xxx/loadtask.c @@ -0,0 +1,76 @@ +/* + * (C) Copyright 2003 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * This file is based on code + * (C) Copyright Motorola, Inc., 2000 + */ + +#include <common.h> +#include <mpc5xxx.h> + +/* BestComm/SmartComm microcode */ +extern int taskTable; + +void loadtask(int basetask, int tasks) +{ + int *sram = (int *)MPC5XXX_SRAM; + int *task_org = &taskTable; + unsigned int start, offset, end; + int i; + +#ifdef DEBUG + printf("basetask = %d, tasks = %d\n", basetask, tasks); + printf("task_org = 0x%08x\n", (unsigned int)task_org); +#endif + + /* setup TaskBAR register */ + *(vu_long *)MPC5XXX_SDMA = MPC5XXX_SRAM; + + /* relocate task table entries */ + offset = (unsigned int)sram; + for (i = basetask; i < basetask + tasks; i++) { + sram[i * 8 + 0] = task_org[i * 8 + 0] + offset; + sram[i * 8 + 1] = task_org[i * 8 + 1] + offset; + sram[i * 8 + 2] = task_org[i * 8 + 2] + offset; + sram[i * 8 + 3] = task_org[i * 8 + 3] + offset; + sram[i * 8 + 4] = task_org[i * 8 + 4]; + sram[i * 8 + 5] = task_org[i * 8 + 5]; + sram[i * 8 + 6] = task_org[i * 8 + 6] + offset; + sram[i * 8 + 7] = task_org[i * 8 + 7]; + } + + /* relocate task descriptors */ + start = (sram[basetask * 8] - (unsigned int)sram); + end = (sram[(basetask + tasks - 1) * 8 + 1] - (unsigned int)sram); + +#ifdef DEBUG + printf ("TDT start = 0x%08x, end = 0x%08x\n", start, end); +#endif + + start /= 4; + end /= 4; + for (i = start; i <= end; i++) { + sram[i] = task_org[i]; + } + + /* relocate variables */ + start = (sram[basetask * 8 + 2] - (unsigned int)sram); + end = (sram[(basetask + tasks - 1) * 8 + 2] + 256 - (unsigned int)sram); + start /= 4; + end /= 4; + for (i = start; i < end; i++) { + sram[i] = task_org[i]; + } + + /* relocate function decriptors */ + start = ((sram[basetask * 8 + 3] & 0xfffffffc) - (unsigned int)sram); + end = ((sram[(basetask + tasks - 1) * 8 + 3] & 0xfffffffc) + 256 - (unsigned int)sram); + start /= 4; + end /= 4; + for (i = start; i < end; i++) { + sram[i] = task_org[i]; + } + + asm volatile ("sync"); +} diff --git a/arch/powerpc/cpu/mpc5xxx/pci_mpc5200.c b/arch/powerpc/cpu/mpc5xxx/pci_mpc5200.c new file mode 100644 index 0000000..8268f8a --- /dev/null +++ b/arch/powerpc/cpu/mpc5xxx/pci_mpc5200.c @@ -0,0 +1,187 @@ +/* + * (C) Copyright 2000-2003 + * Wolfgang Denk, DENX Software Engineering, wd@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_PCI) + +#include <asm/processor.h> +#include <asm/io.h> +#include <pci.h> +#include <mpc5xxx.h> + +/* System RAM mapped over PCI */ +#define CONFIG_PCI_MEMORY_BUS CONFIG_SYS_SDRAM_BASE +#define CONFIG_PCI_MEMORY_PHYS CONFIG_SYS_SDRAM_BASE +#define CONFIG_PCI_MEMORY_SIZE (1024 * 1024 * 1024) + +/* PCIIWCR bit fields */ +#define IWCR_MEM (0 << 3) +#define IWCR_IO (1 << 3) +#define IWCR_READ (0 << 1) +#define IWCR_READLINE (1 << 1) +#define IWCR_READMULT (2 << 1) +#define IWCR_EN (1 << 0) + +static int mpc5200_read_config_dword(struct pci_controller *hose, + pci_dev_t dev, int offset, u32* value) +{ + *(volatile u32 *)MPC5XXX_PCI_CAR = (1 << 31) | dev | offset; + eieio(); + udelay(10); +#if (defined CONFIG_PF5200 || defined CONFIG_CPCI5200) + if (dev & 0x00ff0000) { + u32 val; + val = in_le16((volatile u16 *)(CONFIG_PCI_IO_PHYS+2)); + udelay(10); + val = val << 16; + val |= in_le16((volatile u16 *)(CONFIG_PCI_IO_PHYS+0)); + *value = val; + } else { + *value = in_le32((volatile u32 *)CONFIG_PCI_IO_PHYS); + } + udelay(10); +#else + *value = in_le32((volatile u32 *)CONFIG_PCI_IO_PHYS); +#endif + eieio(); + *(volatile u32 *)MPC5XXX_PCI_CAR = 0; + udelay(10); + return 0; +} + +static int mpc5200_write_config_dword(struct pci_controller *hose, + pci_dev_t dev, int offset, u32 value) +{ + *(volatile u32 *)MPC5XXX_PCI_CAR = (1 << 31) | dev | offset; + eieio(); + udelay(10); + out_le32((volatile u32 *)CONFIG_PCI_IO_PHYS, value); + eieio(); + *(volatile u32 *)MPC5XXX_PCI_CAR = 0; + udelay(10); + return 0; +} + +void pci_mpc5xxx_init (struct pci_controller *hose) +{ + hose->first_busno = 0; + hose->last_busno = 0xff; + + /* System space */ + pci_set_region(hose->regions + 0, + CONFIG_PCI_MEMORY_BUS, + CONFIG_PCI_MEMORY_PHYS, + CONFIG_PCI_MEMORY_SIZE, + PCI_REGION_MEM | PCI_REGION_SYS_MEMORY); + + /* PCI memory space */ + pci_set_region(hose->regions + 1, + CONFIG_PCI_MEM_BUS, + CONFIG_PCI_MEM_PHYS, + CONFIG_PCI_MEM_SIZE, + PCI_REGION_MEM); + + /* PCI IO space */ + pci_set_region(hose->regions + 2, + CONFIG_PCI_IO_BUS, + CONFIG_PCI_IO_PHYS, + CONFIG_PCI_IO_SIZE, + PCI_REGION_IO); + + hose->region_count = 3; + + pci_register_hose(hose); + + /* GPIO Multiplexing - enable PCI */ + *(vu_long *)MPC5XXX_GPS_PORT_CONFIG &= ~(1 << 15); + + /* Set host bridge as pci master and enable memory decoding */ + *(vu_long *)MPC5XXX_PCI_CMD |= + PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY; + + /* Set maximum latency timer */ + *(vu_long *)MPC5XXX_PCI_CFG |= (0xf800); + + /* Set cache line size */ + *(vu_long *)MPC5XXX_PCI_CFG = (*(vu_long *)MPC5XXX_PCI_CFG & ~0xff) | + (CONFIG_SYS_CACHELINE_SIZE / 4); + + /* Map MBAR to PCI space */ + *(vu_long *)MPC5XXX_PCI_BAR0 = CONFIG_SYS_MBAR; + *(vu_long *)MPC5XXX_PCI_TBATR0 = CONFIG_SYS_MBAR | 1; + + /* Map RAM to PCI space */ + *(vu_long *)MPC5XXX_PCI_BAR1 = CONFIG_PCI_MEMORY_BUS | (1 << 3); + *(vu_long *)MPC5XXX_PCI_TBATR1 = CONFIG_PCI_MEMORY_PHYS | 1; + + /* Park XLB on PCI */ + *(vu_long *)(MPC5XXX_XLBARB + 0x40) &= ~((7 << 8) | (3 << 5)); + *(vu_long *)(MPC5XXX_XLBARB + 0x40) |= (3 << 8) | (3 << 5); + + /* Disable interrupts from PCI controller */ + *(vu_long *)MPC5XXX_PCI_GSCR &= ~(7 << 12); + *(vu_long *)MPC5XXX_PCI_ICR &= ~(7 << 24); + + /* Set PCI retry counter to 0 = infinite retry. */ + /* The default of 255 is too short for slow devices. */ + *(vu_long *)MPC5XXX_PCI_ICR &= 0xFFFFFF00; + + /* Disable initiator windows */ + *(vu_long *)MPC5XXX_PCI_IWCR = 0; + + /* Map PCI memory to physical space */ + *(vu_long *)MPC5XXX_PCI_IW0BTAR = CONFIG_PCI_MEM_PHYS | + (((CONFIG_PCI_MEM_SIZE - 1) >> 8) & 0x00ff0000) | + (CONFIG_PCI_MEM_BUS >> 16); + *(vu_long *)MPC5XXX_PCI_IWCR |= (IWCR_MEM | IWCR_READ | IWCR_EN) << 24; + + /* Map PCI I/O to physical space */ + *(vu_long *)MPC5XXX_PCI_IW1BTAR = CONFIG_PCI_IO_PHYS | + (((CONFIG_PCI_IO_SIZE - 1) >> 8) & 0x00ff0000) | + (CONFIG_PCI_IO_BUS >> 16); + *(vu_long *)MPC5XXX_PCI_IWCR |= (IWCR_IO | IWCR_READ | IWCR_EN) << 16; + + /* Reset the PCI bus */ + *(vu_long *)MPC5XXX_PCI_GSCR |= 1; + udelay(1000); + *(vu_long *)MPC5XXX_PCI_GSCR &= ~1; + udelay(1000); + + pci_set_ops(hose, + pci_hose_read_config_byte_via_dword, + pci_hose_read_config_word_via_dword, + mpc5200_read_config_dword, + pci_hose_write_config_byte_via_dword, + pci_hose_write_config_word_via_dword, + mpc5200_write_config_dword); + + udelay(1000); + +#ifdef CONFIG_PCI_SCAN_SHOW + printf("PCI: Bus Dev VenId DevId Class Int\n"); +#endif + + hose->last_busno = pci_hose_scan(hose); +} +#endif /* CONFIG_PCI */ diff --git a/arch/powerpc/cpu/mpc5xxx/serial.c b/arch/powerpc/cpu/mpc5xxx/serial.c new file mode 100644 index 0000000..59a877a --- /dev/null +++ b/arch/powerpc/cpu/mpc5xxx/serial.c @@ -0,0 +1,363 @@ +/* + * (C) Copyright 2000 - 2003 + * Wolfgang Denk, DENX Software Engineering, wd@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 + * + * Hacked for MPC8260 by Murray.Jensen@cmst.csiro.au, 19-Oct-00, with + * changes based on the file arch/powerpc/mbxboot/m8260_tty.c from the + * Linux/PPC sources (m8260_tty.c had no copyright info in it). + * + * Martin Krause, 8 Jun 2006 + * Added CONFIG_SERIAL_MULTI support + */ + +/* + * Minimal serial functions needed to use one of the PSC ports + * as serial console interface. + */ + +#include <common.h> +#include <mpc5xxx.h> + +#if defined (CONFIG_SERIAL_MULTI) +#include <serial.h> +#endif + +DECLARE_GLOBAL_DATA_PTR; + +#if defined(CONFIG_PSC_CONSOLE) + +#if CONFIG_PSC_CONSOLE == 1 +#define PSC_BASE MPC5XXX_PSC1 +#elif CONFIG_PSC_CONSOLE == 2 +#define PSC_BASE MPC5XXX_PSC2 +#elif CONFIG_PSC_CONSOLE == 3 +#define PSC_BASE MPC5XXX_PSC3 +#elif CONFIG_PSC_CONSOLE == 4 +#define PSC_BASE MPC5XXX_PSC4 +#elif CONFIG_PSC_CONSOLE == 5 +#define PSC_BASE MPC5XXX_PSC5 +#elif CONFIG_PSC_CONSOLE == 6 +#define PSC_BASE MPC5XXX_PSC6 +#else +#error CONFIG_PSC_CONSOLE must be in 1 ... 6 +#endif + +#if defined(CONFIG_SERIAL_MULTI) && !defined(CONFIG_PSC_CONSOLE2) +#error you must define CONFIG_PSC_CONSOLE2 if CONFIG_SERIAL_MULTI is set +#endif + +#if defined(CONFIG_SERIAL_MULTI) +#if CONFIG_PSC_CONSOLE2 == 1 +#define PSC_BASE2 MPC5XXX_PSC1 +#elif CONFIG_PSC_CONSOLE2 == 2 +#define PSC_BASE2 MPC5XXX_PSC2 +#elif CONFIG_PSC_CONSOLE2 == 3 +#define PSC_BASE2 MPC5XXX_PSC3 +#elif CONFIG_PSC_CONSOLE2 == 4 +#define PSC_BASE2 MPC5XXX_PSC4 +#elif CONFIG_PSC_CONSOLE2 == 5 +#define PSC_BASE2 MPC5XXX_PSC5 +#elif CONFIG_PSC_CONSOLE2 == 6 +#define PSC_BASE2 MPC5XXX_PSC6 +#else +#error CONFIG_PSC_CONSOLE2 must be in 1 ... 6 +#endif +#endif /* CONFIG_SERIAL_MULTI */ + +#if defined(CONFIG_SERIAL_MULTI) +int serial_init_dev (unsigned long dev_base) +#else +int serial_init (void) +#endif +{ +#if defined(CONFIG_SERIAL_MULTI) + volatile struct mpc5xxx_psc *psc = (struct mpc5xxx_psc *)dev_base; +#else + volatile struct mpc5xxx_psc *psc = (struct mpc5xxx_psc *)PSC_BASE; +#endif + unsigned long baseclk; + int div; + + /* reset PSC */ + psc->command = PSC_SEL_MODE_REG_1; + + /* select clock sources */ + psc->psc_clock_select = 0; + baseclk = (gd->ipb_clk + 16) / 32; + + /* switch to UART mode */ + psc->sicr = 0; + + /* configure parity, bit length and so on */ + psc->mode = PSC_MODE_8_BITS | PSC_MODE_PARNONE; + psc->mode = PSC_MODE_ONE_STOP; + + /* set up UART divisor */ + div = (baseclk + (gd->baudrate/2)) / gd->baudrate; + psc->ctur = (div >> 8) & 0xff; + psc->ctlr = div & 0xff; + + /* disable all interrupts */ + psc->psc_imr = 0; + + /* reset and enable Rx/Tx */ + psc->command = PSC_RST_RX; + psc->command = PSC_RST_TX; + psc->command = PSC_RX_ENABLE | PSC_TX_ENABLE; + + return (0); +} + +#if defined(CONFIG_SERIAL_MULTI) +void serial_putc_dev (unsigned long dev_base, const char c) +#else +void serial_putc(const char c) +#endif +{ +#if defined(CONFIG_SERIAL_MULTI) + volatile struct mpc5xxx_psc *psc = (struct mpc5xxx_psc *)dev_base; +#else + volatile struct mpc5xxx_psc *psc = (struct mpc5xxx_psc *)PSC_BASE; +#endif + + if (c == '\n') +#if defined(CONFIG_SERIAL_MULTI) + serial_putc_dev (dev_base, '\r'); +#else + serial_putc('\r'); +#endif + + /* Wait for last character to go. */ + while (!(psc->psc_status & PSC_SR_TXEMP)) + ; + + psc->psc_buffer_8 = c; +} + +#if defined(CONFIG_SERIAL_MULTI) +void serial_putc_raw_dev(unsigned long dev_base, const char c) +#else +void serial_putc_raw(const char c) +#endif +{ +#if defined(CONFIG_SERIAL_MULTI) + volatile struct mpc5xxx_psc *psc = (struct mpc5xxx_psc *)dev_base; +#else + volatile struct mpc5xxx_psc *psc = (struct mpc5xxx_psc *)PSC_BASE; +#endif + /* Wait for last character to go. */ + while (!(psc->psc_status & PSC_SR_TXEMP)) + ; + + psc->psc_buffer_8 = c; +} + + +#if defined(CONFIG_SERIAL_MULTI) +void serial_puts_dev (unsigned long dev_base, const char *s) +#else +void serial_puts (const char *s) +#endif +{ + while (*s) { +#if defined(CONFIG_SERIAL_MULTI) + serial_putc_dev (dev_base, *s++); +#else + serial_putc (*s++); +#endif + } +} + +#if defined(CONFIG_SERIAL_MULTI) +int serial_getc_dev (unsigned long dev_base) +#else +int serial_getc(void) +#endif +{ +#if defined(CONFIG_SERIAL_MULTI) + volatile struct mpc5xxx_psc *psc = (struct mpc5xxx_psc *)dev_base; +#else + volatile struct mpc5xxx_psc *psc = (struct mpc5xxx_psc *)PSC_BASE; +#endif + + /* Wait for a character to arrive. */ + while (!(psc->psc_status & PSC_SR_RXRDY)) + ; + + return psc->psc_buffer_8; +} + +#if defined(CONFIG_SERIAL_MULTI) +int serial_tstc_dev (unsigned long dev_base) +#else +int serial_tstc(void) +#endif +{ +#if defined(CONFIG_SERIAL_MULTI) + volatile struct mpc5xxx_psc *psc = (struct mpc5xxx_psc *)dev_base; +#else + volatile struct mpc5xxx_psc *psc = (struct mpc5xxx_psc *)PSC_BASE; +#endif + + return (psc->psc_status & PSC_SR_RXRDY); +} + +#if defined(CONFIG_SERIAL_MULTI) +void serial_setbrg_dev (unsigned long dev_base) +#else +void serial_setbrg(void) +#endif +{ +#if defined(CONFIG_SERIAL_MULTI) + volatile struct mpc5xxx_psc *psc = (struct mpc5xxx_psc *)dev_base; +#else + volatile struct mpc5xxx_psc *psc = (struct mpc5xxx_psc *)PSC_BASE; +#endif + unsigned long baseclk, div; + + baseclk = (gd->ipb_clk + 16) / 32; + + /* set up UART divisor */ + div = (baseclk + (gd->baudrate/2)) / gd->baudrate; + psc->ctur = (div >> 8) & 0xFF; + psc->ctlr = div & 0xff; +} + +#if defined(CONFIG_SERIAL_MULTI) +void serial_setrts_dev (unsigned long dev_base, int s) +#else +void serial_setrts(int s) +#endif +{ +#if defined(CONFIG_SERIAL_MULTI) + volatile struct mpc5xxx_psc *psc = (struct mpc5xxx_psc *)dev_base; +#else + volatile struct mpc5xxx_psc *psc = (struct mpc5xxx_psc *)PSC_BASE; +#endif + + if (s) { + /* Assert RTS (become LOW) */ + psc->op1 = 0x1; + } + else { + /* Negate RTS (become HIGH) */ + psc->op0 = 0x1; + } +} + +#if defined(CONFIG_SERIAL_MULTI) +int serial_getcts_dev (unsigned long dev_base) +#else +int serial_getcts(void) +#endif +{ +#if defined(CONFIG_SERIAL_MULTI) + volatile struct mpc5xxx_psc *psc = (struct mpc5xxx_psc *)dev_base; +#else + volatile struct mpc5xxx_psc *psc = (struct mpc5xxx_psc *)PSC_BASE; +#endif + + return (psc->ip & 0x1) ? 0 : 1; +} + +#if defined(CONFIG_SERIAL_MULTI) +int serial0_init(void) +{ + return (serial_init_dev(PSC_BASE)); +} + +int serial1_init(void) +{ + return (serial_init_dev(PSC_BASE2)); +} +void serial0_setbrg (void) +{ + serial_setbrg_dev(PSC_BASE); +} +void serial1_setbrg (void) +{ + serial_setbrg_dev(PSC_BASE2); +} + +void serial0_putc(const char c) +{ + serial_putc_dev(PSC_BASE,c); +} + +void serial1_putc(const char c) +{ + serial_putc_dev(PSC_BASE2, c); +} +void serial0_puts(const char *s) +{ + serial_puts_dev(PSC_BASE, s); +} + +void serial1_puts(const char *s) +{ + serial_puts_dev(PSC_BASE2, s); +} + +int serial0_getc(void) +{ + return(serial_getc_dev(PSC_BASE)); +} + +int serial1_getc(void) +{ + return(serial_getc_dev(PSC_BASE2)); +} +int serial0_tstc(void) +{ + return (serial_tstc_dev(PSC_BASE)); +} + +int serial1_tstc(void) +{ + return (serial_tstc_dev(PSC_BASE2)); +} + +struct serial_device serial0_device = +{ + "serial0", + "UART0", + serial0_init, + serial0_setbrg, + serial0_getc, + serial0_tstc, + serial0_putc, + serial0_puts, +}; + +struct serial_device serial1_device = +{ + "serial1", + "UART1", + serial1_init, + serial1_setbrg, + serial1_getc, + serial1_tstc, + serial1_putc, + serial1_puts, +}; +#endif /* CONFIG_SERIAL_MULTI */ + +#endif /* CONFIG_PSC_CONSOLE */ diff --git a/arch/powerpc/cpu/mpc5xxx/speed.c b/arch/powerpc/cpu/mpc5xxx/speed.c new file mode 100644 index 0000000..8027d3e --- /dev/null +++ b/arch/powerpc/cpu/mpc5xxx/speed.c @@ -0,0 +1,94 @@ +/* + * (C) Copyright 2000-2002 + * Wolfgang Denk, DENX Software Engineering, wd@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> +#include <mpc5xxx.h> +#include <asm/processor.h> + +DECLARE_GLOBAL_DATA_PTR; + +/* ------------------------------------------------------------------------- */ + +/* Bus-to-Core Multipliers */ + +static int bus2core[] = { + 3, 2, 2, 2, 4, 4, 5, 9, + 6, 11, 8, 10, 3, 12, 7, 0, + 6, 5, 13, 2, 14, 4, 15, 9, + 0, 11, 8, 10, 16, 12, 7, 0 +}; +/* ------------------------------------------------------------------------- */ + +/* + * + */ + +int get_clocks (void) +{ + ulong val, vco; + +#if !defined(CONFIG_SYS_MPC5XXX_CLKIN) +#error clock measuring not implemented yet - define CONFIG_SYS_MPC5XXX_CLKIN +#endif + + val = *(vu_long *)MPC5XXX_CDM_PORCFG; + if (val & (1 << 6)) { + vco = CONFIG_SYS_MPC5XXX_CLKIN * 12; + } else { + vco = CONFIG_SYS_MPC5XXX_CLKIN * 16; + } + if (val & (1 << 5)) { + gd->bus_clk = vco / 8; + } else { + gd->bus_clk = vco / 4; + } + gd->cpu_clk = gd->bus_clk * bus2core[val & 0x1f] / 2; + + val = *(vu_long *)MPC5XXX_CDM_CFG; + if (val & (1 << 8)) { + gd->ipb_clk = gd->bus_clk / 2; + } else { + gd->ipb_clk = gd->bus_clk; + } + switch (val & 3) { + case 0: gd->pci_clk = gd->ipb_clk; break; + case 1: gd->pci_clk = gd->ipb_clk / 2; break; + default: gd->pci_clk = gd->bus_clk / 4; break; + } + + return (0); +} + +int prt_mpc5xxx_clks (void) +{ + char buf1[32], buf2[32], buf3[32]; + + printf (" Bus %s MHz, IPB %s MHz, PCI %s MHz\n", + strmhz(buf1, gd->bus_clk), + strmhz(buf2, gd->ipb_clk), + strmhz(buf3, gd->pci_clk) + ); + return (0); +} + +/* ------------------------------------------------------------------------- */ diff --git a/arch/powerpc/cpu/mpc5xxx/start.S b/arch/powerpc/cpu/mpc5xxx/start.S new file mode 100644 index 0000000..8b9f09b --- /dev/null +++ b/arch/powerpc/cpu/mpc5xxx/start.S @@ -0,0 +1,777 @@ +/* + * Copyright (C) 1998 Dan Malek <dmalek@jlc.net> + * Copyright (C) 1999 Magnus Damm <kieraypc01.p.y.kie.era.ericsson.se> + * Copyright (C) 2000 - 2003 Wolfgang Denk <wd@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 + */ + +/* + * U-Boot - Startup Code for MPC5xxx CPUs + */ +#include <config.h> +#include <mpc5xxx.h> +#include <timestamp.h> +#include <version.h> + +#define CONFIG_MPC5xxx 1 /* needed for Linux kernel header files */ +#define _LINUX_CONFIG_H 1 /* avoid reading Linux autoconf.h file */ + +#include <ppc_asm.tmpl> +#include <ppc_defs.h> + +#include <asm/cache.h> +#include <asm/mmu.h> + +#ifndef CONFIG_IDENT_STRING +#define CONFIG_IDENT_STRING "" +#endif + +/* We don't want the MMU yet. +*/ +#undef MSR_KERNEL +/* Floating Point enable, Machine Check and Recoverable Interr. */ +#ifdef DEBUG +#define MSR_KERNEL (MSR_FP|MSR_RI) +#else +#define MSR_KERNEL (MSR_FP|MSR_ME|MSR_RI) +#endif + +/* + * Set up GOT: Global Offset Table + * + * Use r12 to access the GOT + */ + START_GOT + GOT_ENTRY(_GOT2_TABLE_) + GOT_ENTRY(_FIXUP_TABLE_) + + GOT_ENTRY(_start) + GOT_ENTRY(_start_of_vectors) + GOT_ENTRY(_end_of_vectors) + GOT_ENTRY(transfer_to_handler) + + GOT_ENTRY(__init_end) + GOT_ENTRY(_end) + GOT_ENTRY(__bss_start) + END_GOT + +/* + * Version string + */ + .data + .globl version_string +version_string: + .ascii U_BOOT_VERSION + .ascii " (", U_BOOT_DATE, " - ", U_BOOT_TIME, ")" + .ascii CONFIG_IDENT_STRING, "\0" + +/* + * Exception vectors + */ + .text + . = EXC_OFF_SYS_RESET + .globl _start +_start: + li r21, BOOTFLAG_COLD /* Normal Power-On */ + nop + b boot_cold + + . = EXC_OFF_SYS_RESET + 0x10 + + .globl _start_warm +_start_warm: + li r21, BOOTFLAG_WARM /* Software reboot */ + b boot_warm + +boot_cold: +boot_warm: + mfmsr r5 /* save msr contents */ + + /* Move CSBoot and adjust instruction pointer */ + /*--------------------------------------------------------------*/ + +#if defined(CONFIG_SYS_LOWBOOT) +# if defined(CONFIG_SYS_RAMBOOT) +# error CONFIG_SYS_LOWBOOT is incompatible with CONFIG_SYS_RAMBOOT +# endif /* CONFIG_SYS_RAMBOOT */ + lis r4, CONFIG_SYS_DEFAULT_MBAR@h + lis r3, START_REG(CONFIG_SYS_BOOTCS_START)@h + ori r3, r3, START_REG(CONFIG_SYS_BOOTCS_START)@l + stw r3, 0x4(r4) /* CS0 start */ + lis r3, STOP_REG(CONFIG_SYS_BOOTCS_START, CONFIG_SYS_BOOTCS_SIZE)@h + ori r3, r3, STOP_REG(CONFIG_SYS_BOOTCS_START, CONFIG_SYS_BOOTCS_SIZE)@l + stw r3, 0x8(r4) /* CS0 stop */ + lis r3, 0x02010000@h + ori r3, r3, 0x02010000@l + stw r3, 0x54(r4) /* CS0 and Boot enable */ + + lis r3, lowboot_reentry@h /* jump from bootlow address space (0x0000xxxx) */ + ori r3, r3, lowboot_reentry@l /* to the address space the linker used */ + mtlr r3 + blr + +lowboot_reentry: + lis r3, START_REG(CONFIG_SYS_BOOTCS_START)@h + ori r3, r3, START_REG(CONFIG_SYS_BOOTCS_START)@l + stw r3, 0x4c(r4) /* Boot start */ + lis r3, STOP_REG(CONFIG_SYS_BOOTCS_START, CONFIG_SYS_BOOTCS_SIZE)@h + ori r3, r3, STOP_REG(CONFIG_SYS_BOOTCS_START, CONFIG_SYS_BOOTCS_SIZE)@l + stw r3, 0x50(r4) /* Boot stop */ + lis r3, 0x02000001@h + ori r3, r3, 0x02000001@l + stw r3, 0x54(r4) /* Boot enable, CS0 disable */ +#endif /* CONFIG_SYS_LOWBOOT */ + +#if defined(CONFIG_SYS_DEFAULT_MBAR) && !defined(CONFIG_SYS_RAMBOOT) + lis r3, CONFIG_SYS_MBAR@h + ori r3, r3, CONFIG_SYS_MBAR@l + /* MBAR is mirrored into the MBAR SPR */ + mtspr MBAR,r3 + rlwinm r3, r3, 16, 16, 31 + lis r4, CONFIG_SYS_DEFAULT_MBAR@h + stw r3, 0(r4) +#endif /* CONFIG_SYS_DEFAULT_MBAR */ + + /* Initialise the MPC5xxx processor core */ + /*--------------------------------------------------------------*/ + + bl init_5xxx_core + + /* initialize some things that are hard to access from C */ + /*--------------------------------------------------------------*/ + + /* set up stack in on-chip SRAM */ + lis r3, CONFIG_SYS_INIT_RAM_ADDR@h + ori r3, r3, CONFIG_SYS_INIT_RAM_ADDR@l + ori r1, r3, CONFIG_SYS_INIT_SP_OFFSET + li r0, 0 /* Make room for stack frame header and */ + stwu r0, -4(r1) /* clear final stack frame so that */ + stwu r0, -4(r1) /* stack backtraces terminate cleanly */ + + /* let the C-code set up the rest */ + /* */ + /* Be careful to keep code relocatable ! */ + /*--------------------------------------------------------------*/ + + GET_GOT /* initialize GOT access */ + + /* r3: IMMR */ + bl cpu_init_f /* run low-level CPU init code (in Flash)*/ + + mr r3, r21 + /* r3: BOOTFLAG */ + bl board_init_f /* run 1st part of board init code (in Flash)*/ + +/* + * Vector Table + */ + + .globl _start_of_vectors +_start_of_vectors: + +/* Machine check */ + STD_EXCEPTION(0x200, MachineCheck, MachineCheckException) + +/* Data Storage exception. */ + STD_EXCEPTION(0x300, DataStorage, UnknownException) + +/* Instruction Storage exception. */ + STD_EXCEPTION(0x400, InstStorage, UnknownException) + +/* External Interrupt exception. */ + STD_EXCEPTION(0x500, ExtInterrupt, external_interrupt) + +/* Alignment exception. */ + . = 0x600 +Alignment: + EXCEPTION_PROLOG(SRR0, SRR1) + mfspr r4,DAR + stw r4,_DAR(r21) + mfspr r5,DSISR + stw r5,_DSISR(r21) + addi r3,r1,STACK_FRAME_OVERHEAD + EXC_XFER_TEMPLATE(Alignment, AlignmentException, MSR_KERNEL, COPY_EE) + +/* Program check exception */ + . = 0x700 +ProgramCheck: + EXCEPTION_PROLOG(SRR0, SRR1) + addi r3,r1,STACK_FRAME_OVERHEAD + EXC_XFER_TEMPLATE(ProgramCheck, ProgramCheckException, + MSR_KERNEL, COPY_EE) + + STD_EXCEPTION(0x800, FPUnavailable, UnknownException) + + /* I guess we could implement decrementer, and may have + * to someday for timekeeping. + */ + STD_EXCEPTION(0x900, Decrementer, timer_interrupt) + + STD_EXCEPTION(0xa00, Trap_0a, UnknownException) + STD_EXCEPTION(0xb00, Trap_0b, UnknownException) + STD_EXCEPTION(0xc00, SystemCall, UnknownException) + STD_EXCEPTION(0xd00, SingleStep, UnknownException) + + STD_EXCEPTION(0xe00, Trap_0e, UnknownException) + STD_EXCEPTION(0xf00, Trap_0f, UnknownException) + + STD_EXCEPTION(0x1000, InstructionTLBMiss, UnknownException) + STD_EXCEPTION(0x1100, DataLoadTLBMiss, UnknownException) + STD_EXCEPTION(0x1200, DataStoreTLBMiss, UnknownException) +#ifdef DEBUG + . = 0x1300 + /* + * This exception occurs when the program counter matches the + * Instruction Address Breakpoint Register (IABR). + * + * I want the cpu to halt if this occurs so I can hunt around + * with the debugger and look at things. + * + * When DEBUG is defined, both machine check enable (in the MSR) + * and checkstop reset enable (in the reset mode register) are + * turned off and so a checkstop condition will result in the cpu + * halting. + * + * I force the cpu into a checkstop condition by putting an illegal + * instruction here (at least this is the theory). + * + * well - that didnt work, so just do an infinite loop! + */ +1: b 1b +#else + STD_EXCEPTION(0x1300, InstructionBreakpoint, DebugException) +#endif + STD_EXCEPTION(0x1400, SMI, UnknownException) + + STD_EXCEPTION(0x1500, Trap_15, UnknownException) + STD_EXCEPTION(0x1600, Trap_16, UnknownException) + STD_EXCEPTION(0x1700, Trap_17, UnknownException) + STD_EXCEPTION(0x1800, Trap_18, UnknownException) + STD_EXCEPTION(0x1900, Trap_19, UnknownException) + STD_EXCEPTION(0x1a00, Trap_1a, UnknownException) + STD_EXCEPTION(0x1b00, Trap_1b, UnknownException) + STD_EXCEPTION(0x1c00, Trap_1c, UnknownException) + STD_EXCEPTION(0x1d00, Trap_1d, UnknownException) + STD_EXCEPTION(0x1e00, Trap_1e, UnknownException) + STD_EXCEPTION(0x1f00, Trap_1f, UnknownException) + STD_EXCEPTION(0x2000, Trap_20, UnknownException) + STD_EXCEPTION(0x2100, Trap_21, UnknownException) + STD_EXCEPTION(0x2200, Trap_22, UnknownException) + STD_EXCEPTION(0x2300, Trap_23, UnknownException) + STD_EXCEPTION(0x2400, Trap_24, UnknownException) + STD_EXCEPTION(0x2500, Trap_25, UnknownException) + STD_EXCEPTION(0x2600, Trap_26, UnknownException) + STD_EXCEPTION(0x2700, Trap_27, UnknownException) + STD_EXCEPTION(0x2800, Trap_28, UnknownException) + STD_EXCEPTION(0x2900, Trap_29, UnknownException) + STD_EXCEPTION(0x2a00, Trap_2a, UnknownException) + STD_EXCEPTION(0x2b00, Trap_2b, UnknownException) + STD_EXCEPTION(0x2c00, Trap_2c, UnknownException) + STD_EXCEPTION(0x2d00, Trap_2d, UnknownException) + STD_EXCEPTION(0x2e00, Trap_2e, UnknownException) + STD_EXCEPTION(0x2f00, Trap_2f, UnknownException) + + + .globl _end_of_vectors +_end_of_vectors: + + . = 0x3000 + +/* + * This code finishes saving the registers to the exception frame + * and jumps to the appropriate handler for the exception. + * Register r21 is pointer into trap frame, r1 has new stack pointer. + */ + .globl transfer_to_handler +transfer_to_handler: + stw r22,_NIP(r21) + lis r22,MSR_POW@h + andc r23,r23,r22 + stw r23,_MSR(r21) + SAVE_GPR(7, r21) + SAVE_4GPRS(8, r21) + SAVE_8GPRS(12, r21) + SAVE_8GPRS(24, r21) + mflr r23 + andi. r24,r23,0x3f00 /* get vector offset */ + stw r24,TRAP(r21) + li r22,0 + stw r22,RESULT(r21) + lwz r24,0(r23) /* virtual address of handler */ + lwz r23,4(r23) /* where to go when done */ + mtspr SRR0,r24 + mtspr SRR1,r20 + mtlr r23 + SYNC + rfi /* jump to handler, enable MMU */ + +int_return: + mfmsr r28 /* Disable interrupts */ + li r4,0 + ori r4,r4,MSR_EE + andc r28,r28,r4 + SYNC /* Some chip revs need this... */ + mtmsr r28 + SYNC + lwz r2,_CTR(r1) + lwz r0,_LINK(r1) + mtctr r2 + mtlr r0 + lwz r2,_XER(r1) + lwz r0,_CCR(r1) + mtspr XER,r2 + mtcrf 0xFF,r0 + REST_10GPRS(3, r1) + REST_10GPRS(13, r1) + REST_8GPRS(23, r1) + REST_GPR(31, r1) + lwz r2,_NIP(r1) /* Restore environment */ + lwz r0,_MSR(r1) + mtspr SRR0,r2 + mtspr SRR1,r0 + lwz r0,GPR0(r1) + lwz r2,GPR2(r1) + lwz r1,GPR1(r1) + SYNC + rfi + +/* + * This code initialises the MPC5xxx processor core + * (conforms to PowerPC 603e spec) + * Note: expects original MSR contents to be in r5. + */ + + .globl init_5xx_core +init_5xxx_core: + + /* Initialize machine status; enable machine check interrupt */ + /*--------------------------------------------------------------*/ + + li r3, MSR_KERNEL /* Set ME and RI flags */ + rlwimi r3, r5, 0, 25, 25 /* preserve IP bit set by HRCW */ +#ifdef DEBUG + rlwimi r3, r5, 0, 21, 22 /* debugger might set SE & BE bits */ +#endif + SYNC /* Some chip revs need this... */ + mtmsr r3 + SYNC + mtspr SRR1, r3 /* Make SRR1 match MSR */ + + /* Initialize the Hardware Implementation-dependent Registers */ + /* HID0 also contains cache control */ + /*--------------------------------------------------------------*/ + + lis r3, CONFIG_SYS_HID0_INIT@h + ori r3, r3, CONFIG_SYS_HID0_INIT@l + SYNC + mtspr HID0, r3 + + lis r3, CONFIG_SYS_HID0_FINAL@h + ori r3, r3, CONFIG_SYS_HID0_FINAL@l + SYNC + mtspr HID0, r3 + + /* clear all BAT's */ + /*--------------------------------------------------------------*/ + + li r0, 0 + mtspr DBAT0U, r0 + mtspr DBAT0L, r0 + mtspr DBAT1U, r0 + mtspr DBAT1L, r0 + mtspr DBAT2U, r0 + mtspr DBAT2L, r0 + mtspr DBAT3U, r0 + mtspr DBAT3L, r0 + mtspr DBAT4U, r0 + mtspr DBAT4L, r0 + mtspr DBAT5U, r0 + mtspr DBAT5L, r0 + mtspr DBAT6U, r0 + mtspr DBAT6L, r0 + mtspr DBAT7U, r0 + mtspr DBAT7L, r0 + mtspr IBAT0U, r0 + mtspr IBAT0L, r0 + mtspr IBAT1U, r0 + mtspr IBAT1L, r0 + mtspr IBAT2U, r0 + mtspr IBAT2L, r0 + mtspr IBAT3U, r0 + mtspr IBAT3L, r0 + mtspr IBAT4U, r0 + mtspr IBAT4L, r0 + mtspr IBAT5U, r0 + mtspr IBAT5L, r0 + mtspr IBAT6U, r0 + mtspr IBAT6L, r0 + mtspr IBAT7U, r0 + mtspr IBAT7L, r0 + SYNC + + /* invalidate all tlb's */ + /* */ + /* From the 603e User Manual: "The 603e provides the ability to */ + /* invalidate a TLB entry. The TLB Invalidate Entry (tlbie) */ + /* instruction invalidates the TLB entry indexed by the EA, and */ + /* operates on both the instruction and data TLBs simultaneously*/ + /* invalidating four TLB entries (both sets in each TLB). The */ + /* index corresponds to bits 15-19 of the EA. To invalidate all */ + /* entries within both TLBs, 32 tlbie instructions should be */ + /* issued, incrementing this field by one each time." */ + /* */ + /* "Note that the tlbia instruction is not implemented on the */ + /* 603e." */ + /* */ + /* bits 15-19 correspond to addresses 0x00000000 to 0x0001F000 */ + /* incrementing by 0x1000 each time. The code below is sort of */ + /* based on code in "flush_tlbs" from arch/powerpc/kernel/head.S */ + /* */ + /*--------------------------------------------------------------*/ + + li r3, 32 + mtctr r3 + li r3, 0 +1: tlbie r3 + addi r3, r3, 0x1000 + bdnz 1b + SYNC + + /* Done! */ + /*--------------------------------------------------------------*/ + + blr + +/* Cache functions. + * + * Note: requires that all cache bits in + * HID0 are in the low half word. + */ + .globl icache_enable +icache_enable: + mfspr r3, HID0 + ori r3, r3, HID0_ICE + lis r4, 0 + ori r4, r4, HID0_ILOCK + andc r3, r3, r4 + ori r4, r3, HID0_ICFI + isync + mtspr HID0, r4 /* sets enable and invalidate, clears lock */ + isync + mtspr HID0, r3 /* clears invalidate */ + blr + + .globl icache_disable +icache_disable: + mfspr r3, HID0 + lis r4, 0 + ori r4, r4, HID0_ICE|HID0_ILOCK + andc r3, r3, r4 + ori r4, r3, HID0_ICFI + isync + mtspr HID0, r4 /* sets invalidate, clears enable and lock */ + isync + mtspr HID0, r3 /* clears invalidate */ + blr + + .globl icache_status +icache_status: + mfspr r3, HID0 + rlwinm r3, r3, HID0_ICE_BITPOS + 1, 31, 31 + blr + + .globl dcache_enable +dcache_enable: + mfspr r3, HID0 + ori r3, r3, HID0_DCE + lis r4, 0 + ori r4, r4, HID0_DLOCK + andc r3, r3, r4 + ori r4, r3, HID0_DCI + sync + mtspr HID0, r4 /* sets enable and invalidate, clears lock */ + sync + mtspr HID0, r3 /* clears invalidate */ + blr + + .globl dcache_disable +dcache_disable: + mfspr r3, HID0 + lis r4, 0 + ori r4, r4, HID0_DCE|HID0_DLOCK + andc r3, r3, r4 + ori r4, r3, HID0_DCI + sync + mtspr HID0, r4 /* sets invalidate, clears enable and lock */ + sync + mtspr HID0, r3 /* clears invalidate */ + blr + + .globl dcache_status +dcache_status: + mfspr r3, HID0 + rlwinm r3, r3, HID0_DCE_BITPOS + 1, 31, 31 + blr + + .globl get_svr +get_svr: + mfspr r3, SVR + blr + + .globl get_pvr +get_pvr: + mfspr r3, PVR + blr + +/*------------------------------------------------------------------------------*/ + +/* + * void relocate_code (addr_sp, gd, addr_moni) + * + * This "function" does not return, instead it continues in RAM + * after relocating the monitor code. + * + * r3 = dest + * r4 = src + * r5 = length in bytes + * r6 = cachelinesize + */ + .globl relocate_code +relocate_code: + mr r1, r3 /* Set new stack pointer */ + mr r9, r4 /* Save copy of Global Data pointer */ + mr r10, r5 /* Save copy of Destination Address */ + + GET_GOT + mr r3, r5 /* Destination Address */ + lis r4, CONFIG_SYS_MONITOR_BASE@h /* Source Address */ + ori r4, r4, CONFIG_SYS_MONITOR_BASE@l + lwz r5, GOT(__init_end) + sub r5, r5, r4 + li r6, CONFIG_SYS_CACHELINE_SIZE /* Cache Line Size */ + + /* + * Fix GOT pointer: + * + * New GOT-PTR = (old GOT-PTR - CONFIG_SYS_MONITOR_BASE) + Destination Address + * + * Offset: + */ + sub r15, r10, r4 + + /* First our own GOT */ + add r12, r12, r15 + /* then the one used by the C code */ + add r30, r30, r15 + + /* + * Now relocate code + */ + + cmplw cr1,r3,r4 + addi r0,r5,3 + srwi. r0,r0,2 + beq cr1,4f /* In place copy is not necessary */ + beq 7f /* Protect against 0 count */ + mtctr r0 + bge cr1,2f + + la r8,-4(r4) + la r7,-4(r3) +1: lwzu r0,4(r8) + stwu r0,4(r7) + bdnz 1b + b 4f + +2: slwi r0,r0,2 + add r8,r4,r0 + add r7,r3,r0 +3: lwzu r0,-4(r8) + stwu r0,-4(r7) + bdnz 3b + +/* + * Now flush the cache: note that we must start from a cache aligned + * address. Otherwise we might miss one cache line. + */ +4: cmpwi r6,0 + add r5,r3,r5 + beq 7f /* Always flush prefetch queue in any case */ + subi r0,r6,1 + andc r3,r3,r0 + mfspr r7,HID0 /* don't do dcbst if dcache is disabled */ + rlwinm r7,r7,HID0_DCE_BITPOS+1,31,31 + cmpwi r7,0 + beq 9f + mr r4,r3 +5: dcbst 0,r4 + add r4,r4,r6 + cmplw r4,r5 + blt 5b + sync /* Wait for all dcbst to complete on bus */ +9: mfspr r7,HID0 /* don't do icbi if icache is disabled */ + rlwinm r7,r7,HID0_ICE_BITPOS+1,31,31 + cmpwi r7,0 + beq 7f + mr r4,r3 +6: icbi 0,r4 + add r4,r4,r6 + cmplw r4,r5 + blt 6b +7: sync /* Wait for all icbi to complete on bus */ + isync + +/* + * We are done. Do not return, instead branch to second part of board + * initialization, now running from RAM. + */ + + addi r0, r10, in_ram - _start + EXC_OFF_SYS_RESET + mtlr r0 + blr + +in_ram: + + /* + * Relocation Function, r12 point to got2+0x8000 + * + * Adjust got2 pointers, no need to check for 0, this code + * already puts a few entries in the table. + */ + li r0,__got2_entries@sectoff@l + la r3,GOT(_GOT2_TABLE_) + lwz r11,GOT(_GOT2_TABLE_) + mtctr r0 + sub r11,r3,r11 + addi r3,r3,-4 +1: lwzu r0,4(r3) + cmpwi r0,0 + beq- 2f + add r0,r0,r11 + stw r0,0(r3) +2: bdnz 1b + + /* + * Now adjust the fixups and the pointers to the fixups + * in case we need to move ourselves again. + */ + li r0,__fixup_entries@sectoff@l + lwz r3,GOT(_FIXUP_TABLE_) + cmpwi r0,0 + mtctr r0 + addi r3,r3,-4 + beq 4f +3: lwzu r4,4(r3) + lwzux r0,r4,r11 + add r0,r0,r11 + stw r10,0(r3) + stw r0,0(r4) + bdnz 3b +4: +clear_bss: + /* + * Now clear BSS segment + */ + lwz r3,GOT(__bss_start) + lwz r4,GOT(_end) + + cmplw 0, r3, r4 + beq 6f + + li r0, 0 +5: + stw r0, 0(r3) + addi r3, r3, 4 + cmplw 0, r3, r4 + bne 5b +6: + + mr r3, r9 /* Global Data pointer */ + mr r4, r10 /* Destination Address */ + bl board_init_r + + /* + * Copy exception vector code to low memory + * + * r3: dest_addr + * r7: source address, r8: end address, r9: target address + */ + .globl trap_init +trap_init: + mflr r4 /* save link register */ + GET_GOT + lwz r7, GOT(_start) + lwz r8, GOT(_end_of_vectors) + + li r9, 0x100 /* reset vector always at 0x100 */ + + cmplw 0, r7, r8 + bgelr /* return if r7>=r8 - just in case */ +1: + lwz r0, 0(r7) + stw r0, 0(r9) + addi r7, r7, 4 + addi r9, r9, 4 + cmplw 0, r7, r8 + bne 1b + + /* + * relocate `hdlr' and `int_return' entries + */ + li r7, .L_MachineCheck - _start + EXC_OFF_SYS_RESET + li r8, Alignment - _start + EXC_OFF_SYS_RESET +2: + bl trap_reloc + addi r7, r7, 0x100 /* next exception vector */ + cmplw 0, r7, r8 + blt 2b + + li r7, .L_Alignment - _start + EXC_OFF_SYS_RESET + bl trap_reloc + + li r7, .L_ProgramCheck - _start + EXC_OFF_SYS_RESET + bl trap_reloc + + li r7, .L_FPUnavailable - _start + EXC_OFF_SYS_RESET + li r8, SystemCall - _start + EXC_OFF_SYS_RESET +3: + bl trap_reloc + addi r7, r7, 0x100 /* next exception vector */ + cmplw 0, r7, r8 + blt 3b + + li r7, .L_SingleStep - _start + EXC_OFF_SYS_RESET + li r8, _end_of_vectors - _start + EXC_OFF_SYS_RESET +4: + bl trap_reloc + addi r7, r7, 0x100 /* next exception vector */ + cmplw 0, r7, r8 + blt 4b + + mfmsr r3 /* now that the vectors have */ + lis r7, MSR_IP@h /* relocated into low memory */ + ori r7, r7, MSR_IP@l /* MSR[IP] can be turned off */ + andc r3, r3, r7 /* (if it was on) */ + SYNC /* Some chip revs need this... */ + mtmsr r3 + SYNC + + mtlr r4 /* restore link register */ + blr diff --git a/arch/powerpc/cpu/mpc5xxx/traps.c b/arch/powerpc/cpu/mpc5xxx/traps.c new file mode 100644 index 0000000..5972f34 --- /dev/null +++ b/arch/powerpc/cpu/mpc5xxx/traps.c @@ -0,0 +1,245 @@ +/* + * linux/arch/powerpc/kernel/traps.c + * + * Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org) + * + * Modified by Cort Dougan (cort@cs.nmt.edu) + * and Paul Mackerras (paulus@cs.anu.edu.au) + * fixed Machine Check Reasons by Reinhard Meyer (r.meyer@emk-elektronik.de) + * + * (C) Copyright 2000-2003 + * Wolfgang Denk, DENX Software Engineering, wd@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 + */ + +/* + * This file handles the architecture-dependent parts of hardware exceptions + */ + +#include <common.h> +#include <command.h> +#include <kgdb.h> +#include <asm/processor.h> + +/* Returns 0 if exception not found and fixup otherwise. */ +extern unsigned long search_exception_table(unsigned long); + +/* THIS NEEDS CHANGING to use the board info structure. +*/ +#define END_OF_MEM 0x02000000 + +/* + * Trap & Exception support + */ + +void +print_backtrace(unsigned long *sp) +{ + int cnt = 0; + unsigned long i; + + printf("Call backtrace: "); + while (sp) { + if ((uint)sp > END_OF_MEM) + break; + + i = sp[1]; + if (cnt++ % 7 == 0) + printf("\n"); + printf("%08lX ", i); + if (cnt > 32) break; + sp = (unsigned long *)*sp; + } + printf("\n"); +} + +void show_regs(struct pt_regs * regs) +{ + int i; + + printf("NIP: %08lX XER: %08lX LR: %08lX REGS: %p TRAP: %04lx DAR: %08lX\n", + regs->nip, regs->xer, regs->link, regs, regs->trap, regs->dar); + printf("MSR: %08lx EE: %01x PR: %01x FP: %01x ME: %01x IR/DR: %01x%01x\n", + regs->msr, regs->msr&MSR_EE ? 1 : 0, regs->msr&MSR_PR ? 1 : 0, + regs->msr & MSR_FP ? 1 : 0,regs->msr&MSR_ME ? 1 : 0, + regs->msr&MSR_IR ? 1 : 0, + regs->msr&MSR_DR ? 1 : 0); + + printf("\n"); + for (i = 0; i < 32; i++) { + if ((i % 8) == 0) + { + printf("GPR%02d: ", i); + } + + printf("%08lX ", regs->gpr[i]); + if ((i % 8) == 7) + { + printf("\n"); + } + } +} + + +void +_exception(int signr, struct pt_regs *regs) +{ + show_regs(regs); + print_backtrace((unsigned long *)regs->gpr[1]); + panic("Exception in kernel pc %lx signal %d",regs->nip,signr); +} + +void +MachineCheckException(struct pt_regs *regs) +{ + unsigned long fixup; + + /* Probing PCI using config cycles cause this exception + * when a device is not present. Catch it and return to + * the PCI exception handler. + */ + if ((fixup = search_exception_table(regs->nip)) != 0) { + regs->nip = fixup; + return; + } + +#if defined(CONFIG_CMD_KGDB) + if (debugger_exception_handler && (*debugger_exception_handler)(regs)) + return; +#endif + + printf("Machine check in kernel mode.\n"); + printf("Caused by (from msr): "); + printf("regs %p ",regs); + /* refer to 603e Manual (MPC603EUM/AD), chapter 4.5.2.1 */ + switch( regs->msr & 0x000F0000) + { + case (0x80000000>>12) : + printf("Machine check signal - probably due to mm fault\n" + "with mmu off\n"); + break; + case (0x80000000>>13) : + printf("Transfer error ack signal\n"); + break; + case (0x80000000>>14) : + printf("Data parity signal\n"); + break; + case (0x80000000>>15) : + printf("Address parity signal\n"); + break; + default: + printf("Unknown values in msr\n"); + } + show_regs(regs); + print_backtrace((unsigned long *)regs->gpr[1]); + panic("machine check"); +} + +void +AlignmentException(struct pt_regs *regs) +{ +#if defined(CONFIG_CMD_KGDB) + if (debugger_exception_handler && (*debugger_exception_handler)(regs)) + return; +#endif + show_regs(regs); + print_backtrace((unsigned long *)regs->gpr[1]); + panic("Alignment Exception"); +} + +void +ProgramCheckException(struct pt_regs *regs) +{ +#if defined(CONFIG_CMD_KGDB) + if (debugger_exception_handler && (*debugger_exception_handler)(regs)) + return; +#endif + show_regs(regs); + print_backtrace((unsigned long *)regs->gpr[1]); + panic("Program Check Exception"); +} + +void +SoftEmuException(struct pt_regs *regs) +{ +#if defined(CONFIG_CMD_KGDB) + if (debugger_exception_handler && (*debugger_exception_handler)(regs)) + return; +#endif + show_regs(regs); + print_backtrace((unsigned long *)regs->gpr[1]); + panic("Software Emulation Exception"); +} + + +void +UnknownException(struct pt_regs *regs) +{ +#if defined(CONFIG_CMD_KGDB) + if (debugger_exception_handler && (*debugger_exception_handler)(regs)) + return; +#endif + printf("Bad trap at PC: %lx, SR: %lx, vector=%lx\n", + regs->nip, regs->msr, regs->trap); + _exception(0, regs); +} + +#if defined(CONFIG_CMD_BEDBUG) +extern void do_bedbug_breakpoint(struct pt_regs *); +#endif + +void +DebugException(struct pt_regs *regs) +{ + + printf("Debugger trap at @ %lx\n", regs->nip ); + show_regs(regs); +#if defined(CONFIG_CMD_BEDBUG) + do_bedbug_breakpoint( regs ); +#endif +} + +/* Probe an address by reading. If not present, return -1, otherwise + * return 0. + */ +int +addr_probe(uint *addr) +{ +#if 0 + int retval; + + __asm__ __volatile__( \ + "1: lwz %0,0(%1)\n" \ + " eieio\n" \ + " li %0,0\n" \ + "2:\n" \ + ".section .fixup,\"ax\"\n" \ + "3: li %0,-1\n" \ + " b 2b\n" \ + ".section __ex_table,\"a\"\n" \ + " .align 2\n" \ + " .long 1b,3b\n" \ + ".text" \ + : "=r" (retval) : "r"(addr)); + + return (retval); +#endif + return 0; +} diff --git a/arch/powerpc/cpu/mpc5xxx/u-boot-customlayout.lds b/arch/powerpc/cpu/mpc5xxx/u-boot-customlayout.lds new file mode 100644 index 0000000..ecffc1b --- /dev/null +++ b/arch/powerpc/cpu/mpc5xxx/u-boot-customlayout.lds @@ -0,0 +1,133 @@ +/* + * (C) Copyright 2003-2004 + * Wolfgang Denk, DENX Software Engineering, wd@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 + */ + +OUTPUT_ARCH(powerpc) +/* Do we need any of these for elf? + __DYNAMIC = 0; */ +SECTIONS +{ + /* Read-only sections, merged into text segment: */ + . = + SIZEOF_HEADERS; + .interp : { *(.interp) } + .hash : { *(.hash) } + .dynsym : { *(.dynsym) } + .dynstr : { *(.dynstr) } + .rel.text : { *(.rel.text) } + .rela.text : { *(.rela.text) } + .rel.data : { *(.rel.data) } + .rela.data : { *(.rela.data) } + .rel.rodata : { *(.rel.rodata) } + .rela.rodata : { *(.rela.rodata) } + .rel.got : { *(.rel.got) } + .rela.got : { *(.rela.got) } + .rel.ctors : { *(.rel.ctors) } + .rela.ctors : { *(.rela.ctors) } + .rel.dtors : { *(.rel.dtors) } + .rela.dtors : { *(.rela.dtors) } + .rel.bss : { *(.rel.bss) } + .rela.bss : { *(.rela.bss) } + .rel.plt : { *(.rel.plt) } + .rela.plt : { *(.rela.plt) } + .init : { *(.init) } + .plt : { *(.plt) } + .text : + { + /* WARNING - the following is hand-optimized to fit within */ + /* the sector layout of our flash chips! XXX FIXME XXX */ + + arch/powerpc/cpu/mpc5xxx/start.o (.text) + arch/powerpc/cpu/mpc5xxx/traps.o (.text) + lib/crc32.o (.text) + arch/powerpc/lib/cache.o (.text) + arch/powerpc/lib/time.o (.text) + + . = DEFINED(env_offset) ? env_offset : .; + common/env_embedded.o (.ppcenv) + + *(.text) + *(.got1) + . = ALIGN(16); + *(.eh_frame) + *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.rodata*))) + } + .fini : { *(.fini) } =0 + .ctors : { *(.ctors) } + .dtors : { *(.dtors) } + + /* Read-write section, merged into data segment: */ + . = (. + 0x0FFF) & 0xFFFFF000; + _erotext = .; + PROVIDE (erotext = .); + .reloc : + { + *(.got) + _GOT2_TABLE_ = .; + *(.got2) + _FIXUP_TABLE_ = .; + *(.fixup) + } + __got2_entries = (_FIXUP_TABLE_ - _GOT2_TABLE_) >> 2; + __fixup_entries = (. - _FIXUP_TABLE_) >> 2; + + .data : + { + *(.data) + *(.data1) + *(.sdata) + *(.sdata2) + *(.dynamic) + CONSTRUCTORS + } + _edata = .; + PROVIDE (edata = .); + + . = .; + __u_boot_cmd_start = .; + .u_boot_cmd : { *(.u_boot_cmd) } + __u_boot_cmd_end = .; + + + . = .; + __start___ex_table = .; + __ex_table : { *(__ex_table) } + __stop___ex_table = .; + + . = ALIGN(4096); + __init_begin = .; + .text.init : { *(.text.init) } + .data.init : { *(.data.init) } + . = ALIGN(4096); + __init_end = .; + + __bss_start = .; + .bss (NOLOAD) : + { + *(.sbss) *(.scommon) + *(.dynbss) + *(.bss) + *(COMMON) + . = ALIGN(4); + } + _end = . ; + PROVIDE (end = .); +} diff --git a/arch/powerpc/cpu/mpc5xxx/u-boot.lds b/arch/powerpc/cpu/mpc5xxx/u-boot.lds new file mode 100644 index 0000000..ea4060d --- /dev/null +++ b/arch/powerpc/cpu/mpc5xxx/u-boot.lds @@ -0,0 +1,122 @@ +/* + * (C) Copyright 2003-2007 + * Wolfgang Denk, DENX Software Engineering, wd@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 + */ + +OUTPUT_ARCH(powerpc) +/* Do we need any of these for elf? + __DYNAMIC = 0; */ +SECTIONS +{ + /* Read-only sections, merged into text segment: */ + . = + SIZEOF_HEADERS; + .interp : { *(.interp) } + .hash : { *(.hash) } + .dynsym : { *(.dynsym) } + .dynstr : { *(.dynstr) } + .rel.text : { *(.rel.text) } + .rela.text : { *(.rela.text) } + .rel.data : { *(.rel.data) } + .rela.data : { *(.rela.data) } + .rel.rodata : { *(.rel.rodata) } + .rela.rodata : { *(.rela.rodata) } + .rel.got : { *(.rel.got) } + .rela.got : { *(.rela.got) } + .rel.ctors : { *(.rel.ctors) } + .rela.ctors : { *(.rela.ctors) } + .rel.dtors : { *(.rel.dtors) } + .rela.dtors : { *(.rela.dtors) } + .rel.bss : { *(.rel.bss) } + .rela.bss : { *(.rela.bss) } + .rel.plt : { *(.rel.plt) } + .rela.plt : { *(.rela.plt) } + .init : { *(.init) } + .plt : { *(.plt) } + .text : + { + arch/powerpc/cpu/mpc5xxx/start.o (.text) + *(.text) + *(.got1) + . = ALIGN(16); + *(.eh_frame) + *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.rodata*))) + } + .fini : { *(.fini) } =0 + .ctors : { *(.ctors) } + .dtors : { *(.dtors) } + + /* Read-write section, merged into data segment: */ + . = (. + 0x0FFF) & 0xFFFFF000; + _erotext = .; + PROVIDE (erotext = .); + .reloc : + { + *(.got) + _GOT2_TABLE_ = .; + *(.got2) + _FIXUP_TABLE_ = .; + *(.fixup) + } + __got2_entries = (_FIXUP_TABLE_ - _GOT2_TABLE_) >> 2; + __fixup_entries = (. - _FIXUP_TABLE_) >> 2; + + .data : + { + *(.data) + *(.data1) + *(.sdata) + *(.sdata2) + *(.dynamic) + CONSTRUCTORS + } + _edata = .; + PROVIDE (edata = .); + + . = .; + __u_boot_cmd_start = .; + .u_boot_cmd : { *(.u_boot_cmd) } + __u_boot_cmd_end = .; + + + . = .; + __start___ex_table = .; + __ex_table : { *(__ex_table) } + __stop___ex_table = .; + + . = ALIGN(4096); + __init_begin = .; + .text.init : { *(.text.init) } + .data.init : { *(.data.init) } + . = ALIGN(4096); + __init_end = .; + + __bss_start = .; + .bss (NOLOAD) : + { + *(.sbss) *(.scommon) + *(.dynbss) + *(.bss) + *(COMMON) + . = ALIGN(4); + } + _end = . ; + PROVIDE (end = .); +} diff --git a/arch/powerpc/cpu/mpc5xxx/usb.c b/arch/powerpc/cpu/mpc5xxx/usb.c new file mode 100644 index 0000000..bec7da3 --- /dev/null +++ b/arch/powerpc/cpu/mpc5xxx/usb.c @@ -0,0 +1,58 @@ +/* + * (C) Copyright 2007 + * Markus Klotzbuecher, DENX Software Engineering <mk@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_USB_OHCI_NEW) && defined(CONFIG_SYS_USB_OHCI_CPU_INIT) + +#include <mpc5xxx.h> + +int usb_cpu_init(void) +{ + /* Set the USB Clock */ + *(vu_long *)MPC5XXX_CDM_48_FDC = CONFIG_USB_CLOCK; + +#ifdef CONFIG_PSC3_USB /* USB is using the alternate configuration */ + /* remove all PSC3 USB bits first before ORing in ours */ + *(vu_long *)MPC5XXX_GPS_PORT_CONFIG &= ~0x00804f00; +#else + /* remove all USB bits first before ORing in ours */ + *(vu_long *)MPC5XXX_GPS_PORT_CONFIG &= ~0x00807000; +#endif + /* Activate USB port */ + *(vu_long *)MPC5XXX_GPS_PORT_CONFIG |= CONFIG_USB_CONFIG; + + return 0; +} + +int usb_cpu_stop(void) +{ + return 0; +} + +int usb_cpu_init_fail(void) +{ + return 0; +} + +#endif /* defined(CONFIG_USB_OHCI) && defined(CONFIG_SYS_USB_OHCI_CPU_INIT) */ diff --git a/arch/powerpc/cpu/mpc5xxx/usb_ohci.c b/arch/powerpc/cpu/mpc5xxx/usb_ohci.c new file mode 100644 index 0000000..7976e4d --- /dev/null +++ b/arch/powerpc/cpu/mpc5xxx/usb_ohci.c @@ -0,0 +1,1646 @@ +/* + * URB OHCI HCD (Host Controller Driver) for USB on the MPC5200. + * + * (C) Copyright 2003-2004 + * Gary Jennejohn, DENX Software Engineering <garyj@denx.de> + * + * (C) Copyright 2004 + * Pierre Aubert, Staubli Faverges <p.aubert@staubli.com> + * + * Note: Much of this code has been derived from Linux 2.4 + * (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at> + * (C) Copyright 2000-2002 David Brownell + * + * 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 + * + */ +/* + * IMPORTANT NOTES + * 1 - this driver is intended for use with USB Mass Storage Devices + * (BBB) ONLY. There is NO support for Interrupt or Isochronous pipes! + */ + +#include <common.h> + +#ifdef CONFIG_USB_OHCI + +#include <malloc.h> +#include <usb.h> +#include "usb_ohci.h" + +#include <mpc5xxx.h> + +#define OHCI_USE_NPS /* force NoPowerSwitching mode */ +#undef OHCI_VERBOSE_DEBUG /* not always helpful */ +#undef DEBUG +#undef SHOW_INFO +#undef OHCI_FILL_TRACE + +/* For initializing controller (mask in an HCFS mode too) */ +#define OHCI_CONTROL_INIT \ + (OHCI_CTRL_CBSR & 0x3) | OHCI_CTRL_IE | OHCI_CTRL_PLE + +#define readl(a) (*((volatile u32 *)(a))) +#define writel(a, b) (*((volatile u32 *)(b)) = ((volatile u32)a)) + +#define min_t(type,x,y) ({ type __x = (x); type __y = (y); __x < __y ? __x: __y; }) + +#ifdef DEBUG +#define dbg(format, arg...) printf("DEBUG: " format "\n", ## arg) +#else +#define dbg(format, arg...) do {} while(0) +#endif /* DEBUG */ +#define err(format, arg...) printf("ERROR: " format "\n", ## arg) +#ifdef SHOW_INFO +#define info(format, arg...) printf("INFO: " format "\n", ## arg) +#else +#define info(format, arg...) do {} while(0) +#endif + +#define m16_swap(x) swap_16(x) +#define m32_swap(x) swap_32(x) + +#define ohci_cpu_to_le16(x) (x) +#define ohci_cpu_to_le32(x) (x) + +/* global ohci_t */ +static ohci_t gohci; +/* this must be aligned to a 256 byte boundary */ +struct ohci_hcca ghcca[1]; +/* a pointer to the aligned storage */ +struct ohci_hcca *phcca; +/* this allocates EDs for all possible endpoints */ +struct ohci_device ohci_dev; +/* urb_priv */ +urb_priv_t urb_priv; +/* RHSC flag */ +int got_rhsc; +/* device which was disconnected */ +struct usb_device *devgone; +/* flag guarding URB transation */ +int urb_finished = 0; + +/*-------------------------------------------------------------------------*/ + +/* AMD-756 (D2 rev) reports corrupt register contents in some cases. + * The erratum (#4) description is incorrect. AMD's workaround waits + * till some bits (mostly reserved) are clear; ok for all revs. + */ +#define OHCI_QUIRK_AMD756 0xabcd +#define read_roothub(hc, register, mask) ({ \ + u32 temp = readl (&hc->regs->roothub.register); \ + if (hc->flags & OHCI_QUIRK_AMD756) \ + while (temp & mask) \ + temp = readl (&hc->regs->roothub.register); \ + temp; }) + +static u32 roothub_a (struct ohci *hc) + { return read_roothub (hc, a, 0xfc0fe000); } +static inline u32 roothub_b (struct ohci *hc) + { return readl (&hc->regs->roothub.b); } +static inline u32 roothub_status (struct ohci *hc) + { return readl (&hc->regs->roothub.status); } +static u32 roothub_portstatus (struct ohci *hc, int i) + { return read_roothub (hc, portstatus [i], 0xffe0fce0); } + + +/* forward declaration */ +static int hc_interrupt (void); +static void +td_submit_job (struct usb_device * dev, unsigned long pipe, void * buffer, + int transfer_len, struct devrequest * setup, urb_priv_t * urb, int interval); + +/*-------------------------------------------------------------------------* + * URB support functions + *-------------------------------------------------------------------------*/ + +/* free HCD-private data associated with this URB */ + +static void urb_free_priv (urb_priv_t * urb) +{ + int i; + int last; + struct td * td; + + last = urb->length - 1; + if (last >= 0) { + for (i = 0; i <= last; i++) { + td = urb->td[i]; + if (td) { + td->usb_dev = NULL; + urb->td[i] = NULL; + } + } + } +} + +/*-------------------------------------------------------------------------*/ + +#ifdef DEBUG +static int sohci_get_current_frame_number (struct usb_device * dev); + +/* debug| print the main components of an URB + * small: 0) header + data packets 1) just header */ + +static void pkt_print (struct usb_device * dev, unsigned long pipe, void * buffer, + int transfer_len, struct devrequest * setup, char * str, int small) +{ + urb_priv_t * purb = &urb_priv; + + dbg("%s URB:[%4x] dev:%2d,ep:%2d-%c,type:%s,len:%d/%d stat:%#lx", + str, + sohci_get_current_frame_number (dev), + usb_pipedevice (pipe), + usb_pipeendpoint (pipe), + usb_pipeout (pipe)? 'O': 'I', + usb_pipetype (pipe) < 2? (usb_pipeint (pipe)? "INTR": "ISOC"): + (usb_pipecontrol (pipe)? "CTRL": "BULK"), + purb->actual_length, + transfer_len, dev->status); +#ifdef OHCI_VERBOSE_DEBUG + if (!small) { + int i, len; + + if (usb_pipecontrol (pipe)) { + printf (__FILE__ ": cmd(8):"); + for (i = 0; i < 8 ; i++) + printf (" %02x", ((__u8 *) setup) [i]); + printf ("\n"); + } + if (transfer_len > 0 && buffer) { + printf (__FILE__ ": data(%d/%d):", + purb->actual_length, + transfer_len); + len = usb_pipeout (pipe)? + transfer_len: purb->actual_length; + for (i = 0; i < 16 && i < len; i++) + printf (" %02x", ((__u8 *) buffer) [i]); + printf ("%s\n", i < len? "...": ""); + } + } +#endif +} + +/* just for debugging; prints non-empty branches of the int ed tree inclusive iso eds*/ +void ep_print_int_eds (ohci_t *ohci, char * str) { + int i, j; + __u32 * ed_p; + for (i= 0; i < 32; i++) { + j = 5; + ed_p = &(ohci->hcca->int_table [i]); + if (*ed_p == 0) + continue; + printf (__FILE__ ": %s branch int %2d(%2x):", str, i, i); + while (*ed_p != 0 && j--) { + ed_t *ed = (ed_t *)ohci_cpu_to_le32(ed_p); + printf (" ed: %4x;", ed->hwINFO); + ed_p = &ed->hwNextED; + } + printf ("\n"); + } +} + +static void ohci_dump_intr_mask (char *label, __u32 mask) +{ + dbg ("%s: 0x%08x%s%s%s%s%s%s%s%s%s", + label, + mask, + (mask & OHCI_INTR_MIE) ? " MIE" : "", + (mask & OHCI_INTR_OC) ? " OC" : "", + (mask & OHCI_INTR_RHSC) ? " RHSC" : "", + (mask & OHCI_INTR_FNO) ? " FNO" : "", + (mask & OHCI_INTR_UE) ? " UE" : "", + (mask & OHCI_INTR_RD) ? " RD" : "", + (mask & OHCI_INTR_SF) ? " SF" : "", + (mask & OHCI_INTR_WDH) ? " WDH" : "", + (mask & OHCI_INTR_SO) ? " SO" : "" + ); +} + +static void maybe_print_eds (char *label, __u32 value) +{ + ed_t *edp = (ed_t *)value; + + if (value) { + dbg ("%s %08x", label, value); + dbg ("%08x", edp->hwINFO); + dbg ("%08x", edp->hwTailP); + dbg ("%08x", edp->hwHeadP); + dbg ("%08x", edp->hwNextED); + } +} + +static char * hcfs2string (int state) +{ + switch (state) { + case OHCI_USB_RESET: return "reset"; + case OHCI_USB_RESUME: return "resume"; + case OHCI_USB_OPER: return "operational"; + case OHCI_USB_SUSPEND: return "suspend"; + } + return "?"; +} + +/* dump control and status registers */ +static void ohci_dump_status (ohci_t *controller) +{ + struct ohci_regs *regs = controller->regs; + __u32 temp; + + temp = readl (®s->revision) & 0xff; + if (temp != 0x10) + dbg ("spec %d.%d", (temp >> 4), (temp & 0x0f)); + + temp = readl (®s->control); + dbg ("control: 0x%08x%s%s%s HCFS=%s%s%s%s%s CBSR=%d", temp, + (temp & OHCI_CTRL_RWE) ? " RWE" : "", + (temp & OHCI_CTRL_RWC) ? " RWC" : "", + (temp & OHCI_CTRL_IR) ? " IR" : "", + hcfs2string (temp & OHCI_CTRL_HCFS), + (temp & OHCI_CTRL_BLE) ? " BLE" : "", + (temp & OHCI_CTRL_CLE) ? " CLE" : "", + (temp & OHCI_CTRL_IE) ? " IE" : "", + (temp & OHCI_CTRL_PLE) ? " PLE" : "", + temp & OHCI_CTRL_CBSR + ); + + temp = readl (®s->cmdstatus); + dbg ("cmdstatus: 0x%08x SOC=%d%s%s%s%s", temp, + (temp & OHCI_SOC) >> 16, + (temp & OHCI_OCR) ? " OCR" : "", + (temp & OHCI_BLF) ? " BLF" : "", + (temp & OHCI_CLF) ? " CLF" : "", + (temp & OHCI_HCR) ? " HCR" : "" + ); + + ohci_dump_intr_mask ("intrstatus", readl (®s->intrstatus)); + ohci_dump_intr_mask ("intrenable", readl (®s->intrenable)); + + maybe_print_eds ("ed_periodcurrent", readl (®s->ed_periodcurrent)); + + maybe_print_eds ("ed_controlhead", readl (®s->ed_controlhead)); + maybe_print_eds ("ed_controlcurrent", readl (®s->ed_controlcurrent)); + + maybe_print_eds ("ed_bulkhead", readl (®s->ed_bulkhead)); + maybe_print_eds ("ed_bulkcurrent", readl (®s->ed_bulkcurrent)); + + maybe_print_eds ("donehead", readl (®s->donehead)); +} + +static void ohci_dump_roothub (ohci_t *controller, int verbose) +{ + __u32 temp, ndp, i; + + temp = roothub_a (controller); + ndp = (temp & RH_A_NDP); + + if (verbose) { + dbg ("roothub.a: %08x POTPGT=%d%s%s%s%s%s NDP=%d", temp, + ((temp & RH_A_POTPGT) >> 24) & 0xff, + (temp & RH_A_NOCP) ? " NOCP" : "", + (temp & RH_A_OCPM) ? " OCPM" : "", + (temp & RH_A_DT) ? " DT" : "", + (temp & RH_A_NPS) ? " NPS" : "", + (temp & RH_A_PSM) ? " PSM" : "", + ndp + ); + temp = roothub_b (controller); + dbg ("roothub.b: %08x PPCM=%04x DR=%04x", + temp, + (temp & RH_B_PPCM) >> 16, + (temp & RH_B_DR) + ); + temp = roothub_status (controller); + dbg ("roothub.status: %08x%s%s%s%s%s%s", + temp, + (temp & RH_HS_CRWE) ? " CRWE" : "", + (temp & RH_HS_OCIC) ? " OCIC" : "", + (temp & RH_HS_LPSC) ? " LPSC" : "", + (temp & RH_HS_DRWE) ? " DRWE" : "", + (temp & RH_HS_OCI) ? " OCI" : "", + (temp & RH_HS_LPS) ? " LPS" : "" + ); + } + + for (i = 0; i < ndp; i++) { + temp = roothub_portstatus (controller, i); + dbg ("roothub.portstatus [%d] = 0x%08x%s%s%s%s%s%s%s%s%s%s%s%s", + i, + temp, + (temp & RH_PS_PRSC) ? " PRSC" : "", + (temp & RH_PS_OCIC) ? " OCIC" : "", + (temp & RH_PS_PSSC) ? " PSSC" : "", + (temp & RH_PS_PESC) ? " PESC" : "", + (temp & RH_PS_CSC) ? " CSC" : "", + + (temp & RH_PS_LSDA) ? " LSDA" : "", + (temp & RH_PS_PPS) ? " PPS" : "", + (temp & RH_PS_PRS) ? " PRS" : "", + (temp & RH_PS_POCI) ? " POCI" : "", + (temp & RH_PS_PSS) ? " PSS" : "", + + (temp & RH_PS_PES) ? " PES" : "", + (temp & RH_PS_CCS) ? " CCS" : "" + ); + } +} + +static void ohci_dump (ohci_t *controller, int verbose) +{ + dbg ("OHCI controller usb-%s state", controller->slot_name); + + /* dumps some of the state we know about */ + ohci_dump_status (controller); + if (verbose) + ep_print_int_eds (controller, "hcca"); + dbg ("hcca frame #%04x", controller->hcca->frame_no); + ohci_dump_roothub (controller, 1); +} + + +#endif /* DEBUG */ + +/*-------------------------------------------------------------------------* + * Interface functions (URB) + *-------------------------------------------------------------------------*/ + +/* get a transfer request */ + +int sohci_submit_job(struct usb_device *dev, unsigned long pipe, void *buffer, + int transfer_len, struct devrequest *setup, int interval) +{ + ohci_t *ohci; + ed_t * ed; + urb_priv_t *purb_priv; + int i, size = 0; + + ohci = &gohci; + + /* when controller's hung, permit only roothub cleanup attempts + * such as powering down ports */ + if (ohci->disabled) { + err("sohci_submit_job: EPIPE"); + return -1; + } + + /* if we have an unfinished URB from previous transaction let's + * fail and scream as quickly as possible so as not to corrupt + * further communication */ + if (!urb_finished) { + err("sohci_submit_job: URB NOT FINISHED"); + return -1; + } + /* we're about to begin a new transaction here so mark the URB unfinished */ + urb_finished = 0; + + /* every endpoint has a ed, locate and fill it */ + if (!(ed = ep_add_ed (dev, pipe))) { + err("sohci_submit_job: ENOMEM"); + return -1; + } + + /* for the private part of the URB we need the number of TDs (size) */ + switch (usb_pipetype (pipe)) { + case PIPE_BULK: /* one TD for every 4096 Byte */ + size = (transfer_len - 1) / 4096 + 1; + break; + case PIPE_CONTROL: /* 1 TD for setup, 1 for ACK and 1 for every 4096 B */ + size = (transfer_len == 0)? 2: + (transfer_len - 1) / 4096 + 3; + break; + } + + if (size >= (N_URB_TD - 1)) { + err("need %d TDs, only have %d", size, N_URB_TD); + return -1; + } + purb_priv = &urb_priv; + purb_priv->pipe = pipe; + + /* fill the private part of the URB */ + purb_priv->length = size; + purb_priv->ed = ed; + purb_priv->actual_length = 0; + + /* allocate the TDs */ + /* note that td[0] was allocated in ep_add_ed */ + for (i = 0; i < size; i++) { + purb_priv->td[i] = td_alloc (dev); + if (!purb_priv->td[i]) { + purb_priv->length = i; + urb_free_priv (purb_priv); + err("sohci_submit_job: ENOMEM"); + return -1; + } + } + + if (ed->state == ED_NEW || (ed->state & ED_DEL)) { + urb_free_priv (purb_priv); + err("sohci_submit_job: EINVAL"); + return -1; + } + + /* link the ed into a chain if is not already */ + if (ed->state != ED_OPER) + ep_link (ohci, ed); + + /* fill the TDs and link it to the ed */ + td_submit_job(dev, pipe, buffer, transfer_len, setup, purb_priv, interval); + + return 0; +} + +/*-------------------------------------------------------------------------*/ + +#ifdef DEBUG +/* tell us the current USB frame number */ + +static int sohci_get_current_frame_number (struct usb_device *usb_dev) +{ + ohci_t *ohci = &gohci; + + return ohci_cpu_to_le16 (ohci->hcca->frame_no); +} +#endif + +/*-------------------------------------------------------------------------* + * ED handling functions + *-------------------------------------------------------------------------*/ + +/* link an ed into one of the HC chains */ + +static int ep_link (ohci_t *ohci, ed_t *edi) +{ + volatile ed_t *ed = edi; + + ed->state = ED_OPER; + + switch (ed->type) { + case PIPE_CONTROL: + ed->hwNextED = 0; + if (ohci->ed_controltail == NULL) { + writel (ed, &ohci->regs->ed_controlhead); + } else { + ohci->ed_controltail->hwNextED = ohci_cpu_to_le32 ((unsigned long)ed); + } + ed->ed_prev = ohci->ed_controltail; + if (!ohci->ed_controltail && !ohci->ed_rm_list[0] && + !ohci->ed_rm_list[1] && !ohci->sleeping) { + ohci->hc_control |= OHCI_CTRL_CLE; + writel (ohci->hc_control, &ohci->regs->control); + } + ohci->ed_controltail = edi; + break; + + case PIPE_BULK: + ed->hwNextED = 0; + if (ohci->ed_bulktail == NULL) { + writel (ed, &ohci->regs->ed_bulkhead); + } else { + ohci->ed_bulktail->hwNextED = ohci_cpu_to_le32 ((unsigned long)ed); + } + ed->ed_prev = ohci->ed_bulktail; + if (!ohci->ed_bulktail && !ohci->ed_rm_list[0] && + !ohci->ed_rm_list[1] && !ohci->sleeping) { + ohci->hc_control |= OHCI_CTRL_BLE; + writel (ohci->hc_control, &ohci->regs->control); + } + ohci->ed_bulktail = edi; + break; + } + return 0; +} + +/*-------------------------------------------------------------------------*/ + +/* unlink an ed from one of the HC chains. + * just the link to the ed is unlinked. + * the link from the ed still points to another operational ed or 0 + * so the HC can eventually finish the processing of the unlinked ed */ + +static int ep_unlink (ohci_t *ohci, ed_t *edi) +{ + volatile ed_t *ed = edi; + + ed->hwINFO |= ohci_cpu_to_le32 (OHCI_ED_SKIP); + + switch (ed->type) { + case PIPE_CONTROL: + if (ed->ed_prev == NULL) { + if (!ed->hwNextED) { + ohci->hc_control &= ~OHCI_CTRL_CLE; + writel (ohci->hc_control, &ohci->regs->control); + } + writel (ohci_cpu_to_le32 (*((__u32 *)&ed->hwNextED)), &ohci->regs->ed_controlhead); + } else { + ed->ed_prev->hwNextED = ed->hwNextED; + } + if (ohci->ed_controltail == ed) { + ohci->ed_controltail = ed->ed_prev; + } else { + ((ed_t *)ohci_cpu_to_le32 (*((__u32 *)&ed->hwNextED)))->ed_prev = ed->ed_prev; + } + break; + + case PIPE_BULK: + if (ed->ed_prev == NULL) { + if (!ed->hwNextED) { + ohci->hc_control &= ~OHCI_CTRL_BLE; + writel (ohci->hc_control, &ohci->regs->control); + } + writel (ohci_cpu_to_le32 (*((__u32 *)&ed->hwNextED)), &ohci->regs->ed_bulkhead); + } else { + ed->ed_prev->hwNextED = ed->hwNextED; + } + if (ohci->ed_bulktail == ed) { + ohci->ed_bulktail = ed->ed_prev; + } else { + ((ed_t *)ohci_cpu_to_le32 (*((__u32 *)&ed->hwNextED)))->ed_prev = ed->ed_prev; + } + break; + } + ed->state = ED_UNLINK; + return 0; +} + + +/*-------------------------------------------------------------------------*/ + +/* add/reinit an endpoint; this should be done once at the usb_set_configuration command, + * but the USB stack is a little bit stateless so we do it at every transaction + * if the state of the ed is ED_NEW then a dummy td is added and the state is changed to ED_UNLINK + * in all other cases the state is left unchanged + * the ed info fields are setted anyway even though most of them should not change */ + +static ed_t * ep_add_ed (struct usb_device *usb_dev, unsigned long pipe) +{ + td_t *td; + ed_t *ed_ret; + volatile ed_t *ed; + + ed = ed_ret = &ohci_dev.ed[(usb_pipeendpoint (pipe) << 1) | + (usb_pipecontrol (pipe)? 0: usb_pipeout (pipe))]; + + if ((ed->state & ED_DEL) || (ed->state & ED_URB_DEL)) { + err("ep_add_ed: pending delete"); + /* pending delete request */ + return NULL; + } + + if (ed->state == ED_NEW) { + ed->hwINFO = ohci_cpu_to_le32 (OHCI_ED_SKIP); /* skip ed */ + /* dummy td; end of td list for ed */ + td = td_alloc (usb_dev); + ed->hwTailP = ohci_cpu_to_le32 ((unsigned long)td); + ed->hwHeadP = ed->hwTailP; + ed->state = ED_UNLINK; + ed->type = usb_pipetype (pipe); + ohci_dev.ed_cnt++; + } + + ed->hwINFO = ohci_cpu_to_le32 (usb_pipedevice (pipe) + | usb_pipeendpoint (pipe) << 7 + | (usb_pipeisoc (pipe)? 0x8000: 0) + | (usb_pipecontrol (pipe)? 0: (usb_pipeout (pipe)? 0x800: 0x1000)) + | usb_pipeslow (pipe) << 13 + | usb_maxpacket (usb_dev, pipe) << 16); + + return ed_ret; +} + +/*-------------------------------------------------------------------------* + * TD handling functions + *-------------------------------------------------------------------------*/ + +/* enqueue next TD for this URB (OHCI spec 5.2.8.2) */ + +static void td_fill (ohci_t *ohci, unsigned int info, + void *data, int len, + struct usb_device *dev, int index, urb_priv_t *urb_priv) +{ + volatile td_t *td, *td_pt; +#ifdef OHCI_FILL_TRACE + int i; +#endif + + if (index > urb_priv->length) { + err("index > length"); + return; + } + /* use this td as the next dummy */ + td_pt = urb_priv->td [index]; + td_pt->hwNextTD = 0; + + /* fill the old dummy TD */ + td = urb_priv->td [index] = (td_t *)(ohci_cpu_to_le32 (urb_priv->ed->hwTailP) & ~0xf); + + td->ed = urb_priv->ed; + td->next_dl_td = NULL; + td->index = index; + td->data = (__u32)data; +#ifdef OHCI_FILL_TRACE + if (usb_pipebulk(urb_priv->pipe) && usb_pipeout(urb_priv->pipe)) { + for (i = 0; i < len; i++) + printf("td->data[%d] %#2x ",i, ((unsigned char *)td->data)[i]); + printf("\n"); + } +#endif + if (!len) + data = 0; + + td->hwINFO = ohci_cpu_to_le32 (info); + td->hwCBP = ohci_cpu_to_le32 ((unsigned long)data); + if (data) + td->hwBE = ohci_cpu_to_le32 ((unsigned long)(data + len - 1)); + else + td->hwBE = 0; + td->hwNextTD = ohci_cpu_to_le32 ((unsigned long)td_pt); + + /* append to queue */ + td->ed->hwTailP = td->hwNextTD; +} + +/*-------------------------------------------------------------------------*/ + +/* prepare all TDs of a transfer */ +static void td_submit_job (struct usb_device *dev, unsigned long pipe, void *buffer, + int transfer_len, struct devrequest *setup, urb_priv_t *urb, int interval) +{ + ohci_t *ohci = &gohci; + int data_len = transfer_len; + void *data; + int cnt = 0; + __u32 info = 0; + unsigned int toggle = 0; + + /* OHCI handles the DATA-toggles itself, we just use the USB-toggle bits for reseting */ + if(usb_gettoggle(dev, usb_pipeendpoint(pipe), usb_pipeout(pipe))) { + toggle = TD_T_TOGGLE; + } else { + toggle = TD_T_DATA0; + usb_settoggle(dev, usb_pipeendpoint(pipe), usb_pipeout(pipe), 1); + } + urb->td_cnt = 0; + if (data_len) + data = buffer; + else + data = 0; + + switch (usb_pipetype (pipe)) { + case PIPE_BULK: + info = usb_pipeout (pipe)? + TD_CC | TD_DP_OUT : TD_CC | TD_DP_IN ; + while(data_len > 4096) { + td_fill (ohci, info | (cnt? TD_T_TOGGLE:toggle), data, 4096, dev, cnt, urb); + data += 4096; data_len -= 4096; cnt++; + } + info = usb_pipeout (pipe)? + TD_CC | TD_DP_OUT : TD_CC | TD_R | TD_DP_IN ; + td_fill (ohci, info | (cnt? TD_T_TOGGLE:toggle), data, data_len, dev, cnt, urb); + cnt++; + + if (!ohci->sleeping) + writel (OHCI_BLF, &ohci->regs->cmdstatus); /* start bulk list */ + break; + + case PIPE_CONTROL: + info = TD_CC | TD_DP_SETUP | TD_T_DATA0; + td_fill (ohci, info, setup, 8, dev, cnt++, urb); + if (data_len > 0) { + info = usb_pipeout (pipe)? + TD_CC | TD_R | TD_DP_OUT | TD_T_DATA1 : TD_CC | TD_R | TD_DP_IN | TD_T_DATA1; + /* NOTE: mishandles transfers >8K, some >4K */ + td_fill (ohci, info, data, data_len, dev, cnt++, urb); + } + info = usb_pipeout (pipe)? + TD_CC | TD_DP_IN | TD_T_DATA1: TD_CC | TD_DP_OUT | TD_T_DATA1; + td_fill (ohci, info, data, 0, dev, cnt++, urb); + if (!ohci->sleeping) + writel (OHCI_CLF, &ohci->regs->cmdstatus); /* start Control list */ + break; + } + if (urb->length != cnt) + dbg("TD LENGTH %d != CNT %d", urb->length, cnt); +} + +/*-------------------------------------------------------------------------* + * Done List handling functions + *-------------------------------------------------------------------------*/ + + +/* calculate the transfer length and update the urb */ + +static void dl_transfer_length(td_t * td) +{ + __u32 tdINFO, tdBE, tdCBP; + urb_priv_t *lurb_priv = &urb_priv; + + tdINFO = ohci_cpu_to_le32 (td->hwINFO); + tdBE = ohci_cpu_to_le32 (td->hwBE); + tdCBP = ohci_cpu_to_le32 (td->hwCBP); + + + if (!(usb_pipecontrol(lurb_priv->pipe) && + ((td->index == 0) || (td->index == lurb_priv->length - 1)))) { + if (tdBE != 0) { + if (td->hwCBP == 0) + lurb_priv->actual_length += tdBE - td->data + 1; + else + lurb_priv->actual_length += tdCBP - td->data; + } + } +} + +/*-------------------------------------------------------------------------*/ + +/* replies to the request have to be on a FIFO basis so + * we reverse the reversed done-list */ + +static td_t * dl_reverse_done_list (ohci_t *ohci) +{ + __u32 td_list_hc; + td_t *td_rev = NULL; + td_t *td_list = NULL; + urb_priv_t *lurb_priv = NULL; + + td_list_hc = ohci_cpu_to_le32 (ohci->hcca->done_head) & 0xfffffff0; + ohci->hcca->done_head = 0; + + while (td_list_hc) { + td_list = (td_t *)td_list_hc; + + if (TD_CC_GET (ohci_cpu_to_le32 (td_list->hwINFO))) { + lurb_priv = &urb_priv; + dbg(" USB-error/status: %x : %p", + TD_CC_GET (ohci_cpu_to_le32 (td_list->hwINFO)), td_list); + if (td_list->ed->hwHeadP & ohci_cpu_to_le32 (0x1)) { + if (lurb_priv && ((td_list->index + 1) < lurb_priv->length)) { + td_list->ed->hwHeadP = + (lurb_priv->td[lurb_priv->length - 1]->hwNextTD & ohci_cpu_to_le32 (0xfffffff0)) | + (td_list->ed->hwHeadP & ohci_cpu_to_le32 (0x2)); + lurb_priv->td_cnt += lurb_priv->length - td_list->index - 1; + } else + td_list->ed->hwHeadP &= ohci_cpu_to_le32 (0xfffffff2); + } + td_list->hwNextTD = 0; + } + + td_list->next_dl_td = td_rev; + td_rev = td_list; + td_list_hc = ohci_cpu_to_le32 (td_list->hwNextTD) & 0xfffffff0; + } + return td_list; +} + +/*-------------------------------------------------------------------------*/ + +/* td done list */ +static int dl_done_list (ohci_t *ohci, td_t *td_list) +{ + td_t *td_list_next = NULL; + ed_t *ed; + int cc = 0; + int stat = 0; + /* urb_t *urb; */ + urb_priv_t *lurb_priv; + __u32 tdINFO, edHeadP, edTailP; + + while (td_list) { + td_list_next = td_list->next_dl_td; + + lurb_priv = &urb_priv; + tdINFO = ohci_cpu_to_le32 (td_list->hwINFO); + + ed = td_list->ed; + + dl_transfer_length(td_list); + + /* error code of transfer */ + cc = TD_CC_GET (tdINFO); + if (++(lurb_priv->td_cnt) == lurb_priv->length) { + if ((ed->state & (ED_OPER | ED_UNLINK)) + && (lurb_priv->state != URB_DEL)) { + dbg("ConditionCode %#x", cc); + stat = cc_to_error[cc]; + urb_finished = 1; + } + } + + if (ed->state != ED_NEW) { + edHeadP = ohci_cpu_to_le32 (ed->hwHeadP) & 0xfffffff0; + edTailP = ohci_cpu_to_le32 (ed->hwTailP); + + /* unlink eds if they are not busy */ + if ((edHeadP == edTailP) && (ed->state == ED_OPER)) + ep_unlink (ohci, ed); + } + + td_list = td_list_next; + } + return stat; +} + +/*-------------------------------------------------------------------------* + * Virtual Root Hub + *-------------------------------------------------------------------------*/ + +/* Device descriptor */ +static __u8 root_hub_dev_des[] = +{ + 0x12, /* __u8 bLength; */ + 0x01, /* __u8 bDescriptorType; Device */ + 0x10, /* __u16 bcdUSB; v1.1 */ + 0x01, + 0x09, /* __u8 bDeviceClass; HUB_CLASSCODE */ + 0x00, /* __u8 bDeviceSubClass; */ + 0x00, /* __u8 bDeviceProtocol; */ + 0x08, /* __u8 bMaxPacketSize0; 8 Bytes */ + 0x00, /* __u16 idVendor; */ + 0x00, + 0x00, /* __u16 idProduct; */ + 0x00, + 0x00, /* __u16 bcdDevice; */ + 0x00, + 0x00, /* __u8 iManufacturer; */ + 0x01, /* __u8 iProduct; */ + 0x00, /* __u8 iSerialNumber; */ + 0x01 /* __u8 bNumConfigurations; */ +}; + + +/* Configuration descriptor */ +static __u8 root_hub_config_des[] = +{ + 0x09, /* __u8 bLength; */ + 0x02, /* __u8 bDescriptorType; Configuration */ + 0x19, /* __u16 wTotalLength; */ + 0x00, + 0x01, /* __u8 bNumInterfaces; */ + 0x01, /* __u8 bConfigurationValue; */ + 0x00, /* __u8 iConfiguration; */ + 0x40, /* __u8 bmAttributes; + Bit 7: Bus-powered, 6: Self-powered, 5 Remote-wakwup, 4..0: resvd */ + 0x00, /* __u8 MaxPower; */ + + /* interface */ + 0x09, /* __u8 if_bLength; */ + 0x04, /* __u8 if_bDescriptorType; Interface */ + 0x00, /* __u8 if_bInterfaceNumber; */ + 0x00, /* __u8 if_bAlternateSetting; */ + 0x01, /* __u8 if_bNumEndpoints; */ + 0x09, /* __u8 if_bInterfaceClass; HUB_CLASSCODE */ + 0x00, /* __u8 if_bInterfaceSubClass; */ + 0x00, /* __u8 if_bInterfaceProtocol; */ + 0x00, /* __u8 if_iInterface; */ + + /* endpoint */ + 0x07, /* __u8 ep_bLength; */ + 0x05, /* __u8 ep_bDescriptorType; Endpoint */ + 0x81, /* __u8 ep_bEndpointAddress; IN Endpoint 1 */ + 0x03, /* __u8 ep_bmAttributes; Interrupt */ + 0x02, /* __u16 ep_wMaxPacketSize; ((MAX_ROOT_PORTS + 1) / 8 */ + 0x00, + 0xff /* __u8 ep_bInterval; 255 ms */ +}; + +static unsigned char root_hub_str_index0[] = +{ + 0x04, /* __u8 bLength; */ + 0x03, /* __u8 bDescriptorType; String-descriptor */ + 0x09, /* __u8 lang ID */ + 0x04, /* __u8 lang ID */ +}; + +static unsigned char root_hub_str_index1[] = +{ + 28, /* __u8 bLength; */ + 0x03, /* __u8 bDescriptorType; String-descriptor */ + 'O', /* __u8 Unicode */ + 0, /* __u8 Unicode */ + 'H', /* __u8 Unicode */ + 0, /* __u8 Unicode */ + 'C', /* __u8 Unicode */ + 0, /* __u8 Unicode */ + 'I', /* __u8 Unicode */ + 0, /* __u8 Unicode */ + ' ', /* __u8 Unicode */ + 0, /* __u8 Unicode */ + 'R', /* __u8 Unicode */ + 0, /* __u8 Unicode */ + 'o', /* __u8 Unicode */ + 0, /* __u8 Unicode */ + 'o', /* __u8 Unicode */ + 0, /* __u8 Unicode */ + 't', /* __u8 Unicode */ + 0, /* __u8 Unicode */ + ' ', /* __u8 Unicode */ + 0, /* __u8 Unicode */ + 'H', /* __u8 Unicode */ + 0, /* __u8 Unicode */ + 'u', /* __u8 Unicode */ + 0, /* __u8 Unicode */ + 'b', /* __u8 Unicode */ + 0, /* __u8 Unicode */ +}; + +/* Hub class-specific descriptor is constructed dynamically */ + + +/*-------------------------------------------------------------------------*/ + +#define OK(x) len = (x); break +#ifdef DEBUG +#define WR_RH_STAT(x) {info("WR:status %#8x", (x));writel((x), &gohci.regs->roothub.status);} +#define WR_RH_PORTSTAT(x) {info("WR:portstatus[%d] %#8x", wIndex-1, (x));writel((x), &gohci.regs->roothub.portstatus[wIndex-1]);} +#else +#define WR_RH_STAT(x) writel((x), &gohci.regs->roothub.status) +#define WR_RH_PORTSTAT(x) writel((x), &gohci.regs->roothub.portstatus[wIndex-1]) +#endif +#define RD_RH_STAT roothub_status(&gohci) +#define RD_RH_PORTSTAT roothub_portstatus(&gohci,wIndex-1) + +/* request to virtual root hub */ + +int rh_check_port_status(ohci_t *controller) +{ + __u32 temp, ndp, i; + int res; + + res = -1; + temp = roothub_a (controller); + ndp = (temp & RH_A_NDP); + for (i = 0; i < ndp; i++) { + temp = roothub_portstatus (controller, i); + /* check for a device disconnect */ + if (((temp & (RH_PS_PESC | RH_PS_CSC)) == + (RH_PS_PESC | RH_PS_CSC)) && + ((temp & RH_PS_CCS) == 0)) { + res = i; + break; + } + } + return res; +} + +static int ohci_submit_rh_msg(struct usb_device *dev, unsigned long pipe, + void *buffer, int transfer_len, struct devrequest *cmd) +{ + void * data = buffer; + int leni = transfer_len; + int len = 0; + int stat = 0; + __u32 datab[4]; + __u8 *data_buf = (__u8 *)datab; + __u16 bmRType_bReq; + __u16 wValue; + __u16 wIndex; + __u16 wLength; + +#ifdef DEBUG +urb_priv.actual_length = 0; +pkt_print(dev, pipe, buffer, transfer_len, cmd, "SUB(rh)", usb_pipein(pipe)); +#endif + if (usb_pipeint(pipe)) { + info("Root-Hub submit IRQ: NOT implemented"); + return 0; + } + + bmRType_bReq = cmd->requesttype | (cmd->request << 8); + wValue = m16_swap (cmd->value); + wIndex = m16_swap (cmd->index); + wLength = m16_swap (cmd->length); + + info("Root-Hub: adr: %2x cmd(%1x): %08x %04x %04x %04x", + dev->devnum, 8, bmRType_bReq, wValue, wIndex, wLength); + + switch (bmRType_bReq) { + /* Request Destination: + without flags: Device, + RH_INTERFACE: interface, + RH_ENDPOINT: endpoint, + RH_CLASS means HUB here, + RH_OTHER | RH_CLASS almost ever means HUB_PORT here + */ + + case RH_GET_STATUS: + *(__u16 *) data_buf = m16_swap (1); OK (2); + case RH_GET_STATUS | RH_INTERFACE: + *(__u16 *) data_buf = m16_swap (0); OK (2); + case RH_GET_STATUS | RH_ENDPOINT: + *(__u16 *) data_buf = m16_swap (0); OK (2); + case RH_GET_STATUS | RH_CLASS: + *(__u32 *) data_buf = m32_swap ( + RD_RH_STAT & ~(RH_HS_CRWE | RH_HS_DRWE)); + OK (4); + case RH_GET_STATUS | RH_OTHER | RH_CLASS: + *(__u32 *) data_buf = m32_swap (RD_RH_PORTSTAT); OK (4); + + case RH_CLEAR_FEATURE | RH_ENDPOINT: + switch (wValue) { + case (RH_ENDPOINT_STALL): OK (0); + } + break; + + case RH_CLEAR_FEATURE | RH_CLASS: + switch (wValue) { + case RH_C_HUB_LOCAL_POWER: + OK(0); + case (RH_C_HUB_OVER_CURRENT): + WR_RH_STAT(RH_HS_OCIC); OK (0); + } + break; + + case RH_CLEAR_FEATURE | RH_OTHER | RH_CLASS: + switch (wValue) { + case (RH_PORT_ENABLE): + WR_RH_PORTSTAT (RH_PS_CCS ); OK (0); + case (RH_PORT_SUSPEND): + WR_RH_PORTSTAT (RH_PS_POCI); OK (0); + case (RH_PORT_POWER): + WR_RH_PORTSTAT (RH_PS_LSDA); OK (0); + case (RH_C_PORT_CONNECTION): + WR_RH_PORTSTAT (RH_PS_CSC ); OK (0); + case (RH_C_PORT_ENABLE): + WR_RH_PORTSTAT (RH_PS_PESC); OK (0); + case (RH_C_PORT_SUSPEND): + WR_RH_PORTSTAT (RH_PS_PSSC); OK (0); + case (RH_C_PORT_OVER_CURRENT): + WR_RH_PORTSTAT (RH_PS_OCIC); OK (0); + case (RH_C_PORT_RESET): + WR_RH_PORTSTAT (RH_PS_PRSC); OK (0); + } + break; + + case RH_SET_FEATURE | RH_OTHER | RH_CLASS: + switch (wValue) { + case (RH_PORT_SUSPEND): + WR_RH_PORTSTAT (RH_PS_PSS ); OK (0); + case (RH_PORT_RESET): /* BUG IN HUP CODE *********/ + if (RD_RH_PORTSTAT & RH_PS_CCS) + WR_RH_PORTSTAT (RH_PS_PRS); + OK (0); + case (RH_PORT_POWER): + WR_RH_PORTSTAT (RH_PS_PPS ); OK (0); + case (RH_PORT_ENABLE): /* BUG IN HUP CODE *********/ + if (RD_RH_PORTSTAT & RH_PS_CCS) + WR_RH_PORTSTAT (RH_PS_PES ); + OK (0); + } + break; + + case RH_SET_ADDRESS: gohci.rh.devnum = wValue; OK(0); + + case RH_GET_DESCRIPTOR: + switch ((wValue & 0xff00) >> 8) { + case (0x01): /* device descriptor */ + len = min_t(unsigned int, + leni, + min_t(unsigned int, + sizeof (root_hub_dev_des), + wLength)); + data_buf = root_hub_dev_des; OK(len); + case (0x02): /* configuration descriptor */ + len = min_t(unsigned int, + leni, + min_t(unsigned int, + sizeof (root_hub_config_des), + wLength)); + data_buf = root_hub_config_des; OK(len); + case (0x03): /* string descriptors */ + if(wValue==0x0300) { + len = min_t(unsigned int, + leni, + min_t(unsigned int, + sizeof (root_hub_str_index0), + wLength)); + data_buf = root_hub_str_index0; + OK(len); + } + if(wValue==0x0301) { + len = min_t(unsigned int, + leni, + min_t(unsigned int, + sizeof (root_hub_str_index1), + wLength)); + data_buf = root_hub_str_index1; + OK(len); + } + default: + stat = USB_ST_STALLED; + } + break; + + case RH_GET_DESCRIPTOR | RH_CLASS: + { + __u32 temp = roothub_a (&gohci); + + data_buf [0] = 9; /* min length; */ + data_buf [1] = 0x29; + data_buf [2] = temp & RH_A_NDP; + data_buf [3] = 0; + if (temp & RH_A_PSM) /* per-port power switching? */ + data_buf [3] |= 0x1; + if (temp & RH_A_NOCP) /* no overcurrent reporting? */ + data_buf [3] |= 0x10; + else if (temp & RH_A_OCPM) /* per-port overcurrent reporting? */ + data_buf [3] |= 0x8; + + /* corresponds to data_buf[4-7] */ + datab [1] = 0; + data_buf [5] = (temp & RH_A_POTPGT) >> 24; + temp = roothub_b (&gohci); + data_buf [7] = temp & RH_B_DR; + if (data_buf [2] < 7) { + data_buf [8] = 0xff; + } else { + data_buf [0] += 2; + data_buf [8] = (temp & RH_B_DR) >> 8; + data_buf [10] = data_buf [9] = 0xff; + } + + len = min_t(unsigned int, leni, + min_t(unsigned int, data_buf [0], wLength)); + OK (len); + } + + case RH_GET_CONFIGURATION: *(__u8 *) data_buf = 0x01; OK (1); + + case RH_SET_CONFIGURATION: WR_RH_STAT (0x10000); OK (0); + + default: + dbg ("unsupported root hub command"); + stat = USB_ST_STALLED; + } + +#ifdef DEBUG + ohci_dump_roothub (&gohci, 1); +#endif + + len = min_t(int, len, leni); + if (data != data_buf) + memcpy (data, data_buf, len); + dev->act_len = len; + dev->status = stat; + +#ifdef DEBUG + if (transfer_len) + urb_priv.actual_length = transfer_len; + pkt_print(dev, pipe, buffer, transfer_len, cmd, "RET(rh)", 0/*usb_pipein(pipe)*/); +#endif + + return stat; +} + +/*-------------------------------------------------------------------------*/ + +/* common code for handling submit messages - used for all but root hub */ +/* accesses. */ +int submit_common_msg(struct usb_device *dev, unsigned long pipe, void *buffer, + int transfer_len, struct devrequest *setup, int interval) +{ + int stat = 0; + int maxsize = usb_maxpacket(dev, pipe); + int timeout; + + /* device pulled? Shortcut the action. */ + if (devgone == dev) { + dev->status = USB_ST_CRC_ERR; + return 0; + } + +#ifdef DEBUG + urb_priv.actual_length = 0; + pkt_print(dev, pipe, buffer, transfer_len, setup, "SUB", usb_pipein(pipe)); +#endif + if (!maxsize) { + err("submit_common_message: pipesize for pipe %lx is zero", + pipe); + return -1; + } + + if (sohci_submit_job(dev, pipe, buffer, transfer_len, setup, interval) < 0) { + err("sohci_submit_job failed"); + return -1; + } + + /* allow more time for a BULK device to react - some are slow */ +#define BULK_TO 5000 /* timeout in milliseconds */ + if (usb_pipebulk(pipe)) + timeout = BULK_TO; + else + timeout = 100; + + /* wait for it to complete */ + for (;;) { + /* check whether the controller is done */ + stat = hc_interrupt(); + if (stat < 0) { + stat = USB_ST_CRC_ERR; + break; + } + + /* NOTE: since we are not interrupt driven in U-Boot and always + * handle only one URB at a time, we cannot assume the + * transaction finished on the first successful return from + * hc_interrupt().. unless the flag for current URB is set, + * meaning that all TD's to/from device got actually + * transferred and processed. If the current URB is not + * finished we need to re-iterate this loop so as + * hc_interrupt() gets called again as there needs to be some + * more TD's to process still */ + if ((stat >= 0) && (stat != 0xff) && (urb_finished)) { + /* 0xff is returned for an SF-interrupt */ + break; + } + + if (--timeout) { + wait_ms(1); + if (!urb_finished) + dbg("\%"); + + } else { + err("CTL:TIMEOUT "); + dbg("submit_common_msg: TO status %x\n", stat); + stat = USB_ST_CRC_ERR; + urb_finished = 1; + break; + } + } +#if 0 + /* we got an Root Hub Status Change interrupt */ + if (got_rhsc) { +#ifdef DEBUG + ohci_dump_roothub (&gohci, 1); +#endif + got_rhsc = 0; + /* abuse timeout */ + timeout = rh_check_port_status(&gohci); + if (timeout >= 0) { +#if 0 /* this does nothing useful, but leave it here in case that changes */ + /* the called routine adds 1 to the passed value */ + usb_hub_port_connect_change(gohci.rh.dev, timeout - 1); +#endif + /* + * XXX + * This is potentially dangerous because it assumes + * that only one device is ever plugged in! + */ + devgone = dev; + } + } +#endif + + dev->status = stat; + dev->act_len = transfer_len; + +#ifdef DEBUG + pkt_print(dev, pipe, buffer, transfer_len, setup, "RET(ctlr)", usb_pipein(pipe)); +#endif + + /* free TDs in urb_priv */ + urb_free_priv (&urb_priv); + return 0; +} + +/* submit routines called from usb.c */ +int submit_bulk_msg(struct usb_device *dev, unsigned long pipe, void *buffer, + int transfer_len) +{ + info("submit_bulk_msg"); + return submit_common_msg(dev, pipe, buffer, transfer_len, NULL, 0); +} + +int submit_control_msg(struct usb_device *dev, unsigned long pipe, void *buffer, + int transfer_len, struct devrequest *setup) +{ + int maxsize = usb_maxpacket(dev, pipe); + + info("submit_control_msg"); +#ifdef DEBUG + urb_priv.actual_length = 0; + pkt_print(dev, pipe, buffer, transfer_len, setup, "SUB", usb_pipein(pipe)); +#endif + if (!maxsize) { + err("submit_control_message: pipesize for pipe %lx is zero", + pipe); + return -1; + } + if (((pipe >> 8) & 0x7f) == gohci.rh.devnum) { + gohci.rh.dev = dev; + /* root hub - redirect */ + return ohci_submit_rh_msg(dev, pipe, buffer, transfer_len, + setup); + } + + return submit_common_msg(dev, pipe, buffer, transfer_len, setup, 0); +} + +int submit_int_msg(struct usb_device *dev, unsigned long pipe, void *buffer, + int transfer_len, int interval) +{ + info("submit_int_msg"); + return -1; +} + +/*-------------------------------------------------------------------------* + * HC functions + *-------------------------------------------------------------------------*/ + +/* reset the HC and BUS */ + +static int hc_reset (ohci_t *ohci) +{ + int timeout = 30; + int smm_timeout = 50; /* 0,5 sec */ + + if (readl (&ohci->regs->control) & OHCI_CTRL_IR) { /* SMM owns the HC */ + writel (OHCI_OCR, &ohci->regs->cmdstatus); /* request ownership */ + info("USB HC TakeOver from SMM"); + while (readl (&ohci->regs->control) & OHCI_CTRL_IR) { + wait_ms (10); + if (--smm_timeout == 0) { + err("USB HC TakeOver failed!"); + return -1; + } + } + } + + /* Disable HC interrupts */ + writel (OHCI_INTR_MIE, &ohci->regs->intrdisable); + + dbg("USB HC reset_hc usb-%s: ctrl = 0x%X ;", + ohci->slot_name, + readl (&ohci->regs->control)); + + /* Reset USB (needed by some controllers) */ + ohci->hc_control = 0; + writel (ohci->hc_control, &ohci->regs->control); + + /* HC Reset requires max 10 us delay */ + writel (OHCI_HCR, &ohci->regs->cmdstatus); + while ((readl (&ohci->regs->cmdstatus) & OHCI_HCR) != 0) { + if (--timeout == 0) { + err("USB HC reset timed out!"); + return -1; + } + udelay (1); + } + return 0; +} + +/*-------------------------------------------------------------------------*/ + +/* Start an OHCI controller, set the BUS operational + * enable interrupts + * connect the virtual root hub */ + +static int hc_start (ohci_t * ohci) +{ + __u32 mask; + unsigned int fminterval; + + ohci->disabled = 1; + + /* Tell the controller where the control and bulk lists are + * The lists are empty now. */ + + writel (0, &ohci->regs->ed_controlhead); + writel (0, &ohci->regs->ed_bulkhead); + + writel ((__u32)ohci->hcca, &ohci->regs->hcca); /* a reset clears this */ + + fminterval = 0x2edf; + writel ((fminterval * 9) / 10, &ohci->regs->periodicstart); + fminterval |= ((((fminterval - 210) * 6) / 7) << 16); + writel (fminterval, &ohci->regs->fminterval); + writel (0x628, &ohci->regs->lsthresh); + + /* start controller operations */ + ohci->hc_control = OHCI_CONTROL_INIT | OHCI_USB_OPER; + ohci->disabled = 0; + writel (ohci->hc_control, &ohci->regs->control); + + /* disable all interrupts */ + mask = (OHCI_INTR_SO | OHCI_INTR_WDH | OHCI_INTR_SF | OHCI_INTR_RD | + OHCI_INTR_UE | OHCI_INTR_FNO | OHCI_INTR_RHSC | + OHCI_INTR_OC | OHCI_INTR_MIE); + writel (mask, &ohci->regs->intrdisable); + /* clear all interrupts */ + mask &= ~OHCI_INTR_MIE; + writel (mask, &ohci->regs->intrstatus); + /* Choose the interrupts we care about now - but w/o MIE */ + mask = OHCI_INTR_RHSC | OHCI_INTR_UE | OHCI_INTR_WDH | OHCI_INTR_SO; + writel (mask, &ohci->regs->intrenable); + +#ifdef OHCI_USE_NPS + /* required for AMD-756 and some Mac platforms */ + writel ((roothub_a (ohci) | RH_A_NPS) & ~RH_A_PSM, + &ohci->regs->roothub.a); + writel (RH_HS_LPSC, &ohci->regs->roothub.status); +#endif /* OHCI_USE_NPS */ + +#define mdelay(n) ({unsigned long msec=(n); while (msec--) udelay(1000);}) + /* POTPGT delay is bits 24-31, in 2 ms units. */ + mdelay ((roothub_a (ohci) >> 23) & 0x1fe); + + /* connect the virtual root hub */ + ohci->rh.devnum = 0; + + return 0; +} + +/*-------------------------------------------------------------------------*/ + +/* an interrupt happens */ + +static int +hc_interrupt (void) +{ + ohci_t *ohci = &gohci; + struct ohci_regs *regs = ohci->regs; + int ints; + int stat = -1; + + if ((ohci->hcca->done_head != 0) && + !(ohci_cpu_to_le32(ohci->hcca->done_head) & 0x01)) { + + ints = OHCI_INTR_WDH; + + } else if ((ints = readl (®s->intrstatus)) == ~(u32)0) { + ohci->disabled++; + err ("%s device removed!", ohci->slot_name); + return -1; + + } else if ((ints &= readl (®s->intrenable)) == 0) { + dbg("hc_interrupt: returning..\n"); + return 0xff; + } + + /* dbg("Interrupt: %x frame: %x", ints, le16_to_cpu (ohci->hcca->frame_no)); */ + + if (ints & OHCI_INTR_RHSC) { + got_rhsc = 1; + stat = 0xff; + } + + if (ints & OHCI_INTR_UE) { + ohci->disabled++; + err ("OHCI Unrecoverable Error, controller usb-%s disabled", + ohci->slot_name); + /* e.g. due to PCI Master/Target Abort */ + +#ifdef DEBUG + ohci_dump (ohci, 1); +#endif + /* FIXME: be optimistic, hope that bug won't repeat often. */ + /* Make some non-interrupt context restart the controller. */ + /* Count and limit the retries though; either hardware or */ + /* software errors can go forever... */ + hc_reset (ohci); + return -1; + } + + if (ints & OHCI_INTR_WDH) { + writel (OHCI_INTR_WDH, ®s->intrdisable); + stat = dl_done_list (&gohci, dl_reverse_done_list (&gohci)); + writel (OHCI_INTR_WDH, ®s->intrenable); + } + + if (ints & OHCI_INTR_SO) { + dbg("USB Schedule overrun\n"); + writel (OHCI_INTR_SO, ®s->intrenable); + stat = -1; + } + + /* FIXME: this assumes SOF (1/ms) interrupts don't get lost... */ + if (ints & OHCI_INTR_SF) { + unsigned int frame = ohci_cpu_to_le16 (ohci->hcca->frame_no) & 1; + wait_ms(1); + writel (OHCI_INTR_SF, ®s->intrdisable); + if (ohci->ed_rm_list[frame] != NULL) + writel (OHCI_INTR_SF, ®s->intrenable); + stat = 0xff; + } + + writel (ints, ®s->intrstatus); + return stat; +} + +/*-------------------------------------------------------------------------*/ + +/*-------------------------------------------------------------------------*/ + +/* De-allocate all resources.. */ + +static void hc_release_ohci (ohci_t *ohci) +{ + dbg ("USB HC release ohci usb-%s", ohci->slot_name); + + if (!ohci->disabled) + hc_reset (ohci); +} + +/*-------------------------------------------------------------------------*/ + +/* + * low level initalisation routine, called from usb.c + */ +static char ohci_inited = 0; + +int usb_lowlevel_init(void) +{ + + /* Set the USB Clock */ + *(vu_long *)MPC5XXX_CDM_48_FDC = CONFIG_USB_CLOCK; + +#ifdef CONFIG_PSC3_USB /* USB is using the alternate configuration */ + /* remove all PSC3 USB bits first before ORing in ours */ + *(vu_long *)MPC5XXX_GPS_PORT_CONFIG &= ~0x00804f00; +#else + /* remove all USB bits first before ORing in ours */ + *(vu_long *)MPC5XXX_GPS_PORT_CONFIG &= ~0x00807000; +#endif + /* Activate USB port */ + *(vu_long *)MPC5XXX_GPS_PORT_CONFIG |= CONFIG_USB_CONFIG; + + memset (&gohci, 0, sizeof (ohci_t)); + memset (&urb_priv, 0, sizeof (urb_priv_t)); + + /* align the storage */ + if ((__u32)&ghcca[0] & 0xff) { + err("HCCA not aligned!!"); + return -1; + } + phcca = &ghcca[0]; + info("aligned ghcca %p", phcca); + memset(&ohci_dev, 0, sizeof(struct ohci_device)); + if ((__u32)&ohci_dev.ed[0] & 0x7) { + err("EDs not aligned!!"); + return -1; + } + memset(gtd, 0, sizeof(td_t) * (NUM_TD + 1)); + if ((__u32)gtd & 0x7) { + err("TDs not aligned!!"); + return -1; + } + ptd = gtd; + gohci.hcca = phcca; + memset (phcca, 0, sizeof (struct ohci_hcca)); + + gohci.disabled = 1; + gohci.sleeping = 0; + gohci.irq = -1; + gohci.regs = (struct ohci_regs *)MPC5XXX_USB; + + gohci.flags = 0; + gohci.slot_name = "mpc5200"; + + if (hc_reset (&gohci) < 0) { + hc_release_ohci (&gohci); + return -1; + } + + if (hc_start (&gohci) < 0) { + err ("can't start usb-%s", gohci.slot_name); + hc_release_ohci (&gohci); + return -1; + } + +#ifdef DEBUG + ohci_dump (&gohci, 1); +#endif + ohci_inited = 1; + urb_finished = 1; + + return 0; +} + +int usb_lowlevel_stop(void) +{ + /* this gets called really early - before the controller has */ + /* even been initialized! */ + if (!ohci_inited) + return 0; + /* TODO release any interrupts, etc. */ + /* call hc_release_ohci() here ? */ + hc_reset (&gohci); + return 0; +} + +#endif /* CONFIG_USB_OHCI */ diff --git a/arch/powerpc/cpu/mpc5xxx/usb_ohci.h b/arch/powerpc/cpu/mpc5xxx/usb_ohci.h new file mode 100644 index 0000000..629b529 --- /dev/null +++ b/arch/powerpc/cpu/mpc5xxx/usb_ohci.h @@ -0,0 +1,418 @@ +/* + * URB OHCI HCD (Host Controller Driver) for USB. + * + * (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at> + * (C) Copyright 2000-2001 David Brownell <dbrownell@users.sourceforge.net> + * + * usb-ohci.h + */ + + +static int cc_to_error[16] = { + +/* mapping of the OHCI CC status to error codes */ + /* No Error */ 0, + /* CRC Error */ USB_ST_CRC_ERR, + /* Bit Stuff */ USB_ST_BIT_ERR, + /* Data Togg */ USB_ST_CRC_ERR, + /* Stall */ USB_ST_STALLED, + /* DevNotResp */ -1, + /* PIDCheck */ USB_ST_BIT_ERR, + /* UnExpPID */ USB_ST_BIT_ERR, + /* DataOver */ USB_ST_BUF_ERR, + /* DataUnder */ USB_ST_BUF_ERR, + /* reservd */ -1, + /* reservd */ -1, + /* BufferOver */ USB_ST_BUF_ERR, + /* BuffUnder */ USB_ST_BUF_ERR, + /* Not Access */ -1, + /* Not Access */ -1 +}; + +/* ED States */ + +#define ED_NEW 0x00 +#define ED_UNLINK 0x01 +#define ED_OPER 0x02 +#define ED_DEL 0x04 +#define ED_URB_DEL 0x08 + +/* usb_ohci_ed */ +struct ed { + __u32 hwINFO; + __u32 hwTailP; + __u32 hwHeadP; + __u32 hwNextED; + + struct ed *ed_prev; + __u8 int_period; + __u8 int_branch; + __u8 int_load; + __u8 int_interval; + __u8 state; + __u8 type; + __u16 last_iso; + struct ed *ed_rm_list; + + struct usb_device *usb_dev; + __u32 unused[3]; +} __attribute__((aligned(16))); +typedef struct ed ed_t; + + +/* TD info field */ +#define TD_CC 0xf0000000 +#define TD_CC_GET(td_p) ((td_p >>28) & 0x0f) +#define TD_CC_SET(td_p, cc) (td_p) = ((td_p) & 0x0fffffff) | (((cc) & 0x0f) << 28) +#define TD_EC 0x0C000000 +#define TD_T 0x03000000 +#define TD_T_DATA0 0x02000000 +#define TD_T_DATA1 0x03000000 +#define TD_T_TOGGLE 0x00000000 +#define TD_R 0x00040000 +#define TD_DI 0x00E00000 +#define TD_DI_SET(X) (((X) & 0x07)<< 21) +#define TD_DP 0x00180000 +#define TD_DP_SETUP 0x00000000 +#define TD_DP_IN 0x00100000 +#define TD_DP_OUT 0x00080000 + +#define TD_ISO 0x00010000 +#define TD_DEL 0x00020000 + +/* CC Codes */ +#define TD_CC_NOERROR 0x00 +#define TD_CC_CRC 0x01 +#define TD_CC_BITSTUFFING 0x02 +#define TD_CC_DATATOGGLEM 0x03 +#define TD_CC_STALL 0x04 +#define TD_DEVNOTRESP 0x05 +#define TD_PIDCHECKFAIL 0x06 +#define TD_UNEXPECTEDPID 0x07 +#define TD_DATAOVERRUN 0x08 +#define TD_DATAUNDERRUN 0x09 +#define TD_BUFFEROVERRUN 0x0C +#define TD_BUFFERUNDERRUN 0x0D +#define TD_NOTACCESSED 0x0F + + +#define MAXPSW 1 + +struct td { + __u32 hwINFO; + __u32 hwCBP; /* Current Buffer Pointer */ + __u32 hwNextTD; /* Next TD Pointer */ + __u32 hwBE; /* Memory Buffer End Pointer */ + + __u8 unused; + __u8 index; + struct ed *ed; + struct td *next_dl_td; + struct usb_device *usb_dev; + int transfer_len; + __u32 data; + + __u32 unused2[2]; +} __attribute__((aligned(32))); +typedef struct td td_t; + +#define OHCI_ED_SKIP (1 << 14) + +/* + * The HCCA (Host Controller Communications Area) is a 256 byte + * structure defined in the OHCI spec. that the host controller is + * told the base address of. It must be 256-byte aligned. + */ + +#define NUM_INTS 32 /* part of the OHCI standard */ +struct ohci_hcca { + __u32 int_table[NUM_INTS]; /* Interrupt ED table */ + __u16 pad1; /* set to 0 on each frame_no change */ + __u16 frame_no; /* current frame number */ + __u32 done_head; /* info returned for an interrupt */ + u8 reserved_for_hc[116]; +} __attribute__((aligned(256))); + + +/* + * Maximum number of root hub ports. + */ +#define MAX_ROOT_PORTS 15 /* maximum OHCI root hub ports */ + +/* + * This is the structure of the OHCI controller's memory mapped I/O + * region. This is Memory Mapped I/O. You must use the readl() and + * writel() macros defined in asm/io.h to access these!! + */ +struct ohci_regs { + /* control and status registers */ + __u32 revision; + __u32 control; + __u32 cmdstatus; + __u32 intrstatus; + __u32 intrenable; + __u32 intrdisable; + /* memory pointers */ + __u32 hcca; + __u32 ed_periodcurrent; + __u32 ed_controlhead; + __u32 ed_controlcurrent; + __u32 ed_bulkhead; + __u32 ed_bulkcurrent; + __u32 donehead; + /* frame counters */ + __u32 fminterval; + __u32 fmremaining; + __u32 fmnumber; + __u32 periodicstart; + __u32 lsthresh; + /* Root hub ports */ + struct ohci_roothub_regs { + __u32 a; + __u32 b; + __u32 status; + __u32 portstatus[MAX_ROOT_PORTS]; + } roothub; +} __attribute__((aligned(32))); + + +/* OHCI CONTROL AND STATUS REGISTER MASKS */ + +/* + * HcControl (control) register masks + */ +#define OHCI_CTRL_CBSR (3 << 0) /* control/bulk service ratio */ +#define OHCI_CTRL_PLE (1 << 2) /* periodic list enable */ +#define OHCI_CTRL_IE (1 << 3) /* isochronous enable */ +#define OHCI_CTRL_CLE (1 << 4) /* control list enable */ +#define OHCI_CTRL_BLE (1 << 5) /* bulk list enable */ +#define OHCI_CTRL_HCFS (3 << 6) /* host controller functional state */ +#define OHCI_CTRL_IR (1 << 8) /* interrupt routing */ +#define OHCI_CTRL_RWC (1 << 9) /* remote wakeup connected */ +#define OHCI_CTRL_RWE (1 << 10) /* remote wakeup enable */ + +/* pre-shifted values for HCFS */ +# define OHCI_USB_RESET (0 << 6) +# define OHCI_USB_RESUME (1 << 6) +# define OHCI_USB_OPER (2 << 6) +# define OHCI_USB_SUSPEND (3 << 6) + +/* + * HcCommandStatus (cmdstatus) register masks + */ +#define OHCI_HCR (1 << 0) /* host controller reset */ +#define OHCI_CLF (1 << 1) /* control list filled */ +#define OHCI_BLF (1 << 2) /* bulk list filled */ +#define OHCI_OCR (1 << 3) /* ownership change request */ +#define OHCI_SOC (3 << 16) /* scheduling overrun count */ + +/* + * masks used with interrupt registers: + * HcInterruptStatus (intrstatus) + * HcInterruptEnable (intrenable) + * HcInterruptDisable (intrdisable) + */ +#define OHCI_INTR_SO (1 << 0) /* scheduling overrun */ +#define OHCI_INTR_WDH (1 << 1) /* writeback of done_head */ +#define OHCI_INTR_SF (1 << 2) /* start frame */ +#define OHCI_INTR_RD (1 << 3) /* resume detect */ +#define OHCI_INTR_UE (1 << 4) /* unrecoverable error */ +#define OHCI_INTR_FNO (1 << 5) /* frame number overflow */ +#define OHCI_INTR_RHSC (1 << 6) /* root hub status change */ +#define OHCI_INTR_OC (1 << 30) /* ownership change */ +#define OHCI_INTR_MIE (1 << 31) /* master interrupt enable */ + + +/* Virtual Root HUB */ +struct virt_root_hub { + int devnum; /* Address of Root Hub endpoint */ + void *dev; /* was urb */ + void *int_addr; + int send; + int interval; +}; + +/* USB HUB CONSTANTS (not OHCI-specific; see hub.h) */ + +/* destination of request */ +#define RH_INTERFACE 0x01 +#define RH_ENDPOINT 0x02 +#define RH_OTHER 0x03 + +#define RH_CLASS 0x20 +#define RH_VENDOR 0x40 + +/* Requests: bRequest << 8 | bmRequestType */ +#define RH_GET_STATUS 0x0080 +#define RH_CLEAR_FEATURE 0x0100 +#define RH_SET_FEATURE 0x0300 +#define RH_SET_ADDRESS 0x0500 +#define RH_GET_DESCRIPTOR 0x0680 +#define RH_SET_DESCRIPTOR 0x0700 +#define RH_GET_CONFIGURATION 0x0880 +#define RH_SET_CONFIGURATION 0x0900 +#define RH_GET_STATE 0x0280 +#define RH_GET_INTERFACE 0x0A80 +#define RH_SET_INTERFACE 0x0B00 +#define RH_SYNC_FRAME 0x0C80 +/* Our Vendor Specific Request */ +#define RH_SET_EP 0x2000 + + +/* Hub port features */ +#define RH_PORT_CONNECTION 0x00 +#define RH_PORT_ENABLE 0x01 +#define RH_PORT_SUSPEND 0x02 +#define RH_PORT_OVER_CURRENT 0x03 +#define RH_PORT_RESET 0x04 +#define RH_PORT_POWER 0x08 +#define RH_PORT_LOW_SPEED 0x09 + +#define RH_C_PORT_CONNECTION 0x10 +#define RH_C_PORT_ENABLE 0x11 +#define RH_C_PORT_SUSPEND 0x12 +#define RH_C_PORT_OVER_CURRENT 0x13 +#define RH_C_PORT_RESET 0x14 + +/* Hub features */ +#define RH_C_HUB_LOCAL_POWER 0x00 +#define RH_C_HUB_OVER_CURRENT 0x01 + +#define RH_DEVICE_REMOTE_WAKEUP 0x00 +#define RH_ENDPOINT_STALL 0x01 + +#define RH_ACK 0x01 +#define RH_REQ_ERR -1 +#define RH_NACK 0x00 + + +/* OHCI ROOT HUB REGISTER MASKS */ + +/* roothub.portstatus [i] bits */ +#define RH_PS_CCS 0x00000001 /* current connect status */ +#define RH_PS_PES 0x00000002 /* port enable status*/ +#define RH_PS_PSS 0x00000004 /* port suspend status */ +#define RH_PS_POCI 0x00000008 /* port over current indicator */ +#define RH_PS_PRS 0x00000010 /* port reset status */ +#define RH_PS_PPS 0x00000100 /* port power status */ +#define RH_PS_LSDA 0x00000200 /* low speed device attached */ +#define RH_PS_CSC 0x00010000 /* connect status change */ +#define RH_PS_PESC 0x00020000 /* port enable status change */ +#define RH_PS_PSSC 0x00040000 /* port suspend status change */ +#define RH_PS_OCIC 0x00080000 /* over current indicator change */ +#define RH_PS_PRSC 0x00100000 /* port reset status change */ + +/* roothub.status bits */ +#define RH_HS_LPS 0x00000001 /* local power status */ +#define RH_HS_OCI 0x00000002 /* over current indicator */ +#define RH_HS_DRWE 0x00008000 /* device remote wakeup enable */ +#define RH_HS_LPSC 0x00010000 /* local power status change */ +#define RH_HS_OCIC 0x00020000 /* over current indicator change */ +#define RH_HS_CRWE 0x80000000 /* clear remote wakeup enable */ + +/* roothub.b masks */ +#define RH_B_DR 0x0000ffff /* device removable flags */ +#define RH_B_PPCM 0xffff0000 /* port power control mask */ + +/* roothub.a masks */ +#define RH_A_NDP (0xff << 0) /* number of downstream ports */ +#define RH_A_PSM (1 << 8) /* power switching mode */ +#define RH_A_NPS (1 << 9) /* no power switching */ +#define RH_A_DT (1 << 10) /* device type (mbz) */ +#define RH_A_OCPM (1 << 11) /* over current protection mode */ +#define RH_A_NOCP (1 << 12) /* no over current protection */ +#define RH_A_POTPGT (0xff << 24) /* power on to power good time */ + +/* urb */ +#define N_URB_TD 48 +typedef struct +{ + ed_t *ed; + __u16 length; /* number of tds associated with this request */ + __u16 td_cnt; /* number of tds already serviced */ + int state; + unsigned long pipe; + int actual_length; + td_t *td[N_URB_TD]; /* list pointer to all corresponding TDs associated with this request */ +} urb_priv_t; +#define URB_DEL 1 + +/* + * This is the full ohci controller description + * + * Note how the "proper" USB information is just + * a subset of what the full implementation needs. (Linus) + */ + + +typedef struct ohci { + struct ohci_hcca *hcca; /* hcca */ + /*dma_addr_t hcca_dma;*/ + + int irq; + int disabled; /* e.g. got a UE, we're hung */ + int sleeping; + unsigned long flags; /* for HC bugs */ + + struct ohci_regs *regs; /* OHCI controller's memory */ + + ed_t *ed_rm_list[2]; /* lists of all endpoints to be removed */ + ed_t *ed_bulktail; /* last endpoint of bulk list */ + ed_t *ed_controltail; /* last endpoint of control list */ + int intrstatus; + __u32 hc_control; /* copy of the hc control reg */ + struct usb_device *dev[32]; + struct virt_root_hub rh; + + const char *slot_name; +} ohci_t; + +#define NUM_EDS 8 /* num of preallocated endpoint descriptors */ + +struct ohci_device { + ed_t ed[NUM_EDS]; + int ed_cnt; +}; + +/* hcd */ +/* endpoint */ +static int ep_link(ohci_t * ohci, ed_t * ed); +static int ep_unlink(ohci_t * ohci, ed_t * ed); +static ed_t * ep_add_ed(struct usb_device * usb_dev, unsigned long pipe); + +/*-------------------------------------------------------------------------*/ + +/* we need more TDs than EDs */ +#define NUM_TD 64 + +/* +1 so we can align the storage */ +td_t gtd[NUM_TD+1]; +/* pointers to aligned storage */ +td_t *ptd; + +/* TDs ... */ +static inline struct td * +td_alloc (struct usb_device *usb_dev) +{ + int i; + struct td *td; + + td = NULL; + for (i = 0; i < NUM_TD; i++) + { + if (ptd[i].usb_dev == NULL) + { + td = &ptd[i]; + td->usb_dev = usb_dev; + break; + } + } + + return td; +} + +static inline void +ed_free (struct ed *ed) +{ + ed->usb_dev = NULL; +} diff --git a/arch/powerpc/cpu/mpc8220/Makefile b/arch/powerpc/cpu/mpc8220/Makefile new file mode 100644 index 0000000..b4fad28 --- /dev/null +++ b/arch/powerpc/cpu/mpc8220/Makefile @@ -0,0 +1,50 @@ +# +# (C) Copyright 2003-2006 +# Wolfgang Denk, DENX Software Engineering, wd@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 $(TOPDIR)/config.mk + +LIB = $(obj)lib$(CPU).a + +START = start.o +SOBJS = io.o fec_dma_tasks.o +COBJS = cpu.o cpu_init.o dramSetup.o fec.o i2c.o \ + interrupts.o loadtask.o speed.o \ + traps.o uart.o pci.o + +SRCS := $(START:.o=.S) $(SOBJS:.o=.S) $(COBJS:.o=.c) +OBJS := $(addprefix $(obj),$(SOBJS) $(COBJS)) +START := $(addprefix $(obj),$(START)) + +all: $(obj).depend $(START) $(LIB) + +$(LIB): $(OBJS) + $(AR) $(ARFLAGS) $@ $(OBJS) + +######################################################################### + +# defines $(obj).depend target +include $(SRCTREE)/rules.mk + +sinclude $(obj).depend + +######################################################################### diff --git a/arch/powerpc/cpu/mpc8220/config.mk b/arch/powerpc/cpu/mpc8220/config.mk new file mode 100644 index 0000000..e706883 --- /dev/null +++ b/arch/powerpc/cpu/mpc8220/config.mk @@ -0,0 +1,30 @@ +# +# (C) Copyright 2003 +# Wolfgang Denk, DENX Software Engineering, wd@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 +# + +PLATFORM_RELFLAGS += -fPIC -meabi + +PLATFORM_CPPFLAGS += -DCONFIG_MPC8220 -ffixed-r2 \ + -mstring -mcpu=603e -mmultiple + +# Use default linker script. Board port can override in board/*/config.mk +LDSCRIPT := $(SRCTREE)/arch/powerpc/cpu/mpc8220/u-boot.lds diff --git a/arch/powerpc/cpu/mpc8220/cpu.c b/arch/powerpc/cpu/mpc8220/cpu.c new file mode 100644 index 0000000..563cfe0 --- /dev/null +++ b/arch/powerpc/cpu/mpc8220/cpu.c @@ -0,0 +1,104 @@ +/* + * (C) Copyright 2000-2003 + * Wolfgang Denk, DENX Software Engineering, wd@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 + */ + +/* + * CPU specific code for the MPC8220 CPUs + */ + +#include <common.h> +#include <watchdog.h> +#include <command.h> +#include <mpc8220.h> +#include <netdev.h> +#include <asm/processor.h> + +DECLARE_GLOBAL_DATA_PTR; + +int checkcpu (void) +{ + ulong clock = gd->cpu_clk; + char buf[32]; + + puts ("CPU: "); + + printf (CPU_ID_STR); + + printf (" (JTAG ID %08lx)", *(vu_long *) (CONFIG_SYS_MBAR + 0x50)); + + printf (" at %s MHz\n", strmhz (buf, clock)); + + return 0; +} + +/* ------------------------------------------------------------------------- */ + +int do_reset (cmd_tbl_t * cmdtp, int flag, int argc, char *argv[]) +{ + volatile gptmr8220_t *gptmr = (volatile gptmr8220_t *) MMAP_GPTMR; + ulong msr; + + /* Interrupts and MMU off */ + __asm__ __volatile__ ("mfmsr %0":"=r" (msr):); + + msr &= ~(MSR_ME | MSR_EE | MSR_IR | MSR_DR); + __asm__ __volatile__ ("mtmsr %0"::"r" (msr)); + + /* Charge the watchdog timer */ + gptmr->Prescl = 10; + gptmr->Count = 1; + + gptmr->Mode = GPT_TMS_SGPIO; + + gptmr->Control = GPT_CTRL_WDEN | GPT_CTRL_CE; + + return 1; +} + +/* ------------------------------------------------------------------------- */ + +/* + * Get timebase clock frequency (like cpu_clk in Hz) + * + */ +unsigned long get_tbclk (void) +{ + ulong tbclk; + + tbclk = (gd->bus_clk + 3L) / 4L; + + return (tbclk); +} + +/* ------------------------------------------------------------------------- */ + +/* + * Initializes on-chip ethernet controllers. + * to override, implement board_eth_init() + */ +int cpu_eth_init(bd_t *bis) +{ +#if defined(CONFIG_MPC8220_FEC) + mpc8220_fec_initialize(bis); +#endif + return 0; +} diff --git a/arch/powerpc/cpu/mpc8220/cpu_init.c b/arch/powerpc/cpu/mpc8220/cpu_init.c new file mode 100644 index 0000000..8f52c7d --- /dev/null +++ b/arch/powerpc/cpu/mpc8220/cpu_init.c @@ -0,0 +1,136 @@ +/* + * (C) Copyright 2000-2003 + * Wolfgang Denk, DENX Software Engineering, wd@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> +#include <mpc8220.h> + +DECLARE_GLOBAL_DATA_PTR; + +/* + * Breath some life into the CPU... + * + * Set up the memory map, + * initialize a bunch of registers. + */ +void cpu_init_f (void) +{ + volatile flexbus8220_t *flexbus = (volatile flexbus8220_t *) MMAP_FB; + volatile pcfg8220_t *portcfg = (volatile pcfg8220_t *) MMAP_PCFG; + volatile xlbarb8220_t *xlbarb = (volatile xlbarb8220_t *) MMAP_XLBARB; + + /* Pointer is writable since we allocated a register for it */ + gd = (gd_t *) (CONFIG_SYS_INIT_RAM_ADDR + CONFIG_SYS_GBL_DATA_OFFSET); + + /* Clear initial global data */ + memset ((void *) gd, 0, sizeof (gd_t)); + + /* Clear all port configuration */ + portcfg->pcfg0 = 0; + portcfg->pcfg1 = 0; + portcfg->pcfg2 = 0; + portcfg->pcfg3 = 0; + portcfg->pcfg2 = CONFIG_SYS_GP1_PORT2_CONFIG; + portcfg->pcfg3 = CONFIG_SYS_PCI_PORT3_CONFIG | CONFIG_SYS_GP2_PORT3_CONFIG; + + /* + * Flexbus Controller: configure chip selects and enable them + */ +#if defined (CONFIG_SYS_CS0_BASE) + flexbus->csar0 = CONFIG_SYS_CS0_BASE; + +/* Sorcery-C can hang-up after CTRL reg initialization */ +#if defined (CONFIG_SYS_CS0_CTRL) + flexbus->cscr0 = CONFIG_SYS_CS0_CTRL; +#endif + flexbus->csmr0 = ((CONFIG_SYS_CS0_MASK - 1) & 0xffff0000) | 1; + __asm__ volatile ("sync"); +#endif +#if defined (CONFIG_SYS_CS1_BASE) + flexbus->csar1 = CONFIG_SYS_CS1_BASE; + flexbus->cscr1 = CONFIG_SYS_CS1_CTRL; + flexbus->csmr1 = ((CONFIG_SYS_CS1_MASK - 1) & 0xffff0000) | 1; + __asm__ volatile ("sync"); +#endif +#if defined (CONFIG_SYS_CS2_BASE) + flexbus->csar2 = CONFIG_SYS_CS2_BASE; + flexbus->cscr2 = CONFIG_SYS_CS2_CTRL; + flexbus->csmr2 = ((CONFIG_SYS_CS2_MASK - 1) & 0xffff0000) | 1; + portcfg->pcfg3 |= CONFIG_SYS_CS2_PORT3_CONFIG; + __asm__ volatile ("sync"); +#endif +#if defined (CONFIG_SYS_CS3_BASE) + flexbus->csar3 = CONFIG_SYS_CS3_BASE; + flexbus->cscr3 = CONFIG_SYS_CS3_CTRL; + flexbus->csmr3 = ((CONFIG_SYS_CS3_MASK - 1) & 0xffff0000) | 1; + portcfg->pcfg3 |= CONFIG_SYS_CS3_PORT3_CONFIG; + __asm__ volatile ("sync"); +#endif +#if defined (CONFIG_SYS_CS4_BASE) + flexbus->csar4 = CONFIG_SYS_CS4_BASE; + flexbus->cscr4 = CONFIG_SYS_CS4_CTRL; + flexbus->csmr4 = ((CONFIG_SYS_CS4_MASK - 1) & 0xffff0000) | 1; + portcfg->pcfg3 |= CONFIG_SYS_CS4_PORT3_CONFIG; + __asm__ volatile ("sync"); +#endif +#if defined (CONFIG_SYS_CS5_BASE) + flexbus->csar5 = CONFIG_SYS_CS5_BASE; + flexbus->cscr5 = CONFIG_SYS_CS5_CTRL; + flexbus->csmr5 = ((CONFIG_SYS_CS5_MASK - 1) & 0xffff0000) | 1; + portcfg->pcfg3 |= CONFIG_SYS_CS5_PORT3_CONFIG; + __asm__ volatile ("sync"); +#endif + + /* This section of the code cannot place in cpu_init_r(), + it will cause the system to hang */ + /* enable timebase */ + xlbarb->addrTenTimeOut = 0x1000; + xlbarb->dataTenTimeOut = 0x1000; + xlbarb->busActTimeOut = 0x2000; + + xlbarb->config = 0x00002000; + + /* Master Priority Enable */ + xlbarb->mastPriority = 0; + xlbarb->mastPriEn = 0xff; +} + +/* + * initialize higher level parts of CPU like time base and timers + */ +int cpu_init_r (void) +{ + /* this may belongs to disable interrupt section */ + /* mask all interrupts */ + *(vu_long *) 0xf0000700 = 0xfffffc00; + *(vu_long *) 0xf0000714 |= 0x0001ffff; + *(vu_long *) 0xf0000710 &= ~0x00000f00; + + /* route critical ints to normal ints */ + *(vu_long *) 0xf0000710 |= 0x00000001; + +#if defined(CONFIG_CMD_NET) && defined(CONFIG_MPC8220_FEC) + /* load FEC microcode */ + loadtask (0, 2); +#endif + return (0); +} diff --git a/arch/powerpc/cpu/mpc8220/dma.h b/arch/powerpc/cpu/mpc8220/dma.h new file mode 100644 index 0000000..d06ee63 --- /dev/null +++ b/arch/powerpc/cpu/mpc8220/dma.h @@ -0,0 +1,68 @@ +/* + * (C) Copyright 2003 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * This file is based on code + * (C) Copyright Motorola, Inc., 2000 + * + * MPC8220 dma header file + */ + +#ifndef __MPC8220_DMA_H +#define __MPC8220_DMA_H + +#include <common.h> +#include <mpc8220.h> + +/* Task number assignment */ +#define FEC_RECV_TASK_NO 0 +#define FEC_XMIT_TASK_NO 1 + +/*--------------------------------------------------------------------- + * Stuff for Ethernet Tx/Rx tasks + *--------------------------------------------------------------------- + */ + +/* Layout of Ethernet controller Parameter SRAM area: + * ---------------------------------------------------------------- + * 0x00: TBD_BASE, base address of TX BD ring + * 0x04: TBD_NEXT, address of next TX BD to be processed + * 0x08: RBD_BASE, base address of RX BD ring + * 0x0C: RBD_NEXT, address of next RX BD to be processed + * --------------------------------------------------------------- + * ALL PARAMETERS ARE ALL LONGWORDS (FOUR BYTES EACH). + */ + +/* base address of SRAM area to store parameters used by Ethernet tasks */ +#define FEC_PARAM_BASE (MMAP_SRAM + 0x5b00) + +/* base address of SRAM area for buffer descriptors */ +#define FEC_BD_BASE (MMAP_SRAM + 0x5b20) + +/*--------------------------------------------------------------------- + * common shortcuts used by driver C code + *--------------------------------------------------------------------- + */ + +/* Disable SmartDMA task */ +#define DMA_TASK_DISABLE(tasknum) \ +{ \ + volatile ushort *tcr = (ushort *)(MMAP_DMA + 0x0000001c + 2 * tasknum); \ + *tcr = (*tcr) & (~0x8000); \ +} + +/* Enable SmartDMA task */ +#define DMA_TASK_ENABLE(tasknum) \ +{ \ + volatile ushort *tcr = (ushort *) (MMAP_DMA + 0x0000001c + 2 * tasknum);\ + *tcr = (*tcr) | 0x8000; \ +} + +/* Clear interrupt pending bits */ +#define DMA_CLEAR_IEVENT(tasknum) \ +{ \ + struct mpc8220_dma *dma = (struct mpc8220_dma *)MMAP_DMA; \ + dma->IntPend = (1 << tasknum); \ +} + +#endif /* __MPC8220_DMA_H */ diff --git a/arch/powerpc/cpu/mpc8220/dramSetup.c b/arch/powerpc/cpu/mpc8220/dramSetup.c new file mode 100644 index 0000000..52cf133 --- /dev/null +++ b/arch/powerpc/cpu/mpc8220/dramSetup.c @@ -0,0 +1,752 @@ +/* + * (C) Copyright 2004, Freescale, Inc + * TsiChung Liew, Tsi-Chung.Liew@freescale.com + * + * 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 + */ + +/* +DESCRIPTION +Read Dram spd and base on its information to calculate the memory size, +characteristics to initialize the dram on MPC8220 +*/ + +#include <common.h> +#include <mpc8220.h> +#include "i2cCore.h" +#include "dramSetup.h" + +DECLARE_GLOBAL_DATA_PTR; + +#define SPD_SIZE CONFIG_SYS_SDRAM_SPD_SIZE +#define DRAM_SPD (CONFIG_SYS_SDRAM_SPD_I2C_ADDR)<<1 /* on Board SPD eeprom */ +#define TOTAL_BANK CONFIG_SYS_SDRAM_TOTAL_BANKS + +int spd_status (volatile i2c8220_t * pi2c, u8 sta_bit, u8 truefalse) +{ + int i; + + for (i = 0; i < I2C_POLL_COUNT; i++) { + if ((pi2c->sr & sta_bit) == (truefalse ? sta_bit : 0)) + return (OK); + } + + return (ERROR); +} + +int spd_clear (volatile i2c8220_t * pi2c) +{ + pi2c->adr = 0; + pi2c->fdr = 0; + pi2c->cr = 0; + pi2c->sr = 0; + + return (OK); +} + +int spd_stop (volatile i2c8220_t * pi2c) +{ + pi2c->cr &= ~I2C_CTL_STA; /* Generate stop signal */ + if (spd_status (pi2c, I2C_STA_BB, 0) != OK) + return ERROR; + + return (OK); +} + +int spd_readbyte (volatile i2c8220_t * pi2c, u8 * readb, int *index) +{ + pi2c->sr &= ~I2C_STA_IF; /* Clear Interrupt Bit */ + *readb = pi2c->dr; /* Read a byte */ + + /* + Set I2C_CTRL_TXAK will cause Transfer pending and + set I2C_CTRL_STA will cause Interrupt pending + */ + if (*index != 2) { + if (spd_status (pi2c, I2C_STA_CF, 1) != OK) /* Transfer not complete? */ + return ERROR; + } + + if (*index != 1) { + if (spd_status (pi2c, I2C_STA_IF, 1) != OK) + return ERROR; + } + + return (OK); +} + +int readSpdData (u8 * spdData) +{ + volatile i2c8220_t *pi2cReg; + volatile pcfg8220_t *pcfg; + u8 slvAdr = DRAM_SPD; + u8 Tmp; + int Length = SPD_SIZE; + int i = 0; + + /* Enable Port Configuration for SDA and SDL signals */ + pcfg = (volatile pcfg8220_t *) (MMAP_PCFG); + __asm__ ("sync"); + pcfg->pcfg3 &= ~CONFIG_SYS_I2C_PORT3_CONFIG; + __asm__ ("sync"); + + /* Points the structure to I2c mbar memory offset */ + pi2cReg = (volatile i2c8220_t *) (MMAP_I2C); + + + /* Clear FDR, ADR, SR and CR reg */ + pi2cReg->adr = 0; + pi2cReg->fdr = 0; + pi2cReg->cr = 0; + pi2cReg->sr = 0; + + /* Set for fix XLB Bus Frequency */ + switch (gd->bus_clk) { + case 60000000: + pi2cReg->fdr = 0x15; + break; + case 70000000: + pi2cReg->fdr = 0x16; + break; + case 80000000: + pi2cReg->fdr = 0x3a; + break; + case 90000000: + pi2cReg->fdr = 0x17; + break; + case 100000000: + pi2cReg->fdr = 0x3b; + break; + case 110000000: + pi2cReg->fdr = 0x18; + break; + case 120000000: + pi2cReg->fdr = 0x19; + break; + case 130000000: + pi2cReg->fdr = 0x1a; + break; + } + + pi2cReg->adr = CONFIG_SYS_I2C_SLAVE<<1; + + pi2cReg->cr = I2C_CTL_EN; /* Set Enable */ + + /* + The I2C bus should be in Idle state. If the bus is busy, + clear the STA bit in control register + */ + if (spd_status (pi2cReg, I2C_STA_BB, 0) != OK) { + if ((pi2cReg->cr & I2C_CTL_STA) == I2C_CTL_STA) + pi2cReg->cr &= ~I2C_CTL_STA; + + /* Check again if it is still busy, return error if found */ + if (spd_status (pi2cReg, I2C_STA_BB, 1) == OK) + return ERROR; + } + + pi2cReg->cr |= I2C_CTL_TX; /* Enable the I2c for TX, Ack */ + pi2cReg->cr |= I2C_CTL_STA; /* Generate start signal */ + + if (spd_status (pi2cReg, I2C_STA_BB, 1) != OK) + return ERROR; + + + /* Write slave address */ + pi2cReg->sr &= ~I2C_STA_IF; /* Clear Interrupt */ + pi2cReg->dr = slvAdr; /* Write a byte */ + + if (spd_status (pi2cReg, I2C_STA_CF, 1) != OK) { /* Transfer not complete? */ + spd_stop (pi2cReg); + return ERROR; + } + + if (spd_status (pi2cReg, I2C_STA_IF, 1) != OK) { + spd_stop (pi2cReg); + return ERROR; + } + + + /* Issue the offset to start */ + pi2cReg->sr &= ~I2C_STA_IF; /* Clear Interrupt */ + pi2cReg->dr = 0; /* Write a byte */ + + if (spd_status (pi2cReg, I2C_STA_CF, 1) != OK) { /* Transfer not complete? */ + spd_stop (pi2cReg); + return ERROR; + } + + if (spd_status (pi2cReg, I2C_STA_IF, 1) != OK) { + spd_stop (pi2cReg); + return ERROR; + } + + + /* Set repeat start */ + pi2cReg->cr |= I2C_CTL_RSTA; /* Repeat Start */ + + pi2cReg->sr &= ~I2C_STA_IF; /* Clear Interrupt */ + pi2cReg->dr = slvAdr | 1; /* Write a byte */ + + if (spd_status (pi2cReg, I2C_STA_CF, 1) != OK) { /* Transfer not complete? */ + spd_stop (pi2cReg); + return ERROR; + } + + if (spd_status (pi2cReg, I2C_STA_IF, 1) != OK) { + spd_stop (pi2cReg); + return ERROR; + } + + if (((pi2cReg->sr & 0x07) == 0x07) || (pi2cReg->sr & 0x01)) + return ERROR; + + pi2cReg->cr &= ~I2C_CTL_TX; /* Set receive mode */ + + if (((pi2cReg->sr & 0x07) == 0x07) || (pi2cReg->sr & 0x01)) + return ERROR; + + /* Dummy Read */ + if (spd_readbyte (pi2cReg, &Tmp, &i) != OK) { + spd_stop (pi2cReg); + return ERROR; + } + + i = 0; + while (Length) { + if (Length == 2) + pi2cReg->cr |= I2C_CTL_TXAK; + + if (Length == 1) + pi2cReg->cr &= ~I2C_CTL_STA; + + if (spd_readbyte (pi2cReg, spdData, &Length) != OK) { + return spd_stop (pi2cReg); + } + i++; + Length--; + spdData++; + } + + /* Stop the service */ + spd_stop (pi2cReg); + + return OK; +} + +int getBankInfo (int bank, draminfo_t * pBank) +{ + int status; + int checksum; + int count; + u8 spdData[SPD_SIZE]; + + + if (bank > 2 || pBank == 0) { + /* illegal values */ + return (-42); + } + + status = readSpdData (&spdData[0]); + if (status < 0) + return (-1); + + /* check the checksum */ + for (count = 0, checksum = 0; count < LOC_CHECKSUM; count++) + checksum += spdData[count]; + + checksum = checksum - ((checksum / 256) * 256); + + if (checksum != spdData[LOC_CHECKSUM]) + return (-2); + + /* Get the memory type */ + if (! + ((spdData[LOC_TYPE] == TYPE_DDR) + || (spdData[LOC_TYPE] == TYPE_SDR))) + /* not one of the types we support */ + return (-3); + + pBank->type = spdData[LOC_TYPE]; + + /* Set logical banks */ + pBank->banks = spdData[LOC_LOGICAL_BANKS]; + + /* Check that we have enough physical banks to cover the bank we are + * figuring out. Odd-numbered banks correspond to the second bank + * on the device. + */ + if (bank & 1) { + /* Second bank of a "device" */ + if (spdData[LOC_PHYS_BANKS] < 2) + /* this bank doesn't exist on the "device" */ + return (-4); + + if (spdData[LOC_ROWS] & 0xf0) + /* Two asymmetric banks */ + pBank->rows = spdData[LOC_ROWS] >> 4; + else + pBank->rows = spdData[LOC_ROWS]; + + if (spdData[LOC_COLS] & 0xf0) + /* Two asymmetric banks */ + pBank->cols = spdData[LOC_COLS] >> 4; + else + pBank->cols = spdData[LOC_COLS]; + } else { + /* First bank of a "device" */ + pBank->rows = spdData[LOC_ROWS]; + pBank->cols = spdData[LOC_COLS]; + } + + pBank->width = spdData[LOC_WIDTH_HIGH] << 8 | spdData[LOC_WIDTH_LOW]; + pBank->bursts = spdData[LOC_BURSTS]; + pBank->CAS = spdData[LOC_CAS]; + pBank->CS = spdData[LOC_CS]; + pBank->WE = spdData[LOC_WE]; + pBank->Trp = spdData[LOC_Trp]; + pBank->Trcd = spdData[LOC_Trcd]; + pBank->buffered = spdData[LOC_Buffered] & 1; + pBank->refresh = spdData[LOC_REFRESH]; + + return (0); +} + + +/* checkMuxSetting -- given a row/column device geometry, return a mask + * of the valid DRAM controller addr_mux settings for + * that geometry. + * + * Arguments: u8 rows: number of row addresses in this device + * u8 columns: number of column addresses in this device + * + * Returns: a mask of the allowed addr_mux settings for this + * geometry. Each bit in the mask represents a + * possible addr_mux settings (for example, the + * (1<<2) bit in the mask represents the 0b10 setting)/ + * + */ +u8 checkMuxSetting (u8 rows, u8 columns) +{ + muxdesc_t *pIdx, *pMux; + u8 mask; + int lrows, lcolumns; + u32 mux[4] = { 0x00080c04, 0x01080d03, 0x02080e02, 0xffffffff }; + + /* Setup MuxDescriptor in SRAM space */ + /* MUXDESC AddressRuns [] = { + { 0, 8, 12, 4 }, / setting, columns, rows, extra columns / + { 1, 8, 13, 3 }, / setting, columns, rows, extra columns / + { 2, 8, 14, 2 }, / setting, columns, rows, extra columns / + { 0xff } / list terminator / + }; */ + + pIdx = (muxdesc_t *) & mux[0]; + + /* Check rows x columns against each possible address mux setting */ + for (pMux = pIdx, mask = 0;; pMux++) { + lrows = rows; + lcolumns = columns; + + if (pMux->MuxValue == 0xff) + break; /* end of list */ + + /* For a given mux setting, since we want all the memory in a + * device to be contiguous, we want the device "use up" the + * address lines such that there are no extra column or row + * address lines on the device. + */ + + lcolumns -= pMux->Columns; + if (lcolumns < 0) + /* Not enough columns to get to the rows */ + continue; + + lrows -= pMux->Rows; + if (lrows > 0) + /* we have extra rows left -- can't do that! */ + continue; + + /* At this point, we either have to have used up all the + * rows or we have to have no columns left. + */ + + if (lcolumns != 0 && lrows != 0) + /* rows AND columns are left. Bad! */ + continue; + + lcolumns -= pMux->MoreColumns; + + if (lcolumns <= 0) + mask |= (1 << pMux->MuxValue); + } + + return (mask); +} + + +u32 dramSetup (void) +{ + draminfo_t DramInfo[TOTAL_BANK]; + draminfo_t *pDramInfo; + u32 size, temp, cfg_value, mode_value, refresh; + u8 *ptr; + u8 bursts, Trp, Trcd, type, buffered; + u8 muxmask, rows, columns; + int count, banknum; + u32 *prefresh, *pIdx; + u32 refrate[8] = { 15625, 3900, 7800, 31300, + 62500, 125000, 0xffffffff, 0xffffffff + }; + volatile sysconf8220_t *sysconf; + volatile memctl8220_t *memctl; + + sysconf = (volatile sysconf8220_t *) MMAP_MBAR; + memctl = (volatile memctl8220_t *) MMAP_MEMCTL; + + /* Set everything in the descriptions to zero */ + ptr = (u8 *) & DramInfo[0]; + for (count = 0; count < sizeof (DramInfo); count++) + *ptr++ = 0; + + for (banknum = 0; banknum < TOTAL_BANK; banknum++) + sysconf->cscfg[banknum]; + + /* Descriptions of row/column address muxing for various + * addr_mux settings. + */ + + pIdx = prefresh = (u32 *) & refrate[0]; + + /* Get all the info for all three logical banks */ + bursts = 0xff; + Trp = 0; + Trcd = 0; + type = 0; + buffered = 0xff; + refresh = 0xffffffff; + muxmask = 0xff; + + /* Two bank, CS0 and CS1 */ + for (banknum = 0, pDramInfo = &DramInfo[0]; + banknum < TOTAL_BANK; banknum++, pDramInfo++) { + pDramInfo->ordinal = banknum; /* initial sorting */ + if (getBankInfo (banknum, pDramInfo) < 0) + continue; + + /* get cumulative parameters of all three banks */ + if (type && pDramInfo->type != type) + return 0; + + type = pDramInfo->type; + rows = pDramInfo->rows; + columns = pDramInfo->cols; + + /* This chip only supports 13 DRAM memory lines, but some devices + * have 14 rows. To deal with this, ignore the 14th address line + * by limiting the number of rows (and columns) to 13. This will + * mean that for 14-row devices we will only be able to use + * half of the memory, but it's better than nothing. + */ + if (rows > 13) + rows = 13; + if (columns > 13) + columns = 13; + + pDramInfo->size = + ((1 << (rows + columns)) * pDramInfo->width); + pDramInfo->size *= pDramInfo->banks; + pDramInfo->size >>= 3; + + /* figure out which addr_mux configurations will support this device */ + muxmask &= checkMuxSetting (rows, columns); + if (muxmask == 0) + return 0; + + buffered = pDramInfo->buffered; + bursts &= pDramInfo->bursts; /* union of all bursts */ + if (pDramInfo->Trp > Trp) /* worst case (longest) Trp */ + Trp = pDramInfo->Trp; + + if (pDramInfo->Trcd > Trcd) /* worst case (longest) Trcd */ + Trcd = pDramInfo->Trcd; + + prefresh = pIdx; + /* worst case (shortest) Refresh period */ + if (refresh > prefresh[pDramInfo->refresh & 7]) + refresh = prefresh[pDramInfo->refresh & 7]; + + } /* for loop */ + + + /* We only allow a burst length of 8! */ + if (!(bursts & 8)) + bursts = 8; + + /* Sort the devices. In order to get each chip select region + * aligned properly, put the biggest device at the lowest address. + * A simple bubble sort will do the trick. + */ + for (banknum = 0, pDramInfo = &DramInfo[0]; + banknum < TOTAL_BANK; banknum++, pDramInfo++) { + int i; + + for (i = 0; i < TOTAL_BANK; i++) { + if (pDramInfo->size < DramInfo[i].size && + pDramInfo->ordinal < DramInfo[i].ordinal) { + /* If the current bank is smaller, but if the ordinal is also + * smaller, swap the ordinals + */ + u8 temp8; + + temp8 = DramInfo[i].ordinal; + DramInfo[i].ordinal = pDramInfo->ordinal; + pDramInfo->ordinal = temp8; + } + } + } + + + /* Now figure out the base address for each bank. While + * we're at it, figure out how much memory there is. + * + */ + size = 0; + for (banknum = 0; banknum < TOTAL_BANK; banknum++) { + int i; + + for (i = 0; i < TOTAL_BANK; i++) { + if (DramInfo[i].ordinal == banknum + && DramInfo[i].size != 0) { + DramInfo[i].base = size; + size += DramInfo[i].size; + } + } + } + + /* Set up the Drive Strength register */ + sysconf->sdramds = CONFIG_SYS_SDRAM_DRIVE_STRENGTH; + + /* ********************** Cfg 1 ************************* */ + + /* Set the single read to read/write/precharge delay */ + cfg_value = CFG1_SRD2RWP ((type == TYPE_DDR) ? 7 : 0xb); + + /* Set the single write to read/write/precharge delay. + * This may or may not be correct. The controller spec + * says "tWR", but "tWR" does not appear in the SPD. It + * always seems to be 15nsec for the class of device we're + * using, which turns out to be 2 clock cycles at 133MHz, + * so that's what we're going to use. + * + * HOWEVER, because of a bug in the controller, for DDR + * we need to set this to be the same as the value + * calculated for bwt2rwp. + */ + cfg_value |= CFG1_SWT2RWP ((type == TYPE_DDR) ? 7 : 2); + + /* Set the Read CAS latency. We're going to use a CL of + * 2.5 for DDR and 2 SDR. + */ + cfg_value |= CFG1_RLATENCY ((type == TYPE_DDR) ? 7 : 2); + + + /* Set the Active to Read/Write delay. This depends + * on Trcd which is reported as nanoseconds times 4. + * We want to calculate Trcd (in nanoseconds) times XLB clock (in Hz) + * which gives us a dimensionless quantity. Play games with + * the divisions so we don't run out of dynamic ranges. + */ + /* account for megaherz and the times 4 */ + temp = (Trcd * (gd->bus_clk / 1000000)) / 4; + + /* account for nanoseconds and round up, with a minimum value of 2 */ + temp = ((temp + 999) / 1000) - 1; + if (temp < 2) + temp = 2; + + cfg_value |= CFG1_ACT2WR (temp); + + /* Set the precharge to active delay. This depends + * on Trp which is reported as nanoseconds times 4. + * We want to calculate Trp (in nanoseconds) times XLB clock (in Hz) + * which gives us a dimensionless quantity. Play games with + * the divisions so we don't run out of dynamic ranges. + */ + /* account for megaherz and the times 4 */ + temp = (Trp * (gd->bus_clk / 1000000)) / 4; + + /* account for nanoseconds and round up, then subtract 1, with a + * minumum value of 1 and a maximum value of 7. + */ + temp = (((temp + 999) / 1000) - 1) & 7; + if (temp < 1) + temp = 1; + + cfg_value |= CFG1_PRE2ACT (temp); + + /* Set refresh to active delay. This depends + * on Trfc which is not reported in the SPD. + * We'll use a nominal value of 75nsec which is + * what the controller spec uses. + */ + temp = (75 * (gd->bus_clk / 1000000)); + /* account for nanoseconds and round up, then subtract 1 */ + cfg_value |= CFG1_REF2ACT (((temp + 999) / 1000) - 1); + + /* Set the write latency, using the values given in the controller spec */ + cfg_value |= CFG1_WLATENCY ((type == TYPE_DDR) ? 3 : 0); + memctl->cfg1 = cfg_value; /* cfg 1 */ + asm volatile ("sync"); + + + /* ********************** Cfg 2 ************************* */ + + /* Set the burst read to read/precharge delay */ + cfg_value = CFG2_BRD2RP ((type == TYPE_DDR) ? 5 : 8); + + /* Set the burst write to read/precharge delay. Semi-magic numbers + * based on the controller spec recommendations, assuming tWR is + * two clock cycles. + */ + cfg_value |= CFG2_BWT2RWP ((type == TYPE_DDR) ? 7 : 10); + + /* Set the Burst read to write delay. Semi-magic numbers + * based on the DRAM controller documentation. + */ + cfg_value |= CFG2_BRD2WT ((type == TYPE_DDR) ? 7 : 0xb); + + /* Set the burst length -- must be 8!! Well, 7, actually, becuase + * it's burst lenght minus 1. + */ + cfg_value |= CFG2_BURSTLEN (7); + memctl->cfg2 = cfg_value; /* cfg 2 */ + asm volatile ("sync"); + + + /* ********************** mode ************************* */ + + /* Set enable bit, CKE high/low bits, and the DDR/SDR mode bit, + * disable automatic refresh. + */ + cfg_value = CTL_MODE_ENABLE | CTL_CKE_HIGH | + ((type == TYPE_DDR) ? CTL_DDR_MODE : 0); + + /* Set the address mux based on whichever setting(s) is/are common + * to all the devices we have. If there is more than one, choose + * one arbitrarily. + */ + if (muxmask & 0x4) + cfg_value |= CTL_ADDRMUX (2); + else if (muxmask & 0x2) + cfg_value |= CTL_ADDRMUX (1); + else + cfg_value |= CTL_ADDRMUX (0); + + /* Set the refresh interval. */ + temp = ((refresh * (gd->bus_clk / 1000000)) / (1000 * 64)) - 1; + cfg_value |= CTL_REFRESH_INTERVAL (temp); + + /* Set buffered/non-buffered memory */ + if (buffered) + cfg_value |= CTL_BUFFERED; + + memctl->ctrl = cfg_value; /* ctrl */ + asm volatile ("sync"); + + if (type == TYPE_DDR) { + /* issue precharge all */ + temp = cfg_value | CTL_PRECHARGE_CMD; + memctl->ctrl = temp; /* ctrl */ + asm volatile ("sync"); + } + + + /* Set up mode value for CAS latency */ +#if (CONFIG_SYS_SDRAM_CAS_LATENCY==5) /* CL=2.5 */ + mode_value = (MODE_MODE | MODE_BURSTLEN (MODE_BURSTLEN_8) | + MODE_BT_SEQUENTIAL | MODE_CL (MODE_CL_2p5) | MODE_CMD); +#else + mode_value = (MODE_MODE | MODE_BURSTLEN (MODE_BURSTLEN_8) | + MODE_BT_SEQUENTIAL | MODE_CL (MODE_CL_2) | MODE_CMD); +#endif + asm volatile ("sync"); + + /* Write Extended Mode - enable DLL */ + if (type == TYPE_DDR) { + temp = MODE_EXTENDED | MODE_X_DLL_ENABLE | + MODE_X_DS_NORMAL | MODE_CMD; + memctl->mode = (temp >> 16); /* mode */ + asm volatile ("sync"); + + /* Write Mode - reset DLL, set CAS latency */ + temp = mode_value | MODE_OPMODE (MODE_OPMODE_RESETDLL); + memctl->mode = (temp >> 16); /* mode */ + asm volatile ("sync"); + } + + /* Program the chip selects. */ + for (banknum = 0; banknum < TOTAL_BANK; banknum++) { + if (DramInfo[banknum].size != 0) { + u32 mask; + int i; + + for (i = 0, mask = 1; i < 32; mask <<= 1, i++) { + if (DramInfo[banknum].size & mask) + break; + } + temp = (DramInfo[banknum].base & 0xfff00000) | (i - + 1); + + sysconf->cscfg[banknum] = temp; + asm volatile ("sync"); + } + } + + /* Wait for DLL lock */ + udelay (200); + + temp = cfg_value | CTL_PRECHARGE_CMD; /* issue precharge all */ + memctl->ctrl = temp; /* ctrl */ + asm volatile ("sync"); + + temp = cfg_value | CTL_REFRESH_CMD; /* issue precharge all */ + memctl->ctrl = temp; /* ctrl */ + asm volatile ("sync"); + + memctl->ctrl = temp; /* ctrl */ + asm volatile ("sync"); + + /* Write Mode - DLL normal */ + temp = mode_value | MODE_OPMODE (MODE_OPMODE_NORMAL); + memctl->mode = (temp >> 16); /* mode */ + asm volatile ("sync"); + + /* Enable refresh, enable DQS's (if DDR), and lock the control register */ + cfg_value &= ~CTL_MODE_ENABLE; /* lock register */ + cfg_value |= CTL_REFRESH_ENABLE; /* enable refresh */ + + if (type == TYPE_DDR) + cfg_value |= CTL_DQSOEN (0xf); /* enable DQS's for DDR */ + + memctl->ctrl = cfg_value; /* ctrl */ + asm volatile ("sync"); + + return size; +} diff --git a/arch/powerpc/cpu/mpc8220/dramSetup.h b/arch/powerpc/cpu/mpc8220/dramSetup.h new file mode 100644 index 0000000..3b64e08 --- /dev/null +++ b/arch/powerpc/cpu/mpc8220/dramSetup.h @@ -0,0 +1,108 @@ +/* + * dramSetup.h + * + * Prototypes, etc. for the Motorola MPC8220 + * embedded cpu chips + * + * 2004 (c) Freescale, Inc. + * Author: TsiChung Liew <Tsi-Chung.Liew@freescale.com> + * + * 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 + */ +#ifndef __INCdramsetuph +#define __INCdramsetuph +#ifndef __ASSEMBLY__ +/* Where various things are in the SPD */ +#define LOC_TYPE 2 +#define LOC_CHECKSUM 63 +#define LOC_PHYS_BANKS 5 +#define LOC_LOGICAL_BANKS 17 +#define LOC_ROWS 3 +#define LOC_COLS 4 +#define LOC_WIDTH_HIGH 7 +#define LOC_WIDTH_LOW 6 +#define LOC_REFRESH 12 +#define LOC_BURSTS 16 +#define LOC_CAS 18 +#define LOC_CS 19 +#define LOC_WE 20 +#define LOC_Tcyc 9 +#define LOC_Tac 10 +#define LOC_Trp 27 +#define LOC_Trrd 28 +#define LOC_Trcd 29 +#define LOC_Tras 30 +#define LOC_Buffered 21 +/* Types of memory the SPD can tell us about. + * We can actually only use SDRAM and DDR. + */ +#define TYPE_DRAM 1 /* plain old dram */ +#define TYPE_EDO 2 /* EDO dram */ +#define TYPE_Nibble 3 /* serial nibble memory */ +#define TYPE_SDR 4 /* SDRAM */ +#define TYPE_ROM 5 /* */ +#define TYPE_SGRRAM 6 /* graphics memory */ +#define TYPE_DDR 7 /* DDR sdram */ +#define SDRAMDS_MASK 0x3 /* each field is 2 bits wide */ +#define SDRAMDS_SBE_SHIFT 8 /* Clock enable drive strength */ +#define SDRAMDS_SBC_SHIFT 6 /* Clocks drive strength */ +#define SDRAMDS_SBA_SHIFT 4 /* Address drive strength */ +#define SDRAMDS_SBS_SHIFT 2 /* SDR DQS drive strength */ +#define SDRAMDS_SBD_SHIFT 0 /* Data and DQS drive strength */ +#define DRIVE_STRENGTH_HIGH 0 +#define DRIVE_STRENGTH_MED 1 +#define DRIVE_STRENGTH_LOW 2 +#define DRIVE_STRENGTH_OFF 3 + +#define OK 0 +#define ERROR -1 +/* Structure to hold information about address muxing. */ + typedef struct tagMuxDescriptor { + u8 MuxValue; + u8 Columns; + u8 Rows; + u8 MoreColumns; +} muxdesc_t; + +/* Structure to define one physical bank of + * memory. Note that dram size in bytes is + * (2^^(rows+columns)) * width * banks / 8 +*/ +typedef struct tagDramInfo { + u32 size; /* size in bytes */ + u32 base; /* base address */ + u8 ordinal; /* where in the memory map will we put this */ + u8 type; + u8 rows; + u8 cols; + u16 width; /* width of each chip in bits */ + u8 banks; /* number of chips, aka logical banks */ + u8 bursts; /* bit-encoded allowable burst length */ + u8 CAS; /* bit-encoded CAS latency values */ + u8 CS; /* bit-encoded CS latency values */ + u8 WE; /* bit-encoded WE latency values */ + u8 Trp; /* bit-encoded row precharge time */ + u8 Trcd; /* bit-encoded RAS to CAS delay */ + u8 buffered; /* buffered or not */ + u8 refresh; /* encoded refresh rate */ +} draminfo_t; + +#endif /* __ASSEMBLY__ */ + +#endif /* __INCdramsetuph */ diff --git a/arch/powerpc/cpu/mpc8220/fec.c b/arch/powerpc/cpu/mpc8220/fec.c new file mode 100644 index 0000000..992e0ff --- /dev/null +++ b/arch/powerpc/cpu/mpc8220/fec.c @@ -0,0 +1,1000 @@ +/* + * (C) Copyright 2003 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * This file is based on mpc4200fec.c, + * (C) Copyright Motorola, Inc., 2000 + */ + +#include <common.h> +#include <mpc8220.h> +#include <malloc.h> +#include <net.h> +#include <miiphy.h> +#include "dma.h" +#include "fec.h" + +#undef DEBUG +#if defined(CONFIG_CMD_NET) && defined(CONFIG_NET_MULTI) && \ + defined(CONFIG_MPC8220_FEC) + +#if !(defined(CONFIG_MII) || defined(CONFIG_CMD_MII)) +#error "CONFIG_MII has to be defined!" +#endif + +#ifdef DEBUG +static void tfifo_print (char *devname, mpc8220_fec_priv * fec); +static void rfifo_print (char *devname, mpc8220_fec_priv * fec); +#endif /* DEBUG */ + +#ifdef DEBUG +static u32 local_crc32 (char *string, unsigned int crc_value, int len); +#endif + +typedef struct { + u8 data[1500]; /* actual data */ + int length; /* actual length */ + int used; /* buffer in use or not */ + u8 head[16]; /* MAC header(6 + 6 + 2) + 2(aligned) */ +} NBUF; + +int fec8220_miiphy_read (char *devname, u8 phyAddr, u8 regAddr, u16 * retVal); +int fec8220_miiphy_write (char *devname, u8 phyAddr, u8 regAddr, u16 data); + +/********************************************************************/ +#ifdef DEBUG +static void mpc8220_fec_phydump (char *devname) +{ + u16 phyStatus, i; + u8 phyAddr = CONFIG_PHY_ADDR; + u8 reg_mask[] = { +#if CONFIG_PHY_TYPE == 0x79c874 /* AMD Am79C874 */ + /* regs to print: 0...7, 16...19, 21, 23, 24 */ + 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, +#else + /* regs to print: 0...8, 16...20 */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, + 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +#endif + }; + + for (i = 0; i < 32; i++) { + if (reg_mask[i]) { + miiphy_read (devname, phyAddr, i, &phyStatus); + printf ("Mii reg %d: 0x%04x\n", i, phyStatus); + } + } +} +#endif + +/********************************************************************/ +static int mpc8220_fec_rbd_init (mpc8220_fec_priv * fec) +{ + int ix; + char *data; + static int once = 0; + + for (ix = 0; ix < FEC_RBD_NUM; ix++) { + if (!once) { + data = (char *) malloc (FEC_MAX_PKT_SIZE); + if (data == NULL) { + printf ("RBD INIT FAILED\n"); + return -1; + } + fec->rbdBase[ix].dataPointer = (u32) data; + } + fec->rbdBase[ix].status = FEC_RBD_EMPTY; + fec->rbdBase[ix].dataLength = 0; + } + once++; + + /* + * have the last RBD to close the ring + */ + fec->rbdBase[ix - 1].status |= FEC_RBD_WRAP; + fec->rbdIndex = 0; + + return 0; +} + +/********************************************************************/ +static void mpc8220_fec_tbd_init (mpc8220_fec_priv * fec) +{ + int ix; + + for (ix = 0; ix < FEC_TBD_NUM; ix++) { + fec->tbdBase[ix].status = 0; + } + + /* + * Have the last TBD to close the ring + */ + fec->tbdBase[ix - 1].status |= FEC_TBD_WRAP; + + /* + * Initialize some indices + */ + fec->tbdIndex = 0; + fec->usedTbdIndex = 0; + fec->cleanTbdNum = FEC_TBD_NUM; +} + +/********************************************************************/ +static void mpc8220_fec_rbd_clean (mpc8220_fec_priv * fec, FEC_RBD * pRbd) +{ + /* + * Reset buffer descriptor as empty + */ + if ((fec->rbdIndex) == (FEC_RBD_NUM - 1)) + pRbd->status = (FEC_RBD_WRAP | FEC_RBD_EMPTY); + else + pRbd->status = FEC_RBD_EMPTY; + + pRbd->dataLength = 0; + + /* + * Now, we have an empty RxBD, restart the SmartDMA receive task + */ + DMA_TASK_ENABLE (FEC_RECV_TASK_NO); + + /* + * Increment BD count + */ + fec->rbdIndex = (fec->rbdIndex + 1) % FEC_RBD_NUM; +} + +/********************************************************************/ +static void mpc8220_fec_tbd_scrub (mpc8220_fec_priv * fec) +{ + FEC_TBD *pUsedTbd; + +#ifdef DEBUG + printf ("tbd_scrub: fec->cleanTbdNum = %d, fec->usedTbdIndex = %d\n", + fec->cleanTbdNum, fec->usedTbdIndex); +#endif + + /* + * process all the consumed TBDs + */ + while (fec->cleanTbdNum < FEC_TBD_NUM) { + pUsedTbd = &fec->tbdBase[fec->usedTbdIndex]; + if (pUsedTbd->status & FEC_TBD_READY) { +#ifdef DEBUG + printf ("Cannot clean TBD %d, in use\n", + fec->cleanTbdNum); +#endif + return; + } + + /* + * clean this buffer descriptor + */ + if (fec->usedTbdIndex == (FEC_TBD_NUM - 1)) + pUsedTbd->status = FEC_TBD_WRAP; + else + pUsedTbd->status = 0; + + /* + * update some indeces for a correct handling of the TBD ring + */ + fec->cleanTbdNum++; + fec->usedTbdIndex = (fec->usedTbdIndex + 1) % FEC_TBD_NUM; + } +} + +/********************************************************************/ +static void mpc8220_fec_set_hwaddr (mpc8220_fec_priv * fec, char *mac) +{ + u8 currByte; /* byte for which to compute the CRC */ + int byte; /* loop - counter */ + int bit; /* loop - counter */ + u32 crc = 0xffffffff; /* initial value */ + + /* + * The algorithm used is the following: + * we loop on each of the six bytes of the provided address, + * and we compute the CRC by left-shifting the previous + * value by one position, so that each bit in the current + * byte of the address may contribute the calculation. If + * the latter and the MSB in the CRC are different, then + * the CRC value so computed is also ex-ored with the + * "polynomium generator". The current byte of the address + * is also shifted right by one bit at each iteration. + * This is because the CRC generatore in hardware is implemented + * as a shift-register with as many ex-ores as the radixes + * in the polynomium. This suggests that we represent the + * polynomiumm itself as a 32-bit constant. + */ + for (byte = 0; byte < 6; byte++) { + currByte = mac[byte]; + for (bit = 0; bit < 8; bit++) { + if ((currByte & 0x01) ^ (crc & 0x01)) { + crc >>= 1; + crc = crc ^ 0xedb88320; + } else { + crc >>= 1; + } + currByte >>= 1; + } + } + + crc = crc >> 26; + + /* + * Set individual hash table register + */ + if (crc >= 32) { + fec->eth->iaddr1 = (1 << (crc - 32)); + fec->eth->iaddr2 = 0; + } else { + fec->eth->iaddr1 = 0; + fec->eth->iaddr2 = (1 << crc); + } + + /* + * Set physical address + */ + fec->eth->paddr1 = + (mac[0] << 24) + (mac[1] << 16) + (mac[2] << 8) + mac[3]; + fec->eth->paddr2 = (mac[4] << 24) + (mac[5] << 16) + 0x8808; +} + +/********************************************************************/ +static int mpc8220_fec_init (struct eth_device *dev, bd_t * bis) +{ + mpc8220_fec_priv *fec = (mpc8220_fec_priv *) dev->priv; + struct mpc8220_dma *dma = (struct mpc8220_dma *) MMAP_DMA; + const u8 phyAddr = CONFIG_PHY_ADDR; /* Only one PHY */ + +#ifdef DEBUG + printf ("mpc8220_fec_init... Begin\n"); +#endif + + /* + * Initialize RxBD/TxBD rings + */ + mpc8220_fec_rbd_init (fec); + mpc8220_fec_tbd_init (fec); + + /* + * Set up Pin Muxing for FEC 1 + */ + *(vu_long *) MMAP_PCFG = 0; + *(vu_long *) (MMAP_PCFG + 4) = 0; + /* + * Clear FEC-Lite interrupt event register(IEVENT) + */ + fec->eth->ievent = 0xffffffff; + + /* + * Set interrupt mask register + */ + fec->eth->imask = 0x00000000; + + /* + * Set FEC-Lite receive control register(R_CNTRL): + */ + if (fec->xcv_type == SEVENWIRE) { + /* + * Frame length=1518; 7-wire mode + */ + fec->eth->r_cntrl = 0x05ee0020; /*0x05ee0000;FIXME */ + } else { + /* + * Frame length=1518; MII mode; + */ + fec->eth->r_cntrl = 0x05ee0024; /*0x05ee0004;FIXME */ + } + + fec->eth->x_cntrl = 0x00000000; /* half-duplex, heartbeat disabled */ + if (fec->xcv_type != SEVENWIRE) { + /* + * Set MII_SPEED = (1/(mii_speed * 2)) * System Clock + * and do not drop the Preamble. + */ + /* tbd - rtm */ + /*fec->eth->mii_speed = (((gd->ipb_clk >> 20) / 5) << 1); */ + /* No MII for 7-wire mode */ + fec->eth->mii_speed = 0x00000030; + } + + /* + * Set Opcode/Pause Duration Register + */ + fec->eth->op_pause = 0x00010020; /*FIXME0xffff0020; */ + + /* + * Set Rx FIFO alarm and granularity value + */ + fec->eth->rfifo_cntrl = 0x0c000000; + fec->eth->rfifo_alarm = 0x0000030c; +#ifdef DEBUG + if (fec->eth->rfifo_status & 0x00700000) { + printf ("mpc8220_fec_init() RFIFO error\n"); + } +#endif + + /* + * Set Tx FIFO granularity value + */ + /*fec->eth->tfifo_cntrl = 0x0c000000; */ /*tbd - rtm */ + fec->eth->tfifo_cntrl = 0x0e000000; +#ifdef DEBUG + printf ("tfifo_status: 0x%08x\n", fec->eth->tfifo_status); + printf ("tfifo_alarm: 0x%08x\n", fec->eth->tfifo_alarm); +#endif + + /* + * Set transmit fifo watermark register(X_WMRK), default = 64 + */ + fec->eth->tfifo_alarm = 0x00000080; + fec->eth->x_wmrk = 0x2; + + /* + * Set individual address filter for unicast address + * and set physical address registers. + */ + mpc8220_fec_set_hwaddr (fec, (char *)(dev->enetaddr)); + + /* + * Set multicast address filter + */ + fec->eth->gaddr1 = 0x00000000; + fec->eth->gaddr2 = 0x00000000; + + /* + * Turn ON cheater FSM: ???? + */ + fec->eth->xmit_fsm = 0x03000000; + +#if 1 +/*#if defined(CONFIG_MPC5200)*/ + /* + * Turn off COMM bus prefetch in the MGT5200 BestComm. It doesn't + * work w/ the current receive task. + */ + dma->PtdCntrl |= 0x00000001; +#endif + + /* + * Set priority of different initiators + */ + dma->IPR0 = 7; /* always */ + dma->IPR3 = 6; /* Eth RX */ + dma->IPR4 = 5; /* Eth Tx */ + + /* + * Clear SmartDMA task interrupt pending bits + */ + DMA_CLEAR_IEVENT (FEC_RECV_TASK_NO); + + /* + * Initialize SmartDMA parameters stored in SRAM + */ + *(int *) FEC_TBD_BASE = (int) fec->tbdBase; + *(int *) FEC_RBD_BASE = (int) fec->rbdBase; + *(int *) FEC_TBD_NEXT = (int) fec->tbdBase; + *(int *) FEC_RBD_NEXT = (int) fec->rbdBase; + + if (fec->xcv_type != SEVENWIRE) { + /* + * Initialize PHY(LXT971A): + * + * Generally, on power up, the LXT971A reads its configuration + * pins to check for forced operation, If not cofigured for + * forced operation, it uses auto-negotiation/parallel detection + * to automatically determine line operating conditions. + * If the PHY device on the other side of the link supports + * auto-negotiation, the LXT971A auto-negotiates with it + * using Fast Link Pulse(FLP) Bursts. If the PHY partner does not + * support auto-negotiation, the LXT971A automatically detects + * the presence of either link pulses(10Mbps PHY) or Idle + * symbols(100Mbps) and sets its operating conditions accordingly. + * + * When auto-negotiation is controlled by software, the following + * steps are recommended. + * + * Note: + * The physical address is dependent on hardware configuration. + * + */ + int timeout = 1; + u16 phyStatus; + + /* + * Reset PHY, then delay 300ns + */ + miiphy_write (dev->name, phyAddr, 0x0, 0x8000); + udelay (1000); + + if (fec->xcv_type == MII10) { + /* + * Force 10Base-T, FDX operation + */ +#ifdef DEBUG + printf ("Forcing 10 Mbps ethernet link... "); +#endif + miiphy_read (dev->name, phyAddr, 0x1, &phyStatus); + /* + miiphy_write(fec, phyAddr, 0x0, 0x0100); + */ + miiphy_write (dev->name, phyAddr, 0x0, 0x0180); + + timeout = 20; + do { /* wait for link status to go down */ + udelay (10000); + if ((timeout--) == 0) { +#ifdef DEBUG + printf ("hmmm, should not have waited..."); +#endif + break; + } + miiphy_read (dev->name, phyAddr, 0x1, &phyStatus); +#ifdef DEBUG + printf ("="); +#endif + } while ((phyStatus & 0x0004)); /* !link up */ + + timeout = 1000; + do { /* wait for link status to come back up */ + udelay (10000); + if ((timeout--) == 0) { + printf ("failed. Link is down.\n"); + break; + } + miiphy_read (dev->name, phyAddr, 0x1, &phyStatus); +#ifdef DEBUG + printf ("+"); +#endif + } while (!(phyStatus & 0x0004)); /* !link up */ + +#ifdef DEBUG + printf ("done.\n"); +#endif + } else { /* MII100 */ + /* + * Set the auto-negotiation advertisement register bits + */ + miiphy_write (dev->name, phyAddr, 0x4, 0x01e1); + + /* + * Set MDIO bit 0.12 = 1(&& bit 0.9=1?) to enable auto-negotiation + */ + miiphy_write (dev->name, phyAddr, 0x0, 0x1200); + + /* + * Wait for AN completion + */ + timeout = 5000; + do { + udelay (1000); + + if ((timeout--) == 0) { +#ifdef DEBUG + printf ("PHY auto neg 0 failed...\n"); +#endif + return -1; + } + + if (miiphy_read (dev->name, phyAddr, 0x1, &phyStatus) != + 0) { +#ifdef DEBUG + printf ("PHY auto neg 1 failed 0x%04x...\n", phyStatus); +#endif + return -1; + } + } while (!(phyStatus & 0x0004)); + +#ifdef DEBUG + printf ("PHY auto neg complete! \n"); +#endif + } + + } + + /* + * Enable FEC-Lite controller + */ + fec->eth->ecntrl |= 0x00000006; + +#ifdef DEBUG + if (fec->xcv_type != SEVENWIRE) + mpc8220_fec_phydump (dev->name); +#endif + + /* + * Enable SmartDMA receive task + */ + DMA_TASK_ENABLE (FEC_RECV_TASK_NO); + +#ifdef DEBUG + printf ("mpc8220_fec_init... Done \n"); +#endif + + return 1; +} + +/********************************************************************/ +static void mpc8220_fec_halt (struct eth_device *dev) +{ + mpc8220_fec_priv *fec = (mpc8220_fec_priv *) dev->priv; + int counter = 0xffff; + +#ifdef DEBUG + if (fec->xcv_type != SEVENWIRE) + mpc8220_fec_phydump (dev->name); +#endif + + /* + * mask FEC chip interrupts + */ + fec->eth->imask = 0; + + /* + * issue graceful stop command to the FEC transmitter if necessary + */ + fec->eth->x_cntrl |= 0x00000001; + + /* + * wait for graceful stop to register + */ + while ((counter--) && (!(fec->eth->ievent & 0x10000000))); + + /* + * Disable SmartDMA tasks + */ + DMA_TASK_DISABLE (FEC_XMIT_TASK_NO); + DMA_TASK_DISABLE (FEC_RECV_TASK_NO); + + /* + * Disable the Ethernet Controller + */ + fec->eth->ecntrl &= 0xfffffffd; + + /* + * Clear FIFO status registers + */ + fec->eth->rfifo_status &= 0x00700000; + fec->eth->tfifo_status &= 0x00700000; + + fec->eth->reset_cntrl = 0x01000000; + + /* + * Issue a reset command to the FEC chip + */ + fec->eth->ecntrl |= 0x1; + + /* + * wait at least 16 clock cycles + */ + udelay (10); + +#ifdef DEBUG + printf ("Ethernet task stopped\n"); +#endif +} + +#ifdef DEBUG +/********************************************************************/ + +static void tfifo_print (char *devname, mpc8220_fec_priv * fec) +{ + u16 phyAddr = CONFIG_PHY_ADDR; + u16 phyStatus; + + if ((fec->eth->tfifo_lrf_ptr != fec->eth->tfifo_lwf_ptr) + || (fec->eth->tfifo_rdptr != fec->eth->tfifo_wrptr)) { + + miiphy_read (devname, phyAddr, 0x1, &phyStatus); + printf ("\nphyStatus: 0x%04x\n", phyStatus); + printf ("ecntrl: 0x%08x\n", fec->eth->ecntrl); + printf ("ievent: 0x%08x\n", fec->eth->ievent); + printf ("x_status: 0x%08x\n", fec->eth->x_status); + printf ("tfifo: status 0x%08x\n", fec->eth->tfifo_status); + + printf (" control 0x%08x\n", fec->eth->tfifo_cntrl); + printf (" lrfp 0x%08x\n", fec->eth->tfifo_lrf_ptr); + printf (" lwfp 0x%08x\n", fec->eth->tfifo_lwf_ptr); + printf (" alarm 0x%08x\n", fec->eth->tfifo_alarm); + printf (" readptr 0x%08x\n", fec->eth->tfifo_rdptr); + printf (" writptr 0x%08x\n", fec->eth->tfifo_wrptr); + } +} + +static void rfifo_print (char *devname, mpc8220_fec_priv * fec) +{ + u16 phyAddr = CONFIG_PHY_ADDR; + u16 phyStatus; + + if ((fec->eth->rfifo_lrf_ptr != fec->eth->rfifo_lwf_ptr) + || (fec->eth->rfifo_rdptr != fec->eth->rfifo_wrptr)) { + + miiphy_read (devname, phyAddr, 0x1, &phyStatus); + printf ("\nphyStatus: 0x%04x\n", phyStatus); + printf ("ecntrl: 0x%08x\n", fec->eth->ecntrl); + printf ("ievent: 0x%08x\n", fec->eth->ievent); + printf ("x_status: 0x%08x\n", fec->eth->x_status); + printf ("rfifo: status 0x%08x\n", fec->eth->rfifo_status); + + printf (" control 0x%08x\n", fec->eth->rfifo_cntrl); + printf (" lrfp 0x%08x\n", fec->eth->rfifo_lrf_ptr); + printf (" lwfp 0x%08x\n", fec->eth->rfifo_lwf_ptr); + printf (" alarm 0x%08x\n", fec->eth->rfifo_alarm); + printf (" readptr 0x%08x\n", fec->eth->rfifo_rdptr); + printf (" writptr 0x%08x\n", fec->eth->rfifo_wrptr); + } +} +#endif /* DEBUG */ + +/********************************************************************/ + +static int mpc8220_fec_send (struct eth_device *dev, volatile void *eth_data, + int data_length) +{ + /* + * This routine transmits one frame. This routine only accepts + * 6-byte Ethernet addresses. + */ + mpc8220_fec_priv *fec = (mpc8220_fec_priv *) dev->priv; + FEC_TBD *pTbd; + +#ifdef DEBUG + printf ("tbd status: 0x%04x\n", fec->tbdBase[0].status); + tfifo_print (dev->name, fec); +#endif + + /* + * Clear Tx BD ring at first + */ + mpc8220_fec_tbd_scrub (fec); + + /* + * Check for valid length of data. + */ + if ((data_length > 1500) || (data_length <= 0)) { + return -1; + } + + /* + * Check the number of vacant TxBDs. + */ + if (fec->cleanTbdNum < 1) { +#ifdef DEBUG + printf ("No available TxBDs ...\n"); +#endif + return -1; + } + + /* + * Get the first TxBD to send the mac header + */ + pTbd = &fec->tbdBase[fec->tbdIndex]; + pTbd->dataLength = data_length; + pTbd->dataPointer = (u32) eth_data; + pTbd->status |= FEC_TBD_LAST | FEC_TBD_TC | FEC_TBD_READY; + fec->tbdIndex = (fec->tbdIndex + 1) % FEC_TBD_NUM; + +#ifdef DEBUG + printf ("DMA_TASK_ENABLE, fec->tbdIndex = %d \n", fec->tbdIndex); +#endif + + /* + * Kick the MII i/f + */ + if (fec->xcv_type != SEVENWIRE) { + u16 phyStatus; + + miiphy_read (dev->name, 0, 0x1, &phyStatus); + } + + /* + * Enable SmartDMA transmit task + */ + +#ifdef DEBUG + tfifo_print (dev->name, fec); +#endif + + DMA_TASK_ENABLE (FEC_XMIT_TASK_NO); + +#ifdef DEBUG + tfifo_print (dev->name, fec); +#endif + +#ifdef DEBUG + printf ("+"); +#endif + + fec->cleanTbdNum -= 1; + +#ifdef DEBUG + printf ("smartDMA ethernet Tx task enabled\n"); +#endif + /* + * wait until frame is sent . + */ + while (pTbd->status & FEC_TBD_READY) { + udelay (10); +#ifdef DEBUG + printf ("TDB status = %04x\n", pTbd->status); +#endif + } + + return 0; +} + + +/********************************************************************/ +static int mpc8220_fec_recv (struct eth_device *dev) +{ + /* + * This command pulls one frame from the card + */ + mpc8220_fec_priv *fec = (mpc8220_fec_priv *) dev->priv; + FEC_RBD *pRbd = &fec->rbdBase[fec->rbdIndex]; + unsigned long ievent; + int frame_length, len = 0; + NBUF *frame; + +#ifdef DEBUG + printf ("mpc8220_fec_recv %d Start...\n", fec->rbdIndex); + printf ("-"); +#endif + + /* + * Check if any critical events have happened + */ + ievent = fec->eth->ievent; + fec->eth->ievent = ievent; + if (ievent & 0x20060000) { + /* BABT, Rx/Tx FIFO errors */ + mpc8220_fec_halt (dev); + mpc8220_fec_init (dev, NULL); + return 0; + } + if (ievent & 0x80000000) { + /* Heartbeat error */ + fec->eth->x_cntrl |= 0x00000001; + } + if (ievent & 0x10000000) { + /* Graceful stop complete */ + if (fec->eth->x_cntrl & 0x00000001) { + mpc8220_fec_halt (dev); + fec->eth->x_cntrl &= ~0x00000001; + mpc8220_fec_init (dev, NULL); + } + } + + if (!(pRbd->status & FEC_RBD_EMPTY)) { + if ((pRbd->status & FEC_RBD_LAST) + && !(pRbd->status & FEC_RBD_ERR) + && ((pRbd->dataLength - 4) > 14)) { + + /* + * Get buffer address and size + */ + frame = (NBUF *) pRbd->dataPointer; + frame_length = pRbd->dataLength - 4; + +#if (0) + { + int i; + + printf ("recv data hdr:"); + for (i = 0; i < 14; i++) + printf ("%x ", *(frame->head + i)); + printf ("\n"); + } +#endif + /* + * Fill the buffer and pass it to upper layers + */ +/* memcpy(buff, frame->head, 14); + memcpy(buff + 14, frame->data, frame_length);*/ + NetReceive ((volatile uchar *) pRbd->dataPointer, + frame_length); + len = frame_length; + } + /* + * Reset buffer descriptor as empty + */ + mpc8220_fec_rbd_clean (fec, pRbd); + } + DMA_CLEAR_IEVENT (FEC_RECV_TASK_NO); + return len; +} + + +/********************************************************************/ +int mpc8220_fec_initialize (bd_t * bis) +{ + mpc8220_fec_priv *fec; + +#ifdef CONFIG_HAS_ETH1 + mpc8220_fec_priv *fec2; +#endif + struct eth_device *dev; + char *tmp, *end; + char env_enetaddr[6]; + +#ifdef CONFIG_HAS_ETH1 + char env_enet1addr[6]; +#endif + int i; + + fec = (mpc8220_fec_priv *) malloc (sizeof (*fec)); + dev = (struct eth_device *) malloc (sizeof (*dev)); + memset (dev, 0, sizeof *dev); + + fec->eth = (ethernet_regs *) MMAP_FEC1; +#ifdef CONFIG_HAS_ETH1 + fec2 = (mpc8220_fec_priv *) malloc (sizeof (*fec)); + fec2->eth = (ethernet_regs *) MMAP_FEC2; +#endif + fec->tbdBase = (FEC_TBD *) FEC_BD_BASE; + fec->rbdBase = + (FEC_RBD *) (FEC_BD_BASE + FEC_TBD_NUM * sizeof (FEC_TBD)); + fec->xcv_type = MII100; + + dev->priv = (void *) fec; + dev->iobase = MMAP_FEC1; + dev->init = mpc8220_fec_init; + dev->halt = mpc8220_fec_halt; + dev->send = mpc8220_fec_send; + dev->recv = mpc8220_fec_recv; + + sprintf (dev->name, "FEC ETHERNET"); + eth_register (dev); + +#if defined(CONFIG_MII) || defined(CONFIG_CMD_MII) + miiphy_register (dev->name, + fec8220_miiphy_read, fec8220_miiphy_write); +#endif + + /* + * Try to set the mac address now. The fec mac address is + * a garbage after reset. When not using fec for booting + * the Linux fec driver will try to work with this garbage. + */ + tmp = getenv ("ethaddr"); + if (tmp) { + for (i = 0; i < 6; i++) { + env_enetaddr[i] = + tmp ? simple_strtoul (tmp, &end, 16) : 0; + if (tmp) + tmp = (*end) ? end + 1 : end; + } + mpc8220_fec_set_hwaddr (fec, env_enetaddr); + } +#ifdef CONFIG_HAS_ETH1 + tmp = getenv ("eth1addr"); + if (tmp) { + for (i = 0; i < 6; i++) { + env_enet1addr[i] = + tmp ? simple_strtoul (tmp, &end, 16) : 0; + if (tmp) + tmp = (*end) ? end + 1 : end; + } + mpc8220_fec_set_hwaddr (fec2, env_enet1addr); + } +#endif + + return 1; +} + +/* MII-interface related functions */ +/********************************************************************/ +int fec8220_miiphy_read (char *devname, u8 phyAddr, u8 regAddr, u16 * retVal) +{ + ethernet_regs *eth = (ethernet_regs *) MMAP_FEC1; + u32 reg; /* convenient holder for the PHY register */ + u32 phy; /* convenient holder for the PHY */ + int timeout = 0xffff; + + /* + * reading from any PHY's register is done by properly + * programming the FEC's MII data register. + */ + reg = regAddr << FEC_MII_DATA_RA_SHIFT; + phy = phyAddr << FEC_MII_DATA_PA_SHIFT; + + eth->mii_data = + (FEC_MII_DATA_ST | FEC_MII_DATA_OP_RD | FEC_MII_DATA_TA | phy + | reg); + + /* + * wait for the related interrupt + */ + while ((timeout--) && (!(eth->ievent & 0x00800000))); + + if (timeout == 0) { +#ifdef DEBUG + printf ("Read MDIO failed...\n"); +#endif + return -1; + } + + /* + * clear mii interrupt bit + */ + eth->ievent = 0x00800000; + + /* + * it's now safe to read the PHY's register + */ + *retVal = (u16) eth->mii_data; + + return 0; +} + +/********************************************************************/ +int fec8220_miiphy_write (char *devname, u8 phyAddr, u8 regAddr, u16 data) +{ + ethernet_regs *eth = (ethernet_regs *) MMAP_FEC1; + u32 reg; /* convenient holder for the PHY register */ + u32 phy; /* convenient holder for the PHY */ + int timeout = 0xffff; + + reg = regAddr << FEC_MII_DATA_RA_SHIFT; + phy = phyAddr << FEC_MII_DATA_PA_SHIFT; + + eth->mii_data = (FEC_MII_DATA_ST | FEC_MII_DATA_OP_WR | + FEC_MII_DATA_TA | phy | reg | data); + + /* + * wait for the MII interrupt + */ + while ((timeout--) && (!(eth->ievent & 0x00800000))); + + if (timeout == 0) { +#ifdef DEBUG + printf ("Write MDIO failed...\n"); +#endif + return -1; + } + + /* + * clear MII interrupt bit + */ + eth->ievent = 0x00800000; + + return 0; +} + +#ifdef DEBUG +static u32 local_crc32 (char *string, unsigned int crc_value, int len) +{ + int i; + char c; + unsigned int crc, count; + + /* + * crc32 algorithm + */ + /* + * crc = 0xffffffff; * The initialized value should be 0xffffffff + */ + crc = crc_value; + + for (i = len; --i >= 0;) { + c = *string++; + for (count = 0; count < 8; count++) { + if ((c & 0x01) ^ (crc & 0x01)) { + crc >>= 1; + crc = crc ^ 0xedb88320; + } else { + crc >>= 1; + } + c >>= 1; + } + } + + /* + * In big endian system, do byte swaping for crc value + */ + return crc; +} +#endif /* DEBUG */ + +#endif /* CONFIG_MPC8220_FEC */ diff --git a/arch/powerpc/cpu/mpc8220/fec.h b/arch/powerpc/cpu/mpc8220/fec.h new file mode 100644 index 0000000..a8927fc --- /dev/null +++ b/arch/powerpc/cpu/mpc8220/fec.h @@ -0,0 +1,283 @@ +/* + * (C) Copyright 2003-2004 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * This file is based on mpc4200fec.h + * (C) Copyright Motorola, Inc., 2000 + * + * odin ethernet header file + */ + +#ifndef __MPC8220_FEC_H +#define __MPC8220_FEC_H + +#include <common.h> +#include <mpc8220.h> +#include "dma.h" + +typedef struct ethernet_register_set { + +/* [10:2]addr = 00 */ + +/* Control and status Registers (offset 000-1FF) */ + + volatile u32 fec_id; /* MBAR_ETH + 0x000 */ + volatile u32 ievent; /* MBAR_ETH + 0x004 */ + volatile u32 imask; /* MBAR_ETH + 0x008 */ + + volatile u32 RES0[1]; /* MBAR_ETH + 0x00C */ + volatile u32 r_des_active; /* MBAR_ETH + 0x010 */ + volatile u32 x_des_active; /* MBAR_ETH + 0x014 */ + volatile u32 r_des_active_cl; /* MBAR_ETH + 0x018 */ + volatile u32 x_des_active_cl; /* MBAR_ETH + 0x01C */ + volatile u32 ivent_set; /* MBAR_ETH + 0x020 */ + volatile u32 ecntrl; /* MBAR_ETH + 0x024 */ + + volatile u32 RES1[6]; /* MBAR_ETH + 0x028-03C */ + volatile u32 mii_data; /* MBAR_ETH + 0x040 */ + volatile u32 mii_speed; /* MBAR_ETH + 0x044 */ + volatile u32 mii_status; /* MBAR_ETH + 0x048 */ + + volatile u32 RES2[5]; /* MBAR_ETH + 0x04C-05C */ + volatile u32 mib_data; /* MBAR_ETH + 0x060 */ + volatile u32 mib_control; /* MBAR_ETH + 0x064 */ + + volatile u32 RES3[6]; /* MBAR_ETH + 0x068-7C */ + volatile u32 r_activate; /* MBAR_ETH + 0x080 */ + volatile u32 r_cntrl; /* MBAR_ETH + 0x084 */ + volatile u32 r_hash; /* MBAR_ETH + 0x088 */ + volatile u32 r_data; /* MBAR_ETH + 0x08C */ + volatile u32 ar_done; /* MBAR_ETH + 0x090 */ + volatile u32 r_test; /* MBAR_ETH + 0x094 */ + volatile u32 r_mib; /* MBAR_ETH + 0x098 */ + volatile u32 r_da_low; /* MBAR_ETH + 0x09C */ + volatile u32 r_da_high; /* MBAR_ETH + 0x0A0 */ + + volatile u32 RES4[7]; /* MBAR_ETH + 0x0A4-0BC */ + volatile u32 x_activate; /* MBAR_ETH + 0x0C0 */ + volatile u32 x_cntrl; /* MBAR_ETH + 0x0C4 */ + volatile u32 backoff; /* MBAR_ETH + 0x0C8 */ + volatile u32 x_data; /* MBAR_ETH + 0x0CC */ + volatile u32 x_status; /* MBAR_ETH + 0x0D0 */ + volatile u32 x_mib; /* MBAR_ETH + 0x0D4 */ + volatile u32 x_test; /* MBAR_ETH + 0x0D8 */ + volatile u32 fdxfc_da1; /* MBAR_ETH + 0x0DC */ + volatile u32 fdxfc_da2; /* MBAR_ETH + 0x0E0 */ + volatile u32 paddr1; /* MBAR_ETH + 0x0E4 */ + volatile u32 paddr2; /* MBAR_ETH + 0x0E8 */ + volatile u32 op_pause; /* MBAR_ETH + 0x0EC */ + + volatile u32 RES5[4]; /* MBAR_ETH + 0x0F0-0FC */ + volatile u32 instr_reg; /* MBAR_ETH + 0x100 */ + volatile u32 context_reg; /* MBAR_ETH + 0x104 */ + volatile u32 test_cntrl; /* MBAR_ETH + 0x108 */ + volatile u32 acc_reg; /* MBAR_ETH + 0x10C */ + volatile u32 ones; /* MBAR_ETH + 0x110 */ + volatile u32 zeros; /* MBAR_ETH + 0x114 */ + volatile u32 iaddr1; /* MBAR_ETH + 0x118 */ + volatile u32 iaddr2; /* MBAR_ETH + 0x11C */ + volatile u32 gaddr1; /* MBAR_ETH + 0x120 */ + volatile u32 gaddr2; /* MBAR_ETH + 0x124 */ + volatile u32 random; /* MBAR_ETH + 0x128 */ + volatile u32 rand1; /* MBAR_ETH + 0x12C */ + volatile u32 tmp; /* MBAR_ETH + 0x130 */ + + volatile u32 RES6[3]; /* MBAR_ETH + 0x134-13C */ + volatile u32 fifo_id; /* MBAR_ETH + 0x140 */ + volatile u32 x_wmrk; /* MBAR_ETH + 0x144 */ + volatile u32 fcntrl; /* MBAR_ETH + 0x148 */ + volatile u32 r_bound; /* MBAR_ETH + 0x14C */ + volatile u32 r_fstart; /* MBAR_ETH + 0x150 */ + volatile u32 r_count; /* MBAR_ETH + 0x154 */ + volatile u32 r_lag; /* MBAR_ETH + 0x158 */ + volatile u32 r_read; /* MBAR_ETH + 0x15C */ + volatile u32 r_write; /* MBAR_ETH + 0x160 */ + volatile u32 x_count; /* MBAR_ETH + 0x164 */ + volatile u32 x_lag; /* MBAR_ETH + 0x168 */ + volatile u32 x_retry; /* MBAR_ETH + 0x16C */ + volatile u32 x_write; /* MBAR_ETH + 0x170 */ + volatile u32 x_read; /* MBAR_ETH + 0x174 */ + + volatile u32 RES7[2]; /* MBAR_ETH + 0x178-17C */ + volatile u32 fm_cntrl; /* MBAR_ETH + 0x180 */ + volatile u32 rfifo_data; /* MBAR_ETH + 0x184 */ + volatile u32 rfifo_status; /* MBAR_ETH + 0x188 */ + volatile u32 rfifo_cntrl; /* MBAR_ETH + 0x18C */ + volatile u32 rfifo_lrf_ptr; /* MBAR_ETH + 0x190 */ + volatile u32 rfifo_lwf_ptr; /* MBAR_ETH + 0x194 */ + volatile u32 rfifo_alarm; /* MBAR_ETH + 0x198 */ + volatile u32 rfifo_rdptr; /* MBAR_ETH + 0x19C */ + volatile u32 rfifo_wrptr; /* MBAR_ETH + 0x1A0 */ + volatile u32 tfifo_data; /* MBAR_ETH + 0x1A4 */ + volatile u32 tfifo_status; /* MBAR_ETH + 0x1A8 */ + volatile u32 tfifo_cntrl; /* MBAR_ETH + 0x1AC */ + volatile u32 tfifo_lrf_ptr; /* MBAR_ETH + 0x1B0 */ + volatile u32 tfifo_lwf_ptr; /* MBAR_ETH + 0x1B4 */ + volatile u32 tfifo_alarm; /* MBAR_ETH + 0x1B8 */ + volatile u32 tfifo_rdptr; /* MBAR_ETH + 0x1BC */ + volatile u32 tfifo_wrptr; /* MBAR_ETH + 0x1C0 */ + + volatile u32 reset_cntrl; /* MBAR_ETH + 0x1C4 */ + volatile u32 xmit_fsm; /* MBAR_ETH + 0x1C8 */ + + volatile u32 RES8[3]; /* MBAR_ETH + 0x1CC-1D4 */ + volatile u32 rdes_data0; /* MBAR_ETH + 0x1D8 */ + volatile u32 rdes_data1; /* MBAR_ETH + 0x1DC */ + volatile u32 r_length; /* MBAR_ETH + 0x1E0 */ + volatile u32 x_length; /* MBAR_ETH + 0x1E4 */ + volatile u32 x_addr; /* MBAR_ETH + 0x1E8 */ + volatile u32 cdes_data; /* MBAR_ETH + 0x1EC */ + volatile u32 status; /* MBAR_ETH + 0x1F0 */ + volatile u32 dma_control; /* MBAR_ETH + 0x1F4 */ + volatile u32 des_cmnd; /* MBAR_ETH + 0x1F8 */ + volatile u32 data; /* MBAR_ETH + 0x1FC */ + + /* MIB COUNTERS (Offset 200-2FF) */ + + volatile u32 rmon_t_drop; /* MBAR_ETH + 0x200 */ + volatile u32 rmon_t_packets; /* MBAR_ETH + 0x204 */ + volatile u32 rmon_t_bc_pkt; /* MBAR_ETH + 0x208 */ + volatile u32 rmon_t_mc_pkt; /* MBAR_ETH + 0x20C */ + volatile u32 rmon_t_crc_align; /* MBAR_ETH + 0x210 */ + volatile u32 rmon_t_undersize; /* MBAR_ETH + 0x214 */ + volatile u32 rmon_t_oversize; /* MBAR_ETH + 0x218 */ + volatile u32 rmon_t_frag; /* MBAR_ETH + 0x21C */ + volatile u32 rmon_t_jab; /* MBAR_ETH + 0x220 */ + volatile u32 rmon_t_col; /* MBAR_ETH + 0x224 */ + volatile u32 rmon_t_p64; /* MBAR_ETH + 0x228 */ + volatile u32 rmon_t_p65to127; /* MBAR_ETH + 0x22C */ + volatile u32 rmon_t_p128to255; /* MBAR_ETH + 0x230 */ + volatile u32 rmon_t_p256to511; /* MBAR_ETH + 0x234 */ + volatile u32 rmon_t_p512to1023; /* MBAR_ETH + 0x238 */ + volatile u32 rmon_t_p1024to2047;/* MBAR_ETH + 0x23C */ + volatile u32 rmon_t_p_gte2048; /* MBAR_ETH + 0x240 */ + volatile u32 rmon_t_octets; /* MBAR_ETH + 0x244 */ + volatile u32 ieee_t_drop; /* MBAR_ETH + 0x248 */ + volatile u32 ieee_t_frame_ok; /* MBAR_ETH + 0x24C */ + volatile u32 ieee_t_1col; /* MBAR_ETH + 0x250 */ + volatile u32 ieee_t_mcol; /* MBAR_ETH + 0x254 */ + volatile u32 ieee_t_def; /* MBAR_ETH + 0x258 */ + volatile u32 ieee_t_lcol; /* MBAR_ETH + 0x25C */ + volatile u32 ieee_t_excol; /* MBAR_ETH + 0x260 */ + volatile u32 ieee_t_macerr; /* MBAR_ETH + 0x264 */ + volatile u32 ieee_t_cserr; /* MBAR_ETH + 0x268 */ + volatile u32 ieee_t_sqe; /* MBAR_ETH + 0x26C */ + volatile u32 t_fdxfc; /* MBAR_ETH + 0x270 */ + volatile u32 ieee_t_octets_ok; /* MBAR_ETH + 0x274 */ + + volatile u32 RES9[2]; /* MBAR_ETH + 0x278-27C */ + volatile u32 rmon_r_drop; /* MBAR_ETH + 0x280 */ + volatile u32 rmon_r_packets; /* MBAR_ETH + 0x284 */ + volatile u32 rmon_r_bc_pkt; /* MBAR_ETH + 0x288 */ + volatile u32 rmon_r_mc_pkt; /* MBAR_ETH + 0x28C */ + volatile u32 rmon_r_crc_align; /* MBAR_ETH + 0x290 */ + volatile u32 rmon_r_undersize; /* MBAR_ETH + 0x294 */ + volatile u32 rmon_r_oversize; /* MBAR_ETH + 0x298 */ + volatile u32 rmon_r_frag; /* MBAR_ETH + 0x29C */ + volatile u32 rmon_r_jab; /* MBAR_ETH + 0x2A0 */ + + volatile u32 rmon_r_resvd_0; /* MBAR_ETH + 0x2A4 */ + + volatile u32 rmon_r_p64; /* MBAR_ETH + 0x2A8 */ + volatile u32 rmon_r_p65to127; /* MBAR_ETH + 0x2AC */ + volatile u32 rmon_r_p128to255; /* MBAR_ETH + 0x2B0 */ + volatile u32 rmon_r_p256to511; /* MBAR_ETH + 0x2B4 */ + volatile u32 rmon_r_p512to1023; /* MBAR_ETH + 0x2B8 */ + volatile u32 rmon_r_p1024to2047;/* MBAR_ETH + 0x2BC */ + volatile u32 rmon_r_p_gte2048; /* MBAR_ETH + 0x2C0 */ + volatile u32 rmon_r_octets; /* MBAR_ETH + 0x2C4 */ + volatile u32 ieee_r_drop; /* MBAR_ETH + 0x2C8 */ + volatile u32 ieee_r_frame_ok; /* MBAR_ETH + 0x2CC */ + volatile u32 ieee_r_crc; /* MBAR_ETH + 0x2D0 */ + volatile u32 ieee_r_align; /* MBAR_ETH + 0x2D4 */ + volatile u32 r_macerr; /* MBAR_ETH + 0x2D8 */ + volatile u32 r_fdxfc; /* MBAR_ETH + 0x2DC */ + volatile u32 ieee_r_octets_ok; /* MBAR_ETH + 0x2E0 */ + + volatile u32 RES10[6]; /* MBAR_ETH + 0x2E4-2FC */ + + volatile u32 RES11[64]; /* MBAR_ETH + 0x300-3FF */ +} ethernet_regs; + +/* Receive & Transmit Buffer Descriptor definitions */ +typedef struct BufferDescriptor { + u16 status; + u16 dataLength; + u32 dataPointer; +} FEC_RBD; + +typedef struct { + u16 status; + u16 dataLength; + u32 dataPointer; +} FEC_TBD; + +/* private structure */ +typedef enum { + SEVENWIRE, /* 7-wire */ + MII10, /* MII 10Mbps */ + MII100 /* MII 100Mbps */ +} xceiver_type; + +typedef struct { + ethernet_regs *eth; + xceiver_type xcv_type; /* transceiver type */ + FEC_RBD *rbdBase; /* RBD ring */ + FEC_TBD *tbdBase; /* TBD ring */ + u16 rbdIndex; /* next receive BD to read */ + u16 tbdIndex; /* next transmit BD to send */ + u16 usedTbdIndex; /* next transmit BD to clean */ + u16 cleanTbdNum; /* the number of available transmit BDs */ +} mpc8220_fec_priv; + +/* Ethernet parameter area */ +#define FEC_TBD_BASE (FEC_PARAM_BASE + 0x00) +#define FEC_TBD_NEXT (FEC_PARAM_BASE + 0x04) +#define FEC_RBD_BASE (FEC_PARAM_BASE + 0x08) +#define FEC_RBD_NEXT (FEC_PARAM_BASE + 0x0c) + +/* BD Numer definitions */ +#define FEC_TBD_NUM 48 /* The user can adjust this value */ +#define FEC_RBD_NUM 32 /* The user can adjust this value */ + +/* packet size limit */ +#define FEC_MAX_PKT_SIZE 1536 + +/* RBD bits definitions */ +#define FEC_RBD_EMPTY 0x8000 /* Buffer is empty */ +#define FEC_RBD_WRAP 0x2000 /* Last BD in ring */ +#define FEC_RBD_INT 0x1000 /* Interrupt */ +#define FEC_RBD_LAST 0x0800 /* Buffer is last in frame(useless) */ +#define FEC_RBD_MISS 0x0100 /* Miss bit for prom mode */ +#define FEC_RBD_BC 0x0080 /* The received frame is broadcast frame */ +#define FEC_RBD_MC 0x0040 /* The received frame is multicast frame */ +#define FEC_RBD_LG 0x0020 /* Frame length violation */ +#define FEC_RBD_NO 0x0010 /* Nonoctet align frame */ +#define FEC_RBD_SH 0x0008 /* Short frame */ +#define FEC_RBD_CR 0x0004 /* CRC error */ +#define FEC_RBD_OV 0x0002 /* Receive FIFO overrun */ +#define FEC_RBD_TR 0x0001 /* Frame is truncated */ +#define FEC_RBD_ERR (FEC_RBD_LG | FEC_RBD_NO | FEC_RBD_CR | \ + FEC_RBD_OV | FEC_RBD_TR) + +/* TBD bits definitions */ +#define FEC_TBD_READY 0x8000 /* Buffer is ready */ +#define FEC_TBD_WRAP 0x2000 /* Last BD in ring */ +#define FEC_TBD_INT 0x1000 /* Interrupt */ +#define FEC_TBD_LAST 0x0800 /* Buffer is last in frame */ +#define FEC_TBD_TC 0x0400 /* Transmit the CRC */ +#define FEC_TBD_ABC 0x0200 /* Append bad CRC */ + +/* MII-related definitios */ +#define FEC_MII_DATA_ST 0x40000000 /* Start of frame delimiter */ +#define FEC_MII_DATA_OP_RD 0x20000000 /* Perform a read operation */ +#define FEC_MII_DATA_OP_WR 0x10000000 /* Perform a write operation */ +#define FEC_MII_DATA_PA_MSK 0x0f800000 /* PHY Address field mask */ +#define FEC_MII_DATA_RA_MSK 0x007c0000 /* PHY Register field mask */ +#define FEC_MII_DATA_TA 0x00020000 /* Turnaround */ +#define FEC_MII_DATA_DATAMSK 0x0000ffff /* PHY data field */ + +#define FEC_MII_DATA_RA_SHIFT 18 /* MII Register address bits */ +#define FEC_MII_DATA_PA_SHIFT 23 /* MII PHY address bits */ + +#endif /* __MPC8220_FEC_H */ diff --git a/arch/powerpc/cpu/mpc8220/fec_dma_tasks.S b/arch/powerpc/cpu/mpc8220/fec_dma_tasks.S new file mode 100644 index 0000000..3f8a03b --- /dev/null +++ b/arch/powerpc/cpu/mpc8220/fec_dma_tasks.S @@ -0,0 +1,363 @@ +/* + * Copyright (C) 2004, Freescale Semiconductor, Inc. + * + * This file contains microcode for the FEC controller of the MPC8220. + */ + +#include <config.h> + +#if defined(CONFIG_MPC8220) + +/* sas/sccg, gas target */ +.section smartdmaInitData,"aw",@progbits /* Initialized data for task variables */ +.section smartdmaTaskTable,"aw",@progbits /* Task tables */ +.align 9 +.globl taskTable +taskTable: +.globl scEthernetRecv_Entry +scEthernetRecv_Entry: /* Task 0 */ +.long scEthernetRecv_TDT - taskTable /* Task 0 Descriptor Table */ +.long scEthernetRecv_TDT - taskTable + 0x00000094 +.long scEthernetRecv_VarTab - taskTable /* Task 0 Variable Table */ +.long scEthernetRecv_FDT - taskTable + 0x03 /* Task 0 Function Descriptor Table & Flags */ +.long 0x00000000 +.long 0x00000000 +.long scEthernetRecv_CSave - taskTable /* Task 0 context save space */ +.long 0xf0000000 +.globl scEthernetXmit_Entry +scEthernetXmit_Entry: /* Task 1 */ +.long scEthernetXmit_TDT - taskTable /* Task 1 Descriptor Table */ +.long scEthernetXmit_TDT - taskTable + 0x000000e0 +.long scEthernetXmit_VarTab - taskTable /* Task 1 Variable Table */ +.long scEthernetXmit_FDT - taskTable + 0x03 /* Task 1 Function Descriptor Table & Flags */ +.long 0x00000000 +.long 0x00000000 +.long scEthernetXmit_CSave - taskTable /* Task 1 context save space */ +.long 0xf0000000 + + +.globl scEthernetRecv_TDT +scEthernetRecv_TDT: /* Task 0 Descriptor Table */ +.long 0xc4c50000 /* 0000(153): LCDEXT: idx0 = var9 + var10; idx0 once var0; idx0 += inc0 */ +.long 0x84c5e000 /* 0004(153): LCD: idx1 = var9 + var11; ; idx1 += inc0 */ +.long 0x10001f08 /* 0008(156): DRD1A: var7 = idx1; FN=0 MORE init=0 WS=0 RS=0 */ +.long 0x10000380 /* 000C(157): DRD1A: var0 = *idx0; FN=0 MORE init=0 WS=0 RS=0 */ +.long 0x00000f88 /* 0010(158): DRD1A: var3 = *idx1; FN=0 init=0 WS=0 RS=0 */ +.long 0x81980000 /* 0014(162): LCD: idx0 = var3; idx0 once var0; idx0 += inc0 */ +.long 0x10000780 /* 0018(164): DRD1A: var1 = *idx0; FN=0 MORE init=0 WS=0 RS=0 */ +.long 0x60000000 /* 001C(165): DRD2A: EU0=0 EU1=0 EU2=0 EU3=0 EXT init=0 WS=0 RS=0 */ +.long 0x010cf04c /* 0020(165): DRD2B1: var4 = EU3(); EU3(var1,var12) */ +.long 0x82180349 /* 0024(169): LCD: idx0 = var4; idx0 != var13; idx0 += inc1 */ +.long 0x81c68004 /* 0028(172): LCD: idx1 = var3 + var13 + 4; idx1 once var0; idx1 += inc0 */ +.long 0x70000000 /* 002C(174): DRD2A: EU0=0 EU1=0 EU2=0 EU3=0 EXT MORE init=0 WS=0 RS=0 */ +.long 0x018cf04e /* 0030(174): DRD2B1: var6 = EU3(); EU3(var1,var14) */ +.long 0x70000000 /* 0034(175): DRD2A: EU0=0 EU1=0 EU2=0 EU3=0 EXT MORE init=0 WS=0 RS=0 */ +.long 0x020cf04f /* 0038(175): DRD2B1: var8 = EU3(); EU3(var1,var15) */ +.long 0x00000b88 /* 003C(176): DRD1A: var2 = *idx1; FN=0 init=0 WS=0 RS=0 */ +.long 0x80025184 /* 0040(205): LCDEXT: idx1 = 0xf0009184; ; */ +.long 0x86810412 /* 0044(205): LCD: idx2 = var13, idx3 = var2; idx2 < var16; idx2 += inc2, idx3 += inc2 */ +.long 0x0200cf88 /* 0048(209): DRD1A: *idx3 = *idx1; FN=0 init=16 WS=0 RS=0 */ +.long 0x80025184 /* 004C(217): LCDEXT: idx1 = 0xf0009184; ; */ +.long 0x8681845b /* 0050(217): LCD: idx2 = var13, idx3 = var3; idx2 < var17; idx2 += inc3, idx3 += inc3 */ +.long 0x0000cf88 /* 0054(221): DRD1A: *idx3 = *idx1; FN=0 init=0 WS=0 RS=0 */ +.long 0xc31883a4 /* 0058(225): LCDEXT: idx1 = var6; idx1 == var14; idx1 += inc4 */ +.long 0x80190000 /* 005C(225): LCD: idx2 = var0; idx2 once var0; idx2 += inc0 */ +.long 0x04008468 /* 0060(227): DRD1A: idx1 = var13; FN=0 INT init=0 WS=0 RS=0 */ +.long 0xc4038360 /* 0064(232): LCDEXT: idx1 = var8, idx2 = var7; idx1 == var13; idx1 += inc4, idx2 += inc0 */ +.long 0x81c50000 /* 0068(233): LCD: idx3 = var3 + var10; idx3 once var0; idx3 += inc0 */ +.long 0x1000cb18 /* 006C(235): DRD1A: *idx2 = idx3; FN=0 MORE init=0 WS=0 RS=0 */ +.long 0x00000f18 /* 0070(236): DRD1A: var3 = idx3; FN=0 init=0 WS=0 RS=0 */ +.long 0xc418836d /* 0074(238): LCDEXT: idx1 = var8; idx1 > var13; idx1 += inc5 */ +.long 0x83990000 /* 0078(238): LCD: idx2 = var7; idx2 once var0; idx2 += inc0 */ +.long 0x10000c00 /* 007C(240): DRD1A: var3 = var0; FN=0 MORE init=0 WS=0 RS=0 */ +.long 0x0000c800 /* 0080(241): DRD1A: *idx2 = var0; FN=0 init=0 WS=0 RS=0 */ +.long 0x81988000 /* 0084(245): LCD: idx1 = var3; idx1 once var0; idx1 += inc0 */ +.long 0x10000788 /* 0088(247): DRD1A: var1 = *idx1; FN=0 MORE init=0 WS=0 RS=0 */ +.long 0x60000000 /* 008C(248): DRD2A: EU0=0 EU1=0 EU2=0 EU3=0 EXT init=0 WS=0 RS=0 */ +.long 0x080cf04c /* 0090(248): DRD2B1: idx0 = EU3(); EU3(var1,var12) */ +.long 0x000001f8 /* 0094(:0): NOP */ + + +.globl scEthernetXmit_TDT +scEthernetXmit_TDT: /* Task 1 Descriptor Table */ +.long 0x80095b00 /* 0000(280): LCDEXT: idx0 = 0xf0025b00; ; */ +.long 0x85c60004 /* 0004(280): LCD: idx1 = var11 + var12 + 4; idx1 once var0; idx1 += inc0 */ +.long 0x10002308 /* 0008(283): DRD1A: var8 = idx1; FN=0 MORE init=0 WS=0 RS=0 */ +.long 0x10000f88 /* 000C(284): DRD1A: var3 = *idx1; FN=0 MORE init=0 WS=0 RS=0 */ +.long 0x00000380 /* 0010(285): DRD1A: var0 = *idx0; FN=0 init=0 WS=0 RS=0 */ +.long 0x81980000 /* 0014(288): LCD: idx0 = var3; idx0 once var0; idx0 += inc0 */ +.long 0x10000780 /* 0018(290): DRD1A: var1 = *idx0; FN=0 MORE init=0 WS=0 RS=0 */ +.long 0x60000000 /* 001C(291): DRD2A: EU0=0 EU1=0 EU2=0 EU3=0 EXT init=0 WS=0 RS=0 */ +.long 0x024cf04d /* 0020(291): DRD2B1: var9 = EU3(); EU3(var1,var13) */ +.long 0x84980309 /* 0024(294): LCD: idx0 = var9; idx0 != var12; idx0 += inc1 */ +.long 0xc0004003 /* 0028(297): LCDEXT: idx1 = 0x00000003; ; */ +.long 0x81c60004 /* 002C(297): LCD: idx2 = var3 + var12 + 4; idx2 once var0; idx2 += inc0 */ +.long 0x70000000 /* 0030(299): DRD2A: EU0=0 EU1=0 EU2=0 EU3=0 EXT MORE init=0 WS=0 RS=0 */ +.long 0x010cf04e /* 0034(299): DRD2B1: var4 = EU3(); EU3(var1,var14) */ +.long 0x70000000 /* 0038(300): DRD2A: EU0=0 EU1=0 EU2=0 EU3=0 EXT MORE init=0 WS=0 RS=0 */ +.long 0x014cf04f /* 003C(300): DRD2B1: var5 = EU3(); EU3(var1,var15) */ +.long 0x70000000 /* 0040(301): DRD2A: EU0=0 EU1=0 EU2=0 EU3=0 EXT MORE init=0 WS=0 RS=0 */ +.long 0x028cf050 /* 0044(301): DRD2B1: var10 = EU3(); EU3(var1,var16) */ +.long 0x70000000 /* 0048(302): DRD2A: EU0=0 EU1=0 EU2=0 EU3=0 EXT MORE init=0 WS=0 RS=0 */ +.long 0x018cf051 /* 004C(302): DRD2B1: var6 = EU3(); EU3(var1,var17) */ +.long 0x10000b90 /* 0050(303): DRD1A: var2 = *idx2; FN=0 MORE init=0 WS=0 RS=0 */ +.long 0x60000000 /* 0054(304): DRD2A: EU0=0 EU1=0 EU2=0 EU3=0 EXT init=0 WS=0 RS=0 */ +.long 0x01ccf0a1 /* 0058(304): DRD2B1: var7 = EU3(); EU3(var2,idx1) */ +.long 0xc2988312 /* 005C(308): LCDEXT: idx1 = var5; idx1 > var12; idx1 += inc2 */ +.long 0x83490000 /* 0060(308): LCD: idx2 = var6 + var18; idx2 once var0; idx2 += inc0 */ +.long 0x00001b10 /* 0064(310): DRD1A: var6 = idx2; FN=0 init=0 WS=0 RS=0 */ +.long 0x800251a4 /* 0068(315): LCDEXT: idx1 = 0xf00091a4; ; */ +.long 0xc30104dc /* 006C(315): LCDEXT: idx2 = var6, idx3 = var2; idx2 >= var19; idx2 += inc3, idx3 += inc4 */ +.long 0x839a032d /* 0070(316): LCD: idx4 = var7; idx4 == var12; idx4 += inc5 */ +.long 0x0220c798 /* 0074(321): DRD1A: *idx1 = *idx3; FN=0 init=17 WS=0 RS=0 */ +.long 0x800251a4 /* 0078(329): LCDEXT: idx1 = 0xf00091a4; ; */ +.long 0x99198337 /* 007C(329): LCD: idx2 = idx2, idx3 = idx3; idx2 > var12; idx2 += inc6, idx3 += inc7 */ +.long 0x022ac798 /* 0080(333): DRD1A: *idx1 = *idx3; FN=0 init=17 WS=1 RS=1 */ +.long 0x800251a4 /* 0084(350): LCDEXT: idx1 = 0xf00091a4; ; */ +.long 0xc1430000 /* 0088(350): LCDEXT: idx2 = var2 + var6; idx2 once var0; idx2 += inc0 */ +.long 0x82998312 /* 008C(351): LCD: idx3 = var5; idx3 > var12; idx3 += inc2 */ +.long 0x0a2ac790 /* 0090(354): DRD1A: *idx1 = *idx2; FN=0 TFD init=17 WS=1 RS=1 */ +.long 0x81988000 /* 0094(359): LCD: idx1 = var3; idx1 once var0; idx1 += inc0 */ +.long 0x60000002 /* 0098(361): DRD2A: EU0=0 EU1=0 EU2=0 EU3=2 EXT init=0 WS=0 RS=0 */ +.long 0x0c4cfc4d /* 009C(361): DRD2B1: *idx1 = EU3(); EU3(*idx1,var13) */ +.long 0xc21883ad /* 00A0(365): LCDEXT: idx1 = var4; idx1 == var14; idx1 += inc5 */ +.long 0x80190000 /* 00A4(365): LCD: idx2 = var0; idx2 once var0; idx2 += inc0 */ +.long 0x04008460 /* 00A8(367): DRD1A: idx1 = var12; FN=0 INT init=0 WS=0 RS=0 */ +.long 0xc4052305 /* 00AC(371): LCDEXT: idx1 = var8, idx2 = var10; idx2 == var12; idx1 += inc0, idx2 += inc5 */ +.long 0x81ca0000 /* 00B0(372): LCD: idx3 = var3 + var20; idx3 once var0; idx3 += inc0 */ +.long 0x1000c718 /* 00B4(374): DRD1A: *idx1 = idx3; FN=0 MORE init=0 WS=0 RS=0 */ +.long 0x00000f18 /* 00B8(375): DRD1A: var3 = idx3; FN=0 init=0 WS=0 RS=0 */ +.long 0xc4188000 /* 00BC(378): LCDEXT: idx1 = var8; idx1 once var0; idx1 += inc0 */ +.long 0x85190312 /* 00C0(378): LCD: idx2 = var10; idx2 > var12; idx2 += inc2 */ +.long 0x10000c00 /* 00C4(380): DRD1A: var3 = var0; FN=0 MORE init=0 WS=0 RS=0 */ +.long 0x1000c400 /* 00C8(381): DRD1A: *idx1 = var0; FN=0 MORE init=0 WS=0 RS=0 */ +.long 0x00008860 /* 00CC(382): DRD1A: idx2 = var12; FN=0 init=0 WS=0 RS=0 */ +.long 0x81988000 /* 00D0(386): LCD: idx1 = var3; idx1 once var0; idx1 += inc0 */ +.long 0x10000788 /* 00D4(388): DRD1A: var1 = *idx1; FN=0 MORE init=0 WS=0 RS=0 */ +.long 0x60000000 /* 00D8(389): DRD2A: EU0=0 EU1=0 EU2=0 EU3=0 EXT init=0 WS=0 RS=0 */ +.long 0x080cf04d /* 00DC(389): DRD2B1: idx0 = EU3(); EU3(var1,var13) */ +.long 0x000001f8 /* 00E0(:0): NOP */ + +.align 8 + +.globl scEthernetRecv_VarTab +scEthernetRecv_VarTab: /* Task 0 Variable Table */ +.long 0x00000000 /* var[0] */ +.long 0x00000000 /* var[1] */ +.long 0x00000000 /* var[2] */ +.long 0x00000000 /* var[3] */ +.long 0x00000000 /* var[4] */ +.long 0x00000000 /* var[5] */ +.long 0x00000000 /* var[6] */ +.long 0x00000000 /* var[7] */ +.long 0x00000000 /* var[8] */ +.long 0xf0025b00 /* var[9] */ +.long 0x00000008 /* var[10] */ +.long 0x0000000c /* var[11] */ +.long 0x80000000 /* var[12] */ +.long 0x00000000 /* var[13] */ +.long 0x10000000 /* var[14] */ +.long 0x20000000 /* var[15] */ +.long 0x00000800 /* var[16] */ +.long 0x00000001 /* var[17] */ +.long 0x00000000 /* var[18] */ +.long 0x00000000 /* var[19] */ +.long 0x00000000 /* var[20] */ +.long 0x00000000 /* var[21] */ +.long 0x00000000 /* var[22] */ +.long 0x00000000 /* var[23] */ +.long 0x00000000 /* inc[0] */ +.long 0x60000000 /* inc[1] */ +.long 0x20000004 /* inc[2] */ +.long 0x20000001 /* inc[3] */ +.long 0x80000000 /* inc[4] */ +.long 0x40000000 /* inc[5] */ +.long 0x00000000 /* inc[6] */ +.long 0x00000000 /* inc[7] */ + +.align 8 + +.globl scEthernetXmit_VarTab +scEthernetXmit_VarTab: /* Task 1 Variable Table */ +.long 0x00000000 /* var[0] */ +.long 0x00000000 /* var[1] */ +.long 0x00000000 /* var[2] */ +.long 0x00000000 /* var[3] */ +.long 0x00000000 /* var[4] */ +.long 0x00000000 /* var[5] */ +.long 0x00000000 /* var[6] */ +.long 0x00000000 /* var[7] */ +.long 0x00000000 /* var[8] */ +.long 0x00000000 /* var[9] */ +.long 0x00000000 /* var[10] */ +.long 0xf0025b00 /* var[11] */ +.long 0x00000000 /* var[12] */ +.long 0x80000000 /* var[13] */ +.long 0x10000000 /* var[14] */ +.long 0x08000000 /* var[15] */ +.long 0x20000000 /* var[16] */ +.long 0x0000ffff /* var[17] */ +.long 0xffffffff /* var[18] */ +.long 0x00000004 /* var[19] */ +.long 0x00000008 /* var[20] */ +.long 0x00000000 /* var[21] */ +.long 0x00000000 /* var[22] */ +.long 0x00000000 /* var[23] */ +.long 0x00000000 /* inc[0] */ +.long 0x60000000 /* inc[1] */ +.long 0x40000000 /* inc[2] */ +.long 0xc000fffc /* inc[3] */ +.long 0xe0000004 /* inc[4] */ +.long 0x80000000 /* inc[5] */ +.long 0x4000ffff /* inc[6] */ +.long 0xe0000001 /* inc[7] */ + +.align 8 + +.globl scEthernetRecv_FDT +scEthernetRecv_FDT: /* Task 0 Function Descriptor Table */ +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x21800000 /* and(), EU# 3 */ +.long 0x21e00000 /* or(), EU# 3 */ +.long 0x21400000 /* andn(), EU# 3 */ +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 + +.align 8 + +.globl scEthernetXmit_FDT +scEthernetXmit_FDT: /* Task 1 Function Descriptor Table */ +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x21800000 /* and(), EU# 3 */ +.long 0x21e00000 /* or(), EU# 3 */ +.long 0x21400000 /* andn(), EU# 3 */ +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 + + +.globl scEthernetRecv_CSave +scEthernetRecv_CSave: /* Task 0 context save space */ +.space 128, 0x0 + + +.globl scEthernetXmit_CSave +scEthernetXmit_CSave: /* Task 1 context save space */ +.space 128, 0x0 + +#endif diff --git a/arch/powerpc/cpu/mpc8220/i2c.c b/arch/powerpc/cpu/mpc8220/i2c.c new file mode 100644 index 0000000..76ecdf1 --- /dev/null +++ b/arch/powerpc/cpu/mpc8220/i2c.c @@ -0,0 +1,390 @@ +/* + * (C) Copyright 2004, Freescale, Inc + * TsiChung Liew, Tsi-Chung.Liew@freescale.com. + * + * 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> + +DECLARE_GLOBAL_DATA_PTR; + +#ifdef CONFIG_HARD_I2C + +#include <mpc8220.h> +#include <i2c.h> + +typedef struct mpc8220_i2c { + volatile u32 adr; /* I2Cn + 0x00 */ + volatile u32 fdr; /* I2Cn + 0x04 */ + volatile u32 cr; /* I2Cn + 0x08 */ + volatile u32 sr; /* I2Cn + 0x0C */ + volatile u32 dr; /* I2Cn + 0x10 */ +} i2c_t; + +/* I2Cn control register bits */ +#define I2C_EN 0x80 +#define I2C_IEN 0x40 +#define I2C_STA 0x20 +#define I2C_TX 0x10 +#define I2C_TXAK 0x08 +#define I2C_RSTA 0x04 +#define I2C_INIT_MASK (I2C_EN | I2C_STA | I2C_TX | I2C_RSTA) + +/* I2Cn status register bits */ +#define I2C_CF 0x80 +#define I2C_AAS 0x40 +#define I2C_BB 0x20 +#define I2C_AL 0x10 +#define I2C_SRW 0x04 +#define I2C_IF 0x02 +#define I2C_RXAK 0x01 + +#define I2C_TIMEOUT 100 +#define I2C_RETRIES 1 + +struct mpc8220_i2c_tap { + int scl2tap; + int tap2tap; +}; + +static int mpc_reg_in (volatile u32 * reg); +static void mpc_reg_out (volatile u32 * reg, int val, int mask); +static int wait_for_bb (void); +static int wait_for_pin (int *status); +static int do_address (uchar chip, char rdwr_flag); +static int send_bytes (uchar chip, char *buf, int len); +static int receive_bytes (uchar chip, char *buf, int len); +static int mpc_get_fdr (int); + +static int mpc_reg_in (volatile u32 * reg) +{ + int ret; + ret = *reg >> 24; + __asm__ __volatile__ ("eieio"); + return ret; +} + +static void mpc_reg_out (volatile u32 * reg, int val, int mask) +{ + int tmp; + + if (!mask) { + *reg = val << 24; + } else { + tmp = mpc_reg_in (reg); + *reg = ((tmp & ~mask) | (val & mask)) << 24; + } + __asm__ __volatile__ ("eieio"); + + return; +} + +static int wait_for_bb (void) +{ + i2c_t *regs = (i2c_t *) MMAP_I2C; + int timeout = I2C_TIMEOUT; + int status; + + status = mpc_reg_in (®s->sr); + + while (timeout-- && (status & I2C_BB)) { +#if 1 + volatile int temp; + + mpc_reg_out (®s->cr, I2C_STA, I2C_STA); + temp = mpc_reg_in (®s->dr); + mpc_reg_out (®s->cr, 0, I2C_STA); + mpc_reg_out (®s->cr, 0, 0); + mpc_reg_out (®s->cr, I2C_EN, 0); +#endif + udelay (1000); + status = mpc_reg_in (®s->sr); + } + + return (status & I2C_BB); +} + +static int wait_for_pin (int *status) +{ + i2c_t *regs = (i2c_t *) MMAP_I2C; + int timeout = I2C_TIMEOUT; + + *status = mpc_reg_in (®s->sr); + + while (timeout-- && !(*status & I2C_IF)) { + udelay (1000); + *status = mpc_reg_in (®s->sr); + } + + if (!(*status & I2C_IF)) { + return -1; + } + + mpc_reg_out (®s->sr, 0, I2C_IF); + return 0; +} + +static int do_address (uchar chip, char rdwr_flag) +{ + i2c_t *regs = (i2c_t *) MMAP_I2C; + int status; + + chip <<= 1; + + if (rdwr_flag) + chip |= 1; + + mpc_reg_out (®s->cr, I2C_TX, I2C_TX); + mpc_reg_out (®s->dr, chip, 0); + + if (wait_for_pin (&status)) + return -2; + if (status & I2C_RXAK) + return -3; + return 0; +} + +static int send_bytes (uchar chip, char *buf, int len) +{ + i2c_t *regs = (i2c_t *) MMAP_I2C; + int wrcount; + int status; + + for (wrcount = 0; wrcount < len; ++wrcount) { + + mpc_reg_out (®s->dr, buf[wrcount], 0); + + if (wait_for_pin (&status)) + break; + + if (status & I2C_RXAK) + break; + + } + + return !(wrcount == len); + return 0; +} + +static int receive_bytes (uchar chip, char *buf, int len) +{ + i2c_t *regs = (i2c_t *) MMAP_I2C; + int dummy = 1; + int rdcount = 0; + int status; + int i; + + mpc_reg_out (®s->cr, 0, I2C_TX); + + for (i = 0; i < len; ++i) { + buf[rdcount] = mpc_reg_in (®s->dr); + + if (dummy) + dummy = 0; + else + rdcount++; + + if (wait_for_pin (&status)) + return -4; + } + + mpc_reg_out (®s->cr, I2C_TXAK, I2C_TXAK); + buf[rdcount++] = mpc_reg_in (®s->dr); + + if (wait_for_pin (&status)) + return -5; + + mpc_reg_out (®s->cr, 0, I2C_TXAK); + return 0; +} + +/**************** I2C API ****************/ + +void i2c_init (int speed, int saddr) +{ + i2c_t *regs = (i2c_t *) MMAP_I2C; + + mpc_reg_out (®s->cr, 0, 0); + mpc_reg_out (®s->adr, saddr << 1, 0); + + /* Set clock + */ + mpc_reg_out (®s->fdr, mpc_get_fdr (speed), 0); + + /* Enable module + */ + mpc_reg_out (®s->cr, I2C_EN, I2C_INIT_MASK); + mpc_reg_out (®s->sr, 0, I2C_IF); + return; +} + +static int mpc_get_fdr (int speed) +{ + static int fdr = -1; + + if (fdr == -1) { + ulong best_speed = 0; + ulong divider; + ulong ipb, scl; + ulong bestmatch = 0xffffffffUL; + int best_i = 0, best_j = 0, i, j; + int SCL_Tap[] = { 9, 10, 12, 15, 5, 6, 7, 8 }; + struct mpc8220_i2c_tap scltap[] = { + {4, 1}, + {4, 2}, + {6, 4}, + {6, 8}, + {14, 16}, + {30, 32}, + {62, 64}, + {126, 128} + }; + + ipb = gd->bus_clk; + for (i = 7; i >= 0; i--) { + for (j = 7; j >= 0; j--) { + scl = 2 * (scltap[j].scl2tap + + (SCL_Tap[i] - + 1) * scltap[j].tap2tap + 2); + if (ipb <= speed * scl) { + if ((speed * scl - ipb) < bestmatch) { + bestmatch = speed * scl - ipb; + best_i = i; + best_j = j; + best_speed = ipb / scl; + } + } + } + } + divider = (best_i & 3) | ((best_i & 4) << 3) | (best_j << 2); + if (gd->flags & GD_FLG_RELOC) { + fdr = divider; + } else { + printf ("%ld kHz, ", best_speed / 1000); + return divider; + } + } + + return fdr; +} + +int i2c_probe (uchar chip) +{ + i2c_t *regs = (i2c_t *) MMAP_I2C; + int i; + + for (i = 0; i < I2C_RETRIES; i++) { + mpc_reg_out (®s->cr, I2C_STA, I2C_STA); + + if (!do_address (chip, 0)) { + mpc_reg_out (®s->cr, 0, I2C_STA); + break; + } + + mpc_reg_out (®s->cr, 0, I2C_STA); + udelay (50); + } + + return (i == I2C_RETRIES); +} + +int i2c_read (uchar chip, uint addr, int alen, uchar * buf, int len) +{ + uchar xaddr[4]; + i2c_t *regs = (i2c_t *) MMAP_I2C; + int ret = -1; + + xaddr[0] = (addr >> 24) & 0xFF; + xaddr[1] = (addr >> 16) & 0xFF; + xaddr[2] = (addr >> 8) & 0xFF; + xaddr[3] = addr & 0xFF; + + if (wait_for_bb ()) { + printf ("i2c_read: bus is busy\n"); + goto Done; + } + + mpc_reg_out (®s->cr, I2C_STA, I2C_STA); + if (do_address (chip, 0)) { + printf ("i2c_read: failed to address chip\n"); + goto Done; + } + + if (send_bytes (chip, (char *)&xaddr[4 - alen], alen)) { + printf ("i2c_read: send_bytes failed\n"); + goto Done; + } + + mpc_reg_out (®s->cr, I2C_RSTA, I2C_RSTA); + if (do_address (chip, 1)) { + printf ("i2c_read: failed to address chip\n"); + goto Done; + } + + if (receive_bytes (chip, (char *)buf, len)) { + printf ("i2c_read: receive_bytes failed\n"); + goto Done; + } + + ret = 0; + Done: + mpc_reg_out (®s->cr, 0, I2C_STA); + return ret; +} + +int i2c_write (uchar chip, uint addr, int alen, uchar * buf, int len) +{ + uchar xaddr[4]; + i2c_t *regs = (i2c_t *) MMAP_I2C; + int ret = -1; + + xaddr[0] = (addr >> 24) & 0xFF; + xaddr[1] = (addr >> 16) & 0xFF; + xaddr[2] = (addr >> 8) & 0xFF; + xaddr[3] = addr & 0xFF; + + if (wait_for_bb ()) { + printf ("i2c_write: bus is busy\n"); + goto Done; + } + + mpc_reg_out (®s->cr, I2C_STA, I2C_STA); + if (do_address (chip, 0)) { + printf ("i2c_write: failed to address chip\n"); + goto Done; + } + + if (send_bytes (chip, (char *)&xaddr[4 - alen], alen)) { + printf ("i2c_write: send_bytes failed\n"); + goto Done; + } + + if (send_bytes (chip, (char *)buf, len)) { + printf ("i2c_write: send_bytes failed\n"); + goto Done; + } + + ret = 0; + Done: + mpc_reg_out (®s->cr, 0, I2C_STA); + return ret; +} + +#endif /* CONFIG_HARD_I2C */ diff --git a/arch/powerpc/cpu/mpc8220/i2cCore.c b/arch/powerpc/cpu/mpc8220/i2cCore.c new file mode 100644 index 0000000..b89ad03 --- /dev/null +++ b/arch/powerpc/cpu/mpc8220/i2cCore.c @@ -0,0 +1,627 @@ +/* I2cCore.c - MPC8220 PPC I2C Library */ + +/* Copyright 2004 Freescale Semiconductor, Inc. */ + +/* +modification history +-------------------- +01c,29jun04,tcl 1.3 removed CR. Added two bytes offset support. +01b,19jan04,tcl 1.2 removed i2cMsDelay and sysDecGet. renamed i2cMsDelay + back to sysMsDelay +01a,19jan04,tcl 1.1 created and seperated from i2c.c +*/ + +/* +DESCRIPTION +This file contain I2C low level handling library functions +*/ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <vxWorks.h> +#include <sysLib.h> +#include <iosLib.h> +#include <logLib.h> +#include <tickLib.h> + +/* BSP Includes */ +#include "config.h" +#include "mpc8220.h" +#include "i2cCore.h" + +#ifdef DEBUG_I2CCORE +int I2CCDbg = 0; +#endif + +#define ABS(x) ((x < 0)? -x : x) + +char *I2CERR[16] = { + "Transfer in Progress\n", /* 0 */ + "Transfer complete\n", + "Not Addressed\n", /* 2 */ + "Addressed as a slave\n", + "Bus is Idle\n", /* 4 */ + "Bus is busy\n", + "Arbitration Lost\n", /* 6 */ + "Arbitration on Track\n", + "Slave receive, master writing to slave\n", /* 8 */ + "Slave transmit, master reading from slave\n", + "Interrupt is pending\n", /* 10 */ + "Interrupt complete\n", + "Acknowledge received\n", /* 12 */ + "No acknowledge received\n", + "Unknown status\n", /* 14 */ + "\n" +}; + +/****************************************************************************** + * + * chk_status - Check I2C status bit + * + * RETURNS: OK, or ERROR if the bit encounter + * + */ + +STATUS chk_status (PSI2C pi2c, UINT8 sta_bit, UINT8 truefalse) +{ + int i, status = 0; + + for (i = 0; i < I2C_POLL_COUNT; i++) { + if ((pi2c->sr & sta_bit) == (truefalse ? sta_bit : 0)) + return (OK); + } + + I2CCDBG (L2, ("--- sr %x stabit %x truefalse %d\n", + pi2c->sr, sta_bit, truefalse, 0, 0, 0)); + + if (i == I2C_POLL_COUNT) { + switch (sta_bit) { + case I2C_STA_CF: + status = 0; + break; + case I2C_STA_AAS: + status = 2; + break; + case I2C_STA_BB: + status = 4; + break; + case I2C_STA_AL: + status = 6; + break; + case I2C_STA_SRW: + status = 8; + break; + case I2C_STA_IF: + status = 10; + break; + case I2C_STA_RXAK: + status = 12; + break; + default: + status = 14; + break; + } + + if (!truefalse) + status++; + + I2CCDBG (NO, ("--- status %d\n", status, 0, 0, 0, 0, 0)); + I2CCDBG (NO, (I2CERR[status], 0, 0, 0, 0, 0, 0)); + } + + return (ERROR); +} + +/****************************************************************************** + * + * I2C Enable - Enable the I2C Controller + * + */ +STATUS i2c_enable (SI2C * pi2c, PI2CSET pi2cSet) +{ + int fdr = pi2cSet->bit_rate; + UINT8 adr = pi2cSet->i2c_adr; + + I2CCDBG (L2, ("i2c_enable fdr %d adr %x\n", fdr, adr, 0, 0, 0, 0)); + + i2c_clear (pi2c); /* Clear FDR, ADR, SR and CR reg */ + + SetI2cFDR (pi2c, fdr); /* Frequency */ + pi2c->adr = adr; + + pi2c->cr = I2C_CTL_EN; /* Set Enable */ + + /* + The I2C bus should be in Idle state. If the bus is busy, + clear the STA bit in control register + */ + if (chk_status (pi2c, I2C_STA_BB, 0) != OK) { + if ((pi2c->cr & I2C_CTL_STA) == I2C_CTL_STA) + pi2c->cr &= ~I2C_CTL_STA; + + /* Check again if it is still busy, return error if found */ + if (chk_status (pi2c, I2C_STA_BB, 1) == OK) + return ERROR; + } + + return (OK); +} + +/****************************************************************************** + * + * I2C Disable - Disable the I2C Controller + * + */ +STATUS i2c_disable (PSI2C pi2c) +{ + i2c_clear (pi2c); + + pi2c->cr &= I2C_CTL_EN; /* Disable I2c */ + + if ((pi2c->cr & I2C_CTL_STA) == I2C_CTL_STA) + pi2c->cr &= ~I2C_CTL_STA; + + if (chk_status (pi2c, I2C_STA_BB, 0) != OK) + return ERROR; + + return (OK); +} + +/****************************************************************************** + * + * I2C Clear - Clear the I2C Controller + * + */ +STATUS i2c_clear (PSI2C pi2c) +{ + pi2c->adr = 0; + pi2c->fdr = 0; + pi2c->cr = 0; + pi2c->sr = 0; + + return (OK); +} + + +STATUS i2c_start (PSI2C pi2c, PI2CSET pi2cSet) +{ +#ifdef TWOBYTES + UINT16 ByteOffset = pi2cSet->str_adr; +#else + UINT8 ByteOffset = pi2cSet->str_adr; +#endif +#if 1 + UINT8 tmp = 0; +#endif + UINT8 Addr = pi2cSet->slv_adr; + + pi2c->cr |= I2C_CTL_STA; /* Generate start signal */ + + if (chk_status (pi2c, I2C_STA_BB, 1) != OK) + return ERROR; + + /* Write slave address */ + if (i2c_writebyte (pi2c, &Addr) != OK) { + i2c_stop (pi2c); /* Disable I2c */ + return ERROR; + } +#ifdef TWOBYTES +# if 0 + /* Issue the offset to start */ + if (i2c_write2byte (pi2c, &ByteOffset) != OK) { + i2c_stop (pi2c); /* Disable I2c */ + return ERROR; + } +#endif + tmp = (ByteOffset >> 8) & 0xff; + if (i2c_writebyte (pi2c, &tmp) != OK) { + i2c_stop (pi2c); /* Disable I2c */ + return ERROR; + } + tmp = ByteOffset & 0xff; + if (i2c_writebyte (pi2c, &tmp) != OK) { + i2c_stop (pi2c); /* Disable I2c */ + return ERROR; + } +#else + if (i2c_writebyte (pi2c, &ByteOffset) != OK) { + i2c_stop (pi2c); /* Disable I2c */ + return ERROR; + } +#endif + + return (OK); +} + +STATUS i2c_stop (PSI2C pi2c) +{ + pi2c->cr &= ~I2C_CTL_STA; /* Generate stop signal */ + if (chk_status (pi2c, I2C_STA_BB, 0) != OK) + return ERROR; + + return (OK); +} + +/****************************************************************************** + * + * Read Len bytes to the location pointed to by *Data from the device + * with address Addr. + */ +int i2c_readblock (SI2C * pi2c, PI2CSET pi2cSet, UINT8 * Data) +{ + int i = 0; + UINT8 Tmp; + +/* UINT8 ByteOffset = pi2cSet->str_adr; not used? */ + UINT8 Addr = pi2cSet->slv_adr; + int Length = pi2cSet->xfer_size; + + I2CCDBG (L1, ("i2c_readblock addr %x data 0x%08x len %d offset %d\n", + Addr, (int) Data, Length, ByteOffset, 0, 0)); + + if (pi2c->sr & I2C_STA_AL) { /* Check if Arbitration lost */ + I2CCDBG (FN, ("Arbitration lost\n", 0, 0, 0, 0, 0, 0)); + pi2c->sr &= ~I2C_STA_AL; /* Clear Arbitration status bit */ + return ERROR; + } + + pi2c->cr |= I2C_CTL_TX; /* Enable the I2c for TX, Ack */ + + if (i2c_start (pi2c, pi2cSet) == ERROR) + return ERROR; + + pi2c->cr |= I2C_CTL_RSTA; /* Repeat Start */ + + Tmp = Addr | 1; + + if (i2c_writebyte (pi2c, &Tmp) != OK) { + i2c_stop (pi2c); /* Disable I2c */ + return ERROR; + } + + if (((pi2c->sr & 0x07) == 0x07) || (pi2c->sr & 0x01)) + return ERROR; + + pi2c->cr &= ~I2C_CTL_TX; /* Set receive mode */ + + if (((pi2c->sr & 0x07) == 0x07) || (pi2c->sr & 0x01)) + return ERROR; + + /* Dummy Read */ + if (i2c_readbyte (pi2c, &Tmp, &i) != OK) { + i2c_stop (pi2c); /* Disable I2c */ + return ERROR; + } + + i = 0; + while (Length) { + if (Length == 2) + pi2c->cr |= I2C_CTL_TXAK; + + if (Length == 1) + pi2c->cr &= ~I2C_CTL_STA; + + if (i2c_readbyte (pi2c, Data, &Length) != OK) { + return i2c_stop (pi2c); + } + i++; + Length--; + Data++; + } + + if (i2c_stop (pi2c) == ERROR) + return ERROR; + + return i; +} + +STATUS i2c_writeblock (SI2C * pi2c, PI2CSET pi2cSet, UINT8 * Data) +{ + int Length = pi2cSet->xfer_size; + +#ifdef TWOBYTES + UINT16 ByteOffset = pi2cSet->str_adr; +#else + UINT8 ByteOffset = pi2cSet->str_adr; +#endif + int j, k; + + I2CCDBG (L2, ("i2c_writeblock\n", 0, 0, 0, 0, 0, 0)); + + if (pi2c->sr & I2C_STA_AL) { + /* Check if arbitration lost */ + I2CCDBG (L2, ("Arbitration lost\n", 0, 0, 0, 0, 0, 0)); + pi2c->sr &= ~I2C_STA_AL; /* Clear the condition */ + return ERROR; + } + + pi2c->cr |= I2C_CTL_TX; /* Enable the I2c for TX, Ack */ + + /* Do the not even offset first */ + if ((ByteOffset % 8) != 0) { + int remain; + + if (Length > 8) { + remain = 8 - (ByteOffset % 8); + Length -= remain; + + pi2cSet->str_adr = ByteOffset; + + if (i2c_start (pi2c, pi2cSet) == ERROR) + return ERROR; + + for (j = ByteOffset; j < remain; j++) { + if (i2c_writebyte (pi2c, Data++) != OK) + return ERROR; + } + + if (i2c_stop (pi2c) == ERROR) + return ERROR; + + sysMsDelay (32); + + /* Update the new ByteOffset */ + ByteOffset += remain; + } + } + + for (j = ByteOffset, k = 0; j < (Length + ByteOffset); j++) { + if ((j % 8) == 0) { + pi2cSet->str_adr = j; + if (i2c_start (pi2c, pi2cSet) == ERROR) + return ERROR; + } + + k++; + + if (i2c_writebyte (pi2c, Data++) != OK) + return ERROR; + + if ((j == (Length - 1)) || ((k % 8) == 0)) { + if (i2c_stop (pi2c) == ERROR) + return ERROR; + + sysMsDelay (50); + } + + } + + return k; +} + +STATUS i2c_readbyte (SI2C * pi2c, UINT8 * readb, int *index) +{ + pi2c->sr &= ~I2C_STA_IF; /* Clear Interrupt Bit */ + *readb = pi2c->dr; /* Read a byte */ + + /* + Set I2C_CTRL_TXAK will cause Transfer pending and + set I2C_CTRL_STA will cause Interrupt pending + */ + if (*index != 2) { + if (chk_status (pi2c, I2C_STA_CF, 1) != OK) /* Transfer not complete? */ + return ERROR; + } + + if (*index != 1) { + if (chk_status (pi2c, I2C_STA_IF, 1) != OK) + return ERROR; + } + + return (OK); +} + + +STATUS i2c_writebyte (SI2C * pi2c, UINT8 * writeb) +{ + pi2c->sr &= ~I2C_STA_IF; /* Clear Interrupt */ + pi2c->dr = *writeb; /* Write a byte */ + + if (chk_status (pi2c, I2C_STA_CF, 1) != OK) /* Transfer not complete? */ + return ERROR; + + if (chk_status (pi2c, I2C_STA_IF, 1) != OK) + return ERROR; + + return OK; +} + +STATUS i2c_write2byte (SI2C * pi2c, UINT16 * writeb) +{ + UINT8 data; + + data = (UINT8) ((*writeb >> 8) & 0xff); + if (i2c_writebyte (pi2c, &data) != OK) + return ERROR; + data = (UINT8) (*writeb & 0xff); + if (i2c_writebyte (pi2c, &data) != OK) + return ERROR; + return OK; +} + +/* FDR table base on 33MHz - more detail please refer to Odini2c_dividers.xls +FDR FDR scl sda scl2tap2 +510 432 tap tap tap tap scl_per sda_hold I2C Freq 0 1 2 3 4 5 +000 000 9 3 4 1 28 Clocks 9 Clocks 1190 KHz 0 0 0 0 0 0 +000 001 9 3 4 2 44 Clocks 11 Clocks 758 KHz 0 0 1 0 0 0 +000 010 9 3 6 4 80 Clocks 17 Clocks 417 KHz 0 0 0 1 0 0 +000 011 9 3 6 8 144 Clocks 25 Clocks 231 KHz 0 0 1 1 0 0 +000 100 9 3 14 16 288 Clocks 49 Clocks 116 KHz 0 0 0 0 1 0 +000 101 9 3 30 32 576 Clocks 97 Clocks 58 KHz 0 0 1 0 1 0 +000 110 9 3 62 64 1152 Clocks 193 Clocks 29 KHz 0 0 0 1 1 0 +000 111 9 3 126 128 2304 Clocks 385 Clocks 14 KHz 0 0 1 1 1 0 +001 000 10 3 4 1 30 Clocks 9 Clocks 1111 KHz1 0 0 0 0 0 +001 001 10 3 4 2 48 Clocks 11 Clocks 694 KHz 1 0 1 0 0 0 +001 010 10 3 6 4 88 Clocks 17 Clocks 379 KHz 1 0 0 1 0 0 +001 011 10 3 6 8 160 Clocks 25 Clocks 208 KHz 1 0 1 1 0 0 +001 100 10 3 14 16 320 Clocks 49 Clocks 104 KHz 1 0 0 0 1 0 +001 101 10 3 30 32 640 Clocks 97 Clocks 52 KHz 1 0 1 0 1 0 +001 110 10 3 62 64 1280 Clocks 193 Clocks 26 KHz 1 0 0 1 1 0 +001 111 10 3 126 128 2560 Clocks 385 Clocks 13 KHz 1 0 1 1 1 0 +010 000 12 4 4 1 34 Clocks 10 Clocks 980 KHz 0 1 0 0 0 0 +010 001 12 4 4 2 56 Clocks 13 Clocks 595 KHz 0 1 1 0 0 0 +010 010 12 4 6 4 104 Clocks 21 Clocks 321 KHz 0 1 0 1 0 0 +010 011 12 4 6 8 192 Clocks 33 Clocks 174 KHz 0 1 1 1 0 0 +010 100 12 4 14 16 384 Clocks 65 Clocks 87 KHz 0 1 0 0 1 0 +010 101 12 4 30 32 768 Clocks 129 Clocks 43 KHz 0 1 1 0 1 0 +010 110 12 4 62 64 1536 Clocks 257 Clocks 22 KHz 0 1 0 1 1 0 +010 111 12 4 126 128 3072 Clocks 513 Clocks 11 KHz 0 1 1 1 1 0 +011 000 15 4 4 1 40 Clocks 10 Clocks 833 KHz 1 1 0 0 0 0 +011 001 15 4 4 2 68 Clocks 13 Clocks 490 KHz 1 1 1 0 0 0 +011 010 15 4 6 4 128 Clocks 21 Clocks 260 KHz 1 1 0 1 0 0 +011 011 15 4 6 8 240 Clocks 33 Clocks 139 KHz 1 1 1 1 0 0 +011 100 15 4 14 16 480 Clocks 65 Clocks 69 KHz 1 1 0 0 1 0 +011 101 15 4 30 32 960 Clocks 129 Clocks 35 KHz 1 1 1 0 1 0 +011 110 15 4 62 64 1920 Clocks 257 Clocks 17 KHz 1 1 0 1 1 0 +011 111 15 4 126 128 3840 Clocks 513 Clocks 9 KHz 1 1 1 1 1 0 +100 000 5 1 4 1 20 Clocks 7 Clocks 1667 KHz 0 0 0 0 0 1 +100 001 5 1 4 2 28 Clocks 7 Clocks 1190 KHz 0 0 1 0 0 1 +100 010 5 1 6 4 48 Clocks 9 Clocks 694 KHz 0 0 0 1 0 1 +100 011 5 1 6 8 80 Clocks 9 Clocks 417 KHz 0 0 1 1 0 1 +100 100 5 1 14 16 160 Clocks 17 Clocks 208 KHz 0 0 0 0 1 1 +100 101 5 1 30 32 320 Clocks 33 Clocks 104 KHz 0 0 1 0 1 1 +100 110 5 1 62 64 640 Clocks 65 Clocks 52 KHz 0 0 0 1 1 1 +100 111 5 1 126 128 1280 Clocks 129 Clocks 26 KHz 0 0 1 1 1 1 +101 000 6 1 4 1 22 Clocks 7 Clocks 1515 KHz 1 0 0 0 0 1 +101 001 6 1 4 2 32 Clocks 7 Clocks 1042 KHz 1 0 1 0 0 1 +101 010 6 1 6 4 56 Clocks 9 Clocks 595 KHz 1 0 0 1 0 1 +101 011 6 1 6 8 96 Clocks 9 Clocks 347 KHz 1 0 1 1 0 1 +101 100 6 1 14 16 192 Clocks 17 Clocks 174 KHz 1 0 0 0 1 1 +101 101 6 1 30 32 384 Clocks 33 Clocks 87 KHz 1 0 1 0 1 1 +101 110 6 1 62 64 768 Clocks 65 Clocks 43 KHz 1 0 0 1 1 1 +101 111 6 1 126 128 1536 Clocks 129 Clocks 22 KHz 1 0 1 1 1 1 +110 000 7 2 4 1 24 Clocks 8 Clocks 1389 KHz 0 1 0 0 0 1 +110 001 7 2 4 2 36 Clocks 9 Clocks 926 KHz 0 1 1 0 0 1 +110 010 7 2 6 4 64 Clocks 13 Clocks 521 KHz 0 1 0 1 0 1 +110 011 7 2 6 8 112 Clocks 17 Clocks 298 KHz 0 1 1 1 0 1 +110 100 7 2 14 16 224 Clocks 33 Clocks 149 KHz 0 1 0 0 1 1 +110 101 7 2 30 32 448 Clocks 65 Clocks 74 KHz 0 1 1 0 1 1 +110 110 7 2 62 64 896 Clocks 129 Clocks 37 KHz 0 1 0 1 1 1 +110 111 7 2 126 128 1792 Clocks 257 Clocks 19 KHz 0 1 1 1 1 1 +111 000 8 2 4 1 26 Clocks 8 Clocks 1282 KHz 1 1 0 0 0 1 +111 001 8 2 4 2 40 Clocks 9 Clocks 833 KHz 1 1 1 0 0 1 +111 010 8 2 6 4 72 Clocks 13 Clocks 463 KHz 1 1 0 1 0 1 +111 011 8 2 6 8 128 Clocks 17 Clocks 260 KHz 1 1 1 1 0 1 +111 100 8 2 14 16 256 Clocks 33 Clocks 130 KHz 1 1 0 0 1 1 +111 101 8 2 30 32 512 Clocks 65 Clocks 65 KHz 1 1 1 0 1 1 +111 110 8 2 62 64 1024 Clocks 129 Clocks 33 KHz 1 1 0 1 1 1 +111 111 8 2 126 128 2048 Clocks 257 Clocks 16 KHz 1 1 1 1 1 1 +*/ +STATUS SetI2cFDR (PSI2C pi2cRegs, int bitrate) +{ +/* Constants */ + const UINT8 div_hold[8][3] = { {9, 3}, {10, 3}, + {12, 4}, {15, 4}, + {5, 1}, {6, 1}, + {7, 2}, {8, 2} + }; + + const UINT8 scl_tap[8][2] = { {4, 1}, {4, 2}, + {6, 4}, {6, 8}, + {14, 16}, {30, 32}, + {62, 64}, {126, 128} + }; + + UINT8 mfdr_bits; + + int i = 0; + int j = 0; + + int Diff, min; + int WhichFreq, iRec, jRec; + int SCL_Period; + int SCL_Hold; + int I2C_Freq; + + I2CCDBG (L2, ("Entering getBitRate: bitrate %d pi2cRegs 0x%08x\n", + bitrate, (int) pi2cRegs, 0, 0, 0, 0)); + + if (bitrate < 0) { + I2CCDBG (NO, ("Invalid bitrate\n", 0, 0, 0, 0, 0, 0)); + return ERROR; + } + + /* Initialize */ + mfdr_bits = 0; + min = 0x7fffffff; + WhichFreq = iRec = jRec = 0; + + for (i = 0; i < 8; i++) { + for (j = 0; j < 8; j++) { + /* SCL Period = 2 * (scl2tap + [(SCL_Tap - 1) * tap2tap] + 2) + * SCL Hold = scl2tap + ((SDA_Tap - 1) * tap2tap) + 3 + * Bit Rate (I2C Freq) = System Freq / SCL Period + */ + SCL_Period = + 2 * (scl_tap[i][0] + + ((div_hold[j][0] - 1) * scl_tap[i][1]) + + 2); + + /* Now get the I2C Freq */ + I2C_Freq = DEV_CLOCK_FREQ / SCL_Period; + + /* Take equal or slower */ + if (I2C_Freq > bitrate) + continue; + + /* Take the differences */ + Diff = I2C_Freq - bitrate; + + Diff = ABS (Diff); + + /* Find the closer value */ + if (Diff < min) { + min = Diff; + WhichFreq = I2C_Freq; + iRec = i; + jRec = j; + } + + I2CCDBG (L2, + ("--- (%d,%d) I2C_Freq %d minDiff %d min %d\n", + i, j, I2C_Freq, Diff, min, 0)); + } + } + + SCL_Period = + 2 * (scl_tap[iRec][0] + + ((div_hold[jRec][0] - 1) * scl_tap[iRec][1]) + 2); + + I2CCDBG (L2, ("\nmin %d WhichFreq %d iRec %d jRec %d\n", + min, WhichFreq, iRec, jRec, 0, 0)); + I2CCDBG (L2, ("--- scl2tap %d SCL_Tap %d tap2tap %d\n", + scl_tap[iRec][0], div_hold[jRec][0], scl_tap[iRec][1], + 0, 0, 0)); + + /* This may no require */ + SCL_Hold = + scl_tap[iRec][0] + + ((div_hold[jRec][1] - 1) * scl_tap[iRec][1]) + 3; + I2CCDBG (L2, + ("--- SCL_Period %d SCL_Hold %d\n", SCL_Period, SCL_Hold, 0, + 0, 0, 0)); + + I2CCDBG (L2, ("--- mfdr_bits %x\n", mfdr_bits, 0, 0, 0, 0, 0)); + + /* FDR 4,3,2 */ + if ((iRec & 1) == 1) + mfdr_bits |= 0x04; /* FDR 2 */ + if ((iRec & 2) == 2) + mfdr_bits |= 0x08; /* FDR 3 */ + if ((iRec & 4) == 4) + mfdr_bits |= 0x10; /* FDR 4 */ + /* FDR 5,1,0 */ + if ((jRec & 1) == 1) + mfdr_bits |= 0x01; /* FDR 0 */ + if ((jRec & 2) == 2) + mfdr_bits |= 0x02; /* FDR 1 */ + if ((jRec & 4) == 4) + mfdr_bits |= 0x20; /* FDR 5 */ + + I2CCDBG (L2, ("--- mfdr_bits %x\n", mfdr_bits, 0, 0, 0, 0, 0)); + + pi2cRegs->fdr = mfdr_bits; + + return OK; +} diff --git a/arch/powerpc/cpu/mpc8220/i2cCore.h b/arch/powerpc/cpu/mpc8220/i2cCore.h new file mode 100644 index 0000000..72783fd --- /dev/null +++ b/arch/powerpc/cpu/mpc8220/i2cCore.h @@ -0,0 +1,103 @@ +/* + * i2cCore.h + * + * Prototypes, etc. for the Motorola MPC8220 + * embedded cpu chips + * + * 2004 (c) Freescale, Inc. + * Author: TsiChung Liew <Tsi-Chung.Liew@freescale.com> + * + * 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 + */ +#ifndef __INCi2ccoreh +#define __INCi2ccoreh +#ifndef __ASSEMBLY__ +/* device types */ +#define I2C_DEVICE_TYPE_EEPROM 0 +#define I2C_EEPROM_ADRS 0xa0 +#define I2C_CTRL_ADRS I2C_EEPROM_ADRS +#define EEPROM_ADDR0 0xA2 /* on Dimm SPD eeprom */ +#define EEPROM_ADDR1 0xA4 /* on Board SPD eeprom */ +#define EEPROM_ADDR2 0xD2 /* non-standard eeprom - clock generator */ +/* Control Register */ +#define I2C_CTL_EN 0x80 /* I2C Enable */ +#define I2C_CTL_IEN 0x40 /* I2C Interrupt Enable */ +#define I2C_CTL_STA 0x20 /* Master/Slave Mode select */ +#define I2C_CTL_TX 0x10 /* Transmit/Receive Mode Select */ +#define I2C_CTL_TXAK 0x08 /* Transmit Acknowledge Enable */ +#define I2C_CTL_RSTA 0x04 /* Repeat Start */ +/* Status Register */ +#define I2C_STA_CF 0x80 /* Data Transfer */ +#define I2C_STA_AAS 0x40 /* Adressed As Slave */ +#define I2C_STA_BB 0x20 /* Bus Busy */ +#define I2C_STA_AL 0x10 /* Arbitration Lost */ +#define I2C_STA_SRW 0x04 /* Slave Read/Write */ +#define I2C_STA_IF 0x02 /* I2C Interrupt */ +#define I2C_STA_RXAK 0x01 /* Receive Acknowledge */ +/* Interrupt Contol Register */ +#define I2C_INT_BNBE2 0x80 /* Bus Not Busy Enable 2 */ +#define I2C_INT_TE2 0x40 /* Transmit Enable 2 */ +#define I2C_INT_RE2 0x20 /* Receive Enable 2 */ +#define I2C_INT_IE2 0x10 /* Interrupt Enable 2 */ +#define I2C_INT_BNBE1 0x08 /* Bus Not Busy Enable 1 */ +#define I2C_INT_TE1 0x04 /* Transmit Enable 1 */ +#define I2C_INT_RE1 0x02 /* Receive Enable 1 */ +#define I2C_INT_IE1 0x01 /* Interrupt Enable 1 */ +#define I2C_POLL_COUNT 0x100000 +#define I2C_ENABLE 0x00000001 +#define I2C_DISABLE 0x00000002 +#define I2C_START 0x00000004 +#define I2C_REPSTART 0x00000008 +#define I2C_STOP 0x00000010 +#define I2C_BITRATE 0x00000020 +#define I2C_SLAVEADR 0x00000040 +#define I2C_STARTADR 0x00000080 +#undef TWOBYTES +typedef struct i2c_settings { + /* Device settings */ + int bit_rate; /* Device bit rate */ + u8 i2c_adr; /* I2C address */ + u8 slv_adr; /* Slave address */ +#ifdef TWOBYTES + u16 str_adr; /* Start address */ +#else + u8 str_adr; /* Start address */ +#endif + int xfer_size; /* Transfer Size */ + + int bI2c_en; /* Enable or Disable */ + int cmdFlag; /* I2c Command Flags */ +} i2cset_t; + +/* +int check_status(PSI2C pi2c, u8 sta_bit, u8 truefalse); +int i2c_enable(PSI2C pi2c, PI2CSET pi2cSet); +int i2c_disable(PSI2C pi2c); +int i2c_start(PSI2C pi2c, PI2CSET pi2cSet); +int i2c_stop(PSI2C pi2c); +int i2c_clear(PSI2C pi2c); +int i2c_readblock (PSI2C pi2c, PI2CSET pi2cSet, u8 *Data); +int i2c_writeblock (PSI2C pi2c, PI2CSET pi2cSet, u8 *Data); +int i2c_readbyte(PSI2C pi2c, u8 *readb, int *index); +int i2c_writebyte(PSI2C pi2c, u8 *writeb); +int SetI2cFDR( PSI2C pi2cRegs, int bitrate ); +*/ +#endif /* __ASSEMBLY__ */ + +#endif /* __INCi2ccoreh */ diff --git a/arch/powerpc/cpu/mpc8220/interrupts.c b/arch/powerpc/cpu/mpc8220/interrupts.c new file mode 100644 index 0000000..78e9917 --- /dev/null +++ b/arch/powerpc/cpu/mpc8220/interrupts.c @@ -0,0 +1,80 @@ +/* + * (C) Copyright -2003 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * (C) Copyright 2001 + * Josh Huber <huber@mclx.com>, Mission Critical Linux, Inc. + * + * 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 + */ + +/* + * interrupts.c - just enough support for the decrementer/timer + */ + +#include <common.h> +#include <asm/processor.h> +#include <command.h> + +int interrupt_init_cpu (ulong * decrementer_count) +{ + *decrementer_count = get_tbclk () / CONFIG_SYS_HZ; + + return (0); +} + +/****************************************************************************/ + +/* + * Handle external interrupts + */ +void external_interrupt (struct pt_regs *regs) +{ + puts ("external_interrupt (oops!)\n"); +} + +void timer_interrupt_cpu (struct pt_regs *regs) +{ + /* nothing to do here */ + return; +} + +/****************************************************************************/ + +/* + * Install and free a interrupt handler. + */ + +void irq_install_handler (int vec, interrupt_handler_t * handler, void *arg) +{ + +} + +void irq_free_handler (int vec) +{ + +} + +/****************************************************************************/ + +void +do_irqinfo (cmd_tbl_t * cmdtp, bd_t * bd, int flag, int argc, char *argv[]) +{ + puts ("IRQ related functions are unimplemented currently.\n"); +} diff --git a/arch/powerpc/cpu/mpc8220/io.S b/arch/powerpc/cpu/mpc8220/io.S new file mode 100644 index 0000000..5ecdf55 --- /dev/null +++ b/arch/powerpc/cpu/mpc8220/io.S @@ -0,0 +1,128 @@ +/* + * Copyright (C) 1998 Dan Malek <dmalek@jlc.net> + * Copyright (C) 1999 Magnus Damm <kieraypc01.p.y.kie.era.ericsson.se> + * Copyright (C) 2001 Sysgo Real-Time Solutions, GmbH <www.elinos.com> + * Andreas Heppel <aheppel@sysgo.de> + * Copyright (C) 2003 Wolfgang Denk <wd@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 <config.h> +#include <ppc_asm.tmpl> + +/* ------------------------------------------------------------------------------- */ +/* Function: in8 */ +/* Description: Input 8 bits */ +/* ------------------------------------------------------------------------------- */ + .globl in8 +in8: + lbz r3,0(r3) + sync + blr + +/* ------------------------------------------------------------------------------- */ +/* Function: in16 */ +/* Description: Input 16 bits */ +/* ------------------------------------------------------------------------------- */ + .globl in16 +in16: + lhz r3,0(r3) + sync + blr + +/* ------------------------------------------------------------------------------- */ +/* Function: in16r */ +/* Description: Input 16 bits and byte reverse */ +/* ------------------------------------------------------------------------------- */ + .globl in16r +in16r: + lhbrx r3,0,r3 + sync + blr + +/* ------------------------------------------------------------------------------- */ +/* Function: in32 */ +/* Description: Input 32 bits */ +/* ------------------------------------------------------------------------------- */ + .globl in32 +in32: + lwz 3,0(3) + sync + blr + +/* ------------------------------------------------------------------------------- */ +/* Function: in32r */ +/* Description: Input 32 bits and byte reverse */ +/* ------------------------------------------------------------------------------- */ + .globl in32r +in32r: + lwbrx r3,0,r3 + sync + blr + +/* ------------------------------------------------------------------------------- */ +/* Function: out8 */ +/* Description: Output 8 bits */ +/* ------------------------------------------------------------------------------- */ + .globl out8 +out8: + stb r4,0(r3) + sync + blr + +/* ------------------------------------------------------------------------------- */ +/* Function: out16 */ +/* Description: Output 16 bits */ +/* ------------------------------------------------------------------------------- */ + .globl out16 +out16: + sth r4,0(r3) + sync + blr + +/* ------------------------------------------------------------------------------- */ +/* Function: out16r */ +/* Description: Byte reverse and output 16 bits */ +/* ------------------------------------------------------------------------------- */ + .globl out16r +out16r: + sthbrx r4,0,r3 + sync + blr + +/* ------------------------------------------------------------------------------- */ +/* Function: out32 */ +/* Description: Output 32 bits */ +/* ------------------------------------------------------------------------------- */ + .globl out32 +out32: + stw r4,0(r3) + sync + blr + +/* ------------------------------------------------------------------------------- */ +/* Function: out32r */ +/* Description: Byte reverse and output 32 bits */ +/* ------------------------------------------------------------------------------- */ + .globl out32r +out32r: + stwbrx r4,0,r3 + sync + blr diff --git a/arch/powerpc/cpu/mpc8220/loadtask.c b/arch/powerpc/cpu/mpc8220/loadtask.c new file mode 100644 index 0000000..6d8b627 --- /dev/null +++ b/arch/powerpc/cpu/mpc8220/loadtask.c @@ -0,0 +1,78 @@ +/* + * (C) Copyright 2003 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * This file is based on code + * (C) Copyright Motorola, Inc., 2000 + */ + +#include <common.h> +#include <mpc8220.h> + +/* Multichannel DMA microcode */ +extern int taskTable; + +void loadtask (int basetask, int tasks) +{ + int *sram = (int *) (MMAP_SRAM + 512); + int *task_org = &taskTable; + unsigned int start, offset, end; + int i; + +#ifdef DEBUG + printf ("basetask = %d, tasks = %d\n", basetask, tasks); + printf ("task_org = 0x%08x\n", (unsigned int) task_org); +#endif + + /* setup TaskBAR register */ + *(vu_long *) MMAP_DMA = (MMAP_SRAM + 512); + + /* relocate task table entries */ + offset = (unsigned int) sram; + for (i = basetask; i < basetask + tasks; i++) { + sram[i * 8 + 0] = task_org[i * 8 + 0] + offset; + sram[i * 8 + 1] = task_org[i * 8 + 1] + offset; + sram[i * 8 + 2] = task_org[i * 8 + 2] + offset; + sram[i * 8 + 3] = task_org[i * 8 + 3] + offset; + sram[i * 8 + 4] = task_org[i * 8 + 4]; + sram[i * 8 + 5] = task_org[i * 8 + 5]; + sram[i * 8 + 6] = task_org[i * 8 + 6] + offset; + sram[i * 8 + 7] = task_org[i * 8 + 7]; + } + + /* relocate task descriptors */ + start = (sram[basetask * 8] - (unsigned int) sram); + end = (sram[(basetask + tasks - 1) * 8 + 1] - (unsigned int) sram); + +#ifdef DEBUG + printf ("TDT start = 0x%08x, end = 0x%08x\n", start, end); +#endif + + start /= 4; + end /= 4; + for (i = start; i <= end; i++) { + sram[i] = task_org[i]; + } + + /* relocate variables */ + start = (sram[basetask * 8 + 2] - (unsigned int) sram); + end = (sram[(basetask + tasks - 1) * 8 + 2] + 256 - + (unsigned int) sram); + start /= 4; + end /= 4; + for (i = start; i < end; i++) { + sram[i] = task_org[i]; + } + + /* relocate function decriptors */ + start = ((sram[basetask * 8 + 3] & 0xfffffffc) - (unsigned int) sram); + end = ((sram[(basetask + tasks - 1) * 8 + 3] & 0xfffffffc) + 256 - + (unsigned int) sram); + start /= 4; + end /= 4; + for (i = start; i < end; i++) { + sram[i] = task_org[i]; + } + + asm volatile ("sync"); +} diff --git a/arch/powerpc/cpu/mpc8220/pci.c b/arch/powerpc/cpu/mpc8220/pci.c new file mode 100644 index 0000000..7ef43b7 --- /dev/null +++ b/arch/powerpc/cpu/mpc8220/pci.c @@ -0,0 +1,191 @@ +/* + * Copyright 2004 Freescale Semiconductor. + * Copyright (C) 2003 Motorola Inc. + * Xianghua Xiao (x.xiao@motorola.com) + * + * 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 + */ + +/* + * PCI Configuration space access support for MPC8220 PCI Bridge + */ +#include <common.h> +#include <mpc8220.h> +#include <pci.h> +#include <asm/io.h> + +#if defined(CONFIG_PCI) + +/* System RAM mapped over PCI */ +#define CONFIG_PCI_SYS_MEM_BUS CONFIG_SYS_SDRAM_BASE +#define CONFIG_PCI_SYS_MEM_PHYS CONFIG_SYS_SDRAM_BASE +#define CONFIG_PCI_SYS_MEM_SIZE (1024 * 1024 * 1024) + +#define cfg_read(val, addr, type, op) *val = op((type)(addr)); +#define cfg_write(val, addr, type, op) op((type *)(addr), (val)); + +#define PCI_OP(rw, size, type, op, mask) \ +int mpc8220_pci_##rw##_config_##size(struct pci_controller *hose, \ + pci_dev_t dev, int offset, type val) \ +{ \ + u32 addr = 0; \ + u16 cfg_type = 0; \ + addr = ((offset & 0xfc) | cfg_type | (dev) | 0x80000000); \ + out_be32(hose->cfg_addr, addr); \ + __asm__ __volatile__("sync"); \ + cfg_##rw(val, hose->cfg_data + (offset & mask), type, op); \ + out_be32(hose->cfg_addr, addr & 0x7fffffff); \ + __asm__ __volatile__("sync"); \ + return 0; \ +} + +PCI_OP(read, byte, u8 *, in_8, 3) +PCI_OP(read, word, u16 *, in_le16, 2) +PCI_OP(write, byte, u8, out_8, 3) +PCI_OP(write, word, u16, out_le16, 2) +PCI_OP(write, dword, u32, out_le32, 0) + +int mpc8220_pci_read_config_dword(struct pci_controller *hose, pci_dev_t dev, + int offset, u32 *val) +{ + u32 addr; + u32 tmpv; + u32 mask = 2; /* word access */ + /* Read lower 16 bits */ + addr = ((offset & 0xfc) | (dev) | 0x80000000); + out_be32(hose->cfg_addr, addr); + __asm__ __volatile__("sync"); + *val = (u32) in_le16((u16 *) (hose->cfg_data + (offset & mask))); + out_be32(hose->cfg_addr, addr & 0x7fffffff); + __asm__ __volatile__("sync"); + + /* Read upper 16 bits */ + offset += 2; + addr = ((offset & 0xfc) | 1 | (dev) | 0x80000000); + out_be32(hose->cfg_addr, addr); + __asm__ __volatile__("sync"); + tmpv = (u32) in_le16((u16 *) (hose->cfg_data + (offset & mask))); + out_be32(hose->cfg_addr, addr & 0x7fffffff); + __asm__ __volatile__("sync"); + + /* combine results into dword value */ + *val = (tmpv << 16) | *val; + + return 0; +} + +void +pci_mpc8220_init(struct pci_controller *hose) +{ + u32 win0, win1, win2; + volatile mpc8220_xcpci_t *xcpci = + (volatile mpc8220_xcpci_t *) MMAP_XCPCI; + + volatile pcfg8220_t *portcfg = (volatile pcfg8220_t *) MMAP_PCFG; + + win0 = (u32) CONFIG_PCI_MEM_PHYS; + win1 = (u32) CONFIG_PCI_IO_PHYS; + win2 = (u32) CONFIG_PCI_CFG_PHYS; + + /* Assert PCI reset */ + out_be32 (&xcpci->glb_stat_ctl, PCI_GLB_STAT_CTRL_PR); + + /* Disable prefetching but read-multiples will still prefetch */ + out_be32 (&xcpci->target_ctrl, 0x00000000); + + /* Initiator windows */ + out_be32 (&xcpci->init_win0, (win0 >> 16) | win0 | 0x003f0000); + out_be32 (&xcpci->init_win1, ((win1 >> 16) | win1 )); + out_be32 (&xcpci->init_win2, ((win2 >> 16) | win2 )); + + out_be32 (&xcpci->init_win_cfg, + PCI_INIT_WIN_CFG_WIN0_CTRL_EN | + PCI_INIT_WIN_CFG_WIN1_CTRL_EN | PCI_INIT_WIN_CFG_WIN1_CTRL_IO | + PCI_INIT_WIN_CFG_WIN2_CTRL_EN | PCI_INIT_WIN_CFG_WIN2_CTRL_IO); + + out_be32 (&xcpci->init_ctrl, 0x00000000); + + /* Enable bus master and mem access */ + out_be32 (&xcpci->stat_cmd_reg, PCI_STAT_CMD_B | PCI_STAT_CMD_M); + + /* Cache line size and master latency */ + out_be32 (&xcpci->bist_htyp_lat_cshl, (0xf8 << PCI_CFG1_LT_SHIFT)); + + out_be32 (&xcpci->base0, PCI_BASE_ADDR_REG0); /* 256MB - MBAR space */ + out_be32 (&xcpci->base1, PCI_BASE_ADDR_REG1); /* 1GB - SDRAM space */ + + out_be32 (&xcpci->target_bar0, + PCI_TARGET_BASE_ADDR_REG0 | PCI_TARGET_BASE_ADDR_EN); + out_be32 (&xcpci->target_bar1, + PCI_TARGET_BASE_ADDR_REG1 | PCI_TARGET_BASE_ADDR_EN); + + /* Deassert reset bit */ + out_be32 (&xcpci->glb_stat_ctl, 0x00000000); + + /* Enable PCI bus master support */ + /* Set PCIGNT1, PCIREQ1, PCIREQ0/PCIGNTIN, PCIGNT0/PCIREQOUT, + PCIREQ2, PCIGNT2 */ + out_be32((volatile u32 *)&portcfg->pcfg3, + (in_be32((volatile u32 *)&portcfg->pcfg3) & 0xFC3FCE7F)); + out_be32((volatile u32 *)&portcfg->pcfg3, + (in_be32((volatile u32 *)&portcfg->pcfg3) | 0x01400180)); + + hose->first_busno = 0; + hose->last_busno = 0xff; + + pci_set_region(hose->regions + 0, + CONFIG_PCI_MEM_BUS, + CONFIG_PCI_MEM_PHYS, + CONFIG_PCI_MEM_SIZE, + PCI_REGION_MEM); + + pci_set_region(hose->regions + 1, + CONFIG_PCI_IO_BUS, + CONFIG_PCI_IO_PHYS, + CONFIG_PCI_IO_SIZE, + PCI_REGION_IO); + + pci_set_region(hose->regions + 2, + CONFIG_PCI_SYS_MEM_BUS, + CONFIG_PCI_SYS_MEM_PHYS, + CONFIG_PCI_SYS_MEM_SIZE, + PCI_REGION_MEM | PCI_REGION_SYS_MEMORY); + + hose->region_count = 3; + + hose->cfg_addr = &(xcpci->cfg_adr); + hose->cfg_data = (volatile unsigned char *)CONFIG_PCI_CFG_BUS; + + pci_set_ops(hose, + mpc8220_pci_read_config_byte, + mpc8220_pci_read_config_word, + mpc8220_pci_read_config_dword, + mpc8220_pci_write_config_byte, + mpc8220_pci_write_config_word, + mpc8220_pci_write_config_dword); + + /* Hose scan */ + pci_register_hose(hose); + hose->last_busno = pci_hose_scan(hose); + + out_be32 (&xcpci->base0, PCI_BASE_ADDR_REG0); /* 256MB - MBAR space */ + out_be32 (&xcpci->base1, PCI_BASE_ADDR_REG1); /* 1GB - SDRAM space */ +} + +#endif /* CONFIG_PCI */ diff --git a/arch/powerpc/cpu/mpc8220/speed.c b/arch/powerpc/cpu/mpc8220/speed.c new file mode 100644 index 0000000..62ac845 --- /dev/null +++ b/arch/powerpc/cpu/mpc8220/speed.c @@ -0,0 +1,123 @@ +/* + * (C) Copyright 2004, Freescale, Inc + * TsiChung Liew, Tsi-Chung.Liew@freescale.com. + * + * 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> +#include <mpc8220.h> +#include <asm/processor.h> + +DECLARE_GLOBAL_DATA_PTR; + +typedef struct pllmultiplier { + u8 hid1; + int multi; + int vco_div; +} pllcfg_t; + +/* ------------------------------------------------------------------------- */ + +/* + * + */ + +int get_clocks (void) +{ + pllcfg_t bus2core[] = { + {0x02, 2, 8}, /* 1 */ + {0x01, 2, 4}, + {0x0C, 3, 8}, /* 1.5 */ + {0x00, 3, 4}, + {0x18, 3, 2}, + {0x05, 4, 4}, /* 2 */ + {0x04, 4, 2}, + {0x11, 5, 4}, /* 2.5 */ + {0x06, 5, 2}, + {0x10, 6, 4}, /* 3 */ + {0x08, 6, 2}, + {0x0E, 7, 2}, /* 3.5 */ + {0x0A, 8, 2}, /* 4 */ + {0x07, 9, 2}, /* 4.5 */ + {0x0B, 10, 2}, /* 5 */ + {0x09, 11, 2}, /* 5.5 */ + {0x0D, 12, 2}, /* 6 */ + {0x12, 13, 2}, /* 6.5 */ + {0x14, 14, 2}, /* 7 */ + {0x16, 15, 2}, /* 7.5 */ + {0x1C, 16, 2} /* 8 */ + }; + u32 hid1; + int i, size, pci2bus; + +#if !defined(CONFIG_SYS_MPC8220_CLKIN) +#error clock measuring not implemented yet - define CONFIG_SYS_MPC8220_CLKIN +#endif + + gd->inp_clk = CONFIG_SYS_MPC8220_CLKIN; + + /* Read XLB to PCI(INP) clock multiplier */ + pci2bus = (*((volatile u32 *)PCI_REG_PCIGSCR) & + PCI_REG_PCIGSCR_PCI2XLB_CLK_MASK)>>PCI_REG_PCIGSCR_PCI2XLB_CLK_BIT; + + /* XLB bus clock */ + gd->bus_clk = CONFIG_SYS_MPC8220_CLKIN * pci2bus; + + /* PCI clock is same as input clock */ + gd->pci_clk = CONFIG_SYS_MPC8220_CLKIN; + + /* FlexBus is temporary set as the same as input clock */ + /* will do dynamic in the future */ + gd->flb_clk = CONFIG_SYS_MPC8220_CLKIN; + + /* CPU Clock - Read HID1 */ + asm volatile ("mfspr %0, 1009":"=r" (hid1):); + + size = sizeof (bus2core) / sizeof (pllcfg_t); + + hid1 >>= 27; + + for (i = 0; i < size; i++) + if (hid1 == bus2core[i].hid1) { + gd->cpu_clk = (bus2core[i].multi * gd->bus_clk) >> 1; + gd->vco_clk = CONFIG_SYS_MPC8220_SYSPLL_VCO_MULTIPLIER * (gd->pci_clk * bus2core[i].vco_div)/2; + break; + } + + /* hardcoded 81MHz for now */ + gd->pev_clk = 81000000; + + return (0); +} + +int prt_mpc8220_clks (void) +{ + char buf1[32], buf2[32], buf3[32], buf4[32]; + + printf (" Bus %s MHz, CPU %s MHz, PCI %s MHz, VCO %s MHz\n", + strmhz(buf1, gd->bus_clk), + strmhz(buf2, gd->cpu_clk), + strmhz(buf3, gd->pci_clk), + strmhz(buf4, gd->vco_clk) + ); + return (0); +} + +/* ------------------------------------------------------------------------- */ diff --git a/arch/powerpc/cpu/mpc8220/start.S b/arch/powerpc/cpu/mpc8220/start.S new file mode 100644 index 0000000..3d79d8e --- /dev/null +++ b/arch/powerpc/cpu/mpc8220/start.S @@ -0,0 +1,750 @@ +/* + * Copyright (C) 1998 Dan Malek <dmalek@jlc.net> + * Copyright (C) 1999 Magnus Damm <kieraypc01.p.y.kie.era.ericsson.se> + * Copyright (C) 2000 - 2003 Wolfgang Denk <wd@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 + */ + +/* + * U-Boot - Startup Code for MPC8220 CPUs + */ +#include <config.h> +#include <mpc8220.h> +#include <timestamp.h> +#include <version.h> + +#define _LINUX_CONFIG_H 1 /* avoid reading Linux autoconf.h file */ + +#include <ppc_asm.tmpl> +#include <ppc_defs.h> + +#include <asm/cache.h> +#include <asm/mmu.h> + +#ifndef CONFIG_IDENT_STRING +#define CONFIG_IDENT_STRING "" +#endif + +/* We don't want the MMU yet. +*/ +#undef MSR_KERNEL +/* Floating Point enable, Machine Check and Recoverable Interr. */ +#ifdef DEBUG +#define MSR_KERNEL (MSR_FP|MSR_RI) +#else +#define MSR_KERNEL (MSR_FP|MSR_ME|MSR_RI) +#endif + +/* + * Set up GOT: Global Offset Table + * + * Use r12 to access the GOT + */ + START_GOT + GOT_ENTRY(_GOT2_TABLE_) + GOT_ENTRY(_FIXUP_TABLE_) + + GOT_ENTRY(_start) + GOT_ENTRY(_start_of_vectors) + GOT_ENTRY(_end_of_vectors) + GOT_ENTRY(transfer_to_handler) + + GOT_ENTRY(__init_end) + GOT_ENTRY(_end) + GOT_ENTRY(__bss_start) + END_GOT + +/* + * Version string + */ + .data + .globl version_string +version_string: + .ascii U_BOOT_VERSION + .ascii " (", U_BOOT_DATE, " - ", U_BOOT_TIME, ")" + .ascii CONFIG_IDENT_STRING, "\0" + +/* + * Exception vectors + */ + .text + . = EXC_OFF_SYS_RESET + .globl _start +_start: + li r21, BOOTFLAG_COLD /* Normal Power-On */ + nop + b boot_cold + + . = EXC_OFF_SYS_RESET + 0x10 + + .globl _start_warm +_start_warm: + li r21, BOOTFLAG_WARM /* Software reboot */ + b boot_warm + +boot_cold: +boot_warm: + mfmsr r5 /* save msr contents */ + + /* replace default MBAR base address from 0x80000000 + to 0xf0000000 */ + +#if defined(CONFIG_SYS_DEFAULT_MBAR) && !defined(CONFIG_SYS_RAMBOOT) + lis r3, CONFIG_SYS_MBAR@h + ori r3, r3, CONFIG_SYS_MBAR@l + + /* MBAR is mirrored into the MBAR SPR */ + mtspr MBAR,r3 + mtspr SPRN_SPRG7W,r3 + lis r4, CONFIG_SYS_DEFAULT_MBAR@h + stw r3, 0(r4) +#endif /* CONFIG_SYS_DEFAULT_MBAR */ + + /* Initialise the MPC8220 processor core */ + /*--------------------------------------------------------------*/ + + bl init_8220_core + + /* initialize some things that are hard to access from C */ + /*--------------------------------------------------------------*/ + + /* set up stack in on-chip SRAM */ + lis r3, CONFIG_SYS_INIT_RAM_ADDR@h + ori r3, r3, CONFIG_SYS_INIT_RAM_ADDR@l + ori r1, r3, CONFIG_SYS_INIT_SP_OFFSET + + li r0, 0 /* Make room for stack frame header and */ + stwu r0, -4(r1) /* clear final stack frame so that */ + stwu r0, -4(r1) /* stack backtraces terminate cleanly */ + + /* let the C-code set up the rest */ + /* */ + /* Be careful to keep code relocatable ! */ + /*--------------------------------------------------------------*/ + + GET_GOT /* initialize GOT access */ + + /* r3: IMMR */ + bl cpu_init_f /* run low-level CPU init code (in Flash)*/ + + mr r3, r21 + /* r3: BOOTFLAG */ + bl board_init_f /* run 1st part of board init code (in Flash)*/ + +/* + * Vector Table + */ + + .globl _start_of_vectors +_start_of_vectors: + +/* Machine check */ + STD_EXCEPTION(0x200, MachineCheck, MachineCheckException) + +/* Data Storage exception. */ + STD_EXCEPTION(0x300, DataStorage, UnknownException) + +/* Instruction Storage exception. */ + STD_EXCEPTION(0x400, InstStorage, UnknownException) + +/* External Interrupt exception. */ + STD_EXCEPTION(0x500, ExtInterrupt, external_interrupt) + +/* Alignment exception. */ + . = 0x600 +Alignment: + EXCEPTION_PROLOG(SRR0, SRR1) + mfspr r4,DAR + stw r4,_DAR(r21) + mfspr r5,DSISR + stw r5,_DSISR(r21) + addi r3,r1,STACK_FRAME_OVERHEAD + EXC_XFER_TEMPLATE(Alignment, AlignmentException, MSR_KERNEL, COPY_EE) + +/* Program check exception */ + . = 0x700 +ProgramCheck: + EXCEPTION_PROLOG(SRR0, SRR1) + addi r3,r1,STACK_FRAME_OVERHEAD + EXC_XFER_TEMPLATE(ProgramCheck, ProgramCheckException, + MSR_KERNEL, COPY_EE) + + STD_EXCEPTION(0x800, FPUnavailable, UnknownException) + + /* I guess we could implement decrementer, and may have + * to someday for timekeeping. + */ + STD_EXCEPTION(0x900, Decrementer, timer_interrupt) + + STD_EXCEPTION(0xa00, Trap_0a, UnknownException) + STD_EXCEPTION(0xb00, Trap_0b, UnknownException) + STD_EXCEPTION(0xc00, SystemCall, UnknownException) + STD_EXCEPTION(0xd00, SingleStep, UnknownException) + + STD_EXCEPTION(0xe00, Trap_0e, UnknownException) + STD_EXCEPTION(0xf00, Trap_0f, UnknownException) + + STD_EXCEPTION(0x1000, InstructionTLBMiss, UnknownException) + STD_EXCEPTION(0x1100, DataLoadTLBMiss, UnknownException) + STD_EXCEPTION(0x1200, DataStoreTLBMiss, UnknownException) +#ifdef DEBUG + . = 0x1300 + /* + * This exception occurs when the program counter matches the + * Instruction Address Breakpoint Register (IABR). + * + * I want the cpu to halt if this occurs so I can hunt around + * with the debugger and look at things. + * + * When DEBUG is defined, both machine check enable (in the MSR) + * and checkstop reset enable (in the reset mode register) are + * turned off and so a checkstop condition will result in the cpu + * halting. + * + * I force the cpu into a checkstop condition by putting an illegal + * instruction here (at least this is the theory). + * + * well - that didnt work, so just do an infinite loop! + */ +1: b 1b +#else + STD_EXCEPTION(0x1300, InstructionBreakpoint, DebugException) +#endif + STD_EXCEPTION(0x1400, SMI, UnknownException) + + STD_EXCEPTION(0x1500, Trap_15, UnknownException) + STD_EXCEPTION(0x1600, Trap_16, UnknownException) + STD_EXCEPTION(0x1700, Trap_17, UnknownException) + STD_EXCEPTION(0x1800, Trap_18, UnknownException) + STD_EXCEPTION(0x1900, Trap_19, UnknownException) + STD_EXCEPTION(0x1a00, Trap_1a, UnknownException) + STD_EXCEPTION(0x1b00, Trap_1b, UnknownException) + STD_EXCEPTION(0x1c00, Trap_1c, UnknownException) + STD_EXCEPTION(0x1d00, Trap_1d, UnknownException) + STD_EXCEPTION(0x1e00, Trap_1e, UnknownException) + STD_EXCEPTION(0x1f00, Trap_1f, UnknownException) + STD_EXCEPTION(0x2000, Trap_20, UnknownException) + STD_EXCEPTION(0x2100, Trap_21, UnknownException) + STD_EXCEPTION(0x2200, Trap_22, UnknownException) + STD_EXCEPTION(0x2300, Trap_23, UnknownException) + STD_EXCEPTION(0x2400, Trap_24, UnknownException) + STD_EXCEPTION(0x2500, Trap_25, UnknownException) + STD_EXCEPTION(0x2600, Trap_26, UnknownException) + STD_EXCEPTION(0x2700, Trap_27, UnknownException) + STD_EXCEPTION(0x2800, Trap_28, UnknownException) + STD_EXCEPTION(0x2900, Trap_29, UnknownException) + STD_EXCEPTION(0x2a00, Trap_2a, UnknownException) + STD_EXCEPTION(0x2b00, Trap_2b, UnknownException) + STD_EXCEPTION(0x2c00, Trap_2c, UnknownException) + STD_EXCEPTION(0x2d00, Trap_2d, UnknownException) + STD_EXCEPTION(0x2e00, Trap_2e, UnknownException) + STD_EXCEPTION(0x2f00, Trap_2f, UnknownException) + + + .globl _end_of_vectors +_end_of_vectors: + + . = 0x3000 + +/* + * This code finishes saving the registers to the exception frame + * and jumps to the appropriate handler for the exception. + * Register r21 is pointer into trap frame, r1 has new stack pointer. + */ + .globl transfer_to_handler +transfer_to_handler: + stw r22,_NIP(r21) + lis r22,MSR_POW@h + andc r23,r23,r22 + stw r23,_MSR(r21) + SAVE_GPR(7, r21) + SAVE_4GPRS(8, r21) + SAVE_8GPRS(12, r21) + SAVE_8GPRS(24, r21) + mflr r23 + andi. r24,r23,0x3f00 /* get vector offset */ + stw r24,TRAP(r21) + li r22,0 + stw r22,RESULT(r21) + lwz r24,0(r23) /* virtual address of handler */ + lwz r23,4(r23) /* where to go when done */ + mtspr SRR0,r24 + mtspr SRR1,r20 + mtlr r23 + SYNC + rfi /* jump to handler, enable MMU */ + +int_return: + mfmsr r28 /* Disable interrupts */ + li r4,0 + ori r4,r4,MSR_EE + andc r28,r28,r4 + SYNC /* Some chip revs need this... */ + mtmsr r28 + SYNC + lwz r2,_CTR(r1) + lwz r0,_LINK(r1) + mtctr r2 + mtlr r0 + lwz r2,_XER(r1) + lwz r0,_CCR(r1) + mtspr XER,r2 + mtcrf 0xFF,r0 + REST_10GPRS(3, r1) + REST_10GPRS(13, r1) + REST_8GPRS(23, r1) + REST_GPR(31, r1) + lwz r2,_NIP(r1) /* Restore environment */ + lwz r0,_MSR(r1) + mtspr SRR0,r2 + mtspr SRR1,r0 + lwz r0,GPR0(r1) + lwz r2,GPR2(r1) + lwz r1,GPR1(r1) + SYNC + rfi + +/* + * This code initialises the MPC8220 processor core + * (conforms to PowerPC 603e spec) + * Note: expects original MSR contents to be in r5. + */ + + .globl init_8220_core +init_8220_core: + + /* Initialize machine status; enable machine check interrupt */ + /*--------------------------------------------------------------*/ + + li r3, MSR_KERNEL /* Set ME and RI flags */ + rlwimi r3, r5, 0, 25, 25 /* preserve IP bit set by HRCW */ +#ifdef DEBUG + rlwimi r3, r5, 0, 21, 22 /* debugger might set SE & BE bits */ +#endif + SYNC /* Some chip revs need this... */ + mtmsr r3 + SYNC + mtspr SRR1, r3 /* Make SRR1 match MSR */ + + /* Initialize the Hardware Implementation-dependent Registers */ + /* HID0 also contains cache control */ + /*--------------------------------------------------------------*/ + + lis r3, CONFIG_SYS_HID0_INIT@h + ori r3, r3, CONFIG_SYS_HID0_INIT@l + SYNC + mtspr HID0, r3 + + lis r3, CONFIG_SYS_HID0_FINAL@h + ori r3, r3, CONFIG_SYS_HID0_FINAL@l + SYNC + mtspr HID0, r3 + + /* Enable Extra BATs */ + mfspr r3, 1011 /* HID2 */ + lis r4, 0x0004 + ori r4, r4, 0x0000 + or r4, r4, r3 + mtspr 1011, r4 + sync + + /* clear all BAT's */ + /*--------------------------------------------------------------*/ + + li r0, 0 + mtspr DBAT0U, r0 + mtspr DBAT0L, r0 + mtspr DBAT1U, r0 + mtspr DBAT1L, r0 + mtspr DBAT2U, r0 + mtspr DBAT2L, r0 + mtspr DBAT3U, r0 + mtspr DBAT3L, r0 + mtspr DBAT4U, r0 + mtspr DBAT4L, r0 + mtspr DBAT5U, r0 + mtspr DBAT5L, r0 + mtspr DBAT6U, r0 + mtspr DBAT6L, r0 + mtspr DBAT7U, r0 + mtspr DBAT7L, r0 + mtspr IBAT0U, r0 + mtspr IBAT0L, r0 + mtspr IBAT1U, r0 + mtspr IBAT1L, r0 + mtspr IBAT2U, r0 + mtspr IBAT2L, r0 + mtspr IBAT3U, r0 + mtspr IBAT3L, r0 + mtspr IBAT4U, r0 + mtspr IBAT4L, r0 + mtspr IBAT5U, r0 + mtspr IBAT5L, r0 + mtspr IBAT6U, r0 + mtspr IBAT6L, r0 + mtspr IBAT7U, r0 + mtspr IBAT7L, r0 + SYNC + + /* invalidate all tlb's */ + /* */ + /* From the 603e User Manual: "The 603e provides the ability to */ + /* invalidate a TLB entry. The TLB Invalidate Entry (tlbie) */ + /* instruction invalidates the TLB entry indexed by the EA, and */ + /* operates on both the instruction and data TLBs simultaneously*/ + /* invalidating four TLB entries (both sets in each TLB). The */ + /* index corresponds to bits 15-19 of the EA. To invalidate all */ + /* entries within both TLBs, 32 tlbie instructions should be */ + /* issued, incrementing this field by one each time." */ + /* */ + /* "Note that the tlbia instruction is not implemented on the */ + /* 603e." */ + /* */ + /* bits 15-19 correspond to addresses 0x00000000 to 0x0001F000 */ + /* incrementing by 0x1000 each time. The code below is sort of */ + /* based on code in "flush_tlbs" from arch/powerpc/kernel/head.S */ + /* */ + /*--------------------------------------------------------------*/ + + li r3, 32 + mtctr r3 + li r3, 0 +1: tlbie r3 + addi r3, r3, 0x1000 + bdnz 1b + SYNC + + /* Done! */ + /*--------------------------------------------------------------*/ + + blr + +/* Cache functions. + * + * Note: requires that all cache bits in + * HID0 are in the low half word. + */ + .globl icache_enable +icache_enable: + lis r4, 0 + ori r4, r4, CONFIG_SYS_HID0_INIT /* set ICE & ICFI bit */ + rlwinm r3, r4, 0, 21, 19 /* clear the ICFI bit */ + + /* + * The setting of the instruction cache enable (ICE) bit must be + * preceded by an isync instruction to prevent the cache from being + * enabled or disabled while an instruction access is in progress. + */ + isync + mtspr HID0, r4 /* Enable Instr Cache & Inval cache */ + mtspr HID0, r3 /* using 2 consec instructions */ + isync + blr + + .globl icache_disable +icache_disable: + mfspr r3, HID0 + rlwinm r3, r3, 0, 17, 15 /* clear the ICE bit */ + mtspr HID0, r3 + isync + blr + + .globl icache_status +icache_status: + mfspr r3, HID0 + rlwinm r3, r3, HID0_ICE_BITPOS + 1, 31, 31 + blr + + .globl dcache_enable +dcache_enable: + lis r4, 0 + ori r4, r4, HID0_DCE|HID0_DCFI /* set DCE & DCFI bit */ + rlwinm r3, r4, 0, 22, 20 /* clear the DCFI bit */ + + /* Enable address translation in MSR bit */ + mfmsr r5 + ori r5, r5, 0x + + + /* + * The setting of the instruction cache enable (ICE) bit must be + * preceded by an isync instruction to prevent the cache from being + * enabled or disabled while an instruction access is in progress. + */ + isync + mtspr HID0, r4 /* Enable Data Cache & Inval cache*/ + mtspr HID0, r3 /* using 2 consec instructions */ + isync + blr + + .globl dcache_disable +dcache_disable: + mfspr r3, HID0 + rlwinm r3, r3, 0, 18, 16 /* clear the DCE bit */ + mtspr HID0, r3 + isync + blr + + .globl dcache_status +dcache_status: + mfspr r3, HID0 + rlwinm r3, r3, HID0_DCE_BITPOS + 1, 31, 31 + blr + + .globl get_pvr +get_pvr: + mfspr r3, PVR + blr + +/*------------------------------------------------------------------------------*/ + +/* + * void relocate_code (addr_sp, gd, addr_moni) + * + * This "function" does not return, instead it continues in RAM + * after relocating the monitor code. + * + * r3 = dest + * r4 = src + * r5 = length in bytes + * r6 = cachelinesize + */ + .globl relocate_code +relocate_code: + mr r1, r3 /* Set new stack pointer */ + mr r9, r4 /* Save copy of Global Data pointer */ + mr r10, r5 /* Save copy of Destination Address */ + + GET_GOT + mr r3, r5 /* Destination Address */ + lis r4, CONFIG_SYS_MONITOR_BASE@h /* Source Address */ + ori r4, r4, CONFIG_SYS_MONITOR_BASE@l + lwz r5, GOT(__init_end) + sub r5, r5, r4 + li r6, CONFIG_SYS_CACHELINE_SIZE /* Cache Line Size */ + + /* + * Fix GOT pointer: + * + * New GOT-PTR = (old GOT-PTR - CONFIG_SYS_MONITOR_BASE) + Destination Address + * + * Offset: + */ + sub r15, r10, r4 + + /* First our own GOT */ + add r12, r12, r15 + /* then the one used by the C code */ + add r30, r30, r15 + + /* + * Now relocate code + */ + + cmplw cr1,r3,r4 + addi r0,r5,3 + srwi. r0,r0,2 + beq cr1,4f /* In place copy is not necessary */ + beq 7f /* Protect against 0 count */ + mtctr r0 + bge cr1,2f + + la r8,-4(r4) + la r7,-4(r3) +1: lwzu r0,4(r8) + stwu r0,4(r7) + bdnz 1b + b 4f + +2: slwi r0,r0,2 + add r8,r4,r0 + add r7,r3,r0 +3: lwzu r0,-4(r8) + stwu r0,-4(r7) + bdnz 3b + +/* + * Now flush the cache: note that we must start from a cache aligned + * address. Otherwise we might miss one cache line. + */ +4: cmpwi r6,0 + add r5,r3,r5 + beq 7f /* Always flush prefetch queue in any case */ + subi r0,r6,1 + andc r3,r3,r0 + mfspr r7,HID0 /* don't do dcbst if dcache is disabled */ + rlwinm r7,r7,HID0_DCE_BITPOS+1,31,31 + cmpwi r7,0 + beq 9f + mr r4,r3 +5: dcbst 0,r4 + add r4,r4,r6 + cmplw r4,r5 + blt 5b + sync /* Wait for all dcbst to complete on bus */ +9: mfspr r7,HID0 /* don't do icbi if icache is disabled */ + rlwinm r7,r7,HID0_ICE_BITPOS+1,31,31 + cmpwi r7,0 + beq 7f + mr r4,r3 +6: icbi 0,r4 + add r4,r4,r6 + cmplw r4,r5 + blt 6b +7: sync /* Wait for all icbi to complete on bus */ + isync + +/* + * We are done. Do not return, instead branch to second part of board + * initialization, now running from RAM. + */ + + addi r0, r10, in_ram - _start + EXC_OFF_SYS_RESET + mtlr r0 + blr + +in_ram: + + /* + * Relocation Function, r12 point to got2+0x8000 + * + * Adjust got2 pointers, no need to check for 0, this code + * already puts a few entries in the table. + */ + li r0,__got2_entries@sectoff@l + la r3,GOT(_GOT2_TABLE_) + lwz r11,GOT(_GOT2_TABLE_) + mtctr r0 + sub r11,r3,r11 + addi r3,r3,-4 +1: lwzu r0,4(r3) + cmpwi r0,0 + beq- 2f + add r0,r0,r11 + stw r0,0(r3) +2: bdnz 1b + + /* + * Now adjust the fixups and the pointers to the fixups + * in case we need to move ourselves again. + */ + li r0,__fixup_entries@sectoff@l + lwz r3,GOT(_FIXUP_TABLE_) + cmpwi r0,0 + mtctr r0 + addi r3,r3,-4 + beq 4f +3: lwzu r4,4(r3) + lwzux r0,r4,r11 + add r0,r0,r11 + stw r10,0(r3) + stw r0,0(r4) + bdnz 3b +4: +clear_bss: + /* + * Now clear BSS segment + */ + lwz r3,GOT(__bss_start) + lwz r4,GOT(_end) + + cmplw 0, r3, r4 + beq 6f + + li r0, 0 +5: + stw r0, 0(r3) + addi r3, r3, 4 + cmplw 0, r3, r4 + bne 5b +6: + + mr r3, r9 /* Global Data pointer */ + mr r4, r10 /* Destination Address */ + bl board_init_r + + /* + * Copy exception vector code to low memory + * + * r3: dest_addr + * r7: source address, r8: end address, r9: target address + */ + .globl trap_init +trap_init: + mflr r4 /* save link register */ + GET_GOT + lwz r7, GOT(_start) + lwz r8, GOT(_end_of_vectors) + + li r9, 0x100 /* reset vector always at 0x100 */ + + cmplw 0, r7, r8 + bgelr /* return if r7>=r8 - just in case */ +1: + lwz r0, 0(r7) + stw r0, 0(r9) + addi r7, r7, 4 + addi r9, r9, 4 + cmplw 0, r7, r8 + bne 1b + + /* + * relocate `hdlr' and `int_return' entries + */ + li r7, .L_MachineCheck - _start + EXC_OFF_SYS_RESET + li r8, Alignment - _start + EXC_OFF_SYS_RESET +2: + bl trap_reloc + addi r7, r7, 0x100 /* next exception vector */ + cmplw 0, r7, r8 + blt 2b + + li r7, .L_Alignment - _start + EXC_OFF_SYS_RESET + bl trap_reloc + + li r7, .L_ProgramCheck - _start + EXC_OFF_SYS_RESET + bl trap_reloc + + li r7, .L_FPUnavailable - _start + EXC_OFF_SYS_RESET + li r8, SystemCall - _start + EXC_OFF_SYS_RESET +3: + bl trap_reloc + addi r7, r7, 0x100 /* next exception vector */ + cmplw 0, r7, r8 + blt 3b + + li r7, .L_SingleStep - _start + EXC_OFF_SYS_RESET + li r8, _end_of_vectors - _start + EXC_OFF_SYS_RESET +4: + bl trap_reloc + addi r7, r7, 0x100 /* next exception vector */ + cmplw 0, r7, r8 + blt 4b + + mfmsr r3 /* now that the vectors have */ + lis r7, MSR_IP@h /* relocated into low memory */ + ori r7, r7, MSR_IP@l /* MSR[IP] can be turned off */ + andc r3, r3, r7 /* (if it was on) */ + SYNC /* Some chip revs need this... */ + mtmsr r3 + SYNC + + mtlr r4 /* restore link register */ + blr diff --git a/arch/powerpc/cpu/mpc8220/traps.c b/arch/powerpc/cpu/mpc8220/traps.c new file mode 100644 index 0000000..13894c9 --- /dev/null +++ b/arch/powerpc/cpu/mpc8220/traps.c @@ -0,0 +1,236 @@ +/* + * linux/arch/powerpc/kernel/traps.c + * + * Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org) + * + * Modified by Cort Dougan (cort@cs.nmt.edu) + * and Paul Mackerras (paulus@cs.anu.edu.au) + * fixed Machine Check Reasons by Reinhard Meyer (r.meyer@emk-elektronik.de) + * + * (C) Copyright 2000-2003 + * Wolfgang Denk, DENX Software Engineering, wd@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 + */ + +/* + * This file handles the architecture-dependent parts of hardware exceptions + */ + +#include <common.h> +#include <command.h> +#include <kgdb.h> +#include <asm/processor.h> + +/* Returns 0 if exception not found and fixup otherwise. */ +extern unsigned long search_exception_table (unsigned long); + +/* THIS NEEDS CHANGING to use the board info structure. +*/ +#define END_OF_MEM 0x02000000 + +/* + * Trap & Exception support + */ + +void print_backtrace (unsigned long *sp) +{ + int cnt = 0; + unsigned long i; + + printf ("Call backtrace: "); + while (sp) { + if ((uint) sp > END_OF_MEM) + break; + + i = sp[1]; + if (cnt++ % 7 == 0) + printf ("\n"); + printf ("%08lX ", i); + if (cnt > 32) + break; + sp = (unsigned long *) *sp; + } + printf ("\n"); +} + +void show_regs (struct pt_regs *regs) +{ + int i; + + printf ("NIP: %08lX XER: %08lX LR: %08lX REGS: %p TRAP: %04lx DAR: %08lX\n", + regs->nip, regs->xer, regs->link, regs, regs->trap, regs->dar); + printf ("MSR: %08lx EE: %01x PR: %01x FP: %01x ME: %01x IR/DR: %01x%01x\n", + regs->msr, + regs->msr & MSR_EE ? 1 : 0, regs->msr & MSR_PR ? 1 : 0, + regs->msr & MSR_FP ? 1 : 0, regs->msr & MSR_ME ? 1 : 0, + regs->msr & MSR_IR ? 1 : 0, regs->msr & MSR_DR ? 1 : 0); + + printf ("\n"); + for (i = 0; i < 32; i++) { + if ((i % 8) == 0) { + printf ("GPR%02d: ", i); + } + + printf ("%08lX ", regs->gpr[i]); + if ((i % 8) == 7) { + printf ("\n"); + } + } +} + + +void _exception (int signr, struct pt_regs *regs) +{ + show_regs (regs); + print_backtrace ((unsigned long *) regs->gpr[1]); + panic ("Exception in kernel pc %lx signal %d", regs->nip, signr); +} + +void MachineCheckException (struct pt_regs *regs) +{ + unsigned long fixup; + + /* Probing PCI using config cycles cause this exception + * when a device is not present. Catch it and return to + * the PCI exception handler. + */ + if ((fixup = search_exception_table (regs->nip)) != 0) { + regs->nip = fixup; + return; + } +#if defined(CONFIG_CMD_KGDB) + if (debugger_exception_handler + && (*debugger_exception_handler) (regs)) + return; +#endif + + printf ("Machine check in kernel mode.\n"); + printf ("Caused by (from msr): "); + printf ("regs %p ", regs); + /* refer to 603e Manual (MPC603EUM/AD), chapter 4.5.2.1 */ + switch (regs->msr & 0x000F0000) { + case (0x80000000 >> 12): + printf ("Machine check signal - probably due to mm fault\n" + "with mmu off\n"); + break; + case (0x80000000 >> 13): + printf ("Transfer error ack signal\n"); + break; + case (0x80000000 >> 14): + printf ("Data parity signal\n"); + break; + case (0x80000000 >> 15): + printf ("Address parity signal\n"); + break; + default: + printf ("Unknown values in msr\n"); + } + show_regs (regs); + print_backtrace ((unsigned long *) regs->gpr[1]); + panic ("machine check"); +} + +void AlignmentException (struct pt_regs *regs) +{ +#if defined(CONFIG_CMD_KGDB) + if (debugger_exception_handler + && (*debugger_exception_handler) (regs)) + return; +#endif + show_regs (regs); + print_backtrace ((unsigned long *) regs->gpr[1]); + panic ("Alignment Exception"); +} + +void ProgramCheckException (struct pt_regs *regs) +{ +#if defined(CONFIG_CMD_KGDB) + if (debugger_exception_handler + && (*debugger_exception_handler) (regs)) + return; +#endif + show_regs (regs); + print_backtrace ((unsigned long *) regs->gpr[1]); + panic ("Program Check Exception"); +} + +void SoftEmuException (struct pt_regs *regs) +{ +#if defined(CONFIG_CMD_KGDB) + if (debugger_exception_handler + && (*debugger_exception_handler) (regs)) + return; +#endif + show_regs (regs); + print_backtrace ((unsigned long *) regs->gpr[1]); + panic ("Software Emulation Exception"); +} + + +void UnknownException (struct pt_regs *regs) +{ +#if defined(CONFIG_CMD_KGDB) + if (debugger_exception_handler + && (*debugger_exception_handler) (regs)) + return; +#endif + printf ("Bad trap at PC: %lx, SR: %lx, vector=%lx\n", + regs->nip, regs->msr, regs->trap); + _exception (0, regs); +} + +#if defined(CONFIG_CMD_BEDBUG) +extern void do_bedbug_breakpoint (struct pt_regs *); +#endif + +void DebugException (struct pt_regs *regs) +{ + + printf ("Debugger trap at @ %lx\n", regs->nip); + show_regs (regs); +#if defined(CONFIG_CMD_BEDBUG) + do_bedbug_breakpoint (regs); +#endif +} + +/* Probe an address by reading. If not present, return -1, otherwise + * return 0. + */ +int addr_probe (uint * addr) +{ +#if 0 + int retval; + + __asm__ __volatile__ ("1: lwz %0,0(%1)\n" + " eieio\n" + " li %0,0\n" + "2:\n" + ".section .fixup,\"ax\"\n" + "3: li %0,-1\n" + " b 2b\n" + ".section __ex_table,\"a\"\n" + " .align 2\n" + " .long 1b,3b\n" + ".text":"=r" (retval):"r" (addr)); + + return (retval); +#endif + return 0; +} diff --git a/arch/powerpc/cpu/mpc8220/u-boot.lds b/arch/powerpc/cpu/mpc8220/u-boot.lds new file mode 100644 index 0000000..31a7a0e --- /dev/null +++ b/arch/powerpc/cpu/mpc8220/u-boot.lds @@ -0,0 +1,122 @@ +/* + * (C) Copyright 2003-2004 + * Wolfgang Denk, DENX Software Engineering, wd@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 + */ + +OUTPUT_ARCH(powerpc) +/* Do we need any of these for elf? + __DYNAMIC = 0; */ +SECTIONS +{ + /* Read-only sections, merged into text segment: */ + . = + SIZEOF_HEADERS; + .interp : { *(.interp) } + .hash : { *(.hash) } + .dynsym : { *(.dynsym) } + .dynstr : { *(.dynstr) } + .rel.text : { *(.rel.text) } + .rela.text : { *(.rela.text) } + .rel.data : { *(.rel.data) } + .rela.data : { *(.rela.data) } + .rel.rodata : { *(.rel.rodata) } + .rela.rodata : { *(.rela.rodata) } + .rel.got : { *(.rel.got) } + .rela.got : { *(.rela.got) } + .rel.ctors : { *(.rel.ctors) } + .rela.ctors : { *(.rela.ctors) } + .rel.dtors : { *(.rel.dtors) } + .rela.dtors : { *(.rela.dtors) } + .rel.bss : { *(.rel.bss) } + .rela.bss : { *(.rela.bss) } + .rel.plt : { *(.rel.plt) } + .rela.plt : { *(.rela.plt) } + .init : { *(.init) } + .plt : { *(.plt) } + .text : + { + arch/powerpc/cpu/mpc8220/start.o (.text) + *(.text) + *(.got1) + . = ALIGN(16); + *(.eh_frame) + *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.rodata*))) + } + .fini : { *(.fini) } =0 + .ctors : { *(.ctors) } + .dtors : { *(.dtors) } + + /* Read-write section, merged into data segment: */ + . = (. + 0x0FFF) & 0xFFFFF000; + _erotext = .; + PROVIDE (erotext = .); + .reloc : + { + *(.got) + _GOT2_TABLE_ = .; + *(.got2) + _FIXUP_TABLE_ = .; + *(.fixup) + } + __got2_entries = (_FIXUP_TABLE_ - _GOT2_TABLE_) >> 2; + __fixup_entries = (. - _FIXUP_TABLE_) >> 2; + + .data : + { + *(.data) + *(.data1) + *(.sdata) + *(.sdata2) + *(.dynamic) + CONSTRUCTORS + } + _edata = .; + PROVIDE (edata = .); + + . = .; + __u_boot_cmd_start = .; + .u_boot_cmd : { *(.u_boot_cmd) } + __u_boot_cmd_end = .; + + + . = .; + __start___ex_table = .; + __ex_table : { *(__ex_table) } + __stop___ex_table = .; + + . = ALIGN(4096); + __init_begin = .; + .text.init : { *(.text.init) } + .data.init : { *(.data.init) } + . = ALIGN(4096); + __init_end = .; + + __bss_start = .; + .bss (NOLOAD) : + { + *(.sbss) *(.scommon) + *(.dynbss) + *(.bss) + *(COMMON) + . = ALIGN(4); + } + _end = . ; + PROVIDE (end = .); +} diff --git a/arch/powerpc/cpu/mpc8220/uart.c b/arch/powerpc/cpu/mpc8220/uart.c new file mode 100644 index 0000000..0c4b536 --- /dev/null +++ b/arch/powerpc/cpu/mpc8220/uart.c @@ -0,0 +1,126 @@ +/* + * (C) Copyright 2004, Freescale, Inc + * TsiChung Liew, Tsi-Chung.Liew@freescale.com. + * + * 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 + * + */ + +/* + * Minimal serial functions needed to use one of the PSC ports + * as serial console interface. + */ + +#include <common.h> +#include <mpc8220.h> + +DECLARE_GLOBAL_DATA_PTR; + +#define PSC_BASE MMAP_PSC1 + +#if defined(CONFIG_PSC_CONSOLE) +int serial_init (void) +{ + volatile psc8220_t *psc = (psc8220_t *) PSC_BASE; + u32 counter; + + /* write to SICR: SIM2 = uart mode,dcd does not affect rx */ + psc->cr = 0; + psc->ipcr_acr = 0; + psc->isr_imr = 0; + + /* write to CSR: RX/TX baud rate from timers */ + psc->sr_csr = 0xdd000000; + + psc->mr1_2 = PSC_MR1_BITS_CHAR_8 | PSC_MR1_NO_PARITY | PSC_MR2_STOP_BITS_1; + + /* Setting up BaudRate */ + counter = ((gd->bus_clk / gd->baudrate)) >> 5; + counter++; + + /* write to CTUR: divide counter upper byte */ + psc->ctur = ((counter & 0xff00) << 16); + /* write to CTLR: divide counter lower byte */ + psc->ctlr = ((counter & 0x00ff) << 24); + + psc->cr = PSC_CR_RST_RX_CMD; + psc->cr = PSC_CR_RST_TX_CMD; + psc->cr = PSC_CR_RST_ERR_STS_CMD; + psc->cr = PSC_CR_RST_BRK_INT_CMD; + psc->cr = PSC_CR_RST_MR_PTR_CMD; + + psc->cr = PSC_CR_RX_ENABLE | PSC_CR_TX_ENABLE; + return (0); +} + +void serial_putc (const char c) +{ + volatile psc8220_t *psc = (psc8220_t *) PSC_BASE; + + if (c == '\n') + serial_putc ('\r'); + + /* Wait for last character to go. */ + while (!(psc->sr_csr & PSC_SR_TXRDY)); + + psc->xmitbuf[0] = c; +} + +void serial_puts (const char *s) +{ + while (*s) { + serial_putc (*s++); + } +} + +int serial_getc (void) +{ + volatile psc8220_t *psc = (psc8220_t *) PSC_BASE; + + /* Wait for a character to arrive. */ + while (!(psc->sr_csr & PSC_SR_RXRDY)); + return psc->xmitbuf[2]; +} + +int serial_tstc (void) +{ + volatile psc8220_t *psc = (psc8220_t *) PSC_BASE; + + return (psc->sr_csr & PSC_SR_RXRDY); +} + +void serial_setbrg (void) +{ + volatile psc8220_t *psc = (psc8220_t *) PSC_BASE; + u32 counter; + + counter = ((gd->bus_clk / gd->baudrate)) >> 5; + counter++; + + /* write to CTUR: divide counter upper byte */ + psc->ctur = ((counter & 0xff00) << 16); + /* write to CTLR: divide counter lower byte */ + psc->ctlr = ((counter & 0x00ff) << 24); + + psc->cr = PSC_CR_RST_RX_CMD; + psc->cr = PSC_CR_RST_TX_CMD; + + psc->cr = PSC_CR_RX_ENABLE | PSC_CR_TX_ENABLE; +} +#endif /* CONFIG_PSC_CONSOLE */ diff --git a/arch/powerpc/cpu/mpc824x/.gitignore b/arch/powerpc/cpu/mpc824x/.gitignore new file mode 100644 index 0000000..2d79931 --- /dev/null +++ b/arch/powerpc/cpu/mpc824x/.gitignore @@ -0,0 +1 @@ +/bedbug_603e.c diff --git a/arch/powerpc/cpu/mpc824x/Makefile b/arch/powerpc/cpu/mpc824x/Makefile new file mode 100644 index 0000000..a57ad12 --- /dev/null +++ b/arch/powerpc/cpu/mpc824x/Makefile @@ -0,0 +1,56 @@ +# +# (C) Copyright 2000-2006 +# Wolfgang Denk, DENX Software Engineering, wd@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 $(TOPDIR)/config.mk +ifneq ($(OBJTREE),$(SRCTREE)) +$(shell mkdir -p $(obj)drivers/epic) +$(shell mkdir -p $(obj)drivers/i2c) +endif + +LIB = $(obj)lib$(CPU).a + +START = start.o +COBJS = traps.o cpu.o cpu_init.o interrupts.o speed.o \ + drivers/epic/epic1.o drivers/i2c/i2c.o pci.o +COBJS_LN = bedbug_603e.o + +SRCS := $(START:.o=.S) $(SOBJS:.o=.S) $(COBJS:.o=.c) $(addprefix $(obj),$(COBJS_LN:.o=.c)) +OBJS := $(addprefix $(obj),$(SOBJS) $(COBJS) $(COBJS_LN)) +START := $(addprefix $(obj),$(START)) + +all: $(obj).depend $(START) $(LIB) + +$(LIB): $(OBJS) + $(AR) $(ARFLAGS) $@ $(OBJS) + +$(obj)bedbug_603e.c: + ln -sf $(src)../mpc8260/bedbug_603e.c $(obj)bedbug_603e.c + +######################################################################### + +# defines $(obj).depend target +include $(SRCTREE)/rules.mk + +sinclude $(obj).depend + +######################################################################### diff --git a/arch/powerpc/cpu/mpc824x/config.mk b/arch/powerpc/cpu/mpc824x/config.mk new file mode 100644 index 0000000..27c2873 --- /dev/null +++ b/arch/powerpc/cpu/mpc824x/config.mk @@ -0,0 +1,29 @@ +# +# (C) Copyright 2000 +# Wolfgang Denk, DENX Software Engineering, wd@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 +# + +PLATFORM_RELFLAGS += -fPIC -meabi + +PLATFORM_CPPFLAGS += -DCONFIG_MPC824X -ffixed-r2 -mstring -mcpu=603e -msoft-float + +# Use default linker script. Board port can override in board/*/config.mk +LDSCRIPT := $(SRCTREE)/arch/powerpc/cpu/mpc824x/u-boot.lds diff --git a/arch/powerpc/cpu/mpc824x/cpu.c b/arch/powerpc/cpu/mpc824x/cpu.c new file mode 100644 index 0000000..08f6a94 --- /dev/null +++ b/arch/powerpc/cpu/mpc824x/cpu.c @@ -0,0 +1,280 @@ +/* + * (C) Copyright 2000 - 2002 + * Wolfgang Denk, DENX Software Engineering, wd@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 <config.h> +#include <mpc824x.h> +#include <common.h> +#include <command.h> + +DECLARE_GLOBAL_DATA_PTR; + +int checkcpu (void) +{ + unsigned int pvr = get_pvr (); + unsigned int version = pvr >> 16; + unsigned char revision; + ulong clock = gd->cpu_clk; + char buf[32]; + + puts ("CPU: "); + + switch (version) { + case CPU_TYPE_8240: + puts ("MPC8240"); + break; + + case CPU_TYPE_8245: + puts ("MPC8245"); + break; + + default: + return -1; /*not valid for this source */ + } + + CONFIG_READ_BYTE (REVID, revision); + + if (revision) { + printf (" Revision %d.%d", + (revision & 0xf0) >> 4, + (revision & 0x0f)); + } else { + return -1; /* no valid CPU revision info */ + } + + printf (" at %s MHz:", strmhz (buf, clock)); + + printf (" %u kB I-Cache", checkicache () >> 10); + printf (" %u kB D-Cache", checkdcache () >> 10); + + puts ("\n"); + + return 0; +} + +/* ------------------------------------------------------------------------- */ +/* L1 i-cache */ + +int checkicache (void) +{ + /*TODO*/ + return 128 * 4 * 32; +}; + +/* ------------------------------------------------------------------------- */ +/* L1 d-cache */ + +int checkdcache (void) +{ + /*TODO*/ + return 128 * 4 * 32; + +}; + +/*------------------------------------------------------------------- */ + +int do_reset (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]) +{ + ulong msr, addr; + + /* Interrupts and MMU off */ + __asm__ ("mtspr 81, 0"); + + /* Interrupts and MMU off */ + __asm__ __volatile__ ("mfmsr %0":"=r" (msr):); + + msr &= ~0x1030; + __asm__ __volatile__ ("mtmsr %0"::"r" (msr)); + + /* + * Trying to execute the next instruction at a non-existing address + * should cause a machine check, resulting in reset + */ +#ifdef CONFIG_SYS_RESET_ADDRESS + addr = CONFIG_SYS_RESET_ADDRESS; +#else + /* + * note: when CONFIG_SYS_MONITOR_BASE points to a RAM address, + * CONFIG_SYS_MONITOR_BASE - sizeof (ulong) is usually a valid + * address. Better pick an address known to be invalid on + * your system and assign it to CONFIG_SYS_RESET_ADDRESS. + * "(ulong)-1" used to be a good choice for many systems... + */ + addr = CONFIG_SYS_MONITOR_BASE - sizeof (ulong); +#endif + ((void (*)(void)) addr) (); + return 1; + +} + +/* ------------------------------------------------------------------------- */ + +/* + * Get timebase clock frequency (like cpu_clk in Hz) + * This is the sys_logic_clk (memory bus) divided by 4 + */ +unsigned long get_tbclk (void) +{ + return ((get_bus_freq (0) + 2L) / 4L); +} + +/* ------------------------------------------------------------------------- */ + +/* + * The MPC824x has an integrated PCI controller known as the MPC107. + * The following are MPC107 Bridge Controller and PCI Support functions + * + */ + +/* + * This procedure reads a 32-bit address MPC107 register, and returns + * a 32 bit value. It swaps the address to little endian before + * writing it to config address, and swaps the value to big endian + * before returning to the caller. + */ +unsigned int mpc824x_mpc107_getreg (unsigned int regNum) +{ + unsigned int temp; + + /* swap the addr. to little endian */ + *(volatile unsigned int *) CHRP_REG_ADDR = PCISWAP (regNum); + temp = *(volatile unsigned int *) CHRP_REG_DATA; + return PCISWAP (temp); /* swap the data upon return */ +} + +/* + * This procedure writes a 32-bit address MPC107 register. It swaps + * the address to little endian before writing it to config address. + */ + +void mpc824x_mpc107_setreg (unsigned int regNum, unsigned int regVal) +{ + /* swap the addr. to little endian */ + *(volatile unsigned int *) CHRP_REG_ADDR = PCISWAP (regNum); + *(volatile unsigned int *) CHRP_REG_DATA = PCISWAP (regVal); + return; +} + + +/* + * Write a byte (8 bits) to a memory location. + */ +void mpc824x_mpc107_write8 (unsigned int addr, unsigned char data) +{ + *(unsigned char *) addr = data; + __asm__ ("sync"); +} + +/* + * Write a word (16 bits) to a memory location after the value + * has been byte swapped (big to little endian or vice versa) + */ + +void mpc824x_mpc107_write16 (unsigned int address, unsigned short data) +{ + *(volatile unsigned short *) address = BYTE_SWAP_16_BIT (data); + __asm__ ("sync"); +} + +/* + * Write a long word (32 bits) to a memory location after the value + * has been byte swapped (big to little endian or vice versa) + */ + +void mpc824x_mpc107_write32 (unsigned int address, unsigned int data) +{ + *(volatile unsigned int *) address = LONGSWAP (data); + __asm__ ("sync"); +} + +/* + * Read a byte (8 bits) from a memory location. + */ +unsigned char mpc824x_mpc107_read8 (unsigned int addr) +{ + return *(volatile unsigned char *) addr; +} + + +/* + * Read a word (16 bits) from a memory location, and byte swap the + * value before returning to the caller. + */ +unsigned short mpc824x_mpc107_read16 (unsigned int address) +{ + unsigned short retVal; + + retVal = BYTE_SWAP_16_BIT (*(unsigned short *) address); + return retVal; +} + + +/* + * Read a long word (32 bits) from a memory location, and byte + * swap the value before returning to the caller. + */ +unsigned int mpc824x_mpc107_read32 (unsigned int address) +{ + unsigned int retVal; + + retVal = LONGSWAP (*(unsigned int *) address); + return (retVal); +} + + +/* + * Read a register in the Embedded Utilities Memory Block address + * space. + * Input: regNum - register number + utility base address. Example, + * the base address of EPIC is 0x40000, the register number + * being passed is 0x40000+the address of the target register. + * (See epic.h for register addresses). + * Output: The 32 bit little endian value of the register. + */ + +unsigned int mpc824x_eummbar_read (unsigned int regNum) +{ + unsigned int temp; + + temp = *(volatile unsigned int *) (EUMBBAR_VAL + regNum); + temp = PCISWAP (temp); + return temp; +} + + +/* + * Write a value to a register in the Embedded Utilities Memory + * Block address space. + * Input: regNum - register number + utility base address. Example, + * the base address of EPIC is 0x40000, the register + * number is 0x40000+the address of the target register. + * (See epic.h for register addresses). + * regVal - value to be written to the register. + */ + +void mpc824x_eummbar_write (unsigned int regNum, unsigned int regVal) +{ + *(volatile unsigned int *) (EUMBBAR_VAL + regNum) = PCISWAP (regVal); + return; +} + +/* ------------------------------------------------------------------------- */ diff --git a/arch/powerpc/cpu/mpc824x/cpu_init.c b/arch/powerpc/cpu/mpc824x/cpu_init.c new file mode 100644 index 0000000..395f776 --- /dev/null +++ b/arch/powerpc/cpu/mpc824x/cpu_init.c @@ -0,0 +1,417 @@ +/* + * (C) Copyright 2000 + * Rob Taylor. Flying Pig Systems. robt@flyingpig.com. + * + * 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> +#include <asm/processor.h> +#include <mpc824x.h> + +#ifndef CONFIG_SYS_BANK0_ROW +#define CONFIG_SYS_BANK0_ROW 0 +#endif +#ifndef CONFIG_SYS_BANK1_ROW +#define CONFIG_SYS_BANK1_ROW 0 +#endif +#ifndef CONFIG_SYS_BANK2_ROW +#define CONFIG_SYS_BANK2_ROW 0 +#endif +#ifndef CONFIG_SYS_BANK3_ROW +#define CONFIG_SYS_BANK3_ROW 0 +#endif +#ifndef CONFIG_SYS_BANK4_ROW +#define CONFIG_SYS_BANK4_ROW 0 +#endif +#ifndef CONFIG_SYS_BANK5_ROW +#define CONFIG_SYS_BANK5_ROW 0 +#endif +#ifndef CONFIG_SYS_BANK6_ROW +#define CONFIG_SYS_BANK6_ROW 0 +#endif +#ifndef CONFIG_SYS_BANK7_ROW +#define CONFIG_SYS_BANK7_ROW 0 +#endif +#ifndef CONFIG_SYS_DBUS_SIZE2 +#define CONFIG_SYS_DBUS_SIZE2 0 +#endif + +/* + * Breath some life into the CPU... + * + * Set up the memory map, + * initialize a bunch of registers, + */ +void +cpu_init_f (void) +{ +/* MOUSSE board is initialized in asm */ +#if !defined(CONFIG_MOUSSE) && !defined(CONFIG_BMW) + register unsigned long val; + CONFIG_WRITE_HALFWORD(PCICR, 0x06); /* Bus Master, respond to PCI memory space acesses*/ +/* CONFIG_WRITE_HALFWORD(PCISR, 0xffff); */ /*reset PCISR*/ + +#if defined(CONFIG_MUSENKI) || defined(CONFIG_PN62) +/* Why is this here, you ask? Try, just try setting 0x8000 + * in PCIACR with CONFIG_WRITE_HALFWORD() + * this one was a stumper, and we are annoyed + */ + +#define M_CONFIG_WRITE_HALFWORD( addr, data ) \ + __asm__ __volatile__(" \ + stw %2,0(%0)\n \ + sync\n \ + sth %3,2(%1)\n \ + sync\n \ + " \ + : /* no output */ \ + : "r" (CONFIG_ADDR), "r" (CONFIG_DATA), \ + "r" (PCISWAP(addr & ~3)), "r" (PCISWAP(data << 16)) \ + ); + + M_CONFIG_WRITE_HALFWORD(PCIACR, 0x8000); +#endif + + CONFIG_WRITE_BYTE(PCLSR, 0x8); /* set PCI cache line size */ + CONFIG_WRITE_BYTE (PLTR, 0x40); /* set PCI latency timer */ + /* + * Note that although this bit is cleared after a hard reset, it + * must be explicitly set and then cleared by software during + * initialization in order to guarantee correct operation of the + * DLL and the SDRAM_CLK[0:3] signals (if they are used). + */ + CONFIG_READ_BYTE (AMBOR, val); + CONFIG_WRITE_BYTE(AMBOR, val & 0xDF); + CONFIG_WRITE_BYTE(AMBOR, val | 0x20); + CONFIG_WRITE_BYTE(AMBOR, val & 0xDF); +#ifdef CONFIG_MPC8245 + /* silicon bug 28 MPC8245 */ + CONFIG_READ_BYTE(AMBOR,val); + CONFIG_WRITE_BYTE(AMBOR,val|0x1); + +#if 0 + /* + * The following bug only affects older (XPC8245) processors. + * DMA transfers initiated by external devices get corrupted due + * to a hardware scheduling problem. + * + * The effect is: + * when transferring X words, the first 32 words are transferred + * OK, the next 3 x 32 words are 'old' data (from previous DMA) + * while the rest of the X words is xferred fine. + * + * Disabling 3 of the 4 32 word hardware buffers solves the problem + * with no significant performance loss. + */ + + CONFIG_READ_BYTE(PCMBCR,val); + /* in order not to corrupt data which is being read over the PCI bus + * with the PPC as slave, we need to reduce the number of PCMRBs to 1, + * 4.11 in the processor user manual + * */ + +#if 1 + CONFIG_WRITE_BYTE(PCMBCR,(val|0xC0)); /* 1 PCMRB */ +#else + CONFIG_WRITE_BYTE(PCMBCR,(val|0x80)); /* 2 PCMRBs */ + CONFIG_WRITE_BYTE(PCMBCR,(val|0x40)); /* 3 PCMRBs */ + /* default, 4 PCMRBs are used */ +#endif +#endif +#endif + + CONFIG_READ_WORD(PICR1, val); +#if defined(CONFIG_MPC8240) + CONFIG_WRITE_WORD( PICR1, + (val & (PICR1_ADDRESS_MAP | PICR1_RCS0)) | + PIRC1_MSK | PICR1_PROC_TYPE_603E | + PICR1_FLASH_WR_EN | PICR1_MCP_EN | + PICR1_CF_DPARK | PICR1_EN_PCS | + PICR1_CF_APARK ); +#elif defined(CONFIG_MPC8245) + CONFIG_WRITE_WORD( PICR1, + (val & (PICR1_RCS0)) | + PICR1_PROC_TYPE_603E | + PICR1_FLASH_WR_EN | PICR1_MCP_EN | + PICR1_CF_DPARK | PICR1_NO_BUSW_CK | + PICR1_DEC| PICR1_CF_APARK | 0x10); /* 8245 UM says bit 4 must be set */ +#else +#error Specific type of MPC824x must be defined (i.e. CONFIG_MPC8240) +#endif + + CONFIG_READ_WORD(PICR2, val); + val= val & ~ (PICR2_CF_SNOOP_WS_MASK | PICR2_CF_APHASE_WS_MASK); /*mask off waitstate bits*/ +#ifndef CONFIG_PN62 + val |= PICR2_CF_SNOOP_WS_1WS | PICR2_CF_APHASE_WS_1WS; /*1 wait state*/ +#endif + CONFIG_WRITE_WORD(PICR2, val); + + CONFIG_WRITE_WORD(EUMBBAR, CONFIG_SYS_EUMB_ADDR); +#ifndef CONFIG_SYS_RAMBOOT + CONFIG_WRITE_WORD(MCCR1, (CONFIG_SYS_ROMNAL << MCCR1_ROMNAL_SHIFT) | + (CONFIG_SYS_BANK0_ROW) | + (CONFIG_SYS_BANK1_ROW << MCCR1_BANK1ROW_SHIFT) | + (CONFIG_SYS_BANK2_ROW << MCCR1_BANK2ROW_SHIFT) | + (CONFIG_SYS_BANK3_ROW << MCCR1_BANK3ROW_SHIFT) | + (CONFIG_SYS_BANK4_ROW << MCCR1_BANK4ROW_SHIFT) | + (CONFIG_SYS_BANK5_ROW << MCCR1_BANK5ROW_SHIFT) | + (CONFIG_SYS_BANK6_ROW << MCCR1_BANK6ROW_SHIFT) | + (CONFIG_SYS_BANK7_ROW << MCCR1_BANK7ROW_SHIFT) | + (CONFIG_SYS_ROMFAL << MCCR1_ROMFAL_SHIFT)); +#endif + +#if defined(CONFIG_SYS_ASRISE) && defined(CONFIG_SYS_ASFALL) + CONFIG_WRITE_WORD(MCCR2, CONFIG_SYS_REFINT << MCCR2_REFINT_SHIFT | + CONFIG_SYS_ASRISE << MCCR2_ASRISE_SHIFT | + CONFIG_SYS_ASFALL << MCCR2_ASFALL_SHIFT); +#else + CONFIG_WRITE_WORD(MCCR2, CONFIG_SYS_REFINT << MCCR2_REFINT_SHIFT); +#endif + +#if defined(CONFIG_MPC8240) + CONFIG_WRITE_WORD(MCCR3, + (((CONFIG_SYS_BSTOPRE & 0x003c) >> 2) << MCCR3_BSTOPRE2TO5_SHIFT) | + (CONFIG_SYS_REFREC << MCCR3_REFREC_SHIFT) | + (CONFIG_SYS_RDLAT << MCCR3_RDLAT_SHIFT)); +#elif defined(CONFIG_MPC8245) + CONFIG_WRITE_WORD(MCCR3, + (((CONFIG_SYS_BSTOPRE & 0x003c) >> 2) << MCCR3_BSTOPRE2TO5_SHIFT) | + (CONFIG_SYS_REFREC << MCCR3_REFREC_SHIFT)); +#else +#error Specific type of MPC824x must be defined (i.e. CONFIG_MPC8240) +#endif + +/* this is gross. We think these should all be the same, and various boards + * should define CONFIG_SYS_ACTORW to 0 if they don't want to set it, or even, if + * its not set, we define it to zero in this file + */ +#if defined(CONFIG_CU824) || defined(CONFIG_PN62) + CONFIG_WRITE_WORD(MCCR4, + (CONFIG_SYS_PRETOACT << MCCR4_PRETOACT_SHIFT) | + (CONFIG_SYS_ACTTOPRE << MCCR4_ACTTOPRE_SHIFT) | + MCCR4_BIT21 | + (CONFIG_SYS_REGISTERD_TYPE_BUFFER ? MCCR4_REGISTERED: 0) | + ((CONFIG_SYS_BSTOPRE & 0x0003) <<MCCR4_BSTOPRE0TO1_SHIFT ) | + (((CONFIG_SYS_SDMODE_CAS_LAT <<4) | (CONFIG_SYS_SDMODE_WRAP <<3) | + CONFIG_SYS_SDMODE_BURSTLEN) << MCCR4_SDMODE_SHIFT) | + (CONFIG_SYS_ACTORW << MCCR4_ACTTORW_SHIFT) | + (((CONFIG_SYS_BSTOPRE & 0x03c0) >> 6) << MCCR4_BSTOPRE6TO9_SHIFT)); +#elif defined(CONFIG_MPC8240) + CONFIG_WRITE_WORD(MCCR4, + (CONFIG_SYS_PRETOACT << MCCR4_PRETOACT_SHIFT) | + (CONFIG_SYS_ACTTOPRE << MCCR4_ACTTOPRE_SHIFT) | + MCCR4_BIT21 | + (CONFIG_SYS_REGISTERD_TYPE_BUFFER ? MCCR4_REGISTERED: 0) | + ((CONFIG_SYS_BSTOPRE & 0x0003) <<MCCR4_BSTOPRE0TO1_SHIFT ) | + (((CONFIG_SYS_SDMODE_CAS_LAT <<4) | (CONFIG_SYS_SDMODE_WRAP <<3) | + (CONFIG_SYS_SDMODE_BURSTLEN)) <<MCCR4_SDMODE_SHIFT) | + (((CONFIG_SYS_BSTOPRE & 0x03c0) >> 6) <<MCCR4_BSTOPRE6TO9_SHIFT )); +#elif defined(CONFIG_MPC8245) + CONFIG_READ_WORD(MCCR1, val); + val &= MCCR1_DBUS_SIZE0; /* test for 64-bit mem bus */ + + CONFIG_WRITE_WORD(MCCR4, + (CONFIG_SYS_PRETOACT << MCCR4_PRETOACT_SHIFT) | + (CONFIG_SYS_ACTTOPRE << MCCR4_ACTTOPRE_SHIFT) | + (CONFIG_SYS_EXTROM ? MCCR4_EXTROM : 0) | + (CONFIG_SYS_REGDIMM ? MCCR4_REGDIMM : 0) | + (CONFIG_SYS_REGISTERD_TYPE_BUFFER ? MCCR4_REGISTERED: 0) | + ((CONFIG_SYS_BSTOPRE & 0x0003) <<MCCR4_BSTOPRE0TO1_SHIFT ) | + (CONFIG_SYS_DBUS_SIZE2 << MCCR4_DBUS_SIZE2_SHIFT) | + (((CONFIG_SYS_SDMODE_CAS_LAT <<4) | (CONFIG_SYS_SDMODE_WRAP <<3) | + (val ? 2 : 3)) << MCCR4_SDMODE_SHIFT) | + (CONFIG_SYS_ACTORW << MCCR4_ACTTORW_SHIFT) | + (((CONFIG_SYS_BSTOPRE & 0x03c0) >> 6) <<MCCR4_BSTOPRE6TO9_SHIFT )); +#else +#error Specific type of MPC824x must be defined (i.e. CONFIG_MPC8240) +#endif + + CONFIG_WRITE_WORD(MSAR1, + ( (CONFIG_SYS_BANK0_START & MICR_ADDR_MASK) >> MICR_ADDR_SHIFT) | + (((CONFIG_SYS_BANK1_START & MICR_ADDR_MASK) >> MICR_ADDR_SHIFT) << 8) | + (((CONFIG_SYS_BANK2_START & MICR_ADDR_MASK) >> MICR_ADDR_SHIFT) << 16) | + (((CONFIG_SYS_BANK3_START & MICR_ADDR_MASK) >> MICR_ADDR_SHIFT) << 24)); + CONFIG_WRITE_WORD(EMSAR1, + ( (CONFIG_SYS_BANK0_START & MICR_EADDR_MASK) >> MICR_EADDR_SHIFT) | + (((CONFIG_SYS_BANK1_START & MICR_EADDR_MASK) >> MICR_EADDR_SHIFT) << 8) | + (((CONFIG_SYS_BANK2_START & MICR_EADDR_MASK) >> MICR_EADDR_SHIFT) << 16) | + (((CONFIG_SYS_BANK3_START & MICR_EADDR_MASK) >> MICR_EADDR_SHIFT) << 24)); + CONFIG_WRITE_WORD(MSAR2, + ( (CONFIG_SYS_BANK4_START & MICR_ADDR_MASK) >> MICR_ADDR_SHIFT) | + (((CONFIG_SYS_BANK5_START & MICR_ADDR_MASK) >> MICR_ADDR_SHIFT) << 8) | + (((CONFIG_SYS_BANK6_START & MICR_ADDR_MASK) >> MICR_ADDR_SHIFT) << 16) | + (((CONFIG_SYS_BANK7_START & MICR_ADDR_MASK) >> MICR_ADDR_SHIFT) << 24)); + CONFIG_WRITE_WORD(EMSAR2, + ( (CONFIG_SYS_BANK4_START & MICR_EADDR_MASK) >> MICR_EADDR_SHIFT) | + (((CONFIG_SYS_BANK5_START & MICR_EADDR_MASK) >> MICR_EADDR_SHIFT) << 8) | + (((CONFIG_SYS_BANK6_START & MICR_EADDR_MASK) >> MICR_EADDR_SHIFT) << 16) | + (((CONFIG_SYS_BANK7_START & MICR_EADDR_MASK) >> MICR_EADDR_SHIFT) << 24)); + CONFIG_WRITE_WORD(MEAR1, + ( (CONFIG_SYS_BANK0_END & MICR_ADDR_MASK) >> MICR_ADDR_SHIFT) | + (((CONFIG_SYS_BANK1_END & MICR_ADDR_MASK) >> MICR_ADDR_SHIFT) << 8) | + (((CONFIG_SYS_BANK2_END & MICR_ADDR_MASK) >> MICR_ADDR_SHIFT) << 16) | + (((CONFIG_SYS_BANK3_END & MICR_ADDR_MASK) >> MICR_ADDR_SHIFT) << 24)); + CONFIG_WRITE_WORD(EMEAR1, + ( (CONFIG_SYS_BANK0_END & MICR_EADDR_MASK) >> MICR_EADDR_SHIFT) | + (((CONFIG_SYS_BANK1_END & MICR_EADDR_MASK) >> MICR_EADDR_SHIFT) << 8) | + (((CONFIG_SYS_BANK2_END & MICR_EADDR_MASK) >> MICR_EADDR_SHIFT) << 16) | + (((CONFIG_SYS_BANK3_END & MICR_EADDR_MASK) >> MICR_EADDR_SHIFT) << 24)); + CONFIG_WRITE_WORD(MEAR2, + ( (CONFIG_SYS_BANK4_END & MICR_ADDR_MASK) >> MICR_ADDR_SHIFT) | + (((CONFIG_SYS_BANK5_END & MICR_ADDR_MASK) >> MICR_ADDR_SHIFT) << 8) | + (((CONFIG_SYS_BANK6_END & MICR_ADDR_MASK) >> MICR_ADDR_SHIFT) << 16) | + (((CONFIG_SYS_BANK7_END & MICR_ADDR_MASK) >> MICR_ADDR_SHIFT) << 24)); + CONFIG_WRITE_WORD(EMEAR2, + ( (CONFIG_SYS_BANK4_END & MICR_EADDR_MASK) >> MICR_EADDR_SHIFT) | + (((CONFIG_SYS_BANK5_END & MICR_EADDR_MASK) >> MICR_EADDR_SHIFT) << 8) | + (((CONFIG_SYS_BANK6_END & MICR_EADDR_MASK) >> MICR_EADDR_SHIFT) << 16) | + (((CONFIG_SYS_BANK7_END & MICR_EADDR_MASK) >> MICR_EADDR_SHIFT) << 24)); + + CONFIG_WRITE_BYTE(ODCR, CONFIG_SYS_ODCR); +#ifdef CONFIG_SYS_DLL_MAX_DELAY + CONFIG_WRITE_BYTE(MIOCR1, CONFIG_SYS_DLL_MAX_DELAY); /* needed to make DLL lock */ +#endif +#if defined(CONFIG_SYS_DLL_EXTEND) && defined(CONFIG_SYS_PCI_HOLD_DEL) + CONFIG_WRITE_BYTE(PMCR2, CONFIG_SYS_DLL_EXTEND | CONFIG_SYS_PCI_HOLD_DEL); +#endif +#if defined(MIOCR2) && defined(CONFIG_SYS_SDRAM_DSCD) + CONFIG_WRITE_BYTE(MIOCR2, CONFIG_SYS_SDRAM_DSCD); /* change memory input */ +#endif /* setup & hold time */ + + CONFIG_WRITE_BYTE(MBER, + CONFIG_SYS_BANK0_ENABLE | + (CONFIG_SYS_BANK1_ENABLE << 1) | + (CONFIG_SYS_BANK2_ENABLE << 2) | + (CONFIG_SYS_BANK3_ENABLE << 3) | + (CONFIG_SYS_BANK4_ENABLE << 4) | + (CONFIG_SYS_BANK5_ENABLE << 5) | + (CONFIG_SYS_BANK6_ENABLE << 6) | + (CONFIG_SYS_BANK7_ENABLE << 7)); + +#ifdef CONFIG_SYS_PGMAX + CONFIG_WRITE_BYTE(MPMR, CONFIG_SYS_PGMAX); +#endif + + /* ! Wait 200us before initialize other registers */ + /*FIXME: write a decent udelay wait */ + __asm__ __volatile__( + " mtctr %0 \n \ + 0: bdnz 0b\n" + : + : "r" (0x10000)); + + CONFIG_READ_WORD(MCCR1, val); + CONFIG_WRITE_WORD(MCCR1, val | MCCR1_MEMGO); /* set memory access going */ + __asm__ __volatile__("eieio"); + +#endif /* !CONFIG_MOUSSE && !CONFIG_BMW */ +} + + +#ifdef CONFIG_MOUSSE +#ifdef INCLUDE_MPC107_REPORT +struct MPC107_s { + unsigned int iobase; + char desc[120]; +} MPC107Regs[] = { + { BMC_BASE + 0x00, "MPC107 Vendor/Device ID" }, + { BMC_BASE + 0x04, "MPC107 PCI Command/Status Register" }, + { BMC_BASE + 0x08, "MPC107 Revision" }, + { BMC_BASE + 0x0C, "MPC107 Cache Line Size" }, + { BMC_BASE + 0x10, "MPC107 LMBAR" }, + { BMC_BASE + 0x14, "MPC824x PCSR" }, + { BMC_BASE + 0xA8, "MPC824x PICR1" }, + { BMC_BASE + 0xAC, "MPC824x PICR2" }, + { BMC_BASE + 0x46, "MPC824x PACR" }, + { BMC_BASE + 0x310, "MPC824x ITWR" }, + { BMC_BASE + 0x300, "MPC824x OMBAR" }, + { BMC_BASE + 0x308, "MPC824x OTWR" }, + { BMC_BASE + 0x14, "MPC107 Peripheral Control and Status Register" }, + { BMC_BASE + 0x78, "MPC107 EUMBAR" }, + { BMC_BASE + 0xC0, "MPC107 Processor Bus Error Status" }, + { BMC_BASE + 0xC4, "MPC107 PCI Bus Error Status" }, + { BMC_BASE + 0xC8, "MPC107 Processor/PCI Error Address" }, + { BMC_BASE + 0xE0, "MPC107 AMBOR Register" }, + { BMC_BASE + 0xF0, "MPC107 MCCR1 Register" }, + { BMC_BASE + 0xF4, "MPC107 MCCR2 Register" }, + { BMC_BASE + 0xF8, "MPC107 MCCR3 Register" }, + { BMC_BASE + 0xFC, "MPC107 MCCR4 Register" }, +}; +#define N_MPC107_Regs (sizeof(MPC107Regs)/sizeof(MPC107Regs[0])) +#endif /* INCLUDE_MPC107_REPORT */ +#endif /* CONFIG_MOUSSE */ + +/* + * initialize higher level parts of CPU like time base and timers + */ +int cpu_init_r (void) +{ +#ifdef CONFIG_MOUSSE +#ifdef INCLUDE_MPC107_REPORT + unsigned int tmp = 0, i; +#endif + /* + * Initialize the EUMBBAR (Embedded Util Mem Block Base Addr Reg). + * This is necessary before the EPIC, DMA ctlr, I2C ctlr, etc. can + * be accessed. + */ + +#ifdef CONFIG_MPC8240 /* only on MPC8240 */ + mpc824x_mpc107_setreg (EUMBBAR, EUMBBAR_VAL); + /* MOT/SPS: Issue #10002, PCI (FD Alias enable) */ + mpc824x_mpc107_setreg (AMBOR, 0x000000C0); +#endif + + +#ifdef INCLUDE_MPC107_REPORT + /* Check MPC824x PCI Device and Vendor ID */ + while ((tmp = mpc824x_mpc107_getreg (BMC_BASE)) != 0x31057) { + printf (" MPC107: offset=0x%x, val = 0x%x\n", + BMC_BASE, + tmp); + } + + for (i = 0; i < N_MPC107_Regs; i++) { + printf (" 0x%x/%s = 0x%x\n", + MPC107Regs[i].iobase, + MPC107Regs[i].desc, + mpc824x_mpc107_getreg (MPC107Regs[i].iobase)); + } + + printf ("IBAT0L = 0x%08X\n", mfspr (IBAT0L)); + printf ("IBAT0U = 0x%08X\n", mfspr (IBAT0U)); + printf ("IBAT1L = 0x%08X\n", mfspr (IBAT1L)); + printf ("IBAT1U = 0x%08X\n", mfspr (IBAT1U)); + printf ("IBAT2L = 0x%08X\n", mfspr (IBAT2L)); + printf ("IBAT2U = 0x%08X\n", mfspr (IBAT2U)); + printf ("IBAT3L = 0x%08X\n", mfspr (IBAT3L)); + printf ("IBAT3U = 0x%08X\n", mfspr (IBAT3U)); + printf ("DBAT0L = 0x%08X\n", mfspr (DBAT0L)); + printf ("DBAT0U = 0x%08X\n", mfspr (DBAT0U)); + printf ("DBAT1L = 0x%08X\n", mfspr (DBAT1L)); + printf ("DBAT1U = 0x%08X\n", mfspr (DBAT1U)); + printf ("DBAT2L = 0x%08X\n", mfspr (DBAT2L)); + printf ("DBAT2U = 0x%08X\n", mfspr (DBAT2U)); + printf ("DBAT3L = 0x%08X\n", mfspr (DBAT3L)); + printf ("DBAT3U = 0x%08X\n", mfspr (DBAT3U)); +#endif /* INCLUDE_MPC107_REPORT */ +#endif /* CONFIG_MOUSSE */ + return (0); +} diff --git a/arch/powerpc/cpu/mpc824x/drivers/epic.h b/arch/powerpc/cpu/mpc824x/drivers/epic.h new file mode 100644 index 0000000..2803f63 --- /dev/null +++ b/arch/powerpc/cpu/mpc824x/drivers/epic.h @@ -0,0 +1 @@ +#include "epic/epic.h" diff --git a/arch/powerpc/cpu/mpc824x/drivers/epic/README b/arch/powerpc/cpu/mpc824x/drivers/epic/README new file mode 100644 index 0000000..5798996 --- /dev/null +++ b/arch/powerpc/cpu/mpc824x/drivers/epic/README @@ -0,0 +1,102 @@ +CONTENT: + + epic.h + epic1.c + epic2.s + +WHAT ARE THESE FILES: + +These files contain MPC8240 (Kahlua) EPIC +driver routines. The driver routines are not +written for any specific operating system. +They serves the purpose of code sample, and +jump-start for using the MPC8240 EPIC unit. + +For the reason of correctness of C language +syntax, these files are compiled by Metaware +C compiler and assembler. + +ENDIAN NOTATION: + +The algorithm is designed for big-endian mode, +software is responsible for byte swapping. + +USAGE: + +1. The host system that is running on MPC8240 + shall link the files listed here. The memory + location of driver routines shall take into + account of that driver routines need to run + in supervisor mode and they process external + interrupts. + + The routine epic_exception shall be called by + exception vector at location 0x500, i.e., + 603e core external exception vector. + +2. The host system is responsible for configuring + the MPC8240 including Embedded Utilities Memory + Block. All EPIC driver functions require the + content of Embedded Utilities Memory Block + Base Address Register, EUMBBAR, as the first + parameter. + +3. Before EPIC unit of MPC8240 can be used, + initialize EPIC unit by calling epicInit + with the corresponding parameters. + + The initialization shall disable the 603e + core External Exception by calling CoreExtIntDisable( ). + Next, call epicInit( ). Last, enable the 603e core + External Exception by calling CoreExtIntEnable( ). + +4. After EPIC unit has been successfully initialized, + epicIntSourceSet( ) shall be used to register each + external interrupt source. Anytime, an external + interrupt source can be disabled or enabled by + calling corresponding function, epicIntDisable( ), + or epicIntEnable( ). + + Global Timers' resource, base count and frequency, + can be changed by calling epicTmFrequencySet( ) + and epicTmBaseSet( ). + + To stop counting a specific global timer, use + the function, epicTmInhibit while epicTmEnable + can be used to start counting a timer. + +5. To mask a set of external interrupts that are + are certain level below, epicIntPrioritySet( ) + can be used. For example, if the processor's + current task priority register is set to 0x7, + only interrupts of priority 0x8 or higher will + be passed to the processor. + + Be careful when using this function. It may + corrupt the current interrupt pending, selector, + and request registers, resulting an invalid vetor. + + After enabling an interrupt, disable it may also + cause an invalid vector. User may consider using + the spurious vector interrupt service routine to + handle this case. + +6. The EPIC driver routines contains a set + of utilities, Set and Get, for host system + to query and modify the desired EPIC source + registers. + +7. Each external interrupt source shall register + its interrupt service routine. The routine + shall contain all interrupt source specific + processes and keep as short as possible. + + Special customized end of interrupt routine + is optional. If it is needed, it shall contain + the external interrupt source specific end of + interrupt process. + + External interrupt exception vector at 0x500 + shall always call the epicEOI just before + rfi instruction. Refer to the routine, + epic_exception, for a code sample. diff --git a/arch/powerpc/cpu/mpc824x/drivers/epic/epic.h b/arch/powerpc/cpu/mpc824x/drivers/epic/epic.h new file mode 100644 index 0000000..58f81c5 --- /dev/null +++ b/arch/powerpc/cpu/mpc824x/drivers/epic/epic.h @@ -0,0 +1,163 @@ +/********************************************************************* + * mpc8240epic.h - EPIC module of the MPC8240 micro-controller + * + * Copyrigh 1999 Motorola Inc. + * + * Modification History: + * ===================== + * 01a,04Feb99,My Created. + * 15Nov200, robt -modified to use in U-Boot + * +*/ + +#ifndef __INCEPICh +#define __INCEPICh + +#define ULONG unsigned long +#define MAXVEC 20 +#define MAXIRQ 5 /* IRQs */ +#define EPIC_DIRECT_IRQ 0 /* Direct interrupt type */ + +/* EPIC register addresses */ + +#define EPIC_EUMBBAR 0x40000 /* EUMBBAR of EPIC */ +#define EPIC_FEATURES_REG (EPIC_EUMBBAR + 0x01000)/* Feature reporting */ +#define EPIC_GLOBAL_REG (EPIC_EUMBBAR + 0x01020)/* Global config. */ +#define EPIC_INT_CONF_REG (EPIC_EUMBBAR + 0x01030)/* Interrupt config. */ +#define EPIC_VENDOR_ID_REG (EPIC_EUMBBAR + 0x01080)/* Vendor id */ +#define EPIC_PROC_INIT_REG (EPIC_EUMBBAR + 0x01090)/* Processor init. */ +#define EPIC_SPUR_VEC_REG (EPIC_EUMBBAR + 0x010e0)/* Spurious vector */ +#define EPIC_TM_FREQ_REG (EPIC_EUMBBAR + 0x010f0)/* Timer Frequency */ + +#define EPIC_TM0_CUR_COUNT_REG (EPIC_EUMBBAR + 0x01100)/* Gbl TM0 Cur. Count*/ +#define EPIC_TM0_BASE_COUNT_REG (EPIC_EUMBBAR + 0x01110)/* Gbl TM0 Base Count*/ +#define EPIC_TM0_VEC_REG (EPIC_EUMBBAR + 0x01120)/* Gbl TM0 Vector Pri*/ +#define EPIC_TM0_DES_REG (EPIC_EUMBBAR + 0x01130)/* Gbl TM0 Dest. */ + +#define EPIC_TM1_CUR_COUNT_REG (EPIC_EUMBBAR + 0x01140)/* Gbl TM1 Cur. Count*/ +#define EPIC_TM1_BASE_COUNT_REG (EPIC_EUMBBAR + 0x01150)/* Gbl TM1 Base Count*/ +#define EPIC_TM1_VEC_REG (EPIC_EUMBBAR + 0x01160)/* Gbl TM1 Vector Pri*/ +#define EPIC_TM1_DES_REG (EPIC_EUMBBAR + 0x01170)/* Gbl TM1 Dest. */ + +#define EPIC_TM2_CUR_COUNT_REG (EPIC_EUMBBAR + 0x01180)/* Gbl TM2 Cur. Count*/ +#define EPIC_TM2_BASE_COUNT_REG (EPIC_EUMBBAR + 0x01190)/* Gbl TM2 Base Count*/ +#define EPIC_TM2_VEC_REG (EPIC_EUMBBAR + 0x011a0)/* Gbl TM2 Vector Pri*/ +#define EPIC_TM2_DES_REG (EPIC_EUMBBAR + 0x011b0)/* Gbl TM2 Dest */ + +#define EPIC_TM3_CUR_COUNT_REG (EPIC_EUMBBAR + 0x011c0)/* Gbl TM3 Cur. Count*/ +#define EPIC_TM3_BASE_COUNT_REG (EPIC_EUMBBAR + 0x011d0)/* Gbl TM3 Base Count*/ +#define EPIC_TM3_VEC_REG (EPIC_EUMBBAR + 0x011e0)/* Gbl TM3 Vector Pri*/ +#define EPIC_TM3_DES_REG (EPIC_EUMBBAR + 0x011f0)/* Gbl TM3 Dest. */ + +#define EPIC_EX_INT0_VEC_REG (EPIC_EUMBBAR + 0x10200)/* Ext. Int. Sr0 Des */ +#define EPIC_EX_INT0_DES_REG (EPIC_EUMBBAR + 0x10210)/* Ext. Int. Sr0 Vect*/ +#define EPIC_EX_INT1_VEC_REG (EPIC_EUMBBAR + 0x10220)/* Ext. Int. Sr1 Des */ +#define EPIC_EX_INT1_DES_REG (EPIC_EUMBBAR + 0x10230)/* Ext. Int. Sr1 Vect*/ +#define EPIC_EX_INT2_VEC_REG (EPIC_EUMBBAR + 0x10240)/* Ext. Int. Sr2 Des */ +#define EPIC_EX_INT2_DES_REG (EPIC_EUMBBAR + 0x10250)/* Ext. Int. Sr2 Vect*/ +#define EPIC_EX_INT3_VEC_REG (EPIC_EUMBBAR + 0x10260)/* Ext. Int. Sr3 Des */ +#define EPIC_EX_INT3_DES_REG (EPIC_EUMBBAR + 0x10270)/* Ext. Int. Sr3 Vect*/ +#define EPIC_EX_INT4_VEC_REG (EPIC_EUMBBAR + 0x10280)/* Ext. Int. Sr4 Des */ +#define EPIC_EX_INT4_DES_REG (EPIC_EUMBBAR + 0x10290)/* Ext. Int. Sr4 Vect*/ + +#define EPIC_SR_INT0_VEC_REG (EPIC_EUMBBAR + 0x10200)/* Sr. Int. Sr0 Des */ +#define EPIC_SR_INT0_DES_REG (EPIC_EUMBBAR + 0x10210)/* Sr. Int. Sr0 Vect */ +#define EPIC_SR_INT1_VEC_REG (EPIC_EUMBBAR + 0x10220)/* Sr. Int. Sr1 Des */ +#define EPIC_SR_INT1_DES_REG (EPIC_EUMBBAR + 0x10230)/* Sr. Int. Sr1 Vect.*/ +#define EPIC_SR_INT2_VEC_REG (EPIC_EUMBBAR + 0x10240)/* Sr. Int. Sr2 Des */ +#define EPIC_SR_INT2_DES_REG (EPIC_EUMBBAR + 0x10250)/* Sr. Int. Sr2 Vect.*/ +#define EPIC_SR_INT3_VEC_REG (EPIC_EUMBBAR + 0x10260)/* Sr. Int. Sr3 Des */ +#define EPIC_SR_INT3_DES_REG (EPIC_EUMBBAR + 0x10270)/* Sr. Int. Sr3 Vect.*/ +#define EPIC_SR_INT4_VEC_REG (EPIC_EUMBBAR + 0x10280)/* Sr. Int. Sr4 Des */ +#define EPIC_SR_INT4_DES_REG (EPIC_EUMBBAR + 0x10290)/* Sr. Int. Sr4 Vect.*/ + +#define EPIC_SR_INT5_VEC_REG (EPIC_EUMBBAR + 0x102a0)/* Sr. Int. Sr5 Des */ +#define EPIC_SR_INT5_DES_REG (EPIC_EUMBBAR + 0x102b0)/* Sr. Int. Sr5 Vect.*/ +#define EPIC_SR_INT6_VEC_REG (EPIC_EUMBBAR + 0x102c0)/* Sr. Int. Sr6 Des */ +#define EPIC_SR_INT6_DES_REG (EPIC_EUMBBAR + 0x102d0)/* Sr. Int. Sr6 Vect.*/ +#define EPIC_SR_INT7_VEC_REG (EPIC_EUMBBAR + 0x102e0)/* Sr. Int. Sr7 Des */ +#define EPIC_SR_INT7_DES_REG (EPIC_EUMBBAR + 0x102f0)/* Sr. Int. Sr7 Vect.*/ +#define EPIC_SR_INT8_VEC_REG (EPIC_EUMBBAR + 0x10300)/* Sr. Int. Sr8 Des */ +#define EPIC_SR_INT8_DES_REG (EPIC_EUMBBAR + 0x10310)/* Sr. Int. Sr8 Vect.*/ +#define EPIC_SR_INT9_VEC_REG (EPIC_EUMBBAR + 0x10320)/* Sr. Int. Sr9 Des */ +#define EPIC_SR_INT9_DES_REG (EPIC_EUMBBAR + 0x10330)/* Sr. Int. Sr9 Vect.*/ + +#define EPIC_SR_INT10_VEC_REG (EPIC_EUMBBAR + 0x10340)/* Sr. Int. Sr10 Des */ +#define EPIC_SR_INT10_DES_REG (EPIC_EUMBBAR + 0x10350)/* Sr. Int. Sr10 Vect*/ +#define EPIC_SR_INT11_VEC_REG (EPIC_EUMBBAR + 0x10360)/* Sr. Int. Sr11 Des */ +#define EPIC_SR_INT11_DES_REG (EPIC_EUMBBAR + 0x10370)/* Sr. Int. Sr11 Vect*/ +#define EPIC_SR_INT12_VEC_REG (EPIC_EUMBBAR + 0x10380)/* Sr. Int. Sr12 Des */ +#define EPIC_SR_INT12_DES_REG (EPIC_EUMBBAR + 0x10390)/* Sr. Int. Sr12 Vect*/ +#define EPIC_SR_INT13_VEC_REG (EPIC_EUMBBAR + 0x103a0)/* Sr. Int. Sr13 Des */ +#define EPIC_SR_INT13_DES_REG (EPIC_EUMBBAR + 0x103b0)/* Sr. Int. Sr13 Vect*/ +#define EPIC_SR_INT14_VEC_REG (EPIC_EUMBBAR + 0x103c0)/* Sr. Int. Sr14 Des */ +#define EPIC_SR_INT14_DES_REG (EPIC_EUMBBAR + 0x103d0)/* Sr. Int. Sr14 Vect*/ +#define EPIC_SR_INT15_VEC_REG (EPIC_EUMBBAR + 0x103e0)/* Sr. Int. Sr15 Des */ +#define EPIC_SR_INT15_DES_REG (EPIC_EUMBBAR + 0x103f0)/* Sr. Int. Sr15 Vect*/ + +#define EPIC_I2C_INT_VEC_REG (EPIC_EUMBBAR + 0x11020)/* I2C Int. Vect Pri.*/ +#define EPIC_I2C_INT_DES_REG (EPIC_EUMBBAR + 0x11030)/* I2C Int. Dest */ +#define EPIC_DMA0_INT_VEC_REG (EPIC_EUMBBAR + 0x11040)/* DMA0 Int. Vect Pri*/ +#define EPIC_DMA0_INT_DES_REG (EPIC_EUMBBAR + 0x11050)/* DMA0 Int. Dest */ +#define EPIC_DMA1_INT_VEC_REG (EPIC_EUMBBAR + 0x11060)/* DMA1 Int. Vect Pri*/ +#define EPIC_DMA1_INT_DES_REG (EPIC_EUMBBAR + 0x11070)/* DMA1 Int. Dest */ +#define EPIC_MSG_INT_VEC_REG (EPIC_EUMBBAR + 0x110c0)/* Msg Int. Vect Pri*/ +#define EPIC_MSG_INT_DES_REG (EPIC_EUMBBAR + 0x110d0)/* Msg Int. Dest */ + +#define EPIC_PROC_CTASK_PRI_REG (EPIC_EUMBBAR + 0x20080)/* Proc. current task*/ +#define EPIC_PROC_INT_ACK_REG (EPIC_EUMBBAR + 0x200a0)/* Int. acknowledge */ +#define EPIC_PROC_EOI_REG (EPIC_EUMBBAR + 0x200b0)/* End of interrupt */ + +#define EPIC_VEC_PRI_MASK 0x80000000 /* Mask Interrupt bit in IVPR */ +#define EPIC_VEC_PRI_DFLT_PRI 8 /* Interrupt Priority in IVPR */ + +/* Error code */ + +#define OK 0 +#define ERROR -1 + +/* function prototypes */ + +void epicVendorId( unsigned int *step, + unsigned int *devId, + unsigned int *venId + ); +void epicFeatures( unsigned int *noIRQs, + unsigned int *noCPUs, + unsigned int *VerId ); +extern void epicInit( unsigned int IRQType, unsigned int clkRatio); +ULONG sysEUMBBARRead ( ULONG regNum ); +void sysEUMBBARWrite ( ULONG regNum, ULONG regVal); +extern void epicTmFrequencySet( unsigned int frq ); +extern unsigned int epicTmFrequencyGet(void); +extern unsigned int epicTmBaseSet( ULONG srcAddr, + unsigned int cnt, + unsigned int inhibit ); +extern unsigned int epicTmBaseGet ( ULONG srcAddr, unsigned int *val ); +extern unsigned int epicTmCountGet( ULONG srcAddr, unsigned int *val ); +extern unsigned int epicTmInhibit( unsigned int timer ); +extern unsigned int epicTmEnable( ULONG srcAdr ); +extern void CoreExtIntEnable(void); /* Enable 603e external interrupts */ +extern void CoreExtIntDisable(void); /* Disable 603e external interrupts */ +extern unsigned char epicIntTaskGet(void); +extern void epicIntTaskSet( unsigned char val ); +extern unsigned int epicIntAck(void); +extern void epicSprSet( unsigned int eumbbar, unsigned char ); +extern void epicConfigGet( unsigned int *clkRatio, + unsigned int *serEnable ); +extern void SrcVecTableInit(void); +extern unsigned int epicModeGet(void); +extern void epicIntEnable(int Vect); +extern void epicIntDisable(int Vect); +extern int epicIntSourceConfig(int Vect, int Polarity, int Sense, int Prio); +extern unsigned int epicIntAck(void); +extern void epicEOI(void); +extern int epicCurTaskPrioSet(int Vect); + +struct SrcVecTable + { + ULONG srcAddr; + char srcName[40]; + }; + +#endif /* EPIC_H */ diff --git a/arch/powerpc/cpu/mpc824x/drivers/epic/epic1.c b/arch/powerpc/cpu/mpc824x/drivers/epic/epic1.c new file mode 100644 index 0000000..ecbb42d --- /dev/null +++ b/arch/powerpc/cpu/mpc824x/drivers/epic/epic1.c @@ -0,0 +1,517 @@ +/************************************************** + * + * copyright @ motorola, 1999 + * + *************************************************/ +#include <mpc824x.h> +#include <common.h> +#include "epic.h" + + +#define PRINT(format, args...) printf(format , ## args) + +typedef void (*VOIDFUNCPTR) (void); /* ptr to function returning void */ +struct SrcVecTable SrcVecTable[MAXVEC] = /* Addr/Vector cross-reference tbl */ + { + { EPIC_EX_INT0_VEC_REG, "External Direct/Serial Source 0"}, + { EPIC_EX_INT1_VEC_REG, "External Direct/Serial Source 1"}, + { EPIC_EX_INT2_VEC_REG, "External Direct/Serial Source 2"}, + { EPIC_EX_INT3_VEC_REG, "External Direct/Serial Source 3"}, + { EPIC_EX_INT4_VEC_REG, "External Direct/Serial Source 4"}, + + { EPIC_SR_INT5_VEC_REG, "External Serial Source 5"}, + { EPIC_SR_INT6_VEC_REG, "External Serial Source 6"}, + { EPIC_SR_INT7_VEC_REG, "External Serial Source 7"}, + { EPIC_SR_INT8_VEC_REG, "External Serial Source 8"}, + { EPIC_SR_INT9_VEC_REG, "External Serial Source 9"}, + { EPIC_SR_INT10_VEC_REG, "External Serial Source 10"}, + { EPIC_SR_INT11_VEC_REG, "External Serial Source 11"}, + { EPIC_SR_INT12_VEC_REG, "External Serial Source 12"}, + { EPIC_SR_INT13_VEC_REG, "External Serial Source 13"}, + { EPIC_SR_INT14_VEC_REG, "External Serial Source 14"}, + { EPIC_SR_INT15_VEC_REG, "External Serial Source 15"}, + + { EPIC_I2C_INT_VEC_REG, "Internal I2C Source"}, + { EPIC_DMA0_INT_VEC_REG, "Internal DMA0 Source"}, + { EPIC_DMA1_INT_VEC_REG, "Internal DMA1 Source"}, + { EPIC_MSG_INT_VEC_REG, "Internal Message Source"}, + }; + +VOIDFUNCPTR intVecTbl[MAXVEC]; /* Interrupt vector table */ + + +/**************************************************************************** +* epicInit - Initialize the EPIC registers +* +* This routine resets the Global Configuration Register, thus it: +* - Disables all interrupts +* - Sets epic registers to reset values +* - Sets the value of the Processor Current Task Priority to the +* highest priority (0xF). +* epicInit then sets the EPIC operation mode to Mixed Mode (vs. Pass +* Through or 8259 compatible mode). +* +* If IRQType (input) is Direct IRQs: +* - IRQType is written to the SIE bit of the EPIC Interrupt +* Configuration register (ICR). +* - clkRatio is ignored. +* If IRQType is Serial IRQs: +* - both IRQType and clkRatio will be written to the ICR register +*/ + +void epicInit + ( + unsigned int IRQType, /* Direct or Serial */ + unsigned int clkRatio /* Clk Ratio for Serial IRQs */ + ) + { + ULONG tmp; + + tmp = sysEUMBBARRead(EPIC_GLOBAL_REG); + tmp |= 0xa0000000; /* Set the Global Conf. register */ + sysEUMBBARWrite(EPIC_GLOBAL_REG, tmp); + /* + * Wait for EPIC to reset - CLH + */ + while( (sysEUMBBARRead(EPIC_GLOBAL_REG) & 0x80000000) == 1); + sysEUMBBARWrite(EPIC_GLOBAL_REG, 0x20000000); + tmp = sysEUMBBARRead(EPIC_INT_CONF_REG); /* Read interrupt conf. reg */ + + if (IRQType == EPIC_DIRECT_IRQ) /* direct mode */ + sysEUMBBARWrite(EPIC_INT_CONF_REG, tmp & 0xf7ffffff); + else /* Serial mode */ + { + tmp = (clkRatio << 28) | 0x08000000; /* Set clock ratio */ + sysEUMBBARWrite(EPIC_INT_CONF_REG, tmp); + } + + while (epicIntAck() != 0xff) /* Clear all pending interrupts */ + epicEOI(); +} + +/**************************************************************************** + * epicIntEnable - Enable an interrupt source + * + * This routine clears the mask bit of an external, an internal or + * a Timer register to enable the interrupt. + * + * RETURNS: None + */ +void epicIntEnable(int intVec) +{ + ULONG tmp; + ULONG srAddr; + + srAddr = SrcVecTable[intVec].srcAddr; /* Retrieve src Vec/Prio register */ + tmp = sysEUMBBARRead(srAddr); + tmp &= ~EPIC_VEC_PRI_MASK; /* Clear the mask bit */ + tmp |= (EPIC_VEC_PRI_DFLT_PRI << 16); /* Set priority to Default - CLH */ + tmp |= intVec; /* Set Vector number */ + sysEUMBBARWrite(srAddr, tmp); + + return; + } + +/**************************************************************************** + * epicIntDisable - Disable an interrupt source + * + * This routine sets the mask bit of an external, an internal or + * a Timer register to disable the interrupt. + * + * RETURNS: OK or ERROR + * + */ + +void epicIntDisable + ( + int intVec /* Interrupt vector number */ + ) + { + + ULONG tmp, srAddr; + + srAddr = SrcVecTable[intVec].srcAddr; + tmp = sysEUMBBARRead(srAddr); + tmp |= 0x80000000; /* Set the mask bit */ + sysEUMBBARWrite(srAddr, tmp); + return; + } + +/**************************************************************************** + * epicIntSourceConfig - Set properties of an interrupt source + * + * This function sets interrupt properites (Polarity, Sense, Interrupt + * Prority, and Interrupt Vector) of an Interrupt Source. The properties + * can be set when the current source is not in-request or in-service, + * which is determined by the Activity bit. This routine return ERROR + * if the the Activity bit is 1 (in-request or in-service). + * + * This function assumes that the Source Vector/Priority register (input) + * is a valid address. + * + * RETURNS: OK or ERROR + */ + +int epicIntSourceConfig + ( + int Vect, /* interrupt source vector number */ + int Polarity, /* interrupt source polarity */ + int Sense, /* interrupt source Sense */ + int Prio /* interrupt source priority */ + ) + + { + ULONG tmp, newVal; + ULONG actBit, srAddr; + + srAddr = SrcVecTable[Vect].srcAddr; + tmp = sysEUMBBARRead(srAddr); + actBit = (tmp & 40000000) >> 30; /* retrieve activity bit - bit 30 */ + if (actBit == 1) + return ERROR; + + tmp &= 0xff30ff00; /* Erase previously set P,S,Prio,Vector bits */ + newVal = (Polarity << 23) | (Sense << 22) | (Prio << 16) | Vect; + sysEUMBBARWrite(srAddr, tmp | newVal ); + return (OK); + } + +/**************************************************************************** + * epicIntAck - acknowledge an interrupt + * + * This function reads the Interrupt acknowldge register and return + * the vector number of the highest pending interrupt. + * + * RETURNS: Interrupt Vector number. + */ + +unsigned int epicIntAck(void) +{ + return(sysEUMBBARRead( EPIC_PROC_INT_ACK_REG )); +} + +/**************************************************************************** + * epicEOI - signal an end of interrupt + * + * This function writes 0x0 to the EOI register to signal end of interrupt. + * It is usually called after an interrupt routine is served. + * + * RETURNS: None + */ + +void epicEOI(void) + { + sysEUMBBARWrite(EPIC_PROC_EOI_REG, 0x0); + } + +/**************************************************************************** + * epicCurTaskPrioSet - sets the priority of the Processor Current Task + * + * This function should be called after epicInit() to lower the priority + * of the processor current task. + * + * RETURNS: OK or ERROR + */ + +int epicCurTaskPrioSet + ( + int prioNum /* New priority value */ + ) + { + + if ( (prioNum < 0) || (prioNum > 0xF)) + return ERROR; + sysEUMBBARWrite(EPIC_PROC_CTASK_PRI_REG, prioNum); + return OK; + } + + +/************************************************************************ + * function: epicIntTaskGet + * + * description: Get value of processor current interrupt task priority register + * + * note: + ***********************************************************************/ +unsigned char epicIntTaskGet() +{ + /* get the interrupt task priority register */ + ULONG reg; + unsigned char rec; + + reg = sysEUMBBARRead( EPIC_PROC_CTASK_PRI_REG ); + rec = ( reg & 0x0F ); + return rec; +} + + +/************************************************************** + * function: epicISR + * + * description: EPIC service routine called by the core exception + * at 0x500 + * + * note: + **************************************************************/ +unsigned int epicISR(void) +{ + return 0; +} + + +/************************************************************ + * function: epicModeGet + * + * description: query EPIC mode, return 0 if pass through mode + * return 1 if mixed mode + * + * note: + *************************************************************/ +unsigned int epicModeGet(void) +{ + ULONG val; + + val = sysEUMBBARRead( EPIC_GLOBAL_REG ); + return (( val & 0x20000000 ) >> 29); +} + + +/********************************************* + * function: epicConfigGet + * + * description: Get the EPIC interrupt Configuration + * return 0 if not error, otherwise return 1 + * + * note: + ********************************************/ +void epicConfigGet( unsigned int *clkRatio, unsigned int *serEnable) +{ + ULONG val; + + val = sysEUMBBARRead( EPIC_INT_CONF_REG ); + *clkRatio = ( val & 0x70000000 ) >> 28; + *serEnable = ( val & 0x8000000 ) >> 27; +} + + +/******************************************************************* + * sysEUMBBARRead - Read a 32-bit EUMBBAR register + * + * This routine reads the content of a register in the Embedded + * Utilities Memory Block, and swaps to big endian before returning + * the value. + * + * RETURNS: The content of the specified EUMBBAR register. + */ + +ULONG sysEUMBBARRead + ( + ULONG regNum + ) + { + ULONG temp; + + temp = *(ULONG *) (CONFIG_SYS_EUMB_ADDR + regNum); + return ( LONGSWAP(temp)); + } + +/******************************************************************* + * sysEUMBBARWrite - Write a 32-bit EUMBBAR register + * + * This routine swaps the value to little endian then writes it to + * a register in the Embedded Utilities Memory Block address space. + * + * RETURNS: N/A + */ + +void sysEUMBBARWrite + ( + ULONG regNum, /* EUMBBAR register address */ + ULONG regVal /* Value to be written */ + ) + { + + *(ULONG *) (CONFIG_SYS_EUMB_ADDR + regNum) = LONGSWAP(regVal); + return ; + } + + +/******************************************************** + * function: epicVendorId + * + * description: return the EPIC Vendor Identification + * register: + * + * siliccon version, device id, and vendor id + * + * note: + ********************************************************/ +void epicVendorId + ( + unsigned int *step, + unsigned int *devId, + unsigned int *venId + ) + { + ULONG val; + val = sysEUMBBARRead( EPIC_VENDOR_ID_REG ); + *step = ( val & 0x00FF0000 ) >> 16; + *devId = ( val & 0x0000FF00 ) >> 8; + *venId = ( val & 0x000000FF ); + } + +/************************************************** + * function: epicFeatures + * + * description: return the number of IRQ supported, + * number of CPU, and the version of the + * OpenEPIC + * + * note: + *************************************************/ +void epicFeatures + ( + unsigned int *noIRQs, + unsigned int *noCPUs, + unsigned int *verId + ) + { + ULONG val; + + val = sysEUMBBARRead( EPIC_FEATURES_REG ); + *noIRQs = ( val & 0x07FF0000 ) >> 16; + *noCPUs = ( val & 0x00001F00 ) >> 8; + *verId = ( val & 0x000000FF ); +} + + +/********************************************************* + * function: epciTmFrequncySet + * + * description: Set the timer frequency reporting register + ********************************************************/ +void epicTmFrequencySet( unsigned int frq ) +{ + sysEUMBBARWrite(EPIC_TM_FREQ_REG, frq); +} + +/******************************************************* + * function: epicTmFrequncyGet + * + * description: Get the current value of the Timer Frequency + * Reporting register + * + ******************************************************/ +unsigned int epicTmFrequencyGet(void) +{ + return( sysEUMBBARRead(EPIC_TM_FREQ_REG)) ; +} + + +/**************************************************** + * function: epicTmBaseSet + * + * description: Set the #n global timer base count register + * return 0 if no error, otherwise return 1. + * + * note: + ****************************************************/ +unsigned int epicTmBaseSet + ( + ULONG srcAddr, /* Address of the Timer Base register */ + unsigned int cnt, /* Base count */ + unsigned int inhibit /* 1 - count inhibit */ + ) +{ + + unsigned int val = 0x80000000; + /* First inhibit counting the timer */ + sysEUMBBARWrite(srcAddr, val) ; + + /* set the new value */ + val = (cnt & 0x7fffffff) | ((inhibit & 0x1) << 31); + sysEUMBBARWrite(srcAddr, val) ; + return 0; +} + +/*********************************************************************** + * function: epicTmBaseGet + * + * description: Get the current value of the global timer base count register + * return 0 if no error, otherwise return 1. + * + * note: + ***********************************************************************/ +unsigned int epicTmBaseGet( ULONG srcAddr, unsigned int *val ) +{ + *val = sysEUMBBARRead( srcAddr ); + *val = *val & 0x7fffffff; + return 0; +} + +/*********************************************************** + * function: epicTmCountGet + * + * description: Get the value of a given global timer + * current count register + * return 0 if no error, otherwise return 1 + * note: + **********************************************************/ +unsigned int epicTmCountGet( ULONG srcAddr, unsigned int *val ) +{ + *val = sysEUMBBARRead( srcAddr ); + *val = *val & 0x7fffffff; + return 0; +} + + +/*********************************************************** + * function: epicTmInhibit + * + * description: Stop counting of a given global timer + * return 0 if no error, otherwise return 1 + * + * note: + ***********************************************************/ +unsigned int epicTmInhibit( unsigned int srcAddr ) +{ + ULONG val; + + val = sysEUMBBARRead( srcAddr ); + val |= 0x80000000; + sysEUMBBARWrite( srcAddr, val ); + return 0; +} + +/****************************************************************** + * function: epicTmEnable + * + * description: Enable counting of a given global timer + * return 0 if no error, otherwise return 1 + * + * note: + *****************************************************************/ +unsigned int epicTmEnable( ULONG srcAddr ) +{ + ULONG val; + + val = sysEUMBBARRead( srcAddr ); + val &= 0x7fffffff; + sysEUMBBARWrite( srcAddr, val ); + return 0; +} + +void epicSourcePrint(int Vect) + { + ULONG srcVal; + + srcVal = sysEUMBBARRead(SrcVecTable[Vect].srcAddr); + PRINT("%s\n", SrcVecTable[Vect].srcName); + PRINT("Address = 0x%lx\n", SrcVecTable[Vect].srcAddr); + PRINT("Vector = %ld\n", (srcVal & 0x000000FF) ); + PRINT("Mask = %ld\n", srcVal >> 31); + PRINT("Activitiy = %ld\n", (srcVal & 40000000) >> 30); + PRINT("Polarity = %ld\n", (srcVal & 0x00800000) >> 23); + PRINT("Sense = %ld\n", (srcVal & 0x00400000) >> 22); + PRINT("Priority = %ld\n", (srcVal & 0x000F0000) >> 16); + } diff --git a/arch/powerpc/cpu/mpc824x/drivers/epic/epic2.S b/arch/powerpc/cpu/mpc824x/drivers/epic/epic2.S new file mode 100644 index 0000000..52d19aa --- /dev/null +++ b/arch/powerpc/cpu/mpc824x/drivers/epic/epic2.S @@ -0,0 +1,196 @@ +/************************************** + * + * copyright @ Motorola, 1999 + * + **************************************/ + +#include <ppc_asm.tmpl> +#include <ppc_defs.h> +#include <asm/processor.h> + +/********************************************* + * function: CoreExtIntEnable + * + * description: Enable 603e core external interrupt + * + * note: mtmsr is context-synchronization + **********************************************/ + .text + .align 2 + .global CoreExtIntEnable +CoreExtIntEnable: + mfmsr r3 + + ori r3,r3,0x8000 /* enable external interrupt */ + mtmsr r3 + + bclr 20, 0 + +/******************************************* + * function: CoreExtIntDisable + * + * description: Disable 603e core external interrupt + * + * note: + *******************************************/ + .text + .align 2 + .global CoreExtIntDisable +CoreExtIntDisable: + mfmsr r4 + + xor r3,r3,r3 + or r3,r3,r4 + + andis. r4,r4,0xffff + andi. r3,r3,0x7fff /* disable external interrupt */ + + or r3,r3,r4 + mtmsr r3 + + bclr 20, 0 + +/********************************************************* + * function: epicEOI + * + * description: signal the EOI and restore machine status + * Input: r3 - value of eumbbar + * Output: r3 - value of eumbbar + * r4 - ISR vector value + * note: + ********************************************************/ + .text + .align 2 + .global epicEOI +epicEOI: + lis r5,0x0006 /* Build End Of Interrupt Register offset */ + ori r5,r5,0x00b0 + xor r7,r7,r7 /* Clear r7 */ + stwbrx r7,r5,r3 /* Save r7, writing to this register will + * intidate the end of processing the + * highest interrupt. + */ + sync + + /* ---RESTORE MACHINE STATE */ + mfmsr r13 /* Clear Recoverable Interrupt bit in MSR */ + or r7,r7,r13 + + andis. r7,r7,0xffff + andi. r13,r13,0x7ffd /* (and disable interrupts) */ + or r13,r13,r7 + mtmsr r13 + + lwz r13,0x1c(r1) /* pull ctr */ + mtctr r13 + + lwz r13,0x18(r1) /* pull xer */ + mtctr r13 + + lwz r13,0x14(r1) /* pull lr */ + mtctr r13 + + lwz r13,0x10(r1) /* Pull SRR1 from stack */ + mtspr SRR1,r13 /* Restore SRR1 */ + + lwz r13,0xc(r1) /* Pull SRR0 from stack */ + mtspr SRR0,r13 /* Restore SRR0 */ + + lwz r13,0x8(r1) /* Pull User stack pointer from stack */ + mtspr SPRG1,r13 /* Restore SPRG1 */ + + lwz r4,0x4(r1) /* vector value */ + lwz r3,0x0(r1) /* eumbbar */ + sync + + addi r1,r1,0x20 /* Deallocate stack */ + mtspr SPRG0,r1 /* Save updated Supervisor stack pointer */ + mfspr r1,SPRG1 /* Restore User stack pointer */ + + bclr 20,0 + +/*********************************************************** + * function: exception routine called by exception vector + * at 0x500, external interrupt + * + * description: Kahlua EPIC controller + * + * input: r3 - content of eumbbar + * output: r3 - ISR return value + * r4 - Interrupt vector number + * note: + ***********************************************************/ + + .text + .align 2 + .global epic_exception + +epic_exception: + + /*---SAVE MACHINE STATE TO A STACK */ + mtspr SPRG1,r1 /* Save User stack pointer to SPRG1 */ + mfspr r1,SPRG0 /* Load Supervisor stack pointer into r1 */ + + stwu r3,-0x20(r1) /* Push the value of eumbbar onto stack */ + + mfspr r3,SPRG1 /* Push User stack pointer onto stack */ + stw r3,0x8(r1) + mfspr r3,SRR0 /* Push SRR0 onto stack */ + stw r1,0xc(r1) + mfspr r3,SRR1 /* Push SRR1 onto stack */ + stw r3,0x10(r1) + mflr r3 + stw r3,0x14(r1) /* Push LR */ + mfxer r3 + stw r3,0x18(r1) /* Push Xer */ + mfctr r3 + stw r3,0x1c(r1) /* Push CTR */ + + mtspr SPRG0,r1 /* Save updated Supervisor stack pointer + * value to SPRG0 + */ + mfmsr r3 + ori r3,r3,0x0002 /* Set Recoverable Interrupt bit in MSR */ + mtmsr r3 + + /* ---READ IN THE EUMBAR REGISTER */ + lwz r6,0(r1) /* this is eumbbar */ + sync + + /* ---READ EPIC REGISTER: PROCESSOR INTERRUPT ACKNOWLEDGE REGISTER */ + lis r5,0x0006 /* Build Interrupt Acknowledge Register + * offset + */ + ori r5,r5,0x00a0 + lwbrx r7,r5,r6 /* Load interrupt vector into r7 */ + sync + + /* --MASK OFF ALL BITS EXCEPT THE VECTOR */ + xor r3,r3,r3 + xor r4,r4,r4 + or r3, r3, r6 /* eumbbar in r3 */ + andi. r4,r7,0x00ff /* Mask off bits, vector in r4 */ + + stw r4,0x04(r1) /* save the vector value */ + + lis r5,epicISR@ha + ori r5,r5,epicISR@l + mtlr r5 + blrl + + xor r30,r30,r30 + or r30,r30,r3 /* save the r3 which containts the return value from epicISR */ + + /* ---READ IN THE EUMBAR REGISTER */ + lwz r3,0(r1) + sync + + lis r5,epicEOI@ha + ori r5,r5,epicEOI@l + mtlr r5 + blrl + + xor r3,r3,r3 + or r3,r3,r30 /* restore the ISR return value */ + + bclr 20,0 diff --git a/arch/powerpc/cpu/mpc824x/drivers/epic/epicutil.S b/arch/powerpc/cpu/mpc824x/drivers/epic/epicutil.S new file mode 100644 index 0000000..4877050 --- /dev/null +++ b/arch/powerpc/cpu/mpc824x/drivers/epic/epicutil.S @@ -0,0 +1,57 @@ +/************************************** + * + * copyright @ Motorola, 1999 + * + * + * This file contains two commonly used + * lower level utility routines. + * + * The utility routines are also in other + * Kahlua device driver libraries. The + * need to be linked in only once. + **************************************/ + +#include <ppc_asm.tmpl> +#include <ppc_defs.h> + +/********************************************************** + * function: load_runtime_reg + * + * input: r3 - value of eumbbar + * r4 - register offset in embedded utility space + * + * output: r3 - register content + **********************************************************/ + .text + .align 2 + .global load_runtime_reg + +load_runtime_reg: + + xor r5,r5,r5 + or r5,r5,r3 /* save eumbbar */ + + lwbrx r3,r4,r5 + sync + + bclr 20, 0 + +/**************************************************************** + * function: store_runtime_reg + * + * input: r3 - value of eumbbar + * r4 - register offset in embedded utility space + * r5 - new value to be stored + * + ****************************************************************/ + .text + .align 2 + .global store_runtime_reg +store_runtime_reg: + + xor r0,r0,r0 + + stwbrx r5, r4, r3 + sync + + bclr 20,0 diff --git a/arch/powerpc/cpu/mpc824x/drivers/errors.h b/arch/powerpc/cpu/mpc824x/drivers/errors.h new file mode 100644 index 0000000..20794a2 --- /dev/null +++ b/arch/powerpc/cpu/mpc824x/drivers/errors.h @@ -0,0 +1,212 @@ +/* Copyright Motorola, Inc. 1993, 1994 + ALL RIGHTS RESERVED + + You are hereby granted a copyright license to use, modify, and + distribute the SOFTWARE so long as this entire notice is retained + without alteration in any modified and/or redistributed versions, + and that such modified versions are clearly identified as such. + No licenses are granted by implication, estoppel or otherwise under + any patents or trademarks of Motorola, Inc. + + The SOFTWARE is provided on an "AS IS" basis and without warranty. + To the maximum extent permitted by applicable law, MOTOROLA DISCLAIMS + ALL WARRANTIES WHETHER EXPRESS OR IMPLIED, INCLUDING IMPLIED + WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR + PURPOSE AND ANY WARRANTY AGAINST INFRINGEMENT WITH + REGARD TO THE SOFTWARE (INCLUDING ANY MODIFIED VERSIONS + THEREOF) AND ANY ACCOMPANYING WRITTEN MATERIALS. + + To the maximum extent permitted by applicable law, IN NO EVENT SHALL + MOTOROLA BE LIABLE FOR ANY DAMAGES WHATSOEVER + (INCLUDING WITHOUT LIMITATION, DAMAGES FOR LOSS OF + BUSINESS PROFITS, BUSINESS INTERRUPTION, LOSS OF BUSINESS + INFORMATION, OR OTHER PECUNIARY LOSS) ARISING OF THE USE OR + INABILITY TO USE THE SOFTWARE. Motorola assumes no responsibility + for the maintenance and support of the SOFTWARE. + +*/ + + +#include "config.h" + +/* + 1 2 3 4 5 6 7 8 +01234567890123456789012345678901234567890123456789012345678901234567890123456789 +*/ +/* List define statements here */ + +/* These are for all the toolboxes and functions to use. These will help +to standardize the error handling in the current project */ + + /* this is the "data type" for the error + messages in the system */ +#define STATUS unsigned int + + /* this is a success status code */ +#define SUCCESS 1 + + /* likewise this is failure */ +#define FAILURE 0 + +#define NUM_ERRORS 47 + +/* This first section of "defines" are for error codes ONLY. The called + routine will return one of these error codes to the caller. If the final + returned code is "VALID", then everything is a-okay. However, if one + of the functions returns a non-valid status, that error code should be + propogated back to all the callers. At the end, the last caller will + call an error_processing function, and send in the status which was + returned. It's up to the error_processing function to determine which + error occured (as indicated by the status), and print an appropriate + message back to the user. +*/ +/*----------------------------------------------------------------------*/ +/* these are specifically for the parser routines */ + +#define UNKNOWN_COMMAND 0xfb00 /* "unrecognized command " */ +#define UNKNOWN_REGISTER 0xfb01 /* "unknown register "*/ +#define ILLEGAL_RD_STAGE 0xfb02 /* cannot specify reg. family in range*/ +#define ILLEGAL_REG_FAMILY 0xfb03 /* "cannot specify a range of special + or miscellaneous registers"*/ +#define RANGE_CROSS_FAMILY 0xfb04 /* "cannot specify a range across + register families" */ +#define UNIMPLEMENTED_STAGE 0xfb05 /* invalid rd or rmm parameter format */ +#define REG_NOT_WRITEABLE 0xfb06 /* "unknown operator in arguements"*/ +#define INVALID_FILENAME 0xfb07 /* "invalid download filename" */ +#define INVALID_BAUD_RATE 0xfb08 /* invalid baud rate from sb command */ +#define UNSUPPORTED_REGISTER 0xfb09 /* Special register is not supported */ +#define FOR_BOARD_ONLY 0xfb0a /* "Not available for Unix." */ + + +/*----------------------------------------------------------------------*/ +/* these are for the error checking toolbox */ + +#define INVALID 0xfd00 /* NOT valid */ +#define VALID 0xfd01 /* valid */ + + /* This error is found in the fcn: + is_right_size_input() to indicate + that the input was not 8 characters + long. */ +#define INVALID_SIZE 0xfd02 + + /* This error is found in the fcn: + is_valid_address_range() to indicate + that the address given falls outside + of valid memory defined by MEM_START + to MEM_END. + */ +#define OUT_OF_BOUNDS_ADDRESS 0xfd03 + + /* This error is found in the fcn: + is_valid_hex_input() to indicate that + one of more of the characters entered + are not valid hex characters. Valid + hex characters are 0-9, A-F, a-f. + */ +#define INVALID_HEX_INPUT 0xfd04 + + /* This error is found in the fcn: + is_valid_register_number() to indicate + that a given register does not exist. + */ +#define REG_NOT_READABLE 0xfd05 + + /* This error is found in the fcn: + is_word_aligned_address() to indicate + that the given address is not word- + aligned. A word-aligned address ends + in 0x0,0x4,0x8,0xc. + */ +#define NOT_WORD_ALIGNED 0xfd07 + + /* This error is found in the fcn: + is_valid_address_range() to indicate + that the starting address is greater + than the ending address. + */ +#define REVERSED_ADDRESS 0xfd08 + + /* this error tells us that the address + specified as the destination is within + the source addresses */ +#define RANGE_OVERLAP 0xfd09 + + +#define ERROR 0xfd0a /* An error occured */ +#define INVALID_PARAM 0xfd0b /* "invalid input parameter " */ + + +#define INVALID_FLAG 0xfd0c /* invalid flag */ + +/*----------------------------------------------------------------------*/ +/* these are for the getarg toolbox */ + +#define INVALID_NUMBER_ARGS 0xFE00 /* invalid number of commd arguements */ +#define UNKNOWN_PARAMETER 0xFE01 /* "unknown type of parameter "*/ + + +/*----------------------------------------------------------------------*/ +/* these are for the tokenizer toolbox */ + +#define ILLEGAL_CHARACTER 0xFF00 /* unrecognized char. in input stream*/ +#define TTL_NOT_SORTED 0xFF01 /* token translation list not sorted */ +#define TTL_NOT_DEFINED 0xFF02 /* token translation list not assigned*/ +#define INVALID_STRING 0xFF03 /* unable to extract string from input */ +#define BUFFER_EMPTY 0xFF04 /* "input buffer is empty" */ +#define INVALID_MODE 0xFF05 /* input buf is in an unrecognized mode*/ +#define TOK_INTERNAL_ERROR 0xFF06 /* "internal tokenizer error" */ +#define TOO_MANY_IBS 0xFF07 /* "too many open input buffers" */ +#define NO_OPEN_IBS 0xFF08 /* "no open input buffers" */ + + +/* these are for the read from screen toolbox */ + +#define RESERVED_WORD 0xFC00 /* used a reserved word as an arguement*/ + + +/* these are for the breakpoint routines */ + +#define FULL_BPDS 0xFA00 /* breakpoint data structure is full */ + + +/* THESE are for the downloader */ + +#define NOT_IN_S_RECORD_FORMAT 0xf900 /* "not in S-Record Format" */ +#define UNREC_RECORD_TYPE 0xf901 /* "unrecognized record type" */ +#define CONVERSION_ERROR 0xf902 /* "ascii to int conversion error" */ +#define INVALID_MEMORY 0xf903 /* "bad s-record memory address " */ + + +/* these are for the compression and decompression stuff */ + +#define COMP_UNK_CHARACTER 0xf800 /* "unknown compressed character " */ + +#define COMP_UNKNOWN_STATE 0xf801 /* "unknown binary state" */ + +#define NOT_IN_COMPRESSED_FORMAT 0xf802 /* not in compressed S-Record format */ + + +/* these are for the DUART handling things */ + + /* "unrecognized serial port configuration" */ +#define UNKNOWN_PORT_STATE 0xf700 + + +/* these are for the register toolbox */ + + /* "cannot find register in special + purpose register file " */ +#define SPR_NOT_FOUND 0xf600 + + +/* these are for the duart specific stuff */ + + /* "transparent mode needs access to + two serial ports" */ +#define TM_NEEDS_BOTH_PORTS 0xf500 + + +/*----------------------------------------------------------------------*/ +/* these are specifically for the flash routines */ +#define FLASH_ERROR 0xf100 /* general flash error */ diff --git a/arch/powerpc/cpu/mpc824x/drivers/i2c/i2c.c b/arch/powerpc/cpu/mpc824x/drivers/i2c/i2c.c new file mode 100644 index 0000000..637ae4c --- /dev/null +++ b/arch/powerpc/cpu/mpc824x/drivers/i2c/i2c.c @@ -0,0 +1,270 @@ +/* + * (C) Copyright 2003 + * Gleb Natapov <gnatapov@mrv.com> + * Some bits are taken from linux driver writen by adrian@humboldt.co.uk + * + * Hardware I2C driver for MPC107 PCI bridge. + * + * 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> + +#undef I2CDBG + +#ifdef CONFIG_HARD_I2C +#include <i2c.h> + +#define TIMEOUT (CONFIG_SYS_HZ/4) + +#define I2C_Addr ((unsigned *)(CONFIG_SYS_EUMB_ADDR + 0x3000)) + +#define I2CADR &I2C_Addr[0] +#define I2CFDR &I2C_Addr[1] +#define I2CCCR &I2C_Addr[2] +#define I2CCSR &I2C_Addr[3] +#define I2CCDR &I2C_Addr[4] + +#define MPC107_CCR_MEN 0x80 +#define MPC107_CCR_MIEN 0x40 +#define MPC107_CCR_MSTA 0x20 +#define MPC107_CCR_MTX 0x10 +#define MPC107_CCR_TXAK 0x08 +#define MPC107_CCR_RSTA 0x04 + +#define MPC107_CSR_MCF 0x80 +#define MPC107_CSR_MAAS 0x40 +#define MPC107_CSR_MBB 0x20 +#define MPC107_CSR_MAL 0x10 +#define MPC107_CSR_SRW 0x04 +#define MPC107_CSR_MIF 0x02 +#define MPC107_CSR_RXAK 0x01 + +#define I2C_READ 1 +#define I2C_WRITE 0 + +/* taken from linux include/asm-ppc/io.h */ +inline unsigned in_le32 (volatile unsigned *addr) +{ + unsigned ret; + + __asm__ __volatile__ ("lwbrx %0,0,%1;\n" + "twi 0,%0,0;\n" + "isync":"=r" (ret): "r" (addr), "m" (*addr)); + return ret; +} + +inline void out_le32 (volatile unsigned *addr, int val) +{ + __asm__ __volatile__ ("stwbrx %1,0,%2; eieio":"=m" (*addr):"r" (val), + "r" (addr)); +} + +#define writel(val, addr) out_le32(addr, val) +#define readl(addr) in_le32(addr) + +void i2c_init (int speed, int slaveadd) +{ + /* stop I2C controller */ + writel (0x0, I2CCCR); + /* set clock */ + writel (0x1020, I2CFDR); + /* write slave address */ + writel (slaveadd, I2CADR); + /* clear status register */ + writel (0x0, I2CCSR); + /* start I2C controller */ + writel (MPC107_CCR_MEN, I2CCCR); + + return; +} + +static __inline__ int i2c_wait4bus (void) +{ + ulong timeval = get_timer (0); + + while (readl (I2CCSR) & MPC107_CSR_MBB) + if (get_timer (timeval) > TIMEOUT) + return -1; + + return 0; +} + +static __inline__ int i2c_wait (int write) +{ + u32 csr; + ulong timeval = get_timer (0); + + do { + csr = readl (I2CCSR); + + if (!(csr & MPC107_CSR_MIF)) + continue; + + writel (0x0, I2CCSR); + + if (csr & MPC107_CSR_MAL) { +#ifdef I2CDBG + printf ("i2c_wait: MAL\n"); +#endif + return -1; + } + + if (!(csr & MPC107_CSR_MCF)) { +#ifdef I2CDBG + printf ("i2c_wait: unfinished\n"); +#endif + return -1; + } + + if (write == I2C_WRITE && (csr & MPC107_CSR_RXAK)) { +#ifdef I2CDBG + printf ("i2c_wait: No RXACK\n"); +#endif + return -1; + } + + return 0; + } while (get_timer (timeval) < TIMEOUT); + +#ifdef I2CDBG + printf ("i2c_wait: timed out\n"); +#endif + return -1; +} + +static __inline__ int i2c_write_addr (u8 dev, u8 dir, int rsta) +{ + writel (MPC107_CCR_MEN | MPC107_CCR_MSTA | MPC107_CCR_MTX | + (rsta ? MPC107_CCR_RSTA : 0), I2CCCR); + + writel ((dev << 1) | dir, I2CCDR); + + if (i2c_wait (I2C_WRITE) < 0) + return 0; + + return 1; +} + +static __inline__ int __i2c_write (u8 * data, int length) +{ + int i; + + writel (MPC107_CCR_MEN | MPC107_CCR_MSTA | MPC107_CCR_MTX, I2CCCR); + + for (i = 0; i < length; i++) { + writel (data[i], I2CCDR); + + if (i2c_wait (I2C_WRITE) < 0) + break; + } + + return i; +} + +static __inline__ int __i2c_read (u8 * data, int length) +{ + int i; + + writel (MPC107_CCR_MEN | MPC107_CCR_MSTA | + ((length == 1) ? MPC107_CCR_TXAK : 0), I2CCCR); + + /* dummy read */ + readl (I2CCDR); + + for (i = 0; i < length; i++) { + if (i2c_wait (I2C_READ) < 0) + break; + + /* Generate ack on last next to last byte */ + if (i == length - 2) + writel (MPC107_CCR_MEN | MPC107_CCR_MSTA | + MPC107_CCR_TXAK, I2CCCR); + + /* Generate stop on last byte */ + if (i == length - 1) + writel (MPC107_CCR_MEN | MPC107_CCR_TXAK, I2CCCR); + + data[i] = readl (I2CCDR); + } + + return i; +} + +int i2c_read (u8 dev, uint addr, int alen, u8 * data, int length) +{ + int i = 0; + u8 *a = (u8 *) & addr; + + if (i2c_wait4bus () < 0) + goto exit; + + if (i2c_write_addr (dev, I2C_WRITE, 0) == 0) + goto exit; + + if (__i2c_write (&a[4 - alen], alen) != alen) + goto exit; + + if (i2c_write_addr (dev, I2C_READ, 1) == 0) + goto exit; + + i = __i2c_read (data, length); + +exit: + writel (MPC107_CCR_MEN, I2CCCR); + + return !(i == length); +} + +int i2c_write (u8 dev, uint addr, int alen, u8 * data, int length) +{ + int i = 0; + u8 *a = (u8 *) & addr; + + if (i2c_wait4bus () < 0) + goto exit; + + if (i2c_write_addr (dev, I2C_WRITE, 0) == 0) + goto exit; + + if (__i2c_write (&a[4 - alen], alen) != alen) + goto exit; + + i = __i2c_write (data, length); + +exit: + writel (MPC107_CCR_MEN, I2CCCR); + + return !(i == length); +} + +int i2c_probe (uchar chip) +{ + int tmp; + + /* + * Try to read the first location of the chip. The underlying + * driver doesn't appear to support sending just the chip address + * and looking for an <ACK> back. + */ + udelay (10000); + return i2c_read (chip, 0, 1, (uchar *) &tmp, 1); +} + +#endif /* CONFIG_HARD_I2C */ diff --git a/arch/powerpc/cpu/mpc824x/drivers/i2c_export.h b/arch/powerpc/cpu/mpc824x/drivers/i2c_export.h new file mode 100644 index 0000000..6264d18 --- /dev/null +++ b/arch/powerpc/cpu/mpc824x/drivers/i2c_export.h @@ -0,0 +1,103 @@ +#ifndef I2C_EXPORT_H +#define I2C_EXPORT_H + +/**************************************************** + * + * Copyright Motrola 1999 + * + ****************************************************/ + +/* These are the defined return values for the I2C_do_transaction function. + * Any non-zero value indicates failure. Failure modes can be added for + * more detailed error reporting. + */ +typedef enum _i2c_status +{ + I2C_SUCCESS = 0, + I2C_ERROR, +} I2C_Status; + +/* These are the defined tasks for I2C_do_transaction. + * Modes for SLAVE_RCV and SLAVE_XMIT will be added. + */ +typedef enum _i2c_transaction_mode +{ + I2C_MASTER_RCV = 0, + I2C_MASTER_XMIT = 1, +} I2C_TRANSACTION_MODE; + +typedef enum _i2c_interrupt_mode +{ + I2C_INT_DISABLE = 0, + I2C_INT_ENABLE = 1, +} I2C_INTERRUPT_MODE; + +typedef enum _i2c_stop +{ + I2C_NO_STOP = 0, + I2C_STOP = 1, +} I2C_STOP_MODE; + +typedef enum _i2c_restart +{ + I2C_NO_RESTART = 0, + I2C_RESTART = 1, +} I2C_RESTART_MODE; + +/******************** App. API ******************** + * The application API is for user level application + * to use the functionality provided by I2C driver. + * This is a "generic" I2C interface, it should contain + * nothing specific to the Kahlua implementation. + * Only the generic functions are exported by the library. + * + * Note: Its App.s responsibility to swap the data + * byte. In our API, we just transfer whatever + * we are given + **************************************************/ + + +/* Initialize I2C unit with the following: + * driver's slave address + * interrupt enabled + * optional pointer to application layer print function + * + * These parameters may be added: + * desired clock rate + * digital filter frequency sampling rate + * + * This function must be called before I2C unit can be used. + */ +extern I2C_Status I2C_Initialize( + unsigned char addr, /* driver's I2C slave address */ + I2C_INTERRUPT_MODE en_int, /* 1 - enable I2C interrupt + * 0 - disable I2C interrupt + */ + int (*app_print_function)(char *,...)); /* pointer to optional "printf" + * provided by application + */ + +/* Perform the given I2C transaction, only MASTER_XMIT and MASTER_RCV + * are implemented. Both are only in polling mode. + * + * en_int controls interrupt/polling mode + * act is the type of transaction + * addr is the I2C address of the slave device + * len is the length of data to send or receive + * buffer is the address of the data buffer + * stop = I2C_NO_STOP, don't signal STOP at end of transaction + * I2C_STOP, signal STOP at end of transaction + * retry is the timeout retry value, currently ignored + * rsta = I2C_NO_RESTART, this is not continuation of existing transaction + * I2C_RESTART, this is a continuation of existing transaction + */ +extern I2C_Status I2C_do_transaction( I2C_INTERRUPT_MODE en_int, + I2C_TRANSACTION_MODE act, + unsigned char i2c_addr, + unsigned char data_addr, + int len, + char *buffer, + I2C_STOP_MODE stop, + int retry, + I2C_RESTART_MODE rsta); +#endif diff --git a/arch/powerpc/cpu/mpc824x/interrupts.c b/arch/powerpc/cpu/mpc824x/interrupts.c new file mode 100644 index 0000000..139c52c --- /dev/null +++ b/arch/powerpc/cpu/mpc824x/interrupts.c @@ -0,0 +1,93 @@ +/* + * (C) Copyright 2000-2002 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * Rob Taylor, Flying Pig Systems. robt@flyingpig.com + * + * 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> +#include <mpc824x.h> +#include <asm/processor.h> +#include <asm/pci_io.h> +#include <commproc.h> +#include "drivers/epic.h" + +int interrupt_init_cpu (unsigned *decrementer_count) +{ + *decrementer_count = (get_bus_freq (0) / 4) / CONFIG_SYS_HZ; + + /* + * It's all broken at the moment and I currently don't need + * interrupts. If you want to fix it, have a look at the epic + * drivers in dink32 v12. They do everthing and Motorola said + * I could use the dink source in this project as long as + * copyright notices remain intact. + */ + + epicInit (EPIC_DIRECT_IRQ, 0); + /* EPIC won't generate INT unless Current Task Pri < 15 */ + epicCurTaskPrioSet(0); + + return (0); +} + +/****************************************************************************/ + +/* + * Handle external interrupts + */ +void external_interrupt (struct pt_regs *regs) +{ + register unsigned long temp; + + pci_readl (CONFIG_SYS_EUMB_ADDR + EPIC_PROC_INT_ACK_REG, temp); + sync (); /* i'm not convinced this is needed, but dink source has it */ + temp &= 0xff; /*get vector */ + + /*TODO: handle them -... */ + epicEOI (); +} + +/****************************************************************************/ + +/* + * blank int handlers. + */ + +void +irq_install_handler (int vec, interrupt_handler_t * handler, void *arg) +{ +} + +void irq_free_handler (int vec) +{ + +} + +/*TODO: some handlers for winbond and 87308 interrupts + and what about generic pci inteerupts? + vga? + */ + +void timer_interrupt_cpu (struct pt_regs *regs) +{ + /* nothing to do here */ + return; +} diff --git a/arch/powerpc/cpu/mpc824x/pci.c b/arch/powerpc/cpu/mpc824x/pci.c new file mode 100644 index 0000000..72aaec7 --- /dev/null +++ b/arch/powerpc/cpu/mpc824x/pci.c @@ -0,0 +1,78 @@ +/* + * arch/powerpc/kernel/mpc10x_common.c + * + * Common routines for the Motorola SPS MPC106, MPC107 and MPC8240 Host bridge, + * Mem ctlr, EPIC, etc. + * + * Author: Mark A. Greer + * mgreer@mvista.com + * + * Copyright 2001 MontaVista Software 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. + */ + +#include <common.h> + +#ifdef CONFIG_PCI + +#include <asm/processor.h> +#include <asm/io.h> +#include <pci.h> +#include <mpc824x.h> + +void pci_mpc824x_init (struct pci_controller *hose) +{ + hose->first_busno = 0; + hose->last_busno = 0xff; + + /* System memory space */ + pci_set_region(hose->regions + 0, + CHRP_PCI_MEMORY_BUS, + CHRP_PCI_MEMORY_PHYS, + CHRP_PCI_MEMORY_SIZE, + PCI_REGION_MEM | PCI_REGION_SYS_MEMORY); + + /* PCI memory space */ + pci_set_region(hose->regions + 1, + CHRP_PCI_MEM_BUS, + CHRP_PCI_MEM_PHYS, + CHRP_PCI_MEM_SIZE, + PCI_REGION_MEM); + + /* ISA/PCI memory space */ + pci_set_region(hose->regions + 2, + CHRP_ISA_MEM_BUS, + CHRP_ISA_MEM_PHYS, + CHRP_ISA_MEM_SIZE, + PCI_REGION_MEM); + + /* PCI I/O space */ + pci_set_region(hose->regions + 3, + CHRP_PCI_IO_BUS, + CHRP_PCI_IO_PHYS, + CHRP_PCI_IO_SIZE, + PCI_REGION_IO); + + /* ISA/PCI I/O space */ + pci_set_region(hose->regions + 4, + CHRP_ISA_IO_BUS, + CHRP_ISA_IO_PHYS, + CHRP_ISA_IO_SIZE, + PCI_REGION_IO); + + hose->region_count = 5; + + pci_setup_indirect(hose, + CHRP_REG_ADDR, + CHRP_REG_DATA); + + pci_register_hose(hose); + + hose->last_busno = pci_hose_scan(hose); +} + +#endif diff --git a/arch/powerpc/cpu/mpc824x/speed.c b/arch/powerpc/cpu/mpc824x/speed.c new file mode 100644 index 0000000..fdcb972 --- /dev/null +++ b/arch/powerpc/cpu/mpc824x/speed.c @@ -0,0 +1,118 @@ +/* + * (C) Copyright 2001 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * (C) Copyright 2002 + * Gregory E. Allen, gallen@arlut.utexas.edu + * Applied Research Laboratories, The University of Texas at Austin + * + * 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> +#include <mpc824x.h> +#include <asm/processor.h> + +DECLARE_GLOBAL_DATA_PTR; + +/* ------------------------------------------------------------------------- */ +/* NOTE: This describes the proper use of this file. + * + * CONFIG_SYS_CLK_FREQ should be defined as the input frequency on + * PCI_SYNC_IN . + * + * CONFIG_PLL_PCI_TO_MEM_MULTIPLIER is only required on MPC8240 + * boards. It should be defined as the PCI to Memory Multiplier as + * documented in the MPC8240 Hardware Specs. + * + * Other mpc824x boards don't need CONFIG_PLL_PCI_TO_MEM_MULTIPLIER + * because they can determine it from the PCR. + * + * Gary Milliorn <gary.milliorn@motorola.com> (who should know since + * he designed the Sandpoint) told us that the PCR is not in all revs + * of the MPC8240 CPU, so it's not guaranteeable and we cannot do + * away with CONFIG_PLL_PCI_TO_MEM_MULTIPLIER altogether. + */ +/* ------------------------------------------------------------------------- */ + +/* This gives the PCI to Memory multiplier times 10 */ +/* The index is the value of PLL_CFG[0:4] */ +/* This is documented in the MPC8240/5 Hardware Specs */ + +short pll_pci_to_mem_multiplier[] = { +#if defined(CONFIG_MPC8240) + 30, 30, 10, 10, 20, 10, 0, 10, + 10, 0, 20, 0, 20, 0, 20, 0, + 30, 0, 15, 0, 20, 0, 20, 0, + 25, 0, 10, 0, 15, 15, 0, 0, +#elif defined(CONFIG_MPC8245) + 30, 30, 10, 10, 20, 10, 10, 10, + 10, 20, 20, 15, 20, 15, 20, 30, + 30, 40, 15, 40, 20, 25, 20, 40, + 25, 20, 10, 20, 15, 15, 15, 0, +#else +#error Specific type of MPC824x must be defined (i.e. CONFIG_MPC8240) +#endif +}; + +#define CU824_PLL_STATE_REG 0xFE80002F +#define PCR 0x800000E2 + +/* ------------------------------------------------------------------------- */ + +/* compute the memory bus clock frequency */ +ulong get_bus_freq (ulong dummy) +{ + unsigned char pll_cfg; +#if defined(CONFIG_MPC8240) && !defined(CONFIG_CU824) + return (CONFIG_SYS_CLK_FREQ) * (CONFIG_PLL_PCI_TO_MEM_MULTIPLIER); +#elif defined(CONFIG_CU824) + pll_cfg = *(volatile unsigned char *) (CU824_PLL_STATE_REG); + pll_cfg &= 0x1f; +#else + CONFIG_READ_BYTE(PCR, pll_cfg); + pll_cfg = (pll_cfg >> 3) & 0x1f; +#endif + return ((CONFIG_SYS_CLK_FREQ) * pll_pci_to_mem_multiplier[pll_cfg] + 5) / 10; +} + + +/* ------------------------------------------------------------------------- */ + +/* This gives the Memory to CPU Core multiplier times 10 */ +/* The index is the value of PLLRATIO in HID1 */ +/* This is documented in the MPC8240 Hardware Specs */ +/* This is not documented for MPC8245 ? FIXME */ +short pllratio_to_factor[] = { + 0, 0, 0, 10, 20, 20, 25, 45, + 30, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 10, 0, 0, 0, 45, + 30, 0, 40, 0, 0, 0, 35, 0, +}; + +/* compute the CPU and memory bus clock frequencies */ +int get_clocks (void) +{ + uint hid1 = mfspr(HID1); + hid1 = (hid1 >> (32-5)) & 0x1f; + gd->cpu_clk = (pllratio_to_factor[hid1] * get_bus_freq(0) + 5) + / 10; + gd->bus_clk = get_bus_freq(0); + return (0); +} diff --git a/arch/powerpc/cpu/mpc824x/start.S b/arch/powerpc/cpu/mpc824x/start.S new file mode 100644 index 0000000..f3f595a --- /dev/null +++ b/arch/powerpc/cpu/mpc824x/start.S @@ -0,0 +1,766 @@ +/* + * Copyright (C) 1998 Dan Malek <dmalek@jlc.net> + * Copyright (C) 1999 Magnus Damm <kieraypc01.p.y.kie.era.ericsson.se> + * Copyright (C) 2000,2001,2002 Wolfgang Denk <wd@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 + */ + +/* U-Boot - Startup Code for PowerPC based Embedded Boards + * + * + * The processor starts at 0x00000100 and the code is executed + * from flash. The code is organized to be at an other address + * in memory, but as long we don't jump around before relocating. + * board_init lies at a quite high address and when the cpu has + * jumped there, everything is ok. + * This works because the cpu gives the FLASH (CS0) the whole + * address space at startup, and board_init lies as a echo of + * the flash somewhere up there in the memorymap. + * + * board_init will change CS0 to be positioned at the correct + * address and (s)dram will be positioned at address 0 + */ +#include <config.h> +#include <mpc824x.h> +#include <timestamp.h> +#include <version.h> + +#define _LINUX_CONFIG_H 1 /* avoid reading Linux autoconf.h file */ + +#include <ppc_asm.tmpl> +#include <ppc_defs.h> + +#include <asm/cache.h> +#include <asm/mmu.h> + +#ifndef CONFIG_IDENT_STRING +#define CONFIG_IDENT_STRING "" +#endif + +/* We don't want the MMU yet. +*/ +#undef MSR_KERNEL +/* FP, Machine Check and Recoverable Interr. */ +#define MSR_KERNEL ( MSR_FP | MSR_ME | MSR_RI ) + +/* + * Set up GOT: Global Offset Table + * + * Use r12 to access the GOT + */ + START_GOT + GOT_ENTRY(_GOT2_TABLE_) + GOT_ENTRY(_FIXUP_TABLE_) + + GOT_ENTRY(_start) + GOT_ENTRY(_start_of_vectors) + GOT_ENTRY(_end_of_vectors) + GOT_ENTRY(transfer_to_handler) + + GOT_ENTRY(__init_end) + GOT_ENTRY(_end) + GOT_ENTRY(__bss_start) +#if defined(CONFIG_FADS) + GOT_ENTRY(environment) +#endif + END_GOT + +/* + * r3 - 1st arg to board_init(): IMMP pointer + * r4 - 2nd arg to board_init(): boot flag + */ + .text + .long 0x27051956 /* U-Boot Magic Number */ + .globl version_string +version_string: + .ascii U_BOOT_VERSION + .ascii " (", U_BOOT_DATE, " - ", U_BOOT_TIME, ")" + .ascii CONFIG_IDENT_STRING, "\0" + + . = EXC_OFF_SYS_RESET + .globl _start +_start: + li r21, BOOTFLAG_COLD /* Normal Power-On: Boot from FLASH */ + b boot_cold + + . = EXC_OFF_SYS_RESET + 0x10 + + .globl _start_warm +_start_warm: + li r21, BOOTFLAG_WARM /* Software reboot */ + b boot_warm + +boot_cold: +boot_warm: + + /* Initialize machine status; enable machine check interrupt */ + /*----------------------------------------------------------------------*/ + li r3, MSR_KERNEL /* Set FP, ME, RI flags */ + mtmsr r3 + mtspr SRR1, r3 /* Make SRR1 match MSR */ + + addis r0,0,0x0000 /* lets make sure that r0 is really 0 */ + mtspr HID0, r0 /* disable I and D caches */ + + mfspr r3, ICR /* clear Interrupt Cause Register */ + + mfmsr r3 /* turn off address translation */ + addis r4,0,0xffff + ori r4,r4,0xffcf + and r3,r3,r4 + mtmsr r3 + isync + sync /* the MMU should be off... */ + + +in_flash: +#if defined(CONFIG_BMW) + bl early_init_f /* Must be ASM: no stack yet! */ +#endif + /* + * Setup BATs - cannot be done in C since we don't have a stack yet + */ + bl setup_bats + + /* Enable MMU. + */ + mfmsr r3 + ori r3, r3, (MSR_IR | MSR_DR) + mtmsr r3 +#if !defined(CONFIG_BMW) + /* Enable and invalidate data cache. + */ + mfspr r3, HID0 + mr r2, r3 + ori r3, r3, HID0_DCE | HID0_DCI + ori r2, r2, HID0_DCE + sync + mtspr HID0, r3 + mtspr HID0, r2 + sync + + /* Allocate Initial RAM in data cache. + */ + lis r3, CONFIG_SYS_INIT_RAM_ADDR@h + ori r3, r3, CONFIG_SYS_INIT_RAM_ADDR@l + li r2, 128 + mtctr r2 +1: + dcbz r0, r3 + addi r3, r3, 32 + bdnz 1b + + /* Lock way0 in data cache. + */ + mfspr r3, 1011 + lis r2, 0xffff + ori r2, r2, 0xff1f + and r3, r3, r2 + ori r3, r3, 0x0080 + sync + mtspr 1011, r3 +#endif /* !CONFIG_BMW */ + /* + * Thisk the stack pointer *somewhere* sensible. Doesnt + * matter much where as we'll move it when we relocate + */ + lis r1, (CONFIG_SYS_INIT_RAM_ADDR + CONFIG_SYS_GBL_DATA_OFFSET)@h + ori r1, r1, (CONFIG_SYS_INIT_RAM_ADDR + CONFIG_SYS_GBL_DATA_OFFSET)@l + + li r0, 0 /* Make room for stack frame header and */ + stwu r0, -4(r1) /* clear final stack frame so that */ + stwu r0, -4(r1) /* stack backtraces terminate cleanly */ + + /* let the C-code set up the rest */ + /* */ + /* Be careful to keep code relocatable ! */ + /*----------------------------------------------------------------------*/ + + GET_GOT /* initialize GOT access */ + + /* r3: IMMR */ + bl cpu_init_f /* run low-level CPU init code (from Flash) */ + + mr r3, r21 + /* r3: BOOTFLAG */ + bl board_init_f /* run 1st part of board init code (from Flash) */ + + + .globl _start_of_vectors +_start_of_vectors: + +/* Machine check */ + STD_EXCEPTION(EXC_OFF_MACH_CHCK, MachineCheck, MachineCheckException) + +/* Data Storage exception. "Never" generated on the 860. */ + STD_EXCEPTION(EXC_OFF_DATA_STOR, DataStorage, UnknownException) + +/* Instruction Storage exception. "Never" generated on the 860. */ + STD_EXCEPTION(EXC_OFF_INS_STOR, InstStorage, UnknownException) + +/* External Interrupt exception. */ + STD_EXCEPTION(EXC_OFF_EXTERNAL, ExtInterrupt, external_interrupt) + +/* Alignment exception. */ + . = EXC_OFF_ALIGN +Alignment: + EXCEPTION_PROLOG(SRR0, SRR1) + mfspr r4,DAR + stw r4,_DAR(r21) + mfspr r5,DSISR + stw r5,_DSISR(r21) + addi r3,r1,STACK_FRAME_OVERHEAD + EXC_XFER_TEMPLATE(Alignment, AlignmentException, MSR_KERNEL, COPY_EE) + +/* Program check exception */ + . = EXC_OFF_PROGRAM +ProgramCheck: + EXCEPTION_PROLOG(SRR0, SRR1) + addi r3,r1,STACK_FRAME_OVERHEAD + EXC_XFER_TEMPLATE(ProgramCheck, ProgramCheckException, + MSR_KERNEL, COPY_EE) + + /* No FPU on MPC8xx. This exception is not supposed to happen. + */ + STD_EXCEPTION(EXC_OFF_FPUNAVAIL, FPUnavailable, UnknownException) + + /* I guess we could implement decrementer, and may have + * to someday for timekeeping. + */ + STD_EXCEPTION(EXC_OFF_DECR, Decrementer, timer_interrupt) + STD_EXCEPTION(0xa00, Trap_0a, UnknownException) + STD_EXCEPTION(0xb00, Trap_0b, UnknownException) + STD_EXCEPTION(0xc00, SystemCall, UnknownException) + + STD_EXCEPTION(EXC_OFF_TRACE, SingleStep, UnknownException) + + STD_EXCEPTION(EXC_OFF_FPUNASSIST, Trap_0e, UnknownException) + STD_EXCEPTION(EXC_OFF_PMI, Trap_0f, UnknownException) + + STD_EXCEPTION(EXC_OFF_ITME, InstructionTransMiss, UnknownException) + STD_EXCEPTION(EXC_OFF_DLTME, DataLoadTransMiss, UnknownException) + STD_EXCEPTION(EXC_OFF_DSTME, DataStoreTransMiss, UnknownException) + STD_EXCEPTION(EXC_OFF_IABE, InstructionBreakpoint, DebugException) + STD_EXCEPTION(EXC_OFF_SMIE, SysManageInt, UnknownException) + STD_EXCEPTION(0x1500, Reserved5, UnknownException) + STD_EXCEPTION(0x1600, Reserved6, UnknownException) + STD_EXCEPTION(0x1700, Reserved7, UnknownException) + STD_EXCEPTION(0x1800, Reserved8, UnknownException) + STD_EXCEPTION(0x1900, Reserved9, UnknownException) + STD_EXCEPTION(0x1a00, ReservedA, UnknownException) + STD_EXCEPTION(0x1b00, ReservedB, UnknownException) + STD_EXCEPTION(0x1c00, ReservedC, UnknownException) + STD_EXCEPTION(0x1d00, ReservedD, UnknownException) + STD_EXCEPTION(0x1e00, ReservedE, UnknownException) + STD_EXCEPTION(0x1f00, ReservedF, UnknownException) + + STD_EXCEPTION(EXC_OFF_RMTE, RunModeTrace, UnknownException) + + .globl _end_of_vectors +_end_of_vectors: + + + . = 0x3000 + +/* + * This code finishes saving the registers to the exception frame + * and jumps to the appropriate handler for the exception. + * Register r21 is pointer into trap frame, r1 has new stack pointer. + */ + .globl transfer_to_handler +transfer_to_handler: + stw r22,_NIP(r21) + lis r22,MSR_POW@h + andc r23,r23,r22 + stw r23,_MSR(r21) + SAVE_GPR(7, r21) + SAVE_4GPRS(8, r21) + SAVE_8GPRS(12, r21) + SAVE_8GPRS(24, r21) +#if 0 + andi. r23,r23,MSR_PR + mfspr r23,SPRG3 /* if from user, fix up tss.regs */ + beq 2f + addi r24,r1,STACK_FRAME_OVERHEAD + stw r24,PT_REGS(r23) +2: addi r2,r23,-TSS /* set r2 to current */ + tovirt(r2,r2,r23) +#endif + mflr r23 + andi. r24,r23,0x3f00 /* get vector offset */ + stw r24,TRAP(r21) + li r22,0 + stw r22,RESULT(r21) + mtspr SPRG2,r22 /* r1 is now kernel sp */ +#if 0 + addi r24,r2,TASK_STRUCT_SIZE /* check for kernel stack overflow */ + cmplw 0,r1,r2 + cmplw 1,r1,r24 + crand 1,1,4 + bgt stack_ovf /* if r2 < r1 < r2+TASK_STRUCT_SIZE */ +#endif + lwz r24,0(r23) /* virtual address of handler */ + lwz r23,4(r23) /* where to go when done */ + mtspr SRR0,r24 + ori r20,r20,0x30 /* enable IR, DR */ + mtspr SRR1,r20 + mtlr r23 + SYNC + rfi /* jump to handler, enable MMU */ + +int_return: + mfmsr r28 /* Disable interrupts */ + li r4,0 + ori r4,r4,MSR_EE + andc r28,r28,r4 + SYNC /* Some chip revs need this... */ + mtmsr r28 + SYNC + lwz r2,_CTR(r1) + lwz r0,_LINK(r1) + mtctr r2 + mtlr r0 + lwz r2,_XER(r1) + lwz r0,_CCR(r1) + mtspr XER,r2 + mtcrf 0xFF,r0 + REST_10GPRS(3, r1) + REST_10GPRS(13, r1) + REST_8GPRS(23, r1) + REST_GPR(31, r1) + lwz r2,_NIP(r1) /* Restore environment */ + lwz r0,_MSR(r1) + mtspr SRR0,r2 + mtspr SRR1,r0 + lwz r0,GPR0(r1) + lwz r2,GPR2(r1) + lwz r1,GPR1(r1) + SYNC + rfi + +/* Cache functions. +*/ + .globl icache_enable +icache_enable: + mfspr r5,HID0 /* turn on the I cache. */ + ori r5,r5,0x8800 /* Instruction cache only! */ + addis r6,0,0xFFFF + ori r6,r6,0xF7FF + and r6,r5,r6 /* clear the invalidate bit */ + sync + mtspr HID0,r5 + mtspr HID0,r6 + isync + sync + blr + + .globl icache_disable +icache_disable: + mfspr r5,HID0 + addis r6,0,0xFFFF + ori r6,r6,0x7FFF + and r5,r5,r6 + sync + mtspr HID0,r5 + isync + sync + blr + + .globl icache_status +icache_status: + mfspr r3, HID0 + srwi r3, r3, 15 /* >>15 & 1=> select bit 16 */ + andi. r3, r3, 1 + blr + + .globl dcache_enable +dcache_enable: + mfspr r5,HID0 /* turn on the D cache. */ + ori r5,r5,0x4400 /* Data cache only! */ + mfspr r4, PVR /* read PVR */ + srawi r3, r4, 16 /* shift off the least 16 bits */ + cmpi 0, 0, r3, 0xC /* Check for Max pvr */ + bne NotMax + ori r5,r5,0x0040 /* setting the DCFA bit, for Max rev 1 errata */ +NotMax: + addis r6,0,0xFFFF + ori r6,r6,0xFBFF + and r6,r5,r6 /* clear the invalidate bit */ + sync + mtspr HID0,r5 + mtspr HID0,r6 + isync + sync + blr + + .globl dcache_disable +dcache_disable: + mfspr r5,HID0 + addis r6,0,0xFFFF + ori r6,r6,0xBFFF + and r5,r5,r6 + sync + mtspr HID0,r5 + isync + sync + blr + + .globl dcache_status +dcache_status: + mfspr r3, HID0 + srwi r3, r3, 14 /* >>14 & 1=> select bit 17 */ + andi. r3, r3, 1 + blr + + .globl dc_read +dc_read: +/*TODO : who uses this, what should it do? +*/ + blr + + + .globl get_pvr +get_pvr: + mfspr r3, PVR + blr + + +/*------------------------------------------------------------------------------*/ + +/* + * void relocate_code (addr_sp, gd, addr_moni) + * + * This "function" does not return, instead it continues in RAM + * after relocating the monitor code. + * + * r3 = dest + * r4 = src + * r5 = length in bytes + * r6 = cachelinesize + */ + .globl relocate_code +relocate_code: + + mr r1, r3 /* Set new stack pointer */ + mr r9, r4 /* Save copy of Global Data pointer */ + mr r10, r5 /* Save copy of Destination Address */ + + GET_GOT + mr r3, r5 /* Destination Address */ +#ifdef CONFIG_SYS_RAMBOOT + lis r4, CONFIG_SYS_SDRAM_BASE@h /* Source Address */ + ori r4, r4, CONFIG_SYS_SDRAM_BASE@l +#else + lis r4, CONFIG_SYS_MONITOR_BASE@h /* Source Address */ + ori r4, r4, CONFIG_SYS_MONITOR_BASE@l +#endif + lwz r5, GOT(__init_end) + sub r5, r5, r4 + li r6, CONFIG_SYS_CACHELINE_SIZE /* Cache Line Size */ + + /* + * Fix GOT pointer: + * + * New GOT-PTR = (old GOT-PTR - CONFIG_SYS_MONITOR_BASE) + Destination Address + * + * Offset: + */ + sub r15, r10, r4 + + /* First our own GOT */ + add r12, r12, r15 + /* the the one used by the C code */ + add r30, r30, r15 + + /* + * Now relocate code + */ + + cmplw cr1,r3,r4 + addi r0,r5,3 + srwi. r0,r0,2 + beq cr1,4f /* In place copy is not necessary */ + beq 7f /* Protect against 0 count */ + mtctr r0 + bge cr1,2f + + la r8,-4(r4) + la r7,-4(r3) +1: lwzu r0,4(r8) + stwu r0,4(r7) + bdnz 1b + b 4f + +2: slwi r0,r0,2 + add r8,r4,r0 + add r7,r3,r0 +3: lwzu r0,-4(r8) + stwu r0,-4(r7) + bdnz 3b + +4: +#if !defined(CONFIG_BMW) +/* Unlock the data cache and invalidate locked area */ + xor r0, r0, r0 + mtspr 1011, r0 + lis r4, CONFIG_SYS_INIT_RAM_ADDR@h + ori r4, r4, CONFIG_SYS_INIT_RAM_ADDR@l + li r0, 128 + mtctr r0 +41: + dcbi r0, r4 + addi r4, r4, 32 + bdnz 41b +#endif + +/* + * Now flush the cache: note that we must start from a cache aligned + * address. Otherwise we might miss one cache line. + */ + cmpwi r6,0 + add r5,r3,r5 + beq 7f /* Always flush prefetch queue in any case */ + subi r0,r6,1 + andc r3,r3,r0 + mr r4,r3 +5: dcbst 0,r4 + add r4,r4,r6 + cmplw r4,r5 + blt 5b + sync /* Wait for all dcbst to complete on bus */ + mr r4,r3 +6: icbi 0,r4 + add r4,r4,r6 + cmplw r4,r5 + blt 6b +7: sync /* Wait for all icbi to complete on bus */ + isync + +/* + * We are done. Do not return, instead branch to second part of board + * initialization, now running from RAM. + */ + + addi r0, r10, in_ram - _start + EXC_OFF_SYS_RESET + mtlr r0 + blr + +in_ram: + + /* + * Relocation Function, r12 point to got2+0x8000 + * + * Adjust got2 pointers, no need to check for 0, this code + * already puts a few entries in the table. + */ + li r0,__got2_entries@sectoff@l + la r3,GOT(_GOT2_TABLE_) + lwz r11,GOT(_GOT2_TABLE_) + mtctr r0 + sub r11,r3,r11 + addi r3,r3,-4 +1: lwzu r0,4(r3) + cmpwi r0,0 + beq- 2f + add r0,r0,r11 + stw r0,0(r3) +2: bdnz 1b + + /* + * Now adjust the fixups and the pointers to the fixups + * in case we need to move ourselves again. + */ + li r0,__fixup_entries@sectoff@l + lwz r3,GOT(_FIXUP_TABLE_) + cmpwi r0,0 + mtctr r0 + addi r3,r3,-4 + beq 4f +3: lwzu r4,4(r3) + lwzux r0,r4,r11 + add r0,r0,r11 + stw r10,0(r3) + stw r0,0(r4) + bdnz 3b +4: +clear_bss: + /* + * Now clear BSS segment + */ + lwz r3,GOT(__bss_start) + lwz r4,GOT(_end) + + cmplw 0, r3, r4 + beq 6f + + li r0, 0 +5: + stw r0, 0(r3) + addi r3, r3, 4 + cmplw 0, r3, r4 + blt 5b +6: + + mr r3, r9 /* Global Data pointer */ + mr r4, r10 /* Destination Address */ + bl board_init_r + + /* + * Copy exception vector code to low memory + * + * r3: dest_addr + * r7: source address, r8: end address, r9: target address + */ + .globl trap_init +trap_init: + mflr r4 /* save link register */ + GET_GOT + lwz r7, GOT(_start) + lwz r8, GOT(_end_of_vectors) + + li r9, 0x100 /* reset vector always at 0x100 */ + + cmplw 0, r7, r8 + bgelr /* return if r7>=r8 - just in case */ +1: + lwz r0, 0(r7) + stw r0, 0(r9) + addi r7, r7, 4 + addi r9, r9, 4 + cmplw 0, r7, r8 + bne 1b + + /* + * relocate `hdlr' and `int_return' entries + */ + li r7, .L_MachineCheck - _start + EXC_OFF_SYS_RESET + li r8, Alignment - _start + EXC_OFF_SYS_RESET +2: + bl trap_reloc + addi r7, r7, 0x100 /* next exception vector */ + cmplw 0, r7, r8 + blt 2b + + li r7, .L_Alignment - _start + EXC_OFF_SYS_RESET + bl trap_reloc + + li r7, .L_ProgramCheck - _start + EXC_OFF_SYS_RESET + bl trap_reloc + + li r7, .L_FPUnavailable - _start + EXC_OFF_SYS_RESET + li r8, SystemCall - _start + EXC_OFF_SYS_RESET +3: + bl trap_reloc + addi r7, r7, 0x100 /* next exception vector */ + cmplw 0, r7, r8 + blt 3b + + li r7, .L_SingleStep - _start + EXC_OFF_SYS_RESET + li r8, _end_of_vectors - _start + EXC_OFF_SYS_RESET +4: + bl trap_reloc + addi r7, r7, 0x100 /* next exception vector */ + cmplw 0, r7, r8 + blt 4b + + mtlr r4 /* restore link register */ + blr + + /* Setup the BAT registers. + */ +setup_bats: + lis r4, CONFIG_SYS_IBAT0L@h + ori r4, r4, CONFIG_SYS_IBAT0L@l + lis r3, CONFIG_SYS_IBAT0U@h + ori r3, r3, CONFIG_SYS_IBAT0U@l + mtspr IBAT0L, r4 + mtspr IBAT0U, r3 + isync + + lis r4, CONFIG_SYS_DBAT0L@h + ori r4, r4, CONFIG_SYS_DBAT0L@l + lis r3, CONFIG_SYS_DBAT0U@h + ori r3, r3, CONFIG_SYS_DBAT0U@l + mtspr DBAT0L, r4 + mtspr DBAT0U, r3 + isync + + lis r4, CONFIG_SYS_IBAT1L@h + ori r4, r4, CONFIG_SYS_IBAT1L@l + lis r3, CONFIG_SYS_IBAT1U@h + ori r3, r3, CONFIG_SYS_IBAT1U@l + mtspr IBAT1L, r4 + mtspr IBAT1U, r3 + isync + + lis r4, CONFIG_SYS_DBAT1L@h + ori r4, r4, CONFIG_SYS_DBAT1L@l + lis r3, CONFIG_SYS_DBAT1U@h + ori r3, r3, CONFIG_SYS_DBAT1U@l + mtspr DBAT1L, r4 + mtspr DBAT1U, r3 + isync + + lis r4, CONFIG_SYS_IBAT2L@h + ori r4, r4, CONFIG_SYS_IBAT2L@l + lis r3, CONFIG_SYS_IBAT2U@h + ori r3, r3, CONFIG_SYS_IBAT2U@l + mtspr IBAT2L, r4 + mtspr IBAT2U, r3 + isync + + lis r4, CONFIG_SYS_DBAT2L@h + ori r4, r4, CONFIG_SYS_DBAT2L@l + lis r3, CONFIG_SYS_DBAT2U@h + ori r3, r3, CONFIG_SYS_DBAT2U@l + mtspr DBAT2L, r4 + mtspr DBAT2U, r3 + isync + + lis r4, CONFIG_SYS_IBAT3L@h + ori r4, r4, CONFIG_SYS_IBAT3L@l + lis r3, CONFIG_SYS_IBAT3U@h + ori r3, r3, CONFIG_SYS_IBAT3U@l + mtspr IBAT3L, r4 + mtspr IBAT3U, r3 + isync + + lis r4, CONFIG_SYS_DBAT3L@h + ori r4, r4, CONFIG_SYS_DBAT3L@l + lis r3, CONFIG_SYS_DBAT3U@h + ori r3, r3, CONFIG_SYS_DBAT3U@l + mtspr DBAT3L, r4 + mtspr DBAT3U, r3 + isync + + /* Invalidate TLBs. + * -> for (val = 0; val < 0x20000; val+=0x1000) + * -> tlbie(val); + */ + lis r3, 0 + lis r5, 2 + +1: + tlbie r3 + addi r3, r3, 0x1000 + cmp 0, 0, r3, r5 + blt 1b + + blr diff --git a/arch/powerpc/cpu/mpc824x/traps.c b/arch/powerpc/cpu/mpc824x/traps.c new file mode 100644 index 0000000..163b983 --- /dev/null +++ b/arch/powerpc/cpu/mpc824x/traps.c @@ -0,0 +1,219 @@ +/* + * linux/arch/powerpc/kernel/traps.c + * + * Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org) + * + * Modified by Cort Dougan (cort@cs.nmt.edu) + * and Paul Mackerras (paulus@cs.anu.edu.au) + * + * (C) Copyright 2000 + * Wolfgang Denk, DENX Software Engineering, wd@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 + */ + +/* + * This file handles the architecture-dependent parts of hardware exceptions + */ + +#include <common.h> +#include <asm/processor.h> + +/* Returns 0 if exception not found and fixup otherwise. */ +extern unsigned long search_exception_table(unsigned long); + +/* THIS NEEDS CHANGING to use the board info structure. +*/ +#define END_OF_MEM 0x00400000 + +/* + * Trap & Exception support + */ + +void +print_backtrace(unsigned long *sp) +{ + int cnt = 0; + unsigned long i; + + printf("Call backtrace: "); + while (sp) { + if ((uint)sp > END_OF_MEM) + break; + + i = sp[1]; + if (cnt++ % 7 == 0) + printf("\n"); + printf("%08lX ", i); + if (cnt > 32) break; + sp = (unsigned long *)*sp; + } + printf("\n"); +} + +void show_regs(struct pt_regs * regs) +{ + int i; + + printf("NIP: %08lX XER: %08lX LR: %08lX REGS: %p TRAP: %04lx DAR: %08lX\n", + regs->nip, regs->xer, regs->link, regs, regs->trap, regs->dar); + printf("MSR: %08lx EE: %01x PR: %01x FP: %01x ME: %01x IR/DR: %01x%01x\n", + regs->msr, regs->msr&MSR_EE ? 1 : 0, regs->msr&MSR_PR ? 1 : 0, + regs->msr & MSR_FP ? 1 : 0,regs->msr&MSR_ME ? 1 : 0, + regs->msr&MSR_IR ? 1 : 0, + regs->msr&MSR_DR ? 1 : 0); + + printf("\n"); + for (i = 0; i < 32; i++) { + if ((i % 8) == 0) + { + printf("GPR%02d: ", i); + } + + printf("%08lX ", regs->gpr[i]); + if ((i % 8) == 7) + { + printf("\n"); + } + } +} + + +void +_exception(int signr, struct pt_regs *regs) +{ + show_regs(regs); + print_backtrace((unsigned long *)regs->gpr[1]); + panic("Exception in kernel pc %lx signal %d",regs->nip,signr); +} + +void +MachineCheckException(struct pt_regs *regs) +{ + unsigned long fixup; + + /* Probing PCI using config cycles cause this exception + * when a device is not present. Catch it and return to + * the PCI exception handler. + */ + if ((fixup = search_exception_table(regs->nip)) != 0) { + regs->nip = fixup; + return; + } + + printf("Machine check in kernel mode.\n"); + printf("Caused by (from msr): "); + printf("regs %p ",regs); + switch( regs->msr & 0x000F0000) { + case (0x80000000>>12): + printf("Machine check signal - probably due to mm fault\n" + "with mmu off\n"); + break; + case (0x80000000>>13): + printf("Transfer error ack signal\n"); + break; + case (0x80000000>>14): + printf("Data parity signal\n"); + break; + case (0x80000000>>15): + printf("Address parity signal\n"); + break; + default: + printf("Unknown values in msr\n"); + } + show_regs(regs); + print_backtrace((unsigned long *)regs->gpr[1]); + panic("machine check"); +} + +void +AlignmentException(struct pt_regs *regs) +{ + show_regs(regs); + print_backtrace((unsigned long *)regs->gpr[1]); + panic("Alignment Exception"); +} + +void +ProgramCheckException(struct pt_regs *regs) +{ + show_regs(regs); + print_backtrace((unsigned long *)regs->gpr[1]); + panic("Program Check Exception"); +} + +void +SoftEmuException(struct pt_regs *regs) +{ + show_regs(regs); + print_backtrace((unsigned long *)regs->gpr[1]); + panic("Software Emulation Exception"); +} + + +void +UnknownException(struct pt_regs *regs) +{ + printf("Bad trap at PC: %lx, SR: %lx, vector=%lx\n", + regs->nip, regs->msr, regs->trap); + _exception(0, regs); +} + +#if defined(CONFIG_CMD_BEDBUG) +extern void do_bedbug_breakpoint(struct pt_regs *); +#endif + +void +DebugException(struct pt_regs *regs) +{ + + printf("Debugger trap at @ %lx\n", regs->nip ); + show_regs(regs); +#if defined(CONFIG_CMD_BEDBUG) + do_bedbug_breakpoint( regs ); +#endif +} + +/* Probe an address by reading. If not present, return -1, otherwise + * return 0. + */ +int +addr_probe(uint *addr) +{ +#if 0 + int retval; + + __asm__ __volatile__( \ + "1: lwz %0,0(%1)\n" \ + " eieio\n" \ + " li %0,0\n" \ + "2:\n" \ + ".section .fixup,\"ax\"\n" \ + "3: li %0,-1\n" \ + " b 2b\n" \ + ".section __ex_table,\"a\"\n" \ + " .align 2\n" \ + " .long 1b,3b\n" \ + ".text" \ + : "=r" (retval) : "r"(addr)); + + return (retval); +#endif + return 0; +} diff --git a/arch/powerpc/cpu/mpc824x/u-boot.lds b/arch/powerpc/cpu/mpc824x/u-boot.lds new file mode 100644 index 0000000..d1fcd7c --- /dev/null +++ b/arch/powerpc/cpu/mpc824x/u-boot.lds @@ -0,0 +1,122 @@ +/* + * (C) Copyright 2001-2007 + * Wolfgang Denk, DENX Software Engineering, wd@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 + */ + +OUTPUT_ARCH(powerpc) +/* Do we need any of these for elf? + __DYNAMIC = 0; */ +SECTIONS +{ + /* Read-only sections, merged into text segment: */ + . = + SIZEOF_HEADERS; + .interp : { *(.interp) } + .hash : { *(.hash) } + .dynsym : { *(.dynsym) } + .dynstr : { *(.dynstr) } + .rel.text : { *(.rel.text) } + .rela.text : { *(.rela.text) } + .rel.data : { *(.rel.data) } + .rela.data : { *(.rela.data) } + .rel.rodata : { *(.rel.rodata) } + .rela.rodata : { *(.rela.rodata) } + .rel.got : { *(.rel.got) } + .rela.got : { *(.rela.got) } + .rel.ctors : { *(.rel.ctors) } + .rela.ctors : { *(.rela.ctors) } + .rel.dtors : { *(.rel.dtors) } + .rela.dtors : { *(.rela.dtors) } + .rel.bss : { *(.rel.bss) } + .rela.bss : { *(.rela.bss) } + .rel.plt : { *(.rel.plt) } + .rela.plt : { *(.rela.plt) } + .init : { *(.init) } + .plt : { *(.plt) } + .text : + { + arch/powerpc/cpu/mpc824x/start.o (.text) + *(.text) + *(.got1) + . = ALIGN(16); + *(.eh_frame) + *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.rodata*))) + } + .fini : { *(.fini) } =0 + .ctors : { *(.ctors) } + .dtors : { *(.dtors) } + + /* Read-write section, merged into data segment: */ + . = (. + 0x0FFF) & 0xFFFFF000; + _erotext = .; + PROVIDE (erotext = .); + .reloc : + { + *(.got) + _GOT2_TABLE_ = .; + *(.got2) + _FIXUP_TABLE_ = .; + *(.fixup) + } + __got2_entries = (_FIXUP_TABLE_ - _GOT2_TABLE_) >> 2; + __fixup_entries = (. - _FIXUP_TABLE_) >> 2; + + .data : + { + *(.data) + *(.data1) + *(.sdata) + *(.sdata2) + *(.dynamic) + CONSTRUCTORS + } + _edata = .; + PROVIDE (edata = .); + + . = .; + __u_boot_cmd_start = .; + .u_boot_cmd : { *(.u_boot_cmd) } + __u_boot_cmd_end = .; + + + . = .; + __start___ex_table = .; + __ex_table : { *(__ex_table) } + __stop___ex_table = .; + + . = ALIGN(4096); + __init_begin = .; + .text.init : { *(.text.init) } + .data.init : { *(.data.init) } + . = ALIGN(4096); + __init_end = .; + + __bss_start = .; + .bss (NOLOAD) : + { + *(.sbss) *(.scommon) + *(.dynbss) + *(.bss) + *(COMMON) + . = ALIGN(4); + } + _end = . ; + PROVIDE (end = .); +} diff --git a/arch/powerpc/cpu/mpc8260/Makefile b/arch/powerpc/cpu/mpc8260/Makefile new file mode 100644 index 0000000..9f0c2dd --- /dev/null +++ b/arch/powerpc/cpu/mpc8260/Makefile @@ -0,0 +1,53 @@ +# +# (C) Copyright 2000-2008 +# Wolfgang Denk, DENX Software Engineering, wd@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 $(TOPDIR)/config.mk + +LIB = $(obj)lib$(CPU).a + +START = start.o kgdb.o +COBJS = traps.o serial_smc.o serial_scc.o cpu.o cpu_init.o speed.o \ + interrupts.o ether_fcc.o i2c.o commproc.o \ + bedbug_603e.o pci.o spi.o + +COBJS-$(CONFIG_ETHER_ON_SCC) = ether_scc.o + +COBJS += $(COBJS-y) + +SRCS := $(START:.o=.S) $(SOBJS:.o=.S) $(COBJS:.o=.c) +OBJS := $(addprefix $(obj),$(SOBJS) $(COBJS)) +START := $(addprefix $(obj),$(START)) + +all: $(obj).depend $(START) $(LIB) + +$(LIB): $(OBJS) + $(AR) $(ARFLAGS) $@ $(OBJS) $(obj)kgdb.o + +######################################################################### + +# defines $(obj).depend target +include $(SRCTREE)/rules.mk + +sinclude $(obj).depend + +######################################################################### diff --git a/arch/powerpc/cpu/mpc8260/bedbug_603e.c b/arch/powerpc/cpu/mpc8260/bedbug_603e.c new file mode 100644 index 0000000..c969ff6 --- /dev/null +++ b/arch/powerpc/cpu/mpc8260/bedbug_603e.c @@ -0,0 +1,238 @@ +/* + * Bedbug Functions specific to the MPC603e core + */ + +#include <common.h> +#include <command.h> +#include <linux/ctype.h> +#include <bedbug/type.h> +#include <bedbug/bedbug.h> +#include <bedbug/regs.h> +#include <bedbug/ppc.h> + +#if defined(CONFIG_CMD_BEDBUG) \ + && (defined(CONFIG_MPC824X) || defined(CONFIG_MPC8260)) + +#define MAX_BREAK_POINTS 1 + +extern CPU_DEBUG_CTX bug_ctx; + +void bedbug603e_init __P((void)); +void bedbug603e_do_break __P((cmd_tbl_t*,int,int,char*[])); +void bedbug603e_break_isr __P((struct pt_regs*)); +int bedbug603e_find_empty __P((void)); +int bedbug603e_set __P((int,unsigned long)); +int bedbug603e_clear __P((int)); + + +/* ====================================================================== + * Initialize the global bug_ctx structure for the processor. Clear all + * of the breakpoints. + * ====================================================================== */ + +void bedbug603e_init( void ) +{ + int i; + /* -------------------------------------------------- */ + + bug_ctx.hw_debug_enabled = 0; + bug_ctx.stopped = 0; + bug_ctx.current_bp = 0; + bug_ctx.regs = NULL; + + bug_ctx.do_break = bedbug603e_do_break; + bug_ctx.break_isr = bedbug603e_break_isr; + bug_ctx.find_empty = bedbug603e_find_empty; + bug_ctx.set = bedbug603e_set; + bug_ctx.clear = bedbug603e_clear; + + for( i = 1; i <= MAX_BREAK_POINTS; ++i ) + (*bug_ctx.clear)( i ); + + puts ("BEDBUG:ready\n"); + return; +} /* bedbug_init_breakpoints */ + + + +/* ====================================================================== + * Set/clear/show the hardware breakpoint for the 603e. The "off" + * string will disable a specific breakpoint. The "show" string will + * display the current breakpoints. Otherwise an address will set a + * breakpoint at that address. Setting a breakpoint uses the CPU-specific + * set routine which will assign a breakpoint number. + * ====================================================================== */ + +void bedbug603e_do_break (cmd_tbl_t *cmdtp, int flag, int argc, + char *argv[]) +{ + long addr; /* Address to break at */ + int which_bp; /* Breakpoint number */ + /* -------------------------------------------------- */ + + if (argc < 2) + { + cmd_usage(cmdtp); + return; + } + + /* Turn off a breakpoint */ + + if( strcmp( argv[ 1 ], "off" ) == 0 ) + { + if( bug_ctx.hw_debug_enabled == 0 ) + { + puts ( "No breakpoints enabled\n" ); + return; + } + + which_bp = simple_strtoul( argv[ 2 ], NULL, 10 ); + + if( bug_ctx.clear ) + (*bug_ctx.clear)( which_bp ); + + printf( "Breakpoint %d removed\n", which_bp ); + return; + } + + /* Show a list of breakpoints */ + + if( strcmp( argv[ 1 ], "show" ) == 0 ) + { + for( which_bp = 1; which_bp <= MAX_BREAK_POINTS; ++which_bp ) + { + + addr = GET_IABR(); + + printf( "Breakpoint [%d]: ", which_bp ); + if( (addr & 0x00000002) == 0 ) + puts ( "NOT SET\n" ); + else + disppc( (unsigned char *)(addr & 0xFFFFFFFC), 0, 1, bedbug_puts, F_RADHEX ); + } + return; + } + + /* Set a breakpoint at the address */ + + if(!(( isdigit( argv[ 1 ][ 0 ] )) || + (( argv[ 1 ][ 0 ] >= 'a' ) && ( argv[ 1 ][ 0 ] <= 'f' )) || + (( argv[ 1 ][ 0 ] >= 'A' ) && ( argv[ 1 ][ 0 ] <= 'F' )))) + { + cmd_usage(cmdtp); + return; + } + + addr = simple_strtoul( argv[ 1 ], NULL, 16 ); + + if(( bug_ctx.set ) && ( which_bp = (*bug_ctx.set)( 0, addr )) > 0 ) + { + printf( "Breakpoint [%d]: ", which_bp ); + disppc( (unsigned char *)addr, 0, 1, bedbug_puts, F_RADHEX ); + } + + return; +} /* bedbug603e_do_break */ + + + +/* ====================================================================== + * Handle a breakpoint. Enter a mini main loop. Stay in the loop until + * the stopped flag in the debug context is cleared. + * ====================================================================== */ + +void bedbug603e_break_isr( struct pt_regs *regs ) +{ + unsigned long addr; /* Address stopped at */ + /* -------------------------------------------------- */ + + bug_ctx.current_bp = 1; + addr = GET_IABR() & 0xFFFFFFFC; + + bedbug_main_loop( addr, regs ); + return; +} /* bedbug603e_break_isr */ + + + +/* ====================================================================== + * See if the hardware breakpoint is available. + * ====================================================================== */ + +int bedbug603e_find_empty( void ) +{ + /* -------------------------------------------------- */ + + if( (GET_IABR() && 0x00000002) == 0 ) + return 1; + + return 0; +} /* bedbug603e_find_empty */ + + + +/* ====================================================================== + * Set a breakpoint. If 'which_bp' is zero then find an unused breakpoint + * number, otherwise reassign the given breakpoint. If hardware debugging + * is not enabled, then turn it on via the MSR and DBCR0. Set the break + * address in the IABR register. + * ====================================================================== */ + +int bedbug603e_set( int which_bp, unsigned long addr ) +{ + /* -------------------------------------------------- */ + + if(( addr & 0x00000003 ) != 0 ) + { + puts ( "Breakpoints must be on a 32 bit boundary\n" ); + return 0; + } + + /* Only look if which_bp == 0, else use which_bp */ + if(( bug_ctx.find_empty ) && ( !which_bp ) && + ( which_bp = (*bug_ctx.find_empty)()) == 0 ) + { + puts ( "All breakpoints in use\n" ); + return 0; + } + + if( which_bp < 1 || which_bp > MAX_BREAK_POINTS ) + { + printf( "Invalid break point # %d\n", which_bp ); + return 0; + } + + if( ! bug_ctx.hw_debug_enabled ) + { + bug_ctx.hw_debug_enabled = 1; + } + + SET_IABR( addr | 0x00000002 ); + + return which_bp; +} /* bedbug603e_set */ + + + +/* ====================================================================== + * Disable a specific breakoint by setting the IABR register to zero. + * ====================================================================== */ + +int bedbug603e_clear( int which_bp ) +{ + /* -------------------------------------------------- */ + + if( which_bp < 1 || which_bp > MAX_BREAK_POINTS ) + { + printf( "Invalid break point # (%d)\n", which_bp ); + return -1; + } + + SET_IABR( 0 ); + + return 0; +} /* bedbug603e_clear */ + + +/* ====================================================================== */ +#endif diff --git a/arch/powerpc/cpu/mpc8260/commproc.c b/arch/powerpc/cpu/mpc8260/commproc.c new file mode 100644 index 0000000..111a67c --- /dev/null +++ b/arch/powerpc/cpu/mpc8260/commproc.c @@ -0,0 +1,221 @@ +/* + * This file is based on "arch/powerpc/8260_io/commproc.c" - here is it's + * copyright notice: + * + * General Purpose functions for the global management of the + * 8260 Communication Processor Module. + * Copyright (c) 1999 Dan Malek (dmalek@jlc.net) + * Copyright (c) 2000 MontaVista Software, Inc (source@mvista.com) + * 2.3.99 Updates + * + * In addition to the individual control of the communication + * channels, there are a few functions that globally affect the + * communication processor. + * + * Buffer descriptors must be allocated from the dual ported memory + * space. The allocator for that is here. When the communication + * process is reset, we reclaim the memory available. There is + * currently no deallocator for this memory. + */ +#include <common.h> +#include <asm/cpm_8260.h> + +DECLARE_GLOBAL_DATA_PTR; + +void +m8260_cpm_reset(void) +{ + volatile immap_t *immr = (immap_t *)CONFIG_SYS_IMMR; + volatile ulong count; + + /* Reclaim the DP memory for our use. + */ + gd->dp_alloc_base = CPM_DATAONLY_BASE; + gd->dp_alloc_top = gd->dp_alloc_base + CPM_DATAONLY_SIZE; + + /* + * Reset CPM + */ + immr->im_cpm.cp_cpcr = CPM_CR_RST; + count = 0; + do { /* Spin until command processed */ + __asm__ __volatile__ ("eieio"); + } while ((immr->im_cpm.cp_cpcr & CPM_CR_FLG) && ++count < 1000000); + +#ifdef CONFIG_HARD_I2C + *((unsigned short*)(&immr->im_dprambase[PROFF_I2C_BASE])) = 0; +#endif +} + +/* Allocate some memory from the dual ported ram. + * To help protocols with object alignment restrictions, we do that + * if they ask. + */ +uint +m8260_cpm_dpalloc(uint size, uint align) +{ + volatile immap_t *immr = (immap_t *)CONFIG_SYS_IMMR; + uint retloc; + uint align_mask, off; + uint savebase; + + align_mask = align - 1; + savebase = gd->dp_alloc_base; + + if ((off = (gd->dp_alloc_base & align_mask)) != 0) + gd->dp_alloc_base += (align - off); + + if ((off = size & align_mask) != 0) + size += align - off; + + if ((gd->dp_alloc_base + size) >= gd->dp_alloc_top) { + gd->dp_alloc_base = savebase; + panic("m8260_cpm_dpalloc: ran out of dual port ram!"); + } + + retloc = gd->dp_alloc_base; + gd->dp_alloc_base += size; + + memset((void *)&immr->im_dprambase[retloc], 0, size); + + return(retloc); +} + +/* We also own one page of host buffer space for the allocation of + * UART "fifos" and the like. + */ +uint +m8260_cpm_hostalloc(uint size, uint align) +{ + /* the host might not even have RAM yet - just use dual port RAM */ + return (m8260_cpm_dpalloc(size, align)); +} + +/* Set a baud rate generator. This needs lots of work. There are + * eight BRGs, which can be connected to the CPM channels or output + * as clocks. The BRGs are in two different block of internal + * memory mapped space. + * The baud rate clock is the system clock divided by something. + * It was set up long ago during the initial boot phase and is + * is given to us. + * Baud rate clocks are zero-based in the driver code (as that maps + * to port numbers). Documentation uses 1-based numbering. + */ +#define BRG_INT_CLK gd->brg_clk +#define BRG_UART_CLK (BRG_INT_CLK / 16) + +/* This function is used by UARTs, or anything else that uses a 16x + * oversampled clock. + */ +void +m8260_cpm_setbrg(uint brg, uint rate) +{ + volatile immap_t *immr = (immap_t *)CONFIG_SYS_IMMR; + volatile uint *bp; + uint cd = BRG_UART_CLK / rate; + + if ((BRG_UART_CLK % rate) < (rate / 2)) + cd--; + if (brg < 4) { + bp = (uint *)&immr->im_brgc1; + } + else { + bp = (uint *)&immr->im_brgc5; + brg -= 4; + } + bp += brg; + *bp = (cd << 1) | CPM_BRG_EN; +} + +/* This function is used to set high speed synchronous baud rate + * clocks. + */ +void +m8260_cpm_fastbrg(uint brg, uint rate, int div16) +{ + volatile immap_t *immr = (immap_t *)CONFIG_SYS_IMMR; + volatile uint *bp; + + /* This is good enough to get SMCs running..... + */ + if (brg < 4) { + bp = (uint *)&immr->im_brgc1; + } + else { + bp = (uint *)&immr->im_brgc5; + brg -= 4; + } + bp += brg; + *bp = (((((BRG_INT_CLK+rate-1)/rate)-1)&0xfff)<<1)|CPM_BRG_EN; + if (div16) + *bp |= CPM_BRG_DIV16; +} + +/* This function is used to set baud rate generators using an external + * clock source and 16x oversampling. + */ + +void +m8260_cpm_extcbrg(uint brg, uint rate, uint extclk, int pinsel) +{ + volatile immap_t *immr = (immap_t *)CONFIG_SYS_IMMR; + volatile uint *bp; + + if (brg < 4) { + bp = (uint *)&immr->im_brgc1; + } + else { + bp = (uint *)&immr->im_brgc5; + brg -= 4; + } + bp += brg; + *bp = ((((((extclk/16)+rate-1)/rate)-1)&0xfff)<<1)|CPM_BRG_EN; + if (pinsel == 0) + *bp |= CPM_BRG_EXTC_CLK3_9; + else + *bp |= CPM_BRG_EXTC_CLK5_15; +} + +#if defined(CONFIG_POST) || defined(CONFIG_LOGBUFFER) + +void post_word_store (ulong a) +{ + volatile ulong *save_addr = + (volatile ulong *)(CONFIG_SYS_IMMR + CPM_POST_WORD_ADDR); + + *save_addr = a; +} + +ulong post_word_load (void) +{ + volatile ulong *save_addr = + (volatile ulong *)(CONFIG_SYS_IMMR + CPM_POST_WORD_ADDR); + + return *save_addr; +} + +#endif /* CONFIG_POST || CONFIG_LOGBUFFER*/ + +#ifdef CONFIG_BOOTCOUNT_LIMIT + +void bootcount_store (ulong a) +{ + volatile ulong *save_addr = + (volatile ulong *)(CONFIG_SYS_IMMR + CPM_BOOTCOUNT_ADDR); + + save_addr[0] = a; + save_addr[1] = BOOTCOUNT_MAGIC; +} + +ulong bootcount_load (void) +{ + volatile ulong *save_addr = + (volatile ulong *)(CONFIG_SYS_IMMR + CPM_BOOTCOUNT_ADDR); + + if (save_addr[1] != BOOTCOUNT_MAGIC) + return 0; + else + return save_addr[0]; +} + +#endif /* CONFIG_BOOTCOUNT_LIMIT */ diff --git a/arch/powerpc/cpu/mpc8260/config.mk b/arch/powerpc/cpu/mpc8260/config.mk new file mode 100644 index 0000000..a9bb688 --- /dev/null +++ b/arch/powerpc/cpu/mpc8260/config.mk @@ -0,0 +1,30 @@ +# +# (C) Copyright 2000 +# Wolfgang Denk, DENX Software Engineering, wd@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 +# + +PLATFORM_RELFLAGS += -fPIC -meabi + +PLATFORM_CPPFLAGS += -DCONFIG_8260 -DCONFIG_CPM2 -ffixed-r2 \ + -mstring -mcpu=603e -mmultiple + +# Use default linker script. Board port can override in board/*/config.mk +LDSCRIPT := $(SRCTREE)/arch/powerpc/cpu/mpc8260/u-boot.lds diff --git a/arch/powerpc/cpu/mpc8260/cpu.c b/arch/powerpc/cpu/mpc8260/cpu.c new file mode 100644 index 0000000..aedbf29 --- /dev/null +++ b/arch/powerpc/cpu/mpc8260/cpu.c @@ -0,0 +1,338 @@ +/* + * (C) Copyright 2000-2006 + * Wolfgang Denk, DENX Software Engineering, wd@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 + */ + +/* + * CPU specific code for the MPC825x / MPC826x / MPC827x / MPC828x + * + * written or collected and sometimes rewritten by + * Magnus Damm <damm@bitsmart.com> + * + * modified by + * Wolfgang Denk <wd@denx.de> + * + * modified for 8260 by + * Murray Jensen <Murray.Jensen@cmst.csiro.au> + * + * added 8260 masks by + * Marius Groeger <mag@sysgo.de> + * + * added HiP7 (824x/827x/8280) processors support by + * Yuli Barcohen <yuli@arabellasw.com> + */ + +#include <common.h> +#include <watchdog.h> +#include <command.h> +#include <mpc8260.h> +#include <netdev.h> +#include <asm/processor.h> +#include <asm/cpm_8260.h> + +#if defined(CONFIG_OF_LIBFDT) +#include <libfdt.h> +#include <libfdt_env.h> +#include <fdt_support.h> +#endif + +DECLARE_GLOBAL_DATA_PTR; + +#if defined(CONFIG_GET_CPU_STR_F) +extern int get_cpu_str_f (char *buf); +#endif + +int checkcpu (void) +{ + volatile immap_t *immap = (immap_t *) CONFIG_SYS_IMMR; + ulong clock = gd->cpu_clk; + uint pvr = get_pvr (); + uint immr, rev, m, k; + char buf[32]; + + puts ("CPU: "); + + switch (pvr) { + case PVR_8260: + case PVR_8260_HIP3: + k = 3; + break; + case PVR_8260_HIP4: + k = 4; + break; + case PVR_8260_HIP7R1: + case PVR_8260_HIP7RA: + case PVR_8260_HIP7: + k = 7; + break; + default: + return -1; /* whoops! not an MPC8260 */ + } + rev = pvr & 0xff; + + immr = immap->im_memctl.memc_immr; + if ((immr & IMMR_ISB_MSK) != CONFIG_SYS_IMMR) + return -1; /* whoops! someone moved the IMMR */ + +#if defined(CONFIG_GET_CPU_STR_F) + get_cpu_str_f (buf); + printf ("%s (HiP%d Rev %02x, Mask ", buf, k, rev); +#else + printf (CPU_ID_STR " (HiP%d Rev %02x, Mask ", k, rev); +#endif + + /* + * the bottom 16 bits of the immr are the Part Number and Mask Number + * (4-34); the 16 bits at PROFF_REVNUM (0x8af0) in dual port ram is the + * RISC Microcode Revision Number (13-10). + * For the 8260, Motorola doesn't include the Microcode Revision + * in the mask. + */ + m = immr & (IMMR_PARTNUM_MSK | IMMR_MASKNUM_MSK); + k = *((ushort *) & immap->im_dprambase[PROFF_REVNUM]); + + switch (m) { + case 0x0000: + puts ("0.2 2J24M"); + break; + case 0x0010: + puts ("A.0 K22A"); + break; + case 0x0011: + puts ("A.1 1K22A-XC"); + break; + case 0x0001: + puts ("B.1 1K23A"); + break; + case 0x0021: + puts ("B.2 2K23A-XC"); + break; + case 0x0023: + puts ("B.3 3K23A"); + break; + case 0x0024: + puts ("C.2 6K23A"); + break; + case 0x0060: + puts ("A.0(A) 2K25A"); + break; + case 0x0062: + puts ("B.1 4K25A"); + break; + case 0x0064: + puts ("C.0 5K25A"); + break; + case 0x0A00: + puts ("0.0 0K49M"); + break; + case 0x0A01: + puts ("0.1 1K49M"); + break; + case 0x0A10: + puts ("1.0 1K49M"); + break; + case 0x0C00: + puts ("0.0 0K50M"); + break; + case 0x0C10: + puts ("1.0 1K50M"); + break; + case 0x0D00: + puts ("0.0 0K50M"); + break; + case 0x0D10: + puts ("1.0 1K50M"); + break; + default: + printf ("unknown [immr=0x%04x,k=0x%04x]", m, k); + break; + } + + printf (") at %s MHz\n", strmhz (buf, clock)); + + return 0; +} + +/* ------------------------------------------------------------------------- */ +/* configures a UPM by writing into the UPM RAM array */ +/* uses bank 11 and a dummy physical address (=BRx_BA_MSK) */ +/* NOTE: the physical address chosen must not overlap into any other area */ +/* mapped by the memory controller because bank 11 has the lowest priority */ + +void upmconfig (uint upm, uint * table, uint size) +{ + volatile immap_t *immap = (immap_t *) CONFIG_SYS_IMMR; + volatile memctl8260_t *memctl = &immap->im_memctl; + volatile uchar *dummy = (uchar *) BRx_BA_MSK; /* set all BA bits */ + uint i; + + /* first set up bank 11 to reference the correct UPM at a dummy address */ + + memctl->memc_or11 = ORxU_AM_MSK; /* set all AM bits */ + + switch (upm) { + + case UPMA: + memctl->memc_br11 = + ((uint)dummy & BRx_BA_MSK) | BRx_PS_32 | BRx_MS_UPMA | + BRx_V; + memctl->memc_mamr = MxMR_OP_WARR; + break; + + case UPMB: + memctl->memc_br11 = + ((uint)dummy & BRx_BA_MSK) | BRx_PS_32 | BRx_MS_UPMB | + BRx_V; + memctl->memc_mbmr = MxMR_OP_WARR; + break; + + case UPMC: + memctl->memc_br11 = + ((uint)dummy & BRx_BA_MSK) | BRx_PS_32 | BRx_MS_UPMC | + BRx_V; + memctl->memc_mcmr = MxMR_OP_WARR; + break; + + default: + panic ("upmconfig passed invalid UPM number (%u)\n", upm); + break; + + } + + /* + * at this point, the dummy address is set up to access the selected UPM, + * the MAD pointer is zero, and the MxMR OP is set for writing to RAM + * + * now we simply load the mdr with each word and poke the dummy address. + * the MAD is incremented on each access. + */ + + for (i = 0; i < size; i++) { + memctl->memc_mdr = table[i]; + *dummy = 0; + } + + /* now kill bank 11 */ + memctl->memc_br11 = 0; +} + +/* ------------------------------------------------------------------------- */ + +#if !defined(CONFIG_HAVE_OWN_RESET) +int +do_reset (cmd_tbl_t * cmdtp, int flag, int argc, char *argv[]) +{ + ulong msr, addr; + + volatile immap_t *immap = (immap_t *) CONFIG_SYS_IMMR; + + immap->im_clkrst.car_rmr = RMR_CSRE; /* Checkstop Reset enable */ + + /* Interrupts and MMU off */ + __asm__ __volatile__ ("mfmsr %0":"=r" (msr):); + + msr &= ~(MSR_ME | MSR_EE | MSR_IR | MSR_DR); + __asm__ __volatile__ ("mtmsr %0"::"r" (msr)); + + /* + * Trying to execute the next instruction at a non-existing address + * should cause a machine check, resulting in reset + */ +#ifdef CONFIG_SYS_RESET_ADDRESS + addr = CONFIG_SYS_RESET_ADDRESS; +#else + /* + * note: when CONFIG_SYS_MONITOR_BASE points to a RAM address, CONFIG_SYS_MONITOR_BASE + * - sizeof (ulong) is usually a valid address. Better pick an address + * known to be invalid on your system and assign it to CONFIG_SYS_RESET_ADDRESS. + */ + addr = CONFIG_SYS_MONITOR_BASE - sizeof (ulong); +#endif + ((void (*)(void)) addr) (); + return 1; + +} +#endif /* CONFIG_HAVE_OWN_RESET */ + +/* ------------------------------------------------------------------------- */ + +/* + * Get timebase clock frequency (like cpu_clk in Hz) + * + */ +unsigned long get_tbclk (void) +{ + ulong tbclk; + + tbclk = (gd->bus_clk + 3L) / 4L; + + return (tbclk); +} + +/* ------------------------------------------------------------------------- */ + +#if defined(CONFIG_WATCHDOG) +void watchdog_reset (void) +{ + int re_enable = disable_interrupts (); + + reset_8260_watchdog ((immap_t *) CONFIG_SYS_IMMR); + if (re_enable) + enable_interrupts (); +} +#endif /* CONFIG_WATCHDOG */ + +/* ------------------------------------------------------------------------- */ +#if defined(CONFIG_OF_LIBFDT) && defined (CONFIG_OF_BOARD_SETUP) +void ft_cpu_setup (void *blob, bd_t *bd) +{ +#if defined(CONFIG_HAS_ETH0) || defined(CONFIG_HAS_ETH1) ||\ + defined(CONFIG_HAS_ETH2) || defined(CONFIG_HAS_ETH3) + fdt_fixup_ethernet(blob); +#endif + + do_fixup_by_compat_u32(blob, "fsl,cpm2-brg", + "clock-frequency", bd->bi_brgfreq, 1); + + do_fixup_by_prop_u32(blob, "device_type", "cpu", 4, + "bus-frequency", bd->bi_busfreq, 1); + do_fixup_by_prop_u32(blob, "device_type", "cpu", 4, + "timebase-frequency", OF_TBCLK, 1); + do_fixup_by_prop_u32(blob, "device_type", "cpu", 4, + "clock-frequency", bd->bi_intfreq, 1); + fdt_fixup_memory(blob, (u64)bd->bi_memstart, (u64)bd->bi_memsize); +} +#endif /* CONFIG_OF_LIBFDT */ + +/* + * Initializes on-chip ethernet controllers. + * to override, implement board_eth_init() + */ +int cpu_eth_init(bd_t *bis) +{ +#if defined(CONFIG_ETHER_ON_FCC) + fec_initialize(bis); +#endif +#if defined(CONFIG_ETHER_ON_SCC) + mpc82xx_scc_enet_initialize(bis); +#endif + return 0; +} diff --git a/arch/powerpc/cpu/mpc8260/cpu_init.c b/arch/powerpc/cpu/mpc8260/cpu_init.c new file mode 100644 index 0000000..1d52773 --- /dev/null +++ b/arch/powerpc/cpu/mpc8260/cpu_init.c @@ -0,0 +1,292 @@ +/* + * (C) Copyright 2000-2002 + * Wolfgang Denk, DENX Software Engineering, wd@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> +#include <mpc8260.h> +#include <asm/cpm_8260.h> +#include <ioports.h> + +DECLARE_GLOBAL_DATA_PTR; + +#if defined(CONFIG_BOARD_GET_CPU_CLK_F) +extern unsigned long board_get_cpu_clk_f (void); +#endif + +static void config_8260_ioports (volatile immap_t * immr) +{ + int portnum; + + for (portnum = 0; portnum < 4; portnum++) { + uint pmsk = 0, + ppar = 0, + psor = 0, + pdir = 0, + podr = 0, + pdat = 0; + iop_conf_t *iopc = (iop_conf_t *) & iop_conf_tab[portnum][0]; + iop_conf_t *eiopc = iopc + 32; + uint msk = 1; + + /* + * NOTE: + * index 0 refers to pin 31, + * index 31 refers to pin 0 + */ + while (iopc < eiopc) { + if (iopc->conf) { + pmsk |= msk; + if (iopc->ppar) + ppar |= msk; + if (iopc->psor) + psor |= msk; + if (iopc->pdir) + pdir |= msk; + if (iopc->podr) + podr |= msk; + if (iopc->pdat) + pdat |= msk; + } + + msk <<= 1; + iopc++; + } + + if (pmsk != 0) { + volatile ioport_t *iop = ioport_addr (immr, portnum); + uint tpmsk = ~pmsk; + + /* + * the (somewhat confused) paragraph at the + * bottom of page 35-5 warns that there might + * be "unknown behaviour" when programming + * PSORx and PDIRx, if PPARx = 1, so I + * decided this meant I had to disable the + * dedicated function first, and enable it + * last. + */ + iop->ppar &= tpmsk; + iop->psor = (iop->psor & tpmsk) | psor; + iop->podr = (iop->podr & tpmsk) | podr; + iop->pdat = (iop->pdat & tpmsk) | pdat; + iop->pdir = (iop->pdir & tpmsk) | pdir; + iop->ppar |= ppar; + } + } +} + +#define SET_VAL_MASK(a, b, mask) ((a & mask) | (b & ~mask)) +/* + * Breath some life into the CPU... + * + * Set up the memory map, + * initialize a bunch of registers, + * initialize the UPM's + */ +void cpu_init_f (volatile immap_t * immr) +{ +#if !defined(CONFIG_COGENT) /* done in start.S for the cogent */ + uint sccr; +#endif +#if defined(CONFIG_BOARD_GET_CPU_CLK_F) + unsigned long cpu_clk; +#endif + volatile memctl8260_t *memctl = &immr->im_memctl; + extern void m8260_cpm_reset (void); + + /* Pointer is writable since we allocated a register for it */ + gd = (gd_t *) (CONFIG_SYS_INIT_RAM_ADDR + CONFIG_SYS_GBL_DATA_OFFSET); + + /* Clear initial global data */ + memset ((void *) gd, 0, sizeof (gd_t)); + + /* RSR - Reset Status Register - clear all status (5-4) */ + gd->reset_status = immr->im_clkrst.car_rsr; + immr->im_clkrst.car_rsr = RSR_ALLBITS; + + /* RMR - Reset Mode Register - contains checkstop reset enable (5-5) */ + immr->im_clkrst.car_rmr = CONFIG_SYS_RMR; + + /* BCR - Bus Configuration Register (4-25) */ +#if defined(CONFIG_SYS_BCR_60x) && (CONFIG_SYS_BCR_SINGLE) + if (immr->im_siu_conf.sc_bcr & BCR_EBM) { + immr->im_siu_conf.sc_bcr = SET_VAL_MASK(immr->im_siu_conf.sc_bcr, CONFIG_SYS_BCR_60x, 0x80000010); + } else { + immr->im_siu_conf.sc_bcr = SET_VAL_MASK(immr->im_siu_conf.sc_bcr, CONFIG_SYS_BCR_SINGLE, 0x80000010); + } +#else + immr->im_siu_conf.sc_bcr = CONFIG_SYS_BCR; +#endif + + /* SIUMCR - contains debug pin configuration (4-31) */ +#if defined(CONFIG_SYS_SIUMCR_LOW) && (CONFIG_SYS_SIUMCR_HIGH) + cpu_clk = board_get_cpu_clk_f (); + if (cpu_clk >= 100000000) { + immr->im_siu_conf.sc_siumcr = SET_VAL_MASK(immr->im_siu_conf.sc_siumcr, CONFIG_SYS_SIUMCR_HIGH, 0x9f3cc000); + } else { + immr->im_siu_conf.sc_siumcr = SET_VAL_MASK(immr->im_siu_conf.sc_siumcr, CONFIG_SYS_SIUMCR_LOW, 0x9f3cc000); + } +#else + immr->im_siu_conf.sc_siumcr = CONFIG_SYS_SIUMCR; +#endif + + config_8260_ioports (immr); + + /* initialize time counter status and control register (4-40) */ + immr->im_sit.sit_tmcntsc = CONFIG_SYS_TMCNTSC; + + /* initialize the PIT (4-42) */ + immr->im_sit.sit_piscr = CONFIG_SYS_PISCR; + +#if !defined(CONFIG_COGENT) /* done in start.S for the cogent */ + /* System clock control register (9-8) */ + sccr = immr->im_clkrst.car_sccr & + (SCCR_PCI_MODE | SCCR_PCI_MODCK | SCCR_PCIDF_MSK); + immr->im_clkrst.car_sccr = sccr | + (CONFIG_SYS_SCCR & ~(SCCR_PCI_MODE | SCCR_PCI_MODCK | SCCR_PCIDF_MSK) ); +#endif /* !CONFIG_COGENT */ + + /* + * Memory Controller: + */ + + /* Map banks 0 and 1 to the FLASH banks 0 and 1 at preliminary + * addresses - these have to be modified later when FLASH size + * has been determined + */ + +#if defined(CONFIG_SYS_OR0_REMAP) + memctl->memc_or0 = CONFIG_SYS_OR0_REMAP; +#endif +#if defined(CONFIG_SYS_OR1_REMAP) + memctl->memc_or1 = CONFIG_SYS_OR1_REMAP; +#endif + + /* now restrict to preliminary range */ + /* the PS came from the HRCW, don´t change it */ + memctl->memc_br0 = SET_VAL_MASK(memctl->memc_br0 , CONFIG_SYS_BR0_PRELIM, BRx_PS_MSK); + memctl->memc_or0 = CONFIG_SYS_OR0_PRELIM; + +#if defined(CONFIG_SYS_BR1_PRELIM) && defined(CONFIG_SYS_OR1_PRELIM) + memctl->memc_or1 = CONFIG_SYS_OR1_PRELIM; + memctl->memc_br1 = CONFIG_SYS_BR1_PRELIM; +#endif + +#if defined(CONFIG_SYS_BR2_PRELIM) && defined(CONFIG_SYS_OR2_PRELIM) + memctl->memc_or2 = CONFIG_SYS_OR2_PRELIM; + memctl->memc_br2 = CONFIG_SYS_BR2_PRELIM; +#endif + +#if defined(CONFIG_SYS_BR3_PRELIM) && defined(CONFIG_SYS_OR3_PRELIM) + memctl->memc_or3 = CONFIG_SYS_OR3_PRELIM; + memctl->memc_br3 = CONFIG_SYS_BR3_PRELIM; +#endif + +#if defined(CONFIG_SYS_BR4_PRELIM) && defined(CONFIG_SYS_OR4_PRELIM) + memctl->memc_or4 = CONFIG_SYS_OR4_PRELIM; + memctl->memc_br4 = CONFIG_SYS_BR4_PRELIM; +#endif + +#if defined(CONFIG_SYS_BR5_PRELIM) && defined(CONFIG_SYS_OR5_PRELIM) + memctl->memc_or5 = CONFIG_SYS_OR5_PRELIM; + memctl->memc_br5 = CONFIG_SYS_BR5_PRELIM; +#endif + +#if defined(CONFIG_SYS_BR6_PRELIM) && defined(CONFIG_SYS_OR6_PRELIM) + memctl->memc_or6 = CONFIG_SYS_OR6_PRELIM; + memctl->memc_br6 = CONFIG_SYS_BR6_PRELIM; +#endif + +#if defined(CONFIG_SYS_BR7_PRELIM) && defined(CONFIG_SYS_OR7_PRELIM) + memctl->memc_or7 = CONFIG_SYS_OR7_PRELIM; + memctl->memc_br7 = CONFIG_SYS_BR7_PRELIM; +#endif + +#if defined(CONFIG_SYS_BR8_PRELIM) && defined(CONFIG_SYS_OR8_PRELIM) + memctl->memc_or8 = CONFIG_SYS_OR8_PRELIM; + memctl->memc_br8 = CONFIG_SYS_BR8_PRELIM; +#endif + +#if defined(CONFIG_SYS_BR9_PRELIM) && defined(CONFIG_SYS_OR9_PRELIM) + memctl->memc_or9 = CONFIG_SYS_OR9_PRELIM; + memctl->memc_br9 = CONFIG_SYS_BR9_PRELIM; +#endif + +#if defined(CONFIG_SYS_BR10_PRELIM) && defined(CONFIG_SYS_OR10_PRELIM) + memctl->memc_or10 = CONFIG_SYS_OR10_PRELIM; + memctl->memc_br10 = CONFIG_SYS_BR10_PRELIM; +#endif + +#if defined(CONFIG_SYS_BR11_PRELIM) && defined(CONFIG_SYS_OR11_PRELIM) + memctl->memc_or11 = CONFIG_SYS_OR11_PRELIM; + memctl->memc_br11 = CONFIG_SYS_BR11_PRELIM; +#endif + + m8260_cpm_reset (); +} + +/* + * initialize higher level parts of CPU like time base and timers + */ +int cpu_init_r (void) +{ + volatile immap_t *immr = (immap_t *) gd->bd->bi_immr_base; + + immr->im_cpm.cp_rccr = CONFIG_SYS_RCCR; + + return (0); +} + +/* + * print out the reason for the reset + */ +int prt_8260_rsr (void) +{ + static struct { + ulong mask; + char *desc; + } bits[] = { + { + RSR_JTRS, "JTAG"}, { + RSR_CSRS, "Check Stop"}, { + RSR_SWRS, "Software Watchdog"}, { + RSR_BMRS, "Bus Monitor"}, { + RSR_ESRS, "External Soft"}, { + RSR_EHRS, "External Hard"} + }; + static int n = sizeof bits / sizeof bits[0]; + ulong rsr = gd->reset_status; + int i; + char *sep; + + puts (CPU_ID_STR " Reset Status:"); + + sep = " "; + for (i = 0; i < n; i++) + if (rsr & bits[i].mask) { + printf ("%s%s", sep, bits[i].desc); + sep = ", "; + } + + puts ("\n\n"); + return (0); +} diff --git a/arch/powerpc/cpu/mpc8260/ether_fcc.c b/arch/powerpc/cpu/mpc8260/ether_fcc.c new file mode 100644 index 0000000..5ac02a0 --- /dev/null +++ b/arch/powerpc/cpu/mpc8260/ether_fcc.c @@ -0,0 +1,1190 @@ +/* + * MPC8260 FCC Fast Ethernet + * + * Copyright (c) 2000 MontaVista Software, Inc. Dan Malek (dmalek@jlc.net) + * + * (C) Copyright 2000 Sysgo Real-Time Solutions, GmbH <www.elinos.com> + * Marius Groeger <mgroeger@sysgo.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 + */ + +/* + * MPC8260 FCC Fast Ethernet + * Basic ET HW initialization and packet RX/TX routines + * + * This code will not perform the IO port configuration. This should be + * done in the iop_conf_t structure specific for the board. + * + * TODO: + * add a PHY driver to do the negotiation + * reflect negotiation results in FPSMR + * look for ways to configure the board specific stuff elsewhere, eg. + * config_xxx.h or the board directory + */ + +#include <common.h> +#include <malloc.h> +#include <asm/cpm_8260.h> +#include <mpc8260.h> +#include <command.h> +#include <config.h> +#include <net.h> + +#if defined(CONFIG_MII) || defined(CONFIG_CMD_MII) +#include <miiphy.h> +#endif + +DECLARE_GLOBAL_DATA_PTR; + +#if defined(CONFIG_ETHER_ON_FCC) && defined(CONFIG_CMD_NET) && \ + defined(CONFIG_NET_MULTI) + +static struct ether_fcc_info_s +{ + int ether_index; + int proff_enet; + ulong cpm_cr_enet_sblock; + ulong cpm_cr_enet_page; + ulong cmxfcr_mask; + ulong cmxfcr_value; +} + ether_fcc_info[] = +{ +#ifdef CONFIG_ETHER_ON_FCC1 +{ + 0, + PROFF_FCC1, + CPM_CR_FCC1_SBLOCK, + CPM_CR_FCC1_PAGE, + CONFIG_SYS_CMXFCR_MASK1, + CONFIG_SYS_CMXFCR_VALUE1 +}, +#endif + +#ifdef CONFIG_ETHER_ON_FCC2 +{ + 1, + PROFF_FCC2, + CPM_CR_FCC2_SBLOCK, + CPM_CR_FCC2_PAGE, + CONFIG_SYS_CMXFCR_MASK2, + CONFIG_SYS_CMXFCR_VALUE2 +}, +#endif + +#ifdef CONFIG_ETHER_ON_FCC3 +{ + 2, + PROFF_FCC3, + CPM_CR_FCC3_SBLOCK, + CPM_CR_FCC3_PAGE, + CONFIG_SYS_CMXFCR_MASK3, + CONFIG_SYS_CMXFCR_VALUE3 +}, +#endif +}; + +/*---------------------------------------------------------------------*/ + +/* Maximum input DMA size. Must be a should(?) be a multiple of 4. */ +#define PKT_MAXDMA_SIZE 1520 + +/* The FCC stores dest/src/type, data, and checksum for receive packets. */ +#define PKT_MAXBUF_SIZE 1518 +#define PKT_MINBUF_SIZE 64 + +/* Maximum input buffer size. Must be a multiple of 32. */ +#define PKT_MAXBLR_SIZE 1536 + +#define TOUT_LOOP 1000000 + +#define TX_BUF_CNT 2 +#ifdef __GNUC__ +static char txbuf[TX_BUF_CNT][PKT_MAXBLR_SIZE] __attribute__ ((aligned(8))); +#else +#error "txbuf must be 64-bit aligned" +#endif + +static uint rxIdx; /* index of the current RX buffer */ +static uint txIdx; /* index of the current TX buffer */ + +/* + * FCC Ethernet Tx and Rx buffer descriptors. + * Provide for Double Buffering + * Note: PKTBUFSRX is defined in net.h + */ + +typedef volatile struct rtxbd { + cbd_t rxbd[PKTBUFSRX]; + cbd_t txbd[TX_BUF_CNT]; +} RTXBD; + +/* Good news: the FCC supports external BDs! */ +#ifdef __GNUC__ +static RTXBD rtx __attribute__ ((aligned(8))); +#else +#error "rtx must be 64-bit aligned" +#endif + +static int fec_send(struct eth_device* dev, volatile void *packet, int length) +{ + int i; + int result = 0; + + if (length <= 0) { + printf("fec: bad packet size: %d\n", length); + goto out; + } + + for(i=0; rtx.txbd[txIdx].cbd_sc & BD_ENET_TX_READY; i++) { + if (i >= TOUT_LOOP) { + puts ("fec: tx buffer not ready\n"); + goto out; + } + } + + rtx.txbd[txIdx].cbd_bufaddr = (uint)packet; + rtx.txbd[txIdx].cbd_datlen = length; + rtx.txbd[txIdx].cbd_sc |= (BD_ENET_TX_READY | BD_ENET_TX_LAST | + BD_ENET_TX_WRAP); + + for(i=0; rtx.txbd[txIdx].cbd_sc & BD_ENET_TX_READY; i++) { + if (i >= TOUT_LOOP) { + puts ("fec: tx error\n"); + goto out; + } + } + +#ifdef ET_DEBUG + printf("cycles: %d status: %04x\n", i, rtx.txbd[txIdx].cbd_sc); +#endif + + /* return only status bits */ + result = rtx.txbd[txIdx].cbd_sc & BD_ENET_TX_STATS; + +out: + return result; +} + +static int fec_recv(struct eth_device* dev) +{ + int length; + + for (;;) + { + if (rtx.rxbd[rxIdx].cbd_sc & BD_ENET_RX_EMPTY) { + length = -1; + break; /* nothing received - leave for() loop */ + } + length = rtx.rxbd[rxIdx].cbd_datlen; + + if (rtx.rxbd[rxIdx].cbd_sc & 0x003f) { + printf("fec: rx error %04x\n", rtx.rxbd[rxIdx].cbd_sc); + } + else { + /* Pass the packet up to the protocol layers. */ + NetReceive(NetRxPackets[rxIdx], length - 4); + } + + + /* Give the buffer back to the FCC. */ + rtx.rxbd[rxIdx].cbd_datlen = 0; + + /* wrap around buffer index when necessary */ + if ((rxIdx + 1) >= PKTBUFSRX) { + rtx.rxbd[PKTBUFSRX - 1].cbd_sc = (BD_ENET_RX_WRAP | BD_ENET_RX_EMPTY); + rxIdx = 0; + } + else { + rtx.rxbd[rxIdx].cbd_sc = BD_ENET_RX_EMPTY; + rxIdx++; + } + } + return length; +} + + +static int fec_init(struct eth_device* dev, bd_t *bis) +{ + struct ether_fcc_info_s * info = dev->priv; + int i; + volatile immap_t *immr = (immap_t *)CONFIG_SYS_IMMR; + volatile cpm8260_t *cp = &(immr->im_cpm); + fcc_enet_t *pram_ptr; + unsigned long mem_addr; + +#if 0 + mii_discover_phy(); +#endif + + /* 28.9 - (1-2): ioports have been set up already */ + + /* 28.9 - (3): connect FCC's tx and rx clocks */ + immr->im_cpmux.cmx_uar = 0; + immr->im_cpmux.cmx_fcr = (immr->im_cpmux.cmx_fcr & ~info->cmxfcr_mask) | + info->cmxfcr_value; + + /* 28.9 - (4): GFMR: disable tx/rx, CCITT CRC, Mode Ethernet */ + immr->im_fcc[info->ether_index].fcc_gfmr = + FCC_GFMR_MODE_ENET | FCC_GFMR_TCRC_32; + + /* 28.9 - (5): FPSMR: enable full duplex, select CCITT CRC for Ethernet */ + immr->im_fcc[info->ether_index].fcc_fpsmr = CONFIG_SYS_FCC_PSMR | FCC_PSMR_ENCRC; + + /* 28.9 - (6): FDSR: Ethernet Syn */ + immr->im_fcc[info->ether_index].fcc_fdsr = 0xD555; + + /* reset indeces to current rx/tx bd (see eth_send()/eth_rx()) */ + rxIdx = 0; + txIdx = 0; + + /* Setup Receiver Buffer Descriptors */ + for (i = 0; i < PKTBUFSRX; i++) + { + rtx.rxbd[i].cbd_sc = BD_ENET_RX_EMPTY; + rtx.rxbd[i].cbd_datlen = 0; + rtx.rxbd[i].cbd_bufaddr = (uint)NetRxPackets[i]; + } + rtx.rxbd[PKTBUFSRX - 1].cbd_sc |= BD_ENET_RX_WRAP; + + /* Setup Ethernet Transmitter Buffer Descriptors */ + for (i = 0; i < TX_BUF_CNT; i++) + { + rtx.txbd[i].cbd_sc = (BD_ENET_TX_PAD | BD_ENET_TX_LAST | BD_ENET_TX_TC); + rtx.txbd[i].cbd_datlen = 0; + rtx.txbd[i].cbd_bufaddr = (uint)&txbuf[i][0]; + } + rtx.txbd[TX_BUF_CNT - 1].cbd_sc |= BD_ENET_TX_WRAP; + + /* 28.9 - (7): initialise parameter ram */ + pram_ptr = (fcc_enet_t *)&(immr->im_dprambase[info->proff_enet]); + + /* clear whole structure to make sure all reserved fields are zero */ + memset((void*)pram_ptr, 0, sizeof(fcc_enet_t)); + + /* + * common Parameter RAM area + * + * Allocate space in the reserved FCC area of DPRAM for the + * internal buffers. No one uses this space (yet), so we + * can do this. Later, we will add resource management for + * this area. + */ + mem_addr = CPM_FCC_SPECIAL_BASE + ((info->ether_index) * 64); + pram_ptr->fen_genfcc.fcc_riptr = mem_addr; + pram_ptr->fen_genfcc.fcc_tiptr = mem_addr+32; + /* + * Set maximum bytes per receive buffer. + * It must be a multiple of 32. + */ + pram_ptr->fen_genfcc.fcc_mrblr = PKT_MAXBLR_SIZE; + pram_ptr->fen_genfcc.fcc_rstate = (CPMFCR_GBL | CPMFCR_EB | + CONFIG_SYS_CPMFCR_RAMTYPE) << 24; + pram_ptr->fen_genfcc.fcc_rbase = (unsigned int)(&rtx.rxbd[rxIdx]); + pram_ptr->fen_genfcc.fcc_tstate = (CPMFCR_GBL | CPMFCR_EB | + CONFIG_SYS_CPMFCR_RAMTYPE) << 24; + pram_ptr->fen_genfcc.fcc_tbase = (unsigned int)(&rtx.txbd[txIdx]); + + /* protocol-specific area */ + pram_ptr->fen_cmask = 0xdebb20e3; /* CRC mask */ + pram_ptr->fen_cpres = 0xffffffff; /* CRC preset */ + pram_ptr->fen_retlim = 15; /* Retry limit threshold */ + pram_ptr->fen_mflr = PKT_MAXBUF_SIZE; /* maximum frame length register */ + /* + * Set Ethernet station address. + * + * This is supplied in the board information structure, so we + * copy that into the controller. + * So, far we have only been given one Ethernet address. We make + * it unique by setting a few bits in the upper byte of the + * non-static part of the address. + */ +#define ea eth_get_dev()->enetaddr + pram_ptr->fen_paddrh = (ea[5] << 8) + ea[4]; + pram_ptr->fen_paddrm = (ea[3] << 8) + ea[2]; + pram_ptr->fen_paddrl = (ea[1] << 8) + ea[0]; +#undef ea + pram_ptr->fen_minflr = PKT_MINBUF_SIZE; /* minimum frame length register */ + /* pad pointer. use tiptr since we don't need a specific padding char */ + pram_ptr->fen_padptr = pram_ptr->fen_genfcc.fcc_tiptr; + pram_ptr->fen_maxd1 = PKT_MAXDMA_SIZE; /* maximum DMA1 length */ + pram_ptr->fen_maxd2 = PKT_MAXDMA_SIZE; /* maximum DMA2 length */ + pram_ptr->fen_rfthr = 1; + pram_ptr->fen_rfcnt = 1; +#if 0 + printf("pram_ptr->fen_genfcc.fcc_rbase %08lx\n", + pram_ptr->fen_genfcc.fcc_rbase); + printf("pram_ptr->fen_genfcc.fcc_tbase %08lx\n", + pram_ptr->fen_genfcc.fcc_tbase); +#endif + + /* 28.9 - (8): clear out events in FCCE */ + immr->im_fcc[info->ether_index].fcc_fcce = ~0x0; + + /* 28.9 - (9): FCCM: mask all events */ + immr->im_fcc[info->ether_index].fcc_fccm = 0; + + /* 28.9 - (10-12): we don't use ethernet interrupts */ + + /* 28.9 - (13) + * + * Let's re-initialize the channel now. We have to do it later + * than the manual describes because we have just now finished + * the BD initialization. + */ + cp->cp_cpcr = mk_cr_cmd(info->cpm_cr_enet_page, + info->cpm_cr_enet_sblock, + 0x0c, + CPM_CR_INIT_TRX) | CPM_CR_FLG; + do { + __asm__ __volatile__ ("eieio"); + } while (cp->cp_cpcr & CPM_CR_FLG); + + /* 28.9 - (14): enable tx/rx in gfmr */ + immr->im_fcc[info->ether_index].fcc_gfmr |= FCC_GFMR_ENT | FCC_GFMR_ENR; + + return 1; +} + +static void fec_halt(struct eth_device* dev) +{ + struct ether_fcc_info_s * info = dev->priv; + volatile immap_t *immr = (immap_t *)CONFIG_SYS_IMMR; + + /* write GFMR: disable tx/rx */ + immr->im_fcc[info->ether_index].fcc_gfmr &= + ~(FCC_GFMR_ENT | FCC_GFMR_ENR); +} + +int fec_initialize(bd_t *bis) +{ + struct eth_device* dev; + int i; + + for (i = 0; i < sizeof(ether_fcc_info) / sizeof(ether_fcc_info[0]); i++) + { + dev = (struct eth_device*) malloc(sizeof *dev); + memset(dev, 0, sizeof *dev); + + sprintf(dev->name, "FCC%d ETHERNET", + ether_fcc_info[i].ether_index + 1); + dev->priv = ðer_fcc_info[i]; + dev->init = fec_init; + dev->halt = fec_halt; + dev->send = fec_send; + dev->recv = fec_recv; + + eth_register(dev); + +#if (defined(CONFIG_MII) || defined(CONFIG_CMD_MII)) \ + && defined(CONFIG_BITBANGMII) + miiphy_register(dev->name, + bb_miiphy_read, bb_miiphy_write); +#endif + } + + return 1; +} + +#ifdef CONFIG_ETHER_LOOPBACK_TEST + +#define ELBT_BUFSZ 1024 /* must be multiple of 32 */ + +#define ELBT_CRCSZ 4 + +#define ELBT_NRXBD 4 /* must be at least 2 */ +#define ELBT_NTXBD 4 + +#define ELBT_MAXRXERR 32 +#define ELBT_MAXTXERR 32 + +#define ELBT_CLSWAIT 1000 /* msec to wait for further input frames */ + +typedef + struct { + uint off; + char *lab; + } +elbt_prdesc; + +typedef + struct { + uint _l, _f, m, bc, mc, lg, no, sh, cr, ov, cl; + uint badsrc, badtyp, badlen, badbit; + } +elbt_rxeacc; + +static elbt_prdesc rxeacc_descs[] = { + { offsetof(elbt_rxeacc, _l), "Not Last in Frame" }, + { offsetof(elbt_rxeacc, _f), "Not First in Frame" }, + { offsetof(elbt_rxeacc, m), "Address Miss" }, + { offsetof(elbt_rxeacc, bc), "Broadcast Address" }, + { offsetof(elbt_rxeacc, mc), "Multicast Address" }, + { offsetof(elbt_rxeacc, lg), "Frame Length Violation"}, + { offsetof(elbt_rxeacc, no), "Non-Octet Alignment" }, + { offsetof(elbt_rxeacc, sh), "Short Frame" }, + { offsetof(elbt_rxeacc, cr), "CRC Error" }, + { offsetof(elbt_rxeacc, ov), "Overrun" }, + { offsetof(elbt_rxeacc, cl), "Collision" }, + { offsetof(elbt_rxeacc, badsrc), "Bad Src Address" }, + { offsetof(elbt_rxeacc, badtyp), "Bad Frame Type" }, + { offsetof(elbt_rxeacc, badlen), "Bad Frame Length" }, + { offsetof(elbt_rxeacc, badbit), "Data Compare Errors" }, +}; +static int rxeacc_ndesc = sizeof (rxeacc_descs) / sizeof (rxeacc_descs[0]); + +typedef + struct { + uint def, hb, lc, rl, rc, un, csl; + } +elbt_txeacc; + +static elbt_prdesc txeacc_descs[] = { + { offsetof(elbt_txeacc, def), "Defer Indication" }, + { offsetof(elbt_txeacc, hb), "Heartbeat" }, + { offsetof(elbt_txeacc, lc), "Late Collision" }, + { offsetof(elbt_txeacc, rl), "Retransmission Limit" }, + { offsetof(elbt_txeacc, rc), "Retry Count" }, + { offsetof(elbt_txeacc, un), "Underrun" }, + { offsetof(elbt_txeacc, csl), "Carrier Sense Lost" }, +}; +static int txeacc_ndesc = sizeof (txeacc_descs) / sizeof (txeacc_descs[0]); + +typedef + struct { + uchar rxbufs[ELBT_NRXBD][ELBT_BUFSZ]; + uchar txbufs[ELBT_NTXBD][ELBT_BUFSZ]; + cbd_t rxbd[ELBT_NRXBD]; + cbd_t txbd[ELBT_NTXBD]; + enum { Idle, Running, Closing, Closed } state; + int proff, page, sblock; + uint clstime, nsent, ntxerr, nrcvd, nrxerr; + ushort rxerrs[ELBT_MAXRXERR], txerrs[ELBT_MAXTXERR]; + elbt_rxeacc rxeacc; + elbt_txeacc txeacc; + } __attribute__ ((aligned(8))) +elbt_chan; + +static uchar patbytes[ELBT_NTXBD] = { + 0xff, 0xaa, 0x55, 0x00 +}; +static uint patwords[ELBT_NTXBD] = { + 0xffffffff, 0xaaaaaaaa, 0x55555555, 0x00000000 +}; + +#ifdef __GNUC__ +static elbt_chan elbt_chans[3] __attribute__ ((aligned(8))); +#else +#error "elbt_chans must be 64-bit aligned" +#endif + +#define CPM_CR_GRACEFUL_STOP_TX ((ushort)0x0005) + +static elbt_prdesc epram_descs[] = { + { offsetof(fcc_enet_t, fen_crcec), "CRC Errors" }, + { offsetof(fcc_enet_t, fen_alec), "Alignment Errors" }, + { offsetof(fcc_enet_t, fen_disfc), "Discarded Frames" }, + { offsetof(fcc_enet_t, fen_octc), "Octets" }, + { offsetof(fcc_enet_t, fen_colc), "Collisions" }, + { offsetof(fcc_enet_t, fen_broc), "Broadcast Frames" }, + { offsetof(fcc_enet_t, fen_mulc), "Multicast Frames" }, + { offsetof(fcc_enet_t, fen_uspc), "Undersize Frames" }, + { offsetof(fcc_enet_t, fen_frgc), "Fragments" }, + { offsetof(fcc_enet_t, fen_ospc), "Oversize Frames" }, + { offsetof(fcc_enet_t, fen_jbrc), "Jabbers" }, + { offsetof(fcc_enet_t, fen_p64c), "64 Octet Frames" }, + { offsetof(fcc_enet_t, fen_p65c), "65-127 Octet Frames" }, + { offsetof(fcc_enet_t, fen_p128c), "128-255 Octet Frames" }, + { offsetof(fcc_enet_t, fen_p256c), "256-511 Octet Frames" }, + { offsetof(fcc_enet_t, fen_p512c), "512-1023 Octet Frames" }, + { offsetof(fcc_enet_t, fen_p1024c), "1024-1518 Octet Frames"}, +}; +static int epram_ndesc = sizeof (epram_descs) / sizeof (epram_descs[0]); + +/* + * given an elbt_prdesc array and an array of base addresses, print + * each prdesc down the screen with the values fetched from each + * base address across the screen + */ +static void +print_desc (elbt_prdesc descs[], int ndesc, uchar *bases[], int nbase) +{ + elbt_prdesc *dp = descs, *edp = dp + ndesc; + int i; + + printf ("%32s", ""); + + for (i = 0; i < nbase; i++) + printf (" Channel %d", i); + + putc ('\n'); + + while (dp < edp) { + + printf ("%-32s", dp->lab); + + for (i = 0; i < nbase; i++) { + uint val = *(uint *)(bases[i] + dp->off); + + printf (" %10u", val); + } + + putc ('\n'); + + dp++; + } +} + +/* + * return number of bits that are set in a value; value contains + * nbits (right-justified) bits. + */ +static uint __inline__ +nbs (uint value, uint nbits) +{ + uint cnt = 0; +#if 1 + uint pos = sizeof (uint) * 8; + + __asm__ __volatile__ ("\ + mtctr %2\n\ +1: rlwnm. %2,%1,%4,31,31\n\ + beq 2f\n\ + addi %0,%0,1\n\ +2: subi %4,%4,1\n\ + bdnz 1b" + : "=r"(cnt) + : "r"(value), "r"(nbits), "r"(cnt), "r"(pos) + : "ctr", "cc" ); +#else + uint mask = 1; + + do { + if (value & mask) + cnt++; + mask <<= 1; + } while (--nbits); +#endif + + return (cnt); +} + +static ulong +badbits (uchar *bp, int n, ulong pat) +{ + ulong *lp, cnt = 0; + int nl; + + while (n > 0 && ((ulong)bp & (sizeof (ulong) - 1)) != 0) { + uchar diff; + + diff = *bp++ ^ (uchar)pat; + + if (diff) + cnt += nbs ((ulong)diff, 8); + + n--; + } + + lp = (ulong *)bp; + nl = n / sizeof (ulong); + n -= nl * sizeof (ulong); + + while (nl > 0) { + ulong diff; + + diff = *lp++ ^ pat; + + if (diff) + cnt += nbs (diff, 32); + + nl--; + } + + bp = (uchar *)lp; + + while (n > 0) { + uchar diff; + + diff = *bp++ ^ (uchar)pat; + + if (diff) + cnt += nbs ((ulong)diff, 8); + + n--; + } + + return (cnt); +} + +static inline unsigned short +swap16 (unsigned short x) +{ + return (((x & 0xff) << 8) | ((x & 0xff00) >> 8)); +} + +/* broadcast is not an error - we send them like that */ +#define BD_ENET_RX_ERRS (BD_ENET_RX_STATS & ~BD_ENET_RX_BC) + +void +eth_loopback_test (void) +{ + volatile immap_t *immr = (immap_t *)CONFIG_SYS_IMMR; + volatile cpm8260_t *cp = &(immr->im_cpm); + int c, nclosed; + ulong runtime, nmsec; + uchar *bases[3]; + + puts ("FCC Ethernet External loopback test\n"); + + eth_getenv_enetaddr("ethaddr", NetOurEther); + + /* + * global initialisations for all FCC channels + */ + + /* 28.9 - (1-2): ioports have been set up already */ + +#if defined(CONFIG_HYMOD) + /* + * Attention: this is board-specific + * 0, FCC1 + * 1, FCC2 + * 2, FCC3 + */ +# define FCC_START_LOOP 0 +# define FCC_END_LOOP 2 + + /* + * Attention: this is board-specific + * - FCC1 Rx-CLK is CLK10 + * - FCC1 Tx-CLK is CLK11 + * - FCC2 Rx-CLK is CLK13 + * - FCC2 Tx-CLK is CLK14 + * - FCC3 Rx-CLK is CLK15 + * - FCC3 Tx-CLK is CLK16 + */ + + /* 28.9 - (3): connect FCC's tx and rx clocks */ + immr->im_cpmux.cmx_uar = 0; + immr->im_cpmux.cmx_fcr = CMXFCR_RF1CS_CLK10|CMXFCR_TF1CS_CLK11|\ + CMXFCR_RF2CS_CLK13|CMXFCR_TF2CS_CLK14|\ + CMXFCR_RF3CS_CLK15|CMXFCR_TF3CS_CLK16; +#elif defined(CONFIG_SBC8260) || defined(CONFIG_SACSng) + /* + * Attention: this is board-specific + * 1, FCC2 + */ +# define FCC_START_LOOP 1 +# define FCC_END_LOOP 1 + + /* + * Attention: this is board-specific + * - FCC2 Rx-CLK is CLK13 + * - FCC2 Tx-CLK is CLK14 + */ + + /* 28.9 - (3): connect FCC's tx and rx clocks */ + immr->im_cpmux.cmx_uar = 0; + immr->im_cpmux.cmx_fcr = CMXFCR_RF2CS_CLK13|CMXFCR_TF2CS_CLK14; +#else +#error "eth_loopback_test not supported on your board" +#endif + + puts ("Initialise FCC channels:"); + + for (c = FCC_START_LOOP; c <= FCC_END_LOOP; c++) { + elbt_chan *ecp = &elbt_chans[c]; + volatile fcc_t *fcp = &immr->im_fcc[c]; + volatile fcc_enet_t *fpp; + int i; + ulong addr; + + /* + * initialise channel data + */ + + printf (" %d", c); + + memset ((void *)ecp, 0, sizeof (*ecp)); + + ecp->state = Idle; + + switch (c) { + + case 0: /* FCC1 */ + ecp->proff = PROFF_FCC1; + ecp->page = CPM_CR_FCC1_PAGE; + ecp->sblock = CPM_CR_FCC1_SBLOCK; + break; + + case 1: /* FCC2 */ + ecp->proff = PROFF_FCC2; + ecp->page = CPM_CR_FCC2_PAGE; + ecp->sblock = CPM_CR_FCC2_SBLOCK; + break; + + case 2: /* FCC3 */ + ecp->proff = PROFF_FCC3; + ecp->page = CPM_CR_FCC3_PAGE; + ecp->sblock = CPM_CR_FCC3_SBLOCK; + break; + } + + /* + * set up tx buffers and bds + */ + + for (i = 0; i < ELBT_NTXBD; i++) { + cbd_t *bdp = &ecp->txbd[i]; + uchar *bp = &ecp->txbufs[i][0]; + + bdp->cbd_bufaddr = (uint)bp; + /* room for crc */ + bdp->cbd_datlen = ELBT_BUFSZ - ELBT_CRCSZ; + bdp->cbd_sc = BD_ENET_TX_READY | BD_ENET_TX_PAD | \ + BD_ENET_TX_LAST | BD_ENET_TX_TC; + + memset ((void *)bp, patbytes[i], ELBT_BUFSZ); + NetSetEther (bp, NetBcastAddr, 0x8000); + } + ecp->txbd[ELBT_NTXBD - 1].cbd_sc |= BD_ENET_TX_WRAP; + + /* + * set up rx buffers and bds + */ + + for (i = 0; i < ELBT_NRXBD; i++) { + cbd_t *bdp = &ecp->rxbd[i]; + uchar *bp = &ecp->rxbufs[i][0]; + + bdp->cbd_bufaddr = (uint)bp; + bdp->cbd_datlen = 0; + bdp->cbd_sc = BD_ENET_RX_EMPTY; + + memset ((void *)bp, 0, ELBT_BUFSZ); + } + ecp->rxbd[ELBT_NRXBD - 1].cbd_sc |= BD_ENET_RX_WRAP; + + /* + * set up the FCC channel hardware + */ + + /* 28.9 - (4): GFMR: disable tx/rx, CCITT CRC, Mode Ethernet */ + fcp->fcc_gfmr = FCC_GFMR_MODE_ENET | FCC_GFMR_TCRC_32; + + /* 28.9 - (5): FPSMR: fd, enet CRC, Promis, RMON, Rx SHort */ + fcp->fcc_fpsmr = FCC_PSMR_FDE | FCC_PSMR_LPB | \ + FCC_PSMR_ENCRC | FCC_PSMR_PRO | \ + FCC_PSMR_MON | FCC_PSMR_RSH; + + /* 28.9 - (6): FDSR: Ethernet Syn */ + fcp->fcc_fdsr = 0xD555; + + /* 29.9 - (7): initialise parameter ram */ + fpp = (fcc_enet_t *)&(immr->im_dprambase[ecp->proff]); + + /* clear whole struct to make sure all resv fields are zero */ + memset ((void *)fpp, 0, sizeof (fcc_enet_t)); + + /* + * common Parameter RAM area + * + * Allocate space in the reserved FCC area of DPRAM for the + * internal buffers. No one uses this space (yet), so we + * can do this. Later, we will add resource management for + * this area. + */ + addr = CPM_FCC_SPECIAL_BASE + (c * 64); + fpp->fen_genfcc.fcc_riptr = addr; + fpp->fen_genfcc.fcc_tiptr = addr + 32; + + /* + * Set maximum bytes per receive buffer. + * It must be a multiple of 32. + * buffers are in 60x bus memory. + */ + fpp->fen_genfcc.fcc_mrblr = PKT_MAXBLR_SIZE; + fpp->fen_genfcc.fcc_rstate = (CPMFCR_GBL | CPMFCR_EB) << 24; + fpp->fen_genfcc.fcc_rbase = (unsigned int)(&ecp->rxbd[0]); + fpp->fen_genfcc.fcc_tstate = (CPMFCR_GBL | CPMFCR_EB) << 24; + fpp->fen_genfcc.fcc_tbase = (unsigned int)(&ecp->txbd[0]); + + /* protocol-specific area */ + fpp->fen_cmask = 0xdebb20e3; /* CRC mask */ + fpp->fen_cpres = 0xffffffff; /* CRC preset */ + fpp->fen_retlim = 15; /* Retry limit threshold */ + fpp->fen_mflr = PKT_MAXBUF_SIZE;/* max frame length register */ + + /* + * Set Ethernet station address. + * + * This is supplied in the board information structure, so we + * copy that into the controller. + * So, far we have only been given one Ethernet address. We use + * the same address for all channels + */ +#define ea NetOurEther + fpp->fen_paddrh = (ea[5] << 8) + ea[4]; + fpp->fen_paddrm = (ea[3] << 8) + ea[2]; + fpp->fen_paddrl = (ea[1] << 8) + ea[0]; +#undef ea + + fpp->fen_minflr = PKT_MINBUF_SIZE; /* min frame len register */ + /* + * pad pointer. use tiptr since we don't need + * a specific padding char + */ + fpp->fen_padptr = fpp->fen_genfcc.fcc_tiptr; + fpp->fen_maxd1 = PKT_MAXDMA_SIZE; /* max DMA1 length */ + fpp->fen_maxd2 = PKT_MAXDMA_SIZE; /* max DMA2 length */ + fpp->fen_rfthr = 1; + fpp->fen_rfcnt = 1; + + /* 28.9 - (8): clear out events in FCCE */ + fcp->fcc_fcce = ~0x0; + + /* 28.9 - (9): FCCM: mask all events */ + fcp->fcc_fccm = 0; + + /* 28.9 - (10-12): we don't use ethernet interrupts */ + + /* 28.9 - (13) + * + * Let's re-initialize the channel now. We have to do it later + * than the manual describes because we have just now finished + * the BD initialization. + */ + cp->cp_cpcr = mk_cr_cmd (ecp->page, ecp->sblock, \ + 0x0c, CPM_CR_INIT_TRX) | CPM_CR_FLG; + do { + __asm__ __volatile__ ("eieio"); + } while (cp->cp_cpcr & CPM_CR_FLG); + } + + puts (" done\nStarting test... (Ctrl-C to Finish)\n"); + + /* + * Note: don't want serial output from here until the end of the + * test - the delays would probably stuff things up. + */ + + clear_ctrlc (); + runtime = get_timer (0); + + do { + nclosed = 0; + + for (c = FCC_START_LOOP; c <= FCC_END_LOOP; c++) { + volatile fcc_t *fcp = &immr->im_fcc[c]; + elbt_chan *ecp = &elbt_chans[c]; + int i; + + switch (ecp->state) { + + case Idle: + /* + * set the channel Running ... + */ + + /* 28.9 - (14): enable tx/rx in gfmr */ + fcp->fcc_gfmr |= FCC_GFMR_ENT | FCC_GFMR_ENR; + + ecp->state = Running; + break; + + case Running: + /* + * (while Running only) check for + * termination of the test + */ + + (void)ctrlc (); + + if (had_ctrlc ()) { + /* + * initiate a "graceful stop transmit" + * on the channel + */ + cp->cp_cpcr = mk_cr_cmd (ecp->page, \ + ecp->sblock, 0x0c, \ + CPM_CR_GRACEFUL_STOP_TX) | \ + CPM_CR_FLG; + do { + __asm__ __volatile__ ("eieio"); + } while (cp->cp_cpcr & CPM_CR_FLG); + + ecp->clstime = get_timer (0); + ecp->state = Closing; + } + /* fall through ... */ + + case Closing: + /* + * (while Running or Closing) poll the channel: + * - check for any non-READY tx buffers and + * make them ready + * - check for any non-EMPTY rx buffers and + * check that they were received correctly, + * adjust counters etc, then make empty + */ + + for (i = 0; i < ELBT_NTXBD; i++) { + cbd_t *bdp = &ecp->txbd[i]; + ushort sc = bdp->cbd_sc; + + if ((sc & BD_ENET_TX_READY) != 0) + continue; + + /* + * this frame has finished + * transmitting + */ + ecp->nsent++; + + if (sc & BD_ENET_TX_STATS) { + ulong n; + + /* + * we had an error on + * the transmission + */ + n = ecp->ntxerr++; + if (n < ELBT_MAXTXERR) + ecp->txerrs[n] = sc; + + if (sc & BD_ENET_TX_DEF) + ecp->txeacc.def++; + if (sc & BD_ENET_TX_HB) + ecp->txeacc.hb++; + if (sc & BD_ENET_TX_LC) + ecp->txeacc.lc++; + if (sc & BD_ENET_TX_RL) + ecp->txeacc.rl++; + if (sc & BD_ENET_TX_RCMASK) + ecp->txeacc.rc++; + if (sc & BD_ENET_TX_UN) + ecp->txeacc.un++; + if (sc & BD_ENET_TX_CSL) + ecp->txeacc.csl++; + + bdp->cbd_sc &= \ + ~BD_ENET_TX_STATS; + } + + if (ecp->state == Closing) + ecp->clstime = get_timer (0); + + /* make it ready again */ + bdp->cbd_sc |= BD_ENET_TX_READY; + } + + for (i = 0; i < ELBT_NRXBD; i++) { + cbd_t *bdp = &ecp->rxbd[i]; + ushort sc = bdp->cbd_sc, mask; + + if ((sc & BD_ENET_RX_EMPTY) != 0) + continue; + + /* we have a new frame in this buffer */ + ecp->nrcvd++; + + mask = BD_ENET_RX_LAST|BD_ENET_RX_FIRST; + if ((sc & mask) != mask) { + /* somethings wrong here ... */ + if (!(sc & BD_ENET_RX_LAST)) + ecp->rxeacc._l++; + if (!(sc & BD_ENET_RX_FIRST)) + ecp->rxeacc._f++; + } + + if (sc & BD_ENET_RX_ERRS) { + ulong n; + + /* + * we had some sort of error + * on the frame + */ + n = ecp->nrxerr++; + if (n < ELBT_MAXRXERR) + ecp->rxerrs[n] = sc; + + if (sc & BD_ENET_RX_MISS) + ecp->rxeacc.m++; + if (sc & BD_ENET_RX_BC) + ecp->rxeacc.bc++; + if (sc & BD_ENET_RX_MC) + ecp->rxeacc.mc++; + if (sc & BD_ENET_RX_LG) + ecp->rxeacc.lg++; + if (sc & BD_ENET_RX_NO) + ecp->rxeacc.no++; + if (sc & BD_ENET_RX_SH) + ecp->rxeacc.sh++; + if (sc & BD_ENET_RX_CR) + ecp->rxeacc.cr++; + if (sc & BD_ENET_RX_OV) + ecp->rxeacc.ov++; + if (sc & BD_ENET_RX_CL) + ecp->rxeacc.cl++; + + bdp->cbd_sc &= \ + ~BD_ENET_RX_ERRS; + } + else { + ushort datlen = bdp->cbd_datlen; + Ethernet_t *ehp; + ushort prot; + int ours, tb, n, nbytes; + + ehp = (Ethernet_t *) \ + &ecp->rxbufs[i][0]; + + ours = memcmp (ehp->et_src, \ + NetOurEther, 6); + + prot = swap16 (ehp->et_protlen); + tb = prot & 0x8000; + n = prot & 0x7fff; + + nbytes = ELBT_BUFSZ - \ + offsetof (Ethernet_t, \ + et_dsap) - \ + ELBT_CRCSZ; + + /* check the frame is correct */ + if (datlen != ELBT_BUFSZ) + ecp->rxeacc.badlen++; + else if (!ours) + ecp->rxeacc.badsrc++; + else if (!tb || n >= ELBT_NTXBD) + ecp->rxeacc.badtyp++; + else { + ulong patword = \ + patwords[n]; + uint nbb; + + nbb = badbits ( \ + &ehp->et_dsap, \ + nbytes, \ + patword); + + ecp->rxeacc.badbit += \ + nbb; + } + } + + if (ecp->state == Closing) + ecp->clstime = get_timer (0); + + /* make it empty again */ + bdp->cbd_sc |= BD_ENET_RX_EMPTY; + } + + if (ecp->state != Closing) + break; + + /* + * (while Closing) check to see if + * waited long enough + */ + + if (get_timer (ecp->clstime) >= ELBT_CLSWAIT) { + /* write GFMR: disable tx/rx */ + fcp->fcc_gfmr &= \ + ~(FCC_GFMR_ENT | FCC_GFMR_ENR); + ecp->state = Closed; + } + + break; + + case Closed: + nclosed++; + break; + } + } + + } while (nclosed < (FCC_END_LOOP - FCC_START_LOOP + 1)); + + runtime = get_timer (runtime); + if (runtime <= ELBT_CLSWAIT) { + printf ("Whoops! somehow elapsed time (%ld) is wrong (<= %d)\n", + runtime, ELBT_CLSWAIT); + return; + } + nmsec = runtime - ELBT_CLSWAIT; + + printf ("Test Finished in %ldms (plus %dms close wait period)!\n\n", + nmsec, ELBT_CLSWAIT); + + /* + * now print stats + */ + + for (c = FCC_START_LOOP; c <= FCC_END_LOOP; c++) { + elbt_chan *ecp = &elbt_chans[c]; + uint rxpps, txpps, nerr; + + rxpps = (ecp->nrcvd * 1000) / nmsec; + txpps = (ecp->nsent * 1000) / nmsec; + + printf ("Channel %d: %d rcvd (%d pps, %d rxerrs), " + "%d sent (%d pps, %d txerrs)\n\n", c, + ecp->nrcvd, rxpps, ecp->nrxerr, + ecp->nsent, txpps, ecp->ntxerr); + + if ((nerr = ecp->nrxerr) > 0) { + ulong i; + + printf ("\tFirst %d rx errs:", nerr); + for (i = 0; i < nerr; i++) + printf (" %04x", ecp->rxerrs[i]); + putc ('\n'); + } + + if ((nerr = ecp->ntxerr) > 0) { + ulong i; + + printf ("\tFirst %d tx errs:", nerr); + for (i = 0; i < nerr; i++) + printf (" %04x", ecp->txerrs[i]); + putc ('\n'); + } + } + + puts ("Receive Error Counts:\n"); + for (c = FCC_START_LOOP; c <= FCC_END_LOOP; c++) + bases[c] = (uchar *)&elbt_chans[c].rxeacc; + print_desc (rxeacc_descs, rxeacc_ndesc, bases, 3); + + puts ("\nTransmit Error Counts:\n"); + for (c = FCC_START_LOOP; c <= FCC_END_LOOP; c++) + bases[c] = (uchar *)&elbt_chans[c].txeacc; + print_desc (txeacc_descs, txeacc_ndesc, bases, 3); + + puts ("\nRMON(-like) Counters:\n"); + for (c = FCC_START_LOOP; c <= FCC_END_LOOP; c++) + bases[c] = (uchar *)&immr->im_dprambase[elbt_chans[c].proff]; + print_desc (epram_descs, epram_ndesc, bases, 3); +} + +#endif /* CONFIG_ETHER_LOOPBACK_TEST */ + +#endif diff --git a/arch/powerpc/cpu/mpc8260/ether_scc.c b/arch/powerpc/cpu/mpc8260/ether_scc.c new file mode 100644 index 0000000..432111d --- /dev/null +++ b/arch/powerpc/cpu/mpc8260/ether_scc.c @@ -0,0 +1,387 @@ +/* + * MPC8260 SCC Ethernet + * + * Copyright (c) 2000 MontaVista Software, Inc. Dan Malek (dmalek@jlc.net) + * + * (C) Copyright 2000 Sysgo Real-Time Solutions, GmbH <www.elinos.com> + * Marius Groeger <mgroeger@sysgo.de> + * + * (C) Copyright (c) 2001 + * Advent Networks, Inc. <http://www.adventnetworks.com> + * Jay Monkman <jtm@smoothsmoothie.com> + * + * Modified so that it plays nicely when more than one ETHERNET interface + * is in use a la ether_fcc.c. + * (C) Copyright 2008 + * DENX Software Engineerin GmbH + * Gary Jennejohn <garyj@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> +#include <asm/cpm_8260.h> +#include <mpc8260.h> +#include <malloc.h> +#include <net.h> +#include <command.h> +#include <config.h> + +#ifndef CONFIG_NET_MULTI +#error "CONFIG_NET_MULTI must be defined." +#endif + +#if (CONFIG_ETHER_INDEX == 1) +# define PROFF_ENET PROFF_SCC1 +# define CPM_CR_ENET_PAGE CPM_CR_SCC1_PAGE +# define CPM_CR_ENET_SBLOCK CPM_CR_SCC1_SBLOCK +# define CMXSCR_MASK (CMXSCR_SC1 |\ + CMXSCR_RS1CS_MSK |\ + CMXSCR_TS1CS_MSK) + +#elif (CONFIG_ETHER_INDEX == 2) +# define PROFF_ENET PROFF_SCC2 +# define CPM_CR_ENET_PAGE CPM_CR_SCC2_PAGE +# define CPM_CR_ENET_SBLOCK CPM_CR_SCC2_SBLOCK +# define CMXSCR_MASK (CMXSCR_SC2 |\ + CMXSCR_RS2CS_MSK |\ + CMXSCR_TS2CS_MSK) + +#elif (CONFIG_ETHER_INDEX == 3) +# define PROFF_ENET PROFF_SCC3 +# define CPM_CR_ENET_PAGE CPM_CR_SCC3_PAGE +# define CPM_CR_ENET_SBLOCK CPM_CR_SCC3_SBLOCK +# define CMXSCR_MASK (CMXSCR_SC3 |\ + CMXSCR_RS3CS_MSK |\ + CMXSCR_TS3CS_MSK) +#elif (CONFIG_ETHER_INDEX == 4) +# define PROFF_ENET PROFF_SCC4 +# define CPM_CR_ENET_PAGE CPM_CR_SCC4_PAGE +# define CPM_CR_ENET_SBLOCK CPM_CR_SCC4_SBLOCK +# define CMXSCR_MASK (CMXSCR_SC4 |\ + CMXSCR_RS4CS_MSK |\ + CMXSCR_TS4CS_MSK) + +#endif + + +/* Ethernet Transmit and Receive Buffers */ +#define DBUF_LENGTH 1520 + +#define TX_BUF_CNT 2 + +#if !defined(CONFIG_SYS_SCC_TOUT_LOOP) + #define CONFIG_SYS_SCC_TOUT_LOOP 1000000 +#endif + +static char txbuf[TX_BUF_CNT][ DBUF_LENGTH ]; + +static uint rxIdx; /* index of the current RX buffer */ +static uint txIdx; /* index of the current TX buffer */ + +/* + * SCC Ethernet Tx and Rx buffer descriptors allocated at the + * immr->udata_bd address on Dual-Port RAM + * Provide for Double Buffering + */ + +typedef volatile struct CommonBufferDescriptor { + cbd_t rxbd[PKTBUFSRX]; /* Rx BD */ + cbd_t txbd[TX_BUF_CNT]; /* Tx BD */ +} RTXBD; + +static RTXBD *rtx; + + +static int sec_send(struct eth_device *dev, volatile void *packet, int length) +{ + int i; + int result = 0; + + if (length <= 0) { + printf("scc: bad packet size: %d\n", length); + goto out; + } + + for(i=0; rtx->txbd[txIdx].cbd_sc & BD_ENET_TX_READY; i++) { + if (i >= CONFIG_SYS_SCC_TOUT_LOOP) { + puts ("scc: tx buffer not ready\n"); + goto out; + } + } + + rtx->txbd[txIdx].cbd_bufaddr = (uint)packet; + rtx->txbd[txIdx].cbd_datlen = length; + rtx->txbd[txIdx].cbd_sc |= (BD_ENET_TX_READY | BD_ENET_TX_LAST | + BD_ENET_TX_WRAP); + + for(i=0; rtx->txbd[txIdx].cbd_sc & BD_ENET_TX_READY; i++) { + if (i >= CONFIG_SYS_SCC_TOUT_LOOP) { + puts ("scc: tx error\n"); + goto out; + } + } + + /* return only status bits */ + result = rtx->txbd[txIdx].cbd_sc & BD_ENET_TX_STATS; + + out: + return result; +} + + +static int sec_rx(struct eth_device *dev) +{ + int length; + + for (;;) + { + if (rtx->rxbd[rxIdx].cbd_sc & BD_ENET_RX_EMPTY) { + length = -1; + break; /* nothing received - leave for() loop */ + } + + length = rtx->rxbd[rxIdx].cbd_datlen; + + if (rtx->rxbd[rxIdx].cbd_sc & 0x003f) + { + printf("err: %x\n", rtx->rxbd[rxIdx].cbd_sc); + } + else + { + /* Pass the packet up to the protocol layers. */ + NetReceive(NetRxPackets[rxIdx], length - 4); + } + + + /* Give the buffer back to the SCC. */ + rtx->rxbd[rxIdx].cbd_datlen = 0; + + /* wrap around buffer index when necessary */ + if ((rxIdx + 1) >= PKTBUFSRX) { + rtx->rxbd[PKTBUFSRX - 1].cbd_sc = (BD_ENET_RX_WRAP | + BD_ENET_RX_EMPTY); + rxIdx = 0; + } + else { + rtx->rxbd[rxIdx].cbd_sc = BD_ENET_RX_EMPTY; + rxIdx++; + } + } + return length; +} + +/************************************************************** + * + * SCC Ethernet Initialization Routine + * + *************************************************************/ + +static int sec_init(struct eth_device *dev, bd_t *bis) +{ + int i; + volatile immap_t *immr = (immap_t *)CONFIG_SYS_IMMR; + scc_enet_t *pram_ptr; + uint dpaddr; + uchar ea[6]; + + rxIdx = 0; + txIdx = 0; + + /* + * Assign static pointer to BD area. + * Avoid exhausting DPRAM, which would cause a panic. + */ + if (rtx == NULL) { + dpaddr = m8260_cpm_dpalloc(sizeof(RTXBD) + 2, 16); + rtx = (RTXBD *)&immr->im_dprambase[dpaddr]; + } + + /* 24.21 - (1-3): ioports have been set up already */ + + /* 24.21 - (4,5): connect SCC's tx and rx clocks, use NMSI for SCC */ + immr->im_cpmux.cmx_uar = 0; + immr->im_cpmux.cmx_scr = ( (immr->im_cpmux.cmx_scr & ~CMXSCR_MASK) | + CONFIG_SYS_CMXSCR_VALUE); + + + /* 24.21 (6) write RBASE and TBASE to parameter RAM */ + pram_ptr = (scc_enet_t *)&(immr->im_dprambase[PROFF_ENET]); + pram_ptr->sen_genscc.scc_rbase = (unsigned int)(&rtx->rxbd[0]); + pram_ptr->sen_genscc.scc_tbase = (unsigned int)(&rtx->txbd[0]); + + pram_ptr->sen_genscc.scc_rfcr = 0x18; /* Nrml Ops and Mot byte ordering */ + pram_ptr->sen_genscc.scc_tfcr = 0x18; /* Mot byte ordering, Nrml access */ + + pram_ptr->sen_genscc.scc_mrblr = DBUF_LENGTH; /* max. package len 1520 */ + + pram_ptr->sen_cpres = ~(0x0); /* Preset CRC */ + pram_ptr->sen_cmask = 0xdebb20e3; /* Constant Mask for CRC */ + + + /* 24.21 - (7): Write INIT RX AND TX PARAMETERS to CPCR */ + while(immr->im_cpm.cp_cpcr & CPM_CR_FLG); + immr->im_cpm.cp_cpcr = mk_cr_cmd(CPM_CR_ENET_PAGE, + CPM_CR_ENET_SBLOCK, + 0x0c, + CPM_CR_INIT_TRX) | CPM_CR_FLG; + + /* 24.21 - (8-18): Set up parameter RAM */ + pram_ptr->sen_crcec = 0x0; /* Error Counter CRC (unused) */ + pram_ptr->sen_alec = 0x0; /* Align Error Counter (unused) */ + pram_ptr->sen_disfc = 0x0; /* Discard Frame Counter (unused) */ + + pram_ptr->sen_pads = 0x8888; /* Short Frame PAD Characters */ + + pram_ptr->sen_retlim = 15; /* Retry Limit Threshold */ + + pram_ptr->sen_maxflr = 1518; /* MAX Frame Length Register */ + pram_ptr->sen_minflr = 64; /* MIN Frame Length Register */ + + pram_ptr->sen_maxd1 = DBUF_LENGTH; /* MAX DMA1 Length Register */ + pram_ptr->sen_maxd2 = DBUF_LENGTH; /* MAX DMA2 Length Register */ + + pram_ptr->sen_gaddr1 = 0x0; /* Group Address Filter 1 (unused) */ + pram_ptr->sen_gaddr2 = 0x0; /* Group Address Filter 2 (unused) */ + pram_ptr->sen_gaddr3 = 0x0; /* Group Address Filter 3 (unused) */ + pram_ptr->sen_gaddr4 = 0x0; /* Group Address Filter 4 (unused) */ + + eth_getenv_enetaddr("ethaddr", ea); + pram_ptr->sen_paddrh = (ea[5] << 8) + ea[4]; + pram_ptr->sen_paddrm = (ea[3] << 8) + ea[2]; + pram_ptr->sen_paddrl = (ea[1] << 8) + ea[0]; + + pram_ptr->sen_pper = 0x0; /* Persistence (unused) */ + + pram_ptr->sen_iaddr1 = 0x0; /* Individual Address Filter 1 (unused) */ + pram_ptr->sen_iaddr2 = 0x0; /* Individual Address Filter 2 (unused) */ + pram_ptr->sen_iaddr3 = 0x0; /* Individual Address Filter 3 (unused) */ + pram_ptr->sen_iaddr4 = 0x0; /* Individual Address Filter 4 (unused) */ + + pram_ptr->sen_taddrh = 0x0; /* Tmp Address (MSB) (unused) */ + pram_ptr->sen_taddrm = 0x0; /* Tmp Address (unused) */ + pram_ptr->sen_taddrl = 0x0; /* Tmp Address (LSB) (unused) */ + + /* 24.21 - (19): Initialize RxBD */ + for (i = 0; i < PKTBUFSRX; i++) + { + rtx->rxbd[i].cbd_sc = BD_ENET_RX_EMPTY; + rtx->rxbd[i].cbd_datlen = 0; /* Reset */ + rtx->rxbd[i].cbd_bufaddr = (uint)NetRxPackets[i]; + } + + rtx->rxbd[PKTBUFSRX - 1].cbd_sc |= BD_ENET_RX_WRAP; + + /* 24.21 - (20): Initialize TxBD */ + for (i = 0; i < TX_BUF_CNT; i++) + { + rtx->txbd[i].cbd_sc = (BD_ENET_TX_PAD | + BD_ENET_TX_LAST | + BD_ENET_TX_TC); + rtx->txbd[i].cbd_datlen = 0; /* Reset */ + rtx->txbd[i].cbd_bufaddr = (uint)&txbuf[i][0]; + } + + rtx->txbd[TX_BUF_CNT - 1].cbd_sc |= BD_ENET_TX_WRAP; + + /* 24.21 - (21): Write 0xffff to SCCE */ + immr->im_scc[CONFIG_ETHER_INDEX-1].scc_scce = ~(0x0); + + /* 24.21 - (22): Write to SCCM to enable TXE, RXF, TXB events */ + immr->im_scc[CONFIG_ETHER_INDEX-1].scc_sccm = (SCCE_ENET_TXE | + SCCE_ENET_RXF | + SCCE_ENET_TXB); + + /* 24.21 - (23): we don't use ethernet interrupts */ + + /* 24.21 - (24): Clear GSMR_H to enable normal operations */ + immr->im_scc[CONFIG_ETHER_INDEX-1].scc_gsmrh = 0; + + /* 24.21 - (25): Clear GSMR_L to enable normal operations */ + immr->im_scc[CONFIG_ETHER_INDEX-1].scc_gsmrl = (SCC_GSMRL_TCI | + SCC_GSMRL_TPL_48 | + SCC_GSMRL_TPP_10 | + SCC_GSMRL_MODE_ENET); + + /* 24.21 - (26): Initialize DSR */ + immr->im_scc[CONFIG_ETHER_INDEX-1].scc_dsr = 0xd555; + + /* 24.21 - (27): Initialize PSMR2 + * + * Settings: + * CRC = 32-Bit CCITT + * NIB = Begin searching for SFD 22 bits after RENA + * FDE = Full Duplex Enable + * BRO = Reject broadcast packets + * PROMISCOUS = Catch all packets regardless of dest. MAC adress + */ + immr->im_scc[CONFIG_ETHER_INDEX-1].scc_psmr = SCC_PSMR_ENCRC | + SCC_PSMR_NIB22 | +#if defined(CONFIG_SCC_ENET_FULL_DUPLEX) + SCC_PSMR_FDE | +#endif +#if defined(CONFIG_SCC_ENET_NO_BROADCAST) + SCC_PSMR_BRO | +#endif +#if defined(CONFIG_SCC_ENET_PROMISCOUS) + SCC_PSMR_PRO | +#endif + 0; + + /* 24.21 - (28): Write to GSMR_L to enable SCC */ + immr->im_scc[CONFIG_ETHER_INDEX-1].scc_gsmrl |= (SCC_GSMRL_ENR | + SCC_GSMRL_ENT); + + return 0; +} + + +static void sec_halt(struct eth_device *dev) +{ + volatile immap_t *immr = (immap_t *)CONFIG_SYS_IMMR; + immr->im_scc[CONFIG_ETHER_INDEX-1].scc_gsmrl &= ~(SCC_GSMRL_ENR | + SCC_GSMRL_ENT); +} + +#if 0 +static void sec_restart(void) +{ + volatile immap_t *immr = (immap_t *)CONFIG_SYS_IMMR; + immr->im_cpm.cp_scc[CONFIG_ETHER_INDEX-1].scc_gsmrl |= (SCC_GSMRL_ENR | + SCC_GSMRL_ENT); +} +#endif + +int mpc82xx_scc_enet_initialize(bd_t *bis) +{ + struct eth_device *dev; + + dev = (struct eth_device *) malloc(sizeof *dev); + memset(dev, 0, sizeof *dev); + + sprintf(dev->name, "SCC ETHERNET"); + dev->init = sec_init; + dev->halt = sec_halt; + dev->send = sec_send; + dev->recv = sec_rx; + + eth_register(dev); + + return 1; +} diff --git a/arch/powerpc/cpu/mpc8260/i2c.c b/arch/powerpc/cpu/mpc8260/i2c.c new file mode 100644 index 0000000..d2bdcc2 --- /dev/null +++ b/arch/powerpc/cpu/mpc8260/i2c.c @@ -0,0 +1,785 @@ +/* + * (C) Copyright 2000 + * Paolo Scaffardi, AIRVENT SAM s.p.a - RIMINI(ITALY), arsenio@tin.it + * + * (C) Copyright 2000 Sysgo Real-Time Solutions, GmbH <www.elinos.com> + * Marius Groeger <mgroeger@sysgo.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_HARD_I2C) + +#include <asm/cpm_8260.h> +#include <i2c.h> + +/* define to enable debug messages */ +#undef DEBUG_I2C + +DECLARE_GLOBAL_DATA_PTR; + +#if defined(CONFIG_I2C_MULTI_BUS) +static unsigned int i2c_bus_num __attribute__ ((section (".data"))) = 0; +#endif /* CONFIG_I2C_MULTI_BUS */ + +/* uSec to wait between polls of the i2c */ +#define DELAY_US 100 +/* uSec to wait for the CPM to start processing the buffer */ +#define START_DELAY_US 1000 + +/* + * tx/rx per-byte timeout: we delay DELAY_US uSec between polls so the + * timeout will be (tx_length + rx_length) * DELAY_US * TOUT_LOOP + */ +#define TOUT_LOOP 5 + +/*----------------------------------------------------------------------- + * Set default values + */ +#ifndef CONFIG_SYS_I2C_SPEED +#define CONFIG_SYS_I2C_SPEED 50000 +#endif + +/*----------------------------------------------------------------------- + */ + +typedef void (*i2c_ecb_t)(int, int, void *); /* error callback function */ + +/* This structure keeps track of the bd and buffer space usage. */ +typedef struct i2c_state { + int rx_idx; /* index to next free Rx BD */ + int tx_idx; /* index to next free Tx BD */ + void *rxbd; /* pointer to next free Rx BD */ + void *txbd; /* pointer to next free Tx BD */ + int tx_space; /* number of Tx bytes left */ + unsigned char *tx_buf; /* pointer to free Tx area */ + i2c_ecb_t err_cb; /* error callback function */ + void *cb_data; /* private data to be passed */ +} i2c_state_t; + +/* flags for i2c_send() and i2c_receive() */ +#define I2CF_ENABLE_SECONDARY 0x01 /* secondary_address is valid */ +#define I2CF_START_COND 0x02 /* tx: generate start condition */ +#define I2CF_STOP_COND 0x04 /* tx: generate stop condition */ + +/* return codes */ +#define I2CERR_NO_BUFFERS 1 /* no more BDs or buffer space */ +#define I2CERR_MSG_TOO_LONG 2 /* tried to send/receive to much data */ +#define I2CERR_TIMEOUT 3 /* timeout in i2c_doio() */ +#define I2CERR_QUEUE_EMPTY 4 /* i2c_doio called without send/receive */ +#define I2CERR_IO_ERROR 5 /* had an error during comms */ + +/* error callback flags */ +#define I2CECB_RX_ERR 0x10 /* this is a receive error */ +#define I2CECB_RX_OV 0x02 /* receive overrun error */ +#define I2CECB_RX_MASK 0x0f /* mask for error bits */ +#define I2CECB_TX_ERR 0x20 /* this is a transmit error */ +#define I2CECB_TX_CL 0x01 /* transmit collision error */ +#define I2CECB_TX_UN 0x02 /* transmit underflow error */ +#define I2CECB_TX_NAK 0x04 /* transmit no ack error */ +#define I2CECB_TX_MASK 0x0f /* mask for error bits */ +#define I2CECB_TIMEOUT 0x40 /* this is a timeout error */ + +#define ERROR_I2C_NONE 0 +#define ERROR_I2C_LENGTH 1 + +#define I2C_WRITE_BIT 0x00 +#define I2C_READ_BIT 0x01 + +#define I2C_RXTX_LEN 128 /* maximum tx/rx buffer length */ + + +#define NUM_RX_BDS 4 +#define NUM_TX_BDS 4 +#define MAX_TX_SPACE 256 + +typedef struct I2C_BD +{ + unsigned short status; + unsigned short length; + unsigned char *addr; +} I2C_BD; +#define BD_I2C_TX_START 0x0400 /* special status for i2c: Start condition */ + +#define BD_I2C_TX_CL 0x0001 /* collision error */ +#define BD_I2C_TX_UN 0x0002 /* underflow error */ +#define BD_I2C_TX_NAK 0x0004 /* no acknowledge error */ +#define BD_I2C_TX_ERR (BD_I2C_TX_NAK|BD_I2C_TX_UN|BD_I2C_TX_CL) + +#define BD_I2C_RX_ERR BD_SC_OV + +#ifdef DEBUG_I2C +#define PRINTD(x) printf x +#else +#define PRINTD(x) +#endif + +/* + * Returns the best value of I2BRG to meet desired clock speed of I2C with + * input parameters (clock speed, filter, and predivider value). + * It returns computer speed value and the difference between it and desired + * speed. + */ +static inline int +i2c_roundrate(int hz, int speed, int filter, int modval, + int *brgval, int *totspeed) +{ + int moddiv = 1 << (5-(modval & 3)), brgdiv, div; + + PRINTD(("\t[I2C] trying hz=%d, speed=%d, filter=%d, modval=%d\n", + hz, speed, filter, modval)); + + div = moddiv * speed; + brgdiv = (hz + div - 1) / div; + + PRINTD(("\t\tmoddiv=%d, brgdiv=%d\n", moddiv, brgdiv)); + + *brgval = ((brgdiv + 1) / 2) - 3 - (2*filter); + + if ((*brgval < 0) || (*brgval > 255)) { + PRINTD(("\t\trejected brgval=%d\n", *brgval)); + return -1; + } + + brgdiv = 2 * (*brgval + 3 + (2 * filter)); + div = moddiv * brgdiv ; + *totspeed = hz / div; + + PRINTD(("\t\taccepted brgval=%d, totspeed=%d\n", *brgval, *totspeed)); + + return 0; +} + +/* + * Sets the I2C clock predivider and divider to meet required clock speed. + */ +static int i2c_setrate(int hz, int speed) +{ + immap_t *immap = (immap_t *)CONFIG_SYS_IMMR ; + volatile i2c8260_t *i2c = (i2c8260_t *)&immap->im_i2c; + int brgval, + modval, /* 0-3 */ + bestspeed_diff = speed, + bestspeed_brgval=0, + bestspeed_modval=0, + bestspeed_filter=0, + totspeed, + filter = 0; /* Use this fixed value */ + + for (modval = 0; modval < 4; modval++) + { + if (i2c_roundrate (hz, speed, filter, modval, &brgval, &totspeed) == 0) + { + int diff = speed - totspeed ; + + if ((diff >= 0) && (diff < bestspeed_diff)) + { + bestspeed_diff = diff ; + bestspeed_modval = modval; + bestspeed_brgval = brgval; + bestspeed_filter = filter; + } + } + } + + PRINTD(("[I2C] Best is:\n")); + PRINTD(("[I2C] CPU=%dhz RATE=%d F=%d I2MOD=%08x I2BRG=%08x DIFF=%dhz\n", + hz, speed, + bestspeed_filter, bestspeed_modval, bestspeed_brgval, + bestspeed_diff)); + + i2c->i2c_i2mod |= ((bestspeed_modval & 3) << 1) | (bestspeed_filter << 3); + i2c->i2c_i2brg = bestspeed_brgval & 0xff; + + PRINTD(("[I2C] i2mod=%08x i2brg=%08x\n", i2c->i2c_i2mod, i2c->i2c_i2brg)); + + return 1 ; +} + +void i2c_init(int speed, int slaveadd) +{ + volatile immap_t *immap = (immap_t *)CONFIG_SYS_IMMR ; + volatile cpm8260_t *cp = (cpm8260_t *)&immap->im_cpm; + volatile i2c8260_t *i2c = (i2c8260_t *)&immap->im_i2c; + volatile iic_t *iip; + ulong rbase, tbase; + volatile I2C_BD *rxbd, *txbd; + uint dpaddr; + +#ifdef CONFIG_SYS_I2C_INIT_BOARD + /* call board specific i2c bus reset routine before accessing the */ + /* environment, which might be in a chip on that bus. For details */ + /* about this problem see doc/I2C_Edge_Conditions. */ + i2c_init_board(); +#endif + + dpaddr = *((unsigned short*)(&immap->im_dprambase[PROFF_I2C_BASE])); + if (dpaddr == 0) { + /* need to allocate dual port ram */ + dpaddr = m8260_cpm_dpalloc(64 + + (NUM_RX_BDS * sizeof(I2C_BD)) + (NUM_TX_BDS * sizeof(I2C_BD)) + + MAX_TX_SPACE, 64); + *((unsigned short*)(&immap->im_dprambase[PROFF_I2C_BASE])) = dpaddr; + } + + /* + * initialise data in dual port ram: + * + * dpaddr -> parameter ram (64 bytes) + * rbase -> rx BD (NUM_RX_BDS * sizeof(I2C_BD) bytes) + * tbase -> tx BD (NUM_TX_BDS * sizeof(I2C_BD) bytes) + * tx buffer (MAX_TX_SPACE bytes) + */ + + iip = (iic_t *)&immap->im_dprambase[dpaddr]; + memset((void*)iip, 0, sizeof(iic_t)); + + rbase = dpaddr + 64; + tbase = rbase + NUM_RX_BDS * sizeof(I2C_BD); + + /* Disable interrupts */ + i2c->i2c_i2mod = 0x00; + i2c->i2c_i2cmr = 0x00; + i2c->i2c_i2cer = 0xff; + i2c->i2c_i2add = slaveadd; + + /* + * Set the I2C BRG Clock division factor from desired i2c rate + * and current CPU rate (we assume sccr dfbgr field is 0; + * divide BRGCLK by 1) + */ + PRINTD(("[I2C] Setting rate...\n")); + i2c_setrate (gd->brg_clk, CONFIG_SYS_I2C_SPEED) ; + + /* Set I2C controller in master mode */ + i2c->i2c_i2com = 0x01; + + /* Initialize Tx/Rx parameters */ + iip->iic_rbase = rbase; + iip->iic_tbase = tbase; + rxbd = (I2C_BD *)((unsigned char *)&immap->im_dprambase[iip->iic_rbase]); + txbd = (I2C_BD *)((unsigned char *)&immap->im_dprambase[iip->iic_tbase]); + + PRINTD(("[I2C] rbase = %04x\n", iip->iic_rbase)); + PRINTD(("[I2C] tbase = %04x\n", iip->iic_tbase)); + PRINTD(("[I2C] rxbd = %08x\n", (int)rxbd)); + PRINTD(("[I2C] txbd = %08x\n", (int)txbd)); + + /* Set big endian byte order */ + iip->iic_tfcr = 0x10; + iip->iic_rfcr = 0x10; + + /* Set maximum receive size. */ + iip->iic_mrblr = I2C_RXTX_LEN; + + cp->cp_cpcr = mk_cr_cmd(CPM_CR_I2C_PAGE, + CPM_CR_I2C_SBLOCK, + 0x00, + CPM_CR_INIT_TRX) | CPM_CR_FLG; + do { + __asm__ __volatile__ ("eieio"); + } while (cp->cp_cpcr & CPM_CR_FLG); + + /* Clear events and interrupts */ + i2c->i2c_i2cer = 0xff; + i2c->i2c_i2cmr = 0x00; +} + +static +void i2c_newio(i2c_state_t *state) +{ + volatile immap_t *immap = (immap_t *)CONFIG_SYS_IMMR ; + volatile iic_t *iip; + uint dpaddr; + + PRINTD(("[I2C] i2c_newio\n")); + + dpaddr = *((unsigned short*)(&immap->im_dprambase[PROFF_I2C_BASE])); + iip = (iic_t *)&immap->im_dprambase[dpaddr]; + state->rx_idx = 0; + state->tx_idx = 0; + state->rxbd = (void*)&immap->im_dprambase[iip->iic_rbase]; + state->txbd = (void*)&immap->im_dprambase[iip->iic_tbase]; + state->tx_space = MAX_TX_SPACE; + state->tx_buf = (uchar*)state->txbd + NUM_TX_BDS * sizeof(I2C_BD); + state->err_cb = NULL; + state->cb_data = NULL; + + PRINTD(("[I2C] rxbd = %08x\n", (int)state->rxbd)); + PRINTD(("[I2C] txbd = %08x\n", (int)state->txbd)); + PRINTD(("[I2C] tx_buf = %08x\n", (int)state->tx_buf)); + + /* clear the buffer memory */ + memset((char *)state->tx_buf, 0, MAX_TX_SPACE); +} + +static +int i2c_send(i2c_state_t *state, + unsigned char address, + unsigned char secondary_address, + unsigned int flags, + unsigned short size, + unsigned char *dataout) +{ + volatile I2C_BD *txbd; + int i,j; + + PRINTD(("[I2C] i2c_send add=%02d sec=%02d flag=%02d size=%d\n", + address, secondary_address, flags, size)); + + /* trying to send message larger than BD */ + if (size > I2C_RXTX_LEN) + return I2CERR_MSG_TOO_LONG; + + /* no more free bds */ + if (state->tx_idx >= NUM_TX_BDS || state->tx_space < (2 + size)) + return I2CERR_NO_BUFFERS; + + txbd = (I2C_BD *)state->txbd; + txbd->addr = state->tx_buf; + + PRINTD(("[I2C] txbd = %08x\n", (int)txbd)); + + if (flags & I2CF_START_COND) + { + PRINTD(("[I2C] Formatting addresses...\n")); + if (flags & I2CF_ENABLE_SECONDARY) + { + txbd->length = size + 2; /* Length of message plus dest addresses */ + txbd->addr[0] = address << 1; + txbd->addr[1] = secondary_address; + i = 2; + } + else + { + txbd->length = size + 1; /* Length of message plus dest address */ + txbd->addr[0] = address << 1; /* Write destination address to BD */ + i = 1; + } + } + else + { + txbd->length = size; /* Length of message */ + i = 0; + } + + /* set up txbd */ + txbd->status = BD_SC_READY; + if (flags & I2CF_START_COND) + txbd->status |= BD_I2C_TX_START; + if (flags & I2CF_STOP_COND) + txbd->status |= BD_SC_LAST | BD_SC_WRAP; + + /* Copy data to send into buffer */ + PRINTD(("[I2C] copy data...\n")); + for(j = 0; j < size; i++, j++) + txbd->addr[i] = dataout[j]; + + PRINTD(("[I2C] txbd: length=0x%04x status=0x%04x addr[0]=0x%02x addr[1]=0x%02x\n", + txbd->length, + txbd->status, + txbd->addr[0], + txbd->addr[1])); + + /* advance state */ + state->tx_buf += txbd->length; + state->tx_space -= txbd->length; + state->tx_idx++; + state->txbd = (void*)(txbd + 1); + + return 0; +} + +static +int i2c_receive(i2c_state_t *state, + unsigned char address, + unsigned char secondary_address, + unsigned int flags, + unsigned short size_to_expect, + unsigned char *datain) +{ + volatile I2C_BD *rxbd, *txbd; + + PRINTD(("[I2C] i2c_receive %02d %02d %02d\n", address, secondary_address, flags)); + + /* Expected to receive too much */ + if (size_to_expect > I2C_RXTX_LEN) + return I2CERR_MSG_TOO_LONG; + + /* no more free bds */ + if (state->tx_idx >= NUM_TX_BDS || state->rx_idx >= NUM_RX_BDS + || state->tx_space < 2) + return I2CERR_NO_BUFFERS; + + rxbd = (I2C_BD *)state->rxbd; + txbd = (I2C_BD *)state->txbd; + + PRINTD(("[I2C] rxbd = %08x\n", (int)rxbd)); + PRINTD(("[I2C] txbd = %08x\n", (int)txbd)); + + txbd->addr = state->tx_buf; + + /* set up TXBD for destination address */ + if (flags & I2CF_ENABLE_SECONDARY) + { + txbd->length = 2; + txbd->addr[0] = address << 1; /* Write data */ + txbd->addr[1] = secondary_address; /* Internal address */ + txbd->status = BD_SC_READY; + } + else + { + txbd->length = 1 + size_to_expect; + txbd->addr[0] = (address << 1) | 0x01; + txbd->status = BD_SC_READY; + memset(&txbd->addr[1], 0, txbd->length); + } + + /* set up rxbd for reception */ + rxbd->status = BD_SC_EMPTY; + rxbd->length = size_to_expect; + rxbd->addr = datain; + + txbd->status |= BD_I2C_TX_START; + if (flags & I2CF_STOP_COND) + { + txbd->status |= BD_SC_LAST | BD_SC_WRAP; + rxbd->status |= BD_SC_WRAP; + } + + PRINTD(("[I2C] txbd: length=0x%04x status=0x%04x addr[0]=0x%02x addr[1]=0x%02x\n", + txbd->length, + txbd->status, + txbd->addr[0], + txbd->addr[1])); + PRINTD(("[I2C] rxbd: length=0x%04x status=0x%04x addr[0]=0x%02x addr[1]=0x%02x\n", + rxbd->length, + rxbd->status, + rxbd->addr[0], + rxbd->addr[1])); + + /* advance state */ + state->tx_buf += txbd->length; + state->tx_space -= txbd->length; + state->tx_idx++; + state->txbd = (void*)(txbd + 1); + state->rx_idx++; + state->rxbd = (void*)(rxbd + 1); + + return 0; +} + + +static +int i2c_doio(i2c_state_t *state) +{ + volatile immap_t *immap = (immap_t *)CONFIG_SYS_IMMR ; + volatile iic_t *iip; + volatile i2c8260_t *i2c = (i2c8260_t *)&immap->im_i2c; + volatile I2C_BD *txbd, *rxbd; + int n, i, b, rxcnt = 0, rxtimeo = 0, txcnt = 0, txtimeo = 0, rc = 0; + uint dpaddr; + + PRINTD(("[I2C] i2c_doio\n")); + + if (state->tx_idx <= 0 && state->rx_idx <= 0) { + PRINTD(("[I2C] No I/O is queued\n")); + return I2CERR_QUEUE_EMPTY; + } + + dpaddr = *((unsigned short*)(&immap->im_dprambase[PROFF_I2C_BASE])); + iip = (iic_t *)&immap->im_dprambase[dpaddr]; + iip->iic_rbptr = iip->iic_rbase; + iip->iic_tbptr = iip->iic_tbase; + + /* Enable I2C */ + PRINTD(("[I2C] Enabling I2C...\n")); + i2c->i2c_i2mod |= 0x01; + + /* Begin transmission */ + i2c->i2c_i2com |= 0x80; + + /* Loop until transmit & receive completed */ + + if ((n = state->tx_idx) > 0) { + + txbd = ((I2C_BD*)state->txbd) - n; + for (i = 0; i < n; i++) { + txtimeo += TOUT_LOOP * txbd->length; + txbd++; + } + + txbd--; /* wait until last in list is done */ + + PRINTD(("[I2C] Transmitting...(txbd=0x%08lx)\n", (ulong)txbd)); + + udelay(START_DELAY_US); /* give it time to start */ + while((txbd->status & BD_SC_READY) && (++txcnt < txtimeo)) { + udelay(DELAY_US); + if (ctrlc()) + return (-1); + __asm__ __volatile__ ("eieio"); + } + } + + if (txcnt < txtimeo && (n = state->rx_idx) > 0) { + + rxbd = ((I2C_BD*)state->rxbd) - n; + for (i = 0; i < n; i++) { + rxtimeo += TOUT_LOOP * rxbd->length; + rxbd++; + } + + rxbd--; /* wait until last in list is done */ + + PRINTD(("[I2C] Receiving...(rxbd=0x%08lx)\n", (ulong)rxbd)); + + udelay(START_DELAY_US); /* give it time to start */ + while((rxbd->status & BD_SC_EMPTY) && (++rxcnt < rxtimeo)) { + udelay(DELAY_US); + if (ctrlc()) + return (-1); + __asm__ __volatile__ ("eieio"); + } + } + + /* Turn off I2C */ + i2c->i2c_i2mod &= ~0x01; + + if ((n = state->tx_idx) > 0) { + for (i = 0; i < n; i++) { + txbd = ((I2C_BD*)state->txbd) - (n - i); + if ((b = txbd->status & BD_I2C_TX_ERR) != 0) { + if (state->err_cb != NULL) + (*state->err_cb)(I2CECB_TX_ERR|b, i, + state->cb_data); + if (rc == 0) + rc = I2CERR_IO_ERROR; + } + } + } + + if ((n = state->rx_idx) > 0) { + for (i = 0; i < n; i++) { + rxbd = ((I2C_BD*)state->rxbd) - (n - i); + if ((b = rxbd->status & BD_I2C_RX_ERR) != 0) { + if (state->err_cb != NULL) + (*state->err_cb)(I2CECB_RX_ERR|b, i, + state->cb_data); + if (rc == 0) + rc = I2CERR_IO_ERROR; + } + } + } + + if ((txtimeo > 0 && txcnt >= txtimeo) || \ + (rxtimeo > 0 && rxcnt >= rxtimeo)) { + if (state->err_cb != NULL) + (*state->err_cb)(I2CECB_TIMEOUT, -1, state->cb_data); + if (rc == 0) + rc = I2CERR_TIMEOUT; + } + + return (rc); +} + +static void +i2c_probe_callback(int flags, int xnum, void *data) +{ + /* + * the only acceptable errors are a transmit NAK or a receive + * overrun - tx NAK means the device does not exist, rx OV + * means the device must have responded to the slave address + * even though the transfer failed + */ + if (flags == (I2CECB_TX_ERR|I2CECB_TX_NAK)) + *(int *)data |= 1; + if (flags == (I2CECB_RX_ERR|I2CECB_RX_OV)) + *(int *)data |= 2; +} + +int +i2c_probe(uchar chip) +{ + i2c_state_t state; + int rc, err_flag; + uchar buf[1]; + + i2c_newio(&state); + + state.err_cb = i2c_probe_callback; + state.cb_data = (void *) &err_flag; + err_flag = 0; + + rc = i2c_receive(&state, chip, 0, I2CF_START_COND|I2CF_STOP_COND, 1, buf); + + if (rc != 0) + return (rc); /* probe failed */ + + rc = i2c_doio(&state); + + if (rc == 0) + return (0); /* device exists - read succeeded */ + + if (rc == I2CERR_TIMEOUT) + return (-1); /* device does not exist - timeout */ + + if (rc != I2CERR_IO_ERROR || err_flag == 0) + return (rc); /* probe failed */ + + if (err_flag & 1) + return (-1); /* device does not exist - had transmit NAK */ + + return (0); /* device exists - had receive overrun */ +} + + +int +i2c_read(uchar chip, uint addr, int alen, uchar *buffer, int len) +{ + i2c_state_t state; + uchar xaddr[4]; + int rc; + + xaddr[0] = (addr >> 24) & 0xFF; + xaddr[1] = (addr >> 16) & 0xFF; + xaddr[2] = (addr >> 8) & 0xFF; + xaddr[3] = addr & 0xFF; + +#ifdef CONFIG_SYS_I2C_EEPROM_ADDR_OVERFLOW + /* + * EEPROM chips that implement "address overflow" are ones + * like Catalyst 24WC04/08/16 which has 9/10/11 bits of address + * and the extra bits end up in the "chip address" bit slots. + * This makes a 24WC08 (1Kbyte) chip look like four 256 byte + * chips. + * + * Note that we consider the length of the address field to still + * be one byte because the extra address bits are hidden in the + * chip address. + */ + chip |= ((addr >> (alen * 8)) & CONFIG_SYS_I2C_EEPROM_ADDR_OVERFLOW); +#endif + + i2c_newio(&state); + + rc = i2c_send(&state, chip, 0, I2CF_START_COND, alen, &xaddr[4-alen]); + if (rc != 0) { + printf("i2c_read: i2c_send failed (%d)\n", rc); + return 1; + } + + rc = i2c_receive(&state, chip, 0, I2CF_STOP_COND, len, buffer); + if (rc != 0) { + printf("i2c_read: i2c_receive failed (%d)\n", rc); + return 1; + } + + rc = i2c_doio(&state); + if (rc != 0) { + printf("i2c_read: i2c_doio failed (%d)\n", rc); + return 1; + } + return 0; +} + +int +i2c_write(uchar chip, uint addr, int alen, uchar *buffer, int len) +{ + i2c_state_t state; + uchar xaddr[4]; + int rc; + + xaddr[0] = (addr >> 24) & 0xFF; + xaddr[1] = (addr >> 16) & 0xFF; + xaddr[2] = (addr >> 8) & 0xFF; + xaddr[3] = addr & 0xFF; + +#ifdef CONFIG_SYS_I2C_EEPROM_ADDR_OVERFLOW + /* + * EEPROM chips that implement "address overflow" are ones + * like Catalyst 24WC04/08/16 which has 9/10/11 bits of address + * and the extra bits end up in the "chip address" bit slots. + * This makes a 24WC08 (1Kbyte) chip look like four 256 byte + * chips. + * + * Note that we consider the length of the address field to still + * be one byte because the extra address bits are hidden in the + * chip address. + */ + chip |= ((addr >> (alen * 8)) & CONFIG_SYS_I2C_EEPROM_ADDR_OVERFLOW); +#endif + + i2c_newio(&state); + + rc = i2c_send(&state, chip, 0, I2CF_START_COND, alen, &xaddr[4-alen]); + if (rc != 0) { + printf("i2c_write: first i2c_send failed (%d)\n", rc); + return 1; + } + + rc = i2c_send(&state, 0, 0, I2CF_STOP_COND, len, buffer); + if (rc != 0) { + printf("i2c_write: second i2c_send failed (%d)\n", rc); + return 1; + } + + rc = i2c_doio(&state); + if (rc != 0) { + printf("i2c_write: i2c_doio failed (%d)\n", rc); + return 1; + } + return 0; +} + +#if defined(CONFIG_I2C_MULTI_BUS) +/* + * Functions for multiple I2C bus handling + */ +unsigned int i2c_get_bus_num(void) +{ + return i2c_bus_num; +} + +int i2c_set_bus_num(unsigned int bus) +{ +#if defined(CONFIG_I2C_MUX) + if (bus < CONFIG_SYS_MAX_I2C_BUS) { + i2c_bus_num = bus; + } else { + int ret; + + ret = i2x_mux_select_mux(bus); + if (ret == 0) + i2c_bus_num = bus; + else + return ret; + } +#else + if (bus >= CONFIG_SYS_MAX_I2C_BUS) + return -1; + i2c_bus_num = bus; +#endif + return 0; +} + +#endif /* CONFIG_I2C_MULTI_BUS */ +#endif /* CONFIG_HARD_I2C */ diff --git a/arch/powerpc/cpu/mpc8260/interrupts.c b/arch/powerpc/cpu/mpc8260/interrupts.c new file mode 100644 index 0000000..2606c60 --- /dev/null +++ b/arch/powerpc/cpu/mpc8260/interrupts.c @@ -0,0 +1,279 @@ +/* + * (C) Copyright 2000-2002 + * Wolfgang Denk, DENX Software Engineering, wd@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 + * + * Hacked for MPC8260 by Murray.Jensen@cmst.csiro.au, 22-Oct-00 + */ + +#include <common.h> +#include <command.h> +#include <mpc8260.h> +#include <mpc8260_irq.h> +#include <asm/processor.h> + +DECLARE_GLOBAL_DATA_PTR; + +/****************************************************************************/ + +struct irq_action { + interrupt_handler_t *handler; + void *arg; + ulong count; +}; + +static struct irq_action irq_handlers[NR_IRQS]; + +static ulong ppc_cached_irq_mask[NR_MASK_WORDS]; + +/****************************************************************************/ +/* this section was ripped out of arch/powerpc/kernel/ppc8260_pic.c in the */ +/* Linux/PPC 2.4.x source. There was no copyright notice in that file. */ + +/* The 8260 internal interrupt controller. It is usually + * the only interrupt controller. + * There are two 32-bit registers (high/low) for up to 64 + * possible interrupts. + * + * Now, the fun starts.....Interrupt Numbers DO NOT MAP + * in a simple arithmetic fashion to mask or pending registers. + * That is, interrupt 4 does not map to bit position 4. + * We create two tables, indexed by vector number, to indicate + * which register to use and which bit in the register to use. + */ +static u_char irq_to_siureg[] = { + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0 +}; + +static u_char irq_to_siubit[] = { + 31, 16, 17, 18, 19, 20, 21, 22, + 23, 24, 25, 26, 27, 28, 29, 30, + 29, 30, 16, 17, 18, 19, 20, 21, + 22, 23, 24, 25, 26, 27, 28, 31, + 0, 1, 2, 3, 4, 5, 6, 7, + 8, 9, 10, 11, 12, 13, 14, 15, + 15, 14, 13, 12, 11, 10, 9, 8, + 7, 6, 5, 4, 3, 2, 1, 0 +}; + +static void m8260_mask_irq (unsigned int irq_nr) +{ + volatile immap_t *immr = (immap_t *) CONFIG_SYS_IMMR; + int bit, word; + volatile uint *simr; + + bit = irq_to_siubit[irq_nr]; + word = irq_to_siureg[irq_nr]; + + simr = &(immr->im_intctl.ic_simrh); + ppc_cached_irq_mask[word] &= ~(1 << (31 - bit)); + simr[word] = ppc_cached_irq_mask[word]; +} + +static void m8260_unmask_irq (unsigned int irq_nr) +{ + volatile immap_t *immr = (immap_t *) CONFIG_SYS_IMMR; + int bit, word; + volatile uint *simr; + + bit = irq_to_siubit[irq_nr]; + word = irq_to_siureg[irq_nr]; + + simr = &(immr->im_intctl.ic_simrh); + ppc_cached_irq_mask[word] |= (1 << (31 - bit)); + simr[word] = ppc_cached_irq_mask[word]; +} + +static void m8260_mask_and_ack (unsigned int irq_nr) +{ + volatile immap_t *immr = (immap_t *) CONFIG_SYS_IMMR; + int bit, word; + volatile uint *simr, *sipnr; + + bit = irq_to_siubit[irq_nr]; + word = irq_to_siureg[irq_nr]; + + simr = &(immr->im_intctl.ic_simrh); + sipnr = &(immr->im_intctl.ic_sipnrh); + ppc_cached_irq_mask[word] &= ~(1 << (31 - bit)); + simr[word] = ppc_cached_irq_mask[word]; + sipnr[word] = 1 << (31 - bit); +} + +static int m8260_get_irq (struct pt_regs *regs) +{ + volatile immap_t *immr = (immap_t *) CONFIG_SYS_IMMR; + int irq; + unsigned long bits; + + /* For MPC8260, read the SIVEC register and shift the bits down + * to get the irq number. */ + bits = immr->im_intctl.ic_sivec; + irq = bits >> 26; + return irq; +} + +/* end of code ripped out of arch/powerpc/kernel/ppc8260_pic.c */ +/****************************************************************************/ + +int interrupt_init_cpu (unsigned *decrementer_count) +{ + volatile immap_t *immr = (immap_t *) CONFIG_SYS_IMMR; + + *decrementer_count = (gd->bus_clk / 4) / CONFIG_SYS_HZ; + + /* Initialize the default interrupt mapping priorities */ + immr->im_intctl.ic_sicr = 0; + immr->im_intctl.ic_siprr = 0x05309770; + immr->im_intctl.ic_scprrh = 0x05309770; + immr->im_intctl.ic_scprrl = 0x05309770; + + /* disable all interrupts and clear all pending bits */ + immr->im_intctl.ic_simrh = ppc_cached_irq_mask[0] = 0; + immr->im_intctl.ic_simrl = ppc_cached_irq_mask[1] = 0; + immr->im_intctl.ic_sipnrh = 0xffffffff; + immr->im_intctl.ic_sipnrl = 0xffffffff; + +#ifdef CONFIG_HYMOD + /* + * ensure all external interrupt sources default to trigger on + * high-to-low transition (i.e. edge triggered active low) + */ + immr->im_intctl.ic_siexr = -1; +#endif + + return (0); +} + +/****************************************************************************/ + +/* + * Handle external interrupts + */ +void external_interrupt (struct pt_regs *regs) +{ + int irq, unmask = 1; + + irq = m8260_get_irq (regs); + + m8260_mask_and_ack (irq); + + enable_interrupts (); + + if (irq_handlers[irq].handler != NULL) + (*irq_handlers[irq].handler) (irq_handlers[irq].arg); + else { + printf ("\nBogus External Interrupt IRQ %d\n", irq); + /* + * turn off the bogus interrupt, otherwise it + * might repeat forever + */ + unmask = 0; + } + + if (unmask) + m8260_unmask_irq (irq); +} + +/****************************************************************************/ + +/* + * Install and free an interrupt handler. + */ + +void +irq_install_handler (int irq, interrupt_handler_t * handler, void *arg) +{ + if (irq < 0 || irq >= NR_IRQS) { + printf ("irq_install_handler: bad irq number %d\n", irq); + return; + } + + if (irq_handlers[irq].handler != NULL) + printf ("irq_install_handler: 0x%08lx replacing 0x%08lx\n", + (ulong) handler, (ulong) irq_handlers[irq].handler); + + irq_handlers[irq].handler = handler; + irq_handlers[irq].arg = arg; + + m8260_unmask_irq (irq); +} + +void irq_free_handler (int irq) +{ + if (irq < 0 || irq >= NR_IRQS) { + printf ("irq_free_handler: bad irq number %d\n", irq); + return; + } + + m8260_mask_irq (irq); + + irq_handlers[irq].handler = NULL; + irq_handlers[irq].arg = NULL; +} + +/****************************************************************************/ + +void timer_interrupt_cpu (struct pt_regs *regs) +{ + /* nothing to do here */ + return; +} + +/****************************************************************************/ + +#if defined(CONFIG_CMD_IRQ) + +/* ripped this out of ppc4xx/interrupts.c */ + +/******************************************************************************* +* +* irqinfo - print information about PCI devices +* +*/ +void +do_irqinfo (cmd_tbl_t * cmdtp, bd_t * bd, int flag, int argc, char *argv[]) +{ + int irq, re_enable; + + re_enable = disable_interrupts (); + + puts ("\nInterrupt-Information:\n" + "Nr Routine Arg Count\n"); + + for (irq = 0; irq < 32; irq++) + if (irq_handlers[irq].handler != NULL) + printf ("%02d %08lx %08lx %ld\n", irq, + (ulong) irq_handlers[irq].handler, + (ulong) irq_handlers[irq].arg, + irq_handlers[irq].count); + + if (re_enable) + enable_interrupts (); +} + +#endif diff --git a/arch/powerpc/cpu/mpc8260/kgdb.S b/arch/powerpc/cpu/mpc8260/kgdb.S new file mode 100644 index 0000000..c5936c7 --- /dev/null +++ b/arch/powerpc/cpu/mpc8260/kgdb.S @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2000 Murray Jensen <Murray.Jensen@cmst.csiro.au> + * + * 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 <config.h> +#include <command.h> +#include <mpc8260.h> +#include <version.h> + +#define CONFIG_8260 1 /* needed for Linux kernel header files */ +#define _LINUX_CONFIG_H 1 /* avoid reading Linux autoconf.h file */ + +#include <ppc_asm.tmpl> +#include <ppc_defs.h> + +#include <asm/cache.h> +#include <asm/mmu.h> + +#if defined(CONFIG_CMD_KGDB) + + /* + * cache flushing routines for kgdb + */ + + .globl kgdb_flush_cache_all +kgdb_flush_cache_all: + mfspr r3, HID0 + ori r3, r3, HID0_ICFI|HID0_DCI /* Invalidate All */ + SYNC + mtspr HID0, r3 + blr + + .globl kgdb_flush_cache_range +kgdb_flush_cache_range: + li r5,CONFIG_SYS_CACHELINE_SIZE-1 + andc r3,r3,r5 + subf r4,r3,r4 + add r4,r4,r5 + srwi. r4,r4,CONFIG_SYS_CACHELINE_SHIFT + beqlr + mtctr r4 + mr r6,r3 +1: dcbst 0,r3 + addi r3,r3,CONFIG_SYS_CACHELINE_SIZE + bdnz 1b + sync /* wait for dcbst's to get to ram */ + mtctr r4 +2: icbi 0,r6 + addi r6,r6,CONFIG_SYS_CACHELINE_SIZE + bdnz 2b + SYNC + blr + +#endif diff --git a/arch/powerpc/cpu/mpc8260/pci.c b/arch/powerpc/cpu/mpc8260/pci.c new file mode 100644 index 0000000..6a14ba4 --- /dev/null +++ b/arch/powerpc/cpu/mpc8260/pci.c @@ -0,0 +1,466 @@ +/* + * (C) Copyright 2003 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * Copyright (c) 2005 MontaVista Software, Inc. + * Vitaly Bordug <vbordug@ru.mvista.com> + * Added support for PCI bridge on MPC8272ADS + * + * 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> + +#ifdef CONFIG_PCI + +#include <pci.h> +#include <mpc8260.h> +#include <asm/m8260_pci.h> +#include <asm/io.h> +#ifdef CONFIG_OF_LIBFDT +#include <libfdt.h> +#include <fdt_support.h> +#endif + +#if defined CONFIG_MPC8266ADS || defined CONFIG_MPC8272 || defined CONFIG_PM826 +DECLARE_GLOBAL_DATA_PTR; +#endif + +/* + * Local->PCI map (from CPU) controlled by + * MPC826x master window + * + * 0x80000000 - 0xBFFFFFFF CPU2PCI space PCIBR0 + * 0xF4000000 - 0xF7FFFFFF CPU2PCI space PCIBR1 + * + * 0x80000000 - 0x9FFFFFFF 0x80000000 - 0x9FFFFFFF (Outbound ATU #1) + * PCI Mem with prefetch + * + * 0xA0000000 - 0xBFFFFFFF 0xA0000000 - 0xBFFFFFFF (Outbound ATU #2) + * PCI Mem w/o prefetch + * + * 0xF4000000 - 0xF7FFFFFF 0x00000000 - 0x03FFFFFF (Outbound ATU #3) + * 32-bit PCI IO + * + * PCI->Local map (from PCI) + * MPC826x slave window controlled by + * + * 0x00000000 - 0x1FFFFFFF 0x00000000 - 0x1FFFFFFF (Inbound ATU #1) + * MPC826x local memory + */ + +/* + * Slave window that allows PCI masters to access MPC826x local memory. + * This window is set up using the first set of Inbound ATU registers + */ + +#ifndef CONFIG_SYS_PCI_SLV_MEM_LOCAL +#define PCI_SLV_MEM_LOCAL CONFIG_SYS_SDRAM_BASE /* Local base */ +#else +#define PCI_SLV_MEM_LOCAL CONFIG_SYS_PCI_SLV_MEM_LOCAL +#endif + +#ifndef CONFIG_SYS_PCI_SLV_MEM_BUS +#define PCI_SLV_MEM_BUS 0x00000000 /* PCI base */ +#else +#define PCI_SLV_MEM_BUS CONFIG_SYS_PCI_SLV_MEM_BUS +#endif + +#ifndef CONFIG_SYS_PICMR0_MASK_ATTRIB +#define PICMR0_MASK_ATTRIB (PICMR_MASK_512MB | PICMR_ENABLE | \ + PICMR_PREFETCH_EN) +#else +#define PICMR0_MASK_ATTRIB CONFIG_SYS_PICMR0_MASK_ATTRIB +#endif + +/* + * These are the windows that allow the CPU to access PCI address space. + * All three PCI master windows, which allow the CPU to access PCI + * prefetch, non prefetch, and IO space (see below), must all fit within + * these windows. + */ + +/* PCIBR0 */ +#ifndef CONFIG_SYS_PCI_MSTR0_LOCAL +#define PCI_MSTR0_LOCAL 0x80000000 /* Local base */ +#else +#define PCI_MSTR0_LOCAL CONFIG_SYS_PCI_MSTR0_LOCAL +#endif + +#ifndef CONFIG_SYS_PCIMSK0_MASK +#define PCIMSK0_MASK PCIMSK_1GB /* Size of window */ +#else +#define PCIMSK0_MASK CONFIG_SYS_PCIMSK0_MASK +#endif + +/* PCIBR1 */ +#ifndef CONFIG_SYS_PCI_MSTR1_LOCAL +#define PCI_MSTR1_LOCAL 0xF4000000 /* Local base */ +#else +#define PCI_MSTR1_LOCAL CONFIG_SYS_PCI_MSTR1_LOCAL +#endif + +#ifndef CONFIG_SYS_PCIMSK1_MASK +#define PCIMSK1_MASK PCIMSK_64MB /* Size of window */ +#else +#define PCIMSK1_MASK CONFIG_SYS_PCIMSK1_MASK +#endif + +/* + * Master window that allows the CPU to access PCI Memory (prefetch). + * This window will be setup with the first set of Outbound ATU registers + * in the bridge. + */ + +#ifndef CONFIG_SYS_PCI_MSTR_MEM_LOCAL +#define PCI_MSTR_MEM_LOCAL 0x80000000 /* Local base */ +#else +#define PCI_MSTR_MEM_LOCAL CONFIG_SYS_PCI_MSTR_MEM_LOCAL +#endif + +#ifndef CONFIG_SYS_PCI_MSTR_MEM_BUS +#define PCI_MSTR_MEM_BUS 0x80000000 /* PCI base */ +#else +#define PCI_MSTR_MEM_BUS CONFIG_SYS_PCI_MSTR_MEM_BUS +#endif + +#ifndef CONFIG_SYS_CPU_PCI_MEM_START +#define CPU_PCI_MEM_START PCI_MSTR_MEM_LOCAL +#else +#define CPU_PCI_MEM_START CONFIG_SYS_CPU_PCI_MEM_START +#endif + +#ifndef CONFIG_SYS_PCI_MSTR_MEM_SIZE +#define PCI_MSTR_MEM_SIZE 0x10000000 /* 256MB */ +#else +#define PCI_MSTR_MEM_SIZE CONFIG_SYS_PCI_MSTR_MEM_SIZE +#endif + +#ifndef CONFIG_SYS_POCMR0_MASK_ATTRIB +#define POCMR0_MASK_ATTRIB (POCMR_MASK_256MB | POCMR_ENABLE | POCMR_PREFETCH_EN) +#else +#define POCMR0_MASK_ATTRIB CONFIG_SYS_POCMR0_MASK_ATTRIB +#endif + +/* + * Master window that allows the CPU to access PCI Memory (non-prefetch). + * This window will be setup with the second set of Outbound ATU registers + * in the bridge. + */ + +#ifndef CONFIG_SYS_PCI_MSTR_MEMIO_LOCAL +#define PCI_MSTR_MEMIO_LOCAL 0x90000000 /* Local base */ +#else +#define PCI_MSTR_MEMIO_LOCAL CONFIG_SYS_PCI_MSTR_MEMIO_LOCAL +#endif + +#ifndef CONFIG_SYS_PCI_MSTR_MEMIO_BUS +#define PCI_MSTR_MEMIO_BUS 0x90000000 /* PCI base */ +#else +#define PCI_MSTR_MEMIO_BUS CONFIG_SYS_PCI_MSTR_MEMIO_BUS +#endif + +#ifndef CONFIG_SYS_CPU_PCI_MEMIO_START +#define CPU_PCI_MEMIO_START PCI_MSTR_MEMIO_LOCAL +#else +#define CPU_PCI_MEMIO_START CONFIG_SYS_CPU_PCI_MEMIO_START +#endif + +#ifndef CONFIG_SYS_PCI_MSTR_MEMIO_SIZE +#define PCI_MSTR_MEMIO_SIZE 0x10000000 /* 256 MB */ +#else +#define PCI_MSTR_MEMIO_SIZE CONFIG_SYS_PCI_MSTR_MEMIO_SIZE +#endif + +#ifndef CONFIG_SYS_POCMR1_MASK_ATTRIB +#define POCMR1_MASK_ATTRIB (POCMR_MASK_512MB | POCMR_ENABLE) +#else +#define POCMR1_MASK_ATTRIB CONFIG_SYS_POCMR1_MASK_ATTRIB +#endif + +/* + * Master window that allows the CPU to access PCI IO space. + * This window will be setup with the third set of Outbound ATU registers + * in the bridge. + */ + +#ifndef CONFIG_SYS_PCI_MSTR_IO_LOCAL +#define PCI_MSTR_IO_LOCAL 0xA0000000 /* Local base */ +#else +#define PCI_MSTR_IO_LOCAL CONFIG_SYS_PCI_MSTR_IO_LOCAL +#endif + +#ifndef CONFIG_SYS_PCI_MSTR_IO_BUS +#define PCI_MSTR_IO_BUS 0xA0000000 /* PCI base */ +#else +#define PCI_MSTR_IO_BUS CONFIG_SYS_PCI_MSTR_IO_BUS +#endif + +#ifndef CONFIG_SYS_CPU_PCI_IO_START +#define CPU_PCI_IO_START PCI_MSTR_IO_LOCAL +#else +#define CPU_PCI_IO_START CONFIG_SYS_CPU_PCI_IO_START +#endif + +#ifndef CONFIG_SYS_PCI_MSTR_IO_SIZE +#define PCI_MSTR_IO_SIZE 0x10000000 /* 256MB */ +#else +#define PCI_MSTR_IO_SIZE CONFIG_SYS_PCI_MSTR_IO_SIZE +#endif + +#ifndef CONFIG_SYS_POCMR2_MASK_ATTRIB +#define POCMR2_MASK_ATTRIB (POCMR_MASK_256MB | POCMR_ENABLE | POCMR_PCI_IO) +#else +#define POCMR2_MASK_ATTRIB CONFIG_SYS_POCMR2_MASK_ATTRIB +#endif + +/* PCI bus configuration registers. + */ + +#define PCI_CLASS_BRIDGE_CTLR 0x06 + + +static inline void pci_outl (u32 addr, u32 data) +{ + *(volatile u32 *) addr = cpu_to_le32 (data); +} + +void pci_mpc8250_init (struct pci_controller *hose) +{ + u16 tempShort; + + volatile immap_t *immap = (immap_t *) CONFIG_SYS_IMMR; + pci_dev_t host_devno = PCI_BDF (0, 0, 0); + + pci_setup_indirect (hose, CONFIG_SYS_IMMR + PCI_CFG_ADDR_REG, + CONFIG_SYS_IMMR + PCI_CFG_DATA_REG); + + /* + * Setting required to enable local bus for PCI (SIUMCR [LBPC]). + */ +#ifdef CONFIG_MPC8266ADS + immap->im_siu_conf.sc_siumcr = + (immap->im_siu_conf.sc_siumcr & ~SIUMCR_LBPC11) + | SIUMCR_LBPC01; +#elif defined(CONFIG_ADSTYPE) && CONFIG_ADSTYPE == CONFIG_SYS_PQ2FADS +/* nothing to do for this board here */ +#elif defined CONFIG_MPC8272 + immap->im_siu_conf.sc_siumcr = (immap->im_siu_conf.sc_siumcr & + ~SIUMCR_BBD & + ~SIUMCR_ESE & + ~SIUMCR_PBSE & + ~SIUMCR_CDIS & + ~SIUMCR_DPPC11 & + ~SIUMCR_L2CPC11 & + ~SIUMCR_LBPC11 & + ~SIUMCR_APPC11 & + ~SIUMCR_CS10PC11 & + ~SIUMCR_BCTLC11 & + ~SIUMCR_MMR11) + | SIUMCR_DPPC11 + | SIUMCR_L2CPC01 + | SIUMCR_LBPC00 + | SIUMCR_APPC10 + | SIUMCR_CS10PC00 + | SIUMCR_BCTLC00 + | SIUMCR_MMR11; +#elif defined(CONFIG_TQM8272) +/* nothing to do for this Board here */ +#else + /* + * Setting required to enable IRQ1-IRQ7 (SIUMCR [DPPC]), + * and local bus for PCI (SIUMCR [LBPC]). + */ + immap->im_siu_conf.sc_siumcr = (immap->im_siu_conf.sc_siumcr & + ~SIUMCR_LBPC11 & + ~SIUMCR_CS10PC11 & + ~SIUMCR_LBPC11) | + SIUMCR_LBPC01 | + SIUMCR_CS10PC01 | + SIUMCR_APPC10; +#endif + + /* Make PCI lowest priority */ + /* Each 4 bits is a device bus request and the MS 4bits + is highest priority */ + /* Bus 4bit value + --- ---------- + CPM high 0b0000 + CPM middle 0b0001 + CPM low 0b0010 + PCI reguest 0b0011 + Reserved 0b0100 + Reserved 0b0101 + Internal Core 0b0110 + External Master 1 0b0111 + External Master 2 0b1000 + External Master 3 0b1001 + The rest are reserved */ + immap->im_siu_conf.sc_ppc_alrh = 0x61207893; + + /* Park bus on core while modifying PCI Bus accesses */ + immap->im_siu_conf.sc_ppc_acr = 0x6; + + /* + * Set up master windows that allow the CPU to access PCI space. These + * windows are set up using the two SIU PCIBR registers. + */ + immap->im_memctl.memc_pcimsk0 = PCIMSK0_MASK; + immap->im_memctl.memc_pcibr0 = PCI_MSTR0_LOCAL | PCIBR_ENABLE; + +#if defined CONFIG_MPC8266ADS || defined CONFIG_MPC8272 + immap->im_memctl.memc_pcimsk1 = PCIMSK1_MASK; + immap->im_memctl.memc_pcibr1 = PCI_MSTR1_LOCAL | PCIBR_ENABLE; +#endif + + /* Release PCI RST (by default the PCI RST signal is held low) */ + immap->im_pci.pci_gcr = cpu_to_le32 (PCIGCR_PCI_BUS_EN); + + /* give it some time */ + { +#if defined CONFIG_MPC8266ADS || defined CONFIG_MPC8272 + /* Give the PCI cards more time to initialize before query + This might be good for other boards also + */ + int i; + + for (i = 0; i < 1000; ++i) +#endif + udelay (1000); + } + + /* + * Set up master window that allows the CPU to access PCI Memory (prefetch) + * space. This window is set up using the first set of Outbound ATU registers. + */ + immap->im_pci.pci_potar0 = cpu_to_le32 (PCI_MSTR_MEM_BUS >> 12); /* PCI base */ + immap->im_pci.pci_pobar0 = cpu_to_le32 (PCI_MSTR_MEM_LOCAL >> 12); /* Local base */ + immap->im_pci.pci_pocmr0 = cpu_to_le32 (POCMR0_MASK_ATTRIB); /* Size & attribute */ + + /* + * Set up master window that allows the CPU to access PCI Memory (non-prefetch) + * space. This window is set up using the second set of Outbound ATU registers. + */ + immap->im_pci.pci_potar1 = cpu_to_le32 (PCI_MSTR_MEMIO_BUS >> 12); /* PCI base */ + immap->im_pci.pci_pobar1 = cpu_to_le32 (PCI_MSTR_MEMIO_LOCAL >> 12); /* Local base */ + immap->im_pci.pci_pocmr1 = cpu_to_le32 (POCMR1_MASK_ATTRIB); /* Size & attribute */ + + /* + * Set up master window that allows the CPU to access PCI IO space. This window + * is set up using the third set of Outbound ATU registers. + */ + immap->im_pci.pci_potar2 = cpu_to_le32 (PCI_MSTR_IO_BUS >> 12); /* PCI base */ + immap->im_pci.pci_pobar2 = cpu_to_le32 (PCI_MSTR_IO_LOCAL >> 12); /* Local base */ + immap->im_pci.pci_pocmr2 = cpu_to_le32 (POCMR2_MASK_ATTRIB); /* Size & attribute */ + + /* + * Set up slave window that allows PCI masters to access MPC826x local memory. + * This window is set up using the first set of Inbound ATU registers + */ + immap->im_pci.pci_pitar0 = cpu_to_le32 (PCI_SLV_MEM_LOCAL >> 12); /* PCI base */ + immap->im_pci.pci_pibar0 = cpu_to_le32 (PCI_SLV_MEM_BUS >> 12); /* Local base */ + immap->im_pci.pci_picmr0 = cpu_to_le32 (PICMR0_MASK_ATTRIB); /* Size & attribute */ + + /* See above for description - puts PCI request as highest priority */ +#ifdef CONFIG_MPC8272 + immap->im_siu_conf.sc_ppc_alrh = 0x01236745; +#else + immap->im_siu_conf.sc_ppc_alrh = 0x03124567; +#endif + + /* Park the bus on the PCI */ + immap->im_siu_conf.sc_ppc_acr = PPC_ACR_BUS_PARK_PCI; + + /* Host mode - specify the bridge as a host-PCI bridge */ + + pci_hose_write_config_byte (hose, host_devno, PCI_CLASS_CODE, + PCI_CLASS_BRIDGE_CTLR); + + /* Enable the host bridge to be a master on the PCI bus, and to act as a PCI memory target */ + pci_hose_read_config_word (hose, host_devno, PCI_COMMAND, &tempShort); + pci_hose_write_config_word (hose, host_devno, PCI_COMMAND, + tempShort | PCI_COMMAND_MASTER | + PCI_COMMAND_MEMORY); + + /* do some bridge init, should be done on all 8260 based bridges */ + pci_hose_write_config_byte (hose, host_devno, PCI_CACHE_LINE_SIZE, + 0x08); + pci_hose_write_config_byte (hose, host_devno, PCI_LATENCY_TIMER, + 0xF8); + + hose->first_busno = 0; + hose->last_busno = 0xff; + + /* System memory space */ +#if defined CONFIG_MPC8266ADS || defined CONFIG_MPC8272 || defined CONFIG_PM826 + pci_set_region (hose->regions + 0, + PCI_SLV_MEM_BUS, + PCI_SLV_MEM_LOCAL, + gd->ram_size, PCI_REGION_MEM | PCI_REGION_SYS_MEMORY); +#else + pci_set_region (hose->regions + 0, + CONFIG_SYS_SDRAM_BASE, + CONFIG_SYS_SDRAM_BASE, + 0x4000000, PCI_REGION_MEM | PCI_REGION_SYS_MEMORY); +#endif + + /* PCI memory space */ +#if defined CONFIG_MPC8266ADS || defined CONFIG_MPC8272 + pci_set_region (hose->regions + 1, + PCI_MSTR_MEMIO_BUS, + PCI_MSTR_MEMIO_LOCAL, + PCI_MSTR_MEMIO_SIZE, PCI_REGION_MEM); +#else + pci_set_region (hose->regions + 1, + PCI_MSTR_MEM_BUS, + PCI_MSTR_MEM_LOCAL, + PCI_MSTR_MEM_SIZE, PCI_REGION_MEM); +#endif + + /* PCI I/O space */ + pci_set_region (hose->regions + 2, + PCI_MSTR_IO_BUS, + PCI_MSTR_IO_LOCAL, PCI_MSTR_IO_SIZE, PCI_REGION_IO); + + hose->region_count = 3; + + pci_register_hose (hose); + /* Mask off master abort machine checks */ + immap->im_pci.pci_emr &= cpu_to_le32 (~PCI_ERROR_PCI_NO_RSP); + eieio (); + + hose->last_busno = pci_hose_scan (hose); + + + /* clear the error in the error status register */ + immap->im_pci.pci_esr = cpu_to_le32 (PCI_ERROR_PCI_NO_RSP); + + /* unmask master abort machine checks */ + immap->im_pci.pci_emr |= cpu_to_le32 (PCI_ERROR_PCI_NO_RSP); +} + +#if defined(CONFIG_OF_LIBFDT) +void ft_pci_setup(void *blob, bd_t *bd) +{ + do_fixup_by_prop_u32(blob, "device_type", "pci", 4, + "clock-frequency", gd->pci_clk, 1); +} +#endif + +#endif /* CONFIG_PCI */ diff --git a/arch/powerpc/cpu/mpc8260/serial_scc.c b/arch/powerpc/cpu/mpc8260/serial_scc.c new file mode 100644 index 0000000..4ab6a28 --- /dev/null +++ b/arch/powerpc/cpu/mpc8260/serial_scc.c @@ -0,0 +1,498 @@ +/* + * (C) Copyright 2000 + * Wolfgang Denk, DENX Software Engineering, wd@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 + * + * Hacked for MPC8260 by Murray.Jensen@cmst.csiro.au, 19-Oct-00. + */ + +/* + * Minimal serial functions needed to use one of the SCC ports + * as serial console interface. + */ + +#include <common.h> +#include <mpc8260.h> +#include <asm/cpm_8260.h> + +DECLARE_GLOBAL_DATA_PTR; + +#if defined(CONFIG_CONS_ON_SCC) + +#if CONFIG_CONS_INDEX == 1 /* Console on SCC1 */ + +#define SCC_INDEX 0 +#define PROFF_SCC PROFF_SCC1 +#define CMXSCR_MASK (CMXSCR_GR1|CMXSCR_SC1|\ + CMXSCR_RS1CS_MSK|CMXSCR_TS1CS_MSK) +#define CMXSCR_VALUE (CMXSCR_RS1CS_BRG1|CMXSCR_TS1CS_BRG1) +#define CPM_CR_SCC_PAGE CPM_CR_SCC1_PAGE +#define CPM_CR_SCC_SBLOCK CPM_CR_SCC1_SBLOCK + +#elif CONFIG_CONS_INDEX == 2 /* Console on SCC2 */ + +#define SCC_INDEX 1 +#define PROFF_SCC PROFF_SCC2 +#define CMXSCR_MASK (CMXSCR_GR2|CMXSCR_SC2|\ + CMXSCR_RS2CS_MSK|CMXSCR_TS2CS_MSK) +#define CMXSCR_VALUE (CMXSCR_RS2CS_BRG2|CMXSCR_TS2CS_BRG2) +#define CPM_CR_SCC_PAGE CPM_CR_SCC2_PAGE +#define CPM_CR_SCC_SBLOCK CPM_CR_SCC2_SBLOCK + +#elif CONFIG_CONS_INDEX == 3 /* Console on SCC3 */ + +#define SCC_INDEX 2 +#define PROFF_SCC PROFF_SCC3 +#define CMXSCR_MASK (CMXSCR_GR3|CMXSCR_SC3|\ + CMXSCR_RS3CS_MSK|CMXSCR_TS3CS_MSK) +#define CMXSCR_VALUE (CMXSCR_RS3CS_BRG3|CMXSCR_TS3CS_BRG3) +#define CPM_CR_SCC_PAGE CPM_CR_SCC3_PAGE +#define CPM_CR_SCC_SBLOCK CPM_CR_SCC3_SBLOCK + +#elif CONFIG_CONS_INDEX == 4 /* Console on SCC4 */ + +#define SCC_INDEX 3 +#define PROFF_SCC PROFF_SCC4 +#define CMXSCR_MASK (CMXSCR_GR4|CMXSCR_SC4|\ + CMXSCR_RS4CS_MSK|CMXSCR_TS4CS_MSK) +#define CMXSCR_VALUE (CMXSCR_RS4CS_BRG4|CMXSCR_TS4CS_BRG4) +#define CPM_CR_SCC_PAGE CPM_CR_SCC4_PAGE +#define CPM_CR_SCC_SBLOCK CPM_CR_SCC4_SBLOCK + +#else + +#error "console not correctly defined" + +#endif + +int serial_init (void) +{ + volatile immap_t *im = (immap_t *)CONFIG_SYS_IMMR; + volatile scc_t *sp; + volatile scc_uart_t *up; + volatile cbd_t *tbdf, *rbdf; + volatile cpm8260_t *cp = &(im->im_cpm); + uint dpaddr; + + /* initialize pointers to SCC */ + + sp = (scc_t *) &(im->im_scc[SCC_INDEX]); + up = (scc_uart_t *)&im->im_dprambase[PROFF_SCC]; + + /* Disable transmitter/receiver. + */ + sp->scc_gsmrl &= ~(SCC_GSMRL_ENR | SCC_GSMRL_ENT); + + /* put the SCC channel into NMSI (non multiplexd serial interface) + * mode and wire the selected SCC Tx and Rx clocks to BRGx (15-15). + */ + im->im_cpmux.cmx_scr = (im->im_cpmux.cmx_scr&~CMXSCR_MASK)|CMXSCR_VALUE; + + /* Set up the baud rate generator. + */ + serial_setbrg (); + + /* Allocate space for two buffer descriptors in the DP ram. + * damm: allocating space after the two buffers for rx/tx data + */ + + dpaddr = m8260_cpm_dpalloc((2 * sizeof (cbd_t)) + 2, 16); + + /* Set the physical address of the host memory buffers in + * the buffer descriptors. + */ + rbdf = (cbd_t *)&im->im_dprambase[dpaddr]; + rbdf->cbd_bufaddr = (uint) (rbdf+2); + rbdf->cbd_sc = BD_SC_EMPTY | BD_SC_WRAP; + tbdf = rbdf + 1; + tbdf->cbd_bufaddr = ((uint) (rbdf+2)) + 1; + tbdf->cbd_sc = BD_SC_WRAP; + + /* Set up the uart parameters in the parameter ram. + */ + up->scc_genscc.scc_rbase = dpaddr; + up->scc_genscc.scc_tbase = dpaddr+sizeof(cbd_t); + up->scc_genscc.scc_rfcr = CPMFCR_EB; + up->scc_genscc.scc_tfcr = CPMFCR_EB; + up->scc_genscc.scc_mrblr = 1; + up->scc_maxidl = 0; + up->scc_brkcr = 1; + up->scc_parec = 0; + up->scc_frmec = 0; + up->scc_nosec = 0; + up->scc_brkec = 0; + up->scc_uaddr1 = 0; + up->scc_uaddr2 = 0; + up->scc_toseq = 0; + up->scc_char1 = up->scc_char2 = up->scc_char3 = up->scc_char4 = 0x8000; + up->scc_char5 = up->scc_char6 = up->scc_char7 = up->scc_char8 = 0x8000; + up->scc_rccm = 0xc0ff; + + /* Mask all interrupts and remove anything pending. + */ + sp->scc_sccm = 0; + sp->scc_scce = 0xffff; + + /* Set 8 bit FIFO, 16 bit oversampling and UART mode. + */ + sp->scc_gsmrh = SCC_GSMRH_RFW; /* 8 bit FIFO */ + sp->scc_gsmrl = \ + SCC_GSMRL_TDCR_16 | SCC_GSMRL_RDCR_16 | SCC_GSMRL_MODE_UART; + + /* Set CTS flow control, 1 stop bit, 8 bit character length, + * normal async UART mode, no parity + */ + sp->scc_psmr = SCU_PSMR_FLC | SCU_PSMR_CL; + + /* execute the "Init Rx and Tx params" CP command. + */ + + while (cp->cp_cpcr & CPM_CR_FLG) /* wait if cp is busy */ + ; + + cp->cp_cpcr = mk_cr_cmd(CPM_CR_SCC_PAGE, CPM_CR_SCC_SBLOCK, + 0, CPM_CR_INIT_TRX) | CPM_CR_FLG; + + while (cp->cp_cpcr & CPM_CR_FLG) /* wait if cp is busy */ + ; + + /* Enable transmitter/receiver. + */ + sp->scc_gsmrl |= SCC_GSMRL_ENR | SCC_GSMRL_ENT; + + return (0); +} + +void +serial_setbrg (void) +{ +#if defined(CONFIG_CONS_USE_EXTC) + m8260_cpm_extcbrg(SCC_INDEX, gd->baudrate, + CONFIG_CONS_EXTC_RATE, CONFIG_CONS_EXTC_PINSEL); +#else + m8260_cpm_setbrg(SCC_INDEX, gd->baudrate); +#endif +} + +void +serial_putc(const char c) +{ + volatile scc_uart_t *up; + volatile cbd_t *tbdf; + volatile immap_t *im; + + if (c == '\n') + serial_putc ('\r'); + + im = (immap_t *)CONFIG_SYS_IMMR; + up = (scc_uart_t *)&im->im_dprambase[PROFF_SCC]; + tbdf = (cbd_t *)&im->im_dprambase[up->scc_genscc.scc_tbase]; + + /* Wait for last character to go. + */ + while (tbdf->cbd_sc & BD_SC_READY) + ; + + /* Load the character into the transmit buffer. + */ + *(volatile char *)tbdf->cbd_bufaddr = c; + tbdf->cbd_datlen = 1; + tbdf->cbd_sc |= BD_SC_READY; +} + +void +serial_puts (const char *s) +{ + while (*s) { + serial_putc (*s++); + } +} + +int +serial_getc(void) +{ + volatile cbd_t *rbdf; + volatile scc_uart_t *up; + volatile immap_t *im; + unsigned char c; + + im = (immap_t *)CONFIG_SYS_IMMR; + up = (scc_uart_t *)&im->im_dprambase[PROFF_SCC]; + rbdf = (cbd_t *)&im->im_dprambase[up->scc_genscc.scc_rbase]; + + /* Wait for character to show up. + */ + while (rbdf->cbd_sc & BD_SC_EMPTY) + ; + + /* Grab the char and clear the buffer again. + */ + c = *(volatile unsigned char *)rbdf->cbd_bufaddr; + rbdf->cbd_sc |= BD_SC_EMPTY; + + return (c); +} + +int +serial_tstc() +{ + volatile cbd_t *rbdf; + volatile scc_uart_t *up; + volatile immap_t *im; + + im = (immap_t *)CONFIG_SYS_IMMR; + up = (scc_uart_t *)&im->im_dprambase[PROFF_SCC]; + rbdf = (cbd_t *)&im->im_dprambase[up->scc_genscc.scc_rbase]; + + return ((rbdf->cbd_sc & BD_SC_EMPTY) == 0); +} + +#endif /* CONFIG_CONS_ON_SCC */ + +#if defined(CONFIG_KGDB_ON_SCC) + +#if defined(CONFIG_CONS_ON_SCC) && CONFIG_KGDB_INDEX == CONFIG_CONS_INDEX +#error Whoops! serial console and kgdb are on the same scc serial port +#endif + +#if CONFIG_KGDB_INDEX == 1 /* KGDB Port on SCC1 */ + +#define KGDB_SCC_INDEX 0 +#define KGDB_PROFF_SCC PROFF_SCC1 +#define KGDB_CMXSCR_MASK (CMXSCR_GR1|CMXSCR_SC1|\ + CMXSCR_RS1CS_MSK|CMXSCR_TS1CS_MSK) +#define KGDB_CMXSCR_VALUE (CMXSCR_RS1CS_BRG1|CMXSCR_TS1CS_BRG1) +#define KGDB_CPM_CR_SCC_PAGE CPM_CR_SCC1_PAGE +#define KGDB_CPM_CR_SCC_SBLOCK CPM_CR_SCC1_SBLOCK + +#elif CONFIG_KGDB_INDEX == 2 /* KGDB Port on SCC2 */ + +#define KGDB_SCC_INDEX 1 +#define KGDB_PROFF_SCC PROFF_SCC2 +#define KGDB_CMXSCR_MASK (CMXSCR_GR2|CMXSCR_SC2|\ + CMXSCR_RS2CS_MSK|CMXSCR_TS2CS_MSK) +#define KGDB_CMXSCR_VALUE (CMXSCR_RS2CS_BRG2|CMXSCR_TS2CS_BRG2) +#define KGDB_CPM_CR_SCC_PAGE CPM_CR_SCC2_PAGE +#define KGDB_CPM_CR_SCC_SBLOCK CPM_CR_SCC2_SBLOCK + +#elif CONFIG_KGDB_INDEX == 3 /* KGDB Port on SCC3 */ + +#define KGDB_SCC_INDEX 2 +#define KGDB_PROFF_SCC PROFF_SCC3 +#define KGDB_CMXSCR_MASK (CMXSCR_GR3|CMXSCR_SC3|\ + CMXSCR_RS3CS_MSK|CMXSCR_TS3CS_MSK) +#define KGDB_CMXSCR_VALUE (CMXSCR_RS3CS_BRG3|CMXSCR_TS3CS_BRG3) +#define KGDB_CPM_CR_SCC_PAGE CPM_CR_SCC3_PAGE +#define KGDB_CPM_CR_SCC_SBLOCK CPM_CR_SCC3_SBLOCK + +#elif CONFIG_KGDB_INDEX == 4 /* KGDB Port on SCC4 */ + +#define KGDB_SCC_INDEX 3 +#define KGDB_PROFF_SCC PROFF_SCC4 +#define KGDB_CMXSCR_MASK (CMXSCR_GR4|CMXSCR_SC4|\ + CMXSCR_RS4CS_MSK|CMXSCR_TS4CS_MSK) +#define KGDB_CMXSCR_VALUE (CMXSCR_RS4CS_BRG4|CMXSCR_TS4CS_BRG4) +#define KGDB_CPM_CR_SCC_PAGE CPM_CR_SCC4_PAGE +#define KGDB_CPM_CR_SCC_SBLOCK CPM_CR_SCC4_SBLOCK + +#else + +#error "kgdb serial port not correctly defined" + +#endif + +void +kgdb_serial_init (void) +{ + volatile immap_t *im = (immap_t *)CONFIG_SYS_IMMR; + volatile scc_t *sp; + volatile scc_uart_t *up; + volatile cbd_t *tbdf, *rbdf; + volatile cpm8260_t *cp = &(im->im_cpm); + uint dpaddr, speed = CONFIG_KGDB_BAUDRATE; + char *s, *e; + + if ((s = getenv("kgdbrate")) != NULL && *s != '\0') { + ulong rate = simple_strtoul(s, &e, 10); + if (e > s && *e == '\0') + speed = rate; + } + + /* initialize pointers to SCC */ + + sp = (scc_t *) &(im->im_scc[KGDB_SCC_INDEX]); + up = (scc_uart_t *)&im->im_dprambase[KGDB_PROFF_SCC]; + + /* Disable transmitter/receiver. + */ + sp->scc_gsmrl &= ~(SCC_GSMRL_ENR | SCC_GSMRL_ENT); + + /* put the SCC channel into NMSI (non multiplexd serial interface) + * mode and wire the selected SCC Tx and Rx clocks to BRGx (15-15). + */ + im->im_cpmux.cmx_scr = \ + (im->im_cpmux.cmx_scr & ~KGDB_CMXSCR_MASK) | KGDB_CMXSCR_VALUE; + + /* Set up the baud rate generator. + */ +#if defined(CONFIG_KGDB_USE_EXTC) + m8260_cpm_extcbrg(KGDB_SCC_INDEX, speed, + CONFIG_KGDB_EXTC_RATE, CONFIG_KGDB_EXTC_PINSEL); +#else + m8260_cpm_setbrg(KGDB_SCC_INDEX, speed); +#endif + + /* Allocate space for two buffer descriptors in the DP ram. + * damm: allocating space after the two buffers for rx/tx data + */ + + dpaddr = m8260_cpm_dpalloc((2 * sizeof (cbd_t)) + 2, 16); + + /* Set the physical address of the host memory buffers in + * the buffer descriptors. + */ + rbdf = (cbd_t *)&im->im_dprambase[dpaddr]; + rbdf->cbd_bufaddr = (uint) (rbdf+2); + rbdf->cbd_sc = BD_SC_EMPTY | BD_SC_WRAP; + tbdf = rbdf + 1; + tbdf->cbd_bufaddr = ((uint) (rbdf+2)) + 1; + tbdf->cbd_sc = BD_SC_WRAP; + + /* Set up the uart parameters in the parameter ram. + */ + up->scc_genscc.scc_rbase = dpaddr; + up->scc_genscc.scc_tbase = dpaddr+sizeof(cbd_t); + up->scc_genscc.scc_rfcr = CPMFCR_EB; + up->scc_genscc.scc_tfcr = CPMFCR_EB; + up->scc_genscc.scc_mrblr = 1; + up->scc_maxidl = 0; + up->scc_brkcr = 1; + up->scc_parec = 0; + up->scc_frmec = 0; + up->scc_nosec = 0; + up->scc_brkec = 0; + up->scc_uaddr1 = 0; + up->scc_uaddr2 = 0; + up->scc_toseq = 0; + up->scc_char1 = up->scc_char2 = up->scc_char3 = up->scc_char4 = 0x8000; + up->scc_char5 = up->scc_char6 = up->scc_char7 = up->scc_char8 = 0x8000; + up->scc_rccm = 0xc0ff; + + /* Mask all interrupts and remove anything pending. + */ + sp->scc_sccm = 0; + sp->scc_scce = 0xffff; + + /* Set 8 bit FIFO, 16 bit oversampling and UART mode. + */ + sp->scc_gsmrh = SCC_GSMRH_RFW; /* 8 bit FIFO */ + sp->scc_gsmrl = \ + SCC_GSMRL_TDCR_16 | SCC_GSMRL_RDCR_16 | SCC_GSMRL_MODE_UART; + + /* Set CTS flow control, 1 stop bit, 8 bit character length, + * normal async UART mode, no parity + */ + sp->scc_psmr = SCU_PSMR_FLC | SCU_PSMR_CL; + + /* execute the "Init Rx and Tx params" CP command. + */ + + while (cp->cp_cpcr & CPM_CR_FLG) /* wait if cp is busy */ + ; + + cp->cp_cpcr = mk_cr_cmd(KGDB_CPM_CR_SCC_PAGE, KGDB_CPM_CR_SCC_SBLOCK, + 0, CPM_CR_INIT_TRX) | CPM_CR_FLG; + + while (cp->cp_cpcr & CPM_CR_FLG) /* wait if cp is busy */ + ; + + /* Enable transmitter/receiver. + */ + sp->scc_gsmrl |= SCC_GSMRL_ENR | SCC_GSMRL_ENT; + + printf("SCC%d at %dbps ", CONFIG_KGDB_INDEX, speed); +} + +void +putDebugChar(const char c) +{ + volatile scc_uart_t *up; + volatile cbd_t *tbdf; + volatile immap_t *im; + + if (c == '\n') + putDebugChar ('\r'); + + im = (immap_t *)CONFIG_SYS_IMMR; + up = (scc_uart_t *)&im->im_dprambase[KGDB_PROFF_SCC]; + tbdf = (cbd_t *)&im->im_dprambase[up->scc_genscc.scc_tbase]; + + /* Wait for last character to go. + */ + while (tbdf->cbd_sc & BD_SC_READY) + ; + + /* Load the character into the transmit buffer. + */ + *(volatile char *)tbdf->cbd_bufaddr = c; + tbdf->cbd_datlen = 1; + tbdf->cbd_sc |= BD_SC_READY; +} + +void +putDebugStr (const char *s) +{ + while (*s) { + putDebugChar (*s++); + } +} + +int +getDebugChar(void) +{ + volatile cbd_t *rbdf; + volatile scc_uart_t *up; + volatile immap_t *im; + unsigned char c; + + im = (immap_t *)CONFIG_SYS_IMMR; + up = (scc_uart_t *)&im->im_dprambase[KGDB_PROFF_SCC]; + rbdf = (cbd_t *)&im->im_dprambase[up->scc_genscc.scc_rbase]; + + /* Wait for character to show up. + */ + while (rbdf->cbd_sc & BD_SC_EMPTY) + ; + + /* Grab the char and clear the buffer again. + */ + c = *(volatile unsigned char *)rbdf->cbd_bufaddr; + rbdf->cbd_sc |= BD_SC_EMPTY; + + return (c); +} + +void +kgdb_interruptible(int yes) +{ + return; +} + +#endif /* CONFIG_KGDB_ON_SCC */ diff --git a/arch/powerpc/cpu/mpc8260/serial_smc.c b/arch/powerpc/cpu/mpc8260/serial_smc.c new file mode 100644 index 0000000..7b6eaba --- /dev/null +++ b/arch/powerpc/cpu/mpc8260/serial_smc.c @@ -0,0 +1,467 @@ +/* + * (C) Copyright 2000, 2001, 2002 + * Wolfgang Denk, DENX Software Engineering, wd@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 + * + * Hacked for MPC8260 by Murray.Jensen@cmst.csiro.au, 19-Oct-00, with + * changes based on the file arch/powerpc/mbxboot/m8260_tty.c from the + * Linux/PPC sources (m8260_tty.c had no copyright info in it). + */ + +/* + * Minimal serial functions needed to use one of the SMC ports + * as serial console interface. + */ + +#include <common.h> +#include <mpc8260.h> +#include <asm/cpm_8260.h> + +DECLARE_GLOBAL_DATA_PTR; + +#if defined(CONFIG_CONS_ON_SMC) + +#if CONFIG_CONS_INDEX == 1 /* Console on SMC1 */ + +#define SMC_INDEX 0 +#define PROFF_SMC_BASE PROFF_SMC1_BASE +#define PROFF_SMC PROFF_SMC1 +#define CPM_CR_SMC_PAGE CPM_CR_SMC1_PAGE +#define CPM_CR_SMC_SBLOCK CPM_CR_SMC1_SBLOCK +#define CMXSMR_MASK (CMXSMR_SMC1|CMXSMR_SMC1CS_MSK) +#define CMXSMR_VALUE CMXSMR_SMC1CS_BRG7 + +#elif CONFIG_CONS_INDEX == 2 /* Console on SMC2 */ + +#define SMC_INDEX 1 +#define PROFF_SMC_BASE PROFF_SMC2_BASE +#define PROFF_SMC PROFF_SMC2 +#define CPM_CR_SMC_PAGE CPM_CR_SMC2_PAGE +#define CPM_CR_SMC_SBLOCK CPM_CR_SMC2_SBLOCK +#define CMXSMR_MASK (CMXSMR_SMC2|CMXSMR_SMC2CS_MSK) +#define CMXSMR_VALUE CMXSMR_SMC2CS_BRG8 + +#else + +#error "console not correctly defined" + +#endif + +#if !defined(CONFIG_SYS_SMC_RXBUFLEN) +#define CONFIG_SYS_SMC_RXBUFLEN 1 +#define CONFIG_SYS_MAXIDLE 0 +#else +#if !defined(CONFIG_SYS_MAXIDLE) +#error "you must define CONFIG_SYS_MAXIDLE" +#endif +#endif + +typedef volatile struct serialbuffer { + cbd_t rxbd; /* Rx BD */ + cbd_t txbd; /* Tx BD */ + uint rxindex; /* index for next character to read */ + volatile uchar rxbuf[CONFIG_SYS_SMC_RXBUFLEN];/* rx buffers */ + volatile uchar txbuf; /* tx buffers */ +} serialbuffer_t; + +/* map rs_table index to baud rate generator index */ +static unsigned char brg_map[] = { + 6, /* BRG7 for SMC1 */ + 7, /* BRG8 for SMC2 */ + 0, /* BRG1 for SCC1 */ + 1, /* BRG1 for SCC2 */ + 2, /* BRG1 for SCC3 */ + 3, /* BRG1 for SCC4 */ +}; + +int serial_init (void) +{ + volatile immap_t *im = (immap_t *)CONFIG_SYS_IMMR; + volatile smc_t *sp; + volatile smc_uart_t *up; + volatile cpm8260_t *cp = &(im->im_cpm); + uint dpaddr; + volatile serialbuffer_t *rtx; + + /* initialize pointers to SMC */ + + sp = (smc_t *) &(im->im_smc[SMC_INDEX]); + *(ushort *)(&im->im_dprambase[PROFF_SMC_BASE]) = PROFF_SMC; + up = (smc_uart_t *)&im->im_dprambase[PROFF_SMC]; + + /* Disable transmitter/receiver. */ + sp->smc_smcmr &= ~(SMCMR_REN | SMCMR_TEN); + + /* NOTE: I/O port pins are set up via the iop_conf_tab[] table */ + + /* Allocate space for two buffer descriptors in the DP ram. + * damm: allocating space after the two buffers for rx/tx data + */ + + /* allocate size of struct serialbuffer with bd rx/tx, + * buffer rx/tx and rx index + */ + dpaddr = m8260_cpm_dpalloc((sizeof(serialbuffer_t)), 16); + + rtx = (serialbuffer_t *)&im->im_dprambase[dpaddr]; + + /* Set the physical address of the host memory buffers in + * the buffer descriptors. + */ + rtx->rxbd.cbd_bufaddr = (uint) &rtx->rxbuf; + rtx->rxbd.cbd_sc = 0; + + rtx->txbd.cbd_bufaddr = (uint) &rtx->txbuf; + rtx->txbd.cbd_sc = 0; + + /* Set up the uart parameters in the parameter ram. */ + up->smc_rbase = dpaddr; + up->smc_tbase = dpaddr+sizeof(cbd_t); + up->smc_rfcr = CPMFCR_EB; + up->smc_tfcr = CPMFCR_EB; + up->smc_brklen = 0; + up->smc_brkec = 0; + up->smc_brkcr = 0; + + /* Set UART mode, 8 bit, no parity, one stop. + * Enable receive and transmit. + */ + sp->smc_smcmr = smcr_mk_clen(9) | SMCMR_SM_UART; + + /* Mask all interrupts and remove anything pending. */ + sp->smc_smcm = 0; + sp->smc_smce = 0xff; + + /* put the SMC channel into NMSI (non multiplexd serial interface) + * mode and wire either BRG7 to SMC1 or BRG8 to SMC2 (15-17). + */ + im->im_cpmux.cmx_smr = (im->im_cpmux.cmx_smr&~CMXSMR_MASK)|CMXSMR_VALUE; + + /* Set up the baud rate generator. */ + serial_setbrg (); + + /* Make the first buffer the only buffer. */ + rtx->txbd.cbd_sc |= BD_SC_WRAP; + rtx->rxbd.cbd_sc |= BD_SC_EMPTY | BD_SC_WRAP; + + /* single/multi character receive. */ + up->smc_mrblr = CONFIG_SYS_SMC_RXBUFLEN; + up->smc_maxidl = CONFIG_SYS_MAXIDLE; + rtx->rxindex = 0; + + /* Initialize Tx/Rx parameters. */ + + while (cp->cp_cpcr & CPM_CR_FLG) /* wait if cp is busy */ + ; + + cp->cp_cpcr = mk_cr_cmd(CPM_CR_SMC_PAGE, CPM_CR_SMC_SBLOCK, + 0, CPM_CR_INIT_TRX) | CPM_CR_FLG; + + while (cp->cp_cpcr & CPM_CR_FLG) /* wait if cp is busy */ + ; + + /* Enable transmitter/receiver. */ + sp->smc_smcmr |= SMCMR_REN | SMCMR_TEN; + + return (0); +} + +void +serial_setbrg (void) +{ +#if defined(CONFIG_CONS_USE_EXTC) + m8260_cpm_extcbrg(brg_map[SMC_INDEX], gd->baudrate, + CONFIG_CONS_EXTC_RATE, CONFIG_CONS_EXTC_PINSEL); +#else + m8260_cpm_setbrg(brg_map[SMC_INDEX], gd->baudrate); +#endif +} + +void +serial_putc(const char c) +{ + volatile smc_uart_t *up; + volatile immap_t *im = (immap_t *)CONFIG_SYS_IMMR; + volatile serialbuffer_t *rtx; + + if (c == '\n') + serial_putc ('\r'); + + up = (smc_uart_t *)&(im->im_dprambase[PROFF_SMC]); + + rtx = (serialbuffer_t *)&im->im_dprambase[up->smc_rbase]; + + /* Wait for last character to go. */ + while (rtx->txbd.cbd_sc & BD_SC_READY & BD_SC_READY) + ; + rtx->txbuf = c; + rtx->txbd.cbd_datlen = 1; + rtx->txbd.cbd_sc |= BD_SC_READY; +} + +void +serial_puts (const char *s) +{ + while (*s) { + serial_putc (*s++); + } +} + +int +serial_getc(void) +{ + volatile smc_uart_t *up; + volatile immap_t *im = (immap_t *)CONFIG_SYS_IMMR; + volatile serialbuffer_t *rtx; + unsigned char c; + + up = (smc_uart_t *)&(im->im_dprambase[PROFF_SMC]); + + rtx = (serialbuffer_t *)&im->im_dprambase[up->smc_rbase]; + + /* Wait for character to show up. */ + while (rtx->rxbd.cbd_sc & BD_SC_EMPTY) + ; + + /* the characters are read one by one, + * use the rxindex to know the next char to deliver + */ + c = *(unsigned char *) (rtx->rxbd.cbd_bufaddr + rtx->rxindex); + rtx->rxindex++; + + /* check if all char are readout, then make prepare for next receive */ + if (rtx->rxindex >= rtx->rxbd.cbd_datlen) { + rtx->rxindex = 0; + rtx->rxbd.cbd_sc |= BD_SC_EMPTY; + } + return(c); +} + +int +serial_tstc() +{ + volatile smc_uart_t *up; + volatile immap_t *im = (immap_t *)CONFIG_SYS_IMMR; + volatile serialbuffer_t *rtx; + + up = (smc_uart_t *)&(im->im_dprambase[PROFF_SMC]); + rtx = (serialbuffer_t *)&im->im_dprambase[up->smc_rbase]; + + return !(rtx->rxbd.cbd_sc & BD_SC_EMPTY); +} + +#endif /* CONFIG_CONS_ON_SMC */ + +#if defined(CONFIG_KGDB_ON_SMC) + +#if defined(CONFIG_CONS_ON_SMC) && CONFIG_KGDB_INDEX == CONFIG_CONS_INDEX +#error Whoops! serial console and kgdb are on the same smc serial port +#endif + +#if CONFIG_KGDB_INDEX == 1 /* KGDB Port on SMC1 */ + +#define KGDB_SMC_INDEX 0 +#define KGDB_PROFF_SMC_BASE PROFF_SMC1_BASE +#define KGDB_PROFF_SMC PROFF_SMC1 +#define KGDB_CPM_CR_SMC_PAGE CPM_CR_SMC1_PAGE +#define KGDB_CPM_CR_SMC_SBLOCK CPM_CR_SMC1_SBLOCK +#define KGDB_CMXSMR_MASK (CMXSMR_SMC1|CMXSMR_SMC1CS_MSK) +#define KGDB_CMXSMR_VALUE CMXSMR_SMC1CS_BRG7 + +#elif CONFIG_KGDB_INDEX == 2 /* KGDB Port on SMC2 */ + +#define KGDB_SMC_INDEX 1 +#define KGDB_PROFF_SMC_BASE PROFF_SMC2_BASE +#define KGDB_PROFF_SMC PROFF_SMC2 +#define KGDB_CPM_CR_SMC_PAGE CPM_CR_SMC2_PAGE +#define KGDB_CPM_CR_SMC_SBLOCK CPM_CR_SMC2_SBLOCK +#define KGDB_CMXSMR_MASK (CMXSMR_SMC2|CMXSMR_SMC2CS_MSK) +#define KGDB_CMXSMR_VALUE CMXSMR_SMC2CS_BRG8 + +#else + +#error "console not correctly defined" + +#endif + +void +kgdb_serial_init (void) +{ + volatile immap_t *im = (immap_t *)CONFIG_SYS_IMMR; + volatile smc_t *sp; + volatile smc_uart_t *up; + volatile cbd_t *tbdf, *rbdf; + volatile cpm8260_t *cp = &(im->im_cpm); + uint dpaddr, speed = CONFIG_KGDB_BAUDRATE; + char *s, *e; + + if ((s = getenv("kgdbrate")) != NULL && *s != '\0') { + ulong rate = simple_strtoul(s, &e, 10); + if (e > s && *e == '\0') + speed = rate; + } + + /* initialize pointers to SMC */ + + sp = (smc_t *) &(im->im_smc[KGDB_SMC_INDEX]); + *(ushort *)(&im->im_dprambase[KGDB_PROFF_SMC_BASE]) = KGDB_PROFF_SMC; + up = (smc_uart_t *)&im->im_dprambase[KGDB_PROFF_SMC]; + + /* Disable transmitter/receiver. */ + sp->smc_smcmr &= ~(SMCMR_REN | SMCMR_TEN); + + /* NOTE: I/O port pins are set up via the iop_conf_tab[] table */ + + /* Allocate space for two buffer descriptors in the DP ram. + * damm: allocating space after the two buffers for rx/tx data + */ + + dpaddr = m8260_cpm_dpalloc((2 * sizeof (cbd_t)) + 2, 16); + + /* Set the physical address of the host memory buffers in + * the buffer descriptors. + */ + rbdf = (cbd_t *)&im->im_dprambase[dpaddr]; + rbdf->cbd_bufaddr = (uint) (rbdf+2); + rbdf->cbd_sc = 0; + tbdf = rbdf + 1; + tbdf->cbd_bufaddr = ((uint) (rbdf+2)) + 1; + tbdf->cbd_sc = 0; + + /* Set up the uart parameters in the parameter ram. */ + up->smc_rbase = dpaddr; + up->smc_tbase = dpaddr+sizeof(cbd_t); + up->smc_rfcr = CPMFCR_EB; + up->smc_tfcr = CPMFCR_EB; + up->smc_brklen = 0; + up->smc_brkec = 0; + up->smc_brkcr = 0; + + /* Set UART mode, 8 bit, no parity, one stop. + * Enable receive and transmit. + */ + sp->smc_smcmr = smcr_mk_clen(9) | SMCMR_SM_UART; + + /* Mask all interrupts and remove anything pending. */ + sp->smc_smcm = 0; + sp->smc_smce = 0xff; + + /* put the SMC channel into NMSI (non multiplexd serial interface) + * mode and wire either BRG7 to SMC1 or BRG8 to SMC2 (15-17). + */ + im->im_cpmux.cmx_smr = + (im->im_cpmux.cmx_smr & ~KGDB_CMXSMR_MASK) | KGDB_CMXSMR_VALUE; + + /* Set up the baud rate generator. */ +#if defined(CONFIG_KGDB_USE_EXTC) + m8260_cpm_extcbrg(brg_map[KGDB_SMC_INDEX], speed, + CONFIG_KGDB_EXTC_RATE, CONFIG_KGDB_EXTC_PINSEL); +#else + m8260_cpm_setbrg(brg_map[KGDB_SMC_INDEX], speed); +#endif + + /* Make the first buffer the only buffer. */ + tbdf->cbd_sc |= BD_SC_WRAP; + rbdf->cbd_sc |= BD_SC_EMPTY | BD_SC_WRAP; + + /* Single character receive. */ + up->smc_mrblr = 1; + up->smc_maxidl = 0; + + /* Initialize Tx/Rx parameters. */ + + while (cp->cp_cpcr & CPM_CR_FLG) /* wait if cp is busy */ + ; + + cp->cp_cpcr = mk_cr_cmd(KGDB_CPM_CR_SMC_PAGE, KGDB_CPM_CR_SMC_SBLOCK, + 0, CPM_CR_INIT_TRX) | CPM_CR_FLG; + + while (cp->cp_cpcr & CPM_CR_FLG) /* wait if cp is busy */ + ; + + /* Enable transmitter/receiver. */ + sp->smc_smcmr |= SMCMR_REN | SMCMR_TEN; + + printf("SMC%d at %dbps ", CONFIG_KGDB_INDEX, speed); +} + +void +putDebugChar(const char c) +{ + volatile cbd_t *tbdf; + volatile char *buf; + volatile smc_uart_t *up; + volatile immap_t *im = (immap_t *)CONFIG_SYS_IMMR; + + if (c == '\n') + putDebugChar ('\r'); + + up = (smc_uart_t *)&(im->im_dprambase[KGDB_PROFF_SMC]); + + tbdf = (cbd_t *)&im->im_dprambase[up->smc_tbase]; + + /* Wait for last character to go. */ + buf = (char *)tbdf->cbd_bufaddr; + while (tbdf->cbd_sc & BD_SC_READY) + ; + + *buf = c; + tbdf->cbd_datlen = 1; + tbdf->cbd_sc |= BD_SC_READY; +} + +void +putDebugStr (const char *s) +{ + while (*s) { + putDebugChar (*s++); + } +} + +int +getDebugChar(void) +{ + volatile cbd_t *rbdf; + volatile unsigned char *buf; + volatile smc_uart_t *up; + volatile immap_t *im = (immap_t *)CONFIG_SYS_IMMR; + unsigned char c; + + up = (smc_uart_t *)&(im->im_dprambase[KGDB_PROFF_SMC]); + + rbdf = (cbd_t *)&im->im_dprambase[up->smc_rbase]; + + /* Wait for character to show up. */ + buf = (unsigned char *)rbdf->cbd_bufaddr; + while (rbdf->cbd_sc & BD_SC_EMPTY) + ; + c = *buf; + rbdf->cbd_sc |= BD_SC_EMPTY; + + return(c); +} + +void +kgdb_interruptible(int yes) +{ + return; +} + +#endif /* CONFIG_KGDB_ON_SMC */ diff --git a/arch/powerpc/cpu/mpc8260/speed.c b/arch/powerpc/cpu/mpc8260/speed.c new file mode 100644 index 0000000..0e1c2b0 --- /dev/null +++ b/arch/powerpc/cpu/mpc8260/speed.c @@ -0,0 +1,253 @@ +/* + * (C) Copyright 2000-2002 + * Wolfgang Denk, DENX Software Engineering, wd@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> +#include <mpc8260.h> +#include <asm/processor.h> + +#if defined(CONFIG_BOARD_GET_CPU_CLK_F) +extern unsigned long board_get_cpu_clk_f (void); +#endif + +DECLARE_GLOBAL_DATA_PTR; + +/* ------------------------------------------------------------------------- */ + +/* Bus-to-Core Multiplier */ +#define _1x 2 +#define _1_5x 3 +#define _2x 4 +#define _2_5x 5 +#define _3x 6 +#define _3_5x 7 +#define _4x 8 +#define _4_5x 9 +#define _5x 10 +#define _5_5x 11 +#define _6x 12 +#define _6_5x 13 +#define _7x 14 +#define _7_5x 15 +#define _8x 16 +#define _byp -1 +#define _off -2 +#define _unk -3 + +typedef struct { + int b2c_mult; + int vco_div; + char *freq_60x; + char *freq_core; +} corecnf_t; + +/* + * this table based on "Errata to MPC8260 PowerQUICC II User's Manual", + * Rev. 1, 8/2000, page 10. + */ +corecnf_t corecnf_tab[] = { + { _1_5x, 4, " 33-100", " 33-100" }, /* 0x00 */ + { _1x, 4, " 50-150", " 50-150" }, /* 0x01 */ + { _1x, 8, " 25-75 ", " 25-75 " }, /* 0x02 */ + { _byp, -1, " ?-? ", " ?-? " }, /* 0x03 */ + { _2x, 2, " 50-150", "100-300" }, /* 0x04 */ + { _2x, 4, " 25-75 ", " 50-150" }, /* 0x05 */ + { _2_5x, 2, " 40-120", "100-240" }, /* 0x06 */ + { _4_5x, 2, " 22-65 ", "100-300" }, /* 0x07 */ + { _3x, 2, " 33-100", "100-300" }, /* 0x08 */ + { _5_5x, 2, " 18-55 ", "100-300" }, /* 0x09 */ + { _4x, 2, " 25-75 ", "100-300" }, /* 0x0A */ + { _5x, 2, " 20-60 ", "100-300" }, /* 0x0B */ + { _1_5x, 8, " 16-50 ", " 16-50 " }, /* 0x0C */ + { _6x, 2, " 16-50 ", "100-300" }, /* 0x0D */ + { _3_5x, 2, " 30-85 ", "100-300" }, /* 0x0E */ + { _off, -1, " ?-? ", " ?-? " }, /* 0x0F */ + { _3x, 4, " 16-50 ", " 50-150" }, /* 0x10 */ + { _2_5x, 4, " 20-60 ", " 50-120" }, /* 0x11 */ + { _6_5x, 2, " 15-46 ", "100-300" }, /* 0x12 */ + { _byp, -1, " ?-? ", " ?-? " }, /* 0x13 */ + { _7x, 2, " 14-43 ", "100-300" }, /* 0x14 */ + { _2x, 4, " 25-75 ", " 50-150" }, /* 0x15 */ + { _7_5x, 2, " 13-40 ", "100-300" }, /* 0x16 */ + { _4_5x, 2, " 22-65 ", "100-300" }, /* 0x17 */ + { _unk, -1, " ?-? ", " ?-? " }, /* 0x18 */ + { _5_5x, 2, " 18-55 ", "100-300" }, /* 0x19 */ + { _4x, 2, " 25-75 ", "100-300" }, /* 0x1A */ + { _5x, 2, " 20-60 ", "100-300" }, /* 0x1B */ + { _8x, 2, " 12-38 ", "100-300" }, /* 0x1C */ + { _6x, 2, " 16-50 ", "100-300" }, /* 0x1D */ + { _3_5x, 2, " 30-85 ", "100-300" }, /* 0x1E */ + { _off, -1, " ?-? ", " ?-? " }, /* 0x1F */ +}; + +/* ------------------------------------------------------------------------- */ + +/* + * + */ + +int get_clocks (void) +{ + volatile immap_t *immap = (immap_t *) CONFIG_SYS_IMMR; + ulong clkin; + ulong sccr, dfbrg; + ulong scmr, corecnf, busdf, cpmdf, plldf, pllmf; + corecnf_t *cp; + +#if !defined(CONFIG_8260_CLKIN) +#error clock measuring not implemented yet - define CONFIG_8260_CLKIN +#else +#if defined(CONFIG_BOARD_GET_CPU_CLK_F) + clkin = board_get_cpu_clk_f (); +#else + clkin = CONFIG_8260_CLKIN; +#endif +#endif + + sccr = immap->im_clkrst.car_sccr; + dfbrg = (sccr & SCCR_DFBRG_MSK) >> SCCR_DFBRG_SHIFT; + + scmr = immap->im_clkrst.car_scmr; + corecnf = (scmr & SCMR_CORECNF_MSK) >> SCMR_CORECNF_SHIFT; + cp = &corecnf_tab[corecnf]; + + busdf = (scmr & SCMR_BUSDF_MSK) >> SCMR_BUSDF_SHIFT; + cpmdf = (scmr & SCMR_CPMDF_MSK) >> SCMR_CPMDF_SHIFT; + + /* HiP7, HiP7 Rev01, HiP7 RevA */ + if ((get_pvr () == PVR_8260_HIP7) || + (get_pvr () == PVR_8260_HIP7R1) || + (get_pvr () == PVR_8260_HIP7RA)) { + pllmf = (scmr & SCMR_PLLMF_MSKH7) >> SCMR_PLLMF_SHIFT; + gd->vco_out = clkin * (pllmf + 1); + } else { /* HiP3, HiP4 */ + pllmf = (scmr & SCMR_PLLMF_MSK) >> SCMR_PLLMF_SHIFT; + plldf = (scmr & SCMR_PLLDF) ? 1 : 0; + gd->vco_out = (clkin * 2 * (pllmf + 1)) / (plldf + 1); + } +#if 0 + if (gd->vco_out / (busdf + 1) != clkin) { + /* aaarrrggghhh!!! */ + return (1); + } +#endif + + gd->cpm_clk = gd->vco_out / 2; + gd->bus_clk = clkin; + gd->scc_clk = gd->vco_out / 4; + gd->brg_clk = gd->vco_out / (1 << (2 * (dfbrg + 1))); + + if (cp->b2c_mult > 0) { + gd->cpu_clk = (clkin * cp->b2c_mult) / 2; + } else { + gd->cpu_clk = clkin; + } + +#ifdef CONFIG_PCI + gd->pci_clk = clkin; + + if (sccr & SCCR_PCI_MODE) { + uint pci_div; + uint pcidf = (sccr & SCCR_PCIDF_MSK) >> SCCR_PCIDF_SHIFT; + + if (sccr & SCCR_PCI_MODCK) { + pci_div = 2; + if (pcidf == 9) { + pci_div *= 5; + } else if (pcidf == 0xB) { + pci_div *= 6; + } else { + pci_div *= (pcidf + 1); + } + } else { + pci_div = pcidf + 1; + } + + gd->pci_clk = (gd->cpm_clk * 2) / pci_div; + } +#endif + + return (0); +} + +int prt_8260_clks (void) +{ + volatile immap_t *immap = (immap_t *) CONFIG_SYS_IMMR; + ulong sccr, dfbrg; + ulong scmr, corecnf, busdf, cpmdf, plldf, pllmf, pcidf; + corecnf_t *cp; + + sccr = immap->im_clkrst.car_sccr; + dfbrg = (sccr & SCCR_DFBRG_MSK) >> SCCR_DFBRG_SHIFT; + + scmr = immap->im_clkrst.car_scmr; + corecnf = (scmr & SCMR_CORECNF_MSK) >> SCMR_CORECNF_SHIFT; + busdf = (scmr & SCMR_BUSDF_MSK) >> SCMR_BUSDF_SHIFT; + cpmdf = (scmr & SCMR_CPMDF_MSK) >> SCMR_CPMDF_SHIFT; + plldf = (scmr & SCMR_PLLDF) ? 1 : 0; + pllmf = (scmr & SCMR_PLLMF_MSK) >> SCMR_PLLMF_SHIFT; + pcidf = (sccr & SCCR_PCIDF_MSK) >> SCCR_PCIDF_SHIFT; + + cp = &corecnf_tab[corecnf]; + + puts (CPU_ID_STR " Clock Configuration\n - Bus-to-Core Mult "); + + switch (cp->b2c_mult) { + case _byp: + puts ("BYPASS"); + break; + + case _off: + puts ("OFF"); + break; + + case _unk: + puts ("UNKNOWN"); + break; + + default: + printf ("%d%sx", + cp->b2c_mult / 2, + (cp->b2c_mult % 2) ? ".5" : ""); + break; + } + + printf (", VCO Div %d, 60x Bus Freq %s, Core Freq %s\n", + cp->vco_div, cp->freq_60x, cp->freq_core); + + printf (" - dfbrg %ld, corecnf 0x%02lx, busdf %ld, cpmdf %ld, " + "plldf %ld, pllmf %ld, pcidf %ld\n", + dfbrg, corecnf, busdf, cpmdf, + plldf, pllmf, pcidf); + + printf (" - vco_out %10ld, scc_clk %10ld, brg_clk %10ld\n", + gd->vco_out, gd->scc_clk, gd->brg_clk); + + printf (" - cpu_clk %10ld, cpm_clk %10ld, bus_clk %10ld\n", + gd->cpu_clk, gd->cpm_clk, gd->bus_clk); +#ifdef CONFIG_PCI + printf (" - pci_clk %10ld\n", gd->pci_clk); +#endif + putc ('\n'); + + return (0); +} diff --git a/arch/powerpc/cpu/mpc8260/speed.h b/arch/powerpc/cpu/mpc8260/speed.h new file mode 100644 index 0000000..3f32a14 --- /dev/null +++ b/arch/powerpc/cpu/mpc8260/speed.h @@ -0,0 +1,54 @@ +/* + * (C) Copyright 2000 + * Wolfgang Denk, DENX Software Engineering, wd@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 + */ + +/*----------------------------------------------------------------------- + * Timer value for timer 2, ICLK = 10 + * + * SPEED_FCOUNT2 = GCLK / (16 * (TIMER_TMR_PS + 1)) + * SPEED_TMR3_PS = (GCLK / (16 * SPEED_FCOUNT3)) - 1 + * + * SPEED_FCOUNT2 timer 2 counting frequency + * GCLK CPU clock + * SPEED_TMR2_PS prescaler + */ +#define SPEED_TMR2_PS (250 - 1) /* divide by 250 */ + +/*----------------------------------------------------------------------- + * Timer value for PIT + * + * PIT_TIME = SPEED_PITC / PITRTCLK + * PITRTCLK = 8192 + */ +#define SPEED_PITC (82 << 16) /* start counting from 82 */ + +/* + * The new value for PTA is calculated from + * + * PTA = (gclk * Trefresh) / (2 ^ (2 * DFBRG) * PTP * NCS) + * + * gclk CPU clock (not bus clock !) + * Trefresh Refresh cycle * 4 (four word bursts used) + * DFBRG For normal mode (no clock reduction) always 0 + * PTP Prescaler (already adjusted for no. of banks and 4K / 8K refresh) + * NCS Number of SDRAM banks (chip selects) on this UPM. + */ diff --git a/arch/powerpc/cpu/mpc8260/spi.c b/arch/powerpc/cpu/mpc8260/spi.c new file mode 100644 index 0000000..f5d2ac3 --- /dev/null +++ b/arch/powerpc/cpu/mpc8260/spi.c @@ -0,0 +1,435 @@ +/* + * Copyright (c) 2001 Navin Boppuri / Prashant Patel + * <nboppuri@trinetcommunication.com>, + * <pmpatel@trinetcommunication.com> + * Copyright (c) 2001 Gerd Mennchen <Gerd.Mennchen@icn.siemens.de> + * Copyright (c) 2001-2003 Wolfgang Denk, DENX Software Engineering, <wd@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 + */ + +/* + * MPC8260 CPM SPI interface. + * + * Parts of this code are probably not portable and/or specific to + * the board which I used for the tests. Please send fixes/complaints + * to wd@denx.de + * + */ + +#include <common.h> +#include <asm/cpm_8260.h> +#include <linux/ctype.h> +#include <malloc.h> +#include <post.h> +#include <net.h> + +#if defined(CONFIG_SPI) + +/* Warning: + * You cannot enable DEBUG for early system initalization, i. e. when + * this driver is used to read environment parameters like "baudrate" + * from EEPROM which are used to initialize the serial port which is + * needed to print the debug messages... + */ +#undef DEBUG + +#define SPI_EEPROM_WREN 0x06 +#define SPI_EEPROM_RDSR 0x05 +#define SPI_EEPROM_READ 0x03 +#define SPI_EEPROM_WRITE 0x02 + +/* --------------------------------------------------------------- + * Offset for initial SPI buffers in DPRAM: + * We need a 520 byte scratch DPRAM area to use at an early stage. + * It is used between the two initialization calls (spi_init_f() + * and spi_init_r()). + * The value 0x2000 makes it far enough from the start of the data + * area (as well as from the stack pointer). + * --------------------------------------------------------------- */ +#ifndef CONFIG_SYS_SPI_INIT_OFFSET +#define CONFIG_SYS_SPI_INIT_OFFSET 0x2000 +#endif + +#define CPM_SPI_BASE 0x100 + +#ifdef DEBUG + +#define DPRINT(a) printf a; +/* ----------------------------------------------- + * Helper functions to peek into tx and rx buffers + * ----------------------------------------------- */ +static const char * const hex_digit = "0123456789ABCDEF"; + +static char quickhex (int i) +{ + return hex_digit[i]; +} + +static void memdump (void *pv, int num) +{ + int i; + unsigned char *pc = (unsigned char *) pv; + + for (i = 0; i < num; i++) + printf ("%c%c ", quickhex (pc[i] >> 4), quickhex (pc[i] & 0x0f)); + printf ("\t"); + for (i = 0; i < num; i++) + printf ("%c", isprint (pc[i]) ? pc[i] : '.'); + printf ("\n"); +} +#else /* !DEBUG */ + +#define DPRINT(a) + +#endif /* DEBUG */ + +/* ------------------- + * Function prototypes + * ------------------- */ +void spi_init (void); + +ssize_t spi_read (uchar *, int, uchar *, int); +ssize_t spi_write (uchar *, int, uchar *, int); +ssize_t spi_xfer (size_t); + +/* ------------------- + * Variables + * ------------------- */ + +#define MAX_BUFFER 0x104 + +/* ---------------------------------------------------------------------- + * Initially we place the RX and TX buffers at a fixed location in DPRAM! + * ---------------------------------------------------------------------- */ +static uchar *rxbuf = + (uchar *)&((immap_t *)CONFIG_SYS_IMMR)->im_dprambase + [CONFIG_SYS_SPI_INIT_OFFSET]; +static uchar *txbuf = + (uchar *)&((immap_t *)CONFIG_SYS_IMMR)->im_dprambase + [CONFIG_SYS_SPI_INIT_OFFSET+MAX_BUFFER]; + +/* ************************************************************************** + * + * Function: spi_init_f + * + * Description: Init SPI-Controller (ROM part) + * + * return: --- + * + * *********************************************************************** */ +void spi_init_f (void) +{ + unsigned int dpaddr; + + volatile spi_t *spi; + volatile immap_t *immr; + volatile cpm8260_t *cp; + volatile cbd_t *tbdf, *rbdf; + + immr = (immap_t *) CONFIG_SYS_IMMR; + cp = (cpm8260_t *) &immr->im_cpm; + + *(ushort *)(&immr->im_dprambase[PROFF_SPI_BASE]) = PROFF_SPI; + spi = (spi_t *)&immr->im_dprambase[PROFF_SPI]; + +/* 1 */ + /* ------------------------------------------------ + * Initialize Port D SPI pins + * (we are only in Master Mode !) + * ------------------------------------------------ */ + + /* -------------------------------------------- + * GPIO or per. Function + * PPARD[16] = 1 [0x00008000] (SPIMISO) + * PPARD[17] = 1 [0x00004000] (SPIMOSI) + * PPARD[18] = 1 [0x00002000] (SPICLK) + * PPARD[12] = 0 [0x00080000] -> GPIO: (CS for ATC EEPROM) + * -------------------------------------------- */ + immr->im_ioport.iop_ppard |= 0x0000E000; /* set bits */ + immr->im_ioport.iop_ppard &= ~0x00080000; /* reset bit */ + + /* ---------------------------------------------- + * In/Out or per. Function 0/1 + * PDIRD[16] = 0 [0x00008000] -> PERI1: SPIMISO + * PDIRD[17] = 0 [0x00004000] -> PERI1: SPIMOSI + * PDIRD[18] = 0 [0x00002000] -> PERI1: SPICLK + * PDIRD[12] = 1 [0x00080000] -> GPIO OUT: CS for ATC EEPROM + * ---------------------------------------------- */ + immr->im_ioport.iop_pdird &= ~0x0000E000; + immr->im_ioport.iop_pdird |= 0x00080000; + + /* ---------------------------------------------- + * special option reg. + * PSORD[16] = 1 [0x00008000] -> SPIMISO + * PSORD[17] = 1 [0x00004000] -> SPIMOSI + * PSORD[18] = 1 [0x00002000] -> SPICLK + * ---------------------------------------------- */ + immr->im_ioport.iop_psord |= 0x0000E000; + + /* Initialize the parameter ram. + * We need to make sure many things are initialized to zero + */ + spi->spi_rstate = 0; + spi->spi_rdp = 0; + spi->spi_rbptr = 0; + spi->spi_rbc = 0; + spi->spi_rxtmp = 0; + spi->spi_tstate = 0; + spi->spi_tdp = 0; + spi->spi_tbptr = 0; + spi->spi_tbc = 0; + spi->spi_txtmp = 0; + + /* Allocate space for one transmit and one receive buffer + * descriptor in the DP ram + */ +#ifdef CONFIG_SYS_ALLOC_DPRAM + dpaddr = m8260_cpm_dpalloc (sizeof(cbd_t)*2, 8); +#else + dpaddr = CPM_SPI_BASE; +#endif + +/* 3 */ + /* Set up the SPI parameters in the parameter ram */ + spi->spi_rbase = dpaddr; + spi->spi_tbase = dpaddr + sizeof (cbd_t); + + /***********IMPORTANT******************/ + + /* + * Setting transmit and receive buffer descriptor pointers + * initially to rbase and tbase. Only the microcode patches + * documentation talks about initializing this pointer. This + * is missing from the sample I2C driver. If you dont + * initialize these pointers, the kernel hangs. + */ + spi->spi_rbptr = spi->spi_rbase; + spi->spi_tbptr = spi->spi_tbase; + +/* 4 */ + /* Init SPI Tx + Rx Parameters */ + while (cp->cp_cpcr & CPM_CR_FLG) + ; + cp->cp_cpcr = mk_cr_cmd(CPM_CR_SPI_PAGE, CPM_CR_SPI_SBLOCK, + 0, CPM_CR_INIT_TRX) | CPM_CR_FLG; + while (cp->cp_cpcr & CPM_CR_FLG) + ; + +/* 6 */ + /* Set to big endian. */ + spi->spi_tfcr = CPMFCR_EB; + spi->spi_rfcr = CPMFCR_EB; + +/* 7 */ + /* Set maximum receive size. */ + spi->spi_mrblr = MAX_BUFFER; + +/* 8 + 9 */ + /* tx and rx buffer descriptors */ + tbdf = (cbd_t *) & immr->im_dprambase[spi->spi_tbase]; + rbdf = (cbd_t *) & immr->im_dprambase[spi->spi_rbase]; + + tbdf->cbd_sc &= ~BD_SC_READY; + rbdf->cbd_sc &= ~BD_SC_EMPTY; + + /* Set the bd's rx and tx buffer address pointers */ + rbdf->cbd_bufaddr = (ulong) rxbuf; + tbdf->cbd_bufaddr = (ulong) txbuf; + +/* 10 + 11 */ + immr->im_spi.spi_spie = SPI_EMASK; /* Clear all SPI events */ + immr->im_spi.spi_spim = 0x00; /* Mask all SPI events */ + + + return; +} + +/* ************************************************************************** + * + * Function: spi_init_r + * + * Description: Init SPI-Controller (RAM part) - + * The malloc engine is ready and we can move our buffers to + * normal RAM + * + * return: --- + * + * *********************************************************************** */ +void spi_init_r (void) +{ + volatile spi_t *spi; + volatile immap_t *immr; + volatile cpm8260_t *cp; + volatile cbd_t *tbdf, *rbdf; + + immr = (immap_t *) CONFIG_SYS_IMMR; + cp = (cpm8260_t *) &immr->im_cpm; + + spi = (spi_t *)&immr->im_dprambase[PROFF_SPI]; + + /* tx and rx buffer descriptors */ + tbdf = (cbd_t *) & immr->im_dprambase[spi->spi_tbase]; + rbdf = (cbd_t *) & immr->im_dprambase[spi->spi_rbase]; + + /* Allocate memory for RX and TX buffers */ + rxbuf = (uchar *) malloc (MAX_BUFFER); + txbuf = (uchar *) malloc (MAX_BUFFER); + + rbdf->cbd_bufaddr = (ulong) rxbuf; + tbdf->cbd_bufaddr = (ulong) txbuf; + + return; +} + +/**************************************************************************** + * Function: spi_write + **************************************************************************** */ +ssize_t spi_write (uchar *addr, int alen, uchar *buffer, int len) +{ + int i; + + memset(rxbuf, 0, MAX_BUFFER); + memset(txbuf, 0, MAX_BUFFER); + *txbuf = SPI_EEPROM_WREN; /* write enable */ + spi_xfer(1); + memcpy(txbuf, addr, alen); + *txbuf = SPI_EEPROM_WRITE; /* WRITE memory array */ + memcpy(alen + txbuf, buffer, len); + spi_xfer(alen + len); + /* ignore received data */ + for (i = 0; i < 1000; i++) { + *txbuf = SPI_EEPROM_RDSR; /* read status */ + txbuf[1] = 0; + spi_xfer(2); + if (!(rxbuf[1] & 1)) { + break; + } + udelay(1000); + } + if (i >= 1000) { + printf ("*** spi_write: Time out while writing!\n"); + } + + return len; +} + +/**************************************************************************** + * Function: spi_read + **************************************************************************** */ +ssize_t spi_read (uchar *addr, int alen, uchar *buffer, int len) +{ + memset(rxbuf, 0, MAX_BUFFER); + memset(txbuf, 0, MAX_BUFFER); + memcpy(txbuf, addr, alen); + *txbuf = SPI_EEPROM_READ; /* READ memory array */ + + /* + * There is a bug in 860T (?) that cuts the last byte of input + * if we're reading into DPRAM. The solution we choose here is + * to always read len+1 bytes (we have one extra byte at the + * end of the buffer). + */ + spi_xfer(alen + len + 1); + memcpy(buffer, alen + rxbuf, len); + + return len; +} + +/**************************************************************************** + * Function: spi_xfer + **************************************************************************** */ +ssize_t spi_xfer (size_t count) +{ + volatile immap_t *immr; + volatile cpm8260_t *cp; + volatile spi_t *spi; + cbd_t *tbdf, *rbdf; + int tm; + + DPRINT (("*** spi_xfer entered ***\n")); + + immr = (immap_t *) CONFIG_SYS_IMMR; + cp = (cpm8260_t *) &immr->im_cpm; + + spi = (spi_t *)&immr->im_dprambase[PROFF_SPI]; + + tbdf = (cbd_t *) & immr->im_dprambase[spi->spi_tbase]; + rbdf = (cbd_t *) & immr->im_dprambase[spi->spi_rbase]; + + /* Board-specific: Set CS for device (ATC EEPROM) */ + immr->im_ioport.iop_pdatd &= ~0x00080000; + + /* Setting tx bd status and data length */ + tbdf->cbd_sc = BD_SC_READY | BD_SC_LAST | BD_SC_WRAP; + tbdf->cbd_datlen = count; + + DPRINT (("*** spi_xfer: Bytes to be xferred: %d ***\n", + tbdf->cbd_datlen)); + + /* Setting rx bd status and data length */ + rbdf->cbd_sc = BD_SC_EMPTY | BD_SC_WRAP; + rbdf->cbd_datlen = 0; /* rx length has no significance */ + + immr->im_spi.spi_spmode = SPMODE_REV | + SPMODE_MSTR | + SPMODE_EN | + SPMODE_LEN(8) | /* 8 Bits per char */ + SPMODE_PM(0x8) ; /* medium speed */ + immr->im_spi.spi_spie = SPI_EMASK; /* Clear all SPI events */ + immr->im_spi.spi_spim = 0x00; /* Mask all SPI events */ + + /* start spi transfer */ + DPRINT (("*** spi_xfer: Performing transfer ...\n")); + immr->im_spi.spi_spcom |= SPI_STR; /* Start transmit */ + + /* -------------------------------- + * Wait for SPI transmit to get out + * or time out (1 second = 1000 ms) + * -------------------------------- */ + for (tm=0; tm<1000; ++tm) { + if (immr->im_spi.spi_spie & SPI_TXB) { /* Tx Buffer Empty */ + DPRINT (("*** spi_xfer: Tx buffer empty\n")); + break; + } + if ((tbdf->cbd_sc & BD_SC_READY) == 0) { + DPRINT (("*** spi_xfer: Tx BD done\n")); + break; + } + udelay (1000); + } + if (tm >= 1000) { + printf ("*** spi_xfer: Time out while xferring to/from SPI!\n"); + } + DPRINT (("*** spi_xfer: ... transfer ended\n")); + +#ifdef DEBUG + printf ("\nspi_xfer: txbuf after xfer\n"); + memdump ((void *) txbuf, 16); /* dump of txbuf before transmit */ + printf ("spi_xfer: rxbuf after xfer\n"); + memdump ((void *) rxbuf, 16); /* dump of rxbuf after transmit */ + printf ("\n"); +#endif + + /* Clear CS for device */ + immr->im_ioport.iop_pdatd |= 0x00080000; + + return count; +} +#endif /* CONFIG_SPI */ diff --git a/arch/powerpc/cpu/mpc8260/start.S b/arch/powerpc/cpu/mpc8260/start.S new file mode 100644 index 0000000..a435042 --- /dev/null +++ b/arch/powerpc/cpu/mpc8260/start.S @@ -0,0 +1,1023 @@ +/* + * Copyright (C) 1998 Dan Malek <dmalek@jlc.net> + * Copyright (C) 1999 Magnus Damm <kieraypc01.p.y.kie.era.ericsson.se> + * Copyright (C) 2000, 2001,2002 Wolfgang Denk <wd@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 + */ + +/* + * U-Boot - Startup Code for MPC8260 PowerPC based Embedded Boards + */ +#include <config.h> +#include <mpc8260.h> +#include <timestamp.h> +#include <version.h> + +#define CONFIG_8260 1 /* needed for Linux kernel header files */ +#define _LINUX_CONFIG_H 1 /* avoid reading Linux autoconf.h file */ + +#include <ppc_asm.tmpl> +#include <ppc_defs.h> + +#include <asm/cache.h> +#include <asm/mmu.h> + +#ifndef CONFIG_IDENT_STRING +#define CONFIG_IDENT_STRING "" +#endif + +/* We don't want the MMU yet. +*/ +#undef MSR_KERNEL +/* Floating Point enable, Machine Check and Recoverable Interr. */ +#ifdef DEBUG +#define MSR_KERNEL (MSR_FP|MSR_RI) +#else +#define MSR_KERNEL (MSR_FP|MSR_ME|MSR_RI) +#endif + +/* + * Set up GOT: Global Offset Table + * + * Use r12 to access the GOT + */ + START_GOT + GOT_ENTRY(_GOT2_TABLE_) + GOT_ENTRY(_FIXUP_TABLE_) + + GOT_ENTRY(_start) + GOT_ENTRY(_start_of_vectors) + GOT_ENTRY(_end_of_vectors) + GOT_ENTRY(transfer_to_handler) + + GOT_ENTRY(__init_end) + GOT_ENTRY(_end) + GOT_ENTRY(__bss_start) +#if defined(CONFIG_HYMOD) + GOT_ENTRY(environment) +#endif + END_GOT + +/* + * Version string - must be in data segment because MPC8260 uses the first + * 256 bytes for the Hard Reset Configuration Word table (see below). + * Similarly, can't have the U-Boot Magic Number as the first thing in + * the image - don't know how this will affect the image tools, but I guess + * I'll find out soon + */ + .data + .globl version_string +version_string: + .ascii U_BOOT_VERSION + .ascii " (", U_BOOT_DATE, " - ", U_BOOT_TIME, ")" + .ascii CONFIG_IDENT_STRING, "\0" + +/* + * Hard Reset Configuration Word (HRCW) table + * + * The Hard Reset Configuration Word (HRCW) sets a number of useful things + * such as whether there is an external memory controller, whether the + * PowerPC core is disabled (i.e. only the communications processor is + * active, accessed by another CPU on the bus), whether using external + * arbitration, external bus mode, boot port size, core initial prefix, + * internal space base, boot memory space, etc. + * + * These things dictate where the processor begins execution, where the + * boot ROM appears in memory, the memory controller setup when access + * boot ROM, etc. The HRCW is *extremely* important. + * + * The HRCW is read from the bus during reset. One CPU on the bus will + * be a hard reset configuration master, any others will be hard reset + * configuration slaves. The master reads eight HRCWs from flash during + * reset - the first it uses for itself, the other 7 it communicates to + * up to 7 configuration slaves by some complicated mechanism, which is + * not really important here. + * + * The configuration master performs 32 successive reads starting at address + * 0 and incrementing by 8 each read (i.e. on 64 bit boundaries) but only 8 + * bits is read, and always from byte lane D[0-7] (so that port size of the + * boot device does not matter). The first four reads form the 32 bit HRCW + * for the master itself. The second four reads form the HRCW for the first + * slave, and so on, up to seven slaves. The 32 bit HRCW is formed by + * concatenating the four bytes, with the first read placed in byte 0 (the + * most significant byte), and so on with the fourth read placed in byte 3 + * (the least significant byte). + */ +#define _HRCW_TABLE_ENTRY(w) \ + .fill 8,1,(((w)>>24)&0xff); \ + .fill 8,1,(((w)>>16)&0xff); \ + .fill 8,1,(((w)>> 8)&0xff); \ + .fill 8,1,(((w) )&0xff) + .text + .globl _hrcw_table +_hrcw_table: + _HRCW_TABLE_ENTRY(CONFIG_SYS_HRCW_MASTER) + _HRCW_TABLE_ENTRY(CONFIG_SYS_HRCW_SLAVE1) + _HRCW_TABLE_ENTRY(CONFIG_SYS_HRCW_SLAVE2) + _HRCW_TABLE_ENTRY(CONFIG_SYS_HRCW_SLAVE3) + _HRCW_TABLE_ENTRY(CONFIG_SYS_HRCW_SLAVE4) + _HRCW_TABLE_ENTRY(CONFIG_SYS_HRCW_SLAVE5) + _HRCW_TABLE_ENTRY(CONFIG_SYS_HRCW_SLAVE6) + _HRCW_TABLE_ENTRY(CONFIG_SYS_HRCW_SLAVE7) +/* + * After configuration, a system reset exception is executed using the + * vector at offset 0x100 relative to the base set by MSR[IP]. If MSR[IP] + * is 0, the base address is 0x00000000. If MSR[IP] is 1, the base address + * is 0xfff00000. In the case of a Power On Reset or Hard Reset, the value + * of MSR[IP] is determined by the CIP field in the HRCW. + * + * Other bits in the HRCW set up the Base Address and Port Size in BR0. + * This determines the location of the boot ROM (flash or EPROM) in the + * processor's address space at boot time. As long as the HRCW is set up + * so that we eventually end up executing the code below when the processor + * executes the reset exception, the actual values used should not matter. + * + * Once we have got here, the address mask in OR0 is cleared so that the + * bottom 32K of the boot ROM is effectively repeated all throughout the + * processor's address space, after which we can jump to the absolute + * address at which the boot ROM was linked at compile time, and proceed + * to initialise the memory controller without worrying if the rug will be + * pulled out from under us, so to speak (it will be fine as long as we + * configure BR0 with the same boot ROM link address). + */ + . = EXC_OFF_SYS_RESET + + .globl _start +_start: + li r21, BOOTFLAG_COLD /* Normal Power-On: Boot from FLASH*/ + nop + b boot_cold + + . = EXC_OFF_SYS_RESET + 0x10 + + .globl _start_warm +_start_warm: + li r21, BOOTFLAG_WARM /* Software reboot */ + b boot_warm + +boot_cold: +#if defined(CONFIG_MPC8260ADS) && defined(CONFIG_SYS_DEFAULT_IMMR) + lis r3, CONFIG_SYS_DEFAULT_IMMR@h + nop + lwz r4, 0(r3) + nop + rlwinm r4, r4, 0, 8, 5 + nop + oris r4, r4, 0x0200 + nop + stw r4, 0(r3) + nop +#endif /* CONFIG_MPC8260ADS && CONFIG_SYS_DEFAULT_IMMR */ +boot_warm: + mfmsr r5 /* save msr contents */ + +#if defined(CONFIG_COGENT) + /* this is what the cogent EPROM does */ + li r0, 0 + mtmsr r0 + isync + bl cogent_init_8260 +#endif /* CONFIG_COGENT */ + +#if defined(CONFIG_SYS_DEFAULT_IMMR) + lis r3, CONFIG_SYS_IMMR@h + ori r3, r3, CONFIG_SYS_IMMR@l + lis r4, CONFIG_SYS_DEFAULT_IMMR@h + stw r3, 0x1A8(r4) +#endif /* CONFIG_SYS_DEFAULT_IMMR */ + + /* Initialise the MPC8260 processor core */ + /*--------------------------------------------------------------*/ + + bl init_8260_core + +#ifndef CONFIG_SYS_RAMBOOT + /* When booting from ROM (Flash or EPROM), clear the */ + /* Address Mask in OR0 so ROM appears everywhere */ + /*--------------------------------------------------------------*/ + + lis r3, (CONFIG_SYS_IMMR+IM_REGBASE)@h + lwz r4, IM_OR0@l(r3) + li r5, 0x7fff + and r4, r4, r5 + stw r4, IM_OR0@l(r3) + + /* Calculate absolute address in FLASH and jump there */ + /*--------------------------------------------------------------*/ + + lis r3, CONFIG_SYS_MONITOR_BASE@h + ori r3, r3, CONFIG_SYS_MONITOR_BASE@l + addi r3, r3, in_flash - _start + EXC_OFF_SYS_RESET + mtlr r3 + blr + +in_flash: +#endif /* CONFIG_SYS_RAMBOOT */ + + /* initialize some things that are hard to access from C */ + /*--------------------------------------------------------------*/ + + lis r3, CONFIG_SYS_IMMR@h /* set up stack in internal DPRAM */ + ori r1, r3, CONFIG_SYS_INIT_SP_OFFSET + li r0, 0 /* Make room for stack frame header and */ + stwu r0, -4(r1) /* clear final stack frame so that */ + stwu r0, -4(r1) /* stack backtraces terminate cleanly */ + + /* let the C-code set up the rest */ + /* */ + /* Be careful to keep code relocatable ! */ + /*--------------------------------------------------------------*/ + + GET_GOT /* initialize GOT access */ + + /* r3: IMMR */ + bl cpu_init_f /* run low-level CPU init code (in Flash)*/ + +#ifdef DEBUG + bl init_debug /* set up debugging stuff */ +#endif + + mr r3, r21 + /* r3: BOOTFLAG */ + bl board_init_f /* run 1st part of board init code (in Flash)*/ + +/* + * Vector Table + */ + + .globl _start_of_vectors +_start_of_vectors: + +/* Machine check */ + STD_EXCEPTION(0x200, MachineCheck, MachineCheckException) + +/* Data Storage exception. */ + STD_EXCEPTION(0x300, DataStorage, UnknownException) + +/* Instruction Storage exception. */ + STD_EXCEPTION(0x400, InstStorage, UnknownException) + +/* External Interrupt exception. */ + STD_EXCEPTION(0x500, ExtInterrupt, external_interrupt) + +/* Alignment exception. */ + . = 0x600 +Alignment: + EXCEPTION_PROLOG(SRR0, SRR1) + mfspr r4,DAR + stw r4,_DAR(r21) + mfspr r5,DSISR + stw r5,_DSISR(r21) + addi r3,r1,STACK_FRAME_OVERHEAD + EXC_XFER_TEMPLATE(Alignment, AlignmentException, MSR_KERNEL, COPY_EE) + +/* Program check exception */ + . = 0x700 +ProgramCheck: + EXCEPTION_PROLOG(SRR0, SRR1) + addi r3,r1,STACK_FRAME_OVERHEAD + EXC_XFER_TEMPLATE(ProgramCheck, ProgramCheckException, + MSR_KERNEL, COPY_EE) + + STD_EXCEPTION(0x800, FPUnavailable, UnknownException) + + /* I guess we could implement decrementer, and may have + * to someday for timekeeping. + */ + STD_EXCEPTION(0x900, Decrementer, timer_interrupt) + + STD_EXCEPTION(0xa00, Trap_0a, UnknownException) + STD_EXCEPTION(0xb00, Trap_0b, UnknownException) + STD_EXCEPTION(0xc00, SystemCall, UnknownException) + STD_EXCEPTION(0xd00, SingleStep, UnknownException) + + STD_EXCEPTION(0xe00, Trap_0e, UnknownException) + STD_EXCEPTION(0xf00, Trap_0f, UnknownException) + + STD_EXCEPTION(0x1000, InstructionTLBMiss, UnknownException) + STD_EXCEPTION(0x1100, DataLoadTLBMiss, UnknownException) + STD_EXCEPTION(0x1200, DataStoreTLBMiss, UnknownException) +#ifdef DEBUG + . = 0x1300 + /* + * This exception occurs when the program counter matches the + * Instruction Address Breakpoint Register (IABR). + * + * I want the cpu to halt if this occurs so I can hunt around + * with the debugger and look at things. + * + * When DEBUG is defined, both machine check enable (in the MSR) + * and checkstop reset enable (in the reset mode register) are + * turned off and so a checkstop condition will result in the cpu + * halting. + * + * I force the cpu into a checkstop condition by putting an illegal + * instruction here (at least this is the theory). + * + * well - that didnt work, so just do an infinite loop! + */ +1: b 1b +#else + STD_EXCEPTION(0x1300, InstructionBreakpoint, DebugException) +#endif + STD_EXCEPTION(0x1400, SMI, UnknownException) + + STD_EXCEPTION(0x1500, Trap_15, UnknownException) + STD_EXCEPTION(0x1600, Trap_16, UnknownException) + STD_EXCEPTION(0x1700, Trap_17, UnknownException) + STD_EXCEPTION(0x1800, Trap_18, UnknownException) + STD_EXCEPTION(0x1900, Trap_19, UnknownException) + STD_EXCEPTION(0x1a00, Trap_1a, UnknownException) + STD_EXCEPTION(0x1b00, Trap_1b, UnknownException) + STD_EXCEPTION(0x1c00, Trap_1c, UnknownException) + STD_EXCEPTION(0x1d00, Trap_1d, UnknownException) + STD_EXCEPTION(0x1e00, Trap_1e, UnknownException) + STD_EXCEPTION(0x1f00, Trap_1f, UnknownException) + STD_EXCEPTION(0x2000, Trap_20, UnknownException) + STD_EXCEPTION(0x2100, Trap_21, UnknownException) + STD_EXCEPTION(0x2200, Trap_22, UnknownException) + STD_EXCEPTION(0x2300, Trap_23, UnknownException) + STD_EXCEPTION(0x2400, Trap_24, UnknownException) + STD_EXCEPTION(0x2500, Trap_25, UnknownException) + STD_EXCEPTION(0x2600, Trap_26, UnknownException) + STD_EXCEPTION(0x2700, Trap_27, UnknownException) + STD_EXCEPTION(0x2800, Trap_28, UnknownException) + STD_EXCEPTION(0x2900, Trap_29, UnknownException) + STD_EXCEPTION(0x2a00, Trap_2a, UnknownException) + STD_EXCEPTION(0x2b00, Trap_2b, UnknownException) + STD_EXCEPTION(0x2c00, Trap_2c, UnknownException) + STD_EXCEPTION(0x2d00, Trap_2d, UnknownException) + STD_EXCEPTION(0x2e00, Trap_2e, UnknownException) + STD_EXCEPTION(0x2f00, Trap_2f, UnknownException) + + + .globl _end_of_vectors +_end_of_vectors: + + . = 0x3000 + +/* + * This code finishes saving the registers to the exception frame + * and jumps to the appropriate handler for the exception. + * Register r21 is pointer into trap frame, r1 has new stack pointer. + */ + .globl transfer_to_handler +transfer_to_handler: + stw r22,_NIP(r21) + lis r22,MSR_POW@h + andc r23,r23,r22 + stw r23,_MSR(r21) + SAVE_GPR(7, r21) + SAVE_4GPRS(8, r21) + SAVE_8GPRS(12, r21) + SAVE_8GPRS(24, r21) + mflr r23 + andi. r24,r23,0x3f00 /* get vector offset */ + stw r24,TRAP(r21) + li r22,0 + stw r22,RESULT(r21) + lwz r24,0(r23) /* virtual address of handler */ + lwz r23,4(r23) /* where to go when done */ + mtspr SRR0,r24 + mtspr SRR1,r20 + mtlr r23 + SYNC + rfi /* jump to handler, enable MMU */ + +int_return: + mfmsr r28 /* Disable interrupts */ + li r4,0 + ori r4,r4,MSR_EE + andc r28,r28,r4 + SYNC /* Some chip revs need this... */ + mtmsr r28 + SYNC + lwz r2,_CTR(r1) + lwz r0,_LINK(r1) + mtctr r2 + mtlr r0 + lwz r2,_XER(r1) + lwz r0,_CCR(r1) + mtspr XER,r2 + mtcrf 0xFF,r0 + REST_10GPRS(3, r1) + REST_10GPRS(13, r1) + REST_8GPRS(23, r1) + REST_GPR(31, r1) + lwz r2,_NIP(r1) /* Restore environment */ + lwz r0,_MSR(r1) + mtspr SRR0,r2 + mtspr SRR1,r0 + lwz r0,GPR0(r1) + lwz r2,GPR2(r1) + lwz r1,GPR1(r1) + SYNC + rfi + +#if defined(CONFIG_COGENT) + +/* + * This code initialises the MPC8260 processor core + * (conforms to PowerPC 603e spec) + */ + + .globl cogent_init_8260 +cogent_init_8260: + + /* Taken from page 14 of CMA282 manual */ + /*--------------------------------------------------------------*/ + + lis r4, (CONFIG_SYS_IMMR+IM_REGBASE)@h + lis r3, CONFIG_SYS_IMMR@h + stw r3, IM_IMMR@l(r4) + lwz r3, IM_IMMR@l(r4) + stw r3, 0(r0) + lis r3, CONFIG_SYS_SYPCR@h + ori r3, r3, CONFIG_SYS_SYPCR@l + stw r3, IM_SYPCR@l(r4) + lwz r3, IM_SYPCR@l(r4) + stw r3, 4(r0) + lis r3, CONFIG_SYS_SCCR@h + ori r3, r3, CONFIG_SYS_SCCR@l + stw r3, IM_SCCR@l(r4) + lwz r3, IM_SCCR@l(r4) + stw r3, 8(r0) + + /* the rest of this was disassembled from the */ + /* EPROM code that came with my CMA282 CPU module */ + /*--------------------------------------------------------------*/ + + lis r1, 0x1234 + ori r1, r1, 0x5678 + stw r1, 0x20(r0) + lwz r1, 0x20(r0) + stw r1, 0x24(r0) + lwz r1, 0x24(r0) + lis r3, 0x0e80 + ori r3, r3, 0 + stw r1, 4(r3) + lwz r1, 4(r3) + + /* Done! */ + /*--------------------------------------------------------------*/ + + blr + +#endif /* CONFIG_COGENT */ + +/* + * This code initialises the MPC8260 processor core + * (conforms to PowerPC 603e spec) + * Note: expects original MSR contents to be in r5. + */ + + .globl init_8260_core +init_8260_core: + + /* Initialize machine status; enable machine check interrupt */ + /*--------------------------------------------------------------*/ + + li r3, MSR_KERNEL /* Set ME and RI flags */ + rlwimi r3, r5, 0, 25, 25 /* preserve IP bit set by HRCW */ +#ifdef DEBUG + rlwimi r3, r5, 0, 21, 22 /* debugger might set SE & BE bits */ +#endif + SYNC /* Some chip revs need this... */ + mtmsr r3 + SYNC + mtspr SRR1, r3 /* Make SRR1 match MSR */ + + /* Initialise the SYPCR early, and reset the watchdog (if req) */ + /*--------------------------------------------------------------*/ + + lis r3, (CONFIG_SYS_IMMR+IM_REGBASE)@h +#if !defined(CONFIG_COGENT) + lis r4, CONFIG_SYS_SYPCR@h + ori r4, r4, CONFIG_SYS_SYPCR@l + stw r4, IM_SYPCR@l(r3) +#endif /* !CONFIG_COGENT */ +#if defined(CONFIG_WATCHDOG) + li r4, 21868 /* = 0x556c */ + sth r4, IM_SWSR@l(r3) + li r4, -21959 /* = 0xaa39 */ + sth r4, IM_SWSR@l(r3) +#endif /* CONFIG_WATCHDOG */ + + /* Initialize the Hardware Implementation-dependent Registers */ + /* HID0 also contains cache control */ + /*--------------------------------------------------------------*/ + + lis r3, CONFIG_SYS_HID0_INIT@h + ori r3, r3, CONFIG_SYS_HID0_INIT@l + SYNC + mtspr HID0, r3 + + lis r3, CONFIG_SYS_HID0_FINAL@h + ori r3, r3, CONFIG_SYS_HID0_FINAL@l + SYNC + mtspr HID0, r3 + + lis r3, CONFIG_SYS_HID2@h + ori r3, r3, CONFIG_SYS_HID2@l + mtspr HID2, r3 + + /* clear all BAT's */ + /*--------------------------------------------------------------*/ + + li r0, 0 + mtspr DBAT0U, r0 + mtspr DBAT0L, r0 + mtspr DBAT1U, r0 + mtspr DBAT1L, r0 + mtspr DBAT2U, r0 + mtspr DBAT2L, r0 + mtspr DBAT3U, r0 + mtspr DBAT3L, r0 + mtspr IBAT0U, r0 + mtspr IBAT0L, r0 + mtspr IBAT1U, r0 + mtspr IBAT1L, r0 + mtspr IBAT2U, r0 + mtspr IBAT2L, r0 + mtspr IBAT3U, r0 + mtspr IBAT3L, r0 + SYNC + + /* invalidate all tlb's */ + /* */ + /* From the 603e User Manual: "The 603e provides the ability to */ + /* invalidate a TLB entry. The TLB Invalidate Entry (tlbie) */ + /* instruction invalidates the TLB entry indexed by the EA, and */ + /* operates on both the instruction and data TLBs simultaneously*/ + /* invalidating four TLB entries (both sets in each TLB). The */ + /* index corresponds to bits 15-19 of the EA. To invalidate all */ + /* entries within both TLBs, 32 tlbie instructions should be */ + /* issued, incrementing this field by one each time." */ + /* */ + /* "Note that the tlbia instruction is not implemented on the */ + /* 603e." */ + /* */ + /* bits 15-19 correspond to addresses 0x00000000 to 0x0001F000 */ + /* incrementing by 0x1000 each time. The code below is sort of */ + /* based on code in "flush_tlbs" from arch/powerpc/kernel/head.S */ + /* */ + /*--------------------------------------------------------------*/ + + li r3, 32 + mtctr r3 + li r3, 0 +1: tlbie r3 + addi r3, r3, 0x1000 + bdnz 1b + SYNC + + /* Done! */ + /*--------------------------------------------------------------*/ + + blr + +#ifdef DEBUG + +/* + * initialise things related to debugging. + * + * must be called after the global offset table (GOT) is initialised + * (GET_GOT) and after cpu_init_f() has executed. + */ + + .globl init_debug +init_debug: + + lis r3, (CONFIG_SYS_IMMR+IM_REGBASE)@h + + /* Quick and dirty hack to enable the RAM and copy the */ + /* vectors so that we can take exceptions. */ + /*--------------------------------------------------------------*/ + /* write Memory Refresh Prescaler */ + li r4, CONFIG_SYS_MPTPR + sth r4, IM_MPTPR@l(r3) + /* write 60x Refresh Timer */ + li r4, CONFIG_SYS_PSRT + stb r4, IM_PSRT@l(r3) + /* init the 60x SDRAM Mode Register */ + lis r4, (CONFIG_SYS_PSDMR|PSDMR_OP_NORM)@h + ori r4, r4, (CONFIG_SYS_PSDMR|PSDMR_OP_NORM)@l + stw r4, IM_PSDMR@l(r3) + /* write Precharge All Banks command */ + lis r4, (CONFIG_SYS_PSDMR|PSDMR_OP_PREA)@h + ori r4, r4, (CONFIG_SYS_PSDMR|PSDMR_OP_PREA)@l + stw r4, IM_PSDMR@l(r3) + stb r0, 0(0) + /* write eight CBR Refresh commands */ + lis r4, (CONFIG_SYS_PSDMR|PSDMR_OP_CBRR)@h + ori r4, r4, (CONFIG_SYS_PSDMR|PSDMR_OP_CBRR)@l + stw r4, IM_PSDMR@l(r3) + stb r0, 0(0) + stb r0, 0(0) + stb r0, 0(0) + stb r0, 0(0) + stb r0, 0(0) + stb r0, 0(0) + stb r0, 0(0) + stb r0, 0(0) + /* write Mode Register Write command */ + lis r4, (CONFIG_SYS_PSDMR|PSDMR_OP_MRW)@h + ori r4, r4, (CONFIG_SYS_PSDMR|PSDMR_OP_MRW)@l + stw r4, IM_PSDMR@l(r3) + stb r0, 0(0) + /* write Normal Operation command and enable Refresh */ + lis r4, (CONFIG_SYS_PSDMR|PSDMR_OP_NORM|PSDMR_RFEN)@h + ori r4, r4, (CONFIG_SYS_PSDMR|PSDMR_OP_NORM|PSDMR_RFEN)@l + stw r4, IM_PSDMR@l(r3) + stb r0, 0(0) + /* RAM should now be operational */ + +#define VEC_WRD_CNT ((_end_of_vectors - _start + EXC_OFF_SYS_RESET) / 4) + mflr r3 + GET_GOT + mtlr r3 + lwz r3, GOT(_end_of_vectors) + rlwinm r4, r3, 0, 18, 31 /* _end_of_vectors & 0x3FFF */ + lis r5, VEC_WRD_CNT@h + ori r5, r5, VEC_WRD_CNT@l + mtctr r5 +1: + lwzu r5, -4(r3) + stwu r5, -4(r4) + bdnz 1b + + /* Load the Instruction Address Breakpoint Register (IABR). */ + /* */ + /* The address to load is stored in the first word of dual port */ + /* ram and should be preserved while the power is on, so you */ + /* can plug addresses into that location then reset the cpu and */ + /* this code will load that address into the IABR after the */ + /* reset. */ + /* */ + /* When the program counter matches the contents of the IABR, */ + /* an exception is generated (before the instruction at that */ + /* location completes). The vector for this exception is 0x1300 */ + /*--------------------------------------------------------------*/ + lis r3, CONFIG_SYS_IMMR@h + lwz r3, 0(r3) + mtspr IABR, r3 + + /* Set the entire dual port RAM (where the initial stack */ + /* resides) to a known value - makes it easier to see where */ + /* the stack has been written */ + /*--------------------------------------------------------------*/ + lis r3, (CONFIG_SYS_IMMR + CONFIG_SYS_INIT_SP_OFFSET)@h + ori r3, r3, (CONFIG_SYS_IMMR + CONFIG_SYS_INIT_SP_OFFSET)@l + li r4, ((CONFIG_SYS_INIT_SP_OFFSET - 4) / 4) + mtctr r4 + lis r4, 0xdeadbeaf@h + ori r4, r4, 0xdeadbeaf@l +1: + stwu r4, -4(r3) + bdnz 1b + + /* Done! */ + /*--------------------------------------------------------------*/ + + blr +#endif + +/* Cache functions. + * + * Note: requires that all cache bits in + * HID0 are in the low half word. + */ + .globl icache_enable +icache_enable: + mfspr r3, HID0 + ori r3, r3, HID0_ICE + lis r4, 0 + ori r4, r4, HID0_ILOCK + andc r3, r3, r4 + ori r4, r3, HID0_ICFI + isync + mtspr HID0, r4 /* sets enable and invalidate, clears lock */ + isync + mtspr HID0, r3 /* clears invalidate */ + blr + + .globl icache_disable +icache_disable: + mfspr r3, HID0 + lis r4, 0 + ori r4, r4, HID0_ICE|HID0_ILOCK + andc r3, r3, r4 + ori r4, r3, HID0_ICFI + isync + mtspr HID0, r4 /* sets invalidate, clears enable and lock */ + isync + mtspr HID0, r3 /* clears invalidate */ + blr + + .globl icache_status +icache_status: + mfspr r3, HID0 + rlwinm r3, r3, HID0_ICE_BITPOS + 1, 31, 31 + blr + + .globl dcache_enable +dcache_enable: + mfspr r3, HID0 + ori r3, r3, HID0_DCE + lis r4, 0 + ori r4, r4, HID0_DLOCK + andc r3, r3, r4 + ori r4, r3, HID0_DCI + sync + mtspr HID0, r4 /* sets enable and invalidate, clears lock */ + sync + mtspr HID0, r3 /* clears invalidate */ + blr + + .globl dcache_disable +dcache_disable: + mfspr r3, HID0 + lis r4, 0 + ori r4, r4, HID0_DCE|HID0_DLOCK + andc r3, r3, r4 + ori r4, r3, HID0_DCI + sync + mtspr HID0, r4 /* sets invalidate, clears enable and lock */ + sync + mtspr HID0, r3 /* clears invalidate */ + blr + + .globl dcache_status +dcache_status: + mfspr r3, HID0 + rlwinm r3, r3, HID0_DCE_BITPOS + 1, 31, 31 + blr + + .globl get_pvr +get_pvr: + mfspr r3, PVR + blr + +/*------------------------------------------------------------------------------*/ + +/* + * void relocate_code (addr_sp, gd, addr_moni) + * + * This "function" does not return, instead it continues in RAM + * after relocating the monitor code. + * + * r3 = dest + * r4 = src + * r5 = length in bytes + * r6 = cachelinesize + */ + .globl relocate_code +relocate_code: + mr r1, r3 /* Set new stack pointer */ + mr r9, r4 /* Save copy of Global Data pointer */ + mr r10, r5 /* Save copy of Destination Address */ + + GET_GOT + mr r3, r5 /* Destination Address */ + lis r4, CONFIG_SYS_MONITOR_BASE@h /* Source Address */ + ori r4, r4, CONFIG_SYS_MONITOR_BASE@l + lwz r5, GOT(__init_end) + sub r5, r5, r4 + li r6, CONFIG_SYS_CACHELINE_SIZE /* Cache Line Size */ + + /* + * Fix GOT pointer: + * + * New GOT-PTR = (old GOT-PTR - CONFIG_SYS_MONITOR_BASE) + Destination Address + * + * Offset: + */ + sub r15, r10, r4 + + /* First our own GOT */ + add r12, r12, r15 + /* then the one used by the C code */ + add r30, r30, r15 + + /* + * Now relocate code + */ + + cmplw cr1,r3,r4 + addi r0,r5,3 + srwi. r0,r0,2 + beq cr1,4f /* In place copy is not necessary */ + beq 7f /* Protect against 0 count */ + mtctr r0 + bge cr1,2f + + la r8,-4(r4) + la r7,-4(r3) +1: lwzu r0,4(r8) + stwu r0,4(r7) + bdnz 1b + b 4f + +2: slwi r0,r0,2 + add r8,r4,r0 + add r7,r3,r0 +3: lwzu r0,-4(r8) + stwu r0,-4(r7) + bdnz 3b + +/* + * Now flush the cache: note that we must start from a cache aligned + * address. Otherwise we might miss one cache line. + */ +4: cmpwi r6,0 + add r5,r3,r5 + beq 7f /* Always flush prefetch queue in any case */ + subi r0,r6,1 + andc r3,r3,r0 + mfspr r7,HID0 /* don't do dcbst if dcache is disabled */ + rlwinm r7,r7,HID0_DCE_BITPOS+1,31,31 + cmpwi r7,0 + beq 9f + mr r4,r3 +5: dcbst 0,r4 + add r4,r4,r6 + cmplw r4,r5 + blt 5b + sync /* Wait for all dcbst to complete on bus */ +9: mfspr r7,HID0 /* don't do icbi if icache is disabled */ + rlwinm r7,r7,HID0_ICE_BITPOS+1,31,31 + cmpwi r7,0 + beq 7f + mr r4,r3 +6: icbi 0,r4 + add r4,r4,r6 + cmplw r4,r5 + blt 6b +7: sync /* Wait for all icbi to complete on bus */ + isync + +/* + * We are done. Do not return, instead branch to second part of board + * initialization, now running from RAM. + */ + + addi r0, r10, in_ram - _start + EXC_OFF_SYS_RESET + mtlr r0 + blr + +in_ram: + + /* + * Relocation Function, r12 point to got2+0x8000 + * + * Adjust got2 pointers, no need to check for 0, this code + * already puts a few entries in the table. + */ + li r0,__got2_entries@sectoff@l + la r3,GOT(_GOT2_TABLE_) + lwz r11,GOT(_GOT2_TABLE_) + mtctr r0 + sub r11,r3,r11 + addi r3,r3,-4 +1: lwzu r0,4(r3) + cmpwi r0,0 + beq- 2f + add r0,r0,r11 + stw r0,0(r3) +2: bdnz 1b + + /* + * Now adjust the fixups and the pointers to the fixups + * in case we need to move ourselves again. + */ + li r0,__fixup_entries@sectoff@l + lwz r3,GOT(_FIXUP_TABLE_) + cmpwi r0,0 + mtctr r0 + addi r3,r3,-4 + beq 4f +3: lwzu r4,4(r3) + lwzux r0,r4,r11 + add r0,r0,r11 + stw r10,0(r3) + stw r0,0(r4) + bdnz 3b +4: +clear_bss: + /* + * Now clear BSS segment + */ + lwz r3,GOT(__bss_start) +#if defined(CONFIG_HYMOD) + /* + * For HYMOD - the environment is the very last item in flash. + * The real .bss stops just before environment starts, so only + * clear up to that point. + * + * taken from mods for FADS board + */ + lwz r4,GOT(environment) +#else + lwz r4,GOT(_end) +#endif + + cmplw 0, r3, r4 + beq 6f + + li r0, 0 +5: + stw r0, 0(r3) + addi r3, r3, 4 + cmplw 0, r3, r4 + bne 5b +6: + + mr r3, r9 /* Global Data pointer */ + mr r4, r10 /* Destination Address */ + bl board_init_r + + /* + * Copy exception vector code to low memory + * + * r3: dest_addr + * r7: source address, r8: end address, r9: target address + */ + .globl trap_init +trap_init: + mflr r4 /* save link register */ + GET_GOT + lwz r7, GOT(_start) + lwz r8, GOT(_end_of_vectors) + + li r9, 0x100 /* reset vector always at 0x100 */ + + cmplw 0, r7, r8 + bgelr /* return if r7>=r8 - just in case */ +1: + lwz r0, 0(r7) + stw r0, 0(r9) + addi r7, r7, 4 + addi r9, r9, 4 + cmplw 0, r7, r8 + bne 1b + + /* + * relocate `hdlr' and `int_return' entries + */ + li r7, .L_MachineCheck - _start + EXC_OFF_SYS_RESET + li r8, Alignment - _start + EXC_OFF_SYS_RESET +2: + bl trap_reloc + addi r7, r7, 0x100 /* next exception vector */ + cmplw 0, r7, r8 + blt 2b + + li r7, .L_Alignment - _start + EXC_OFF_SYS_RESET + bl trap_reloc + + li r7, .L_ProgramCheck - _start + EXC_OFF_SYS_RESET + bl trap_reloc + + li r7, .L_FPUnavailable - _start + EXC_OFF_SYS_RESET + li r8, SystemCall - _start + EXC_OFF_SYS_RESET +3: + bl trap_reloc + addi r7, r7, 0x100 /* next exception vector */ + cmplw 0, r7, r8 + blt 3b + + li r7, .L_SingleStep - _start + EXC_OFF_SYS_RESET + li r8, _end_of_vectors - _start + EXC_OFF_SYS_RESET +4: + bl trap_reloc + addi r7, r7, 0x100 /* next exception vector */ + cmplw 0, r7, r8 + blt 4b + + mfmsr r3 /* now that the vectors have */ + lis r7, MSR_IP@h /* relocated into low memory */ + ori r7, r7, MSR_IP@l /* MSR[IP] can be turned off */ + andc r3, r3, r7 /* (if it was on) */ + SYNC /* Some chip revs need this... */ + mtmsr r3 + SYNC + + mtlr r4 /* restore link register */ + blr diff --git a/arch/powerpc/cpu/mpc8260/traps.c b/arch/powerpc/cpu/mpc8260/traps.c new file mode 100644 index 0000000..c116cdf --- /dev/null +++ b/arch/powerpc/cpu/mpc8260/traps.c @@ -0,0 +1,273 @@ +/* + * linux/arch/powerpc/kernel/traps.c + * + * Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org) + * + * Modified by Cort Dougan (cort@cs.nmt.edu) + * and Paul Mackerras (paulus@cs.anu.edu.au) + * + * (C) Copyright 2000 + * Wolfgang Denk, DENX Software Engineering, wd@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 + */ + +/* + * This file handles the architecture-dependent parts of hardware exceptions + */ + +#include <common.h> +#include <command.h> +#include <kgdb.h> +#include <asm/processor.h> +#include <asm/m8260_pci.h> + +/* Returns 0 if exception not found and fixup otherwise. */ +extern unsigned long search_exception_table(unsigned long); + +/* THIS NEEDS CHANGING to use the board info structure. +*/ +#define END_OF_MEM 0x02000000 + +/* + * Trap & Exception support + */ + +void +print_backtrace(unsigned long *sp) +{ + int cnt = 0; + unsigned long i; + + puts ("Call backtrace: "); + while (sp) { + if ((uint)sp > END_OF_MEM) + break; + + i = sp[1]; + if (cnt++ % 7 == 0) + putc ('\n'); + printf("%08lX ", i); + if (cnt > 32) break; + sp = (unsigned long *)*sp; + } + putc ('\n'); +} + +void show_regs(struct pt_regs * regs) +{ + int i; + + printf("NIP: %08lX XER: %08lX LR: %08lX REGS: %p TRAP: %04lx DAR: %08lX\n", + regs->nip, regs->xer, regs->link, regs, regs->trap, regs->dar); + printf("MSR: %08lx EE: %01x PR: %01x FP: %01x ME: %01x IR/DR: %01x%01x\n", + regs->msr, regs->msr&MSR_EE ? 1 : 0, regs->msr&MSR_PR ? 1 : 0, + regs->msr & MSR_FP ? 1 : 0,regs->msr&MSR_ME ? 1 : 0, + regs->msr&MSR_IR ? 1 : 0, + regs->msr&MSR_DR ? 1 : 0); + + putc ('\n'); + for (i = 0; i < 32; i++) { + if ((i % 8) == 0) { + printf("GPR%02d: ", i); + } + + printf("%08lX ", regs->gpr[i]); + if ((i % 8) == 7) { + putc ('\n'); + } + } +} + + +void +_exception(int signr, struct pt_regs *regs) +{ + show_regs(regs); + print_backtrace((unsigned long *)regs->gpr[1]); + panic("Exception in kernel pc %lx signal %d",regs->nip,signr); +} + +#ifdef CONFIG_PCI +void dump_pci (void) +{ + + volatile immap_t *immap = (immap_t *) CONFIG_SYS_IMMR; + + printf ("PCI: err status %x err mask %x err ctrl %x\n", + le32_to_cpu (immap->im_pci.pci_esr), + le32_to_cpu (immap->im_pci.pci_emr), + le32_to_cpu (immap->im_pci.pci_ecr)); + printf (" error address %x error data %x ctrl %x\n", + le32_to_cpu (immap->im_pci.pci_eacr), + le32_to_cpu (immap->im_pci.pci_edcr), + le32_to_cpu (immap->im_pci.pci_eccr)); + +} +#endif + +void +MachineCheckException(struct pt_regs *regs) +{ + unsigned long fixup; + + /* Probing PCI using config cycles cause this exception + * when a device is not present. Catch it and return to + * the PCI exception handler. + */ +#ifdef CONFIG_PCI + volatile immap_t *immap = (immap_t *)CONFIG_SYS_IMMR; +#ifdef DEBUG + dump_pci(); +#endif + /* clear the error in the error status register */ + if(immap->im_pci.pci_esr & cpu_to_le32(PCI_ERROR_PCI_NO_RSP)) { + immap->im_pci.pci_esr = cpu_to_le32(PCI_ERROR_PCI_NO_RSP); + return; + } +#endif + if ((fixup = search_exception_table(regs->nip)) != 0) { + regs->nip = fixup; + return; + } + +#if defined(CONFIG_CMD_KGDB) + if (debugger_exception_handler && (*debugger_exception_handler)(regs)) + return; +#endif + + puts ("Machine check in kernel mode.\n" + "Caused by (from msr): "); + printf("regs %p ",regs); + switch( regs->msr & 0x000F0000) { + case (0x80000000>>12): + puts ("Machine check signal - probably due to mm fault\n" + "with mmu off\n"); + break; + case (0x80000000>>13): + puts ("Transfer error ack signal\n"); + break; + case (0x80000000>>14): + puts ("Data parity signal\n"); + break; + case (0x80000000>>15): + puts ("Address parity signal\n"); + break; + default: + puts ("Unknown values in msr\n"); + } + show_regs(regs); + print_backtrace((unsigned long *)regs->gpr[1]); +#ifdef CONFIG_PCI + dump_pci(); +#endif + panic("machine check"); +} + +void +AlignmentException(struct pt_regs *regs) +{ +#if defined(CONFIG_CMD_KGDB) + if (debugger_exception_handler && (*debugger_exception_handler)(regs)) + return; +#endif + show_regs(regs); + print_backtrace((unsigned long *)regs->gpr[1]); + panic("Alignment Exception"); +} + +void +ProgramCheckException(struct pt_regs *regs) +{ +#if defined(CONFIG_CMD_KGDB) + if (debugger_exception_handler && (*debugger_exception_handler)(regs)) + return; +#endif + show_regs(regs); + print_backtrace((unsigned long *)regs->gpr[1]); + panic("Program Check Exception"); +} + +void +SoftEmuException(struct pt_regs *regs) +{ +#if defined(CONFIG_CMD_KGDB) + if (debugger_exception_handler && (*debugger_exception_handler)(regs)) + return; +#endif + show_regs(regs); + print_backtrace((unsigned long *)regs->gpr[1]); + panic("Software Emulation Exception"); +} + + +void +UnknownException(struct pt_regs *regs) +{ +#if defined(CONFIG_CMD_KGDB) + if (debugger_exception_handler && (*debugger_exception_handler)(regs)) + return; +#endif + printf("Bad trap at PC: %lx, SR: %lx, vector=%lx\n", + regs->nip, regs->msr, regs->trap); + _exception(0, regs); +} + +#if defined(CONFIG_CMD_BEDBUG) +extern void do_bedbug_breakpoint(struct pt_regs *); +#endif + +void +DebugException(struct pt_regs *regs) +{ + + printf("Debugger trap at @ %lx\n", regs->nip ); + show_regs(regs); +#if defined(CONFIG_CMD_BEDBUG) + do_bedbug_breakpoint( regs ); +#endif +} + +/* Probe an address by reading. If not present, return -1, otherwise + * return 0. + */ +int +addr_probe(uint *addr) +{ +#if 0 + int retval; + + __asm__ __volatile__( \ + "1: lwz %0,0(%1)\n" \ + " eieio\n" \ + " li %0,0\n" \ + "2:\n" \ + ".section .fixup,\"ax\"\n" \ + "3: li %0,-1\n" \ + " b 2b\n" \ + ".section __ex_table,\"a\"\n" \ + " .align 2\n" \ + " .long 1b,3b\n" \ + ".text" \ + : "=r" (retval) : "r"(addr)); + + return (retval); +#endif + return 0; +} diff --git a/arch/powerpc/cpu/mpc8260/u-boot.lds b/arch/powerpc/cpu/mpc8260/u-boot.lds new file mode 100644 index 0000000..b8681e7 --- /dev/null +++ b/arch/powerpc/cpu/mpc8260/u-boot.lds @@ -0,0 +1,122 @@ +/* + * (C) Copyright 2001-2007 + * Wolfgang Denk, DENX Software Engineering, wd@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 + */ + +OUTPUT_ARCH(powerpc) +/* Do we need any of these for elf? + __DYNAMIC = 0; */ +SECTIONS +{ + /* Read-only sections, merged into text segment: */ + . = + SIZEOF_HEADERS; + .interp : { *(.interp) } + .hash : { *(.hash) } + .dynsym : { *(.dynsym) } + .dynstr : { *(.dynstr) } + .rel.text : { *(.rel.text) } + .rela.text : { *(.rela.text) } + .rel.data : { *(.rel.data) } + .rela.data : { *(.rela.data) } + .rel.rodata : { *(.rel.rodata) } + .rela.rodata : { *(.rela.rodata) } + .rel.got : { *(.rel.got) } + .rela.got : { *(.rela.got) } + .rel.ctors : { *(.rel.ctors) } + .rela.ctors : { *(.rela.ctors) } + .rel.dtors : { *(.rel.dtors) } + .rela.dtors : { *(.rela.dtors) } + .rel.bss : { *(.rel.bss) } + .rela.bss : { *(.rela.bss) } + .rel.plt : { *(.rel.plt) } + .rela.plt : { *(.rela.plt) } + .init : { *(.init) } + .plt : { *(.plt) } + .text : + { + arch/powerpc/cpu/mpc8260/start.o (.text) + *(.text) + *(.got1) + . = ALIGN(16); + *(.eh_frame) + *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.rodata*))) + } + .fini : { *(.fini) } =0 + .ctors : { *(.ctors) } + .dtors : { *(.dtors) } + + /* Read-write section, merged into data segment: */ + . = (. + 0x0FFF) & 0xFFFFF000; + _erotext = .; + PROVIDE (erotext = .); + .reloc : + { + *(.got) + _GOT2_TABLE_ = .; + *(.got2) + _FIXUP_TABLE_ = .; + *(.fixup) + } + __got2_entries = (_FIXUP_TABLE_ - _GOT2_TABLE_) >> 2; + __fixup_entries = (. - _FIXUP_TABLE_) >> 2; + + .data : + { + *(.data) + *(.data1) + *(.sdata) + *(.sdata2) + *(.dynamic) + CONSTRUCTORS + } + _edata = .; + PROVIDE (edata = .); + + . = .; + __u_boot_cmd_start = .; + .u_boot_cmd : { *(.u_boot_cmd) } + __u_boot_cmd_end = .; + + + . = .; + __start___ex_table = .; + __ex_table : { *(__ex_table) } + __stop___ex_table = .; + + . = ALIGN(4096); + __init_begin = .; + .text.init : { *(.text.init) } + .data.init : { *(.data.init) } + . = ALIGN(4096); + __init_end = .; + + __bss_start = .; + .bss (NOLOAD) : + { + *(.sbss) *(.scommon) + *(.dynbss) + *(.bss) + *(COMMON) + . = ALIGN(4); + } + _end = . ; + PROVIDE (end = .); +} diff --git a/arch/powerpc/cpu/mpc83xx/Makefile b/arch/powerpc/cpu/mpc83xx/Makefile new file mode 100644 index 0000000..15e2c18 --- /dev/null +++ b/arch/powerpc/cpu/mpc83xx/Makefile @@ -0,0 +1,62 @@ +# +# (C) Copyright 2006 +# Wolfgang Denk, DENX Software Engineering, wd@denx.de. +# +# Copyright 2004 Freescale Semiconductor, Inc. +# +# 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 $(TOPDIR)/config.mk + +LIB = $(obj)lib$(CPU).a + +START = start.o + +COBJS-y += traps.o +COBJS-y += cpu.o +COBJS-y += cpu_init.o +COBJS-y += speed.o +COBJS-y += interrupts.o +COBJS-y += spd_sdram.o +COBJS-y += ecc.o +COBJS-$(CONFIG_QE) += qe_io.o +COBJS-$(CONFIG_FSL_SERDES) += serdes.o +COBJS-$(CONFIG_PCI) += pci.o +COBJS-$(CONFIG_PCIE) += pcie.o +COBJS-$(CONFIG_OF_LIBFDT) += fdt.o + +COBJS := $(COBJS-y) +SRCS := $(START:.o=.S) $(SOBJS:.o=.S) $(COBJS:.o=.c) +OBJS := $(addprefix $(obj),$(SOBJS) $(COBJS)) +START := $(addprefix $(obj),$(START)) + +all: $(obj).depend $(START) $(LIB) + +$(LIB): $(OBJS) + $(AR) $(ARFLAGS) $@ $(OBJS) + +######################################################################### + +# defines $(obj).depend target +include $(SRCTREE)/rules.mk + +sinclude $(obj).depend + +######################################################################### diff --git a/arch/powerpc/cpu/mpc83xx/config.mk b/arch/powerpc/cpu/mpc83xx/config.mk new file mode 100644 index 0000000..8a3a8c1 --- /dev/null +++ b/arch/powerpc/cpu/mpc83xx/config.mk @@ -0,0 +1,29 @@ +# +# Copyright 2004 Freescale Semiconductor, Inc. +# +# 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 +# + +PLATFORM_RELFLAGS += -fPIC -meabi + +PLATFORM_CPPFLAGS += -DCONFIG_MPC83xx -DCONFIG_E300 \ + -ffixed-r2 -msoft-float + +# Use default linker script. Board port can override in board/*/config.mk +LDSCRIPT := $(SRCTREE)/arch/powerpc/cpu/mpc83xx/u-boot.lds diff --git a/arch/powerpc/cpu/mpc83xx/cpu.c b/arch/powerpc/cpu/mpc83xx/cpu.c new file mode 100644 index 0000000..51180d6 --- /dev/null +++ b/arch/powerpc/cpu/mpc83xx/cpu.c @@ -0,0 +1,334 @@ +/* + * Copyright (C) 2004-2007 Freescale Semiconductor, Inc. + * + * 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 + */ + +/* + * CPU specific code for the MPC83xx family. + * + * Derived from the MPC8260 and MPC85xx. + */ + +#include <common.h> +#include <watchdog.h> +#include <command.h> +#include <mpc83xx.h> +#include <asm/processor.h> +#include <libfdt.h> +#include <tsec.h> +#include <netdev.h> +#include <fsl_esdhc.h> +#ifdef CONFIG_BOOTCOUNT_LIMIT +#include <asm/immap_qe.h> +#include <asm/io.h> +#endif + +DECLARE_GLOBAL_DATA_PTR; + +int checkcpu(void) +{ + volatile immap_t *immr; + ulong clock = gd->cpu_clk; + u32 pvr = get_pvr(); + u32 spridr; + char buf[32]; + int i; + + const struct cpu_type { + char name[15]; + u32 partid; + } cpu_type_list [] = { + CPU_TYPE_ENTRY(8311), + CPU_TYPE_ENTRY(8313), + CPU_TYPE_ENTRY(8314), + CPU_TYPE_ENTRY(8315), + CPU_TYPE_ENTRY(8321), + CPU_TYPE_ENTRY(8323), + CPU_TYPE_ENTRY(8343), + CPU_TYPE_ENTRY(8347_TBGA_), + CPU_TYPE_ENTRY(8347_PBGA_), + CPU_TYPE_ENTRY(8349), + CPU_TYPE_ENTRY(8358_TBGA_), + CPU_TYPE_ENTRY(8358_PBGA_), + CPU_TYPE_ENTRY(8360), + CPU_TYPE_ENTRY(8377), + CPU_TYPE_ENTRY(8378), + CPU_TYPE_ENTRY(8379), + }; + + immr = (immap_t *)CONFIG_SYS_IMMR; + + puts("CPU: "); + + switch (pvr & 0xffff0000) { + case PVR_E300C1: + printf("e300c1, "); + break; + + case PVR_E300C2: + printf("e300c2, "); + break; + + case PVR_E300C3: + printf("e300c3, "); + break; + + case PVR_E300C4: + printf("e300c4, "); + break; + + default: + printf("Unknown core, "); + } + + spridr = immr->sysconf.spridr; + + for (i = 0; i < ARRAY_SIZE(cpu_type_list); i++) + if (cpu_type_list[i].partid == PARTID_NO_E(spridr)) { + puts("MPC"); + puts(cpu_type_list[i].name); + if (IS_E_PROCESSOR(spridr)) + puts("E"); + if (REVID_MAJOR(spridr) >= 2) + puts("A"); + printf(", Rev: %d.%d", REVID_MAJOR(spridr), + REVID_MINOR(spridr)); + break; + } + + if (i == ARRAY_SIZE(cpu_type_list)) + printf("(SPRIDR %08x unknown), ", spridr); + + printf(" at %s MHz, ", strmhz(buf, clock)); + + printf("CSB: %s MHz\n", strmhz(buf, gd->csb_clk)); + + return 0; +} + + +/* + * Program a UPM with the code supplied in the table. + * + * The 'dummy' variable is used to increment the MAD. 'dummy' is + * supposed to be a pointer to the memory of the device being + * programmed by the UPM. The data in the MDR is written into + * memory and the MAD is incremented every time there's a write + * to 'dummy'. Unfortunately, the current prototype for this + * function doesn't allow for passing the address of this + * device, and changing the prototype will break a number lots + * of other code, so we need to use a round-about way of finding + * the value for 'dummy'. + * + * The value can be extracted from the base address bits of the + * Base Register (BR) associated with the specific UPM. To find + * that BR, we need to scan all 8 BRs until we find the one that + * has its MSEL bits matching the UPM we want. Once we know the + * right BR, we can extract the base address bits from it. + * + * The MxMR and the BR and OR of the chosen bank should all be + * configured before calling this function. + * + * Parameters: + * upm: 0=UPMA, 1=UPMB, 2=UPMC + * table: Pointer to an array of values to program + * size: Number of elements in the array. Must be 64 or less. + */ +void upmconfig (uint upm, uint *table, uint size) +{ + volatile immap_t *immap = (immap_t *) CONFIG_SYS_IMMR; + volatile fsl_lbus_t *lbus = &immap->lbus; + volatile uchar *dummy = NULL; + const u32 msel = (upm + 4) << BR_MSEL_SHIFT; /* What the MSEL field in BRn should be */ + volatile u32 *mxmr = &lbus->mamr + upm; /* Pointer to mamr, mbmr, or mcmr */ + uint i; + + /* Scan all the banks to determine the base address of the device */ + for (i = 0; i < 8; i++) { + if ((lbus->bank[i].br & BR_MSEL) == msel) { + dummy = (uchar *) (lbus->bank[i].br & BR_BA); + break; + } + } + + if (!dummy) { + printf("Error: %s() could not find matching BR\n", __FUNCTION__); + hang(); + } + + /* Set the OP field in the MxMR to "write" and the MAD field to 000000 */ + *mxmr = (*mxmr & 0xCFFFFFC0) | 0x10000000; + + for (i = 0; i < size; i++) { + lbus->mdr = table[i]; + __asm__ __volatile__ ("sync"); + *dummy = 0; /* Write the value to memory and increment MAD */ + __asm__ __volatile__ ("sync"); + while(((*mxmr & 0x3f) != ((i + 1) & 0x3f))); + } + + /* Set the OP field in the MxMR to "normal" and the MAD field to 000000 */ + *mxmr &= 0xCFFFFFC0; +} + + +int +do_reset (cmd_tbl_t * cmdtp, int flag, int argc, char *argv[]) +{ + ulong msr; +#ifndef MPC83xx_RESET + ulong addr; +#endif + + volatile immap_t *immap = (immap_t *) CONFIG_SYS_IMMR; + + puts("Resetting the board.\n"); + +#ifdef MPC83xx_RESET + + /* Interrupts and MMU off */ + __asm__ __volatile__ ("mfmsr %0":"=r" (msr):); + + msr &= ~( MSR_EE | MSR_IR | MSR_DR); + __asm__ __volatile__ ("mtmsr %0"::"r" (msr)); + + /* enable Reset Control Reg */ + immap->reset.rpr = 0x52535445; + __asm__ __volatile__ ("sync"); + __asm__ __volatile__ ("isync"); + + /* confirm Reset Control Reg is enabled */ + while(!((immap->reset.rcer) & RCER_CRE)); + + udelay(200); + + /* perform reset, only one bit */ + immap->reset.rcr = RCR_SWHR; + +#else /* ! MPC83xx_RESET */ + + immap->reset.rmr = RMR_CSRE; /* Checkstop Reset enable */ + + /* Interrupts and MMU off */ + __asm__ __volatile__ ("mfmsr %0":"=r" (msr):); + + msr &= ~(MSR_ME | MSR_EE | MSR_IR | MSR_DR); + __asm__ __volatile__ ("mtmsr %0"::"r" (msr)); + + /* + * Trying to execute the next instruction at a non-existing address + * should cause a machine check, resulting in reset + */ + addr = CONFIG_SYS_RESET_ADDRESS; + + ((void (*)(void)) addr) (); +#endif /* MPC83xx_RESET */ + + return 1; +} + + +/* + * Get timebase clock frequency (like cpu_clk in Hz) + */ + +unsigned long get_tbclk(void) +{ + ulong tbclk; + + tbclk = (gd->bus_clk + 3L) / 4L; + + return tbclk; +} + + +#if defined(CONFIG_WATCHDOG) +void watchdog_reset (void) +{ + int re_enable = disable_interrupts(); + + /* Reset the 83xx watchdog */ + volatile immap_t *immr = (immap_t *) CONFIG_SYS_IMMR; + immr->wdt.swsrr = 0x556c; + immr->wdt.swsrr = 0xaa39; + + if (re_enable) + enable_interrupts (); +} +#endif + +/* + * Initializes on-chip ethernet controllers. + * to override, implement board_eth_init() + */ +int cpu_eth_init(bd_t *bis) +{ +#if defined(CONFIG_UEC_ETH) + uec_standard_init(bis); +#endif + +#if defined(CONFIG_TSEC_ENET) + tsec_standard_init(bis); +#endif + return 0; +} + +/* + * Initializes on-chip MMC controllers. + * to override, implement board_mmc_init() + */ +int cpu_mmc_init(bd_t *bis) +{ +#ifdef CONFIG_FSL_ESDHC + return fsl_esdhc_mmc_init(bis); +#else + return 0; +#endif +} + +#ifdef CONFIG_BOOTCOUNT_LIMIT + +#if !defined(CONFIG_MPC8360) +#error "CONFIG_BOOTCOUNT_LIMIT only for MPC8360 implemented" +#endif + +#if !defined(CONFIG_BOOTCOUNT_ADDR) +#define CONFIG_BOOTCOUNT_ADDR (0x110000 + QE_MURAM_SIZE - 2 * sizeof(unsigned long)) +#endif + +#include <asm/io.h> + +void bootcount_store (ulong a) +{ + void *reg = (void *)(CONFIG_SYS_IMMR + CONFIG_BOOTCOUNT_ADDR); + out_be32 (reg, a); + out_be32 (reg + 4, BOOTCOUNT_MAGIC); +} + +ulong bootcount_load (void) +{ + void *reg = (void *)(CONFIG_SYS_IMMR + CONFIG_BOOTCOUNT_ADDR); + + if (in_be32 (reg + 4) != BOOTCOUNT_MAGIC) + return 0; + else + return in_be32 (reg); +} +#endif /* CONFIG_BOOTCOUNT_LIMIT */ diff --git a/arch/powerpc/cpu/mpc83xx/cpu_init.c b/arch/powerpc/cpu/mpc83xx/cpu_init.c new file mode 100644 index 0000000..75b4522 --- /dev/null +++ b/arch/powerpc/cpu/mpc83xx/cpu_init.c @@ -0,0 +1,548 @@ +/* + * Copyright (C) 2004-2009 Freescale Semiconductor, Inc. + * + * 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> +#include <mpc83xx.h> +#include <ioports.h> +#include <asm/io.h> +#ifdef CONFIG_USB_EHCI_FSL +#include <usb/ehci-fsl.h> +#endif + +DECLARE_GLOBAL_DATA_PTR; + +#ifdef CONFIG_QE +extern qe_iop_conf_t qe_iop_conf_tab[]; +extern void qe_config_iopin(u8 port, u8 pin, int dir, + int open_drain, int assign); +extern void qe_init(uint qe_base); +extern void qe_reset(void); + +static void config_qe_ioports(void) +{ + u8 port, pin; + int dir, open_drain, assign; + int i; + + for (i = 0; qe_iop_conf_tab[i].assign != QE_IOP_TAB_END; i++) { + port = qe_iop_conf_tab[i].port; + pin = qe_iop_conf_tab[i].pin; + dir = qe_iop_conf_tab[i].dir; + open_drain = qe_iop_conf_tab[i].open_drain; + assign = qe_iop_conf_tab[i].assign; + qe_config_iopin(port, pin, dir, open_drain, assign); + } +} +#endif + +/* + * Breathe some life into the CPU... + * + * Set up the memory map, + * initialize a bunch of registers, + * initialize the UPM's + */ +void cpu_init_f (volatile immap_t * im) +{ + __be32 acr_mask = +#ifdef CONFIG_SYS_ACR_PIPE_DEP /* Arbiter pipeline depth */ + (ACR_PIPE_DEP << ACR_PIPE_DEP_SHIFT) | +#endif +#ifdef CONFIG_SYS_ACR_RPTCNT /* Arbiter repeat count */ + (ACR_RPTCNT << ACR_RPTCNT_SHIFT) | +#endif +#ifdef CONFIG_SYS_ACR_APARK /* Arbiter address parking mode */ + (ACR_APARK << ACR_APARK_SHIFT) | +#endif +#ifdef CONFIG_SYS_ACR_PARKM /* Arbiter parking master */ + (ACR_PARKM << ACR_PARKM_SHIFT) | +#endif + 0; + __be32 acr_val = +#ifdef CONFIG_SYS_ACR_PIPE_DEP /* Arbiter pipeline depth */ + (CONFIG_SYS_ACR_PIPE_DEP << ACR_PIPE_DEP_SHIFT) | +#endif +#ifdef CONFIG_SYS_ACR_RPTCNT /* Arbiter repeat count */ + (CONFIG_SYS_ACR_RPTCNT << ACR_RPTCNT_SHIFT) | +#endif +#ifdef CONFIG_SYS_ACR_APARK /* Arbiter address parking mode */ + (CONFIG_SYS_ACR_APARK << ACR_APARK_SHIFT) | +#endif +#ifdef CONFIG_SYS_ACR_PARKM /* Arbiter parking master */ + (CONFIG_SYS_ACR_PARKM << ACR_PARKM_SHIFT) | +#endif + 0; + __be32 spcr_mask = +#ifdef CONFIG_SYS_SPCR_OPT /* Optimize transactions between CSB and other dev */ + (SPCR_OPT << SPCR_OPT_SHIFT) | +#endif +#ifdef CONFIG_SYS_SPCR_TSECEP /* all eTSEC's Emergency priority */ + (SPCR_TSECEP << SPCR_TSECEP_SHIFT) | +#endif +#ifdef CONFIG_SYS_SPCR_TSEC1EP /* TSEC1 Emergency priority */ + (SPCR_TSEC1EP << SPCR_TSEC1EP_SHIFT) | +#endif +#ifdef CONFIG_SYS_SPCR_TSEC2EP /* TSEC2 Emergency priority */ + (SPCR_TSEC2EP << SPCR_TSEC2EP_SHIFT) | +#endif + 0; + __be32 spcr_val = +#ifdef CONFIG_SYS_SPCR_OPT + (CONFIG_SYS_SPCR_OPT << SPCR_OPT_SHIFT) | +#endif +#ifdef CONFIG_SYS_SPCR_TSECEP /* all eTSEC's Emergency priority */ + (CONFIG_SYS_SPCR_TSECEP << SPCR_TSECEP_SHIFT) | +#endif +#ifdef CONFIG_SYS_SPCR_TSEC1EP /* TSEC1 Emergency priority */ + (CONFIG_SYS_SPCR_TSEC1EP << SPCR_TSEC1EP_SHIFT) | +#endif +#ifdef CONFIG_SYS_SPCR_TSEC2EP /* TSEC2 Emergency priority */ + (CONFIG_SYS_SPCR_TSEC2EP << SPCR_TSEC2EP_SHIFT) | +#endif + 0; + __be32 sccr_mask = +#ifdef CONFIG_SYS_SCCR_ENCCM /* Encryption clock mode */ + (SCCR_ENCCM << SCCR_ENCCM_SHIFT) | +#endif +#ifdef CONFIG_SYS_SCCR_PCICM /* PCI & DMA clock mode */ + (SCCR_PCICM << SCCR_PCICM_SHIFT) | +#endif +#ifdef CONFIG_SYS_SCCR_TSECCM /* all TSEC's clock mode */ + (SCCR_TSECCM << SCCR_TSECCM_SHIFT) | +#endif +#ifdef CONFIG_SYS_SCCR_TSEC1CM /* TSEC1 clock mode */ + (SCCR_TSEC1CM << SCCR_TSEC1CM_SHIFT) | +#endif +#ifdef CONFIG_SYS_SCCR_TSEC2CM /* TSEC2 clock mode */ + (SCCR_TSEC2CM << SCCR_TSEC2CM_SHIFT) | +#endif +#ifdef CONFIG_SYS_SCCR_TSEC1ON /* TSEC1 clock switch */ + (SCCR_TSEC1ON << SCCR_TSEC1ON_SHIFT) | +#endif +#ifdef CONFIG_SYS_SCCR_TSEC2ON /* TSEC2 clock switch */ + (SCCR_TSEC2ON << SCCR_TSEC2ON_SHIFT) | +#endif +#ifdef CONFIG_SYS_SCCR_USBMPHCM /* USB MPH clock mode */ + (SCCR_USBMPHCM << SCCR_USBMPHCM_SHIFT) | +#endif +#ifdef CONFIG_SYS_SCCR_USBDRCM /* USB DR clock mode */ + (SCCR_USBDRCM << SCCR_USBDRCM_SHIFT) | +#endif +#ifdef CONFIG_SYS_SCCR_SATACM /* SATA controller clock mode */ + (SCCR_SATACM << SCCR_SATACM_SHIFT) | +#endif + 0; + __be32 sccr_val = +#ifdef CONFIG_SYS_SCCR_ENCCM /* Encryption clock mode */ + (CONFIG_SYS_SCCR_ENCCM << SCCR_ENCCM_SHIFT) | +#endif +#ifdef CONFIG_SYS_SCCR_PCICM /* PCI & DMA clock mode */ + (CONFIG_SYS_SCCR_PCICM << SCCR_PCICM_SHIFT) | +#endif +#ifdef CONFIG_SYS_SCCR_TSECCM /* all TSEC's clock mode */ + (CONFIG_SYS_SCCR_TSECCM << SCCR_TSECCM_SHIFT) | +#endif +#ifdef CONFIG_SYS_SCCR_TSEC1CM /* TSEC1 clock mode */ + (CONFIG_SYS_SCCR_TSEC1CM << SCCR_TSEC1CM_SHIFT) | +#endif +#ifdef CONFIG_SYS_SCCR_TSEC2CM /* TSEC2 clock mode */ + (CONFIG_SYS_SCCR_TSEC2CM << SCCR_TSEC2CM_SHIFT) | +#endif +#ifdef CONFIG_SYS_SCCR_TSEC1ON /* TSEC1 clock switch */ + (CONFIG_SYS_SCCR_TSEC1ON << SCCR_TSEC1ON_SHIFT) | +#endif +#ifdef CONFIG_SYS_SCCR_TSEC2ON /* TSEC2 clock switch */ + (CONFIG_SYS_SCCR_TSEC2ON << SCCR_TSEC2ON_SHIFT) | +#endif +#ifdef CONFIG_SYS_SCCR_USBMPHCM /* USB MPH clock mode */ + (CONFIG_SYS_SCCR_USBMPHCM << SCCR_USBMPHCM_SHIFT) | +#endif +#ifdef CONFIG_SYS_SCCR_USBDRCM /* USB DR clock mode */ + (CONFIG_SYS_SCCR_USBDRCM << SCCR_USBDRCM_SHIFT) | +#endif +#ifdef CONFIG_SYS_SCCR_SATACM /* SATA controller clock mode */ + (CONFIG_SYS_SCCR_SATACM << SCCR_SATACM_SHIFT) | +#endif + 0; + __be32 lcrr_mask = +#ifdef CONFIG_SYS_LCRR_DBYP /* PLL bypass */ + LCRR_DBYP | +#endif +#ifdef CONFIG_SYS_LCRR_EADC /* external address delay */ + LCRR_EADC | +#endif +#ifdef CONFIG_SYS_LCRR_CLKDIV /* system clock divider */ + LCRR_CLKDIV | +#endif + 0; + __be32 lcrr_val = +#ifdef CONFIG_SYS_LCRR_DBYP /* PLL bypass */ + CONFIG_SYS_LCRR_DBYP | +#endif +#ifdef CONFIG_SYS_LCRR_EADC + CONFIG_SYS_LCRR_EADC | +#endif +#ifdef CONFIG_SYS_LCRR_CLKDIV /* system clock divider */ + CONFIG_SYS_LCRR_CLKDIV | +#endif + 0; + + /* Pointer is writable since we allocated a register for it */ + gd = (gd_t *) (CONFIG_SYS_INIT_RAM_ADDR + CONFIG_SYS_GBL_DATA_OFFSET); + + /* Clear initial global data */ + memset ((void *) gd, 0, sizeof (gd_t)); + + /* system performance tweaking */ + clrsetbits_be32(&im->arbiter.acr, acr_mask, acr_val); + + clrsetbits_be32(&im->sysconf.spcr, spcr_mask, spcr_val); + + clrsetbits_be32(&im->clk.sccr, sccr_mask, sccr_val); + + /* RSR - Reset Status Register - clear all status (4.6.1.3) */ + gd->reset_status = __raw_readl(&im->reset.rsr); + __raw_writel(~(RSR_RES), &im->reset.rsr); + + /* AER - Arbiter Event Register - store status */ + gd->arbiter_event_attributes = __raw_readl(&im->arbiter.aeatr); + gd->arbiter_event_address = __raw_readl(&im->arbiter.aeadr); + + /* + * RMR - Reset Mode Register + * contains checkstop reset enable (4.6.1.4) + */ + __raw_writel(RMR_CSRE & (1<<RMR_CSRE_SHIFT), &im->reset.rmr); + + /* LCRR - Clock Ratio Register (10.3.1.16) + * write, read, and isync per MPC8379ERM rev.1 CLKDEV field description + */ + clrsetbits_be32(&im->lbus.lcrr, lcrr_mask, lcrr_val); + __raw_readl(&im->lbus.lcrr); + isync(); + + /* Enable Time Base & Decrementer ( so we will have udelay() )*/ + setbits_be32(&im->sysconf.spcr, SPCR_TBEN); + + /* System General Purpose Register */ +#ifdef CONFIG_SYS_SICRH +#if defined(CONFIG_MPC834x) || defined(CONFIG_MPC8313) + /* regarding to MPC34x manual rev.1 bits 28..29 must be preserved */ + __raw_writel((im->sysconf.sicrh & 0x0000000C) | CONFIG_SYS_SICRH, + &im->sysconf.sicrh); +#else + __raw_writel(CONFIG_SYS_SICRH, &im->sysconf.sicrh); +#endif +#endif +#ifdef CONFIG_SYS_SICRL + __raw_writel(CONFIG_SYS_SICRL, &im->sysconf.sicrl); +#endif +#ifdef CONFIG_SYS_DDRCDR /* DDR control driver register */ + __raw_writel(CONFIG_SYS_DDRCDR, &im->sysconf.ddrcdr); +#endif +#ifdef CONFIG_SYS_OBIR /* Output buffer impedance register */ + __raw_writel(CONFIG_SYS_OBIR, &im->sysconf.obir); +#endif + +#ifdef CONFIG_QE + /* Config QE ioports */ + config_qe_ioports(); +#endif + + /* + * Memory Controller: + */ + + /* Map banks 0 and 1 to the FLASH banks 0 and 1 at preliminary + * addresses - these have to be modified later when FLASH size + * has been determined + */ + +#if defined(CONFIG_SYS_BR0_PRELIM) \ + && defined(CONFIG_SYS_OR0_PRELIM) \ + && defined(CONFIG_SYS_LBLAWBAR0_PRELIM) \ + && defined(CONFIG_SYS_LBLAWAR0_PRELIM) + im->lbus.bank[0].br = CONFIG_SYS_BR0_PRELIM; + im->lbus.bank[0].or = CONFIG_SYS_OR0_PRELIM; + im->sysconf.lblaw[0].bar = CONFIG_SYS_LBLAWBAR0_PRELIM; + im->sysconf.lblaw[0].ar = CONFIG_SYS_LBLAWAR0_PRELIM; +#else +#error CONFIG_SYS_BR0_PRELIM, CONFIG_SYS_OR0_PRELIM, CONFIG_SYS_LBLAWBAR0_PRELIM & CONFIG_SYS_LBLAWAR0_PRELIM must be defined +#endif + +#if defined(CONFIG_SYS_BR1_PRELIM) && defined(CONFIG_SYS_OR1_PRELIM) + im->lbus.bank[1].br = CONFIG_SYS_BR1_PRELIM; + im->lbus.bank[1].or = CONFIG_SYS_OR1_PRELIM; +#endif +#if defined(CONFIG_SYS_LBLAWBAR1_PRELIM) && defined(CONFIG_SYS_LBLAWAR1_PRELIM) + im->sysconf.lblaw[1].bar = CONFIG_SYS_LBLAWBAR1_PRELIM; + im->sysconf.lblaw[1].ar = CONFIG_SYS_LBLAWAR1_PRELIM; +#endif +#if defined(CONFIG_SYS_BR2_PRELIM) && defined(CONFIG_SYS_OR2_PRELIM) + im->lbus.bank[2].br = CONFIG_SYS_BR2_PRELIM; + im->lbus.bank[2].or = CONFIG_SYS_OR2_PRELIM; +#endif +#if defined(CONFIG_SYS_LBLAWBAR2_PRELIM) && defined(CONFIG_SYS_LBLAWAR2_PRELIM) + im->sysconf.lblaw[2].bar = CONFIG_SYS_LBLAWBAR2_PRELIM; + im->sysconf.lblaw[2].ar = CONFIG_SYS_LBLAWAR2_PRELIM; +#endif +#if defined(CONFIG_SYS_BR3_PRELIM) && defined(CONFIG_SYS_OR3_PRELIM) + im->lbus.bank[3].br = CONFIG_SYS_BR3_PRELIM; + im->lbus.bank[3].or = CONFIG_SYS_OR3_PRELIM; +#endif +#if defined(CONFIG_SYS_LBLAWBAR3_PRELIM) && defined(CONFIG_SYS_LBLAWAR3_PRELIM) + im->sysconf.lblaw[3].bar = CONFIG_SYS_LBLAWBAR3_PRELIM; + im->sysconf.lblaw[3].ar = CONFIG_SYS_LBLAWAR3_PRELIM; +#endif +#if defined(CONFIG_SYS_BR4_PRELIM) && defined(CONFIG_SYS_OR4_PRELIM) + im->lbus.bank[4].br = CONFIG_SYS_BR4_PRELIM; + im->lbus.bank[4].or = CONFIG_SYS_OR4_PRELIM; +#endif +#if defined(CONFIG_SYS_LBLAWBAR4_PRELIM) && defined(CONFIG_SYS_LBLAWAR4_PRELIM) + im->sysconf.lblaw[4].bar = CONFIG_SYS_LBLAWBAR4_PRELIM; + im->sysconf.lblaw[4].ar = CONFIG_SYS_LBLAWAR4_PRELIM; +#endif +#if defined(CONFIG_SYS_BR5_PRELIM) && defined(CONFIG_SYS_OR5_PRELIM) + im->lbus.bank[5].br = CONFIG_SYS_BR5_PRELIM; + im->lbus.bank[5].or = CONFIG_SYS_OR5_PRELIM; +#endif +#if defined(CONFIG_SYS_LBLAWBAR5_PRELIM) && defined(CONFIG_SYS_LBLAWAR5_PRELIM) + im->sysconf.lblaw[5].bar = CONFIG_SYS_LBLAWBAR5_PRELIM; + im->sysconf.lblaw[5].ar = CONFIG_SYS_LBLAWAR5_PRELIM; +#endif +#if defined(CONFIG_SYS_BR6_PRELIM) && defined(CONFIG_SYS_OR6_PRELIM) + im->lbus.bank[6].br = CONFIG_SYS_BR6_PRELIM; + im->lbus.bank[6].or = CONFIG_SYS_OR6_PRELIM; +#endif +#if defined(CONFIG_SYS_LBLAWBAR6_PRELIM) && defined(CONFIG_SYS_LBLAWAR6_PRELIM) + im->sysconf.lblaw[6].bar = CONFIG_SYS_LBLAWBAR6_PRELIM; + im->sysconf.lblaw[6].ar = CONFIG_SYS_LBLAWAR6_PRELIM; +#endif +#if defined(CONFIG_SYS_BR7_PRELIM) && defined(CONFIG_SYS_OR7_PRELIM) + im->lbus.bank[7].br = CONFIG_SYS_BR7_PRELIM; + im->lbus.bank[7].or = CONFIG_SYS_OR7_PRELIM; +#endif +#if defined(CONFIG_SYS_LBLAWBAR7_PRELIM) && defined(CONFIG_SYS_LBLAWAR7_PRELIM) + im->sysconf.lblaw[7].bar = CONFIG_SYS_LBLAWBAR7_PRELIM; + im->sysconf.lblaw[7].ar = CONFIG_SYS_LBLAWAR7_PRELIM; +#endif +#ifdef CONFIG_SYS_GPIO1_PRELIM + im->gpio[0].dat = CONFIG_SYS_GPIO1_DAT; + im->gpio[0].dir = CONFIG_SYS_GPIO1_DIR; +#endif +#ifdef CONFIG_SYS_GPIO2_PRELIM + im->gpio[1].dat = CONFIG_SYS_GPIO2_DAT; + im->gpio[1].dir = CONFIG_SYS_GPIO2_DIR; +#endif +#ifdef CONFIG_USB_EHCI_FSL +#ifndef CONFIG_MPC834x + uint32_t temp; + struct usb_ehci *ehci = (struct usb_ehci *)CONFIG_SYS_MPC8xxx_USB_ADDR; + + /* Configure interface. */ + setbits_be32(&ehci->control, REFSEL_16MHZ | UTMI_PHY_EN); + + /* Wait for clock to stabilize */ + do { + temp = __raw_readl(&ehci->control); + udelay(1000); + } while (!(temp & PHY_CLK_VALID)); +#endif +#endif +} + +int cpu_init_r (void) +{ +#ifdef CONFIG_QE + uint qe_base = CONFIG_SYS_IMMR + 0x00100000; /* QE immr base */ + + qe_init(qe_base); + qe_reset(); +#endif + return 0; +} + +/* + * Print out the bus arbiter event + */ +#if defined(CONFIG_DISPLAY_AER_FULL) +static int print_83xx_arb_event(int force) +{ + static char* event[] = { + "Address Time Out", + "Data Time Out", + "Address Only Transfer Type", + "External Control Word Transfer Type", + "Reserved Transfer Type", + "Transfer Error", + "reserved", + "reserved" + }; + static char* master[] = { + "e300 Core Data Transaction", + "reserved", + "e300 Core Instruction Fetch", + "reserved", + "TSEC1", + "TSEC2", + "USB MPH", + "USB DR", + "Encryption Core", + "I2C Boot Sequencer", + "JTAG", + "reserved", + "eSDHC", + "PCI1", + "PCI2", + "DMA", + "QUICC Engine 00", + "QUICC Engine 01", + "QUICC Engine 10", + "QUICC Engine 11", + "reserved", + "reserved", + "reserved", + "reserved", + "SATA1", + "SATA2", + "SATA3", + "SATA4", + "reserved", + "PCI Express 1", + "PCI Express 2", + "TDM-DMAC" + }; + static char *transfer[] = { + "Address-only, Clean Block", + "Address-only, lwarx reservation set", + "Single-beat or Burst write", + "reserved", + "Address-only, Flush Block", + "reserved", + "Burst write", + "reserved", + "Address-only, sync", + "Address-only, tlbsync", + "Single-beat or Burst read", + "Single-beat or Burst read", + "Address-only, Kill Block", + "Address-only, icbi", + "Burst read", + "reserved", + "Address-only, eieio", + "reserved", + "Single-beat write", + "reserved", + "ecowx - Illegal single-beat write", + "reserved", + "reserved", + "reserved", + "Address-only, TLB Invalidate", + "reserved", + "Single-beat or Burst read", + "reserved", + "eciwx - Illegal single-beat read", + "reserved", + "Burst read", + "reserved" + }; + + int etype = (gd->arbiter_event_attributes & AEATR_EVENT) + >> AEATR_EVENT_SHIFT; + int mstr_id = (gd->arbiter_event_attributes & AEATR_MSTR_ID) + >> AEATR_MSTR_ID_SHIFT; + int tbst = (gd->arbiter_event_attributes & AEATR_TBST) + >> AEATR_TBST_SHIFT; + int tsize = (gd->arbiter_event_attributes & AEATR_TSIZE) + >> AEATR_TSIZE_SHIFT; + int ttype = (gd->arbiter_event_attributes & AEATR_TTYPE) + >> AEATR_TTYPE_SHIFT; + + if (!force && !gd->arbiter_event_address) + return 0; + + puts("Arbiter Event Status:\n"); + printf(" Event Address: 0x%08lX\n", gd->arbiter_event_address); + printf(" Event Type: 0x%1x = %s\n", etype, event[etype]); + printf(" Master ID: 0x%02x = %s\n", mstr_id, master[mstr_id]); + printf(" Transfer Size: 0x%1x = %d bytes\n", (tbst<<3) | tsize, + tbst ? (tsize ? tsize : 8) : 16 + 8 * tsize); + printf(" Transfer Type: 0x%02x = %s\n", ttype, transfer[ttype]); + + return gd->arbiter_event_address; +} + +#elif defined(CONFIG_DISPLAY_AER_BRIEF) + +static int print_83xx_arb_event(int force) +{ + if (!force && !gd->arbiter_event_address) + return 0; + + printf("Arbiter Event Status: AEATR=0x%08lX, AEADR=0x%08lX\n", + gd->arbiter_event_attributes, gd->arbiter_event_address); + + return gd->arbiter_event_address; +} +#endif /* CONFIG_DISPLAY_AER_xxxx */ + +/* + * Figure out the cause of the reset + */ +int prt_83xx_rsr(void) +{ + static struct { + ulong mask; + char *desc; + } bits[] = { + { + RSR_SWSR, "Software Soft"}, { + RSR_SWHR, "Software Hard"}, { + RSR_JSRS, "JTAG Soft"}, { + RSR_CSHR, "Check Stop"}, { + RSR_SWRS, "Software Watchdog"}, { + RSR_BMRS, "Bus Monitor"}, { + RSR_SRS, "External/Internal Soft"}, { + RSR_HRS, "External/Internal Hard"} + }; + static int n = sizeof bits / sizeof bits[0]; + ulong rsr = gd->reset_status; + int i; + char *sep; + + puts("Reset Status:"); + + sep = " "; + for (i = 0; i < n; i++) + if (rsr & bits[i].mask) { + printf("%s%s", sep, bits[i].desc); + sep = ", "; + } + puts("\n"); + +#if defined(CONFIG_DISPLAY_AER_FULL) || defined(CONFIG_DISPLAY_AER_BRIEF) + print_83xx_arb_event(rsr & RSR_BMRS); +#endif + puts("\n"); + + return 0; +} diff --git a/arch/powerpc/cpu/mpc83xx/ecc.c b/arch/powerpc/cpu/mpc83xx/ecc.c new file mode 100644 index 0000000..f3942b4 --- /dev/null +++ b/arch/powerpc/cpu/mpc83xx/ecc.c @@ -0,0 +1,390 @@ +/* + * Copyright (C) 2007 Freescale Semiconductor, Inc. + * + * Dave Liu <daveliu@freescale.com> + * based on the contribution of Marian Balakowicz <m8@semihalf.com> + * + * 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. + */ + +#include <common.h> +#include <mpc83xx.h> +#include <command.h> + +#if defined(CONFIG_DDR_ECC) && defined(CONFIG_DDR_ECC_CMD) +void ecc_print_status(void) +{ + volatile immap_t *immap = (immap_t *) CONFIG_SYS_IMMR; + volatile ddr83xx_t *ddr = &immap->ddr; + + printf("\nECC mode: %s\n\n", + (ddr->sdram_cfg & SDRAM_CFG_ECC_EN) ? "ON" : "OFF"); + + /* Interrupts */ + printf("Memory Error Interrupt Enable:\n"); + printf(" Multiple-Bit Error Interrupt Enable: %d\n", + (ddr->err_int_en & ECC_ERR_INT_EN_MBEE) ? 1 : 0); + printf(" Single-Bit Error Interrupt Enable: %d\n", + (ddr->err_int_en & ECC_ERR_INT_EN_SBEE) ? 1 : 0); + printf(" Memory Select Error Interrupt Enable: %d\n\n", + (ddr->err_int_en & ECC_ERR_INT_EN_MSEE) ? 1 : 0); + + /* Error disable */ + printf("Memory Error Disable:\n"); + printf(" Multiple-Bit Error Disable: %d\n", + (ddr->err_disable & ECC_ERROR_DISABLE_MBED) ? 1 : 0); + printf(" Sinle-Bit Error Disable: %d\n", + (ddr->err_disable & ECC_ERROR_DISABLE_SBED) ? 1 : 0); + printf(" Memory Select Error Disable: %d\n\n", + (ddr->err_disable & ECC_ERROR_DISABLE_MSED) ? 1 : 0); + + /* Error injection */ + printf("Memory Data Path Error Injection Mask High/Low: %08x %08x\n", + ddr->data_err_inject_hi, ddr->data_err_inject_lo); + + printf("Memory Data Path Error Injection Mask ECC:\n"); + printf(" ECC Mirror Byte: %d\n", + (ddr->ecc_err_inject & ECC_ERR_INJECT_EMB) ? 1 : 0); + printf(" ECC Injection Enable: %d\n", + (ddr->ecc_err_inject & ECC_ERR_INJECT_EIEN) ? 1 : 0); + printf(" ECC Error Injection Mask: 0x%02x\n\n", + ddr->ecc_err_inject & ECC_ERR_INJECT_EEIM); + + /* SBE counter/threshold */ + printf("Memory Single-Bit Error Management (0..255):\n"); + printf(" Single-Bit Error Threshold: %d\n", + (ddr->err_sbe & ECC_ERROR_MAN_SBET) >> ECC_ERROR_MAN_SBET_SHIFT); + printf(" Single-Bit Error Counter: %d\n\n", + (ddr->err_sbe & ECC_ERROR_MAN_SBEC) >> ECC_ERROR_MAN_SBEC_SHIFT); + + /* Error detect */ + printf("Memory Error Detect:\n"); + printf(" Multiple Memory Errors: %d\n", + (ddr->err_detect & ECC_ERROR_DETECT_MME) ? 1 : 0); + printf(" Multiple-Bit Error: %d\n", + (ddr->err_detect & ECC_ERROR_DETECT_MBE) ? 1 : 0); + printf(" Single-Bit Error: %d\n", + (ddr->err_detect & ECC_ERROR_DETECT_SBE) ? 1 : 0); + printf(" Memory Select Error: %d\n\n", + (ddr->err_detect & ECC_ERROR_DETECT_MSE) ? 1 : 0); + + /* Capture data */ + printf("Memory Error Address Capture: 0x%08x\n", ddr->capture_address); + printf("Memory Data Path Read Capture High/Low: %08x %08x\n", + ddr->capture_data_hi, ddr->capture_data_lo); + printf("Memory Data Path Read Capture ECC: 0x%02x\n\n", + ddr->capture_ecc & CAPTURE_ECC_ECE); + + printf("Memory Error Attributes Capture:\n"); + printf(" Data Beat Number: %d\n", + (ddr->capture_attributes & ECC_CAPT_ATTR_BNUM) >> + ECC_CAPT_ATTR_BNUM_SHIFT); + printf(" Transaction Size: %d\n", + (ddr->capture_attributes & ECC_CAPT_ATTR_TSIZ) >> + ECC_CAPT_ATTR_TSIZ_SHIFT); + printf(" Transaction Source: %d\n", + (ddr->capture_attributes & ECC_CAPT_ATTR_TSRC) >> + ECC_CAPT_ATTR_TSRC_SHIFT); + printf(" Transaction Type: %d\n", + (ddr->capture_attributes & ECC_CAPT_ATTR_TTYP) >> + ECC_CAPT_ATTR_TTYP_SHIFT); + printf(" Error Information Valid: %d\n\n", + ddr->capture_attributes & ECC_CAPT_ATTR_VLD); +} + +int do_ecc(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[]) +{ + volatile immap_t *immap = (immap_t *) CONFIG_SYS_IMMR; + volatile ddr83xx_t *ddr = &immap->ddr; + volatile u32 val; + u64 *addr; + u32 count; + register u64 *i; + u32 ret[2]; + u32 pattern[2]; + u32 writeback[2]; + + /* The pattern is written into memory to generate error */ + pattern[0] = 0xfedcba98UL; + pattern[1] = 0x76543210UL; + + /* After injecting error, re-initialize the memory with the value */ + writeback[0] = 0x01234567UL; + writeback[1] = 0x89abcdefUL; + + if (argc > 4) { + cmd_usage(cmdtp); + return 1; + } + + if (argc == 2) { + if (strcmp(argv[1], "status") == 0) { + ecc_print_status(); + return 0; + } else if (strcmp(argv[1], "captureclear") == 0) { + ddr->capture_address = 0; + ddr->capture_data_hi = 0; + ddr->capture_data_lo = 0; + ddr->capture_ecc = 0; + ddr->capture_attributes = 0; + return 0; + } + } + if (argc == 3) { + if (strcmp(argv[1], "sbecnt") == 0) { + val = simple_strtoul(argv[2], NULL, 10); + if (val > 255) { + printf("Incorrect Counter value, " + "should be 0..255\n"); + return 1; + } + + val = (val << ECC_ERROR_MAN_SBEC_SHIFT); + val |= (ddr->err_sbe & ECC_ERROR_MAN_SBET); + + ddr->err_sbe = val; + return 0; + } else if (strcmp(argv[1], "sbethr") == 0) { + val = simple_strtoul(argv[2], NULL, 10); + if (val > 255) { + printf("Incorrect Counter value, " + "should be 0..255\n"); + return 1; + } + + val = (val << ECC_ERROR_MAN_SBET_SHIFT); + val |= (ddr->err_sbe & ECC_ERROR_MAN_SBEC); + + ddr->err_sbe = val; + return 0; + } else if (strcmp(argv[1], "errdisable") == 0) { + val = ddr->err_disable; + + if (strcmp(argv[2], "+sbe") == 0) { + val |= ECC_ERROR_DISABLE_SBED; + } else if (strcmp(argv[2], "+mbe") == 0) { + val |= ECC_ERROR_DISABLE_MBED; + } else if (strcmp(argv[2], "+mse") == 0) { + val |= ECC_ERROR_DISABLE_MSED; + } else if (strcmp(argv[2], "+all") == 0) { + val |= (ECC_ERROR_DISABLE_SBED | + ECC_ERROR_DISABLE_MBED | + ECC_ERROR_DISABLE_MSED); + } else if (strcmp(argv[2], "-sbe") == 0) { + val &= ~ECC_ERROR_DISABLE_SBED; + } else if (strcmp(argv[2], "-mbe") == 0) { + val &= ~ECC_ERROR_DISABLE_MBED; + } else if (strcmp(argv[2], "-mse") == 0) { + val &= ~ECC_ERROR_DISABLE_MSED; + } else if (strcmp(argv[2], "-all") == 0) { + val &= ~(ECC_ERROR_DISABLE_SBED | + ECC_ERROR_DISABLE_MBED | + ECC_ERROR_DISABLE_MSED); + } else { + printf("Incorrect err_disable field\n"); + return 1; + } + + ddr->err_disable = val; + __asm__ __volatile__("sync"); + __asm__ __volatile__("isync"); + return 0; + } else if (strcmp(argv[1], "errdetectclr") == 0) { + val = ddr->err_detect; + + if (strcmp(argv[2], "mme") == 0) { + val |= ECC_ERROR_DETECT_MME; + } else if (strcmp(argv[2], "sbe") == 0) { + val |= ECC_ERROR_DETECT_SBE; + } else if (strcmp(argv[2], "mbe") == 0) { + val |= ECC_ERROR_DETECT_MBE; + } else if (strcmp(argv[2], "mse") == 0) { + val |= ECC_ERROR_DETECT_MSE; + } else if (strcmp(argv[2], "all") == 0) { + val |= (ECC_ERROR_DETECT_MME | + ECC_ERROR_DETECT_MBE | + ECC_ERROR_DETECT_SBE | + ECC_ERROR_DETECT_MSE); + } else { + printf("Incorrect err_detect field\n"); + return 1; + } + + ddr->err_detect = val; + return 0; + } else if (strcmp(argv[1], "injectdatahi") == 0) { + val = simple_strtoul(argv[2], NULL, 16); + + ddr->data_err_inject_hi = val; + return 0; + } else if (strcmp(argv[1], "injectdatalo") == 0) { + val = simple_strtoul(argv[2], NULL, 16); + + ddr->data_err_inject_lo = val; + return 0; + } else if (strcmp(argv[1], "injectecc") == 0) { + val = simple_strtoul(argv[2], NULL, 16); + if (val > 0xff) { + printf("Incorrect ECC inject mask, " + "should be 0x00..0xff\n"); + return 1; + } + val |= (ddr->ecc_err_inject & ~ECC_ERR_INJECT_EEIM); + + ddr->ecc_err_inject = val; + return 0; + } else if (strcmp(argv[1], "inject") == 0) { + val = ddr->ecc_err_inject; + + if (strcmp(argv[2], "en") == 0) + val |= ECC_ERR_INJECT_EIEN; + else if (strcmp(argv[2], "dis") == 0) + val &= ~ECC_ERR_INJECT_EIEN; + else + printf("Incorrect command\n"); + + ddr->ecc_err_inject = val; + __asm__ __volatile__("sync"); + __asm__ __volatile__("isync"); + return 0; + } else if (strcmp(argv[1], "mirror") == 0) { + val = ddr->ecc_err_inject; + + if (strcmp(argv[2], "en") == 0) + val |= ECC_ERR_INJECT_EMB; + else if (strcmp(argv[2], "dis") == 0) + val &= ~ECC_ERR_INJECT_EMB; + else + printf("Incorrect command\n"); + + ddr->ecc_err_inject = val; + return 0; + } + } + if (argc == 4) { + if (strcmp(argv[1], "testdw") == 0) { + addr = (u64 *) simple_strtoul(argv[2], NULL, 16); + count = simple_strtoul(argv[3], NULL, 16); + + if ((u32) addr % 8) { + printf("Address not alligned on " + "double word boundary\n"); + return 1; + } + disable_interrupts(); + + for (i = addr; i < addr + count; i++) { + + /* enable injects */ + ddr->ecc_err_inject |= ECC_ERR_INJECT_EIEN; + __asm__ __volatile__("sync"); + __asm__ __volatile__("isync"); + + /* write memory location injecting errors */ + ppcDWstore((u32 *) i, pattern); + __asm__ __volatile__("sync"); + + /* disable injects */ + ddr->ecc_err_inject &= ~ECC_ERR_INJECT_EIEN; + __asm__ __volatile__("sync"); + __asm__ __volatile__("isync"); + + /* read data, this generates ECC error */ + ppcDWload((u32 *) i, ret); + __asm__ __volatile__("sync"); + + /* re-initialize memory, double word write the location again, + * generates new ECC code this time */ + ppcDWstore((u32 *) i, writeback); + __asm__ __volatile__("sync"); + } + enable_interrupts(); + return 0; + } + if (strcmp(argv[1], "testword") == 0) { + addr = (u64 *) simple_strtoul(argv[2], NULL, 16); + count = simple_strtoul(argv[3], NULL, 16); + + if ((u32) addr % 8) { + printf("Address not alligned on " + "double word boundary\n"); + return 1; + } + disable_interrupts(); + + for (i = addr; i < addr + count; i++) { + + /* enable injects */ + ddr->ecc_err_inject |= ECC_ERR_INJECT_EIEN; + __asm__ __volatile__("sync"); + __asm__ __volatile__("isync"); + + /* write memory location injecting errors */ + *(u32 *) i = 0xfedcba98UL; + __asm__ __volatile__("sync"); + + /* sub double word write, + * bus will read-modify-write, + * generates ECC error */ + *((u32 *) i + 1) = 0x76543210UL; + __asm__ __volatile__("sync"); + + /* disable injects */ + ddr->ecc_err_inject &= ~ECC_ERR_INJECT_EIEN; + __asm__ __volatile__("sync"); + __asm__ __volatile__("isync"); + + /* re-initialize memory, + * double word write the location again, + * generates new ECC code this time */ + ppcDWstore((u32 *) i, writeback); + __asm__ __volatile__("sync"); + } + enable_interrupts(); + return 0; + } + } + cmd_usage(cmdtp); + return 1; +} + +U_BOOT_CMD(ecc, 4, 0, do_ecc, + "support for DDR ECC features", + "status - print out status info\n" + "ecc captureclear - clear capture regs data\n" + "ecc sbecnt <val> - set Single-Bit Error counter\n" + "ecc sbethr <val> - set Single-Bit Threshold\n" + "ecc errdisable <flag> - clear/set disable Memory Error Disable, flag:\n" + " [-|+]sbe - Single-Bit Error\n" + " [-|+]mbe - Multiple-Bit Error\n" + " [-|+]mse - Memory Select Error\n" + " [-|+]all - all errors\n" + "ecc errdetectclr <flag> - clear Memory Error Detect, flag:\n" + " mme - Multiple Memory Errors\n" + " sbe - Single-Bit Error\n" + " mbe - Multiple-Bit Error\n" + " mse - Memory Select Error\n" + " all - all errors\n" + "ecc injectdatahi <hi> - set Memory Data Path Error Injection Mask High\n" + "ecc injectdatalo <lo> - set Memory Data Path Error Injection Mask Low\n" + "ecc injectecc <ecc> - set ECC Error Injection Mask\n" + "ecc inject <en|dis> - enable/disable error injection\n" + "ecc mirror <en|dis> - enable/disable mirror byte\n" + "ecc testdw <addr> <cnt> - test mem region with double word access:\n" + " - enables injects\n" + " - writes pattern injecting errors with double word access\n" + " - disables injects\n" + " - reads pattern back with double word access, generates error\n" + " - re-inits memory\n" + "ecc testword <addr> <cnt> - test mem region with word access:\n" + " - enables injects\n" + " - writes pattern injecting errors with word access\n" + " - writes pattern with word access, generates error\n" + " - disables injects\n" " - re-inits memory"); +#endif diff --git a/arch/powerpc/cpu/mpc83xx/fdt.c b/arch/powerpc/cpu/mpc83xx/fdt.c new file mode 100644 index 0000000..daf73a6 --- /dev/null +++ b/arch/powerpc/cpu/mpc83xx/fdt.c @@ -0,0 +1,143 @@ +/* + * Copyright 2007 Freescale Semiconductor, Inc. + * + * (C) Copyright 2000 + * Wolfgang Denk, DENX Software Engineering, wd@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> +#include <libfdt.h> +#include <fdt_support.h> +#include <asm/processor.h> + +extern void ft_qe_setup(void *blob); + +DECLARE_GLOBAL_DATA_PTR; + +#if defined(CONFIG_BOOTCOUNT_LIMIT) && defined(CONFIG_MPC8360) +#include <asm/immap_qe.h> + +void fdt_fixup_muram (void *blob) +{ + ulong data[2]; + + data[0] = 0; + data[1] = QE_MURAM_SIZE - 2 * sizeof(unsigned long); + do_fixup_by_compat(blob, "fsl,qe-muram-data", "reg", + data, sizeof (data), 0); +} +#endif + +void ft_cpu_setup(void *blob, bd_t *bd) +{ + immap_t *immr = (immap_t *)CONFIG_SYS_IMMR; + int spridr = immr->sysconf.spridr; + + /* + * delete crypto node if not on an E-processor + * initial revisions of the MPC834xE/6xE have the original SEC 2.0. + * EA revisions got the SEC uprevved to 2.4 but since the default device + * tree contains SEC 2.0 properties we uprev them here. + */ + if (!IS_E_PROCESSOR(spridr)) + fdt_fixup_crypto_node(blob, 0); + else if (IS_E_PROCESSOR(spridr) && + (SPR_FAMILY(spridr) == SPR_834X_FAMILY || + SPR_FAMILY(spridr) == SPR_836X_FAMILY) && + REVID_MAJOR(spridr) >= 2) + fdt_fixup_crypto_node(blob, 0x0204); + +#if defined(CONFIG_HAS_ETH0) || defined(CONFIG_HAS_ETH1) ||\ + defined(CONFIG_HAS_ETH2) || defined(CONFIG_HAS_ETH3) ||\ + defined(CONFIG_HAS_ETH4) || defined(CONFIG_HAS_ETH5) + fdt_fixup_ethernet(blob); +#ifdef CONFIG_MPC8313 + /* + * mpc8313e erratum IPIC1 swapped TSEC interrupt ID numbers on rev. 1 + * h/w (see AN3545). The base device tree in use has rev. 1 ID numbers, + * so if on Rev. 2 (and higher) h/w, we fix them up here + */ + if (REVID_MAJOR(immr->sysconf.spridr) >= 2) { + int nodeoffset, path; + const char *prop; + + nodeoffset = fdt_path_offset(blob, "/aliases"); + if (nodeoffset >= 0) { +#if defined(CONFIG_HAS_ETH0) + prop = fdt_getprop(blob, nodeoffset, "ethernet0", NULL); + if (prop) { + u32 tmp[] = { 32, 0x8, 33, 0x8, 34, 0x8 }; + + path = fdt_path_offset(blob, prop); + prop = fdt_getprop(blob, path, "interrupts", 0); + if (prop) + fdt_setprop(blob, path, "interrupts", + &tmp, sizeof(tmp)); + } +#endif +#if defined(CONFIG_HAS_ETH1) + prop = fdt_getprop(blob, nodeoffset, "ethernet1", NULL); + if (prop) { + u32 tmp[] = { 35, 0x8, 36, 0x8, 37, 0x8 }; + + path = fdt_path_offset(blob, prop); + prop = fdt_getprop(blob, path, "interrupts", 0); + if (prop) + fdt_setprop(blob, path, "interrupts", + &tmp, sizeof(tmp)); + } +#endif + } + } +#endif +#endif + + do_fixup_by_prop_u32(blob, "device_type", "cpu", 4, + "timebase-frequency", (bd->bi_busfreq / 4), 1); + do_fixup_by_prop_u32(blob, "device_type", "cpu", 4, + "bus-frequency", bd->bi_busfreq, 1); + do_fixup_by_prop_u32(blob, "device_type", "cpu", 4, + "clock-frequency", gd->core_clk, 1); + do_fixup_by_prop_u32(blob, "device_type", "soc", 4, + "bus-frequency", bd->bi_busfreq, 1); + do_fixup_by_compat_u32(blob, "fsl,soc", + "bus-frequency", bd->bi_busfreq, 1); + do_fixup_by_compat_u32(blob, "fsl,soc", + "clock-frequency", bd->bi_busfreq, 1); + do_fixup_by_compat_u32(blob, "fsl,immr", + "bus-frequency", bd->bi_busfreq, 1); + do_fixup_by_compat_u32(blob, "fsl,immr", + "clock-frequency", bd->bi_busfreq, 1); +#ifdef CONFIG_QE + ft_qe_setup(blob); +#endif + +#ifdef CONFIG_SYS_NS16550 + do_fixup_by_compat_u32(blob, "ns16550", + "clock-frequency", CONFIG_SYS_NS16550_CLK, 1); +#endif + + fdt_fixup_memory(blob, (u64)bd->bi_memstart, (u64)bd->bi_memsize); + +#if defined(CONFIG_BOOTCOUNT_LIMIT) + fdt_fixup_muram (blob); +#endif +} diff --git a/arch/powerpc/cpu/mpc83xx/interrupts.c b/arch/powerpc/cpu/mpc83xx/interrupts.c new file mode 100644 index 0000000..faffbaf --- /dev/null +++ b/arch/powerpc/cpu/mpc83xx/interrupts.c @@ -0,0 +1,97 @@ +/* + * (C) Copyright 2000-2002 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * Copyright 2004 Freescale Semiconductor, Inc. + * + * 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> +#include <command.h> +#include <mpc83xx.h> +#include <asm/processor.h> + +DECLARE_GLOBAL_DATA_PTR; + +struct irq_action { + interrupt_handler_t *handler; + void *arg; + ulong count; +}; + +int interrupt_init_cpu (unsigned *decrementer_count) +{ + volatile immap_t *immr = (immap_t *) CONFIG_SYS_IMMR; + + *decrementer_count = (gd->bus_clk / 4) / CONFIG_SYS_HZ; + + /* Enable e300 time base */ + + immr->sysconf.spcr |= 0x00400000; + + return 0; +} + + +/* + * Handle external interrupts + */ + +void external_interrupt (struct pt_regs *regs) +{ +} + + +/* + * Install and free an interrupt handler. + */ + +void +irq_install_handler (int irq, interrupt_handler_t * handler, void *arg) +{ +} + + +void irq_free_handler (int irq) +{ +} + + +void timer_interrupt_cpu (struct pt_regs *regs) +{ + /* nothing to do here */ + return; +} + + +#if defined(CONFIG_CMD_IRQ) + +/* ripped this out of ppc4xx/interrupts.c */ + +/* + * irqinfo - print information about PCI devices + */ + +void +do_irqinfo(cmd_tbl_t *cmdtp, bd_t *bd, int flag, int argc, char *argv[]) +{ +} + +#endif diff --git a/arch/powerpc/cpu/mpc83xx/nand_init.c b/arch/powerpc/cpu/mpc83xx/nand_init.c new file mode 100644 index 0000000..38e141a --- /dev/null +++ b/arch/powerpc/cpu/mpc83xx/nand_init.c @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2004-2008 Freescale Semiconductor, Inc. + * + * 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> +#include <mpc83xx.h> + +DECLARE_GLOBAL_DATA_PTR; + +/* + * Breathe some life into the CPU... + * + * Set up the memory map, + * initialize a bunch of registers, + * initialize the UPM's + */ +void cpu_init_f (volatile immap_t * im) +{ + int i; + + /* Pointer is writable since we allocated a register for it */ + gd = (gd_t *) (CONFIG_SYS_INIT_RAM_ADDR + CONFIG_SYS_GBL_DATA_OFFSET); + + /* Clear initial global data */ + for (i = 0; i < sizeof(gd_t); i++) + ((char *)gd)[i] = 0; + + /* system performance tweaking */ + +#ifdef CONFIG_SYS_ACR_PIPE_DEP + /* Arbiter pipeline depth */ + im->arbiter.acr = (im->arbiter.acr & ~ACR_PIPE_DEP) | + (CONFIG_SYS_ACR_PIPE_DEP << ACR_PIPE_DEP_SHIFT); +#endif + +#ifdef CONFIG_SYS_ACR_RPTCNT + /* Arbiter repeat count */ + im->arbiter.acr = (im->arbiter.acr & ~(ACR_RPTCNT)) | + (CONFIG_SYS_ACR_RPTCNT << ACR_RPTCNT_SHIFT); +#endif + +#ifdef CONFIG_SYS_SPCR_OPT + /* Optimize transactions between CSB and other devices */ + im->sysconf.spcr = (im->sysconf.spcr & ~SPCR_OPT) | + (CONFIG_SYS_SPCR_OPT << SPCR_OPT_SHIFT); +#endif + + /* Enable Time Base & Decrimenter (so we will have udelay()) */ + im->sysconf.spcr |= SPCR_TBEN; + + /* DDR control driver register */ +#ifdef CONFIG_SYS_DDRCDR + im->sysconf.ddrcdr = CONFIG_SYS_DDRCDR; +#endif + /* Output buffer impedance register */ +#ifdef CONFIG_SYS_OBIR + im->sysconf.obir = CONFIG_SYS_OBIR; +#endif + + /* + * Memory Controller: + */ + + /* Map banks 0 and 1 to the FLASH banks 0 and 1 at preliminary + * addresses - these have to be modified later when FLASH size + * has been determined + */ + +#if defined(CONFIG_SYS_NAND_BR_PRELIM) \ + && defined(CONFIG_SYS_NAND_OR_PRELIM) \ + && defined(CONFIG_SYS_NAND_LBLAWBAR_PRELIM) \ + && defined(CONFIG_SYS_NAND_LBLAWAR_PRELIM) + im->lbus.bank[0].br = CONFIG_SYS_NAND_BR_PRELIM; + im->lbus.bank[0].or = CONFIG_SYS_NAND_OR_PRELIM; + im->sysconf.lblaw[0].bar = CONFIG_SYS_NAND_LBLAWBAR_PRELIM; + im->sysconf.lblaw[0].ar = CONFIG_SYS_NAND_LBLAWAR_PRELIM; +#else +#error CONFIG_SYS_NAND_BR_PRELIM, CONFIG_SYS_NAND_OR_PRELIM, CONFIG_SYS_NAND_LBLAWBAR_PRELIM & CONFIG_SYS_NAND_LBLAWAR_PRELIM must be defined +#endif +} + +/* + * Get timebase clock frequency (like cpu_clk in Hz) + */ +unsigned long get_tbclk(void) +{ + return (gd->bus_clk + 3L) / 4L; +} + +void puts(const char *str) +{ + while (*str) + putc(*str++); +} diff --git a/arch/powerpc/cpu/mpc83xx/pci.c b/arch/powerpc/cpu/mpc83xx/pci.c new file mode 100644 index 0000000..a42b230 --- /dev/null +++ b/arch/powerpc/cpu/mpc83xx/pci.c @@ -0,0 +1,241 @@ +/* + * Copyright (C) Freescale Semiconductor, Inc. 2007 + * + * Author: Scott Wood <scottwood@freescale.com>, + * with some bits from older board-specific PCI initialization. + * + * 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> +#include <pci.h> + +#if defined(CONFIG_OF_LIBFDT) +#include <libfdt.h> +#include <fdt_support.h> +#endif + +#include <asm/mpc8349_pci.h> + +#define MAX_BUSES 2 + +DECLARE_GLOBAL_DATA_PTR; + +static struct pci_controller pci_hose[MAX_BUSES]; +static int pci_num_buses; + +static void pci_init_bus(int bus, struct pci_region *reg) +{ + volatile immap_t *immr = (volatile immap_t *)CONFIG_SYS_IMMR; + volatile pot83xx_t *pot = immr->ios.pot; + volatile pcictrl83xx_t *pci_ctrl = &immr->pci_ctrl[bus]; + struct pci_controller *hose = &pci_hose[bus]; + u32 dev; + u16 reg16; + int i; + + if (bus == 1) + pot += 3; + + /* Setup outbound translation windows */ + for (i = 0; i < 3; i++, reg++, pot++) { + if (reg->size == 0) + break; + + hose->regions[i] = *reg; + hose->region_count++; + + pot->potar = reg->bus_start >> 12; + pot->pobar = reg->phys_start >> 12; + pot->pocmr = ~(reg->size - 1) >> 12; + + if (reg->flags & PCI_REGION_IO) + pot->pocmr |= POCMR_IO; +#ifdef CONFIG_83XX_PCI_STREAMING + else if (reg->flags & PCI_REGION_PREFETCH) + pot->pocmr |= POCMR_SE; +#endif + + if (bus == 1) + pot->pocmr |= POCMR_DST; + + pot->pocmr |= POCMR_EN; + } + + /* Point inbound translation at RAM */ + pci_ctrl->pitar1 = 0; + pci_ctrl->pibar1 = 0; + pci_ctrl->piebar1 = 0; + pci_ctrl->piwar1 = PIWAR_EN | PIWAR_PF | PIWAR_RTT_SNOOP | + PIWAR_WTT_SNOOP | (__ilog2(gd->ram_size - 1)); + + i = hose->region_count++; + hose->regions[i].bus_start = 0; + hose->regions[i].phys_start = 0; + hose->regions[i].size = gd->ram_size; + hose->regions[i].flags = PCI_REGION_MEM | PCI_REGION_SYS_MEMORY; + + hose->first_busno = pci_last_busno() + 1; + hose->last_busno = 0xff; + + pci_setup_indirect(hose, CONFIG_SYS_IMMR + 0x8300 + bus * 0x80, + CONFIG_SYS_IMMR + 0x8304 + bus * 0x80); + + pci_register_hose(hose); + + /* + * Write to Command register + */ + reg16 = 0xff; + dev = PCI_BDF(hose->first_busno, 0, 0); + pci_hose_read_config_word(hose, dev, PCI_COMMAND, ®16); + reg16 |= PCI_COMMAND_SERR | PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY; + pci_hose_write_config_word(hose, dev, PCI_COMMAND, reg16); + + /* + * Clear non-reserved bits in status register. + */ + pci_hose_write_config_word(hose, dev, PCI_STATUS, 0xffff); + pci_hose_write_config_byte(hose, dev, PCI_LATENCY_TIMER, 0x80); + pci_hose_write_config_byte(hose, dev, PCI_CACHE_LINE_SIZE, 0x08); + +#ifdef CONFIG_PCI_SCAN_SHOW + printf("PCI: Bus Dev VenId DevId Class Int\n"); +#endif +#ifndef CONFIG_PCISLAVE + /* + * Hose scan. + */ + hose->last_busno = pci_hose_scan(hose); +#endif +} + +/* + * The caller must have already set OCCR, and the PCI_LAW BARs + * must have been set to cover all of the requested regions. + * + * If fewer than three regions are requested, then the region + * list is terminated with a region of size 0. + */ +void mpc83xx_pci_init(int num_buses, struct pci_region **reg, int warmboot) +{ + volatile immap_t *immr = (volatile immap_t *)CONFIG_SYS_IMMR; + int i; + + if (num_buses > MAX_BUSES) { + printf("%d PCI buses requsted, %d supported\n", + num_buses, MAX_BUSES); + + num_buses = MAX_BUSES; + } + + pci_num_buses = num_buses; + + /* + * Release PCI RST Output signal. + * Power on to RST high must be at least 100 ms as per PCI spec. + * On warm boots only 1 ms is required. + */ + udelay(warmboot ? 1000 : 100000); + + for (i = 0; i < num_buses; i++) + immr->pci_ctrl[i].gcr = 1; + + /* + * RST high to first config access must be at least 2^25 cycles + * as per PCI spec. This could be cut in half if we know we're + * running at 66MHz. This could be insufficiently long if we're + * running the PCI bus at significantly less than 33MHz. + */ + udelay(1020000); + + for (i = 0; i < num_buses; i++) + pci_init_bus(i, reg[i]); +} + +#ifdef CONFIG_PCISLAVE + +#define PCI_FUNCTION_CONFIG 0x44 +#define PCI_FUNCTION_CFG_LOCK 0x20 + +/* + * Unlock the configuration bit so that the host system can begin booting + * + * This should be used after you have: + * 1) Called mpc83xx_pci_init() + * 2) Set up your inbound translation windows to the appropriate size + */ +void mpc83xx_pcislave_unlock(int bus) +{ + struct pci_controller *hose = &pci_hose[bus]; + u32 dev; + u16 reg16; + + /* Unlock configuration lock in PCI function configuration register */ + dev = PCI_BDF(hose->first_busno, 0, 0); + pci_hose_read_config_word (hose, dev, PCI_FUNCTION_CONFIG, ®16); + reg16 &= ~(PCI_FUNCTION_CFG_LOCK); + pci_hose_write_config_word (hose, dev, PCI_FUNCTION_CONFIG, reg16); + + /* The configuration bit is now unlocked, so we can scan the bus */ + hose->last_busno = pci_hose_scan(hose); +} +#endif + +#if defined(CONFIG_OF_LIBFDT) +void ft_pci_setup(void *blob, bd_t *bd) +{ + int nodeoffset; + int tmp[2]; + const char *path; + + if (pci_num_buses < 1) + return; + + nodeoffset = fdt_path_offset(blob, "/aliases"); + if (nodeoffset >= 0) { + path = fdt_getprop(blob, nodeoffset, "pci0", NULL); + if (path) { + tmp[0] = cpu_to_be32(pci_hose[0].first_busno); + tmp[1] = cpu_to_be32(pci_hose[0].last_busno); + do_fixup_by_path(blob, path, "bus-range", + &tmp, sizeof(tmp), 1); + + tmp[0] = cpu_to_be32(gd->pci_clk); + do_fixup_by_path(blob, path, "clock-frequency", + &tmp, sizeof(tmp[0]), 1); + } + + if (pci_num_buses < 2) + return; + + path = fdt_getprop(blob, nodeoffset, "pci1", NULL); + if (path) { + tmp[0] = cpu_to_be32(pci_hose[1].first_busno); + tmp[1] = cpu_to_be32(pci_hose[1].last_busno); + do_fixup_by_path(blob, path, "bus-range", + &tmp, sizeof(tmp), 1); + + tmp[0] = cpu_to_be32(gd->pci_clk); + do_fixup_by_path(blob, path, "clock-frequency", + &tmp, sizeof(tmp[0]), 1); + } + } +} +#endif /* CONFIG_OF_LIBFDT */ diff --git a/arch/powerpc/cpu/mpc83xx/pcie.c b/arch/powerpc/cpu/mpc83xx/pcie.c new file mode 100644 index 0000000..77f8906 --- /dev/null +++ b/arch/powerpc/cpu/mpc83xx/pcie.c @@ -0,0 +1,317 @@ +/* + * Copyright (C) 2007-2009 Freescale Semiconductor, Inc. + * Copyright (C) 2008-2009 MontaVista Software, Inc. + * + * Authors: Tony Li <tony.li@freescale.com> + * Anton Vorontsov <avorontsov@ru.mvista.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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include <common.h> +#include <pci.h> +#include <mpc83xx.h> +#include <asm/io.h> + +DECLARE_GLOBAL_DATA_PTR; + +#define PCIE_MAX_BUSES 2 + +#ifdef CONFIG_83XX_GENERIC_PCIE_REGISTER_HOSES + +static int mpc83xx_pcie_remap_cfg(struct pci_controller *hose, pci_dev_t dev) +{ + int bus = PCI_BUS(dev) - hose->first_busno; + immap_t *immr = (immap_t *)CONFIG_SYS_IMMR; + pex83xx_t *pex = &immr->pciexp[bus]; + struct pex_outbound_window *out_win = &pex->bridge.pex_outbound_win[0]; + u8 devfn = PCI_DEV(dev) << 3 | PCI_FUNC(dev); + u32 dev_base = bus << 24 | devfn << 16; + + if (hose->indirect_type == INDIRECT_TYPE_NO_PCIE_LINK) + return -1; + /* + * Workaround for the HW bug: for Type 0 configure transactions the + * PCI-E controller does not check the device number bits and just + * assumes that the device number bits are 0. + */ + if (devfn & 0xf8) + return -1; + + out_le32(&out_win->tarl, dev_base); + return 0; +} + +#define cfg_read(val, addr, type, op) \ + do { *val = op((type)(addr)); } while (0) +#define cfg_write(val, addr, type, op) \ + do { op((type *)(addr), (val)); } while (0) + +#define cfg_read_err(val) do { *val = -1; } while (0) +#define cfg_write_err(val) do { } while (0) + +#define PCIE_OP(rw, size, type, op) \ +static int pcie_##rw##_config_##size(struct pci_controller *hose, \ + pci_dev_t dev, int offset, \ + type val) \ +{ \ + int ret; \ + \ + ret = mpc83xx_pcie_remap_cfg(hose, dev); \ + if (ret) { \ + cfg_##rw##_err(val); \ + return ret; \ + } \ + cfg_##rw(val, (void *)hose->cfg_addr + offset, type, op); \ + return 0; \ +} + +PCIE_OP(read, byte, u8 *, in_8) +PCIE_OP(read, word, u16 *, in_le16) +PCIE_OP(read, dword, u32 *, in_le32) +PCIE_OP(write, byte, u8, out_8) +PCIE_OP(write, word, u16, out_le16) +PCIE_OP(write, dword, u32, out_le32) + +static void mpc83xx_pcie_register_hose(int bus, struct pci_region *reg, + u8 link) +{ + extern void disable_addr_trans(void); /* start.S */ + static struct pci_controller pcie_hose[PCIE_MAX_BUSES]; + struct pci_controller *hose = &pcie_hose[bus]; + int i; + + /* + * There are no spare BATs to remap all PCI-E windows for U-Boot, so + * disable translations. In general, this is not great solution, and + * that's why we don't register PCI-E hoses by default. + */ + disable_addr_trans(); + + for (i = 0; i < 2; i++, reg++) { + if (reg->size == 0) + break; + + hose->regions[i] = *reg; + hose->region_count++; + } + + i = hose->region_count++; + hose->regions[i].bus_start = 0; + hose->regions[i].phys_start = 0; + hose->regions[i].size = gd->ram_size; + hose->regions[i].flags = PCI_REGION_MEM | PCI_REGION_SYS_MEMORY; + + i = hose->region_count++; + hose->regions[i].bus_start = CONFIG_SYS_IMMR; + hose->regions[i].phys_start = CONFIG_SYS_IMMR; + hose->regions[i].size = 0x100000; + hose->regions[i].flags = PCI_REGION_MEM | PCI_REGION_SYS_MEMORY; + + hose->first_busno = pci_last_busno() + 1; + hose->last_busno = 0xff; + + if (bus == 0) + hose->cfg_addr = (unsigned int *)CONFIG_SYS_PCIE1_CFG_BASE; + else + hose->cfg_addr = (unsigned int *)CONFIG_SYS_PCIE2_CFG_BASE; + + pci_set_ops(hose, + pcie_read_config_byte, + pcie_read_config_word, + pcie_read_config_dword, + pcie_write_config_byte, + pcie_write_config_word, + pcie_write_config_dword); + + if (!link) + hose->indirect_type = INDIRECT_TYPE_NO_PCIE_LINK; + + pci_register_hose(hose); + +#ifdef CONFIG_PCI_SCAN_SHOW + printf("PCI: Bus Dev VenId DevId Class Int\n"); +#endif + /* + * Hose scan. + */ + hose->last_busno = pci_hose_scan(hose); +} + +#else + +static void mpc83xx_pcie_register_hose(int bus, struct pci_region *reg, + u8 link) {} + +#endif /* CONFIG_83XX_GENERIC_PCIE_REGISTER_HOSES */ + +static void mpc83xx_pcie_init_bus(int bus, struct pci_region *reg) +{ + immap_t *immr = (immap_t *)CONFIG_SYS_IMMR; + pex83xx_t *pex = &immr->pciexp[bus]; + struct pex_outbound_window *out_win; + struct pex_inbound_window *in_win; + void *hose_cfg_base; + unsigned int ram_sz; + unsigned int barl; + unsigned int tar; + u16 reg16; + int i; + + /* Enable pex csb bridge inbound & outbound transactions */ + out_le32(&pex->bridge.pex_csb_ctrl, + in_le32(&pex->bridge.pex_csb_ctrl) | PEX_CSB_CTRL_OBPIOE | + PEX_CSB_CTRL_IBPIOE); + + /* Enable bridge outbound */ + out_le32(&pex->bridge.pex_csb_obctrl, PEX_CSB_OBCTRL_PIOE | + PEX_CSB_OBCTRL_MEMWE | PEX_CSB_OBCTRL_IOWE | + PEX_CSB_OBCTRL_CFGWE); + + out_win = &pex->bridge.pex_outbound_win[0]; + if (bus) { + out_le32(&out_win->ar, PEX_OWAR_EN | PEX_OWAR_TYPE_CFG | + CONFIG_SYS_PCIE2_CFG_SIZE); + out_le32(&out_win->bar, CONFIG_SYS_PCIE2_CFG_BASE); + } else { + out_le32(&out_win->ar, PEX_OWAR_EN | PEX_OWAR_TYPE_CFG | + CONFIG_SYS_PCIE1_CFG_SIZE); + out_le32(&out_win->bar, CONFIG_SYS_PCIE1_CFG_BASE); + } + out_le32(&out_win->tarl, 0); + out_le32(&out_win->tarh, 0); + + for (i = 0; i < 2; i++, reg++) { + u32 ar; + + if (reg->size == 0) + break; + + out_win = &pex->bridge.pex_outbound_win[i + 1]; + out_le32(&out_win->bar, reg->phys_start); + out_le32(&out_win->tarl, reg->bus_start); + out_le32(&out_win->tarh, 0); + ar = PEX_OWAR_EN | (reg->size & PEX_OWAR_SIZE); + if (reg->flags & PCI_REGION_IO) + ar |= PEX_OWAR_TYPE_IO; + else + ar |= PEX_OWAR_TYPE_MEM; + out_le32(&out_win->ar, ar); + } + + out_le32(&pex->bridge.pex_csb_ibctrl, PEX_CSB_IBCTRL_PIOE); + + ram_sz = gd->ram_size; + barl = 0; + tar = 0; + i = 0; + while (ram_sz > 0) { + in_win = &pex->bridge.pex_inbound_win[i]; + out_le32(&in_win->barl, barl); + out_le32(&in_win->barh, 0x0); + out_le32(&in_win->tar, tar); + if (ram_sz >= 0x10000000) { + /* The maxium windows size is 256M */ + out_le32(&in_win->ar, PEX_IWAR_EN | PEX_IWAR_NSOV | + PEX_IWAR_TYPE_PF | 0x0FFFF000); + barl += 0x10000000; + tar += 0x10000000; + ram_sz -= 0x10000000; + } else { + /* The UM is not clear here. + * So, round up to even Mb boundary */ + + ram_sz = ram_sz >> (20 + + ((ram_sz & 0xFFFFF) ? 1 : 0)); + if (!(ram_sz % 2)) + ram_sz -= 1; + out_le32(&in_win->ar, PEX_IWAR_EN | PEX_IWAR_NSOV | + PEX_IWAR_TYPE_PF | (ram_sz << 20) | 0xFF000); + ram_sz = 0; + } + i++; + } + + in_win = &pex->bridge.pex_inbound_win[i]; + out_le32(&in_win->barl, CONFIG_SYS_IMMR); + out_le32(&in_win->barh, 0); + out_le32(&in_win->tar, CONFIG_SYS_IMMR); + out_le32(&in_win->ar, PEX_IWAR_EN | + PEX_IWAR_TYPE_NO_PF | PEX_IWAR_SIZE_1M); + + /* Enable the host virtual INTX interrupts */ + out_le32(&pex->bridge.pex_int_axi_misc_enb, + in_le32(&pex->bridge.pex_int_axi_misc_enb) | 0x1E0); + + /* Hose configure header is memory-mapped */ + hose_cfg_base = (void *)pex; + + get_clocks(); + /* Configure the PCIE controller core clock ratio */ + out_le32(hose_cfg_base + PEX_GCLK_RATIO, + (((bus ? gd->pciexp2_clk : gd->pciexp1_clk) / 1000000) * 16) + / 333); + udelay(1000000); + + /* Do Type 1 bridge configuration */ + out_8(hose_cfg_base + PCI_PRIMARY_BUS, 0); + out_8(hose_cfg_base + PCI_SECONDARY_BUS, 1); + out_8(hose_cfg_base + PCI_SUBORDINATE_BUS, 255); + + /* + * Write to Command register + */ + reg16 = in_le16(hose_cfg_base + PCI_COMMAND); + reg16 |= PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY | PCI_COMMAND_IO | + PCI_COMMAND_SERR | PCI_COMMAND_PARITY; + out_le16(hose_cfg_base + PCI_COMMAND, reg16); + + /* + * Clear non-reserved bits in status register. + */ + out_le16(hose_cfg_base + PCI_STATUS, 0xffff); + out_8(hose_cfg_base + PCI_LATENCY_TIMER, 0x80); + out_8(hose_cfg_base + PCI_CACHE_LINE_SIZE, 0x08); + + printf("PCIE%d: ", bus); + + reg16 = in_le16(hose_cfg_base + PCI_LTSSM); + if (reg16 >= PCI_LTSSM_L0) + printf("link\n"); + else + printf("No link\n"); + + mpc83xx_pcie_register_hose(bus, reg, reg16 >= PCI_LTSSM_L0); +} + +/* + * The caller must have already set SCCR, SERDES and the PCIE_LAW BARs + * must have been set to cover all of the requested regions. + */ +void mpc83xx_pcie_init(int num_buses, struct pci_region **reg, int warmboot) +{ + int i; + + /* + * Release PCI RST Output signal. + * Power on to RST high must be at least 100 ms as per PCI spec. + * On warm boots only 1 ms is required. + */ + udelay(warmboot ? 1000 : 100000); + + for (i = 0; i < num_buses; i++) + mpc83xx_pcie_init_bus(i, reg[i]); +} diff --git a/arch/powerpc/cpu/mpc83xx/qe_io.c b/arch/powerpc/cpu/mpc83xx/qe_io.c new file mode 100644 index 0000000..db94f00 --- /dev/null +++ b/arch/powerpc/cpu/mpc83xx/qe_io.c @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2006 Freescale Semiconductor, Inc. + * + * Dave Liu <daveliu@freescale.com> + * based on source code of Shlomi Gridish + * + * 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" +#include "asm/errno.h" +#include "asm/io.h" +#include "asm/immap_83xx.h" + +#define NUM_OF_PINS 32 +void qe_config_iopin(u8 port, u8 pin, int dir, int open_drain, int assign) +{ + u32 pin_2bit_mask; + u32 pin_2bit_dir; + u32 pin_2bit_assign; + u32 pin_1bit_mask; + u32 tmp_val; + volatile immap_t *im = (volatile immap_t *)CONFIG_SYS_IMMR; + volatile qepio83xx_t *par_io = (volatile qepio83xx_t *)&im->qepio; + + /* Caculate pin location and 2bit mask and dir */ + pin_2bit_mask = (u32)(0x3 << (NUM_OF_PINS-(pin%(NUM_OF_PINS/2)+1)*2)); + pin_2bit_dir = (u32)(dir << (NUM_OF_PINS-(pin%(NUM_OF_PINS/2)+1)*2)); + + /* Setup the direction */ + tmp_val = (pin > (NUM_OF_PINS/2) - 1) ? \ + in_be32(&par_io->ioport[port].dir2) : + in_be32(&par_io->ioport[port].dir1); + + if (pin > (NUM_OF_PINS/2) -1) { + out_be32(&par_io->ioport[port].dir2, ~pin_2bit_mask & tmp_val); + out_be32(&par_io->ioport[port].dir2, pin_2bit_dir | tmp_val); + } else { + out_be32(&par_io->ioport[port].dir1, ~pin_2bit_mask & tmp_val); + out_be32(&par_io->ioport[port].dir1, pin_2bit_dir | tmp_val); + } + + /* Calculate pin location for 1bit mask */ + pin_1bit_mask = (u32)(1 << (NUM_OF_PINS - (pin+1))); + + /* Setup the open drain */ + tmp_val = in_be32(&par_io->ioport[port].podr); + if (open_drain) { + out_be32(&par_io->ioport[port].podr, pin_1bit_mask | tmp_val); + } else { + out_be32(&par_io->ioport[port].podr, ~pin_1bit_mask & tmp_val); + } + + /* Setup the assignment */ + tmp_val = (pin > (NUM_OF_PINS/2) - 1) ? + in_be32(&par_io->ioport[port].ppar2): + in_be32(&par_io->ioport[port].ppar1); + pin_2bit_assign = (u32)(assign + << (NUM_OF_PINS - (pin%(NUM_OF_PINS/2)+1)*2)); + + /* Clear and set 2 bits mask */ + if (pin > (NUM_OF_PINS/2) - 1) { + out_be32(&par_io->ioport[port].ppar2, ~pin_2bit_mask & tmp_val); + out_be32(&par_io->ioport[port].ppar2, pin_2bit_assign | tmp_val); + } else { + out_be32(&par_io->ioport[port].ppar1, ~pin_2bit_mask & tmp_val); + out_be32(&par_io->ioport[port].ppar1, pin_2bit_assign | tmp_val); + } +} diff --git a/arch/powerpc/cpu/mpc83xx/serdes.c b/arch/powerpc/cpu/mpc83xx/serdes.c new file mode 100644 index 0000000..64033fe --- /dev/null +++ b/arch/powerpc/cpu/mpc83xx/serdes.c @@ -0,0 +1,145 @@ +/* + * Freescale SerDes initialization routine + * + * Copyright (C) 2007 Freescale Semicondutor, Inc. + * Copyright (C) 2008 MontaVista Software, Inc. + * + * Author: Li Yang <leoli@freescale.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. + */ + +#include <config.h> +#include <common.h> +#include <asm/io.h> +#include <asm/fsl_serdes.h> + +/* SerDes registers */ +#define FSL_SRDSCR0_OFFS 0x0 +#define FSL_SRDSCR0_DPP_1V2 0x00008800 +#define FSL_SRDSCR1_OFFS 0x4 +#define FSL_SRDSCR1_PLLBW 0x00000040 +#define FSL_SRDSCR2_OFFS 0x8 +#define FSL_SRDSCR2_VDD_1V2 0x00800000 +#define FSL_SRDSCR2_SEIC_MASK 0x00001c1c +#define FSL_SRDSCR2_SEIC_SATA 0x00001414 +#define FSL_SRDSCR2_SEIC_PEX 0x00001010 +#define FSL_SRDSCR2_SEIC_SGMII 0x00000101 +#define FSL_SRDSCR3_OFFS 0xc +#define FSL_SRDSCR3_KFR_SATA 0x10100000 +#define FSL_SRDSCR3_KPH_SATA 0x04040000 +#define FSL_SRDSCR3_SDFM_SATA_PEX 0x01010000 +#define FSL_SRDSCR3_SDTXL_SATA 0x00000505 +#define FSL_SRDSCR4_OFFS 0x10 +#define FSL_SRDSCR4_PROT_SATA 0x00000808 +#define FSL_SRDSCR4_PROT_PEX 0x00000101 +#define FSL_SRDSCR4_PROT_SGMII 0x00000505 +#define FSL_SRDSCR4_PLANE_X2 0x01000000 +#define FSL_SRDSRSTCTL_OFFS 0x20 +#define FSL_SRDSRSTCTL_RST 0x80000000 +#define FSL_SRDSRSTCTL_SATA_RESET 0xf + +void fsl_setup_serdes(u32 offset, char proto, u32 rfcks, char vdd) +{ + void *regs = (void *)CONFIG_SYS_IMMR + offset; + u32 tmp; + + /* 1.0V corevdd */ + if (vdd) { + /* DPPE/DPPA = 0 */ + tmp = in_be32(regs + FSL_SRDSCR0_OFFS); + tmp &= ~FSL_SRDSCR0_DPP_1V2; + out_be32(regs + FSL_SRDSCR0_OFFS, tmp); + + /* VDD = 0 */ + tmp = in_be32(regs + FSL_SRDSCR2_OFFS); + tmp &= ~FSL_SRDSCR2_VDD_1V2; + out_be32(regs + FSL_SRDSCR2_OFFS, tmp); + } + + /* protocol specific configuration */ + switch (proto) { + case FSL_SERDES_PROTO_SATA: + /* Set and clear reset bits */ + tmp = in_be32(regs + FSL_SRDSRSTCTL_OFFS); + tmp |= FSL_SRDSRSTCTL_SATA_RESET; + out_be32(regs + FSL_SRDSRSTCTL_OFFS, tmp); + udelay(1000); + tmp &= ~FSL_SRDSRSTCTL_SATA_RESET; + out_be32(regs + FSL_SRDSRSTCTL_OFFS, tmp); + + /* Configure SRDSCR1 */ + tmp = in_be32(regs + FSL_SRDSCR1_OFFS); + tmp &= ~FSL_SRDSCR1_PLLBW; + out_be32(regs + FSL_SRDSCR1_OFFS, tmp); + + /* Configure SRDSCR2 */ + tmp = in_be32(regs + FSL_SRDSCR2_OFFS); + tmp &= ~FSL_SRDSCR2_SEIC_MASK; + tmp |= FSL_SRDSCR2_SEIC_SATA; + out_be32(regs + FSL_SRDSCR2_OFFS, tmp); + + /* Configure SRDSCR3 */ + tmp = FSL_SRDSCR3_KFR_SATA | FSL_SRDSCR3_KPH_SATA | + FSL_SRDSCR3_SDFM_SATA_PEX | + FSL_SRDSCR3_SDTXL_SATA; + out_be32(regs + FSL_SRDSCR3_OFFS, tmp); + + /* Configure SRDSCR4 */ + tmp = rfcks | FSL_SRDSCR4_PROT_SATA; + out_be32(regs + FSL_SRDSCR4_OFFS, tmp); + break; + case FSL_SERDES_PROTO_PEX: + case FSL_SERDES_PROTO_PEX_X2: + /* Configure SRDSCR1 */ + tmp = in_be32(regs + FSL_SRDSCR1_OFFS); + tmp |= FSL_SRDSCR1_PLLBW; + out_be32(regs + FSL_SRDSCR1_OFFS, tmp); + + /* Configure SRDSCR2 */ + tmp = in_be32(regs + FSL_SRDSCR2_OFFS); + tmp &= ~FSL_SRDSCR2_SEIC_MASK; + tmp |= FSL_SRDSCR2_SEIC_PEX; + out_be32(regs + FSL_SRDSCR2_OFFS, tmp); + + /* Configure SRDSCR3 */ + tmp = FSL_SRDSCR3_SDFM_SATA_PEX; + out_be32(regs + FSL_SRDSCR3_OFFS, tmp); + + /* Configure SRDSCR4 */ + tmp = rfcks | FSL_SRDSCR4_PROT_PEX; + if (proto == FSL_SERDES_PROTO_PEX_X2) + tmp |= FSL_SRDSCR4_PLANE_X2; + out_be32(regs + FSL_SRDSCR4_OFFS, tmp); + break; + case FSL_SERDES_PROTO_SGMII: + /* Configure SRDSCR1 */ + tmp = in_be32(regs + FSL_SRDSCR1_OFFS); + tmp &= ~FSL_SRDSCR1_PLLBW; + out_be32(regs + FSL_SRDSCR1_OFFS, tmp); + + /* Configure SRDSCR2 */ + tmp = in_be32(regs + FSL_SRDSCR2_OFFS); + tmp &= ~FSL_SRDSCR2_SEIC_MASK; + tmp |= FSL_SRDSCR2_SEIC_SGMII; + out_be32(regs + FSL_SRDSCR2_OFFS, tmp); + + /* Configure SRDSCR3 */ + out_be32(regs + FSL_SRDSCR3_OFFS, 0); + + /* Configure SRDSCR4 */ + tmp = rfcks | FSL_SRDSCR4_PROT_SGMII; + out_be32(regs + FSL_SRDSCR4_OFFS, tmp); + break; + default: + return; + } + + /* Do a software reset */ + tmp = in_be32(regs + FSL_SRDSRSTCTL_OFFS); + tmp |= FSL_SRDSRSTCTL_RST; + out_be32(regs + FSL_SRDSRSTCTL_OFFS, tmp); +} diff --git a/arch/powerpc/cpu/mpc83xx/spd_sdram.c b/arch/powerpc/cpu/mpc83xx/spd_sdram.c new file mode 100644 index 0000000..44aaa9a --- /dev/null +++ b/arch/powerpc/cpu/mpc83xx/spd_sdram.c @@ -0,0 +1,918 @@ +/* + * (C) Copyright 2006-2007 Freescale Semiconductor, Inc. + * + * (C) Copyright 2006 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * Copyright (C) 2004-2006 Freescale Semiconductor, Inc. + * (C) Copyright 2003 Motorola Inc. + * Xianghua Xiao (X.Xiao@motorola.com) + * + * 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> +#include <asm/processor.h> +#include <asm/io.h> +#include <i2c.h> +#include <spd.h> +#include <asm/mmu.h> +#include <spd_sdram.h> + +DECLARE_GLOBAL_DATA_PTR; + +void board_add_ram_info(int use_default) +{ + volatile immap_t *immap = (immap_t *) CONFIG_SYS_IMMR; + volatile ddr83xx_t *ddr = &immap->ddr; + char buf[32]; + + printf(" (DDR%d", ((ddr->sdram_cfg & SDRAM_CFG_SDRAM_TYPE_MASK) + >> SDRAM_CFG_SDRAM_TYPE_SHIFT) - 1); + + if (ddr->sdram_cfg & SDRAM_CFG_32_BE) + puts(", 32-bit"); + else + puts(", 64-bit"); + + if (ddr->sdram_cfg & SDRAM_CFG_ECC_EN) + puts(", ECC on"); + else + puts(", ECC off"); + + printf(", %s MHz)", strmhz(buf, gd->mem_clk)); + +#if defined(CONFIG_SYS_LB_SDRAM) && defined(CONFIG_SYS_LBC_SDRAM_SIZE) + puts("\nSDRAM: "); + print_size (CONFIG_SYS_LBC_SDRAM_SIZE * 1024 * 1024, " (local bus)"); +#endif +} + +#ifdef CONFIG_SPD_EEPROM +#ifndef CONFIG_SYS_READ_SPD +#define CONFIG_SYS_READ_SPD i2c_read +#endif + +/* + * Convert picoseconds into clock cycles (rounding up if needed). + */ +int +picos_to_clk(int picos) +{ + unsigned int mem_bus_clk; + int clks; + + mem_bus_clk = gd->mem_clk >> 1; + clks = picos / (1000000000 / (mem_bus_clk / 1000)); + if (picos % (1000000000 / (mem_bus_clk / 1000)) != 0) + clks++; + + return clks; +} + +unsigned int banksize(unsigned char row_dens) +{ + return ((row_dens >> 2) | ((row_dens & 3) << 6)) << 24; +} + +int read_spd(uint addr) +{ + return ((int) addr); +} + +#undef SPD_DEBUG +#ifdef SPD_DEBUG +static void spd_debug(spd_eeprom_t *spd) +{ + printf ("\nDIMM type: %-18.18s\n", spd->mpart); + printf ("SPD size: %d\n", spd->info_size); + printf ("EEPROM size: %d\n", 1 << spd->chip_size); + printf ("Memory type: %d\n", spd->mem_type); + printf ("Row addr: %d\n", spd->nrow_addr); + printf ("Column addr: %d\n", spd->ncol_addr); + printf ("# of rows: %d\n", spd->nrows); + printf ("Row density: %d\n", spd->row_dens); + printf ("# of banks: %d\n", spd->nbanks); + printf ("Data width: %d\n", + 256 * spd->dataw_msb + spd->dataw_lsb); + printf ("Chip width: %d\n", spd->primw); + printf ("Refresh rate: %02X\n", spd->refresh); + printf ("CAS latencies: %02X\n", spd->cas_lat); + printf ("Write latencies: %02X\n", spd->write_lat); + printf ("tRP: %d\n", spd->trp); + printf ("tRCD: %d\n", spd->trcd); + printf ("\n"); +} +#endif /* SPD_DEBUG */ + +long int spd_sdram() +{ + volatile immap_t *immap = (immap_t *)CONFIG_SYS_IMMR; + volatile ddr83xx_t *ddr = &immap->ddr; + volatile law83xx_t *ecm = &immap->sysconf.ddrlaw[0]; + spd_eeprom_t spd; + unsigned int n_ranks; + unsigned int odt_rd_cfg, odt_wr_cfg; + unsigned char twr_clk, twtr_clk; + unsigned int sdram_type; + unsigned int memsize; + unsigned int law_size; + unsigned char caslat, caslat_ctrl; + unsigned int trfc, trfc_clk, trfc_low, trfc_high; + unsigned int trcd_clk, trtp_clk; + unsigned char cke_min_clk; + unsigned char add_lat, wr_lat; + unsigned char wr_data_delay; + unsigned char four_act; + unsigned char cpo; + unsigned char burstlen; + unsigned char odt_cfg, mode_odt_enable; + unsigned int max_bus_clk; + unsigned int max_data_rate, effective_data_rate; + unsigned int ddrc_clk; + unsigned int refresh_clk; + unsigned int sdram_cfg; + unsigned int ddrc_ecc_enable; + unsigned int pvr = get_pvr(); + + /* + * First disable the memory controller (could be enabled + * by the debugger) + */ + clrsetbits_be32(&ddr->sdram_cfg, SDRAM_CFG_MEM_EN, 0); + sync(); + isync(); + + /* Read SPD parameters with I2C */ + CONFIG_SYS_READ_SPD(SPD_EEPROM_ADDRESS, 0, 1, (uchar *) & spd, sizeof (spd)); +#ifdef SPD_DEBUG + spd_debug(&spd); +#endif + /* Check the memory type */ + if (spd.mem_type != SPD_MEMTYPE_DDR && spd.mem_type != SPD_MEMTYPE_DDR2) { + debug("DDR: Module mem type is %02X\n", spd.mem_type); + return 0; + } + + /* Check the number of physical bank */ + if (spd.mem_type == SPD_MEMTYPE_DDR) { + n_ranks = spd.nrows; + } else { + n_ranks = (spd.nrows & 0x7) + 1; + } + + if (n_ranks > 2) { + printf("DDR: The number of physical bank is %02X\n", n_ranks); + return 0; + } + + /* Check if the number of row of the module is in the range of DDRC */ + if (spd.nrow_addr < 12 || spd.nrow_addr > 15) { + printf("DDR: Row number is out of range of DDRC, row=%02X\n", + spd.nrow_addr); + return 0; + } + + /* Check if the number of col of the module is in the range of DDRC */ + if (spd.ncol_addr < 8 || spd.ncol_addr > 11) { + printf("DDR: Col number is out of range of DDRC, col=%02X\n", + spd.ncol_addr); + return 0; + } + +#ifdef CONFIG_SYS_DDRCDR_VALUE + /* + * Adjust DDR II IO voltage biasing. It just makes it work. + */ + if(spd.mem_type == SPD_MEMTYPE_DDR2) { + immap->sysconf.ddrcdr = CONFIG_SYS_DDRCDR_VALUE; + } + udelay(50000); +#endif + + /* + * ODT configuration recommendation from DDR Controller Chapter. + */ + odt_rd_cfg = 0; /* Never assert ODT */ + odt_wr_cfg = 0; /* Never assert ODT */ + if (spd.mem_type == SPD_MEMTYPE_DDR2) { + odt_wr_cfg = 1; /* Assert ODT on writes to CSn */ + } + + /* Setup DDR chip select register */ +#ifdef CONFIG_SYS_83XX_DDR_USES_CS0 + ddr->csbnds[0].csbnds = (banksize(spd.row_dens) >> 24) - 1; + ddr->cs_config[0] = ( 1 << 31 + | (odt_rd_cfg << 20) + | (odt_wr_cfg << 16) + | ((spd.nbanks == 8 ? 1 : 0) << 14) + | ((spd.nrow_addr - 12) << 8) + | (spd.ncol_addr - 8) ); + debug("\n"); + debug("cs0_bnds = 0x%08x\n",ddr->csbnds[0].csbnds); + debug("cs0_config = 0x%08x\n",ddr->cs_config[0]); + + if (n_ranks == 2) { + ddr->csbnds[1].csbnds = ( (banksize(spd.row_dens) >> 8) + | ((banksize(spd.row_dens) >> 23) - 1) ); + ddr->cs_config[1] = ( 1<<31 + | (odt_rd_cfg << 20) + | (odt_wr_cfg << 16) + | ((spd.nbanks == 8 ? 1 : 0) << 14) + | ((spd.nrow_addr - 12) << 8) + | (spd.ncol_addr - 8) ); + debug("cs1_bnds = 0x%08x\n",ddr->csbnds[1].csbnds); + debug("cs1_config = 0x%08x\n",ddr->cs_config[1]); + } + +#else + ddr->csbnds[2].csbnds = (banksize(spd.row_dens) >> 24) - 1; + ddr->cs_config[2] = ( 1 << 31 + | (odt_rd_cfg << 20) + | (odt_wr_cfg << 16) + | ((spd.nbanks == 8 ? 1 : 0) << 14) + | ((spd.nrow_addr - 12) << 8) + | (spd.ncol_addr - 8) ); + debug("\n"); + debug("cs2_bnds = 0x%08x\n",ddr->csbnds[2].csbnds); + debug("cs2_config = 0x%08x\n",ddr->cs_config[2]); + + if (n_ranks == 2) { + ddr->csbnds[3].csbnds = ( (banksize(spd.row_dens) >> 8) + | ((banksize(spd.row_dens) >> 23) - 1) ); + ddr->cs_config[3] = ( 1<<31 + | (odt_rd_cfg << 20) + | (odt_wr_cfg << 16) + | ((spd.nbanks == 8 ? 1 : 0) << 14) + | ((spd.nrow_addr - 12) << 8) + | (spd.ncol_addr - 8) ); + debug("cs3_bnds = 0x%08x\n",ddr->csbnds[3].csbnds); + debug("cs3_config = 0x%08x\n",ddr->cs_config[3]); + } +#endif + + /* + * Figure out memory size in Megabytes. + */ + memsize = n_ranks * banksize(spd.row_dens) / 0x100000; + + /* + * First supported LAW size is 16M, at LAWAR_SIZE_16M == 23. + */ + law_size = 19 + __ilog2(memsize); + + /* + * Set up LAWBAR for all of DDR. + */ + ecm->bar = CONFIG_SYS_DDR_SDRAM_BASE & 0xfffff000; + ecm->ar = (LAWAR_EN | LAWAR_TRGT_IF_DDR | (LAWAR_SIZE & law_size)); + debug("DDR:bar=0x%08x\n", ecm->bar); + debug("DDR:ar=0x%08x\n", ecm->ar); + + /* + * Find the largest CAS by locating the highest 1 bit + * in the spd.cas_lat field. Translate it to a DDR + * controller field value: + * + * CAS Lat DDR I DDR II Ctrl + * Clocks SPD Bit SPD Bit Value + * ------- ------- ------- ----- + * 1.0 0 0001 + * 1.5 1 0010 + * 2.0 2 2 0011 + * 2.5 3 0100 + * 3.0 4 3 0101 + * 3.5 5 0110 + * 4.0 6 4 0111 + * 4.5 1000 + * 5.0 5 1001 + */ + caslat = __ilog2(spd.cas_lat); + if ((spd.mem_type == SPD_MEMTYPE_DDR) + && (caslat > 6)) { + printf("DDR I: Invalid SPD CAS Latency: 0x%x.\n", spd.cas_lat); + return 0; + } else if (spd.mem_type == SPD_MEMTYPE_DDR2 + && (caslat < 2 || caslat > 5)) { + printf("DDR II: Invalid SPD CAS Latency: 0x%x.\n", + spd.cas_lat); + return 0; + } + debug("DDR: caslat SPD bit is %d\n", caslat); + + max_bus_clk = 1000 *10 / (((spd.clk_cycle & 0xF0) >> 4) * 10 + + (spd.clk_cycle & 0x0f)); + max_data_rate = max_bus_clk * 2; + + debug("DDR:Module maximum data rate is: %d MHz\n", max_data_rate); + + ddrc_clk = gd->mem_clk / 1000000; + effective_data_rate = 0; + + if (max_data_rate >= 460) { /* it is DDR2-800, 667, 533 */ + if (spd.cas_lat & 0x08) + caslat = 3; + else + caslat = 4; + if (ddrc_clk <= 460 && ddrc_clk > 350) + effective_data_rate = 400; + else if (ddrc_clk <=350 && ddrc_clk > 280) + effective_data_rate = 333; + else if (ddrc_clk <= 280 && ddrc_clk > 230) + effective_data_rate = 266; + else + effective_data_rate = 200; + } else if (max_data_rate >= 390 && max_data_rate < 460) { /* it is DDR 400 */ + if (ddrc_clk <= 460 && ddrc_clk > 350) { + /* DDR controller clk at 350~460 */ + effective_data_rate = 400; /* 5ns */ + caslat = caslat; + } else if (ddrc_clk <= 350 && ddrc_clk > 280) { + /* DDR controller clk at 280~350 */ + effective_data_rate = 333; /* 6ns */ + if (spd.clk_cycle2 == 0x60) + caslat = caslat - 1; + else + caslat = caslat; + } else if (ddrc_clk <= 280 && ddrc_clk > 230) { + /* DDR controller clk at 230~280 */ + effective_data_rate = 266; /* 7.5ns */ + if (spd.clk_cycle3 == 0x75) + caslat = caslat - 2; + else if (spd.clk_cycle2 == 0x75) + caslat = caslat - 1; + else + caslat = caslat; + } else if (ddrc_clk <= 230 && ddrc_clk > 90) { + /* DDR controller clk at 90~230 */ + effective_data_rate = 200; /* 10ns */ + if (spd.clk_cycle3 == 0xa0) + caslat = caslat - 2; + else if (spd.clk_cycle2 == 0xa0) + caslat = caslat - 1; + else + caslat = caslat; + } + } else if (max_data_rate >= 323) { /* it is DDR 333 */ + if (ddrc_clk <= 350 && ddrc_clk > 280) { + /* DDR controller clk at 280~350 */ + effective_data_rate = 333; /* 6ns */ + caslat = caslat; + } else if (ddrc_clk <= 280 && ddrc_clk > 230) { + /* DDR controller clk at 230~280 */ + effective_data_rate = 266; /* 7.5ns */ + if (spd.clk_cycle2 == 0x75) + caslat = caslat - 1; + else + caslat = caslat; + } else if (ddrc_clk <= 230 && ddrc_clk > 90) { + /* DDR controller clk at 90~230 */ + effective_data_rate = 200; /* 10ns */ + if (spd.clk_cycle3 == 0xa0) + caslat = caslat - 2; + else if (spd.clk_cycle2 == 0xa0) + caslat = caslat - 1; + else + caslat = caslat; + } + } else if (max_data_rate >= 256) { /* it is DDR 266 */ + if (ddrc_clk <= 350 && ddrc_clk > 280) { + /* DDR controller clk at 280~350 */ + printf("DDR: DDR controller freq is more than " + "max data rate of the module\n"); + return 0; + } else if (ddrc_clk <= 280 && ddrc_clk > 230) { + /* DDR controller clk at 230~280 */ + effective_data_rate = 266; /* 7.5ns */ + caslat = caslat; + } else if (ddrc_clk <= 230 && ddrc_clk > 90) { + /* DDR controller clk at 90~230 */ + effective_data_rate = 200; /* 10ns */ + if (spd.clk_cycle2 == 0xa0) + caslat = caslat - 1; + } + } else if (max_data_rate >= 190) { /* it is DDR 200 */ + if (ddrc_clk <= 350 && ddrc_clk > 230) { + /* DDR controller clk at 230~350 */ + printf("DDR: DDR controller freq is more than " + "max data rate of the module\n"); + return 0; + } else if (ddrc_clk <= 230 && ddrc_clk > 90) { + /* DDR controller clk at 90~230 */ + effective_data_rate = 200; /* 10ns */ + caslat = caslat; + } + } + + debug("DDR:Effective data rate is: %dMHz\n", effective_data_rate); + debug("DDR:The MSB 1 of CAS Latency is: %d\n", caslat); + + /* + * Errata DDR6 work around: input enable 2 cycles earlier. + * including MPC834x Rev1.0/1.1 and MPC8360 Rev1.1/1.2. + */ + if(PVR_MAJ(pvr) <= 1 && spd.mem_type == SPD_MEMTYPE_DDR){ + if (caslat == 2) + ddr->debug_reg = 0x201c0000; /* CL=2 */ + else if (caslat == 3) + ddr->debug_reg = 0x202c0000; /* CL=2.5 */ + else if (caslat == 4) + ddr->debug_reg = 0x202c0000; /* CL=3.0 */ + + __asm__ __volatile__ ("sync"); + + debug("Errata DDR6 (debug_reg=0x%08x)\n", ddr->debug_reg); + } + + /* + * Convert caslat clocks to DDR controller value. + * Force caslat_ctrl to be DDR Controller field-sized. + */ + if (spd.mem_type == SPD_MEMTYPE_DDR) { + caslat_ctrl = (caslat + 1) & 0x07; + } else { + caslat_ctrl = (2 * caslat - 1) & 0x0f; + } + + debug("DDR: effective data rate is %d MHz\n", effective_data_rate); + debug("DDR: caslat SPD bit is %d, controller field is 0x%x\n", + caslat, caslat_ctrl); + + /* + * Timing Config 0. + * Avoid writing for DDR I. + */ + if (spd.mem_type == SPD_MEMTYPE_DDR2) { + unsigned char taxpd_clk = 8; /* By the book. */ + unsigned char tmrd_clk = 2; /* By the book. */ + unsigned char act_pd_exit = 2; /* Empirical? */ + unsigned char pre_pd_exit = 6; /* Empirical? */ + + ddr->timing_cfg_0 = (0 + | ((act_pd_exit & 0x7) << 20) /* ACT_PD_EXIT */ + | ((pre_pd_exit & 0x7) << 16) /* PRE_PD_EXIT */ + | ((taxpd_clk & 0xf) << 8) /* ODT_PD_EXIT */ + | ((tmrd_clk & 0xf) << 0) /* MRS_CYC */ + ); + debug("DDR: timing_cfg_0 = 0x%08x\n", ddr->timing_cfg_0); + } + + /* + * For DDR I, WRREC(Twr) and WRTORD(Twtr) are not in SPD, + * use conservative value. + * For DDR II, they are bytes 36 and 37, in quarter nanos. + */ + + if (spd.mem_type == SPD_MEMTYPE_DDR) { + twr_clk = 3; /* Clocks */ + twtr_clk = 1; /* Clocks */ + } else { + twr_clk = picos_to_clk(spd.twr * 250); + twtr_clk = picos_to_clk(spd.twtr * 250); + if (twtr_clk < 2) + twtr_clk = 2; + } + + /* + * Calculate Trfc, in picos. + * DDR I: Byte 42 straight up in ns. + * DDR II: Byte 40 and 42 swizzled some, in ns. + */ + if (spd.mem_type == SPD_MEMTYPE_DDR) { + trfc = spd.trfc * 1000; /* up to ps */ + } else { + unsigned int byte40_table_ps[8] = { + 0, + 250, + 330, + 500, + 660, + 750, + 0, + 0 + }; + + trfc = (((spd.trctrfc_ext & 0x1) * 256) + spd.trfc) * 1000 + + byte40_table_ps[(spd.trctrfc_ext >> 1) & 0x7]; + } + trfc_clk = picos_to_clk(trfc); + + /* + * Trcd, Byte 29, from quarter nanos to ps and clocks. + */ + trcd_clk = picos_to_clk(spd.trcd * 250) & 0x7; + + /* + * Convert trfc_clk to DDR controller fields. DDR I should + * fit in the REFREC field (16-19) of TIMING_CFG_1, but the + * 83xx controller has an extended REFREC field of three bits. + * The controller automatically adds 8 clocks to this value, + * so preadjust it down 8 first before splitting it up. + */ + trfc_low = (trfc_clk - 8) & 0xf; + trfc_high = ((trfc_clk - 8) >> 4) & 0x3; + + ddr->timing_cfg_1 = + (((picos_to_clk(spd.trp * 250) & 0x07) << 28 ) | /* PRETOACT */ + ((picos_to_clk(spd.tras * 1000) & 0x0f ) << 24 ) | /* ACTTOPRE */ + (trcd_clk << 20 ) | /* ACTTORW */ + (caslat_ctrl << 16 ) | /* CASLAT */ + (trfc_low << 12 ) | /* REFEC */ + ((twr_clk & 0x07) << 8) | /* WRRREC */ + ((picos_to_clk(spd.trrd * 250) & 0x07) << 4) | /* ACTTOACT */ + ((twtr_clk & 0x07) << 0) /* WRTORD */ + ); + + /* + * Additive Latency + * For DDR I, 0. + * For DDR II, with ODT enabled, use "a value" less than ACTTORW, + * which comes from Trcd, and also note that: + * add_lat + caslat must be >= 4 + */ + add_lat = 0; + if (spd.mem_type == SPD_MEMTYPE_DDR2 + && (odt_wr_cfg || odt_rd_cfg) + && (caslat < 4)) { + add_lat = 4 - caslat; + if ((add_lat + caslat) < 4) { + add_lat = 0; + } + } + + /* + * Write Data Delay + * Historically 0x2 == 4/8 clock delay. + * Empirically, 0x3 == 6/8 clock delay is suggested for DDR I 266. + */ + wr_data_delay = 2; + + /* + * Write Latency + * Read to Precharge + * Minimum CKE Pulse Width. + * Four Activate Window + */ + if (spd.mem_type == SPD_MEMTYPE_DDR) { + /* + * This is a lie. It should really be 1, but if it is + * set to 1, bits overlap into the old controller's + * otherwise unused ACSM field. If we leave it 0, then + * the HW will magically treat it as 1 for DDR 1. Oh Yea. + */ + wr_lat = 0; + + trtp_clk = 2; /* By the book. */ + cke_min_clk = 1; /* By the book. */ + four_act = 1; /* By the book. */ + + } else { + wr_lat = caslat - 1; + + /* Convert SPD value from quarter nanos to picos. */ + trtp_clk = picos_to_clk(spd.trtp * 250); + if (trtp_clk < 2) + trtp_clk = 2; + trtp_clk += add_lat; + + cke_min_clk = 3; /* By the book. */ + four_act = picos_to_clk(37500); /* By the book. 1k pages? */ + } + + /* + * Empirically set ~MCAS-to-preamble override for DDR 2. + * Your milage will vary. + */ + cpo = 0; + if (spd.mem_type == SPD_MEMTYPE_DDR2) { + if (effective_data_rate == 266) { + cpo = 0x4; /* READ_LAT + 1/2 */ + } else if (effective_data_rate == 333) { + cpo = 0x6; /* READ_LAT + 1 */ + } else if (effective_data_rate == 400) { + cpo = 0x7; /* READ_LAT + 5/4 */ + } else { + /* Automatic calibration */ + cpo = 0x1f; + } + } + + ddr->timing_cfg_2 = (0 + | ((add_lat & 0x7) << 28) /* ADD_LAT */ + | ((cpo & 0x1f) << 23) /* CPO */ + | ((wr_lat & 0x7) << 19) /* WR_LAT */ + | ((trtp_clk & 0x7) << 13) /* RD_TO_PRE */ + | ((wr_data_delay & 0x7) << 10) /* WR_DATA_DELAY */ + | ((cke_min_clk & 0x7) << 6) /* CKE_PLS */ + | ((four_act & 0x1f) << 0) /* FOUR_ACT */ + ); + + debug("DDR:timing_cfg_1=0x%08x\n", ddr->timing_cfg_1); + debug("DDR:timing_cfg_2=0x%08x\n", ddr->timing_cfg_2); + + /* Check DIMM data bus width */ + if (spd.dataw_lsb < 64) { + if (spd.mem_type == SPD_MEMTYPE_DDR) + burstlen = 0x03; /* 32 bit data bus, burst len is 8 */ + else + burstlen = 0x02; /* 32 bit data bus, burst len is 4 */ + debug("\n DDR DIMM: data bus width is 32 bit"); + } else { + burstlen = 0x02; /* Others act as 64 bit bus, burst len is 4 */ + debug("\n DDR DIMM: data bus width is 64 bit"); + } + + /* Is this an ECC DDR chip? */ + if (spd.config == 0x02) + debug(" with ECC\n"); + else + debug(" without ECC\n"); + + /* Burst length is always 4 for 64 bit data bus, 8 for 32 bit data bus, + Burst type is sequential + */ + if (spd.mem_type == SPD_MEMTYPE_DDR) { + switch (caslat) { + case 1: + ddr->sdram_mode = 0x50 | burstlen; /* CL=1.5 */ + break; + case 2: + ddr->sdram_mode = 0x20 | burstlen; /* CL=2.0 */ + break; + case 3: + ddr->sdram_mode = 0x60 | burstlen; /* CL=2.5 */ + break; + case 4: + ddr->sdram_mode = 0x30 | burstlen; /* CL=3.0 */ + break; + default: + printf("DDR:only CL 1.5, 2.0, 2.5, 3.0 is supported\n"); + return 0; + } + } else { + mode_odt_enable = 0x0; /* Default disabled */ + if (odt_wr_cfg || odt_rd_cfg) { + /* + * Bits 6 and 2 in Extended MRS(1) + * Bit 2 == 0x04 == 75 Ohm, with 2 DIMM modules. + * Bit 6 == 0x40 == 150 Ohm, with 1 DIMM module. + */ + mode_odt_enable = 0x40; /* 150 Ohm */ + } + + ddr->sdram_mode = + (0 + | (1 << (16 + 10)) /* DQS Differential disable */ + | (add_lat << (16 + 3)) /* Additive Latency in EMRS1 */ + | (mode_odt_enable << 16) /* ODT Enable in EMRS1 */ + | ((twr_clk - 1) << 9) /* Write Recovery Autopre */ + | (caslat << 4) /* caslat */ + | (burstlen << 0) /* Burst length */ + ); + } + debug("DDR:sdram_mode=0x%08x\n", ddr->sdram_mode); + + /* + * Clear EMRS2 and EMRS3. + */ + ddr->sdram_mode2 = 0; + debug("DDR: sdram_mode2 = 0x%08x\n", ddr->sdram_mode2); + + switch (spd.refresh) { + case 0x00: + case 0x80: + refresh_clk = picos_to_clk(15625000); + break; + case 0x01: + case 0x81: + refresh_clk = picos_to_clk(3900000); + break; + case 0x02: + case 0x82: + refresh_clk = picos_to_clk(7800000); + break; + case 0x03: + case 0x83: + refresh_clk = picos_to_clk(31300000); + break; + case 0x04: + case 0x84: + refresh_clk = picos_to_clk(62500000); + break; + case 0x05: + case 0x85: + refresh_clk = picos_to_clk(125000000); + break; + default: + refresh_clk = 0x512; + break; + } + + /* + * Set BSTOPRE to 0x100 for page mode + * If auto-charge is used, set BSTOPRE = 0 + */ + ddr->sdram_interval = ((refresh_clk & 0x3fff) << 16) | 0x100; + debug("DDR:sdram_interval=0x%08x\n", ddr->sdram_interval); + + /* + * SDRAM Cfg 2 + */ + odt_cfg = 0; +#ifndef CONFIG_NEVER_ASSERT_ODT_TO_CPU + if (odt_rd_cfg | odt_wr_cfg) { + odt_cfg = 0x2; /* ODT to IOs during reads */ + } +#endif + if (spd.mem_type == SPD_MEMTYPE_DDR2) { + ddr->sdram_cfg2 = (0 + | (0 << 26) /* True DQS */ + | (odt_cfg << 21) /* ODT only read */ + | (1 << 12) /* 1 refresh at a time */ + ); + + debug("DDR: sdram_cfg2 = 0x%08x\n", ddr->sdram_cfg2); + } + +#ifdef CONFIG_SYS_DDR_SDRAM_CLK_CNTL /* Optional platform specific value */ + ddr->sdram_clk_cntl = CONFIG_SYS_DDR_SDRAM_CLK_CNTL; +#endif + debug("DDR:sdram_clk_cntl=0x%08x\n", ddr->sdram_clk_cntl); + + asm("sync;isync"); + + udelay(600); + + /* + * Figure out the settings for the sdram_cfg register. Build up + * the value in 'sdram_cfg' before writing since the write into + * the register will actually enable the memory controller, and all + * settings must be done before enabling. + * + * sdram_cfg[0] = 1 (ddr sdram logic enable) + * sdram_cfg[1] = 1 (self-refresh-enable) + * sdram_cfg[5:7] = (SDRAM type = DDR SDRAM) + * 010 DDR 1 SDRAM + * 011 DDR 2 SDRAM + * sdram_cfg[12] = 0 (32_BE =0 , 64 bit bus mode) + * sdram_cfg[13] = 0 (8_BE =0, 4-beat bursts) + */ + if (spd.mem_type == SPD_MEMTYPE_DDR) + sdram_type = SDRAM_CFG_SDRAM_TYPE_DDR1; + else + sdram_type = SDRAM_CFG_SDRAM_TYPE_DDR2; + + sdram_cfg = (0 + | SDRAM_CFG_MEM_EN /* DDR enable */ + | SDRAM_CFG_SREN /* Self refresh */ + | sdram_type /* SDRAM type */ + ); + + /* sdram_cfg[3] = RD_EN - registered DIMM enable */ + if (spd.mod_attr & 0x02) + sdram_cfg |= SDRAM_CFG_RD_EN; + + /* The DIMM is 32bit width */ + if (spd.dataw_lsb < 64) { + if (spd.mem_type == SPD_MEMTYPE_DDR) + sdram_cfg |= SDRAM_CFG_32_BE | SDRAM_CFG_8_BE; + if (spd.mem_type == SPD_MEMTYPE_DDR2) + sdram_cfg |= SDRAM_CFG_32_BE; + } + + ddrc_ecc_enable = 0; + +#if defined(CONFIG_DDR_ECC) + /* Enable ECC with sdram_cfg[2] */ + if (spd.config == 0x02) { + sdram_cfg |= 0x20000000; + ddrc_ecc_enable = 1; + /* disable error detection */ + ddr->err_disable = ~ECC_ERROR_ENABLE; + /* set single bit error threshold to maximum value, + * reset counter to zero */ + ddr->err_sbe = (255 << ECC_ERROR_MAN_SBET_SHIFT) | + (0 << ECC_ERROR_MAN_SBEC_SHIFT); + } + + debug("DDR:err_disable=0x%08x\n", ddr->err_disable); + debug("DDR:err_sbe=0x%08x\n", ddr->err_sbe); +#endif + debug(" DDRC ECC mode: %s\n", ddrc_ecc_enable ? "ON":"OFF"); + +#if defined(CONFIG_DDR_2T_TIMING) + /* + * Enable 2T timing by setting sdram_cfg[16]. + */ + sdram_cfg |= SDRAM_CFG_2T_EN; +#endif + /* Enable controller, and GO! */ + ddr->sdram_cfg = sdram_cfg; + asm("sync;isync"); + udelay(500); + + debug("DDR:sdram_cfg=0x%08x\n", ddr->sdram_cfg); + return memsize; /*in MBytes*/ +} +#endif /* CONFIG_SPD_EEPROM */ + +#if defined(CONFIG_DDR_ECC) && !defined(CONFIG_ECC_INIT_VIA_DDRCONTROLLER) +/* + * Use timebase counter, get_timer() is not availabe + * at this point of initialization yet. + */ +static __inline__ unsigned long get_tbms (void) +{ + unsigned long tbl; + unsigned long tbu1, tbu2; + unsigned long ms; + unsigned long long tmp; + + ulong tbclk = get_tbclk(); + + /* get the timebase ticks */ + do { + asm volatile ("mftbu %0":"=r" (tbu1):); + asm volatile ("mftb %0":"=r" (tbl):); + asm volatile ("mftbu %0":"=r" (tbu2):); + } while (tbu1 != tbu2); + + /* convert ticks to ms */ + tmp = (unsigned long long)(tbu1); + tmp = (tmp << 32); + tmp += (unsigned long long)(tbl); + ms = tmp/(tbclk/1000); + + return ms; +} + +/* + * Initialize all of memory for ECC, then enable errors. + */ +void ddr_enable_ecc(unsigned int dram_size) +{ + volatile immap_t *immap = (immap_t *)CONFIG_SYS_IMMR; + volatile ddr83xx_t *ddr= &immap->ddr; + unsigned long t_start, t_end; + register u64 *p; + register uint size; + unsigned int pattern[2]; + + icache_enable(); + t_start = get_tbms(); + pattern[0] = 0xdeadbeef; + pattern[1] = 0xdeadbeef; + +#if defined(CONFIG_DDR_ECC_INIT_VIA_DMA) + dma_meminit(pattern[0], dram_size); +#else + debug("ddr init: CPU FP write method\n"); + size = dram_size; + for (p = 0; p < (u64*)(size); p++) { + ppcDWstore((u32*)p, pattern); + } + __asm__ __volatile__ ("sync"); +#endif + + t_end = get_tbms(); + icache_disable(); + + debug("\nREADY!!\n"); + debug("ddr init duration: %ld ms\n", t_end - t_start); + + /* Clear All ECC Errors */ + if ((ddr->err_detect & ECC_ERROR_DETECT_MME) == ECC_ERROR_DETECT_MME) + ddr->err_detect |= ECC_ERROR_DETECT_MME; + if ((ddr->err_detect & ECC_ERROR_DETECT_MBE) == ECC_ERROR_DETECT_MBE) + ddr->err_detect |= ECC_ERROR_DETECT_MBE; + if ((ddr->err_detect & ECC_ERROR_DETECT_SBE) == ECC_ERROR_DETECT_SBE) + ddr->err_detect |= ECC_ERROR_DETECT_SBE; + if ((ddr->err_detect & ECC_ERROR_DETECT_MSE) == ECC_ERROR_DETECT_MSE) + ddr->err_detect |= ECC_ERROR_DETECT_MSE; + + /* Disable ECC-Interrupts */ + ddr->err_int_en &= ECC_ERR_INT_DISABLE; + + /* Enable errors for ECC */ + ddr->err_disable &= ECC_ERROR_ENABLE; + + __asm__ __volatile__ ("sync"); + __asm__ __volatile__ ("isync"); +} +#endif /* CONFIG_DDR_ECC */ diff --git a/arch/powerpc/cpu/mpc83xx/speed.c b/arch/powerpc/cpu/mpc83xx/speed.c new file mode 100644 index 0000000..bde7e92 --- /dev/null +++ b/arch/powerpc/cpu/mpc83xx/speed.c @@ -0,0 +1,549 @@ +/* + * (C) Copyright 2000-2002 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * Copyright (C) 2004-2007 Freescale Semiconductor, Inc. + * + * 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> +#include <mpc83xx.h> +#include <command.h> +#include <asm/processor.h> + +DECLARE_GLOBAL_DATA_PTR; + +/* ----------------------------------------------------------------- */ + +typedef enum { + _unk, + _off, + _byp, + _x8, + _x4, + _x2, + _x1, + _1x, + _1_5x, + _2x, + _2_5x, + _3x +} mult_t; + +typedef struct { + mult_t core_csb_ratio; + mult_t vco_divider; +} corecnf_t; + +corecnf_t corecnf_tab[] = { + {_byp, _byp}, /* 0x00 */ + {_byp, _byp}, /* 0x01 */ + {_byp, _byp}, /* 0x02 */ + {_byp, _byp}, /* 0x03 */ + {_byp, _byp}, /* 0x04 */ + {_byp, _byp}, /* 0x05 */ + {_byp, _byp}, /* 0x06 */ + {_byp, _byp}, /* 0x07 */ + {_1x, _x2}, /* 0x08 */ + {_1x, _x4}, /* 0x09 */ + {_1x, _x8}, /* 0x0A */ + {_1x, _x8}, /* 0x0B */ + {_1_5x, _x2}, /* 0x0C */ + {_1_5x, _x4}, /* 0x0D */ + {_1_5x, _x8}, /* 0x0E */ + {_1_5x, _x8}, /* 0x0F */ + {_2x, _x2}, /* 0x10 */ + {_2x, _x4}, /* 0x11 */ + {_2x, _x8}, /* 0x12 */ + {_2x, _x8}, /* 0x13 */ + {_2_5x, _x2}, /* 0x14 */ + {_2_5x, _x4}, /* 0x15 */ + {_2_5x, _x8}, /* 0x16 */ + {_2_5x, _x8}, /* 0x17 */ + {_3x, _x2}, /* 0x18 */ + {_3x, _x4}, /* 0x19 */ + {_3x, _x8}, /* 0x1A */ + {_3x, _x8}, /* 0x1B */ +}; + +/* ----------------------------------------------------------------- */ + +/* + * + */ +int get_clocks(void) +{ + volatile immap_t *im = (immap_t *) CONFIG_SYS_IMMR; + u32 pci_sync_in; + u8 spmf; + u8 clkin_div; + u32 sccr; + u32 corecnf_tab_index; + u8 corepll; + u32 lcrr; + + u32 csb_clk; +#if defined(CONFIG_MPC834x) || defined(CONFIG_MPC831x) || defined(CONFIG_MPC837x) + u32 tsec1_clk; + u32 tsec2_clk; + u32 usbdr_clk; +#endif +#ifdef CONFIG_MPC834x + u32 usbmph_clk; +#endif + u32 core_clk; + u32 i2c1_clk; +#if !defined(CONFIG_MPC832x) + u32 i2c2_clk; +#endif +#if defined(CONFIG_MPC8315) + u32 tdm_clk; +#endif +#if defined(CONFIG_MPC837x) + u32 sdhc_clk; +#endif + u32 enc_clk; + u32 lbiu_clk; + u32 lclk_clk; + u32 mem_clk; +#if defined(CONFIG_MPC8360) + u32 mem_sec_clk; +#endif +#if defined(CONFIG_MPC8360) || defined(CONFIG_MPC832x) + u32 qepmf; + u32 qepdf; + u32 qe_clk; + u32 brg_clk; +#endif +#if defined(CONFIG_MPC837x) || defined(CONFIG_MPC831x) + u32 pciexp1_clk; + u32 pciexp2_clk; +#endif +#if defined(CONFIG_MPC837x) || defined(CONFIG_MPC8315) + u32 sata_clk; +#endif + + if ((im->sysconf.immrbar & IMMRBAR_BASE_ADDR) != (u32) im) + return -1; + + clkin_div = ((im->clk.spmr & SPMR_CKID) >> SPMR_CKID_SHIFT); + + if (im->reset.rcwh & HRCWH_PCI_HOST) { +#if defined(CONFIG_83XX_CLKIN) + pci_sync_in = CONFIG_83XX_CLKIN / (1 + clkin_div); +#else + pci_sync_in = 0xDEADBEEF; +#endif + } else { +#if defined(CONFIG_83XX_PCICLK) + pci_sync_in = CONFIG_83XX_PCICLK; +#else + pci_sync_in = 0xDEADBEEF; +#endif + } + + spmf = ((im->reset.rcwl & HRCWL_SPMF) >> HRCWL_SPMF_SHIFT); + csb_clk = pci_sync_in * (1 + clkin_div) * spmf; + + sccr = im->clk.sccr; + +#if defined(CONFIG_MPC834x) || defined(CONFIG_MPC831x) || defined(CONFIG_MPC837x) + switch ((sccr & SCCR_TSEC1CM) >> SCCR_TSEC1CM_SHIFT) { + case 0: + tsec1_clk = 0; + break; + case 1: + tsec1_clk = csb_clk; + break; + case 2: + tsec1_clk = csb_clk / 2; + break; + case 3: + tsec1_clk = csb_clk / 3; + break; + default: + /* unkown SCCR_TSEC1CM value */ + return -2; + } + + switch ((sccr & SCCR_USBDRCM) >> SCCR_USBDRCM_SHIFT) { + case 0: + usbdr_clk = 0; + break; + case 1: + usbdr_clk = csb_clk; + break; + case 2: + usbdr_clk = csb_clk / 2; + break; + case 3: + usbdr_clk = csb_clk / 3; + break; + default: + /* unkown SCCR_USBDRCM value */ + return -3; + } +#endif + +#if defined(CONFIG_MPC834x) || defined(CONFIG_MPC837x) || defined(CONFIG_MPC8315) + switch ((sccr & SCCR_TSEC2CM) >> SCCR_TSEC2CM_SHIFT) { + case 0: + tsec2_clk = 0; + break; + case 1: + tsec2_clk = csb_clk; + break; + case 2: + tsec2_clk = csb_clk / 2; + break; + case 3: + tsec2_clk = csb_clk / 3; + break; + default: + /* unkown SCCR_TSEC2CM value */ + return -4; + } +#elif defined(CONFIG_MPC8313) + tsec2_clk = tsec1_clk; + + if (!(sccr & SCCR_TSEC1ON)) + tsec1_clk = 0; + if (!(sccr & SCCR_TSEC2ON)) + tsec2_clk = 0; +#endif + +#if defined(CONFIG_MPC834x) + switch ((sccr & SCCR_USBMPHCM) >> SCCR_USBMPHCM_SHIFT) { + case 0: + usbmph_clk = 0; + break; + case 1: + usbmph_clk = csb_clk; + break; + case 2: + usbmph_clk = csb_clk / 2; + break; + case 3: + usbmph_clk = csb_clk / 3; + break; + default: + /* unkown SCCR_USBMPHCM value */ + return -5; + } + + if (usbmph_clk != 0 && usbdr_clk != 0 && usbmph_clk != usbdr_clk) { + /* if USB MPH clock is not disabled and + * USB DR clock is not disabled then + * USB MPH & USB DR must have the same rate + */ + return -6; + } +#endif + switch ((sccr & SCCR_ENCCM) >> SCCR_ENCCM_SHIFT) { + case 0: + enc_clk = 0; + break; + case 1: + enc_clk = csb_clk; + break; + case 2: + enc_clk = csb_clk / 2; + break; + case 3: + enc_clk = csb_clk / 3; + break; + default: + /* unkown SCCR_ENCCM value */ + return -7; + } + +#if defined(CONFIG_MPC837x) + switch ((sccr & SCCR_SDHCCM) >> SCCR_SDHCCM_SHIFT) { + case 0: + sdhc_clk = 0; + break; + case 1: + sdhc_clk = csb_clk; + break; + case 2: + sdhc_clk = csb_clk / 2; + break; + case 3: + sdhc_clk = csb_clk / 3; + break; + default: + /* unkown SCCR_SDHCCM value */ + return -8; + } +#endif +#if defined(CONFIG_MPC8315) + switch ((sccr & SCCR_TDMCM) >> SCCR_TDMCM_SHIFT) { + case 0: + tdm_clk = 0; + break; + case 1: + tdm_clk = csb_clk; + break; + case 2: + tdm_clk = csb_clk / 2; + break; + case 3: + tdm_clk = csb_clk / 3; + break; + default: + /* unkown SCCR_TDMCM value */ + return -8; + } +#endif + +#if defined(CONFIG_MPC834x) + i2c1_clk = tsec2_clk; +#elif defined(CONFIG_MPC8360) + i2c1_clk = csb_clk; +#elif defined(CONFIG_MPC832x) + i2c1_clk = enc_clk; +#elif defined(CONFIG_MPC831x) + i2c1_clk = enc_clk; +#elif defined(CONFIG_MPC837x) + i2c1_clk = sdhc_clk; +#endif +#if !defined(CONFIG_MPC832x) + i2c2_clk = csb_clk; /* i2c-2 clk is equal to csb clk */ +#endif + +#if defined(CONFIG_MPC837x) || defined(CONFIG_MPC831x) + switch ((sccr & SCCR_PCIEXP1CM) >> SCCR_PCIEXP1CM_SHIFT) { + case 0: + pciexp1_clk = 0; + break; + case 1: + pciexp1_clk = csb_clk; + break; + case 2: + pciexp1_clk = csb_clk / 2; + break; + case 3: + pciexp1_clk = csb_clk / 3; + break; + default: + /* unkown SCCR_PCIEXP1CM value */ + return -9; + } + + switch ((sccr & SCCR_PCIEXP2CM) >> SCCR_PCIEXP2CM_SHIFT) { + case 0: + pciexp2_clk = 0; + break; + case 1: + pciexp2_clk = csb_clk; + break; + case 2: + pciexp2_clk = csb_clk / 2; + break; + case 3: + pciexp2_clk = csb_clk / 3; + break; + default: + /* unkown SCCR_PCIEXP2CM value */ + return -10; + } +#endif + +#if defined(CONFIG_MPC837x) || defined(CONFIG_MPC8315) + switch ((sccr & SCCR_SATA1CM) >> SCCR_SATA1CM_SHIFT) { + case 0: + sata_clk = 0; + break; + case 1: + sata_clk = csb_clk; + break; + case 2: + sata_clk = csb_clk / 2; + break; + case 3: + sata_clk = csb_clk / 3; + break; + default: + /* unkown SCCR_SATACM value */ + return -11; + } +#endif + + lbiu_clk = csb_clk * + (1 + ((im->reset.rcwl & HRCWL_LBIUCM) >> HRCWL_LBIUCM_SHIFT)); + lcrr = (im->lbus.lcrr & LCRR_CLKDIV) >> LCRR_CLKDIV_SHIFT; + switch (lcrr) { + case 2: + case 4: + case 8: + lclk_clk = lbiu_clk / lcrr; + break; + default: + /* unknown lcrr */ + return -12; + } + + mem_clk = csb_clk * + (1 + ((im->reset.rcwl & HRCWL_DDRCM) >> HRCWL_DDRCM_SHIFT)); + corepll = (im->reset.rcwl & HRCWL_COREPLL) >> HRCWL_COREPLL_SHIFT; +#if defined(CONFIG_MPC8360) + mem_sec_clk = csb_clk * (1 + + ((im->reset.rcwl & HRCWL_LBIUCM) >> HRCWL_LBIUCM_SHIFT)); +#endif + + corecnf_tab_index = ((corepll & 0x1F) << 2) | ((corepll & 0x60) >> 5); + if (corecnf_tab_index > (sizeof(corecnf_tab) / sizeof(corecnf_t))) { + /* corecnf_tab_index is too high, possibly worng value */ + return -11; + } + switch (corecnf_tab[corecnf_tab_index].core_csb_ratio) { + case _byp: + case _x1: + case _1x: + core_clk = csb_clk; + break; + case _1_5x: + core_clk = (3 * csb_clk) / 2; + break; + case _2x: + core_clk = 2 * csb_clk; + break; + case _2_5x: + core_clk = (5 * csb_clk) / 2; + break; + case _3x: + core_clk = 3 * csb_clk; + break; + default: + /* unkown core to csb ratio */ + return -13; + } + +#if defined(CONFIG_MPC8360) || defined(CONFIG_MPC832x) + qepmf = (im->reset.rcwl & HRCWL_CEPMF) >> HRCWL_CEPMF_SHIFT; + qepdf = (im->reset.rcwl & HRCWL_CEPDF) >> HRCWL_CEPDF_SHIFT; + qe_clk = (pci_sync_in * qepmf) / (1 + qepdf); + brg_clk = qe_clk / 2; +#endif + + gd->csb_clk = csb_clk; +#if defined(CONFIG_MPC834x) || defined(CONFIG_MPC831x) || defined(CONFIG_MPC837x) + gd->tsec1_clk = tsec1_clk; + gd->tsec2_clk = tsec2_clk; + gd->usbdr_clk = usbdr_clk; +#endif +#if defined(CONFIG_MPC834x) + gd->usbmph_clk = usbmph_clk; +#endif +#if defined(CONFIG_MPC8315) + gd->tdm_clk = tdm_clk; +#endif +#if defined(CONFIG_MPC837x) + gd->sdhc_clk = sdhc_clk; +#endif + gd->core_clk = core_clk; + gd->i2c1_clk = i2c1_clk; +#if !defined(CONFIG_MPC832x) + gd->i2c2_clk = i2c2_clk; +#endif + gd->enc_clk = enc_clk; + gd->lbiu_clk = lbiu_clk; + gd->lclk_clk = lclk_clk; + gd->mem_clk = mem_clk; +#if defined(CONFIG_MPC8360) + gd->mem_sec_clk = mem_sec_clk; +#endif +#if defined(CONFIG_MPC8360) || defined(CONFIG_MPC832x) + gd->qe_clk = qe_clk; + gd->brg_clk = brg_clk; +#endif +#if defined(CONFIG_MPC837x) + gd->pciexp1_clk = pciexp1_clk; + gd->pciexp2_clk = pciexp2_clk; +#endif +#if defined(CONFIG_MPC837x) || defined(CONFIG_MPC8315) + gd->sata_clk = sata_clk; +#endif + gd->pci_clk = pci_sync_in; + gd->cpu_clk = gd->core_clk; + gd->bus_clk = gd->csb_clk; + return 0; + +} + +/******************************************** + * get_bus_freq + * return system bus freq in Hz + *********************************************/ +ulong get_bus_freq(ulong dummy) +{ + return gd->csb_clk; +} + +int do_clocks (cmd_tbl_t * cmdtp, int flag, int argc, char *argv[]) +{ + char buf[32]; + + printf("Clock configuration:\n"); + printf(" Core: %-4s MHz\n", strmhz(buf, gd->core_clk)); + printf(" Coherent System Bus: %-4s MHz\n", strmhz(buf, gd->csb_clk)); +#if defined(CONFIG_MPC8360) || defined(CONFIG_MPC832x) + printf(" QE: %-4s MHz\n", strmhz(buf, gd->qe_clk)); + printf(" BRG: %-4s MHz\n", strmhz(buf, gd->brg_clk)); +#endif + printf(" Local Bus Controller:%-4s MHz\n", strmhz(buf, gd->lbiu_clk)); + printf(" Local Bus: %-4s MHz\n", strmhz(buf, gd->lclk_clk)); + printf(" DDR: %-4s MHz\n", strmhz(buf, gd->mem_clk)); +#if defined(CONFIG_MPC8360) + printf(" DDR Secondary: %-4s MHz\n", strmhz(buf, gd->mem_sec_clk)); +#endif + printf(" SEC: %-4s MHz\n", strmhz(buf, gd->enc_clk)); + printf(" I2C1: %-4s MHz\n", strmhz(buf, gd->i2c1_clk)); +#if !defined(CONFIG_MPC832x) + printf(" I2C2: %-4s MHz\n", strmhz(buf, gd->i2c2_clk)); +#endif +#if defined(CONFIG_MPC8315) + printf(" TDM: %-4s MHz\n", strmhz(buf, gd->tdm_clk)); +#endif +#if defined(CONFIG_MPC837x) + printf(" SDHC: %-4s MHz\n", strmhz(buf, gd->sdhc_clk)); +#endif +#if defined(CONFIG_MPC834x) || defined(CONFIG_MPC831x) || defined(CONFIG_MPC837x) + printf(" TSEC1: %-4s MHz\n", strmhz(buf, gd->tsec1_clk)); + printf(" TSEC2: %-4s MHz\n", strmhz(buf, gd->tsec2_clk)); + printf(" USB DR: %-4s MHz\n", strmhz(buf, gd->usbdr_clk)); +#endif +#if defined(CONFIG_MPC834x) + printf(" USB MPH: %-4s MHz\n", strmhz(buf, gd->usbmph_clk)); +#endif +#if defined(CONFIG_MPC837x) + printf(" PCIEXP1: %-4s MHz\n", strmhz(buf, gd->pciexp1_clk)); + printf(" PCIEXP2: %-4s MHz\n", strmhz(buf, gd->pciexp2_clk)); +#endif +#if defined(CONFIG_MPC837x) || defined(CONFIG_MPC8315) + printf(" SATA: %-4s MHz\n", strmhz(buf, gd->sata_clk)); +#endif + return 0; +} + +U_BOOT_CMD(clocks, 1, 0, do_clocks, + "print clock configuration", + " clocks" +); diff --git a/arch/powerpc/cpu/mpc83xx/start.S b/arch/powerpc/cpu/mpc83xx/start.S new file mode 100644 index 0000000..a7c8079 --- /dev/null +++ b/arch/powerpc/cpu/mpc83xx/start.S @@ -0,0 +1,1207 @@ +/* + * Copyright (C) 1998 Dan Malek <dmalek@jlc.net> + * Copyright (C) 1999 Magnus Damm <kieraypc01.p.y.kie.era.ericsson.se> + * Copyright (C) 2000, 2001,2002 Wolfgang Denk <wd@denx.de> + * Copyright Freescale Semiconductor, Inc. 2004, 2006, 2008. + * + * 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 + */ + +/* + * U-Boot - Startup Code for MPC83xx PowerPC based Embedded Boards + */ + +#include <config.h> +#include <mpc83xx.h> +#include <timestamp.h> +#include <version.h> + +#define CONFIG_83XX 1 /* needed for Linux kernel header files*/ +#define _LINUX_CONFIG_H 1 /* avoid reading Linux autoconf.h file */ + +#include <ppc_asm.tmpl> +#include <ppc_defs.h> + +#include <asm/cache.h> +#include <asm/mmu.h> + +#ifndef CONFIG_IDENT_STRING +#define CONFIG_IDENT_STRING "MPC83XX" +#endif + +/* We don't want the MMU yet. + */ +#undef MSR_KERNEL + +/* + * Floating Point enable, Machine Check and Recoverable Interr. + */ +#ifdef DEBUG +#define MSR_KERNEL (MSR_FP|MSR_RI) +#else +#define MSR_KERNEL (MSR_FP|MSR_ME|MSR_RI) +#endif + +#if !defined(CONFIG_NAND_SPL) && !defined(CONFIG_SYS_RAMBOOT) +#define CONFIG_SYS_FLASHBOOT +#endif + +/* + * Set up GOT: Global Offset Table + * + * Use r12 to access the GOT + */ + START_GOT + GOT_ENTRY(_GOT2_TABLE_) + GOT_ENTRY(__bss_start) + GOT_ENTRY(_end) + +#ifndef CONFIG_NAND_SPL + GOT_ENTRY(_FIXUP_TABLE_) + GOT_ENTRY(_start) + GOT_ENTRY(_start_of_vectors) + GOT_ENTRY(_end_of_vectors) + GOT_ENTRY(transfer_to_handler) +#endif + END_GOT + +/* + * The Hard Reset Configuration Word (HRCW) table is in the first 64 + * (0x40) bytes of flash. It has 8 bytes, but each byte is repeated 8 + * times so the processor can fetch it out of flash whether the flash + * is 8, 16, 32, or 64 bits wide (hardware trickery). + */ + .text +#define _HRCW_TABLE_ENTRY(w) \ + .fill 8,1,(((w)>>24)&0xff); \ + .fill 8,1,(((w)>>16)&0xff); \ + .fill 8,1,(((w)>> 8)&0xff); \ + .fill 8,1,(((w) )&0xff) + + _HRCW_TABLE_ENTRY(CONFIG_SYS_HRCW_LOW) + _HRCW_TABLE_ENTRY(CONFIG_SYS_HRCW_HIGH) + +/* + * Magic number and version string - put it after the HRCW since it + * cannot be first in flash like it is in many other processors. + */ + .long 0x27051956 /* U-Boot Magic Number */ + + .globl version_string +version_string: + .ascii U_BOOT_VERSION + .ascii " (", U_BOOT_DATE, " - ", U_BOOT_TIME, ")" + .ascii " ", CONFIG_IDENT_STRING, "\0" + + .align 2 + + .globl enable_addr_trans +enable_addr_trans: + /* enable address translation */ + mfmsr r5 + ori r5, r5, (MSR_IR | MSR_DR) + mtmsr r5 + isync + blr + + .globl disable_addr_trans +disable_addr_trans: + /* disable address translation */ + mflr r4 + mfmsr r3 + andi. r0, r3, (MSR_IR | MSR_DR) + beqlr + andc r3, r3, r0 + mtspr SRR0, r4 + mtspr SRR1, r3 + rfi + + .globl get_pvr +get_pvr: + mfspr r3, PVR + blr + + .globl ppcDWstore +ppcDWstore: + lfd 1, 0(r4) + stfd 1, 0(r3) + blr + + .globl ppcDWload +ppcDWload: + lfd 1, 0(r3) + stfd 1, 0(r4) + blr + +#ifndef CONFIG_DEFAULT_IMMR +#error CONFIG_DEFAULT_IMMR must be defined +#endif /* CONFIG_SYS_DEFAULT_IMMR */ +#ifndef CONFIG_SYS_IMMR +#define CONFIG_SYS_IMMR CONFIG_DEFAULT_IMMR +#endif /* CONFIG_SYS_IMMR */ + +/* + * After configuration, a system reset exception is executed using the + * vector at offset 0x100 relative to the base set by MSR[IP]. If + * MSR[IP] is 0, the base address is 0x00000000. If MSR[IP] is 1, the + * base address is 0xfff00000. In the case of a Power On Reset or Hard + * Reset, the value of MSR[IP] is determined by the CIP field in the + * HRCW. + * + * Other bits in the HRCW set up the Base Address and Port Size in BR0. + * This determines the location of the boot ROM (flash or EPROM) in the + * processor's address space at boot time. As long as the HRCW is set up + * so that we eventually end up executing the code below when the + * processor executes the reset exception, the actual values used should + * not matter. + * + * Once we have got here, the address mask in OR0 is cleared so that the + * bottom 32K of the boot ROM is effectively repeated all throughout the + * processor's address space, after which we can jump to the absolute + * address at which the boot ROM was linked at compile time, and proceed + * to initialise the memory controller without worrying if the rug will + * be pulled out from under us, so to speak (it will be fine as long as + * we configure BR0 with the same boot ROM link address). + */ + . = EXC_OFF_SYS_RESET + + .globl _start +_start: /* time t 0 */ + li r21, BOOTFLAG_COLD /* Normal Power-On: Boot from FLASH*/ + nop + b boot_cold + + . = EXC_OFF_SYS_RESET + 0x10 + + .globl _start_warm +_start_warm: + li r21, BOOTFLAG_WARM /* Software reboot */ + b boot_warm + + +boot_cold: /* time t 3 */ + lis r4, CONFIG_DEFAULT_IMMR@h + nop +boot_warm: /* time t 5 */ + mfmsr r5 /* save msr contents */ + + /* 83xx manuals prescribe a specific sequence for updating IMMRBAR. */ + bl 1f +1: mflr r7 + + lis r3, CONFIG_SYS_IMMR@h + ori r3, r3, CONFIG_SYS_IMMR@l + + lwz r6, IMMRBAR(r4) + isync + + stw r3, IMMRBAR(r4) + lwz r6, 0(r7) /* Arbitrary external load */ + isync + + lwz r6, IMMRBAR(r3) + isync + + /* Initialise the E300 processor core */ + /*------------------------------------------*/ + +#ifdef CONFIG_NAND_SPL + /* The FCM begins execution after only the first page + * is loaded. Wait for the rest before branching + * to another flash page. + */ +1: lwz r6, 0x50b0(r3) + andi. r6, r6, 1 + beq 1b +#endif + + bl init_e300_core + +#ifdef CONFIG_SYS_FLASHBOOT + + /* Inflate flash location so it appears everywhere, calculate */ + /* the absolute address in final location of the FLASH, jump */ + /* there and deflate the flash size back to minimal size */ + /*------------------------------------------------------------*/ + bl map_flash_by_law1 + lis r4, (CONFIG_SYS_MONITOR_BASE)@h + ori r4, r4, (CONFIG_SYS_MONITOR_BASE)@l + addi r5, r4, in_flash - _start + EXC_OFF_SYS_RESET + mtlr r5 + blr +in_flash: +#if 1 /* Remapping flash with LAW0. */ + bl remap_flash_by_law0 +#endif +#endif /* CONFIG_SYS_FLASHBOOT */ + + /* setup the bats */ + bl setup_bats + sync + + /* + * Cache must be enabled here for stack-in-cache trick. + * This means we need to enable the BATS. + * This means: + * 1) for the EVB, original gt regs need to be mapped + * 2) need to have an IBAT for the 0xf region, + * we are running there! + * Cache should be turned on after BATs, since by default + * everything is write-through. + * The init-mem BAT can be reused after reloc. The old + * gt-regs BAT can be reused after board_init_f calls + * board_early_init_f (EVB only). + */ + /* enable address translation */ + bl enable_addr_trans + sync + + /* enable the data cache */ + bl dcache_enable + sync +#ifdef CONFIG_SYS_INIT_RAM_LOCK + bl lock_ram_in_cache + sync +#endif + + /* set up the stack pointer in our newly created + * cache-ram (r1) */ + lis r1, (CONFIG_SYS_INIT_RAM_ADDR + CONFIG_SYS_GBL_DATA_OFFSET)@h + ori r1, r1, (CONFIG_SYS_INIT_RAM_ADDR + CONFIG_SYS_GBL_DATA_OFFSET)@l + + li r0, 0 /* Make room for stack frame header and */ + stwu r0, -4(r1) /* clear final stack frame so that */ + stwu r0, -4(r1) /* stack backtraces terminate cleanly */ + + + /* let the C-code set up the rest */ + /* */ + /* Be careful to keep code relocatable & stack humble */ + /*------------------------------------------------------*/ + + GET_GOT /* initialize GOT access */ + + /* r3: IMMR */ + lis r3, CONFIG_SYS_IMMR@h + /* run low-level CPU init code (in Flash)*/ + bl cpu_init_f + + /* r3: BOOTFLAG */ + mr r3, r21 + /* run 1st part of board init code (in Flash)*/ + bl board_init_f + +#ifndef CONFIG_NAND_SPL +/* + * Vector Table + */ + + .globl _start_of_vectors +_start_of_vectors: + +/* Machine check */ + STD_EXCEPTION(0x200, MachineCheck, MachineCheckException) + +/* Data Storage exception. */ + STD_EXCEPTION(0x300, DataStorage, UnknownException) + +/* Instruction Storage exception. */ + STD_EXCEPTION(0x400, InstStorage, UnknownException) + +/* External Interrupt exception. */ +#ifndef FIXME + STD_EXCEPTION(0x500, ExtInterrupt, external_interrupt) +#endif + +/* Alignment exception. */ + . = 0x600 +Alignment: + EXCEPTION_PROLOG(SRR0, SRR1) + mfspr r4,DAR + stw r4,_DAR(r21) + mfspr r5,DSISR + stw r5,_DSISR(r21) + addi r3,r1,STACK_FRAME_OVERHEAD + EXC_XFER_TEMPLATE(Alignment, AlignmentException, MSR_KERNEL, COPY_EE) + +/* Program check exception */ + . = 0x700 +ProgramCheck: + EXCEPTION_PROLOG(SRR0, SRR1) + addi r3,r1,STACK_FRAME_OVERHEAD + EXC_XFER_TEMPLATE(ProgramCheck, ProgramCheckException, + MSR_KERNEL, COPY_EE) + + STD_EXCEPTION(0x800, FPUnavailable, UnknownException) + + /* I guess we could implement decrementer, and may have + * to someday for timekeeping. + */ + STD_EXCEPTION(0x900, Decrementer, timer_interrupt) + + STD_EXCEPTION(0xa00, Trap_0a, UnknownException) + STD_EXCEPTION(0xb00, Trap_0b, UnknownException) + STD_EXCEPTION(0xc00, SystemCall, UnknownException) + STD_EXCEPTION(0xd00, SingleStep, UnknownException) + + STD_EXCEPTION(0xe00, Trap_0e, UnknownException) + STD_EXCEPTION(0xf00, Trap_0f, UnknownException) + + STD_EXCEPTION(0x1000, InstructionTLBMiss, UnknownException) + STD_EXCEPTION(0x1100, DataLoadTLBMiss, UnknownException) + STD_EXCEPTION(0x1200, DataStoreTLBMiss, UnknownException) +#ifdef DEBUG + . = 0x1300 + /* + * This exception occurs when the program counter matches the + * Instruction Address Breakpoint Register (IABR). + * + * I want the cpu to halt if this occurs so I can hunt around + * with the debugger and look at things. + * + * When DEBUG is defined, both machine check enable (in the MSR) + * and checkstop reset enable (in the reset mode register) are + * turned off and so a checkstop condition will result in the cpu + * halting. + * + * I force the cpu into a checkstop condition by putting an illegal + * instruction here (at least this is the theory). + * + * well - that didnt work, so just do an infinite loop! + */ +1: b 1b +#else + STD_EXCEPTION(0x1300, InstructionBreakpoint, DebugException) +#endif + STD_EXCEPTION(0x1400, SMI, UnknownException) + + STD_EXCEPTION(0x1500, Trap_15, UnknownException) + STD_EXCEPTION(0x1600, Trap_16, UnknownException) + STD_EXCEPTION(0x1700, Trap_17, UnknownException) + STD_EXCEPTION(0x1800, Trap_18, UnknownException) + STD_EXCEPTION(0x1900, Trap_19, UnknownException) + STD_EXCEPTION(0x1a00, Trap_1a, UnknownException) + STD_EXCEPTION(0x1b00, Trap_1b, UnknownException) + STD_EXCEPTION(0x1c00, Trap_1c, UnknownException) + STD_EXCEPTION(0x1d00, Trap_1d, UnknownException) + STD_EXCEPTION(0x1e00, Trap_1e, UnknownException) + STD_EXCEPTION(0x1f00, Trap_1f, UnknownException) + STD_EXCEPTION(0x2000, Trap_20, UnknownException) + STD_EXCEPTION(0x2100, Trap_21, UnknownException) + STD_EXCEPTION(0x2200, Trap_22, UnknownException) + STD_EXCEPTION(0x2300, Trap_23, UnknownException) + STD_EXCEPTION(0x2400, Trap_24, UnknownException) + STD_EXCEPTION(0x2500, Trap_25, UnknownException) + STD_EXCEPTION(0x2600, Trap_26, UnknownException) + STD_EXCEPTION(0x2700, Trap_27, UnknownException) + STD_EXCEPTION(0x2800, Trap_28, UnknownException) + STD_EXCEPTION(0x2900, Trap_29, UnknownException) + STD_EXCEPTION(0x2a00, Trap_2a, UnknownException) + STD_EXCEPTION(0x2b00, Trap_2b, UnknownException) + STD_EXCEPTION(0x2c00, Trap_2c, UnknownException) + STD_EXCEPTION(0x2d00, Trap_2d, UnknownException) + STD_EXCEPTION(0x2e00, Trap_2e, UnknownException) + STD_EXCEPTION(0x2f00, Trap_2f, UnknownException) + + + .globl _end_of_vectors +_end_of_vectors: + + . = 0x3000 + +/* + * This code finishes saving the registers to the exception frame + * and jumps to the appropriate handler for the exception. + * Register r21 is pointer into trap frame, r1 has new stack pointer. + */ + .globl transfer_to_handler +transfer_to_handler: + stw r22,_NIP(r21) + lis r22,MSR_POW@h + andc r23,r23,r22 + stw r23,_MSR(r21) + SAVE_GPR(7, r21) + SAVE_4GPRS(8, r21) + SAVE_8GPRS(12, r21) + SAVE_8GPRS(24, r21) + mflr r23 + andi. r24,r23,0x3f00 /* get vector offset */ + stw r24,TRAP(r21) + li r22,0 + stw r22,RESULT(r21) + lwz r24,0(r23) /* virtual address of handler */ + lwz r23,4(r23) /* where to go when done */ + mtspr SRR0,r24 + mtspr SRR1,r20 + mtlr r23 + SYNC + rfi /* jump to handler, enable MMU */ + +int_return: + mfmsr r28 /* Disable interrupts */ + li r4,0 + ori r4,r4,MSR_EE + andc r28,r28,r4 + SYNC /* Some chip revs need this... */ + mtmsr r28 + SYNC + lwz r2,_CTR(r1) + lwz r0,_LINK(r1) + mtctr r2 + mtlr r0 + lwz r2,_XER(r1) + lwz r0,_CCR(r1) + mtspr XER,r2 + mtcrf 0xFF,r0 + REST_10GPRS(3, r1) + REST_10GPRS(13, r1) + REST_8GPRS(23, r1) + REST_GPR(31, r1) + lwz r2,_NIP(r1) /* Restore environment */ + lwz r0,_MSR(r1) + mtspr SRR0,r2 + mtspr SRR1,r0 + lwz r0,GPR0(r1) + lwz r2,GPR2(r1) + lwz r1,GPR1(r1) + SYNC + rfi +#endif /* !CONFIG_NAND_SPL */ + +/* + * This code initialises the E300 processor core + * (conforms to PowerPC 603e spec) + * Note: expects original MSR contents to be in r5. + */ + .globl init_e300_core +init_e300_core: /* time t 10 */ + /* Initialize machine status; enable machine check interrupt */ + /*-----------------------------------------------------------*/ + + li r3, MSR_KERNEL /* Set ME and RI flags */ + rlwimi r3, r5, 0, 25, 25 /* preserve IP bit set by HRCW */ +#ifdef DEBUG + rlwimi r3, r5, 0, 21, 22 /* debugger might set SE & BE bits */ +#endif + SYNC /* Some chip revs need this... */ + mtmsr r3 + SYNC + mtspr SRR1, r3 /* Make SRR1 match MSR */ + + + lis r3, CONFIG_SYS_IMMR@h +#if defined(CONFIG_WATCHDOG) + /* Initialise the Wathcdog values and reset it (if req) */ + /*------------------------------------------------------*/ + lis r4, CONFIG_SYS_WATCHDOG_VALUE + ori r4, r4, (SWCRR_SWEN | SWCRR_SWRI | SWCRR_SWPR) + stw r4, SWCRR(r3) + + /* and reset it */ + + li r4, 0x556C + sth r4, SWSRR@l(r3) + li r4, -0x55C7 + sth r4, SWSRR@l(r3) +#else + /* Disable Wathcdog */ + /*-------------------*/ + lwz r4, SWCRR(r3) + /* Check to see if its enabled for disabling + once disabled by SW you can't re-enable */ + andi. r4, r4, 0x4 + beq 1f + xor r4, r4, r4 + stw r4, SWCRR(r3) +1: +#endif /* CONFIG_WATCHDOG */ + +#if defined(CONFIG_MASK_AER_AO) + /* Write the Arbiter Event Enable to mask Address Only traps. */ + /* This prevents the dcbz instruction from being trapped when */ + /* HID0_ABE Address Broadcast Enable is set and the MEMORY */ + /* COHERENCY bit is set in the WIMG bits, which is often */ + /* needed for PCI operation. */ + lwz r4, 0x0808(r3) + rlwinm r0, r4, 0, ~AER_AO + stw r0, 0x0808(r3) +#endif /* CONFIG_MASK_AER_AO */ + + /* Initialize the Hardware Implementation-dependent Registers */ + /* HID0 also contains cache control */ + /* - force invalidation of data and instruction caches */ + /*------------------------------------------------------*/ + + lis r3, CONFIG_SYS_HID0_INIT@h + ori r3, r3, (CONFIG_SYS_HID0_INIT | HID0_ICFI | HID0_DCFI)@l + SYNC + mtspr HID0, r3 + + lis r3, CONFIG_SYS_HID0_FINAL@h + ori r3, r3, (CONFIG_SYS_HID0_FINAL & ~(HID0_ICFI | HID0_DCFI))@l + SYNC + mtspr HID0, r3 + + lis r3, CONFIG_SYS_HID2@h + ori r3, r3, CONFIG_SYS_HID2@l + SYNC + mtspr HID2, r3 + + /* Done! */ + /*------------------------------*/ + blr + + /* setup_bats - set them up to some initial state */ + .globl setup_bats +setup_bats: + addis r0, r0, 0x0000 + + /* IBAT 0 */ + addis r4, r0, CONFIG_SYS_IBAT0L@h + ori r4, r4, CONFIG_SYS_IBAT0L@l + addis r3, r0, CONFIG_SYS_IBAT0U@h + ori r3, r3, CONFIG_SYS_IBAT0U@l + mtspr IBAT0L, r4 + mtspr IBAT0U, r3 + + /* DBAT 0 */ + addis r4, r0, CONFIG_SYS_DBAT0L@h + ori r4, r4, CONFIG_SYS_DBAT0L@l + addis r3, r0, CONFIG_SYS_DBAT0U@h + ori r3, r3, CONFIG_SYS_DBAT0U@l + mtspr DBAT0L, r4 + mtspr DBAT0U, r3 + + /* IBAT 1 */ + addis r4, r0, CONFIG_SYS_IBAT1L@h + ori r4, r4, CONFIG_SYS_IBAT1L@l + addis r3, r0, CONFIG_SYS_IBAT1U@h + ori r3, r3, CONFIG_SYS_IBAT1U@l + mtspr IBAT1L, r4 + mtspr IBAT1U, r3 + + /* DBAT 1 */ + addis r4, r0, CONFIG_SYS_DBAT1L@h + ori r4, r4, CONFIG_SYS_DBAT1L@l + addis r3, r0, CONFIG_SYS_DBAT1U@h + ori r3, r3, CONFIG_SYS_DBAT1U@l + mtspr DBAT1L, r4 + mtspr DBAT1U, r3 + + /* IBAT 2 */ + addis r4, r0, CONFIG_SYS_IBAT2L@h + ori r4, r4, CONFIG_SYS_IBAT2L@l + addis r3, r0, CONFIG_SYS_IBAT2U@h + ori r3, r3, CONFIG_SYS_IBAT2U@l + mtspr IBAT2L, r4 + mtspr IBAT2U, r3 + + /* DBAT 2 */ + addis r4, r0, CONFIG_SYS_DBAT2L@h + ori r4, r4, CONFIG_SYS_DBAT2L@l + addis r3, r0, CONFIG_SYS_DBAT2U@h + ori r3, r3, CONFIG_SYS_DBAT2U@l + mtspr DBAT2L, r4 + mtspr DBAT2U, r3 + + /* IBAT 3 */ + addis r4, r0, CONFIG_SYS_IBAT3L@h + ori r4, r4, CONFIG_SYS_IBAT3L@l + addis r3, r0, CONFIG_SYS_IBAT3U@h + ori r3, r3, CONFIG_SYS_IBAT3U@l + mtspr IBAT3L, r4 + mtspr IBAT3U, r3 + + /* DBAT 3 */ + addis r4, r0, CONFIG_SYS_DBAT3L@h + ori r4, r4, CONFIG_SYS_DBAT3L@l + addis r3, r0, CONFIG_SYS_DBAT3U@h + ori r3, r3, CONFIG_SYS_DBAT3U@l + mtspr DBAT3L, r4 + mtspr DBAT3U, r3 + +#ifdef CONFIG_HIGH_BATS + /* IBAT 4 */ + addis r4, r0, CONFIG_SYS_IBAT4L@h + ori r4, r4, CONFIG_SYS_IBAT4L@l + addis r3, r0, CONFIG_SYS_IBAT4U@h + ori r3, r3, CONFIG_SYS_IBAT4U@l + mtspr IBAT4L, r4 + mtspr IBAT4U, r3 + + /* DBAT 4 */ + addis r4, r0, CONFIG_SYS_DBAT4L@h + ori r4, r4, CONFIG_SYS_DBAT4L@l + addis r3, r0, CONFIG_SYS_DBAT4U@h + ori r3, r3, CONFIG_SYS_DBAT4U@l + mtspr DBAT4L, r4 + mtspr DBAT4U, r3 + + /* IBAT 5 */ + addis r4, r0, CONFIG_SYS_IBAT5L@h + ori r4, r4, CONFIG_SYS_IBAT5L@l + addis r3, r0, CONFIG_SYS_IBAT5U@h + ori r3, r3, CONFIG_SYS_IBAT5U@l + mtspr IBAT5L, r4 + mtspr IBAT5U, r3 + + /* DBAT 5 */ + addis r4, r0, CONFIG_SYS_DBAT5L@h + ori r4, r4, CONFIG_SYS_DBAT5L@l + addis r3, r0, CONFIG_SYS_DBAT5U@h + ori r3, r3, CONFIG_SYS_DBAT5U@l + mtspr DBAT5L, r4 + mtspr DBAT5U, r3 + + /* IBAT 6 */ + addis r4, r0, CONFIG_SYS_IBAT6L@h + ori r4, r4, CONFIG_SYS_IBAT6L@l + addis r3, r0, CONFIG_SYS_IBAT6U@h + ori r3, r3, CONFIG_SYS_IBAT6U@l + mtspr IBAT6L, r4 + mtspr IBAT6U, r3 + + /* DBAT 6 */ + addis r4, r0, CONFIG_SYS_DBAT6L@h + ori r4, r4, CONFIG_SYS_DBAT6L@l + addis r3, r0, CONFIG_SYS_DBAT6U@h + ori r3, r3, CONFIG_SYS_DBAT6U@l + mtspr DBAT6L, r4 + mtspr DBAT6U, r3 + + /* IBAT 7 */ + addis r4, r0, CONFIG_SYS_IBAT7L@h + ori r4, r4, CONFIG_SYS_IBAT7L@l + addis r3, r0, CONFIG_SYS_IBAT7U@h + ori r3, r3, CONFIG_SYS_IBAT7U@l + mtspr IBAT7L, r4 + mtspr IBAT7U, r3 + + /* DBAT 7 */ + addis r4, r0, CONFIG_SYS_DBAT7L@h + ori r4, r4, CONFIG_SYS_DBAT7L@l + addis r3, r0, CONFIG_SYS_DBAT7U@h + ori r3, r3, CONFIG_SYS_DBAT7U@l + mtspr DBAT7L, r4 + mtspr DBAT7U, r3 +#endif + + isync + + /* invalidate all tlb's + * + * From the 603e User Manual: "The 603e provides the ability to + * invalidate a TLB entry. The TLB Invalidate Entry (tlbie) + * instruction invalidates the TLB entry indexed by the EA, and + * operates on both the instruction and data TLBs simultaneously + * invalidating four TLB entries (both sets in each TLB). The + * index corresponds to bits 15-19 of the EA. To invalidate all + * entries within both TLBs, 32 tlbie instructions should be + * issued, incrementing this field by one each time." + * + * "Note that the tlbia instruction is not implemented on the + * 603e." + * + * bits 15-19 correspond to addresses 0x00000000 to 0x0001F000 + * incrementing by 0x1000 each time. The code below is sort of + * based on code in "flush_tlbs" from arch/powerpc/kernel/head.S + * + */ + lis r3, 0 + lis r5, 2 + +1: + tlbie r3 + addi r3, r3, 0x1000 + cmp 0, 0, r3, r5 + blt 1b + + blr + +/* Cache functions. + * + * Note: requires that all cache bits in + * HID0 are in the low half word. + */ + .globl icache_enable +icache_enable: + mfspr r3, HID0 + ori r3, r3, HID0_ICE + li r4, HID0_ICFI|HID0_ILOCK + andc r3, r3, r4 + ori r4, r3, HID0_ICFI + isync + mtspr HID0, r4 /* sets enable and invalidate, clears lock */ + isync + mtspr HID0, r3 /* clears invalidate */ + blr + + .globl icache_disable +icache_disable: + mfspr r3, HID0 + lis r4, 0 + ori r4, r4, HID0_ICE|HID0_ICFI|HID0_ILOCK + andc r3, r3, r4 + isync + mtspr HID0, r3 /* clears invalidate, enable and lock */ + blr + + .globl icache_status +icache_status: + mfspr r3, HID0 + rlwinm r3, r3, (31 - HID0_ICE_SHIFT + 1), 31, 31 + blr + + .globl dcache_enable +dcache_enable: + mfspr r3, HID0 + li r5, HID0_DCFI|HID0_DLOCK + andc r3, r3, r5 + ori r3, r3, HID0_DCE + sync + mtspr HID0, r3 /* enable, no invalidate */ + blr + + .globl dcache_disable +dcache_disable: + mflr r4 + bl flush_dcache /* uses r3 and r5 */ + mfspr r3, HID0 + li r5, HID0_DCE|HID0_DLOCK + andc r3, r3, r5 + ori r5, r3, HID0_DCFI + sync + mtspr HID0, r5 /* sets invalidate, clears enable and lock */ + sync + mtspr HID0, r3 /* clears invalidate */ + mtlr r4 + blr + + .globl dcache_status +dcache_status: + mfspr r3, HID0 + rlwinm r3, r3, (31 - HID0_DCE_SHIFT + 1), 31, 31 + blr + + .globl flush_dcache +flush_dcache: + lis r3, 0 + lis r5, CONFIG_SYS_CACHELINE_SIZE +1: cmp 0, 1, r3, r5 + bge 2f + lwz r5, 0(r3) + lis r5, CONFIG_SYS_CACHELINE_SIZE + addi r3, r3, 0x4 + b 1b +2: blr + +/*-------------------------------------------------------------------*/ + +/* + * void relocate_code (addr_sp, gd, addr_moni) + * + * This "function" does not return, instead it continues in RAM + * after relocating the monitor code. + * + * r3 = dest + * r4 = src + * r5 = length in bytes + * r6 = cachelinesize + */ + .globl relocate_code +relocate_code: + mr r1, r3 /* Set new stack pointer */ + mr r9, r4 /* Save copy of Global Data pointer */ + mr r10, r5 /* Save copy of Destination Address */ + + GET_GOT + mr r3, r5 /* Destination Address */ + lis r4, CONFIG_SYS_MONITOR_BASE@h /* Source Address */ + ori r4, r4, CONFIG_SYS_MONITOR_BASE@l + lwz r5, GOT(__bss_start) + sub r5, r5, r4 + li r6, CONFIG_SYS_CACHELINE_SIZE /* Cache Line Size */ + + /* + * Fix GOT pointer: + * + * New GOT-PTR = (old GOT-PTR - CONFIG_SYS_MONITOR_BASE) + * + Destination Address + * + * Offset: + */ + sub r15, r10, r4 + + /* First our own GOT */ + add r12, r12, r15 + /* then the one used by the C code */ + add r30, r30, r15 + + /* + * Now relocate code + */ + + cmplw cr1,r3,r4 + addi r0,r5,3 + srwi. r0,r0,2 + beq cr1,4f /* In place copy is not necessary */ + beq 7f /* Protect against 0 count */ + mtctr r0 + bge cr1,2f + la r8,-4(r4) + la r7,-4(r3) + + /* copy */ +1: lwzu r0,4(r8) + stwu r0,4(r7) + bdnz 1b + + addi r0,r5,3 + srwi. r0,r0,2 + mtctr r0 + la r8,-4(r4) + la r7,-4(r3) + + /* and compare */ +20: lwzu r20,4(r8) + lwzu r21,4(r7) + xor. r22, r20, r21 + bne 30f + bdnz 20b + b 4f + + /* compare failed */ +30: li r3, 0 + blr + +2: slwi r0,r0,2 /* re copy in reverse order ... y do we needed it? */ + add r8,r4,r0 + add r7,r3,r0 +3: lwzu r0,-4(r8) + stwu r0,-4(r7) + bdnz 3b + +/* + * Now flush the cache: note that we must start from a cache aligned + * address. Otherwise we might miss one cache line. + */ +4: cmpwi r6,0 + add r5,r3,r5 + beq 7f /* Always flush prefetch queue in any case */ + subi r0,r6,1 + andc r3,r3,r0 + mr r4,r3 +5: dcbst 0,r4 + add r4,r4,r6 + cmplw r4,r5 + blt 5b + sync /* Wait for all dcbst to complete on bus */ + mr r4,r3 +6: icbi 0,r4 + add r4,r4,r6 + cmplw r4,r5 + blt 6b +7: sync /* Wait for all icbi to complete on bus */ + isync + +/* + * We are done. Do not return, instead branch to second part of board + * initialization, now running from RAM. + */ + addi r0, r10, in_ram - _start + EXC_OFF_SYS_RESET + mtlr r0 + blr + +in_ram: + + /* + * Relocation Function, r12 point to got2+0x8000 + * + * Adjust got2 pointers, no need to check for 0, this code + * already puts a few entries in the table. + */ + li r0,__got2_entries@sectoff@l + la r3,GOT(_GOT2_TABLE_) + lwz r11,GOT(_GOT2_TABLE_) + mtctr r0 + sub r11,r3,r11 + addi r3,r3,-4 +1: lwzu r0,4(r3) + cmpwi r0,0 + beq- 2f + add r0,r0,r11 + stw r0,0(r3) +2: bdnz 1b + +#ifndef CONFIG_NAND_SPL + /* + * Now adjust the fixups and the pointers to the fixups + * in case we need to move ourselves again. + */ + li r0,__fixup_entries@sectoff@l + lwz r3,GOT(_FIXUP_TABLE_) + cmpwi r0,0 + mtctr r0 + addi r3,r3,-4 + beq 4f +3: lwzu r4,4(r3) + lwzux r0,r4,r11 + add r0,r0,r11 + stw r10,0(r3) + stw r0,0(r4) + bdnz 3b +4: +#endif + +clear_bss: + /* + * Now clear BSS segment + */ + lwz r3,GOT(__bss_start) +#if defined(CONFIG_HYMOD) + /* + * For HYMOD - the environment is the very last item in flash. + * The real .bss stops just before environment starts, so only + * clear up to that point. + * + * taken from mods for FADS board + */ + lwz r4,GOT(environment) +#else + lwz r4,GOT(_end) +#endif + + cmplw 0, r3, r4 + beq 6f + + li r0, 0 +5: + stw r0, 0(r3) + addi r3, r3, 4 + cmplw 0, r3, r4 + bne 5b +6: + + mr r3, r9 /* Global Data pointer */ + mr r4, r10 /* Destination Address */ + bl board_init_r + +#ifndef CONFIG_NAND_SPL + /* + * Copy exception vector code to low memory + * + * r3: dest_addr + * r7: source address, r8: end address, r9: target address + */ + .globl trap_init +trap_init: + mflr r4 /* save link register */ + GET_GOT + lwz r7, GOT(_start) + lwz r8, GOT(_end_of_vectors) + + li r9, 0x100 /* reset vector always at 0x100 */ + + cmplw 0, r7, r8 + bgelr /* return if r7>=r8 - just in case */ +1: + lwz r0, 0(r7) + stw r0, 0(r9) + addi r7, r7, 4 + addi r9, r9, 4 + cmplw 0, r7, r8 + bne 1b + + /* + * relocate `hdlr' and `int_return' entries + */ + li r7, .L_MachineCheck - _start + EXC_OFF_SYS_RESET + li r8, Alignment - _start + EXC_OFF_SYS_RESET +2: + bl trap_reloc + addi r7, r7, 0x100 /* next exception vector */ + cmplw 0, r7, r8 + blt 2b + + li r7, .L_Alignment - _start + EXC_OFF_SYS_RESET + bl trap_reloc + + li r7, .L_ProgramCheck - _start + EXC_OFF_SYS_RESET + bl trap_reloc + + li r7, .L_FPUnavailable - _start + EXC_OFF_SYS_RESET + li r8, SystemCall - _start + EXC_OFF_SYS_RESET +3: + bl trap_reloc + addi r7, r7, 0x100 /* next exception vector */ + cmplw 0, r7, r8 + blt 3b + + li r7, .L_SingleStep - _start + EXC_OFF_SYS_RESET + li r8, _end_of_vectors - _start + EXC_OFF_SYS_RESET +4: + bl trap_reloc + addi r7, r7, 0x100 /* next exception vector */ + cmplw 0, r7, r8 + blt 4b + + mfmsr r3 /* now that the vectors have */ + lis r7, MSR_IP@h /* relocated into low memory */ + ori r7, r7, MSR_IP@l /* MSR[IP] can be turned off */ + andc r3, r3, r7 /* (if it was on) */ + SYNC /* Some chip revs need this... */ + mtmsr r3 + SYNC + + mtlr r4 /* restore link register */ + blr + +#endif /* !CONFIG_NAND_SPL */ + +#ifdef CONFIG_SYS_INIT_RAM_LOCK +lock_ram_in_cache: + /* Allocate Initial RAM in data cache. + */ + lis r3, (CONFIG_SYS_INIT_RAM_ADDR & ~31)@h + ori r3, r3, (CONFIG_SYS_INIT_RAM_ADDR & ~31)@l + li r4, ((CONFIG_SYS_INIT_RAM_END & ~31) + \ + (CONFIG_SYS_INIT_RAM_ADDR & 31) + 31) / 32 + mtctr r4 +1: + dcbz r0, r3 + addi r3, r3, 32 + bdnz 1b + + /* Lock the data cache */ + mfspr r0, HID0 + ori r0, r0, HID0_DLOCK + sync + mtspr HID0, r0 + sync + blr + +#ifndef CONFIG_NAND_SPL +.globl unlock_ram_in_cache +unlock_ram_in_cache: + /* invalidate the INIT_RAM section */ + lis r3, (CONFIG_SYS_INIT_RAM_ADDR & ~31)@h + ori r3, r3, (CONFIG_SYS_INIT_RAM_ADDR & ~31)@l + li r4, ((CONFIG_SYS_INIT_RAM_END & ~31) + \ + (CONFIG_SYS_INIT_RAM_ADDR & 31) + 31) / 32 + mtctr r4 +1: icbi r0, r3 + dcbi r0, r3 + addi r3, r3, 32 + bdnz 1b + sync /* Wait for all icbi to complete on bus */ + isync + + /* Unlock the data cache and invalidate it */ + mfspr r3, HID0 + li r5, HID0_DLOCK|HID0_DCFI + andc r3, r3, r5 /* no invalidate, unlock */ + ori r5, r3, HID0_DCFI /* invalidate, unlock */ + sync + mtspr HID0, r5 /* invalidate, unlock */ + sync + mtspr HID0, r3 /* no invalidate, unlock */ + blr +#endif /* !CONFIG_NAND_SPL */ +#endif /* CONFIG_SYS_INIT_RAM_LOCK */ + +#ifdef CONFIG_SYS_FLASHBOOT +map_flash_by_law1: + /* When booting from ROM (Flash or EPROM), clear the */ + /* Address Mask in OR0 so ROM appears everywhere */ + /*----------------------------------------------------*/ + lis r3, (CONFIG_SYS_IMMR)@h /* r3 <= CONFIG_SYS_IMMR */ + lwz r4, OR0@l(r3) + li r5, 0x7fff /* r5 <= 0x00007FFFF */ + and r4, r4, r5 + stw r4, OR0@l(r3) /* OR0 <= OR0 & 0x00007FFFF */ + + /* As MPC8349E User's Manual presented, when RCW[BMS] is set to 0, + * system will boot from 0x0000_0100, and the LBLAWBAR0[BASE_ADDR] + * reset value is 0x00000; when RCW[BMS] is set to 1, system will boot + * from 0xFFF0_0100, and the LBLAWBAR0[BASE_ADDR] reset value is + * 0xFF800. From the hard resetting to here, the processor fetched and + * executed the instructions one by one. There is not absolutely + * jumping happened. Laterly, the u-boot code has to do an absolutely + * jumping to tell the CPU instruction fetching component what the + * u-boot TEXT base address is. Because the TEXT base resides in the + * boot ROM memory space, to garantee the code can run smoothly after + * that jumping, we must map in the entire boot ROM by Local Access + * Window. Sometimes, we desire an non-0x00000 or non-0xFF800 starting + * address for boot ROM, such as 0xFE000000. In this case, the default + * LBIU Local Access Widow 0 will not cover this memory space. So, we + * need another window to map in it. + */ + lis r4, (CONFIG_SYS_FLASH_BASE)@h + ori r4, r4, (CONFIG_SYS_FLASH_BASE)@l + stw r4, LBLAWBAR1(r3) /* LBLAWBAR1 <= CONFIG_SYS_FLASH_BASE */ + + /* Store 0x80000012 + log2(CONFIG_SYS_FLASH_SIZE) into LBLAWAR1 */ + lis r4, (0x80000012)@h + ori r4, r4, (0x80000012)@l + li r5, CONFIG_SYS_FLASH_SIZE +1: srawi. r5, r5, 1 /* r5 = r5 >> 1 */ + addi r4, r4, 1 + bne 1b + + stw r4, LBLAWAR1(r3) /* LBLAWAR1 <= 8MB Flash Size */ + blr + + /* Though all the LBIU Local Access Windows and LBC Banks will be + * initialized in the C code, we'd better configure boot ROM's + * window 0 and bank 0 correctly at here. + */ +remap_flash_by_law0: + /* Initialize the BR0 with the boot ROM starting address. */ + lwz r4, BR0(r3) + li r5, 0x7FFF + and r4, r4, r5 + lis r5, (CONFIG_SYS_FLASH_BASE & 0xFFFF8000)@h + ori r5, r5, (CONFIG_SYS_FLASH_BASE & 0xFFFF8000)@l + or r5, r5, r4 + stw r5, BR0(r3) /* r5 <= (CONFIG_SYS_FLASH_BASE & 0xFFFF8000) | (BR0 & 0x00007FFF) */ + + lwz r4, OR0(r3) + lis r5, ~((CONFIG_SYS_FLASH_SIZE << 4) - 1) + or r4, r4, r5 + stw r4, OR0(r3) + + lis r4, (CONFIG_SYS_FLASH_BASE)@h + ori r4, r4, (CONFIG_SYS_FLASH_BASE)@l + stw r4, LBLAWBAR0(r3) /* LBLAWBAR0 <= CONFIG_SYS_FLASH_BASE */ + + /* Store 0x80000012 + log2(CONFIG_SYS_FLASH_SIZE) into LBLAWAR0 */ + lis r4, (0x80000012)@h + ori r4, r4, (0x80000012)@l + li r5, CONFIG_SYS_FLASH_SIZE +1: srawi. r5, r5, 1 /* r5 = r5 >> 1 */ + addi r4, r4, 1 + bne 1b + stw r4, LBLAWAR0(r3) /* LBLAWAR0 <= Flash Size */ + + + xor r4, r4, r4 + stw r4, LBLAWBAR1(r3) + stw r4, LBLAWAR1(r3) /* Off LBIU LAW1 */ + blr +#endif /* CONFIG_SYS_FLASHBOOT */ diff --git a/arch/powerpc/cpu/mpc83xx/traps.c b/arch/powerpc/cpu/mpc83xx/traps.c new file mode 100644 index 0000000..9d71b8b --- /dev/null +++ b/arch/powerpc/cpu/mpc83xx/traps.c @@ -0,0 +1,266 @@ +/* + * (C) Copyright 2000 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org) + * + * 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 + */ + +/* + * This file handles the architecture-dependent parts of hardware + * exceptions + */ + +#include <common.h> +#include <command.h> +#include <kgdb.h> +#include <asm/processor.h> +#include <asm/mpc8349_pci.h> + +DECLARE_GLOBAL_DATA_PTR; + +/* Returns 0 if exception not found and fixup otherwise. */ +extern unsigned long search_exception_table(unsigned long); + +#define END_OF_MEM (gd->bd->bi_memstart + gd->bd->bi_memsize) + +/* + * Trap & Exception support + */ + +void +print_backtrace(unsigned long *sp) +{ + int cnt = 0; + unsigned long i; + + puts ("Call backtrace: "); + while (sp) { + if ((uint)sp > END_OF_MEM) + break; + + i = sp[1]; + if (cnt++ % 7 == 0) + putc ('\n'); + printf("%08lX ", i); + if (cnt > 32) break; + sp = (unsigned long *)*sp; + } + putc ('\n'); +} + +void show_regs(struct pt_regs * regs) +{ + int i; + + printf("NIP: %08lX XER: %08lX LR: %08lX REGS: %p TRAP: %04lx DAR: %08lX\n", + regs->nip, regs->xer, regs->link, regs, regs->trap, regs->dar); + printf("MSR: %08lx EE: %01x PR: %01x FP: %01x ME: %01x IR/DR: %01x%01x\n", + regs->msr, regs->msr&MSR_EE ? 1 : 0, regs->msr&MSR_PR ? 1 : 0, + regs->msr & MSR_FP ? 1 : 0,regs->msr&MSR_ME ? 1 : 0, + regs->msr&MSR_IR ? 1 : 0, + regs->msr&MSR_DR ? 1 : 0); + + putc ('\n'); + for (i = 0; i < 32; i++) { + if ((i % 8) == 0) { + printf("GPR%02d: ", i); + } + + printf("%08lX ", regs->gpr[i]); + if ((i % 8) == 7) { + putc ('\n'); + } + } +} + + +void +_exception(int signr, struct pt_regs *regs) +{ + show_regs(regs); + print_backtrace((unsigned long *)regs->gpr[1]); + panic("Exception in kernel pc %lx signal %d",regs->nip,signr); +} + +#ifdef CONFIG_PCI +void dump_pci (void) +{ +/* + volatile immap_t *immap = (immap_t *) CONFIG_SYS_IMMR; + printf ("PCI: err status %x err mask %x err ctrl %x\n", + le32_to_cpu (immap->im_pci.pci_esr), + le32_to_cpu (immap->im_pci.pci_emr), + le32_to_cpu (immap->im_pci.pci_ecr)); + printf (" error address %x error data %x ctrl %x\n", + le32_to_cpu (immap->im_pci.pci_eacr), + le32_to_cpu (immap->im_pci.pci_edcr), + le32_to_cpu (immap->im_pci.pci_eccr)); +*/ +} +#endif + +void +MachineCheckException(struct pt_regs *regs) +{ + unsigned long fixup; + + /* Probing PCI using config cycles cause this exception + * when a device is not present. Catch it and return to + * the PCI exception handler. + */ +#ifdef CONFIG_PCI +#if 0 + volatile immap_t *immap = (immap_t *)CONFIG_SYS_IMMR; +#ifdef DEBUG + dump_pci(); +#endif + /* clear the error in the error status register */ + if(immap->im_pci.pci_esr & cpu_to_le32(PCI_ERROR_PCI_NO_RSP)) { + immap->im_pci.pci_esr = cpu_to_le32(PCI_ERROR_PCI_NO_RSP); + return; + } +#endif +#endif /* CONFIG_PCI */ + if ((fixup = search_exception_table(regs->nip)) != 0) { + regs->nip = fixup; + return; + } + +#if defined(CONFIG_CMD_KGDB) + if (debugger_exception_handler && (*debugger_exception_handler)(regs)) + return; +#endif + + puts ("Machine check in kernel mode.\n" + "Caused by (from msr): "); + printf("regs %p ",regs); + switch( regs->msr & 0x000F0000) { + case (0x80000000>>12): + puts ("Machine check signal - probably due to mm fault\n" + "with mmu off\n"); + break; + case (0x80000000>>13): + puts ("Transfer error ack signal\n"); + break; + case (0x80000000>>14): + puts ("Data parity signal\n"); + break; + case (0x80000000>>15): + puts ("Address parity signal\n"); + break; + default: + puts ("Unknown values in msr\n"); + } + show_regs(regs); + print_backtrace((unsigned long *)regs->gpr[1]); +#ifdef CONFIG_PCI + dump_pci(); +#endif + panic("machine check"); +} + +void +AlignmentException(struct pt_regs *regs) +{ +#if defined(CONFIG_CMD_KGDB) + if (debugger_exception_handler && (*debugger_exception_handler)(regs)) + return; +#endif + show_regs(regs); + print_backtrace((unsigned long *)regs->gpr[1]); + panic("Alignment Exception"); +} + +void +ProgramCheckException(struct pt_regs *regs) +{ +#if defined(CONFIG_CMD_KGDB) + if (debugger_exception_handler && (*debugger_exception_handler)(regs)) + return; +#endif + show_regs(regs); + print_backtrace((unsigned long *)regs->gpr[1]); + panic("Program Check Exception"); +} + +void +SoftEmuException(struct pt_regs *regs) +{ +#if defined(CONFIG_CMD_KGDB) + if (debugger_exception_handler && (*debugger_exception_handler)(regs)) + return; +#endif + show_regs(regs); + print_backtrace((unsigned long *)regs->gpr[1]); + panic("Software Emulation Exception"); +} + + +void +UnknownException(struct pt_regs *regs) +{ +#if defined(CONFIG_CMD_KGDB) + if (debugger_exception_handler && (*debugger_exception_handler)(regs)) + return; +#endif + printf("Bad trap at PC: %lx, SR: %lx, vector=%lx\n", + regs->nip, regs->msr, regs->trap); + _exception(0, regs); +} + +#if defined(CONFIG_CMD_BEDBUG) +extern void do_bedbug_breakpoint(struct pt_regs *); +#endif + +void +DebugException(struct pt_regs *regs) +{ + printf("Debugger trap at @ %lx\n", regs->nip ); + show_regs(regs); +#if defined(CONFIG_CMD_BEDBUG) + do_bedbug_breakpoint( regs ); +#endif +} + +/* Probe an address by reading. If not present, return -1, otherwise + * return 0. + */ +int +addr_probe(uint *addr) +{ +#if 0 + int retval; + + __asm__ __volatile__( \ + "1: lwz %0,0(%1)\n" \ + " eieio\n" \ + " li %0,0\n" \ + "2:\n" \ + ".section .fixup,\"ax\"\n" \ + "3: li %0,-1\n" \ + " b 2b\n" \ + ".section __ex_table,\"a\"\n" \ + " .align 2\n" \ + " .long 1b,3b\n" \ + ".text" \ + : "=r" (retval) : "r"(addr)); + + return (retval); +#endif + return 0; +} diff --git a/arch/powerpc/cpu/mpc83xx/u-boot.lds b/arch/powerpc/cpu/mpc83xx/u-boot.lds new file mode 100644 index 0000000..0b74a13 --- /dev/null +++ b/arch/powerpc/cpu/mpc83xx/u-boot.lds @@ -0,0 +1,121 @@ +/* + * (C) Copyright 2006 + * Wolfgang Denk, DENX Software Engineering, wd@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 + */ + +OUTPUT_ARCH(powerpc) +SECTIONS +{ + /* Read-only sections, merged into text segment: */ + . = + SIZEOF_HEADERS; + .interp : { *(.interp) } + .hash : { *(.hash) } + .dynsym : { *(.dynsym) } + .dynstr : { *(.dynstr) } + .rel.text : { *(.rel.text) } + .rela.text : { *(.rela.text) } + .rel.data : { *(.rel.data) } + .rela.data : { *(.rela.data) } + .rel.rodata : { *(.rel.rodata) } + .rela.rodata : { *(.rela.rodata) } + .rel.got : { *(.rel.got) } + .rela.got : { *(.rela.got) } + .rel.ctors : { *(.rel.ctors) } + .rela.ctors : { *(.rela.ctors) } + .rel.dtors : { *(.rel.dtors) } + .rela.dtors : { *(.rela.dtors) } + .rel.bss : { *(.rel.bss) } + .rela.bss : { *(.rela.bss) } + .rel.plt : { *(.rel.plt) } + .rela.plt : { *(.rela.plt) } + .init : { *(.init) } + .plt : { *(.plt) } + .text : + { + arch/powerpc/cpu/mpc83xx/start.o (.text) + *(.text) + *(.got1) + . = ALIGN(16); + *(.eh_frame) + *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.rodata*))) + } + .fini : { *(.fini) } =0 + .ctors : { *(.ctors) } + .dtors : { *(.dtors) } + + /* Read-write section, merged into data segment: */ + . = (. + 0x0FFF) & 0xFFFFF000; + _erotext = .; + PROVIDE (erotext = .); + .reloc : + { + *(.got) + _GOT2_TABLE_ = .; + *(.got2) + _FIXUP_TABLE_ = .; + *(.fixup) + } + __got2_entries = (_FIXUP_TABLE_ - _GOT2_TABLE_) >> 2; + __fixup_entries = (. - _FIXUP_TABLE_) >> 2; + + .data : + { + *(.data) + *(.data1) + *(.sdata) + *(.sdata2) + *(.dynamic) + CONSTRUCTORS + } + _edata = .; + PROVIDE (edata = .); + + . = .; + __u_boot_cmd_start = .; + .u_boot_cmd : { *(.u_boot_cmd) } + __u_boot_cmd_end = .; + + + . = .; + __start___ex_table = .; + __ex_table : { *(__ex_table) } + __stop___ex_table = .; + + . = ALIGN(4096); + __init_begin = .; + .text.init : { *(.text.init) } + .data.init : { *(.data.init) } + . = ALIGN(4096); + __init_end = .; + + __bss_start = .; + .bss (NOLOAD) : + { + *(.sbss) *(.scommon) + *(.dynbss) + *(.bss) + *(COMMON) + . = ALIGN(4); + } + _end = . ; + PROVIDE (end = .); +} +ENTRY(_start) diff --git a/arch/powerpc/cpu/mpc85xx/Makefile b/arch/powerpc/cpu/mpc85xx/Makefile new file mode 100644 index 0000000..f064fee --- /dev/null +++ b/arch/powerpc/cpu/mpc85xx/Makefile @@ -0,0 +1,95 @@ +# +# (C) Copyright 2006 +# Wolfgang Denk, DENX Software Engineering, wd@denx.de. +# +# (C) Copyright 2002,2003 Motorola Inc. +# Xianghua Xiao,X.Xiao@motorola.com +# +# 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 $(TOPDIR)/config.mk + +LIB = $(obj)lib$(CPU).a + +START = start.o resetvec.o +SOBJS-$(CONFIG_MP) += release.o +SOBJS = $(SOBJS-y) + +COBJS-$(CONFIG_CPM2) += commproc.o + +# supports ddr1 +COBJS-$(CONFIG_MPC8540) += ddr-gen1.o +COBJS-$(CONFIG_MPC8560) += ddr-gen1.o +COBJS-$(CONFIG_MPC8541) += ddr-gen1.o +COBJS-$(CONFIG_MPC8555) += ddr-gen1.o + +# supports ddr1/2 +COBJS-$(CONFIG_MPC8548) += ddr-gen2.o +COBJS-$(CONFIG_MPC8568) += ddr-gen2.o +COBJS-$(CONFIG_MPC8544) += ddr-gen2.o + +# supports ddr1/2/3 +COBJS-$(CONFIG_MPC8572) += ddr-gen3.o +COBJS-$(CONFIG_MPC8536) += ddr-gen3.o +COBJS-$(CONFIG_MPC8569) += ddr-gen3.o +COBJS-$(CONFIG_P1011) += ddr-gen3.o +COBJS-$(CONFIG_P1012) += ddr-gen3.o +COBJS-$(CONFIG_P1013) += ddr-gen3.o +COBJS-$(CONFIG_P1020) += ddr-gen3.o +COBJS-$(CONFIG_P1021) += ddr-gen3.o +COBJS-$(CONFIG_P1022) += ddr-gen3.o +COBJS-$(CONFIG_P2010) += ddr-gen3.o +COBJS-$(CONFIG_P2020) += ddr-gen3.o +COBJS-$(CONFIG_PPC_P4080) += ddr-gen3.o + +COBJS-$(CONFIG_CPM2) += ether_fcc.o +COBJS-$(CONFIG_OF_LIBFDT) += fdt.o +COBJS-$(CONFIG_MP) += mp.o +COBJS-$(CONFIG_MPC8536) += mpc8536_serdes.o +COBJS-$(CONFIG_PCI) += pci.o +COBJS-$(CONFIG_QE) += qe_io.o +COBJS-$(CONFIG_CPM2) += serial_scc.o + +COBJS = $(COBJS-y) +COBJS += cpu.o +COBJS += cpu_init.o +COBJS += cpu_init_early.o +COBJS += interrupts.o +COBJS += speed.o +COBJS += tlb.o +COBJS += traps.o + +SRCS := $(START:.o=.S) $(SOBJS:.o=.S) $(COBJS:.o=.c) +OBJS := $(addprefix $(obj),$(SOBJS) $(COBJS)) +START := $(addprefix $(obj),$(START)) + +all: $(obj).depend $(START) $(LIB) + +$(LIB): $(OBJS) + $(AR) $(ARFLAGS) $@ $(OBJS) + +######################################################################### + +# defines $(obj).depend target +include $(SRCTREE)/rules.mk + +sinclude $(obj).depend + +######################################################################### diff --git a/arch/powerpc/cpu/mpc85xx/commproc.c b/arch/powerpc/cpu/mpc85xx/commproc.c new file mode 100644 index 0000000..f0fd1cb --- /dev/null +++ b/arch/powerpc/cpu/mpc85xx/commproc.c @@ -0,0 +1,205 @@ +/* + * Adapted for Motorola MPC8560 chips + * Xianghua Xiao <x.xiao@motorola.com> + * + * This file is based on "arch/powerpc/8260_io/commproc.c" - here is it's + * copyright notice: + * + * General Purpose functions for the global management of the + * 8220 Communication Processor Module. + * Copyright (c) 1999 Dan Malek (dmalek@jlc.net) + * Copyright (c) 2000 MontaVista Software, Inc (source@mvista.com) + * 2.3.99 Updates + * Copyright (c) 2003 Motorola,Inc. + * + * In addition to the individual control of the communication + * channels, there are a few functions that globally affect the + * communication processor. + * + * Buffer descriptors must be allocated from the dual ported memory + * space. The allocator for that is here. When the communication + * process is reset, we reclaim the memory available. There is + * currently no deallocator for this memory. + */ +#include <common.h> +#include <asm/cpm_85xx.h> + +DECLARE_GLOBAL_DATA_PTR; + +/* + * because we have stack and init data in dual port ram + * we must reduce the size + */ +#undef CPM_DATAONLY_SIZE +#define CPM_DATAONLY_SIZE ((uint)(8 * 1024) - CPM_DATAONLY_BASE) + +void +m8560_cpm_reset(void) +{ + volatile ccsr_cpm_t *cpm = (ccsr_cpm_t *)CONFIG_SYS_MPC85xx_CPM_ADDR; + volatile ulong count; + + gd = (gd_t *) (CONFIG_SYS_INIT_RAM_ADDR + CONFIG_SYS_GBL_DATA_OFFSET); + + /* Reclaim the DP memory for our use. + */ + gd->dp_alloc_base = CPM_DATAONLY_BASE; + gd->dp_alloc_top = gd->dp_alloc_base + CPM_DATAONLY_SIZE; + + /* + * Reset CPM + */ + cpm->im_cpm_cp.cpcr = CPM_CR_RST; + count = 0; + do { /* Spin until command processed */ + __asm__ __volatile__ ("eieio"); + } while ((cpm->im_cpm_cp.cpcr & CPM_CR_FLG) && ++count < 1000000); +} + +/* Allocate some memory from the dual ported ram. + * To help protocols with object alignment restrictions, we do that + * if they ask. + */ +uint +m8560_cpm_dpalloc(uint size, uint align) +{ + volatile ccsr_cpm_t *cpm = (ccsr_cpm_t *)CONFIG_SYS_MPC85xx_CPM_ADDR; + uint retloc; + uint align_mask, off; + uint savebase; + + align_mask = align - 1; + savebase = gd->dp_alloc_base; + + if ((off = (gd->dp_alloc_base & align_mask)) != 0) + gd->dp_alloc_base += (align - off); + + if ((off = size & align_mask) != 0) + size += align - off; + + if ((gd->dp_alloc_base + size) >= gd->dp_alloc_top) { + gd->dp_alloc_base = savebase; + panic("m8560_cpm_dpalloc: ran out of dual port ram!"); + } + + retloc = gd->dp_alloc_base; + gd->dp_alloc_base += size; + + memset((void *)&(cpm->im_dprambase[retloc]), 0, size); + + return(retloc); +} + +/* We also own one page of host buffer space for the allocation of + * UART "fifos" and the like. + */ +uint +m8560_cpm_hostalloc(uint size, uint align) +{ + /* the host might not even have RAM yet - just use dual port RAM */ + return (m8560_cpm_dpalloc(size, align)); +} + +/* Set a baud rate generator. This needs lots of work. There are + * eight BRGs, which can be connected to the CPM channels or output + * as clocks. The BRGs are in two different block of internal + * memory mapped space. + * The baud rate clock is the system clock divided by something. + * It was set up long ago during the initial boot phase and is + * is given to us. + * Baud rate clocks are zero-based in the driver code (as that maps + * to port numbers). Documentation uses 1-based numbering. + */ +#define BRG_INT_CLK gd->brg_clk +#define BRG_UART_CLK ((BRG_INT_CLK + 15) / 16) + +/* This function is used by UARTS, or anything else that uses a 16x + * oversampled clock. + */ +void +m8560_cpm_setbrg(uint brg, uint rate) +{ + volatile ccsr_cpm_t *cpm = (ccsr_cpm_t *)CONFIG_SYS_MPC85xx_CPM_ADDR; + volatile uint *bp; + + /* This is good enough to get SMCs running..... + */ + if (brg < 4) { + bp = (uint *)&(cpm->im_cpm_brg1.brgc1); + } + else { + bp = (uint *)&(cpm->im_cpm_brg2.brgc5); + brg -= 4; + } + bp += brg; + *bp = (((((BRG_UART_CLK+rate-1)/rate)-1)&0xfff)<<1)|CPM_BRG_EN; +} + +/* This function is used to set high speed synchronous baud rate + * clocks. + */ +void +m8560_cpm_fastbrg(uint brg, uint rate, int div16) +{ + volatile ccsr_cpm_t *cpm = (ccsr_cpm_t *)CONFIG_SYS_MPC85xx_CPM_ADDR; + volatile uint *bp; + + /* This is good enough to get SMCs running..... + */ + if (brg < 4) { + bp = (uint *)&(cpm->im_cpm_brg1.brgc1); + } + else { + bp = (uint *)&(cpm->im_cpm_brg2.brgc5); + brg -= 4; + } + bp += brg; + *bp = (((((BRG_INT_CLK+rate-1)/rate)-1)&0xfff)<<1)|CPM_BRG_EN; + if (div16) + *bp |= CPM_BRG_DIV16; +} + +/* This function is used to set baud rate generators using an external + * clock source and 16x oversampling. + */ + +void +m8560_cpm_extcbrg(uint brg, uint rate, uint extclk, int pinsel) +{ + volatile ccsr_cpm_t *cpm = (ccsr_cpm_t *)CONFIG_SYS_MPC85xx_CPM_ADDR; + volatile uint *bp; + + if (brg < 4) { + bp = (uint *)&(cpm->im_cpm_brg1.brgc1); + } + else { + bp = (uint *)&(cpm->im_cpm_brg2.brgc5); + brg -= 4; + } + bp += brg; + *bp = ((((((extclk/16)+rate-1)/rate)-1)&0xfff)<<1)|CPM_BRG_EN; + if (pinsel == 0) + *bp |= CPM_BRG_EXTC_CLK3_9; + else + *bp |= CPM_BRG_EXTC_CLK5_15; +} + +#ifdef CONFIG_POST + +void post_word_store (ulong a) +{ + volatile ulong *save_addr = + (volatile ulong *)(CONFIG_SYS_IMMR + CPM_POST_WORD_ADDR); + + *save_addr = a; +} + +ulong post_word_load (void) +{ + volatile ulong *save_addr = + (volatile ulong *)(CONFIG_SYS_IMMR + CPM_POST_WORD_ADDR); + + return *save_addr; +} + +#endif /* CONFIG_POST */ diff --git a/arch/powerpc/cpu/mpc85xx/config.mk b/arch/powerpc/cpu/mpc85xx/config.mk new file mode 100644 index 0000000..f07d920 --- /dev/null +++ b/arch/powerpc/cpu/mpc85xx/config.mk @@ -0,0 +1,35 @@ +# +# (C) Copyright 2002,2003 Motorola Inc. +# Xianghua Xiao, X.Xiao@motorola.com +# +# 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 +# + +PLATFORM_RELFLAGS += -fPIC -meabi + +PLATFORM_CPPFLAGS += -ffixed-r2 -Wa,-me500 -msoft-float -mno-string + +# -mspe=yes is needed to have -mno-spe accepted by a buggy GCC; +# see "[PATCH,rs6000] make -mno-spe work as expected" on +# http://gcc.gnu.org/ml/gcc-patches/2008-04/msg00311.html +PLATFORM_CPPFLAGS +=$(call cc-option,-mspe=yes) +PLATFORM_CPPFLAGS +=$(call cc-option,-mno-spe) + +# Use default linker script. Board port can override in board/*/config.mk +LDSCRIPT := $(SRCTREE)/arch/powerpc/cpu/mpc85xx/u-boot.lds diff --git a/arch/powerpc/cpu/mpc85xx/cpu.c b/arch/powerpc/cpu/mpc85xx/cpu.c new file mode 100644 index 0000000..0cc6e03 --- /dev/null +++ b/arch/powerpc/cpu/mpc85xx/cpu.c @@ -0,0 +1,330 @@ +/* + * Copyright 2004,2007-2009 Freescale Semiconductor, Inc. + * (C) Copyright 2002, 2003 Motorola Inc. + * Xianghua Xiao (X.Xiao@motorola.com) + * + * (C) Copyright 2000 + * Wolfgang Denk, DENX Software Engineering, wd@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 <config.h> +#include <common.h> +#include <watchdog.h> +#include <command.h> +#include <fsl_esdhc.h> +#include <asm/cache.h> +#include <asm/io.h> + +DECLARE_GLOBAL_DATA_PTR; + +int checkcpu (void) +{ + sys_info_t sysinfo; + uint pvr, svr; + uint fam; + uint ver; + uint major, minor; + struct cpu_type *cpu; + char buf1[32], buf2[32]; +#ifdef CONFIG_DDR_CLK_FREQ + volatile ccsr_gur_t *gur = (void *)(CONFIG_SYS_MPC85xx_GUTS_ADDR); +#ifdef CONFIG_FSL_CORENET + u32 ddr_sync = ((gur->rcwsr[5]) & FSL_CORENET_RCWSR5_DDR_SYNC) + >> FSL_CORENET_RCWSR5_DDR_SYNC_SHIFT; +#else + u32 ddr_ratio = ((gur->porpllsr) & MPC85xx_PORPLLSR_DDR_RATIO) + >> MPC85xx_PORPLLSR_DDR_RATIO_SHIFT; +#endif +#else +#ifdef CONFIG_FSL_CORENET + u32 ddr_sync = 0; +#else + u32 ddr_ratio = 0; +#endif +#endif /* CONFIG_DDR_CLK_FREQ */ + int i; + + svr = get_svr(); + major = SVR_MAJ(svr); +#ifdef CONFIG_MPC8536 + major &= 0x7; /* the msb of this nibble is a mfg code */ +#endif + minor = SVR_MIN(svr); + + if (cpu_numcores() > 1) { +#ifndef CONFIG_MP + puts("Unicore software on multiprocessor system!!\n" + "To enable mutlticore build define CONFIG_MP\n"); +#endif + volatile ccsr_pic_t *pic = (void *)(CONFIG_SYS_MPC85xx_PIC_ADDR); + printf("CPU%d: ", pic->whoami); + } else { + puts("CPU: "); + } + + cpu = gd->cpu; + + puts(cpu->name); + if (IS_E_PROCESSOR(svr)) + puts("E"); + + printf(", Version: %d.%d, (0x%08x)\n", major, minor, svr); + + pvr = get_pvr(); + fam = PVR_FAM(pvr); + ver = PVR_VER(pvr); + major = PVR_MAJ(pvr); + minor = PVR_MIN(pvr); + + printf("Core: "); + switch (fam) { + case PVR_FAM(PVR_85xx): + puts("E500"); + break; + default: + puts("Unknown"); + break; + } + + if (PVR_MEM(pvr) == 0x03) + puts("MC"); + + printf(", Version: %d.%d, (0x%08x)\n", major, minor, pvr); + + get_sys_info(&sysinfo); + + puts("Clock Configuration:"); + for (i = 0; i < cpu_numcores(); i++) { + if (!(i & 3)) + printf ("\n "); + printf("CPU%d:%-4s MHz, ", + i,strmhz(buf1, sysinfo.freqProcessor[i])); + } + printf("\n CCB:%-4s MHz,\n", strmhz(buf1, sysinfo.freqSystemBus)); + +#ifdef CONFIG_FSL_CORENET + if (ddr_sync == 1) { + printf(" DDR:%-4s MHz (%s MT/s data rate) " + "(Synchronous), ", + strmhz(buf1, sysinfo.freqDDRBus/2), + strmhz(buf2, sysinfo.freqDDRBus)); + } else { + printf(" DDR:%-4s MHz (%s MT/s data rate) " + "(Asynchronous), ", + strmhz(buf1, sysinfo.freqDDRBus/2), + strmhz(buf2, sysinfo.freqDDRBus)); + } +#else + switch (ddr_ratio) { + case 0x0: + printf(" DDR:%-4s MHz (%s MT/s data rate), ", + strmhz(buf1, sysinfo.freqDDRBus/2), + strmhz(buf2, sysinfo.freqDDRBus)); + break; + case 0x7: + printf(" DDR:%-4s MHz (%s MT/s data rate) " + "(Synchronous), ", + strmhz(buf1, sysinfo.freqDDRBus/2), + strmhz(buf2, sysinfo.freqDDRBus)); + break; + default: + printf(" DDR:%-4s MHz (%s MT/s data rate) " + "(Asynchronous), ", + strmhz(buf1, sysinfo.freqDDRBus/2), + strmhz(buf2, sysinfo.freqDDRBus)); + break; + } +#endif + + if (sysinfo.freqLocalBus > LCRR_CLKDIV) { + printf("LBC:%-4s MHz\n", strmhz(buf1, sysinfo.freqLocalBus)); + } else { + printf("LBC: unknown (LCRR[CLKDIV] = 0x%02lx)\n", + sysinfo.freqLocalBus); + } + +#ifdef CONFIG_CPM2 + printf("CPM: %s MHz\n", strmhz(buf1, sysinfo.freqSystemBus)); +#endif + +#ifdef CONFIG_QE + printf(" QE:%-4s MHz\n", strmhz(buf1, sysinfo.freqQE)); +#endif + +#ifdef CONFIG_SYS_DPAA_FMAN + for (i = 0; i < CONFIG_SYS_NUM_FMAN; i++) { + printf(" FMAN%d: %s MHz\n", i, + strmhz(buf1, sysinfo.freqFMan[i])); + } +#endif + +#ifdef CONFIG_SYS_DPAA_PME + printf(" PME: %s MHz\n", strmhz(buf1, sysinfo.freqPME)); +#endif + + puts("L1: D-cache 32 kB enabled\n I-cache 32 kB enabled\n"); + + return 0; +} + + +/* ------------------------------------------------------------------------- */ + +int do_reset (cmd_tbl_t *cmdtp, bd_t *bd, int flag, int argc, char *argv[]) +{ +/* Everything after the first generation of PQ3 parts has RSTCR */ +#if defined(CONFIG_MPC8540) || defined(CONFIG_MPC8541) || \ + defined(CONFIG_MPC8555) || defined(CONFIG_MPC8560) + unsigned long val, msr; + + /* + * Initiate hard reset in debug control register DBCR0 + * Make sure MSR[DE] = 1. This only resets the core. + */ + msr = mfmsr (); + msr |= MSR_DE; + mtmsr (msr); + + val = mfspr(DBCR0); + val |= 0x70000000; + mtspr(DBCR0,val); +#else + volatile ccsr_gur_t *gur = (void *)(CONFIG_SYS_MPC85xx_GUTS_ADDR); + out_be32(&gur->rstcr, 0x2); /* HRESET_REQ */ + udelay(100); +#endif + + return 1; +} + + +/* + * Get timebase clock frequency + */ +unsigned long get_tbclk (void) +{ +#ifdef CONFIG_FSL_CORENET + return (gd->bus_clk + 8) / 16; +#else + return (gd->bus_clk + 4UL)/8UL; +#endif +} + + +#if defined(CONFIG_WATCHDOG) +void +watchdog_reset(void) +{ + int re_enable = disable_interrupts(); + reset_85xx_watchdog(); + if (re_enable) enable_interrupts(); +} + +void +reset_85xx_watchdog(void) +{ + /* + * Clear TSR(WIS) bit by writing 1 + */ + unsigned long val; + val = mfspr(SPRN_TSR); + val |= TSR_WIS; + mtspr(SPRN_TSR, val); +} +#endif /* CONFIG_WATCHDOG */ + +/* + * Configures a UPM. The function requires the respective MxMR to be set + * before calling this function. "size" is the number or entries, not a sizeof. + */ +void upmconfig (uint upm, uint * table, uint size) +{ + int i, mdr, mad, old_mad = 0; + volatile u32 *mxmr; + volatile ccsr_lbc_t *lbc = (void *)(CONFIG_SYS_MPC85xx_LBC_ADDR); + volatile u32 *brp,*orp; + volatile u8* dummy = NULL; + int upmmask; + + switch (upm) { + case UPMA: + mxmr = &lbc->mamr; + upmmask = BR_MS_UPMA; + break; + case UPMB: + mxmr = &lbc->mbmr; + upmmask = BR_MS_UPMB; + break; + case UPMC: + mxmr = &lbc->mcmr; + upmmask = BR_MS_UPMC; + break; + default: + printf("%s: Bad UPM index %d to configure\n", __FUNCTION__, upm); + hang(); + } + + /* Find the address for the dummy write transaction */ + for (brp = &lbc->br0, orp = &lbc->or0, i = 0; i < 8; + i++, brp += 2, orp += 2) { + + /* Look for a valid BR with selected UPM */ + if ((in_be32(brp) & (BR_V | BR_MSEL)) == (BR_V | upmmask)) { + dummy = (volatile u8*)(in_be32(brp) & BR_BA); + break; + } + } + + if (i == 8) { + printf("Error: %s() could not find matching BR\n", __FUNCTION__); + hang(); + } + + for (i = 0; i < size; i++) { + /* 1 */ + out_be32(mxmr, (in_be32(mxmr) & 0x4fffffc0) | MxMR_OP_WARR | i); + /* 2 */ + out_be32(&lbc->mdr, table[i]); + /* 3 */ + mdr = in_be32(&lbc->mdr); + /* 4 */ + *(volatile u8 *)dummy = 0; + /* 5 */ + do { + mad = in_be32(mxmr) & MxMR_MAD_MSK; + } while (mad <= old_mad && !(!mad && i == (size-1))); + old_mad = mad; + } + out_be32(mxmr, (in_be32(mxmr) & 0x4fffffc0) | MxMR_OP_NORM); +} + +/* + * Initializes on-chip MMC controllers. + * to override, implement board_mmc_init() + */ +int cpu_mmc_init(bd_t *bis) +{ +#ifdef CONFIG_FSL_ESDHC + return fsl_esdhc_mmc_init(bis); +#else + return 0; +#endif +} diff --git a/arch/powerpc/cpu/mpc85xx/cpu_init.c b/arch/powerpc/cpu/mpc85xx/cpu_init.c new file mode 100644 index 0000000..e0126d3 --- /dev/null +++ b/arch/powerpc/cpu/mpc85xx/cpu_init.c @@ -0,0 +1,405 @@ +/* + * Copyright 2007-2009 Freescale Semiconductor, Inc. + * + * (C) Copyright 2003 Motorola Inc. + * Modified by Xianghua Xiao, X.Xiao@motorola.com + * + * (C) Copyright 2000 + * Wolfgang Denk, DENX Software Engineering, wd@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> +#include <watchdog.h> +#include <asm/processor.h> +#include <ioports.h> +#include <asm/io.h> +#include <asm/mmu.h> +#include <asm/fsl_law.h> +#include "mp.h" + +DECLARE_GLOBAL_DATA_PTR; + +#ifdef CONFIG_MPC8536 +extern void fsl_serdes_init(void); +#endif + +#ifdef CONFIG_QE +extern qe_iop_conf_t qe_iop_conf_tab[]; +extern void qe_config_iopin(u8 port, u8 pin, int dir, + int open_drain, int assign); +extern void qe_init(uint qe_base); +extern void qe_reset(void); + +static void config_qe_ioports(void) +{ + u8 port, pin; + int dir, open_drain, assign; + int i; + + for (i = 0; qe_iop_conf_tab[i].assign != QE_IOP_TAB_END; i++) { + port = qe_iop_conf_tab[i].port; + pin = qe_iop_conf_tab[i].pin; + dir = qe_iop_conf_tab[i].dir; + open_drain = qe_iop_conf_tab[i].open_drain; + assign = qe_iop_conf_tab[i].assign; + qe_config_iopin(port, pin, dir, open_drain, assign); + } +} +#endif + +#ifdef CONFIG_CPM2 +void config_8560_ioports (volatile ccsr_cpm_t * cpm) +{ + int portnum; + + for (portnum = 0; portnum < 4; portnum++) { + uint pmsk = 0, + ppar = 0, + psor = 0, + pdir = 0, + podr = 0, + pdat = 0; + iop_conf_t *iopc = (iop_conf_t *) & iop_conf_tab[portnum][0]; + iop_conf_t *eiopc = iopc + 32; + uint msk = 1; + + /* + * NOTE: + * index 0 refers to pin 31, + * index 31 refers to pin 0 + */ + while (iopc < eiopc) { + if (iopc->conf) { + pmsk |= msk; + if (iopc->ppar) + ppar |= msk; + if (iopc->psor) + psor |= msk; + if (iopc->pdir) + pdir |= msk; + if (iopc->podr) + podr |= msk; + if (iopc->pdat) + pdat |= msk; + } + + msk <<= 1; + iopc++; + } + + if (pmsk != 0) { + volatile ioport_t *iop = ioport_addr (cpm, portnum); + uint tpmsk = ~pmsk; + + /* + * the (somewhat confused) paragraph at the + * bottom of page 35-5 warns that there might + * be "unknown behaviour" when programming + * PSORx and PDIRx, if PPARx = 1, so I + * decided this meant I had to disable the + * dedicated function first, and enable it + * last. + */ + iop->ppar &= tpmsk; + iop->psor = (iop->psor & tpmsk) | psor; + iop->podr = (iop->podr & tpmsk) | podr; + iop->pdat = (iop->pdat & tpmsk) | pdat; + iop->pdir = (iop->pdir & tpmsk) | pdir; + iop->ppar |= ppar; + } + } +} +#endif + +/* + * Breathe some life into the CPU... + * + * Set up the memory map + * initialize a bunch of registers + */ + +#ifdef CONFIG_FSL_CORENET +static void corenet_tb_init(void) +{ + volatile ccsr_rcpm_t *rcpm = + (void *)(CONFIG_SYS_FSL_CORENET_RCPM_ADDR); + volatile ccsr_pic_t *pic = + (void *)(CONFIG_SYS_MPC85xx_PIC_ADDR); + u32 whoami = in_be32(&pic->whoami); + + /* Enable the timebase register for this core */ + out_be32(&rcpm->ctbenrl, (1 << whoami)); +} +#endif + +void cpu_init_f (void) +{ + volatile ccsr_lbc_t *memctl = (void *)(CONFIG_SYS_MPC85xx_LBC_ADDR); + extern void m8560_cpm_reset (void); +#ifdef CONFIG_MPC8548 + ccsr_local_ecm_t *ecm = (void *)(CONFIG_SYS_MPC85xx_ECM_ADDR); + uint svr = get_svr(); + + /* + * CPU2 errata workaround: A core hang possible while executing + * a msync instruction and a snoopable transaction from an I/O + * master tagged to make quick forward progress is present. + * Fixed in silicon rev 2.1. + */ + if ((SVR_MAJ(svr) == 1) || ((SVR_MAJ(svr) == 2 && SVR_MIN(svr) == 0x0))) + out_be32(&ecm->eebpcr, in_be32(&ecm->eebpcr) | (1 << 16)); +#endif + + disable_tlb(14); + disable_tlb(15); + +#ifdef CONFIG_CPM2 + config_8560_ioports((ccsr_cpm_t *)CONFIG_SYS_MPC85xx_CPM_ADDR); +#endif + + /* Map banks 0 and 1 to the FLASH banks 0 and 1 at preliminary + * addresses - these have to be modified later when FLASH size + * has been determined + */ +#if defined(CONFIG_SYS_OR0_REMAP) + memctl->or0 = CONFIG_SYS_OR0_REMAP; +#endif +#if defined(CONFIG_SYS_OR1_REMAP) + memctl->or1 = CONFIG_SYS_OR1_REMAP; +#endif + + /* now restrict to preliminary range */ + /* if cs1 is already set via debugger, leave cs0/cs1 alone */ + if (! memctl->br1 & 1) { +#if defined(CONFIG_SYS_BR0_PRELIM) && defined(CONFIG_SYS_OR0_PRELIM) + memctl->br0 = CONFIG_SYS_BR0_PRELIM; + memctl->or0 = CONFIG_SYS_OR0_PRELIM; +#endif + +#if defined(CONFIG_SYS_BR1_PRELIM) && defined(CONFIG_SYS_OR1_PRELIM) + memctl->or1 = CONFIG_SYS_OR1_PRELIM; + memctl->br1 = CONFIG_SYS_BR1_PRELIM; +#endif + } + +#if defined(CONFIG_SYS_BR2_PRELIM) && defined(CONFIG_SYS_OR2_PRELIM) + memctl->or2 = CONFIG_SYS_OR2_PRELIM; + memctl->br2 = CONFIG_SYS_BR2_PRELIM; +#endif + +#if defined(CONFIG_SYS_BR3_PRELIM) && defined(CONFIG_SYS_OR3_PRELIM) + memctl->or3 = CONFIG_SYS_OR3_PRELIM; + memctl->br3 = CONFIG_SYS_BR3_PRELIM; +#endif + +#if defined(CONFIG_SYS_BR4_PRELIM) && defined(CONFIG_SYS_OR4_PRELIM) + memctl->or4 = CONFIG_SYS_OR4_PRELIM; + memctl->br4 = CONFIG_SYS_BR4_PRELIM; +#endif + +#if defined(CONFIG_SYS_BR5_PRELIM) && defined(CONFIG_SYS_OR5_PRELIM) + memctl->or5 = CONFIG_SYS_OR5_PRELIM; + memctl->br5 = CONFIG_SYS_BR5_PRELIM; +#endif + +#if defined(CONFIG_SYS_BR6_PRELIM) && defined(CONFIG_SYS_OR6_PRELIM) + memctl->or6 = CONFIG_SYS_OR6_PRELIM; + memctl->br6 = CONFIG_SYS_BR6_PRELIM; +#endif + +#if defined(CONFIG_SYS_BR7_PRELIM) && defined(CONFIG_SYS_OR7_PRELIM) + memctl->or7 = CONFIG_SYS_OR7_PRELIM; + memctl->br7 = CONFIG_SYS_BR7_PRELIM; +#endif + +#if defined(CONFIG_CPM2) + m8560_cpm_reset(); +#endif +#ifdef CONFIG_QE + /* Config QE ioports */ + config_qe_ioports(); +#endif +#if defined(CONFIG_MPC8536) + fsl_serdes_init(); +#endif +#if defined(CONFIG_FSL_DMA) + dma_init(); +#endif +#ifdef CONFIG_FSL_CORENET + corenet_tb_init(); +#endif + init_used_tlb_cams(); +} + + +/* + * Initialize L2 as cache. + * + * The newer 8548, etc, parts have twice as much cache, but + * use the same bit-encoding as the older 8555, etc, parts. + * + */ + +int cpu_init_r(void) +{ + puts ("L2: "); + +#if defined(CONFIG_L2_CACHE) + volatile ccsr_l2cache_t *l2cache = (void *)CONFIG_SYS_MPC85xx_L2_ADDR; + volatile uint cache_ctl; + uint svr, ver; + uint l2srbar; + u32 l2siz_field; + + svr = get_svr(); + ver = SVR_SOC_VER(svr); + + asm("msync;isync"); + cache_ctl = l2cache->l2ctl; + +#if defined(CONFIG_SYS_RAMBOOT) && defined(CONFIG_SYS_INIT_L2_ADDR) + if (cache_ctl & MPC85xx_L2CTL_L2E) { + /* Clear L2 SRAM memory-mapped base address */ + out_be32(&l2cache->l2srbar0, 0x0); + out_be32(&l2cache->l2srbar1, 0x0); + + /* set MBECCDIS=0, SBECCDIS=0 */ + clrbits_be32(&l2cache->l2errdis, + (MPC85xx_L2ERRDIS_MBECC | + MPC85xx_L2ERRDIS_SBECC)); + + /* set L2E=0, L2SRAM=0 */ + clrbits_be32(&l2cache->l2ctl, + (MPC85xx_L2CTL_L2E | + MPC85xx_L2CTL_L2SRAM_ENTIRE)); + } +#endif + + l2siz_field = (cache_ctl >> 28) & 0x3; + + switch (l2siz_field) { + case 0x0: + printf(" unknown size (0x%08x)\n", cache_ctl); + return -1; + break; + case 0x1: + if (ver == SVR_8540 || ver == SVR_8560 || + ver == SVR_8541 || ver == SVR_8541_E || + ver == SVR_8555 || ver == SVR_8555_E) { + puts("128 KB "); + /* set L2E=1, L2I=1, & L2BLKSZ=1 (128 Kbyte) */ + cache_ctl = 0xc4000000; + } else { + puts("256 KB "); + cache_ctl = 0xc0000000; /* set L2E=1, L2I=1, & L2SRAM=0 */ + } + break; + case 0x2: + if (ver == SVR_8540 || ver == SVR_8560 || + ver == SVR_8541 || ver == SVR_8541_E || + ver == SVR_8555 || ver == SVR_8555_E) { + puts("256 KB "); + /* set L2E=1, L2I=1, & L2BLKSZ=2 (256 Kbyte) */ + cache_ctl = 0xc8000000; + } else { + puts ("512 KB "); + /* set L2E=1, L2I=1, & L2SRAM=0 */ + cache_ctl = 0xc0000000; + } + break; + case 0x3: + puts("1024 KB "); + /* set L2E=1, L2I=1, & L2SRAM=0 */ + cache_ctl = 0xc0000000; + break; + } + + if (l2cache->l2ctl & MPC85xx_L2CTL_L2E) { + puts("already enabled"); + l2srbar = l2cache->l2srbar0; +#ifdef CONFIG_SYS_INIT_L2_ADDR + if (l2cache->l2ctl & MPC85xx_L2CTL_L2SRAM_ENTIRE + && l2srbar >= CONFIG_SYS_FLASH_BASE) { + l2srbar = CONFIG_SYS_INIT_L2_ADDR; + l2cache->l2srbar0 = l2srbar; + printf("moving to 0x%08x", CONFIG_SYS_INIT_L2_ADDR); + } +#endif /* CONFIG_SYS_INIT_L2_ADDR */ + puts("\n"); + } else { + asm("msync;isync"); + l2cache->l2ctl = cache_ctl; /* invalidate & enable */ + asm("msync;isync"); + puts("enabled\n"); + } +#elif defined(CONFIG_BACKSIDE_L2_CACHE) + u32 l2cfg0 = mfspr(SPRN_L2CFG0); + + /* invalidate the L2 cache */ + mtspr(SPRN_L2CSR0, (L2CSR0_L2FI|L2CSR0_L2LFC)); + while (mfspr(SPRN_L2CSR0) & (L2CSR0_L2FI|L2CSR0_L2LFC)) + ; + +#ifdef CONFIG_SYS_CACHE_STASHING + /* set stash id to (coreID) * 2 + 32 + L2 (1) */ + mtspr(SPRN_L2CSR1, (32 + 1)); +#endif + + /* enable the cache */ + mtspr(SPRN_L2CSR0, CONFIG_SYS_INIT_L2CSR0); + + if (CONFIG_SYS_INIT_L2CSR0 & L2CSR0_L2E) { + while (!(mfspr(SPRN_L2CSR0) & L2CSR0_L2E)) + ; + printf("%d KB enabled\n", (l2cfg0 & 0x3fff) * 64); + } +#else + puts("disabled\n"); +#endif +#ifdef CONFIG_QE + uint qe_base = CONFIG_SYS_IMMR + 0x00080000; /* QE immr base */ + qe_init(qe_base); + qe_reset(); +#endif + +#if defined(CONFIG_MP) + setup_mp(); +#endif + return 0; +} + +extern void setup_ivors(void); + +void arch_preboot_os(void) +{ + u32 msr; + + /* + * We are changing interrupt offsets and are about to boot the OS so + * we need to make sure we disable all async interrupts. EE is already + * disabled by the time we get called. + */ + msr = mfmsr(); + msr &= ~(MSR_ME|MSR_CE|MSR_DE); + mtmsr(msr); + + setup_ivors(); +} diff --git a/arch/powerpc/cpu/mpc85xx/cpu_init_early.c b/arch/powerpc/cpu/mpc85xx/cpu_init_early.c new file mode 100644 index 0000000..32aa94b --- /dev/null +++ b/arch/powerpc/cpu/mpc85xx/cpu_init_early.c @@ -0,0 +1,112 @@ +/* + * Copyright 2009 Freescale Semiconductor, 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include <common.h> +#include <asm/processor.h> +#include <asm/mmu.h> +#include <asm/fsl_law.h> + +DECLARE_GLOBAL_DATA_PTR; + +#if (CONFIG_SYS_CCSRBAR_DEFAULT != CONFIG_SYS_CCSRBAR_PHYS) +#ifdef CONFIG_FSL_CORENET +static void setup_ccsrbar(void) +{ + u32 temp; + volatile u32 *ccsr_virt = (volatile u32 *)(CONFIG_SYS_CCSRBAR + 0x1000); + volatile ccsr_local_t *ccm; + + /* + * We can't call set_law() because we haven't moved + * CCSR yet. + */ + ccm = (void *)ccsr_virt; + + out_be32(&ccm->law[0].lawbarh, + (u64)CONFIG_SYS_CCSRBAR_PHYS >> 32); + out_be32(&ccm->law[0].lawbarl, (u32)CONFIG_SYS_CCSRBAR_PHYS); + out_be32(&ccm->law[0].lawar, + LAW_EN | (0x1e << 20) | LAW_SIZE_4K); + + in_be32((u32 *)(ccsr_virt + 0)); + in_be32((u32 *)(ccsr_virt + 1)); + isync(); + + ccm = (void *)CONFIG_SYS_CCSRBAR; + /* Now use the temporary LAW to move CCSR */ + out_be32(&ccm->ccsrbarh, (u64)CONFIG_SYS_CCSRBAR_PHYS >> 32); + out_be32(&ccm->ccsrbarl, (u32)CONFIG_SYS_CCSRBAR_PHYS); + out_be32(&ccm->ccsrar, CCSRAR_C); + temp = in_be32(&ccm->ccsrar); + disable_law(0); +} +#else +static void setup_ccsrbar(void) +{ + u32 temp; + volatile u32 *ccsr_virt = (volatile u32 *)(CONFIG_SYS_CCSRBAR + 0x1000); + + temp = in_be32(ccsr_virt); + out_be32(ccsr_virt, CONFIG_SYS_CCSRBAR_PHYS >> 12); + temp = in_be32((volatile u32 *)CONFIG_SYS_CCSRBAR); +} +#endif +#endif + +/* We run cpu_init_early_f in AS = 1 */ +void cpu_init_early_f(void) +{ + u32 mas0, mas1, mas2, mas3, mas7; + int i; + + /* Pointer is writable since we allocated a register for it */ + gd = (gd_t *) (CONFIG_SYS_INIT_RAM_ADDR + CONFIG_SYS_GBL_DATA_OFFSET); + + /* + * Clear initial global data + * we don't use memset so we can share this code with NAND_SPL + */ + for (i = 0; i < sizeof(gd_t); i++) + ((char *)gd)[i] = 0; + + mas0 = MAS0_TLBSEL(0) | MAS0_ESEL(0); + mas1 = MAS1_VALID | MAS1_TID(0) | MAS1_TS | MAS1_TSIZE(BOOKE_PAGESZ_4K); + mas2 = FSL_BOOKE_MAS2(CONFIG_SYS_CCSRBAR, MAS2_I|MAS2_G); + mas3 = FSL_BOOKE_MAS3(CONFIG_SYS_CCSRBAR_PHYS, 0, MAS3_SW|MAS3_SR); + mas7 = FSL_BOOKE_MAS7(CONFIG_SYS_CCSRBAR_PHYS); + + write_tlb(mas0, mas1, mas2, mas3, mas7); + + /* set up CCSR if we want it moved */ +#if (CONFIG_SYS_CCSRBAR_DEFAULT != CONFIG_SYS_CCSRBAR_PHYS) + mas0 = MAS0_TLBSEL(0) | MAS0_ESEL(1); + /* mas1 is the same as above */ + mas2 = FSL_BOOKE_MAS2(CONFIG_SYS_CCSRBAR + 0x1000, MAS2_I|MAS2_G); + mas3 = FSL_BOOKE_MAS3(CONFIG_SYS_CCSRBAR_DEFAULT, 0, MAS3_SW|MAS3_SR); + mas7 = FSL_BOOKE_MAS7(CONFIG_SYS_CCSRBAR_DEFAULT); + + write_tlb(mas0, mas1, mas2, mas3, mas7); + + setup_ccsrbar(); +#endif + + init_laws(); + invalidate_tlb(0); + init_tlbs(); +} diff --git a/arch/powerpc/cpu/mpc85xx/cpu_init_nand.c b/arch/powerpc/cpu/mpc85xx/cpu_init_nand.c new file mode 100644 index 0000000..184cca4 --- /dev/null +++ b/arch/powerpc/cpu/mpc85xx/cpu_init_nand.c @@ -0,0 +1,63 @@ +/* + * Copyright 2009 Freescale Semiconductor, Inc. + * + * 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> +#include <asm/io.h> + +void cpu_init_f(void) +{ + ccsr_lbc_t *lbc = (void *)(CONFIG_SYS_MPC85xx_LBC_ADDR); + + /* + * LCRR - Clock Ratio Register - set up local bus timing + * when needed + */ + out_be32(&lbc->lcrr, LCRR_DBYP | LCRR_CLKDIV_8); + +#if defined(CONFIG_NAND_BR_PRELIM) && defined(CONFIG_NAND_OR_PRELIM) + out_be32(&lbc->br0, CONFIG_NAND_BR_PRELIM); + out_be32(&lbc->or0, CONFIG_NAND_OR_PRELIM); +#else +#error CONFIG_NAND_BR_PRELIM, CONFIG_NAND_OR_PRELIM must be defined +#endif + +#if defined(CONFIG_SYS_RAMBOOT) && defined(CONFIG_SYS_INIT_L2_ADDR) + ccsr_l2cache_t *l2cache = (void *)CONFIG_SYS_MPC85xx_L2_ADDR; + char *l2srbar; + int i; + + out_be32(&l2cache->l2srbar0, CONFIG_SYS_INIT_L2_ADDR); + + /* set MBECCDIS=1, SBECCDIS=1 */ + out_be32(&l2cache->l2errdis, + (MPC85xx_L2ERRDIS_MBECC | MPC85xx_L2ERRDIS_SBECC)); + + /* set L2E=1 & L2SRAM=001 */ + out_be32(&l2cache->l2ctl, + (MPC85xx_L2CTL_L2E | MPC85xx_L2CTL_L2SRAM_ENTIRE)); + + /* Initialize L2 SRAM to zero */ + l2srbar = (char *)CONFIG_SYS_INIT_L2_ADDR; + for (i = 0; i < CONFIG_SYS_L2_SIZE; i++) + l2srbar[i] = 0; +#endif +} diff --git a/arch/powerpc/cpu/mpc85xx/ddr-gen1.c b/arch/powerpc/cpu/mpc85xx/ddr-gen1.c new file mode 100644 index 0000000..54437dd --- /dev/null +++ b/arch/powerpc/cpu/mpc85xx/ddr-gen1.c @@ -0,0 +1,89 @@ +/* + * Copyright 2008 Freescale Semiconductor, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * Version 2 as published by the Free Software Foundation. + */ + +#include <common.h> +#include <asm/io.h> +#include <asm/fsl_ddr_sdram.h> + +#if (CONFIG_CHIP_SELECTS_PER_CTRL > 4) +#error Invalid setting for CONFIG_CHIP_SELECTS_PER_CTRL +#endif + +void fsl_ddr_set_memctl_regs(const fsl_ddr_cfg_regs_t *regs, + unsigned int ctrl_num) +{ + unsigned int i; + volatile ccsr_ddr_t *ddr = (void *)CONFIG_SYS_MPC85xx_DDR_ADDR; + + if (ctrl_num != 0) { + printf("%s unexpected ctrl_num = %u\n", __FUNCTION__, ctrl_num); + return; + } + + for (i = 0; i < CONFIG_CHIP_SELECTS_PER_CTRL; i++) { + if (i == 0) { + out_be32(&ddr->cs0_bnds, regs->cs[i].bnds); + out_be32(&ddr->cs0_config, regs->cs[i].config); + + } else if (i == 1) { + out_be32(&ddr->cs1_bnds, regs->cs[i].bnds); + out_be32(&ddr->cs1_config, regs->cs[i].config); + + } else if (i == 2) { + out_be32(&ddr->cs2_bnds, regs->cs[i].bnds); + out_be32(&ddr->cs2_config, regs->cs[i].config); + + } else if (i == 3) { + out_be32(&ddr->cs3_bnds, regs->cs[i].bnds); + out_be32(&ddr->cs3_config, regs->cs[i].config); + } + } + + out_be32(&ddr->timing_cfg_1, regs->timing_cfg_1); + out_be32(&ddr->timing_cfg_2, regs->timing_cfg_2); + out_be32(&ddr->sdram_mode, regs->ddr_sdram_mode); + out_be32(&ddr->sdram_interval, regs->ddr_sdram_interval); +#if defined(CONFIG_MPC8555) || defined(CONFIG_MPC8541) + out_be32(&ddr->sdram_clk_cntl, regs->ddr_sdram_clk_cntl); +#endif + + /* + * 200 painful micro-seconds must elapse between + * the DDR clock setup and the DDR config enable. + */ + udelay(200); + asm volatile("sync;isync"); + + out_be32(&ddr->sdram_cfg, regs->ddr_sdram_cfg); + + asm("sync;isync;msync"); + udelay(500); +} + +#if defined(CONFIG_DDR_ECC) && !defined(CONFIG_ECC_INIT_VIA_DDRCONTROLLER) +/* + * Initialize all of memory for ECC, then enable errors. + */ + +void +ddr_enable_ecc(unsigned int dram_size) +{ + volatile ccsr_ddr_t *ddr= (void *)(CONFIG_SYS_MPC85xx_DDR_ADDR); + + dma_meminit(CONFIG_MEM_INIT_VALUE, dram_size); + + /* + * Enable errors for ECC. + */ + debug("DMA DDR: err_disable = 0x%08x\n", ddr->err_disable); + ddr->err_disable = 0x00000000; + asm("sync;isync;msync"); + debug("DMA DDR: err_disable = 0x%08x\n", ddr->err_disable); +} + +#endif /* CONFIG_DDR_ECC && ! CONFIG_ECC_INIT_VIA_DDRCONTROLLER */ diff --git a/arch/powerpc/cpu/mpc85xx/ddr-gen2.c b/arch/powerpc/cpu/mpc85xx/ddr-gen2.c new file mode 100644 index 0000000..655f99c --- /dev/null +++ b/arch/powerpc/cpu/mpc85xx/ddr-gen2.c @@ -0,0 +1,74 @@ +/* + * Copyright 2008 Freescale Semiconductor, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * Version 2 as published by the Free Software Foundation. + */ + +#include <common.h> +#include <asm/io.h> +#include <asm/fsl_ddr_sdram.h> + +#if (CONFIG_CHIP_SELECTS_PER_CTRL > 4) +#error Invalid setting for CONFIG_CHIP_SELECTS_PER_CTRL +#endif + +void fsl_ddr_set_memctl_regs(const fsl_ddr_cfg_regs_t *regs, + unsigned int ctrl_num) +{ + unsigned int i; + volatile ccsr_ddr_t *ddr = (void *)CONFIG_SYS_MPC85xx_DDR_ADDR; + + if (ctrl_num) { + printf("%s unexpected ctrl_num = %u\n", __FUNCTION__, ctrl_num); + return; + } + + for (i = 0; i < CONFIG_CHIP_SELECTS_PER_CTRL; i++) { + if (i == 0) { + out_be32(&ddr->cs0_bnds, regs->cs[i].bnds); + out_be32(&ddr->cs0_config, regs->cs[i].config); + + } else if (i == 1) { + out_be32(&ddr->cs1_bnds, regs->cs[i].bnds); + out_be32(&ddr->cs1_config, regs->cs[i].config); + + } else if (i == 2) { + out_be32(&ddr->cs2_bnds, regs->cs[i].bnds); + out_be32(&ddr->cs2_config, regs->cs[i].config); + + } else if (i == 3) { + out_be32(&ddr->cs3_bnds, regs->cs[i].bnds); + out_be32(&ddr->cs3_config, regs->cs[i].config); + } + } + + out_be32(&ddr->timing_cfg_3, regs->timing_cfg_3); + out_be32(&ddr->timing_cfg_0, regs->timing_cfg_0); + out_be32(&ddr->timing_cfg_1, regs->timing_cfg_1); + out_be32(&ddr->timing_cfg_2, regs->timing_cfg_2); + out_be32(&ddr->sdram_cfg_2, regs->ddr_sdram_cfg_2); + out_be32(&ddr->sdram_mode, regs->ddr_sdram_mode); + out_be32(&ddr->sdram_mode_2, regs->ddr_sdram_mode_2); + out_be32(&ddr->sdram_md_cntl, regs->ddr_sdram_md_cntl); + out_be32(&ddr->sdram_interval, regs->ddr_sdram_interval); + out_be32(&ddr->sdram_data_init, regs->ddr_data_init); + out_be32(&ddr->sdram_clk_cntl, regs->ddr_sdram_clk_cntl); + out_be32(&ddr->init_addr, regs->ddr_init_addr); + out_be32(&ddr->init_ext_addr, regs->ddr_init_ext_addr); + + /* + * 200 painful micro-seconds must elapse between + * the DDR clock setup and the DDR config enable. + */ + udelay(200); + asm volatile("sync;isync"); + + out_be32(&ddr->sdram_cfg, regs->ddr_sdram_cfg); + + /* Poll DDR_SDRAM_CFG_2[D_INIT] bit until auto-data init is done. */ + while (in_be32(&ddr->sdram_cfg_2) & 0x10) { + udelay(10000); /* throttle polling rate */ + } +} diff --git a/arch/powerpc/cpu/mpc85xx/ddr-gen3.c b/arch/powerpc/cpu/mpc85xx/ddr-gen3.c new file mode 100644 index 0000000..0691ca4 --- /dev/null +++ b/arch/powerpc/cpu/mpc85xx/ddr-gen3.c @@ -0,0 +1,116 @@ +/* + * Copyright 2008 Freescale Semiconductor, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * Version 2 as published by the Free Software Foundation. + */ + +#include <common.h> +#include <asm/io.h> +#include <asm/fsl_ddr_sdram.h> + +#if (CONFIG_CHIP_SELECTS_PER_CTRL > 4) +#error Invalid setting for CONFIG_CHIP_SELECTS_PER_CTRL +#endif + +void fsl_ddr_set_memctl_regs(const fsl_ddr_cfg_regs_t *regs, + unsigned int ctrl_num) +{ + unsigned int i; + volatile ccsr_ddr_t *ddr; + u32 temp_sdram_cfg; + + switch (ctrl_num) { + case 0: + ddr = (void *)CONFIG_SYS_MPC85xx_DDR_ADDR; + break; + case 1: + ddr = (void *)CONFIG_SYS_MPC85xx_DDR2_ADDR; + break; + default: + printf("%s unexpected ctrl_num = %u\n", __FUNCTION__, ctrl_num); + return; + } + + for (i = 0; i < CONFIG_CHIP_SELECTS_PER_CTRL; i++) { + if (i == 0) { + out_be32(&ddr->cs0_bnds, regs->cs[i].bnds); + out_be32(&ddr->cs0_config, regs->cs[i].config); + out_be32(&ddr->cs0_config_2, regs->cs[i].config_2); + + } else if (i == 1) { + out_be32(&ddr->cs1_bnds, regs->cs[i].bnds); + out_be32(&ddr->cs1_config, regs->cs[i].config); + out_be32(&ddr->cs1_config_2, regs->cs[i].config_2); + + } else if (i == 2) { + out_be32(&ddr->cs2_bnds, regs->cs[i].bnds); + out_be32(&ddr->cs2_config, regs->cs[i].config); + out_be32(&ddr->cs2_config_2, regs->cs[i].config_2); + + } else if (i == 3) { + out_be32(&ddr->cs3_bnds, regs->cs[i].bnds); + out_be32(&ddr->cs3_config, regs->cs[i].config); + out_be32(&ddr->cs3_config_2, regs->cs[i].config_2); + } + } + + out_be32(&ddr->timing_cfg_3, regs->timing_cfg_3); + out_be32(&ddr->timing_cfg_0, regs->timing_cfg_0); + out_be32(&ddr->timing_cfg_1, regs->timing_cfg_1); + out_be32(&ddr->timing_cfg_2, regs->timing_cfg_2); + out_be32(&ddr->sdram_cfg_2, regs->ddr_sdram_cfg_2); + out_be32(&ddr->sdram_mode, regs->ddr_sdram_mode); + out_be32(&ddr->sdram_mode_2, regs->ddr_sdram_mode_2); + out_be32(&ddr->sdram_md_cntl, regs->ddr_sdram_md_cntl); + out_be32(&ddr->sdram_interval, regs->ddr_sdram_interval); + out_be32(&ddr->sdram_data_init, regs->ddr_data_init); + out_be32(&ddr->sdram_clk_cntl, regs->ddr_sdram_clk_cntl); + out_be32(&ddr->init_addr, regs->ddr_init_addr); + out_be32(&ddr->init_ext_addr, regs->ddr_init_ext_addr); + + out_be32(&ddr->timing_cfg_4, regs->timing_cfg_4); + out_be32(&ddr->timing_cfg_5, regs->timing_cfg_5); + out_be32(&ddr->ddr_zq_cntl, regs->ddr_zq_cntl); + out_be32(&ddr->ddr_wrlvl_cntl, regs->ddr_wrlvl_cntl); + out_be32(&ddr->ddr_sr_cntr, regs->ddr_sr_cntr); + out_be32(&ddr->ddr_sdram_rcw_1, regs->ddr_sdram_rcw_1); + out_be32(&ddr->ddr_sdram_rcw_2, regs->ddr_sdram_rcw_2); + + /* Set, but do not enable the memory */ + temp_sdram_cfg = regs->ddr_sdram_cfg; + temp_sdram_cfg &= ~(SDRAM_CFG_MEM_EN); + out_be32(&ddr->sdram_cfg, temp_sdram_cfg); + /* + * For 8572 DDR1 erratum - DDR controller may enter illegal state + * when operatiing in 32-bit bus mode with 4-beat bursts, + * This erratum does not affect DDR3 mode, only for DDR2 mode. + */ +#ifdef CONFIG_MPC8572 + if ((((in_be32(&ddr->sdram_cfg) >> 24) & 0x7) == SDRAM_TYPE_DDR2) + && in_be32(&ddr->sdram_cfg) & 0x80000) { + /* set DEBUG_1[31] */ + u32 temp = in_be32(&ddr->debug_1); + out_be32(&ddr->debug_1, temp | 1); + } +#endif + + /* + * 500 painful micro-seconds must elapse between + * the DDR clock setup and the DDR config enable. + * DDR2 need 200 us, and DDR3 need 500 us from spec, + * we choose the max, that is 500 us for all of case. + */ + udelay(500); + asm volatile("sync;isync"); + + /* Let the controller go */ + temp_sdram_cfg = in_be32(&ddr->sdram_cfg); + out_be32(&ddr->sdram_cfg, temp_sdram_cfg | SDRAM_CFG_MEM_EN); + + /* Poll DDR_SDRAM_CFG_2[D_INIT] bit until auto-data init is done. */ + while (in_be32(&ddr->sdram_cfg_2) & 0x10) { + udelay(10000); /* throttle polling rate */ + } +} diff --git a/arch/powerpc/cpu/mpc85xx/ether_fcc.c b/arch/powerpc/cpu/mpc85xx/ether_fcc.c new file mode 100644 index 0000000..5f1414d --- /dev/null +++ b/arch/powerpc/cpu/mpc85xx/ether_fcc.c @@ -0,0 +1,469 @@ +/* + * MPC8560 FCC Fast Ethernet + * Copyright (c) 2003 Motorola,Inc. + * Xianghua Xiao, (X.Xiao@motorola.com) + * + * Copyright (c) 2000 MontaVista Software, Inc. Dan Malek (dmalek@jlc.net) + * + * (C) Copyright 2000 Sysgo Real-Time Solutions, GmbH <www.elinos.com> + * Marius Groeger <mgroeger@sysgo.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 + */ + +/* + * MPC8560 FCC Fast Ethernet + * Basic ET HW initialization and packet RX/TX routines + * + * This code will not perform the IO port configuration. This should be + * done in the iop_conf_t structure specific for the board. + * + * TODO: + * add a PHY driver to do the negotiation + * reflect negotiation results in FPSMR + * look for ways to configure the board specific stuff elsewhere, eg. + * config_xxx.h or the board directory + */ + +#include <common.h> +#include <malloc.h> +#include <asm/cpm_85xx.h> +#include <command.h> +#include <config.h> +#include <net.h> + +#if defined(CONFIG_MII) || defined(CONFIG_CMD_MII) +#include <miiphy.h> +#endif + +#if defined(CONFIG_ETHER_ON_FCC) && defined(CONFIG_CMD_NET) && \ + defined(CONFIG_NET_MULTI) + +static struct ether_fcc_info_s +{ + int ether_index; + int proff_enet; + ulong cpm_cr_enet_sblock; + ulong cpm_cr_enet_page; + ulong cmxfcr_mask; + ulong cmxfcr_value; +} + ether_fcc_info[] = +{ +#ifdef CONFIG_ETHER_ON_FCC1 +{ + 0, + PROFF_FCC1, + CPM_CR_FCC1_SBLOCK, + CPM_CR_FCC1_PAGE, + CONFIG_SYS_CMXFCR_MASK1, + CONFIG_SYS_CMXFCR_VALUE1 +}, +#endif + +#ifdef CONFIG_ETHER_ON_FCC2 +{ + 1, + PROFF_FCC2, + CPM_CR_FCC2_SBLOCK, + CPM_CR_FCC2_PAGE, + CONFIG_SYS_CMXFCR_MASK2, + CONFIG_SYS_CMXFCR_VALUE2 +}, +#endif + +#ifdef CONFIG_ETHER_ON_FCC3 +{ + 2, + PROFF_FCC3, + CPM_CR_FCC3_SBLOCK, + CPM_CR_FCC3_PAGE, + CONFIG_SYS_CMXFCR_MASK3, + CONFIG_SYS_CMXFCR_VALUE3 +}, +#endif +}; + +/*---------------------------------------------------------------------*/ + +/* Maximum input DMA size. Must be a should(?) be a multiple of 4. */ +#define PKT_MAXDMA_SIZE 1520 + +/* The FCC stores dest/src/type, data, and checksum for receive packets. */ +#define PKT_MAXBUF_SIZE 1518 +#define PKT_MINBUF_SIZE 64 + +/* Maximum input buffer size. Must be a multiple of 32. */ +#define PKT_MAXBLR_SIZE 1536 + +#define TOUT_LOOP 1000000 + +#define TX_BUF_CNT 2 + +static uint rxIdx; /* index of the current RX buffer */ +static uint txIdx; /* index of the current TX buffer */ + +/* + * FCC Ethernet Tx and Rx buffer descriptors. + * Provide for Double Buffering + * Note: PKTBUFSRX is defined in net.h + */ + +typedef volatile struct rtxbd { + cbd_t rxbd[PKTBUFSRX]; + cbd_t txbd[TX_BUF_CNT]; +} RTXBD; + +/* Good news: the FCC supports external BDs! */ +#ifdef __GNUC__ +static RTXBD rtx __attribute__ ((aligned(8))); +#else +#error "rtx must be 64-bit aligned" +#endif + +#undef ET_DEBUG + +static int fec_send(struct eth_device* dev, volatile void *packet, int length) +{ + int i = 0; + int result = 0; + + if (length <= 0) { + printf("fec: bad packet size: %d\n", length); + goto out; + } + + for(i=0; rtx.txbd[txIdx].cbd_sc & BD_ENET_TX_READY; i++) { + if (i >= TOUT_LOOP) { + printf("fec: tx buffer not ready\n"); + goto out; + } + } + + rtx.txbd[txIdx].cbd_bufaddr = (uint)packet; + rtx.txbd[txIdx].cbd_datlen = length; + rtx.txbd[txIdx].cbd_sc |= (BD_ENET_TX_READY | BD_ENET_TX_LAST | \ + BD_ENET_TX_TC | BD_ENET_TX_PAD); + + for(i=0; rtx.txbd[txIdx].cbd_sc & BD_ENET_TX_READY; i++) { + if (i >= TOUT_LOOP) { + printf("fec: tx error\n"); + goto out; + } + } + +#ifdef ET_DEBUG + printf("cycles: 0x%x txIdx=0x%04x status: 0x%04x\n", i, txIdx,rtx.txbd[txIdx].cbd_sc); + printf("packets at 0x%08x, length_in_bytes=0x%x\n",(uint)packet,length); + for(i=0;i<(length/16 + 1);i++) { + printf("%08x %08x %08x %08x\n",*((uint *)rtx.txbd[txIdx].cbd_bufaddr+i*4),\ + *((uint *)rtx.txbd[txIdx].cbd_bufaddr + i*4 + 1),*((uint *)rtx.txbd[txIdx].cbd_bufaddr + i*4 + 2), \ + *((uint *)rtx.txbd[txIdx].cbd_bufaddr + i*4 + 3)); + } +#endif + + /* return only status bits */ + result = rtx.txbd[txIdx].cbd_sc & BD_ENET_TX_STATS; + txIdx = (txIdx + 1) % TX_BUF_CNT; + +out: + return result; +} + +static int fec_recv(struct eth_device* dev) +{ + int length; + + for (;;) + { + if (rtx.rxbd[rxIdx].cbd_sc & BD_ENET_RX_EMPTY) { + length = -1; + break; /* nothing received - leave for() loop */ + } + length = rtx.rxbd[rxIdx].cbd_datlen; + + if (rtx.rxbd[rxIdx].cbd_sc & 0x003f) { + printf("fec: rx error %04x\n", rtx.rxbd[rxIdx].cbd_sc); + } + else { + /* Pass the packet up to the protocol layers. */ + NetReceive(NetRxPackets[rxIdx], length - 4); + } + + + /* Give the buffer back to the FCC. */ + rtx.rxbd[rxIdx].cbd_datlen = 0; + + /* wrap around buffer index when necessary */ + if ((rxIdx + 1) >= PKTBUFSRX) { + rtx.rxbd[PKTBUFSRX - 1].cbd_sc = (BD_ENET_RX_WRAP | BD_ENET_RX_EMPTY); + rxIdx = 0; + } + else { + rtx.rxbd[rxIdx].cbd_sc = BD_ENET_RX_EMPTY; + rxIdx++; + } + } + return length; +} + + +static int fec_init(struct eth_device* dev, bd_t *bis) +{ + struct ether_fcc_info_s * info = dev->priv; + int i; + volatile ccsr_cpm_t *cpm = (ccsr_cpm_t *)CONFIG_SYS_MPC85xx_CPM_ADDR; + volatile ccsr_cpm_cp_t *cp = &(cpm->im_cpm_cp); + fcc_enet_t *pram_ptr; + unsigned long mem_addr; + +#if 0 + mii_discover_phy(); +#endif + + /* 28.9 - (1-2): ioports have been set up already */ + + /* 28.9 - (3): connect FCC's tx and rx clocks */ + cpm->im_cpm_mux.cmxuar = 0; /* ATM */ + cpm->im_cpm_mux.cmxfcr = (cpm->im_cpm_mux.cmxfcr & ~info->cmxfcr_mask) | + info->cmxfcr_value; + + /* 28.9 - (4): GFMR: disable tx/rx, CCITT CRC, set Mode Ethernet */ + if(info->ether_index == 0) { + cpm->im_cpm_fcc1.gfmr = FCC_GFMR_MODE_ENET | FCC_GFMR_TCRC_32; + } else if (info->ether_index == 1) { + cpm->im_cpm_fcc2.gfmr = FCC_GFMR_MODE_ENET | FCC_GFMR_TCRC_32; + } else if (info->ether_index == 2) { + cpm->im_cpm_fcc3.gfmr = FCC_GFMR_MODE_ENET | FCC_GFMR_TCRC_32; + } + + /* 28.9 - (5): FPSMR: enable full duplex, select CCITT CRC for Ethernet,MII */ + if(info->ether_index == 0) { + cpm->im_cpm_fcc1.fpsmr = CONFIG_SYS_FCC_PSMR | FCC_PSMR_ENCRC; + } else if (info->ether_index == 1){ + cpm->im_cpm_fcc2.fpsmr = CONFIG_SYS_FCC_PSMR | FCC_PSMR_ENCRC; + } else if (info->ether_index == 2){ + cpm->im_cpm_fcc3.fpsmr = CONFIG_SYS_FCC_PSMR | FCC_PSMR_ENCRC; + } + + /* 28.9 - (6): FDSR: Ethernet Syn */ + if(info->ether_index == 0) { + cpm->im_cpm_fcc1.fdsr = 0xD555; + } else if (info->ether_index == 1) { + cpm->im_cpm_fcc2.fdsr = 0xD555; + } else if (info->ether_index == 2) { + cpm->im_cpm_fcc3.fdsr = 0xD555; + } + + /* reset indeces to current rx/tx bd (see eth_send()/eth_rx()) */ + rxIdx = 0; + txIdx = 0; + + /* Setup Receiver Buffer Descriptors */ + for (i = 0; i < PKTBUFSRX; i++) + { + rtx.rxbd[i].cbd_sc = BD_ENET_RX_EMPTY; + rtx.rxbd[i].cbd_datlen = 0; + rtx.rxbd[i].cbd_bufaddr = (uint)NetRxPackets[i]; + } + rtx.rxbd[PKTBUFSRX - 1].cbd_sc |= BD_ENET_RX_WRAP; + + /* Setup Ethernet Transmitter Buffer Descriptors */ + for (i = 0; i < TX_BUF_CNT; i++) + { + rtx.txbd[i].cbd_sc = 0; + rtx.txbd[i].cbd_datlen = 0; + rtx.txbd[i].cbd_bufaddr = 0; + } + rtx.txbd[TX_BUF_CNT - 1].cbd_sc |= BD_ENET_TX_WRAP; + + /* 28.9 - (7): initialize parameter ram */ + pram_ptr = (fcc_enet_t *)&(cpm->im_dprambase[info->proff_enet]); + + /* clear whole structure to make sure all reserved fields are zero */ + memset((void*)pram_ptr, 0, sizeof(fcc_enet_t)); + + /* + * common Parameter RAM area + * + * Allocate space in the reserved FCC area of DPRAM for the + * internal buffers. No one uses this space (yet), so we + * can do this. Later, we will add resource management for + * this area. + * CPM_FCC_SPECIAL_BASE: 0xB000 for MPC8540, MPC8560 + * 0x9000 for MPC8541, MPC8555 + */ + mem_addr = CPM_FCC_SPECIAL_BASE + ((info->ether_index) * 64); + pram_ptr->fen_genfcc.fcc_riptr = mem_addr; + pram_ptr->fen_genfcc.fcc_tiptr = mem_addr+32; + /* + * Set maximum bytes per receive buffer. + * It must be a multiple of 32. + */ + pram_ptr->fen_genfcc.fcc_mrblr = PKT_MAXBLR_SIZE; /* 1536 */ + /* localbus SDRAM should be preferred */ + pram_ptr->fen_genfcc.fcc_rstate = (CPMFCR_GBL | CPMFCR_EB | + CONFIG_SYS_CPMFCR_RAMTYPE) << 24; + pram_ptr->fen_genfcc.fcc_rbase = (unsigned int)(&rtx.rxbd[rxIdx]); + pram_ptr->fen_genfcc.fcc_rbdstat = 0; + pram_ptr->fen_genfcc.fcc_rbdlen = 0; + pram_ptr->fen_genfcc.fcc_rdptr = 0; + /* localbus SDRAM should be preferred */ + pram_ptr->fen_genfcc.fcc_tstate = (CPMFCR_GBL | CPMFCR_EB | + CONFIG_SYS_CPMFCR_RAMTYPE) << 24; + pram_ptr->fen_genfcc.fcc_tbase = (unsigned int)(&rtx.txbd[txIdx]); + pram_ptr->fen_genfcc.fcc_tbdstat = 0; + pram_ptr->fen_genfcc.fcc_tbdlen = 0; + pram_ptr->fen_genfcc.fcc_tdptr = 0; + + /* protocol-specific area */ + pram_ptr->fen_statbuf = 0x0; + pram_ptr->fen_cmask = 0xdebb20e3; /* CRC mask */ + pram_ptr->fen_cpres = 0xffffffff; /* CRC preset */ + pram_ptr->fen_crcec = 0; + pram_ptr->fen_alec = 0; + pram_ptr->fen_disfc = 0; + pram_ptr->fen_retlim = 15; /* Retry limit threshold */ + pram_ptr->fen_retcnt = 0; + pram_ptr->fen_pper = 0; + pram_ptr->fen_boffcnt = 0; + pram_ptr->fen_gaddrh = 0; + pram_ptr->fen_gaddrl = 0; + pram_ptr->fen_mflr = PKT_MAXBUF_SIZE; /* maximum frame length register */ + /* + * Set Ethernet station address. + * + * This is supplied in the board information structure, so we + * copy that into the controller. + * So far we have only been given one Ethernet address. We make + * it unique by setting a few bits in the upper byte of the + * non-static part of the address. + */ +#define ea eth_get_dev()->enetaddr + pram_ptr->fen_paddrh = (ea[5] << 8) + ea[4]; + pram_ptr->fen_paddrm = (ea[3] << 8) + ea[2]; + pram_ptr->fen_paddrl = (ea[1] << 8) + ea[0]; +#undef ea + pram_ptr->fen_ibdcount = 0; + pram_ptr->fen_ibdstart = 0; + pram_ptr->fen_ibdend = 0; + pram_ptr->fen_txlen = 0; + pram_ptr->fen_iaddrh = 0; /* disable hash */ + pram_ptr->fen_iaddrl = 0; + pram_ptr->fen_minflr = PKT_MINBUF_SIZE; /* minimum frame length register: 64 */ + /* pad pointer. use tiptr since we don't need a specific padding char */ + pram_ptr->fen_padptr = pram_ptr->fen_genfcc.fcc_tiptr; + pram_ptr->fen_maxd1 = PKT_MAXDMA_SIZE; /* maximum DMA1 length:1520 */ + pram_ptr->fen_maxd2 = PKT_MAXDMA_SIZE; /* maximum DMA2 length:1520 */ + +#if defined(ET_DEBUG) + printf("parm_ptr(0xff788500) = %p\n",pram_ptr); + printf("pram_ptr->fen_genfcc.fcc_rbase %08x\n", + pram_ptr->fen_genfcc.fcc_rbase); + printf("pram_ptr->fen_genfcc.fcc_tbase %08x\n", + pram_ptr->fen_genfcc.fcc_tbase); +#endif + + /* 28.9 - (8)(9): clear out events in FCCE */ + /* 28.9 - (9): FCCM: mask all events */ + if(info->ether_index == 0) { + cpm->im_cpm_fcc1.fcce = ~0x0; + cpm->im_cpm_fcc1.fccm = 0; + } else if (info->ether_index == 1) { + cpm->im_cpm_fcc2.fcce = ~0x0; + cpm->im_cpm_fcc2.fccm = 0; + } else if (info->ether_index == 2) { + cpm->im_cpm_fcc3.fcce = ~0x0; + cpm->im_cpm_fcc3.fccm = 0; + } + + /* 28.9 - (10-12): we don't use ethernet interrupts */ + + /* 28.9 - (13) + * + * Let's re-initialize the channel now. We have to do it later + * than the manual describes because we have just now finished + * the BD initialization. + */ + cp->cpcr = mk_cr_cmd(info->cpm_cr_enet_page, + info->cpm_cr_enet_sblock, + 0x0c, + CPM_CR_INIT_TRX) | CPM_CR_FLG; + do { + __asm__ __volatile__ ("eieio"); + } while (cp->cpcr & CPM_CR_FLG); + + /* 28.9 - (14): enable tx/rx in gfmr */ + if(info->ether_index == 0) { + cpm->im_cpm_fcc1.gfmr |= FCC_GFMR_ENT | FCC_GFMR_ENR; + } else if (info->ether_index == 1) { + cpm->im_cpm_fcc2.gfmr |= FCC_GFMR_ENT | FCC_GFMR_ENR; + } else if (info->ether_index == 2) { + cpm->im_cpm_fcc3.gfmr |= FCC_GFMR_ENT | FCC_GFMR_ENR; + } + + return 1; +} + +static void fec_halt(struct eth_device* dev) +{ + struct ether_fcc_info_s * info = dev->priv; + volatile ccsr_cpm_t *cpm = (ccsr_cpm_t *)CONFIG_SYS_MPC85xx_CPM_ADDR; + + /* write GFMR: disable tx/rx */ + if(info->ether_index == 0) { + cpm->im_cpm_fcc1.gfmr &= ~(FCC_GFMR_ENT | FCC_GFMR_ENR); + } else if(info->ether_index == 1) { + cpm->im_cpm_fcc2.gfmr &= ~(FCC_GFMR_ENT | FCC_GFMR_ENR); + } else if(info->ether_index == 2) { + cpm->im_cpm_fcc3.gfmr &= ~(FCC_GFMR_ENT | FCC_GFMR_ENR); + } +} + +int fec_initialize(bd_t *bis) +{ + struct eth_device* dev; + int i; + + for (i = 0; i < sizeof(ether_fcc_info) / sizeof(ether_fcc_info[0]); i++) + { + dev = (struct eth_device*) malloc(sizeof *dev); + memset(dev, 0, sizeof *dev); + + sprintf(dev->name, "FCC%d ETHERNET", + ether_fcc_info[i].ether_index + 1); + dev->priv = ðer_fcc_info[i]; + dev->init = fec_init; + dev->halt = fec_halt; + dev->send = fec_send; + dev->recv = fec_recv; + + eth_register(dev); + +#if (defined(CONFIG_MII) || defined(CONFIG_CMD_MII)) \ + && defined(CONFIG_BITBANGMII) + miiphy_register(dev->name, + bb_miiphy_read, bb_miiphy_write); +#endif + } + + return 1; +} + +#endif diff --git a/arch/powerpc/cpu/mpc85xx/fdt.c b/arch/powerpc/cpu/mpc85xx/fdt.c new file mode 100644 index 0000000..1d11ab4 --- /dev/null +++ b/arch/powerpc/cpu/mpc85xx/fdt.c @@ -0,0 +1,417 @@ +/* + * Copyright 2007-2009 Freescale Semiconductor, Inc. + * + * (C) Copyright 2000 + * Wolfgang Denk, DENX Software Engineering, wd@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> +#include <libfdt.h> +#include <fdt_support.h> +#include <asm/processor.h> +#include <linux/ctype.h> +#ifdef CONFIG_FSL_ESDHC +#include <fsl_esdhc.h> +#endif + +DECLARE_GLOBAL_DATA_PTR; + +extern void ft_qe_setup(void *blob); +extern void ft_fixup_num_cores(void *blob); + +#ifdef CONFIG_MP +#include "mp.h" + +void ft_fixup_cpu(void *blob, u64 memory_limit) +{ + int off; + ulong spin_tbl_addr = get_spin_phys_addr(); + u32 bootpg = determine_mp_bootpg(); + u32 id = get_my_id(); + + off = fdt_node_offset_by_prop_value(blob, -1, "device_type", "cpu", 4); + while (off != -FDT_ERR_NOTFOUND) { + u32 *reg = (u32 *)fdt_getprop(blob, off, "reg", 0); + + if (reg) { + if (*reg == id) { + fdt_setprop_string(blob, off, "status", "okay"); + } else { + u64 val = *reg * SIZE_BOOT_ENTRY + spin_tbl_addr; + val = cpu_to_fdt32(val); + fdt_setprop_string(blob, off, "status", + "disabled"); + fdt_setprop_string(blob, off, "enable-method", + "spin-table"); + fdt_setprop(blob, off, "cpu-release-addr", + &val, sizeof(val)); + } + } else { + printf ("cpu NULL\n"); + } + off = fdt_node_offset_by_prop_value(blob, off, + "device_type", "cpu", 4); + } + + /* Reserve the boot page so OSes dont use it */ + if ((u64)bootpg < memory_limit) { + off = fdt_add_mem_rsv(blob, bootpg, (u64)4096); + if (off < 0) + printf("%s: %s\n", __FUNCTION__, fdt_strerror(off)); + } +} +#endif + +#define ft_fixup_l3cache(x, y) + +#if defined(CONFIG_L2_CACHE) +/* return size in kilobytes */ +static inline u32 l2cache_size(void) +{ + volatile ccsr_l2cache_t *l2cache = (void *)CONFIG_SYS_MPC85xx_L2_ADDR; + volatile u32 l2siz_field = (l2cache->l2ctl >> 28) & 0x3; + u32 ver = SVR_SOC_VER(get_svr()); + + switch (l2siz_field) { + case 0x0: + break; + case 0x1: + if (ver == SVR_8540 || ver == SVR_8560 || + ver == SVR_8541 || ver == SVR_8541_E || + ver == SVR_8555 || ver == SVR_8555_E) + return 128; + else + return 256; + break; + case 0x2: + if (ver == SVR_8540 || ver == SVR_8560 || + ver == SVR_8541 || ver == SVR_8541_E || + ver == SVR_8555 || ver == SVR_8555_E) + return 256; + else + return 512; + break; + case 0x3: + return 1024; + break; + } + + return 0; +} + +static inline void ft_fixup_l2cache(void *blob) +{ + int len, off; + u32 *ph; + struct cpu_type *cpu = identify_cpu(SVR_SOC_VER(get_svr())); + char compat_buf[38]; + + const u32 line_size = 32; + const u32 num_ways = 8; + const u32 size = l2cache_size() * 1024; + const u32 num_sets = size / (line_size * num_ways); + + off = fdt_node_offset_by_prop_value(blob, -1, "device_type", "cpu", 4); + if (off < 0) { + debug("no cpu node fount\n"); + return; + } + + ph = (u32 *)fdt_getprop(blob, off, "next-level-cache", 0); + + if (ph == NULL) { + debug("no next-level-cache property\n"); + return ; + } + + off = fdt_node_offset_by_phandle(blob, *ph); + if (off < 0) { + printf("%s: %s\n", __func__, fdt_strerror(off)); + return ; + } + + if (cpu) { + if (isdigit(cpu->name[0])) + len = sprintf(compat_buf, + "fsl,mpc%s-l2-cache-controller", cpu->name); + else + len = sprintf(compat_buf, + "fsl,%c%s-l2-cache-controller", + tolower(cpu->name[0]), cpu->name + 1); + + sprintf(&compat_buf[len + 1], "cache"); + } + fdt_setprop(blob, off, "cache-unified", NULL, 0); + fdt_setprop_cell(blob, off, "cache-block-size", line_size); + fdt_setprop_cell(blob, off, "cache-size", size); + fdt_setprop_cell(blob, off, "cache-sets", num_sets); + fdt_setprop_cell(blob, off, "cache-level", 2); + fdt_setprop(blob, off, "compatible", compat_buf, sizeof(compat_buf)); + + /* we dont bother w/L3 since no platform of this type has one */ +} +#elif defined(CONFIG_BACKSIDE_L2_CACHE) +static inline void ft_fixup_l2cache(void *blob) +{ + int off, l2_off, l3_off = -1; + u32 *ph; + u32 l2cfg0 = mfspr(SPRN_L2CFG0); + u32 size, line_size, num_ways, num_sets; + + size = (l2cfg0 & 0x3fff) * 64 * 1024; + num_ways = ((l2cfg0 >> 14) & 0x1f) + 1; + line_size = (((l2cfg0 >> 23) & 0x3) + 1) * 32; + num_sets = size / (line_size * num_ways); + + off = fdt_node_offset_by_prop_value(blob, -1, "device_type", "cpu", 4); + + while (off != -FDT_ERR_NOTFOUND) { + ph = (u32 *)fdt_getprop(blob, off, "next-level-cache", 0); + + if (ph == NULL) { + debug("no next-level-cache property\n"); + goto next; + } + + l2_off = fdt_node_offset_by_phandle(blob, *ph); + if (l2_off < 0) { + printf("%s: %s\n", __func__, fdt_strerror(off)); + goto next; + } + +#ifdef CONFIG_SYS_CACHE_STASHING + { + u32 *reg = (u32 *)fdt_getprop(blob, off, "reg", 0); + if (reg) + fdt_setprop_cell(blob, l2_off, "cache-stash-id", + (*reg * 2) + 32 + 1); + } +#endif + + fdt_setprop(blob, l2_off, "cache-unified", NULL, 0); + fdt_setprop_cell(blob, l2_off, "cache-block-size", line_size); + fdt_setprop_cell(blob, l2_off, "cache-size", size); + fdt_setprop_cell(blob, l2_off, "cache-sets", num_sets); + fdt_setprop_cell(blob, l2_off, "cache-level", 2); + fdt_setprop(blob, l2_off, "compatible", "cache", 6); + + if (l3_off < 0) { + ph = (u32 *)fdt_getprop(blob, l2_off, "next-level-cache", 0); + + if (ph == NULL) { + debug("no next-level-cache property\n"); + goto next; + } + l3_off = *ph; + } +next: + off = fdt_node_offset_by_prop_value(blob, off, + "device_type", "cpu", 4); + } + if (l3_off > 0) { + l3_off = fdt_node_offset_by_phandle(blob, l3_off); + if (l3_off < 0) { + printf("%s: %s\n", __func__, fdt_strerror(off)); + return ; + } + ft_fixup_l3cache(blob, l3_off); + } +} +#else +#define ft_fixup_l2cache(x) +#endif + +static inline void ft_fixup_cache(void *blob) +{ + int off; + + off = fdt_node_offset_by_prop_value(blob, -1, "device_type", "cpu", 4); + + while (off != -FDT_ERR_NOTFOUND) { + u32 l1cfg0 = mfspr(SPRN_L1CFG0); + u32 l1cfg1 = mfspr(SPRN_L1CFG1); + u32 isize, iline_size, inum_sets, inum_ways; + u32 dsize, dline_size, dnum_sets, dnum_ways; + + /* d-side config */ + dsize = (l1cfg0 & 0x7ff) * 1024; + dnum_ways = ((l1cfg0 >> 11) & 0xff) + 1; + dline_size = (((l1cfg0 >> 23) & 0x3) + 1) * 32; + dnum_sets = dsize / (dline_size * dnum_ways); + + fdt_setprop_cell(blob, off, "d-cache-block-size", dline_size); + fdt_setprop_cell(blob, off, "d-cache-size", dsize); + fdt_setprop_cell(blob, off, "d-cache-sets", dnum_sets); + +#ifdef CONFIG_SYS_CACHE_STASHING + { + u32 *reg = (u32 *)fdt_getprop(blob, off, "reg", 0); + if (reg) + fdt_setprop_cell(blob, off, "cache-stash-id", + (*reg * 2) + 32 + 0); + } +#endif + + /* i-side config */ + isize = (l1cfg1 & 0x7ff) * 1024; + inum_ways = ((l1cfg1 >> 11) & 0xff) + 1; + iline_size = (((l1cfg1 >> 23) & 0x3) + 1) * 32; + inum_sets = isize / (iline_size * inum_ways); + + fdt_setprop_cell(blob, off, "i-cache-block-size", iline_size); + fdt_setprop_cell(blob, off, "i-cache-size", isize); + fdt_setprop_cell(blob, off, "i-cache-sets", inum_sets); + + off = fdt_node_offset_by_prop_value(blob, off, + "device_type", "cpu", 4); + } + + ft_fixup_l2cache(blob); +} + + +void fdt_add_enet_stashing(void *fdt) +{ + do_fixup_by_compat(fdt, "gianfar", "bd-stash", NULL, 0, 1); + + do_fixup_by_compat_u32(fdt, "gianfar", "rx-stash-len", 96, 1); + + do_fixup_by_compat_u32(fdt, "gianfar", "rx-stash-idx", 0, 1); +} + +#if defined(CONFIG_SYS_DPAA_FMAN) || defined(CONFIG_SYS_DPAA_PME) +static void ft_fixup_clks(void *blob, const char *alias, unsigned long freq) +{ + const char *path = fdt_get_alias(blob, alias); + + int off = fdt_path_offset(blob, path); + + if (off >= 0) { + off = fdt_setprop_cell(blob, off, "clock-frequency", freq); + if (off > 0) + printf("WARNING enable to set clock-frequency " + "for %s: %s\n", alias, fdt_strerror(off)); + } +} + +static void ft_fixup_dpaa_clks(void *blob) +{ + sys_info_t sysinfo; + + get_sys_info(&sysinfo); + ft_fixup_clks(blob, "fman0", sysinfo.freqFMan[0]); + +#if (CONFIG_SYS_NUM_FMAN == 2) + ft_fixup_clks(blob, "fman1", sysinfo.freqFMan[1]); +#endif + +#ifdef CONFIG_SYS_DPAA_PME + ft_fixup_clks(blob, "pme", sysinfo.freqPME); +#endif +} +#else +#define ft_fixup_dpaa_clks(x) +#endif + +#ifdef CONFIG_QE +static void ft_fixup_qe_snum(void *blob) +{ + unsigned int svr; + + svr = mfspr(SPRN_SVR); + if (SVR_SOC_VER(svr) == SVR_8569_E) { + if(IS_SVR_REV(svr, 1, 0)) + do_fixup_by_compat_u32(blob, "fsl,qe", + "fsl,qe-num-snums", 46, 1); + else + do_fixup_by_compat_u32(blob, "fsl,qe", + "fsl,qe-num-snums", 76, 1); + } +} +#endif + +void ft_cpu_setup(void *blob, bd_t *bd) +{ + int off; + int val; + sys_info_t sysinfo; + + /* delete crypto node if not on an E-processor */ + if (!IS_E_PROCESSOR(get_svr())) + fdt_fixup_crypto_node(blob, 0); + + fdt_fixup_ethernet(blob); + + fdt_add_enet_stashing(blob); + + do_fixup_by_prop_u32(blob, "device_type", "cpu", 4, + "timebase-frequency", get_tbclk(), 1); + do_fixup_by_prop_u32(blob, "device_type", "cpu", 4, + "bus-frequency", bd->bi_busfreq, 1); + get_sys_info(&sysinfo); + off = fdt_node_offset_by_prop_value(blob, -1, "device_type", "cpu", 4); + while (off != -FDT_ERR_NOTFOUND) { + u32 *reg = (u32 *)fdt_getprop(blob, off, "reg", 0); + val = cpu_to_fdt32(sysinfo.freqProcessor[*reg]); + fdt_setprop(blob, off, "clock-frequency", &val, 4); + off = fdt_node_offset_by_prop_value(blob, off, "device_type", + "cpu", 4); + } + do_fixup_by_prop_u32(blob, "device_type", "soc", 4, + "bus-frequency", bd->bi_busfreq, 1); + + do_fixup_by_compat_u32(blob, "fsl,pq3-localbus", + "bus-frequency", gd->lbc_clk, 1); + do_fixup_by_compat_u32(blob, "fsl,elbc", + "bus-frequency", gd->lbc_clk, 1); +#ifdef CONFIG_QE + ft_qe_setup(blob); + ft_fixup_qe_snum(blob); +#endif + +#ifdef CONFIG_SYS_NS16550 + do_fixup_by_compat_u32(blob, "ns16550", + "clock-frequency", CONFIG_SYS_NS16550_CLK, 1); +#endif + +#ifdef CONFIG_CPM2 + do_fixup_by_compat_u32(blob, "fsl,cpm2-scc-uart", + "current-speed", bd->bi_baudrate, 1); + + do_fixup_by_compat_u32(blob, "fsl,cpm2-brg", + "clock-frequency", bd->bi_brgfreq, 1); +#endif + + fdt_fixup_memory(blob, (u64)bd->bi_memstart, (u64)bd->bi_memsize); + +#ifdef CONFIG_MP + ft_fixup_cpu(blob, (u64)bd->bi_memstart + (u64)bd->bi_memsize); +#endif + ft_fixup_num_cores(blob); + + ft_fixup_cache(blob); + +#if defined(CONFIG_FSL_ESDHC) + fdt_fixup_esdhc(blob, bd); +#endif + + ft_fixup_dpaa_clks(blob); +} diff --git a/arch/powerpc/cpu/mpc85xx/fixed_ivor.S b/arch/powerpc/cpu/mpc85xx/fixed_ivor.S new file mode 100644 index 0000000..320cae3 --- /dev/null +++ b/arch/powerpc/cpu/mpc85xx/fixed_ivor.S @@ -0,0 +1,79 @@ +/* + * Copyright 2009 Freescale Semiconductor, Inc. + * + * Kumar Gala <kumar.gala@freescale.com> + * + * 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 + */ + +/* This file is intended to be included by other asm code since + * we will want to execute this on both the primary core when + * it does a bootm and the secondary core's that get released + * out of the spin table */ + +#define SET_IVOR(vector_number, vector_offset) \ + li r3,vector_offset@l; \ + mtspr SPRN_IVOR##vector_number,r3; + +#define SET_GIVOR(vector_number, vector_offset) \ + li r3,vector_offset@l; \ + mtspr SPRN_GIVOR##vector_number,r3; + + SET_IVOR(0, 0x020) /* Critical Input */ + SET_IVOR(1, 0x000) /* Machine Check */ + SET_IVOR(2, 0x060) /* Data Storage */ + SET_IVOR(3, 0x080) /* Instruction Storage */ + SET_IVOR(4, 0x0a0) /* External Input */ + SET_IVOR(5, 0x0c0) /* Alignment */ + SET_IVOR(6, 0x0e0) /* Program */ + SET_IVOR(7, 0x100) /* FP Unavailable */ + SET_IVOR(8, 0x120) /* System Call */ + SET_IVOR(9, 0x140) /* Auxiliary Processor Unavailable */ + SET_IVOR(10, 0x160) /* Decrementer */ + SET_IVOR(11, 0x180) /* Fixed Interval Timer */ + SET_IVOR(12, 0x1a0) /* Watchdog Timer */ + SET_IVOR(13, 0x1c0) /* Data TLB Error */ + SET_IVOR(14, 0x1e0) /* Instruction TLB Error */ + SET_IVOR(15, 0x040) /* Debug */ + +/* e500v1 & e500v2 only */ +#ifndef CONFIG_E500MC + SET_IVOR(32, 0x200) /* SPE Unavailable */ + SET_IVOR(33, 0x220) /* Embedded FP Data */ + SET_IVOR(34, 0x240) /* Embedded FP Round */ +#endif + + SET_IVOR(35, 0x260) /* Performance monitor */ + +/* e500mc only */ +#ifdef CONFIG_E500MC + SET_IVOR(36, 0x280) /* Processor doorbell */ + SET_IVOR(37, 0x2a0) /* Processor doorbell critical */ + SET_IVOR(38, 0x2c0) /* Guest Processor doorbell */ + SET_IVOR(39, 0x2e0) /* Guest Processor critical & machine check */ + SET_IVOR(40, 0x300) /* Hypervisor system call */ + SET_IVOR(41, 0x320) /* Hypervisor Priviledge */ + + SET_GIVOR(2, 0x060) /* Guest Data Storage */ + SET_GIVOR(3, 0x080) /* Guest Instruction Storage */ + SET_GIVOR(4, 0x0a0) /* Guest External Input */ + SET_GIVOR(8, 0x120) /* Guest System Call */ + SET_GIVOR(13, 0x1c0) /* Guest Data TLB Error */ + SET_GIVOR(14, 0x1e0) /* Guest Instruction TLB Error */ +#endif diff --git a/arch/powerpc/cpu/mpc85xx/interrupts.c b/arch/powerpc/cpu/mpc85xx/interrupts.c new file mode 100644 index 0000000..409367d --- /dev/null +++ b/arch/powerpc/cpu/mpc85xx/interrupts.c @@ -0,0 +1,110 @@ +/* + * (C) Copyright 2000-2002 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * (C) Copyright 2002 (440 port) + * Scott McNutt, Artesyn Communication Producs, smcnutt@artsyncp.com + * + * (C) Copyright 2003 Motorola Inc. (MPC85xx port) + * Xianghua Xiao (X.Xiao@motorola.com) + * + * 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> +#include <watchdog.h> +#include <command.h> +#include <asm/processor.h> +#include <asm/io.h> + +int interrupt_init_cpu(unsigned int *decrementer_count) +{ + ccsr_pic_t __iomem *pic = (void *)CONFIG_SYS_MPC85xx_PIC_ADDR; + + out_be32(&pic->gcr, MPC85xx_PICGCR_RST); + while (in_be32(&pic->gcr) & MPC85xx_PICGCR_RST) + ; + out_be32(&pic->gcr, MPC85xx_PICGCR_M); + in_be32(&pic->gcr); + + *decrementer_count = get_tbclk() / CONFIG_SYS_HZ; + + /* PIE is same as DIE, dec interrupt enable */ + mtspr(SPRN_TCR, TCR_PIE); + +#ifdef CONFIG_INTERRUPTS + pic->iivpr1 = 0x810001; /* 50220 enable ecm interrupts */ + debug("iivpr1@%x = %x\n", (uint)&pic->iivpr1, pic->iivpr1); + + pic->iivpr2 = 0x810002; /* 50240 enable ddr interrupts */ + debug("iivpr2@%x = %x\n", (uint)&pic->iivpr2, pic->iivpr2); + + pic->iivpr3 = 0x810003; /* 50260 enable lbc interrupts */ + debug("iivpr3@%x = %x\n", (uint)&pic->iivpr3, pic->iivpr3); + +#ifdef CONFIG_PCI1 + pic->iivpr8 = 0x810008; /* enable pci1 interrupts */ + debug("iivpr8@%x = %x\n", (uint)&pic->iivpr8, pic->iivpr8); +#endif +#if defined(CONFIG_PCI2) || defined(CONFIG_PCIE2) + pic->iivpr9 = 0x810009; /* enable pci1 interrupts */ + debug("iivpr9@%x = %x\n", (uint)&pic->iivpr9, pic->iivpr9); +#endif +#ifdef CONFIG_PCIE1 + pic->iivpr10 = 0x81000a; /* enable pcie1 interrupts */ + debug("iivpr10@%x = %x\n", (uint)&pic->iivpr10, pic->iivpr10); +#endif +#ifdef CONFIG_PCIE3 + pic->iivpr11 = 0x81000b; /* enable pcie3 interrupts */ + debug("iivpr11@%x = %x\n", (uint)&pic->iivpr11, pic->iivpr11); +#endif + + pic->ctpr=0; /* 40080 clear current task priority register */ +#endif + + return (0); +} + +/* Install and free a interrupt handler. Not implemented yet. */ + +void +irq_install_handler(int vec, interrupt_handler_t *handler, void *arg) +{ + return; +} + +void +irq_free_handler(int vec) +{ + return; +} + +void timer_interrupt_cpu(struct pt_regs *regs) +{ + /* PIS is same as DIS, dec interrupt status */ + mtspr(SPRN_TSR, TSR_PIS); +} + +#if defined(CONFIG_CMD_IRQ) +/* irqinfo - print information about PCI devices,not implemented. */ +int do_irqinfo(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]) +{ + return 0; +} +#endif diff --git a/arch/powerpc/cpu/mpc85xx/mp.c b/arch/powerpc/cpu/mpc85xx/mp.c new file mode 100644 index 0000000..826bf32 --- /dev/null +++ b/arch/powerpc/cpu/mpc85xx/mp.c @@ -0,0 +1,355 @@ +/* + * Copyright 2008-2010 Freescale Semiconductor, Inc. + * + * 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> +#include <asm/processor.h> +#include <ioports.h> +#include <lmb.h> +#include <asm/io.h> +#include <asm/mmu.h> +#include <asm/fsl_law.h> +#include "mp.h" + +DECLARE_GLOBAL_DATA_PTR; + +u32 get_my_id() +{ + return mfspr(SPRN_PIR); +} + +int cpu_reset(int nr) +{ + volatile ccsr_pic_t *pic = (void *)(CONFIG_SYS_MPC85xx_PIC_ADDR); + out_be32(&pic->pir, 1 << nr); + /* the dummy read works around an errata on early 85xx MP PICs */ + (void)in_be32(&pic->pir); + out_be32(&pic->pir, 0x0); + + return 0; +} + +int cpu_status(int nr) +{ + u32 *table, id = get_my_id(); + + if (nr == id) { + table = (u32 *)get_spin_virt_addr(); + printf("table base @ 0x%p\n", table); + } else { + table = (u32 *)get_spin_virt_addr() + nr * NUM_BOOT_ENTRY; + printf("Running on cpu %d\n", id); + printf("\n"); + printf("table @ 0x%p\n", table); + printf(" addr - 0x%08x\n", table[BOOT_ENTRY_ADDR_LOWER]); + printf(" pir - 0x%08x\n", table[BOOT_ENTRY_PIR]); + printf(" r3 - 0x%08x\n", table[BOOT_ENTRY_R3_LOWER]); + printf(" r6 - 0x%08x\n", table[BOOT_ENTRY_R6_LOWER]); + } + + return 0; +} + +#ifdef CONFIG_FSL_CORENET +int cpu_disable(int nr) +{ + volatile ccsr_gur_t *gur = (void *)(CONFIG_SYS_MPC85xx_GUTS_ADDR); + + setbits_be32(&gur->coredisrl, 1 << nr); + + return 0; +} +#else +int cpu_disable(int nr) +{ + volatile ccsr_gur_t *gur = (void *)(CONFIG_SYS_MPC85xx_GUTS_ADDR); + + switch (nr) { + case 0: + setbits_be32(&gur->devdisr, MPC85xx_DEVDISR_CPU0); + break; + case 1: + setbits_be32(&gur->devdisr, MPC85xx_DEVDISR_CPU1); + break; + default: + printf("Invalid cpu number for disable %d\n", nr); + return 1; + } + + return 0; +} +#endif + +static u8 boot_entry_map[4] = { + 0, + BOOT_ENTRY_PIR, + BOOT_ENTRY_R3_LOWER, + BOOT_ENTRY_R6_LOWER, +}; + +int cpu_release(int nr, int argc, char *argv[]) +{ + u32 i, val, *table = (u32 *)get_spin_virt_addr() + nr * NUM_BOOT_ENTRY; + u64 boot_addr; + + if (nr == get_my_id()) { + printf("Invalid to release the boot core.\n\n"); + return 1; + } + + if (argc != 4) { + printf("Invalid number of arguments to release.\n\n"); + return 1; + } + + boot_addr = simple_strtoull(argv[0], NULL, 16); + + /* handle pir, r3, r6 */ + for (i = 1; i < 4; i++) { + if (argv[i][0] != '-') { + u8 entry = boot_entry_map[i]; + val = simple_strtoul(argv[i], NULL, 16); + table[entry] = val; + } + } + + table[BOOT_ENTRY_ADDR_UPPER] = (u32)(boot_addr >> 32); + + /* ensure all table updates complete before final address write */ + eieio(); + + table[BOOT_ENTRY_ADDR_LOWER] = (u32)(boot_addr & 0xffffffff); + + return 0; +} + +u32 determine_mp_bootpg(void) +{ + /* if we have 4G or more of memory, put the boot page at 4Gb-4k */ + if ((u64)gd->ram_size > 0xfffff000) + return (0xfffff000); + + return (gd->ram_size - 4096); +} + +ulong get_spin_phys_addr(void) +{ + extern ulong __secondary_start_page; + extern ulong __spin_table; + + return (determine_mp_bootpg() + + (ulong)&__spin_table - (ulong)&__secondary_start_page); +} + +ulong get_spin_virt_addr(void) +{ + extern ulong __secondary_start_page; + extern ulong __spin_table; + + return (CONFIG_BPTR_VIRT_ADDR + + (ulong)&__spin_table - (ulong)&__secondary_start_page); +} + +#ifdef CONFIG_FSL_CORENET +static void plat_mp_up(unsigned long bootpg) +{ + u32 up, cpu_up_mask, whoami; + u32 *table = (u32 *)get_spin_virt_addr(); + volatile ccsr_gur_t *gur; + volatile ccsr_local_t *ccm; + volatile ccsr_rcpm_t *rcpm; + volatile ccsr_pic_t *pic; + int timeout = 10; + u32 nr_cpus; + struct law_entry e; + + gur = (void *)(CONFIG_SYS_MPC85xx_GUTS_ADDR); + ccm = (void *)(CONFIG_SYS_FSL_CORENET_CCM_ADDR); + rcpm = (void *)(CONFIG_SYS_FSL_CORENET_RCPM_ADDR); + pic = (void *)(CONFIG_SYS_MPC85xx_PIC_ADDR); + + nr_cpus = ((in_be32(&pic->frr) >> 8) & 0xff) + 1; + + whoami = in_be32(&pic->whoami); + cpu_up_mask = 1 << whoami; + out_be32(&ccm->bstrl, bootpg); + + e = find_law(bootpg); + out_be32(&ccm->bstrar, LAW_EN | e.trgt_id << 20 | LAW_SIZE_4K); + + /* readback to sync write */ + in_be32(&ccm->bstrar); + + /* disable time base at the platform */ + out_be32(&rcpm->ctbenrl, cpu_up_mask); + + /* release the hounds */ + up = ((1 << nr_cpus) - 1); + out_be32(&gur->brrl, up); + + /* wait for everyone */ + while (timeout) { + int i; + for (i = 0; i < nr_cpus; i++) { + if (table[i * NUM_BOOT_ENTRY + BOOT_ENTRY_ADDR_LOWER]) + cpu_up_mask |= (1 << i); + }; + + if ((cpu_up_mask & up) == up) + break; + + udelay(100); + timeout--; + } + + if (timeout == 0) + printf("CPU up timeout. CPU up mask is %x should be %x\n", + cpu_up_mask, up); + + /* enable time base at the platform */ + out_be32(&rcpm->ctbenrl, 0); + mtspr(SPRN_TBWU, 0); + mtspr(SPRN_TBWL, 0); + out_be32(&rcpm->ctbenrl, (1 << nr_cpus) - 1); + +#ifdef CONFIG_MPC8xxx_DISABLE_BPTR + /* + * Disabling Boot Page Translation allows the memory region 0xfffff000 + * to 0xffffffff to be used normally. Leaving Boot Page Translation + * enabled remaps 0xfffff000 to SDRAM which makes that memory region + * unusable for normal operation but it does allow OSes to easily + * reset a processor core to put it back into U-Boot's spinloop. + */ + clrbits_be32(&ecm->bptr, 0x80000000); +#endif +} +#else +static void plat_mp_up(unsigned long bootpg) +{ + u32 up, cpu_up_mask, whoami; + u32 *table = (u32 *)get_spin_virt_addr(); + volatile u32 bpcr; + volatile ccsr_local_ecm_t *ecm = (void *)(CONFIG_SYS_MPC85xx_ECM_ADDR); + volatile ccsr_gur_t *gur = (void *)(CONFIG_SYS_MPC85xx_GUTS_ADDR); + volatile ccsr_pic_t *pic = (void *)(CONFIG_SYS_MPC85xx_PIC_ADDR); + u32 devdisr; + int timeout = 10; + + whoami = in_be32(&pic->whoami); + out_be32(&ecm->bptr, 0x80000000 | (bootpg >> 12)); + + /* disable time base at the platform */ + devdisr = in_be32(&gur->devdisr); + if (whoami) + devdisr |= MPC85xx_DEVDISR_TB0; + else + devdisr |= MPC85xx_DEVDISR_TB1; + out_be32(&gur->devdisr, devdisr); + + /* release the hounds */ + up = ((1 << cpu_numcores()) - 1); + bpcr = in_be32(&ecm->eebpcr); + bpcr |= (up << 24); + out_be32(&ecm->eebpcr, bpcr); + asm("sync; isync; msync"); + + cpu_up_mask = 1 << whoami; + /* wait for everyone */ + while (timeout) { + int i; + for (i = 0; i < cpu_numcores(); i++) { + if (table[i * NUM_BOOT_ENTRY + BOOT_ENTRY_ADDR_LOWER]) + cpu_up_mask |= (1 << i); + }; + + if ((cpu_up_mask & up) == up) + break; + + udelay(100); + timeout--; + } + + if (timeout == 0) + printf("CPU up timeout. CPU up mask is %x should be %x\n", + cpu_up_mask, up); + + /* enable time base at the platform */ + if (whoami) + devdisr |= MPC85xx_DEVDISR_TB1; + else + devdisr |= MPC85xx_DEVDISR_TB0; + out_be32(&gur->devdisr, devdisr); + mtspr(SPRN_TBWU, 0); + mtspr(SPRN_TBWL, 0); + + devdisr &= ~(MPC85xx_DEVDISR_TB0 | MPC85xx_DEVDISR_TB1); + out_be32(&gur->devdisr, devdisr); + +#ifdef CONFIG_MPC8xxx_DISABLE_BPTR + /* + * Disabling Boot Page Translation allows the memory region 0xfffff000 + * to 0xffffffff to be used normally. Leaving Boot Page Translation + * enabled remaps 0xfffff000 to SDRAM which makes that memory region + * unusable for normal operation but it does allow OSes to easily + * reset a processor core to put it back into U-Boot's spinloop. + */ + clrbits_be32(&ecm->bptr, 0x80000000); +#endif +} +#endif + +void cpu_mp_lmb_reserve(struct lmb *lmb) +{ + u32 bootpg = determine_mp_bootpg(); + + lmb_reserve(lmb, bootpg, 4096); +} + +void setup_mp(void) +{ + extern ulong __secondary_start_page; + extern ulong __bootpg_addr; + ulong fixup = (ulong)&__secondary_start_page; + u32 bootpg = determine_mp_bootpg(); + + /* Store the bootpg's SDRAM address for use by secondary CPU cores */ + __bootpg_addr = bootpg; + + /* look for the tlb covering the reset page, there better be one */ + int i = find_tlb_idx((void *)CONFIG_BPTR_VIRT_ADDR, 1); + + /* we found a match */ + if (i != -1) { + /* map reset page to bootpg so we can copy code there */ + disable_tlb(i); + + set_tlb(1, CONFIG_BPTR_VIRT_ADDR, bootpg, /* tlb, epn, rpn */ + MAS3_SX|MAS3_SW|MAS3_SR, MAS2_I|MAS2_G, /* perms, wimge */ + 0, i, BOOKE_PAGESZ_4K, 1); /* ts, esel, tsize, iprot */ + + memcpy((void *)CONFIG_BPTR_VIRT_ADDR, (void *)fixup, 4096); + + plat_mp_up(bootpg); + } else { + puts("WARNING: No reset page TLB. " + "Skipping secondary core setup\n"); + } +} diff --git a/arch/powerpc/cpu/mpc85xx/mp.h b/arch/powerpc/cpu/mpc85xx/mp.h new file mode 100644 index 0000000..3422cc1 --- /dev/null +++ b/arch/powerpc/cpu/mpc85xx/mp.h @@ -0,0 +1,21 @@ +#ifndef __MPC85XX_MP_H_ +#define __MPC85XX_MP_H_ + +#include <asm/mp.h> + +ulong get_spin_phys_addr(void); +ulong get_spin_virt_addr(void); +u32 get_my_id(void); + +#define BOOT_ENTRY_ADDR_UPPER 0 +#define BOOT_ENTRY_ADDR_LOWER 1 +#define BOOT_ENTRY_R3_UPPER 2 +#define BOOT_ENTRY_R3_LOWER 3 +#define BOOT_ENTRY_RESV 4 +#define BOOT_ENTRY_PIR 5 +#define BOOT_ENTRY_R6_UPPER 6 +#define BOOT_ENTRY_R6_LOWER 7 +#define NUM_BOOT_ENTRY 8 +#define SIZE_BOOT_ENTRY (NUM_BOOT_ENTRY * sizeof(u32)) + +#endif diff --git a/arch/powerpc/cpu/mpc85xx/mpc8536_serdes.c b/arch/powerpc/cpu/mpc85xx/mpc8536_serdes.c new file mode 100644 index 0000000..cb6a6f0 --- /dev/null +++ b/arch/powerpc/cpu/mpc85xx/mpc8536_serdes.c @@ -0,0 +1,180 @@ +/* + * Copyright (C) 2008 Freescale Semicondutor, Inc. + * Dave Liu <daveliu@freescale.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. + */ + +#include <config.h> +#include <common.h> +#include <asm/io.h> +#include <asm/immap_85xx.h> + +/* PORDEVSR register */ +#define GUTS_PORDEVSR_OFFS 0xc +#define GUTS_PORDEVSR_SERDES2_IO_SEL 0x38000000 +#define GUTS_PORDEVSR_SERDES2_IO_SEL_SHIFT 27 + +/* SerDes CR0 register */ +#define FSL_SRDSCR0_OFFS 0x0 +#define FSL_SRDSCR0_TXEQA_MASK 0x00007000 +#define FSL_SRDSCR0_TXEQA_SGMII 0x00004000 +#define FSL_SRDSCR0_TXEQA_SATA 0x00001000 +#define FSL_SRDSCR0_TXEQE_MASK 0x00000700 +#define FSL_SRDSCR0_TXEQE_SGMII 0x00000400 +#define FSL_SRDSCR0_TXEQE_SATA 0x00000100 + +/* SerDes CR1 register */ +#define FSL_SRDSCR1_OFFS 0x4 +#define FSL_SRDSCR1_LANEA_MASK 0x80200000 +#define FSL_SRDSCR1_LANEA_OFF 0x80200000 +#define FSL_SRDSCR1_LANEE_MASK 0x08020000 +#define FSL_SRDSCR1_LANEE_OFF 0x08020000 + +/* SerDes CR2 register */ +#define FSL_SRDSCR2_OFFS 0x8 +#define FSL_SRDSCR2_EICA_MASK 0x00001f00 +#define FSL_SRDSCR2_EICA_SGMII 0x00000400 +#define FSL_SRDSCR2_EICA_SATA 0x00001400 +#define FSL_SRDSCR2_EICE_MASK 0x0000001f +#define FSL_SRDSCR2_EICE_SGMII 0x00000004 +#define FSL_SRDSCR2_EICE_SATA 0x00000014 + +/* SerDes CR3 register */ +#define FSL_SRDSCR3_OFFS 0xc +#define FSL_SRDSCR3_LANEA_MASK 0x3f000700 +#define FSL_SRDSCR3_LANEA_SGMII 0x00000000 +#define FSL_SRDSCR3_LANEA_SATA 0x15000500 +#define FSL_SRDSCR3_LANEE_MASK 0x003f0007 +#define FSL_SRDSCR3_LANEE_SGMII 0x00000000 +#define FSL_SRDSCR3_LANEE_SATA 0x00150005 + +void fsl_serdes_init(void) +{ + void *guts = (void *)(CONFIG_SYS_MPC85xx_GUTS_ADDR); + void *sd = (void *)CONFIG_SYS_MPC85xx_SERDES2_ADDR; + u32 pordevsr = in_be32(guts + GUTS_PORDEVSR_OFFS); + u32 srds2_io_sel; + u32 tmp; + + /* parse the SRDS2_IO_SEL of PORDEVSR */ + srds2_io_sel = (pordevsr & GUTS_PORDEVSR_SERDES2_IO_SEL) + >> GUTS_PORDEVSR_SERDES2_IO_SEL_SHIFT; + + switch (srds2_io_sel) { + case 1: /* Lane A - SATA1, Lane E - SATA2 */ + /* CR 0 */ + tmp = in_be32(sd + FSL_SRDSCR0_OFFS); + tmp &= ~FSL_SRDSCR0_TXEQA_MASK; + tmp |= FSL_SRDSCR0_TXEQA_SATA; + tmp &= ~FSL_SRDSCR0_TXEQE_MASK; + tmp |= FSL_SRDSCR0_TXEQE_SATA; + out_be32(sd + FSL_SRDSCR0_OFFS, tmp); + /* CR 1 */ + tmp = in_be32(sd + FSL_SRDSCR1_OFFS); + tmp &= ~FSL_SRDSCR1_LANEA_MASK; + tmp &= ~FSL_SRDSCR1_LANEE_MASK; + out_be32(sd + FSL_SRDSCR1_OFFS, tmp); + /* CR 2 */ + tmp = in_be32(sd + FSL_SRDSCR2_OFFS); + tmp &= ~FSL_SRDSCR2_EICA_MASK; + tmp |= FSL_SRDSCR2_EICA_SATA; + tmp &= ~FSL_SRDSCR2_EICE_MASK; + tmp |= FSL_SRDSCR2_EICE_SATA; + out_be32(sd + FSL_SRDSCR2_OFFS, tmp); + /* CR 3 */ + tmp = in_be32(sd + FSL_SRDSCR3_OFFS); + tmp &= ~FSL_SRDSCR3_LANEA_MASK; + tmp |= FSL_SRDSCR3_LANEA_SATA; + tmp &= ~FSL_SRDSCR3_LANEE_MASK; + tmp |= FSL_SRDSCR3_LANEE_SATA; + out_be32(sd + FSL_SRDSCR3_OFFS, tmp); + break; + case 3: /* Lane A - SATA1, Lane E - disabled */ + /* CR 0 */ + tmp = in_be32(sd + FSL_SRDSCR0_OFFS); + tmp &= ~FSL_SRDSCR0_TXEQA_MASK; + tmp |= FSL_SRDSCR0_TXEQA_SATA; + out_be32(sd + FSL_SRDSCR0_OFFS, tmp); + /* CR 1 */ + tmp = in_be32(sd + FSL_SRDSCR1_OFFS); + tmp &= ~FSL_SRDSCR1_LANEE_MASK; + tmp |= FSL_SRDSCR1_LANEE_OFF; + out_be32(sd + FSL_SRDSCR1_OFFS, tmp); + /* CR 2 */ + tmp = in_be32(sd + FSL_SRDSCR2_OFFS); + tmp &= ~FSL_SRDSCR2_EICA_MASK; + tmp |= FSL_SRDSCR2_EICA_SATA; + out_be32(sd + FSL_SRDSCR2_OFFS, tmp); + /* CR 3 */ + tmp = in_be32(sd + FSL_SRDSCR3_OFFS); + tmp &= ~FSL_SRDSCR3_LANEA_MASK; + tmp |= FSL_SRDSCR3_LANEA_SATA; + out_be32(sd + FSL_SRDSCR3_OFFS, tmp); + break; + case 4: /* Lane A - eTSEC1 SGMII, Lane E - eTSEC3 SGMII */ + /* CR 0 */ + tmp = in_be32(sd + FSL_SRDSCR0_OFFS); + tmp &= ~FSL_SRDSCR0_TXEQA_MASK; + tmp |= FSL_SRDSCR0_TXEQA_SGMII; + tmp &= ~FSL_SRDSCR0_TXEQE_MASK; + tmp |= FSL_SRDSCR0_TXEQE_SGMII; + out_be32(sd + FSL_SRDSCR0_OFFS, tmp); + /* CR 1 */ + tmp = in_be32(sd + FSL_SRDSCR1_OFFS); + tmp &= ~FSL_SRDSCR1_LANEA_MASK; + tmp &= ~FSL_SRDSCR1_LANEE_MASK; + out_be32(sd + FSL_SRDSCR1_OFFS, tmp); + /* CR 2 */ + tmp = in_be32(sd + FSL_SRDSCR2_OFFS); + tmp &= ~FSL_SRDSCR2_EICA_MASK; + tmp |= FSL_SRDSCR2_EICA_SGMII; + tmp &= ~FSL_SRDSCR2_EICE_MASK; + tmp |= FSL_SRDSCR2_EICE_SGMII; + out_be32(sd + FSL_SRDSCR2_OFFS, tmp); + /* CR 3 */ + tmp = in_be32(sd + FSL_SRDSCR3_OFFS); + tmp &= ~FSL_SRDSCR3_LANEA_MASK; + tmp |= FSL_SRDSCR3_LANEA_SGMII; + tmp &= ~FSL_SRDSCR3_LANEE_MASK; + tmp |= FSL_SRDSCR3_LANEE_SGMII; + out_be32(sd + FSL_SRDSCR3_OFFS, tmp); + break; + case 6: /* Lane A - eTSEC1 SGMII, Lane E - disabled */ + /* CR 0 */ + tmp = in_be32(sd + FSL_SRDSCR0_OFFS); + tmp &= ~FSL_SRDSCR0_TXEQA_MASK; + tmp |= FSL_SRDSCR0_TXEQA_SGMII; + out_be32(sd + FSL_SRDSCR0_OFFS, tmp); + /* CR 1 */ + tmp = in_be32(sd + FSL_SRDSCR1_OFFS); + tmp &= ~FSL_SRDSCR1_LANEE_MASK; + tmp |= FSL_SRDSCR1_LANEE_OFF; + out_be32(sd + FSL_SRDSCR1_OFFS, tmp); + /* CR 2 */ + tmp = in_be32(sd + FSL_SRDSCR2_OFFS); + tmp &= ~FSL_SRDSCR2_EICA_MASK; + tmp |= FSL_SRDSCR2_EICA_SGMII; + out_be32(sd + FSL_SRDSCR2_OFFS, tmp); + /* CR 3 */ + tmp = in_be32(sd + FSL_SRDSCR3_OFFS); + tmp &= ~FSL_SRDSCR3_LANEA_MASK; + tmp |= FSL_SRDSCR3_LANEA_SGMII; + out_be32(sd + FSL_SRDSCR3_OFFS, tmp); + break; + case 7: /* Lane A - disabled, Lane E - disabled */ + /* CR 1 */ + tmp = in_be32(sd + FSL_SRDSCR1_OFFS); + tmp &= ~FSL_SRDSCR1_LANEA_MASK; + tmp |= FSL_SRDSCR1_LANEA_OFF; + tmp &= ~FSL_SRDSCR1_LANEE_MASK; + tmp |= FSL_SRDSCR1_LANEE_OFF; + out_be32(sd + FSL_SRDSCR1_OFFS, tmp); + break; + default: + break; + } +} diff --git a/arch/powerpc/cpu/mpc85xx/pci.c b/arch/powerpc/cpu/mpc85xx/pci.c new file mode 100644 index 0000000..75d2716 --- /dev/null +++ b/arch/powerpc/cpu/mpc85xx/pci.c @@ -0,0 +1,230 @@ +/* + * Copyright 2004 Freescale Semiconductor. + * Copyright (C) 2003 Motorola Inc. + * Xianghua Xiao (x.xiao@motorola.com) + * + * 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 + */ + +/* + * PCI Configuration space access support for MPC85xx PCI Bridge + */ +#include <common.h> +#include <asm/cpm_85xx.h> +#include <pci.h> + +#if !defined(CONFIG_FSL_PCI_INIT) + +#ifndef CONFIG_SYS_PCI1_MEM_BUS +#define CONFIG_SYS_PCI1_MEM_BUS CONFIG_SYS_PCI1_MEM_BASE +#endif + +#ifndef CONFIG_SYS_PCI1_IO_BUS +#define CONFIG_SYS_PCI1_IO_BUS CONFIG_SYS_PCI1_IO_BASE +#endif + +#ifndef CONFIG_SYS_PCI2_MEM_BUS +#define CONFIG_SYS_PCI2_MEM_BUS CONFIG_SYS_PCI2_MEM_BASE +#endif + +#ifndef CONFIG_SYS_PCI2_IO_BUS +#define CONFIG_SYS_PCI2_IO_BUS CONFIG_SYS_PCI2_IO_BASE +#endif + +static struct pci_controller *pci_hose; + +void +pci_mpc85xx_init(struct pci_controller *board_hose) +{ + u16 reg16; + u32 dev; + + volatile ccsr_pcix_t *pcix = (void *)(CONFIG_SYS_MPC85xx_PCIX_ADDR); +#ifdef CONFIG_MPC85XX_PCI2 + volatile ccsr_pcix_t *pcix2 = (void *)(CONFIG_SYS_MPC85xx_PCIX2_ADDR); +#endif + volatile ccsr_gur_t *gur = (void *)(CONFIG_SYS_MPC85xx_GUTS_ADDR); + struct pci_controller * hose; + + pci_hose = board_hose; + + hose = &pci_hose[0]; + + hose->first_busno = 0; + hose->last_busno = 0xff; + + pci_setup_indirect(hose, + (CONFIG_SYS_IMMR+0x8000), + (CONFIG_SYS_IMMR+0x8004)); + + /* + * Hose scan. + */ + dev = PCI_BDF(hose->first_busno, 0, 0); + pci_hose_read_config_word (hose, dev, PCI_COMMAND, ®16); + reg16 |= PCI_COMMAND_SERR | PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY; + pci_hose_write_config_word(hose, dev, PCI_COMMAND, reg16); + + /* + * Clear non-reserved bits in status register. + */ + pci_hose_write_config_word(hose, dev, PCI_STATUS, 0xffff); + + if (!(gur->pordevsr & MPC85xx_PORDEVSR_PCI1)) { + /* PCI-X init */ + if (CONFIG_SYS_CLK_FREQ < 66000000) + printf("PCI-X will only work at 66 MHz\n"); + + reg16 = PCI_X_CMD_MAX_SPLIT | PCI_X_CMD_MAX_READ + | PCI_X_CMD_ERO | PCI_X_CMD_DPERR_E; + pci_hose_write_config_word(hose, dev, PCIX_COMMAND, reg16); + } + + pcix->potar1 = (CONFIG_SYS_PCI1_MEM_BUS >> 12) & 0x000fffff; + pcix->potear1 = 0x00000000; + pcix->powbar1 = (CONFIG_SYS_PCI1_MEM_PHYS >> 12) & 0x000fffff; + pcix->powbear1 = 0x00000000; + pcix->powar1 = (POWAR_EN | POWAR_MEM_READ | + POWAR_MEM_WRITE | (__ilog2(CONFIG_SYS_PCI1_MEM_SIZE) - 1)); + + pcix->potar2 = (CONFIG_SYS_PCI1_IO_BUS >> 12) & 0x000fffff; + pcix->potear2 = 0x00000000; + pcix->powbar2 = (CONFIG_SYS_PCI1_IO_PHYS >> 12) & 0x000fffff; + pcix->powbear2 = 0x00000000; + pcix->powar2 = (POWAR_EN | POWAR_IO_READ | + POWAR_IO_WRITE | (__ilog2(CONFIG_SYS_PCI1_IO_SIZE) - 1)); + + pcix->pitar1 = 0x00000000; + pcix->piwbar1 = 0x00000000; + pcix->piwar1 = (PIWAR_EN | PIWAR_PF | PIWAR_LOCAL | + PIWAR_READ_SNOOP | PIWAR_WRITE_SNOOP | PIWAR_MEM_2G); + + pcix->powar3 = 0; + pcix->powar4 = 0; + pcix->piwar2 = 0; + pcix->piwar3 = 0; + + pci_set_region(hose->regions + 0, + CONFIG_SYS_PCI1_MEM_BUS, + CONFIG_SYS_PCI1_MEM_PHYS, + CONFIG_SYS_PCI1_MEM_SIZE, + PCI_REGION_MEM); + + pci_set_region(hose->regions + 1, + CONFIG_SYS_PCI1_IO_BUS, + CONFIG_SYS_PCI1_IO_PHYS, + CONFIG_SYS_PCI1_IO_SIZE, + PCI_REGION_IO); + + hose->region_count = 2; + + pci_register_hose(hose); + +#if defined(CONFIG_MPC8555CDS) || defined(CONFIG_MPC8541CDS) + /* + * This is a SW workaround for an apparent HW problem + * in the PCI controller on the MPC85555/41 CDS boards. + * The first config cycle must be to a valid, known + * device on the PCI bus in order to trick the PCI + * controller state machine into a known valid state. + * Without this, the first config cycle has the chance + * of hanging the controller permanently, just leaving + * it in a semi-working state, or leaving it working. + * + * Pick on the Tundra, Device 17, to get it right. + */ + { + u8 header_type; + + pci_hose_read_config_byte(hose, + PCI_BDF(0,BRIDGE_ID,0), + PCI_HEADER_TYPE, + &header_type); + } +#endif + + hose->last_busno = pci_hose_scan(hose); + +#ifdef CONFIG_MPC85XX_PCI2 + hose = &pci_hose[1]; + + hose->first_busno = pci_hose[0].last_busno + 1; + hose->last_busno = 0xff; + + pci_setup_indirect(hose, + (CONFIG_SYS_IMMR+0x9000), + (CONFIG_SYS_IMMR+0x9004)); + + dev = PCI_BDF(hose->first_busno, 0, 0); + pci_hose_read_config_word (hose, dev, PCI_COMMAND, ®16); + reg16 |= PCI_COMMAND_SERR | PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY; + pci_hose_write_config_word(hose, dev, PCI_COMMAND, reg16); + + /* + * Clear non-reserved bits in status register. + */ + pci_hose_write_config_word(hose, dev, PCI_STATUS, 0xffff); + + pcix2->potar1 = (CONFIG_SYS_PCI2_MEM_BUS >> 12) & 0x000fffff; + pcix2->potear1 = 0x00000000; + pcix2->powbar1 = (CONFIG_SYS_PCI2_MEM_PHYS >> 12) & 0x000fffff; + pcix2->powbear1 = 0x00000000; + pcix2->powar1 = (POWAR_EN | POWAR_MEM_READ | + POWAR_MEM_WRITE | (__ilog2(CONFIG_SYS_PCI2_MEM_SIZE) - 1)); + + pcix2->potar2 = (CONFIG_SYS_PCI2_IO_BUS >> 12) & 0x000fffff; + pcix2->potear2 = 0x00000000; + pcix2->powbar2 = (CONFIG_SYS_PCI2_IO_PHYS >> 12) & 0x000fffff; + pcix2->powbear2 = 0x00000000; + pcix2->powar2 = (POWAR_EN | POWAR_IO_READ | + POWAR_IO_WRITE | (__ilog2(CONFIG_SYS_PCI2_IO_SIZE) - 1)); + + pcix2->pitar1 = 0x00000000; + pcix2->piwbar1 = 0x00000000; + pcix2->piwar1 = (PIWAR_EN | PIWAR_PF | PIWAR_LOCAL | + PIWAR_READ_SNOOP | PIWAR_WRITE_SNOOP | PIWAR_MEM_2G); + + pcix2->powar3 = 0; + pcix2->powar4 = 0; + pcix2->piwar2 = 0; + pcix2->piwar3 = 0; + + pci_set_region(hose->regions + 0, + CONFIG_SYS_PCI2_MEM_BUS, + CONFIG_SYS_PCI2_MEM_PHYS, + CONFIG_SYS_PCI2_MEM_SIZE, + PCI_REGION_MEM); + + pci_set_region(hose->regions + 1, + CONFIG_SYS_PCI2_IO_BUS, + CONFIG_SYS_PCI2_IO_PHYS, + CONFIG_SYS_PCI2_IO_SIZE, + PCI_REGION_IO); + + hose->region_count = 2; + + /* + * Hose scan. + */ + pci_register_hose(hose); + + hose->last_busno = pci_hose_scan(hose); +#endif +} +#endif /* !CONFIG_FSL_PCI_INIT */ diff --git a/arch/powerpc/cpu/mpc85xx/qe_io.c b/arch/powerpc/cpu/mpc85xx/qe_io.c new file mode 100644 index 0000000..72a29b7 --- /dev/null +++ b/arch/powerpc/cpu/mpc85xx/qe_io.c @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2006 Freescale Semiconductor, Inc. + * + * Dave Liu <daveliu@freescale.com> + * based on source code of Shlomi Gridish + * + * 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" +#include "asm/errno.h" +#include "asm/io.h" +#include "asm/immap_85xx.h" + +#if defined(CONFIG_QE) +#define NUM_OF_PINS 32 +void qe_config_iopin(u8 port, u8 pin, int dir, int open_drain, int assign) +{ + u32 pin_2bit_mask; + u32 pin_2bit_dir; + u32 pin_2bit_assign; + u32 pin_1bit_mask; + u32 tmp_val; + volatile ccsr_gur_t *gur = (void *)(CONFIG_SYS_MPC85xx_GUTS_ADDR); + volatile par_io_t *par_io = (volatile par_io_t *) + &(gur->qe_par_io); + + /* Caculate pin location and 2bit mask and dir */ + pin_2bit_mask = (u32)(0x3 << (NUM_OF_PINS-(pin%(NUM_OF_PINS/2)+1)*2)); + pin_2bit_dir = (u32)(dir << (NUM_OF_PINS-(pin%(NUM_OF_PINS/2)+1)*2)); + + /* Setup the direction */ + tmp_val = (pin > (NUM_OF_PINS/2) - 1) ? \ + in_be32(&par_io[port].cpdir2) : + in_be32(&par_io[port].cpdir1); + + if (pin > (NUM_OF_PINS/2) -1) { + out_be32(&par_io[port].cpdir2, ~pin_2bit_mask & tmp_val); + out_be32(&par_io[port].cpdir2, pin_2bit_dir | tmp_val); + } else { + out_be32(&par_io[port].cpdir1, ~pin_2bit_mask & tmp_val); + out_be32(&par_io[port].cpdir1, pin_2bit_dir | tmp_val); + } + + /* Calculate pin location for 1bit mask */ + pin_1bit_mask = (u32)(1 << (NUM_OF_PINS - (pin+1))); + + /* Setup the open drain */ + tmp_val = in_be32(&par_io[port].cpodr); + if (open_drain) + out_be32(&par_io[port].cpodr, pin_1bit_mask | tmp_val); + else + out_be32(&par_io[port].cpodr, ~pin_1bit_mask & tmp_val); + + /* Setup the assignment */ + tmp_val = (pin > (NUM_OF_PINS/2) - 1) ? + in_be32(&par_io[port].cppar2): + in_be32(&par_io[port].cppar1); + pin_2bit_assign = (u32)(assign + << (NUM_OF_PINS - (pin%(NUM_OF_PINS/2)+1)*2)); + + /* Clear and set 2 bits mask */ + if (pin > (NUM_OF_PINS/2) - 1) { + out_be32(&par_io[port].cppar2, ~pin_2bit_mask & tmp_val); + out_be32(&par_io[port].cppar2, pin_2bit_assign | tmp_val); + } else { + out_be32(&par_io[port].cppar1, ~pin_2bit_mask & tmp_val); + out_be32(&par_io[port].cppar1, pin_2bit_assign | tmp_val); + } +} + +#endif /* CONFIG_QE */ diff --git a/arch/powerpc/cpu/mpc85xx/release.S b/arch/powerpc/cpu/mpc85xx/release.S new file mode 100644 index 0000000..0b5b9da --- /dev/null +++ b/arch/powerpc/cpu/mpc85xx/release.S @@ -0,0 +1,311 @@ +/* + * Copyright 2008-2010 Freescale Semiconductor, Inc. + * Kumar Gala <kumar.gala@freescale.com> + * + * 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 <config.h> +#include <mpc85xx.h> +#include <version.h> + +#define _LINUX_CONFIG_H 1 /* avoid reading Linux autoconf.h file */ + +#include <ppc_asm.tmpl> +#include <ppc_defs.h> + +#include <asm/cache.h> +#include <asm/mmu.h> + +/* To boot secondary cpus, we need a place for them to start up. + * Normally, they start at 0xfffffffc, but that's usually the + * firmware, and we don't want to have to run the firmware again. + * Instead, the primary cpu will set the BPTR to point here to + * this page. We then set up the core, and head to + * start_secondary. Note that this means that the code below + * must never exceed 1023 instructions (the branch at the end + * would then be the 1024th). + */ + .globl __secondary_start_page + .align 12 +__secondary_start_page: +/* First do some preliminary setup */ + lis r3, HID0_EMCP@h /* enable machine check */ +#ifndef CONFIG_E500MC + ori r3,r3,HID0_TBEN@l /* enable Timebase */ +#endif +#ifdef CONFIG_PHYS_64BIT + ori r3,r3,HID0_ENMAS7@l /* enable MAS7 updates */ +#endif + mtspr SPRN_HID0,r3 + +#ifndef CONFIG_E500MC + li r3,(HID1_ASTME|HID1_ABE)@l /* Addr streaming & broadcast */ + mfspr r0,PVR + andi. r0,r0,0xff + cmpwi r0,0x50@l /* if we are rev 5.0 or greater set MBDD */ + blt 1f + /* Set MBDD bit also */ + ori r3, r3, HID1_MBDD@l +1: + mtspr SPRN_HID1,r3 +#endif + + /* Enable branch prediction */ + lis r3,BUCSR_ENABLE@h + ori r3,r3,BUCSR_ENABLE@l + mtspr SPRN_BUCSR,r3 + + /* Ensure TB is 0 */ + li r3,0 + mttbl r3 + mttbu r3 + + /* Enable/invalidate the I-Cache */ + lis r2,(L1CSR1_ICFI|L1CSR1_ICLFR)@h + ori r2,r2,(L1CSR1_ICFI|L1CSR1_ICLFR)@l + mtspr SPRN_L1CSR1,r2 +1: + mfspr r3,SPRN_L1CSR1 + and. r1,r3,r2 + bne 1b + + lis r3,(L1CSR1_CPE|L1CSR1_ICE)@h + ori r3,r3,(L1CSR1_CPE|L1CSR1_ICE)@l + mtspr SPRN_L1CSR1,r3 + isync +2: + mfspr r3,SPRN_L1CSR1 + andi. r1,r3,L1CSR1_ICE@l + beq 2b + + /* Enable/invalidate the D-Cache */ + lis r2,(L1CSR0_DCFI|L1CSR0_DCLFR)@h + ori r2,r2,(L1CSR0_DCFI|L1CSR0_DCLFR)@l + mtspr SPRN_L1CSR0,r2 +1: + mfspr r3,SPRN_L1CSR0 + and. r1,r3,r2 + bne 1b + + lis r3,(L1CSR0_CPE|L1CSR0_DCE)@h + ori r3,r3,(L1CSR0_CPE|L1CSR0_DCE)@l + mtspr SPRN_L1CSR0,r3 + isync +2: + mfspr r3,SPRN_L1CSR0 + andi. r1,r3,L1CSR0_DCE@l + beq 2b + +#define toreset(x) (x - __secondary_start_page + 0xfffff000) + + /* get our PIR to figure out our table entry */ + lis r3,toreset(__spin_table)@h + ori r3,r3,toreset(__spin_table)@l + + /* r10 has the base address for the entry */ + mfspr r0,SPRN_PIR +#ifdef CONFIG_E500MC + rlwinm r4,r0,27,27,31 +#else + mr r4,r0 +#endif + slwi r8,r4,5 + add r10,r3,r8 + +#if defined(CONFIG_E500MC) && defined(CONFIG_SYS_CACHE_STASHING) + /* set stash id to (coreID) * 2 + 32 + L1 CT (0) */ + slwi r8,r4,1 + addi r8,r8,32 + mtspr L1CSR2,r8 +#endif + +#ifdef CONFIG_BACKSIDE_L2_CACHE + /* Enable/invalidate the L2 cache */ + msync + lis r2,(L2CSR0_L2FI|L2CSR0_L2LFC)@h + ori r2,r2,(L2CSR0_L2FI|L2CSR0_L2LFC)@l + mtspr SPRN_L2CSR0,r2 +1: + mfspr r3,SPRN_L2CSR0 + and. r1,r3,r2 + bne 1b + +#ifdef CONFIG_SYS_CACHE_STASHING + /* set stash id to (coreID) * 2 + 32 + L2 (1) */ + addi r3,r8,1 + mtspr SPRN_L2CSR1,r3 +#endif + + lis r3,CONFIG_SYS_INIT_L2CSR0@h + ori r3,r3,CONFIG_SYS_INIT_L2CSR0@l + mtspr SPRN_L2CSR0,r3 + isync +2: + mfspr r3,SPRN_L2CSR0 + andis. r1,r3,L2CSR0_L2E@h + beq 2b +#endif + +#define EPAPR_MAGIC (0x45504150) +#define ENTRY_ADDR_UPPER 0 +#define ENTRY_ADDR_LOWER 4 +#define ENTRY_R3_UPPER 8 +#define ENTRY_R3_LOWER 12 +#define ENTRY_RESV 16 +#define ENTRY_PIR 20 +#define ENTRY_R6_UPPER 24 +#define ENTRY_R6_LOWER 28 +#define ENTRY_SIZE 32 + + /* setup the entry */ + li r3,0 + li r8,1 + stw r0,ENTRY_PIR(r10) + stw r3,ENTRY_ADDR_UPPER(r10) + stw r8,ENTRY_ADDR_LOWER(r10) + stw r3,ENTRY_R3_UPPER(r10) + stw r4,ENTRY_R3_LOWER(r10) + stw r3,ENTRY_R6_UPPER(r10) + stw r3,ENTRY_R6_LOWER(r10) + + /* load r13 with the address of the 'bootpg' in SDRAM */ + lis r13,toreset(__bootpg_addr)@h + ori r13,r13,toreset(__bootpg_addr)@l + lwz r13,0(r13) + + /* setup mapping for AS = 1, and jump there */ + lis r11,(MAS0_TLBSEL(1)|MAS0_ESEL(1))@h + mtspr SPRN_MAS0,r11 + lis r11,(MAS1_VALID|MAS1_IPROT)@h + ori r11,r11,(MAS1_TS|MAS1_TSIZE(BOOKE_PAGESZ_4K))@l + mtspr SPRN_MAS1,r11 + oris r11,r13,(MAS2_I|MAS2_G)@h + ori r11,r13,(MAS2_I|MAS2_G)@l + mtspr SPRN_MAS2,r11 + oris r11,r13,(MAS3_SX|MAS3_SW|MAS3_SR)@h + ori r11,r13,(MAS3_SX|MAS3_SW|MAS3_SR)@l + mtspr SPRN_MAS3,r11 + tlbwe + + bl 1f +1: mflr r11 + /* + * OR in 0xfff to create a mask of the bootpg SDRAM address. We use + * this mask to fixup the cpu spin table and the address that we want + * to jump to, eg change them from 0xfffffxxx to 0x7ffffxxx if the + * bootpg is at 0x7ffff000 in SDRAM. + */ + ori r13,r13,0xfff + and r11, r11, r13 + and r10, r10, r13 + + addi r11,r11,(2f-1b) + mfmsr r13 + ori r12,r13,MSR_IS|MSR_DS@l + + mtspr SPRN_SRR0,r11 + mtspr SPRN_SRR1,r12 + rfi + + /* spin waiting for addr */ +2: + lwz r4,ENTRY_ADDR_LOWER(r10) + andi. r11,r4,1 + bne 2b + isync + + /* setup IVORs to match fixed offsets */ +#include "fixed_ivor.S" + + /* get the upper bits of the addr */ + lwz r11,ENTRY_ADDR_UPPER(r10) + + /* setup branch addr */ + mtspr SPRN_SRR0,r4 + + /* mark the entry as released */ + li r8,3 + stw r8,ENTRY_ADDR_LOWER(r10) + + /* mask by ~64M to setup our tlb we will jump to */ + rlwinm r12,r4,0,0,5 + + /* setup r3, r4, r5, r6, r7, r8, r9 */ + lwz r3,ENTRY_R3_LOWER(r10) + li r4,0 + li r5,0 + lwz r6,ENTRY_R6_LOWER(r10) + lis r7,(64*1024*1024)@h + li r8,0 + li r9,0 + + /* load up the pir */ + lwz r0,ENTRY_PIR(r10) + mtspr SPRN_PIR,r0 + mfspr r0,SPRN_PIR + stw r0,ENTRY_PIR(r10) + + mtspr IVPR,r12 +/* + * Coming here, we know the cpu has one TLB mapping in TLB1[0] + * which maps 0xfffff000-0xffffffff one-to-one. We set up a + * second mapping that maps addr 1:1 for 64M, and then we jump to + * addr + */ + lis r10,(MAS0_TLBSEL(1)|MAS0_ESEL(0))@h + mtspr SPRN_MAS0,r10 + lis r10,(MAS1_VALID|MAS1_IPROT)@h + ori r10,r10,(MAS1_TSIZE(BOOKE_PAGESZ_64M))@l + mtspr SPRN_MAS1,r10 + /* WIMGE = 0b00000 for now */ + mtspr SPRN_MAS2,r12 + ori r12,r12,(MAS3_SX|MAS3_SW|MAS3_SR) + mtspr SPRN_MAS3,r12 +#ifdef CONFIG_ENABLE_36BIT_PHYS + mtspr SPRN_MAS7,r11 +#endif + tlbwe + +/* Now we have another mapping for this page, so we jump to that + * mapping + */ + mtspr SPRN_SRR1,r13 + rfi + + /* + * Allocate some space for the SDRAM address of the bootpg. + * This variable has to be in the boot page so that it can + * be accessed by secondary cores when they come out of reset. + */ + .globl __bootpg_addr +__bootpg_addr: + .long 0 + + .align L1_CACHE_SHIFT + .globl __spin_table +__spin_table: + .space CONFIG_MAX_CPUS*ENTRY_SIZE + + /* Fill in the empty space. The actual reset vector is + * the last word of the page */ +__secondary_start_code_end: + .space 4092 - (__secondary_start_code_end - __secondary_start_page) +__secondary_reset_vector: + b __secondary_start_page diff --git a/arch/powerpc/cpu/mpc85xx/resetvec.S b/arch/powerpc/cpu/mpc85xx/resetvec.S new file mode 100644 index 0000000..29555d4 --- /dev/null +++ b/arch/powerpc/cpu/mpc85xx/resetvec.S @@ -0,0 +1,2 @@ + .section .resetvec,"ax" + b _start_e500 diff --git a/arch/powerpc/cpu/mpc85xx/serial_scc.c b/arch/powerpc/cpu/mpc85xx/serial_scc.c new file mode 100644 index 0000000..2dab212 --- /dev/null +++ b/arch/powerpc/cpu/mpc85xx/serial_scc.c @@ -0,0 +1,268 @@ +/* + * (C) Copyright 2003 Motorola Inc. + * Xianghua Xiao (X.Xiao@motorola.com) + * Modified based on 8260 for 8560. + * + * (C) Copyright 2000 + * Wolfgang Denk, DENX Software Engineering, wd@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 + * + * Hacked for MPC8260 by Murray.Jensen@cmst.csiro.au, 19-Oct-00. + */ + +/* + * Minimal serial functions needed to use one of the SCC ports + * as serial console interface. + */ + +#include <common.h> +#include <asm/cpm_85xx.h> + +DECLARE_GLOBAL_DATA_PTR; + +#if defined(CONFIG_CONS_ON_SCC) + +#if CONFIG_CONS_INDEX == 1 /* Console on SCC1 */ + +#define SCC_INDEX 0 +#define PROFF_SCC PROFF_SCC1 +#define CMXSCR_MASK (CMXSCR_GR1|CMXSCR_SC1|\ + CMXSCR_RS1CS_MSK|CMXSCR_TS1CS_MSK) +#define CMXSCR_VALUE (CMXSCR_RS1CS_BRG1|CMXSCR_TS1CS_BRG1) +#define CPM_CR_SCC_PAGE CPM_CR_SCC1_PAGE +#define CPM_CR_SCC_SBLOCK CPM_CR_SCC1_SBLOCK + +#elif CONFIG_CONS_INDEX == 2 /* Console on SCC2 */ + +#define SCC_INDEX 1 +#define PROFF_SCC PROFF_SCC2 +#define CMXSCR_MASK (CMXSCR_GR2|CMXSCR_SC2|\ + CMXSCR_RS2CS_MSK|CMXSCR_TS2CS_MSK) +#define CMXSCR_VALUE (CMXSCR_RS2CS_BRG2|CMXSCR_TS2CS_BRG2) +#define CPM_CR_SCC_PAGE CPM_CR_SCC2_PAGE +#define CPM_CR_SCC_SBLOCK CPM_CR_SCC2_SBLOCK + +#elif CONFIG_CONS_INDEX == 3 /* Console on SCC3 */ + +#define SCC_INDEX 2 +#define PROFF_SCC PROFF_SCC3 +#define CMXSCR_MASK (CMXSCR_GR3|CMXSCR_SC3|\ + CMXSCR_RS3CS_MSK|CMXSCR_TS3CS_MSK) +#define CMXSCR_VALUE (CMXSCR_RS3CS_BRG3|CMXSCR_TS3CS_BRG3) +#define CPM_CR_SCC_PAGE CPM_CR_SCC3_PAGE +#define CPM_CR_SCC_SBLOCK CPM_CR_SCC3_SBLOCK + +#elif CONFIG_CONS_INDEX == 4 /* Console on SCC4 */ + +#define SCC_INDEX 3 +#define PROFF_SCC PROFF_SCC4 +#define CMXSCR_MASK (CMXSCR_GR4|CMXSCR_SC4|\ + CMXSCR_RS4CS_MSK|CMXSCR_TS4CS_MSK) +#define CMXSCR_VALUE (CMXSCR_RS4CS_BRG4|CMXSCR_TS4CS_BRG4) +#define CPM_CR_SCC_PAGE CPM_CR_SCC4_PAGE +#define CPM_CR_SCC_SBLOCK CPM_CR_SCC4_SBLOCK + +#else + +#error "console not correctly defined" + +#endif + +int serial_init (void) +{ + volatile ccsr_cpm_t *cpm = (ccsr_cpm_t *)CONFIG_SYS_MPC85xx_CPM_ADDR; + volatile ccsr_cpm_scc_t *sp; + volatile scc_uart_t *up; + volatile cbd_t *tbdf, *rbdf; + volatile ccsr_cpm_cp_t *cp = &(cpm->im_cpm_cp); + uint dpaddr; + + /* initialize pointers to SCC */ + + sp = (ccsr_cpm_scc_t *) &(cpm->im_cpm_scc[SCC_INDEX]); + up = (scc_uart_t *)&(cpm->im_dprambase[PROFF_SCC]); + + /* Disable transmitter/receiver. + */ + sp->gsmrl &= ~(SCC_GSMRL_ENR | SCC_GSMRL_ENT); + + /* put the SCC channel into NMSI (non multiplexd serial interface) + * mode and wire the selected SCC Tx and Rx clocks to BRGx (15-15). + */ + cpm->im_cpm_mux.cmxscr = \ + (cpm->im_cpm_mux.cmxscr&~CMXSCR_MASK)|CMXSCR_VALUE; + + /* Set up the baud rate generator. + */ + serial_setbrg (); + + /* Allocate space for two buffer descriptors in the DP ram. + * damm: allocating space after the two buffers for rx/tx data + */ + + dpaddr = m8560_cpm_dpalloc((2 * sizeof (cbd_t)) + 2, 16); + + /* Set the physical address of the host memory buffers in + * the buffer descriptors. + */ + rbdf = (cbd_t *)&(cpm->im_dprambase[dpaddr]); + rbdf->cbd_bufaddr = (uint) (rbdf+2); + rbdf->cbd_sc = BD_SC_EMPTY | BD_SC_WRAP; + tbdf = rbdf + 1; + tbdf->cbd_bufaddr = ((uint) (rbdf+2)) + 1; + tbdf->cbd_sc = BD_SC_WRAP; + + /* Set up the uart parameters in the parameter ram. + */ + up->scc_genscc.scc_rbase = dpaddr; + up->scc_genscc.scc_tbase = dpaddr+sizeof(cbd_t); + up->scc_genscc.scc_rfcr = CPMFCR_EB; + up->scc_genscc.scc_tfcr = CPMFCR_EB; + up->scc_genscc.scc_mrblr = 1; + up->scc_maxidl = 0; + up->scc_brkcr = 1; + up->scc_parec = 0; + up->scc_frmec = 0; + up->scc_nosec = 0; + up->scc_brkec = 0; + up->scc_uaddr1 = 0; + up->scc_uaddr2 = 0; + up->scc_toseq = 0; + up->scc_char1 = up->scc_char2 = up->scc_char3 = up->scc_char4 = 0x8000; + up->scc_char5 = up->scc_char6 = up->scc_char7 = up->scc_char8 = 0x8000; + up->scc_rccm = 0xc0ff; + + /* Mask all interrupts and remove anything pending. + */ + sp->sccm = 0; + sp->scce = 0xffff; + + /* Set 8 bit FIFO, 16 bit oversampling and UART mode. + */ + sp->gsmrh = SCC_GSMRH_RFW; /* 8 bit FIFO */ + sp->gsmrl = \ + SCC_GSMRL_TDCR_16 | SCC_GSMRL_RDCR_16 | SCC_GSMRL_MODE_UART; + + /* Set CTS no flow control, 1 stop bit, 8 bit character length, + * normal async UART mode, no parity + */ + sp->psmr = SCU_PSMR_CL; + + /* execute the "Init Rx and Tx params" CP command. + */ + + while (cp->cpcr & CPM_CR_FLG) /* wait if cp is busy */ + ; + + cp->cpcr = mk_cr_cmd(CPM_CR_SCC_PAGE, CPM_CR_SCC_SBLOCK, + 0, CPM_CR_INIT_TRX) | CPM_CR_FLG; + + while (cp->cpcr & CPM_CR_FLG) /* wait if cp is busy */ + ; + + /* Enable transmitter/receiver. + */ + sp->gsmrl |= SCC_GSMRL_ENR | SCC_GSMRL_ENT; + + return (0); +} + +void +serial_setbrg (void) +{ +#if defined(CONFIG_CONS_USE_EXTC) + m8560_cpm_extcbrg(SCC_INDEX, gd->baudrate, + CONFIG_CONS_EXTC_RATE, CONFIG_CONS_EXTC_PINSEL); +#else + m8560_cpm_setbrg(SCC_INDEX, gd->baudrate); +#endif +} + +void +serial_putc(const char c) +{ + volatile scc_uart_t *up; + volatile cbd_t *tbdf; + volatile ccsr_cpm_t *cpm = (ccsr_cpm_t *)CONFIG_SYS_MPC85xx_CPM_ADDR; + + if (c == '\n') + serial_putc ('\r'); + + up = (scc_uart_t *)&(cpm->im_dprambase[PROFF_SCC]); + tbdf = (cbd_t *)&(cpm->im_dprambase[up->scc_genscc.scc_tbase]); + + /* Wait for last character to go. + */ + while (tbdf->cbd_sc & BD_SC_READY) + ; + + /* Load the character into the transmit buffer. + */ + *(volatile char *)tbdf->cbd_bufaddr = c; + tbdf->cbd_datlen = 1; + tbdf->cbd_sc |= BD_SC_READY; +} + +void +serial_puts (const char *s) +{ + while (*s) { + serial_putc (*s++); + } +} + +int +serial_getc(void) +{ + volatile cbd_t *rbdf; + volatile scc_uart_t *up; + volatile ccsr_cpm_t *cpm = (ccsr_cpm_t *)CONFIG_SYS_MPC85xx_CPM_ADDR; + unsigned char c; + + up = (scc_uart_t *)&(cpm->im_dprambase[PROFF_SCC]); + rbdf = (cbd_t *)&(cpm->im_dprambase[up->scc_genscc.scc_rbase]); + + /* Wait for character to show up. + */ + while (rbdf->cbd_sc & BD_SC_EMPTY) + ; + + /* Grab the char and clear the buffer again. + */ + c = *(volatile unsigned char *)rbdf->cbd_bufaddr; + rbdf->cbd_sc |= BD_SC_EMPTY; + + return (c); +} + +int +serial_tstc() +{ + volatile cbd_t *rbdf; + volatile scc_uart_t *up; + volatile ccsr_cpm_t *cpm = (ccsr_cpm_t *)CONFIG_SYS_MPC85xx_CPM_ADDR; + + up = (scc_uart_t *)&(cpm->im_dprambase[PROFF_SCC]); + rbdf = (cbd_t *)&(cpm->im_dprambase[up->scc_genscc.scc_rbase]); + + return ((rbdf->cbd_sc & BD_SC_EMPTY) == 0); +} + +#endif /* CONFIG_CONS_ON_SCC */ diff --git a/arch/powerpc/cpu/mpc85xx/speed.c b/arch/powerpc/cpu/mpc85xx/speed.c new file mode 100644 index 0000000..268edbc --- /dev/null +++ b/arch/powerpc/cpu/mpc85xx/speed.c @@ -0,0 +1,283 @@ +/* + * Copyright 2004, 2007-2009 Freescale Semiconductor, Inc. + * + * (C) Copyright 2003 Motorola Inc. + * Xianghua Xiao, (X.Xiao@motorola.com) + * + * (C) Copyright 2000 + * Wolfgang Denk, DENX Software Engineering, wd@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> +#include <ppc_asm.tmpl> +#include <asm/processor.h> +#include <asm/io.h> + +DECLARE_GLOBAL_DATA_PTR; + +/* --------------------------------------------------------------- */ + +void get_sys_info (sys_info_t * sysInfo) +{ + volatile ccsr_gur_t *gur = (void *)(CONFIG_SYS_MPC85xx_GUTS_ADDR); +#ifdef CONFIG_FSL_CORENET + volatile ccsr_clk_t *clk = (void *)(CONFIG_SYS_FSL_CORENET_CLK_ADDR); + + const u8 core_cplx_PLL[16] = { + [ 0] = 0, /* CC1 PPL / 1 */ + [ 1] = 0, /* CC1 PPL / 2 */ + [ 2] = 0, /* CC1 PPL / 4 */ + [ 4] = 1, /* CC2 PPL / 1 */ + [ 5] = 1, /* CC2 PPL / 2 */ + [ 6] = 1, /* CC2 PPL / 4 */ + [ 8] = 2, /* CC3 PPL / 1 */ + [ 9] = 2, /* CC3 PPL / 2 */ + [10] = 2, /* CC3 PPL / 4 */ + [12] = 3, /* CC4 PPL / 1 */ + [13] = 3, /* CC4 PPL / 2 */ + [14] = 3, /* CC4 PPL / 4 */ + }; + + const u8 core_cplx_PLL_div[16] = { + [ 0] = 1, /* CC1 PPL / 1 */ + [ 1] = 2, /* CC1 PPL / 2 */ + [ 2] = 4, /* CC1 PPL / 4 */ + [ 4] = 1, /* CC2 PPL / 1 */ + [ 5] = 2, /* CC2 PPL / 2 */ + [ 6] = 4, /* CC2 PPL / 4 */ + [ 8] = 1, /* CC3 PPL / 1 */ + [ 9] = 2, /* CC3 PPL / 2 */ + [10] = 4, /* CC3 PPL / 4 */ + [12] = 1, /* CC4 PPL / 1 */ + [13] = 2, /* CC4 PPL / 2 */ + [14] = 4, /* CC4 PPL / 4 */ + }; + uint lcrr_div, i, freqCC_PLL[4], rcw_tmp; + unsigned long sysclk = CONFIG_SYS_CLK_FREQ; + + sysInfo->freqSystemBus = sysclk; + sysInfo->freqDDRBus = sysclk; + freqCC_PLL[0] = sysclk; + freqCC_PLL[1] = sysclk; + freqCC_PLL[2] = sysclk; + freqCC_PLL[3] = sysclk; + + sysInfo->freqSystemBus *= (in_be32(&gur->rcwsr[0]) >> 25) & 0x1f; + sysInfo->freqDDRBus *= ((in_be32(&gur->rcwsr[0]) >> 17) & 0x1f); + freqCC_PLL[0] *= (in_be32(&clk->pllc1gsr) >> 1) & 0x3f; + freqCC_PLL[1] *= (in_be32(&clk->pllc2gsr) >> 1) & 0x3f; + freqCC_PLL[2] *= (in_be32(&clk->pllc3gsr) >> 1) & 0x3f; + freqCC_PLL[3] *= (in_be32(&clk->pllc4gsr) >> 1) & 0x3f; + + rcw_tmp = in_be32(&gur->rcwsr[3]); + for (i = 0; i < cpu_numcores(); i++) { + u32 c_pll_sel = (in_be32(&clk->clkc0csr + i*8) >> 27) & 0xf; + u32 cplx_pll = core_cplx_PLL[c_pll_sel]; + + sysInfo->freqProcessor[i] = + freqCC_PLL[cplx_pll] / core_cplx_PLL_div[c_pll_sel]; + } + +#define PME_CLK_SEL 0x80000000 +#define FM1_CLK_SEL 0x40000000 +#define FM2_CLK_SEL 0x20000000 + rcw_tmp = in_be32(&gur->rcwsr[7]); + +#ifdef CONFIG_SYS_DPAA_PME + if (rcw_tmp & PME_CLK_SEL) + sysInfo->freqPME = freqCC_PLL[2] / 2; + else + sysInfo->freqPME = sysInfo->freqSystemBus / 2; +#endif + +#ifdef CONFIG_SYS_DPAA_FMAN + if (rcw_tmp & FM1_CLK_SEL) + sysInfo->freqFMan[0] = freqCC_PLL[2] / 2; + else + sysInfo->freqFMan[0] = sysInfo->freqSystemBus / 2; +#if (CONFIG_SYS_NUM_FMAN) == 2 + if (rcw_tmp & FM2_CLK_SEL) + sysInfo->freqFMan[1] = freqCC_PLL[2] / 2; + else + sysInfo->freqFMan[1] = sysInfo->freqSystemBus / 2; +#endif +#endif + +#else + uint plat_ratio,e500_ratio,half_freqSystemBus; + uint lcrr_div; + int i; +#ifdef CONFIG_QE + u32 qe_ratio; +#endif + + plat_ratio = (gur->porpllsr) & 0x0000003e; + plat_ratio >>= 1; + sysInfo->freqSystemBus = plat_ratio * CONFIG_SYS_CLK_FREQ; + + /* Divide before multiply to avoid integer + * overflow for processor speeds above 2GHz */ + half_freqSystemBus = sysInfo->freqSystemBus/2; + for (i = 0; i < cpu_numcores(); i++) { + e500_ratio = ((gur->porpllsr) >> (i * 8 + 16)) & 0x3f; + sysInfo->freqProcessor[i] = e500_ratio * half_freqSystemBus; + } + + /* Note: freqDDRBus is the MCLK frequency, not the data rate. */ + sysInfo->freqDDRBus = sysInfo->freqSystemBus; + +#ifdef CONFIG_DDR_CLK_FREQ + { + u32 ddr_ratio = ((gur->porpllsr) & MPC85xx_PORPLLSR_DDR_RATIO) + >> MPC85xx_PORPLLSR_DDR_RATIO_SHIFT; + if (ddr_ratio != 0x7) + sysInfo->freqDDRBus = ddr_ratio * CONFIG_DDR_CLK_FREQ; + } +#endif +#endif + +#ifdef CONFIG_QE + qe_ratio = ((gur->porpllsr) & MPC85xx_PORPLLSR_QE_RATIO) + >> MPC85xx_PORPLLSR_QE_RATIO_SHIFT; + sysInfo->freqQE = qe_ratio * CONFIG_SYS_CLK_FREQ; +#endif + +#if defined(CONFIG_SYS_LBC_LCRR) + /* We will program LCRR to this value later */ + lcrr_div = CONFIG_SYS_LBC_LCRR & LCRR_CLKDIV; +#else + { + volatile ccsr_lbc_t *lbc = (void *)(CONFIG_SYS_MPC85xx_LBC_ADDR); + lcrr_div = in_be32(&lbc->lcrr) & LCRR_CLKDIV; + } +#endif + if (lcrr_div == 2 || lcrr_div == 4 || lcrr_div == 8) { +#if defined(CONFIG_FSL_CORENET) + /* If this is corenet based SoC, bit-representation + * for four times the clock divider values. + */ + lcrr_div *= 4; +#elif !defined(CONFIG_MPC8540) && !defined(CONFIG_MPC8541) && \ + !defined(CONFIG_MPC8555) && !defined(CONFIG_MPC8560) + /* + * Yes, the entire PQ38 family use the same + * bit-representation for twice the clock divider values. + */ + lcrr_div *= 2; +#endif + sysInfo->freqLocalBus = sysInfo->freqSystemBus / lcrr_div; + } else { + /* In case anyone cares what the unknown value is */ + sysInfo->freqLocalBus = lcrr_div; + } +} + + +int get_clocks (void) +{ + sys_info_t sys_info; +#ifdef CONFIG_MPC8544 + volatile ccsr_gur_t *gur = (void *) CONFIG_SYS_MPC85xx_GUTS_ADDR; +#endif +#if defined(CONFIG_CPM2) + volatile ccsr_cpm_t *cpm = (ccsr_cpm_t *)CONFIG_SYS_MPC85xx_CPM_ADDR; + uint sccr, dfbrg; + + /* set VCO = 4 * BRG */ + cpm->im_cpm_intctl.sccr &= 0xfffffffc; + sccr = cpm->im_cpm_intctl.sccr; + dfbrg = (sccr & SCCR_DFBRG_MSK) >> SCCR_DFBRG_SHIFT; +#endif + get_sys_info (&sys_info); + gd->cpu_clk = sys_info.freqProcessor[0]; + gd->bus_clk = sys_info.freqSystemBus; + gd->mem_clk = sys_info.freqDDRBus; + gd->lbc_clk = sys_info.freqLocalBus; + +#ifdef CONFIG_QE + gd->qe_clk = sys_info.freqQE; + gd->brg_clk = gd->qe_clk / 2; +#endif + /* + * The base clock for I2C depends on the actual SOC. Unfortunately, + * there is no pattern that can be used to determine the frequency, so + * the only choice is to look up the actual SOC number and use the value + * for that SOC. This information is taken from application note + * AN2919. + */ +#if defined(CONFIG_MPC8540) || defined(CONFIG_MPC8541) || \ + defined(CONFIG_MPC8560) || defined(CONFIG_MPC8555) + gd->i2c1_clk = sys_info.freqSystemBus; +#elif defined(CONFIG_MPC8544) + /* + * On the 8544, the I2C clock is the same as the SEC clock. This can be + * either CCB/2 or CCB/3, depending on the value of cfg_sec_freq. See + * 4.4.3.3 of the 8544 RM. Note that this might actually work for all + * 85xx, but only the 8544 has cfg_sec_freq, so it's unknown if the + * PORDEVSR2_SEC_CFG bit is 0 on all 85xx boards that are not an 8544. + */ + if (gur->pordevsr2 & MPC85xx_PORDEVSR2_SEC_CFG) + gd->i2c1_clk = sys_info.freqSystemBus / 3; + else + gd->i2c1_clk = sys_info.freqSystemBus / 2; +#else + /* Most 85xx SOCs use CCB/2, so this is the default behavior. */ + gd->i2c1_clk = sys_info.freqSystemBus / 2; +#endif + gd->i2c2_clk = gd->i2c1_clk; + +#if defined(CONFIG_FSL_ESDHC) +#ifdef CONFIG_MPC8569 + gd->sdhc_clk = gd->bus_clk; +#else + gd->sdhc_clk = gd->bus_clk / 2; +#endif +#endif /* defined(CONFIG_FSL_ESDHC) */ + +#if defined(CONFIG_CPM2) + gd->vco_out = 2*sys_info.freqSystemBus; + gd->cpm_clk = gd->vco_out / 2; + gd->scc_clk = gd->vco_out / 4; + gd->brg_clk = gd->vco_out / (1 << (2 * (dfbrg + 1))); +#endif + + if(gd->cpu_clk != 0) return (0); + else return (1); +} + + +/******************************************** + * get_bus_freq + * return system bus freq in Hz + *********************************************/ +ulong get_bus_freq (ulong dummy) +{ + return gd->bus_clk; +} + +/******************************************** + * get_ddr_freq + * return ddr bus freq in Hz + *********************************************/ +ulong get_ddr_freq (ulong dummy) +{ + return gd->mem_clk; +} diff --git a/arch/powerpc/cpu/mpc85xx/start.S b/arch/powerpc/cpu/mpc85xx/start.S new file mode 100644 index 0000000..b3cb56a --- /dev/null +++ b/arch/powerpc/cpu/mpc85xx/start.S @@ -0,0 +1,1195 @@ +/* + * Copyright 2004, 2007-2010 Freescale Semiconductor, Inc. + * Copyright (C) 2003 Motorola,Inc. + * + * 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 + */ + +/* U-Boot Startup Code for Motorola 85xx PowerPC based Embedded Boards + * + * The processor starts at 0xfffffffc and the code is first executed in the + * last 4K page(0xfffff000-0xffffffff) in flash/rom. + * + */ + +#include <config.h> +#include <mpc85xx.h> +#include <timestamp.h> +#include <version.h> + +#define _LINUX_CONFIG_H 1 /* avoid reading Linux autoconf.h file */ + +#include <ppc_asm.tmpl> +#include <ppc_defs.h> + +#include <asm/cache.h> +#include <asm/mmu.h> + +#ifndef CONFIG_IDENT_STRING +#define CONFIG_IDENT_STRING "" +#endif + +#undef MSR_KERNEL +#define MSR_KERNEL ( MSR_ME ) /* Machine Check */ + +/* + * Set up GOT: Global Offset Table + * + * Use r12 to access the GOT + */ + START_GOT + GOT_ENTRY(_GOT2_TABLE_) + GOT_ENTRY(_FIXUP_TABLE_) + +#ifndef CONFIG_NAND_SPL + GOT_ENTRY(_start) + GOT_ENTRY(_start_of_vectors) + GOT_ENTRY(_end_of_vectors) + GOT_ENTRY(transfer_to_handler) +#endif + + GOT_ENTRY(__init_end) + GOT_ENTRY(_end) + GOT_ENTRY(__bss_start) + END_GOT + +/* + * e500 Startup -- after reset only the last 4KB of the effective + * address space is mapped in the MMU L2 TLB1 Entry0. The .bootpg + * section is located at THIS LAST page and basically does three + * things: clear some registers, set up exception tables and + * add more TLB entries for 'larger spaces'(e.g. the boot rom) to + * continue the boot procedure. + + * Once the boot rom is mapped by TLB entries we can proceed + * with normal startup. + * + */ + + .section .bootpg,"ax" + .globl _start_e500 + +_start_e500: + +/* clear registers/arrays not reset by hardware */ + + /* L1 */ + li r0,2 + mtspr L1CSR0,r0 /* invalidate d-cache */ + mtspr L1CSR1,r0 /* invalidate i-cache */ + + mfspr r1,DBSR + mtspr DBSR,r1 /* Clear all valid bits */ + + /* + * Enable L1 Caches early + * + */ + +#if defined(CONFIG_E500MC) && defined(CONFIG_SYS_CACHE_STASHING) + /* set stash id to (coreID) * 2 + 32 + L1 CT (0) */ + li r2,(32 + 0) + mtspr L1CSR2,r2 +#endif + + /* Enable/invalidate the I-Cache */ + lis r2,(L1CSR1_ICFI|L1CSR1_ICLFR)@h + ori r2,r2,(L1CSR1_ICFI|L1CSR1_ICLFR)@l + mtspr SPRN_L1CSR1,r2 +1: + mfspr r3,SPRN_L1CSR1 + and. r1,r3,r2 + bne 1b + + lis r3,(L1CSR1_CPE|L1CSR1_ICE)@h + ori r3,r3,(L1CSR1_CPE|L1CSR1_ICE)@l + mtspr SPRN_L1CSR1,r3 + isync +2: + mfspr r3,SPRN_L1CSR1 + andi. r1,r3,L1CSR1_ICE@l + beq 2b + + /* Enable/invalidate the D-Cache */ + lis r2,(L1CSR0_DCFI|L1CSR0_DCLFR)@h + ori r2,r2,(L1CSR0_DCFI|L1CSR0_DCLFR)@l + mtspr SPRN_L1CSR0,r2 +1: + mfspr r3,SPRN_L1CSR0 + and. r1,r3,r2 + bne 1b + + lis r3,(L1CSR0_CPE|L1CSR0_DCE)@h + ori r3,r3,(L1CSR0_CPE|L1CSR0_DCE)@l + mtspr SPRN_L1CSR0,r3 + isync +2: + mfspr r3,SPRN_L1CSR0 + andi. r1,r3,L1CSR0_DCE@l + beq 2b + + /* Setup interrupt vectors */ + lis r1,TEXT_BASE@h + mtspr IVPR,r1 + + li r1,0x0100 + mtspr IVOR0,r1 /* 0: Critical input */ + li r1,0x0200 + mtspr IVOR1,r1 /* 1: Machine check */ + li r1,0x0300 + mtspr IVOR2,r1 /* 2: Data storage */ + li r1,0x0400 + mtspr IVOR3,r1 /* 3: Instruction storage */ + li r1,0x0500 + mtspr IVOR4,r1 /* 4: External interrupt */ + li r1,0x0600 + mtspr IVOR5,r1 /* 5: Alignment */ + li r1,0x0700 + mtspr IVOR6,r1 /* 6: Program check */ + li r1,0x0800 + mtspr IVOR7,r1 /* 7: floating point unavailable */ + li r1,0x0900 + mtspr IVOR8,r1 /* 8: System call */ + /* 9: Auxiliary processor unavailable(unsupported) */ + li r1,0x0a00 + mtspr IVOR10,r1 /* 10: Decrementer */ + li r1,0x0b00 + mtspr IVOR11,r1 /* 11: Interval timer */ + li r1,0x0c00 + mtspr IVOR12,r1 /* 12: Watchdog timer */ + li r1,0x0d00 + mtspr IVOR13,r1 /* 13: Data TLB error */ + li r1,0x0e00 + mtspr IVOR14,r1 /* 14: Instruction TLB error */ + li r1,0x0f00 + mtspr IVOR15,r1 /* 15: Debug */ + + /* Clear and set up some registers. */ + li r0,0x0000 + lis r1,0xffff + mtspr DEC,r0 /* prevent dec exceptions */ + mttbl r0 /* prevent fit & wdt exceptions */ + mttbu r0 + mtspr TSR,r1 /* clear all timer exception status */ + mtspr TCR,r0 /* disable all */ + mtspr ESR,r0 /* clear exception syndrome register */ + mtspr MCSR,r0 /* machine check syndrome register */ + mtxer r0 /* clear integer exception register */ + +#ifdef CONFIG_SYS_BOOK3E_HV + mtspr MAS8,r0 /* make sure MAS8 is clear */ +#endif + + /* Enable Time Base and Select Time Base Clock */ + lis r0,HID0_EMCP@h /* Enable machine check */ +#if defined(CONFIG_ENABLE_36BIT_PHYS) + ori r0,r0,HID0_ENMAS7@l /* Enable MAS7 */ +#endif +#ifndef CONFIG_E500MC + ori r0,r0,HID0_TBEN@l /* Enable Timebase */ +#endif + mtspr HID0,r0 + +#ifndef CONFIG_E500MC + li r0,(HID1_ASTME|HID1_ABE)@l /* Addr streaming & broadcast */ + mfspr r3,PVR + andi. r3,r3, 0xff + cmpwi r3,0x50@l /* if we are rev 5.0 or greater set MBDD */ + blt 1f + /* Set MBDD bit also */ + ori r0, r0, HID1_MBDD@l +1: + mtspr HID1,r0 +#endif + + /* Enable Branch Prediction */ +#if defined(CONFIG_BTB) + lis r0,BUCSR_ENABLE@h + ori r0,r0,BUCSR_ENABLE@l + mtspr SPRN_BUCSR,r0 +#endif + +#if defined(CONFIG_SYS_INIT_DBCR) + lis r1,0xffff + ori r1,r1,0xffff + mtspr DBSR,r1 /* Clear all status bits */ + lis r0,CONFIG_SYS_INIT_DBCR@h /* DBCR0[IDM] must be set */ + ori r0,r0,CONFIG_SYS_INIT_DBCR@l + mtspr DBCR0,r0 +#endif + +#ifdef CONFIG_MPC8569 +#define CONFIG_SYS_LBC_ADDR (CONFIG_SYS_CCSRBAR_DEFAULT + 0x5000) +#define CONFIG_SYS_LBCR_ADDR (CONFIG_SYS_LBC_ADDR + 0xd0) + + /* MPC8569 Rev.0 silcon needs to set bit 13 of LBCR to allow elBC to + * use address space which is more than 12bits, and it must be done in + * the 4K boot page. So we set this bit here. + */ + + /* create a temp mapping TLB0[0] for LBCR */ + lis r6,FSL_BOOKE_MAS0(0, 0, 0)@h + ori r6,r6,FSL_BOOKE_MAS0(0, 0, 0)@l + + lis r7,FSL_BOOKE_MAS1(1, 0, 0, 0, BOOKE_PAGESZ_4K)@h + ori r7,r7,FSL_BOOKE_MAS1(1, 0, 0, 0, BOOKE_PAGESZ_4K)@l + + lis r8,FSL_BOOKE_MAS2(CONFIG_SYS_LBC_ADDR, MAS2_I|MAS2_G)@h + ori r8,r8,FSL_BOOKE_MAS2(CONFIG_SYS_LBC_ADDR, MAS2_I|MAS2_G)@l + + lis r9,FSL_BOOKE_MAS3(CONFIG_SYS_LBC_ADDR, 0, + (MAS3_SX|MAS3_SW|MAS3_SR))@h + ori r9,r9,FSL_BOOKE_MAS3(CONFIG_SYS_LBC_ADDR, 0, + (MAS3_SX|MAS3_SW|MAS3_SR))@l + + mtspr MAS0,r6 + mtspr MAS1,r7 + mtspr MAS2,r8 + mtspr MAS3,r9 + isync + msync + tlbwe + + /* Set LBCR register */ + lis r4,CONFIG_SYS_LBCR_ADDR@h + ori r4,r4,CONFIG_SYS_LBCR_ADDR@l + + lis r5,CONFIG_SYS_LBC_LBCR@h + ori r5,r5,CONFIG_SYS_LBC_LBCR@l + stw r5,0(r4) + isync + + /* invalidate this temp TLB */ + lis r4,CONFIG_SYS_LBC_ADDR@h + ori r4,r4,CONFIG_SYS_LBC_ADDR@l + tlbivax 0,r4 + isync + +#endif /* CONFIG_MPC8569 */ + + lis r6,FSL_BOOKE_MAS0(1, 15, 0)@h + ori r6,r6,FSL_BOOKE_MAS0(1, 15, 0)@l + +#ifndef CONFIG_SYS_RAMBOOT + /* create a temp mapping in AS=1 to the 4M boot window */ + lis r7,FSL_BOOKE_MAS1(1, 1, 0, 1, BOOKE_PAGESZ_4M)@h + ori r7,r7,FSL_BOOKE_MAS1(1, 1, 0, 1, BOOKE_PAGESZ_4M)@l + + lis r8,FSL_BOOKE_MAS2(TEXT_BASE & 0xffc00000, (MAS2_I|MAS2_G))@h + ori r8,r8,FSL_BOOKE_MAS2(TEXT_BASE & 0xffc00000, (MAS2_I|MAS2_G))@l + + /* The 85xx has the default boot window 0xff800000 - 0xffffffff */ + lis r9,FSL_BOOKE_MAS3(0xffc00000, 0, (MAS3_SX|MAS3_SW|MAS3_SR))@h + ori r9,r9,FSL_BOOKE_MAS3(0xffc00000, 0, (MAS3_SX|MAS3_SW|MAS3_SR))@l +#else + /* + * create a temp mapping in AS=1 to the 1M TEXT_BASE space, the main + * image has been relocated to TEXT_BASE on the second stage. + */ + lis r7,FSL_BOOKE_MAS1(1, 1, 0, 1, BOOKE_PAGESZ_1M)@h + ori r7,r7,FSL_BOOKE_MAS1(1, 1, 0, 1, BOOKE_PAGESZ_1M)@l + + lis r8,FSL_BOOKE_MAS2(TEXT_BASE, (MAS2_I|MAS2_G))@h + ori r8,r8,FSL_BOOKE_MAS2(TEXT_BASE, (MAS2_I|MAS2_G))@l + + lis r9,FSL_BOOKE_MAS3(TEXT_BASE, 0, (MAS3_SX|MAS3_SW|MAS3_SR))@h + ori r9,r9,FSL_BOOKE_MAS3(TEXT_BASE, 0, (MAS3_SX|MAS3_SW|MAS3_SR))@l +#endif + + mtspr MAS0,r6 + mtspr MAS1,r7 + mtspr MAS2,r8 + mtspr MAS3,r9 + isync + msync + tlbwe + + /* create a temp mapping in AS=1 to the stack */ + lis r6,FSL_BOOKE_MAS0(1, 14, 0)@h + ori r6,r6,FSL_BOOKE_MAS0(1, 14, 0)@l + + lis r7,FSL_BOOKE_MAS1(1, 1, 0, 1, BOOKE_PAGESZ_16K)@h + ori r7,r7,FSL_BOOKE_MAS1(1, 1, 0, 1, BOOKE_PAGESZ_16K)@l + + lis r8,FSL_BOOKE_MAS2(CONFIG_SYS_INIT_RAM_ADDR, 0)@h + ori r8,r8,FSL_BOOKE_MAS2(CONFIG_SYS_INIT_RAM_ADDR, 0)@l + + lis r9,FSL_BOOKE_MAS3(CONFIG_SYS_INIT_RAM_ADDR, 0, (MAS3_SX|MAS3_SW|MAS3_SR))@h + ori r9,r9,FSL_BOOKE_MAS3(CONFIG_SYS_INIT_RAM_ADDR, 0, (MAS3_SX|MAS3_SW|MAS3_SR))@l + + mtspr MAS0,r6 + mtspr MAS1,r7 + mtspr MAS2,r8 + mtspr MAS3,r9 + isync + msync + tlbwe + + lis r6,MSR_IS|MSR_DS@h + ori r6,r6,MSR_IS|MSR_DS@l + lis r7,switch_as@h + ori r7,r7,switch_as@l + + mtspr SPRN_SRR0,r7 + mtspr SPRN_SRR1,r6 + rfi + +switch_as: +/* L1 DCache is used for initial RAM */ + + /* Allocate Initial RAM in data cache. + */ + lis r3,CONFIG_SYS_INIT_RAM_ADDR@h + ori r3,r3,CONFIG_SYS_INIT_RAM_ADDR@l + mfspr r2, L1CFG0 + andi. r2, r2, 0x1ff + /* cache size * 1024 / (2 * L1 line size) */ + slwi r2, r2, (10 - 1 - L1_CACHE_SHIFT) + mtctr r2 + li r0,0 +1: + dcbz r0,r3 + dcbtls 0,r0,r3 + addi r3,r3,CONFIG_SYS_CACHELINE_SIZE + bdnz 1b + + /* Jump out the last 4K page and continue to 'normal' start */ +#ifdef CONFIG_SYS_RAMBOOT + b _start_cont +#else + /* Calculate absolute address in FLASH and jump there */ + /*--------------------------------------------------------------*/ + lis r3,CONFIG_SYS_MONITOR_BASE@h + ori r3,r3,CONFIG_SYS_MONITOR_BASE@l + addi r3,r3,_start_cont - _start + _START_OFFSET + mtlr r3 + blr +#endif + + .text + .globl _start +_start: + .long 0x27051956 /* U-BOOT Magic Number */ + .globl version_string +version_string: + .ascii U_BOOT_VERSION + .ascii " (", U_BOOT_DATE, " - ", U_BOOT_TIME, ")" + .ascii CONFIG_IDENT_STRING, "\0" + + .align 4 + .globl _start_cont +_start_cont: + /* Setup the stack in initial RAM,could be L2-as-SRAM or L1 dcache*/ + lis r1,CONFIG_SYS_INIT_RAM_ADDR@h + ori r1,r1,CONFIG_SYS_INIT_SP_OFFSET@l + + li r0,0 + stwu r0,-4(r1) + stwu r0,-4(r1) /* Terminate call chain */ + + stwu r1,-8(r1) /* Save back chain and move SP */ + lis r0,RESET_VECTOR@h /* Address of reset vector */ + ori r0,r0,RESET_VECTOR@l + stwu r1,-8(r1) /* Save back chain and move SP */ + stw r0,+12(r1) /* Save return addr (underflow vect) */ + + GET_GOT + bl cpu_init_early_f + + /* switch back to AS = 0 */ + lis r3,(MSR_CE|MSR_ME|MSR_DE)@h + ori r3,r3,(MSR_CE|MSR_ME|MSR_DE)@l + mtmsr r3 + isync + + bl cpu_init_f + bl board_init_f + isync + +#ifndef CONFIG_NAND_SPL + . = EXC_OFF_SYS_RESET + .globl _start_of_vectors +_start_of_vectors: + +/* Critical input. */ + CRIT_EXCEPTION(0x0100, CriticalInput, CritcalInputException) + +/* Machine check */ + MCK_EXCEPTION(0x200, MachineCheck, MachineCheckException) + +/* Data Storage exception. */ + STD_EXCEPTION(0x0300, DataStorage, UnknownException) + +/* Instruction Storage exception. */ + STD_EXCEPTION(0x0400, InstStorage, UnknownException) + +/* External Interrupt exception. */ + STD_EXCEPTION(0x0500, ExtInterrupt, ExtIntException) + +/* Alignment exception. */ + . = 0x0600 +Alignment: + EXCEPTION_PROLOG(SRR0, SRR1) + mfspr r4,DAR + stw r4,_DAR(r21) + mfspr r5,DSISR + stw r5,_DSISR(r21) + addi r3,r1,STACK_FRAME_OVERHEAD + EXC_XFER_TEMPLATE(Alignment, AlignmentException, MSR_KERNEL, COPY_EE) + +/* Program check exception */ + . = 0x0700 +ProgramCheck: + EXCEPTION_PROLOG(SRR0, SRR1) + addi r3,r1,STACK_FRAME_OVERHEAD + EXC_XFER_TEMPLATE(ProgramCheck, ProgramCheckException, + MSR_KERNEL, COPY_EE) + + /* No FPU on MPC85xx. This exception is not supposed to happen. + */ + STD_EXCEPTION(0x0800, FPUnavailable, UnknownException) + + . = 0x0900 +/* + * r0 - SYSCALL number + * r3-... arguments + */ +SystemCall: + addis r11,r0,0 /* get functions table addr */ + ori r11,r11,0 /* Note: this code is patched in trap_init */ + addis r12,r0,0 /* get number of functions */ + ori r12,r12,0 + + cmplw 0,r0,r12 + bge 1f + + rlwinm r0,r0,2,0,31 /* fn_addr = fn_tbl[r0] */ + add r11,r11,r0 + lwz r11,0(r11) + + li r20,0xd00-4 /* Get stack pointer */ + lwz r12,0(r20) + subi r12,r12,12 /* Adjust stack pointer */ + li r0,0xc00+_end_back-SystemCall + cmplw 0,r0,r12 /* Check stack overflow */ + bgt 1f + stw r12,0(r20) + + mflr r0 + stw r0,0(r12) + mfspr r0,SRR0 + stw r0,4(r12) + mfspr r0,SRR1 + stw r0,8(r12) + + li r12,0xc00+_back-SystemCall + mtlr r12 + mtspr SRR0,r11 + +1: SYNC + rfi +_back: + + mfmsr r11 /* Disable interrupts */ + li r12,0 + ori r12,r12,MSR_EE + andc r11,r11,r12 + SYNC /* Some chip revs need this... */ + mtmsr r11 + SYNC + + li r12,0xd00-4 /* restore regs */ + lwz r12,0(r12) + + lwz r11,0(r12) + mtlr r11 + lwz r11,4(r12) + mtspr SRR0,r11 + lwz r11,8(r12) + mtspr SRR1,r11 + + addi r12,r12,12 /* Adjust stack pointer */ + li r20,0xd00-4 + stw r12,0(r20) + + SYNC + rfi +_end_back: + + STD_EXCEPTION(0x0a00, Decrementer, timer_interrupt) + STD_EXCEPTION(0x0b00, IntervalTimer, UnknownException) + STD_EXCEPTION(0x0c00, WatchdogTimer, UnknownException) + + STD_EXCEPTION(0x0d00, DataTLBError, UnknownException) + STD_EXCEPTION(0x0e00, InstructionTLBError, UnknownException) + + CRIT_EXCEPTION(0x0f00, DebugBreakpoint, DebugException ) + + .globl _end_of_vectors +_end_of_vectors: + + + . = . + (0x100 - ( . & 0xff )) /* align for debug */ + +/* + * This code finishes saving the registers to the exception frame + * and jumps to the appropriate handler for the exception. + * Register r21 is pointer into trap frame, r1 has new stack pointer. + */ + .globl transfer_to_handler +transfer_to_handler: + stw r22,_NIP(r21) + lis r22,MSR_POW@h + andc r23,r23,r22 + stw r23,_MSR(r21) + SAVE_GPR(7, r21) + SAVE_4GPRS(8, r21) + SAVE_8GPRS(12, r21) + SAVE_8GPRS(24, r21) + + mflr r23 + andi. r24,r23,0x3f00 /* get vector offset */ + stw r24,TRAP(r21) + li r22,0 + stw r22,RESULT(r21) + mtspr SPRG2,r22 /* r1 is now kernel sp */ + + lwz r24,0(r23) /* virtual address of handler */ + lwz r23,4(r23) /* where to go when done */ + mtspr SRR0,r24 + mtspr SRR1,r20 + mtlr r23 + SYNC + rfi /* jump to handler, enable MMU */ + +int_return: + mfmsr r28 /* Disable interrupts */ + li r4,0 + ori r4,r4,MSR_EE + andc r28,r28,r4 + SYNC /* Some chip revs need this... */ + mtmsr r28 + SYNC + lwz r2,_CTR(r1) + lwz r0,_LINK(r1) + mtctr r2 + mtlr r0 + lwz r2,_XER(r1) + lwz r0,_CCR(r1) + mtspr XER,r2 + mtcrf 0xFF,r0 + REST_10GPRS(3, r1) + REST_10GPRS(13, r1) + REST_8GPRS(23, r1) + REST_GPR(31, r1) + lwz r2,_NIP(r1) /* Restore environment */ + lwz r0,_MSR(r1) + mtspr SRR0,r2 + mtspr SRR1,r0 + lwz r0,GPR0(r1) + lwz r2,GPR2(r1) + lwz r1,GPR1(r1) + SYNC + rfi + +crit_return: + mfmsr r28 /* Disable interrupts */ + li r4,0 + ori r4,r4,MSR_EE + andc r28,r28,r4 + SYNC /* Some chip revs need this... */ + mtmsr r28 + SYNC + lwz r2,_CTR(r1) + lwz r0,_LINK(r1) + mtctr r2 + mtlr r0 + lwz r2,_XER(r1) + lwz r0,_CCR(r1) + mtspr XER,r2 + mtcrf 0xFF,r0 + REST_10GPRS(3, r1) + REST_10GPRS(13, r1) + REST_8GPRS(23, r1) + REST_GPR(31, r1) + lwz r2,_NIP(r1) /* Restore environment */ + lwz r0,_MSR(r1) + mtspr SPRN_CSRR0,r2 + mtspr SPRN_CSRR1,r0 + lwz r0,GPR0(r1) + lwz r2,GPR2(r1) + lwz r1,GPR1(r1) + SYNC + rfci + +mck_return: + mfmsr r28 /* Disable interrupts */ + li r4,0 + ori r4,r4,MSR_EE + andc r28,r28,r4 + SYNC /* Some chip revs need this... */ + mtmsr r28 + SYNC + lwz r2,_CTR(r1) + lwz r0,_LINK(r1) + mtctr r2 + mtlr r0 + lwz r2,_XER(r1) + lwz r0,_CCR(r1) + mtspr XER,r2 + mtcrf 0xFF,r0 + REST_10GPRS(3, r1) + REST_10GPRS(13, r1) + REST_8GPRS(23, r1) + REST_GPR(31, r1) + lwz r2,_NIP(r1) /* Restore environment */ + lwz r0,_MSR(r1) + mtspr SPRN_MCSRR0,r2 + mtspr SPRN_MCSRR1,r0 + lwz r0,GPR0(r1) + lwz r2,GPR2(r1) + lwz r1,GPR1(r1) + SYNC + rfmci + +/* Cache functions. +*/ +.globl invalidate_icache +invalidate_icache: + mfspr r0,L1CSR1 + ori r0,r0,L1CSR1_ICFI + msync + isync + mtspr L1CSR1,r0 + isync + blr /* entire I cache */ + +.globl invalidate_dcache +invalidate_dcache: + mfspr r0,L1CSR0 + ori r0,r0,L1CSR0_DCFI + msync + isync + mtspr L1CSR0,r0 + isync + blr + + .globl icache_enable +icache_enable: + mflr r8 + bl invalidate_icache + mtlr r8 + isync + mfspr r4,L1CSR1 + ori r4,r4,0x0001 + oris r4,r4,0x0001 + mtspr L1CSR1,r4 + isync + blr + + .globl icache_disable +icache_disable: + mfspr r0,L1CSR1 + lis r3,0 + ori r3,r3,L1CSR1_ICE + andc r0,r0,r3 + mtspr L1CSR1,r0 + isync + blr + + .globl icache_status +icache_status: + mfspr r3,L1CSR1 + andi. r3,r3,L1CSR1_ICE + blr + + .globl dcache_enable +dcache_enable: + mflr r8 + bl invalidate_dcache + mtlr r8 + isync + mfspr r0,L1CSR0 + ori r0,r0,0x0001 + oris r0,r0,0x0001 + msync + isync + mtspr L1CSR0,r0 + isync + blr + + .globl dcache_disable +dcache_disable: + mfspr r3,L1CSR0 + lis r4,0 + ori r4,r4,L1CSR0_DCE + andc r3,r3,r4 + mtspr L1CSR0,r0 + isync + blr + + .globl dcache_status +dcache_status: + mfspr r3,L1CSR0 + andi. r3,r3,L1CSR0_DCE + blr + + .globl get_pir +get_pir: + mfspr r3,PIR + blr + + .globl get_pvr +get_pvr: + mfspr r3,PVR + blr + + .globl get_svr +get_svr: + mfspr r3,SVR + blr + + .globl wr_tcr +wr_tcr: + mtspr TCR,r3 + blr + +/*------------------------------------------------------------------------------- */ +/* Function: in8 */ +/* Description: Input 8 bits */ +/*------------------------------------------------------------------------------- */ + .globl in8 +in8: + lbz r3,0x0000(r3) + blr + +/*------------------------------------------------------------------------------- */ +/* Function: out8 */ +/* Description: Output 8 bits */ +/*------------------------------------------------------------------------------- */ + .globl out8 +out8: + stb r4,0x0000(r3) + sync + blr + +/*------------------------------------------------------------------------------- */ +/* Function: out16 */ +/* Description: Output 16 bits */ +/*------------------------------------------------------------------------------- */ + .globl out16 +out16: + sth r4,0x0000(r3) + sync + blr + +/*------------------------------------------------------------------------------- */ +/* Function: out16r */ +/* Description: Byte reverse and output 16 bits */ +/*------------------------------------------------------------------------------- */ + .globl out16r +out16r: + sthbrx r4,r0,r3 + sync + blr + +/*------------------------------------------------------------------------------- */ +/* Function: out32 */ +/* Description: Output 32 bits */ +/*------------------------------------------------------------------------------- */ + .globl out32 +out32: + stw r4,0x0000(r3) + sync + blr + +/*------------------------------------------------------------------------------- */ +/* Function: out32r */ +/* Description: Byte reverse and output 32 bits */ +/*------------------------------------------------------------------------------- */ + .globl out32r +out32r: + stwbrx r4,r0,r3 + sync + blr + +/*------------------------------------------------------------------------------- */ +/* Function: in16 */ +/* Description: Input 16 bits */ +/*------------------------------------------------------------------------------- */ + .globl in16 +in16: + lhz r3,0x0000(r3) + blr + +/*------------------------------------------------------------------------------- */ +/* Function: in16r */ +/* Description: Input 16 bits and byte reverse */ +/*------------------------------------------------------------------------------- */ + .globl in16r +in16r: + lhbrx r3,r0,r3 + blr + +/*------------------------------------------------------------------------------- */ +/* Function: in32 */ +/* Description: Input 32 bits */ +/*------------------------------------------------------------------------------- */ + .globl in32 +in32: + lwz 3,0x0000(3) + blr + +/*------------------------------------------------------------------------------- */ +/* Function: in32r */ +/* Description: Input 32 bits and byte reverse */ +/*------------------------------------------------------------------------------- */ + .globl in32r +in32r: + lwbrx r3,r0,r3 + blr +#endif /* !CONFIG_NAND_SPL */ + +/*------------------------------------------------------------------------------*/ + +/* + * void write_tlb(mas0, mas1, mas2, mas3, mas7) + */ + .globl write_tlb +write_tlb: + mtspr MAS0,r3 + mtspr MAS1,r4 + mtspr MAS2,r5 + mtspr MAS3,r6 +#ifdef CONFIG_ENABLE_36BIT_PHYS + mtspr MAS7,r7 +#endif + li r3,0 +#ifdef CONFIG_SYS_BOOK3E_HV + mtspr MAS8,r3 +#endif + isync + tlbwe + msync + isync + blr + +/* + * void relocate_code (addr_sp, gd, addr_moni) + * + * This "function" does not return, instead it continues in RAM + * after relocating the monitor code. + * + * r3 = dest + * r4 = src + * r5 = length in bytes + * r6 = cachelinesize + */ + .globl relocate_code +relocate_code: + mr r1,r3 /* Set new stack pointer */ + mr r9,r4 /* Save copy of Init Data pointer */ + mr r10,r5 /* Save copy of Destination Address */ + + GET_GOT + mr r3,r5 /* Destination Address */ + lis r4,CONFIG_SYS_MONITOR_BASE@h /* Source Address */ + ori r4,r4,CONFIG_SYS_MONITOR_BASE@l + lwz r5,GOT(__init_end) + sub r5,r5,r4 + li r6,CONFIG_SYS_CACHELINE_SIZE /* Cache Line Size */ + + /* + * Fix GOT pointer: + * + * New GOT-PTR = (old GOT-PTR - CONFIG_SYS_MONITOR_BASE) + Destination Address + * + * Offset: + */ + sub r15,r10,r4 + + /* First our own GOT */ + add r12,r12,r15 + /* the the one used by the C code */ + add r30,r30,r15 + + /* + * Now relocate code + */ + + cmplw cr1,r3,r4 + addi r0,r5,3 + srwi. r0,r0,2 + beq cr1,4f /* In place copy is not necessary */ + beq 7f /* Protect against 0 count */ + mtctr r0 + bge cr1,2f + + la r8,-4(r4) + la r7,-4(r3) +1: lwzu r0,4(r8) + stwu r0,4(r7) + bdnz 1b + b 4f + +2: slwi r0,r0,2 + add r8,r4,r0 + add r7,r3,r0 +3: lwzu r0,-4(r8) + stwu r0,-4(r7) + bdnz 3b + +/* + * Now flush the cache: note that we must start from a cache aligned + * address. Otherwise we might miss one cache line. + */ +4: cmpwi r6,0 + add r5,r3,r5 + beq 7f /* Always flush prefetch queue in any case */ + subi r0,r6,1 + andc r3,r3,r0 + mr r4,r3 +5: dcbst 0,r4 + add r4,r4,r6 + cmplw r4,r5 + blt 5b + sync /* Wait for all dcbst to complete on bus */ + mr r4,r3 +6: icbi 0,r4 + add r4,r4,r6 + cmplw r4,r5 + blt 6b +7: sync /* Wait for all icbi to complete on bus */ + isync + + /* + * Re-point the IVPR at RAM + */ + mtspr IVPR,r10 + +/* + * We are done. Do not return, instead branch to second part of board + * initialization, now running from RAM. + */ + + addi r0,r10,in_ram - _start + _START_OFFSET + mtlr r0 + blr /* NEVER RETURNS! */ + .globl in_ram +in_ram: + + /* + * Relocation Function, r12 point to got2+0x8000 + * + * Adjust got2 pointers, no need to check for 0, this code + * already puts a few entries in the table. + */ + li r0,__got2_entries@sectoff@l + la r3,GOT(_GOT2_TABLE_) + lwz r11,GOT(_GOT2_TABLE_) + mtctr r0 + sub r11,r3,r11 + addi r3,r3,-4 +1: lwzu r0,4(r3) + cmpwi r0,0 + beq- 2f + add r0,r0,r11 + stw r0,0(r3) +2: bdnz 1b + + /* + * Now adjust the fixups and the pointers to the fixups + * in case we need to move ourselves again. + */ + li r0,__fixup_entries@sectoff@l + lwz r3,GOT(_FIXUP_TABLE_) + cmpwi r0,0 + mtctr r0 + addi r3,r3,-4 + beq 4f +3: lwzu r4,4(r3) + lwzux r0,r4,r11 + add r0,r0,r11 + stw r10,0(r3) + stw r0,0(r4) + bdnz 3b +4: +clear_bss: + /* + * Now clear BSS segment + */ + lwz r3,GOT(__bss_start) + lwz r4,GOT(_end) + + cmplw 0,r3,r4 + beq 6f + + li r0,0 +5: + stw r0,0(r3) + addi r3,r3,4 + cmplw 0,r3,r4 + bne 5b +6: + + mr r3,r9 /* Init Data pointer */ + mr r4,r10 /* Destination Address */ + bl board_init_r + +#ifndef CONFIG_NAND_SPL + /* + * Copy exception vector code to low memory + * + * r3: dest_addr + * r7: source address, r8: end address, r9: target address + */ + .globl trap_init +trap_init: + mflr r4 /* save link register */ + GET_GOT + lwz r7,GOT(_start_of_vectors) + lwz r8,GOT(_end_of_vectors) + + li r9,0x100 /* reset vector always at 0x100 */ + + cmplw 0,r7,r8 + bgelr /* return if r7>=r8 - just in case */ +1: + lwz r0,0(r7) + stw r0,0(r9) + addi r7,r7,4 + addi r9,r9,4 + cmplw 0,r7,r8 + bne 1b + + /* + * relocate `hdlr' and `int_return' entries + */ + li r7,.L_CriticalInput - _start + _START_OFFSET + bl trap_reloc + li r7,.L_MachineCheck - _start + _START_OFFSET + bl trap_reloc + li r7,.L_DataStorage - _start + _START_OFFSET + bl trap_reloc + li r7,.L_InstStorage - _start + _START_OFFSET + bl trap_reloc + li r7,.L_ExtInterrupt - _start + _START_OFFSET + bl trap_reloc + li r7,.L_Alignment - _start + _START_OFFSET + bl trap_reloc + li r7,.L_ProgramCheck - _start + _START_OFFSET + bl trap_reloc + li r7,.L_FPUnavailable - _start + _START_OFFSET + bl trap_reloc + li r7,.L_Decrementer - _start + _START_OFFSET + bl trap_reloc + li r7,.L_IntervalTimer - _start + _START_OFFSET + li r8,_end_of_vectors - _start + _START_OFFSET +2: + bl trap_reloc + addi r7,r7,0x100 /* next exception vector */ + cmplw 0,r7,r8 + blt 2b + + lis r7,0x0 + mtspr IVPR,r7 + + mtlr r4 /* restore link register */ + blr + +.globl unlock_ram_in_cache +unlock_ram_in_cache: + /* invalidate the INIT_RAM section */ + lis r3,(CONFIG_SYS_INIT_RAM_ADDR & ~(CONFIG_SYS_CACHELINE_SIZE-1))@h + ori r3,r3,(CONFIG_SYS_INIT_RAM_ADDR & ~(CONFIG_SYS_CACHELINE_SIZE-1))@l + mfspr r4,L1CFG0 + andi. r4,r4,0x1ff + slwi r4,r4,(10 - 1 - L1_CACHE_SHIFT) + mtctr r4 +1: dcbi r0,r3 + addi r3,r3,CONFIG_SYS_CACHELINE_SIZE + bdnz 1b + sync + + /* Invalidate the TLB entries for the cache */ + lis r3,CONFIG_SYS_INIT_RAM_ADDR@h + ori r3,r3,CONFIG_SYS_INIT_RAM_ADDR@l + tlbivax 0,r3 + addi r3,r3,0x1000 + tlbivax 0,r3 + addi r3,r3,0x1000 + tlbivax 0,r3 + addi r3,r3,0x1000 + tlbivax 0,r3 + isync + blr + +.globl flush_dcache +flush_dcache: + mfspr r3,SPRN_L1CFG0 + + rlwinm r5,r3,9,3 /* Extract cache block size */ + twlgti r5,1 /* Only 32 and 64 byte cache blocks + * are currently defined. + */ + li r4,32 + subfic r6,r5,2 /* r6 = log2(1KiB / cache block size) - + * log2(number of ways) + */ + slw r5,r4,r5 /* r5 = cache block size */ + + rlwinm r7,r3,0,0xff /* Extract number of KiB in the cache */ + mulli r7,r7,13 /* An 8-way cache will require 13 + * loads per set. + */ + slw r7,r7,r6 + + /* save off HID0 and set DCFA */ + mfspr r8,SPRN_HID0 + ori r9,r8,HID0_DCFA@l + mtspr SPRN_HID0,r9 + isync + + lis r4,0 + mtctr r7 + +1: lwz r3,0(r4) /* Load... */ + add r4,r4,r5 + bdnz 1b + + msync + lis r4,0 + mtctr r7 + +1: dcbf 0,r4 /* ...and flush. */ + add r4,r4,r5 + bdnz 1b + + /* restore HID0 */ + mtspr SPRN_HID0,r8 + isync + + blr + +.globl setup_ivors +setup_ivors: + +#include "fixed_ivor.S" + blr +#endif /* !CONFIG_NAND_SPL */ diff --git a/arch/powerpc/cpu/mpc85xx/tlb.c b/arch/powerpc/cpu/mpc85xx/tlb.c new file mode 100644 index 0000000..b3037ac --- /dev/null +++ b/arch/powerpc/cpu/mpc85xx/tlb.c @@ -0,0 +1,277 @@ +/* + * Copyright 2008-2009 Freescale Semiconductor, Inc. + * + * (C) Copyright 2000 + * Wolfgang Denk, DENX Software Engineering, wd@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> +#include <asm/processor.h> +#include <asm/mmu.h> +#ifdef CONFIG_ADDR_MAP +#include <addr_map.h> +#endif + +DECLARE_GLOBAL_DATA_PTR; + +void invalidate_tlb(u8 tlb) +{ + if (tlb == 0) + mtspr(MMUCSR0, 0x4); + if (tlb == 1) + mtspr(MMUCSR0, 0x2); +} + +void init_tlbs(void) +{ + int i; + + for (i = 0; i < num_tlb_entries; i++) { + write_tlb(tlb_table[i].mas0, + tlb_table[i].mas1, + tlb_table[i].mas2, + tlb_table[i].mas3, + tlb_table[i].mas7); + } + + return ; +} + +#ifndef CONFIG_NAND_SPL +static inline void use_tlb_cam(u8 idx) +{ + int i = idx / 32; + int bit = idx % 32; + + gd->used_tlb_cams[i] |= (1 << bit); +} + +static inline void free_tlb_cam(u8 idx) +{ + int i = idx / 32; + int bit = idx % 32; + + gd->used_tlb_cams[i] &= ~(1 << bit); +} + +void init_used_tlb_cams(void) +{ + int i; + unsigned int num_cam = mfspr(SPRN_TLB1CFG) & 0xfff; + + for (i = 0; i < ((CONFIG_SYS_NUM_TLBCAMS+31)/32); i++) + gd->used_tlb_cams[i] = 0; + + /* walk all the entries */ + for (i = 0; i < num_cam; i++) { + u32 _mas1; + + mtspr(MAS0, FSL_BOOKE_MAS0(1, i, 0)); + + asm volatile("tlbre;isync"); + _mas1 = mfspr(MAS1); + + /* if the entry isn't valid skip it */ + if ((_mas1 & MAS1_VALID)) + use_tlb_cam(i); + } +} + +int find_free_tlbcam(void) +{ + int i; + u32 idx; + + for (i = 0; i < ((CONFIG_SYS_NUM_TLBCAMS+31)/32); i++) { + idx = ffz(gd->used_tlb_cams[i]); + + if (idx != 32) + break; + } + + idx += i * 32; + + if (idx >= CONFIG_SYS_NUM_TLBCAMS) + return -1; + + return idx; +} + +void set_tlb(u8 tlb, u32 epn, u64 rpn, + u8 perms, u8 wimge, + u8 ts, u8 esel, u8 tsize, u8 iprot) +{ + u32 _mas0, _mas1, _mas2, _mas3, _mas7; + + if (tlb == 1) + use_tlb_cam(esel); + + _mas0 = FSL_BOOKE_MAS0(tlb, esel, 0); + _mas1 = FSL_BOOKE_MAS1(1, iprot, 0, ts, tsize); + _mas2 = FSL_BOOKE_MAS2(epn, wimge); + _mas3 = FSL_BOOKE_MAS3(rpn, 0, perms); + _mas7 = FSL_BOOKE_MAS7(rpn); + + write_tlb(_mas0, _mas1, _mas2, _mas3, _mas7); + +#ifdef CONFIG_ADDR_MAP + if ((tlb == 1) && (gd->flags & GD_FLG_RELOC)) + addrmap_set_entry(epn, rpn, (1UL << ((tsize * 2) + 10)), esel); +#endif +} + +void disable_tlb(u8 esel) +{ + u32 _mas0, _mas1, _mas2, _mas3, _mas7; + + free_tlb_cam(esel); + + _mas0 = FSL_BOOKE_MAS0(1, esel, 0); + _mas1 = 0; + _mas2 = 0; + _mas3 = 0; + _mas7 = 0; + + mtspr(MAS0, _mas0); + mtspr(MAS1, _mas1); + mtspr(MAS2, _mas2); + mtspr(MAS3, _mas3); +#ifdef CONFIG_ENABLE_36BIT_PHYS + mtspr(MAS7, _mas7); +#endif + asm volatile("isync;msync;tlbwe;isync"); + +#ifdef CONFIG_ADDR_MAP + if (gd->flags & GD_FLG_RELOC) + addrmap_set_entry(0, 0, 0, esel); +#endif +} + +static void tlbsx (const volatile unsigned *addr) +{ + __asm__ __volatile__ ("tlbsx 0,%0" : : "r" (addr), "m" (*addr)); +} + +/* return -1 if we didn't find anything */ +int find_tlb_idx(void *addr, u8 tlbsel) +{ + u32 _mas0, _mas1; + + /* zero out Search PID, AS */ + mtspr(MAS6, 0); + + tlbsx(addr); + + _mas0 = mfspr(MAS0); + _mas1 = mfspr(MAS1); + + /* we found something, and its in the TLB we expect */ + if ((MAS1_VALID & _mas1) && + (MAS0_TLBSEL(tlbsel) == (_mas0 & MAS0_TLBSEL_MSK))) { + return ((_mas0 & MAS0_ESEL_MSK) >> 16); + } + + return -1; +} + +#ifdef CONFIG_ADDR_MAP +void init_addr_map(void) +{ + int i; + unsigned int num_cam = mfspr(SPRN_TLB1CFG) & 0xfff; + + /* walk all the entries */ + for (i = 0; i < num_cam; i++) { + unsigned long epn; + u32 tsize, _mas1; + phys_addr_t rpn; + + mtspr(MAS0, FSL_BOOKE_MAS0(1, i, 0)); + + asm volatile("tlbre;isync"); + _mas1 = mfspr(MAS1); + + /* if the entry isn't valid skip it */ + if (!(_mas1 & MAS1_VALID)) + continue; + + tsize = (_mas1 >> 8) & 0xf; + epn = mfspr(MAS2) & MAS2_EPN; + rpn = mfspr(MAS3) & MAS3_RPN; +#ifdef CONFIG_ENABLE_36BIT_PHYS + rpn |= ((phys_addr_t)mfspr(MAS7)) << 32; +#endif + + addrmap_set_entry(epn, rpn, (1UL << ((tsize * 2) + 10)), i); + } + + return ; +} +#endif + +unsigned int setup_ddr_tlbs(unsigned int memsize_in_meg) +{ + int i; + unsigned int tlb_size; + unsigned int ram_tlb_address = (unsigned int)CONFIG_SYS_DDR_SDRAM_BASE; + unsigned int max_cam = (mfspr(SPRN_TLB1CFG) >> 16) & 0xf; + u64 size, memsize = (u64)memsize_in_meg << 20; + + size = min(memsize, CONFIG_MAX_MEM_MAPPED); + + /* Convert (4^max) kB to (2^max) bytes */ + max_cam = max_cam * 2 + 10; + + for (i = 0; size && i < 8; i++) { + int ram_tlb_index = find_free_tlbcam(); + u32 camsize = __ilog2_u64(size) & ~1U; + u32 align = __ilog2(ram_tlb_address) & ~1U; + + if (ram_tlb_index == -1) + break; + + if (align == -2) align = max_cam; + if (camsize > align) + camsize = align; + + if (camsize > max_cam) + camsize = max_cam; + + tlb_size = (camsize - 10) / 2; + + set_tlb(1, ram_tlb_address, ram_tlb_address, + MAS3_SX|MAS3_SW|MAS3_SR, 0, + 0, ram_tlb_index, tlb_size, 1); + + size -= 1ULL << camsize; + memsize -= 1ULL << camsize; + ram_tlb_address += 1UL << camsize; + } + + if (memsize) + print_size(memsize, " left unmapped\n"); + + /* + * Confirm that the requested amount of memory was mapped. + */ + return memsize_in_meg; +} +#endif /* !CONFIG_NAND_SPL */ diff --git a/arch/powerpc/cpu/mpc85xx/traps.c b/arch/powerpc/cpu/mpc85xx/traps.c new file mode 100644 index 0000000..7e96664 --- /dev/null +++ b/arch/powerpc/cpu/mpc85xx/traps.c @@ -0,0 +1,325 @@ +/* + * linux/arch/powerpc/kernel/traps.c + * + * Copyright 2007 Freescale Semiconductor. + * Copyright (C) 2003 Motorola + * Modified by Xianghua Xiao(x.xiao@motorola.com) + * + * Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org) + * + * Modified by Cort Dougan (cort@cs.nmt.edu) + * and Paul Mackerras (paulus@cs.anu.edu.au) + * + * (C) Copyright 2000 + * Wolfgang Denk, DENX Software Engineering, wd@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 + */ + +/* + * This file handles the architecture-dependent parts of hardware exceptions + */ + +#include <common.h> +#include <command.h> +#include <kgdb.h> +#include <asm/processor.h> + +DECLARE_GLOBAL_DATA_PTR; + +/* Returns 0 if exception not found and fixup otherwise. */ +extern unsigned long search_exception_table(unsigned long); + +/* + * End of addressable memory. This may be less than the actual + * amount of memory on the system if we're unable to keep all + * the memory mapped in. + */ +extern ulong get_effective_memsize(void); +#define END_OF_MEM (gd->bd->bi_memstart + get_effective_memsize()) + +static __inline__ void set_tsr(unsigned long val) +{ + asm volatile("mtspr 0x150, %0" : : "r" (val)); +} + +static __inline__ unsigned long get_esr(void) +{ + unsigned long val; + asm volatile("mfspr %0, 0x03e" : "=r" (val) :); + return val; +} + +#define ESR_MCI 0x80000000 +#define ESR_PIL 0x08000000 +#define ESR_PPR 0x04000000 +#define ESR_PTR 0x02000000 +#define ESR_DST 0x00800000 +#define ESR_DIZ 0x00400000 +#define ESR_U0F 0x00008000 + +#if defined(CONFIG_CMD_BEDBUG) +extern void do_bedbug_breakpoint(struct pt_regs *); +#endif + +/* + * Trap & Exception support + */ + +void +print_backtrace(unsigned long *sp) +{ + int cnt = 0; + unsigned long i; + + printf("Call backtrace: "); + while (sp) { + if ((uint)sp > END_OF_MEM) + break; + + i = sp[1]; + if (cnt++ % 7 == 0) + printf("\n"); + printf("%08lX ", i); + if (cnt > 32) break; + sp = (unsigned long *)*sp; + } + printf("\n"); +} + +void show_regs(struct pt_regs * regs) +{ + int i; + + printf("NIP: %08lX XER: %08lX LR: %08lX REGS: %p TRAP: %04lx DAR: %08lX\n", + regs->nip, regs->xer, regs->link, regs, regs->trap, regs->dar); + printf("MSR: %08lx EE: %01x PR: %01x FP: %01x ME: %01x IR/DR: %01x%01x\n", + regs->msr, regs->msr&MSR_EE ? 1 : 0, regs->msr&MSR_PR ? 1 : 0, + regs->msr & MSR_FP ? 1 : 0,regs->msr&MSR_ME ? 1 : 0, + regs->msr&MSR_IR ? 1 : 0, + regs->msr&MSR_DR ? 1 : 0); + + printf("\n"); + for (i = 0; i < 32; i++) { + if ((i % 8) == 0) + { + printf("GPR%02d: ", i); + } + + printf("%08lX ", regs->gpr[i]); + if ((i % 8) == 7) + { + printf("\n"); + } + } +} + + +void +_exception(int signr, struct pt_regs *regs) +{ + show_regs(regs); + print_backtrace((unsigned long *)regs->gpr[1]); + panic("Exception in kernel pc %lx signal %d",regs->nip,signr); +} + +void +CritcalInputException(struct pt_regs *regs) +{ + panic("Critical Input Exception"); +} + +int machinecheck_count = 0; +int machinecheck_error = 0; +void +MachineCheckException(struct pt_regs *regs) +{ + unsigned long fixup; + unsigned int mcsr, mcsrr0, mcsrr1, mcar; + + /* Probing PCI using config cycles cause this exception + * when a device is not present. Catch it and return to + * the PCI exception handler. + */ + if ((fixup = search_exception_table(regs->nip)) != 0) { + regs->nip = fixup; + return; + } + + mcsrr0 = mfspr(SPRN_MCSRR0); + mcsrr1 = mfspr(SPRN_MCSRR1); + mcsr = mfspr(SPRN_MCSR); + mcar = mfspr(SPRN_MCAR); + + machinecheck_count++; + machinecheck_error=1; + +#if defined(CONFIG_CMD_KGDB) + if (debugger_exception_handler && (*debugger_exception_handler)(regs)) + return; +#endif + + printf("Machine check in kernel mode.\n"); + printf("Caused by (from mcsr): "); + printf("mcsr = 0x%08x\n", mcsr); + if (mcsr & 0x80000000) + printf("Machine check input pin\n"); + if (mcsr & 0x40000000) + printf("Instruction cache parity error\n"); + if (mcsr & 0x20000000) + printf("Data cache push parity error\n"); + if (mcsr & 0x10000000) + printf("Data cache parity error\n"); + if (mcsr & 0x00000080) + printf("Bus instruction address error\n"); + if (mcsr & 0x00000040) + printf("Bus Read address error\n"); + if (mcsr & 0x00000020) + printf("Bus Write address error\n"); + if (mcsr & 0x00000010) + printf("Bus Instruction data bus error\n"); + if (mcsr & 0x00000008) + printf("Bus Read data bus error\n"); + if (mcsr & 0x00000004) + printf("Bus Write bus error\n"); + if (mcsr & 0x00000002) + printf("Bus Instruction parity error\n"); + if (mcsr & 0x00000001) + printf("Bus Read parity error\n"); + + show_regs(regs); + printf("MCSR=0x%08x \tMCSRR0=0x%08x \nMCSRR1=0x%08x \tMCAR=0x%08x\n", + mcsr, mcsrr0, mcsrr1, mcar); + print_backtrace((unsigned long *)regs->gpr[1]); + if (machinecheck_count > 10) { + panic("machine check count too high\n"); + } + + if (machinecheck_count > 1) { + regs->nip += 4; /* skip offending instruction */ + printf("Skipping current instr, Returning to 0x%08lx\n", + regs->nip); + } else { + printf("Returning back to 0x%08lx\n",regs->nip); + } +} + +void +AlignmentException(struct pt_regs *regs) +{ +#if defined(CONFIG_CMD_KGDB) + if (debugger_exception_handler && (*debugger_exception_handler)(regs)) + return; +#endif + + show_regs(regs); + print_backtrace((unsigned long *)regs->gpr[1]); + panic("Alignment Exception"); +} + +void +ProgramCheckException(struct pt_regs *regs) +{ + long esr_val; + +#if defined(CONFIG_CMD_KGDB) + if (debugger_exception_handler && (*debugger_exception_handler)(regs)) + return; +#endif + + show_regs(regs); + + esr_val = get_esr(); + if( esr_val & ESR_PIL ) + printf( "** Illegal Instruction **\n" ); + else if( esr_val & ESR_PPR ) + printf( "** Privileged Instruction **\n" ); + else if( esr_val & ESR_PTR ) + printf( "** Trap Instruction **\n" ); + + print_backtrace((unsigned long *)regs->gpr[1]); + panic("Program Check Exception"); +} + +void +PITException(struct pt_regs *regs) +{ + /* + * Reset PIT interrupt + */ + set_tsr(0x0c000000); + + /* + * Call timer_interrupt routine in interrupts.c + */ + timer_interrupt(NULL); +} + + +void +UnknownException(struct pt_regs *regs) +{ +#if defined(CONFIG_CMD_KGDB) + if (debugger_exception_handler && (*debugger_exception_handler)(regs)) + return; +#endif + + printf("Bad trap at PC: %lx, SR: %lx, vector=%lx\n", + regs->nip, regs->msr, regs->trap); + _exception(0, regs); +} + +void +ExtIntException(struct pt_regs *regs) +{ + volatile ccsr_pic_t *pic = (void *)(CONFIG_SYS_MPC85xx_PIC_ADDR); + + uint vect; + +#if defined(CONFIG_CMD_KGDB) + if (debugger_exception_handler && (*debugger_exception_handler)(regs)) + return; +#endif + + printf("External Interrupt Exception at PC: %lx, SR: %lx, vector=%lx", + regs->nip, regs->msr, regs->trap); + vect = pic->iack0; + printf(" irq IACK0@%05x=%d\n",(int)&pic->iack0,vect); + show_regs(regs); + print_backtrace((unsigned long *)regs->gpr[1]); +} + +void +DebugException(struct pt_regs *regs) +{ + printf("Debugger trap at @ %lx\n", regs->nip ); + show_regs(regs); +#if defined(CONFIG_CMD_BEDBUG) + do_bedbug_breakpoint( regs ); +#endif +} + +/* Probe an address by reading. If not present, return -1, otherwise + * return 0. + */ +int +addr_probe(uint *addr) +{ + return 0; +} diff --git a/arch/powerpc/cpu/mpc85xx/u-boot-nand.lds b/arch/powerpc/cpu/mpc85xx/u-boot-nand.lds new file mode 100644 index 0000000..5fd3e6c --- /dev/null +++ b/arch/powerpc/cpu/mpc85xx/u-boot-nand.lds @@ -0,0 +1,137 @@ +/* + * Copyright 2009 Freescale Semiconductor, Inc. + * + * 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 + */ + +OUTPUT_ARCH(powerpc) +/* Do we need any of these for elf? + __DYNAMIC = 0; */ +PHDRS +{ + text PT_LOAD; + bss PT_LOAD; +} + +SECTIONS +{ + /* Read-only sections, merged into text segment: */ + . = + SIZEOF_HEADERS; + .interp : { *(.interp) } + .hash : { *(.hash) } + .dynsym : { *(.dynsym) } + .dynstr : { *(.dynstr) } + .rel.text : { *(.rel.text) } + .rela.text : { *(.rela.text) } + .rel.data : { *(.rel.data) } + .rela.data : { *(.rela.data) } + .rel.rodata : { *(.rel.rodata) } + .rela.rodata : { *(.rela.rodata) } + .rel.got : { *(.rel.got) } + .rela.got : { *(.rela.got) } + .rel.ctors : { *(.rel.ctors) } + .rela.ctors : { *(.rela.ctors) } + .rel.dtors : { *(.rel.dtors) } + .rela.dtors : { *(.rela.dtors) } + .rel.bss : { *(.rel.bss) } + .rela.bss : { *(.rela.bss) } + .rel.plt : { *(.rel.plt) } + .rela.plt : { *(.rela.plt) } + .init : { *(.init) } + .plt : { *(.plt) } + .text : + { + *(.text) + *(.got1) + } :text + _etext = .; + PROVIDE (etext = .); + .rodata : + { + *(.eh_frame) + *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.rodata*))) + } :text + .fini : { *(.fini) } =0 + .ctors : { *(.ctors) } + .dtors : { *(.dtors) } + + /* Read-write section, merged into data segment: */ + . = (. + 0x00FF) & 0xFFFFFF00; + _erotext = .; + PROVIDE (erotext = .); + .reloc : + { + *(.got) + _GOT2_TABLE_ = .; + *(.got2) + _FIXUP_TABLE_ = .; + *(.fixup) + } + __got2_entries = (_FIXUP_TABLE_ - _GOT2_TABLE_) >> 2; + __fixup_entries = (. - _FIXUP_TABLE_) >> 2; + + .data : + { + *(.data) + *(.data1) + *(.sdata) + *(.sdata2) + *(.dynamic) + CONSTRUCTORS + } + _edata = .; + PROVIDE (edata = .); + + . = .; + __u_boot_cmd_start = .; + .u_boot_cmd : { *(.u_boot_cmd) } + __u_boot_cmd_end = .; + + . = .; + __start___ex_table = .; + __ex_table : { *(__ex_table) } + __stop___ex_table = .; + + . = ALIGN(256); + __init_begin = .; + .text.init : { *(.text.init) } + .data.init : { *(.data.init) } + . = ALIGN(256); + __init_end = .; + + .bootpg ADDR(.text) - 0x1000 : + { + arch/powerpc/cpu/mpc85xx/start.o (.bootpg) + } :text = 0xffff + + . = ADDR(.text) + 0x80000; + + __bss_start = .; + .bss (NOLOAD) : + { + *(.sbss) *(.scommon) + *(.dynbss) + *(.bss) + *(COMMON) + } :bss + + . = ALIGN(4); + _end = . ; + PROVIDE (end = .); +} diff --git a/arch/powerpc/cpu/mpc85xx/u-boot-nand_spl.lds b/arch/powerpc/cpu/mpc85xx/u-boot-nand_spl.lds new file mode 100644 index 0000000..7d9cee9 --- /dev/null +++ b/arch/powerpc/cpu/mpc85xx/u-boot-nand_spl.lds @@ -0,0 +1,67 @@ +/* + * (C) Copyright 2006 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de + * + * Copyright 2009 Freescale Semiconductor, Inc. + * + * 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 + */ + +OUTPUT_ARCH(powerpc) +SECTIONS +{ + . = 0xfff00000; + .text : { + *(.text) + } + _etext = .; + + .reloc : { + _GOT2_TABLE_ = .; + *(.got2) + _FIXUP_TABLE_ = .; + *(.fixup) + } + __got2_entries = (_FIXUP_TABLE_ - _GOT2_TABLE_) >> 2; + __fixup_entries = (. - _FIXUP_TABLE_) >> 2; + + . = ALIGN(8); + .data : { + *(.rodata*) + *(.data*) + *(.sdata*) + } + _edata = .; + + . = ALIGN(8); + __init_begin = .; + __init_end = .; + + .resetvec ADDR(.text) + 0xffc : { + *(.resetvec) + } = 0xffff + + __bss_start = .; + .bss : { + *(.sbss) + *(.bss) + } + _end = .; +} +ASSERT(__init_end <= 0xfff00ffc, "NAND bootstrap too big"); diff --git a/arch/powerpc/cpu/mpc85xx/u-boot.lds b/arch/powerpc/cpu/mpc85xx/u-boot.lds new file mode 100644 index 0000000..c88b1f3 --- /dev/null +++ b/arch/powerpc/cpu/mpc85xx/u-boot.lds @@ -0,0 +1,157 @@ +/* + * Copyright 2007-2009 Freescale Semiconductor, Inc. + * + * 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 + */ + +#ifndef RESET_VECTOR_ADDRESS +#define RESET_VECTOR_ADDRESS 0xfffffffc +#endif + +OUTPUT_ARCH(powerpc) +/* Do we need any of these for elf? + __DYNAMIC = 0; */ +PHDRS +{ + text PT_LOAD; + bss PT_LOAD; +} + +SECTIONS +{ + /* Read-only sections, merged into text segment: */ + . = + SIZEOF_HEADERS; + .interp : { *(.interp) } + .hash : { *(.hash) } + .dynsym : { *(.dynsym) } + .dynstr : { *(.dynstr) } + .rel.text : { *(.rel.text) } + .rela.text : { *(.rela.text) } + .rel.data : { *(.rel.data) } + .rela.data : { *(.rela.data) } + .rel.rodata : { *(.rel.rodata) } + .rela.rodata : { *(.rela.rodata) } + .rel.got : { *(.rel.got) } + .rela.got : { *(.rela.got) } + .rel.ctors : { *(.rel.ctors) } + .rela.ctors : { *(.rela.ctors) } + .rel.dtors : { *(.rel.dtors) } + .rela.dtors : { *(.rela.dtors) } + .rel.bss : { *(.rel.bss) } + .rela.bss : { *(.rela.bss) } + .rel.plt : { *(.rel.plt) } + .rela.plt : { *(.rela.plt) } + .init : { *(.init) } + .plt : { *(.plt) } + .text : + { + *(.text) + *(.got1) + } :text + _etext = .; + PROVIDE (etext = .); + .rodata : + { + *(.eh_frame) + *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.rodata*))) + } :text + .fini : { *(.fini) } =0 + .ctors : { *(.ctors) } + .dtors : { *(.dtors) } + + /* Read-write section, merged into data segment: */ + . = (. + 0x00FF) & 0xFFFFFF00; + _erotext = .; + PROVIDE (erotext = .); + .reloc : + { + *(.got) + _GOT2_TABLE_ = .; + *(.got2) + _FIXUP_TABLE_ = .; + *(.fixup) + } + __got2_entries = (_FIXUP_TABLE_ - _GOT2_TABLE_) >> 2; + __fixup_entries = (. - _FIXUP_TABLE_) >> 2; + + .data : + { + *(.data) + *(.data1) + *(.sdata) + *(.sdata2) + *(.dynamic) + CONSTRUCTORS + } + _edata = .; + PROVIDE (edata = .); + + . = .; + __u_boot_cmd_start = .; + .u_boot_cmd : { *(.u_boot_cmd) } + __u_boot_cmd_end = .; + + . = .; + __start___ex_table = .; + __ex_table : { *(__ex_table) } + __stop___ex_table = .; + + . = ALIGN(256); + __init_begin = .; + .text.init : { *(.text.init) } + .data.init : { *(.data.init) } + . = ALIGN(256); + __init_end = .; + + .bootpg RESET_VECTOR_ADDRESS - 0xffc : + { + arch/powerpc/cpu/mpc85xx/start.o (.bootpg) + } :text = 0xffff + + .resetvec RESET_VECTOR_ADDRESS : + { + *(.resetvec) + } :text = 0xffff + + . = RESET_VECTOR_ADDRESS + 0x4; + + /* + * Make sure that the bss segment isn't linked at 0x0, otherwise its + * address won't be updated during relocation fixups. Note that + * this is a temporary fix. Code to dynamically the fixup the bss + * location will be added in the future. When the bss relocation + * fixup code is present this workaround should be removed. + */ +#if (RESET_VECTOR_ADDRESS == 0xfffffffc) + . |= 0x10; +#endif + + __bss_start = .; + .bss (NOLOAD) : + { + *(.sbss) *(.scommon) + *(.dynbss) + *(.bss) + *(COMMON) + } :bss + + . = ALIGN(4); + _end = . ; + PROVIDE (end = .); +} diff --git a/arch/powerpc/cpu/mpc86xx/Makefile b/arch/powerpc/cpu/mpc86xx/Makefile new file mode 100644 index 0000000..daca79a --- /dev/null +++ b/arch/powerpc/cpu/mpc86xx/Makefile @@ -0,0 +1,63 @@ +# +# Copyright 2007 Freescale Semiconductor, Inc. +# (C) Copyright 2002,2003 Motorola Inc. +# Xianghua Xiao,X.Xiao@motorola.com +# +# (C) Copyright 2004 Freescale Semiconductor. (MC86xx Port) +# Jeff Brown +# 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 $(TOPDIR)/config.mk + +LIB = $(obj)lib$(CPU).a + +START = start.o + +SOBJS-y += cache.o +SOBJS-$(CONFIG_MP) += release.o + +COBJS-y += cpu.o +COBJS-y += cpu_init.o +# 8610 & 8641 are identical w/regards to DDR +COBJS-$(CONFIG_MPC8610) += ddr-8641.o +COBJS-$(CONFIG_MPC8641) += ddr-8641.o +COBJS-$(CONFIG_OF_LIBFDT) += fdt.o +COBJS-y += interrupts.o +COBJS-$(CONFIG_MP) += mp.o +COBJS-y += speed.o +COBJS-y += traps.o + +SRCS := $(START:.o=.S) $(SOBJS-y:.o=.S) $(COBJS-y:.o=.c) +OBJS := $(addprefix $(obj),$(SOBJS-y) $(COBJS-y)) +START := $(addprefix $(obj),$(START)) + +all: $(obj).depend $(START) $(LIB) + +$(LIB): $(OBJS) + $(AR) $(ARFLAGS) $@ $(ASOBJS) $(OBJS) + +######################################################################### + +# defines $(obj).depend target +include $(SRCTREE)/rules.mk + +sinclude $(obj).depend + +######################################################################### diff --git a/arch/powerpc/cpu/mpc86xx/cache.S b/arch/powerpc/cpu/mpc86xx/cache.S new file mode 100644 index 0000000..0bb058b --- /dev/null +++ b/arch/powerpc/cpu/mpc86xx/cache.S @@ -0,0 +1,378 @@ +#include <config.h> +#include <mpc86xx.h> +#include <version.h> + +#include <ppc_asm.tmpl> +#include <ppc_defs.h> + +#include <asm/cache.h> +#include <asm/mmu.h> + +#ifndef CACHE_LINE_SIZE +# define CACHE_LINE_SIZE L1_CACHE_BYTES +#endif + +#if CACHE_LINE_SIZE == 128 +#define LG_CACHE_LINE_SIZE 7 +#elif CACHE_LINE_SIZE == 32 +#define LG_CACHE_LINE_SIZE 5 +#elif CACHE_LINE_SIZE == 16 +#define LG_CACHE_LINE_SIZE 4 +#elif CACHE_LINE_SIZE == 8 +#define LG_CACHE_LINE_SIZE 3 +#else +# error "Invalid cache line size!" +#endif + +/* + * Most of this code is taken from 74xx_7xx/cache.S + * and then cleaned up a bit + */ + +/* + * Invalidate L1 instruction cache. + */ +_GLOBAL(invalidate_l1_instruction_cache) + /* use invalidate-all bit in HID0 */ + mfspr r3,HID0 + ori r3,r3,HID0_ICFI + mtspr HID0,r3 + isync + blr + +/* + * Invalidate L1 data cache. + */ +_GLOBAL(invalidate_l1_data_cache) + mfspr r3,HID0 + ori r3,r3,HID0_DCFI + mtspr HID0,r3 + isync + blr + +/* + * Flush data cache. + */ +_GLOBAL(flush_dcache) + lis r3,0 + lis r5,CACHE_LINE_SIZE +flush: + cmp 0,1,r3,r5 + bge done + lwz r5,0(r3) + lis r5,CACHE_LINE_SIZE + addi r3,r3,0x4 + b flush +done: + blr +/* + * Write any modified data cache blocks out to memory + * and invalidate the corresponding instruction cache blocks. + * This is a no-op on the 601. + * + * flush_icache_range(unsigned long start, unsigned long stop) + */ +_GLOBAL(flush_icache_range) + li r5,CACHE_LINE_SIZE-1 + andc r3,r3,r5 + subf r4,r3,r4 + add r4,r4,r5 + srwi. r4,r4,LG_CACHE_LINE_SIZE + beqlr + mtctr r4 + mr r6,r3 +1: dcbst 0,r3 + addi r3,r3,CACHE_LINE_SIZE + bdnz 1b + sync /* wait for dcbst's to get to ram */ + mtctr r4 +2: icbi 0,r6 + addi r6,r6,CACHE_LINE_SIZE + bdnz 2b + sync /* additional sync needed on g4 */ + isync + blr +/* + * Write any modified data cache blocks out to memory. + * Does not invalidate the corresponding cache lines (especially for + * any corresponding instruction cache). + * + * clean_dcache_range(unsigned long start, unsigned long stop) + */ +_GLOBAL(clean_dcache_range) + li r5,CACHE_LINE_SIZE-1 + andc r3,r3,r5 /* align r3 down to cache line */ + subf r4,r3,r4 /* r4 = offset of stop from start of cache line */ + add r4,r4,r5 /* r4 += cache_line_size-1 */ + srwi. r4,r4,LG_CACHE_LINE_SIZE /* r4 = number of cache lines to flush */ + beqlr /* if r4 == 0 return */ + mtctr r4 /* ctr = r4 */ + + sync +1: dcbst 0,r3 + addi r3,r3,CACHE_LINE_SIZE + bdnz 1b + sync /* wait for dcbst's to get to ram */ + blr + +/* + * Write any modified data cache blocks out to memory + * and invalidate the corresponding instruction cache blocks. + * + * flush_dcache_range(unsigned long start, unsigned long stop) + */ +_GLOBAL(flush_dcache_range) + li r5,CACHE_LINE_SIZE-1 + andc r3,r3,r5 + subf r4,r3,r4 + add r4,r4,r5 + srwi. r4,r4,LG_CACHE_LINE_SIZE + beqlr + mtctr r4 + + sync +1: dcbf 0,r3 + addi r3,r3,CACHE_LINE_SIZE + bdnz 1b + sync /* wait for dcbf's to get to ram */ + blr + +/* + * Like above, but invalidate the D-cache. This is used by the 8xx + * to invalidate the cache so the PPC core doesn't get stale data + * from the CPM (no cache snooping here :-). + * + * invalidate_dcache_range(unsigned long start, unsigned long stop) + */ +_GLOBAL(invalidate_dcache_range) + li r5,CACHE_LINE_SIZE-1 + andc r3,r3,r5 + subf r4,r3,r4 + add r4,r4,r5 + srwi. r4,r4,LG_CACHE_LINE_SIZE + beqlr + mtctr r4 + + sync +1: dcbi 0,r3 + addi r3,r3,CACHE_LINE_SIZE + bdnz 1b + sync /* wait for dcbi's to get to ram */ + blr + +/* + * Flush a particular page from the data cache to RAM. + * Note: this is necessary because the instruction cache does *not* + * snoop from the data cache. + * + * void __flush_page_to_ram(void *page) + */ +_GLOBAL(__flush_page_to_ram) + rlwinm r3,r3,0,0,19 /* Get page base address */ + li r4,4096/CACHE_LINE_SIZE /* Number of lines in a page */ + mtctr r4 + mr r6,r3 +0: dcbst 0,r3 /* Write line to ram */ + addi r3,r3,CACHE_LINE_SIZE + bdnz 0b + sync + mtctr r4 +1: icbi 0,r6 + addi r6,r6,CACHE_LINE_SIZE + bdnz 1b + sync + isync + blr + +/* + * Flush a particular page from the instruction cache. + * Note: this is necessary because the instruction cache does *not* + * snoop from the data cache. + * + * void __flush_icache_page(void *page) + */ +_GLOBAL(__flush_icache_page) + li r4,4096/CACHE_LINE_SIZE /* Number of lines in a page */ + mtctr r4 +1: icbi 0,r3 + addi r3,r3,CACHE_LINE_SIZE + bdnz 1b + sync + isync + blr + +/* + * Clear a page using the dcbz instruction, which doesn't cause any + * memory traffic (except to write out any cache lines which get + * displaced). This only works on cacheable memory. + */ +_GLOBAL(clear_page) + li r0,4096/CACHE_LINE_SIZE + mtctr r0 +1: dcbz 0,r3 + addi r3,r3,CACHE_LINE_SIZE + bdnz 1b + blr + +/* + * Enable L1 Instruction cache + */ +_GLOBAL(icache_enable) + mfspr r3, HID0 + li r5, HID0_ICFI|HID0_ILOCK + andc r3, r3, r5 + ori r3, r3, HID0_ICE + ori r5, r3, HID0_ICFI + mtspr HID0, r5 + mtspr HID0, r3 + isync + blr + +/* + * Disable L1 Instruction cache + */ +_GLOBAL(icache_disable) + mflr r4 + bl invalidate_l1_instruction_cache /* uses r3 */ + sync + mtlr r4 + mfspr r3, HID0 + li r5, 0 + ori r5, r5, HID0_ICE + andc r3, r3, r5 + mtspr HID0, r3 + isync + blr + +/* + * Is instruction cache enabled? + */ +_GLOBAL(icache_status) + mfspr r3, HID0 + andi. r3, r3, HID0_ICE + blr + + +_GLOBAL(l1dcache_enable) + mfspr r3, HID0 + li r5, HID0_DCFI|HID0_DLOCK + andc r3, r3, r5 + mtspr HID0, r3 /* no invalidate, unlock */ + ori r3, r3, HID0_DCE + ori r5, r3, HID0_DCFI + mtspr HID0, r5 /* enable + invalidate */ + mtspr HID0, r3 /* enable */ + sync + blr + +/* + * Enable data cache(s) - L1 and optionally L2 + * Calls l2cache_enable. LR saved in r5 + */ +_GLOBAL(dcache_enable) + mfspr r3, HID0 + li r5, HID0_DCFI|HID0_DLOCK + andc r3, r3, r5 + mtspr HID0, r3 /* no invalidate, unlock */ + ori r3, r3, HID0_DCE + ori r5, r3, HID0_DCFI + mtspr HID0, r5 /* enable + invalidate */ + mtspr HID0, r3 /* enable */ + sync +#ifdef CONFIG_SYS_L2 + mflr r5 + bl l2cache_enable /* uses r3 and r4 */ + sync + mtlr r5 +#endif + blr + + +/* + * Disable data cache(s) - L1 and optionally L2 + * Calls flush_dcache and l2cache_disable_no_flush. + * LR saved in r4 + */ +_GLOBAL(dcache_disable) + mflr r4 /* save link register */ + bl flush_dcache /* uses r3 and r5 */ + sync + mfspr r3, HID0 + li r5, HID0_DCFI|HID0_DLOCK + andc r3, r3, r5 + mtspr HID0, r3 /* no invalidate, unlock */ + li r5, HID0_DCE|HID0_DCFI + andc r3, r3, r5 /* no enable, no invalidate */ + mtspr HID0, r3 + sync +#ifdef CONFIG_SYS_L2 + bl l2cache_disable_no_flush /* uses r3 */ +#endif + mtlr r4 /* restore link register */ + blr + +/* + * Is data cache enabled? + */ +_GLOBAL(dcache_status) + mfspr r3, HID0 + andi. r3, r3, HID0_DCE + blr + +/* + * Invalidate L2 cache using L2I, assume L2 is enabled + */ +_GLOBAL(l2cache_invalidate) + mfspr r3, l2cr + rlwinm. r3, r3, 0, 0, 0 + beq 1f + + mfspr r3, l2cr + rlwinm r3, r3, 0, 1, 31 + +#ifdef CONFIG_ALTIVEC + dssall +#endif + sync + mtspr l2cr, r3 + sync +1: mfspr r3, l2cr + oris r3, r3, L2CR_L2I@h + mtspr l2cr, r3 + +invl2: + mfspr r3, l2cr + andis. r3, r3, L2CR_L2I@h + bne invl2 + blr + +/* + * Enable L2 cache + * Calls l2cache_invalidate. LR is saved in r4 + */ +_GLOBAL(l2cache_enable) + mflr r4 /* save link register */ + bl l2cache_invalidate /* uses r3 */ + sync + lis r3, L2_ENABLE@h + ori r3, r3, L2_ENABLE@l + mtspr l2cr, r3 + isync + mtlr r4 /* restore link register */ + blr + +/* + * Disable L2 cache + * Calls flush_dcache. LR is saved in r4 + */ +_GLOBAL(l2cache_disable) + mflr r4 /* save link register */ + bl flush_dcache /* uses r3 and r5 */ + sync + mtlr r4 /* restore link register */ +l2cache_disable_no_flush: /* provide way to disable L2 w/o flushing */ + lis r3, L2_INIT@h + ori r3, r3, L2_INIT@l + mtspr l2cr, r3 + isync + blr diff --git a/arch/powerpc/cpu/mpc86xx/config.mk b/arch/powerpc/cpu/mpc86xx/config.mk new file mode 100644 index 0000000..ca2f837 --- /dev/null +++ b/arch/powerpc/cpu/mpc86xx/config.mk @@ -0,0 +1,27 @@ +# +# (C) Copyright 2004 Freescale Semiconductor. +# Jeff Brown +# +# 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 +# + +PLATFORM_RELFLAGS += -fPIC -meabi + +PLATFORM_CPPFLAGS += -ffixed-r2 -mstring +PLATFORM_CPPFLAGS += -maltivec -mabi=altivec -msoft-float diff --git a/arch/powerpc/cpu/mpc86xx/cpu.c b/arch/powerpc/cpu/mpc86xx/cpu.c new file mode 100644 index 0000000..1887575 --- /dev/null +++ b/arch/powerpc/cpu/mpc86xx/cpu.c @@ -0,0 +1,233 @@ +/* + * Copyright 2006,2009-2010 Freescale Semiconductor, Inc. + * Jeff Brown + * Srikanth Srinivasan (srikanth.srinivasan@freescale.com) + * + * 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> +#include <watchdog.h> +#include <command.h> +#include <asm/cache.h> +#include <asm/mmu.h> +#include <mpc86xx.h> +#include <asm/fsl_law.h> + +DECLARE_GLOBAL_DATA_PTR; + +/* + * Default board reset function + */ +static void +__board_reset(void) +{ + /* Do nothing */ +} +void board_reset(void) __attribute__((weak, alias("__board_reset"))); + + +int +checkcpu(void) +{ + sys_info_t sysinfo; + uint pvr, svr; + uint ver; + uint major, minor; + char buf1[32], buf2[32]; + volatile immap_t *immap = (immap_t *) CONFIG_SYS_IMMR; + volatile ccsr_gur_t *gur = &immap->im_gur; + struct cpu_type *cpu; + uint msscr0 = mfspr(MSSCR0); + + svr = get_svr(); + ver = SVR_SOC_VER(svr); + major = SVR_MAJ(svr); + minor = SVR_MIN(svr); + + if (cpu_numcores() > 1) { +#ifndef CONFIG_MP + puts("Unicore software on multiprocessor system!!\n" + "To enable mutlticore build define CONFIG_MP\n"); +#endif + } + puts("CPU: "); + + cpu = gd->cpu; + + puts(cpu->name); + + printf(", Version: %d.%d, (0x%08x)\n", major, minor, svr); + puts("Core: "); + + pvr = get_pvr(); + ver = PVR_E600_VER(pvr); + major = PVR_E600_MAJ(pvr); + minor = PVR_E600_MIN(pvr); + + printf("E600 Core %d", (msscr0 & 0x20) ? 1 : 0 ); + if (gur->pordevsr & MPC86xx_PORDEVSR_CORE1TE) + puts("\n Core1Translation Enabled"); + debug(" (MSSCR0=%x, PORDEVSR=%x)", msscr0, gur->pordevsr); + + printf(", Version: %d.%d, (0x%08x)\n", major, minor, pvr); + + get_sys_info(&sysinfo); + + puts("Clock Configuration:\n"); + printf(" CPU:%-4s MHz, ", strmhz(buf1, sysinfo.freqProcessor)); + printf("MPX:%-4s MHz\n", strmhz(buf1, sysinfo.freqSystemBus)); + printf(" DDR:%-4s MHz (%s MT/s data rate), ", + strmhz(buf1, sysinfo.freqSystemBus / 2), + strmhz(buf2, sysinfo.freqSystemBus)); + + if (sysinfo.freqLocalBus > LCRR_CLKDIV) { + printf("LBC:%-4s MHz\n", strmhz(buf1, sysinfo.freqLocalBus)); + } else { + printf("LBC: unknown (LCRR[CLKDIV] = 0x%02lx)\n", + sysinfo.freqLocalBus); + } + + puts("L1: D-cache 32 KB enabled\n"); + puts(" I-cache 32 KB enabled\n"); + + puts("L2: "); + if (get_l2cr() & 0x80000000) { +#if defined(CONFIG_MPC8610) + puts("256"); +#elif defined(CONFIG_MPC8641) + puts("512"); +#endif + puts(" KB enabled\n"); + } else { + puts("Disabled\n"); + } + + return 0; +} + + +void +do_reset(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]) +{ + volatile immap_t *immap = (immap_t *)CONFIG_SYS_IMMR; + volatile ccsr_gur_t *gur = &immap->im_gur; + + /* Attempt board-specific reset */ + board_reset(); + + /* Next try asserting HRESET_REQ */ + out_be32(&gur->rstcr, MPC86xx_RSTCR_HRST_REQ); + + while (1) + ; +} + + +/* + * Get timebase clock frequency + */ +unsigned long +get_tbclk(void) +{ + sys_info_t sys_info; + + get_sys_info(&sys_info); + return (sys_info.freqSystemBus + 3L) / 4L; +} + + +#if defined(CONFIG_WATCHDOG) +void +watchdog_reset(void) +{ +#if defined(CONFIG_MPC8610) + /* + * This actually feed the hard enabled watchdog. + */ + volatile immap_t *immap = (immap_t *)CONFIG_SYS_IMMR; + volatile ccsr_wdt_t *wdt = &immap->im_wdt; + volatile ccsr_gur_t *gur = &immap->im_gur; + u32 tmp = gur->pordevsr; + + if (tmp & 0x4000) { + wdt->swsrr = 0x556c; + wdt->swsrr = 0xaa39; + } +#endif +} +#endif /* CONFIG_WATCHDOG */ + +/* + * Print out the state of various machine registers. + * Currently prints out LAWs, BR0/OR0, and BATs + */ +void mpc86xx_reginfo(void) +{ + immap_t *immap = (immap_t *)CONFIG_SYS_IMMR; + ccsr_lbc_t *lbc = &immap->im_lbc; + + print_bats(); + print_laws(); + + printf ("Local Bus Controller Registers\n" + "\tBR0\t0x%08X\tOR0\t0x%08X \n", in_be32(&lbc->br0), in_be32(&lbc->or0)); + printf("\tBR1\t0x%08X\tOR1\t0x%08X \n", in_be32(&lbc->br1), in_be32(&lbc->or1)); + printf("\tBR2\t0x%08X\tOR2\t0x%08X \n", in_be32(&lbc->br2), in_be32(&lbc->or2)); + printf("\tBR3\t0x%08X\tOR3\t0x%08X \n", in_be32(&lbc->br3), in_be32(&lbc->or3)); + printf("\tBR4\t0x%08X\tOR4\t0x%08X \n", in_be32(&lbc->br4), in_be32(&lbc->or4)); + printf("\tBR5\t0x%08X\tOR5\t0x%08X \n", in_be32(&lbc->br5), in_be32(&lbc->or5)); + printf("\tBR6\t0x%08X\tOR6\t0x%08X \n", in_be32(&lbc->br6), in_be32(&lbc->or6)); + printf("\tBR7\t0x%08X\tOR7\t0x%08X \n", in_be32(&lbc->br7), in_be32(&lbc->or7)); + +} + +/* + * Set the DDR BATs to reflect the actual size of DDR. + * + * dram_size is the actual size of DDR, in bytes + * + * Note: we assume that CONFIG_MAX_MEM_MAPPED is 2G or smaller as we only + * are using a single BAT to cover DDR. + * + * If this is not true, (e.g. CONFIG_MAX_MEM_MAPPED is 2GB but HID0_XBSEN + * is not defined) then we might have a situation where U-Boot will attempt + * to relocated itself outside of the region mapped by DBAT0. + * This will cause a machine check. + * + * Currently we are limited to power of two sized DDR since we only use a + * single bat. If a non-power of two size is used that is less than + * CONFIG_MAX_MEM_MAPPED u-boot will crash. + * + */ +void setup_ddr_bat(phys_addr_t dram_size) +{ + unsigned long batu, bl; + + bl = TO_BATU_BL(min(dram_size, CONFIG_MAX_MEM_MAPPED)); + + if (BATU_SIZE(bl) != dram_size) { + u64 sz = (u64)dram_size - BATU_SIZE(bl); + print_size(sz, " left unmapped\n"); + } + + batu = bl | BATU_VS | BATU_VP; + write_bat(DBAT0, batu, CONFIG_SYS_DBAT0L); + write_bat(IBAT0, batu, CONFIG_SYS_IBAT0L); +} diff --git a/arch/powerpc/cpu/mpc86xx/cpu_init.c b/arch/powerpc/cpu/mpc86xx/cpu_init.c new file mode 100644 index 0000000..b4f047d --- /dev/null +++ b/arch/powerpc/cpu/mpc86xx/cpu_init.c @@ -0,0 +1,190 @@ +/* + * Copyright 2004,2009 Freescale Semiconductor, Inc. + * Jeff Brown + * Srikanth Srinivasan (srikanth.srinivasan@freescale.com) + * + * 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 + */ + +/* + * cpu_init.c - low level cpu init + */ + +#include <config.h> +#include <common.h> +#include <mpc86xx.h> +#include <asm/mmu.h> +#include <asm/fsl_law.h> +#include <asm/mp.h> + +void setup_bats(void); + +DECLARE_GLOBAL_DATA_PTR; + +/* + * Breathe some life into the CPU... + * + * Set up the memory map + * initialize a bunch of registers + */ + +void cpu_init_f(void) +{ + volatile immap_t *immap = (immap_t *)CONFIG_SYS_IMMR; + volatile ccsr_lbc_t *memctl = &immap->im_lbc; + + /* Pointer is writable since we allocated a register for it */ + gd = (gd_t *) (CONFIG_SYS_INIT_RAM_ADDR + CONFIG_SYS_GBL_DATA_OFFSET); + + /* Clear initial global data */ + memset ((void *) gd, 0, sizeof (gd_t)); + +#ifdef CONFIG_FSL_LAW + init_laws(); +#endif + + setup_bats(); + + /* Map banks 0 and 1 to the FLASH banks 0 and 1 at preliminary + * addresses - these have to be modified later when FLASH size + * has been determined + */ + +#if defined(CONFIG_SYS_OR0_REMAP) + memctl->or0 = CONFIG_SYS_OR0_REMAP; +#endif +#if defined(CONFIG_SYS_OR1_REMAP) + memctl->or1 = CONFIG_SYS_OR1_REMAP; +#endif + + /* now restrict to preliminary range */ +#if defined(CONFIG_SYS_BR0_PRELIM) && defined(CONFIG_SYS_OR0_PRELIM) + memctl->br0 = CONFIG_SYS_BR0_PRELIM; + memctl->or0 = CONFIG_SYS_OR0_PRELIM; +#endif + +#if defined(CONFIG_SYS_BR1_PRELIM) && defined(CONFIG_SYS_OR1_PRELIM) + memctl->or1 = CONFIG_SYS_OR1_PRELIM; + memctl->br1 = CONFIG_SYS_BR1_PRELIM; +#endif + +#if defined(CONFIG_SYS_BR2_PRELIM) && defined(CONFIG_SYS_OR2_PRELIM) + memctl->or2 = CONFIG_SYS_OR2_PRELIM; + memctl->br2 = CONFIG_SYS_BR2_PRELIM; +#endif + +#if defined(CONFIG_SYS_BR3_PRELIM) && defined(CONFIG_SYS_OR3_PRELIM) + memctl->or3 = CONFIG_SYS_OR3_PRELIM; + memctl->br3 = CONFIG_SYS_BR3_PRELIM; +#endif + +#if defined(CONFIG_SYS_BR4_PRELIM) && defined(CONFIG_SYS_OR4_PRELIM) + memctl->or4 = CONFIG_SYS_OR4_PRELIM; + memctl->br4 = CONFIG_SYS_BR4_PRELIM; +#endif + +#if defined(CONFIG_SYS_BR5_PRELIM) && defined(CONFIG_SYS_OR5_PRELIM) + memctl->or5 = CONFIG_SYS_OR5_PRELIM; + memctl->br5 = CONFIG_SYS_BR5_PRELIM; +#endif + +#if defined(CONFIG_SYS_BR6_PRELIM) && defined(CONFIG_SYS_OR6_PRELIM) + memctl->or6 = CONFIG_SYS_OR6_PRELIM; + memctl->br6 = CONFIG_SYS_BR6_PRELIM; +#endif + +#if defined(CONFIG_SYS_BR7_PRELIM) && defined(CONFIG_SYS_OR7_PRELIM) + memctl->or7 = CONFIG_SYS_OR7_PRELIM; + memctl->br7 = CONFIG_SYS_BR7_PRELIM; +#endif +#if defined(CONFIG_FSL_DMA) + dma_init(); +#endif + + /* enable the timebase bit in HID0 */ + set_hid0(get_hid0() | 0x4000000); + + /* enable EMCP, SYNCBE | ABE bits in HID1 */ + set_hid1(get_hid1() | 0x80000C00); +} + +/* + * initialize higher level parts of CPU like timers + */ +int cpu_init_r(void) +{ +#if defined(CONFIG_MP) + setup_mp(); +#endif + return 0; +} + +/* Set up BAT registers */ +void setup_bats(void) +{ +#if defined(CONFIG_SYS_DBAT0U) && defined(CONFIG_SYS_DBAT0L) + write_bat(DBAT0, CONFIG_SYS_DBAT0U, CONFIG_SYS_DBAT0L); +#endif +#if defined(CONFIG_SYS_IBAT0U) && defined(CONFIG_SYS_IBAT0L) + write_bat(IBAT0, CONFIG_SYS_IBAT0U, CONFIG_SYS_IBAT0L); +#endif + write_bat(DBAT1, CONFIG_SYS_DBAT1U, CONFIG_SYS_DBAT1L); + write_bat(IBAT1, CONFIG_SYS_IBAT1U, CONFIG_SYS_IBAT1L); + write_bat(DBAT2, CONFIG_SYS_DBAT2U, CONFIG_SYS_DBAT2L); + write_bat(IBAT2, CONFIG_SYS_IBAT2U, CONFIG_SYS_IBAT2L); + write_bat(DBAT3, CONFIG_SYS_DBAT3U, CONFIG_SYS_DBAT3L); + write_bat(IBAT3, CONFIG_SYS_IBAT3U, CONFIG_SYS_IBAT3L); + write_bat(DBAT4, CONFIG_SYS_DBAT4U, CONFIG_SYS_DBAT4L); + write_bat(IBAT4, CONFIG_SYS_IBAT4U, CONFIG_SYS_IBAT4L); + write_bat(DBAT5, CONFIG_SYS_DBAT5U, CONFIG_SYS_DBAT5L); + write_bat(IBAT5, CONFIG_SYS_IBAT5U, CONFIG_SYS_IBAT5L); + write_bat(DBAT6, CONFIG_SYS_DBAT6U, CONFIG_SYS_DBAT6L); + write_bat(IBAT6, CONFIG_SYS_IBAT6U, CONFIG_SYS_IBAT6L); + write_bat(DBAT7, CONFIG_SYS_DBAT7U, CONFIG_SYS_DBAT7L); + write_bat(IBAT7, CONFIG_SYS_IBAT7U, CONFIG_SYS_IBAT7L); + + return; +} + +#ifdef CONFIG_ADDR_MAP +/* Initialize address mapping array */ +void init_addr_map(void) +{ + int i; + ppc_bat_t bat = DBAT0; + phys_size_t size; + unsigned long upper, lower; + + for (i = 0; i < CONFIG_SYS_NUM_ADDR_MAP; i++, bat++) { + if (read_bat(bat, &upper, &lower) != -1) { + if (!BATU_VALID(upper)) + size = 0; + else + size = BATU_SIZE(upper); + addrmap_set_entry(BATU_VADDR(upper), BATL_PADDR(lower), + size, i); + } +#ifdef CONFIG_HIGH_BATS + /* High bats are not contiguous with low BAT numbers */ + if (bat == DBAT3) + bat = DBAT4 - 1; +#endif + } +} +#endif diff --git a/arch/powerpc/cpu/mpc86xx/ddr-8641.c b/arch/powerpc/cpu/mpc86xx/ddr-8641.c new file mode 100644 index 0000000..b8f2c93 --- /dev/null +++ b/arch/powerpc/cpu/mpc86xx/ddr-8641.c @@ -0,0 +1,85 @@ +/* + * Copyright 2008 Freescale Semiconductor, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * Version 2 as published by the Free Software Foundation. + */ + +#include <common.h> +#include <asm/io.h> +#include <asm/fsl_ddr_sdram.h> + +#if (CONFIG_CHIP_SELECTS_PER_CTRL > 4) +#error Invalid setting for CONFIG_CHIP_SELECTS_PER_CTRL +#endif + +void fsl_ddr_set_memctl_regs(const fsl_ddr_cfg_regs_t *regs, + unsigned int ctrl_num) +{ + unsigned int i; + volatile ccsr_ddr_t *ddr; + + switch (ctrl_num) { + case 0: + ddr = (void *)CONFIG_SYS_MPC86xx_DDR_ADDR; + break; + case 1: + ddr = (void *)CONFIG_SYS_MPC86xx_DDR2_ADDR; + break; + default: + printf("%s unexpected ctrl_num = %u\n", __FUNCTION__, ctrl_num); + return; + } + + for (i = 0; i < CONFIG_CHIP_SELECTS_PER_CTRL; i++) { + if (i == 0) { + out_be32(&ddr->cs0_bnds, regs->cs[i].bnds); + out_be32(&ddr->cs0_config, regs->cs[i].config); + + } else if (i == 1) { + out_be32(&ddr->cs1_bnds, regs->cs[i].bnds); + out_be32(&ddr->cs1_config, regs->cs[i].config); + + } else if (i == 2) { + out_be32(&ddr->cs2_bnds, regs->cs[i].bnds); + out_be32(&ddr->cs2_config, regs->cs[i].config); + + } else if (i == 3) { + out_be32(&ddr->cs3_bnds, regs->cs[i].bnds); + out_be32(&ddr->cs3_config, regs->cs[i].config); + } + } + + out_be32(&ddr->timing_cfg_3, regs->timing_cfg_3); + out_be32(&ddr->timing_cfg_0, regs->timing_cfg_0); + out_be32(&ddr->timing_cfg_1, regs->timing_cfg_1); + out_be32(&ddr->timing_cfg_2, regs->timing_cfg_2); + out_be32(&ddr->sdram_cfg_2, regs->ddr_sdram_cfg_2); + out_be32(&ddr->sdram_mode, regs->ddr_sdram_mode); + out_be32(&ddr->sdram_mode_2, regs->ddr_sdram_mode_2); + out_be32(&ddr->sdram_mode_cntl, regs->ddr_sdram_md_cntl); + out_be32(&ddr->sdram_interval, regs->ddr_sdram_interval); + out_be32(&ddr->sdram_data_init, regs->ddr_data_init); + out_be32(&ddr->sdram_clk_cntl, regs->ddr_sdram_clk_cntl); + out_be32(&ddr->init_addr, regs->ddr_init_addr); + out_be32(&ddr->init_ext_addr, regs->ddr_init_ext_addr); + + debug("before go\n"); + + /* + * 200 painful micro-seconds must elapse between + * the DDR clock setup and the DDR config enable. + */ + udelay(200); + asm volatile("sync;isync"); + + out_be32(&ddr->sdram_cfg, regs->ddr_sdram_cfg); + + /* + * Poll DDR_SDRAM_CFG_2[D_INIT] bit until auto-data init is done + */ + while (in_be32(&ddr->sdram_cfg_2) & 0x10) { + udelay(10000); /* throttle polling rate */ + } +} diff --git a/arch/powerpc/cpu/mpc86xx/fdt.c b/arch/powerpc/cpu/mpc86xx/fdt.c new file mode 100644 index 0000000..51f3f4c --- /dev/null +++ b/arch/powerpc/cpu/mpc86xx/fdt.c @@ -0,0 +1,60 @@ +/* + * Copyright 2008 Freescale Semiconductor, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * Version 2 as published by the Free Software Foundation. + */ + +#include <common.h> +#include <libfdt.h> +#include <fdt_support.h> +#include <asm/mp.h> + +DECLARE_GLOBAL_DATA_PTR; + +extern void ft_fixup_num_cores(void *blob); + +void ft_cpu_setup(void *blob, bd_t *bd) +{ +#ifdef CONFIG_MP + int off; + u32 bootpg = determine_mp_bootpg(); +#endif + + do_fixup_by_prop_u32(blob, "device_type", "cpu", 4, + "timebase-frequency", bd->bi_busfreq / 4, 1); + do_fixup_by_prop_u32(blob, "device_type", "cpu", 4, + "bus-frequency", bd->bi_busfreq, 1); + do_fixup_by_prop_u32(blob, "device_type", "cpu", 4, + "clock-frequency", bd->bi_intfreq, 1); + do_fixup_by_prop_u32(blob, "device_type", "soc", 4, + "bus-frequency", bd->bi_busfreq, 1); + +#if defined(CONFIG_MPC8641) + do_fixup_by_compat_u32(blob, "fsl,mpc8641-localbus", + "bus-frequency", gd->lbc_clk, 1); +#endif + do_fixup_by_compat_u32(blob, "fsl,elbc", + "bus-frequency", gd->lbc_clk, 1); + + fdt_fixup_memory(blob, (u64)bd->bi_memstart, (u64)bd->bi_memsize); + +#if defined(CONFIG_HAS_ETH0) || defined(CONFIG_HAS_ETH1) \ + || defined(CONFIG_HAS_ETH2) || defined(CONFIG_HAS_ETH3) + fdt_fixup_ethernet(blob); +#endif + +#ifdef CONFIG_SYS_NS16550 + do_fixup_by_compat_u32(blob, "ns16550", + "clock-frequency", CONFIG_SYS_NS16550_CLK, 1); +#endif + +#ifdef CONFIG_MP + /* Reserve the boot page so OSes dont use it */ + off = fdt_add_mem_rsv(blob, bootpg, (u64)4096); + if (off < 0) + printf("%s: %s\n", __FUNCTION__, fdt_strerror(off)); +#endif + ft_fixup_num_cores(blob); +} diff --git a/arch/powerpc/cpu/mpc86xx/interrupts.c b/arch/powerpc/cpu/mpc86xx/interrupts.c new file mode 100644 index 0000000..c78fc72 --- /dev/null +++ b/arch/powerpc/cpu/mpc86xx/interrupts.c @@ -0,0 +1,115 @@ +/* + * (C) Copyright 2000-2002 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * (C) Copyright 2002 (440 port) + * Scott McNutt, Artesyn Communication Producs, smcnutt@artsyncp.com + * + * (C) Copyright 2003 Motorola Inc. (MPC85xx port) + * Xianghua Xiao (X.Xiao@motorola.com) + * + * (C) Copyright 2004, 2007 Freescale Semiconductor. (MPC86xx Port) + * Jeff Brown + * Srikanth Srinivasan (srikanth.srinivasan@freescale.com) + * + * 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> +#include <mpc86xx.h> +#include <command.h> +#include <asm/processor.h> + +int interrupt_init_cpu(unsigned long *decrementer_count) +{ + volatile immap_t *immr = (immap_t *)CONFIG_SYS_IMMR; + volatile ccsr_pic_t *pic = &immr->im_pic; + + pic->gcr = MPC86xx_PICGCR_RST; + while (pic->gcr & MPC86xx_PICGCR_RST) + ; + pic->gcr = MPC86xx_PICGCR_MODE; + + *decrementer_count = get_tbclk() / CONFIG_SYS_HZ; + debug("interrupt init: tbclk() = %d MHz, decrementer_count = %ld\n", + (get_tbclk() / 1000000), + *decrementer_count); + +#ifdef CONFIG_INTERRUPTS + + pic->iivpr1 = 0x810001; /* 50220 enable mcm interrupts */ + debug("iivpr1@%x = %x\n", &pic->iivpr1, pic->iivpr1); + + pic->iivpr2 = 0x810002; /* 50240 enable ddr interrupts */ + debug("iivpr2@%x = %x\n", &pic->iivpr2, pic->iivpr2); + + pic->iivpr3 = 0x810003; /* 50260 enable lbc interrupts */ + debug("iivpr3@%x = %x\n", &pic->iivpr3, pic->iivpr3); + +#if defined(CONFIG_PCI1) || defined(CONFIG_PCIE1) + pic->iivpr8 = 0x810008; /* enable pcie1 interrupts */ + debug("iivpr8@%x = %x\n", &pic->iivpr8, pic->iivpr8); +#endif +#if defined(CONFIG_PCI2) || defined(CONFIG_PCIE2) + pic->iivpr9 = 0x810009; /* enable pcie2 interrupts */ + debug("iivpr9@%x = %x\n", &pic->iivpr9, pic->iivpr9); +#endif + + pic->ctpr = 0; /* 40080 clear current task priority register */ +#endif + + return 0; +} + +/* + * timer_interrupt - gets called when the decrementer overflows, + * with interrupts disabled. + * Trivial implementation - no need to be really accurate. + */ +void timer_interrupt_cpu(struct pt_regs *regs) +{ + /* nothing to do here */ +} + +/* + * Install and free a interrupt handler. Not implemented yet. + */ +void irq_install_handler(int vec, interrupt_handler_t *handler, void *arg) +{ +} + +void irq_free_handler(int vec) +{ +} + +/* + * irqinfo - print information about PCI devices,not implemented. + */ +int do_irqinfo(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]) +{ + return 0; +} + +/* + * Handle external interrupts + */ +void external_interrupt(struct pt_regs *regs) +{ + puts("external_interrupt (oops!)\n"); +} diff --git a/arch/powerpc/cpu/mpc86xx/mp.c b/arch/powerpc/cpu/mpc86xx/mp.c new file mode 100644 index 0000000..b4a0faa --- /dev/null +++ b/arch/powerpc/cpu/mpc86xx/mp.c @@ -0,0 +1,125 @@ +/* + * Copyright 2008-2010 Freescale Semiconductor, Inc. + * + * 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> +#include <asm/processor.h> +#include <asm/mmu.h> +#include <ioports.h> +#include <lmb.h> +#include <asm/io.h> +#include <asm/mp.h> + +DECLARE_GLOBAL_DATA_PTR; + +int cpu_reset(int nr) +{ + /* dummy function so common/cmd_mp.c will build + * should be implemented in the future, when cpu_release() + * is supported. Be aware there may be a similiar bug + * as exists on MPC85xx w/its PIC having a timing window + * associated to resetting the core */ + return 1; +} + +int cpu_status(int nr) +{ + /* dummy function so common/cmd_mp.c will build */ + return 0; +} + +int cpu_disable(int nr) +{ + volatile immap_t *immap = (immap_t *) CONFIG_SYS_CCSRBAR; + volatile ccsr_gur_t *gur = &immap->im_gur; + + switch (nr) { + case 0: + setbits_be32(&gur->devdisr, MPC86xx_DEVDISR_CPU0); + break; + case 1: + setbits_be32(&gur->devdisr, MPC86xx_DEVDISR_CPU1); + break; + default: + printf("Invalid cpu number for disable %d\n", nr); + return 1; + } + + return 0; +} + +int cpu_release(int nr, int argc, char *argv[]) +{ + /* dummy function so common/cmd_mp.c will build + * should be implemented in the future */ + return 1; +} + +u32 determine_mp_bootpg(void) +{ + /* if we have 4G or more of memory, put the boot page at 4Gb-1M */ + if ((u64)gd->ram_size > 0xfffff000) + return (0xfff00000); + + return (gd->ram_size - (1024 * 1024)); +} + +void cpu_mp_lmb_reserve(struct lmb *lmb) +{ + u32 bootpg = determine_mp_bootpg(); + + /* tell u-boot we stole a page */ + lmb_reserve(lmb, bootpg, 4096); +} + +/* + * Copy the code for other cpus to execute into an + * aligned location accessible via BPTR + */ +void setup_mp(void) +{ + extern ulong __secondary_start_page; + ulong fixup = (ulong)&__secondary_start_page; + u32 bootpg = determine_mp_bootpg(); + u32 bootpg_va; + + if (bootpg >= CONFIG_SYS_MAX_DDR_BAT_SIZE) { + /* We're not covered by the DDR mapping, set up BAT */ + write_bat(DBAT7, CONFIG_SYS_SCRATCH_VA | BATU_BL_128K | + BATU_VS | BATU_VP, + bootpg | BATL_PP_RW | BATL_MEMCOHERENCE); + bootpg_va = CONFIG_SYS_SCRATCH_VA; + } else { + bootpg_va = bootpg; + } + + memcpy((void *)bootpg_va, (void *)fixup, 4096); + flush_cache(bootpg_va, 4096); + + /* remove the temporary BAT mapping */ + if (bootpg >= CONFIG_SYS_MAX_DDR_BAT_SIZE) + write_bat(DBAT7, 0, 0); + + /* If the physical location of bootpg is not at fff00000, set BPTR */ + if (bootpg != 0xfff00000) + out_be32((uint *)(CONFIG_SYS_CCSRBAR + 0x20), 0x80000000 | + (bootpg >> 12)); +} diff --git a/arch/powerpc/cpu/mpc86xx/release.S b/arch/powerpc/cpu/mpc86xx/release.S new file mode 100644 index 0000000..67a6f2b --- /dev/null +++ b/arch/powerpc/cpu/mpc86xx/release.S @@ -0,0 +1,167 @@ +/* + * Copyright 2004, 2007, 2008 Freescale Semiconductor. + * Srikanth Srinivasan <srikanth.srinivaan@freescale.com> + * + * 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 <config.h> +#include <mpc86xx.h> +#include <version.h> + +#include <ppc_asm.tmpl> +#include <ppc_defs.h> + +#include <asm/cache.h> +#include <asm/mmu.h> + +/* If this is a multi-cpu system then we need to handle the + * 2nd cpu. The assumption is that the 2nd cpu is being + * held in boot holdoff mode until the 1st cpu unlocks it + * from Linux. We'll do some basic cpu init and then pass + * it to the Linux Reset Vector. + * Sri: Much of this initialization is not required. Linux + * rewrites the bats, and the sprs and also enables the L1 cache. + * + * Core 0 must copy this to a 1M aligned region and set BPTR + * to point to it. + */ + .align 12 +.globl __secondary_start_page +__secondary_start_page: + .space 0x100 /* space over to reset vector loc */ + mfspr r0, MSSCR0 + andi. r0, r0, 0x0020 + rlwinm r0,r0,27,31,31 + mtspr PIR, r0 + + /* Invalidate BATs */ + li r0, 0 + mtspr IBAT0U, r0 + mtspr IBAT1U, r0 + mtspr IBAT2U, r0 + mtspr IBAT3U, r0 + mtspr IBAT4U, r0 + mtspr IBAT5U, r0 + mtspr IBAT6U, r0 + mtspr IBAT7U, r0 + isync + mtspr DBAT0U, r0 + mtspr DBAT1U, r0 + mtspr DBAT2U, r0 + mtspr DBAT3U, r0 + mtspr DBAT4U, r0 + mtspr DBAT5U, r0 + mtspr DBAT6U, r0 + mtspr DBAT7U, r0 + isync + sync + + /* enable extended addressing */ + mfspr r0, HID0 + lis r0, (HID0_HIGH_BAT_EN | HID0_XBSEN | HID0_XAEN)@h + ori r0, r0, (HID0_HIGH_BAT_EN | HID0_XBSEN | HID0_XAEN)@l + mtspr HID0, r0 + sync + isync + +#ifdef CONFIG_SYS_L2 + /* init the L2 cache */ + addis r3, r0, L2_INIT@h + ori r3, r3, L2_INIT@l + sync + mtspr l2cr, r3 +#ifdef CONFIG_ALTIVEC + dssall +#endif + /* invalidate the L2 cache */ + mfspr r3, l2cr + rlwinm. r3, r3, 0, 0, 0 + beq 1f + + mfspr r3, l2cr + rlwinm r3, r3, 0, 1, 31 + +#ifdef CONFIG_ALTIVEC + dssall +#endif + sync + mtspr l2cr, r3 + sync +1: mfspr r3, l2cr + oris r3, r3, L2CR_L2I@h + mtspr l2cr, r3 + +invl2: + mfspr r3, l2cr + andis. r3, r3, L2CR_L2I@h + bne invl2 + sync +#endif + + /* enable and invalidate the data cache */ + mfspr r3, HID0 + li r5, HID0_DCFI|HID0_DLOCK + andc r3, r3, r5 + mtspr HID0, r3 /* no invalidate, unlock */ + ori r3, r3, HID0_DCE + ori r5, r3, HID0_DCFI + mtspr HID0, r5 /* enable + invalidate */ + mtspr HID0, r3 /* enable */ + sync +#ifdef CONFIG_SYS_L2 + sync + lis r3, L2_ENABLE@h + ori r3, r3, L2_ENABLE@l + mtspr l2cr, r3 + isync + sync +#endif + + /* enable and invalidate the instruction cache*/ + mfspr r3, HID0 + li r5, HID0_ICFI|HID0_ILOCK + andc r3, r3, r5 + ori r3, r3, HID0_ICE + ori r5, r3, HID0_ICFI + mtspr HID0, r5 + mtspr HID0, r3 + isync + sync + + /* TBEN in HID0 */ + mfspr r4, HID0 + oris r4, r4, 0x0400 + mtspr HID0, r4 + sync + isync + + /* MCP|SYNCBE|ABE in HID1 */ + mfspr r4, HID1 + oris r4, r4, 0x8000 + ori r4, r4, 0x0C00 + mtspr HID1, r4 + sync + isync + + lis r3, CONFIG_LINUX_RESET_VEC@h + ori r3, r3, CONFIG_LINUX_RESET_VEC@l + mtlr r3 + blr + + /* Never Returns, Running in Linux Now */ diff --git a/arch/powerpc/cpu/mpc86xx/speed.c b/arch/powerpc/cpu/mpc86xx/speed.c new file mode 100644 index 0000000..64a3479 --- /dev/null +++ b/arch/powerpc/cpu/mpc86xx/speed.c @@ -0,0 +1,163 @@ +/* + * Copyright 2004 Freescale Semiconductor. + * Jeff Brown + * Srikanth Srinivasan (srikanth.srinivasan@freescale.com) + * + * (C) Copyright 2000-2002 + * Wolfgang Denk, DENX Software Engineering, wd@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> +#include <mpc86xx.h> +#include <asm/processor.h> +#include <asm/io.h> + +DECLARE_GLOBAL_DATA_PTR; + +/* used in some defintiions of CONFIG_SYS_CLK_FREQ */ +extern unsigned long get_board_sys_clk(unsigned long dummy); + +void get_sys_info(sys_info_t *sysInfo) +{ + volatile immap_t *immap = (immap_t *) CONFIG_SYS_IMMR; + volatile ccsr_gur_t *gur = &immap->im_gur; + uint plat_ratio, e600_ratio; + uint lcrr_div; + + plat_ratio = (gur->porpllsr) & 0x0000003e; + plat_ratio >>= 1; + + switch (plat_ratio) { + case 0x0: + sysInfo->freqSystemBus = 16 * CONFIG_SYS_CLK_FREQ; + break; + case 0x02: + case 0x03: + case 0x04: + case 0x05: + case 0x06: + case 0x08: + case 0x09: + case 0x0a: + case 0x0c: + case 0x10: + sysInfo->freqSystemBus = plat_ratio * CONFIG_SYS_CLK_FREQ; + break; + default: + sysInfo->freqSystemBus = 0; + break; + } + + e600_ratio = (gur->porpllsr) & 0x003f0000; + e600_ratio >>= 16; + + switch (e600_ratio) { + case 0x10: + sysInfo->freqProcessor = 2 * sysInfo->freqSystemBus; + break; + case 0x19: + sysInfo->freqProcessor = 5 * sysInfo->freqSystemBus / 2; + break; + case 0x20: + sysInfo->freqProcessor = 3 * sysInfo->freqSystemBus; + break; + case 0x39: + sysInfo->freqProcessor = 7 * sysInfo->freqSystemBus / 2; + break; + case 0x28: + sysInfo->freqProcessor = 4 * sysInfo->freqSystemBus; + break; + case 0x1d: + sysInfo->freqProcessor = 9 * sysInfo->freqSystemBus / 2; + break; + default: + sysInfo->freqProcessor = e600_ratio + sysInfo->freqSystemBus; + break; + } + +#if defined(CONFIG_SYS_LBC_LCRR) + /* We will program LCRR to this value later */ + lcrr_div = CONFIG_SYS_LBC_LCRR & LCRR_CLKDIV; +#else + { + volatile ccsr_lbc_t *lbc = &immap->im_lbc; + lcrr_div = in_be32(&lbc->lcrr) & LCRR_CLKDIV; + } +#endif + if (lcrr_div == 2 || lcrr_div == 4 || lcrr_div == 8) { + sysInfo->freqLocalBus = sysInfo->freqSystemBus / (lcrr_div * 2); + } else { + /* In case anyone cares what the unknown value is */ + sysInfo->freqLocalBus = lcrr_div; + } +} + + +/* + * Measure CPU clock speed (core clock GCLK1, GCLK2) + * (Approx. GCLK frequency in Hz) + */ + +int get_clocks(void) +{ + sys_info_t sys_info; + + get_sys_info(&sys_info); + gd->cpu_clk = sys_info.freqProcessor; + gd->bus_clk = sys_info.freqSystemBus; + gd->lbc_clk = sys_info.freqLocalBus; + + /* + * The base clock for I2C depends on the actual SOC. Unfortunately, + * there is no pattern that can be used to determine the frequency, so + * the only choice is to look up the actual SOC number and use the value + * for that SOC. This information is taken from application note + * AN2919. + */ +#ifdef CONFIG_MPC8610 + gd->i2c1_clk = sys_info.freqSystemBus; +#else + gd->i2c1_clk = sys_info.freqSystemBus / 2; +#endif + gd->i2c2_clk = gd->i2c1_clk; + + if (gd->cpu_clk != 0) + return 0; + else + return 1; +} + + +/* + * get_bus_freq + * Return system bus freq in Hz + */ + +ulong get_bus_freq(ulong dummy) +{ + ulong val; + sys_info_t sys_info; + + get_sys_info(&sys_info); + val = sys_info.freqSystemBus; + + return val; +} diff --git a/arch/powerpc/cpu/mpc86xx/start.S b/arch/powerpc/cpu/mpc86xx/start.S new file mode 100644 index 0000000..ed1e4ca --- /dev/null +++ b/arch/powerpc/cpu/mpc86xx/start.S @@ -0,0 +1,957 @@ +/* + * Copyright 2004, 2007 Freescale Semiconductor. + * Srikanth Srinivasan <srikanth.srinivaan@freescale.com> + * + * 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 + */ + +/* U-Boot - Startup Code for 86xx PowerPC based Embedded Boards + * + * + * The processor starts at 0xfff00100 and the code is executed + * from flash. The code is organized to be at an other address + * in memory, but as long we don't jump around before relocating. + * board_init lies at a quite high address and when the cpu has + * jumped there, everything is ok. + */ +#include <config.h> +#include <mpc86xx.h> +#include <timestamp.h> +#include <version.h> + +#include <ppc_asm.tmpl> +#include <ppc_defs.h> + +#include <asm/cache.h> +#include <asm/mmu.h> + +#ifndef CONFIG_IDENT_STRING +#define CONFIG_IDENT_STRING "" +#endif + +/* + * Need MSR_DR | MSR_IR enabled to access I/O (printf) in exceptions + */ + +/* + * Set up GOT: Global Offset Table + * + * Use r12 to access the GOT + */ + START_GOT + GOT_ENTRY(_GOT2_TABLE_) + GOT_ENTRY(_FIXUP_TABLE_) + + GOT_ENTRY(_start) + GOT_ENTRY(_start_of_vectors) + GOT_ENTRY(_end_of_vectors) + GOT_ENTRY(transfer_to_handler) + + GOT_ENTRY(__init_end) + GOT_ENTRY(_end) + GOT_ENTRY(__bss_start) + END_GOT + +/* + * r3 - 1st arg to board_init(): IMMP pointer + * r4 - 2nd arg to board_init(): boot flag + */ + .text + .long 0x27051956 /* U-Boot Magic Number */ + .globl version_string +version_string: + .ascii U_BOOT_VERSION + .ascii " (", U_BOOT_DATE, " - ", U_BOOT_TIME, ")" + .ascii CONFIG_IDENT_STRING, "\0" + + . = EXC_OFF_SYS_RESET + .globl _start +_start: + li r21, BOOTFLAG_COLD /* Normal Power-On: Boot from FLASH */ + b boot_cold + sync + + . = EXC_OFF_SYS_RESET + 0x10 + + .globl _start_warm +_start_warm: + li r21, BOOTFLAG_WARM /* Software reboot */ + b boot_warm + sync + + /* the boot code is located below the exception table */ + + .globl _start_of_vectors +_start_of_vectors: + +/* Machine check */ + STD_EXCEPTION(0x200, MachineCheck, MachineCheckException) + +/* Data Storage exception. */ + STD_EXCEPTION(0x300, DataStorage, UnknownException) + +/* Instruction Storage exception. */ + STD_EXCEPTION(0x400, InstStorage, UnknownException) + +/* External Interrupt exception. */ + STD_EXCEPTION(0x500, ExtInterrupt, external_interrupt) + +/* Alignment exception. */ + . = 0x600 +Alignment: + EXCEPTION_PROLOG(SRR0, SRR1) + mfspr r4,DAR + stw r4,_DAR(r21) + mfspr r5,DSISR + stw r5,_DSISR(r21) + addi r3,r1,STACK_FRAME_OVERHEAD + EXC_XFER_TEMPLATE(Alignment, AlignmentException, MSR_KERNEL, COPY_EE) + +/* Program check exception */ + . = 0x700 +ProgramCheck: + EXCEPTION_PROLOG(SRR0, SRR1) + addi r3,r1,STACK_FRAME_OVERHEAD + EXC_XFER_TEMPLATE(ProgramCheck, ProgramCheckException, + MSR_KERNEL, COPY_EE) + + STD_EXCEPTION(0x800, FPUnavailable, UnknownException) + + /* I guess we could implement decrementer, and may have + * to someday for timekeeping. + */ + STD_EXCEPTION(0x900, Decrementer, timer_interrupt) + STD_EXCEPTION(0xa00, Trap_0a, UnknownException) + STD_EXCEPTION(0xb00, Trap_0b, UnknownException) + STD_EXCEPTION(0xc00, SystemCall, UnknownException) + STD_EXCEPTION(0xd00, SingleStep, UnknownException) + STD_EXCEPTION(0xe00, Trap_0e, UnknownException) + STD_EXCEPTION(0xf00, Trap_0f, UnknownException) + STD_EXCEPTION(0x1000, SoftEmu, SoftEmuException) + STD_EXCEPTION(0x1100, InstructionTLBMiss, UnknownException) + STD_EXCEPTION(0x1200, DataTLBMiss, UnknownException) + STD_EXCEPTION(0x1300, InstructionTLBError, UnknownException) + STD_EXCEPTION(0x1400, DataTLBError, UnknownException) + STD_EXCEPTION(0x1500, Reserved5, UnknownException) + STD_EXCEPTION(0x1600, Reserved6, UnknownException) + STD_EXCEPTION(0x1700, Reserved7, UnknownException) + STD_EXCEPTION(0x1800, Reserved8, UnknownException) + STD_EXCEPTION(0x1900, Reserved9, UnknownException) + STD_EXCEPTION(0x1a00, ReservedA, UnknownException) + STD_EXCEPTION(0x1b00, ReservedB, UnknownException) + STD_EXCEPTION(0x1c00, DataBreakpoint, UnknownException) + STD_EXCEPTION(0x1d00, InstructionBreakpoint, UnknownException) + STD_EXCEPTION(0x1e00, PeripheralBreakpoint, UnknownException) + STD_EXCEPTION(0x1f00, DevPortBreakpoint, UnknownException) + + .globl _end_of_vectors +_end_of_vectors: + + . = 0x2000 + +boot_cold: +boot_warm: + /* + * NOTE: Only Cpu 0 will ever come here. Other cores go to an + * address specified by the BPTR + */ +1: +#ifdef CONFIG_SYS_RAMBOOT + /* disable everything */ + li r0, 0 + mtspr HID0, r0 + sync + mtmsr 0 +#endif + + /* Invalidate BATs */ + bl invalidate_bats + sync + /* Invalidate all of TLB before MMU turn on */ + bl clear_tlbs + sync + +#ifdef CONFIG_SYS_L2 + /* init the L2 cache */ + lis r3, L2_INIT@h + ori r3, r3, L2_INIT@l + mtspr l2cr, r3 + /* invalidate the L2 cache */ + bl l2cache_invalidate + sync +#endif + + /* + * Calculate absolute address in FLASH and jump there + *------------------------------------------------------*/ + lis r3, CONFIG_SYS_MONITOR_BASE_EARLY@h + ori r3, r3, CONFIG_SYS_MONITOR_BASE_EARLY@l + addi r3, r3, in_flash - _start + EXC_OFF_SYS_RESET + mtlr r3 + blr + +in_flash: + /* let the C-code set up the rest */ + /* */ + /* Be careful to keep code relocatable ! */ + /*------------------------------------------------------*/ + /* perform low-level init */ + + /* enable extended addressing */ + bl enable_ext_addr + + /* setup the bats */ + bl early_bats + + /* + * Cache must be enabled here for stack-in-cache trick. + * This means we need to enable the BATS. + * Cache should be turned on after BATs, since by default + * everything is write-through. + */ + + /* enable address translation */ + mfmsr r5 + ori r5, r5, (MSR_IR | MSR_DR) + lis r3,addr_trans_enabled@h + ori r3, r3, addr_trans_enabled@l + mtspr SPRN_SRR0,r3 + mtspr SPRN_SRR1,r5 + rfi + +addr_trans_enabled: + /* enable and invalidate the data cache */ +/* bl l1dcache_enable */ + bl dcache_enable + sync + +#if 1 + bl icache_enable +#endif + +#ifdef CONFIG_SYS_INIT_RAM_LOCK + bl lock_ram_in_cache + sync +#endif + +#if (CONFIG_SYS_CCSRBAR_DEFAULT != CONFIG_SYS_CCSRBAR) + bl setup_ccsrbar +#endif + + /* set up the stack pointer in our newly created + * cache-ram (r1) */ + lis r1, (CONFIG_SYS_INIT_RAM_ADDR + CONFIG_SYS_GBL_DATA_OFFSET)@h + ori r1, r1, (CONFIG_SYS_INIT_RAM_ADDR + CONFIG_SYS_GBL_DATA_OFFSET)@l + + li r0, 0 /* Make room for stack frame header and */ + stwu r0, -4(r1) /* clear final stack frame so that */ + stwu r0, -4(r1) /* stack backtraces terminate cleanly */ + + GET_GOT /* initialize GOT access */ + + /* run low-level CPU init code (from Flash) */ + bl cpu_init_f + sync + +#ifdef RUN_DIAG + + /* Load PX_AUX register address in r4 */ + lis r4, PIXIS_BASE@h + ori r4, r4, 0x6 + /* Load contents of PX_AUX in r3 bits 24 to 31*/ + lbz r3, 0(r4) + + /* Mask and obtain the bit in r3 */ + rlwinm. r3, r3, 0, 24, 24 + /* If not zero, jump and continue with u-boot */ + bne diag_done + + /* Load back contents of PX_AUX in r3 bits 24 to 31 */ + lbz r3, 0(r4) + /* Set the MSB of the register value */ + ori r3, r3, 0x80 + /* Write value in r3 back to PX_AUX */ + stb r3, 0(r4) + + /* Get the address to jump to in r3*/ + lis r3, CONFIG_SYS_DIAG_ADDR@h + ori r3, r3, CONFIG_SYS_DIAG_ADDR@l + + /* Load the LR with the branch address */ + mtlr r3 + + /* Branch to diagnostic */ + blr + +diag_done: +#endif + +/* bl l2cache_enable */ + mr r3, r21 + + /* r3: BOOTFLAG */ + /* run 1st part of board init code (from Flash) */ + bl board_init_f + sync + + /* NOTREACHED */ + + .globl invalidate_bats +invalidate_bats: + + li r0, 0 + /* invalidate BATs */ + mtspr IBAT0U, r0 + mtspr IBAT1U, r0 + mtspr IBAT2U, r0 + mtspr IBAT3U, r0 + mtspr IBAT4U, r0 + mtspr IBAT5U, r0 + mtspr IBAT6U, r0 + mtspr IBAT7U, r0 + + isync + mtspr DBAT0U, r0 + mtspr DBAT1U, r0 + mtspr DBAT2U, r0 + mtspr DBAT3U, r0 + mtspr DBAT4U, r0 + mtspr DBAT5U, r0 + mtspr DBAT6U, r0 + mtspr DBAT7U, r0 + + isync + sync + blr + +/* + * early_bats: + * + * Set up bats needed early on - this is usually the BAT for the + * stack-in-cache, the Flash, and CCSR space + */ + .globl early_bats +early_bats: + /* IBAT 3 */ + lis r4, CONFIG_SYS_IBAT3L@h + ori r4, r4, CONFIG_SYS_IBAT3L@l + lis r3, CONFIG_SYS_IBAT3U@h + ori r3, r3, CONFIG_SYS_IBAT3U@l + mtspr IBAT3L, r4 + mtspr IBAT3U, r3 + isync + + /* DBAT 3 */ + lis r4, CONFIG_SYS_DBAT3L@h + ori r4, r4, CONFIG_SYS_DBAT3L@l + lis r3, CONFIG_SYS_DBAT3U@h + ori r3, r3, CONFIG_SYS_DBAT3U@l + mtspr DBAT3L, r4 + mtspr DBAT3U, r3 + isync + + /* IBAT 5 */ + lis r4, CONFIG_SYS_IBAT5L@h + ori r4, r4, CONFIG_SYS_IBAT5L@l + lis r3, CONFIG_SYS_IBAT5U@h + ori r3, r3, CONFIG_SYS_IBAT5U@l + mtspr IBAT5L, r4 + mtspr IBAT5U, r3 + isync + + /* DBAT 5 */ + lis r4, CONFIG_SYS_DBAT5L@h + ori r4, r4, CONFIG_SYS_DBAT5L@l + lis r3, CONFIG_SYS_DBAT5U@h + ori r3, r3, CONFIG_SYS_DBAT5U@l + mtspr DBAT5L, r4 + mtspr DBAT5U, r3 + isync + + /* IBAT 6 */ + lis r4, CONFIG_SYS_IBAT6L_EARLY@h + ori r4, r4, CONFIG_SYS_IBAT6L_EARLY@l + lis r3, CONFIG_SYS_IBAT6U_EARLY@h + ori r3, r3, CONFIG_SYS_IBAT6U_EARLY@l + mtspr IBAT6L, r4 + mtspr IBAT6U, r3 + isync + + /* DBAT 6 */ + lis r4, CONFIG_SYS_DBAT6L_EARLY@h + ori r4, r4, CONFIG_SYS_DBAT6L_EARLY@l + lis r3, CONFIG_SYS_DBAT6U_EARLY@h + ori r3, r3, CONFIG_SYS_DBAT6U_EARLY@l + mtspr DBAT6L, r4 + mtspr DBAT6U, r3 + isync + +#if(CONFIG_SYS_CCSRBAR_DEFAULT != CONFIG_SYS_CCSRBAR) + /* IBAT 7 */ + lis r4, CONFIG_SYS_CCSR_DEFAULT_IBATL@h + ori r4, r4, CONFIG_SYS_CCSR_DEFAULT_IBATL@l + lis r3, CONFIG_SYS_CCSR_DEFAULT_IBATU@h + ori r3, r3, CONFIG_SYS_CCSR_DEFAULT_IBATU@l + mtspr IBAT7L, r4 + mtspr IBAT7U, r3 + isync + + /* DBAT 7 */ + lis r4, CONFIG_SYS_CCSR_DEFAULT_DBATL@h + ori r4, r4, CONFIG_SYS_CCSR_DEFAULT_DBATL@l + lis r3, CONFIG_SYS_CCSR_DEFAULT_DBATU@h + ori r3, r3, CONFIG_SYS_CCSR_DEFAULT_DBATU@l + mtspr DBAT7L, r4 + mtspr DBAT7U, r3 + isync +#endif + blr + + .globl clear_tlbs +clear_tlbs: + addis r3, 0, 0x0000 + addis r5, 0, 0x4 + isync +tlblp: + tlbie r3 + sync + addi r3, r3, 0x1000 + cmp 0, 0, r3, r5 + blt tlblp + blr + + .globl disable_addr_trans +disable_addr_trans: + /* disable address translation */ + mflr r4 + mfmsr r3 + andi. r0, r3, (MSR_IR | MSR_DR) + beqlr + andc r3, r3, r0 + mtspr SRR0, r4 + mtspr SRR1, r3 + rfi + +/* + * This code finishes saving the registers to the exception frame + * and jumps to the appropriate handler for the exception. + * Register r21 is pointer into trap frame, r1 has new stack pointer. + */ + .globl transfer_to_handler +transfer_to_handler: + stw r22,_NIP(r21) + lis r22,MSR_POW@h + andc r23,r23,r22 + stw r23,_MSR(r21) + SAVE_GPR(7, r21) + SAVE_4GPRS(8, r21) + SAVE_8GPRS(12, r21) + SAVE_8GPRS(24, r21) + mflr r23 + andi. r24,r23,0x3f00 /* get vector offset */ + stw r24,TRAP(r21) + li r22,0 + stw r22,RESULT(r21) + mtspr SPRG2,r22 /* r1 is now kernel sp */ + lwz r24,0(r23) /* virtual address of handler */ + lwz r23,4(r23) /* where to go when done */ + mtspr SRR0,r24 + mtspr SRR1,r20 + mtlr r23 + SYNC + rfi /* jump to handler, enable MMU */ + +int_return: + mfmsr r28 /* Disable interrupts */ + li r4,0 + ori r4,r4,MSR_EE + andc r28,r28,r4 + SYNC /* Some chip revs need this... */ + mtmsr r28 + SYNC + lwz r2,_CTR(r1) + lwz r0,_LINK(r1) + mtctr r2 + mtlr r0 + lwz r2,_XER(r1) + lwz r0,_CCR(r1) + mtspr XER,r2 + mtcrf 0xFF,r0 + REST_10GPRS(3, r1) + REST_10GPRS(13, r1) + REST_8GPRS(23, r1) + REST_GPR(31, r1) + lwz r2,_NIP(r1) /* Restore environment */ + lwz r0,_MSR(r1) + mtspr SRR0,r2 + mtspr SRR1,r0 + lwz r0,GPR0(r1) + lwz r2,GPR2(r1) + lwz r1,GPR1(r1) + SYNC + rfi + + .globl dc_read +dc_read: + blr + + .globl get_pvr +get_pvr: + mfspr r3, PVR + blr + + .globl get_svr +get_svr: + mfspr r3, SVR + blr + + +/* + * Function: in8 + * Description: Input 8 bits + */ + .globl in8 +in8: + lbz r3,0x0000(r3) + blr + +/* + * Function: out8 + * Description: Output 8 bits + */ + .globl out8 +out8: + stb r4,0x0000(r3) + blr + +/* + * Function: out16 + * Description: Output 16 bits + */ + .globl out16 +out16: + sth r4,0x0000(r3) + blr + +/* + * Function: out16r + * Description: Byte reverse and output 16 bits + */ + .globl out16r +out16r: + sthbrx r4,r0,r3 + blr + +/* + * Function: out32 + * Description: Output 32 bits + */ + .globl out32 +out32: + stw r4,0x0000(r3) + blr + +/* + * Function: out32r + * Description: Byte reverse and output 32 bits + */ + .globl out32r +out32r: + stwbrx r4,r0,r3 + blr + +/* + * Function: in16 + * Description: Input 16 bits + */ + .globl in16 +in16: + lhz r3,0x0000(r3) + blr + +/* + * Function: in16r + * Description: Input 16 bits and byte reverse + */ + .globl in16r +in16r: + lhbrx r3,r0,r3 + blr + +/* + * Function: in32 + * Description: Input 32 bits + */ + .globl in32 +in32: + lwz 3,0x0000(3) + blr + +/* + * Function: in32r + * Description: Input 32 bits and byte reverse + */ + .globl in32r +in32r: + lwbrx r3,r0,r3 + blr + +/* + * void relocate_code (addr_sp, gd, addr_moni) + * + * This "function" does not return, instead it continues in RAM + * after relocating the monitor code. + * + * r3 = dest + * r4 = src + * r5 = length in bytes + * r6 = cachelinesize + */ + .globl relocate_code +relocate_code: + + mr r1, r3 /* Set new stack pointer */ + mr r9, r4 /* Save copy of Global Data pointer */ + mr r10, r5 /* Save copy of Destination Address */ + + GET_GOT + mr r3, r5 /* Destination Address */ + lis r4, CONFIG_SYS_MONITOR_BASE@h /* Source Address */ + ori r4, r4, CONFIG_SYS_MONITOR_BASE@l + lwz r5, GOT(__init_end) + sub r5, r5, r4 + li r6, CONFIG_SYS_CACHELINE_SIZE /* Cache Line Size */ + + /* + * Fix GOT pointer: + * + * New GOT-PTR = (old GOT-PTR - CONFIG_SYS_MONITOR_BASE) + Destination Address + * + * Offset: + */ + sub r15, r10, r4 + + /* First our own GOT */ + add r12, r12, r15 + /* then the one used by the C code */ + add r30, r30, r15 + + /* + * Now relocate code + */ + cmplw cr1,r3,r4 + addi r0,r5,3 + srwi. r0,r0,2 + beq cr1,4f /* In place copy is not necessary */ + beq 7f /* Protect against 0 count */ + mtctr r0 + bge cr1,2f + + la r8,-4(r4) + la r7,-4(r3) +1: lwzu r0,4(r8) + stwu r0,4(r7) + bdnz 1b + b 4f + +2: slwi r0,r0,2 + add r8,r4,r0 + add r7,r3,r0 +3: lwzu r0,-4(r8) + stwu r0,-4(r7) + bdnz 3b +/* + * Now flush the cache: note that we must start from a cache aligned + * address. Otherwise we might miss one cache line. + */ +4: cmpwi r6,0 + add r5,r3,r5 + beq 7f /* Always flush prefetch queue in any case */ + subi r0,r6,1 + andc r3,r3,r0 + mr r4,r3 +5: dcbst 0,r4 + add r4,r4,r6 + cmplw r4,r5 + blt 5b + sync /* Wait for all dcbst to complete on bus */ + mr r4,r3 +6: icbi 0,r4 + add r4,r4,r6 + cmplw r4,r5 + blt 6b +7: sync /* Wait for all icbi to complete on bus */ + isync + +/* + * We are done. Do not return, instead branch to second part of board + * initialization, now running from RAM. + */ + addi r0, r10, in_ram - _start + EXC_OFF_SYS_RESET + mtlr r0 + blr + +in_ram: + /* + * Relocation Function, r12 point to got2+0x8000 + * + * Adjust got2 pointers, no need to check for 0, this code + * already puts a few entries in the table. + */ + li r0,__got2_entries@sectoff@l + la r3,GOT(_GOT2_TABLE_) + lwz r11,GOT(_GOT2_TABLE_) + mtctr r0 + sub r11,r3,r11 + addi r3,r3,-4 +1: lwzu r0,4(r3) + cmpwi r0,0 + beq- 2f + add r0,r0,r11 + stw r0,0(r3) +2: bdnz 1b + + /* + * Now adjust the fixups and the pointers to the fixups + * in case we need to move ourselves again. + */ + li r0,__fixup_entries@sectoff@l + lwz r3,GOT(_FIXUP_TABLE_) + cmpwi r0,0 + mtctr r0 + addi r3,r3,-4 + beq 4f +3: lwzu r4,4(r3) + lwzux r0,r4,r11 + add r0,r0,r11 + stw r10,0(r3) + stw r0,0(r4) + bdnz 3b +4: +/* clear_bss: */ + /* + * Now clear BSS segment + */ + lwz r3,GOT(__bss_start) + lwz r4,GOT(_end) + + cmplw 0, r3, r4 + beq 6f + + li r0, 0 +5: + stw r0, 0(r3) + addi r3, r3, 4 + cmplw 0, r3, r4 + bne 5b +6: + mr r3, r9 /* Init Date pointer */ + mr r4, r10 /* Destination Address */ + bl board_init_r + + /* not reached - end relocate_code */ +/*-----------------------------------------------------------------------*/ + + /* + * Copy exception vector code to low memory + * + * r3: dest_addr + * r7: source address, r8: end address, r9: target address + */ + .globl trap_init +trap_init: + mflr r4 /* save link register */ + GET_GOT + lwz r7, GOT(_start) + lwz r8, GOT(_end_of_vectors) + + li r9, 0x100 /* reset vector always at 0x100 */ + + cmplw 0, r7, r8 + bgelr /* return if r7>=r8 - just in case */ +1: + lwz r0, 0(r7) + stw r0, 0(r9) + addi r7, r7, 4 + addi r9, r9, 4 + cmplw 0, r7, r8 + bne 1b + + /* + * relocate `hdlr' and `int_return' entries + */ + li r7, .L_MachineCheck - _start + EXC_OFF_SYS_RESET + li r8, Alignment - _start + EXC_OFF_SYS_RESET +2: + bl trap_reloc + addi r7, r7, 0x100 /* next exception vector */ + cmplw 0, r7, r8 + blt 2b + + li r7, .L_Alignment - _start + EXC_OFF_SYS_RESET + bl trap_reloc + + li r7, .L_ProgramCheck - _start + EXC_OFF_SYS_RESET + bl trap_reloc + + li r7, .L_FPUnavailable - _start + EXC_OFF_SYS_RESET + li r8, SystemCall - _start + EXC_OFF_SYS_RESET +3: + bl trap_reloc + addi r7, r7, 0x100 /* next exception vector */ + cmplw 0, r7, r8 + blt 3b + + li r7, .L_SingleStep - _start + EXC_OFF_SYS_RESET + li r8, _end_of_vectors - _start + EXC_OFF_SYS_RESET +4: + bl trap_reloc + addi r7, r7, 0x100 /* next exception vector */ + cmplw 0, r7, r8 + blt 4b + + /* enable execptions from RAM vectors */ + mfmsr r7 + li r8,MSR_IP + andc r7,r7,r8 + ori r7,r7,MSR_ME /* Enable Machine Check */ + mtmsr r7 + + mtlr r4 /* restore link register */ + blr + +.globl enable_ext_addr +enable_ext_addr: + mfspr r0, HID0 + lis r0, (HID0_HIGH_BAT_EN | HID0_XBSEN | HID0_XAEN)@h + ori r0, r0, (HID0_HIGH_BAT_EN | HID0_XBSEN | HID0_XAEN)@l + mtspr HID0, r0 + sync + isync + blr + +#if (CONFIG_SYS_CCSRBAR_DEFAULT != CONFIG_SYS_CCSRBAR) +.globl setup_ccsrbar +setup_ccsrbar: + /* Special sequence needed to update CCSRBAR itself */ + lis r4, CONFIG_SYS_CCSRBAR_DEFAULT@h + ori r4, r4, CONFIG_SYS_CCSRBAR_DEFAULT@l + + lis r5, CONFIG_SYS_CCSRBAR_PHYS_LOW@h + ori r5, r5, CONFIG_SYS_CCSRBAR_PHYS_LOW@l + srwi r5,r5,12 + li r6, CONFIG_SYS_CCSRBAR_PHYS_HIGH@l + rlwimi r5,r6,20,8,11 + stw r5, 0(r4) /* Store physical value of CCSR */ + isync + + lis r5, TEXT_BASE@h + ori r5,r5,TEXT_BASE@l + lwz r5, 0(r5) + isync + + /* Use VA of CCSR to do read */ + lis r3, CONFIG_SYS_CCSRBAR@h + lwz r5, CONFIG_SYS_CCSRBAR@l(r3) + isync + + blr +#endif + +#ifdef CONFIG_SYS_INIT_RAM_LOCK +lock_ram_in_cache: + /* Allocate Initial RAM in data cache. + */ + lis r3, (CONFIG_SYS_INIT_RAM_ADDR & ~31)@h + ori r3, r3, (CONFIG_SYS_INIT_RAM_ADDR & ~31)@l + li r4, ((CONFIG_SYS_INIT_RAM_END & ~31) + \ + (CONFIG_SYS_INIT_RAM_ADDR & 31) + 31) / 32 + mtctr r4 +1: + dcbz r0, r3 + addi r3, r3, 32 + bdnz 1b +#if 1 +/* Lock the data cache */ + mfspr r0, HID0 + ori r0, r0, 0x1000 + sync + mtspr HID0, r0 + sync + blr +#endif +#if 0 + /* Lock the first way of the data cache */ + mfspr r0, LDSTCR + ori r0, r0, 0x0080 +#if defined(CONFIG_ALTIVEC) + dssall +#endif + sync + mtspr LDSTCR, r0 + sync + isync + blr +#endif + +.globl unlock_ram_in_cache +unlock_ram_in_cache: + /* invalidate the INIT_RAM section */ + lis r3, (CONFIG_SYS_INIT_RAM_ADDR & ~31)@h + ori r3, r3, (CONFIG_SYS_INIT_RAM_ADDR & ~31)@l + li r4, ((CONFIG_SYS_INIT_RAM_END & ~31) + \ + (CONFIG_SYS_INIT_RAM_ADDR & 31) + 31) / 32 + mtctr r4 +1: icbi r0, r3 + addi r3, r3, 32 + bdnz 1b + sync /* Wait for all icbi to complete on bus */ + isync +#if 1 +/* Unlock the data cache and invalidate it */ + mfspr r0, HID0 + li r3,0x1000 + andc r0,r0,r3 + li r3,0x0400 + or r0,r0,r3 + sync + mtspr HID0, r0 + sync + blr +#endif +#if 0 + /* Unlock the first way of the data cache */ + mfspr r0, LDSTCR + li r3,0x0080 + andc r0,r0,r3 +#ifdef CONFIG_ALTIVEC + dssall +#endif + sync + mtspr LDSTCR, r0 + sync + isync + li r3,0x0400 + or r0,r0,r3 + sync + mtspr HID0, r0 + sync + blr +#endif +#endif diff --git a/arch/powerpc/cpu/mpc86xx/traps.c b/arch/powerpc/cpu/mpc86xx/traps.c new file mode 100644 index 0000000..406403e --- /dev/null +++ b/arch/powerpc/cpu/mpc86xx/traps.c @@ -0,0 +1,233 @@ +/* + * Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org) + * + * Modified by Cort Dougan (cort@cs.nmt.edu) + * and Paul Mackerras (paulus@cs.anu.edu.au) + * + * (C) Copyright 2000 + * Wolfgang Denk, DENX Software Engineering, wd@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 + */ + +/* + * This file handles the architecture-dependent parts of hardware exceptions + */ + +#include <common.h> +#include <command.h> +#include <kgdb.h> +#include <asm/processor.h> + +DECLARE_GLOBAL_DATA_PTR; + +/* Returns 0 if exception not found and fixup otherwise. */ +extern unsigned long search_exception_table(unsigned long); + +/* + * End of addressable memory. This may be less than the actual + * amount of memory on the system if we're unable to keep all + * the memory mapped in. + */ +extern ulong get_effective_memsize(void); +#define END_OF_MEM (gd->bd->bi_memstart + get_effective_memsize()) + +/* + * Trap & Exception support + */ + +void +print_backtrace(unsigned long *sp) +{ + int cnt = 0; + unsigned long i; + + printf("Call backtrace: "); + while (sp) { + if ((uint) sp > END_OF_MEM) + break; + + i = sp[1]; + if (cnt++ % 7 == 0) + printf("\n"); + printf("%08lX ", i); + if (cnt > 32) + break; + sp = (unsigned long *)*sp; + } + printf("\n"); +} + +void +show_regs(struct pt_regs *regs) +{ + int i; + + printf("NIP: %08lX XER: %08lX LR: %08lX REGS:" + " %p TRAP: %04lx DAR: %08lX\n", + regs->nip, regs->xer, regs->link, regs, regs->trap, regs->dar); + printf("MSR: %08lx EE: %01x PR: %01x FP:" + " %01x ME: %01x IR/DR: %01x%01x\n", + regs->msr, regs->msr & MSR_EE ? 1 : 0, + regs->msr & MSR_PR ? 1 : 0, regs->msr & MSR_FP ? 1 : 0, + regs->msr & MSR_ME ? 1 : 0, regs->msr & MSR_IR ? 1 : 0, + regs->msr & MSR_DR ? 1 : 0); + + printf("\n"); + for (i = 0; i < 32; i++) { + if ((i % 8) == 0) { + printf("GPR%02d: ", i); + } + + printf("%08lX ", regs->gpr[i]); + if ((i % 8) == 7) { + printf("\n"); + } + } +} + + +void +_exception(int signr, struct pt_regs *regs) +{ + show_regs(regs); + print_backtrace((unsigned long *)regs->gpr[1]); + panic("Exception in kernel pc %lx signal %d", regs->nip, signr); +} + +void +MachineCheckException(struct pt_regs *regs) +{ + unsigned long fixup; + + /* Probing PCI using config cycles cause this exception + * when a device is not present. Catch it and return to + * the PCI exception handler. + */ + if ((fixup = search_exception_table(regs->nip)) != 0) { + regs->nip = fixup; + return; + } + +#if defined(CONFIG_CMD_KGDB) + if (debugger_exception_handler && (*debugger_exception_handler) (regs)) + return; +#endif + + printf("Machine check in kernel mode.\n"); + printf("Caused by (from msr): "); + printf("regs %p ", regs); + switch ( regs->msr & 0x001F0000) { + case (0x80000000>>11): + printf("MSS error. MSSSR0: %08x\n", mfspr(SPRN_MSSSR0)); + break; + case (0x80000000>>12): + printf("Machine check signal - probably due to mm fault\n" + "with mmu off\n"); + break; + case (0x80000000 >> 13): + printf("Transfer error ack signal\n"); + break; + case (0x80000000 >> 14): + printf("Data parity signal\n"); + break; + case (0x80000000 >> 15): + printf("Address parity signal\n"); + break; + default: + printf("Unknown values in msr\n"); + } + show_regs(regs); + print_backtrace((unsigned long *)regs->gpr[1]); + panic("machine check"); +} + +void +AlignmentException(struct pt_regs *regs) +{ +#if defined(CONFIG_CMD_KGDB) + if (debugger_exception_handler && (*debugger_exception_handler) (regs)) + return; +#endif + show_regs(regs); + print_backtrace((unsigned long *)regs->gpr[1]); + panic("Alignment Exception"); +} + +void +ProgramCheckException(struct pt_regs *regs) +{ + unsigned char *p = regs ? (unsigned char *)(regs->nip) : NULL; + int i, j; + +#if defined(CONFIG_CMD_KGDB) + if (debugger_exception_handler && (*debugger_exception_handler) (regs)) + return; +#endif + show_regs(regs); + + p = (unsigned char *)((unsigned long)p & 0xFFFFFFE0); + p -= 32; + for (i = 0; i < 256; i += 16) { + printf("%08x: ", (unsigned int)p + i); + for (j = 0; j < 16; j++) { + printf("%02x ", p[i + j]); + } + printf("\n"); + } + + print_backtrace((unsigned long *)regs->gpr[1]); + panic("Program Check Exception"); +} + +void +SoftEmuException(struct pt_regs *regs) +{ +#if defined(CONFIG_CMD_KGDB) + if (debugger_exception_handler && (*debugger_exception_handler) (regs)) + return; +#endif + show_regs(regs); + print_backtrace((unsigned long *)regs->gpr[1]); + panic("Software Emulation Exception"); +} + +void +UnknownException(struct pt_regs *regs) +{ +#if defined(CONFIG_CMD_KGDB) + if (debugger_exception_handler && (*debugger_exception_handler) (regs)) + return; +#endif + printf("UnknownException regs@%lx\n", (ulong)regs); + printf("Bad trap at PC: %lx, SR: %lx, vector=%lx\n", + regs->nip, regs->msr, regs->trap); + _exception(0, regs); +} + +/* + * Probe an address by reading. + * If not present, return -1, + * otherwise return 0. + */ +int +addr_probe(uint *addr) +{ + return 0; +} diff --git a/arch/powerpc/cpu/mpc8xx/Makefile b/arch/powerpc/cpu/mpc8xx/Makefile new file mode 100644 index 0000000..5f70459 --- /dev/null +++ b/arch/powerpc/cpu/mpc8xx/Makefile @@ -0,0 +1,66 @@ +# +# (C) Copyright 2000-2006 +# Wolfgang Denk, DENX Software Engineering, wd@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 $(TOPDIR)/config.mk + +# CFLAGS += -DET_DEBUG + +LIB = $(obj)lib$(CPU).a + +START-y += start.o +START-y += kgdb.o +COBJS-y += bedbug_860.o +COBJS-y += commproc.o +COBJS-y += cpu.o +COBJS-y += cpu_init.o +COBJS-y += fec.o +COBJS-$(CONFIG_OF_LIBFDT) += fdt.o +COBJS-y += i2c.o +COBJS-y += interrupts.o +COBJS-y += lcd.o +COBJS-y += scc.o +COBJS-y += serial.o +COBJS-y += speed.o +COBJS-y += spi.o +COBJS-y += traps.o +COBJS-y += upatch.o +COBJS-y += video.o +SOBJS-y += plprcr_write.o + +SRCS := $(START-y:.o=.S) $(SOBJS-y:.o=.S) $(COBJS-y:.o=.c) +OBJS := $(addprefix $(obj),$(SOBJS-y) $(COBJS-y)) +START := $(addprefix $(obj),$(START-y)) + +all: $(obj).depend $(START) $(LIB) + +$(LIB): $(OBJS) + $(AR) $(ARFLAGS) $@ $(OBJS) $(obj)kgdb.o + +######################################################################### + +# defines $(obj).depend target +include $(SRCTREE)/rules.mk + +sinclude $(obj).depend + +######################################################################### diff --git a/arch/powerpc/cpu/mpc8xx/bedbug_860.c b/arch/powerpc/cpu/mpc8xx/bedbug_860.c new file mode 100644 index 0000000..0308bbb --- /dev/null +++ b/arch/powerpc/cpu/mpc8xx/bedbug_860.c @@ -0,0 +1,316 @@ +/* + * Bedbug Functions specific to the MPC860 chip + */ + +#include <common.h> +#include <command.h> +#include <linux/ctype.h> +#include <bedbug/bedbug.h> +#include <bedbug/regs.h> +#include <bedbug/ppc.h> +#include <bedbug/type.h> + +#if defined(CONFIG_CMD_BEDBUG) && defined(CONFIG_8xx) + +#define MAX_BREAK_POINTS 2 + +extern CPU_DEBUG_CTX bug_ctx; + +void bedbug860_init __P((void)); +void bedbug860_do_break __P((cmd_tbl_t*,int,int,char*[])); +void bedbug860_break_isr __P((struct pt_regs*)); +int bedbug860_find_empty __P((void)); +int bedbug860_set __P((int,unsigned long)); +int bedbug860_clear __P((int)); + + +/* ====================================================================== + * Initialize the global bug_ctx structure for the MPC860. Clear all + * of the breakpoints. + * ====================================================================== */ + +void bedbug860_init( void ) +{ + int i; + /* -------------------------------------------------- */ + + bug_ctx.hw_debug_enabled = 0; + bug_ctx.stopped = 0; + bug_ctx.current_bp = 0; + bug_ctx.regs = NULL; + + bug_ctx.do_break = bedbug860_do_break; + bug_ctx.break_isr = bedbug860_break_isr; + bug_ctx.find_empty = bedbug860_find_empty; + bug_ctx.set = bedbug860_set; + bug_ctx.clear = bedbug860_clear; + + for( i = 1; i <= MAX_BREAK_POINTS; ++i ) + (*bug_ctx.clear)( i ); + + puts ("BEDBUG:ready\n"); + return; +} /* bedbug_init_breakpoints */ + + + +/* ====================================================================== + * Set/clear/show one of the hardware breakpoints for the 860. The "off" + * string will disable a specific breakpoint. The "show" string will + * display the current breakpoints. Otherwise an address will set a + * breakpoint at that address. Setting a breakpoint uses the CPU-specific + * set routine which will assign a breakpoint number. + * ====================================================================== */ + +void bedbug860_do_break (cmd_tbl_t *cmdtp, int flag, int argc, + char *argv[]) +{ + long addr = 0; /* Address to break at */ + int which_bp; /* Breakpoint number */ + /* -------------------------------------------------- */ + + if (argc < 2) + { + cmd_usage(cmdtp); + return; + } + + /* Turn off a breakpoint */ + + if( strcmp( argv[ 1 ], "off" ) == 0 ) + { + if( bug_ctx.hw_debug_enabled == 0 ) + { + printf( "No breakpoints enabled\n" ); + return; + } + + which_bp = simple_strtoul( argv[ 2 ], NULL, 10 ); + + if( bug_ctx.clear ) + (*bug_ctx.clear)( which_bp ); + + printf( "Breakpoint %d removed\n", which_bp ); + return; + } + + /* Show a list of breakpoints */ + + if( strcmp( argv[ 1 ], "show" ) == 0 ) + { + for( which_bp = 1; which_bp <= MAX_BREAK_POINTS; ++which_bp ) + { + + switch( which_bp ) + { + case 1: addr = GET_CMPA(); break; + case 2: addr = GET_CMPB(); break; + case 3: addr = GET_CMPC(); break; + case 4: addr = GET_CMPD(); break; + } + + printf( "Breakpoint [%d]: ", which_bp ); + if( addr == 0 ) + printf( "NOT SET\n" ); + else + disppc( (unsigned char *)addr, 0, 1, bedbug_puts, F_RADHEX ); + } + return; + } + + /* Set a breakpoint at the address */ + + if( !isdigit( argv[ 1 ][ 0 ])) + { + cmd_usage(cmdtp); + return; + } + + addr = simple_strtoul( argv[ 1 ], NULL, 16 ) & 0xfffffffc; + + if(( bug_ctx.set ) && ( which_bp = (*bug_ctx.set)( 0, addr )) > 0 ) + { + printf( "Breakpoint [%d]: ", which_bp ); + disppc( (unsigned char *)addr, 0, 1, bedbug_puts, F_RADHEX ); + } + + return; +} /* bedbug860_do_break */ + + + +/* ====================================================================== + * Handle a breakpoint. First determine which breakpoint was hit by + * looking at the DeBug Status Register (DBSR), clear the breakpoint + * and enter a mini main loop. Stay in the loop until the stopped flag + * in the debug context is cleared. + * ====================================================================== */ + +void bedbug860_break_isr( struct pt_regs *regs ) +{ + unsigned long addr; /* Address stopped at */ + unsigned long cause; /* Address stopped at */ + /* -------------------------------------------------- */ + + cause = GET_ICR(); + + if( !(cause & 0x00000004)) { + printf( "Not an instruction breakpoint (ICR 0x%08lx)\n", cause ); + return; + } + + addr = regs->nip; + + if( addr == GET_CMPA() ) + { + bug_ctx.current_bp = 1; + } + else if( addr == GET_CMPB() ) + { + bug_ctx.current_bp = 2; + } + else if( addr == GET_CMPC() ) + { + bug_ctx.current_bp = 3; + } + else if( addr == GET_CMPD() ) + { + bug_ctx.current_bp = 4; + } + + bedbug_main_loop( addr, regs ); + return; +} /* bedbug860_break_isr */ + + + +/* ====================================================================== + * Look through all of the hardware breakpoints available to see if one + * is unused. + * ====================================================================== */ + +int bedbug860_find_empty( void ) +{ + /* -------------------------------------------------- */ + + if( GET_CMPA() == 0 ) + return 1; + + if( GET_CMPB() == 0 ) + return 2; + + if( GET_CMPC() == 0 ) + return 3; + + if( GET_CMPD() == 0 ) + return 4; + + return 0; +} /* bedbug860_find_empty */ + + + +/* ====================================================================== + * Set a breakpoint. If 'which_bp' is zero then find an unused breakpoint + * number, otherwise reassign the given breakpoint. If hardware debugging + * is not enabled, then turn it on via the MSR and DBCR0. Set the break + * address in the appropriate IACx register and enable proper address + * beakpoint in DBCR0. + * ====================================================================== */ + +int bedbug860_set( int which_bp, unsigned long addr ) +{ + /* -------------------------------------------------- */ + + /* Only look if which_bp == 0, else use which_bp */ + if(( bug_ctx.find_empty ) && ( !which_bp ) && + ( which_bp = (*bug_ctx.find_empty)()) == 0 ) + { + printf( "All breakpoints in use\n" ); + return 0; + } + + if( which_bp < 1 || which_bp > MAX_BREAK_POINTS ) + { + printf( "Invalid break point # %d\n", which_bp ); + return 0; + } + + if( ! bug_ctx.hw_debug_enabled ) + { + bug_ctx.hw_debug_enabled = 1; + SET_DER( GET_DER() | 0x00000004 ); + } + + switch( which_bp ) + { + case 1: + SET_CMPA( addr ); + SET_ICTRL( GET_ICTRL() | 0x80080800 ); /* CTA=Equal,IW0=Match A,SIW0EN */ + break; + + case 2: + SET_CMPB( addr ); + SET_ICTRL( GET_ICTRL() | 0x10020400 ); /* CTB=Equal,IW1=Match B,SIW1EN */ + break; + + case 3: + SET_CMPC( addr ); + SET_ICTRL( GET_ICTRL() | 0x02008200 ); /* CTC=Equal,IW2=Match C,SIW2EN */ + break; + + case 4: + SET_CMPD( addr ); + SET_ICTRL( GET_ICTRL() | 0x00404100 ); /* CTD=Equal,IW3=Match D,SIW3EN */ + break; + } + + return which_bp; +} /* bedbug860_set */ + + + +/* ====================================================================== + * Disable a specific breakoint by setting the appropriate IACx register + * to zero and claring the instruction address breakpoint in DBCR0. + * ====================================================================== */ + +int bedbug860_clear( int which_bp ) +{ + /* -------------------------------------------------- */ + + if( which_bp < 1 || which_bp > MAX_BREAK_POINTS ) + { + printf( "Invalid break point # (%d)\n", which_bp ); + return -1; + } + + switch( which_bp ) + { + case 1: + SET_CMPA( 0 ); + SET_ICTRL( GET_ICTRL() & ~0x80080800 ); /* CTA=Equal,IW0=Match A,SIW0EN */ + break; + + case 2: + SET_CMPB( 0 ); + SET_ICTRL( GET_ICTRL() & ~0x10020400 ); /* CTB=Equal,IW1=Match B,SIW1EN */ + break; + + case 3: + SET_CMPC( 0 ); + SET_ICTRL( GET_ICTRL() & ~0x02008200 ); /* CTC=Equal,IW2=Match C,SIW2EN */ + break; + + case 4: + SET_CMPD( 0 ); + SET_ICTRL( GET_ICTRL() & ~0x00404100 ); /* CTD=Equal,IW3=Match D,SIW3EN */ + break; + } + + return 0; +} /* bedbug860_clear */ + + +/* ====================================================================== */ +#endif diff --git a/arch/powerpc/cpu/mpc8xx/commproc.c b/arch/powerpc/cpu/mpc8xx/commproc.c new file mode 100644 index 0000000..a87a0dc --- /dev/null +++ b/arch/powerpc/cpu/mpc8xx/commproc.c @@ -0,0 +1,131 @@ +/* + * (C) Copyright 2000-2002 + * Wolfgang Denk, DENX Software Engineering, wd@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> +#include <commproc.h> + +DECLARE_GLOBAL_DATA_PTR; + +#ifdef CONFIG_SYS_ALLOC_DPRAM + +int dpram_init (void) +{ + /* Reclaim the DP memory for our use. */ + gd->dp_alloc_base = CPM_DATAONLY_BASE; + gd->dp_alloc_top = CPM_DATAONLY_BASE + CPM_DATAONLY_SIZE; + + return (0); +} + +/* Allocate some memory from the dual ported ram. We may want to + * enforce alignment restrictions, but right now everyone is a good + * citizen. + */ +uint dpram_alloc (uint size) +{ + uint addr = gd->dp_alloc_base; + + if ((gd->dp_alloc_base + size) >= gd->dp_alloc_top) + return (CPM_DP_NOSPACE); + + gd->dp_alloc_base += size; + + return addr; +} + +uint dpram_base (void) +{ + return gd->dp_alloc_base; +} + +/* Allocate some memory from the dual ported ram. We may want to + * enforce alignment restrictions, but right now everyone is a good + * citizen. + */ +uint dpram_alloc_align (uint size, uint align) +{ + uint addr, mask = align - 1; + + addr = (gd->dp_alloc_base + mask) & ~mask; + + if ((addr + size) >= gd->dp_alloc_top) + return (CPM_DP_NOSPACE); + + gd->dp_alloc_base = addr + size; + + return addr; +} + +uint dpram_base_align (uint align) +{ + uint mask = align - 1; + + return (gd->dp_alloc_base + mask) & ~mask; +} +#endif /* CONFIG_SYS_ALLOC_DPRAM */ + +#if defined(CONFIG_POST) || defined(CONFIG_LOGBUFFER) + +void post_word_store (ulong a) +{ + volatile void *save_addr = + ((immap_t *) CONFIG_SYS_IMMR)->im_cpm.cp_dpmem + CPM_POST_WORD_ADDR; + + *(volatile ulong *) save_addr = a; +} + +ulong post_word_load (void) +{ + volatile void *save_addr = + ((immap_t *) CONFIG_SYS_IMMR)->im_cpm.cp_dpmem + CPM_POST_WORD_ADDR; + + return *(volatile ulong *) save_addr; +} + +#endif /* CONFIG_POST || CONFIG_LOGBUFFER*/ + +#ifdef CONFIG_BOOTCOUNT_LIMIT + +void bootcount_store (ulong a) +{ + volatile ulong *save_addr = + (volatile ulong *)( ((immap_t *) CONFIG_SYS_IMMR)->im_cpm.cp_dpmem + + CPM_BOOTCOUNT_ADDR ); + + save_addr[0] = a; + save_addr[1] = BOOTCOUNT_MAGIC; +} + +ulong bootcount_load (void) +{ + volatile ulong *save_addr = + (volatile ulong *)( ((immap_t *) CONFIG_SYS_IMMR)->im_cpm.cp_dpmem + + CPM_BOOTCOUNT_ADDR ); + + if (save_addr[1] != BOOTCOUNT_MAGIC) + return 0; + else + return save_addr[0]; +} + +#endif /* CONFIG_BOOTCOUNT_LIMIT */ diff --git a/arch/powerpc/cpu/mpc8xx/config.mk b/arch/powerpc/cpu/mpc8xx/config.mk new file mode 100644 index 0000000..5540d65 --- /dev/null +++ b/arch/powerpc/cpu/mpc8xx/config.mk @@ -0,0 +1,26 @@ +# +# (C) Copyright 2000 +# Wolfgang Denk, DENX Software Engineering, wd@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 +# + +PLATFORM_RELFLAGS += -fPIC -meabi + +PLATFORM_CPPFLAGS += -DCONFIG_8xx -ffixed-r2 -mstring -mcpu=860 -msoft-float diff --git a/arch/powerpc/cpu/mpc8xx/cpu.c b/arch/powerpc/cpu/mpc8xx/cpu.c new file mode 100644 index 0000000..2eb848b --- /dev/null +++ b/arch/powerpc/cpu/mpc8xx/cpu.c @@ -0,0 +1,654 @@ +/* + * (C) Copyright 2000-2002 + * Wolfgang Denk, DENX Software Engineering, wd@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 + */ + +/* + * m8xx.c + * + * CPU specific code + * + * written or collected and sometimes rewritten by + * Magnus Damm <damm@bitsmart.com> + * + * minor modifications by + * Wolfgang Denk <wd@denx.de> + */ + +#include <common.h> +#include <watchdog.h> +#include <command.h> +#include <mpc8xx.h> +#include <commproc.h> +#include <netdev.h> +#include <asm/cache.h> + +#if defined(CONFIG_OF_LIBFDT) +#include <libfdt.h> +#include <libfdt_env.h> +#include <fdt_support.h> +#endif + +DECLARE_GLOBAL_DATA_PTR; + +static char *cpu_warning = "\n " \ + "*** Warning: CPU Core has Silicon Bugs -- Check the Errata ***"; + +#if ((defined(CONFIG_MPC86x) || defined(CONFIG_MPC855)) && \ + !defined(CONFIG_MPC862)) + +static int check_CPU (long clock, uint pvr, uint immr) +{ + char *id_str = +# if defined(CONFIG_MPC855) + "PC855"; +# elif defined(CONFIG_MPC860P) + "PC860P"; +# else + NULL; +# endif + volatile immap_t *immap = (immap_t *) (immr & 0xFFFF0000); + uint k, m; + char buf[32]; + char pre = 'X'; + char *mid = "xx"; + char *suf; + + /* the highest 16 bits should be 0x0050 for a 860 */ + + if ((pvr >> 16) != 0x0050) + return -1; + + k = (immr << 16) | *((ushort *) & immap->im_cpm.cp_dparam[0xB0]); + m = 0; + suf = ""; + + /* + * Some boards use sockets so different CPUs can be used. + * We have to check chip version in run time. + */ + switch (k) { + case 0x00020001: pre = 'P'; break; + case 0x00030001: break; + case 0x00120003: suf = "A"; break; + case 0x00130003: suf = "A3"; break; + + case 0x00200004: suf = "B"; break; + + case 0x00300004: suf = "C"; break; + case 0x00310004: suf = "C1"; m = 1; break; + + case 0x00200064: mid = "SR"; suf = "B"; break; + case 0x00300065: mid = "SR"; suf = "C"; break; + case 0x00310065: mid = "SR"; suf = "C1"; m = 1; break; + case 0x05010000: suf = "D3"; m = 1; break; + case 0x05020000: suf = "D4"; m = 1; break; + /* this value is not documented anywhere */ + case 0x40000000: pre = 'P'; suf = "D"; m = 1; break; + /* MPC866P/MPC866T/MPC859T/MPC859DSL/MPC852T */ + case 0x08010004: /* Rev. A.0 */ + suf = "A"; + /* fall through */ + case 0x08000003: /* Rev. 0.3 */ + pre = 'M'; m = 1; + if (id_str == NULL) + id_str = +# if defined(CONFIG_MPC852T) + "PC852T"; +# elif defined(CONFIG_MPC859T) + "PC859T"; +# elif defined(CONFIG_MPC859DSL) + "PC859DSL"; +# elif defined(CONFIG_MPC866T) + "PC866T"; +# else + "PC866x"; /* Unknown chip from MPC866 family */ +# endif + break; + case 0x09000000: pre = 'M'; mid = suf = ""; m = 1; + if (id_str == NULL) + id_str = "PC885"; /* 870/875/880/885 */ + break; + + default: suf = NULL; break; + } + + if (id_str == NULL) + id_str = "PC86x"; /* Unknown 86x chip */ + if (suf) + printf ("%c%s%sZPnn%s", pre, id_str, mid, suf); + else + printf ("unknown M%s (0x%08x)", id_str, k); + + +#if defined(CONFIG_SYS_8xx_CPUCLK_MIN) && defined(CONFIG_SYS_8xx_CPUCLK_MAX) + printf (" at %s MHz [%d.%d...%d.%d MHz]\n ", + strmhz (buf, clock), + CONFIG_SYS_8xx_CPUCLK_MIN / 1000000, + ((CONFIG_SYS_8xx_CPUCLK_MIN % 1000000) + 50000) / 100000, + CONFIG_SYS_8xx_CPUCLK_MAX / 1000000, + ((CONFIG_SYS_8xx_CPUCLK_MAX % 1000000) + 50000) / 100000 + ); +#else + printf (" at %s MHz: ", strmhz (buf, clock)); +#endif + printf ("%u kB I-Cache %u kB D-Cache", + checkicache () >> 10, + checkdcache () >> 10 + ); + + /* do we have a FEC (860T/P or 852/859/866/885)? */ + + immap->im_cpm.cp_fec.fec_addr_low = 0x12345678; + if (immap->im_cpm.cp_fec.fec_addr_low == 0x12345678) { + printf (" FEC present"); + } + + if (!m) { + puts (cpu_warning); + } + + putc ('\n'); + +#ifdef DEBUG + if(clock != measure_gclk()) { + printf ("clock %ldHz != %dHz\n", clock, measure_gclk()); + } +#endif + + return 0; +} + +#elif defined(CONFIG_MPC862) + +static int check_CPU (long clock, uint pvr, uint immr) +{ + volatile immap_t *immap = (immap_t *) (immr & 0xFFFF0000); + uint k, m; + char buf[32]; + char pre = 'X'; + char *mid = "xx"; + char *suf; + + /* the highest 16 bits should be 0x0050 for a 8xx */ + + if ((pvr >> 16) != 0x0050) + return -1; + + k = (immr << 16) | *((ushort *) & immap->im_cpm.cp_dparam[0xB0]); + m = 0; + + switch (k) { + + /* this value is not documented anywhere */ + case 0x06000000: mid = "P"; suf = "0"; break; + case 0x06010001: mid = "P"; suf = "A"; m = 1; break; + case 0x07000003: mid = "P"; suf = "B"; m = 1; break; + default: suf = NULL; break; + } + +#ifndef CONFIG_MPC857 + if (suf) + printf ("%cPC862%sZPnn%s", pre, mid, suf); + else + printf ("unknown MPC862 (0x%08x)", k); +#else + if (suf) + printf ("%cPC857TZPnn%s", pre, suf); /* only 857T tested right now! */ + else + printf ("unknown MPC857 (0x%08x)", k); +#endif + + printf (" at %s MHz:", strmhz (buf, clock)); + + printf (" %u kB I-Cache", checkicache () >> 10); + printf (" %u kB D-Cache", checkdcache () >> 10); + + /* lets check and see if we're running on a 862T (or P?) */ + + immap->im_cpm.cp_fec.fec_addr_low = 0x12345678; + if (immap->im_cpm.cp_fec.fec_addr_low == 0x12345678) { + printf (" FEC present"); + } + + if (!m) { + puts (cpu_warning); + } + + putc ('\n'); + + return 0; +} + +#elif defined(CONFIG_MPC823) + +static int check_CPU (long clock, uint pvr, uint immr) +{ + volatile immap_t *immap = (immap_t *) (immr & 0xFFFF0000); + uint k, m; + char buf[32]; + char *suf; + + /* the highest 16 bits should be 0x0050 for a 8xx */ + + if ((pvr >> 16) != 0x0050) + return -1; + + k = (immr << 16) | *((ushort *) & immap->im_cpm.cp_dparam[0xB0]); + m = 0; + + switch (k) { + /* MPC823 */ + case 0x20000000: suf = "0"; break; + case 0x20010000: suf = "0.1"; break; + case 0x20020000: suf = "Z2/3"; break; + case 0x20020001: suf = "Z3"; break; + case 0x21000000: suf = "A"; break; + case 0x21010000: suf = "B"; m = 1; break; + case 0x21010001: suf = "B2"; m = 1; break; + /* MPC823E */ + case 0x24010000: suf = NULL; + puts ("PPC823EZTnnB2"); + m = 1; + break; + default: + suf = NULL; + printf ("unknown MPC823 (0x%08x)", k); + break; + } + if (suf) + printf ("PPC823ZTnn%s", suf); + + printf (" at %s MHz:", strmhz (buf, clock)); + + printf (" %u kB I-Cache", checkicache () >> 10); + printf (" %u kB D-Cache", checkdcache () >> 10); + + /* lets check and see if we're running on a 860T (or P?) */ + + immap->im_cpm.cp_fec.fec_addr_low = 0x12345678; + if (immap->im_cpm.cp_fec.fec_addr_low == 0x12345678) { + puts (" FEC present"); + } + + if (!m) { + puts (cpu_warning); + } + + putc ('\n'); + + return 0; +} + +#elif defined(CONFIG_MPC850) + +static int check_CPU (long clock, uint pvr, uint immr) +{ + volatile immap_t *immap = (immap_t *) (immr & 0xFFFF0000); + uint k, m; + char buf[32]; + + /* the highest 16 bits should be 0x0050 for a 8xx */ + + if ((pvr >> 16) != 0x0050) + return -1; + + k = (immr << 16) | *((ushort *) & immap->im_cpm.cp_dparam[0xB0]); + m = 0; + + switch (k) { + case 0x20020001: + printf ("XPC850xxZT"); + break; + case 0x21000065: + printf ("XPC850xxZTA"); + break; + case 0x21010067: + printf ("XPC850xxZTB"); + m = 1; + break; + case 0x21020068: + printf ("XPC850xxZTC"); + m = 1; + break; + default: + printf ("unknown MPC850 (0x%08x)", k); + } + printf (" at %s MHz:", strmhz (buf, clock)); + + printf (" %u kB I-Cache", checkicache () >> 10); + printf (" %u kB D-Cache", checkdcache () >> 10); + + /* lets check and see if we're running on a 850T (or P?) */ + + immap->im_cpm.cp_fec.fec_addr_low = 0x12345678; + if (immap->im_cpm.cp_fec.fec_addr_low == 0x12345678) { + printf (" FEC present"); + } + + if (!m) { + puts (cpu_warning); + } + + putc ('\n'); + + return 0; +} +#else +#error CPU undefined +#endif +/* ------------------------------------------------------------------------- */ + +int checkcpu (void) +{ + ulong clock = gd->cpu_clk; + uint immr = get_immr (0); /* Return full IMMR contents */ + uint pvr = get_pvr (); + + puts ("CPU: "); + + /* 850 has PARTNUM 20 */ + /* 801 has PARTNUM 10 */ + return check_CPU (clock, pvr, immr); +} + +/* ------------------------------------------------------------------------- */ +/* L1 i-cache */ +/* the standard 860 has 128 sets of 16 bytes in 2 ways (= 4 kB) */ +/* the 860 P (plus) has 256 sets of 16 bytes in 4 ways (= 16 kB) */ + +int checkicache (void) +{ + volatile immap_t *immap = (immap_t *) CONFIG_SYS_IMMR; + volatile memctl8xx_t *memctl = &immap->im_memctl; + u32 cacheon = rd_ic_cst () & IDC_ENABLED; + +#ifdef CONFIG_IP86x + u32 k = memctl->memc_br1 & ~0x00007fff; /* probe in flash memoryarea */ +#else + u32 k = memctl->memc_br0 & ~0x00007fff; /* probe in flash memoryarea */ +#endif + u32 m; + u32 lines = -1; + + wr_ic_cst (IDC_UNALL); + wr_ic_cst (IDC_INVALL); + wr_ic_cst (IDC_DISABLE); + __asm__ volatile ("isync"); + + while (!((m = rd_ic_cst ()) & IDC_CERR2)) { + wr_ic_adr (k); + wr_ic_cst (IDC_LDLCK); + __asm__ volatile ("isync"); + + lines++; + k += 0x10; /* the number of bytes in a cacheline */ + } + + wr_ic_cst (IDC_UNALL); + wr_ic_cst (IDC_INVALL); + + if (cacheon) + wr_ic_cst (IDC_ENABLE); + else + wr_ic_cst (IDC_DISABLE); + + __asm__ volatile ("isync"); + + return lines << 4; +}; + +/* ------------------------------------------------------------------------- */ +/* L1 d-cache */ +/* the standard 860 has 128 sets of 16 bytes in 2 ways (= 4 kB) */ +/* the 860 P (plus) has 256 sets of 16 bytes in 2 ways (= 8 kB) */ +/* call with cache disabled */ + +int checkdcache (void) +{ + volatile immap_t *immap = (immap_t *) CONFIG_SYS_IMMR; + volatile memctl8xx_t *memctl = &immap->im_memctl; + u32 cacheon = rd_dc_cst () & IDC_ENABLED; + +#ifdef CONFIG_IP86x + u32 k = memctl->memc_br1 & ~0x00007fff; /* probe in flash memoryarea */ +#else + u32 k = memctl->memc_br0 & ~0x00007fff; /* probe in flash memoryarea */ +#endif + u32 m; + u32 lines = -1; + + wr_dc_cst (IDC_UNALL); + wr_dc_cst (IDC_INVALL); + wr_dc_cst (IDC_DISABLE); + + while (!((m = rd_dc_cst ()) & IDC_CERR2)) { + wr_dc_adr (k); + wr_dc_cst (IDC_LDLCK); + lines++; + k += 0x10; /* the number of bytes in a cacheline */ + } + + wr_dc_cst (IDC_UNALL); + wr_dc_cst (IDC_INVALL); + + if (cacheon) + wr_dc_cst (IDC_ENABLE); + else + wr_dc_cst (IDC_DISABLE); + + return lines << 4; +}; + +/* ------------------------------------------------------------------------- */ + +void upmconfig (uint upm, uint * table, uint size) +{ + uint i; + uint addr = 0; + volatile immap_t *immap = (immap_t *) CONFIG_SYS_IMMR; + volatile memctl8xx_t *memctl = &immap->im_memctl; + + for (i = 0; i < size; i++) { + memctl->memc_mdr = table[i]; /* (16-15) */ + memctl->memc_mcr = addr | upm; /* (16-16) */ + addr++; + } +} + +/* ------------------------------------------------------------------------- */ + +#ifndef CONFIG_LWMON + +int do_reset (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]) +{ + ulong msr, addr; + + volatile immap_t *immap = (immap_t *) CONFIG_SYS_IMMR; + + immap->im_clkrst.car_plprcr |= PLPRCR_CSR; /* Checkstop Reset enable */ + + /* Interrupts and MMU off */ + __asm__ volatile ("mtspr 81, 0"); + __asm__ volatile ("mfmsr %0":"=r" (msr)); + + msr &= ~0x1030; + __asm__ volatile ("mtmsr %0"::"r" (msr)); + + /* + * Trying to execute the next instruction at a non-existing address + * should cause a machine check, resulting in reset + */ +#ifdef CONFIG_SYS_RESET_ADDRESS + addr = CONFIG_SYS_RESET_ADDRESS; +#else + /* + * note: when CONFIG_SYS_MONITOR_BASE points to a RAM address, CONFIG_SYS_MONITOR_BASE + * - sizeof (ulong) is usually a valid address. Better pick an address + * known to be invalid on your system and assign it to CONFIG_SYS_RESET_ADDRESS. + * "(ulong)-1" used to be a good choice for many systems... + */ + addr = CONFIG_SYS_MONITOR_BASE - sizeof (ulong); +#endif + ((void (*)(void)) addr) (); + return 1; +} + +#else /* CONFIG_LWMON */ + +/* + * On the LWMON board, the MCLR reset input of the PIC's on the board + * uses a 47K/1n RC combination which has a 47us time constant. The + * low signal on the HRESET pin of the CPU is only 512 clocks = 8 us + * and thus too short to reset the external hardware. So we use the + * watchdog to reset the board. + */ +int do_reset (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]) +{ + /* prevent triggering the watchdog */ + disable_interrupts (); + + /* make sure the watchdog is running */ + reset_8xx_watchdog ((immap_t *) CONFIG_SYS_IMMR); + + /* wait for watchdog reset */ + while (1) {}; + + /* NOTREACHED */ + return 1; +} + +#endif /* CONFIG_LWMON */ + +/* ------------------------------------------------------------------------- */ + +/* + * Get timebase clock frequency (like cpu_clk in Hz) + * + * See sections 14.2 and 14.6 of the User's Manual + */ +unsigned long get_tbclk (void) +{ + uint immr = get_immr (0); /* Return full IMMR contents */ + volatile immap_t *immap = (volatile immap_t *)(immr & 0xFFFF0000); + ulong oscclk, factor, pll; + + if (immap->im_clkrst.car_sccr & SCCR_TBS) { + return (gd->cpu_clk / 16); + } + + pll = immap->im_clkrst.car_plprcr; + +#define PLPRCR_val(a) ((pll & PLPRCR_ ## a ## _MSK) >> PLPRCR_ ## a ## _SHIFT) + + /* + * For newer PQ1 chips (MPC866/87x/88x families), PLL multiplication + * factor is calculated as follows: + * + * MFN + * MFI + ------- + * MFD + 1 + * factor = ----------------- + * (PDF + 1) * 2^S + * + * For older chips, it's just MF field of PLPRCR plus one. + */ + if ((immr & 0x0FFF) >= MPC8xx_NEW_CLK) { /* MPC866/87x/88x series */ + factor = (PLPRCR_val(MFI) + PLPRCR_val(MFN)/(PLPRCR_val(MFD)+1))/ + (PLPRCR_val(PDF)+1) / (1<<PLPRCR_val(S)); + } else { + factor = PLPRCR_val(MF)+1; + } + + oscclk = gd->cpu_clk / factor; + + if ((immap->im_clkrst.car_sccr & SCCR_RTSEL) == 0 || factor > 2) { + return (oscclk / 4); + } + return (oscclk / 16); +} + +/* ------------------------------------------------------------------------- */ + +#if defined(CONFIG_WATCHDOG) +void watchdog_reset (void) +{ + int re_enable = disable_interrupts (); + + reset_8xx_watchdog ((immap_t *) CONFIG_SYS_IMMR); + if (re_enable) + enable_interrupts (); +} +#endif /* CONFIG_WATCHDOG */ + +#if defined(CONFIG_WATCHDOG) || defined(CONFIG_LWMON) + +void reset_8xx_watchdog (volatile immap_t * immr) +{ +# if defined(CONFIG_LWMON) + /* + * The LWMON board uses a MAX6301 Watchdog + * with the trigger pin connected to port PA.7 + * + * (The old board version used a MAX706TESA Watchdog, which + * had to be handled exactly the same.) + */ +# define WATCHDOG_BIT 0x0100 + immr->im_ioport.iop_papar &= ~(WATCHDOG_BIT); /* GPIO */ + immr->im_ioport.iop_padir |= WATCHDOG_BIT; /* Output */ + immr->im_ioport.iop_paodr &= ~(WATCHDOG_BIT); /* active output */ + + immr->im_ioport.iop_padat ^= WATCHDOG_BIT; /* Toggle WDI */ +# elif defined(CONFIG_KUP4K) || defined(CONFIG_KUP4X) + /* + * The KUP4 boards uses a TPS3705 Watchdog + * with the trigger pin connected to port PA.5 + */ +# define WATCHDOG_BIT 0x0400 + immr->im_ioport.iop_papar &= ~(WATCHDOG_BIT); /* GPIO */ + immr->im_ioport.iop_padir |= WATCHDOG_BIT; /* Output */ + immr->im_ioport.iop_paodr &= ~(WATCHDOG_BIT); /* active output */ + + immr->im_ioport.iop_padat ^= WATCHDOG_BIT; /* Toggle WDI */ +# else + /* + * All other boards use the MPC8xx Internal Watchdog + */ + immr->im_siu_conf.sc_swsr = 0x556c; /* write magic1 */ + immr->im_siu_conf.sc_swsr = 0xaa39; /* write magic2 */ +# endif /* CONFIG_LWMON */ +} +#endif /* CONFIG_WATCHDOG */ + +/* + * Initializes on-chip ethernet controllers. + * to override, implement board_eth_init() + */ +int cpu_eth_init(bd_t *bis) +{ +#if defined(SCC_ENET) && defined(CONFIG_CMD_NET) + scc_initialize(bis); +#endif +#if defined(FEC_ENET) + fec_initialize(bis); +#endif + return 0; +} diff --git a/arch/powerpc/cpu/mpc8xx/cpu_init.c b/arch/powerpc/cpu/mpc8xx/cpu_init.c new file mode 100644 index 0000000..eb0091b --- /dev/null +++ b/arch/powerpc/cpu/mpc8xx/cpu_init.c @@ -0,0 +1,285 @@ +/* + * (C) Copyright 2000-2002 + * Wolfgang Denk, DENX Software Engineering, wd@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> +#include <watchdog.h> + +#include <mpc8xx.h> +#include <commproc.h> + +#if defined(CONFIG_SYS_RTCSC) || defined(CONFIG_SYS_RMDS) +DECLARE_GLOBAL_DATA_PTR; +#endif + +#if defined(CONFIG_SYS_I2C_UCODE_PATCH) || defined(CONFIG_SYS_SPI_UCODE_PATCH) || \ + defined(CONFIG_SYS_SMC_UCODE_PATCH) +void cpm_load_patch (volatile immap_t * immr); +#endif + +/* + * Breath some life into the CPU... + * + * Set up the memory map, + * initialize a bunch of registers, + * initialize the UPM's + */ +void cpu_init_f (volatile immap_t * immr) +{ +#ifndef CONFIG_MBX + volatile memctl8xx_t *memctl = &immr->im_memctl; +# ifdef CONFIG_SYS_PLPRCR + ulong mfmask; +# endif +#endif + ulong reg; + + /* SYPCR - contains watchdog control (11-9) */ + + immr->im_siu_conf.sc_sypcr = CONFIG_SYS_SYPCR; + +#if defined(CONFIG_WATCHDOG) + reset_8xx_watchdog (immr); +#endif /* CONFIG_WATCHDOG */ + + /* SIUMCR - contains debug pin configuration (11-6) */ +#ifndef CONFIG_SVM_SC8xx + immr->im_siu_conf.sc_siumcr |= CONFIG_SYS_SIUMCR; +#else + immr->im_siu_conf.sc_siumcr = CONFIG_SYS_SIUMCR; +#endif + /* initialize timebase status and control register (11-26) */ + /* unlock TBSCRK */ + + immr->im_sitk.sitk_tbscrk = KAPWR_KEY; + immr->im_sit.sit_tbscr = CONFIG_SYS_TBSCR; + + /* initialize the PIT (11-31) */ + + immr->im_sitk.sitk_piscrk = KAPWR_KEY; + immr->im_sit.sit_piscr = CONFIG_SYS_PISCR; + + /* System integration timers. Don't change EBDF! (15-27) */ + + immr->im_clkrstk.cark_sccrk = KAPWR_KEY; + reg = immr->im_clkrst.car_sccr; + reg &= SCCR_MASK; + reg |= CONFIG_SYS_SCCR; + immr->im_clkrst.car_sccr = reg; + + /* PLL (CPU clock) settings (15-30) */ + + immr->im_clkrstk.cark_plprcrk = KAPWR_KEY; + +#ifndef CONFIG_MBX /* MBX board does things different */ + + /* If CONFIG_SYS_PLPRCR (set in the various *_config.h files) tries to + * set the MF field, then just copy CONFIG_SYS_PLPRCR over car_plprcr, + * otherwise OR in CONFIG_SYS_PLPRCR so we do not change the current MF + * field value. + * + * For newer (starting MPC866) chips PLPRCR layout is different. + */ +#ifdef CONFIG_SYS_PLPRCR + if (get_immr(0xFFFF) >= MPC8xx_NEW_CLK) + mfmask = PLPRCR_MFACT_MSK; + else + mfmask = PLPRCR_MF_MSK; + + if ((CONFIG_SYS_PLPRCR & mfmask) != 0) + reg = CONFIG_SYS_PLPRCR; /* reset control bits */ + else { + reg = immr->im_clkrst.car_plprcr; + reg &= mfmask; /* isolate MF-related fields */ + reg |= CONFIG_SYS_PLPRCR; /* reset control bits */ + } + immr->im_clkrst.car_plprcr = reg; +#endif + + /* + * Memory Controller: + */ + + /* perform BR0 reset that MPC850 Rev. A can't guarantee */ + reg = memctl->memc_br0; + reg &= BR_PS_MSK; /* Clear everything except Port Size bits */ + reg |= BR_V; /* then add just the "Bank Valid" bit */ + memctl->memc_br0 = reg; + + /* Map banks 0 (and maybe 1) to the FLASH banks 0 (and 1) at + * preliminary addresses - these have to be modified later + * when FLASH size has been determined + * + * Depending on the size of the memory region defined by + * CONFIG_SYS_OR0_REMAP some boards (wide address mask) allow to map the + * CONFIG_SYS_MONITOR_BASE, while others (narrower address mask) can't + * map CONFIG_SYS_MONITOR_BASE. + * + * For example, for CONFIG_IVMS8, the CONFIG_SYS_MONITOR_BASE is + * 0xff000000, but CONFIG_SYS_OR0_REMAP's address mask is 0xfff80000. + * + * If BR0 wasn't loaded with address base 0xff000000, then BR0's + * base address remains as 0x00000000. However, the address mask + * have been narrowed to 512Kb, so CONFIG_SYS_MONITOR_BASE wasn't mapped + * into the Bank0. + * + * This is why CONFIG_IVMS8 and similar boards must load BR0 with + * CONFIG_SYS_BR0_PRELIM in advance. + * + * [Thanks to Michael Liao for this explanation. + * I owe him a free beer. - wd] + */ + +#if defined(CONFIG_GTH) || \ + defined(CONFIG_HERMES) || \ + defined(CONFIG_ICU862) || \ + defined(CONFIG_IP860) || \ + defined(CONFIG_IVML24) || \ + defined(CONFIG_IVMS8) || \ + defined(CONFIG_LWMON) || \ + defined(CONFIG_MHPC) || \ + defined(CONFIG_PCU_E) || \ + defined(CONFIG_R360MPI) || \ + defined(CONFIG_RMU) || \ + defined(CONFIG_RPXCLASSIC) || \ + defined(CONFIG_RPXLITE) || \ + defined(CONFIG_SPC1920) || \ + defined(CONFIG_SPD823TS) + + memctl->memc_br0 = CONFIG_SYS_BR0_PRELIM; +#endif + +#if defined(CONFIG_SYS_OR0_REMAP) + memctl->memc_or0 = CONFIG_SYS_OR0_REMAP; +#endif +#if defined(CONFIG_SYS_OR1_REMAP) + memctl->memc_or1 = CONFIG_SYS_OR1_REMAP; +#endif +#if defined(CONFIG_SYS_OR5_REMAP) + memctl->memc_or5 = CONFIG_SYS_OR5_REMAP; +#endif + + /* now restrict to preliminary range */ + memctl->memc_br0 = CONFIG_SYS_BR0_PRELIM; + memctl->memc_or0 = CONFIG_SYS_OR0_PRELIM; + +#if (defined(CONFIG_SYS_OR1_PRELIM) && defined(CONFIG_SYS_BR1_PRELIM)) + memctl->memc_or1 = CONFIG_SYS_OR1_PRELIM; + memctl->memc_br1 = CONFIG_SYS_BR1_PRELIM; +#endif + +#if defined(CONFIG_IP860) /* disable CS0 now that Flash is mapped on CS1 */ + memctl->memc_br0 = 0; +#endif + +#if defined(CONFIG_SYS_OR2_PRELIM) && defined(CONFIG_SYS_BR2_PRELIM) + memctl->memc_or2 = CONFIG_SYS_OR2_PRELIM; + memctl->memc_br2 = CONFIG_SYS_BR2_PRELIM; +#endif + +#if defined(CONFIG_SYS_OR3_PRELIM) && defined(CONFIG_SYS_BR3_PRELIM) + memctl->memc_or3 = CONFIG_SYS_OR3_PRELIM; + memctl->memc_br3 = CONFIG_SYS_BR3_PRELIM; +#endif + +#if defined(CONFIG_SYS_OR4_PRELIM) && defined(CONFIG_SYS_BR4_PRELIM) + memctl->memc_or4 = CONFIG_SYS_OR4_PRELIM; + memctl->memc_br4 = CONFIG_SYS_BR4_PRELIM; +#endif + +#if defined(CONFIG_SYS_OR5_PRELIM) && defined(CONFIG_SYS_BR5_PRELIM) + memctl->memc_or5 = CONFIG_SYS_OR5_PRELIM; + memctl->memc_br5 = CONFIG_SYS_BR5_PRELIM; +#endif + +#if defined(CONFIG_SYS_OR6_PRELIM) && defined(CONFIG_SYS_BR6_PRELIM) + memctl->memc_or6 = CONFIG_SYS_OR6_PRELIM; + memctl->memc_br6 = CONFIG_SYS_BR6_PRELIM; +#endif + +#if defined(CONFIG_SYS_OR7_PRELIM) && defined(CONFIG_SYS_BR7_PRELIM) + memctl->memc_or7 = CONFIG_SYS_OR7_PRELIM; + memctl->memc_br7 = CONFIG_SYS_BR7_PRELIM; +#endif + +#endif /* ! CONFIG_MBX */ + + /* + * Reset CPM + */ + immr->im_cpm.cp_cpcr = CPM_CR_RST | CPM_CR_FLG; + do { /* Spin until command processed */ + __asm__ ("eieio"); + } while (immr->im_cpm.cp_cpcr & CPM_CR_FLG); + +#ifdef CONFIG_MBX + /* + * on the MBX, things are a little bit different: + * - we need to read the VPD to get board information + * - the plprcr is set up dynamically + * - the memory controller is set up dynamically + */ + mbx_init (); +#endif /* CONFIG_MBX */ + +#ifdef CONFIG_RPXCLASSIC + rpxclassic_init (); +#endif + +#if defined(CONFIG_RPXLITE) && defined(CONFIG_ENV_IS_IN_NVRAM) + rpxlite_init (); +#endif + +#ifdef CONFIG_SYS_RCCR /* must be done before cpm_load_patch() */ + /* write config value */ + immr->im_cpm.cp_rccr = CONFIG_SYS_RCCR; +#endif + +#if defined(CONFIG_SYS_I2C_UCODE_PATCH) || defined(CONFIG_SYS_SPI_UCODE_PATCH) || \ + defined(CONFIG_SYS_SMC_UCODE_PATCH) + cpm_load_patch (immr); /* load mpc8xx microcode patch */ +#endif +} + +/* + * initialize higher level parts of CPU like timers + */ +int cpu_init_r (void) +{ +#if defined(CONFIG_SYS_RTCSC) || defined(CONFIG_SYS_RMDS) + bd_t *bd = gd->bd; + volatile immap_t *immr = (volatile immap_t *) (bd->bi_immr_base); +#endif + +#ifdef CONFIG_SYS_RTCSC + /* Unlock RTSC register */ + immr->im_sitk.sitk_rtcsck = KAPWR_KEY; + /* write config value */ + immr->im_sit.sit_rtcsc = CONFIG_SYS_RTCSC; +#endif + +#ifdef CONFIG_SYS_RMDS + /* write config value */ + immr->im_cpm.cp_rmds = CONFIG_SYS_RMDS; +#endif + return (0); +} diff --git a/arch/powerpc/cpu/mpc8xx/fdt.c b/arch/powerpc/cpu/mpc8xx/fdt.c new file mode 100644 index 0000000..7130983 --- /dev/null +++ b/arch/powerpc/cpu/mpc8xx/fdt.c @@ -0,0 +1,46 @@ +/* + * Copyright 2008 (C) Bryan O'Donoghue + * + * Code copied & edited from Freescale mpc85xx stuff. + * + * 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> +#include <libfdt.h> +#include <fdt_support.h> + +DECLARE_GLOBAL_DATA_PTR; + +void ft_cpu_setup(void *blob, bd_t *bd) +{ + do_fixup_by_prop_u32(blob, "device_type", "cpu", 4, + "timebase-frequency", get_tbclk(), 1); + do_fixup_by_prop_u32(blob, "device_type", "cpu", 4, + "bus-frequency", bd->bi_busfreq, 1); + do_fixup_by_prop_u32(blob, "device_type", "cpu", 4, + "clock-frequency", bd->bi_intfreq, 1); + do_fixup_by_compat_u32(blob, "fsl,cpm-brg", "clock-frequency", + gd->brg_clk, 1); + + /* Fixup ethernet MAC addresses */ + fdt_fixup_ethernet(blob); + + fdt_fixup_memory(blob, (u64)bd->bi_memstart, (u64)bd->bi_memsize); +} diff --git a/arch/powerpc/cpu/mpc8xx/fec.c b/arch/powerpc/cpu/mpc8xx/fec.c new file mode 100644 index 0000000..89c1ff9 --- /dev/null +++ b/arch/powerpc/cpu/mpc8xx/fec.c @@ -0,0 +1,1026 @@ +/* + * (C) Copyright 2000 + * Wolfgang Denk, DENX Software Engineering, wd@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> +#include <malloc.h> +#include <commproc.h> +#include <net.h> +#include <command.h> + +DECLARE_GLOBAL_DATA_PTR; + +#undef ET_DEBUG + +#if defined(CONFIG_CMD_NET) && \ + (defined(FEC_ENET) || defined(CONFIG_ETHER_ON_FEC1) || defined(CONFIG_ETHER_ON_FEC2)) + +/* compatibility test, if only FEC_ENET defined assume ETHER on FEC1 */ +#if defined(FEC_ENET) && !defined(CONFIG_ETHER_ON_FEC1) && !defined(CONFIG_ETHER_ON_FEC2) +#define CONFIG_ETHER_ON_FEC1 1 +#endif + +/* define WANT_MII when MII support is required */ +#if defined(CONFIG_SYS_DISCOVER_PHY) || defined(CONFIG_FEC1_PHY) || defined(CONFIG_FEC2_PHY) +#define WANT_MII +#else +#undef WANT_MII +#endif + +#if defined(WANT_MII) +#include <miiphy.h> + +#if !(defined(CONFIG_MII) || defined(CONFIG_CMD_MII)) +#error "CONFIG_MII has to be defined!" +#endif + +#endif + +#if defined(CONFIG_RMII) && !defined(WANT_MII) +#error RMII support is unusable without a working PHY. +#endif + +#ifdef CONFIG_SYS_DISCOVER_PHY +static int mii_discover_phy(struct eth_device *dev); +#endif + +int fec8xx_miiphy_read(char *devname, unsigned char addr, + unsigned char reg, unsigned short *value); +int fec8xx_miiphy_write(char *devname, unsigned char addr, + unsigned char reg, unsigned short value); + +static struct ether_fcc_info_s +{ + int ether_index; + int fecp_offset; + int phy_addr; + int actual_phy_addr; + int initialized; +} + ether_fcc_info[] = { +#if defined(CONFIG_ETHER_ON_FEC1) + { + 0, + offsetof(immap_t, im_cpm.cp_fec1), +#if defined(CONFIG_FEC1_PHY) + CONFIG_FEC1_PHY, +#else + -1, /* discover */ +#endif + -1, + 0, + + }, +#endif +#if defined(CONFIG_ETHER_ON_FEC2) + { + 1, + offsetof(immap_t, im_cpm.cp_fec2), +#if defined(CONFIG_FEC2_PHY) + CONFIG_FEC2_PHY, +#else + -1, +#endif + -1, + 0, + }, +#endif +}; + +/* Ethernet Transmit and Receive Buffers */ +#define DBUF_LENGTH 1520 + +#define TX_BUF_CNT 2 + +#define TOUT_LOOP 100 + +#define PKT_MAXBUF_SIZE 1518 +#define PKT_MINBUF_SIZE 64 +#define PKT_MAXBLR_SIZE 1520 + +#ifdef __GNUC__ +static char txbuf[DBUF_LENGTH] __attribute__ ((aligned(8))); +#else +#error txbuf must be aligned. +#endif + +static uint rxIdx; /* index of the current RX buffer */ +static uint txIdx; /* index of the current TX buffer */ + +/* + * FEC Ethernet Tx and Rx buffer descriptors allocated at the + * immr->udata_bd address on Dual-Port RAM + * Provide for Double Buffering + */ + +typedef volatile struct CommonBufferDescriptor { + cbd_t rxbd[PKTBUFSRX]; /* Rx BD */ + cbd_t txbd[TX_BUF_CNT]; /* Tx BD */ +} RTXBD; + +static RTXBD *rtx = NULL; + +static int fec_send(struct eth_device* dev, volatile void *packet, int length); +static int fec_recv(struct eth_device* dev); +static int fec_init(struct eth_device* dev, bd_t * bd); +static void fec_halt(struct eth_device* dev); +#if defined(CONFIG_MII) || defined(CONFIG_CMD_MII) +static void __mii_init(void); +#endif + +int fec_initialize(bd_t *bis) +{ + struct eth_device* dev; + struct ether_fcc_info_s *efis; + int i; + + for (i = 0; i < sizeof(ether_fcc_info) / sizeof(ether_fcc_info[0]); i++) { + + dev = malloc(sizeof(*dev)); + if (dev == NULL) + hang(); + + memset(dev, 0, sizeof(*dev)); + + /* for FEC1 make sure that the name of the interface is the same + as the old one for compatibility reasons */ + if (i == 0) { + sprintf (dev->name, "FEC ETHERNET"); + } else { + sprintf (dev->name, "FEC%d ETHERNET", + ether_fcc_info[i].ether_index + 1); + } + + efis = ðer_fcc_info[i]; + + /* + * reset actual phy addr + */ + efis->actual_phy_addr = -1; + + dev->priv = efis; + dev->init = fec_init; + dev->halt = fec_halt; + dev->send = fec_send; + dev->recv = fec_recv; + + eth_register(dev); + +#if defined(CONFIG_MII) || defined(CONFIG_CMD_MII) + miiphy_register(dev->name, + fec8xx_miiphy_read, fec8xx_miiphy_write); +#endif + } + return 1; +} + +static int fec_send(struct eth_device* dev, volatile void *packet, int length) +{ + int j, rc; + struct ether_fcc_info_s *efis = dev->priv; + volatile fec_t *fecp = (volatile fec_t *)(CONFIG_SYS_IMMR + efis->fecp_offset); + + /* section 16.9.23.3 + * Wait for ready + */ + j = 0; + while ((rtx->txbd[txIdx].cbd_sc & BD_ENET_TX_READY) && (j<TOUT_LOOP)) { + udelay(1); + j++; + } + if (j>=TOUT_LOOP) { + printf("TX not ready\n"); + } + + rtx->txbd[txIdx].cbd_bufaddr = (uint)packet; + rtx->txbd[txIdx].cbd_datlen = length; + rtx->txbd[txIdx].cbd_sc |= BD_ENET_TX_READY | BD_ENET_TX_LAST; + __asm__ ("eieio"); + + /* Activate transmit Buffer Descriptor polling */ + fecp->fec_x_des_active = 0x01000000; /* Descriptor polling active */ + + j = 0; + while ((rtx->txbd[txIdx].cbd_sc & BD_ENET_TX_READY) && (j<TOUT_LOOP)) { +#if defined(CONFIG_ICU862) + udelay(10); +#else + udelay(1); +#endif + j++; + } + if (j>=TOUT_LOOP) { + printf("TX timeout\n"); + } +#ifdef ET_DEBUG + printf("%s[%d] %s: cycles: %d status: %x retry cnt: %d\n", + __FILE__,__LINE__,__FUNCTION__,j,rtx->txbd[txIdx].cbd_sc, + (rtx->txbd[txIdx].cbd_sc & 0x003C)>>2); +#endif + /* return only status bits */; + rc = (rtx->txbd[txIdx].cbd_sc & BD_ENET_TX_STATS); + + txIdx = (txIdx + 1) % TX_BUF_CNT; + + return rc; +} + +static int fec_recv (struct eth_device *dev) +{ + struct ether_fcc_info_s *efis = dev->priv; + volatile fec_t *fecp = + (volatile fec_t *) (CONFIG_SYS_IMMR + efis->fecp_offset); + int length; + + for (;;) { + /* section 16.9.23.2 */ + if (rtx->rxbd[rxIdx].cbd_sc & BD_ENET_RX_EMPTY) { + length = -1; + break; /* nothing received - leave for() loop */ + } + + length = rtx->rxbd[rxIdx].cbd_datlen; + + if (rtx->rxbd[rxIdx].cbd_sc & 0x003f) { +#ifdef ET_DEBUG + printf ("%s[%d] err: %x\n", + __FUNCTION__, __LINE__, + rtx->rxbd[rxIdx].cbd_sc); +#endif + } else { + volatile uchar *rx = NetRxPackets[rxIdx]; + + length -= 4; + +#if defined(CONFIG_CMD_CDP) + if ((rx[0] & 1) != 0 + && memcmp ((uchar *) rx, NetBcastAddr, 6) != 0 + && memcmp ((uchar *) rx, NetCDPAddr, 6) != 0) + rx = NULL; +#endif + /* + * Pass the packet up to the protocol layers. + */ + if (rx != NULL) + NetReceive (rx, length); + } + + /* Give the buffer back to the FEC. */ + rtx->rxbd[rxIdx].cbd_datlen = 0; + + /* wrap around buffer index when necessary */ + if ((rxIdx + 1) >= PKTBUFSRX) { + rtx->rxbd[PKTBUFSRX - 1].cbd_sc = + (BD_ENET_RX_WRAP | BD_ENET_RX_EMPTY); + rxIdx = 0; + } else { + rtx->rxbd[rxIdx].cbd_sc = BD_ENET_RX_EMPTY; + rxIdx++; + } + + __asm__ ("eieio"); + + /* Try to fill Buffer Descriptors */ + fecp->fec_r_des_active = 0x01000000; /* Descriptor polling active */ + } + + return length; +} + +/************************************************************** + * + * FEC Ethernet Initialization Routine + * + *************************************************************/ + +#define FEC_ECNTRL_PINMUX 0x00000004 +#define FEC_ECNTRL_ETHER_EN 0x00000002 +#define FEC_ECNTRL_RESET 0x00000001 + +#define FEC_RCNTRL_BC_REJ 0x00000010 +#define FEC_RCNTRL_PROM 0x00000008 +#define FEC_RCNTRL_MII_MODE 0x00000004 +#define FEC_RCNTRL_DRT 0x00000002 +#define FEC_RCNTRL_LOOP 0x00000001 + +#define FEC_TCNTRL_FDEN 0x00000004 +#define FEC_TCNTRL_HBC 0x00000002 +#define FEC_TCNTRL_GTS 0x00000001 + +#define FEC_RESET_DELAY 50 + +#if defined(CONFIG_RMII) + +static inline void fec_10Mbps(struct eth_device *dev) +{ + struct ether_fcc_info_s *efis = dev->priv; + int fecidx = efis->ether_index; + uint mask = (fecidx == 0) ? 0x0000010 : 0x0000008; + + if ((unsigned int)fecidx >= 2) + hang(); + + ((volatile immap_t *)CONFIG_SYS_IMMR)->im_cpm.cp_cptr |= mask; +} + +static inline void fec_100Mbps(struct eth_device *dev) +{ + struct ether_fcc_info_s *efis = dev->priv; + int fecidx = efis->ether_index; + uint mask = (fecidx == 0) ? 0x0000010 : 0x0000008; + + if ((unsigned int)fecidx >= 2) + hang(); + + ((volatile immap_t *)CONFIG_SYS_IMMR)->im_cpm.cp_cptr &= ~mask; +} + +#endif + +static inline void fec_full_duplex(struct eth_device *dev) +{ + struct ether_fcc_info_s *efis = dev->priv; + volatile fec_t *fecp = (volatile fec_t *)(CONFIG_SYS_IMMR + efis->fecp_offset); + + fecp->fec_r_cntrl &= ~FEC_RCNTRL_DRT; + fecp->fec_x_cntrl |= FEC_TCNTRL_FDEN; /* FD enable */ +} + +static inline void fec_half_duplex(struct eth_device *dev) +{ + struct ether_fcc_info_s *efis = dev->priv; + volatile fec_t *fecp = (volatile fec_t *)(CONFIG_SYS_IMMR + efis->fecp_offset); + + fecp->fec_r_cntrl |= FEC_RCNTRL_DRT; + fecp->fec_x_cntrl &= ~FEC_TCNTRL_FDEN; /* FD disable */ +} + +static void fec_pin_init(int fecidx) +{ + bd_t *bd = gd->bd; + volatile immap_t *immr = (immap_t *) CONFIG_SYS_IMMR; + volatile fec_t *fecp; + + /* + * only two FECs please + */ + if ((unsigned int)fecidx >= 2) + hang(); + + if (fecidx == 0) + fecp = &immr->im_cpm.cp_fec1; + else + fecp = &immr->im_cpm.cp_fec2; + + /* + * Set MII speed to 2.5 MHz or slightly below. + * * According to the MPC860T (Rev. D) Fast ethernet controller user + * * manual (6.2.14), + * * the MII management interface clock must be less than or equal + * * to 2.5 MHz. + * * This MDC frequency is equal to system clock / (2 * MII_SPEED). + * * Then MII_SPEED = system_clock / 2 * 2,5 MHz. + * + * All MII configuration is done via FEC1 registers: + */ + immr->im_cpm.cp_fec1.fec_mii_speed = ((bd->bi_intfreq + 4999999) / 5000000) << 1; + +#if defined(CONFIG_NETTA) || defined(CONFIG_NETPHONE) || defined(CONFIG_NETTA2) + /* our PHYs are the limit at 2.5 MHz */ + fecp->fec_mii_speed <<= 1; +#endif + +#if defined(CONFIG_MPC885_FAMILY) && defined(WANT_MII) + /* use MDC for MII */ + immr->im_ioport.iop_pdpar |= 0x0080; + immr->im_ioport.iop_pddir &= ~0x0080; +#endif + + if (fecidx == 0) { +#if defined(CONFIG_ETHER_ON_FEC1) + +#if defined(CONFIG_MPC885_FAMILY) /* MPC87x/88x have got 2 FECs and different pinout */ + +#if !defined(CONFIG_RMII) + + immr->im_ioport.iop_papar |= 0xf830; + immr->im_ioport.iop_padir |= 0x0830; + immr->im_ioport.iop_padir &= ~0xf000; + + immr->im_cpm.cp_pbpar |= 0x00001001; + immr->im_cpm.cp_pbdir &= ~0x00001001; + + immr->im_ioport.iop_pcpar |= 0x000c; + immr->im_ioport.iop_pcdir &= ~0x000c; + + immr->im_cpm.cp_pepar |= 0x00000003; + immr->im_cpm.cp_pedir |= 0x00000003; + immr->im_cpm.cp_peso &= ~0x00000003; + + immr->im_cpm.cp_cptr &= ~0x00000100; + +#else + +#if !defined(CONFIG_FEC1_PHY_NORXERR) + immr->im_ioport.iop_papar |= 0x1000; + immr->im_ioport.iop_padir &= ~0x1000; +#endif + immr->im_ioport.iop_papar |= 0xe810; + immr->im_ioport.iop_padir |= 0x0810; + immr->im_ioport.iop_padir &= ~0xe000; + + immr->im_cpm.cp_pbpar |= 0x00000001; + immr->im_cpm.cp_pbdir &= ~0x00000001; + + immr->im_cpm.cp_cptr |= 0x00000100; + immr->im_cpm.cp_cptr &= ~0x00000050; + +#endif /* !CONFIG_RMII */ + +#elif !defined(CONFIG_ICU862) && !defined(CONFIG_IAD210) + /* + * Configure all of port D for MII. + */ + immr->im_ioport.iop_pdpar = 0x1fff; + + /* + * Bits moved from Rev. D onward + */ + if ((get_immr(0) & 0xffff) < 0x0501) + immr->im_ioport.iop_pddir = 0x1c58; /* Pre rev. D */ + else + immr->im_ioport.iop_pddir = 0x1fff; /* Rev. D and later */ +#else + /* + * Configure port A for MII. + */ + +#if defined(CONFIG_ICU862) && defined(CONFIG_SYS_DISCOVER_PHY) + + /* + * On the ICU862 board the MII-MDC pin is routed to PD8 pin + * * of CPU, so for this board we need to configure Utopia and + * * enable PD8 to MII-MDC function + */ + immr->im_ioport.iop_pdpar |= 0x4080; +#endif + + /* + * Has Utopia been configured? + */ + if (immr->im_ioport.iop_pdpar & (0x8000 >> 1)) { + /* + * YES - Use MUXED mode for UTOPIA bus. + * This frees Port A for use by MII (see 862UM table 41-6). + */ + immr->im_ioport.utmode &= ~0x80; + } else { + /* + * NO - set SPLIT mode for UTOPIA bus. + * + * This doesn't really effect UTOPIA (which isn't + * enabled anyway) but just tells the 862 + * to use port A for MII (see 862UM table 41-6). + */ + immr->im_ioport.utmode |= 0x80; + } +#endif /* !defined(CONFIG_ICU862) */ + +#endif /* CONFIG_ETHER_ON_FEC1 */ + } else if (fecidx == 1) { + +#if defined(CONFIG_ETHER_ON_FEC2) + +#if defined(CONFIG_MPC885_FAMILY) /* MPC87x/88x have got 2 FECs and different pinout */ + +#if !defined(CONFIG_RMII) + immr->im_cpm.cp_pepar |= 0x0003fffc; + immr->im_cpm.cp_pedir |= 0x0003fffc; + immr->im_cpm.cp_peso &= ~0x000087fc; + immr->im_cpm.cp_peso |= 0x00037800; + + immr->im_cpm.cp_cptr &= ~0x00000080; +#else + +#if !defined(CONFIG_FEC2_PHY_NORXERR) + immr->im_cpm.cp_pepar |= 0x00000010; + immr->im_cpm.cp_pedir |= 0x00000010; + immr->im_cpm.cp_peso &= ~0x00000010; +#endif + immr->im_cpm.cp_pepar |= 0x00039620; + immr->im_cpm.cp_pedir |= 0x00039620; + immr->im_cpm.cp_peso |= 0x00031000; + immr->im_cpm.cp_peso &= ~0x00008620; + + immr->im_cpm.cp_cptr |= 0x00000080; + immr->im_cpm.cp_cptr &= ~0x00000028; +#endif /* CONFIG_RMII */ + +#endif /* CONFIG_MPC885_FAMILY */ + +#endif /* CONFIG_ETHER_ON_FEC2 */ + + } +} + +static int fec_reset(volatile fec_t *fecp) +{ + int i; + + /* Whack a reset. + * A delay is required between a reset of the FEC block and + * initialization of other FEC registers because the reset takes + * some time to complete. If you don't delay, subsequent writes + * to FEC registers might get killed by the reset routine which is + * still in progress. + */ + + fecp->fec_ecntrl = FEC_ECNTRL_PINMUX | FEC_ECNTRL_RESET; + for (i = 0; + (fecp->fec_ecntrl & FEC_ECNTRL_RESET) && (i < FEC_RESET_DELAY); + ++i) { + udelay (1); + } + if (i == FEC_RESET_DELAY) + return -1; + + return 0; +} + +static int fec_init (struct eth_device *dev, bd_t * bd) +{ + struct ether_fcc_info_s *efis = dev->priv; + volatile immap_t *immr = (immap_t *) CONFIG_SYS_IMMR; + volatile fec_t *fecp = + (volatile fec_t *) (CONFIG_SYS_IMMR + efis->fecp_offset); + int i; + + if (efis->ether_index == 0) { +#if defined(CONFIG_FADS) /* FADS family uses FPGA (BCSR) to control PHYs */ +#if defined(CONFIG_MPC885ADS) + *(vu_char *) BCSR5 &= ~(BCSR5_MII1_EN | BCSR5_MII1_RST); +#else + /* configure FADS for fast (FEC) ethernet, half-duplex */ + /* The LXT970 needs about 50ms to recover from reset, so + * wait for it by discovering the PHY before leaving eth_init(). + */ + { + volatile uint *bcsr4 = (volatile uint *) BCSR4; + + *bcsr4 = (*bcsr4 & ~(BCSR4_FETH_EN | BCSR4_FETHCFG1)) + | (BCSR4_FETHCFG0 | BCSR4_FETHFDE | + BCSR4_FETHRST); + + /* reset the LXT970 PHY */ + *bcsr4 &= ~BCSR4_FETHRST; + udelay (10); + *bcsr4 |= BCSR4_FETHRST; + udelay (10); + } +#endif /* CONFIG_MPC885ADS */ +#endif /* CONFIG_FADS */ + } + +#if defined(CONFIG_MII) || defined(CONFIG_CMD_MII) + /* the MII interface is connected to FEC1 + * so for the miiphy_xxx function to work we must + * call mii_init since fec_halt messes the thing up + */ + if (efis->ether_index != 0) + __mii_init(); +#endif + + if (fec_reset(fecp) < 0) + printf ("FEC_RESET_DELAY timeout\n"); + + /* We use strictly polling mode only + */ + fecp->fec_imask = 0; + + /* Clear any pending interrupt + */ + fecp->fec_ievent = 0xffc0; + + /* No need to set the IVEC register */ + + /* Set station address + */ +#define ea dev->enetaddr + fecp->fec_addr_low = (ea[0] << 24) | (ea[1] << 16) | (ea[2] << 8) | (ea[3]); + fecp->fec_addr_high = (ea[4] << 8) | (ea[5]); +#undef ea + +#if defined(CONFIG_CMD_CDP) + /* + * Turn on multicast address hash table + */ + fecp->fec_hash_table_high = 0xffffffff; + fecp->fec_hash_table_low = 0xffffffff; +#else + /* Clear multicast address hash table + */ + fecp->fec_hash_table_high = 0; + fecp->fec_hash_table_low = 0; +#endif + + /* Set maximum receive buffer size. + */ + fecp->fec_r_buff_size = PKT_MAXBLR_SIZE; + + /* Set maximum frame length + */ + fecp->fec_r_hash = PKT_MAXBUF_SIZE; + + /* + * Setup Buffers and Buffer Desriptors + */ + rxIdx = 0; + txIdx = 0; + + if (!rtx) { +#ifdef CONFIG_SYS_ALLOC_DPRAM + rtx = (RTXBD *) (immr->im_cpm.cp_dpmem + + dpram_alloc_align (sizeof (RTXBD), 8)); +#else + rtx = (RTXBD *) (immr->im_cpm.cp_dpmem + CPM_FEC_BASE); +#endif + } + /* + * Setup Receiver Buffer Descriptors (13.14.24.18) + * Settings: + * Empty, Wrap + */ + for (i = 0; i < PKTBUFSRX; i++) { + rtx->rxbd[i].cbd_sc = BD_ENET_RX_EMPTY; + rtx->rxbd[i].cbd_datlen = 0; /* Reset */ + rtx->rxbd[i].cbd_bufaddr = (uint) NetRxPackets[i]; + } + rtx->rxbd[PKTBUFSRX - 1].cbd_sc |= BD_ENET_RX_WRAP; + + /* + * Setup Ethernet Transmitter Buffer Descriptors (13.14.24.19) + * Settings: + * Last, Tx CRC + */ + for (i = 0; i < TX_BUF_CNT; i++) { + rtx->txbd[i].cbd_sc = BD_ENET_TX_LAST | BD_ENET_TX_TC; + rtx->txbd[i].cbd_datlen = 0; /* Reset */ + rtx->txbd[i].cbd_bufaddr = (uint) (&txbuf[0]); + } + rtx->txbd[TX_BUF_CNT - 1].cbd_sc |= BD_ENET_TX_WRAP; + + /* Set receive and transmit descriptor base + */ + fecp->fec_r_des_start = (unsigned int) (&rtx->rxbd[0]); + fecp->fec_x_des_start = (unsigned int) (&rtx->txbd[0]); + + /* Enable MII mode + */ +#if 0 /* Full duplex mode */ + fecp->fec_r_cntrl = FEC_RCNTRL_MII_MODE; + fecp->fec_x_cntrl = FEC_TCNTRL_FDEN; +#else /* Half duplex mode */ + fecp->fec_r_cntrl = FEC_RCNTRL_MII_MODE | FEC_RCNTRL_DRT; + fecp->fec_x_cntrl = 0; +#endif + + /* Enable big endian and don't care about SDMA FC. + */ + fecp->fec_fun_code = 0x78000000; + + /* + * Setup the pin configuration of the FEC + */ + fec_pin_init (efis->ether_index); + + rxIdx = 0; + txIdx = 0; + + /* + * Now enable the transmit and receive processing + */ + fecp->fec_ecntrl = FEC_ECNTRL_PINMUX | FEC_ECNTRL_ETHER_EN; + + if (efis->phy_addr == -1) { +#ifdef CONFIG_SYS_DISCOVER_PHY + /* + * wait for the PHY to wake up after reset + */ + efis->actual_phy_addr = mii_discover_phy (dev); + + if (efis->actual_phy_addr == -1) { + printf ("Unable to discover phy!\n"); + return -1; + } +#else + efis->actual_phy_addr = -1; +#endif + } else { + efis->actual_phy_addr = efis->phy_addr; + } + +#if defined(CONFIG_MII) && defined(CONFIG_RMII) + /* + * adapt the RMII speed to the speed of the phy + */ + if (miiphy_speed (dev->name, efis->actual_phy_addr) == _100BASET) { + fec_100Mbps (dev); + } else { + fec_10Mbps (dev); + } +#endif + +#if defined(CONFIG_MII) + /* + * adapt to the half/full speed settings + */ + if (miiphy_duplex (dev->name, efis->actual_phy_addr) == FULL) { + fec_full_duplex (dev); + } else { + fec_half_duplex (dev); + } +#endif + + /* And last, try to fill Rx Buffer Descriptors */ + fecp->fec_r_des_active = 0x01000000; /* Descriptor polling active */ + + efis->initialized = 1; + + return 0; +} + + +static void fec_halt(struct eth_device* dev) +{ + struct ether_fcc_info_s *efis = dev->priv; + volatile fec_t *fecp = (volatile fec_t *)(CONFIG_SYS_IMMR + efis->fecp_offset); + int i; + + /* avoid halt if initialized; mii gets stuck otherwise */ + if (!efis->initialized) + return; + + /* Whack a reset. + * A delay is required between a reset of the FEC block and + * initialization of other FEC registers because the reset takes + * some time to complete. If you don't delay, subsequent writes + * to FEC registers might get killed by the reset routine which is + * still in progress. + */ + + fecp->fec_ecntrl = FEC_ECNTRL_PINMUX | FEC_ECNTRL_RESET; + for (i = 0; + (fecp->fec_ecntrl & FEC_ECNTRL_RESET) && (i < FEC_RESET_DELAY); + ++i) { + udelay (1); + } + if (i == FEC_RESET_DELAY) { + printf ("FEC_RESET_DELAY timeout\n"); + return; + } + + efis->initialized = 0; +} + +#if defined(CONFIG_SYS_DISCOVER_PHY) || defined(CONFIG_MII) || defined(CONFIG_CMD_MII) + +/* Make MII read/write commands for the FEC. +*/ + +#define mk_mii_read(ADDR, REG) (0x60020000 | ((ADDR << 23) | \ + (REG & 0x1f) << 18)) + +#define mk_mii_write(ADDR, REG, VAL) (0x50020000 | ((ADDR << 23) | \ + (REG & 0x1f) << 18) | \ + (VAL & 0xffff)) + +/* Interrupt events/masks. +*/ +#define FEC_ENET_HBERR ((uint)0x80000000) /* Heartbeat error */ +#define FEC_ENET_BABR ((uint)0x40000000) /* Babbling receiver */ +#define FEC_ENET_BABT ((uint)0x20000000) /* Babbling transmitter */ +#define FEC_ENET_GRA ((uint)0x10000000) /* Graceful stop complete */ +#define FEC_ENET_TXF ((uint)0x08000000) /* Full frame transmitted */ +#define FEC_ENET_TXB ((uint)0x04000000) /* A buffer was transmitted */ +#define FEC_ENET_RXF ((uint)0x02000000) /* Full frame received */ +#define FEC_ENET_RXB ((uint)0x01000000) /* A buffer was received */ +#define FEC_ENET_MII ((uint)0x00800000) /* MII interrupt */ +#define FEC_ENET_EBERR ((uint)0x00400000) /* SDMA bus error */ + +/* PHY identification + */ +#define PHY_ID_LXT970 0x78100000 /* LXT970 */ +#define PHY_ID_LXT971 0x001378e0 /* LXT971 and 972 */ +#define PHY_ID_82555 0x02a80150 /* Intel 82555 */ +#define PHY_ID_QS6612 0x01814400 /* QS6612 */ +#define PHY_ID_AMD79C784 0x00225610 /* AMD 79C784 */ +#define PHY_ID_LSI80225 0x0016f870 /* LSI 80225 */ +#define PHY_ID_LSI80225B 0x0016f880 /* LSI 80225/B */ +#define PHY_ID_DM9161 0x0181B880 /* Davicom DM9161 */ +#define PHY_ID_KSM8995M 0x00221450 /* MICREL KS8995MA */ + +/* send command to phy using mii, wait for result */ +static uint +mii_send(uint mii_cmd) +{ + uint mii_reply; + volatile fec_t *ep; + int cnt; + + ep = &(((immap_t *)CONFIG_SYS_IMMR)->im_cpm.cp_fec); + + ep->fec_mii_data = mii_cmd; /* command to phy */ + + /* wait for mii complete */ + cnt = 0; + while (!(ep->fec_ievent & FEC_ENET_MII)) { + if (++cnt > 1000) { + printf("mii_send STUCK!\n"); + break; + } + } + mii_reply = ep->fec_mii_data; /* result from phy */ + ep->fec_ievent = FEC_ENET_MII; /* clear MII complete */ +#if 0 + printf("%s[%d] %s: sent=0x%8.8x, reply=0x%8.8x\n", + __FILE__,__LINE__,__FUNCTION__,mii_cmd,mii_reply); +#endif + return (mii_reply & 0xffff); /* data read from phy */ +} +#endif + +#if defined(CONFIG_SYS_DISCOVER_PHY) +static int mii_discover_phy(struct eth_device *dev) +{ +#define MAX_PHY_PASSES 11 + uint phyno; + int pass; + uint phytype; + int phyaddr; + + phyaddr = -1; /* didn't find a PHY yet */ + for (pass = 1; pass <= MAX_PHY_PASSES && phyaddr < 0; ++pass) { + if (pass > 1) { + /* PHY may need more time to recover from reset. + * The LXT970 needs 50ms typical, no maximum is + * specified, so wait 10ms before try again. + * With 11 passes this gives it 100ms to wake up. + */ + udelay(10000); /* wait 10ms */ + } + for (phyno = 0; phyno < 32 && phyaddr < 0; ++phyno) { + phytype = mii_send(mk_mii_read(phyno, PHY_PHYIDR2)); +#ifdef ET_DEBUG + printf("PHY type 0x%x pass %d type ", phytype, pass); +#endif + if (phytype != 0xffff) { + phyaddr = phyno; + phytype |= mii_send(mk_mii_read(phyno, + PHY_PHYIDR1)) << 16; + +#ifdef ET_DEBUG + printf("PHY @ 0x%x pass %d type ",phyno,pass); + switch (phytype & 0xfffffff0) { + case PHY_ID_LXT970: + printf("LXT970\n"); + break; + case PHY_ID_LXT971: + printf("LXT971\n"); + break; + case PHY_ID_82555: + printf("82555\n"); + break; + case PHY_ID_QS6612: + printf("QS6612\n"); + break; + case PHY_ID_AMD79C784: + printf("AMD79C784\n"); + break; + case PHY_ID_LSI80225B: + printf("LSI L80225/B\n"); + break; + case PHY_ID_DM9161: + printf("Davicom DM9161\n"); + break; + case PHY_ID_KSM8995M: + printf("MICREL KS8995M\n"); + break; + default: + printf("0x%08x\n", phytype); + break; + } +#endif + } + } + } + if (phyaddr < 0) { + printf("No PHY device found.\n"); + } + return phyaddr; +} +#endif /* CONFIG_SYS_DISCOVER_PHY */ + +#if (defined(CONFIG_MII) || defined(CONFIG_CMD_MII)) && !defined(CONFIG_BITBANGMII) + +/**************************************************************************** + * mii_init -- Initialize the MII via FEC 1 for MII command without ethernet + * This function is a subset of eth_init + **************************************************************************** + */ +static void __mii_init(void) +{ + volatile immap_t *immr = (immap_t *) CONFIG_SYS_IMMR; + volatile fec_t *fecp = &(immr->im_cpm.cp_fec); + + if (fec_reset(fecp) < 0) + printf ("FEC_RESET_DELAY timeout\n"); + + /* We use strictly polling mode only + */ + fecp->fec_imask = 0; + + /* Clear any pending interrupt + */ + fecp->fec_ievent = 0xffc0; + + /* Now enable the transmit and receive processing + */ + fecp->fec_ecntrl = FEC_ECNTRL_PINMUX | FEC_ECNTRL_ETHER_EN; +} + +void mii_init (void) +{ + int i; + + __mii_init(); + + /* Setup the pin configuration of the FEC(s) + */ + for (i = 0; i < sizeof(ether_fcc_info) / sizeof(ether_fcc_info[0]); i++) + fec_pin_init(ether_fcc_info[i].ether_index); +} + +/***************************************************************************** + * Read and write a MII PHY register, routines used by MII Utilities + * + * FIXME: These routines are expected to return 0 on success, but mii_send + * does _not_ return an error code. Maybe 0xFFFF means error, i.e. + * no PHY connected... + * For now always return 0. + * FIXME: These routines only work after calling eth_init() at least once! + * Otherwise they hang in mii_send() !!! Sorry! + *****************************************************************************/ + +int fec8xx_miiphy_read(char *devname, unsigned char addr, + unsigned char reg, unsigned short *value) +{ + short rdreg; /* register working value */ + +#ifdef MII_DEBUG + printf ("miiphy_read(0x%x) @ 0x%x = ", reg, addr); +#endif + rdreg = mii_send(mk_mii_read(addr, reg)); + + *value = rdreg; +#ifdef MII_DEBUG + printf ("0x%04x\n", *value); +#endif + return 0; +} + +int fec8xx_miiphy_write(char *devname, unsigned char addr, + unsigned char reg, unsigned short value) +{ + short rdreg; /* register working value */ +#ifdef MII_DEBUG + printf ("miiphy_write(0x%x) @ 0x%x = ", reg, addr); +#endif + rdreg = mii_send(mk_mii_write(addr, reg, value)); + +#ifdef MII_DEBUG + printf ("0x%04x\n", value); +#endif + return 0; +} +#endif + +#endif diff --git a/arch/powerpc/cpu/mpc8xx/fec.h b/arch/powerpc/cpu/mpc8xx/fec.h new file mode 100644 index 0000000..a49417c --- /dev/null +++ b/arch/powerpc/cpu/mpc8xx/fec.h @@ -0,0 +1,28 @@ +/* + * (C) Copyright 2000 + * Wolfgang Denk, DENX Software Engineering, wd@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 + */ + +#ifndef _FEC_H_ +#define _FEC_H_ + + +#endif /* _FEC_H_ */ diff --git a/arch/powerpc/cpu/mpc8xx/i2c.c b/arch/powerpc/cpu/mpc8xx/i2c.c new file mode 100644 index 0000000..338caba --- /dev/null +++ b/arch/powerpc/cpu/mpc8xx/i2c.c @@ -0,0 +1,707 @@ +/* + * (C) Copyright 2000 + * Paolo Scaffardi, AIRVENT SAM s.p.a - RIMINI(ITALY), arsenio@tin.it + * + * (C) Copyright 2000 Sysgo Real-Time Solutions, GmbH <www.elinos.com> + * Marius Groeger <mgroeger@sysgo.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 + * + * Back ported to the 8xx platform (from the 8260 platform) by + * Murray.Jensen@cmst.csiro.au, 27-Jan-01. + */ + +#include <common.h> + +#ifdef CONFIG_HARD_I2C + +#include <commproc.h> +#include <i2c.h> +#ifdef CONFIG_LWMON +#include <watchdog.h> +#endif + +DECLARE_GLOBAL_DATA_PTR; + +/* define to enable debug messages */ +#undef DEBUG_I2C + +/* tx/rx timeout (we need the i2c early, so we don't use get_timer()) */ +#define TOUT_LOOP 1000000 + +#define NUM_RX_BDS 4 +#define NUM_TX_BDS 4 +#define MAX_TX_SPACE 256 +#define I2C_RXTX_LEN 128 /* maximum tx/rx buffer length */ + +typedef struct I2C_BD +{ + unsigned short status; + unsigned short length; + unsigned char *addr; +} I2C_BD; +#define BD_I2C_TX_START 0x0400 /* special status for i2c: Start condition */ + +#define BD_I2C_TX_CL 0x0001 /* collision error */ +#define BD_I2C_TX_UN 0x0002 /* underflow error */ +#define BD_I2C_TX_NAK 0x0004 /* no acknowledge error */ +#define BD_I2C_TX_ERR (BD_I2C_TX_NAK|BD_I2C_TX_UN|BD_I2C_TX_CL) + +#define BD_I2C_RX_ERR BD_SC_OV + +typedef void (*i2c_ecb_t)(int, int); /* error callback function */ + +/* This structure keeps track of the bd and buffer space usage. */ +typedef struct i2c_state { + int rx_idx; /* index to next free Rx BD */ + int tx_idx; /* index to next free Tx BD */ + void *rxbd; /* pointer to next free Rx BD */ + void *txbd; /* pointer to next free Tx BD */ + int tx_space; /* number of Tx bytes left */ + unsigned char *tx_buf; /* pointer to free Tx area */ + i2c_ecb_t err_cb; /* error callback function */ +} i2c_state_t; + + +/* flags for i2c_send() and i2c_receive() */ +#define I2CF_ENABLE_SECONDARY 0x01 /* secondary_address is valid */ +#define I2CF_START_COND 0x02 /* tx: generate start condition */ +#define I2CF_STOP_COND 0x04 /* tx: generate stop condition */ + +/* return codes */ +#define I2CERR_NO_BUFFERS 0x01 /* no more BDs or buffer space */ +#define I2CERR_MSG_TOO_LONG 0x02 /* tried to send/receive to much data */ +#define I2CERR_TIMEOUT 0x03 /* timeout in i2c_doio() */ +#define I2CERR_QUEUE_EMPTY 0x04 /* i2c_doio called without send/receive */ + +/* error callback flags */ +#define I2CECB_RX_ERR 0x10 /* this is a receive error */ +#define I2CECB_RX_ERR_OV 0x02 /* receive overrun error */ +#define I2CECB_RX_MASK 0x0f /* mask for error bits */ +#define I2CECB_TX_ERR 0x20 /* this is a transmit error */ +#define I2CECB_TX_CL 0x01 /* transmit collision error */ +#define I2CECB_TX_UN 0x02 /* transmit underflow error */ +#define I2CECB_TX_NAK 0x04 /* transmit no ack error */ +#define I2CECB_TX_MASK 0x0f /* mask for error bits */ +#define I2CECB_TIMEOUT 0x40 /* this is a timeout error */ + +#ifdef DEBUG_I2C +#define PRINTD(x) printf x +#else +#define PRINTD(x) +#endif + +/* + * Returns the best value of I2BRG to meet desired clock speed of I2C with + * input parameters (clock speed, filter, and predivider value). + * It returns computer speed value and the difference between it and desired + * speed. + */ +static inline int +i2c_roundrate(int hz, int speed, int filter, int modval, + int *brgval, int *totspeed) +{ + int moddiv = 1 << (5-(modval & 3)), brgdiv, div; + + PRINTD(("\t[I2C] trying hz=%d, speed=%d, filter=%d, modval=%d\n", + hz, speed, filter, modval)); + + div = moddiv * speed; + brgdiv = (hz + div - 1) / div; + + PRINTD(("\t\tmoddiv=%d, brgdiv=%d\n", moddiv, brgdiv)); + + *brgval = ((brgdiv + 1) / 2) - 3 - (2*filter); + + if ((*brgval < 0) || (*brgval > 255)) { + PRINTD(("\t\trejected brgval=%d\n", *brgval)); + return -1; + } + + brgdiv = 2 * (*brgval + 3 + (2 * filter)); + div = moddiv * brgdiv ; + *totspeed = hz / div; + + PRINTD(("\t\taccepted brgval=%d, totspeed=%d\n", *brgval, *totspeed)); + + return 0; +} + +/* + * Sets the I2C clock predivider and divider to meet required clock speed. + */ +static int +i2c_setrate (int hz, int speed) +{ + immap_t *immap = (immap_t *) CONFIG_SYS_IMMR; + volatile i2c8xx_t *i2c = (i2c8xx_t *) & immap->im_i2c; + int brgval, + modval, /* 0-3 */ + bestspeed_diff = speed, + bestspeed_brgval = 0, + bestspeed_modval = 0, + bestspeed_filter = 0, + totspeed, + filter = 0; /* Use this fixed value */ + + for (modval = 0; modval < 4; modval++) { + if (i2c_roundrate(hz,speed,filter,modval,&brgval,&totspeed) == 0) { + int diff = speed - totspeed; + + if ((diff >= 0) && (diff < bestspeed_diff)) { + bestspeed_diff = diff; + bestspeed_modval = modval; + bestspeed_brgval = brgval; + bestspeed_filter = filter; + } + } + } + + PRINTD (("[I2C] Best is:\n")); + PRINTD (("[I2C] CPU=%dhz RATE=%d F=%d I2MOD=%08x I2BRG=%08x DIFF=%dhz\n", + hz, + speed, + bestspeed_filter, + bestspeed_modval, + bestspeed_brgval, + bestspeed_diff)); + + i2c->i2c_i2mod |= ((bestspeed_modval & 3) << 1) | (bestspeed_filter << 3); + i2c->i2c_i2brg = bestspeed_brgval & 0xff; + + PRINTD (("[I2C] i2mod=%08x i2brg=%08x\n", i2c->i2c_i2mod, + i2c->i2c_i2brg)); + + return 1; +} + +void +i2c_init(int speed, int slaveaddr) +{ + volatile immap_t *immap = (immap_t *)CONFIG_SYS_IMMR ; + volatile cpm8xx_t *cp = (cpm8xx_t *)&immap->im_cpm; + volatile i2c8xx_t *i2c = (i2c8xx_t *)&immap->im_i2c; + volatile iic_t *iip = (iic_t *)&cp->cp_dparam[PROFF_IIC]; + ulong rbase, tbase; + volatile I2C_BD *rxbd, *txbd; + uint dpaddr; + +#ifdef CONFIG_SYS_I2C_INIT_BOARD + /* call board specific i2c bus reset routine before accessing the */ + /* environment, which might be in a chip on that bus. For details */ + /* about this problem see doc/I2C_Edge_Conditions. */ + i2c_init_board(); +#endif + +#ifdef CONFIG_SYS_I2C_UCODE_PATCH + iip = (iic_t *)&cp->cp_dpmem[iip->iic_rpbase]; +#else + /* Disable relocation */ + iip->iic_rpbase = 0; +#endif + +#ifdef CONFIG_SYS_ALLOC_DPRAM + dpaddr = iip->iic_rbase; + if (dpaddr == 0) { + /* need to allocate dual port ram */ + dpaddr = dpram_alloc_align( + (NUM_RX_BDS * sizeof(I2C_BD)) + (NUM_TX_BDS * sizeof(I2C_BD)) + + MAX_TX_SPACE, 8); + } +#else + dpaddr = CPM_I2C_BASE; +#endif + + /* + * initialise data in dual port ram: + * + * dpaddr->rbase -> rx BD (NUM_RX_BDS * sizeof(I2C_BD) bytes) + * tbase -> tx BD (NUM_TX_BDS * sizeof(I2C_BD) bytes) + * tx buffer (MAX_TX_SPACE bytes) + */ + + rbase = dpaddr; + tbase = rbase + NUM_RX_BDS * sizeof(I2C_BD); + + /* Initialize Port B I2C pins. */ + cp->cp_pbpar |= 0x00000030; + cp->cp_pbdir |= 0x00000030; + cp->cp_pbodr |= 0x00000030; + + /* Disable interrupts */ + i2c->i2c_i2mod = 0x00; + i2c->i2c_i2cmr = 0x00; + i2c->i2c_i2cer = 0xff; + i2c->i2c_i2add = slaveaddr; + + /* + * Set the I2C BRG Clock division factor from desired i2c rate + * and current CPU rate (we assume sccr dfbgr field is 0; + * divide BRGCLK by 1) + */ + PRINTD(("[I2C] Setting rate...\n")); + i2c_setrate (gd->cpu_clk, CONFIG_SYS_I2C_SPEED) ; + + /* Set I2C controller in master mode */ + i2c->i2c_i2com = 0x01; + + /* Set SDMA bus arbitration level to 5 (SDCR) */ + immap->im_siu_conf.sc_sdcr = 0x0001 ; + + /* Initialize Tx/Rx parameters */ + iip->iic_rbase = rbase; + iip->iic_tbase = tbase; + rxbd = (I2C_BD *)((unsigned char *)&cp->cp_dpmem[iip->iic_rbase]); + txbd = (I2C_BD *)((unsigned char *)&cp->cp_dpmem[iip->iic_tbase]); + + PRINTD(("[I2C] rbase = %04x\n", iip->iic_rbase)); + PRINTD(("[I2C] tbase = %04x\n", iip->iic_tbase)); + PRINTD(("[I2C] rxbd = %08x\n", (int)rxbd)); + PRINTD(("[I2C] txbd = %08x\n", (int)txbd)); + + /* Set big endian byte order */ + iip->iic_tfcr = 0x10; + iip->iic_rfcr = 0x10; + + /* Set maximum receive size. */ + iip->iic_mrblr = I2C_RXTX_LEN; + +#ifdef CONFIG_SYS_I2C_UCODE_PATCH + /* + * Initialize required parameters if using microcode patch. + */ + iip->iic_rbptr = iip->iic_rbase; + iip->iic_tbptr = iip->iic_tbase; + iip->iic_rstate = 0; + iip->iic_tstate = 0; +#else + cp->cp_cpcr = mk_cr_cmd(CPM_CR_CH_I2C, CPM_CR_INIT_TRX) | CPM_CR_FLG; + do { + __asm__ __volatile__ ("eieio"); + } while (cp->cp_cpcr & CPM_CR_FLG); +#endif + + /* Clear events and interrupts */ + i2c->i2c_i2cer = 0xff; + i2c->i2c_i2cmr = 0x00; +} + +static void +i2c_newio(i2c_state_t *state) +{ + volatile immap_t *immap = (immap_t *)CONFIG_SYS_IMMR ; + volatile cpm8xx_t *cp = (cpm8xx_t *)&immap->im_cpm; + volatile iic_t *iip = (iic_t *)&cp->cp_dparam[PROFF_IIC]; + + PRINTD(("[I2C] i2c_newio\n")); + +#ifdef CONFIG_SYS_I2C_UCODE_PATCH + iip = (iic_t *)&cp->cp_dpmem[iip->iic_rpbase]; +#endif + state->rx_idx = 0; + state->tx_idx = 0; + state->rxbd = (void*)&cp->cp_dpmem[iip->iic_rbase]; + state->txbd = (void*)&cp->cp_dpmem[iip->iic_tbase]; + state->tx_space = MAX_TX_SPACE; + state->tx_buf = (uchar*)state->txbd + NUM_TX_BDS * sizeof(I2C_BD); + state->err_cb = NULL; + + PRINTD(("[I2C] rxbd = %08x\n", (int)state->rxbd)); + PRINTD(("[I2C] txbd = %08x\n", (int)state->txbd)); + PRINTD(("[I2C] tx_buf = %08x\n", (int)state->tx_buf)); + + /* clear the buffer memory */ + memset((char *)state->tx_buf, 0, MAX_TX_SPACE); +} + +static int +i2c_send(i2c_state_t *state, + unsigned char address, + unsigned char secondary_address, + unsigned int flags, + unsigned short size, + unsigned char *dataout) +{ + volatile I2C_BD *txbd; + int i,j; + + PRINTD(("[I2C] i2c_send add=%02d sec=%02d flag=%02d size=%d\n", + address, secondary_address, flags, size)); + + /* trying to send message larger than BD */ + if (size > I2C_RXTX_LEN) + return I2CERR_MSG_TOO_LONG; + + /* no more free bds */ + if (state->tx_idx >= NUM_TX_BDS || state->tx_space < (2 + size)) + return I2CERR_NO_BUFFERS; + + txbd = (I2C_BD *)state->txbd; + txbd->addr = state->tx_buf; + + PRINTD(("[I2C] txbd = %08x\n", (int)txbd)); + + if (flags & I2CF_START_COND) { + PRINTD(("[I2C] Formatting addresses...\n")); + if (flags & I2CF_ENABLE_SECONDARY) { + txbd->length = size + 2; /* Length of msg + dest addr */ + txbd->addr[0] = address << 1; + txbd->addr[1] = secondary_address; + i = 2; + } else { + txbd->length = size + 1; /* Length of msg + dest addr */ + txbd->addr[0] = address << 1; /* Write dest addr to BD */ + i = 1; + } + } else { + txbd->length = size; /* Length of message */ + i = 0; + } + + /* set up txbd */ + txbd->status = BD_SC_READY; + if (flags & I2CF_START_COND) + txbd->status |= BD_I2C_TX_START; + if (flags & I2CF_STOP_COND) + txbd->status |= BD_SC_LAST | BD_SC_WRAP; + + /* Copy data to send into buffer */ + PRINTD(("[I2C] copy data...\n")); + for(j = 0; j < size; i++, j++) + txbd->addr[i] = dataout[j]; + + PRINTD(("[I2C] txbd: length=0x%04x status=0x%04x addr[0]=0x%02x addr[1]=0x%02x\n", + txbd->length, + txbd->status, + txbd->addr[0], + txbd->addr[1])); + + /* advance state */ + state->tx_buf += txbd->length; + state->tx_space -= txbd->length; + state->tx_idx++; + state->txbd = (void*)(txbd + 1); + + return 0; +} + +static int +i2c_receive(i2c_state_t *state, + unsigned char address, + unsigned char secondary_address, + unsigned int flags, + unsigned short size_to_expect, + unsigned char *datain) +{ + volatile I2C_BD *rxbd, *txbd; + + PRINTD(("[I2C] i2c_receive %02d %02d %02d\n", address, secondary_address, flags)); + + /* Expected to receive too much */ + if (size_to_expect > I2C_RXTX_LEN) + return I2CERR_MSG_TOO_LONG; + + /* no more free bds */ + if (state->tx_idx >= NUM_TX_BDS || state->rx_idx >= NUM_RX_BDS + || state->tx_space < 2) + return I2CERR_NO_BUFFERS; + + rxbd = (I2C_BD *)state->rxbd; + txbd = (I2C_BD *)state->txbd; + + PRINTD(("[I2C] rxbd = %08x\n", (int)rxbd)); + PRINTD(("[I2C] txbd = %08x\n", (int)txbd)); + + txbd->addr = state->tx_buf; + + /* set up TXBD for destination address */ + if (flags & I2CF_ENABLE_SECONDARY) { + txbd->length = 2; + txbd->addr[0] = address << 1; /* Write data */ + txbd->addr[1] = secondary_address; /* Internal address */ + txbd->status = BD_SC_READY; + } else { + txbd->length = 1 + size_to_expect; + txbd->addr[0] = (address << 1) | 0x01; + txbd->status = BD_SC_READY; + memset(&txbd->addr[1], 0, txbd->length); + } + + /* set up rxbd for reception */ + rxbd->status = BD_SC_EMPTY; + rxbd->length = size_to_expect; + rxbd->addr = datain; + + txbd->status |= BD_I2C_TX_START; + if (flags & I2CF_STOP_COND) { + txbd->status |= BD_SC_LAST | BD_SC_WRAP; + rxbd->status |= BD_SC_WRAP; + } + + PRINTD(("[I2C] txbd: length=0x%04x status=0x%04x addr[0]=0x%02x addr[1]=0x%02x\n", + txbd->length, + txbd->status, + txbd->addr[0], + txbd->addr[1])); + PRINTD(("[I2C] rxbd: length=0x%04x status=0x%04x addr[0]=0x%02x addr[1]=0x%02x\n", + rxbd->length, + rxbd->status, + rxbd->addr[0], + rxbd->addr[1])); + + /* advance state */ + state->tx_buf += txbd->length; + state->tx_space -= txbd->length; + state->tx_idx++; + state->txbd = (void*)(txbd + 1); + state->rx_idx++; + state->rxbd = (void*)(rxbd + 1); + + return 0; +} + + +static int i2c_doio(i2c_state_t *state) +{ + volatile immap_t *immap = (immap_t *)CONFIG_SYS_IMMR ; + volatile cpm8xx_t *cp = (cpm8xx_t *)&immap->im_cpm; + volatile i2c8xx_t *i2c = (i2c8xx_t *)&immap->im_i2c; + volatile iic_t *iip = (iic_t *)&cp->cp_dparam[PROFF_IIC]; + volatile I2C_BD *txbd, *rxbd; + volatile int j = 0; + + PRINTD(("[I2C] i2c_doio\n")); + +#ifdef CONFIG_SYS_I2C_UCODE_PATCH + iip = (iic_t *)&cp->cp_dpmem[iip->iic_rpbase]; +#endif + + if (state->tx_idx <= 0 && state->rx_idx <= 0) { + PRINTD(("[I2C] No I/O is queued\n")); + return I2CERR_QUEUE_EMPTY; + } + + iip->iic_rbptr = iip->iic_rbase; + iip->iic_tbptr = iip->iic_tbase; + + /* Enable I2C */ + PRINTD(("[I2C] Enabling I2C...\n")); + i2c->i2c_i2mod |= 0x01; + + /* Begin transmission */ + i2c->i2c_i2com |= 0x80; + + /* Loop until transmit & receive completed */ + + if (state->tx_idx > 0) { + txbd = ((I2C_BD*)state->txbd) - 1; + PRINTD(("[I2C] Transmitting...(txbd=0x%08lx)\n", (ulong)txbd)); + while((txbd->status & BD_SC_READY) && (j++ < TOUT_LOOP)) { + if (ctrlc()) { + return (-1); + } + __asm__ __volatile__ ("eieio"); + } + } + + if ((state->rx_idx > 0) && (j < TOUT_LOOP)) { + rxbd = ((I2C_BD*)state->rxbd) - 1; + PRINTD(("[I2C] Receiving...(rxbd=0x%08lx)\n", (ulong)rxbd)); + while((rxbd->status & BD_SC_EMPTY) && (j++ < TOUT_LOOP)) { + if (ctrlc()) { + return (-1); + } + __asm__ __volatile__ ("eieio"); + } + } + + /* Turn off I2C */ + i2c->i2c_i2mod &= ~0x01; + + if (state->err_cb != NULL) { + int n, i, b; + + /* + * if we have an error callback function, look at the + * error bits in the bd status and pass them back + */ + + if ((n = state->tx_idx) > 0) { + for (i = 0; i < n; i++) { + txbd = ((I2C_BD*)state->txbd) - (n - i); + if ((b = txbd->status & BD_I2C_TX_ERR) != 0) + (*state->err_cb)(I2CECB_TX_ERR|b, i); + } + } + + if ((n = state->rx_idx) > 0) { + for (i = 0; i < n; i++) { + rxbd = ((I2C_BD*)state->rxbd) - (n - i); + if ((b = rxbd->status & BD_I2C_RX_ERR) != 0) + (*state->err_cb)(I2CECB_RX_ERR|b, i); + } + } + + if (j >= TOUT_LOOP) + (*state->err_cb)(I2CECB_TIMEOUT, 0); + } + + return (j >= TOUT_LOOP) ? I2CERR_TIMEOUT : 0; +} + +static int had_tx_nak; + +static void +i2c_test_callback(int flags, int xnum) +{ + if ((flags & I2CECB_TX_ERR) && (flags & I2CECB_TX_NAK)) + had_tx_nak = 1; +} + +int i2c_probe(uchar chip) +{ + i2c_state_t state; + int rc; + uchar buf[1]; + + i2c_init(CONFIG_SYS_I2C_SPEED, CONFIG_SYS_I2C_SLAVE); + + i2c_newio(&state); + + state.err_cb = i2c_test_callback; + had_tx_nak = 0; + + rc = i2c_receive(&state, chip, 0, I2CF_START_COND|I2CF_STOP_COND, 1, buf); + + if (rc != 0) + return (rc); + + rc = i2c_doio(&state); + + if ((rc != 0) && (rc != I2CERR_TIMEOUT)) + return (rc); + + return (had_tx_nak); +} + +int i2c_read(uchar chip, uint addr, int alen, uchar *buffer, int len) +{ + i2c_state_t state; + uchar xaddr[4]; + int rc; + +#ifdef CONFIG_LWMON + WATCHDOG_RESET(); +#endif + + xaddr[0] = (addr >> 24) & 0xFF; + xaddr[1] = (addr >> 16) & 0xFF; + xaddr[2] = (addr >> 8) & 0xFF; + xaddr[3] = addr & 0xFF; + +#ifdef CONFIG_SYS_I2C_EEPROM_ADDR_OVERFLOW + /* + * EEPROM chips that implement "address overflow" are ones like + * Catalyst 24WC04/08/16 which has 9/10/11 bits of address and the + * extra bits end up in the "chip address" bit slots. This makes + * a 24WC08 (1Kbyte) chip look like four 256 byte chips. + * + * Note that we consider the length of the address field to still + * be one byte because the extra address bits are hidden in the + * chip address. + */ + chip |= ((addr >> (alen * 8)) & CONFIG_SYS_I2C_EEPROM_ADDR_OVERFLOW); +#endif + + i2c_newio(&state); + + rc = i2c_send(&state, chip, 0, I2CF_START_COND, alen, &xaddr[4-alen]); + if (rc != 0) { + if (gd->have_console) + printf("i2c_read: i2c_send failed (%d)\n", rc); + return 1; + } + + rc = i2c_receive(&state, chip, 0, I2CF_STOP_COND, len, buffer); + if (rc != 0) { + if (gd->have_console) + printf("i2c_read: i2c_receive failed (%d)\n", rc); + return 1; + } + + rc = i2c_doio(&state); + if (rc != 0) { + if (gd->have_console) + printf("i2c_read: i2c_doio failed (%d)\n", rc); + return 1; + } + return 0; +} + +int i2c_write(uchar chip, uint addr, int alen, uchar *buffer, int len) +{ + i2c_state_t state; + uchar xaddr[4]; + int rc; + + xaddr[0] = (addr >> 24) & 0xFF; + xaddr[1] = (addr >> 16) & 0xFF; + xaddr[2] = (addr >> 8) & 0xFF; + xaddr[3] = addr & 0xFF; + +#ifdef CONFIG_SYS_I2C_EEPROM_ADDR_OVERFLOW + /* + * EEPROM chips that implement "address overflow" are ones like + * Catalyst 24WC04/08/16 which has 9/10/11 bits of address and the + * extra bits end up in the "chip address" bit slots. This makes + * a 24WC08 (1Kbyte) chip look like four 256 byte chips. + * + * Note that we consider the length of the address field to still + * be one byte because the extra address bits are hidden in the + * chip address. + */ + chip |= ((addr >> (alen * 8)) & CONFIG_SYS_I2C_EEPROM_ADDR_OVERFLOW); +#endif + + i2c_newio(&state); + + rc = i2c_send(&state, chip, 0, I2CF_START_COND, alen, &xaddr[4-alen]); + if (rc != 0) { + if (gd->have_console) + printf("i2c_write: first i2c_send failed (%d)\n", rc); + return 1; + } + + rc = i2c_send(&state, 0, 0, I2CF_STOP_COND, len, buffer); + if (rc != 0) { + if (gd->have_console) + printf("i2c_write: second i2c_send failed (%d)\n", rc); + return 1; + } + + rc = i2c_doio(&state); + if (rc != 0) { + if (gd->have_console) + printf("i2c_write: i2c_doio failed (%d)\n", rc); + return 1; + } + return 0; +} + +#endif /* CONFIG_HARD_I2C */ diff --git a/arch/powerpc/cpu/mpc8xx/interrupts.c b/arch/powerpc/cpu/mpc8xx/interrupts.c new file mode 100644 index 0000000..5daa6b2 --- /dev/null +++ b/arch/powerpc/cpu/mpc8xx/interrupts.c @@ -0,0 +1,294 @@ +/* + * (C) Copyright 2000-2002 + * Wolfgang Denk, DENX Software Engineering, wd@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> +#include <mpc8xx.h> +#include <mpc8xx_irq.h> +#include <asm/processor.h> +#include <commproc.h> + +/************************************************************************/ + +/* + * CPM interrupt vector functions. + */ +struct interrupt_action { + interrupt_handler_t *handler; + void *arg; +}; + +static struct interrupt_action cpm_vecs[CPMVEC_NR]; +static struct interrupt_action irq_vecs[NR_IRQS]; + +static void cpm_interrupt_init (void); +static void cpm_interrupt (void *regs); + +/************************************************************************/ + +int interrupt_init_cpu (unsigned *decrementer_count) +{ + volatile immap_t *immr = (immap_t *) CONFIG_SYS_IMMR; + + *decrementer_count = get_tbclk () / CONFIG_SYS_HZ; + + /* disable all interrupts */ + immr->im_siu_conf.sc_simask = 0; + + /* Configure CPM interrupts */ + cpm_interrupt_init (); + + return (0); +} + +/************************************************************************/ + +/* + * Handle external interrupts + */ +void external_interrupt (struct pt_regs *regs) +{ + volatile immap_t *immr = (immap_t *) CONFIG_SYS_IMMR; + int irq; + ulong simask, newmask; + ulong vec, v_bit; + + /* + * read the SIVEC register and shift the bits down + * to get the irq number + */ + vec = immr->im_siu_conf.sc_sivec; + irq = vec >> 26; + v_bit = 0x80000000UL >> irq; + + /* + * Read Interrupt Mask Register and Mask Interrupts + */ + simask = immr->im_siu_conf.sc_simask; + newmask = simask & (~(0xFFFF0000 >> irq)); + immr->im_siu_conf.sc_simask = newmask; + + if (!(irq & 0x1)) { /* External Interrupt ? */ + ulong siel; + + /* + * Read Interrupt Edge/Level Register + */ + siel = immr->im_siu_conf.sc_siel; + + if (siel & v_bit) { /* edge triggered interrupt ? */ + /* + * Rewrite SIPEND Register to clear interrupt + */ + immr->im_siu_conf.sc_sipend = v_bit; + } + } + + if (irq_vecs[irq].handler != NULL) { + irq_vecs[irq].handler (irq_vecs[irq].arg); + } else { + printf ("\nBogus External Interrupt IRQ %d Vector %ld\n", + irq, vec); + /* turn off the bogus interrupt to avoid it from now */ + simask &= ~v_bit; + } + /* + * Re-Enable old Interrupt Mask + */ + immr->im_siu_conf.sc_simask = simask; +} + +/************************************************************************/ + +/* + * CPM interrupt handler + */ +static void cpm_interrupt (void *regs) +{ + volatile immap_t *immr = (immap_t *) CONFIG_SYS_IMMR; + uint vec; + + /* + * Get the vector by setting the ACK bit + * and then reading the register. + */ + immr->im_cpic.cpic_civr = 1; + vec = immr->im_cpic.cpic_civr; + vec >>= 11; + + if (cpm_vecs[vec].handler != NULL) { + (*cpm_vecs[vec].handler) (cpm_vecs[vec].arg); + } else { + immr->im_cpic.cpic_cimr &= ~(1 << vec); + printf ("Masking bogus CPM interrupt vector 0x%x\n", vec); + } + /* + * After servicing the interrupt, + * we have to remove the status indicator. + */ + immr->im_cpic.cpic_cisr |= (1 << vec); +} + +/* + * The CPM can generate the error interrupt when there is a race + * condition between generating and masking interrupts. All we have + * to do is ACK it and return. This is a no-op function so we don't + * need any special tests in the interrupt handler. + */ +static void cpm_error_interrupt (void *dummy) +{ +} + +/************************************************************************/ +/* + * Install and free an interrupt handler + */ +void irq_install_handler (int vec, interrupt_handler_t * handler, + void *arg) +{ + volatile immap_t *immr = (immap_t *) CONFIG_SYS_IMMR; + + if ((vec & CPMVEC_OFFSET) != 0) { + /* CPM interrupt */ + vec &= 0xffff; + if (cpm_vecs[vec].handler != NULL) { + printf ("CPM interrupt 0x%x replacing 0x%x\n", + (uint) handler, + (uint) cpm_vecs[vec].handler); + } + cpm_vecs[vec].handler = handler; + cpm_vecs[vec].arg = arg; + immr->im_cpic.cpic_cimr |= (1 << vec); +#if 0 + printf ("Install CPM interrupt for vector %d ==> %p\n", + vec, handler); +#endif + } else { + /* SIU interrupt */ + if (irq_vecs[vec].handler != NULL) { + printf ("SIU interrupt %d 0x%x replacing 0x%x\n", + vec, + (uint) handler, + (uint) cpm_vecs[vec].handler); + } + irq_vecs[vec].handler = handler; + irq_vecs[vec].arg = arg; + immr->im_siu_conf.sc_simask |= 1 << (31 - vec); +#if 0 + printf ("Install SIU interrupt for vector %d ==> %p\n", + vec, handler); +#endif + } +} + +void irq_free_handler (int vec) +{ + volatile immap_t *immr = (immap_t *) CONFIG_SYS_IMMR; + + if ((vec & CPMVEC_OFFSET) != 0) { + /* CPM interrupt */ + vec &= 0xffff; +#if 0 + printf ("Free CPM interrupt for vector %d ==> %p\n", + vec, cpm_vecs[vec].handler); +#endif + immr->im_cpic.cpic_cimr &= ~(1 << vec); + cpm_vecs[vec].handler = NULL; + cpm_vecs[vec].arg = NULL; + } else { + /* SIU interrupt */ +#if 0 + printf ("Free CPM interrupt for vector %d ==> %p\n", + vec, cpm_vecs[vec].handler); +#endif + immr->im_siu_conf.sc_simask &= ~(1 << (31 - vec)); + irq_vecs[vec].handler = NULL; + irq_vecs[vec].arg = NULL; + } +} + +/************************************************************************/ + +static void cpm_interrupt_init (void) +{ + volatile immap_t *immr = (immap_t *) CONFIG_SYS_IMMR; + + /* + * Initialize the CPM interrupt controller. + */ + + immr->im_cpic.cpic_cicr = + (CICR_SCD_SCC4 | + CICR_SCC_SCC3 | + CICR_SCB_SCC2 | + CICR_SCA_SCC1) | ((CPM_INTERRUPT / 2) << 13) | CICR_HP_MASK; + + immr->im_cpic.cpic_cimr = 0; + + /* + * Install the error handler. + */ + irq_install_handler (CPMVEC_ERROR, cpm_error_interrupt, NULL); + + immr->im_cpic.cpic_cicr |= CICR_IEN; + + /* + * Install the cpm interrupt handler + */ + irq_install_handler (CPM_INTERRUPT, cpm_interrupt, NULL); +} + +/************************************************************************/ + +/* + * timer_interrupt - gets called when the decrementer overflows, + * with interrupts disabled. + * Trivial implementation - no need to be really accurate. + */ +void timer_interrupt_cpu (struct pt_regs *regs) +{ + volatile immap_t *immr = (immap_t *) CONFIG_SYS_IMMR; + +#if 0 + printf ("*** Timer Interrupt *** "); +#endif + /* Reset Timer Expired and Timers Interrupt Status */ + immr->im_clkrstk.cark_plprcrk = KAPWR_KEY; + __asm__ ("nop"); + /* + Clear TEXPS (and TMIST on older chips). SPLSS (on older + chips) is cleared too. + + Bitwise OR is a read-modify-write operation so ALL bits + which are cleared by writing `1' would be cleared by + operations like + + immr->im_clkrst.car_plprcr |= PLPRCR_TEXPS; + + The same can be achieved by simple writing of the PLPRCR + to itself. If a bit value should be preserved, read the + register, ZERO the bit and write, not OR, the result back. + */ + immr->im_clkrst.car_plprcr = immr->im_clkrst.car_plprcr; +} + +/************************************************************************/ diff --git a/arch/powerpc/cpu/mpc8xx/kgdb.S b/arch/powerpc/cpu/mpc8xx/kgdb.S new file mode 100644 index 0000000..2cc8fe6 --- /dev/null +++ b/arch/powerpc/cpu/mpc8xx/kgdb.S @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2000 Murray Jensen <Murray.Jensen@cmst.csiro.au> + * + * 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 <config.h> +#include <command.h> +#include <mpc8xx.h> +#include <version.h> + +#define CONFIG_8xx 1 /* needed for Linux kernel header files */ +#define _LINUX_CONFIG_H 1 /* avoid reading Linux autoconf.h file */ + +#include <ppc_asm.tmpl> +#include <ppc_defs.h> + +#include <asm/cache.h> +#include <asm/mmu.h> + +#if defined(CONFIG_CMD_KGDB) + + /* + * cache flushing routines for kgdb + */ + + .globl kgdb_flush_cache_all +kgdb_flush_cache_all: + lis r3, IDC_INVALL@h + mtspr DC_CST, r3 + sync + lis r3, IDC_INVALL@h + mtspr IC_CST, r3 + SYNC + blr + + .globl kgdb_flush_cache_range +kgdb_flush_cache_range: + li r5,CONFIG_SYS_CACHELINE_SIZE-1 + andc r3,r3,r5 + subf r4,r3,r4 + add r4,r4,r5 + srwi. r4,r4,CONFIG_SYS_CACHELINE_SHIFT + beqlr + mtctr r4 + mr r6,r3 +1: dcbst 0,r3 + addi r3,r3,CONFIG_SYS_CACHELINE_SIZE + bdnz 1b + sync /* wait for dcbst's to get to ram */ + mtctr r4 +2: icbi 0,r6 + addi r6,r6,CONFIG_SYS_CACHELINE_SIZE + bdnz 2b + SYNC + blr + +#endif diff --git a/arch/powerpc/cpu/mpc8xx/lcd.c b/arch/powerpc/cpu/mpc8xx/lcd.c new file mode 100644 index 0000000..4b88b21 --- /dev/null +++ b/arch/powerpc/cpu/mpc8xx/lcd.c @@ -0,0 +1,621 @@ +/* + * (C) Copyright 2001-2002 + * Wolfgang Denk, DENX Software Engineering -- wd@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 + */ + +/************************************************************************/ +/* ** HEADER FILES */ +/************************************************************************/ + +/* #define DEBUG */ + +#include <config.h> +#include <common.h> +#include <command.h> +#include <watchdog.h> +#include <version.h> +#include <stdarg.h> +#include <lcdvideo.h> +#include <linux/types.h> +#include <stdio_dev.h> +#if defined(CONFIG_POST) +#include <post.h> +#endif +#include <lcd.h> + +#ifdef CONFIG_LCD + +/************************************************************************/ +/* ** CONFIG STUFF -- should be moved to board config file */ +/************************************************************************/ +#ifndef CONFIG_LCD_INFO +#define CONFIG_LCD_INFO /* Display Logo, (C) and system info */ +#endif + +#if defined(CONFIG_V37) || defined(CONFIG_EDT32F10) +#undef CONFIG_LCD_LOGO +#undef CONFIG_LCD_INFO +#endif + +/*----------------------------------------------------------------------*/ +#ifdef CONFIG_KYOCERA_KCS057QV1AJ +/* + * Kyocera KCS057QV1AJ-G23. Passive, color, single scan. + */ +#define LCD_BPP LCD_COLOR4 + +vidinfo_t panel_info = { + 640, 480, 132, 99, CONFIG_SYS_HIGH, CONFIG_SYS_HIGH, CONFIG_SYS_HIGH, CONFIG_SYS_HIGH, CONFIG_SYS_HIGH, + LCD_BPP, 1, 0, 1, 0, 5, 0, 0, 0 + /* wbl, vpw, lcdac, wbf */ +}; +#endif /* CONFIG_KYOCERA_KCS057QV1AJ */ +/*----------------------------------------------------------------------*/ + +/*----------------------------------------------------------------------*/ +#ifdef CONFIG_HITACHI_SP19X001_Z1A +/* + * Hitachi SP19X001-. Active, color, single scan. + */ +vidinfo_t panel_info = { + 640, 480, 154, 116, CONFIG_SYS_HIGH, CONFIG_SYS_HIGH, CONFIG_SYS_HIGH, CONFIG_SYS_HIGH, CONFIG_SYS_HIGH, + LCD_COLOR8, 1, 0, 1, 0, 0, 0, 0, 0 + /* wbl, vpw, lcdac, wbf */ +}; +#endif /* CONFIG_HITACHI_SP19X001_Z1A */ +/*----------------------------------------------------------------------*/ + +/*----------------------------------------------------------------------*/ +#ifdef CONFIG_NEC_NL6448AC33 +/* + * NEC NL6448AC33-18. Active, color, single scan. + */ +vidinfo_t panel_info = { + 640, 480, 132, 99, CONFIG_SYS_HIGH, CONFIG_SYS_HIGH, CONFIG_SYS_LOW, CONFIG_SYS_LOW, CONFIG_SYS_HIGH, + 3, 0, 0, 1, 1, 144, 2, 0, 33 + /* wbl, vpw, lcdac, wbf */ +}; +#endif /* CONFIG_NEC_NL6448AC33 */ +/*----------------------------------------------------------------------*/ + +#ifdef CONFIG_NEC_NL6448BC20 +/* + * NEC NL6448BC20-08. 6.5", 640x480. Active, color, single scan. + */ +vidinfo_t panel_info = { + 640, 480, 132, 99, CONFIG_SYS_HIGH, CONFIG_SYS_HIGH, CONFIG_SYS_LOW, CONFIG_SYS_LOW, CONFIG_SYS_HIGH, + 3, 0, 0, 1, 1, 144, 2, 0, 33 + /* wbl, vpw, lcdac, wbf */ +}; +#endif /* CONFIG_NEC_NL6448BC20 */ +/*----------------------------------------------------------------------*/ + +#ifdef CONFIG_NEC_NL6448BC33_54 +/* + * NEC NL6448BC33-54. 10.4", 640x480. Active, color, single scan. + */ +vidinfo_t panel_info = { + 640, 480, 212, 158, CONFIG_SYS_HIGH, CONFIG_SYS_HIGH, CONFIG_SYS_LOW, CONFIG_SYS_LOW, CONFIG_SYS_HIGH, + 3, 0, 0, 1, 1, 144, 2, 0, 33 + /* wbl, vpw, lcdac, wbf */ +}; +#endif /* CONFIG_NEC_NL6448BC33_54 */ +/*----------------------------------------------------------------------*/ + +#ifdef CONFIG_SHARP_LQ104V7DS01 +/* + * SHARP LQ104V7DS01. 6.5", 640x480. Active, color, single scan. + */ +vidinfo_t panel_info = { + 640, 480, 132, 99, CONFIG_SYS_HIGH, CONFIG_SYS_HIGH, CONFIG_SYS_LOW, CONFIG_SYS_LOW, CONFIG_SYS_LOW, + 3, 0, 0, 1, 1, 25, 1, 0, 33 + /* wbl, vpw, lcdac, wbf */ +}; +#endif /* CONFIG_SHARP_LQ104V7DS01 */ +/*----------------------------------------------------------------------*/ + +#ifdef CONFIG_SHARP_16x9 +/* + * Sharp 320x240. Active, color, single scan. It isn't 16x9, and I am + * not sure what it is....... + */ +vidinfo_t panel_info = { + 320, 240, 0, 0, CONFIG_SYS_HIGH, CONFIG_SYS_HIGH, CONFIG_SYS_HIGH, CONFIG_SYS_HIGH, CONFIG_SYS_HIGH, + 3, 0, 0, 1, 1, 15, 4, 0, 3 +}; +#endif /* CONFIG_SHARP_16x9 */ +/*----------------------------------------------------------------------*/ + +#ifdef CONFIG_SHARP_LQ057Q3DC02 +/* + * Sharp LQ057Q3DC02 display. Active, color, single scan. + */ +#undef LCD_DF +#define LCD_DF 12 + +vidinfo_t panel_info = { + 320, 240, 0, 0, CONFIG_SYS_HIGH, CONFIG_SYS_HIGH, CONFIG_SYS_LOW, CONFIG_SYS_LOW, CONFIG_SYS_HIGH, + 3, 0, 0, 1, 1, 15, 4, 0, 3 + /* wbl, vpw, lcdac, wbf */ +}; +#define CONFIG_LCD_INFO_BELOW_LOGO +#endif /* CONFIG_SHARP_LQ057Q3DC02 */ +/*----------------------------------------------------------------------*/ + +#ifdef CONFIG_SHARP_LQ64D341 +/* + * Sharp LQ64D341 display, 640x480. Active, color, single scan. + */ +vidinfo_t panel_info = { + 640, 480, 0, 0, CONFIG_SYS_HIGH, CONFIG_SYS_HIGH, CONFIG_SYS_LOW, CONFIG_SYS_LOW, CONFIG_SYS_HIGH, + 3, 0, 0, 1, 1, 128, 16, 0, 32 + /* wbl, vpw, lcdac, wbf */ +}; +#endif /* CONFIG_SHARP_LQ64D341 */ + +#ifdef CONFIG_SHARP_LQ065T9DR51U +/* + * Sharp LQ065T9DR51U display, 400x240. Active, color, single scan. + */ +vidinfo_t panel_info = { + 400, 240, 143, 79, CONFIG_SYS_HIGH, CONFIG_SYS_HIGH, CONFIG_SYS_HIGH, CONFIG_SYS_HIGH, CONFIG_SYS_HIGH, + 3, 0, 0, 1, 1, 248, 4, 0, 35 + /* wbl, vpw, lcdac, wbf */ +}; +#define CONFIG_LCD_INFO_BELOW_LOGO +#endif /* CONFIG_SHARP_LQ065T9DR51U */ + +#ifdef CONFIG_SHARP_LQ084V1DG21 +/* + * Sharp LQ084V1DG21 display, 640x480. Active, color, single scan. + */ +vidinfo_t panel_info = { + 640, 480, 171, 129, CONFIG_SYS_HIGH, CONFIG_SYS_HIGH, CONFIG_SYS_LOW, CONFIG_SYS_LOW, CONFIG_SYS_LOW, + 3, 0, 0, 1, 1, 160, 3, 0, 48 + /* wbl, vpw, lcdac, wbf */ +}; +#endif /* CONFIG_SHARP_LQ084V1DG21 */ + +/*----------------------------------------------------------------------*/ + +#ifdef CONFIG_HLD1045 +/* + * HLD1045 display, 640x480. Active, color, single scan. + */ +vidinfo_t panel_info = { + 640, 480, 0, 0, CONFIG_SYS_HIGH, CONFIG_SYS_HIGH, CONFIG_SYS_LOW, CONFIG_SYS_LOW, CONFIG_SYS_HIGH, + 3, 0, 0, 1, 1, 160, 3, 0, 48 + /* wbl, vpw, lcdac, wbf */ +}; +#endif /* CONFIG_HLD1045 */ +/*----------------------------------------------------------------------*/ + +#ifdef CONFIG_PRIMEVIEW_V16C6448AC +/* + * Prime View V16C6448AC + */ +vidinfo_t panel_info = { + 640, 480, 130, 98, CONFIG_SYS_HIGH, CONFIG_SYS_HIGH, CONFIG_SYS_LOW, CONFIG_SYS_LOW, CONFIG_SYS_HIGH, + 3, 0, 0, 1, 1, 144, 2, 0, 35 + /* wbl, vpw, lcdac, wbf */ +}; +#endif /* CONFIG_PRIMEVIEW_V16C6448AC */ + +/*----------------------------------------------------------------------*/ + +#ifdef CONFIG_OPTREX_BW +/* + * Optrex CBL50840-2 NF-FW 99 22 M5 + * or + * Hitachi LMG6912RPFC-00T + * or + * Hitachi SP14Q002 + * + * 320x240. Black & white. + */ +#define OPTREX_BPP 0 /* 0 - monochrome, 1 bpp */ + /* 1 - 4 grey levels, 2 bpp */ + /* 2 - 16 grey levels, 4 bpp */ +vidinfo_t panel_info = { + 320, 240, 0, 0, CONFIG_SYS_HIGH, CONFIG_SYS_HIGH, CONFIG_SYS_HIGH, CONFIG_SYS_HIGH, CONFIG_SYS_LOW, + OPTREX_BPP, 0, 0, 0, 0, 0, 0, 0, 0, 4 +}; +#endif /* CONFIG_OPTREX_BW */ + +/*-----------------------------------------------------------------*/ +#ifdef CONFIG_EDT32F10 +/* + * Emerging Display Technologies 320x240. Passive, monochrome, single scan. + */ +#define LCD_BPP LCD_MONOCHROME +#define LCD_DF 10 + +vidinfo_t panel_info = { + 320, 240, 0, 0, CONFIG_SYS_HIGH, CONFIG_SYS_HIGH, CONFIG_SYS_HIGH, CONFIG_SYS_HIGH, CONFIG_SYS_LOW, + LCD_BPP, 0, 0, 0, 0, 33, 0, 0, 0 +}; +#endif +/*----------------------------------------------------------------------*/ + + +int lcd_line_length; + +int lcd_color_fg; +int lcd_color_bg; + +/* + * Frame buffer memory information + */ +void *lcd_base; /* Start of framebuffer memory */ +void *lcd_console_address; /* Start of console buffer */ + +short console_col; +short console_row; + +/************************************************************************/ + +void lcd_ctrl_init (void *lcdbase); +void lcd_enable (void); +#if LCD_BPP == LCD_COLOR8 +void lcd_setcolreg (ushort regno, + ushort red, ushort green, ushort blue); +#endif +#if LCD_BPP == LCD_MONOCHROME +void lcd_initcolregs (void); +#endif + +#if defined(CONFIG_RBC823) +void lcd_disable (void); +#endif + +/************************************************************************/ + +/************************************************************************/ +/* ----------------- chipset specific functions ----------------------- */ +/************************************************************************/ + +/* + * Calculate fb size for VIDEOLFB_ATAG. + */ +ulong calc_fbsize (void) +{ + ulong size; + int line_length = (panel_info.vl_col * NBITS (panel_info.vl_bpix)) / 8; + + size = line_length * panel_info.vl_row; + + return size; +} + +void lcd_ctrl_init (void *lcdbase) +{ + volatile immap_t *immr = (immap_t *) CONFIG_SYS_IMMR; + volatile lcd823_t *lcdp = &immr->im_lcd; + + uint lccrtmp; + uint lchcr_hpc_tmp; + + /* Initialize the LCD control register according to the LCD + * parameters defined. We do everything here but enable + * the controller. + */ + +#ifdef CONFIG_RPXLITE + /* This is special for RPXlite_DW Software Development Platform **[Sam]** */ + panel_info.vl_dp = CONFIG_SYS_LOW; +#endif + + lccrtmp = LCDBIT (LCCR_BNUM_BIT, + (((panel_info.vl_row * panel_info.vl_col) * (1 << LCD_BPP)) / 128)); + + lccrtmp |= LCDBIT (LCCR_CLKP_BIT, panel_info.vl_clkp) | + LCDBIT (LCCR_OEP_BIT, panel_info.vl_oep) | + LCDBIT (LCCR_HSP_BIT, panel_info.vl_hsp) | + LCDBIT (LCCR_VSP_BIT, panel_info.vl_vsp) | + LCDBIT (LCCR_DP_BIT, panel_info.vl_dp) | + LCDBIT (LCCR_BPIX_BIT, panel_info.vl_bpix) | + LCDBIT (LCCR_LBW_BIT, panel_info.vl_lbw) | + LCDBIT (LCCR_SPLT_BIT, panel_info.vl_splt) | + LCDBIT (LCCR_CLOR_BIT, panel_info.vl_clor) | + LCDBIT (LCCR_TFT_BIT, panel_info.vl_tft); + +#if 0 + lccrtmp |= ((SIU_LEVEL5 / 2) << 12); + lccrtmp |= LCCR_EIEN; +#endif + + lcdp->lcd_lccr = lccrtmp; + lcdp->lcd_lcsr = 0xFF; /* Clear pending interrupts */ + + /* Initialize LCD controller bus priorities. + */ +#ifdef CONFIG_RBC823 + immr->im_siu_conf.sc_sdcr = (immr->im_siu_conf.sc_sdcr & ~0x0f) | 1; /* RAID = 01, LAID = 00 */ +#else + immr->im_siu_conf.sc_sdcr &= ~0x0f; /* RAID = LAID = 0 */ + + /* set SHFT/CLOCK division factor 4 + * This needs to be set based upon display type and processor + * speed. The TFT displays run about 20 to 30 MHz. + * I was running 64 MHz processor speed. + * The value for this divider must be chosen so the result is + * an integer of the processor speed (i.e., divide by 3 with + * 64 MHz would be bad). + */ + immr->im_clkrst.car_sccr &= ~0x1F; + immr->im_clkrst.car_sccr |= LCD_DF; /* was 8 */ + +#endif /* CONFIG_RBC823 */ + +#if defined(CONFIG_RBC823) + /* Enable LCD on port D. + */ + immr->im_ioport.iop_pddat &= 0x0300; + immr->im_ioport.iop_pdpar |= 0x1CFF; + immr->im_ioport.iop_pddir |= 0x1CFF; + + /* Configure LCD_ON, VEE_ON, CCFL_ON on port B. + */ + immr->im_cpm.cp_pbdat &= ~0x00005001; + immr->im_cpm.cp_pbpar &= ~0x00005001; + immr->im_cpm.cp_pbdir |= 0x00005001; +#elif !defined(CONFIG_EDT32F10) + /* Enable LCD on port D. + */ + immr->im_ioport.iop_pdpar |= 0x1FFF; + immr->im_ioport.iop_pddir |= 0x1FFF; + + /* Enable LCD_A/B/C on port B. + */ + immr->im_cpm.cp_pbpar |= 0x00005001; + immr->im_cpm.cp_pbdir |= 0x00005001; +#else + /* Enable LCD on port D. + */ + immr->im_ioport.iop_pdpar |= 0x1DFF; + immr->im_ioport.iop_pdpar &= ~0x0200; + immr->im_ioport.iop_pddir |= 0x1FFF; + immr->im_ioport.iop_pddat |= 0x0200; +#endif + + /* Load the physical address of the linear frame buffer + * into the LCD controller. + * BIG NOTE: This has to be modified to load A and B depending + * upon the split mode of the LCD. + */ + lcdp->lcd_lcfaa = (ulong)lcd_base; + lcdp->lcd_lcfba = (ulong)lcd_base; + + /* MORE HACKS...This must be updated according to 823 manual + * for different panels. + * Udi Finkelstein - done - see below: + * Note: You better not try unsupported combinations such as + * 4-bit wide passive dual scan LCD at 4/8 Bit color. + */ + lchcr_hpc_tmp = + (panel_info.vl_col * + (panel_info.vl_tft ? 8 : + (((2 - panel_info.vl_lbw) << /* 4 bit=2, 8-bit = 1 */ + /* use << to mult by: single scan = 1, dual scan = 2 */ + panel_info.vl_splt) * + (panel_info.vl_bpix | 1)))) >> 3; /* 2/4 BPP = 1, 8/16 BPP = 3 */ + + lcdp->lcd_lchcr = LCHCR_BO | + LCDBIT (LCHCR_AT_BIT, 4) | + LCDBIT (LCHCR_HPC_BIT, lchcr_hpc_tmp) | + panel_info.vl_wbl; + + lcdp->lcd_lcvcr = LCDBIT (LCVCR_VPW_BIT, panel_info.vl_vpw) | + LCDBIT (LCVCR_LCD_AC_BIT, panel_info.vl_lcdac) | + LCDBIT (LCVCR_VPC_BIT, panel_info.vl_row) | + panel_info.vl_wbf; + +} + +/*----------------------------------------------------------------------*/ + +#ifdef NOT_USED_SO_FAR +static void +lcd_getcolreg (ushort regno, ushort *red, ushort *green, ushort *blue) +{ + volatile immap_t *immr = (immap_t *) CONFIG_SYS_IMMR; + volatile cpm8xx_t *cp = &(immr->im_cpm); + unsigned short colreg, *cmap_ptr; + + cmap_ptr = (unsigned short *)&cp->lcd_cmap[regno * 2]; + + colreg = *cmap_ptr; +#ifdef CONFIG_SYS_INVERT_COLORS + colreg ^= 0x0FFF; +#endif + + *red = (colreg >> 8) & 0x0F; + *green = (colreg >> 4) & 0x0F; + *blue = colreg & 0x0F; +} +#endif /* NOT_USED_SO_FAR */ + +/*----------------------------------------------------------------------*/ + +#if LCD_BPP == LCD_COLOR8 +void +lcd_setcolreg (ushort regno, ushort red, ushort green, ushort blue) +{ + volatile immap_t *immr = (immap_t *) CONFIG_SYS_IMMR; + volatile cpm8xx_t *cp = &(immr->im_cpm); + unsigned short colreg, *cmap_ptr; + + cmap_ptr = (unsigned short *)&cp->lcd_cmap[regno * 2]; + + colreg = ((red & 0x0F) << 8) | + ((green & 0x0F) << 4) | + (blue & 0x0F) ; +#ifdef CONFIG_SYS_INVERT_COLORS + colreg ^= 0x0FFF; +#endif + *cmap_ptr = colreg; + + debug ("setcolreg: reg %2d @ %p: R=%02X G=%02X B=%02X => %02X%02X\n", + regno, &(cp->lcd_cmap[regno * 2]), + red, green, blue, + cp->lcd_cmap[ regno * 2 ], cp->lcd_cmap[(regno * 2) + 1]); +} +#endif /* LCD_COLOR8 */ + +/*----------------------------------------------------------------------*/ + +#if LCD_BPP == LCD_MONOCHROME +static +void lcd_initcolregs (void) +{ + volatile immap_t *immr = (immap_t *) CONFIG_SYS_IMMR; + volatile cpm8xx_t *cp = &(immr->im_cpm); + ushort regno; + + for (regno = 0; regno < 16; regno++) { + cp->lcd_cmap[regno * 2] = 0; + cp->lcd_cmap[(regno * 2) + 1] = regno & 0x0f; + } +} +#endif + +/*----------------------------------------------------------------------*/ + +void lcd_enable (void) +{ + volatile immap_t *immr = (immap_t *) CONFIG_SYS_IMMR; + volatile lcd823_t *lcdp = &immr->im_lcd; + + /* Enable the LCD panel */ +#ifndef CONFIG_RBC823 + immr->im_siu_conf.sc_sdcr |= (1 << (31 - 25)); /* LAM = 1 */ +#endif + lcdp->lcd_lccr |= LCCR_PON; + +#ifdef CONFIG_V37 + /* Turn on display backlight */ + immr->im_cpm.cp_pbpar |= 0x00008000; + immr->im_cpm.cp_pbdir |= 0x00008000; +#elif defined(CONFIG_RBC823) + /* Turn on display backlight */ + immr->im_cpm.cp_pbdat |= 0x00004000; +#endif + +#if defined(CONFIG_LWMON) + { uchar c = pic_read (0x60); +#if defined(CONFIG_LCD) && defined(CONFIG_LWMON) && (CONFIG_POST & CONFIG_SYS_POST_SYSMON) + /* Enable LCD later in sysmon test, only if temperature is OK */ +#else + c |= 0x07; /* Power on CCFL, Enable CCFL, Chip Enable LCD */ +#endif + pic_write (0x60, c); + } +#endif /* CONFIG_LWMON */ + +#if defined(CONFIG_R360MPI) + { + extern void r360_i2c_lcd_write (uchar data0, uchar data1); + unsigned long bgi, ctr; + char *p; + + if ((p = getenv("lcdbgi")) != NULL) { + bgi = simple_strtoul (p, 0, 10) & 0xFFF; + } else { + bgi = 0xFFF; + } + + if ((p = getenv("lcdctr")) != NULL) { + ctr = simple_strtoul (p, 0, 10) & 0xFFF; + } else { + ctr=0x7FF; + } + + r360_i2c_lcd_write(0x10, 0x01); + r360_i2c_lcd_write(0x20, 0x01); + r360_i2c_lcd_write(0x30 | ((bgi>>8) & 0xF), bgi & 0xFF); + r360_i2c_lcd_write(0x40 | ((ctr>>8) & 0xF), ctr & 0xFF); + } +#endif /* CONFIG_R360MPI */ +#ifdef CONFIG_RBC823 + udelay(200000); /* wait 200ms */ + /* Turn VEE_ON first */ + immr->im_cpm.cp_pbdat |= 0x00000001; + udelay(200000); /* wait 200ms */ + /* Now turn on LCD_ON */ + immr->im_cpm.cp_pbdat |= 0x00001000; +#endif +#ifdef CONFIG_RRVISION + debug ("PC4->Output(1): enable LVDS\n"); + debug ("PC5->Output(0): disable PAL clock\n"); + immr->im_ioport.iop_pddir |= 0x1000; + immr->im_ioport.iop_pcpar &= ~(0x0C00); + immr->im_ioport.iop_pcdir |= 0x0C00 ; + immr->im_ioport.iop_pcdat |= 0x0800 ; + immr->im_ioport.iop_pcdat &= ~(0x0400); + debug ("PDPAR=0x%04X PDDIR=0x%04X PDDAT=0x%04X\n", + immr->im_ioport.iop_pdpar, + immr->im_ioport.iop_pddir, + immr->im_ioport.iop_pddat); + debug ("PCPAR=0x%04X PCDIR=0x%04X PCDAT=0x%04X\n", + immr->im_ioport.iop_pcpar, + immr->im_ioport.iop_pcdir, + immr->im_ioport.iop_pcdat); +#endif +} + +/*----------------------------------------------------------------------*/ + +#if defined (CONFIG_RBC823) +void lcd_disable (void) +{ + volatile immap_t *immr = (immap_t *) CONFIG_SYS_IMMR; + volatile lcd823_t *lcdp = &immr->im_lcd; + +#if defined(CONFIG_LWMON) + { uchar c = pic_read (0x60); + c &= ~0x07; /* Power off CCFL, Disable CCFL, Chip Disable LCD */ + pic_write (0x60, c); + } +#elif defined(CONFIG_R360MPI) + { + extern void r360_i2c_lcd_write (uchar data0, uchar data1); + + r360_i2c_lcd_write(0x10, 0x00); + r360_i2c_lcd_write(0x20, 0x00); + r360_i2c_lcd_write(0x30, 0x00); + r360_i2c_lcd_write(0x40, 0x00); + } +#endif /* CONFIG_LWMON */ + /* Disable the LCD panel */ + lcdp->lcd_lccr &= ~LCCR_PON; +#ifdef CONFIG_RBC823 + /* Turn off display backlight, VEE and LCD_ON */ + immr->im_cpm.cp_pbdat &= ~0x00005001; +#else + immr->im_siu_conf.sc_sdcr &= ~(1 << (31 - 25)); /* LAM = 0 */ +#endif /* CONFIG_RBC823 */ +} +#endif /* NOT_USED_SO_FAR || CONFIG_RBC823 */ + + +/************************************************************************/ + +#endif /* CONFIG_LCD */ diff --git a/arch/powerpc/cpu/mpc8xx/plprcr_write.S b/arch/powerpc/cpu/mpc8xx/plprcr_write.S new file mode 100644 index 0000000..e325671 --- /dev/null +++ b/arch/powerpc/cpu/mpc8xx/plprcr_write.S @@ -0,0 +1,135 @@ +/* + * (C) Copyright 2004 + * Wolfgang Denk, DENX Software Engineering, wd@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 <mpc8xx.h> +#include <ppc_asm.tmpl> +#include <asm/cache.h> + +#define CACHE_CMD_ENABLE 0x02000000 +#define CACHE_CMD_DISABLE 0x04000000 +#define CACHE_CMD_LOAD_LOCK 0x06000000 +#define CACHE_CMD_UNLOCK_LINE 0x08000000 +#define CACHE_CMD_UNLOCK_ALL 0x0A000000 +#define CACHE_CMD_INVALIDATE 0x0C000000 +#define SPEED_PLPRCR_WAIT_5CYC 150 +#define _CACHE_ALIGN_SIZE 16 + + + .text + .align 2 + .globl plprcr_write_866 + +/* + * void plprcr_write_866 (long plprcr) + * Write PLPRCR, including workaround for device errata SIU4 and SIU9. + */ + +plprcr_write_866: + mfspr r10, LR /* save the Link Register value */ + + /* turn instruction cache on (no MMU required for instructions) + */ + lis r4, CACHE_CMD_ENABLE@h + ori r4, r4, CACHE_CMD_ENABLE@l + mtspr IC_CST, r4 + isync + + /* clear IC_CST error bits + */ + mfspr r4, IC_CST + + bl plprcr_here + +plprcr_here: + mflr r5 + + /* calculate relocation offset + */ + lis r4, plprcr_here@h + ori r4, r4, plprcr_here@l + sub r5, r5, r4 + + /* calculate first address of this function + */ + lis r6, plprcr_write_866@h + ori r6, r6, plprcr_write_866@l + add r6, r6, r5 + + /* calculate end address of this function + */ + lis r7, plprcr_end@h + ori r7, r7, plprcr_end@l + add r7, r7, r5 + + /* load and lock code addresses + */ + mr r5, r6 + +plprcr_loop: + mtspr IC_ADR, r5 + addi r5, r5, _CACHE_ALIGN_SIZE /* increment by one line */ + + lis r4, CACHE_CMD_LOAD_LOCK@h + ori r4, r4, CACHE_CMD_LOAD_LOCK@l + mtspr IC_CST, r4 + isync + + cmpw r5, r7 + blt plprcr_loop + + /* IC_CST error bits not evaluated + */ + + /* switch PLPRCR + */ + mfspr r4, IMMR /* read IMMR */ + rlwinm r4, r4, 0, 0, 15 /* only high 16 bits count */ + + /* write sequence according to MPC866 Errata + */ + stw r3, PLPRCR(r4) + isync + + lis r3, SPEED_PLPRCR_WAIT_5CYC@h + ori r3, r3, SPEED_PLPRCR_WAIT_5CYC@l + +plprcr_wait: + cmpwi r3, 0 + beq plprcr_wait_end + nop + subi r3, r3, 1 + b plprcr_wait + +plprcr_wait_end: + + /* unlock instruction cache but leave it enabled + */ + lis r4, CACHE_CMD_UNLOCK_ALL@h + ori r4, r4, CACHE_CMD_UNLOCK_ALL@l + mtspr IC_CST, r4 + isync + + mtspr LR, r10 /* restore original Link Register value */ + blr + +plprcr_end: diff --git a/arch/powerpc/cpu/mpc8xx/scc.c b/arch/powerpc/cpu/mpc8xx/scc.c new file mode 100644 index 0000000..effb967 --- /dev/null +++ b/arch/powerpc/cpu/mpc8xx/scc.c @@ -0,0 +1,572 @@ +/* + * File: scc.c + * Description: + * Basic ET HW initialization and packet RX/TX routines + * + * NOTE <<<IMPORTANT: PLEASE READ>>>: + * Do not cache Rx/Tx buffers! + */ + +/* + * MPC823 <-> MC68160 Connections: + * + * Setup MPC823 to work with MC68160 Enhanced Ethernet + * Serial Tranceiver as follows: + * + * MPC823 Signal MC68160 Comments + * ------ ------ ------- -------- + * PA-12 ETHTX --------> TX Eth. Port Transmit Data + * PB-18 E_TENA --------> TENA Eth. Transmit Port Enable + * PA-5 ETHTCK <-------- TCLK Eth. Port Transmit Clock + * PA-13 ETHRX <-------- RX Eth. Port Receive Data + * PC-8 E_RENA <-------- RENA Eth. Receive Enable + * PA-6 ETHRCK <-------- RCLK Eth. Port Receive Clock + * PC-9 E_CLSN <-------- CLSN Eth. Port Collision Indication + * + * FADS Board Signal MC68160 Comments + * ----------------- ------- -------- + * (BCSR1) ETHEN* --------> CS2 Eth. Port Enable + * (BSCR4) TPSQEL* --------> TPSQEL Twisted Pair Signal Quality Error Test Enable + * (BCSR4) TPFLDL* --------> TPFLDL Twisted Pair Full-Duplex + * (BCSR4) ETHLOOP --------> LOOP Eth. Port Diagnostic Loop-Back + * + */ + +#include <common.h> +#include <malloc.h> +#include <commproc.h> +#include <net.h> +#include <command.h> + +#if defined(CONFIG_CMD_NET) && defined(SCC_ENET) + +/* Ethernet Transmit and Receive Buffers */ +#define DBUF_LENGTH 1520 + +#define TX_BUF_CNT 2 + +#define TOUT_LOOP 10000 /* 10 ms to have a packet sent */ + +static char txbuf[DBUF_LENGTH]; + +static uint rxIdx; /* index of the current RX buffer */ +static uint txIdx; /* index of the current TX buffer */ + +/* + * SCC Ethernet Tx and Rx buffer descriptors allocated at the + * immr->udata_bd address on Dual-Port RAM + * Provide for Double Buffering + */ + +typedef volatile struct CommonBufferDescriptor { + cbd_t rxbd[PKTBUFSRX]; /* Rx BD */ + cbd_t txbd[TX_BUF_CNT]; /* Tx BD */ +} RTXBD; + +static RTXBD *rtx; + +static int scc_send(struct eth_device* dev, volatile void *packet, int length); +static int scc_recv(struct eth_device* dev); +static int scc_init (struct eth_device* dev, bd_t * bd); +static void scc_halt(struct eth_device* dev); + +int scc_initialize(bd_t *bis) +{ + struct eth_device* dev; + + dev = (struct eth_device*) malloc(sizeof *dev); + memset(dev, 0, sizeof *dev); + + sprintf(dev->name, "SCC ETHERNET"); + dev->iobase = 0; + dev->priv = 0; + dev->init = scc_init; + dev->halt = scc_halt; + dev->send = scc_send; + dev->recv = scc_recv; + + eth_register(dev); + + return 1; +} + +static int scc_send(struct eth_device* dev, volatile void *packet, int length) +{ + int i, j=0; +#if 0 + volatile char *in, *out; +#endif + + /* section 16.9.23.3 + * Wait for ready + */ +#if 0 + while (rtx->txbd[txIdx].cbd_sc & BD_ENET_TX_READY); + out = (char *)(rtx->txbd[txIdx].cbd_bufaddr); + in = packet; + for(i = 0; i < length; i++) { + *out++ = *in++; + } + rtx->txbd[txIdx].cbd_datlen = length; + rtx->txbd[txIdx].cbd_sc |= (BD_ENET_TX_READY | BD_ENET_TX_LAST); + while (rtx->txbd[txIdx].cbd_sc & BD_ENET_TX_READY) j++; + +#ifdef ET_DEBUG + printf("cycles: %d status: %x\n", j, rtx->txbd[txIdx].cbd_sc); +#endif + i = (rtx->txbd[txIdx++].cbd_sc & BD_ENET_TX_STATS) /* return only status bits */; + + /* wrap around buffer index when necessary */ + if (txIdx >= TX_BUF_CNT) txIdx = 0; +#endif + + while ((rtx->txbd[txIdx].cbd_sc & BD_ENET_TX_READY) && (j<TOUT_LOOP)) { + udelay (1); /* will also trigger Wd if needed */ + j++; + } + if (j>=TOUT_LOOP) printf("TX not ready\n"); + rtx->txbd[txIdx].cbd_bufaddr = (uint)packet; + rtx->txbd[txIdx].cbd_datlen = length; + rtx->txbd[txIdx].cbd_sc |= (BD_ENET_TX_READY | BD_ENET_TX_LAST |BD_ENET_TX_WRAP); + while ((rtx->txbd[txIdx].cbd_sc & BD_ENET_TX_READY) && (j<TOUT_LOOP)) { + udelay (1); /* will also trigger Wd if needed */ + j++; + } + if (j>=TOUT_LOOP) printf("TX timeout\n"); +#ifdef ET_DEBUG + printf("cycles: %d status: %x\n", j, rtx->txbd[txIdx].cbd_sc); +#endif + i = (rtx->txbd[txIdx].cbd_sc & BD_ENET_TX_STATS) /* return only status bits */; + return i; +} + +static int scc_recv (struct eth_device *dev) +{ + int length; + + for (;;) { + /* section 16.9.23.2 */ + if (rtx->rxbd[rxIdx].cbd_sc & BD_ENET_RX_EMPTY) { + length = -1; + break; /* nothing received - leave for() loop */ + } + + length = rtx->rxbd[rxIdx].cbd_datlen; + + if (rtx->rxbd[rxIdx].cbd_sc & 0x003f) { +#ifdef ET_DEBUG + printf ("err: %x\n", rtx->rxbd[rxIdx].cbd_sc); +#endif + } else { + /* Pass the packet up to the protocol layers. */ + NetReceive (NetRxPackets[rxIdx], length - 4); + } + + + /* Give the buffer back to the SCC. */ + rtx->rxbd[rxIdx].cbd_datlen = 0; + + /* wrap around buffer index when necessary */ + if ((rxIdx + 1) >= PKTBUFSRX) { + rtx->rxbd[PKTBUFSRX - 1].cbd_sc = + (BD_ENET_RX_WRAP | BD_ENET_RX_EMPTY); + rxIdx = 0; + } else { + rtx->rxbd[rxIdx].cbd_sc = BD_ENET_RX_EMPTY; + rxIdx++; + } + } + return length; +} + +/************************************************************** + * + * SCC Ethernet Initialization Routine + * + *************************************************************/ + +static int scc_init (struct eth_device *dev, bd_t * bis) +{ + + int i; + scc_enet_t *pram_ptr; + + volatile immap_t *immr = (immap_t *) CONFIG_SYS_IMMR; + +#if defined(CONFIG_LWMON) + reset_phy(); +#endif + +#ifdef CONFIG_FADS +#if defined(CONFIG_MPC86xADS) || defined(CONFIG_MPC860T) + /* The MPC86xADS/FADS860T don't use the MODEM_EN or DATA_VOICE signals. */ + *((uint *) BCSR4) &= ~BCSR4_ETHLOOP; + *((uint *) BCSR4) |= BCSR4_TFPLDL | BCSR4_TPSQEL; + *((uint *) BCSR1) &= ~BCSR1_ETHEN; +#else + *((uint *) BCSR4) &= ~(BCSR4_ETHLOOP | BCSR4_MODEM_EN); + *((uint *) BCSR4) |= BCSR4_TFPLDL | BCSR4_TPSQEL | BCSR4_DATA_VOICE; + *((uint *) BCSR1) &= ~BCSR1_ETHEN; +#endif +#endif + + pram_ptr = (scc_enet_t *) & (immr->im_cpm.cp_dparam[PROFF_ENET]); + + rxIdx = 0; + txIdx = 0; + + if (!rtx) { +#ifdef CONFIG_SYS_ALLOC_DPRAM + rtx = (RTXBD *) (immr->im_cpm.cp_dpmem + + dpram_alloc_align (sizeof (RTXBD), 8)); +#else + rtx = (RTXBD *) (immr->im_cpm.cp_dpmem + CPM_SCC_BASE); +#endif + } + +#if (defined(PA_ENET_RXD) && defined(PA_ENET_TXD)) + /* Configure port A pins for Txd and Rxd. + */ + immr->im_ioport.iop_papar |= (PA_ENET_RXD | PA_ENET_TXD); + immr->im_ioport.iop_padir &= ~(PA_ENET_RXD | PA_ENET_TXD); + immr->im_ioport.iop_paodr &= ~PA_ENET_TXD; +#elif (defined(PB_ENET_RXD) && defined(PB_ENET_TXD)) + /* Configure port B pins for Txd and Rxd. + */ + immr->im_cpm.cp_pbpar |= (PB_ENET_RXD | PB_ENET_TXD); + immr->im_cpm.cp_pbdir &= ~(PB_ENET_RXD | PB_ENET_TXD); + immr->im_cpm.cp_pbodr &= ~PB_ENET_TXD; +#else +#error Configuration Error: exactly ONE of PA_ENET_[RT]XD, PB_ENET_[RT]XD must be defined +#endif + +#if defined(PC_ENET_LBK) + /* Configure port C pins to disable External Loopback + */ + immr->im_ioport.iop_pcpar &= ~PC_ENET_LBK; + immr->im_ioport.iop_pcdir |= PC_ENET_LBK; + immr->im_ioport.iop_pcso &= ~PC_ENET_LBK; + immr->im_ioport.iop_pcdat &= ~PC_ENET_LBK; /* Disable Loopback */ +#endif /* PC_ENET_LBK */ + + /* Configure port C pins to enable CLSN and RENA. + */ + immr->im_ioport.iop_pcpar &= ~(PC_ENET_CLSN | PC_ENET_RENA); + immr->im_ioport.iop_pcdir &= ~(PC_ENET_CLSN | PC_ENET_RENA); + immr->im_ioport.iop_pcso |= (PC_ENET_CLSN | PC_ENET_RENA); + + /* Configure port A for TCLK and RCLK. + */ + immr->im_ioport.iop_papar |= (PA_ENET_TCLK | PA_ENET_RCLK); + immr->im_ioport.iop_padir &= ~(PA_ENET_TCLK | PA_ENET_RCLK); + + /* + * Configure Serial Interface clock routing -- see section 16.7.5.3 + * First, clear all SCC bits to zero, then set the ones we want. + */ + + immr->im_cpm.cp_sicr &= ~SICR_ENET_MASK; + immr->im_cpm.cp_sicr |= SICR_ENET_CLKRT; + + + /* + * Initialize SDCR -- see section 16.9.23.7 + * SDMA configuration register + */ + immr->im_siu_conf.sc_sdcr = 0x01; + + + /* + * Setup SCC Ethernet Parameter RAM + */ + + pram_ptr->sen_genscc.scc_rfcr = 0x18; /* Normal Operation and Mot byte ordering */ + pram_ptr->sen_genscc.scc_tfcr = 0x18; /* Mot byte ordering, Normal access */ + + pram_ptr->sen_genscc.scc_mrblr = DBUF_LENGTH; /* max. ET package len 1520 */ + + pram_ptr->sen_genscc.scc_rbase = (unsigned int) (&rtx->rxbd[0]); /* Set RXBD tbl start at Dual Port */ + pram_ptr->sen_genscc.scc_tbase = (unsigned int) (&rtx->txbd[0]); /* Set TXBD tbl start at Dual Port */ + + /* + * Setup Receiver Buffer Descriptors (13.14.24.18) + * Settings: + * Empty, Wrap + */ + + for (i = 0; i < PKTBUFSRX; i++) { + rtx->rxbd[i].cbd_sc = BD_ENET_RX_EMPTY; + rtx->rxbd[i].cbd_datlen = 0; /* Reset */ + rtx->rxbd[i].cbd_bufaddr = (uint) NetRxPackets[i]; + } + + rtx->rxbd[PKTBUFSRX - 1].cbd_sc |= BD_ENET_RX_WRAP; + + /* + * Setup Ethernet Transmitter Buffer Descriptors (13.14.24.19) + * Settings: + * Add PADs to Short FRAMES, Wrap, Last, Tx CRC + */ + + for (i = 0; i < TX_BUF_CNT; i++) { + rtx->txbd[i].cbd_sc = + (BD_ENET_TX_PAD | BD_ENET_TX_LAST | BD_ENET_TX_TC); + rtx->txbd[i].cbd_datlen = 0; /* Reset */ + rtx->txbd[i].cbd_bufaddr = (uint) (&txbuf[0]); + } + + rtx->txbd[TX_BUF_CNT - 1].cbd_sc |= BD_ENET_TX_WRAP; + + /* + * Enter Command: Initialize Rx Params for SCC + */ + + do { /* Spin until ready to issue command */ + __asm__ ("eieio"); + } while (immr->im_cpm.cp_cpcr & CPM_CR_FLG); + /* Issue command */ + immr->im_cpm.cp_cpcr = + ((CPM_CR_INIT_RX << 8) | (CPM_CR_ENET << 4) | CPM_CR_FLG); + do { /* Spin until command processed */ + __asm__ ("eieio"); + } while (immr->im_cpm.cp_cpcr & CPM_CR_FLG); + + /* + * Ethernet Specific Parameter RAM + * see table 13-16, pg. 660, + * pg. 681 (example with suggested settings) + */ + + pram_ptr->sen_cpres = ~(0x0); /* Preset CRC */ + pram_ptr->sen_cmask = 0xdebb20e3; /* Constant Mask for CRC */ + pram_ptr->sen_crcec = 0x0; /* Error Counter CRC (unused) */ + pram_ptr->sen_alec = 0x0; /* Alignment Error Counter (unused) */ + pram_ptr->sen_disfc = 0x0; /* Discard Frame Counter (unused) */ + pram_ptr->sen_pads = 0x8888; /* Short Frame PAD Characters */ + + pram_ptr->sen_retlim = 15; /* Retry Limit Threshold */ + pram_ptr->sen_maxflr = 1518; /* MAX Frame Length Register */ + pram_ptr->sen_minflr = 64; /* MIN Frame Length Register */ + + pram_ptr->sen_maxd1 = DBUF_LENGTH; /* MAX DMA1 Length Register */ + pram_ptr->sen_maxd2 = DBUF_LENGTH; /* MAX DMA2 Length Register */ + + pram_ptr->sen_gaddr1 = 0x0; /* Group Address Filter 1 (unused) */ + pram_ptr->sen_gaddr2 = 0x0; /* Group Address Filter 2 (unused) */ + pram_ptr->sen_gaddr3 = 0x0; /* Group Address Filter 3 (unused) */ + pram_ptr->sen_gaddr4 = 0x0; /* Group Address Filter 4 (unused) */ + +#define ea eth_get_dev()->enetaddr + pram_ptr->sen_paddrh = (ea[5] << 8) + ea[4]; + pram_ptr->sen_paddrm = (ea[3] << 8) + ea[2]; + pram_ptr->sen_paddrl = (ea[1] << 8) + ea[0]; +#undef ea + + pram_ptr->sen_pper = 0x0; /* Persistence (unused) */ + pram_ptr->sen_iaddr1 = 0x0; /* Individual Address Filter 1 (unused) */ + pram_ptr->sen_iaddr2 = 0x0; /* Individual Address Filter 2 (unused) */ + pram_ptr->sen_iaddr3 = 0x0; /* Individual Address Filter 3 (unused) */ + pram_ptr->sen_iaddr4 = 0x0; /* Individual Address Filter 4 (unused) */ + pram_ptr->sen_taddrh = 0x0; /* Tmp Address (MSB) (unused) */ + pram_ptr->sen_taddrm = 0x0; /* Tmp Address (unused) */ + pram_ptr->sen_taddrl = 0x0; /* Tmp Address (LSB) (unused) */ + + /* + * Enter Command: Initialize Tx Params for SCC + */ + + do { /* Spin until ready to issue command */ + __asm__ ("eieio"); + } while (immr->im_cpm.cp_cpcr & CPM_CR_FLG); + /* Issue command */ + immr->im_cpm.cp_cpcr = + ((CPM_CR_INIT_TX << 8) | (CPM_CR_ENET << 4) | CPM_CR_FLG); + do { /* Spin until command processed */ + __asm__ ("eieio"); + } while (immr->im_cpm.cp_cpcr & CPM_CR_FLG); + + /* + * Mask all Events in SCCM - we use polling mode + */ + immr->im_cpm.cp_scc[SCC_ENET].scc_sccm = 0; + + /* + * Clear Events in SCCE -- Clear bits by writing 1's + */ + + immr->im_cpm.cp_scc[SCC_ENET].scc_scce = ~(0x0); + + + /* + * Initialize GSMR High 32-Bits + * Settings: Normal Mode + */ + + immr->im_cpm.cp_scc[SCC_ENET].scc_gsmrh = 0; + + /* + * Initialize GSMR Low 32-Bits, but do not Enable Transmit/Receive + * Settings: + * TCI = Invert + * TPL = 48 bits + * TPP = Repeating 10's + * MODE = Ethernet + */ + + immr->im_cpm.cp_scc[SCC_ENET].scc_gsmrl = (SCC_GSMRL_TCI | + SCC_GSMRL_TPL_48 | + SCC_GSMRL_TPP_10 | + SCC_GSMRL_MODE_ENET); + + /* + * Initialize the DSR -- see section 13.14.4 (pg. 513) v0.4 + */ + + immr->im_cpm.cp_scc[SCC_ENET].scc_dsr = 0xd555; + + /* + * Initialize the PSMR + * Settings: + * CRC = 32-Bit CCITT + * NIB = Begin searching for SFD 22 bits after RENA + * FDE = Full Duplex Enable + * LPB = Loopback Enable (Needed when FDE is set) + * BRO = Reject broadcast packets + * PROMISCOUS = Catch all packets regardless of dest. MAC adress + */ + immr->im_cpm.cp_scc[SCC_ENET].scc_psmr = SCC_PSMR_ENCRC | + SCC_PSMR_NIB22 | +#if defined(CONFIG_SCC_ENET_FULL_DUPLEX) + SCC_PSMR_FDE | SCC_PSMR_LPB | +#endif +#if defined(CONFIG_SCC_ENET_NO_BROADCAST) + SCC_PSMR_BRO | +#endif +#if defined(CONFIG_SCC_ENET_PROMISCOUS) + SCC_PSMR_PRO | +#endif + 0; + + /* + * Configure Ethernet TENA Signal + */ + +#if (defined(PC_ENET_TENA) && !defined(PB_ENET_TENA)) + immr->im_ioport.iop_pcpar |= PC_ENET_TENA; + immr->im_ioport.iop_pcdir &= ~PC_ENET_TENA; +#elif (defined(PB_ENET_TENA) && !defined(PC_ENET_TENA)) + immr->im_cpm.cp_pbpar |= PB_ENET_TENA; + immr->im_cpm.cp_pbdir |= PB_ENET_TENA; +#else +#error Configuration Error: exactly ONE of PB_ENET_TENA, PC_ENET_TENA must be defined +#endif + +#if defined(CONFIG_ADS) && defined(CONFIG_MPC860) + /* + * Port C is used to control the PHY,MC68160. + */ + immr->im_ioport.iop_pcdir |= + (PC_ENET_ETHLOOP | PC_ENET_TPFLDL | PC_ENET_TPSQEL); + + immr->im_ioport.iop_pcdat |= PC_ENET_TPFLDL; + immr->im_ioport.iop_pcdat &= ~(PC_ENET_ETHLOOP | PC_ENET_TPSQEL); + *((uint *) BCSR1) &= ~BCSR1_ETHEN; +#endif /* MPC860ADS */ + +#if defined(CONFIG_AMX860) + /* + * Port B is used to control the PHY,MC68160. + */ + immr->im_cpm.cp_pbdir |= + (PB_ENET_ETHLOOP | PB_ENET_TPFLDL | PB_ENET_TPSQEL); + + immr->im_cpm.cp_pbdat |= PB_ENET_TPFLDL; + immr->im_cpm.cp_pbdat &= ~(PB_ENET_ETHLOOP | PB_ENET_TPSQEL); + + immr->im_ioport.iop_pddir |= PD_ENET_ETH_EN; + immr->im_ioport.iop_pddat &= ~PD_ENET_ETH_EN; +#endif /* AMX860 */ + +#ifdef CONFIG_RPXCLASSIC + *((uchar *) BCSR0) &= ~BCSR0_ETHLPBK; + *((uchar *) BCSR0) |= (BCSR0_ETHEN | BCSR0_COLTEST | BCSR0_FULLDPLX); +#endif + +#ifdef CONFIG_RPXLITE + *((uchar *) BCSR0) |= BCSR0_ETHEN; +#endif + +#if defined(CONFIG_QS860T) + /* + * PB27=FDE-, set output low for full duplex + * PB26=Link Test Enable, normally high output + */ + immr->im_cpm.cp_pbdir |= 0x00000030; + immr->im_cpm.cp_pbdat |= 0x00000020; + immr->im_cpm.cp_pbdat &= ~0x00000010; +#endif /* QS860T */ + +#ifdef CONFIG_MBX + board_ether_init (); +#endif + +#if defined(CONFIG_NETVIA) +#if defined(PA_ENET_PDN) + immr->im_ioport.iop_papar &= ~PA_ENET_PDN; + immr->im_ioport.iop_padir |= PA_ENET_PDN; + immr->im_ioport.iop_padat |= PA_ENET_PDN; +#elif defined(PB_ENET_PDN) + immr->im_cpm.cp_pbpar &= ~PB_ENET_PDN; + immr->im_cpm.cp_pbdir |= PB_ENET_PDN; + immr->im_cpm.cp_pbdat |= PB_ENET_PDN; +#elif defined(PC_ENET_PDN) + immr->im_ioport.iop_pcpar &= ~PC_ENET_PDN; + immr->im_ioport.iop_pcdir |= PC_ENET_PDN; + immr->im_ioport.iop_pcdat |= PC_ENET_PDN; +#elif defined(PD_ENET_PDN) + immr->im_ioport.iop_pdpar &= ~PD_ENET_PDN; + immr->im_ioport.iop_pddir |= PD_ENET_PDN; + immr->im_ioport.iop_pddat |= PD_ENET_PDN; +#endif +#endif + + /* + * Set the ENT/ENR bits in the GSMR Low -- Enable Transmit/Receive + */ + + immr->im_cpm.cp_scc[SCC_ENET].scc_gsmrl |= + (SCC_GSMRL_ENR | SCC_GSMRL_ENT); + + /* + * Work around transmit problem with first eth packet + */ +#if defined (CONFIG_FADS) + udelay (10000); /* wait 10 ms */ +#elif defined (CONFIG_AMX860) || defined(CONFIG_RPXCLASSIC) + udelay (100000); /* wait 100 ms */ +#endif + + return 1; +} + + +static void scc_halt (struct eth_device *dev) +{ + volatile immap_t *immr = (immap_t *) CONFIG_SYS_IMMR; + + immr->im_cpm.cp_scc[SCC_ENET].scc_gsmrl &= + ~(SCC_GSMRL_ENR | SCC_GSMRL_ENT); + + immr->im_ioport.iop_pcso &= ~(PC_ENET_CLSN | PC_ENET_RENA); +} + +#if 0 +void restart (void) +{ + volatile immap_t *immr = (immap_t *) CONFIG_SYS_IMMR; + + immr->im_cpm.cp_scc[SCC_ENET].scc_gsmrl |= + (SCC_GSMRL_ENR | SCC_GSMRL_ENT); +} +#endif +#endif diff --git a/arch/powerpc/cpu/mpc8xx/serial.c b/arch/powerpc/cpu/mpc8xx/serial.c new file mode 100644 index 0000000..664db65 --- /dev/null +++ b/arch/powerpc/cpu/mpc8xx/serial.c @@ -0,0 +1,745 @@ +/* + * (C) Copyright 2000 + * Wolfgang Denk, DENX Software Engineering, wd@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> +#include <commproc.h> +#include <command.h> +#include <serial.h> +#include <watchdog.h> + +DECLARE_GLOBAL_DATA_PTR; + +#if !defined(CONFIG_8xx_CONS_NONE) /* No Console at all */ + +#if defined(CONFIG_8xx_CONS_SMC1) /* Console on SMC1 */ +#define SMC_INDEX 0 +#define PROFF_SMC PROFF_SMC1 +#define CPM_CR_CH_SMC CPM_CR_CH_SMC1 + +#elif defined(CONFIG_8xx_CONS_SMC2) /* Console on SMC2 */ +#define SMC_INDEX 1 +#define PROFF_SMC PROFF_SMC2 +#define CPM_CR_CH_SMC CPM_CR_CH_SMC2 + +#endif /* CONFIG_8xx_CONS_SMCx */ + +#if defined(CONFIG_8xx_CONS_SCC1) /* Console on SCC1 */ +#define SCC_INDEX 0 +#define PROFF_SCC PROFF_SCC1 +#define CPM_CR_CH_SCC CPM_CR_CH_SCC1 + +#elif defined(CONFIG_8xx_CONS_SCC2) /* Console on SCC2 */ +#define SCC_INDEX 1 +#define PROFF_SCC PROFF_SCC2 +#define CPM_CR_CH_SCC CPM_CR_CH_SCC2 + +#elif defined(CONFIG_8xx_CONS_SCC3) /* Console on SCC3 */ +#define SCC_INDEX 2 +#define PROFF_SCC PROFF_SCC3 +#define CPM_CR_CH_SCC CPM_CR_CH_SCC3 + +#elif defined(CONFIG_8xx_CONS_SCC4) /* Console on SCC4 */ +#define SCC_INDEX 3 +#define PROFF_SCC PROFF_SCC4 +#define CPM_CR_CH_SCC CPM_CR_CH_SCC4 + +#endif /* CONFIG_8xx_CONS_SCCx */ + +#if !defined(CONFIG_SYS_SMC_RXBUFLEN) +#define CONFIG_SYS_SMC_RXBUFLEN 1 +#define CONFIG_SYS_MAXIDLE 0 +#else +#if !defined(CONFIG_SYS_MAXIDLE) +#error "you must define CONFIG_SYS_MAXIDLE" +#endif +#endif + +typedef volatile struct serialbuffer { + cbd_t rxbd; /* Rx BD */ + cbd_t txbd; /* Tx BD */ + uint rxindex; /* index for next character to read */ + volatile uchar rxbuf[CONFIG_SYS_SMC_RXBUFLEN];/* rx buffers */ + volatile uchar txbuf; /* tx buffers */ +} serialbuffer_t; + +static void serial_setdivisor(volatile cpm8xx_t *cp) +{ + int divisor=(gd->cpu_clk + 8*gd->baudrate)/16/gd->baudrate; + + if(divisor/16>0x1000) { + /* bad divisor, assume 50MHz clock and 9600 baud */ + divisor=(50*1000*1000 + 8*9600)/16/9600; + } + +#ifdef CONFIG_SYS_BRGCLK_PRESCALE + divisor /= CONFIG_SYS_BRGCLK_PRESCALE; +#endif + + if(divisor<=0x1000) { + cp->cp_brgc1=((divisor-1)<<1) | CPM_BRG_EN; + } else { + cp->cp_brgc1=((divisor/16-1)<<1) | CPM_BRG_EN | CPM_BRG_DIV16; + } +} + +#if (defined (CONFIG_8xx_CONS_SMC1) || defined (CONFIG_8xx_CONS_SMC2)) + +/* + * Minimal serial functions needed to use one of the SMC ports + * as serial console interface. + */ + +static void smc_setbrg (void) +{ + volatile immap_t *im = (immap_t *)CONFIG_SYS_IMMR; + volatile cpm8xx_t *cp = &(im->im_cpm); + + /* Set up the baud rate generator. + * See 8xx_io/commproc.c for details. + * + * Wire BRG1 to SMCx + */ + + cp->cp_simode = 0x00000000; + + serial_setdivisor(cp); +} + +static int smc_init (void) +{ + volatile immap_t *im = (immap_t *)CONFIG_SYS_IMMR; + volatile smc_t *sp; + volatile smc_uart_t *up; + volatile cpm8xx_t *cp = &(im->im_cpm); +#if (!defined(CONFIG_8xx_CONS_SMC1)) && (defined(CONFIG_MPC823) || defined(CONFIG_MPC850)) + volatile iop8xx_t *ip = (iop8xx_t *)&(im->im_ioport); +#endif + uint dpaddr; + volatile serialbuffer_t *rtx; + + /* initialize pointers to SMC */ + + sp = (smc_t *) &(cp->cp_smc[SMC_INDEX]); + up = (smc_uart_t *) &cp->cp_dparam[PROFF_SMC]; +#ifdef CONFIG_SYS_SMC_UCODE_PATCH + up = (smc_uart_t *) &cp->cp_dpmem[up->smc_rpbase]; +#else + /* Disable relocation */ + up->smc_rpbase = 0; +#endif + + /* Disable transmitter/receiver. */ + sp->smc_smcmr &= ~(SMCMR_REN | SMCMR_TEN); + + /* Enable SDMA. */ + im->im_siu_conf.sc_sdcr = 1; + + /* clear error conditions */ +#ifdef CONFIG_SYS_SDSR + im->im_sdma.sdma_sdsr = CONFIG_SYS_SDSR; +#else + im->im_sdma.sdma_sdsr = 0x83; +#endif + + /* clear SDMA interrupt mask */ +#ifdef CONFIG_SYS_SDMR + im->im_sdma.sdma_sdmr = CONFIG_SYS_SDMR; +#else + im->im_sdma.sdma_sdmr = 0x00; +#endif + +#if defined(CONFIG_8xx_CONS_SMC1) + /* Use Port B for SMC1 instead of other functions. */ + cp->cp_pbpar |= 0x000000c0; + cp->cp_pbdir &= ~0x000000c0; + cp->cp_pbodr &= ~0x000000c0; +#else /* CONFIG_8xx_CONS_SMC2 */ +# if defined(CONFIG_MPC823) || defined(CONFIG_MPC850) + /* Use Port A for SMC2 instead of other functions. */ + ip->iop_papar |= 0x00c0; + ip->iop_padir &= ~0x00c0; + ip->iop_paodr &= ~0x00c0; +# else /* must be a 860 then */ + /* Use Port B for SMC2 instead of other functions. + */ + cp->cp_pbpar |= 0x00000c00; + cp->cp_pbdir &= ~0x00000c00; + cp->cp_pbodr &= ~0x00000c00; +# endif +#endif + +#if defined(CONFIG_FADS) || defined(CONFIG_ADS) + /* Enable RS232 */ +#if defined(CONFIG_8xx_CONS_SMC1) + *((uint *) BCSR1) &= ~BCSR1_RS232EN_1; +#else + *((uint *) BCSR1) &= ~BCSR1_RS232EN_2; +#endif +#endif /* CONFIG_FADS */ + +#if defined(CONFIG_RPXLITE) || defined(CONFIG_RPXCLASSIC) + /* Enable Monitor Port Transceiver */ + *((uchar *) BCSR0) |= BCSR0_ENMONXCVR ; +#endif /* CONFIG_RPXLITE */ + + /* Set the physical address of the host memory buffers in + * the buffer descriptors. + */ + +#ifdef CONFIG_SYS_ALLOC_DPRAM + /* allocate + * size of struct serialbuffer with bd rx/tx, buffer rx/tx and rx index + */ + dpaddr = dpram_alloc_align((sizeof(serialbuffer_t)), 8); +#else + dpaddr = CPM_SERIAL_BASE ; +#endif + + rtx = (serialbuffer_t *)&cp->cp_dpmem[dpaddr]; + /* Allocate space for two buffer descriptors in the DP ram. + * For now, this address seems OK, but it may have to + * change with newer versions of the firmware. + * damm: allocating space after the two buffers for rx/tx data + */ + + rtx->rxbd.cbd_bufaddr = (uint) &rtx->rxbuf; + rtx->rxbd.cbd_sc = 0; + + rtx->txbd.cbd_bufaddr = (uint) &rtx->txbuf; + rtx->txbd.cbd_sc = 0; + + /* Set up the uart parameters in the parameter ram. */ + up->smc_rbase = dpaddr; + up->smc_tbase = dpaddr+sizeof(cbd_t); + up->smc_rfcr = SMC_EB; + up->smc_tfcr = SMC_EB; +#if defined (CONFIG_SYS_SMC_UCODE_PATCH) + up->smc_rbptr = up->smc_rbase; + up->smc_tbptr = up->smc_tbase; + up->smc_rstate = 0; + up->smc_tstate = 0; +#endif + +#if defined(CONFIG_MBX) + board_serial_init(); +#endif /* CONFIG_MBX */ + + /* Set UART mode, 8 bit, no parity, one stop. + * Enable receive and transmit. + */ + sp->smc_smcmr = smcr_mk_clen(9) | SMCMR_SM_UART; + + /* Mask all interrupts and remove anything pending. + */ + sp->smc_smcm = 0; + sp->smc_smce = 0xff; + +#ifdef CONFIG_SYS_SPC1920_SMC1_CLK4 + /* clock source is PLD */ + + /* set freq to 19200 Baud */ + *((volatile uchar *) CONFIG_SYS_SPC1920_PLD_BASE+6) = 0x3; + /* configure clk4 as input */ + im->im_ioport.iop_pdpar |= 0x800; + im->im_ioport.iop_pddir &= ~0x800; + + cp->cp_simode = ((cp->cp_simode & ~0xf000) | 0x7000); +#else + /* Set up the baud rate generator */ + smc_setbrg (); +#endif + + /* Make the first buffer the only buffer. */ + rtx->txbd.cbd_sc |= BD_SC_WRAP; + rtx->rxbd.cbd_sc |= BD_SC_EMPTY | BD_SC_WRAP; + + /* single/multi character receive. */ + up->smc_mrblr = CONFIG_SYS_SMC_RXBUFLEN; + up->smc_maxidl = CONFIG_SYS_MAXIDLE; + rtx->rxindex = 0; + + /* Initialize Tx/Rx parameters. */ + while (cp->cp_cpcr & CPM_CR_FLG) /* wait if cp is busy */ + ; + + cp->cp_cpcr = mk_cr_cmd(CPM_CR_CH_SMC, CPM_CR_INIT_TRX) | CPM_CR_FLG; + + while (cp->cp_cpcr & CPM_CR_FLG) /* wait if cp is busy */ + ; + + /* Enable transmitter/receiver. */ + sp->smc_smcmr |= SMCMR_REN | SMCMR_TEN; + + return (0); +} + +static void +smc_putc(const char c) +{ + volatile smc_uart_t *up; + volatile immap_t *im = (immap_t *)CONFIG_SYS_IMMR; + volatile cpm8xx_t *cpmp = &(im->im_cpm); + volatile serialbuffer_t *rtx; + +#ifdef CONFIG_MODEM_SUPPORT + if (gd->be_quiet) + return; +#endif + + if (c == '\n') + smc_putc ('\r'); + + up = (smc_uart_t *)&cpmp->cp_dparam[PROFF_SMC]; +#ifdef CONFIG_SYS_SMC_UCODE_PATCH + up = (smc_uart_t *) &cpmp->cp_dpmem[up->smc_rpbase]; +#endif + + rtx = (serialbuffer_t *)&cpmp->cp_dpmem[up->smc_rbase]; + + /* Wait for last character to go. */ + rtx->txbuf = c; + rtx->txbd.cbd_datlen = 1; + rtx->txbd.cbd_sc |= BD_SC_READY; + __asm__("eieio"); + + while (rtx->txbd.cbd_sc & BD_SC_READY) { + WATCHDOG_RESET (); + __asm__("eieio"); + } +} + +static void +smc_puts (const char *s) +{ + while (*s) { + smc_putc (*s++); + } +} + +static int +smc_getc(void) +{ + volatile smc_uart_t *up; + volatile immap_t *im = (immap_t *)CONFIG_SYS_IMMR; + volatile cpm8xx_t *cpmp = &(im->im_cpm); + volatile serialbuffer_t *rtx; + unsigned char c; + + up = (smc_uart_t *)&cpmp->cp_dparam[PROFF_SMC]; +#ifdef CONFIG_SYS_SMC_UCODE_PATCH + up = (smc_uart_t *) &cpmp->cp_dpmem[up->smc_rpbase]; +#endif + rtx = (serialbuffer_t *)&cpmp->cp_dpmem[up->smc_rbase]; + + /* Wait for character to show up. */ + while (rtx->rxbd.cbd_sc & BD_SC_EMPTY) + WATCHDOG_RESET (); + + /* the characters are read one by one, + * use the rxindex to know the next char to deliver + */ + c = *(unsigned char *) (rtx->rxbd.cbd_bufaddr+rtx->rxindex); + rtx->rxindex++; + + /* check if all char are readout, then make prepare for next receive */ + if (rtx->rxindex >= rtx->rxbd.cbd_datlen) { + rtx->rxindex = 0; + rtx->rxbd.cbd_sc |= BD_SC_EMPTY; + } + return(c); +} + +static int +smc_tstc(void) +{ + volatile smc_uart_t *up; + volatile immap_t *im = (immap_t *)CONFIG_SYS_IMMR; + volatile cpm8xx_t *cpmp = &(im->im_cpm); + volatile serialbuffer_t *rtx; + + up = (smc_uart_t *)&cpmp->cp_dparam[PROFF_SMC]; +#ifdef CONFIG_SYS_SMC_UCODE_PATCH + up = (smc_uart_t *) &cpmp->cp_dpmem[up->smc_rpbase]; +#endif + + rtx = (serialbuffer_t *)&cpmp->cp_dpmem[up->smc_rbase]; + + return !(rtx->rxbd.cbd_sc & BD_SC_EMPTY); +} + +struct serial_device serial_smc_device = +{ + "serial_smc", + "SMC", + smc_init, + smc_setbrg, + smc_getc, + smc_tstc, + smc_putc, + smc_puts, +}; + +#endif /* CONFIG_8xx_CONS_SMC1 || CONFIG_8xx_CONS_SMC2 */ + +#if defined(CONFIG_8xx_CONS_SCC1) || defined(CONFIG_8xx_CONS_SCC2) || \ + defined(CONFIG_8xx_CONS_SCC3) || defined(CONFIG_8xx_CONS_SCC4) + +static void +scc_setbrg (void) +{ + volatile immap_t *im = (immap_t *)CONFIG_SYS_IMMR; + volatile cpm8xx_t *cp = &(im->im_cpm); + + /* Set up the baud rate generator. + * See 8xx_io/commproc.c for details. + * + * Wire BRG1 to SCCx + */ + + cp->cp_sicr &= ~(0x000000FF << (8 * SCC_INDEX)); + + serial_setdivisor(cp); +} + +static int scc_init (void) +{ + volatile immap_t *im = (immap_t *)CONFIG_SYS_IMMR; + volatile scc_t *sp; + volatile scc_uart_t *up; + volatile cbd_t *tbdf, *rbdf; + volatile cpm8xx_t *cp = &(im->im_cpm); + uint dpaddr; +#if (SCC_INDEX != 2) || !defined(CONFIG_MPC850) + volatile iop8xx_t *ip = (iop8xx_t *)&(im->im_ioport); +#endif + + /* initialize pointers to SCC */ + + sp = (scc_t *) &(cp->cp_scc[SCC_INDEX]); + up = (scc_uart_t *) &cp->cp_dparam[PROFF_SCC]; + +#if defined(CONFIG_LWMON) && defined(CONFIG_8xx_CONS_SCC2) + { /* Disable Ethernet, enable Serial */ + uchar c; + + c = pic_read (0x61); + c &= ~0x40; /* enable COM3 */ + c |= 0x80; /* disable Ethernet */ + pic_write (0x61, c); + + /* enable RTS2 */ + cp->cp_pbpar |= 0x2000; + cp->cp_pbdat |= 0x2000; + cp->cp_pbdir |= 0x2000; + } +#endif /* CONFIG_LWMON */ + + /* Disable transmitter/receiver. */ + sp->scc_gsmrl &= ~(SCC_GSMRL_ENR | SCC_GSMRL_ENT); + +#if (SCC_INDEX == 2) && defined(CONFIG_MPC850) + /* + * The MPC850 has SCC3 on Port B + */ + cp->cp_pbpar |= 0x06; + cp->cp_pbdir &= ~0x06; + cp->cp_pbodr &= ~0x06; + +#elif (SCC_INDEX < 2) || !defined(CONFIG_IP860) + /* + * Standard configuration for SCC's is on Part A + */ + ip->iop_papar |= ((3 << (2 * SCC_INDEX))); + ip->iop_padir &= ~((3 << (2 * SCC_INDEX))); + ip->iop_paodr &= ~((3 << (2 * SCC_INDEX))); +#else + /* + * The IP860 has SCC3 and SCC4 on Port D + */ + ip->iop_pdpar |= ((3 << (2 * SCC_INDEX))); +#endif + + /* Allocate space for two buffer descriptors in the DP ram. */ + +#ifdef CONFIG_SYS_ALLOC_DPRAM + dpaddr = dpram_alloc_align (sizeof(cbd_t)*2 + 2, 8) ; +#else + dpaddr = CPM_SERIAL2_BASE ; +#endif + + /* Enable SDMA. */ + im->im_siu_conf.sc_sdcr = 0x0001; + + /* Set the physical address of the host memory buffers in + * the buffer descriptors. + */ + + rbdf = (cbd_t *)&cp->cp_dpmem[dpaddr]; + rbdf->cbd_bufaddr = (uint) (rbdf+2); + rbdf->cbd_sc = 0; + tbdf = rbdf + 1; + tbdf->cbd_bufaddr = ((uint) (rbdf+2)) + 1; + tbdf->cbd_sc = 0; + + /* Set up the baud rate generator. */ + scc_setbrg (); + + /* Set up the uart parameters in the parameter ram. */ + up->scc_genscc.scc_rbase = dpaddr; + up->scc_genscc.scc_tbase = dpaddr+sizeof(cbd_t); + + /* Initialize Tx/Rx parameters. */ + while (cp->cp_cpcr & CPM_CR_FLG) /* wait if cp is busy */ + ; + cp->cp_cpcr = mk_cr_cmd(CPM_CR_CH_SCC, CPM_CR_INIT_TRX) | CPM_CR_FLG; + + while (cp->cp_cpcr & CPM_CR_FLG) /* wait if cp is busy */ + ; + + up->scc_genscc.scc_rfcr = SCC_EB | 0x05; + up->scc_genscc.scc_tfcr = SCC_EB | 0x05; + + up->scc_genscc.scc_mrblr = 1; /* Single character receive */ + up->scc_maxidl = 0; /* disable max idle */ + up->scc_brkcr = 1; /* send one break character on stop TX */ + up->scc_parec = 0; + up->scc_frmec = 0; + up->scc_nosec = 0; + up->scc_brkec = 0; + up->scc_uaddr1 = 0; + up->scc_uaddr2 = 0; + up->scc_toseq = 0; + up->scc_char1 = 0x8000; + up->scc_char2 = 0x8000; + up->scc_char3 = 0x8000; + up->scc_char4 = 0x8000; + up->scc_char5 = 0x8000; + up->scc_char6 = 0x8000; + up->scc_char7 = 0x8000; + up->scc_char8 = 0x8000; + up->scc_rccm = 0xc0ff; + + /* Set low latency / small fifo. */ + sp->scc_gsmrh = SCC_GSMRH_RFW; + + /* Set SCC(x) clock mode to 16x + * See 8xx_io/commproc.c for details. + * + * Wire BRG1 to SCCn + */ + + /* Set UART mode, clock divider 16 on Tx and Rx */ + sp->scc_gsmrl &= ~0xF; + sp->scc_gsmrl |= + (SCC_GSMRL_MODE_UART | SCC_GSMRL_TDCR_16 | SCC_GSMRL_RDCR_16); + + sp->scc_psmr = 0; + sp->scc_psmr |= SCU_PSMR_CL; + + /* Mask all interrupts and remove anything pending. */ + sp->scc_sccm = 0; + sp->scc_scce = 0xffff; + sp->scc_dsr = 0x7e7e; + sp->scc_psmr = 0x3000; + + /* Make the first buffer the only buffer. */ + tbdf->cbd_sc |= BD_SC_WRAP; + rbdf->cbd_sc |= BD_SC_EMPTY | BD_SC_WRAP; + + /* Enable transmitter/receiver. */ + sp->scc_gsmrl |= (SCC_GSMRL_ENR | SCC_GSMRL_ENT); + + return (0); +} + +static void +scc_putc(const char c) +{ + volatile cbd_t *tbdf; + volatile char *buf; + volatile scc_uart_t *up; + volatile immap_t *im = (immap_t *)CONFIG_SYS_IMMR; + volatile cpm8xx_t *cpmp = &(im->im_cpm); + +#ifdef CONFIG_MODEM_SUPPORT + if (gd->be_quiet) + return; +#endif + + if (c == '\n') + scc_putc ('\r'); + + up = (scc_uart_t *)&cpmp->cp_dparam[PROFF_SCC]; + + tbdf = (cbd_t *)&cpmp->cp_dpmem[up->scc_genscc.scc_tbase]; + + /* Wait for last character to go. */ + + buf = (char *)tbdf->cbd_bufaddr; + + *buf = c; + tbdf->cbd_datlen = 1; + tbdf->cbd_sc |= BD_SC_READY; + __asm__("eieio"); + + while (tbdf->cbd_sc & BD_SC_READY) { + __asm__("eieio"); + WATCHDOG_RESET (); + } +} + +static void +scc_puts (const char *s) +{ + while (*s) { + scc_putc (*s++); + } +} + +static int +scc_getc(void) +{ + volatile cbd_t *rbdf; + volatile unsigned char *buf; + volatile scc_uart_t *up; + volatile immap_t *im = (immap_t *)CONFIG_SYS_IMMR; + volatile cpm8xx_t *cpmp = &(im->im_cpm); + unsigned char c; + + up = (scc_uart_t *)&cpmp->cp_dparam[PROFF_SCC]; + + rbdf = (cbd_t *)&cpmp->cp_dpmem[up->scc_genscc.scc_rbase]; + + /* Wait for character to show up. */ + buf = (unsigned char *)rbdf->cbd_bufaddr; + + while (rbdf->cbd_sc & BD_SC_EMPTY) + WATCHDOG_RESET (); + + c = *buf; + rbdf->cbd_sc |= BD_SC_EMPTY; + + return(c); +} + +static int +scc_tstc(void) +{ + volatile cbd_t *rbdf; + volatile scc_uart_t *up; + volatile immap_t *im = (immap_t *)CONFIG_SYS_IMMR; + volatile cpm8xx_t *cpmp = &(im->im_cpm); + + up = (scc_uart_t *)&cpmp->cp_dparam[PROFF_SCC]; + + rbdf = (cbd_t *)&cpmp->cp_dpmem[up->scc_genscc.scc_rbase]; + + return(!(rbdf->cbd_sc & BD_SC_EMPTY)); +} + +struct serial_device serial_scc_device = +{ + "serial_scc", + "SCC", + scc_init, + scc_setbrg, + scc_getc, + scc_tstc, + scc_putc, + scc_puts, +}; + +#endif /* CONFIG_8xx_CONS_SCCx */ + +#ifdef CONFIG_MODEM_SUPPORT +void disable_putc(void) +{ + gd->be_quiet = 1; +} + +void enable_putc(void) +{ + gd->be_quiet = 0; +} +#endif + +#if defined(CONFIG_CMD_KGDB) + +void +kgdb_serial_init(void) +{ + int i = -1; + + if (strcmp(default_serial_console()->ctlr, "SMC") == 0) + { +#if defined(CONFIG_8xx_CONS_SMC1) + i = 1; +#elif defined(CONFIG_8xx_CONS_SMC2) + i = 2; +#endif + } + else if (strcmp(default_serial_console()->ctlr, "SMC") == 0) + { +#if defined(CONFIG_8xx_CONS_SCC1) + i = 1; +#elif defined(CONFIG_8xx_CONS_SCC2) + i = 2; +#elif defined(CONFIG_8xx_CONS_SCC3) + i = 3; +#elif defined(CONFIG_8xx_CONS_SCC4) + i = 4; +#endif + } + + if (i >= 0) + { + serial_printf("[on %s%d] ", default_serial_console()->ctlr, i); + } +} + +void +putDebugChar (int c) +{ + serial_putc (c); +} + +void +putDebugStr (const char *str) +{ + serial_puts (str); +} + +int +getDebugChar (void) +{ + return serial_getc(); +} + +void +kgdb_interruptible (int yes) +{ + return; +} +#endif + +#endif /* CONFIG_8xx_CONS_NONE */ diff --git a/arch/powerpc/cpu/mpc8xx/speed.c b/arch/powerpc/cpu/mpc8xx/speed.c new file mode 100644 index 0000000..f309f29 --- /dev/null +++ b/arch/powerpc/cpu/mpc8xx/speed.c @@ -0,0 +1,416 @@ +/* + * (C) Copyright 2000-2004 + * Wolfgang Denk, DENX Software Engineering, wd@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> +#include <mpc8xx.h> +#include <asm/processor.h> + +DECLARE_GLOBAL_DATA_PTR; + +#if !defined(CONFIG_8xx_CPUCLK_DEFAULT) || defined(CONFIG_SYS_MEASURE_CPUCLK) || defined(DEBUG) + +#define PITC_SHIFT 16 +#define PITR_SHIFT 16 +/* pitc values to time for 58/8192 seconds (about 70.8 milliseconds) */ +#define SPEED_PIT_COUNTS 58 +#define SPEED_PITC ((SPEED_PIT_COUNTS - 1) << PITC_SHIFT) +#define SPEED_PITC_INIT ((SPEED_PIT_COUNTS + 1) << PITC_SHIFT) + +/* Access functions for the Machine State Register */ +static __inline__ unsigned long get_msr(void) +{ + unsigned long msr; + + asm volatile("mfmsr %0" : "=r" (msr) :); + return msr; +} + +static __inline__ void set_msr(unsigned long msr) +{ + asm volatile("mtmsr %0" : : "r" (msr)); +} + +/* ------------------------------------------------------------------------- */ + +/* + * Measure CPU clock speed (core clock GCLK1, GCLK2), + * also determine bus clock speed (checking bus divider factor) + * + * (Approx. GCLK frequency in Hz) + * + * Initializes timer 2 and PIT, but disables them before return. + * [Use timer 2, because MPC823 CPUs mask 0.x do not have timers 3 and 4] + * + * When measuring the CPU clock against the PIT, we count cpu clocks + * for 58/8192 seconds with a prescale divide by 177 for the cpu clock. + * These strange values for the timing interval and prescaling are used + * because the formula for the CPU clock is: + * + * CPU clock = count * (177 * (8192 / 58)) + * + * = count * 24999.7241 + * + * which is very close to + * + * = count * 25000 + * + * Since the count gives the CPU clock divided by 25000, we can get + * the CPU clock rounded to the nearest 0.1 MHz by + * + * CPU clock = ((count + 2) / 4) * 100000; + * + * The rounding is important since the measurement is sometimes going + * to be high or low by 0.025 MHz, depending on exactly how the clocks + * and counters interact. By rounding we get the exact answer for any + * CPU clock that is an even multiple of 0.1 MHz. + */ + +unsigned long measure_gclk(void) +{ + volatile immap_t *immr = (immap_t *) CONFIG_SYS_IMMR; + volatile cpmtimer8xx_t *timerp = &immr->im_cpmtimer; + ulong timer2_val; + ulong msr_val; + +#ifdef CONFIG_SYS_8XX_XIN + /* dont use OSCM, only use EXTCLK/512 */ + immr->im_clkrst.car_sccr |= SCCR_RTSEL | SCCR_RTDIV; +#else + immr->im_clkrst.car_sccr &= ~(SCCR_RTSEL | SCCR_RTDIV); +#endif + + /* Reset + Stop Timer 2, no cascading + */ + timerp->cpmt_tgcr &= ~(TGCR_CAS2 | TGCR_RST2); + + /* Keep stopped, halt in debug mode + */ + timerp->cpmt_tgcr |= (TGCR_FRZ2 | TGCR_STP2); + + /* Timer 2 setup: + * Output ref. interrupt disable, int. clock + * Prescale by 177. Note that prescaler divides by value + 1 + * so we must subtract 1 here. + */ + timerp->cpmt_tmr2 = ((177 - 1) << TMR_PS_SHIFT) | TMR_ICLK_IN_GEN; + + timerp->cpmt_tcn2 = 0; /* reset state */ + timerp->cpmt_tgcr |= TGCR_RST2; /* enable timer 2 */ + + /* + * PIT setup: + * + * We want to time for SPEED_PITC_COUNTS counts (of 8192 Hz), + * so the count value would be SPEED_PITC_COUNTS - 1. + * But there would be an uncertainty in the start time of 1/4 + * count since when we enable the PIT the count is not + * synchronized to the 32768 Hz oscillator. The trick here is + * to start the count higher and wait until the PIT count + * changes to the required value before starting timer 2. + * + * One count high should be enough, but occasionally the start + * is off by 1 or 2 counts of 32768 Hz. With the start value + * set two counts high it seems very reliable. + */ + + immr->im_sitk.sitk_pitck = KAPWR_KEY; /* PIT initialization */ + immr->im_sit.sit_pitc = SPEED_PITC_INIT; + + immr->im_sitk.sitk_piscrk = KAPWR_KEY; + immr->im_sit.sit_piscr = CONFIG_SYS_PISCR; + + /* + * Start measurement - disable interrupts, just in case + */ + msr_val = get_msr (); + set_msr (msr_val & ~MSR_EE); + + immr->im_sit.sit_piscr |= PISCR_PTE; + + /* spin until get exact count when we want to start */ + while (immr->im_sit.sit_pitr > SPEED_PITC); + + timerp->cpmt_tgcr &= ~TGCR_STP2; /* Start Timer 2 */ + while ((immr->im_sit.sit_piscr & PISCR_PS) == 0); + timerp->cpmt_tgcr |= TGCR_STP2; /* Stop Timer 2 */ + + /* re-enable external interrupts if they were on */ + set_msr (msr_val); + + /* Disable timer and PIT + */ + timer2_val = timerp->cpmt_tcn2; /* save before reset timer */ + + timerp->cpmt_tgcr &= ~(TGCR_RST2 | TGCR_FRZ2 | TGCR_STP2); + immr->im_sit.sit_piscr &= ~PISCR_PTE; + +#if defined(CONFIG_SYS_8XX_XIN) + /* not using OSCM, using XIN, so scale appropriately */ + return (((timer2_val + 2) / 4) * (CONFIG_SYS_8XX_XIN/512))/8192 * 100000L; +#else + return ((timer2_val + 2) / 4) * 100000L; /* convert to Hz */ +#endif +} + +#endif + +void get_brgclk(uint sccr) +{ + uint divider = 0; + + switch((sccr&SCCR_DFBRG11)>>11){ + case 0: + divider = 1; + break; + case 1: + divider = 4; + break; + case 2: + divider = 16; + break; + case 3: + divider = 64; + break; + } + gd->brg_clk = gd->cpu_clk/divider; +} + +#if !defined(CONFIG_8xx_CPUCLK_DEFAULT) + +/* + * get_clocks() fills in gd->cpu_clock depending on CONFIG_8xx_GCLK_FREQ + * or (if it is not defined) measure_gclk() (which uses the ref clock) + * from above. + */ +int get_clocks (void) +{ + uint immr = get_immr (0); /* Return full IMMR contents */ + volatile immap_t *immap = (immap_t *) (immr & 0xFFFF0000); + uint sccr = immap->im_clkrst.car_sccr; + /* + * If for some reason measuring the gclk frequency won't + * work, we return the hardwired value. + * (For example, the cogent CMA286-60 CPU module has no + * separate oscillator for PITRTCLK) + */ +#if defined(CONFIG_8xx_GCLK_FREQ) + gd->cpu_clk = CONFIG_8xx_GCLK_FREQ; +#elif defined(CONFIG_8xx_OSCLK) +#define PLPRCR_val(a) ((pll & PLPRCR_ ## a ## _MSK) >> PLPRCR_ ## a ## _SHIFT) + uint pll = immap->im_clkrst.car_plprcr; + uint clk; + + if ((immr & 0x0FFF) >= MPC8xx_NEW_CLK) { /* MPC866/87x/88x series */ + clk = ((CONFIG_8xx_OSCLK / (PLPRCR_val(PDF)+1)) * + (PLPRCR_val(MFI) + PLPRCR_val(MFN) / (PLPRCR_val(MFD)+1))) / + (1<<PLPRCR_val(S)); + } else { + clk = CONFIG_8xx_OSCLK * (PLPRCR_val(MF)+1); + } + if (pll & PLPRCR_CSRC) { /* Low frequency division factor is used */ + gd->cpu_clk = clk / (2 << ((sccr >> 8) & 7)); + } else { /* High frequency division factor is used */ + gd->cpu_clk = clk / (1 << ((sccr >> 5) & 7)); + } +#else + gd->cpu_clk = measure_gclk(); +#endif /* CONFIG_8xx_GCLK_FREQ */ + + if ((sccr & SCCR_EBDF11) == 0) { + /* No Bus Divider active */ + gd->bus_clk = gd->cpu_clk; + } else { + /* The MPC8xx has only one BDF: half clock speed */ + gd->bus_clk = gd->cpu_clk / 2; + } + + get_brgclk(sccr); + + return (0); +} + +#else /* CONFIG_8xx_CPUCLK_DEFAULT defined, use dynamic clock setting */ + +static long init_pll_866 (long clk); + +/* This function sets up PLL (init_pll_866() is called) and + * fills gd->cpu_clk and gd->bus_clk according to the environment + * variable 'cpuclk' or to CONFIG_8xx_CPUCLK_DEFAULT (if 'cpuclk' + * contains invalid value). + * This functions requires an MPC866 or newer series CPU. + */ +int get_clocks_866 (void) +{ + volatile immap_t *immr = (immap_t *) CONFIG_SYS_IMMR; + char tmp[64]; + long cpuclk = 0; + long sccr_reg; + + if (getenv_r ("cpuclk", tmp, sizeof (tmp)) > 0) + cpuclk = simple_strtoul (tmp, NULL, 10) * 1000000; + + if ((CONFIG_SYS_8xx_CPUCLK_MIN > cpuclk) || (CONFIG_SYS_8xx_CPUCLK_MAX < cpuclk)) + cpuclk = CONFIG_8xx_CPUCLK_DEFAULT; + + gd->cpu_clk = init_pll_866 (cpuclk); +#if defined(CONFIG_SYS_MEASURE_CPUCLK) + gd->cpu_clk = measure_gclk (); +#endif + + get_brgclk(immr->im_clkrst.car_sccr); + + /* if cpu clock <= 66 MHz then set bus division factor to 1, + * otherwise set it to 2 + */ + sccr_reg = immr->im_clkrst.car_sccr; + sccr_reg &= ~SCCR_EBDF11; + + if (gd->cpu_clk <= 66000000) { + sccr_reg |= SCCR_EBDF00; /* bus division factor = 1 */ + gd->bus_clk = gd->cpu_clk; + } else { + sccr_reg |= SCCR_EBDF01; /* bus division factor = 2 */ + gd->bus_clk = gd->cpu_clk / 2; + } + immr->im_clkrst.car_sccr = sccr_reg; + + return (0); +} + +/* Adjust sdram refresh rate to actual CPU clock. + */ +int sdram_adjust_866 (void) +{ + volatile immap_t *immr = (immap_t *) CONFIG_SYS_IMMR; + long mamr; + + mamr = immr->im_memctl.memc_mamr; + mamr &= ~MAMR_PTA_MSK; + mamr |= ((gd->cpu_clk / CONFIG_SYS_PTA_PER_CLK) << MAMR_PTA_SHIFT); + immr->im_memctl.memc_mamr = mamr; + + return (0); +} + +/* Configure PLL for MPC866/859/885 CPU series + * PLL multiplication factor is set to the value nearest to the desired clk, + * assuming a oscclk of 10 MHz. + */ +static long init_pll_866 (long clk) +{ + extern void plprcr_write_866 (long); + + volatile immap_t *immr = (immap_t *) CONFIG_SYS_IMMR; + long n, plprcr; + char mfi, mfn, mfd, s, pdf; + long step_mfi, step_mfn; + + if (clk < 20000000) { + clk *= 2; + pdf = 1; + } else { + pdf = 0; + } + + if (clk < 40000000) { + s = 2; + step_mfi = CONFIG_8xx_OSCLK / 4; + mfd = 7; + step_mfn = CONFIG_8xx_OSCLK / 30; + } else if (clk < 80000000) { + s = 1; + step_mfi = CONFIG_8xx_OSCLK / 2; + mfd = 14; + step_mfn = CONFIG_8xx_OSCLK / 30; + } else { + s = 0; + step_mfi = CONFIG_8xx_OSCLK; + mfd = 29; + step_mfn = CONFIG_8xx_OSCLK / 30; + } + + /* Calculate integer part of multiplication factor + */ + n = clk / step_mfi; + mfi = (char)n; + + /* Calculate numerator of fractional part of multiplication factor + */ + n = clk - (n * step_mfi); + mfn = (char)(n / step_mfn); + + /* Calculate effective clk + */ + n = ((mfi * step_mfi) + (mfn * step_mfn)) / (pdf + 1); + + immr->im_clkrstk.cark_plprcrk = KAPWR_KEY; + + plprcr = (immr->im_clkrst.car_plprcr & ~(PLPRCR_MFN_MSK + | PLPRCR_MFD_MSK | PLPRCR_S_MSK + | PLPRCR_MFI_MSK | PLPRCR_DBRMO + | PLPRCR_PDF_MSK)) + | (mfn << PLPRCR_MFN_SHIFT) + | (mfd << PLPRCR_MFD_SHIFT) + | (s << PLPRCR_S_SHIFT) + | (mfi << PLPRCR_MFI_SHIFT) + | (pdf << PLPRCR_PDF_SHIFT); + + if( (mfn > 0) && ((mfd / mfn) > 10) ) + plprcr |= PLPRCR_DBRMO; + + plprcr_write_866 (plprcr); /* set value using SIU4/9 workaround */ + immr->im_clkrstk.cark_plprcrk = 0x00000000; + + return (n); +} + +#endif /* CONFIG_8xx_CPUCLK_DEFAULT */ + +#if defined(CONFIG_TQM8xxL) && !defined(CONFIG_TQM866M) \ + && !defined(CONFIG_TQM885D) +/* + * Adjust sdram refresh rate to actual CPU clock + * and set timebase source according to actual CPU clock + */ +int adjust_sdram_tbs_8xx (void) +{ + volatile immap_t *immr = (immap_t *) CONFIG_SYS_IMMR; + long mamr; + long sccr; + + mamr = immr->im_memctl.memc_mamr; + mamr &= ~MAMR_PTA_MSK; + mamr |= ((gd->cpu_clk / CONFIG_SYS_PTA_PER_CLK) << MAMR_PTA_SHIFT); + immr->im_memctl.memc_mamr = mamr; + + if (gd->cpu_clk < 67000000) { + sccr = immr->im_clkrst.car_sccr; + sccr |= SCCR_TBS; + immr->im_clkrst.car_sccr = sccr; + } + + return (0); +} +#endif /* CONFIG_TQM8xxL/M, !TQM866M, !TQM885D */ + +/* ------------------------------------------------------------------------- */ diff --git a/arch/powerpc/cpu/mpc8xx/spi.c b/arch/powerpc/cpu/mpc8xx/spi.c new file mode 100644 index 0000000..b2ac23e --- /dev/null +++ b/arch/powerpc/cpu/mpc8xx/spi.c @@ -0,0 +1,560 @@ +/* + * Copyright (c) 2001 Navin Boppuri / Prashant Patel + * <nboppuri@trinetcommunication.com>, + * <pmpatel@trinetcommunication.com> + * Copyright (c) 2001 Gerd Mennchen <Gerd.Mennchen@icn.siemens.de> + * Copyright (c) 2001 Wolfgang Denk, DENX Software Engineering, <wd@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 + */ + +/* + * MPC8xx CPM SPI interface. + * + * Parts of this code are probably not portable and/or specific to + * the board which I used for the tests. Please send fixes/complaints + * to wd@denx.de + * + */ + +#include <common.h> +#include <mpc8xx.h> +#include <commproc.h> +#include <linux/ctype.h> +#include <malloc.h> +#include <post.h> +#include <serial.h> + +#if (defined(CONFIG_SPI)) || (CONFIG_POST & CONFIG_SYS_POST_SPI) + +/* Warning: + * You cannot enable DEBUG for early system initalization, i. e. when + * this driver is used to read environment parameters like "baudrate" + * from EEPROM which are used to initialize the serial port which is + * needed to print the debug messages... + */ +#undef DEBUG + +#define SPI_EEPROM_WREN 0x06 +#define SPI_EEPROM_RDSR 0x05 +#define SPI_EEPROM_READ 0x03 +#define SPI_EEPROM_WRITE 0x02 + +/* --------------------------------------------------------------- + * Offset for initial SPI buffers in DPRAM: + * We need a 520 byte scratch DPRAM area to use at an early stage. + * It is used between the two initialization calls (spi_init_f() + * and spi_init_r()). + * The value 0xb00 makes it far enough from the start of the data + * area (as well as from the stack pointer). + * --------------------------------------------------------------- */ +#ifndef CONFIG_SYS_SPI_INIT_OFFSET +#define CONFIG_SYS_SPI_INIT_OFFSET 0xB00 +#endif + +#ifdef DEBUG + +#define DPRINT(a) printf a; +/* ----------------------------------------------- + * Helper functions to peek into tx and rx buffers + * ----------------------------------------------- */ +static const char * const hex_digit = "0123456789ABCDEF"; + +static char quickhex (int i) +{ + return hex_digit[i]; +} + +static void memdump (void *pv, int num) +{ + int i; + unsigned char *pc = (unsigned char *) pv; + + for (i = 0; i < num; i++) + printf ("%c%c ", quickhex (pc[i] >> 4), quickhex (pc[i] & 0x0f)); + printf ("\t"); + for (i = 0; i < num; i++) + printf ("%c", isprint (pc[i]) ? pc[i] : '.'); + printf ("\n"); +} +#else /* !DEBUG */ + +#define DPRINT(a) + +#endif /* DEBUG */ + +/* ------------------- + * Function prototypes + * ------------------- */ +void spi_init (void); + +ssize_t spi_read (uchar *, int, uchar *, int); +ssize_t spi_write (uchar *, int, uchar *, int); +ssize_t spi_xfer (size_t); + +/* ------------------- + * Variables + * ------------------- */ + +#define MAX_BUFFER 0x104 + +/* ---------------------------------------------------------------------- + * Initially we place the RX and TX buffers at a fixed location in DPRAM! + * ---------------------------------------------------------------------- */ +static uchar *rxbuf = + (uchar *)&((cpm8xx_t *)&((immap_t *)CONFIG_SYS_IMMR)->im_cpm)->cp_dpmem + [CONFIG_SYS_SPI_INIT_OFFSET]; +static uchar *txbuf = + (uchar *)&((cpm8xx_t *)&((immap_t *)CONFIG_SYS_IMMR)->im_cpm)->cp_dpmem + [CONFIG_SYS_SPI_INIT_OFFSET+MAX_BUFFER]; + +/* ************************************************************************** + * + * Function: spi_init_f + * + * Description: Init SPI-Controller (ROM part) + * + * return: --- + * + * *********************************************************************** */ +void spi_init_f (void) +{ + unsigned int dpaddr; + + volatile spi_t *spi; + volatile immap_t *immr; + volatile cpic8xx_t *cpi; + volatile cpm8xx_t *cp; + volatile iop8xx_t *iop; + volatile cbd_t *tbdf, *rbdf; + + immr = (immap_t *) CONFIG_SYS_IMMR; + cpi = (cpic8xx_t *)&immr->im_cpic; + iop = (iop8xx_t *) &immr->im_ioport; + cp = (cpm8xx_t *) &immr->im_cpm; + +#ifdef CONFIG_SYS_SPI_UCODE_PATCH + spi = (spi_t *)&cp->cp_dpmem[spi->spi_rpbase]; +#else + spi = (spi_t *)&cp->cp_dparam[PROFF_SPI]; + /* Disable relocation */ + spi->spi_rpbase = 0; +#endif + +/* 1 */ + /* ------------------------------------------------ + * Initialize Port B SPI pins -> page 34-8 MPC860UM + * (we are only in Master Mode !) + * ------------------------------------------------ */ + + /* -------------------------------------------- + * GPIO or per. Function + * PBPAR[28] = 1 [0x00000008] -> PERI: (SPIMISO) + * PBPAR[29] = 1 [0x00000004] -> PERI: (SPIMOSI) + * PBPAR[30] = 1 [0x00000002] -> PERI: (SPICLK) + * PBPAR[31] = 0 [0x00000001] -> GPIO: (CS for PCUE/CCM-EEPROM) + * -------------------------------------------- */ + cp->cp_pbpar |= 0x0000000E; /* set bits */ + cp->cp_pbpar &= ~0x00000001; /* reset bit */ + + /* ---------------------------------------------- + * In/Out or per. Function 0/1 + * PBDIR[28] = 1 [0x00000008] -> PERI1: SPIMISO + * PBDIR[29] = 1 [0x00000004] -> PERI1: SPIMOSI + * PBDIR[30] = 1 [0x00000002] -> PERI1: SPICLK + * PBDIR[31] = 1 [0x00000001] -> GPIO OUT: CS for PCUE/CCM-EEPROM + * ---------------------------------------------- */ + cp->cp_pbdir |= 0x0000000F; + + /* ---------------------------------------------- + * open drain or active output + * PBODR[28] = 1 [0x00000008] -> open drain: SPIMISO + * PBODR[29] = 0 [0x00000004] -> active output SPIMOSI + * PBODR[30] = 0 [0x00000002] -> active output: SPICLK + * PBODR[31] = 0 [0x00000001] -> active output: GPIO OUT: CS for PCUE/CCM + * ---------------------------------------------- */ + + cp->cp_pbodr |= 0x00000008; + cp->cp_pbodr &= ~0x00000007; + + /* Initialize the parameter ram. + * We need to make sure many things are initialized to zero + */ + spi->spi_rstate = 0; + spi->spi_rdp = 0; + spi->spi_rbptr = 0; + spi->spi_rbc = 0; + spi->spi_rxtmp = 0; + spi->spi_tstate = 0; + spi->spi_tdp = 0; + spi->spi_tbptr = 0; + spi->spi_tbc = 0; + spi->spi_txtmp = 0; + + /* Allocate space for one transmit and one receive buffer + * descriptor in the DP ram + */ +#ifdef CONFIG_SYS_ALLOC_DPRAM + dpaddr = dpram_alloc_align (sizeof(cbd_t)*2, 8); +#else + dpaddr = CPM_SPI_BASE; +#endif + +/* 3 */ + /* Set up the SPI parameters in the parameter ram */ + spi->spi_rbase = dpaddr; + spi->spi_tbase = dpaddr + sizeof (cbd_t); + + /***********IMPORTANT******************/ + + /* + * Setting transmit and receive buffer descriptor pointers + * initially to rbase and tbase. Only the microcode patches + * documentation talks about initializing this pointer. This + * is missing from the sample I2C driver. If you dont + * initialize these pointers, the kernel hangs. + */ + spi->spi_rbptr = spi->spi_rbase; + spi->spi_tbptr = spi->spi_tbase; + +/* 4 */ +#ifdef CONFIG_SYS_SPI_UCODE_PATCH + /* + * Initialize required parameters if using microcode patch. + */ + spi->spi_rstate = 0; + spi->spi_tstate = 0; +#else + /* Init SPI Tx + Rx Parameters */ + while (cp->cp_cpcr & CPM_CR_FLG) + ; + cp->cp_cpcr = mk_cr_cmd(CPM_CR_CH_SPI, CPM_CR_INIT_TRX) | CPM_CR_FLG; + while (cp->cp_cpcr & CPM_CR_FLG) + ; +#endif /* CONFIG_SYS_SPI_UCODE_PATCH */ + +/* 5 */ + /* Set SDMA configuration register */ + immr->im_siu_conf.sc_sdcr = 0x0001; + +/* 6 */ + /* Set to big endian. */ + spi->spi_tfcr = SMC_EB; + spi->spi_rfcr = SMC_EB; + +/* 7 */ + /* Set maximum receive size. */ + spi->spi_mrblr = MAX_BUFFER; + +/* 8 + 9 */ + /* tx and rx buffer descriptors */ + tbdf = (cbd_t *) & cp->cp_dpmem[spi->spi_tbase]; + rbdf = (cbd_t *) & cp->cp_dpmem[spi->spi_rbase]; + + tbdf->cbd_sc &= ~BD_SC_READY; + rbdf->cbd_sc &= ~BD_SC_EMPTY; + + /* Set the bd's rx and tx buffer address pointers */ + rbdf->cbd_bufaddr = (ulong) rxbuf; + tbdf->cbd_bufaddr = (ulong) txbuf; + +/* 10 + 11 */ + cp->cp_spim = 0; /* Mask all SPI events */ + cp->cp_spie = SPI_EMASK; /* Clear all SPI events */ + + return; +} + +/* ************************************************************************** + * + * Function: spi_init_r + * + * Description: Init SPI-Controller (RAM part) - + * The malloc engine is ready and we can move our buffers to + * normal RAM + * + * return: --- + * + * *********************************************************************** */ +void spi_init_r (void) +{ + volatile cpm8xx_t *cp; + volatile spi_t *spi; + volatile immap_t *immr; + volatile cbd_t *tbdf, *rbdf; + + immr = (immap_t *) CONFIG_SYS_IMMR; + cp = (cpm8xx_t *) &immr->im_cpm; + +#ifdef CONFIG_SYS_SPI_UCODE_PATCH + spi = (spi_t *)&cp->cp_dpmem[spi->spi_rpbase]; +#else + spi = (spi_t *)&cp->cp_dparam[PROFF_SPI]; + /* Disable relocation */ + spi->spi_rpbase = 0; +#endif + + /* tx and rx buffer descriptors */ + tbdf = (cbd_t *) & cp->cp_dpmem[spi->spi_tbase]; + rbdf = (cbd_t *) & cp->cp_dpmem[spi->spi_rbase]; + + /* Allocate memory for RX and TX buffers */ + rxbuf = (uchar *) malloc (MAX_BUFFER); + txbuf = (uchar *) malloc (MAX_BUFFER); + + rbdf->cbd_bufaddr = (ulong) rxbuf; + tbdf->cbd_bufaddr = (ulong) txbuf; + + return; +} + +/**************************************************************************** + * Function: spi_write + **************************************************************************** */ +ssize_t spi_write (uchar *addr, int alen, uchar *buffer, int len) +{ + int i; + + memset(rxbuf, 0, MAX_BUFFER); + memset(txbuf, 0, MAX_BUFFER); + *txbuf = SPI_EEPROM_WREN; /* write enable */ + spi_xfer(1); + memcpy(txbuf, addr, alen); + *txbuf = SPI_EEPROM_WRITE; /* WRITE memory array */ + memcpy(alen + txbuf, buffer, len); + spi_xfer(alen + len); + /* ignore received data */ + for (i = 0; i < 1000; i++) { + *txbuf = SPI_EEPROM_RDSR; /* read status */ + txbuf[1] = 0; + spi_xfer(2); + if (!(rxbuf[1] & 1)) { + break; + } + udelay(1000); + } + if (i >= 1000) { + printf ("*** spi_write: Time out while writing!\n"); + } + + return len; +} + +/**************************************************************************** + * Function: spi_read + **************************************************************************** */ +ssize_t spi_read (uchar *addr, int alen, uchar *buffer, int len) +{ + memset(rxbuf, 0, MAX_BUFFER); + memset(txbuf, 0, MAX_BUFFER); + memcpy(txbuf, addr, alen); + *txbuf = SPI_EEPROM_READ; /* READ memory array */ + + /* + * There is a bug in 860T (?) that cuts the last byte of input + * if we're reading into DPRAM. The solution we choose here is + * to always read len+1 bytes (we have one extra byte at the + * end of the buffer). + */ + spi_xfer(alen + len + 1); + memcpy(buffer, alen + rxbuf, len); + + return len; +} + +/**************************************************************************** + * Function: spi_xfer + **************************************************************************** */ +ssize_t spi_xfer (size_t count) +{ + volatile immap_t *immr; + volatile cpm8xx_t *cp; + volatile spi_t *spi; + cbd_t *tbdf, *rbdf; + ushort loop; + int tm; + + DPRINT (("*** spi_xfer entered ***\n")); + + immr = (immap_t *) CONFIG_SYS_IMMR; + cp = (cpm8xx_t *) &immr->im_cpm; + +#ifdef CONFIG_SYS_SPI_UCODE_PATCH + spi = (spi_t *)&cp->cp_dpmem[spi->spi_rpbase]; +#else + spi = (spi_t *)&cp->cp_dparam[PROFF_SPI]; + /* Disable relocation */ + spi->spi_rpbase = 0; +#endif + + tbdf = (cbd_t *) & cp->cp_dpmem[spi->spi_tbase]; + rbdf = (cbd_t *) & cp->cp_dpmem[spi->spi_rbase]; + + /* Set CS for device */ + cp->cp_pbdat &= ~0x0001; + + /* Setting tx bd status and data length */ + tbdf->cbd_sc = BD_SC_READY | BD_SC_LAST | BD_SC_WRAP; + tbdf->cbd_datlen = count; + + DPRINT (("*** spi_xfer: Bytes to be xferred: %d ***\n", + tbdf->cbd_datlen)); + + /* Setting rx bd status and data length */ + rbdf->cbd_sc = BD_SC_EMPTY | BD_SC_WRAP; + rbdf->cbd_datlen = 0; /* rx length has no significance */ + + loop = cp->cp_spmode & SPMODE_LOOP; + cp->cp_spmode = /*SPMODE_DIV16 |*/ /* BRG/16 mode not used here */ + loop | + SPMODE_REV | + SPMODE_MSTR | + SPMODE_EN | + SPMODE_LEN(8) | /* 8 Bits per char */ + SPMODE_PM(0x8) ; /* medium speed */ + cp->cp_spim = 0; /* Mask all SPI events */ + cp->cp_spie = SPI_EMASK; /* Clear all SPI events */ + + /* start spi transfer */ + DPRINT (("*** spi_xfer: Performing transfer ...\n")); + cp->cp_spcom |= SPI_STR; /* Start transmit */ + + /* -------------------------------- + * Wait for SPI transmit to get out + * or time out (1 second = 1000 ms) + * -------------------------------- */ + for (tm=0; tm<1000; ++tm) { + if (cp->cp_spie & SPI_TXB) { /* Tx Buffer Empty */ + DPRINT (("*** spi_xfer: Tx buffer empty\n")); + break; + } + if ((tbdf->cbd_sc & BD_SC_READY) == 0) { + DPRINT (("*** spi_xfer: Tx BD done\n")); + break; + } + udelay (1000); + } + if (tm >= 1000) { + printf ("*** spi_xfer: Time out while xferring to/from SPI!\n"); + } + DPRINT (("*** spi_xfer: ... transfer ended\n")); + +#ifdef DEBUG + printf ("\nspi_xfer: txbuf after xfer\n"); + memdump ((void *) txbuf, 16); /* dump of txbuf before transmit */ + printf ("spi_xfer: rxbuf after xfer\n"); + memdump ((void *) rxbuf, 16); /* dump of rxbuf after transmit */ + printf ("\n"); +#endif + + /* Clear CS for device */ + cp->cp_pbdat |= 0x0001; + + return count; +} +#endif /* CONFIG_SPI || (CONFIG_POST & CONFIG_SYS_POST_SPI) */ + +/* + * SPI test + * + * The Serial Peripheral Interface (SPI) is tested in the local loopback mode. + * The interface is configured accordingly and several packets + * are transfered. The configurable test parameters are: + * TEST_MIN_LENGTH - minimum size of packet to transfer + * TEST_MAX_LENGTH - maximum size of packet to transfer + * TEST_NUM - number of tests + */ + +#if CONFIG_POST & CONFIG_SYS_POST_SPI + +#define TEST_MIN_LENGTH 1 +#define TEST_MAX_LENGTH MAX_BUFFER +#define TEST_NUM 1 + +static void packet_fill (char * packet, int length) +{ + char c = (char) length; + int i; + + for (i = 0; i < length; i++) + { + packet[i] = c++; + } +} + +static int packet_check (char * packet, int length) +{ + char c = (char) length; + int i; + + for (i = 0; i < length; i++) { + if (packet[i] != c++) return -1; + } + + return 0; +} + +int spi_post_test (int flags) +{ + int res = -1; + volatile immap_t *immr = (immap_t *) CONFIG_SYS_IMMR; + volatile cpm8xx_t *cp = (cpm8xx_t *) & immr->im_cpm; + int i; + int l; + + spi_init_f (); + spi_init_r (); + + cp->cp_spmode |= SPMODE_LOOP; + + for (i = 0; i < TEST_NUM; i++) { + for (l = TEST_MIN_LENGTH; l <= TEST_MAX_LENGTH; l += 8) { + packet_fill ((char *)txbuf, l); + + spi_xfer (l); + + if (packet_check ((char *)rxbuf, l) < 0) { + goto Done; + } + } + } + + res = 0; + + Done: + + cp->cp_spmode &= ~SPMODE_LOOP; + + /* + * SCC2 parameter RAM space overlaps + * the SPI parameter RAM space. So we need to restore + * the SCC2 configuration if it is used by UART. + */ + +#if !defined(CONFIG_8xx_CONS_NONE) + serial_reinit_all (); +#endif + + if (res != 0) { + post_log ("SPI test failed\n"); + } + + return res; +} +#endif /* CONFIG_POST & CONFIG_SYS_POST_SPI */ diff --git a/arch/powerpc/cpu/mpc8xx/start.S b/arch/powerpc/cpu/mpc8xx/start.S new file mode 100644 index 0000000..7cf602f --- /dev/null +++ b/arch/powerpc/cpu/mpc8xx/start.S @@ -0,0 +1,684 @@ +/* + * Copyright (C) 1998 Dan Malek <dmalek@jlc.net> + * Copyright (C) 1999 Magnus Damm <kieraypc01.p.y.kie.era.ericsson.se> + * Copyright (C) 2000,2001,2002 Wolfgang Denk <wd@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 + */ + +/* U-Boot - Startup Code for PowerPC based Embedded Boards + * + * + * The processor starts at 0x00000100 and the code is executed + * from flash. The code is organized to be at an other address + * in memory, but as long we don't jump around before relocating, + * board_init lies at a quite high address and when the cpu has + * jumped there, everything is ok. + * This works because the cpu gives the FLASH (CS0) the whole + * address space at startup, and board_init lies as a echo of + * the flash somewhere up there in the memory map. + * + * board_init will change CS0 to be positioned at the correct + * address and (s)dram will be positioned at address 0 + */ +#include <config.h> +#include <mpc8xx.h> +#include <timestamp.h> +#include <version.h> + +#define CONFIG_8xx 1 /* needed for Linux kernel header files */ +#define _LINUX_CONFIG_H 1 /* avoid reading Linux autoconf.h file */ + +#include <ppc_asm.tmpl> +#include <ppc_defs.h> + +#include <asm/cache.h> +#include <asm/mmu.h> + +#ifndef CONFIG_IDENT_STRING +#define CONFIG_IDENT_STRING "" +#endif + +/* We don't want the MMU yet. +*/ +#undef MSR_KERNEL +#define MSR_KERNEL ( MSR_ME | MSR_RI ) /* Machine Check and Recoverable Interr. */ + +/* + * Set up GOT: Global Offset Table + * + * Use r12 to access the GOT + */ + START_GOT + GOT_ENTRY(_GOT2_TABLE_) + GOT_ENTRY(_FIXUP_TABLE_) + + GOT_ENTRY(_start) + GOT_ENTRY(_start_of_vectors) + GOT_ENTRY(_end_of_vectors) + GOT_ENTRY(transfer_to_handler) + + GOT_ENTRY(__init_end) + GOT_ENTRY(_end) + GOT_ENTRY(__bss_start) + END_GOT + +/* + * r3 - 1st arg to board_init(): IMMP pointer + * r4 - 2nd arg to board_init(): boot flag + */ + .text + .long 0x27051956 /* U-Boot Magic Number */ + .globl version_string +version_string: + .ascii U_BOOT_VERSION + .ascii " (", U_BOOT_DATE, " - ", U_BOOT_TIME, ")" + .ascii CONFIG_IDENT_STRING, "\0" + + . = EXC_OFF_SYS_RESET + .globl _start +_start: + lis r3, CONFIG_SYS_IMMR@h /* position IMMR */ + mtspr 638, r3 + li r21, BOOTFLAG_COLD /* Normal Power-On: Boot from FLASH */ + b boot_cold + + . = EXC_OFF_SYS_RESET + 0x10 + + .globl _start_warm +_start_warm: + li r21, BOOTFLAG_WARM /* Software reboot */ + b boot_warm + +boot_cold: +boot_warm: + + /* Initialize machine status; enable machine check interrupt */ + /*----------------------------------------------------------------------*/ + li r3, MSR_KERNEL /* Set ME, RI flags */ + mtmsr r3 + mtspr SRR1, r3 /* Make SRR1 match MSR */ + + mfspr r3, ICR /* clear Interrupt Cause Register */ + + /* Initialize debug port registers */ + /*----------------------------------------------------------------------*/ + xor r0, r0, r0 /* Clear R0 */ + mtspr LCTRL1, r0 /* Initialize debug port regs */ + mtspr LCTRL2, r0 + mtspr COUNTA, r0 + mtspr COUNTB, r0 + + /* Reset the caches */ + /*----------------------------------------------------------------------*/ + + mfspr r3, IC_CST /* Clear error bits */ + mfspr r3, DC_CST + + lis r3, IDC_UNALL@h /* Unlock all */ + mtspr IC_CST, r3 + mtspr DC_CST, r3 + + lis r3, IDC_INVALL@h /* Invalidate all */ + mtspr IC_CST, r3 + mtspr DC_CST, r3 + + lis r3, IDC_DISABLE@h /* Disable data cache */ + mtspr DC_CST, r3 + +#if !defined(CONFIG_SYS_DELAYED_ICACHE) + /* On IP860 and PCU E, + * we cannot enable IC yet + */ + lis r3, IDC_ENABLE@h /* Enable instruction cache */ +#endif + mtspr IC_CST, r3 + + /* invalidate all tlb's */ + /*----------------------------------------------------------------------*/ + + tlbia + isync + + /* + * Calculate absolute address in FLASH and jump there + *----------------------------------------------------------------------*/ + + lis r3, CONFIG_SYS_MONITOR_BASE@h + ori r3, r3, CONFIG_SYS_MONITOR_BASE@l + addi r3, r3, in_flash - _start + EXC_OFF_SYS_RESET + mtlr r3 + blr + +in_flash: + + /* initialize some SPRs that are hard to access from C */ + /*----------------------------------------------------------------------*/ + + lis r3, CONFIG_SYS_IMMR@h /* pass IMMR as arg1 to C routine */ + ori r1, r3, CONFIG_SYS_INIT_SP_OFFSET /* set up the stack in internal DPRAM */ + /* Note: R0 is still 0 here */ + stwu r0, -4(r1) /* clear final stack frame so that */ + stwu r0, -4(r1) /* stack backtraces terminate cleanly */ + + /* + * Disable serialized ifetch and show cycles + * (i.e. set processor to normal mode). + * This is also a silicon bug workaround, see errata + */ + + li r2, 0x0007 + mtspr ICTRL, r2 + + /* Set up debug mode entry */ + + lis r2, CONFIG_SYS_DER@h + ori r2, r2, CONFIG_SYS_DER@l + mtspr DER, r2 + + /* let the C-code set up the rest */ + /* */ + /* Be careful to keep code relocatable ! */ + /*----------------------------------------------------------------------*/ + + GET_GOT /* initialize GOT access */ + + /* r3: IMMR */ + bl cpu_init_f /* run low-level CPU init code (from Flash) */ + + mr r3, r21 + /* r3: BOOTFLAG */ + bl board_init_f /* run 1st part of board init code (from Flash) */ + + + .globl _start_of_vectors +_start_of_vectors: + +/* Machine check */ + STD_EXCEPTION(0x200, MachineCheck, MachineCheckException) + +/* Data Storage exception. "Never" generated on the 860. */ + STD_EXCEPTION(0x300, DataStorage, UnknownException) + +/* Instruction Storage exception. "Never" generated on the 860. */ + STD_EXCEPTION(0x400, InstStorage, UnknownException) + +/* External Interrupt exception. */ + STD_EXCEPTION(0x500, ExtInterrupt, external_interrupt) + +/* Alignment exception. */ + . = 0x600 +Alignment: + EXCEPTION_PROLOG(SRR0, SRR1) + mfspr r4,DAR + stw r4,_DAR(r21) + mfspr r5,DSISR + stw r5,_DSISR(r21) + addi r3,r1,STACK_FRAME_OVERHEAD + EXC_XFER_TEMPLATE(Alignment, AlignmentException, MSR_KERNEL, COPY_EE) + +/* Program check exception */ + . = 0x700 +ProgramCheck: + EXCEPTION_PROLOG(SRR0, SRR1) + addi r3,r1,STACK_FRAME_OVERHEAD + EXC_XFER_TEMPLATE(ProgramCheck, ProgramCheckException, + MSR_KERNEL, COPY_EE) + + /* No FPU on MPC8xx. This exception is not supposed to happen. + */ + STD_EXCEPTION(0x800, FPUnavailable, UnknownException) + + /* I guess we could implement decrementer, and may have + * to someday for timekeeping. + */ + STD_EXCEPTION(0x900, Decrementer, timer_interrupt) + STD_EXCEPTION(0xa00, Trap_0a, UnknownException) + STD_EXCEPTION(0xb00, Trap_0b, UnknownException) + STD_EXCEPTION(0xc00, SystemCall, UnknownException) + STD_EXCEPTION(0xd00, SingleStep, UnknownException) + + STD_EXCEPTION(0xe00, Trap_0e, UnknownException) + STD_EXCEPTION(0xf00, Trap_0f, UnknownException) + + /* On the MPC8xx, this is a software emulation interrupt. It occurs + * for all unimplemented and illegal instructions. + */ + STD_EXCEPTION(0x1000, SoftEmu, SoftEmuException) + + STD_EXCEPTION(0x1100, InstructionTLBMiss, UnknownException) + STD_EXCEPTION(0x1200, DataTLBMiss, UnknownException) + STD_EXCEPTION(0x1300, InstructionTLBError, UnknownException) + STD_EXCEPTION(0x1400, DataTLBError, UnknownException) + + STD_EXCEPTION(0x1500, Reserved5, UnknownException) + STD_EXCEPTION(0x1600, Reserved6, UnknownException) + STD_EXCEPTION(0x1700, Reserved7, UnknownException) + STD_EXCEPTION(0x1800, Reserved8, UnknownException) + STD_EXCEPTION(0x1900, Reserved9, UnknownException) + STD_EXCEPTION(0x1a00, ReservedA, UnknownException) + STD_EXCEPTION(0x1b00, ReservedB, UnknownException) + + STD_EXCEPTION(0x1c00, DataBreakpoint, UnknownException) + STD_EXCEPTION(0x1d00, InstructionBreakpoint, DebugException) + STD_EXCEPTION(0x1e00, PeripheralBreakpoint, UnknownException) + STD_EXCEPTION(0x1f00, DevPortBreakpoint, UnknownException) + + + .globl _end_of_vectors +_end_of_vectors: + + + . = 0x2000 + +/* + * This code finishes saving the registers to the exception frame + * and jumps to the appropriate handler for the exception. + * Register r21 is pointer into trap frame, r1 has new stack pointer. + */ + .globl transfer_to_handler +transfer_to_handler: + stw r22,_NIP(r21) + lis r22,MSR_POW@h + andc r23,r23,r22 + stw r23,_MSR(r21) + SAVE_GPR(7, r21) + SAVE_4GPRS(8, r21) + SAVE_8GPRS(12, r21) + SAVE_8GPRS(24, r21) + mflr r23 + andi. r24,r23,0x3f00 /* get vector offset */ + stw r24,TRAP(r21) + li r22,0 + stw r22,RESULT(r21) + mtspr SPRG2,r22 /* r1 is now kernel sp */ + lwz r24,0(r23) /* virtual address of handler */ + lwz r23,4(r23) /* where to go when done */ + mtspr SRR0,r24 + mtspr SRR1,r20 + mtlr r23 + SYNC + rfi /* jump to handler, enable MMU */ + +int_return: + mfmsr r28 /* Disable interrupts */ + li r4,0 + ori r4,r4,MSR_EE + andc r28,r28,r4 + SYNC /* Some chip revs need this... */ + mtmsr r28 + SYNC + lwz r2,_CTR(r1) + lwz r0,_LINK(r1) + mtctr r2 + mtlr r0 + lwz r2,_XER(r1) + lwz r0,_CCR(r1) + mtspr XER,r2 + mtcrf 0xFF,r0 + REST_10GPRS(3, r1) + REST_10GPRS(13, r1) + REST_8GPRS(23, r1) + REST_GPR(31, r1) + lwz r2,_NIP(r1) /* Restore environment */ + lwz r0,_MSR(r1) + mtspr SRR0,r2 + mtspr SRR1,r0 + lwz r0,GPR0(r1) + lwz r2,GPR2(r1) + lwz r1,GPR1(r1) + SYNC + rfi + +/* Cache functions. +*/ + .globl icache_enable +icache_enable: + SYNC + lis r3, IDC_INVALL@h + mtspr IC_CST, r3 + lis r3, IDC_ENABLE@h + mtspr IC_CST, r3 + blr + + .globl icache_disable +icache_disable: + SYNC + lis r3, IDC_DISABLE@h + mtspr IC_CST, r3 + blr + + .globl icache_status +icache_status: + mfspr r3, IC_CST + srwi r3, r3, 31 /* >>31 => select bit 0 */ + blr + + .globl dcache_enable +dcache_enable: +#if 0 + SYNC +#endif +#if 1 + lis r3, 0x0400 /* Set cache mode with MMU off */ + mtspr MD_CTR, r3 +#endif + + lis r3, IDC_INVALL@h + mtspr DC_CST, r3 +#if 0 + lis r3, DC_SFWT@h + mtspr DC_CST, r3 +#endif + lis r3, IDC_ENABLE@h + mtspr DC_CST, r3 + blr + + .globl dcache_disable +dcache_disable: + SYNC + lis r3, IDC_DISABLE@h + mtspr DC_CST, r3 + lis r3, IDC_INVALL@h + mtspr DC_CST, r3 + blr + + .globl dcache_status +dcache_status: + mfspr r3, DC_CST + srwi r3, r3, 31 /* >>31 => select bit 0 */ + blr + + .globl dc_read +dc_read: + mtspr DC_ADR, r3 + mfspr r3, DC_DAT + blr + +/* + * unsigned int get_immr (unsigned int mask) + * + * return (mask ? (IMMR & mask) : IMMR); + */ + .globl get_immr +get_immr: + mr r4,r3 /* save mask */ + mfspr r3, IMMR /* IMMR */ + cmpwi 0,r4,0 /* mask != 0 ? */ + beq 4f + and r3,r3,r4 /* IMMR & mask */ +4: + blr + + .globl get_pvr +get_pvr: + mfspr r3, PVR + blr + + + .globl wr_ic_cst +wr_ic_cst: + mtspr IC_CST, r3 + blr + + .globl rd_ic_cst +rd_ic_cst: + mfspr r3, IC_CST + blr + + .globl wr_ic_adr +wr_ic_adr: + mtspr IC_ADR, r3 + blr + + + .globl wr_dc_cst +wr_dc_cst: + mtspr DC_CST, r3 + blr + + .globl rd_dc_cst +rd_dc_cst: + mfspr r3, DC_CST + blr + + .globl wr_dc_adr +wr_dc_adr: + mtspr DC_ADR, r3 + blr + +/*------------------------------------------------------------------------------*/ + +/* + * void relocate_code (addr_sp, gd, addr_moni) + * + * This "function" does not return, instead it continues in RAM + * after relocating the monitor code. + * + * r3 = dest + * r4 = src + * r5 = length in bytes + * r6 = cachelinesize + */ + .globl relocate_code +relocate_code: + mr r1, r3 /* Set new stack pointer */ + mr r9, r4 /* Save copy of Global Data pointer */ + mr r10, r5 /* Save copy of Destination Address */ + + GET_GOT + mr r3, r5 /* Destination Address */ + lis r4, CONFIG_SYS_MONITOR_BASE@h /* Source Address */ + ori r4, r4, CONFIG_SYS_MONITOR_BASE@l + lwz r5, GOT(__init_end) + sub r5, r5, r4 + li r6, CONFIG_SYS_CACHELINE_SIZE /* Cache Line Size */ + + /* + * Fix GOT pointer: + * + * New GOT-PTR = (old GOT-PTR - CONFIG_SYS_MONITOR_BASE) + Destination Address + * + * Offset: + */ + sub r15, r10, r4 + + /* First our own GOT */ + add r12, r12, r15 + /* then the one used by the C code */ + add r30, r30, r15 + + /* + * Now relocate code + */ + + cmplw cr1,r3,r4 + addi r0,r5,3 + srwi. r0,r0,2 + beq cr1,4f /* In place copy is not necessary */ + beq 7f /* Protect against 0 count */ + mtctr r0 + bge cr1,2f + + la r8,-4(r4) + la r7,-4(r3) +1: lwzu r0,4(r8) + stwu r0,4(r7) + bdnz 1b + b 4f + +2: slwi r0,r0,2 + add r8,r4,r0 + add r7,r3,r0 +3: lwzu r0,-4(r8) + stwu r0,-4(r7) + bdnz 3b + +/* + * Now flush the cache: note that we must start from a cache aligned + * address. Otherwise we might miss one cache line. + */ +4: cmpwi r6,0 + add r5,r3,r5 + beq 7f /* Always flush prefetch queue in any case */ + subi r0,r6,1 + andc r3,r3,r0 + mr r4,r3 +5: dcbst 0,r4 + add r4,r4,r6 + cmplw r4,r5 + blt 5b + sync /* Wait for all dcbst to complete on bus */ + mr r4,r3 +6: icbi 0,r4 + add r4,r4,r6 + cmplw r4,r5 + blt 6b +7: sync /* Wait for all icbi to complete on bus */ + isync + +/* + * We are done. Do not return, instead branch to second part of board + * initialization, now running from RAM. + */ + + addi r0, r10, in_ram - _start + EXC_OFF_SYS_RESET + mtlr r0 + blr + +in_ram: + + /* + * Relocation Function, r12 point to got2+0x8000 + * + * Adjust got2 pointers, no need to check for 0, this code + * already puts a few entries in the table. + */ + li r0,__got2_entries@sectoff@l + la r3,GOT(_GOT2_TABLE_) + lwz r11,GOT(_GOT2_TABLE_) + mtctr r0 + sub r11,r3,r11 + addi r3,r3,-4 +1: lwzu r0,4(r3) + cmpwi r0,0 + beq- 2f + add r0,r0,r11 + stw r0,0(r3) +2: bdnz 1b + + /* + * Now adjust the fixups and the pointers to the fixups + * in case we need to move ourselves again. + */ + li r0,__fixup_entries@sectoff@l + lwz r3,GOT(_FIXUP_TABLE_) + cmpwi r0,0 + mtctr r0 + addi r3,r3,-4 + beq 4f +3: lwzu r4,4(r3) + lwzux r0,r4,r11 + add r0,r0,r11 + stw r10,0(r3) + stw r0,0(r4) + bdnz 3b +4: +clear_bss: + /* + * Now clear BSS segment + */ + lwz r3,GOT(__bss_start) + lwz r4,GOT(_end) + + cmplw 0, r3, r4 + beq 6f + + li r0, 0 +5: + stw r0, 0(r3) + addi r3, r3, 4 + cmplw 0, r3, r4 + bne 5b +6: + + mr r3, r9 /* Global Data pointer */ + mr r4, r10 /* Destination Address */ + bl board_init_r + + /* + * Copy exception vector code to low memory + * + * r3: dest_addr + * r7: source address, r8: end address, r9: target address + */ + .globl trap_init +trap_init: + mflr r4 /* save link register */ + GET_GOT + lwz r7, GOT(_start) + lwz r8, GOT(_end_of_vectors) + + li r9, 0x100 /* reset vector always at 0x100 */ + + cmplw 0, r7, r8 + bgelr /* return if r7>=r8 - just in case */ +1: + lwz r0, 0(r7) + stw r0, 0(r9) + addi r7, r7, 4 + addi r9, r9, 4 + cmplw 0, r7, r8 + bne 1b + + /* + * relocate `hdlr' and `int_return' entries + */ + li r7, .L_MachineCheck - _start + EXC_OFF_SYS_RESET + li r8, Alignment - _start + EXC_OFF_SYS_RESET +2: + bl trap_reloc + addi r7, r7, 0x100 /* next exception vector */ + cmplw 0, r7, r8 + blt 2b + + li r7, .L_Alignment - _start + EXC_OFF_SYS_RESET + bl trap_reloc + + li r7, .L_ProgramCheck - _start + EXC_OFF_SYS_RESET + bl trap_reloc + + li r7, .L_FPUnavailable - _start + EXC_OFF_SYS_RESET + li r8, SystemCall - _start + EXC_OFF_SYS_RESET +3: + bl trap_reloc + addi r7, r7, 0x100 /* next exception vector */ + cmplw 0, r7, r8 + blt 3b + + li r7, .L_SingleStep - _start + EXC_OFF_SYS_RESET + li r8, _end_of_vectors - _start + EXC_OFF_SYS_RESET +4: + bl trap_reloc + addi r7, r7, 0x100 /* next exception vector */ + cmplw 0, r7, r8 + blt 4b + + mtlr r4 /* restore link register */ + blr diff --git a/arch/powerpc/cpu/mpc8xx/traps.c b/arch/powerpc/cpu/mpc8xx/traps.c new file mode 100644 index 0000000..343dced --- /dev/null +++ b/arch/powerpc/cpu/mpc8xx/traps.c @@ -0,0 +1,241 @@ +/* + * linux/arch/powerpc/kernel/traps.c + * + * Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org) + * + * Modified by Cort Dougan (cort@cs.nmt.edu) + * and Paul Mackerras (paulus@cs.anu.edu.au) + * + * (C) Copyright 2000 + * Wolfgang Denk, DENX Software Engineering, wd@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 + */ + +/* + * This file handles the architecture-dependent parts of hardware exceptions + */ + +#include <common.h> +#include <command.h> +#include <kgdb.h> +#include <asm/processor.h> + +#if defined(CONFIG_CMD_BEDBUG) +extern void do_bedbug_breakpoint(struct pt_regs *); +#endif + +/* Returns 0 if exception not found and fixup otherwise. */ +extern unsigned long search_exception_table(unsigned long); + +/* THIS NEEDS CHANGING to use the board info structure. +*/ +#define END_OF_MEM 0x02000000 + +/* + * Trap & Exception support + */ + +void +print_backtrace(unsigned long *sp) +{ + int cnt = 0; + unsigned long i; + + printf("Call backtrace: "); + while (sp) { + if ((uint)sp > END_OF_MEM) + break; + + i = sp[1]; + if (cnt++ % 7 == 0) + printf("\n"); + printf("%08lX ", i); + if (cnt > 32) break; + sp = (unsigned long *)*sp; + } + printf("\n"); +} + +void show_regs(struct pt_regs * regs) +{ + int i; + + printf("NIP: %08lX XER: %08lX LR: %08lX REGS: %p TRAP: %04lx DAR: %08lX\n", + regs->nip, regs->xer, regs->link, regs, regs->trap, regs->dar); + printf("MSR: %08lx EE: %01x PR: %01x FP: %01x ME: %01x IR/DR: %01x%01x\n", + regs->msr, regs->msr&MSR_EE ? 1 : 0, regs->msr&MSR_PR ? 1 : 0, + regs->msr & MSR_FP ? 1 : 0,regs->msr&MSR_ME ? 1 : 0, + regs->msr&MSR_IR ? 1 : 0, + regs->msr&MSR_DR ? 1 : 0); + + printf("\n"); + for (i = 0; i < 32; i++) { + if ((i % 8) == 0) + { + printf("GPR%02d: ", i); + } + + printf("%08lX ", regs->gpr[i]); + if ((i % 8) == 7) + { + printf("\n"); + } + } +} + + +void +_exception(int signr, struct pt_regs *regs) +{ + show_regs(regs); + print_backtrace((unsigned long *)regs->gpr[1]); + panic("Exception in kernel pc %lx signal %d",regs->nip,signr); +} + +void +MachineCheckException(struct pt_regs *regs) +{ + unsigned long fixup; + + /* Probing PCI using config cycles cause this exception + * when a device is not present. Catch it and return to + * the PCI exception handler. + */ + if ((fixup = search_exception_table(regs->nip)) != 0) { + regs->nip = fixup; + return; + } + +#if defined(CONFIG_CMD_KGDB) + if (debugger_exception_handler && (*debugger_exception_handler)(regs)) + return; +#endif + + printf("Machine check in kernel mode.\n"); + printf("Caused by (from msr): "); + printf("regs %p ",regs); + switch( regs->msr & 0x000F0000) { + case (0x80000000>>12): + printf("Machine check signal - probably due to mm fault\n" + "with mmu off\n"); + break; + case (0x80000000>>13): + printf("Transfer error ack signal\n"); + break; + case (0x80000000>>14): + printf("Data parity signal\n"); + break; + case (0x80000000>>15): + printf("Address parity signal\n"); + break; + default: + printf("Unknown values in msr\n"); + } + show_regs(regs); + print_backtrace((unsigned long *)regs->gpr[1]); + panic("machine check"); +} + +void +AlignmentException(struct pt_regs *regs) +{ +#if defined(CONFIG_CMD_KGDB) + if (debugger_exception_handler && (*debugger_exception_handler)(regs)) + return; +#endif + show_regs(regs); + print_backtrace((unsigned long *)regs->gpr[1]); + panic("Alignment Exception"); +} + +void +ProgramCheckException(struct pt_regs *regs) +{ +#if defined(CONFIG_CMD_KGDB) + if (debugger_exception_handler && (*debugger_exception_handler)(regs)) + return; +#endif + show_regs(regs); + print_backtrace((unsigned long *)regs->gpr[1]); + panic("Program Check Exception"); +} + +void +SoftEmuException(struct pt_regs *regs) +{ +#if defined(CONFIG_CMD_KGDB) + if (debugger_exception_handler && (*debugger_exception_handler)(regs)) + return; +#endif + show_regs(regs); + print_backtrace((unsigned long *)regs->gpr[1]); + panic("Software Emulation Exception"); +} + + +void +UnknownException(struct pt_regs *regs) +{ +#if defined(CONFIG_CMD_KGDB) + if (debugger_exception_handler && (*debugger_exception_handler)(regs)) + return; +#endif + printf("Bad trap at PC: %lx, SR: %lx, vector=%lx\n", + regs->nip, regs->msr, regs->trap); + _exception(0, regs); +} + +void +DebugException(struct pt_regs *regs) +{ + printf("Debugger trap at @ %lx\n", regs->nip ); + show_regs(regs); +#if defined(CONFIG_CMD_BEDBUG) + do_bedbug_breakpoint( regs ); +#endif +} + +/* Probe an address by reading. If not present, return -1, otherwise + * return 0. + */ +int +addr_probe(uint *addr) +{ +#if 0 + int retval; + + __asm__ __volatile__( \ + "1: lwz %0,0(%1)\n" \ + " eieio\n" \ + " li %0,0\n" \ + "2:\n" \ + ".section .fixup,\"ax\"\n" \ + "3: li %0,-1\n" \ + " b 2b\n" \ + ".section __ex_table,\"a\"\n" \ + " .align 2\n" \ + " .long 1b,3b\n" \ + ".text" \ + : "=r" (retval) : "r"(addr)); + + return (retval); +#endif + return 0; +} diff --git a/arch/powerpc/cpu/mpc8xx/upatch.c b/arch/powerpc/cpu/mpc8xx/upatch.c new file mode 100644 index 0000000..a8cb735 --- /dev/null +++ b/arch/powerpc/cpu/mpc8xx/upatch.c @@ -0,0 +1,194 @@ +#include <common.h> +#include <commproc.h> + +#if defined(CONFIG_SYS_I2C_UCODE_PATCH) || defined(CONFIG_SYS_SPI_UCODE_PATCH) || \ + defined(CONFIG_SYS_SMC_UCODE_PATCH) + +static void UcodeCopy (volatile cpm8xx_t *cpm); + +void cpm_load_patch (volatile immap_t *immr) +{ + immr->im_cpm.cp_rccr &= ~0x0003; /* Disable microcode program area */ + + UcodeCopy ((cpm8xx_t *)&immr->im_cpm); /* Copy ucode patch to DPRAM */ +#ifdef CONFIG_SYS_SPI_UCODE_PATCH + { + volatile spi_t *spi = (spi_t *) & immr->im_cpm.cp_dparam[PROFF_SPI]; + /* Activate the microcode per the instructions in the microcode manual */ + /* NOTE: We're only relocating the SPI parameters (not I2C). */ + immr->im_cpm.cp_cpmcr1 = 0x802a; /* Write Trap register 1 value */ + immr->im_cpm.cp_cpmcr2 = 0x8028; /* Write Trap register 2 value */ + spi->spi_rpbase = CONFIG_SYS_SPI_DPMEM_OFFSET; /* Where to relocte SPI params */ + } +#endif + +#ifdef CONFIG_SYS_I2C_UCODE_PATCH + { + volatile iic_t *iip = (iic_t *) & immr->im_cpm.cp_dparam[PROFF_IIC]; + /* Activate the microcode per the instructions in the microcode manual */ + /* NOTE: We're only relocating the I2C parameters (not SPI). */ + immr->im_cpm.cp_cpmcr3 = 0x802e; /* Write Trap register 3 value */ + immr->im_cpm.cp_cpmcr4 = 0x802c; /* Write Trap register 4 value */ + iip->iic_rpbase = CONFIG_SYS_I2C_DPMEM_OFFSET; /* Where to relocte I2C params */ + } +#endif + +#ifdef CONFIG_SYS_SMC_UCODE_PATCH + { + volatile smc_uart_t *up = (smc_uart_t *) & immr->im_cpm.cp_dparam[PROFF_SMC1]; + /* Activate the microcode per the instructions in the microcode manual */ + /* NOTE: We're only relocating the SMC parameters. */ + immr->im_cpm.cp_cpmcr1 = 0x8080; /* Write Trap register 1 value */ + immr->im_cpm.cp_cpmcr2 = 0x8088; /* Write Trap register 2 value */ + up->smc_rpbase = CONFIG_SYS_SMC_DPMEM_OFFSET; /* Where to relocte SMC params */ + } +#endif + + /* + * Enable DPRAM microcode to execute from the first 512 bytes + * and a 256 byte extension of DPRAM. + */ +#ifdef CONFIG_SYS_SMC_UCODE_PATCH + immr->im_cpm.cp_rccr |= 0x0002; +#else + immr->im_cpm.cp_rccr |= 0x0001; +#endif +} + +#if defined(CONFIG_SYS_I2C_UCODE_PATCH) || defined(CONFIG_SYS_SPI_UCODE_PATCh) +static ulong patch_2000[] = { + 0x7FFFEFD9, 0x3FFD0000, 0x7FFB49F7, 0x7FF90000, + 0x5FEFADF7, 0x5F88ADF7, 0x5FEFAFF7, 0x5F88AFF7, + 0x3A9CFBC8, 0x77CAE1BB, 0xF4DE7FAD, 0xABAE9330, + 0x4E08FDCF, 0x6E0FAFF8, 0x7CCF76CF, 0xFDAFF9CF, + 0xABF88DC8, 0xAB5879F7, 0xB0927383, 0xDFD079F7, + 0xB090E6BB, 0xE5BBE74F, 0xB3FA6F0F, 0x6FFB76CE, + 0xEE0CF9CF, 0x2BFBEFEF, 0xCFEEF9CF, 0x76CEAD23, + 0x90B3DF99, 0x7FDDD0C1, 0x4BF847FD, 0x7CCF76CE, + 0xCFEF77CA, 0x7EAF7FAD, 0x7DFDF0B7, 0xEF7A7FCA, + 0x77CAFBC8, 0x6079E722, 0xFBC85FFF, 0xDFFF5FB3, + 0xFFFBFBC8, 0xF3C894A5, 0xE7C9EDF9, 0x7F9A7FAD, + 0x5F36AFE8, 0x5F5BFFDF, 0xDF95CB9E, 0xAF7D5FC3, + 0xAFED8C1B, 0x5FC3AFDD, 0x5FC5DF99, 0x7EFDB0B3, + 0x5FB3FFFE, 0xABAE5FB3, 0xFFFE5FD0, 0x600BE6BB, + 0x600B5FD0, 0xDFC827FB, 0xEFDF5FCA, 0xCFDE3A9C, + 0xE7C9EDF9, 0xF3C87F9E, 0x54CA7FED, 0x2D3A3637, + 0x756F7E9A, 0xF1CE37EF, 0x2E677FEE, 0x10EBADF8, + 0xEFDECFEA, 0xE52F7D9F, 0xE12BF1CE, 0x5F647E9A, + 0x4DF8CFEA, 0x5F717D9B, 0xEFEECFEA, 0x5F73E522, + 0xEFDE5F73, 0xCFDA0B61, 0x7385DF61, 0xE7C9EDF9, + 0x7E9A30D5, 0x1458BFFF, 0xF3C85FFF, 0xDFFFA7F8, + 0x5F5BBFFE, 0x7F7D10D0, 0x144D5F33, 0xBFFFAF78, + 0x5F5BBFFD, 0xA7F85F33, 0xBFFE77FD, 0x30BD4E08, + 0xFDCFE5FF, 0x6E0FAFF8, 0x7EEF7E9F, 0xFDEFF1CF, + 0x5F17ABF8, 0x0D5B5F5B, 0xFFEF79F7, 0x309EAFDD, + 0x5F3147F8, 0x5F31AFED, 0x7FDD50AF, 0x497847FD, + 0x7F9E7FED, 0x7DFD70A9, 0xEF7E7ECE, 0x6BA07F9E, + 0x2D227EFD, 0x30DB5F5B, 0xFFFD5F5B, 0xFFEF5F5B, + 0xFFDF0C9C, 0xAFED0A9A, 0xAFDD0C37, 0x5F37AFBD, + 0x7FBDB081, 0x5F8147F8, +}; + +static ulong patch_2F00[] = { + 0x3E303430, 0x34343737, 0xABBF9B99, 0x4B4FBDBD, + 0x59949334, 0x9FFF37FB, 0x9B177DD9, 0x936956BB, + 0xFBDD697B, 0xDD2FD113, 0x1DB9F7BB, 0x36313963, + 0x79373369, 0x3193137F, 0x7331737A, 0xF7BB9B99, + 0x9BB19795, 0x77FDFD3D, 0x573B773F, 0x737933F7, + 0xB991D115, 0x31699315, 0x31531694, 0xBF4FBDBD, + 0x35931497, 0x35376956, 0xBD697B9D, 0x96931313, + 0x19797937, 0x69350000, +}; +#else + +static ulong patch_2000[] = { + 0x3fff0000, 0x3ffd0000, 0x3ffb0000, 0x3ff90000, + 0x5fefeff8, 0x5f91eff8, 0x3ff30000, 0x3ff10000, + 0x3a11e710, 0xedf0ccb9, 0xf318ed66, 0x7f0e5fe2, + 0x7fedbb38, 0x3afe7468, 0x7fedf4d8, 0x8ffbb92d, + 0xb83b77fd, 0xb0bb5eb9, 0xdfda7fed, 0x90bde74d, + 0x6f0dcbd3, 0xe7decfed, 0xcb50cfed, 0xcfeddf6d, + 0x914d4f74, 0x5eaedfcb, 0x9ee0e7df, 0xefbb6ffb, + 0xe7ef7f0e, 0x9ee57fed, 0xebb7effa, 0xeb30affb, + 0x7fea90b3, 0x7e0cf09f, 0xbffff318, 0x5fffdfff, + 0xac35efea, 0x7fce1fc1, 0xe2ff5fbd, 0xaffbe2ff, + 0x5fbfaffb, 0xf9a87d0f, 0xaef8770f, 0x7d0fb0a2, + 0xeffbbfff, 0xcfef5fba, 0x7d0fbfff, 0x5fba4cf8, + 0x7fddd09b, 0x49f847fd, 0x7efdf097, 0x7fedfffd, + 0x7dfdf093, 0xef7e7e1e, 0x5fba7f0e, 0x3a11e710, + 0xedf0cc87, 0xfb18ad0a, 0x1f85bbb8, 0x74283b7e, + 0x7375e4bb, 0x2ab64fb8, 0x5c7de4bb, 0x32fdffbf, + 0x5f0843f8, 0x7ce3e1bb, 0xe74f7ded, 0x6f0f4fe8, + 0xc7ba32be, 0x73f2efeb, 0x600b4f78, 0xe5bb760b, + 0x5388aef8, 0x4ef80b6a, 0xcfef9ee5, 0xabf8751f, + 0xefef5b88, 0x741f4fe8, 0x751e760d, 0x7fdb70dd, + 0x741cafce, 0xefcc7fce, 0x751e7088, 0x741ce7bb, + 0x334ecfed, 0xafdbefeb, 0xe5bb760b, 0x53ceaef8, + 0xafe8e7eb, 0x4bf8771e, 0x7e007fed, 0x4fcbe2cc, + 0x7fbc3085, 0x7b0f7a0f, 0x34b177fd, 0xb0e75e93, + 0xdf313e3b, 0xaf78741f, 0x741f30cc, 0xcfef5f08, + 0x741f3e88, 0xafb8771e, 0x5f437fed, 0x0bafe2cc, + 0x741ccfec, 0xe5ca53a9, 0x6fcb4f74, 0x5e89df27, + 0x2a923d14, 0x4b8fdf0c, 0x751f741c, 0x6c1eeffa, + 0xefea7fce, 0x6ffc309a, 0xefec3fca, 0x308fdf0a, + 0xadf85e7a, 0xaf7daefd, 0x5e7adf0a, 0x5e7aafdd, + 0x761f1088, 0x1e7c7efd, 0x3089fffe, 0x4908fb18, + 0x5fffdfff, 0xafbbf0f7, 0x4ef85f43, 0xadf81489, + 0x7a0f7089, 0xcfef5089, 0x7a0fdf0c, 0x5e7cafed, + 0xbc6e780f, 0xefef780f, 0xefef790f, 0xa7f85eeb, + 0xffef790f, 0xefef790f, 0x1489df0a, 0x5e7aadfd, + 0x5f09fffb, 0xe79aded9, 0xeff96079, 0x607ae79a, + 0xded8eff9, 0x60795edb, 0x607acfef, 0xefefefdf, + 0xefbfef7f, 0xeeffedff, 0xebffe7ff, 0xafefafdf, + 0xafbfaf7f, 0xaeffadff, 0xabffa7ff, 0x6fef6fdf, + 0x6fbf6f7f, 0x6eff6dff, 0x6bff67ff, 0x2fef2fdf, + 0x2fbf2f7f, 0x2eff2dff, 0x2bff27ff, 0x4e08fd1f, + 0xe5ff6e0f, 0xaff87eef, 0x7e0ffdef, 0xf11f6079, + 0xabf8f51e, 0x7e0af11c, 0x37cfae16, 0x7fec909a, + 0xadf8efdc, 0xcfeae52f, 0x7d0fe12b, 0xf11c6079, + 0x7e0a4df8, 0xcfea5ea0, 0x7d0befec, 0xcfea5ea2, + 0xe522efdc, 0x5ea2cfda, 0x4e08fd1f, 0x6e0faff8, + 0x7c1f761f, 0xfdeff91f, 0x6079abf8, 0x761cee00, + 0xf91f2bfb, 0xefefcfec, 0xf91f6079, 0x761c27fb, + 0xefdf5e83, 0xcfdc7fdd, 0x50f84bf8, 0x47fd7c1f, + 0x761ccfcf, 0x7eef7fed, 0x7dfd70ef, 0xef7e7f1e, + 0x771efb18, 0x6079e722, 0xe6bbe5bb, 0x2e66e5bb, + 0x600b2ee1, 0xe2bbe2bb, 0xe2bbe2bb, 0x2f5ee2bb, + 0xe2bb2ff9, 0x6079e2bb, +}; + +static ulong patch_2F00[] = { + 0x30303030, 0x3e3e3030, 0xaf79b9b3, 0xbaa3b979, + 0x9693369f, 0x79f79777, 0x97333fff, 0xfb3b9e9f, + 0x79b91d11, 0x9e13f3ff, 0x3f9b6bd9, 0xe173d136, + 0x695669d1, 0x697b3daf, 0x79b93a3a, 0x3f979f91, + 0x379ff976, 0xf99777fd, 0x9779737d, 0xe9d6bbf9, + 0xbfffd9df, 0x97f7fd97, 0x6f7b9bff, 0xf9bd9683, + 0x397db973, 0xd97b3b9f, 0xd7f9f733, 0x9993bb9e, + 0xe1f9ef93, 0x73773337, 0xb936917d, 0x11f87379, + 0xb979d336, 0x8b7ded73, 0x1b7d9337, 0x31f3f22f, + 0x3f2327ee, 0xeeeeeeee, 0xeeeeeeee, 0xeeeeeeee, + 0xeeeeee4b, 0xf4fbdbd2, 0x58bb1878, 0x577fdfd2, + 0xd573b773, 0xf7374b4f, 0xbdbd25b8, 0xb177d2d1, + 0x7376856b, 0xbfdd687b, 0xdd2fff8f, 0x78ffff8f, + 0xf22f0000, +}; +#endif + +static void UcodeCopy (volatile cpm8xx_t *cpm) +{ + vu_long *p; + int i; + + p = (vu_long *)&(cpm->cp_dpmem[0x0000]); + for (i=0; i < sizeof(patch_2000)/4; ++i) { + p[i] = patch_2000[i]; + } + + p = (vu_long *)&(cpm->cp_dpmem[0x0F00]); + for (i=0; i < sizeof(patch_2F00)/4; ++i) { + p[i] = patch_2F00[i]; + } +} + +#endif /* CONFIG_SYS_I2C_UCODE_PATCH, CONFIG_SYS_SPI_UCODE_PATCH */ diff --git a/arch/powerpc/cpu/mpc8xx/video.c b/arch/powerpc/cpu/mpc8xx/video.c new file mode 100644 index 0000000..c79c499 --- /dev/null +++ b/arch/powerpc/cpu/mpc8xx/video.c @@ -0,0 +1,1331 @@ +/* + * (C) Copyright 2000 + * Paolo Scaffardi, AIRVENT SAM s.p.a - RIMINI(ITALY), arsenio@tin.it + * (C) Copyright 2002 + * Wolfgang Denk, wd@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 + */ + +/* #define DEBUG */ + +/************************************************************************/ +/* ** HEADER FILES */ +/************************************************************************/ + +#include <stdarg.h> +#include <common.h> +#include <config.h> +#include <version.h> +#include <timestamp.h> +#include <i2c.h> +#include <linux/types.h> +#include <stdio_dev.h> + +#ifdef CONFIG_VIDEO + +DECLARE_GLOBAL_DATA_PTR; + +/************************************************************************/ +/* ** DEBUG SETTINGS */ +/************************************************************************/ + +#if 0 +#define VIDEO_DEBUG_COLORBARS /* Force colorbars output */ +#endif + +/************************************************************************/ +/* ** VIDEO MODE SETTINGS */ +/************************************************************************/ + +#if 0 +#define VIDEO_MODE_EXTENDED /* Allow screen size bigger than visible area */ +#define VIDEO_MODE_NTSC +#endif + +#define VIDEO_MODE_PAL + +#if 0 +#define VIDEO_BLINK /* This enables cursor blinking (under construction) */ +#endif + +#define VIDEO_INFO /* Show U-Boot information */ +#define VIDEO_INFO_X VIDEO_LOGO_WIDTH+8 +#define VIDEO_INFO_Y 16 + +/************************************************************************/ +/* ** VIDEO ENCODER CONSTANTS */ +/************************************************************************/ + +#ifdef CONFIG_VIDEO_ENCODER_AD7176 + +#include <video_ad7176.h> /* Sets encoder data, mode, and visible and active area */ + +#define VIDEO_I2C 1 +#define VIDEO_I2C_ADDR CONFIG_VIDEO_ENCODER_AD7176_ADDR +#endif + +#ifdef CONFIG_VIDEO_ENCODER_AD7177 + +#include <video_ad7177.h> /* Sets encoder data, mode, and visible and active area */ + +#define VIDEO_I2C 1 +#define VIDEO_I2C_ADDR CONFIG_VIDEO_ENCODER_AD7177_ADDR +#endif + +#ifdef CONFIG_VIDEO_ENCODER_AD7179 + +#include <video_ad7179.h> /* Sets encoder data, mode, and visible and active area */ + +#define VIDEO_I2C 1 +#define VIDEO_I2C_ADDR CONFIG_VIDEO_ENCODER_AD7179_ADDR +#endif + +/************************************************************************/ +/* ** VIDEO MODE CONSTANTS */ +/************************************************************************/ + +#ifdef VIDEO_MODE_EXTENDED +#define VIDEO_COLS VIDEO_ACTIVE_COLS +#define VIDEO_ROWS VIDEO_ACTIVE_ROWS +#else +#define VIDEO_COLS VIDEO_VISIBLE_COLS +#define VIDEO_ROWS VIDEO_VISIBLE_ROWS +#endif + +#define VIDEO_PIXEL_SIZE (VIDEO_MODE_BPP/8) +#define VIDEO_SIZE (VIDEO_ROWS*VIDEO_COLS*VIDEO_PIXEL_SIZE) /* Total size of buffer */ +#define VIDEO_PIX_BLOCKS (VIDEO_SIZE >> 2) /* Number of ints */ +#define VIDEO_LINE_LEN (VIDEO_COLS*VIDEO_PIXEL_SIZE) /* Number of bytes per line */ +#define VIDEO_BURST_LEN (VIDEO_COLS/8) + +#ifdef VIDEO_MODE_YUYV +#define VIDEO_BG_COL 0x80D880D8 /* Background color in YUYV format */ +#else +#define VIDEO_BG_COL 0xF8F8F8F8 /* Background color in RGB format */ +#endif + +/************************************************************************/ +/* ** FONT AND LOGO DATA */ +/************************************************************************/ + +#include <video_font.h> /* Get font data, width and height */ + +#ifdef CONFIG_VIDEO_LOGO +#include <video_logo.h> /* Get logo data, width and height */ + +#define VIDEO_LOGO_WIDTH DEF_U_BOOT_LOGO_WIDTH +#define VIDEO_LOGO_HEIGHT DEF_U_BOOT_LOGO_HEIGHT +#define VIDEO_LOGO_ADDR &u_boot_logo +#endif + +/************************************************************************/ +/* ** VIDEO CONTROLLER CONSTANTS */ +/************************************************************************/ + +/* VCCR - VIDEO CONTROLLER CONFIGURATION REGISTER */ + +#define VIDEO_VCCR_VON 0 /* Video controller ON */ +#define VIDEO_VCCR_CSRC 1 /* Clock source */ +#define VIDEO_VCCR_PDF 13 /* Pixel display format */ +#define VIDEO_VCCR_IEN 11 /* Interrupt enable */ + +/* VSR - VIDEO STATUS REGISTER */ + +#define VIDEO_VSR_CAS 6 /* Active set */ +#define VIDEO_VSR_EOF 0 /* End of frame */ + +/* VCMR - VIDEO COMMAND REGISTER */ + +#define VIDEO_VCMR_BD 0 /* Blank display */ +#define VIDEO_VCMR_ASEL 1 /* Active set selection */ + +/* VBCB - VIDEO BACKGROUND COLOR BUFFER REGISTER */ + +#define VIDEO_BCSR4_RESET_BIT 21 /* BCSR4 - Extern video encoder reset */ +#define VIDEO_BCSR4_EXTCLK_BIT 22 /* BCSR4 - Extern clock enable */ +#define VIDEO_BCSR4_VIDLED_BIT 23 /* BCSR4 - Video led disable */ + +/************************************************************************/ +/* ** CONSOLE CONSTANTS */ +/************************************************************************/ + +#ifdef CONFIG_VIDEO_LOGO +#define CONSOLE_ROWS ((VIDEO_ROWS - VIDEO_LOGO_HEIGHT) / VIDEO_FONT_HEIGHT) +#define VIDEO_LOGO_SKIP (VIDEO_COLS - VIDEO_LOGO_WIDTH) +#else +#define CONSOLE_ROWS (VIDEO_ROWS / VIDEO_FONT_HEIGHT) +#endif + +#define CONSOLE_COLS (VIDEO_COLS / VIDEO_FONT_WIDTH) +#define CONSOLE_ROW_SIZE (VIDEO_FONT_HEIGHT * VIDEO_LINE_LEN) +#define CONSOLE_ROW_FIRST (video_console_address) +#define CONSOLE_ROW_SECOND (video_console_address + CONSOLE_ROW_SIZE) +#define CONSOLE_ROW_LAST (video_console_address + CONSOLE_SIZE - CONSOLE_ROW_SIZE) +#define CONSOLE_SIZE (CONSOLE_ROW_SIZE * CONSOLE_ROWS) +#define CONSOLE_SCROLL_SIZE (CONSOLE_SIZE - CONSOLE_ROW_SIZE) + +/* + * Simple color definitions + */ +#define CONSOLE_COLOR_BLACK 0 +#define CONSOLE_COLOR_RED 1 +#define CONSOLE_COLOR_GREEN 2 +#define CONSOLE_COLOR_YELLOW 3 +#define CONSOLE_COLOR_BLUE 4 +#define CONSOLE_COLOR_MAGENTA 5 +#define CONSOLE_COLOR_CYAN 6 +#define CONSOLE_COLOR_GREY 13 +#define CONSOLE_COLOR_GREY2 14 +#define CONSOLE_COLOR_WHITE 15 /* Must remain last / highest */ + +/************************************************************************/ +/* ** BITOPS MACROS */ +/************************************************************************/ + +#define HISHORT(i) ((i >> 16)&0xffff) +#define LOSHORT(i) (i & 0xffff) +#define HICHAR(s) ((i >> 8)&0xff) +#define LOCHAR(s) (i & 0xff) +#define HI(c) ((c >> 4)&0xf) +#define LO(c) (c & 0xf) +#define SWAPINT(i) (HISHORT(i) | (LOSHORT(i) << 16)) +#define SWAPSHORT(s) (HICHAR(s) | (LOCHAR(s) << 8)) +#define SWAPCHAR(c) (HI(c) | (LO(c) << 4)) +#define BITMASK(b) (1 << (b)) +#define GETBIT(v,b) (((v) & BITMASK(b)) > 0) +#define SETBIT(v,b,d) (v = (((d)>0) ? (v) | BITMASK(b): (v) & ~BITMASK(b))) + +/************************************************************************/ +/* ** STRUCTURES */ +/************************************************************************/ + +typedef struct { + unsigned char V, Y1, U, Y2; +} tYUYV; + +/* This structure is based on the Video Ram in the MPC823. */ +typedef struct VRAM { + unsigned hx:2, /* Horizontal sync */ + vx:2, /* Vertical sync */ + fx:2, /* Frame */ + bx:2, /* Blank */ + res1:6, /* Reserved */ + vds:2, /* Video Data Select */ + inter:1, /* Interrupt */ + res2:2, /* Reserved */ + lcyc:11, /* Loop/video cycles */ + lp:1, /* Loop start/end */ + lst:1; /* Last entry */ +} VRAM; + +/************************************************************************/ +/* ** VARIABLES */ +/************************************************************************/ + +static int + video_panning_range_x = 0, /* Video mode invisible pixels x range */ + video_panning_range_y = 0, /* Video mode invisible pixels y range */ + video_panning_value_x = 0, /* Video mode x panning value (absolute) */ + video_panning_value_y = 0, /* Video mode y panning value (absolute) */ + video_panning_factor_x = 0, /* Video mode x panning value (-127 +127) */ + video_panning_factor_y = 0, /* Video mode y panning value (-127 +127) */ + console_col = 0, /* Cursor col */ + console_row = 0, /* Cursor row */ + video_palette[16]; /* Our palette */ + +static const int video_font_draw_table[] = + { 0x00000000, 0x0000ffff, 0xffff0000, 0xffffffff }; + +static char + video_color_fg = 0, /* Current fg color index (0-15) */ + video_color_bg = 0, /* Current bg color index (0-15) */ + video_enable = 0; /* Video has been initialized? */ + +static void + *video_fb_address, /* Frame buffer address */ + *video_console_address; /* Console frame buffer start address */ + +/************************************************************************/ +/* ** MEMORY FUNCTIONS (32bit) */ +/************************************************************************/ + +static void memsetl (int *p, int c, int v) +{ + while (c--) + *(p++) = v; +} + +static void memcpyl (int *d, int *s, int c) +{ + while (c--) + *(d++) = *(s++); +} + +/************************************************************************/ +/* ** VIDEO DRAWING AND COLOR FUNCTIONS */ +/************************************************************************/ + +static int video_maprgb (int r, int g, int b) +{ +#ifdef VIDEO_MODE_YUYV + unsigned int pR, pG, pB; + tYUYV YUYV; + unsigned int *ret = (unsigned int *) &YUYV; + + /* Transform (0-255) components to (0-100) */ + + pR = r * 100 / 255; + pG = g * 100 / 255; + pB = b * 100 / 255; + + /* Calculate YUV values (0-255) from RGB beetween 0-100 */ + + YUYV.Y1 = YUYV.Y2 = 209 * (pR + pG + pB) / 300 + 16; + YUYV.U = pR - (pG * 3 / 4) - (pB / 4) + 128; + YUYV.V = pB - (pR / 4) - (pG * 3 / 4) + 128; + return *ret; +#endif +#ifdef VIDEO_MODE_RGB + return ((r >> 3) << 11) | ((g > 2) << 6) | (b >> 3); +#endif +} + +static void video_setpalette (int color, int r, int g, int b) +{ + color &= 0xf; + + video_palette[color] = video_maprgb (r, g, b); + + /* Swap values if our panning offset is odd */ + if (video_panning_value_x & 1) + video_palette[color] = SWAPINT (video_palette[color]); +} + +static void video_fill (int color) +{ + memsetl (video_fb_address, VIDEO_PIX_BLOCKS, color); +} + +static void video_setfgcolor (int i) +{ + video_color_fg = i & 0xf; +} + +static void video_setbgcolor (int i) +{ + video_color_bg = i & 0xf; +} + +static int video_pickcolor (int i) +{ + return video_palette[i & 0xf]; +} + +/* Absolute console plotting functions */ + +#ifdef VIDEO_BLINK +static void video_revchar (int xx, int yy) +{ + int rows; + u8 *dest; + + dest = video_fb_address + yy * VIDEO_LINE_LEN + xx * 2; + + for (rows = VIDEO_FONT_HEIGHT; rows--; dest += VIDEO_LINE_LEN) { + switch (VIDEO_FONT_WIDTH) { + case 16: + ((u32 *) dest)[6] ^= 0xffffffff; + ((u32 *) dest)[7] ^= 0xffffffff; + /* FALL THROUGH */ + case 12: + ((u32 *) dest)[4] ^= 0xffffffff; + ((u32 *) dest)[5] ^= 0xffffffff; + /* FALL THROUGH */ + case 8: + ((u32 *) dest)[2] ^= 0xffffffff; + ((u32 *) dest)[3] ^= 0xffffffff; + /* FALL THROUGH */ + case 4: + ((u32 *) dest)[0] ^= 0xffffffff; + ((u32 *) dest)[1] ^= 0xffffffff; + } + } +} +#endif + +static void video_drawchars (int xx, int yy, unsigned char *s, int count) +{ + u8 *cdat, *dest, *dest0; + int rows, offset, c; + u32 eorx, fgx, bgx; + + offset = yy * VIDEO_LINE_LEN + xx * 2; + dest0 = video_fb_address + offset; + + fgx = video_pickcolor (video_color_fg); + bgx = video_pickcolor (video_color_bg); + + if (xx & 1) { + fgx = SWAPINT (fgx); + bgx = SWAPINT (bgx); + } + + eorx = fgx ^ bgx; + + switch (VIDEO_FONT_WIDTH) { + case 4: + case 8: + while (count--) { + c = *s; + cdat = video_fontdata + c * VIDEO_FONT_HEIGHT; + for (rows = VIDEO_FONT_HEIGHT, dest = dest0; + rows--; + dest += VIDEO_LINE_LEN) { + u8 bits = *cdat++; + + ((u32 *) dest)[0] = + (video_font_draw_table[bits >> 6] & eorx) ^ bgx; + ((u32 *) dest)[1] = + (video_font_draw_table[bits >> 4 & 3] & eorx) ^ bgx; + if (VIDEO_FONT_WIDTH == 8) { + ((u32 *) dest)[2] = + (video_font_draw_table[bits >> 2 & 3] & eorx) ^ bgx; + ((u32 *) dest)[3] = + (video_font_draw_table[bits & 3] & eorx) ^ bgx; + } + } + dest0 += VIDEO_FONT_WIDTH * 2; + s++; + } + break; + case 12: + case 16: + while (count--) { + cdat = video_fontdata + (*s) * (VIDEO_FONT_HEIGHT << 1); + for (rows = VIDEO_FONT_HEIGHT, dest = dest0; rows--; + dest += VIDEO_LINE_LEN) { + u8 bits = *cdat++; + + ((u32 *) dest)[0] = + (video_font_draw_table[bits >> 6] & eorx) ^ bgx; + ((u32 *) dest)[1] = + (video_font_draw_table[bits >> 4 & 3] & eorx) ^ bgx; + ((u32 *) dest)[2] = + (video_font_draw_table[bits >> 2 & 3] & eorx) ^ bgx; + ((u32 *) dest)[3] = + (video_font_draw_table[bits & 3] & eorx) ^ bgx; + bits = *cdat++; + ((u32 *) dest)[4] = + (video_font_draw_table[bits >> 6] & eorx) ^ bgx; + ((u32 *) dest)[5] = + (video_font_draw_table[bits >> 4 & 3] & eorx) ^ bgx; + if (VIDEO_FONT_WIDTH == 16) { + ((u32 *) dest)[6] = + (video_font_draw_table[bits >> 2 & 3] & eorx) ^ bgx; + ((u32 *) dest)[7] = + (video_font_draw_table[bits & 3] & eorx) ^ bgx; + } + } + s++; + dest0 += VIDEO_FONT_WIDTH * 2; + } + break; + } +} + +static inline void video_drawstring (int xx, int yy, char *s) +{ + video_drawchars (xx, yy, (unsigned char *)s, strlen (s)); +} + +/* Relative to console plotting functions */ + +static void video_putchars (int xx, int yy, unsigned char *s, int count) +{ +#ifdef CONFIG_VIDEO_LOGO + video_drawchars (xx, yy + VIDEO_LOGO_HEIGHT, s, count); +#else + video_drawchars (xx, yy, s, count); +#endif +} + +static void video_putchar (int xx, int yy, unsigned char c) +{ +#ifdef CONFIG_VIDEO_LOGO + video_drawchars (xx, yy + VIDEO_LOGO_HEIGHT, &c, 1); +#else + video_drawchars (xx, yy, &c, 1); +#endif +} + +static inline void video_putstring (int xx, int yy, unsigned char *s) +{ + video_putchars (xx, yy, (unsigned char *)s, strlen ((char *)s)); +} + +/************************************************************************/ +/* ** VIDEO CONTROLLER LOW-LEVEL FUNCTIONS */ +/************************************************************************/ + +#if !defined(CONFIG_RRVISION) +static void video_mode_dupefield (VRAM * source, VRAM * dest, int entries) +{ + int i; + + for (i = 0; i < entries; i++) { + dest[i] = source[i]; /* Copy the entire record */ + dest[i].fx = (!dest[i].fx) * 3; /* Negate field bit */ + } + + dest[0].lcyc++; /* Add a cycle to the first entry */ + dest[entries - 1].lst = 1; /* Set end of ram entries */ +} +#endif + +static void inline video_mode_addentry (VRAM * vr, + int Hx, int Vx, int Fx, int Bx, + int VDS, int INT, int LCYC, int LP, int LST) +{ + vr->hx = Hx; + vr->vx = Vx; + vr->fx = Fx; + vr->bx = Bx; + vr->vds = VDS; + vr->inter = INT; + vr->lcyc = LCYC; + vr->lp = LP; + vr->lst = LST; +} + +#define ADDENTRY(a,b,c,d,e,f,g,h,i) video_mode_addentry(&vr[entry++],a,b,c,d,e,f,g,h,i) + +static int video_mode_generate (void) +{ + immap_t *immap = (immap_t *) CONFIG_SYS_IMMR; + VRAM *vr = (VRAM *) (((void *) immap) + 0xb00); /* Pointer to the VRAM table */ + int DX, X1, X2, DY, Y1, Y2, entry = 0, fifo; + + /* CHECKING PARAMETERS */ + + if (video_panning_factor_y < -128) + video_panning_factor_y = -128; + + if (video_panning_factor_y > 128) + video_panning_factor_y = 128; + + if (video_panning_factor_x < -128) + video_panning_factor_x = -128; + + if (video_panning_factor_x > 128) + video_panning_factor_x = 128; + + /* Setting panning */ + + DX = video_panning_range_x = (VIDEO_ACTIVE_COLS - VIDEO_COLS) * 2; + DY = video_panning_range_y = (VIDEO_ACTIVE_ROWS - VIDEO_ROWS) / 2; + + video_panning_value_x = (video_panning_factor_x + 128) * DX / 256; + video_panning_value_y = (video_panning_factor_y + 128) * DY / 256; + + /* We assume these are burst units (multiplied by 2, we need it pari) */ + X1 = video_panning_value_x & 0xfffe; + X2 = DX - X1; + + /* We assume these are field line units (divided by 2, we need it pari) */ + Y1 = video_panning_value_y & 0xfffe; + Y2 = DY - Y1; + + debug("X1=%d, X2=%d, Y1=%d, Y2=%d, DX=%d, DY=%d VIDEO_COLS=%d \n", + X1, X2, Y1, Y2, DX, DY, VIDEO_COLS); + +#ifdef VIDEO_MODE_NTSC +/* + * Hx Vx Fx Bx VDS INT LCYC LP LST + * + * Retrace blanking + */ + ADDENTRY (0, 0, 3, 0, 1, 0, 3, 1, 0); + ADDENTRY (3, 0, 3, 0, 1, 0, 243, 0, 0); + ADDENTRY (3, 0, 3, 0, 1, 0, 1440, 0, 0); + ADDENTRY (3, 0, 3, 0, 1, 0, 32, 1, 0); +/* + * Vertical blanking + */ + ADDENTRY (0, 0, 0, 0, 1, 0, 18, 1, 0); + ADDENTRY (3, 0, 0, 0, 1, 0, 243, 0, 0); + ADDENTRY (3, 0, 0, 0, 1, 0, 1440, 0, 0); + ADDENTRY (3, 0, 0, 0, 1, 0, 32, 1, 0); +/* + * Odd field active area (TOP) + */ + if (Y1 > 0) { + ADDENTRY (0, 0, 0, 0, 1, 0, Y1, 1, 0); + ADDENTRY (3, 0, 0, 0, 1, 0, 235, 0, 0); + ADDENTRY (3, 0, 0, 3, 1, 0, 1448, 0, 0); + ADDENTRY (3, 0, 0, 0, 1, 0, 32, 1, 0); + } +/* + * Odd field active area + */ + ADDENTRY (0, 0, 0, 0, 1, 0, 240 - DY, 1, 0); + ADDENTRY (3, 0, 0, 0, 1, 0, 235, 0, 0); + ADDENTRY (3, 0, 0, 3, 1, 0, 8 + X1, 0, 0); + ADDENTRY (3, 0, 0, 3, 0, 0, VIDEO_COLS * 2, 0, 0); + + if (X2 > 0) + ADDENTRY (3, 0, 0, 3, 1, 0, X2, 0, 0); + + ADDENTRY (3, 0, 0, 0, 1, 0, 32, 1, 0); + +/* + * Odd field active area (BOTTOM) + */ + if (Y1 > 0) { + ADDENTRY (0, 0, 0, 0, 1, 0, Y2, 1, 0); + ADDENTRY (3, 0, 0, 0, 1, 0, 235, 0, 0); + ADDENTRY (3, 0, 0, 3, 1, 0, 1448, 0, 0); + ADDENTRY (3, 0, 0, 0, 1, 0, 32, 1, 0); + } +/* + * Vertical blanking + */ + ADDENTRY (0, 0, 0, 0, 1, 0, 4, 1, 0); + ADDENTRY (3, 0, 0, 0, 1, 0, 243, 0, 0); + ADDENTRY (3, 0, 0, 0, 1, 0, 1440, 0, 0); + ADDENTRY (3, 0, 0, 0, 1, 0, 32, 1, 0); +/* + * Vertical blanking + */ + ADDENTRY (0, 0, 3, 0, 1, 0, 19, 1, 0); + ADDENTRY (3, 0, 3, 0, 1, 0, 243, 0, 0); + ADDENTRY (3, 0, 3, 0, 1, 0, 1440, 0, 0); + ADDENTRY (3, 0, 3, 0, 1, 0, 32, 1, 0); +/* + * Even field active area (TOP) + */ + if (Y1 > 0) { + ADDENTRY (0, 0, 3, 0, 1, 0, Y1, 1, 0); + ADDENTRY (3, 0, 3, 0, 1, 0, 235, 0, 0); + ADDENTRY (3, 0, 3, 3, 1, 0, 1448, 0, 0); + ADDENTRY (3, 0, 3, 0, 1, 0, 32, 1, 0); + } +/* + * Even field active area (CENTER) + */ + ADDENTRY (0, 0, 3, 0, 1, 0, 240 - DY, 1, 0); + ADDENTRY (3, 0, 3, 0, 1, 0, 235, 0, 0); + ADDENTRY (3, 0, 3, 3, 1, 0, 8 + X1, 0, 0); + ADDENTRY (3, 0, 3, 3, 0, 0, VIDEO_COLS * 2, 0, 0); + + if (X2 > 0) + ADDENTRY (3, 0, 3, 3, 1, 0, X2, 0, 0); + + ADDENTRY (3, 0, 3, 0, 1, 0, 32, 1, 0); +/* + * Even field active area (BOTTOM) + */ + if (Y1 > 0) { + ADDENTRY (0, 0, 3, 0, 1, 0, Y2, 1, 0); + ADDENTRY (3, 0, 3, 0, 1, 0, 235, 0, 0); + ADDENTRY (3, 0, 3, 3, 1, 0, 1448, 0, 0); + ADDENTRY (3, 0, 3, 0, 1, 0, 32, 1, 0); + } +/* + * Vertical blanking + */ + ADDENTRY (0, 0, 3, 0, 1, 0, 1, 1, 0); + ADDENTRY (3, 0, 3, 0, 1, 0, 243, 0, 0); + ADDENTRY (3, 0, 3, 0, 1, 0, 1440, 0, 0); + ADDENTRY (3, 0, 3, 0, 1, 1, 32, 1, 1); +#endif + +#ifdef VIDEO_MODE_PAL + +#if defined(CONFIG_RRVISION) + +#define HPW 160 /* horizontal pulse width (was 139) */ +#define VPW 2 /* vertical pulse width */ +#define HBP 104 /* horizontal back porch (was 112) */ +#define VBP 19 /* vertical back porch (was 19) */ +#define VID_R 240 /* number of rows */ + + debug ("[VIDEO CTRL] Starting to add controller entries..."); +/* + * Even field + */ + ADDENTRY (0, 3, 0, 3, 1, 0, 2, 0, 0); + ADDENTRY (0, 0, 0, 3, 1, 0, HPW, 0, 0); + ADDENTRY (3, 0, 0, 3, 1, 0, HBP + (VIDEO_COLS * 2) + 72, 0, 0); + + ADDENTRY (0, 0, 0, 3, 1, 0, VPW, 1, 0); + ADDENTRY (0, 0, 0, 3, 1, 0, HPW-1, 0, 0); + ADDENTRY (3, 0, 0, 3, 1, 0, HBP + (VIDEO_COLS * 2) + 72, 1, 0); + + ADDENTRY (0, 3, 0, 3, 1, 0, VBP, 1, 0); + ADDENTRY (0, 3, 0, 3, 1, 0, HPW-1, 0, 0); + ADDENTRY (3, 3, 0, 3, 1, 0, HBP + (VIDEO_COLS * 2) + 72, 1, 0); +/* + * Active area + */ + ADDENTRY (0, 3, 0, 3, 1, 0, VID_R , 1, 0); + ADDENTRY (0, 3, 0, 3, 1, 0, HPW-1, 0, 0); + ADDENTRY (3, 3, 0, 3, 1, 0, HBP, 0, 0); + ADDENTRY (3, 3, 0, 3, 0, 0, VIDEO_COLS*2, 0, 0); + ADDENTRY (3, 3, 0, 3, 1, 0, 72, 1, 1); + + ADDENTRY (0, 3, 0, 3, 1, 0, 51, 1, 0); + ADDENTRY (0, 3, 0, 3, 1, 0, HPW-1, 0, 0); + ADDENTRY (3, 3, 0, 3, 1, 0, HBP +(VIDEO_COLS * 2) + 72 , 1, 0); +/* + * Odd field + */ + ADDENTRY (0, 3, 0, 3, 1, 0, 2, 0, 0); + ADDENTRY (0, 0, 0, 3, 1, 0, HPW, 0, 0); + ADDENTRY (3, 0, 0, 3, 1, 0, HBP + (VIDEO_COLS * 2) + 72, 0, 0); + + ADDENTRY (0, 0, 0, 3, 1, 0, VPW+1, 1, 0); + ADDENTRY (0, 0, 0, 3, 1, 0, HPW-1, 0, 0); + ADDENTRY (3, 0, 0, 3, 1, 0, HBP + (VIDEO_COLS * 2) + 72, 1, 0); + + ADDENTRY (0, 3, 0, 3, 1, 0, VBP, 1, 0); + ADDENTRY (0, 3, 0, 3, 1, 0, HPW-1, 0, 0); + ADDENTRY (3, 3, 0, 3, 1, 0, HBP + (VIDEO_COLS * 2) + 72, 1, 0); +/* + * Active area + */ + ADDENTRY (0, 3, 0, 3, 1, 0, VID_R , 1, 0); + ADDENTRY (0, 3, 0, 3, 1, 0, HPW-1, 0, 0); + ADDENTRY (3, 3, 0, 3, 1, 0, HBP, 0, 0); + ADDENTRY (3, 3, 0, 3, 0, 0, VIDEO_COLS*2, 0, 0); + ADDENTRY (3, 3, 0, 3, 1, 0, 72, 1, 1); + + ADDENTRY (0, 3, 0, 3, 1, 0, 51, 1, 0); + ADDENTRY (0, 3, 0, 3, 1, 0, HPW-1, 0, 0); + ADDENTRY (3, 3, 0, 3, 1, 0, HBP +(VIDEO_COLS * 2) + 72 , 1, 0); + + debug ("done\n"); + +#else /* !CONFIG_RRVISION */ + +/* + * Hx Vx Fx Bx VDS INT LCYC LP LST + * + * vertical; blanking + */ + ADDENTRY (0, 0, 0, 0, 1, 0, 22, 1, 0); + ADDENTRY (3, 0, 0, 0, 1, 0, 263, 0, 0); + ADDENTRY (3, 0, 0, 0, 1, 0, 1440, 0, 0); + ADDENTRY (3, 0, 0, 0, 1, 0, 24, 1, 0); +/* + * active area (TOP) + */ + if (Y1 > 0) { + ADDENTRY (0, 0, 0, 0, 1, 0, Y1, 1, 0); /* 11? */ + ADDENTRY (3, 0, 0, 0, 1, 0, 255, 0, 0); + ADDENTRY (3, 0, 0, 3, 1, 0, 1448, 0, 0); + ADDENTRY (3, 0, 0, 0, 1, 0, 24, 1, 0); + } +/* + * field active area (CENTER) + */ + ADDENTRY (0, 0, 0, 0, 1, 0, 288 - DY, 1, 0); /* 265? */ + ADDENTRY (3, 0, 0, 0, 1, 0, 255, 0, 0); + ADDENTRY (3, 0, 0, 3, 1, 0, 8 + X1, 0, 0); + ADDENTRY (3, 0, 0, 3, 0, 0, VIDEO_COLS * 2, 0, 0); + + if (X2 > 0) + ADDENTRY (3, 0, 0, 1, 1, 0, X2, 0, 0); + + ADDENTRY (3, 0, 0, 0, 1, 0, 24, 1, 0); +/* + * field active area (BOTTOM) + */ + if (Y2 > 0) { + ADDENTRY (0, 0, 0, 0, 1, 0, Y2, 1, 0); /* 12? */ + ADDENTRY (3, 0, 0, 0, 1, 0, 255, 0, 0); + ADDENTRY (3, 0, 0, 3, 1, 0, 1448, 0, 0); + ADDENTRY (3, 0, 0, 0, 1, 0, 24, 1, 0); + } +/* + * field vertical; blanking + */ + ADDENTRY (0, 0, 0, 0, 1, 0, 2, 1, 0); + ADDENTRY (3, 0, 0, 0, 1, 0, 263, 0, 0); + ADDENTRY (3, 0, 0, 0, 1, 0, 1440, 0, 0); + ADDENTRY (3, 0, 0, 0, 1, 0, 24, 1, 0); +/* + * Create the other field (like this, but whit other field selected, + * one more cycle loop and a last identifier) + */ + video_mode_dupefield (vr, &vr[entry], entry); +#endif /* CONFIG_RRVISION */ + +#endif /* VIDEO_MODE_PAL */ + + /* See what FIFO are we using */ + fifo = GETBIT (immap->im_vid.vid_vsr, VIDEO_VSR_CAS); + + /* Set number of lines and burst (only one frame for now) */ + if (fifo) { + immap->im_vid.vid_vfcr0 = VIDEO_BURST_LEN | + (VIDEO_BURST_LEN << 8) | ((VIDEO_ROWS / 2) << 19); + } else { + immap->im_vid.vid_vfcr1 = VIDEO_BURST_LEN | + (VIDEO_BURST_LEN << 8) | ((VIDEO_ROWS / 2) << 19); + } + + SETBIT (immap->im_vid.vid_vcmr, VIDEO_VCMR_ASEL, !fifo); + +/* + * Wait until changes are applied (not done) + * while (GETBIT(immap->im_vid.vid_vsr, VIDEO_VSR_CAS) == fifo) ; + */ + + /* Return number of VRAM entries */ + return entry * 2; +} + +static void video_encoder_init (void) +{ +#ifdef VIDEO_I2C + int rc; + + /* Initialize the I2C */ + debug ("[VIDEO ENCODER] Initializing I2C bus...\n"); + i2c_init (CONFIG_SYS_I2C_SPEED, CONFIG_SYS_I2C_SLAVE); + +#ifdef CONFIG_FADS + /* Reset ADV7176 chip */ + debug ("[VIDEO ENCODER] Resetting encoder...\n"); + (*(int *) BCSR4) &= ~(1 << 21); + + /* Wait for 5 ms inside the reset */ + debug ("[VIDEO ENCODER] Waiting for encoder reset...\n"); + udelay (5000); + + /* Take ADV7176 out of reset */ + (*(int *) BCSR4) |= 1 << 21; + + /* Wait for 5 ms after the reset */ + udelay (5000); +#endif /* CONFIG_FADS */ + + /* Send configuration */ +#ifdef DEBUG + { + int i; + + puts ("[VIDEO ENCODER] Configuring the encoder...\n"); + + printf ("Sending %zu bytes (@ %08lX) to I2C 0x%lX:\n ", + sizeof(video_encoder_data), + (ulong)video_encoder_data, + (ulong)VIDEO_I2C_ADDR); + for (i=0; i<sizeof(video_encoder_data); ++i) { + printf(" %02X", video_encoder_data[i]); + } + putc ('\n'); + } +#endif /* DEBUG */ + + if ((rc = i2c_write (VIDEO_I2C_ADDR, 0, 1, + video_encoder_data, + sizeof(video_encoder_data))) != 0) { + printf ("i2c_send error: rc=%d\n", rc); + return; + } +#endif /* VIDEO_I2C */ + return; +} + +static void video_ctrl_init (void *memptr) +{ + immap_t *immap = (immap_t *) CONFIG_SYS_IMMR; + + video_fb_address = memptr; + + /* Set background */ + debug ("[VIDEO CTRL] Setting background color...\n"); + immap->im_vid.vid_vbcb = VIDEO_BG_COL; + + /* Show the background */ + debug ("[VIDEO CTRL] Forcing background...\n"); + SETBIT (immap->im_vid.vid_vcmr, VIDEO_VCMR_BD, 1); + + /* Turn off video controller */ + debug ("[VIDEO CTRL] Turning off video controller...\n"); + SETBIT (immap->im_vid.vid_vccr, VIDEO_VCCR_VON, 0); + +#ifdef CONFIG_FADS + /* Turn on Video Port LED */ + debug ("[VIDEO CTRL] Turning off video port led...\n"); + SETBIT (*(int *) BCSR4, VIDEO_BCSR4_VIDLED_BIT, 1); + + /* Disable internal clock */ + debug ("[VIDEO CTRL] Disabling internal clock...\n"); + SETBIT (*(int *) BCSR4, VIDEO_BCSR4_EXTCLK_BIT, 0); +#endif + + /* Generate and make active a new video mode */ + debug ("[VIDEO CTRL] Generating video mode...\n"); + video_mode_generate (); + + /* Start of frame buffer (even and odd frame, to make it working with */ + /* any selected active set) */ + debug ("[VIDEO CTRL] Setting frame buffer address...\n"); + immap->im_vid.vid_vfaa1 = + immap->im_vid.vid_vfaa0 = (u32) video_fb_address; + immap->im_vid.vid_vfba1 = + immap->im_vid.vid_vfba0 = + (u32) video_fb_address + VIDEO_LINE_LEN; + + /* YUV, Big endian, SHIFT/CLK/CLK input (BEFORE ENABLING 27MHZ EXT CLOCK) */ + debug ("[VIDEO CTRL] Setting pixel mode and clocks...\n"); + immap->im_vid.vid_vccr = 0x2042; + + /* Configure port pins */ + debug ("[VIDEO CTRL] Configuring input/output pins...\n"); + immap->im_ioport.iop_pdpar = 0x1fff; + immap->im_ioport.iop_pddir = 0x0000; + +#ifdef CONFIG_FADS + /* Turn on Video Port Clock - ONLY AFTER SET VCCR TO ENABLE EXTERNAL CLOCK */ + debug ("[VIDEO CTRL] Turning on video clock...\n"); + SETBIT (*(int *) BCSR4, VIDEO_BCSR4_EXTCLK_BIT, 1); + + /* Turn on Video Port LED */ + debug ("[VIDEO CTRL] Turning on video port led...\n"); + SETBIT (*(int *) BCSR4, VIDEO_BCSR4_VIDLED_BIT, 0); +#endif +#ifdef CONFIG_RRVISION + debug ("PC5->Output(1): enable PAL clock"); + immap->im_ioport.iop_pcpar &= ~(0x0400); + immap->im_ioport.iop_pcdir |= 0x0400 ; + immap->im_ioport.iop_pcdat |= 0x0400 ; + debug ("PDPAR=0x%04X PDDIR=0x%04X PDDAT=0x%04X\n", + immap->im_ioport.iop_pdpar, + immap->im_ioport.iop_pddir, + immap->im_ioport.iop_pddat); + debug ("PCPAR=0x%04X PCDIR=0x%04X PCDAT=0x%04X\n", + immap->im_ioport.iop_pcpar, + immap->im_ioport.iop_pcdir, + immap->im_ioport.iop_pcdat); +#endif /* CONFIG_RRVISION */ + + /* Blanking the screen. */ + debug ("[VIDEO CTRL] Blanking the screen...\n"); + video_fill (VIDEO_BG_COL); + + /* + * Turns on Aggressive Mode. Normally, turning on the caches + * will cause the screen to flicker when the caches try to + * fill. This gives the FIFO's for the Video Controller + * higher priority and prevents flickering because of + * underrun. This may still be an issue when using FLASH, + * since accessing data from Flash is so slow. + */ + debug ("[VIDEO CTRL] Turning on aggressive mode...\n"); + immap->im_siu_conf.sc_sdcr = 0x40; + + /* Turn on video controller */ + debug ("[VIDEO CTRL] Turning on video controller...\n"); + SETBIT (immap->im_vid.vid_vccr, VIDEO_VCCR_VON, 1); + + /* Show the display */ + debug ("[VIDEO CTRL] Enabling the video...\n"); + SETBIT (immap->im_vid.vid_vcmr, VIDEO_VCMR_BD, 0); +} + +/************************************************************************/ +/* ** CONSOLE FUNCTIONS */ +/************************************************************************/ + +static void console_scrollup (void) +{ + /* Copy up rows ignoring the first one */ + memcpyl (CONSOLE_ROW_FIRST, CONSOLE_ROW_SECOND, CONSOLE_SCROLL_SIZE >> 2); + + /* Clear the last one */ + memsetl (CONSOLE_ROW_LAST, CONSOLE_ROW_SIZE >> 2, VIDEO_BG_COL); +} + +static inline void console_back (void) +{ + console_col--; + + if (console_col < 0) { + console_col = CONSOLE_COLS - 1; + console_row--; + if (console_row < 0) + console_row = 0; + } + + video_putchar ( console_col * VIDEO_FONT_WIDTH, + console_row * VIDEO_FONT_HEIGHT, ' '); +} + +static inline void console_newline (void) +{ + console_row++; + console_col = 0; + + /* Check if we need to scroll the terminal */ + if (console_row >= CONSOLE_ROWS) { + /* Scroll everything up */ + console_scrollup (); + + /* Decrement row number */ + console_row--; + } +} + +void video_putc (const char c) +{ + if (!video_enable) { + serial_putc (c); + return; + } + + switch (c) { + case 13: /* Simply ignore this */ + break; + + case '\n': /* Next line, please */ + console_newline (); + break; + + case 9: /* Tab (8 chars alignment) */ + console_col |= 0x0008; /* Next 8 chars boundary */ + console_col &= ~0x0007; /* Set this bit to zero */ + + if (console_col >= CONSOLE_COLS) + console_newline (); + break; + + case 8: /* Eat last character */ + console_back (); + break; + + default: /* Add to the console */ + video_putchar ( console_col * VIDEO_FONT_WIDTH, + console_row * VIDEO_FONT_HEIGHT, c); + console_col++; + /* Check if we need to go to next row */ + if (console_col >= CONSOLE_COLS) + console_newline (); + } +} + +void video_puts (const char *s) +{ + int count = strlen (s); + + if (!video_enable) + while (count--) + serial_putc (*s++); + else + while (count--) + video_putc (*s++); +} + +/************************************************************************/ +/* ** CURSOR BLINKING FUNCTIONS */ +/************************************************************************/ + +#ifdef VIDEO_BLINK + +#define BLINK_TIMER_ID 0 +#define BLINK_TIMER_HZ 2 + +static unsigned char blink_enabled = 0; +static timer_t blink_timer; + +static void blink_update (void) +{ + static int blink_row = -1, blink_col = -1, blink_old = 0; + + /* Check if we have a new position to invert */ + if ((console_row != blink_row) || (console_col != blink_col)) { + /* Check if we need to reverse last character */ + if (blink_old) + video_revchar ( blink_col * VIDEO_FONT_WIDTH, + (blink_row +#ifdef CONFIG_VIDEO_LOGO + + VIDEO_LOGO_HEIGHT +#endif + ) * VIDEO_FONT_HEIGHT); + + /* Update values */ + blink_row = console_row; + blink_col = console_col; + blink_old = 0; + } + +/* Reverse this character */ + blink_old = !blink_old; + video_revchar ( console_col * VIDEO_FONT_WIDTH, + (console_row +#ifdef CONFIG_VIDEO_LOGO + + VIDEO_LOGO_HEIGHT +#endif + ) * VIDEO_FONT_HEIGHT); + +} + +/* + * Handler for blinking cursor + */ +static void blink_handler (void *arg) +{ +/* Blink */ + blink_update (); +/* Ack the timer */ + timer_ack (&blink_timer); +} + +int blink_set (int blink) +{ + int ret = blink_enabled; + + if (blink) + timer_enable (&blink_timer); + else + timer_disable (&blink_timer); + + blink_enabled = blink; + + return ret; +} + +static inline void blink_close (void) +{ + timer_close (&blink_timer); +} + +static inline void blink_init (void) +{ + timer_init (&blink_timer, + BLINK_TIMER_ID, BLINK_TIMER_HZ, + blink_handler); +} +#endif + +/************************************************************************/ +/* ** LOGO PLOTTING FUNCTIONS */ +/************************************************************************/ + +#ifdef CONFIG_VIDEO_LOGO +void easylogo_plot (fastimage_t * image, void *screen, int width, int x, + int y) +{ + int skip = width - image->width, xcount, ycount = image->height; + +#ifdef VIDEO_MODE_YUYV + ushort *source = (ushort *) image->data; + ushort *dest = (ushort *) screen + y * width + x; + + while (ycount--) { + xcount = image->width; + while (xcount--) + *dest++ = *source++; + dest += skip; + } +#endif +#ifdef VIDEO_MODE_RGB + unsigned char + *source = (unsigned short *) image->data, + *dest = (unsigned short *) screen + ((y * width) + x) * 3; + + while (ycount--) { + xcount = image->width * 3; + memcpy (dest, source, xcount); + source += xcount; + dest += ycount; + } +#endif +} + +static void *video_logo (void) +{ + u16 *screen = video_fb_address, width = VIDEO_COLS; +#ifdef VIDEO_INFO +# ifndef CONFIG_FADS + char temp[32]; +# endif + char info[80]; +#endif /* VIDEO_INFO */ + + easylogo_plot (VIDEO_LOGO_ADDR, screen, width, 0, 0); + +#ifdef VIDEO_INFO + sprintf (info, "%s (%s - %s) ", + U_BOOT_VERSION, U_BOOT_DATE, U_BOOT_TIME); + video_drawstring (VIDEO_INFO_X, VIDEO_INFO_Y, info); + + sprintf (info, "(C) 2002 DENX Software Engineering"); + video_drawstring (VIDEO_INFO_X, VIDEO_INFO_Y + VIDEO_FONT_HEIGHT, + info); + + sprintf (info, " Wolfgang DENK, wd@denx.de"); + video_drawstring (VIDEO_INFO_X, VIDEO_INFO_Y + VIDEO_FONT_HEIGHT * 2, + info); +#ifndef CONFIG_FADS /* all normal boards */ + /* leave one blank line */ + + sprintf (info, "MPC823 CPU at %s MHz, %ld MB RAM, %ld MB Flash", + strmhz(temp, gd->cpu_clk), + gd->ram_size >> 20, + gd->bd->bi_flashsize >> 20 ); + video_drawstring (VIDEO_INFO_X, VIDEO_INFO_Y + VIDEO_FONT_HEIGHT * 4, + info); +#else /* FADS :-( */ + sprintf (info, "MPC823 CPU at 50 MHz on FADS823 board"); + video_drawstring (VIDEO_INFO_X, VIDEO_INFO_Y + VIDEO_FONT_HEIGHT, + info); + + sprintf (info, "2MB FLASH - 8MB DRAM - 4MB SRAM"); + video_drawstring (VIDEO_INFO_X, VIDEO_INFO_Y + VIDEO_FONT_HEIGHT * 2, + info); +#endif +#endif + + return video_fb_address + VIDEO_LOGO_HEIGHT * VIDEO_LINE_LEN; +} +#endif + +/************************************************************************/ +/* ** VIDEO HIGH-LEVEL FUNCTIONS */ +/************************************************************************/ + +static int video_init (void *videobase) +{ + /* Initialize the encoder */ + debug ("[VIDEO] Initializing video encoder...\n"); + video_encoder_init (); + + /* Initialize the video controller */ + debug ("[VIDEO] Initializing video controller at %08x...\n", + (int) videobase); + video_ctrl_init (videobase); + + /* Setting the palette */ + video_setpalette (CONSOLE_COLOR_BLACK, 0, 0, 0); + video_setpalette (CONSOLE_COLOR_RED, 0xFF, 0, 0); + video_setpalette (CONSOLE_COLOR_GREEN, 0, 0xFF, 0); + video_setpalette (CONSOLE_COLOR_YELLOW, 0xFF, 0xFF, 0); + video_setpalette (CONSOLE_COLOR_BLUE, 0, 0, 0xFF); + video_setpalette (CONSOLE_COLOR_MAGENTA, 0xFF, 0, 0xFF); + video_setpalette (CONSOLE_COLOR_CYAN, 0, 0xFF, 0xFF); + video_setpalette (CONSOLE_COLOR_GREY, 0xAA, 0xAA, 0xAA); + video_setpalette (CONSOLE_COLOR_GREY2, 0xF8, 0xF8, 0xF8); + video_setpalette (CONSOLE_COLOR_WHITE, 0xFF, 0xFF, 0xFF); + +#ifndef CONFIG_SYS_WHITE_ON_BLACK + video_setfgcolor (CONSOLE_COLOR_BLACK); + video_setbgcolor (CONSOLE_COLOR_GREY2); +#else + video_setfgcolor (CONSOLE_COLOR_GREY2); + video_setbgcolor (CONSOLE_COLOR_BLACK); +#endif /* CONFIG_SYS_WHITE_ON_BLACK */ + +#ifdef CONFIG_VIDEO_LOGO + /* Paint the logo and retrieve tv base address */ + debug ("[VIDEO] Drawing the logo...\n"); + video_console_address = video_logo (); +#else + video_console_address = video_fb_address; +#endif + +#ifdef VIDEO_BLINK + /* Enable the blinking (under construction) */ + blink_init (); + blink_set (0); /* To Fix! */ +#endif + + /* Initialize the console */ + console_col = 0; + console_row = 0; + video_enable = 1; + +#ifdef VIDEO_MODE_PAL +# define VIDEO_MODE_TMP1 "PAL" +#endif +#ifdef VIDEO_MODE_NTSC +# define VIDEO_MODE_TMP1 "NTSC" +#endif +#ifdef VIDEO_MODE_YUYV +# define VIDEO_MODE_TMP2 "YCbYCr" +#endif +#ifdef VIDEO_MODE_RGB +# define VIDEO_MODE_TMP2 "RGB" +#endif + debug ( VIDEO_MODE_TMP1 + " %dx%dx%d (" VIDEO_MODE_TMP2 ") on %s - console %dx%d\n", + VIDEO_COLS, VIDEO_ROWS, VIDEO_MODE_BPP, + VIDEO_ENCODER_NAME, CONSOLE_COLS, CONSOLE_ROWS); + return 0; +} + +int drv_video_init (void) +{ + int error, devices = 1; + + struct stdio_dev videodev; + + video_init ((void *)(gd->fb_base)); /* Video initialization */ + +/* Device initialization */ + + memset (&videodev, 0, sizeof (videodev)); + + strcpy (videodev.name, "video"); + videodev.ext = DEV_EXT_VIDEO; /* Video extensions */ + videodev.flags = DEV_FLAGS_OUTPUT; /* Output only */ + videodev.putc = video_putc; /* 'putc' function */ + videodev.puts = video_puts; /* 'puts' function */ + + error = stdio_register (&videodev); + + return (error == 0) ? devices : error; +} + +/************************************************************************/ +/* ** ROM capable initialization part - needed to reserve FB memory */ +/************************************************************************/ + +/* + * This is called early in the system initialization to grab memory + * for the video controller. + * Returns new address for monitor, after reserving video buffer memory + * + * Note that this is running from ROM, so no write access to global data. + */ +ulong video_setmem (ulong addr) +{ + /* Allocate pages for the frame buffer. */ + addr -= VIDEO_SIZE; + + debug ("Reserving %dk for Video Framebuffer at: %08lx\n", + VIDEO_SIZE>>10, addr); + + return (addr); +} + +#endif diff --git a/arch/powerpc/cpu/mpc8xx/wlkbd.c b/arch/powerpc/cpu/mpc8xx/wlkbd.c new file mode 100644 index 0000000..13009e2 --- /dev/null +++ b/arch/powerpc/cpu/mpc8xx/wlkbd.c @@ -0,0 +1,36 @@ +/* + * (C) Copyright 2000 + * Paolo Scaffardi, AIRVENT SAM s.p.a - RIMINI(ITALY), arsenio@tin.it + * + * 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> +#include <config.h> + +#ifdef CONFIG_WL_4PPM_KEYBOARD + +/* WIP: Wireless keyboard on SMC + */ +int drv_wlkbd_init (void) +{ + return 0 ; +} + +#endif /* CONFIG_WL_4PPM_KEYBOARD */ diff --git a/arch/powerpc/cpu/mpc8xxx/Makefile b/arch/powerpc/cpu/mpc8xxx/Makefile new file mode 100644 index 0000000..481f9e5 --- /dev/null +++ b/arch/powerpc/cpu/mpc8xxx/Makefile @@ -0,0 +1,27 @@ +# +# Copyright 2009 Freescale Semiconductor, Inc. +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# Version 2 as published by the Free Software Foundation. +# + +include $(TOPDIR)/config.mk + +LIB = $(obj)lib8xxx.a + +COBJS-y += cpu.o +COBJS-$(CONFIG_OF_LIBFDT) += fdt.o +COBJS-$(CONFIG_PCI) += pci_cfg.o + +SRCS := $(START:.o=.S) $(SOBJS-y:.o=.S) $(COBJS-y:.o=.c) +OBJS := $(addprefix $(obj),$(SOBJS-y) $(COBJS-y)) + +all: $(obj).depend $(LIB) + +$(LIB): $(OBJS) + $(AR) $(ARFLAGS) $@ $(OBJS) + +include $(SRCTREE)/rules.mk + +sinclude $(obj).depend diff --git a/arch/powerpc/cpu/mpc8xxx/cpu.c b/arch/powerpc/cpu/mpc8xxx/cpu.c new file mode 100644 index 0000000..22f3423 --- /dev/null +++ b/arch/powerpc/cpu/mpc8xxx/cpu.c @@ -0,0 +1,144 @@ +/* + * Copyright 2009-2010 Freescale Semiconductor, Inc. + * + * This file is derived from arch/powerpc/cpu/mpc85xx/cpu.c and + * arch/powerpc/cpu/mpc86xx/cpu.c. Basically this file contains + * cpu specific common code for 85xx/86xx processors. + * 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 <config.h> +#include <common.h> +#include <command.h> +#include <tsec.h> +#include <netdev.h> +#include <asm/cache.h> +#include <asm/io.h> + +DECLARE_GLOBAL_DATA_PTR; + +struct cpu_type cpu_type_list [] = { +#if defined(CONFIG_MPC85xx) + CPU_TYPE_ENTRY(8533, 8533, 1), + CPU_TYPE_ENTRY(8533, 8533_E, 1), + CPU_TYPE_ENTRY(8535, 8535, 1), + CPU_TYPE_ENTRY(8535, 8535_E, 1), + CPU_TYPE_ENTRY(8536, 8536, 1), + CPU_TYPE_ENTRY(8536, 8536_E, 1), + CPU_TYPE_ENTRY(8540, 8540, 1), + CPU_TYPE_ENTRY(8541, 8541, 1), + CPU_TYPE_ENTRY(8541, 8541_E, 1), + CPU_TYPE_ENTRY(8543, 8543, 1), + CPU_TYPE_ENTRY(8543, 8543_E, 1), + CPU_TYPE_ENTRY(8544, 8544, 1), + CPU_TYPE_ENTRY(8544, 8544_E, 1), + CPU_TYPE_ENTRY(8545, 8545, 1), + CPU_TYPE_ENTRY(8545, 8545_E, 1), + CPU_TYPE_ENTRY(8547, 8547_E, 1), + CPU_TYPE_ENTRY(8548, 8548, 1), + CPU_TYPE_ENTRY(8548, 8548_E, 1), + CPU_TYPE_ENTRY(8555, 8555, 1), + CPU_TYPE_ENTRY(8555, 8555_E, 1), + CPU_TYPE_ENTRY(8560, 8560, 1), + CPU_TYPE_ENTRY(8567, 8567, 1), + CPU_TYPE_ENTRY(8567, 8567_E, 1), + CPU_TYPE_ENTRY(8568, 8568, 1), + CPU_TYPE_ENTRY(8568, 8568_E, 1), + CPU_TYPE_ENTRY(8569, 8569, 1), + CPU_TYPE_ENTRY(8569, 8569_E, 1), + CPU_TYPE_ENTRY(8572, 8572, 2), + CPU_TYPE_ENTRY(8572, 8572_E, 2), + CPU_TYPE_ENTRY(P1011, P1011, 1), + CPU_TYPE_ENTRY(P1011, P1011_E, 1), + CPU_TYPE_ENTRY(P1012, P1012, 1), + CPU_TYPE_ENTRY(P1012, P1012_E, 1), + CPU_TYPE_ENTRY(P1013, P1013, 1), + CPU_TYPE_ENTRY(P1013, P1013_E, 1), + CPU_TYPE_ENTRY(P1020, P1020, 2), + CPU_TYPE_ENTRY(P1020, P1020_E, 2), + CPU_TYPE_ENTRY(P1021, P1021, 2), + CPU_TYPE_ENTRY(P1021, P1021_E, 2), + CPU_TYPE_ENTRY(P1022, P1022, 2), + CPU_TYPE_ENTRY(P1022, P1022_E, 2), + CPU_TYPE_ENTRY(P2010, P2010, 1), + CPU_TYPE_ENTRY(P2010, P2010_E, 1), + CPU_TYPE_ENTRY(P2020, P2020, 2), + CPU_TYPE_ENTRY(P2020, P2020_E, 2), + CPU_TYPE_ENTRY(P4040, P4040, 4), + CPU_TYPE_ENTRY(P4040, P4040_E, 4), + CPU_TYPE_ENTRY(P4080, P4080, 8), + CPU_TYPE_ENTRY(P4080, P4080_E, 8), +#elif defined(CONFIG_MPC86xx) + CPU_TYPE_ENTRY(8610, 8610, 1), + CPU_TYPE_ENTRY(8641, 8641, 2), + CPU_TYPE_ENTRY(8641D, 8641D, 2), +#endif +}; + +struct cpu_type cpu_type_unknown = CPU_TYPE_ENTRY(Unknown, Unknown, 1); + +struct cpu_type *identify_cpu(u32 ver) +{ + int i; + for (i = 0; i < ARRAY_SIZE(cpu_type_list); i++) { + if (cpu_type_list[i].soc_ver == ver) + return &cpu_type_list[i]; + } + return &cpu_type_unknown; +} + +int cpu_numcores() { + struct cpu_type *cpu; + cpu = gd->cpu; + return cpu->num_cores; +} + +int probecpu (void) +{ + uint svr; + uint ver; + + svr = get_svr(); + ver = SVR_SOC_VER(svr); + + gd->cpu = identify_cpu(ver); + + return 0; +} + +/* + * Initializes on-chip ethernet controllers. + * to override, implement board_eth_init() + */ +int cpu_eth_init(bd_t *bis) +{ +#if defined(CONFIG_ETHER_ON_FCC) + fec_initialize(bis); +#endif + +#if defined(CONFIG_UEC_ETH) + uec_standard_init(bis); +#endif + +#if defined(CONFIG_TSEC_ENET) || defined(CONFIG_MPC85XX_FEC) + tsec_standard_init(bis); +#endif + + return 0; +} diff --git a/arch/powerpc/cpu/mpc8xxx/ddr/Makefile b/arch/powerpc/cpu/mpc8xxx/ddr/Makefile new file mode 100644 index 0000000..cb7f856 --- /dev/null +++ b/arch/powerpc/cpu/mpc8xxx/ddr/Makefile @@ -0,0 +1,35 @@ +# +# Copyright 2008 Freescale Semiconductor, Inc. +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# Version 2 as published by the Free Software Foundation. +# + +include $(TOPDIR)/config.mk + +LIB = $(obj)libddr.a + +COBJS-$(CONFIG_FSL_DDR1) += main.o util.o ctrl_regs.o options.o \ + lc_common_dimm_params.o +COBJS-$(CONFIG_FSL_DDR1) += ddr1_dimm_params.o + +COBJS-$(CONFIG_FSL_DDR2) += main.o util.o ctrl_regs.o options.o \ + lc_common_dimm_params.o +COBJS-$(CONFIG_FSL_DDR2) += ddr2_dimm_params.o + +COBJS-$(CONFIG_FSL_DDR3) += main.o util.o ctrl_regs.o options.o \ + lc_common_dimm_params.o +COBJS-$(CONFIG_FSL_DDR3) += ddr3_dimm_params.o + +SRCS := $(START:.o=.S) $(SOBJS-y:.o=.S) $(COBJS-y:.o=.c) +OBJS := $(addprefix $(obj),$(SOBJS-y) $(COBJS-y)) + +all: $(obj).depend $(LIB) + +$(LIB): $(OBJS) + $(AR) $(ARFLAGS) $@ $(OBJS) + +include $(SRCTREE)/rules.mk + +sinclude $(obj).depend diff --git a/arch/powerpc/cpu/mpc8xxx/ddr/common_timing_params.h b/arch/powerpc/cpu/mpc8xxx/ddr/common_timing_params.h new file mode 100644 index 0000000..5aea517 --- /dev/null +++ b/arch/powerpc/cpu/mpc8xxx/ddr/common_timing_params.h @@ -0,0 +1,53 @@ +/* + * Copyright 2008 Freescale Semiconductor, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * Version 2 as published by the Free Software Foundation. + */ + +#ifndef COMMON_TIMING_PARAMS_H +#define COMMON_TIMING_PARAMS_H + +typedef struct { + /* parameters to constrict */ + + unsigned int tCKmin_X_ps; + unsigned int tCKmax_ps; + unsigned int tCKmax_max_ps; + unsigned int tRCD_ps; + unsigned int tRP_ps; + unsigned int tRAS_ps; + + unsigned int tWR_ps; /* maximum = 63750 ps */ + unsigned int tWTR_ps; /* maximum = 63750 ps */ + unsigned int tRFC_ps; /* maximum = 255 ns + 256 ns + .75 ns + = 511750 ps */ + + unsigned int tRRD_ps; /* maximum = 63750 ps */ + unsigned int tRC_ps; /* maximum = 254 ns + .75 ns = 254750 ps */ + + unsigned int refresh_rate_ps; + + unsigned int tIS_ps; /* byte 32, spd->ca_setup */ + unsigned int tIH_ps; /* byte 33, spd->ca_hold */ + unsigned int tDS_ps; /* byte 34, spd->data_setup */ + unsigned int tDH_ps; /* byte 35, spd->data_hold */ + unsigned int tRTP_ps; /* byte 38, spd->trtp */ + unsigned int tDQSQ_max_ps; /* byte 44, spd->tdqsq */ + unsigned int tQHS_ps; /* byte 45, spd->tqhs */ + + unsigned int ndimms_present; + unsigned int lowest_common_SPD_caslat; + unsigned int highest_common_derated_caslat; + unsigned int additive_latency; + unsigned int all_DIMMs_burst_lengths_bitmask; + unsigned int all_DIMMs_registered; + unsigned int all_DIMMs_unbuffered; + unsigned int all_DIMMs_ECC_capable; + + unsigned long long total_mem; + unsigned long long base_address; +} common_timing_params_t; + +#endif diff --git a/arch/powerpc/cpu/mpc8xxx/ddr/ctrl_regs.c b/arch/powerpc/cpu/mpc8xxx/ddr/ctrl_regs.c new file mode 100644 index 0000000..03f9c43 --- /dev/null +++ b/arch/powerpc/cpu/mpc8xxx/ddr/ctrl_regs.c @@ -0,0 +1,1366 @@ +/* + * Copyright 2008-2010 Freescale Semiconductor, 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. + */ + +/* + * Generic driver for Freescale DDR/DDR2/DDR3 memory controller. + * Based on code from spd_sdram.c + * Author: James Yang [at freescale.com] + */ + +#include <common.h> +#include <asm/fsl_ddr_sdram.h> + +#include "ddr.h" + +extern unsigned int picos_to_mclk(unsigned int picos); +/* + * Determine Rtt value. + * + * This should likely be either board or controller specific. + * + * Rtt(nominal) - DDR2: + * 0 = Rtt disabled + * 1 = 75 ohm + * 2 = 150 ohm + * 3 = 50 ohm + * Rtt(nominal) - DDR3: + * 0 = Rtt disabled + * 1 = 60 ohm + * 2 = 120 ohm + * 3 = 40 ohm + * 4 = 20 ohm + * 5 = 30 ohm + * + * FIXME: Apparently 8641 needs a value of 2 + * FIXME: Old code seys if 667 MHz or higher, use 3 on 8572 + * + * FIXME: There was some effort down this line earlier: + * + * unsigned int i; + * for (i = 0; i < CONFIG_CHIP_SELECTS_PER_CTRL/2; i++) { + * if (popts->dimmslot[i].num_valid_cs + * && (popts->cs_local_opts[2*i].odt_rd_cfg + * || popts->cs_local_opts[2*i].odt_wr_cfg)) { + * rtt = 2; + * break; + * } + * } + */ +static inline int fsl_ddr_get_rtt(void) +{ + int rtt; + +#if defined(CONFIG_FSL_DDR1) + rtt = 0; +#elif defined(CONFIG_FSL_DDR2) + rtt = 3; +#else + rtt = 0; +#endif + + return rtt; +} + +/* + * compute the CAS write latency according to DDR3 spec + * CWL = 5 if tCK >= 2.5ns + * 6 if 2.5ns > tCK >= 1.875ns + * 7 if 1.875ns > tCK >= 1.5ns + * 8 if 1.5ns > tCK >= 1.25ns + */ +static inline unsigned int compute_cas_write_latency(void) +{ + unsigned int cwl; + const unsigned int mclk_ps = get_memory_clk_period_ps(); + + if (mclk_ps >= 2500) + cwl = 5; + else if (mclk_ps >= 1875) + cwl = 6; + else if (mclk_ps >= 1500) + cwl = 7; + else if (mclk_ps >= 1250) + cwl = 8; + else + cwl = 8; + return cwl; +} + +/* Chip Select Configuration (CSn_CONFIG) */ +static void set_csn_config(int i, fsl_ddr_cfg_regs_t *ddr, + const memctl_options_t *popts, + const dimm_params_t *dimm_params) +{ + unsigned int cs_n_en = 0; /* Chip Select enable */ + unsigned int intlv_en = 0; /* Memory controller interleave enable */ + unsigned int intlv_ctl = 0; /* Interleaving control */ + unsigned int ap_n_en = 0; /* Chip select n auto-precharge enable */ + unsigned int odt_rd_cfg = 0; /* ODT for reads configuration */ + unsigned int odt_wr_cfg = 0; /* ODT for writes configuration */ + unsigned int ba_bits_cs_n = 0; /* Num of bank bits for SDRAM on CSn */ + unsigned int row_bits_cs_n = 0; /* Num of row bits for SDRAM on CSn */ + unsigned int col_bits_cs_n = 0; /* Num of ocl bits for SDRAM on CSn */ + + /* Compute CS_CONFIG only for existing ranks of each DIMM. */ + if ((((i&1) == 0) + && (dimm_params[i/2].n_ranks == 1)) + || (dimm_params[i/2].n_ranks == 2)) { + unsigned int n_banks_per_sdram_device; + cs_n_en = 1; + if (i == 0) { + /* These fields only available in CS0_CONFIG */ + intlv_en = popts->memctl_interleaving; + intlv_ctl = popts->memctl_interleaving_mode; + } + ap_n_en = popts->cs_local_opts[i].auto_precharge; + odt_rd_cfg = popts->cs_local_opts[i].odt_rd_cfg; + odt_wr_cfg = popts->cs_local_opts[i].odt_wr_cfg; + n_banks_per_sdram_device + = dimm_params[i/2].n_banks_per_sdram_device; + ba_bits_cs_n = __ilog2(n_banks_per_sdram_device) - 2; + row_bits_cs_n = dimm_params[i/2].n_row_addr - 12; + col_bits_cs_n = dimm_params[i/2].n_col_addr - 8; + } + + ddr->cs[i].config = (0 + | ((cs_n_en & 0x1) << 31) + | ((intlv_en & 0x3) << 29) + | ((intlv_ctl & 0xf) << 24) + | ((ap_n_en & 0x1) << 23) + + /* XXX: some implementation only have 1 bit starting at left */ + | ((odt_rd_cfg & 0x7) << 20) + + /* XXX: Some implementation only have 1 bit starting at left */ + | ((odt_wr_cfg & 0x7) << 16) + + | ((ba_bits_cs_n & 0x3) << 14) + | ((row_bits_cs_n & 0x7) << 8) + | ((col_bits_cs_n & 0x7) << 0) + ); + debug("FSLDDR: cs[%d]_config = 0x%08x\n", i,ddr->cs[i].config); +} + +/* Chip Select Configuration 2 (CSn_CONFIG_2) */ +/* FIXME: 8572 */ +static void set_csn_config_2(int i, fsl_ddr_cfg_regs_t *ddr) +{ + unsigned int pasr_cfg = 0; /* Partial array self refresh config */ + + ddr->cs[i].config_2 = ((pasr_cfg & 7) << 24); + debug("FSLDDR: cs[%d]_config_2 = 0x%08x\n", i, ddr->cs[i].config_2); +} + +/* -3E = 667 CL5, -25 = CL6 800, -25E = CL5 800 */ + +#if !defined(CONFIG_FSL_DDR1) +/* + * DDR SDRAM Timing Configuration 0 (TIMING_CFG_0) + * + * Avoid writing for DDR I. The new PQ38 DDR controller + * dreams up non-zero default values to be backwards compatible. + */ +static void set_timing_cfg_0(fsl_ddr_cfg_regs_t *ddr) +{ + unsigned char trwt_mclk = 0; /* Read-to-write turnaround */ + unsigned char twrt_mclk = 0; /* Write-to-read turnaround */ + /* 7.5 ns on -3E; 0 means WL - CL + BL/2 + 1 */ + unsigned char trrt_mclk = 0; /* Read-to-read turnaround */ + unsigned char twwt_mclk = 0; /* Write-to-write turnaround */ + + /* Active powerdown exit timing (tXARD and tXARDS). */ + unsigned char act_pd_exit_mclk; + /* Precharge powerdown exit timing (tXP). */ + unsigned char pre_pd_exit_mclk; + /* Precharge powerdown exit timing (tAXPD). */ + unsigned char taxpd_mclk; + /* Mode register set cycle time (tMRD). */ + unsigned char tmrd_mclk; + +#if defined(CONFIG_FSL_DDR3) + /* + * (tXARD and tXARDS). Empirical? + * The DDR3 spec has not tXARD, + * we use the tXP instead of it. + * tXP=max(3nCK, 7.5ns) for DDR3. + * spec has not the tAXPD, we use + * tAXPD=8, need design to confirm. + */ + int tXP = max((get_memory_clk_period_ps() * 3), 7500); /* unit=ps */ + act_pd_exit_mclk = picos_to_mclk(tXP); + /* Mode register MR0[A12] is '1' - fast exit */ + pre_pd_exit_mclk = act_pd_exit_mclk; + taxpd_mclk = 8; + tmrd_mclk = 4; +#else /* CONFIG_FSL_DDR2 */ + /* + * (tXARD and tXARDS). Empirical? + * tXARD = 2 for DDR2 + * tXP=2 + * tAXPD=8 + */ + act_pd_exit_mclk = 2; + pre_pd_exit_mclk = 2; + taxpd_mclk = 8; + tmrd_mclk = 2; +#endif + + ddr->timing_cfg_0 = (0 + | ((trwt_mclk & 0x3) << 30) /* RWT */ + | ((twrt_mclk & 0x3) << 28) /* WRT */ + | ((trrt_mclk & 0x3) << 26) /* RRT */ + | ((twwt_mclk & 0x3) << 24) /* WWT */ + | ((act_pd_exit_mclk & 0x7) << 20) /* ACT_PD_EXIT */ + | ((pre_pd_exit_mclk & 0xF) << 16) /* PRE_PD_EXIT */ + | ((taxpd_mclk & 0xf) << 8) /* ODT_PD_EXIT */ + | ((tmrd_mclk & 0xf) << 0) /* MRS_CYC */ + ); + debug("FSLDDR: timing_cfg_0 = 0x%08x\n", ddr->timing_cfg_0); +} +#endif /* defined(CONFIG_FSL_DDR2) */ + +/* DDR SDRAM Timing Configuration 3 (TIMING_CFG_3) */ +static void set_timing_cfg_3(fsl_ddr_cfg_regs_t *ddr, + const common_timing_params_t *common_dimm, + unsigned int cas_latency) +{ + /* Extended Activate to precharge interval (tRAS) */ + unsigned int ext_acttopre = 0; + unsigned int ext_refrec; /* Extended refresh recovery time (tRFC) */ + unsigned int ext_caslat = 0; /* Extended MCAS latency from READ cmd */ + unsigned int cntl_adj = 0; /* Control Adjust */ + + /* If the tRAS > 19 MCLK, we use the ext mode */ + if (picos_to_mclk(common_dimm->tRAS_ps) > 0x13) + ext_acttopre = 1; + + ext_refrec = (picos_to_mclk(common_dimm->tRFC_ps) - 8) >> 4; + + /* If the CAS latency more than 8, use the ext mode */ + if (cas_latency > 8) + ext_caslat = 1; + + ddr->timing_cfg_3 = (0 + | ((ext_acttopre & 0x1) << 24) + | ((ext_refrec & 0xF) << 16) + | ((ext_caslat & 0x1) << 12) + | ((cntl_adj & 0x7) << 0) + ); + debug("FSLDDR: timing_cfg_3 = 0x%08x\n", ddr->timing_cfg_3); +} + +/* DDR SDRAM Timing Configuration 1 (TIMING_CFG_1) */ +static void set_timing_cfg_1(fsl_ddr_cfg_regs_t *ddr, + const memctl_options_t *popts, + const common_timing_params_t *common_dimm, + unsigned int cas_latency) +{ + /* Precharge-to-activate interval (tRP) */ + unsigned char pretoact_mclk; + /* Activate to precharge interval (tRAS) */ + unsigned char acttopre_mclk; + /* Activate to read/write interval (tRCD) */ + unsigned char acttorw_mclk; + /* CASLAT */ + unsigned char caslat_ctrl; + /* Refresh recovery time (tRFC) ; trfc_low */ + unsigned char refrec_ctrl; + /* Last data to precharge minimum interval (tWR) */ + unsigned char wrrec_mclk; + /* Activate-to-activate interval (tRRD) */ + unsigned char acttoact_mclk; + /* Last write data pair to read command issue interval (tWTR) */ + unsigned char wrtord_mclk; + + pretoact_mclk = picos_to_mclk(common_dimm->tRP_ps); + acttopre_mclk = picos_to_mclk(common_dimm->tRAS_ps); + acttorw_mclk = picos_to_mclk(common_dimm->tRCD_ps); + + /* + * Translate CAS Latency to a DDR controller field value: + * + * CAS Lat DDR I DDR II Ctrl + * Clocks SPD Bit SPD Bit Value + * ------- ------- ------- ----- + * 1.0 0 0001 + * 1.5 1 0010 + * 2.0 2 2 0011 + * 2.5 3 0100 + * 3.0 4 3 0101 + * 3.5 5 0110 + * 4.0 4 0111 + * 4.5 1000 + * 5.0 5 1001 + */ +#if defined(CONFIG_FSL_DDR1) + caslat_ctrl = (cas_latency + 1) & 0x07; +#elif defined(CONFIG_FSL_DDR2) + caslat_ctrl = 2 * cas_latency - 1; +#else + /* + * if the CAS latency more than 8 cycle, + * we need set extend bit for it at + * TIMING_CFG_3[EXT_CASLAT] + */ + if (cas_latency > 8) + cas_latency -= 8; + caslat_ctrl = 2 * cas_latency - 1; +#endif + + refrec_ctrl = picos_to_mclk(common_dimm->tRFC_ps) - 8; + wrrec_mclk = picos_to_mclk(common_dimm->tWR_ps); + if (popts->OTF_burst_chop_en) + wrrec_mclk += 2; + + acttoact_mclk = picos_to_mclk(common_dimm->tRRD_ps); + /* + * JEDEC has min requirement for tRRD + */ +#if defined(CONFIG_FSL_DDR3) + if (acttoact_mclk < 4) + acttoact_mclk = 4; +#endif + wrtord_mclk = picos_to_mclk(common_dimm->tWTR_ps); + /* + * JEDEC has some min requirements for tWTR + */ +#if defined(CONFIG_FSL_DDR2) + if (wrtord_mclk < 2) + wrtord_mclk = 2; +#elif defined(CONFIG_FSL_DDR3) + if (wrtord_mclk < 4) + wrtord_mclk = 4; +#endif + if (popts->OTF_burst_chop_en) + wrtord_mclk += 2; + + ddr->timing_cfg_1 = (0 + | ((pretoact_mclk & 0x0F) << 28) + | ((acttopre_mclk & 0x0F) << 24) + | ((acttorw_mclk & 0xF) << 20) + | ((caslat_ctrl & 0xF) << 16) + | ((refrec_ctrl & 0xF) << 12) + | ((wrrec_mclk & 0x0F) << 8) + | ((acttoact_mclk & 0x07) << 4) + | ((wrtord_mclk & 0x07) << 0) + ); + debug("FSLDDR: timing_cfg_1 = 0x%08x\n", ddr->timing_cfg_1); +} + +/* DDR SDRAM Timing Configuration 2 (TIMING_CFG_2) */ +static void set_timing_cfg_2(fsl_ddr_cfg_regs_t *ddr, + const memctl_options_t *popts, + const common_timing_params_t *common_dimm, + unsigned int cas_latency, + unsigned int additive_latency) +{ + /* Additive latency */ + unsigned char add_lat_mclk; + /* CAS-to-preamble override */ + unsigned short cpo; + /* Write latency */ + unsigned char wr_lat; + /* Read to precharge (tRTP) */ + unsigned char rd_to_pre; + /* Write command to write data strobe timing adjustment */ + unsigned char wr_data_delay; + /* Minimum CKE pulse width (tCKE) */ + unsigned char cke_pls; + /* Window for four activates (tFAW) */ + unsigned short four_act; + + /* FIXME add check that this must be less than acttorw_mclk */ + add_lat_mclk = additive_latency; + cpo = popts->cpo_override; + +#if defined(CONFIG_FSL_DDR1) + /* + * This is a lie. It should really be 1, but if it is + * set to 1, bits overlap into the old controller's + * otherwise unused ACSM field. If we leave it 0, then + * the HW will magically treat it as 1 for DDR 1. Oh Yea. + */ + wr_lat = 0; +#elif defined(CONFIG_FSL_DDR2) + wr_lat = cas_latency - 1; +#else + wr_lat = compute_cas_write_latency(); +#endif + + rd_to_pre = picos_to_mclk(common_dimm->tRTP_ps); + /* + * JEDEC has some min requirements for tRTP + */ +#if defined(CONFIG_FSL_DDR2) + if (rd_to_pre < 2) + rd_to_pre = 2; +#elif defined(CONFIG_FSL_DDR3) + if (rd_to_pre < 4) + rd_to_pre = 4; +#endif + if (additive_latency) + rd_to_pre += additive_latency; + if (popts->OTF_burst_chop_en) + rd_to_pre += 2; /* according to UM */ + + wr_data_delay = popts->write_data_delay; + cke_pls = picos_to_mclk(popts->tCKE_clock_pulse_width_ps); + four_act = picos_to_mclk(popts->tFAW_window_four_activates_ps); + + ddr->timing_cfg_2 = (0 + | ((add_lat_mclk & 0xf) << 28) + | ((cpo & 0x1f) << 23) + | ((wr_lat & 0xf) << 19) + | ((rd_to_pre & RD_TO_PRE_MASK) << RD_TO_PRE_SHIFT) + | ((wr_data_delay & WR_DATA_DELAY_MASK) << WR_DATA_DELAY_SHIFT) + | ((cke_pls & 0x7) << 6) + | ((four_act & 0x3f) << 0) + ); + debug("FSLDDR: timing_cfg_2 = 0x%08x\n", ddr->timing_cfg_2); +} + +/* DDR SDRAM control configuration (DDR_SDRAM_CFG) */ +static void set_ddr_sdram_cfg(fsl_ddr_cfg_regs_t *ddr, + const memctl_options_t *popts, + const common_timing_params_t *common_dimm) +{ + unsigned int mem_en; /* DDR SDRAM interface logic enable */ + unsigned int sren; /* Self refresh enable (during sleep) */ + unsigned int ecc_en; /* ECC enable. */ + unsigned int rd_en; /* Registered DIMM enable */ + unsigned int sdram_type; /* Type of SDRAM */ + unsigned int dyn_pwr; /* Dynamic power management mode */ + unsigned int dbw; /* DRAM dta bus width */ + unsigned int eight_be = 0; /* 8-beat burst enable, DDR2 is zero */ + unsigned int ncap = 0; /* Non-concurrent auto-precharge */ + unsigned int threeT_en; /* Enable 3T timing */ + unsigned int twoT_en; /* Enable 2T timing */ + unsigned int ba_intlv_ctl; /* Bank (CS) interleaving control */ + unsigned int x32_en = 0; /* x32 enable */ + unsigned int pchb8 = 0; /* precharge bit 8 enable */ + unsigned int hse; /* Global half strength override */ + unsigned int mem_halt = 0; /* memory controller halt */ + unsigned int bi = 0; /* Bypass initialization */ + + mem_en = 1; + sren = popts->self_refresh_in_sleep; + if (common_dimm->all_DIMMs_ECC_capable) { + /* Allow setting of ECC only if all DIMMs are ECC. */ + ecc_en = popts->ECC_mode; + } else { + ecc_en = 0; + } + + rd_en = (common_dimm->all_DIMMs_registered + && !common_dimm->all_DIMMs_unbuffered); + + sdram_type = CONFIG_FSL_SDRAM_TYPE; + + dyn_pwr = popts->dynamic_power; + dbw = popts->data_bus_width; + /* 8-beat burst enable DDR-III case + * we must clear it when use the on-the-fly mode, + * must set it when use the 32-bits bus mode. + */ + if (sdram_type == SDRAM_TYPE_DDR3) { + if (popts->burst_length == DDR_BL8) + eight_be = 1; + if (popts->burst_length == DDR_OTF) + eight_be = 0; + if (dbw == 0x1) + eight_be = 1; + } + + threeT_en = popts->threeT_en; + twoT_en = popts->twoT_en; + ba_intlv_ctl = popts->ba_intlv_ctl; + hse = popts->half_strength_driver_enable; + + ddr->ddr_sdram_cfg = (0 + | ((mem_en & 0x1) << 31) + | ((sren & 0x1) << 30) + | ((ecc_en & 0x1) << 29) + | ((rd_en & 0x1) << 28) + | ((sdram_type & 0x7) << 24) + | ((dyn_pwr & 0x1) << 21) + | ((dbw & 0x3) << 19) + | ((eight_be & 0x1) << 18) + | ((ncap & 0x1) << 17) + | ((threeT_en & 0x1) << 16) + | ((twoT_en & 0x1) << 15) + | ((ba_intlv_ctl & 0x7F) << 8) + | ((x32_en & 0x1) << 5) + | ((pchb8 & 0x1) << 4) + | ((hse & 0x1) << 3) + | ((mem_halt & 0x1) << 1) + | ((bi & 0x1) << 0) + ); + debug("FSLDDR: ddr_sdram_cfg = 0x%08x\n", ddr->ddr_sdram_cfg); +} + +/* DDR SDRAM control configuration 2 (DDR_SDRAM_CFG_2) */ +static void set_ddr_sdram_cfg_2(fsl_ddr_cfg_regs_t *ddr, + const memctl_options_t *popts) +{ + unsigned int frc_sr = 0; /* Force self refresh */ + unsigned int sr_ie = 0; /* Self-refresh interrupt enable */ + unsigned int dll_rst_dis; /* DLL reset disable */ + unsigned int dqs_cfg; /* DQS configuration */ + unsigned int odt_cfg; /* ODT configuration */ + unsigned int num_pr; /* Number of posted refreshes */ + unsigned int obc_cfg; /* On-The-Fly Burst Chop Cfg */ + unsigned int ap_en; /* Address Parity Enable */ + unsigned int d_init; /* DRAM data initialization */ + unsigned int rcw_en = 0; /* Register Control Word Enable */ + unsigned int md_en = 0; /* Mirrored DIMM Enable */ + + dll_rst_dis = 1; /* Make this configurable */ + dqs_cfg = popts->DQS_config; + if (popts->cs_local_opts[0].odt_rd_cfg + || popts->cs_local_opts[0].odt_wr_cfg) { + /* FIXME */ + odt_cfg = 2; + } else { + odt_cfg = 0; + } + + num_pr = 1; /* Make this configurable */ + + /* + * 8572 manual says + * {TIMING_CFG_1[PRETOACT] + * + [DDR_SDRAM_CFG_2[NUM_PR] + * * ({EXT_REFREC || REFREC} + 8 + 2)]} + * << DDR_SDRAM_INTERVAL[REFINT] + */ +#if defined(CONFIG_FSL_DDR3) + obc_cfg = popts->OTF_burst_chop_en; +#else + obc_cfg = 0; +#endif + + ap_en = 0; /* Make this configurable? */ + +#if defined(CONFIG_ECC_INIT_VIA_DDRCONTROLLER) + /* Use the DDR controller to auto initialize memory. */ + d_init = 1; + ddr->ddr_data_init = CONFIG_MEM_INIT_VALUE; + debug("DDR: ddr_data_init = 0x%08x\n", ddr->ddr_data_init); +#else + /* Memory will be initialized via DMA, or not at all. */ + d_init = 0; +#endif + +#if defined(CONFIG_FSL_DDR3) + md_en = popts->mirrored_dimm; +#endif + ddr->ddr_sdram_cfg_2 = (0 + | ((frc_sr & 0x1) << 31) + | ((sr_ie & 0x1) << 30) + | ((dll_rst_dis & 0x1) << 29) + | ((dqs_cfg & 0x3) << 26) + | ((odt_cfg & 0x3) << 21) + | ((num_pr & 0xf) << 12) + | ((obc_cfg & 0x1) << 6) + | ((ap_en & 0x1) << 5) + | ((d_init & 0x1) << 4) + | ((rcw_en & 0x1) << 2) + | ((md_en & 0x1) << 0) + ); + debug("FSLDDR: ddr_sdram_cfg_2 = 0x%08x\n", ddr->ddr_sdram_cfg_2); +} + +/* DDR SDRAM Mode configuration 2 (DDR_SDRAM_MODE_2) */ +static void set_ddr_sdram_mode_2(fsl_ddr_cfg_regs_t *ddr, + const memctl_options_t *popts) +{ + unsigned short esdmode2 = 0; /* Extended SDRAM mode 2 */ + unsigned short esdmode3 = 0; /* Extended SDRAM mode 3 */ + +#if defined(CONFIG_FSL_DDR3) + unsigned int rtt_wr = 0; /* Rtt_WR - dynamic ODT off */ + unsigned int srt = 0; /* self-refresh temerature, normal range */ + unsigned int asr = 0; /* auto self-refresh disable */ + unsigned int cwl = compute_cas_write_latency() - 5; + unsigned int pasr = 0; /* partial array self refresh disable */ + + if (popts->rtt_override) + rtt_wr = popts->rtt_wr_override_value; + + esdmode2 = (0 + | ((rtt_wr & 0x3) << 9) + | ((srt & 0x1) << 7) + | ((asr & 0x1) << 6) + | ((cwl & 0x7) << 3) + | ((pasr & 0x7) << 0)); +#endif + ddr->ddr_sdram_mode_2 = (0 + | ((esdmode2 & 0xFFFF) << 16) + | ((esdmode3 & 0xFFFF) << 0) + ); + debug("FSLDDR: ddr_sdram_mode_2 = 0x%08x\n", ddr->ddr_sdram_mode_2); +} + +/* DDR SDRAM Interval Configuration (DDR_SDRAM_INTERVAL) */ +static void set_ddr_sdram_interval(fsl_ddr_cfg_regs_t *ddr, + const memctl_options_t *popts, + const common_timing_params_t *common_dimm) +{ + unsigned int refint; /* Refresh interval */ + unsigned int bstopre; /* Precharge interval */ + + refint = picos_to_mclk(common_dimm->refresh_rate_ps); + + bstopre = popts->bstopre; + + /* refint field used 0x3FFF in earlier controllers */ + ddr->ddr_sdram_interval = (0 + | ((refint & 0xFFFF) << 16) + | ((bstopre & 0x3FFF) << 0) + ); + debug("FSLDDR: ddr_sdram_interval = 0x%08x\n", ddr->ddr_sdram_interval); +} + +#if defined(CONFIG_FSL_DDR3) +/* DDR SDRAM Mode configuration set (DDR_SDRAM_MODE) */ +static void set_ddr_sdram_mode(fsl_ddr_cfg_regs_t *ddr, + const memctl_options_t *popts, + const common_timing_params_t *common_dimm, + unsigned int cas_latency, + unsigned int additive_latency) +{ + unsigned short esdmode; /* Extended SDRAM mode */ + unsigned short sdmode; /* SDRAM mode */ + + /* Mode Register - MR1 */ + unsigned int qoff = 0; /* Output buffer enable 0=yes, 1=no */ + unsigned int tdqs_en = 0; /* TDQS Enable: 0=no, 1=yes */ + unsigned int rtt; + unsigned int wrlvl_en = 0; /* Write level enable: 0=no, 1=yes */ + unsigned int al = 0; /* Posted CAS# additive latency (AL) */ + unsigned int dic = 1; /* Output driver impedance, 34ohm */ + unsigned int dll_en = 0; /* DLL Enable 0=Enable (Normal), + 1=Disable (Test/Debug) */ + + /* Mode Register - MR0 */ + unsigned int dll_on; /* DLL control for precharge PD, 0=off, 1=on */ + unsigned int wr; /* Write Recovery */ + unsigned int dll_rst; /* DLL Reset */ + unsigned int mode; /* Normal=0 or Test=1 */ + unsigned int caslat = 4;/* CAS# latency, default set as 6 cycles */ + /* BT: Burst Type (0=Nibble Sequential, 1=Interleaved) */ + unsigned int bt; + unsigned int bl; /* BL: Burst Length */ + + unsigned int wr_mclk; + + const unsigned int mclk_ps = get_memory_clk_period_ps(); + + rtt = fsl_ddr_get_rtt(); + if (popts->rtt_override) + rtt = popts->rtt_override_value; + + if (additive_latency == (cas_latency - 1)) + al = 1; + if (additive_latency == (cas_latency - 2)) + al = 2; + + /* + * The esdmode value will also be used for writing + * MR1 during write leveling for DDR3, although the + * bits specifically related to the write leveling + * scheme will be handled automatically by the DDR + * controller. so we set the wrlvl_en = 0 here. + */ + esdmode = (0 + | ((qoff & 0x1) << 12) + | ((tdqs_en & 0x1) << 11) + | ((rtt & 0x4) << 7) /* rtt field is split */ + | ((wrlvl_en & 0x1) << 7) + | ((rtt & 0x2) << 5) /* rtt field is split */ + | ((dic & 0x2) << 4) /* DIC field is split */ + | ((al & 0x3) << 3) + | ((rtt & 0x1) << 2) /* rtt field is split */ + | ((dic & 0x1) << 1) /* DIC field is split */ + | ((dll_en & 0x1) << 0) + ); + + /* + * DLL control for precharge PD + * 0=slow exit DLL off (tXPDLL) + * 1=fast exit DLL on (tXP) + */ + dll_on = 1; + wr_mclk = (common_dimm->tWR_ps + mclk_ps - 1) / mclk_ps; + if (wr_mclk >= 12) + wr = 6; + else if (wr_mclk >= 9) + wr = 5; + else + wr = wr_mclk - 4; + dll_rst = 0; /* dll no reset */ + mode = 0; /* normal mode */ + + /* look up table to get the cas latency bits */ + if (cas_latency >= 5 && cas_latency <= 11) { + unsigned char cas_latency_table[7] = { + 0x2, /* 5 clocks */ + 0x4, /* 6 clocks */ + 0x6, /* 7 clocks */ + 0x8, /* 8 clocks */ + 0xa, /* 9 clocks */ + 0xc, /* 10 clocks */ + 0xe /* 11 clocks */ + }; + caslat = cas_latency_table[cas_latency - 5]; + } + bt = 0; /* Nibble sequential */ + + switch (popts->burst_length) { + case DDR_BL8: + bl = 0; + break; + case DDR_OTF: + bl = 1; + break; + case DDR_BC4: + bl = 2; + break; + default: + printf("Error: invalid burst length of %u specified. " + " Defaulting to on-the-fly BC4 or BL8 beats.\n", + popts->burst_length); + bl = 1; + break; + } + + sdmode = (0 + | ((dll_on & 0x1) << 12) + | ((wr & 0x7) << 9) + | ((dll_rst & 0x1) << 8) + | ((mode & 0x1) << 7) + | (((caslat >> 1) & 0x7) << 4) + | ((bt & 0x1) << 3) + | ((bl & 0x3) << 0) + ); + + ddr->ddr_sdram_mode = (0 + | ((esdmode & 0xFFFF) << 16) + | ((sdmode & 0xFFFF) << 0) + ); + + debug("FSLDDR: ddr_sdram_mode = 0x%08x\n", ddr->ddr_sdram_mode); +} + +#else /* !CONFIG_FSL_DDR3 */ + +/* DDR SDRAM Mode configuration set (DDR_SDRAM_MODE) */ +static void set_ddr_sdram_mode(fsl_ddr_cfg_regs_t *ddr, + const memctl_options_t *popts, + const common_timing_params_t *common_dimm, + unsigned int cas_latency, + unsigned int additive_latency) +{ + unsigned short esdmode; /* Extended SDRAM mode */ + unsigned short sdmode; /* SDRAM mode */ + + /* + * FIXME: This ought to be pre-calculated in a + * technology-specific routine, + * e.g. compute_DDR2_mode_register(), and then the + * sdmode and esdmode passed in as part of common_dimm. + */ + + /* Extended Mode Register */ + unsigned int mrs = 0; /* Mode Register Set */ + unsigned int outputs = 0; /* 0=Enabled, 1=Disabled */ + unsigned int rdqs_en = 0; /* RDQS Enable: 0=no, 1=yes */ + unsigned int dqs_en = 0; /* DQS# Enable: 0=enable, 1=disable */ + unsigned int ocd = 0; /* 0x0=OCD not supported, + 0x7=OCD default state */ + unsigned int rtt; + unsigned int al; /* Posted CAS# additive latency (AL) */ + unsigned int ods = 0; /* Output Drive Strength: + 0 = Full strength (18ohm) + 1 = Reduced strength (4ohm) */ + unsigned int dll_en = 0; /* DLL Enable 0=Enable (Normal), + 1=Disable (Test/Debug) */ + + /* Mode Register (MR) */ + unsigned int mr; /* Mode Register Definition */ + unsigned int pd; /* Power-Down Mode */ + unsigned int wr; /* Write Recovery */ + unsigned int dll_res; /* DLL Reset */ + unsigned int mode; /* Normal=0 or Test=1 */ + unsigned int caslat = 0;/* CAS# latency */ + /* BT: Burst Type (0=Sequential, 1=Interleaved) */ + unsigned int bt; + unsigned int bl; /* BL: Burst Length */ + +#if defined(CONFIG_FSL_DDR2) + const unsigned int mclk_ps = get_memory_clk_period_ps(); +#endif + + rtt = fsl_ddr_get_rtt(); + + al = additive_latency; + + esdmode = (0 + | ((mrs & 0x3) << 14) + | ((outputs & 0x1) << 12) + | ((rdqs_en & 0x1) << 11) + | ((dqs_en & 0x1) << 10) + | ((ocd & 0x7) << 7) + | ((rtt & 0x2) << 5) /* rtt field is split */ + | ((al & 0x7) << 3) + | ((rtt & 0x1) << 2) /* rtt field is split */ + | ((ods & 0x1) << 1) + | ((dll_en & 0x1) << 0) + ); + + mr = 0; /* FIXME: CHECKME */ + + /* + * 0 = Fast Exit (Normal) + * 1 = Slow Exit (Low Power) + */ + pd = 0; + +#if defined(CONFIG_FSL_DDR1) + wr = 0; /* Historical */ +#elif defined(CONFIG_FSL_DDR2) + wr = (common_dimm->tWR_ps + mclk_ps - 1) / mclk_ps - 1; +#endif + dll_res = 0; + mode = 0; + +#if defined(CONFIG_FSL_DDR1) + if (1 <= cas_latency && cas_latency <= 4) { + unsigned char mode_caslat_table[4] = { + 0x5, /* 1.5 clocks */ + 0x2, /* 2.0 clocks */ + 0x6, /* 2.5 clocks */ + 0x3 /* 3.0 clocks */ + }; + caslat = mode_caslat_table[cas_latency - 1]; + } else { + printf("Warning: unknown cas_latency %d\n", cas_latency); + } +#elif defined(CONFIG_FSL_DDR2) + caslat = cas_latency; +#endif + bt = 0; + + switch (popts->burst_length) { + case DDR_BL4: + bl = 2; + break; + case DDR_BL8: + bl = 3; + break; + default: + printf("Error: invalid burst length of %u specified. " + " Defaulting to 4 beats.\n", + popts->burst_length); + bl = 2; + break; + } + + sdmode = (0 + | ((mr & 0x3) << 14) + | ((pd & 0x1) << 12) + | ((wr & 0x7) << 9) + | ((dll_res & 0x1) << 8) + | ((mode & 0x1) << 7) + | ((caslat & 0x7) << 4) + | ((bt & 0x1) << 3) + | ((bl & 0x7) << 0) + ); + + ddr->ddr_sdram_mode = (0 + | ((esdmode & 0xFFFF) << 16) + | ((sdmode & 0xFFFF) << 0) + ); + debug("FSLDDR: ddr_sdram_mode = 0x%08x\n", ddr->ddr_sdram_mode); +} +#endif + +/* DDR SDRAM Data Initialization (DDR_DATA_INIT) */ +static void set_ddr_data_init(fsl_ddr_cfg_regs_t *ddr) +{ + unsigned int init_value; /* Initialization value */ + + init_value = 0xDEADBEEF; + ddr->ddr_data_init = init_value; +} + +/* + * DDR SDRAM Clock Control (DDR_SDRAM_CLK_CNTL) + * The old controller on the 8540/60 doesn't have this register. + * Hope it's OK to set it (to 0) anyway. + */ +static void set_ddr_sdram_clk_cntl(fsl_ddr_cfg_regs_t *ddr, + const memctl_options_t *popts) +{ + unsigned int clk_adjust; /* Clock adjust */ + + clk_adjust = popts->clk_adjust; + ddr->ddr_sdram_clk_cntl = (clk_adjust & 0xF) << 23; +} + +/* DDR Initialization Address (DDR_INIT_ADDR) */ +static void set_ddr_init_addr(fsl_ddr_cfg_regs_t *ddr) +{ + unsigned int init_addr = 0; /* Initialization address */ + + ddr->ddr_init_addr = init_addr; +} + +/* DDR Initialization Address (DDR_INIT_EXT_ADDR) */ +static void set_ddr_init_ext_addr(fsl_ddr_cfg_regs_t *ddr) +{ + unsigned int uia = 0; /* Use initialization address */ + unsigned int init_ext_addr = 0; /* Initialization address */ + + ddr->ddr_init_ext_addr = (0 + | ((uia & 0x1) << 31) + | (init_ext_addr & 0xF) + ); +} + +/* DDR SDRAM Timing Configuration 4 (TIMING_CFG_4) */ +static void set_timing_cfg_4(fsl_ddr_cfg_regs_t *ddr, + const memctl_options_t *popts) +{ + unsigned int rwt = 0; /* Read-to-write turnaround for same CS */ + unsigned int wrt = 0; /* Write-to-read turnaround for same CS */ + unsigned int rrt = 0; /* Read-to-read turnaround for same CS */ + unsigned int wwt = 0; /* Write-to-write turnaround for same CS */ + unsigned int dll_lock = 0; /* DDR SDRAM DLL Lock Time */ + +#if defined(CONFIG_FSL_DDR3) + if (popts->burst_length == DDR_BL8) { + /* We set BL/2 for fixed BL8 */ + rrt = 0; /* BL/2 clocks */ + wwt = 0; /* BL/2 clocks */ + } else { + /* We need to set BL/2 + 2 to BC4 and OTF */ + rrt = 2; /* BL/2 + 2 clocks */ + wwt = 2; /* BL/2 + 2 clocks */ + } + dll_lock = 1; /* tDLLK = 512 clocks from spec */ +#endif + ddr->timing_cfg_4 = (0 + | ((rwt & 0xf) << 28) + | ((wrt & 0xf) << 24) + | ((rrt & 0xf) << 20) + | ((wwt & 0xf) << 16) + | (dll_lock & 0x3) + ); + debug("FSLDDR: timing_cfg_4 = 0x%08x\n", ddr->timing_cfg_4); +} + +/* DDR SDRAM Timing Configuration 5 (TIMING_CFG_5) */ +static void set_timing_cfg_5(fsl_ddr_cfg_regs_t *ddr) +{ + unsigned int rodt_on = 0; /* Read to ODT on */ + unsigned int rodt_off = 0; /* Read to ODT off */ + unsigned int wodt_on = 0; /* Write to ODT on */ + unsigned int wodt_off = 0; /* Write to ODT off */ + +#if defined(CONFIG_FSL_DDR3) + rodt_on = 3; /* 2 clocks */ + rodt_off = 4; /* 4 clocks */ + wodt_on = 2; /* 1 clocks */ + wodt_off = 4; /* 4 clocks */ +#endif + + ddr->timing_cfg_5 = (0 + | ((rodt_on & 0x1f) << 24) + | ((rodt_off & 0x7) << 20) + | ((wodt_on & 0x1f) << 12) + | ((wodt_off & 0x7) << 8) + ); + debug("FSLDDR: timing_cfg_5 = 0x%08x\n", ddr->timing_cfg_5); +} + +/* DDR ZQ Calibration Control (DDR_ZQ_CNTL) */ +static void set_ddr_zq_cntl(fsl_ddr_cfg_regs_t *ddr, unsigned int zq_en) +{ + unsigned int zqinit = 0;/* POR ZQ Calibration Time (tZQinit) */ + /* Normal Operation Full Calibration Time (tZQoper) */ + unsigned int zqoper = 0; + /* Normal Operation Short Calibration Time (tZQCS) */ + unsigned int zqcs = 0; + + if (zq_en) { + zqinit = 9; /* 512 clocks */ + zqoper = 8; /* 256 clocks */ + zqcs = 6; /* 64 clocks */ + } + + ddr->ddr_zq_cntl = (0 + | ((zq_en & 0x1) << 31) + | ((zqinit & 0xF) << 24) + | ((zqoper & 0xF) << 16) + | ((zqcs & 0xF) << 8) + ); +} + +/* DDR Write Leveling Control (DDR_WRLVL_CNTL) */ +static void set_ddr_wrlvl_cntl(fsl_ddr_cfg_regs_t *ddr, unsigned int wrlvl_en, + const memctl_options_t *popts) +{ + /* + * First DQS pulse rising edge after margining mode + * is programmed (tWL_MRD) + */ + unsigned int wrlvl_mrd = 0; + /* ODT delay after margining mode is programmed (tWL_ODTEN) */ + unsigned int wrlvl_odten = 0; + /* DQS/DQS_ delay after margining mode is programmed (tWL_DQSEN) */ + unsigned int wrlvl_dqsen = 0; + /* WRLVL_SMPL: Write leveling sample time */ + unsigned int wrlvl_smpl = 0; + /* WRLVL_WLR: Write leveling repeition time */ + unsigned int wrlvl_wlr = 0; + /* WRLVL_START: Write leveling start time */ + unsigned int wrlvl_start = 0; + + /* suggest enable write leveling for DDR3 due to fly-by topology */ + if (wrlvl_en) { + /* tWL_MRD min = 40 nCK, we set it 64 */ + wrlvl_mrd = 0x6; + /* tWL_ODTEN 128 */ + wrlvl_odten = 0x7; + /* tWL_DQSEN min = 25 nCK, we set it 32 */ + wrlvl_dqsen = 0x5; + /* + * Write leveling sample time at least need 6 clocks + * higher than tWLO to allow enough time for progagation + * delay and sampling the prime data bits. + */ + wrlvl_smpl = 0xf; + /* + * Write leveling repetition time + * at least tWLO + 6 clocks clocks + * we set it 32 + */ + wrlvl_wlr = 0x5; + /* + * Write leveling start time + * The value use for the DQS_ADJUST for the first sample + * when write leveling is enabled. + */ + wrlvl_start = 0x8; + /* + * Override the write leveling sample and start time + * according to specific board + */ + if (popts->wrlvl_override) { + wrlvl_smpl = popts->wrlvl_sample; + wrlvl_start = popts->wrlvl_start; + } + } + + ddr->ddr_wrlvl_cntl = (0 + | ((wrlvl_en & 0x1) << 31) + | ((wrlvl_mrd & 0x7) << 24) + | ((wrlvl_odten & 0x7) << 20) + | ((wrlvl_dqsen & 0x7) << 16) + | ((wrlvl_smpl & 0xf) << 12) + | ((wrlvl_wlr & 0x7) << 8) + | ((wrlvl_start & 0x1F) << 0) + ); +} + +/* DDR Self Refresh Counter (DDR_SR_CNTR) */ +static void set_ddr_sr_cntr(fsl_ddr_cfg_regs_t *ddr, unsigned int sr_it) +{ + /* Self Refresh Idle Threshold */ + ddr->ddr_sr_cntr = (sr_it & 0xF) << 16; +} + +/* DDR SDRAM Register Control Word 1 (DDR_SDRAM_RCW_1) */ +static void set_ddr_sdram_rcw_1(fsl_ddr_cfg_regs_t *ddr) +{ + unsigned int rcw0 = 0; /* RCW0: Register Control Word 0 */ + unsigned int rcw1 = 0; /* RCW1: Register Control Word 1 */ + unsigned int rcw2 = 0; /* RCW2: Register Control Word 2 */ + unsigned int rcw3 = 0; /* RCW3: Register Control Word 3 */ + unsigned int rcw4 = 0; /* RCW4: Register Control Word 4 */ + unsigned int rcw5 = 0; /* RCW5: Register Control Word 5 */ + unsigned int rcw6 = 0; /* RCW6: Register Control Word 6 */ + unsigned int rcw7 = 0; /* RCW7: Register Control Word 7 */ + + ddr->ddr_sdram_rcw_1 = (0 + | ((rcw0 & 0xF) << 28) + | ((rcw1 & 0xF) << 24) + | ((rcw2 & 0xF) << 20) + | ((rcw3 & 0xF) << 16) + | ((rcw4 & 0xF) << 12) + | ((rcw5 & 0xF) << 8) + | ((rcw6 & 0xF) << 4) + | ((rcw7 & 0xF) << 0) + ); +} + +/* DDR SDRAM Register Control Word 2 (DDR_SDRAM_RCW_2) */ +static void set_ddr_sdram_rcw_2(fsl_ddr_cfg_regs_t *ddr) +{ + unsigned int rcw8 = 0; /* RCW0: Register Control Word 8 */ + unsigned int rcw9 = 0; /* RCW1: Register Control Word 9 */ + unsigned int rcw10 = 0; /* RCW2: Register Control Word 10 */ + unsigned int rcw11 = 0; /* RCW3: Register Control Word 11 */ + unsigned int rcw12 = 0; /* RCW4: Register Control Word 12 */ + unsigned int rcw13 = 0; /* RCW5: Register Control Word 13 */ + unsigned int rcw14 = 0; /* RCW6: Register Control Word 14 */ + unsigned int rcw15 = 0; /* RCW7: Register Control Word 15 */ + + ddr->ddr_sdram_rcw_2 = (0 + | ((rcw8 & 0xF) << 28) + | ((rcw9 & 0xF) << 24) + | ((rcw10 & 0xF) << 20) + | ((rcw11 & 0xF) << 16) + | ((rcw12 & 0xF) << 12) + | ((rcw13 & 0xF) << 8) + | ((rcw14 & 0xF) << 4) + | ((rcw15 & 0xF) << 0) + ); +} + +unsigned int +check_fsl_memctl_config_regs(const fsl_ddr_cfg_regs_t *ddr) +{ + unsigned int res = 0; + + /* + * Check that DDR_SDRAM_CFG[RD_EN] and DDR_SDRAM_CFG[2T_EN] are + * not set at the same time. + */ + if (ddr->ddr_sdram_cfg & 0x10000000 + && ddr->ddr_sdram_cfg & 0x00008000) { + printf("Error: DDR_SDRAM_CFG[RD_EN] and DDR_SDRAM_CFG[2T_EN] " + " should not be set at the same time.\n"); + res++; + } + + return res; +} + +unsigned int +compute_fsl_memctl_config_regs(const memctl_options_t *popts, + fsl_ddr_cfg_regs_t *ddr, + const common_timing_params_t *common_dimm, + const dimm_params_t *dimm_params, + unsigned int dbw_cap_adj) +{ + unsigned int i; + unsigned int cas_latency; + unsigned int additive_latency; + unsigned int sr_it; + unsigned int zq_en; + unsigned int wrlvl_en; + + memset(ddr, 0, sizeof(fsl_ddr_cfg_regs_t)); + + if (common_dimm == NULL) { + printf("Error: subset DIMM params struct null pointer\n"); + return 1; + } + + /* + * Process overrides first. + * + * FIXME: somehow add dereated caslat to this + */ + cas_latency = (popts->cas_latency_override) + ? popts->cas_latency_override_value + : common_dimm->lowest_common_SPD_caslat; + + additive_latency = (popts->additive_latency_override) + ? popts->additive_latency_override_value + : common_dimm->additive_latency; + + sr_it = (popts->auto_self_refresh_en) + ? popts->sr_it + : 0; + /* ZQ calibration */ + zq_en = (popts->zq_en) ? 1 : 0; + /* write leveling */ + wrlvl_en = (popts->wrlvl_en) ? 1 : 0; + + /* Chip Select Memory Bounds (CSn_BNDS) */ + for (i = 0; i < CONFIG_CHIP_SELECTS_PER_CTRL; i++) { + unsigned long long ea = 0, sa = 0; + + if (popts->ba_intlv_ctl && (i > 0) && + ((popts->ba_intlv_ctl & 0x60) != FSL_DDR_CS2_CS3 )) { + /* Don't set up boundaries for other CS + * other than CS0, if bank interleaving + * is enabled and not CS2+CS3 interleaved. + * But we need to set the ODT_RD_CFG and + * ODT_WR_CFG for CS1_CONFIG here. + */ + set_csn_config(i, ddr, popts, dimm_params); + break; + } + + if (dimm_params[i/2].n_ranks == 0) { + debug("Skipping setup of CS%u " + "because n_ranks on DIMM %u is 0\n", i, i/2); + continue; + } + if (popts->memctl_interleaving && popts->ba_intlv_ctl) { + /* + * This works superbank 2CS + * There are 2 memory controllers configured + * identically, memory is interleaved between them, + * and each controller uses rank interleaving within + * itself. Therefore the starting and ending address + * on each controller is twice the amount present on + * each controller. + */ + unsigned long long rank_density + = dimm_params[0].capacity; + ea = (2 * (rank_density >> dbw_cap_adj)) - 1; + } + else if (!popts->memctl_interleaving && popts->ba_intlv_ctl) { + /* + * If memory interleaving between controllers is NOT + * enabled, the starting address for each memory + * controller is distinct. However, because rank + * interleaving is enabled, the starting and ending + * addresses of the total memory on that memory + * controller needs to be programmed into its + * respective CS0_BNDS. + */ + unsigned long long rank_density + = dimm_params[i/2].rank_density; + switch (popts->ba_intlv_ctl & FSL_DDR_CS0_CS1_CS2_CS3) { + case FSL_DDR_CS0_CS1_CS2_CS3: + /* CS0+CS1+CS2+CS3 interleaving, only CS0_CNDS + * needs to be set. + */ + sa = common_dimm->base_address; + ea = sa + (4 * (rank_density >> dbw_cap_adj))-1; + break; + case FSL_DDR_CS0_CS1_AND_CS2_CS3: + /* CS0+CS1 and CS2+CS3 interleaving, CS0_CNDS + * and CS2_CNDS need to be set. + */ + if (!(i&1)) { + sa = dimm_params[i/2].base_address; + ea = sa + (i * (rank_density >> + dbw_cap_adj)) - 1; + } + break; + case FSL_DDR_CS0_CS1: + /* CS0+CS1 interleaving, CS0_CNDS needs + * to be set + */ + sa = common_dimm->base_address; + ea = sa + (2 * (rank_density >> dbw_cap_adj))-1; + break; + case FSL_DDR_CS2_CS3: + /* CS2+CS3 interleaving*/ + if (i == 2) { + sa = dimm_params[i/2].base_address; + ea = sa + (2 * (rank_density >> + dbw_cap_adj)) - 1; + } + break; + default: /* No bank(chip-select) interleaving */ + break; + } + } + else if (popts->memctl_interleaving && !popts->ba_intlv_ctl) { + /* + * Only the rank on CS0 of each memory controller may + * be used if memory controller interleaving is used + * without rank interleaving within each memory + * controller. However, the ending address programmed + * into each CS0 must be the sum of the amount of + * memory in the two CS0 ranks. + */ + if (i == 0) { + unsigned long long rank_density + = dimm_params[0].rank_density; + ea = (2 * (rank_density >> dbw_cap_adj)) - 1; + } + + } + else if (!popts->memctl_interleaving && !popts->ba_intlv_ctl) { + /* + * No rank interleaving and no memory controller + * interleaving. + */ + unsigned long long rank_density + = dimm_params[i/2].rank_density; + sa = dimm_params[i/2].base_address; + ea = sa + (rank_density >> dbw_cap_adj) - 1; + if (i&1) { + if ((dimm_params[i/2].n_ranks == 1)) { + /* Odd chip select, single-rank dimm */ + sa = 0; + ea = 0; + } else { + /* Odd chip select, dual-rank DIMM */ + sa += rank_density >> dbw_cap_adj; + ea += rank_density >> dbw_cap_adj; + } + } + } + + sa >>= 24; + ea >>= 24; + + ddr->cs[i].bnds = (0 + | ((sa & 0xFFF) << 16) /* starting address MSB */ + | ((ea & 0xFFF) << 0) /* ending address MSB */ + ); + + debug("FSLDDR: cs[%d]_bnds = 0x%08x\n", i, ddr->cs[i].bnds); + set_csn_config(i, ddr, popts, dimm_params); + set_csn_config_2(i, ddr); + } + +#if !defined(CONFIG_FSL_DDR1) + set_timing_cfg_0(ddr); +#endif + + set_timing_cfg_3(ddr, common_dimm, cas_latency); + set_timing_cfg_1(ddr, popts, common_dimm, cas_latency); + set_timing_cfg_2(ddr, popts, common_dimm, + cas_latency, additive_latency); + + set_ddr_sdram_cfg(ddr, popts, common_dimm); + + set_ddr_sdram_cfg_2(ddr, popts); + set_ddr_sdram_mode(ddr, popts, common_dimm, + cas_latency, additive_latency); + set_ddr_sdram_mode_2(ddr, popts); + set_ddr_sdram_interval(ddr, popts, common_dimm); + set_ddr_data_init(ddr); + set_ddr_sdram_clk_cntl(ddr, popts); + set_ddr_init_addr(ddr); + set_ddr_init_ext_addr(ddr); + set_timing_cfg_4(ddr, popts); + set_timing_cfg_5(ddr); + + set_ddr_zq_cntl(ddr, zq_en); + set_ddr_wrlvl_cntl(ddr, wrlvl_en, popts); + + set_ddr_sr_cntr(ddr, sr_it); + + set_ddr_sdram_rcw_1(ddr); + set_ddr_sdram_rcw_2(ddr); + + return check_fsl_memctl_config_regs(ddr); +} diff --git a/arch/powerpc/cpu/mpc8xxx/ddr/ddr.h b/arch/powerpc/cpu/mpc8xxx/ddr/ddr.h new file mode 100644 index 0000000..f122075 --- /dev/null +++ b/arch/powerpc/cpu/mpc8xxx/ddr/ddr.h @@ -0,0 +1,81 @@ +/* + * Copyright 2008 Freescale Semiconductor, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * Version 2 as published by the Free Software Foundation. + */ + +#ifndef FSL_DDR_MAIN_H +#define FSL_DDR_MAIN_H + +#include <asm/fsl_ddr_sdram.h> +#include <asm/fsl_ddr_dimm_params.h> + +#include "common_timing_params.h" + +/* + * Bind the main DDR setup driver's generic names + * to this specific DDR technology. + */ +static __inline__ int +compute_dimm_parameters(const generic_spd_eeprom_t *spd, + dimm_params_t *pdimm, + unsigned int dimm_number) +{ + return ddr_compute_dimm_parameters(spd, pdimm, dimm_number); +} + +/* + * Data Structures + * + * All data structures have to be on the stack + */ +#define CONFIG_SYS_NUM_DDR_CTLRS CONFIG_NUM_DDR_CONTROLLERS +#define CONFIG_SYS_DIMM_SLOTS_PER_CTLR CONFIG_DIMM_SLOTS_PER_CTLR + +typedef struct { + generic_spd_eeprom_t + spd_installed_dimms[CONFIG_SYS_NUM_DDR_CTLRS][CONFIG_SYS_DIMM_SLOTS_PER_CTLR]; + struct dimm_params_s + dimm_params[CONFIG_SYS_NUM_DDR_CTLRS][CONFIG_SYS_DIMM_SLOTS_PER_CTLR]; + memctl_options_t memctl_opts[CONFIG_SYS_NUM_DDR_CTLRS]; + common_timing_params_t common_timing_params[CONFIG_SYS_NUM_DDR_CTLRS]; + fsl_ddr_cfg_regs_t fsl_ddr_config_reg[CONFIG_SYS_NUM_DDR_CTLRS]; +} fsl_ddr_info_t; + +/* Compute steps */ +#define STEP_GET_SPD (1 << 0) +#define STEP_COMPUTE_DIMM_PARMS (1 << 1) +#define STEP_COMPUTE_COMMON_PARMS (1 << 2) +#define STEP_GATHER_OPTS (1 << 3) +#define STEP_ASSIGN_ADDRESSES (1 << 4) +#define STEP_COMPUTE_REGS (1 << 5) +#define STEP_PROGRAM_REGS (1 << 6) +#define STEP_ALL 0xFFF + +extern unsigned long long +fsl_ddr_compute(fsl_ddr_info_t *pinfo, unsigned int start_step); + +extern const char * step_to_string(unsigned int step); + +extern unsigned int +compute_fsl_memctl_config_regs(const memctl_options_t *popts, + fsl_ddr_cfg_regs_t *ddr, + const common_timing_params_t *common_dimm, + const dimm_params_t *dimm_parameters, + unsigned int dbw_capacity_adjust); +extern unsigned int +compute_lowest_common_dimm_parameters(const dimm_params_t *dimm_params, + common_timing_params_t *outpdimm, + unsigned int number_of_dimms); +extern unsigned int populate_memctl_options(int all_DIMMs_registered, + memctl_options_t *popts, + dimm_params_t *pdimm, + unsigned int ctrl_num); + +extern unsigned int mclk_to_picos(unsigned int mclk); +extern unsigned int get_memory_clk_period_ps(void); +extern unsigned int picos_to_mclk(unsigned int picos); + +#endif diff --git a/arch/powerpc/cpu/mpc8xxx/ddr/ddr1_dimm_params.c b/arch/powerpc/cpu/mpc8xxx/ddr/ddr1_dimm_params.c new file mode 100644 index 0000000..9184764 --- /dev/null +++ b/arch/powerpc/cpu/mpc8xxx/ddr/ddr1_dimm_params.c @@ -0,0 +1,343 @@ +/* + * Copyright 2008 Freescale Semiconductor, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * Version 2 as published by the Free Software Foundation. + */ + +#include <common.h> +#include <asm/fsl_ddr_sdram.h> + +#include "ddr.h" + +/* + * Calculate the Density of each Physical Rank. + * Returned size is in bytes. + * + * Study these table from Byte 31 of JEDEC SPD Spec. + * + * DDR I DDR II + * Bit Size Size + * --- ----- ------ + * 7 high 512MB 512MB + * 6 256MB 256MB + * 5 128MB 128MB + * 4 64MB 16GB + * 3 32MB 8GB + * 2 16MB 4GB + * 1 2GB 2GB + * 0 low 1GB 1GB + * + * Reorder Table to be linear by stripping the bottom + * 2 or 5 bits off and shifting them up to the top. + */ + +static unsigned long long +compute_ranksize(unsigned int mem_type, unsigned char row_dens) +{ + unsigned long long bsize; + + /* Bottom 2 bits up to the top. */ + bsize = ((row_dens >> 2) | ((row_dens & 3) << 6)); + bsize <<= 24ULL; + debug("DDR: DDR I rank density = 0x%08x\n", bsize); + + return bsize; +} + +/* + * Convert a two-nibble BCD value into a cycle time. + * While the spec calls for nano-seconds, picos are returned. + * + * This implements the tables for bytes 9, 23 and 25 for both + * DDR I and II. No allowance for distinguishing the invalid + * fields absent for DDR I yet present in DDR II is made. + * (That is, cycle times of .25, .33, .66 and .75 ns are + * allowed for both DDR II and I.) + */ +static unsigned int +convert_bcd_tenths_to_cycle_time_ps(unsigned int spd_val) +{ + /* Table look up the lower nibble, allow DDR I & II. */ + unsigned int tenths_ps[16] = { + 0, + 100, + 200, + 300, + 400, + 500, + 600, + 700, + 800, + 900, + 250, /* This and the next 3 entries valid ... */ + 330, /* ... only for tCK calculations. */ + 660, + 750, + 0, /* undefined */ + 0 /* undefined */ + }; + + unsigned int whole_ns = (spd_val & 0xF0) >> 4; + unsigned int tenth_ns = spd_val & 0x0F; + unsigned int ps = whole_ns * 1000 + tenths_ps[tenth_ns]; + + return ps; +} + +static unsigned int +convert_bcd_hundredths_to_cycle_time_ps(unsigned int spd_val) +{ + unsigned int tenth_ns = (spd_val & 0xF0) >> 4; + unsigned int hundredth_ns = spd_val & 0x0F; + unsigned int ps = tenth_ns * 100 + hundredth_ns * 10; + + return ps; +} + +static unsigned int byte40_table_ps[8] = { + 0, + 250, + 330, + 500, + 660, + 750, + 0, /* supposed to be RFC, but not sure what that means */ + 0 /* Undefined */ +}; + +static unsigned int +compute_trfc_ps_from_spd(unsigned char trctrfc_ext, unsigned char trfc) +{ + unsigned int trfc_ps; + + trfc_ps = (((trctrfc_ext & 0x1) * 256) + trfc) * 1000 + + byte40_table_ps[(trctrfc_ext >> 1) & 0x7]; + + return trfc_ps; +} + +static unsigned int +compute_trc_ps_from_spd(unsigned char trctrfc_ext, unsigned char trc) +{ + unsigned int trc_ps; + + trc_ps = trc * 1000 + byte40_table_ps[(trctrfc_ext >> 4) & 0x7]; + + return trc_ps; +} + +/* + * tCKmax from DDR I SPD Byte 43 + * + * Bits 7:2 == whole ns + * Bits 1:0 == quarter ns + * 00 == 0.00 ns + * 01 == 0.25 ns + * 10 == 0.50 ns + * 11 == 0.75 ns + * + * Returns picoseconds. + */ +static unsigned int +compute_tckmax_from_spd_ps(unsigned int byte43) +{ + return (byte43 >> 2) * 1000 + (byte43 & 0x3) * 250; +} + +/* + * Determine Refresh Rate. Ignore self refresh bit on DDR I. + * Table from SPD Spec, Byte 12, converted to picoseconds and + * filled in with "default" normal values. + */ +static unsigned int +determine_refresh_rate_ps(const unsigned int spd_refresh) +{ + unsigned int refresh_time_ps[8] = { + 15625000, /* 0 Normal 1.00x */ + 3900000, /* 1 Reduced .25x */ + 7800000, /* 2 Extended .50x */ + 31300000, /* 3 Extended 2.00x */ + 62500000, /* 4 Extended 4.00x */ + 125000000, /* 5 Extended 8.00x */ + 15625000, /* 6 Normal 1.00x filler */ + 15625000, /* 7 Normal 1.00x filler */ + }; + + return refresh_time_ps[spd_refresh & 0x7]; +} + +/* + * The purpose of this function is to compute a suitable + * CAS latency given the DRAM clock period. The SPD only + * defines at most 3 CAS latencies. Typically the slower in + * frequency the DIMM runs at, the shorter its CAS latency can be. + * If the DIMM is operating at a sufficiently low frequency, + * it may be able to run at a CAS latency shorter than the + * shortest SPD-defined CAS latency. + * + * If a CAS latency is not found, 0 is returned. + * + * Do this by finding in the standard speed bin table the longest + * tCKmin that doesn't exceed the value of mclk_ps (tCK). + * + * An assumption made is that the SDRAM device allows the + * CL to be programmed for a value that is lower than those + * advertised by the SPD. This is not always the case, + * as those modes not defined in the SPD are optional. + * + * CAS latency de-rating based upon values JEDEC Standard No. 79-E + * Table 11. + * + * ordinal 2, ddr1_speed_bins[1] contains tCK for CL=2 + */ + /* CL2.0 CL2.5 CL3.0 */ +unsigned short ddr1_speed_bins[] = {0, 7500, 6000, 5000 }; + +unsigned int +compute_derated_DDR1_CAS_latency(unsigned int mclk_ps) +{ + const unsigned int num_speed_bins = ARRAY_SIZE(ddr1_speed_bins); + unsigned int lowest_tCKmin_found = 0; + unsigned int lowest_tCKmin_CL = 0; + unsigned int i; + + debug("mclk_ps = %u\n", mclk_ps); + + for (i = 0; i < num_speed_bins; i++) { + unsigned int x = ddr1_speed_bins[i]; + debug("i=%u, x = %u, lowest_tCKmin_found = %u\n", + i, x, lowest_tCKmin_found); + if (x && lowest_tCKmin_found <= x && x <= mclk_ps) { + lowest_tCKmin_found = x; + lowest_tCKmin_CL = i + 1; + } + } + + debug("lowest_tCKmin_CL = %u\n", lowest_tCKmin_CL); + + return lowest_tCKmin_CL; +} + +/* + * ddr_compute_dimm_parameters for DDR1 SPD + * + * Compute DIMM parameters based upon the SPD information in spd. + * Writes the results to the dimm_params_t structure pointed by pdimm. + * + * FIXME: use #define for the retvals + */ +unsigned int +ddr_compute_dimm_parameters(const ddr1_spd_eeprom_t *spd, + dimm_params_t *pdimm, + unsigned int dimm_number) +{ + unsigned int retval; + + if (spd->mem_type) { + if (spd->mem_type != SPD_MEMTYPE_DDR) { + printf("DIMM %u: is not a DDR1 SPD.\n", dimm_number); + return 1; + } + } else { + memset(pdimm, 0, sizeof(dimm_params_t)); + return 1; + } + + retval = ddr1_spd_check(spd); + if (retval) { + printf("DIMM %u: failed checksum\n", dimm_number); + return 2; + } + + /* + * The part name in ASCII in the SPD EEPROM is not null terminated. + * Guarantee null termination here by presetting all bytes to 0 + * and copying the part name in ASCII from the SPD onto it + */ + memset(pdimm->mpart, 0, sizeof(pdimm->mpart)); + memcpy(pdimm->mpart, spd->mpart, sizeof(pdimm->mpart) - 1); + + /* DIMM organization parameters */ + pdimm->n_ranks = spd->nrows; + pdimm->rank_density = compute_ranksize(spd->mem_type, spd->bank_dens); + pdimm->capacity = pdimm->n_ranks * pdimm->rank_density; + pdimm->data_width = spd->dataw_lsb; + pdimm->primary_sdram_width = spd->primw; + pdimm->ec_sdram_width = spd->ecw; + + /* + * FIXME: Need to determine registered_dimm status. + * 1 == register buffered + * 0 == unbuffered + */ + pdimm->registered_dimm = 0; /* unbuffered */ + + /* SDRAM device parameters */ + pdimm->n_row_addr = spd->nrow_addr; + pdimm->n_col_addr = spd->ncol_addr; + pdimm->n_banks_per_sdram_device = spd->nbanks; + pdimm->edc_config = spd->config; + pdimm->burst_lengths_bitmask = spd->burstl; + pdimm->row_density = spd->bank_dens; + + /* + * Calculate the Maximum Data Rate based on the Minimum Cycle time. + * The SPD clk_cycle field (tCKmin) is measured in tenths of + * nanoseconds and represented as BCD. + */ + pdimm->tCKmin_X_ps + = convert_bcd_tenths_to_cycle_time_ps(spd->clk_cycle); + pdimm->tCKmin_X_minus_1_ps + = convert_bcd_tenths_to_cycle_time_ps(spd->clk_cycle2); + pdimm->tCKmin_X_minus_2_ps + = convert_bcd_tenths_to_cycle_time_ps(spd->clk_cycle3); + + pdimm->tCKmax_ps = compute_tckmax_from_spd_ps(spd->tckmax); + + /* + * Compute CAS latencies defined by SPD + * The SPD caslat_X should have at least 1 and at most 3 bits set. + * + * If cas_lat after masking is 0, the __ilog2 function returns + * 255 into the variable. This behavior is abused once. + */ + pdimm->caslat_X = __ilog2(spd->cas_lat); + pdimm->caslat_X_minus_1 = __ilog2(spd->cas_lat + & ~(1 << pdimm->caslat_X)); + pdimm->caslat_X_minus_2 = __ilog2(spd->cas_lat + & ~(1 << pdimm->caslat_X) + & ~(1 << pdimm->caslat_X_minus_1)); + + /* Compute CAS latencies below that defined by SPD */ + pdimm->caslat_lowest_derated + = compute_derated_DDR1_CAS_latency(get_memory_clk_period_ps()); + + /* Compute timing parameters */ + pdimm->tRCD_ps = spd->trcd * 250; + pdimm->tRP_ps = spd->trp * 250; + pdimm->tRAS_ps = spd->tras * 1000; + + pdimm->tWR_ps = mclk_to_picos(3); + pdimm->tWTR_ps = mclk_to_picos(1); + pdimm->tRFC_ps = compute_trfc_ps_from_spd(0, spd->trfc); + + pdimm->tRRD_ps = spd->trrd * 250; + pdimm->tRC_ps = compute_trc_ps_from_spd(0, spd->trc); + + pdimm->refresh_rate_ps = determine_refresh_rate_ps(spd->refresh); + + pdimm->tIS_ps = convert_bcd_hundredths_to_cycle_time_ps(spd->ca_setup); + pdimm->tIH_ps = convert_bcd_hundredths_to_cycle_time_ps(spd->ca_hold); + pdimm->tDS_ps + = convert_bcd_hundredths_to_cycle_time_ps(spd->data_setup); + pdimm->tDH_ps + = convert_bcd_hundredths_to_cycle_time_ps(spd->data_hold); + + pdimm->tRTP_ps = mclk_to_picos(2); /* By the book. */ + pdimm->tDQSQ_max_ps = spd->tdqsq * 10; + pdimm->tQHS_ps = spd->tqhs * 10; + + return 0; +} diff --git a/arch/powerpc/cpu/mpc8xxx/ddr/ddr2_dimm_params.c b/arch/powerpc/cpu/mpc8xxx/ddr/ddr2_dimm_params.c new file mode 100644 index 0000000..d9d0fa7 --- /dev/null +++ b/arch/powerpc/cpu/mpc8xxx/ddr/ddr2_dimm_params.c @@ -0,0 +1,339 @@ +/* + * Copyright 2008 Freescale Semiconductor, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * Version 2 as published by the Free Software Foundation. + */ + +#include <common.h> +#include <asm/fsl_ddr_sdram.h> + +#include "ddr.h" +/* + * Calculate the Density of each Physical Rank. + * Returned size is in bytes. + * + * Study these table from Byte 31 of JEDEC SPD Spec. + * + * DDR I DDR II + * Bit Size Size + * --- ----- ------ + * 7 high 512MB 512MB + * 6 256MB 256MB + * 5 128MB 128MB + * 4 64MB 16GB + * 3 32MB 8GB + * 2 16MB 4GB + * 1 2GB 2GB + * 0 low 1GB 1GB + * + * Reorder Table to be linear by stripping the bottom + * 2 or 5 bits off and shifting them up to the top. + * + */ +static unsigned long long +compute_ranksize(unsigned int mem_type, unsigned char row_dens) +{ + unsigned long long bsize; + + /* Bottom 5 bits up to the top. */ + bsize = ((row_dens >> 5) | ((row_dens & 31) << 3)); + bsize <<= 27ULL; + debug("DDR: DDR II rank density = 0x%08x\n", bsize); + + return bsize; +} + +/* + * Convert a two-nibble BCD value into a cycle time. + * While the spec calls for nano-seconds, picos are returned. + * + * This implements the tables for bytes 9, 23 and 25 for both + * DDR I and II. No allowance for distinguishing the invalid + * fields absent for DDR I yet present in DDR II is made. + * (That is, cycle times of .25, .33, .66 and .75 ns are + * allowed for both DDR II and I.) + */ +static unsigned int +convert_bcd_tenths_to_cycle_time_ps(unsigned int spd_val) +{ + /* Table look up the lower nibble, allow DDR I & II. */ + unsigned int tenths_ps[16] = { + 0, + 100, + 200, + 300, + 400, + 500, + 600, + 700, + 800, + 900, + 250, /* This and the next 3 entries valid ... */ + 330, /* ... only for tCK calculations. */ + 660, + 750, + 0, /* undefined */ + 0 /* undefined */ + }; + + unsigned int whole_ns = (spd_val & 0xF0) >> 4; + unsigned int tenth_ns = spd_val & 0x0F; + unsigned int ps = whole_ns * 1000 + tenths_ps[tenth_ns]; + + return ps; +} + +static unsigned int +convert_bcd_hundredths_to_cycle_time_ps(unsigned int spd_val) +{ + unsigned int tenth_ns = (spd_val & 0xF0) >> 4; + unsigned int hundredth_ns = spd_val & 0x0F; + unsigned int ps = tenth_ns * 100 + hundredth_ns * 10; + + return ps; +} + +static unsigned int byte40_table_ps[8] = { + 0, + 250, + 330, + 500, + 660, + 750, + 0, /* supposed to be RFC, but not sure what that means */ + 0 /* Undefined */ +}; + +static unsigned int +compute_trfc_ps_from_spd(unsigned char trctrfc_ext, unsigned char trfc) +{ + unsigned int trfc_ps; + + trfc_ps = (((trctrfc_ext & 0x1) * 256) + trfc) * 1000 + + byte40_table_ps[(trctrfc_ext >> 1) & 0x7]; + + return trfc_ps; +} + +static unsigned int +compute_trc_ps_from_spd(unsigned char trctrfc_ext, unsigned char trc) +{ + unsigned int trc_ps; + + trc_ps = trc * 1000 + byte40_table_ps[(trctrfc_ext >> 4) & 0x7]; + + return trc_ps; +} + +/* + * Determine Refresh Rate. Ignore self refresh bit on DDR I. + * Table from SPD Spec, Byte 12, converted to picoseconds and + * filled in with "default" normal values. + */ +static unsigned int +determine_refresh_rate_ps(const unsigned int spd_refresh) +{ + unsigned int refresh_time_ps[8] = { + 15625000, /* 0 Normal 1.00x */ + 3900000, /* 1 Reduced .25x */ + 7800000, /* 2 Extended .50x */ + 31300000, /* 3 Extended 2.00x */ + 62500000, /* 4 Extended 4.00x */ + 125000000, /* 5 Extended 8.00x */ + 15625000, /* 6 Normal 1.00x filler */ + 15625000, /* 7 Normal 1.00x filler */ + }; + + return refresh_time_ps[spd_refresh & 0x7]; +} + +/* + * The purpose of this function is to compute a suitable + * CAS latency given the DRAM clock period. The SPD only + * defines at most 3 CAS latencies. Typically the slower in + * frequency the DIMM runs at, the shorter its CAS latency can. + * be. If the DIMM is operating at a sufficiently low frequency, + * it may be able to run at a CAS latency shorter than the + * shortest SPD-defined CAS latency. + * + * If a CAS latency is not found, 0 is returned. + * + * Do this by finding in the standard speed bin table the longest + * tCKmin that doesn't exceed the value of mclk_ps (tCK). + * + * An assumption made is that the SDRAM device allows the + * CL to be programmed for a value that is lower than those + * advertised by the SPD. This is not always the case, + * as those modes not defined in the SPD are optional. + * + * CAS latency de-rating based upon values JEDEC Standard No. 79-2C + * Table 40, "DDR2 SDRAM stanadard speed bins and tCK, tRCD, tRP, tRAS, + * and tRC for corresponding bin" + * + * ordinal 2, ddr2_speed_bins[1] contains tCK for CL=3 + * Not certain if any good value exists for CL=2 + */ + /* CL2 CL3 CL4 CL5 CL6 */ +unsigned short ddr2_speed_bins[] = { 0, 5000, 3750, 3000, 2500 }; + +unsigned int +compute_derated_DDR2_CAS_latency(unsigned int mclk_ps) +{ + const unsigned int num_speed_bins = ARRAY_SIZE(ddr2_speed_bins); + unsigned int lowest_tCKmin_found = 0; + unsigned int lowest_tCKmin_CL = 0; + unsigned int i; + + debug("mclk_ps = %u\n", mclk_ps); + + for (i = 0; i < num_speed_bins; i++) { + unsigned int x = ddr2_speed_bins[i]; + debug("i=%u, x = %u, lowest_tCKmin_found = %u\n", + i, x, lowest_tCKmin_found); + if (x && x <= mclk_ps && x >= lowest_tCKmin_found ) { + lowest_tCKmin_found = x; + lowest_tCKmin_CL = i + 2; + } + } + + debug("lowest_tCKmin_CL = %u\n", lowest_tCKmin_CL); + + return lowest_tCKmin_CL; +} + +/* + * ddr_compute_dimm_parameters for DDR2 SPD + * + * Compute DIMM parameters based upon the SPD information in spd. + * Writes the results to the dimm_params_t structure pointed by pdimm. + * + * FIXME: use #define for the retvals + */ +unsigned int +ddr_compute_dimm_parameters(const ddr2_spd_eeprom_t *spd, + dimm_params_t *pdimm, + unsigned int dimm_number) +{ + unsigned int retval; + + if (spd->mem_type) { + if (spd->mem_type != SPD_MEMTYPE_DDR2) { + printf("DIMM %u: is not a DDR2 SPD.\n", dimm_number); + return 1; + } + } else { + memset(pdimm, 0, sizeof(dimm_params_t)); + return 1; + } + + retval = ddr2_spd_check(spd); + if (retval) { + printf("DIMM %u: failed checksum\n", dimm_number); + return 2; + } + + /* + * The part name in ASCII in the SPD EEPROM is not null terminated. + * Guarantee null termination here by presetting all bytes to 0 + * and copying the part name in ASCII from the SPD onto it + */ + memset(pdimm->mpart, 0, sizeof(pdimm->mpart)); + memcpy(pdimm->mpart, spd->mpart, sizeof(pdimm->mpart) - 1); + + /* DIMM organization parameters */ + pdimm->n_ranks = (spd->mod_ranks & 0x7) + 1; + pdimm->rank_density = compute_ranksize(spd->mem_type, spd->rank_dens); + pdimm->capacity = pdimm->n_ranks * pdimm->rank_density; + pdimm->data_width = spd->dataw; + pdimm->primary_sdram_width = spd->primw; + pdimm->ec_sdram_width = spd->ecw; + + /* FIXME: what about registered SO-DIMM? */ + switch (spd->dimm_type) { + case 0x01: /* RDIMM */ + case 0x10: /* Mini-RDIMM */ + pdimm->registered_dimm = 1; /* register buffered */ + break; + + case 0x02: /* UDIMM */ + case 0x04: /* SO-DIMM */ + case 0x08: /* Micro-DIMM */ + case 0x20: /* Mini-UDIMM */ + pdimm->registered_dimm = 0; /* unbuffered */ + break; + + default: + printf("unknown dimm_type 0x%02X\n", spd->dimm_type); + return 1; + break; + } + + /* SDRAM device parameters */ + pdimm->n_row_addr = spd->nrow_addr; + pdimm->n_col_addr = spd->ncol_addr; + pdimm->n_banks_per_sdram_device = spd->nbanks; + pdimm->edc_config = spd->config; + pdimm->burst_lengths_bitmask = spd->burstl; + pdimm->row_density = spd->rank_dens; + + /* + * Calculate the Maximum Data Rate based on the Minimum Cycle time. + * The SPD clk_cycle field (tCKmin) is measured in tenths of + * nanoseconds and represented as BCD. + */ + pdimm->tCKmin_X_ps + = convert_bcd_tenths_to_cycle_time_ps(spd->clk_cycle); + pdimm->tCKmin_X_minus_1_ps + = convert_bcd_tenths_to_cycle_time_ps(spd->clk_cycle2); + pdimm->tCKmin_X_minus_2_ps + = convert_bcd_tenths_to_cycle_time_ps(spd->clk_cycle3); + + pdimm->tCKmax_ps = convert_bcd_tenths_to_cycle_time_ps(spd->tckmax); + + /* + * Compute CAS latencies defined by SPD + * The SPD caslat_X should have at least 1 and at most 3 bits set. + * + * If cas_lat after masking is 0, the __ilog2 function returns + * 255 into the variable. This behavior is abused once. + */ + pdimm->caslat_X = __ilog2(spd->cas_lat); + pdimm->caslat_X_minus_1 = __ilog2(spd->cas_lat + & ~(1 << pdimm->caslat_X)); + pdimm->caslat_X_minus_2 = __ilog2(spd->cas_lat + & ~(1 << pdimm->caslat_X) + & ~(1 << pdimm->caslat_X_minus_1)); + + /* Compute CAS latencies below that defined by SPD */ + pdimm->caslat_lowest_derated + = compute_derated_DDR2_CAS_latency(get_memory_clk_period_ps()); + + /* Compute timing parameters */ + pdimm->tRCD_ps = spd->trcd * 250; + pdimm->tRP_ps = spd->trp * 250; + pdimm->tRAS_ps = spd->tras * 1000; + + pdimm->tWR_ps = spd->twr * 250; + pdimm->tWTR_ps = spd->twtr * 250; + pdimm->tRFC_ps = compute_trfc_ps_from_spd(spd->trctrfc_ext, spd->trfc); + + pdimm->tRRD_ps = spd->trrd * 250; + pdimm->tRC_ps = compute_trc_ps_from_spd(spd->trctrfc_ext, spd->trc); + + pdimm->refresh_rate_ps = determine_refresh_rate_ps(spd->refresh); + + pdimm->tIS_ps = convert_bcd_hundredths_to_cycle_time_ps(spd->ca_setup); + pdimm->tIH_ps = convert_bcd_hundredths_to_cycle_time_ps(spd->ca_hold); + pdimm->tDS_ps + = convert_bcd_hundredths_to_cycle_time_ps(spd->data_setup); + pdimm->tDH_ps + = convert_bcd_hundredths_to_cycle_time_ps(spd->data_hold); + + pdimm->tRTP_ps = spd->trtp * 250; + pdimm->tDQSQ_max_ps = spd->tdqsq * 10; + pdimm->tQHS_ps = spd->tqhs * 10; + + return 0; +} diff --git a/arch/powerpc/cpu/mpc8xxx/ddr/ddr3_dimm_params.c b/arch/powerpc/cpu/mpc8xxx/ddr/ddr3_dimm_params.c new file mode 100644 index 0000000..d4199ba --- /dev/null +++ b/arch/powerpc/cpu/mpc8xxx/ddr/ddr3_dimm_params.c @@ -0,0 +1,314 @@ +/* + * Copyright 2008-2009 Freescale Semiconductor, Inc. + * Dave Liu <daveliu@freescale.com> + * + * calculate the organization and timing parameter + * from ddr3 spd, please refer to the spec + * JEDEC standard No.21-C 4_01_02_11R18.pdf + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * Version 2 as published by the Free Software Foundation. + */ + +#include <common.h> +#include <asm/fsl_ddr_sdram.h> + +#include "ddr.h" + +/* + * Calculate the Density of each Physical Rank. + * Returned size is in bytes. + * + * each rank size = + * sdram capacity(bit) / 8 * primary bus width / sdram width + * + * where: sdram capacity = spd byte4[3:0] + * primary bus width = spd byte8[2:0] + * sdram width = spd byte7[2:0] + * + * SPD byte4 - sdram density and banks + * bit[3:0] size(bit) size(byte) + * 0000 256Mb 32MB + * 0001 512Mb 64MB + * 0010 1Gb 128MB + * 0011 2Gb 256MB + * 0100 4Gb 512MB + * 0101 8Gb 1GB + * 0110 16Gb 2GB + * + * SPD byte8 - module memory bus width + * bit[2:0] primary bus width + * 000 8bits + * 001 16bits + * 010 32bits + * 011 64bits + * + * SPD byte7 - module organiztion + * bit[2:0] sdram device width + * 000 4bits + * 001 8bits + * 010 16bits + * 011 32bits + * + */ +static unsigned long long +compute_ranksize(const ddr3_spd_eeprom_t *spd) +{ + unsigned long long bsize; + + int nbit_sdram_cap_bsize = 0; + int nbit_primary_bus_width = 0; + int nbit_sdram_width = 0; + + if ((spd->density_banks & 0xf) < 7) + nbit_sdram_cap_bsize = (spd->density_banks & 0xf) + 28; + if ((spd->bus_width & 0x7) < 4) + nbit_primary_bus_width = (spd->bus_width & 0x7) + 3; + if ((spd->organization & 0x7) < 4) + nbit_sdram_width = (spd->organization & 0x7) + 2; + + bsize = 1ULL << (nbit_sdram_cap_bsize - 3 + + nbit_primary_bus_width - nbit_sdram_width); + + debug("DDR: DDR III rank density = 0x%16lx\n", bsize); + + return bsize; +} + +/* + * ddr_compute_dimm_parameters for DDR3 SPD + * + * Compute DIMM parameters based upon the SPD information in spd. + * Writes the results to the dimm_params_t structure pointed by pdimm. + * + */ +unsigned int +ddr_compute_dimm_parameters(const ddr3_spd_eeprom_t *spd, + dimm_params_t *pdimm, + unsigned int dimm_number) +{ + unsigned int retval; + unsigned int mtb_ps; + + if (spd->mem_type) { + if (spd->mem_type != SPD_MEMTYPE_DDR3) { + printf("DIMM %u: is not a DDR3 SPD.\n", dimm_number); + return 1; + } + } else { + memset(pdimm, 0, sizeof(dimm_params_t)); + return 1; + } + + retval = ddr3_spd_check(spd); + if (retval) { + printf("DIMM %u: failed checksum\n", dimm_number); + return 2; + } + + /* + * The part name in ASCII in the SPD EEPROM is not null terminated. + * Guarantee null termination here by presetting all bytes to 0 + * and copying the part name in ASCII from the SPD onto it + */ + memset(pdimm->mpart, 0, sizeof(pdimm->mpart)); + memcpy(pdimm->mpart, spd->mpart, sizeof(pdimm->mpart) - 1); + + /* DIMM organization parameters */ + pdimm->n_ranks = ((spd->organization >> 3) & 0x7) + 1; + pdimm->rank_density = compute_ranksize(spd); + pdimm->capacity = pdimm->n_ranks * pdimm->rank_density; + pdimm->primary_sdram_width = 1 << (3 + (spd->bus_width & 0x7)); + if ((spd->bus_width >> 3) & 0x3) + pdimm->ec_sdram_width = 8; + else + pdimm->ec_sdram_width = 0; + pdimm->data_width = pdimm->primary_sdram_width + + pdimm->ec_sdram_width; + + switch (spd->module_type & 0xf) { + case 0x01: /* RDIMM */ + case 0x05: /* Mini-RDIMM */ + pdimm->registered_dimm = 1; /* register buffered */ + break; + + case 0x02: /* UDIMM */ + case 0x03: /* SO-DIMM */ + case 0x04: /* Micro-DIMM */ + case 0x06: /* Mini-UDIMM */ + pdimm->registered_dimm = 0; /* unbuffered */ + break; + + default: + printf("unknown dimm_type 0x%02X\n", spd->module_type); + return 1; + } + + /* SDRAM device parameters */ + pdimm->n_row_addr = ((spd->addressing >> 3) & 0x7) + 12; + pdimm->n_col_addr = (spd->addressing & 0x7) + 9; + pdimm->n_banks_per_sdram_device = 8 << ((spd->density_banks >> 4) & 0x7); + + /* + * The SPD spec has not the ECC bit, + * We consider the DIMM as ECC capability + * when the extension bus exist + */ + if (pdimm->ec_sdram_width) + pdimm->edc_config = 0x02; + else + pdimm->edc_config = 0x00; + + /* + * The SPD spec has not the burst length byte + * but DDR3 spec has nature BL8 and BC4, + * BL8 -bit3, BC4 -bit2 + */ + pdimm->burst_lengths_bitmask = 0x0c; + pdimm->row_density = __ilog2(pdimm->rank_density); + + /* MTB - medium timebase + * The unit in the SPD spec is ns, + * We convert it to ps. + * eg: MTB = 0.125ns (125ps) + */ + mtb_ps = (spd->mtb_dividend * 1000) /spd->mtb_divisor; + pdimm->mtb_ps = mtb_ps; + + /* + * sdram minimum cycle time + * we assume the MTB is 0.125ns + * eg: + * tCK_min=15 MTB (1.875ns) ->DDR3-1066 + * =12 MTB (1.5ns) ->DDR3-1333 + * =10 MTB (1.25ns) ->DDR3-1600 + */ + pdimm->tCKmin_X_ps = spd->tCK_min * mtb_ps; + + /* + * CAS latency supported + * bit4 - CL4 + * bit5 - CL5 + * bit18 - CL18 + */ + pdimm->caslat_X = ((spd->caslat_msb << 8) | spd->caslat_lsb) << 4; + + /* + * min CAS latency time + * eg: tAA_min = + * DDR3-800D 100 MTB (12.5ns) + * DDR3-1066F 105 MTB (13.125ns) + * DDR3-1333H 108 MTB (13.5ns) + * DDR3-1600H 90 MTB (11.25ns) + */ + pdimm->tAA_ps = spd->tAA_min * mtb_ps; + + /* + * min write recovery time + * eg: + * tWR_min = 120 MTB (15ns) -> all speed grades. + */ + pdimm->tWR_ps = spd->tWR_min * mtb_ps; + + /* + * min RAS to CAS delay time + * eg: tRCD_min = + * DDR3-800 100 MTB (12.5ns) + * DDR3-1066F 105 MTB (13.125ns) + * DDR3-1333H 108 MTB (13.5ns) + * DDR3-1600H 90 MTB (11.25) + */ + pdimm->tRCD_ps = spd->tRCD_min * mtb_ps; + + /* + * min row active to row active delay time + * eg: tRRD_min = + * DDR3-800(1KB page) 80 MTB (10ns) + * DDR3-1333(1KB page) 48 MTB (6ns) + */ + pdimm->tRRD_ps = spd->tRRD_min * mtb_ps; + + /* + * min row precharge delay time + * eg: tRP_min = + * DDR3-800D 100 MTB (12.5ns) + * DDR3-1066F 105 MTB (13.125ns) + * DDR3-1333H 108 MTB (13.5ns) + * DDR3-1600H 90 MTB (11.25ns) + */ + pdimm->tRP_ps = spd->tRP_min * mtb_ps; + + /* min active to precharge delay time + * eg: tRAS_min = + * DDR3-800D 300 MTB (37.5ns) + * DDR3-1066F 300 MTB (37.5ns) + * DDR3-1333H 288 MTB (36ns) + * DDR3-1600H 280 MTB (35ns) + */ + pdimm->tRAS_ps = (((spd->tRAS_tRC_ext & 0xf) << 8) | spd->tRAS_min_lsb) + * mtb_ps; + /* + * min active to actice/refresh delay time + * eg: tRC_min = + * DDR3-800D 400 MTB (50ns) + * DDR3-1066F 405 MTB (50.625ns) + * DDR3-1333H 396 MTB (49.5ns) + * DDR3-1600H 370 MTB (46.25ns) + */ + pdimm->tRC_ps = (((spd->tRAS_tRC_ext & 0xf0) << 4) | spd->tRC_min_lsb) + * mtb_ps; + /* + * min refresh recovery delay time + * eg: tRFC_min = + * 512Mb 720 MTB (90ns) + * 1Gb 880 MTB (110ns) + * 2Gb 1280 MTB (160ns) + */ + pdimm->tRFC_ps = ((spd->tRFC_min_msb << 8) | spd->tRFC_min_lsb) + * mtb_ps; + /* + * min internal write to read command delay time + * eg: tWTR_min = 40 MTB (7.5ns) - all speed bins. + * tWRT is at least 4 mclk independent of operating freq. + */ + pdimm->tWTR_ps = spd->tWTR_min * mtb_ps; + + /* + * min internal read to precharge command delay time + * eg: tRTP_min = 40 MTB (7.5ns) - all speed bins. + * tRTP is at least 4 mclk independent of operating freq. + */ + pdimm->tRTP_ps = spd->tRTP_min * mtb_ps; + + /* + * Average periodic refresh interval + * tREFI = 7.8 us at normal temperature range + * = 3.9 us at ext temperature range + */ + pdimm->refresh_rate_ps = 7800000; + + /* + * min four active window delay time + * eg: tFAW_min = + * DDR3-800(1KB page) 320 MTB (40ns) + * DDR3-1066(1KB page) 300 MTB (37.5ns) + * DDR3-1333(1KB page) 240 MTB (30ns) + * DDR3-1600(1KB page) 240 MTB (30ns) + */ + pdimm->tFAW_ps = (((spd->tFAW_msb & 0xf) << 8) | spd->tFAW_min) + * mtb_ps; + + /* + * We need check the address mirror for unbuffered DIMM + * If SPD indicate the address map mirror, The DDR controller + * need care it. + */ + if ((spd->module_type == SPD_MODULETYPE_UDIMM) || + (spd->module_type == SPD_MODULETYPE_SODIMM) || + (spd->module_type == SPD_MODULETYPE_MICRODIMM) || + (spd->module_type == SPD_MODULETYPE_MINIUDIMM)) + pdimm->mirrored_dimm = spd->mod_section.unbuffered.addr_mapping & 0x1; + + return 0; +} diff --git a/arch/powerpc/cpu/mpc8xxx/ddr/lc_common_dimm_params.c b/arch/powerpc/cpu/mpc8xxx/ddr/lc_common_dimm_params.c new file mode 100644 index 0000000..e888e3e --- /dev/null +++ b/arch/powerpc/cpu/mpc8xxx/ddr/lc_common_dimm_params.c @@ -0,0 +1,468 @@ +/* + * Copyright 2008 Freescale Semiconductor, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * Version 2 as published by the Free Software Foundation. + */ + +#include <common.h> +#include <asm/fsl_ddr_sdram.h> + +#include "ddr.h" + +unsigned int +compute_cas_latency_ddr3(const dimm_params_t *dimm_params, + common_timing_params_t *outpdimm, + unsigned int number_of_dimms) +{ + unsigned int i; + unsigned int tAAmin_ps = 0; + unsigned int tCKmin_X_ps = 0; + unsigned int common_caslat; + unsigned int caslat_actual; + unsigned int retry = 16; + unsigned int tmp; + const unsigned int mclk_ps = get_memory_clk_period_ps(); + + /* compute the common CAS latency supported between slots */ + tmp = dimm_params[0].caslat_X; + for (i = 1; i < number_of_dimms; i++) + tmp &= dimm_params[i].caslat_X; + common_caslat = tmp; + + /* compute the max tAAmin tCKmin between slots */ + for (i = 0; i < number_of_dimms; i++) { + tAAmin_ps = max(tAAmin_ps, dimm_params[i].tAA_ps); + tCKmin_X_ps = max(tCKmin_X_ps, dimm_params[i].tCKmin_X_ps); + } + /* validate if the memory clk is in the range of dimms */ + if (mclk_ps < tCKmin_X_ps) { + printf("The DIMM max tCKmin is %d ps," + "doesn't support the MCLK cycle %d ps\n", + tCKmin_X_ps, mclk_ps); + return 1; + } + /* determine the acutal cas latency */ + caslat_actual = (tAAmin_ps + mclk_ps - 1) / mclk_ps; + /* check if the dimms support the CAS latency */ + while (!(common_caslat & (1 << caslat_actual)) && retry > 0) { + caslat_actual++; + retry--; + } + /* once the caculation of caslat_actual is completed + * we must verify that this CAS latency value does not + * exceed tAAmax, which is 20 ns for all DDR3 speed grades + */ + if (caslat_actual * mclk_ps > 20000) { + printf("The choosen cas latency %d is too large\n", + caslat_actual); + return 1; + } + outpdimm->lowest_common_SPD_caslat = caslat_actual; + + return 0; +} + +/* + * compute_lowest_common_dimm_parameters() + * + * Determine the worst-case DIMM timing parameters from the set of DIMMs + * whose parameters have been computed into the array pointed to + * by dimm_params. + */ +unsigned int +compute_lowest_common_dimm_parameters(const dimm_params_t *dimm_params, + common_timing_params_t *outpdimm, + unsigned int number_of_dimms) +{ + unsigned int i; + + unsigned int tCKmin_X_ps = 0; + unsigned int tCKmax_ps = 0xFFFFFFFF; + unsigned int tCKmax_max_ps = 0; + unsigned int tRCD_ps = 0; + unsigned int tRP_ps = 0; + unsigned int tRAS_ps = 0; + unsigned int tWR_ps = 0; + unsigned int tWTR_ps = 0; + unsigned int tRFC_ps = 0; + unsigned int tRRD_ps = 0; + unsigned int tRC_ps = 0; + unsigned int refresh_rate_ps = 0; + unsigned int tIS_ps = 0; + unsigned int tIH_ps = 0; + unsigned int tDS_ps = 0; + unsigned int tDH_ps = 0; + unsigned int tRTP_ps = 0; + unsigned int tDQSQ_max_ps = 0; + unsigned int tQHS_ps = 0; + + unsigned int temp1, temp2; + unsigned int additive_latency = 0; +#if !defined(CONFIG_FSL_DDR3) + const unsigned int mclk_ps = get_memory_clk_period_ps(); + unsigned int lowest_good_caslat; + unsigned int not_ok; + + debug("using mclk_ps = %u\n", mclk_ps); +#endif + + temp1 = 0; + for (i = 0; i < number_of_dimms; i++) { + /* + * If there are no ranks on this DIMM, + * it probably doesn't exist, so skip it. + */ + if (dimm_params[i].n_ranks == 0) { + temp1++; + continue; + } + + /* + * Find minimum tCKmax_ps to find fastest slow speed, + * i.e., this is the slowest the whole system can go. + */ + tCKmax_ps = min(tCKmax_ps, dimm_params[i].tCKmax_ps); + + /* Either find maximum value to determine slowest + * speed, delay, time, period, etc */ + tCKmin_X_ps = max(tCKmin_X_ps, dimm_params[i].tCKmin_X_ps); + tCKmax_max_ps = max(tCKmax_max_ps, dimm_params[i].tCKmax_ps); + tRCD_ps = max(tRCD_ps, dimm_params[i].tRCD_ps); + tRP_ps = max(tRP_ps, dimm_params[i].tRP_ps); + tRAS_ps = max(tRAS_ps, dimm_params[i].tRAS_ps); + tWR_ps = max(tWR_ps, dimm_params[i].tWR_ps); + tWTR_ps = max(tWTR_ps, dimm_params[i].tWTR_ps); + tRFC_ps = max(tRFC_ps, dimm_params[i].tRFC_ps); + tRRD_ps = max(tRRD_ps, dimm_params[i].tRRD_ps); + tRC_ps = max(tRC_ps, dimm_params[i].tRC_ps); + tIS_ps = max(tIS_ps, dimm_params[i].tIS_ps); + tIH_ps = max(tIH_ps, dimm_params[i].tIH_ps); + tDS_ps = max(tDS_ps, dimm_params[i].tDS_ps); + tDH_ps = max(tDH_ps, dimm_params[i].tDH_ps); + tRTP_ps = max(tRTP_ps, dimm_params[i].tRTP_ps); + tQHS_ps = max(tQHS_ps, dimm_params[i].tQHS_ps); + refresh_rate_ps = max(refresh_rate_ps, + dimm_params[i].refresh_rate_ps); + + /* + * Find maximum tDQSQ_max_ps to find slowest. + * + * FIXME: is finding the slowest value the correct + * strategy for this parameter? + */ + tDQSQ_max_ps = max(tDQSQ_max_ps, dimm_params[i].tDQSQ_max_ps); + } + + outpdimm->ndimms_present = number_of_dimms - temp1; + + if (temp1 == number_of_dimms) { + debug("no dimms this memory controller\n"); + return 0; + } + + outpdimm->tCKmin_X_ps = tCKmin_X_ps; + outpdimm->tCKmax_ps = tCKmax_ps; + outpdimm->tCKmax_max_ps = tCKmax_max_ps; + outpdimm->tRCD_ps = tRCD_ps; + outpdimm->tRP_ps = tRP_ps; + outpdimm->tRAS_ps = tRAS_ps; + outpdimm->tWR_ps = tWR_ps; + outpdimm->tWTR_ps = tWTR_ps; + outpdimm->tRFC_ps = tRFC_ps; + outpdimm->tRRD_ps = tRRD_ps; + outpdimm->tRC_ps = tRC_ps; + outpdimm->refresh_rate_ps = refresh_rate_ps; + outpdimm->tIS_ps = tIS_ps; + outpdimm->tIH_ps = tIH_ps; + outpdimm->tDS_ps = tDS_ps; + outpdimm->tDH_ps = tDH_ps; + outpdimm->tRTP_ps = tRTP_ps; + outpdimm->tDQSQ_max_ps = tDQSQ_max_ps; + outpdimm->tQHS_ps = tQHS_ps; + + /* Determine common burst length for all DIMMs. */ + temp1 = 0xff; + for (i = 0; i < number_of_dimms; i++) { + if (dimm_params[i].n_ranks) { + temp1 &= dimm_params[i].burst_lengths_bitmask; + } + } + outpdimm->all_DIMMs_burst_lengths_bitmask = temp1; + + /* Determine if all DIMMs registered buffered. */ + temp1 = temp2 = 0; + for (i = 0; i < number_of_dimms; i++) { + if (dimm_params[i].n_ranks) { + if (dimm_params[i].registered_dimm) + temp1 = 1; + if (!dimm_params[i].registered_dimm) + temp2 = 1; + } + } + + outpdimm->all_DIMMs_registered = 0; + if (temp1 && !temp2) { + outpdimm->all_DIMMs_registered = 1; + } + + outpdimm->all_DIMMs_unbuffered = 0; + if (!temp1 && temp2) { + outpdimm->all_DIMMs_unbuffered = 1; + } + + /* CHECKME: */ + if (!outpdimm->all_DIMMs_registered + && !outpdimm->all_DIMMs_unbuffered) { + printf("ERROR: Mix of registered buffered and unbuffered " + "DIMMs detected!\n"); + } + +#if defined(CONFIG_FSL_DDR3) + if (compute_cas_latency_ddr3(dimm_params, outpdimm, number_of_dimms)) + return 1; +#else + /* + * Compute a CAS latency suitable for all DIMMs + * + * Strategy for SPD-defined latencies: compute only + * CAS latency defined by all DIMMs. + */ + + /* + * Step 1: find CAS latency common to all DIMMs using bitwise + * operation. + */ + temp1 = 0xFF; + for (i = 0; i < number_of_dimms; i++) { + if (dimm_params[i].n_ranks) { + temp2 = 0; + temp2 |= 1 << dimm_params[i].caslat_X; + temp2 |= 1 << dimm_params[i].caslat_X_minus_1; + temp2 |= 1 << dimm_params[i].caslat_X_minus_2; + /* + * FIXME: If there was no entry for X-2 (X-1) in + * the SPD, then caslat_X_minus_2 + * (caslat_X_minus_1) contains either 255 or + * 0xFFFFFFFF because that's what the glorious + * __ilog2 function returns for an input of 0. + * On 32-bit PowerPC, left shift counts with bit + * 26 set (that the value of 255 or 0xFFFFFFFF + * will have), cause the destination register to + * be 0. That is why this works. + */ + temp1 &= temp2; + } + } + + /* + * Step 2: check each common CAS latency against tCK of each + * DIMM's SPD. + */ + lowest_good_caslat = 0; + temp2 = 0; + while (temp1) { + not_ok = 0; + temp2 = __ilog2(temp1); + debug("checking common caslat = %u\n", temp2); + + /* Check if this CAS latency will work on all DIMMs at tCK. */ + for (i = 0; i < number_of_dimms; i++) { + if (!dimm_params[i].n_ranks) { + continue; + } + if (dimm_params[i].caslat_X == temp2) { + if (mclk_ps >= dimm_params[i].tCKmin_X_ps) { + debug("CL = %u ok on DIMM %u at tCK=%u" + " ps with its tCKmin_X_ps of %u\n", + temp2, i, mclk_ps, + dimm_params[i].tCKmin_X_ps); + continue; + } else { + not_ok++; + } + } + + if (dimm_params[i].caslat_X_minus_1 == temp2) { + unsigned int tCKmin_X_minus_1_ps + = dimm_params[i].tCKmin_X_minus_1_ps; + if (mclk_ps >= tCKmin_X_minus_1_ps) { + debug("CL = %u ok on DIMM %u at " + "tCK=%u ps with its " + "tCKmin_X_minus_1_ps of %u\n", + temp2, i, mclk_ps, + tCKmin_X_minus_1_ps); + continue; + } else { + not_ok++; + } + } + + if (dimm_params[i].caslat_X_minus_2 == temp2) { + unsigned int tCKmin_X_minus_2_ps + = dimm_params[i].tCKmin_X_minus_2_ps; + if (mclk_ps >= tCKmin_X_minus_2_ps) { + debug("CL = %u ok on DIMM %u at " + "tCK=%u ps with its " + "tCKmin_X_minus_2_ps of %u\n", + temp2, i, mclk_ps, + tCKmin_X_minus_2_ps); + continue; + } else { + not_ok++; + } + } + } + + if (!not_ok) { + lowest_good_caslat = temp2; + } + + temp1 &= ~(1 << temp2); + } + + debug("lowest common SPD-defined CAS latency = %u\n", + lowest_good_caslat); + outpdimm->lowest_common_SPD_caslat = lowest_good_caslat; + + + /* + * Compute a common 'de-rated' CAS latency. + * + * The strategy here is to find the *highest* dereated cas latency + * with the assumption that all of the DIMMs will support a dereated + * CAS latency higher than or equal to their lowest dereated value. + */ + temp1 = 0; + for (i = 0; i < number_of_dimms; i++) { + temp1 = max(temp1, dimm_params[i].caslat_lowest_derated); + } + outpdimm->highest_common_derated_caslat = temp1; + debug("highest common dereated CAS latency = %u\n", temp1); +#endif /* #if defined(CONFIG_FSL_DDR3) */ + + /* Determine if all DIMMs ECC capable. */ + temp1 = 1; + for (i = 0; i < number_of_dimms; i++) { + if (dimm_params[i].n_ranks && dimm_params[i].edc_config != 2) { + temp1 = 0; + break; + } + } + if (temp1) { + debug("all DIMMs ECC capable\n"); + } else { + debug("Warning: not all DIMMs ECC capable, cant enable ECC\n"); + } + outpdimm->all_DIMMs_ECC_capable = temp1; + +#ifndef CONFIG_FSL_DDR3 + /* FIXME: move to somewhere else to validate. */ + if (mclk_ps > tCKmax_max_ps) { + printf("Warning: some of the installed DIMMs " + "can not operate this slowly.\n"); + return 1; + } +#endif + /* + * Compute additive latency. + * + * For DDR1, additive latency should be 0. + * + * For DDR2, with ODT enabled, use "a value" less than ACTTORW, + * which comes from Trcd, and also note that: + * add_lat + caslat must be >= 4 + * + * For DDR3, we use the AL=0 + * + * When to use additive latency for DDR2: + * + * I. Because you are using CL=3 and need to do ODT on writes and + * want functionality. + * 1. Are you going to use ODT? (Does your board not have + * additional termination circuitry for DQ, DQS, DQS_, + * DM, RDQS, RDQS_ for x4/x8 configs?) + * 2. If so, is your lowest supported CL going to be 3? + * 3. If so, then you must set AL=1 because + * + * WL >= 3 for ODT on writes + * RL = AL + CL + * WL = RL - 1 + * -> + * WL = AL + CL - 1 + * AL + CL - 1 >= 3 + * AL + CL >= 4 + * QED + * + * RL >= 3 for ODT on reads + * RL = AL + CL + * + * Since CL aren't usually less than 2, AL=0 is a minimum, + * so the WL-derived AL should be the -- FIXME? + * + * II. Because you are using auto-precharge globally and want to + * use additive latency (posted CAS) to get more bandwidth. + * 1. Are you going to use auto-precharge mode globally? + * + * Use addtivie latency and compute AL to be 1 cycle less than + * tRCD, i.e. the READ or WRITE command is in the cycle + * immediately following the ACTIVATE command.. + * + * III. Because you feel like it or want to do some sort of + * degraded-performance experiment. + * 1. Do you just want to use additive latency because you feel + * like it? + * + * Validation: AL is less than tRCD, and within the other + * read-to-precharge constraints. + */ + + additive_latency = 0; + +#if defined(CONFIG_FSL_DDR2) + if (lowest_good_caslat < 4) { + additive_latency = picos_to_mclk(tRCD_ps) - lowest_good_caslat; + if (mclk_to_picos(additive_latency) > tRCD_ps) { + additive_latency = picos_to_mclk(tRCD_ps); + debug("setting additive_latency to %u because it was " + " greater than tRCD_ps\n", additive_latency); + } + } + +#elif defined(CONFIG_FSL_DDR3) + /* + * The system will not use the global auto-precharge mode. + * However, it uses the page mode, so we set AL=0 + */ + additive_latency = 0; +#endif + + /* + * Validate additive latency + * FIXME: move to somewhere else to validate + * + * AL <= tRCD(min) + */ + if (mclk_to_picos(additive_latency) > tRCD_ps) { + printf("Error: invalid additive latency exceeds tRCD(min).\n"); + return 1; + } + + /* + * RL = CL + AL; RL >= 3 for ODT_RD_CFG to be enabled + * WL = RL - 1; WL >= 3 for ODT_WL_CFG to be enabled + * ADD_LAT (the register) must be set to a value less + * than ACTTORW if WL = 1, then AL must be set to 1 + * RD_TO_PRE (the register) must be set to a minimum + * tRTP + AL if AL is nonzero + */ + + /* + * Additive latency will be applied only if the memctl option to + * use it. + */ + outpdimm->additive_latency = additive_latency; + + return 0; +} diff --git a/arch/powerpc/cpu/mpc8xxx/ddr/main.c b/arch/powerpc/cpu/mpc8xxx/ddr/main.c new file mode 100644 index 0000000..faa1af9 --- /dev/null +++ b/arch/powerpc/cpu/mpc8xxx/ddr/main.c @@ -0,0 +1,479 @@ +/* + * Copyright 2008 Freescale Semiconductor, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * Version 2 as published by the Free Software Foundation. + */ + +/* + * Generic driver for Freescale DDR/DDR2/DDR3 memory controller. + * Based on code from spd_sdram.c + * Author: James Yang [at freescale.com] + */ + +#include <common.h> +#include <asm/fsl_ddr_sdram.h> + +#include "ddr.h" + +extern void fsl_ddr_set_lawbar( + const common_timing_params_t *memctl_common_params, + unsigned int memctl_interleaved, + unsigned int ctrl_num); + +/* processor specific function */ +extern void fsl_ddr_set_memctl_regs(const fsl_ddr_cfg_regs_t *regs, + unsigned int ctrl_num); + +/* Board-specific functions defined in each board's ddr.c */ +extern void fsl_ddr_get_spd(generic_spd_eeprom_t *ctrl_dimms_spd, + unsigned int ctrl_num); + +/* + * ASSUMPTIONS: + * - Same number of CONFIG_DIMM_SLOTS_PER_CTLR on each controller + * - Same memory data bus width on all controllers + * + * NOTES: + * + * The memory controller and associated documentation use confusing + * terminology when referring to the orgranization of DRAM. + * + * Here is a terminology translation table: + * + * memory controller/documention |industry |this code |signals + * -------------------------------|-----------|-----------|----------------- + * physical bank/bank |rank |rank |chip select (CS) + * logical bank/sub-bank |bank |bank |bank address (BA) + * page/row |row |page |row address + * ??? |column |column |column address + * + * The naming confusion is further exacerbated by the descriptions of the + * memory controller interleaving feature, where accesses are interleaved + * _BETWEEN_ two seperate memory controllers. This is configured only in + * CS0_CONFIG[INTLV_CTL] of each memory controller. + * + * memory controller documentation | number of chip selects + * | per memory controller supported + * --------------------------------|----------------------------------------- + * cache line interleaving | 1 (CS0 only) + * page interleaving | 1 (CS0 only) + * bank interleaving | 1 (CS0 only) + * superbank interleraving | depends on bank (chip select) + * | interleraving [rank interleaving] + * | mode used on every memory controller + * + * Even further confusing is the existence of the interleaving feature + * _WITHIN_ each memory controller. The feature is referred to in + * documentation as chip select interleaving or bank interleaving, + * although it is configured in the DDR_SDRAM_CFG field. + * + * Name of field | documentation name | this code + * -----------------------------|-----------------------|------------------ + * DDR_SDRAM_CFG[BA_INTLV_CTL] | Bank (chip select) | rank interleaving + * | interleaving + */ + +#ifdef DEBUG +const char *step_string_tbl[] = { + "STEP_GET_SPD", + "STEP_COMPUTE_DIMM_PARMS", + "STEP_COMPUTE_COMMON_PARMS", + "STEP_GATHER_OPTS", + "STEP_ASSIGN_ADDRESSES", + "STEP_COMPUTE_REGS", + "STEP_PROGRAM_REGS", + "STEP_ALL" +}; + +const char * step_to_string(unsigned int step) { + + unsigned int s = __ilog2(step); + + if ((1 << s) != step) + return step_string_tbl[7]; + + return step_string_tbl[s]; +} +#endif + +int step_assign_addresses(fsl_ddr_info_t *pinfo, + unsigned int dbw_cap_adj[], + unsigned int *memctl_interleaving, + unsigned int *rank_interleaving) +{ + int i, j; + + /* + * If a reduced data width is requested, but the SPD + * specifies a physically wider device, adjust the + * computed dimm capacities accordingly before + * assigning addresses. + */ + for (i = 0; i < CONFIG_NUM_DDR_CONTROLLERS; i++) { + unsigned int found = 0; + + switch (pinfo->memctl_opts[i].data_bus_width) { + case 2: + /* 16-bit */ + printf("can't handle 16-bit mode yet\n"); + break; + + case 1: + /* 32-bit */ + for (j = 0; j < CONFIG_DIMM_SLOTS_PER_CTLR; j++) { + unsigned int dw; + dw = pinfo->dimm_params[i][j].data_width; + if (pinfo->dimm_params[i][j].n_ranks + && (dw == 72 || dw == 64)) { + /* + * FIXME: can't really do it + * like this because this just + * further reduces the memory + */ + found = 1; + break; + } + } + if (found) { + dbw_cap_adj[i] = 1; + } + break; + + case 0: + /* 64-bit */ + break; + + default: + printf("unexpected data bus width " + "specified controller %u\n", i); + return 1; + } + } + + /* + * Check if all controllers are configured for memory + * controller interleaving. + */ + j = 0; + for (i = 0; i < CONFIG_NUM_DDR_CONTROLLERS; i++) { + if (pinfo->memctl_opts[i].memctl_interleaving) { + j++; + } + } + if (j == 2) + *memctl_interleaving = 1; + + /* Check that all controllers are rank interleaving. */ + j = 0; + for (i = 0; i < CONFIG_NUM_DDR_CONTROLLERS; i++) { + if (pinfo->memctl_opts[i].ba_intlv_ctl) { + j++; + } + } + if (j == 2) + *rank_interleaving = 1; + + if (*memctl_interleaving) { + unsigned long long addr, total_mem_per_ctlr = 0; + /* + * If interleaving between memory controllers, + * make each controller start at a base address + * of 0. + * + * Also, if bank interleaving (chip select + * interleaving) is enabled on each memory + * controller, CS0 needs to be programmed to + * cover the entire memory range on that memory + * controller + * + * Bank interleaving also implies that each + * addressed chip select is identical in size. + */ + + for (i = 0; i < CONFIG_NUM_DDR_CONTROLLERS; i++) { + addr = 0; + pinfo->common_timing_params[i].base_address = 0ull; + for (j = 0; j < CONFIG_DIMM_SLOTS_PER_CTLR; j++) { + unsigned long long cap + = pinfo->dimm_params[i][j].capacity; + + pinfo->dimm_params[i][j].base_address = addr; + addr += cap >> dbw_cap_adj[i]; + total_mem_per_ctlr += cap >> dbw_cap_adj[i]; + } + } + pinfo->common_timing_params[0].total_mem = total_mem_per_ctlr; + } else { + /* + * Simple linear assignment if memory + * controllers are not interleaved. + */ + unsigned long long cur_memsize = 0; + for (i = 0; i < CONFIG_NUM_DDR_CONTROLLERS; i++) { + u64 total_mem_per_ctlr = 0; + pinfo->common_timing_params[i].base_address = + cur_memsize; + for (j = 0; j < CONFIG_DIMM_SLOTS_PER_CTLR; j++) { + /* Compute DIMM base addresses. */ + unsigned long long cap = + pinfo->dimm_params[i][j].capacity; + pinfo->dimm_params[i][j].base_address = + cur_memsize; + cur_memsize += cap >> dbw_cap_adj[i]; + total_mem_per_ctlr += cap >> dbw_cap_adj[i]; + } + pinfo->common_timing_params[i].total_mem = + total_mem_per_ctlr; + } + } + + return 0; +} + +unsigned long long +fsl_ddr_compute(fsl_ddr_info_t *pinfo, unsigned int start_step) +{ + unsigned int i, j; + unsigned int all_controllers_memctl_interleaving = 0; + unsigned int all_controllers_rank_interleaving = 0; + unsigned long long total_mem = 0; + + fsl_ddr_cfg_regs_t *ddr_reg = pinfo->fsl_ddr_config_reg; + common_timing_params_t *timing_params = pinfo->common_timing_params; + + /* data bus width capacity adjust shift amount */ + unsigned int dbw_capacity_adjust[CONFIG_NUM_DDR_CONTROLLERS]; + + for (i = 0; i < CONFIG_NUM_DDR_CONTROLLERS; i++) { + dbw_capacity_adjust[i] = 0; + } + + debug("starting at step %u (%s)\n", + start_step, step_to_string(start_step)); + + switch (start_step) { + case STEP_GET_SPD: + /* STEP 1: Gather all DIMM SPD data */ + for (i = 0; i < CONFIG_NUM_DDR_CONTROLLERS; i++) { + fsl_ddr_get_spd(pinfo->spd_installed_dimms[i], i); + } + + case STEP_COMPUTE_DIMM_PARMS: + /* STEP 2: Compute DIMM parameters from SPD data */ + + for (i = 0; i < CONFIG_NUM_DDR_CONTROLLERS; i++) { + for (j = 0; j < CONFIG_DIMM_SLOTS_PER_CTLR; j++) { + unsigned int retval; + generic_spd_eeprom_t *spd = + &(pinfo->spd_installed_dimms[i][j]); + dimm_params_t *pdimm = + &(pinfo->dimm_params[i][j]); + + retval = compute_dimm_parameters(spd, pdimm, i); + if (retval == 2) { + printf("Error: compute_dimm_parameters" + " non-zero returned FATAL value " + "for memctl=%u dimm=%u\n", i, j); + return 0; + } + if (retval) { + debug("Warning: compute_dimm_parameters" + " non-zero return value for memctl=%u " + "dimm=%u\n", i, j); + } + } + } + + case STEP_COMPUTE_COMMON_PARMS: + /* + * STEP 3: Compute a common set of timing parameters + * suitable for all of the DIMMs on each memory controller + */ + for (i = 0; i < CONFIG_NUM_DDR_CONTROLLERS; i++) { + debug("Computing lowest common DIMM" + " parameters for memctl=%u\n", i); + compute_lowest_common_dimm_parameters( + pinfo->dimm_params[i], + &timing_params[i], + CONFIG_DIMM_SLOTS_PER_CTLR); + } + + case STEP_GATHER_OPTS: + /* STEP 4: Gather configuration requirements from user */ + for (i = 0; i < CONFIG_NUM_DDR_CONTROLLERS; i++) { + debug("Reloading memory controller " + "configuration options for memctl=%u\n", i); + /* + * This "reloads" the memory controller options + * to defaults. If the user "edits" an option, + * next_step points to the step after this, + * which is currently STEP_ASSIGN_ADDRESSES. + */ + populate_memctl_options( + timing_params[i].all_DIMMs_registered, + &pinfo->memctl_opts[i], + pinfo->dimm_params[i], i); + } + + case STEP_ASSIGN_ADDRESSES: + /* STEP 5: Assign addresses to chip selects */ + step_assign_addresses(pinfo, + dbw_capacity_adjust, + &all_controllers_memctl_interleaving, + &all_controllers_rank_interleaving); + + case STEP_COMPUTE_REGS: + /* STEP 6: compute controller register values */ + debug("FSL Memory ctrl cg register computation\n"); + for (i = 0; i < CONFIG_NUM_DDR_CONTROLLERS; i++) { + if (timing_params[i].ndimms_present == 0) { + memset(&ddr_reg[i], 0, + sizeof(fsl_ddr_cfg_regs_t)); + continue; + } + + compute_fsl_memctl_config_regs( + &pinfo->memctl_opts[i], + &ddr_reg[i], &timing_params[i], + pinfo->dimm_params[i], + dbw_capacity_adjust[i]); + } + + default: + break; + } + + /* Compute the total amount of memory. */ + + /* + * If bank interleaving but NOT memory controller interleaving + * CS_BNDS describe the quantity of memory on each memory + * controller, so the total is the sum across. + */ + if (!all_controllers_memctl_interleaving + && all_controllers_rank_interleaving) { + total_mem = 0; + for (i = 0; i < CONFIG_NUM_DDR_CONTROLLERS; i++) { + total_mem += timing_params[i].total_mem; + } + + } else { + /* + * Compute the amount of memory available just by + * looking for the highest valid CSn_BNDS value. + * This allows us to also experiment with using + * only CS0 when using dual-rank DIMMs. + */ + unsigned int max_end = 0; + + for (i = 0; i < CONFIG_NUM_DDR_CONTROLLERS; i++) { + for (j = 0; j < CONFIG_CHIP_SELECTS_PER_CTRL; j++) { + fsl_ddr_cfg_regs_t *reg = &ddr_reg[i]; + if (reg->cs[j].config & 0x80000000) { + unsigned int end; + end = reg->cs[j].bnds & 0xFFF; + if (end > max_end) { + max_end = end; + } + } + } + } + + total_mem = 1 + (((unsigned long long)max_end << 24ULL) + | 0xFFFFFFULL); + } + + return total_mem; +} + +/* + * fsl_ddr_sdram() -- this is the main function to be called by + * initdram() in the board file. + * + * It returns amount of memory configured in bytes. + */ +phys_size_t fsl_ddr_sdram(void) +{ + unsigned int i; + unsigned int memctl_interleaved; + unsigned long long total_memory; + fsl_ddr_info_t info; + + /* Reset info structure. */ + memset(&info, 0, sizeof(fsl_ddr_info_t)); + + /* Compute it once normally. */ + total_memory = fsl_ddr_compute(&info, STEP_GET_SPD); + + /* Check for memory controller interleaving. */ + memctl_interleaved = 0; + for (i = 0; i < CONFIG_NUM_DDR_CONTROLLERS; i++) { + memctl_interleaved += + info.memctl_opts[i].memctl_interleaving; + } + + if (memctl_interleaved) { + if (memctl_interleaved == CONFIG_NUM_DDR_CONTROLLERS) { + debug("memctl interleaving\n"); + /* + * Change the meaning of memctl_interleaved + * to be "boolean". + */ + memctl_interleaved = 1; + } else { + printf("Warning: memctl interleaving not " + "properly configured on all controllers\n"); + memctl_interleaved = 0; + for (i = 0; i < CONFIG_NUM_DDR_CONTROLLERS; i++) + info.memctl_opts[i].memctl_interleaving = 0; + debug("Recomputing with memctl_interleaving off.\n"); + total_memory = fsl_ddr_compute(&info, + STEP_ASSIGN_ADDRESSES); + } + } + + /* Program configuration registers. */ + for (i = 0; i < CONFIG_NUM_DDR_CONTROLLERS; i++) { + debug("Programming controller %u\n", i); + if (info.common_timing_params[i].ndimms_present == 0) { + debug("No dimms present on controller %u; " + "skipping programming\n", i); + continue; + } + + fsl_ddr_set_memctl_regs(&(info.fsl_ddr_config_reg[i]), i); + } + + if (memctl_interleaved) { + const unsigned int ctrl_num = 0; + + /* Only set LAWBAR1 if memory controller interleaving is on. */ + fsl_ddr_set_lawbar(&info.common_timing_params[0], + memctl_interleaved, ctrl_num); + } else { + /* + * Memory controller interleaving is NOT on; + * set each lawbar individually. + */ + for (i = 0; i < CONFIG_NUM_DDR_CONTROLLERS; i++) { + fsl_ddr_set_lawbar(&info.common_timing_params[i], + 0, i); + } + } + + debug("total_memory = %llu\n", total_memory); + +#if !defined(CONFIG_PHYS_64BIT) + /* Check for 4G or more. Bad. */ + if (total_memory >= (1ull << 32)) { + printf("Detected %lld MB of memory\n", total_memory >> 20); + printf("This U-Boot only supports < 4G of DDR\n"); + printf("You could rebuild it with CONFIG_PHYS_64BIT\n"); + total_memory = CONFIG_MAX_MEM_MAPPED; + } +#endif + + return total_memory; +} diff --git a/arch/powerpc/cpu/mpc8xxx/ddr/options.c b/arch/powerpc/cpu/mpc8xxx/ddr/options.c new file mode 100644 index 0000000..46731c8 --- /dev/null +++ b/arch/powerpc/cpu/mpc8xxx/ddr/options.c @@ -0,0 +1,297 @@ +/* + * Copyright 2008, 2010 Freescale Semiconductor, 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. + */ + +#include <common.h> +#include <asm/fsl_ddr_sdram.h> + +#include "ddr.h" + +/* Board-specific functions defined in each board's ddr.c */ +extern void fsl_ddr_board_options(memctl_options_t *popts, + dimm_params_t *pdimm, + unsigned int ctrl_num); + +unsigned int populate_memctl_options(int all_DIMMs_registered, + memctl_options_t *popts, + dimm_params_t *pdimm, + unsigned int ctrl_num) +{ + unsigned int i; + const char *p; + + /* Chip select options. */ + + /* Pick chip-select local options. */ + for (i = 0; i < CONFIG_CHIP_SELECTS_PER_CTRL; i++) { + /* If not DDR2, odt_rd_cfg and odt_wr_cfg need to be 0. */ + + /* only for single CS? */ + popts->cs_local_opts[i].odt_rd_cfg = 0; + + popts->cs_local_opts[i].odt_wr_cfg = 1; + popts->cs_local_opts[i].auto_precharge = 0; + } + + /* Pick interleaving mode. */ + + /* + * 0 = no interleaving + * 1 = interleaving between 2 controllers + */ + popts->memctl_interleaving = 0; + + /* + * 0 = cacheline + * 1 = page + * 2 = (logical) bank + * 3 = superbank (only if CS interleaving is enabled) + */ + popts->memctl_interleaving_mode = 0; + + /* + * 0: cacheline: bit 30 of the 36-bit physical addr selects the memctl + * 1: page: bit to the left of the column bits selects the memctl + * 2: bank: bit to the left of the bank bits selects the memctl + * 3: superbank: bit to the left of the chip select selects the memctl + * + * NOTE: ba_intlv (rank interleaving) is independent of memory + * controller interleaving; it is only within a memory controller. + * Must use superbank interleaving if rank interleaving is used and + * memory controller interleaving is enabled. + */ + + /* + * 0 = no + * 0x40 = CS0,CS1 + * 0x20 = CS2,CS3 + * 0x60 = CS0,CS1 + CS2,CS3 + * 0x04 = CS0,CS1,CS2,CS3 + */ + popts->ba_intlv_ctl = 0; + + /* Memory Organization Parameters */ + popts->registered_dimm_en = all_DIMMs_registered; + + /* Operational Mode Paramters */ + + /* Pick ECC modes */ +#ifdef CONFIG_DDR_ECC + popts->ECC_mode = 1; /* 0 = disabled, 1 = enabled */ +#else + popts->ECC_mode = 0; /* 0 = disabled, 1 = enabled */ +#endif + popts->ECC_init_using_memctl = 1; /* 0 = use DMA, 1 = use memctl */ + + /* + * Choose DQS config + * 0 for DDR1 + * 1 for DDR2 + */ +#if defined(CONFIG_FSL_DDR1) + popts->DQS_config = 0; +#elif defined(CONFIG_FSL_DDR2) || defined(CONFIG_FSL_DDR3) + popts->DQS_config = 1; +#endif + + /* Choose self-refresh during sleep. */ + popts->self_refresh_in_sleep = 1; + + /* Choose dynamic power management mode. */ + popts->dynamic_power = 0; + + /* 0 = 64-bit, 1 = 32-bit, 2 = 16-bit */ + popts->data_bus_width = 0; + + /* Choose burst length. */ +#if defined(CONFIG_FSL_DDR3) +#if defined(CONFIG_E500MC) + popts->OTF_burst_chop_en = 0; /* on-the-fly burst chop disable */ + popts->burst_length = DDR_BL8; /* Fixed 8-beat burst len */ +#else + popts->OTF_burst_chop_en = 1; /* on-the-fly burst chop */ + popts->burst_length = DDR_OTF; /* on-the-fly BC4 and BL8 */ +#endif +#else + popts->burst_length = DDR_BL4; /* has to be 4 for DDR2 */ +#endif + + /* Choose ddr controller address mirror mode */ +#if defined(CONFIG_FSL_DDR3) + popts->mirrored_dimm = pdimm[0].mirrored_dimm; +#endif + + /* Global Timing Parameters. */ + debug("mclk_ps = %u ps\n", get_memory_clk_period_ps()); + + /* Pick a caslat override. */ + popts->cas_latency_override = 0; + popts->cas_latency_override_value = 3; + if (popts->cas_latency_override) { + debug("using caslat override value = %u\n", + popts->cas_latency_override_value); + } + + /* Decide whether to use the computed derated latency */ + popts->use_derated_caslat = 0; + + /* Choose an additive latency. */ + popts->additive_latency_override = 0; + popts->additive_latency_override_value = 3; + if (popts->additive_latency_override) { + debug("using additive latency override value = %u\n", + popts->additive_latency_override_value); + } + + /* + * 2T_EN setting + * + * Factors to consider for 2T_EN: + * - number of DIMMs installed + * - number of components, number of active ranks + * - how much time you want to spend playing around + */ + popts->twoT_en = 0; + popts->threeT_en = 0; + + /* + * BSTTOPRE precharge interval + * + * Set this to 0 for global auto precharge + * + * FIXME: Should this be configured in picoseconds? + * Why it should be in ps: better understanding of this + * relative to actual DRAM timing parameters such as tRAS. + * e.g. tRAS(min) = 40 ns + */ + popts->bstopre = 0x100; + + /* Minimum CKE pulse width -- tCKE(MIN) */ + popts->tCKE_clock_pulse_width_ps + = mclk_to_picos(FSL_DDR_MIN_TCKE_PULSE_WIDTH_DDR); + + /* + * Window for four activates -- tFAW + * + * FIXME: UM: applies only to DDR2/DDR3 with eight logical banks only + * FIXME: varies depending upon number of column addresses or data + * FIXME: width, was considering looking at pdimm->primary_sdram_width + */ +#if defined(CONFIG_FSL_DDR1) + popts->tFAW_window_four_activates_ps = mclk_to_picos(1); + +#elif defined(CONFIG_FSL_DDR2) + /* + * x4/x8; some datasheets have 35000 + * x16 wide columns only? Use 50000? + */ + popts->tFAW_window_four_activates_ps = 37500; + +#elif defined(CONFIG_FSL_DDR3) + popts->tFAW_window_four_activates_ps = pdimm[0].tFAW_ps; +#endif + popts->zq_en = 0; + popts->wrlvl_en = 0; +#if defined(CONFIG_FSL_DDR3) + /* + * due to ddr3 dimm is fly-by topology + * we suggest to enable write leveling to + * meet the tQDSS under different loading. + */ + popts->wrlvl_en = 1; + popts->wrlvl_override = 0; +#endif + + /* + * Check interleaving configuration from environment. + * Please refer to doc/README.fsl-ddr for the detail. + * + * If memory controller interleaving is enabled, then the data + * bus widths must be programmed identically for the 2 memory + * controllers. + * + * XXX: Attempt to set both controllers to the same chip select + * interleaving mode. It will do a best effort to get the + * requested ranks interleaved together such that the result + * should be a subset of the requested configuration. + */ +#if (CONFIG_NUM_DDR_CONTROLLERS > 1) + if ((p = getenv("memctl_intlv_ctl")) != NULL) { + if (pdimm[0].n_ranks == 0) { + printf("There is no rank on CS0. Because only rank on " + "CS0 and ranks chip-select interleaved with CS0" + " are controller interleaved, force non memory " + "controller interleaving\n"); + popts->memctl_interleaving = 0; + } else { + popts->memctl_interleaving = 1; + if (strcmp(p, "cacheline") == 0) + popts->memctl_interleaving_mode = + FSL_DDR_CACHE_LINE_INTERLEAVING; + else if (strcmp(p, "page") == 0) + popts->memctl_interleaving_mode = + FSL_DDR_PAGE_INTERLEAVING; + else if (strcmp(p, "bank") == 0) + popts->memctl_interleaving_mode = + FSL_DDR_BANK_INTERLEAVING; + else if (strcmp(p, "superbank") == 0) + popts->memctl_interleaving_mode = + FSL_DDR_SUPERBANK_INTERLEAVING; + else + popts->memctl_interleaving_mode = + simple_strtoul(p, NULL, 0); + } + } +#endif + + if( ((p = getenv("ba_intlv_ctl")) != NULL) && + (CONFIG_CHIP_SELECTS_PER_CTRL > 1)) { + if (strcmp(p, "cs0_cs1") == 0) + popts->ba_intlv_ctl = FSL_DDR_CS0_CS1; + else if (strcmp(p, "cs2_cs3") == 0) + popts->ba_intlv_ctl = FSL_DDR_CS2_CS3; + else if (strcmp(p, "cs0_cs1_and_cs2_cs3") == 0) + popts->ba_intlv_ctl = FSL_DDR_CS0_CS1_AND_CS2_CS3; + else if (strcmp(p, "cs0_cs1_cs2_cs3") == 0) + popts->ba_intlv_ctl = FSL_DDR_CS0_CS1_CS2_CS3; + else + popts->ba_intlv_ctl = simple_strtoul(p, NULL, 0); + + switch (popts->ba_intlv_ctl & FSL_DDR_CS0_CS1_CS2_CS3) { + case FSL_DDR_CS0_CS1_CS2_CS3: + case FSL_DDR_CS0_CS1: + if (pdimm[0].n_ranks != 2) { + popts->ba_intlv_ctl = 0; + printf("Not enough bank(chip-select) for " + "CS0+CS1, force non-interleaving!\n"); + } + break; + case FSL_DDR_CS2_CS3: + if (pdimm[1].n_ranks !=2){ + popts->ba_intlv_ctl = 0; + printf("Not enough bank(CS) for CS2+CS3, " + "force non-interleaving!\n"); + } + break; + case FSL_DDR_CS0_CS1_AND_CS2_CS3: + if ((pdimm[0].n_ranks != 2)||(pdimm[1].n_ranks != 2)) { + popts->ba_intlv_ctl = 0; + printf("Not enough bank(CS) for CS0+CS1 or " + "CS2+CS3, force non-interleaving!\n"); + } + break; + default: + popts->ba_intlv_ctl = 0; + break; + } + } + + fsl_ddr_board_options(popts, pdimm, ctrl_num); + + return 0; +} diff --git a/arch/powerpc/cpu/mpc8xxx/ddr/util.c b/arch/powerpc/cpu/mpc8xxx/ddr/util.c new file mode 100644 index 0000000..1e2d921 --- /dev/null +++ b/arch/powerpc/cpu/mpc8xxx/ddr/util.c @@ -0,0 +1,206 @@ +/* + * Copyright 2008 Freescale Semiconductor, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * Version 2 as published by the Free Software Foundation. + */ + +#include <common.h> +#include <asm/fsl_law.h> + +#include "ddr.h" + +unsigned int fsl_ddr_get_mem_data_rate(void); + +/* + * Round mclk_ps to nearest 10 ps in memory controller code. + * + * If an imprecise data rate is too high due to rounding error + * propagation, compute a suitably rounded mclk_ps to compute + * a working memory controller configuration. + */ +unsigned int get_memory_clk_period_ps(void) +{ + unsigned int mclk_ps; + + mclk_ps = 2000000000000ULL / fsl_ddr_get_mem_data_rate(); + /* round to nearest 10 ps */ + return 10 * ((mclk_ps + 5) / 10); +} + +/* Convert picoseconds into DRAM clock cycles (rounding up if needed). */ +unsigned int picos_to_mclk(unsigned int picos) +{ + const unsigned long long ULL_2e12 = 2000000000000ULL; + const unsigned long long ULL_8Fs = 0xFFFFFFFFULL; + unsigned long long clks; + unsigned long long clks_temp; + + if (!picos) + return 0; + + clks = fsl_ddr_get_mem_data_rate() * (unsigned long long) picos; + clks_temp = clks; + clks = clks / ULL_2e12; + if (clks_temp % ULL_2e12) { + clks++; + } + + if (clks > ULL_8Fs) { + clks = ULL_8Fs; + } + + return (unsigned int) clks; +} + +unsigned int mclk_to_picos(unsigned int mclk) +{ + return get_memory_clk_period_ps() * mclk; +} + +void +__fsl_ddr_set_lawbar(const common_timing_params_t *memctl_common_params, + unsigned int memctl_interleaved, + unsigned int ctrl_num) +{ + unsigned long long base = memctl_common_params->base_address; + unsigned long long size = memctl_common_params->total_mem; + + /* + * If no DIMMs on this controller, do not proceed any further. + */ + if (!memctl_common_params->ndimms_present) { + return; + } + +#if !defined(CONFIG_PHYS_64BIT) + if (base >= CONFIG_MAX_MEM_MAPPED) + return; + if ((base + size) >= CONFIG_MAX_MEM_MAPPED) + size = CONFIG_MAX_MEM_MAPPED - base; +#endif + + if (ctrl_num == 0) { + /* + * Set up LAW for DDR controller 1 space. + */ + unsigned int lawbar1_target_id = memctl_interleaved + ? LAW_TRGT_IF_DDR_INTRLV : LAW_TRGT_IF_DDR_1; + + if (set_ddr_laws(base, size, lawbar1_target_id) < 0) { + printf("%s: ERROR (ctrl #0, intrlv=%d)\n", __func__, + memctl_interleaved); + return ; + } + } else if (ctrl_num == 1) { + if (set_ddr_laws(base, size, LAW_TRGT_IF_DDR_2) < 0) { + printf("%s: ERROR (ctrl #1)\n", __func__); + return ; + } + } else { + printf("%s: unexpected DDR controller number (%u)\n", __func__, + ctrl_num); + } +} + +__attribute__((weak, alias("__fsl_ddr_set_lawbar"))) void +fsl_ddr_set_lawbar(const common_timing_params_t *memctl_common_params, + unsigned int memctl_interleaved, + unsigned int ctrl_num); + +void board_add_ram_info(int use_default) +{ +#if defined(CONFIG_MPC85xx) + volatile ccsr_ddr_t *ddr = (void *)(CONFIG_SYS_MPC85xx_DDR_ADDR); +#elif defined(CONFIG_MPC86xx) + volatile ccsr_ddr_t *ddr = (void *)(CONFIG_SYS_MPC86xx_DDR_ADDR); +#endif +#if (CONFIG_NUM_DDR_CONTROLLERS > 1) + uint32_t cs0_config = in_be32(&ddr->cs0_config); +#endif + uint32_t sdram_cfg = in_be32(&ddr->sdram_cfg); + int cas_lat; + + puts(" (DDR"); + switch ((sdram_cfg & SDRAM_CFG_SDRAM_TYPE_MASK) >> + SDRAM_CFG_SDRAM_TYPE_SHIFT) { + case SDRAM_TYPE_DDR1: + puts("1"); + break; + case SDRAM_TYPE_DDR2: + puts("2"); + break; + case SDRAM_TYPE_DDR3: + puts("3"); + break; + default: + puts("?"); + break; + } + + if (sdram_cfg & SDRAM_CFG_32_BE) + puts(", 32-bit"); + else + puts(", 64-bit"); + + /* Calculate CAS latency based on timing cfg values */ + cas_lat = ((in_be32(&ddr->timing_cfg_1) >> 16) & 0xf) + 1; + if ((in_be32(&ddr->timing_cfg_3) >> 12) & 1) + cas_lat += (8 << 1); + printf(", CL=%d", cas_lat >> 1); + if (cas_lat & 0x1) + puts(".5"); + + if (sdram_cfg & SDRAM_CFG_ECC_EN) + puts(", ECC on)"); + else + puts(", ECC off)"); + +#if (CONFIG_NUM_DDR_CONTROLLERS > 1) + if (cs0_config & 0x20000000) { + puts("\n"); + puts(" DDR Controller Interleaving Mode: "); + + switch ((cs0_config >> 24) & 0xf) { + case FSL_DDR_CACHE_LINE_INTERLEAVING: + puts("cache line"); + break; + case FSL_DDR_PAGE_INTERLEAVING: + puts("page"); + break; + case FSL_DDR_BANK_INTERLEAVING: + puts("bank"); + break; + case FSL_DDR_SUPERBANK_INTERLEAVING: + puts("super-bank"); + break; + default: + puts("invalid"); + break; + } + } +#endif + + if ((sdram_cfg >> 8) & 0x7f) { + puts("\n"); + puts(" DDR Chip-Select Interleaving Mode: "); + switch(sdram_cfg >> 8 & 0x7f) { + case FSL_DDR_CS0_CS1_CS2_CS3: + puts("CS0+CS1+CS2+CS3"); + break; + case FSL_DDR_CS0_CS1: + puts("CS0+CS1"); + break; + case FSL_DDR_CS2_CS3: + puts("CS2+CS3"); + break; + case FSL_DDR_CS0_CS1_AND_CS2_CS3: + puts("CS0+CS1 and CS2+CS3"); + break; + default: + puts("invalid"); + break; + } + } +} diff --git a/arch/powerpc/cpu/mpc8xxx/fdt.c b/arch/powerpc/cpu/mpc8xxx/fdt.c new file mode 100644 index 0000000..ccefaf5 --- /dev/null +++ b/arch/powerpc/cpu/mpc8xxx/fdt.c @@ -0,0 +1,55 @@ +/* + * Copyright 2009 Freescale Semiconductor, Inc. + * + * This file is derived from arch/powerpc/cpu/mpc85xx/cpu.c and + * arch/powerpc/cpu/mpc86xx/cpu.c. Basically this file contains + * cpu specific common code for 85xx/86xx processors. + * 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> +#include <libfdt.h> +#include <fdt_support.h> + +void ft_fixup_num_cores(void *blob) { + int off, num_cores, del_cores; + + del_cores = 0; + num_cores = cpu_numcores(); + + off = fdt_node_offset_by_prop_value(blob, -1, "device_type", "cpu", 4); + while (off != -FDT_ERR_NOTFOUND) { + u32 *reg = (u32 *)fdt_getprop(blob, off, "reg", 0); + + /* if we find a cpu node outside of what we expect delete it + * and reset the offset back to the start since we can't + * trust the offsets anymore + */ + if (*reg > num_cores-1) { + fdt_del_node(blob, off); + del_cores++; + off = -1; + } + off = fdt_node_offset_by_prop_value(blob, off, + "device_type", "cpu", 4); + } + debug ("%x core system found\n", num_cores); + debug ("deleted %d extra core entry entries from device tree\n", + del_cores); +} diff --git a/arch/powerpc/cpu/mpc8xxx/pci_cfg.c b/arch/powerpc/cpu/mpc8xxx/pci_cfg.c new file mode 100644 index 0000000..9b7181d --- /dev/null +++ b/arch/powerpc/cpu/mpc8xxx/pci_cfg.c @@ -0,0 +1,214 @@ +/* + * Copyright 2009-2010 Freescale Semiconductor, Inc. + * + * 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> +#include <asm/fsl_law.h> +#include <pci.h> + +struct pci_info { + u32 cfg; +}; + +/* The cfg field is a bit mask in which each bit represents the value of + * cfg_IO_ports[] signal and the bit is set if the interface would be + * enabled based on the value of cfg_IO_ports[] signal + * + * On MPC86xx/PQ3 based systems: + * we extract cfg_IO_ports from GUTS register PORDEVSR + * + * cfg_IO_ports only exist on systems w/PCIe (we set cfg 0 for systems + * without PCIe) + */ + +#if defined(CONFIG_MPC8540) || defined(CONFIG_MPC8560) +static struct pci_info pci_config_info[] = +{ + [LAW_TRGT_IF_PCI] = { + .cfg = 0, + }, +}; +#elif defined(CONFIG_MPC8541) || defined(CONFIG_MPC8555) +static struct pci_info pci_config_info[] = +{ + [LAW_TRGT_IF_PCI] = { + .cfg = 0, + }, +}; +#elif defined(CONFIG_MPC8536) +static struct pci_info pci_config_info[] = +{ + [LAW_TRGT_IF_PCI] = { + .cfg = 0, + }, + [LAW_TRGT_IF_PCIE_1] = { + .cfg = (1 << 2) | (1 << 3) | (1 << 5) | (1 << 7), + }, + [LAW_TRGT_IF_PCIE_2] = { + .cfg = (1 << 5) | (1 << 7), + }, + [LAW_TRGT_IF_PCIE_3] = { + .cfg = (1 << 7), + }, +}; +#elif defined(CONFIG_MPC8544) +static struct pci_info pci_config_info[] = +{ + [LAW_TRGT_IF_PCI] = { + .cfg = 0, + }, + [LAW_TRGT_IF_PCIE_1] = { + .cfg = (1 << 2) | (1 << 3) | (1 << 4) | (1 << 5) | + (1 << 6) | (1 << 7), + }, + [LAW_TRGT_IF_PCIE_2] = { + .cfg = (1 << 4) | (1 << 5) | (1 << 6) | (1 << 7), + }, + [LAW_TRGT_IF_PCIE_3] = { + .cfg = (1 << 6) | (1 << 7), + }, +}; +#elif defined(CONFIG_MPC8548) +static struct pci_info pci_config_info[] = +{ + [LAW_TRGT_IF_PCI_1] = { + .cfg = 0, + }, + [LAW_TRGT_IF_PCI_2] = { + .cfg = 0, + }, + /* PCI_2 is always host and we dont use iosel to determine enable/disable */ + [LAW_TRGT_IF_PCIE_1] = { + .cfg = (1 << 3) | (1 << 4) | (1 << 7), + }, +}; +#elif defined(CONFIG_MPC8568) +static struct pci_info pci_config_info[] = +{ + [LAW_TRGT_IF_PCI] = { + .cfg = 0, + }, + [LAW_TRGT_IF_PCIE_1] = { + .cfg = (1 << 3) | (1 << 4) | (1 << 7), + }, +}; +#elif defined(CONFIG_MPC8569) +static struct pci_info pci_config_info[] = +{ + [LAW_TRGT_IF_PCIE_1] = { + .cfg = (1 << 0) | (1 << 4) | (1 << 5) | (1 << 6) | (1 << 7) | + (1 << 8) | (1 << 0xc) | (1 << 0xf), + }, +}; +#elif defined(CONFIG_MPC8572) +static struct pci_info pci_config_info[] = +{ + [LAW_TRGT_IF_PCIE_1] = { + .cfg = (1 << 2) | (1 << 3) | (1 << 7) | + (1 << 0xb) | (1 << 0xc) | (1 << 0xf), + }, + [LAW_TRGT_IF_PCIE_2] = { + .cfg = (1 << 3) | (1 << 7), + }, + [LAW_TRGT_IF_PCIE_3] = { + .cfg = (1 << 7), + }, +}; +#elif defined(CONFIG_MPC8610) +static struct pci_info pci_config_info[] = +{ + [LAW_TRGT_IF_PCI_1] = { + .cfg = 0, + }, + [LAW_TRGT_IF_PCIE_1] = { + .cfg = (1 << 1) | (1 << 4), + }, + [LAW_TRGT_IF_PCIE_2] = { + .cfg = (1 << 0) | (1 << 4), + }, +}; +#elif defined(CONFIG_MPC8641) +static struct pci_info pci_config_info[] = +{ + [LAW_TRGT_IF_PCIE_1] = { + .cfg = (1 << 2) | (1 << 3) | (1 << 5) | (1 << 6) | + (1 << 7) | (1 << 0xe) | (1 << 0xf), + }, +}; +#elif defined(CONFIG_P1011) || defined(CONFIG_P1020) || \ + defined(CONFIG_P1012) || defined(CONFIG_P1021) +static struct pci_info pci_config_info[] = +{ + [LAW_TRGT_IF_PCIE_1] = { + .cfg = (1 << 0) | (1 << 6) | (1 << 0xe) | (1 << 0xf), + }, + [LAW_TRGT_IF_PCIE_2] = { + .cfg = (1 << 0xe), + }, +}; +#elif defined(CONFIG_P1013) || defined(CONFIG_P1022) +static struct pci_info pci_config_info[] = +{ + [LAW_TRGT_IF_PCIE_1] = { + .cfg = (1 << 6) | (1 << 7) | (1 << 9) | (1 << 0xa) | + (1 << 0xb) | (1 << 0xd) | (1 << 0xe) | + (1 << 0xf) | (1 << 0x15) | (1 << 0x16) | + (1 << 0x17) | (1 << 0x18) | (1 << 0x19) | + (1 << 0x1a) | (1 << 0x1b) | (1 << 0x1c) | + (1 << 0x1d) | (1 << 0x1e) | (1 << 0x1f), + }, + [LAW_TRGT_IF_PCIE_2] = { + .cfg = (1 << 0) | (1 << 1) | (1 << 6) | (1 << 7) | + (1 << 9) | (1 << 0xa) | (1 << 0xb) | (1 << 0xd) | + (1 << 0x15) | (1 << 0x16) | (1 << 0x17) | + (1 << 0x18) | (1 << 0x1c), + }, + [LAW_TRGT_IF_PCIE_3] = { + .cfg = (1 << 6) | (1 << 7) | (1 << 9) | (1 << 0xd) | + (1 << 0x15) | (1 << 0x16) | (1 << 0x17) | (1 << 0x18) | + (1 << 0x19) | (1 << 0x1a) | (1 << 0x1b), + }, +}; +#elif defined(CONFIG_P2010) || defined(CONFIG_P2020) +static struct pci_info pci_config_info[] = +{ + [LAW_TRGT_IF_PCIE_1] = { + .cfg = (1 << 0) | (1 << 2) | (1 << 4) | (1 << 6) | + (1 << 0xd) | (1 << 0xe) | (1 << 0xf), + }, + [LAW_TRGT_IF_PCIE_2] = { + .cfg = (1 << 2) | (1 << 0xe), + }, + [LAW_TRGT_IF_PCIE_3] = { + .cfg = (1 << 2) | (1 << 4), + }, +}; +#elif defined(CONFIG_FSL_CORENET) +#else +#error Need to define pci_config_info for processor +#endif + +#ifndef CONFIG_FSL_CORENET +int is_fsl_pci_cfg(enum law_trgt_if trgt, u32 io_sel) +{ + return ((1 << io_sel) & pci_config_info[trgt].cfg); +} +#endif diff --git a/arch/powerpc/cpu/ppc4xx/40x_spd_sdram.c b/arch/powerpc/cpu/ppc4xx/40x_spd_sdram.c new file mode 100644 index 0000000..f65cd88 --- /dev/null +++ b/arch/powerpc/cpu/ppc4xx/40x_spd_sdram.c @@ -0,0 +1,464 @@ +/* + * arch/powerpc/cpu/ppc4xx/40x_spd_sdram.c + * This SPD SDRAM detection code supports IBM/AMCC PPC44x cpu with a + * SDRAM controller. Those are all current 405 PPC's. + * + * (C) Copyright 2001 + * Bill Hunter, Wave 7 Optics, williamhunter@attbi.com + * + * Based on code by: + * + * Kenneth Johansson ,Ericsson AB. + * kenneth.johansson@etx.ericsson.se + * + * hacked up by bill hunter. fixed so we could run before + * serial_init and console_init. previous version avoided this by + * running out of cache memory during serial/console init, then running + * this code later. + * + * (C) Copyright 2002 + * Jun Gu, Artesyn Technology, jung@artesyncp.com + * Support for AMCC 440 based on OpenBIOS draminit.c from IBM. + * + * (C) Copyright 2005 + * 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> +#include <asm/processor.h> +#include <i2c.h> +#include <ppc4xx.h> + +#if defined(CONFIG_SPD_EEPROM) && !defined(CONFIG_440) + +/* + * Set default values + */ +#ifndef CONFIG_SYS_I2C_SPEED +#define CONFIG_SYS_I2C_SPEED 50000 +#endif + +#define ONE_BILLION 1000000000 + +#define SDRAM0_CFG_DCE 0x80000000 +#define SDRAM0_CFG_SRE 0x40000000 +#define SDRAM0_CFG_PME 0x20000000 +#define SDRAM0_CFG_MEMCHK 0x10000000 +#define SDRAM0_CFG_REGEN 0x08000000 +#define SDRAM0_CFG_ECCDD 0x00400000 +#define SDRAM0_CFG_EMDULR 0x00200000 +#define SDRAM0_CFG_DRW_SHIFT (31-6) +#define SDRAM0_CFG_BRPF_SHIFT (31-8) + +#define SDRAM0_TR_CASL_SHIFT (31-8) +#define SDRAM0_TR_PTA_SHIFT (31-13) +#define SDRAM0_TR_CTP_SHIFT (31-15) +#define SDRAM0_TR_LDF_SHIFT (31-17) +#define SDRAM0_TR_RFTA_SHIFT (31-29) +#define SDRAM0_TR_RCD_SHIFT (31-31) + +#define SDRAM0_RTR_SHIFT (31-15) +#define SDRAM0_ECCCFG_SHIFT (31-11) + +/* SDRAM0_CFG enable macro */ +#define SDRAM0_CFG_BRPF(x) ( ( x & 0x3)<< SDRAM0_CFG_BRPF_SHIFT ) + +#define SDRAM0_BXCR_SZ_MASK 0x000e0000 +#define SDRAM0_BXCR_AM_MASK 0x0000e000 + +#define SDRAM0_BXCR_SZ_SHIFT (31-14) +#define SDRAM0_BXCR_AM_SHIFT (31-18) + +#define SDRAM0_BXCR_SZ(x) ( (( x << SDRAM0_BXCR_SZ_SHIFT) & SDRAM0_BXCR_SZ_MASK) ) +#define SDRAM0_BXCR_AM(x) ( (( x << SDRAM0_BXCR_AM_SHIFT) & SDRAM0_BXCR_AM_MASK) ) + +#ifdef CONFIG_SPDDRAM_SILENT +# define SPD_ERR(x) do { return 0; } while (0) +#else +# define SPD_ERR(x) do { printf(x); return(0); } while (0) +#endif + +#define sdram_HZ_to_ns(hertz) (1000000000/(hertz)) + +/* function prototypes */ +int spd_read(uint addr); + + +/* + * This function is reading data from the DIMM module EEPROM over the SPD bus + * and uses that to program the sdram controller. + * + * This works on boards that has the same schematics that the AMCC walnut has. + * + * Input: null for default I2C spd functions or a pointer to a custom function + * returning spd_data. + */ + +long int spd_sdram(int(read_spd)(uint addr)) +{ + int tmp,row,col; + int total_size,bank_size,bank_code; + int ecc_on; + int mode; + int bank_cnt; + + int sdram0_pmit=0x07c00000; +#ifndef CONFIG_405EP /* not on PPC405EP */ + int sdram0_besr0 = -1; + int sdram0_besr1 = -1; + int sdram0_eccesr = -1; +#endif + int sdram0_ecccfg; + + int sdram0_rtr=0; + int sdram0_tr=0; + + int sdram0_b0cr; + int sdram0_b1cr; + int sdram0_b2cr; + int sdram0_b3cr; + + int sdram0_cfg=0; + + int t_rp; + int t_rcd; + int t_ras; + int t_rc; + int min_cas; + + PPC4xx_SYS_INFO sys_info; + unsigned long bus_period_x_10; + + /* + * get the board info + */ + get_sys_info(&sys_info); + bus_period_x_10 = ONE_BILLION / (sys_info.freqPLB / 10); + + if (read_spd == 0){ + read_spd=spd_read; + /* + * Make sure I2C controller is initialized + * before continuing. + */ + i2c_init(CONFIG_SYS_I2C_SPEED, CONFIG_SYS_I2C_SLAVE); + } + + /* Make shure we are using SDRAM */ + if (read_spd(2) != 0x04) { + SPD_ERR("SDRAM - non SDRAM memory module found\n"); + } + + /* ------------------------------------------------------------------ + * configure memory timing register + * + * data from DIMM: + * 27 IN Row Precharge Time ( t RP) + * 29 MIN RAS to CAS Delay ( t RCD) + * 127 Component and Clock Detail ,clk0-clk3, junction temp, CAS + * -------------------------------------------------------------------*/ + + /* + * first figure out which cas latency mode to use + * use the min supported mode + */ + + tmp = read_spd(127) & 0x6; + if (tmp == 0x02) { /* only cas = 2 supported */ + min_cas = 2; +/* t_ck = read_spd(9); */ +/* t_ac = read_spd(10); */ + } else if (tmp == 0x04) { /* only cas = 3 supported */ + min_cas = 3; +/* t_ck = read_spd(9); */ +/* t_ac = read_spd(10); */ + } else if (tmp == 0x06) { /* 2,3 supported, so use 2 */ + min_cas = 2; +/* t_ck = read_spd(23); */ +/* t_ac = read_spd(24); */ + } else { + SPD_ERR("SDRAM - unsupported CAS latency \n"); + } + + /* get some timing values, t_rp,t_rcd,t_ras,t_rc + */ + t_rp = read_spd(27); + t_rcd = read_spd(29); + t_ras = read_spd(30); + t_rc = t_ras + t_rp; + + /* The following timing calcs subtract 1 before deviding. + * this has effect of using ceiling instead of floor rounding, + * and also subtracting 1 to convert number to reg value + */ + /* set up CASL */ + sdram0_tr = (min_cas - 1) << SDRAM0_TR_CASL_SHIFT; + /* set up PTA */ + sdram0_tr |= ((((t_rp - 1) * 10)/bus_period_x_10) & 0x3) << SDRAM0_TR_PTA_SHIFT; + /* set up CTP */ + tmp = (((t_rc - t_rcd - t_rp -1) * 10) / bus_period_x_10) & 0x3; + if (tmp < 1) + tmp = 1; + sdram0_tr |= tmp << SDRAM0_TR_CTP_SHIFT; + /* set LDF = 2 cycles, reg value = 1 */ + sdram0_tr |= 1 << SDRAM0_TR_LDF_SHIFT; + /* set RFTA = t_rfc/bus_period, use t_rfc = t_rc */ + tmp = (((t_rc - 1) * 10) / bus_period_x_10) - 3; + if (tmp < 0) + tmp = 0; + if (tmp > 6) + tmp = 6; + sdram0_tr |= tmp << SDRAM0_TR_RFTA_SHIFT; + /* set RCD = t_rcd/bus_period*/ + sdram0_tr |= ((((t_rcd - 1) * 10) / bus_period_x_10) &0x3) << SDRAM0_TR_RCD_SHIFT ; + + + /*------------------------------------------------------------------ + * configure RTR register + * -------------------------------------------------------------------*/ + row = read_spd(3); + col = read_spd(4); + tmp = read_spd(12) & 0x7f ; /* refresh type less self refresh bit */ + switch (tmp) { + case 0x00: + tmp = 15625; + break; + case 0x01: + tmp = 15625 / 4; + break; + case 0x02: + tmp = 15625 / 2; + break; + case 0x03: + tmp = 15625 * 2; + break; + case 0x04: + tmp = 15625 * 4; + break; + case 0x05: + tmp = 15625 * 8; + break; + default: + SPD_ERR("SDRAM - Bad refresh period \n"); + } + /* convert from nsec to bus cycles */ + tmp = (tmp * 10) / bus_period_x_10; + sdram0_rtr = (tmp & 0x3ff8) << SDRAM0_RTR_SHIFT; + + /*------------------------------------------------------------------ + * determine the number of banks used + * -------------------------------------------------------------------*/ + /* byte 7:6 is module data width */ + if (read_spd(7) != 0) + SPD_ERR("SDRAM - unsupported module width\n"); + tmp = read_spd(6); + if (tmp < 32) + SPD_ERR("SDRAM - unsupported module width\n"); + else if (tmp < 64) + bank_cnt = 1; /* one bank per sdram side */ + else if (tmp < 73) + bank_cnt = 2; /* need two banks per side */ + else if (tmp < 161) + bank_cnt = 4; /* need four banks per side */ + else + SPD_ERR("SDRAM - unsupported module width\n"); + + /* byte 5 is the module row count (refered to as dimm "sides") */ + tmp = read_spd(5); + if (tmp == 1) + ; + else if (tmp==2) + bank_cnt *= 2; + else if (tmp==4) + bank_cnt *= 4; + else + bank_cnt = 8; /* 8 is an error code */ + + if (bank_cnt > 4) /* we only have 4 banks to work with */ + SPD_ERR("SDRAM - unsupported module rows for this width\n"); + + /* now check for ECC ability of module. We only support ECC + * on 32 bit wide devices with 8 bit ECC. + */ + if ((read_spd(11)==2) && (read_spd(6)==40) && (read_spd(14)==8)) { + sdram0_ecccfg = 0xf << SDRAM0_ECCCFG_SHIFT; + ecc_on = 1; + } else { + sdram0_ecccfg = 0; + ecc_on = 0; + } + + /*------------------------------------------------------------------ + * calculate total size + * -------------------------------------------------------------------*/ + /* calculate total size and do sanity check */ + tmp = read_spd(31); + total_size = 1 << 22; /* total_size = 4MB */ + /* now multiply 4M by the smallest device row density */ + /* note that we don't support asymetric rows */ + while (((tmp & 0x0001) == 0) && (tmp != 0)) { + total_size = total_size << 1; + tmp = tmp >> 1; + } + total_size *= read_spd(5); /* mult by module rows (dimm sides) */ + + /*------------------------------------------------------------------ + * map rows * cols * banks to a mode + * -------------------------------------------------------------------*/ + + switch (row) { + case 11: + switch (col) { + case 8: + mode=4; /* mode 5 */ + break; + case 9: + case 10: + mode=0; /* mode 1 */ + break; + default: + SPD_ERR("SDRAM - unsupported mode\n"); + } + break; + case 12: + switch (col) { + case 8: + mode=3; /* mode 4 */ + break; + case 9: + case 10: + mode=1; /* mode 2 */ + break; + default: + SPD_ERR("SDRAM - unsupported mode\n"); + } + break; + case 13: + switch (col) { + case 8: + mode=5; /* mode 6 */ + break; + case 9: + case 10: + if (read_spd(17) == 2) + mode = 6; /* mode 7 */ + else + mode = 2; /* mode 3 */ + break; + case 11: + mode = 2; /* mode 3 */ + break; + default: + SPD_ERR("SDRAM - unsupported mode\n"); + } + break; + default: + SPD_ERR("SDRAM - unsupported mode\n"); + } + + /*------------------------------------------------------------------ + * using the calculated values, compute the bank + * config register values. + * -------------------------------------------------------------------*/ + sdram0_b1cr = 0; + sdram0_b2cr = 0; + sdram0_b3cr = 0; + + /* compute the size of each bank */ + bank_size = total_size / bank_cnt; + /* convert bank size to bank size code for ppc4xx + by takeing log2(bank_size) - 22 */ + tmp = bank_size; /* start with tmp = bank_size */ + bank_code = 0; /* and bank_code = 0 */ + while (tmp > 1) { /* this takes log2 of tmp */ + bank_code++; /* and stores result in bank_code */ + tmp = tmp >> 1; + } /* bank_code is now log2(bank_size) */ + bank_code -= 22; /* subtract 22 to get the code */ + + tmp = SDRAM0_BXCR_SZ(bank_code) | SDRAM0_BXCR_AM(mode) | 1; + sdram0_b0cr = (bank_size * 0) | tmp; +#ifndef CONFIG_405EP /* not on PPC405EP */ + if (bank_cnt > 1) + sdram0_b2cr = (bank_size * 1) | tmp; + if (bank_cnt > 2) + sdram0_b1cr = (bank_size * 2) | tmp; + if (bank_cnt > 3) + sdram0_b3cr = (bank_size * 3) | tmp; +#else + /* PPC405EP chip only supports two SDRAM banks */ + if (bank_cnt > 1) + sdram0_b1cr = (bank_size * 1) | tmp; + if (bank_cnt > 2) + total_size = 2 * bank_size; +#endif + + /* + * enable sdram controller DCE=1 + * enable burst read prefetch to 32 bytes BRPF=2 + * leave other functions off + */ + + /*------------------------------------------------------------------ + * now that we've done our calculations, we are ready to + * program all the registers. + * -------------------------------------------------------------------*/ + + /* disable memcontroller so updates work */ + mtsdram(SDRAM0_CFG, 0); + +#ifndef CONFIG_405EP /* not on PPC405EP */ + mtsdram(SDRAM0_BESR0, sdram0_besr0); + mtsdram(SDRAM0_BESR1, sdram0_besr1); + mtsdram(SDRAM0_ECCCFG, sdram0_ecccfg); + mtsdram(SDRAM0_ECCESR, sdram0_eccesr); +#endif + mtsdram(SDRAM0_RTR, sdram0_rtr); + mtsdram(SDRAM0_PMIT, sdram0_pmit); + mtsdram(SDRAM0_B0CR, sdram0_b0cr); + mtsdram(SDRAM0_B1CR, sdram0_b1cr); +#ifndef CONFIG_405EP /* not on PPC405EP */ + mtsdram(SDRAM0_B2CR, sdram0_b2cr); + mtsdram(SDRAM0_B3CR, sdram0_b3cr); +#endif + mtsdram(SDRAM0_TR, sdram0_tr); + + /* SDRAM have a power on delay, 500 micro should do */ + udelay(500); + sdram0_cfg = SDRAM0_CFG_DCE | SDRAM0_CFG_BRPF(1) | SDRAM0_CFG_ECCDD | SDRAM0_CFG_EMDULR; + if (ecc_on) + sdram0_cfg |= SDRAM0_CFG_MEMCHK; + mtsdram(SDRAM0_CFG, sdram0_cfg); + + return (total_size); +} + +int spd_read(uint addr) +{ + uchar data[2]; + + if (i2c_read(SPD_EEPROM_ADDRESS, addr, 1, data, 1) == 0) + return (int)data[0]; + else + return 0; +} + +#endif /* CONFIG_SPD_EEPROM */ diff --git a/arch/powerpc/cpu/ppc4xx/44x_spd_ddr.c b/arch/powerpc/cpu/ppc4xx/44x_spd_ddr.c new file mode 100644 index 0000000..005315b --- /dev/null +++ b/arch/powerpc/cpu/ppc4xx/44x_spd_ddr.c @@ -0,0 +1,1248 @@ +/* + * arch/powerpc/cpu/ppc4xx/44x_spd_ddr.c + * This SPD DDR detection code supports IBM/AMCC PPC44x cpu with a + * DDR controller. Those are 440GP/GX/EP/GR. + * + * (C) Copyright 2001 + * Bill Hunter, Wave 7 Optics, williamhunter@attbi.com + * + * Based on code by: + * + * Kenneth Johansson ,Ericsson AB. + * kenneth.johansson@etx.ericsson.se + * + * hacked up by bill hunter. fixed so we could run before + * serial_init and console_init. previous version avoided this by + * running out of cache memory during serial/console init, then running + * this code later. + * + * (C) Copyright 2002 + * Jun Gu, Artesyn Technology, jung@artesyncp.com + * Support for AMCC 440 based on OpenBIOS draminit.c from IBM. + * + * (C) Copyright 2005-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 + */ + +/* define DEBUG for debugging output (obviously ;-)) */ +#if 0 +#define DEBUG +#endif + +#include <common.h> +#include <asm/processor.h> +#include <i2c.h> +#include <ppc4xx.h> +#include <asm/mmu.h> + +#include "ecc.h" + +#if defined(CONFIG_SPD_EEPROM) && \ + (defined(CONFIG_440GP) || defined(CONFIG_440GX) || \ + defined(CONFIG_440EP) || defined(CONFIG_440GR)) + +/* + * Set default values + */ +#ifndef CONFIG_SYS_I2C_SPEED +#define CONFIG_SYS_I2C_SPEED 50000 +#endif + +#define ONE_BILLION 1000000000 + +/* + * Board-specific Platform code can reimplement spd_ddr_init_hang () if needed + */ +void __spd_ddr_init_hang (void) +{ + hang (); +} +void spd_ddr_init_hang (void) __attribute__((weak, alias("__spd_ddr_init_hang"))); + +/*-----------------------------------------------------------------------------+ + | General Definition + +-----------------------------------------------------------------------------*/ +#define DEFAULT_SPD_ADDR1 0x53 +#define DEFAULT_SPD_ADDR2 0x52 +#define MAXBANKS 4 /* at most 4 dimm banks */ +#define MAX_SPD_BYTES 256 +#define NUMHALFCYCLES 4 +#define NUMMEMTESTS 8 +#define NUMMEMWORDS 8 +#define MAXBXCR 4 +#define TRUE 1 +#define FALSE 0 + +/* + * This DDR2 setup code can dynamically setup the TLB entries for the DDR2 memory + * region. Right now the cache should still be disabled in U-Boot because of the + * EMAC driver, that need it's buffer descriptor to be located in non cached + * memory. + * + * If at some time this restriction doesn't apply anymore, just define + * CONFIG_4xx_DCACHE in the board config file and this code should setup + * everything correctly. + */ +#ifdef CONFIG_4xx_DCACHE +#define MY_TLB_WORD2_I_ENABLE 0 /* enable caching on SDRAM */ +#else +#define MY_TLB_WORD2_I_ENABLE TLB_WORD2_I_ENABLE /* disable caching on SDRAM */ +#endif + +/* bank_parms is used to sort the bank sizes by descending order */ +struct bank_param { + unsigned long cr; + unsigned long bank_size_bytes; +}; + +typedef struct bank_param BANKPARMS; + +#ifdef CONFIG_SYS_SIMULATE_SPD_EEPROM +extern const unsigned char cfg_simulate_spd_eeprom[128]; +#endif + +static unsigned char spd_read(uchar chip, uint addr); +static void get_spd_info(unsigned long *dimm_populated, + unsigned char *iic0_dimm_addr, + unsigned long num_dimm_banks); +static void check_mem_type(unsigned long *dimm_populated, + unsigned char *iic0_dimm_addr, + unsigned long num_dimm_banks); +static void check_volt_type(unsigned long *dimm_populated, + unsigned char *iic0_dimm_addr, + unsigned long num_dimm_banks); +static void program_cfg0(unsigned long *dimm_populated, + unsigned char *iic0_dimm_addr, + unsigned long num_dimm_banks); +static void program_cfg1(unsigned long *dimm_populated, + unsigned char *iic0_dimm_addr, + unsigned long num_dimm_banks); +static void program_rtr(unsigned long *dimm_populated, + unsigned char *iic0_dimm_addr, + unsigned long num_dimm_banks); +static void program_tr0(unsigned long *dimm_populated, + unsigned char *iic0_dimm_addr, + unsigned long num_dimm_banks); +static void program_tr1(void); + +static unsigned long program_bxcr(unsigned long *dimm_populated, + unsigned char *iic0_dimm_addr, + unsigned long num_dimm_banks); + +/* + * This function is reading data from the DIMM module EEPROM over the SPD bus + * and uses that to program the sdram controller. + * + * This works on boards that has the same schematics that the AMCC walnut has. + * + * BUG: Don't handle ECC memory + * BUG: A few values in the TR register is currently hardcoded + */ +long int spd_sdram(void) { + unsigned char iic0_dimm_addr[] = SPD_EEPROM_ADDRESS; + unsigned long dimm_populated[sizeof(iic0_dimm_addr)]; + unsigned long total_size; + unsigned long cfg0; + unsigned long mcsts; + unsigned long num_dimm_banks; /* on board dimm banks */ + + num_dimm_banks = sizeof(iic0_dimm_addr); + + /* + * Make sure I2C controller is initialized + * before continuing. + */ + i2c_init(CONFIG_SYS_I2C_SPEED, CONFIG_SYS_I2C_SLAVE); + + /* + * Read the SPD information using I2C interface. Check to see if the + * DIMM slots are populated. + */ + get_spd_info(dimm_populated, iic0_dimm_addr, num_dimm_banks); + + /* + * Check the memory type for the dimms plugged. + */ + check_mem_type(dimm_populated, iic0_dimm_addr, num_dimm_banks); + + /* + * Check the voltage type for the dimms plugged. + */ + check_volt_type(dimm_populated, iic0_dimm_addr, num_dimm_banks); + +#if defined(CONFIG_440GX) || defined(CONFIG_440EP) || defined(CONFIG_440GR) + /* + * Soft-reset SDRAM controller. + */ + mtsdr(SDR0_SRST, SDR0_SRST_DMC); + mtsdr(SDR0_SRST, 0x00000000); +#endif + + /* + * program 440GP SDRAM controller options (SDRAM0_CFG0) + */ + program_cfg0(dimm_populated, iic0_dimm_addr, num_dimm_banks); + + /* + * program 440GP SDRAM controller options (SDRAM0_CFG1) + */ + program_cfg1(dimm_populated, iic0_dimm_addr, num_dimm_banks); + + /* + * program SDRAM refresh register (SDRAM0_RTR) + */ + program_rtr(dimm_populated, iic0_dimm_addr, num_dimm_banks); + + /* + * program SDRAM Timing Register 0 (SDRAM0_TR0) + */ + program_tr0(dimm_populated, iic0_dimm_addr, num_dimm_banks); + + /* + * program the BxCR registers to find out total sdram installed + */ + total_size = program_bxcr(dimm_populated, iic0_dimm_addr, + num_dimm_banks); + +#ifdef CONFIG_PROG_SDRAM_TLB /* this define should eventually be removed */ + /* and program tlb entries for this size (dynamic) */ + program_tlb(0, 0, total_size, MY_TLB_WORD2_I_ENABLE); +#endif + + /* + * program SDRAM Clock Timing Register (SDRAM0_CLKTR) + */ + mtsdram(SDRAM0_CLKTR, 0x40000000); + + /* + * delay to ensure 200 usec has elapsed + */ + udelay(400); + + /* + * enable the memory controller + */ + mfsdram(SDRAM0_CFG0, cfg0); + mtsdram(SDRAM0_CFG0, cfg0 | SDRAM_CFG0_DCEN); + + /* + * wait for SDRAM_CFG0_DC_EN to complete + */ + while (1) { + mfsdram(SDRAM0_MCSTS, mcsts); + if ((mcsts & SDRAM_MCSTS_MRSC) != 0) + break; + } + + /* + * program SDRAM Timing Register 1, adding some delays + */ + program_tr1(); + +#ifdef CONFIG_DDR_ECC + /* + * If ecc is enabled, initialize the parity bits. + */ + ecc_init(CONFIG_SYS_SDRAM_BASE, total_size); +#endif + + return total_size; +} + +static unsigned char spd_read(uchar chip, uint addr) +{ + unsigned char data[2]; + +#ifdef CONFIG_SYS_SIMULATE_SPD_EEPROM + if (chip == CONFIG_SYS_SIMULATE_SPD_EEPROM) { + /* + * Onboard spd eeprom requested -> simulate values + */ + return cfg_simulate_spd_eeprom[addr]; + } +#endif /* CONFIG_SYS_SIMULATE_SPD_EEPROM */ + + if (i2c_probe(chip) == 0) { + if (i2c_read(chip, addr, 1, data, 1) == 0) { + return data[0]; + } + } + + return 0; +} + +static void get_spd_info(unsigned long *dimm_populated, + unsigned char *iic0_dimm_addr, + unsigned long num_dimm_banks) +{ + unsigned long dimm_num; + unsigned long dimm_found; + unsigned char num_of_bytes; + unsigned char total_size; + + dimm_found = FALSE; + for (dimm_num = 0; dimm_num < num_dimm_banks; dimm_num++) { + num_of_bytes = 0; + total_size = 0; + + num_of_bytes = spd_read(iic0_dimm_addr[dimm_num], 0); + total_size = spd_read(iic0_dimm_addr[dimm_num], 1); + + if ((num_of_bytes != 0) && (total_size != 0)) { + dimm_populated[dimm_num] = TRUE; + dimm_found = TRUE; + debug("DIMM slot %lu: populated\n", dimm_num); + } else { + dimm_populated[dimm_num] = FALSE; + debug("DIMM slot %lu: Not populated\n", dimm_num); + } + } + + if (dimm_found == FALSE) { + printf("ERROR - No memory installed. Install a DDR-SDRAM DIMM.\n\n"); + spd_ddr_init_hang (); + } +} + +static void check_mem_type(unsigned long *dimm_populated, + unsigned char *iic0_dimm_addr, + unsigned long num_dimm_banks) +{ + unsigned long dimm_num; + unsigned char dimm_type; + + for (dimm_num = 0; dimm_num < num_dimm_banks; dimm_num++) { + if (dimm_populated[dimm_num] == TRUE) { + dimm_type = spd_read(iic0_dimm_addr[dimm_num], 2); + switch (dimm_type) { + case 7: + debug("DIMM slot %lu: DDR SDRAM detected\n", dimm_num); + break; + default: + printf("ERROR: Unsupported DIMM detected in slot %lu.\n", + dimm_num); + printf("Only DDR SDRAM DIMMs are supported.\n"); + printf("Replace the DIMM module with a supported DIMM.\n\n"); + spd_ddr_init_hang (); + break; + } + } + } +} + +static void check_volt_type(unsigned long *dimm_populated, + unsigned char *iic0_dimm_addr, + unsigned long num_dimm_banks) +{ + unsigned long dimm_num; + unsigned long voltage_type; + + for (dimm_num = 0; dimm_num < num_dimm_banks; dimm_num++) { + if (dimm_populated[dimm_num] == TRUE) { + voltage_type = spd_read(iic0_dimm_addr[dimm_num], 8); + if (voltage_type != 0x04) { + printf("ERROR: DIMM %lu with unsupported voltage level.\n", + dimm_num); + spd_ddr_init_hang (); + } else { + debug("DIMM %lu voltage level supported.\n", dimm_num); + } + break; + } + } +} + +static void program_cfg0(unsigned long *dimm_populated, + unsigned char *iic0_dimm_addr, + unsigned long num_dimm_banks) +{ + unsigned long dimm_num; + unsigned long cfg0; + unsigned long ecc_enabled; + unsigned char ecc; + unsigned char attributes; + unsigned long data_width; + unsigned long dimm_32bit; + unsigned long dimm_64bit; + + /* + * get Memory Controller Options 0 data + */ + mfsdram(SDRAM0_CFG0, cfg0); + + /* + * clear bits + */ + cfg0 &= ~(SDRAM_CFG0_DCEN | SDRAM_CFG0_MCHK_MASK | + SDRAM_CFG0_RDEN | SDRAM_CFG0_PMUD | + SDRAM_CFG0_DMWD_MASK | + SDRAM_CFG0_UIOS_MASK | SDRAM_CFG0_PDP); + + + /* + * FIXME: assume the DDR SDRAMs in both banks are the same + */ + ecc_enabled = TRUE; + for (dimm_num = 0; dimm_num < num_dimm_banks; dimm_num++) { + if (dimm_populated[dimm_num] == TRUE) { + ecc = spd_read(iic0_dimm_addr[dimm_num], 11); + if (ecc != 0x02) { + ecc_enabled = FALSE; + } + + /* + * program Registered DIMM Enable + */ + attributes = spd_read(iic0_dimm_addr[dimm_num], 21); + if ((attributes & 0x02) != 0x00) { + cfg0 |= SDRAM_CFG0_RDEN; + } + + /* + * program DDR SDRAM Data Width + */ + data_width = + (unsigned long)spd_read(iic0_dimm_addr[dimm_num],6) + + (((unsigned long)spd_read(iic0_dimm_addr[dimm_num],7)) << 8); + if (data_width == 64 || data_width == 72) { + dimm_64bit = TRUE; + cfg0 |= SDRAM_CFG0_DMWD_64; + } else if (data_width == 32 || data_width == 40) { + dimm_32bit = TRUE; + cfg0 |= SDRAM_CFG0_DMWD_32; + } else { + printf("WARNING: DIMM with datawidth of %lu bits.\n", + data_width); + printf("Only DIMMs with 32 or 64 bit datawidths supported.\n"); + spd_ddr_init_hang (); + } + break; + } + } + + /* + * program Memory Data Error Checking + */ + if (ecc_enabled == TRUE) { + cfg0 |= SDRAM_CFG0_MCHK_GEN; + } else { + cfg0 |= SDRAM_CFG0_MCHK_NON; + } + + /* + * program Page Management Unit (0 == enabled) + */ + cfg0 &= ~SDRAM_CFG0_PMUD; + + /* + * program Memory Controller Options 0 + * Note: DCEN must be enabled after all DDR SDRAM controller + * configuration registers get initialized. + */ + mtsdram(SDRAM0_CFG0, cfg0); +} + +static void program_cfg1(unsigned long *dimm_populated, + unsigned char *iic0_dimm_addr, + unsigned long num_dimm_banks) +{ + unsigned long cfg1; + mfsdram(SDRAM0_CFG1, cfg1); + + /* + * Self-refresh exit, disable PM + */ + cfg1 &= ~(SDRAM_CFG1_SRE | SDRAM_CFG1_PMEN); + + /* + * program Memory Controller Options 1 + */ + mtsdram(SDRAM0_CFG1, cfg1); +} + +static void program_rtr(unsigned long *dimm_populated, + unsigned char *iic0_dimm_addr, + unsigned long num_dimm_banks) +{ + unsigned long dimm_num; + unsigned long bus_period_x_10; + unsigned long refresh_rate = 0; + unsigned char refresh_rate_type; + unsigned long refresh_interval; + unsigned long sdram_rtr; + PPC4xx_SYS_INFO sys_info; + + /* + * get the board info + */ + get_sys_info(&sys_info); + bus_period_x_10 = ONE_BILLION / (sys_info.freqPLB / 10); + + for (dimm_num = 0; dimm_num < num_dimm_banks; dimm_num++) { + if (dimm_populated[dimm_num] == TRUE) { + refresh_rate_type = 0x7F & spd_read(iic0_dimm_addr[dimm_num], 12); + switch (refresh_rate_type) { + case 0x00: + refresh_rate = 15625; + break; + case 0x01: + refresh_rate = 15625/4; + break; + case 0x02: + refresh_rate = 15625/2; + break; + case 0x03: + refresh_rate = 15626*2; + break; + case 0x04: + refresh_rate = 15625*4; + break; + case 0x05: + refresh_rate = 15625*8; + break; + default: + printf("ERROR: DIMM %lu, unsupported refresh rate/type.\n", + dimm_num); + printf("Replace the DIMM module with a supported DIMM.\n"); + break; + } + + break; + } + } + + refresh_interval = refresh_rate * 10 / bus_period_x_10; + sdram_rtr = (refresh_interval & 0x3ff8) << 16; + + /* + * program Refresh Timer Register (SDRAM0_RTR) + */ + mtsdram(SDRAM0_RTR, sdram_rtr); +} + +static void program_tr0(unsigned long *dimm_populated, + unsigned char *iic0_dimm_addr, + unsigned long num_dimm_banks) +{ + unsigned long dimm_num; + unsigned long tr0; + unsigned char wcsbc; + unsigned char t_rp_ns; + unsigned char t_rcd_ns; + unsigned char t_ras_ns; + unsigned long t_rp_clk; + unsigned long t_ras_rcd_clk; + unsigned long t_rcd_clk; + unsigned long t_rfc_clk; + unsigned long plb_check; + unsigned char cas_bit; + unsigned long cas_index; + unsigned char cas_2_0_available; + unsigned char cas_2_5_available; + unsigned char cas_3_0_available; + unsigned long cycle_time_ns_x_10[3]; + unsigned long tcyc_3_0_ns_x_10; + unsigned long tcyc_2_5_ns_x_10; + unsigned long tcyc_2_0_ns_x_10; + unsigned long tcyc_reg; + unsigned long bus_period_x_10; + PPC4xx_SYS_INFO sys_info; + unsigned long residue; + + /* + * get the board info + */ + get_sys_info(&sys_info); + bus_period_x_10 = ONE_BILLION / (sys_info.freqPLB / 10); + + /* + * get SDRAM Timing Register 0 (SDRAM_TR0) and clear bits + */ + mfsdram(SDRAM0_TR0, tr0); + tr0 &= ~(SDRAM_TR0_SDWR_MASK | SDRAM_TR0_SDWD_MASK | + SDRAM_TR0_SDCL_MASK | SDRAM_TR0_SDPA_MASK | + SDRAM_TR0_SDCP_MASK | SDRAM_TR0_SDLD_MASK | + SDRAM_TR0_SDRA_MASK | SDRAM_TR0_SDRD_MASK); + + /* + * initialization + */ + wcsbc = 0; + t_rp_ns = 0; + t_rcd_ns = 0; + t_ras_ns = 0; + cas_2_0_available = TRUE; + cas_2_5_available = TRUE; + cas_3_0_available = TRUE; + tcyc_2_0_ns_x_10 = 0; + tcyc_2_5_ns_x_10 = 0; + tcyc_3_0_ns_x_10 = 0; + + for (dimm_num = 0; dimm_num < num_dimm_banks; dimm_num++) { + if (dimm_populated[dimm_num] == TRUE) { + wcsbc = spd_read(iic0_dimm_addr[dimm_num], 15); + t_rp_ns = spd_read(iic0_dimm_addr[dimm_num], 27) >> 2; + t_rcd_ns = spd_read(iic0_dimm_addr[dimm_num], 29) >> 2; + t_ras_ns = spd_read(iic0_dimm_addr[dimm_num], 30); + cas_bit = spd_read(iic0_dimm_addr[dimm_num], 18); + + for (cas_index = 0; cas_index < 3; cas_index++) { + switch (cas_index) { + case 0: + tcyc_reg = spd_read(iic0_dimm_addr[dimm_num], 9); + break; + case 1: + tcyc_reg = spd_read(iic0_dimm_addr[dimm_num], 23); + break; + default: + tcyc_reg = spd_read(iic0_dimm_addr[dimm_num], 25); + break; + } + + if ((tcyc_reg & 0x0F) >= 10) { + printf("ERROR: Tcyc incorrect for DIMM in slot %lu\n", + dimm_num); + spd_ddr_init_hang (); + } + + cycle_time_ns_x_10[cas_index] = + (((tcyc_reg & 0xF0) >> 4) * 10) + (tcyc_reg & 0x0F); + } + + cas_index = 0; + + if ((cas_bit & 0x80) != 0) { + cas_index += 3; + } else if ((cas_bit & 0x40) != 0) { + cas_index += 2; + } else if ((cas_bit & 0x20) != 0) { + cas_index += 1; + } + + if (((cas_bit & 0x10) != 0) && (cas_index < 3)) { + tcyc_3_0_ns_x_10 = cycle_time_ns_x_10[cas_index]; + cas_index++; + } else { + if (cas_index != 0) { + cas_index++; + } + cas_3_0_available = FALSE; + } + + if (((cas_bit & 0x08) != 0) || (cas_index < 3)) { + tcyc_2_5_ns_x_10 = cycle_time_ns_x_10[cas_index]; + cas_index++; + } else { + if (cas_index != 0) { + cas_index++; + } + cas_2_5_available = FALSE; + } + + if (((cas_bit & 0x04) != 0) || (cas_index < 3)) { + tcyc_2_0_ns_x_10 = cycle_time_ns_x_10[cas_index]; + cas_index++; + } else { + if (cas_index != 0) { + cas_index++; + } + cas_2_0_available = FALSE; + } + + break; + } + } + + /* + * Program SD_WR and SD_WCSBC fields + */ + tr0 |= SDRAM_TR0_SDWR_2_CLK; /* Write Recovery: 2 CLK */ + switch (wcsbc) { + case 0: + tr0 |= SDRAM_TR0_SDWD_0_CLK; + break; + default: + tr0 |= SDRAM_TR0_SDWD_1_CLK; + break; + } + + /* + * Program SD_CASL field + */ + if ((cas_2_0_available == TRUE) && + (bus_period_x_10 >= tcyc_2_0_ns_x_10)) { + tr0 |= SDRAM_TR0_SDCL_2_0_CLK; + } else if ((cas_2_5_available == TRUE) && + (bus_period_x_10 >= tcyc_2_5_ns_x_10)) { + tr0 |= SDRAM_TR0_SDCL_2_5_CLK; + } else if ((cas_3_0_available == TRUE) && + (bus_period_x_10 >= tcyc_3_0_ns_x_10)) { + tr0 |= SDRAM_TR0_SDCL_3_0_CLK; + } else { + printf("ERROR: No supported CAS latency with the installed DIMMs.\n"); + printf("Only CAS latencies of 2.0, 2.5, and 3.0 are supported.\n"); + printf("Make sure the PLB speed is within the supported range.\n"); + spd_ddr_init_hang (); + } + + /* + * Calculate Trp in clock cycles and round up if necessary + * Program SD_PTA field + */ + t_rp_clk = sys_info.freqPLB * t_rp_ns / ONE_BILLION; + plb_check = ONE_BILLION * t_rp_clk / t_rp_ns; + if (sys_info.freqPLB != plb_check) { + t_rp_clk++; + } + switch ((unsigned long)t_rp_clk) { + case 0: + case 1: + case 2: + tr0 |= SDRAM_TR0_SDPA_2_CLK; + break; + case 3: + tr0 |= SDRAM_TR0_SDPA_3_CLK; + break; + default: + tr0 |= SDRAM_TR0_SDPA_4_CLK; + break; + } + + /* + * Program SD_CTP field + */ + t_ras_rcd_clk = sys_info.freqPLB * (t_ras_ns - t_rcd_ns) / ONE_BILLION; + plb_check = ONE_BILLION * t_ras_rcd_clk / (t_ras_ns - t_rcd_ns); + if (sys_info.freqPLB != plb_check) { + t_ras_rcd_clk++; + } + switch (t_ras_rcd_clk) { + case 0: + case 1: + case 2: + tr0 |= SDRAM_TR0_SDCP_2_CLK; + break; + case 3: + tr0 |= SDRAM_TR0_SDCP_3_CLK; + break; + case 4: + tr0 |= SDRAM_TR0_SDCP_4_CLK; + break; + default: + tr0 |= SDRAM_TR0_SDCP_5_CLK; + break; + } + + /* + * Program SD_LDF field + */ + tr0 |= SDRAM_TR0_SDLD_2_CLK; + + /* + * Program SD_RFTA field + * FIXME tRFC hardcoded as 75 nanoseconds + */ + t_rfc_clk = sys_info.freqPLB / (ONE_BILLION / 75); + residue = sys_info.freqPLB % (ONE_BILLION / 75); + if (residue >= (ONE_BILLION / 150)) { + t_rfc_clk++; + } + switch (t_rfc_clk) { + case 0: + case 1: + case 2: + case 3: + case 4: + case 5: + case 6: + tr0 |= SDRAM_TR0_SDRA_6_CLK; + break; + case 7: + tr0 |= SDRAM_TR0_SDRA_7_CLK; + break; + case 8: + tr0 |= SDRAM_TR0_SDRA_8_CLK; + break; + case 9: + tr0 |= SDRAM_TR0_SDRA_9_CLK; + break; + case 10: + tr0 |= SDRAM_TR0_SDRA_10_CLK; + break; + case 11: + tr0 |= SDRAM_TR0_SDRA_11_CLK; + break; + case 12: + tr0 |= SDRAM_TR0_SDRA_12_CLK; + break; + default: + tr0 |= SDRAM_TR0_SDRA_13_CLK; + break; + } + + /* + * Program SD_RCD field + */ + t_rcd_clk = sys_info.freqPLB * t_rcd_ns / ONE_BILLION; + plb_check = ONE_BILLION * t_rcd_clk / t_rcd_ns; + if (sys_info.freqPLB != plb_check) { + t_rcd_clk++; + } + switch (t_rcd_clk) { + case 0: + case 1: + case 2: + tr0 |= SDRAM_TR0_SDRD_2_CLK; + break; + case 3: + tr0 |= SDRAM_TR0_SDRD_3_CLK; + break; + default: + tr0 |= SDRAM_TR0_SDRD_4_CLK; + break; + } + + debug("tr0: %x\n", tr0); + mtsdram(SDRAM0_TR0, tr0); +} + +static int short_mem_test(void) +{ + unsigned long i, j; + unsigned long bxcr_num; + unsigned long *membase; + const unsigned long test[NUMMEMTESTS][NUMMEMWORDS] = { + {0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, + 0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF}, + {0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000, + 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000}, + {0xAAAAAAAA, 0xAAAAAAAA, 0x55555555, 0x55555555, + 0xAAAAAAAA, 0xAAAAAAAA, 0x55555555, 0x55555555}, + {0x55555555, 0x55555555, 0xAAAAAAAA, 0xAAAAAAAA, + 0x55555555, 0x55555555, 0xAAAAAAAA, 0xAAAAAAAA}, + {0xA5A5A5A5, 0xA5A5A5A5, 0x5A5A5A5A, 0x5A5A5A5A, + 0xA5A5A5A5, 0xA5A5A5A5, 0x5A5A5A5A, 0x5A5A5A5A}, + {0x5A5A5A5A, 0x5A5A5A5A, 0xA5A5A5A5, 0xA5A5A5A5, + 0x5A5A5A5A, 0x5A5A5A5A, 0xA5A5A5A5, 0xA5A5A5A5}, + {0xAA55AA55, 0xAA55AA55, 0x55AA55AA, 0x55AA55AA, + 0xAA55AA55, 0xAA55AA55, 0x55AA55AA, 0x55AA55AA}, + {0x55AA55AA, 0x55AA55AA, 0xAA55AA55, 0xAA55AA55, + 0x55AA55AA, 0x55AA55AA, 0xAA55AA55, 0xAA55AA55}}; + + for (bxcr_num = 0; bxcr_num < MAXBXCR; bxcr_num++) { + mtdcr(SDRAM0_CFGADDR, SDRAM0_B0CR + (bxcr_num << 2)); + if ((mfdcr(SDRAM0_CFGDATA) & SDRAM_BXCR_SDBE) == SDRAM_BXCR_SDBE) { + /* Bank is enabled */ + membase = (unsigned long*) + (mfdcr(SDRAM0_CFGDATA) & SDRAM_BXCR_SDBA_MASK); + + /* + * Run the short memory test + */ + for (i = 0; i < NUMMEMTESTS; i++) { + for (j = 0; j < NUMMEMWORDS; j++) { + /* printf("bank enabled base:%x\n", &membase[j]); */ + membase[j] = test[i][j]; + ppcDcbf((unsigned long)&(membase[j])); + } + + for (j = 0; j < NUMMEMWORDS; j++) { + if (membase[j] != test[i][j]) { + ppcDcbf((unsigned long)&(membase[j])); + return 0; + } + ppcDcbf((unsigned long)&(membase[j])); + } + + if (j < NUMMEMWORDS) + return 0; + } + + /* + * see if the rdclt value passed + */ + if (i < NUMMEMTESTS) + return 0; + } + } + + return 1; +} + +static void program_tr1(void) +{ + unsigned long tr0; + unsigned long tr1; + unsigned long cfg0; + unsigned long ecc_temp; + unsigned long dlycal; + unsigned long dly_val; + unsigned long k; + unsigned long max_pass_length; + unsigned long current_pass_length; + unsigned long current_fail_length; + unsigned long current_start; + unsigned long rdclt; + unsigned long rdclt_offset; + long max_start; + long max_end; + long rdclt_average; + unsigned char window_found; + unsigned char fail_found; + unsigned char pass_found; + PPC4xx_SYS_INFO sys_info; + + /* + * get the board info + */ + get_sys_info(&sys_info); + + /* + * get SDRAM Timing Register 0 (SDRAM_TR0) and clear bits + */ + mfsdram(SDRAM0_TR1, tr1); + tr1 &= ~(SDRAM_TR1_RDSS_MASK | SDRAM_TR1_RDSL_MASK | + SDRAM_TR1_RDCD_MASK | SDRAM_TR1_RDCT_MASK); + + mfsdram(SDRAM0_TR0, tr0); + if (((tr0 & SDRAM_TR0_SDCL_MASK) == SDRAM_TR0_SDCL_2_5_CLK) && + (sys_info.freqPLB > 100000000)) { + tr1 |= SDRAM_TR1_RDSS_TR2; + tr1 |= SDRAM_TR1_RDSL_STAGE3; + tr1 |= SDRAM_TR1_RDCD_RCD_1_2; + } else { + tr1 |= SDRAM_TR1_RDSS_TR1; + tr1 |= SDRAM_TR1_RDSL_STAGE2; + tr1 |= SDRAM_TR1_RDCD_RCD_0_0; + } + + /* + * save CFG0 ECC setting to a temporary variable and turn ECC off + */ + mfsdram(SDRAM0_CFG0, cfg0); + ecc_temp = cfg0 & SDRAM_CFG0_MCHK_MASK; + mtsdram(SDRAM0_CFG0, (cfg0 & ~SDRAM_CFG0_MCHK_MASK) | SDRAM_CFG0_MCHK_NON); + + /* + * get the delay line calibration register value + */ + mfsdram(SDRAM0_DLYCAL, dlycal); + dly_val = SDRAM_DLYCAL_DLCV_DECODE(dlycal) << 2; + + max_pass_length = 0; + max_start = 0; + max_end = 0; + current_pass_length = 0; + current_fail_length = 0; + current_start = 0; + rdclt_offset = 0; + window_found = FALSE; + fail_found = FALSE; + pass_found = FALSE; + debug("Starting memory test "); + + for (k = 0; k < NUMHALFCYCLES; k++) { + for (rdclt = 0; rdclt < dly_val; rdclt++) { + /* + * Set the timing reg for the test. + */ + mtsdram(SDRAM0_TR1, (tr1 | SDRAM_TR1_RDCT_ENCODE(rdclt))); + + if (short_mem_test()) { + if (fail_found == TRUE) { + pass_found = TRUE; + if (current_pass_length == 0) { + current_start = rdclt_offset + rdclt; + } + + current_fail_length = 0; + current_pass_length++; + + if (current_pass_length > max_pass_length) { + max_pass_length = current_pass_length; + max_start = current_start; + max_end = rdclt_offset + rdclt; + } + } + } else { + current_pass_length = 0; + current_fail_length++; + + if (current_fail_length >= (dly_val>>2)) { + if (fail_found == FALSE) { + fail_found = TRUE; + } else if (pass_found == TRUE) { + window_found = TRUE; + break; + } + } + } + } + debug("."); + + if (window_found == TRUE) { + break; + } + + tr1 = tr1 ^ SDRAM_TR1_RDCD_MASK; + rdclt_offset += dly_val; + } + debug("\n"); + + /* + * make sure we find the window + */ + if (window_found == FALSE) { + printf("ERROR: Cannot determine a common read delay.\n"); + spd_ddr_init_hang (); + } + + /* + * restore the orignal ECC setting + */ + mtsdram(SDRAM0_CFG0, (cfg0 & ~SDRAM_CFG0_MCHK_MASK) | ecc_temp); + + /* + * set the SDRAM TR1 RDCD value + */ + tr1 &= ~SDRAM_TR1_RDCD_MASK; + if ((tr0 & SDRAM_TR0_SDCL_MASK) == SDRAM_TR0_SDCL_2_5_CLK) { + tr1 |= SDRAM_TR1_RDCD_RCD_1_2; + } else { + tr1 |= SDRAM_TR1_RDCD_RCD_0_0; + } + + /* + * set the SDRAM TR1 RDCLT value + */ + tr1 &= ~SDRAM_TR1_RDCT_MASK; + while (max_end >= (dly_val << 1)) { + max_end -= (dly_val << 1); + max_start -= (dly_val << 1); + } + + rdclt_average = ((max_start + max_end) >> 1); + + if (rdclt_average < 0) { + rdclt_average = 0; + } + + if (rdclt_average >= dly_val) { + rdclt_average -= dly_val; + tr1 = tr1 ^ SDRAM_TR1_RDCD_MASK; + } + tr1 |= SDRAM_TR1_RDCT_ENCODE(rdclt_average); + + debug("tr1: %x\n", tr1); + + /* + * program SDRAM Timing Register 1 TR1 + */ + mtsdram(SDRAM0_TR1, tr1); +} + +static unsigned long program_bxcr(unsigned long *dimm_populated, + unsigned char *iic0_dimm_addr, + unsigned long num_dimm_banks) +{ + unsigned long dimm_num; + unsigned long bank_base_addr; + unsigned long cr; + unsigned long i; + unsigned long j; + unsigned long temp; + unsigned char num_row_addr; + unsigned char num_col_addr; + unsigned char num_banks; + unsigned char bank_size_id; + unsigned long ctrl_bank_num[MAXBANKS]; + unsigned long bx_cr_num; + unsigned long largest_size_index; + unsigned long largest_size; + unsigned long current_size_index; + BANKPARMS bank_parms[MAXBXCR]; + unsigned long sorted_bank_num[MAXBXCR]; /* DDR Controller bank number table (sorted by size) */ + unsigned long sorted_bank_size[MAXBXCR]; /* DDR Controller bank size table (sorted by size)*/ + + /* + * Set the BxCR regs. First, wipe out the bank config registers. + */ + for (bx_cr_num = 0; bx_cr_num < MAXBXCR; bx_cr_num++) { + mtdcr(SDRAM0_CFGADDR, SDRAM0_B0CR + (bx_cr_num << 2)); + mtdcr(SDRAM0_CFGDATA, 0x00000000); + bank_parms[bx_cr_num].bank_size_bytes = 0; + } + +#ifdef CONFIG_BAMBOO + /* + * This next section is hardware dependent and must be programmed + * to match the hardware. For bamboo, the following holds... + * 1. SDRAM0_B0CR: Bank 0 of dimm 0 ctrl_bank_num : 0 (soldered onboard) + * 2. SDRAM0_B1CR: Bank 0 of dimm 1 ctrl_bank_num : 1 + * 3. SDRAM0_B2CR: Bank 1 of dimm 1 ctrl_bank_num : 1 + * 4. SDRAM0_B3CR: Bank 0 of dimm 2 ctrl_bank_num : 3 + * ctrl_bank_num corresponds to the first usable DDR controller bank number by DIMM + */ + ctrl_bank_num[0] = 0; + ctrl_bank_num[1] = 1; + ctrl_bank_num[2] = 3; +#else + /* + * Ocotea, Ebony and the other IBM/AMCC eval boards have + * 2 DIMM slots with each max 2 banks + */ + ctrl_bank_num[0] = 0; + ctrl_bank_num[1] = 2; +#endif + + /* + * reset the bank_base address + */ + bank_base_addr = CONFIG_SYS_SDRAM_BASE; + + for (dimm_num = 0; dimm_num < num_dimm_banks; dimm_num++) { + if (dimm_populated[dimm_num] == TRUE) { + num_row_addr = spd_read(iic0_dimm_addr[dimm_num], 3); + num_col_addr = spd_read(iic0_dimm_addr[dimm_num], 4); + num_banks = spd_read(iic0_dimm_addr[dimm_num], 5); + bank_size_id = spd_read(iic0_dimm_addr[dimm_num], 31); + debug("DIMM%d: row=%d col=%d banks=%d\n", dimm_num, + num_row_addr, num_col_addr, num_banks); + + /* + * Set the SDRAM0_BxCR regs + */ + cr = 0; + switch (bank_size_id) { + case 0x02: + cr |= SDRAM_BXCR_SDSZ_8; + break; + case 0x04: + cr |= SDRAM_BXCR_SDSZ_16; + break; + case 0x08: + cr |= SDRAM_BXCR_SDSZ_32; + break; + case 0x10: + cr |= SDRAM_BXCR_SDSZ_64; + break; + case 0x20: + cr |= SDRAM_BXCR_SDSZ_128; + break; + case 0x40: + cr |= SDRAM_BXCR_SDSZ_256; + break; + case 0x80: + cr |= SDRAM_BXCR_SDSZ_512; + break; + default: + printf("DDR-SDRAM: DIMM %lu BxCR configuration.\n", + dimm_num); + printf("ERROR: Unsupported value for the banksize: %d.\n", + bank_size_id); + printf("Replace the DIMM module with a supported DIMM.\n\n"); + spd_ddr_init_hang (); + } + + switch (num_col_addr) { + case 0x08: + cr |= SDRAM_BXCR_SDAM_1; + break; + case 0x09: + cr |= SDRAM_BXCR_SDAM_2; + break; + case 0x0A: + cr |= SDRAM_BXCR_SDAM_3; + break; + case 0x0B: + cr |= SDRAM_BXCR_SDAM_4; + break; + default: + printf("DDR-SDRAM: DIMM %lu BxCR configuration.\n", + dimm_num); + printf("ERROR: Unsupported value for number of " + "column addresses: %d.\n", num_col_addr); + printf("Replace the DIMM module with a supported DIMM.\n\n"); + spd_ddr_init_hang (); + } + + /* + * enable the bank + */ + cr |= SDRAM_BXCR_SDBE; + + for (i = 0; i < num_banks; i++) { + bank_parms[ctrl_bank_num[dimm_num]+i].bank_size_bytes = + (4 << 20) * bank_size_id; + bank_parms[ctrl_bank_num[dimm_num]+i].cr = cr; + debug("DIMM%d-bank %d (SDRAM0_B%dCR): bank_size_bytes=%d\n", + dimm_num, i, ctrl_bank_num[dimm_num]+i, + bank_parms[ctrl_bank_num[dimm_num]+i].bank_size_bytes); + } + } + } + + /* Initialize sort tables */ + for (i = 0; i < MAXBXCR; i++) { + sorted_bank_num[i] = i; + sorted_bank_size[i] = bank_parms[i].bank_size_bytes; + } + + for (i = 0; i < MAXBXCR-1; i++) { + largest_size = sorted_bank_size[i]; + largest_size_index = 255; + + /* Find the largest remaining value */ + for (j = i + 1; j < MAXBXCR; j++) { + if (sorted_bank_size[j] > largest_size) { + /* Save largest remaining value and its index */ + largest_size = sorted_bank_size[j]; + largest_size_index = j; + } + } + + if (largest_size_index != 255) { + /* Swap the current and largest values */ + current_size_index = sorted_bank_num[largest_size_index]; + sorted_bank_size[largest_size_index] = sorted_bank_size[i]; + sorted_bank_size[i] = largest_size; + sorted_bank_num[largest_size_index] = sorted_bank_num[i]; + sorted_bank_num[i] = current_size_index; + } + } + + /* Set the SDRAM0_BxCR regs thanks to sort tables */ + for (bx_cr_num = 0, bank_base_addr = 0; bx_cr_num < MAXBXCR; bx_cr_num++) { + if (bank_parms[sorted_bank_num[bx_cr_num]].bank_size_bytes) { + mtdcr(SDRAM0_CFGADDR, SDRAM0_B0CR + (sorted_bank_num[bx_cr_num] << 2)); + temp = mfdcr(SDRAM0_CFGDATA) & ~(SDRAM_BXCR_SDBA_MASK | SDRAM_BXCR_SDSZ_MASK | + SDRAM_BXCR_SDAM_MASK | SDRAM_BXCR_SDBE); + temp = temp | (bank_base_addr & SDRAM_BXCR_SDBA_MASK) | + bank_parms[sorted_bank_num[bx_cr_num]].cr; + mtdcr(SDRAM0_CFGDATA, temp); + bank_base_addr += bank_parms[sorted_bank_num[bx_cr_num]].bank_size_bytes; + debug("SDRAM0_B%dCR=0x%08lx\n", sorted_bank_num[bx_cr_num], temp); + } + } + + return(bank_base_addr); +} +#endif /* CONFIG_SPD_EEPROM */ diff --git a/arch/powerpc/cpu/ppc4xx/44x_spd_ddr2.c b/arch/powerpc/cpu/ppc4xx/44x_spd_ddr2.c new file mode 100644 index 0000000..faddee9 --- /dev/null +++ b/arch/powerpc/cpu/ppc4xx/44x_spd_ddr2.c @@ -0,0 +1,3174 @@ +/* + * arch/powerpc/cpu/ppc4xx/44x_spd_ddr2.c + * This SPD SDRAM detection code supports AMCC PPC44x cpu's with a + * DDR2 controller (non Denali Core). Those currently are: + * + * 405: 405EX(r) + * 440/460: 440SP/440SPe/460EX/460GT + * + * Copyright (c) 2008 Nuovation System Designs, LLC + * Grant Erickson <gerickson@nuovations.com> + + * (C) Copyright 2007-2009 + * Stefan Roese, DENX Software Engineering, sr@denx.de. + * + * COPYRIGHT AMCC CORPORATION 2004 + * + * 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 + * + */ + +/* define DEBUG for debugging output (obviously ;-)) */ +#if 0 +#define DEBUG +#endif + +#include <common.h> +#include <command.h> +#include <ppc4xx.h> +#include <i2c.h> +#include <asm/io.h> +#include <asm/processor.h> +#include <asm/mmu.h> +#include <asm/cache.h> + +#include "ecc.h" + +#if defined(CONFIG_SDRAM_PPC4xx_IBM_DDR2) + +#define PPC4xx_IBM_DDR2_DUMP_REGISTER(mnemonic) \ + do { \ + u32 data; \ + mfsdram(SDRAM_##mnemonic, data); \ + printf("%20s[%02x] = 0x%08X\n", \ + "SDRAM_" #mnemonic, SDRAM_##mnemonic, data); \ + } while (0) + +#define PPC4xx_IBM_DDR2_DUMP_MQ_REGISTER(mnemonic) \ + do { \ + u32 data; \ + data = mfdcr(SDRAM_##mnemonic); \ + printf("%20s[%02x] = 0x%08X\n", \ + "SDRAM_" #mnemonic, SDRAM_##mnemonic, data); \ + } while (0) + +#if defined(CONFIG_440) +/* + * This DDR2 setup code can dynamically setup the TLB entries for the DDR2 + * memory region. Right now the cache should still be disabled in U-Boot + * because of the EMAC driver, that need its buffer descriptor to be located + * in non cached memory. + * + * If at some time this restriction doesn't apply anymore, just define + * CONFIG_4xx_DCACHE in the board config file and this code should setup + * everything correctly. + */ +#ifdef CONFIG_4xx_DCACHE +/* enable caching on SDRAM */ +#define MY_TLB_WORD2_I_ENABLE 0 +#else +/* disable caching on SDRAM */ +#define MY_TLB_WORD2_I_ENABLE TLB_WORD2_I_ENABLE +#endif /* CONFIG_4xx_DCACHE */ + +void dcbz_area(u32 start_address, u32 num_bytes); +#endif /* CONFIG_440 */ + +#define MAXRANKS 4 +#define MAXBXCF 4 + +#define MULDIV64(m1, m2, d) (u32)(((u64)(m1) * (u64)(m2)) / (u64)(d)) + +#if !defined(CONFIG_NAND_SPL) +/*-----------------------------------------------------------------------------+ + * sdram_memsize + *-----------------------------------------------------------------------------*/ +phys_size_t sdram_memsize(void) +{ + phys_size_t mem_size; + unsigned long mcopt2; + unsigned long mcstat; + unsigned long mb0cf; + unsigned long sdsz; + unsigned long i; + + mem_size = 0; + + mfsdram(SDRAM_MCOPT2, mcopt2); + mfsdram(SDRAM_MCSTAT, mcstat); + + /* DDR controller must be enabled and not in self-refresh. */ + /* Otherwise memsize is zero. */ + if (((mcopt2 & SDRAM_MCOPT2_DCEN_MASK) == SDRAM_MCOPT2_DCEN_ENABLE) + && ((mcopt2 & SDRAM_MCOPT2_SREN_MASK) == SDRAM_MCOPT2_SREN_EXIT) + && ((mcstat & (SDRAM_MCSTAT_MIC_MASK | SDRAM_MCSTAT_SRMS_MASK)) + == (SDRAM_MCSTAT_MIC_COMP | SDRAM_MCSTAT_SRMS_NOT_SF))) { + for (i = 0; i < MAXBXCF; i++) { + mfsdram(SDRAM_MB0CF + (i << 2), mb0cf); + /* Banks enabled */ + if ((mb0cf & SDRAM_BXCF_M_BE_MASK) == SDRAM_BXCF_M_BE_ENABLE) { +#if defined(CONFIG_440) + sdsz = mfdcr_any(SDRAM_R0BAS + i) & SDRAM_RXBAS_SDSZ_MASK; +#else + sdsz = mb0cf & SDRAM_RXBAS_SDSZ_MASK; +#endif + switch(sdsz) { + case SDRAM_RXBAS_SDSZ_8: + mem_size+=8; + break; + case SDRAM_RXBAS_SDSZ_16: + mem_size+=16; + break; + case SDRAM_RXBAS_SDSZ_32: + mem_size+=32; + break; + case SDRAM_RXBAS_SDSZ_64: + mem_size+=64; + break; + case SDRAM_RXBAS_SDSZ_128: + mem_size+=128; + break; + case SDRAM_RXBAS_SDSZ_256: + mem_size+=256; + break; + case SDRAM_RXBAS_SDSZ_512: + mem_size+=512; + break; + case SDRAM_RXBAS_SDSZ_1024: + mem_size+=1024; + break; + case SDRAM_RXBAS_SDSZ_2048: + mem_size+=2048; + break; + case SDRAM_RXBAS_SDSZ_4096: + mem_size+=4096; + break; + default: + printf("WARNING: Unsupported bank size (SDSZ=0x%lx)!\n" + , sdsz); + mem_size=0; + break; + } + } + } + } + + return mem_size << 20; +} + +/*-----------------------------------------------------------------------------+ + * is_ecc_enabled + *-----------------------------------------------------------------------------*/ +static unsigned long is_ecc_enabled(void) +{ + unsigned long val; + + mfsdram(SDRAM_MCOPT1, val); + + return SDRAM_MCOPT1_MCHK_CHK_DECODE(val); +} + +/*-----------------------------------------------------------------------------+ + * board_add_ram_info + *-----------------------------------------------------------------------------*/ +void board_add_ram_info(int use_default) +{ + PPC4xx_SYS_INFO board_cfg; + u32 val; + + if (is_ecc_enabled()) + puts(" (ECC"); + else + puts(" (ECC not"); + + get_sys_info(&board_cfg); + +#if defined(CONFIG_405EX) + val = board_cfg.freqPLB; +#else + mfsdr(SDR0_DDR0, val); + val = MULDIV64((board_cfg.freqPLB), SDR0_DDR0_DDRM_DECODE(val), 1); +#endif + printf(" enabled, %d MHz", (val * 2) / 1000000); + + mfsdram(SDRAM_MMODE, val); + val = (val & SDRAM_MMODE_DCL_MASK) >> 4; + printf(", CL%d)", val); +} +#endif /* !CONFIG_NAND_SPL */ + +#if defined(CONFIG_SPD_EEPROM) + +/*-----------------------------------------------------------------------------+ + * Defines + *-----------------------------------------------------------------------------*/ +#ifndef TRUE +#define TRUE 1 +#endif +#ifndef FALSE +#define FALSE 0 +#endif + +#define SDRAM_DDR1 1 +#define SDRAM_DDR2 2 +#define SDRAM_NONE 0 + +#define MAXDIMMS 2 +#define MAX_SPD_BYTES 256 /* Max number of bytes on the DIMM's SPD EEPROM */ + +#define ONE_BILLION 1000000000 + +#define CMD_NOP (7 << 19) +#define CMD_PRECHARGE (2 << 19) +#define CMD_REFRESH (1 << 19) +#define CMD_EMR (0 << 19) +#define CMD_READ (5 << 19) +#define CMD_WRITE (4 << 19) + +#define SELECT_MR (0 << 16) +#define SELECT_EMR (1 << 16) +#define SELECT_EMR2 (2 << 16) +#define SELECT_EMR3 (3 << 16) + +/* MR */ +#define DLL_RESET 0x00000100 + +#define WRITE_RECOV_2 (1 << 9) +#define WRITE_RECOV_3 (2 << 9) +#define WRITE_RECOV_4 (3 << 9) +#define WRITE_RECOV_5 (4 << 9) +#define WRITE_RECOV_6 (5 << 9) + +#define BURST_LEN_4 0x00000002 + +/* EMR */ +#define ODT_0_OHM 0x00000000 +#define ODT_50_OHM 0x00000044 +#define ODT_75_OHM 0x00000004 +#define ODT_150_OHM 0x00000040 + +#define ODS_FULL 0x00000000 +#define ODS_REDUCED 0x00000002 +#define OCD_CALIB_DEF 0x00000380 + +/* defines for ODT (On Die Termination) of the 440SP(e) DDR2 controller */ +#define ODT_EB0R (0x80000000 >> 8) +#define ODT_EB0W (0x80000000 >> 7) +#define CALC_ODT_R(n) (ODT_EB0R << (n << 1)) +#define CALC_ODT_W(n) (ODT_EB0W << (n << 1)) +#define CALC_ODT_RW(n) (CALC_ODT_R(n) | CALC_ODT_W(n)) + +/* Defines for the Read Cycle Delay test */ +#define NUMMEMTESTS 8 +#define NUMMEMWORDS 8 +#define NUMLOOPS 64 /* memory test loops */ + +/* + * Newer PPC's like 440SPe, 460EX/GT can be equipped with more than 2GB of SDRAM. + * To support such configurations, we "only" map the first 2GB via the TLB's. We + * need some free virtual address space for the remaining peripherals like, SoC + * devices, FLASH etc. + * + * Note that ECC is currently not supported on configurations with more than 2GB + * SDRAM. This is because we only map the first 2GB on such systems, and therefore + * the ECC parity byte of the remaining area can't be written. + */ + +/* + * Board-specific Platform code can reimplement spd_ddr_init_hang () if needed + */ +void __spd_ddr_init_hang (void) +{ + hang (); +} +void spd_ddr_init_hang (void) __attribute__((weak, alias("__spd_ddr_init_hang"))); + +/* + * To provide an interface for board specific config values in this common + * DDR setup code, we implement he "weak" default functions here. They return + * the default value back to the caller. + * + * Please see include/configs/yucca.h for an example fora board specific + * implementation. + */ +u32 __ddr_wrdtr(u32 default_val) +{ + return default_val; +} +u32 ddr_wrdtr(u32) __attribute__((weak, alias("__ddr_wrdtr"))); + +u32 __ddr_clktr(u32 default_val) +{ + return default_val; +} +u32 ddr_clktr(u32) __attribute__((weak, alias("__ddr_clktr"))); + + +/* Private Structure Definitions */ + +/* enum only to ease code for cas latency setting */ +typedef enum ddr_cas_id { + DDR_CAS_2 = 20, + DDR_CAS_2_5 = 25, + DDR_CAS_3 = 30, + DDR_CAS_4 = 40, + DDR_CAS_5 = 50 +} ddr_cas_id_t; + +/*-----------------------------------------------------------------------------+ + * Prototypes + *-----------------------------------------------------------------------------*/ +static void get_spd_info(unsigned long *dimm_populated, + unsigned char *iic0_dimm_addr, + unsigned long num_dimm_banks); +static void check_mem_type(unsigned long *dimm_populated, + unsigned char *iic0_dimm_addr, + unsigned long num_dimm_banks); +static void check_frequency(unsigned long *dimm_populated, + unsigned char *iic0_dimm_addr, + unsigned long num_dimm_banks); +static void check_rank_number(unsigned long *dimm_populated, + unsigned char *iic0_dimm_addr, + unsigned long num_dimm_banks); +static void check_voltage_type(unsigned long *dimm_populated, + unsigned char *iic0_dimm_addr, + unsigned long num_dimm_banks); +static void program_memory_queue(unsigned long *dimm_populated, + unsigned char *iic0_dimm_addr, + unsigned long num_dimm_banks); +static void program_codt(unsigned long *dimm_populated, + unsigned char *iic0_dimm_addr, + unsigned long num_dimm_banks); +static void program_mode(unsigned long *dimm_populated, + unsigned char *iic0_dimm_addr, + unsigned long num_dimm_banks, + ddr_cas_id_t *selected_cas, + int *write_recovery); +static void program_tr(unsigned long *dimm_populated, + unsigned char *iic0_dimm_addr, + unsigned long num_dimm_banks); +static void program_rtr(unsigned long *dimm_populated, + unsigned char *iic0_dimm_addr, + unsigned long num_dimm_banks); +static void program_bxcf(unsigned long *dimm_populated, + unsigned char *iic0_dimm_addr, + unsigned long num_dimm_banks); +static void program_copt1(unsigned long *dimm_populated, + unsigned char *iic0_dimm_addr, + unsigned long num_dimm_banks); +static void program_initplr(unsigned long *dimm_populated, + unsigned char *iic0_dimm_addr, + unsigned long num_dimm_banks, + ddr_cas_id_t selected_cas, + int write_recovery); +#ifdef CONFIG_DDR_ECC +static void program_ecc(unsigned long *dimm_populated, + unsigned char *iic0_dimm_addr, + unsigned long num_dimm_banks, + unsigned long tlb_word2_i_value); +#endif +#if !defined(CONFIG_PPC4xx_DDR_AUTOCALIBRATION) +static void program_DQS_calibration(unsigned long *dimm_populated, + unsigned char *iic0_dimm_addr, + unsigned long num_dimm_banks); +#ifdef HARD_CODED_DQS /* calibration test with hardvalues */ +static void test(void); +#else +static void DQS_calibration_process(void); +#endif +#endif +int do_reset (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]); + +static unsigned char spd_read(uchar chip, uint addr) +{ + unsigned char data[2]; + + if (i2c_probe(chip) == 0) + if (i2c_read(chip, addr, 1, data, 1) == 0) + return data[0]; + + return 0; +} + +/*-----------------------------------------------------------------------------+ + * initdram. Initializes the 440SP Memory Queue and DDR SDRAM controller. + * Note: This routine runs from flash with a stack set up in the chip's + * sram space. It is important that the routine does not require .sbss, .bss or + * .data sections. It also cannot call routines that require these sections. + *-----------------------------------------------------------------------------*/ +/*----------------------------------------------------------------------------- + * Function: initdram + * Description: Configures SDRAM memory banks for DDR operation. + * Auto Memory Configuration option reads the DDR SDRAM EEPROMs + * via the IIC bus and then configures the DDR SDRAM memory + * banks appropriately. If Auto Memory Configuration is + * not used, it is assumed that no DIMM is plugged + *-----------------------------------------------------------------------------*/ +phys_size_t initdram(int board_type) +{ + unsigned char iic0_dimm_addr[] = SPD_EEPROM_ADDRESS; + unsigned char spd0[MAX_SPD_BYTES]; + unsigned char spd1[MAX_SPD_BYTES]; + unsigned char *dimm_spd[MAXDIMMS]; + unsigned long dimm_populated[MAXDIMMS] = {SDRAM_NONE, SDRAM_NONE}; + unsigned long num_dimm_banks; /* on board dimm banks */ + unsigned long val; + ddr_cas_id_t selected_cas = DDR_CAS_5; /* preset to silence compiler */ + int write_recovery; + phys_size_t dram_size = 0; + + num_dimm_banks = sizeof(iic0_dimm_addr); + + /*------------------------------------------------------------------ + * Set up an array of SPD matrixes. + *-----------------------------------------------------------------*/ + dimm_spd[0] = spd0; + dimm_spd[1] = spd1; + + /*------------------------------------------------------------------ + * Reset the DDR-SDRAM controller. + *-----------------------------------------------------------------*/ + mtsdr(SDR0_SRST, (0x80000000 >> 10)); + mtsdr(SDR0_SRST, 0x00000000); + + /* + * Make sure I2C controller is initialized + * before continuing. + */ + + /* switch to correct I2C bus */ + I2C_SET_BUS(CONFIG_SYS_SPD_BUS_NUM); + i2c_init(CONFIG_SYS_I2C_SPEED, CONFIG_SYS_I2C_SLAVE); + + /*------------------------------------------------------------------ + * Clear out the serial presence detect buffers. + * Perform IIC reads from the dimm. Fill in the spds. + * Check to see if the dimm slots are populated + *-----------------------------------------------------------------*/ + get_spd_info(dimm_populated, iic0_dimm_addr, num_dimm_banks); + + /*------------------------------------------------------------------ + * Check the memory type for the dimms plugged. + *-----------------------------------------------------------------*/ + check_mem_type(dimm_populated, iic0_dimm_addr, num_dimm_banks); + + /*------------------------------------------------------------------ + * Check the frequency supported for the dimms plugged. + *-----------------------------------------------------------------*/ + check_frequency(dimm_populated, iic0_dimm_addr, num_dimm_banks); + + /*------------------------------------------------------------------ + * Check the total rank number. + *-----------------------------------------------------------------*/ + check_rank_number(dimm_populated, iic0_dimm_addr, num_dimm_banks); + + /*------------------------------------------------------------------ + * Check the voltage type for the dimms plugged. + *-----------------------------------------------------------------*/ + check_voltage_type(dimm_populated, iic0_dimm_addr, num_dimm_banks); + + /*------------------------------------------------------------------ + * Program SDRAM controller options 2 register + * Except Enabling of the memory controller. + *-----------------------------------------------------------------*/ + mfsdram(SDRAM_MCOPT2, val); + mtsdram(SDRAM_MCOPT2, + (val & + ~(SDRAM_MCOPT2_SREN_MASK | SDRAM_MCOPT2_PMEN_MASK | + SDRAM_MCOPT2_IPTR_MASK | SDRAM_MCOPT2_XSRP_MASK | + SDRAM_MCOPT2_ISIE_MASK)) + | (SDRAM_MCOPT2_SREN_ENTER | SDRAM_MCOPT2_PMEN_DISABLE | + SDRAM_MCOPT2_IPTR_IDLE | SDRAM_MCOPT2_XSRP_ALLOW | + SDRAM_MCOPT2_ISIE_ENABLE)); + + /*------------------------------------------------------------------ + * Program SDRAM controller options 1 register + * Note: Does not enable the memory controller. + *-----------------------------------------------------------------*/ + program_copt1(dimm_populated, iic0_dimm_addr, num_dimm_banks); + + /*------------------------------------------------------------------ + * Set the SDRAM Controller On Die Termination Register + *-----------------------------------------------------------------*/ + program_codt(dimm_populated, iic0_dimm_addr, num_dimm_banks); + + /*------------------------------------------------------------------ + * Program SDRAM refresh register. + *-----------------------------------------------------------------*/ + program_rtr(dimm_populated, iic0_dimm_addr, num_dimm_banks); + + /*------------------------------------------------------------------ + * Program SDRAM mode register. + *-----------------------------------------------------------------*/ + program_mode(dimm_populated, iic0_dimm_addr, num_dimm_banks, + &selected_cas, &write_recovery); + + /*------------------------------------------------------------------ + * Set the SDRAM Write Data/DM/DQS Clock Timing Reg + *-----------------------------------------------------------------*/ + mfsdram(SDRAM_WRDTR, val); + mtsdram(SDRAM_WRDTR, (val & ~(SDRAM_WRDTR_LLWP_MASK | SDRAM_WRDTR_WTR_MASK)) | + ddr_wrdtr(SDRAM_WRDTR_LLWP_1_CYC | SDRAM_WRDTR_WTR_90_DEG_ADV)); + + /*------------------------------------------------------------------ + * Set the SDRAM Clock Timing Register + *-----------------------------------------------------------------*/ + mfsdram(SDRAM_CLKTR, val); + mtsdram(SDRAM_CLKTR, (val & ~SDRAM_CLKTR_CLKP_MASK) | + ddr_clktr(SDRAM_CLKTR_CLKP_0_DEG)); + + /*------------------------------------------------------------------ + * Program the BxCF registers. + *-----------------------------------------------------------------*/ + program_bxcf(dimm_populated, iic0_dimm_addr, num_dimm_banks); + + /*------------------------------------------------------------------ + * Program SDRAM timing registers. + *-----------------------------------------------------------------*/ + program_tr(dimm_populated, iic0_dimm_addr, num_dimm_banks); + + /*------------------------------------------------------------------ + * Set the Extended Mode register + *-----------------------------------------------------------------*/ + mfsdram(SDRAM_MEMODE, val); + mtsdram(SDRAM_MEMODE, + (val & ~(SDRAM_MEMODE_DIC_MASK | SDRAM_MEMODE_DLL_MASK | + SDRAM_MEMODE_RTT_MASK | SDRAM_MEMODE_DQS_MASK)) | + (SDRAM_MEMODE_DIC_NORMAL | SDRAM_MEMODE_DLL_ENABLE + | SDRAM_MEMODE_RTT_150OHM | SDRAM_MEMODE_DQS_ENABLE)); + + /*------------------------------------------------------------------ + * Program Initialization preload registers. + *-----------------------------------------------------------------*/ + program_initplr(dimm_populated, iic0_dimm_addr, num_dimm_banks, + selected_cas, write_recovery); + + /*------------------------------------------------------------------ + * Delay to ensure 200usec have elapsed since reset. + *-----------------------------------------------------------------*/ + udelay(400); + + /*------------------------------------------------------------------ + * Set the memory queue core base addr. + *-----------------------------------------------------------------*/ + program_memory_queue(dimm_populated, iic0_dimm_addr, num_dimm_banks); + + /*------------------------------------------------------------------ + * Program SDRAM controller options 2 register + * Enable the memory controller. + *-----------------------------------------------------------------*/ + mfsdram(SDRAM_MCOPT2, val); + mtsdram(SDRAM_MCOPT2, + (val & ~(SDRAM_MCOPT2_SREN_MASK | SDRAM_MCOPT2_DCEN_MASK | + SDRAM_MCOPT2_IPTR_MASK | SDRAM_MCOPT2_ISIE_MASK)) | + SDRAM_MCOPT2_IPTR_EXECUTE); + + /*------------------------------------------------------------------ + * Wait for IPTR_EXECUTE init sequence to complete. + *-----------------------------------------------------------------*/ + do { + mfsdram(SDRAM_MCSTAT, val); + } while ((val & SDRAM_MCSTAT_MIC_MASK) == SDRAM_MCSTAT_MIC_NOTCOMP); + + /* enable the controller only after init sequence completes */ + mfsdram(SDRAM_MCOPT2, val); + mtsdram(SDRAM_MCOPT2, (val | SDRAM_MCOPT2_DCEN_ENABLE)); + + /* Make sure delay-line calibration is done before proceeding */ + do { + mfsdram(SDRAM_DLCR, val); + } while (!(val & SDRAM_DLCR_DLCS_COMPLETE)); + + /* get installed memory size */ + dram_size = sdram_memsize(); + + /* + * Limit size to 2GB + */ + if (dram_size > CONFIG_MAX_MEM_MAPPED) + dram_size = CONFIG_MAX_MEM_MAPPED; + + /* and program tlb entries for this size (dynamic) */ + + /* + * Program TLB entries with caches enabled, for best performace + * while auto-calibrating and ECC generation + */ + program_tlb(0, 0, dram_size, 0); + + /*------------------------------------------------------------------ + * DQS calibration. + *-----------------------------------------------------------------*/ +#if defined(CONFIG_PPC4xx_DDR_AUTOCALIBRATION) + DQS_autocalibration(); +#else + program_DQS_calibration(dimm_populated, iic0_dimm_addr, num_dimm_banks); +#endif + +#ifdef CONFIG_DDR_ECC + /*------------------------------------------------------------------ + * If ecc is enabled, initialize the parity bits. + *-----------------------------------------------------------------*/ + program_ecc(dimm_populated, iic0_dimm_addr, num_dimm_banks, 0); +#endif + + /* + * Now after initialization (auto-calibration and ECC generation) + * remove the TLB entries with caches enabled and program again with + * desired cache functionality + */ + remove_tlb(0, dram_size); + program_tlb(0, 0, dram_size, MY_TLB_WORD2_I_ENABLE); + + ppc4xx_ibm_ddr2_register_dump(); + + /* + * Clear potential errors resulting from auto-calibration. + * If not done, then we could get an interrupt later on when + * exceptions are enabled. + */ + set_mcsr(get_mcsr()); + + return sdram_memsize(); +} + +static void get_spd_info(unsigned long *dimm_populated, + unsigned char *iic0_dimm_addr, + unsigned long num_dimm_banks) +{ + unsigned long dimm_num; + unsigned long dimm_found; + unsigned char num_of_bytes; + unsigned char total_size; + + dimm_found = FALSE; + for (dimm_num = 0; dimm_num < num_dimm_banks; dimm_num++) { + num_of_bytes = 0; + total_size = 0; + + num_of_bytes = spd_read(iic0_dimm_addr[dimm_num], 0); + debug("\nspd_read(0x%x) returned %d\n", + iic0_dimm_addr[dimm_num], num_of_bytes); + total_size = spd_read(iic0_dimm_addr[dimm_num], 1); + debug("spd_read(0x%x) returned %d\n", + iic0_dimm_addr[dimm_num], total_size); + + if ((num_of_bytes != 0) && (total_size != 0)) { + dimm_populated[dimm_num] = TRUE; + dimm_found = TRUE; + debug("DIMM slot %lu: populated\n", dimm_num); + } else { + dimm_populated[dimm_num] = FALSE; + debug("DIMM slot %lu: Not populated\n", dimm_num); + } + } + + if (dimm_found == FALSE) { + printf("ERROR - No memory installed. Install a DDR-SDRAM DIMM.\n\n"); + spd_ddr_init_hang (); + } +} + + +/*------------------------------------------------------------------ + * For the memory DIMMs installed, this routine verifies that they + * really are DDR specific DIMMs. + *-----------------------------------------------------------------*/ +static void check_mem_type(unsigned long *dimm_populated, + unsigned char *iic0_dimm_addr, + unsigned long num_dimm_banks) +{ + unsigned long dimm_num; + unsigned long dimm_type; + + for (dimm_num = 0; dimm_num < num_dimm_banks; dimm_num++) { + if (dimm_populated[dimm_num] == TRUE) { + dimm_type = spd_read(iic0_dimm_addr[dimm_num], 2); + switch (dimm_type) { + case 1: + printf("ERROR: Standard Fast Page Mode DRAM DIMM detected in " + "slot %d.\n", (unsigned int)dimm_num); + printf("Only DDR and DDR2 SDRAM DIMMs are supported.\n"); + printf("Replace the DIMM module with a supported DIMM.\n\n"); + spd_ddr_init_hang (); + break; + case 2: + printf("ERROR: EDO DIMM detected in slot %d.\n", + (unsigned int)dimm_num); + printf("Only DDR and DDR2 SDRAM DIMMs are supported.\n"); + printf("Replace the DIMM module with a supported DIMM.\n\n"); + spd_ddr_init_hang (); + break; + case 3: + printf("ERROR: Pipelined Nibble DIMM detected in slot %d.\n", + (unsigned int)dimm_num); + printf("Only DDR and DDR2 SDRAM DIMMs are supported.\n"); + printf("Replace the DIMM module with a supported DIMM.\n\n"); + spd_ddr_init_hang (); + break; + case 4: + printf("ERROR: SDRAM DIMM detected in slot %d.\n", + (unsigned int)dimm_num); + printf("Only DDR and DDR2 SDRAM DIMMs are supported.\n"); + printf("Replace the DIMM module with a supported DIMM.\n\n"); + spd_ddr_init_hang (); + break; + case 5: + printf("ERROR: Multiplexed ROM DIMM detected in slot %d.\n", + (unsigned int)dimm_num); + printf("Only DDR and DDR2 SDRAM DIMMs are supported.\n"); + printf("Replace the DIMM module with a supported DIMM.\n\n"); + spd_ddr_init_hang (); + break; + case 6: + printf("ERROR: SGRAM DIMM detected in slot %d.\n", + (unsigned int)dimm_num); + printf("Only DDR and DDR2 SDRAM DIMMs are supported.\n"); + printf("Replace the DIMM module with a supported DIMM.\n\n"); + spd_ddr_init_hang (); + break; + case 7: + debug("DIMM slot %lu: DDR1 SDRAM detected\n", dimm_num); + dimm_populated[dimm_num] = SDRAM_DDR1; + break; + case 8: + debug("DIMM slot %lu: DDR2 SDRAM detected\n", dimm_num); + dimm_populated[dimm_num] = SDRAM_DDR2; + break; + default: + printf("ERROR: Unknown DIMM detected in slot %d.\n", + (unsigned int)dimm_num); + printf("Only DDR1 and DDR2 SDRAM DIMMs are supported.\n"); + printf("Replace the DIMM module with a supported DIMM.\n\n"); + spd_ddr_init_hang (); + break; + } + } + } + for (dimm_num = 1; dimm_num < num_dimm_banks; dimm_num++) { + if ((dimm_populated[dimm_num-1] != SDRAM_NONE) + && (dimm_populated[dimm_num] != SDRAM_NONE) + && (dimm_populated[dimm_num-1] != dimm_populated[dimm_num])) { + printf("ERROR: DIMM's DDR1 and DDR2 type can not be mixed.\n"); + spd_ddr_init_hang (); + } + } +} + +/*------------------------------------------------------------------ + * For the memory DIMMs installed, this routine verifies that + * frequency previously calculated is supported. + *-----------------------------------------------------------------*/ +static void check_frequency(unsigned long *dimm_populated, + unsigned char *iic0_dimm_addr, + unsigned long num_dimm_banks) +{ + unsigned long dimm_num; + unsigned long tcyc_reg; + unsigned long cycle_time; + unsigned long calc_cycle_time; + unsigned long sdram_freq; + unsigned long sdr_ddrpll; + PPC4xx_SYS_INFO board_cfg; + + /*------------------------------------------------------------------ + * Get the board configuration info. + *-----------------------------------------------------------------*/ + get_sys_info(&board_cfg); + + mfsdr(SDR0_DDR0, sdr_ddrpll); + sdram_freq = ((board_cfg.freqPLB) * SDR0_DDR0_DDRM_DECODE(sdr_ddrpll)); + + /* + * calc_cycle_time is calculated from DDR frequency set by board/chip + * and is expressed in multiple of 10 picoseconds + * to match the way DIMM cycle time is calculated below. + */ + calc_cycle_time = MULDIV64(ONE_BILLION, 100, sdram_freq); + + for (dimm_num = 0; dimm_num < num_dimm_banks; dimm_num++) { + if (dimm_populated[dimm_num] != SDRAM_NONE) { + tcyc_reg = spd_read(iic0_dimm_addr[dimm_num], 9); + /* + * Byte 9, Cycle time for CAS Latency=X, is split into two nibbles: + * the higher order nibble (bits 4-7) designates the cycle time + * to a granularity of 1ns; + * the value presented by the lower order nibble (bits 0-3) + * has a granularity of .1ns and is added to the value designated + * by the higher nibble. In addition, four lines of the lower order + * nibble are assigned to support +.25,+.33, +.66 and +.75. + */ + /* Convert from hex to decimal */ + if ((tcyc_reg & 0x0F) == 0x0D) + cycle_time = (((tcyc_reg & 0xF0) >> 4) * 100) + 75; + else if ((tcyc_reg & 0x0F) == 0x0C) + cycle_time = (((tcyc_reg & 0xF0) >> 4) * 100) + 66; + else if ((tcyc_reg & 0x0F) == 0x0B) + cycle_time = (((tcyc_reg & 0xF0) >> 4) * 100) + 33; + else if ((tcyc_reg & 0x0F) == 0x0A) + cycle_time = (((tcyc_reg & 0xF0) >> 4) * 100) + 25; + else + cycle_time = (((tcyc_reg & 0xF0) >> 4) * 100) + + ((tcyc_reg & 0x0F)*10); + debug("cycle_time=%lu [10 picoseconds]\n", cycle_time); + + if (cycle_time > (calc_cycle_time + 10)) { + /* + * the provided sdram cycle_time is too small + * for the available DIMM cycle_time. + * The additionnal 100ps is here to accept a small incertainty. + */ + printf("ERROR: DRAM DIMM detected with cycle_time %d ps in " + "slot %d \n while calculated cycle time is %d ps.\n", + (unsigned int)(cycle_time*10), + (unsigned int)dimm_num, + (unsigned int)(calc_cycle_time*10)); + printf("Replace the DIMM, or change DDR frequency via " + "strapping bits.\n\n"); + spd_ddr_init_hang (); + } + } + } +} + +/*------------------------------------------------------------------ + * For the memory DIMMs installed, this routine verifies two + * ranks/banks maximum are availables. + *-----------------------------------------------------------------*/ +static void check_rank_number(unsigned long *dimm_populated, + unsigned char *iic0_dimm_addr, + unsigned long num_dimm_banks) +{ + unsigned long dimm_num; + unsigned long dimm_rank; + unsigned long total_rank = 0; + + for (dimm_num = 0; dimm_num < num_dimm_banks; dimm_num++) { + if (dimm_populated[dimm_num] != SDRAM_NONE) { + dimm_rank = spd_read(iic0_dimm_addr[dimm_num], 5); + if (((unsigned long)spd_read(iic0_dimm_addr[dimm_num], 2)) == 0x08) + dimm_rank = (dimm_rank & 0x0F) +1; + else + dimm_rank = dimm_rank & 0x0F; + + + if (dimm_rank > MAXRANKS) { + printf("ERROR: DRAM DIMM detected with %lu ranks in " + "slot %lu is not supported.\n", dimm_rank, dimm_num); + printf("Only %d ranks are supported for all DIMM.\n", MAXRANKS); + printf("Replace the DIMM module with a supported DIMM.\n\n"); + spd_ddr_init_hang (); + } else + total_rank += dimm_rank; + } + if (total_rank > MAXRANKS) { + printf("ERROR: DRAM DIMM detected with a total of %d ranks " + "for all slots.\n", (unsigned int)total_rank); + printf("Only %d ranks are supported for all DIMM.\n", MAXRANKS); + printf("Remove one of the DIMM modules.\n\n"); + spd_ddr_init_hang (); + } + } +} + +/*------------------------------------------------------------------ + * only support 2.5V modules. + * This routine verifies this. + *-----------------------------------------------------------------*/ +static void check_voltage_type(unsigned long *dimm_populated, + unsigned char *iic0_dimm_addr, + unsigned long num_dimm_banks) +{ + unsigned long dimm_num; + unsigned long voltage_type; + + for (dimm_num = 0; dimm_num < num_dimm_banks; dimm_num++) { + if (dimm_populated[dimm_num] != SDRAM_NONE) { + voltage_type = spd_read(iic0_dimm_addr[dimm_num], 8); + switch (voltage_type) { + case 0x00: + printf("ERROR: Only DIMMs DDR 2.5V or DDR2 1.8V are supported.\n"); + printf("This DIMM is 5.0 Volt/TTL.\n"); + printf("Replace the DIMM module in slot %d with a supported DIMM.\n\n", + (unsigned int)dimm_num); + spd_ddr_init_hang (); + break; + case 0x01: + printf("ERROR: Only DIMMs DDR 2.5V or DDR2 1.8V are supported.\n"); + printf("This DIMM is LVTTL.\n"); + printf("Replace the DIMM module in slot %d with a supported DIMM.\n\n", + (unsigned int)dimm_num); + spd_ddr_init_hang (); + break; + case 0x02: + printf("ERROR: Only DIMMs DDR 2.5V or DDR2 1.8V are supported.\n"); + printf("This DIMM is 1.5 Volt.\n"); + printf("Replace the DIMM module in slot %d with a supported DIMM.\n\n", + (unsigned int)dimm_num); + spd_ddr_init_hang (); + break; + case 0x03: + printf("ERROR: Only DIMMs DDR 2.5V or DDR2 1.8V are supported.\n"); + printf("This DIMM is 3.3 Volt/TTL.\n"); + printf("Replace the DIMM module in slot %d with a supported DIMM.\n\n", + (unsigned int)dimm_num); + spd_ddr_init_hang (); + break; + case 0x04: + /* 2.5 Voltage only for DDR1 */ + break; + case 0x05: + /* 1.8 Voltage only for DDR2 */ + break; + default: + printf("ERROR: Only DIMMs DDR 2.5V or DDR2 1.8V are supported.\n"); + printf("Replace the DIMM module in slot %d with a supported DIMM.\n\n", + (unsigned int)dimm_num); + spd_ddr_init_hang (); + break; + } + } + } +} + +/*-----------------------------------------------------------------------------+ + * program_copt1. + *-----------------------------------------------------------------------------*/ +static void program_copt1(unsigned long *dimm_populated, + unsigned char *iic0_dimm_addr, + unsigned long num_dimm_banks) +{ + unsigned long dimm_num; + unsigned long mcopt1; + unsigned long ecc_enabled; + unsigned long ecc = 0; + unsigned long data_width = 0; + unsigned long dimm_32bit; + unsigned long dimm_64bit; + unsigned long registered = 0; + unsigned long attribute = 0; + unsigned long buf0, buf1; /* TODO: code to be changed for IOP1.6 to support 4 DIMMs */ + unsigned long bankcount; + unsigned long ddrtype; + unsigned long val; + +#ifdef CONFIG_DDR_ECC + ecc_enabled = TRUE; +#else + ecc_enabled = FALSE; +#endif + dimm_32bit = FALSE; + dimm_64bit = FALSE; + buf0 = FALSE; + buf1 = FALSE; + + /*------------------------------------------------------------------ + * Set memory controller options reg 1, SDRAM_MCOPT1. + *-----------------------------------------------------------------*/ + mfsdram(SDRAM_MCOPT1, val); + mcopt1 = val & ~(SDRAM_MCOPT1_MCHK_MASK | SDRAM_MCOPT1_RDEN_MASK | + SDRAM_MCOPT1_PMU_MASK | SDRAM_MCOPT1_DMWD_MASK | + SDRAM_MCOPT1_UIOS_MASK | SDRAM_MCOPT1_BCNT_MASK | + SDRAM_MCOPT1_DDR_TYPE_MASK | SDRAM_MCOPT1_RWOO_MASK | + SDRAM_MCOPT1_WOOO_MASK | SDRAM_MCOPT1_DCOO_MASK | + SDRAM_MCOPT1_DREF_MASK); + + mcopt1 |= SDRAM_MCOPT1_QDEP; + mcopt1 |= SDRAM_MCOPT1_PMU_OPEN; + mcopt1 |= SDRAM_MCOPT1_RWOO_DISABLED; + mcopt1 |= SDRAM_MCOPT1_WOOO_DISABLED; + mcopt1 |= SDRAM_MCOPT1_DCOO_DISABLED; + mcopt1 |= SDRAM_MCOPT1_DREF_NORMAL; + + for (dimm_num = 0; dimm_num < num_dimm_banks; dimm_num++) { + if (dimm_populated[dimm_num] != SDRAM_NONE) { + /* test ecc support */ + ecc = (unsigned long)spd_read(iic0_dimm_addr[dimm_num], 11); + if (ecc != 0x02) /* ecc not supported */ + ecc_enabled = FALSE; + + /* test bank count */ + bankcount = (unsigned long)spd_read(iic0_dimm_addr[dimm_num], 17); + if (bankcount == 0x04) /* bank count = 4 */ + mcopt1 |= SDRAM_MCOPT1_4_BANKS; + else /* bank count = 8 */ + mcopt1 |= SDRAM_MCOPT1_8_BANKS; + + /* test DDR type */ + ddrtype = (unsigned long)spd_read(iic0_dimm_addr[dimm_num], 2); + /* test for buffered/unbuffered, registered, differential clocks */ + registered = (unsigned long)spd_read(iic0_dimm_addr[dimm_num], 20); + attribute = (unsigned long)spd_read(iic0_dimm_addr[dimm_num], 21); + + /* TODO: code to be changed for IOP1.6 to support 4 DIMMs */ + if (dimm_num == 0) { + if (dimm_populated[dimm_num] == SDRAM_DDR1) /* DDR1 type */ + mcopt1 |= SDRAM_MCOPT1_DDR1_TYPE; + if (dimm_populated[dimm_num] == SDRAM_DDR2) /* DDR2 type */ + mcopt1 |= SDRAM_MCOPT1_DDR2_TYPE; + if (registered == 1) { /* DDR2 always buffered */ + /* TODO: what about above comments ? */ + mcopt1 |= SDRAM_MCOPT1_RDEN; + buf0 = TRUE; + } else { + /* TODO: the mask 0x02 doesn't match Samsung def for byte 21. */ + if ((attribute & 0x02) == 0x00) { + /* buffered not supported */ + buf0 = FALSE; + } else { + mcopt1 |= SDRAM_MCOPT1_RDEN; + buf0 = TRUE; + } + } + } + else if (dimm_num == 1) { + if (dimm_populated[dimm_num] == SDRAM_DDR1) /* DDR1 type */ + mcopt1 |= SDRAM_MCOPT1_DDR1_TYPE; + if (dimm_populated[dimm_num] == SDRAM_DDR2) /* DDR2 type */ + mcopt1 |= SDRAM_MCOPT1_DDR2_TYPE; + if (registered == 1) { + /* DDR2 always buffered */ + mcopt1 |= SDRAM_MCOPT1_RDEN; + buf1 = TRUE; + } else { + if ((attribute & 0x02) == 0x00) { + /* buffered not supported */ + buf1 = FALSE; + } else { + mcopt1 |= SDRAM_MCOPT1_RDEN; + buf1 = TRUE; + } + } + } + + /* Note that for DDR2 the byte 7 is reserved, but OK to keep code as is. */ + data_width = (unsigned long)spd_read(iic0_dimm_addr[dimm_num], 6) + + (((unsigned long)spd_read(iic0_dimm_addr[dimm_num], 7)) << 8); + + switch (data_width) { + case 72: + case 64: + dimm_64bit = TRUE; + break; + case 40: + case 32: + dimm_32bit = TRUE; + break; + default: + printf("WARNING: Detected a DIMM with a data width of %lu bits.\n", + data_width); + printf("Only DIMMs with 32 or 64 bit DDR-SDRAM widths are supported.\n"); + break; + } + } + } + + /* verify matching properties */ + if ((dimm_populated[0] != SDRAM_NONE) && (dimm_populated[1] != SDRAM_NONE)) { + if (buf0 != buf1) { + printf("ERROR: DIMM's buffered/unbuffered, registered, clocking don't match.\n"); + spd_ddr_init_hang (); + } + } + + if ((dimm_64bit == TRUE) && (dimm_32bit == TRUE)) { + printf("ERROR: Cannot mix 32 bit and 64 bit DDR-SDRAM DIMMs together.\n"); + spd_ddr_init_hang (); + } + else if ((dimm_64bit == TRUE) && (dimm_32bit == FALSE)) { + mcopt1 |= SDRAM_MCOPT1_DMWD_64; + } else if ((dimm_64bit == FALSE) && (dimm_32bit == TRUE)) { + mcopt1 |= SDRAM_MCOPT1_DMWD_32; + } else { + printf("ERROR: Please install only 32 or 64 bit DDR-SDRAM DIMMs.\n\n"); + spd_ddr_init_hang (); + } + + if (ecc_enabled == TRUE) + mcopt1 |= SDRAM_MCOPT1_MCHK_GEN; + else + mcopt1 |= SDRAM_MCOPT1_MCHK_NON; + + mtsdram(SDRAM_MCOPT1, mcopt1); +} + +/*-----------------------------------------------------------------------------+ + * program_codt. + *-----------------------------------------------------------------------------*/ +static void program_codt(unsigned long *dimm_populated, + unsigned char *iic0_dimm_addr, + unsigned long num_dimm_banks) +{ + unsigned long codt; + unsigned long modt0 = 0; + unsigned long modt1 = 0; + unsigned long modt2 = 0; + unsigned long modt3 = 0; + unsigned char dimm_num; + unsigned char dimm_rank; + unsigned char total_rank = 0; + unsigned char total_dimm = 0; + unsigned char dimm_type = 0; + unsigned char firstSlot = 0; + + /*------------------------------------------------------------------ + * Set the SDRAM Controller On Die Termination Register + *-----------------------------------------------------------------*/ + mfsdram(SDRAM_CODT, codt); + codt &= ~(SDRAM_CODT_DQS_SINGLE_END | SDRAM_CODT_CKSE_SINGLE_END); + codt |= SDRAM_CODT_IO_NMODE; + + for (dimm_num = 0; dimm_num < num_dimm_banks; dimm_num++) { + if (dimm_populated[dimm_num] != SDRAM_NONE) { + dimm_rank = (unsigned long)spd_read(iic0_dimm_addr[dimm_num], 5); + if (((unsigned long)spd_read(iic0_dimm_addr[dimm_num], 2)) == 0x08) { + dimm_rank = (dimm_rank & 0x0F) + 1; + dimm_type = SDRAM_DDR2; + } else { + dimm_rank = dimm_rank & 0x0F; + dimm_type = SDRAM_DDR1; + } + + total_rank += dimm_rank; + total_dimm++; + if ((dimm_num == 0) && (total_dimm == 1)) + firstSlot = TRUE; + else + firstSlot = FALSE; + } + } + if (dimm_type == SDRAM_DDR2) { + codt |= SDRAM_CODT_DQS_1_8_V_DDR2; + if ((total_dimm == 1) && (firstSlot == TRUE)) { + if (total_rank == 1) { /* PUUU */ + codt |= CALC_ODT_R(0); + modt0 = CALC_ODT_W(0); + modt1 = 0x00000000; + modt2 = 0x00000000; + modt3 = 0x00000000; + } + if (total_rank == 2) { /* PPUU */ + codt |= CALC_ODT_R(0) | CALC_ODT_R(1); + modt0 = CALC_ODT_W(0) | CALC_ODT_W(1); + modt1 = 0x00000000; + modt2 = 0x00000000; + modt3 = 0x00000000; + } + } else if ((total_dimm == 1) && (firstSlot != TRUE)) { + if (total_rank == 1) { /* UUPU */ + codt |= CALC_ODT_R(2); + modt0 = 0x00000000; + modt1 = 0x00000000; + modt2 = CALC_ODT_W(2); + modt3 = 0x00000000; + } + if (total_rank == 2) { /* UUPP */ + codt |= CALC_ODT_R(2) | CALC_ODT_R(3); + modt0 = 0x00000000; + modt1 = 0x00000000; + modt2 = CALC_ODT_W(2) | CALC_ODT_W(3); + modt3 = 0x00000000; + } + } + if (total_dimm == 2) { + if (total_rank == 2) { /* PUPU */ + codt |= CALC_ODT_R(0) | CALC_ODT_R(2); + modt0 = CALC_ODT_RW(2); + modt1 = 0x00000000; + modt2 = CALC_ODT_RW(0); + modt3 = 0x00000000; + } + if (total_rank == 4) { /* PPPP */ + codt |= CALC_ODT_R(0) | CALC_ODT_R(1) | + CALC_ODT_R(2) | CALC_ODT_R(3); + modt0 = CALC_ODT_RW(2) | CALC_ODT_RW(3); + modt1 = 0x00000000; + modt2 = CALC_ODT_RW(0) | CALC_ODT_RW(1); + modt3 = 0x00000000; + } + } + } else { + codt |= SDRAM_CODT_DQS_2_5_V_DDR1; + modt0 = 0x00000000; + modt1 = 0x00000000; + modt2 = 0x00000000; + modt3 = 0x00000000; + + if (total_dimm == 1) { + if (total_rank == 1) + codt |= 0x00800000; + if (total_rank == 2) + codt |= 0x02800000; + } + if (total_dimm == 2) { + if (total_rank == 2) + codt |= 0x08800000; + if (total_rank == 4) + codt |= 0x2a800000; + } + } + + debug("nb of dimm %d\n", total_dimm); + debug("nb of rank %d\n", total_rank); + if (total_dimm == 1) + debug("dimm in slot %d\n", firstSlot); + + mtsdram(SDRAM_CODT, codt); + mtsdram(SDRAM_MODT0, modt0); + mtsdram(SDRAM_MODT1, modt1); + mtsdram(SDRAM_MODT2, modt2); + mtsdram(SDRAM_MODT3, modt3); +} + +/*-----------------------------------------------------------------------------+ + * program_initplr. + *-----------------------------------------------------------------------------*/ +static void program_initplr(unsigned long *dimm_populated, + unsigned char *iic0_dimm_addr, + unsigned long num_dimm_banks, + ddr_cas_id_t selected_cas, + int write_recovery) +{ + u32 cas = 0; + u32 odt = 0; + u32 ods = 0; + u32 mr; + u32 wr; + u32 emr; + u32 emr2; + u32 emr3; + int dimm_num; + int total_dimm = 0; + + /****************************************************** + ** Assumption: if more than one DIMM, all DIMMs are the same + ** as already checked in check_memory_type + ******************************************************/ + + if ((dimm_populated[0] == SDRAM_DDR1) || (dimm_populated[1] == SDRAM_DDR1)) { + mtsdram(SDRAM_INITPLR0, 0x81B80000); + mtsdram(SDRAM_INITPLR1, 0x81900400); + mtsdram(SDRAM_INITPLR2, 0x81810000); + mtsdram(SDRAM_INITPLR3, 0xff800162); + mtsdram(SDRAM_INITPLR4, 0x81900400); + mtsdram(SDRAM_INITPLR5, 0x86080000); + mtsdram(SDRAM_INITPLR6, 0x86080000); + mtsdram(SDRAM_INITPLR7, 0x81000062); + } else if ((dimm_populated[0] == SDRAM_DDR2) || (dimm_populated[1] == SDRAM_DDR2)) { + switch (selected_cas) { + case DDR_CAS_3: + cas = 3 << 4; + break; + case DDR_CAS_4: + cas = 4 << 4; + break; + case DDR_CAS_5: + cas = 5 << 4; + break; + default: + printf("ERROR: ucode error on selected_cas value %d", selected_cas); + spd_ddr_init_hang (); + break; + } + +#if 0 + /* + * ToDo - Still a problem with the write recovery: + * On the Corsair CM2X512-5400C4 module, setting write recovery + * in the INITPLR reg to the value calculated in program_mode() + * results in not correctly working DDR2 memory (crash after + * relocation). + * + * So for now, set the write recovery to 3. This seems to work + * on the Corair module too. + * + * 2007-03-01, sr + */ + switch (write_recovery) { + case 3: + wr = WRITE_RECOV_3; + break; + case 4: + wr = WRITE_RECOV_4; + break; + case 5: + wr = WRITE_RECOV_5; + break; + case 6: + wr = WRITE_RECOV_6; + break; + default: + printf("ERROR: write recovery not support (%d)", write_recovery); + spd_ddr_init_hang (); + break; + } +#else + wr = WRITE_RECOV_3; /* test-only, see description above */ +#endif + + for (dimm_num = 0; dimm_num < num_dimm_banks; dimm_num++) + if (dimm_populated[dimm_num] != SDRAM_NONE) + total_dimm++; + if (total_dimm == 1) { + odt = ODT_150_OHM; + ods = ODS_FULL; + } else if (total_dimm == 2) { + odt = ODT_75_OHM; + ods = ODS_REDUCED; + } else { + printf("ERROR: Unsupported number of DIMM's (%d)", total_dimm); + spd_ddr_init_hang (); + } + + mr = CMD_EMR | SELECT_MR | BURST_LEN_4 | wr | cas; + emr = CMD_EMR | SELECT_EMR | odt | ods; + emr2 = CMD_EMR | SELECT_EMR2; + emr3 = CMD_EMR | SELECT_EMR3; + /* NOP - Wait 106 MemClk cycles */ + mtsdram(SDRAM_INITPLR0, SDRAM_INITPLR_ENABLE | CMD_NOP | + SDRAM_INITPLR_IMWT_ENCODE(106)); + udelay(1000); + /* precharge 4 MemClk cycles */ + mtsdram(SDRAM_INITPLR1, SDRAM_INITPLR_ENABLE | CMD_PRECHARGE | + SDRAM_INITPLR_IMWT_ENCODE(4)); + /* EMR2 - Wait tMRD (2 MemClk cycles) */ + mtsdram(SDRAM_INITPLR2, SDRAM_INITPLR_ENABLE | emr2 | + SDRAM_INITPLR_IMWT_ENCODE(2)); + /* EMR3 - Wait tMRD (2 MemClk cycles) */ + mtsdram(SDRAM_INITPLR3, SDRAM_INITPLR_ENABLE | emr3 | + SDRAM_INITPLR_IMWT_ENCODE(2)); + /* EMR DLL ENABLE - Wait tMRD (2 MemClk cycles) */ + mtsdram(SDRAM_INITPLR4, SDRAM_INITPLR_ENABLE | emr | + SDRAM_INITPLR_IMWT_ENCODE(2)); + /* MR w/ DLL reset - 200 cycle wait for DLL reset */ + mtsdram(SDRAM_INITPLR5, SDRAM_INITPLR_ENABLE | mr | DLL_RESET | + SDRAM_INITPLR_IMWT_ENCODE(200)); + udelay(1000); + /* precharge 4 MemClk cycles */ + mtsdram(SDRAM_INITPLR6, SDRAM_INITPLR_ENABLE | CMD_PRECHARGE | + SDRAM_INITPLR_IMWT_ENCODE(4)); + /* Refresh 25 MemClk cycles */ + mtsdram(SDRAM_INITPLR7, SDRAM_INITPLR_ENABLE | CMD_REFRESH | + SDRAM_INITPLR_IMWT_ENCODE(25)); + /* Refresh 25 MemClk cycles */ + mtsdram(SDRAM_INITPLR8, SDRAM_INITPLR_ENABLE | CMD_REFRESH | + SDRAM_INITPLR_IMWT_ENCODE(25)); + /* Refresh 25 MemClk cycles */ + mtsdram(SDRAM_INITPLR9, SDRAM_INITPLR_ENABLE | CMD_REFRESH | + SDRAM_INITPLR_IMWT_ENCODE(25)); + /* Refresh 25 MemClk cycles */ + mtsdram(SDRAM_INITPLR10, SDRAM_INITPLR_ENABLE | CMD_REFRESH | + SDRAM_INITPLR_IMWT_ENCODE(25)); + /* MR w/o DLL reset - Wait tMRD (2 MemClk cycles) */ + mtsdram(SDRAM_INITPLR11, SDRAM_INITPLR_ENABLE | mr | + SDRAM_INITPLR_IMWT_ENCODE(2)); + /* EMR OCD Default - Wait tMRD (2 MemClk cycles) */ + mtsdram(SDRAM_INITPLR12, SDRAM_INITPLR_ENABLE | OCD_CALIB_DEF | + SDRAM_INITPLR_IMWT_ENCODE(2) | emr); + /* EMR OCD Exit */ + mtsdram(SDRAM_INITPLR13, SDRAM_INITPLR_ENABLE | emr | + SDRAM_INITPLR_IMWT_ENCODE(2)); + } else { + printf("ERROR: ucode error as unknown DDR type in program_initplr"); + spd_ddr_init_hang (); + } +} + +/*------------------------------------------------------------------ + * This routine programs the SDRAM_MMODE register. + * the selected_cas is an output parameter, that will be passed + * by caller to call the above program_initplr( ) + *-----------------------------------------------------------------*/ +static void program_mode(unsigned long *dimm_populated, + unsigned char *iic0_dimm_addr, + unsigned long num_dimm_banks, + ddr_cas_id_t *selected_cas, + int *write_recovery) +{ + unsigned long dimm_num; + unsigned long sdram_ddr1; + unsigned long t_wr_ns; + unsigned long t_wr_clk; + unsigned long cas_bit; + unsigned long cas_index; + unsigned long sdram_freq; + unsigned long ddr_check; + unsigned long mmode; + unsigned long tcyc_reg; + unsigned long cycle_2_0_clk; + unsigned long cycle_2_5_clk; + unsigned long cycle_3_0_clk; + unsigned long cycle_4_0_clk; + unsigned long cycle_5_0_clk; + unsigned long max_2_0_tcyc_ns_x_100; + unsigned long max_2_5_tcyc_ns_x_100; + unsigned long max_3_0_tcyc_ns_x_100; + unsigned long max_4_0_tcyc_ns_x_100; + unsigned long max_5_0_tcyc_ns_x_100; + unsigned long cycle_time_ns_x_100[3]; + PPC4xx_SYS_INFO board_cfg; + unsigned char cas_2_0_available; + unsigned char cas_2_5_available; + unsigned char cas_3_0_available; + unsigned char cas_4_0_available; + unsigned char cas_5_0_available; + unsigned long sdr_ddrpll; + + /*------------------------------------------------------------------ + * Get the board configuration info. + *-----------------------------------------------------------------*/ + get_sys_info(&board_cfg); + + mfsdr(SDR0_DDR0, sdr_ddrpll); + sdram_freq = MULDIV64((board_cfg.freqPLB), SDR0_DDR0_DDRM_DECODE(sdr_ddrpll), 1); + debug("sdram_freq=%lu\n", sdram_freq); + + /*------------------------------------------------------------------ + * Handle the timing. We need to find the worst case timing of all + * the dimm modules installed. + *-----------------------------------------------------------------*/ + t_wr_ns = 0; + cas_2_0_available = TRUE; + cas_2_5_available = TRUE; + cas_3_0_available = TRUE; + cas_4_0_available = TRUE; + cas_5_0_available = TRUE; + max_2_0_tcyc_ns_x_100 = 10; + max_2_5_tcyc_ns_x_100 = 10; + max_3_0_tcyc_ns_x_100 = 10; + max_4_0_tcyc_ns_x_100 = 10; + max_5_0_tcyc_ns_x_100 = 10; + sdram_ddr1 = TRUE; + + /* loop through all the DIMM slots on the board */ + for (dimm_num = 0; dimm_num < num_dimm_banks; dimm_num++) { + /* If a dimm is installed in a particular slot ... */ + if (dimm_populated[dimm_num] != SDRAM_NONE) { + if (dimm_populated[dimm_num] == SDRAM_DDR1) + sdram_ddr1 = TRUE; + else + sdram_ddr1 = FALSE; + + /* t_wr_ns = max(t_wr_ns, (unsigned long)dimm_spd[dimm_num][36] >> 2); */ /* not used in this loop. */ + cas_bit = spd_read(iic0_dimm_addr[dimm_num], 18); + debug("cas_bit[SPD byte 18]=%02lx\n", cas_bit); + + /* For a particular DIMM, grab the three CAS values it supports */ + for (cas_index = 0; cas_index < 3; cas_index++) { + switch (cas_index) { + case 0: + tcyc_reg = spd_read(iic0_dimm_addr[dimm_num], 9); + break; + case 1: + tcyc_reg = spd_read(iic0_dimm_addr[dimm_num], 23); + break; + default: + tcyc_reg = spd_read(iic0_dimm_addr[dimm_num], 25); + break; + } + + if ((tcyc_reg & 0x0F) >= 10) { + if ((tcyc_reg & 0x0F) == 0x0D) { + /* Convert from hex to decimal */ + cycle_time_ns_x_100[cas_index] = + (((tcyc_reg & 0xF0) >> 4) * 100) + 75; + } else { + printf("ERROR: SPD reported Tcyc is incorrect for DIMM " + "in slot %d\n", (unsigned int)dimm_num); + spd_ddr_init_hang (); + } + } else { + /* Convert from hex to decimal */ + cycle_time_ns_x_100[cas_index] = + (((tcyc_reg & 0xF0) >> 4) * 100) + + ((tcyc_reg & 0x0F)*10); + } + debug("cas_index=%lu: cycle_time_ns_x_100=%lu\n", cas_index, + cycle_time_ns_x_100[cas_index]); + } + + /* The rest of this routine determines if CAS 2.0, 2.5, 3.0, 4.0 and 5.0 are */ + /* supported for a particular DIMM. */ + cas_index = 0; + + if (sdram_ddr1) { + /* + * DDR devices use the following bitmask for CAS latency: + * Bit 7 6 5 4 3 2 1 0 + * TBD 4.0 3.5 3.0 2.5 2.0 1.5 1.0 + */ + if (((cas_bit & 0x40) == 0x40) && (cas_index < 3) && + (cycle_time_ns_x_100[cas_index] != 0)) { + max_4_0_tcyc_ns_x_100 = max(max_4_0_tcyc_ns_x_100, + cycle_time_ns_x_100[cas_index]); + cas_index++; + } else { + if (cas_index != 0) + cas_index++; + cas_4_0_available = FALSE; + } + + if (((cas_bit & 0x10) == 0x10) && (cas_index < 3) && + (cycle_time_ns_x_100[cas_index] != 0)) { + max_3_0_tcyc_ns_x_100 = max(max_3_0_tcyc_ns_x_100, + cycle_time_ns_x_100[cas_index]); + cas_index++; + } else { + if (cas_index != 0) + cas_index++; + cas_3_0_available = FALSE; + } + + if (((cas_bit & 0x08) == 0x08) && (cas_index < 3) && + (cycle_time_ns_x_100[cas_index] != 0)) { + max_2_5_tcyc_ns_x_100 = max(max_2_5_tcyc_ns_x_100, + cycle_time_ns_x_100[cas_index]); + cas_index++; + } else { + if (cas_index != 0) + cas_index++; + cas_2_5_available = FALSE; + } + + if (((cas_bit & 0x04) == 0x04) && (cas_index < 3) && + (cycle_time_ns_x_100[cas_index] != 0)) { + max_2_0_tcyc_ns_x_100 = max(max_2_0_tcyc_ns_x_100, + cycle_time_ns_x_100[cas_index]); + cas_index++; + } else { + if (cas_index != 0) + cas_index++; + cas_2_0_available = FALSE; + } + } else { + /* + * DDR2 devices use the following bitmask for CAS latency: + * Bit 7 6 5 4 3 2 1 0 + * TBD 6.0 5.0 4.0 3.0 2.0 TBD TBD + */ + if (((cas_bit & 0x20) == 0x20) && (cas_index < 3) && + (cycle_time_ns_x_100[cas_index] != 0)) { + max_5_0_tcyc_ns_x_100 = max(max_5_0_tcyc_ns_x_100, + cycle_time_ns_x_100[cas_index]); + cas_index++; + } else { + if (cas_index != 0) + cas_index++; + cas_5_0_available = FALSE; + } + + if (((cas_bit & 0x10) == 0x10) && (cas_index < 3) && + (cycle_time_ns_x_100[cas_index] != 0)) { + max_4_0_tcyc_ns_x_100 = max(max_4_0_tcyc_ns_x_100, + cycle_time_ns_x_100[cas_index]); + cas_index++; + } else { + if (cas_index != 0) + cas_index++; + cas_4_0_available = FALSE; + } + + if (((cas_bit & 0x08) == 0x08) && (cas_index < 3) && + (cycle_time_ns_x_100[cas_index] != 0)) { + max_3_0_tcyc_ns_x_100 = max(max_3_0_tcyc_ns_x_100, + cycle_time_ns_x_100[cas_index]); + cas_index++; + } else { + if (cas_index != 0) + cas_index++; + cas_3_0_available = FALSE; + } + } + } + } + + /*------------------------------------------------------------------ + * Set the SDRAM mode, SDRAM_MMODE + *-----------------------------------------------------------------*/ + mfsdram(SDRAM_MMODE, mmode); + mmode = mmode & ~(SDRAM_MMODE_WR_MASK | SDRAM_MMODE_DCL_MASK); + + /* add 10 here because of rounding problems */ + cycle_2_0_clk = MULDIV64(ONE_BILLION, 100, max_2_0_tcyc_ns_x_100) + 10; + cycle_2_5_clk = MULDIV64(ONE_BILLION, 100, max_2_5_tcyc_ns_x_100) + 10; + cycle_3_0_clk = MULDIV64(ONE_BILLION, 100, max_3_0_tcyc_ns_x_100) + 10; + cycle_4_0_clk = MULDIV64(ONE_BILLION, 100, max_4_0_tcyc_ns_x_100) + 10; + cycle_5_0_clk = MULDIV64(ONE_BILLION, 100, max_5_0_tcyc_ns_x_100) + 10; + debug("cycle_3_0_clk=%lu\n", cycle_3_0_clk); + debug("cycle_4_0_clk=%lu\n", cycle_4_0_clk); + debug("cycle_5_0_clk=%lu\n", cycle_5_0_clk); + + if (sdram_ddr1 == TRUE) { /* DDR1 */ + if ((cas_2_0_available == TRUE) && (sdram_freq <= cycle_2_0_clk)) { + mmode |= SDRAM_MMODE_DCL_DDR1_2_0_CLK; + *selected_cas = DDR_CAS_2; + } else if ((cas_2_5_available == TRUE) && (sdram_freq <= cycle_2_5_clk)) { + mmode |= SDRAM_MMODE_DCL_DDR1_2_5_CLK; + *selected_cas = DDR_CAS_2_5; + } else if ((cas_3_0_available == TRUE) && (sdram_freq <= cycle_3_0_clk)) { + mmode |= SDRAM_MMODE_DCL_DDR1_3_0_CLK; + *selected_cas = DDR_CAS_3; + } else { + printf("ERROR: Cannot find a supported CAS latency with the installed DIMMs.\n"); + printf("Only DIMMs DDR1 with CAS latencies of 2.0, 2.5, and 3.0 are supported.\n"); + printf("Make sure the PLB speed is within the supported range of the DIMMs.\n\n"); + spd_ddr_init_hang (); + } + } else { /* DDR2 */ + debug("cas_3_0_available=%d\n", cas_3_0_available); + debug("cas_4_0_available=%d\n", cas_4_0_available); + debug("cas_5_0_available=%d\n", cas_5_0_available); + if ((cas_3_0_available == TRUE) && (sdram_freq <= cycle_3_0_clk)) { + mmode |= SDRAM_MMODE_DCL_DDR2_3_0_CLK; + *selected_cas = DDR_CAS_3; + } else if ((cas_4_0_available == TRUE) && (sdram_freq <= cycle_4_0_clk)) { + mmode |= SDRAM_MMODE_DCL_DDR2_4_0_CLK; + *selected_cas = DDR_CAS_4; + } else if ((cas_5_0_available == TRUE) && (sdram_freq <= cycle_5_0_clk)) { + mmode |= SDRAM_MMODE_DCL_DDR2_5_0_CLK; + *selected_cas = DDR_CAS_5; + } else { + printf("ERROR: Cannot find a supported CAS latency with the installed DIMMs.\n"); + printf("Only DIMMs DDR2 with CAS latencies of 3.0, 4.0, and 5.0 are supported.\n"); + printf("Make sure the PLB speed is within the supported range of the DIMMs.\n"); + printf("cas3=%d cas4=%d cas5=%d\n", + cas_3_0_available, cas_4_0_available, cas_5_0_available); + printf("sdram_freq=%lu cycle3=%lu cycle4=%lu cycle5=%lu\n\n", + sdram_freq, cycle_3_0_clk, cycle_4_0_clk, cycle_5_0_clk); + spd_ddr_init_hang (); + } + } + + if (sdram_ddr1 == TRUE) + mmode |= SDRAM_MMODE_WR_DDR1; + else { + + /* loop through all the DIMM slots on the board */ + for (dimm_num = 0; dimm_num < num_dimm_banks; dimm_num++) { + /* If a dimm is installed in a particular slot ... */ + if (dimm_populated[dimm_num] != SDRAM_NONE) + t_wr_ns = max(t_wr_ns, + spd_read(iic0_dimm_addr[dimm_num], 36) >> 2); + } + + /* + * convert from nanoseconds to ddr clocks + * round up if necessary + */ + t_wr_clk = MULDIV64(sdram_freq, t_wr_ns, ONE_BILLION); + ddr_check = MULDIV64(ONE_BILLION, t_wr_clk, t_wr_ns); + if (sdram_freq != ddr_check) + t_wr_clk++; + + switch (t_wr_clk) { + case 0: + case 1: + case 2: + case 3: + mmode |= SDRAM_MMODE_WR_DDR2_3_CYC; + break; + case 4: + mmode |= SDRAM_MMODE_WR_DDR2_4_CYC; + break; + case 5: + mmode |= SDRAM_MMODE_WR_DDR2_5_CYC; + break; + default: + mmode |= SDRAM_MMODE_WR_DDR2_6_CYC; + break; + } + *write_recovery = t_wr_clk; + } + + debug("CAS latency = %d\n", *selected_cas); + debug("Write recovery = %d\n", *write_recovery); + + mtsdram(SDRAM_MMODE, mmode); +} + +/*-----------------------------------------------------------------------------+ + * program_rtr. + *-----------------------------------------------------------------------------*/ +static void program_rtr(unsigned long *dimm_populated, + unsigned char *iic0_dimm_addr, + unsigned long num_dimm_banks) +{ + PPC4xx_SYS_INFO board_cfg; + unsigned long max_refresh_rate; + unsigned long dimm_num; + unsigned long refresh_rate_type; + unsigned long refresh_rate; + unsigned long rint; + unsigned long sdram_freq; + unsigned long sdr_ddrpll; + unsigned long val; + + /*------------------------------------------------------------------ + * Get the board configuration info. + *-----------------------------------------------------------------*/ + get_sys_info(&board_cfg); + + /*------------------------------------------------------------------ + * Set the SDRAM Refresh Timing Register, SDRAM_RTR + *-----------------------------------------------------------------*/ + mfsdr(SDR0_DDR0, sdr_ddrpll); + sdram_freq = ((board_cfg.freqPLB) * SDR0_DDR0_DDRM_DECODE(sdr_ddrpll)); + + max_refresh_rate = 0; + for (dimm_num = 0; dimm_num < num_dimm_banks; dimm_num++) { + if (dimm_populated[dimm_num] != SDRAM_NONE) { + + refresh_rate_type = spd_read(iic0_dimm_addr[dimm_num], 12); + refresh_rate_type &= 0x7F; + switch (refresh_rate_type) { + case 0: + refresh_rate = 15625; + break; + case 1: + refresh_rate = 3906; + break; + case 2: + refresh_rate = 7812; + break; + case 3: + refresh_rate = 31250; + break; + case 4: + refresh_rate = 62500; + break; + case 5: + refresh_rate = 125000; + break; + default: + refresh_rate = 0; + printf("ERROR: DIMM %d unsupported refresh rate/type.\n", + (unsigned int)dimm_num); + printf("Replace the DIMM module with a supported DIMM.\n\n"); + spd_ddr_init_hang (); + break; + } + + max_refresh_rate = max(max_refresh_rate, refresh_rate); + } + } + + rint = MULDIV64(sdram_freq, max_refresh_rate, ONE_BILLION); + mfsdram(SDRAM_RTR, val); + mtsdram(SDRAM_RTR, (val & ~SDRAM_RTR_RINT_MASK) | + (SDRAM_RTR_RINT_ENCODE(rint))); +} + +/*------------------------------------------------------------------ + * This routine programs the SDRAM_TRx registers. + *-----------------------------------------------------------------*/ +static void program_tr(unsigned long *dimm_populated, + unsigned char *iic0_dimm_addr, + unsigned long num_dimm_banks) +{ + unsigned long dimm_num; + unsigned long sdram_ddr1; + unsigned long t_rp_ns; + unsigned long t_rcd_ns; + unsigned long t_rrd_ns; + unsigned long t_ras_ns; + unsigned long t_rc_ns; + unsigned long t_rfc_ns; + unsigned long t_wpc_ns; + unsigned long t_wtr_ns; + unsigned long t_rpc_ns; + unsigned long t_rp_clk; + unsigned long t_rcd_clk; + unsigned long t_rrd_clk; + unsigned long t_ras_clk; + unsigned long t_rc_clk; + unsigned long t_rfc_clk; + unsigned long t_wpc_clk; + unsigned long t_wtr_clk; + unsigned long t_rpc_clk; + unsigned long sdtr1, sdtr2, sdtr3; + unsigned long ddr_check; + unsigned long sdram_freq; + unsigned long sdr_ddrpll; + + PPC4xx_SYS_INFO board_cfg; + + /*------------------------------------------------------------------ + * Get the board configuration info. + *-----------------------------------------------------------------*/ + get_sys_info(&board_cfg); + + mfsdr(SDR0_DDR0, sdr_ddrpll); + sdram_freq = ((board_cfg.freqPLB) * SDR0_DDR0_DDRM_DECODE(sdr_ddrpll)); + + /*------------------------------------------------------------------ + * Handle the timing. We need to find the worst case timing of all + * the dimm modules installed. + *-----------------------------------------------------------------*/ + t_rp_ns = 0; + t_rrd_ns = 0; + t_rcd_ns = 0; + t_ras_ns = 0; + t_rc_ns = 0; + t_rfc_ns = 0; + t_wpc_ns = 0; + t_wtr_ns = 0; + t_rpc_ns = 0; + sdram_ddr1 = TRUE; + + /* loop through all the DIMM slots on the board */ + for (dimm_num = 0; dimm_num < num_dimm_banks; dimm_num++) { + /* If a dimm is installed in a particular slot ... */ + if (dimm_populated[dimm_num] != SDRAM_NONE) { + if (dimm_populated[dimm_num] == SDRAM_DDR2) + sdram_ddr1 = TRUE; + else + sdram_ddr1 = FALSE; + + t_rcd_ns = max(t_rcd_ns, spd_read(iic0_dimm_addr[dimm_num], 29) >> 2); + t_rrd_ns = max(t_rrd_ns, spd_read(iic0_dimm_addr[dimm_num], 28) >> 2); + t_rp_ns = max(t_rp_ns, spd_read(iic0_dimm_addr[dimm_num], 27) >> 2); + t_ras_ns = max(t_ras_ns, spd_read(iic0_dimm_addr[dimm_num], 30)); + t_rc_ns = max(t_rc_ns, spd_read(iic0_dimm_addr[dimm_num], 41)); + t_rfc_ns = max(t_rfc_ns, spd_read(iic0_dimm_addr[dimm_num], 42)); + } + } + + /*------------------------------------------------------------------ + * Set the SDRAM Timing Reg 1, SDRAM_TR1 + *-----------------------------------------------------------------*/ + mfsdram(SDRAM_SDTR1, sdtr1); + sdtr1 &= ~(SDRAM_SDTR1_LDOF_MASK | SDRAM_SDTR1_RTW_MASK | + SDRAM_SDTR1_WTWO_MASK | SDRAM_SDTR1_RTRO_MASK); + + /* default values */ + sdtr1 |= SDRAM_SDTR1_LDOF_2_CLK; + sdtr1 |= SDRAM_SDTR1_RTW_2_CLK; + + /* normal operations */ + sdtr1 |= SDRAM_SDTR1_WTWO_0_CLK; + sdtr1 |= SDRAM_SDTR1_RTRO_1_CLK; + + mtsdram(SDRAM_SDTR1, sdtr1); + + /*------------------------------------------------------------------ + * Set the SDRAM Timing Reg 2, SDRAM_TR2 + *-----------------------------------------------------------------*/ + mfsdram(SDRAM_SDTR2, sdtr2); + sdtr2 &= ~(SDRAM_SDTR2_RCD_MASK | SDRAM_SDTR2_WTR_MASK | + SDRAM_SDTR2_XSNR_MASK | SDRAM_SDTR2_WPC_MASK | + SDRAM_SDTR2_RPC_MASK | SDRAM_SDTR2_RP_MASK | + SDRAM_SDTR2_RRD_MASK); + + /* + * convert t_rcd from nanoseconds to ddr clocks + * round up if necessary + */ + t_rcd_clk = MULDIV64(sdram_freq, t_rcd_ns, ONE_BILLION); + ddr_check = MULDIV64(ONE_BILLION, t_rcd_clk, t_rcd_ns); + if (sdram_freq != ddr_check) + t_rcd_clk++; + + switch (t_rcd_clk) { + case 0: + case 1: + sdtr2 |= SDRAM_SDTR2_RCD_1_CLK; + break; + case 2: + sdtr2 |= SDRAM_SDTR2_RCD_2_CLK; + break; + case 3: + sdtr2 |= SDRAM_SDTR2_RCD_3_CLK; + break; + case 4: + sdtr2 |= SDRAM_SDTR2_RCD_4_CLK; + break; + default: + sdtr2 |= SDRAM_SDTR2_RCD_5_CLK; + break; + } + + if (sdram_ddr1 == TRUE) { /* DDR1 */ + if (sdram_freq < 200000000) { + sdtr2 |= SDRAM_SDTR2_WTR_1_CLK; + sdtr2 |= SDRAM_SDTR2_WPC_2_CLK; + sdtr2 |= SDRAM_SDTR2_RPC_2_CLK; + } else { + sdtr2 |= SDRAM_SDTR2_WTR_2_CLK; + sdtr2 |= SDRAM_SDTR2_WPC_3_CLK; + sdtr2 |= SDRAM_SDTR2_RPC_2_CLK; + } + } else { /* DDR2 */ + /* loop through all the DIMM slots on the board */ + for (dimm_num = 0; dimm_num < num_dimm_banks; dimm_num++) { + /* If a dimm is installed in a particular slot ... */ + if (dimm_populated[dimm_num] != SDRAM_NONE) { + t_wpc_ns = max(t_wtr_ns, spd_read(iic0_dimm_addr[dimm_num], 36) >> 2); + t_wtr_ns = max(t_wtr_ns, spd_read(iic0_dimm_addr[dimm_num], 37) >> 2); + t_rpc_ns = max(t_rpc_ns, spd_read(iic0_dimm_addr[dimm_num], 38) >> 2); + } + } + + /* + * convert from nanoseconds to ddr clocks + * round up if necessary + */ + t_wpc_clk = MULDIV64(sdram_freq, t_wpc_ns, ONE_BILLION); + ddr_check = MULDIV64(ONE_BILLION, t_wpc_clk, t_wpc_ns); + if (sdram_freq != ddr_check) + t_wpc_clk++; + + switch (t_wpc_clk) { + case 0: + case 1: + case 2: + sdtr2 |= SDRAM_SDTR2_WPC_2_CLK; + break; + case 3: + sdtr2 |= SDRAM_SDTR2_WPC_3_CLK; + break; + case 4: + sdtr2 |= SDRAM_SDTR2_WPC_4_CLK; + break; + case 5: + sdtr2 |= SDRAM_SDTR2_WPC_5_CLK; + break; + default: + sdtr2 |= SDRAM_SDTR2_WPC_6_CLK; + break; + } + + /* + * convert from nanoseconds to ddr clocks + * round up if necessary + */ + t_wtr_clk = MULDIV64(sdram_freq, t_wtr_ns, ONE_BILLION); + ddr_check = MULDIV64(ONE_BILLION, t_wtr_clk, t_wtr_ns); + if (sdram_freq != ddr_check) + t_wtr_clk++; + + switch (t_wtr_clk) { + case 0: + case 1: + sdtr2 |= SDRAM_SDTR2_WTR_1_CLK; + break; + case 2: + sdtr2 |= SDRAM_SDTR2_WTR_2_CLK; + break; + case 3: + sdtr2 |= SDRAM_SDTR2_WTR_3_CLK; + break; + default: + sdtr2 |= SDRAM_SDTR2_WTR_4_CLK; + break; + } + + /* + * convert from nanoseconds to ddr clocks + * round up if necessary + */ + t_rpc_clk = MULDIV64(sdram_freq, t_rpc_ns, ONE_BILLION); + ddr_check = MULDIV64(ONE_BILLION, t_rpc_clk, t_rpc_ns); + if (sdram_freq != ddr_check) + t_rpc_clk++; + + switch (t_rpc_clk) { + case 0: + case 1: + case 2: + sdtr2 |= SDRAM_SDTR2_RPC_2_CLK; + break; + case 3: + sdtr2 |= SDRAM_SDTR2_RPC_3_CLK; + break; + default: + sdtr2 |= SDRAM_SDTR2_RPC_4_CLK; + break; + } + } + + /* default value */ + sdtr2 |= SDRAM_SDTR2_XSNR_16_CLK; + + /* + * convert t_rrd from nanoseconds to ddr clocks + * round up if necessary + */ + t_rrd_clk = MULDIV64(sdram_freq, t_rrd_ns, ONE_BILLION); + ddr_check = MULDIV64(ONE_BILLION, t_rrd_clk, t_rrd_ns); + if (sdram_freq != ddr_check) + t_rrd_clk++; + + if (t_rrd_clk == 3) + sdtr2 |= SDRAM_SDTR2_RRD_3_CLK; + else + sdtr2 |= SDRAM_SDTR2_RRD_2_CLK; + + /* + * convert t_rp from nanoseconds to ddr clocks + * round up if necessary + */ + t_rp_clk = MULDIV64(sdram_freq, t_rp_ns, ONE_BILLION); + ddr_check = MULDIV64(ONE_BILLION, t_rp_clk, t_rp_ns); + if (sdram_freq != ddr_check) + t_rp_clk++; + + switch (t_rp_clk) { + case 0: + case 1: + case 2: + case 3: + sdtr2 |= SDRAM_SDTR2_RP_3_CLK; + break; + case 4: + sdtr2 |= SDRAM_SDTR2_RP_4_CLK; + break; + case 5: + sdtr2 |= SDRAM_SDTR2_RP_5_CLK; + break; + case 6: + sdtr2 |= SDRAM_SDTR2_RP_6_CLK; + break; + default: + sdtr2 |= SDRAM_SDTR2_RP_7_CLK; + break; + } + + mtsdram(SDRAM_SDTR2, sdtr2); + + /*------------------------------------------------------------------ + * Set the SDRAM Timing Reg 3, SDRAM_TR3 + *-----------------------------------------------------------------*/ + mfsdram(SDRAM_SDTR3, sdtr3); + sdtr3 &= ~(SDRAM_SDTR3_RAS_MASK | SDRAM_SDTR3_RC_MASK | + SDRAM_SDTR3_XCS_MASK | SDRAM_SDTR3_RFC_MASK); + + /* + * convert t_ras from nanoseconds to ddr clocks + * round up if necessary + */ + t_ras_clk = MULDIV64(sdram_freq, t_ras_ns, ONE_BILLION); + ddr_check = MULDIV64(ONE_BILLION, t_ras_clk, t_ras_ns); + if (sdram_freq != ddr_check) + t_ras_clk++; + + sdtr3 |= SDRAM_SDTR3_RAS_ENCODE(t_ras_clk); + + /* + * convert t_rc from nanoseconds to ddr clocks + * round up if necessary + */ + t_rc_clk = MULDIV64(sdram_freq, t_rc_ns, ONE_BILLION); + ddr_check = MULDIV64(ONE_BILLION, t_rc_clk, t_rc_ns); + if (sdram_freq != ddr_check) + t_rc_clk++; + + sdtr3 |= SDRAM_SDTR3_RC_ENCODE(t_rc_clk); + + /* default xcs value */ + sdtr3 |= SDRAM_SDTR3_XCS; + + /* + * convert t_rfc from nanoseconds to ddr clocks + * round up if necessary + */ + t_rfc_clk = MULDIV64(sdram_freq, t_rfc_ns, ONE_BILLION); + ddr_check = MULDIV64(ONE_BILLION, t_rfc_clk, t_rfc_ns); + if (sdram_freq != ddr_check) + t_rfc_clk++; + + sdtr3 |= SDRAM_SDTR3_RFC_ENCODE(t_rfc_clk); + + mtsdram(SDRAM_SDTR3, sdtr3); +} + +/*-----------------------------------------------------------------------------+ + * program_bxcf. + *-----------------------------------------------------------------------------*/ +static void program_bxcf(unsigned long *dimm_populated, + unsigned char *iic0_dimm_addr, + unsigned long num_dimm_banks) +{ + unsigned long dimm_num; + unsigned long num_col_addr; + unsigned long num_ranks; + unsigned long num_banks; + unsigned long mode; + unsigned long ind_rank; + unsigned long ind; + unsigned long ind_bank; + unsigned long bank_0_populated; + + /*------------------------------------------------------------------ + * Set the BxCF regs. First, wipe out the bank config registers. + *-----------------------------------------------------------------*/ + mtsdram(SDRAM_MB0CF, 0x00000000); + mtsdram(SDRAM_MB1CF, 0x00000000); + mtsdram(SDRAM_MB2CF, 0x00000000); + mtsdram(SDRAM_MB3CF, 0x00000000); + + mode = SDRAM_BXCF_M_BE_ENABLE; + + bank_0_populated = 0; + + for (dimm_num = 0; dimm_num < num_dimm_banks; dimm_num++) { + if (dimm_populated[dimm_num] != SDRAM_NONE) { + num_col_addr = spd_read(iic0_dimm_addr[dimm_num], 4); + num_ranks = spd_read(iic0_dimm_addr[dimm_num], 5); + if ((spd_read(iic0_dimm_addr[dimm_num], 2)) == 0x08) + num_ranks = (num_ranks & 0x0F) +1; + else + num_ranks = num_ranks & 0x0F; + + num_banks = spd_read(iic0_dimm_addr[dimm_num], 17); + + for (ind_bank = 0; ind_bank < 2; ind_bank++) { + if (num_banks == 4) + ind = 0; + else + ind = 5 << 8; + switch (num_col_addr) { + case 0x08: + mode |= (SDRAM_BXCF_M_AM_0 + ind); + break; + case 0x09: + mode |= (SDRAM_BXCF_M_AM_1 + ind); + break; + case 0x0A: + mode |= (SDRAM_BXCF_M_AM_2 + ind); + break; + case 0x0B: + mode |= (SDRAM_BXCF_M_AM_3 + ind); + break; + case 0x0C: + mode |= (SDRAM_BXCF_M_AM_4 + ind); + break; + default: + printf("DDR-SDRAM: DIMM %d BxCF configuration.\n", + (unsigned int)dimm_num); + printf("ERROR: Unsupported value for number of " + "column addresses: %d.\n", (unsigned int)num_col_addr); + printf("Replace the DIMM module with a supported DIMM.\n\n"); + spd_ddr_init_hang (); + } + } + + if ((dimm_populated[dimm_num] != SDRAM_NONE)&& (dimm_num ==1)) + bank_0_populated = 1; + + for (ind_rank = 0; ind_rank < num_ranks; ind_rank++) { + mtsdram(SDRAM_MB0CF + + ((dimm_num + bank_0_populated + ind_rank) << 2), + mode); + } + } + } +} + +/*------------------------------------------------------------------ + * program memory queue. + *-----------------------------------------------------------------*/ +static void program_memory_queue(unsigned long *dimm_populated, + unsigned char *iic0_dimm_addr, + unsigned long num_dimm_banks) +{ + unsigned long dimm_num; + phys_size_t rank_base_addr; + unsigned long rank_reg; + phys_size_t rank_size_bytes; + unsigned long rank_size_id; + unsigned long num_ranks; + unsigned long baseadd_size; + unsigned long i; + unsigned long bank_0_populated = 0; + phys_size_t total_size = 0; + + /*------------------------------------------------------------------ + * Reset the rank_base_address. + *-----------------------------------------------------------------*/ + rank_reg = SDRAM_R0BAS; + + rank_base_addr = 0x00000000; + + for (dimm_num = 0; dimm_num < num_dimm_banks; dimm_num++) { + if (dimm_populated[dimm_num] != SDRAM_NONE) { + num_ranks = spd_read(iic0_dimm_addr[dimm_num], 5); + if ((spd_read(iic0_dimm_addr[dimm_num], 2)) == 0x08) + num_ranks = (num_ranks & 0x0F) + 1; + else + num_ranks = num_ranks & 0x0F; + + rank_size_id = spd_read(iic0_dimm_addr[dimm_num], 31); + + /*------------------------------------------------------------------ + * Set the sizes + *-----------------------------------------------------------------*/ + baseadd_size = 0; + switch (rank_size_id) { + case 0x01: + baseadd_size |= SDRAM_RXBAS_SDSZ_1024; + total_size = 1024; + break; + case 0x02: + baseadd_size |= SDRAM_RXBAS_SDSZ_2048; + total_size = 2048; + break; + case 0x04: + baseadd_size |= SDRAM_RXBAS_SDSZ_4096; + total_size = 4096; + break; + case 0x08: + baseadd_size |= SDRAM_RXBAS_SDSZ_32; + total_size = 32; + break; + case 0x10: + baseadd_size |= SDRAM_RXBAS_SDSZ_64; + total_size = 64; + break; + case 0x20: + baseadd_size |= SDRAM_RXBAS_SDSZ_128; + total_size = 128; + break; + case 0x40: + baseadd_size |= SDRAM_RXBAS_SDSZ_256; + total_size = 256; + break; + case 0x80: + baseadd_size |= SDRAM_RXBAS_SDSZ_512; + total_size = 512; + break; + default: + printf("DDR-SDRAM: DIMM %d memory queue configuration.\n", + (unsigned int)dimm_num); + printf("ERROR: Unsupported value for the banksize: %d.\n", + (unsigned int)rank_size_id); + printf("Replace the DIMM module with a supported DIMM.\n\n"); + spd_ddr_init_hang (); + } + rank_size_bytes = total_size << 20; + + if ((dimm_populated[dimm_num] != SDRAM_NONE) && (dimm_num == 1)) + bank_0_populated = 1; + + for (i = 0; i < num_ranks; i++) { + mtdcr_any(rank_reg+i+dimm_num+bank_0_populated, + (SDRAM_RXBAS_SDBA_ENCODE(rank_base_addr) | + baseadd_size)); + rank_base_addr += rank_size_bytes; + } + } + } + +#if defined(CONFIG_440SP) || defined(CONFIG_440SPE) || \ + defined(CONFIG_460EX) || defined(CONFIG_460GT) || \ + defined(CONFIG_460SX) + /* + * Enable high bandwidth access + * This is currently not used, but with this setup + * it is possible to use it later on in e.g. the Linux + * EMAC driver for performance gain. + */ + mtdcr(SDRAM_PLBADDULL, 0x00000000); /* MQ0_BAUL */ + mtdcr(SDRAM_PLBADDUHB, 0x00000008); /* MQ0_BAUH */ + + /* + * Set optimal value for Memory Queue HB/LL Configuration registers + */ + mtdcr(SDRAM_CONF1HB, (mfdcr(SDRAM_CONF1HB) & ~SDRAM_CONF1HB_MASK) | + SDRAM_CONF1HB_AAFR | SDRAM_CONF1HB_RPEN | SDRAM_CONF1HB_RFTE | + SDRAM_CONF1HB_RPLM | SDRAM_CONF1HB_WRCL); + mtdcr(SDRAM_CONF1LL, (mfdcr(SDRAM_CONF1LL) & ~SDRAM_CONF1LL_MASK) | + SDRAM_CONF1LL_AAFR | SDRAM_CONF1LL_RPEN | SDRAM_CONF1LL_RFTE | + SDRAM_CONF1LL_RPLM); + mtdcr(SDRAM_CONFPATHB, mfdcr(SDRAM_CONFPATHB) | SDRAM_CONFPATHB_TPEN); +#endif +} + +#ifdef CONFIG_DDR_ECC +/*-----------------------------------------------------------------------------+ + * program_ecc. + *-----------------------------------------------------------------------------*/ +static void program_ecc(unsigned long *dimm_populated, + unsigned char *iic0_dimm_addr, + unsigned long num_dimm_banks, + unsigned long tlb_word2_i_value) +{ + unsigned long dimm_num; + unsigned long ecc; + + ecc = 0; + /* loop through all the DIMM slots on the board */ + for (dimm_num = 0; dimm_num < MAXDIMMS; dimm_num++) { + /* If a dimm is installed in a particular slot ... */ + if (dimm_populated[dimm_num] != SDRAM_NONE) + ecc = max(ecc, spd_read(iic0_dimm_addr[dimm_num], 11)); + } + if (ecc == 0) + return; + + do_program_ecc(tlb_word2_i_value); +} +#endif + +#if !defined(CONFIG_PPC4xx_DDR_AUTOCALIBRATION) +/*-----------------------------------------------------------------------------+ + * program_DQS_calibration. + *-----------------------------------------------------------------------------*/ +static void program_DQS_calibration(unsigned long *dimm_populated, + unsigned char *iic0_dimm_addr, + unsigned long num_dimm_banks) +{ + unsigned long val; + +#ifdef HARD_CODED_DQS /* calibration test with hardvalues */ + mtsdram(SDRAM_RQDC, 0x80000037); + mtsdram(SDRAM_RDCC, 0x40000000); + mtsdram(SDRAM_RFDC, 0x000001DF); + + test(); +#else + /*------------------------------------------------------------------ + * Program RDCC register + * Read sample cycle auto-update enable + *-----------------------------------------------------------------*/ + + mfsdram(SDRAM_RDCC, val); + mtsdram(SDRAM_RDCC, + (val & ~(SDRAM_RDCC_RDSS_MASK | SDRAM_RDCC_RSAE_MASK)) + | SDRAM_RDCC_RSAE_ENABLE); + + /*------------------------------------------------------------------ + * Program RQDC register + * Internal DQS delay mechanism enable + *-----------------------------------------------------------------*/ + mtsdram(SDRAM_RQDC, (SDRAM_RQDC_RQDE_ENABLE|SDRAM_RQDC_RQFD_ENCODE(0x38))); + + /*------------------------------------------------------------------ + * Program RFDC register + * Set Feedback Fractional Oversample + * Auto-detect read sample cycle enable + * Set RFOS to 1/4 of memclk cycle (0x3f) + *-----------------------------------------------------------------*/ + mfsdram(SDRAM_RFDC, val); + mtsdram(SDRAM_RFDC, + (val & ~(SDRAM_RFDC_ARSE_MASK | SDRAM_RFDC_RFOS_MASK | + SDRAM_RFDC_RFFD_MASK)) + | (SDRAM_RFDC_ARSE_ENABLE | SDRAM_RFDC_RFOS_ENCODE(0x3f) | + SDRAM_RFDC_RFFD_ENCODE(0))); + + DQS_calibration_process(); +#endif +} + +static int short_mem_test(void) +{ + u32 *membase; + u32 bxcr_num; + u32 bxcf; + int i; + int j; + phys_size_t base_addr; + u32 test[NUMMEMTESTS][NUMMEMWORDS] = { + {0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, + 0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF}, + {0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000, + 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000}, + {0xAAAAAAAA, 0xAAAAAAAA, 0x55555555, 0x55555555, + 0xAAAAAAAA, 0xAAAAAAAA, 0x55555555, 0x55555555}, + {0x55555555, 0x55555555, 0xAAAAAAAA, 0xAAAAAAAA, + 0x55555555, 0x55555555, 0xAAAAAAAA, 0xAAAAAAAA}, + {0xA5A5A5A5, 0xA5A5A5A5, 0x5A5A5A5A, 0x5A5A5A5A, + 0xA5A5A5A5, 0xA5A5A5A5, 0x5A5A5A5A, 0x5A5A5A5A}, + {0x5A5A5A5A, 0x5A5A5A5A, 0xA5A5A5A5, 0xA5A5A5A5, + 0x5A5A5A5A, 0x5A5A5A5A, 0xA5A5A5A5, 0xA5A5A5A5}, + {0xAA55AA55, 0xAA55AA55, 0x55AA55AA, 0x55AA55AA, + 0xAA55AA55, 0xAA55AA55, 0x55AA55AA, 0x55AA55AA}, + {0x55AA55AA, 0x55AA55AA, 0xAA55AA55, 0xAA55AA55, + 0x55AA55AA, 0x55AA55AA, 0xAA55AA55, 0xAA55AA55} }; + int l; + + for (bxcr_num = 0; bxcr_num < MAXBXCF; bxcr_num++) { + mfsdram(SDRAM_MB0CF + (bxcr_num << 2), bxcf); + + /* Banks enabled */ + if ((bxcf & SDRAM_BXCF_M_BE_MASK) == SDRAM_BXCF_M_BE_ENABLE) { + /* Bank is enabled */ + + /* + * Only run test on accessable memory (below 2GB) + */ + base_addr = SDRAM_RXBAS_SDBA_DECODE(mfdcr_any(SDRAM_R0BAS+bxcr_num)); + if (base_addr >= CONFIG_MAX_MEM_MAPPED) + continue; + + /*------------------------------------------------------------------ + * Run the short memory test. + *-----------------------------------------------------------------*/ + membase = (u32 *)(u32)base_addr; + + for (i = 0; i < NUMMEMTESTS; i++) { + for (j = 0; j < NUMMEMWORDS; j++) { + membase[j] = test[i][j]; + ppcDcbf((u32)&(membase[j])); + } + sync(); + for (l=0; l<NUMLOOPS; l++) { + for (j = 0; j < NUMMEMWORDS; j++) { + if (membase[j] != test[i][j]) { + ppcDcbf((u32)&(membase[j])); + return 0; + } + ppcDcbf((u32)&(membase[j])); + } + sync(); + } + } + } /* if bank enabled */ + } /* for bxcf_num */ + + return 1; +} + +#ifndef HARD_CODED_DQS +/*-----------------------------------------------------------------------------+ + * DQS_calibration_process. + *-----------------------------------------------------------------------------*/ +static void DQS_calibration_process(void) +{ + unsigned long rfdc_reg; + unsigned long rffd; + unsigned long val; + long rffd_average; + long max_start; + long min_end; + unsigned long begin_rqfd[MAXRANKS]; + unsigned long begin_rffd[MAXRANKS]; + unsigned long end_rqfd[MAXRANKS]; + unsigned long end_rffd[MAXRANKS]; + char window_found; + unsigned long dlycal; + unsigned long dly_val; + unsigned long max_pass_length; + unsigned long current_pass_length; + unsigned long current_fail_length; + unsigned long current_start; + long max_end; + unsigned char fail_found; + unsigned char pass_found; +#if !defined(CONFIG_DDR_RQDC_FIXED) + u32 rqdc_reg; + u32 rqfd; + u32 rqfd_start; + u32 rqfd_average; + int loopi = 0; + char str[] = "Auto calibration -"; + char slash[] = "\\|/-\\|/-"; + + /*------------------------------------------------------------------ + * Test to determine the best read clock delay tuning bits. + * + * Before the DDR controller can be used, the read clock delay needs to be + * set. This is SDRAM_RQDC[RQFD] and SDRAM_RFDC[RFFD]. + * This value cannot be hardcoded into the program because it changes + * depending on the board's setup and environment. + * To do this, all delay values are tested to see if they + * work or not. By doing this, you get groups of fails with groups of + * passing values. The idea is to find the start and end of a passing + * window and take the center of it to use as the read clock delay. + * + * A failure has to be seen first so that when we hit a pass, we know + * that it is truely the start of the window. If we get passing values + * to start off with, we don't know if we are at the start of the window. + * + * The code assumes that a failure will always be found. + * If a failure is not found, there is no easy way to get the middle + * of the passing window. I guess we can pretty much pick any value + * but some values will be better than others. Since the lowest speed + * we can clock the DDR interface at is 200 MHz (2x 100 MHz PLB speed), + * from experimentation it is safe to say you will always have a failure. + *-----------------------------------------------------------------*/ + + /* first fix RQDC[RQFD] to an average of 80 degre phase shift to find RFDC[RFFD] */ + rqfd_start = 64; /* test-only: don't know if this is the _best_ start value */ + + puts(str); + +calibration_loop: + mfsdram(SDRAM_RQDC, rqdc_reg); + mtsdram(SDRAM_RQDC, (rqdc_reg & ~SDRAM_RQDC_RQFD_MASK) | + SDRAM_RQDC_RQFD_ENCODE(rqfd_start)); +#else /* CONFIG_DDR_RQDC_FIXED */ + /* + * On Katmai the complete auto-calibration somehow doesn't seem to + * produce the best results, meaning optimal values for RQFD/RFFD. + * This was discovered by GDA using a high bandwidth scope, + * analyzing the DDR2 signals. GDA provided a fixed value for RQFD, + * so now on Katmai "only" RFFD is auto-calibrated. + */ + mtsdram(SDRAM_RQDC, CONFIG_DDR_RQDC_FIXED); +#endif /* CONFIG_DDR_RQDC_FIXED */ + + max_start = 0; + min_end = 0; + begin_rqfd[0] = 0; + begin_rffd[0] = 0; + begin_rqfd[1] = 0; + begin_rffd[1] = 0; + end_rqfd[0] = 0; + end_rffd[0] = 0; + end_rqfd[1] = 0; + end_rffd[1] = 0; + window_found = FALSE; + + max_pass_length = 0; + max_start = 0; + max_end = 0; + current_pass_length = 0; + current_fail_length = 0; + current_start = 0; + window_found = FALSE; + fail_found = FALSE; + pass_found = FALSE; + + /* + * get the delay line calibration register value + */ + mfsdram(SDRAM_DLCR, dlycal); + dly_val = SDRAM_DLYCAL_DLCV_DECODE(dlycal) << 2; + + for (rffd = 0; rffd <= SDRAM_RFDC_RFFD_MAX; rffd++) { + mfsdram(SDRAM_RFDC, rfdc_reg); + rfdc_reg &= ~(SDRAM_RFDC_RFFD_MASK); + + /*------------------------------------------------------------------ + * Set the timing reg for the test. + *-----------------------------------------------------------------*/ + mtsdram(SDRAM_RFDC, rfdc_reg | SDRAM_RFDC_RFFD_ENCODE(rffd)); + + /*------------------------------------------------------------------ + * See if the rffd value passed. + *-----------------------------------------------------------------*/ + if (short_mem_test()) { + if (fail_found == TRUE) { + pass_found = TRUE; + if (current_pass_length == 0) + current_start = rffd; + + current_fail_length = 0; + current_pass_length++; + + if (current_pass_length > max_pass_length) { + max_pass_length = current_pass_length; + max_start = current_start; + max_end = rffd; + } + } + } else { + current_pass_length = 0; + current_fail_length++; + + if (current_fail_length >= (dly_val >> 2)) { + if (fail_found == FALSE) { + fail_found = TRUE; + } else if (pass_found == TRUE) { + window_found = TRUE; + break; + } + } + } + } /* for rffd */ + + /*------------------------------------------------------------------ + * Set the average RFFD value + *-----------------------------------------------------------------*/ + rffd_average = ((max_start + max_end) >> 1); + + if (rffd_average < 0) + rffd_average = 0; + + if (rffd_average > SDRAM_RFDC_RFFD_MAX) + rffd_average = SDRAM_RFDC_RFFD_MAX; + /* now fix RFDC[RFFD] found and find RQDC[RQFD] */ + mtsdram(SDRAM_RFDC, rfdc_reg | SDRAM_RFDC_RFFD_ENCODE(rffd_average)); + +#if !defined(CONFIG_DDR_RQDC_FIXED) + max_pass_length = 0; + max_start = 0; + max_end = 0; + current_pass_length = 0; + current_fail_length = 0; + current_start = 0; + window_found = FALSE; + fail_found = FALSE; + pass_found = FALSE; + + for (rqfd = 0; rqfd <= SDRAM_RQDC_RQFD_MAX; rqfd++) { + mfsdram(SDRAM_RQDC, rqdc_reg); + rqdc_reg &= ~(SDRAM_RQDC_RQFD_MASK); + + /*------------------------------------------------------------------ + * Set the timing reg for the test. + *-----------------------------------------------------------------*/ + mtsdram(SDRAM_RQDC, rqdc_reg | SDRAM_RQDC_RQFD_ENCODE(rqfd)); + + /*------------------------------------------------------------------ + * See if the rffd value passed. + *-----------------------------------------------------------------*/ + if (short_mem_test()) { + if (fail_found == TRUE) { + pass_found = TRUE; + if (current_pass_length == 0) + current_start = rqfd; + + current_fail_length = 0; + current_pass_length++; + + if (current_pass_length > max_pass_length) { + max_pass_length = current_pass_length; + max_start = current_start; + max_end = rqfd; + } + } + } else { + current_pass_length = 0; + current_fail_length++; + + if (fail_found == FALSE) { + fail_found = TRUE; + } else if (pass_found == TRUE) { + window_found = TRUE; + break; + } + } + } + + rqfd_average = ((max_start + max_end) >> 1); + + /*------------------------------------------------------------------ + * Make sure we found the valid read passing window. Halt if not + *-----------------------------------------------------------------*/ + if (window_found == FALSE) { + if (rqfd_start < SDRAM_RQDC_RQFD_MAX) { + putc('\b'); + putc(slash[loopi++ % 8]); + + /* try again from with a different RQFD start value */ + rqfd_start++; + goto calibration_loop; + } + + printf("\nERROR: Cannot determine a common read delay for the " + "DIMM(s) installed.\n"); + debug("%s[%d] ERROR : \n", __FUNCTION__,__LINE__); + ppc4xx_ibm_ddr2_register_dump(); + spd_ddr_init_hang (); + } + + if (rqfd_average < 0) + rqfd_average = 0; + + if (rqfd_average > SDRAM_RQDC_RQFD_MAX) + rqfd_average = SDRAM_RQDC_RQFD_MAX; + + mtsdram(SDRAM_RQDC, + (rqdc_reg & ~SDRAM_RQDC_RQFD_MASK) | + SDRAM_RQDC_RQFD_ENCODE(rqfd_average)); + + blank_string(strlen(str)); +#endif /* CONFIG_DDR_RQDC_FIXED */ + + /* + * Now complete RDSS configuration as mentioned on page 7 of the AMCC + * PowerPC440SP/SPe DDR2 application note: + * "DDR1/DDR2 Initialization Sequence and Dynamic Tuning" + */ + mfsdram(SDRAM_RTSR, val); + if ((val & SDRAM_RTSR_TRK1SM_MASK) == SDRAM_RTSR_TRK1SM_ATPLS1) { + mfsdram(SDRAM_RDCC, val); + if ((val & SDRAM_RDCC_RDSS_MASK) != SDRAM_RDCC_RDSS_T4) { + val += 0x40000000; + mtsdram(SDRAM_RDCC, val); + } + } + + mfsdram(SDRAM_DLCR, val); + debug("%s[%d] DLCR: 0x%08lX\n", __FUNCTION__, __LINE__, val); + mfsdram(SDRAM_RQDC, val); + debug("%s[%d] RQDC: 0x%08lX\n", __FUNCTION__, __LINE__, val); + mfsdram(SDRAM_RFDC, val); + debug("%s[%d] RFDC: 0x%08lX\n", __FUNCTION__, __LINE__, val); + mfsdram(SDRAM_RDCC, val); + debug("%s[%d] RDCC: 0x%08lX\n", __FUNCTION__, __LINE__, val); +} +#else /* calibration test with hardvalues */ +/*-----------------------------------------------------------------------------+ + * DQS_calibration_process. + *-----------------------------------------------------------------------------*/ +static void test(void) +{ + unsigned long dimm_num; + unsigned long ecc_temp; + unsigned long i, j; + unsigned long *membase; + unsigned long bxcf[MAXRANKS]; + unsigned long val; + char window_found; + char begin_found[MAXDIMMS]; + char end_found[MAXDIMMS]; + char search_end[MAXDIMMS]; + unsigned long test[NUMMEMTESTS][NUMMEMWORDS] = { + {0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, + 0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF}, + {0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000, + 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000}, + {0xAAAAAAAA, 0xAAAAAAAA, 0x55555555, 0x55555555, + 0xAAAAAAAA, 0xAAAAAAAA, 0x55555555, 0x55555555}, + {0x55555555, 0x55555555, 0xAAAAAAAA, 0xAAAAAAAA, + 0x55555555, 0x55555555, 0xAAAAAAAA, 0xAAAAAAAA}, + {0xA5A5A5A5, 0xA5A5A5A5, 0x5A5A5A5A, 0x5A5A5A5A, + 0xA5A5A5A5, 0xA5A5A5A5, 0x5A5A5A5A, 0x5A5A5A5A}, + {0x5A5A5A5A, 0x5A5A5A5A, 0xA5A5A5A5, 0xA5A5A5A5, + 0x5A5A5A5A, 0x5A5A5A5A, 0xA5A5A5A5, 0xA5A5A5A5}, + {0xAA55AA55, 0xAA55AA55, 0x55AA55AA, 0x55AA55AA, + 0xAA55AA55, 0xAA55AA55, 0x55AA55AA, 0x55AA55AA}, + {0x55AA55AA, 0x55AA55AA, 0xAA55AA55, 0xAA55AA55, + 0x55AA55AA, 0x55AA55AA, 0xAA55AA55, 0xAA55AA55} }; + + /*------------------------------------------------------------------ + * Test to determine the best read clock delay tuning bits. + * + * Before the DDR controller can be used, the read clock delay needs to be + * set. This is SDRAM_RQDC[RQFD] and SDRAM_RFDC[RFFD]. + * This value cannot be hardcoded into the program because it changes + * depending on the board's setup and environment. + * To do this, all delay values are tested to see if they + * work or not. By doing this, you get groups of fails with groups of + * passing values. The idea is to find the start and end of a passing + * window and take the center of it to use as the read clock delay. + * + * A failure has to be seen first so that when we hit a pass, we know + * that it is truely the start of the window. If we get passing values + * to start off with, we don't know if we are at the start of the window. + * + * The code assumes that a failure will always be found. + * If a failure is not found, there is no easy way to get the middle + * of the passing window. I guess we can pretty much pick any value + * but some values will be better than others. Since the lowest speed + * we can clock the DDR interface at is 200 MHz (2x 100 MHz PLB speed), + * from experimentation it is safe to say you will always have a failure. + *-----------------------------------------------------------------*/ + mfsdram(SDRAM_MCOPT1, ecc_temp); + ecc_temp &= SDRAM_MCOPT1_MCHK_MASK; + mfsdram(SDRAM_MCOPT1, val); + mtsdram(SDRAM_MCOPT1, (val & ~SDRAM_MCOPT1_MCHK_MASK) | + SDRAM_MCOPT1_MCHK_NON); + + window_found = FALSE; + begin_found[0] = FALSE; + end_found[0] = FALSE; + search_end[0] = FALSE; + begin_found[1] = FALSE; + end_found[1] = FALSE; + search_end[1] = FALSE; + + for (dimm_num = 0; dimm_num < MAXDIMMS; dimm_num++) { + mfsdram(SDRAM_MB0CF + (bxcr_num << 2), bxcf[bxcr_num]); + + /* Banks enabled */ + if ((bxcf[dimm_num] & SDRAM_BXCF_M_BE_MASK) == SDRAM_BXCF_M_BE_ENABLE) { + + /* Bank is enabled */ + membase = + (unsigned long*)(SDRAM_RXBAS_SDBA_DECODE(mfdcr_any(SDRAM_R0BAS+dimm_num))); + + /*------------------------------------------------------------------ + * Run the short memory test. + *-----------------------------------------------------------------*/ + for (i = 0; i < NUMMEMTESTS; i++) { + for (j = 0; j < NUMMEMWORDS; j++) { + membase[j] = test[i][j]; + ppcDcbf((u32)&(membase[j])); + } + sync(); + for (j = 0; j < NUMMEMWORDS; j++) { + if (membase[j] != test[i][j]) { + ppcDcbf((u32)&(membase[j])); + break; + } + ppcDcbf((u32)&(membase[j])); + } + sync(); + if (j < NUMMEMWORDS) + break; + } + + /*------------------------------------------------------------------ + * See if the rffd value passed. + *-----------------------------------------------------------------*/ + if (i < NUMMEMTESTS) { + if ((end_found[dimm_num] == FALSE) && + (search_end[dimm_num] == TRUE)) { + end_found[dimm_num] = TRUE; + } + if ((end_found[0] == TRUE) && + (end_found[1] == TRUE)) + break; + } else { + if (begin_found[dimm_num] == FALSE) { + begin_found[dimm_num] = TRUE; + search_end[dimm_num] = TRUE; + } + } + } else { + begin_found[dimm_num] = TRUE; + end_found[dimm_num] = TRUE; + } + } + + if ((begin_found[0] == TRUE) && (begin_found[1] == TRUE)) + window_found = TRUE; + + /*------------------------------------------------------------------ + * Make sure we found the valid read passing window. Halt if not + *-----------------------------------------------------------------*/ + if (window_found == FALSE) { + printf("ERROR: Cannot determine a common read delay for the " + "DIMM(s) installed.\n"); + spd_ddr_init_hang (); + } + + /*------------------------------------------------------------------ + * Restore the ECC variable to what it originally was + *-----------------------------------------------------------------*/ + mtsdram(SDRAM_MCOPT1, + (ppcMfdcr_sdram(SDRAM_MCOPT1) & ~SDRAM_MCOPT1_MCHK_MASK) + | ecc_temp); +} +#endif /* !HARD_CODED_DQS */ +#endif /* !defined(CONFIG_PPC4xx_DDR_AUTOCALIBRATION) */ + +#else /* CONFIG_SPD_EEPROM */ + +/*----------------------------------------------------------------------------- + * Function: initdram + * Description: Configures the PPC4xx IBM DDR1/DDR2 SDRAM memory controller. + * The configuration is performed using static, compile- + * time parameters. + * Configures the PPC405EX(r) and PPC460EX/GT + *---------------------------------------------------------------------------*/ +phys_size_t initdram(int board_type) +{ + /* + * Only run this SDRAM init code once. For NAND booting + * targets like Kilauea, we call initdram() early from the + * 4k NAND booting image (CONFIG_NAND_SPL) from nand_boot(). + * Later on the NAND U-Boot image runs (CONFIG_NAND_U_BOOT) + * which calls initdram() again. This time the controller + * mustn't be reconfigured again since we're already running + * from SDRAM. + */ +#if !defined(CONFIG_NAND_U_BOOT) || defined(CONFIG_NAND_SPL) + unsigned long val; + +#if defined(CONFIG_440) + mtdcr(SDRAM_R0BAS, CONFIG_SYS_SDRAM_R0BAS); + mtdcr(SDRAM_R1BAS, CONFIG_SYS_SDRAM_R1BAS); + mtdcr(SDRAM_R2BAS, CONFIG_SYS_SDRAM_R2BAS); + mtdcr(SDRAM_R3BAS, CONFIG_SYS_SDRAM_R3BAS); + mtdcr(SDRAM_PLBADDULL, CONFIG_SYS_SDRAM_PLBADDULL); /* MQ0_BAUL */ + mtdcr(SDRAM_PLBADDUHB, CONFIG_SYS_SDRAM_PLBADDUHB); /* MQ0_BAUH */ + mtdcr(SDRAM_CONF1LL, CONFIG_SYS_SDRAM_CONF1LL); + mtdcr(SDRAM_CONF1HB, CONFIG_SYS_SDRAM_CONF1HB); + mtdcr(SDRAM_CONFPATHB, CONFIG_SYS_SDRAM_CONFPATHB); +#endif + + /* Set Memory Bank Configuration Registers */ + + mtsdram(SDRAM_MB0CF, CONFIG_SYS_SDRAM0_MB0CF); + mtsdram(SDRAM_MB1CF, CONFIG_SYS_SDRAM0_MB1CF); + mtsdram(SDRAM_MB2CF, CONFIG_SYS_SDRAM0_MB2CF); + mtsdram(SDRAM_MB3CF, CONFIG_SYS_SDRAM0_MB3CF); + + /* Set Memory Clock Timing Register */ + + mtsdram(SDRAM_CLKTR, CONFIG_SYS_SDRAM0_CLKTR); + + /* Set Refresh Time Register */ + + mtsdram(SDRAM_RTR, CONFIG_SYS_SDRAM0_RTR); + + /* Set SDRAM Timing Registers */ + + mtsdram(SDRAM_SDTR1, CONFIG_SYS_SDRAM0_SDTR1); + mtsdram(SDRAM_SDTR2, CONFIG_SYS_SDRAM0_SDTR2); + mtsdram(SDRAM_SDTR3, CONFIG_SYS_SDRAM0_SDTR3); + + /* Set Mode and Extended Mode Registers */ + + mtsdram(SDRAM_MMODE, CONFIG_SYS_SDRAM0_MMODE); + mtsdram(SDRAM_MEMODE, CONFIG_SYS_SDRAM0_MEMODE); + + /* Set Memory Controller Options 1 Register */ + + mtsdram(SDRAM_MCOPT1, CONFIG_SYS_SDRAM0_MCOPT1); + + /* Set Manual Initialization Control Registers */ + + mtsdram(SDRAM_INITPLR0, CONFIG_SYS_SDRAM0_INITPLR0); + mtsdram(SDRAM_INITPLR1, CONFIG_SYS_SDRAM0_INITPLR1); + mtsdram(SDRAM_INITPLR2, CONFIG_SYS_SDRAM0_INITPLR2); + mtsdram(SDRAM_INITPLR3, CONFIG_SYS_SDRAM0_INITPLR3); + mtsdram(SDRAM_INITPLR4, CONFIG_SYS_SDRAM0_INITPLR4); + mtsdram(SDRAM_INITPLR5, CONFIG_SYS_SDRAM0_INITPLR5); + mtsdram(SDRAM_INITPLR6, CONFIG_SYS_SDRAM0_INITPLR6); + mtsdram(SDRAM_INITPLR7, CONFIG_SYS_SDRAM0_INITPLR7); + mtsdram(SDRAM_INITPLR8, CONFIG_SYS_SDRAM0_INITPLR8); + mtsdram(SDRAM_INITPLR9, CONFIG_SYS_SDRAM0_INITPLR9); + mtsdram(SDRAM_INITPLR10, CONFIG_SYS_SDRAM0_INITPLR10); + mtsdram(SDRAM_INITPLR11, CONFIG_SYS_SDRAM0_INITPLR11); + mtsdram(SDRAM_INITPLR12, CONFIG_SYS_SDRAM0_INITPLR12); + mtsdram(SDRAM_INITPLR13, CONFIG_SYS_SDRAM0_INITPLR13); + mtsdram(SDRAM_INITPLR14, CONFIG_SYS_SDRAM0_INITPLR14); + mtsdram(SDRAM_INITPLR15, CONFIG_SYS_SDRAM0_INITPLR15); + + /* Set On-Die Termination Registers */ + + mtsdram(SDRAM_CODT, CONFIG_SYS_SDRAM0_CODT); + mtsdram(SDRAM_MODT0, CONFIG_SYS_SDRAM0_MODT0); + mtsdram(SDRAM_MODT1, CONFIG_SYS_SDRAM0_MODT1); + + /* Set Write Timing Register */ + + mtsdram(SDRAM_WRDTR, CONFIG_SYS_SDRAM0_WRDTR); + + /* + * Start Initialization by SDRAM0_MCOPT2[SREN] = 0 and + * SDRAM0_MCOPT2[IPTR] = 1 + */ + + mtsdram(SDRAM_MCOPT2, (SDRAM_MCOPT2_SREN_EXIT | + SDRAM_MCOPT2_IPTR_EXECUTE)); + + /* + * Poll SDRAM0_MCSTAT[MIC] for assertion to indicate the + * completion of initialization. + */ + + do { + mfsdram(SDRAM_MCSTAT, val); + } while ((val & SDRAM_MCSTAT_MIC_MASK) != SDRAM_MCSTAT_MIC_COMP); + + /* Set Delay Control Registers */ + + mtsdram(SDRAM_DLCR, CONFIG_SYS_SDRAM0_DLCR); + +#if !defined(CONFIG_PPC4xx_DDR_AUTOCALIBRATION) + mtsdram(SDRAM_RDCC, CONFIG_SYS_SDRAM0_RDCC); + mtsdram(SDRAM_RQDC, CONFIG_SYS_SDRAM0_RQDC); + mtsdram(SDRAM_RFDC, CONFIG_SYS_SDRAM0_RFDC); +#endif /* !CONFIG_PPC4xx_DDR_AUTOCALIBRATION */ + + /* + * Enable Controller by SDRAM0_MCOPT2[DCEN] = 1: + */ + + mfsdram(SDRAM_MCOPT2, val); + mtsdram(SDRAM_MCOPT2, val | SDRAM_MCOPT2_DCEN_ENABLE); + +#if defined(CONFIG_440) + /* + * Program TLB entries with caches enabled, for best performace + * while auto-calibrating and ECC generation + */ + program_tlb(0, 0, (CONFIG_SYS_MBYTES_SDRAM << 20), 0); +#endif + +#if defined(CONFIG_PPC4xx_DDR_AUTOCALIBRATION) +#if !defined(CONFIG_NAND_U_BOOT) && !defined(CONFIG_NAND_SPL) + /*------------------------------------------------------------------ + | DQS calibration. + +-----------------------------------------------------------------*/ + DQS_autocalibration(); +#endif /* !defined(CONFIG_NAND_U_BOOT) && !defined(CONFIG_NAND_SPL) */ +#endif /* CONFIG_PPC4xx_DDR_AUTOCALIBRATION */ + +#if defined(CONFIG_DDR_ECC) + do_program_ecc(0); +#endif /* defined(CONFIG_DDR_ECC) */ + +#if defined(CONFIG_440) + /* + * Now after initialization (auto-calibration and ECC generation) + * remove the TLB entries with caches enabled and program again with + * desired cache functionality + */ + remove_tlb(0, (CONFIG_SYS_MBYTES_SDRAM << 20)); + program_tlb(0, 0, (CONFIG_SYS_MBYTES_SDRAM << 20), MY_TLB_WORD2_I_ENABLE); +#endif + + ppc4xx_ibm_ddr2_register_dump(); + +#if defined(CONFIG_PPC4xx_DDR_AUTOCALIBRATION) + /* + * Clear potential errors resulting from auto-calibration. + * If not done, then we could get an interrupt later on when + * exceptions are enabled. + */ + set_mcsr(get_mcsr()); +#endif /* CONFIG_PPC4xx_DDR_AUTOCALIBRATION */ + +#endif /* !defined(CONFIG_NAND_U_BOOT) || defined(CONFIG_NAND_SPL) */ + + return (CONFIG_SYS_MBYTES_SDRAM << 20); +} +#endif /* CONFIG_SPD_EEPROM */ + +#if !defined(CONFIG_NAND_U_BOOT) && !defined(CONFIG_NAND_SPL) +#if defined(CONFIG_440) +u32 mfdcr_any(u32 dcr) +{ + u32 val; + + switch (dcr) { + case SDRAM_R0BAS + 0: + val = mfdcr(SDRAM_R0BAS + 0); + break; + case SDRAM_R0BAS + 1: + val = mfdcr(SDRAM_R0BAS + 1); + break; + case SDRAM_R0BAS + 2: + val = mfdcr(SDRAM_R0BAS + 2); + break; + case SDRAM_R0BAS + 3: + val = mfdcr(SDRAM_R0BAS + 3); + break; + default: + printf("DCR %d not defined in case statement!!!\n", dcr); + val = 0; /* just to satisfy the compiler */ + } + + return val; +} + +void mtdcr_any(u32 dcr, u32 val) +{ + switch (dcr) { + case SDRAM_R0BAS + 0: + mtdcr(SDRAM_R0BAS + 0, val); + break; + case SDRAM_R0BAS + 1: + mtdcr(SDRAM_R0BAS + 1, val); + break; + case SDRAM_R0BAS + 2: + mtdcr(SDRAM_R0BAS + 2, val); + break; + case SDRAM_R0BAS + 3: + mtdcr(SDRAM_R0BAS + 3, val); + break; + default: + printf("DCR %d not defined in case statement!!!\n", dcr); + } +} +#endif /* defined(CONFIG_440) */ +#endif /* !defined(CONFIG_NAND_U_BOOT) && !defined(CONFIG_NAND_SPL) */ + +inline void ppc4xx_ibm_ddr2_register_dump(void) +{ +#if defined(DEBUG) + printf("\nPPC4xx IBM DDR2 Register Dump:\n"); + +#if (defined(CONFIG_440SP) || defined(CONFIG_440SPE) || \ + defined(CONFIG_460EX) || defined(CONFIG_460GT)) + PPC4xx_IBM_DDR2_DUMP_MQ_REGISTER(R0BAS); + PPC4xx_IBM_DDR2_DUMP_MQ_REGISTER(R1BAS); + PPC4xx_IBM_DDR2_DUMP_MQ_REGISTER(R2BAS); + PPC4xx_IBM_DDR2_DUMP_MQ_REGISTER(R3BAS); +#endif /* (defined(CONFIG_440SP) || ... */ +#if defined(CONFIG_405EX) + PPC4xx_IBM_DDR2_DUMP_REGISTER(BESR); + PPC4xx_IBM_DDR2_DUMP_REGISTER(BEARL); + PPC4xx_IBM_DDR2_DUMP_REGISTER(BEARH); + PPC4xx_IBM_DDR2_DUMP_REGISTER(WMIRQ); + PPC4xx_IBM_DDR2_DUMP_REGISTER(PLBOPT); + PPC4xx_IBM_DDR2_DUMP_REGISTER(PUABA); +#endif /* defined(CONFIG_405EX) */ + PPC4xx_IBM_DDR2_DUMP_REGISTER(MB0CF); + PPC4xx_IBM_DDR2_DUMP_REGISTER(MB1CF); + PPC4xx_IBM_DDR2_DUMP_REGISTER(MB2CF); + PPC4xx_IBM_DDR2_DUMP_REGISTER(MB3CF); + PPC4xx_IBM_DDR2_DUMP_REGISTER(MCSTAT); + PPC4xx_IBM_DDR2_DUMP_REGISTER(MCOPT1); + PPC4xx_IBM_DDR2_DUMP_REGISTER(MCOPT2); + PPC4xx_IBM_DDR2_DUMP_REGISTER(MODT0); + PPC4xx_IBM_DDR2_DUMP_REGISTER(MODT1); + PPC4xx_IBM_DDR2_DUMP_REGISTER(MODT2); + PPC4xx_IBM_DDR2_DUMP_REGISTER(MODT3); + PPC4xx_IBM_DDR2_DUMP_REGISTER(CODT); +#if (defined(CONFIG_440SP) || defined(CONFIG_440SPE) || \ + defined(CONFIG_460EX) || defined(CONFIG_460GT)) + PPC4xx_IBM_DDR2_DUMP_REGISTER(VVPR); + PPC4xx_IBM_DDR2_DUMP_REGISTER(OPARS); + /* + * OPART is only used as a trigger register. + * + * No data is contained in this register, and reading or writing + * to is can cause bad things to happen (hangs). Just skip it and + * report "N/A". + */ + printf("%20s = N/A\n", "SDRAM_OPART"); +#endif /* defined(CONFIG_440SP) || ... */ + PPC4xx_IBM_DDR2_DUMP_REGISTER(RTR); + PPC4xx_IBM_DDR2_DUMP_REGISTER(INITPLR0); + PPC4xx_IBM_DDR2_DUMP_REGISTER(INITPLR1); + PPC4xx_IBM_DDR2_DUMP_REGISTER(INITPLR2); + PPC4xx_IBM_DDR2_DUMP_REGISTER(INITPLR3); + PPC4xx_IBM_DDR2_DUMP_REGISTER(INITPLR4); + PPC4xx_IBM_DDR2_DUMP_REGISTER(INITPLR5); + PPC4xx_IBM_DDR2_DUMP_REGISTER(INITPLR6); + PPC4xx_IBM_DDR2_DUMP_REGISTER(INITPLR7); + PPC4xx_IBM_DDR2_DUMP_REGISTER(INITPLR8); + PPC4xx_IBM_DDR2_DUMP_REGISTER(INITPLR9); + PPC4xx_IBM_DDR2_DUMP_REGISTER(INITPLR10); + PPC4xx_IBM_DDR2_DUMP_REGISTER(INITPLR11); + PPC4xx_IBM_DDR2_DUMP_REGISTER(INITPLR12); + PPC4xx_IBM_DDR2_DUMP_REGISTER(INITPLR13); + PPC4xx_IBM_DDR2_DUMP_REGISTER(INITPLR14); + PPC4xx_IBM_DDR2_DUMP_REGISTER(INITPLR15); + PPC4xx_IBM_DDR2_DUMP_REGISTER(RQDC); + PPC4xx_IBM_DDR2_DUMP_REGISTER(RFDC); + PPC4xx_IBM_DDR2_DUMP_REGISTER(RDCC); + PPC4xx_IBM_DDR2_DUMP_REGISTER(DLCR); + PPC4xx_IBM_DDR2_DUMP_REGISTER(CLKTR); + PPC4xx_IBM_DDR2_DUMP_REGISTER(WRDTR); + PPC4xx_IBM_DDR2_DUMP_REGISTER(SDTR1); + PPC4xx_IBM_DDR2_DUMP_REGISTER(SDTR2); + PPC4xx_IBM_DDR2_DUMP_REGISTER(SDTR3); + PPC4xx_IBM_DDR2_DUMP_REGISTER(MMODE); + PPC4xx_IBM_DDR2_DUMP_REGISTER(MEMODE); + PPC4xx_IBM_DDR2_DUMP_REGISTER(ECCES); +#if (defined(CONFIG_440SP) || defined(CONFIG_440SPE) || \ + defined(CONFIG_460EX) || defined(CONFIG_460GT)) + PPC4xx_IBM_DDR2_DUMP_REGISTER(CID); +#endif /* defined(CONFIG_440SP) || ... */ + PPC4xx_IBM_DDR2_DUMP_REGISTER(RID); + PPC4xx_IBM_DDR2_DUMP_REGISTER(FCSR); + PPC4xx_IBM_DDR2_DUMP_REGISTER(RTSR); +#endif /* defined(DEBUG) */ +} + +#endif /* CONFIG_SDRAM_PPC4xx_IBM_DDR2 */ diff --git a/arch/powerpc/cpu/ppc4xx/4xx_ibm_ddr2_autocalib.c b/arch/powerpc/cpu/ppc4xx/4xx_ibm_ddr2_autocalib.c new file mode 100644 index 0000000..aed0ed7 --- /dev/null +++ b/arch/powerpc/cpu/ppc4xx/4xx_ibm_ddr2_autocalib.c @@ -0,0 +1,1251 @@ +/* + * arch/powerpc/cpu/ppc4xx/4xx_ibm_ddr2_autocalib.c + * This SPD SDRAM detection code supports AMCC PPC44x cpu's with a + * DDR2 controller (non Denali Core). Those currently are: + * + * 405: 405EX + * 440/460: 440SP/440SPe/460EX/460GT/460SX + * + * (C) Copyright 2008 Applied Micro Circuits Corporation + * Adam Graham <agraham@amcc.com> + * + * (C) Copyright 2007-2008 + * Stefan Roese, DENX Software Engineering, sr@denx.de. + * + * COPYRIGHT AMCC CORPORATION 2004 + * + * 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 + * + */ + +/* define DEBUG for debugging output (obviously ;-)) */ +#undef DEBUG + +#include <common.h> +#include <ppc4xx.h> +#include <asm/io.h> +#include <asm/processor.h> + +#include "ecc.h" + +#if defined(CONFIG_PPC4xx_DDR_AUTOCALIBRATION) + +/* + * Only compile the DDR auto-calibration code for NOR boot and + * not for NAND boot (NAND SPL and NAND U-Boot - NUB) + */ +#if !defined(CONFIG_NAND_U_BOOT) && !defined(CONFIG_NAND_SPL) + +#define MAXBXCF 4 +#define SDRAM_RXBAS_SHIFT_1M 20 + +#if defined(CONFIG_SYS_DECREMENT_PATTERNS) +#define NUMMEMTESTS 24 +#else +#define NUMMEMTESTS 8 +#endif /* CONFIG_SYS_DECREMENT_PATTERNS */ +#define NUMLOOPS 1 /* configure as you deem approporiate */ +#define NUMMEMWORDS 16 + +#define SDRAM_RDCC_RDSS_VAL(n) SDRAM_RDCC_RDSS_DECODE(ddr_rdss_opt(n)) + +/* Private Structure Definitions */ + +struct autocal_regs { + u32 rffd; + u32 rqfd; +}; + +struct ddrautocal { + u32 rffd; + u32 rffd_min; + u32 rffd_max; + u32 rffd_size; + u32 rqfd; + u32 rqfd_size; + u32 rdcc; + u32 flags; +}; + +struct sdram_timing { + u32 wrdtr; + u32 clktr; +}; + +struct sdram_timing_clks { + u32 wrdtr; + u32 clktr; + u32 rdcc; + u32 flags; +}; + +struct autocal_clks { + struct sdram_timing_clks clocks; + struct ddrautocal autocal; +}; + +/*--------------------------------------------------------------------------+ + * Prototypes + *--------------------------------------------------------------------------*/ +#if defined(CONFIG_PPC4xx_DDR_METHOD_A) +static u32 DQS_calibration_methodA(struct ddrautocal *); +static u32 program_DQS_calibration_methodA(struct ddrautocal *); +#else +static u32 DQS_calibration_methodB(struct ddrautocal *); +static u32 program_DQS_calibration_methodB(struct ddrautocal *); +#endif +static int short_mem_test(u32 *); + +/* + * To provide an interface for board specific config values in this common + * DDR setup code, we implement he "weak" default functions here. They return + * the default value back to the caller. + * + * Please see include/configs/yucca.h for an example fora board specific + * implementation. + */ + +#if !defined(CONFIG_SPD_EEPROM) +u32 __ddr_wrdtr(u32 default_val) +{ + return default_val; +} +u32 ddr_wrdtr(u32) __attribute__((weak, alias("__ddr_wrdtr"))); + +u32 __ddr_clktr(u32 default_val) +{ + return default_val; +} +u32 ddr_clktr(u32) __attribute__((weak, alias("__ddr_clktr"))); + +/* + * Board-specific Platform code can reimplement spd_ddr_init_hang () if needed + */ +void __spd_ddr_init_hang(void) +{ + hang(); +} +void +spd_ddr_init_hang(void) __attribute__((weak, alias("__spd_ddr_init_hang"))); +#endif /* defined(CONFIG_SPD_EEPROM) */ + +ulong __ddr_scan_option(ulong default_val) +{ + return default_val; +} +ulong ddr_scan_option(ulong) __attribute__((weak, alias("__ddr_scan_option"))); + +u32 __ddr_rdss_opt(u32 default_val) +{ + return default_val; +} +u32 ddr_rdss_opt(ulong) __attribute__((weak, alias("__ddr_rdss_opt"))); + + +static u32 *get_membase(int bxcr_num) +{ + ulong bxcf; + u32 *membase; + +#if defined(SDRAM_R0BAS) + /* BAS from Memory Queue rank reg. */ + membase = + (u32 *)(SDRAM_RXBAS_SDBA_DECODE(mfdcr_any(SDRAM_R0BAS+bxcr_num))); + bxcf = 0; /* just to satisfy the compiler */ +#else + /* BAS from SDRAM_MBxCF mem rank reg. */ + mfsdram(SDRAM_MB0CF + (bxcr_num<<2), bxcf); + membase = (u32 *)((bxcf & 0xfff80000) << 3); +#endif + + return membase; +} + +static inline void ecc_clear_status_reg(void) +{ + mtsdram(SDRAM_ECCES, 0xffffffff); +#if defined(SDRAM_R0BAS) + mtdcr(SDRAM_ERRSTATLL, 0xffffffff); +#endif +} + +/* + * Reset and relock memory DLL after SDRAM_CLKTR change + */ +static inline void relock_memory_DLL(void) +{ + u32 reg; + + mtsdram(SDRAM_MCOPT2, SDRAM_MCOPT2_IPTR_EXECUTE); + + do { + mfsdram(SDRAM_MCSTAT, reg); + } while (!(reg & SDRAM_MCSTAT_MIC_COMP)); + + mfsdram(SDRAM_MCOPT2, reg); + mtsdram(SDRAM_MCOPT2, reg | SDRAM_MCOPT2_DCEN_ENABLE); +} + +static int ecc_check_status_reg(void) +{ + u32 ecc_status; + + /* + * Compare suceeded, now check + * if got ecc error. If got an + * ecc error, then don't count + * this as a passing value + */ + mfsdram(SDRAM_ECCES, ecc_status); + if (ecc_status != 0x00000000) { + /* clear on error */ + ecc_clear_status_reg(); + /* ecc check failure */ + return 0; + } + ecc_clear_status_reg(); + sync(); + + return 1; +} + +/* return 1 if passes, 0 if fail */ +static int short_mem_test(u32 *base_address) +{ + int i, j, l; + u32 ecc_mode = 0; + + ulong test[NUMMEMTESTS][NUMMEMWORDS] = { + /* 0 */ {0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, + 0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, + 0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, + 0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF}, + /* 1 */ {0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000, + 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000, + 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000, + 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000}, + /* 2 */ {0xAAAAAAAA, 0xAAAAAAAA, 0x55555555, 0x55555555, + 0xAAAAAAAA, 0xAAAAAAAA, 0x55555555, 0x55555555, + 0xAAAAAAAA, 0xAAAAAAAA, 0x55555555, 0x55555555, + 0xAAAAAAAA, 0xAAAAAAAA, 0x55555555, 0x55555555}, + /* 3 */ {0x55555555, 0x55555555, 0xAAAAAAAA, 0xAAAAAAAA, + 0x55555555, 0x55555555, 0xAAAAAAAA, 0xAAAAAAAA, + 0x55555555, 0x55555555, 0xAAAAAAAA, 0xAAAAAAAA, + 0x55555555, 0x55555555, 0xAAAAAAAA, 0xAAAAAAAA}, + /* 4 */ {0xA5A5A5A5, 0xA5A5A5A5, 0x5A5A5A5A, 0x5A5A5A5A, + 0xA5A5A5A5, 0xA5A5A5A5, 0x5A5A5A5A, 0x5A5A5A5A, + 0xA5A5A5A5, 0xA5A5A5A5, 0x5A5A5A5A, 0x5A5A5A5A, + 0xA5A5A5A5, 0xA5A5A5A5, 0x5A5A5A5A, 0x5A5A5A5A}, + /* 5 */ {0x5A5A5A5A, 0x5A5A5A5A, 0xA5A5A5A5, 0xA5A5A5A5, + 0x5A5A5A5A, 0x5A5A5A5A, 0xA5A5A5A5, 0xA5A5A5A5, + 0x5A5A5A5A, 0x5A5A5A5A, 0xA5A5A5A5, 0xA5A5A5A5, + 0x5A5A5A5A, 0x5A5A5A5A, 0xA5A5A5A5, 0xA5A5A5A5}, + /* 6 */ {0xAA55AA55, 0xAA55AA55, 0x55AA55AA, 0x55AA55AA, + 0xAA55AA55, 0xAA55AA55, 0x55AA55AA, 0x55AA55AA, + 0xAA55AA55, 0xAA55AA55, 0x55AA55AA, 0x55AA55AA, + 0xAA55AA55, 0xAA55AA55, 0x55AA55AA, 0x55AA55AA}, + /* 7 */ {0x55AA55AA, 0x55AA55AA, 0xAA55AA55, 0xAA55AA55, + 0x55AA55AA, 0x55AA55AA, 0xAA55AA55, 0xAA55AA55, + 0x55AA55AA, 0x55AA55AA, 0xAA55AA55, 0xAA55AA55, + 0x55AA55AA, 0x55AA55AA, 0xAA55AA55, 0xAA55AA55}, + +#if defined(CONFIG_SYS_DECREMENT_PATTERNS) + /* 8 */ {0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff}, + /* 9 */ {0xfffefffe, 0xfffefffe, 0xfffefffe, 0xfffefffe, + 0xfffefffe, 0xfffefffe, 0xfffefffe, 0xfffefffe, + 0xfffefffe, 0xfffefffe, 0xfffefffe, 0xfffefffe, + 0xfffefffe, 0xfffefffe, 0xfffefffe, 0xfffefffe}, + /* 10 */{0xfffdfffd, 0xfffdfffd, 0xfffdffff, 0xfffdfffd, + 0xfffdfffd, 0xfffdfffd, 0xfffdffff, 0xfffdfffd, + 0xfffdfffd, 0xfffdfffd, 0xfffdffff, 0xfffdfffd, + 0xfffdfffd, 0xfffdfffd, 0xfffdffff, 0xfffdfffd}, + /* 11 */{0xfffcfffc, 0xfffcfffc, 0xfffcfffc, 0xfffcfffc, + 0xfffcfffc, 0xfffcfffc, 0xfffcfffc, 0xfffcfffc, + 0xfffcfffc, 0xfffcfffc, 0xfffcfffc, 0xfffcfffc, + 0xfffcfffc, 0xfffcfffc, 0xfffcfffc, 0xfffcfffc}, + /* 12 */{0xfffbfffb, 0xfffffffb, 0xfffffffb, 0xfffffffb, + 0xfffbfffb, 0xfffffffb, 0xfffffffb, 0xfffffffb, + 0xfffbfffb, 0xfffffffb, 0xfffffffb, 0xfffffffb, + 0xfffbfffb, 0xfffffffb, 0xfffffffb, 0xfffffffb}, + /* 13 */{0xfffafffa, 0xfffafffa, 0xfffffffa, 0xfffafffa, + 0xfffafffa, 0xfffafffa, 0xfffafffa, 0xfffafffa, + 0xfffafffa, 0xfffafffa, 0xfffafffa, 0xfffafffa, + 0xfffafffa, 0xfffafffa, 0xfffafffa, 0xfffafffa}, + /* 14 */{0xfff9fff9, 0xfff9fff9, 0xfff9fff9, 0xfff9fff9, + 0xfff9fff9, 0xfff9fff9, 0xfff9fff9, 0xfff9fff9, + 0xfff9fff9, 0xfff9fff9, 0xfff9fff9, 0xfff9fff9, + 0xfff9fff9, 0xfff9fff9, 0xfff9fff9, 0xfff9fff9}, + /* 15 */{0xfff8fff8, 0xfff8fff8, 0xfff8fff8, 0xfff8fff8, + 0xfff8fff8, 0xfff8fff8, 0xfff8fff8, 0xfff8fff8, + 0xfff8fff8, 0xfff8fff8, 0xfff8fff8, 0xfff8fff8, + 0xfff8fff8, 0xfff8fff8, 0xfff8fff8, 0xfff8fff8}, + /* 16 */{0xfff7fff7, 0xfff7ffff, 0xfff7fff7, 0xfff7fff7, + 0xfff7fff7, 0xfff7ffff, 0xfff7fff7, 0xfff7fff7, + 0xfff7fff7, 0xfff7ffff, 0xfff7fff7, 0xfff7fff7, + 0xfff7ffff, 0xfff7ffff, 0xfff7fff7, 0xfff7fff7}, + /* 17 */{0xfff6fff5, 0xfff6ffff, 0xfff6fff6, 0xfff6fff7, + 0xfff6fff5, 0xfff6ffff, 0xfff6fff6, 0xfff6fff7, + 0xfff6fff5, 0xfff6ffff, 0xfff6fff6, 0xfff6fff7, + 0xfff6fff5, 0xfff6ffff, 0xfff6fff6, 0xfff6fff7}, + /* 18 */{0xfff5fff4, 0xfff5ffff, 0xfff5fff5, 0xfff5fff5, + 0xfff5fff4, 0xfff5ffff, 0xfff5fff5, 0xfff5fff5, + 0xfff5fff4, 0xfff5ffff, 0xfff5fff5, 0xfff5fff5, + 0xfff5fff4, 0xfff5ffff, 0xfff5fff5, 0xfff5fff5}, + /* 19 */{0xfff4fff3, 0xfff4ffff, 0xfff4fff4, 0xfff4fff4, + 0xfff4fff3, 0xfff4ffff, 0xfff4fff4, 0xfff4fff4, + 0xfff4fff3, 0xfff4ffff, 0xfff4fff4, 0xfff4fff4, + 0xfff4fff3, 0xfff4ffff, 0xfff4fff4, 0xfff4fff4}, + /* 20 */{0xfff3fff2, 0xfff3ffff, 0xfff3fff3, 0xfff3fff3, + 0xfff3fff2, 0xfff3ffff, 0xfff3fff3, 0xfff3fff3, + 0xfff3fff2, 0xfff3ffff, 0xfff3fff3, 0xfff3fff3, + 0xfff3fff2, 0xfff3ffff, 0xfff3fff3, 0xfff3fff3}, + /* 21 */{0xfff2ffff, 0xfff2ffff, 0xfff2fff2, 0xfff2fff2, + 0xfff2ffff, 0xfff2ffff, 0xfff2fff2, 0xfff2fff2, + 0xfff2ffff, 0xfff2ffff, 0xfff2fff2, 0xfff2fff2, + 0xfff2ffff, 0xfff2ffff, 0xfff2fff2, 0xfff2fff2}, + /* 22 */{0xfff1ffff, 0xfff1ffff, 0xfff1fff1, 0xfff1fff1, + 0xfff1ffff, 0xfff1ffff, 0xfff1fff1, 0xfff1fff1, + 0xfff1ffff, 0xfff1ffff, 0xfff1fff1, 0xfff1fff1, + 0xfff1ffff, 0xfff1ffff, 0xfff1fff1, 0xfff1fff1}, + /* 23 */{0xfff0fff0, 0xfff0fff0, 0xfff0fff0, 0xfff0fff0, + 0xfff0fff0, 0xfff0fff0, 0xfff0fff0, 0xfff0fff0, + 0xfff0fff0, 0xfff0fff0, 0xfff0fff0, 0xfff0fff0, + 0xfff0fff0, 0xfff0fffe, 0xfff0fff0, 0xfff0fff0}, +#endif /* CONFIG_SYS_DECREMENT_PATTERNS */ + }; + + mfsdram(SDRAM_MCOPT1, ecc_mode); + if ((ecc_mode & SDRAM_MCOPT1_MCHK_CHK_REP) == + SDRAM_MCOPT1_MCHK_CHK_REP) { + ecc_clear_status_reg(); + sync(); + ecc_mode = 1; + } else { + ecc_mode = 0; + } + + /* + * Run the short memory test. + */ + for (i = 0; i < NUMMEMTESTS; i++) { + for (j = 0; j < NUMMEMWORDS; j++) { + base_address[j] = test[i][j]; + ppcDcbf((ulong)&(base_address[j])); + } + sync(); + iobarrier_rw(); + for (l = 0; l < NUMLOOPS; l++) { + for (j = 0; j < NUMMEMWORDS; j++) { + if (base_address[j] != test[i][j]) { + ppcDcbf((u32)&(base_address[j])); + return 0; + } else { + if (ecc_mode) { + if (!ecc_check_status_reg()) + return 0; + } + } + ppcDcbf((u32)&(base_address[j])); + } /* for (j = 0; j < NUMMEMWORDS; j++) */ + sync(); + iobarrier_rw(); + } /* for (l=0; l<NUMLOOPS; l++) */ + } + + return 1; +} + +#if defined(CONFIG_PPC4xx_DDR_METHOD_A) +/*-----------------------------------------------------------------------------+ +| program_DQS_calibration_methodA. ++-----------------------------------------------------------------------------*/ +static u32 program_DQS_calibration_methodA(struct ddrautocal *ddrcal) +{ + u32 pass_result = 0; + +#ifdef DEBUG + ulong temp; + + mfsdram(SDRAM_RDCC, temp); + debug("<%s>SDRAM_RDCC=0x%08x\n", __func__, temp); +#endif + + pass_result = DQS_calibration_methodA(ddrcal); + + return pass_result; +} + +/* + * DQS_calibration_methodA() + * + * Autocalibration Method A + * + * ARRAY [Entire DQS Range] DQS_Valid_Window ; initialized to all zeros + * ARRAY [Entire FDBK Range] FDBK_Valid_Window; initialized to all zeros + * MEMWRITE(addr, expected_data); + * for (i = 0; i < Entire DQS Range; i++) { RQDC.RQFD + * for (j = 0; j < Entire FDBK Range; j++) { RFDC.RFFD + * MEMREAD(addr, actual_data); + * if (actual_data == expected_data) { + * DQS_Valid_Window[i] = 1; RQDC.RQFD + * FDBK_Valid_Window[i][j] = 1; RFDC.RFFD + * } + * } + * } + */ +static u32 DQS_calibration_methodA(struct ddrautocal *cal) +{ + ulong rfdc_reg; + ulong rffd; + + ulong rqdc_reg; + ulong rqfd; + + u32 *membase; + ulong bxcf; + int rqfd_average; + int bxcr_num; + int rffd_average; + int pass; + u32 passed = 0; + + int in_window; + struct autocal_regs curr_win_min; + struct autocal_regs curr_win_max; + struct autocal_regs best_win_min; + struct autocal_regs best_win_max; + struct autocal_regs loop_win_min; + struct autocal_regs loop_win_max; + +#ifdef DEBUG + ulong temp; +#endif + ulong rdcc; + + char slash[] = "\\|/-\\|/-"; + int loopi = 0; + + /* start */ + in_window = 0; + + memset(&curr_win_min, 0, sizeof(curr_win_min)); + memset(&curr_win_max, 0, sizeof(curr_win_max)); + memset(&best_win_min, 0, sizeof(best_win_min)); + memset(&best_win_max, 0, sizeof(best_win_max)); + memset(&loop_win_min, 0, sizeof(loop_win_min)); + memset(&loop_win_max, 0, sizeof(loop_win_max)); + + rdcc = 0; + + /* + * Program RDCC register + * Read sample cycle auto-update enable + */ + mtsdram(SDRAM_RDCC, + ddr_rdss_opt(SDRAM_RDCC_RDSS_T2) | SDRAM_RDCC_RSAE_ENABLE); + +#ifdef DEBUG + mfsdram(SDRAM_RDCC, temp); + debug("<%s>SDRAM_RDCC=0x%x\n", __func__, temp); + mfsdram(SDRAM_RTSR, temp); + debug("<%s>SDRAM_RTSR=0x%x\n", __func__, temp); + mfsdram(SDRAM_FCSR, temp); + debug("<%s>SDRAM_FCSR=0x%x\n", __func__, temp); +#endif + + /* + * Program RQDC register + * Internal DQS delay mechanism enable + */ + mtsdram(SDRAM_RQDC, + SDRAM_RQDC_RQDE_ENABLE | SDRAM_RQDC_RQFD_ENCODE(0x00)); + +#ifdef DEBUG + mfsdram(SDRAM_RQDC, temp); + debug("<%s>SDRAM_RQDC=0x%x\n", __func__, temp); +#endif + + /* + * Program RFDC register + * Set Feedback Fractional Oversample + * Auto-detect read sample cycle enable + */ + mtsdram(SDRAM_RFDC, SDRAM_RFDC_ARSE_ENABLE | + SDRAM_RFDC_RFOS_ENCODE(0) | SDRAM_RFDC_RFFD_ENCODE(0)); + +#ifdef DEBUG + mfsdram(SDRAM_RFDC, temp); + debug("<%s>SDRAM_RFDC=0x%x\n", __func__, temp); +#endif + + putc(' '); + for (rqfd = 0; rqfd <= SDRAM_RQDC_RQFD_MAX; rqfd++) { + + mfsdram(SDRAM_RQDC, rqdc_reg); + rqdc_reg &= ~(SDRAM_RQDC_RQFD_MASK); + mtsdram(SDRAM_RQDC, rqdc_reg | SDRAM_RQDC_RQFD_ENCODE(rqfd)); + + putc('\b'); + putc(slash[loopi++ % 8]); + + curr_win_min.rffd = 0; + curr_win_max.rffd = 0; + in_window = 0; + + for (rffd = 0, pass = 0; rffd <= SDRAM_RFDC_RFFD_MAX; rffd++) { + mfsdram(SDRAM_RFDC, rfdc_reg); + rfdc_reg &= ~(SDRAM_RFDC_RFFD_MASK); + mtsdram(SDRAM_RFDC, + rfdc_reg | SDRAM_RFDC_RFFD_ENCODE(rffd)); + + for (bxcr_num = 0; bxcr_num < MAXBXCF; bxcr_num++) { + mfsdram(SDRAM_MB0CF + (bxcr_num<<2), bxcf); + + /* Banks enabled */ + if (bxcf & SDRAM_BXCF_M_BE_MASK) { + /* Bank is enabled */ + membase = get_membase(bxcr_num); + pass = short_mem_test(membase); + } /* if bank enabled */ + } /* for bxcr_num */ + + /* If this value passed update RFFD windows */ + if (pass && !in_window) { /* at the start of window */ + in_window = 1; + curr_win_min.rffd = curr_win_max.rffd = rffd; + curr_win_min.rqfd = curr_win_max.rqfd = rqfd; + mfsdram(SDRAM_RDCC, rdcc); /*record this value*/ + } else if (!pass && in_window) { /* at end of window */ + in_window = 0; + } else if (pass && in_window) { /* within the window */ + curr_win_max.rffd = rffd; + curr_win_max.rqfd = rqfd; + } + /* else if (!pass && !in_window) + skip - no pass, not currently in a window */ + + if (in_window) { + if ((curr_win_max.rffd - curr_win_min.rffd) > + (best_win_max.rffd - best_win_min.rffd)) { + best_win_min.rffd = curr_win_min.rffd; + best_win_max.rffd = curr_win_max.rffd; + + best_win_min.rqfd = curr_win_min.rqfd; + best_win_max.rqfd = curr_win_max.rqfd; + cal->rdcc = rdcc; + } + passed = 1; + } + } /* RFDC.RFFD */ + + /* + * save-off the best window results of the RFDC.RFFD + * for this RQDC.RQFD setting + */ + /* + * if (just ended RFDC.RFDC loop pass window) > + * (prior RFDC.RFFD loop pass window) + */ + if ((best_win_max.rffd - best_win_min.rffd) > + (loop_win_max.rffd - loop_win_min.rffd)) { + loop_win_min.rffd = best_win_min.rffd; + loop_win_max.rffd = best_win_max.rffd; + loop_win_min.rqfd = rqfd; + loop_win_max.rqfd = rqfd; + debug("RQFD.min 0x%08x, RQFD.max 0x%08x, " + "RFFD.min 0x%08x, RFFD.max 0x%08x\n", + loop_win_min.rqfd, loop_win_max.rqfd, + loop_win_min.rffd, loop_win_max.rffd); + } + } /* RQDC.RQFD */ + + putc('\b'); + + debug("\n"); + + if ((loop_win_min.rffd == 0) && (loop_win_max.rffd == 0) && + (best_win_min.rffd == 0) && (best_win_max.rffd == 0) && + (best_win_min.rqfd == 0) && (best_win_max.rqfd == 0)) { + passed = 0; + } + + /* + * Need to program RQDC before RFDC. + */ + debug("<%s> RQFD Min: 0x%x\n", __func__, loop_win_min.rqfd); + debug("<%s> RQFD Max: 0x%x\n", __func__, loop_win_max.rqfd); + rqfd_average = loop_win_max.rqfd; + + if (rqfd_average < 0) + rqfd_average = 0; + + if (rqfd_average > SDRAM_RQDC_RQFD_MAX) + rqfd_average = SDRAM_RQDC_RQFD_MAX; + + debug("<%s> RFFD average: 0x%08x\n", __func__, rqfd_average); + mtsdram(SDRAM_RQDC, (rqdc_reg & ~SDRAM_RQDC_RQFD_MASK) | + SDRAM_RQDC_RQFD_ENCODE(rqfd_average)); + + debug("<%s> RFFD Min: 0x%08x\n", __func__, loop_win_min.rffd); + debug("<%s> RFFD Max: 0x%08x\n", __func__, loop_win_max.rffd); + rffd_average = ((loop_win_min.rffd + loop_win_max.rffd) / 2); + + if (rffd_average < 0) + rffd_average = 0; + + if (rffd_average > SDRAM_RFDC_RFFD_MAX) + rffd_average = SDRAM_RFDC_RFFD_MAX; + + debug("<%s> RFFD average: 0x%08x\n", __func__, rffd_average); + mtsdram(SDRAM_RFDC, rfdc_reg | SDRAM_RFDC_RFFD_ENCODE(rffd_average)); + + /* if something passed, then return the size of the largest window */ + if (passed != 0) { + passed = loop_win_max.rffd - loop_win_min.rffd; + cal->rqfd = rqfd_average; + cal->rffd = rffd_average; + cal->rffd_min = loop_win_min.rffd; + cal->rffd_max = loop_win_max.rffd; + } + + return (u32)passed; +} + +#else /* !defined(CONFIG_PPC4xx_DDR_METHOD_A) */ + +/*-----------------------------------------------------------------------------+ +| program_DQS_calibration_methodB. ++-----------------------------------------------------------------------------*/ +static u32 program_DQS_calibration_methodB(struct ddrautocal *ddrcal) +{ + u32 pass_result = 0; + +#ifdef DEBUG + ulong temp; +#endif + + /* + * Program RDCC register + * Read sample cycle auto-update enable + */ + mtsdram(SDRAM_RDCC, + ddr_rdss_opt(SDRAM_RDCC_RDSS_T2) | SDRAM_RDCC_RSAE_ENABLE); + +#ifdef DEBUG + mfsdram(SDRAM_RDCC, temp); + debug("<%s>SDRAM_RDCC=0x%08x\n", __func__, temp); +#endif + + /* + * Program RQDC register + * Internal DQS delay mechanism enable + */ + mtsdram(SDRAM_RQDC, +#if defined(CONFIG_DDR_RQDC_START_VAL) + SDRAM_RQDC_RQDE_ENABLE | + SDRAM_RQDC_RQFD_ENCODE(CONFIG_DDR_RQDC_START_VAL)); +#else + SDRAM_RQDC_RQDE_ENABLE | SDRAM_RQDC_RQFD_ENCODE(0x38)); +#endif + +#ifdef DEBUG + mfsdram(SDRAM_RQDC, temp); + debug("<%s>SDRAM_RQDC=0x%08x\n", __func__, temp); +#endif + + /* + * Program RFDC register + * Set Feedback Fractional Oversample + * Auto-detect read sample cycle enable + */ + mtsdram(SDRAM_RFDC, SDRAM_RFDC_ARSE_ENABLE | + SDRAM_RFDC_RFOS_ENCODE(0) | + SDRAM_RFDC_RFFD_ENCODE(0)); + +#ifdef DEBUG + mfsdram(SDRAM_RFDC, temp); + debug("<%s>SDRAM_RFDC=0x%08x\n", __func__, temp); +#endif + + pass_result = DQS_calibration_methodB(ddrcal); + + return pass_result; +} + +/* + * DQS_calibration_methodB() + * + * Autocalibration Method B + * + * ARRAY [Entire DQS Range] DQS_Valid_Window ; initialized to all zeros + * ARRAY [Entire Feedback Range] FDBK_Valid_Window; initialized to all zeros + * MEMWRITE(addr, expected_data); + * Initialialize the DQS delay to 80 degrees (MCIF0_RRQDC[RQFD]=0x38). + * + * for (j = 0; j < Entire Feedback Range; j++) { + * MEMREAD(addr, actual_data); + * if (actual_data == expected_data) { + * FDBK_Valid_Window[j] = 1; + * } + * } + * + * Set MCIF0_RFDC[RFFD] to the middle of the FDBK_Valid_Window. + * + * for (i = 0; i < Entire DQS Range; i++) { + * MEMREAD(addr, actual_data); + * if (actual_data == expected_data) { + * DQS_Valid_Window[i] = 1; + * } + * } + * + * Set MCIF0_RRQDC[RQFD] to the middle of the DQS_Valid_Window. + */ +/*-----------------------------------------------------------------------------+ +| DQS_calibration_methodB. ++-----------------------------------------------------------------------------*/ +static u32 DQS_calibration_methodB(struct ddrautocal *cal) +{ + ulong rfdc_reg; + ulong rffd; + + ulong rqdc_reg; + ulong rqfd; + + ulong rdcc; + + u32 *membase; + ulong bxcf; + int rqfd_average; + int bxcr_num; + int rffd_average; + int pass; + uint passed = 0; + + int in_window; + u32 curr_win_min, curr_win_max; + u32 best_win_min, best_win_max; + u32 size = 0; + + /*------------------------------------------------------------------ + | Test to determine the best read clock delay tuning bits. + | + | Before the DDR controller can be used, the read clock delay needs to + | be set. This is SDRAM_RQDC[RQFD] and SDRAM_RFDC[RFFD]. + | This value cannot be hardcoded into the program because it changes + | depending on the board's setup and environment. + | To do this, all delay values are tested to see if they + | work or not. By doing this, you get groups of fails with groups of + | passing values. The idea is to find the start and end of a passing + | window and take the center of it to use as the read clock delay. + | + | A failure has to be seen first so that when we hit a pass, we know + | that it is truely the start of the window. If we get passing values + | to start off with, we don't know if we are at the start of the window + | + | The code assumes that a failure will always be found. + | If a failure is not found, there is no easy way to get the middle + | of the passing window. I guess we can pretty much pick any value + | but some values will be better than others. Since the lowest speed + | we can clock the DDR interface at is 200 MHz (2x 100 MHz PLB speed), + | from experimentation it is safe to say you will always have a failure + +-----------------------------------------------------------------*/ + + debug("\n\n"); + + in_window = 0; + rdcc = 0; + + curr_win_min = curr_win_max = 0; + best_win_min = best_win_max = 0; + for (rffd = 0; rffd <= SDRAM_RFDC_RFFD_MAX; rffd++) { + mfsdram(SDRAM_RFDC, rfdc_reg); + rfdc_reg &= ~(SDRAM_RFDC_RFFD_MASK); + mtsdram(SDRAM_RFDC, rfdc_reg | SDRAM_RFDC_RFFD_ENCODE(rffd)); + + pass = 1; + for (bxcr_num = 0; bxcr_num < MAXBXCF; bxcr_num++) { + mfsdram(SDRAM_MB0CF + (bxcr_num<<2), bxcf); + + /* Banks enabled */ + if (bxcf & SDRAM_BXCF_M_BE_MASK) { + /* Bank is enabled */ + membase = get_membase(bxcr_num); + pass &= short_mem_test(membase); + } /* if bank enabled */ + } /* for bxcf_num */ + + /* If this value passed */ + if (pass && !in_window) { /* start of passing window */ + in_window = 1; + curr_win_min = curr_win_max = rffd; + mfsdram(SDRAM_RDCC, rdcc); /* record this value */ + } else if (!pass && in_window) { /* end passing window */ + in_window = 0; + } else if (pass && in_window) { /* within the passing window */ + curr_win_max = rffd; + } + + if (in_window) { + if ((curr_win_max - curr_win_min) > + (best_win_max - best_win_min)) { + best_win_min = curr_win_min; + best_win_max = curr_win_max; + cal->rdcc = rdcc; + } + passed = 1; + } + } /* for rffd */ + + if ((best_win_min == 0) && (best_win_max == 0)) + passed = 0; + else + size = best_win_max - best_win_min; + + debug("RFFD Min: 0x%x\n", best_win_min); + debug("RFFD Max: 0x%x\n", best_win_max); + rffd_average = ((best_win_min + best_win_max) / 2); + + cal->rffd_min = best_win_min; + cal->rffd_max = best_win_max; + + if (rffd_average < 0) + rffd_average = 0; + + if (rffd_average > SDRAM_RFDC_RFFD_MAX) + rffd_average = SDRAM_RFDC_RFFD_MAX; + + mtsdram(SDRAM_RFDC, rfdc_reg | SDRAM_RFDC_RFFD_ENCODE(rffd_average)); + + rffd = rffd_average; + in_window = 0; + + curr_win_min = curr_win_max = 0; + best_win_min = best_win_max = 0; + for (rqfd = 0; rqfd <= SDRAM_RQDC_RQFD_MAX; rqfd++) { + mfsdram(SDRAM_RQDC, rqdc_reg); + rqdc_reg &= ~(SDRAM_RQDC_RQFD_MASK); + mtsdram(SDRAM_RQDC, rqdc_reg | SDRAM_RQDC_RQFD_ENCODE(rqfd)); + + pass = 1; + for (bxcr_num = 0; bxcr_num < MAXBXCF; bxcr_num++) { + + mfsdram(SDRAM_MB0CF + (bxcr_num<<2), bxcf); + + /* Banks enabled */ + if (bxcf & SDRAM_BXCF_M_BE_MASK) { + /* Bank is enabled */ + membase = get_membase(bxcr_num); + pass &= short_mem_test(membase); + } /* if bank enabled */ + } /* for bxcf_num */ + + /* If this value passed */ + if (pass && !in_window) { + in_window = 1; + curr_win_min = curr_win_max = rqfd; + } else if (!pass && in_window) { + in_window = 0; + } else if (pass && in_window) { + curr_win_max = rqfd; + } + + if (in_window) { + if ((curr_win_max - curr_win_min) > + (best_win_max - best_win_min)) { + best_win_min = curr_win_min; + best_win_max = curr_win_max; + } + passed = 1; + } + } /* for rqfd */ + + if ((best_win_min == 0) && (best_win_max == 0)) + passed = 0; + + debug("RQFD Min: 0x%x\n", best_win_min); + debug("RQFD Max: 0x%x\n", best_win_max); + rqfd_average = ((best_win_min + best_win_max) / 2); + + if (rqfd_average < 0) + rqfd_average = 0; + + if (rqfd_average > SDRAM_RQDC_RQFD_MAX) + rqfd_average = SDRAM_RQDC_RQFD_MAX; + + mtsdram(SDRAM_RQDC, (rqdc_reg & ~SDRAM_RQDC_RQFD_MASK) | + SDRAM_RQDC_RQFD_ENCODE(rqfd_average)); + + mfsdram(SDRAM_RQDC, rqdc_reg); + mfsdram(SDRAM_RFDC, rfdc_reg); + + /* + * Need to program RQDC before RFDC. The value is read above. + * That is the reason why auto cal not work. + * See, comments below. + */ + mtsdram(SDRAM_RQDC, rqdc_reg); + mtsdram(SDRAM_RFDC, rfdc_reg); + + debug("RQDC: 0x%08X\n", rqdc_reg); + debug("RFDC: 0x%08X\n", rfdc_reg); + + /* if something passed, then return the size of the largest window */ + if (passed != 0) { + passed = size; + cal->rqfd = rqfd_average; + cal->rffd = rffd_average; + } + + return (uint)passed; +} +#endif /* defined(CONFIG_PPC4xx_DDR_METHOD_A) */ + +/* + * Default table for DDR auto-calibration of all + * possible WRDTR and CLKTR values. + * Table format is: + * {SDRAM_WRDTR.[WDTR], SDRAM_CLKTR.[CKTR]} + * + * Table is terminated with {-1, -1} value pair. + * + * Board vendors can specify their own board specific subset of + * known working {SDRAM_WRDTR.[WDTR], SDRAM_CLKTR.[CKTR]} value + * pairs via a board defined ddr_scan_option() function. + */ +struct sdram_timing full_scan_options[] = { + {0, 0}, {0, 1}, {0, 2}, {0, 3}, + {1, 0}, {1, 1}, {1, 2}, {1, 3}, + {2, 0}, {2, 1}, {2, 2}, {2, 3}, + {3, 0}, {3, 1}, {3, 2}, {3, 3}, + {4, 0}, {4, 1}, {4, 2}, {4, 3}, + {5, 0}, {5, 1}, {5, 2}, {5, 3}, + {6, 0}, {6, 1}, {6, 2}, {6, 3}, + {-1, -1} +}; + +/*---------------------------------------------------------------------------+ +| DQS_calibration. ++----------------------------------------------------------------------------*/ +u32 DQS_autocalibration(void) +{ + u32 wdtr; + u32 clkp; + u32 result = 0; + u32 best_result = 0; + u32 best_rdcc; + struct ddrautocal ddrcal; + struct autocal_clks tcal; + ulong rfdc_reg; + ulong rqdc_reg; + u32 val; + int verbose_lvl = 0; + char *str; + char slash[] = "\\|/-\\|/-"; + int loopi = 0; + struct sdram_timing *scan_list; + +#if defined(DEBUG_PPC4xx_DDR_AUTOCALIBRATION) + int i; + char tmp[64]; /* long enough for environment variables */ +#endif + + memset(&tcal, 0, sizeof(tcal)); + + ddr_scan_option((ulong)full_scan_options); + + scan_list = + (struct sdram_timing *)ddr_scan_option((ulong)full_scan_options); + + mfsdram(SDRAM_MCOPT1, val); + if ((val & SDRAM_MCOPT1_MCHK_CHK_REP) == SDRAM_MCOPT1_MCHK_CHK_REP) + str = "ECC Auto calibration -"; + else + str = "Auto calibration -"; + + puts(str); + +#if defined(DEBUG_PPC4xx_DDR_AUTOCALIBRATION) + i = getenv_r("autocalib", tmp, sizeof(tmp)); + if (i < 0) + strcpy(tmp, CONFIG_AUTOCALIB); + + if (strcmp(tmp, "final") == 0) { + /* display the final autocalibration results only */ + verbose_lvl = 1; + } else if (strcmp(tmp, "loop") == 0) { + /* display summary autocalibration info per iteration */ + verbose_lvl = 2; + } else if (strcmp(tmp, "display") == 0) { + /* display full debug autocalibration window info. */ + verbose_lvl = 3; + } +#endif /* (DEBUG_PPC4xx_DDR_AUTOCALIBRATION) */ + + best_rdcc = (SDRAM_RDCC_RDSS_T4 >> 30); + + while ((scan_list->wrdtr != -1) && (scan_list->clktr != -1)) { + wdtr = scan_list->wrdtr; + clkp = scan_list->clktr; + + mfsdram(SDRAM_WRDTR, val); + val &= ~(SDRAM_WRDTR_LLWP_MASK | SDRAM_WRDTR_WTR_MASK); + mtsdram(SDRAM_WRDTR, (val | + ddr_wrdtr(SDRAM_WRDTR_LLWP_1_CYC | (wdtr << 25)))); + + mtsdram(SDRAM_CLKTR, clkp << 30); + + relock_memory_DLL(); + + putc('\b'); + putc(slash[loopi++ % 8]); + +#ifdef DEBUG + debug("\n"); + debug("*** --------------\n"); + mfsdram(SDRAM_WRDTR, val); + debug("*** SDRAM_WRDTR set to 0x%08x\n", val); + mfsdram(SDRAM_CLKTR, val); + debug("*** SDRAM_CLKTR set to 0x%08x\n", val); +#endif + + debug("\n"); + if (verbose_lvl > 2) { + printf("*** SDRAM_WRDTR (wdtr) set to %d\n", wdtr); + printf("*** SDRAM_CLKTR (clkp) set to %d\n", clkp); + } + + memset(&ddrcal, 0, sizeof(ddrcal)); + + /* + * DQS calibration. + */ + /* + * program_DQS_calibration_method[A|B]() returns 0 if no + * passing RFDC.[RFFD] window is found or returns the size + * of the best passing window; in the case of a found passing + * window, the ddrcal will contain the values of the best + * window RQDC.[RQFD] and RFDC.[RFFD]. + */ + + /* + * Call PPC4xx SDRAM DDR autocalibration methodA or methodB. + * Default is methodB. + * Defined the autocalibration method in the board specific + * header file. + * Please see include/configs/kilauea.h for an example for + * a board specific implementation. + */ +#if defined(CONFIG_PPC4xx_DDR_METHOD_A) + result = program_DQS_calibration_methodA(&ddrcal); +#else + result = program_DQS_calibration_methodB(&ddrcal); +#endif + + sync(); + + /* + * Clear potential errors resulting from auto-calibration. + * If not done, then we could get an interrupt later on when + * exceptions are enabled. + */ + set_mcsr(get_mcsr()); + + val = ddrcal.rdcc; /* RDCC from the best passing window */ + + udelay(100); + + if (verbose_lvl > 1) { + char *tstr; + switch ((val >> 30)) { + case 0: + if (result != 0) + tstr = "T1"; + else + tstr = "N/A"; + break; + case 1: + tstr = "T2"; + break; + case 2: + tstr = "T3"; + break; + case 3: + tstr = "T4"; + break; + default: + tstr = "unknown"; + break; + } + printf("** WRDTR(%d) CLKTR(%d), Wind (%d), best (%d), " + "max-min(0x%04x)(0x%04x), RDCC: %s\n", + wdtr, clkp, result, best_result, + ddrcal.rffd_min, ddrcal.rffd_max, tstr); + } + + /* + * The DQS calibration "result" is either "0" + * if no passing window was found, or is the + * size of the RFFD passing window. + */ + /* + * want the lowest Read Sample Cycle Select + */ + val = SDRAM_RDCC_RDSS_DECODE(val); + debug("*** (%d) (%d) current_rdcc, best_rdcc\n", + val, best_rdcc); + + if ((result != 0) && + (val >= SDRAM_RDCC_RDSS_VAL(SDRAM_RDCC_RDSS_T2))) { + if (((result == best_result) && (val < best_rdcc)) || + ((result > best_result) && (val <= best_rdcc))) { + tcal.autocal.flags = 1; + debug("*** (%d)(%d) result passed window " + "size: 0x%08x, rqfd = 0x%08x, " + "rffd = 0x%08x, rdcc = 0x%08x\n", + wdtr, clkp, result, ddrcal.rqfd, + ddrcal.rffd, ddrcal.rdcc); + + /* + * Save the SDRAM_WRDTR and SDRAM_CLKTR + * settings for the largest returned + * RFFD passing window size. + */ + best_rdcc = val; + tcal.clocks.wrdtr = wdtr; + tcal.clocks.clktr = clkp; + tcal.clocks.rdcc = SDRAM_RDCC_RDSS_ENCODE(val); + tcal.autocal.rqfd = ddrcal.rqfd; + tcal.autocal.rffd = ddrcal.rffd; + best_result = result; + + if (verbose_lvl > 2) { + printf("** (%d)(%d) " + "best result: 0x%04x\n", + wdtr, clkp, + best_result); + printf("** (%d)(%d) " + "best WRDTR: 0x%04x\n", + wdtr, clkp, + tcal.clocks.wrdtr); + printf("** (%d)(%d) " + "best CLKTR: 0x%04x\n", + wdtr, clkp, + tcal.clocks.clktr); + printf("** (%d)(%d) " + "best RQDC: 0x%04x\n", + wdtr, clkp, + tcal.autocal.rqfd); + printf("** (%d)(%d) " + "best RFDC: 0x%04x\n", + wdtr, clkp, + tcal.autocal.rffd); + printf("** (%d)(%d) " + "best RDCC: 0x%08x\n", + wdtr, clkp, + (u32)tcal.clocks.rdcc); + mfsdram(SDRAM_RTSR, val); + printf("** (%d)(%d) best " + "loop RTSR: 0x%08x\n", + wdtr, clkp, val); + mfsdram(SDRAM_FCSR, val); + printf("** (%d)(%d) best " + "loop FCSR: 0x%08x\n", + wdtr, clkp, val); + } + } + } /* if ((result != 0) && (val >= (ddr_rdss_opt()))) */ + scan_list++; + } /* while ((scan_list->wrdtr != -1) && (scan_list->clktr != -1)) */ + + if (tcal.autocal.flags == 1) { + if (verbose_lvl > 0) { + printf("*** --------------\n"); + printf("*** best_result window size: %d\n", + best_result); + printf("*** best_result WRDTR: 0x%04x\n", + tcal.clocks.wrdtr); + printf("*** best_result CLKTR: 0x%04x\n", + tcal.clocks.clktr); + printf("*** best_result RQFD: 0x%04x\n", + tcal.autocal.rqfd); + printf("*** best_result RFFD: 0x%04x\n", + tcal.autocal.rffd); + printf("*** best_result RDCC: 0x%04x\n", + tcal.clocks.rdcc); + printf("*** --------------\n"); + printf("\n"); + } + + /* + * if got best passing result window, then lock in the + * best CLKTR, WRDTR, RQFD, and RFFD values + */ + mfsdram(SDRAM_WRDTR, val); + mtsdram(SDRAM_WRDTR, (val & + ~(SDRAM_WRDTR_LLWP_MASK | SDRAM_WRDTR_WTR_MASK)) | + ddr_wrdtr(SDRAM_WRDTR_LLWP_1_CYC | + (tcal.clocks.wrdtr << 25))); + + mtsdram(SDRAM_CLKTR, tcal.clocks.clktr << 30); + + relock_memory_DLL(); + + mfsdram(SDRAM_RQDC, rqdc_reg); + rqdc_reg &= ~(SDRAM_RQDC_RQFD_MASK); + mtsdram(SDRAM_RQDC, rqdc_reg | + SDRAM_RQDC_RQFD_ENCODE(tcal.autocal.rqfd)); + + mfsdram(SDRAM_RQDC, rqdc_reg); + debug("*** best_result: read value SDRAM_RQDC 0x%08x\n", + rqdc_reg); + + mfsdram(SDRAM_RFDC, rfdc_reg); + rfdc_reg &= ~(SDRAM_RFDC_RFFD_MASK); + mtsdram(SDRAM_RFDC, rfdc_reg | + SDRAM_RFDC_RFFD_ENCODE(tcal.autocal.rffd)); + + mfsdram(SDRAM_RFDC, rfdc_reg); + debug("*** best_result: read value SDRAM_RFDC 0x%08x\n", + rfdc_reg); + mfsdram(SDRAM_RDCC, val); + debug("*** SDRAM_RDCC 0x%08x\n", val); + } else { + /* + * no valid windows were found + */ + printf("DQS memory calibration window can not be determined, " + "terminating u-boot.\n"); + ppc4xx_ibm_ddr2_register_dump(); + spd_ddr_init_hang(); + } + + blank_string(strlen(str)); + + return 0; +} +#else /* defined(CONFIG_NAND_U_BOOT) || defined(CONFIG_NAND_SPL) */ +u32 DQS_autocalibration(void) +{ + return 0; +} +#endif /* !defined(CONFIG_NAND_U_BOOT) && !defined(CONFIG_NAND_SPL) */ +#endif /* defined(CONFIG_PPC4xx_DDR_AUTOCALIBRATION) */ diff --git a/arch/powerpc/cpu/ppc4xx/4xx_pci.c b/arch/powerpc/cpu/ppc4xx/4xx_pci.c new file mode 100644 index 0000000..eed4534 --- /dev/null +++ b/arch/powerpc/cpu/ppc4xx/4xx_pci.c @@ -0,0 +1,873 @@ +/*-----------------------------------------------------------------------------+ + * This source code is dual-licensed. You may use it under the terms of + * the GNU General Public license version 2, or under the license below. + * + * This source code has been made available to you by IBM on an AS-IS + * basis. Anyone receiving this source is licensed under IBM + * copyrights to use it in any way he or she deems fit, including + * copying it, modifying it, compiling it, and redistributing it either + * with or without modifications. No license under IBM patents or + * patent applications is to be implied by the copyright license. + * + * Any user of this software should understand that IBM cannot provide + * technical support for this software and will not be responsible for + * any consequences resulting from the use of this software. + * + * Any person who transfers this source code or any derivative work + * must include the IBM copyright notice, this paragraph, and the + * preceding two paragraphs in the transferred software. + * + * COPYRIGHT I B M CORPORATION 1995 + * LICENSED MATERIAL - PROGRAM PROPERTY OF I B M + *-----------------------------------------------------------------------------*/ +/*----------------------------------------------------------------------------+ + * + * File Name: 405gp_pci.c + * + * Function: Initialization code for the 405GP PCI Configuration regs. + * + * Author: Mark Game + * + * Change Activity- + * + * Date Description of Change BY + * --------- --------------------- --- + * 09-Sep-98 Created MCG + * 02-Nov-98 Removed External arbiter selected message JWB + * 27-Nov-98 Zero out PTMBAR2 and disable in PTM2MS JWB + * 04-Jan-99 Zero out other unused PMM and PTM regs. Change bus scan MCG + * from (0 to n) to (1 to n). + * 17-May-99 Port to Walnut JWB + * 17-Jun-99 Updated for VGA support JWB + * 21-Jun-99 Updated to allow SRAM region to be a target from PCI bus JWB + * 19-Jul-99 Updated for 405GP pass 1 errata #26 (Low PCI subsequent MCG + * target latency timer values are not supported). + * Should be fixed in pass 2. + * 09-Sep-99 Removed use of PTM2 since the SRAM region no longer needs JWB + * to be a PCI target. Zero out PTMBAR2 and disable in PTM2MS. + * 10-Dec-99 Updated PCI_Write_CFG_Reg for pass2 errata #6 JWB + * 11-Jan-00 Ensure PMMxMAs disabled before setting PMMxLAs. This is not + * really required after a reset since PMMxMAs are already + * disabled but is a good practice nonetheless. JWB + * 12-Jun-01 stefan.roese@esd-electronics.com + * - PCI host/adapter handling reworked + * 09-Jul-01 stefan.roese@esd-electronics.com + * - PCI host now configures from device 0 (not 1) to max_dev, + * (host configures itself) + * - On CPCI-405 pci base address and size is generated from + * SDRAM and FLASH size (CFG regs not used anymore) + * - Some minor changes for CPCI-405-A (adapter version) + * 14-Sep-01 stefan.roese@esd-electronics.com + * - CONFIG_PCI_SCAN_SHOW added to print pci devices upon startup + * 28-Sep-01 stefan.roese@esd-electronics.com + * - Changed pci master configuration for linux compatibility + * (no need for bios_fixup() anymore) + * 26-Feb-02 stefan.roese@esd-electronics.com + * - Bug fixed in pci configuration (Andrew May) + * - Removed pci class code init for CPCI405 board + * 15-May-02 stefan.roese@esd-electronics.com + * - New vga device handling + * 29-May-02 stefan.roese@esd-electronics.com + * - PCI class code init added (if defined) + *----------------------------------------------------------------------------*/ + +#include <common.h> +#include <command.h> +#include <asm/4xx_pci.h> +#include <asm/processor.h> +#include <asm/io.h> +#include <pci.h> + +#ifdef CONFIG_PCI + +DECLARE_GLOBAL_DATA_PTR; + +#if defined(CONFIG_405GP) || defined(CONFIG_405EP) + +#if defined(CONFIG_PMC405) +ushort pmc405_pci_subsys_deviceid(void); +#endif + +/*#define DEBUG*/ + +/* + * Board-specific pci initialization + * Platform code can reimplement pci_pre_init() if needed + */ +int __pci_pre_init(struct pci_controller *hose) +{ +#if defined(CONFIG_405EP) + /* + * Enable the internal PCI arbiter by default. + * + * On 405EP CPUs the internal arbiter can be controlled + * by the I2C strapping EEPROM. If you want to do so + * or if you want to disable the arbiter pci_pre_init() + * must be reimplemented without enabling the arbiter. + * The arbiter is enabled in this place because of + * compatibility reasons. + */ + mtdcr(CPC0_PCI, mfdcr(CPC0_PCI) | CPC0_PCI_ARBIT_EN); +#endif /* CONFIG_405EP */ + + return 1; +} +int pci_pre_init(struct pci_controller *hose) + __attribute__((weak, alias("__pci_pre_init"))); + +int __is_pci_host(struct pci_controller *hose) +{ +#if defined(CONFIG_405GP) + if (mfdcr(CPC0_PSR) & PSR_PCI_ARBIT_EN) + return 1; +#elif defined (CONFIG_405EP) + if (mfdcr(CPC0_PCI) & CPC0_PCI_ARBIT_EN) + return 1; +#endif + return 0; +} +int is_pci_host(struct pci_controller *hose) __attribute__((weak, alias("__is_pci_host"))); + +/*-----------------------------------------------------------------------------+ + * pci_init. Initializes the 405GP PCI Configuration regs. + *-----------------------------------------------------------------------------*/ +void pci_405gp_init(struct pci_controller *hose) +{ + int i, reg_num = 0; + bd_t *bd = gd->bd; + + unsigned short temp_short; + unsigned long ptmpcila[2] = {CONFIG_SYS_PCI_PTM1PCI, CONFIG_SYS_PCI_PTM2PCI}; +#if defined(CONFIG_PCI_4xx_PTM_OVERWRITE) + char *ptmla_str, *ptmms_str; +#endif + unsigned long ptmla[2] = {CONFIG_SYS_PCI_PTM1LA, CONFIG_SYS_PCI_PTM2LA}; + unsigned long ptmms[2] = {CONFIG_SYS_PCI_PTM1MS, CONFIG_SYS_PCI_PTM2MS}; +#if defined(CONFIG_PIP405) || defined (CONFIG_MIP405) + unsigned long pmmla[3] = {0x80000000, 0xA0000000, 0}; + unsigned long pmmma[3] = {0xE0000001, 0xE0000001, 0}; + unsigned long pmmpcila[3] = {0x80000000, 0x00000000, 0}; + unsigned long pmmpciha[3] = {0x00000000, 0x00000000, 0}; +#else + unsigned long pmmla[3] = {0x80000000, 0,0}; + unsigned long pmmma[3] = {0xC0000001, 0,0}; + unsigned long pmmpcila[3] = {0x80000000, 0,0}; + unsigned long pmmpciha[3] = {0x00000000, 0,0}; +#endif +#ifdef CONFIG_PCI_PNP +#if (CONFIG_PCI_HOST == PCI_HOST_AUTO) + char *s; +#endif +#endif + +#if defined(CONFIG_PCI_4xx_PTM_OVERWRITE) + ptmla_str = getenv("ptm1la"); + ptmms_str = getenv("ptm1ms"); + if(NULL != ptmla_str && NULL != ptmms_str ) { + ptmla[0] = simple_strtoul (ptmla_str, NULL, 16); + ptmms[0] = simple_strtoul (ptmms_str, NULL, 16); + } + + ptmla_str = getenv("ptm2la"); + ptmms_str = getenv("ptm2ms"); + if(NULL != ptmla_str && NULL != ptmms_str ) { + ptmla[1] = simple_strtoul (ptmla_str, NULL, 16); + ptmms[1] = simple_strtoul (ptmms_str, NULL, 16); + } +#endif + + /* + * Register the hose + */ + hose->first_busno = 0; + hose->last_busno = 0xff; + + /* ISA/PCI I/O space */ + pci_set_region(hose->regions + reg_num++, + MIN_PCI_PCI_IOADDR, + MIN_PLB_PCI_IOADDR, + 0x10000, + PCI_REGION_IO); + + /* PCI I/O space */ + pci_set_region(hose->regions + reg_num++, + 0x00800000, + 0xe8800000, + 0x03800000, + PCI_REGION_IO); + + reg_num = 2; + + /* Memory spaces */ + for (i=0; i<2; i++) + if (ptmms[i] & 1) + { + if (!i) hose->pci_fb = hose->regions + reg_num; + + pci_set_region(hose->regions + reg_num++, + ptmpcila[i], ptmla[i], + ~(ptmms[i] & 0xfffff000) + 1, + PCI_REGION_MEM | + PCI_REGION_SYS_MEMORY); + } + + /* PCI memory spaces */ + for (i=0; i<3; i++) + if (pmmma[i] & 1) + { + pci_set_region(hose->regions + reg_num++, + pmmpcila[i], pmmla[i], + ~(pmmma[i] & 0xfffff000) + 1, + PCI_REGION_MEM); + } + + hose->region_count = reg_num; + + pci_setup_indirect(hose, + PCICFGADR, + PCICFGDATA); + + if (hose->pci_fb) + pciauto_region_init(hose->pci_fb); + + /* Let board change/modify hose & do initial checks */ + if (pci_pre_init(hose) == 0) { + printf("PCI: Board-specific initialization failed.\n"); + printf("PCI: Configuration aborted.\n"); + return; + } + + pci_register_hose(hose); + + /*--------------------------------------------------------------------------+ + * 405GP PCI Master configuration. + * Map one 512 MB range of PLB/processor addresses to PCI memory space. + * PLB address 0x80000000-0xBFFFFFFF ==> PCI address 0x80000000-0xBFFFFFFF + * Use byte reversed out routines to handle endianess. + *--------------------------------------------------------------------------*/ + out32r(PMM0MA, (pmmma[0]&~0x1)); /* disable, configure PMMxLA, PMMxPCILA first */ + out32r(PMM0LA, pmmla[0]); + out32r(PMM0PCILA, pmmpcila[0]); + out32r(PMM0PCIHA, pmmpciha[0]); + out32r(PMM0MA, pmmma[0]); + + /*--------------------------------------------------------------------------+ + * PMM1 is not used. Initialize them to zero. + *--------------------------------------------------------------------------*/ + out32r(PMM1MA, (pmmma[1]&~0x1)); + out32r(PMM1LA, pmmla[1]); + out32r(PMM1PCILA, pmmpcila[1]); + out32r(PMM1PCIHA, pmmpciha[1]); + out32r(PMM1MA, pmmma[1]); + + /*--------------------------------------------------------------------------+ + * PMM2 is not used. Initialize them to zero. + *--------------------------------------------------------------------------*/ + out32r(PMM2MA, (pmmma[2]&~0x1)); + out32r(PMM2LA, pmmla[2]); + out32r(PMM2PCILA, pmmpcila[2]); + out32r(PMM2PCIHA, pmmpciha[2]); + out32r(PMM2MA, pmmma[2]); + + /*--------------------------------------------------------------------------+ + * 405GP PCI Target configuration. (PTM1) + * Note: PTM1MS is hardwire enabled but we set the enable bit anyway. + *--------------------------------------------------------------------------*/ + out32r(PTM1LA, ptmla[0]); /* insert address */ + out32r(PTM1MS, ptmms[0]); /* insert size, enable bit is 1 */ + pci_write_config_dword(PCIDEVID_405GP, PCI_BASE_ADDRESS_1, ptmpcila[0]); + + /*--------------------------------------------------------------------------+ + * 405GP PCI Target configuration. (PTM2) + *--------------------------------------------------------------------------*/ + out32r(PTM2LA, ptmla[1]); /* insert address */ + pci_write_config_dword(PCIDEVID_405GP, PCI_BASE_ADDRESS_2, ptmpcila[1]); + + if (ptmms[1] == 0) + { + out32r(PTM2MS, 0x00000001); /* set enable bit */ + pci_write_config_dword(PCIDEVID_405GP, PCI_BASE_ADDRESS_2, 0x00000000); + out32r(PTM2MS, 0x00000000); /* disable */ + } + else + { + out32r(PTM2MS, ptmms[1]); /* insert size, enable bit is 1 */ + } + + /* + * Insert Subsystem Vendor and Device ID + */ + pci_write_config_word(PCIDEVID_405GP, PCI_SUBSYSTEM_VENDOR_ID, CONFIG_SYS_PCI_SUBSYS_VENDORID); +#ifdef CONFIG_CPCI405 + if (is_pci_host(hose)) + pci_write_config_word(PCIDEVID_405GP, PCI_SUBSYSTEM_ID, CONFIG_SYS_PCI_SUBSYS_DEVICEID); + else + pci_write_config_word(PCIDEVID_405GP, PCI_SUBSYSTEM_ID, CONFIG_SYS_PCI_SUBSYS_DEVICEID2); +#else + pci_write_config_word(PCIDEVID_405GP, PCI_SUBSYSTEM_ID, CONFIG_SYS_PCI_SUBSYS_DEVICEID); +#endif + + /* + * Insert Class-code + */ +#ifdef CONFIG_SYS_PCI_CLASSCODE + pci_write_config_word(PCIDEVID_405GP, PCI_CLASS_SUB_CODE, CONFIG_SYS_PCI_CLASSCODE); +#endif /* CONFIG_SYS_PCI_CLASSCODE */ + + /*--------------------------------------------------------------------------+ + * If PCI speed = 66MHz, set 66MHz capable bit. + *--------------------------------------------------------------------------*/ + if (bd->bi_pci_busfreq >= 66000000) { + pci_read_config_word(PCIDEVID_405GP, PCI_STATUS, &temp_short); + pci_write_config_word(PCIDEVID_405GP,PCI_STATUS,(temp_short|PCI_STATUS_66MHZ)); + } + +#if (CONFIG_PCI_HOST != PCI_HOST_ADAPTER) +#if (CONFIG_PCI_HOST == PCI_HOST_AUTO) + if (is_pci_host(hose) || + (((s = getenv("pciscan")) != NULL) && (strcmp(s, "yes") == 0))) +#endif + { + /*--------------------------------------------------------------------------+ + * Write the 405GP PCI Configuration regs. + * Enable 405GP to be a master on the PCI bus (PMM). + * Enable 405GP to act as a PCI memory target (PTM). + *--------------------------------------------------------------------------*/ + pci_read_config_word(PCIDEVID_405GP, PCI_COMMAND, &temp_short); + pci_write_config_word(PCIDEVID_405GP, PCI_COMMAND, temp_short | + PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY); + } +#endif + +#if defined(CONFIG_405EP) + /* + * on ppc405ep vendor/device id is not set + * The user manual says 0x1014 (IBM) / 0x0156 (405GP!) + * are the correct values. + */ + pci_write_config_word(PCIDEVID_405GP, PCI_VENDOR_ID, PCI_VENDOR_ID_IBM); + pci_write_config_word(PCIDEVID_405GP, + PCI_DEVICE_ID, PCI_DEVICE_ID_IBM_405GP); +#endif + + /* + * Set HCE bit (Host Configuration Enabled) + */ + pci_read_config_word(PCIDEVID_405GP, PCIBRDGOPT2, &temp_short); + pci_write_config_word(PCIDEVID_405GP, PCIBRDGOPT2, (temp_short | 0x0001)); + +#ifdef CONFIG_PCI_PNP + /*--------------------------------------------------------------------------+ + * Scan the PCI bus and configure devices found. + *--------------------------------------------------------------------------*/ +#if (CONFIG_PCI_HOST == PCI_HOST_AUTO) + if (is_pci_host(hose) || + (((s = getenv("pciscan")) != NULL) && (strcmp(s, "yes") == 0))) +#endif + { +#ifdef CONFIG_PCI_SCAN_SHOW + printf("PCI: Bus Dev VenId DevId Class Int\n"); +#endif + hose->last_busno = pci_hose_scan(hose); + } +#endif /* CONFIG_PCI_PNP */ + +} + +/* + * drivers/pci/pci.c skips every host bridge but the 405GP since it could + * be set as an Adapter. + * + * I (Andrew May) don't know what we should do here, but I don't want + * the auto setup of a PCI device disabling what is done pci_405gp_init + * as has happened before. + */ +void pci_405gp_setup_bridge(struct pci_controller *hose, pci_dev_t dev, + struct pci_config_table *entry) +{ +#ifdef DEBUG + printf("405gp_setup_bridge\n"); +#endif +} + +/* + * + */ + +void pci_405gp_fixup_irq(struct pci_controller *hose, pci_dev_t dev) +{ + unsigned char int_line = 0xff; + + /* + * Write pci interrupt line register (cpci405 specific) + */ + switch (PCI_DEV(dev) & 0x03) + { + case 0: + int_line = 27 + 2; + break; + case 1: + int_line = 27 + 3; + break; + case 2: + int_line = 27 + 0; + break; + case 3: + int_line = 27 + 1; + break; + } + + pci_hose_write_config_byte(hose, dev, PCI_INTERRUPT_LINE, int_line); +} + +void pci_405gp_setup_vga(struct pci_controller *hose, pci_dev_t dev, + struct pci_config_table *entry) +{ + unsigned int cmdstat = 0; + + pciauto_setup_device(hose, dev, 6, hose->pci_mem, hose->pci_prefetch, hose->pci_io); + + /* always enable io space on vga boards */ + pci_hose_read_config_dword(hose, dev, PCI_COMMAND, &cmdstat); + cmdstat |= PCI_COMMAND_IO; + pci_hose_write_config_dword(hose, dev, PCI_COMMAND, cmdstat); +} + +#if !(defined(CONFIG_PIP405) || defined (CONFIG_MIP405)) && !(defined (CONFIG_SC3)) + +/* + *As is these functs get called out of flash Not a horrible + *thing, but something to keep in mind. (no statics?) + */ +static struct pci_config_table pci_405gp_config_table[] = { +/*if VendID is 0 it terminates the table search (ie Walnut)*/ +#ifdef CONFIG_SYS_PCI_SUBSYS_VENDORID + {CONFIG_SYS_PCI_SUBSYS_VENDORID, PCI_ANY_ID, PCI_CLASS_BRIDGE_HOST, + PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, pci_405gp_setup_bridge}, +#endif + {PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_DISPLAY_VGA, + PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, pci_405gp_setup_vga}, + + {PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_NOT_DEFINED_VGA, + PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, pci_405gp_setup_vga}, + + { } +}; + +static struct pci_controller hose = { + fixup_irq: pci_405gp_fixup_irq, + config_table: pci_405gp_config_table, +}; + +void pci_init_board(void) +{ + /*we want the ptrs to RAM not flash (ie don't use init list)*/ + hose.fixup_irq = pci_405gp_fixup_irq; + hose.config_table = pci_405gp_config_table; + pci_405gp_init(&hose); +} + +#endif + +#endif /* CONFIG_405GP */ + +/*-----------------------------------------------------------------------------+ + * CONFIG_440 + *-----------------------------------------------------------------------------*/ +#if defined(CONFIG_440) + +static struct pci_controller ppc440_hose = {0}; + +/* + * This routine is called to determine if a pci scan should be + * performed. With various hardware environments (especially cPCI and + * PPMC) it's insufficient to depend on the state of the arbiter enable + * bit in the strap register, or generic host/adapter assumptions. + * + * Rather than hard-code a bad assumption in the general 440 code, the + * 440 pci code requires the board to decide at runtime. + * + * Return 0 for adapter mode, non-zero for host (monarch) mode. + * + * Weak default implementation: "Normal" boards implement the PCI + * host functionality. This can be overridden for PCI adapter boards. + */ +int __is_pci_host(struct pci_controller *hose) +{ + return 1; +} +int is_pci_host(struct pci_controller *hose) + __attribute__((weak, alias("__is_pci_host"))); + +#if defined(CONFIG_440EP) || defined(CONFIG_440EPX) || \ + defined(CONFIG_440GR) || defined(CONFIG_440GRX) + +#if defined(CONFIG_SYS_PCI_TARGET_INIT) +/* + * pci_target_init + * + * The bootstrap configuration provides default settings for the pci + * inbound map (PIM). But the bootstrap config choices are limited and + * may not be sufficient for a given board. + */ +void __pci_target_init(struct pci_controller *hose) +{ + /* + * Set up Direct MMIO registers + */ + + /* + * PowerPC440 EP PCI Master configuration. + * Map one 1Gig range of PLB/processor addresses to PCI memory space. + * PLB address 0xA0000000-0xDFFFFFFF ==> PCI address 0xA0000000-0xDFFFFFFF + * Use byte reversed out routines to handle endianess. + * Make this region non-prefetchable. + */ + /* PMM0 Mask/Attribute - disabled b4 setting */ + out_le32((void *)PCIL0_PMM0MA, 0x00000000); + /* PMM0 Local Address */ + out_le32((void *)PCIL0_PMM0LA, CONFIG_SYS_PCI_MEMBASE); + /* PMM0 PCI Low Address */ + out_le32((void *)PCIL0_PMM0PCILA, CONFIG_SYS_PCI_MEMBASE); + /* PMM0 PCI High Address */ + out_le32((void *)PCIL0_PMM0PCIHA, 0x00000000); + /* 512M + No prefetching, and enable region */ + out_le32((void *)PCIL0_PMM0MA, 0xE0000001); + + /* PMM1 Mask/Attribute - disabled b4 setting */ + out_le32((void *)PCIL0_PMM1MA, 0x00000000); + /* PMM1 Local Address */ + out_le32((void *)PCIL0_PMM1LA, CONFIG_SYS_PCI_MEMBASE2); + /* PMM1 PCI Low Address */ + out_le32((void *)PCIL0_PMM1PCILA, CONFIG_SYS_PCI_MEMBASE2); + /* PMM1 PCI High Address */ + out_le32((void *)PCIL0_PMM1PCIHA, 0x00000000); + /* 512M + No prefetching, and enable region */ + out_le32((void *)PCIL0_PMM1MA, 0xE0000001); + + out_le32((void *)PCIL0_PTM1MS, 0x00000001); /* Memory Size/Attribute */ + out_le32((void *)PCIL0_PTM1LA, 0); /* Local Addr. Reg */ + out_le32((void *)PCIL0_PTM2MS, 0); /* Memory Size/Attribute */ + out_le32((void *)PCIL0_PTM2LA, 0); /* Local Addr. Reg */ + + /* + * Set up Configuration registers + */ + + /* Program the board's subsystem id/vendor id */ + pci_write_config_word(0, PCI_SUBSYSTEM_VENDOR_ID, + CONFIG_SYS_PCI_SUBSYS_VENDORID); + pci_write_config_word(0, PCI_SUBSYSTEM_ID, CONFIG_SYS_PCI_SUBSYS_ID); + + /* Configure command register as bus master */ + pci_write_config_word(0, PCI_COMMAND, PCI_COMMAND_MASTER); + + /* 240nS PCI clock */ + pci_write_config_word(0, PCI_LATENCY_TIMER, 1); + + /* No error reporting */ + pci_write_config_word(0, PCI_ERREN, 0); + + pci_write_config_dword(0, PCI_BRDGOPT2, 0x00000101); +} +#endif /* CONFIG_SYS_PCI_TARGET_INIT */ + +/* + * pci_pre_init + * + * This routine is called just prior to registering the hose and gives + * the board the opportunity to check things. Returning a value of zero + * indicates that things are bad & PCI initialization should be aborted. + * + * Different boards may wish to customize the pci controller structure + * (add regions, override default access routines, etc) or perform + * certain pre-initialization actions. + * + */ +int __pci_pre_init(struct pci_controller *hose) +{ + u32 reg; + + /* + * Set priority for all PLB3 devices to 0. + * Set PLB3 arbiter to fair mode. + */ + mfsdr(SD0_AMP1, reg); + mtsdr(SD0_AMP1, (reg & 0x000000FF) | 0x0000FF00); + reg = mfdcr(PLB3_ACR); + mtdcr(PLB3_ACR, reg | 0x80000000); + + /* + * Set priority for all PLB4 devices to 0. + */ + mfsdr(SD0_AMP0, reg); + mtsdr(SD0_AMP0, (reg & 0x000000FF) | 0x0000FF00); + reg = mfdcr(PLB4_ACR) | 0xa0000000; + mtdcr(PLB4_ACR, reg); + + /* + * Set Nebula PLB4 arbiter to fair mode. + */ + /* Segment0 */ + reg = (mfdcr(PLB0_ACR) & ~PLB0_ACR_PPM_MASK) | PLB0_ACR_PPM_FAIR; + reg = (reg & ~PLB0_ACR_HBU_MASK) | PLB0_ACR_HBU_ENABLED; + reg = (reg & ~PLB0_ACR_RDP_MASK) | PLB0_ACR_RDP_4DEEP; + reg = (reg & ~PLB0_ACR_WRP_MASK) | PLB0_ACR_WRP_2DEEP; + mtdcr(PLB0_ACR, reg); + + /* Segment1 */ + reg = (mfdcr(PLB1_ACR) & ~PLB1_ACR_PPM_MASK) | PLB1_ACR_PPM_FAIR; + reg = (reg & ~PLB1_ACR_HBU_MASK) | PLB1_ACR_HBU_ENABLED; + reg = (reg & ~PLB1_ACR_RDP_MASK) | PLB1_ACR_RDP_4DEEP; + reg = (reg & ~PLB1_ACR_WRP_MASK) | PLB1_ACR_WRP_2DEEP; + mtdcr(PLB1_ACR, reg); + +#if defined(CONFIG_SYS_PCI_BOARD_FIXUP_IRQ) + hose->fixup_irq = board_pci_fixup_irq; +#endif + + return 1; +} + +#else /* defined(CONFIG_440EP) ... */ + +#if defined(CONFIG_SYS_PCI_TARGET_INIT) +void __pci_target_init(struct pci_controller * hose) +{ + /* + * Disable everything + */ + out_le32((void *)PCIL0_PIM0SA, 0); /* disable */ + out_le32((void *)PCIL0_PIM1SA, 0); /* disable */ + out_le32((void *)PCIL0_PIM2SA, 0); /* disable */ + out_le32((void *)PCIL0_EROMBA, 0); /* disable expansion rom */ + + /* + * Map all of SDRAM to PCI address 0x0000_0000. Note that the 440 + * strapping options do not support sizes such as 128/256 MB. + */ + out_le32((void *)PCIL0_PIM0LAL, CONFIG_SYS_SDRAM_BASE); + out_le32((void *)PCIL0_PIM0LAH, 0); + out_le32((void *)PCIL0_PIM0SA, ~(gd->ram_size - 1) | 1); + out_le32((void *)PCIL0_BAR0, 0); + + /* + * Program the board's subsystem id/vendor id + */ + out_le16((void *)PCIL0_SBSYSVID, CONFIG_SYS_PCI_SUBSYS_VENDORID); + out_le16((void *)PCIL0_SBSYSID, CONFIG_SYS_PCI_SUBSYS_DEVICEID); + + out_le16((void *)PCIL0_CMD, in_le16((void *)PCIL0_CMD) | + PCI_COMMAND_MEMORY); +} +#endif /* CONFIG_SYS_PCI_TARGET_INIT */ + +int __pci_pre_init(struct pci_controller *hose) +{ + /* + * This board is always configured as the host & requires the + * PCI arbiter to be enabled. + */ + if (!pci_arbiter_enabled()) { + printf("PCI: PCI Arbiter disabled!\n"); + return 0; + } + + return 1; +} + +#endif /* defined(CONFIG_440EP) ... */ + +#if defined(CONFIG_SYS_PCI_TARGET_INIT) +void pci_target_init(struct pci_controller * hose) + __attribute__((weak, alias("__pci_target_init"))); +#endif /* CONFIG_SYS_PCI_TARGET_INIT */ + +int pci_pre_init(struct pci_controller *hose) + __attribute__((weak, alias("__pci_pre_init"))); + +#if defined(CONFIG_SYS_PCI_MASTER_INIT) +void __pci_master_init(struct pci_controller *hose) +{ + u16 reg; + + /* + * Write the PowerPC440 EP PCI Configuration regs. + * Enable PowerPC440 EP to be a master on the PCI bus (PMM). + * Enable PowerPC440 EP to act as a PCI memory target (PTM). + */ + pci_read_config_word(0, PCI_COMMAND, ®); + pci_write_config_word(0, PCI_COMMAND, reg | + PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY); +} +void pci_master_init(struct pci_controller *hose) + __attribute__((weak, alias("__pci_master_init"))); +#endif /* CONFIG_SYS_PCI_MASTER_INIT */ + +int pci_440_init (struct pci_controller *hose) +{ + int reg_num = 0; + +#ifndef CONFIG_DISABLE_PISE_TEST + /*--------------------------------------------------------------------------+ + * The PCI initialization sequence enable bit must be set ... if not abort + * pci setup since updating the bit requires chip reset. + *--------------------------------------------------------------------------*/ +#if defined(CONFIG_440GX) || defined(CONFIG_440SP) || defined(CONFIG_440SPE) + unsigned long strap; + + mfsdr(SDR0_SDSTP1,strap); + if ((strap & SDR0_SDSTP1_PISE_MASK) == 0) { + printf("PCI: SDR0_STRP1[PISE] not set.\n"); + printf("PCI: Configuration aborted.\n"); + return -1; + } +#elif defined(CONFIG_440GP) + unsigned long strap; + + strap = mfdcr(CPC0_STRP1); + if ((strap & CPC0_STRP1_PISE_MASK) == 0) { + printf("PCI: CPC0_STRP1[PISE] not set.\n"); + printf("PCI: Configuration aborted.\n"); + return -1; + } +#endif +#endif /* CONFIG_DISABLE_PISE_TEST */ + + /*--------------------------------------------------------------------------+ + * PCI controller init + *--------------------------------------------------------------------------*/ + hose->first_busno = 0; + hose->last_busno = 0; + + /* PCI I/O space */ + pci_set_region(hose->regions + reg_num++, + 0x00000000, + PCIL0_IOBASE, + 0x10000, + PCI_REGION_IO); + + /* PCI memory space */ + pci_set_region(hose->regions + reg_num++, + CONFIG_SYS_PCI_TARGBASE, + CONFIG_SYS_PCI_MEMBASE, +#ifdef CONFIG_SYS_PCI_MEMSIZE + CONFIG_SYS_PCI_MEMSIZE, +#else + 0x10000000, +#endif + PCI_REGION_MEM ); + +#if defined(CONFIG_PCI_SYS_MEM_BUS) && defined(CONFIG_PCI_SYS_MEM_PHYS) && \ + defined(CONFIG_PCI_SYS_MEM_SIZE) + /* System memory space */ + pci_set_region(hose->regions + reg_num++, + CONFIG_PCI_SYS_MEM_BUS, + CONFIG_PCI_SYS_MEM_PHYS, + CONFIG_PCI_SYS_MEM_SIZE, + PCI_REGION_MEM | PCI_REGION_SYS_MEMORY ); +#endif + + hose->region_count = reg_num; + + pci_setup_indirect(hose, PCIL0_CFGADR, PCIL0_CFGDATA); + + /* Let board change/modify hose & do initial checks */ + if (pci_pre_init(hose) == 0) { + printf("PCI: Board-specific initialization failed.\n"); + printf("PCI: Configuration aborted.\n"); + return -1; + } + + pci_register_hose( hose ); + + /*--------------------------------------------------------------------------+ + * PCI target init + *--------------------------------------------------------------------------*/ +#if defined(CONFIG_SYS_PCI_TARGET_INIT) + pci_target_init(hose); /* Let board setup pci target */ +#else + out16r( PCIL0_SBSYSVID, CONFIG_SYS_PCI_SUBSYS_VENDORID ); + out16r( PCIL0_SBSYSID, CONFIG_SYS_PCI_SUBSYS_ID ); + out16r( PCIL0_CLS, 0x00060000 ); /* Bridge, host bridge */ +#endif + +#if defined(CONFIG_440GX) || defined(CONFIG_440SPE) || \ + defined(CONFIG_460EX) || defined(CONFIG_460GT) + out32r( PCIL0_BRDGOPT1, 0x04000060 ); /* PLB Rq pri highest */ + out32r( PCIL0_BRDGOPT2, in32(PCIL0_BRDGOPT2) | 0x83 ); /* Enable host config, clear Timeout, ensure int src1 */ +#elif defined(PCIL0_BRDGOPT1) + out32r( PCIL0_BRDGOPT1, 0x10000060 ); /* PLB Rq pri highest */ + out32r( PCIL0_BRDGOPT2, in32(PCIL0_BRDGOPT2) | 1 ); /* Enable host config */ +#endif + + /*--------------------------------------------------------------------------+ + * PCI master init: default is one 256MB region for PCI memory: + * 0x3_00000000 - 0x3_0FFFFFFF ==> CONFIG_SYS_PCI_MEMBASE + *--------------------------------------------------------------------------*/ +#if defined(CONFIG_SYS_PCI_MASTER_INIT) + pci_master_init(hose); /* Let board setup pci master */ +#else + out32r( PCIL0_POM0SA, 0 ); /* disable */ + out32r( PCIL0_POM1SA, 0 ); /* disable */ + out32r( PCIL0_POM2SA, 0 ); /* disable */ +#if defined(CONFIG_440SPE) + out32r( PCIL0_POM0LAL, 0x10000000 ); + out32r( PCIL0_POM0LAH, 0x0000000c ); +#elif defined(CONFIG_460EX) || defined(CONFIG_460GT) + out32r( PCIL0_POM0LAL, 0x20000000 ); + out32r( PCIL0_POM0LAH, 0x0000000c ); +#else + out32r( PCIL0_POM0LAL, 0x00000000 ); + out32r( PCIL0_POM0LAH, 0x00000003 ); +#endif + out32r( PCIL0_POM0PCIAL, CONFIG_SYS_PCI_MEMBASE ); + out32r( PCIL0_POM0PCIAH, 0x00000000 ); + out32r( PCIL0_POM0SA, 0xf0000001 ); /* 256MB, enabled */ + out32r( PCIL0_STS, in32r( PCIL0_STS ) & ~0x0000fff8 ); +#endif + + /*--------------------------------------------------------------------------+ + * PCI host configuration -- we don't make any assumptions here ... the + * _board_must_indicate_ what to do -- there's just too many runtime + * scenarios in environments like cPCI, PPMC, etc. to make a determination + * based on hard-coded values or state of arbiter enable. + *--------------------------------------------------------------------------*/ + if (is_pci_host(hose)) { +#ifdef CONFIG_PCI_SCAN_SHOW + printf("PCI: Bus Dev VenId DevId Class Int\n"); +#endif +#if !defined(CONFIG_440EP) && !defined(CONFIG_440GR) && \ + !defined(CONFIG_440EPX) && !defined(CONFIG_440GRX) + out16r( PCIL0_CMD, in16r( PCIL0_CMD ) | PCI_COMMAND_MASTER); +#endif + hose->last_busno = pci_hose_scan(hose); + } + return hose->last_busno; +} + +void pci_init_board(void) +{ + int busno; + + busno = pci_440_init (&ppc440_hose); +#if (defined(CONFIG_440SPE) || \ + defined(CONFIG_460EX) || defined(CONFIG_460GT)) && \ + !defined(CONFIG_PCI_DISABLE_PCIE) + pcie_setup_hoses(busno + 1); +#endif +} + +#endif /* CONFIG_440 */ + +#if defined(CONFIG_405EX) +void pci_init_board(void) +{ +#ifdef CONFIG_PCI_SCAN_SHOW + printf("PCI: Bus Dev VenId DevId Class Int\n"); +#endif + pcie_setup_hoses(0); +} +#endif /* CONFIG_405EX */ + +#endif /* CONFIG_PCI */ diff --git a/arch/powerpc/cpu/ppc4xx/4xx_pcie.c b/arch/powerpc/cpu/ppc4xx/4xx_pcie.c new file mode 100644 index 0000000..10b58b7 --- /dev/null +++ b/arch/powerpc/cpu/ppc4xx/4xx_pcie.c @@ -0,0 +1,1298 @@ +/* + * (C) Copyright 2006 - 2008 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * Copyright (c) 2005 Cisco Systems. All rights reserved. + * Roland Dreier <rolandd@cisco.com> + * + * 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. + * + */ + +/* define DEBUG for debugging output (obviously ;-)) */ +#if 0 +#define DEBUG +#endif + +#include <common.h> +#include <pci.h> +#include <ppc4xx.h> +#include <asm/processor.h> +#include <asm/io.h> +#include <asm/errno.h> + +#if (defined(CONFIG_440SPE) || defined(CONFIG_405EX) || \ + defined(CONFIG_460EX) || defined(CONFIG_460GT)) && \ + defined(CONFIG_PCI) && !defined(CONFIG_PCI_DISABLE_PCIE) + +#include <asm/4xx_pcie.h> + +enum { + PTYPE_ENDPOINT = 0x0, + PTYPE_LEGACY_ENDPOINT = 0x1, + PTYPE_ROOT_PORT = 0x4, + + LNKW_X1 = 0x1, + LNKW_X4 = 0x4, + LNKW_X8 = 0x8 +}; + +static struct pci_controller pcie_hose[CONFIG_SYS_PCIE_NR_PORTS]; + +/* + * Per default, all cards are present, so we need to check if the + * link comes up. + */ +int __board_pcie_card_present(int port) +{ + return 1; +} +int board_pcie_card_present(int port) + __attribute__((weak, alias("__board_pcie_card_present"))); + +/* + * Some boards have runtime detection of the first and last PCIe + * slot used, so let's provide weak default functions for the + * common version. + */ +int __board_pcie_first(void) +{ + return 0; +} +int board_pcie_first(void) + __attribute__((weak, alias("__board_pcie_first"))); + +int __board_pcie_last(void) +{ + return CONFIG_SYS_PCIE_NR_PORTS - 1; +} +int board_pcie_last(void) + __attribute__((weak, alias("__board_pcie_last"))); + +void __board_pcie_setup_port(int port, int rootpoint) +{ + /* noting in this weak default implementation */ +} +void board_pcie_setup_port(int port, int rootpoint) + __attribute__((weak, alias("__board_pcie_setup_port"))); + +void pcie_setup_hoses(int busno) +{ + struct pci_controller *hose; + int i, bus; + int ret = 0; + char *env; + unsigned int delay; + int first = board_pcie_first(); + int last = board_pcie_last(); + + /* + * Assume we're called after the PCI(X) hose(s) are initialized, + * which takes bus ID 0... and therefore start numbering PCIe's + * from the next number. + */ + bus = busno; + + for (i = first; i <= last; i++) { + /* + * Some boards (e.g. Katmai) can detects via hardware + * if a PCIe card is plugged, so let's check this. + */ + if (!board_pcie_card_present(i)) + continue; + + if (is_end_point(i)) { + board_pcie_setup_port(i, 0); + ret = ppc4xx_init_pcie_endport(i); + } else { + board_pcie_setup_port(i, 1); + ret = ppc4xx_init_pcie_rootport(i); + } + if (ret == -ENODEV) + continue; + if (ret) { + printf("PCIE%d: initialization as %s failed\n", i, + is_end_point(i) ? "endpoint" : "root-complex"); + continue; + } + + hose = &pcie_hose[i]; + hose->first_busno = bus; + hose->last_busno = bus; + hose->current_busno = bus; + + /* setup mem resource */ + pci_set_region(hose->regions + 0, + CONFIG_SYS_PCIE_MEMBASE + i * CONFIG_SYS_PCIE_MEMSIZE, + CONFIG_SYS_PCIE_MEMBASE + i * CONFIG_SYS_PCIE_MEMSIZE, + CONFIG_SYS_PCIE_MEMSIZE, + PCI_REGION_MEM); + hose->region_count = 1; + pci_register_hose(hose); + + if (is_end_point(i)) { + ppc4xx_setup_pcie_endpoint(hose, i); + /* + * Reson for no scanning is endpoint can not generate + * upstream configuration accesses. + */ + } else { + ppc4xx_setup_pcie_rootpoint(hose, i); + env = getenv ("pciscandelay"); + if (env != NULL) { + delay = simple_strtoul(env, NULL, 10); + if (delay > 5) + printf("Warning, expect noticable delay before " + "PCIe scan due to 'pciscandelay' value!\n"); + mdelay(delay * 1000); + } + + /* + * Config access can only go down stream + */ + hose->last_busno = pci_hose_scan(hose); + bus = hose->last_busno + 1; + } + } +} + +static int validate_endpoint(struct pci_controller *hose) +{ + if (hose->cfg_data == (u8 *)CONFIG_SYS_PCIE0_CFGBASE) + return (is_end_point(0)); + else if (hose->cfg_data == (u8 *)CONFIG_SYS_PCIE1_CFGBASE) + return (is_end_point(1)); +#if CONFIG_SYS_PCIE_NR_PORTS > 2 + else if (hose->cfg_data == (u8 *)CONFIG_SYS_PCIE2_CFGBASE) + return (is_end_point(2)); +#endif + + return 0; +} + +static u8* pcie_get_base(struct pci_controller *hose, unsigned int devfn) +{ + u8 *base = (u8*)hose->cfg_data; + + /* use local configuration space for the first bus */ + if (PCI_BUS(devfn) == 0) { + if (hose->cfg_data == (u8*)CONFIG_SYS_PCIE0_CFGBASE) + base = (u8*)CONFIG_SYS_PCIE0_XCFGBASE; + if (hose->cfg_data == (u8*)CONFIG_SYS_PCIE1_CFGBASE) + base = (u8*)CONFIG_SYS_PCIE1_XCFGBASE; +#if CONFIG_SYS_PCIE_NR_PORTS > 2 + if (hose->cfg_data == (u8*)CONFIG_SYS_PCIE2_CFGBASE) + base = (u8*)CONFIG_SYS_PCIE2_XCFGBASE; +#endif + } + + return base; +} + +static void pcie_dmer_disable(void) +{ + mtdcr (DCRN_PEGPL_CFG(DCRN_PCIE0_BASE), + mfdcr (DCRN_PEGPL_CFG(DCRN_PCIE0_BASE)) | GPL_DMER_MASK_DISA); + mtdcr (DCRN_PEGPL_CFG(DCRN_PCIE1_BASE), + mfdcr (DCRN_PEGPL_CFG(DCRN_PCIE1_BASE)) | GPL_DMER_MASK_DISA); +#if CONFIG_SYS_PCIE_NR_PORTS > 2 + mtdcr (DCRN_PEGPL_CFG(DCRN_PCIE2_BASE), + mfdcr (DCRN_PEGPL_CFG(DCRN_PCIE2_BASE)) | GPL_DMER_MASK_DISA); +#endif +} + +static void pcie_dmer_enable(void) +{ + mtdcr (DCRN_PEGPL_CFG (DCRN_PCIE0_BASE), + mfdcr (DCRN_PEGPL_CFG(DCRN_PCIE0_BASE)) & ~GPL_DMER_MASK_DISA); + mtdcr (DCRN_PEGPL_CFG (DCRN_PCIE1_BASE), + mfdcr (DCRN_PEGPL_CFG(DCRN_PCIE1_BASE)) & ~GPL_DMER_MASK_DISA); +#if CONFIG_SYS_PCIE_NR_PORTS > 2 + mtdcr (DCRN_PEGPL_CFG (DCRN_PCIE2_BASE), + mfdcr (DCRN_PEGPL_CFG(DCRN_PCIE2_BASE)) & ~GPL_DMER_MASK_DISA); +#endif +} + +static int pcie_read_config(struct pci_controller *hose, unsigned int devfn, + int offset, int len, u32 *val) { + + u8 *address; + *val = 0; + + if (validate_endpoint(hose)) + return 0; /* No upstream config access */ + + /* + * Bus numbers are relative to hose->first_busno + */ + devfn -= PCI_BDF(hose->first_busno, 0, 0); + + /* + * NOTICE: configuration space ranges are currenlty mapped only for + * the first 16 buses, so such limit must be imposed. In case more + * buses are required the TLB settings in board/amcc/<board>/init.S + * need to be altered accordingly (one bus takes 1 MB of memory space). + */ + if (PCI_BUS(devfn) >= 16) + return 0; + + /* + * Only single device/single function is supported for the primary and + * secondary buses of the 440SPe host bridge. + */ + if ((!((PCI_FUNC(devfn) == 0) && (PCI_DEV(devfn) == 0))) && + ((PCI_BUS(devfn) == 0) || (PCI_BUS(devfn) == 1))) + return 0; + + address = pcie_get_base(hose, devfn); + offset += devfn << 4; + + /* + * Reading from configuration space of non-existing device can + * generate transaction errors. For the read duration we suppress + * assertion of machine check exceptions to avoid those. + */ + pcie_dmer_disable (); + + debug("%s: cfg_data=%08x offset=%08x\n", __func__, hose->cfg_data, offset); + switch (len) { + case 1: + *val = in_8(hose->cfg_data + offset); + break; + case 2: + *val = in_le16((u16 *)(hose->cfg_data + offset)); + break; + default: + *val = in_le32((u32*)(hose->cfg_data + offset)); + break; + } + + pcie_dmer_enable (); + + return 0; +} + +static int pcie_write_config(struct pci_controller *hose, unsigned int devfn, + int offset, int len, u32 val) { + + u8 *address; + + if (validate_endpoint(hose)) + return 0; /* No upstream config access */ + + /* + * Bus numbers are relative to hose->first_busno + */ + devfn -= PCI_BDF(hose->first_busno, 0, 0); + + /* + * Same constraints as in pcie_read_config(). + */ + if (PCI_BUS(devfn) >= 16) + return 0; + + if ((!((PCI_FUNC(devfn) == 0) && (PCI_DEV(devfn) == 0))) && + ((PCI_BUS(devfn) == 0) || (PCI_BUS(devfn) == 1))) + return 0; + + address = pcie_get_base(hose, devfn); + offset += devfn << 4; + + /* + * Suppress MCK exceptions, similar to pcie_read_config() + */ + pcie_dmer_disable (); + + switch (len) { + case 1: + out_8(hose->cfg_data + offset, val); + break; + case 2: + out_le16((u16 *)(hose->cfg_data + offset), val); + break; + default: + out_le32((u32 *)(hose->cfg_data + offset), val); + break; + } + + pcie_dmer_enable (); + + return 0; +} + +int pcie_read_config_byte(struct pci_controller *hose,pci_dev_t dev,int offset,u8 *val) +{ + u32 v; + int rv; + + rv = pcie_read_config(hose, dev, offset, 1, &v); + *val = (u8)v; + return rv; +} + +int pcie_read_config_word(struct pci_controller *hose,pci_dev_t dev,int offset,u16 *val) +{ + u32 v; + int rv; + + rv = pcie_read_config(hose, dev, offset, 2, &v); + *val = (u16)v; + return rv; +} + +int pcie_read_config_dword(struct pci_controller *hose,pci_dev_t dev,int offset,u32 *val) +{ + u32 v; + int rv; + + rv = pcie_read_config(hose, dev, offset, 3, &v); + *val = (u32)v; + return rv; +} + +int pcie_write_config_byte(struct pci_controller *hose,pci_dev_t dev,int offset,u8 val) +{ + return pcie_write_config(hose,(u32)dev,offset,1,val); +} + +int pcie_write_config_word(struct pci_controller *hose,pci_dev_t dev,int offset,u16 val) +{ + return pcie_write_config(hose,(u32)dev,offset,2,(u32 )val); +} + +int pcie_write_config_dword(struct pci_controller *hose,pci_dev_t dev,int offset,u32 val) +{ + return pcie_write_config(hose,(u32)dev,offset,3,(u32 )val); +} + +#if defined(CONFIG_440SPE) +static void ppc4xx_setup_utl(u32 port) { + + volatile void *utl_base = NULL; + + /* + * Map UTL registers + */ + switch (port) { + case 0: + mtdcr(DCRN_PEGPL_REGBAH(PCIE0), 0x0000000c); + mtdcr(DCRN_PEGPL_REGBAL(PCIE0), 0x20000000); + mtdcr(DCRN_PEGPL_REGMSK(PCIE0), 0x00007001); + mtdcr(DCRN_PEGPL_SPECIAL(PCIE0), 0x68782800); + break; + + case 1: + mtdcr(DCRN_PEGPL_REGBAH(PCIE1), 0x0000000c); + mtdcr(DCRN_PEGPL_REGBAL(PCIE1), 0x20001000); + mtdcr(DCRN_PEGPL_REGMSK(PCIE1), 0x00007001); + mtdcr(DCRN_PEGPL_SPECIAL(PCIE1), 0x68782800); + break; + + case 2: + mtdcr(DCRN_PEGPL_REGBAH(PCIE2), 0x0000000c); + mtdcr(DCRN_PEGPL_REGBAL(PCIE2), 0x20002000); + mtdcr(DCRN_PEGPL_REGMSK(PCIE2), 0x00007001); + mtdcr(DCRN_PEGPL_SPECIAL(PCIE2), 0x68782800); + break; + } + utl_base = (unsigned int *)(CONFIG_SYS_PCIE_BASE + 0x1000 * port); + + /* + * Set buffer allocations and then assert VRB and TXE. + */ + out_be32(utl_base + PEUTL_OUTTR, 0x08000000); + out_be32(utl_base + PEUTL_INTR, 0x02000000); + out_be32(utl_base + PEUTL_OPDBSZ, 0x10000000); + out_be32(utl_base + PEUTL_PBBSZ, 0x53000000); + out_be32(utl_base + PEUTL_IPHBSZ, 0x08000000); + out_be32(utl_base + PEUTL_IPDBSZ, 0x10000000); + out_be32(utl_base + PEUTL_RCIRQEN, 0x00f00000); + out_be32(utl_base + PEUTL_PCTL, 0x80800066); +} + +static int check_error(void) +{ + u32 valPE0, valPE1, valPE2; + int err = 0; + + /* SDR0_PEGPLLLCT1 reset */ + if (!(valPE0 = SDR_READ(PESDR0_PLLLCT1) & 0x01000000)) + printf("PCIE: SDR0_PEGPLLLCT1 reset error 0x%x\n", valPE0); + + valPE0 = SDR_READ(PESDR0_RCSSET); + valPE1 = SDR_READ(PESDR1_RCSSET); + valPE2 = SDR_READ(PESDR2_RCSSET); + + /* SDR0_PExRCSSET rstgu */ + if (!(valPE0 & 0x01000000) || + !(valPE1 & 0x01000000) || + !(valPE2 & 0x01000000)) { + printf("PCIE: SDR0_PExRCSSET rstgu error\n"); + err = -1; + } + + /* SDR0_PExRCSSET rstdl */ + if (!(valPE0 & 0x00010000) || + !(valPE1 & 0x00010000) || + !(valPE2 & 0x00010000)) { + printf("PCIE: SDR0_PExRCSSET rstdl error\n"); + err = -1; + } + + /* SDR0_PExRCSSET rstpyn */ + if ((valPE0 & 0x00001000) || + (valPE1 & 0x00001000) || + (valPE2 & 0x00001000)) { + printf("PCIE: SDR0_PExRCSSET rstpyn error\n"); + err = -1; + } + + /* SDR0_PExRCSSET hldplb */ + if ((valPE0 & 0x10000000) || + (valPE1 & 0x10000000) || + (valPE2 & 0x10000000)) { + printf("PCIE: SDR0_PExRCSSET hldplb error\n"); + err = -1; + } + + /* SDR0_PExRCSSET rdy */ + if ((valPE0 & 0x00100000) || + (valPE1 & 0x00100000) || + (valPE2 & 0x00100000)) { + printf("PCIE: SDR0_PExRCSSET rdy error\n"); + err = -1; + } + + /* SDR0_PExRCSSET shutdown */ + if ((valPE0 & 0x00000100) || + (valPE1 & 0x00000100) || + (valPE2 & 0x00000100)) { + printf("PCIE: SDR0_PExRCSSET shutdown error\n"); + err = -1; + } + return err; +} + +/* + * Initialize PCI Express core + */ +int ppc4xx_init_pcie(void) +{ + int time_out = 20; + + /* Set PLL clock receiver to LVPECL */ + SDR_WRITE(PESDR0_PLLLCT1, SDR_READ(PESDR0_PLLLCT1) | 1 << 28); + + if (check_error()) { + printf("ERROR: failed to set PCIe reference clock receiver --" + "PESDR0_PLLLCT1 = 0x%08x\n", SDR_READ(PESDR0_PLLLCT1)); + + return -1; + } + + /* Did resistance calibration work? */ + if (!(SDR_READ(PESDR0_PLLLCT2) & 0x10000)) { + printf("ERROR: PCIe resistance calibration failed --" + "PESDR0_PLLLCT2 = 0x%08x\n", SDR_READ(PESDR0_PLLLCT2)); + + return -1; + } + /* De-assert reset of PCIe PLL, wait for lock */ + SDR_WRITE(PESDR0_PLLLCT1, SDR_READ(PESDR0_PLLLCT1) & ~(1 << 24)); + udelay(300); /* 300 uS is maximum time lock should take */ + + while (time_out) { + if (!(SDR_READ(PESDR0_PLLLCT3) & 0x10000000)) { + time_out--; + udelay(20); /* Wait 20 uS more if needed */ + } else + break; + } + if (!time_out) { + printf("ERROR: PCIe PLL VCO output not locked to ref clock --" + "PESDR0_PLLLCTS=0x%08x\n", SDR_READ(PESDR0_PLLLCT3)); + + return -1; + } + return 0; +} +#endif + +#if defined(CONFIG_460EX) || defined(CONFIG_460GT) +static void ppc4xx_setup_utl(u32 port) +{ + volatile void *utl_base = NULL; + + /* + * Map UTL registers at 0x0801_n000 (4K 0xfff mask) PEGPLn_REGMSK + */ + switch (port) { + case 0: + mtdcr(DCRN_PEGPL_REGBAH(PCIE0), U64_TO_U32_HIGH(CONFIG_SYS_PCIE0_UTLBASE)); + mtdcr(DCRN_PEGPL_REGBAL(PCIE0), U64_TO_U32_LOW(CONFIG_SYS_PCIE0_UTLBASE)); + mtdcr(DCRN_PEGPL_REGMSK(PCIE0), 0x00007001); /* BAM 11100000=4KB */ + mtdcr(DCRN_PEGPL_SPECIAL(PCIE0), 0); + break; + + case 1: + mtdcr(DCRN_PEGPL_REGBAH(PCIE1), U64_TO_U32_HIGH(CONFIG_SYS_PCIE0_UTLBASE)); + mtdcr(DCRN_PEGPL_REGBAL(PCIE1), U64_TO_U32_LOW(CONFIG_SYS_PCIE0_UTLBASE) + + 0x1000); + mtdcr(DCRN_PEGPL_REGMSK(PCIE1), 0x00007001); /* BAM 11100000=4KB */ + mtdcr(DCRN_PEGPL_SPECIAL(PCIE1), 0); + break; + } + utl_base = (unsigned int *)(CONFIG_SYS_PCIE_BASE + 0x1000 * port); + + /* + * Set buffer allocations and then assert VRB and TXE. + */ + out_be32(utl_base + PEUTL_PBCTL, 0x0800000c); /* PLBME, CRRE */ + out_be32(utl_base + PEUTL_OUTTR, 0x08000000); + out_be32(utl_base + PEUTL_INTR, 0x02000000); + out_be32(utl_base + PEUTL_OPDBSZ, 0x04000000); /* OPD = 512 Bytes */ + out_be32(utl_base + PEUTL_PBBSZ, 0x00000000); /* Max 512 Bytes */ + out_be32(utl_base + PEUTL_IPHBSZ, 0x02000000); + out_be32(utl_base + PEUTL_IPDBSZ, 0x04000000); /* IPD = 512 Bytes */ + out_be32(utl_base + PEUTL_RCIRQEN, 0x00f00000); + out_be32(utl_base + PEUTL_PCTL, 0x80800066); /* VRB,TXE,timeout=default */ +} + +/* + * TODO: double check PCI express SDR based on the latest user manual + * Some registers specified here no longer exist.. has to be + * updated based on the final EAS spec. + */ +static int check_error(void) +{ + u32 valPE0, valPE1; + int err = 0; + + valPE0 = SDR_READ(SDRN_PESDR_RCSSET(0)); + valPE1 = SDR_READ(SDRN_PESDR_RCSSET(1)); + + /* SDR0_PExRCSSET rstgu */ + if (!(valPE0 & PESDRx_RCSSET_RSTGU) || !(valPE1 & PESDRx_RCSSET_RSTGU)) { + printf("PCIE: SDR0_PExRCSSET rstgu error\n"); + err = -1; + } + + /* SDR0_PExRCSSET rstdl */ + if (!(valPE0 & PESDRx_RCSSET_RSTDL) || !(valPE1 & PESDRx_RCSSET_RSTDL)) { + printf("PCIE: SDR0_PExRCSSET rstdl error\n"); + err = -1; + } + + /* SDR0_PExRCSSET rstpyn */ + if ((valPE0 & PESDRx_RCSSET_RSTPYN) || (valPE1 & PESDRx_RCSSET_RSTPYN)) { + printf("PCIE: SDR0_PExRCSSET rstpyn error\n"); + err = -1; + } + + /* SDR0_PExRCSSET hldplb */ + if ((valPE0 & PESDRx_RCSSET_HLDPLB) || (valPE1 & PESDRx_RCSSET_HLDPLB)) { + printf("PCIE: SDR0_PExRCSSET hldplb error\n"); + err = -1; + } + + /* SDR0_PExRCSSET rdy */ + if ((valPE0 & PESDRx_RCSSET_RDY) || (valPE1 & PESDRx_RCSSET_RDY)) { + printf("PCIE: SDR0_PExRCSSET rdy error\n"); + err = -1; + } + + return err; +} + +/* + * Initialize PCI Express core as described in User Manual + * TODO: double check PE SDR PLL Register with the updated user manual. + */ +int ppc4xx_init_pcie(void) +{ + if (check_error()) + return -1; + + return 0; +} +#endif /* CONFIG_460EX */ + +#if defined(CONFIG_405EX) +static void ppc4xx_setup_utl(u32 port) +{ + u32 utl_base; + + /* + * Map UTL registers at 0xef4f_n000 (4K 0xfff mask) PEGPLn_REGMSK + */ + switch (port) { + case 0: + mtdcr(DCRN_PEGPL_REGBAH(PCIE0), 0x00000000); + mtdcr(DCRN_PEGPL_REGBAL(PCIE0), CONFIG_SYS_PCIE0_UTLBASE); + mtdcr(DCRN_PEGPL_REGMSK(PCIE0), 0x00007001); /* 4k region, valid */ + mtdcr(DCRN_PEGPL_SPECIAL(PCIE0), 0); + break; + + case 1: + mtdcr(DCRN_PEGPL_REGBAH(PCIE1), 0x00000000); + mtdcr(DCRN_PEGPL_REGBAL(PCIE1), CONFIG_SYS_PCIE1_UTLBASE); + mtdcr(DCRN_PEGPL_REGMSK(PCIE1), 0x00007001); /* 4k region, valid */ + mtdcr(DCRN_PEGPL_SPECIAL(PCIE1), 0); + + break; + } + utl_base = (port==0) ? CONFIG_SYS_PCIE0_UTLBASE : CONFIG_SYS_PCIE1_UTLBASE; + + /* + * Set buffer allocations and then assert VRB and TXE. + */ + out_be32((u32 *)(utl_base + PEUTL_OUTTR), 0x02000000); + out_be32((u32 *)(utl_base + PEUTL_INTR), 0x02000000); + out_be32((u32 *)(utl_base + PEUTL_OPDBSZ), 0x04000000); + out_be32((u32 *)(utl_base + PEUTL_PBBSZ), 0x21000000); + out_be32((u32 *)(utl_base + PEUTL_IPHBSZ), 0x02000000); + out_be32((u32 *)(utl_base + PEUTL_IPDBSZ), 0x04000000); + out_be32((u32 *)(utl_base + PEUTL_RCIRQEN), 0x00f00000); + out_be32((u32 *)(utl_base + PEUTL_PCTL), 0x80800066); + + out_be32((u32 *)(utl_base + PEUTL_PBCTL), 0x0800000c); + out_be32((u32 *)(utl_base + PEUTL_RCSTA), + in_be32((u32 *)(utl_base + PEUTL_RCSTA)) | 0x000040000); +} + +int ppc4xx_init_pcie(void) +{ + /* + * Nothing to do on 405EX + */ + return 0; +} +#endif /* CONFIG_405EX */ + +/* + * Board-specific pcie initialization + * Platform code can reimplement ppc4xx_init_pcie_port_hw() if needed + */ + +/* + * Initialize various parts of the PCI Express core for our port: + * + * - Set as a root port and enable max width + * (PXIE0 -> X8, PCIE1 and PCIE2 -> X4). + * - Set up UTL configuration. + * - Increase SERDES drive strength to levels suggested by AMCC. + * - De-assert RSTPYN, RSTDL and RSTGU. + * + * NOTICE for 440SPE revB chip: PESDRn_UTLSET2 is not set - we leave it + * with default setting 0x11310000. The register has new fields, + * PESDRn_UTLSET2[LKINE] in particular: clearing it leads to PCIE core + * hang. + */ +#if defined(CONFIG_440SPE) +int __ppc4xx_init_pcie_port_hw(int port, int rootport) +{ + u32 val = 1 << 24; + u32 utlset1; + + if (rootport) { + val = PTYPE_ROOT_PORT << 20; + utlset1 = 0x21222222; + } else { + val = PTYPE_LEGACY_ENDPOINT << 20; + utlset1 = 0x20222222; + } + + if (port == 0) + val |= LNKW_X8 << 12; + else + val |= LNKW_X4 << 12; + + SDR_WRITE(SDRN_PESDR_DLPSET(port), val); + SDR_WRITE(SDRN_PESDR_UTLSET1(port), utlset1); + if (!ppc440spe_revB()) + SDR_WRITE(SDRN_PESDR_UTLSET2(port), 0x11000000); + SDR_WRITE(SDRN_PESDR_HSSL0SET1(port), 0x35000000); + SDR_WRITE(SDRN_PESDR_HSSL1SET1(port), 0x35000000); + SDR_WRITE(SDRN_PESDR_HSSL2SET1(port), 0x35000000); + SDR_WRITE(SDRN_PESDR_HSSL3SET1(port), 0x35000000); + if (port == 0) { + SDR_WRITE(PESDR0_HSSL4SET1, 0x35000000); + SDR_WRITE(PESDR0_HSSL5SET1, 0x35000000); + SDR_WRITE(PESDR0_HSSL6SET1, 0x35000000); + SDR_WRITE(PESDR0_HSSL7SET1, 0x35000000); + } + SDR_WRITE(SDRN_PESDR_RCSSET(port), (SDR_READ(SDRN_PESDR_RCSSET(port)) & + ~(1 << 24 | 1 << 16)) | 1 << 12); + + return 0; +} +#endif /* CONFIG_440SPE */ + +#if defined(CONFIG_460EX) || defined(CONFIG_460GT) +int __ppc4xx_init_pcie_port_hw(int port, int rootport) +{ + u32 val; + u32 utlset1; + + if (rootport) + val = PTYPE_ROOT_PORT << 20; + else + val = PTYPE_LEGACY_ENDPOINT << 20; + + if (port == 0) { + val |= LNKW_X1 << 12; + utlset1 = 0x20000000; + } else { + val |= LNKW_X4 << 12; + utlset1 = 0x20101101; + } + + SDR_WRITE(SDRN_PESDR_DLPSET(port), val); + SDR_WRITE(SDRN_PESDR_UTLSET1(port), utlset1); + SDR_WRITE(SDRN_PESDR_UTLSET2(port), 0x01210000); + + switch (port) { + case 0: + SDR_WRITE(PESDR0_L0CDRCTL, 0x00003230); + SDR_WRITE(PESDR0_L0DRV, 0x00000130); + SDR_WRITE(PESDR0_L0CLK, 0x00000006); + + SDR_WRITE(PESDR0_PHY_CTL_RST,0x10000000); + break; + + case 1: + SDR_WRITE(PESDR1_L0CDRCTL, 0x00003230); + SDR_WRITE(PESDR1_L1CDRCTL, 0x00003230); + SDR_WRITE(PESDR1_L2CDRCTL, 0x00003230); + SDR_WRITE(PESDR1_L3CDRCTL, 0x00003230); + SDR_WRITE(PESDR1_L0DRV, 0x00000130); + SDR_WRITE(PESDR1_L1DRV, 0x00000130); + SDR_WRITE(PESDR1_L2DRV, 0x00000130); + SDR_WRITE(PESDR1_L3DRV, 0x00000130); + SDR_WRITE(PESDR1_L0CLK, 0x00000006); + SDR_WRITE(PESDR1_L1CLK, 0x00000006); + SDR_WRITE(PESDR1_L2CLK, 0x00000006); + SDR_WRITE(PESDR1_L3CLK, 0x00000006); + + SDR_WRITE(PESDR1_PHY_CTL_RST,0x10000000); + break; + } + + SDR_WRITE(SDRN_PESDR_RCSSET(port), SDR_READ(SDRN_PESDR_RCSSET(port)) | + (PESDRx_RCSSET_RSTGU | PESDRx_RCSSET_RSTPYN)); + + /* Poll for PHY reset */ + switch (port) { + case 0: + while (!(SDR_READ(PESDR0_RSTSTA) & 0x1)) + udelay(10); + break; + case 1: + while (!(SDR_READ(PESDR1_RSTSTA) & 0x1)) + udelay(10); + break; + } + + SDR_WRITE(SDRN_PESDR_RCSSET(port), + (SDR_READ(SDRN_PESDR_RCSSET(port)) & + ~(PESDRx_RCSSET_RSTGU | PESDRx_RCSSET_RSTDL)) | + PESDRx_RCSSET_RSTPYN); + + return 0; +} +#endif /* CONFIG_440SPE */ + +#if defined(CONFIG_405EX) +int __ppc4xx_init_pcie_port_hw(int port, int rootport) +{ + u32 val; + + if (rootport) + val = 0x00401000; + else + val = 0x00101000; + + SDR_WRITE(SDRN_PESDR_DLPSET(port), val); + SDR_WRITE(SDRN_PESDR_UTLSET1(port), 0x00000000); + SDR_WRITE(SDRN_PESDR_UTLSET2(port), 0x01010000); + SDR_WRITE(SDRN_PESDR_PHYSET1(port), 0x720F0000); + SDR_WRITE(SDRN_PESDR_PHYSET2(port), 0x70600003); + + /* Assert the PE0_PHY reset */ + SDR_WRITE(SDRN_PESDR_RCSSET(port), 0x01010000); + udelay(1000); + + /* deassert the PE0_hotreset */ + if (is_end_point(port)) + SDR_WRITE(SDRN_PESDR_RCSSET(port), 0x01111000); + else + SDR_WRITE(SDRN_PESDR_RCSSET(port), 0x01101000); + + /* poll for phy !reset */ + while (!(SDR_READ(SDRN_PESDR_PHYSTA(port)) & 0x00001000)) + ; + + /* deassert the PE0_gpl_utl_reset */ + SDR_WRITE(SDRN_PESDR_RCSSET(port), 0x00101000); + + if (port == 0) + mtdcr(DCRN_PEGPL_CFG(PCIE0), 0x10000000); /* guarded on */ + else + mtdcr(DCRN_PEGPL_CFG(PCIE1), 0x10000000); /* guarded on */ + + return 0; +} +#endif /* CONFIG_405EX */ + +int ppc4xx_init_pcie_port_hw(int port, int rootport) +__attribute__((weak, alias("__ppc4xx_init_pcie_port_hw"))); + +/* + * We map PCI Express configuration access into the 512MB regions + * + * NOTICE: revB is very strict about PLB real addressess and ranges to + * be mapped for config space; it seems to only work with d_nnnn_nnnn + * range (hangs the core upon config transaction attempts when set + * otherwise) while revA uses c_nnnn_nnnn. + * + * For 440SPe revA: + * PCIE0: 0xc_4000_0000 + * PCIE1: 0xc_8000_0000 + * PCIE2: 0xc_c000_0000 + * + * For 440SPe revB: + * PCIE0: 0xd_0000_0000 + * PCIE1: 0xd_2000_0000 + * PCIE2: 0xd_4000_0000 + * + * For 405EX: + * PCIE0: 0xa000_0000 + * PCIE1: 0xc000_0000 + * + * For 460EX/GT: + * PCIE0: 0xd_0000_0000 + * PCIE1: 0xd_2000_0000 + */ +static inline u64 ppc4xx_get_cfgaddr(int port) +{ +#if defined(CONFIG_405EX) + if (port == 0) + return (u64)CONFIG_SYS_PCIE0_CFGBASE; + else + return (u64)CONFIG_SYS_PCIE1_CFGBASE; +#endif +#if defined(CONFIG_440SPE) + if (ppc440spe_revB()) { + switch (port) { + default: /* to satisfy compiler */ + case 0: + return 0x0000000d00000000ULL; + case 1: + return 0x0000000d20000000ULL; + case 2: + return 0x0000000d40000000ULL; + } + } else { + switch (port) { + default: /* to satisfy compiler */ + case 0: + return 0x0000000c40000000ULL; + case 1: + return 0x0000000c80000000ULL; + case 2: + return 0x0000000cc0000000ULL; + } + } +#endif +#if defined(CONFIG_460EX) || defined(CONFIG_460GT) + if (port == 0) + return 0x0000000d00000000ULL; + else + return 0x0000000d20000000ULL; +#endif +} + +/* + * 4xx boards as endpoint and root point setup + * and + * testing inbound and out bound windows + * + * 4xx boards can be plugged into another 4xx boards or you can get PCI-E + * cable which can be used to setup loop back from one port to another port. + * Please rememeber that unless there is a endpoint plugged in to root port it + * will not initialize. It is the same in case of endpoint , unless there is + * root port attached it will not initialize. + * + * In this release of software all the PCI-E ports are configured as either + * endpoint or rootpoint.In future we will have support for selective ports + * setup as endpoint and root point in single board. + * + * Once your board came up as root point , you can verify by reading + * /proc/bus/pci/devices. Where you can see the configuration registers + * of endpoint device attached to the port. + * + * Enpoint cofiguration can be verified by connecting 4xx board to any + * host or another 4xx board. Then try to scan the device. In case of + * linux use "lspci" or appripriate os command. + * + * How do I verify the inbound and out bound windows ? (4xx to 4xx) + * in this configuration inbound and outbound windows are setup to access + * sram memroy area. SRAM is at 0x4 0000 0000 , on PLB bus. This address + * is mapped at 0x90000000. From u-boot prompt write data 0xb000 0000, + * This is waere your POM(PLB out bound memory window) mapped. then + * read the data from other 4xx board's u-boot prompt at address + * 0x9000 0000(SRAM). Data should match. + * In case of inbound , write data to u-boot command prompt at 0xb000 0000 + * which is mapped to 0x4 0000 0000. Now on rootpoint yucca u-boot prompt check + * data at 0x9000 0000(SRAM).Data should match. + */ +int ppc4xx_init_pcie_port(int port, int rootport) +{ + static int core_init; + volatile u32 val = 0; + int attempts; + u64 addr; + u32 low, high; + + if (!core_init) { + if (ppc4xx_init_pcie()) + return -1; + ++core_init; + } + + /* + * Initialize various parts of the PCI Express core for our port + */ + ppc4xx_init_pcie_port_hw(port, rootport); + + /* + * Notice: the following delay has critical impact on device + * initialization - if too short (<50ms) the link doesn't get up. + */ + mdelay(100); + + val = SDR_READ(SDRN_PESDR_RCSSTS(port)); + if (val & (1 << 20)) { + printf("PCIE%d: PGRST failed %08x\n", port, val); + return -1; + } + + /* + * Verify link is up + */ + val = SDR_READ(SDRN_PESDR_LOOP(port)); + if (!(val & 0x00001000)) { + printf("PCIE%d: link is not up.\n", port); + return -ENODEV; + } + + /* + * Setup UTL registers - but only on revA! + * We use default settings for revB chip. + */ + if (!ppc440spe_revB()) + ppc4xx_setup_utl(port); + + /* + * We map PCI Express configuration access into the 512MB regions + */ + addr = ppc4xx_get_cfgaddr(port); + low = U64_TO_U32_LOW(addr); + high = U64_TO_U32_HIGH(addr); + + switch (port) { + case 0: + mtdcr(DCRN_PEGPL_CFGBAH(PCIE0), high); + mtdcr(DCRN_PEGPL_CFGBAL(PCIE0), low); + mtdcr(DCRN_PEGPL_CFGMSK(PCIE0), 0xe0000001); /* 512MB region, valid */ + break; + case 1: + mtdcr(DCRN_PEGPL_CFGBAH(PCIE1), high); + mtdcr(DCRN_PEGPL_CFGBAL(PCIE1), low); + mtdcr(DCRN_PEGPL_CFGMSK(PCIE1), 0xe0000001); /* 512MB region, valid */ + break; +#if CONFIG_SYS_PCIE_NR_PORTS > 2 + case 2: + mtdcr(DCRN_PEGPL_CFGBAH(PCIE2), high); + mtdcr(DCRN_PEGPL_CFGBAL(PCIE2), low); + mtdcr(DCRN_PEGPL_CFGMSK(PCIE2), 0xe0000001); /* 512MB region, valid */ + break; +#endif + } + + /* + * Check for VC0 active and assert RDY. + */ + attempts = 10; + while(!(SDR_READ(SDRN_PESDR_RCSSTS(port)) & (1 << 16))) { + if (!(attempts--)) { + printf("PCIE%d: VC0 not active\n", port); + return -1; + } + mdelay(1000); + } + SDR_WRITE(SDRN_PESDR_RCSSET(port), + SDR_READ(SDRN_PESDR_RCSSET(port)) | 1 << 20); + mdelay(100); + + return 0; +} + +int ppc4xx_init_pcie_rootport(int port) +{ + return ppc4xx_init_pcie_port(port, 1); +} + +int ppc4xx_init_pcie_endport(int port) +{ + return ppc4xx_init_pcie_port(port, 0); +} + +void ppc4xx_setup_pcie_rootpoint(struct pci_controller *hose, int port) +{ + volatile void *mbase = NULL; + volatile void *rmbase = NULL; + + pci_set_ops(hose, + pcie_read_config_byte, + pcie_read_config_word, + pcie_read_config_dword, + pcie_write_config_byte, + pcie_write_config_word, + pcie_write_config_dword); + + switch (port) { + case 0: + mbase = (u32 *)CONFIG_SYS_PCIE0_XCFGBASE; + rmbase = (u32 *)CONFIG_SYS_PCIE0_CFGBASE; + hose->cfg_data = (u8 *)CONFIG_SYS_PCIE0_CFGBASE; + break; + case 1: + mbase = (u32 *)CONFIG_SYS_PCIE1_XCFGBASE; + rmbase = (u32 *)CONFIG_SYS_PCIE1_CFGBASE; + hose->cfg_data = (u8 *)CONFIG_SYS_PCIE1_CFGBASE; + break; +#if CONFIG_SYS_PCIE_NR_PORTS > 2 + case 2: + mbase = (u32 *)CONFIG_SYS_PCIE2_XCFGBASE; + rmbase = (u32 *)CONFIG_SYS_PCIE2_CFGBASE; + hose->cfg_data = (u8 *)CONFIG_SYS_PCIE2_CFGBASE; + break; +#endif + } + + /* + * Set bus numbers on our root port + */ + out_8((u8 *)mbase + PCI_PRIMARY_BUS, 0); + out_8((u8 *)mbase + PCI_SECONDARY_BUS, 1); + out_8((u8 *)mbase + PCI_SUBORDINATE_BUS, 1); + + /* + * Set up outbound translation to hose->mem_space from PLB + * addresses at an offset of 0xd_0000_0000. We set the low + * bits of the mask to 11 to turn off splitting into 8 + * subregions and to enable the outbound translation. + */ + out_le32(mbase + PECFG_POM0LAH, 0x00000000); + out_le32(mbase + PECFG_POM0LAL, CONFIG_SYS_PCIE_MEMBASE + + port * CONFIG_SYS_PCIE_MEMSIZE); + debug("PECFG_POM0LA=%08x.%08x\n", in_le32(mbase + PECFG_POM0LAH), + in_le32(mbase + PECFG_POM0LAL)); + + switch (port) { + case 0: + mtdcr(DCRN_PEGPL_OMR1BAH(PCIE0), CONFIG_SYS_PCIE_ADDR_HIGH); + mtdcr(DCRN_PEGPL_OMR1BAL(PCIE0), CONFIG_SYS_PCIE_MEMBASE + + port * CONFIG_SYS_PCIE_MEMSIZE); + mtdcr(DCRN_PEGPL_OMR1MSKH(PCIE0), 0x7fffffff); + mtdcr(DCRN_PEGPL_OMR1MSKL(PCIE0), + ~(CONFIG_SYS_PCIE_MEMSIZE - 1) | 3); + debug("0:PEGPL_OMR1BA=%08x.%08x MSK=%08x.%08x\n", + mfdcr(DCRN_PEGPL_OMR1BAH(PCIE0)), + mfdcr(DCRN_PEGPL_OMR1BAL(PCIE0)), + mfdcr(DCRN_PEGPL_OMR1MSKH(PCIE0)), + mfdcr(DCRN_PEGPL_OMR1MSKL(PCIE0))); + break; + case 1: + mtdcr(DCRN_PEGPL_OMR1BAH(PCIE1), CONFIG_SYS_PCIE_ADDR_HIGH); + mtdcr(DCRN_PEGPL_OMR1BAL(PCIE1), CONFIG_SYS_PCIE_MEMBASE + + port * CONFIG_SYS_PCIE_MEMSIZE); + mtdcr(DCRN_PEGPL_OMR1MSKH(PCIE1), 0x7fffffff); + mtdcr(DCRN_PEGPL_OMR1MSKL(PCIE1), + ~(CONFIG_SYS_PCIE_MEMSIZE - 1) | 3); + debug("1:PEGPL_OMR1BA=%08x.%08x MSK=%08x.%08x\n", + mfdcr(DCRN_PEGPL_OMR1BAH(PCIE1)), + mfdcr(DCRN_PEGPL_OMR1BAL(PCIE1)), + mfdcr(DCRN_PEGPL_OMR1MSKH(PCIE1)), + mfdcr(DCRN_PEGPL_OMR1MSKL(PCIE1))); + break; +#if CONFIG_SYS_PCIE_NR_PORTS > 2 + case 2: + mtdcr(DCRN_PEGPL_OMR1BAH(PCIE2), CONFIG_SYS_PCIE_ADDR_HIGH); + mtdcr(DCRN_PEGPL_OMR1BAL(PCIE2), CONFIG_SYS_PCIE_MEMBASE + + port * CONFIG_SYS_PCIE_MEMSIZE); + mtdcr(DCRN_PEGPL_OMR1MSKH(PCIE2), 0x7fffffff); + mtdcr(DCRN_PEGPL_OMR1MSKL(PCIE2), + ~(CONFIG_SYS_PCIE_MEMSIZE - 1) | 3); + debug("2:PEGPL_OMR1BA=%08x.%08x MSK=%08x.%08x\n", + mfdcr(DCRN_PEGPL_OMR1BAH(PCIE2)), + mfdcr(DCRN_PEGPL_OMR1BAL(PCIE2)), + mfdcr(DCRN_PEGPL_OMR1MSKH(PCIE2)), + mfdcr(DCRN_PEGPL_OMR1MSKL(PCIE2))); + break; +#endif + } + + /* Set up 4GB inbound memory window at 0 */ + out_le32(mbase + PCI_BASE_ADDRESS_0, 0); + out_le32(mbase + PCI_BASE_ADDRESS_1, 0); + out_le32(mbase + PECFG_BAR0HMPA, 0x7ffffff); + out_le32(mbase + PECFG_BAR0LMPA, 0); + + out_le32(mbase + PECFG_PIM01SAH, 0xffff0000); + out_le32(mbase + PECFG_PIM01SAL, 0x00000000); + out_le32(mbase + PECFG_PIM0LAL, 0); + out_le32(mbase + PECFG_PIM0LAH, 0); + out_le32(mbase + PECFG_PIM1LAL, 0x00000000); + out_le32(mbase + PECFG_PIM1LAH, 0x00000004); + out_le32(mbase + PECFG_PIMEN, 0x1); + + /* Enable I/O, Mem, and Busmaster cycles */ + out_le16((u16 *)(mbase + PCI_COMMAND), + in_le16((u16 *)(mbase + PCI_COMMAND)) | + PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER); + + /* Set Device and Vendor Id */ + out_le16(mbase + 0x200, 0xaaa0 + port); + out_le16(mbase + 0x202, 0xbed0 + port); + + /* Set Class Code to PCI-PCI bridge and Revision Id to 1 */ + out_le32(mbase + 0x208, 0x06040001); + + printf("PCIE%d: successfully set as root-complex\n", port); +} + +int ppc4xx_setup_pcie_endpoint(struct pci_controller *hose, int port) +{ + volatile void *mbase = NULL; + int attempts = 0; + + pci_set_ops(hose, + pcie_read_config_byte, + pcie_read_config_word, + pcie_read_config_dword, + pcie_write_config_byte, + pcie_write_config_word, + pcie_write_config_dword); + + switch (port) { + case 0: + mbase = (u32 *)CONFIG_SYS_PCIE0_XCFGBASE; + hose->cfg_data = (u8 *)CONFIG_SYS_PCIE0_CFGBASE; + break; + case 1: + mbase = (u32 *)CONFIG_SYS_PCIE1_XCFGBASE; + hose->cfg_data = (u8 *)CONFIG_SYS_PCIE1_CFGBASE; + break; +#if defined(CONFIG_SYS_PCIE2_CFGBASE) + case 2: + mbase = (u32 *)CONFIG_SYS_PCIE2_XCFGBASE; + hose->cfg_data = (u8 *)CONFIG_SYS_PCIE2_CFGBASE; + break; +#endif + } + + /* + * Set up outbound translation to hose->mem_space from PLB + * addresses at an offset of 0xd_0000_0000. We set the low + * bits of the mask to 11 to turn off splitting into 8 + * subregions and to enable the outbound translation. + */ + out_le32(mbase + PECFG_POM0LAH, 0x00001ff8); + out_le32(mbase + PECFG_POM0LAL, 0x00001000); + + switch (port) { + case 0: + mtdcr(DCRN_PEGPL_OMR1BAH(PCIE0), CONFIG_SYS_PCIE_ADDR_HIGH); + mtdcr(DCRN_PEGPL_OMR1BAL(PCIE0), CONFIG_SYS_PCIE_MEMBASE + + port * CONFIG_SYS_PCIE_MEMSIZE); + mtdcr(DCRN_PEGPL_OMR1MSKH(PCIE0), 0x7fffffff); + mtdcr(DCRN_PEGPL_OMR1MSKL(PCIE0), + ~(CONFIG_SYS_PCIE_MEMSIZE - 1) | 3); + break; + case 1: + mtdcr(DCRN_PEGPL_OMR1BAH(PCIE1), CONFIG_SYS_PCIE_ADDR_HIGH); + mtdcr(DCRN_PEGPL_OMR1BAL(PCIE1), CONFIG_SYS_PCIE_MEMBASE + + port * CONFIG_SYS_PCIE_MEMSIZE); + mtdcr(DCRN_PEGPL_OMR1MSKH(PCIE1), 0x7fffffff); + mtdcr(DCRN_PEGPL_OMR1MSKL(PCIE1), + ~(CONFIG_SYS_PCIE_MEMSIZE - 1) | 3); + break; +#if CONFIG_SYS_PCIE_NR_PORTS > 2 + case 2: + mtdcr(DCRN_PEGPL_OMR1BAH(PCIE2), CONFIG_SYS_PCIE_ADDR_HIGH); + mtdcr(DCRN_PEGPL_OMR1BAL(PCIE2), CONFIG_SYS_PCIE_MEMBASE + + port * CONFIG_SYS_PCIE_MEMSIZE); + mtdcr(DCRN_PEGPL_OMR1MSKH(PCIE2), 0x7fffffff); + mtdcr(DCRN_PEGPL_OMR1MSKL(PCIE2), + ~(CONFIG_SYS_PCIE_MEMSIZE - 1) | 3); + break; +#endif + } + + /* Set up 64MB inbound memory window at 0 */ + out_le32(mbase + PCI_BASE_ADDRESS_0, 0); + out_le32(mbase + PCI_BASE_ADDRESS_1, 0); + + out_le32(mbase + PECFG_PIM01SAH, 0xffffffff); + out_le32(mbase + PECFG_PIM01SAL, 0xfc000000); + + /* Setup BAR0 */ + out_le32(mbase + PECFG_BAR0HMPA, 0x7fffffff); + out_le32(mbase + PECFG_BAR0LMPA, 0xfc000000 | PCI_BASE_ADDRESS_MEM_TYPE_64); + + /* Disable BAR1 & BAR2 */ + out_le32(mbase + PECFG_BAR1MPA, 0); + out_le32(mbase + PECFG_BAR2HMPA, 0); + out_le32(mbase + PECFG_BAR2LMPA, 0); + + out_le32(mbase + PECFG_PIM0LAL, U64_TO_U32_LOW(CONFIG_SYS_PCIE_INBOUND_BASE)); + out_le32(mbase + PECFG_PIM0LAH, U64_TO_U32_HIGH(CONFIG_SYS_PCIE_INBOUND_BASE)); + out_le32(mbase + PECFG_PIMEN, 0x1); + + /* Enable I/O, Mem, and Busmaster cycles */ + out_le16((u16 *)(mbase + PCI_COMMAND), + in_le16((u16 *)(mbase + PCI_COMMAND)) | + PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER); + out_le16(mbase + 0x200, 0xcaad); /* Setting vendor ID */ + out_le16(mbase + 0x202, 0xfeed); /* Setting device ID */ + + /* Set Class Code to Processor/PPC */ + out_le32(mbase + 0x208, 0x0b200001); + + attempts = 10; + while(!(SDR_READ(SDRN_PESDR_RCSSTS(port)) & (1 << 8))) { + if (!(attempts--)) { + printf("PCIE%d: BME not active\n", port); + return -1; + } + mdelay(1000); + } + + printf("PCIE%d: successfully set as endpoint\n", port); + + return 0; +} +#endif /* CONFIG_440SPE && CONFIG_PCI */ diff --git a/arch/powerpc/cpu/ppc4xx/4xx_uart.c b/arch/powerpc/cpu/ppc4xx/4xx_uart.c new file mode 100644 index 0000000..8de6542 --- /dev/null +++ b/arch/powerpc/cpu/ppc4xx/4xx_uart.c @@ -0,0 +1,878 @@ +/* + * (C) Copyright 2000-2006 + * Wolfgang Denk, DENX Software Engineering, wd@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 + */ + +/* + * This source code is dual-licensed. You may use it under the terms of the + * GNU General Public License version 2, or under the license below. + * + * This source code has been made available to you by IBM on an AS-IS + * basis. Anyone receiving this source is licensed under IBM + * copyrights to use it in any way he or she deems fit, including + * copying it, modifying it, compiling it, and redistributing it either + * with or without modifications. No license under IBM patents or + * patent applications is to be implied by the copyright license. + * + * Any user of this software should understand that IBM cannot provide + * technical support for this software and will not be responsible for + * any consequences resulting from the use of this software. + * + * Any person who transfers this source code or any derivative work + * must include the IBM copyright notice, this paragraph, and the + * preceding two paragraphs in the transferred software. + * + * COPYRIGHT I B M CORPORATION 1995 + * LICENSED MATERIAL - PROGRAM PROPERTY OF I B M + */ + +#include <common.h> +#include <commproc.h> +#include <asm/processor.h> +#include <asm/io.h> +#include <watchdog.h> +#include <ppc4xx.h> + +#ifdef CONFIG_SERIAL_MULTI +#include <serial.h> +#endif + +#ifdef CONFIG_SERIAL_SOFTWARE_FIFO +#include <malloc.h> +#endif + +DECLARE_GLOBAL_DATA_PTR; + +#if defined(CONFIG_405GP) || defined(CONFIG_405CR) || \ + defined(CONFIG_405EP) || defined(CONFIG_405EZ) || \ + defined(CONFIG_405EX) || defined(CONFIG_440) + +#if defined(CONFIG_440) +#if defined(CONFIG_440EP) || defined(CONFIG_440GR) || \ + defined(CONFIG_440EPX) || defined(CONFIG_440GRX) || \ + defined(CONFIG_460EX) || defined(CONFIG_460GT) +#define UART0_BASE (CONFIG_SYS_PERIPHERAL_BASE + 0x00000300) +#define UART1_BASE (CONFIG_SYS_PERIPHERAL_BASE + 0x00000400) +#else +#define UART0_BASE (CONFIG_SYS_PERIPHERAL_BASE + 0x00000200) +#define UART1_BASE (CONFIG_SYS_PERIPHERAL_BASE + 0x00000300) +#endif + +#if defined(CONFIG_440SP) || defined(CONFIG_440SPE) +#define UART2_BASE (CONFIG_SYS_PERIPHERAL_BASE + 0x00000600) +#endif + +#if defined(CONFIG_460EX) || defined(CONFIG_460GT) +#define UART2_BASE (CONFIG_SYS_PERIPHERAL_BASE + 0x00000500) +#define UART3_BASE (CONFIG_SYS_PERIPHERAL_BASE + 0x00000600) +#endif + +#if defined(CONFIG_440GP) +#define CR0_MASK 0x3fff0000 +#define CR0_EXTCLK_ENA 0x00600000 +#define CR0_UDIV_POS 16 +#define UDIV_SUBTRACT 1 +#define UART0_SDR CPC0_CR0 +#define MFREG(a, d) d = mfdcr(a) +#define MTREG(a, d) mtdcr(a, d) +#else /* #if defined(CONFIG_440GP) */ +/* all other 440 PPC's access clock divider via sdr register */ +#define CR0_MASK 0xdfffffff +#define CR0_EXTCLK_ENA 0x00800000 +#define CR0_UDIV_POS 0 +#define UDIV_SUBTRACT 0 +#define UART0_SDR SDR0_UART0 +#define UART1_SDR SDR0_UART1 +#if defined(CONFIG_440EP) || defined(CONFIG_440EPX) || \ + defined(CONFIG_440GR) || defined(CONFIG_440GRX) || \ + defined(CONFIG_440SP) || defined(CONFIG_440SPE) || \ + defined(CONFIG_460EX) || defined(CONFIG_460GT) +#define UART2_SDR SDR0_UART2 +#endif +#if defined(CONFIG_440EP) || defined(CONFIG_440EPX) || \ + defined(CONFIG_440GR) || defined(CONFIG_440GRX) || \ + defined(CONFIG_460EX) || defined(CONFIG_460GT) +#define UART3_SDR SDR0_UART3 +#endif +#define MFREG(a, d) mfsdr(a, d) +#define MTREG(a, d) mtsdr(a, d) +#endif /* #if defined(CONFIG_440GP) */ +#elif defined(CONFIG_405EP) || defined(CONFIG_405EZ) +#define UART0_BASE 0xef600300 +#define UART1_BASE 0xef600400 +#define UCR0_MASK 0x0000007f +#define UCR1_MASK 0x00007f00 +#define UCR0_UDIV_POS 0 +#define UCR1_UDIV_POS 8 +#define UDIV_MAX 127 +#elif defined(CONFIG_405EX) +#define UART0_BASE 0xef600200 +#define UART1_BASE 0xef600300 +#define CR0_MASK 0x000000ff +#define CR0_EXTCLK_ENA 0x00800000 +#define CR0_UDIV_POS 0 +#define UDIV_SUBTRACT 0 +#define UART0_SDR SDR0_UART0 +#define UART1_SDR SDR0_UART1 +#else /* CONFIG_405GP || CONFIG_405CR */ +#define UART0_BASE 0xef600300 +#define UART1_BASE 0xef600400 +#define CR0_MASK 0x00001fff +#define CR0_EXTCLK_ENA 0x000000c0 +#define CR0_UDIV_POS 1 +#define UDIV_MAX 32 +#endif + +/* using serial port 0 or 1 as U-Boot console ? */ +#if defined(CONFIG_UART1_CONSOLE) +#define ACTING_UART0_BASE UART1_BASE +#define ACTING_UART1_BASE UART0_BASE +#else +#define ACTING_UART0_BASE UART0_BASE +#define ACTING_UART1_BASE UART1_BASE +#endif + +#if defined(CONFIG_405EP) && defined(CONFIG_SYS_EXT_SERIAL_CLOCK) +#error "External serial clock not supported on AMCC PPC405EP!" +#endif + +#define UART_RBR 0x00 +#define UART_THR 0x00 +#define UART_IER 0x01 +#define UART_IIR 0x02 +#define UART_FCR 0x02 +#define UART_LCR 0x03 +#define UART_MCR 0x04 +#define UART_LSR 0x05 +#define UART_MSR 0x06 +#define UART_SCR 0x07 +#define UART_DLL 0x00 +#define UART_DLM 0x01 + +/*-----------------------------------------------------------------------------+ + | Line Status Register. + +-----------------------------------------------------------------------------*/ +#define asyncLSRDataReady1 0x01 +#define asyncLSROverrunError1 0x02 +#define asyncLSRParityError1 0x04 +#define asyncLSRFramingError1 0x08 +#define asyncLSRBreakInterrupt1 0x10 +#define asyncLSRTxHoldEmpty1 0x20 +#define asyncLSRTxShiftEmpty1 0x40 +#define asyncLSRRxFifoError1 0x80 + +#ifdef CONFIG_SERIAL_SOFTWARE_FIFO +/*-----------------------------------------------------------------------------+ + | Fifo + +-----------------------------------------------------------------------------*/ +typedef struct { + char *rx_buffer; + ulong rx_put; + ulong rx_get; +} serial_buffer_t; + +volatile static serial_buffer_t buf_info; +#endif + +static void serial_init_common(u32 base, u32 udiv, u16 bdiv) +{ + PPC4xx_SYS_INFO sys_info; + u8 val; + + get_sys_info(&sys_info); + + /* Correct UART frequency in bd-info struct now that + * the UART divisor is available + */ +#ifdef CONFIG_SYS_EXT_SERIAL_CLOCK + gd->uart_clk = CONFIG_SYS_EXT_SERIAL_CLOCK; +#else + gd->uart_clk = sys_info.freqUART / udiv; +#endif + + out_8((u8 *)base + UART_LCR, 0x80); /* set DLAB bit */ + out_8((u8 *)base + UART_DLL, bdiv); /* set baudrate divisor */ + out_8((u8 *)base + UART_DLM, bdiv >> 8); /* set baudrate divisor */ + out_8((u8 *)base + UART_LCR, 0x03); /* clear DLAB; set 8 bits, no parity */ + out_8((u8 *)base + UART_FCR, 0x00); /* disable FIFO */ + out_8((u8 *)base + UART_MCR, 0x00); /* no modem control DTR RTS */ + val = in_8((u8 *)base + UART_LSR); /* clear line status */ + val = in_8((u8 *)base + UART_RBR); /* read receive buffer */ + out_8((u8 *)base + UART_SCR, 0x00); /* set scratchpad */ + out_8((u8 *)base + UART_IER, 0x00); /* set interrupt enable reg */ +} + +#if (defined(CONFIG_440) || defined(CONFIG_405EX)) && \ + !defined(CONFIG_SYS_EXT_SERIAL_CLOCK) +static void serial_divs (int baudrate, unsigned long *pudiv, + unsigned short *pbdiv) +{ + sys_info_t sysinfo; + unsigned long div; /* total divisor udiv * bdiv */ + unsigned long umin; /* minimum udiv */ + unsigned short diff; /* smallest diff */ + unsigned long udiv; /* best udiv */ + unsigned short idiff; /* current diff */ + unsigned short ibdiv; /* current bdiv */ + unsigned long i; + unsigned long est; /* current estimate */ + + get_sys_info(&sysinfo); + + udiv = 32; /* Assume lowest possible serial clk */ + div = sysinfo.freqPLB / (16 * baudrate); /* total divisor */ + umin = sysinfo.pllOpbDiv << 1; /* 2 x OPB divisor */ + diff = 32; /* highest possible */ + + /* i is the test udiv value -- start with the largest + * possible (32) to minimize serial clock and constrain + * search to umin. + */ + for (i = 32; i > umin; i--) { + ibdiv = div / i; + est = i * ibdiv; + idiff = (est > div) ? (est-div) : (div-est); + if (idiff == 0) { + udiv = i; + break; /* can't do better */ + } else if (idiff < diff) { + udiv = i; /* best so far */ + diff = idiff; /* update lowest diff*/ + } + } + + *pudiv = udiv; + *pbdiv = div / udiv; +} + +#elif defined(CONFIG_405EZ) + +static void serial_divs (int baudrate, unsigned long *pudiv, + unsigned short *pbdiv) +{ + sys_info_t sysinfo; + unsigned long div; /* total divisor udiv * bdiv */ + unsigned long umin; /* minimum udiv */ + unsigned short diff; /* smallest diff */ + unsigned long udiv; /* best udiv */ + unsigned short idiff; /* current diff */ + unsigned short ibdiv; /* current bdiv */ + unsigned long i; + unsigned long est; /* current estimate */ + unsigned long plloutb; + unsigned long cpr_pllc; + u32 reg; + + /* check the pll feedback source */ + mfcpr(CPR0_PLLC, cpr_pllc); + + get_sys_info(&sysinfo); + + plloutb = ((CONFIG_SYS_CLK_FREQ * ((cpr_pllc & PLLC_SRC_MASK) ? + sysinfo.pllFwdDivB : sysinfo.pllFwdDiv) * + sysinfo.pllFbkDiv) / sysinfo.pllFwdDivB); + udiv = 256; /* Assume lowest possible serial clk */ + div = plloutb / (16 * baudrate); /* total divisor */ + umin = (plloutb / get_OPB_freq()) << 1; /* 2 x OPB divisor */ + diff = 256; /* highest possible */ + + /* i is the test udiv value -- start with the largest + * possible (256) to minimize serial clock and constrain + * search to umin. + */ + for (i = 256; i > umin; i--) { + ibdiv = div / i; + est = i * ibdiv; + idiff = (est > div) ? (est-div) : (div-est); + if (idiff == 0) { + udiv = i; + break; /* can't do better */ + } else if (idiff < diff) { + udiv = i; /* best so far */ + diff = idiff; /* update lowest diff*/ + } + } + + *pudiv = udiv; + mfcpr(CPC0_PERD0, reg); + reg &= ~0x0000ffff; + reg |= ((udiv - 0) << 8) | (udiv - 0); + mtcpr(CPC0_PERD0, reg); + *pbdiv = div / udiv; +} +#endif /* defined(CONFIG_440) && !defined(CONFIG_SYS_EXT_SERIAL_CLK) */ + +/* + * Minimal serial functions needed to use one of the SMC ports + * as serial console interface. + */ + +#if defined(CONFIG_440) +int serial_init_dev(unsigned long base) +{ + unsigned long reg; + unsigned long udiv; + unsigned short bdiv; +#ifdef CONFIG_SYS_EXT_SERIAL_CLOCK + unsigned long tmp; +#endif + + MFREG(UART0_SDR, reg); + reg &= ~CR0_MASK; + +#ifdef CONFIG_SYS_EXT_SERIAL_CLOCK + reg |= CR0_EXTCLK_ENA; + udiv = 1; + tmp = gd->baudrate * 16; + bdiv = (CONFIG_SYS_EXT_SERIAL_CLOCK + tmp / 2) / tmp; +#else + /* For 440, the cpu clock is on divider chain A, UART on divider + * chain B ... so cpu clock is irrelevant. Get the "optimized" + * values that are subject to the 1/2 opb clock constraint + */ + serial_divs (gd->baudrate, &udiv, &bdiv); +#endif + + reg |= (udiv - UDIV_SUBTRACT) << CR0_UDIV_POS; /* set the UART divisor */ + + /* + * Configure input clock to baudrate generator for all + * available serial ports here + */ + MTREG(UART0_SDR, reg); +#if defined(UART1_SDR) + MTREG(UART1_SDR, reg); +#endif +#if defined(UART2_SDR) + MTREG(UART2_SDR, reg); +#endif +#if defined(UART3_SDR) + MTREG(UART3_SDR, reg); +#endif + + serial_init_common(base, udiv, bdiv); + + return (0); +} + +#else /* !defined(CONFIG_440) */ + +int serial_init_dev (unsigned long base) +{ + unsigned long reg; + unsigned long tmp; + unsigned long clk; + unsigned long udiv; + unsigned short bdiv; + +#ifdef CONFIG_405EX + clk = tmp = 0; + mfsdr(UART0_SDR, reg); + reg &= ~CR0_MASK; +#ifdef CONFIG_SYS_EXT_SERIAL_CLOCK + reg |= CR0_EXTCLK_ENA; + udiv = 1; + tmp = gd->baudrate * 16; + bdiv = (CONFIG_SYS_EXT_SERIAL_CLOCK + tmp / 2) / tmp; +#else + serial_divs(gd->baudrate, &udiv, &bdiv); +#endif + reg |= (udiv - UDIV_SUBTRACT) << CR0_UDIV_POS; /* set the UART divisor */ + + /* + * Configure input clock to baudrate generator for all + * available serial ports here + */ + mtsdr(UART0_SDR, reg); + +#if defined(UART1_SDR) + mtsdr(UART1_SDR, reg); +#endif + +#elif defined(CONFIG_405EZ) + serial_divs(gd->baudrate, &udiv, &bdiv); + clk = tmp = reg = 0; +#else +#ifdef CONFIG_405EP + reg = mfdcr(CPC0_UCR) & ~(UCR0_MASK | UCR1_MASK); + clk = gd->cpu_clk; + tmp = CONFIG_SYS_BASE_BAUD * 16; + udiv = (clk + tmp / 2) / tmp; + if (udiv > UDIV_MAX) /* max. n bits for udiv */ + udiv = UDIV_MAX; + reg |= (udiv) << UCR0_UDIV_POS; /* set the UART divisor */ + reg |= (udiv) << UCR1_UDIV_POS; /* set the UART divisor */ + mtdcr (CPC0_UCR, reg); +#else /* CONFIG_405EP */ + reg = mfdcr(CPC0_CR0) & ~CR0_MASK; +#ifdef CONFIG_SYS_EXT_SERIAL_CLOCK + clk = CONFIG_SYS_EXT_SERIAL_CLOCK; + udiv = 1; + reg |= CR0_EXTCLK_ENA; +#else + clk = gd->cpu_clk; +#ifdef CONFIG_SYS_405_UART_ERRATA_59 + udiv = 31; /* Errata 59: stuck at 31 */ +#else + tmp = CONFIG_SYS_BASE_BAUD * 16; + udiv = (clk + tmp / 2) / tmp; + if (udiv > UDIV_MAX) /* max. n bits for udiv */ + udiv = UDIV_MAX; +#endif +#endif + reg |= (udiv - 1) << CR0_UDIV_POS; /* set the UART divisor */ + mtdcr (CPC0_CR0, reg); +#endif /* CONFIG_405EP */ + tmp = gd->baudrate * udiv * 16; + bdiv = (clk + tmp / 2) / tmp; +#endif /* CONFIG_405EX */ + + serial_init_common(base, udiv, bdiv); + + return (0); +} + +#endif /* if defined(CONFIG_440) */ + +void serial_setbrg_dev(unsigned long base) +{ + serial_init_dev(base); +} + +void serial_putc_dev(unsigned long base, const char c) +{ + int i; + + if (c == '\n') + serial_putc_dev(base, '\r'); + + /* check THRE bit, wait for transmiter available */ + for (i = 1; i < 3500; i++) { + if ((in_8((u8 *)base + UART_LSR) & 0x20) == 0x20) + break; + udelay (100); + } + + out_8((u8 *)base + UART_THR, c); /* put character out */ +} + +void serial_puts_dev (unsigned long base, const char *s) +{ + while (*s) + serial_putc_dev (base, *s++); +} + +int serial_getc_dev (unsigned long base) +{ + unsigned char status = 0; + + while (1) { +#if defined(CONFIG_HW_WATCHDOG) + WATCHDOG_RESET (); /* Reset HW Watchdog, if needed */ +#endif /* CONFIG_HW_WATCHDOG */ + + status = in_8((u8 *)base + UART_LSR); + if ((status & asyncLSRDataReady1) != 0x0) + break; + + if ((status & ( asyncLSRFramingError1 | + asyncLSROverrunError1 | + asyncLSRParityError1 | + asyncLSRBreakInterrupt1 )) != 0) { + out_8((u8 *)base + UART_LSR, + asyncLSRFramingError1 | + asyncLSROverrunError1 | + asyncLSRParityError1 | + asyncLSRBreakInterrupt1); + } + } + + return (0x000000ff & (int) in_8((u8 *)base)); +} + +int serial_tstc_dev (unsigned long base) +{ + unsigned char status; + + status = in_8((u8 *)base + UART_LSR); + if ((status & asyncLSRDataReady1) != 0x0) + return (1); + + if ((status & ( asyncLSRFramingError1 | + asyncLSROverrunError1 | + asyncLSRParityError1 | + asyncLSRBreakInterrupt1 )) != 0) { + out_8((u8 *)base + UART_LSR, + asyncLSRFramingError1 | + asyncLSROverrunError1 | + asyncLSRParityError1 | + asyncLSRBreakInterrupt1); + } + + return 0; +} + +#ifdef CONFIG_SERIAL_SOFTWARE_FIFO + +void serial_isr (void *arg) +{ + int space; + int c; + const int rx_get = buf_info.rx_get; + int rx_put = buf_info.rx_put; + + if (rx_get <= rx_put) + space = CONFIG_SERIAL_SOFTWARE_FIFO - (rx_put - rx_get); + else + space = rx_get - rx_put; + + while (serial_tstc_dev (ACTING_UART0_BASE)) { + c = serial_getc_dev (ACTING_UART0_BASE); + if (space) { + buf_info.rx_buffer[rx_put++] = c; + space--; + } + if (rx_put == CONFIG_SERIAL_SOFTWARE_FIFO) + rx_put = 0; + if (space < CONFIG_SERIAL_SOFTWARE_FIFO / 4) { + /* Stop flow by setting RTS inactive */ + out_8((u8 *)ACTING_UART0_BASE + UART_MCR, + in_8((u8 *)ACTING_UART0_BASE + UART_MCR) & + (0xFF ^ 0x02)); + } + } + buf_info.rx_put = rx_put; +} + +void serial_buffered_init (void) +{ + serial_puts ("Switching to interrupt driven serial input mode.\n"); + buf_info.rx_buffer = malloc (CONFIG_SERIAL_SOFTWARE_FIFO); + buf_info.rx_put = 0; + buf_info.rx_get = 0; + + if (in_8((u8 *)ACTING_UART0_BASE + UART_MSR) & 0x10) + serial_puts ("Check CTS signal present on serial port: OK.\n"); + else + serial_puts ("WARNING: CTS signal not present on serial port.\n"); + + irq_install_handler ( VECNUM_U0 /*UART0 */ /*int vec */ , + serial_isr /*interrupt_handler_t *handler */ , + (void *) &buf_info /*void *arg */ ); + + /* Enable "RX Data Available" Interrupt on UART */ + out_8(ACTING_UART0_BASE + UART_IER, 0x01); + /* Set DTR active */ + out_8(ACTING_UART0_BASE + UART_MCR, + in_8((u8 *)ACTING_UART0_BASE + UART_MCR) | 0x01); + /* Start flow by setting RTS active */ + out_8(ACTING_UART0_BASE + UART_MCR, + in_8((u8 *)ACTING_UART0_BASE + UART_MCR) | 0x02); + /* Setup UART FIFO: RX trigger level: 4 byte, Enable FIFO */ + out_8(ACTING_UART0_BASE + UART_FCR, (1 << 6) | 1); +} + +void serial_buffered_putc (const char c) +{ + /* Wait for CTS */ +#if defined(CONFIG_HW_WATCHDOG) + while (!(in_8((u8 *)ACTING_UART0_BASE + UART_MSR) & 0x10)) + WATCHDOG_RESET (); +#else + while (!(in_8((u8 *)ACTING_UART0_BASE + UART_MSR) & 0x10)); +#endif + serial_putc (c); +} + +void serial_buffered_puts (const char *s) +{ + serial_puts (s); +} + +int serial_buffered_getc (void) +{ + int space; + int c; + int rx_get = buf_info.rx_get; + int rx_put; + +#if defined(CONFIG_HW_WATCHDOG) + while (rx_get == buf_info.rx_put) + WATCHDOG_RESET (); +#else + while (rx_get == buf_info.rx_put); +#endif + c = buf_info.rx_buffer[rx_get++]; + if (rx_get == CONFIG_SERIAL_SOFTWARE_FIFO) + rx_get = 0; + buf_info.rx_get = rx_get; + + rx_put = buf_info.rx_put; + if (rx_get <= rx_put) + space = CONFIG_SERIAL_SOFTWARE_FIFO - (rx_put - rx_get); + else + space = rx_get - rx_put; + + if (space > CONFIG_SERIAL_SOFTWARE_FIFO / 2) { + /* Start flow by setting RTS active */ + out_8(ACTING_UART0_BASE + UART_MCR, + in_8((u8 *)ACTING_UART0_BASE + UART_MCR) | 0x02); + } + + return c; +} + +int serial_buffered_tstc (void) +{ + return (buf_info.rx_get != buf_info.rx_put) ? 1 : 0; +} + +#endif /* CONFIG_SERIAL_SOFTWARE_FIFO */ + +#if defined(CONFIG_CMD_KGDB) +/* + AS HARNOIS : according to CONFIG_KGDB_SER_INDEX kgdb uses serial port + number 0 or number 1 + - if CONFIG_KGDB_SER_INDEX = 1 => serial port number 0 : + configuration has been already done + - if CONFIG_KGDB_SER_INDEX = 2 => serial port number 1 : + configure port 1 for serial I/O with rate = CONFIG_KGDB_BAUDRATE +*/ +#if (CONFIG_KGDB_SER_INDEX & 2) +void kgdb_serial_init (void) +{ + u8 val; + u16 br_reg; + + get_clocks (); + br_reg = (((((gd->cpu_clk / 16) / 18) * 10) / CONFIG_KGDB_BAUDRATE) + + 5) / 10; + /* + * Init onboard 16550 UART + */ + out_8((u8 *)ACTING_UART1_BASE + UART_LCR, 0x80); /* set DLAB bit */ + out_8((u8 *)ACTING_UART1_BASE + UART_DLL, (br_reg & 0x00ff)); /* set divisor for 9600 baud */ + out_8((u8 *)ACTING_UART1_BASE + UART_DLM, ((br_reg & 0xff00) >> 8)); /* set divisor for 9600 baud */ + out_8((u8 *)ACTING_UART1_BASE + UART_LCR, 0x03); /* line control 8 bits no parity */ + out_8((u8 *)ACTING_UART1_BASE + UART_FCR, 0x00); /* disable FIFO */ + out_8((u8 *)ACTING_UART1_BASE + UART_MCR, 0x00); /* no modem control DTR RTS */ + val = in_8((u8 *)ACTING_UART1_BASE + UART_LSR); /* clear line status */ + val = in_8((u8 *)ACTING_UART1_BASE + UART_RBR); /* read receive buffer */ + out_8((u8 *)ACTING_UART1_BASE + UART_SCR, 0x00); /* set scratchpad */ + out_8((u8 *)ACTING_UART1_BASE + UART_IER, 0x00); /* set interrupt enable reg */ +} + +void putDebugChar (const char c) +{ + if (c == '\n') + serial_putc ('\r'); + + out_8((u8 *)ACTING_UART1_BASE + UART_THR, c); /* put character out */ + + /* check THRE bit, wait for transfer done */ + while ((in_8((u8 *)ACTING_UART1_BASE + UART_LSR) & 0x20) != 0x20); +} + +void putDebugStr (const char *s) +{ + while (*s) + serial_putc (*s++); +} + +int getDebugChar (void) +{ + unsigned char status = 0; + + while (1) { + status = in_8((u8 *)ACTING_UART1_BASE + UART_LSR); + if ((status & asyncLSRDataReady1) != 0x0) + break; + + if ((status & (asyncLSRFramingError1 | + asyncLSROverrunError1 | + asyncLSRParityError1 | + asyncLSRBreakInterrupt1 )) != 0) { + out_8((u8 *)ACTING_UART1_BASE + UART_LSR, + asyncLSRFramingError1 | + asyncLSROverrunError1 | + asyncLSRParityError1 | + asyncLSRBreakInterrupt1); + } + } + + return (0x000000ff & (int) in_8((u8 *)ACTING_UART1_BASE)); +} + +void kgdb_interruptible (int yes) +{ + return; +} + +#else /* ! (CONFIG_KGDB_SER_INDEX & 2) */ + +void kgdb_serial_init (void) +{ + serial_printf ("[on serial] "); +} + +void putDebugChar (int c) +{ + serial_putc (c); +} + +void putDebugStr (const char *str) +{ + serial_puts (str); +} + +int getDebugChar (void) +{ + return serial_getc (); +} + +void kgdb_interruptible (int yes) +{ + return; +} +#endif /* (CONFIG_KGDB_SER_INDEX & 2) */ +#endif + + +#if defined(CONFIG_SERIAL_MULTI) +int serial0_init(void) +{ + return (serial_init_dev(UART0_BASE)); +} + +int serial1_init(void) +{ + return (serial_init_dev(UART1_BASE)); +} + +void serial0_setbrg (void) +{ + serial_setbrg_dev(UART0_BASE); +} + +void serial1_setbrg (void) +{ + serial_setbrg_dev(UART1_BASE); +} + +void serial0_putc(const char c) +{ + serial_putc_dev(UART0_BASE,c); +} + +void serial1_putc(const char c) +{ + serial_putc_dev(UART1_BASE, c); +} + +void serial0_puts(const char *s) +{ + serial_puts_dev(UART0_BASE, s); +} + +void serial1_puts(const char *s) +{ + serial_puts_dev(UART1_BASE, s); +} + +int serial0_getc(void) +{ + return(serial_getc_dev(UART0_BASE)); +} + +int serial1_getc(void) +{ + return(serial_getc_dev(UART1_BASE)); +} + +int serial0_tstc(void) +{ + return (serial_tstc_dev(UART0_BASE)); +} + +int serial1_tstc(void) +{ + return (serial_tstc_dev(UART1_BASE)); +} + +struct serial_device serial0_device = +{ + "serial0", + "UART0", + serial0_init, + serial0_setbrg, + serial0_getc, + serial0_tstc, + serial0_putc, + serial0_puts, +}; + +struct serial_device serial1_device = +{ + "serial1", + "UART1", + serial1_init, + serial1_setbrg, + serial1_getc, + serial1_tstc, + serial1_putc, + serial1_puts, +}; +#else +/* + * Wrapper functions + */ +int serial_init(void) +{ + return serial_init_dev(ACTING_UART0_BASE); +} + +void serial_setbrg(void) +{ + serial_setbrg_dev(ACTING_UART0_BASE); +} + +void serial_putc(const char c) +{ + serial_putc_dev(ACTING_UART0_BASE, c); +} + +void serial_puts(const char *s) +{ + serial_puts_dev(ACTING_UART0_BASE, s); +} + +int serial_getc(void) +{ + return serial_getc_dev(ACTING_UART0_BASE); +} + +int serial_tstc(void) +{ + return serial_tstc_dev(ACTING_UART0_BASE); +} +#endif /* CONFIG_SERIAL_MULTI */ + +#endif /* CONFIG_405GP || CONFIG_405CR */ diff --git a/arch/powerpc/cpu/ppc4xx/Makefile b/arch/powerpc/cpu/ppc4xx/Makefile new file mode 100644 index 0000000..88d53fb --- /dev/null +++ b/arch/powerpc/cpu/ppc4xx/Makefile @@ -0,0 +1,92 @@ +# +# (C) Copyright 2000-2006 +# Wolfgang Denk, DENX Software Engineering, wd@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 $(TOPDIR)/config.mk + +LIB = $(obj)lib$(CPU).a + +START := resetvec.o +START += start.o + +SOBJS := cache.o +SOBJS += dcr.o +SOBJS += kgdb.o + +COBJS := 40x_spd_sdram.o + +COBJS += 44x_spd_ddr.o +COBJS += 44x_spd_ddr2.o +ifdef CONFIG_PPC4xx_DDR_AUTOCALIBRATION +COBJS += 4xx_ibm_ddr2_autocalib.o +endif +COBJS += 4xx_pci.o +COBJS += 4xx_pcie.o +COBJS += bedbug_405.o +ifdef CONFIG_CMD_CHIP_CONFIG +COBJS += cmd_chip_config.o +endif +COBJS += commproc.o +COBJS += cpu.o +COBJS += cpu_init.o +COBJS += denali_data_eye.o +COBJS += denali_spd_ddr2.o +COBJS += ecc.o +COBJS += fdt.o +COBJS += interrupts.o +COBJS += iop480_uart.o +ifdef CONFIG_CMD_REGINFO +COBJS += reginfo.o +endif +COBJS += sdram.o +COBJS += speed.o +COBJS += tlb.o +COBJS += traps.o +COBJS += usb.o +COBJS += usb_ohci.o +COBJS += usbdev.o +ifndef CONFIG_XILINX_440 +COBJS += 4xx_uart.o +COBJS += gpio.o +COBJS += miiphy.o +COBJS += uic.o +else +COBJS += xilinx_irq.o +endif + +SRCS := $(START:.o=.S) $(SOBJS:.o=.S) $(COBJS:.o=.c) +OBJS := $(addprefix $(obj),$(SOBJS) $(COBJS)) +START := $(addprefix $(obj),$(START)) + +all: $(obj).depend $(START) $(LIB) + +$(LIB): $(OBJS) + $(AR) $(ARFLAGS) $@ $(OBJS) + +######################################################################### + +# defines $(obj).depend target +include $(SRCTREE)/rules.mk + +sinclude $(obj).depend + +######################################################################### diff --git a/arch/powerpc/cpu/ppc4xx/bedbug_405.c b/arch/powerpc/cpu/ppc4xx/bedbug_405.c new file mode 100644 index 0000000..ef11cb6 --- /dev/null +++ b/arch/powerpc/cpu/ppc4xx/bedbug_405.c @@ -0,0 +1,308 @@ +/* + * Bedbug Functions specific to the PPC405 chip + */ + +#include <common.h> +#include <command.h> +#include <linux/ctype.h> +#include <bedbug/type.h> +#include <bedbug/bedbug.h> +#include <bedbug/regs.h> +#include <bedbug/ppc.h> + +#if defined(CONFIG_CMD_BEDBUG) && defined(CONFIG_4xx) + +#define MAX_BREAK_POINTS 4 + +extern CPU_DEBUG_CTX bug_ctx; + +void bedbug405_init __P ((void)); +void bedbug405_do_break __P ((cmd_tbl_t *, int, int, char *[])); +void bedbug405_break_isr __P ((struct pt_regs *)); +int bedbug405_find_empty __P ((void)); +int bedbug405_set __P ((int, unsigned long)); +int bedbug405_clear __P ((int)); + + +/* ====================================================================== + * Initialize the global bug_ctx structure for the AMCC PPC405. Clear all + * of the breakpoints. + * ====================================================================== */ + +void bedbug405_init (void) +{ + int i; + + /* -------------------------------------------------- */ + + bug_ctx.hw_debug_enabled = 0; + bug_ctx.stopped = 0; + bug_ctx.current_bp = 0; + bug_ctx.regs = NULL; + + bug_ctx.do_break = bedbug405_do_break; + bug_ctx.break_isr = bedbug405_break_isr; + bug_ctx.find_empty = bedbug405_find_empty; + bug_ctx.set = bedbug405_set; + bug_ctx.clear = bedbug405_clear; + + for (i = 1; i <= MAX_BREAK_POINTS; ++i) + (*bug_ctx.clear) (i); + + puts ("BEDBUG:ready\n"); + return; +} /* bedbug_init_breakpoints */ + + + +/* ====================================================================== + * Set/clear/show one of the hardware breakpoints for the 405. The "off" + * string will disable a specific breakpoint. The "show" string will + * display the current breakpoints. Otherwise an address will set a + * breakpoint at that address. Setting a breakpoint uses the CPU-specific + * set routine which will assign a breakpoint number. + * ====================================================================== */ + +void bedbug405_do_break (cmd_tbl_t * cmdtp, int flag, int argc, char *argv[]) +{ + long addr = 0; /* Address to break at */ + int which_bp; /* Breakpoint number */ + + /* -------------------------------------------------- */ + + if (argc < 2) { + cmd_usage(cmdtp); + return; + } + + /* Turn off a breakpoint */ + + if (strcmp (argv[1], "off") == 0) { + if (bug_ctx.hw_debug_enabled == 0) { + printf ("No breakpoints enabled\n"); + return; + } + + which_bp = simple_strtoul (argv[2], NULL, 10); + + if (bug_ctx.clear) + (*bug_ctx.clear) (which_bp); + + printf ("Breakpoint %d removed\n", which_bp); + return; + } + + /* Show a list of breakpoints */ + + if (strcmp (argv[1], "show") == 0) { + for (which_bp = 1; which_bp <= MAX_BREAK_POINTS; ++which_bp) { + + switch (which_bp) { + case 1: + addr = GET_IAC1 (); + break; + case 2: + addr = GET_IAC2 (); + break; + case 3: + addr = GET_IAC3 (); + break; + case 4: + addr = GET_IAC4 (); + break; + } + + printf ("Breakpoint [%d]: ", which_bp); + if (addr == 0) + printf ("NOT SET\n"); + else + disppc ((unsigned char *) addr, 0, 1, bedbug_puts, + F_RADHEX); + } + return; + } + + /* Set a breakpoint at the address */ + + if (!isdigit (argv[1][0])) { + cmd_usage(cmdtp); + return; + } + + addr = simple_strtoul (argv[1], NULL, 16) & 0xfffffffc; + + if ((bug_ctx.set) && (which_bp = (*bug_ctx.set) (0, addr)) > 0) { + printf ("Breakpoint [%d]: ", which_bp); + disppc ((unsigned char *) addr, 0, 1, bedbug_puts, F_RADHEX); + } + + return; +} /* bedbug405_do_break */ + + + +/* ====================================================================== + * Handle a breakpoint. First determine which breakpoint was hit by + * looking at the DeBug Status Register (DBSR), clear the breakpoint + * and enter a mini main loop. Stay in the loop until the stopped flag + * in the debug context is cleared. + * ====================================================================== */ + +void bedbug405_break_isr (struct pt_regs *regs) +{ + unsigned long dbsr_val; /* Value of the DBSR */ + unsigned long addr = 0; /* Address stopped at */ + + /* -------------------------------------------------- */ + + dbsr_val = GET_DBSR (); + + if (dbsr_val & DBSR_IA1) { + bug_ctx.current_bp = 1; + addr = GET_IAC1 (); + SET_DBSR (DBSR_IA1); /* Write a 1 to clear */ + } else if (dbsr_val & DBSR_IA2) { + bug_ctx.current_bp = 2; + addr = GET_IAC2 (); + SET_DBSR (DBSR_IA2); /* Write a 1 to clear */ + } else if (dbsr_val & DBSR_IA3) { + bug_ctx.current_bp = 3; + addr = GET_IAC3 (); + SET_DBSR (DBSR_IA3); /* Write a 1 to clear */ + } else if (dbsr_val & DBSR_IA4) { + bug_ctx.current_bp = 4; + addr = GET_IAC4 (); + SET_DBSR (DBSR_IA4); /* Write a 1 to clear */ + } + + bedbug_main_loop (addr, regs); + return; +} /* bedbug405_break_isr */ + + + +/* ====================================================================== + * Look through all of the hardware breakpoints available to see if one + * is unused. + * ====================================================================== */ + +int bedbug405_find_empty (void) +{ + /* -------------------------------------------------- */ + + if (GET_IAC1 () == 0) + return 1; + + if (GET_IAC2 () == 0) + return 2; + + if (GET_IAC3 () == 0) + return 3; + + if (GET_IAC4 () == 0) + return 4; + + return 0; +} /* bedbug405_find_empty */ + + + +/* ====================================================================== + * Set a breakpoint. If 'which_bp' is zero then find an unused breakpoint + * number, otherwise reassign the given breakpoint. If hardware debugging + * is not enabled, then turn it on via the MSR and DBCR0. Set the break + * address in the appropriate IACx register and enable proper address + * beakpoint in DBCR0. + * ====================================================================== */ + +int bedbug405_set (int which_bp, unsigned long addr) +{ + /* -------------------------------------------------- */ + + /* Only look if which_bp == 0, else use which_bp */ + if ((bug_ctx.find_empty) && (!which_bp) && + (which_bp = (*bug_ctx.find_empty) ()) == 0) { + printf ("All breakpoints in use\n"); + return 0; + } + + if (which_bp < 1 || which_bp > MAX_BREAK_POINTS) { + printf ("Invalid break point # %d\n", which_bp); + return 0; + } + + if (!bug_ctx.hw_debug_enabled) { + SET_MSR (GET_MSR () | 0x200); /* set MSR[ DE ] */ + SET_DBCR0 (GET_DBCR0 () | DBCR0_IDM); + bug_ctx.hw_debug_enabled = 1; + } + + switch (which_bp) { + case 1: + SET_IAC1 (addr); + SET_DBCR0 (GET_DBCR0 () | DBCR0_IA1); + break; + + case 2: + SET_IAC2 (addr); + SET_DBCR0 (GET_DBCR0 () | DBCR0_IA2); + break; + + case 3: + SET_IAC3 (addr); + SET_DBCR0 (GET_DBCR0 () | DBCR0_IA3); + break; + + case 4: + SET_IAC4 (addr); + SET_DBCR0 (GET_DBCR0 () | DBCR0_IA4); + break; + } + + return which_bp; +} /* bedbug405_set */ + + + +/* ====================================================================== + * Disable a specific breakoint by setting the appropriate IACx register + * to zero and claring the instruction address breakpoint in DBCR0. + * ====================================================================== */ + +int bedbug405_clear (int which_bp) +{ + /* -------------------------------------------------- */ + + if (which_bp < 1 || which_bp > MAX_BREAK_POINTS) { + printf ("Invalid break point # (%d)\n", which_bp); + return -1; + } + + switch (which_bp) { + case 1: + SET_IAC1 (0); + SET_DBCR0 (GET_DBCR0 () & ~DBCR0_IA1); + break; + + case 2: + SET_IAC2 (0); + SET_DBCR0 (GET_DBCR0 () & ~DBCR0_IA2); + break; + + case 3: + SET_IAC3 (0); + SET_DBCR0 (GET_DBCR0 () & ~DBCR0_IA3); + break; + + case 4: + SET_IAC4 (0); + SET_DBCR0 (GET_DBCR0 () & ~DBCR0_IA4); + break; + } + + return 0; +} /* bedbug405_clear */ + + +/* ====================================================================== */ +#endif diff --git a/arch/powerpc/cpu/ppc4xx/cache.S b/arch/powerpc/cpu/ppc4xx/cache.S new file mode 100644 index 0000000..269716f --- /dev/null +++ b/arch/powerpc/cpu/ppc4xx/cache.S @@ -0,0 +1,235 @@ +/* + * This file contains miscellaneous low-level functions. + * Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org) + * + * Largely rewritten by Cort Dougan (cort@cs.nmt.edu) + * and Paul Mackerras. + * + * 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. + * + */ + +#include <config.h> +#include <config.h> +#include <ppc4xx.h> +#include <ppc_asm.tmpl> +#include <ppc_defs.h> +#include <asm/cache.h> +#include <asm/mmu.h> + +/* + * Flush instruction cache. + */ +_GLOBAL(invalidate_icache) + iccci r0,r0 + isync + blr + +/* + * Write any modified data cache blocks out to memory + * and invalidate the corresponding instruction cache blocks. + * + * flush_icache_range(unsigned long start, unsigned long stop) + */ +_GLOBAL(flush_icache_range) + li r5,L1_CACHE_BYTES-1 + andc r3,r3,r5 + subf r4,r3,r4 + add r4,r4,r5 + srwi. r4,r4,L1_CACHE_SHIFT + beqlr + mtctr r4 + mr r6,r3 +1: dcbst 0,r3 + addi r3,r3,L1_CACHE_BYTES + bdnz 1b + sync /* wait for dcbst's to get to ram */ + mtctr r4 +2: icbi 0,r6 + addi r6,r6,L1_CACHE_BYTES + bdnz 2b + sync /* additional sync needed on g4 */ + isync + blr + +/* + * Write any modified data cache blocks out to memory. + * Does not invalidate the corresponding cache lines (especially for + * any corresponding instruction cache). + * + * clean_dcache_range(unsigned long start, unsigned long stop) + */ +_GLOBAL(clean_dcache_range) + li r5,L1_CACHE_BYTES-1 + andc r3,r3,r5 + subf r4,r3,r4 + add r4,r4,r5 + srwi. r4,r4,L1_CACHE_SHIFT + beqlr + mtctr r4 + +1: dcbst 0,r3 + addi r3,r3,L1_CACHE_BYTES + bdnz 1b + sync /* wait for dcbst's to get to ram */ + blr + +/* + * Write any modified data cache blocks out to memory and invalidate them. + * Does not invalidate the corresponding instruction cache blocks. + * + * flush_dcache_range(unsigned long start, unsigned long stop) + */ +_GLOBAL(flush_dcache_range) + li r5,L1_CACHE_BYTES-1 + andc r3,r3,r5 + subf r4,r3,r4 + add r4,r4,r5 + srwi. r4,r4,L1_CACHE_SHIFT + beqlr + mtctr r4 + +1: dcbf 0,r3 + addi r3,r3,L1_CACHE_BYTES + bdnz 1b + sync /* wait for dcbst's to get to ram */ + blr + +/* + * Like above, but invalidate the D-cache. This is used by the 8xx + * to invalidate the cache so the PPC core doesn't get stale data + * from the CPM (no cache snooping here :-). + * + * invalidate_dcache_range(unsigned long start, unsigned long stop) + */ +_GLOBAL(invalidate_dcache_range) + li r5,L1_CACHE_BYTES-1 + andc r3,r3,r5 + subf r4,r3,r4 + add r4,r4,r5 + srwi. r4,r4,L1_CACHE_SHIFT + beqlr + mtctr r4 + +1: dcbi 0,r3 + addi r3,r3,L1_CACHE_BYTES + bdnz 1b + sync /* wait for dcbi's to get to ram */ + blr + +/* + * 40x cores have 8K or 16K dcache and 32 byte line size. + * 44x has a 32K dcache and 32 byte line size. + * 8xx has 1, 2, 4, 8K variants. + * For now, cover the worst case of the 44x. + * Must be called with external interrupts disabled. + */ +#define CACHE_NWAYS 64 +#define CACHE_NLINES 32 + +_GLOBAL(flush_dcache) + li r4,(2 * CACHE_NWAYS * CACHE_NLINES) + mtctr r4 + lis r5,0 +1: lwz r3,0(r5) /* Load one word from every line */ + addi r5,r5,L1_CACHE_BYTES + bdnz 1b + sync + blr + +_GLOBAL(invalidate_dcache) + addi r6,0,0x0000 /* clear GPR 6 */ + /* Do loop for # of dcache congruence classes. */ + lis r7,(CONFIG_SYS_DCACHE_SIZE / L1_CACHE_BYTES / 2)@ha /* TBS for large sized cache */ + ori r7,r7,(CONFIG_SYS_DCACHE_SIZE / L1_CACHE_BYTES / 2)@l + /* NOTE: dccci invalidates both */ + mtctr r7 /* ways in the D cache */ +..dcloop: + dccci 0,r6 /* invalidate line */ + addi r6,r6,L1_CACHE_BYTES /* bump to next line */ + bdnz ..dcloop + sync + blr + +/* + * Cache functions. + * + * NOTE: currently the 440s run with dcache _disabled_ once relocated to DRAM, + * although for some cache-ralated calls stubs have to be provided to satisfy + * symbols resolution. + * Icache-related functions are used in POST framework. + * + */ +#ifdef CONFIG_440 + + .globl dcache_disable + .globl dcache_enable + .globl icache_disable + .globl icache_enable +dcache_disable: +dcache_enable: +icache_disable: +icache_enable: + blr + + .globl dcache_status + .globl icache_status +dcache_status: +icache_status: + mr r3, 0 + blr + +#else /* CONFIG_440 */ + + .globl icache_enable +icache_enable: + mflr r8 + bl invalidate_icache + mtlr r8 + isync + addis r3,r0, 0xc000 /* set bit 0 */ + mticcr r3 + blr + + .globl icache_disable +icache_disable: + addis r3,r0, 0x0000 /* clear bit 0 */ + mticcr r3 + isync + blr + + .globl icache_status +icache_status: + mficcr r3 + srwi r3, r3, 31 /* >>31 => select bit 0 */ + blr + + .globl dcache_enable +dcache_enable: + mflr r8 + bl invalidate_dcache + mtlr r8 + isync + addis r3,r0, 0x8000 /* set bit 0 */ + mtdccr r3 + blr + + .globl dcache_disable +dcache_disable: + mflr r8 + bl flush_dcache + mtlr r8 + addis r3,r0, 0x0000 /* clear bit 0 */ + mtdccr r3 + blr + + .globl dcache_status +dcache_status: + mfdccr r3 + srwi r3, r3, 31 /* >>31 => select bit 0 */ + blr + +#endif /* CONFIG_440 */ diff --git a/arch/powerpc/cpu/ppc4xx/cmd_chip_config.c b/arch/powerpc/cpu/ppc4xx/cmd_chip_config.c new file mode 100644 index 0000000..ba57211 --- /dev/null +++ b/arch/powerpc/cpu/ppc4xx/cmd_chip_config.c @@ -0,0 +1,148 @@ +/* + * (C) Copyright 2008-2009 + * Stefan Roese, DENX Software Engineering, sr@denx.de. + * + * (C) Copyright 2009 + * Dirk Eibach, Guntermann & Drunck GmbH, eibach@gdsys.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> +#include <command.h> +#include <i2c.h> +#include <asm/ppc4xx_config.h> +#include <asm/io.h> + +static void print_configs(int cur_config_nr) +{ + int i; + + for (i = 0; i < ppc4xx_config_count; i++) { + printf("%-16s - %s", ppc4xx_config_val[i].label, + ppc4xx_config_val[i].description); + if (i == cur_config_nr) + printf(" ***"); + printf("\n"); + } + +} + +static int do_chip_config(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]) +{ + int i; + int ret; + int cur_config_nr = -1; + u8 cur_config[CONFIG_4xx_CONFIG_BLOCKSIZE]; + + /* + * First switch to correct I2C bus. This is I2C bus 0 + * for all currently available 4xx derivats. + */ + I2C_SET_BUS(0); + +#ifdef CONFIG_CMD_EEPROM + ret = eeprom_read(CONFIG_4xx_CONFIG_I2C_EEPROM_ADDR, + CONFIG_4xx_CONFIG_I2C_EEPROM_OFFSET, + cur_config, CONFIG_4xx_CONFIG_BLOCKSIZE); +#else + ret = i2c_read(CONFIG_4xx_CONFIG_I2C_EEPROM_ADDR, + CONFIG_4xx_CONFIG_I2C_EEPROM_OFFSET, + 1, cur_config, CONFIG_4xx_CONFIG_BLOCKSIZE); +#endif + if (ret) { + printf("Error reading EEPROM at addr 0x%x\n", + CONFIG_4xx_CONFIG_I2C_EEPROM_ADDR); + return -1; + } + + /* + * Search the current configuration + */ + for (i = 0; i < ppc4xx_config_count; i++) { + if (memcmp(cur_config, ppc4xx_config_val[i].val, + CONFIG_4xx_CONFIG_BLOCKSIZE) == 0) + cur_config_nr = i; + } + + if (cur_config_nr == -1) { + printf("Warning: The I2C bootstrap values don't match any" + " of the available options!\n"); + printf("I2C bootstrap EEPROM values are (I2C address 0x%02x):\n", + CONFIG_4xx_CONFIG_I2C_EEPROM_ADDR); + for (i = 0; i < CONFIG_4xx_CONFIG_BLOCKSIZE; i++) { + printf("%02x ", cur_config[i]); + } + printf("\n"); + } + + if (argc < 2) { + printf("Available configurations (I2C address 0x%02x):\n", + CONFIG_4xx_CONFIG_I2C_EEPROM_ADDR); + print_configs(cur_config_nr); + return 0; + } + + for (i = 0; i < ppc4xx_config_count; i++) { + /* + * Search for configuration name/label + */ + if (strcmp(argv[1], ppc4xx_config_val[i].label) == 0) { + printf("Using configuration:\n%-16s - %s\n", + ppc4xx_config_val[i].label, + ppc4xx_config_val[i].description); + +#ifdef CONFIG_CMD_EEPROM + ret = eeprom_write(CONFIG_4xx_CONFIG_I2C_EEPROM_ADDR, + CONFIG_4xx_CONFIG_I2C_EEPROM_OFFSET, + ppc4xx_config_val[i].val, + CONFIG_4xx_CONFIG_BLOCKSIZE); +#else + ret = i2c_write(CONFIG_4xx_CONFIG_I2C_EEPROM_ADDR, + CONFIG_4xx_CONFIG_I2C_EEPROM_OFFSET, + 1, ppc4xx_config_val[i].val, + CONFIG_4xx_CONFIG_BLOCKSIZE); +#endif + udelay(CONFIG_SYS_EEPROM_PAGE_WRITE_DELAY_MS * 1000); + if (ret) { + printf("Error updating EEPROM at addr 0x%x\n", + CONFIG_4xx_CONFIG_I2C_EEPROM_ADDR); + return -1; + } + + printf("done (dump via 'i2c md %x 0.1 %x')\n", + CONFIG_4xx_CONFIG_I2C_EEPROM_ADDR, + CONFIG_4xx_CONFIG_BLOCKSIZE); + printf("Reset the board for the changes to" + " take effect\n"); + return 0; + } + } + + printf("Configuration %s not found!\n", argv[1]); + print_configs(cur_config_nr); + return -1; +} + +U_BOOT_CMD( + chip_config, 2, 0, do_chip_config, + "program the I2C bootstrap EEPROM", + "[config-label]" +); diff --git a/arch/powerpc/cpu/ppc4xx/commproc.c b/arch/powerpc/cpu/ppc4xx/commproc.c new file mode 100644 index 0000000..a1696d3 --- /dev/null +++ b/arch/powerpc/cpu/ppc4xx/commproc.c @@ -0,0 +1,77 @@ +/* + * (C) Copyright 2000-2004 + * Wolfgang Denk, DENX Software Engineering, wd@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 + * + * + * Atapted for ppc4XX by Denis Peter + */ + +#include <common.h> +#include <commproc.h> +#include <asm/io.h> + +#if defined(CONFIG_POST) || defined(CONFIG_LOGBUFFER) + +#if defined(CONFIG_SYS_POST_WORD_ADDR) +# define _POST_ADDR ((CONFIG_SYS_OCM_DATA_ADDR) + (CONFIG_SYS_POST_WORD_ADDR)) +#elif defined(CONFIG_SYS_POST_ALT_WORD_ADDR) +# define _POST_ADDR (CONFIG_SYS_POST_ALT_WORD_ADDR) +#endif + +void post_word_store (ulong a) +{ + volatile void *save_addr = (volatile void *)(_POST_ADDR); + + out_be32(save_addr, a); +} + +ulong post_word_load (void) +{ + volatile void *save_addr = (volatile void *)(_POST_ADDR); + + return in_be32(save_addr); +} + +#endif /* CONFIG_POST || CONFIG_LOGBUFFER*/ + +#ifdef CONFIG_BOOTCOUNT_LIMIT + +void bootcount_store (ulong a) +{ + volatile ulong *save_addr = + (volatile ulong *)(CONFIG_SYS_OCM_DATA_ADDR + CONFIG_SYS_BOOTCOUNT_ADDR); + + save_addr[0] = a; + save_addr[1] = BOOTCOUNT_MAGIC; +} + +ulong bootcount_load (void) +{ + volatile ulong *save_addr = + (volatile ulong *)(CONFIG_SYS_OCM_DATA_ADDR + CONFIG_SYS_BOOTCOUNT_ADDR); + + if (save_addr[1] != BOOTCOUNT_MAGIC) + return 0; + else + return save_addr[0]; +} + +#endif /* CONFIG_BOOTCOUNT_LIMIT */ diff --git a/arch/powerpc/cpu/ppc4xx/config.mk b/arch/powerpc/cpu/ppc4xx/config.mk new file mode 100644 index 0000000..5bda710 --- /dev/null +++ b/arch/powerpc/cpu/ppc4xx/config.mk @@ -0,0 +1,37 @@ +# +# (C) Copyright 2000 +# Wolfgang Denk, DENX Software Engineering, wd@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 +# + +PLATFORM_RELFLAGS += -fPIC -meabi +PLATFORM_CPPFLAGS += -DCONFIG_4xx -ffixed-r2 -mstring -msoft-float + +cfg=$(shell grep configs $(OBJTREE)/include/config.h | sed 's/.*<\(configs.*\)>/\1/') +is440:=$(shell grep CONFIG_440 $(TOPDIR)/include/$(cfg)) + +ifneq (,$(findstring CONFIG_440,$(is440))) +PLATFORM_CPPFLAGS += -Wa,-m440 -mcpu=440 +else +PLATFORM_CPPFLAGS += -Wa,-m405 -mcpu=405 +endif + +# Use default linker script. Board port can override in board/*/config.mk +LDSCRIPT := $(SRCTREE)/arch/powerpc/cpu/ppc4xx/u-boot.lds diff --git a/arch/powerpc/cpu/ppc4xx/cpu.c b/arch/powerpc/cpu/ppc4xx/cpu.c new file mode 100644 index 0000000..e1a023b --- /dev/null +++ b/arch/powerpc/cpu/ppc4xx/cpu.c @@ -0,0 +1,754 @@ +/* + * (C) Copyright 2000-2007 + * Wolfgang Denk, DENX Software Engineering, wd@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 + */ + +/* + * CPU specific code + * + * written or collected and sometimes rewritten by + * Magnus Damm <damm@bitsmart.com> + * + * minor modifications by + * Wolfgang Denk <wd@denx.de> + */ + +#include <common.h> +#include <watchdog.h> +#include <command.h> +#include <asm/cache.h> +#include <ppc4xx.h> +#include <netdev.h> + +DECLARE_GLOBAL_DATA_PTR; + +void board_reset(void); + +/* + * To provide an interface to detect CPU number for boards that support + * more then one CPU, we implement the "weak" default functions here. + * + * Returns CPU number + */ +int __get_cpu_num(void) +{ + return NA_OR_UNKNOWN_CPU; +} +int get_cpu_num(void) __attribute__((weak, alias("__get_cpu_num"))); + +#if defined(CONFIG_PCI) +#if defined(CONFIG_405GP) || \ + defined(CONFIG_440EP) || defined(CONFIG_440GR) || \ + defined(CONFIG_440EPX) || defined(CONFIG_440GRX) + +#define PCI_ASYNC + +static int pci_async_enabled(void) +{ +#if defined(CONFIG_405GP) + return (mfdcr(CPC0_PSR) & PSR_PCI_ASYNC_EN); +#endif + +#if defined(CONFIG_440EP) || defined(CONFIG_440GR) || \ + defined(CONFIG_440EPX) || defined(CONFIG_440GRX) || \ + defined(CONFIG_460EX) || defined(CONFIG_460GT) + unsigned long val; + + mfsdr(SDR0_SDSTP1, val); + return (val & SDR0_SDSTP1_PAME_MASK); +#endif +} +#endif +#endif /* CONFIG_PCI */ + +#if defined(CONFIG_PCI) && !defined(CONFIG_IOP480) && \ + !defined(CONFIG_405) && !defined(CONFIG_405EX) +int pci_arbiter_enabled(void) +{ +#if defined(CONFIG_405GP) + return (mfdcr(CPC0_PSR) & PSR_PCI_ARBIT_EN); +#endif + +#if defined(CONFIG_405EP) + return (mfdcr(CPC0_PCI) & CPC0_PCI_ARBIT_EN); +#endif + +#if defined(CONFIG_440GP) + return (mfdcr(CPC0_STRP1) & CPC0_STRP1_PAE_MASK); +#endif + +#if defined(CONFIG_440GX) || defined(CONFIG_440SP) || defined(CONFIG_440SPE) + unsigned long val; + + mfsdr(SDR0_XCR, val); + return (val & 0x80000000); +#endif +#if defined(CONFIG_440EP) || defined(CONFIG_440GR) || \ + defined(CONFIG_440EPX) || defined(CONFIG_440GRX) || \ + defined(CONFIG_460EX) || defined(CONFIG_460GT) + unsigned long val; + + mfsdr(SDR0_PCI0, val); + return (val & 0x80000000); +#endif +} +#endif + +#if defined(CONFIG_405EP) +#define I2C_BOOTROM + +static int i2c_bootrom_enabled(void) +{ +#if defined(CONFIG_405EP) + return (mfdcr(CPC0_BOOT) & CPC0_BOOT_SEP); +#else + unsigned long val; + + mfsdr(SDR0_SDCS0, val); + return (val & SDR0_SDCS_SDD); +#endif +} +#endif + +#if defined(CONFIG_440GX) +#define SDR0_PINSTP_SHIFT 29 +static char *bootstrap_str[] = { + "EBC (16 bits)", + "EBC (8 bits)", + "EBC (32 bits)", + "EBC (8 bits)", + "PCI", + "I2C (Addr 0x54)", + "Reserved", + "I2C (Addr 0x50)", +}; +static char bootstrap_char[] = { 'A', 'B', 'C', 'B', 'D', 'E', 'x', 'F' }; +#endif + +#if defined(CONFIG_440SP) || defined(CONFIG_440SPE) +#define SDR0_PINSTP_SHIFT 30 +static char *bootstrap_str[] = { + "EBC (8 bits)", + "PCI", + "I2C (Addr 0x54)", + "I2C (Addr 0x50)", +}; +static char bootstrap_char[] = { 'A', 'B', 'C', 'D'}; +#endif + +#if defined(CONFIG_440EP) || defined(CONFIG_440GR) +#define SDR0_PINSTP_SHIFT 29 +static char *bootstrap_str[] = { + "EBC (8 bits)", + "PCI", + "NAND (8 bits)", + "EBC (16 bits)", + "EBC (16 bits)", + "I2C (Addr 0x54)", + "PCI", + "I2C (Addr 0x52)", +}; +static char bootstrap_char[] = { 'A', 'B', 'C', 'D', 'E', 'G', 'F', 'H' }; +#endif + +#if defined(CONFIG_440EPX) || defined(CONFIG_440GRX) +#define SDR0_PINSTP_SHIFT 29 +static char *bootstrap_str[] = { + "EBC (8 bits)", + "EBC (16 bits)", + "EBC (16 bits)", + "NAND (8 bits)", + "PCI", + "I2C (Addr 0x54)", + "PCI", + "I2C (Addr 0x52)", +}; +static char bootstrap_char[] = { 'A', 'B', 'C', 'D', 'E', 'G', 'F', 'H' }; +#endif + +#if defined(CONFIG_460EX) || defined(CONFIG_460GT) +#define SDR0_PINSTP_SHIFT 29 +static char *bootstrap_str[] = { + "EBC (8 bits)", + "EBC (16 bits)", + "PCI", + "PCI", + "EBC (16 bits)", + "NAND (8 bits)", + "I2C (Addr 0x54)", /* A8 */ + "I2C (Addr 0x52)", /* A4 */ +}; +static char bootstrap_char[] = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H' }; +#endif + +#if defined(CONFIG_460SX) +#define SDR0_PINSTP_SHIFT 29 +static char *bootstrap_str[] = { + "EBC (8 bits)", + "EBC (16 bits)", + "EBC (32 bits)", + "NAND (8 bits)", + "I2C (Addr 0x54)", /* A8 */ + "I2C (Addr 0x52)", /* A4 */ +}; +static char bootstrap_char[] = { 'A', 'B', 'C', 'D', 'E', 'G' }; +#endif + +#if defined(CONFIG_405EZ) +#define SDR0_PINSTP_SHIFT 28 +static char *bootstrap_str[] = { + "EBC (8 bits)", + "SPI (fast)", + "NAND (512 page, 4 addr cycle)", + "I2C (Addr 0x50)", + "EBC (32 bits)", + "I2C (Addr 0x50)", + "NAND (2K page, 5 addr cycle)", + "I2C (Addr 0x50)", + "EBC (16 bits)", + "Reserved", + "NAND (2K page, 4 addr cycle)", + "I2C (Addr 0x50)", + "NAND (512 page, 3 addr cycle)", + "I2C (Addr 0x50)", + "SPI (slow)", + "I2C (Addr 0x50)", +}; +static char bootstrap_char[] = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', \ + 'I', 'x', 'K', 'L', 'M', 'N', 'O', 'P' }; +#endif + +#if defined(CONFIG_405EX) +#define SDR0_PINSTP_SHIFT 29 +static char *bootstrap_str[] = { + "EBC (8 bits)", + "EBC (16 bits)", + "EBC (16 bits)", + "NAND (8 bits)", + "NAND (8 bits)", + "I2C (Addr 0x54)", + "EBC (8 bits)", + "I2C (Addr 0x52)", +}; +static char bootstrap_char[] = { 'A', 'B', 'C', 'D', 'E', 'G', 'F', 'H' }; +#endif + +#if defined(SDR0_PINSTP_SHIFT) +static int bootstrap_option(void) +{ + unsigned long val; + + mfsdr(SDR0_PINSTP, val); + return ((val & 0xf0000000) >> SDR0_PINSTP_SHIFT); +} +#endif /* SDR0_PINSTP_SHIFT */ + + +#if defined(CONFIG_440) +static int do_chip_reset (unsigned long sys0, unsigned long sys1) +{ + /* Changes to CPC0_SYS0 and CPC0_SYS1 require chip + * reset. + */ + mtdcr (CPC0_CR0, mfdcr (CPC0_CR0) | 0x80000000); /* Set SWE */ + mtdcr (CPC0_SYS0, sys0); + mtdcr (CPC0_SYS1, sys1); + mtdcr (CPC0_CR0, mfdcr (CPC0_CR0) & ~0x80000000); /* Clr SWE */ + mtspr (SPRN_DBCR0, 0x20000000); /* Reset the chip */ + + return 1; +} +#endif + + +int checkcpu (void) +{ +#if !defined(CONFIG_405) /* not used on Xilinx 405 FPGA implementations */ + uint pvr = get_pvr(); + ulong clock = gd->cpu_clk; + char buf[32]; +#if defined(CONFIG_460EX) || defined(CONFIG_460GT) + u32 reg; +#endif + +#if !defined(CONFIG_IOP480) + char addstr[64] = ""; + sys_info_t sys_info; + int cpu_num; + + cpu_num = get_cpu_num(); + if (cpu_num >= 0) + printf("CPU%d: ", cpu_num); + else + puts("CPU: "); + + get_sys_info(&sys_info); + +#if defined(CONFIG_XILINX_440) + puts("IBM PowerPC 4"); +#else + puts("AMCC PowerPC 4"); +#endif + +#if defined(CONFIG_405GP) || defined(CONFIG_405CR) || \ + defined(CONFIG_405EP) || defined(CONFIG_405EZ) || \ + defined(CONFIG_405EX) + puts("05"); +#endif +#if defined(CONFIG_440) +#if defined(CONFIG_460EX) || defined(CONFIG_460GT) + puts("60"); +#else + puts("40"); +#endif +#endif + + switch (pvr) { + case PVR_405GP_RB: + puts("GP Rev. B"); + break; + + case PVR_405GP_RC: + puts("GP Rev. C"); + break; + + case PVR_405GP_RD: + puts("GP Rev. D"); + break; + +#ifdef CONFIG_405GP + case PVR_405GP_RE: /* 405GP rev E and 405CR rev C have same PVR */ + puts("GP Rev. E"); + break; +#endif + + case PVR_405CR_RA: + puts("CR Rev. A"); + break; + + case PVR_405CR_RB: + puts("CR Rev. B"); + break; + +#ifdef CONFIG_405CR + case PVR_405CR_RC: /* 405GP rev E and 405CR rev C have same PVR */ + puts("CR Rev. C"); + break; +#endif + + case PVR_405GPR_RB: + puts("GPr Rev. B"); + break; + + case PVR_405EP_RB: + puts("EP Rev. B"); + break; + + case PVR_405EZ_RA: + puts("EZ Rev. A"); + break; + + case PVR_405EX1_RA: + puts("EX Rev. A"); + strcpy(addstr, "Security support"); + break; + + case PVR_405EXR2_RA: + puts("EXr Rev. A"); + strcpy(addstr, "No Security support"); + break; + + case PVR_405EX1_RC: + puts("EX Rev. C"); + strcpy(addstr, "Security support"); + break; + + case PVR_405EX2_RC: + puts("EX Rev. C"); + strcpy(addstr, "No Security support"); + break; + + case PVR_405EXR1_RC: + puts("EXr Rev. C"); + strcpy(addstr, "Security support"); + break; + + case PVR_405EXR2_RC: + puts("EXr Rev. C"); + strcpy(addstr, "No Security support"); + break; + + case PVR_405EX1_RD: + puts("EX Rev. D"); + strcpy(addstr, "Security support"); + break; + + case PVR_405EX2_RD: + puts("EX Rev. D"); + strcpy(addstr, "No Security support"); + break; + + case PVR_405EXR1_RD: + puts("EXr Rev. D"); + strcpy(addstr, "Security support"); + break; + + case PVR_405EXR2_RD: + puts("EXr Rev. D"); + strcpy(addstr, "No Security support"); + break; + +#if defined(CONFIG_440) + case PVR_440GP_RB: + puts("GP Rev. B"); + /* See errata 1.12: CHIP_4 */ + if ((mfdcr(CPC0_SYS0) != mfdcr(CPC0_STRP0)) || + (mfdcr(CPC0_SYS1) != mfdcr(CPC0_STRP1)) ){ + puts ( "\n\t CPC0_SYSx DCRs corrupted. " + "Resetting chip ...\n"); + udelay( 1000 * 1000 ); /* Give time for serial buf to clear */ + do_chip_reset ( mfdcr(CPC0_STRP0), + mfdcr(CPC0_STRP1) ); + } + break; + + case PVR_440GP_RC: + puts("GP Rev. C"); + break; + + case PVR_440GX_RA: + puts("GX Rev. A"); + break; + + case PVR_440GX_RB: + puts("GX Rev. B"); + break; + + case PVR_440GX_RC: + puts("GX Rev. C"); + break; + + case PVR_440GX_RF: + puts("GX Rev. F"); + break; + + case PVR_440EP_RA: + puts("EP Rev. A"); + break; + +#ifdef CONFIG_440EP + case PVR_440EP_RB: /* 440EP rev B and 440GR rev A have same PVR */ + puts("EP Rev. B"); + break; + + case PVR_440EP_RC: /* 440EP rev C and 440GR rev B have same PVR */ + puts("EP Rev. C"); + break; +#endif /* CONFIG_440EP */ + +#ifdef CONFIG_440GR + case PVR_440GR_RA: /* 440EP rev B and 440GR rev A have same PVR */ + puts("GR Rev. A"); + break; + + case PVR_440GR_RB: /* 440EP rev C and 440GR rev B have same PVR */ + puts("GR Rev. B"); + break; +#endif /* CONFIG_440GR */ +#endif /* CONFIG_440 */ + +#ifdef CONFIG_440EPX + case PVR_440EPX1_RA: /* 440EPx rev A and 440GRx rev A have same PVR */ + puts("EPx Rev. A"); + strcpy(addstr, "Security/Kasumi support"); + break; + + case PVR_440EPX2_RA: /* 440EPx rev A and 440GRx rev A have same PVR */ + puts("EPx Rev. A"); + strcpy(addstr, "No Security/Kasumi support"); + break; +#endif /* CONFIG_440EPX */ + +#ifdef CONFIG_440GRX + case PVR_440GRX1_RA: /* 440EPx rev A and 440GRx rev A have same PVR */ + puts("GRx Rev. A"); + strcpy(addstr, "Security/Kasumi support"); + break; + + case PVR_440GRX2_RA: /* 440EPx rev A and 440GRx rev A have same PVR */ + puts("GRx Rev. A"); + strcpy(addstr, "No Security/Kasumi support"); + break; +#endif /* CONFIG_440GRX */ + + case PVR_440SP_6_RAB: + puts("SP Rev. A/B"); + strcpy(addstr, "RAID 6 support"); + break; + + case PVR_440SP_RAB: + puts("SP Rev. A/B"); + strcpy(addstr, "No RAID 6 support"); + break; + + case PVR_440SP_6_RC: + puts("SP Rev. C"); + strcpy(addstr, "RAID 6 support"); + break; + + case PVR_440SP_RC: + puts("SP Rev. C"); + strcpy(addstr, "No RAID 6 support"); + break; + + case PVR_440SPe_6_RA: + puts("SPe Rev. A"); + strcpy(addstr, "RAID 6 support"); + break; + + case PVR_440SPe_RA: + puts("SPe Rev. A"); + strcpy(addstr, "No RAID 6 support"); + break; + + case PVR_440SPe_6_RB: + puts("SPe Rev. B"); + strcpy(addstr, "RAID 6 support"); + break; + + case PVR_440SPe_RB: + puts("SPe Rev. B"); + strcpy(addstr, "No RAID 6 support"); + break; + +#if defined(CONFIG_460EX) || defined(CONFIG_460GT) + case PVR_460EX_RA: + puts("EX Rev. A"); + strcpy(addstr, "No Security/Kasumi support"); + break; + + case PVR_460EX_SE_RA: + puts("EX Rev. A"); + strcpy(addstr, "Security/Kasumi support"); + break; + + case PVR_460EX_RB: + puts("EX Rev. B"); + mfsdr(SDR0_ECID3, reg); + if (reg & 0x00100000) + strcpy(addstr, "No Security/Kasumi support"); + else + strcpy(addstr, "Security/Kasumi support"); + break; + + case PVR_460GT_RA: + puts("GT Rev. A"); + strcpy(addstr, "No Security/Kasumi support"); + break; + + case PVR_460GT_SE_RA: + puts("GT Rev. A"); + strcpy(addstr, "Security/Kasumi support"); + break; + + case PVR_460GT_RB: + puts("GT Rev. B"); + mfsdr(SDR0_ECID3, reg); + if (reg & 0x00100000) + strcpy(addstr, "No Security/Kasumi support"); + else + strcpy(addstr, "Security/Kasumi support"); + break; +#endif + + case PVR_460SX_RA: + puts("SX Rev. A"); + strcpy(addstr, "Security support"); + break; + + case PVR_460SX_RA_V1: + puts("SX Rev. A"); + strcpy(addstr, "No Security support"); + break; + + case PVR_460GX_RA: + puts("GX Rev. A"); + strcpy(addstr, "Security support"); + break; + + case PVR_460GX_RA_V1: + puts("GX Rev. A"); + strcpy(addstr, "No Security support"); + break; + + case PVR_VIRTEX5: + puts("x5 VIRTEX5"); + break; + + default: + printf (" UNKNOWN (PVR=%08x)", pvr); + break; + } + + printf (" at %s MHz (PLB=%lu OPB=%lu EBC=%lu", + strmhz(buf, clock), + sys_info.freqPLB / 1000000, + get_OPB_freq() / 1000000, + sys_info.freqEBC / 1000000); +#if defined(CONFIG_PCI) && \ + (defined(CONFIG_440EP) || defined(CONFIG_440EPX) || \ + defined(CONFIG_440GR) || defined(CONFIG_440GRX)) + printf(" PCI=%lu MHz", sys_info.freqPCI / 1000000); +#endif + printf(")\n"); + + if (addstr[0] != 0) + printf(" %s\n", addstr); + +#if defined(I2C_BOOTROM) + printf (" I2C boot EEPROM %sabled\n", i2c_bootrom_enabled() ? "en" : "dis"); +#endif /* I2C_BOOTROM */ +#if defined(SDR0_PINSTP_SHIFT) + printf (" Bootstrap Option %c - ", bootstrap_char[bootstrap_option()]); + printf ("Boot ROM Location %s", bootstrap_str[bootstrap_option()]); +#ifdef CONFIG_NAND_U_BOOT + puts(", booting from NAND"); +#endif /* CONFIG_NAND_U_BOOT */ + putc('\n'); +#endif /* SDR0_PINSTP_SHIFT */ + +#if defined(CONFIG_PCI) && !defined(CONFIG_405EX) + printf (" Internal PCI arbiter %sabled", pci_arbiter_enabled() ? "en" : "dis"); +#endif + +#if defined(CONFIG_PCI) && defined(PCI_ASYNC) + if (pci_async_enabled()) { + printf (", PCI async ext clock used"); + } else { + printf (", PCI sync clock at %lu MHz", + sys_info.freqPLB / sys_info.pllPciDiv / 1000000); + } +#endif + +#if defined(CONFIG_PCI) && !defined(CONFIG_405EX) + putc('\n'); +#endif + +#if defined(CONFIG_405EP) || defined(CONFIG_405EZ) || defined(CONFIG_405EX) + printf (" 16 kB I-Cache 16 kB D-Cache"); +#elif defined(CONFIG_440) + printf (" 32 kB I-Cache 32 kB D-Cache"); +#else + printf (" 16 kB I-Cache %d kB D-Cache", + ((pvr | 0x00000001) == PVR_405GPR_RB) ? 16 : 8); +#endif +#endif /* !defined(CONFIG_IOP480) */ + +#if defined(CONFIG_IOP480) + printf ("PLX IOP480 (PVR=%08x)", pvr); + printf (" at %s MHz:", strmhz(buf, clock)); + printf (" %u kB I-Cache", 4); + printf (" %u kB D-Cache", 2); +#endif + +#endif /* !defined(CONFIG_405) */ + + putc ('\n'); + + return 0; +} + +int ppc440spe_revB() { + unsigned int pvr; + + pvr = get_pvr(); + if ((pvr == PVR_440SPe_6_RB) || (pvr == PVR_440SPe_RB)) + return 1; + else + return 0; +} + +/* ------------------------------------------------------------------------- */ + +int do_reset (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]) +{ +#if defined(CONFIG_BOARD_RESET) + board_reset(); +#else +#if defined(CONFIG_SYS_4xx_RESET_TYPE) + mtspr(SPRN_DBCR0, CONFIG_SYS_4xx_RESET_TYPE << 28); +#else + /* + * Initiate system reset in debug control register DBCR + */ + mtspr(SPRN_DBCR0, 0x30000000); +#endif /* defined(CONFIG_SYS_4xx_RESET_TYPE) */ +#endif /* defined(CONFIG_BOARD_RESET) */ + + return 1; +} + + +/* + * Get timebase clock frequency + */ +unsigned long get_tbclk (void) +{ +#if !defined(CONFIG_IOP480) + sys_info_t sys_info; + + get_sys_info(&sys_info); + return (sys_info.freqProcessor); +#else + return (66000000); +#endif + +} + + +#if defined(CONFIG_WATCHDOG) +void watchdog_reset(void) +{ + int re_enable = disable_interrupts(); + reset_4xx_watchdog(); + if (re_enable) enable_interrupts(); +} + +void reset_4xx_watchdog(void) +{ + /* + * Clear TSR(WIS) bit + */ + mtspr(SPRN_TSR, 0x40000000); +} +#endif /* CONFIG_WATCHDOG */ + +/* + * Initializes on-chip ethernet controllers. + * to override, implement board_eth_init() + */ +int cpu_eth_init(bd_t *bis) +{ +#if defined(CONFIG_PPC4xx_EMAC) + ppc_4xx_eth_initialize(bis); +#endif + return 0; +} diff --git a/arch/powerpc/cpu/ppc4xx/cpu_init.c b/arch/powerpc/cpu/ppc4xx/cpu_init.c new file mode 100644 index 0000000..8a6e545 --- /dev/null +++ b/arch/powerpc/cpu/ppc4xx/cpu_init.c @@ -0,0 +1,456 @@ +/* + * (C) Copyright 2000-2007 + * Wolfgang Denk, DENX Software Engineering, wd@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> +#include <watchdog.h> +#include <ppc4xx_enet.h> +#include <asm/processor.h> +#include <asm/gpio.h> +#include <ppc4xx.h> + +#if defined(CONFIG_405GP) || defined(CONFIG_405EP) +DECLARE_GLOBAL_DATA_PTR; +#endif + +#ifndef CONFIG_SYS_PLL_RECONFIG +#define CONFIG_SYS_PLL_RECONFIG 0 +#endif + +void reconfigure_pll(u32 new_cpu_freq) +{ +#if defined(CONFIG_440EPX) + int reset_needed = 0; + u32 reg, temp; + u32 prbdv0, target_prbdv0, /* CLK_PRIMBD */ + fwdva, target_fwdva, fwdvb, target_fwdvb, /* CLK_PLLD */ + fbdv, target_fbdv, lfbdv, target_lfbdv, + perdv0, target_perdv0, /* CLK_PERD */ + spcid0, target_spcid0; /* CLK_SPCID */ + + /* Reconfigure clocks if necessary. + * See PPC440EPx User's Manual, sections 8.2 and 14 */ + if (new_cpu_freq == 667) { + target_prbdv0 = 2; + target_fwdva = 2; + target_fwdvb = 4; + target_fbdv = 20; + target_lfbdv = 1; + target_perdv0 = 4; + target_spcid0 = 4; + + mfcpr(CPR0_PRIMBD0, reg); + temp = (reg & PRBDV_MASK) >> 24; + prbdv0 = temp ? temp : 8; + if (prbdv0 != target_prbdv0) { + reg &= ~PRBDV_MASK; + reg |= ((target_prbdv0 == 8 ? 0 : target_prbdv0) << 24); + mtcpr(CPR0_PRIMBD0, reg); + reset_needed = 1; + } + + mfcpr(CPR0_PLLD, reg); + + temp = (reg & PLLD_FWDVA_MASK) >> 16; + fwdva = temp ? temp : 16; + + temp = (reg & PLLD_FWDVB_MASK) >> 8; + fwdvb = temp ? temp : 8; + + temp = (reg & PLLD_FBDV_MASK) >> 24; + fbdv = temp ? temp : 32; + + temp = (reg & PLLD_LFBDV_MASK); + lfbdv = temp ? temp : 64; + + if (fwdva != target_fwdva || fbdv != target_fbdv || lfbdv != target_lfbdv) { + reg &= ~(PLLD_FWDVA_MASK | PLLD_FWDVB_MASK | + PLLD_FBDV_MASK | PLLD_LFBDV_MASK); + reg |= ((target_fwdva == 16 ? 0 : target_fwdva) << 16) | + ((target_fwdvb == 8 ? 0 : target_fwdvb) << 8) | + ((target_fbdv == 32 ? 0 : target_fbdv) << 24) | + (target_lfbdv == 64 ? 0 : target_lfbdv); + mtcpr(CPR0_PLLD, reg); + reset_needed = 1; + } + + mfcpr(CPR0_PERD, reg); + perdv0 = (reg & CPR0_PERD_PERDV0_MASK) >> 24; + if (perdv0 != target_perdv0) { + reg &= ~CPR0_PERD_PERDV0_MASK; + reg |= (target_perdv0 << 24); + mtcpr(CPR0_PERD, reg); + reset_needed = 1; + } + + mfcpr(CPR0_SPCID, reg); + temp = (reg & CPR0_SPCID_SPCIDV0_MASK) >> 24; + spcid0 = temp ? temp : 4; + if (spcid0 != target_spcid0) { + reg &= ~CPR0_SPCID_SPCIDV0_MASK; + reg |= ((target_spcid0 == 4 ? 0 : target_spcid0) << 24); + mtcpr(CPR0_SPCID, reg); + reset_needed = 1; + } + } + + /* Get current value of FWDVA.*/ + mfcpr(CPR0_PLLD, reg); + temp = (reg & PLLD_FWDVA_MASK) >> 16; + + /* + * Check to see if FWDVA has been set to value of 1. if it has we must + * modify it. + */ + if (temp == 1) { + mfcpr(CPR0_PLLD, reg); + /* Get current value of fbdv. */ + temp = (reg & PLLD_FBDV_MASK) >> 24; + fbdv = temp ? temp : 32; + /* Get current value of lfbdv. */ + temp = (reg & PLLD_LFBDV_MASK); + lfbdv = temp ? temp : 64; + /* + * Load register that contains current boot strapping option. + */ + mfcpr(CPR0_ICFG, reg); + /* Shift strapping option into low 3 bits.*/ + reg = (reg >> 28); + + if ((reg == BOOT_STRAP_OPTION_A) || (reg == BOOT_STRAP_OPTION_B) || + (reg == BOOT_STRAP_OPTION_D) || (reg == BOOT_STRAP_OPTION_E)) { + /* + * Get current value of FWDVA. Assign current FWDVA to + * new FWDVB. + */ + mfcpr(CPR0_PLLD, reg); + target_fwdvb = (reg & PLLD_FWDVA_MASK) >> 16; + fwdvb = target_fwdvb ? target_fwdvb : 8; + /* + * Get current value of FWDVB. Assign current FWDVB to + * new FWDVA. + */ + target_fwdva = (reg & PLLD_FWDVB_MASK) >> 8; + fwdva = target_fwdva ? target_fwdva : 16; + /* + * Update CPR0_PLLD with switched FWDVA and FWDVB. + */ + reg &= ~(PLLD_FWDVA_MASK | PLLD_FWDVB_MASK | + PLLD_FBDV_MASK | PLLD_LFBDV_MASK); + reg |= ((fwdva == 16 ? 0 : fwdva) << 16) | + ((fwdvb == 8 ? 0 : fwdvb) << 8) | + ((fbdv == 32 ? 0 : fbdv) << 24) | + (lfbdv == 64 ? 0 : lfbdv); + mtcpr(CPR0_PLLD, reg); + /* Acknowledge that a reset is required. */ + reset_needed = 1; + } + } + + if (reset_needed) { + /* + * Set reload inhibit so configuration will persist across + * processor resets + */ + mfcpr(CPR0_ICFG, reg); + reg &= ~CPR0_ICFG_RLI_MASK; + reg |= 1 << 31; + mtcpr(CPR0_ICFG, reg); + + /* Reset processor if configuration changed */ + __asm__ __volatile__ ("sync; isync"); + mtspr(SPRN_DBCR0, 0x20000000); + } +#endif +} + +/* + * Breath some life into the CPU... + * + * Reconfigure PLL if necessary, + * set up the memory map, + * initialize a bunch of registers + */ +void +cpu_init_f (void) +{ +#if defined(CONFIG_WATCHDOG) || defined(CONFIG_440GX) || defined(CONFIG_460EX) + u32 val; +#endif + + reconfigure_pll(CONFIG_SYS_PLL_RECONFIG); + +#if (defined(CONFIG_405EP) || defined (CONFIG_405EX)) && !defined(CONFIG_SYS_4xx_GPIO_TABLE) + /* + * GPIO0 setup (select GPIO or alternate function) + */ +#if defined(CONFIG_SYS_GPIO0_OR) + out32(GPIO0_OR, CONFIG_SYS_GPIO0_OR); /* set initial state of output pins */ +#endif +#if defined(CONFIG_SYS_GPIO0_ODR) + out32(GPIO0_ODR, CONFIG_SYS_GPIO0_ODR); /* open-drain select */ +#endif + out32(GPIO0_OSRH, CONFIG_SYS_GPIO0_OSRH); /* output select */ + out32(GPIO0_OSRL, CONFIG_SYS_GPIO0_OSRL); + out32(GPIO0_ISR1H, CONFIG_SYS_GPIO0_ISR1H); /* input select */ + out32(GPIO0_ISR1L, CONFIG_SYS_GPIO0_ISR1L); + out32(GPIO0_TSRH, CONFIG_SYS_GPIO0_TSRH); /* three-state select */ + out32(GPIO0_TSRL, CONFIG_SYS_GPIO0_TSRL); +#if defined(CONFIG_SYS_GPIO0_ISR2H) + out32(GPIO0_ISR2H, CONFIG_SYS_GPIO0_ISR2H); + out32(GPIO0_ISR2L, CONFIG_SYS_GPIO0_ISR2L); +#endif +#if defined (CONFIG_SYS_GPIO0_TCR) + out32(GPIO0_TCR, CONFIG_SYS_GPIO0_TCR); /* enable output driver for outputs */ +#endif +#endif /* CONFIG_405EP ... && !CONFIG_SYS_4xx_GPIO_TABLE */ + +#if defined (CONFIG_405EP) + /* + * Set EMAC noise filter bits + */ + mtdcr(CPC0_EPCTL, CPC0_EPRCSR_E0NFE | CPC0_EPRCSR_E1NFE); +#endif /* CONFIG_405EP */ + +#if defined(CONFIG_SYS_4xx_GPIO_TABLE) + gpio_set_chip_configuration(); +#endif /* CONFIG_SYS_4xx_GPIO_TABLE */ + + /* + * External Bus Controller (EBC) Setup + */ +#if (defined(CONFIG_SYS_EBC_PB0AP) && defined(CONFIG_SYS_EBC_PB0CR)) +#if (defined(CONFIG_405GP) || defined(CONFIG_405CR) || \ + defined(CONFIG_405EP) || defined(CONFIG_405EZ) || \ + defined(CONFIG_405EX) || defined(CONFIG_405)) + /* + * Move the next instructions into icache, since these modify the flash + * we are running from! + */ + asm volatile(" bl 0f" ::: "lr"); + asm volatile("0: mflr 3" ::: "r3"); + asm volatile(" addi 4, 0, 14" ::: "r4"); + asm volatile(" mtctr 4" ::: "ctr"); + asm volatile("1: icbt 0, 3"); + asm volatile(" addi 3, 3, 32" ::: "r3"); + asm volatile(" bdnz 1b" ::: "ctr", "cr0"); + asm volatile(" addis 3, 0, 0x0" ::: "r3"); + asm volatile(" ori 3, 3, 0xA000" ::: "r3"); + asm volatile(" mtctr 3" ::: "ctr"); + asm volatile("2: bdnz 2b" ::: "ctr", "cr0"); +#endif + + mtebc(PB0AP, CONFIG_SYS_EBC_PB0AP); + mtebc(PB0CR, CONFIG_SYS_EBC_PB0CR); +#endif + +#if (defined(CONFIG_SYS_EBC_PB1AP) && defined(CONFIG_SYS_EBC_PB1CR) && !(CONFIG_SYS_INIT_DCACHE_CS == 1)) + mtebc(PB1AP, CONFIG_SYS_EBC_PB1AP); + mtebc(PB1CR, CONFIG_SYS_EBC_PB1CR); +#endif + +#if (defined(CONFIG_SYS_EBC_PB2AP) && defined(CONFIG_SYS_EBC_PB2CR) && !(CONFIG_SYS_INIT_DCACHE_CS == 2)) + mtebc(PB2AP, CONFIG_SYS_EBC_PB2AP); + mtebc(PB2CR, CONFIG_SYS_EBC_PB2CR); +#endif + +#if (defined(CONFIG_SYS_EBC_PB3AP) && defined(CONFIG_SYS_EBC_PB3CR) && !(CONFIG_SYS_INIT_DCACHE_CS == 3)) + mtebc(PB3AP, CONFIG_SYS_EBC_PB3AP); + mtebc(PB3CR, CONFIG_SYS_EBC_PB3CR); +#endif + +#if (defined(CONFIG_SYS_EBC_PB4AP) && defined(CONFIG_SYS_EBC_PB4CR) && !(CONFIG_SYS_INIT_DCACHE_CS == 4)) + mtebc(PB4AP, CONFIG_SYS_EBC_PB4AP); + mtebc(PB4CR, CONFIG_SYS_EBC_PB4CR); +#endif + +#if (defined(CONFIG_SYS_EBC_PB5AP) && defined(CONFIG_SYS_EBC_PB5CR) && !(CONFIG_SYS_INIT_DCACHE_CS == 5)) + mtebc(PB5AP, CONFIG_SYS_EBC_PB5AP); + mtebc(PB5CR, CONFIG_SYS_EBC_PB5CR); +#endif + +#if (defined(CONFIG_SYS_EBC_PB6AP) && defined(CONFIG_SYS_EBC_PB6CR) && !(CONFIG_SYS_INIT_DCACHE_CS == 6)) + mtebc(PB6AP, CONFIG_SYS_EBC_PB6AP); + mtebc(PB6CR, CONFIG_SYS_EBC_PB6CR); +#endif + +#if (defined(CONFIG_SYS_EBC_PB7AP) && defined(CONFIG_SYS_EBC_PB7CR) && !(CONFIG_SYS_INIT_DCACHE_CS == 7)) + mtebc(PB7AP, CONFIG_SYS_EBC_PB7AP); + mtebc(PB7CR, CONFIG_SYS_EBC_PB7CR); +#endif + +#if defined (CONFIG_SYS_EBC_CFG) + mtebc(EBC0_CFG, CONFIG_SYS_EBC_CFG); +#endif + +#if defined(CONFIG_WATCHDOG) + val = mfspr(tcr); +#if defined(CONFIG_440EP) || defined(CONFIG_440GR) + val |= 0xb8000000; /* generate system reset after 1.34 seconds */ +#elif defined(CONFIG_440EPX) + val |= 0xb0000000; /* generate system reset after 1.34 seconds */ +#else + val |= 0xf0000000; /* generate system reset after 2.684 seconds */ +#endif +#if defined(CONFIG_SYS_4xx_RESET_TYPE) + val &= ~0x30000000; /* clear WRC bits */ + val |= CONFIG_SYS_4xx_RESET_TYPE << 28; /* set board specific WRC type */ +#endif + mtspr(tcr, val); + + val = mfspr(tsr); + val |= 0x80000000; /* enable watchdog timer */ + mtspr(tsr, val); + + reset_4xx_watchdog(); +#endif /* CONFIG_WATCHDOG */ + +#if defined(CONFIG_440GX) + /* Take the GX out of compatibility mode + * Travis Sawyer, 9 Mar 2004 + * NOTE: 440gx user manual inconsistency here + * Compatibility mode and Ethernet Clock select are not + * correct in the manual + */ + mfsdr(SDR0_MFR, val); + val &= ~0x10000000; + mtsdr(SDR0_MFR,val); +#endif /* CONFIG_440GX */ + +#if defined(CONFIG_460EX) + /* + * Set SDR0_AHB_CFG[A2P_INCR4] (bit 24) and + * clear SDR0_AHB_CFG[A2P_PROT2] (bit 25) for a new 460EX errata + * regarding concurrent use of AHB USB OTG, USB 2.0 host and SATA + */ + mfsdr(SDR0_AHB_CFG, val); + val |= 0x80; + val &= ~0x40; + mtsdr(SDR0_AHB_CFG, val); + mfsdr(SDR0_USB2HOST_CFG, val); + val &= ~0xf00; + val |= 0x400; + mtsdr(SDR0_USB2HOST_CFG, val); +#endif /* CONFIG_460EX */ + +#if defined(CONFIG_405EX) || \ + defined(CONFIG_440SP) || defined(CONFIG_440SPE) || \ + defined(CONFIG_460EX) || defined(CONFIG_460GT) || \ + defined(CONFIG_460SX) + /* + * Set PLB4 arbiter (Segment 0 and 1) to 4 deep pipeline read + */ + mtdcr(PLB0_ACR, (mfdcr(PLB0_ACR) & ~PLB0_ACR_RDP_MASK) | + PLB0_ACR_RDP_4DEEP); + mtdcr(PLB1_ACR, (mfdcr(PLB1_ACR) & ~PLB1_ACR_RDP_MASK) | + PLB1_ACR_RDP_4DEEP); +#endif /* CONFIG_440SP/SPE || CONFIG_460EX/GT || CONFIG_405EX */ +} + +/* + * initialize higher level parts of CPU like time base and timers + */ +int cpu_init_r (void) +{ +#if defined(CONFIG_405GP) + uint pvr = get_pvr(); + + /* + * Set edge conditioning circuitry on PPC405GPr + * for compatibility to existing PPC405GP designs. + */ + if ((pvr & 0xfffffff0) == (PVR_405GPR_RB & 0xfffffff0)) { + mtdcr(CPC0_ECR, 0x60606000); + } +#endif /* defined(CONFIG_405GP) */ + + return 0; +} + +#if defined(CONFIG_PCI) && \ + (defined(CONFIG_440EP) || defined(CONFIG_440EPX) || \ + defined(CONFIG_440GR) || defined(CONFIG_440GRX)) +/* + * 440EP(x)/GR(x) PCI async/sync clocking restriction: + * + * In asynchronous PCI mode, the synchronous PCI clock must meet + * certain requirements. The following equation describes the + * relationship that must be maintained between the asynchronous PCI + * clock and synchronous PCI clock. Select an appropriate PCI:PLB + * ratio to maintain the relationship: + * + * AsyncPCIClk - 1MHz <= SyncPCIclock <= (2 * AsyncPCIClk) - 1MHz + */ +static int ppc4xx_pci_sync_clock_ok(u32 sync, u32 async) +{ + if (((async - 1000000) > sync) || (sync > ((2 * async) - 1000000))) + return 0; + else + return 1; +} + +int ppc4xx_pci_sync_clock_config(u32 async) +{ + sys_info_t sys_info; + u32 sync; + int div; + u32 reg; + u32 spcid_val[] = { + CPR0_SPCID_SPCIDV0_DIV1, CPR0_SPCID_SPCIDV0_DIV2, + CPR0_SPCID_SPCIDV0_DIV3, CPR0_SPCID_SPCIDV0_DIV4 }; + + get_sys_info(&sys_info); + sync = sys_info.freqPCI; + + /* + * First check if the equation above is met + */ + if (!ppc4xx_pci_sync_clock_ok(sync, async)) { + /* + * Reconfigure PCI sync clock to meet the equation. + * Start with highest possible PCI sync frequency + * (divider 1). + */ + for (div = 1; div <= 4; div++) { + sync = sys_info.freqPLB / div; + if (ppc4xx_pci_sync_clock_ok(sync, async)) + break; + } + + if (div <= 4) { + mtcpr(CPR0_SPCID, spcid_val[div]); + + mfcpr(CPR0_ICFG, reg); + reg |= CPR0_ICFG_RLI_MASK; + mtcpr(CPR0_ICFG, reg); + + /* do chip reset */ + mtspr(SPRN_DBCR0, 0x20000000); + } else { + /* Impossible to configure the PCI sync clock */ + return -1; + } + } + + return 0; +} +#endif diff --git a/arch/powerpc/cpu/ppc4xx/dcr.S b/arch/powerpc/cpu/ppc4xx/dcr.S new file mode 100644 index 0000000..93465a3 --- /dev/null +++ b/arch/powerpc/cpu/ppc4xx/dcr.S @@ -0,0 +1,198 @@ +/* + * (C) Copyright 2001 + * Erik Theisen, Wave 7 Optics, etheisen@mindspring.com + * + * 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 <config.h> + +#if defined(CONFIG_4xx) && defined(CONFIG_CMD_SETGETDCR) + +#include <ppc4xx.h> + +#define _LINUX_CONFIG_H 1 /* avoid reading Linux autoconf.h file */ + +#include <ppc_asm.tmpl> +#include <ppc_defs.h> + +#include <asm/cache.h> +#include <asm/mmu.h> + +#define _ASMLANGUAGE + +/***************************************************************************** + * + * XXX - DANGER + * These routines make use of self modifying code. DO NOT CALL THEM + * UNTIL THEY ARE RELOCATED TO RAM. Additionally, I do not + * recommend them for use in anything other than an interactive + * debugging environment. This is mainly due to performance reasons. + * + ****************************************************************************/ + +/* + * static void _create_MFDCR(unsigned short dcrn) + * + * Builds a 'mfdcr' instruction for get_dcr + * function. + */ + .section ".text" + .align 2 + .type _create_MFDCR,@function +_create_MFDCR: + /* + * Build up a 'mfdcr' instruction formatted as follows: + * + * OPCD | RT | DCRF | XO | CR | + * ---------------|--------------|--------------|----| + * 0 5 | 6 10 | 11 20 | 21 30 | 31 | + * | | DCRN | | | + * 31 | %r3 | (5..9|0..4) | 323 | 0 | + * + * Where: + * OPCD = opcode - 31 + * RT = destination register - %r3 return register + * DCRF = DCRN # with upper and lower halves swapped + * XO = extended opcode - 323 + * CR = CR[CR0] NOT undefined - 0 + */ + rlwinm r0, r3, 27, 27, 31 /* OPCD = 31 */ + rlwinm r3, r3, 5, 22, 26 + or r3, r3, r0 + slwi r3, r3, 10 + oris r3, r3, 0x3e30 /* RT = %r3 */ + ori r3, r3, 323 /* XO = 323 */ + slwi r3, r3, 1 /* CR = 0 */ + + mflr r4 + stw r3, 0(r4) /* Store instr in get_dcr() */ + dcbst r0, r4 /* Make sure val is written out */ + sync /* Wait for write to complete */ + icbi r0, r4 /* Make sure old instr is dumped */ + isync /* Wait for icbi to complete */ + + blr +.Lfe1: .size _create_MFDCR,.Lfe1-_create_MFDCR +/* end _create_MFDCR() */ + +/* + * static void _create_MTDCR(unsigned short dcrn, unsigned long value) + * + * Builds a 'mtdcr' instruction for set_dcr + * function. + */ + .section ".text" + .align 2 + .type _create_MTDCR,@function +_create_MTDCR: + /* + * Build up a 'mtdcr' instruction formatted as follows: + * + * OPCD | RS | DCRF | XO | CR | + * ---------------|--------------|--------------|----| + * 0 5 | 6 10 | 11 20 | 21 30 | 31 | + * | | DCRN | | | + * 31 | %r3 | (5..9|0..4) | 451 | 0 | + * + * Where: + * OPCD = opcode - 31 + * RS = source register - %r4 + * DCRF = dest. DCRN # with upper and lower halves swapped + * XO = extended opcode - 451 + * CR = CR[CR0] NOT undefined - 0 + */ + rlwinm r0, r3, 27, 27, 31 /* OPCD = 31 */ + rlwinm r3, r3, 5, 22, 26 + or r3, r3, r0 + slwi r3, r3, 10 + oris r3, r3, 0x3e40 /* RS = %r4 */ + ori r3, r3, 451 /* XO = 451 */ + slwi r3, r3, 1 /* CR = 0 */ + + mflr r5 + stw r3, 0(r5) /* Store instr in set_dcr() */ + dcbst r0, r5 /* Make sure val is written out */ + sync /* Wait for write to complete */ + icbi r0, r5 /* Make sure old instr is dumped */ + isync /* Wait for icbi to complete */ + + blr +.Lfe2: .size _create_MTDCR,.Lfe2-_create_MTDCR +/* end _create_MTDCR() */ + + +/* + * unsigned long get_dcr(unsigned short dcrn) + * + * Return a given DCR's value. + */ + /* */ + /* XXX - This is self modifying code, hence */ + /* it is in the data section. */ + /* */ + .section ".data" + .align 2 + .globl get_dcr + .type get_dcr,@function +get_dcr: + mflr r0 /* Get link register */ + stwu r1, -32(r1) /* Save back chain and move SP */ + stw r0, +36(r1) /* Save link register */ + + bl _create_MFDCR /* Build following instruction */ + /* XXX - we build this instuction up on the fly. */ + .long 0 /* Get DCR's value */ + + lwz r0, +36(r1) /* Get saved link register */ + mtlr r0 /* Restore link register */ + addi r1, r1, +32 /* Remove frame from stack */ + blr /* Return to calling function */ +.Lfe3: .size get_dcr,.Lfe3-get_dcr +/* end get_dcr() */ + + +/* + * unsigned void set_dcr(unsigned short dcrn, unsigned long value) + * + * Return a given DCR's value. + */ + /* + * XXX - This is self modifying code, hence + * it is in the data section. + */ + .section ".data" + .align 2 + .globl set_dcr + .type set_dcr,@function +set_dcr: + mflr r0 /* Get link register */ + stwu r1, -32(r1) /* Save back chain and move SP */ + stw r0, +36(r1) /* Save link register */ + + bl _create_MTDCR /* Build following instruction */ + /* XXX - we build this instuction up on the fly. */ + .long 0 /* Set DCR's value */ + + lwz r0, +36(r1) /* Get saved link register */ + mtlr r0 /* Restore link register */ + addi r1, r1, +32 /* Remove frame from stack */ + blr /* Return to calling function */ +.Lfe4: .size set_dcr,.Lfe4-set_dcr +/* end set_dcr() */ +#endif diff --git a/arch/powerpc/cpu/ppc4xx/denali_data_eye.c b/arch/powerpc/cpu/ppc4xx/denali_data_eye.c new file mode 100644 index 0000000..03b8d3c --- /dev/null +++ b/arch/powerpc/cpu/ppc4xx/denali_data_eye.c @@ -0,0 +1,389 @@ +/* + * arch/powerpc/cpu/ppc4xx/denali_data_eye.c + * Extracted from board/amcc/sequoia/sdram.c by Larry Johnson <lrj@acm.org>. + * + * (C) Copyright 2006 + * Sylvie Gohl, AMCC/IBM, gohl.sylvie@fr.ibm.com + * Jacqueline Pira-Ferriol, AMCC/IBM, jpira-ferriol@fr.ibm.com + * Thierry Roman, AMCC/IBM, thierry_roman@fr.ibm.com + * Alain Saurel, AMCC/IBM, alain.saurel@fr.ibm.com + * Robert Snyder, AMCC/IBM, rob.snyder@fr.ibm.com + * + * (C) Copyright 2006-2007 + * Stefan Roese, DENX Software Engineering, sr@denx.de. + * + * 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 + */ + +/* define DEBUG for debugging output (obviously ;-)) */ +#if 0 +#define DEBUG +#endif + +#include <common.h> +#include <asm/processor.h> +#include <asm/io.h> +#include <ppc4xx.h> + +#if defined(CONFIG_440EPX) || defined(CONFIG_440GRX) +/*-----------------------------------------------------------------------------+ + * denali_wait_for_dlllock. + +----------------------------------------------------------------------------*/ +int denali_wait_for_dlllock(void) +{ + u32 val; + int wait; + + /* -----------------------------------------------------------+ + * Wait for the DCC master delay line to finish calibration + * ----------------------------------------------------------*/ + for (wait = 0; wait != 0xffff; ++wait) { + mfsdram(DDR0_17, val); + if (DDR0_17_DLLLOCKREG_DECODE(val)) { + /* dlllockreg bit on */ + return 0; + } + } + debug("0x%04x: DDR0_17 Value (dlllockreg bit): 0x%08x\n", wait, val); + debug("Waiting for dlllockreg bit to raise\n"); + return -1; +} + +#if defined(CONFIG_DDR_DATA_EYE) +#define DDR_DCR_BASE 0x10 +#define ddrcfga (DDR_DCR_BASE+0x0) /* DDR configuration address reg */ +#define ddrcfgd (DDR_DCR_BASE+0x1) /* DDR configuration data reg */ + +/*-----------------------------------------------------------------------------+ + * wait_for_dram_init_complete. + +----------------------------------------------------------------------------*/ +static int wait_for_dram_init_complete(void) +{ + unsigned long val; + int wait = 0; + + /* --------------------------------------------------------------+ + * Wait for 'DRAM initialization complete' bit in status register + * -------------------------------------------------------------*/ + mtdcr(ddrcfga, DDR0_00); + + while (wait != 0xffff) { + val = mfdcr(ddrcfgd); + if ((val & DDR0_00_INT_STATUS_BIT6) == DDR0_00_INT_STATUS_BIT6) + /* 'DRAM initialization complete' bit */ + return 0; + else + wait++; + } + debug("DRAM initialization complete bit in status register did not " + "rise\n"); + return -1; +} + +#define NUM_TRIES 64 +#define NUM_READS 10 + +/*-----------------------------------------------------------------------------+ + * denali_core_search_data_eye. + +----------------------------------------------------------------------------*/ +void denali_core_search_data_eye(void) +{ + int k, j; + u32 val; + u32 wr_dqs_shift, dqs_out_shift, dll_dqs_delay_X; + u32 max_passing_cases = 0, wr_dqs_shift_with_max_passing_cases = 0; + u32 passing_cases = 0, dll_dqs_delay_X_sw_val = 0; + u32 dll_dqs_delay_X_start_window = 0, dll_dqs_delay_X_end_window = 0; + volatile u32 *ram_pointer; + u32 test[NUM_TRIES] = { + 0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, + 0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000, + 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000, + 0xAAAAAAAA, 0xAAAAAAAA, 0x55555555, 0x55555555, + 0xAAAAAAAA, 0xAAAAAAAA, 0x55555555, 0x55555555, + 0x55555555, 0x55555555, 0xAAAAAAAA, 0xAAAAAAAA, + 0x55555555, 0x55555555, 0xAAAAAAAA, 0xAAAAAAAA, + 0xA5A5A5A5, 0xA5A5A5A5, 0x5A5A5A5A, 0x5A5A5A5A, + 0xA5A5A5A5, 0xA5A5A5A5, 0x5A5A5A5A, 0x5A5A5A5A, + 0x5A5A5A5A, 0x5A5A5A5A, 0xA5A5A5A5, 0xA5A5A5A5, + 0x5A5A5A5A, 0x5A5A5A5A, 0xA5A5A5A5, 0xA5A5A5A5, + 0xAA55AA55, 0xAA55AA55, 0x55AA55AA, 0x55AA55AA, + 0xAA55AA55, 0xAA55AA55, 0x55AA55AA, 0x55AA55AA, + 0x55AA55AA, 0x55AA55AA, 0xAA55AA55, 0xAA55AA55, + 0x55AA55AA, 0x55AA55AA, 0xAA55AA55, 0xAA55AA55 + }; + + ram_pointer = (volatile u32 *)(CONFIG_SYS_SDRAM_BASE); + + for (wr_dqs_shift = 64; wr_dqs_shift < 96; wr_dqs_shift++) { + /* for (wr_dqs_shift=1; wr_dqs_shift<96; wr_dqs_shift++) { */ + + /* -----------------------------------------------------------+ + * De-assert 'start' parameter. + * ----------------------------------------------------------*/ + mtdcr(ddrcfga, DDR0_02); + val = (mfdcr(ddrcfgd) & ~DDR0_02_START_MASK) | + DDR0_02_START_OFF; + mtdcr(ddrcfgd, val); + + /* -----------------------------------------------------------+ + * Set 'wr_dqs_shift' + * ----------------------------------------------------------*/ + mtdcr(ddrcfga, DDR0_09); + val = (mfdcr(ddrcfgd) & ~DDR0_09_WR_DQS_SHIFT_MASK) | + DDR0_09_WR_DQS_SHIFT_ENCODE(wr_dqs_shift); + mtdcr(ddrcfgd, val); + + /* -----------------------------------------------------------+ + * Set 'dqs_out_shift' = wr_dqs_shift + 32 + * ----------------------------------------------------------*/ + dqs_out_shift = wr_dqs_shift + 32; + mtdcr(ddrcfga, DDR0_22); + val = (mfdcr(ddrcfgd) & ~DDR0_22_DQS_OUT_SHIFT_MASK) | + DDR0_22_DQS_OUT_SHIFT_ENCODE(dqs_out_shift); + mtdcr(ddrcfgd, val); + + passing_cases = 0; + + for (dll_dqs_delay_X = 1; dll_dqs_delay_X < 64; + dll_dqs_delay_X++) { + /* for (dll_dqs_delay_X=1; dll_dqs_delay_X<128; + dll_dqs_delay_X++) { */ + /* -----------------------------------------------------------+ + * Set 'dll_dqs_delay_X'. + * ----------------------------------------------------------*/ + /* dll_dqs_delay_0 */ + mtdcr(ddrcfga, DDR0_17); + val = (mfdcr(ddrcfgd) & ~DDR0_17_DLL_DQS_DELAY_0_MASK) + | DDR0_17_DLL_DQS_DELAY_0_ENCODE(dll_dqs_delay_X); + mtdcr(ddrcfgd, val); + /* dll_dqs_delay_1 to dll_dqs_delay_4 */ + mtdcr(ddrcfga, DDR0_18); + val = (mfdcr(ddrcfgd) & ~DDR0_18_DLL_DQS_DELAY_X_MASK) + | DDR0_18_DLL_DQS_DELAY_4_ENCODE(dll_dqs_delay_X) + | DDR0_18_DLL_DQS_DELAY_3_ENCODE(dll_dqs_delay_X) + | DDR0_18_DLL_DQS_DELAY_2_ENCODE(dll_dqs_delay_X) + | DDR0_18_DLL_DQS_DELAY_1_ENCODE(dll_dqs_delay_X); + mtdcr(ddrcfgd, val); + /* dll_dqs_delay_5 to dll_dqs_delay_8 */ + mtdcr(ddrcfga, DDR0_19); + val = (mfdcr(ddrcfgd) & ~DDR0_19_DLL_DQS_DELAY_X_MASK) + | DDR0_19_DLL_DQS_DELAY_8_ENCODE(dll_dqs_delay_X) + | DDR0_19_DLL_DQS_DELAY_7_ENCODE(dll_dqs_delay_X) + | DDR0_19_DLL_DQS_DELAY_6_ENCODE(dll_dqs_delay_X) + | DDR0_19_DLL_DQS_DELAY_5_ENCODE(dll_dqs_delay_X); + mtdcr(ddrcfgd, val); + /* clear any ECC errors */ + mtdcr(ddrcfga, DDR0_00); + mtdcr(ddrcfgd, + mfdcr(ddrcfgd) | DDR0_00_INT_ACK_ENCODE(0x3C)); + + sync(); + eieio(); + + /* -----------------------------------------------------------+ + * Assert 'start' parameter. + * ----------------------------------------------------------*/ + mtdcr(ddrcfga, DDR0_02); + val = (mfdcr(ddrcfgd) & ~DDR0_02_START_MASK) | + DDR0_02_START_ON; + mtdcr(ddrcfgd, val); + + sync(); + eieio(); + + /* -----------------------------------------------------------+ + * Wait for the DCC master delay line to finish calibration + * ----------------------------------------------------------*/ + if (denali_wait_for_dlllock() != 0) { + printf("dll lock did not occur !!!\n"); + printf("denali_core_search_data_eye!!!\n"); + printf("wr_dqs_shift = %d - dll_dqs_delay_X = " + "%d\n", wr_dqs_shift, dll_dqs_delay_X); + hang(); + } + sync(); + eieio(); + + if (wait_for_dram_init_complete() != 0) { + printf("dram init complete did not occur!!!\n"); + printf("denali_core_search_data_eye!!!\n"); + printf("wr_dqs_shift = %d - dll_dqs_delay_X = " + "%d\n", wr_dqs_shift, dll_dqs_delay_X); + hang(); + } + udelay(100); /* wait 100us to ensure init is really completed !!! */ + + /* write values */ + for (j = 0; j < NUM_TRIES; j++) { + ram_pointer[j] = test[j]; + + /* clear any cache at ram location */ + __asm__("dcbf 0,%0": :"r"(&ram_pointer[j])); + } + + /* read values back */ + for (j = 0; j < NUM_TRIES; j++) { + for (k = 0; k < NUM_READS; k++) { + /* clear any cache at ram location */ + __asm__("dcbf 0,%0": :"r"(&ram_pointer + [j])); + + if (ram_pointer[j] != test[j]) + break; + } + + /* read error */ + if (k != NUM_READS) + break; + } + + /* See if the dll_dqs_delay_X value passed. */ + mtdcr(ddrcfga, DDR0_00); + if (j < NUM_TRIES + || (DDR0_00_INT_STATUS_DECODE(mfdcr(ddrcfgd)) & + 0x3F)) { + /* Failed */ + passing_cases = 0; + /* break; */ + } else { + /* Passed */ + if (passing_cases == 0) + dll_dqs_delay_X_sw_val = + dll_dqs_delay_X; + passing_cases++; + if (passing_cases >= max_passing_cases) { + max_passing_cases = passing_cases; + wr_dqs_shift_with_max_passing_cases = + wr_dqs_shift; + dll_dqs_delay_X_start_window = + dll_dqs_delay_X_sw_val; + dll_dqs_delay_X_end_window = + dll_dqs_delay_X; + } + } + + /* -----------------------------------------------------------+ + * De-assert 'start' parameter. + * ----------------------------------------------------------*/ + mtdcr(ddrcfga, DDR0_02); + val = (mfdcr(ddrcfgd) & ~DDR0_02_START_MASK) | + DDR0_02_START_OFF; + mtdcr(ddrcfgd, val); + } /* for (dll_dqs_delay_X=0; dll_dqs_delay_X<128; dll_dqs_delay_X++) */ + } /* for (wr_dqs_shift=0; wr_dqs_shift<96; wr_dqs_shift++) */ + + /* -----------------------------------------------------------+ + * Largest passing window is now detected. + * ----------------------------------------------------------*/ + + /* Compute dll_dqs_delay_X value */ + dll_dqs_delay_X = (dll_dqs_delay_X_end_window + + dll_dqs_delay_X_start_window) / 2; + wr_dqs_shift = wr_dqs_shift_with_max_passing_cases; + + debug("DQS calibration - Window detected:\n"); + debug("max_passing_cases = %d\n", max_passing_cases); + debug("wr_dqs_shift = %d\n", wr_dqs_shift); + debug("dll_dqs_delay_X = %d\n", dll_dqs_delay_X); + debug("dll_dqs_delay_X window = %d - %d\n", + dll_dqs_delay_X_start_window, dll_dqs_delay_X_end_window); + + /* -----------------------------------------------------------+ + * De-assert 'start' parameter. + * ----------------------------------------------------------*/ + mtdcr(ddrcfga, DDR0_02); + val = (mfdcr(ddrcfgd) & ~DDR0_02_START_MASK) | DDR0_02_START_OFF; + mtdcr(ddrcfgd, val); + + /* -----------------------------------------------------------+ + * Set 'wr_dqs_shift' + * ----------------------------------------------------------*/ + mtdcr(ddrcfga, DDR0_09); + val = (mfdcr(ddrcfgd) & ~DDR0_09_WR_DQS_SHIFT_MASK) + | DDR0_09_WR_DQS_SHIFT_ENCODE(wr_dqs_shift); + mtdcr(ddrcfgd, val); + debug("DDR0_09=0x%08lx\n", val); + + /* -----------------------------------------------------------+ + * Set 'dqs_out_shift' = wr_dqs_shift + 32 + * ----------------------------------------------------------*/ + dqs_out_shift = wr_dqs_shift + 32; + mtdcr(ddrcfga, DDR0_22); + val = (mfdcr(ddrcfgd) & ~DDR0_22_DQS_OUT_SHIFT_MASK) + | DDR0_22_DQS_OUT_SHIFT_ENCODE(dqs_out_shift); + mtdcr(ddrcfgd, val); + debug("DDR0_22=0x%08lx\n", val); + + /* -----------------------------------------------------------+ + * Set 'dll_dqs_delay_X'. + * ----------------------------------------------------------*/ + /* dll_dqs_delay_0 */ + mtdcr(ddrcfga, DDR0_17); + val = (mfdcr(ddrcfgd) & ~DDR0_17_DLL_DQS_DELAY_0_MASK) + | DDR0_17_DLL_DQS_DELAY_0_ENCODE(dll_dqs_delay_X); + mtdcr(ddrcfgd, val); + debug("DDR0_17=0x%08lx\n", val); + + /* dll_dqs_delay_1 to dll_dqs_delay_4 */ + mtdcr(ddrcfga, DDR0_18); + val = (mfdcr(ddrcfgd) & ~DDR0_18_DLL_DQS_DELAY_X_MASK) + | DDR0_18_DLL_DQS_DELAY_4_ENCODE(dll_dqs_delay_X) + | DDR0_18_DLL_DQS_DELAY_3_ENCODE(dll_dqs_delay_X) + | DDR0_18_DLL_DQS_DELAY_2_ENCODE(dll_dqs_delay_X) + | DDR0_18_DLL_DQS_DELAY_1_ENCODE(dll_dqs_delay_X); + mtdcr(ddrcfgd, val); + debug("DDR0_18=0x%08lx\n", val); + + /* dll_dqs_delay_5 to dll_dqs_delay_8 */ + mtdcr(ddrcfga, DDR0_19); + val = (mfdcr(ddrcfgd) & ~DDR0_19_DLL_DQS_DELAY_X_MASK) + | DDR0_19_DLL_DQS_DELAY_8_ENCODE(dll_dqs_delay_X) + | DDR0_19_DLL_DQS_DELAY_7_ENCODE(dll_dqs_delay_X) + | DDR0_19_DLL_DQS_DELAY_6_ENCODE(dll_dqs_delay_X) + | DDR0_19_DLL_DQS_DELAY_5_ENCODE(dll_dqs_delay_X); + mtdcr(ddrcfgd, val); + debug("DDR0_19=0x%08lx\n", val); + + /* -----------------------------------------------------------+ + * Assert 'start' parameter. + * ----------------------------------------------------------*/ + mtdcr(ddrcfga, DDR0_02); + val = (mfdcr(ddrcfgd) & ~DDR0_02_START_MASK) | DDR0_02_START_ON; + mtdcr(ddrcfgd, val); + + sync(); + eieio(); + + /* -----------------------------------------------------------+ + * Wait for the DCC master delay line to finish calibration + * ----------------------------------------------------------*/ + if (denali_wait_for_dlllock() != 0) { + printf("dll lock did not occur !!!\n"); + hang(); + } + sync(); + eieio(); + + if (wait_for_dram_init_complete() != 0) { + printf("dram init complete did not occur !!!\n"); + hang(); + } + udelay(100); /* wait 100us to ensure init is really completed !!! */ +} +#endif /* defined(CONFIG_DDR_DATA_EYE) */ +#endif /* defined(CONFIG_440EPX) || defined(CONFIG_440GRX) */ diff --git a/arch/powerpc/cpu/ppc4xx/denali_spd_ddr2.c b/arch/powerpc/cpu/ppc4xx/denali_spd_ddr2.c new file mode 100644 index 0000000..080ea0a --- /dev/null +++ b/arch/powerpc/cpu/ppc4xx/denali_spd_ddr2.c @@ -0,0 +1,1256 @@ +/* + * arch/powerpc/cpu/ppc4xx/denali_spd_ddr2.c + * This SPD SDRAM detection code supports AMCC PPC44x CPUs with a Denali-core + * DDR2 controller, specifically the 440EPx/GRx. + * + * (C) Copyright 2007-2008 + * Larry Johnson, lrj@acm.org. + * + * Based primarily on arch/powerpc/cpu/ppc4xx/4xx_spd_ddr2.c, which is... + * + * (C) Copyright 2007 + * Stefan Roese, DENX Software Engineering, sr@denx.de. + * + * COPYRIGHT AMCC CORPORATION 2004 + * + * 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 + * + */ + +/* define DEBUG for debugging output (obviously ;-)) */ +#if 0 +#define DEBUG +#endif + +#include <common.h> +#include <command.h> +#include <ppc4xx.h> +#include <i2c.h> +#include <asm/io.h> +#include <asm/processor.h> +#include <asm/mmu.h> +#include <asm/cache.h> + +#if defined(CONFIG_SPD_EEPROM) && \ + (defined(CONFIG_440EPX) || defined(CONFIG_440GRX)) + +/*-----------------------------------------------------------------------------+ + * Defines + *-----------------------------------------------------------------------------*/ +#ifndef TRUE +#define TRUE 1 +#endif +#ifndef FALSE +#define FALSE 0 +#endif + +#define MAXDIMMS 2 +#define MAXRANKS 2 + +#define ONE_BILLION 1000000000 + +#define MULDIV64(m1, m2, d) (u32)(((u64)(m1) * (u64)(m2)) / (u64)(d)) + +#define DLL_DQS_DELAY 0x19 +#define DLL_DQS_BYPASS 0x0B +#define DQS_OUT_SHIFT 0x7F + +/* + * This DDR2 setup code can dynamically setup the TLB entries for the DDR2 memory + * region. Right now the cache should still be disabled in U-Boot because of the + * EMAC driver, that need it's buffer descriptor to be located in non cached + * memory. + * + * If at some time this restriction doesn't apply anymore, just define + * CONFIG_4xx_DCACHE in the board config file and this code should setup + * everything correctly. + */ +#if defined(CONFIG_4xx_DCACHE) +#define MY_TLB_WORD2_I_ENABLE 0 /* enable caching on SDRAM */ +#else +#define MY_TLB_WORD2_I_ENABLE TLB_WORD2_I_ENABLE /* disable caching on SDRAM */ +#endif + +/*-----------------------------------------------------------------------------+ + * Prototypes + *-----------------------------------------------------------------------------*/ +extern int denali_wait_for_dlllock(void); +extern void denali_core_search_data_eye(void); +extern void dcbz_area(u32 start_address, u32 num_bytes); + +/* + * Board-specific Platform code can reimplement spd_ddr_init_hang () if needed + */ +void __spd_ddr_init_hang(void) +{ + hang(); +} +void spd_ddr_init_hang(void) + __attribute__ ((weak, alias("__spd_ddr_init_hang"))); + +#if defined(DEBUG) +static void print_mcsr(void) +{ + printf("MCSR = 0x%08X\n", mfspr(SPRN_MCSR)); +} + +static void denali_sdram_register_dump(void) +{ + unsigned int sdram_data; + + printf("\n Register Dump:\n"); + mfsdram(DDR0_00, sdram_data); + printf(" DDR0_00 = 0x%08X", sdram_data); + mfsdram(DDR0_01, sdram_data); + printf(" DDR0_01 = 0x%08X\n", sdram_data); + mfsdram(DDR0_02, sdram_data); + printf(" DDR0_02 = 0x%08X", sdram_data); + mfsdram(DDR0_03, sdram_data); + printf(" DDR0_03 = 0x%08X\n", sdram_data); + mfsdram(DDR0_04, sdram_data); + printf(" DDR0_04 = 0x%08X", sdram_data); + mfsdram(DDR0_05, sdram_data); + printf(" DDR0_05 = 0x%08X\n", sdram_data); + mfsdram(DDR0_06, sdram_data); + printf(" DDR0_06 = 0x%08X", sdram_data); + mfsdram(DDR0_07, sdram_data); + printf(" DDR0_07 = 0x%08X\n", sdram_data); + mfsdram(DDR0_08, sdram_data); + printf(" DDR0_08 = 0x%08X", sdram_data); + mfsdram(DDR0_09, sdram_data); + printf(" DDR0_09 = 0x%08X\n", sdram_data); + mfsdram(DDR0_10, sdram_data); + printf(" DDR0_10 = 0x%08X", sdram_data); + mfsdram(DDR0_11, sdram_data); + printf(" DDR0_11 = 0x%08X\n", sdram_data); + mfsdram(DDR0_12, sdram_data); + printf(" DDR0_12 = 0x%08X", sdram_data); + mfsdram(DDR0_14, sdram_data); + printf(" DDR0_14 = 0x%08X\n", sdram_data); + mfsdram(DDR0_17, sdram_data); + printf(" DDR0_17 = 0x%08X", sdram_data); + mfsdram(DDR0_18, sdram_data); + printf(" DDR0_18 = 0x%08X\n", sdram_data); + mfsdram(DDR0_19, sdram_data); + printf(" DDR0_19 = 0x%08X", sdram_data); + mfsdram(DDR0_20, sdram_data); + printf(" DDR0_20 = 0x%08X\n", sdram_data); + mfsdram(DDR0_21, sdram_data); + printf(" DDR0_21 = 0x%08X", sdram_data); + mfsdram(DDR0_22, sdram_data); + printf(" DDR0_22 = 0x%08X\n", sdram_data); + mfsdram(DDR0_23, sdram_data); + printf(" DDR0_23 = 0x%08X", sdram_data); + mfsdram(DDR0_24, sdram_data); + printf(" DDR0_24 = 0x%08X\n", sdram_data); + mfsdram(DDR0_25, sdram_data); + printf(" DDR0_25 = 0x%08X", sdram_data); + mfsdram(DDR0_26, sdram_data); + printf(" DDR0_26 = 0x%08X\n", sdram_data); + mfsdram(DDR0_27, sdram_data); + printf(" DDR0_27 = 0x%08X", sdram_data); + mfsdram(DDR0_28, sdram_data); + printf(" DDR0_28 = 0x%08X\n", sdram_data); + mfsdram(DDR0_31, sdram_data); + printf(" DDR0_31 = 0x%08X", sdram_data); + mfsdram(DDR0_32, sdram_data); + printf(" DDR0_32 = 0x%08X\n", sdram_data); + mfsdram(DDR0_33, sdram_data); + printf(" DDR0_33 = 0x%08X", sdram_data); + mfsdram(DDR0_34, sdram_data); + printf(" DDR0_34 = 0x%08X\n", sdram_data); + mfsdram(DDR0_35, sdram_data); + printf(" DDR0_35 = 0x%08X", sdram_data); + mfsdram(DDR0_36, sdram_data); + printf(" DDR0_36 = 0x%08X\n", sdram_data); + mfsdram(DDR0_37, sdram_data); + printf(" DDR0_37 = 0x%08X", sdram_data); + mfsdram(DDR0_38, sdram_data); + printf(" DDR0_38 = 0x%08X\n", sdram_data); + mfsdram(DDR0_39, sdram_data); + printf(" DDR0_39 = 0x%08X", sdram_data); + mfsdram(DDR0_40, sdram_data); + printf(" DDR0_40 = 0x%08X\n", sdram_data); + mfsdram(DDR0_41, sdram_data); + printf(" DDR0_41 = 0x%08X", sdram_data); + mfsdram(DDR0_42, sdram_data); + printf(" DDR0_42 = 0x%08X\n", sdram_data); + mfsdram(DDR0_43, sdram_data); + printf(" DDR0_43 = 0x%08X", sdram_data); + mfsdram(DDR0_44, sdram_data); + printf(" DDR0_44 = 0x%08X\n", sdram_data); +} +#else +static inline void denali_sdram_register_dump(void) +{ +} + +inline static void print_mcsr(void) +{ +} +#endif /* defined(DEBUG) */ + +static int is_ecc_enabled(void) +{ + u32 val; + + mfsdram(DDR0_22, val); + return 0x3 == DDR0_22_CTRL_RAW_DECODE(val); +} + +static unsigned char spd_read(u8 chip, unsigned int addr) +{ + u8 data[2]; + + if (0 != i2c_probe(chip) || 0 != i2c_read(chip, addr, 1, data, 1)) { + debug("spd_read(0x%02X, 0x%02X) failed\n", chip, addr); + return 0; + } + debug("spd_read(0x%02X, 0x%02X) returned 0x%02X\n", + chip, addr, data[0]); + return data[0]; +} + +static unsigned long get_tcyc(unsigned char reg) +{ + /* + * Byte 9, et al: Cycle time for CAS Latency=X, is split into two + * nibbles: the higher order nibble (bits 4-7) designates the cycle time + * to a granularity of 1ns; the value presented by the lower order + * nibble (bits 0-3) has a granularity of .1ns and is added to the value + * designated by the higher nibble. In addition, four lines of the lower + * order nibble are assigned to support +.25, +.33, +.66, and +.75. + */ + + unsigned char subfield_b = reg & 0x0F; + + switch (subfield_b & 0x0F) { + case 0x0: + case 0x1: + case 0x2: + case 0x3: + case 0x4: + case 0x5: + case 0x6: + case 0x7: + case 0x8: + case 0x9: + return 1000 * (reg >> 4) + 100 * subfield_b; + case 0xA: + return 1000 * (reg >> 4) + 250; + case 0xB: + return 1000 * (reg >> 4) + 333; + case 0xC: + return 1000 * (reg >> 4) + 667; + case 0xD: + return 1000 * (reg >> 4) + 750; + } + return 0; +} + +/*------------------------------------------------------------------ + * Find the installed DIMMs, make sure that the are DDR2, and fill + * in the dimm_ranks array. Then dimm_ranks[dimm_num] > 0 iff the + * DIMM and dimm_num is present. + * Note: Because there are only two chip-select lines, it is assumed + * that a board with a single socket can support two ranks on that + * socket, while a board with two sockets can support only one rank + * on each socket. + *-----------------------------------------------------------------*/ +static void get_spd_info(unsigned long dimm_ranks[], + unsigned long *ranks, + unsigned char const iic0_dimm_addr[], + unsigned long num_dimm_banks) +{ + unsigned long dimm_num; + unsigned long dimm_found = FALSE; + unsigned long const max_ranks_per_dimm = (1 == num_dimm_banks) ? 2 : 1; + unsigned char num_of_bytes; + unsigned char total_size; + + *ranks = 0; + for (dimm_num = 0; dimm_num < num_dimm_banks; dimm_num++) { + num_of_bytes = 0; + total_size = 0; + + num_of_bytes = spd_read(iic0_dimm_addr[dimm_num], 0); + total_size = spd_read(iic0_dimm_addr[dimm_num], 1); + if ((num_of_bytes != 0) && (total_size != 0)) { + unsigned char const dimm_type = + spd_read(iic0_dimm_addr[dimm_num], 2); + + unsigned long ranks_on_dimm = + (spd_read(iic0_dimm_addr[dimm_num], 5) & 0x07) + 1; + + if (8 != dimm_type) { + switch (dimm_type) { + case 1: + printf("ERROR: Standard Fast Page Mode " + "DRAM DIMM"); + break; + case 2: + printf("ERROR: EDO DIMM"); + break; + case 3: + printf("ERROR: Pipelined Nibble DIMM"); + break; + case 4: + printf("ERROR: SDRAM DIMM"); + break; + case 5: + printf("ERROR: Multiplexed ROM DIMM"); + break; + case 6: + printf("ERROR: SGRAM DIMM"); + break; + case 7: + printf("ERROR: DDR1 DIMM"); + break; + default: + printf("ERROR: Unknown DIMM (type %d)", + (unsigned int)dimm_type); + break; + } + printf(" detected in slot %lu.\n", dimm_num); + printf("Only DDR2 SDRAM DIMMs are supported." + "\n"); + printf("Replace the module with a DDR2 DIMM." + "\n\n"); + spd_ddr_init_hang(); + } + dimm_found = TRUE; + debug("DIMM slot %lu: populated with %lu-rank DDR2 DIMM" + "\n", dimm_num, ranks_on_dimm); + if (ranks_on_dimm > max_ranks_per_dimm) { + printf("WARNING: DRAM DIMM in slot %lu has %lu " + "ranks.\n", dimm_num, ranks_on_dimm); + if (1 == max_ranks_per_dimm) { + printf("Only one rank will be used.\n"); + } else { + printf + ("Only two ranks will be used.\n"); + } + ranks_on_dimm = max_ranks_per_dimm; + } + dimm_ranks[dimm_num] = ranks_on_dimm; + *ranks += ranks_on_dimm; + } else { + dimm_ranks[dimm_num] = 0; + debug("DIMM slot %lu: Not populated\n", dimm_num); + } + } + if (dimm_found == FALSE) { + printf("ERROR: No memory installed.\n"); + printf("Install at least one DDR2 DIMM.\n\n"); + spd_ddr_init_hang(); + } + debug("Total number of ranks = %d\n", *ranks); +} + +/*------------------------------------------------------------------ + * For the memory DIMMs installed, this routine verifies that + * frequency previously calculated is supported. + *-----------------------------------------------------------------*/ +static void check_frequency(unsigned long *dimm_ranks, + unsigned char const iic0_dimm_addr[], + unsigned long num_dimm_banks, + unsigned long sdram_freq) +{ + unsigned long dimm_num; + unsigned long cycle_time; + unsigned long calc_cycle_time; + + /* + * calc_cycle_time is calculated from DDR frequency set by board/chip + * and is expressed in picoseconds to match the way DIMM cycle time is + * calculated below. + */ + calc_cycle_time = MULDIV64(ONE_BILLION, 1000, sdram_freq); + + for (dimm_num = 0; dimm_num < num_dimm_banks; dimm_num++) { + if (dimm_ranks[dimm_num]) { + cycle_time = + get_tcyc(spd_read(iic0_dimm_addr[dimm_num], 9)); + debug("cycle_time=%d ps\n", cycle_time); + + if (cycle_time > (calc_cycle_time + 10)) { + /* + * the provided sdram cycle_time is too small + * for the available DIMM cycle_time. The + * additionnal 10ps is here to accept a small + * incertainty. + */ + printf + ("ERROR: DRAM DIMM detected with cycle_time %d ps in " + "slot %d \n while calculated cycle time is %d ps.\n", + (unsigned int)cycle_time, + (unsigned int)dimm_num, + (unsigned int)calc_cycle_time); + printf + ("Replace the DIMM, or change DDR frequency via " + "strapping bits.\n\n"); + spd_ddr_init_hang(); + } + } + } +} + +/*------------------------------------------------------------------ + * This routine gets size information for the installed memory + * DIMMs. + *-----------------------------------------------------------------*/ +static void get_dimm_size(unsigned long dimm_ranks[], + unsigned char const iic0_dimm_addr[], + unsigned long num_dimm_banks, + unsigned long *const rows, + unsigned long *const banks, + unsigned long *const cols, unsigned long *const width) +{ + unsigned long dimm_num; + + *rows = 0; + *banks = 0; + *cols = 0; + *width = 0; + for (dimm_num = 0; dimm_num < num_dimm_banks; dimm_num++) { + if (dimm_ranks[dimm_num]) { + unsigned long t; + + /* Rows */ + t = spd_read(iic0_dimm_addr[dimm_num], 3); + if (0 == *rows) { + *rows = t; + } else if (t != *rows) { + printf("ERROR: DRAM DIMM modules do not all " + "have the same number of rows.\n\n"); + spd_ddr_init_hang(); + } + /* Banks */ + t = spd_read(iic0_dimm_addr[dimm_num], 17); + if (0 == *banks) { + *banks = t; + } else if (t != *banks) { + printf("ERROR: DRAM DIMM modules do not all " + "have the same number of banks.\n\n"); + spd_ddr_init_hang(); + } + /* Columns */ + t = spd_read(iic0_dimm_addr[dimm_num], 4); + if (0 == *cols) { + *cols = t; + } else if (t != *cols) { + printf("ERROR: DRAM DIMM modules do not all " + "have the same number of columns.\n\n"); + spd_ddr_init_hang(); + } + /* Data width */ + t = spd_read(iic0_dimm_addr[dimm_num], 6); + if (0 == *width) { + *width = t; + } else if (t != *width) { + printf("ERROR: DRAM DIMM modules do not all " + "have the same data width.\n\n"); + spd_ddr_init_hang(); + } + } + } + debug("Number of rows = %d\n", *rows); + debug("Number of columns = %d\n", *cols); + debug("Number of banks = %d\n", *banks); + debug("Data width = %d\n", *width); + if (*rows > 14) { + printf("ERROR: DRAM DIMM modules have %lu address rows.\n", + *rows); + printf("Only modules with 14 or fewer rows are supported.\n\n"); + spd_ddr_init_hang(); + } + if (4 != *banks && 8 != *banks) { + printf("ERROR: DRAM DIMM modules have %lu banks.\n", *banks); + printf("Only modules with 4 or 8 banks are supported.\n\n"); + spd_ddr_init_hang(); + } + if (*cols > 12) { + printf("ERROR: DRAM DIMM modules have %lu address columns.\n", + *cols); + printf("Only modules with 12 or fewer columns are " + "supported.\n\n"); + spd_ddr_init_hang(); + } + if (32 != *width && 40 != *width && 64 != *width && 72 != *width) { + printf("ERROR: DRAM DIMM modules have a width of %lu bit.\n", + *width); + printf("Only modules with widths of 32, 40, 64, and 72 bits " + "are supported.\n\n"); + spd_ddr_init_hang(); + } +} + +/*------------------------------------------------------------------ + * Only 1.8V modules are supported. This routine verifies this. + *-----------------------------------------------------------------*/ +static void check_voltage_type(unsigned long dimm_ranks[], + unsigned char const iic0_dimm_addr[], + unsigned long num_dimm_banks) +{ + unsigned long dimm_num; + unsigned long voltage_type; + + for (dimm_num = 0; dimm_num < num_dimm_banks; dimm_num++) { + if (dimm_ranks[dimm_num]) { + voltage_type = spd_read(iic0_dimm_addr[dimm_num], 8); + if (0x05 != voltage_type) { /* 1.8V for DDR2 */ + printf("ERROR: Slot %lu provides 1.8V for DDR2 " + "DIMMs.\n", dimm_num); + switch (voltage_type) { + case 0x00: + printf("This DIMM is 5.0 Volt/TTL.\n"); + break; + case 0x01: + printf("This DIMM is LVTTL.\n"); + break; + case 0x02: + printf("This DIMM is 1.5 Volt.\n"); + break; + case 0x03: + printf("This DIMM is 3.3 Volt/TTL.\n"); + break; + case 0x04: + printf("This DIMM is 2.5 Volt.\n"); + break; + default: + printf("This DIMM is an unknown " + "voltage.\n"); + break; + } + printf("Replace it with a 1.8V DDR2 DIMM.\n\n"); + spd_ddr_init_hang(); + } + } + } +} + +static void program_ddr0_03(unsigned long dimm_ranks[], + unsigned char const iic0_dimm_addr[], + unsigned long num_dimm_banks, + unsigned long sdram_freq, + unsigned long rows, unsigned long *cas_latency) +{ + unsigned long dimm_num; + unsigned long cas_index; + unsigned long cycle_2_0_clk; + unsigned long cycle_3_0_clk; + unsigned long cycle_4_0_clk; + unsigned long cycle_5_0_clk; + unsigned long max_2_0_tcyc_ps = 100; + unsigned long max_3_0_tcyc_ps = 100; + unsigned long max_4_0_tcyc_ps = 100; + unsigned long max_5_0_tcyc_ps = 100; + unsigned char cas_available = 0x3C; /* value for DDR2 */ + u32 ddr0_03 = DDR0_03_BSTLEN_ENCODE(0x2) | DDR0_03_INITAREF_ENCODE(0x2); + unsigned int const tcyc_addr[3] = { 9, 23, 25 }; + + /*------------------------------------------------------------------ + * Get the board configuration info. + *-----------------------------------------------------------------*/ + debug("sdram_freq = %d\n", sdram_freq); + + /*------------------------------------------------------------------ + * Handle the timing. We need to find the worst case timing of all + * the dimm modules installed. + *-----------------------------------------------------------------*/ + /* loop through all the DIMM slots on the board */ + for (dimm_num = 0; dimm_num < num_dimm_banks; dimm_num++) { + /* If a dimm is installed in a particular slot ... */ + if (dimm_ranks[dimm_num]) { + unsigned char const cas_bit = + spd_read(iic0_dimm_addr[dimm_num], 18); + unsigned char cas_mask; + + cas_available &= cas_bit; + for (cas_mask = 0x80; cas_mask; cas_mask >>= 1) { + if (cas_bit & cas_mask) + break; + } + debug("cas_bit (SPD byte 18) = %02X, cas_mask = %02X\n", + cas_bit, cas_mask); + + for (cas_index = 0; cas_index < 3; + cas_mask >>= 1, cas_index++) { + unsigned long cycle_time_ps; + + if (!(cas_available & cas_mask)) { + continue; + } + cycle_time_ps = + get_tcyc(spd_read(iic0_dimm_addr[dimm_num], + tcyc_addr[cas_index])); + + debug("cas_index = %d: cycle_time_ps = %d\n", + cas_index, cycle_time_ps); + /* + * DDR2 devices use the following bitmask for CAS latency: + * Bit 7 6 5 4 3 2 1 0 + * TBD 6.0 5.0 4.0 3.0 2.0 TBD TBD + */ + switch (cas_mask) { + case 0x20: + max_5_0_tcyc_ps = + max(max_5_0_tcyc_ps, cycle_time_ps); + break; + case 0x10: + max_4_0_tcyc_ps = + max(max_4_0_tcyc_ps, cycle_time_ps); + break; + case 0x08: + max_3_0_tcyc_ps = + max(max_3_0_tcyc_ps, cycle_time_ps); + break; + case 0x04: + max_2_0_tcyc_ps = + max(max_2_0_tcyc_ps, cycle_time_ps); + break; + } + } + } + } + debug("cas_available (bit map) = 0x%02X\n", cas_available); + + /*------------------------------------------------------------------ + * Set the SDRAM mode, SDRAM_MMODE + *-----------------------------------------------------------------*/ + + /* add 10 here because of rounding problems */ + cycle_2_0_clk = MULDIV64(ONE_BILLION, 1000, max_2_0_tcyc_ps) + 10; + cycle_3_0_clk = MULDIV64(ONE_BILLION, 1000, max_3_0_tcyc_ps) + 10; + cycle_4_0_clk = MULDIV64(ONE_BILLION, 1000, max_4_0_tcyc_ps) + 10; + cycle_5_0_clk = MULDIV64(ONE_BILLION, 1000, max_5_0_tcyc_ps) + 10; + debug("cycle_2_0_clk = %d\n", cycle_2_0_clk); + debug("cycle_3_0_clk = %d\n", cycle_3_0_clk); + debug("cycle_4_0_clk = %d\n", cycle_4_0_clk); + debug("cycle_5_0_clk = %d\n", cycle_5_0_clk); + + if ((cas_available & 0x04) && (sdram_freq <= cycle_2_0_clk)) { + *cas_latency = 2; + ddr0_03 |= DDR0_03_CASLAT_ENCODE(0x2) | + DDR0_03_CASLAT_LIN_ENCODE(0x4); + } else if ((cas_available & 0x08) && (sdram_freq <= cycle_3_0_clk)) { + *cas_latency = 3; + ddr0_03 |= DDR0_03_CASLAT_ENCODE(0x3) | + DDR0_03_CASLAT_LIN_ENCODE(0x6); + } else if ((cas_available & 0x10) && (sdram_freq <= cycle_4_0_clk)) { + *cas_latency = 4; + ddr0_03 |= DDR0_03_CASLAT_ENCODE(0x4) | + DDR0_03_CASLAT_LIN_ENCODE(0x8); + } else if ((cas_available & 0x20) && (sdram_freq <= cycle_5_0_clk)) { + *cas_latency = 5; + ddr0_03 |= DDR0_03_CASLAT_ENCODE(0x5) | + DDR0_03_CASLAT_LIN_ENCODE(0xA); + } else { + printf("ERROR: Cannot find a supported CAS latency with the " + "installed DIMMs.\n"); + printf("Only DDR2 DIMMs with CAS latencies of 2.0, 3.0, 4.0, " + "and 5.0 are supported.\n"); + printf("Make sure the PLB speed is within the supported range " + "of the DIMMs.\n"); + printf("sdram_freq=%ld cycle2=%ld cycle3=%ld cycle4=%ld " + "cycle5=%ld\n\n", sdram_freq, cycle_2_0_clk, + cycle_3_0_clk, cycle_4_0_clk, cycle_5_0_clk); + spd_ddr_init_hang(); + } + debug("CAS latency = %d\n", *cas_latency); + mtsdram(DDR0_03, ddr0_03); +} + +static void program_ddr0_04(unsigned long dimm_ranks[], + unsigned char const iic0_dimm_addr[], + unsigned long num_dimm_banks, + unsigned long sdram_freq) +{ + unsigned long dimm_num; + unsigned long t_rc_ps = 0; + unsigned long t_rrd_ps = 0; + unsigned long t_rtp_ps = 0; + unsigned long t_rc_clk; + unsigned long t_rrd_clk; + unsigned long t_rtp_clk; + + /*------------------------------------------------------------------ + * Handle the timing. We need to find the worst case timing of all + * the dimm modules installed. + *-----------------------------------------------------------------*/ + /* loop through all the DIMM slots on the board */ + for (dimm_num = 0; dimm_num < num_dimm_banks; dimm_num++) { + /* If a dimm is installed in a particular slot ... */ + if (dimm_ranks[dimm_num]) { + unsigned long ps; + + /* tRC */ + ps = 1000 * spd_read(iic0_dimm_addr[dimm_num], 41); + switch (spd_read(iic0_dimm_addr[dimm_num], 40) >> 4) { + case 0x1: + ps += 250; + break; + case 0x2: + ps += 333; + break; + case 0x3: + ps += 500; + break; + case 0x4: + ps += 667; + break; + case 0x5: + ps += 750; + break; + } + t_rc_ps = max(t_rc_ps, ps); + /* tRRD */ + ps = 250 * spd_read(iic0_dimm_addr[dimm_num], 28); + t_rrd_ps = max(t_rrd_ps, ps); + /* tRTP */ + ps = 250 * spd_read(iic0_dimm_addr[dimm_num], 38); + t_rtp_ps = max(t_rtp_ps, ps); + } + } + debug("t_rc_ps = %d\n", t_rc_ps); + t_rc_clk = (MULDIV64(sdram_freq, t_rc_ps, ONE_BILLION) + 999) / 1000; + debug("t_rrd_ps = %d\n", t_rrd_ps); + t_rrd_clk = (MULDIV64(sdram_freq, t_rrd_ps, ONE_BILLION) + 999) / 1000; + debug("t_rtp_ps = %d\n", t_rtp_ps); + t_rtp_clk = (MULDIV64(sdram_freq, t_rtp_ps, ONE_BILLION) + 999) / 1000; + mtsdram(DDR0_04, DDR0_04_TRC_ENCODE(t_rc_clk) | + DDR0_04_TRRD_ENCODE(t_rrd_clk) | + DDR0_04_TRTP_ENCODE(t_rtp_clk)); +} + +static void program_ddr0_05(unsigned long dimm_ranks[], + unsigned char const iic0_dimm_addr[], + unsigned long num_dimm_banks, + unsigned long sdram_freq) +{ + unsigned long dimm_num; + unsigned long t_rp_ps = 0; + unsigned long t_ras_ps = 0; + unsigned long t_rp_clk; + unsigned long t_ras_clk; + u32 ddr0_05 = DDR0_05_TMRD_ENCODE(0x2) | DDR0_05_TEMRS_ENCODE(0x2); + + /*------------------------------------------------------------------ + * Handle the timing. We need to find the worst case timing of all + * the dimm modules installed. + *-----------------------------------------------------------------*/ + /* loop through all the DIMM slots on the board */ + for (dimm_num = 0; dimm_num < num_dimm_banks; dimm_num++) { + /* If a dimm is installed in a particular slot ... */ + if (dimm_ranks[dimm_num]) { + unsigned long ps; + + /* tRP */ + ps = 250 * spd_read(iic0_dimm_addr[dimm_num], 27); + t_rp_ps = max(t_rp_ps, ps); + /* tRAS */ + ps = 1000 * spd_read(iic0_dimm_addr[dimm_num], 30); + t_ras_ps = max(t_ras_ps, ps); + } + } + debug("t_rp_ps = %d\n", t_rp_ps); + t_rp_clk = (MULDIV64(sdram_freq, t_rp_ps, ONE_BILLION) + 999) / 1000; + debug("t_ras_ps = %d\n", t_ras_ps); + t_ras_clk = (MULDIV64(sdram_freq, t_ras_ps, ONE_BILLION) + 999) / 1000; + mtsdram(DDR0_05, ddr0_05 | DDR0_05_TRP_ENCODE(t_rp_clk) | + DDR0_05_TRAS_MIN_ENCODE(t_ras_clk)); +} + +static void program_ddr0_06(unsigned long dimm_ranks[], + unsigned char const iic0_dimm_addr[], + unsigned long num_dimm_banks, + unsigned long sdram_freq) +{ + unsigned long dimm_num; + unsigned char spd_40; + unsigned long t_wtr_ps = 0; + unsigned long t_rfc_ps = 0; + unsigned long t_wtr_clk; + unsigned long t_rfc_clk; + u32 ddr0_06 = + DDR0_06_WRITEINTERP_ENCODE(0x1) | DDR0_06_TDLL_ENCODE(200); + + /*------------------------------------------------------------------ + * Handle the timing. We need to find the worst case timing of all + * the dimm modules installed. + *-----------------------------------------------------------------*/ + /* loop through all the DIMM slots on the board */ + for (dimm_num = 0; dimm_num < num_dimm_banks; dimm_num++) { + /* If a dimm is installed in a particular slot ... */ + if (dimm_ranks[dimm_num]) { + unsigned long ps; + + /* tWTR */ + ps = 250 * spd_read(iic0_dimm_addr[dimm_num], 37); + t_wtr_ps = max(t_wtr_ps, ps); + /* tRFC */ + ps = 1000 * spd_read(iic0_dimm_addr[dimm_num], 42); + spd_40 = spd_read(iic0_dimm_addr[dimm_num], 40); + ps += 256000 * (spd_40 & 0x01); + switch ((spd_40 & 0x0E) >> 1) { + case 0x1: + ps += 250; + break; + case 0x2: + ps += 333; + break; + case 0x3: + ps += 500; + break; + case 0x4: + ps += 667; + break; + case 0x5: + ps += 750; + break; + } + t_rfc_ps = max(t_rfc_ps, ps); + } + } + debug("t_wtr_ps = %d\n", t_wtr_ps); + t_wtr_clk = (MULDIV64(sdram_freq, t_wtr_ps, ONE_BILLION) + 999) / 1000; + debug("t_rfc_ps = %d\n", t_rfc_ps); + t_rfc_clk = (MULDIV64(sdram_freq, t_rfc_ps, ONE_BILLION) + 999) / 1000; + mtsdram(DDR0_06, ddr0_06 | DDR0_06_TWTR_ENCODE(t_wtr_clk) | + DDR0_06_TRFC_ENCODE(t_rfc_clk)); +} + +static void program_ddr0_10(unsigned long dimm_ranks[], unsigned long ranks) +{ + unsigned long csmap; + + if (2 == ranks) { + /* Both chip selects in use */ + csmap = 0x03; + } else { + /* One chip select in use */ + csmap = (1 == dimm_ranks[0]) ? 0x1 : 0x2; + } + mtsdram(DDR0_10, DDR0_10_WRITE_MODEREG_ENCODE(0x0) | + DDR0_10_CS_MAP_ENCODE(csmap) | + DDR0_10_OCD_ADJUST_PUP_CS_0_ENCODE(0)); +} + +static void program_ddr0_11(unsigned long sdram_freq) +{ + unsigned long const t_xsnr_ps = 200000; /* 200 ns */ + unsigned long t_xsnr_clk; + + debug("t_xsnr_ps = %d\n", t_xsnr_ps); + t_xsnr_clk = + (MULDIV64(sdram_freq, t_xsnr_ps, ONE_BILLION) + 999) / 1000; + mtsdram(DDR0_11, DDR0_11_SREFRESH_ENCODE(0) | + DDR0_11_TXSNR_ENCODE(t_xsnr_clk) | DDR0_11_TXSR_ENCODE(200)); +} + +static void program_ddr0_22(unsigned long dimm_ranks[], + unsigned char const iic0_dimm_addr[], + unsigned long num_dimm_banks, unsigned long width) +{ +#if defined(CONFIG_DDR_ECC) + unsigned long dimm_num; + unsigned long ecc_available = width >= 64; + u32 ddr0_22 = DDR0_22_DQS_OUT_SHIFT_BYPASS_ENCODE(0x26) | + DDR0_22_DQS_OUT_SHIFT_ENCODE(DQS_OUT_SHIFT) | + DDR0_22_DLL_DQS_BYPASS_8_ENCODE(DLL_DQS_BYPASS); + + /* loop through all the DIMM slots on the board */ + for (dimm_num = 0; dimm_num < num_dimm_banks; dimm_num++) { + /* If a dimm is installed in a particular slot ... */ + if (dimm_ranks[dimm_num]) { + /* Check for ECC */ + if (0 == (spd_read(iic0_dimm_addr[dimm_num], 11) & + 0x02)) { + ecc_available = FALSE; + } + } + } + if (ecc_available) { + debug("ECC found on all DIMMs present\n"); + mtsdram(DDR0_22, ddr0_22 | DDR0_22_CTRL_RAW_ENCODE(0x3)); + } else { + debug("ECC not found on some or all DIMMs present\n"); + mtsdram(DDR0_22, ddr0_22 | DDR0_22_CTRL_RAW_ENCODE(0x0)); + } +#else + mtsdram(DDR0_22, DDR0_22_CTRL_RAW_ENCODE(0x0) | + DDR0_22_DQS_OUT_SHIFT_BYPASS_ENCODE(0x26) | + DDR0_22_DQS_OUT_SHIFT_ENCODE(DQS_OUT_SHIFT) | + DDR0_22_DLL_DQS_BYPASS_8_ENCODE(DLL_DQS_BYPASS)); +#endif /* defined(CONFIG_DDR_ECC) */ +} + +static void program_ddr0_24(unsigned long ranks) +{ + u32 ddr0_24 = DDR0_24_RTT_PAD_TERMINATION_ENCODE(0x1) | /* 75 ohm */ + DDR0_24_ODT_RD_MAP_CS1_ENCODE(0x0); + + if (2 == ranks) { + /* Both chip selects in use */ + ddr0_24 |= DDR0_24_ODT_WR_MAP_CS1_ENCODE(0x1) | + DDR0_24_ODT_WR_MAP_CS0_ENCODE(0x2); + } else { + /* One chip select in use */ + /* One of the two fields added to ddr0_24 is a "don't care" */ + ddr0_24 |= DDR0_24_ODT_WR_MAP_CS1_ENCODE(0x2) | + DDR0_24_ODT_WR_MAP_CS0_ENCODE(0x1); + } + mtsdram(DDR0_24, ddr0_24); +} + +static void program_ddr0_26(unsigned long sdram_freq) +{ + unsigned long const t_ref_ps = 7800000; /* 7.8 us. refresh */ + /* TODO: check definition of tRAS_MAX */ + unsigned long const t_ras_max_ps = 9 * t_ref_ps; + unsigned long t_ras_max_clk; + unsigned long t_ref_clk; + + /* Round down t_ras_max_clk and t_ref_clk */ + debug("t_ras_max_ps = %d\n", t_ras_max_ps); + t_ras_max_clk = MULDIV64(sdram_freq, t_ras_max_ps, ONE_BILLION) / 1000; + debug("t_ref_ps = %d\n", t_ref_ps); + t_ref_clk = MULDIV64(sdram_freq, t_ref_ps, ONE_BILLION) / 1000; + mtsdram(DDR0_26, DDR0_26_TRAS_MAX_ENCODE(t_ras_max_clk) | + DDR0_26_TREF_ENCODE(t_ref_clk)); +} + +static void program_ddr0_27(unsigned long sdram_freq) +{ + unsigned long const t_init_ps = 200000000; /* 200 us. init */ + unsigned long t_init_clk; + + debug("t_init_ps = %d\n", t_init_ps); + t_init_clk = + (MULDIV64(sdram_freq, t_init_ps, ONE_BILLION) + 999) / 1000; + mtsdram(DDR0_27, DDR0_27_EMRS_DATA_ENCODE(0x0000) | + DDR0_27_TINIT_ENCODE(t_init_clk)); +} + +static void program_ddr0_43(unsigned long dimm_ranks[], + unsigned char const iic0_dimm_addr[], + unsigned long num_dimm_banks, + unsigned long sdram_freq, + unsigned long cols, unsigned long banks) +{ + unsigned long dimm_num; + unsigned long t_wr_ps = 0; + unsigned long t_wr_clk; + u32 ddr0_43 = DDR0_43_APREBIT_ENCODE(10) | + DDR0_43_COLUMN_SIZE_ENCODE(12 - cols) | + DDR0_43_EIGHT_BANK_MODE_ENCODE(8 == banks ? 1 : 0); + + /*------------------------------------------------------------------ + * Handle the timing. We need to find the worst case timing of all + * the dimm modules installed. + *-----------------------------------------------------------------*/ + /* loop through all the DIMM slots on the board */ + for (dimm_num = 0; dimm_num < num_dimm_banks; dimm_num++) { + /* If a dimm is installed in a particular slot ... */ + if (dimm_ranks[dimm_num]) { + unsigned long ps; + + ps = 250 * spd_read(iic0_dimm_addr[dimm_num], 36); + t_wr_ps = max(t_wr_ps, ps); + } + } + debug("t_wr_ps = %d\n", t_wr_ps); + t_wr_clk = (MULDIV64(sdram_freq, t_wr_ps, ONE_BILLION) + 999) / 1000; + mtsdram(DDR0_43, ddr0_43 | DDR0_43_TWR_ENCODE(t_wr_clk)); +} + +static void program_ddr0_44(unsigned long dimm_ranks[], + unsigned char const iic0_dimm_addr[], + unsigned long num_dimm_banks, + unsigned long sdram_freq) +{ + unsigned long dimm_num; + unsigned long t_rcd_ps = 0; + unsigned long t_rcd_clk; + + /*------------------------------------------------------------------ + * Handle the timing. We need to find the worst case timing of all + * the dimm modules installed. + *-----------------------------------------------------------------*/ + /* loop through all the DIMM slots on the board */ + for (dimm_num = 0; dimm_num < num_dimm_banks; dimm_num++) { + /* If a dimm is installed in a particular slot ... */ + if (dimm_ranks[dimm_num]) { + unsigned long ps; + + ps = 250 * spd_read(iic0_dimm_addr[dimm_num], 29); + t_rcd_ps = max(t_rcd_ps, ps); + } + } + debug("t_rcd_ps = %d\n", t_rcd_ps); + t_rcd_clk = (MULDIV64(sdram_freq, t_rcd_ps, ONE_BILLION) + 999) / 1000; + mtsdram(DDR0_44, DDR0_44_TRCD_ENCODE(t_rcd_clk)); +} + +/*-----------------------------------------------------------------------------+ + * initdram. Initializes the 440EPx/GPx DDR SDRAM controller. + * Note: This routine runs from flash with a stack set up in the chip's + * sram space. It is important that the routine does not require .sbss, .bss or + * .data sections. It also cannot call routines that require these sections. + *-----------------------------------------------------------------------------*/ +/*----------------------------------------------------------------------------- + * Function: initdram + * Description: Configures SDRAM memory banks for DDR operation. + * Auto Memory Configuration option reads the DDR SDRAM EEPROMs + * via the IIC bus and then configures the DDR SDRAM memory + * banks appropriately. If Auto Memory Configuration is + * not used, it is assumed that no DIMM is plugged + *-----------------------------------------------------------------------------*/ +phys_size_t initdram(int board_type) +{ + unsigned char const iic0_dimm_addr[] = SPD_EEPROM_ADDRESS; + unsigned long dimm_ranks[MAXDIMMS]; + unsigned long ranks; + unsigned long rows; + unsigned long banks; + unsigned long cols; + unsigned long width; + unsigned long const sdram_freq = get_bus_freq(0); + unsigned long const num_dimm_banks = sizeof(iic0_dimm_addr); /* on board dimm banks */ + unsigned long cas_latency = 0; /* to quiet initialization warning */ + unsigned long dram_size; + + debug("\nEntering initdram()\n"); + + /*------------------------------------------------------------------ + * Stop the DDR-SDRAM controller. + *-----------------------------------------------------------------*/ + mtsdram(DDR0_02, DDR0_02_START_ENCODE(0)); + + /* + * Make sure I2C controller is initialized + * before continuing. + */ + /* switch to correct I2C bus */ + I2C_SET_BUS(CONFIG_SYS_SPD_BUS_NUM); + i2c_init(CONFIG_SYS_I2C_SPEED, CONFIG_SYS_I2C_SLAVE); + + /*------------------------------------------------------------------ + * Clear out the serial presence detect buffers. + * Perform IIC reads from the dimm. Fill in the spds. + * Check to see if the dimm slots are populated + *-----------------------------------------------------------------*/ + get_spd_info(dimm_ranks, &ranks, iic0_dimm_addr, num_dimm_banks); + + /*------------------------------------------------------------------ + * Check the frequency supported for the dimms plugged. + *-----------------------------------------------------------------*/ + check_frequency(dimm_ranks, iic0_dimm_addr, num_dimm_banks, sdram_freq); + + /*------------------------------------------------------------------ + * Check and get size information. + *-----------------------------------------------------------------*/ + get_dimm_size(dimm_ranks, iic0_dimm_addr, num_dimm_banks, &rows, &banks, + &cols, &width); + + /*------------------------------------------------------------------ + * Check the voltage type for the dimms plugged. + *-----------------------------------------------------------------*/ + check_voltage_type(dimm_ranks, iic0_dimm_addr, num_dimm_banks); + + /*------------------------------------------------------------------ + * Program registers for SDRAM controller. + *-----------------------------------------------------------------*/ + mtsdram(DDR0_00, DDR0_00_DLL_INCREMENT_ENCODE(0x19) | + DDR0_00_DLL_START_POINT_DECODE(0x0A)); + + mtsdram(DDR0_01, DDR0_01_PLB0_DB_CS_LOWER_ENCODE(0x01) | + DDR0_01_PLB0_DB_CS_UPPER_ENCODE(0x00) | + DDR0_01_INT_MASK_ENCODE(0xFF)); + + program_ddr0_03(dimm_ranks, iic0_dimm_addr, num_dimm_banks, sdram_freq, + rows, &cas_latency); + + program_ddr0_04(dimm_ranks, iic0_dimm_addr, num_dimm_banks, sdram_freq); + + program_ddr0_05(dimm_ranks, iic0_dimm_addr, num_dimm_banks, sdram_freq); + + program_ddr0_06(dimm_ranks, iic0_dimm_addr, num_dimm_banks, sdram_freq); + + /* + * TODO: tFAW not found in SPD. Value of 13 taken from Sequoia + * board SDRAM, but may be overly conservative. + */ + mtsdram(DDR0_07, DDR0_07_NO_CMD_INIT_ENCODE(0) | + DDR0_07_TFAW_ENCODE(13) | + DDR0_07_AUTO_REFRESH_MODE_ENCODE(1) | + DDR0_07_AREFRESH_ENCODE(0)); + + mtsdram(DDR0_08, DDR0_08_WRLAT_ENCODE(cas_latency - 1) | + DDR0_08_TCPD_ENCODE(200) | DDR0_08_DQS_N_EN_ENCODE(0) | + DDR0_08_DDRII_ENCODE(1)); + + mtsdram(DDR0_09, DDR0_09_OCD_ADJUST_PDN_CS_0_ENCODE(0x00) | + DDR0_09_RTT_0_ENCODE(0x1) | + DDR0_09_WR_DQS_SHIFT_BYPASS_ENCODE(0x1D) | + DDR0_09_WR_DQS_SHIFT_ENCODE(DQS_OUT_SHIFT - 0x20)); + + program_ddr0_10(dimm_ranks, ranks); + + program_ddr0_11(sdram_freq); + + mtsdram(DDR0_12, DDR0_12_TCKE_ENCODE(3)); + + mtsdram(DDR0_14, DDR0_14_DLL_BYPASS_MODE_ENCODE(0) | + DDR0_14_REDUC_ENCODE(width <= 40 ? 1 : 0) | + DDR0_14_REG_DIMM_ENABLE_ENCODE(0)); + + mtsdram(DDR0_17, DDR0_17_DLL_DQS_DELAY_0_ENCODE(DLL_DQS_DELAY)); + + mtsdram(DDR0_18, DDR0_18_DLL_DQS_DELAY_4_ENCODE(DLL_DQS_DELAY) | + DDR0_18_DLL_DQS_DELAY_3_ENCODE(DLL_DQS_DELAY) | + DDR0_18_DLL_DQS_DELAY_2_ENCODE(DLL_DQS_DELAY) | + DDR0_18_DLL_DQS_DELAY_1_ENCODE(DLL_DQS_DELAY)); + + mtsdram(DDR0_19, DDR0_19_DLL_DQS_DELAY_8_ENCODE(DLL_DQS_DELAY) | + DDR0_19_DLL_DQS_DELAY_7_ENCODE(DLL_DQS_DELAY) | + DDR0_19_DLL_DQS_DELAY_6_ENCODE(DLL_DQS_DELAY) | + DDR0_19_DLL_DQS_DELAY_5_ENCODE(DLL_DQS_DELAY)); + + mtsdram(DDR0_20, DDR0_20_DLL_DQS_BYPASS_3_ENCODE(DLL_DQS_BYPASS) | + DDR0_20_DLL_DQS_BYPASS_2_ENCODE(DLL_DQS_BYPASS) | + DDR0_20_DLL_DQS_BYPASS_1_ENCODE(DLL_DQS_BYPASS) | + DDR0_20_DLL_DQS_BYPASS_0_ENCODE(DLL_DQS_BYPASS)); + + mtsdram(DDR0_21, DDR0_21_DLL_DQS_BYPASS_7_ENCODE(DLL_DQS_BYPASS) | + DDR0_21_DLL_DQS_BYPASS_6_ENCODE(DLL_DQS_BYPASS) | + DDR0_21_DLL_DQS_BYPASS_5_ENCODE(DLL_DQS_BYPASS) | + DDR0_21_DLL_DQS_BYPASS_4_ENCODE(DLL_DQS_BYPASS)); + + program_ddr0_22(dimm_ranks, iic0_dimm_addr, num_dimm_banks, width); + + mtsdram(DDR0_23, DDR0_23_ODT_RD_MAP_CS0_ENCODE(0x0) | + DDR0_23_FWC_ENCODE(0)); + + program_ddr0_24(ranks); + + program_ddr0_26(sdram_freq); + + program_ddr0_27(sdram_freq); + + mtsdram(DDR0_28, DDR0_28_EMRS3_DATA_ENCODE(0x0000) | + DDR0_28_EMRS2_DATA_ENCODE(0x0000)); + + mtsdram(DDR0_31, DDR0_31_XOR_CHECK_BITS_ENCODE(0x0000)); + + mtsdram(DDR0_42, DDR0_42_ADDR_PINS_ENCODE(14 - rows) | + DDR0_42_CASLAT_LIN_GATE_ENCODE(2 * cas_latency)); + + program_ddr0_43(dimm_ranks, iic0_dimm_addr, num_dimm_banks, sdram_freq, + cols, banks); + + program_ddr0_44(dimm_ranks, iic0_dimm_addr, num_dimm_banks, sdram_freq); + + denali_sdram_register_dump(); + + dram_size = (width >= 64) ? 8 : 4; + dram_size *= 1 << cols; + dram_size *= banks; + dram_size *= 1 << rows; + dram_size *= ranks; + debug("dram_size = %lu\n", dram_size); + + /* Start the SDRAM controler */ + mtsdram(DDR0_02, DDR0_02_START_ENCODE(1)); + denali_wait_for_dlllock(); + +#if defined(CONFIG_DDR_DATA_EYE) + /* + * Map the first 1 MiB of memory in the TLB, and perform the data eye + * search. + */ + program_tlb(0, CONFIG_SYS_SDRAM_BASE, TLB_1MB_SIZE, TLB_WORD2_I_ENABLE); + denali_core_search_data_eye(); + denali_sdram_register_dump(); + remove_tlb(CONFIG_SYS_SDRAM_BASE, TLB_1MB_SIZE); +#endif + +#if defined(CONFIG_ZERO_SDRAM) || defined(CONFIG_DDR_ECC) + program_tlb(0, CONFIG_SYS_SDRAM_BASE, dram_size, 0); + sync(); + /* Zero the memory */ + debug("Zeroing SDRAM..."); +#if defined(CONFIG_SYS_MEM_TOP_HIDE) + dcbz_area(CONFIG_SYS_SDRAM_BASE, dram_size - CONFIG_SYS_MEM_TOP_HIDE); +#else +#error Please define CONFIG_SYS_MEM_TOP_HIDE (see README) in your board config file +#endif + /* Write modified dcache lines back to memory */ + clean_dcache_range(CONFIG_SYS_SDRAM_BASE, CONFIG_SYS_SDRAM_BASE + dram_size - CONFIG_SYS_MEM_TOP_HIDE); + debug("Completed\n"); + sync(); + remove_tlb(CONFIG_SYS_SDRAM_BASE, dram_size); + +#if defined(CONFIG_DDR_ECC) + /* + * If ECC is enabled, clear and enable interrupts + */ + if (is_ecc_enabled()) { + u32 val; + + sync(); + /* Clear error status */ + mfsdram(DDR0_00, val); + mtsdram(DDR0_00, val | DDR0_00_INT_ACK_ALL); + /* Set 'int_mask' parameter to functionnal value */ + mfsdram(DDR0_01, val); + mtsdram(DDR0_01, (val & ~DDR0_01_INT_MASK_MASK) | + DDR0_01_INT_MASK_ALL_OFF); +#if defined(CONFIG_DDR_DATA_EYE) + /* + * Running denali_core_search_data_eye() when ECC is enabled + * causes non-ECC machine checks. This clears them. + */ + print_mcsr(); + mtspr(SPRN_MCSR, mfspr(SPRN_MCSR)); + print_mcsr(); +#endif + sync(); + } +#endif /* defined(CONFIG_DDR_ECC) */ +#endif /* defined(CONFIG_ZERO_SDRAM) || defined(CONFIG_DDR_ECC) */ + + program_tlb(0, CONFIG_SYS_SDRAM_BASE, dram_size, MY_TLB_WORD2_I_ENABLE); + return dram_size; +} + +void board_add_ram_info(int use_default) +{ + u32 val; + + printf(" (ECC"); + if (!is_ecc_enabled()) { + printf(" not"); + } + printf(" enabled, %ld MHz", (2 * get_bus_freq(0)) / 1000000); + + mfsdram(DDR0_03, val); + printf(", CL%d)", DDR0_03_CASLAT_LIN_DECODE(val) >> 1); +} +#endif /* CONFIG_SPD_EEPROM */ diff --git a/arch/powerpc/cpu/ppc4xx/ecc.c b/arch/powerpc/cpu/ppc4xx/ecc.c new file mode 100644 index 0000000..f105605 --- /dev/null +++ b/arch/powerpc/cpu/ppc4xx/ecc.c @@ -0,0 +1,185 @@ +/* + * Copyright (c) 2008 Nuovation System Designs, LLC + * Grant Erickson <gerickson@nuovations.com> + * + * (C) Copyright 2005-2009 + * Stefan Roese, DENX Software Engineering, sr@denx.de. + * + * (C) Copyright 2002 + * Jun Gu, Artesyn Technology, jung@artesyncp.com + * + * (C) Copyright 2001 + * Bill Hunter, Wave 7 Optics, williamhunter@attbi.com + * + * 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 abe 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 + * + * Description: + * This file implements generic DRAM ECC initialization for + * PowerPC processors using a SDRAM DDR/DDR2 controller, + * including the 405EX(r), 440GP/GX/EP/GR, 440SP(E), and + * 460EX/GT. + */ + +#include <common.h> +#include <ppc4xx.h> +#include <ppc_asm.tmpl> +#include <ppc_defs.h> +#include <asm/processor.h> +#include <asm/io.h> +#include <asm/mmu.h> +#include <asm/cache.h> + +#include "ecc.h" + +#if defined(CONFIG_SDRAM_PPC4xx_IBM_DDR) || \ + defined(CONFIG_SDRAM_PPC4xx_IBM_DDR2) +#if defined(CONFIG_DDR_ECC) || defined(CONFIG_SDRAM_ECC) + +#if defined(CONFIG_405EX) +/* + * Currently only 405EX uses 16bit data bus width as an alternative + * option to 32bit data width (SDRAM0_MCOPT1_WDTH) + */ +#define SDRAM_DATA_ALT_WIDTH 2 +#else +#define SDRAM_DATA_ALT_WIDTH 8 +#endif + +static void wait_ddr_idle(void) +{ + u32 val; + + do { + mfsdram(SDRAM_MCSTAT, val); + } while ((val & SDRAM_MCSTAT_IDLE_MASK) == SDRAM_MCSTAT_IDLE_NOT); +} + +static void program_ecc_addr(unsigned long start_address, + unsigned long num_bytes, + unsigned long tlb_word2_i_value) +{ + unsigned long current_address; + unsigned long end_address; + unsigned long address_increment; + unsigned long mcopt1; + char str[] = "ECC generation -"; + char slash[] = "\\|/-\\|/-"; + int loop = 0; + int loopi = 0; + + current_address = start_address; + mfsdram(SDRAM_MCOPT1, mcopt1); + if ((mcopt1 & SDRAM_MCOPT1_MCHK_MASK) != SDRAM_MCOPT1_MCHK_NON) { + mtsdram(SDRAM_MCOPT1, + (mcopt1 & ~SDRAM_MCOPT1_MCHK_MASK) | SDRAM_MCOPT1_MCHK_GEN); + sync(); + eieio(); + wait_ddr_idle(); + + puts(str); + +#ifdef CONFIG_440 + if (tlb_word2_i_value == TLB_WORD2_I_ENABLE) { +#endif + /* ECC bit set method for non-cached memory */ + if ((mcopt1 & SDRAM_MCOPT1_DMWD_MASK) == SDRAM_MCOPT1_DMWD_32) + address_increment = 4; + else + address_increment = SDRAM_DATA_ALT_WIDTH; + end_address = current_address + num_bytes; + + while (current_address < end_address) { + *((unsigned long *)current_address) = 0; + current_address += address_increment; + + if ((loop++ % (2 << 20)) == 0) { + putc('\b'); + putc(slash[loopi++ % 8]); + } + } +#ifdef CONFIG_440 + } else { + /* ECC bit set method for cached memory */ + dcbz_area(start_address, num_bytes); + /* Write modified dcache lines back to memory */ + clean_dcache_range(start_address, start_address + num_bytes); + } +#endif /* CONFIG_440 */ + + blank_string(strlen(str)); + + sync(); + eieio(); + wait_ddr_idle(); + + /* clear ECC error repoting registers */ + mtsdram(SDRAM_ECCES, 0xffffffff); + mtdcr(0x4c, 0xffffffff); + + mtsdram(SDRAM_MCOPT1, + (mcopt1 & ~SDRAM_MCOPT1_MCHK_MASK) | SDRAM_MCOPT1_MCHK_CHK_REP); + sync(); + eieio(); + wait_ddr_idle(); + } +} + +#if defined(CONFIG_SDRAM_PPC4xx_IBM_DDR) +void ecc_init(unsigned long * const start, unsigned long size) +{ + /* + * Init ECC with cache disabled (on PPC's with IBM DDR + * controller (non DDR2), not tested with cache enabled yet + */ + program_ecc_addr((u32)start, size, TLB_WORD2_I_ENABLE); +} +#endif + +#if defined(CONFIG_SDRAM_PPC4xx_IBM_DDR2) +void do_program_ecc(unsigned long tlb_word2_i_value) +{ + unsigned long mcopt1; + unsigned long mcopt2; + unsigned long mcstat; + phys_size_t memsize = sdram_memsize(); + + if (memsize > CONFIG_MAX_MEM_MAPPED) { + printf("\nWarning: Can't enable ECC on systems with more than 2GB of SDRAM!\n"); + return; + } + + mfsdram(SDRAM_MCOPT1, mcopt1); + mfsdram(SDRAM_MCOPT2, mcopt2); + + if ((mcopt1 & SDRAM_MCOPT1_MCHK_MASK) != SDRAM_MCOPT1_MCHK_NON) { + /* DDR controller must be enabled and not in self-refresh. */ + mfsdram(SDRAM_MCSTAT, mcstat); + if (((mcopt2 & SDRAM_MCOPT2_DCEN_MASK) == SDRAM_MCOPT2_DCEN_ENABLE) + && ((mcopt2 & SDRAM_MCOPT2_SREN_MASK) == SDRAM_MCOPT2_SREN_EXIT) + && ((mcstat & (SDRAM_MCSTAT_MIC_MASK | SDRAM_MCSTAT_SRMS_MASK)) + == (SDRAM_MCSTAT_MIC_COMP | SDRAM_MCSTAT_SRMS_NOT_SF))) { + + program_ecc_addr(0, memsize, tlb_word2_i_value); + } + } +} +#endif + +#endif /* defined(CONFIG_DDR_ECC) || defined(CONFIG_SDRAM_ECC) */ +#endif /* defined(CONFIG_SDRAM_PPC4xx_IBM_DDR)... */ diff --git a/arch/powerpc/cpu/ppc4xx/ecc.h b/arch/powerpc/cpu/ppc4xx/ecc.h new file mode 100644 index 0000000..b258891 --- /dev/null +++ b/arch/powerpc/cpu/ppc4xx/ecc.h @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2008 Nuovation System Designs, LLC + * Grant Erickson <gerickson@nuovations.com> + * + * Copyright (c) 2007-2009 DENX Software Engineering, GmbH + * Stefan Roese <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 abe 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 + * + * Description: + * This file implements ECC initialization for PowerPC processors + * using the IBM SDRAM DDR1 & DDR2 controller. + * + */ + +#ifndef _ECC_H_ +#define _ECC_H_ + +/* + * Since the IBM DDR controller used on 440GP/GX/EP/GR is not register + * compatible to the IBM DDR/2 controller used on 405EX/440SP/SPe/460EX/GT + * we need to make some processor dependant defines used later on by the + * driver. + */ + +/* For 440GP/GX/EP/GR */ +#if defined(CONFIG_SDRAM_PPC4xx_IBM_DDR) +#define SDRAM_MCOPT1 SDRAM_CFG0 +#define SDRAM_MCOPT1_MCHK_MASK SDRAM_CFG0_MCHK_MASK +#define SDRAM_MCOPT1_MCHK_NON SDRAM_CFG0_MCHK_NON +#define SDRAM_MCOPT1_MCHK_GEN SDRAM_CFG0_MCHK_GEN +#define SDRAM_MCOPT1_MCHK_CHK SDRAM_CFG0_MCHK_CHK +#define SDRAM_MCOPT1_MCHK_CHK_REP SDRAM_CFG0_MCHK_CHK +#define SDRAM_MCOPT1_DMWD_MASK SDRAM_CFG0_DMWD_MASK +#define SDRAM_MCOPT1_DMWD_32 SDRAM_CFG0_DMWD_32 + +#define SDRAM_MCSTAT SDRAM0_MCSTS +#define SDRAM_MCSTAT_IDLE_MASK SDRAM_MCSTS_CIS +#define SDRAM_MCSTAT_IDLE_NOT SDRAM_MCSTS_IDLE_NOT + +#define SDRAM_ECCES SDRAM0_ECCESR +#endif + +void ecc_init(unsigned long * const start, unsigned long size); +void do_program_ecc(unsigned long tlb_word2_i_value); + +static void inline blank_string(int size) +{ + int i; + + for (i = 0; i < size; i++) + putc('\b'); + for (i = 0; i < size; i++) + putc(' '); + for (i = 0; i < size; i++) + putc('\b'); +} + +#endif /* _ECC_H_ */ diff --git a/arch/powerpc/cpu/ppc4xx/fdt.c b/arch/powerpc/cpu/ppc4xx/fdt.c new file mode 100644 index 0000000..15a184b --- /dev/null +++ b/arch/powerpc/cpu/ppc4xx/fdt.c @@ -0,0 +1,177 @@ +/* + * (C) Copyright 2007-2008 + * 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> +#include <watchdog.h> +#include <command.h> +#include <asm/cache.h> +#include <ppc4xx.h> + +#if defined(CONFIG_OF_LIBFDT) && defined(CONFIG_OF_BOARD_SETUP) +#include <libfdt.h> +#include <libfdt_env.h> +#include <fdt_support.h> +#include <asm/4xx_pcie.h> + +DECLARE_GLOBAL_DATA_PTR; + +void __ft_board_setup(void *blob, bd_t *bd) +{ + int rc; + int i; + u32 bxcr; + u32 ranges[EBC_NUM_BANKS * 4]; + u32 *p = ranges; + char ebc_path[] = "/plb/opb/ebc"; + + ft_cpu_setup(blob, bd); + + /* + * Read 4xx EBC bus bridge registers to get mappings of the + * peripheral banks into the OPB/PLB address space + */ + for (i = 0; i < EBC_NUM_BANKS; i++) { + mtdcr(EBC0_CFGADDR, EBC_BXCR(i)); + bxcr = mfdcr(EBC0_CFGDATA); + + if ((bxcr & EBC_BXCR_BU_MASK) != EBC_BXCR_BU_NONE) { + *p++ = i; + *p++ = 0; + *p++ = bxcr & EBC_BXCR_BAS_MASK; + *p++ = EBC_BXCR_BANK_SIZE(bxcr); + +#ifdef CONFIG_FDT_FIXUP_NOR_FLASH_SIZE + /* Try to update reg property in nor flash node too */ + fdt_fixup_nor_flash_size(blob, i, + EBC_BXCR_BANK_SIZE(bxcr)); +#endif + } + } + + /* Some 405 PPC's have EBC as direct PLB child in the dts */ + if (fdt_path_offset(blob, ebc_path) < 0) + strcpy(ebc_path, "/plb/ebc"); + rc = fdt_find_and_setprop(blob, ebc_path, "ranges", ranges, + (p - ranges) * sizeof(u32), 1); + if (rc) { + printf("Unable to update property EBC mappings, err=%s\n", + fdt_strerror(rc)); + } +} +void ft_board_setup(void *blob, bd_t *bd) __attribute__((weak, alias("__ft_board_setup"))); + +/* + * Fixup all PCIe nodes by setting the device_type property + * to "pci-endpoint" instead is "pci" for endpoint ports. + * This property will get checked later by the Linux driver + * to properly configure the PCIe port in Linux (again). + */ +void fdt_pcie_setup(void *blob) +{ + const char *compat = "ibm,plb-pciex"; + const char *prop = "device_type"; + const char *prop_val = "pci-endpoint"; + const u32 *port; + int no; + int rc; + + /* Search first PCIe node */ + no = fdt_node_offset_by_compatible(blob, -1, compat); + while (no != -FDT_ERR_NOTFOUND) { + port = fdt_getprop(blob, no, "port", NULL); + if (port == NULL) { + printf("WARNING: could not find port property\n"); + } else { + if (is_end_point(*port)) { + rc = fdt_setprop(blob, no, prop, prop_val, + strlen(prop_val) + 1); + if (rc < 0) + printf("WARNING: could not set %s for %s: %s.\n", + prop, compat, fdt_strerror(rc)); + } + } + + /* Jump to next PCIe node */ + no = fdt_node_offset_by_compatible(blob, no, compat); + } +} + +void ft_cpu_setup(void *blob, bd_t *bd) +{ + sys_info_t sys_info; + int off, ndepth = 0; + + get_sys_info(&sys_info); + + do_fixup_by_prop_u32(blob, "device_type", "cpu", 4, "timebase-frequency", + bd->bi_intfreq, 1); + do_fixup_by_prop_u32(blob, "device_type", "cpu", 4, "clock-frequency", + bd->bi_intfreq, 1); + do_fixup_by_path_u32(blob, "/plb", "clock-frequency", sys_info.freqPLB, 1); + do_fixup_by_path_u32(blob, "/plb/opb", "clock-frequency", sys_info.freqOPB, 1); + + if (fdt_path_offset(blob, "/plb/opb/ebc") >= 0) + do_fixup_by_path_u32(blob, "/plb/opb/ebc", "clock-frequency", + sys_info.freqEBC, 1); + else + do_fixup_by_path_u32(blob, "/plb/ebc", "clock-frequency", + sys_info.freqEBC, 1); + + fdt_fixup_memory(blob, (u64)bd->bi_memstart, (u64)bd->bi_memsize); + + /* + * Fixup all UART clocks for CPU internal UARTs + * (only these UARTs are definitely clocked by gd->uart_clk) + * + * These UARTs are direct childs of /plb/opb. This code + * does not touch any UARTs that are connected to the ebc. + */ + off = fdt_path_offset(blob, "/plb/opb"); + while ((off = fdt_next_node(blob, off, &ndepth)) >= 0) { + /* + * process all sub nodes and stop when we are back + * at the starting depth + */ + if (ndepth <= 0) + break; + + /* only update direct childs */ + if ((ndepth == 1) && + (fdt_node_check_compatible(blob, off, "ns16550") == 0)) + fdt_setprop(blob, off, + "clock-frequency", + (void*)&(gd->uart_clk), 4); + } + + /* + * Fixup all ethernet nodes + * Note: aliases in the dts are required for this + */ + fdt_fixup_ethernet(blob); + + /* + * Fixup all available PCIe nodes by setting the device_type property + */ + fdt_pcie_setup(blob); +} +#endif /* CONFIG_OF_LIBFDT && CONFIG_OF_BOARD_SETUP */ diff --git a/arch/powerpc/cpu/ppc4xx/gpio.c b/arch/powerpc/cpu/ppc4xx/gpio.c new file mode 100644 index 0000000..c0d351a --- /dev/null +++ b/arch/powerpc/cpu/ppc4xx/gpio.c @@ -0,0 +1,255 @@ +/* + * (C) Copyright 2007-2008 + * 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> +#include <asm/processor.h> +#include <asm/io.h> +#include <asm/gpio.h> + +#if defined(CONFIG_SYS_4xx_GPIO_TABLE) +gpio_param_s const gpio_tab[GPIO_GROUP_MAX][GPIO_MAX] = CONFIG_SYS_4xx_GPIO_TABLE; +#endif + +#if defined(GPIO0_OSRL) +/* Only some 4xx variants support alternate funtions on the GPIO's */ +void gpio_config(int pin, int in_out, int gpio_alt, int out_val) +{ + u32 mask; + u32 mask2; + u32 val; + u32 offs = 0; + u32 offs2 = 0; + int pin2 = pin << 1; + + if (pin >= GPIO_MAX) { + offs = 0x100; + pin -= GPIO_MAX; + } + + if (pin >= GPIO_MAX/2) { + offs2 = 0x4; + pin2 = (pin - GPIO_MAX/2) << 1; + } + + mask = 0x80000000 >> pin; + mask2 = 0xc0000000 >> pin2; + + /* first set TCR to 0 */ + out_be32((void *)GPIO0_TCR + offs, in_be32((void *)GPIO0_TCR + offs) & ~mask); + + if (in_out == GPIO_OUT) { + val = in_be32((void *)GPIO0_OSRL + offs + offs2) & ~mask2; + switch (gpio_alt) { + case GPIO_ALT1: + val |= GPIO_ALT1_SEL >> pin2; + break; + case GPIO_ALT2: + val |= GPIO_ALT2_SEL >> pin2; + break; + case GPIO_ALT3: + val |= GPIO_ALT3_SEL >> pin2; + break; + } + out_be32((void *)GPIO0_OSRL + offs + offs2, val); + + /* setup requested output value */ + if (out_val == GPIO_OUT_0) + out_be32((void *)GPIO0_OR + offs, + in_be32((void *)GPIO0_OR + offs) & ~mask); + else if (out_val == GPIO_OUT_1) + out_be32((void *)GPIO0_OR + offs, + in_be32((void *)GPIO0_OR + offs) | mask); + + /* now configure TCR to drive output if selected */ + out_be32((void *)GPIO0_TCR + offs, + in_be32((void *)GPIO0_TCR + offs) | mask); + } else { + val = in_be32((void *)GPIO0_ISR1L + offs + offs2) & ~mask2; + val |= GPIO_IN_SEL >> pin2; + out_be32((void *)GPIO0_ISR1L + offs + offs2, val); + } +} +#endif /* GPIO_OSRL */ + +void gpio_write_bit(int pin, int val) +{ + u32 offs = 0; + + if (pin >= GPIO_MAX) { + offs = 0x100; + pin -= GPIO_MAX; + } + + if (val) + out_be32((void *)GPIO0_OR + offs, + in_be32((void *)GPIO0_OR + offs) | GPIO_VAL(pin)); + else + out_be32((void *)GPIO0_OR + offs, + in_be32((void *)GPIO0_OR + offs) & ~GPIO_VAL(pin)); +} + +int gpio_read_out_bit(int pin) +{ + u32 offs = 0; + + if (pin >= GPIO_MAX) { + offs = 0x100; + pin -= GPIO_MAX; + } + + return (in_be32((void *)GPIO0_OR + offs) & GPIO_VAL(pin) ? 1 : 0); +} + +int gpio_read_in_bit(int pin) +{ + u32 offs = 0; + + if (pin >= GPIO_MAX) { + offs = 0x100; + pin -= GPIO_MAX; + } + + return (in_be32((void *)GPIO0_IR + offs) & GPIO_VAL(pin) ? 1 : 0); +} + +#if defined(CONFIG_SYS_4xx_GPIO_TABLE) +void gpio_set_chip_configuration(void) +{ + unsigned char i=0, j=0, offs=0, gpio_core; + unsigned long reg, core_add; + + for (gpio_core=0; gpio_core<GPIO_GROUP_MAX; gpio_core++) { + j = 0; + offs = 0; + /* GPIO config of the GPIOs 0 to 31 */ + for (i=0; i<GPIO_MAX; i++, j++) { + if (i == GPIO_MAX/2) { + offs = 4; + j = i-16; + } + + core_add = gpio_tab[gpio_core][i].add; + + if ((gpio_tab[gpio_core][i].in_out == GPIO_IN) || + (gpio_tab[gpio_core][i].in_out == GPIO_BI)) { + + switch (gpio_tab[gpio_core][i].alt_nb) { + case GPIO_SEL: + break; + + case GPIO_ALT1: + reg = in_be32((void *)GPIO_IS1(core_add+offs)) + & ~(GPIO_MASK >> (j*2)); + reg = reg | (GPIO_IN_SEL >> (j*2)); + out_be32((void *)GPIO_IS1(core_add+offs), reg); + break; + + case GPIO_ALT2: + reg = in_be32((void *)GPIO_IS2(core_add+offs)) + & ~(GPIO_MASK >> (j*2)); + reg = reg | (GPIO_IN_SEL >> (j*2)); + out_be32((void *)GPIO_IS2(core_add+offs), reg); + break; + + case GPIO_ALT3: + reg = in_be32((void *)GPIO_IS3(core_add+offs)) + & ~(GPIO_MASK >> (j*2)); + reg = reg | (GPIO_IN_SEL >> (j*2)); + out_be32((void *)GPIO_IS3(core_add+offs), reg); + break; + } + } + + if ((gpio_tab[gpio_core][i].in_out == GPIO_OUT) || + (gpio_tab[gpio_core][i].in_out == GPIO_BI)) { + + u32 gpio_alt_sel = 0; + + switch (gpio_tab[gpio_core][i].alt_nb) { + case GPIO_SEL: + /* + * Setup output value + * 1 -> high level + * 0 -> low level + * else -> don't touch + */ + reg = in_be32((void *)GPIO_OR(core_add)); + if (gpio_tab[gpio_core][i].out_val == GPIO_OUT_1) + reg |= (0x80000000 >> (i)); + else if (gpio_tab[gpio_core][i].out_val == GPIO_OUT_0) + reg &= ~(0x80000000 >> (i)); + out_be32((void *)GPIO_OR(core_add), reg); + + reg = in_be32((void *)GPIO_TCR(core_add)) | + (0x80000000 >> (i)); + out_be32((void *)GPIO_TCR(core_add), reg); + + reg = in_be32((void *)GPIO_OS(core_add+offs)) + & ~(GPIO_MASK >> (j*2)); + out_be32((void *)GPIO_OS(core_add+offs), reg); + reg = in_be32((void *)GPIO_TS(core_add+offs)) + & ~(GPIO_MASK >> (j*2)); + out_be32((void *)GPIO_TS(core_add+offs), reg); + break; + + case GPIO_ALT1: + gpio_alt_sel = GPIO_ALT1_SEL; + break; + + case GPIO_ALT2: + gpio_alt_sel = GPIO_ALT2_SEL; + break; + + case GPIO_ALT3: + gpio_alt_sel = GPIO_ALT3_SEL; + break; + } + + if (0 != gpio_alt_sel) { + reg = in_be32((void *)GPIO_OS(core_add+offs)) + & ~(GPIO_MASK >> (j*2)); + reg = reg | (gpio_alt_sel >> (j*2)); + out_be32((void *)GPIO_OS(core_add+offs), reg); + + if (gpio_tab[gpio_core][i].out_val == GPIO_OUT_1) { + reg = in_be32((void *)GPIO_TCR(core_add)) + | (0x80000000 >> (i)); + out_be32((void *)GPIO_TCR(core_add), reg); + reg = in_be32((void *)GPIO_TS(core_add+offs)) + & ~(GPIO_MASK >> (j*2)); + out_be32((void *)GPIO_TS(core_add+offs), reg); + } else { + reg = in_be32((void *)GPIO_TCR(core_add)) + & ~(0x80000000 >> (i)); + out_be32((void *)GPIO_TCR(core_add), reg); + reg = in_be32((void *)GPIO_TS(core_add+offs)) + & ~(GPIO_MASK >> (j*2)); + reg = reg | (gpio_alt_sel >> (j*2)); + out_be32((void *)GPIO_TS(core_add+offs), reg); + } + } + } + } + } +} +#endif /* CONFIG_SYS_4xx_GPIO_TABLE */ diff --git a/arch/powerpc/cpu/ppc4xx/interrupts.c b/arch/powerpc/cpu/ppc4xx/interrupts.c new file mode 100644 index 0000000..6db8421 --- /dev/null +++ b/arch/powerpc/cpu/ppc4xx/interrupts.c @@ -0,0 +1,216 @@ +/* + * (C) Copyright 2000-2002 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * (C) Copyright 2002 (440 port) + * Scott McNutt, Artesyn Communication Producs, smcnutt@artsyncp.com + * + * (C) Copyright 2003 (440GX port) + * Travis B. Sawyer, Sandburst Corporation, tsawyer@sandburst.com + * + * (C) Copyright 2008 (PPC440X05 port for Virtex 5 FX) + * Ricardo Ribalda-Universidad Autonoma de Madrid-ricardo.ribalda@uam.es + * Work supported by Qtechnology (htpp://qtec.com) + * + * 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> +#include <watchdog.h> +#include <command.h> +#include <asm/processor.h> +#include <asm/interrupt.h> +#include <ppc4xx.h> +#include <ppc_asm.tmpl> +#include <commproc.h> + +DECLARE_GLOBAL_DATA_PTR; + +/* + * CPM interrupt vector functions. + */ +struct irq_action { + interrupt_handler_t *handler; + void *arg; + int count; +}; +static struct irq_action irq_vecs[IRQ_MAX]; + +#if defined(CONFIG_440) + +/* SPRN changed in 440 */ +static __inline__ void set_evpr(unsigned long val) +{ + asm volatile("mtspr 0x03f,%0" : : "r" (val)); +} + +#else /* !defined(CONFIG_440) */ + +static __inline__ void set_pit(unsigned long val) +{ + asm volatile("mtpit %0" : : "r" (val)); +} + + +static __inline__ void set_tcr(unsigned long val) +{ + asm volatile("mttcr %0" : : "r" (val)); +} + + +static __inline__ void set_evpr(unsigned long val) +{ + asm volatile("mtevpr %0" : : "r" (val)); +} +#endif /* defined(CONFIG_440 */ + +int interrupt_init_cpu (unsigned *decrementer_count) +{ + int vec; + unsigned long val; + + /* decrementer is automatically reloaded */ + *decrementer_count = 0; + + /* + * Mark all irqs as free + */ + for (vec = 0; vec < IRQ_MAX; vec++) { + irq_vecs[vec].handler = NULL; + irq_vecs[vec].arg = NULL; + irq_vecs[vec].count = 0; + } + +#ifdef CONFIG_4xx + /* + * Init PIT + */ +#if defined(CONFIG_440) + val = mfspr( SPRN_TCR ); + val &= (~0x04400000); /* clear DIS & ARE */ + mtspr( SPRN_TCR, val ); + mtspr( SPRN_DEC, 0 ); /* Prevent exception after TSR clear*/ + mtspr( SPRN_DECAR, 0 ); /* clear reload */ + mtspr( SPRN_TSR, 0x08000000 ); /* clear DEC status */ + val = gd->bd->bi_intfreq/1000; /* 1 msec */ + mtspr( SPRN_DECAR, val ); /* Set auto-reload value */ + mtspr( SPRN_DEC, val ); /* Set inital val */ +#else + set_pit(gd->bd->bi_intfreq / 1000); +#endif +#endif /* CONFIG_4xx */ + +#ifdef CONFIG_ADCIOP + /* + * Init PIT + */ + set_pit(66000); +#endif + + /* + * Enable PIT + */ + val = mfspr(SPRN_TCR); + val |= 0x04400000; + mtspr(SPRN_TCR, val); + + /* + * Set EVPR to 0 + */ + set_evpr(0x00000000); + + /* + * Call uic or xilinx_irq pic_enable + */ + pic_enable(); + + return (0); +} + +void timer_interrupt_cpu(struct pt_regs *regs) +{ + /* nothing to do here */ + return; +} + +void interrupt_run_handler(int vec) +{ + irq_vecs[vec].count++; + + if (irq_vecs[vec].handler != NULL) { + /* call isr */ + (*irq_vecs[vec].handler) (irq_vecs[vec].arg); + } else { + pic_irq_disable(vec); + printf("Masking bogus interrupt vector %d\n", vec); + } + + pic_irq_ack(vec); + return; +} + +void irq_install_handler(int vec, interrupt_handler_t * handler, void *arg) +{ + /* + * Print warning when replacing with a different irq vector + */ + if ((irq_vecs[vec].handler != NULL) && (irq_vecs[vec].handler != handler)) { + printf("Interrupt vector %d: handler 0x%x replacing 0x%x\n", + vec, (uint) handler, (uint) irq_vecs[vec].handler); + } + irq_vecs[vec].handler = handler; + irq_vecs[vec].arg = arg; + + pic_irq_enable(vec); + return; +} + +void irq_free_handler(int vec) +{ + debug("Free interrupt for vector %d ==> %p\n", + vec, irq_vecs[vec].handler); + + pic_irq_disable(vec); + + irq_vecs[vec].handler = NULL; + irq_vecs[vec].arg = NULL; + return; +} + +#if defined(CONFIG_CMD_IRQ) +int do_irqinfo(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]) +{ + int vec; + + printf ("Interrupt-Information:\n"); + printf ("Nr Routine Arg Count\n"); + + for (vec = 0; vec < IRQ_MAX; vec++) { + if (irq_vecs[vec].handler != NULL) { + printf ("%02d %08lx %08lx %d\n", + vec, + (ulong)irq_vecs[vec].handler, + (ulong)irq_vecs[vec].arg, + irq_vecs[vec].count); + } + } + + return 0; +} +#endif diff --git a/arch/powerpc/cpu/ppc4xx/iop480_uart.c b/arch/powerpc/cpu/ppc4xx/iop480_uart.c new file mode 100644 index 0000000..0e3423f --- /dev/null +++ b/arch/powerpc/cpu/ppc4xx/iop480_uart.c @@ -0,0 +1,237 @@ +/* + * (C) Copyright 2000-2006 + * Wolfgang Denk, DENX Software Engineering, wd@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> +#include <commproc.h> +#include <asm/processor.h> +#include <asm/io.h> +#include <watchdog.h> + +#ifdef CONFIG_SERIAL_MULTI +#include <serial.h> +#endif + +DECLARE_GLOBAL_DATA_PTR; + +#ifdef CONFIG_IOP480 + +#define SPU_BASE 0x40000000 + +#define spu_LineStat_rc 0x00 /* Line Status Register (Read/Clear) */ +#define spu_LineStat_w 0x04 /* Line Status Register (Set) */ +#define spu_Handshk_rc 0x08 /* Handshake Status Register (Read/Clear) */ +#define spu_Handshk_w 0x0c /* Handshake Status Register (Set) */ +#define spu_BRateDivh 0x10 /* Baud rate divisor high */ +#define spu_BRateDivl 0x14 /* Baud rate divisor low */ +#define spu_CtlReg 0x18 /* Control Register */ +#define spu_RxCmd 0x1c /* Rx Command Register */ +#define spu_TxCmd 0x20 /* Tx Command Register */ +#define spu_RxBuff 0x24 /* Rx data buffer */ +#define spu_TxBuff 0x24 /* Tx data buffer */ + +/*-----------------------------------------------------------------------------+ + | Line Status Register. + +-----------------------------------------------------------------------------*/ +#define asyncLSRport1 0x40000000 +#define asyncLSRport1set 0x40000004 +#define asyncLSRDataReady 0x80 +#define asyncLSRFramingError 0x40 +#define asyncLSROverrunError 0x20 +#define asyncLSRParityError 0x10 +#define asyncLSRBreakInterrupt 0x08 +#define asyncLSRTxHoldEmpty 0x04 +#define asyncLSRTxShiftEmpty 0x02 + +/*-----------------------------------------------------------------------------+ + | Handshake Status Register. + +-----------------------------------------------------------------------------*/ +#define asyncHSRport1 0x40000008 +#define asyncHSRport1set 0x4000000c +#define asyncHSRDsr 0x80 +#define asyncLSRCts 0x40 + +/*-----------------------------------------------------------------------------+ + | Control Register. + +-----------------------------------------------------------------------------*/ +#define asyncCRport1 0x40000018 +#define asyncCRNormal 0x00 +#define asyncCRLoopback 0x40 +#define asyncCRAutoEcho 0x80 +#define asyncCRDtr 0x20 +#define asyncCRRts 0x10 +#define asyncCRWordLength7 0x00 +#define asyncCRWordLength8 0x08 +#define asyncCRParityDisable 0x00 +#define asyncCRParityEnable 0x04 +#define asyncCREvenParity 0x00 +#define asyncCROddParity 0x02 +#define asyncCRStopBitsOne 0x00 +#define asyncCRStopBitsTwo 0x01 +#define asyncCRDisableDtrRts 0x00 + +/*-----------------------------------------------------------------------------+ + | Receiver Command Register. + +-----------------------------------------------------------------------------*/ +#define asyncRCRport1 0x4000001c +#define asyncRCRDisable 0x00 +#define asyncRCREnable 0x80 +#define asyncRCRIntDisable 0x00 +#define asyncRCRIntEnabled 0x20 +#define asyncRCRDMACh2 0x40 +#define asyncRCRDMACh3 0x60 +#define asyncRCRErrorInt 0x10 +#define asyncRCRPauseEnable 0x08 + +/*-----------------------------------------------------------------------------+ + | Transmitter Command Register. + +-----------------------------------------------------------------------------*/ +#define asyncTCRport1 0x40000020 +#define asyncTCRDisable 0x00 +#define asyncTCREnable 0x80 +#define asyncTCRIntDisable 0x00 +#define asyncTCRIntEnabled 0x20 +#define asyncTCRDMACh2 0x40 +#define asyncTCRDMACh3 0x60 +#define asyncTCRTxEmpty 0x10 +#define asyncTCRErrorInt 0x08 +#define asyncTCRStopPause 0x04 +#define asyncTCRBreakGen 0x02 + +/*-----------------------------------------------------------------------------+ + | Miscellanies defines. + +-----------------------------------------------------------------------------*/ +#define asyncTxBufferport1 0x40000024 +#define asyncRxBufferport1 0x40000024 +#define asyncDLABLsbport1 0x40000014 +#define asyncDLABMsbport1 0x40000010 +#define asyncXOFFchar 0x13 +#define asyncXONchar 0x11 + +/* + * Minimal serial functions needed to use one of the SMC ports + * as serial console interface. + */ + +int serial_init (void) +{ + volatile char val; + unsigned short br_reg; + + br_reg = ((((CONFIG_CPUCLOCK * 1000000) / 16) / gd->baudrate) - 1); + + /* + * Init onboard UART + */ + out_8((u8 *)SPU_BASE + spu_LineStat_rc, 0x78); /* Clear all bits in Line Status Reg */ + out_8((u8 *)SPU_BASE + spu_BRateDivl, (br_reg & 0x00ff)); /* Set baud rate divisor... */ + out_8((u8 *)SPU_BASE + spu_BRateDivh, ((br_reg & 0xff00) >> 8)); /* ... */ + out_8((u8 *)SPU_BASE + spu_CtlReg, 0x08); /* Set 8 bits, no parity and 1 stop bit */ + out_8((u8 *)SPU_BASE + spu_RxCmd, 0xb0); /* Enable Rx */ + out_8((u8 *)SPU_BASE + spu_TxCmd, 0x9c); /* Enable Tx */ + out_8((u8 *)SPU_BASE + spu_Handshk_rc, 0xff); /* Clear Handshake */ + val = in_8((u8 *)SPU_BASE + spu_RxBuff); /* Dummy read, to clear receiver */ + + return (0); +} + +void serial_setbrg (void) +{ + unsigned short br_reg; + + br_reg = ((((CONFIG_CPUCLOCK * 1000000) / 16) / gd->baudrate) - 1); + + out_8((u8 *)SPU_BASE + spu_BRateDivl, + (br_reg & 0x00ff)); /* Set baud rate divisor... */ + out_8((u8 *)SPU_BASE + spu_BRateDivh, + ((br_reg & 0xff00) >> 8)); /* ... */ +} + +void serial_putc (const char c) +{ + if (c == '\n') + serial_putc ('\r'); + + /* load status from handshake register */ + if (in_8((u8 *)SPU_BASE + spu_Handshk_rc) != 00) + out_8((u8 *)SPU_BASE + spu_Handshk_rc, 0xff); /* Clear Handshake */ + + out_8((u8 *)SPU_BASE + spu_TxBuff, c); /* Put char */ + + while ((in_8((u8 *)SPU_BASE + spu_LineStat_rc) & 04) != 04) { + if (in_8((u8 *)SPU_BASE + spu_Handshk_rc) != 00) + out_8((u8 *)SPU_BASE + spu_Handshk_rc, 0xff); /* Clear Handshake */ + } +} + +void serial_puts (const char *s) +{ + while (*s) { + serial_putc (*s++); + } +} + +int serial_getc () +{ + unsigned char status = 0; + + while (1) { + status = in_8((u8 *)asyncLSRport1); + if ((status & asyncLSRDataReady) != 0x0) { + break; + } + if ((status & ( asyncLSRFramingError | + asyncLSROverrunError | + asyncLSRParityError | + asyncLSRBreakInterrupt )) != 0) { + (void) out_8((u8 *)asyncLSRport1, + asyncLSRFramingError | + asyncLSROverrunError | + asyncLSRParityError | + asyncLSRBreakInterrupt ); + } + } + return (0x000000ff & (int) in_8((u8 *)asyncRxBufferport1)); +} + +int serial_tstc () +{ + unsigned char status; + + status = in_8((u8 *)asyncLSRport1); + if ((status & asyncLSRDataReady) != 0x0) { + return (1); + } + if ((status & ( asyncLSRFramingError | + asyncLSROverrunError | + asyncLSRParityError | + asyncLSRBreakInterrupt )) != 0) { + (void) out_8((u8 *)asyncLSRport1, + asyncLSRFramingError | + asyncLSROverrunError | + asyncLSRParityError | + asyncLSRBreakInterrupt); + } + return 0; +} + +#endif /* CONFIG_IOP480 */ diff --git a/arch/powerpc/cpu/ppc4xx/kgdb.S b/arch/powerpc/cpu/ppc4xx/kgdb.S new file mode 100644 index 0000000..4227a4c --- /dev/null +++ b/arch/powerpc/cpu/ppc4xx/kgdb.S @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2000 Murray Jensen <Murray.Jensen@cmst.csiro.au> + * + * 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 <config.h> +#include <command.h> +#include <ppc4xx.h> +#include <version.h> + +#define CONFIG_405GP 1 /* needed for Linux kernel header files */ +#define _LINUX_CONFIG_H 1 /* avoid reading Linux autoconf.h file */ + +#include <ppc_asm.tmpl> +#include <ppc_defs.h> + +#include <asm/cache.h> +#include <asm/mmu.h> + +#if defined(CONFIG_CMD_KGDB) + /* + * cache flushing routines for kgdb + */ + + .globl kgdb_flush_cache_all +kgdb_flush_cache_all: + /* icache */ + iccci r0,r0 /* iccci invalidates the entire I cache */ + /* dcache */ + addi r6,0,0x0000 /* clear GPR 6 */ + addi r7,r0, 128 /* do loop for # of dcache lines */ + /* NOTE: dccci invalidates both */ + mtctr r7 /* ways in the D cache */ +..dcloop: + dccci 0,r6 /* invalidate line */ + addi r6,r6, 32 /* bump to next line */ + bdnz ..dcloop + blr + + .globl kgdb_flush_cache_range +kgdb_flush_cache_range: + li r5,L1_CACHE_BYTES-1 + andc r3,r3,r5 + subf r4,r3,r4 + add r4,r4,r5 + srwi. r4,r4,L1_CACHE_SHIFT + beqlr + mtctr r4 + mr r6,r3 +1: dcbst 0,r3 + addi r3,r3,L1_CACHE_BYTES + bdnz 1b + sync /* wait for dcbst's to get to ram */ + mtctr r4 +2: icbi 0,r6 + addi r6,r6,L1_CACHE_BYTES + bdnz 2b + SYNC + blr + +#endif diff --git a/arch/powerpc/cpu/ppc4xx/miiphy.c b/arch/powerpc/cpu/ppc4xx/miiphy.c new file mode 100644 index 0000000..22ed5c2 --- /dev/null +++ b/arch/powerpc/cpu/ppc4xx/miiphy.c @@ -0,0 +1,366 @@ +/*-----------------------------------------------------------------------------+ + | This source code is dual-licensed. You may use it under the terms of the + | GNU General Public License version 2, or under the license below. + | + | This source code has been made available to you by IBM on an AS-IS + | basis. Anyone receiving this source is licensed under IBM + | copyrights to use it in any way he or she deems fit, including + | copying it, modifying it, compiling it, and redistributing it either + | with or without modifications. No license under IBM patents or + | patent applications is to be implied by the copyright license. + | + | Any user of this software should understand that IBM cannot provide + | technical support for this software and will not be responsible for + | any consequences resulting from the use of this software. + | + | Any person who transfers this source code or any derivative work + | must include the IBM copyright notice, this paragraph, and the + | preceding two paragraphs in the transferred software. + | + | COPYRIGHT I B M CORPORATION 1995 + | LICENSED MATERIAL - PROGRAM PROPERTY OF I B M + +-----------------------------------------------------------------------------*/ +/*-----------------------------------------------------------------------------+ + | + | File Name: miiphy.c + | + | Function: This module has utilities for accessing the MII PHY through + | the EMAC3 macro. + | + | Author: Mark Wisner + | + +-----------------------------------------------------------------------------*/ + +/* define DEBUG for debugging output (obviously ;-)) */ +#if 0 +#define DEBUG +#endif + +#include <common.h> +#include <asm/processor.h> +#include <asm/io.h> +#include <ppc_asm.tmpl> +#include <commproc.h> +#include <ppc4xx_enet.h> +#include <405_mal.h> +#include <miiphy.h> + +#if !defined(CONFIG_PHY_CLK_FREQ) +#define CONFIG_PHY_CLK_FREQ 0 +#endif + +/***********************************************************/ +/* Dump out to the screen PHY regs */ +/***********************************************************/ + +void miiphy_dump (char *devname, unsigned char addr) +{ + unsigned long i; + unsigned short data; + + for (i = 0; i < 0x1A; i++) { + if (miiphy_read (devname, addr, i, &data)) { + printf ("read error for reg %lx\n", i); + return; + } + printf ("Phy reg %lx ==> %4x\n", i, data); + + /* jump to the next set of regs */ + if (i == 0x07) + i = 0x0f; + + } /* end for loop */ +} /* end dump */ + +/***********************************************************/ +/* (Re)start autonegotiation */ +/***********************************************************/ +int phy_setup_aneg (char *devname, unsigned char addr) +{ + u16 bmcr; + +#if defined(CONFIG_PHY_DYNAMIC_ANEG) + /* + * Set up advertisement based on capablilities reported by the PHY. + * This should work for both copper and fiber. + */ + u16 bmsr; +#if defined(CONFIG_PHY_GIGE) + u16 exsr = 0x0000; +#endif + + miiphy_read (devname, addr, PHY_BMSR, &bmsr); + +#if defined(CONFIG_PHY_GIGE) + if (bmsr & PHY_BMSR_EXT_STAT) + miiphy_read (devname, addr, PHY_EXSR, &exsr); + + if (exsr & (PHY_EXSR_1000XF | PHY_EXSR_1000XH)) { + /* 1000BASE-X */ + u16 anar = 0x0000; + + if (exsr & PHY_EXSR_1000XF) + anar |= PHY_X_ANLPAR_FD; + + if (exsr & PHY_EXSR_1000XH) + anar |= PHY_X_ANLPAR_HD; + + miiphy_write (devname, addr, PHY_ANAR, anar); + } else +#endif + { + u16 anar, btcr; + + miiphy_read (devname, addr, PHY_ANAR, &anar); + anar &= ~(0x5000 | PHY_ANLPAR_T4 | PHY_ANLPAR_TXFD | + PHY_ANLPAR_TX | PHY_ANLPAR_10FD | PHY_ANLPAR_10); + + miiphy_read (devname, addr, PHY_1000BTCR, &btcr); + btcr &= ~(0x00FF | PHY_1000BTCR_1000FD | PHY_1000BTCR_1000HD); + + if (bmsr & PHY_BMSR_100T4) + anar |= PHY_ANLPAR_T4; + + if (bmsr & PHY_BMSR_100TXF) + anar |= PHY_ANLPAR_TXFD; + + if (bmsr & PHY_BMSR_100TXH) + anar |= PHY_ANLPAR_TX; + + if (bmsr & PHY_BMSR_10TF) + anar |= PHY_ANLPAR_10FD; + + if (bmsr & PHY_BMSR_10TH) + anar |= PHY_ANLPAR_10; + + miiphy_write (devname, addr, PHY_ANAR, anar); + +#if defined(CONFIG_PHY_GIGE) + if (exsr & PHY_EXSR_1000TF) + btcr |= PHY_1000BTCR_1000FD; + + if (exsr & PHY_EXSR_1000TH) + btcr |= PHY_1000BTCR_1000HD; + + miiphy_write (devname, addr, PHY_1000BTCR, btcr); +#endif + } + +#else /* defined(CONFIG_PHY_DYNAMIC_ANEG) */ + /* + * Set up standard advertisement + */ + u16 adv; + + miiphy_read (devname, addr, PHY_ANAR, &adv); + adv |= (PHY_ANLPAR_ACK | PHY_ANLPAR_TXFD | PHY_ANLPAR_TX | + PHY_ANLPAR_10FD | PHY_ANLPAR_10); + miiphy_write (devname, addr, PHY_ANAR, adv); + + miiphy_read (devname, addr, PHY_1000BTCR, &adv); + adv |= (0x0300); + miiphy_write (devname, addr, PHY_1000BTCR, adv); + +#endif /* defined(CONFIG_PHY_DYNAMIC_ANEG) */ + + /* Start/Restart aneg */ + miiphy_read (devname, addr, PHY_BMCR, &bmcr); + bmcr |= (PHY_BMCR_AUTON | PHY_BMCR_RST_NEG); + miiphy_write (devname, addr, PHY_BMCR, bmcr); + + return 0; +} + +/***********************************************************/ +/* read a phy reg and return the value with a rc */ +/***********************************************************/ +/* AMCC_TODO: + * Find out of the choice for the emac for MDIO is from the bridges, + * i.e. ZMII or RGMII as approporiate. If the bridges are not used + * to determine the emac for MDIO, then is the SDR0_ETH_CFG[MDIO_SEL] + * used? If so, then this routine below does not apply to the 460EX/GT. + * + * sr: Currently on 460EX only EMAC0 works with MDIO, so we always + * return EMAC0 offset here + * vg: For 460EX/460GT if internal GPCS PHY address is specified + * return appropriate EMAC offset + */ +unsigned int miiphy_getemac_offset(u8 addr) +{ +#if (defined(CONFIG_440) && \ + !defined(CONFIG_440SP) && !defined(CONFIG_440SPE) && \ + !defined(CONFIG_460EX) && !defined(CONFIG_460GT)) && \ + defined(CONFIG_NET_MULTI) + unsigned long zmii; + unsigned long eoffset; + + /* Need to find out which mdi port we're using */ + zmii = in_be32((void *)ZMII0_FER); + + if (zmii & (ZMII_FER_MDI << ZMII_FER_V (0))) + /* using port 0 */ + eoffset = 0; + + else if (zmii & (ZMII_FER_MDI << ZMII_FER_V (1))) + /* using port 1 */ + eoffset = 0x100; + + else if (zmii & (ZMII_FER_MDI << ZMII_FER_V (2))) + /* using port 2 */ + eoffset = 0x400; + + else if (zmii & (ZMII_FER_MDI << ZMII_FER_V (3))) + /* using port 3 */ + eoffset = 0x600; + + else { + /* None of the mdi ports are enabled! */ + /* enable port 0 */ + zmii |= ZMII_FER_MDI << ZMII_FER_V (0); + out_be32((void *)ZMII0_FER, zmii); + eoffset = 0; + /* need to soft reset port 0 */ + zmii = in_be32((void *)EMAC0_MR0); + zmii |= EMAC_MR0_SRST; + out_be32((void *)EMAC0_MR0, zmii); + } + + return (eoffset); +#else + +#if defined(CONFIG_NET_MULTI) && defined(CONFIG_405EX) + unsigned long rgmii; + int devnum = 1; + + rgmii = in_be32((void *)RGMII_FER); + if (rgmii & (1 << (19 - devnum))) + return 0x100; +#endif + +#if defined(CONFIG_460EX) || defined(CONFIG_460GT) + u32 eoffset = 0; + + switch (addr) { +#if defined(CONFIG_HAS_ETH1) && defined(CONFIG_GPCS_PHY1_ADDR) + case CONFIG_GPCS_PHY1_ADDR: + if (addr == EMAC_MR1_IPPA_GET(in_be32((void *)EMAC0_MR1 + 0x100))) + eoffset = 0x100; + break; +#endif +#if defined(CONFIG_HAS_ETH2) && defined(CONFIG_GPCS_PHY2_ADDR) + case CONFIG_GPCS_PHY2_ADDR: + if (addr == EMAC_MR1_IPPA_GET(in_be32((void *)EMAC0_MR1 + 0x300))) + eoffset = 0x300; + break; +#endif +#if defined(CONFIG_HAS_ETH3) && defined(CONFIG_GPCS_PHY3_ADDR) + case CONFIG_GPCS_PHY3_ADDR: + if (addr == EMAC_MR1_IPPA_GET(in_be32((void *)EMAC0_MR1 + 0x400))) + eoffset = 0x400; + break; +#endif + default: + eoffset = 0; + break; + } + return eoffset; +#endif + + return 0; +#endif +} + +static int emac_miiphy_wait(u32 emac_reg) +{ + u32 sta_reg; + int i; + + /* wait for completion */ + i = 0; + do { + sta_reg = in_be32((void *)EMAC0_STACR + emac_reg); + if (i++ > 5) { + debug("%s [%d]: Timeout! EMAC0_STACR=0x%0x\n", __func__, + __LINE__, sta_reg); + return -1; + } + udelay(10); + } while ((sta_reg & EMAC_STACR_OC) == EMAC_STACR_OC_MASK); + + return 0; +} + +static int emac_miiphy_command(u8 addr, u8 reg, int cmd, u16 value) +{ + u32 emac_reg; + u32 sta_reg; + + emac_reg = miiphy_getemac_offset(addr); + + /* wait for completion */ + if (emac_miiphy_wait(emac_reg) != 0) + return -1; + + sta_reg = reg; /* reg address */ + + /* set clock (50MHz) and read flags */ +#if defined(CONFIG_440GX) || defined(CONFIG_440SPE) || \ + defined(CONFIG_440EPX) || defined(CONFIG_440GRX) || \ + defined(CONFIG_460EX) || defined(CONFIG_460GT) || \ + defined(CONFIG_405EX) +#if defined(CONFIG_IBM_EMAC4_V4) /* EMAC4 V4 changed bit setting */ + sta_reg = (sta_reg & ~EMAC_STACR_OP_MASK) | cmd; +#else + sta_reg |= cmd; +#endif +#else + sta_reg = (sta_reg | cmd) & ~EMAC_STACR_CLK_100MHZ; +#endif + + /* Some boards (mainly 405EP based) define the PHY clock freqency fixed */ + sta_reg = sta_reg | CONFIG_PHY_CLK_FREQ; + sta_reg = sta_reg | ((u32)addr << 5); /* Phy address */ + sta_reg = sta_reg | EMAC_STACR_OC_MASK; /* new IBM emac v4 */ + if (cmd == EMAC_STACR_WRITE) + memcpy(&sta_reg, &value, 2); /* put in data */ + + out_be32((void *)EMAC0_STACR + emac_reg, sta_reg); + debug("%s [%d]: sta_reg=%08x\n", __func__, __LINE__, sta_reg); + + /* wait for completion */ + if (emac_miiphy_wait(emac_reg) != 0) + return -1; + + debug("%s [%d]: sta_reg=%08x\n", __func__, __LINE__, sta_reg); + if ((sta_reg & EMAC_STACR_PHYE) != 0) + return -1; + + return 0; +} + +int emac4xx_miiphy_read (char *devname, unsigned char addr, unsigned char reg, + unsigned short *value) +{ + unsigned long sta_reg; + unsigned long emac_reg; + + emac_reg = miiphy_getemac_offset(addr); + + if (emac_miiphy_command(addr, reg, EMAC_STACR_READ, 0) != 0) + return -1; + + sta_reg = in_be32((void *)EMAC0_STACR + emac_reg); + *value = sta_reg >> 16; + + return 0; +} + +/***********************************************************/ +/* write a phy reg and return the value with a rc */ +/***********************************************************/ + +int emac4xx_miiphy_write (char *devname, unsigned char addr, unsigned char reg, + unsigned short value) +{ + return emac_miiphy_command(addr, reg, EMAC_STACR_WRITE, value); +} diff --git a/arch/powerpc/cpu/ppc4xx/reginfo.c b/arch/powerpc/cpu/ppc4xx/reginfo.c new file mode 100644 index 0000000..a975667 --- /dev/null +++ b/arch/powerpc/cpu/ppc4xx/reginfo.c @@ -0,0 +1,370 @@ +/* + *(C) Copyright 2005-2009 Netstal Maschinen AG + * Bruno Hars (Bruno.Hars@netstal.com) + * Niklaus Giger (Niklaus.Giger@netstal.com) + * + * This source code is free software; you can redistribute it + * and/or modify it in source code form 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 + */ + +/* + * reginfo.c - register dump of HW-configuratin register for PPC4xx based board + */ + +#include <common.h> +#include <command.h> +#include <asm/processor.h> +#include <asm/io.h> +#include <asm/ppc4xx-uic.h> +#include <ppc4xx_enet.h> + +enum REGISTER_TYPE { + IDCR1, /* Indirectly Accessed DCR via SDRAM0_CFGADDR/SDRAM0_CFGDATA */ + IDCR2, /* Indirectly Accessed DCR via EBC0_CFGADDR/EBC0_CFGDATA */ + IDCR3, /* Indirectly Accessed DCR via EBM0_CFGADDR/EBM0_CFGDATA */ + IDCR4, /* Indirectly Accessed DCR via PPM0_CFGADDR/PPM0_CFGDATA */ + IDCR5, /* Indirectly Accessed DCR via CPR0_CFGADDR/CPR0_CFGDATA */ + IDCR6, /* Indirectly Accessed DCR via SDR0_CFGADDR/SDR0_CFGDATA */ + MM /* Directly Accessed MMIO Register */ +}; + +struct cpu_register { + char *name; + enum REGISTER_TYPE type; + u32 address; +}; + +/* + * PPC440EPx registers ordered for output + * name type addr size + * ------------------------------------------- + */ + +const struct cpu_register ppc4xx_reg[] = { + {"PB0CR", IDCR2, PB0CR}, + {"PB0AP", IDCR2, PB0AP}, + {"PB1CR", IDCR2, PB1CR}, + {"PB1AP", IDCR2, PB1AP}, + {"PB2CR", IDCR2, PB2CR}, + {"PB2AP", IDCR2, PB2AP}, + {"PB3CR", IDCR2, PB3CR}, + {"PB3AP", IDCR2, PB3AP}, + + {"PB4CR", IDCR2, PB4CR}, + {"PB4AP", IDCR2, PB4AP}, +#if !defined(CONFIG_405EP) + {"PB5CR", IDCR2, PB5CR}, + {"PB5AP", IDCR2, PB5AP}, + {"PB6CR", IDCR2, PB6CR}, + {"PB6AP", IDCR2, PB6AP}, + {"PB7CR", IDCR2, PB7CR}, + {"PB7AP", IDCR2, PB7AP}, +#endif + + {"PBEAR", IDCR2, PBEAR}, +#if defined(CONFIG_405EP) || defined (CONFIG_405GP) + {"PBESR0", IDCR2, PBESR0}, + {"PBESR1", IDCR2, PBESR1}, +#endif + {"EBC0_CFG", IDCR2, EBC0_CFG}, + +#ifdef CONFIG_405GP + {"SDRAM0_BESR0", IDCR1, SDRAM0_BESR0}, + {"SDRAM0_BESRS0", IDCR1, SDRAM0_BESRS0}, + {"SDRAM0_BESR1", IDCR1, SDRAM0_BESR1}, + {"SDRAM0_BESRS1", IDCR1, SDRAM0_BESRS1}, + {"SDRAM0_BEAR", IDCR1, SDRAM0_BEAR}, + {"SDRAM0_CFG", IDCR1, SDRAM0_CFG}, + {"SDRAM0_RTR", IDCR1, SDRAM0_RTR}, + {"SDRAM0_PMIT", IDCR1, SDRAM0_PMIT}, + + {"SDRAM0_B0CR", IDCR1, SDRAM0_B0CR}, + {"SDRAM0_B1CR", IDCR1, SDRAM0_B1CR}, + {"SDRAM0_B2CR", IDCR1, SDRAM0_B2CR}, + {"SDRAM0_B3CR", IDCR1, SDRAM0_B1CR}, + {"SDRAM0_TR", IDCR1, SDRAM0_TR}, + {"SDRAM0_ECCCFG", IDCR1, SDRAM0_B1CR}, + {"SDRAM0_ECCESR", IDCR1, SDRAM0_ECCESR}, + + +#endif + +#ifdef CONFIG_440EPX + {"SDR0_SDSTP0", IDCR6, SDR0_SDSTP0}, + {"SDR0_SDSTP1", IDCR6, SDR0_SDSTP1}, + {"SDR0_SDSTP2", IDCR6, SDR0_SDSTP2}, + {"SDR0_SDSTP3", IDCR6, SDR0_SDSTP3}, + {"SDR0_CUST0", IDCR6, SDR0_CUST0}, + {"SDR0_CUST1", IDCR6, SDR0_CUST1}, + {"SDR0_EBC0", IDCR6, SDR0_EBC0}, + {"SDR0_AMP0", IDCR6, SD0_AMP0}, + {"SDR0_AMP1", IDCR6, SD0_AMP1}, + {"SDR0_CP440", IDCR6, SDR0_CP440}, + {"SDR0_CRYP0", IDCR6, SDR0_CRYP0}, + {"SDR0_DDRCFG", IDCR6, SDR0_DDRCFG}, + {"SDR0_EMAC0RXST", IDCR6, SDR0_EMAC0RXST}, + {"SDR0_EMAC0TXST", IDCR6, SDR0_EMAC0TXST}, + {"SDR0_MFR", IDCR6, SDR0_MFR}, + {"SDR0_PCI0", IDCR6, SDR0_PCI0}, + {"SDR0_PFC0", IDCR6, SDR0_PFC0}, + {"SDR0_PFC1", IDCR6, SDR0_PFC1}, + {"SDR0_PFC2", IDCR6, SDR0_PFC2}, + {"SDR0_PFC4", IDCR6, SDR0_PFC4}, + {"SDR0_UART0", IDCR6, SDR0_UART0}, + {"SDR0_UART1", IDCR6, SDR0_UART1}, + {"SDR0_UART2", IDCR6, SDR0_UART2}, + {"SDR0_UART3", IDCR6, SDR0_UART3}, + {"DDR0_02", IDCR1, DDR0_02}, + {"DDR0_00", IDCR1, DDR0_00}, + {"DDR0_01", IDCR1, DDR0_01}, + {"DDR0_03", IDCR1, DDR0_03}, + {"DDR0_04", IDCR1, DDR0_04}, + {"DDR0_05", IDCR1, DDR0_05}, + {"DDR0_06", IDCR1, DDR0_06}, + {"DDR0_07", IDCR1, DDR0_07}, + {"DDR0_08", IDCR1, DDR0_08}, + {"DDR0_09", IDCR1, DDR0_09}, + {"DDR0_10", IDCR1, DDR0_10}, + {"DDR0_11", IDCR1, DDR0_11}, + {"DDR0_12", IDCR1, DDR0_12}, + {"DDR0_14", IDCR1, DDR0_14}, + {"DDR0_17", IDCR1, DDR0_17}, + {"DDR0_18", IDCR1, DDR0_18}, + {"DDR0_19", IDCR1, DDR0_19}, + {"DDR0_20", IDCR1, DDR0_20}, + {"DDR0_21", IDCR1, DDR0_21}, + {"DDR0_22", IDCR1, DDR0_22}, + {"DDR0_23", IDCR1, DDR0_23}, + {"DDR0_24", IDCR1, DDR0_24}, + {"DDR0_25", IDCR1, DDR0_25}, + {"DDR0_26", IDCR1, DDR0_26}, + {"DDR0_27", IDCR1, DDR0_27}, + {"DDR0_28", IDCR1, DDR0_28}, + {"DDR0_31", IDCR1, DDR0_31}, + {"DDR0_32", IDCR1, DDR0_32}, + {"DDR0_33", IDCR1, DDR0_33}, + {"DDR0_34", IDCR1, DDR0_34}, + {"DDR0_35", IDCR1, DDR0_35}, + {"DDR0_36", IDCR1, DDR0_36}, + {"DDR0_37", IDCR1, DDR0_37}, + {"DDR0_38", IDCR1, DDR0_38}, + {"DDR0_39", IDCR1, DDR0_39}, + {"DDR0_40", IDCR1, DDR0_40}, + {"DDR0_41", IDCR1, DDR0_41}, + {"DDR0_42", IDCR1, DDR0_42}, + {"DDR0_43", IDCR1, DDR0_43}, + {"DDR0_44", IDCR1, DDR0_44}, + {"CPR0_ICFG", IDCR5, CPR0_ICFG}, + {"CPR0_MALD", IDCR5, CPR0_MALD}, + {"CPR0_OPBD00", IDCR5, CPR0_OPBD0}, + {"CPR0_PERD0", IDCR5, CPR0_PERD}, + {"CPR0_PLLC0", IDCR5, CPR0_PLLC}, + {"CPR0_PLLD0", IDCR5, CPR0_PLLD}, + {"CPR0_PRIMAD0", IDCR5, CPR0_PRIMAD0}, + {"CPR0_PRIMBD0", IDCR5, CPR0_PRIMBD0}, + {"CPR0_SPCID", IDCR5, CPR0_SPCID}, + {"SPI0_MODE", MM, SPI0_MODE}, + {"IIC0_CLKDIV", MM, PCIL0_PMM1MA}, + {"PCIL0_PMM0MA", MM, PCIL0_PMM0MA}, + {"PCIL0_PMM1MA", MM, PCIL0_PMM1MA}, + {"PCIL0_PTM1LA", MM, PCIL0_PMM1MA}, + {"PCIL0_PTM1MS", MM, PCIL0_PTM1MS}, + {"PCIL0_PTM2LA", MM, PCIL0_PMM1MA}, + {"PCIL0_PTM2MS", MM, PCIL0_PTM2MS}, + {"ZMII0_FER", MM, ZMII0_FER}, + {"ZMII0_SSR", MM, ZMII0_SSR}, + {"EMAC0_IPGVR", MM, EMAC0_IPGVR}, + {"EMAC0_MR1", MM, EMAC0_MR1}, + {"EMAC0_PTR", MM, EMAC0_PTR}, + {"EMAC0_RWMR", MM, EMAC0_RWMR}, + {"EMAC0_STACR", MM, EMAC0_STACR}, + {"EMAC0_TMR0", MM, EMAC0_TMR0}, + {"EMAC0_TMR1", MM, EMAC0_TMR1}, + {"EMAC0_TRTR", MM, EMAC0_TRTR}, + {"EMAC1_MR1", MM, EMAC1_MR1}, + {"GPIO0_OR", MM, GPIO0_OR}, + {"GPIO1_OR", MM, GPIO1_OR}, + {"GPIO0_TCR", MM, GPIO0_TCR}, + {"GPIO1_TCR", MM, GPIO1_TCR}, + {"GPIO0_ODR", MM, GPIO0_ODR}, + {"GPIO1_ODR", MM, GPIO1_ODR}, + {"GPIO0_OSRL", MM, GPIO0_OSRL}, + {"GPIO0_OSRH", MM, GPIO0_OSRH}, + {"GPIO1_OSRL", MM, GPIO1_OSRL}, + {"GPIO1_OSRH", MM, GPIO1_OSRH}, + {"GPIO0_TSRL", MM, GPIO0_TSRL}, + {"GPIO0_TSRH", MM, GPIO0_TSRH}, + {"GPIO1_TSRL", MM, GPIO1_TSRL}, + {"GPIO1_TSRH", MM, GPIO1_TSRH}, + {"GPIO0_IR", MM, GPIO0_IR}, + {"GPIO1_IR", MM, GPIO1_IR}, + {"GPIO0_ISR1L", MM, GPIO0_ISR1L}, + {"GPIO0_ISR1H", MM, GPIO0_ISR1H}, + {"GPIO1_ISR1L", MM, GPIO1_ISR1L}, + {"GPIO1_ISR1H", MM, GPIO1_ISR1H}, + {"GPIO0_ISR2L", MM, GPIO0_ISR2L}, + {"GPIO0_ISR2H", MM, GPIO0_ISR2H}, + {"GPIO1_ISR2L", MM, GPIO1_ISR2L}, + {"GPIO1_ISR2H", MM, GPIO1_ISR2H}, + {"GPIO0_ISR3L", MM, GPIO0_ISR3L}, + {"GPIO0_ISR3H", MM, GPIO0_ISR3H}, + {"GPIO1_ISR3L", MM, GPIO1_ISR3L}, + {"GPIO1_ISR3H", MM, GPIO1_ISR3H}, + {"SDR0_USB2PHY0CR", IDCR6, SDR0_USB2PHY0CR}, + {"SDR0_USB2H0CR", IDCR6, SDR0_USB2H0CR}, + {"SDR0_USB2D0CR", IDCR6, SDR0_USB2D0CR}, +#endif +}; + +/* + * CPU Register dump of PPC4xx HW configuration registers + * Output: first all DCR-registers, then in order of struct ppc4xx_reg + */ +#define PRINT_DCR(dcr) printf("0x%08x %-16s: 0x%08x\n", dcr,#dcr, mfdcr(dcr)); + +void ppc4xx_reginfo(void) +{ + unsigned int i; + unsigned int n; + u32 value; + enum REGISTER_TYPE type; +#if defined (CONFIG_405EP) + printf("Dump PPC405EP HW configuration registers\n\n"); +#elif CONFIG_405GP + printf ("Dump 405GP HW configuration registers\n\n"); +#elif CONFIG_440EPX + printf("Dump PPC440EPx HW configuration registers\n\n"); +#endif + printf("MSR: 0x%08x\n", mfmsr()); + + printf ("\nUniversal Interrupt Controller Regs\n"); + PRINT_DCR(UIC0SR); + PRINT_DCR(UIC0ER); + PRINT_DCR(UIC0CR); + PRINT_DCR(UIC0PR); + PRINT_DCR(UIC0TR); + PRINT_DCR(UIC0MSR); + PRINT_DCR(UIC0VR); + PRINT_DCR(UIC0VCR); + +#if (UIC_MAX > 1) + PRINT_DCR(UIC2SR); + PRINT_DCR(UIC2ER); + PRINT_DCR(UIC2CR); + PRINT_DCR(UIC2PR); + PRINT_DCR(UIC2TR); + PRINT_DCR(UIC2MSR); + PRINT_DCR(UIC2VR); + PRINT_DCR(UIC2VCR); +#endif + +#if (UIC_MAX > 2) + PRINT_DCR(UIC2SR); + PRINT_DCR(UIC2ER); + PRINT_DCR(UIC2CR); + PRINT_DCR(UIC2PR); + PRINT_DCR(UIC2TR); + PRINT_DCR(UIC2MSR); + PRINT_DCR(UIC2VR); + PRINT_DCR(UIC2VCR); +#endif + +#if (UIC_MAX > 3) + PRINT_DCR(UIC3SR); + PRINT_DCR(UIC3ER); + PRINT_DCR(UIC3CR); + PRINT_DCR(UIC3PR); + PRINT_DCR(UIC3TR); + PRINT_DCR(UIC3MSR); + PRINT_DCR(UIC3VR); + PRINT_DCR(UIC3VCR); +#endif + +#if defined (CONFIG_405EP) || defined (CONFIG_405GP) + printf ("\n\nDMA Channels\n"); + PRINT_DCR(DMASR); + PRINT_DCR(DMASGC); + PRINT_DCR(DMAADR); + + PRINT_DCR(DMACR0); + PRINT_DCR(DMACT0); + PRINT_DCR(DMADA0); + PRINT_DCR(DMASA0); + PRINT_DCR(DMASB0); + + PRINT_DCR(DMACR1); + PRINT_DCR(DMACT1); + PRINT_DCR(DMADA1); + PRINT_DCR(DMASA1); + PRINT_DCR(DMASB1); + + PRINT_DCR(DMACR2); + PRINT_DCR(DMACT2); + PRINT_DCR(DMADA2); + PRINT_DCR(DMASA2); + PRINT_DCR(DMASB2); + + PRINT_DCR(DMACR3); + PRINT_DCR(DMACT3); + PRINT_DCR(DMADA3); + PRINT_DCR(DMASA3); + PRINT_DCR(DMASB3); +#endif + + printf ("\n\nVarious HW-Configuration registers\n"); +#if defined (CONFIG_440EPX) + PRINT_DCR(MAL0_CFG); + PRINT_DCR(CPM0_ER); + PRINT_DCR(CPM1_ER); + PRINT_DCR(PLB4A0_ACR); + PRINT_DCR(PLB4A1_ACR); + PRINT_DCR(PLB3A0_ACR); + PRINT_DCR(OPB2PLB40_BCTRL); + PRINT_DCR(P4P3BO0_CFG); +#endif + n = sizeof(ppc4xx_reg) / sizeof(ppc4xx_reg[0]); + for (i = 0; i < n; i++) { + value = 0; + type = ppc4xx_reg[i].type; + switch (type) { + case IDCR1: /* Indirect via SDRAM0_CFGADDR/DDR0_CFGDATA */ + mtdcr(SDRAM0_CFGADDR, ppc4xx_reg[i].address); + value = mfdcr(SDRAM0_CFGDATA); + break; + case IDCR2: /* Indirect via EBC0_CFGADDR/EBC0_CFGDATA */ + mtdcr(EBC0_CFGADDR, ppc4xx_reg[i].address); + value = mfdcr(EBC0_CFGDATA); + break; + case IDCR5: /* Indirect via CPR0_CFGADDR/CPR0_CFGDATA */ + mtdcr(CPR0_CFGADDR, ppc4xx_reg[i].address); + value = mfdcr(CPR0_CFGDATA); + break; + case IDCR6: /* Indirect via SDR0_CFGADDR/SDR0_CFGDATA */ + mtdcr(SDR0_CFGADDR, ppc4xx_reg[i].address); + value = mfdcr(SDR0_CFGDATA); + break; + case MM: /* Directly Accessed MMIO Register */ + value = in_be32((const volatile unsigned __iomem *) + ppc4xx_reg[i].address); + break; + default: + printf("\nERROR: struct entry %d: unknown register" + "type\n", i); + break; + } + printf("0x%08x %-16s: 0x%08x\n",ppc4xx_reg[i].address, + ppc4xx_reg[i].name, value); + } +} diff --git a/arch/powerpc/cpu/ppc4xx/resetvec.S b/arch/powerpc/cpu/ppc4xx/resetvec.S new file mode 100644 index 0000000..b3308bd --- /dev/null +++ b/arch/powerpc/cpu/ppc4xx/resetvec.S @@ -0,0 +1,12 @@ +/* Copyright MontaVista Software Incorporated, 2000 */ +#include <config.h> + .section .resetvec,"ax" +#if defined(CONFIG_440) + b _start_440 +#else +#if defined(CONFIG_BOOT_PCI) && defined(CONFIG_MIP405) + b _start_pci +#else + b _start +#endif +#endif diff --git a/arch/powerpc/cpu/ppc4xx/sdram.c b/arch/powerpc/cpu/ppc4xx/sdram.c new file mode 100644 index 0000000..30c6e0e --- /dev/null +++ b/arch/powerpc/cpu/ppc4xx/sdram.c @@ -0,0 +1,468 @@ +/* + * (C) Copyright 2005-2007 + * Stefan Roese, DENX Software Engineering, sr@denx.de. + * + * (C) Copyright 2006 + * DAVE Srl <www.dave-tech.it> + * + * (C) Copyright 2002-2004 + * Stefan Roese, esd gmbh germany, stefan.roese@esd-electronics.com + * + * 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> +#include <ppc4xx.h> +#include <asm/processor.h> +#include "sdram.h" +#include "ecc.h" + +#ifdef CONFIG_SDRAM_BANK0 + +#ifndef CONFIG_440 + +#ifndef CONFIG_SYS_SDRAM_TABLE +sdram_conf_t mb0cf[] = { + {(128 << 20), 13, 0x000A4001}, /* (0-128MB) Address Mode 3, 13x10(4) */ + {(64 << 20), 13, 0x00084001}, /* (0-64MB) Address Mode 3, 13x9(4) */ + {(32 << 20), 12, 0x00062001}, /* (0-32MB) Address Mode 2, 12x9(4) */ + {(16 << 20), 12, 0x00046001}, /* (0-16MB) Address Mode 4, 12x8(4) */ + {(4 << 20), 11, 0x00008001}, /* (0-4MB) Address Mode 5, 11x8(2) */ +}; +#else +sdram_conf_t mb0cf[] = CONFIG_SYS_SDRAM_TABLE; +#endif + +#define N_MB0CF (sizeof(mb0cf) / sizeof(mb0cf[0])) + +#ifdef CONFIG_SYS_SDRAM_CASL +static ulong ns2clks(ulong ns) +{ + ulong bus_period_x_10 = ONE_BILLION / (get_bus_freq(0) / 10); + + return ((ns * 10) + bus_period_x_10) / bus_period_x_10; +} +#endif /* CONFIG_SYS_SDRAM_CASL */ + +static ulong compute_sdtr1(ulong speed) +{ +#ifdef CONFIG_SYS_SDRAM_CASL + ulong tmp; + ulong sdtr1 = 0; + + /* CASL */ + if (CONFIG_SYS_SDRAM_CASL < 2) + sdtr1 |= (1 << SDRAM0_TR_CASL); + else + if (CONFIG_SYS_SDRAM_CASL > 4) + sdtr1 |= (3 << SDRAM0_TR_CASL); + else + sdtr1 |= ((CONFIG_SYS_SDRAM_CASL-1) << SDRAM0_TR_CASL); + + /* PTA */ + tmp = ns2clks(CONFIG_SYS_SDRAM_PTA); + if ((tmp >= 2) && (tmp <= 4)) + sdtr1 |= ((tmp-1) << SDRAM0_TR_PTA); + else + sdtr1 |= ((4-1) << SDRAM0_TR_PTA); + + /* CTP */ + tmp = ns2clks(CONFIG_SYS_SDRAM_CTP); + if ((tmp >= 2) && (tmp <= 4)) + sdtr1 |= ((tmp-1) << SDRAM0_TR_CTP); + else + sdtr1 |= ((4-1) << SDRAM0_TR_CTP); + + /* LDF */ + tmp = ns2clks(CONFIG_SYS_SDRAM_LDF); + if ((tmp >= 2) && (tmp <= 4)) + sdtr1 |= ((tmp-1) << SDRAM0_TR_LDF); + else + sdtr1 |= ((2-1) << SDRAM0_TR_LDF); + + /* RFTA */ + tmp = ns2clks(CONFIG_SYS_SDRAM_RFTA); + if ((tmp >= 4) && (tmp <= 10)) + sdtr1 |= ((tmp-4) << SDRAM0_TR_RFTA); + else + sdtr1 |= ((10-4) << SDRAM0_TR_RFTA); + + /* RCD */ + tmp = ns2clks(CONFIG_SYS_SDRAM_RCD); + if ((tmp >= 2) && (tmp <= 4)) + sdtr1 |= ((tmp-1) << SDRAM0_TR_RCD); + else + sdtr1 |= ((4-1) << SDRAM0_TR_RCD); + + return sdtr1; +#else /* CONFIG_SYS_SDRAM_CASL */ + /* + * If no values are configured in the board config file + * use the default values, which seem to be ok for most + * boards. + * + * REMARK: + * For new board ports we strongly recommend to define the + * correct values for the used SDRAM chips in your board + * config file (see PPChameleonEVB.h) + */ + if (speed > 100000000) { + /* + * 133 MHz SDRAM + */ + return 0x01074015; + } else { + /* + * default: 100 MHz SDRAM + */ + return 0x0086400d; + } +#endif /* CONFIG_SYS_SDRAM_CASL */ +} + +/* refresh is expressed in ms */ +static ulong compute_rtr(ulong speed, ulong rows, ulong refresh) +{ +#ifdef CONFIG_SYS_SDRAM_CASL + ulong tmp; + + tmp = ((refresh*1000*1000) / (1 << rows)) * (speed / 1000); + tmp /= 1000000; + + return ((tmp & 0x00003FF8) << 16); +#else /* CONFIG_SYS_SDRAM_CASL */ + if (speed > 100000000) { + /* + * 133 MHz SDRAM + */ + return 0x07f00000; + } else { + /* + * default: 100 MHz SDRAM + */ + return 0x05f00000; + } +#endif /* CONFIG_SYS_SDRAM_CASL */ +} + +/* + * Autodetect onboard SDRAM on 405 platforms + */ +phys_size_t initdram(int board_type) +{ + ulong speed; + ulong sdtr1; + int i; + + /* + * Determine SDRAM speed + */ + speed = get_bus_freq(0); /* parameter not used on ppc4xx */ + + /* + * sdtr1 (register SDRAM0_TR) must take into account timings listed + * in SDRAM chip datasheet. rtr (register SDRAM0_RTR) must take into + * account actual SDRAM size. So we can set up sdtr1 according to what + * is specified in board configuration file while rtr dependds on SDRAM + * size we are assuming before detection. + */ + sdtr1 = compute_sdtr1(speed); + + for (i=0; i<N_MB0CF; i++) { + /* + * Disable memory controller. + */ + mtsdram(SDRAM0_CFG, 0x00000000); + + /* + * Set MB0CF for bank 0. + */ + mtsdram(SDRAM0_B0CR, mb0cf[i].reg); + mtsdram(SDRAM0_TR, sdtr1); + mtsdram(SDRAM0_RTR, compute_rtr(speed, mb0cf[i].rows, 64)); + + udelay(200); + + /* + * Set memory controller options reg, MCOPT1. + * Set DC_EN to '1' and BRD_PRF to '01' for 16 byte PLB Burst + * read/prefetch. + */ + mtsdram(SDRAM0_CFG, 0x80800000); + + udelay(10000); + + if (get_ram_size(0, mb0cf[i].size) == mb0cf[i].size) { + phys_size_t size = mb0cf[i].size; + + /* + * OK, size detected. Enable second bank if + * defined (assumes same type as bank 0) + */ +#ifdef CONFIG_SDRAM_BANK1 + mtsdram(SDRAM0_CFG, 0x00000000); + mtsdram(SDRAM0_B1CR, mb0cf[i].size | mb0cf[i].reg); + mtsdram(SDRAM0_CFG, 0x80800000); + udelay(10000); + + /* + * Check if 2nd bank is really available. + * If the size not equal to the size of the first + * bank, then disable the 2nd bank completely. + */ + if (get_ram_size((long *)mb0cf[i].size, mb0cf[i].size) != + mb0cf[i].size) { + mtsdram(SDRAM0_B1CR, 0); + mtsdram(SDRAM0_CFG, 0); + } else { + /* + * We have two identical banks, so the size + * is twice the bank size + */ + size = 2 * size; + } +#endif + + /* + * OK, size detected -> all done + */ + return size; + } + } + + return 0; +} + +#else /* CONFIG_440 */ + +/* + * Define some default values. Those can be overwritten in the + * board config file. + */ + +#ifndef CONFIG_SYS_SDRAM_TABLE +sdram_conf_t mb0cf[] = { + {(256 << 20), 13, 0x000C4001}, /* 256MB mode 3, 13x10(4) */ + {(128 << 20), 13, 0x000A4001}, /* 128MB mode 3, 13x10(4) */ + {(64 << 20), 12, 0x00082001} /* 64MB mode 2, 12x9(4) */ +}; +#else +sdram_conf_t mb0cf[] = CONFIG_SYS_SDRAM_TABLE; +#endif + +#ifndef CONFIG_SYS_SDRAM0_TR0 +#define CONFIG_SYS_SDRAM0_TR0 0x41094012 +#endif + +#ifndef CONFIG_SYS_SDRAM0_WDDCTR +#define CONFIG_SYS_SDRAM0_WDDCTR 0x00000000 /* wrcp=0 dcd=0 */ +#endif + +#ifndef CONFIG_SYS_SDRAM0_RTR +#define CONFIG_SYS_SDRAM0_RTR 0x04100000 /* 7.8us @ 133MHz PLB */ +#endif + +#ifndef CONFIG_SYS_SDRAM0_CFG0 +#define CONFIG_SYS_SDRAM0_CFG0 0x82000000 /* DCEN=1, PMUD=0, 64-bit */ +#endif + +#define N_MB0CF (sizeof(mb0cf) / sizeof(mb0cf[0])) + +#define NUM_TRIES 64 +#define NUM_READS 10 + +static void sdram_tr1_set(int ram_address, int* tr1_value) +{ + int i; + int j, k; + volatile unsigned int* ram_pointer = (unsigned int *)ram_address; + int first_good = -1, last_bad = 0x1ff; + + unsigned long test[NUM_TRIES] = { + 0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, + 0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000, + 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000, + 0xAAAAAAAA, 0xAAAAAAAA, 0x55555555, 0x55555555, + 0xAAAAAAAA, 0xAAAAAAAA, 0x55555555, 0x55555555, + 0x55555555, 0x55555555, 0xAAAAAAAA, 0xAAAAAAAA, + 0x55555555, 0x55555555, 0xAAAAAAAA, 0xAAAAAAAA, + 0xA5A5A5A5, 0xA5A5A5A5, 0x5A5A5A5A, 0x5A5A5A5A, + 0xA5A5A5A5, 0xA5A5A5A5, 0x5A5A5A5A, 0x5A5A5A5A, + 0x5A5A5A5A, 0x5A5A5A5A, 0xA5A5A5A5, 0xA5A5A5A5, + 0x5A5A5A5A, 0x5A5A5A5A, 0xA5A5A5A5, 0xA5A5A5A5, + 0xAA55AA55, 0xAA55AA55, 0x55AA55AA, 0x55AA55AA, + 0xAA55AA55, 0xAA55AA55, 0x55AA55AA, 0x55AA55AA, + 0x55AA55AA, 0x55AA55AA, 0xAA55AA55, 0xAA55AA55, + 0x55AA55AA, 0x55AA55AA, 0xAA55AA55, 0xAA55AA55 }; + + /* go through all possible SDRAM0_TR1[RDCT] values */ + for (i=0; i<=0x1ff; i++) { + /* set the current value for TR1 */ + mtsdram(SDRAM0_TR1, (0x80800800 | i)); + + /* write values */ + for (j=0; j<NUM_TRIES; j++) { + ram_pointer[j] = test[j]; + + /* clear any cache at ram location */ + __asm__("dcbf 0,%0": :"r" (&ram_pointer[j])); + } + + /* read values back */ + for (j=0; j<NUM_TRIES; j++) { + for (k=0; k<NUM_READS; k++) { + /* clear any cache at ram location */ + __asm__("dcbf 0,%0": :"r" (&ram_pointer[j])); + + if (ram_pointer[j] != test[j]) + break; + } + + /* read error */ + if (k != NUM_READS) + break; + } + + /* we have a SDRAM0_TR1[RDCT] that is part of the window */ + if (j == NUM_TRIES) { + if (first_good == -1) + first_good = i; /* found beginning of window */ + } else { /* bad read */ + /* if we have not had a good read then don't care */ + if (first_good != -1) { + /* first failure after a good read */ + last_bad = i-1; + break; + } + } + } + + /* return the current value for TR1 */ + *tr1_value = (first_good + last_bad) / 2; +} + +/* + * Autodetect onboard DDR SDRAM on 440 platforms + * + * NOTE: Some of the hardcoded values are hardware dependant, + * so this should be extended for other future boards + * using this routine! + */ +phys_size_t initdram(int board_type) +{ + int i; + int tr1_bank1; + +#if defined(CONFIG_440GX) || defined(CONFIG_440EP) || \ + defined(CONFIG_440GR) || defined(CONFIG_440SP) + /* + * Soft-reset SDRAM controller. + */ + mtsdr(SDR0_SRST, SDR0_SRST_DMC); + mtsdr(SDR0_SRST, 0x00000000); +#endif + + for (i=0; i<N_MB0CF; i++) { + /* + * Disable memory controller. + */ + mtsdram(SDRAM0_CFG0, 0x00000000); + + /* + * Setup some default + */ + mtsdram(SDRAM0_UABBA, 0x00000000); /* ubba=0 (default) */ + mtsdram(SDRAM0_SLIO, 0x00000000); /* rdre=0 wrre=0 rarw=0 */ + mtsdram(SDRAM0_DEVOPT, 0x00000000); /* dll=0 ds=0 (normal) */ + mtsdram(SDRAM0_WDDCTR, CONFIG_SYS_SDRAM0_WDDCTR); + mtsdram(SDRAM0_CLKTR, 0x40000000); /* clkp=1 (90 deg wr) dcdt=0 */ + + /* + * Following for CAS Latency = 2.5 @ 133 MHz PLB + */ + mtsdram(SDRAM0_B0CR, mb0cf[i].reg); + mtsdram(SDRAM0_TR0, CONFIG_SYS_SDRAM0_TR0); + mtsdram(SDRAM0_TR1, 0x80800800); /* SS=T2 SL=STAGE 3 CD=1 CT=0x00*/ + mtsdram(SDRAM0_RTR, CONFIG_SYS_SDRAM0_RTR); + mtsdram(SDRAM0_CFG1, 0x00000000); /* Self-refresh exit, disable PM*/ + udelay(400); /* Delay 200 usecs (min) */ + + /* + * Enable the controller, then wait for DCEN to complete + */ + mtsdram(SDRAM0_CFG0, CONFIG_SYS_SDRAM0_CFG0); + udelay(10000); + + if (get_ram_size(0, mb0cf[i].size) == mb0cf[i].size) { + phys_size_t size = mb0cf[i].size; + /* + * Optimize TR1 to current hardware environment + */ + sdram_tr1_set(0x00000000, &tr1_bank1); + mtsdram(SDRAM0_TR1, (tr1_bank1 | 0x80800800)); + + + /* + * OK, size detected. Enable second bank if + * defined (assumes same type as bank 0) + */ +#ifdef CONFIG_SDRAM_BANK1 + mtsdram(SDRAM0_CFG0, 0); + mtsdram(SDRAM0_B1CR, mb0cf[i].size | mb0cf[i].reg); + mtsdram(SDRAM0_CFG0, CONFIG_SYS_SDRAM0_CFG0); + udelay(10000); + + /* + * Check if 2nd bank is really available. + * If the size not equal to the size of the first + * bank, then disable the 2nd bank completely. + */ + if (get_ram_size((long *)mb0cf[i].size, mb0cf[i].size) + != mb0cf[i].size) { + mtsdram(SDRAM0_CFG0, 0); + mtsdram(SDRAM0_B1CR, 0); + mtsdram(SDRAM0_CFG0, CONFIG_SYS_SDRAM0_CFG0); + udelay(10000); + } else { + /* + * We have two identical banks, so the size + * is twice the bank size + */ + size = 2 * size; + } +#endif + +#ifdef CONFIG_SDRAM_ECC + ecc_init(0, size); +#endif + + /* + * OK, size detected -> all done + */ + return size; + } + } + + return 0; /* nothing found ! */ +} + +#endif /* CONFIG_440 */ + +#endif /* CONFIG_SDRAM_BANK0 */ diff --git a/arch/powerpc/cpu/ppc4xx/sdram.h b/arch/powerpc/cpu/ppc4xx/sdram.h new file mode 100644 index 0000000..bea3376 --- /dev/null +++ b/arch/powerpc/cpu/ppc4xx/sdram.h @@ -0,0 +1,76 @@ +/* + * (C) Copyright 2006 + * Stefan Roese, DENX Software Engineering, sr@denx.de. + * + * (C) Copyright 2006 + * DAVE Srl <www.dave-tech.it> + * + * 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 + */ + +#ifndef _SDRAM_H_ +#define _SDRAM_H_ + +#include <config.h> + +#define ONE_BILLION 1000000000 + +struct sdram_conf_s { + unsigned long size; + int rows; + unsigned long reg; +}; + +typedef struct sdram_conf_s sdram_conf_t; + +/* Bitfields offsets */ +#define SDRAM0_TR_CASL (31 - 8) +#define SDRAM0_TR_PTA (31 - 13) +#define SDRAM0_TR_CTP (31 - 15) +#define SDRAM0_TR_LDF (31 - 17) +#define SDRAM0_TR_RFTA (31 - 29) +#define SDRAM0_TR_RCD (31 - 31) + +#ifdef CONFIG_SYS_SDRAM_CL +/* SDRAM timings [ns] according to AMCC/IBM names (see SDRAM_faq.doc) */ +#define CONFIG_SYS_SDRAM_CASL CONFIG_SYS_SDRAM_CL +#define CONFIG_SYS_SDRAM_PTA CONFIG_SYS_SDRAM_tRP +#define CONFIG_SYS_SDRAM_CTP (CONFIG_SYS_SDRAM_tRC - CONFIG_SYS_SDRAM_tRCD - CONFIG_SYS_SDRAM_tRP) +#define CONFIG_SYS_SDRAM_LDF 0 +#ifdef CONFIG_SYS_SDRAM_tRFC +#define CONFIG_SYS_SDRAM_RFTA CONFIG_SYS_SDRAM_tRFC +#else +#define CONFIG_SYS_SDRAM_RFTA CONFIG_SYS_SDRAM_tRC +#endif +#define CONFIG_SYS_SDRAM_RCD CONFIG_SYS_SDRAM_tRCD +#endif /* #ifdef CONFIG_SYS_SDRAM_CL */ + +/* + * Some defines for the 440 DDR controller + */ +#define SDRAM_CFG0_DC_EN 0x80000000 /* SDRAM Controller Enable */ +#define SDRAM_CFG0_MEMCHK 0x30000000 /* Memory data error checking mask*/ +#define SDRAM_CFG0_MEMCHK_NON 0x00000000 /* No ECC generation */ +#define SDRAM_CFG0_MEMCHK_GEN 0x20000000 /* ECC generation */ +#define SDRAM_CFG0_MEMCHK_CHK 0x30000000 /* ECC generation and checking */ +#define SDRAM_CFG0_DRAMWDTH 0x02000000 /* DRAM width mask */ +#define SDRAM_CFG0_DRAMWDTH_32 0x00000000 /* 32 bits */ +#define SDRAM_CFG0_DRAMWDTH_64 0x02000000 /* 64 bits */ + +#endif diff --git a/arch/powerpc/cpu/ppc4xx/speed.c b/arch/powerpc/cpu/ppc4xx/speed.c new file mode 100644 index 0000000..906face --- /dev/null +++ b/arch/powerpc/cpu/ppc4xx/speed.c @@ -0,0 +1,1177 @@ +/* + * (C) Copyright 2000-2008 + * Wolfgang Denk, DENX Software Engineering, wd@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> +#include <ppc_asm.tmpl> +#include <ppc4xx.h> +#include <asm/processor.h> + +DECLARE_GLOBAL_DATA_PTR; + +#define ONE_BILLION 1000000000 +#ifdef DEBUG +#define DEBUGF(fmt,args...) printf(fmt ,##args) +#else +#define DEBUGF(fmt,args...) +#endif + +#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) + +#if defined(CONFIG_405GP) || defined(CONFIG_405CR) + +void get_sys_info (PPC4xx_SYS_INFO * sysInfo) +{ + unsigned long pllmr; + unsigned long sysClkPeriodPs = ONE_BILLION / (CONFIG_SYS_CLK_FREQ / 1000); + uint pvr = get_pvr(); + unsigned long psr; + unsigned long m; + + /* + * Read PLL Mode register + */ + pllmr = mfdcr (CPC0_PLLMR); + + /* + * Read Pin Strapping register + */ + psr = mfdcr (CPC0_PSR); + + /* + * Determine FWD_DIV. + */ + sysInfo->pllFwdDiv = 8 - ((pllmr & PLLMR_FWD_DIV_MASK) >> 29); + + /* + * Determine FBK_DIV. + */ + sysInfo->pllFbkDiv = ((pllmr & PLLMR_FB_DIV_MASK) >> 25); + if (sysInfo->pllFbkDiv == 0) { + sysInfo->pllFbkDiv = 16; + } + + /* + * Determine PLB_DIV. + */ + sysInfo->pllPlbDiv = ((pllmr & PLLMR_CPU_TO_PLB_MASK) >> 17) + 1; + + /* + * Determine PCI_DIV. + */ + sysInfo->pllPciDiv = ((pllmr & PLLMR_PCI_TO_PLB_MASK) >> 13) + 1; + + /* + * Determine EXTBUS_DIV. + */ + sysInfo->pllExtBusDiv = ((pllmr & PLLMR_EXB_TO_PLB_MASK) >> 11) + 2; + + /* + * Determine OPB_DIV. + */ + sysInfo->pllOpbDiv = ((pllmr & PLLMR_OPB_TO_PLB_MASK) >> 15) + 1; + + /* + * Check if PPC405GPr used (mask minor revision field) + */ + if ((pvr & 0xfffffff0) == (PVR_405GPR_RB & 0xfffffff0)) { + /* + * Determine FWD_DIV B (only PPC405GPr with new mode strapping). + */ + sysInfo->pllFwdDivB = 8 - (pllmr & PLLMR_FWDB_DIV_MASK); + + /* + * Determine factor m depending on PLL feedback clock source + */ + if (!(psr & PSR_PCI_ASYNC_EN)) { + if (psr & PSR_NEW_MODE_EN) { + /* + * sync pci clock used as feedback (new mode) + */ + m = 1 * sysInfo->pllFwdDivB * 2 * sysInfo->pllPciDiv; + } else { + /* + * sync pci clock used as feedback (legacy mode) + */ + m = 1 * sysInfo->pllFwdDivB * sysInfo->pllPlbDiv * sysInfo->pllPciDiv; + } + } else if (psr & PSR_NEW_MODE_EN) { + if (psr & PSR_PERCLK_SYNC_MODE_EN) { + /* + * PerClk used as feedback (new mode) + */ + m = 1 * sysInfo->pllFwdDivB * 2 * sysInfo->pllExtBusDiv; + } else { + /* + * CPU clock used as feedback (new mode) + */ + m = sysInfo->pllFbkDiv * sysInfo->pllFwdDiv; + } + } else if (sysInfo->pllExtBusDiv == sysInfo->pllFbkDiv) { + /* + * PerClk used as feedback (legacy mode) + */ + m = 1 * sysInfo->pllFwdDivB * sysInfo->pllPlbDiv * sysInfo->pllExtBusDiv; + } else { + /* + * PLB clock used as feedback (legacy mode) + */ + m = sysInfo->pllFbkDiv * sysInfo->pllFwdDivB * sysInfo->pllPlbDiv; + } + + sysInfo->freqVCOHz = (1000000000000LL * (unsigned long long)m) / + (unsigned long long)sysClkPeriodPs; + sysInfo->freqProcessor = sysInfo->freqVCOHz / sysInfo->pllFwdDiv; + sysInfo->freqPLB = sysInfo->freqVCOHz / (sysInfo->pllFwdDivB * sysInfo->pllPlbDiv); + } else { + /* + * Check pllFwdDiv to see if running in bypass mode where the CPU speed + * is equal to the 405GP SYS_CLK_FREQ. If not in bypass mode, check VCO + * to make sure it is within the proper range. + * spec: VCO = SYS_CLOCK x FBKDIV x PLBDIV x FWDDIV + * Note freqVCO is calculated in MHz to avoid errors introduced by rounding. + */ + if (sysInfo->pllFwdDiv == 1) { + sysInfo->freqProcessor = CONFIG_SYS_CLK_FREQ; + sysInfo->freqPLB = CONFIG_SYS_CLK_FREQ / sysInfo->pllPlbDiv; + } else { + sysInfo->freqVCOHz = ( 1000000000000LL * + (unsigned long long)sysInfo->pllFwdDiv * + (unsigned long long)sysInfo->pllFbkDiv * + (unsigned long long)sysInfo->pllPlbDiv + ) / (unsigned long long)sysClkPeriodPs; + sysInfo->freqPLB = (ONE_BILLION / ((sysClkPeriodPs * 10) / + sysInfo->pllFbkDiv)) * 10000; + sysInfo->freqProcessor = sysInfo->freqPLB * sysInfo->pllPlbDiv; + } + } + + sysInfo->freqOPB = sysInfo->freqPLB / sysInfo->pllOpbDiv; + sysInfo->freqEBC = sysInfo->freqPLB / sysInfo->pllExtBusDiv; + sysInfo->freqUART = sysInfo->freqProcessor; +} + + +/******************************************** + * get_PCI_freq + * return PCI bus freq in Hz + *********************************************/ +ulong get_PCI_freq (void) +{ + ulong val; + PPC4xx_SYS_INFO sys_info; + + get_sys_info (&sys_info); + val = sys_info.freqPLB / sys_info.pllPciDiv; + return val; +} + + +#elif defined(CONFIG_440) + +#if defined(CONFIG_460EX) || defined(CONFIG_460GT) || \ + defined(CONFIG_460SX) +static u8 pll_fwdv_multi_bits[] = { + /* values for: 1 - 16 */ + 0x00, 0x01, 0x0f, 0x04, 0x09, 0x0a, 0x0d, 0x0e, 0x03, 0x0c, + 0x05, 0x08, 0x07, 0x02, 0x0b, 0x06 +}; + +u32 get_cpr0_fwdv(unsigned long cpr_reg_fwdv) +{ + u32 index; + + for (index = 0; index < ARRAY_SIZE(pll_fwdv_multi_bits); index++) + if (cpr_reg_fwdv == (u32)pll_fwdv_multi_bits[index]) + return index + 1; + + return 0; +} + +static u8 pll_fbdv_multi_bits[] = { + /* values for: 1 - 100 */ + 0x00, 0xff, 0x7e, 0xfd, 0x7a, 0xf5, 0x6a, 0xd5, 0x2a, 0xd4, + 0x29, 0xd3, 0x26, 0xcc, 0x19, 0xb3, 0x67, 0xce, 0x1d, 0xbb, + 0x77, 0xee, 0x5d, 0xba, 0x74, 0xe9, 0x52, 0xa5, 0x4b, 0x96, + 0x2c, 0xd8, 0x31, 0xe3, 0x46, 0x8d, 0x1b, 0xb7, 0x6f, 0xde, + 0x3d, 0xfb, 0x76, 0xed, 0x5a, 0xb5, 0x6b, 0xd6, 0x2d, 0xdb, + 0x36, 0xec, 0x59, 0xb2, 0x64, 0xc9, 0x12, 0xa4, 0x48, 0x91, + 0x23, 0xc7, 0x0e, 0x9c, 0x38, 0xf0, 0x61, 0xc2, 0x05, 0x8b, + 0x17, 0xaf, 0x5f, 0xbe, 0x7c, 0xf9, 0x72, 0xe5, 0x4a, 0x95, + 0x2b, 0xd7, 0x2e, 0xdc, 0x39, 0xf3, 0x66, 0xcd, 0x1a, 0xb4, + 0x68, 0xd1, 0x22, 0xc4, 0x09, 0x93, 0x27, 0xcf, 0x1e, 0xbc, + /* values for: 101 - 200 */ + 0x78, 0xf1, 0x62, 0xc5, 0x0a, 0x94, 0x28, 0xd0, 0x21, 0xc3, + 0x06, 0x8c, 0x18, 0xb0, 0x60, 0xc1, 0x02, 0x84, 0x08, 0x90, + 0x20, 0xc0, 0x01, 0x83, 0x07, 0x8f, 0x1f, 0xbf, 0x7f, 0xfe, + 0x7d, 0xfa, 0x75, 0xea, 0x55, 0xaa, 0x54, 0xa9, 0x53, 0xa6, + 0x4c, 0x99, 0x33, 0xe7, 0x4e, 0x9d, 0x3b, 0xf7, 0x6e, 0xdd, + 0x3a, 0xf4, 0x69, 0xd2, 0x25, 0xcb, 0x16, 0xac, 0x58, 0xb1, + 0x63, 0xc6, 0x0d, 0x9b, 0x37, 0xef, 0x5e, 0xbd, 0x7b, 0xf6, + 0x6d, 0xda, 0x35, 0xeb, 0x56, 0xad, 0x5b, 0xb6, 0x6c, 0xd9, + 0x32, 0xe4, 0x49, 0x92, 0x24, 0xc8, 0x11, 0xa3, 0x47, 0x8e, + 0x1c, 0xb8, 0x70, 0xe1, 0x42, 0x85, 0x0b, 0x97, 0x2f, 0xdf, + /* values for: 201 - 255 */ + 0x3e, 0xfc, 0x79, 0xf2, 0x65, 0xca, 0x15, 0xab, 0x57, 0xae, + 0x5c, 0xb9, 0x73, 0xe6, 0x4d, 0x9a, 0x34, 0xe8, 0x51, 0xa2, + 0x44, 0x89, 0x13, 0xa7, 0x4f, 0x9e, 0x3c, 0xf8, 0x71, 0xe2, + 0x45, 0x8a, 0x14, 0xa8, 0x50, 0xa1, 0x43, 0x86, 0x0c, 0x98, + 0x30, 0xe0, 0x41, 0x82, 0x04, 0x88, 0x10, 0xa0, 0x40, 0x81, + 0x03, 0x87, 0x0f, 0x9f, 0x3f /* END */ +}; + +u32 get_cpr0_fbdv(unsigned long cpr_reg_fbdv) +{ + u32 index; + + for (index = 0; index < ARRAY_SIZE(pll_fbdv_multi_bits); index++) + if (cpr_reg_fbdv == (u32)pll_fbdv_multi_bits[index]) + return index + 1; + + return 0; +} + +/* + * AMCC_TODO: verify this routine against latest EAS, cause stuff changed + * with latest EAS + */ +void get_sys_info (sys_info_t * sysInfo) +{ + unsigned long strp0; + unsigned long strp1; + unsigned long temp; + unsigned long m; + unsigned long plbedv0; + + /* Extract configured divisors */ + mfsdr(SDR0_SDSTP0, strp0); + mfsdr(SDR0_SDSTP1, strp1); + + temp = ((strp0 & PLLSYS0_FWD_DIV_A_MASK) >> 4); + sysInfo->pllFwdDivA = get_cpr0_fwdv(temp); + + temp = (strp0 & PLLSYS0_FWD_DIV_B_MASK); + sysInfo->pllFwdDivB = get_cpr0_fwdv(temp); + + temp = (strp0 & PLLSYS0_FB_DIV_MASK) >> 8; + sysInfo->pllFbkDiv = get_cpr0_fbdv(temp); + + temp = (strp1 & PLLSYS0_OPB_DIV_MASK) >> 26; + sysInfo->pllOpbDiv = temp ? temp : 4; + + /* AMCC_TODO: verify the SDR0_SDSTP1.PERDV0 value sysInfo->pllExtBusDiv */ + temp = (strp1 & PLLSYS0_PERCLK_DIV_MASK) >> 24; + sysInfo->pllExtBusDiv = temp ? temp : 4; + + temp = (strp1 & PLLSYS0_PLBEDV0_DIV_MASK) >> 29; + plbedv0 = temp ? temp: 8; + + /* Calculate 'M' based on feedback source */ + temp = (strp0 & PLLSYS0_SEL_MASK) >> 27; + if (temp == 0) { + /* PLL internal feedback */ + m = sysInfo->pllFbkDiv; + } else { + /* PLL PerClk feedback */ + m = sysInfo->pllFwdDivA * plbedv0 * sysInfo->pllOpbDiv * + sysInfo->pllExtBusDiv; + } + + /* Now calculate the individual clocks */ + sysInfo->freqVCOMhz = (m * CONFIG_SYS_CLK_FREQ) + (m >> 1); + sysInfo->freqProcessor = sysInfo->freqVCOMhz/sysInfo->pllFwdDivA; + sysInfo->freqPLB = sysInfo->freqVCOMhz / sysInfo->pllFwdDivA / plbedv0; + sysInfo->freqOPB = sysInfo->freqPLB / sysInfo->pllOpbDiv; + sysInfo->freqEBC = sysInfo->freqOPB / sysInfo->pllExtBusDiv; + sysInfo->freqDDR = sysInfo->freqPLB; + sysInfo->freqUART = sysInfo->freqPLB; + + return; +} + +#elif defined(CONFIG_440EP) || defined(CONFIG_440GR) || \ + defined(CONFIG_440EPX) || defined(CONFIG_440GRX) +void get_sys_info (sys_info_t *sysInfo) +{ + unsigned long temp; + unsigned long reg; + unsigned long lfdiv; + unsigned long m; + unsigned long prbdv0; + /* + WARNING: ASSUMES the following: + ENG=1 + PRADV0=1 + PRBDV0=1 + */ + + /* Decode CPR0_PLLD0 for divisors */ + mfcpr(CPR0_PLLD, reg); + temp = (reg & PLLD_FWDVA_MASK) >> 16; + sysInfo->pllFwdDivA = temp ? temp : 16; + temp = (reg & PLLD_FWDVB_MASK) >> 8; + sysInfo->pllFwdDivB = temp ? temp: 8 ; + temp = (reg & PLLD_FBDV_MASK) >> 24; + sysInfo->pllFbkDiv = temp ? temp : 32; + lfdiv = reg & PLLD_LFBDV_MASK; + + mfcpr(CPR0_OPBD0, reg); + temp = (reg & OPBDDV_MASK) >> 24; + sysInfo->pllOpbDiv = temp ? temp : 4; + + mfcpr(CPR0_PERD, reg); + temp = (reg & PERDV_MASK) >> 24; + sysInfo->pllExtBusDiv = temp ? temp : 8; + + mfcpr(CPR0_PRIMBD0, reg); + temp = (reg & PRBDV_MASK) >> 24; + prbdv0 = temp ? temp : 8; + + mfcpr(CPR0_SPCID, reg); + temp = (reg & SPCID_MASK) >> 24; + sysInfo->pllPciDiv = temp ? temp : 4; + + /* Calculate 'M' based on feedback source */ + mfsdr(SDR0_SDSTP0, reg); + temp = (reg & PLLSYS0_SEL_MASK) >> 27; + if (temp == 0) { /* PLL output */ + /* Figure which pll to use */ + mfcpr(CPR0_PLLC, reg); + temp = (reg & PLLC_SRC_MASK) >> 29; + if (!temp) /* PLLOUTA */ + m = sysInfo->pllFbkDiv * lfdiv * sysInfo->pllFwdDivA; + else /* PLLOUTB */ + m = sysInfo->pllFbkDiv * lfdiv * sysInfo->pllFwdDivB; + } + else if (temp == 1) /* CPU output */ + m = sysInfo->pllFbkDiv * sysInfo->pllFwdDivA; + else /* PerClk */ + m = sysInfo->pllExtBusDiv * sysInfo->pllOpbDiv * sysInfo->pllFwdDivB; + + /* Now calculate the individual clocks */ + sysInfo->freqVCOMhz = (m * CONFIG_SYS_CLK_FREQ) + (m>>1); + sysInfo->freqProcessor = sysInfo->freqVCOMhz/sysInfo->pllFwdDivA; + sysInfo->freqPLB = sysInfo->freqVCOMhz/sysInfo->pllFwdDivB/prbdv0; + sysInfo->freqOPB = sysInfo->freqPLB/sysInfo->pllOpbDiv; + sysInfo->freqEBC = sysInfo->freqPLB/sysInfo->pllExtBusDiv; + sysInfo->freqPCI = sysInfo->freqPLB/sysInfo->pllPciDiv; + sysInfo->freqUART = sysInfo->freqPLB; + + /* Figure which timer source to use */ + if (mfspr(SPRN_CCR1) & 0x0080) { + /* External Clock, assume same as SYS_CLK */ + temp = sysInfo->freqProcessor / 2; /* Max extern clock speed */ + if (CONFIG_SYS_CLK_FREQ > temp) + sysInfo->freqTmrClk = temp; + else + sysInfo->freqTmrClk = CONFIG_SYS_CLK_FREQ; + } + else /* Internal clock */ + sysInfo->freqTmrClk = sysInfo->freqProcessor; +} + +/******************************************** + * get_PCI_freq + * return PCI bus freq in Hz + *********************************************/ +ulong get_PCI_freq (void) +{ + sys_info_t sys_info; + get_sys_info (&sys_info); + return sys_info.freqPCI; +} + +#elif !defined(CONFIG_440GX) && !defined(CONFIG_440SP) && !defined(CONFIG_440SPE) \ + && !defined(CONFIG_XILINX_440) +void get_sys_info (sys_info_t * sysInfo) +{ + unsigned long strp0; + unsigned long temp; + unsigned long m; + + /* Extract configured divisors */ + strp0 = mfdcr( CPC0_STRP0 ); + sysInfo->pllFwdDivA = 8 - ((strp0 & PLLSYS0_FWD_DIV_A_MASK) >> 15); + sysInfo->pllFwdDivB = 8 - ((strp0 & PLLSYS0_FWD_DIV_B_MASK) >> 12); + temp = (strp0 & PLLSYS0_FB_DIV_MASK) >> 18; + sysInfo->pllFbkDiv = temp ? temp : 16; + sysInfo->pllOpbDiv = 1 + ((strp0 & PLLSYS0_OPB_DIV_MASK) >> 10); + sysInfo->pllExtBusDiv = 1 + ((strp0 & PLLSYS0_EPB_DIV_MASK) >> 8); + + /* Calculate 'M' based on feedback source */ + if( strp0 & PLLSYS0_EXTSL_MASK ) + m = sysInfo->pllExtBusDiv * sysInfo->pllOpbDiv * sysInfo->pllFwdDivB; + else + m = sysInfo->pllFbkDiv * sysInfo->pllFwdDivA; + + /* Now calculate the individual clocks */ + sysInfo->freqVCOMhz = (m * CONFIG_SYS_CLK_FREQ) + (m>>1); + sysInfo->freqProcessor = sysInfo->freqVCOMhz/sysInfo->pllFwdDivA; + sysInfo->freqPLB = sysInfo->freqVCOMhz/sysInfo->pllFwdDivB; + if( get_pvr() == PVR_440GP_RB ) /* Rev B divs an extra 2 -- geez! */ + sysInfo->freqPLB >>= 1; + sysInfo->freqOPB = sysInfo->freqPLB/sysInfo->pllOpbDiv; + sysInfo->freqEBC = sysInfo->freqOPB/sysInfo->pllExtBusDiv; + sysInfo->freqUART = sysInfo->freqPLB; +} +#else + +#if !defined(CONFIG_XILINX_440) +void get_sys_info (sys_info_t * sysInfo) +{ + unsigned long strp0; + unsigned long strp1; + unsigned long temp; + unsigned long temp1; + unsigned long lfdiv; + unsigned long m; + unsigned long prbdv0; + +#if defined(CONFIG_YUCCA) + unsigned long sys_freq; + unsigned long sys_per=0; + unsigned long msr; + unsigned long pci_clock_per; + unsigned long sdr_ddrpll; + + /*-------------------------------------------------------------------------+ + | Get the system clock period. + +-------------------------------------------------------------------------*/ + sys_per = determine_sysper(); + + msr = (mfmsr () & ~(MSR_EE)); /* disable interrupts */ + + /*-------------------------------------------------------------------------+ + | Calculate the system clock speed from the period. + +-------------------------------------------------------------------------*/ + sys_freq = (ONE_BILLION / sys_per) * 1000; +#endif + + /* Extract configured divisors */ + mfsdr( SDR0_SDSTP0,strp0 ); + mfsdr( SDR0_SDSTP1,strp1 ); + + temp = ((strp0 & PLLSYS0_FWD_DIV_A_MASK) >> 8); + sysInfo->pllFwdDivA = temp ? temp : 16 ; + temp = ((strp0 & PLLSYS0_FWD_DIV_B_MASK) >> 5); + sysInfo->pllFwdDivB = temp ? temp: 8 ; + temp = (strp0 & PLLSYS0_FB_DIV_MASK) >> 12; + sysInfo->pllFbkDiv = temp ? temp : 32; + temp = (strp0 & PLLSYS0_OPB_DIV_MASK); + sysInfo->pllOpbDiv = temp ? temp : 4; + temp = (strp1 & PLLSYS1_PERCLK_DIV_MASK) >> 24; + sysInfo->pllExtBusDiv = temp ? temp : 4; + prbdv0 = (strp0 >> 2) & 0x7; + + /* Calculate 'M' based on feedback source */ + temp = (strp0 & PLLSYS0_SEL_MASK) >> 27; + temp1 = (strp1 & PLLSYS1_LF_DIV_MASK) >> 26; + lfdiv = temp1 ? temp1 : 64; + if (temp == 0) { /* PLL output */ + /* Figure which pll to use */ + temp = (strp0 & PLLSYS0_SRC_MASK) >> 30; + if (!temp) + m = sysInfo->pllFbkDiv * lfdiv * sysInfo->pllFwdDivA; + else + m = sysInfo->pllFbkDiv * lfdiv * sysInfo->pllFwdDivB; + } + else if (temp == 1) /* CPU output */ + m = sysInfo->pllFbkDiv * sysInfo->pllFwdDivA; + else /* PerClk */ + m = sysInfo->pllExtBusDiv * sysInfo->pllOpbDiv * sysInfo->pllFwdDivB; + + /* Now calculate the individual clocks */ +#if defined(CONFIG_YUCCA) + sysInfo->freqVCOMhz = (m * sys_freq) ; +#else + sysInfo->freqVCOMhz = (m * CONFIG_SYS_CLK_FREQ) + (m >> 1); +#endif + sysInfo->freqProcessor = sysInfo->freqVCOMhz/sysInfo->pllFwdDivA; + sysInfo->freqPLB = sysInfo->freqVCOMhz/sysInfo->pllFwdDivB/prbdv0; + sysInfo->freqOPB = sysInfo->freqPLB/sysInfo->pllOpbDiv; + sysInfo->freqEBC = sysInfo->freqOPB/sysInfo->pllExtBusDiv; + +#if defined(CONFIG_YUCCA) + /* Determine PCI Clock Period */ + pci_clock_per = determine_pci_clock_per(); + sysInfo->freqPCI = (ONE_BILLION/pci_clock_per) * 1000; + mfsdr(SDR0_DDR0, sdr_ddrpll); + sysInfo->freqDDR = ((sysInfo->freqPLB) * SDR0_DDR0_DDRM_DECODE(sdr_ddrpll)); +#endif + + sysInfo->freqUART = sysInfo->freqPLB; +} + +#endif +#endif /* CONFIG_XILINX_440 */ + +#if defined(CONFIG_YUCCA) +unsigned long determine_sysper(void) +{ + unsigned int fpga_clocking_reg; + unsigned int master_clock_selection; + unsigned long master_clock_per = 0; + unsigned long fb_div_selection; + unsigned int vco_div_reg_value; + unsigned long vco_div_selection; + unsigned long sys_per = 0; + int extClkVal; + + /*-------------------------------------------------------------------------+ + | Read FPGA reg 0 and reg 1 to get FPGA reg information + +-------------------------------------------------------------------------*/ + fpga_clocking_reg = in16(FPGA_REG16); + + + /* Determine Master Clock Source Selection */ + master_clock_selection = fpga_clocking_reg & FPGA_REG16_MASTER_CLK_MASK; + + switch(master_clock_selection) { + case FPGA_REG16_MASTER_CLK_66_66: + master_clock_per = PERIOD_66_66MHZ; + break; + case FPGA_REG16_MASTER_CLK_50: + master_clock_per = PERIOD_50_00MHZ; + break; + case FPGA_REG16_MASTER_CLK_33_33: + master_clock_per = PERIOD_33_33MHZ; + break; + case FPGA_REG16_MASTER_CLK_25: + master_clock_per = PERIOD_25_00MHZ; + break; + case FPGA_REG16_MASTER_CLK_EXT: + if ((extClkVal==EXTCLK_33_33) + && (extClkVal==EXTCLK_50) + && (extClkVal==EXTCLK_66_66) + && (extClkVal==EXTCLK_83)) { + /* calculate master clock period from external clock value */ + master_clock_per=(ONE_BILLION/extClkVal) * 1000; + } else { + /* Unsupported */ + DEBUGF ("%s[%d] *** master clock selection failed ***\n", __FUNCTION__,__LINE__); + hang(); + } + break; + default: + /* Unsupported */ + DEBUGF ("%s[%d] *** master clock selection failed ***\n", __FUNCTION__,__LINE__); + hang(); + break; + } + + /* Determine FB divisors values */ + if ((fpga_clocking_reg & FPGA_REG16_FB1_DIV_MASK) == FPGA_REG16_FB1_DIV_LOW) { + if ((fpga_clocking_reg & FPGA_REG16_FB2_DIV_MASK) == FPGA_REG16_FB2_DIV_LOW) + fb_div_selection = FPGA_FB_DIV_6; + else + fb_div_selection = FPGA_FB_DIV_12; + } else { + if ((fpga_clocking_reg & FPGA_REG16_FB2_DIV_MASK) == FPGA_REG16_FB2_DIV_LOW) + fb_div_selection = FPGA_FB_DIV_10; + else + fb_div_selection = FPGA_FB_DIV_20; + } + + /* Determine VCO divisors values */ + vco_div_reg_value = fpga_clocking_reg & FPGA_REG16_VCO_DIV_MASK; + + switch(vco_div_reg_value) { + case FPGA_REG16_VCO_DIV_4: + vco_div_selection = FPGA_VCO_DIV_4; + break; + case FPGA_REG16_VCO_DIV_6: + vco_div_selection = FPGA_VCO_DIV_6; + break; + case FPGA_REG16_VCO_DIV_8: + vco_div_selection = FPGA_VCO_DIV_8; + break; + case FPGA_REG16_VCO_DIV_10: + default: + vco_div_selection = FPGA_VCO_DIV_10; + break; + } + + if (master_clock_selection == FPGA_REG16_MASTER_CLK_EXT) { + switch(master_clock_per) { + case PERIOD_25_00MHZ: + if (fb_div_selection == FPGA_FB_DIV_12) { + if (vco_div_selection == FPGA_VCO_DIV_4) + sys_per = PERIOD_75_00MHZ; + if (vco_div_selection == FPGA_VCO_DIV_6) + sys_per = PERIOD_50_00MHZ; + } + break; + case PERIOD_33_33MHZ: + if (fb_div_selection == FPGA_FB_DIV_6) { + if (vco_div_selection == FPGA_VCO_DIV_4) + sys_per = PERIOD_50_00MHZ; + if (vco_div_selection == FPGA_VCO_DIV_6) + sys_per = PERIOD_33_33MHZ; + } + if (fb_div_selection == FPGA_FB_DIV_10) { + if (vco_div_selection == FPGA_VCO_DIV_4) + sys_per = PERIOD_83_33MHZ; + if (vco_div_selection == FPGA_VCO_DIV_10) + sys_per = PERIOD_33_33MHZ; + } + if (fb_div_selection == FPGA_FB_DIV_12) { + if (vco_div_selection == FPGA_VCO_DIV_4) + sys_per = PERIOD_100_00MHZ; + if (vco_div_selection == FPGA_VCO_DIV_6) + sys_per = PERIOD_66_66MHZ; + if (vco_div_selection == FPGA_VCO_DIV_8) + sys_per = PERIOD_50_00MHZ; + } + break; + case PERIOD_50_00MHZ: + if (fb_div_selection == FPGA_FB_DIV_6) { + if (vco_div_selection == FPGA_VCO_DIV_4) + sys_per = PERIOD_75_00MHZ; + if (vco_div_selection == FPGA_VCO_DIV_6) + sys_per = PERIOD_50_00MHZ; + } + if (fb_div_selection == FPGA_FB_DIV_10) { + if (vco_div_selection == FPGA_VCO_DIV_6) + sys_per = PERIOD_83_33MHZ; + if (vco_div_selection == FPGA_VCO_DIV_10) + sys_per = PERIOD_50_00MHZ; + } + if (fb_div_selection == FPGA_FB_DIV_12) { + if (vco_div_selection == FPGA_VCO_DIV_6) + sys_per = PERIOD_100_00MHZ; + if (vco_div_selection == FPGA_VCO_DIV_8) + sys_per = PERIOD_75_00MHZ; + } + break; + case PERIOD_66_66MHZ: + if (fb_div_selection == FPGA_FB_DIV_6) { + if (vco_div_selection == FPGA_VCO_DIV_4) + sys_per = PERIOD_100_00MHZ; + if (vco_div_selection == FPGA_VCO_DIV_6) + sys_per = PERIOD_66_66MHZ; + if (vco_div_selection == FPGA_VCO_DIV_8) + sys_per = PERIOD_50_00MHZ; + } + if (fb_div_selection == FPGA_FB_DIV_10) { + if (vco_div_selection == FPGA_VCO_DIV_8) + sys_per = PERIOD_83_33MHZ; + if (vco_div_selection == FPGA_VCO_DIV_10) + sys_per = PERIOD_66_66MHZ; + } + if (fb_div_selection == FPGA_FB_DIV_12) { + if (vco_div_selection == FPGA_VCO_DIV_8) + sys_per = PERIOD_100_00MHZ; + } + break; + default: + break; + } + + if (sys_per == 0) { + /* Other combinations are not supported */ + DEBUGF ("%s[%d] *** sys period compute failed ***\n", __FUNCTION__,__LINE__); + hang(); + } + } else { + /* calcul system clock without cheking */ + /* if engineering option clock no check is selected */ + /* sys_per = master_clock_per * vco_div_selection / fb_div_selection */ + sys_per = (master_clock_per/fb_div_selection) * vco_div_selection; + } + + return(sys_per); +} + +/*-------------------------------------------------------------------------+ +| determine_pci_clock_per. ++-------------------------------------------------------------------------*/ +unsigned long determine_pci_clock_per(void) +{ + unsigned long pci_clock_selection, pci_period; + + /*-------------------------------------------------------------------------+ + | Read FPGA reg 6 to get PCI 0 FPGA reg information + +-------------------------------------------------------------------------*/ + pci_clock_selection = in16(FPGA_REG16); /* was reg6 averifier */ + + + pci_clock_selection = pci_clock_selection & FPGA_REG16_PCI0_CLK_MASK; + + switch (pci_clock_selection) { + case FPGA_REG16_PCI0_CLK_133_33: + pci_period = PERIOD_133_33MHZ; + break; + case FPGA_REG16_PCI0_CLK_100: + pci_period = PERIOD_100_00MHZ; + break; + case FPGA_REG16_PCI0_CLK_66_66: + pci_period = PERIOD_66_66MHZ; + break; + default: + pci_period = PERIOD_33_33MHZ;; + break; + } + + return(pci_period); +} +#endif + +#elif defined(CONFIG_XILINX_405) +extern void get_sys_info (sys_info_t * sysInfo); +extern ulong get_PCI_freq (void); + +#elif defined(CONFIG_AP1000) +void get_sys_info (sys_info_t * sysInfo) +{ + sysInfo->freqProcessor = 240 * 1000 * 1000; + sysInfo->freqPLB = 80 * 1000 * 1000; + sysInfo->freqPCI = 33 * 1000 * 1000; +} + +#elif defined(CONFIG_405) + +void get_sys_info (sys_info_t * sysInfo) +{ + sysInfo->freqVCOMhz=3125000; + sysInfo->freqProcessor=12*1000*1000; + sysInfo->freqPLB=50*1000*1000; + sysInfo->freqPCI=66*1000*1000; +} + +#elif defined(CONFIG_405EP) +void get_sys_info (PPC4xx_SYS_INFO * sysInfo) +{ + unsigned long pllmr0; + unsigned long pllmr1; + unsigned long sysClkPeriodPs = ONE_BILLION / (CONFIG_SYS_CLK_FREQ / 1000); + unsigned long m; + unsigned long pllmr0_ccdv; + + /* + * Read PLL Mode registers + */ + pllmr0 = mfdcr (CPC0_PLLMR0); + pllmr1 = mfdcr (CPC0_PLLMR1); + + /* + * Determine forward divider A + */ + sysInfo->pllFwdDiv = 8 - ((pllmr1 & PLLMR1_FWDVA_MASK) >> 16); + + /* + * Determine forward divider B (should be equal to A) + */ + sysInfo->pllFwdDivB = 8 - ((pllmr1 & PLLMR1_FWDVB_MASK) >> 12); + + /* + * Determine FBK_DIV. + */ + sysInfo->pllFbkDiv = ((pllmr1 & PLLMR1_FBMUL_MASK) >> 20); + if (sysInfo->pllFbkDiv == 0) + sysInfo->pllFbkDiv = 16; + + /* + * Determine PLB_DIV. + */ + sysInfo->pllPlbDiv = ((pllmr0 & PLLMR0_CPU_TO_PLB_MASK) >> 16) + 1; + + /* + * Determine PCI_DIV. + */ + sysInfo->pllPciDiv = (pllmr0 & PLLMR0_PCI_TO_PLB_MASK) + 1; + + /* + * Determine EXTBUS_DIV. + */ + sysInfo->pllExtBusDiv = ((pllmr0 & PLLMR0_EXB_TO_PLB_MASK) >> 8) + 2; + + /* + * Determine OPB_DIV. + */ + sysInfo->pllOpbDiv = ((pllmr0 & PLLMR0_OPB_TO_PLB_MASK) >> 12) + 1; + + /* + * Determine the M factor + */ + m = sysInfo->pllFbkDiv * sysInfo->pllFwdDivB; + + /* + * Determine VCO clock frequency + */ + sysInfo->freqVCOHz = (1000000000000LL * (unsigned long long)m) / + (unsigned long long)sysClkPeriodPs; + + /* + * Determine CPU clock frequency + */ + pllmr0_ccdv = ((pllmr0 & PLLMR0_CPU_DIV_MASK) >> 20) + 1; + if (pllmr1 & PLLMR1_SSCS_MASK) { + /* + * This is true if FWDVA == FWDVB: + * sysInfo->freqProcessor = (CONFIG_SYS_CLK_FREQ * sysInfo->pllFbkDiv) + * / pllmr0_ccdv; + */ + sysInfo->freqProcessor = (CONFIG_SYS_CLK_FREQ * sysInfo->pllFbkDiv * sysInfo->pllFwdDivB) + / sysInfo->pllFwdDiv / pllmr0_ccdv; + } else { + sysInfo->freqProcessor = CONFIG_SYS_CLK_FREQ / pllmr0_ccdv; + } + + /* + * Determine PLB clock frequency + */ + sysInfo->freqPLB = sysInfo->freqProcessor / sysInfo->pllPlbDiv; + + sysInfo->freqEBC = sysInfo->freqPLB / sysInfo->pllExtBusDiv; + + sysInfo->freqOPB = sysInfo->freqPLB / sysInfo->pllOpbDiv; + + sysInfo->freqUART = sysInfo->freqProcessor * pllmr0_ccdv; +} + + +/******************************************** + * get_PCI_freq + * return PCI bus freq in Hz + *********************************************/ +ulong get_PCI_freq (void) +{ + ulong val; + PPC4xx_SYS_INFO sys_info; + + get_sys_info (&sys_info); + val = sys_info.freqPLB / sys_info.pllPciDiv; + return val; +} + +#elif defined(CONFIG_405EZ) +void get_sys_info (PPC4xx_SYS_INFO * sysInfo) +{ + unsigned long cpr_plld; + unsigned long cpr_pllc; + unsigned long cpr_primad; + unsigned long sysClkPeriodPs = ONE_BILLION / (CONFIG_SYS_CLK_FREQ/1000); + unsigned long primad_cpudv; + unsigned long m; + unsigned long plloutb; + + /* + * Read PLL Mode registers + */ + mfcpr(CPR0_PLLD, cpr_plld); + mfcpr(CPR0_PLLC, cpr_pllc); + + /* + * Determine forward divider A + */ + sysInfo->pllFwdDiv = ((cpr_plld & PLLD_FWDVA_MASK) >> 16); + + /* + * Determine forward divider B + */ + sysInfo->pllFwdDivB = ((cpr_plld & PLLD_FWDVB_MASK) >> 8); + if (sysInfo->pllFwdDivB == 0) + sysInfo->pllFwdDivB = 8; + + /* + * Determine FBK_DIV. + */ + sysInfo->pllFbkDiv = ((cpr_plld & PLLD_FBDV_MASK) >> 24); + if (sysInfo->pllFbkDiv == 0) + sysInfo->pllFbkDiv = 256; + + /* + * Read CPR_PRIMAD register + */ + mfcpr(CPC0_PRIMAD, cpr_primad); + + /* + * Determine PLB_DIV. + */ + sysInfo->pllPlbDiv = ((cpr_primad & PRIMAD_PLBDV_MASK) >> 16); + if (sysInfo->pllPlbDiv == 0) + sysInfo->pllPlbDiv = 16; + + /* + * Determine EXTBUS_DIV. + */ + sysInfo->pllExtBusDiv = (cpr_primad & PRIMAD_EBCDV_MASK); + if (sysInfo->pllExtBusDiv == 0) + sysInfo->pllExtBusDiv = 16; + + /* + * Determine OPB_DIV. + */ + sysInfo->pllOpbDiv = ((cpr_primad & PRIMAD_OPBDV_MASK) >> 8); + if (sysInfo->pllOpbDiv == 0) + sysInfo->pllOpbDiv = 16; + + /* + * Determine the M factor + */ + if (cpr_pllc & PLLC_SRC_MASK) + m = sysInfo->pllFbkDiv * sysInfo->pllFwdDivB; + else + m = sysInfo->pllFbkDiv * sysInfo->pllFwdDiv; + + /* + * Determine VCO clock frequency + */ + sysInfo->freqVCOHz = (1000000000000LL * (unsigned long long)m) / + (unsigned long long)sysClkPeriodPs; + + /* + * Determine CPU clock frequency + */ + primad_cpudv = ((cpr_primad & PRIMAD_CPUDV_MASK) >> 24); + if (primad_cpudv == 0) + primad_cpudv = 16; + + sysInfo->freqProcessor = (CONFIG_SYS_CLK_FREQ * m) / + sysInfo->pllFwdDiv / primad_cpudv; + + /* + * Determine PLB clock frequency + */ + sysInfo->freqPLB = (CONFIG_SYS_CLK_FREQ * m) / + sysInfo->pllFwdDiv / sysInfo->pllPlbDiv; + + sysInfo->freqOPB = (CONFIG_SYS_CLK_FREQ * sysInfo->pllFbkDiv) / + sysInfo->pllOpbDiv; + + sysInfo->freqEBC = (CONFIG_SYS_CLK_FREQ * sysInfo->pllFbkDiv) / + sysInfo->pllExtBusDiv; + + plloutb = ((CONFIG_SYS_CLK_FREQ * ((cpr_pllc & PLLC_SRC_MASK) ? + sysInfo->pllFwdDivB : sysInfo->pllFwdDiv) * sysInfo->pllFbkDiv) / + sysInfo->pllFwdDivB); + sysInfo->freqUART = plloutb; +} + +#elif defined(CONFIG_405EX) + +/* + * TODO: We need to get the CPR registers and calculate these values correctly!!!! + * We need the specs!!!! + */ +static unsigned char get_fbdv(unsigned char index) +{ + unsigned char ret = 0; + /* This is table should be 256 bytes. + * Only take first 52 values. + */ + unsigned char fbdv_tb[] = { + 0x00, 0xff, 0x7f, 0xfd, + 0x7a, 0xf5, 0x6a, 0xd5, + 0x2a, 0xd4, 0x29, 0xd3, + 0x26, 0xcc, 0x19, 0xb3, + 0x67, 0xce, 0x1d, 0xbb, + 0x77, 0xee, 0x5d, 0xba, + 0x74, 0xe9, 0x52, 0xa5, + 0x4b, 0x96, 0x2c, 0xd8, + 0x31, 0xe3, 0x46, 0x8d, + 0x1b, 0xb7, 0x6f, 0xde, + 0x3d, 0xfb, 0x76, 0xed, + 0x5a, 0xb5, 0x6b, 0xd6, + 0x2d, 0xdb, 0x36, 0xec, + + }; + + if ((index & 0x7f) == 0) + return 1; + while (ret < sizeof (fbdv_tb)) { + if (fbdv_tb[ret] == index) + break; + ret++; + } + ret++; + + return ret; +} + +#define PLL_FBK_PLL_LOCAL 0 +#define PLL_FBK_CPU 1 +#define PLL_FBK_PERCLK 5 + +void get_sys_info (sys_info_t * sysInfo) +{ + unsigned long sysClkPeriodPs = ONE_BILLION / (CONFIG_SYS_CLK_FREQ / 1000); + unsigned long m = 1; + unsigned int tmp; + unsigned char fwdva[16] = { + 1, 2, 14, 9, 4, 11, 16, 13, + 12, 5, 6, 15, 10, 7, 8, 3, + }; + unsigned char sel, cpudv0, plb2xDiv; + + mfcpr(CPR0_PLLD, tmp); + + /* + * Determine forward divider A + */ + sysInfo->pllFwdDiv = fwdva[((tmp >> 16) & 0x0f)]; /* FWDVA */ + + /* + * Determine FBK_DIV. + */ + sysInfo->pllFbkDiv = get_fbdv(((tmp >> 24) & 0x0ff)); /* FBDV */ + + /* + * Determine PLBDV0 + */ + sysInfo->pllPlbDiv = 2; + + /* + * Determine PERDV0 + */ + mfcpr(CPR0_PERD, tmp); + tmp = (tmp >> 24) & 0x03; + sysInfo->pllExtBusDiv = (tmp == 0) ? 4 : tmp; + + /* + * Determine OPBDV0 + */ + mfcpr(CPR0_OPBD0, tmp); + tmp = (tmp >> 24) & 0x03; + sysInfo->pllOpbDiv = (tmp == 0) ? 4 : tmp; + + /* Determine PLB2XDV0 */ + mfcpr(CPR0_PLBD, tmp); + tmp = (tmp >> 16) & 0x07; + plb2xDiv = (tmp == 0) ? 8 : tmp; + + /* Determine CPUDV0 */ + mfcpr(CPR0_CPUD, tmp); + tmp = (tmp >> 24) & 0x07; + cpudv0 = (tmp == 0) ? 8 : tmp; + + /* Determine SEL(5:7) in CPR0_PLLC */ + mfcpr(CPR0_PLLC, tmp); + sel = (tmp >> 24) & 0x07; + + /* + * Determine the M factor + * PLL local: M = FBDV + * CPU clock: M = FBDV * FWDVA * CPUDV0 + * PerClk : M = FBDV * FWDVA * PLB2XDV0 * PLBDV0(2) * OPBDV0 * PERDV0 + * + */ + switch (sel) { + case PLL_FBK_CPU: + m = sysInfo->pllFwdDiv * cpudv0; + break; + case PLL_FBK_PERCLK: + m = sysInfo->pllFwdDiv * plb2xDiv * 2 + * sysInfo->pllOpbDiv * sysInfo->pllExtBusDiv; + break; + case PLL_FBK_PLL_LOCAL: + break; + default: + printf("%s unknown m\n", __FUNCTION__); + return; + + } + m *= sysInfo->pllFbkDiv; + + /* + * Determine VCO clock frequency + */ + sysInfo->freqVCOHz = (1000000000000LL * (unsigned long long)m) / + (unsigned long long)sysClkPeriodPs; + + /* + * Determine CPU clock frequency + */ + sysInfo->freqProcessor = sysInfo->freqVCOHz / (sysInfo->pllFwdDiv * cpudv0); + + /* + * Determine PLB clock frequency, ddr1x should be the same + */ + sysInfo->freqPLB = sysInfo->freqVCOHz / (sysInfo->pllFwdDiv * plb2xDiv * 2); + sysInfo->freqOPB = sysInfo->freqPLB/sysInfo->pllOpbDiv; + sysInfo->freqDDR = sysInfo->freqPLB; + sysInfo->freqEBC = sysInfo->freqOPB / sysInfo->pllExtBusDiv; + sysInfo->freqUART = sysInfo->freqPLB; +} + +#endif + +int get_clocks (void) +{ +#if defined(CONFIG_405GP) || defined(CONFIG_405CR) || \ + defined(CONFIG_405EP) || defined(CONFIG_405EZ) || \ + defined(CONFIG_405EX) || defined(CONFIG_405) || \ + defined(CONFIG_440) + sys_info_t sys_info; + + get_sys_info (&sys_info); + gd->cpu_clk = sys_info.freqProcessor; + gd->bus_clk = sys_info.freqPLB; + +#endif /* defined(CONFIG_405GP) || defined(CONFIG_405CR) */ + +#ifdef CONFIG_IOP480 + gd->cpu_clk = 66000000; + gd->bus_clk = 66000000; +#endif + return (0); +} + + +/******************************************** + * get_bus_freq + * return PLB bus freq in Hz + *********************************************/ +ulong get_bus_freq (ulong dummy) +{ + ulong val; + +#if defined(CONFIG_405GP) || defined(CONFIG_405CR) || \ + defined(CONFIG_405EP) || defined(CONFIG_405EZ) || \ + defined(CONFIG_405EX) || defined(CONFIG_405) || \ + defined(CONFIG_440) + sys_info_t sys_info; + + get_sys_info (&sys_info); + val = sys_info.freqPLB; + +#elif defined(CONFIG_IOP480) + + val = 66; + +#else +# error get_bus_freq() not implemented +#endif + + return val; +} + +#if !defined(CONFIG_IOP480) +ulong get_OPB_freq (void) +{ + PPC4xx_SYS_INFO sys_info; + + get_sys_info (&sys_info); + + return sys_info.freqOPB; +} +#endif diff --git a/arch/powerpc/cpu/ppc4xx/start.S b/arch/powerpc/cpu/ppc4xx/start.S new file mode 100644 index 0000000..c739deb --- /dev/null +++ b/arch/powerpc/cpu/ppc4xx/start.S @@ -0,0 +1,2170 @@ +/* + * Copyright (C) 1998 Dan Malek <dmalek@jlc.net> + * Copyright (C) 1999 Magnus Damm <kieraypc01.p.y.kie.era.ericsson.se> + * Copyright (C) 2000,2001,2002 Wolfgang Denk <wd@denx.de> + * Copyright (C) 2007 Stefan Roese <sr@denx.de>, DENX Software Engineering + * Copyright (c) 2008 Nuovation System Designs, LLC + * Grant Erickson <gerickson@nuovations.com> + * + * 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 + */ +/*------------------------------------------------------------------------------+ + * This source code is dual-licensed. You may use it under the terms of the + * GNU General Public License version 2, or under the license below. + * + * This source code has been made available to you by IBM on an AS-IS + * basis. Anyone receiving this source is licensed under IBM + * copyrights to use it in any way he or she deems fit, including + * copying it, modifying it, compiling it, and redistributing it either + * with or without modifications. No license under IBM patents or + * patent applications is to be implied by the copyright license. + * + * Any user of this software should understand that IBM cannot provide + * technical support for this software and will not be responsible for + * any consequences resulting from the use of this software. + * + * Any person who transfers this source code or any derivative work + * must include the IBM copyright notice, this paragraph, and the + * preceding two paragraphs in the transferred software. + * + * COPYRIGHT I B M CORPORATION 1995 + * LICENSED MATERIAL - PROGRAM PROPERTY OF I B M + *------------------------------------------------------------------------------- + */ + +/* U-Boot - Startup Code for AMCC 4xx PowerPC based Embedded Boards + * + * + * The processor starts at 0xfffffffc and the code is executed + * from flash/rom. + * in memory, but as long we don't jump around before relocating. + * board_init lies at a quite high address and when the cpu has + * jumped there, everything is ok. + * This works because the cpu gives the FLASH (CS0) the whole + * address space at startup, and board_init lies as a echo of + * the flash somewhere up there in the memorymap. + * + * board_init will change CS0 to be positioned at the correct + * address and (s)dram will be positioned at address 0 + */ +#include <config.h> +#include <ppc4xx.h> +#include <timestamp.h> +#include <version.h> + +#define _LINUX_CONFIG_H 1 /* avoid reading Linux autoconf.h file */ + +#include <ppc_asm.tmpl> +#include <ppc_defs.h> + +#include <asm/cache.h> +#include <asm/mmu.h> +#include <asm/ppc4xx-isram.h> + +#ifndef CONFIG_IDENT_STRING +#define CONFIG_IDENT_STRING "" +#endif + +#ifdef CONFIG_SYS_INIT_DCACHE_CS +# if (CONFIG_SYS_INIT_DCACHE_CS == 0) +# define PBxAP PB1AP +# define PBxCR PB0CR +# if (defined(CONFIG_SYS_EBC_PB0AP) && defined(CONFIG_SYS_EBC_PB0CR)) +# define PBxAP_VAL CONFIG_SYS_EBC_PB0AP +# define PBxCR_VAL CONFIG_SYS_EBC_PB0CR +# endif +# endif +# if (CONFIG_SYS_INIT_DCACHE_CS == 1) +# define PBxAP PB1AP +# define PBxCR PB1CR +# if (defined(CONFIG_SYS_EBC_PB1AP) && defined(CONFIG_SYS_EBC_PB1CR)) +# define PBxAP_VAL CONFIG_SYS_EBC_PB1AP +# define PBxCR_VAL CONFIG_SYS_EBC_PB1CR +# endif +# endif +# if (CONFIG_SYS_INIT_DCACHE_CS == 2) +# define PBxAP PB2AP +# define PBxCR PB2CR +# if (defined(CONFIG_SYS_EBC_PB2AP) && defined(CONFIG_SYS_EBC_PB2CR)) +# define PBxAP_VAL CONFIG_SYS_EBC_PB2AP +# define PBxCR_VAL CONFIG_SYS_EBC_PB2CR +# endif +# endif +# if (CONFIG_SYS_INIT_DCACHE_CS == 3) +# define PBxAP PB3AP +# define PBxCR PB3CR +# if (defined(CONFIG_SYS_EBC_PB3AP) && defined(CONFIG_SYS_EBC_PB3CR)) +# define PBxAP_VAL CONFIG_SYS_EBC_PB3AP +# define PBxCR_VAL CONFIG_SYS_EBC_PB3CR +# endif +# endif +# if (CONFIG_SYS_INIT_DCACHE_CS == 4) +# define PBxAP PB4AP +# define PBxCR PB4CR +# if (defined(CONFIG_SYS_EBC_PB4AP) && defined(CONFIG_SYS_EBC_PB4CR)) +# define PBxAP_VAL CONFIG_SYS_EBC_PB4AP +# define PBxCR_VAL CONFIG_SYS_EBC_PB4CR +# endif +# endif +# if (CONFIG_SYS_INIT_DCACHE_CS == 5) +# define PBxAP PB5AP +# define PBxCR PB5CR +# if (defined(CONFIG_SYS_EBC_PB5AP) && defined(CONFIG_SYS_EBC_PB5CR)) +# define PBxAP_VAL CONFIG_SYS_EBC_PB5AP +# define PBxCR_VAL CONFIG_SYS_EBC_PB5CR +# endif +# endif +# if (CONFIG_SYS_INIT_DCACHE_CS == 6) +# define PBxAP PB6AP +# define PBxCR PB6CR +# if (defined(CONFIG_SYS_EBC_PB6AP) && defined(CONFIG_SYS_EBC_PB6CR)) +# define PBxAP_VAL CONFIG_SYS_EBC_PB6AP +# define PBxCR_VAL CONFIG_SYS_EBC_PB6CR +# endif +# endif +# if (CONFIG_SYS_INIT_DCACHE_CS == 7) +# define PBxAP PB7AP +# define PBxCR PB7CR +# if (defined(CONFIG_SYS_EBC_PB7AP) && defined(CONFIG_SYS_EBC_PB7CR)) +# define PBxAP_VAL CONFIG_SYS_EBC_PB7AP +# define PBxCR_VAL CONFIG_SYS_EBC_PB7CR +# endif +# endif +# ifndef PBxAP_VAL +# define PBxAP_VAL 0 +# endif +# ifndef PBxCR_VAL +# define PBxCR_VAL 0 +# endif +/* + * Memory Bank x (nothingness) initialization CONFIG_SYS_INIT_RAM_ADDR + 64 MiB + * used as temporary stack pointer for the primordial stack + */ +# ifndef CONFIG_SYS_INIT_DCACHE_PBxAR +# define CONFIG_SYS_INIT_DCACHE_PBxAR (EBC_BXAP_BME_DISABLED | \ + EBC_BXAP_TWT_ENCODE(7) | \ + EBC_BXAP_BCE_DISABLE | \ + EBC_BXAP_BCT_2TRANS | \ + EBC_BXAP_CSN_ENCODE(0) | \ + EBC_BXAP_OEN_ENCODE(0) | \ + EBC_BXAP_WBN_ENCODE(0) | \ + EBC_BXAP_WBF_ENCODE(0) | \ + EBC_BXAP_TH_ENCODE(2) | \ + EBC_BXAP_RE_DISABLED | \ + EBC_BXAP_SOR_NONDELAYED | \ + EBC_BXAP_BEM_WRITEONLY | \ + EBC_BXAP_PEN_DISABLED) +# endif /* CONFIG_SYS_INIT_DCACHE_PBxAR */ +# ifndef CONFIG_SYS_INIT_DCACHE_PBxCR +# define CONFIG_SYS_INIT_DCACHE_PBxCR (EBC_BXCR_BAS_ENCODE(CONFIG_SYS_INIT_RAM_ADDR) | \ + EBC_BXCR_BS_64MB | \ + EBC_BXCR_BU_RW | \ + EBC_BXCR_BW_16BIT) +# endif /* CONFIG_SYS_INIT_DCACHE_PBxCR */ +# ifndef CONFIG_SYS_INIT_RAM_PATTERN +# define CONFIG_SYS_INIT_RAM_PATTERN 0xDEADDEAD +# endif +#endif /* CONFIG_SYS_INIT_DCACHE_CS */ + +#if (defined(CONFIG_SYS_INIT_RAM_DCACHE) && (CONFIG_SYS_INIT_RAM_END > (4 << 10))) +#error Only 4k of init-ram is supported - please adjust CONFIG_SYS_INIT_RAM_END! +#endif + +/* + * Unless otherwise overriden, enable two 128MB cachable instruction regions + * at CONFIG_SYS_SDRAM_BASE and another 128MB cacheable instruction region covering + * NOR flash at CONFIG_SYS_FLASH_BASE. Disable all cacheable data regions. + */ +#if !defined(CONFIG_SYS_FLASH_BASE) +/* If not already defined, set it to the "last" 128MByte region */ +# define CONFIG_SYS_FLASH_BASE 0xf8000000 +#endif +#if !defined(CONFIG_SYS_ICACHE_SACR_VALUE) +# define CONFIG_SYS_ICACHE_SACR_VALUE \ + (PPC_128MB_SACR_VALUE(CONFIG_SYS_SDRAM_BASE + ( 0 << 20)) | \ + PPC_128MB_SACR_VALUE(CONFIG_SYS_SDRAM_BASE + (128 << 20)) | \ + PPC_128MB_SACR_VALUE(CONFIG_SYS_FLASH_BASE)) +#endif /* !defined(CONFIG_SYS_ICACHE_SACR_VALUE) */ + +#if !defined(CONFIG_SYS_DCACHE_SACR_VALUE) +# define CONFIG_SYS_DCACHE_SACR_VALUE \ + (0x00000000) +#endif /* !defined(CONFIG_SYS_DCACHE_SACR_VALUE) */ + +#define function_prolog(func_name) .text; \ + .align 2; \ + .globl func_name; \ + func_name: +#define function_epilog(func_name) .type func_name,@function; \ + .size func_name,.-func_name + +/* We don't want the MMU yet. +*/ +#undef MSR_KERNEL +#define MSR_KERNEL ( MSR_ME ) /* Machine Check */ + + + .extern ext_bus_cntlr_init +#ifdef CONFIG_NAND_U_BOOT + .extern reconfig_tlb0 +#endif + +/* + * Set up GOT: Global Offset Table + * + * Use r12 to access the GOT + */ +#if !defined(CONFIG_NAND_SPL) + START_GOT + GOT_ENTRY(_GOT2_TABLE_) + GOT_ENTRY(_FIXUP_TABLE_) + + GOT_ENTRY(_start) + GOT_ENTRY(_start_of_vectors) + GOT_ENTRY(_end_of_vectors) + GOT_ENTRY(transfer_to_handler) + + GOT_ENTRY(__init_end) + GOT_ENTRY(_end) + GOT_ENTRY(__bss_start) + END_GOT +#endif /* CONFIG_NAND_SPL */ + +#if defined(CONFIG_NAND_U_BOOT) && !defined(CONFIG_NAND_SPL) + /* + * NAND U-Boot image is started from offset 0 + */ + .text +#if defined(CONFIG_440) + bl reconfig_tlb0 +#endif + GET_GOT + bl cpu_init_f /* run low-level CPU init code (from Flash) */ + bl board_init_f +#endif + +#if defined(CONFIG_SYS_RAMBOOT) + /* + * 4xx RAM-booting U-Boot image is started from offset 0 + */ + .text + bl _start_440 +#endif + +/* + * 440 Startup -- on reset only the top 4k of the effective + * address space is mapped in by an entry in the instruction + * and data shadow TLB. The .bootpg section is located in the + * top 4k & does only what's necessary to map in the the rest + * of the boot rom. Once the boot rom is mapped in we can + * proceed with normal startup. + * + * NOTE: CS0 only covers the top 2MB of the effective address + * space after reset. + */ + +#if defined(CONFIG_440) +#if !defined(CONFIG_NAND_SPL) + .section .bootpg,"ax" +#endif + .globl _start_440 + +/**************************************************************************/ +_start_440: + /*--------------------------------------------------------------------+ + | 440EPX BUP Change - Hardware team request + +--------------------------------------------------------------------*/ +#if defined(CONFIG_440EPX) || defined(CONFIG_440GRX) + sync + nop + nop +#endif + /*----------------------------------------------------------------+ + | Core bug fix. Clear the esr + +-----------------------------------------------------------------*/ + li r0,0 + mtspr SPRN_ESR,r0 + /*----------------------------------------------------------------*/ + /* Clear and set up some registers. */ + /*----------------------------------------------------------------*/ + iccci r0,r0 /* NOTE: operands not used for 440 */ + dccci r0,r0 /* NOTE: operands not used for 440 */ + sync + li r0,0 + mtspr SPRN_SRR0,r0 + mtspr SPRN_SRR1,r0 + mtspr SPRN_CSRR0,r0 + mtspr SPRN_CSRR1,r0 + /* NOTE: 440GX adds machine check status regs */ +#if defined(CONFIG_440) && !defined(CONFIG_440GP) + mtspr SPRN_MCSRR0,r0 + mtspr SPRN_MCSRR1,r0 + mfspr r1,SPRN_MCSR + mtspr SPRN_MCSR,r1 +#endif + + /*----------------------------------------------------------------*/ + /* CCR0 init */ + /*----------------------------------------------------------------*/ + /* Disable store gathering & broadcast, guarantee inst/data + * cache block touch, force load/store alignment + * (see errata 1.12: 440_33) + */ + lis r1,0x0030 /* store gathering & broadcast disable */ + ori r1,r1,0x6000 /* cache touch */ + mtspr SPRN_CCR0,r1 + + /*----------------------------------------------------------------*/ + /* Initialize debug */ + /*----------------------------------------------------------------*/ + mfspr r1,SPRN_DBCR0 + andis. r1, r1, 0x8000 /* test DBCR0[EDM] bit */ + bne skip_debug_init /* if set, don't clear debug register */ + mtspr SPRN_DBCR0,r0 + mtspr SPRN_DBCR1,r0 + mtspr SPRN_DBCR2,r0 + mtspr SPRN_IAC1,r0 + mtspr SPRN_IAC2,r0 + mtspr SPRN_IAC3,r0 + mtspr SPRN_DAC1,r0 + mtspr SPRN_DAC2,r0 + mtspr SPRN_DVC1,r0 + mtspr SPRN_DVC2,r0 + + mfspr r1,SPRN_DBSR + mtspr SPRN_DBSR,r1 /* Clear all valid bits */ +skip_debug_init: + +#if defined (CONFIG_440SPE) + /*----------------------------------------------------------------+ + | Initialize Core Configuration Reg1. + | a. ICDPEI: Record even parity. Normal operation. + | b. ICTPEI: Record even parity. Normal operation. + | c. DCTPEI: Record even parity. Normal operation. + | d. DCDPEI: Record even parity. Normal operation. + | e. DCUPEI: Record even parity. Normal operation. + | f. DCMPEI: Record even parity. Normal operation. + | g. FCOM: Normal operation + | h. MMUPEI: Record even parity. Normal operation. + | i. FFF: Flush only as much data as necessary. + | j. TCS: Timebase increments from CPU clock. + +-----------------------------------------------------------------*/ + li r0,0 + mtspr SPRN_CCR1, r0 + + /*----------------------------------------------------------------+ + | Reset the timebase. + | The previous write to CCR1 sets the timebase source. + +-----------------------------------------------------------------*/ + mtspr SPRN_TBWL, r0 + mtspr SPRN_TBWU, r0 +#endif + + /*----------------------------------------------------------------*/ + /* Setup interrupt vectors */ + /*----------------------------------------------------------------*/ + mtspr SPRN_IVPR,r0 /* Vectors start at 0x0000_0000 */ + li r1,0x0100 + mtspr SPRN_IVOR0,r1 /* Critical input */ + li r1,0x0200 + mtspr SPRN_IVOR1,r1 /* Machine check */ + li r1,0x0300 + mtspr SPRN_IVOR2,r1 /* Data storage */ + li r1,0x0400 + mtspr SPRN_IVOR3,r1 /* Instruction storage */ + li r1,0x0500 + mtspr SPRN_IVOR4,r1 /* External interrupt */ + li r1,0x0600 + mtspr SPRN_IVOR5,r1 /* Alignment */ + li r1,0x0700 + mtspr SPRN_IVOR6,r1 /* Program check */ + li r1,0x0800 + mtspr SPRN_IVOR7,r1 /* Floating point unavailable */ + li r1,0x0c00 + mtspr SPRN_IVOR8,r1 /* System call */ + li r1,0x0a00 + mtspr SPRN_IVOR9,r1 /* Auxiliary Processor unavailable */ + li r1,0x0900 + mtspr SPRN_IVOR10,r1 /* Decrementer */ + li r1,0x1300 + mtspr SPRN_IVOR13,r1 /* Data TLB error */ + li r1,0x1400 + mtspr SPRN_IVOR14,r1 /* Instr TLB error */ + li r1,0x2000 + mtspr SPRN_IVOR15,r1 /* Debug */ + + /*----------------------------------------------------------------*/ + /* Configure cache regions */ + /*----------------------------------------------------------------*/ + mtspr SPRN_INV0,r0 + mtspr SPRN_INV1,r0 + mtspr SPRN_INV2,r0 + mtspr SPRN_INV3,r0 + mtspr SPRN_DNV0,r0 + mtspr SPRN_DNV1,r0 + mtspr SPRN_DNV2,r0 + mtspr SPRN_DNV3,r0 + mtspr SPRN_ITV0,r0 + mtspr SPRN_ITV1,r0 + mtspr SPRN_ITV2,r0 + mtspr SPRN_ITV3,r0 + mtspr SPRN_DTV0,r0 + mtspr SPRN_DTV1,r0 + mtspr SPRN_DTV2,r0 + mtspr SPRN_DTV3,r0 + + /*----------------------------------------------------------------*/ + /* Cache victim limits */ + /*----------------------------------------------------------------*/ + /* floors 0, ceiling max to use the entire cache -- nothing locked + */ + lis r1,0x0001 + ori r1,r1,0xf800 + mtspr SPRN_IVLIM,r1 + mtspr SPRN_DVLIM,r1 + + /*----------------------------------------------------------------+ + |Initialize MMUCR[STID] = 0. + +-----------------------------------------------------------------*/ + mfspr r0,SPRN_MMUCR + addis r1,0,0xFFFF + ori r1,r1,0xFF00 + and r0,r0,r1 + mtspr SPRN_MMUCR,r0 + + /*----------------------------------------------------------------*/ + /* Clear all TLB entries -- TID = 0, TS = 0 */ + /*----------------------------------------------------------------*/ + addis r0,0,0x0000 +#ifdef CONFIG_SYS_RAMBOOT + li r4,0 /* Start with TLB #0 */ +#else + li r4,1 /* Start with TLB #1 */ +#endif + li r1,64 /* 64 TLB entries */ + sub r1,r1,r4 /* calculate last TLB # */ + mtctr r1 +rsttlb: +#ifdef CONFIG_SYS_RAMBOOT + tlbre r3,r4,0 /* Read contents from TLB word #0 to get EPN */ + rlwinm. r3,r3,0,0xfffffc00 /* Mask EPN */ + beq tlbnxt /* Skip EPN=0 TLB, this is the SDRAM TLB */ +#endif + tlbwe r0,r4,0 /* Invalidate all entries (V=0)*/ + tlbwe r0,r4,1 + tlbwe r0,r4,2 +tlbnxt: addi r4,r4,1 /* Next TLB */ + bdnz rsttlb + + /*----------------------------------------------------------------*/ + /* TLB entry setup -- step thru tlbtab */ + /*----------------------------------------------------------------*/ +#if defined(CONFIG_440SPE_REVA) + /*----------------------------------------------------------------*/ + /* We have different TLB tables for revA and rev B of 440SPe */ + /*----------------------------------------------------------------*/ + mfspr r1, PVR + lis r0,0x5342 + ori r0,r0,0x1891 + cmpw r7,r1,r0 + bne r7,..revA + bl tlbtabB + b ..goon +..revA: + bl tlbtabA +..goon: +#else + bl tlbtab /* Get tlbtab pointer */ +#endif + mr r5,r0 + li r1,0x003f /* 64 TLB entries max */ + mtctr r1 + li r4,0 /* TLB # */ + + addi r5,r5,-4 +1: +#ifdef CONFIG_SYS_RAMBOOT + tlbre r3,r4,0 /* Read contents from TLB word #0 */ + rlwinm. r3,r3,0,0x00000200 /* Mask V (valid) bit */ + bne tlbnx2 /* Skip V=1 TLB, this is the SDRAM TLB */ +#endif + lwzu r0,4(r5) + cmpwi r0,0 + beq 2f /* 0 marks end */ + lwzu r1,4(r5) + lwzu r2,4(r5) + tlbwe r0,r4,0 /* TLB Word 0 */ + tlbwe r1,r4,1 /* TLB Word 1 */ + tlbwe r2,r4,2 /* TLB Word 2 */ +tlbnx2: addi r4,r4,1 /* Next TLB */ + bdnz 1b + + /*----------------------------------------------------------------*/ + /* Continue from 'normal' start */ + /*----------------------------------------------------------------*/ +2: + bl 3f + b _start + +3: li r0,0 + mtspr SPRN_SRR1,r0 /* Keep things disabled for now */ + mflr r1 + mtspr SPRN_SRR0,r1 + rfi +#endif /* CONFIG_440 */ + +/* + * r3 - 1st arg to board_init(): IMMP pointer + * r4 - 2nd arg to board_init(): boot flag + */ +#ifndef CONFIG_NAND_SPL + .text + .long 0x27051956 /* U-Boot Magic Number */ + .globl version_string +version_string: + .ascii U_BOOT_VERSION + .ascii " (", U_BOOT_DATE, " - ", U_BOOT_TIME, ")" + .ascii CONFIG_IDENT_STRING, "\0" + + . = EXC_OFF_SYS_RESET + .globl _start_of_vectors +_start_of_vectors: + +/* Critical input. */ + CRIT_EXCEPTION(0x100, CritcalInput, UnknownException) + +#ifdef CONFIG_440 +/* Machine check */ + MCK_EXCEPTION(0x200, MachineCheck, MachineCheckException) +#else + CRIT_EXCEPTION(0x200, MachineCheck, MachineCheckException) +#endif /* CONFIG_440 */ + +/* Data Storage exception. */ + STD_EXCEPTION(0x300, DataStorage, UnknownException) + +/* Instruction Storage exception. */ + STD_EXCEPTION(0x400, InstStorage, UnknownException) + +/* External Interrupt exception. */ + STD_EXCEPTION(0x500, ExtInterrupt, external_interrupt) + +/* Alignment exception. */ + . = 0x600 +Alignment: + EXCEPTION_PROLOG(SRR0, SRR1) + mfspr r4,DAR + stw r4,_DAR(r21) + mfspr r5,DSISR + stw r5,_DSISR(r21) + addi r3,r1,STACK_FRAME_OVERHEAD + EXC_XFER_TEMPLATE(Alignment, AlignmentException, MSR_KERNEL, COPY_EE) + +/* Program check exception */ + . = 0x700 +ProgramCheck: + EXCEPTION_PROLOG(SRR0, SRR1) + addi r3,r1,STACK_FRAME_OVERHEAD + EXC_XFER_TEMPLATE(ProgramCheck, ProgramCheckException, + MSR_KERNEL, COPY_EE) + +#ifdef CONFIG_440 + STD_EXCEPTION(0x800, FPUnavailable, UnknownException) + STD_EXCEPTION(0x900, Decrementer, DecrementerPITException) + STD_EXCEPTION(0xa00, APU, UnknownException) +#endif + STD_EXCEPTION(0xc00, SystemCall, UnknownException) + +#ifdef CONFIG_440 + STD_EXCEPTION(0x1300, DataTLBError, UnknownException) + STD_EXCEPTION(0x1400, InstructionTLBError, UnknownException) +#else + STD_EXCEPTION(0x1000, PIT, DecrementerPITException) + STD_EXCEPTION(0x1100, InstructionTLBMiss, UnknownException) + STD_EXCEPTION(0x1200, DataTLBMiss, UnknownException) +#endif + CRIT_EXCEPTION(0x2000, DebugBreakpoint, DebugException ) + + .globl _end_of_vectors +_end_of_vectors: + . = _START_OFFSET +#endif + .globl _start +_start: + +/*****************************************************************************/ +#if defined(CONFIG_440) + + /*----------------------------------------------------------------*/ + /* Clear and set up some registers. */ + /*----------------------------------------------------------------*/ + li r0,0x0000 + lis r1,0xffff + mtspr SPRN_DEC,r0 /* prevent dec exceptions */ + mtspr SPRN_TBWL,r0 /* prevent fit & wdt exceptions */ + mtspr SPRN_TBWU,r0 + mtspr SPRN_TSR,r1 /* clear all timer exception status */ + mtspr SPRN_TCR,r0 /* disable all */ + mtspr SPRN_ESR,r0 /* clear exception syndrome register */ + mtxer r0 /* clear integer exception register */ + + /*----------------------------------------------------------------*/ + /* Debug setup -- some (not very good) ice's need an event*/ + /* to establish control :-( Define CONFIG_SYS_INIT_DBCR to the dbsr */ + /* value you need in this case 0x8cff 0000 should do the trick */ + /*----------------------------------------------------------------*/ +#if defined(CONFIG_SYS_INIT_DBCR) + lis r1,0xffff + ori r1,r1,0xffff + mtspr SPRN_DBSR,r1 /* Clear all status bits */ + lis r0,CONFIG_SYS_INIT_DBCR@h + ori r0,r0,CONFIG_SYS_INIT_DBCR@l + mtspr SPRN_DBCR0,r0 + isync +#endif + + /*----------------------------------------------------------------*/ + /* Setup the internal SRAM */ + /*----------------------------------------------------------------*/ + li r0,0 + +#ifdef CONFIG_SYS_INIT_RAM_DCACHE + /* Clear Dcache to use as RAM */ + addis r3,r0,CONFIG_SYS_INIT_RAM_ADDR@h + ori r3,r3,CONFIG_SYS_INIT_RAM_ADDR@l + addis r4,r0,CONFIG_SYS_INIT_RAM_END@h + ori r4,r4,CONFIG_SYS_INIT_RAM_END@l + rlwinm. r5,r4,0,27,31 + rlwinm r5,r4,27,5,31 + beq ..d_ran + addi r5,r5,0x0001 +..d_ran: + mtctr r5 +..d_ag: + dcbz r0,r3 + addi r3,r3,32 + bdnz ..d_ag + + /* + * Lock the init-ram/stack in d-cache, so that other regions + * may use d-cache as well + * Note, that this current implementation locks exactly 4k + * of d-cache, so please make sure that you don't define a + * bigger init-ram area. Take a look at the lwmon5 440EPx + * implementation as a reference. + */ + msync + isync + /* 8. set TFLOOR/NFLOOR to 8 (-> 8*16*32 bytes locked -> 4k) */ + lis r1,0x0201 + ori r1,r1,0xf808 + mtspr SPRN_DVLIM,r1 + lis r1,0x0808 + ori r1,r1,0x0808 + mtspr SPRN_DNV0,r1 + mtspr SPRN_DNV1,r1 + mtspr SPRN_DNV2,r1 + mtspr SPRN_DNV3,r1 + mtspr SPRN_DTV0,r1 + mtspr SPRN_DTV1,r1 + mtspr SPRN_DTV2,r1 + mtspr SPRN_DTV3,r1 + msync + isync +#endif /* CONFIG_SYS_INIT_RAM_DCACHE */ + + /* 440EP & 440GR are only 440er PPC's without internal SRAM */ +#if !defined(CONFIG_440EP) && !defined(CONFIG_440GR) + /* not all PPC's have internal SRAM usable as L2-cache */ +#if defined(CONFIG_440GX) || \ + defined(CONFIG_440SP) || defined(CONFIG_440SPE) || \ + defined(CONFIG_460SX) + mtdcr L2_CACHE_CFG,r0 /* Ensure L2 Cache is off */ +#elif defined(CONFIG_460EX) || defined(CONFIG_460GT) + lis r1, 0x0000 + ori r1,r1,0x0008 /* Set L2_CACHE_CFG[RDBW]=1 */ + mtdcr L2_CACHE_CFG,r1 +#endif + + lis r2,0x7fff + ori r2,r2,0xffff + mfdcr r1,ISRAM0_DPC + and r1,r1,r2 /* Disable parity check */ + mtdcr ISRAM0_DPC,r1 + mfdcr r1,ISRAM0_PMEG + and r1,r1,r2 /* Disable pwr mgmt */ + mtdcr ISRAM0_PMEG,r1 + + lis r1,0x8000 /* BAS = 8000_0000 */ +#if defined(CONFIG_440GX) || defined(CONFIG_440SP) + ori r1,r1,0x0980 /* first 64k */ + mtdcr ISRAM0_SB0CR,r1 + lis r1,0x8001 + ori r1,r1,0x0980 /* second 64k */ + mtdcr ISRAM0_SB1CR,r1 + lis r1, 0x8002 + ori r1,r1, 0x0980 /* third 64k */ + mtdcr ISRAM0_SB2CR,r1 + lis r1, 0x8003 + ori r1,r1, 0x0980 /* fourth 64k */ + mtdcr ISRAM0_SB3CR,r1 +#elif defined(CONFIG_440SPE) || defined(CONFIG_460EX) || defined(CONFIG_460GT) + lis r1,0x0000 /* BAS = X_0000_0000 */ + ori r1,r1,0x0984 /* first 64k */ + mtdcr ISRAM0_SB0CR,r1 + lis r1,0x0001 + ori r1,r1,0x0984 /* second 64k */ + mtdcr ISRAM0_SB1CR,r1 + lis r1, 0x0002 + ori r1,r1, 0x0984 /* third 64k */ + mtdcr ISRAM0_SB2CR,r1 + lis r1, 0x0003 + ori r1,r1, 0x0984 /* fourth 64k */ + mtdcr ISRAM0_SB3CR,r1 +#if defined(CONFIG_460EX) || defined(CONFIG_460GT) + lis r2,0x7fff + ori r2,r2,0xffff + mfdcr r1,ISRAM1_DPC + and r1,r1,r2 /* Disable parity check */ + mtdcr ISRAM1_DPC,r1 + mfdcr r1,ISRAM1_PMEG + and r1,r1,r2 /* Disable pwr mgmt */ + mtdcr ISRAM1_PMEG,r1 + + lis r1,0x0004 /* BAS = 4_0004_0000 */ + ori r1,r1,0x0984 /* 64k */ + mtdcr ISRAM1_SB0CR,r1 +#endif +#elif defined(CONFIG_460SX) + lis r1,0x0000 /* BAS = 0000_0000 */ + ori r1,r1,0x0B84 /* first 128k */ + mtdcr ISRAM0_SB0CR,r1 + lis r1,0x0001 + ori r1,r1,0x0B84 /* second 128k */ + mtdcr ISRAM0_SB1CR,r1 + lis r1, 0x0002 + ori r1,r1, 0x0B84 /* third 128k */ + mtdcr ISRAM0_SB2CR,r1 + lis r1, 0x0003 + ori r1,r1, 0x0B84 /* fourth 128k */ + mtdcr ISRAM0_SB3CR,r1 +#elif defined(CONFIG_440GP) + ori r1,r1,0x0380 /* 8k rw */ + mtdcr ISRAM0_SB0CR,r1 + mtdcr ISRAM0_SB1CR,r0 /* Disable bank 1 */ +#endif +#endif /* #if !defined(CONFIG_440EP) && !defined(CONFIG_440GR) */ + + /*----------------------------------------------------------------*/ + /* Setup the stack in internal SRAM */ + /*----------------------------------------------------------------*/ + lis r1,CONFIG_SYS_INIT_RAM_ADDR@h + ori r1,r1,CONFIG_SYS_INIT_SP_OFFSET@l + li r0,0 + stwu r0,-4(r1) + stwu r0,-4(r1) /* Terminate call chain */ + + stwu r1,-8(r1) /* Save back chain and move SP */ + lis r0,RESET_VECTOR@h /* Address of reset vector */ + ori r0,r0, RESET_VECTOR@l + stwu r1,-8(r1) /* Save back chain and move SP */ + stw r0,+12(r1) /* Save return addr (underflow vect) */ + +#ifdef CONFIG_NAND_SPL + bl nand_boot_common /* will not return */ +#else + GET_GOT + + bl cpu_init_f /* run low-level CPU init code (from Flash) */ + bl board_init_f +#endif + +#endif /* CONFIG_440 */ + +/*****************************************************************************/ +#ifdef CONFIG_IOP480 + /*----------------------------------------------------------------------- */ + /* Set up some machine state registers. */ + /*----------------------------------------------------------------------- */ + addi r0,r0,0x0000 /* initialize r0 to zero */ + mtspr SPRN_ESR,r0 /* clear Exception Syndrome Reg */ + mttcr r0 /* timer control register */ + mtexier r0 /* disable all interrupts */ + addis r4,r0,0xFFFF /* set r4 to 0xFFFFFFFF (status in the */ + ori r4,r4,0xFFFF /* dbsr is cleared by setting bits to 1) */ + mtdbsr r4 /* clear/reset the dbsr */ + mtexisr r4 /* clear all pending interrupts */ + addis r4,r0,0x8000 + mtexier r4 /* enable critical exceptions */ + addis r4,r0,0x0000 /* assume 403GCX - enable core clk */ + ori r4,r4,0x4020 /* dbling (no harm done on GA and GC */ + mtiocr r4 /* since bit not used) & DRC to latch */ + /* data bus on rising edge of CAS */ + /*----------------------------------------------------------------------- */ + /* Clear XER. */ + /*----------------------------------------------------------------------- */ + mtxer r0 + /*----------------------------------------------------------------------- */ + /* Invalidate i-cache and d-cache TAG arrays. */ + /*----------------------------------------------------------------------- */ + addi r3,0,1024 /* 1/4 of I-cache size, half of D-cache */ + addi r4,0,1024 /* 1/4 of I-cache */ +..cloop: + iccci 0,r3 + iccci r4,r3 + dccci 0,r3 + addic. r3,r3,-16 /* move back one cache line */ + bne ..cloop /* loop back to do rest until r3 = 0 */ + + /* */ + /* initialize IOP480 so it can read 1 MB code area for SRAM spaces */ + /* this requires enabling MA[17..0], by default only MA[12..0] are enabled. */ + /* */ + + /* first copy IOP480 register base address into r3 */ + addis r3,0,0x5000 /* IOP480 register base address hi */ +/* ori r3,r3,0x0000 / IOP480 register base address lo */ + +#ifdef CONFIG_ADCIOP + /* use r4 as the working variable */ + /* turn on CS3 (LOCCTL.7) */ + lwz r4,0x84(r3) /* LOCTL is at offset 0x84 */ + andi. r4,r4,0xff7f /* make bit 7 = 0 -- CS3 mode */ + stw r4,0x84(r3) /* LOCTL is at offset 0x84 */ +#endif + +#ifdef CONFIG_DASA_SIM + /* use r4 as the working variable */ + /* turn on MA17 (LOCCTL.7) */ + lwz r4,0x84(r3) /* LOCTL is at offset 0x84 */ + ori r4,r4,0x80 /* make bit 7 = 1 -- MA17 mode */ + stw r4,0x84(r3) /* LOCTL is at offset 0x84 */ +#endif + + /* turn on MA16..13 (LCS0BRD.12 = 0) */ + lwz r4,0x100(r3) /* LCS0BRD is at offset 0x100 */ + andi. r4,r4,0xefff /* make bit 12 = 0 */ + stw r4,0x100(r3) /* LCS0BRD is at offset 0x100 */ + + /* make sure above stores all comlete before going on */ + sync + + /* last thing, set local init status done bit (DEVINIT.31) */ + lwz r4,0x80(r3) /* DEVINIT is at offset 0x80 */ + oris r4,r4,0x8000 /* make bit 31 = 1 */ + stw r4,0x80(r3) /* DEVINIT is at offset 0x80 */ + + /* clear all pending interrupts and disable all interrupts */ + li r4,-1 /* set p1 to 0xffffffff */ + stw r4,0x1b0(r3) /* clear all pending interrupts */ + stw r4,0x1b8(r3) /* clear all pending interrupts */ + li r4,0 /* set r4 to 0 */ + stw r4,0x1b4(r3) /* disable all interrupts */ + stw r4,0x1bc(r3) /* disable all interrupts */ + + /* make sure above stores all comlete before going on */ + sync + + /* Set-up icache cacheability. */ + lis r1, CONFIG_SYS_ICACHE_SACR_VALUE@h + ori r1, r1, CONFIG_SYS_ICACHE_SACR_VALUE@l + mticcr r1 + isync + + /* Set-up dcache cacheability. */ + lis r1, CONFIG_SYS_DCACHE_SACR_VALUE@h + ori r1, r1, CONFIG_SYS_DCACHE_SACR_VALUE@l + mtdccr r1 + + addis r1,r0,CONFIG_SYS_INIT_RAM_ADDR@h + ori r1,r1,CONFIG_SYS_INIT_SP_OFFSET /* set up the stack to SDRAM */ + li r0, 0 /* Make room for stack frame header and */ + stwu r0, -4(r1) /* clear final stack frame so that */ + stwu r0, -4(r1) /* stack backtraces terminate cleanly */ + + GET_GOT /* initialize GOT access */ + + bl board_init_f /* run first part of init code (from Flash) */ + +#endif /* CONFIG_IOP480 */ + +/*****************************************************************************/ +#if defined(CONFIG_405GP) || defined(CONFIG_405CR) || \ + defined(CONFIG_405EP) || defined(CONFIG_405EZ) || \ + defined(CONFIG_405EX) || defined(CONFIG_405) + /*----------------------------------------------------------------------- */ + /* Clear and set up some registers. */ + /*----------------------------------------------------------------------- */ + addi r4,r0,0x0000 +#if !defined(CONFIG_405EX) + mtspr SPRN_SGR,r4 +#else + /* + * On 405EX, completely clearing the SGR leads to PPC hangup + * upon PCIe configuration access. The PCIe memory regions + * need to be guarded! + */ + lis r3,0x0000 + ori r3,r3,0x7FFC + mtspr SPRN_SGR,r3 +#endif + mtspr SPRN_DCWR,r4 + mtesr r4 /* clear Exception Syndrome Reg */ + mttcr r4 /* clear Timer Control Reg */ + mtxer r4 /* clear Fixed-Point Exception Reg */ + mtevpr r4 /* clear Exception Vector Prefix Reg */ + addi r4,r0,(0xFFFF-0x10000) /* set r4 to 0xFFFFFFFF (status in the */ + /* dbsr is cleared by setting bits to 1) */ + mtdbsr r4 /* clear/reset the dbsr */ + + /* Invalidate the i- and d-caches. */ + bl invalidate_icache + bl invalidate_dcache + + /* Set-up icache cacheability. */ + lis r4, CONFIG_SYS_ICACHE_SACR_VALUE@h + ori r4, r4, CONFIG_SYS_ICACHE_SACR_VALUE@l + mticcr r4 + isync + + /* Set-up dcache cacheability. */ + lis r4, CONFIG_SYS_DCACHE_SACR_VALUE@h + ori r4, r4, CONFIG_SYS_DCACHE_SACR_VALUE@l + mtdccr r4 + +#if !(defined(CONFIG_SYS_EBC_PB0AP) && defined(CONFIG_SYS_EBC_PB0CR))\ + && !defined (CONFIG_XILINX_405) + /*----------------------------------------------------------------------- */ + /* Tune the speed and size for flash CS0 */ + /*----------------------------------------------------------------------- */ + bl ext_bus_cntlr_init +#endif + +#if !(defined(CONFIG_SYS_INIT_DCACHE_CS) || defined(CONFIG_SYS_TEMP_STACK_OCM)) + /* + * For boards that don't have OCM and can't use the data cache + * for their primordial stack, setup stack here directly after the + * SDRAM is initialized in ext_bus_cntlr_init. + */ + lis r1, CONFIG_SYS_INIT_RAM_ADDR@h + ori r1,r1,CONFIG_SYS_INIT_SP_OFFSET /* set up the stack in SDRAM */ + + li r0, 0 /* Make room for stack frame header and */ + stwu r0, -4(r1) /* clear final stack frame so that */ + stwu r0, -4(r1) /* stack backtraces terminate cleanly */ + /* + * Set up a dummy frame to store reset vector as return address. + * this causes stack underflow to reset board. + */ + stwu r1, -8(r1) /* Save back chain and move SP */ + lis r0, RESET_VECTOR@h /* Address of reset vector */ + ori r0, r0, RESET_VECTOR@l + stwu r1, -8(r1) /* Save back chain and move SP */ + stw r0, +12(r1) /* Save return addr (underflow vect) */ +#endif /* !(CONFIG_SYS_INIT_DCACHE_CS || !CONFIG_SYS_TEM_STACK_OCM) */ + +#if defined(CONFIG_405EP) + /*----------------------------------------------------------------------- */ + /* DMA Status, clear to come up clean */ + /*----------------------------------------------------------------------- */ + addis r3,r0, 0xFFFF /* Clear all existing DMA status */ + ori r3,r3, 0xFFFF + mtdcr DMASR, r3 + + bl ppc405ep_init /* do ppc405ep specific init */ +#endif /* CONFIG_405EP */ + +#if defined(CONFIG_SYS_OCM_DATA_ADDR) && defined(CONFIG_SYS_OCM_DATA_SIZE) +#if defined(CONFIG_405EZ) + /******************************************************************** + * Setup OCM - On Chip Memory - PPC405EZ uses OCM Controller V2 + *******************************************************************/ + /* + * We can map the OCM on the PLB3, so map it at + * CONFIG_SYS_OCM_DATA_ADDR + 0x8000 + */ + lis r3,CONFIG_SYS_OCM_DATA_ADDR@h /* OCM location */ + ori r3,r3,CONFIG_SYS_OCM_DATA_ADDR@l + ori r3,r3,0x0270 /* 16K for Bank 1, R/W/Enable */ + mtdcr OCM0_PLBCR1,r3 /* Set PLB Access */ + ori r3,r3,0x4000 /* Add 0x4000 for bank 2 */ + mtdcr OCM0_PLBCR2,r3 /* Set PLB Access */ + isync + + lis r3,CONFIG_SYS_OCM_DATA_ADDR@h /* OCM location */ + ori r3,r3,CONFIG_SYS_OCM_DATA_ADDR@l + ori r3,r3,0x0270 /* 16K for Bank 1, R/W/Enable */ + mtdcr OCM0_DSRC1, r3 /* Set Data Side */ + mtdcr OCM0_ISRC1, r3 /* Set Instruction Side */ + ori r3,r3,0x4000 /* Add 0x4000 for bank 2 */ + mtdcr OCM0_DSRC2, r3 /* Set Data Side */ + mtdcr OCM0_ISRC2, r3 /* Set Instruction Side */ + addis r3,0,0x0800 /* OCM Data Parity Disable - 1 Wait State */ + mtdcr OCM0_DISDPC,r3 + + isync +#else /* CONFIG_405EZ */ + /******************************************************************** + * Setup OCM - On Chip Memory + *******************************************************************/ + /* Setup OCM */ + lis r0, 0x7FFF + ori r0, r0, 0xFFFF + mfdcr r3, OCM0_ISCNTL /* get instr-side IRAM config */ + mfdcr r4, OCM0_DSCNTL /* get data-side IRAM config */ + and r3, r3, r0 /* disable data-side IRAM */ + and r4, r4, r0 /* disable data-side IRAM */ + mtdcr OCM0_ISCNTL, r3 /* set instr-side IRAM config */ + mtdcr OCM0_DSCNTL, r4 /* set data-side IRAM config */ + isync + + lis r3,CONFIG_SYS_OCM_DATA_ADDR@h /* OCM location */ + ori r3,r3,CONFIG_SYS_OCM_DATA_ADDR@l + mtdcr OCM0_DSARC, r3 + addis r4, 0, 0xC000 /* OCM data area enabled */ + mtdcr OCM0_DSCNTL, r4 + isync +#endif /* CONFIG_405EZ */ +#endif + + /*----------------------------------------------------------------------- */ + /* Setup temporary stack in DCACHE or OCM if needed for SDRAM SPD. */ + /*----------------------------------------------------------------------- */ +#ifdef CONFIG_SYS_INIT_DCACHE_CS + li r4, PBxAP + mtdcr EBC0_CFGADDR, r4 + lis r4, CONFIG_SYS_INIT_DCACHE_PBxAR@h + ori r4, r4, CONFIG_SYS_INIT_DCACHE_PBxAR@l + mtdcr EBC0_CFGDATA, r4 + + addi r4, 0, PBxCR + mtdcr EBC0_CFGADDR, r4 + lis r4, CONFIG_SYS_INIT_DCACHE_PBxCR@h + ori r4, r4, CONFIG_SYS_INIT_DCACHE_PBxCR@l + mtdcr EBC0_CFGDATA, r4 + + /* + * Enable the data cache for the 128MB storage access control region + * at CONFIG_SYS_INIT_RAM_ADDR. + */ + mfdccr r4 + oris r4, r4, PPC_128MB_SACR_VALUE(CONFIG_SYS_INIT_RAM_ADDR)@h + ori r4, r4, PPC_128MB_SACR_VALUE(CONFIG_SYS_INIT_RAM_ADDR)@l + mtdccr r4 + + /* + * Preallocate data cache lines to be used to avoid a subsequent + * cache miss and an ensuing machine check exception when exceptions + * are enabled. + */ + li r0, 0 + + lis r3, CONFIG_SYS_INIT_RAM_ADDR@h + ori r3, r3, CONFIG_SYS_INIT_RAM_ADDR@l + + lis r4, CONFIG_SYS_INIT_RAM_END@h + ori r4, r4, CONFIG_SYS_INIT_RAM_END@l + + /* + * Convert the size, in bytes, to the number of cache lines/blocks + * to preallocate. + */ + clrlwi. r5, r4, (32 - L1_CACHE_SHIFT) + srwi r5, r4, L1_CACHE_SHIFT + beq ..load_counter + addi r5, r5, 0x0001 +..load_counter: + mtctr r5 + + /* Preallocate the computed number of cache blocks. */ +..alloc_dcache_block: + dcba r0, r3 + addi r3, r3, L1_CACHE_BYTES + bdnz ..alloc_dcache_block + sync + + /* + * Load the initial stack pointer and data area and convert the size, + * in bytes, to the number of words to initialize to a known value. + */ + lis r1, CONFIG_SYS_INIT_RAM_ADDR@h + ori r1, r1, CONFIG_SYS_INIT_SP_OFFSET@l + + lis r4, (CONFIG_SYS_INIT_RAM_END >> 2)@h + ori r4, r4, (CONFIG_SYS_INIT_RAM_END >> 2)@l + mtctr r4 + + lis r2, CONFIG_SYS_INIT_RAM_ADDR@h + ori r2, r2, CONFIG_SYS_INIT_RAM_END@l + + lis r4, CONFIG_SYS_INIT_RAM_PATTERN@h + ori r4, r4, CONFIG_SYS_INIT_RAM_PATTERN@l + +..stackloop: + stwu r4, -4(r2) + bdnz ..stackloop + + /* + * Make room for stack frame header and clear final stack frame so + * that stack backtraces terminate cleanly. + */ + stwu r0, -4(r1) + stwu r0, -4(r1) + + /* + * Set up a dummy frame to store reset vector as return address. + * this causes stack underflow to reset board. + */ + stwu r1, -8(r1) /* Save back chain and move SP */ + addis r0, 0, RESET_VECTOR@h /* Address of reset vector */ + ori r0, r0, RESET_VECTOR@l + stwu r1, -8(r1) /* Save back chain and move SP */ + stw r0, +12(r1) /* Save return addr (underflow vect) */ + +#elif defined(CONFIG_SYS_TEMP_STACK_OCM) && \ + (defined(CONFIG_SYS_OCM_DATA_ADDR) && defined(CONFIG_SYS_OCM_DATA_SIZE)) + /* + * Stack in OCM. + */ + + /* Set up Stack at top of OCM */ + lis r1, (CONFIG_SYS_INIT_RAM_ADDR + CONFIG_SYS_INIT_SP_OFFSET)@h + ori r1, r1, (CONFIG_SYS_INIT_RAM_ADDR + CONFIG_SYS_INIT_SP_OFFSET)@l + + /* Set up a zeroized stack frame so that backtrace works right */ + li r0, 0 + stwu r0, -4(r1) + stwu r0, -4(r1) + + /* + * Set up a dummy frame to store reset vector as return address. + * this causes stack underflow to reset board. + */ + stwu r1, -8(r1) /* Save back chain and move SP */ + lis r0, RESET_VECTOR@h /* Address of reset vector */ + ori r0, r0, RESET_VECTOR@l + stwu r1, -8(r1) /* Save back chain and move SP */ + stw r0, +12(r1) /* Save return addr (underflow vect) */ +#endif /* CONFIG_SYS_INIT_DCACHE_CS */ + +#ifdef CONFIG_NAND_SPL + bl nand_boot_common /* will not return */ +#else + GET_GOT /* initialize GOT access */ + + bl cpu_init_f /* run low-level CPU init code (from Flash) */ + + /* NEVER RETURNS! */ + bl board_init_f /* run first part of init code (from Flash) */ +#endif /* CONFIG_NAND_SPL */ + +#endif /* CONFIG_405GP || CONFIG_405CR || CONFIG_405 || CONFIG_405EP */ + /*----------------------------------------------------------------------- */ + + +#ifndef CONFIG_NAND_SPL +/* + * This code finishes saving the registers to the exception frame + * and jumps to the appropriate handler for the exception. + * Register r21 is pointer into trap frame, r1 has new stack pointer. + */ + .globl transfer_to_handler +transfer_to_handler: + stw r22,_NIP(r21) + lis r22,MSR_POW@h + andc r23,r23,r22 + stw r23,_MSR(r21) + SAVE_GPR(7, r21) + SAVE_4GPRS(8, r21) + SAVE_8GPRS(12, r21) + SAVE_8GPRS(24, r21) + mflr r23 + andi. r24,r23,0x3f00 /* get vector offset */ + stw r24,TRAP(r21) + li r22,0 + stw r22,RESULT(r21) + mtspr SPRG2,r22 /* r1 is now kernel sp */ + lwz r24,0(r23) /* virtual address of handler */ + lwz r23,4(r23) /* where to go when done */ + mtspr SRR0,r24 + mtspr SRR1,r20 + mtlr r23 + SYNC + rfi /* jump to handler, enable MMU */ + +int_return: + mfmsr r28 /* Disable interrupts */ + li r4,0 + ori r4,r4,MSR_EE + andc r28,r28,r4 + SYNC /* Some chip revs need this... */ + mtmsr r28 + SYNC + lwz r2,_CTR(r1) + lwz r0,_LINK(r1) + mtctr r2 + mtlr r0 + lwz r2,_XER(r1) + lwz r0,_CCR(r1) + mtspr XER,r2 + mtcrf 0xFF,r0 + REST_10GPRS(3, r1) + REST_10GPRS(13, r1) + REST_8GPRS(23, r1) + REST_GPR(31, r1) + lwz r2,_NIP(r1) /* Restore environment */ + lwz r0,_MSR(r1) + mtspr SRR0,r2 + mtspr SRR1,r0 + lwz r0,GPR0(r1) + lwz r2,GPR2(r1) + lwz r1,GPR1(r1) + SYNC + rfi + +crit_return: + mfmsr r28 /* Disable interrupts */ + li r4,0 + ori r4,r4,MSR_EE + andc r28,r28,r4 + SYNC /* Some chip revs need this... */ + mtmsr r28 + SYNC + lwz r2,_CTR(r1) + lwz r0,_LINK(r1) + mtctr r2 + mtlr r0 + lwz r2,_XER(r1) + lwz r0,_CCR(r1) + mtspr XER,r2 + mtcrf 0xFF,r0 + REST_10GPRS(3, r1) + REST_10GPRS(13, r1) + REST_8GPRS(23, r1) + REST_GPR(31, r1) + lwz r2,_NIP(r1) /* Restore environment */ + lwz r0,_MSR(r1) + mtspr SPRN_CSRR0,r2 + mtspr SPRN_CSRR1,r0 + lwz r0,GPR0(r1) + lwz r2,GPR2(r1) + lwz r1,GPR1(r1) + SYNC + rfci + +#ifdef CONFIG_440 +mck_return: + mfmsr r28 /* Disable interrupts */ + li r4,0 + ori r4,r4,MSR_EE + andc r28,r28,r4 + SYNC /* Some chip revs need this... */ + mtmsr r28 + SYNC + lwz r2,_CTR(r1) + lwz r0,_LINK(r1) + mtctr r2 + mtlr r0 + lwz r2,_XER(r1) + lwz r0,_CCR(r1) + mtspr XER,r2 + mtcrf 0xFF,r0 + REST_10GPRS(3, r1) + REST_10GPRS(13, r1) + REST_8GPRS(23, r1) + REST_GPR(31, r1) + lwz r2,_NIP(r1) /* Restore environment */ + lwz r0,_MSR(r1) + mtspr SPRN_MCSRR0,r2 + mtspr SPRN_MCSRR1,r0 + lwz r0,GPR0(r1) + lwz r2,GPR2(r1) + lwz r1,GPR1(r1) + SYNC + rfmci +#endif /* CONFIG_440 */ + + + .globl get_pvr +get_pvr: + mfspr r3, PVR + blr + +/*------------------------------------------------------------------------------- */ +/* Function: out16 */ +/* Description: Output 16 bits */ +/*------------------------------------------------------------------------------- */ + .globl out16 +out16: + sth r4,0x0000(r3) + blr + +/*------------------------------------------------------------------------------- */ +/* Function: out16r */ +/* Description: Byte reverse and output 16 bits */ +/*------------------------------------------------------------------------------- */ + .globl out16r +out16r: + sthbrx r4,r0,r3 + blr + +/*------------------------------------------------------------------------------- */ +/* Function: out32r */ +/* Description: Byte reverse and output 32 bits */ +/*------------------------------------------------------------------------------- */ + .globl out32r +out32r: + stwbrx r4,r0,r3 + blr + +/*------------------------------------------------------------------------------- */ +/* Function: in16 */ +/* Description: Input 16 bits */ +/*------------------------------------------------------------------------------- */ + .globl in16 +in16: + lhz r3,0x0000(r3) + blr + +/*------------------------------------------------------------------------------- */ +/* Function: in16r */ +/* Description: Input 16 bits and byte reverse */ +/*------------------------------------------------------------------------------- */ + .globl in16r +in16r: + lhbrx r3,r0,r3 + blr + +/*------------------------------------------------------------------------------- */ +/* Function: in32r */ +/* Description: Input 32 bits and byte reverse */ +/*------------------------------------------------------------------------------- */ + .globl in32r +in32r: + lwbrx r3,r0,r3 + blr + +/* + * void relocate_code (addr_sp, gd, addr_moni) + * + * This "function" does not return, instead it continues in RAM + * after relocating the monitor code. + * + * r3 = Relocated stack pointer + * r4 = Relocated global data pointer + * r5 = Relocated text pointer + */ + .globl relocate_code +relocate_code: +#if defined(CONFIG_4xx_DCACHE) || defined(CONFIG_SYS_INIT_DCACHE_CS) + /* + * We need to flush the initial global data (gd_t) before the dcache + * will be invalidated. + */ + + /* Save registers */ + mr r9, r3 + mr r10, r4 + mr r11, r5 + + /* Flush initial global data range */ + mr r3, r4 + addi r4, r4, CONFIG_SYS_GBL_DATA_SIZE@l + bl flush_dcache_range + +#if defined(CONFIG_SYS_INIT_DCACHE_CS) + /* + * Undo the earlier data cache set-up for the primordial stack and + * data area. First, invalidate the data cache and then disable data + * cacheability for that area. Finally, restore the EBC values, if + * any. + */ + + /* Invalidate the primordial stack and data area in cache */ + lis r3, CONFIG_SYS_INIT_RAM_ADDR@h + ori r3, r3, CONFIG_SYS_INIT_RAM_ADDR@l + + lis r4, CONFIG_SYS_INIT_RAM_END@h + ori r4, r4, CONFIG_SYS_INIT_RAM_END@l + add r4, r4, r3 + + bl invalidate_dcache_range + + /* Disable cacheability for the region */ + mfdccr r3 + lis r4, ~PPC_128MB_SACR_VALUE(CONFIG_SYS_INIT_RAM_ADDR)@h + ori r4, r4, ~PPC_128MB_SACR_VALUE(CONFIG_SYS_INIT_RAM_ADDR)@l + and r3, r3, r4 + mtdccr r3 + + /* Restore the EBC parameters */ + li r3, PBxAP + mtdcr EBC0_CFGADDR, r3 + lis r3, PBxAP_VAL@h + ori r3, r3, PBxAP_VAL@l + mtdcr EBC0_CFGDATA, r3 + + li r3, PBxCR + mtdcr EBC0_CFGADDR, r3 + lis r3, PBxCR_VAL@h + ori r3, r3, PBxCR_VAL@l + mtdcr EBC0_CFGDATA, r3 +#endif /* defined(CONFIG_SYS_INIT_DCACHE_CS) */ + + /* Restore registers */ + mr r3, r9 + mr r4, r10 + mr r5, r11 +#endif /* defined(CONFIG_4xx_DCACHE) || defined(CONFIG_SYS_INIT_DCACHE_CS) */ + +#ifdef CONFIG_SYS_INIT_RAM_DCACHE + /* + * Unlock the previously locked d-cache + */ + msync + isync + /* set TFLOOR/NFLOOR to 0 again */ + lis r6,0x0001 + ori r6,r6,0xf800 + mtspr SPRN_DVLIM,r6 + lis r6,0x0000 + ori r6,r6,0x0000 + mtspr SPRN_DNV0,r6 + mtspr SPRN_DNV1,r6 + mtspr SPRN_DNV2,r6 + mtspr SPRN_DNV3,r6 + mtspr SPRN_DTV0,r6 + mtspr SPRN_DTV1,r6 + mtspr SPRN_DTV2,r6 + mtspr SPRN_DTV3,r6 + msync + isync +#endif /* CONFIG_SYS_INIT_RAM_DCACHE */ + +#if defined(CONFIG_440EP) || defined(CONFIG_440GR) || \ + defined(CONFIG_440EPX) || defined(CONFIG_440GRX) || \ + defined(CONFIG_440SP) || defined(CONFIG_440SPE) || \ + defined(CONFIG_460EX) || defined(CONFIG_460GT) || \ + defined(CONFIG_460SX) + /* + * On some 440er platforms the cache is enabled in the first TLB (Boot-CS) + * to speed up the boot process. Now this cache needs to be disabled. + */ + iccci 0,0 /* Invalidate inst cache */ + dccci 0,0 /* Invalidate data cache, now no longer our stack */ + sync + isync + + /* Clear all potential pending exceptions */ + mfspr r1,SPRN_MCSR + mtspr SPRN_MCSR,r1 +#ifdef CONFIG_SYS_TLB_FOR_BOOT_FLASH + addi r1,r0,CONFIG_SYS_TLB_FOR_BOOT_FLASH /* Use defined TLB */ +#else + addi r1,r0,0x0000 /* Default TLB entry is #0 */ +#endif /* CONFIG_SYS_TLB_FOR_BOOT_FLASH */ + tlbre r0,r1,0x0002 /* Read contents */ + ori r0,r0,0x0c00 /* Or in the inhibit, write through bit */ + tlbwe r0,r1,0x0002 /* Save it out */ + sync + isync +#endif /* defined(CONFIG_440EP) || ... || defined(CONFIG_460GT) */ + mr r1, r3 /* Set new stack pointer */ + mr r9, r4 /* Save copy of Init Data pointer */ + mr r10, r5 /* Save copy of Destination Address */ + + GET_GOT + mr r3, r5 /* Destination Address */ + lis r4, CONFIG_SYS_MONITOR_BASE@h /* Source Address */ + ori r4, r4, CONFIG_SYS_MONITOR_BASE@l + lwz r5, GOT(__init_end) + sub r5, r5, r4 + li r6, L1_CACHE_BYTES /* Cache Line Size */ + + /* + * Fix GOT pointer: + * + * New GOT-PTR = (old GOT-PTR - CONFIG_SYS_MONITOR_BASE) + Destination Address + * + * Offset: + */ + sub r15, r10, r4 + + /* First our own GOT */ + add r12, r12, r15 + /* then the one used by the C code */ + add r30, r30, r15 + + /* + * Now relocate code + */ + + cmplw cr1,r3,r4 + addi r0,r5,3 + srwi. r0,r0,2 + beq cr1,4f /* In place copy is not necessary */ + beq 7f /* Protect against 0 count */ + mtctr r0 + bge cr1,2f + + la r8,-4(r4) + la r7,-4(r3) +1: lwzu r0,4(r8) + stwu r0,4(r7) + bdnz 1b + b 4f + +2: slwi r0,r0,2 + add r8,r4,r0 + add r7,r3,r0 +3: lwzu r0,-4(r8) + stwu r0,-4(r7) + bdnz 3b + +/* + * Now flush the cache: note that we must start from a cache aligned + * address. Otherwise we might miss one cache line. + */ +4: cmpwi r6,0 + add r5,r3,r5 + beq 7f /* Always flush prefetch queue in any case */ + subi r0,r6,1 + andc r3,r3,r0 + mr r4,r3 +5: dcbst 0,r4 + add r4,r4,r6 + cmplw r4,r5 + blt 5b + sync /* Wait for all dcbst to complete on bus */ + mr r4,r3 +6: icbi 0,r4 + add r4,r4,r6 + cmplw r4,r5 + blt 6b +7: sync /* Wait for all icbi to complete on bus */ + isync + +/* + * We are done. Do not return, instead branch to second part of board + * initialization, now running from RAM. + */ + + addi r0, r10, in_ram - _start + _START_OFFSET + mtlr r0 + blr /* NEVER RETURNS! */ + +in_ram: + + /* + * Relocation Function, r12 point to got2+0x8000 + * + * Adjust got2 pointers, no need to check for 0, this code + * already puts a few entries in the table. + */ + li r0,__got2_entries@sectoff@l + la r3,GOT(_GOT2_TABLE_) + lwz r11,GOT(_GOT2_TABLE_) + mtctr r0 + sub r11,r3,r11 + addi r3,r3,-4 +1: lwzu r0,4(r3) + cmpwi r0,0 + beq- 2f + add r0,r0,r11 + stw r0,0(r3) +2: bdnz 1b + + /* + * Now adjust the fixups and the pointers to the fixups + * in case we need to move ourselves again. + */ + li r0,__fixup_entries@sectoff@l + lwz r3,GOT(_FIXUP_TABLE_) + cmpwi r0,0 + mtctr r0 + addi r3,r3,-4 + beq 4f +3: lwzu r4,4(r3) + lwzux r0,r4,r11 + add r0,r0,r11 + stw r10,0(r3) + stw r0,0(r4) + bdnz 3b +4: +clear_bss: + /* + * Now clear BSS segment + */ + lwz r3,GOT(__bss_start) + lwz r4,GOT(_end) + + cmplw 0, r3, r4 + beq 7f + + li r0, 0 + + andi. r5, r4, 3 + beq 6f + sub r4, r4, r5 + mtctr r5 + mr r5, r4 +5: stb r0, 0(r5) + addi r5, r5, 1 + bdnz 5b +6: + stw r0, 0(r3) + addi r3, r3, 4 + cmplw 0, r3, r4 + bne 6b + +7: + mr r3, r9 /* Init Data pointer */ + mr r4, r10 /* Destination Address */ + bl board_init_r + + /* + * Copy exception vector code to low memory + * + * r3: dest_addr + * r7: source address, r8: end address, r9: target address + */ + .globl trap_init +trap_init: + mflr r4 /* save link register */ + GET_GOT + lwz r7, GOT(_start_of_vectors) + lwz r8, GOT(_end_of_vectors) + + li r9, 0x100 /* reset vector always at 0x100 */ + + cmplw 0, r7, r8 + bgelr /* return if r7>=r8 - just in case */ +1: + lwz r0, 0(r7) + stw r0, 0(r9) + addi r7, r7, 4 + addi r9, r9, 4 + cmplw 0, r7, r8 + bne 1b + + /* + * relocate `hdlr' and `int_return' entries + */ + li r7, .L_MachineCheck - _start + _START_OFFSET + li r8, Alignment - _start + _START_OFFSET +2: + bl trap_reloc + addi r7, r7, 0x100 /* next exception vector */ + cmplw 0, r7, r8 + blt 2b + + li r7, .L_Alignment - _start + _START_OFFSET + bl trap_reloc + + li r7, .L_ProgramCheck - _start + _START_OFFSET + bl trap_reloc + +#ifdef CONFIG_440 + li r7, .L_FPUnavailable - _start + _START_OFFSET + bl trap_reloc + + li r7, .L_Decrementer - _start + _START_OFFSET + bl trap_reloc + + li r7, .L_APU - _start + _START_OFFSET + bl trap_reloc + + li r7, .L_InstructionTLBError - _start + _START_OFFSET + bl trap_reloc + + li r7, .L_DataTLBError - _start + _START_OFFSET + bl trap_reloc +#else /* CONFIG_440 */ + li r7, .L_PIT - _start + _START_OFFSET + bl trap_reloc + + li r7, .L_InstructionTLBMiss - _start + _START_OFFSET + bl trap_reloc + + li r7, .L_DataTLBMiss - _start + _START_OFFSET + bl trap_reloc +#endif /* CONFIG_440 */ + + li r7, .L_DebugBreakpoint - _start + _START_OFFSET + bl trap_reloc + +#if !defined(CONFIG_440) + addi r7,r0,0x1000 /* set ME bit (Machine Exceptions) */ + oris r7,r7,0x0002 /* set CE bit (Critical Exceptions) */ + mtmsr r7 /* change MSR */ +#else + bl __440_msr_set + b __440_msr_continue + +__440_msr_set: + addi r7,r0,0x1000 /* set ME bit (Machine Exceptions) */ + oris r7,r7,0x0002 /* set CE bit (Critical Exceptions) */ + mtspr SPRN_SRR1,r7 + mflr r7 + mtspr SPRN_SRR0,r7 + rfi +__440_msr_continue: +#endif + + mtlr r4 /* restore link register */ + blr + +#if defined(CONFIG_440) +/*----------------------------------------------------------------------------+ +| dcbz_area. ++----------------------------------------------------------------------------*/ + function_prolog(dcbz_area) + rlwinm. r5,r4,0,27,31 + rlwinm r5,r4,27,5,31 + beq ..d_ra2 + addi r5,r5,0x0001 +..d_ra2:mtctr r5 +..d_ag2:dcbz r0,r3 + addi r3,r3,32 + bdnz ..d_ag2 + sync + blr + function_epilog(dcbz_area) +#endif /* CONFIG_440 */ +#endif /* CONFIG_NAND_SPL */ + +/*------------------------------------------------------------------------------- */ +/* Function: in8 */ +/* Description: Input 8 bits */ +/*------------------------------------------------------------------------------- */ + .globl in8 +in8: + lbz r3,0x0000(r3) + blr + +/*------------------------------------------------------------------------------- */ +/* Function: out8 */ +/* Description: Output 8 bits */ +/*------------------------------------------------------------------------------- */ + .globl out8 +out8: + stb r4,0x0000(r3) + blr + +/*------------------------------------------------------------------------------- */ +/* Function: out32 */ +/* Description: Output 32 bits */ +/*------------------------------------------------------------------------------- */ + .globl out32 +out32: + stw r4,0x0000(r3) + blr + +/*------------------------------------------------------------------------------- */ +/* Function: in32 */ +/* Description: Input 32 bits */ +/*------------------------------------------------------------------------------- */ + .globl in32 +in32: + lwz 3,0x0000(3) + blr + +/**************************************************************************/ +/* PPC405EP specific stuff */ +/**************************************************************************/ +#ifdef CONFIG_405EP +ppc405ep_init: + +#ifdef CONFIG_BUBINGA + /* + * Initialize EBC chip selects 1 & 4 and GPIO pins (for alternate + * function) to support FPGA and NVRAM accesses below. + */ + + lis r3,GPIO0_OSRH@h /* config GPIO output select */ + ori r3,r3,GPIO0_OSRH@l + lis r4,CONFIG_SYS_GPIO0_OSRH@h + ori r4,r4,CONFIG_SYS_GPIO0_OSRH@l + stw r4,0(r3) + lis r3,GPIO0_OSRL@h + ori r3,r3,GPIO0_OSRL@l + lis r4,CONFIG_SYS_GPIO0_OSRL@h + ori r4,r4,CONFIG_SYS_GPIO0_OSRL@l + stw r4,0(r3) + + lis r3,GPIO0_ISR1H@h /* config GPIO input select */ + ori r3,r3,GPIO0_ISR1H@l + lis r4,CONFIG_SYS_GPIO0_ISR1H@h + ori r4,r4,CONFIG_SYS_GPIO0_ISR1H@l + stw r4,0(r3) + lis r3,GPIO0_ISR1L@h + ori r3,r3,GPIO0_ISR1L@l + lis r4,CONFIG_SYS_GPIO0_ISR1L@h + ori r4,r4,CONFIG_SYS_GPIO0_ISR1L@l + stw r4,0(r3) + + lis r3,GPIO0_TSRH@h /* config GPIO three-state select */ + ori r3,r3,GPIO0_TSRH@l + lis r4,CONFIG_SYS_GPIO0_TSRH@h + ori r4,r4,CONFIG_SYS_GPIO0_TSRH@l + stw r4,0(r3) + lis r3,GPIO0_TSRL@h + ori r3,r3,GPIO0_TSRL@l + lis r4,CONFIG_SYS_GPIO0_TSRL@h + ori r4,r4,CONFIG_SYS_GPIO0_TSRL@l + stw r4,0(r3) + + lis r3,GPIO0_TCR@h /* config GPIO driver output enables */ + ori r3,r3,GPIO0_TCR@l + lis r4,CONFIG_SYS_GPIO0_TCR@h + ori r4,r4,CONFIG_SYS_GPIO0_TCR@l + stw r4,0(r3) + + li r3,PB1AP /* program EBC bank 1 for RTC access */ + mtdcr EBC0_CFGADDR,r3 + lis r3,CONFIG_SYS_EBC_PB1AP@h + ori r3,r3,CONFIG_SYS_EBC_PB1AP@l + mtdcr EBC0_CFGDATA,r3 + li r3,PB1CR + mtdcr EBC0_CFGADDR,r3 + lis r3,CONFIG_SYS_EBC_PB1CR@h + ori r3,r3,CONFIG_SYS_EBC_PB1CR@l + mtdcr EBC0_CFGDATA,r3 + + li r3,PB1AP /* program EBC bank 1 for RTC access */ + mtdcr EBC0_CFGADDR,r3 + lis r3,CONFIG_SYS_EBC_PB1AP@h + ori r3,r3,CONFIG_SYS_EBC_PB1AP@l + mtdcr EBC0_CFGDATA,r3 + li r3,PB1CR + mtdcr EBC0_CFGADDR,r3 + lis r3,CONFIG_SYS_EBC_PB1CR@h + ori r3,r3,CONFIG_SYS_EBC_PB1CR@l + mtdcr EBC0_CFGDATA,r3 + + li r3,PB4AP /* program EBC bank 4 for FPGA access */ + mtdcr EBC0_CFGADDR,r3 + lis r3,CONFIG_SYS_EBC_PB4AP@h + ori r3,r3,CONFIG_SYS_EBC_PB4AP@l + mtdcr EBC0_CFGDATA,r3 + li r3,PB4CR + mtdcr EBC0_CFGADDR,r3 + lis r3,CONFIG_SYS_EBC_PB4CR@h + ori r3,r3,CONFIG_SYS_EBC_PB4CR@l + mtdcr EBC0_CFGDATA,r3 +#endif + + /* + !----------------------------------------------------------------------- + ! Check to see if chip is in bypass mode. + ! If so, write stored CPC0_PLLMR0 and CPC0_PLLMR1 values and perform a + ! CPU reset Otherwise, skip this step and keep going. + ! Note: Running BIOS in bypass mode is not supported since PLB speed + ! will not be fast enough for the SDRAM (min 66MHz) + !----------------------------------------------------------------------- + */ + mfdcr r5, CPC0_PLLMR1 + rlwinm r4,r5,1,0x1 /* get system clock source (SSCS) */ + cmpi cr0,0,r4,0x1 + + beq pll_done /* if SSCS =b'1' then PLL has */ + /* already been set */ + /* and CPU has been reset */ + /* so skip to next section */ + +#ifdef CONFIG_BUBINGA + /* + !----------------------------------------------------------------------- + ! Read NVRAM to get value to write in PLLMR. + ! If value has not been correctly saved, write default value + ! Default config values (assuming on-board 33MHz SYS_CLK) are above. + ! See CPU_DEFAULT_200 and CPU_DEFAULT_266 above. + ! + ! WARNING: This code assumes the first three words in the nvram_t + ! structure in openbios.h. Changing the beginning of + ! the structure will break this code. + ! + !----------------------------------------------------------------------- + */ + addis r3,0,NVRAM_BASE@h + addi r3,r3,NVRAM_BASE@l + + lwz r4, 0(r3) + addis r5,0,NVRVFY1@h + addi r5,r5,NVRVFY1@l + cmp cr0,0,r4,r5 /* Compare 1st NVRAM Magic number*/ + bne ..no_pllset + addi r3,r3,4 + lwz r4, 0(r3) + addis r5,0,NVRVFY2@h + addi r5,r5,NVRVFY2@l + cmp cr0,0,r4,r5 /* Compare 2 NVRAM Magic number */ + bne ..no_pllset + addi r3,r3,8 /* Skip over conf_size */ + lwz r4, 4(r3) /* Load PLLMR1 value from NVRAM */ + lwz r3, 0(r3) /* Load PLLMR0 value from NVRAM */ + rlwinm r5,r4,1,0x1 /* get system clock source (SSCS) */ + cmpi cr0,0,r5,1 /* See if PLL is locked */ + beq pll_write +..no_pllset: +#endif /* CONFIG_BUBINGA */ + +#ifdef CONFIG_TAIHU + mfdcr r4, CPC0_BOOT + andi. r5, r4, CPC0_BOOT_SEP@l + bne strap_1 /* serial eeprom present */ + addis r5,0,CPLD_REG0_ADDR@h + ori r5,r5,CPLD_REG0_ADDR@l + andi. r5, r5, 0x10 + bne _pci_66mhz +#endif /* CONFIG_TAIHU */ + +#if defined(CONFIG_ZEUS) + mfdcr r4, CPC0_BOOT + andi. r5, r4, CPC0_BOOT_SEP@l + bne strap_1 /* serial eeprom present */ + lis r3,0x0000 + addi r3,r3,0x3030 + lis r4,0x8042 + addi r4,r4,0x223e + b 1f +strap_1: + mfdcr r3, CPC0_PLLMR0 + mfdcr r4, CPC0_PLLMR1 + b 1f +#endif + + addis r3,0,PLLMR0_DEFAULT@h /* PLLMR0 default value */ + ori r3,r3,PLLMR0_DEFAULT@l /* */ + addis r4,0,PLLMR1_DEFAULT@h /* PLLMR1 default value */ + ori r4,r4,PLLMR1_DEFAULT@l /* */ + +#ifdef CONFIG_TAIHU + b 1f +_pci_66mhz: + addis r3,0,PLLMR0_DEFAULT_PCI66@h + ori r3,r3,PLLMR0_DEFAULT_PCI66@l + addis r4,0,PLLMR1_DEFAULT_PCI66@h + ori r4,r4,PLLMR1_DEFAULT_PCI66@l + b 1f +strap_1: + mfdcr r3, CPC0_PLLMR0 + mfdcr r4, CPC0_PLLMR1 +#endif /* CONFIG_TAIHU */ + +1: + b pll_write /* Write the CPC0_PLLMR with new value */ + +pll_done: + /* + !----------------------------------------------------------------------- + ! Clear Soft Reset Register + ! This is needed to enable PCI if not booting from serial EPROM + !----------------------------------------------------------------------- + */ + addi r3, 0, 0x0 + mtdcr CPC0_SRR, r3 + + addis r3,0,0x0010 + mtctr r3 +pci_wait: + bdnz pci_wait + + blr /* return to main code */ + +/* +!----------------------------------------------------------------------------- +! Function: pll_write +! Description: Updates the value of the CPC0_PLLMR according to CMOS27E documentation +! That is: +! 1. Pll is first disabled (de-activated by putting in bypass mode) +! 2. PLL is reset +! 3. Clock dividers are set while PLL is held in reset and bypassed +! 4. PLL Reset is cleared +! 5. Wait 100us for PLL to lock +! 6. A core reset is performed +! Input: r3 = Value to write to CPC0_PLLMR0 +! Input: r4 = Value to write to CPC0_PLLMR1 +! Output r3 = none +!----------------------------------------------------------------------------- +*/ + .globl pll_write +pll_write: + mfdcr r5, CPC0_UCR + andis. r5,r5,0xFFFF + ori r5,r5,0x0101 /* Stop the UART clocks */ + mtdcr CPC0_UCR,r5 /* Before changing PLL */ + + mfdcr r5, CPC0_PLLMR1 + rlwinm r5,r5,0,0x7FFFFFFF /* Disable PLL */ + mtdcr CPC0_PLLMR1,r5 + oris r5,r5,0x4000 /* Set PLL Reset */ + mtdcr CPC0_PLLMR1,r5 + + mtdcr CPC0_PLLMR0,r3 /* Set clock dividers */ + rlwinm r5,r4,0,0x3FFFFFFF /* Reset & Bypass new PLL dividers */ + oris r5,r5,0x4000 /* Set PLL Reset */ + mtdcr CPC0_PLLMR1,r5 /* Set clock dividers */ + rlwinm r5,r5,0,0xBFFFFFFF /* Clear PLL Reset */ + mtdcr CPC0_PLLMR1,r5 + + /* + ! Wait min of 100us for PLL to lock. + ! See CMOS 27E databook for more info. + ! At 200MHz, that means waiting 20,000 instructions + */ + addi r3,0,20000 /* 2000 = 0x4e20 */ + mtctr r3 +pll_wait: + bdnz pll_wait + + oris r5,r5,0x8000 /* Enable PLL */ + mtdcr CPC0_PLLMR1,r5 /* Engage */ + + /* + * Reset CPU to guarantee timings are OK + * Not sure if this is needed... + */ + addis r3,0,0x1000 + mtspr SPRN_DBCR0,r3 /* This will cause a CPU core reset, and */ + /* execution will continue from the poweron */ + /* vector of 0xfffffffc */ +#endif /* CONFIG_405EP */ + +#if defined(CONFIG_440) +/*----------------------------------------------------------------------------+ +| mttlb3. ++----------------------------------------------------------------------------*/ + function_prolog(mttlb3) + TLBWE(4,3,2) + blr + function_epilog(mttlb3) + +/*----------------------------------------------------------------------------+ +| mftlb3. ++----------------------------------------------------------------------------*/ + function_prolog(mftlb3) + TLBRE(3,3,2) + blr + function_epilog(mftlb3) + +/*----------------------------------------------------------------------------+ +| mttlb2. ++----------------------------------------------------------------------------*/ + function_prolog(mttlb2) + TLBWE(4,3,1) + blr + function_epilog(mttlb2) + +/*----------------------------------------------------------------------------+ +| mftlb2. ++----------------------------------------------------------------------------*/ + function_prolog(mftlb2) + TLBRE(3,3,1) + blr + function_epilog(mftlb2) + +/*----------------------------------------------------------------------------+ +| mttlb1. ++----------------------------------------------------------------------------*/ + function_prolog(mttlb1) + TLBWE(4,3,0) + blr + function_epilog(mttlb1) + +/*----------------------------------------------------------------------------+ +| mftlb1. ++----------------------------------------------------------------------------*/ + function_prolog(mftlb1) + TLBRE(3,3,0) + blr + function_epilog(mftlb1) +#endif /* CONFIG_440 */ + +#if defined(CONFIG_NAND_SPL) +/* + * void nand_boot_relocate(dst, src, bytes) + * + * r3 = Destination address to copy code to (in SDRAM) + * r4 = Source address to copy code from + * r5 = size to copy in bytes + */ +nand_boot_relocate: + mr r6,r3 + mr r7,r4 + mflr r8 + + /* + * Copy SPL from icache into SDRAM + */ + subi r3,r3,4 + subi r4,r4,4 + srwi r5,r5,2 + mtctr r5 +..spl_loop: + lwzu r0,4(r4) + stwu r0,4(r3) + bdnz ..spl_loop + + /* + * Calculate "corrected" link register, so that we "continue" + * in execution in destination range + */ + sub r3,r7,r6 /* r3 = src - dst */ + sub r8,r8,r3 /* r8 = link-reg - (src - dst) */ + mtlr r8 + blr + +nand_boot_common: + /* + * First initialize SDRAM. It has to be available *before* calling + * nand_boot(). + */ + lis r3,CONFIG_SYS_SDRAM_BASE@h + ori r3,r3,CONFIG_SYS_SDRAM_BASE@l + bl initdram + + /* + * Now copy the 4k SPL code into SDRAM and continue execution + * from there. + */ + lis r3,CONFIG_SYS_NAND_BOOT_SPL_DST@h + ori r3,r3,CONFIG_SYS_NAND_BOOT_SPL_DST@l + lis r4,CONFIG_SYS_NAND_BOOT_SPL_SRC@h + ori r4,r4,CONFIG_SYS_NAND_BOOT_SPL_SRC@l + lis r5,CONFIG_SYS_NAND_BOOT_SPL_SIZE@h + ori r5,r5,CONFIG_SYS_NAND_BOOT_SPL_SIZE@l + bl nand_boot_relocate + + /* + * We're running from SDRAM now!!! + * + * It is necessary for 4xx systems to relocate from running at + * the original location (0xfffffxxx) to somewhere else (SDRAM + * preferably). This is because CS0 needs to be reconfigured for + * NAND access. And we can't reconfigure this CS when currently + * "running" from it. + */ + + /* + * Finally call nand_boot() to load main NAND U-Boot image from + * NAND and jump to it. + */ + bl nand_boot /* will not return */ +#endif /* CONFIG_NAND_SPL */ diff --git a/arch/powerpc/cpu/ppc4xx/tlb.c b/arch/powerpc/cpu/ppc4xx/tlb.c new file mode 100644 index 0000000..24a9a9c --- /dev/null +++ b/arch/powerpc/cpu/ppc4xx/tlb.c @@ -0,0 +1,352 @@ +/* + * (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 <ppc440.h> +#include <asm/cache.h> +#include <asm/io.h> +#include <asm/mmu.h> + +typedef struct region { + u64 base; + u32 size; + u32 tlb_word2_i_value; +} region_t; + +void remove_tlb(u32 vaddr, u32 size) +{ + int i; + u32 tlb_word0_value; + u32 tlb_vaddr; + u32 tlb_size = 0; + + for (i=0; i<PPC4XX_TLB_SIZE; i++) { + tlb_word0_value = mftlb1(i); + tlb_vaddr = TLB_WORD0_EPN_DECODE(tlb_word0_value); + if (((tlb_word0_value & TLB_WORD0_V_MASK) == TLB_WORD0_V_ENABLE) && + (tlb_vaddr >= vaddr)) { + /* + * TLB is enabled and start address is lower or equal + * than the area we are looking for. Now we only have + * to check the size/end address for a match. + */ + switch (tlb_word0_value & TLB_WORD0_SIZE_MASK) { + case TLB_WORD0_SIZE_1KB: + tlb_size = 1 << 10; + break; + case TLB_WORD0_SIZE_4KB: + tlb_size = 4 << 10; + break; + case TLB_WORD0_SIZE_16KB: + tlb_size = 16 << 10; + break; + case TLB_WORD0_SIZE_64KB: + tlb_size = 64 << 10; + break; + case TLB_WORD0_SIZE_256KB: + tlb_size = 256 << 10; + break; + case TLB_WORD0_SIZE_1MB: + tlb_size = 1 << 20; + break; + case TLB_WORD0_SIZE_16MB: + tlb_size = 16 << 20; + break; + case TLB_WORD0_SIZE_256MB: + tlb_size = 256 << 20; + break; + } + + /* + * Now check the end-address if it's in the range + */ + if ((tlb_vaddr + tlb_size - 1) <= (vaddr + size - 1)) + /* + * Found a TLB in the range. + * Disable it by writing 0 to tlb0 word. + */ + mttlb1(i, 0); + } + } + + /* Execute an ISYNC instruction so that the new TLB entry takes effect */ + asm("isync"); +} + +/* + * Change the I attribute (cache inhibited) of a TLB or multiple TLB's. + * This function is used to either turn cache on or off in a specific + * memory area. + */ +void change_tlb(u32 vaddr, u32 size, u32 tlb_word2_i_value) +{ + int i; + u32 tlb_word0_value; + u32 tlb_word2_value; + u32 tlb_vaddr; + u32 tlb_size = 0; + + for (i=0; i<PPC4XX_TLB_SIZE; i++) { + tlb_word0_value = mftlb1(i); + tlb_vaddr = TLB_WORD0_EPN_DECODE(tlb_word0_value); + if (((tlb_word0_value & TLB_WORD0_V_MASK) == TLB_WORD0_V_ENABLE) && + (tlb_vaddr >= vaddr)) { + /* + * TLB is enabled and start address is lower or equal + * than the area we are looking for. Now we only have + * to check the size/end address for a match. + */ + switch (tlb_word0_value & TLB_WORD0_SIZE_MASK) { + case TLB_WORD0_SIZE_1KB: + tlb_size = 1 << 10; + break; + case TLB_WORD0_SIZE_4KB: + tlb_size = 4 << 10; + break; + case TLB_WORD0_SIZE_16KB: + tlb_size = 16 << 10; + break; + case TLB_WORD0_SIZE_64KB: + tlb_size = 64 << 10; + break; + case TLB_WORD0_SIZE_256KB: + tlb_size = 256 << 10; + break; + case TLB_WORD0_SIZE_1MB: + tlb_size = 1 << 20; + break; + case TLB_WORD0_SIZE_16MB: + tlb_size = 16 << 20; + break; + case TLB_WORD0_SIZE_256MB: + tlb_size = 256 << 20; + break; + } + + /* + * Now check the end-address if it's in the range + */ + if (((tlb_vaddr + tlb_size - 1) <= (vaddr + size - 1)) || + ((tlb_vaddr < (vaddr + size - 1)) && + ((tlb_vaddr + tlb_size - 1) > (vaddr + size - 1)))) { + /* + * Found a TLB in the range. + * Change cache attribute in tlb2 word. + */ + 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; + + /* + * Now either flush or invalidate the dcache + */ + if (tlb_word2_i_value) + flush_dcache(); + else + invalidate_dcache(); + + mttlb3(i, tlb_word2_value); + asm("iccci 0,0"); + } + } + } + + /* Execute an ISYNC instruction so that the new TLB entry takes effect */ + asm("isync"); +} + +static int add_tlb_entry(u64 phys_addr, + u32 virt_addr, + u32 tlb_word0_size_value, + u32 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(virt_addr) | TLB_WORD0_V_ENABLE | + TLB_WORD0_TS_0 | tlb_word0_size_value; + tlb_word1_value = TLB_WORD1_RPN_ENCODE((u32)phys_addr) | + TLB_WORD1_ERPN_ENCODE(phys_addr >> 32); + 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(u64 phys_addr, + u32 virt_addr, + u32 mem_size, + u32 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 (((phys_addr & TLB_256MB_ALIGN_MASK) == phys_addr) && + (mem_size >= TLB_256MB_SIZE)) { + /* Add a 256MB TLB entry */ + if ((rc = add_tlb_entry(phys_addr, virt_addr, + TLB_WORD0_SIZE_256MB, tlb_i)) == 0) { + mem_size -= TLB_256MB_SIZE; + phys_addr += TLB_256MB_SIZE; + virt_addr += TLB_256MB_SIZE; + } + } else if (((phys_addr & TLB_16MB_ALIGN_MASK) == phys_addr) && + (mem_size >= TLB_16MB_SIZE)) { + /* Add a 16MB TLB entry */ + if ((rc = add_tlb_entry(phys_addr, virt_addr, + TLB_WORD0_SIZE_16MB, tlb_i)) == 0) { + mem_size -= TLB_16MB_SIZE; + phys_addr += TLB_16MB_SIZE; + virt_addr += TLB_16MB_SIZE; + } + } else if (((phys_addr & TLB_1MB_ALIGN_MASK) == phys_addr) && + (mem_size >= TLB_1MB_SIZE)) { + /* Add a 1MB TLB entry */ + if ((rc = add_tlb_entry(phys_addr, virt_addr, + TLB_WORD0_SIZE_1MB, tlb_i)) == 0) { + mem_size -= TLB_1MB_SIZE; + phys_addr += TLB_1MB_SIZE; + virt_addr += TLB_1MB_SIZE; + } + } else if (((phys_addr & TLB_256KB_ALIGN_MASK) == phys_addr) && + (mem_size >= TLB_256KB_SIZE)) { + /* Add a 256KB TLB entry */ + if ((rc = add_tlb_entry(phys_addr, virt_addr, + TLB_WORD0_SIZE_256KB, tlb_i)) == 0) { + mem_size -= TLB_256KB_SIZE; + phys_addr += TLB_256KB_SIZE; + virt_addr += TLB_256KB_SIZE; + } + } else if (((phys_addr & TLB_64KB_ALIGN_MASK) == phys_addr) && + (mem_size >= TLB_64KB_SIZE)) { + /* Add a 64KB TLB entry */ + if ((rc = add_tlb_entry(phys_addr, virt_addr, + TLB_WORD0_SIZE_64KB, tlb_i)) == 0) { + mem_size -= TLB_64KB_SIZE; + phys_addr += TLB_64KB_SIZE; + virt_addr += TLB_64KB_SIZE; + } + } else if (((phys_addr & TLB_16KB_ALIGN_MASK) == phys_addr) && + (mem_size >= TLB_16KB_SIZE)) { + /* Add a 16KB TLB entry */ + if ((rc = add_tlb_entry(phys_addr, virt_addr, + TLB_WORD0_SIZE_16KB, tlb_i)) == 0) { + mem_size -= TLB_16KB_SIZE; + phys_addr += TLB_16KB_SIZE; + virt_addr += TLB_16KB_SIZE; + } + } else if (((phys_addr & TLB_4KB_ALIGN_MASK) == phys_addr) && + (mem_size >= TLB_4KB_SIZE)) { + /* Add a 4KB TLB entry */ + if ((rc = add_tlb_entry(phys_addr, virt_addr, + TLB_WORD0_SIZE_4KB, tlb_i)) == 0) { + mem_size -= TLB_4KB_SIZE; + phys_addr += TLB_4KB_SIZE; + virt_addr += TLB_4KB_SIZE; + } + } else if (((phys_addr & TLB_1KB_ALIGN_MASK) == phys_addr) && + (mem_size >= TLB_1KB_SIZE)) { + /* Add a 1KB TLB entry */ + if ((rc = add_tlb_entry(phys_addr, virt_addr, + TLB_WORD0_SIZE_1KB, tlb_i)) == 0) { + mem_size -= TLB_1KB_SIZE; + phys_addr += TLB_1KB_SIZE; + virt_addr += TLB_1KB_SIZE; + } + } else { + printf("ERROR: no TLB size exists for the base address 0x%llx.\n", + phys_addr); + } + + if (rc != 0) + printf("ERROR: no TLB entries available for the base addr 0x%llx.\n", + phys_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(u64 phys_addr, u32 virt_addr, u32 size, u32 tlb_word2_i_value) +{ + region_t region_array; + + region_array.base = phys_addr; + region_array.size = size; + region_array.tlb_word2_i_value = tlb_word2_i_value; /* en-/disable cache */ + + /* Call the routine to add in the tlb entries for the memory regions */ + program_tlb_addr(region_array.base, virt_addr, region_array.size, + region_array.tlb_word2_i_value); + + return; +} + +#endif /* CONFIG_440 */ diff --git a/arch/powerpc/cpu/ppc4xx/traps.c b/arch/powerpc/cpu/ppc4xx/traps.c new file mode 100644 index 0000000..1616772 --- /dev/null +++ b/arch/powerpc/cpu/ppc4xx/traps.c @@ -0,0 +1,409 @@ +/* + * linux/arch/powerpc/kernel/traps.c + * + * Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org) + * + * Modified by Cort Dougan (cort@cs.nmt.edu) + * and Paul Mackerras (paulus@cs.anu.edu.au) + * + * (C) Copyright 2000 + * Wolfgang Denk, DENX Software Engineering, wd@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 + */ + +/* + * This file handles the architecture-dependent parts of hardware exceptions + */ + +#include <common.h> +#include <command.h> +#include <kgdb.h> +#include <asm/processor.h> + +DECLARE_GLOBAL_DATA_PTR; + +/* Returns 0 if exception not found and fixup otherwise. */ +extern unsigned long search_exception_table(unsigned long); + +/* THIS NEEDS CHANGING to use the board info structure. + */ +#define END_OF_MEM (gd->bd->bi_memstart + gd->bd->bi_memsize) + +static __inline__ void set_tsr(unsigned long val) +{ +#if defined(CONFIG_440) + asm volatile("mtspr 0x150, %0" : : "r" (val)); +#else + asm volatile("mttsr %0" : : "r" (val)); +#endif +} + +static __inline__ unsigned long get_esr(void) +{ + unsigned long val; + +#if defined(CONFIG_440) + asm volatile("mfspr %0, 0x03e" : "=r" (val) :); +#else + asm volatile("mfesr %0" : "=r" (val) :); +#endif + return val; +} + +#define ESR_MCI 0x80000000 +#define ESR_PIL 0x08000000 +#define ESR_PPR 0x04000000 +#define ESR_PTR 0x02000000 +#define ESR_DST 0x00800000 +#define ESR_DIZ 0x00400000 +#define ESR_U0F 0x00008000 + +#if defined(CONFIG_CMD_BEDBUG) +extern void do_bedbug_breakpoint(struct pt_regs *); +#endif + +/* + * Trap & Exception support + */ + +void +print_backtrace(unsigned long *sp) +{ + int cnt = 0; + unsigned long i; + + printf("Call backtrace: "); + while (sp) { + if ((uint)sp > END_OF_MEM) + break; + + i = sp[1]; + if (cnt++ % 7 == 0) + printf("\n"); + printf("%08lX ", i); + if (cnt > 32) break; + sp = (unsigned long *)*sp; + } + printf("\n"); +} + +void show_regs(struct pt_regs * regs) +{ + int i; + + printf("NIP: %08lX XER: %08lX LR: %08lX REGS: %p TRAP: %04lx DEAR: %08lX\n", + regs->nip, regs->xer, regs->link, regs, regs->trap, regs->dar); + printf("MSR: %08lx EE: %01x PR: %01x FP: %01x ME: %01x IR/DR: %01x%01x\n", + regs->msr, regs->msr&MSR_EE ? 1 : 0, regs->msr&MSR_PR ? 1 : 0, + regs->msr & MSR_FP ? 1 : 0,regs->msr&MSR_ME ? 1 : 0, + regs->msr&MSR_IR ? 1 : 0, + regs->msr&MSR_DR ? 1 : 0); + + printf("\n"); + for (i = 0; i < 32; i++) { + if ((i % 8) == 0) { + printf("GPR%02d: ", i); + } + + printf("%08lX ", regs->gpr[i]); + if ((i % 8) == 7) { + printf("\n"); + } + } +} + + +void +_exception(int signr, struct pt_regs *regs) +{ + show_regs(regs); + print_backtrace((unsigned long *)regs->gpr[1]); + panic("Exception"); +} + +void +MachineCheckException(struct pt_regs *regs) +{ + unsigned long fixup, val; +#if defined(CONFIG_440EPX) || defined(CONFIG_440GRX) + u32 value2; + int corr_ecc = 0; + int uncorr_ecc = 0; +#endif + + if ((fixup = search_exception_table(regs->nip)) != 0) { + regs->nip = fixup; + val = mfspr(MCSR); + /* Clear MCSR */ + mtspr(SPRN_MCSR, val); + return; + } + +#if defined(CONFIG_CMD_KGDB) + if (debugger_exception_handler && (*debugger_exception_handler)(regs)) + return; +#endif + + printf("Machine Check Exception.\n"); + printf("Caused by (from msr): "); + printf("regs %p ", regs); + + val = get_esr(); + +#if !defined(CONFIG_440) && !defined(CONFIG_405EX) + if (val& ESR_IMCP) { + printf("Instruction"); + mtspr(ESR, val & ~ESR_IMCP); + } else { + printf("Data"); + } + printf(" machine check.\n"); + +#elif defined(CONFIG_440) || defined(CONFIG_405EX) + if (val& ESR_IMCP){ + printf("Instruction Synchronous Machine Check exception\n"); + mtspr(SPRN_ESR, val & ~ESR_IMCP); + } else { + val = mfspr(MCSR); + if (val & MCSR_IB) + printf("Instruction Read PLB Error\n"); +#if defined(CONFIG_440) + if (val & MCSR_DRB) + printf("Data Read PLB Error\n"); + if (val & MCSR_DWB) + printf("Data Write PLB Error\n"); +#else + if (val & MCSR_DB) + printf("Data PLB Error\n"); +#endif + if (val & MCSR_TLBP) + printf("TLB Parity Error\n"); + if (val & MCSR_ICP){ + /*flush_instruction_cache(); */ + printf("I-Cache Parity Error\n"); + } + if (val & MCSR_DCSP) + printf("D-Cache Search Parity Error\n"); + if (val & MCSR_DCFP) + printf("D-Cache Flush Parity Error\n"); + if (val & MCSR_IMPE) + printf("Machine Check exception is imprecise\n"); + + /* Clear MCSR */ + mtspr(SPRN_MCSR, val); + } +#if defined(CONFIG_440EPX) || defined(CONFIG_440GRX) + mfsdram(DDR0_00, val) ; + printf("DDR0: DDR0_00 %lx\n", val); + val = (val >> 16) & 0xff; + if (val & 0x80) + printf("DDR0: At least one interrupt active\n"); + if (val & 0x40) + printf("DDR0: DRAM initialization complete.\n"); + if (val & 0x20) { + printf("DDR0: Multiple uncorrectable ECC events.\n"); + uncorr_ecc = 1; + } + if (val & 0x10) { + printf("DDR0: Single uncorrectable ECC event.\n"); + uncorr_ecc = 1; + } + if (val & 0x08) { + printf("DDR0: Multiple correctable ECC events.\n"); + corr_ecc = 1; + } + if (val & 0x04) { + printf("DDR0: Single correctable ECC event.\n"); + corr_ecc = 1; + } + if (val & 0x02) + printf("Multiple accesses outside the defined" + " physical memory space detected\n"); + if (val & 0x01) + printf("DDR0: Single access outside the defined" + " physical memory space detected.\n"); + + mfsdram(DDR0_01, val); + val = (val >> 8) & 0x7; + switch (val ) { + case 0: + printf("DDR0: Write Out-of-Range command\n"); + break; + case 1: + printf("DDR0: Read Out-of-Range command\n"); + break; + case 2: + printf("DDR0: Masked write Out-of-Range command\n"); + break; + case 4: + printf("DDR0: Wrap write Out-of-Range command\n"); + break; + case 5: + printf("DDR0: Wrap read Out-of-Range command\n"); + break; + default: + mfsdram(DDR0_01, value2); + printf("DDR0: No DDR0 error know 0x%lx %x\n", val, value2); + } + mfsdram(DDR0_23, val); + if (((val >> 16) & 0xff) && corr_ecc) + printf("DDR0: Syndrome for correctable ECC event 0x%lx\n", + (val >> 16) & 0xff); + mfsdram(DDR0_23, val); + if (((val >> 8) & 0xff) && uncorr_ecc) + printf("DDR0: Syndrome for uncorrectable ECC event 0x%lx\n", + (val >> 8) & 0xff); + mfsdram(DDR0_33, val); + if (val) + printf("DDR0: Address of command that caused an " + "Out-of-Range interrupt %lx\n", val); + mfsdram(DDR0_34, val); + if (val && uncorr_ecc) + printf("DDR0: Address of uncorrectable ECC event %lx\n", val); + mfsdram(DDR0_35, val); + if (val && uncorr_ecc) + printf("DDR0: Address of uncorrectable ECC event %lx\n", val); + mfsdram(DDR0_36, val); + if (val && uncorr_ecc) + printf("DDR0: Data of uncorrectable ECC event 0x%08lx\n", val); + mfsdram(DDR0_37, val); + if (val && uncorr_ecc) + printf("DDR0: Data of uncorrectable ECC event 0x%08lx\n", val); + mfsdram(DDR0_38, val); + if (val && corr_ecc) + printf("DDR0: Address of correctable ECC event %lx\n", val); + mfsdram(DDR0_39, val); + if (val && corr_ecc) + printf("DDR0: Address of correctable ECC event %lx\n", val); + mfsdram(DDR0_40, val); + if (val && corr_ecc) + printf("DDR0: Data of correctable ECC event 0x%08lx\n", val); + mfsdram(DDR0_41, val); + if (val && corr_ecc) + printf("DDR0: Data of correctable ECC event 0x%08lx\n", val); +#endif /* CONFIG_440EPX */ +#endif /* CONFIG_440 */ + show_regs(regs); + print_backtrace((unsigned long *)regs->gpr[1]); + panic("machine check"); +} + +void +AlignmentException(struct pt_regs *regs) +{ +#if defined(CONFIG_CMD_KGDB) + if (debugger_exception_handler && (*debugger_exception_handler)(regs)) + return; +#endif + + show_regs(regs); + print_backtrace((unsigned long *)regs->gpr[1]); + panic("Alignment Exception"); +} + +void +ProgramCheckException(struct pt_regs *regs) +{ + long esr_val; + +#if defined(CONFIG_CMD_KGDB) + if (debugger_exception_handler && (*debugger_exception_handler)(regs)) + return; +#endif + + show_regs(regs); + + esr_val = get_esr(); + if( esr_val & ESR_PIL ) + printf( "** Illegal Instruction **\n" ); + else if( esr_val & ESR_PPR ) + printf( "** Privileged Instruction **\n" ); + else if( esr_val & ESR_PTR ) + printf( "** Trap Instruction **\n" ); + + print_backtrace((unsigned long *)regs->gpr[1]); + panic("Program Check Exception"); +} + +void +DecrementerPITException(struct pt_regs *regs) +{ + /* + * Reset PIT interrupt + */ + set_tsr(0x08000000); + + /* + * Call timer_interrupt routine in interrupts.c + */ + timer_interrupt(NULL); +} + + +void +UnknownException(struct pt_regs *regs) +{ +#if defined(CONFIG_CMD_KGDB) + if (debugger_exception_handler && (*debugger_exception_handler)(regs)) + return; +#endif + + printf("Bad trap at PC: %lx, SR: %lx, vector=%lx\n", + regs->nip, regs->msr, regs->trap); + _exception(0, regs); +} + +void +DebugException(struct pt_regs *regs) +{ + printf("Debugger trap at @ %lx\n", regs->nip ); + show_regs(regs); +#if defined(CONFIG_CMD_BEDBUG) + do_bedbug_breakpoint( regs ); +#endif +} + +/* Probe an address by reading. If not present, return -1, otherwise + * return 0. + */ +int +addr_probe(uint *addr) +{ +#if 0 + int retval; + + __asm__ __volatile__( \ + "1: lwz %0,0(%1)\n" \ + " eieio\n" \ + " li %0,0\n" \ + "2:\n" \ + ".section .fixup,\"ax\"\n" \ + "3: li %0,-1\n" \ + " b 2b\n" \ + ".section __ex_table,\"a\"\n" \ + " .align 2\n" \ + " .long 1b,3b\n" \ + ".text" \ + : "=r" (retval) : "r"(addr)); + + return (retval); +#endif + return 0; +} diff --git a/arch/powerpc/cpu/ppc4xx/u-boot.lds b/arch/powerpc/cpu/ppc4xx/u-boot.lds new file mode 100644 index 0000000..eca1f9d --- /dev/null +++ b/arch/powerpc/cpu/ppc4xx/u-boot.lds @@ -0,0 +1,169 @@ +/* + * Copyright 2007-2009 Freescale Semiconductor, Inc. + * + * 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 "config.h" /* CONFIG_BOARDDIR */ + +#ifndef RESET_VECTOR_ADDRESS +#define RESET_VECTOR_ADDRESS 0xfffffffc +#endif + +OUTPUT_ARCH(powerpc) +/* Do we need any of these for elf? + __DYNAMIC = 0; */ +PHDRS +{ + text PT_LOAD; + bss PT_LOAD; +} + +SECTIONS +{ + /* Read-only sections, merged into text segment: */ + . = + SIZEOF_HEADERS; + .interp : { *(.interp) } + .hash : { *(.hash) } + .dynsym : { *(.dynsym) } + .dynstr : { *(.dynstr) } + .rel.text : { *(.rel.text) } + .rela.text : { *(.rela.text) } + .rel.data : { *(.rel.data) } + .rela.data : { *(.rela.data) } + .rel.rodata : { *(.rel.rodata) } + .rela.rodata : { *(.rela.rodata) } + .rel.got : { *(.rel.got) } + .rela.got : { *(.rela.got) } + .rel.ctors : { *(.rel.ctors) } + .rela.ctors : { *(.rela.ctors) } + .rel.dtors : { *(.rel.dtors) } + .rela.dtors : { *(.rela.dtors) } + .rel.bss : { *(.rel.bss) } + .rela.bss : { *(.rela.bss) } + .rel.plt : { *(.rel.plt) } + .rela.plt : { *(.rela.plt) } + .init : { *(.init) } + .plt : { *(.plt) } + .text : + { + *(.text) + *(.got1) + } :text + _etext = .; + PROVIDE (etext = .); + .rodata : + { + *(.eh_frame) + *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.rodata*))) + } :text + .fini : { *(.fini) } =0 + .ctors : { *(.ctors) } + .dtors : { *(.dtors) } + + /* Read-write section, merged into data segment: */ + . = (. + 0x00FF) & 0xFFFFFF00; + _erotext = .; + PROVIDE (erotext = .); + .reloc : + { + *(.got) + _GOT2_TABLE_ = .; + *(.got2) + _FIXUP_TABLE_ = .; + *(.fixup) + } + __got2_entries = (_FIXUP_TABLE_ - _GOT2_TABLE_) >> 2; + __fixup_entries = (. - _FIXUP_TABLE_) >> 2; + + .data : + { + *(.data) + *(.data1) + *(.sdata) + *(.sdata2) + *(.dynamic) + CONSTRUCTORS + } + _edata = .; + PROVIDE (edata = .); + + . = .; + __u_boot_cmd_start = .; + .u_boot_cmd : { *(.u_boot_cmd) } + __u_boot_cmd_end = .; + + . = .; + __start___ex_table = .; + __ex_table : { *(__ex_table) } + __stop___ex_table = .; + + . = ALIGN(256); + __init_begin = .; + .text.init : { *(.text.init) } + .data.init : { *(.data.init) } + . = ALIGN(256); + __init_end = .; + +#ifdef CONFIG_440 + .bootpg RESET_VECTOR_ADDRESS - 0xffc : + { + arch/powerpc/cpu/ppc4xx/start.o (.bootpg) + + /* + * PPC440 board need a board specific object with the + * TLB definitions. This needs to get included right after + * start.o, since the first shadow TLB only covers 4k + * of address space. + */ + CONFIG_BOARDDIR/init.o (.bootpg) + } :text = 0xffff +#endif + + .resetvec RESET_VECTOR_ADDRESS : + { + *(.resetvec) + } :text = 0xffff + + . = RESET_VECTOR_ADDRESS + 0x4; + + /* + * Make sure that the bss segment isn't linked at 0x0, otherwise its + * address won't be updated during relocation fixups. Note that + * this is a temporary fix. Code to dynamically the fixup the bss + * location will be added in the future. When the bss relocation + * fixup code is present this workaround should be removed. + */ +#if (RESET_VECTOR_ADDRESS == 0xfffffffc) + . |= 0x10; +#endif + + __bss_start = .; + .bss (NOLOAD) : + { + *(.sbss) *(.scommon) + *(.dynbss) + *(.bss) + *(COMMON) + } :bss + + . = ALIGN(4); + _end = . ; + PROVIDE (end = .); +} diff --git a/arch/powerpc/cpu/ppc4xx/uic.c b/arch/powerpc/cpu/ppc4xx/uic.c new file mode 100644 index 0000000..8b1b259 --- /dev/null +++ b/arch/powerpc/cpu/ppc4xx/uic.c @@ -0,0 +1,180 @@ +/* + * (C) Copyright 2000-2002 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * (C) Copyright 2002 (440 port) + * Scott McNutt, Artesyn Communication Producs, smcnutt@artsyncp.com + * + * (C) Copyright 2003 (440GX port) + * Travis B. Sawyer, Sandburst Corporation, tsawyer@sandburst.com + * + * (C) Copyright 2008 (PPC440X05 port for Virtex 5 FX) + * Ricardo Ribalda-Universidad Autonoma de Madrid-ricardo.ribalda@uam.es + * Work supported by Qtechnology (htpp://qtec.com) + * + * 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> +#include <watchdog.h> +#include <command.h> +#include <asm/processor.h> +#include <asm/interrupt.h> +#include <ppc4xx.h> +#include <ppc_asm.tmpl> +#include <commproc.h> + +#if (UIC_MAX > 3) +#define UICB0_ALL (UIC_MASK(VECNUM_UIC1CI) | UIC_MASK(VECNUM_UIC1NCI) | \ + UIC_MASK(VECNUM_UIC2CI) | UIC_MASK(VECNUM_UIC2NCI) | \ + UIC_MASK(VECNUM_UIC3CI) | UIC_MASK(VECNUM_UIC3NCI)) +#elif (UIC_MAX > 2) +#define UICB0_ALL (UIC_MASK(VECNUM_UIC1CI) | UIC_MASK(VECNUM_UIC1NCI) | \ + UIC_MASK(VECNUM_UIC2CI) | UIC_MASK(VECNUM_UIC2NCI)) +#elif (UIC_MAX > 1) +#define UICB0_ALL (UIC_MASK(VECNUM_UIC1CI) | UIC_MASK(VECNUM_UIC1NCI)) +#else +#define UICB0_ALL 0 +#endif + +u32 get_dcr(u16); + +DECLARE_GLOBAL_DATA_PTR; + +void pic_enable(void) +{ +#if (UIC_MAX > 1) + /* Install the UIC1 handlers */ + irq_install_handler(VECNUM_UIC1NCI, (void *)(void *)external_interrupt, 0); + irq_install_handler(VECNUM_UIC1CI, (void *)(void *)external_interrupt, 0); +#endif +#if (UIC_MAX > 2) + irq_install_handler(VECNUM_UIC2NCI, (void *)(void *)external_interrupt, 0); + irq_install_handler(VECNUM_UIC2CI, (void *)(void *)external_interrupt, 0); +#endif +#if (UIC_MAX > 3) + irq_install_handler(VECNUM_UIC3NCI, (void *)(void *)external_interrupt, 0); + irq_install_handler(VECNUM_UIC3CI, (void *)(void *)external_interrupt, 0); +#endif +} + +/* Handler for UIC interrupt */ +static void uic_interrupt(u32 uic_base, int vec_base) +{ + u32 uic_msr; + u32 msr_shift; + int vec; + + /* + * Read masked interrupt status register to determine interrupt source + */ + uic_msr = get_dcr(uic_base + UIC_MSR); + msr_shift = uic_msr; + vec = vec_base; + + while (msr_shift != 0) { + if (msr_shift & 0x80000000) + interrupt_run_handler(vec); + /* + * Shift msr to next position and increment vector + */ + msr_shift <<= 1; + vec++; + } +} + +/* + * Handle external interrupts + */ +void external_interrupt(struct pt_regs *regs) +{ + u32 uic_msr; + + /* + * Read masked interrupt status register to determine interrupt source + */ + uic_msr = mfdcr(UIC0MSR); + +#if (UIC_MAX > 1) + if ((UIC_MASK(VECNUM_UIC1CI) & uic_msr) || + (UIC_MASK(VECNUM_UIC1NCI) & uic_msr)) + uic_interrupt(UIC1_DCR_BASE, 32); +#endif + +#if (UIC_MAX > 2) + if ((UIC_MASK(VECNUM_UIC2CI) & uic_msr) || + (UIC_MASK(VECNUM_UIC2NCI) & uic_msr)) + uic_interrupt(UIC2_DCR_BASE, 64); +#endif + +#if (UIC_MAX > 3) + if ((UIC_MASK(VECNUM_UIC3CI) & uic_msr) || + (UIC_MASK(VECNUM_UIC3NCI) & uic_msr)) + uic_interrupt(UIC3_DCR_BASE, 96); +#endif + + mtdcr(UIC0SR, (uic_msr & UICB0_ALL)); + + if (uic_msr & ~(UICB0_ALL)) + uic_interrupt(UIC0_DCR_BASE, 0); + + return; +} + +void pic_irq_ack(unsigned int vec) +{ + if ((vec >= 0) && (vec < 32)) + mtdcr(UIC0SR, UIC_MASK(vec)); + else if ((vec >= 32) && (vec < 64)) + mtdcr(UIC1SR, UIC_MASK(vec)); + else if ((vec >= 64) && (vec < 96)) + mtdcr(UIC2SR, UIC_MASK(vec)); + else if (vec >= 96) + mtdcr(UIC3SR, UIC_MASK(vec)); +} + +/* + * Install and free a interrupt handler. + */ +void pic_irq_enable(unsigned int vec) +{ + + if ((vec >= 0) && (vec < 32)) + mtdcr(UIC0ER, mfdcr(UIC0ER) | UIC_MASK(vec)); + else if ((vec >= 32) && (vec < 64)) + mtdcr(UIC1ER, mfdcr(UIC1ER) | UIC_MASK(vec)); + else if ((vec >= 64) && (vec < 96)) + mtdcr(UIC2ER, mfdcr(UIC2ER) | UIC_MASK(vec)); + else if (vec >= 96) + mtdcr(UIC3ER, mfdcr(UIC3ER) | UIC_MASK(vec)); + + debug("Install interrupt vector %d\n", vec); +} + +void pic_irq_disable(unsigned int vec) +{ + if ((vec >= 0) && (vec < 32)) + mtdcr(UIC0ER, mfdcr(UIC0ER) & ~UIC_MASK(vec)); + else if ((vec >= 32) && (vec < 64)) + mtdcr(UIC1ER, mfdcr(UIC1ER) & ~UIC_MASK(vec)); + else if ((vec >= 64) && (vec < 96)) + mtdcr(UIC2ER, mfdcr(UIC2ER) & ~UIC_MASK(vec)); + else if (vec >= 96) + mtdcr(UIC3ER, mfdcr(UIC3ER) & ~UIC_MASK(vec)); +} diff --git a/arch/powerpc/cpu/ppc4xx/usb.c b/arch/powerpc/cpu/ppc4xx/usb.c new file mode 100644 index 0000000..592efe7 --- /dev/null +++ b/arch/powerpc/cpu/ppc4xx/usb.c @@ -0,0 +1,66 @@ +/* + * (C) Copyright 2007 + * Markus Klotzbuecher, DENX Software Engineering <mk@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_USB_OHCI_NEW) && defined(CONFIG_SYS_USB_OHCI_CPU_INIT) + +#ifdef CONFIG_4xx_DCACHE +#include <asm/mmu.h> +DECLARE_GLOBAL_DATA_PTR; +#endif + +#include "usbdev.h" + +int usb_cpu_init(void) +{ +#ifdef CONFIG_4xx_DCACHE + /* disable cache */ + change_tlb(gd->bd->bi_memstart, gd->bd->bi_memsize, TLB_WORD2_I_ENABLE); +#endif + +#if defined(CONFIG_440EP) || defined(CONFIG_440EPX) + usb_dev_init(); +#endif + return 0; +} + +int usb_cpu_stop(void) +{ +#ifdef CONFIG_4xx_DCACHE + /* enable cache */ + change_tlb(gd->bd->bi_memstart, gd->bd->bi_memsize, 0); +#endif + return 0; +} + +int usb_cpu_init_fail(void) +{ +#ifdef CONFIG_4xx_DCACHE + /* enable cache */ + change_tlb(gd->bd->bi_memstart, gd->bd->bi_memsize, 0); +#endif + return 0; +} + +#endif /* defined(CONFIG_USB_OHCI) && defined(CONFIG_SYS_USB_OHCI_CPU_INIT) */ diff --git a/arch/powerpc/cpu/ppc4xx/usb_ohci.c b/arch/powerpc/cpu/ppc4xx/usb_ohci.c new file mode 100644 index 0000000..a9edacd --- /dev/null +++ b/arch/powerpc/cpu/ppc4xx/usb_ohci.c @@ -0,0 +1,1648 @@ +/* + * URB OHCI HCD (Host Controller Driver) for USB on the PPC440EP. + * + * (C) Copyright 2003-2004 + * Gary Jennejohn, DENX Software Engineering <garyj@denx.de> + * + * (C) Copyright 2004 + * Pierre Aubert, Staubli Faverges <p.aubert@staubli.com> + * + * Note: Much of this code has been derived from Linux 2.4 + * (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at> + * (C) Copyright 2000-2002 David Brownell + * + * 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 + * + */ +/* + * IMPORTANT NOTES + * 1 - this driver is intended for use with USB Mass Storage Devices + * (BBB) ONLY. There is NO support for Interrupt or Isochronous pipes! + */ + +#include <common.h> + +#ifdef CONFIG_USB_OHCI + +#include <malloc.h> +#include <usb.h> +#include "usb_ohci.h" + +#include "usbdev.h" + +#define OHCI_USE_NPS /* force NoPowerSwitching mode */ +#undef OHCI_VERBOSE_DEBUG /* not always helpful */ +#undef DEBUG +#undef SHOW_INFO +#undef OHCI_FILL_TRACE + +/* For initializing controller (mask in an HCFS mode too) */ +#define OHCI_CONTROL_INIT \ + (OHCI_CTRL_CBSR & 0x3) | OHCI_CTRL_IE | OHCI_CTRL_PLE + +#define readl(a) (*((volatile u32 *)(a))) +#define writel(a, b) (*((volatile u32 *)(b)) = ((volatile u32)a)) + +#define min_t(type,x,y) ({ type __x = (x); type __y = (y); __x < __y ? __x: __y; }) + +#ifdef DEBUG +#define dbg(format, arg...) printf("DEBUG: " format "\n", ## arg) +#else +#define dbg(format, arg...) do {} while(0) +#endif /* DEBUG */ +#define err(format, arg...) printf("ERROR: " format "\n", ## arg) +#ifdef SHOW_INFO +#define info(format, arg...) printf("INFO: " format "\n", ## arg) +#else +#define info(format, arg...) do {} while(0) +#endif + +#define m16_swap(x) swap_16(x) +#define m32_swap(x) swap_32(x) + +#if defined(CONFIG_405EZ) || defined(CONFIG_440EP) || defined(CONFIG_440EPX) +#define ohci_cpu_to_le16(x) (x) +#define ohci_cpu_to_le32(x) (x) +#else +#define ohci_cpu_to_le16(x) swap_16(x) +#define ohci_cpu_to_le32(x) swap_32(x) +#endif + +/* global ohci_t */ +static ohci_t gohci; +/* this must be aligned to a 256 byte boundary */ +struct ohci_hcca ghcca[1]; +/* a pointer to the aligned storage */ +struct ohci_hcca *phcca; +/* this allocates EDs for all possible endpoints */ +struct ohci_device ohci_dev; +/* urb_priv */ +urb_priv_t urb_priv; +/* RHSC flag */ +int got_rhsc; +/* device which was disconnected */ +struct usb_device *devgone; +/* flag guarding URB transation */ +int urb_finished = 0; + +/*-------------------------------------------------------------------------*/ + +/* AMD-756 (D2 rev) reports corrupt register contents in some cases. + * The erratum (#4) description is incorrect. AMD's workaround waits + * till some bits (mostly reserved) are clear; ok for all revs. + */ +#define OHCI_QUIRK_AMD756 0xabcd +#define read_roothub(hc, register, mask) ({ \ + u32 temp = readl (&hc->regs->roothub.register); \ + if (hc->flags & OHCI_QUIRK_AMD756) \ + while (temp & mask) \ + temp = readl (&hc->regs->roothub.register); \ + temp; }) + +static u32 roothub_a (struct ohci *hc) + { return read_roothub (hc, a, 0xfc0fe000); } +static inline u32 roothub_b (struct ohci *hc) + { return readl (&hc->regs->roothub.b); } +static inline u32 roothub_status (struct ohci *hc) + { return readl (&hc->regs->roothub.status); } +static u32 roothub_portstatus (struct ohci *hc, int i) + { return read_roothub (hc, portstatus [i], 0xffe0fce0); } + + +/* forward declaration */ +static int hc_interrupt (void); +static void +td_submit_job (struct usb_device * dev, unsigned long pipe, void * buffer, + int transfer_len, struct devrequest * setup, urb_priv_t * urb, int interval); + +/*-------------------------------------------------------------------------* + * URB support functions + *-------------------------------------------------------------------------*/ + +/* free HCD-private data associated with this URB */ + +static void urb_free_priv (urb_priv_t * urb) +{ + int i; + int last; + struct td * td; + + last = urb->length - 1; + if (last >= 0) { + for (i = 0; i <= last; i++) { + td = urb->td[i]; + if (td) { + td->usb_dev = NULL; + urb->td[i] = NULL; + } + } + } +} + +/*-------------------------------------------------------------------------*/ + +#ifdef DEBUG +static int sohci_get_current_frame_number (struct usb_device * dev); + +/* debug| print the main components of an URB + * small: 0) header + data packets 1) just header */ + +static void pkt_print (struct usb_device * dev, unsigned long pipe, void * buffer, + int transfer_len, struct devrequest * setup, char * str, int small) +{ + urb_priv_t * purb = &urb_priv; + + dbg("%s URB:[%4x] dev:%2d,ep:%2d-%c,type:%s,len:%d/%d stat:%#lx", + str, + sohci_get_current_frame_number (dev), + usb_pipedevice (pipe), + usb_pipeendpoint (pipe), + usb_pipeout (pipe)? 'O': 'I', + usb_pipetype (pipe) < 2? (usb_pipeint (pipe)? "INTR": "ISOC"): + (usb_pipecontrol (pipe)? "CTRL": "BULK"), + purb->actual_length, + transfer_len, dev->status); +#ifdef OHCI_VERBOSE_DEBUG + if (!small) { + int i, len; + + if (usb_pipecontrol (pipe)) { + printf (__FILE__ ": cmd(8):"); + for (i = 0; i < 8 ; i++) + printf (" %02x", ((__u8 *) setup) [i]); + printf ("\n"); + } + if (transfer_len > 0 && buffer) { + printf (__FILE__ ": data(%d/%d):", + purb->actual_length, + transfer_len); + len = usb_pipeout (pipe)? + transfer_len: purb->actual_length; + for (i = 0; i < 16 && i < len; i++) + printf (" %02x", ((__u8 *) buffer) [i]); + printf ("%s\n", i < len? "...": ""); + } + } +#endif +} + +/* just for debugging; prints non-empty branches of the int ed tree inclusive iso eds*/ +void ep_print_int_eds (ohci_t *ohci, char * str) { + int i, j; + __u32 * ed_p; + for (i= 0; i < 32; i++) { + j = 5; + ed_p = &(ohci->hcca->int_table [i]); + if (*ed_p == 0) + continue; + printf (__FILE__ ": %s branch int %2d(%2x):", str, i, i); + while (*ed_p != 0 && j--) { + ed_t *ed = (ed_t *)ohci_cpu_to_le32(ed_p); + printf (" ed: %4x;", ed->hwINFO); + ed_p = &ed->hwNextED; + } + printf ("\n"); + } +} + +static void ohci_dump_intr_mask (char *label, __u32 mask) +{ + dbg ("%s: 0x%08x%s%s%s%s%s%s%s%s%s", + label, + mask, + (mask & OHCI_INTR_MIE) ? " MIE" : "", + (mask & OHCI_INTR_OC) ? " OC" : "", + (mask & OHCI_INTR_RHSC) ? " RHSC" : "", + (mask & OHCI_INTR_FNO) ? " FNO" : "", + (mask & OHCI_INTR_UE) ? " UE" : "", + (mask & OHCI_INTR_RD) ? " RD" : "", + (mask & OHCI_INTR_SF) ? " SF" : "", + (mask & OHCI_INTR_WDH) ? " WDH" : "", + (mask & OHCI_INTR_SO) ? " SO" : "" + ); +} + +static void maybe_print_eds (char *label, __u32 value) +{ + ed_t *edp = (ed_t *)value; + + if (value) { + dbg ("%s %08x", label, value); + dbg ("%08x", edp->hwINFO); + dbg ("%08x", edp->hwTailP); + dbg ("%08x", edp->hwHeadP); + dbg ("%08x", edp->hwNextED); + } +} + +static char * hcfs2string (int state) +{ + switch (state) { + case OHCI_USB_RESET: return "reset"; + case OHCI_USB_RESUME: return "resume"; + case OHCI_USB_OPER: return "operational"; + case OHCI_USB_SUSPEND: return "suspend"; + } + return "?"; +} + +/* dump control and status registers */ +static void ohci_dump_status (ohci_t *controller) +{ + struct ohci_regs *regs = controller->regs; + __u32 temp; + + temp = readl (®s->revision) & 0xff; + if (temp != 0x10) + dbg ("spec %d.%d", (temp >> 4), (temp & 0x0f)); + + temp = readl (®s->control); + dbg ("control: 0x%08x%s%s%s HCFS=%s%s%s%s%s CBSR=%d", temp, + (temp & OHCI_CTRL_RWE) ? " RWE" : "", + (temp & OHCI_CTRL_RWC) ? " RWC" : "", + (temp & OHCI_CTRL_IR) ? " IR" : "", + hcfs2string (temp & OHCI_CTRL_HCFS), + (temp & OHCI_CTRL_BLE) ? " BLE" : "", + (temp & OHCI_CTRL_CLE) ? " CLE" : "", + (temp & OHCI_CTRL_IE) ? " IE" : "", + (temp & OHCI_CTRL_PLE) ? " PLE" : "", + temp & OHCI_CTRL_CBSR + ); + + temp = readl (®s->cmdstatus); + dbg ("cmdstatus: 0x%08x SOC=%d%s%s%s%s", temp, + (temp & OHCI_SOC) >> 16, + (temp & OHCI_OCR) ? " OCR" : "", + (temp & OHCI_BLF) ? " BLF" : "", + (temp & OHCI_CLF) ? " CLF" : "", + (temp & OHCI_HCR) ? " HCR" : "" + ); + + ohci_dump_intr_mask ("intrstatus", readl (®s->intrstatus)); + ohci_dump_intr_mask ("intrenable", readl (®s->intrenable)); + + maybe_print_eds ("ed_periodcurrent", readl (®s->ed_periodcurrent)); + + maybe_print_eds ("ed_controlhead", readl (®s->ed_controlhead)); + maybe_print_eds ("ed_controlcurrent", readl (®s->ed_controlcurrent)); + + maybe_print_eds ("ed_bulkhead", readl (®s->ed_bulkhead)); + maybe_print_eds ("ed_bulkcurrent", readl (®s->ed_bulkcurrent)); + + maybe_print_eds ("donehead", readl (®s->donehead)); +} + +static void ohci_dump_roothub (ohci_t *controller, int verbose) +{ + __u32 temp, ndp, i; + + temp = roothub_a (controller); + ndp = (temp & RH_A_NDP); + + if (verbose) { + dbg ("roothub.a: %08x POTPGT=%d%s%s%s%s%s NDP=%d", temp, + ((temp & RH_A_POTPGT) >> 24) & 0xff, + (temp & RH_A_NOCP) ? " NOCP" : "", + (temp & RH_A_OCPM) ? " OCPM" : "", + (temp & RH_A_DT) ? " DT" : "", + (temp & RH_A_NPS) ? " NPS" : "", + (temp & RH_A_PSM) ? " PSM" : "", + ndp + ); + temp = roothub_b (controller); + dbg ("roothub.b: %08x PPCM=%04x DR=%04x", + temp, + (temp & RH_B_PPCM) >> 16, + (temp & RH_B_DR) + ); + temp = roothub_status (controller); + dbg ("roothub.status: %08x%s%s%s%s%s%s", + temp, + (temp & RH_HS_CRWE) ? " CRWE" : "", + (temp & RH_HS_OCIC) ? " OCIC" : "", + (temp & RH_HS_LPSC) ? " LPSC" : "", + (temp & RH_HS_DRWE) ? " DRWE" : "", + (temp & RH_HS_OCI) ? " OCI" : "", + (temp & RH_HS_LPS) ? " LPS" : "" + ); + } + + for (i = 0; i < ndp; i++) { + temp = roothub_portstatus (controller, i); + dbg ("roothub.portstatus [%d] = 0x%08x%s%s%s%s%s%s%s%s%s%s%s%s", + i, + temp, + (temp & RH_PS_PRSC) ? " PRSC" : "", + (temp & RH_PS_OCIC) ? " OCIC" : "", + (temp & RH_PS_PSSC) ? " PSSC" : "", + (temp & RH_PS_PESC) ? " PESC" : "", + (temp & RH_PS_CSC) ? " CSC" : "", + + (temp & RH_PS_LSDA) ? " LSDA" : "", + (temp & RH_PS_PPS) ? " PPS" : "", + (temp & RH_PS_PRS) ? " PRS" : "", + (temp & RH_PS_POCI) ? " POCI" : "", + (temp & RH_PS_PSS) ? " PSS" : "", + + (temp & RH_PS_PES) ? " PES" : "", + (temp & RH_PS_CCS) ? " CCS" : "" + ); + } +} + +static void ohci_dump (ohci_t *controller, int verbose) +{ + dbg ("OHCI controller usb-%s state", controller->slot_name); + + /* dumps some of the state we know about */ + ohci_dump_status (controller); + if (verbose) + ep_print_int_eds (controller, "hcca"); + dbg ("hcca frame #%04x", controller->hcca->frame_no); + ohci_dump_roothub (controller, 1); +} + + +#endif /* DEBUG */ + +/*-------------------------------------------------------------------------* + * Interface functions (URB) + *-------------------------------------------------------------------------*/ + +/* get a transfer request */ + +int sohci_submit_job(struct usb_device *dev, unsigned long pipe, void *buffer, + int transfer_len, struct devrequest *setup, int interval) +{ + ohci_t *ohci; + ed_t * ed; + urb_priv_t *purb_priv; + int i, size = 0; + + ohci = &gohci; + + /* when controller's hung, permit only roothub cleanup attempts + * such as powering down ports */ + if (ohci->disabled) { + err("sohci_submit_job: EPIPE"); + return -1; + } + + /* if we have an unfinished URB from previous transaction let's + * fail and scream as quickly as possible so as not to corrupt + * further communication */ + if (!urb_finished) { + err("sohci_submit_job: URB NOT FINISHED"); + return -1; + } + /* we're about to begin a new transaction here so mark the URB unfinished */ + urb_finished = 0; + + /* every endpoint has a ed, locate and fill it */ + if (!(ed = ep_add_ed (dev, pipe))) { + err("sohci_submit_job: ENOMEM"); + return -1; + } + + /* for the private part of the URB we need the number of TDs (size) */ + switch (usb_pipetype (pipe)) { + case PIPE_BULK: /* one TD for every 4096 Byte */ + size = (transfer_len - 1) / 4096 + 1; + break; + case PIPE_CONTROL: /* 1 TD for setup, 1 for ACK and 1 for every 4096 B */ + size = (transfer_len == 0)? 2: + (transfer_len - 1) / 4096 + 3; + break; + } + + if (size >= (N_URB_TD - 1)) { + err("need %d TDs, only have %d", size, N_URB_TD); + return -1; + } + purb_priv = &urb_priv; + purb_priv->pipe = pipe; + + /* fill the private part of the URB */ + purb_priv->length = size; + purb_priv->ed = ed; + purb_priv->actual_length = 0; + + /* allocate the TDs */ + /* note that td[0] was allocated in ep_add_ed */ + for (i = 0; i < size; i++) { + purb_priv->td[i] = td_alloc (dev); + if (!purb_priv->td[i]) { + purb_priv->length = i; + urb_free_priv (purb_priv); + err("sohci_submit_job: ENOMEM"); + return -1; + } + } + + if (ed->state == ED_NEW || (ed->state & ED_DEL)) { + urb_free_priv (purb_priv); + err("sohci_submit_job: EINVAL"); + return -1; + } + + /* link the ed into a chain if is not already */ + if (ed->state != ED_OPER) + ep_link (ohci, ed); + + /* fill the TDs and link it to the ed */ + td_submit_job(dev, pipe, buffer, transfer_len, setup, purb_priv, interval); + + return 0; +} + +/*-------------------------------------------------------------------------*/ + +#ifdef DEBUG +/* tell us the current USB frame number */ + +static int sohci_get_current_frame_number (struct usb_device *usb_dev) +{ + ohci_t *ohci = &gohci; + + return ohci_cpu_to_le16 (ohci->hcca->frame_no); +} +#endif + +/*-------------------------------------------------------------------------* + * ED handling functions + *-------------------------------------------------------------------------*/ + +/* link an ed into one of the HC chains */ + +static int ep_link (ohci_t *ohci, ed_t *edi) +{ + volatile ed_t *ed = edi; + + ed->state = ED_OPER; + + switch (ed->type) { + case PIPE_CONTROL: + ed->hwNextED = 0; + if (ohci->ed_controltail == NULL) { + writel (ed, &ohci->regs->ed_controlhead); + } else { + ohci->ed_controltail->hwNextED = ohci_cpu_to_le32 ((unsigned long)ed); + } + ed->ed_prev = ohci->ed_controltail; + if (!ohci->ed_controltail && !ohci->ed_rm_list[0] && + !ohci->ed_rm_list[1] && !ohci->sleeping) { + ohci->hc_control |= OHCI_CTRL_CLE; + writel (ohci->hc_control, &ohci->regs->control); + } + ohci->ed_controltail = edi; + break; + + case PIPE_BULK: + ed->hwNextED = 0; + if (ohci->ed_bulktail == NULL) { + writel (ed, &ohci->regs->ed_bulkhead); + } else { + ohci->ed_bulktail->hwNextED = ohci_cpu_to_le32 ((unsigned long)ed); + } + ed->ed_prev = ohci->ed_bulktail; + if (!ohci->ed_bulktail && !ohci->ed_rm_list[0] && + !ohci->ed_rm_list[1] && !ohci->sleeping) { + ohci->hc_control |= OHCI_CTRL_BLE; + writel (ohci->hc_control, &ohci->regs->control); + } + ohci->ed_bulktail = edi; + break; + } + return 0; +} + +/*-------------------------------------------------------------------------*/ + +/* unlink an ed from one of the HC chains. + * just the link to the ed is unlinked. + * the link from the ed still points to another operational ed or 0 + * so the HC can eventually finish the processing of the unlinked ed */ + +static int ep_unlink (ohci_t *ohci, ed_t *edi) +{ + volatile ed_t *ed = edi; + + ed->hwINFO |= ohci_cpu_to_le32 (OHCI_ED_SKIP); + + switch (ed->type) { + case PIPE_CONTROL: + if (ed->ed_prev == NULL) { + if (!ed->hwNextED) { + ohci->hc_control &= ~OHCI_CTRL_CLE; + writel (ohci->hc_control, &ohci->regs->control); + } + writel (ohci_cpu_to_le32 (*((__u32 *)&ed->hwNextED)), &ohci->regs->ed_controlhead); + } else { + ed->ed_prev->hwNextED = ed->hwNextED; + } + if (ohci->ed_controltail == ed) { + ohci->ed_controltail = ed->ed_prev; + } else { + ((ed_t *)ohci_cpu_to_le32 (*((__u32 *)&ed->hwNextED)))->ed_prev = ed->ed_prev; + } + break; + + case PIPE_BULK: + if (ed->ed_prev == NULL) { + if (!ed->hwNextED) { + ohci->hc_control &= ~OHCI_CTRL_BLE; + writel (ohci->hc_control, &ohci->regs->control); + } + writel (ohci_cpu_to_le32 (*((__u32 *)&ed->hwNextED)), &ohci->regs->ed_bulkhead); + } else { + ed->ed_prev->hwNextED = ed->hwNextED; + } + if (ohci->ed_bulktail == ed) { + ohci->ed_bulktail = ed->ed_prev; + } else { + ((ed_t *)ohci_cpu_to_le32 (*((__u32 *)&ed->hwNextED)))->ed_prev = ed->ed_prev; + } + break; + } + ed->state = ED_UNLINK; + return 0; +} + + +/*-------------------------------------------------------------------------*/ + +/* add/reinit an endpoint; this should be done once at the usb_set_configuration command, + * but the USB stack is a little bit stateless so we do it at every transaction + * if the state of the ed is ED_NEW then a dummy td is added and the state is changed to ED_UNLINK + * in all other cases the state is left unchanged + * the ed info fields are setted anyway even though most of them should not change */ + +static ed_t * ep_add_ed (struct usb_device *usb_dev, unsigned long pipe) +{ + td_t *td; + ed_t *ed_ret; + volatile ed_t *ed; + + ed = ed_ret = &ohci_dev.ed[(usb_pipeendpoint (pipe) << 1) | + (usb_pipecontrol (pipe)? 0: usb_pipeout (pipe))]; + + if ((ed->state & ED_DEL) || (ed->state & ED_URB_DEL)) { + err("ep_add_ed: pending delete"); + /* pending delete request */ + return NULL; + } + + if (ed->state == ED_NEW) { + ed->hwINFO = ohci_cpu_to_le32 (OHCI_ED_SKIP); /* skip ed */ + /* dummy td; end of td list for ed */ + td = td_alloc (usb_dev); + ed->hwTailP = ohci_cpu_to_le32 ((unsigned long)td); + ed->hwHeadP = ed->hwTailP; + ed->state = ED_UNLINK; + ed->type = usb_pipetype (pipe); + ohci_dev.ed_cnt++; + } + + ed->hwINFO = ohci_cpu_to_le32 (usb_pipedevice (pipe) + | usb_pipeendpoint (pipe) << 7 + | (usb_pipeisoc (pipe)? 0x8000: 0) + | (usb_pipecontrol (pipe)? 0: (usb_pipeout (pipe)? 0x800: 0x1000)) + | usb_pipeslow (pipe) << 13 + | usb_maxpacket (usb_dev, pipe) << 16); + + return ed_ret; +} + +/*-------------------------------------------------------------------------* + * TD handling functions + *-------------------------------------------------------------------------*/ + +/* enqueue next TD for this URB (OHCI spec 5.2.8.2) */ + +static void td_fill (ohci_t *ohci, unsigned int info, + void *data, int len, + struct usb_device *dev, int index, urb_priv_t *urb_priv) +{ + volatile td_t *td, *td_pt; +#ifdef OHCI_FILL_TRACE + int i; +#endif + + if (index > urb_priv->length) { + err("index > length"); + return; + } + /* use this td as the next dummy */ + td_pt = urb_priv->td [index]; + td_pt->hwNextTD = 0; + + /* fill the old dummy TD */ + td = urb_priv->td [index] = (td_t *)(ohci_cpu_to_le32 (urb_priv->ed->hwTailP) & ~0xf); + + td->ed = urb_priv->ed; + td->next_dl_td = NULL; + td->index = index; + td->data = (__u32)data; +#ifdef OHCI_FILL_TRACE + if (usb_pipebulk(urb_priv->pipe) && usb_pipeout(urb_priv->pipe)) { + for (i = 0; i < len; i++) + printf("td->data[%d] %#2x ",i, ((unsigned char *)td->data)[i]); + printf("\n"); + } +#endif + if (!len) + data = 0; + + td->hwINFO = ohci_cpu_to_le32 (info); + td->hwCBP = ohci_cpu_to_le32 ((unsigned long)data); + if (data) + td->hwBE = ohci_cpu_to_le32 ((unsigned long)(data + len - 1)); + else + td->hwBE = 0; + td->hwNextTD = ohci_cpu_to_le32 ((unsigned long)td_pt); + + /* append to queue */ + td->ed->hwTailP = td->hwNextTD; +} + +/*-------------------------------------------------------------------------*/ + +/* prepare all TDs of a transfer */ +static void td_submit_job (struct usb_device *dev, unsigned long pipe, void *buffer, + int transfer_len, struct devrequest *setup, urb_priv_t *urb, int interval) +{ + ohci_t *ohci = &gohci; + int data_len = transfer_len; + void *data; + int cnt = 0; + __u32 info = 0; + unsigned int toggle = 0; + + /* OHCI handles the DATA-toggles itself, we just use the USB-toggle bits for reseting */ + if(usb_gettoggle(dev, usb_pipeendpoint(pipe), usb_pipeout(pipe))) { + toggle = TD_T_TOGGLE; + } else { + toggle = TD_T_DATA0; + usb_settoggle(dev, usb_pipeendpoint(pipe), usb_pipeout(pipe), 1); + } + urb->td_cnt = 0; + if (data_len) + data = buffer; + else + data = 0; + + switch (usb_pipetype (pipe)) { + case PIPE_BULK: + info = usb_pipeout (pipe)? + TD_CC | TD_DP_OUT : TD_CC | TD_DP_IN ; + while(data_len > 4096) { + td_fill (ohci, info | (cnt? TD_T_TOGGLE:toggle), data, 4096, dev, cnt, urb); + data += 4096; data_len -= 4096; cnt++; + } + info = usb_pipeout (pipe)? + TD_CC | TD_DP_OUT : TD_CC | TD_R | TD_DP_IN ; + td_fill (ohci, info | (cnt? TD_T_TOGGLE:toggle), data, data_len, dev, cnt, urb); + cnt++; + + if (!ohci->sleeping) + writel (OHCI_BLF, &ohci->regs->cmdstatus); /* start bulk list */ + break; + + case PIPE_CONTROL: + info = TD_CC | TD_DP_SETUP | TD_T_DATA0; + td_fill (ohci, info, setup, 8, dev, cnt++, urb); + if (data_len > 0) { + info = usb_pipeout (pipe)? + TD_CC | TD_R | TD_DP_OUT | TD_T_DATA1 : TD_CC | TD_R | TD_DP_IN | TD_T_DATA1; + /* NOTE: mishandles transfers >8K, some >4K */ + td_fill (ohci, info, data, data_len, dev, cnt++, urb); + } + info = usb_pipeout (pipe)? + TD_CC | TD_DP_IN | TD_T_DATA1: TD_CC | TD_DP_OUT | TD_T_DATA1; + td_fill (ohci, info, data, 0, dev, cnt++, urb); + if (!ohci->sleeping) + writel (OHCI_CLF, &ohci->regs->cmdstatus); /* start Control list */ + break; + } + if (urb->length != cnt) + dbg("TD LENGTH %d != CNT %d", urb->length, cnt); +} + +/*-------------------------------------------------------------------------* + * Done List handling functions + *-------------------------------------------------------------------------*/ + + +/* calculate the transfer length and update the urb */ + +static void dl_transfer_length(td_t * td) +{ + __u32 tdINFO, tdBE, tdCBP; + urb_priv_t *lurb_priv = &urb_priv; + + tdINFO = ohci_cpu_to_le32 (td->hwINFO); + tdBE = ohci_cpu_to_le32 (td->hwBE); + tdCBP = ohci_cpu_to_le32 (td->hwCBP); + + + if (!(usb_pipecontrol(lurb_priv->pipe) && + ((td->index == 0) || (td->index == lurb_priv->length - 1)))) { + if (tdBE != 0) { + if (td->hwCBP == 0) + lurb_priv->actual_length += tdBE - td->data + 1; + else + lurb_priv->actual_length += tdCBP - td->data; + } + } +} + +/*-------------------------------------------------------------------------*/ + +/* replies to the request have to be on a FIFO basis so + * we reverse the reversed done-list */ + +static td_t * dl_reverse_done_list (ohci_t *ohci) +{ + __u32 td_list_hc; + td_t *td_rev = NULL; + td_t *td_list = NULL; + urb_priv_t *lurb_priv = NULL; + + td_list_hc = ohci_cpu_to_le32 (ohci->hcca->done_head) & 0xfffffff0; + ohci->hcca->done_head = 0; + + while (td_list_hc) { + td_list = (td_t *)td_list_hc; + + if (TD_CC_GET (ohci_cpu_to_le32 (td_list->hwINFO))) { + lurb_priv = &urb_priv; + dbg(" USB-error/status: %x : %p", + TD_CC_GET (ohci_cpu_to_le32 (td_list->hwINFO)), td_list); + if (td_list->ed->hwHeadP & ohci_cpu_to_le32 (0x1)) { + if (lurb_priv && ((td_list->index + 1) < lurb_priv->length)) { + td_list->ed->hwHeadP = + (lurb_priv->td[lurb_priv->length - 1]->hwNextTD & ohci_cpu_to_le32 (0xfffffff0)) | + (td_list->ed->hwHeadP & ohci_cpu_to_le32 (0x2)); + lurb_priv->td_cnt += lurb_priv->length - td_list->index - 1; + } else + td_list->ed->hwHeadP &= ohci_cpu_to_le32 (0xfffffff2); + } +#ifdef CONFIG_MPC5200 + td_list->hwNextTD = 0; +#endif + } + + td_list->next_dl_td = td_rev; + td_rev = td_list; + td_list_hc = ohci_cpu_to_le32 (td_list->hwNextTD) & 0xfffffff0; + } + return td_list; +} + +/*-------------------------------------------------------------------------*/ + +/* td done list */ +static int dl_done_list (ohci_t *ohci, td_t *td_list) +{ + td_t *td_list_next = NULL; + ed_t *ed; + int cc = 0; + int stat = 0; + /* urb_t *urb; */ + urb_priv_t *lurb_priv; + __u32 tdINFO, edHeadP, edTailP; + + while (td_list) { + td_list_next = td_list->next_dl_td; + + lurb_priv = &urb_priv; + tdINFO = ohci_cpu_to_le32 (td_list->hwINFO); + + ed = td_list->ed; + + dl_transfer_length(td_list); + + /* error code of transfer */ + cc = TD_CC_GET (tdINFO); + if (++(lurb_priv->td_cnt) == lurb_priv->length) { + if ((ed->state & (ED_OPER | ED_UNLINK)) + && (lurb_priv->state != URB_DEL)) { + dbg("ConditionCode %#x", cc); + stat = cc_to_error[cc]; + urb_finished = 1; + } + } + + if (ed->state != ED_NEW) { + edHeadP = ohci_cpu_to_le32 (ed->hwHeadP) & 0xfffffff0; + edTailP = ohci_cpu_to_le32 (ed->hwTailP); + + /* unlink eds if they are not busy */ + if ((edHeadP == edTailP) && (ed->state == ED_OPER)) + ep_unlink (ohci, ed); + } + + td_list = td_list_next; + } + return stat; +} + +/*-------------------------------------------------------------------------* + * Virtual Root Hub + *-------------------------------------------------------------------------*/ + +/* Device descriptor */ +static __u8 root_hub_dev_des[] = +{ + 0x12, /* __u8 bLength; */ + 0x01, /* __u8 bDescriptorType; Device */ + 0x10, /* __u16 bcdUSB; v1.1 */ + 0x01, + 0x09, /* __u8 bDeviceClass; HUB_CLASSCODE */ + 0x00, /* __u8 bDeviceSubClass; */ + 0x00, /* __u8 bDeviceProtocol; */ + 0x08, /* __u8 bMaxPacketSize0; 8 Bytes */ + 0x00, /* __u16 idVendor; */ + 0x00, + 0x00, /* __u16 idProduct; */ + 0x00, + 0x00, /* __u16 bcdDevice; */ + 0x00, + 0x00, /* __u8 iManufacturer; */ + 0x01, /* __u8 iProduct; */ + 0x00, /* __u8 iSerialNumber; */ + 0x01 /* __u8 bNumConfigurations; */ +}; + + +/* Configuration descriptor */ +static __u8 root_hub_config_des[] = +{ + 0x09, /* __u8 bLength; */ + 0x02, /* __u8 bDescriptorType; Configuration */ + 0x19, /* __u16 wTotalLength; */ + 0x00, + 0x01, /* __u8 bNumInterfaces; */ + 0x01, /* __u8 bConfigurationValue; */ + 0x00, /* __u8 iConfiguration; */ + 0x40, /* __u8 bmAttributes; + Bit 7: Bus-powered, 6: Self-powered, 5 Remote-wakwup, 4..0: resvd */ + 0x00, /* __u8 MaxPower; */ + + /* interface */ + 0x09, /* __u8 if_bLength; */ + 0x04, /* __u8 if_bDescriptorType; Interface */ + 0x00, /* __u8 if_bInterfaceNumber; */ + 0x00, /* __u8 if_bAlternateSetting; */ + 0x01, /* __u8 if_bNumEndpoints; */ + 0x09, /* __u8 if_bInterfaceClass; HUB_CLASSCODE */ + 0x00, /* __u8 if_bInterfaceSubClass; */ + 0x00, /* __u8 if_bInterfaceProtocol; */ + 0x00, /* __u8 if_iInterface; */ + + /* endpoint */ + 0x07, /* __u8 ep_bLength; */ + 0x05, /* __u8 ep_bDescriptorType; Endpoint */ + 0x81, /* __u8 ep_bEndpointAddress; IN Endpoint 1 */ + 0x03, /* __u8 ep_bmAttributes; Interrupt */ + 0x02, /* __u16 ep_wMaxPacketSize; ((MAX_ROOT_PORTS + 1) / 8 */ + 0x00, + 0xff /* __u8 ep_bInterval; 255 ms */ +}; + +static unsigned char root_hub_str_index0[] = +{ + 0x04, /* __u8 bLength; */ + 0x03, /* __u8 bDescriptorType; String-descriptor */ + 0x09, /* __u8 lang ID */ + 0x04, /* __u8 lang ID */ +}; + +static unsigned char root_hub_str_index1[] = +{ + 28, /* __u8 bLength; */ + 0x03, /* __u8 bDescriptorType; String-descriptor */ + 'O', /* __u8 Unicode */ + 0, /* __u8 Unicode */ + 'H', /* __u8 Unicode */ + 0, /* __u8 Unicode */ + 'C', /* __u8 Unicode */ + 0, /* __u8 Unicode */ + 'I', /* __u8 Unicode */ + 0, /* __u8 Unicode */ + ' ', /* __u8 Unicode */ + 0, /* __u8 Unicode */ + 'R', /* __u8 Unicode */ + 0, /* __u8 Unicode */ + 'o', /* __u8 Unicode */ + 0, /* __u8 Unicode */ + 'o', /* __u8 Unicode */ + 0, /* __u8 Unicode */ + 't', /* __u8 Unicode */ + 0, /* __u8 Unicode */ + ' ', /* __u8 Unicode */ + 0, /* __u8 Unicode */ + 'H', /* __u8 Unicode */ + 0, /* __u8 Unicode */ + 'u', /* __u8 Unicode */ + 0, /* __u8 Unicode */ + 'b', /* __u8 Unicode */ + 0, /* __u8 Unicode */ +}; + +/* Hub class-specific descriptor is constructed dynamically */ + + +/*-------------------------------------------------------------------------*/ + +#define OK(x) len = (x); break +#ifdef DEBUG +#define WR_RH_STAT(x) {info("WR:status %#8x", (x));writel((x), &gohci.regs->roothub.status);} +#define WR_RH_PORTSTAT(x) {info("WR:portstatus[%d] %#8x", wIndex-1, (x));writel((x), &gohci.regs->roothub.portstatus[wIndex-1]);} +#else +#define WR_RH_STAT(x) writel((x), &gohci.regs->roothub.status) +#define WR_RH_PORTSTAT(x) writel((x), &gohci.regs->roothub.portstatus[wIndex-1]) +#endif +#define RD_RH_STAT roothub_status(&gohci) +#define RD_RH_PORTSTAT roothub_portstatus(&gohci,wIndex-1) + +/* request to virtual root hub */ + +int rh_check_port_status(ohci_t *controller) +{ + __u32 temp, ndp, i; + int res; + + res = -1; + temp = roothub_a (controller); + ndp = (temp & RH_A_NDP); + for (i = 0; i < ndp; i++) { + temp = roothub_portstatus (controller, i); + /* check for a device disconnect */ + if (((temp & (RH_PS_PESC | RH_PS_CSC)) == + (RH_PS_PESC | RH_PS_CSC)) && + ((temp & RH_PS_CCS) == 0)) { + res = i; + break; + } + } + return res; +} + +static int ohci_submit_rh_msg(struct usb_device *dev, unsigned long pipe, + void *buffer, int transfer_len, struct devrequest *cmd) +{ + void * data = buffer; + int leni = transfer_len; + int len = 0; + int stat = 0; + __u32 datab[4]; + __u8 *data_buf = (__u8 *)datab; + __u16 bmRType_bReq; + __u16 wValue; + __u16 wIndex; + __u16 wLength; + +#ifdef DEBUG +urb_priv.actual_length = 0; +pkt_print(dev, pipe, buffer, transfer_len, cmd, "SUB(rh)", usb_pipein(pipe)); +#endif + if (usb_pipeint(pipe)) { + info("Root-Hub submit IRQ: NOT implemented"); + return 0; + } + + bmRType_bReq = cmd->requesttype | (cmd->request << 8); + wValue = m16_swap (cmd->value); + wIndex = m16_swap (cmd->index); + wLength = m16_swap (cmd->length); + + info("Root-Hub: adr: %2x cmd(%1x): %08x %04x %04x %04x", + dev->devnum, 8, bmRType_bReq, wValue, wIndex, wLength); + + switch (bmRType_bReq) { + /* Request Destination: + without flags: Device, + RH_INTERFACE: interface, + RH_ENDPOINT: endpoint, + RH_CLASS means HUB here, + RH_OTHER | RH_CLASS almost ever means HUB_PORT here + */ + + case RH_GET_STATUS: + *(__u16 *) data_buf = m16_swap (1); OK (2); + case RH_GET_STATUS | RH_INTERFACE: + *(__u16 *) data_buf = m16_swap (0); OK (2); + case RH_GET_STATUS | RH_ENDPOINT: + *(__u16 *) data_buf = m16_swap (0); OK (2); + case RH_GET_STATUS | RH_CLASS: + *(__u32 *) data_buf = m32_swap ( + RD_RH_STAT & ~(RH_HS_CRWE | RH_HS_DRWE)); + OK (4); + case RH_GET_STATUS | RH_OTHER | RH_CLASS: + *(__u32 *) data_buf = m32_swap (RD_RH_PORTSTAT); OK (4); + + case RH_CLEAR_FEATURE | RH_ENDPOINT: + switch (wValue) { + case (RH_ENDPOINT_STALL): OK (0); + } + break; + + case RH_CLEAR_FEATURE | RH_CLASS: + switch (wValue) { + case RH_C_HUB_LOCAL_POWER: + OK(0); + case (RH_C_HUB_OVER_CURRENT): + WR_RH_STAT(RH_HS_OCIC); OK (0); + } + break; + + case RH_CLEAR_FEATURE | RH_OTHER | RH_CLASS: + switch (wValue) { + case (RH_PORT_ENABLE): + WR_RH_PORTSTAT (RH_PS_CCS ); OK (0); + case (RH_PORT_SUSPEND): + WR_RH_PORTSTAT (RH_PS_POCI); OK (0); + case (RH_PORT_POWER): + WR_RH_PORTSTAT (RH_PS_LSDA); OK (0); + case (RH_C_PORT_CONNECTION): + WR_RH_PORTSTAT (RH_PS_CSC ); OK (0); + case (RH_C_PORT_ENABLE): + WR_RH_PORTSTAT (RH_PS_PESC); OK (0); + case (RH_C_PORT_SUSPEND): + WR_RH_PORTSTAT (RH_PS_PSSC); OK (0); + case (RH_C_PORT_OVER_CURRENT): + WR_RH_PORTSTAT (RH_PS_OCIC); OK (0); + case (RH_C_PORT_RESET): + WR_RH_PORTSTAT (RH_PS_PRSC); OK (0); + } + break; + + case RH_SET_FEATURE | RH_OTHER | RH_CLASS: + switch (wValue) { + case (RH_PORT_SUSPEND): + WR_RH_PORTSTAT (RH_PS_PSS ); OK (0); + case (RH_PORT_RESET): /* BUG IN HUP CODE *********/ + if (RD_RH_PORTSTAT & RH_PS_CCS) + WR_RH_PORTSTAT (RH_PS_PRS); + OK (0); + case (RH_PORT_POWER): + WR_RH_PORTSTAT (RH_PS_PPS ); OK (0); + case (RH_PORT_ENABLE): /* BUG IN HUP CODE *********/ + if (RD_RH_PORTSTAT & RH_PS_CCS) + WR_RH_PORTSTAT (RH_PS_PES ); + OK (0); + } + break; + + case RH_SET_ADDRESS: gohci.rh.devnum = wValue; OK(0); + + case RH_GET_DESCRIPTOR: + switch ((wValue & 0xff00) >> 8) { + case (0x01): /* device descriptor */ + len = min_t(unsigned int, + leni, + min_t(unsigned int, + sizeof (root_hub_dev_des), + wLength)); + data_buf = root_hub_dev_des; OK(len); + case (0x02): /* configuration descriptor */ + len = min_t(unsigned int, + leni, + min_t(unsigned int, + sizeof (root_hub_config_des), + wLength)); + data_buf = root_hub_config_des; OK(len); + case (0x03): /* string descriptors */ + if(wValue==0x0300) { + len = min_t(unsigned int, + leni, + min_t(unsigned int, + sizeof (root_hub_str_index0), + wLength)); + data_buf = root_hub_str_index0; + OK(len); + } + if(wValue==0x0301) { + len = min_t(unsigned int, + leni, + min_t(unsigned int, + sizeof (root_hub_str_index1), + wLength)); + data_buf = root_hub_str_index1; + OK(len); + } + default: + stat = USB_ST_STALLED; + } + break; + + case RH_GET_DESCRIPTOR | RH_CLASS: + { + __u32 temp = roothub_a (&gohci); + + data_buf [0] = 9; /* min length; */ + data_buf [1] = 0x29; + data_buf [2] = temp & RH_A_NDP; + data_buf [3] = 0; + if (temp & RH_A_PSM) /* per-port power switching? */ + data_buf [3] |= 0x1; + if (temp & RH_A_NOCP) /* no overcurrent reporting? */ + data_buf [3] |= 0x10; + else if (temp & RH_A_OCPM) /* per-port overcurrent reporting? */ + data_buf [3] |= 0x8; + + /* corresponds to data_buf[4-7] */ + datab [1] = 0; + data_buf [5] = (temp & RH_A_POTPGT) >> 24; + temp = roothub_b (&gohci); + data_buf [7] = temp & RH_B_DR; + if (data_buf [2] < 7) { + data_buf [8] = 0xff; + } else { + data_buf [0] += 2; + data_buf [8] = (temp & RH_B_DR) >> 8; + data_buf [10] = data_buf [9] = 0xff; + } + + len = min_t(unsigned int, leni, + min_t(unsigned int, data_buf [0], wLength)); + OK (len); + } + + case RH_GET_CONFIGURATION: *(__u8 *) data_buf = 0x01; OK (1); + + case RH_SET_CONFIGURATION: WR_RH_STAT (0x10000); OK (0); + + default: + dbg ("unsupported root hub command"); + stat = USB_ST_STALLED; + } + +#ifdef DEBUG + ohci_dump_roothub (&gohci, 1); +#endif + + len = min_t(int, len, leni); + if (data != data_buf) + memcpy (data, data_buf, len); + dev->act_len = len; + dev->status = stat; + +#ifdef DEBUG + if (transfer_len) + urb_priv.actual_length = transfer_len; + pkt_print(dev, pipe, buffer, transfer_len, cmd, "RET(rh)", 0/*usb_pipein(pipe)*/); +#endif + + return stat; +} + +/*-------------------------------------------------------------------------*/ + +/* common code for handling submit messages - used for all but root hub */ +/* accesses. */ +int submit_common_msg(struct usb_device *dev, unsigned long pipe, void *buffer, + int transfer_len, struct devrequest *setup, int interval) +{ + int stat = 0; + int maxsize = usb_maxpacket(dev, pipe); + int timeout; + + /* device pulled? Shortcut the action. */ + if (devgone == dev) { + dev->status = USB_ST_CRC_ERR; + return 0; + } + +#ifdef DEBUG + urb_priv.actual_length = 0; + pkt_print(dev, pipe, buffer, transfer_len, setup, "SUB", usb_pipein(pipe)); +#endif + if (!maxsize) { + err("submit_common_message: pipesize for pipe %lx is zero", + pipe); + return -1; + } + + if (sohci_submit_job(dev, pipe, buffer, transfer_len, setup, interval) < 0) { + err("sohci_submit_job failed"); + return -1; + } + + /* allow more time for a BULK device to react - some are slow */ +#define BULK_TO 5000 /* timeout in milliseconds */ + if (usb_pipebulk(pipe)) + timeout = BULK_TO; + else + timeout = 100; + + /* wait for it to complete */ + for (;;) { + /* check whether the controller is done */ + stat = hc_interrupt(); + if (stat < 0) { + stat = USB_ST_CRC_ERR; + break; + } + + /* NOTE: since we are not interrupt driven in U-Boot and always + * handle only one URB at a time, we cannot assume the + * transaction finished on the first successful return from + * hc_interrupt().. unless the flag for current URB is set, + * meaning that all TD's to/from device got actually + * transferred and processed. If the current URB is not + * finished we need to re-iterate this loop so as + * hc_interrupt() gets called again as there needs to be some + * more TD's to process still */ + if ((stat >= 0) && (stat != 0xff) && (urb_finished)) { + /* 0xff is returned for an SF-interrupt */ + break; + } + + if (--timeout) { + wait_ms(1); + if (!urb_finished) + dbg("\%"); + + } else { + err("CTL:TIMEOUT "); + dbg("submit_common_msg: TO status %x\n", stat); + stat = USB_ST_CRC_ERR; + urb_finished = 1; + break; + } + } +#if 0 + /* we got an Root Hub Status Change interrupt */ + if (got_rhsc) { +#ifdef DEBUG + ohci_dump_roothub (&gohci, 1); +#endif + got_rhsc = 0; + /* abuse timeout */ + timeout = rh_check_port_status(&gohci); + if (timeout >= 0) { +#if 0 /* this does nothing useful, but leave it here in case that changes */ + /* the called routine adds 1 to the passed value */ + usb_hub_port_connect_change(gohci.rh.dev, timeout - 1); +#endif + /* + * XXX + * This is potentially dangerous because it assumes + * that only one device is ever plugged in! + */ + devgone = dev; + } + } +#endif + + dev->status = stat; + dev->act_len = transfer_len; + +#ifdef DEBUG + pkt_print(dev, pipe, buffer, transfer_len, setup, "RET(ctlr)", usb_pipein(pipe)); +#endif + + /* free TDs in urb_priv */ + urb_free_priv (&urb_priv); + return 0; +} + +/* submit routines called from usb.c */ +int submit_bulk_msg(struct usb_device *dev, unsigned long pipe, void *buffer, + int transfer_len) +{ + info("submit_bulk_msg"); + return submit_common_msg(dev, pipe, buffer, transfer_len, NULL, 0); +} + +int submit_control_msg(struct usb_device *dev, unsigned long pipe, void *buffer, + int transfer_len, struct devrequest *setup) +{ + int maxsize = usb_maxpacket(dev, pipe); + + info("submit_control_msg"); +#ifdef DEBUG + urb_priv.actual_length = 0; + pkt_print(dev, pipe, buffer, transfer_len, setup, "SUB", usb_pipein(pipe)); +#endif + if (!maxsize) { + err("submit_control_message: pipesize for pipe %lx is zero", + pipe); + return -1; + } + if (((pipe >> 8) & 0x7f) == gohci.rh.devnum) { + gohci.rh.dev = dev; + /* root hub - redirect */ + return ohci_submit_rh_msg(dev, pipe, buffer, transfer_len, + setup); + } + + return submit_common_msg(dev, pipe, buffer, transfer_len, setup, 0); +} + +int submit_int_msg(struct usb_device *dev, unsigned long pipe, void *buffer, + int transfer_len, int interval) +{ + info("submit_int_msg"); + return -1; +} + +/*-------------------------------------------------------------------------* + * HC functions + *-------------------------------------------------------------------------*/ + +/* reset the HC and BUS */ + +static int hc_reset (ohci_t *ohci) +{ + int timeout = 30; + int smm_timeout = 50; /* 0,5 sec */ + + if (readl (&ohci->regs->control) & OHCI_CTRL_IR) { /* SMM owns the HC */ + writel (OHCI_OCR, &ohci->regs->cmdstatus); /* request ownership */ + info("USB HC TakeOver from SMM"); + while (readl (&ohci->regs->control) & OHCI_CTRL_IR) { + wait_ms (10); + if (--smm_timeout == 0) { + err("USB HC TakeOver failed!"); + return -1; + } + } + } + + /* Disable HC interrupts */ + writel (OHCI_INTR_MIE, &ohci->regs->intrdisable); + + dbg("USB HC reset_hc usb-%s: ctrl = 0x%X ;", + ohci->slot_name, + readl (&ohci->regs->control)); + + /* Reset USB (needed by some controllers) */ + ohci->hc_control = 0; + writel (ohci->hc_control, &ohci->regs->control); + + /* HC Reset requires max 10 us delay */ + writel (OHCI_HCR, &ohci->regs->cmdstatus); + while ((readl (&ohci->regs->cmdstatus) & OHCI_HCR) != 0) { + if (--timeout == 0) { + err("USB HC reset timed out!"); + return -1; + } + udelay (1); + } + return 0; +} + +/*-------------------------------------------------------------------------*/ + +/* Start an OHCI controller, set the BUS operational + * enable interrupts + * connect the virtual root hub */ + +static int hc_start (ohci_t * ohci) +{ + __u32 mask; + unsigned int fminterval; + + ohci->disabled = 1; + + /* Tell the controller where the control and bulk lists are + * The lists are empty now. */ + + writel (0, &ohci->regs->ed_controlhead); + writel (0, &ohci->regs->ed_bulkhead); + + writel ((__u32)ohci->hcca, &ohci->regs->hcca); /* a reset clears this */ + + fminterval = 0x2edf; + writel ((fminterval * 9) / 10, &ohci->regs->periodicstart); + fminterval |= ((((fminterval - 210) * 6) / 7) << 16); + writel (fminterval, &ohci->regs->fminterval); + writel (0x628, &ohci->regs->lsthresh); + + /* start controller operations */ + ohci->hc_control = OHCI_CONTROL_INIT | OHCI_USB_OPER; + ohci->disabled = 0; + writel (ohci->hc_control, &ohci->regs->control); + + /* disable all interrupts */ + mask = (OHCI_INTR_SO | OHCI_INTR_WDH | OHCI_INTR_SF | OHCI_INTR_RD | + OHCI_INTR_UE | OHCI_INTR_FNO | OHCI_INTR_RHSC | + OHCI_INTR_OC | OHCI_INTR_MIE); + writel (mask, &ohci->regs->intrdisable); + /* clear all interrupts */ + mask &= ~OHCI_INTR_MIE; + writel (mask, &ohci->regs->intrstatus); + /* Choose the interrupts we care about now - but w/o MIE */ + mask = OHCI_INTR_RHSC | OHCI_INTR_UE | OHCI_INTR_WDH | OHCI_INTR_SO; + writel (mask, &ohci->regs->intrenable); + +#ifdef OHCI_USE_NPS + /* required for AMD-756 and some Mac platforms */ + writel ((roothub_a (ohci) | RH_A_NPS) & ~RH_A_PSM, + &ohci->regs->roothub.a); + writel (RH_HS_LPSC, &ohci->regs->roothub.status); +#endif /* OHCI_USE_NPS */ + +#define mdelay(n) ({unsigned long msec=(n); while (msec--) udelay(1000);}) + /* POTPGT delay is bits 24-31, in 2 ms units. */ + mdelay ((roothub_a (ohci) >> 23) & 0x1fe); + + /* connect the virtual root hub */ + ohci->rh.devnum = 0; + + return 0; +} + +/*-------------------------------------------------------------------------*/ + +/* an interrupt happens */ + +static int +hc_interrupt (void) +{ + ohci_t *ohci = &gohci; + struct ohci_regs *regs = ohci->regs; + int ints; + int stat = -1; + + if ((ohci->hcca->done_head != 0) && + !(ohci_cpu_to_le32(ohci->hcca->done_head) & 0x01)) { + + ints = OHCI_INTR_WDH; + + } else if ((ints = readl (®s->intrstatus)) == ~(u32)0) { + ohci->disabled++; + err ("%s device removed!", ohci->slot_name); + return -1; + + } else if ((ints &= readl (®s->intrenable)) == 0) { + dbg("hc_interrupt: returning..\n"); + return 0xff; + } + + /* dbg("Interrupt: %x frame: %x", ints, le16_to_cpu (ohci->hcca->frame_no)); */ + + if (ints & OHCI_INTR_RHSC) { + got_rhsc = 1; + stat = 0xff; + } + + if (ints & OHCI_INTR_UE) { + ohci->disabled++; + err ("OHCI Unrecoverable Error, controller usb-%s disabled", + ohci->slot_name); + /* e.g. due to PCI Master/Target Abort */ + +#ifdef DEBUG + ohci_dump (ohci, 1); +#endif + /* FIXME: be optimistic, hope that bug won't repeat often. */ + /* Make some non-interrupt context restart the controller. */ + /* Count and limit the retries though; either hardware or */ + /* software errors can go forever... */ + hc_reset (ohci); + return -1; + } + + if (ints & OHCI_INTR_WDH) { + writel (OHCI_INTR_WDH, ®s->intrdisable); + stat = dl_done_list (&gohci, dl_reverse_done_list (&gohci)); + writel (OHCI_INTR_WDH, ®s->intrenable); + } + + if (ints & OHCI_INTR_SO) { + dbg("USB Schedule overrun\n"); + writel (OHCI_INTR_SO, ®s->intrenable); + stat = -1; + } + + /* FIXME: this assumes SOF (1/ms) interrupts don't get lost... */ + if (ints & OHCI_INTR_SF) { + unsigned int frame = ohci_cpu_to_le16 (ohci->hcca->frame_no) & 1; + wait_ms(1); + writel (OHCI_INTR_SF, ®s->intrdisable); + if (ohci->ed_rm_list[frame] != NULL) + writel (OHCI_INTR_SF, ®s->intrenable); + stat = 0xff; + } + + writel (ints, ®s->intrstatus); + return stat; +} + +/*-------------------------------------------------------------------------*/ + +/*-------------------------------------------------------------------------*/ + +/* De-allocate all resources.. */ + +static void hc_release_ohci (ohci_t *ohci) +{ + dbg ("USB HC release ohci usb-%s", ohci->slot_name); + + if (!ohci->disabled) + hc_reset (ohci); +} + +/*-------------------------------------------------------------------------*/ + +/* + * low level initalisation routine, called from usb.c + */ +static char ohci_inited = 0; + +int usb_lowlevel_init(void) +{ + memset (&gohci, 0, sizeof (ohci_t)); + memset (&urb_priv, 0, sizeof (urb_priv_t)); + + /* align the storage */ + if ((__u32)&ghcca[0] & 0xff) { + err("HCCA not aligned!!"); + return -1; + } + phcca = &ghcca[0]; + info("aligned ghcca %p", phcca); + memset(&ohci_dev, 0, sizeof(struct ohci_device)); + if ((__u32)&ohci_dev.ed[0] & 0x7) { + err("EDs not aligned!!"); + return -1; + } + memset(gtd, 0, sizeof(td_t) * (NUM_TD + 1)); + if ((__u32)gtd & 0x7) { + err("TDs not aligned!!"); + return -1; + } + ptd = gtd; + gohci.hcca = phcca; + memset (phcca, 0, sizeof (struct ohci_hcca)); + + gohci.disabled = 1; + gohci.sleeping = 0; + gohci.irq = -1; +#if defined(CONFIG_440EP) + gohci.regs = (struct ohci_regs *)(CONFIG_SYS_PERIPHERAL_BASE | 0x1000); +#elif defined(CONFIG_440EPX) || defined(CONFIG_SYS_USB_HOST) + gohci.regs = (struct ohci_regs *)(CONFIG_SYS_USB_HOST); +#endif + + gohci.flags = 0; + gohci.slot_name = "ppc440"; + + if (hc_reset (&gohci) < 0) { + hc_release_ohci (&gohci); + return -1; + } + + if (hc_start (&gohci) < 0) { + err ("can't start usb-%s", gohci.slot_name); + hc_release_ohci (&gohci); + return -1; + } + +#ifdef DEBUG + ohci_dump (&gohci, 1); +#endif + ohci_inited = 1; + urb_finished = 1; + +#if defined(CONFIG_440EP) || defined(CONFIG_440EPX) + /* init the device driver */ + usb_dev_init(); +#endif + + return 0; +} + +int usb_lowlevel_stop(void) +{ + /* this gets called really early - before the controller has */ + /* even been initialized! */ + if (!ohci_inited) + return 0; + /* TODO release any interrupts, etc. */ + /* call hc_release_ohci() here ? */ + hc_reset (&gohci); + return 0; +} + +#endif /* CONFIG_USB_OHCI */ diff --git a/arch/powerpc/cpu/ppc4xx/usb_ohci.h b/arch/powerpc/cpu/ppc4xx/usb_ohci.h new file mode 100644 index 0000000..2c3dc4f --- /dev/null +++ b/arch/powerpc/cpu/ppc4xx/usb_ohci.h @@ -0,0 +1,410 @@ +/* + * URB OHCI HCD (Host Controller Driver) for USB. + * + * (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at> + * (C) Copyright 2000-2001 David Brownell <dbrownell@users.sourceforge.net> + * + * usb-ohci.h + */ + +static int cc_to_error[16] = { + +/* mapping of the OHCI CC status to error codes */ + /* No Error */ 0, + /* CRC Error */ USB_ST_CRC_ERR, + /* Bit Stuff */ USB_ST_BIT_ERR, + /* Data Togg */ USB_ST_CRC_ERR, + /* Stall */ USB_ST_STALLED, + /* DevNotResp */ -1, + /* PIDCheck */ USB_ST_BIT_ERR, + /* UnExpPID */ USB_ST_BIT_ERR, + /* DataOver */ USB_ST_BUF_ERR, + /* DataUnder */ USB_ST_BUF_ERR, + /* reservd */ -1, + /* reservd */ -1, + /* BufferOver */ USB_ST_BUF_ERR, + /* BuffUnder */ USB_ST_BUF_ERR, + /* Not Access */ -1, + /* Not Access */ -1 +}; + +/* ED States */ + +#define ED_NEW 0x00 +#define ED_UNLINK 0x01 +#define ED_OPER 0x02 +#define ED_DEL 0x04 +#define ED_URB_DEL 0x08 + +/* usb_ohci_ed */ +struct ed { + __u32 hwINFO; + __u32 hwTailP; + __u32 hwHeadP; + __u32 hwNextED; + + struct ed *ed_prev; + __u8 int_period; + __u8 int_branch; + __u8 int_load; + __u8 int_interval; + __u8 state; + __u8 type; + __u16 last_iso; + struct ed *ed_rm_list; + + struct usb_device *usb_dev; + __u32 unused[3]; +} __attribute__((aligned(16))); +typedef struct ed ed_t; + +/* TD info field */ +#define TD_CC 0xf0000000 +#define TD_CC_GET(td_p) ((td_p >>28) & 0x0f) +#define TD_CC_SET(td_p, cc) (td_p) = ((td_p) & 0x0fffffff) | (((cc) & 0x0f) << 28) +#define TD_EC 0x0C000000 +#define TD_T 0x03000000 +#define TD_T_DATA0 0x02000000 +#define TD_T_DATA1 0x03000000 +#define TD_T_TOGGLE 0x00000000 +#define TD_R 0x00040000 +#define TD_DI 0x00E00000 +#define TD_DI_SET(X) (((X) & 0x07)<< 21) +#define TD_DP 0x00180000 +#define TD_DP_SETUP 0x00000000 +#define TD_DP_IN 0x00100000 +#define TD_DP_OUT 0x00080000 + +#define TD_ISO 0x00010000 +#define TD_DEL 0x00020000 + +/* CC Codes */ +#define TD_CC_NOERROR 0x00 +#define TD_CC_CRC 0x01 +#define TD_CC_BITSTUFFING 0x02 +#define TD_CC_DATATOGGLEM 0x03 +#define TD_CC_STALL 0x04 +#define TD_DEVNOTRESP 0x05 +#define TD_PIDCHECKFAIL 0x06 +#define TD_UNEXPECTEDPID 0x07 +#define TD_DATAOVERRUN 0x08 +#define TD_DATAUNDERRUN 0x09 +#define TD_BUFFEROVERRUN 0x0C +#define TD_BUFFERUNDERRUN 0x0D +#define TD_NOTACCESSED 0x0F + +#define MAXPSW 1 + +struct td { + __u32 hwINFO; + __u32 hwCBP; /* Current Buffer Pointer */ + __u32 hwNextTD; /* Next TD Pointer */ + __u32 hwBE; /* Memory Buffer End Pointer */ + + __u16 hwPSW[MAXPSW]; + __u8 unused; + __u8 index; + struct ed *ed; + struct td *next_dl_td; + struct usb_device *usb_dev; + int transfer_len; + __u32 data; + + __u32 unused2[2]; +} __attribute__((aligned(32))); +typedef struct td td_t; + +#define OHCI_ED_SKIP (1 << 14) + +/* + * The HCCA (Host Controller Communications Area) is a 256 byte + * structure defined in the OHCI spec. that the host controller is + * told the base address of. It must be 256-byte aligned. + */ + +#define NUM_INTS 32 /* part of the OHCI standard */ +struct ohci_hcca { + __u32 int_table[NUM_INTS]; /* Interrupt ED table */ +#if defined(CONFIG_MPC5200) + __u16 pad1; /* set to 0 on each frame_no change */ + __u16 frame_no; /* current frame number */ +#else + __u16 frame_no; /* current frame number */ + __u16 pad1; /* set to 0 on each frame_no change */ +#endif + __u32 done_head; /* info returned for an interrupt */ + u8 reserved_for_hc[116]; +} __attribute__((aligned(256))); + +/* + * Maximum number of root hub ports. + */ +#define MAX_ROOT_PORTS 15 /* maximum OHCI root hub ports */ + +/* + * This is the structure of the OHCI controller's memory mapped I/O + * region. This is Memory Mapped I/O. You must use the readl() and + * writel() macros defined in asm/io.h to access these!! + */ +struct ohci_regs { + /* control and status registers */ + __u32 revision; + __u32 control; + __u32 cmdstatus; + __u32 intrstatus; + __u32 intrenable; + __u32 intrdisable; + /* memory pointers */ + __u32 hcca; + __u32 ed_periodcurrent; + __u32 ed_controlhead; + __u32 ed_controlcurrent; + __u32 ed_bulkhead; + __u32 ed_bulkcurrent; + __u32 donehead; + /* frame counters */ + __u32 fminterval; + __u32 fmremaining; + __u32 fmnumber; + __u32 periodicstart; + __u32 lsthresh; + /* Root hub ports */ + struct ohci_roothub_regs { + __u32 a; + __u32 b; + __u32 status; + __u32 portstatus[MAX_ROOT_PORTS]; + } roothub; +} __attribute__((aligned(32))); + +/* OHCI CONTROL AND STATUS REGISTER MASKS */ + +/* + * HcControl (control) register masks + */ +#define OHCI_CTRL_CBSR (3 << 0) /* control/bulk service ratio */ +#define OHCI_CTRL_PLE (1 << 2) /* periodic list enable */ +#define OHCI_CTRL_IE (1 << 3) /* isochronous enable */ +#define OHCI_CTRL_CLE (1 << 4) /* control list enable */ +#define OHCI_CTRL_BLE (1 << 5) /* bulk list enable */ +#define OHCI_CTRL_HCFS (3 << 6) /* host controller functional state */ +#define OHCI_CTRL_IR (1 << 8) /* interrupt routing */ +#define OHCI_CTRL_RWC (1 << 9) /* remote wakeup connected */ +#define OHCI_CTRL_RWE (1 << 10) /* remote wakeup enable */ + +/* pre-shifted values for HCFS */ +# define OHCI_USB_RESET (0 << 6) +# define OHCI_USB_RESUME (1 << 6) +# define OHCI_USB_OPER (2 << 6) +# define OHCI_USB_SUSPEND (3 << 6) + +/* + * HcCommandStatus (cmdstatus) register masks + */ +#define OHCI_HCR (1 << 0) /* host controller reset */ +#define OHCI_CLF (1 << 1) /* control list filled */ +#define OHCI_BLF (1 << 2) /* bulk list filled */ +#define OHCI_OCR (1 << 3) /* ownership change request */ +#define OHCI_SOC (3 << 16) /* scheduling overrun count */ + +/* + * masks used with interrupt registers: + * HcInterruptStatus (intrstatus) + * HcInterruptEnable (intrenable) + * HcInterruptDisable (intrdisable) + */ +#define OHCI_INTR_SO (1 << 0) /* scheduling overrun */ +#define OHCI_INTR_WDH (1 << 1) /* writeback of done_head */ +#define OHCI_INTR_SF (1 << 2) /* start frame */ +#define OHCI_INTR_RD (1 << 3) /* resume detect */ +#define OHCI_INTR_UE (1 << 4) /* unrecoverable error */ +#define OHCI_INTR_FNO (1 << 5) /* frame number overflow */ +#define OHCI_INTR_RHSC (1 << 6) /* root hub status change */ +#define OHCI_INTR_OC (1 << 30) /* ownership change */ +#define OHCI_INTR_MIE (1 << 31) /* master interrupt enable */ + +/* Virtual Root HUB */ +struct virt_root_hub { + int devnum; /* Address of Root Hub endpoint */ + void *dev; /* was urb */ + void *int_addr; + int send; + int interval; +}; + +/* USB HUB CONSTANTS (not OHCI-specific; see hub.h) */ + +/* destination of request */ +#define RH_INTERFACE 0x01 +#define RH_ENDPOINT 0x02 +#define RH_OTHER 0x03 + +#define RH_CLASS 0x20 +#define RH_VENDOR 0x40 + +/* Requests: bRequest << 8 | bmRequestType */ +#define RH_GET_STATUS 0x0080 +#define RH_CLEAR_FEATURE 0x0100 +#define RH_SET_FEATURE 0x0300 +#define RH_SET_ADDRESS 0x0500 +#define RH_GET_DESCRIPTOR 0x0680 +#define RH_SET_DESCRIPTOR 0x0700 +#define RH_GET_CONFIGURATION 0x0880 +#define RH_SET_CONFIGURATION 0x0900 +#define RH_GET_STATE 0x0280 +#define RH_GET_INTERFACE 0x0A80 +#define RH_SET_INTERFACE 0x0B00 +#define RH_SYNC_FRAME 0x0C80 +/* Our Vendor Specific Request */ +#define RH_SET_EP 0x2000 + +/* Hub port features */ +#define RH_PORT_CONNECTION 0x00 +#define RH_PORT_ENABLE 0x01 +#define RH_PORT_SUSPEND 0x02 +#define RH_PORT_OVER_CURRENT 0x03 +#define RH_PORT_RESET 0x04 +#define RH_PORT_POWER 0x08 +#define RH_PORT_LOW_SPEED 0x09 + +#define RH_C_PORT_CONNECTION 0x10 +#define RH_C_PORT_ENABLE 0x11 +#define RH_C_PORT_SUSPEND 0x12 +#define RH_C_PORT_OVER_CURRENT 0x13 +#define RH_C_PORT_RESET 0x14 + +/* Hub features */ +#define RH_C_HUB_LOCAL_POWER 0x00 +#define RH_C_HUB_OVER_CURRENT 0x01 + +#define RH_DEVICE_REMOTE_WAKEUP 0x00 +#define RH_ENDPOINT_STALL 0x01 + +#define RH_ACK 0x01 +#define RH_REQ_ERR -1 +#define RH_NACK 0x00 + +/* OHCI ROOT HUB REGISTER MASKS */ + +/* roothub.portstatus [i] bits */ +#define RH_PS_CCS 0x00000001 /* current connect status */ +#define RH_PS_PES 0x00000002 /* port enable status */ +#define RH_PS_PSS 0x00000004 /* port suspend status */ +#define RH_PS_POCI 0x00000008 /* port over current indicator */ +#define RH_PS_PRS 0x00000010 /* port reset status */ +#define RH_PS_PPS 0x00000100 /* port power status */ +#define RH_PS_LSDA 0x00000200 /* low speed device attached */ +#define RH_PS_CSC 0x00010000 /* connect status change */ +#define RH_PS_PESC 0x00020000 /* port enable status change */ +#define RH_PS_PSSC 0x00040000 /* port suspend status change */ +#define RH_PS_OCIC 0x00080000 /* over current indicator change */ +#define RH_PS_PRSC 0x00100000 /* port reset status change */ + +/* roothub.status bits */ +#define RH_HS_LPS 0x00000001 /* local power status */ +#define RH_HS_OCI 0x00000002 /* over current indicator */ +#define RH_HS_DRWE 0x00008000 /* device remote wakeup enable */ +#define RH_HS_LPSC 0x00010000 /* local power status change */ +#define RH_HS_OCIC 0x00020000 /* over current indicator change */ +#define RH_HS_CRWE 0x80000000 /* clear remote wakeup enable */ + +/* roothub.b masks */ +#define RH_B_DR 0x0000ffff /* device removable flags */ +#define RH_B_PPCM 0xffff0000 /* port power control mask */ + +/* roothub.a masks */ +#define RH_A_NDP (0xff << 0) /* number of downstream ports */ +#define RH_A_PSM (1 << 8) /* power switching mode */ +#define RH_A_NPS (1 << 9) /* no power switching */ +#define RH_A_DT (1 << 10) /* device type (mbz) */ +#define RH_A_OCPM (1 << 11) /* over current protection mode */ +#define RH_A_NOCP (1 << 12) /* no over current protection */ +#define RH_A_POTPGT (0xff << 24) /* power on to power good time */ + +/* urb */ +#define N_URB_TD 48 +typedef struct { + ed_t *ed; + __u16 length; /* number of tds associated with this request */ + __u16 td_cnt; /* number of tds already serviced */ + int state; + unsigned long pipe; + int actual_length; + td_t *td[N_URB_TD]; /* list pointer to all corresponding TDs associated with this request */ +} urb_priv_t; +#define URB_DEL 1 + +/* + * This is the full ohci controller description + * + * Note how the "proper" USB information is just + * a subset of what the full implementation needs. (Linus) + */ + +typedef struct ohci { + struct ohci_hcca *hcca; /* hcca */ + /*dma_addr_t hcca_dma; */ + + int irq; + int disabled; /* e.g. got a UE, we're hung */ + int sleeping; + unsigned long flags; /* for HC bugs */ + + struct ohci_regs *regs; /* OHCI controller's memory */ + + ed_t *ed_rm_list[2]; /* lists of all endpoints to be removed */ + ed_t *ed_bulktail; /* last endpoint of bulk list */ + ed_t *ed_controltail; /* last endpoint of control list */ + int intrstatus; + __u32 hc_control; /* copy of the hc control reg */ + struct usb_device *dev[32]; + struct virt_root_hub rh; + + const char *slot_name; +} ohci_t; + +#define NUM_EDS 8 /* num of preallocated endpoint descriptors */ + +struct ohci_device { + ed_t ed[NUM_EDS]; + int ed_cnt; +}; + +/* hcd */ +/* endpoint */ +static int ep_link(ohci_t * ohci, ed_t * ed); +static int ep_unlink(ohci_t * ohci, ed_t * ed); +static ed_t *ep_add_ed(struct usb_device *usb_dev, unsigned long pipe); + +/*-------------------------------------------------------------------------*/ + +/* we need more TDs than EDs */ +#define NUM_TD 64 + +/* +1 so we can align the storage */ +td_t gtd[NUM_TD + 1]; +/* pointers to aligned storage */ +td_t *ptd; + +/* TDs ... */ +static inline struct td *td_alloc(struct usb_device *usb_dev) +{ + int i; + struct td *td; + + td = NULL; + for (i = 0; i < NUM_TD; i++) { + if (ptd[i].usb_dev == NULL) { + td = &ptd[i]; + td->usb_dev = usb_dev; + break; + } + } + + return td; +} + +static inline void ed_free(struct ed *ed) +{ + ed->usb_dev = NULL; +} diff --git a/arch/powerpc/cpu/ppc4xx/usbdev.c b/arch/powerpc/cpu/ppc4xx/usbdev.c new file mode 100644 index 0000000..fe398af --- /dev/null +++ b/arch/powerpc/cpu/ppc4xx/usbdev.c @@ -0,0 +1,230 @@ +/*USB 1.1,2.0 device*/ + +#include <common.h> +#include <asm/processor.h> + +#if (defined(CONFIG_440EP) || defined(CONFIG_440EPX)) && defined(CONFIG_CMD_USB) + +#include <usb.h> +#include <asm/ppc4xx-uic.h> +#include "usbdev.h" + +#define USB_DT_DEVICE 0x01 +#define USB_DT_CONFIG 0x02 +#define USB_DT_STRING 0x03 +#define USB_DT_INTERFACE 0x04 +#define USB_DT_ENDPOINT 0x05 + +int set_value = -1; + +void process_endpoints(unsigned short usb2d0_intrin) +{ + /*will hold the packet received */ + struct usb_device_descriptor usb_device_packet; + struct usb_configuration_descriptor usb_config_packet; + struct usb_string_descriptor usb_string_packet; + struct devrequest setup_packet; + unsigned int *setup_packet_pt; + unsigned char *packet_pt = NULL; + int temp, temp1; + + int i; + + /*printf("{USB device} - endpoint 0x%X \n", usb2d0_intrin); */ + + /*set usb address, seems to not work unless it is done in the next + interrupt, so that is why it is done this way */ + if (set_value != -1) + *(unsigned char *)USB2D0_FADDR_8 = (unsigned char)set_value; + + /*endpoint 1 */ + if (usb2d0_intrin & 0x01) { + setup_packet_pt = (unsigned int *)&setup_packet; + + /*copy packet */ + setup_packet_pt[0] = *(unsigned int *)USB2D0_FIFO_0; + setup_packet_pt[1] = *(unsigned int *)USB2D0_FIFO_0; + temp = *(unsigned int *)USB2D0_FIFO_0; + temp1 = *(unsigned int *)USB2D0_FIFO_0; + + /*do some swapping */ + setup_packet.value = swap_16(setup_packet.value); + setup_packet.index = swap_16(setup_packet.index); + setup_packet.length = swap_16(setup_packet.length); + + /*clear rx packet */ + *(unsigned short *)USB2D0_INCSR0_8 = 0x48; + + /*printf("0x%X 0x%X 0x%X 0x%X 0x%X 0x%X 0x%X\n", setup_packet.requesttype, + setup_packet.request, setup_packet.value, + setup_packet.index, setup_packet.length, temp, temp1 ); */ + + switch (setup_packet.request) { + case USB_REQ_GET_DESCRIPTOR: + + switch (setup_packet.value >> 8) { + case USB_DT_DEVICE: + /*create packet */ + usb_device_packet.bLength = 18; + usb_device_packet.bDescriptorType = + USB_DT_DEVICE; +#ifdef USB_2_0_DEVICE + usb_device_packet.bcdUSB = swap_16(0x200); +#else + usb_device_packet.bcdUSB = swap_16(0x110); +#endif + usb_device_packet.bDeviceClass = 0xff; + usb_device_packet.bDeviceSubClass = 0; + usb_device_packet.bDeviceProtocol = 0; + usb_device_packet.bMaxPacketSize0 = 32; + usb_device_packet.idVendor = swap_16(1); + usb_device_packet.idProduct = swap_16(2); + usb_device_packet.bcdDevice = swap_16(0x300); + usb_device_packet.iManufacturer = 1; + usb_device_packet.iProduct = 1; + usb_device_packet.iSerialNumber = 1; + usb_device_packet.bNumConfigurations = 1; + + /*put packet in fifo */ + packet_pt = (unsigned char *)&usb_device_packet; + break; + + case USB_DT_CONFIG: + /*create packet */ + usb_config_packet.bLength = 9; + usb_config_packet.bDescriptorType = + USB_DT_CONFIG; + usb_config_packet.wTotalLength = swap_16(25); + usb_config_packet.bNumInterfaces = 1; + usb_config_packet.bConfigurationValue = 1; + usb_config_packet.iConfiguration = 0; + usb_config_packet.bmAttributes = 0x40; + usb_config_packet.bMaxPower = 0; + + /*put packet in fifo */ + packet_pt = (unsigned char *)&usb_config_packet; + break; + + case USB_DT_STRING: + /*create packet */ + usb_string_packet.bLength = 2; + usb_string_packet.bDescriptorType = + USB_DT_STRING; + usb_string_packet.wData[0] = 0x0094; + + /*put packet in fifo */ + packet_pt = (unsigned char *)&usb_string_packet; + break; + } + + /*put packet in fifo */ + for (i = 0; i < (setup_packet.length); i++) { + *(unsigned char *)USB2D0_FIFO_0 = packet_pt[i]; + } + + /*give tx command */ + *(unsigned short *)USB2D0_INCSR0_8 = 0x0a; + + break; + + case USB_REQ_SET_ADDRESS: + + /*copy usb address */ + set_value = setup_packet.value; + + break; + } + + } +} + +void process_other(unsigned char usb2d0_intrusb) +{ + + /*check for sof */ + if (usb2d0_intrusb & 0x08) { + /*printf("{USB device} - sof detected\n"); */ + } + + /*check for reset */ + if (usb2d0_intrusb & 0x04) { + /*printf("{USB device} - reset detected\n"); */ + + /*copy usb address of zero, need to do this when usb reset */ + set_value = 0; + } + + if (usb2d0_intrusb & 0x02) { + /*printf("{USB device} - resume detected\n"); */ + } + + if (usb2d0_intrusb & 0x01) { + /*printf("{USB device} - suspend detected\n"); */ + } +} + +int usbInt(void) +{ + /*Must read these 2 registers and use values to clear interrupts. If you + do not read them then the interrupt will not be cleared. If you do not + use the variable the optimizer will not do a read. */ + volatile unsigned short usb2d0_intrin = + *(unsigned short *)USB2D0_INTRIN_16; + volatile unsigned char usb2d0_intrusb = + *(unsigned char *)USB2D0_INTRUSB_8; + + /*check if there was an endpoint interrupt */ + if (usb2d0_intrin != 0) { + process_endpoints(usb2d0_intrin); + } + + /*check for other interrupts */ + if (usb2d0_intrusb != 0) { + process_other(usb2d0_intrusb); + } + + return 0; +} + +#if defined(CONFIG_440EPX) +void usb_dev_init() +{ + printf("USB 2.0 Device init\n"); + + /*usb dev init */ + *(unsigned char *)USB2D0_POWER_8 = 0xa1; /* 2.0 */ + + /*enable interrupts */ + *(unsigned char *)USB2D0_INTRUSBE_8 = 0x0f; + + irq_install_handler(VECNUM_USBDEV, (interrupt_handler_t *) usbInt, + NULL); +} +#else +void usb_dev_init() +{ +#ifdef USB_2_0_DEVICE + printf("USB 2.0 Device init\n"); + /*select 2.0 device */ + mtsdr(SDR0_USB0, 0x0); /* 2.0 */ + + /*usb dev init */ + *(unsigned char *)USB2D0_POWER_8 = 0xa1; /* 2.0 */ +#else + printf("USB 1.1 Device init\n"); + /*select 1.1 device */ + mtsdr(SDR0_USB0, 0x2); /* 1.1 */ + + /*usb dev init */ + *(unsigned char *)USB2D0_POWER_8 = 0xc0; /* 1.1 */ +#endif + + /*enable interrupts */ + *(unsigned char *)USB2D0_INTRUSBE_8 = 0x0f; + + irq_install_handler(VECNUM_USBDEV, (interrupt_handler_t *) usbInt, + NULL); +} +#endif + +#endif /* CONFIG_440EP || CONFIG_440EPX */ diff --git a/arch/powerpc/cpu/ppc4xx/usbdev.h b/arch/powerpc/cpu/ppc4xx/usbdev.h new file mode 100644 index 0000000..ef6a2da --- /dev/null +++ b/arch/powerpc/cpu/ppc4xx/usbdev.h @@ -0,0 +1,31 @@ +#include <config.h> + +/*Common Registers*/ +#define USB2D0_INTRIN_16 (CONFIG_SYS_USB_DEVICE | 0x100) +#define USB2D0_POWER_8 (CONFIG_SYS_USB_DEVICE | 0x102) +#define USB2D0_FADDR_8 (CONFIG_SYS_USB_DEVICE | 0x103) +#define USB2D0_INTRINE_16 (CONFIG_SYS_USB_DEVICE | 0x104) +#define USB2D0_INTROUT_16 (CONFIG_SYS_USB_DEVICE | 0x106) +#define USB2D0_INTRUSBE_8 (CONFIG_SYS_USB_DEVICE | 0x108) +#define USB2D0_INTRUSB_8 (CONFIG_SYS_USB_DEVICE | 0x109) +#define USB2D0_INTROUTE_16 (CONFIG_SYS_USB_DEVICE | 0x10a) +#define USB2D0_TSTMODE_8 (CONFIG_SYS_USB_DEVICE | 0x10c) +#define USB2D0_INDEX_8 (CONFIG_SYS_USB_DEVICE | 0x10d) +#define USB2D0_FRAME_16 (CONFIG_SYS_USB_DEVICE | 0x10e) + +/*Indexed Registers*/ +#define USB2D0_INCSR0_8 (CONFIG_SYS_USB_DEVICE | 0x110) +#define USB2D0_INCSR_16 (CONFIG_SYS_USB_DEVICE | 0x110) +#define USB2D0_INMAXP_16 (CONFIG_SYS_USB_DEVICE | 0x112) +#define USB2D0_OUTCSR_16 (CONFIG_SYS_USB_DEVICE | 0x114) +#define USB2D0_OUTMAXP_16 (CONFIG_SYS_USB_DEVICE | 0x116) +#define USB2D0_OUTCOUNT0_8 (CONFIG_SYS_USB_DEVICE | 0x11a) +#define USB2D0_OUTCOUNT_16 (CONFIG_SYS_USB_DEVICE | 0x11a) + +/*FIFOs*/ +#define USB2D0_FIFO_0 (CONFIG_SYS_USB_DEVICE | 0x120) +#define USB2D0_FIFO_1 (CONFIG_SYS_USB_DEVICE | 0x124) +#define USB2D0_FIFO_2 (CONFIG_SYS_USB_DEVICE | 0x128) +#define USB2D0_FIFO_3 (CONFIG_SYS_USB_DEVICE | 0x12c) + +void usb_dev_init(void); diff --git a/arch/powerpc/cpu/ppc4xx/xilinx_irq.c b/arch/powerpc/cpu/ppc4xx/xilinx_irq.c new file mode 100644 index 0000000..7108777 --- /dev/null +++ b/arch/powerpc/cpu/ppc4xx/xilinx_irq.c @@ -0,0 +1,100 @@ +/* + * (C) Copyright 2008 + * Ricado Ribalda-Universidad Autonoma de Madrid-ricardo.ribalda@uam.es + * This work has been supported by: QTechnology http://qtec.com/ + * Based on interrupts.c Wolfgang Denk-DENX Software Engineering-wd@denx.de + * 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, see <http://www.gnu.org/licenses/>. +*/ +#include <common.h> +#include <watchdog.h> +#include <command.h> +#include <asm/processor.h> +#include <asm/interrupt.h> +#include <ppc4xx.h> +#include <ppc_asm.tmpl> +#include <commproc.h> +#include <asm/io.h> +#include <asm/xilinx_irq.h> + +DECLARE_GLOBAL_DATA_PTR; + +void pic_enable(void) +{ + debug("Xilinx PIC at 0x%8x\n", intc); + + /* + * Disable all external interrupts until they are + * explicitly requested. + */ + out_be32((u32 *) IER, 0); + + /* Acknowledge any pending interrupts just in case. */ + out_be32((u32 *) IAR, 0xffffffff); + + /* Turn on the Master Enable. */ + out_be32((u32 *) MER, 0x3UL); + + return; +} + +int xilinx_pic_irq_get(void) +{ + u32 irq; + irq = in_be32((u32 *) IVR); + + /* If no interrupt is pending then all bits of the IVR are set to 1. As + * the IVR is as many bits wide as numbers of inputs are available. + * Therefore, if all bits of the IVR are set to one, its content will + * be bigger than XPAR_INTC_MAX_NUM_INTR_INPUTS. + */ + if (irq >= XPAR_INTC_MAX_NUM_INTR_INPUTS) + irq = -1; /* report no pending interrupt. */ + + debug("get_irq: %d\n", irq); + return (irq); +} + +void pic_irq_enable(unsigned int irq) +{ + u32 mask = IRQ_MASK(irq); + debug("enable: %d\n", irq); + out_be32((u32 *) SIE, mask); +} + +void pic_irq_disable(unsigned int irq) +{ + u32 mask = IRQ_MASK(irq); + debug("disable: %d\n", irq); + out_be32((u32 *) CIE, mask); +} + +void pic_irq_ack(unsigned int irq) +{ + u32 mask = IRQ_MASK(irq); + debug("ack: %d\n", irq); + out_be32((u32 *) IAR, mask); +} + +void external_interrupt(struct pt_regs *regs) +{ + int irq; + + irq = xilinx_pic_irq_get(); + if (irq < 0) + return; + + interrupt_run_handler(irq); + + return; +} |