diff options
Diffstat (limited to 'arch/powerpc/cpu/mpc8260')
-rw-r--r-- | arch/powerpc/cpu/mpc8260/Makefile | 53 | ||||
-rw-r--r-- | arch/powerpc/cpu/mpc8260/bedbug_603e.c | 238 | ||||
-rw-r--r-- | arch/powerpc/cpu/mpc8260/commproc.c | 221 | ||||
-rw-r--r-- | arch/powerpc/cpu/mpc8260/config.mk | 30 | ||||
-rw-r--r-- | arch/powerpc/cpu/mpc8260/cpu.c | 338 | ||||
-rw-r--r-- | arch/powerpc/cpu/mpc8260/cpu_init.c | 292 | ||||
-rw-r--r-- | arch/powerpc/cpu/mpc8260/ether_fcc.c | 1190 | ||||
-rw-r--r-- | arch/powerpc/cpu/mpc8260/ether_scc.c | 387 | ||||
-rw-r--r-- | arch/powerpc/cpu/mpc8260/i2c.c | 785 | ||||
-rw-r--r-- | arch/powerpc/cpu/mpc8260/interrupts.c | 279 | ||||
-rw-r--r-- | arch/powerpc/cpu/mpc8260/kgdb.S | 72 | ||||
-rw-r--r-- | arch/powerpc/cpu/mpc8260/pci.c | 466 | ||||
-rw-r--r-- | arch/powerpc/cpu/mpc8260/serial_scc.c | 498 | ||||
-rw-r--r-- | arch/powerpc/cpu/mpc8260/serial_smc.c | 467 | ||||
-rw-r--r-- | arch/powerpc/cpu/mpc8260/speed.c | 253 | ||||
-rw-r--r-- | arch/powerpc/cpu/mpc8260/speed.h | 54 | ||||
-rw-r--r-- | arch/powerpc/cpu/mpc8260/spi.c | 435 | ||||
-rw-r--r-- | arch/powerpc/cpu/mpc8260/start.S | 1023 | ||||
-rw-r--r-- | arch/powerpc/cpu/mpc8260/traps.c | 273 | ||||
-rw-r--r-- | arch/powerpc/cpu/mpc8260/u-boot.lds | 122 |
20 files changed, 7476 insertions, 0 deletions
diff --git a/arch/powerpc/cpu/mpc8260/Makefile b/arch/powerpc/cpu/mpc8260/Makefile new file mode 100644 index 0000000..9f0c2dd --- /dev/null +++ b/arch/powerpc/cpu/mpc8260/Makefile @@ -0,0 +1,53 @@ +# +# (C) Copyright 2000-2008 +# Wolfgang Denk, DENX Software Engineering, wd@denx.de. +# +# See file CREDITS for list of people who contributed to this +# project. +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation; either version 2 of +# the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, +# MA 02111-1307 USA +# + +include $(TOPDIR)/config.mk + +LIB = $(obj)lib$(CPU).a + +START = start.o kgdb.o +COBJS = traps.o serial_smc.o serial_scc.o cpu.o cpu_init.o speed.o \ + interrupts.o ether_fcc.o i2c.o commproc.o \ + bedbug_603e.o pci.o spi.o + +COBJS-$(CONFIG_ETHER_ON_SCC) = ether_scc.o + +COBJS += $(COBJS-y) + +SRCS := $(START:.o=.S) $(SOBJS:.o=.S) $(COBJS:.o=.c) +OBJS := $(addprefix $(obj),$(SOBJS) $(COBJS)) +START := $(addprefix $(obj),$(START)) + +all: $(obj).depend $(START) $(LIB) + +$(LIB): $(OBJS) + $(AR) $(ARFLAGS) $@ $(OBJS) $(obj)kgdb.o + +######################################################################### + +# defines $(obj).depend target +include $(SRCTREE)/rules.mk + +sinclude $(obj).depend + +######################################################################### diff --git a/arch/powerpc/cpu/mpc8260/bedbug_603e.c b/arch/powerpc/cpu/mpc8260/bedbug_603e.c new file mode 100644 index 0000000..c969ff6 --- /dev/null +++ b/arch/powerpc/cpu/mpc8260/bedbug_603e.c @@ -0,0 +1,238 @@ +/* + * Bedbug Functions specific to the MPC603e core + */ + +#include <common.h> +#include <command.h> +#include <linux/ctype.h> +#include <bedbug/type.h> +#include <bedbug/bedbug.h> +#include <bedbug/regs.h> +#include <bedbug/ppc.h> + +#if defined(CONFIG_CMD_BEDBUG) \ + && (defined(CONFIG_MPC824X) || defined(CONFIG_MPC8260)) + +#define MAX_BREAK_POINTS 1 + +extern CPU_DEBUG_CTX bug_ctx; + +void bedbug603e_init __P((void)); +void bedbug603e_do_break __P((cmd_tbl_t*,int,int,char*[])); +void bedbug603e_break_isr __P((struct pt_regs*)); +int bedbug603e_find_empty __P((void)); +int bedbug603e_set __P((int,unsigned long)); +int bedbug603e_clear __P((int)); + + +/* ====================================================================== + * Initialize the global bug_ctx structure for the processor. Clear all + * of the breakpoints. + * ====================================================================== */ + +void bedbug603e_init( void ) +{ + int i; + /* -------------------------------------------------- */ + + bug_ctx.hw_debug_enabled = 0; + bug_ctx.stopped = 0; + bug_ctx.current_bp = 0; + bug_ctx.regs = NULL; + + bug_ctx.do_break = bedbug603e_do_break; + bug_ctx.break_isr = bedbug603e_break_isr; + bug_ctx.find_empty = bedbug603e_find_empty; + bug_ctx.set = bedbug603e_set; + bug_ctx.clear = bedbug603e_clear; + + for( i = 1; i <= MAX_BREAK_POINTS; ++i ) + (*bug_ctx.clear)( i ); + + puts ("BEDBUG:ready\n"); + return; +} /* bedbug_init_breakpoints */ + + + +/* ====================================================================== + * Set/clear/show the hardware breakpoint for the 603e. The "off" + * string will disable a specific breakpoint. The "show" string will + * display the current breakpoints. Otherwise an address will set a + * breakpoint at that address. Setting a breakpoint uses the CPU-specific + * set routine which will assign a breakpoint number. + * ====================================================================== */ + +void bedbug603e_do_break (cmd_tbl_t *cmdtp, int flag, int argc, + char *argv[]) +{ + long addr; /* Address to break at */ + int which_bp; /* Breakpoint number */ + /* -------------------------------------------------- */ + + if (argc < 2) + { + cmd_usage(cmdtp); + return; + } + + /* Turn off a breakpoint */ + + if( strcmp( argv[ 1 ], "off" ) == 0 ) + { + if( bug_ctx.hw_debug_enabled == 0 ) + { + puts ( "No breakpoints enabled\n" ); + return; + } + + which_bp = simple_strtoul( argv[ 2 ], NULL, 10 ); + + if( bug_ctx.clear ) + (*bug_ctx.clear)( which_bp ); + + printf( "Breakpoint %d removed\n", which_bp ); + return; + } + + /* Show a list of breakpoints */ + + if( strcmp( argv[ 1 ], "show" ) == 0 ) + { + for( which_bp = 1; which_bp <= MAX_BREAK_POINTS; ++which_bp ) + { + + addr = GET_IABR(); + + printf( "Breakpoint [%d]: ", which_bp ); + if( (addr & 0x00000002) == 0 ) + puts ( "NOT SET\n" ); + else + disppc( (unsigned char *)(addr & 0xFFFFFFFC), 0, 1, bedbug_puts, F_RADHEX ); + } + return; + } + + /* Set a breakpoint at the address */ + + if(!(( isdigit( argv[ 1 ][ 0 ] )) || + (( argv[ 1 ][ 0 ] >= 'a' ) && ( argv[ 1 ][ 0 ] <= 'f' )) || + (( argv[ 1 ][ 0 ] >= 'A' ) && ( argv[ 1 ][ 0 ] <= 'F' )))) + { + cmd_usage(cmdtp); + return; + } + + addr = simple_strtoul( argv[ 1 ], NULL, 16 ); + + if(( bug_ctx.set ) && ( which_bp = (*bug_ctx.set)( 0, addr )) > 0 ) + { + printf( "Breakpoint [%d]: ", which_bp ); + disppc( (unsigned char *)addr, 0, 1, bedbug_puts, F_RADHEX ); + } + + return; +} /* bedbug603e_do_break */ + + + +/* ====================================================================== + * Handle a breakpoint. Enter a mini main loop. Stay in the loop until + * the stopped flag in the debug context is cleared. + * ====================================================================== */ + +void bedbug603e_break_isr( struct pt_regs *regs ) +{ + unsigned long addr; /* Address stopped at */ + /* -------------------------------------------------- */ + + bug_ctx.current_bp = 1; + addr = GET_IABR() & 0xFFFFFFFC; + + bedbug_main_loop( addr, regs ); + return; +} /* bedbug603e_break_isr */ + + + +/* ====================================================================== + * See if the hardware breakpoint is available. + * ====================================================================== */ + +int bedbug603e_find_empty( void ) +{ + /* -------------------------------------------------- */ + + if( (GET_IABR() && 0x00000002) == 0 ) + return 1; + + return 0; +} /* bedbug603e_find_empty */ + + + +/* ====================================================================== + * Set a breakpoint. If 'which_bp' is zero then find an unused breakpoint + * number, otherwise reassign the given breakpoint. If hardware debugging + * is not enabled, then turn it on via the MSR and DBCR0. Set the break + * address in the IABR register. + * ====================================================================== */ + +int bedbug603e_set( int which_bp, unsigned long addr ) +{ + /* -------------------------------------------------- */ + + if(( addr & 0x00000003 ) != 0 ) + { + puts ( "Breakpoints must be on a 32 bit boundary\n" ); + return 0; + } + + /* Only look if which_bp == 0, else use which_bp */ + if(( bug_ctx.find_empty ) && ( !which_bp ) && + ( which_bp = (*bug_ctx.find_empty)()) == 0 ) + { + puts ( "All breakpoints in use\n" ); + return 0; + } + + if( which_bp < 1 || which_bp > MAX_BREAK_POINTS ) + { + printf( "Invalid break point # %d\n", which_bp ); + return 0; + } + + if( ! bug_ctx.hw_debug_enabled ) + { + bug_ctx.hw_debug_enabled = 1; + } + + SET_IABR( addr | 0x00000002 ); + + return which_bp; +} /* bedbug603e_set */ + + + +/* ====================================================================== + * Disable a specific breakoint by setting the IABR register to zero. + * ====================================================================== */ + +int bedbug603e_clear( int which_bp ) +{ + /* -------------------------------------------------- */ + + if( which_bp < 1 || which_bp > MAX_BREAK_POINTS ) + { + printf( "Invalid break point # (%d)\n", which_bp ); + return -1; + } + + SET_IABR( 0 ); + + return 0; +} /* bedbug603e_clear */ + + +/* ====================================================================== */ +#endif diff --git a/arch/powerpc/cpu/mpc8260/commproc.c b/arch/powerpc/cpu/mpc8260/commproc.c new file mode 100644 index 0000000..111a67c --- /dev/null +++ b/arch/powerpc/cpu/mpc8260/commproc.c @@ -0,0 +1,221 @@ +/* + * This file is based on "arch/powerpc/8260_io/commproc.c" - here is it's + * copyright notice: + * + * General Purpose functions for the global management of the + * 8260 Communication Processor Module. + * Copyright (c) 1999 Dan Malek (dmalek@jlc.net) + * Copyright (c) 2000 MontaVista Software, Inc (source@mvista.com) + * 2.3.99 Updates + * + * In addition to the individual control of the communication + * channels, there are a few functions that globally affect the + * communication processor. + * + * Buffer descriptors must be allocated from the dual ported memory + * space. The allocator for that is here. When the communication + * process is reset, we reclaim the memory available. There is + * currently no deallocator for this memory. + */ +#include <common.h> +#include <asm/cpm_8260.h> + +DECLARE_GLOBAL_DATA_PTR; + +void +m8260_cpm_reset(void) +{ + volatile immap_t *immr = (immap_t *)CONFIG_SYS_IMMR; + volatile ulong count; + + /* Reclaim the DP memory for our use. + */ + gd->dp_alloc_base = CPM_DATAONLY_BASE; + gd->dp_alloc_top = gd->dp_alloc_base + CPM_DATAONLY_SIZE; + + /* + * Reset CPM + */ + immr->im_cpm.cp_cpcr = CPM_CR_RST; + count = 0; + do { /* Spin until command processed */ + __asm__ __volatile__ ("eieio"); + } while ((immr->im_cpm.cp_cpcr & CPM_CR_FLG) && ++count < 1000000); + +#ifdef CONFIG_HARD_I2C + *((unsigned short*)(&immr->im_dprambase[PROFF_I2C_BASE])) = 0; +#endif +} + +/* Allocate some memory from the dual ported ram. + * To help protocols with object alignment restrictions, we do that + * if they ask. + */ +uint +m8260_cpm_dpalloc(uint size, uint align) +{ + volatile immap_t *immr = (immap_t *)CONFIG_SYS_IMMR; + uint retloc; + uint align_mask, off; + uint savebase; + + align_mask = align - 1; + savebase = gd->dp_alloc_base; + + if ((off = (gd->dp_alloc_base & align_mask)) != 0) + gd->dp_alloc_base += (align - off); + + if ((off = size & align_mask) != 0) + size += align - off; + + if ((gd->dp_alloc_base + size) >= gd->dp_alloc_top) { + gd->dp_alloc_base = savebase; + panic("m8260_cpm_dpalloc: ran out of dual port ram!"); + } + + retloc = gd->dp_alloc_base; + gd->dp_alloc_base += size; + + memset((void *)&immr->im_dprambase[retloc], 0, size); + + return(retloc); +} + +/* We also own one page of host buffer space for the allocation of + * UART "fifos" and the like. + */ +uint +m8260_cpm_hostalloc(uint size, uint align) +{ + /* the host might not even have RAM yet - just use dual port RAM */ + return (m8260_cpm_dpalloc(size, align)); +} + +/* Set a baud rate generator. This needs lots of work. There are + * eight BRGs, which can be connected to the CPM channels or output + * as clocks. The BRGs are in two different block of internal + * memory mapped space. + * The baud rate clock is the system clock divided by something. + * It was set up long ago during the initial boot phase and is + * is given to us. + * Baud rate clocks are zero-based in the driver code (as that maps + * to port numbers). Documentation uses 1-based numbering. + */ +#define BRG_INT_CLK gd->brg_clk +#define BRG_UART_CLK (BRG_INT_CLK / 16) + +/* This function is used by UARTs, or anything else that uses a 16x + * oversampled clock. + */ +void +m8260_cpm_setbrg(uint brg, uint rate) +{ + volatile immap_t *immr = (immap_t *)CONFIG_SYS_IMMR; + volatile uint *bp; + uint cd = BRG_UART_CLK / rate; + + if ((BRG_UART_CLK % rate) < (rate / 2)) + cd--; + if (brg < 4) { + bp = (uint *)&immr->im_brgc1; + } + else { + bp = (uint *)&immr->im_brgc5; + brg -= 4; + } + bp += brg; + *bp = (cd << 1) | CPM_BRG_EN; +} + +/* This function is used to set high speed synchronous baud rate + * clocks. + */ +void +m8260_cpm_fastbrg(uint brg, uint rate, int div16) +{ + volatile immap_t *immr = (immap_t *)CONFIG_SYS_IMMR; + volatile uint *bp; + + /* This is good enough to get SMCs running..... + */ + if (brg < 4) { + bp = (uint *)&immr->im_brgc1; + } + else { + bp = (uint *)&immr->im_brgc5; + brg -= 4; + } + bp += brg; + *bp = (((((BRG_INT_CLK+rate-1)/rate)-1)&0xfff)<<1)|CPM_BRG_EN; + if (div16) + *bp |= CPM_BRG_DIV16; +} + +/* This function is used to set baud rate generators using an external + * clock source and 16x oversampling. + */ + +void +m8260_cpm_extcbrg(uint brg, uint rate, uint extclk, int pinsel) +{ + volatile immap_t *immr = (immap_t *)CONFIG_SYS_IMMR; + volatile uint *bp; + + if (brg < 4) { + bp = (uint *)&immr->im_brgc1; + } + else { + bp = (uint *)&immr->im_brgc5; + brg -= 4; + } + bp += brg; + *bp = ((((((extclk/16)+rate-1)/rate)-1)&0xfff)<<1)|CPM_BRG_EN; + if (pinsel == 0) + *bp |= CPM_BRG_EXTC_CLK3_9; + else + *bp |= CPM_BRG_EXTC_CLK5_15; +} + +#if defined(CONFIG_POST) || defined(CONFIG_LOGBUFFER) + +void post_word_store (ulong a) +{ + volatile ulong *save_addr = + (volatile ulong *)(CONFIG_SYS_IMMR + CPM_POST_WORD_ADDR); + + *save_addr = a; +} + +ulong post_word_load (void) +{ + volatile ulong *save_addr = + (volatile ulong *)(CONFIG_SYS_IMMR + CPM_POST_WORD_ADDR); + + return *save_addr; +} + +#endif /* CONFIG_POST || CONFIG_LOGBUFFER*/ + +#ifdef CONFIG_BOOTCOUNT_LIMIT + +void bootcount_store (ulong a) +{ + volatile ulong *save_addr = + (volatile ulong *)(CONFIG_SYS_IMMR + CPM_BOOTCOUNT_ADDR); + + save_addr[0] = a; + save_addr[1] = BOOTCOUNT_MAGIC; +} + +ulong bootcount_load (void) +{ + volatile ulong *save_addr = + (volatile ulong *)(CONFIG_SYS_IMMR + CPM_BOOTCOUNT_ADDR); + + if (save_addr[1] != BOOTCOUNT_MAGIC) + return 0; + else + return save_addr[0]; +} + +#endif /* CONFIG_BOOTCOUNT_LIMIT */ diff --git a/arch/powerpc/cpu/mpc8260/config.mk b/arch/powerpc/cpu/mpc8260/config.mk new file mode 100644 index 0000000..a9bb688 --- /dev/null +++ b/arch/powerpc/cpu/mpc8260/config.mk @@ -0,0 +1,30 @@ +# +# (C) Copyright 2000 +# Wolfgang Denk, DENX Software Engineering, wd@denx.de. +# +# See file CREDITS for list of people who contributed to this +# project. +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation; either version 2 of +# the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, +# MA 02111-1307 USA +# + +PLATFORM_RELFLAGS += -fPIC -meabi + +PLATFORM_CPPFLAGS += -DCONFIG_8260 -DCONFIG_CPM2 -ffixed-r2 \ + -mstring -mcpu=603e -mmultiple + +# Use default linker script. Board port can override in board/*/config.mk +LDSCRIPT := $(SRCTREE)/arch/powerpc/cpu/mpc8260/u-boot.lds diff --git a/arch/powerpc/cpu/mpc8260/cpu.c b/arch/powerpc/cpu/mpc8260/cpu.c new file mode 100644 index 0000000..aedbf29 --- /dev/null +++ b/arch/powerpc/cpu/mpc8260/cpu.c @@ -0,0 +1,338 @@ +/* + * (C) Copyright 2000-2006 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +/* + * CPU specific code for the MPC825x / MPC826x / MPC827x / MPC828x + * + * written or collected and sometimes rewritten by + * Magnus Damm <damm@bitsmart.com> + * + * modified by + * Wolfgang Denk <wd@denx.de> + * + * modified for 8260 by + * Murray Jensen <Murray.Jensen@cmst.csiro.au> + * + * added 8260 masks by + * Marius Groeger <mag@sysgo.de> + * + * added HiP7 (824x/827x/8280) processors support by + * Yuli Barcohen <yuli@arabellasw.com> + */ + +#include <common.h> +#include <watchdog.h> +#include <command.h> +#include <mpc8260.h> +#include <netdev.h> +#include <asm/processor.h> +#include <asm/cpm_8260.h> + +#if defined(CONFIG_OF_LIBFDT) +#include <libfdt.h> +#include <libfdt_env.h> +#include <fdt_support.h> +#endif + +DECLARE_GLOBAL_DATA_PTR; + +#if defined(CONFIG_GET_CPU_STR_F) +extern int get_cpu_str_f (char *buf); +#endif + +int checkcpu (void) +{ + volatile immap_t *immap = (immap_t *) CONFIG_SYS_IMMR; + ulong clock = gd->cpu_clk; + uint pvr = get_pvr (); + uint immr, rev, m, k; + char buf[32]; + + puts ("CPU: "); + + switch (pvr) { + case PVR_8260: + case PVR_8260_HIP3: + k = 3; + break; + case PVR_8260_HIP4: + k = 4; + break; + case PVR_8260_HIP7R1: + case PVR_8260_HIP7RA: + case PVR_8260_HIP7: + k = 7; + break; + default: + return -1; /* whoops! not an MPC8260 */ + } + rev = pvr & 0xff; + + immr = immap->im_memctl.memc_immr; + if ((immr & IMMR_ISB_MSK) != CONFIG_SYS_IMMR) + return -1; /* whoops! someone moved the IMMR */ + +#if defined(CONFIG_GET_CPU_STR_F) + get_cpu_str_f (buf); + printf ("%s (HiP%d Rev %02x, Mask ", buf, k, rev); +#else + printf (CPU_ID_STR " (HiP%d Rev %02x, Mask ", k, rev); +#endif + + /* + * the bottom 16 bits of the immr are the Part Number and Mask Number + * (4-34); the 16 bits at PROFF_REVNUM (0x8af0) in dual port ram is the + * RISC Microcode Revision Number (13-10). + * For the 8260, Motorola doesn't include the Microcode Revision + * in the mask. + */ + m = immr & (IMMR_PARTNUM_MSK | IMMR_MASKNUM_MSK); + k = *((ushort *) & immap->im_dprambase[PROFF_REVNUM]); + + switch (m) { + case 0x0000: + puts ("0.2 2J24M"); + break; + case 0x0010: + puts ("A.0 K22A"); + break; + case 0x0011: + puts ("A.1 1K22A-XC"); + break; + case 0x0001: + puts ("B.1 1K23A"); + break; + case 0x0021: + puts ("B.2 2K23A-XC"); + break; + case 0x0023: + puts ("B.3 3K23A"); + break; + case 0x0024: + puts ("C.2 6K23A"); + break; + case 0x0060: + puts ("A.0(A) 2K25A"); + break; + case 0x0062: + puts ("B.1 4K25A"); + break; + case 0x0064: + puts ("C.0 5K25A"); + break; + case 0x0A00: + puts ("0.0 0K49M"); + break; + case 0x0A01: + puts ("0.1 1K49M"); + break; + case 0x0A10: + puts ("1.0 1K49M"); + break; + case 0x0C00: + puts ("0.0 0K50M"); + break; + case 0x0C10: + puts ("1.0 1K50M"); + break; + case 0x0D00: + puts ("0.0 0K50M"); + break; + case 0x0D10: + puts ("1.0 1K50M"); + break; + default: + printf ("unknown [immr=0x%04x,k=0x%04x]", m, k); + break; + } + + printf (") at %s MHz\n", strmhz (buf, clock)); + + return 0; +} + +/* ------------------------------------------------------------------------- */ +/* configures a UPM by writing into the UPM RAM array */ +/* uses bank 11 and a dummy physical address (=BRx_BA_MSK) */ +/* NOTE: the physical address chosen must not overlap into any other area */ +/* mapped by the memory controller because bank 11 has the lowest priority */ + +void upmconfig (uint upm, uint * table, uint size) +{ + volatile immap_t *immap = (immap_t *) CONFIG_SYS_IMMR; + volatile memctl8260_t *memctl = &immap->im_memctl; + volatile uchar *dummy = (uchar *) BRx_BA_MSK; /* set all BA bits */ + uint i; + + /* first set up bank 11 to reference the correct UPM at a dummy address */ + + memctl->memc_or11 = ORxU_AM_MSK; /* set all AM bits */ + + switch (upm) { + + case UPMA: + memctl->memc_br11 = + ((uint)dummy & BRx_BA_MSK) | BRx_PS_32 | BRx_MS_UPMA | + BRx_V; + memctl->memc_mamr = MxMR_OP_WARR; + break; + + case UPMB: + memctl->memc_br11 = + ((uint)dummy & BRx_BA_MSK) | BRx_PS_32 | BRx_MS_UPMB | + BRx_V; + memctl->memc_mbmr = MxMR_OP_WARR; + break; + + case UPMC: + memctl->memc_br11 = + ((uint)dummy & BRx_BA_MSK) | BRx_PS_32 | BRx_MS_UPMC | + BRx_V; + memctl->memc_mcmr = MxMR_OP_WARR; + break; + + default: + panic ("upmconfig passed invalid UPM number (%u)\n", upm); + break; + + } + + /* + * at this point, the dummy address is set up to access the selected UPM, + * the MAD pointer is zero, and the MxMR OP is set for writing to RAM + * + * now we simply load the mdr with each word and poke the dummy address. + * the MAD is incremented on each access. + */ + + for (i = 0; i < size; i++) { + memctl->memc_mdr = table[i]; + *dummy = 0; + } + + /* now kill bank 11 */ + memctl->memc_br11 = 0; +} + +/* ------------------------------------------------------------------------- */ + +#if !defined(CONFIG_HAVE_OWN_RESET) +int +do_reset (cmd_tbl_t * cmdtp, int flag, int argc, char *argv[]) +{ + ulong msr, addr; + + volatile immap_t *immap = (immap_t *) CONFIG_SYS_IMMR; + + immap->im_clkrst.car_rmr = RMR_CSRE; /* Checkstop Reset enable */ + + /* Interrupts and MMU off */ + __asm__ __volatile__ ("mfmsr %0":"=r" (msr):); + + msr &= ~(MSR_ME | MSR_EE | MSR_IR | MSR_DR); + __asm__ __volatile__ ("mtmsr %0"::"r" (msr)); + + /* + * Trying to execute the next instruction at a non-existing address + * should cause a machine check, resulting in reset + */ +#ifdef CONFIG_SYS_RESET_ADDRESS + addr = CONFIG_SYS_RESET_ADDRESS; +#else + /* + * note: when CONFIG_SYS_MONITOR_BASE points to a RAM address, CONFIG_SYS_MONITOR_BASE + * - sizeof (ulong) is usually a valid address. Better pick an address + * known to be invalid on your system and assign it to CONFIG_SYS_RESET_ADDRESS. + */ + addr = CONFIG_SYS_MONITOR_BASE - sizeof (ulong); +#endif + ((void (*)(void)) addr) (); + return 1; + +} +#endif /* CONFIG_HAVE_OWN_RESET */ + +/* ------------------------------------------------------------------------- */ + +/* + * Get timebase clock frequency (like cpu_clk in Hz) + * + */ +unsigned long get_tbclk (void) +{ + ulong tbclk; + + tbclk = (gd->bus_clk + 3L) / 4L; + + return (tbclk); +} + +/* ------------------------------------------------------------------------- */ + +#if defined(CONFIG_WATCHDOG) +void watchdog_reset (void) +{ + int re_enable = disable_interrupts (); + + reset_8260_watchdog ((immap_t *) CONFIG_SYS_IMMR); + if (re_enable) + enable_interrupts (); +} +#endif /* CONFIG_WATCHDOG */ + +/* ------------------------------------------------------------------------- */ +#if defined(CONFIG_OF_LIBFDT) && defined (CONFIG_OF_BOARD_SETUP) +void ft_cpu_setup (void *blob, bd_t *bd) +{ +#if defined(CONFIG_HAS_ETH0) || defined(CONFIG_HAS_ETH1) ||\ + defined(CONFIG_HAS_ETH2) || defined(CONFIG_HAS_ETH3) + fdt_fixup_ethernet(blob); +#endif + + do_fixup_by_compat_u32(blob, "fsl,cpm2-brg", + "clock-frequency", bd->bi_brgfreq, 1); + + do_fixup_by_prop_u32(blob, "device_type", "cpu", 4, + "bus-frequency", bd->bi_busfreq, 1); + do_fixup_by_prop_u32(blob, "device_type", "cpu", 4, + "timebase-frequency", OF_TBCLK, 1); + do_fixup_by_prop_u32(blob, "device_type", "cpu", 4, + "clock-frequency", bd->bi_intfreq, 1); + fdt_fixup_memory(blob, (u64)bd->bi_memstart, (u64)bd->bi_memsize); +} +#endif /* CONFIG_OF_LIBFDT */ + +/* + * Initializes on-chip ethernet controllers. + * to override, implement board_eth_init() + */ +int cpu_eth_init(bd_t *bis) +{ +#if defined(CONFIG_ETHER_ON_FCC) + fec_initialize(bis); +#endif +#if defined(CONFIG_ETHER_ON_SCC) + mpc82xx_scc_enet_initialize(bis); +#endif + return 0; +} diff --git a/arch/powerpc/cpu/mpc8260/cpu_init.c b/arch/powerpc/cpu/mpc8260/cpu_init.c new file mode 100644 index 0000000..1d52773 --- /dev/null +++ b/arch/powerpc/cpu/mpc8260/cpu_init.c @@ -0,0 +1,292 @@ +/* + * (C) Copyright 2000-2002 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include <common.h> +#include <mpc8260.h> +#include <asm/cpm_8260.h> +#include <ioports.h> + +DECLARE_GLOBAL_DATA_PTR; + +#if defined(CONFIG_BOARD_GET_CPU_CLK_F) +extern unsigned long board_get_cpu_clk_f (void); +#endif + +static void config_8260_ioports (volatile immap_t * immr) +{ + int portnum; + + for (portnum = 0; portnum < 4; portnum++) { + uint pmsk = 0, + ppar = 0, + psor = 0, + pdir = 0, + podr = 0, + pdat = 0; + iop_conf_t *iopc = (iop_conf_t *) & iop_conf_tab[portnum][0]; + iop_conf_t *eiopc = iopc + 32; + uint msk = 1; + + /* + * NOTE: + * index 0 refers to pin 31, + * index 31 refers to pin 0 + */ + while (iopc < eiopc) { + if (iopc->conf) { + pmsk |= msk; + if (iopc->ppar) + ppar |= msk; + if (iopc->psor) + psor |= msk; + if (iopc->pdir) + pdir |= msk; + if (iopc->podr) + podr |= msk; + if (iopc->pdat) + pdat |= msk; + } + + msk <<= 1; + iopc++; + } + + if (pmsk != 0) { + volatile ioport_t *iop = ioport_addr (immr, portnum); + uint tpmsk = ~pmsk; + + /* + * the (somewhat confused) paragraph at the + * bottom of page 35-5 warns that there might + * be "unknown behaviour" when programming + * PSORx and PDIRx, if PPARx = 1, so I + * decided this meant I had to disable the + * dedicated function first, and enable it + * last. + */ + iop->ppar &= tpmsk; + iop->psor = (iop->psor & tpmsk) | psor; + iop->podr = (iop->podr & tpmsk) | podr; + iop->pdat = (iop->pdat & tpmsk) | pdat; + iop->pdir = (iop->pdir & tpmsk) | pdir; + iop->ppar |= ppar; + } + } +} + +#define SET_VAL_MASK(a, b, mask) ((a & mask) | (b & ~mask)) +/* + * Breath some life into the CPU... + * + * Set up the memory map, + * initialize a bunch of registers, + * initialize the UPM's + */ +void cpu_init_f (volatile immap_t * immr) +{ +#if !defined(CONFIG_COGENT) /* done in start.S for the cogent */ + uint sccr; +#endif +#if defined(CONFIG_BOARD_GET_CPU_CLK_F) + unsigned long cpu_clk; +#endif + volatile memctl8260_t *memctl = &immr->im_memctl; + extern void m8260_cpm_reset (void); + + /* Pointer is writable since we allocated a register for it */ + gd = (gd_t *) (CONFIG_SYS_INIT_RAM_ADDR + CONFIG_SYS_GBL_DATA_OFFSET); + + /* Clear initial global data */ + memset ((void *) gd, 0, sizeof (gd_t)); + + /* RSR - Reset Status Register - clear all status (5-4) */ + gd->reset_status = immr->im_clkrst.car_rsr; + immr->im_clkrst.car_rsr = RSR_ALLBITS; + + /* RMR - Reset Mode Register - contains checkstop reset enable (5-5) */ + immr->im_clkrst.car_rmr = CONFIG_SYS_RMR; + + /* BCR - Bus Configuration Register (4-25) */ +#if defined(CONFIG_SYS_BCR_60x) && (CONFIG_SYS_BCR_SINGLE) + if (immr->im_siu_conf.sc_bcr & BCR_EBM) { + immr->im_siu_conf.sc_bcr = SET_VAL_MASK(immr->im_siu_conf.sc_bcr, CONFIG_SYS_BCR_60x, 0x80000010); + } else { + immr->im_siu_conf.sc_bcr = SET_VAL_MASK(immr->im_siu_conf.sc_bcr, CONFIG_SYS_BCR_SINGLE, 0x80000010); + } +#else + immr->im_siu_conf.sc_bcr = CONFIG_SYS_BCR; +#endif + + /* SIUMCR - contains debug pin configuration (4-31) */ +#if defined(CONFIG_SYS_SIUMCR_LOW) && (CONFIG_SYS_SIUMCR_HIGH) + cpu_clk = board_get_cpu_clk_f (); + if (cpu_clk >= 100000000) { + immr->im_siu_conf.sc_siumcr = SET_VAL_MASK(immr->im_siu_conf.sc_siumcr, CONFIG_SYS_SIUMCR_HIGH, 0x9f3cc000); + } else { + immr->im_siu_conf.sc_siumcr = SET_VAL_MASK(immr->im_siu_conf.sc_siumcr, CONFIG_SYS_SIUMCR_LOW, 0x9f3cc000); + } +#else + immr->im_siu_conf.sc_siumcr = CONFIG_SYS_SIUMCR; +#endif + + config_8260_ioports (immr); + + /* initialize time counter status and control register (4-40) */ + immr->im_sit.sit_tmcntsc = CONFIG_SYS_TMCNTSC; + + /* initialize the PIT (4-42) */ + immr->im_sit.sit_piscr = CONFIG_SYS_PISCR; + +#if !defined(CONFIG_COGENT) /* done in start.S for the cogent */ + /* System clock control register (9-8) */ + sccr = immr->im_clkrst.car_sccr & + (SCCR_PCI_MODE | SCCR_PCI_MODCK | SCCR_PCIDF_MSK); + immr->im_clkrst.car_sccr = sccr | + (CONFIG_SYS_SCCR & ~(SCCR_PCI_MODE | SCCR_PCI_MODCK | SCCR_PCIDF_MSK) ); +#endif /* !CONFIG_COGENT */ + + /* + * Memory Controller: + */ + + /* Map banks 0 and 1 to the FLASH banks 0 and 1 at preliminary + * addresses - these have to be modified later when FLASH size + * has been determined + */ + +#if defined(CONFIG_SYS_OR0_REMAP) + memctl->memc_or0 = CONFIG_SYS_OR0_REMAP; +#endif +#if defined(CONFIG_SYS_OR1_REMAP) + memctl->memc_or1 = CONFIG_SYS_OR1_REMAP; +#endif + + /* now restrict to preliminary range */ + /* the PS came from the HRCW, don´t change it */ + memctl->memc_br0 = SET_VAL_MASK(memctl->memc_br0 , CONFIG_SYS_BR0_PRELIM, BRx_PS_MSK); + memctl->memc_or0 = CONFIG_SYS_OR0_PRELIM; + +#if defined(CONFIG_SYS_BR1_PRELIM) && defined(CONFIG_SYS_OR1_PRELIM) + memctl->memc_or1 = CONFIG_SYS_OR1_PRELIM; + memctl->memc_br1 = CONFIG_SYS_BR1_PRELIM; +#endif + +#if defined(CONFIG_SYS_BR2_PRELIM) && defined(CONFIG_SYS_OR2_PRELIM) + memctl->memc_or2 = CONFIG_SYS_OR2_PRELIM; + memctl->memc_br2 = CONFIG_SYS_BR2_PRELIM; +#endif + +#if defined(CONFIG_SYS_BR3_PRELIM) && defined(CONFIG_SYS_OR3_PRELIM) + memctl->memc_or3 = CONFIG_SYS_OR3_PRELIM; + memctl->memc_br3 = CONFIG_SYS_BR3_PRELIM; +#endif + +#if defined(CONFIG_SYS_BR4_PRELIM) && defined(CONFIG_SYS_OR4_PRELIM) + memctl->memc_or4 = CONFIG_SYS_OR4_PRELIM; + memctl->memc_br4 = CONFIG_SYS_BR4_PRELIM; +#endif + +#if defined(CONFIG_SYS_BR5_PRELIM) && defined(CONFIG_SYS_OR5_PRELIM) + memctl->memc_or5 = CONFIG_SYS_OR5_PRELIM; + memctl->memc_br5 = CONFIG_SYS_BR5_PRELIM; +#endif + +#if defined(CONFIG_SYS_BR6_PRELIM) && defined(CONFIG_SYS_OR6_PRELIM) + memctl->memc_or6 = CONFIG_SYS_OR6_PRELIM; + memctl->memc_br6 = CONFIG_SYS_BR6_PRELIM; +#endif + +#if defined(CONFIG_SYS_BR7_PRELIM) && defined(CONFIG_SYS_OR7_PRELIM) + memctl->memc_or7 = CONFIG_SYS_OR7_PRELIM; + memctl->memc_br7 = CONFIG_SYS_BR7_PRELIM; +#endif + +#if defined(CONFIG_SYS_BR8_PRELIM) && defined(CONFIG_SYS_OR8_PRELIM) + memctl->memc_or8 = CONFIG_SYS_OR8_PRELIM; + memctl->memc_br8 = CONFIG_SYS_BR8_PRELIM; +#endif + +#if defined(CONFIG_SYS_BR9_PRELIM) && defined(CONFIG_SYS_OR9_PRELIM) + memctl->memc_or9 = CONFIG_SYS_OR9_PRELIM; + memctl->memc_br9 = CONFIG_SYS_BR9_PRELIM; +#endif + +#if defined(CONFIG_SYS_BR10_PRELIM) && defined(CONFIG_SYS_OR10_PRELIM) + memctl->memc_or10 = CONFIG_SYS_OR10_PRELIM; + memctl->memc_br10 = CONFIG_SYS_BR10_PRELIM; +#endif + +#if defined(CONFIG_SYS_BR11_PRELIM) && defined(CONFIG_SYS_OR11_PRELIM) + memctl->memc_or11 = CONFIG_SYS_OR11_PRELIM; + memctl->memc_br11 = CONFIG_SYS_BR11_PRELIM; +#endif + + m8260_cpm_reset (); +} + +/* + * initialize higher level parts of CPU like time base and timers + */ +int cpu_init_r (void) +{ + volatile immap_t *immr = (immap_t *) gd->bd->bi_immr_base; + + immr->im_cpm.cp_rccr = CONFIG_SYS_RCCR; + + return (0); +} + +/* + * print out the reason for the reset + */ +int prt_8260_rsr (void) +{ + static struct { + ulong mask; + char *desc; + } bits[] = { + { + RSR_JTRS, "JTAG"}, { + RSR_CSRS, "Check Stop"}, { + RSR_SWRS, "Software Watchdog"}, { + RSR_BMRS, "Bus Monitor"}, { + RSR_ESRS, "External Soft"}, { + RSR_EHRS, "External Hard"} + }; + static int n = sizeof bits / sizeof bits[0]; + ulong rsr = gd->reset_status; + int i; + char *sep; + + puts (CPU_ID_STR " Reset Status:"); + + sep = " "; + for (i = 0; i < n; i++) + if (rsr & bits[i].mask) { + printf ("%s%s", sep, bits[i].desc); + sep = ", "; + } + + puts ("\n\n"); + return (0); +} diff --git a/arch/powerpc/cpu/mpc8260/ether_fcc.c b/arch/powerpc/cpu/mpc8260/ether_fcc.c new file mode 100644 index 0000000..5ac02a0 --- /dev/null +++ b/arch/powerpc/cpu/mpc8260/ether_fcc.c @@ -0,0 +1,1190 @@ +/* + * MPC8260 FCC Fast Ethernet + * + * Copyright (c) 2000 MontaVista Software, Inc. Dan Malek (dmalek@jlc.net) + * + * (C) Copyright 2000 Sysgo Real-Time Solutions, GmbH <www.elinos.com> + * Marius Groeger <mgroeger@sysgo.de> + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +/* + * MPC8260 FCC Fast Ethernet + * Basic ET HW initialization and packet RX/TX routines + * + * This code will not perform the IO port configuration. This should be + * done in the iop_conf_t structure specific for the board. + * + * TODO: + * add a PHY driver to do the negotiation + * reflect negotiation results in FPSMR + * look for ways to configure the board specific stuff elsewhere, eg. + * config_xxx.h or the board directory + */ + +#include <common.h> +#include <malloc.h> +#include <asm/cpm_8260.h> +#include <mpc8260.h> +#include <command.h> +#include <config.h> +#include <net.h> + +#if defined(CONFIG_MII) || defined(CONFIG_CMD_MII) +#include <miiphy.h> +#endif + +DECLARE_GLOBAL_DATA_PTR; + +#if defined(CONFIG_ETHER_ON_FCC) && defined(CONFIG_CMD_NET) && \ + defined(CONFIG_NET_MULTI) + +static struct ether_fcc_info_s +{ + int ether_index; + int proff_enet; + ulong cpm_cr_enet_sblock; + ulong cpm_cr_enet_page; + ulong cmxfcr_mask; + ulong cmxfcr_value; +} + ether_fcc_info[] = +{ +#ifdef CONFIG_ETHER_ON_FCC1 +{ + 0, + PROFF_FCC1, + CPM_CR_FCC1_SBLOCK, + CPM_CR_FCC1_PAGE, + CONFIG_SYS_CMXFCR_MASK1, + CONFIG_SYS_CMXFCR_VALUE1 +}, +#endif + +#ifdef CONFIG_ETHER_ON_FCC2 +{ + 1, + PROFF_FCC2, + CPM_CR_FCC2_SBLOCK, + CPM_CR_FCC2_PAGE, + CONFIG_SYS_CMXFCR_MASK2, + CONFIG_SYS_CMXFCR_VALUE2 +}, +#endif + +#ifdef CONFIG_ETHER_ON_FCC3 +{ + 2, + PROFF_FCC3, + CPM_CR_FCC3_SBLOCK, + CPM_CR_FCC3_PAGE, + CONFIG_SYS_CMXFCR_MASK3, + CONFIG_SYS_CMXFCR_VALUE3 +}, +#endif +}; + +/*---------------------------------------------------------------------*/ + +/* Maximum input DMA size. Must be a should(?) be a multiple of 4. */ +#define PKT_MAXDMA_SIZE 1520 + +/* The FCC stores dest/src/type, data, and checksum for receive packets. */ +#define PKT_MAXBUF_SIZE 1518 +#define PKT_MINBUF_SIZE 64 + +/* Maximum input buffer size. Must be a multiple of 32. */ +#define PKT_MAXBLR_SIZE 1536 + +#define TOUT_LOOP 1000000 + +#define TX_BUF_CNT 2 +#ifdef __GNUC__ +static char txbuf[TX_BUF_CNT][PKT_MAXBLR_SIZE] __attribute__ ((aligned(8))); +#else +#error "txbuf must be 64-bit aligned" +#endif + +static uint rxIdx; /* index of the current RX buffer */ +static uint txIdx; /* index of the current TX buffer */ + +/* + * FCC Ethernet Tx and Rx buffer descriptors. + * Provide for Double Buffering + * Note: PKTBUFSRX is defined in net.h + */ + +typedef volatile struct rtxbd { + cbd_t rxbd[PKTBUFSRX]; + cbd_t txbd[TX_BUF_CNT]; +} RTXBD; + +/* Good news: the FCC supports external BDs! */ +#ifdef __GNUC__ +static RTXBD rtx __attribute__ ((aligned(8))); +#else +#error "rtx must be 64-bit aligned" +#endif + +static int fec_send(struct eth_device* dev, volatile void *packet, int length) +{ + int i; + int result = 0; + + if (length <= 0) { + printf("fec: bad packet size: %d\n", length); + goto out; + } + + for(i=0; rtx.txbd[txIdx].cbd_sc & BD_ENET_TX_READY; i++) { + if (i >= TOUT_LOOP) { + puts ("fec: tx buffer not ready\n"); + goto out; + } + } + + rtx.txbd[txIdx].cbd_bufaddr = (uint)packet; + rtx.txbd[txIdx].cbd_datlen = length; + rtx.txbd[txIdx].cbd_sc |= (BD_ENET_TX_READY | BD_ENET_TX_LAST | + BD_ENET_TX_WRAP); + + for(i=0; rtx.txbd[txIdx].cbd_sc & BD_ENET_TX_READY; i++) { + if (i >= TOUT_LOOP) { + puts ("fec: tx error\n"); + goto out; + } + } + +#ifdef ET_DEBUG + printf("cycles: %d status: %04x\n", i, rtx.txbd[txIdx].cbd_sc); +#endif + + /* return only status bits */ + result = rtx.txbd[txIdx].cbd_sc & BD_ENET_TX_STATS; + +out: + return result; +} + +static int fec_recv(struct eth_device* dev) +{ + int length; + + for (;;) + { + if (rtx.rxbd[rxIdx].cbd_sc & BD_ENET_RX_EMPTY) { + length = -1; + break; /* nothing received - leave for() loop */ + } + length = rtx.rxbd[rxIdx].cbd_datlen; + + if (rtx.rxbd[rxIdx].cbd_sc & 0x003f) { + printf("fec: rx error %04x\n", rtx.rxbd[rxIdx].cbd_sc); + } + else { + /* Pass the packet up to the protocol layers. */ + NetReceive(NetRxPackets[rxIdx], length - 4); + } + + + /* Give the buffer back to the FCC. */ + rtx.rxbd[rxIdx].cbd_datlen = 0; + + /* wrap around buffer index when necessary */ + if ((rxIdx + 1) >= PKTBUFSRX) { + rtx.rxbd[PKTBUFSRX - 1].cbd_sc = (BD_ENET_RX_WRAP | BD_ENET_RX_EMPTY); + rxIdx = 0; + } + else { + rtx.rxbd[rxIdx].cbd_sc = BD_ENET_RX_EMPTY; + rxIdx++; + } + } + return length; +} + + +static int fec_init(struct eth_device* dev, bd_t *bis) +{ + struct ether_fcc_info_s * info = dev->priv; + int i; + volatile immap_t *immr = (immap_t *)CONFIG_SYS_IMMR; + volatile cpm8260_t *cp = &(immr->im_cpm); + fcc_enet_t *pram_ptr; + unsigned long mem_addr; + +#if 0 + mii_discover_phy(); +#endif + + /* 28.9 - (1-2): ioports have been set up already */ + + /* 28.9 - (3): connect FCC's tx and rx clocks */ + immr->im_cpmux.cmx_uar = 0; + immr->im_cpmux.cmx_fcr = (immr->im_cpmux.cmx_fcr & ~info->cmxfcr_mask) | + info->cmxfcr_value; + + /* 28.9 - (4): GFMR: disable tx/rx, CCITT CRC, Mode Ethernet */ + immr->im_fcc[info->ether_index].fcc_gfmr = + FCC_GFMR_MODE_ENET | FCC_GFMR_TCRC_32; + + /* 28.9 - (5): FPSMR: enable full duplex, select CCITT CRC for Ethernet */ + immr->im_fcc[info->ether_index].fcc_fpsmr = CONFIG_SYS_FCC_PSMR | FCC_PSMR_ENCRC; + + /* 28.9 - (6): FDSR: Ethernet Syn */ + immr->im_fcc[info->ether_index].fcc_fdsr = 0xD555; + + /* reset indeces to current rx/tx bd (see eth_send()/eth_rx()) */ + rxIdx = 0; + txIdx = 0; + + /* Setup Receiver Buffer Descriptors */ + for (i = 0; i < PKTBUFSRX; i++) + { + rtx.rxbd[i].cbd_sc = BD_ENET_RX_EMPTY; + rtx.rxbd[i].cbd_datlen = 0; + rtx.rxbd[i].cbd_bufaddr = (uint)NetRxPackets[i]; + } + rtx.rxbd[PKTBUFSRX - 1].cbd_sc |= BD_ENET_RX_WRAP; + + /* Setup Ethernet Transmitter Buffer Descriptors */ + for (i = 0; i < TX_BUF_CNT; i++) + { + rtx.txbd[i].cbd_sc = (BD_ENET_TX_PAD | BD_ENET_TX_LAST | BD_ENET_TX_TC); + rtx.txbd[i].cbd_datlen = 0; + rtx.txbd[i].cbd_bufaddr = (uint)&txbuf[i][0]; + } + rtx.txbd[TX_BUF_CNT - 1].cbd_sc |= BD_ENET_TX_WRAP; + + /* 28.9 - (7): initialise parameter ram */ + pram_ptr = (fcc_enet_t *)&(immr->im_dprambase[info->proff_enet]); + + /* clear whole structure to make sure all reserved fields are zero */ + memset((void*)pram_ptr, 0, sizeof(fcc_enet_t)); + + /* + * common Parameter RAM area + * + * Allocate space in the reserved FCC area of DPRAM for the + * internal buffers. No one uses this space (yet), so we + * can do this. Later, we will add resource management for + * this area. + */ + mem_addr = CPM_FCC_SPECIAL_BASE + ((info->ether_index) * 64); + pram_ptr->fen_genfcc.fcc_riptr = mem_addr; + pram_ptr->fen_genfcc.fcc_tiptr = mem_addr+32; + /* + * Set maximum bytes per receive buffer. + * It must be a multiple of 32. + */ + pram_ptr->fen_genfcc.fcc_mrblr = PKT_MAXBLR_SIZE; + pram_ptr->fen_genfcc.fcc_rstate = (CPMFCR_GBL | CPMFCR_EB | + CONFIG_SYS_CPMFCR_RAMTYPE) << 24; + pram_ptr->fen_genfcc.fcc_rbase = (unsigned int)(&rtx.rxbd[rxIdx]); + pram_ptr->fen_genfcc.fcc_tstate = (CPMFCR_GBL | CPMFCR_EB | + CONFIG_SYS_CPMFCR_RAMTYPE) << 24; + pram_ptr->fen_genfcc.fcc_tbase = (unsigned int)(&rtx.txbd[txIdx]); + + /* protocol-specific area */ + pram_ptr->fen_cmask = 0xdebb20e3; /* CRC mask */ + pram_ptr->fen_cpres = 0xffffffff; /* CRC preset */ + pram_ptr->fen_retlim = 15; /* Retry limit threshold */ + pram_ptr->fen_mflr = PKT_MAXBUF_SIZE; /* maximum frame length register */ + /* + * Set Ethernet station address. + * + * This is supplied in the board information structure, so we + * copy that into the controller. + * So, far we have only been given one Ethernet address. We make + * it unique by setting a few bits in the upper byte of the + * non-static part of the address. + */ +#define ea eth_get_dev()->enetaddr + pram_ptr->fen_paddrh = (ea[5] << 8) + ea[4]; + pram_ptr->fen_paddrm = (ea[3] << 8) + ea[2]; + pram_ptr->fen_paddrl = (ea[1] << 8) + ea[0]; +#undef ea + pram_ptr->fen_minflr = PKT_MINBUF_SIZE; /* minimum frame length register */ + /* pad pointer. use tiptr since we don't need a specific padding char */ + pram_ptr->fen_padptr = pram_ptr->fen_genfcc.fcc_tiptr; + pram_ptr->fen_maxd1 = PKT_MAXDMA_SIZE; /* maximum DMA1 length */ + pram_ptr->fen_maxd2 = PKT_MAXDMA_SIZE; /* maximum DMA2 length */ + pram_ptr->fen_rfthr = 1; + pram_ptr->fen_rfcnt = 1; +#if 0 + printf("pram_ptr->fen_genfcc.fcc_rbase %08lx\n", + pram_ptr->fen_genfcc.fcc_rbase); + printf("pram_ptr->fen_genfcc.fcc_tbase %08lx\n", + pram_ptr->fen_genfcc.fcc_tbase); +#endif + + /* 28.9 - (8): clear out events in FCCE */ + immr->im_fcc[info->ether_index].fcc_fcce = ~0x0; + + /* 28.9 - (9): FCCM: mask all events */ + immr->im_fcc[info->ether_index].fcc_fccm = 0; + + /* 28.9 - (10-12): we don't use ethernet interrupts */ + + /* 28.9 - (13) + * + * Let's re-initialize the channel now. We have to do it later + * than the manual describes because we have just now finished + * the BD initialization. + */ + cp->cp_cpcr = mk_cr_cmd(info->cpm_cr_enet_page, + info->cpm_cr_enet_sblock, + 0x0c, + CPM_CR_INIT_TRX) | CPM_CR_FLG; + do { + __asm__ __volatile__ ("eieio"); + } while (cp->cp_cpcr & CPM_CR_FLG); + + /* 28.9 - (14): enable tx/rx in gfmr */ + immr->im_fcc[info->ether_index].fcc_gfmr |= FCC_GFMR_ENT | FCC_GFMR_ENR; + + return 1; +} + +static void fec_halt(struct eth_device* dev) +{ + struct ether_fcc_info_s * info = dev->priv; + volatile immap_t *immr = (immap_t *)CONFIG_SYS_IMMR; + + /* write GFMR: disable tx/rx */ + immr->im_fcc[info->ether_index].fcc_gfmr &= + ~(FCC_GFMR_ENT | FCC_GFMR_ENR); +} + +int fec_initialize(bd_t *bis) +{ + struct eth_device* dev; + int i; + + for (i = 0; i < sizeof(ether_fcc_info) / sizeof(ether_fcc_info[0]); i++) + { + dev = (struct eth_device*) malloc(sizeof *dev); + memset(dev, 0, sizeof *dev); + + sprintf(dev->name, "FCC%d ETHERNET", + ether_fcc_info[i].ether_index + 1); + dev->priv = ðer_fcc_info[i]; + dev->init = fec_init; + dev->halt = fec_halt; + dev->send = fec_send; + dev->recv = fec_recv; + + eth_register(dev); + +#if (defined(CONFIG_MII) || defined(CONFIG_CMD_MII)) \ + && defined(CONFIG_BITBANGMII) + miiphy_register(dev->name, + bb_miiphy_read, bb_miiphy_write); +#endif + } + + return 1; +} + +#ifdef CONFIG_ETHER_LOOPBACK_TEST + +#define ELBT_BUFSZ 1024 /* must be multiple of 32 */ + +#define ELBT_CRCSZ 4 + +#define ELBT_NRXBD 4 /* must be at least 2 */ +#define ELBT_NTXBD 4 + +#define ELBT_MAXRXERR 32 +#define ELBT_MAXTXERR 32 + +#define ELBT_CLSWAIT 1000 /* msec to wait for further input frames */ + +typedef + struct { + uint off; + char *lab; + } +elbt_prdesc; + +typedef + struct { + uint _l, _f, m, bc, mc, lg, no, sh, cr, ov, cl; + uint badsrc, badtyp, badlen, badbit; + } +elbt_rxeacc; + +static elbt_prdesc rxeacc_descs[] = { + { offsetof(elbt_rxeacc, _l), "Not Last in Frame" }, + { offsetof(elbt_rxeacc, _f), "Not First in Frame" }, + { offsetof(elbt_rxeacc, m), "Address Miss" }, + { offsetof(elbt_rxeacc, bc), "Broadcast Address" }, + { offsetof(elbt_rxeacc, mc), "Multicast Address" }, + { offsetof(elbt_rxeacc, lg), "Frame Length Violation"}, + { offsetof(elbt_rxeacc, no), "Non-Octet Alignment" }, + { offsetof(elbt_rxeacc, sh), "Short Frame" }, + { offsetof(elbt_rxeacc, cr), "CRC Error" }, + { offsetof(elbt_rxeacc, ov), "Overrun" }, + { offsetof(elbt_rxeacc, cl), "Collision" }, + { offsetof(elbt_rxeacc, badsrc), "Bad Src Address" }, + { offsetof(elbt_rxeacc, badtyp), "Bad Frame Type" }, + { offsetof(elbt_rxeacc, badlen), "Bad Frame Length" }, + { offsetof(elbt_rxeacc, badbit), "Data Compare Errors" }, +}; +static int rxeacc_ndesc = sizeof (rxeacc_descs) / sizeof (rxeacc_descs[0]); + +typedef + struct { + uint def, hb, lc, rl, rc, un, csl; + } +elbt_txeacc; + +static elbt_prdesc txeacc_descs[] = { + { offsetof(elbt_txeacc, def), "Defer Indication" }, + { offsetof(elbt_txeacc, hb), "Heartbeat" }, + { offsetof(elbt_txeacc, lc), "Late Collision" }, + { offsetof(elbt_txeacc, rl), "Retransmission Limit" }, + { offsetof(elbt_txeacc, rc), "Retry Count" }, + { offsetof(elbt_txeacc, un), "Underrun" }, + { offsetof(elbt_txeacc, csl), "Carrier Sense Lost" }, +}; +static int txeacc_ndesc = sizeof (txeacc_descs) / sizeof (txeacc_descs[0]); + +typedef + struct { + uchar rxbufs[ELBT_NRXBD][ELBT_BUFSZ]; + uchar txbufs[ELBT_NTXBD][ELBT_BUFSZ]; + cbd_t rxbd[ELBT_NRXBD]; + cbd_t txbd[ELBT_NTXBD]; + enum { Idle, Running, Closing, Closed } state; + int proff, page, sblock; + uint clstime, nsent, ntxerr, nrcvd, nrxerr; + ushort rxerrs[ELBT_MAXRXERR], txerrs[ELBT_MAXTXERR]; + elbt_rxeacc rxeacc; + elbt_txeacc txeacc; + } __attribute__ ((aligned(8))) +elbt_chan; + +static uchar patbytes[ELBT_NTXBD] = { + 0xff, 0xaa, 0x55, 0x00 +}; +static uint patwords[ELBT_NTXBD] = { + 0xffffffff, 0xaaaaaaaa, 0x55555555, 0x00000000 +}; + +#ifdef __GNUC__ +static elbt_chan elbt_chans[3] __attribute__ ((aligned(8))); +#else +#error "elbt_chans must be 64-bit aligned" +#endif + +#define CPM_CR_GRACEFUL_STOP_TX ((ushort)0x0005) + +static elbt_prdesc epram_descs[] = { + { offsetof(fcc_enet_t, fen_crcec), "CRC Errors" }, + { offsetof(fcc_enet_t, fen_alec), "Alignment Errors" }, + { offsetof(fcc_enet_t, fen_disfc), "Discarded Frames" }, + { offsetof(fcc_enet_t, fen_octc), "Octets" }, + { offsetof(fcc_enet_t, fen_colc), "Collisions" }, + { offsetof(fcc_enet_t, fen_broc), "Broadcast Frames" }, + { offsetof(fcc_enet_t, fen_mulc), "Multicast Frames" }, + { offsetof(fcc_enet_t, fen_uspc), "Undersize Frames" }, + { offsetof(fcc_enet_t, fen_frgc), "Fragments" }, + { offsetof(fcc_enet_t, fen_ospc), "Oversize Frames" }, + { offsetof(fcc_enet_t, fen_jbrc), "Jabbers" }, + { offsetof(fcc_enet_t, fen_p64c), "64 Octet Frames" }, + { offsetof(fcc_enet_t, fen_p65c), "65-127 Octet Frames" }, + { offsetof(fcc_enet_t, fen_p128c), "128-255 Octet Frames" }, + { offsetof(fcc_enet_t, fen_p256c), "256-511 Octet Frames" }, + { offsetof(fcc_enet_t, fen_p512c), "512-1023 Octet Frames" }, + { offsetof(fcc_enet_t, fen_p1024c), "1024-1518 Octet Frames"}, +}; +static int epram_ndesc = sizeof (epram_descs) / sizeof (epram_descs[0]); + +/* + * given an elbt_prdesc array and an array of base addresses, print + * each prdesc down the screen with the values fetched from each + * base address across the screen + */ +static void +print_desc (elbt_prdesc descs[], int ndesc, uchar *bases[], int nbase) +{ + elbt_prdesc *dp = descs, *edp = dp + ndesc; + int i; + + printf ("%32s", ""); + + for (i = 0; i < nbase; i++) + printf (" Channel %d", i); + + putc ('\n'); + + while (dp < edp) { + + printf ("%-32s", dp->lab); + + for (i = 0; i < nbase; i++) { + uint val = *(uint *)(bases[i] + dp->off); + + printf (" %10u", val); + } + + putc ('\n'); + + dp++; + } +} + +/* + * return number of bits that are set in a value; value contains + * nbits (right-justified) bits. + */ +static uint __inline__ +nbs (uint value, uint nbits) +{ + uint cnt = 0; +#if 1 + uint pos = sizeof (uint) * 8; + + __asm__ __volatile__ ("\ + mtctr %2\n\ +1: rlwnm. %2,%1,%4,31,31\n\ + beq 2f\n\ + addi %0,%0,1\n\ +2: subi %4,%4,1\n\ + bdnz 1b" + : "=r"(cnt) + : "r"(value), "r"(nbits), "r"(cnt), "r"(pos) + : "ctr", "cc" ); +#else + uint mask = 1; + + do { + if (value & mask) + cnt++; + mask <<= 1; + } while (--nbits); +#endif + + return (cnt); +} + +static ulong +badbits (uchar *bp, int n, ulong pat) +{ + ulong *lp, cnt = 0; + int nl; + + while (n > 0 && ((ulong)bp & (sizeof (ulong) - 1)) != 0) { + uchar diff; + + diff = *bp++ ^ (uchar)pat; + + if (diff) + cnt += nbs ((ulong)diff, 8); + + n--; + } + + lp = (ulong *)bp; + nl = n / sizeof (ulong); + n -= nl * sizeof (ulong); + + while (nl > 0) { + ulong diff; + + diff = *lp++ ^ pat; + + if (diff) + cnt += nbs (diff, 32); + + nl--; + } + + bp = (uchar *)lp; + + while (n > 0) { + uchar diff; + + diff = *bp++ ^ (uchar)pat; + + if (diff) + cnt += nbs ((ulong)diff, 8); + + n--; + } + + return (cnt); +} + +static inline unsigned short +swap16 (unsigned short x) +{ + return (((x & 0xff) << 8) | ((x & 0xff00) >> 8)); +} + +/* broadcast is not an error - we send them like that */ +#define BD_ENET_RX_ERRS (BD_ENET_RX_STATS & ~BD_ENET_RX_BC) + +void +eth_loopback_test (void) +{ + volatile immap_t *immr = (immap_t *)CONFIG_SYS_IMMR; + volatile cpm8260_t *cp = &(immr->im_cpm); + int c, nclosed; + ulong runtime, nmsec; + uchar *bases[3]; + + puts ("FCC Ethernet External loopback test\n"); + + eth_getenv_enetaddr("ethaddr", NetOurEther); + + /* + * global initialisations for all FCC channels + */ + + /* 28.9 - (1-2): ioports have been set up already */ + +#if defined(CONFIG_HYMOD) + /* + * Attention: this is board-specific + * 0, FCC1 + * 1, FCC2 + * 2, FCC3 + */ +# define FCC_START_LOOP 0 +# define FCC_END_LOOP 2 + + /* + * Attention: this is board-specific + * - FCC1 Rx-CLK is CLK10 + * - FCC1 Tx-CLK is CLK11 + * - FCC2 Rx-CLK is CLK13 + * - FCC2 Tx-CLK is CLK14 + * - FCC3 Rx-CLK is CLK15 + * - FCC3 Tx-CLK is CLK16 + */ + + /* 28.9 - (3): connect FCC's tx and rx clocks */ + immr->im_cpmux.cmx_uar = 0; + immr->im_cpmux.cmx_fcr = CMXFCR_RF1CS_CLK10|CMXFCR_TF1CS_CLK11|\ + CMXFCR_RF2CS_CLK13|CMXFCR_TF2CS_CLK14|\ + CMXFCR_RF3CS_CLK15|CMXFCR_TF3CS_CLK16; +#elif defined(CONFIG_SBC8260) || defined(CONFIG_SACSng) + /* + * Attention: this is board-specific + * 1, FCC2 + */ +# define FCC_START_LOOP 1 +# define FCC_END_LOOP 1 + + /* + * Attention: this is board-specific + * - FCC2 Rx-CLK is CLK13 + * - FCC2 Tx-CLK is CLK14 + */ + + /* 28.9 - (3): connect FCC's tx and rx clocks */ + immr->im_cpmux.cmx_uar = 0; + immr->im_cpmux.cmx_fcr = CMXFCR_RF2CS_CLK13|CMXFCR_TF2CS_CLK14; +#else +#error "eth_loopback_test not supported on your board" +#endif + + puts ("Initialise FCC channels:"); + + for (c = FCC_START_LOOP; c <= FCC_END_LOOP; c++) { + elbt_chan *ecp = &elbt_chans[c]; + volatile fcc_t *fcp = &immr->im_fcc[c]; + volatile fcc_enet_t *fpp; + int i; + ulong addr; + + /* + * initialise channel data + */ + + printf (" %d", c); + + memset ((void *)ecp, 0, sizeof (*ecp)); + + ecp->state = Idle; + + switch (c) { + + case 0: /* FCC1 */ + ecp->proff = PROFF_FCC1; + ecp->page = CPM_CR_FCC1_PAGE; + ecp->sblock = CPM_CR_FCC1_SBLOCK; + break; + + case 1: /* FCC2 */ + ecp->proff = PROFF_FCC2; + ecp->page = CPM_CR_FCC2_PAGE; + ecp->sblock = CPM_CR_FCC2_SBLOCK; + break; + + case 2: /* FCC3 */ + ecp->proff = PROFF_FCC3; + ecp->page = CPM_CR_FCC3_PAGE; + ecp->sblock = CPM_CR_FCC3_SBLOCK; + break; + } + + /* + * set up tx buffers and bds + */ + + for (i = 0; i < ELBT_NTXBD; i++) { + cbd_t *bdp = &ecp->txbd[i]; + uchar *bp = &ecp->txbufs[i][0]; + + bdp->cbd_bufaddr = (uint)bp; + /* room for crc */ + bdp->cbd_datlen = ELBT_BUFSZ - ELBT_CRCSZ; + bdp->cbd_sc = BD_ENET_TX_READY | BD_ENET_TX_PAD | \ + BD_ENET_TX_LAST | BD_ENET_TX_TC; + + memset ((void *)bp, patbytes[i], ELBT_BUFSZ); + NetSetEther (bp, NetBcastAddr, 0x8000); + } + ecp->txbd[ELBT_NTXBD - 1].cbd_sc |= BD_ENET_TX_WRAP; + + /* + * set up rx buffers and bds + */ + + for (i = 0; i < ELBT_NRXBD; i++) { + cbd_t *bdp = &ecp->rxbd[i]; + uchar *bp = &ecp->rxbufs[i][0]; + + bdp->cbd_bufaddr = (uint)bp; + bdp->cbd_datlen = 0; + bdp->cbd_sc = BD_ENET_RX_EMPTY; + + memset ((void *)bp, 0, ELBT_BUFSZ); + } + ecp->rxbd[ELBT_NRXBD - 1].cbd_sc |= BD_ENET_RX_WRAP; + + /* + * set up the FCC channel hardware + */ + + /* 28.9 - (4): GFMR: disable tx/rx, CCITT CRC, Mode Ethernet */ + fcp->fcc_gfmr = FCC_GFMR_MODE_ENET | FCC_GFMR_TCRC_32; + + /* 28.9 - (5): FPSMR: fd, enet CRC, Promis, RMON, Rx SHort */ + fcp->fcc_fpsmr = FCC_PSMR_FDE | FCC_PSMR_LPB | \ + FCC_PSMR_ENCRC | FCC_PSMR_PRO | \ + FCC_PSMR_MON | FCC_PSMR_RSH; + + /* 28.9 - (6): FDSR: Ethernet Syn */ + fcp->fcc_fdsr = 0xD555; + + /* 29.9 - (7): initialise parameter ram */ + fpp = (fcc_enet_t *)&(immr->im_dprambase[ecp->proff]); + + /* clear whole struct to make sure all resv fields are zero */ + memset ((void *)fpp, 0, sizeof (fcc_enet_t)); + + /* + * common Parameter RAM area + * + * Allocate space in the reserved FCC area of DPRAM for the + * internal buffers. No one uses this space (yet), so we + * can do this. Later, we will add resource management for + * this area. + */ + addr = CPM_FCC_SPECIAL_BASE + (c * 64); + fpp->fen_genfcc.fcc_riptr = addr; + fpp->fen_genfcc.fcc_tiptr = addr + 32; + + /* + * Set maximum bytes per receive buffer. + * It must be a multiple of 32. + * buffers are in 60x bus memory. + */ + fpp->fen_genfcc.fcc_mrblr = PKT_MAXBLR_SIZE; + fpp->fen_genfcc.fcc_rstate = (CPMFCR_GBL | CPMFCR_EB) << 24; + fpp->fen_genfcc.fcc_rbase = (unsigned int)(&ecp->rxbd[0]); + fpp->fen_genfcc.fcc_tstate = (CPMFCR_GBL | CPMFCR_EB) << 24; + fpp->fen_genfcc.fcc_tbase = (unsigned int)(&ecp->txbd[0]); + + /* protocol-specific area */ + fpp->fen_cmask = 0xdebb20e3; /* CRC mask */ + fpp->fen_cpres = 0xffffffff; /* CRC preset */ + fpp->fen_retlim = 15; /* Retry limit threshold */ + fpp->fen_mflr = PKT_MAXBUF_SIZE;/* max frame length register */ + + /* + * Set Ethernet station address. + * + * This is supplied in the board information structure, so we + * copy that into the controller. + * So, far we have only been given one Ethernet address. We use + * the same address for all channels + */ +#define ea NetOurEther + fpp->fen_paddrh = (ea[5] << 8) + ea[4]; + fpp->fen_paddrm = (ea[3] << 8) + ea[2]; + fpp->fen_paddrl = (ea[1] << 8) + ea[0]; +#undef ea + + fpp->fen_minflr = PKT_MINBUF_SIZE; /* min frame len register */ + /* + * pad pointer. use tiptr since we don't need + * a specific padding char + */ + fpp->fen_padptr = fpp->fen_genfcc.fcc_tiptr; + fpp->fen_maxd1 = PKT_MAXDMA_SIZE; /* max DMA1 length */ + fpp->fen_maxd2 = PKT_MAXDMA_SIZE; /* max DMA2 length */ + fpp->fen_rfthr = 1; + fpp->fen_rfcnt = 1; + + /* 28.9 - (8): clear out events in FCCE */ + fcp->fcc_fcce = ~0x0; + + /* 28.9 - (9): FCCM: mask all events */ + fcp->fcc_fccm = 0; + + /* 28.9 - (10-12): we don't use ethernet interrupts */ + + /* 28.9 - (13) + * + * Let's re-initialize the channel now. We have to do it later + * than the manual describes because we have just now finished + * the BD initialization. + */ + cp->cp_cpcr = mk_cr_cmd (ecp->page, ecp->sblock, \ + 0x0c, CPM_CR_INIT_TRX) | CPM_CR_FLG; + do { + __asm__ __volatile__ ("eieio"); + } while (cp->cp_cpcr & CPM_CR_FLG); + } + + puts (" done\nStarting test... (Ctrl-C to Finish)\n"); + + /* + * Note: don't want serial output from here until the end of the + * test - the delays would probably stuff things up. + */ + + clear_ctrlc (); + runtime = get_timer (0); + + do { + nclosed = 0; + + for (c = FCC_START_LOOP; c <= FCC_END_LOOP; c++) { + volatile fcc_t *fcp = &immr->im_fcc[c]; + elbt_chan *ecp = &elbt_chans[c]; + int i; + + switch (ecp->state) { + + case Idle: + /* + * set the channel Running ... + */ + + /* 28.9 - (14): enable tx/rx in gfmr */ + fcp->fcc_gfmr |= FCC_GFMR_ENT | FCC_GFMR_ENR; + + ecp->state = Running; + break; + + case Running: + /* + * (while Running only) check for + * termination of the test + */ + + (void)ctrlc (); + + if (had_ctrlc ()) { + /* + * initiate a "graceful stop transmit" + * on the channel + */ + cp->cp_cpcr = mk_cr_cmd (ecp->page, \ + ecp->sblock, 0x0c, \ + CPM_CR_GRACEFUL_STOP_TX) | \ + CPM_CR_FLG; + do { + __asm__ __volatile__ ("eieio"); + } while (cp->cp_cpcr & CPM_CR_FLG); + + ecp->clstime = get_timer (0); + ecp->state = Closing; + } + /* fall through ... */ + + case Closing: + /* + * (while Running or Closing) poll the channel: + * - check for any non-READY tx buffers and + * make them ready + * - check for any non-EMPTY rx buffers and + * check that they were received correctly, + * adjust counters etc, then make empty + */ + + for (i = 0; i < ELBT_NTXBD; i++) { + cbd_t *bdp = &ecp->txbd[i]; + ushort sc = bdp->cbd_sc; + + if ((sc & BD_ENET_TX_READY) != 0) + continue; + + /* + * this frame has finished + * transmitting + */ + ecp->nsent++; + + if (sc & BD_ENET_TX_STATS) { + ulong n; + + /* + * we had an error on + * the transmission + */ + n = ecp->ntxerr++; + if (n < ELBT_MAXTXERR) + ecp->txerrs[n] = sc; + + if (sc & BD_ENET_TX_DEF) + ecp->txeacc.def++; + if (sc & BD_ENET_TX_HB) + ecp->txeacc.hb++; + if (sc & BD_ENET_TX_LC) + ecp->txeacc.lc++; + if (sc & BD_ENET_TX_RL) + ecp->txeacc.rl++; + if (sc & BD_ENET_TX_RCMASK) + ecp->txeacc.rc++; + if (sc & BD_ENET_TX_UN) + ecp->txeacc.un++; + if (sc & BD_ENET_TX_CSL) + ecp->txeacc.csl++; + + bdp->cbd_sc &= \ + ~BD_ENET_TX_STATS; + } + + if (ecp->state == Closing) + ecp->clstime = get_timer (0); + + /* make it ready again */ + bdp->cbd_sc |= BD_ENET_TX_READY; + } + + for (i = 0; i < ELBT_NRXBD; i++) { + cbd_t *bdp = &ecp->rxbd[i]; + ushort sc = bdp->cbd_sc, mask; + + if ((sc & BD_ENET_RX_EMPTY) != 0) + continue; + + /* we have a new frame in this buffer */ + ecp->nrcvd++; + + mask = BD_ENET_RX_LAST|BD_ENET_RX_FIRST; + if ((sc & mask) != mask) { + /* somethings wrong here ... */ + if (!(sc & BD_ENET_RX_LAST)) + ecp->rxeacc._l++; + if (!(sc & BD_ENET_RX_FIRST)) + ecp->rxeacc._f++; + } + + if (sc & BD_ENET_RX_ERRS) { + ulong n; + + /* + * we had some sort of error + * on the frame + */ + n = ecp->nrxerr++; + if (n < ELBT_MAXRXERR) + ecp->rxerrs[n] = sc; + + if (sc & BD_ENET_RX_MISS) + ecp->rxeacc.m++; + if (sc & BD_ENET_RX_BC) + ecp->rxeacc.bc++; + if (sc & BD_ENET_RX_MC) + ecp->rxeacc.mc++; + if (sc & BD_ENET_RX_LG) + ecp->rxeacc.lg++; + if (sc & BD_ENET_RX_NO) + ecp->rxeacc.no++; + if (sc & BD_ENET_RX_SH) + ecp->rxeacc.sh++; + if (sc & BD_ENET_RX_CR) + ecp->rxeacc.cr++; + if (sc & BD_ENET_RX_OV) + ecp->rxeacc.ov++; + if (sc & BD_ENET_RX_CL) + ecp->rxeacc.cl++; + + bdp->cbd_sc &= \ + ~BD_ENET_RX_ERRS; + } + else { + ushort datlen = bdp->cbd_datlen; + Ethernet_t *ehp; + ushort prot; + int ours, tb, n, nbytes; + + ehp = (Ethernet_t *) \ + &ecp->rxbufs[i][0]; + + ours = memcmp (ehp->et_src, \ + NetOurEther, 6); + + prot = swap16 (ehp->et_protlen); + tb = prot & 0x8000; + n = prot & 0x7fff; + + nbytes = ELBT_BUFSZ - \ + offsetof (Ethernet_t, \ + et_dsap) - \ + ELBT_CRCSZ; + + /* check the frame is correct */ + if (datlen != ELBT_BUFSZ) + ecp->rxeacc.badlen++; + else if (!ours) + ecp->rxeacc.badsrc++; + else if (!tb || n >= ELBT_NTXBD) + ecp->rxeacc.badtyp++; + else { + ulong patword = \ + patwords[n]; + uint nbb; + + nbb = badbits ( \ + &ehp->et_dsap, \ + nbytes, \ + patword); + + ecp->rxeacc.badbit += \ + nbb; + } + } + + if (ecp->state == Closing) + ecp->clstime = get_timer (0); + + /* make it empty again */ + bdp->cbd_sc |= BD_ENET_RX_EMPTY; + } + + if (ecp->state != Closing) + break; + + /* + * (while Closing) check to see if + * waited long enough + */ + + if (get_timer (ecp->clstime) >= ELBT_CLSWAIT) { + /* write GFMR: disable tx/rx */ + fcp->fcc_gfmr &= \ + ~(FCC_GFMR_ENT | FCC_GFMR_ENR); + ecp->state = Closed; + } + + break; + + case Closed: + nclosed++; + break; + } + } + + } while (nclosed < (FCC_END_LOOP - FCC_START_LOOP + 1)); + + runtime = get_timer (runtime); + if (runtime <= ELBT_CLSWAIT) { + printf ("Whoops! somehow elapsed time (%ld) is wrong (<= %d)\n", + runtime, ELBT_CLSWAIT); + return; + } + nmsec = runtime - ELBT_CLSWAIT; + + printf ("Test Finished in %ldms (plus %dms close wait period)!\n\n", + nmsec, ELBT_CLSWAIT); + + /* + * now print stats + */ + + for (c = FCC_START_LOOP; c <= FCC_END_LOOP; c++) { + elbt_chan *ecp = &elbt_chans[c]; + uint rxpps, txpps, nerr; + + rxpps = (ecp->nrcvd * 1000) / nmsec; + txpps = (ecp->nsent * 1000) / nmsec; + + printf ("Channel %d: %d rcvd (%d pps, %d rxerrs), " + "%d sent (%d pps, %d txerrs)\n\n", c, + ecp->nrcvd, rxpps, ecp->nrxerr, + ecp->nsent, txpps, ecp->ntxerr); + + if ((nerr = ecp->nrxerr) > 0) { + ulong i; + + printf ("\tFirst %d rx errs:", nerr); + for (i = 0; i < nerr; i++) + printf (" %04x", ecp->rxerrs[i]); + putc ('\n'); + } + + if ((nerr = ecp->ntxerr) > 0) { + ulong i; + + printf ("\tFirst %d tx errs:", nerr); + for (i = 0; i < nerr; i++) + printf (" %04x", ecp->txerrs[i]); + putc ('\n'); + } + } + + puts ("Receive Error Counts:\n"); + for (c = FCC_START_LOOP; c <= FCC_END_LOOP; c++) + bases[c] = (uchar *)&elbt_chans[c].rxeacc; + print_desc (rxeacc_descs, rxeacc_ndesc, bases, 3); + + puts ("\nTransmit Error Counts:\n"); + for (c = FCC_START_LOOP; c <= FCC_END_LOOP; c++) + bases[c] = (uchar *)&elbt_chans[c].txeacc; + print_desc (txeacc_descs, txeacc_ndesc, bases, 3); + + puts ("\nRMON(-like) Counters:\n"); + for (c = FCC_START_LOOP; c <= FCC_END_LOOP; c++) + bases[c] = (uchar *)&immr->im_dprambase[elbt_chans[c].proff]; + print_desc (epram_descs, epram_ndesc, bases, 3); +} + +#endif /* CONFIG_ETHER_LOOPBACK_TEST */ + +#endif diff --git a/arch/powerpc/cpu/mpc8260/ether_scc.c b/arch/powerpc/cpu/mpc8260/ether_scc.c new file mode 100644 index 0000000..432111d --- /dev/null +++ b/arch/powerpc/cpu/mpc8260/ether_scc.c @@ -0,0 +1,387 @@ +/* + * MPC8260 SCC Ethernet + * + * Copyright (c) 2000 MontaVista Software, Inc. Dan Malek (dmalek@jlc.net) + * + * (C) Copyright 2000 Sysgo Real-Time Solutions, GmbH <www.elinos.com> + * Marius Groeger <mgroeger@sysgo.de> + * + * (C) Copyright (c) 2001 + * Advent Networks, Inc. <http://www.adventnetworks.com> + * Jay Monkman <jtm@smoothsmoothie.com> + * + * Modified so that it plays nicely when more than one ETHERNET interface + * is in use a la ether_fcc.c. + * (C) Copyright 2008 + * DENX Software Engineerin GmbH + * Gary Jennejohn <garyj@denx.de> + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include <common.h> +#include <asm/cpm_8260.h> +#include <mpc8260.h> +#include <malloc.h> +#include <net.h> +#include <command.h> +#include <config.h> + +#ifndef CONFIG_NET_MULTI +#error "CONFIG_NET_MULTI must be defined." +#endif + +#if (CONFIG_ETHER_INDEX == 1) +# define PROFF_ENET PROFF_SCC1 +# define CPM_CR_ENET_PAGE CPM_CR_SCC1_PAGE +# define CPM_CR_ENET_SBLOCK CPM_CR_SCC1_SBLOCK +# define CMXSCR_MASK (CMXSCR_SC1 |\ + CMXSCR_RS1CS_MSK |\ + CMXSCR_TS1CS_MSK) + +#elif (CONFIG_ETHER_INDEX == 2) +# define PROFF_ENET PROFF_SCC2 +# define CPM_CR_ENET_PAGE CPM_CR_SCC2_PAGE +# define CPM_CR_ENET_SBLOCK CPM_CR_SCC2_SBLOCK +# define CMXSCR_MASK (CMXSCR_SC2 |\ + CMXSCR_RS2CS_MSK |\ + CMXSCR_TS2CS_MSK) + +#elif (CONFIG_ETHER_INDEX == 3) +# define PROFF_ENET PROFF_SCC3 +# define CPM_CR_ENET_PAGE CPM_CR_SCC3_PAGE +# define CPM_CR_ENET_SBLOCK CPM_CR_SCC3_SBLOCK +# define CMXSCR_MASK (CMXSCR_SC3 |\ + CMXSCR_RS3CS_MSK |\ + CMXSCR_TS3CS_MSK) +#elif (CONFIG_ETHER_INDEX == 4) +# define PROFF_ENET PROFF_SCC4 +# define CPM_CR_ENET_PAGE CPM_CR_SCC4_PAGE +# define CPM_CR_ENET_SBLOCK CPM_CR_SCC4_SBLOCK +# define CMXSCR_MASK (CMXSCR_SC4 |\ + CMXSCR_RS4CS_MSK |\ + CMXSCR_TS4CS_MSK) + +#endif + + +/* Ethernet Transmit and Receive Buffers */ +#define DBUF_LENGTH 1520 + +#define TX_BUF_CNT 2 + +#if !defined(CONFIG_SYS_SCC_TOUT_LOOP) + #define CONFIG_SYS_SCC_TOUT_LOOP 1000000 +#endif + +static char txbuf[TX_BUF_CNT][ DBUF_LENGTH ]; + +static uint rxIdx; /* index of the current RX buffer */ +static uint txIdx; /* index of the current TX buffer */ + +/* + * SCC Ethernet Tx and Rx buffer descriptors allocated at the + * immr->udata_bd address on Dual-Port RAM + * Provide for Double Buffering + */ + +typedef volatile struct CommonBufferDescriptor { + cbd_t rxbd[PKTBUFSRX]; /* Rx BD */ + cbd_t txbd[TX_BUF_CNT]; /* Tx BD */ +} RTXBD; + +static RTXBD *rtx; + + +static int sec_send(struct eth_device *dev, volatile void *packet, int length) +{ + int i; + int result = 0; + + if (length <= 0) { + printf("scc: bad packet size: %d\n", length); + goto out; + } + + for(i=0; rtx->txbd[txIdx].cbd_sc & BD_ENET_TX_READY; i++) { + if (i >= CONFIG_SYS_SCC_TOUT_LOOP) { + puts ("scc: tx buffer not ready\n"); + goto out; + } + } + + rtx->txbd[txIdx].cbd_bufaddr = (uint)packet; + rtx->txbd[txIdx].cbd_datlen = length; + rtx->txbd[txIdx].cbd_sc |= (BD_ENET_TX_READY | BD_ENET_TX_LAST | + BD_ENET_TX_WRAP); + + for(i=0; rtx->txbd[txIdx].cbd_sc & BD_ENET_TX_READY; i++) { + if (i >= CONFIG_SYS_SCC_TOUT_LOOP) { + puts ("scc: tx error\n"); + goto out; + } + } + + /* return only status bits */ + result = rtx->txbd[txIdx].cbd_sc & BD_ENET_TX_STATS; + + out: + return result; +} + + +static int sec_rx(struct eth_device *dev) +{ + int length; + + for (;;) + { + if (rtx->rxbd[rxIdx].cbd_sc & BD_ENET_RX_EMPTY) { + length = -1; + break; /* nothing received - leave for() loop */ + } + + length = rtx->rxbd[rxIdx].cbd_datlen; + + if (rtx->rxbd[rxIdx].cbd_sc & 0x003f) + { + printf("err: %x\n", rtx->rxbd[rxIdx].cbd_sc); + } + else + { + /* Pass the packet up to the protocol layers. */ + NetReceive(NetRxPackets[rxIdx], length - 4); + } + + + /* Give the buffer back to the SCC. */ + rtx->rxbd[rxIdx].cbd_datlen = 0; + + /* wrap around buffer index when necessary */ + if ((rxIdx + 1) >= PKTBUFSRX) { + rtx->rxbd[PKTBUFSRX - 1].cbd_sc = (BD_ENET_RX_WRAP | + BD_ENET_RX_EMPTY); + rxIdx = 0; + } + else { + rtx->rxbd[rxIdx].cbd_sc = BD_ENET_RX_EMPTY; + rxIdx++; + } + } + return length; +} + +/************************************************************** + * + * SCC Ethernet Initialization Routine + * + *************************************************************/ + +static int sec_init(struct eth_device *dev, bd_t *bis) +{ + int i; + volatile immap_t *immr = (immap_t *)CONFIG_SYS_IMMR; + scc_enet_t *pram_ptr; + uint dpaddr; + uchar ea[6]; + + rxIdx = 0; + txIdx = 0; + + /* + * Assign static pointer to BD area. + * Avoid exhausting DPRAM, which would cause a panic. + */ + if (rtx == NULL) { + dpaddr = m8260_cpm_dpalloc(sizeof(RTXBD) + 2, 16); + rtx = (RTXBD *)&immr->im_dprambase[dpaddr]; + } + + /* 24.21 - (1-3): ioports have been set up already */ + + /* 24.21 - (4,5): connect SCC's tx and rx clocks, use NMSI for SCC */ + immr->im_cpmux.cmx_uar = 0; + immr->im_cpmux.cmx_scr = ( (immr->im_cpmux.cmx_scr & ~CMXSCR_MASK) | + CONFIG_SYS_CMXSCR_VALUE); + + + /* 24.21 (6) write RBASE and TBASE to parameter RAM */ + pram_ptr = (scc_enet_t *)&(immr->im_dprambase[PROFF_ENET]); + pram_ptr->sen_genscc.scc_rbase = (unsigned int)(&rtx->rxbd[0]); + pram_ptr->sen_genscc.scc_tbase = (unsigned int)(&rtx->txbd[0]); + + pram_ptr->sen_genscc.scc_rfcr = 0x18; /* Nrml Ops and Mot byte ordering */ + pram_ptr->sen_genscc.scc_tfcr = 0x18; /* Mot byte ordering, Nrml access */ + + pram_ptr->sen_genscc.scc_mrblr = DBUF_LENGTH; /* max. package len 1520 */ + + pram_ptr->sen_cpres = ~(0x0); /* Preset CRC */ + pram_ptr->sen_cmask = 0xdebb20e3; /* Constant Mask for CRC */ + + + /* 24.21 - (7): Write INIT RX AND TX PARAMETERS to CPCR */ + while(immr->im_cpm.cp_cpcr & CPM_CR_FLG); + immr->im_cpm.cp_cpcr = mk_cr_cmd(CPM_CR_ENET_PAGE, + CPM_CR_ENET_SBLOCK, + 0x0c, + CPM_CR_INIT_TRX) | CPM_CR_FLG; + + /* 24.21 - (8-18): Set up parameter RAM */ + pram_ptr->sen_crcec = 0x0; /* Error Counter CRC (unused) */ + pram_ptr->sen_alec = 0x0; /* Align Error Counter (unused) */ + pram_ptr->sen_disfc = 0x0; /* Discard Frame Counter (unused) */ + + pram_ptr->sen_pads = 0x8888; /* Short Frame PAD Characters */ + + pram_ptr->sen_retlim = 15; /* Retry Limit Threshold */ + + pram_ptr->sen_maxflr = 1518; /* MAX Frame Length Register */ + pram_ptr->sen_minflr = 64; /* MIN Frame Length Register */ + + pram_ptr->sen_maxd1 = DBUF_LENGTH; /* MAX DMA1 Length Register */ + pram_ptr->sen_maxd2 = DBUF_LENGTH; /* MAX DMA2 Length Register */ + + pram_ptr->sen_gaddr1 = 0x0; /* Group Address Filter 1 (unused) */ + pram_ptr->sen_gaddr2 = 0x0; /* Group Address Filter 2 (unused) */ + pram_ptr->sen_gaddr3 = 0x0; /* Group Address Filter 3 (unused) */ + pram_ptr->sen_gaddr4 = 0x0; /* Group Address Filter 4 (unused) */ + + eth_getenv_enetaddr("ethaddr", ea); + pram_ptr->sen_paddrh = (ea[5] << 8) + ea[4]; + pram_ptr->sen_paddrm = (ea[3] << 8) + ea[2]; + pram_ptr->sen_paddrl = (ea[1] << 8) + ea[0]; + + pram_ptr->sen_pper = 0x0; /* Persistence (unused) */ + + pram_ptr->sen_iaddr1 = 0x0; /* Individual Address Filter 1 (unused) */ + pram_ptr->sen_iaddr2 = 0x0; /* Individual Address Filter 2 (unused) */ + pram_ptr->sen_iaddr3 = 0x0; /* Individual Address Filter 3 (unused) */ + pram_ptr->sen_iaddr4 = 0x0; /* Individual Address Filter 4 (unused) */ + + pram_ptr->sen_taddrh = 0x0; /* Tmp Address (MSB) (unused) */ + pram_ptr->sen_taddrm = 0x0; /* Tmp Address (unused) */ + pram_ptr->sen_taddrl = 0x0; /* Tmp Address (LSB) (unused) */ + + /* 24.21 - (19): Initialize RxBD */ + for (i = 0; i < PKTBUFSRX; i++) + { + rtx->rxbd[i].cbd_sc = BD_ENET_RX_EMPTY; + rtx->rxbd[i].cbd_datlen = 0; /* Reset */ + rtx->rxbd[i].cbd_bufaddr = (uint)NetRxPackets[i]; + } + + rtx->rxbd[PKTBUFSRX - 1].cbd_sc |= BD_ENET_RX_WRAP; + + /* 24.21 - (20): Initialize TxBD */ + for (i = 0; i < TX_BUF_CNT; i++) + { + rtx->txbd[i].cbd_sc = (BD_ENET_TX_PAD | + BD_ENET_TX_LAST | + BD_ENET_TX_TC); + rtx->txbd[i].cbd_datlen = 0; /* Reset */ + rtx->txbd[i].cbd_bufaddr = (uint)&txbuf[i][0]; + } + + rtx->txbd[TX_BUF_CNT - 1].cbd_sc |= BD_ENET_TX_WRAP; + + /* 24.21 - (21): Write 0xffff to SCCE */ + immr->im_scc[CONFIG_ETHER_INDEX-1].scc_scce = ~(0x0); + + /* 24.21 - (22): Write to SCCM to enable TXE, RXF, TXB events */ + immr->im_scc[CONFIG_ETHER_INDEX-1].scc_sccm = (SCCE_ENET_TXE | + SCCE_ENET_RXF | + SCCE_ENET_TXB); + + /* 24.21 - (23): we don't use ethernet interrupts */ + + /* 24.21 - (24): Clear GSMR_H to enable normal operations */ + immr->im_scc[CONFIG_ETHER_INDEX-1].scc_gsmrh = 0; + + /* 24.21 - (25): Clear GSMR_L to enable normal operations */ + immr->im_scc[CONFIG_ETHER_INDEX-1].scc_gsmrl = (SCC_GSMRL_TCI | + SCC_GSMRL_TPL_48 | + SCC_GSMRL_TPP_10 | + SCC_GSMRL_MODE_ENET); + + /* 24.21 - (26): Initialize DSR */ + immr->im_scc[CONFIG_ETHER_INDEX-1].scc_dsr = 0xd555; + + /* 24.21 - (27): Initialize PSMR2 + * + * Settings: + * CRC = 32-Bit CCITT + * NIB = Begin searching for SFD 22 bits after RENA + * FDE = Full Duplex Enable + * BRO = Reject broadcast packets + * PROMISCOUS = Catch all packets regardless of dest. MAC adress + */ + immr->im_scc[CONFIG_ETHER_INDEX-1].scc_psmr = SCC_PSMR_ENCRC | + SCC_PSMR_NIB22 | +#if defined(CONFIG_SCC_ENET_FULL_DUPLEX) + SCC_PSMR_FDE | +#endif +#if defined(CONFIG_SCC_ENET_NO_BROADCAST) + SCC_PSMR_BRO | +#endif +#if defined(CONFIG_SCC_ENET_PROMISCOUS) + SCC_PSMR_PRO | +#endif + 0; + + /* 24.21 - (28): Write to GSMR_L to enable SCC */ + immr->im_scc[CONFIG_ETHER_INDEX-1].scc_gsmrl |= (SCC_GSMRL_ENR | + SCC_GSMRL_ENT); + + return 0; +} + + +static void sec_halt(struct eth_device *dev) +{ + volatile immap_t *immr = (immap_t *)CONFIG_SYS_IMMR; + immr->im_scc[CONFIG_ETHER_INDEX-1].scc_gsmrl &= ~(SCC_GSMRL_ENR | + SCC_GSMRL_ENT); +} + +#if 0 +static void sec_restart(void) +{ + volatile immap_t *immr = (immap_t *)CONFIG_SYS_IMMR; + immr->im_cpm.cp_scc[CONFIG_ETHER_INDEX-1].scc_gsmrl |= (SCC_GSMRL_ENR | + SCC_GSMRL_ENT); +} +#endif + +int mpc82xx_scc_enet_initialize(bd_t *bis) +{ + struct eth_device *dev; + + dev = (struct eth_device *) malloc(sizeof *dev); + memset(dev, 0, sizeof *dev); + + sprintf(dev->name, "SCC ETHERNET"); + dev->init = sec_init; + dev->halt = sec_halt; + dev->send = sec_send; + dev->recv = sec_rx; + + eth_register(dev); + + return 1; +} diff --git a/arch/powerpc/cpu/mpc8260/i2c.c b/arch/powerpc/cpu/mpc8260/i2c.c new file mode 100644 index 0000000..d2bdcc2 --- /dev/null +++ b/arch/powerpc/cpu/mpc8260/i2c.c @@ -0,0 +1,785 @@ +/* + * (C) Copyright 2000 + * Paolo Scaffardi, AIRVENT SAM s.p.a - RIMINI(ITALY), arsenio@tin.it + * + * (C) Copyright 2000 Sysgo Real-Time Solutions, GmbH <www.elinos.com> + * Marius Groeger <mgroeger@sysgo.de> + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include <common.h> + +#if defined(CONFIG_HARD_I2C) + +#include <asm/cpm_8260.h> +#include <i2c.h> + +/* define to enable debug messages */ +#undef DEBUG_I2C + +DECLARE_GLOBAL_DATA_PTR; + +#if defined(CONFIG_I2C_MULTI_BUS) +static unsigned int i2c_bus_num __attribute__ ((section (".data"))) = 0; +#endif /* CONFIG_I2C_MULTI_BUS */ + +/* uSec to wait between polls of the i2c */ +#define DELAY_US 100 +/* uSec to wait for the CPM to start processing the buffer */ +#define START_DELAY_US 1000 + +/* + * tx/rx per-byte timeout: we delay DELAY_US uSec between polls so the + * timeout will be (tx_length + rx_length) * DELAY_US * TOUT_LOOP + */ +#define TOUT_LOOP 5 + +/*----------------------------------------------------------------------- + * Set default values + */ +#ifndef CONFIG_SYS_I2C_SPEED +#define CONFIG_SYS_I2C_SPEED 50000 +#endif + +/*----------------------------------------------------------------------- + */ + +typedef void (*i2c_ecb_t)(int, int, void *); /* error callback function */ + +/* This structure keeps track of the bd and buffer space usage. */ +typedef struct i2c_state { + int rx_idx; /* index to next free Rx BD */ + int tx_idx; /* index to next free Tx BD */ + void *rxbd; /* pointer to next free Rx BD */ + void *txbd; /* pointer to next free Tx BD */ + int tx_space; /* number of Tx bytes left */ + unsigned char *tx_buf; /* pointer to free Tx area */ + i2c_ecb_t err_cb; /* error callback function */ + void *cb_data; /* private data to be passed */ +} i2c_state_t; + +/* flags for i2c_send() and i2c_receive() */ +#define I2CF_ENABLE_SECONDARY 0x01 /* secondary_address is valid */ +#define I2CF_START_COND 0x02 /* tx: generate start condition */ +#define I2CF_STOP_COND 0x04 /* tx: generate stop condition */ + +/* return codes */ +#define I2CERR_NO_BUFFERS 1 /* no more BDs or buffer space */ +#define I2CERR_MSG_TOO_LONG 2 /* tried to send/receive to much data */ +#define I2CERR_TIMEOUT 3 /* timeout in i2c_doio() */ +#define I2CERR_QUEUE_EMPTY 4 /* i2c_doio called without send/receive */ +#define I2CERR_IO_ERROR 5 /* had an error during comms */ + +/* error callback flags */ +#define I2CECB_RX_ERR 0x10 /* this is a receive error */ +#define I2CECB_RX_OV 0x02 /* receive overrun error */ +#define I2CECB_RX_MASK 0x0f /* mask for error bits */ +#define I2CECB_TX_ERR 0x20 /* this is a transmit error */ +#define I2CECB_TX_CL 0x01 /* transmit collision error */ +#define I2CECB_TX_UN 0x02 /* transmit underflow error */ +#define I2CECB_TX_NAK 0x04 /* transmit no ack error */ +#define I2CECB_TX_MASK 0x0f /* mask for error bits */ +#define I2CECB_TIMEOUT 0x40 /* this is a timeout error */ + +#define ERROR_I2C_NONE 0 +#define ERROR_I2C_LENGTH 1 + +#define I2C_WRITE_BIT 0x00 +#define I2C_READ_BIT 0x01 + +#define I2C_RXTX_LEN 128 /* maximum tx/rx buffer length */ + + +#define NUM_RX_BDS 4 +#define NUM_TX_BDS 4 +#define MAX_TX_SPACE 256 + +typedef struct I2C_BD +{ + unsigned short status; + unsigned short length; + unsigned char *addr; +} I2C_BD; +#define BD_I2C_TX_START 0x0400 /* special status for i2c: Start condition */ + +#define BD_I2C_TX_CL 0x0001 /* collision error */ +#define BD_I2C_TX_UN 0x0002 /* underflow error */ +#define BD_I2C_TX_NAK 0x0004 /* no acknowledge error */ +#define BD_I2C_TX_ERR (BD_I2C_TX_NAK|BD_I2C_TX_UN|BD_I2C_TX_CL) + +#define BD_I2C_RX_ERR BD_SC_OV + +#ifdef DEBUG_I2C +#define PRINTD(x) printf x +#else +#define PRINTD(x) +#endif + +/* + * Returns the best value of I2BRG to meet desired clock speed of I2C with + * input parameters (clock speed, filter, and predivider value). + * It returns computer speed value and the difference between it and desired + * speed. + */ +static inline int +i2c_roundrate(int hz, int speed, int filter, int modval, + int *brgval, int *totspeed) +{ + int moddiv = 1 << (5-(modval & 3)), brgdiv, div; + + PRINTD(("\t[I2C] trying hz=%d, speed=%d, filter=%d, modval=%d\n", + hz, speed, filter, modval)); + + div = moddiv * speed; + brgdiv = (hz + div - 1) / div; + + PRINTD(("\t\tmoddiv=%d, brgdiv=%d\n", moddiv, brgdiv)); + + *brgval = ((brgdiv + 1) / 2) - 3 - (2*filter); + + if ((*brgval < 0) || (*brgval > 255)) { + PRINTD(("\t\trejected brgval=%d\n", *brgval)); + return -1; + } + + brgdiv = 2 * (*brgval + 3 + (2 * filter)); + div = moddiv * brgdiv ; + *totspeed = hz / div; + + PRINTD(("\t\taccepted brgval=%d, totspeed=%d\n", *brgval, *totspeed)); + + return 0; +} + +/* + * Sets the I2C clock predivider and divider to meet required clock speed. + */ +static int i2c_setrate(int hz, int speed) +{ + immap_t *immap = (immap_t *)CONFIG_SYS_IMMR ; + volatile i2c8260_t *i2c = (i2c8260_t *)&immap->im_i2c; + int brgval, + modval, /* 0-3 */ + bestspeed_diff = speed, + bestspeed_brgval=0, + bestspeed_modval=0, + bestspeed_filter=0, + totspeed, + filter = 0; /* Use this fixed value */ + + for (modval = 0; modval < 4; modval++) + { + if (i2c_roundrate (hz, speed, filter, modval, &brgval, &totspeed) == 0) + { + int diff = speed - totspeed ; + + if ((diff >= 0) && (diff < bestspeed_diff)) + { + bestspeed_diff = diff ; + bestspeed_modval = modval; + bestspeed_brgval = brgval; + bestspeed_filter = filter; + } + } + } + + PRINTD(("[I2C] Best is:\n")); + PRINTD(("[I2C] CPU=%dhz RATE=%d F=%d I2MOD=%08x I2BRG=%08x DIFF=%dhz\n", + hz, speed, + bestspeed_filter, bestspeed_modval, bestspeed_brgval, + bestspeed_diff)); + + i2c->i2c_i2mod |= ((bestspeed_modval & 3) << 1) | (bestspeed_filter << 3); + i2c->i2c_i2brg = bestspeed_brgval & 0xff; + + PRINTD(("[I2C] i2mod=%08x i2brg=%08x\n", i2c->i2c_i2mod, i2c->i2c_i2brg)); + + return 1 ; +} + +void i2c_init(int speed, int slaveadd) +{ + volatile immap_t *immap = (immap_t *)CONFIG_SYS_IMMR ; + volatile cpm8260_t *cp = (cpm8260_t *)&immap->im_cpm; + volatile i2c8260_t *i2c = (i2c8260_t *)&immap->im_i2c; + volatile iic_t *iip; + ulong rbase, tbase; + volatile I2C_BD *rxbd, *txbd; + uint dpaddr; + +#ifdef CONFIG_SYS_I2C_INIT_BOARD + /* call board specific i2c bus reset routine before accessing the */ + /* environment, which might be in a chip on that bus. For details */ + /* about this problem see doc/I2C_Edge_Conditions. */ + i2c_init_board(); +#endif + + dpaddr = *((unsigned short*)(&immap->im_dprambase[PROFF_I2C_BASE])); + if (dpaddr == 0) { + /* need to allocate dual port ram */ + dpaddr = m8260_cpm_dpalloc(64 + + (NUM_RX_BDS * sizeof(I2C_BD)) + (NUM_TX_BDS * sizeof(I2C_BD)) + + MAX_TX_SPACE, 64); + *((unsigned short*)(&immap->im_dprambase[PROFF_I2C_BASE])) = dpaddr; + } + + /* + * initialise data in dual port ram: + * + * dpaddr -> parameter ram (64 bytes) + * rbase -> rx BD (NUM_RX_BDS * sizeof(I2C_BD) bytes) + * tbase -> tx BD (NUM_TX_BDS * sizeof(I2C_BD) bytes) + * tx buffer (MAX_TX_SPACE bytes) + */ + + iip = (iic_t *)&immap->im_dprambase[dpaddr]; + memset((void*)iip, 0, sizeof(iic_t)); + + rbase = dpaddr + 64; + tbase = rbase + NUM_RX_BDS * sizeof(I2C_BD); + + /* Disable interrupts */ + i2c->i2c_i2mod = 0x00; + i2c->i2c_i2cmr = 0x00; + i2c->i2c_i2cer = 0xff; + i2c->i2c_i2add = slaveadd; + + /* + * Set the I2C BRG Clock division factor from desired i2c rate + * and current CPU rate (we assume sccr dfbgr field is 0; + * divide BRGCLK by 1) + */ + PRINTD(("[I2C] Setting rate...\n")); + i2c_setrate (gd->brg_clk, CONFIG_SYS_I2C_SPEED) ; + + /* Set I2C controller in master mode */ + i2c->i2c_i2com = 0x01; + + /* Initialize Tx/Rx parameters */ + iip->iic_rbase = rbase; + iip->iic_tbase = tbase; + rxbd = (I2C_BD *)((unsigned char *)&immap->im_dprambase[iip->iic_rbase]); + txbd = (I2C_BD *)((unsigned char *)&immap->im_dprambase[iip->iic_tbase]); + + PRINTD(("[I2C] rbase = %04x\n", iip->iic_rbase)); + PRINTD(("[I2C] tbase = %04x\n", iip->iic_tbase)); + PRINTD(("[I2C] rxbd = %08x\n", (int)rxbd)); + PRINTD(("[I2C] txbd = %08x\n", (int)txbd)); + + /* Set big endian byte order */ + iip->iic_tfcr = 0x10; + iip->iic_rfcr = 0x10; + + /* Set maximum receive size. */ + iip->iic_mrblr = I2C_RXTX_LEN; + + cp->cp_cpcr = mk_cr_cmd(CPM_CR_I2C_PAGE, + CPM_CR_I2C_SBLOCK, + 0x00, + CPM_CR_INIT_TRX) | CPM_CR_FLG; + do { + __asm__ __volatile__ ("eieio"); + } while (cp->cp_cpcr & CPM_CR_FLG); + + /* Clear events and interrupts */ + i2c->i2c_i2cer = 0xff; + i2c->i2c_i2cmr = 0x00; +} + +static +void i2c_newio(i2c_state_t *state) +{ + volatile immap_t *immap = (immap_t *)CONFIG_SYS_IMMR ; + volatile iic_t *iip; + uint dpaddr; + + PRINTD(("[I2C] i2c_newio\n")); + + dpaddr = *((unsigned short*)(&immap->im_dprambase[PROFF_I2C_BASE])); + iip = (iic_t *)&immap->im_dprambase[dpaddr]; + state->rx_idx = 0; + state->tx_idx = 0; + state->rxbd = (void*)&immap->im_dprambase[iip->iic_rbase]; + state->txbd = (void*)&immap->im_dprambase[iip->iic_tbase]; + state->tx_space = MAX_TX_SPACE; + state->tx_buf = (uchar*)state->txbd + NUM_TX_BDS * sizeof(I2C_BD); + state->err_cb = NULL; + state->cb_data = NULL; + + PRINTD(("[I2C] rxbd = %08x\n", (int)state->rxbd)); + PRINTD(("[I2C] txbd = %08x\n", (int)state->txbd)); + PRINTD(("[I2C] tx_buf = %08x\n", (int)state->tx_buf)); + + /* clear the buffer memory */ + memset((char *)state->tx_buf, 0, MAX_TX_SPACE); +} + +static +int i2c_send(i2c_state_t *state, + unsigned char address, + unsigned char secondary_address, + unsigned int flags, + unsigned short size, + unsigned char *dataout) +{ + volatile I2C_BD *txbd; + int i,j; + + PRINTD(("[I2C] i2c_send add=%02d sec=%02d flag=%02d size=%d\n", + address, secondary_address, flags, size)); + + /* trying to send message larger than BD */ + if (size > I2C_RXTX_LEN) + return I2CERR_MSG_TOO_LONG; + + /* no more free bds */ + if (state->tx_idx >= NUM_TX_BDS || state->tx_space < (2 + size)) + return I2CERR_NO_BUFFERS; + + txbd = (I2C_BD *)state->txbd; + txbd->addr = state->tx_buf; + + PRINTD(("[I2C] txbd = %08x\n", (int)txbd)); + + if (flags & I2CF_START_COND) + { + PRINTD(("[I2C] Formatting addresses...\n")); + if (flags & I2CF_ENABLE_SECONDARY) + { + txbd->length = size + 2; /* Length of message plus dest addresses */ + txbd->addr[0] = address << 1; + txbd->addr[1] = secondary_address; + i = 2; + } + else + { + txbd->length = size + 1; /* Length of message plus dest address */ + txbd->addr[0] = address << 1; /* Write destination address to BD */ + i = 1; + } + } + else + { + txbd->length = size; /* Length of message */ + i = 0; + } + + /* set up txbd */ + txbd->status = BD_SC_READY; + if (flags & I2CF_START_COND) + txbd->status |= BD_I2C_TX_START; + if (flags & I2CF_STOP_COND) + txbd->status |= BD_SC_LAST | BD_SC_WRAP; + + /* Copy data to send into buffer */ + PRINTD(("[I2C] copy data...\n")); + for(j = 0; j < size; i++, j++) + txbd->addr[i] = dataout[j]; + + PRINTD(("[I2C] txbd: length=0x%04x status=0x%04x addr[0]=0x%02x addr[1]=0x%02x\n", + txbd->length, + txbd->status, + txbd->addr[0], + txbd->addr[1])); + + /* advance state */ + state->tx_buf += txbd->length; + state->tx_space -= txbd->length; + state->tx_idx++; + state->txbd = (void*)(txbd + 1); + + return 0; +} + +static +int i2c_receive(i2c_state_t *state, + unsigned char address, + unsigned char secondary_address, + unsigned int flags, + unsigned short size_to_expect, + unsigned char *datain) +{ + volatile I2C_BD *rxbd, *txbd; + + PRINTD(("[I2C] i2c_receive %02d %02d %02d\n", address, secondary_address, flags)); + + /* Expected to receive too much */ + if (size_to_expect > I2C_RXTX_LEN) + return I2CERR_MSG_TOO_LONG; + + /* no more free bds */ + if (state->tx_idx >= NUM_TX_BDS || state->rx_idx >= NUM_RX_BDS + || state->tx_space < 2) + return I2CERR_NO_BUFFERS; + + rxbd = (I2C_BD *)state->rxbd; + txbd = (I2C_BD *)state->txbd; + + PRINTD(("[I2C] rxbd = %08x\n", (int)rxbd)); + PRINTD(("[I2C] txbd = %08x\n", (int)txbd)); + + txbd->addr = state->tx_buf; + + /* set up TXBD for destination address */ + if (flags & I2CF_ENABLE_SECONDARY) + { + txbd->length = 2; + txbd->addr[0] = address << 1; /* Write data */ + txbd->addr[1] = secondary_address; /* Internal address */ + txbd->status = BD_SC_READY; + } + else + { + txbd->length = 1 + size_to_expect; + txbd->addr[0] = (address << 1) | 0x01; + txbd->status = BD_SC_READY; + memset(&txbd->addr[1], 0, txbd->length); + } + + /* set up rxbd for reception */ + rxbd->status = BD_SC_EMPTY; + rxbd->length = size_to_expect; + rxbd->addr = datain; + + txbd->status |= BD_I2C_TX_START; + if (flags & I2CF_STOP_COND) + { + txbd->status |= BD_SC_LAST | BD_SC_WRAP; + rxbd->status |= BD_SC_WRAP; + } + + PRINTD(("[I2C] txbd: length=0x%04x status=0x%04x addr[0]=0x%02x addr[1]=0x%02x\n", + txbd->length, + txbd->status, + txbd->addr[0], + txbd->addr[1])); + PRINTD(("[I2C] rxbd: length=0x%04x status=0x%04x addr[0]=0x%02x addr[1]=0x%02x\n", + rxbd->length, + rxbd->status, + rxbd->addr[0], + rxbd->addr[1])); + + /* advance state */ + state->tx_buf += txbd->length; + state->tx_space -= txbd->length; + state->tx_idx++; + state->txbd = (void*)(txbd + 1); + state->rx_idx++; + state->rxbd = (void*)(rxbd + 1); + + return 0; +} + + +static +int i2c_doio(i2c_state_t *state) +{ + volatile immap_t *immap = (immap_t *)CONFIG_SYS_IMMR ; + volatile iic_t *iip; + volatile i2c8260_t *i2c = (i2c8260_t *)&immap->im_i2c; + volatile I2C_BD *txbd, *rxbd; + int n, i, b, rxcnt = 0, rxtimeo = 0, txcnt = 0, txtimeo = 0, rc = 0; + uint dpaddr; + + PRINTD(("[I2C] i2c_doio\n")); + + if (state->tx_idx <= 0 && state->rx_idx <= 0) { + PRINTD(("[I2C] No I/O is queued\n")); + return I2CERR_QUEUE_EMPTY; + } + + dpaddr = *((unsigned short*)(&immap->im_dprambase[PROFF_I2C_BASE])); + iip = (iic_t *)&immap->im_dprambase[dpaddr]; + iip->iic_rbptr = iip->iic_rbase; + iip->iic_tbptr = iip->iic_tbase; + + /* Enable I2C */ + PRINTD(("[I2C] Enabling I2C...\n")); + i2c->i2c_i2mod |= 0x01; + + /* Begin transmission */ + i2c->i2c_i2com |= 0x80; + + /* Loop until transmit & receive completed */ + + if ((n = state->tx_idx) > 0) { + + txbd = ((I2C_BD*)state->txbd) - n; + for (i = 0; i < n; i++) { + txtimeo += TOUT_LOOP * txbd->length; + txbd++; + } + + txbd--; /* wait until last in list is done */ + + PRINTD(("[I2C] Transmitting...(txbd=0x%08lx)\n", (ulong)txbd)); + + udelay(START_DELAY_US); /* give it time to start */ + while((txbd->status & BD_SC_READY) && (++txcnt < txtimeo)) { + udelay(DELAY_US); + if (ctrlc()) + return (-1); + __asm__ __volatile__ ("eieio"); + } + } + + if (txcnt < txtimeo && (n = state->rx_idx) > 0) { + + rxbd = ((I2C_BD*)state->rxbd) - n; + for (i = 0; i < n; i++) { + rxtimeo += TOUT_LOOP * rxbd->length; + rxbd++; + } + + rxbd--; /* wait until last in list is done */ + + PRINTD(("[I2C] Receiving...(rxbd=0x%08lx)\n", (ulong)rxbd)); + + udelay(START_DELAY_US); /* give it time to start */ + while((rxbd->status & BD_SC_EMPTY) && (++rxcnt < rxtimeo)) { + udelay(DELAY_US); + if (ctrlc()) + return (-1); + __asm__ __volatile__ ("eieio"); + } + } + + /* Turn off I2C */ + i2c->i2c_i2mod &= ~0x01; + + if ((n = state->tx_idx) > 0) { + for (i = 0; i < n; i++) { + txbd = ((I2C_BD*)state->txbd) - (n - i); + if ((b = txbd->status & BD_I2C_TX_ERR) != 0) { + if (state->err_cb != NULL) + (*state->err_cb)(I2CECB_TX_ERR|b, i, + state->cb_data); + if (rc == 0) + rc = I2CERR_IO_ERROR; + } + } + } + + if ((n = state->rx_idx) > 0) { + for (i = 0; i < n; i++) { + rxbd = ((I2C_BD*)state->rxbd) - (n - i); + if ((b = rxbd->status & BD_I2C_RX_ERR) != 0) { + if (state->err_cb != NULL) + (*state->err_cb)(I2CECB_RX_ERR|b, i, + state->cb_data); + if (rc == 0) + rc = I2CERR_IO_ERROR; + } + } + } + + if ((txtimeo > 0 && txcnt >= txtimeo) || \ + (rxtimeo > 0 && rxcnt >= rxtimeo)) { + if (state->err_cb != NULL) + (*state->err_cb)(I2CECB_TIMEOUT, -1, state->cb_data); + if (rc == 0) + rc = I2CERR_TIMEOUT; + } + + return (rc); +} + +static void +i2c_probe_callback(int flags, int xnum, void *data) +{ + /* + * the only acceptable errors are a transmit NAK or a receive + * overrun - tx NAK means the device does not exist, rx OV + * means the device must have responded to the slave address + * even though the transfer failed + */ + if (flags == (I2CECB_TX_ERR|I2CECB_TX_NAK)) + *(int *)data |= 1; + if (flags == (I2CECB_RX_ERR|I2CECB_RX_OV)) + *(int *)data |= 2; +} + +int +i2c_probe(uchar chip) +{ + i2c_state_t state; + int rc, err_flag; + uchar buf[1]; + + i2c_newio(&state); + + state.err_cb = i2c_probe_callback; + state.cb_data = (void *) &err_flag; + err_flag = 0; + + rc = i2c_receive(&state, chip, 0, I2CF_START_COND|I2CF_STOP_COND, 1, buf); + + if (rc != 0) + return (rc); /* probe failed */ + + rc = i2c_doio(&state); + + if (rc == 0) + return (0); /* device exists - read succeeded */ + + if (rc == I2CERR_TIMEOUT) + return (-1); /* device does not exist - timeout */ + + if (rc != I2CERR_IO_ERROR || err_flag == 0) + return (rc); /* probe failed */ + + if (err_flag & 1) + return (-1); /* device does not exist - had transmit NAK */ + + return (0); /* device exists - had receive overrun */ +} + + +int +i2c_read(uchar chip, uint addr, int alen, uchar *buffer, int len) +{ + i2c_state_t state; + uchar xaddr[4]; + int rc; + + xaddr[0] = (addr >> 24) & 0xFF; + xaddr[1] = (addr >> 16) & 0xFF; + xaddr[2] = (addr >> 8) & 0xFF; + xaddr[3] = addr & 0xFF; + +#ifdef CONFIG_SYS_I2C_EEPROM_ADDR_OVERFLOW + /* + * EEPROM chips that implement "address overflow" are ones + * like Catalyst 24WC04/08/16 which has 9/10/11 bits of address + * and the extra bits end up in the "chip address" bit slots. + * This makes a 24WC08 (1Kbyte) chip look like four 256 byte + * chips. + * + * Note that we consider the length of the address field to still + * be one byte because the extra address bits are hidden in the + * chip address. + */ + chip |= ((addr >> (alen * 8)) & CONFIG_SYS_I2C_EEPROM_ADDR_OVERFLOW); +#endif + + i2c_newio(&state); + + rc = i2c_send(&state, chip, 0, I2CF_START_COND, alen, &xaddr[4-alen]); + if (rc != 0) { + printf("i2c_read: i2c_send failed (%d)\n", rc); + return 1; + } + + rc = i2c_receive(&state, chip, 0, I2CF_STOP_COND, len, buffer); + if (rc != 0) { + printf("i2c_read: i2c_receive failed (%d)\n", rc); + return 1; + } + + rc = i2c_doio(&state); + if (rc != 0) { + printf("i2c_read: i2c_doio failed (%d)\n", rc); + return 1; + } + return 0; +} + +int +i2c_write(uchar chip, uint addr, int alen, uchar *buffer, int len) +{ + i2c_state_t state; + uchar xaddr[4]; + int rc; + + xaddr[0] = (addr >> 24) & 0xFF; + xaddr[1] = (addr >> 16) & 0xFF; + xaddr[2] = (addr >> 8) & 0xFF; + xaddr[3] = addr & 0xFF; + +#ifdef CONFIG_SYS_I2C_EEPROM_ADDR_OVERFLOW + /* + * EEPROM chips that implement "address overflow" are ones + * like Catalyst 24WC04/08/16 which has 9/10/11 bits of address + * and the extra bits end up in the "chip address" bit slots. + * This makes a 24WC08 (1Kbyte) chip look like four 256 byte + * chips. + * + * Note that we consider the length of the address field to still + * be one byte because the extra address bits are hidden in the + * chip address. + */ + chip |= ((addr >> (alen * 8)) & CONFIG_SYS_I2C_EEPROM_ADDR_OVERFLOW); +#endif + + i2c_newio(&state); + + rc = i2c_send(&state, chip, 0, I2CF_START_COND, alen, &xaddr[4-alen]); + if (rc != 0) { + printf("i2c_write: first i2c_send failed (%d)\n", rc); + return 1; + } + + rc = i2c_send(&state, 0, 0, I2CF_STOP_COND, len, buffer); + if (rc != 0) { + printf("i2c_write: second i2c_send failed (%d)\n", rc); + return 1; + } + + rc = i2c_doio(&state); + if (rc != 0) { + printf("i2c_write: i2c_doio failed (%d)\n", rc); + return 1; + } + return 0; +} + +#if defined(CONFIG_I2C_MULTI_BUS) +/* + * Functions for multiple I2C bus handling + */ +unsigned int i2c_get_bus_num(void) +{ + return i2c_bus_num; +} + +int i2c_set_bus_num(unsigned int bus) +{ +#if defined(CONFIG_I2C_MUX) + if (bus < CONFIG_SYS_MAX_I2C_BUS) { + i2c_bus_num = bus; + } else { + int ret; + + ret = i2x_mux_select_mux(bus); + if (ret == 0) + i2c_bus_num = bus; + else + return ret; + } +#else + if (bus >= CONFIG_SYS_MAX_I2C_BUS) + return -1; + i2c_bus_num = bus; +#endif + return 0; +} + +#endif /* CONFIG_I2C_MULTI_BUS */ +#endif /* CONFIG_HARD_I2C */ diff --git a/arch/powerpc/cpu/mpc8260/interrupts.c b/arch/powerpc/cpu/mpc8260/interrupts.c new file mode 100644 index 0000000..2606c60 --- /dev/null +++ b/arch/powerpc/cpu/mpc8260/interrupts.c @@ -0,0 +1,279 @@ +/* + * (C) Copyright 2000-2002 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Hacked for MPC8260 by Murray.Jensen@cmst.csiro.au, 22-Oct-00 + */ + +#include <common.h> +#include <command.h> +#include <mpc8260.h> +#include <mpc8260_irq.h> +#include <asm/processor.h> + +DECLARE_GLOBAL_DATA_PTR; + +/****************************************************************************/ + +struct irq_action { + interrupt_handler_t *handler; + void *arg; + ulong count; +}; + +static struct irq_action irq_handlers[NR_IRQS]; + +static ulong ppc_cached_irq_mask[NR_MASK_WORDS]; + +/****************************************************************************/ +/* this section was ripped out of arch/powerpc/kernel/ppc8260_pic.c in the */ +/* Linux/PPC 2.4.x source. There was no copyright notice in that file. */ + +/* The 8260 internal interrupt controller. It is usually + * the only interrupt controller. + * There are two 32-bit registers (high/low) for up to 64 + * possible interrupts. + * + * Now, the fun starts.....Interrupt Numbers DO NOT MAP + * in a simple arithmetic fashion to mask or pending registers. + * That is, interrupt 4 does not map to bit position 4. + * We create two tables, indexed by vector number, to indicate + * which register to use and which bit in the register to use. + */ +static u_char irq_to_siureg[] = { + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0 +}; + +static u_char irq_to_siubit[] = { + 31, 16, 17, 18, 19, 20, 21, 22, + 23, 24, 25, 26, 27, 28, 29, 30, + 29, 30, 16, 17, 18, 19, 20, 21, + 22, 23, 24, 25, 26, 27, 28, 31, + 0, 1, 2, 3, 4, 5, 6, 7, + 8, 9, 10, 11, 12, 13, 14, 15, + 15, 14, 13, 12, 11, 10, 9, 8, + 7, 6, 5, 4, 3, 2, 1, 0 +}; + +static void m8260_mask_irq (unsigned int irq_nr) +{ + volatile immap_t *immr = (immap_t *) CONFIG_SYS_IMMR; + int bit, word; + volatile uint *simr; + + bit = irq_to_siubit[irq_nr]; + word = irq_to_siureg[irq_nr]; + + simr = &(immr->im_intctl.ic_simrh); + ppc_cached_irq_mask[word] &= ~(1 << (31 - bit)); + simr[word] = ppc_cached_irq_mask[word]; +} + +static void m8260_unmask_irq (unsigned int irq_nr) +{ + volatile immap_t *immr = (immap_t *) CONFIG_SYS_IMMR; + int bit, word; + volatile uint *simr; + + bit = irq_to_siubit[irq_nr]; + word = irq_to_siureg[irq_nr]; + + simr = &(immr->im_intctl.ic_simrh); + ppc_cached_irq_mask[word] |= (1 << (31 - bit)); + simr[word] = ppc_cached_irq_mask[word]; +} + +static void m8260_mask_and_ack (unsigned int irq_nr) +{ + volatile immap_t *immr = (immap_t *) CONFIG_SYS_IMMR; + int bit, word; + volatile uint *simr, *sipnr; + + bit = irq_to_siubit[irq_nr]; + word = irq_to_siureg[irq_nr]; + + simr = &(immr->im_intctl.ic_simrh); + sipnr = &(immr->im_intctl.ic_sipnrh); + ppc_cached_irq_mask[word] &= ~(1 << (31 - bit)); + simr[word] = ppc_cached_irq_mask[word]; + sipnr[word] = 1 << (31 - bit); +} + +static int m8260_get_irq (struct pt_regs *regs) +{ + volatile immap_t *immr = (immap_t *) CONFIG_SYS_IMMR; + int irq; + unsigned long bits; + + /* For MPC8260, read the SIVEC register and shift the bits down + * to get the irq number. */ + bits = immr->im_intctl.ic_sivec; + irq = bits >> 26; + return irq; +} + +/* end of code ripped out of arch/powerpc/kernel/ppc8260_pic.c */ +/****************************************************************************/ + +int interrupt_init_cpu (unsigned *decrementer_count) +{ + volatile immap_t *immr = (immap_t *) CONFIG_SYS_IMMR; + + *decrementer_count = (gd->bus_clk / 4) / CONFIG_SYS_HZ; + + /* Initialize the default interrupt mapping priorities */ + immr->im_intctl.ic_sicr = 0; + immr->im_intctl.ic_siprr = 0x05309770; + immr->im_intctl.ic_scprrh = 0x05309770; + immr->im_intctl.ic_scprrl = 0x05309770; + + /* disable all interrupts and clear all pending bits */ + immr->im_intctl.ic_simrh = ppc_cached_irq_mask[0] = 0; + immr->im_intctl.ic_simrl = ppc_cached_irq_mask[1] = 0; + immr->im_intctl.ic_sipnrh = 0xffffffff; + immr->im_intctl.ic_sipnrl = 0xffffffff; + +#ifdef CONFIG_HYMOD + /* + * ensure all external interrupt sources default to trigger on + * high-to-low transition (i.e. edge triggered active low) + */ + immr->im_intctl.ic_siexr = -1; +#endif + + return (0); +} + +/****************************************************************************/ + +/* + * Handle external interrupts + */ +void external_interrupt (struct pt_regs *regs) +{ + int irq, unmask = 1; + + irq = m8260_get_irq (regs); + + m8260_mask_and_ack (irq); + + enable_interrupts (); + + if (irq_handlers[irq].handler != NULL) + (*irq_handlers[irq].handler) (irq_handlers[irq].arg); + else { + printf ("\nBogus External Interrupt IRQ %d\n", irq); + /* + * turn off the bogus interrupt, otherwise it + * might repeat forever + */ + unmask = 0; + } + + if (unmask) + m8260_unmask_irq (irq); +} + +/****************************************************************************/ + +/* + * Install and free an interrupt handler. + */ + +void +irq_install_handler (int irq, interrupt_handler_t * handler, void *arg) +{ + if (irq < 0 || irq >= NR_IRQS) { + printf ("irq_install_handler: bad irq number %d\n", irq); + return; + } + + if (irq_handlers[irq].handler != NULL) + printf ("irq_install_handler: 0x%08lx replacing 0x%08lx\n", + (ulong) handler, (ulong) irq_handlers[irq].handler); + + irq_handlers[irq].handler = handler; + irq_handlers[irq].arg = arg; + + m8260_unmask_irq (irq); +} + +void irq_free_handler (int irq) +{ + if (irq < 0 || irq >= NR_IRQS) { + printf ("irq_free_handler: bad irq number %d\n", irq); + return; + } + + m8260_mask_irq (irq); + + irq_handlers[irq].handler = NULL; + irq_handlers[irq].arg = NULL; +} + +/****************************************************************************/ + +void timer_interrupt_cpu (struct pt_regs *regs) +{ + /* nothing to do here */ + return; +} + +/****************************************************************************/ + +#if defined(CONFIG_CMD_IRQ) + +/* ripped this out of ppc4xx/interrupts.c */ + +/******************************************************************************* +* +* irqinfo - print information about PCI devices +* +*/ +void +do_irqinfo (cmd_tbl_t * cmdtp, bd_t * bd, int flag, int argc, char *argv[]) +{ + int irq, re_enable; + + re_enable = disable_interrupts (); + + puts ("\nInterrupt-Information:\n" + "Nr Routine Arg Count\n"); + + for (irq = 0; irq < 32; irq++) + if (irq_handlers[irq].handler != NULL) + printf ("%02d %08lx %08lx %ld\n", irq, + (ulong) irq_handlers[irq].handler, + (ulong) irq_handlers[irq].arg, + irq_handlers[irq].count); + + if (re_enable) + enable_interrupts (); +} + +#endif diff --git a/arch/powerpc/cpu/mpc8260/kgdb.S b/arch/powerpc/cpu/mpc8260/kgdb.S new file mode 100644 index 0000000..c5936c7 --- /dev/null +++ b/arch/powerpc/cpu/mpc8260/kgdb.S @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2000 Murray Jensen <Murray.Jensen@cmst.csiro.au> + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include <config.h> +#include <command.h> +#include <mpc8260.h> +#include <version.h> + +#define CONFIG_8260 1 /* needed for Linux kernel header files */ +#define _LINUX_CONFIG_H 1 /* avoid reading Linux autoconf.h file */ + +#include <ppc_asm.tmpl> +#include <ppc_defs.h> + +#include <asm/cache.h> +#include <asm/mmu.h> + +#if defined(CONFIG_CMD_KGDB) + + /* + * cache flushing routines for kgdb + */ + + .globl kgdb_flush_cache_all +kgdb_flush_cache_all: + mfspr r3, HID0 + ori r3, r3, HID0_ICFI|HID0_DCI /* Invalidate All */ + SYNC + mtspr HID0, r3 + blr + + .globl kgdb_flush_cache_range +kgdb_flush_cache_range: + li r5,CONFIG_SYS_CACHELINE_SIZE-1 + andc r3,r3,r5 + subf r4,r3,r4 + add r4,r4,r5 + srwi. r4,r4,CONFIG_SYS_CACHELINE_SHIFT + beqlr + mtctr r4 + mr r6,r3 +1: dcbst 0,r3 + addi r3,r3,CONFIG_SYS_CACHELINE_SIZE + bdnz 1b + sync /* wait for dcbst's to get to ram */ + mtctr r4 +2: icbi 0,r6 + addi r6,r6,CONFIG_SYS_CACHELINE_SIZE + bdnz 2b + SYNC + blr + +#endif diff --git a/arch/powerpc/cpu/mpc8260/pci.c b/arch/powerpc/cpu/mpc8260/pci.c new file mode 100644 index 0000000..6a14ba4 --- /dev/null +++ b/arch/powerpc/cpu/mpc8260/pci.c @@ -0,0 +1,466 @@ +/* + * (C) Copyright 2003 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * Copyright (c) 2005 MontaVista Software, Inc. + * Vitaly Bordug <vbordug@ru.mvista.com> + * Added support for PCI bridge on MPC8272ADS + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include <common.h> + +#ifdef CONFIG_PCI + +#include <pci.h> +#include <mpc8260.h> +#include <asm/m8260_pci.h> +#include <asm/io.h> +#ifdef CONFIG_OF_LIBFDT +#include <libfdt.h> +#include <fdt_support.h> +#endif + +#if defined CONFIG_MPC8266ADS || defined CONFIG_MPC8272 || defined CONFIG_PM826 +DECLARE_GLOBAL_DATA_PTR; +#endif + +/* + * Local->PCI map (from CPU) controlled by + * MPC826x master window + * + * 0x80000000 - 0xBFFFFFFF CPU2PCI space PCIBR0 + * 0xF4000000 - 0xF7FFFFFF CPU2PCI space PCIBR1 + * + * 0x80000000 - 0x9FFFFFFF 0x80000000 - 0x9FFFFFFF (Outbound ATU #1) + * PCI Mem with prefetch + * + * 0xA0000000 - 0xBFFFFFFF 0xA0000000 - 0xBFFFFFFF (Outbound ATU #2) + * PCI Mem w/o prefetch + * + * 0xF4000000 - 0xF7FFFFFF 0x00000000 - 0x03FFFFFF (Outbound ATU #3) + * 32-bit PCI IO + * + * PCI->Local map (from PCI) + * MPC826x slave window controlled by + * + * 0x00000000 - 0x1FFFFFFF 0x00000000 - 0x1FFFFFFF (Inbound ATU #1) + * MPC826x local memory + */ + +/* + * Slave window that allows PCI masters to access MPC826x local memory. + * This window is set up using the first set of Inbound ATU registers + */ + +#ifndef CONFIG_SYS_PCI_SLV_MEM_LOCAL +#define PCI_SLV_MEM_LOCAL CONFIG_SYS_SDRAM_BASE /* Local base */ +#else +#define PCI_SLV_MEM_LOCAL CONFIG_SYS_PCI_SLV_MEM_LOCAL +#endif + +#ifndef CONFIG_SYS_PCI_SLV_MEM_BUS +#define PCI_SLV_MEM_BUS 0x00000000 /* PCI base */ +#else +#define PCI_SLV_MEM_BUS CONFIG_SYS_PCI_SLV_MEM_BUS +#endif + +#ifndef CONFIG_SYS_PICMR0_MASK_ATTRIB +#define PICMR0_MASK_ATTRIB (PICMR_MASK_512MB | PICMR_ENABLE | \ + PICMR_PREFETCH_EN) +#else +#define PICMR0_MASK_ATTRIB CONFIG_SYS_PICMR0_MASK_ATTRIB +#endif + +/* + * These are the windows that allow the CPU to access PCI address space. + * All three PCI master windows, which allow the CPU to access PCI + * prefetch, non prefetch, and IO space (see below), must all fit within + * these windows. + */ + +/* PCIBR0 */ +#ifndef CONFIG_SYS_PCI_MSTR0_LOCAL +#define PCI_MSTR0_LOCAL 0x80000000 /* Local base */ +#else +#define PCI_MSTR0_LOCAL CONFIG_SYS_PCI_MSTR0_LOCAL +#endif + +#ifndef CONFIG_SYS_PCIMSK0_MASK +#define PCIMSK0_MASK PCIMSK_1GB /* Size of window */ +#else +#define PCIMSK0_MASK CONFIG_SYS_PCIMSK0_MASK +#endif + +/* PCIBR1 */ +#ifndef CONFIG_SYS_PCI_MSTR1_LOCAL +#define PCI_MSTR1_LOCAL 0xF4000000 /* Local base */ +#else +#define PCI_MSTR1_LOCAL CONFIG_SYS_PCI_MSTR1_LOCAL +#endif + +#ifndef CONFIG_SYS_PCIMSK1_MASK +#define PCIMSK1_MASK PCIMSK_64MB /* Size of window */ +#else +#define PCIMSK1_MASK CONFIG_SYS_PCIMSK1_MASK +#endif + +/* + * Master window that allows the CPU to access PCI Memory (prefetch). + * This window will be setup with the first set of Outbound ATU registers + * in the bridge. + */ + +#ifndef CONFIG_SYS_PCI_MSTR_MEM_LOCAL +#define PCI_MSTR_MEM_LOCAL 0x80000000 /* Local base */ +#else +#define PCI_MSTR_MEM_LOCAL CONFIG_SYS_PCI_MSTR_MEM_LOCAL +#endif + +#ifndef CONFIG_SYS_PCI_MSTR_MEM_BUS +#define PCI_MSTR_MEM_BUS 0x80000000 /* PCI base */ +#else +#define PCI_MSTR_MEM_BUS CONFIG_SYS_PCI_MSTR_MEM_BUS +#endif + +#ifndef CONFIG_SYS_CPU_PCI_MEM_START +#define CPU_PCI_MEM_START PCI_MSTR_MEM_LOCAL +#else +#define CPU_PCI_MEM_START CONFIG_SYS_CPU_PCI_MEM_START +#endif + +#ifndef CONFIG_SYS_PCI_MSTR_MEM_SIZE +#define PCI_MSTR_MEM_SIZE 0x10000000 /* 256MB */ +#else +#define PCI_MSTR_MEM_SIZE CONFIG_SYS_PCI_MSTR_MEM_SIZE +#endif + +#ifndef CONFIG_SYS_POCMR0_MASK_ATTRIB +#define POCMR0_MASK_ATTRIB (POCMR_MASK_256MB | POCMR_ENABLE | POCMR_PREFETCH_EN) +#else +#define POCMR0_MASK_ATTRIB CONFIG_SYS_POCMR0_MASK_ATTRIB +#endif + +/* + * Master window that allows the CPU to access PCI Memory (non-prefetch). + * This window will be setup with the second set of Outbound ATU registers + * in the bridge. + */ + +#ifndef CONFIG_SYS_PCI_MSTR_MEMIO_LOCAL +#define PCI_MSTR_MEMIO_LOCAL 0x90000000 /* Local base */ +#else +#define PCI_MSTR_MEMIO_LOCAL CONFIG_SYS_PCI_MSTR_MEMIO_LOCAL +#endif + +#ifndef CONFIG_SYS_PCI_MSTR_MEMIO_BUS +#define PCI_MSTR_MEMIO_BUS 0x90000000 /* PCI base */ +#else +#define PCI_MSTR_MEMIO_BUS CONFIG_SYS_PCI_MSTR_MEMIO_BUS +#endif + +#ifndef CONFIG_SYS_CPU_PCI_MEMIO_START +#define CPU_PCI_MEMIO_START PCI_MSTR_MEMIO_LOCAL +#else +#define CPU_PCI_MEMIO_START CONFIG_SYS_CPU_PCI_MEMIO_START +#endif + +#ifndef CONFIG_SYS_PCI_MSTR_MEMIO_SIZE +#define PCI_MSTR_MEMIO_SIZE 0x10000000 /* 256 MB */ +#else +#define PCI_MSTR_MEMIO_SIZE CONFIG_SYS_PCI_MSTR_MEMIO_SIZE +#endif + +#ifndef CONFIG_SYS_POCMR1_MASK_ATTRIB +#define POCMR1_MASK_ATTRIB (POCMR_MASK_512MB | POCMR_ENABLE) +#else +#define POCMR1_MASK_ATTRIB CONFIG_SYS_POCMR1_MASK_ATTRIB +#endif + +/* + * Master window that allows the CPU to access PCI IO space. + * This window will be setup with the third set of Outbound ATU registers + * in the bridge. + */ + +#ifndef CONFIG_SYS_PCI_MSTR_IO_LOCAL +#define PCI_MSTR_IO_LOCAL 0xA0000000 /* Local base */ +#else +#define PCI_MSTR_IO_LOCAL CONFIG_SYS_PCI_MSTR_IO_LOCAL +#endif + +#ifndef CONFIG_SYS_PCI_MSTR_IO_BUS +#define PCI_MSTR_IO_BUS 0xA0000000 /* PCI base */ +#else +#define PCI_MSTR_IO_BUS CONFIG_SYS_PCI_MSTR_IO_BUS +#endif + +#ifndef CONFIG_SYS_CPU_PCI_IO_START +#define CPU_PCI_IO_START PCI_MSTR_IO_LOCAL +#else +#define CPU_PCI_IO_START CONFIG_SYS_CPU_PCI_IO_START +#endif + +#ifndef CONFIG_SYS_PCI_MSTR_IO_SIZE +#define PCI_MSTR_IO_SIZE 0x10000000 /* 256MB */ +#else +#define PCI_MSTR_IO_SIZE CONFIG_SYS_PCI_MSTR_IO_SIZE +#endif + +#ifndef CONFIG_SYS_POCMR2_MASK_ATTRIB +#define POCMR2_MASK_ATTRIB (POCMR_MASK_256MB | POCMR_ENABLE | POCMR_PCI_IO) +#else +#define POCMR2_MASK_ATTRIB CONFIG_SYS_POCMR2_MASK_ATTRIB +#endif + +/* PCI bus configuration registers. + */ + +#define PCI_CLASS_BRIDGE_CTLR 0x06 + + +static inline void pci_outl (u32 addr, u32 data) +{ + *(volatile u32 *) addr = cpu_to_le32 (data); +} + +void pci_mpc8250_init (struct pci_controller *hose) +{ + u16 tempShort; + + volatile immap_t *immap = (immap_t *) CONFIG_SYS_IMMR; + pci_dev_t host_devno = PCI_BDF (0, 0, 0); + + pci_setup_indirect (hose, CONFIG_SYS_IMMR + PCI_CFG_ADDR_REG, + CONFIG_SYS_IMMR + PCI_CFG_DATA_REG); + + /* + * Setting required to enable local bus for PCI (SIUMCR [LBPC]). + */ +#ifdef CONFIG_MPC8266ADS + immap->im_siu_conf.sc_siumcr = + (immap->im_siu_conf.sc_siumcr & ~SIUMCR_LBPC11) + | SIUMCR_LBPC01; +#elif defined(CONFIG_ADSTYPE) && CONFIG_ADSTYPE == CONFIG_SYS_PQ2FADS +/* nothing to do for this board here */ +#elif defined CONFIG_MPC8272 + immap->im_siu_conf.sc_siumcr = (immap->im_siu_conf.sc_siumcr & + ~SIUMCR_BBD & + ~SIUMCR_ESE & + ~SIUMCR_PBSE & + ~SIUMCR_CDIS & + ~SIUMCR_DPPC11 & + ~SIUMCR_L2CPC11 & + ~SIUMCR_LBPC11 & + ~SIUMCR_APPC11 & + ~SIUMCR_CS10PC11 & + ~SIUMCR_BCTLC11 & + ~SIUMCR_MMR11) + | SIUMCR_DPPC11 + | SIUMCR_L2CPC01 + | SIUMCR_LBPC00 + | SIUMCR_APPC10 + | SIUMCR_CS10PC00 + | SIUMCR_BCTLC00 + | SIUMCR_MMR11; +#elif defined(CONFIG_TQM8272) +/* nothing to do for this Board here */ +#else + /* + * Setting required to enable IRQ1-IRQ7 (SIUMCR [DPPC]), + * and local bus for PCI (SIUMCR [LBPC]). + */ + immap->im_siu_conf.sc_siumcr = (immap->im_siu_conf.sc_siumcr & + ~SIUMCR_LBPC11 & + ~SIUMCR_CS10PC11 & + ~SIUMCR_LBPC11) | + SIUMCR_LBPC01 | + SIUMCR_CS10PC01 | + SIUMCR_APPC10; +#endif + + /* Make PCI lowest priority */ + /* Each 4 bits is a device bus request and the MS 4bits + is highest priority */ + /* Bus 4bit value + --- ---------- + CPM high 0b0000 + CPM middle 0b0001 + CPM low 0b0010 + PCI reguest 0b0011 + Reserved 0b0100 + Reserved 0b0101 + Internal Core 0b0110 + External Master 1 0b0111 + External Master 2 0b1000 + External Master 3 0b1001 + The rest are reserved */ + immap->im_siu_conf.sc_ppc_alrh = 0x61207893; + + /* Park bus on core while modifying PCI Bus accesses */ + immap->im_siu_conf.sc_ppc_acr = 0x6; + + /* + * Set up master windows that allow the CPU to access PCI space. These + * windows are set up using the two SIU PCIBR registers. + */ + immap->im_memctl.memc_pcimsk0 = PCIMSK0_MASK; + immap->im_memctl.memc_pcibr0 = PCI_MSTR0_LOCAL | PCIBR_ENABLE; + +#if defined CONFIG_MPC8266ADS || defined CONFIG_MPC8272 + immap->im_memctl.memc_pcimsk1 = PCIMSK1_MASK; + immap->im_memctl.memc_pcibr1 = PCI_MSTR1_LOCAL | PCIBR_ENABLE; +#endif + + /* Release PCI RST (by default the PCI RST signal is held low) */ + immap->im_pci.pci_gcr = cpu_to_le32 (PCIGCR_PCI_BUS_EN); + + /* give it some time */ + { +#if defined CONFIG_MPC8266ADS || defined CONFIG_MPC8272 + /* Give the PCI cards more time to initialize before query + This might be good for other boards also + */ + int i; + + for (i = 0; i < 1000; ++i) +#endif + udelay (1000); + } + + /* + * Set up master window that allows the CPU to access PCI Memory (prefetch) + * space. This window is set up using the first set of Outbound ATU registers. + */ + immap->im_pci.pci_potar0 = cpu_to_le32 (PCI_MSTR_MEM_BUS >> 12); /* PCI base */ + immap->im_pci.pci_pobar0 = cpu_to_le32 (PCI_MSTR_MEM_LOCAL >> 12); /* Local base */ + immap->im_pci.pci_pocmr0 = cpu_to_le32 (POCMR0_MASK_ATTRIB); /* Size & attribute */ + + /* + * Set up master window that allows the CPU to access PCI Memory (non-prefetch) + * space. This window is set up using the second set of Outbound ATU registers. + */ + immap->im_pci.pci_potar1 = cpu_to_le32 (PCI_MSTR_MEMIO_BUS >> 12); /* PCI base */ + immap->im_pci.pci_pobar1 = cpu_to_le32 (PCI_MSTR_MEMIO_LOCAL >> 12); /* Local base */ + immap->im_pci.pci_pocmr1 = cpu_to_le32 (POCMR1_MASK_ATTRIB); /* Size & attribute */ + + /* + * Set up master window that allows the CPU to access PCI IO space. This window + * is set up using the third set of Outbound ATU registers. + */ + immap->im_pci.pci_potar2 = cpu_to_le32 (PCI_MSTR_IO_BUS >> 12); /* PCI base */ + immap->im_pci.pci_pobar2 = cpu_to_le32 (PCI_MSTR_IO_LOCAL >> 12); /* Local base */ + immap->im_pci.pci_pocmr2 = cpu_to_le32 (POCMR2_MASK_ATTRIB); /* Size & attribute */ + + /* + * Set up slave window that allows PCI masters to access MPC826x local memory. + * This window is set up using the first set of Inbound ATU registers + */ + immap->im_pci.pci_pitar0 = cpu_to_le32 (PCI_SLV_MEM_LOCAL >> 12); /* PCI base */ + immap->im_pci.pci_pibar0 = cpu_to_le32 (PCI_SLV_MEM_BUS >> 12); /* Local base */ + immap->im_pci.pci_picmr0 = cpu_to_le32 (PICMR0_MASK_ATTRIB); /* Size & attribute */ + + /* See above for description - puts PCI request as highest priority */ +#ifdef CONFIG_MPC8272 + immap->im_siu_conf.sc_ppc_alrh = 0x01236745; +#else + immap->im_siu_conf.sc_ppc_alrh = 0x03124567; +#endif + + /* Park the bus on the PCI */ + immap->im_siu_conf.sc_ppc_acr = PPC_ACR_BUS_PARK_PCI; + + /* Host mode - specify the bridge as a host-PCI bridge */ + + pci_hose_write_config_byte (hose, host_devno, PCI_CLASS_CODE, + PCI_CLASS_BRIDGE_CTLR); + + /* Enable the host bridge to be a master on the PCI bus, and to act as a PCI memory target */ + pci_hose_read_config_word (hose, host_devno, PCI_COMMAND, &tempShort); + pci_hose_write_config_word (hose, host_devno, PCI_COMMAND, + tempShort | PCI_COMMAND_MASTER | + PCI_COMMAND_MEMORY); + + /* do some bridge init, should be done on all 8260 based bridges */ + pci_hose_write_config_byte (hose, host_devno, PCI_CACHE_LINE_SIZE, + 0x08); + pci_hose_write_config_byte (hose, host_devno, PCI_LATENCY_TIMER, + 0xF8); + + hose->first_busno = 0; + hose->last_busno = 0xff; + + /* System memory space */ +#if defined CONFIG_MPC8266ADS || defined CONFIG_MPC8272 || defined CONFIG_PM826 + pci_set_region (hose->regions + 0, + PCI_SLV_MEM_BUS, + PCI_SLV_MEM_LOCAL, + gd->ram_size, PCI_REGION_MEM | PCI_REGION_SYS_MEMORY); +#else + pci_set_region (hose->regions + 0, + CONFIG_SYS_SDRAM_BASE, + CONFIG_SYS_SDRAM_BASE, + 0x4000000, PCI_REGION_MEM | PCI_REGION_SYS_MEMORY); +#endif + + /* PCI memory space */ +#if defined CONFIG_MPC8266ADS || defined CONFIG_MPC8272 + pci_set_region (hose->regions + 1, + PCI_MSTR_MEMIO_BUS, + PCI_MSTR_MEMIO_LOCAL, + PCI_MSTR_MEMIO_SIZE, PCI_REGION_MEM); +#else + pci_set_region (hose->regions + 1, + PCI_MSTR_MEM_BUS, + PCI_MSTR_MEM_LOCAL, + PCI_MSTR_MEM_SIZE, PCI_REGION_MEM); +#endif + + /* PCI I/O space */ + pci_set_region (hose->regions + 2, + PCI_MSTR_IO_BUS, + PCI_MSTR_IO_LOCAL, PCI_MSTR_IO_SIZE, PCI_REGION_IO); + + hose->region_count = 3; + + pci_register_hose (hose); + /* Mask off master abort machine checks */ + immap->im_pci.pci_emr &= cpu_to_le32 (~PCI_ERROR_PCI_NO_RSP); + eieio (); + + hose->last_busno = pci_hose_scan (hose); + + + /* clear the error in the error status register */ + immap->im_pci.pci_esr = cpu_to_le32 (PCI_ERROR_PCI_NO_RSP); + + /* unmask master abort machine checks */ + immap->im_pci.pci_emr |= cpu_to_le32 (PCI_ERROR_PCI_NO_RSP); +} + +#if defined(CONFIG_OF_LIBFDT) +void ft_pci_setup(void *blob, bd_t *bd) +{ + do_fixup_by_prop_u32(blob, "device_type", "pci", 4, + "clock-frequency", gd->pci_clk, 1); +} +#endif + +#endif /* CONFIG_PCI */ diff --git a/arch/powerpc/cpu/mpc8260/serial_scc.c b/arch/powerpc/cpu/mpc8260/serial_scc.c new file mode 100644 index 0000000..4ab6a28 --- /dev/null +++ b/arch/powerpc/cpu/mpc8260/serial_scc.c @@ -0,0 +1,498 @@ +/* + * (C) Copyright 2000 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Hacked for MPC8260 by Murray.Jensen@cmst.csiro.au, 19-Oct-00. + */ + +/* + * Minimal serial functions needed to use one of the SCC ports + * as serial console interface. + */ + +#include <common.h> +#include <mpc8260.h> +#include <asm/cpm_8260.h> + +DECLARE_GLOBAL_DATA_PTR; + +#if defined(CONFIG_CONS_ON_SCC) + +#if CONFIG_CONS_INDEX == 1 /* Console on SCC1 */ + +#define SCC_INDEX 0 +#define PROFF_SCC PROFF_SCC1 +#define CMXSCR_MASK (CMXSCR_GR1|CMXSCR_SC1|\ + CMXSCR_RS1CS_MSK|CMXSCR_TS1CS_MSK) +#define CMXSCR_VALUE (CMXSCR_RS1CS_BRG1|CMXSCR_TS1CS_BRG1) +#define CPM_CR_SCC_PAGE CPM_CR_SCC1_PAGE +#define CPM_CR_SCC_SBLOCK CPM_CR_SCC1_SBLOCK + +#elif CONFIG_CONS_INDEX == 2 /* Console on SCC2 */ + +#define SCC_INDEX 1 +#define PROFF_SCC PROFF_SCC2 +#define CMXSCR_MASK (CMXSCR_GR2|CMXSCR_SC2|\ + CMXSCR_RS2CS_MSK|CMXSCR_TS2CS_MSK) +#define CMXSCR_VALUE (CMXSCR_RS2CS_BRG2|CMXSCR_TS2CS_BRG2) +#define CPM_CR_SCC_PAGE CPM_CR_SCC2_PAGE +#define CPM_CR_SCC_SBLOCK CPM_CR_SCC2_SBLOCK + +#elif CONFIG_CONS_INDEX == 3 /* Console on SCC3 */ + +#define SCC_INDEX 2 +#define PROFF_SCC PROFF_SCC3 +#define CMXSCR_MASK (CMXSCR_GR3|CMXSCR_SC3|\ + CMXSCR_RS3CS_MSK|CMXSCR_TS3CS_MSK) +#define CMXSCR_VALUE (CMXSCR_RS3CS_BRG3|CMXSCR_TS3CS_BRG3) +#define CPM_CR_SCC_PAGE CPM_CR_SCC3_PAGE +#define CPM_CR_SCC_SBLOCK CPM_CR_SCC3_SBLOCK + +#elif CONFIG_CONS_INDEX == 4 /* Console on SCC4 */ + +#define SCC_INDEX 3 +#define PROFF_SCC PROFF_SCC4 +#define CMXSCR_MASK (CMXSCR_GR4|CMXSCR_SC4|\ + CMXSCR_RS4CS_MSK|CMXSCR_TS4CS_MSK) +#define CMXSCR_VALUE (CMXSCR_RS4CS_BRG4|CMXSCR_TS4CS_BRG4) +#define CPM_CR_SCC_PAGE CPM_CR_SCC4_PAGE +#define CPM_CR_SCC_SBLOCK CPM_CR_SCC4_SBLOCK + +#else + +#error "console not correctly defined" + +#endif + +int serial_init (void) +{ + volatile immap_t *im = (immap_t *)CONFIG_SYS_IMMR; + volatile scc_t *sp; + volatile scc_uart_t *up; + volatile cbd_t *tbdf, *rbdf; + volatile cpm8260_t *cp = &(im->im_cpm); + uint dpaddr; + + /* initialize pointers to SCC */ + + sp = (scc_t *) &(im->im_scc[SCC_INDEX]); + up = (scc_uart_t *)&im->im_dprambase[PROFF_SCC]; + + /* Disable transmitter/receiver. + */ + sp->scc_gsmrl &= ~(SCC_GSMRL_ENR | SCC_GSMRL_ENT); + + /* put the SCC channel into NMSI (non multiplexd serial interface) + * mode and wire the selected SCC Tx and Rx clocks to BRGx (15-15). + */ + im->im_cpmux.cmx_scr = (im->im_cpmux.cmx_scr&~CMXSCR_MASK)|CMXSCR_VALUE; + + /* Set up the baud rate generator. + */ + serial_setbrg (); + + /* Allocate space for two buffer descriptors in the DP ram. + * damm: allocating space after the two buffers for rx/tx data + */ + + dpaddr = m8260_cpm_dpalloc((2 * sizeof (cbd_t)) + 2, 16); + + /* Set the physical address of the host memory buffers in + * the buffer descriptors. + */ + rbdf = (cbd_t *)&im->im_dprambase[dpaddr]; + rbdf->cbd_bufaddr = (uint) (rbdf+2); + rbdf->cbd_sc = BD_SC_EMPTY | BD_SC_WRAP; + tbdf = rbdf + 1; + tbdf->cbd_bufaddr = ((uint) (rbdf+2)) + 1; + tbdf->cbd_sc = BD_SC_WRAP; + + /* Set up the uart parameters in the parameter ram. + */ + up->scc_genscc.scc_rbase = dpaddr; + up->scc_genscc.scc_tbase = dpaddr+sizeof(cbd_t); + up->scc_genscc.scc_rfcr = CPMFCR_EB; + up->scc_genscc.scc_tfcr = CPMFCR_EB; + up->scc_genscc.scc_mrblr = 1; + up->scc_maxidl = 0; + up->scc_brkcr = 1; + up->scc_parec = 0; + up->scc_frmec = 0; + up->scc_nosec = 0; + up->scc_brkec = 0; + up->scc_uaddr1 = 0; + up->scc_uaddr2 = 0; + up->scc_toseq = 0; + up->scc_char1 = up->scc_char2 = up->scc_char3 = up->scc_char4 = 0x8000; + up->scc_char5 = up->scc_char6 = up->scc_char7 = up->scc_char8 = 0x8000; + up->scc_rccm = 0xc0ff; + + /* Mask all interrupts and remove anything pending. + */ + sp->scc_sccm = 0; + sp->scc_scce = 0xffff; + + /* Set 8 bit FIFO, 16 bit oversampling and UART mode. + */ + sp->scc_gsmrh = SCC_GSMRH_RFW; /* 8 bit FIFO */ + sp->scc_gsmrl = \ + SCC_GSMRL_TDCR_16 | SCC_GSMRL_RDCR_16 | SCC_GSMRL_MODE_UART; + + /* Set CTS flow control, 1 stop bit, 8 bit character length, + * normal async UART mode, no parity + */ + sp->scc_psmr = SCU_PSMR_FLC | SCU_PSMR_CL; + + /* execute the "Init Rx and Tx params" CP command. + */ + + while (cp->cp_cpcr & CPM_CR_FLG) /* wait if cp is busy */ + ; + + cp->cp_cpcr = mk_cr_cmd(CPM_CR_SCC_PAGE, CPM_CR_SCC_SBLOCK, + 0, CPM_CR_INIT_TRX) | CPM_CR_FLG; + + while (cp->cp_cpcr & CPM_CR_FLG) /* wait if cp is busy */ + ; + + /* Enable transmitter/receiver. + */ + sp->scc_gsmrl |= SCC_GSMRL_ENR | SCC_GSMRL_ENT; + + return (0); +} + +void +serial_setbrg (void) +{ +#if defined(CONFIG_CONS_USE_EXTC) + m8260_cpm_extcbrg(SCC_INDEX, gd->baudrate, + CONFIG_CONS_EXTC_RATE, CONFIG_CONS_EXTC_PINSEL); +#else + m8260_cpm_setbrg(SCC_INDEX, gd->baudrate); +#endif +} + +void +serial_putc(const char c) +{ + volatile scc_uart_t *up; + volatile cbd_t *tbdf; + volatile immap_t *im; + + if (c == '\n') + serial_putc ('\r'); + + im = (immap_t *)CONFIG_SYS_IMMR; + up = (scc_uart_t *)&im->im_dprambase[PROFF_SCC]; + tbdf = (cbd_t *)&im->im_dprambase[up->scc_genscc.scc_tbase]; + + /* Wait for last character to go. + */ + while (tbdf->cbd_sc & BD_SC_READY) + ; + + /* Load the character into the transmit buffer. + */ + *(volatile char *)tbdf->cbd_bufaddr = c; + tbdf->cbd_datlen = 1; + tbdf->cbd_sc |= BD_SC_READY; +} + +void +serial_puts (const char *s) +{ + while (*s) { + serial_putc (*s++); + } +} + +int +serial_getc(void) +{ + volatile cbd_t *rbdf; + volatile scc_uart_t *up; + volatile immap_t *im; + unsigned char c; + + im = (immap_t *)CONFIG_SYS_IMMR; + up = (scc_uart_t *)&im->im_dprambase[PROFF_SCC]; + rbdf = (cbd_t *)&im->im_dprambase[up->scc_genscc.scc_rbase]; + + /* Wait for character to show up. + */ + while (rbdf->cbd_sc & BD_SC_EMPTY) + ; + + /* Grab the char and clear the buffer again. + */ + c = *(volatile unsigned char *)rbdf->cbd_bufaddr; + rbdf->cbd_sc |= BD_SC_EMPTY; + + return (c); +} + +int +serial_tstc() +{ + volatile cbd_t *rbdf; + volatile scc_uart_t *up; + volatile immap_t *im; + + im = (immap_t *)CONFIG_SYS_IMMR; + up = (scc_uart_t *)&im->im_dprambase[PROFF_SCC]; + rbdf = (cbd_t *)&im->im_dprambase[up->scc_genscc.scc_rbase]; + + return ((rbdf->cbd_sc & BD_SC_EMPTY) == 0); +} + +#endif /* CONFIG_CONS_ON_SCC */ + +#if defined(CONFIG_KGDB_ON_SCC) + +#if defined(CONFIG_CONS_ON_SCC) && CONFIG_KGDB_INDEX == CONFIG_CONS_INDEX +#error Whoops! serial console and kgdb are on the same scc serial port +#endif + +#if CONFIG_KGDB_INDEX == 1 /* KGDB Port on SCC1 */ + +#define KGDB_SCC_INDEX 0 +#define KGDB_PROFF_SCC PROFF_SCC1 +#define KGDB_CMXSCR_MASK (CMXSCR_GR1|CMXSCR_SC1|\ + CMXSCR_RS1CS_MSK|CMXSCR_TS1CS_MSK) +#define KGDB_CMXSCR_VALUE (CMXSCR_RS1CS_BRG1|CMXSCR_TS1CS_BRG1) +#define KGDB_CPM_CR_SCC_PAGE CPM_CR_SCC1_PAGE +#define KGDB_CPM_CR_SCC_SBLOCK CPM_CR_SCC1_SBLOCK + +#elif CONFIG_KGDB_INDEX == 2 /* KGDB Port on SCC2 */ + +#define KGDB_SCC_INDEX 1 +#define KGDB_PROFF_SCC PROFF_SCC2 +#define KGDB_CMXSCR_MASK (CMXSCR_GR2|CMXSCR_SC2|\ + CMXSCR_RS2CS_MSK|CMXSCR_TS2CS_MSK) +#define KGDB_CMXSCR_VALUE (CMXSCR_RS2CS_BRG2|CMXSCR_TS2CS_BRG2) +#define KGDB_CPM_CR_SCC_PAGE CPM_CR_SCC2_PAGE +#define KGDB_CPM_CR_SCC_SBLOCK CPM_CR_SCC2_SBLOCK + +#elif CONFIG_KGDB_INDEX == 3 /* KGDB Port on SCC3 */ + +#define KGDB_SCC_INDEX 2 +#define KGDB_PROFF_SCC PROFF_SCC3 +#define KGDB_CMXSCR_MASK (CMXSCR_GR3|CMXSCR_SC3|\ + CMXSCR_RS3CS_MSK|CMXSCR_TS3CS_MSK) +#define KGDB_CMXSCR_VALUE (CMXSCR_RS3CS_BRG3|CMXSCR_TS3CS_BRG3) +#define KGDB_CPM_CR_SCC_PAGE CPM_CR_SCC3_PAGE +#define KGDB_CPM_CR_SCC_SBLOCK CPM_CR_SCC3_SBLOCK + +#elif CONFIG_KGDB_INDEX == 4 /* KGDB Port on SCC4 */ + +#define KGDB_SCC_INDEX 3 +#define KGDB_PROFF_SCC PROFF_SCC4 +#define KGDB_CMXSCR_MASK (CMXSCR_GR4|CMXSCR_SC4|\ + CMXSCR_RS4CS_MSK|CMXSCR_TS4CS_MSK) +#define KGDB_CMXSCR_VALUE (CMXSCR_RS4CS_BRG4|CMXSCR_TS4CS_BRG4) +#define KGDB_CPM_CR_SCC_PAGE CPM_CR_SCC4_PAGE +#define KGDB_CPM_CR_SCC_SBLOCK CPM_CR_SCC4_SBLOCK + +#else + +#error "kgdb serial port not correctly defined" + +#endif + +void +kgdb_serial_init (void) +{ + volatile immap_t *im = (immap_t *)CONFIG_SYS_IMMR; + volatile scc_t *sp; + volatile scc_uart_t *up; + volatile cbd_t *tbdf, *rbdf; + volatile cpm8260_t *cp = &(im->im_cpm); + uint dpaddr, speed = CONFIG_KGDB_BAUDRATE; + char *s, *e; + + if ((s = getenv("kgdbrate")) != NULL && *s != '\0') { + ulong rate = simple_strtoul(s, &e, 10); + if (e > s && *e == '\0') + speed = rate; + } + + /* initialize pointers to SCC */ + + sp = (scc_t *) &(im->im_scc[KGDB_SCC_INDEX]); + up = (scc_uart_t *)&im->im_dprambase[KGDB_PROFF_SCC]; + + /* Disable transmitter/receiver. + */ + sp->scc_gsmrl &= ~(SCC_GSMRL_ENR | SCC_GSMRL_ENT); + + /* put the SCC channel into NMSI (non multiplexd serial interface) + * mode and wire the selected SCC Tx and Rx clocks to BRGx (15-15). + */ + im->im_cpmux.cmx_scr = \ + (im->im_cpmux.cmx_scr & ~KGDB_CMXSCR_MASK) | KGDB_CMXSCR_VALUE; + + /* Set up the baud rate generator. + */ +#if defined(CONFIG_KGDB_USE_EXTC) + m8260_cpm_extcbrg(KGDB_SCC_INDEX, speed, + CONFIG_KGDB_EXTC_RATE, CONFIG_KGDB_EXTC_PINSEL); +#else + m8260_cpm_setbrg(KGDB_SCC_INDEX, speed); +#endif + + /* Allocate space for two buffer descriptors in the DP ram. + * damm: allocating space after the two buffers for rx/tx data + */ + + dpaddr = m8260_cpm_dpalloc((2 * sizeof (cbd_t)) + 2, 16); + + /* Set the physical address of the host memory buffers in + * the buffer descriptors. + */ + rbdf = (cbd_t *)&im->im_dprambase[dpaddr]; + rbdf->cbd_bufaddr = (uint) (rbdf+2); + rbdf->cbd_sc = BD_SC_EMPTY | BD_SC_WRAP; + tbdf = rbdf + 1; + tbdf->cbd_bufaddr = ((uint) (rbdf+2)) + 1; + tbdf->cbd_sc = BD_SC_WRAP; + + /* Set up the uart parameters in the parameter ram. + */ + up->scc_genscc.scc_rbase = dpaddr; + up->scc_genscc.scc_tbase = dpaddr+sizeof(cbd_t); + up->scc_genscc.scc_rfcr = CPMFCR_EB; + up->scc_genscc.scc_tfcr = CPMFCR_EB; + up->scc_genscc.scc_mrblr = 1; + up->scc_maxidl = 0; + up->scc_brkcr = 1; + up->scc_parec = 0; + up->scc_frmec = 0; + up->scc_nosec = 0; + up->scc_brkec = 0; + up->scc_uaddr1 = 0; + up->scc_uaddr2 = 0; + up->scc_toseq = 0; + up->scc_char1 = up->scc_char2 = up->scc_char3 = up->scc_char4 = 0x8000; + up->scc_char5 = up->scc_char6 = up->scc_char7 = up->scc_char8 = 0x8000; + up->scc_rccm = 0xc0ff; + + /* Mask all interrupts and remove anything pending. + */ + sp->scc_sccm = 0; + sp->scc_scce = 0xffff; + + /* Set 8 bit FIFO, 16 bit oversampling and UART mode. + */ + sp->scc_gsmrh = SCC_GSMRH_RFW; /* 8 bit FIFO */ + sp->scc_gsmrl = \ + SCC_GSMRL_TDCR_16 | SCC_GSMRL_RDCR_16 | SCC_GSMRL_MODE_UART; + + /* Set CTS flow control, 1 stop bit, 8 bit character length, + * normal async UART mode, no parity + */ + sp->scc_psmr = SCU_PSMR_FLC | SCU_PSMR_CL; + + /* execute the "Init Rx and Tx params" CP command. + */ + + while (cp->cp_cpcr & CPM_CR_FLG) /* wait if cp is busy */ + ; + + cp->cp_cpcr = mk_cr_cmd(KGDB_CPM_CR_SCC_PAGE, KGDB_CPM_CR_SCC_SBLOCK, + 0, CPM_CR_INIT_TRX) | CPM_CR_FLG; + + while (cp->cp_cpcr & CPM_CR_FLG) /* wait if cp is busy */ + ; + + /* Enable transmitter/receiver. + */ + sp->scc_gsmrl |= SCC_GSMRL_ENR | SCC_GSMRL_ENT; + + printf("SCC%d at %dbps ", CONFIG_KGDB_INDEX, speed); +} + +void +putDebugChar(const char c) +{ + volatile scc_uart_t *up; + volatile cbd_t *tbdf; + volatile immap_t *im; + + if (c == '\n') + putDebugChar ('\r'); + + im = (immap_t *)CONFIG_SYS_IMMR; + up = (scc_uart_t *)&im->im_dprambase[KGDB_PROFF_SCC]; + tbdf = (cbd_t *)&im->im_dprambase[up->scc_genscc.scc_tbase]; + + /* Wait for last character to go. + */ + while (tbdf->cbd_sc & BD_SC_READY) + ; + + /* Load the character into the transmit buffer. + */ + *(volatile char *)tbdf->cbd_bufaddr = c; + tbdf->cbd_datlen = 1; + tbdf->cbd_sc |= BD_SC_READY; +} + +void +putDebugStr (const char *s) +{ + while (*s) { + putDebugChar (*s++); + } +} + +int +getDebugChar(void) +{ + volatile cbd_t *rbdf; + volatile scc_uart_t *up; + volatile immap_t *im; + unsigned char c; + + im = (immap_t *)CONFIG_SYS_IMMR; + up = (scc_uart_t *)&im->im_dprambase[KGDB_PROFF_SCC]; + rbdf = (cbd_t *)&im->im_dprambase[up->scc_genscc.scc_rbase]; + + /* Wait for character to show up. + */ + while (rbdf->cbd_sc & BD_SC_EMPTY) + ; + + /* Grab the char and clear the buffer again. + */ + c = *(volatile unsigned char *)rbdf->cbd_bufaddr; + rbdf->cbd_sc |= BD_SC_EMPTY; + + return (c); +} + +void +kgdb_interruptible(int yes) +{ + return; +} + +#endif /* CONFIG_KGDB_ON_SCC */ diff --git a/arch/powerpc/cpu/mpc8260/serial_smc.c b/arch/powerpc/cpu/mpc8260/serial_smc.c new file mode 100644 index 0000000..7b6eaba --- /dev/null +++ b/arch/powerpc/cpu/mpc8260/serial_smc.c @@ -0,0 +1,467 @@ +/* + * (C) Copyright 2000, 2001, 2002 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Hacked for MPC8260 by Murray.Jensen@cmst.csiro.au, 19-Oct-00, with + * changes based on the file arch/powerpc/mbxboot/m8260_tty.c from the + * Linux/PPC sources (m8260_tty.c had no copyright info in it). + */ + +/* + * Minimal serial functions needed to use one of the SMC ports + * as serial console interface. + */ + +#include <common.h> +#include <mpc8260.h> +#include <asm/cpm_8260.h> + +DECLARE_GLOBAL_DATA_PTR; + +#if defined(CONFIG_CONS_ON_SMC) + +#if CONFIG_CONS_INDEX == 1 /* Console on SMC1 */ + +#define SMC_INDEX 0 +#define PROFF_SMC_BASE PROFF_SMC1_BASE +#define PROFF_SMC PROFF_SMC1 +#define CPM_CR_SMC_PAGE CPM_CR_SMC1_PAGE +#define CPM_CR_SMC_SBLOCK CPM_CR_SMC1_SBLOCK +#define CMXSMR_MASK (CMXSMR_SMC1|CMXSMR_SMC1CS_MSK) +#define CMXSMR_VALUE CMXSMR_SMC1CS_BRG7 + +#elif CONFIG_CONS_INDEX == 2 /* Console on SMC2 */ + +#define SMC_INDEX 1 +#define PROFF_SMC_BASE PROFF_SMC2_BASE +#define PROFF_SMC PROFF_SMC2 +#define CPM_CR_SMC_PAGE CPM_CR_SMC2_PAGE +#define CPM_CR_SMC_SBLOCK CPM_CR_SMC2_SBLOCK +#define CMXSMR_MASK (CMXSMR_SMC2|CMXSMR_SMC2CS_MSK) +#define CMXSMR_VALUE CMXSMR_SMC2CS_BRG8 + +#else + +#error "console not correctly defined" + +#endif + +#if !defined(CONFIG_SYS_SMC_RXBUFLEN) +#define CONFIG_SYS_SMC_RXBUFLEN 1 +#define CONFIG_SYS_MAXIDLE 0 +#else +#if !defined(CONFIG_SYS_MAXIDLE) +#error "you must define CONFIG_SYS_MAXIDLE" +#endif +#endif + +typedef volatile struct serialbuffer { + cbd_t rxbd; /* Rx BD */ + cbd_t txbd; /* Tx BD */ + uint rxindex; /* index for next character to read */ + volatile uchar rxbuf[CONFIG_SYS_SMC_RXBUFLEN];/* rx buffers */ + volatile uchar txbuf; /* tx buffers */ +} serialbuffer_t; + +/* map rs_table index to baud rate generator index */ +static unsigned char brg_map[] = { + 6, /* BRG7 for SMC1 */ + 7, /* BRG8 for SMC2 */ + 0, /* BRG1 for SCC1 */ + 1, /* BRG1 for SCC2 */ + 2, /* BRG1 for SCC3 */ + 3, /* BRG1 for SCC4 */ +}; + +int serial_init (void) +{ + volatile immap_t *im = (immap_t *)CONFIG_SYS_IMMR; + volatile smc_t *sp; + volatile smc_uart_t *up; + volatile cpm8260_t *cp = &(im->im_cpm); + uint dpaddr; + volatile serialbuffer_t *rtx; + + /* initialize pointers to SMC */ + + sp = (smc_t *) &(im->im_smc[SMC_INDEX]); + *(ushort *)(&im->im_dprambase[PROFF_SMC_BASE]) = PROFF_SMC; + up = (smc_uart_t *)&im->im_dprambase[PROFF_SMC]; + + /* Disable transmitter/receiver. */ + sp->smc_smcmr &= ~(SMCMR_REN | SMCMR_TEN); + + /* NOTE: I/O port pins are set up via the iop_conf_tab[] table */ + + /* Allocate space for two buffer descriptors in the DP ram. + * damm: allocating space after the two buffers for rx/tx data + */ + + /* allocate size of struct serialbuffer with bd rx/tx, + * buffer rx/tx and rx index + */ + dpaddr = m8260_cpm_dpalloc((sizeof(serialbuffer_t)), 16); + + rtx = (serialbuffer_t *)&im->im_dprambase[dpaddr]; + + /* Set the physical address of the host memory buffers in + * the buffer descriptors. + */ + rtx->rxbd.cbd_bufaddr = (uint) &rtx->rxbuf; + rtx->rxbd.cbd_sc = 0; + + rtx->txbd.cbd_bufaddr = (uint) &rtx->txbuf; + rtx->txbd.cbd_sc = 0; + + /* Set up the uart parameters in the parameter ram. */ + up->smc_rbase = dpaddr; + up->smc_tbase = dpaddr+sizeof(cbd_t); + up->smc_rfcr = CPMFCR_EB; + up->smc_tfcr = CPMFCR_EB; + up->smc_brklen = 0; + up->smc_brkec = 0; + up->smc_brkcr = 0; + + /* Set UART mode, 8 bit, no parity, one stop. + * Enable receive and transmit. + */ + sp->smc_smcmr = smcr_mk_clen(9) | SMCMR_SM_UART; + + /* Mask all interrupts and remove anything pending. */ + sp->smc_smcm = 0; + sp->smc_smce = 0xff; + + /* put the SMC channel into NMSI (non multiplexd serial interface) + * mode and wire either BRG7 to SMC1 or BRG8 to SMC2 (15-17). + */ + im->im_cpmux.cmx_smr = (im->im_cpmux.cmx_smr&~CMXSMR_MASK)|CMXSMR_VALUE; + + /* Set up the baud rate generator. */ + serial_setbrg (); + + /* Make the first buffer the only buffer. */ + rtx->txbd.cbd_sc |= BD_SC_WRAP; + rtx->rxbd.cbd_sc |= BD_SC_EMPTY | BD_SC_WRAP; + + /* single/multi character receive. */ + up->smc_mrblr = CONFIG_SYS_SMC_RXBUFLEN; + up->smc_maxidl = CONFIG_SYS_MAXIDLE; + rtx->rxindex = 0; + + /* Initialize Tx/Rx parameters. */ + + while (cp->cp_cpcr & CPM_CR_FLG) /* wait if cp is busy */ + ; + + cp->cp_cpcr = mk_cr_cmd(CPM_CR_SMC_PAGE, CPM_CR_SMC_SBLOCK, + 0, CPM_CR_INIT_TRX) | CPM_CR_FLG; + + while (cp->cp_cpcr & CPM_CR_FLG) /* wait if cp is busy */ + ; + + /* Enable transmitter/receiver. */ + sp->smc_smcmr |= SMCMR_REN | SMCMR_TEN; + + return (0); +} + +void +serial_setbrg (void) +{ +#if defined(CONFIG_CONS_USE_EXTC) + m8260_cpm_extcbrg(brg_map[SMC_INDEX], gd->baudrate, + CONFIG_CONS_EXTC_RATE, CONFIG_CONS_EXTC_PINSEL); +#else + m8260_cpm_setbrg(brg_map[SMC_INDEX], gd->baudrate); +#endif +} + +void +serial_putc(const char c) +{ + volatile smc_uart_t *up; + volatile immap_t *im = (immap_t *)CONFIG_SYS_IMMR; + volatile serialbuffer_t *rtx; + + if (c == '\n') + serial_putc ('\r'); + + up = (smc_uart_t *)&(im->im_dprambase[PROFF_SMC]); + + rtx = (serialbuffer_t *)&im->im_dprambase[up->smc_rbase]; + + /* Wait for last character to go. */ + while (rtx->txbd.cbd_sc & BD_SC_READY & BD_SC_READY) + ; + rtx->txbuf = c; + rtx->txbd.cbd_datlen = 1; + rtx->txbd.cbd_sc |= BD_SC_READY; +} + +void +serial_puts (const char *s) +{ + while (*s) { + serial_putc (*s++); + } +} + +int +serial_getc(void) +{ + volatile smc_uart_t *up; + volatile immap_t *im = (immap_t *)CONFIG_SYS_IMMR; + volatile serialbuffer_t *rtx; + unsigned char c; + + up = (smc_uart_t *)&(im->im_dprambase[PROFF_SMC]); + + rtx = (serialbuffer_t *)&im->im_dprambase[up->smc_rbase]; + + /* Wait for character to show up. */ + while (rtx->rxbd.cbd_sc & BD_SC_EMPTY) + ; + + /* the characters are read one by one, + * use the rxindex to know the next char to deliver + */ + c = *(unsigned char *) (rtx->rxbd.cbd_bufaddr + rtx->rxindex); + rtx->rxindex++; + + /* check if all char are readout, then make prepare for next receive */ + if (rtx->rxindex >= rtx->rxbd.cbd_datlen) { + rtx->rxindex = 0; + rtx->rxbd.cbd_sc |= BD_SC_EMPTY; + } + return(c); +} + +int +serial_tstc() +{ + volatile smc_uart_t *up; + volatile immap_t *im = (immap_t *)CONFIG_SYS_IMMR; + volatile serialbuffer_t *rtx; + + up = (smc_uart_t *)&(im->im_dprambase[PROFF_SMC]); + rtx = (serialbuffer_t *)&im->im_dprambase[up->smc_rbase]; + + return !(rtx->rxbd.cbd_sc & BD_SC_EMPTY); +} + +#endif /* CONFIG_CONS_ON_SMC */ + +#if defined(CONFIG_KGDB_ON_SMC) + +#if defined(CONFIG_CONS_ON_SMC) && CONFIG_KGDB_INDEX == CONFIG_CONS_INDEX +#error Whoops! serial console and kgdb are on the same smc serial port +#endif + +#if CONFIG_KGDB_INDEX == 1 /* KGDB Port on SMC1 */ + +#define KGDB_SMC_INDEX 0 +#define KGDB_PROFF_SMC_BASE PROFF_SMC1_BASE +#define KGDB_PROFF_SMC PROFF_SMC1 +#define KGDB_CPM_CR_SMC_PAGE CPM_CR_SMC1_PAGE +#define KGDB_CPM_CR_SMC_SBLOCK CPM_CR_SMC1_SBLOCK +#define KGDB_CMXSMR_MASK (CMXSMR_SMC1|CMXSMR_SMC1CS_MSK) +#define KGDB_CMXSMR_VALUE CMXSMR_SMC1CS_BRG7 + +#elif CONFIG_KGDB_INDEX == 2 /* KGDB Port on SMC2 */ + +#define KGDB_SMC_INDEX 1 +#define KGDB_PROFF_SMC_BASE PROFF_SMC2_BASE +#define KGDB_PROFF_SMC PROFF_SMC2 +#define KGDB_CPM_CR_SMC_PAGE CPM_CR_SMC2_PAGE +#define KGDB_CPM_CR_SMC_SBLOCK CPM_CR_SMC2_SBLOCK +#define KGDB_CMXSMR_MASK (CMXSMR_SMC2|CMXSMR_SMC2CS_MSK) +#define KGDB_CMXSMR_VALUE CMXSMR_SMC2CS_BRG8 + +#else + +#error "console not correctly defined" + +#endif + +void +kgdb_serial_init (void) +{ + volatile immap_t *im = (immap_t *)CONFIG_SYS_IMMR; + volatile smc_t *sp; + volatile smc_uart_t *up; + volatile cbd_t *tbdf, *rbdf; + volatile cpm8260_t *cp = &(im->im_cpm); + uint dpaddr, speed = CONFIG_KGDB_BAUDRATE; + char *s, *e; + + if ((s = getenv("kgdbrate")) != NULL && *s != '\0') { + ulong rate = simple_strtoul(s, &e, 10); + if (e > s && *e == '\0') + speed = rate; + } + + /* initialize pointers to SMC */ + + sp = (smc_t *) &(im->im_smc[KGDB_SMC_INDEX]); + *(ushort *)(&im->im_dprambase[KGDB_PROFF_SMC_BASE]) = KGDB_PROFF_SMC; + up = (smc_uart_t *)&im->im_dprambase[KGDB_PROFF_SMC]; + + /* Disable transmitter/receiver. */ + sp->smc_smcmr &= ~(SMCMR_REN | SMCMR_TEN); + + /* NOTE: I/O port pins are set up via the iop_conf_tab[] table */ + + /* Allocate space for two buffer descriptors in the DP ram. + * damm: allocating space after the two buffers for rx/tx data + */ + + dpaddr = m8260_cpm_dpalloc((2 * sizeof (cbd_t)) + 2, 16); + + /* Set the physical address of the host memory buffers in + * the buffer descriptors. + */ + rbdf = (cbd_t *)&im->im_dprambase[dpaddr]; + rbdf->cbd_bufaddr = (uint) (rbdf+2); + rbdf->cbd_sc = 0; + tbdf = rbdf + 1; + tbdf->cbd_bufaddr = ((uint) (rbdf+2)) + 1; + tbdf->cbd_sc = 0; + + /* Set up the uart parameters in the parameter ram. */ + up->smc_rbase = dpaddr; + up->smc_tbase = dpaddr+sizeof(cbd_t); + up->smc_rfcr = CPMFCR_EB; + up->smc_tfcr = CPMFCR_EB; + up->smc_brklen = 0; + up->smc_brkec = 0; + up->smc_brkcr = 0; + + /* Set UART mode, 8 bit, no parity, one stop. + * Enable receive and transmit. + */ + sp->smc_smcmr = smcr_mk_clen(9) | SMCMR_SM_UART; + + /* Mask all interrupts and remove anything pending. */ + sp->smc_smcm = 0; + sp->smc_smce = 0xff; + + /* put the SMC channel into NMSI (non multiplexd serial interface) + * mode and wire either BRG7 to SMC1 or BRG8 to SMC2 (15-17). + */ + im->im_cpmux.cmx_smr = + (im->im_cpmux.cmx_smr & ~KGDB_CMXSMR_MASK) | KGDB_CMXSMR_VALUE; + + /* Set up the baud rate generator. */ +#if defined(CONFIG_KGDB_USE_EXTC) + m8260_cpm_extcbrg(brg_map[KGDB_SMC_INDEX], speed, + CONFIG_KGDB_EXTC_RATE, CONFIG_KGDB_EXTC_PINSEL); +#else + m8260_cpm_setbrg(brg_map[KGDB_SMC_INDEX], speed); +#endif + + /* Make the first buffer the only buffer. */ + tbdf->cbd_sc |= BD_SC_WRAP; + rbdf->cbd_sc |= BD_SC_EMPTY | BD_SC_WRAP; + + /* Single character receive. */ + up->smc_mrblr = 1; + up->smc_maxidl = 0; + + /* Initialize Tx/Rx parameters. */ + + while (cp->cp_cpcr & CPM_CR_FLG) /* wait if cp is busy */ + ; + + cp->cp_cpcr = mk_cr_cmd(KGDB_CPM_CR_SMC_PAGE, KGDB_CPM_CR_SMC_SBLOCK, + 0, CPM_CR_INIT_TRX) | CPM_CR_FLG; + + while (cp->cp_cpcr & CPM_CR_FLG) /* wait if cp is busy */ + ; + + /* Enable transmitter/receiver. */ + sp->smc_smcmr |= SMCMR_REN | SMCMR_TEN; + + printf("SMC%d at %dbps ", CONFIG_KGDB_INDEX, speed); +} + +void +putDebugChar(const char c) +{ + volatile cbd_t *tbdf; + volatile char *buf; + volatile smc_uart_t *up; + volatile immap_t *im = (immap_t *)CONFIG_SYS_IMMR; + + if (c == '\n') + putDebugChar ('\r'); + + up = (smc_uart_t *)&(im->im_dprambase[KGDB_PROFF_SMC]); + + tbdf = (cbd_t *)&im->im_dprambase[up->smc_tbase]; + + /* Wait for last character to go. */ + buf = (char *)tbdf->cbd_bufaddr; + while (tbdf->cbd_sc & BD_SC_READY) + ; + + *buf = c; + tbdf->cbd_datlen = 1; + tbdf->cbd_sc |= BD_SC_READY; +} + +void +putDebugStr (const char *s) +{ + while (*s) { + putDebugChar (*s++); + } +} + +int +getDebugChar(void) +{ + volatile cbd_t *rbdf; + volatile unsigned char *buf; + volatile smc_uart_t *up; + volatile immap_t *im = (immap_t *)CONFIG_SYS_IMMR; + unsigned char c; + + up = (smc_uart_t *)&(im->im_dprambase[KGDB_PROFF_SMC]); + + rbdf = (cbd_t *)&im->im_dprambase[up->smc_rbase]; + + /* Wait for character to show up. */ + buf = (unsigned char *)rbdf->cbd_bufaddr; + while (rbdf->cbd_sc & BD_SC_EMPTY) + ; + c = *buf; + rbdf->cbd_sc |= BD_SC_EMPTY; + + return(c); +} + +void +kgdb_interruptible(int yes) +{ + return; +} + +#endif /* CONFIG_KGDB_ON_SMC */ diff --git a/arch/powerpc/cpu/mpc8260/speed.c b/arch/powerpc/cpu/mpc8260/speed.c new file mode 100644 index 0000000..0e1c2b0 --- /dev/null +++ b/arch/powerpc/cpu/mpc8260/speed.c @@ -0,0 +1,253 @@ +/* + * (C) Copyright 2000-2002 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include <common.h> +#include <mpc8260.h> +#include <asm/processor.h> + +#if defined(CONFIG_BOARD_GET_CPU_CLK_F) +extern unsigned long board_get_cpu_clk_f (void); +#endif + +DECLARE_GLOBAL_DATA_PTR; + +/* ------------------------------------------------------------------------- */ + +/* Bus-to-Core Multiplier */ +#define _1x 2 +#define _1_5x 3 +#define _2x 4 +#define _2_5x 5 +#define _3x 6 +#define _3_5x 7 +#define _4x 8 +#define _4_5x 9 +#define _5x 10 +#define _5_5x 11 +#define _6x 12 +#define _6_5x 13 +#define _7x 14 +#define _7_5x 15 +#define _8x 16 +#define _byp -1 +#define _off -2 +#define _unk -3 + +typedef struct { + int b2c_mult; + int vco_div; + char *freq_60x; + char *freq_core; +} corecnf_t; + +/* + * this table based on "Errata to MPC8260 PowerQUICC II User's Manual", + * Rev. 1, 8/2000, page 10. + */ +corecnf_t corecnf_tab[] = { + { _1_5x, 4, " 33-100", " 33-100" }, /* 0x00 */ + { _1x, 4, " 50-150", " 50-150" }, /* 0x01 */ + { _1x, 8, " 25-75 ", " 25-75 " }, /* 0x02 */ + { _byp, -1, " ?-? ", " ?-? " }, /* 0x03 */ + { _2x, 2, " 50-150", "100-300" }, /* 0x04 */ + { _2x, 4, " 25-75 ", " 50-150" }, /* 0x05 */ + { _2_5x, 2, " 40-120", "100-240" }, /* 0x06 */ + { _4_5x, 2, " 22-65 ", "100-300" }, /* 0x07 */ + { _3x, 2, " 33-100", "100-300" }, /* 0x08 */ + { _5_5x, 2, " 18-55 ", "100-300" }, /* 0x09 */ + { _4x, 2, " 25-75 ", "100-300" }, /* 0x0A */ + { _5x, 2, " 20-60 ", "100-300" }, /* 0x0B */ + { _1_5x, 8, " 16-50 ", " 16-50 " }, /* 0x0C */ + { _6x, 2, " 16-50 ", "100-300" }, /* 0x0D */ + { _3_5x, 2, " 30-85 ", "100-300" }, /* 0x0E */ + { _off, -1, " ?-? ", " ?-? " }, /* 0x0F */ + { _3x, 4, " 16-50 ", " 50-150" }, /* 0x10 */ + { _2_5x, 4, " 20-60 ", " 50-120" }, /* 0x11 */ + { _6_5x, 2, " 15-46 ", "100-300" }, /* 0x12 */ + { _byp, -1, " ?-? ", " ?-? " }, /* 0x13 */ + { _7x, 2, " 14-43 ", "100-300" }, /* 0x14 */ + { _2x, 4, " 25-75 ", " 50-150" }, /* 0x15 */ + { _7_5x, 2, " 13-40 ", "100-300" }, /* 0x16 */ + { _4_5x, 2, " 22-65 ", "100-300" }, /* 0x17 */ + { _unk, -1, " ?-? ", " ?-? " }, /* 0x18 */ + { _5_5x, 2, " 18-55 ", "100-300" }, /* 0x19 */ + { _4x, 2, " 25-75 ", "100-300" }, /* 0x1A */ + { _5x, 2, " 20-60 ", "100-300" }, /* 0x1B */ + { _8x, 2, " 12-38 ", "100-300" }, /* 0x1C */ + { _6x, 2, " 16-50 ", "100-300" }, /* 0x1D */ + { _3_5x, 2, " 30-85 ", "100-300" }, /* 0x1E */ + { _off, -1, " ?-? ", " ?-? " }, /* 0x1F */ +}; + +/* ------------------------------------------------------------------------- */ + +/* + * + */ + +int get_clocks (void) +{ + volatile immap_t *immap = (immap_t *) CONFIG_SYS_IMMR; + ulong clkin; + ulong sccr, dfbrg; + ulong scmr, corecnf, busdf, cpmdf, plldf, pllmf; + corecnf_t *cp; + +#if !defined(CONFIG_8260_CLKIN) +#error clock measuring not implemented yet - define CONFIG_8260_CLKIN +#else +#if defined(CONFIG_BOARD_GET_CPU_CLK_F) + clkin = board_get_cpu_clk_f (); +#else + clkin = CONFIG_8260_CLKIN; +#endif +#endif + + sccr = immap->im_clkrst.car_sccr; + dfbrg = (sccr & SCCR_DFBRG_MSK) >> SCCR_DFBRG_SHIFT; + + scmr = immap->im_clkrst.car_scmr; + corecnf = (scmr & SCMR_CORECNF_MSK) >> SCMR_CORECNF_SHIFT; + cp = &corecnf_tab[corecnf]; + + busdf = (scmr & SCMR_BUSDF_MSK) >> SCMR_BUSDF_SHIFT; + cpmdf = (scmr & SCMR_CPMDF_MSK) >> SCMR_CPMDF_SHIFT; + + /* HiP7, HiP7 Rev01, HiP7 RevA */ + if ((get_pvr () == PVR_8260_HIP7) || + (get_pvr () == PVR_8260_HIP7R1) || + (get_pvr () == PVR_8260_HIP7RA)) { + pllmf = (scmr & SCMR_PLLMF_MSKH7) >> SCMR_PLLMF_SHIFT; + gd->vco_out = clkin * (pllmf + 1); + } else { /* HiP3, HiP4 */ + pllmf = (scmr & SCMR_PLLMF_MSK) >> SCMR_PLLMF_SHIFT; + plldf = (scmr & SCMR_PLLDF) ? 1 : 0; + gd->vco_out = (clkin * 2 * (pllmf + 1)) / (plldf + 1); + } +#if 0 + if (gd->vco_out / (busdf + 1) != clkin) { + /* aaarrrggghhh!!! */ + return (1); + } +#endif + + gd->cpm_clk = gd->vco_out / 2; + gd->bus_clk = clkin; + gd->scc_clk = gd->vco_out / 4; + gd->brg_clk = gd->vco_out / (1 << (2 * (dfbrg + 1))); + + if (cp->b2c_mult > 0) { + gd->cpu_clk = (clkin * cp->b2c_mult) / 2; + } else { + gd->cpu_clk = clkin; + } + +#ifdef CONFIG_PCI + gd->pci_clk = clkin; + + if (sccr & SCCR_PCI_MODE) { + uint pci_div; + uint pcidf = (sccr & SCCR_PCIDF_MSK) >> SCCR_PCIDF_SHIFT; + + if (sccr & SCCR_PCI_MODCK) { + pci_div = 2; + if (pcidf == 9) { + pci_div *= 5; + } else if (pcidf == 0xB) { + pci_div *= 6; + } else { + pci_div *= (pcidf + 1); + } + } else { + pci_div = pcidf + 1; + } + + gd->pci_clk = (gd->cpm_clk * 2) / pci_div; + } +#endif + + return (0); +} + +int prt_8260_clks (void) +{ + volatile immap_t *immap = (immap_t *) CONFIG_SYS_IMMR; + ulong sccr, dfbrg; + ulong scmr, corecnf, busdf, cpmdf, plldf, pllmf, pcidf; + corecnf_t *cp; + + sccr = immap->im_clkrst.car_sccr; + dfbrg = (sccr & SCCR_DFBRG_MSK) >> SCCR_DFBRG_SHIFT; + + scmr = immap->im_clkrst.car_scmr; + corecnf = (scmr & SCMR_CORECNF_MSK) >> SCMR_CORECNF_SHIFT; + busdf = (scmr & SCMR_BUSDF_MSK) >> SCMR_BUSDF_SHIFT; + cpmdf = (scmr & SCMR_CPMDF_MSK) >> SCMR_CPMDF_SHIFT; + plldf = (scmr & SCMR_PLLDF) ? 1 : 0; + pllmf = (scmr & SCMR_PLLMF_MSK) >> SCMR_PLLMF_SHIFT; + pcidf = (sccr & SCCR_PCIDF_MSK) >> SCCR_PCIDF_SHIFT; + + cp = &corecnf_tab[corecnf]; + + puts (CPU_ID_STR " Clock Configuration\n - Bus-to-Core Mult "); + + switch (cp->b2c_mult) { + case _byp: + puts ("BYPASS"); + break; + + case _off: + puts ("OFF"); + break; + + case _unk: + puts ("UNKNOWN"); + break; + + default: + printf ("%d%sx", + cp->b2c_mult / 2, + (cp->b2c_mult % 2) ? ".5" : ""); + break; + } + + printf (", VCO Div %d, 60x Bus Freq %s, Core Freq %s\n", + cp->vco_div, cp->freq_60x, cp->freq_core); + + printf (" - dfbrg %ld, corecnf 0x%02lx, busdf %ld, cpmdf %ld, " + "plldf %ld, pllmf %ld, pcidf %ld\n", + dfbrg, corecnf, busdf, cpmdf, + plldf, pllmf, pcidf); + + printf (" - vco_out %10ld, scc_clk %10ld, brg_clk %10ld\n", + gd->vco_out, gd->scc_clk, gd->brg_clk); + + printf (" - cpu_clk %10ld, cpm_clk %10ld, bus_clk %10ld\n", + gd->cpu_clk, gd->cpm_clk, gd->bus_clk); +#ifdef CONFIG_PCI + printf (" - pci_clk %10ld\n", gd->pci_clk); +#endif + putc ('\n'); + + return (0); +} diff --git a/arch/powerpc/cpu/mpc8260/speed.h b/arch/powerpc/cpu/mpc8260/speed.h new file mode 100644 index 0000000..3f32a14 --- /dev/null +++ b/arch/powerpc/cpu/mpc8260/speed.h @@ -0,0 +1,54 @@ +/* + * (C) Copyright 2000 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +/*----------------------------------------------------------------------- + * Timer value for timer 2, ICLK = 10 + * + * SPEED_FCOUNT2 = GCLK / (16 * (TIMER_TMR_PS + 1)) + * SPEED_TMR3_PS = (GCLK / (16 * SPEED_FCOUNT3)) - 1 + * + * SPEED_FCOUNT2 timer 2 counting frequency + * GCLK CPU clock + * SPEED_TMR2_PS prescaler + */ +#define SPEED_TMR2_PS (250 - 1) /* divide by 250 */ + +/*----------------------------------------------------------------------- + * Timer value for PIT + * + * PIT_TIME = SPEED_PITC / PITRTCLK + * PITRTCLK = 8192 + */ +#define SPEED_PITC (82 << 16) /* start counting from 82 */ + +/* + * The new value for PTA is calculated from + * + * PTA = (gclk * Trefresh) / (2 ^ (2 * DFBRG) * PTP * NCS) + * + * gclk CPU clock (not bus clock !) + * Trefresh Refresh cycle * 4 (four word bursts used) + * DFBRG For normal mode (no clock reduction) always 0 + * PTP Prescaler (already adjusted for no. of banks and 4K / 8K refresh) + * NCS Number of SDRAM banks (chip selects) on this UPM. + */ diff --git a/arch/powerpc/cpu/mpc8260/spi.c b/arch/powerpc/cpu/mpc8260/spi.c new file mode 100644 index 0000000..f5d2ac3 --- /dev/null +++ b/arch/powerpc/cpu/mpc8260/spi.c @@ -0,0 +1,435 @@ +/* + * Copyright (c) 2001 Navin Boppuri / Prashant Patel + * <nboppuri@trinetcommunication.com>, + * <pmpatel@trinetcommunication.com> + * Copyright (c) 2001 Gerd Mennchen <Gerd.Mennchen@icn.siemens.de> + * Copyright (c) 2001-2003 Wolfgang Denk, DENX Software Engineering, <wd@denx.de>. + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +/* + * MPC8260 CPM SPI interface. + * + * Parts of this code are probably not portable and/or specific to + * the board which I used for the tests. Please send fixes/complaints + * to wd@denx.de + * + */ + +#include <common.h> +#include <asm/cpm_8260.h> +#include <linux/ctype.h> +#include <malloc.h> +#include <post.h> +#include <net.h> + +#if defined(CONFIG_SPI) + +/* Warning: + * You cannot enable DEBUG for early system initalization, i. e. when + * this driver is used to read environment parameters like "baudrate" + * from EEPROM which are used to initialize the serial port which is + * needed to print the debug messages... + */ +#undef DEBUG + +#define SPI_EEPROM_WREN 0x06 +#define SPI_EEPROM_RDSR 0x05 +#define SPI_EEPROM_READ 0x03 +#define SPI_EEPROM_WRITE 0x02 + +/* --------------------------------------------------------------- + * Offset for initial SPI buffers in DPRAM: + * We need a 520 byte scratch DPRAM area to use at an early stage. + * It is used between the two initialization calls (spi_init_f() + * and spi_init_r()). + * The value 0x2000 makes it far enough from the start of the data + * area (as well as from the stack pointer). + * --------------------------------------------------------------- */ +#ifndef CONFIG_SYS_SPI_INIT_OFFSET +#define CONFIG_SYS_SPI_INIT_OFFSET 0x2000 +#endif + +#define CPM_SPI_BASE 0x100 + +#ifdef DEBUG + +#define DPRINT(a) printf a; +/* ----------------------------------------------- + * Helper functions to peek into tx and rx buffers + * ----------------------------------------------- */ +static const char * const hex_digit = "0123456789ABCDEF"; + +static char quickhex (int i) +{ + return hex_digit[i]; +} + +static void memdump (void *pv, int num) +{ + int i; + unsigned char *pc = (unsigned char *) pv; + + for (i = 0; i < num; i++) + printf ("%c%c ", quickhex (pc[i] >> 4), quickhex (pc[i] & 0x0f)); + printf ("\t"); + for (i = 0; i < num; i++) + printf ("%c", isprint (pc[i]) ? pc[i] : '.'); + printf ("\n"); +} +#else /* !DEBUG */ + +#define DPRINT(a) + +#endif /* DEBUG */ + +/* ------------------- + * Function prototypes + * ------------------- */ +void spi_init (void); + +ssize_t spi_read (uchar *, int, uchar *, int); +ssize_t spi_write (uchar *, int, uchar *, int); +ssize_t spi_xfer (size_t); + +/* ------------------- + * Variables + * ------------------- */ + +#define MAX_BUFFER 0x104 + +/* ---------------------------------------------------------------------- + * Initially we place the RX and TX buffers at a fixed location in DPRAM! + * ---------------------------------------------------------------------- */ +static uchar *rxbuf = + (uchar *)&((immap_t *)CONFIG_SYS_IMMR)->im_dprambase + [CONFIG_SYS_SPI_INIT_OFFSET]; +static uchar *txbuf = + (uchar *)&((immap_t *)CONFIG_SYS_IMMR)->im_dprambase + [CONFIG_SYS_SPI_INIT_OFFSET+MAX_BUFFER]; + +/* ************************************************************************** + * + * Function: spi_init_f + * + * Description: Init SPI-Controller (ROM part) + * + * return: --- + * + * *********************************************************************** */ +void spi_init_f (void) +{ + unsigned int dpaddr; + + volatile spi_t *spi; + volatile immap_t *immr; + volatile cpm8260_t *cp; + volatile cbd_t *tbdf, *rbdf; + + immr = (immap_t *) CONFIG_SYS_IMMR; + cp = (cpm8260_t *) &immr->im_cpm; + + *(ushort *)(&immr->im_dprambase[PROFF_SPI_BASE]) = PROFF_SPI; + spi = (spi_t *)&immr->im_dprambase[PROFF_SPI]; + +/* 1 */ + /* ------------------------------------------------ + * Initialize Port D SPI pins + * (we are only in Master Mode !) + * ------------------------------------------------ */ + + /* -------------------------------------------- + * GPIO or per. Function + * PPARD[16] = 1 [0x00008000] (SPIMISO) + * PPARD[17] = 1 [0x00004000] (SPIMOSI) + * PPARD[18] = 1 [0x00002000] (SPICLK) + * PPARD[12] = 0 [0x00080000] -> GPIO: (CS for ATC EEPROM) + * -------------------------------------------- */ + immr->im_ioport.iop_ppard |= 0x0000E000; /* set bits */ + immr->im_ioport.iop_ppard &= ~0x00080000; /* reset bit */ + + /* ---------------------------------------------- + * In/Out or per. Function 0/1 + * PDIRD[16] = 0 [0x00008000] -> PERI1: SPIMISO + * PDIRD[17] = 0 [0x00004000] -> PERI1: SPIMOSI + * PDIRD[18] = 0 [0x00002000] -> PERI1: SPICLK + * PDIRD[12] = 1 [0x00080000] -> GPIO OUT: CS for ATC EEPROM + * ---------------------------------------------- */ + immr->im_ioport.iop_pdird &= ~0x0000E000; + immr->im_ioport.iop_pdird |= 0x00080000; + + /* ---------------------------------------------- + * special option reg. + * PSORD[16] = 1 [0x00008000] -> SPIMISO + * PSORD[17] = 1 [0x00004000] -> SPIMOSI + * PSORD[18] = 1 [0x00002000] -> SPICLK + * ---------------------------------------------- */ + immr->im_ioport.iop_psord |= 0x0000E000; + + /* Initialize the parameter ram. + * We need to make sure many things are initialized to zero + */ + spi->spi_rstate = 0; + spi->spi_rdp = 0; + spi->spi_rbptr = 0; + spi->spi_rbc = 0; + spi->spi_rxtmp = 0; + spi->spi_tstate = 0; + spi->spi_tdp = 0; + spi->spi_tbptr = 0; + spi->spi_tbc = 0; + spi->spi_txtmp = 0; + + /* Allocate space for one transmit and one receive buffer + * descriptor in the DP ram + */ +#ifdef CONFIG_SYS_ALLOC_DPRAM + dpaddr = m8260_cpm_dpalloc (sizeof(cbd_t)*2, 8); +#else + dpaddr = CPM_SPI_BASE; +#endif + +/* 3 */ + /* Set up the SPI parameters in the parameter ram */ + spi->spi_rbase = dpaddr; + spi->spi_tbase = dpaddr + sizeof (cbd_t); + + /***********IMPORTANT******************/ + + /* + * Setting transmit and receive buffer descriptor pointers + * initially to rbase and tbase. Only the microcode patches + * documentation talks about initializing this pointer. This + * is missing from the sample I2C driver. If you dont + * initialize these pointers, the kernel hangs. + */ + spi->spi_rbptr = spi->spi_rbase; + spi->spi_tbptr = spi->spi_tbase; + +/* 4 */ + /* Init SPI Tx + Rx Parameters */ + while (cp->cp_cpcr & CPM_CR_FLG) + ; + cp->cp_cpcr = mk_cr_cmd(CPM_CR_SPI_PAGE, CPM_CR_SPI_SBLOCK, + 0, CPM_CR_INIT_TRX) | CPM_CR_FLG; + while (cp->cp_cpcr & CPM_CR_FLG) + ; + +/* 6 */ + /* Set to big endian. */ + spi->spi_tfcr = CPMFCR_EB; + spi->spi_rfcr = CPMFCR_EB; + +/* 7 */ + /* Set maximum receive size. */ + spi->spi_mrblr = MAX_BUFFER; + +/* 8 + 9 */ + /* tx and rx buffer descriptors */ + tbdf = (cbd_t *) & immr->im_dprambase[spi->spi_tbase]; + rbdf = (cbd_t *) & immr->im_dprambase[spi->spi_rbase]; + + tbdf->cbd_sc &= ~BD_SC_READY; + rbdf->cbd_sc &= ~BD_SC_EMPTY; + + /* Set the bd's rx and tx buffer address pointers */ + rbdf->cbd_bufaddr = (ulong) rxbuf; + tbdf->cbd_bufaddr = (ulong) txbuf; + +/* 10 + 11 */ + immr->im_spi.spi_spie = SPI_EMASK; /* Clear all SPI events */ + immr->im_spi.spi_spim = 0x00; /* Mask all SPI events */ + + + return; +} + +/* ************************************************************************** + * + * Function: spi_init_r + * + * Description: Init SPI-Controller (RAM part) - + * The malloc engine is ready and we can move our buffers to + * normal RAM + * + * return: --- + * + * *********************************************************************** */ +void spi_init_r (void) +{ + volatile spi_t *spi; + volatile immap_t *immr; + volatile cpm8260_t *cp; + volatile cbd_t *tbdf, *rbdf; + + immr = (immap_t *) CONFIG_SYS_IMMR; + cp = (cpm8260_t *) &immr->im_cpm; + + spi = (spi_t *)&immr->im_dprambase[PROFF_SPI]; + + /* tx and rx buffer descriptors */ + tbdf = (cbd_t *) & immr->im_dprambase[spi->spi_tbase]; + rbdf = (cbd_t *) & immr->im_dprambase[spi->spi_rbase]; + + /* Allocate memory for RX and TX buffers */ + rxbuf = (uchar *) malloc (MAX_BUFFER); + txbuf = (uchar *) malloc (MAX_BUFFER); + + rbdf->cbd_bufaddr = (ulong) rxbuf; + tbdf->cbd_bufaddr = (ulong) txbuf; + + return; +} + +/**************************************************************************** + * Function: spi_write + **************************************************************************** */ +ssize_t spi_write (uchar *addr, int alen, uchar *buffer, int len) +{ + int i; + + memset(rxbuf, 0, MAX_BUFFER); + memset(txbuf, 0, MAX_BUFFER); + *txbuf = SPI_EEPROM_WREN; /* write enable */ + spi_xfer(1); + memcpy(txbuf, addr, alen); + *txbuf = SPI_EEPROM_WRITE; /* WRITE memory array */ + memcpy(alen + txbuf, buffer, len); + spi_xfer(alen + len); + /* ignore received data */ + for (i = 0; i < 1000; i++) { + *txbuf = SPI_EEPROM_RDSR; /* read status */ + txbuf[1] = 0; + spi_xfer(2); + if (!(rxbuf[1] & 1)) { + break; + } + udelay(1000); + } + if (i >= 1000) { + printf ("*** spi_write: Time out while writing!\n"); + } + + return len; +} + +/**************************************************************************** + * Function: spi_read + **************************************************************************** */ +ssize_t spi_read (uchar *addr, int alen, uchar *buffer, int len) +{ + memset(rxbuf, 0, MAX_BUFFER); + memset(txbuf, 0, MAX_BUFFER); + memcpy(txbuf, addr, alen); + *txbuf = SPI_EEPROM_READ; /* READ memory array */ + + /* + * There is a bug in 860T (?) that cuts the last byte of input + * if we're reading into DPRAM. The solution we choose here is + * to always read len+1 bytes (we have one extra byte at the + * end of the buffer). + */ + spi_xfer(alen + len + 1); + memcpy(buffer, alen + rxbuf, len); + + return len; +} + +/**************************************************************************** + * Function: spi_xfer + **************************************************************************** */ +ssize_t spi_xfer (size_t count) +{ + volatile immap_t *immr; + volatile cpm8260_t *cp; + volatile spi_t *spi; + cbd_t *tbdf, *rbdf; + int tm; + + DPRINT (("*** spi_xfer entered ***\n")); + + immr = (immap_t *) CONFIG_SYS_IMMR; + cp = (cpm8260_t *) &immr->im_cpm; + + spi = (spi_t *)&immr->im_dprambase[PROFF_SPI]; + + tbdf = (cbd_t *) & immr->im_dprambase[spi->spi_tbase]; + rbdf = (cbd_t *) & immr->im_dprambase[spi->spi_rbase]; + + /* Board-specific: Set CS for device (ATC EEPROM) */ + immr->im_ioport.iop_pdatd &= ~0x00080000; + + /* Setting tx bd status and data length */ + tbdf->cbd_sc = BD_SC_READY | BD_SC_LAST | BD_SC_WRAP; + tbdf->cbd_datlen = count; + + DPRINT (("*** spi_xfer: Bytes to be xferred: %d ***\n", + tbdf->cbd_datlen)); + + /* Setting rx bd status and data length */ + rbdf->cbd_sc = BD_SC_EMPTY | BD_SC_WRAP; + rbdf->cbd_datlen = 0; /* rx length has no significance */ + + immr->im_spi.spi_spmode = SPMODE_REV | + SPMODE_MSTR | + SPMODE_EN | + SPMODE_LEN(8) | /* 8 Bits per char */ + SPMODE_PM(0x8) ; /* medium speed */ + immr->im_spi.spi_spie = SPI_EMASK; /* Clear all SPI events */ + immr->im_spi.spi_spim = 0x00; /* Mask all SPI events */ + + /* start spi transfer */ + DPRINT (("*** spi_xfer: Performing transfer ...\n")); + immr->im_spi.spi_spcom |= SPI_STR; /* Start transmit */ + + /* -------------------------------- + * Wait for SPI transmit to get out + * or time out (1 second = 1000 ms) + * -------------------------------- */ + for (tm=0; tm<1000; ++tm) { + if (immr->im_spi.spi_spie & SPI_TXB) { /* Tx Buffer Empty */ + DPRINT (("*** spi_xfer: Tx buffer empty\n")); + break; + } + if ((tbdf->cbd_sc & BD_SC_READY) == 0) { + DPRINT (("*** spi_xfer: Tx BD done\n")); + break; + } + udelay (1000); + } + if (tm >= 1000) { + printf ("*** spi_xfer: Time out while xferring to/from SPI!\n"); + } + DPRINT (("*** spi_xfer: ... transfer ended\n")); + +#ifdef DEBUG + printf ("\nspi_xfer: txbuf after xfer\n"); + memdump ((void *) txbuf, 16); /* dump of txbuf before transmit */ + printf ("spi_xfer: rxbuf after xfer\n"); + memdump ((void *) rxbuf, 16); /* dump of rxbuf after transmit */ + printf ("\n"); +#endif + + /* Clear CS for device */ + immr->im_ioport.iop_pdatd |= 0x00080000; + + return count; +} +#endif /* CONFIG_SPI */ diff --git a/arch/powerpc/cpu/mpc8260/start.S b/arch/powerpc/cpu/mpc8260/start.S new file mode 100644 index 0000000..a435042 --- /dev/null +++ b/arch/powerpc/cpu/mpc8260/start.S @@ -0,0 +1,1023 @@ +/* + * Copyright (C) 1998 Dan Malek <dmalek@jlc.net> + * Copyright (C) 1999 Magnus Damm <kieraypc01.p.y.kie.era.ericsson.se> + * Copyright (C) 2000, 2001,2002 Wolfgang Denk <wd@denx.de> + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +/* + * U-Boot - Startup Code for MPC8260 PowerPC based Embedded Boards + */ +#include <config.h> +#include <mpc8260.h> +#include <timestamp.h> +#include <version.h> + +#define CONFIG_8260 1 /* needed for Linux kernel header files */ +#define _LINUX_CONFIG_H 1 /* avoid reading Linux autoconf.h file */ + +#include <ppc_asm.tmpl> +#include <ppc_defs.h> + +#include <asm/cache.h> +#include <asm/mmu.h> + +#ifndef CONFIG_IDENT_STRING +#define CONFIG_IDENT_STRING "" +#endif + +/* We don't want the MMU yet. +*/ +#undef MSR_KERNEL +/* Floating Point enable, Machine Check and Recoverable Interr. */ +#ifdef DEBUG +#define MSR_KERNEL (MSR_FP|MSR_RI) +#else +#define MSR_KERNEL (MSR_FP|MSR_ME|MSR_RI) +#endif + +/* + * Set up GOT: Global Offset Table + * + * Use r12 to access the GOT + */ + START_GOT + GOT_ENTRY(_GOT2_TABLE_) + GOT_ENTRY(_FIXUP_TABLE_) + + GOT_ENTRY(_start) + GOT_ENTRY(_start_of_vectors) + GOT_ENTRY(_end_of_vectors) + GOT_ENTRY(transfer_to_handler) + + GOT_ENTRY(__init_end) + GOT_ENTRY(_end) + GOT_ENTRY(__bss_start) +#if defined(CONFIG_HYMOD) + GOT_ENTRY(environment) +#endif + END_GOT + +/* + * Version string - must be in data segment because MPC8260 uses the first + * 256 bytes for the Hard Reset Configuration Word table (see below). + * Similarly, can't have the U-Boot Magic Number as the first thing in + * the image - don't know how this will affect the image tools, but I guess + * I'll find out soon + */ + .data + .globl version_string +version_string: + .ascii U_BOOT_VERSION + .ascii " (", U_BOOT_DATE, " - ", U_BOOT_TIME, ")" + .ascii CONFIG_IDENT_STRING, "\0" + +/* + * Hard Reset Configuration Word (HRCW) table + * + * The Hard Reset Configuration Word (HRCW) sets a number of useful things + * such as whether there is an external memory controller, whether the + * PowerPC core is disabled (i.e. only the communications processor is + * active, accessed by another CPU on the bus), whether using external + * arbitration, external bus mode, boot port size, core initial prefix, + * internal space base, boot memory space, etc. + * + * These things dictate where the processor begins execution, where the + * boot ROM appears in memory, the memory controller setup when access + * boot ROM, etc. The HRCW is *extremely* important. + * + * The HRCW is read from the bus during reset. One CPU on the bus will + * be a hard reset configuration master, any others will be hard reset + * configuration slaves. The master reads eight HRCWs from flash during + * reset - the first it uses for itself, the other 7 it communicates to + * up to 7 configuration slaves by some complicated mechanism, which is + * not really important here. + * + * The configuration master performs 32 successive reads starting at address + * 0 and incrementing by 8 each read (i.e. on 64 bit boundaries) but only 8 + * bits is read, and always from byte lane D[0-7] (so that port size of the + * boot device does not matter). The first four reads form the 32 bit HRCW + * for the master itself. The second four reads form the HRCW for the first + * slave, and so on, up to seven slaves. The 32 bit HRCW is formed by + * concatenating the four bytes, with the first read placed in byte 0 (the + * most significant byte), and so on with the fourth read placed in byte 3 + * (the least significant byte). + */ +#define _HRCW_TABLE_ENTRY(w) \ + .fill 8,1,(((w)>>24)&0xff); \ + .fill 8,1,(((w)>>16)&0xff); \ + .fill 8,1,(((w)>> 8)&0xff); \ + .fill 8,1,(((w) )&0xff) + .text + .globl _hrcw_table +_hrcw_table: + _HRCW_TABLE_ENTRY(CONFIG_SYS_HRCW_MASTER) + _HRCW_TABLE_ENTRY(CONFIG_SYS_HRCW_SLAVE1) + _HRCW_TABLE_ENTRY(CONFIG_SYS_HRCW_SLAVE2) + _HRCW_TABLE_ENTRY(CONFIG_SYS_HRCW_SLAVE3) + _HRCW_TABLE_ENTRY(CONFIG_SYS_HRCW_SLAVE4) + _HRCW_TABLE_ENTRY(CONFIG_SYS_HRCW_SLAVE5) + _HRCW_TABLE_ENTRY(CONFIG_SYS_HRCW_SLAVE6) + _HRCW_TABLE_ENTRY(CONFIG_SYS_HRCW_SLAVE7) +/* + * After configuration, a system reset exception is executed using the + * vector at offset 0x100 relative to the base set by MSR[IP]. If MSR[IP] + * is 0, the base address is 0x00000000. If MSR[IP] is 1, the base address + * is 0xfff00000. In the case of a Power On Reset or Hard Reset, the value + * of MSR[IP] is determined by the CIP field in the HRCW. + * + * Other bits in the HRCW set up the Base Address and Port Size in BR0. + * This determines the location of the boot ROM (flash or EPROM) in the + * processor's address space at boot time. As long as the HRCW is set up + * so that we eventually end up executing the code below when the processor + * executes the reset exception, the actual values used should not matter. + * + * Once we have got here, the address mask in OR0 is cleared so that the + * bottom 32K of the boot ROM is effectively repeated all throughout the + * processor's address space, after which we can jump to the absolute + * address at which the boot ROM was linked at compile time, and proceed + * to initialise the memory controller without worrying if the rug will be + * pulled out from under us, so to speak (it will be fine as long as we + * configure BR0 with the same boot ROM link address). + */ + . = EXC_OFF_SYS_RESET + + .globl _start +_start: + li r21, BOOTFLAG_COLD /* Normal Power-On: Boot from FLASH*/ + nop + b boot_cold + + . = EXC_OFF_SYS_RESET + 0x10 + + .globl _start_warm +_start_warm: + li r21, BOOTFLAG_WARM /* Software reboot */ + b boot_warm + +boot_cold: +#if defined(CONFIG_MPC8260ADS) && defined(CONFIG_SYS_DEFAULT_IMMR) + lis r3, CONFIG_SYS_DEFAULT_IMMR@h + nop + lwz r4, 0(r3) + nop + rlwinm r4, r4, 0, 8, 5 + nop + oris r4, r4, 0x0200 + nop + stw r4, 0(r3) + nop +#endif /* CONFIG_MPC8260ADS && CONFIG_SYS_DEFAULT_IMMR */ +boot_warm: + mfmsr r5 /* save msr contents */ + +#if defined(CONFIG_COGENT) + /* this is what the cogent EPROM does */ + li r0, 0 + mtmsr r0 + isync + bl cogent_init_8260 +#endif /* CONFIG_COGENT */ + +#if defined(CONFIG_SYS_DEFAULT_IMMR) + lis r3, CONFIG_SYS_IMMR@h + ori r3, r3, CONFIG_SYS_IMMR@l + lis r4, CONFIG_SYS_DEFAULT_IMMR@h + stw r3, 0x1A8(r4) +#endif /* CONFIG_SYS_DEFAULT_IMMR */ + + /* Initialise the MPC8260 processor core */ + /*--------------------------------------------------------------*/ + + bl init_8260_core + +#ifndef CONFIG_SYS_RAMBOOT + /* When booting from ROM (Flash or EPROM), clear the */ + /* Address Mask in OR0 so ROM appears everywhere */ + /*--------------------------------------------------------------*/ + + lis r3, (CONFIG_SYS_IMMR+IM_REGBASE)@h + lwz r4, IM_OR0@l(r3) + li r5, 0x7fff + and r4, r4, r5 + stw r4, IM_OR0@l(r3) + + /* Calculate absolute address in FLASH and jump there */ + /*--------------------------------------------------------------*/ + + lis r3, CONFIG_SYS_MONITOR_BASE@h + ori r3, r3, CONFIG_SYS_MONITOR_BASE@l + addi r3, r3, in_flash - _start + EXC_OFF_SYS_RESET + mtlr r3 + blr + +in_flash: +#endif /* CONFIG_SYS_RAMBOOT */ + + /* initialize some things that are hard to access from C */ + /*--------------------------------------------------------------*/ + + lis r3, CONFIG_SYS_IMMR@h /* set up stack in internal DPRAM */ + ori r1, r3, CONFIG_SYS_INIT_SP_OFFSET + li r0, 0 /* Make room for stack frame header and */ + stwu r0, -4(r1) /* clear final stack frame so that */ + stwu r0, -4(r1) /* stack backtraces terminate cleanly */ + + /* let the C-code set up the rest */ + /* */ + /* Be careful to keep code relocatable ! */ + /*--------------------------------------------------------------*/ + + GET_GOT /* initialize GOT access */ + + /* r3: IMMR */ + bl cpu_init_f /* run low-level CPU init code (in Flash)*/ + +#ifdef DEBUG + bl init_debug /* set up debugging stuff */ +#endif + + mr r3, r21 + /* r3: BOOTFLAG */ + bl board_init_f /* run 1st part of board init code (in Flash)*/ + +/* + * Vector Table + */ + + .globl _start_of_vectors +_start_of_vectors: + +/* Machine check */ + STD_EXCEPTION(0x200, MachineCheck, MachineCheckException) + +/* Data Storage exception. */ + STD_EXCEPTION(0x300, DataStorage, UnknownException) + +/* Instruction Storage exception. */ + STD_EXCEPTION(0x400, InstStorage, UnknownException) + +/* External Interrupt exception. */ + STD_EXCEPTION(0x500, ExtInterrupt, external_interrupt) + +/* Alignment exception. */ + . = 0x600 +Alignment: + EXCEPTION_PROLOG(SRR0, SRR1) + mfspr r4,DAR + stw r4,_DAR(r21) + mfspr r5,DSISR + stw r5,_DSISR(r21) + addi r3,r1,STACK_FRAME_OVERHEAD + EXC_XFER_TEMPLATE(Alignment, AlignmentException, MSR_KERNEL, COPY_EE) + +/* Program check exception */ + . = 0x700 +ProgramCheck: + EXCEPTION_PROLOG(SRR0, SRR1) + addi r3,r1,STACK_FRAME_OVERHEAD + EXC_XFER_TEMPLATE(ProgramCheck, ProgramCheckException, + MSR_KERNEL, COPY_EE) + + STD_EXCEPTION(0x800, FPUnavailable, UnknownException) + + /* I guess we could implement decrementer, and may have + * to someday for timekeeping. + */ + STD_EXCEPTION(0x900, Decrementer, timer_interrupt) + + STD_EXCEPTION(0xa00, Trap_0a, UnknownException) + STD_EXCEPTION(0xb00, Trap_0b, UnknownException) + STD_EXCEPTION(0xc00, SystemCall, UnknownException) + STD_EXCEPTION(0xd00, SingleStep, UnknownException) + + STD_EXCEPTION(0xe00, Trap_0e, UnknownException) + STD_EXCEPTION(0xf00, Trap_0f, UnknownException) + + STD_EXCEPTION(0x1000, InstructionTLBMiss, UnknownException) + STD_EXCEPTION(0x1100, DataLoadTLBMiss, UnknownException) + STD_EXCEPTION(0x1200, DataStoreTLBMiss, UnknownException) +#ifdef DEBUG + . = 0x1300 + /* + * This exception occurs when the program counter matches the + * Instruction Address Breakpoint Register (IABR). + * + * I want the cpu to halt if this occurs so I can hunt around + * with the debugger and look at things. + * + * When DEBUG is defined, both machine check enable (in the MSR) + * and checkstop reset enable (in the reset mode register) are + * turned off and so a checkstop condition will result in the cpu + * halting. + * + * I force the cpu into a checkstop condition by putting an illegal + * instruction here (at least this is the theory). + * + * well - that didnt work, so just do an infinite loop! + */ +1: b 1b +#else + STD_EXCEPTION(0x1300, InstructionBreakpoint, DebugException) +#endif + STD_EXCEPTION(0x1400, SMI, UnknownException) + + STD_EXCEPTION(0x1500, Trap_15, UnknownException) + STD_EXCEPTION(0x1600, Trap_16, UnknownException) + STD_EXCEPTION(0x1700, Trap_17, UnknownException) + STD_EXCEPTION(0x1800, Trap_18, UnknownException) + STD_EXCEPTION(0x1900, Trap_19, UnknownException) + STD_EXCEPTION(0x1a00, Trap_1a, UnknownException) + STD_EXCEPTION(0x1b00, Trap_1b, UnknownException) + STD_EXCEPTION(0x1c00, Trap_1c, UnknownException) + STD_EXCEPTION(0x1d00, Trap_1d, UnknownException) + STD_EXCEPTION(0x1e00, Trap_1e, UnknownException) + STD_EXCEPTION(0x1f00, Trap_1f, UnknownException) + STD_EXCEPTION(0x2000, Trap_20, UnknownException) + STD_EXCEPTION(0x2100, Trap_21, UnknownException) + STD_EXCEPTION(0x2200, Trap_22, UnknownException) + STD_EXCEPTION(0x2300, Trap_23, UnknownException) + STD_EXCEPTION(0x2400, Trap_24, UnknownException) + STD_EXCEPTION(0x2500, Trap_25, UnknownException) + STD_EXCEPTION(0x2600, Trap_26, UnknownException) + STD_EXCEPTION(0x2700, Trap_27, UnknownException) + STD_EXCEPTION(0x2800, Trap_28, UnknownException) + STD_EXCEPTION(0x2900, Trap_29, UnknownException) + STD_EXCEPTION(0x2a00, Trap_2a, UnknownException) + STD_EXCEPTION(0x2b00, Trap_2b, UnknownException) + STD_EXCEPTION(0x2c00, Trap_2c, UnknownException) + STD_EXCEPTION(0x2d00, Trap_2d, UnknownException) + STD_EXCEPTION(0x2e00, Trap_2e, UnknownException) + STD_EXCEPTION(0x2f00, Trap_2f, UnknownException) + + + .globl _end_of_vectors +_end_of_vectors: + + . = 0x3000 + +/* + * This code finishes saving the registers to the exception frame + * and jumps to the appropriate handler for the exception. + * Register r21 is pointer into trap frame, r1 has new stack pointer. + */ + .globl transfer_to_handler +transfer_to_handler: + stw r22,_NIP(r21) + lis r22,MSR_POW@h + andc r23,r23,r22 + stw r23,_MSR(r21) + SAVE_GPR(7, r21) + SAVE_4GPRS(8, r21) + SAVE_8GPRS(12, r21) + SAVE_8GPRS(24, r21) + mflr r23 + andi. r24,r23,0x3f00 /* get vector offset */ + stw r24,TRAP(r21) + li r22,0 + stw r22,RESULT(r21) + lwz r24,0(r23) /* virtual address of handler */ + lwz r23,4(r23) /* where to go when done */ + mtspr SRR0,r24 + mtspr SRR1,r20 + mtlr r23 + SYNC + rfi /* jump to handler, enable MMU */ + +int_return: + mfmsr r28 /* Disable interrupts */ + li r4,0 + ori r4,r4,MSR_EE + andc r28,r28,r4 + SYNC /* Some chip revs need this... */ + mtmsr r28 + SYNC + lwz r2,_CTR(r1) + lwz r0,_LINK(r1) + mtctr r2 + mtlr r0 + lwz r2,_XER(r1) + lwz r0,_CCR(r1) + mtspr XER,r2 + mtcrf 0xFF,r0 + REST_10GPRS(3, r1) + REST_10GPRS(13, r1) + REST_8GPRS(23, r1) + REST_GPR(31, r1) + lwz r2,_NIP(r1) /* Restore environment */ + lwz r0,_MSR(r1) + mtspr SRR0,r2 + mtspr SRR1,r0 + lwz r0,GPR0(r1) + lwz r2,GPR2(r1) + lwz r1,GPR1(r1) + SYNC + rfi + +#if defined(CONFIG_COGENT) + +/* + * This code initialises the MPC8260 processor core + * (conforms to PowerPC 603e spec) + */ + + .globl cogent_init_8260 +cogent_init_8260: + + /* Taken from page 14 of CMA282 manual */ + /*--------------------------------------------------------------*/ + + lis r4, (CONFIG_SYS_IMMR+IM_REGBASE)@h + lis r3, CONFIG_SYS_IMMR@h + stw r3, IM_IMMR@l(r4) + lwz r3, IM_IMMR@l(r4) + stw r3, 0(r0) + lis r3, CONFIG_SYS_SYPCR@h + ori r3, r3, CONFIG_SYS_SYPCR@l + stw r3, IM_SYPCR@l(r4) + lwz r3, IM_SYPCR@l(r4) + stw r3, 4(r0) + lis r3, CONFIG_SYS_SCCR@h + ori r3, r3, CONFIG_SYS_SCCR@l + stw r3, IM_SCCR@l(r4) + lwz r3, IM_SCCR@l(r4) + stw r3, 8(r0) + + /* the rest of this was disassembled from the */ + /* EPROM code that came with my CMA282 CPU module */ + /*--------------------------------------------------------------*/ + + lis r1, 0x1234 + ori r1, r1, 0x5678 + stw r1, 0x20(r0) + lwz r1, 0x20(r0) + stw r1, 0x24(r0) + lwz r1, 0x24(r0) + lis r3, 0x0e80 + ori r3, r3, 0 + stw r1, 4(r3) + lwz r1, 4(r3) + + /* Done! */ + /*--------------------------------------------------------------*/ + + blr + +#endif /* CONFIG_COGENT */ + +/* + * This code initialises the MPC8260 processor core + * (conforms to PowerPC 603e spec) + * Note: expects original MSR contents to be in r5. + */ + + .globl init_8260_core +init_8260_core: + + /* Initialize machine status; enable machine check interrupt */ + /*--------------------------------------------------------------*/ + + li r3, MSR_KERNEL /* Set ME and RI flags */ + rlwimi r3, r5, 0, 25, 25 /* preserve IP bit set by HRCW */ +#ifdef DEBUG + rlwimi r3, r5, 0, 21, 22 /* debugger might set SE & BE bits */ +#endif + SYNC /* Some chip revs need this... */ + mtmsr r3 + SYNC + mtspr SRR1, r3 /* Make SRR1 match MSR */ + + /* Initialise the SYPCR early, and reset the watchdog (if req) */ + /*--------------------------------------------------------------*/ + + lis r3, (CONFIG_SYS_IMMR+IM_REGBASE)@h +#if !defined(CONFIG_COGENT) + lis r4, CONFIG_SYS_SYPCR@h + ori r4, r4, CONFIG_SYS_SYPCR@l + stw r4, IM_SYPCR@l(r3) +#endif /* !CONFIG_COGENT */ +#if defined(CONFIG_WATCHDOG) + li r4, 21868 /* = 0x556c */ + sth r4, IM_SWSR@l(r3) + li r4, -21959 /* = 0xaa39 */ + sth r4, IM_SWSR@l(r3) +#endif /* CONFIG_WATCHDOG */ + + /* Initialize the Hardware Implementation-dependent Registers */ + /* HID0 also contains cache control */ + /*--------------------------------------------------------------*/ + + lis r3, CONFIG_SYS_HID0_INIT@h + ori r3, r3, CONFIG_SYS_HID0_INIT@l + SYNC + mtspr HID0, r3 + + lis r3, CONFIG_SYS_HID0_FINAL@h + ori r3, r3, CONFIG_SYS_HID0_FINAL@l + SYNC + mtspr HID0, r3 + + lis r3, CONFIG_SYS_HID2@h + ori r3, r3, CONFIG_SYS_HID2@l + mtspr HID2, r3 + + /* clear all BAT's */ + /*--------------------------------------------------------------*/ + + li r0, 0 + mtspr DBAT0U, r0 + mtspr DBAT0L, r0 + mtspr DBAT1U, r0 + mtspr DBAT1L, r0 + mtspr DBAT2U, r0 + mtspr DBAT2L, r0 + mtspr DBAT3U, r0 + mtspr DBAT3L, r0 + mtspr IBAT0U, r0 + mtspr IBAT0L, r0 + mtspr IBAT1U, r0 + mtspr IBAT1L, r0 + mtspr IBAT2U, r0 + mtspr IBAT2L, r0 + mtspr IBAT3U, r0 + mtspr IBAT3L, r0 + SYNC + + /* invalidate all tlb's */ + /* */ + /* From the 603e User Manual: "The 603e provides the ability to */ + /* invalidate a TLB entry. The TLB Invalidate Entry (tlbie) */ + /* instruction invalidates the TLB entry indexed by the EA, and */ + /* operates on both the instruction and data TLBs simultaneously*/ + /* invalidating four TLB entries (both sets in each TLB). The */ + /* index corresponds to bits 15-19 of the EA. To invalidate all */ + /* entries within both TLBs, 32 tlbie instructions should be */ + /* issued, incrementing this field by one each time." */ + /* */ + /* "Note that the tlbia instruction is not implemented on the */ + /* 603e." */ + /* */ + /* bits 15-19 correspond to addresses 0x00000000 to 0x0001F000 */ + /* incrementing by 0x1000 each time. The code below is sort of */ + /* based on code in "flush_tlbs" from arch/powerpc/kernel/head.S */ + /* */ + /*--------------------------------------------------------------*/ + + li r3, 32 + mtctr r3 + li r3, 0 +1: tlbie r3 + addi r3, r3, 0x1000 + bdnz 1b + SYNC + + /* Done! */ + /*--------------------------------------------------------------*/ + + blr + +#ifdef DEBUG + +/* + * initialise things related to debugging. + * + * must be called after the global offset table (GOT) is initialised + * (GET_GOT) and after cpu_init_f() has executed. + */ + + .globl init_debug +init_debug: + + lis r3, (CONFIG_SYS_IMMR+IM_REGBASE)@h + + /* Quick and dirty hack to enable the RAM and copy the */ + /* vectors so that we can take exceptions. */ + /*--------------------------------------------------------------*/ + /* write Memory Refresh Prescaler */ + li r4, CONFIG_SYS_MPTPR + sth r4, IM_MPTPR@l(r3) + /* write 60x Refresh Timer */ + li r4, CONFIG_SYS_PSRT + stb r4, IM_PSRT@l(r3) + /* init the 60x SDRAM Mode Register */ + lis r4, (CONFIG_SYS_PSDMR|PSDMR_OP_NORM)@h + ori r4, r4, (CONFIG_SYS_PSDMR|PSDMR_OP_NORM)@l + stw r4, IM_PSDMR@l(r3) + /* write Precharge All Banks command */ + lis r4, (CONFIG_SYS_PSDMR|PSDMR_OP_PREA)@h + ori r4, r4, (CONFIG_SYS_PSDMR|PSDMR_OP_PREA)@l + stw r4, IM_PSDMR@l(r3) + stb r0, 0(0) + /* write eight CBR Refresh commands */ + lis r4, (CONFIG_SYS_PSDMR|PSDMR_OP_CBRR)@h + ori r4, r4, (CONFIG_SYS_PSDMR|PSDMR_OP_CBRR)@l + stw r4, IM_PSDMR@l(r3) + stb r0, 0(0) + stb r0, 0(0) + stb r0, 0(0) + stb r0, 0(0) + stb r0, 0(0) + stb r0, 0(0) + stb r0, 0(0) + stb r0, 0(0) + /* write Mode Register Write command */ + lis r4, (CONFIG_SYS_PSDMR|PSDMR_OP_MRW)@h + ori r4, r4, (CONFIG_SYS_PSDMR|PSDMR_OP_MRW)@l + stw r4, IM_PSDMR@l(r3) + stb r0, 0(0) + /* write Normal Operation command and enable Refresh */ + lis r4, (CONFIG_SYS_PSDMR|PSDMR_OP_NORM|PSDMR_RFEN)@h + ori r4, r4, (CONFIG_SYS_PSDMR|PSDMR_OP_NORM|PSDMR_RFEN)@l + stw r4, IM_PSDMR@l(r3) + stb r0, 0(0) + /* RAM should now be operational */ + +#define VEC_WRD_CNT ((_end_of_vectors - _start + EXC_OFF_SYS_RESET) / 4) + mflr r3 + GET_GOT + mtlr r3 + lwz r3, GOT(_end_of_vectors) + rlwinm r4, r3, 0, 18, 31 /* _end_of_vectors & 0x3FFF */ + lis r5, VEC_WRD_CNT@h + ori r5, r5, VEC_WRD_CNT@l + mtctr r5 +1: + lwzu r5, -4(r3) + stwu r5, -4(r4) + bdnz 1b + + /* Load the Instruction Address Breakpoint Register (IABR). */ + /* */ + /* The address to load is stored in the first word of dual port */ + /* ram and should be preserved while the power is on, so you */ + /* can plug addresses into that location then reset the cpu and */ + /* this code will load that address into the IABR after the */ + /* reset. */ + /* */ + /* When the program counter matches the contents of the IABR, */ + /* an exception is generated (before the instruction at that */ + /* location completes). The vector for this exception is 0x1300 */ + /*--------------------------------------------------------------*/ + lis r3, CONFIG_SYS_IMMR@h + lwz r3, 0(r3) + mtspr IABR, r3 + + /* Set the entire dual port RAM (where the initial stack */ + /* resides) to a known value - makes it easier to see where */ + /* the stack has been written */ + /*--------------------------------------------------------------*/ + lis r3, (CONFIG_SYS_IMMR + CONFIG_SYS_INIT_SP_OFFSET)@h + ori r3, r3, (CONFIG_SYS_IMMR + CONFIG_SYS_INIT_SP_OFFSET)@l + li r4, ((CONFIG_SYS_INIT_SP_OFFSET - 4) / 4) + mtctr r4 + lis r4, 0xdeadbeaf@h + ori r4, r4, 0xdeadbeaf@l +1: + stwu r4, -4(r3) + bdnz 1b + + /* Done! */ + /*--------------------------------------------------------------*/ + + blr +#endif + +/* Cache functions. + * + * Note: requires that all cache bits in + * HID0 are in the low half word. + */ + .globl icache_enable +icache_enable: + mfspr r3, HID0 + ori r3, r3, HID0_ICE + lis r4, 0 + ori r4, r4, HID0_ILOCK + andc r3, r3, r4 + ori r4, r3, HID0_ICFI + isync + mtspr HID0, r4 /* sets enable and invalidate, clears lock */ + isync + mtspr HID0, r3 /* clears invalidate */ + blr + + .globl icache_disable +icache_disable: + mfspr r3, HID0 + lis r4, 0 + ori r4, r4, HID0_ICE|HID0_ILOCK + andc r3, r3, r4 + ori r4, r3, HID0_ICFI + isync + mtspr HID0, r4 /* sets invalidate, clears enable and lock */ + isync + mtspr HID0, r3 /* clears invalidate */ + blr + + .globl icache_status +icache_status: + mfspr r3, HID0 + rlwinm r3, r3, HID0_ICE_BITPOS + 1, 31, 31 + blr + + .globl dcache_enable +dcache_enable: + mfspr r3, HID0 + ori r3, r3, HID0_DCE + lis r4, 0 + ori r4, r4, HID0_DLOCK + andc r3, r3, r4 + ori r4, r3, HID0_DCI + sync + mtspr HID0, r4 /* sets enable and invalidate, clears lock */ + sync + mtspr HID0, r3 /* clears invalidate */ + blr + + .globl dcache_disable +dcache_disable: + mfspr r3, HID0 + lis r4, 0 + ori r4, r4, HID0_DCE|HID0_DLOCK + andc r3, r3, r4 + ori r4, r3, HID0_DCI + sync + mtspr HID0, r4 /* sets invalidate, clears enable and lock */ + sync + mtspr HID0, r3 /* clears invalidate */ + blr + + .globl dcache_status +dcache_status: + mfspr r3, HID0 + rlwinm r3, r3, HID0_DCE_BITPOS + 1, 31, 31 + blr + + .globl get_pvr +get_pvr: + mfspr r3, PVR + blr + +/*------------------------------------------------------------------------------*/ + +/* + * void relocate_code (addr_sp, gd, addr_moni) + * + * This "function" does not return, instead it continues in RAM + * after relocating the monitor code. + * + * r3 = dest + * r4 = src + * r5 = length in bytes + * r6 = cachelinesize + */ + .globl relocate_code +relocate_code: + mr r1, r3 /* Set new stack pointer */ + mr r9, r4 /* Save copy of Global Data pointer */ + mr r10, r5 /* Save copy of Destination Address */ + + GET_GOT + mr r3, r5 /* Destination Address */ + lis r4, CONFIG_SYS_MONITOR_BASE@h /* Source Address */ + ori r4, r4, CONFIG_SYS_MONITOR_BASE@l + lwz r5, GOT(__init_end) + sub r5, r5, r4 + li r6, CONFIG_SYS_CACHELINE_SIZE /* Cache Line Size */ + + /* + * Fix GOT pointer: + * + * New GOT-PTR = (old GOT-PTR - CONFIG_SYS_MONITOR_BASE) + Destination Address + * + * Offset: + */ + sub r15, r10, r4 + + /* First our own GOT */ + add r12, r12, r15 + /* then the one used by the C code */ + add r30, r30, r15 + + /* + * Now relocate code + */ + + cmplw cr1,r3,r4 + addi r0,r5,3 + srwi. r0,r0,2 + beq cr1,4f /* In place copy is not necessary */ + beq 7f /* Protect against 0 count */ + mtctr r0 + bge cr1,2f + + la r8,-4(r4) + la r7,-4(r3) +1: lwzu r0,4(r8) + stwu r0,4(r7) + bdnz 1b + b 4f + +2: slwi r0,r0,2 + add r8,r4,r0 + add r7,r3,r0 +3: lwzu r0,-4(r8) + stwu r0,-4(r7) + bdnz 3b + +/* + * Now flush the cache: note that we must start from a cache aligned + * address. Otherwise we might miss one cache line. + */ +4: cmpwi r6,0 + add r5,r3,r5 + beq 7f /* Always flush prefetch queue in any case */ + subi r0,r6,1 + andc r3,r3,r0 + mfspr r7,HID0 /* don't do dcbst if dcache is disabled */ + rlwinm r7,r7,HID0_DCE_BITPOS+1,31,31 + cmpwi r7,0 + beq 9f + mr r4,r3 +5: dcbst 0,r4 + add r4,r4,r6 + cmplw r4,r5 + blt 5b + sync /* Wait for all dcbst to complete on bus */ +9: mfspr r7,HID0 /* don't do icbi if icache is disabled */ + rlwinm r7,r7,HID0_ICE_BITPOS+1,31,31 + cmpwi r7,0 + beq 7f + mr r4,r3 +6: icbi 0,r4 + add r4,r4,r6 + cmplw r4,r5 + blt 6b +7: sync /* Wait for all icbi to complete on bus */ + isync + +/* + * We are done. Do not return, instead branch to second part of board + * initialization, now running from RAM. + */ + + addi r0, r10, in_ram - _start + EXC_OFF_SYS_RESET + mtlr r0 + blr + +in_ram: + + /* + * Relocation Function, r12 point to got2+0x8000 + * + * Adjust got2 pointers, no need to check for 0, this code + * already puts a few entries in the table. + */ + li r0,__got2_entries@sectoff@l + la r3,GOT(_GOT2_TABLE_) + lwz r11,GOT(_GOT2_TABLE_) + mtctr r0 + sub r11,r3,r11 + addi r3,r3,-4 +1: lwzu r0,4(r3) + cmpwi r0,0 + beq- 2f + add r0,r0,r11 + stw r0,0(r3) +2: bdnz 1b + + /* + * Now adjust the fixups and the pointers to the fixups + * in case we need to move ourselves again. + */ + li r0,__fixup_entries@sectoff@l + lwz r3,GOT(_FIXUP_TABLE_) + cmpwi r0,0 + mtctr r0 + addi r3,r3,-4 + beq 4f +3: lwzu r4,4(r3) + lwzux r0,r4,r11 + add r0,r0,r11 + stw r10,0(r3) + stw r0,0(r4) + bdnz 3b +4: +clear_bss: + /* + * Now clear BSS segment + */ + lwz r3,GOT(__bss_start) +#if defined(CONFIG_HYMOD) + /* + * For HYMOD - the environment is the very last item in flash. + * The real .bss stops just before environment starts, so only + * clear up to that point. + * + * taken from mods for FADS board + */ + lwz r4,GOT(environment) +#else + lwz r4,GOT(_end) +#endif + + cmplw 0, r3, r4 + beq 6f + + li r0, 0 +5: + stw r0, 0(r3) + addi r3, r3, 4 + cmplw 0, r3, r4 + bne 5b +6: + + mr r3, r9 /* Global Data pointer */ + mr r4, r10 /* Destination Address */ + bl board_init_r + + /* + * Copy exception vector code to low memory + * + * r3: dest_addr + * r7: source address, r8: end address, r9: target address + */ + .globl trap_init +trap_init: + mflr r4 /* save link register */ + GET_GOT + lwz r7, GOT(_start) + lwz r8, GOT(_end_of_vectors) + + li r9, 0x100 /* reset vector always at 0x100 */ + + cmplw 0, r7, r8 + bgelr /* return if r7>=r8 - just in case */ +1: + lwz r0, 0(r7) + stw r0, 0(r9) + addi r7, r7, 4 + addi r9, r9, 4 + cmplw 0, r7, r8 + bne 1b + + /* + * relocate `hdlr' and `int_return' entries + */ + li r7, .L_MachineCheck - _start + EXC_OFF_SYS_RESET + li r8, Alignment - _start + EXC_OFF_SYS_RESET +2: + bl trap_reloc + addi r7, r7, 0x100 /* next exception vector */ + cmplw 0, r7, r8 + blt 2b + + li r7, .L_Alignment - _start + EXC_OFF_SYS_RESET + bl trap_reloc + + li r7, .L_ProgramCheck - _start + EXC_OFF_SYS_RESET + bl trap_reloc + + li r7, .L_FPUnavailable - _start + EXC_OFF_SYS_RESET + li r8, SystemCall - _start + EXC_OFF_SYS_RESET +3: + bl trap_reloc + addi r7, r7, 0x100 /* next exception vector */ + cmplw 0, r7, r8 + blt 3b + + li r7, .L_SingleStep - _start + EXC_OFF_SYS_RESET + li r8, _end_of_vectors - _start + EXC_OFF_SYS_RESET +4: + bl trap_reloc + addi r7, r7, 0x100 /* next exception vector */ + cmplw 0, r7, r8 + blt 4b + + mfmsr r3 /* now that the vectors have */ + lis r7, MSR_IP@h /* relocated into low memory */ + ori r7, r7, MSR_IP@l /* MSR[IP] can be turned off */ + andc r3, r3, r7 /* (if it was on) */ + SYNC /* Some chip revs need this... */ + mtmsr r3 + SYNC + + mtlr r4 /* restore link register */ + blr diff --git a/arch/powerpc/cpu/mpc8260/traps.c b/arch/powerpc/cpu/mpc8260/traps.c new file mode 100644 index 0000000..c116cdf --- /dev/null +++ b/arch/powerpc/cpu/mpc8260/traps.c @@ -0,0 +1,273 @@ +/* + * linux/arch/powerpc/kernel/traps.c + * + * Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org) + * + * Modified by Cort Dougan (cort@cs.nmt.edu) + * and Paul Mackerras (paulus@cs.anu.edu.au) + * + * (C) Copyright 2000 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +/* + * This file handles the architecture-dependent parts of hardware exceptions + */ + +#include <common.h> +#include <command.h> +#include <kgdb.h> +#include <asm/processor.h> +#include <asm/m8260_pci.h> + +/* Returns 0 if exception not found and fixup otherwise. */ +extern unsigned long search_exception_table(unsigned long); + +/* THIS NEEDS CHANGING to use the board info structure. +*/ +#define END_OF_MEM 0x02000000 + +/* + * Trap & Exception support + */ + +void +print_backtrace(unsigned long *sp) +{ + int cnt = 0; + unsigned long i; + + puts ("Call backtrace: "); + while (sp) { + if ((uint)sp > END_OF_MEM) + break; + + i = sp[1]; + if (cnt++ % 7 == 0) + putc ('\n'); + printf("%08lX ", i); + if (cnt > 32) break; + sp = (unsigned long *)*sp; + } + putc ('\n'); +} + +void show_regs(struct pt_regs * regs) +{ + int i; + + printf("NIP: %08lX XER: %08lX LR: %08lX REGS: %p TRAP: %04lx DAR: %08lX\n", + regs->nip, regs->xer, regs->link, regs, regs->trap, regs->dar); + printf("MSR: %08lx EE: %01x PR: %01x FP: %01x ME: %01x IR/DR: %01x%01x\n", + regs->msr, regs->msr&MSR_EE ? 1 : 0, regs->msr&MSR_PR ? 1 : 0, + regs->msr & MSR_FP ? 1 : 0,regs->msr&MSR_ME ? 1 : 0, + regs->msr&MSR_IR ? 1 : 0, + regs->msr&MSR_DR ? 1 : 0); + + putc ('\n'); + for (i = 0; i < 32; i++) { + if ((i % 8) == 0) { + printf("GPR%02d: ", i); + } + + printf("%08lX ", regs->gpr[i]); + if ((i % 8) == 7) { + putc ('\n'); + } + } +} + + +void +_exception(int signr, struct pt_regs *regs) +{ + show_regs(regs); + print_backtrace((unsigned long *)regs->gpr[1]); + panic("Exception in kernel pc %lx signal %d",regs->nip,signr); +} + +#ifdef CONFIG_PCI +void dump_pci (void) +{ + + volatile immap_t *immap = (immap_t *) CONFIG_SYS_IMMR; + + printf ("PCI: err status %x err mask %x err ctrl %x\n", + le32_to_cpu (immap->im_pci.pci_esr), + le32_to_cpu (immap->im_pci.pci_emr), + le32_to_cpu (immap->im_pci.pci_ecr)); + printf (" error address %x error data %x ctrl %x\n", + le32_to_cpu (immap->im_pci.pci_eacr), + le32_to_cpu (immap->im_pci.pci_edcr), + le32_to_cpu (immap->im_pci.pci_eccr)); + +} +#endif + +void +MachineCheckException(struct pt_regs *regs) +{ + unsigned long fixup; + + /* Probing PCI using config cycles cause this exception + * when a device is not present. Catch it and return to + * the PCI exception handler. + */ +#ifdef CONFIG_PCI + volatile immap_t *immap = (immap_t *)CONFIG_SYS_IMMR; +#ifdef DEBUG + dump_pci(); +#endif + /* clear the error in the error status register */ + if(immap->im_pci.pci_esr & cpu_to_le32(PCI_ERROR_PCI_NO_RSP)) { + immap->im_pci.pci_esr = cpu_to_le32(PCI_ERROR_PCI_NO_RSP); + return; + } +#endif + if ((fixup = search_exception_table(regs->nip)) != 0) { + regs->nip = fixup; + return; + } + +#if defined(CONFIG_CMD_KGDB) + if (debugger_exception_handler && (*debugger_exception_handler)(regs)) + return; +#endif + + puts ("Machine check in kernel mode.\n" + "Caused by (from msr): "); + printf("regs %p ",regs); + switch( regs->msr & 0x000F0000) { + case (0x80000000>>12): + puts ("Machine check signal - probably due to mm fault\n" + "with mmu off\n"); + break; + case (0x80000000>>13): + puts ("Transfer error ack signal\n"); + break; + case (0x80000000>>14): + puts ("Data parity signal\n"); + break; + case (0x80000000>>15): + puts ("Address parity signal\n"); + break; + default: + puts ("Unknown values in msr\n"); + } + show_regs(regs); + print_backtrace((unsigned long *)regs->gpr[1]); +#ifdef CONFIG_PCI + dump_pci(); +#endif + panic("machine check"); +} + +void +AlignmentException(struct pt_regs *regs) +{ +#if defined(CONFIG_CMD_KGDB) + if (debugger_exception_handler && (*debugger_exception_handler)(regs)) + return; +#endif + show_regs(regs); + print_backtrace((unsigned long *)regs->gpr[1]); + panic("Alignment Exception"); +} + +void +ProgramCheckException(struct pt_regs *regs) +{ +#if defined(CONFIG_CMD_KGDB) + if (debugger_exception_handler && (*debugger_exception_handler)(regs)) + return; +#endif + show_regs(regs); + print_backtrace((unsigned long *)regs->gpr[1]); + panic("Program Check Exception"); +} + +void +SoftEmuException(struct pt_regs *regs) +{ +#if defined(CONFIG_CMD_KGDB) + if (debugger_exception_handler && (*debugger_exception_handler)(regs)) + return; +#endif + show_regs(regs); + print_backtrace((unsigned long *)regs->gpr[1]); + panic("Software Emulation Exception"); +} + + +void +UnknownException(struct pt_regs *regs) +{ +#if defined(CONFIG_CMD_KGDB) + if (debugger_exception_handler && (*debugger_exception_handler)(regs)) + return; +#endif + printf("Bad trap at PC: %lx, SR: %lx, vector=%lx\n", + regs->nip, regs->msr, regs->trap); + _exception(0, regs); +} + +#if defined(CONFIG_CMD_BEDBUG) +extern void do_bedbug_breakpoint(struct pt_regs *); +#endif + +void +DebugException(struct pt_regs *regs) +{ + + printf("Debugger trap at @ %lx\n", regs->nip ); + show_regs(regs); +#if defined(CONFIG_CMD_BEDBUG) + do_bedbug_breakpoint( regs ); +#endif +} + +/* Probe an address by reading. If not present, return -1, otherwise + * return 0. + */ +int +addr_probe(uint *addr) +{ +#if 0 + int retval; + + __asm__ __volatile__( \ + "1: lwz %0,0(%1)\n" \ + " eieio\n" \ + " li %0,0\n" \ + "2:\n" \ + ".section .fixup,\"ax\"\n" \ + "3: li %0,-1\n" \ + " b 2b\n" \ + ".section __ex_table,\"a\"\n" \ + " .align 2\n" \ + " .long 1b,3b\n" \ + ".text" \ + : "=r" (retval) : "r"(addr)); + + return (retval); +#endif + return 0; +} diff --git a/arch/powerpc/cpu/mpc8260/u-boot.lds b/arch/powerpc/cpu/mpc8260/u-boot.lds new file mode 100644 index 0000000..b8681e7 --- /dev/null +++ b/arch/powerpc/cpu/mpc8260/u-boot.lds @@ -0,0 +1,122 @@ +/* + * (C) Copyright 2001-2007 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +OUTPUT_ARCH(powerpc) +/* Do we need any of these for elf? + __DYNAMIC = 0; */ +SECTIONS +{ + /* Read-only sections, merged into text segment: */ + . = + SIZEOF_HEADERS; + .interp : { *(.interp) } + .hash : { *(.hash) } + .dynsym : { *(.dynsym) } + .dynstr : { *(.dynstr) } + .rel.text : { *(.rel.text) } + .rela.text : { *(.rela.text) } + .rel.data : { *(.rel.data) } + .rela.data : { *(.rela.data) } + .rel.rodata : { *(.rel.rodata) } + .rela.rodata : { *(.rela.rodata) } + .rel.got : { *(.rel.got) } + .rela.got : { *(.rela.got) } + .rel.ctors : { *(.rel.ctors) } + .rela.ctors : { *(.rela.ctors) } + .rel.dtors : { *(.rel.dtors) } + .rela.dtors : { *(.rela.dtors) } + .rel.bss : { *(.rel.bss) } + .rela.bss : { *(.rela.bss) } + .rel.plt : { *(.rel.plt) } + .rela.plt : { *(.rela.plt) } + .init : { *(.init) } + .plt : { *(.plt) } + .text : + { + arch/powerpc/cpu/mpc8260/start.o (.text) + *(.text) + *(.got1) + . = ALIGN(16); + *(.eh_frame) + *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.rodata*))) + } + .fini : { *(.fini) } =0 + .ctors : { *(.ctors) } + .dtors : { *(.dtors) } + + /* Read-write section, merged into data segment: */ + . = (. + 0x0FFF) & 0xFFFFF000; + _erotext = .; + PROVIDE (erotext = .); + .reloc : + { + *(.got) + _GOT2_TABLE_ = .; + *(.got2) + _FIXUP_TABLE_ = .; + *(.fixup) + } + __got2_entries = (_FIXUP_TABLE_ - _GOT2_TABLE_) >> 2; + __fixup_entries = (. - _FIXUP_TABLE_) >> 2; + + .data : + { + *(.data) + *(.data1) + *(.sdata) + *(.sdata2) + *(.dynamic) + CONSTRUCTORS + } + _edata = .; + PROVIDE (edata = .); + + . = .; + __u_boot_cmd_start = .; + .u_boot_cmd : { *(.u_boot_cmd) } + __u_boot_cmd_end = .; + + + . = .; + __start___ex_table = .; + __ex_table : { *(__ex_table) } + __stop___ex_table = .; + + . = ALIGN(4096); + __init_begin = .; + .text.init : { *(.text.init) } + .data.init : { *(.data.init) } + . = ALIGN(4096); + __init_end = .; + + __bss_start = .; + .bss (NOLOAD) : + { + *(.sbss) *(.scommon) + *(.dynbss) + *(.bss) + *(COMMON) + . = ALIGN(4); + } + _end = . ; + PROVIDE (end = .); +} |