diff options
Diffstat (limited to 'cpu/mpc85xx')
-rwxr-xr-x | cpu/mpc85xx/Makefile | 45 | ||||
-rwxr-xr-x | cpu/mpc85xx/commproc.c | 214 | ||||
-rwxr-xr-x | cpu/mpc85xx/config.mk | 26 | ||||
-rwxr-xr-x | cpu/mpc85xx/cpu.c | 229 | ||||
-rwxr-xr-x | cpu/mpc85xx/cpu_init.c | 236 | ||||
-rwxr-xr-x | cpu/mpc85xx/ether_fcc.c | 473 | ||||
-rwxr-xr-x | cpu/mpc85xx/i2c.c | 265 | ||||
-rwxr-xr-x | cpu/mpc85xx/interrupts.c | 162 | ||||
-rwxr-xr-x | cpu/mpc85xx/pci.c | 122 | ||||
-rwxr-xr-x | cpu/mpc85xx/resetvec.S | 2 | ||||
-rwxr-xr-x | cpu/mpc85xx/serial_scc.c | 274 | ||||
-rwxr-xr-x | cpu/mpc85xx/spd_sdram.c | 1118 | ||||
-rwxr-xr-x | cpu/mpc85xx/speed.c | 123 | ||||
-rwxr-xr-x | cpu/mpc85xx/start.S | 1157 | ||||
-rwxr-xr-x | cpu/mpc85xx/traps.c | 273 |
15 files changed, 4719 insertions, 0 deletions
diff --git a/cpu/mpc85xx/Makefile b/cpu/mpc85xx/Makefile new file mode 100755 index 0000000..5298dc1 --- /dev/null +++ b/cpu/mpc85xx/Makefile @@ -0,0 +1,45 @@ +# +# (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 = lib$(CPU).a + +START = start.o resetvec.o +COBJS = traps.o cpu.o cpu_init.o speed.o interrupts.o \ + pci.o serial_scc.o commproc.o ether_fcc.o i2c.o spd_sdram.o +OBJS = $(COBJS) + +all: .depend $(START) $(LIB) + +$(LIB): $(OBJS) + $(AR) crv $@ $(OBJS) + +######################################################################### + +.depend: Makefile $(START:.o=.S) $(AOBJS:.o=.S) $(COBJS:.o=.c) + $(CC) -M $(CFLAGS) $(START:.o=.S) $(AOBJS:.o=.S) $(COBJS:.o=.c) > $@ + +sinclude .depend + +######################################################################### diff --git a/cpu/mpc85xx/commproc.c b/cpu/mpc85xx/commproc.c new file mode 100755 index 0000000..aa8a5a5 --- /dev/null +++ b/cpu/mpc85xx/commproc.c @@ -0,0 +1,214 @@ +/* + * Adapted for Motorola MPC8560 chips + * Xianghua Xiao <x.xiao@motorola.com> + * + * This file is based on "arch/ppc/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> + +#if defined(CONFIG_CPM2) +/* + * 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) +{ + DECLARE_GLOBAL_DATA_PTR; + + volatile immap_t *immr = (immap_t *)CFG_IMMR; + volatile ulong count; + + gd = (gd_t *) (CFG_INIT_RAM_ADDR + CFG_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 + */ + immr->im_cpm.im_cpm_cp.cpcr = CPM_CR_RST; + count = 0; + do { /* Spin until command processed */ + __asm__ __volatile__ ("eieio"); + } while ((immr->im_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) +{ + DECLARE_GLOBAL_DATA_PTR; + + volatile immap_t *immr = (immap_t *)CFG_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("m8560_cpm_dpalloc: ran out of dual port ram!"); + } + + retloc = gd->dp_alloc_base; + gd->dp_alloc_base += size; + + memset((void *)&(immr->im_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) +{ + DECLARE_GLOBAL_DATA_PTR; + + volatile immap_t *immr = (immap_t *)CFG_IMMR; + volatile uint *bp; + + /* This is good enough to get SMCs running..... + */ + if (brg < 4) { + bp = (uint *)&(immr->im_cpm.im_cpm_brg1.brgc1); + } + else { + bp = (uint *)&(immr->im_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) +{ + DECLARE_GLOBAL_DATA_PTR; + + volatile immap_t *immr = (immap_t *)CFG_IMMR; + volatile uint *bp; + + /* This is good enough to get SMCs running..... + */ + if (brg < 4) { + bp = (uint *)&(immr->im_cpm.im_cpm_brg1.brgc1); + } + else { + bp = (uint *)&(immr->im_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 immap_t *immr = (immap_t *)CFG_IMMR; + volatile uint *bp; + + if (brg < 4) { + bp = (uint *)&(immr->im_cpm.im_cpm_brg1.brgc1); + } + else { + bp = (uint *)&(immr->im_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 *)(CFG_IMMR + CPM_POST_WORD_ADDR); + + *save_addr = a; +} + +ulong post_word_load (void) +{ + volatile ulong *save_addr = + (volatile ulong *)(CFG_IMMR + CPM_POST_WORD_ADDR); + + return *save_addr; +} + +#endif /* CONFIG_POST */ + +#endif /* CONFIG_CPM2 */ diff --git a/cpu/mpc85xx/config.mk b/cpu/mpc85xx/config.mk new file mode 100755 index 0000000..6121074 --- /dev/null +++ b/cpu/mpc85xx/config.mk @@ -0,0 +1,26 @@ +# +# (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 -ffixed-r14 -meabi + +PLATFORM_CPPFLAGS += -DCONFIG_MPC85xx -DCONFIG_E500 -ffixed-r2 -ffixed-r29 -Wa,-me500 -msoft-float -mno-string diff --git a/cpu/mpc85xx/cpu.c b/cpu/mpc85xx/cpu.c new file mode 100755 index 0000000..f7fe22e --- /dev/null +++ b/cpu/mpc85xx/cpu.c @@ -0,0 +1,229 @@ +/* + * Copyright 2004 Freescale Semiconductor. + * (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 <common.h> +#include <watchdog.h> +#include <command.h> +#include <asm/cache.h> + +/* ------------------------------------------------------------------------- */ + +int checkcpu (void) +{ + sys_info_t sysinfo; + uint lcrr; /* local bus clock ratio register */ + uint clkdiv; /* clock divider portion of lcrr */ + uint pvr, svr; + uint fam; + uint ver; + uint major, minor; + + svr = get_svr(); + ver = SVR_VER(svr); + major = SVR_MAJ(svr); + minor = SVR_MIN(svr); + + puts("CPU: "); + switch (ver) { + case SVR_8540: + puts("8540"); + break; + case SVR_8541: + puts("8541"); + break; + case SVR_8555: + puts("8555"); + break; + case SVR_8560: + puts("8560"); + break; + case SVR_8548: + puts("8548"); + break; + case SVR_8548_E: + puts("8548_E"); + break; + default: + puts("Unknown"); + break; + } + 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; + } + printf(", Version: %d.%d, (0x%08x)\n", major, minor, pvr); + + get_sys_info(&sysinfo); + + puts("Clock Configuration:\n"); + printf(" CPU:%4lu MHz, ", sysinfo.freqProcessor / 1000000); + printf("CCB:%4lu MHz,\n", sysinfo.freqSystemBus / 1000000); + printf(" DDR:%4lu MHz, ", sysinfo.freqSystemBus / 2000000); + +#if defined(CFG_LBC_LCRR) + lcrr = CFG_LBC_LCRR; +#else + { + volatile immap_t *immap = (immap_t *)CFG_IMMR; + volatile ccsr_lbc_t *lbc= &immap->im_lbc; + + lcrr = lbc->lcrr; + } +#endif + clkdiv = lcrr & 0x0f; + if (clkdiv == 2 || clkdiv == 4 || clkdiv == 8) { +#ifdef CONFIG_MPC8548 + /* + * Yes, the entire PQ38 family use the same + * bit-representation for twice the clock divider values. + */ + clkdiv *= 2; +#endif + printf("LBC:%4lu MHz\n", + sysinfo.freqSystemBus / 1000000 / clkdiv); + } else { + printf("LBC: unknown (lcrr: 0x%08x)\n", lcrr); + } + + if (ver == SVR_8560) { + printf("CPM: %lu Mhz\n", + sysinfo.freqSystemBus / 1000000); + } + + 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[]) +{ + /* + * Initiate hard reset in debug control register DBCR0 + * Make sure MSR[DE] = 1 + */ + unsigned long val; + + val = mfspr(DBCR0); + val |= 0x70000000; + mtspr(DBCR0,val); + + return 1; +} + + +/* + * Get timebase clock frequency + */ +unsigned long get_tbclk (void) +{ + + sys_info_t sys_info; + + get_sys_info(&sys_info); + return ((sys_info.freqSystemBus + 7L) / 8L); +} + + +#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(tsr); + val |= 0x40000000; + mtspr(tsr, val); +} +#endif /* CONFIG_WATCHDOG */ + +#if defined(CONFIG_DDR_ECC) +void dma_init(void) { + volatile immap_t *immap = (immap_t *)CFG_IMMR; + volatile ccsr_dma_t *dma = &immap->im_dma; + + dma->satr0 = 0x02c40000; + dma->datr0 = 0x02c40000; + asm("sync; isync; msync"); + return; +} + +uint dma_check(void) { + volatile immap_t *immap = (immap_t *)CFG_IMMR; + volatile ccsr_dma_t *dma = &immap->im_dma; + volatile uint status = dma->sr0; + + /* While the channel is busy, spin */ + while((status & 4) == 4) { + status = dma->sr0; + } + + if (status != 0) { + printf ("DMA Error: status = %x\n", status); + } + return status; +} + +int dma_xfer(void *dest, uint count, void *src) { + volatile immap_t *immap = (immap_t *)CFG_IMMR; + volatile ccsr_dma_t *dma = &immap->im_dma; + + dma->dar0 = (uint) dest; + dma->sar0 = (uint) src; + dma->bcr0 = count; + dma->mr0 = 0xf000004; + asm("sync;isync;msync"); + dma->mr0 = 0xf000005; + asm("sync;isync;msync"); + return dma_check(); +} +#endif diff --git a/cpu/mpc85xx/cpu_init.c b/cpu/mpc85xx/cpu_init.c new file mode 100755 index 0000000..efde9cc --- /dev/null +++ b/cpu/mpc85xx/cpu_init.c @@ -0,0 +1,236 @@ +/* + * (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> + +#ifdef CONFIG_CPM2 +static void config_8560_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; + } + } +} +#endif + +/* + * Breathe some life into the CPU... + * + * Set up the memory map + * initialize a bunch of registers + */ + +void cpu_init_f (void) +{ + DECLARE_GLOBAL_DATA_PTR; + volatile immap_t *immap = (immap_t *)CFG_IMMR; + volatile ccsr_lbc_t *memctl = &immap->im_lbc; + extern void m8560_cpm_reset (void); + + /* Pointer is writable since we allocated a register for it */ + gd = (gd_t *) (CFG_INIT_RAM_ADDR + CFG_GBL_DATA_OFFSET); + + /* Clear initial global data */ + memset ((void *) gd, 0, sizeof (gd_t)); + + +#ifdef CONFIG_CPM2 + config_8560_ioports(immap); +#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(CFG_OR0_REMAP) + memctl->or0 = CFG_OR0_REMAP; +#endif +#if defined(CFG_OR1_REMAP) + memctl->or1 = CFG_OR1_REMAP; +#endif + + /* now restrict to preliminary range */ +#if defined(CFG_BR0_PRELIM) && defined(CFG_OR0_PRELIM) + memctl->br0 = CFG_BR0_PRELIM; + memctl->or0 = CFG_OR0_PRELIM; +#endif + +#if defined(CFG_BR1_PRELIM) && defined(CFG_OR1_PRELIM) + memctl->or1 = CFG_OR1_PRELIM; + memctl->br1 = CFG_BR1_PRELIM; +#endif + +#if !defined(CONFIG_MPC85xx) +#if defined(CFG_BR2_PRELIM) && defined(CFG_OR2_PRELIM) + memctl->or2 = CFG_OR2_PRELIM; + memctl->br2 = CFG_BR2_PRELIM; +#endif +#endif + +#if defined(CFG_BR3_PRELIM) && defined(CFG_OR3_PRELIM) + memctl->or3 = CFG_OR3_PRELIM; + memctl->br3 = CFG_BR3_PRELIM; +#endif + +#if defined(CFG_BR4_PRELIM) && defined(CFG_OR4_PRELIM) + memctl->or4 = CFG_OR4_PRELIM; + memctl->br4 = CFG_BR4_PRELIM; +#endif + +#if defined(CFG_BR5_PRELIM) && defined(CFG_OR5_PRELIM) + memctl->or5 = CFG_OR5_PRELIM; + memctl->br5 = CFG_BR5_PRELIM; +#endif + +#if defined(CFG_BR6_PRELIM) && defined(CFG_OR6_PRELIM) + memctl->or6 = CFG_OR6_PRELIM; + memctl->br6 = CFG_BR6_PRELIM; +#endif + +#if defined(CFG_BR7_PRELIM) && defined(CFG_OR7_PRELIM) + memctl->or7 = CFG_OR7_PRELIM; + memctl->br7 = CFG_BR7_PRELIM; +#endif + +#if defined(CONFIG_CPM2) + m8560_cpm_reset(); +#endif +} + + +/* + * 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. + * + * FIXME: Use PVR_VER(pvr) == 1 test here instead of SVR_VER()? + */ + +int cpu_init_r(void) +{ +#if defined(CONFIG_L2_CACHE) + volatile immap_t *immap = (immap_t *)CFG_IMMR; + volatile ccsr_l2cache_t *l2cache = &immap->im_l2cache; + volatile uint cache_ctl; + uint svr, ver; + + svr = get_svr(); + ver = SVR_VER(svr); + + asm("msync;isync"); + cache_ctl = l2cache->l2ctl; + + switch (cache_ctl & 0x30000000) { + case 0x20000000: + if (ver == SVR_8548 || ver == SVR_8548_E) { + printf ("L2 cache 512KB:"); + } else { + printf ("L2 cache 256KB:"); + } + break; + case 0x00000000: + case 0x10000000: + case 0x30000000: + default: + printf ("L2 cache unknown size (0x%08x)\n", cache_ctl); + return -1; + } + + asm("msync;isync"); + l2cache->l2ctl = 0x68000000; /* invalidate */ + cache_ctl = l2cache->l2ctl; + asm("msync;isync"); + + l2cache->l2ctl = 0xa8000000; /* enable 256KB L2 cache */ + cache_ctl = l2cache->l2ctl; + asm("msync;isync"); + + printf(" enabled\n"); +#else + printf("L2 cache: disabled\n"); +#endif + + return 0; +} diff --git a/cpu/mpc85xx/ether_fcc.c b/cpu/mpc85xx/ether_fcc.c new file mode 100755 index 0000000..d15d242 --- /dev/null +++ b/cpu/mpc85xx/ether_fcc.c @@ -0,0 +1,473 @@ +/* + * 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) || (CONFIG_COMMANDS & CFG_CMD_MII) +#include <miiphy.h> +#endif + +#if defined(CONFIG_CPM2) + +#if defined(CONFIG_ETHER_ON_FCC) && (CONFIG_COMMANDS & CFG_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, + CFG_CMXFCR_MASK1, + CFG_CMXFCR_VALUE1 +}, +#endif + +#ifdef CONFIG_ETHER_ON_FCC2 +{ + 1, + PROFF_FCC2, + CPM_CR_FCC2_SBLOCK, + CPM_CR_FCC2_PAGE, + CFG_CMXFCR_MASK2, + CFG_CMXFCR_VALUE2 +}, +#endif + +#ifdef CONFIG_ETHER_ON_FCC3 +{ + 2, + PROFF_FCC3, + CPM_CR_FCC3_SBLOCK, + CPM_CR_FCC3_PAGE, + CFG_CMXFCR_MASK3, + CFG_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 immap_t *immr = (immap_t *)CFG_IMMR; + volatile ccsr_cpm_cp_t *cp = &(immr->im_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 */ + immr->im_cpm.im_cpm_mux.cmxuar = 0; /* ATM */ + immr->im_cpm.im_cpm_mux.cmxfcr = (immr->im_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) { + immr->im_cpm.im_cpm_fcc1.gfmr = FCC_GFMR_MODE_ENET | FCC_GFMR_TCRC_32; + } else if (info->ether_index == 1) { + immr->im_cpm.im_cpm_fcc2.gfmr = FCC_GFMR_MODE_ENET | FCC_GFMR_TCRC_32; + } else if (info->ether_index == 2) { + immr->im_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) { + immr->im_cpm.im_cpm_fcc1.fpsmr = CFG_FCC_PSMR | FCC_PSMR_ENCRC; + } else if (info->ether_index == 1){ + immr->im_cpm.im_cpm_fcc2.fpsmr = CFG_FCC_PSMR | FCC_PSMR_ENCRC; + } else if (info->ether_index == 2){ + immr->im_cpm.im_cpm_fcc3.fpsmr = CFG_FCC_PSMR | FCC_PSMR_ENCRC; + } + + /* 28.9 - (6): FDSR: Ethernet Syn */ + if(info->ether_index == 0) { + immr->im_cpm.im_cpm_fcc1.fdsr = 0xD555; + } else if (info->ether_index == 1) { + immr->im_cpm.im_cpm_fcc2.fdsr = 0xD555; + } else if (info->ether_index == 2) { + immr->im_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 *)&(immr->im_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 | + CFG_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 | + CFG_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) { + immr->im_cpm.im_cpm_fcc1.fcce = ~0x0; + immr->im_cpm.im_cpm_fcc1.fccm = 0; + } else if (info->ether_index == 1) { + immr->im_cpm.im_cpm_fcc2.fcce = ~0x0; + immr->im_cpm.im_cpm_fcc2.fccm = 0; + } else if (info->ether_index == 2) { + immr->im_cpm.im_cpm_fcc3.fcce = ~0x0; + immr->im_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) { + immr->im_cpm.im_cpm_fcc1.gfmr |= FCC_GFMR_ENT | FCC_GFMR_ENR; + } else if (info->ether_index == 1) { + immr->im_cpm.im_cpm_fcc2.gfmr |= FCC_GFMR_ENT | FCC_GFMR_ENR; + } else if (info->ether_index == 2) { + immr->im_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 immap_t *immr = (immap_t *)CFG_IMMR; + + /* write GFMR: disable tx/rx */ + if(info->ether_index == 0) { + immr->im_cpm.im_cpm_fcc1.gfmr &= ~(FCC_GFMR_ENT | FCC_GFMR_ENR); + } else if(info->ether_index == 1) { + immr->im_cpm.im_cpm_fcc2.gfmr &= ~(FCC_GFMR_ENT | FCC_GFMR_ENR); + } else if(info->ether_index == 2) { + immr->im_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) || (CONFIG_COMMANDS & CFG_CMD_MII)) \ + && defined(CONFIG_BITBANGMII) + miiphy_register(dev->name, + bb_miiphy_read, bb_miiphy_write); +#endif + } + + return 1; +} + +#endif /* CONFIG_ETHER_ON_FCC && CFG_CMD_NET && CONFIG_NET_MULTI */ + +#endif /* CONFIG_CPM2 */ diff --git a/cpu/mpc85xx/i2c.c b/cpu/mpc85xx/i2c.c new file mode 100755 index 0000000..32dcf5d --- /dev/null +++ b/cpu/mpc85xx/i2c.c @@ -0,0 +1,265 @@ +/* + * (C) Copyright 2003,Motorola Inc. + * Xianghua Xiao <x.xiao@motorola.com> + * Adapted for Motorola 85xx chip. + * + * (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> +#include <command.h> +#include <asm/io.h> + +#ifdef CONFIG_HARD_I2C +#include <i2c.h> + +#define TIMEOUT (CFG_HZ/4) + +#define I2C_Addr ((u8 *)(CFG_CCSRBAR + 0x3000)) + +#define I2CADR &I2C_Addr[0] +#define I2CFDR &I2C_Addr[4] +#define I2CCCR &I2C_Addr[8] +#define I2CCSR &I2C_Addr[12] +#define I2CCDR &I2C_Addr[16] +#define I2CDFSRR &I2C_Addr[20] + +#define I2C_READ 1 +#define I2C_WRITE 0 + +void +i2c_init(int speed, int slaveadd) +{ + /* stop I2C controller */ + writeb(0x0, I2CCCR); + + /* set clock */ + writeb(0x3f, I2CFDR); + + /* set default filter */ + writeb(0x10,I2CDFSRR); + + /* write slave address */ + writeb(slaveadd, I2CADR); + + /* clear status register */ + writeb(0x0, I2CCSR); + + /* start I2C controller */ + writeb(MPC85xx_I2CCR_MEN, I2CCCR); +} + +static __inline__ int +i2c_wait4bus (void) +{ + ulong timeval = get_timer (0); + + while (readb(I2CCSR) & MPC85xx_I2CSR_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 = readb(I2CCSR); + + if (!(csr & MPC85xx_I2CSR_MIF)) + continue; + + writeb(0x0, I2CCSR); + + if (csr & MPC85xx_I2CSR_MAL) { + debug("i2c_wait: MAL\n"); + return -1; + } + + if (!(csr & MPC85xx_I2CSR_MCF)) { + debug("i2c_wait: unfinished\n"); + return -1; + } + + if (write == I2C_WRITE && (csr & MPC85xx_I2CSR_RXAK)) { + debug("i2c_wait: No RXACK\n"); + return -1; + } + + return 0; + } while (get_timer (timeval) < TIMEOUT); + + debug("i2c_wait: timed out\n"); + return -1; +} + +static __inline__ int +i2c_write_addr (u8 dev, u8 dir, int rsta) +{ + writeb(MPC85xx_I2CCR_MEN | MPC85xx_I2CCR_MSTA | MPC85xx_I2CCR_MTX | + (rsta?MPC85xx_I2CCR_RSTA:0), + I2CCCR); + + writeb((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; + + writeb(MPC85xx_I2CCR_MEN | MPC85xx_I2CCR_MSTA | MPC85xx_I2CCR_MTX, + I2CCCR); + + for (i=0; i < length; i++) { + writeb(data[i], I2CCDR); + + if (i2c_wait (I2C_WRITE) < 0) + break; + } + + return i; +} + +static __inline__ int +__i2c_read (u8 *data, int length) +{ + int i; + + writeb(MPC85xx_I2CCR_MEN | MPC85xx_I2CCR_MSTA | + ((length == 1) ? MPC85xx_I2CCR_TXAK : 0), + I2CCCR); + + /* dummy read */ + readb(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) + writeb(MPC85xx_I2CCR_MEN | MPC85xx_I2CCR_MSTA | + MPC85xx_I2CCR_TXAK, + I2CCCR); + + /* Generate stop on last byte */ + if (i == length - 1) + writeb(MPC85xx_I2CCR_MEN | MPC85xx_I2CCR_TXAK, I2CCCR); + + data[i] = readb(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: + writeb(MPC85xx_I2CCR_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: + writeb(MPC85xx_I2CCR_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); +} + +uchar i2c_reg_read (uchar i2c_addr, uchar reg) +{ + uchar buf[1]; + + i2c_read (i2c_addr, reg, 1, buf, 1); + + return (buf[0]); +} + +void i2c_reg_write (uchar i2c_addr, uchar reg, uchar val) +{ + i2c_write (i2c_addr, reg, 1, &val, 1); +} + +#endif /* CONFIG_HARD_I2C */ diff --git a/cpu/mpc85xx/interrupts.c b/cpu/mpc85xx/interrupts.c new file mode 100755 index 0000000..832781b --- /dev/null +++ b/cpu/mpc85xx/interrupts.c @@ -0,0 +1,162 @@ +/* + * (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 <ppc_asm.tmpl> + +unsigned decrementer_count; /* count value for 1e6/HZ microseconds */ + +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)); + asm volatile("isync"); +} + +static __inline__ unsigned long get_dec (void) +{ + unsigned long val; + + asm volatile ("mfdec %0":"=r" (val):); + + return val; +} + + +static __inline__ void set_dec (unsigned long val) +{ + if (val) + asm volatile ("mtdec %0"::"r" (val)); +} + +void enable_interrupts (void) +{ + set_msr (get_msr() | MSR_EE); +} + +/* returns flag if MSR_EE was set before */ +int disable_interrupts (void) +{ + ulong msr = get_msr(); + set_msr (msr & ~MSR_EE); + return ((msr & MSR_EE) != 0); +} + +int interrupt_init (void) +{ + volatile immap_t *immr = (immap_t *)CFG_IMMR; + + immr->im_pic.gcr = MPC85xx_PICGCR_RST; + while (immr->im_pic.gcr & MPC85xx_PICGCR_RST); + immr->im_pic.gcr = MPC85xx_PICGCR_M; + decrementer_count = get_tbclk() / CFG_HZ; + mtspr(SPRN_TCR, TCR_PIE); + set_dec (decrementer_count); + set_msr (get_msr () | MSR_EE); + 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; +} + +/****************************************************************************/ + + +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(struct pt_regs *regs) +{ + timestamp++; + set_dec (decrementer_count); + mtspr(SPRN_TSR, TSR_PIS); +#if defined(CONFIG_WATCHDOG) + if ((timestamp % 1000) == 0) + reset_85xx_watchdog(); +#endif /* CONFIG_WATCHDOG */ +} + +void reset_timer (void) +{ + timestamp = 0; +} + +ulong get_timer (ulong base) +{ + return (timestamp - base); +} + +void set_timer (ulong t) +{ + timestamp = t; +} + +#if (CONFIG_COMMANDS & CFG_CMD_IRQ) + +/******************************************************************************* + * + * irqinfo - print information about PCI devices,not implemented. + * + */ +int +do_irqinfo(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]) +{ + printf ("\nInterrupt-unsupported:\n"); + + return 0; +} + +#endif /* CONFIG_COMMANDS & CFG_CMD_IRQ */ diff --git a/cpu/mpc85xx/pci.c b/cpu/mpc85xx/pci.c new file mode 100755 index 0000000..a94493e --- /dev/null +++ b/cpu/mpc85xx/pci.c @@ -0,0 +1,122 @@ +/* + * 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_PCI) + +void +pci_mpc85xx_init(struct pci_controller *hose) +{ + volatile immap_t *immap = (immap_t *)CFG_CCSRBAR; + volatile ccsr_pcix_t *pcix = &immap->im_pcix; + + u16 reg16; + + hose->first_busno = 0; + hose->last_busno = 0xff; + + pci_set_region(hose->regions + 0, + CFG_PCI1_MEM_BASE, + CFG_PCI1_MEM_PHYS, + CFG_PCI1_MEM_SIZE, + PCI_REGION_MEM); + + pci_set_region(hose->regions + 1, + CFG_PCI1_IO_BASE, + CFG_PCI1_IO_PHYS, + CFG_PCI1_IO_SIZE, + PCI_REGION_IO); + + hose->region_count = 2; + + pci_setup_indirect(hose, + (CFG_IMMR+0x8000), + (CFG_IMMR+0x8004)); + + pcix->potar1 = (CFG_PCI1_MEM_BASE >> 12) & 0x000fffff; + pcix->potear1 = 0x00000000; + pcix->powbar1 = (CFG_PCI1_MEM_BASE >> 12) & 0x000fffff; + pcix->powbear1 = 0x00000000; + pcix->powar1 = 0x8004401c; /* 512M MEM space */ + + pcix->potar2 = 0x00000000; + pcix->potear2 = 0x00000000; + pcix->powbar2 = (CFG_PCI1_IO_BASE >> 12) & 0x000fffff; + pcix->powbear2 = 0x00000000; + pcix->powar2 = 0x80088017; /* 16M IO space */ + + pcix->pitar1 = 0x00000000; + pcix->piwbar1 = 0x00000000; + pcix->piwar1 = 0xa0f5501e; /* Enable, Prefetch, Local Mem, + * Snoop R/W, 2G */ + + /* + * Hose scan. + */ + pci_register_hose(hose); + + pci_read_config_word (PCI_BDF(0,0,0), PCI_COMMAND, ®16); + reg16 |= PCI_COMMAND_SERR | PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY; + pci_write_config_word(PCI_BDF(0,0,0), PCI_COMMAND, reg16); + + /* + * Clear non-reserved bits in status register. + */ + pci_write_config_word(PCI_BDF(0,0,0), PCI_STATUS, 0xffff); + pci_write_config_byte(PCI_BDF(0,0,0), PCI_LATENCY_TIMER,0x80); + +#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,17,0), + PCI_HEADER_TYPE, + &header_type); + } +#endif + + hose->last_busno = pci_hose_scan(hose); +} + +#endif /* CONFIG_PCI */ diff --git a/cpu/mpc85xx/resetvec.S b/cpu/mpc85xx/resetvec.S new file mode 100755 index 0000000..29555d4 --- /dev/null +++ b/cpu/mpc85xx/resetvec.S @@ -0,0 +1,2 @@ + .section .resetvec,"ax" + b _start_e500 diff --git a/cpu/mpc85xx/serial_scc.c b/cpu/mpc85xx/serial_scc.c new file mode 100755 index 0000000..cf060d6 --- /dev/null +++ b/cpu/mpc85xx/serial_scc.c @@ -0,0 +1,274 @@ +/* + * (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> + +#if defined(CONFIG_CPM2) +#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 *)CFG_IMMR; + volatile ccsr_cpm_scc_t *sp; + volatile scc_uart_t *up; + volatile cbd_t *tbdf, *rbdf; + volatile ccsr_cpm_cp_t *cp = &(im->im_cpm.im_cpm_cp); + uint dpaddr; + + /* initialize pointers to SCC */ + + sp = (ccsr_cpm_scc_t *) &(im->im_cpm.im_cpm_scc[SCC_INDEX]); + up = (scc_uart_t *)&(im->im_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). + */ + im->im_cpm.im_cpm_mux.cmxscr = \ + (im->im_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 *)&(im->im_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) +{ + DECLARE_GLOBAL_DATA_PTR; + +#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 immap_t *im; + + if (c == '\n') + serial_putc ('\r'); + + im = (immap_t *)CFG_IMMR; + up = (scc_uart_t *)&(im->im_cpm.im_dprambase[PROFF_SCC]); + tbdf = (cbd_t *)&(im->im_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 immap_t *im; + unsigned char c; + + im = (immap_t *)CFG_IMMR; + up = (scc_uart_t *)&(im->im_cpm.im_dprambase[PROFF_SCC]); + rbdf = (cbd_t *)&(im->im_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 immap_t *im; + + im = (immap_t *)CFG_IMMR; + up = (scc_uart_t *)&(im->im_cpm.im_dprambase[PROFF_SCC]); + rbdf = (cbd_t *)&(im->im_cpm.im_dprambase[up->scc_genscc.scc_rbase]); + + return ((rbdf->cbd_sc & BD_SC_EMPTY) == 0); +} + +#endif /* CONFIG_CONS_ON_SCC */ + +#endif /* CONFIG_CPM2 */ diff --git a/cpu/mpc85xx/spd_sdram.c b/cpu/mpc85xx/spd_sdram.c new file mode 100755 index 0000000..af99282 --- /dev/null +++ b/cpu/mpc85xx/spd_sdram.c @@ -0,0 +1,1118 @@ +/* + * Copyright 2004 Freescale Semiconductor. + * (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 <i2c.h> +#include <spd.h> +#include <asm/mmu.h> + + +#if defined(CONFIG_DDR_ECC) && !defined(CONFIG_ECC_INIT_VIA_DDRCONTROLLER) +extern void dma_init(void); +extern uint dma_check(void); +extern int dma_xfer(void *dest, uint count, void *src); +#endif + +#ifdef CONFIG_SPD_EEPROM + +#ifndef CFG_READ_SPD +#define CFG_READ_SPD i2c_read +#endif + +static unsigned int setup_laws_and_tlbs(unsigned int memsize); + + +/* + * Convert picoseconds into clock cycles (rounding up if needed). + */ + +int +picos_to_clk(int picos) +{ + int clks; + + clks = picos / (2000000000 / (get_bus_freq(0) / 1000)); + if (picos % (2000000000 / (get_bus_freq(0) / 1000)) != 0) { + clks++; + } + + return clks; +} + + +/* + * 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. + */ + +unsigned int +compute_banksize(unsigned int mem_type, unsigned char row_dens) +{ + unsigned int bsize; + + if (mem_type == SPD_MEMTYPE_DDR) { + /* Bottom 2 bits up to the top. */ + bsize = ((row_dens >> 2) | ((row_dens & 3) << 6)) << 24; + debug("DDR: DDR I rank density = 0x%08x\n", bsize); + } else { + /* Bottom 5 bits up to the top. */ + bsize = ((row_dens >> 5) | ((row_dens & 31) << 3)) << 27; + 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.) + */ + +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, + 330, /* FIXME: Is 333 better/valid? */ + 660, /* FIXME: Is 667 better/valid? */ + 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; +} + + +long int +spd_sdram(void) +{ + volatile immap_t *immap = (immap_t *)CFG_IMMR; + volatile ccsr_ddr_t *ddr = &immap->im_ddr; + volatile ccsr_gur_t *gur = &immap->im_gur; + spd_eeprom_t spd; + unsigned int n_ranks; + unsigned int rank_density; + unsigned int odt_rd_cfg, odt_wr_cfg; + unsigned int odt_cfg, mode_odt_enable; + unsigned int dqs_cfg; + unsigned char twr_clk, twtr_clk, twr_auto_clk; + unsigned int tCKmin_ps, tCKmax_ps; + unsigned int max_data_rate, effective_data_rate; + unsigned int busfreq; + unsigned sdram_cfg; + unsigned int memsize; + unsigned char caslat, caslat_ctrl; + unsigned int trfc, trfc_clk, trfc_low, trfc_high; + unsigned int trcd_clk; + unsigned int trtp_clk; + unsigned char cke_min_clk; + unsigned char add_lat; + unsigned char wr_lat; + unsigned char wr_data_delay; + unsigned char four_act; + unsigned char cpo; + unsigned char burst_len; + unsigned int mode_caslat; + unsigned char sdram_type; + unsigned char d_init; + + /* + * Read SPD information. + */ + CFG_READ_SPD(SPD_EEPROM_ADDRESS, 0, 1, (uchar *) &spd, sizeof(spd)); + + /* + * Check for supported memory module types. + */ + if (spd.mem_type != SPD_MEMTYPE_DDR && + spd.mem_type != SPD_MEMTYPE_DDR2) { + printf("Unable to locate DDR I or DDR II module.\n" + " Fundamental memory type is 0x%0x\n", + spd.mem_type); + return 0; + } + + /* + * These test gloss over DDR I and II differences in interpretation + * of bytes 3 and 4, but irrelevantly. Multiple asymmetric banks + * are not supported on DDR I; and not encoded on DDR II. + * + * Also note that the 8548 controller can support: + * 12 <= nrow <= 16 + * and + * 8 <= ncol <= 11 (still, for DDR) + * 6 <= ncol <= 9 (for FCRAM) + */ + if (spd.nrow_addr < 12 || spd.nrow_addr > 14) { + printf("DDR: Unsupported number of Row Addr lines: %d.\n", + spd.nrow_addr); + return 0; + } + if (spd.ncol_addr < 8 || spd.ncol_addr > 11) { + printf("DDR: Unsupported number of Column Addr lines: %d.\n", + spd.ncol_addr); + return 0; + } + + /* + * Determine the number of physical banks controlled by + * different Chip Select signals. This is not quite the + * same as the number of DIMM modules on the board. Feh. + */ + if (spd.mem_type == SPD_MEMTYPE_DDR) { + n_ranks = spd.nrows; + } else { + n_ranks = (spd.nrows & 0x7) + 1; + } + + debug("DDR: number of ranks = %d\n", n_ranks); + + if (n_ranks > 2) { + printf("DDR: Only 2 chip selects are supported: %d\n", + n_ranks); + return 0; + } + + /* + * Adjust DDR II IO voltage biasing. It just makes it work. + */ + if (spd.mem_type == SPD_MEMTYPE_DDR2) { + gur->ddrioovcr = (0 + | 0x80000000 /* Enable */ + | 0x10000000 /* VSEL to 1.8V */ + ); + } + + /* + * Determine the size of each Rank in bytes. + */ + rank_density = compute_banksize(spd.mem_type, spd.row_dens); + + + /* + * Eg: Bounds: 0x0000_0000 to 0x0f000_0000 first 256 Meg + */ + ddr->cs0_bnds = (rank_density >> 24) - 1; + + /* + * 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 CS0 */ +#if 0 + /* FIXME: How to determine the number of dimm modules? */ + if (n_dimm_modules == 2) { + odt_rd_cfg = 1; /* Assert ODT on reads to CS0 */ + } +#endif + } + + ddr->cs0_config = ( 1 << 31 + | (odt_rd_cfg << 20) + | (odt_wr_cfg << 16) + | (spd.nrow_addr - 12) << 8 + | (spd.ncol_addr - 8) ); + debug("\n"); + debug("DDR: cs0_bnds = 0x%08x\n", ddr->cs0_bnds); + debug("DDR: cs0_config = 0x%08x\n", ddr->cs0_config); + + if (n_ranks == 2) { + /* + * Eg: Bounds: 0x0f00_0000 to 0x1e0000_0000, second 256 Meg + */ + ddr->cs1_bnds = ( (rank_density >> 8) + | ((rank_density >> (24 - 1)) - 1) ); + ddr->cs1_config = ( 1<<31 + | (odt_rd_cfg << 20) + | (odt_wr_cfg << 16) + | (spd.nrow_addr - 12) << 8 + | (spd.ncol_addr - 8) ); + debug("DDR: cs1_bnds = 0x%08x\n", ddr->cs1_bnds); + debug("DDR: cs1_config = 0x%08x\n", ddr->cs1_config); + } + + + /* + * 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 4 0111 + * 4.5 1000 + * 5.0 5 1001 + */ + caslat = __ilog2(spd.cas_lat); + if ((spd.mem_type == SPD_MEMTYPE_DDR) + && (caslat > 5)) { + 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); + + /* + * 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. + */ + tCKmin_ps = convert_bcd_tenths_to_cycle_time_ps(spd.clk_cycle); + debug("DDR: tCKmin = %d ps\n", tCKmin_ps); + + /* + * Double-data rate, scaled 1000 to picoseconds, and back down to MHz. + */ + max_data_rate = 2 * 1000 * 1000 / tCKmin_ps; + debug("DDR: Module max data rate = %d Mhz\n", max_data_rate); + + + /* + * Adjust the CAS Latency to allow for bus speeds that + * are slower than the DDR module. + */ + busfreq = get_bus_freq(0) / 1000000; /* MHz */ + + effective_data_rate = max_data_rate; + if (busfreq < 90) { + /* DDR rate out-of-range */ + puts("DDR: platform frequency is not fit for DDR rate\n"); + return 0; + + } else if (90 <= busfreq && busfreq < 230 && max_data_rate >= 230) { + /* + * busfreq 90~230 range, treated as DDR 200. + */ + effective_data_rate = 200; + if (spd.clk_cycle3 == 0xa0) /* 10 ns */ + caslat -= 2; + else if (spd.clk_cycle2 == 0xa0) + caslat--; + + } else if (230 <= busfreq && busfreq < 280 && max_data_rate >= 280) { + /* + * busfreq 230~280 range, treated as DDR 266. + */ + effective_data_rate = 266; + if (spd.clk_cycle3 == 0x75) /* 7.5 ns */ + caslat -= 2; + else if (spd.clk_cycle2 == 0x75) + caslat--; + + } else if (280 <= busfreq && busfreq < 350 && max_data_rate >= 350) { + /* + * busfreq 280~350 range, treated as DDR 333. + */ + effective_data_rate = 333; + if (spd.clk_cycle3 == 0x60) /* 6.0 ns */ + caslat -= 2; + else if (spd.clk_cycle2 == 0x60) + caslat--; + + } else if (350 <= busfreq && busfreq < 460 && max_data_rate >= 460) { + /* + * busfreq 350~460 range, treated as DDR 400. + */ + effective_data_rate = 400; + if (spd.clk_cycle3 == 0x50) /* 5.0 ns */ + caslat -= 2; + else if (spd.clk_cycle2 == 0x50) + caslat--; + + } else if (460 <= busfreq && busfreq < 560 && max_data_rate >= 560) { + /* + * busfreq 460~560 range, treated as DDR 533. + */ + effective_data_rate = 533; + if (spd.clk_cycle3 == 0x3D) /* 3.75 ns */ + caslat -= 2; + else if (spd.clk_cycle2 == 0x3D) + caslat--; + + } else if (560 <= busfreq && busfreq < 700 && max_data_rate >= 700) { + /* + * busfreq 560~700 range, treated as DDR 667. + */ + effective_data_rate = 667; + if (spd.clk_cycle3 == 0x30) /* 3.0 ns */ + caslat -= 2; + else if (spd.clk_cycle2 == 0x30) + caslat--; + + } else if (700 <= busfreq) { + /* + * DDR rate out-of-range + */ + printf("DDR: Bus freq %d MHz is not fit for DDR rate %d MHz\n", + busfreq, max_data_rate); + return 0; + } + + + /* + * 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. The new PQ38 DDR controller + * dreams up non-zero default values to be backwards compatible. + */ + 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 */ + ); +#if 0 + ddr->timing_cfg_0 |= 0xaa000000; /* extra cycles */ +#endif + debug("DDR: timing_cfg_0 = 0x%08x\n", ddr->timing_cfg_0); + + } else { +#if 0 + /* + * Force extra cycles with 0xaa bits. + * Incidentally supply the dreamt-up backwards compat value! + */ + ddr->timing_cfg_0 = 0x00110105; /* backwards compat value */ + ddr->timing_cfg_0 |= 0xaa000000; /* extra cycles */ + debug("DDR: HACK timing_cfg_0 = 0x%08x\n", ddr->timing_cfg_0); +#endif + } + + + /* + * Some Timing Config 1 values now. + * Sneak Extended Refresh Recovery in here too. + */ + + /* + * 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); + } + + /* + * 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 + * 8548 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; + + /* + * Sneak in some Extended Refresh Recovery. + */ + ddr->ext_refrec = (trfc_high << 16); + debug("DDR: ext_refrec = 0x%08x\n", ddr->ext_refrec); + + ddr->timing_cfg_1 = + (0 + | ((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 */ + ); + + debug("DDR: timing_cfg_1 = 0x%08x\n", ddr->timing_cfg_1); + + + /* + * Timing_Config_2 + * Was: 0x00000800; + */ + + /* + * 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 > trcd_clk) { + add_lat = trcd_clk - 1; + } + } + + /* + * Write Data Delay + * Historically 0x2 == 4/8 clock delay. + * Empirically, 0x3 == 6/8 clock delay is suggested for DDR I 266. + */ + wr_data_delay = 3; + + /* + * 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); + + 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 || effective_data_rate == 333) { + cpo = 0x7; /* READ_LAT + 5/4 */ + } else if (effective_data_rate == 400) { + cpo = 0x9; /* READ_LAT + 7/4 */ + } else { + /* Pure speculation */ + cpo = 0xb; + } + } + + 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_2 = 0x%08x\n", ddr->timing_cfg_2); + + + /* + * Determine the Mode Register Set. + * + * This is nominally part specific, but it appears to be + * consistent for all DDR I devices, and for all DDR II devices. + * + * caslat must be programmed + * burst length is always 4 + * burst type is sequential + * + * For DDR I: + * operating mode is "normal" + * + * For DDR II: + * other stuff + */ + + mode_caslat = 0; + + /* + * Table lookup from DDR I or II Device Operation Specs. + */ + if (spd.mem_type == SPD_MEMTYPE_DDR) { + if (1 <= caslat && caslat <= 4) { + unsigned char mode_caslat_table[4] = { + 0x5, /* 1.5 clocks */ + 0x2, /* 2.0 clocks */ + 0x6, /* 2.5 clocks */ + 0x3 /* 3.0 clocks */ + }; + mode_caslat = mode_caslat_table[caslat - 1]; + } else { + puts("DDR I: Only CAS Latencies of 1.5, 2.0, " + "2.5 and 3.0 clocks are supported.\n"); + return 0; + } + + } else { + if (2 <= caslat && caslat <= 5) { + mode_caslat = caslat; + } else { + puts("DDR II: Only CAS Latencies of 2.0, 3.0, " + "4.0 and 5.0 clocks are supported.\n"); + return 0; + } + } + + /* + * Encoded Burst Lenght of 4. + */ + burst_len = 2; /* Fiat. */ + + if (spd.mem_type == SPD_MEMTYPE_DDR) { + twr_auto_clk = 0; /* Historical */ + } else { + /* + * Determine tCK max in picos. Grab tWR and convert to picos. + * Auto-precharge write recovery is: + * WR = roundup(tWR_ns/tCKmax_ns). + * + * Ponder: Is twr_auto_clk different than twr_clk? + */ + tCKmax_ps = convert_bcd_tenths_to_cycle_time_ps(spd.tckmax); + twr_auto_clk = (spd.twr * 250 + tCKmax_ps - 1) / tCKmax_ps; + } + + + /* + * Mode Reg in bits 16 ~ 31, + * Extended Mode Reg 1 in bits 0 ~ 15. + */ + 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 + | (add_lat << (16 + 3)) /* Additive Latency in EMRS1 */ + | (mode_odt_enable << 16) /* ODT Enable in EMRS1 */ + | (twr_auto_clk << 9) /* Write Recovery Autopre */ + | (mode_caslat << 4) /* caslat */ + | (burst_len << 0) /* Burst length */ + ); + + debug("DDR: sdram_mode = 0x%08x\n", ddr->sdram_mode); + + + /* + * Clear EMRS2 and EMRS3. + */ + ddr->sdram_mode_2 = 0; + debug("DDR: sdram_mode_2 = 0x%08x\n", ddr->sdram_mode_2); + + + /* + * 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. + */ + { + unsigned int refresh_clk; + unsigned int refresh_time_ns[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 */ + }; + + refresh_clk = picos_to_clk(refresh_time_ns[spd.refresh & 0x7]); + + /* + * Set BSTOPRE to 0x100 for page mode + * If auto-charge is used, set BSTOPRE = 0 + */ + ddr->sdram_interval = + (0 + | (refresh_clk & 0x3fff) << 16 + | 0x100 + ); + debug("DDR: sdram_interval = 0x%08x\n", ddr->sdram_interval); + } + + /* + * Is this an ECC DDR chip? + * But don't mess with it if the DDR controller will init mem. + */ +#if defined(CONFIG_DDR_ECC) && !defined(CONFIG_ECC_INIT_VIA_DDRCONTROLLER) + if (spd.config == 0x02) { + ddr->err_disable = 0x0000000d; + ddr->err_sbe = 0x00ff0000; + } + debug("DDR: err_disable = 0x%08x\n", ddr->err_disable); + debug("DDR: err_sbe = 0x%08x\n", ddr->err_sbe); +#endif + + asm("sync;isync;msync"); + udelay(500); + + /* + * SDRAM Cfg 2 + */ + + /* + * When ODT is enabled, Chap 9 suggests asserting ODT to + * internal IOs only during reads. + */ + odt_cfg = 0; + if (odt_rd_cfg | odt_wr_cfg) { + odt_cfg = 0x2; /* ODT to IOs during reads */ + } + + /* + * Try to use differential DQS with DDR II. + */ + if (spd.mem_type == SPD_MEMTYPE_DDR) { + dqs_cfg = 0; /* No Differential DQS for DDR I */ + } else { + dqs_cfg = 0x1; /* Differential DQS for DDR II */ + } + +#if defined(CONFIG_ECC_INIT_VIA_DDRCONTROLLER) + /* + * Use the DDR controller to auto initialize memory. + */ + d_init = 1; + ddr->sdram_data_init = CONFIG_MEM_INIT_VALUE; + debug("DDR: ddr_data_init = 0x%08x\n", ddr->sdram_data_init); +#else + /* + * Memory will be initialized via DMA, or not at all. + */ + d_init = 0; +#endif + + ddr->sdram_cfg_2 = (0 + | (dqs_cfg << 26) /* Differential DQS */ + | (odt_cfg << 21) /* ODT */ + | (d_init << 4) /* D_INIT auto init DDR */ + ); + + debug("DDR: sdram_cfg_2 = 0x%08x\n", ddr->sdram_cfg_2); + + +#ifdef MPC85xx_DDR_SDRAM_CLK_CNTL + { + unsigned char clk_adjust; + + /* + * Setup the clock control. + * SDRAM_CLK_CNTL[0] = Source synchronous enable == 1 + * SDRAM_CLK_CNTL[5-7] = Clock Adjust + * 0110 3/4 cycle late + * 0111 7/8 cycle late + */ + if (spd.mem_type == SPD_MEMTYPE_DDR) { + clk_adjust = 0x6; + } else { + clk_adjust = 0x7; + } + + ddr->sdram_clk_cntl = (0 + | 0x80000000 + | (clk_adjust << 23) + ); + debug("DDR: sdram_clk_cntl = 0x%08x\n", ddr->sdram_clk_cntl); + } +#endif + + /* + * Figure out the settings for the sdram_cfg register. + * Build up the entire register in 'sdram_cfg' before writing + * since the write into the register will actually enable the + * memory controller; 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_type = (spd.mem_type == SPD_MEMTYPE_DDR) ? 2 : 3; + sdram_cfg = (0 + | (1 << 31) /* Enable */ + | (1 << 30) /* Self refresh */ + | (sdram_type << 24) /* SDRAM type */ + ); + + /* + * sdram_cfg[3] = RD_EN - registered DIMM enable + * A value of 0x26 indicates micron registered DIMMS (micron.com) + */ + if (spd.mem_type == SPD_MEMTYPE_DDR && spd.mod_attr == 0x26) { + sdram_cfg |= 0x10000000; /* RD_EN */ + } + +#if defined(CONFIG_DDR_ECC) + /* + * If the user wanted ECC (enabled via sdram_cfg[2]) + */ + if (spd.config == 0x02) { + sdram_cfg |= 0x20000000; /* ECC_EN */ + } +#endif + + /* + * REV1 uses 1T timing. + * REV2 may use 1T or 2T as configured by the user. + */ + { + uint pvr = get_pvr(); + + if (pvr != PVR_85xx_REV1) { +#if defined(CONFIG_DDR_2T_TIMING) + /* + * Enable 2T timing by setting sdram_cfg[16]. + */ + sdram_cfg |= 0x8000; /* 2T_EN */ +#endif + } + } + + /* + * 200 painful micro-seconds must elapse between + * the DDR clock setup and the DDR config enable. + */ + udelay(200); + + /* + * Go! + */ + ddr->sdram_cfg = sdram_cfg; + + asm("sync;isync;msync"); + udelay(500); + + debug("DDR: sdram_cfg = 0x%08x\n", ddr->sdram_cfg); + + +#if defined(CONFIG_ECC_INIT_VIA_DDRCONTROLLER) + /* + * Poll until memory is initialized. + * 512 Meg at 400 might hit this 200 times or so. + */ + while ((ddr->sdram_cfg_2 & (d_init << 4)) != 0) { + udelay(1000); + } +#endif + + + /* + * Figure out memory size in Megabytes. + */ + memsize = n_ranks * rank_density / 0x100000; + + /* + * Establish Local Access Window and TLB mappings for DDR memory. + */ + memsize = setup_laws_and_tlbs(memsize); + if (memsize == 0) { + return 0; + } + + return memsize * 1024 * 1024; +} + + +/* + * Setup Local Access Window and TLB1 mappings for the requested + * amount of memory. Returns the amount of memory actually mapped + * (usually the original request size), or 0 on error. + */ + +static unsigned int +setup_laws_and_tlbs(unsigned int memsize) +{ + volatile immap_t *immap = (immap_t *)CFG_IMMR; + volatile ccsr_local_ecm_t *ecm = &immap->im_local_ecm; + unsigned int tlb_size; + unsigned int law_size; + unsigned int ram_tlb_index; + unsigned int ram_tlb_address; + + /* + * Determine size of each TLB1 entry. + */ + switch (memsize) { + case 16: + case 32: + tlb_size = BOOKE_PAGESZ_16M; + break; + case 64: + case 128: + tlb_size = BOOKE_PAGESZ_64M; + break; + case 256: + case 512: + case 1024: + case 2048: + tlb_size = BOOKE_PAGESZ_256M; + break; + default: + puts("DDR: only 16M,32M,64M,128M,256M,512M,1G and 2G are supported.\n"); + + /* + * The memory was not able to be mapped. + */ + return 0; + break; + } + + /* + * Configure DDR TLB1 entries. + * Starting at TLB1 8, use no more than 8 TLB1 entries. + */ + ram_tlb_index = 8; + ram_tlb_address = (unsigned int)CFG_DDR_SDRAM_BASE; + while (ram_tlb_address < (memsize * 1024 * 1024) + && ram_tlb_index < 16) { + mtspr(MAS0, TLB1_MAS0(1, ram_tlb_index, 0)); + mtspr(MAS1, TLB1_MAS1(1, 1, 0, 0, tlb_size)); + mtspr(MAS2, TLB1_MAS2(E500_TLB_EPN(ram_tlb_address), + 0, 0, 0, 0, 0, 0, 0, 0)); + mtspr(MAS3, TLB1_MAS3(E500_TLB_RPN(ram_tlb_address), + 0, 0, 0, 0, 0, 1, 0, 1, 0, 1)); + asm volatile("isync;msync;tlbwe;isync"); + + debug("DDR: MAS0=0x%08x\n", TLB1_MAS0(1, ram_tlb_index, 0)); + debug("DDR: MAS1=0x%08x\n", TLB1_MAS1(1, 1, 0, 0, tlb_size)); + debug("DDR: MAS2=0x%08x\n", + TLB1_MAS2(E500_TLB_EPN(ram_tlb_address), + 0, 0, 0, 0, 0, 0, 0, 0)); + debug("DDR: MAS3=0x%08x\n", + TLB1_MAS3(E500_TLB_RPN(ram_tlb_address), + 0, 0, 0, 0, 0, 1, 0, 1, 0, 1)); + + ram_tlb_address += (0x1000 << ((tlb_size - 1) * 2)); + ram_tlb_index++; + } + + + /* + * First supported LAW size is 16M, at LAWAR_SIZE_16M == 23. Fnord. + */ + law_size = 19 + __ilog2(memsize); + + /* + * Set up LAWBAR for all of DDR. + */ + ecm->lawbar1 = ((CFG_DDR_SDRAM_BASE >> 12) & 0xfffff); + ecm->lawar1 = (LAWAR_EN + | LAWAR_TRGT_IF_DDR + | (LAWAR_SIZE & law_size)); + debug("DDR: LAWBAR1=0x%08x\n", ecm->lawbar1); + debug("DDR: LARAR1=0x%08x\n", ecm->lawar1); + + /* + * Confirm that the requested amount of memory was mapped. + */ + return memsize; +} + +#endif /* CONFIG_SPD_EEPROM */ + + +#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) +{ + uint *p = 0; + uint i = 0; + volatile immap_t *immap = (immap_t *)CFG_IMMR; + volatile ccsr_ddr_t *ddr= &immap->im_ddr; + + dma_init(); + + for (*p = 0; p < (uint *)(8 * 1024); p++) { + if (((unsigned int)p & 0x1f) == 0) { + ppcDcbz((unsigned long) p); + } + *p = (unsigned int)CONFIG_MEM_INIT_VALUE; + if (((unsigned int)p & 0x1c) == 0x1c) { + ppcDcbf((unsigned long) p); + } + } + + /* 8K */ + dma_xfer((uint *)0x2000, 0x2000, (uint *)0); + /* 16K */ + dma_xfer((uint *)0x4000, 0x4000, (uint *)0); + /* 32K */ + dma_xfer((uint *)0x8000, 0x8000, (uint *)0); + /* 64K */ + dma_xfer((uint *)0x10000, 0x10000, (uint *)0); + /* 128k */ + dma_xfer((uint *)0x20000, 0x20000, (uint *)0); + /* 256k */ + dma_xfer((uint *)0x40000, 0x40000, (uint *)0); + /* 512k */ + dma_xfer((uint *)0x80000, 0x80000, (uint *)0); + /* 1M */ + dma_xfer((uint *)0x100000, 0x100000, (uint *)0); + /* 2M */ + dma_xfer((uint *)0x200000, 0x200000, (uint *)0); + /* 4M */ + dma_xfer((uint *)0x400000, 0x400000, (uint *)0); + + for (i = 1; i < dram_size / 0x800000; i++) { + dma_xfer((uint *)(0x800000*i), 0x800000, (uint *)0); + } + + /* + * 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/cpu/mpc85xx/speed.c b/cpu/mpc85xx/speed.c new file mode 100755 index 0000000..d736742 --- /dev/null +++ b/cpu/mpc85xx/speed.c @@ -0,0 +1,123 @@ +/* + * Copyright 2004 Freescale Semiconductor. + * (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> + +/* --------------------------------------------------------------- */ + +void get_sys_info (sys_info_t * sysInfo) +{ + volatile immap_t *immap = (immap_t *)CFG_IMMR; + volatile ccsr_gur_t *gur = &immap->im_gur; + uint plat_ratio,e500_ratio; + + plat_ratio = (gur->porpllsr) & 0x0000003e; + plat_ratio >>= 1; + switch(plat_ratio) { + 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; + } + + e500_ratio = (gur->porpllsr) & 0x003f0000; + e500_ratio >>= 16; + switch(e500_ratio) { + case 0x04: + sysInfo->freqProcessor = 2*sysInfo->freqSystemBus; + break; + case 0x05: + sysInfo->freqProcessor = 5*sysInfo->freqSystemBus/2; + break; + case 0x06: + sysInfo->freqProcessor = 3*sysInfo->freqSystemBus; + break; + case 0x07: + sysInfo->freqProcessor = 7*sysInfo->freqSystemBus/2; + break; + default: + sysInfo->freqProcessor = 0; + break; + } +} + +int get_clocks (void) +{ + DECLARE_GLOBAL_DATA_PTR; + sys_info_t sys_info; +#if defined(CONFIG_CPM2) + volatile immap_t *immap = (immap_t *) CFG_IMMR; + uint sccr, dfbrg; + + /* set VCO = 4 * BRG */ + immap->im_cpm.im_cpm_intctl.sccr &= 0xfffffffc; + sccr = immap->im_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; + gd->bus_clk = sys_info.freqSystemBus; +#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) +{ + ulong val; + + sys_info_t sys_info; + + get_sys_info (&sys_info); + val = sys_info.freqSystemBus; + + return val; +} diff --git a/cpu/mpc85xx/start.S b/cpu/mpc85xx/start.S new file mode 100755 index 0000000..7ac6573 --- /dev/null +++ b/cpu/mpc85xx/start.S @@ -0,0 +1,1157 @@ +/* + * 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 + */ + +/* 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 <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 r14 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 + +/* + * 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: + mfspr r0, PVR + lis r1, PVR_85xx_REV1@h + ori r1, r1, PVR_85xx_REV1@l + cmpw r0, r1 + bne 1f + + /* Semi-bogus errata fixup for Rev 1 */ + li r0,0x2000 + mtspr 977,r0 + + /* + * Before invalidating MMU L1/L2, read TLB1 Entry 0 and then + * write it back immediately to fixup a Rev 1 bug (Errata CPU4) + * for this initial TLB1 entry 0, otherwise the TLB1 entry 0 + * will be invalidated (incorrectly). + */ + lis r2,0x1000 + mtspr MAS0,r2 + tlbre + tlbwe + isync + +1: + /* + * Clear and set up some registers. + * Note: Some registers need strict synchronization by + * sync/mbar/msync/isync when being "mtspr". + * BookE: isync before PID,tlbivax,tlbwe + * BookE: isync after MSR,PID; msync_isync after tlbivax & tlbwe + * E500: msync,isync before L1CSR0 + * E500: isync after BBEAR,BBTAR,BUCSR,DBCR0,DBCR1,HID0,HID1, + * L1CSR0, L1CSR1, MAS[0,1,2,3,4,6],MMUCSR0, PID[0,1,2], + * SPEFCSR + */ + + /* invalidate d-cache */ + mfspr r0,L1CSR0 + ori r0,r0,0x0002 + msync + isync + mtspr L1CSR0,r0 + isync + + /* disable d-cache */ + li r0,0x0 + mtspr L1CSR0,r0 + + /* invalidate i-cache */ + mfspr r0,L1CSR1 + ori r0,r0,0x0002 + mtspr L1CSR1,r0 + isync + + /* disable i-cache */ + li r0,0x0 + mtspr L1CSR1,r0 + isync + + /* clear registers */ + li r0,0 + mtspr SRR0,r0 + mtspr SRR1,r0 + mtspr CSRR0,r0 + mtspr CSRR1,r0 + mtspr MCSRR0,r0 + mtspr MCSRR1,r0 + + mtspr ESR,r0 + mtspr MCSR,r0 + mtspr DEAR,r0 + + /* not needed and conflicts with some debuggers */ + /* mtspr DBCR0,r0 */ + mtspr DBCR1,r0 + mtspr DBCR2,r0 + /* not needed and conflicts with some debuggers */ + /* mtspr IAC1,r0 */ + /* mtspr IAC2,r0 */ + mtspr DAC1,r0 + mtspr DAC2,r0 + + mfspr r1,DBSR + mtspr DBSR,r1 /* Clear all valid bits */ + + mtspr PID0,r0 + mtspr PID1,r0 + mtspr PID2,r0 + mtspr TCR,r0 + + mtspr BUCSR,r0 /* disable branch prediction */ + mtspr MAS4,r0 + mtspr MAS6,r0 +#if defined(CONFIG_ENABLE_36BIT_PHYS) + mtspr MAS7,r0 +#endif + isync + + /* 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 */ + + /* + * Invalidate MMU L1/L2 + * + * Note: There is a fixup earlier for Errata CPU4 on + * Rev 1 parts that must precede this MMU invalidation. + */ + li r2, 0x001e + mtspr MMUCSR0, r2 + isync + + /* + * Invalidate all TLB0 entries. + */ + li r3,4 + li r4,0 + tlbivax r4,r3 + /* + * To avoid REV1 Errata CPU6 issues, make sure + * the instruction following tlbivax is not a store. + */ + + /* + * After reset, CCSRBAR is located at CFG_CCSRBAR_DEFAULT, i.e. + * 0xff700000-0xff800000. We need add a TLB1 entry for this 1MB + * region before we can access any CCSR registers such as L2 + * registers, Local Access Registers,etc. We will also re-allocate + * CFG_CCSRBAR_DEFAULT to CFG_CCSRBAR immediately after TLB1 setup. + * + * Please refer to board-specif directory for TLB1 entry configuration. + * (e.g. board/<yourboard>/init.S) + * + */ + bl tlb1_entry + mr r5,r0 + li r1,0x0020 /* max 16 TLB1 plus some TLB0 entries */ + mtctr r1 + lwzu r4,0(r5) /* how many TLB1 entries we actually use */ + +0: cmpwi r4,0 + beq 1f + lwzu r0,4(r5) + lwzu r1,4(r5) + lwzu r2,4(r5) + lwzu r3,4(r5) + mtspr MAS0,r0 + mtspr MAS1,r1 + mtspr MAS2,r2 + mtspr MAS3,r3 + isync + msync + tlbwe + isync + addi r4,r4,-1 + bdnz 0b + +1: +#if (CFG_CCSRBAR_DEFAULT != CFG_CCSRBAR) + /* Special sequence needed to update CCSRBAR itself */ + lis r4, CFG_CCSRBAR_DEFAULT@h + ori r4, r4, CFG_CCSRBAR_DEFAULT@l + + lis r5, CFG_CCSRBAR@h + ori r5, r5, CFG_CCSRBAR@l + srwi r6,r5,12 + stw r6, 0(r4) + isync + + lis r5, 0xffff + ori r5,r5,0xf000 + lwz r5, 0(r5) + isync + + lis r3, CFG_CCSRBAR@h + lwz r5, CFG_CCSRBAR@l(r3) + isync +#endif + + + /* set up local access windows, defined at board/<boardname>/init.S */ + lis r7,CFG_CCSRBAR@h + ori r7,r7,CFG_CCSRBAR@l + + bl law_entry + mr r6,r0 + li r1,0x0007 /* 8 LAWs, but reserve one for boot-over-rio-or-pci */ + mtctr r1 + lwzu r5,0(r6) /* how many windows we actually use */ + + li r2,0x0c28 /* the first pair is reserved for boot-over-rio-or-pci */ + li r1,0x0c30 + +0: cmpwi r5,0 + beq 1f + lwzu r4,4(r6) + lwzu r3,4(r6) + stwx r4,r7,r2 + stwx r3,r7,r1 + addi r5,r5,-1 + addi r2,r2,0x0020 + addi r1,r1,0x0020 + bdnz 0b + + /* Jump out the last 4K page and continue to 'normal' start */ +1: bl 3f + b _start + +3: li r0,0 + mtspr SRR1,r0 /* Keep things disabled for now */ + mflr r1 + mtspr SRR0,r1 + rfi + +/* + * 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 " (", __DATE__, " - ", __TIME__, ")" + .ascii CONFIG_IDENT_STRING, "\0" + + . = EXC_OFF_SYS_RESET + .globl _start +_start: + /* 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 */ + lis r1,0x0002 /* set CE bit (Critical Exceptions) */ + ori r1,r1,0x1200 /* set ME/DE bit */ + mtmsr r1 /* change MSR */ + isync + + /* Enable Time Base and Select Time Base Clock */ + lis r0,HID0_EMCP@h /* Enable machine check */ + ori r0,r0,0x4000 /* time base is processor clock */ +#if defined(CONFIG_ENABLE_36BIT_PHYS) + ori r0,r0,0x0080 /* enable MAS7 updates */ +#endif + mtspr HID0,r0 + +#if defined(CONFIG_ADDR_STREAMING) + li r0,0x3000 +#else + li r0,0x1000 +#endif + mtspr HID1,r0 + + /* Enable Branch Prediction */ +#if defined(CONFIG_BTB) + li r0,0x201 /* BBFI = 1, BPEN = 1 */ + mtspr BUCSR,r0 +#endif + +#if defined(CFG_INIT_DBCR) + lis r1,0xffff + ori r1,r1,0xffff + mtspr DBSR,r1 /* Clear all status bits */ + lis r0,CFG_INIT_DBCR@h /* DBCR0[IDM] must be set */ + ori r0,r0,CFG_INIT_DBCR@l + mtspr DBCR0,r0 +#endif + +/* L1 DCache is used for initial RAM */ + mfspr r2, L1CSR0 + ori r2, r2, 0x0003 + oris r2, r2, 0x0001 + mtspr L1CSR0, r2 /* enable/invalidate L1 Dcache */ + isync + + /* Allocate Initial RAM in data cache. + */ + lis r3, CFG_INIT_RAM_ADDR@h + ori r3, r3, CFG_INIT_RAM_ADDR@l + li r2, 512 /* 512*32=16K */ + mtctr r2 + li r0, 0 +1: + dcbz r0, r3 + dcbtls 0,r0, r3 + addi r3, r3, 32 + bdnz 1b + +#ifndef CFG_RAMBOOT + /* Calculate absolute address in FLASH and jump there */ + /*--------------------------------------------------------------*/ + lis r3, CFG_MONITOR_BASE@h + ori r3, r3, CFG_MONITOR_BASE@l + addi r3, r3, in_flash - _start + EXC_OFF_SYS_RESET + mtlr r3 + blr + +in_flash: +#endif /* CFG_RAMBOOT */ + + /* Setup the stack in initial RAM,could be L2-as-SRAM or L1 dcache*/ + lis r1,CFG_INIT_RAM_ADDR@h + ori r1,r1,CFG_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_f + bl icache_enable + bl board_init_f + isync + +/* --FIXME-- machine check with MCSRRn and rfmci */ + + .globl _start_of_vectors +_start_of_vectors: +#if 0 +/* Critical input. */ + CRIT_EXCEPTION(0x0100, CritcalInput, CritcalInputException) +#endif +/* Machine check --FIXME-- Should be MACH_EXCEPTION */ + CRIT_EXCEPTION(0x0200, 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, UnknownException) + +/* Alignment exception. */ + . = 0x0600 +Alignment: + EXCEPTION_PROLOG + mfspr r4,DAR + stw r4,_DAR(r21) + mfspr r5,DSISR + stw r5,_DSISR(r21) + addi r3,r1,STACK_FRAME_OVERHEAD + li r20,MSR_KERNEL + rlwimi r20,r23,0,16,16 /* copy EE bit from saved MSR */ + lwz r6,GOT(transfer_to_handler) + mtlr r6 + blrl +.L_Alignment: + .long AlignmentException - _start + EXC_OFF_SYS_RESET + .long int_return - _start + EXC_OFF_SYS_RESET + +/* Program check exception */ + . = 0x0700 +ProgramCheck: + EXCEPTION_PROLOG + addi r3,r1,STACK_FRAME_OVERHEAD + li r20,MSR_KERNEL + rlwimi r20,r23,0,16,16 /* copy EE bit from saved MSR */ + lwz r6,GOT(transfer_to_handler) + mtlr r6 + blrl +.L_ProgramCheck: + .long ProgramCheckException - _start + EXC_OFF_SYS_RESET + .long int_return - _start + EXC_OFF_SYS_RESET + + /* 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: + + + . = 0x2100 + +/* + * 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 990,r2 /* SRR2 */ + mtspr 991,r0 /* SRR3 */ + lwz r0,GPR0(r1) + lwz r2,GPR2(r1) + lwz r1,GPR1(r1) + SYNC + rfci + +/* Cache functions. +*/ +invalidate_icache: + mfspr r0,L1CSR1 + ori r0,r0,0x0002 + mtspr L1CSR1,r0 + isync + blr /* entire I cache */ + +invalidate_dcache: + mfspr r0,L1CSR0 + ori r0,r0,0x0002 + 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 r1,0xfffffffe@h + ori r1,r1,0xfffffffe@l + and r0,r0,r1 + mtspr L1CSR1,r0 + isync + blr + + .globl icache_status +icache_status: + mfspr r3,L1CSR1 + srwi r3, r3, 31 /* >>31 => select bit 0 */ + 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 r0,L1CSR0 + lis r1,0xfffffffe@h + ori r1,r1,0xfffffffe@l + and r0,r0,r1 + msync + isync + mtspr L1CSR0,r0 + isync + blr + + .globl dcache_status +dcache_status: + mfspr r3,L1CSR0 + srwi r3, r3, 31 /* >>31 => select bit 0 */ + 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) + 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 + +/*------------------------------------------------------------------------------- */ +/* Function: ppcDcbf */ +/* Description: Data Cache block flush */ +/* Input: r3 = effective address */ +/* Output: none. */ +/*------------------------------------------------------------------------------- */ + .globl ppcDcbf +ppcDcbf: + dcbf r0,r3 + blr + +/*------------------------------------------------------------------------------- */ +/* Function: ppcDcbi */ +/* Description: Data Cache block Invalidate */ +/* Input: r3 = effective address */ +/* Output: none. */ +/*------------------------------------------------------------------------------- */ + .globl ppcDcbi +ppcDcbi: + dcbi r0,r3 + blr + +/*-------------------------------------------------------------------------- + * Function: ppcDcbz + * Description: Data Cache block zero. + * Input: r3 = effective address + * Output: none. + *-------------------------------------------------------------------------- */ + + .globl ppcDcbz +ppcDcbz: + dcbz r0,r3 + blr + +/*------------------------------------------------------------------------------- */ +/* Function: ppcSync */ +/* Description: Processor Synchronize */ +/* Input: none. */ +/* Output: none. */ +/*------------------------------------------------------------------------------- */ + .globl ppcSync +ppcSync: + sync + 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 */ + + mr r3, r5 /* Destination Address */ + lis r4, CFG_MONITOR_BASE@h /* Source Address */ + ori r4, r4, CFG_MONITOR_BASE@l + lwz r5,GOT(__init_end) + sub r5,r5,r4 + li r6, CFG_CACHELINE_SIZE /* Cache Line Size */ + + /* + * Fix GOT pointer: + * + * New GOT-PTR = (old GOT-PTR - CFG_MONITOR_BASE) + Destination Address + * + * Offset: + */ + sub r15, r10, r4 + + /* First our own GOT */ + add r14, r14, 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 + EXC_OFF_SYS_RESET + mtlr r0 + blr /* NEVER RETURNS! */ + +in_ram: + + /* + * Relocation Function, r14 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) + add r0,r0,r11 + stw r0,0(r3) + bdnz 1b + + /* + * Now adjust the fixups and the pointers to the fixups + * in case we need to move ourselves again. + */ +2: 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 + + /* + * Copy exception vector code to low memory + * + * r3: dest_addr + * r7: source address, r8: end address, r9: target address + */ + .globl trap_init +trap_init: + 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 */ + + mflr r4 /* save link register */ +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 + bl trap_reloc + li r7, .L_DataStorage - _start + EXC_OFF_SYS_RESET + bl trap_reloc + li r7, .L_InstStorage - _start + EXC_OFF_SYS_RESET + bl trap_reloc + li r7, .L_ExtInterrupt - _start + EXC_OFF_SYS_RESET + bl trap_reloc + 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 + bl trap_reloc + li r7, .L_Decrementer - _start + EXC_OFF_SYS_RESET + bl trap_reloc + li r7, .L_IntervalTimer - _start + EXC_OFF_SYS_RESET + li r8, _end_of_vectors - _start + EXC_OFF_SYS_RESET +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 + + /* + * Function: relocate entries for one exception vector + */ +trap_reloc: + lwz r0, 0(r7) /* hdlr ... */ + add r0, r0, r3 /* ... += dest_addr */ + stw r0, 0(r7) + + lwz r0, 4(r7) /* int_return ... */ + add r0, r0, r3 /* ... += dest_addr */ + stw r0, 4(r7) + + blr + +#ifdef CFG_INIT_RAM_LOCK +.globl unlock_ram_in_cache +unlock_ram_in_cache: + /* invalidate the INIT_RAM section */ + lis r3, (CFG_INIT_RAM_ADDR & ~31)@h + ori r3, r3, (CFG_INIT_RAM_ADDR & ~31)@l + li r2,512 + mtctr r2 +1: icbi r0, r3 + dcbi r0, r3 + addi r3, r3, 32 + bdnz 1b + sync /* Wait for all icbi to complete on bus */ + isync + blr +#endif diff --git a/cpu/mpc85xx/traps.c b/cpu/mpc85xx/traps.c new file mode 100755 index 0000000..a87eed2 --- /dev/null +++ b/cpu/mpc85xx/traps.c @@ -0,0 +1,273 @@ +/* + * linux/arch/ppc/kernel/traps.c + * + * 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 <asm/processor.h> + +#if (CONFIG_COMMANDS & CFG_CMD_KGDB) +int (*debugger_exception_handler)(struct pt_regs *) = 0; +#endif + +/* Returns 0 if exception not found and fixup otherwise. */ +extern unsigned long search_exception_table(unsigned long); + +/* + * End of memory as shown by board info and determined by DDR setup. + */ +#define END_OF_MEM (gd->bd->bi_memstart + gd->bd->bi_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 (CONFIG_COMMANDS & CFG_CMD_BEDBUG) +extern void do_bedbug_breakpoint(struct pt_regs *); +#endif + +/* + * Trap & Exception support + */ + +void +print_backtrace(unsigned long *sp) +{ + DECLARE_GLOBAL_DATA_PTR; + 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"); +} + +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 (CONFIG_COMMANDS & CFG_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 (CONFIG_COMMANDS & CFG_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 (CONFIG_COMMANDS & CFG_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 (CONFIG_COMMANDS & CFG_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 (CONFIG_COMMANDS & CFG_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; +} |