diff options
Diffstat (limited to 'arch/powerpc/cpu/mpc8xx')
23 files changed, 9188 insertions, 0 deletions
| diff --git a/arch/powerpc/cpu/mpc8xx/Makefile b/arch/powerpc/cpu/mpc8xx/Makefile new file mode 100644 index 000000000..5f7045969 --- /dev/null +++ b/arch/powerpc/cpu/mpc8xx/Makefile @@ -0,0 +1,66 @@ +# +# (C) Copyright 2000-2006 +# Wolfgang Denk, DENX Software Engineering, wd@denx.de. +# +# See file CREDITS for list of people who contributed to this +# project. +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation; either version 2 of +# the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, +# MA 02111-1307 USA +# + +include $(TOPDIR)/config.mk + +# CFLAGS += -DET_DEBUG + +LIB	= $(obj)lib$(CPU).a + +START-y	+= start.o +START-y	+= kgdb.o +COBJS-y	+= bedbug_860.o +COBJS-y	+= commproc.o +COBJS-y	+= cpu.o +COBJS-y	+= cpu_init.o +COBJS-y	+= fec.o +COBJS-$(CONFIG_OF_LIBFDT) += fdt.o +COBJS-y	+= i2c.o +COBJS-y	+= interrupts.o +COBJS-y	+= lcd.o +COBJS-y	+= scc.o +COBJS-y	+= serial.o +COBJS-y	+= speed.o +COBJS-y	+= spi.o +COBJS-y	+= traps.o +COBJS-y	+= upatch.o +COBJS-y	+= video.o +SOBJS-y	+= plprcr_write.o + +SRCS	:= $(START-y:.o=.S) $(SOBJS-y:.o=.S) $(COBJS-y:.o=.c) +OBJS	:= $(addprefix $(obj),$(SOBJS-y) $(COBJS-y)) +START	:= $(addprefix $(obj),$(START-y)) + +all:	$(obj).depend $(START) $(LIB) + +$(LIB):	$(OBJS) +	$(AR) $(ARFLAGS) $@ $(OBJS) $(obj)kgdb.o + +######################################################################### + +# defines $(obj).depend target +include $(SRCTREE)/rules.mk + +sinclude $(obj).depend + +######################################################################### diff --git a/arch/powerpc/cpu/mpc8xx/bedbug_860.c b/arch/powerpc/cpu/mpc8xx/bedbug_860.c new file mode 100644 index 000000000..0308bbbf7 --- /dev/null +++ b/arch/powerpc/cpu/mpc8xx/bedbug_860.c @@ -0,0 +1,316 @@ +/* + * Bedbug Functions specific to the MPC860 chip + */ + +#include <common.h> +#include <command.h> +#include <linux/ctype.h> +#include <bedbug/bedbug.h> +#include <bedbug/regs.h> +#include <bedbug/ppc.h> +#include <bedbug/type.h> + +#if defined(CONFIG_CMD_BEDBUG) && defined(CONFIG_8xx) + +#define MAX_BREAK_POINTS 2 + +extern CPU_DEBUG_CTX bug_ctx; + +void bedbug860_init __P((void)); +void bedbug860_do_break __P((cmd_tbl_t*,int,int,char*[])); +void bedbug860_break_isr __P((struct pt_regs*)); +int  bedbug860_find_empty __P((void)); +int  bedbug860_set __P((int,unsigned long)); +int  bedbug860_clear __P((int)); + + +/* ====================================================================== + * Initialize the global bug_ctx structure for the MPC860.  Clear all + * of the breakpoints. + * ====================================================================== */ + +void bedbug860_init( void ) +{ +  int	i; +  /* -------------------------------------------------- */ + +  bug_ctx.hw_debug_enabled = 0; +  bug_ctx.stopped = 0; +  bug_ctx.current_bp = 0; +  bug_ctx.regs = NULL; + +  bug_ctx.do_break   = bedbug860_do_break; +  bug_ctx.break_isr  = bedbug860_break_isr; +  bug_ctx.find_empty = bedbug860_find_empty; +  bug_ctx.set        = bedbug860_set; +  bug_ctx.clear      = bedbug860_clear; + +  for( i = 1; i <= MAX_BREAK_POINTS; ++i ) +    (*bug_ctx.clear)( i ); + +  puts ("BEDBUG:ready\n"); +  return; +} /* bedbug_init_breakpoints */ + + + +/* ====================================================================== + * Set/clear/show one of the hardware breakpoints for the 860.  The "off" + * string will disable a specific breakpoint.  The "show" string will + * display the current breakpoints.  Otherwise an address will set a + * breakpoint at that address.  Setting a breakpoint uses the CPU-specific + * set routine which will assign a breakpoint number. + * ====================================================================== */ + +void bedbug860_do_break (cmd_tbl_t *cmdtp, int flag, int argc, +			 char *argv[]) +{ +  long		addr = 0;       /* Address to break at  */ +  int		which_bp;       /* Breakpoint number    */ +  /* -------------------------------------------------- */ + +  if (argc < 2) +  { +    cmd_usage(cmdtp); +    return; +  } + +  /* Turn off a breakpoint */ + +  if( strcmp( argv[ 1 ], "off" ) == 0 ) +  { +    if( bug_ctx.hw_debug_enabled == 0 ) +    { +      printf( "No breakpoints enabled\n" ); +      return; +    } + +    which_bp = simple_strtoul( argv[ 2 ], NULL, 10 ); + +    if( bug_ctx.clear ) +      (*bug_ctx.clear)( which_bp ); + +    printf( "Breakpoint %d removed\n", which_bp ); +    return; +  } + +  /* Show a list of breakpoints */ + +  if( strcmp( argv[ 1 ], "show" ) == 0 ) +  { +    for( which_bp = 1; which_bp <= MAX_BREAK_POINTS; ++which_bp ) +    { + +      switch( which_bp ) +      { +      case 1: addr = GET_CMPA(); break; +      case 2: addr = GET_CMPB(); break; +      case 3: addr = GET_CMPC(); break; +      case 4: addr = GET_CMPD(); break; +      } + +      printf( "Breakpoint [%d]: ", which_bp ); +      if( addr == 0 ) +	printf( "NOT SET\n" ); +      else +	disppc( (unsigned char *)addr, 0, 1, bedbug_puts, F_RADHEX ); +    } +    return; +  } + +  /* Set a breakpoint at the address */ + +  if( !isdigit( argv[ 1 ][ 0 ])) +  { +    cmd_usage(cmdtp); +    return; +  } + +  addr = simple_strtoul( argv[ 1 ], NULL, 16 ) & 0xfffffffc; + +  if(( bug_ctx.set ) && ( which_bp = (*bug_ctx.set)( 0, addr )) > 0 ) +  { +    printf( "Breakpoint [%d]: ", which_bp ); +    disppc( (unsigned char *)addr, 0, 1, bedbug_puts, F_RADHEX ); +  } + +  return; +} /* bedbug860_do_break */ + + + +/* ====================================================================== + * Handle a breakpoint.  First determine which breakpoint was hit by + * looking at the DeBug Status Register (DBSR), clear the breakpoint + * and enter a mini main loop.  Stay in the loop until the stopped flag + * in the debug context is cleared. + * ====================================================================== */ + +void bedbug860_break_isr( struct pt_regs *regs ) +{ +  unsigned long	addr;     /* Address stopped at   */ +  unsigned long	cause;     /* Address stopped at   */ +  /* -------------------------------------------------- */ + +  cause = GET_ICR(); + +  if( !(cause & 0x00000004)) { +    printf( "Not an instruction breakpoint (ICR 0x%08lx)\n", cause ); +    return; +  } + +  addr = regs->nip; + +  if( addr == GET_CMPA() ) +  { +    bug_ctx.current_bp = 1; +  } +  else if( addr == GET_CMPB() ) +  { +    bug_ctx.current_bp = 2; +  } +  else if( addr == GET_CMPC() ) +  { +    bug_ctx.current_bp = 3; +  } +  else if( addr == GET_CMPD() ) +  { +    bug_ctx.current_bp = 4; +  } + +  bedbug_main_loop( addr, regs ); +  return; +} /* bedbug860_break_isr */ + + + +/* ====================================================================== + * Look through all of the hardware breakpoints available to see if one + * is unused. + * ====================================================================== */ + +int bedbug860_find_empty( void ) +{ +  /* -------------------------------------------------- */ + +  if( GET_CMPA() == 0 ) +    return 1; + +  if( GET_CMPB() == 0 ) +    return 2; + +  if( GET_CMPC() == 0 ) +    return 3; + +  if( GET_CMPD() == 0 ) +    return 4; + +  return 0; +} /* bedbug860_find_empty */ + + + +/* ====================================================================== + * Set a breakpoint.  If 'which_bp' is zero then find an unused breakpoint + * number, otherwise reassign the given breakpoint.  If hardware debugging + * is not enabled, then turn it on via the MSR and DBCR0.  Set the break + * address in the appropriate IACx register and enable proper address + * beakpoint in DBCR0. + * ====================================================================== */ + +int bedbug860_set( int which_bp, unsigned long addr ) +{ +  /* -------------------------------------------------- */ + +  /* Only look if which_bp == 0, else use which_bp */ +  if(( bug_ctx.find_empty ) && ( !which_bp ) && +     ( which_bp = (*bug_ctx.find_empty)()) == 0 ) +  { +    printf( "All breakpoints in use\n" ); +    return 0; +  } + +  if( which_bp < 1 || which_bp > MAX_BREAK_POINTS ) +  { +    printf( "Invalid break point # %d\n", which_bp ); +    return 0; +  } + +  if( ! bug_ctx.hw_debug_enabled ) +  { +    bug_ctx.hw_debug_enabled = 1; +    SET_DER( GET_DER() | 0x00000004 ); +  } + +  switch( which_bp ) +  { +  case 1: +    SET_CMPA( addr ); +    SET_ICTRL( GET_ICTRL() | 0x80080800 ); /* CTA=Equal,IW0=Match A,SIW0EN */ +    break; + +  case 2: +    SET_CMPB( addr ); +    SET_ICTRL( GET_ICTRL() | 0x10020400 ); /* CTB=Equal,IW1=Match B,SIW1EN */ +    break; + +  case 3: +    SET_CMPC( addr ); +    SET_ICTRL( GET_ICTRL() | 0x02008200 ); /* CTC=Equal,IW2=Match C,SIW2EN */ +    break; + +  case 4: +    SET_CMPD( addr ); +    SET_ICTRL( GET_ICTRL() | 0x00404100 ); /* CTD=Equal,IW3=Match D,SIW3EN */ +    break; +  } + +  return which_bp; +} /* bedbug860_set */ + + + +/* ====================================================================== + * Disable a specific breakoint by setting the appropriate IACx register + * to zero and claring the instruction address breakpoint in DBCR0. + * ====================================================================== */ + +int bedbug860_clear( int which_bp ) +{ +  /* -------------------------------------------------- */ + +  if( which_bp < 1 || which_bp > MAX_BREAK_POINTS ) +  { +    printf( "Invalid break point # (%d)\n", which_bp ); +    return -1; +  } + +  switch( which_bp ) +  { +  case 1: +    SET_CMPA( 0 ); +    SET_ICTRL( GET_ICTRL() & ~0x80080800 ); /* CTA=Equal,IW0=Match A,SIW0EN */ +    break; + +  case 2: +    SET_CMPB( 0 ); +    SET_ICTRL( GET_ICTRL() & ~0x10020400 ); /* CTB=Equal,IW1=Match B,SIW1EN */ +    break; + +  case 3: +    SET_CMPC( 0 ); +    SET_ICTRL( GET_ICTRL() & ~0x02008200 ); /* CTC=Equal,IW2=Match C,SIW2EN */ +    break; + +  case 4: +    SET_CMPD( 0 ); +    SET_ICTRL( GET_ICTRL() & ~0x00404100 ); /* CTD=Equal,IW3=Match D,SIW3EN */ +    break; +  } + +  return 0; +} /* bedbug860_clear */ + + +/* ====================================================================== */ +#endif diff --git a/arch/powerpc/cpu/mpc8xx/commproc.c b/arch/powerpc/cpu/mpc8xx/commproc.c new file mode 100644 index 000000000..a87a0dce8 --- /dev/null +++ b/arch/powerpc/cpu/mpc8xx/commproc.c @@ -0,0 +1,131 @@ +/* + * (C) Copyright 2000-2002 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include <common.h> +#include <commproc.h> + +DECLARE_GLOBAL_DATA_PTR; + +#ifdef CONFIG_SYS_ALLOC_DPRAM + +int dpram_init (void) +{ +	/* Reclaim the DP memory for our use. */ +	gd->dp_alloc_base = CPM_DATAONLY_BASE; +	gd->dp_alloc_top  = CPM_DATAONLY_BASE + CPM_DATAONLY_SIZE; + +	return (0); +} + +/* Allocate some memory from the dual ported ram.  We may want to + * enforce alignment restrictions, but right now everyone is a good + * citizen. + */ +uint dpram_alloc (uint size) +{ +	uint addr = gd->dp_alloc_base; + +	if ((gd->dp_alloc_base + size) >= gd->dp_alloc_top) +		return (CPM_DP_NOSPACE); + +	gd->dp_alloc_base += size; + +	return addr; +} + +uint dpram_base (void) +{ +	return gd->dp_alloc_base; +} + +/* Allocate some memory from the dual ported ram.  We may want to + * enforce alignment restrictions, but right now everyone is a good + * citizen. + */ +uint dpram_alloc_align (uint size, uint align) +{ +	uint addr, mask = align - 1; + +	addr = (gd->dp_alloc_base + mask) & ~mask; + +	if ((addr + size) >= gd->dp_alloc_top) +		return (CPM_DP_NOSPACE); + +	gd->dp_alloc_base = addr + size; + +	return addr; +} + +uint dpram_base_align (uint align) +{ +	uint mask = align - 1; + +	return (gd->dp_alloc_base + mask) & ~mask; +} +#endif	/* CONFIG_SYS_ALLOC_DPRAM */ + +#if defined(CONFIG_POST) || defined(CONFIG_LOGBUFFER) + +void post_word_store (ulong a) +{ +	volatile void *save_addr = +		((immap_t *) CONFIG_SYS_IMMR)->im_cpm.cp_dpmem + CPM_POST_WORD_ADDR; + +	*(volatile ulong *) save_addr = a; +} + +ulong post_word_load (void) +{ +	volatile void *save_addr = +		((immap_t *) CONFIG_SYS_IMMR)->im_cpm.cp_dpmem + CPM_POST_WORD_ADDR; + +	return *(volatile ulong *) save_addr; +} + +#endif	/* CONFIG_POST || CONFIG_LOGBUFFER*/ + +#ifdef CONFIG_BOOTCOUNT_LIMIT + +void bootcount_store (ulong a) +{ +	volatile ulong *save_addr = +		(volatile ulong *)( ((immap_t *) CONFIG_SYS_IMMR)->im_cpm.cp_dpmem + +		                    CPM_BOOTCOUNT_ADDR ); + +	save_addr[0] = a; +	save_addr[1] = BOOTCOUNT_MAGIC; +} + +ulong bootcount_load (void) +{ +	volatile ulong *save_addr = +		(volatile ulong *)( ((immap_t *) CONFIG_SYS_IMMR)->im_cpm.cp_dpmem + +		                    CPM_BOOTCOUNT_ADDR ); + +	if (save_addr[1] != BOOTCOUNT_MAGIC) +		return 0; +	else +		return save_addr[0]; +} + +#endif /* CONFIG_BOOTCOUNT_LIMIT */ diff --git a/arch/powerpc/cpu/mpc8xx/config.mk b/arch/powerpc/cpu/mpc8xx/config.mk new file mode 100644 index 000000000..5540d6535 --- /dev/null +++ b/arch/powerpc/cpu/mpc8xx/config.mk @@ -0,0 +1,26 @@ +# +# (C) Copyright 2000 +# Wolfgang Denk, DENX Software Engineering, wd@denx.de. +# +# See file CREDITS for list of people who contributed to this +# project. +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation; either version 2 of +# the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, +# MA 02111-1307 USA +# + +PLATFORM_RELFLAGS += -fPIC -meabi + +PLATFORM_CPPFLAGS += -DCONFIG_8xx -ffixed-r2 -mstring -mcpu=860 -msoft-float diff --git a/arch/powerpc/cpu/mpc8xx/cpu.c b/arch/powerpc/cpu/mpc8xx/cpu.c new file mode 100644 index 000000000..2eb848bd0 --- /dev/null +++ b/arch/powerpc/cpu/mpc8xx/cpu.c @@ -0,0 +1,654 @@ +/* + * (C) Copyright 2000-2002 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +/* + * m8xx.c + * + * CPU specific code + * + * written or collected and sometimes rewritten by + * Magnus Damm <damm@bitsmart.com> + * + * minor modifications by + * Wolfgang Denk <wd@denx.de> + */ + +#include <common.h> +#include <watchdog.h> +#include <command.h> +#include <mpc8xx.h> +#include <commproc.h> +#include <netdev.h> +#include <asm/cache.h> + +#if defined(CONFIG_OF_LIBFDT) +#include <libfdt.h> +#include <libfdt_env.h> +#include <fdt_support.h> +#endif + +DECLARE_GLOBAL_DATA_PTR; + +static char *cpu_warning = "\n         " \ +	"*** Warning: CPU Core has Silicon Bugs -- Check the Errata ***"; + +#if ((defined(CONFIG_MPC86x) || defined(CONFIG_MPC855)) && \ +     !defined(CONFIG_MPC862)) + +static int check_CPU (long clock, uint pvr, uint immr) +{ +	char *id_str = +# if defined(CONFIG_MPC855) +	"PC855"; +# elif defined(CONFIG_MPC860P) +	"PC860P"; +# else +	NULL; +# endif +	volatile immap_t *immap = (immap_t *) (immr & 0xFFFF0000); +	uint k, m; +	char buf[32]; +	char pre = 'X'; +	char *mid = "xx"; +	char *suf; + +	/* the highest 16 bits should be 0x0050 for a 860 */ + +	if ((pvr >> 16) != 0x0050) +		return -1; + +	k = (immr << 16) | *((ushort *) & immap->im_cpm.cp_dparam[0xB0]); +	m = 0; +	suf = ""; + +	/* +	 * Some boards use sockets so different CPUs can be used. +	 * We have to check chip version in run time. +	 */ +	switch (k) { +	case 0x00020001: pre = 'P'; break; +	case 0x00030001: break; +	case 0x00120003: suf = "A"; break; +	case 0x00130003: suf = "A3"; break; + +	case 0x00200004: suf = "B"; break; + +	case 0x00300004: suf = "C"; break; +	case 0x00310004: suf = "C1"; m = 1; break; + +	case 0x00200064: mid = "SR"; suf = "B"; break; +	case 0x00300065: mid = "SR"; suf = "C"; break; +	case 0x00310065: mid = "SR"; suf = "C1"; m = 1; break; +	case 0x05010000: suf = "D3"; m = 1; break; +	case 0x05020000: suf = "D4"; m = 1; break; +		/* this value is not documented anywhere */ +	case 0x40000000: pre = 'P'; suf = "D"; m = 1; break; +		/* MPC866P/MPC866T/MPC859T/MPC859DSL/MPC852T */ +	case 0x08010004:		/* Rev. A.0 */ +		suf = "A"; +		/* fall through */ +	case 0x08000003:		/* Rev. 0.3 */ +		pre = 'M'; m = 1; +		if (id_str == NULL) +			id_str = +# if defined(CONFIG_MPC852T) +		"PC852T"; +# elif defined(CONFIG_MPC859T) +		"PC859T"; +# elif defined(CONFIG_MPC859DSL) +		"PC859DSL"; +# elif defined(CONFIG_MPC866T) +		"PC866T"; +# else +		"PC866x"; /* Unknown chip from MPC866 family */ +# endif +		break; +	case 0x09000000: pre = 'M'; mid = suf = ""; m = 1; +		if (id_str == NULL) +			id_str = "PC885"; /* 870/875/880/885 */ +		break; + +	default: suf = NULL; break; +	} + +	if (id_str == NULL) +		id_str = "PC86x";	/* Unknown 86x chip */ +	if (suf) +		printf ("%c%s%sZPnn%s", pre, id_str, mid, suf); +	else +		printf ("unknown M%s (0x%08x)", id_str, k); + + +#if defined(CONFIG_SYS_8xx_CPUCLK_MIN) && defined(CONFIG_SYS_8xx_CPUCLK_MAX) +	printf (" at %s MHz [%d.%d...%d.%d MHz]\n       ", +		strmhz (buf, clock), +		CONFIG_SYS_8xx_CPUCLK_MIN / 1000000, +		((CONFIG_SYS_8xx_CPUCLK_MIN % 1000000) + 50000) / 100000, +		CONFIG_SYS_8xx_CPUCLK_MAX / 1000000, +		((CONFIG_SYS_8xx_CPUCLK_MAX % 1000000) + 50000) / 100000 +	); +#else +	printf (" at %s MHz: ", strmhz (buf, clock)); +#endif +	printf ("%u kB I-Cache %u kB D-Cache", +		checkicache () >> 10, +		checkdcache () >> 10 +	); + +	/* do we have a FEC (860T/P or 852/859/866/885)? */ + +	immap->im_cpm.cp_fec.fec_addr_low = 0x12345678; +	if (immap->im_cpm.cp_fec.fec_addr_low == 0x12345678) { +		printf (" FEC present"); +	} + +	if (!m) { +		puts (cpu_warning); +	} + +	putc ('\n'); + +#ifdef DEBUG +	if(clock != measure_gclk()) { +	    printf ("clock %ldHz != %dHz\n", clock, measure_gclk()); +	} +#endif + +	return 0; +} + +#elif defined(CONFIG_MPC862) + +static int check_CPU (long clock, uint pvr, uint immr) +{ +	volatile immap_t *immap = (immap_t *) (immr & 0xFFFF0000); +	uint k, m; +	char buf[32]; +	char pre = 'X'; +	char *mid = "xx"; +	char *suf; + +	/* the highest 16 bits should be 0x0050 for a 8xx */ + +	if ((pvr >> 16) != 0x0050) +		return -1; + +	k = (immr << 16) | *((ushort *) & immap->im_cpm.cp_dparam[0xB0]); +	m = 0; + +	switch (k) { + +		/* this value is not documented anywhere */ +	case 0x06000000: mid = "P"; suf = "0"; break; +	case 0x06010001: mid = "P"; suf = "A"; m = 1; break; +	case 0x07000003: mid = "P"; suf = "B"; m = 1; break; +	default: suf = NULL; break; +	} + +#ifndef CONFIG_MPC857 +	if (suf) +		printf ("%cPC862%sZPnn%s", pre, mid, suf); +	else +		printf ("unknown MPC862 (0x%08x)", k); +#else +	if (suf) +		printf ("%cPC857TZPnn%s", pre, suf); /* only 857T tested right now! */ +	else +		printf ("unknown MPC857 (0x%08x)", k); +#endif + +	printf (" at %s MHz:", strmhz (buf, clock)); + +	printf (" %u kB I-Cache", checkicache () >> 10); +	printf (" %u kB D-Cache", checkdcache () >> 10); + +	/* lets check and see if we're running on a 862T (or P?) */ + +	immap->im_cpm.cp_fec.fec_addr_low = 0x12345678; +	if (immap->im_cpm.cp_fec.fec_addr_low == 0x12345678) { +		printf (" FEC present"); +	} + +	if (!m) { +		puts (cpu_warning); +	} + +	putc ('\n'); + +	return 0; +} + +#elif defined(CONFIG_MPC823) + +static int check_CPU (long clock, uint pvr, uint immr) +{ +	volatile immap_t *immap = (immap_t *) (immr & 0xFFFF0000); +	uint k, m; +	char buf[32]; +	char *suf; + +	/* the highest 16 bits should be 0x0050 for a 8xx */ + +	if ((pvr >> 16) != 0x0050) +		return -1; + +	k = (immr << 16) | *((ushort *) & immap->im_cpm.cp_dparam[0xB0]); +	m = 0; + +	switch (k) { +		/* MPC823 */ +	case 0x20000000: suf = "0"; break; +	case 0x20010000: suf = "0.1"; break; +	case 0x20020000: suf = "Z2/3"; break; +	case 0x20020001: suf = "Z3"; break; +	case 0x21000000: suf = "A"; break; +	case 0x21010000: suf = "B"; m = 1; break; +	case 0x21010001: suf = "B2"; m = 1; break; +		/* MPC823E */ +	case 0x24010000: suf = NULL; +			puts ("PPC823EZTnnB2"); +			m = 1; +			break; +	default: +			suf = NULL; +			printf ("unknown MPC823 (0x%08x)", k); +			break; +	} +	if (suf) +		printf ("PPC823ZTnn%s", suf); + +	printf (" at %s MHz:", strmhz (buf, clock)); + +	printf (" %u kB I-Cache", checkicache () >> 10); +	printf (" %u kB D-Cache", checkdcache () >> 10); + +	/* lets check and see if we're running on a 860T (or P?) */ + +	immap->im_cpm.cp_fec.fec_addr_low = 0x12345678; +	if (immap->im_cpm.cp_fec.fec_addr_low == 0x12345678) { +		puts (" FEC present"); +	} + +	if (!m) { +		puts (cpu_warning); +	} + +	putc ('\n'); + +	return 0; +} + +#elif defined(CONFIG_MPC850) + +static int check_CPU (long clock, uint pvr, uint immr) +{ +	volatile immap_t *immap = (immap_t *) (immr & 0xFFFF0000); +	uint k, m; +	char buf[32]; + +	/* the highest 16 bits should be 0x0050 for a 8xx */ + +	if ((pvr >> 16) != 0x0050) +		return -1; + +	k = (immr << 16) | *((ushort *) & immap->im_cpm.cp_dparam[0xB0]); +	m = 0; + +	switch (k) { +	case 0x20020001: +		printf ("XPC850xxZT"); +		break; +	case 0x21000065: +		printf ("XPC850xxZTA"); +		break; +	case 0x21010067: +		printf ("XPC850xxZTB"); +		m = 1; +		break; +	case 0x21020068: +		printf ("XPC850xxZTC"); +		m = 1; +		break; +	default: +		printf ("unknown MPC850 (0x%08x)", k); +	} +	printf (" at %s MHz:", strmhz (buf, clock)); + +	printf (" %u kB I-Cache", checkicache () >> 10); +	printf (" %u kB D-Cache", checkdcache () >> 10); + +	/* lets check and see if we're running on a 850T (or P?) */ + +	immap->im_cpm.cp_fec.fec_addr_low = 0x12345678; +	if (immap->im_cpm.cp_fec.fec_addr_low == 0x12345678) { +		printf (" FEC present"); +	} + +	if (!m) { +		puts (cpu_warning); +	} + +	putc ('\n'); + +	return 0; +} +#else +#error CPU undefined +#endif +/* ------------------------------------------------------------------------- */ + +int checkcpu (void) +{ +	ulong clock = gd->cpu_clk; +	uint immr = get_immr (0);	/* Return full IMMR contents */ +	uint pvr = get_pvr (); + +	puts ("CPU:   "); + +	/* 850 has PARTNUM 20 */ +	/* 801 has PARTNUM 10 */ +	return check_CPU (clock, pvr, immr); +} + +/* ------------------------------------------------------------------------- */ +/* L1 i-cache                                                                */ +/* the standard 860 has 128 sets of 16 bytes in 2 ways (= 4 kB)              */ +/* the 860 P (plus) has 256 sets of 16 bytes in 4 ways (= 16 kB)             */ + +int checkicache (void) +{ +	volatile immap_t *immap = (immap_t *) CONFIG_SYS_IMMR; +	volatile memctl8xx_t *memctl = &immap->im_memctl; +	u32 cacheon = rd_ic_cst () & IDC_ENABLED; + +#ifdef CONFIG_IP86x +	u32 k = memctl->memc_br1 & ~0x00007fff;	/* probe in flash memoryarea */ +#else +	u32 k = memctl->memc_br0 & ~0x00007fff;	/* probe in flash memoryarea */ +#endif +	u32 m; +	u32 lines = -1; + +	wr_ic_cst (IDC_UNALL); +	wr_ic_cst (IDC_INVALL); +	wr_ic_cst (IDC_DISABLE); +	__asm__ volatile ("isync"); + +	while (!((m = rd_ic_cst ()) & IDC_CERR2)) { +		wr_ic_adr (k); +		wr_ic_cst (IDC_LDLCK); +		__asm__ volatile ("isync"); + +		lines++; +		k += 0x10;				/* the number of bytes in a cacheline */ +	} + +	wr_ic_cst (IDC_UNALL); +	wr_ic_cst (IDC_INVALL); + +	if (cacheon) +		wr_ic_cst (IDC_ENABLE); +	else +		wr_ic_cst (IDC_DISABLE); + +	__asm__ volatile ("isync"); + +	return lines << 4; +}; + +/* ------------------------------------------------------------------------- */ +/* L1 d-cache                                                                */ +/* the standard 860 has 128 sets of 16 bytes in 2 ways (= 4 kB)              */ +/* the 860 P (plus) has 256 sets of 16 bytes in 2 ways (= 8 kB)              */ +/* call with cache disabled                                                  */ + +int checkdcache (void) +{ +	volatile immap_t *immap = (immap_t *) CONFIG_SYS_IMMR; +	volatile memctl8xx_t *memctl = &immap->im_memctl; +	u32 cacheon = rd_dc_cst () & IDC_ENABLED; + +#ifdef CONFIG_IP86x +	u32 k = memctl->memc_br1 & ~0x00007fff;	/* probe in flash memoryarea */ +#else +	u32 k = memctl->memc_br0 & ~0x00007fff;	/* probe in flash memoryarea */ +#endif +	u32 m; +	u32 lines = -1; + +	wr_dc_cst (IDC_UNALL); +	wr_dc_cst (IDC_INVALL); +	wr_dc_cst (IDC_DISABLE); + +	while (!((m = rd_dc_cst ()) & IDC_CERR2)) { +		wr_dc_adr (k); +		wr_dc_cst (IDC_LDLCK); +		lines++; +		k += 0x10;	/* the number of bytes in a cacheline */ +	} + +	wr_dc_cst (IDC_UNALL); +	wr_dc_cst (IDC_INVALL); + +	if (cacheon) +		wr_dc_cst (IDC_ENABLE); +	else +		wr_dc_cst (IDC_DISABLE); + +	return lines << 4; +}; + +/* ------------------------------------------------------------------------- */ + +void upmconfig (uint upm, uint * table, uint size) +{ +	uint i; +	uint addr = 0; +	volatile immap_t *immap = (immap_t *) CONFIG_SYS_IMMR; +	volatile memctl8xx_t *memctl = &immap->im_memctl; + +	for (i = 0; i < size; i++) { +		memctl->memc_mdr = table[i];	/* (16-15) */ +		memctl->memc_mcr = addr | upm;	/* (16-16) */ +		addr++; +	} +} + +/* ------------------------------------------------------------------------- */ + +#ifndef CONFIG_LWMON + +int do_reset (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]) +{ +	ulong msr, addr; + +	volatile immap_t *immap = (immap_t *) CONFIG_SYS_IMMR; + +	immap->im_clkrst.car_plprcr |= PLPRCR_CSR;	/* Checkstop Reset enable */ + +	/* Interrupts and MMU off */ +	__asm__ volatile ("mtspr    81, 0"); +	__asm__ volatile ("mfmsr    %0":"=r" (msr)); + +	msr &= ~0x1030; +	__asm__ volatile ("mtmsr    %0"::"r" (msr)); + +	/* +	 * Trying to execute the next instruction at a non-existing address +	 * should cause a machine check, resulting in reset +	 */ +#ifdef CONFIG_SYS_RESET_ADDRESS +	addr = CONFIG_SYS_RESET_ADDRESS; +#else +	/* +	 * note: when CONFIG_SYS_MONITOR_BASE points to a RAM address, CONFIG_SYS_MONITOR_BASE +	 * - sizeof (ulong) is usually a valid address. Better pick an address +	 * known to be invalid on your system and assign it to CONFIG_SYS_RESET_ADDRESS. +	 * "(ulong)-1" used to be a good choice for many systems... +	 */ +	addr = CONFIG_SYS_MONITOR_BASE - sizeof (ulong); +#endif +	((void (*)(void)) addr) (); +	return 1; +} + +#else	/* CONFIG_LWMON */ + +/* + * On the LWMON board, the MCLR reset input of the PIC's on the board + * uses a 47K/1n RC combination which has a 47us time  constant.  The + * low  signal on the HRESET pin of the CPU is only 512 clocks = 8 us + * and thus too short to reset the external hardware. So we  use  the + * watchdog to reset the board. + */ +int do_reset (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]) +{ +	/* prevent triggering the watchdog */ +	disable_interrupts (); + +	/* make sure the watchdog is running */ +	reset_8xx_watchdog ((immap_t *) CONFIG_SYS_IMMR); + +	/* wait for watchdog reset */ +	while (1) {}; + +	/* NOTREACHED */ +	return 1; +} + +#endif	/* CONFIG_LWMON */ + +/* ------------------------------------------------------------------------- */ + +/* + * Get timebase clock frequency (like cpu_clk in Hz) + * + * See sections 14.2 and 14.6 of the User's Manual + */ +unsigned long get_tbclk (void) +{ +	uint immr = get_immr (0);	/* Return full IMMR contents */ +	volatile immap_t *immap = (volatile immap_t *)(immr & 0xFFFF0000); +	ulong oscclk, factor, pll; + +	if (immap->im_clkrst.car_sccr & SCCR_TBS) { +		return (gd->cpu_clk / 16); +	} + +	pll = immap->im_clkrst.car_plprcr; + +#define PLPRCR_val(a) ((pll & PLPRCR_ ## a ## _MSK) >> PLPRCR_ ## a ## _SHIFT) + +	/* +	 * For newer PQ1 chips (MPC866/87x/88x families), PLL multiplication +	 * factor is calculated as follows: +	 * +	 *		     MFN +	 *	     MFI + ------- +	 *		   MFD + 1 +	 * factor =  ----------------- +	 *	     (PDF + 1) * 2^S +	 * +	 * For older chips, it's just MF field of PLPRCR plus one. +	 */ +	if ((immr & 0x0FFF) >= MPC8xx_NEW_CLK) { /* MPC866/87x/88x series */ +		factor = (PLPRCR_val(MFI) + PLPRCR_val(MFN)/(PLPRCR_val(MFD)+1))/ +			(PLPRCR_val(PDF)+1) / (1<<PLPRCR_val(S)); +	} else { +		factor = PLPRCR_val(MF)+1; +	} + +	oscclk = gd->cpu_clk / factor; + +	if ((immap->im_clkrst.car_sccr & SCCR_RTSEL) == 0 || factor > 2) { +		return (oscclk / 4); +	} +	return (oscclk / 16); +} + +/* ------------------------------------------------------------------------- */ + +#if defined(CONFIG_WATCHDOG) +void watchdog_reset (void) +{ +	int re_enable = disable_interrupts (); + +	reset_8xx_watchdog ((immap_t *) CONFIG_SYS_IMMR); +	if (re_enable) +		enable_interrupts (); +} +#endif /* CONFIG_WATCHDOG */ + +#if defined(CONFIG_WATCHDOG) || defined(CONFIG_LWMON) + +void reset_8xx_watchdog (volatile immap_t * immr) +{ +# if defined(CONFIG_LWMON) +	/* +	 * The LWMON board uses a MAX6301 Watchdog +	 * with the trigger pin connected to port PA.7 +	 * +	 * (The old board version used a MAX706TESA Watchdog, which +	 * had to be handled exactly the same.) +	 */ +# define WATCHDOG_BIT	0x0100 +	immr->im_ioport.iop_papar &= ~(WATCHDOG_BIT);	/* GPIO     */ +	immr->im_ioport.iop_padir |= WATCHDOG_BIT;	/* Output   */ +	immr->im_ioport.iop_paodr &= ~(WATCHDOG_BIT);	/* active output */ + +	immr->im_ioport.iop_padat ^= WATCHDOG_BIT;	/* Toggle WDI   */ +# elif defined(CONFIG_KUP4K) || defined(CONFIG_KUP4X) +	/* +	 * The KUP4 boards uses a TPS3705 Watchdog +	 * with the trigger pin connected to port PA.5 +	 */ +# define WATCHDOG_BIT	0x0400 +	immr->im_ioport.iop_papar &= ~(WATCHDOG_BIT);	/* GPIO     */ +	immr->im_ioport.iop_padir |= WATCHDOG_BIT;	/* Output   */ +	immr->im_ioport.iop_paodr &= ~(WATCHDOG_BIT);	/* active output */ + +	immr->im_ioport.iop_padat ^= WATCHDOG_BIT;	/* Toggle WDI   */ +# else +	/* +	 * All other boards use the MPC8xx Internal Watchdog +	 */ +	immr->im_siu_conf.sc_swsr = 0x556c;	/* write magic1 */ +	immr->im_siu_conf.sc_swsr = 0xaa39;	/* write magic2 */ +# endif /* CONFIG_LWMON */ +} +#endif /* CONFIG_WATCHDOG */ + +/* + * Initializes on-chip ethernet controllers. + * to override, implement board_eth_init() + */ +int cpu_eth_init(bd_t *bis) +{ +#if defined(SCC_ENET) && defined(CONFIG_CMD_NET) +	scc_initialize(bis); +#endif +#if defined(FEC_ENET) +	fec_initialize(bis); +#endif +	return 0; +} diff --git a/arch/powerpc/cpu/mpc8xx/cpu_init.c b/arch/powerpc/cpu/mpc8xx/cpu_init.c new file mode 100644 index 000000000..eb0091bdb --- /dev/null +++ b/arch/powerpc/cpu/mpc8xx/cpu_init.c @@ -0,0 +1,285 @@ +/* + * (C) Copyright 2000-2002 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include <common.h> +#include <watchdog.h> + +#include <mpc8xx.h> +#include <commproc.h> + +#if defined(CONFIG_SYS_RTCSC) || defined(CONFIG_SYS_RMDS) +DECLARE_GLOBAL_DATA_PTR; +#endif + +#if defined(CONFIG_SYS_I2C_UCODE_PATCH) || defined(CONFIG_SYS_SPI_UCODE_PATCH) || \ +    defined(CONFIG_SYS_SMC_UCODE_PATCH) +void cpm_load_patch (volatile immap_t * immr); +#endif + +/* + * Breath some life into the CPU... + * + * Set up the memory map, + * initialize a bunch of registers, + * initialize the UPM's + */ +void cpu_init_f (volatile immap_t * immr) +{ +#ifndef CONFIG_MBX +	volatile memctl8xx_t *memctl = &immr->im_memctl; +# ifdef CONFIG_SYS_PLPRCR +	ulong mfmask; +# endif +#endif +	ulong reg; + +	/* SYPCR - contains watchdog control (11-9) */ + +	immr->im_siu_conf.sc_sypcr = CONFIG_SYS_SYPCR; + +#if defined(CONFIG_WATCHDOG) +	reset_8xx_watchdog (immr); +#endif /* CONFIG_WATCHDOG */ + +	/* SIUMCR - contains debug pin configuration (11-6) */ +#ifndef CONFIG_SVM_SC8xx +	immr->im_siu_conf.sc_siumcr |= CONFIG_SYS_SIUMCR; +#else +	immr->im_siu_conf.sc_siumcr = CONFIG_SYS_SIUMCR; +#endif +	/* initialize timebase status and control register (11-26) */ +	/* unlock TBSCRK */ + +	immr->im_sitk.sitk_tbscrk = KAPWR_KEY; +	immr->im_sit.sit_tbscr = CONFIG_SYS_TBSCR; + +	/* initialize the PIT (11-31) */ + +	immr->im_sitk.sitk_piscrk = KAPWR_KEY; +	immr->im_sit.sit_piscr = CONFIG_SYS_PISCR; + +	/* System integration timers. Don't change EBDF! (15-27) */ + +	immr->im_clkrstk.cark_sccrk = KAPWR_KEY; +	reg = immr->im_clkrst.car_sccr; +	reg &= SCCR_MASK; +	reg |= CONFIG_SYS_SCCR; +	immr->im_clkrst.car_sccr = reg; + +	/* PLL (CPU clock) settings (15-30) */ + +	immr->im_clkrstk.cark_plprcrk = KAPWR_KEY; + +#ifndef CONFIG_MBX		/* MBX board does things different */ + +	/* If CONFIG_SYS_PLPRCR (set in the various *_config.h files) tries to +	 * set the MF field, then just copy CONFIG_SYS_PLPRCR over car_plprcr, +	 * otherwise OR in CONFIG_SYS_PLPRCR so we do not change the current MF +	 * field value. +	 * +	 * For newer (starting MPC866) chips PLPRCR layout is different. +	 */ +#ifdef CONFIG_SYS_PLPRCR +	if (get_immr(0xFFFF) >= MPC8xx_NEW_CLK) +	   mfmask = PLPRCR_MFACT_MSK; +	else +	   mfmask = PLPRCR_MF_MSK; + +	if ((CONFIG_SYS_PLPRCR & mfmask) != 0) +	   reg = CONFIG_SYS_PLPRCR;			/* reset control bits   */ +	else { +	   reg = immr->im_clkrst.car_plprcr; +	   reg &= mfmask;			/* isolate MF-related fields */ +	   reg |= CONFIG_SYS_PLPRCR;			/* reset control bits   */ +	} +	immr->im_clkrst.car_plprcr = reg; +#endif + +	/* +	 * Memory Controller: +	 */ + +	/* perform BR0 reset that MPC850 Rev. A can't guarantee */ +	reg = memctl->memc_br0; +	reg &= BR_PS_MSK;	/* Clear everything except Port Size bits */ +	reg |= BR_V;		/* then add just the "Bank Valid" bit     */ +	memctl->memc_br0 = reg; + +	/* Map banks 0 (and maybe 1) to the FLASH banks 0 (and 1) at +	 * preliminary addresses - these have to be modified later +	 * when FLASH size has been determined +	 * +	 * Depending on the size of the memory region defined by +	 * CONFIG_SYS_OR0_REMAP some boards (wide address mask) allow to map the +	 * CONFIG_SYS_MONITOR_BASE, while others (narrower address mask) can't +	 * map CONFIG_SYS_MONITOR_BASE. +	 * +	 * For example, for CONFIG_IVMS8, the CONFIG_SYS_MONITOR_BASE is +	 * 0xff000000, but CONFIG_SYS_OR0_REMAP's address mask is 0xfff80000. +	 * +	 * If BR0 wasn't loaded with address base 0xff000000, then BR0's +	 * base address remains as 0x00000000. However, the address mask +	 * have been narrowed to 512Kb, so CONFIG_SYS_MONITOR_BASE wasn't mapped +	 * into the Bank0. +	 * +	 * This is why CONFIG_IVMS8 and similar boards must load BR0 with +	 * CONFIG_SYS_BR0_PRELIM in advance. +	 * +	 * [Thanks to Michael Liao for this explanation. +	 *  I owe him a free beer. - wd] +	 */ + +#if defined(CONFIG_GTH)	        || \ +    defined(CONFIG_HERMES)	|| \ +    defined(CONFIG_ICU862)	|| \ +    defined(CONFIG_IP860)	|| \ +    defined(CONFIG_IVML24)	|| \ +    defined(CONFIG_IVMS8)	|| \ +    defined(CONFIG_LWMON)	|| \ +    defined(CONFIG_MHPC)	|| \ +    defined(CONFIG_PCU_E)	|| \ +    defined(CONFIG_R360MPI)	|| \ +    defined(CONFIG_RMU)		|| \ +    defined(CONFIG_RPXCLASSIC)	|| \ +    defined(CONFIG_RPXLITE)	|| \ +    defined(CONFIG_SPC1920)	|| \ +    defined(CONFIG_SPD823TS) + +	memctl->memc_br0 = CONFIG_SYS_BR0_PRELIM; +#endif + +#if defined(CONFIG_SYS_OR0_REMAP) +	memctl->memc_or0 = CONFIG_SYS_OR0_REMAP; +#endif +#if defined(CONFIG_SYS_OR1_REMAP) +	memctl->memc_or1 = CONFIG_SYS_OR1_REMAP; +#endif +#if defined(CONFIG_SYS_OR5_REMAP) +	memctl->memc_or5 = CONFIG_SYS_OR5_REMAP; +#endif + +	/* now restrict to preliminary range */ +	memctl->memc_br0 = CONFIG_SYS_BR0_PRELIM; +	memctl->memc_or0 = CONFIG_SYS_OR0_PRELIM; + +#if (defined(CONFIG_SYS_OR1_PRELIM) && defined(CONFIG_SYS_BR1_PRELIM)) +	memctl->memc_or1 = CONFIG_SYS_OR1_PRELIM; +	memctl->memc_br1 = CONFIG_SYS_BR1_PRELIM; +#endif + +#if defined(CONFIG_IP860) /* disable CS0 now that Flash is mapped on CS1 */ +	memctl->memc_br0 = 0; +#endif + +#if defined(CONFIG_SYS_OR2_PRELIM) && defined(CONFIG_SYS_BR2_PRELIM) +	memctl->memc_or2 = CONFIG_SYS_OR2_PRELIM; +	memctl->memc_br2 = CONFIG_SYS_BR2_PRELIM; +#endif + +#if defined(CONFIG_SYS_OR3_PRELIM) && defined(CONFIG_SYS_BR3_PRELIM) +	memctl->memc_or3 = CONFIG_SYS_OR3_PRELIM; +	memctl->memc_br3 = CONFIG_SYS_BR3_PRELIM; +#endif + +#if defined(CONFIG_SYS_OR4_PRELIM) && defined(CONFIG_SYS_BR4_PRELIM) +	memctl->memc_or4 = CONFIG_SYS_OR4_PRELIM; +	memctl->memc_br4 = CONFIG_SYS_BR4_PRELIM; +#endif + +#if defined(CONFIG_SYS_OR5_PRELIM) && defined(CONFIG_SYS_BR5_PRELIM) +	memctl->memc_or5 = CONFIG_SYS_OR5_PRELIM; +	memctl->memc_br5 = CONFIG_SYS_BR5_PRELIM; +#endif + +#if defined(CONFIG_SYS_OR6_PRELIM) && defined(CONFIG_SYS_BR6_PRELIM) +	memctl->memc_or6 = CONFIG_SYS_OR6_PRELIM; +	memctl->memc_br6 = CONFIG_SYS_BR6_PRELIM; +#endif + +#if defined(CONFIG_SYS_OR7_PRELIM) && defined(CONFIG_SYS_BR7_PRELIM) +	memctl->memc_or7 = CONFIG_SYS_OR7_PRELIM; +	memctl->memc_br7 = CONFIG_SYS_BR7_PRELIM; +#endif + +#endif /* ! CONFIG_MBX */ + +	/* +	 * Reset CPM +	 */ +	immr->im_cpm.cp_cpcr = CPM_CR_RST | CPM_CR_FLG; +	do {			/* Spin until command processed     */ +		__asm__ ("eieio"); +	} while (immr->im_cpm.cp_cpcr & CPM_CR_FLG); + +#ifdef CONFIG_MBX +	/* +	 * on the MBX, things are a little bit different: +	 * - we need to read the VPD to get board information +	 * - the plprcr is set up dynamically +	 * - the memory controller is set up dynamically +	 */ +	mbx_init (); +#endif /* CONFIG_MBX */ + +#ifdef CONFIG_RPXCLASSIC +	rpxclassic_init (); +#endif + +#if defined(CONFIG_RPXLITE) && defined(CONFIG_ENV_IS_IN_NVRAM) +	rpxlite_init (); +#endif + +#ifdef CONFIG_SYS_RCCR			/* must be done before cpm_load_patch() */ +	/* write config value */ +	immr->im_cpm.cp_rccr = CONFIG_SYS_RCCR; +#endif + +#if defined(CONFIG_SYS_I2C_UCODE_PATCH) || defined(CONFIG_SYS_SPI_UCODE_PATCH) || \ +    defined(CONFIG_SYS_SMC_UCODE_PATCH) +	cpm_load_patch (immr);	/* load mpc8xx  microcode patch */ +#endif +} + +/* + * initialize higher level parts of CPU like timers + */ +int cpu_init_r (void) +{ +#if defined(CONFIG_SYS_RTCSC) || defined(CONFIG_SYS_RMDS) +	bd_t *bd = gd->bd; +	volatile immap_t *immr = (volatile immap_t *) (bd->bi_immr_base); +#endif + +#ifdef CONFIG_SYS_RTCSC +	/* Unlock RTSC register */ +	immr->im_sitk.sitk_rtcsck = KAPWR_KEY; +	/* write config value */ +	immr->im_sit.sit_rtcsc = CONFIG_SYS_RTCSC; +#endif + +#ifdef CONFIG_SYS_RMDS +	/* write config value */ +	immr->im_cpm.cp_rmds = CONFIG_SYS_RMDS; +#endif +	return (0); +} diff --git a/arch/powerpc/cpu/mpc8xx/fdt.c b/arch/powerpc/cpu/mpc8xx/fdt.c new file mode 100644 index 000000000..7130983ff --- /dev/null +++ b/arch/powerpc/cpu/mpc8xx/fdt.c @@ -0,0 +1,46 @@ +/* + * Copyright 2008 (C) Bryan O'Donoghue + * + * Code copied & edited from Freescale mpc85xx stuff. + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include <common.h> +#include <libfdt.h> +#include <fdt_support.h> + +DECLARE_GLOBAL_DATA_PTR; + +void ft_cpu_setup(void *blob, bd_t *bd) +{ +	do_fixup_by_prop_u32(blob, "device_type", "cpu", 4, +		"timebase-frequency", get_tbclk(), 1); +	do_fixup_by_prop_u32(blob, "device_type", "cpu", 4, +		"bus-frequency", bd->bi_busfreq, 1); +	do_fixup_by_prop_u32(blob, "device_type", "cpu", 4, +		"clock-frequency", bd->bi_intfreq, 1); +	do_fixup_by_compat_u32(blob, "fsl,cpm-brg", "clock-frequency", +		gd->brg_clk, 1); + +	/* Fixup ethernet MAC addresses */ +	fdt_fixup_ethernet(blob); + +	fdt_fixup_memory(blob, (u64)bd->bi_memstart, (u64)bd->bi_memsize); +} diff --git a/arch/powerpc/cpu/mpc8xx/fec.c b/arch/powerpc/cpu/mpc8xx/fec.c new file mode 100644 index 000000000..89c1ff939 --- /dev/null +++ b/arch/powerpc/cpu/mpc8xx/fec.c @@ -0,0 +1,1026 @@ +/* + * (C) Copyright 2000 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include <common.h> +#include <malloc.h> +#include <commproc.h> +#include <net.h> +#include <command.h> + +DECLARE_GLOBAL_DATA_PTR; + +#undef	ET_DEBUG + +#if defined(CONFIG_CMD_NET) && \ +	(defined(FEC_ENET) || defined(CONFIG_ETHER_ON_FEC1) || defined(CONFIG_ETHER_ON_FEC2)) + +/* compatibility test, if only FEC_ENET defined assume ETHER on FEC1 */ +#if defined(FEC_ENET) && !defined(CONFIG_ETHER_ON_FEC1) && !defined(CONFIG_ETHER_ON_FEC2) +#define CONFIG_ETHER_ON_FEC1 1 +#endif + +/* define WANT_MII when MII support is required */ +#if defined(CONFIG_SYS_DISCOVER_PHY) || defined(CONFIG_FEC1_PHY) || defined(CONFIG_FEC2_PHY) +#define WANT_MII +#else +#undef WANT_MII +#endif + +#if defined(WANT_MII) +#include <miiphy.h> + +#if !(defined(CONFIG_MII) || defined(CONFIG_CMD_MII)) +#error "CONFIG_MII has to be defined!" +#endif + +#endif + +#if defined(CONFIG_RMII) && !defined(WANT_MII) +#error RMII support is unusable without a working PHY. +#endif + +#ifdef CONFIG_SYS_DISCOVER_PHY +static int mii_discover_phy(struct eth_device *dev); +#endif + +int fec8xx_miiphy_read(char *devname, unsigned char addr, +		unsigned char  reg, unsigned short *value); +int fec8xx_miiphy_write(char *devname, unsigned char  addr, +		unsigned char  reg, unsigned short value); + +static struct ether_fcc_info_s +{ +	int ether_index; +	int fecp_offset; +	int phy_addr; +	int actual_phy_addr; +	int initialized; +} +	ether_fcc_info[] = { +#if defined(CONFIG_ETHER_ON_FEC1) +	{ +		0, +		offsetof(immap_t, im_cpm.cp_fec1), +#if defined(CONFIG_FEC1_PHY) +		CONFIG_FEC1_PHY, +#else +		-1,	/* discover */ +#endif +		-1, +		0, + +	}, +#endif +#if defined(CONFIG_ETHER_ON_FEC2) +	{ +		1, +		offsetof(immap_t, im_cpm.cp_fec2), +#if defined(CONFIG_FEC2_PHY) +		CONFIG_FEC2_PHY, +#else +		-1, +#endif +		-1, +		0, +	}, +#endif +}; + +/* Ethernet Transmit and Receive Buffers */ +#define DBUF_LENGTH  1520 + +#define TX_BUF_CNT 2 + +#define TOUT_LOOP 100 + +#define PKT_MAXBUF_SIZE		1518 +#define PKT_MINBUF_SIZE		64 +#define PKT_MAXBLR_SIZE		1520 + +#ifdef __GNUC__ +static char txbuf[DBUF_LENGTH] __attribute__ ((aligned(8))); +#else +#error txbuf must be aligned. +#endif + +static uint rxIdx;	/* index of the current RX buffer */ +static uint txIdx;	/* index of the current TX buffer */ + +/* +  * FEC Ethernet Tx and Rx buffer descriptors allocated at the +  *  immr->udata_bd address on Dual-Port RAM +  * Provide for Double Buffering +  */ + +typedef volatile struct CommonBufferDescriptor { +    cbd_t rxbd[PKTBUFSRX];		/* Rx BD */ +    cbd_t txbd[TX_BUF_CNT];		/* Tx BD */ +} RTXBD; + +static RTXBD *rtx = NULL; + +static int fec_send(struct eth_device* dev, volatile void *packet, int length); +static int fec_recv(struct eth_device* dev); +static int fec_init(struct eth_device* dev, bd_t * bd); +static void fec_halt(struct eth_device* dev); +#if defined(CONFIG_MII) || defined(CONFIG_CMD_MII) +static void __mii_init(void); +#endif + +int fec_initialize(bd_t *bis) +{ +	struct eth_device* dev; +	struct ether_fcc_info_s *efis; +	int             i; + +	for (i = 0; i < sizeof(ether_fcc_info) / sizeof(ether_fcc_info[0]); i++) { + +		dev = malloc(sizeof(*dev)); +		if (dev == NULL) +			hang(); + +		memset(dev, 0, sizeof(*dev)); + +		/* for FEC1 make sure that the name of the interface is the same +		   as the old one for compatibility reasons */ +		if (i == 0) { +			sprintf (dev->name, "FEC ETHERNET"); +		} else { +			sprintf (dev->name, "FEC%d ETHERNET", +				ether_fcc_info[i].ether_index + 1); +		} + +		efis = ðer_fcc_info[i]; + +		/* +		 * reset actual phy addr +		 */ +		efis->actual_phy_addr = -1; + +		dev->priv = efis; +		dev->init = fec_init; +		dev->halt = fec_halt; +		dev->send = fec_send; +		dev->recv = fec_recv; + +		eth_register(dev); + +#if defined(CONFIG_MII) || defined(CONFIG_CMD_MII) +		miiphy_register(dev->name, +			fec8xx_miiphy_read, fec8xx_miiphy_write); +#endif +	} +	return 1; +} + +static int fec_send(struct eth_device* dev, volatile void *packet, int length) +{ +	int j, rc; +	struct ether_fcc_info_s *efis = dev->priv; +	volatile fec_t *fecp = (volatile fec_t *)(CONFIG_SYS_IMMR + efis->fecp_offset); + +	/* section 16.9.23.3 +	 * Wait for ready +	 */ +	j = 0; +	while ((rtx->txbd[txIdx].cbd_sc & BD_ENET_TX_READY) && (j<TOUT_LOOP)) { +		udelay(1); +		j++; +	} +	if (j>=TOUT_LOOP) { +		printf("TX not ready\n"); +	} + +	rtx->txbd[txIdx].cbd_bufaddr = (uint)packet; +	rtx->txbd[txIdx].cbd_datlen  = length; +	rtx->txbd[txIdx].cbd_sc |= BD_ENET_TX_READY | BD_ENET_TX_LAST; +	__asm__ ("eieio"); + +	/* Activate transmit Buffer Descriptor polling */ +	fecp->fec_x_des_active = 0x01000000;	/* Descriptor polling active	*/ + +	j = 0; +	while ((rtx->txbd[txIdx].cbd_sc & BD_ENET_TX_READY) && (j<TOUT_LOOP)) { +#if defined(CONFIG_ICU862) +		udelay(10); +#else +		udelay(1); +#endif +		j++; +	} +	if (j>=TOUT_LOOP) { +		printf("TX timeout\n"); +	} +#ifdef ET_DEBUG +	printf("%s[%d] %s: cycles: %d    status: %x  retry cnt: %d\n", +	__FILE__,__LINE__,__FUNCTION__,j,rtx->txbd[txIdx].cbd_sc, +	(rtx->txbd[txIdx].cbd_sc & 0x003C)>>2); +#endif +	/* return only status bits */; +	rc = (rtx->txbd[txIdx].cbd_sc & BD_ENET_TX_STATS); + +	txIdx = (txIdx + 1) % TX_BUF_CNT; + +	return rc; +} + +static int fec_recv (struct eth_device *dev) +{ +	struct ether_fcc_info_s *efis = dev->priv; +	volatile fec_t *fecp = +		(volatile fec_t *) (CONFIG_SYS_IMMR + efis->fecp_offset); +	int length; + +	for (;;) { +		/* section 16.9.23.2 */ +		if (rtx->rxbd[rxIdx].cbd_sc & BD_ENET_RX_EMPTY) { +			length = -1; +			break;	/* nothing received - leave for() loop */ +		} + +		length = rtx->rxbd[rxIdx].cbd_datlen; + +		if (rtx->rxbd[rxIdx].cbd_sc & 0x003f) { +#ifdef ET_DEBUG +			printf ("%s[%d] err: %x\n", +				__FUNCTION__, __LINE__, +				rtx->rxbd[rxIdx].cbd_sc); +#endif +		} else { +			volatile uchar *rx = NetRxPackets[rxIdx]; + +			length -= 4; + +#if defined(CONFIG_CMD_CDP) +			if ((rx[0] & 1) != 0 +			    && memcmp ((uchar *) rx, NetBcastAddr, 6) != 0 +			    && memcmp ((uchar *) rx, NetCDPAddr, 6) != 0) +				rx = NULL; +#endif +			/* +			 * Pass the packet up to the protocol layers. +			 */ +			if (rx != NULL) +				NetReceive (rx, length); +		} + +		/* Give the buffer back to the FEC. */ +		rtx->rxbd[rxIdx].cbd_datlen = 0; + +		/* wrap around buffer index when necessary */ +		if ((rxIdx + 1) >= PKTBUFSRX) { +			rtx->rxbd[PKTBUFSRX - 1].cbd_sc = +				(BD_ENET_RX_WRAP | BD_ENET_RX_EMPTY); +			rxIdx = 0; +		} else { +			rtx->rxbd[rxIdx].cbd_sc = BD_ENET_RX_EMPTY; +			rxIdx++; +		} + +		__asm__ ("eieio"); + +		/* Try to fill Buffer Descriptors */ +		fecp->fec_r_des_active = 0x01000000;	/* Descriptor polling active    */ +	} + +	return length; +} + +/************************************************************** + * + * FEC Ethernet Initialization Routine + * + *************************************************************/ + +#define	FEC_ECNTRL_PINMUX	0x00000004 +#define FEC_ECNTRL_ETHER_EN	0x00000002 +#define FEC_ECNTRL_RESET	0x00000001 + +#define FEC_RCNTRL_BC_REJ	0x00000010 +#define FEC_RCNTRL_PROM		0x00000008 +#define FEC_RCNTRL_MII_MODE	0x00000004 +#define FEC_RCNTRL_DRT		0x00000002 +#define FEC_RCNTRL_LOOP		0x00000001 + +#define FEC_TCNTRL_FDEN		0x00000004 +#define FEC_TCNTRL_HBC		0x00000002 +#define FEC_TCNTRL_GTS		0x00000001 + +#define	FEC_RESET_DELAY		50 + +#if defined(CONFIG_RMII) + +static inline void fec_10Mbps(struct eth_device *dev) +{ +	struct ether_fcc_info_s *efis = dev->priv; +	int fecidx = efis->ether_index; +	uint mask = (fecidx == 0) ? 0x0000010 : 0x0000008; + +	if ((unsigned int)fecidx >= 2) +		hang(); + +	((volatile immap_t *)CONFIG_SYS_IMMR)->im_cpm.cp_cptr |=  mask; +} + +static inline void fec_100Mbps(struct eth_device *dev) +{ +	struct ether_fcc_info_s *efis = dev->priv; +	int fecidx = efis->ether_index; +	uint mask = (fecidx == 0) ? 0x0000010 : 0x0000008; + +	if ((unsigned int)fecidx >= 2) +		hang(); + +	((volatile immap_t *)CONFIG_SYS_IMMR)->im_cpm.cp_cptr &= ~mask; +} + +#endif + +static inline void fec_full_duplex(struct eth_device *dev) +{ +	struct ether_fcc_info_s *efis = dev->priv; +	volatile fec_t *fecp = (volatile fec_t *)(CONFIG_SYS_IMMR + efis->fecp_offset); + +	fecp->fec_r_cntrl &= ~FEC_RCNTRL_DRT; +	fecp->fec_x_cntrl |=  FEC_TCNTRL_FDEN;	/* FD enable */ +} + +static inline void fec_half_duplex(struct eth_device *dev) +{ +	struct ether_fcc_info_s *efis = dev->priv; +	volatile fec_t *fecp = (volatile fec_t *)(CONFIG_SYS_IMMR + efis->fecp_offset); + +	fecp->fec_r_cntrl |=  FEC_RCNTRL_DRT; +	fecp->fec_x_cntrl &= ~FEC_TCNTRL_FDEN;	/* FD disable */ +} + +static void fec_pin_init(int fecidx) +{ +	bd_t           *bd = gd->bd; +	volatile immap_t *immr = (immap_t *) CONFIG_SYS_IMMR; +	volatile fec_t *fecp; + +	/* +	 * only two FECs please +	 */ +	if ((unsigned int)fecidx >= 2) +		hang(); + +	if (fecidx == 0) +		fecp = &immr->im_cpm.cp_fec1; +	else +		fecp = &immr->im_cpm.cp_fec2; + +	/* +	 * Set MII speed to 2.5 MHz or slightly below. +	 * * According to the MPC860T (Rev. D) Fast ethernet controller user +	 * * manual (6.2.14), +	 * * the MII management interface clock must be less than or equal +	 * * to 2.5 MHz. +	 * * This MDC frequency is equal to system clock / (2 * MII_SPEED). +	 * * Then MII_SPEED = system_clock / 2 * 2,5 MHz. +	 * +	 * All MII configuration is done via FEC1 registers: +	 */ +	immr->im_cpm.cp_fec1.fec_mii_speed = ((bd->bi_intfreq + 4999999) / 5000000) << 1; + +#if defined(CONFIG_NETTA) || defined(CONFIG_NETPHONE) || defined(CONFIG_NETTA2) +	/* our PHYs are the limit at 2.5 MHz */ +	fecp->fec_mii_speed <<= 1; +#endif + +#if defined(CONFIG_MPC885_FAMILY) && defined(WANT_MII) +	/* use MDC for MII */ +	immr->im_ioport.iop_pdpar |=  0x0080; +	immr->im_ioport.iop_pddir &= ~0x0080; +#endif + +	if (fecidx == 0) { +#if defined(CONFIG_ETHER_ON_FEC1) + +#if defined(CONFIG_MPC885_FAMILY) /* MPC87x/88x have got 2 FECs and different pinout */ + +#if !defined(CONFIG_RMII) + +		immr->im_ioport.iop_papar |=  0xf830; +		immr->im_ioport.iop_padir |=  0x0830; +		immr->im_ioport.iop_padir &= ~0xf000; + +		immr->im_cpm.cp_pbpar     |=  0x00001001; +		immr->im_cpm.cp_pbdir     &= ~0x00001001; + +		immr->im_ioport.iop_pcpar |=  0x000c; +		immr->im_ioport.iop_pcdir &= ~0x000c; + +		immr->im_cpm.cp_pepar     |=  0x00000003; +		immr->im_cpm.cp_pedir     |=  0x00000003; +		immr->im_cpm.cp_peso      &= ~0x00000003; + +		immr->im_cpm.cp_cptr      &= ~0x00000100; + +#else + +#if !defined(CONFIG_FEC1_PHY_NORXERR) +		immr->im_ioport.iop_papar |=  0x1000; +		immr->im_ioport.iop_padir &= ~0x1000; +#endif +		immr->im_ioport.iop_papar |=  0xe810; +		immr->im_ioport.iop_padir |=  0x0810; +		immr->im_ioport.iop_padir &= ~0xe000; + +		immr->im_cpm.cp_pbpar     |=  0x00000001; +		immr->im_cpm.cp_pbdir     &= ~0x00000001; + +		immr->im_cpm.cp_cptr      |=  0x00000100; +		immr->im_cpm.cp_cptr      &= ~0x00000050; + +#endif /* !CONFIG_RMII */ + +#elif !defined(CONFIG_ICU862) && !defined(CONFIG_IAD210) +		/* +		 * Configure all of port D for MII. +		 */ +		immr->im_ioport.iop_pdpar = 0x1fff; + +		/* +		 * Bits moved from Rev. D onward +		 */ +		if ((get_immr(0) & 0xffff) < 0x0501) +			immr->im_ioport.iop_pddir = 0x1c58;	/* Pre rev. D */ +		else +			immr->im_ioport.iop_pddir = 0x1fff;	/* Rev. D and later */ +#else +		/* +		 * Configure port A for MII. +		 */ + +#if defined(CONFIG_ICU862) && defined(CONFIG_SYS_DISCOVER_PHY) + +		/* +		 * On the ICU862 board the MII-MDC pin is routed to PD8 pin +		 * * of CPU, so for this board we need to configure Utopia and +		 * * enable PD8 to MII-MDC function +		 */ +		immr->im_ioport.iop_pdpar |= 0x4080; +#endif + +		/* +		 * Has Utopia been configured? +		 */ +		if (immr->im_ioport.iop_pdpar & (0x8000 >> 1)) { +			/* +			 * YES - Use MUXED mode for UTOPIA bus. +			 * This frees Port A for use by MII (see 862UM table 41-6). +			 */ +			immr->im_ioport.utmode &= ~0x80; +		} else { +			/* +			 * NO - set SPLIT mode for UTOPIA bus. +			 * +			 * This doesn't really effect UTOPIA (which isn't +			 * enabled anyway) but just tells the 862 +			 * to use port A for MII (see 862UM table 41-6). +			 */ +			immr->im_ioport.utmode |= 0x80; +		} +#endif				/* !defined(CONFIG_ICU862) */ + +#endif	/* CONFIG_ETHER_ON_FEC1 */ +	} else if (fecidx == 1) { + +#if defined(CONFIG_ETHER_ON_FEC2) + +#if defined(CONFIG_MPC885_FAMILY) /* MPC87x/88x have got 2 FECs and different pinout */ + +#if !defined(CONFIG_RMII) +		immr->im_cpm.cp_pepar     |=  0x0003fffc; +		immr->im_cpm.cp_pedir     |=  0x0003fffc; +		immr->im_cpm.cp_peso      &= ~0x000087fc; +		immr->im_cpm.cp_peso      |=  0x00037800; + +		immr->im_cpm.cp_cptr      &= ~0x00000080; +#else + +#if !defined(CONFIG_FEC2_PHY_NORXERR) +		immr->im_cpm.cp_pepar     |=  0x00000010; +		immr->im_cpm.cp_pedir     |=  0x00000010; +		immr->im_cpm.cp_peso      &= ~0x00000010; +#endif +		immr->im_cpm.cp_pepar     |=  0x00039620; +		immr->im_cpm.cp_pedir     |=  0x00039620; +		immr->im_cpm.cp_peso      |=  0x00031000; +		immr->im_cpm.cp_peso      &= ~0x00008620; + +		immr->im_cpm.cp_cptr      |=  0x00000080; +		immr->im_cpm.cp_cptr      &= ~0x00000028; +#endif /* CONFIG_RMII */ + +#endif /* CONFIG_MPC885_FAMILY */ + +#endif /* CONFIG_ETHER_ON_FEC2 */ + +	} +} + +static int fec_reset(volatile fec_t *fecp) +{ +	int i; + +	/* Whack a reset. +	 * A delay is required between a reset of the FEC block and +	 * initialization of other FEC registers because the reset takes +	 * some time to complete. If you don't delay, subsequent writes +	 * to FEC registers might get killed by the reset routine which is +	 * still in progress. +	 */ + +	fecp->fec_ecntrl = FEC_ECNTRL_PINMUX | FEC_ECNTRL_RESET; +	for (i = 0; +	     (fecp->fec_ecntrl & FEC_ECNTRL_RESET) && (i < FEC_RESET_DELAY); +	     ++i) { +		udelay (1); +	} +	if (i == FEC_RESET_DELAY) +		return -1; + +	return 0; +} + +static int fec_init (struct eth_device *dev, bd_t * bd) +{ +	struct ether_fcc_info_s *efis = dev->priv; +	volatile immap_t *immr = (immap_t *) CONFIG_SYS_IMMR; +	volatile fec_t *fecp = +		(volatile fec_t *) (CONFIG_SYS_IMMR + efis->fecp_offset); +	int i; + +	if (efis->ether_index == 0) { +#if defined(CONFIG_FADS)	/* FADS family uses FPGA (BCSR) to control PHYs */ +#if defined(CONFIG_MPC885ADS) +		*(vu_char *) BCSR5 &= ~(BCSR5_MII1_EN | BCSR5_MII1_RST); +#else +		/* configure FADS for fast (FEC) ethernet, half-duplex */ +		/* The LXT970 needs about 50ms to recover from reset, so +		 * wait for it by discovering the PHY before leaving eth_init(). +		 */ +		{ +			volatile uint *bcsr4 = (volatile uint *) BCSR4; + +			*bcsr4 = (*bcsr4 & ~(BCSR4_FETH_EN | BCSR4_FETHCFG1)) +				| (BCSR4_FETHCFG0 | BCSR4_FETHFDE | +				   BCSR4_FETHRST); + +			/* reset the LXT970 PHY */ +			*bcsr4 &= ~BCSR4_FETHRST; +			udelay (10); +			*bcsr4 |= BCSR4_FETHRST; +			udelay (10); +		} +#endif /* CONFIG_MPC885ADS */ +#endif /* CONFIG_FADS */ +	} + +#if defined(CONFIG_MII) || defined(CONFIG_CMD_MII) +	/* the MII interface is connected to FEC1 +	 * so for the miiphy_xxx function to work we must +	 * call mii_init since fec_halt messes the thing up +	 */ +	if (efis->ether_index != 0) +		__mii_init(); +#endif + +	if (fec_reset(fecp) < 0) +		printf ("FEC_RESET_DELAY timeout\n"); + +	/* We use strictly polling mode only +	 */ +	fecp->fec_imask = 0; + +	/* Clear any pending interrupt +	 */ +	fecp->fec_ievent = 0xffc0; + +	/* No need to set the IVEC register */ + +	/* Set station address +	 */ +#define ea dev->enetaddr +	fecp->fec_addr_low = (ea[0] << 24) | (ea[1] << 16) | (ea[2] << 8) | (ea[3]); +	fecp->fec_addr_high = (ea[4] << 8) | (ea[5]); +#undef ea + +#if defined(CONFIG_CMD_CDP) +	/* +	 * Turn on multicast address hash table +	 */ +	fecp->fec_hash_table_high = 0xffffffff; +	fecp->fec_hash_table_low = 0xffffffff; +#else +	/* Clear multicast address hash table +	 */ +	fecp->fec_hash_table_high = 0; +	fecp->fec_hash_table_low = 0; +#endif + +	/* Set maximum receive buffer size. +	 */ +	fecp->fec_r_buff_size = PKT_MAXBLR_SIZE; + +	/* Set maximum frame length +	 */ +	fecp->fec_r_hash = PKT_MAXBUF_SIZE; + +	/* +	 * Setup Buffers and Buffer Desriptors +	 */ +	rxIdx = 0; +	txIdx = 0; + +	if (!rtx) { +#ifdef CONFIG_SYS_ALLOC_DPRAM +		rtx = (RTXBD *) (immr->im_cpm.cp_dpmem + +				 dpram_alloc_align (sizeof (RTXBD), 8)); +#else +		rtx = (RTXBD *) (immr->im_cpm.cp_dpmem + CPM_FEC_BASE); +#endif +	} +	/* +	 * Setup Receiver Buffer Descriptors (13.14.24.18) +	 * Settings: +	 *     Empty, Wrap +	 */ +	for (i = 0; i < PKTBUFSRX; i++) { +		rtx->rxbd[i].cbd_sc = BD_ENET_RX_EMPTY; +		rtx->rxbd[i].cbd_datlen = 0;	/* Reset */ +		rtx->rxbd[i].cbd_bufaddr = (uint) NetRxPackets[i]; +	} +	rtx->rxbd[PKTBUFSRX - 1].cbd_sc |= BD_ENET_RX_WRAP; + +	/* +	 * Setup Ethernet Transmitter Buffer Descriptors (13.14.24.19) +	 * Settings: +	 *    Last, Tx CRC +	 */ +	for (i = 0; i < TX_BUF_CNT; i++) { +		rtx->txbd[i].cbd_sc = BD_ENET_TX_LAST | BD_ENET_TX_TC; +		rtx->txbd[i].cbd_datlen = 0;	/* Reset */ +		rtx->txbd[i].cbd_bufaddr = (uint) (&txbuf[0]); +	} +	rtx->txbd[TX_BUF_CNT - 1].cbd_sc |= BD_ENET_TX_WRAP; + +	/* Set receive and transmit descriptor base +	 */ +	fecp->fec_r_des_start = (unsigned int) (&rtx->rxbd[0]); +	fecp->fec_x_des_start = (unsigned int) (&rtx->txbd[0]); + +	/* Enable MII mode +	 */ +#if 0				/* Full duplex mode */ +	fecp->fec_r_cntrl = FEC_RCNTRL_MII_MODE; +	fecp->fec_x_cntrl = FEC_TCNTRL_FDEN; +#else  /* Half duplex mode */ +	fecp->fec_r_cntrl = FEC_RCNTRL_MII_MODE | FEC_RCNTRL_DRT; +	fecp->fec_x_cntrl = 0; +#endif + +	/* Enable big endian and don't care about SDMA FC. +	 */ +	fecp->fec_fun_code = 0x78000000; + +	/* +	 * Setup the pin configuration of the FEC +	 */ +	fec_pin_init (efis->ether_index); + +	rxIdx = 0; +	txIdx = 0; + +	/* +	 * Now enable the transmit and receive processing +	 */ +	fecp->fec_ecntrl = FEC_ECNTRL_PINMUX | FEC_ECNTRL_ETHER_EN; + +	if (efis->phy_addr == -1) { +#ifdef CONFIG_SYS_DISCOVER_PHY +		/* +		 * wait for the PHY to wake up after reset +		 */ +		efis->actual_phy_addr = mii_discover_phy (dev); + +		if (efis->actual_phy_addr == -1) { +			printf ("Unable to discover phy!\n"); +			return -1; +		} +#else +		efis->actual_phy_addr = -1; +#endif +	} else { +		efis->actual_phy_addr = efis->phy_addr; +	} + +#if defined(CONFIG_MII) && defined(CONFIG_RMII) +	/* +	 * adapt the RMII speed to the speed of the phy +	 */ +	if (miiphy_speed (dev->name, efis->actual_phy_addr) == _100BASET) { +		fec_100Mbps (dev); +	} else { +		fec_10Mbps (dev); +	} +#endif + +#if defined(CONFIG_MII) +	/* +	 * adapt to the half/full speed settings +	 */ +	if (miiphy_duplex (dev->name, efis->actual_phy_addr) == FULL) { +		fec_full_duplex (dev); +	} else { +		fec_half_duplex (dev); +	} +#endif + +	/* And last, try to fill Rx Buffer Descriptors */ +	fecp->fec_r_des_active = 0x01000000;	/* Descriptor polling active    */ + +	efis->initialized = 1; + +	return 0; +} + + +static void fec_halt(struct eth_device* dev) +{ +	struct ether_fcc_info_s *efis = dev->priv; +	volatile fec_t *fecp = (volatile fec_t *)(CONFIG_SYS_IMMR + efis->fecp_offset); +	int i; + +	/* avoid halt if initialized; mii gets stuck otherwise */ +	if (!efis->initialized) +		return; + +	/* Whack a reset. +	 * A delay is required between a reset of the FEC block and +	 * initialization of other FEC registers because the reset takes +	 * some time to complete. If you don't delay, subsequent writes +	 * to FEC registers might get killed by the reset routine which is +	 * still in progress. +	 */ + +	fecp->fec_ecntrl = FEC_ECNTRL_PINMUX | FEC_ECNTRL_RESET; +	for (i = 0; +	     (fecp->fec_ecntrl & FEC_ECNTRL_RESET) && (i < FEC_RESET_DELAY); +	     ++i) { +		udelay (1); +	} +	if (i == FEC_RESET_DELAY) { +		printf ("FEC_RESET_DELAY timeout\n"); +		return; +	} + +	efis->initialized = 0; +} + +#if defined(CONFIG_SYS_DISCOVER_PHY) || defined(CONFIG_MII) || defined(CONFIG_CMD_MII) + +/* Make MII read/write commands for the FEC. +*/ + +#define mk_mii_read(ADDR, REG)	(0x60020000 | ((ADDR << 23) | \ +						(REG & 0x1f) << 18)) + +#define mk_mii_write(ADDR, REG, VAL)	(0x50020000 | ((ADDR << 23) | \ +						(REG & 0x1f) << 18) | \ +						(VAL & 0xffff)) + +/* Interrupt events/masks. +*/ +#define FEC_ENET_HBERR	((uint)0x80000000)	/* Heartbeat error */ +#define FEC_ENET_BABR	((uint)0x40000000)	/* Babbling receiver */ +#define FEC_ENET_BABT	((uint)0x20000000)	/* Babbling transmitter */ +#define FEC_ENET_GRA	((uint)0x10000000)	/* Graceful stop complete */ +#define FEC_ENET_TXF	((uint)0x08000000)	/* Full frame transmitted */ +#define FEC_ENET_TXB	((uint)0x04000000)	/* A buffer was transmitted */ +#define FEC_ENET_RXF	((uint)0x02000000)	/* Full frame received */ +#define FEC_ENET_RXB	((uint)0x01000000)	/* A buffer was received */ +#define FEC_ENET_MII	((uint)0x00800000)	/* MII interrupt */ +#define FEC_ENET_EBERR	((uint)0x00400000)	/* SDMA bus error */ + +/* PHY identification + */ +#define PHY_ID_LXT970		0x78100000	/* LXT970 */ +#define PHY_ID_LXT971		0x001378e0	/* LXT971 and 972 */ +#define PHY_ID_82555		0x02a80150	/* Intel 82555 */ +#define PHY_ID_QS6612		0x01814400	/* QS6612 */ +#define PHY_ID_AMD79C784	0x00225610	/* AMD 79C784 */ +#define PHY_ID_LSI80225		0x0016f870	/* LSI 80225 */ +#define PHY_ID_LSI80225B	0x0016f880	/* LSI 80225/B */ +#define PHY_ID_DM9161		0x0181B880	/* Davicom DM9161 */ +#define PHY_ID_KSM8995M		0x00221450	/* MICREL KS8995MA */ + +/* send command to phy using mii, wait for result */ +static uint +mii_send(uint mii_cmd) +{ +	uint mii_reply; +	volatile fec_t	*ep; +	int cnt; + +	ep = &(((immap_t *)CONFIG_SYS_IMMR)->im_cpm.cp_fec); + +	ep->fec_mii_data = mii_cmd;	/* command to phy */ + +	/* wait for mii complete */ +	cnt = 0; +	while (!(ep->fec_ievent & FEC_ENET_MII)) { +		if (++cnt > 1000) { +			printf("mii_send STUCK!\n"); +			break; +		} +	} +	mii_reply = ep->fec_mii_data;		/* result from phy */ +	ep->fec_ievent = FEC_ENET_MII;		/* clear MII complete */ +#if 0 +	printf("%s[%d] %s: sent=0x%8.8x, reply=0x%8.8x\n", +		__FILE__,__LINE__,__FUNCTION__,mii_cmd,mii_reply); +#endif +	return (mii_reply & 0xffff);		/* data read from phy */ +} +#endif + +#if defined(CONFIG_SYS_DISCOVER_PHY) +static int mii_discover_phy(struct eth_device *dev) +{ +#define MAX_PHY_PASSES 11 +	uint phyno; +	int  pass; +	uint phytype; +	int phyaddr; + +	phyaddr = -1;	/* didn't find a PHY yet */ +	for (pass = 1; pass <= MAX_PHY_PASSES && phyaddr < 0; ++pass) { +		if (pass > 1) { +			/* PHY may need more time to recover from reset. +			 * The LXT970 needs 50ms typical, no maximum is +			 * specified, so wait 10ms before try again. +			 * With 11 passes this gives it 100ms to wake up. +			 */ +			udelay(10000);	/* wait 10ms */ +		} +		for (phyno = 0; phyno < 32 && phyaddr < 0; ++phyno) { +			phytype = mii_send(mk_mii_read(phyno, PHY_PHYIDR2)); +#ifdef ET_DEBUG +			printf("PHY type 0x%x pass %d type ", phytype, pass); +#endif +			if (phytype != 0xffff) { +				phyaddr = phyno; +				phytype |= mii_send(mk_mii_read(phyno, +								PHY_PHYIDR1)) << 16; + +#ifdef ET_DEBUG +				printf("PHY @ 0x%x pass %d type ",phyno,pass); +				switch (phytype & 0xfffffff0) { +				case PHY_ID_LXT970: +					printf("LXT970\n"); +					break; +				case PHY_ID_LXT971: +					printf("LXT971\n"); +					break; +				case PHY_ID_82555: +					printf("82555\n"); +					break; +				case PHY_ID_QS6612: +					printf("QS6612\n"); +					break; +				case PHY_ID_AMD79C784: +					printf("AMD79C784\n"); +					break; +				case PHY_ID_LSI80225B: +					printf("LSI L80225/B\n"); +					break; +				case PHY_ID_DM9161: +					printf("Davicom DM9161\n"); +					break; +				case PHY_ID_KSM8995M: +					printf("MICREL KS8995M\n"); +					break; +				default: +					printf("0x%08x\n", phytype); +					break; +				} +#endif +			} +		} +	} +	if (phyaddr < 0) { +		printf("No PHY device found.\n"); +	} +	return phyaddr; +} +#endif	/* CONFIG_SYS_DISCOVER_PHY */ + +#if (defined(CONFIG_MII) || defined(CONFIG_CMD_MII)) && !defined(CONFIG_BITBANGMII) + +/**************************************************************************** + * mii_init -- Initialize the MII via FEC 1 for MII command without ethernet + * This function is a subset of eth_init + **************************************************************************** + */ +static void __mii_init(void) +{ +	volatile immap_t *immr = (immap_t *) CONFIG_SYS_IMMR; +	volatile fec_t *fecp = &(immr->im_cpm.cp_fec); + +	if (fec_reset(fecp) < 0) +		printf ("FEC_RESET_DELAY timeout\n"); + +	/* We use strictly polling mode only +	 */ +	fecp->fec_imask = 0; + +	/* Clear any pending interrupt +	 */ +	fecp->fec_ievent = 0xffc0; + +	/* Now enable the transmit and receive processing +	 */ +	fecp->fec_ecntrl = FEC_ECNTRL_PINMUX | FEC_ECNTRL_ETHER_EN; +} + +void mii_init (void) +{ +	int i; + +	__mii_init(); + +	/* Setup the pin configuration of the FEC(s) +	*/ +	for (i = 0; i < sizeof(ether_fcc_info) / sizeof(ether_fcc_info[0]); i++) +		fec_pin_init(ether_fcc_info[i].ether_index); +} + +/***************************************************************************** + * Read and write a MII PHY register, routines used by MII Utilities + * + * FIXME: These routines are expected to return 0 on success, but mii_send + *	  does _not_ return an error code. Maybe 0xFFFF means error, i.e. + *	  no PHY connected... + *	  For now always return 0. + * FIXME: These routines only work after calling eth_init() at least once! + *	  Otherwise they hang in mii_send() !!! Sorry! + *****************************************************************************/ + +int fec8xx_miiphy_read(char *devname, unsigned char addr, +		unsigned char  reg, unsigned short *value) +{ +	short rdreg;    /* register working value */ + +#ifdef MII_DEBUG +	printf ("miiphy_read(0x%x) @ 0x%x = ", reg, addr); +#endif +	rdreg = mii_send(mk_mii_read(addr, reg)); + +	*value = rdreg; +#ifdef MII_DEBUG +	printf ("0x%04x\n", *value); +#endif +	return 0; +} + +int fec8xx_miiphy_write(char *devname, unsigned char  addr, +		unsigned char  reg, unsigned short value) +{ +	short rdreg;    /* register working value */ +#ifdef MII_DEBUG +	printf ("miiphy_write(0x%x) @ 0x%x = ", reg, addr); +#endif +	rdreg = mii_send(mk_mii_write(addr, reg, value)); + +#ifdef MII_DEBUG +	printf ("0x%04x\n", value); +#endif +	return 0; +} +#endif + +#endif diff --git a/arch/powerpc/cpu/mpc8xx/fec.h b/arch/powerpc/cpu/mpc8xx/fec.h new file mode 100644 index 000000000..a49417c66 --- /dev/null +++ b/arch/powerpc/cpu/mpc8xx/fec.h @@ -0,0 +1,28 @@ +/* + * (C) Copyright 2000 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#ifndef	_FEC_H_ +#define	_FEC_H_ + + +#endif	/* _FEC_H_ */ diff --git a/arch/powerpc/cpu/mpc8xx/i2c.c b/arch/powerpc/cpu/mpc8xx/i2c.c new file mode 100644 index 000000000..338cababe --- /dev/null +++ b/arch/powerpc/cpu/mpc8xx/i2c.c @@ -0,0 +1,707 @@ +/* + * (C) Copyright 2000 + * Paolo Scaffardi, AIRVENT SAM s.p.a - RIMINI(ITALY), arsenio@tin.it + * + * (C) Copyright 2000 Sysgo Real-Time Solutions, GmbH <www.elinos.com> + * Marius Groeger <mgroeger@sysgo.de> + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Back ported to the 8xx platform (from the 8260 platform) by + * Murray.Jensen@cmst.csiro.au, 27-Jan-01. + */ + +#include <common.h> + +#ifdef CONFIG_HARD_I2C + +#include <commproc.h> +#include <i2c.h> +#ifdef CONFIG_LWMON +#include <watchdog.h> +#endif + +DECLARE_GLOBAL_DATA_PTR; + +/* define to enable debug messages */ +#undef	DEBUG_I2C + +/* tx/rx timeout (we need the i2c early, so we don't use get_timer()) */ +#define TOUT_LOOP 1000000 + +#define NUM_RX_BDS 4 +#define NUM_TX_BDS 4 +#define MAX_TX_SPACE 256 +#define I2C_RXTX_LEN 128	/* maximum tx/rx buffer length */ + +typedef struct I2C_BD +{ +  unsigned short status; +  unsigned short length; +  unsigned char *addr; +} I2C_BD; +#define BD_I2C_TX_START 0x0400  /* special status for i2c: Start condition */ + +#define BD_I2C_TX_CL	0x0001	/* collision error */ +#define BD_I2C_TX_UN	0x0002	/* underflow error */ +#define BD_I2C_TX_NAK	0x0004	/* no acknowledge error */ +#define BD_I2C_TX_ERR	(BD_I2C_TX_NAK|BD_I2C_TX_UN|BD_I2C_TX_CL) + +#define BD_I2C_RX_ERR	BD_SC_OV + +typedef void (*i2c_ecb_t)(int, int);	/* error callback function */ + +/* This structure keeps track of the bd and buffer space usage. */ +typedef struct i2c_state { +	int		rx_idx;		/* index   to next free Rx BD */ +	int		tx_idx;		/* index   to next free Tx BD */ +	void		*rxbd;		/* pointer to next free Rx BD */ +	void		*txbd;		/* pointer to next free Tx BD */ +	int		tx_space;	/* number  of Tx bytes left   */ +	unsigned char	*tx_buf;	/* pointer to free Tx area    */ +	i2c_ecb_t	err_cb;		/* error callback function    */ +} i2c_state_t; + + +/* flags for i2c_send() and i2c_receive() */ +#define I2CF_ENABLE_SECONDARY	0x01	/* secondary_address is valid		*/ +#define I2CF_START_COND		0x02	/* tx: generate start condition		*/ +#define I2CF_STOP_COND		0x04	/* tx: generate stop  condition		*/ + +/* return codes */ +#define I2CERR_NO_BUFFERS	0x01	/* no more BDs or buffer space		*/ +#define I2CERR_MSG_TOO_LONG	0x02	/* tried to send/receive to much data	*/ +#define I2CERR_TIMEOUT		0x03	/* timeout in i2c_doio()		*/ +#define I2CERR_QUEUE_EMPTY	0x04	/* i2c_doio called without send/receive */ + +/* error callback flags */ +#define I2CECB_RX_ERR		0x10	/* this is a receive error		*/ +#define     I2CECB_RX_ERR_OV	0x02	/* receive overrun error		*/ +#define     I2CECB_RX_MASK	0x0f	/* mask for error bits			*/ +#define I2CECB_TX_ERR		0x20	/* this is a transmit error		*/ +#define     I2CECB_TX_CL	0x01	/* transmit collision error		*/ +#define     I2CECB_TX_UN	0x02	/* transmit underflow error		*/ +#define     I2CECB_TX_NAK	0x04	/* transmit no ack error		*/ +#define     I2CECB_TX_MASK	0x0f	/* mask for error bits			*/ +#define I2CECB_TIMEOUT		0x40	/* this is a timeout error		*/ + +#ifdef DEBUG_I2C +#define PRINTD(x) printf x +#else +#define PRINTD(x) +#endif + +/* + * Returns the best value of I2BRG to meet desired clock speed of I2C with + * input parameters (clock speed, filter, and predivider value). + * It returns computer speed value and the difference between it and desired + * speed. + */ +static inline int +i2c_roundrate(int hz, int speed, int filter, int modval, +		int *brgval, int *totspeed) +{ +    int moddiv = 1 << (5-(modval & 3)), brgdiv, div; + +    PRINTD(("\t[I2C] trying hz=%d, speed=%d, filter=%d, modval=%d\n", +	hz, speed, filter, modval)); + +    div = moddiv * speed; +    brgdiv = (hz + div - 1) / div; + +    PRINTD(("\t\tmoddiv=%d, brgdiv=%d\n", moddiv, brgdiv)); + +    *brgval = ((brgdiv + 1) / 2) - 3 - (2*filter); + +    if ((*brgval < 0) || (*brgval > 255)) { +	  PRINTD(("\t\trejected brgval=%d\n", *brgval)); +	  return -1; +    } + +    brgdiv = 2 * (*brgval + 3 + (2 * filter)); +    div = moddiv * brgdiv ; +    *totspeed = hz / div; + +    PRINTD(("\t\taccepted brgval=%d, totspeed=%d\n", *brgval, *totspeed)); + +    return  0; +} + +/* + * Sets the I2C clock predivider and divider to meet required clock speed. + */ +static int +i2c_setrate (int hz, int speed) +{ +	immap_t		*immap = (immap_t *) CONFIG_SYS_IMMR; +	volatile i2c8xx_t *i2c = (i2c8xx_t *) & immap->im_i2c; +	int		brgval, +			modval,		/* 0-3 */ +			bestspeed_diff = speed, +			bestspeed_brgval = 0, +			bestspeed_modval = 0, +			bestspeed_filter = 0, +			totspeed, +			filter = 0;	/* Use this fixed value */ + +	for (modval = 0; modval < 4; modval++) { +		if (i2c_roundrate(hz,speed,filter,modval,&brgval,&totspeed) == 0) { +			int diff = speed - totspeed; + +			if ((diff >= 0) && (diff < bestspeed_diff)) { +				bestspeed_diff = diff; +				bestspeed_modval = modval; +				bestspeed_brgval = brgval; +				bestspeed_filter = filter; +			} +		} +	} + +	PRINTD (("[I2C] Best is:\n")); +	PRINTD (("[I2C] CPU=%dhz RATE=%d F=%d I2MOD=%08x I2BRG=%08x DIFF=%dhz\n", +		hz, +		speed, +		bestspeed_filter, +		bestspeed_modval, +		bestspeed_brgval, +		bestspeed_diff)); + +	i2c->i2c_i2mod |= ((bestspeed_modval & 3) << 1) | (bestspeed_filter << 3); +	i2c->i2c_i2brg = bestspeed_brgval & 0xff; + +	PRINTD (("[I2C] i2mod=%08x i2brg=%08x\n", i2c->i2c_i2mod, +			 i2c->i2c_i2brg)); + +	return 1; +} + +void +i2c_init(int speed, int slaveaddr) +{ +	volatile immap_t *immap = (immap_t *)CONFIG_SYS_IMMR ; +	volatile cpm8xx_t *cp = (cpm8xx_t *)&immap->im_cpm; +	volatile i2c8xx_t *i2c	= (i2c8xx_t *)&immap->im_i2c; +	volatile iic_t *iip = (iic_t *)&cp->cp_dparam[PROFF_IIC]; +	ulong rbase, tbase; +	volatile I2C_BD *rxbd, *txbd; +	uint dpaddr; + +#ifdef CONFIG_SYS_I2C_INIT_BOARD +	/* call board specific i2c bus reset routine before accessing the   */ +	/* environment, which might be in a chip on that bus. For details   */ +	/* about this problem see doc/I2C_Edge_Conditions.                  */ +	i2c_init_board(); +#endif + +#ifdef CONFIG_SYS_I2C_UCODE_PATCH +	iip = (iic_t *)&cp->cp_dpmem[iip->iic_rpbase]; +#else +	/* Disable relocation */ +	iip->iic_rpbase = 0; +#endif + +#ifdef CONFIG_SYS_ALLOC_DPRAM +	dpaddr = iip->iic_rbase; +	if (dpaddr == 0) { +	    /* need to allocate dual port ram */ +	    dpaddr = dpram_alloc_align( +		(NUM_RX_BDS * sizeof(I2C_BD)) + (NUM_TX_BDS * sizeof(I2C_BD)) + +		MAX_TX_SPACE, 8); +	} +#else +	dpaddr = CPM_I2C_BASE; +#endif + +	/* +	 * initialise data in dual port ram: +	 * +	 * dpaddr->rbase -> rx BD         (NUM_RX_BDS * sizeof(I2C_BD) bytes) +	 *         tbase -> tx BD         (NUM_TX_BDS * sizeof(I2C_BD) bytes) +	 *                  tx buffer     (MAX_TX_SPACE bytes) +	 */ + +	rbase = dpaddr; +	tbase = rbase + NUM_RX_BDS * sizeof(I2C_BD); + +	/* Initialize Port B I2C pins. */ +	cp->cp_pbpar |= 0x00000030; +	cp->cp_pbdir |= 0x00000030; +	cp->cp_pbodr |= 0x00000030; + +	/* Disable interrupts */ +	i2c->i2c_i2mod = 0x00; +	i2c->i2c_i2cmr = 0x00; +	i2c->i2c_i2cer = 0xff; +	i2c->i2c_i2add = slaveaddr; + +	/* +	 * Set the I2C BRG Clock division factor from desired i2c rate +	 * and current CPU rate (we assume sccr dfbgr field is 0; +	 * divide BRGCLK by 1) +	 */ +	PRINTD(("[I2C] Setting rate...\n")); +	i2c_setrate (gd->cpu_clk, CONFIG_SYS_I2C_SPEED) ; + +	/* Set I2C controller in master mode */ +	i2c->i2c_i2com = 0x01; + +	/* Set SDMA bus arbitration level to 5 (SDCR) */ +	immap->im_siu_conf.sc_sdcr = 0x0001 ; + +	/* Initialize Tx/Rx parameters */ +	iip->iic_rbase = rbase; +	iip->iic_tbase = tbase; +	rxbd = (I2C_BD *)((unsigned char *)&cp->cp_dpmem[iip->iic_rbase]); +	txbd = (I2C_BD *)((unsigned char *)&cp->cp_dpmem[iip->iic_tbase]); + +	PRINTD(("[I2C] rbase = %04x\n", iip->iic_rbase)); +	PRINTD(("[I2C] tbase = %04x\n", iip->iic_tbase)); +	PRINTD(("[I2C] rxbd = %08x\n", (int)rxbd)); +	PRINTD(("[I2C] txbd = %08x\n", (int)txbd)); + +	/* Set big endian byte order */ +	iip->iic_tfcr = 0x10; +	iip->iic_rfcr = 0x10; + +	/* Set maximum receive size. */ +	iip->iic_mrblr = I2C_RXTX_LEN; + +#ifdef CONFIG_SYS_I2C_UCODE_PATCH +	/* +	 *  Initialize required parameters if using microcode patch. +	 */ +	iip->iic_rbptr  = iip->iic_rbase; +	iip->iic_tbptr  = iip->iic_tbase; +	iip->iic_rstate = 0; +	iip->iic_tstate = 0; +#else +	cp->cp_cpcr = mk_cr_cmd(CPM_CR_CH_I2C, CPM_CR_INIT_TRX) | CPM_CR_FLG; +	do { +		__asm__ __volatile__ ("eieio"); +	} while (cp->cp_cpcr & CPM_CR_FLG); +#endif + +	/* Clear events and interrupts */ +	i2c->i2c_i2cer = 0xff; +	i2c->i2c_i2cmr = 0x00; +} + +static void +i2c_newio(i2c_state_t *state) +{ +	volatile immap_t *immap = (immap_t *)CONFIG_SYS_IMMR ; +	volatile cpm8xx_t *cp = (cpm8xx_t *)&immap->im_cpm; +	volatile iic_t *iip = (iic_t *)&cp->cp_dparam[PROFF_IIC]; + +	PRINTD(("[I2C] i2c_newio\n")); + +#ifdef CONFIG_SYS_I2C_UCODE_PATCH +	iip = (iic_t *)&cp->cp_dpmem[iip->iic_rpbase]; +#endif +	state->rx_idx = 0; +	state->tx_idx = 0; +	state->rxbd = (void*)&cp->cp_dpmem[iip->iic_rbase]; +	state->txbd = (void*)&cp->cp_dpmem[iip->iic_tbase]; +	state->tx_space = MAX_TX_SPACE; +	state->tx_buf = (uchar*)state->txbd + NUM_TX_BDS * sizeof(I2C_BD); +	state->err_cb = NULL; + +	PRINTD(("[I2C] rxbd = %08x\n", (int)state->rxbd)); +	PRINTD(("[I2C] txbd = %08x\n", (int)state->txbd)); +	PRINTD(("[I2C] tx_buf = %08x\n", (int)state->tx_buf)); + +	/* clear the buffer memory */ +	memset((char *)state->tx_buf, 0, MAX_TX_SPACE); +} + +static int +i2c_send(i2c_state_t *state, +	 unsigned char address, +	 unsigned char secondary_address, +	 unsigned int flags, +	 unsigned short size, +	 unsigned char *dataout) +{ +	volatile I2C_BD *txbd; +	int i,j; + +	PRINTD(("[I2C] i2c_send add=%02d sec=%02d flag=%02d size=%d\n", +			address, secondary_address, flags, size)); + +	/* trying to send message larger than BD */ +	if (size > I2C_RXTX_LEN) +	  return I2CERR_MSG_TOO_LONG; + +	/* no more free bds */ +	if (state->tx_idx >= NUM_TX_BDS || state->tx_space < (2 + size)) +	  return I2CERR_NO_BUFFERS; + +	txbd = (I2C_BD *)state->txbd; +	txbd->addr = state->tx_buf; + +	PRINTD(("[I2C] txbd = %08x\n", (int)txbd)); + +	if (flags & I2CF_START_COND) { +		PRINTD(("[I2C] Formatting addresses...\n")); +		if (flags & I2CF_ENABLE_SECONDARY) { +			txbd->length = size + 2;  /* Length of msg + dest addr */ +			txbd->addr[0] = address << 1; +			txbd->addr[1] = secondary_address; +			i = 2; +		} else { +			txbd->length = size + 1;  /* Length of msg + dest addr */ +			txbd->addr[0] = address << 1;  /* Write dest addr to BD */ +			i = 1; +		} +	} else { +		txbd->length = size;  /* Length of message */ +		i = 0; +	} + +	/* set up txbd */ +	txbd->status = BD_SC_READY; +	if (flags & I2CF_START_COND) +	  txbd->status |= BD_I2C_TX_START; +	if (flags & I2CF_STOP_COND) +	  txbd->status |= BD_SC_LAST | BD_SC_WRAP; + +	/* Copy data to send into buffer */ +	PRINTD(("[I2C] copy data...\n")); +	for(j = 0; j < size; i++, j++) +	  txbd->addr[i] = dataout[j]; + +	PRINTD(("[I2C] txbd: length=0x%04x status=0x%04x addr[0]=0x%02x addr[1]=0x%02x\n", +		   txbd->length, +		   txbd->status, +		   txbd->addr[0], +		   txbd->addr[1])); + +	/* advance state */ +	state->tx_buf += txbd->length; +	state->tx_space -= txbd->length; +	state->tx_idx++; +	state->txbd = (void*)(txbd + 1); + +	return 0; +} + +static int +i2c_receive(i2c_state_t *state, +	    unsigned char address, +	    unsigned char secondary_address, +	    unsigned int flags, +	    unsigned short size_to_expect, +	    unsigned char *datain) +{ +	volatile I2C_BD *rxbd, *txbd; + +	PRINTD(("[I2C] i2c_receive %02d %02d %02d\n", address, secondary_address, flags)); + +	/* Expected to receive too much */ +	if (size_to_expect > I2C_RXTX_LEN) +	  return I2CERR_MSG_TOO_LONG; + +	/* no more free bds */ +	if (state->tx_idx >= NUM_TX_BDS || state->rx_idx >= NUM_RX_BDS +		 || state->tx_space < 2) +	  return I2CERR_NO_BUFFERS; + +	rxbd = (I2C_BD *)state->rxbd; +	txbd = (I2C_BD *)state->txbd; + +	PRINTD(("[I2C] rxbd = %08x\n", (int)rxbd)); +	PRINTD(("[I2C] txbd = %08x\n", (int)txbd)); + +	txbd->addr = state->tx_buf; + +	/* set up TXBD for destination address */ +	if (flags & I2CF_ENABLE_SECONDARY) { +		txbd->length = 2; +		txbd->addr[0] = address << 1;   /* Write data */ +		txbd->addr[1] = secondary_address;  /* Internal address */ +		txbd->status = BD_SC_READY; +	} else { +		txbd->length = 1 + size_to_expect; +		txbd->addr[0] = (address << 1) | 0x01; +		txbd->status = BD_SC_READY; +		memset(&txbd->addr[1], 0, txbd->length); +	} + +	/* set up rxbd for reception */ +	rxbd->status = BD_SC_EMPTY; +	rxbd->length = size_to_expect; +	rxbd->addr = datain; + +	txbd->status |= BD_I2C_TX_START; +	if (flags & I2CF_STOP_COND) { +		txbd->status |= BD_SC_LAST | BD_SC_WRAP; +		rxbd->status |= BD_SC_WRAP; +	} + +	PRINTD(("[I2C] txbd: length=0x%04x status=0x%04x addr[0]=0x%02x addr[1]=0x%02x\n", +		   txbd->length, +		   txbd->status, +		   txbd->addr[0], +		   txbd->addr[1])); +	PRINTD(("[I2C] rxbd: length=0x%04x status=0x%04x addr[0]=0x%02x addr[1]=0x%02x\n", +		   rxbd->length, +		   rxbd->status, +		   rxbd->addr[0], +		   rxbd->addr[1])); + +	/* advance state */ +	state->tx_buf += txbd->length; +	state->tx_space -= txbd->length; +	state->tx_idx++; +	state->txbd = (void*)(txbd + 1); +	state->rx_idx++; +	state->rxbd = (void*)(rxbd + 1); + +	return 0; +} + + +static int i2c_doio(i2c_state_t *state) +{ +	volatile immap_t *immap = (immap_t *)CONFIG_SYS_IMMR ; +	volatile cpm8xx_t *cp = (cpm8xx_t *)&immap->im_cpm; +	volatile i2c8xx_t *i2c	= (i2c8xx_t *)&immap->im_i2c; +	volatile iic_t *iip = (iic_t *)&cp->cp_dparam[PROFF_IIC]; +	volatile I2C_BD *txbd, *rxbd; +	volatile int j = 0; + +	PRINTD(("[I2C] i2c_doio\n")); + +#ifdef CONFIG_SYS_I2C_UCODE_PATCH +	iip = (iic_t *)&cp->cp_dpmem[iip->iic_rpbase]; +#endif + +	if (state->tx_idx <= 0 && state->rx_idx <= 0) { +		PRINTD(("[I2C] No I/O is queued\n")); +		return I2CERR_QUEUE_EMPTY; +	} + +	iip->iic_rbptr = iip->iic_rbase; +	iip->iic_tbptr = iip->iic_tbase; + +	/* Enable I2C */ +	PRINTD(("[I2C] Enabling I2C...\n")); +	i2c->i2c_i2mod |= 0x01; + +	/* Begin transmission */ +	i2c->i2c_i2com |= 0x80; + +	/* Loop until transmit & receive completed */ + +	if (state->tx_idx > 0) { +		txbd = ((I2C_BD*)state->txbd) - 1; +		PRINTD(("[I2C] Transmitting...(txbd=0x%08lx)\n", (ulong)txbd)); +		while((txbd->status & BD_SC_READY) && (j++ < TOUT_LOOP)) { +			if (ctrlc()) { +				return (-1); +			} +			__asm__ __volatile__ ("eieio"); +		} +	} + +	if ((state->rx_idx > 0) && (j < TOUT_LOOP)) { +		rxbd = ((I2C_BD*)state->rxbd) - 1; +		PRINTD(("[I2C] Receiving...(rxbd=0x%08lx)\n", (ulong)rxbd)); +		while((rxbd->status & BD_SC_EMPTY) && (j++ < TOUT_LOOP)) { +			if (ctrlc()) { +				return (-1); +			} +			__asm__ __volatile__ ("eieio"); +		} +	} + +	/* Turn off I2C */ +	i2c->i2c_i2mod &= ~0x01; + +	if (state->err_cb != NULL) { +		int n, i, b; + +		/* +		 * if we have an error callback function, look at the +		 * error bits in the bd status and pass them back +		 */ + +		if ((n = state->tx_idx) > 0) { +			for (i = 0; i < n; i++) { +				txbd = ((I2C_BD*)state->txbd) - (n - i); +				if ((b = txbd->status & BD_I2C_TX_ERR) != 0) +					(*state->err_cb)(I2CECB_TX_ERR|b, i); +			} +		} + +		if ((n = state->rx_idx) > 0) { +			for (i = 0; i < n; i++) { +				rxbd = ((I2C_BD*)state->rxbd) - (n - i); +				if ((b = rxbd->status & BD_I2C_RX_ERR) != 0) +					(*state->err_cb)(I2CECB_RX_ERR|b, i); +			} +		} + +		if (j >= TOUT_LOOP) +			(*state->err_cb)(I2CECB_TIMEOUT, 0); +	} + +	return (j >= TOUT_LOOP) ? I2CERR_TIMEOUT : 0; +} + +static int had_tx_nak; + +static void +i2c_test_callback(int flags, int xnum) +{ +	if ((flags & I2CECB_TX_ERR) && (flags & I2CECB_TX_NAK)) +		had_tx_nak = 1; +} + +int i2c_probe(uchar chip) +{ +	i2c_state_t state; +	int rc; +	uchar buf[1]; + +	i2c_init(CONFIG_SYS_I2C_SPEED, CONFIG_SYS_I2C_SLAVE); + +	i2c_newio(&state); + +	state.err_cb = i2c_test_callback; +	had_tx_nak = 0; + +	rc = i2c_receive(&state, chip, 0, I2CF_START_COND|I2CF_STOP_COND, 1, buf); + +	if (rc != 0) +		return (rc); + +	rc = i2c_doio(&state); + +	if ((rc != 0) && (rc != I2CERR_TIMEOUT)) +		return (rc); + +	return (had_tx_nak); +} + +int i2c_read(uchar chip, uint addr, int alen, uchar *buffer, int len) +{ +	i2c_state_t state; +	uchar xaddr[4]; +	int rc; + +#ifdef CONFIG_LWMON +	WATCHDOG_RESET(); +#endif + +	xaddr[0] = (addr >> 24) & 0xFF; +	xaddr[1] = (addr >> 16) & 0xFF; +	xaddr[2] = (addr >>  8) & 0xFF; +	xaddr[3] =  addr        & 0xFF; + +#ifdef CONFIG_SYS_I2C_EEPROM_ADDR_OVERFLOW +	/* +	 * EEPROM chips that implement "address overflow" are ones like +	 * Catalyst 24WC04/08/16 which has 9/10/11 bits of address and the +	 * extra bits end up in the "chip address" bit slots.  This makes +	 * a 24WC08 (1Kbyte) chip look like four 256 byte chips. +	 * +	 * Note that we consider the length of the address field to still +	 * be one byte because the extra address bits are hidden in the +	 * chip address. +	 */ +	 chip |= ((addr >> (alen * 8)) & CONFIG_SYS_I2C_EEPROM_ADDR_OVERFLOW); +#endif + +	i2c_newio(&state); + +	rc = i2c_send(&state, chip, 0, I2CF_START_COND, alen, &xaddr[4-alen]); +	if (rc != 0) { +		if (gd->have_console) +			printf("i2c_read: i2c_send failed (%d)\n", rc); +		return 1; +	} + +	rc = i2c_receive(&state, chip, 0, I2CF_STOP_COND, len, buffer); +	if (rc != 0) { +		if (gd->have_console) +			printf("i2c_read: i2c_receive failed (%d)\n", rc); +		return 1; +	} + +	rc = i2c_doio(&state); +	if (rc != 0) { +		if (gd->have_console) +			printf("i2c_read: i2c_doio failed (%d)\n", rc); +		return 1; +	} +	return 0; +} + +int i2c_write(uchar chip, uint addr, int alen, uchar *buffer, int len) +{ +	i2c_state_t state; +	uchar xaddr[4]; +	int rc; + +	xaddr[0] = (addr >> 24) & 0xFF; +	xaddr[1] = (addr >> 16) & 0xFF; +	xaddr[2] = (addr >>  8) & 0xFF; +	xaddr[3] =  addr        & 0xFF; + +#ifdef CONFIG_SYS_I2C_EEPROM_ADDR_OVERFLOW +	/* +	 * EEPROM chips that implement "address overflow" are ones like +	 * Catalyst 24WC04/08/16 which has 9/10/11 bits of address and the +	 * extra bits end up in the "chip address" bit slots.  This makes +	 * a 24WC08 (1Kbyte) chip look like four 256 byte chips. +	 * +	 * Note that we consider the length of the address field to still +	 * be one byte because the extra address bits are hidden in the +	 * chip address. +	 */ +	 chip |= ((addr >> (alen * 8)) & CONFIG_SYS_I2C_EEPROM_ADDR_OVERFLOW); +#endif + +	i2c_newio(&state); + +	rc = i2c_send(&state, chip, 0, I2CF_START_COND, alen, &xaddr[4-alen]); +	if (rc != 0) { +		if (gd->have_console) +			printf("i2c_write: first i2c_send failed (%d)\n", rc); +		return 1; +	} + +	rc = i2c_send(&state, 0, 0, I2CF_STOP_COND, len, buffer); +	if (rc != 0) { +		if (gd->have_console) +			printf("i2c_write: second i2c_send failed (%d)\n", rc); +		return 1; +	} + +	rc = i2c_doio(&state); +	if (rc != 0) { +		if (gd->have_console) +			printf("i2c_write: i2c_doio failed (%d)\n", rc); +		return 1; +	} +	return 0; +} + +#endif	/* CONFIG_HARD_I2C */ diff --git a/arch/powerpc/cpu/mpc8xx/interrupts.c b/arch/powerpc/cpu/mpc8xx/interrupts.c new file mode 100644 index 000000000..5daa6b275 --- /dev/null +++ b/arch/powerpc/cpu/mpc8xx/interrupts.c @@ -0,0 +1,294 @@ +/* + * (C) Copyright 2000-2002 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include <common.h> +#include <mpc8xx.h> +#include <mpc8xx_irq.h> +#include <asm/processor.h> +#include <commproc.h> + +/************************************************************************/ + +/* + * CPM interrupt vector functions. + */ +struct interrupt_action { +	interrupt_handler_t *handler; +	void *arg; +}; + +static struct interrupt_action cpm_vecs[CPMVEC_NR]; +static struct interrupt_action irq_vecs[NR_IRQS]; + +static void cpm_interrupt_init (void); +static void cpm_interrupt (void *regs); + +/************************************************************************/ + +int interrupt_init_cpu (unsigned *decrementer_count) +{ +	volatile immap_t *immr = (immap_t *) CONFIG_SYS_IMMR; + +	*decrementer_count = get_tbclk () / CONFIG_SYS_HZ; + +	/* disable all interrupts */ +	immr->im_siu_conf.sc_simask = 0; + +	/* Configure CPM interrupts */ +	cpm_interrupt_init (); + +	return (0); +} + +/************************************************************************/ + +/* + * Handle external interrupts + */ +void external_interrupt (struct pt_regs *regs) +{ +	volatile immap_t *immr = (immap_t *) CONFIG_SYS_IMMR; +	int irq; +	ulong simask, newmask; +	ulong vec, v_bit; + +	/* +	 * read the SIVEC register and shift the bits down +	 * to get the irq number +	 */ +	vec = immr->im_siu_conf.sc_sivec; +	irq = vec >> 26; +	v_bit = 0x80000000UL >> irq; + +	/* +	 * Read Interrupt Mask Register and Mask Interrupts +	 */ +	simask = immr->im_siu_conf.sc_simask; +	newmask = simask & (~(0xFFFF0000 >> irq)); +	immr->im_siu_conf.sc_simask = newmask; + +	if (!(irq & 0x1)) {		/* External Interrupt ?     */ +		ulong siel; + +		/* +		 * Read Interrupt Edge/Level Register +		 */ +		siel = immr->im_siu_conf.sc_siel; + +		if (siel & v_bit) {	/* edge triggered interrupt ?   */ +			/* +			 * Rewrite SIPEND Register to clear interrupt +			 */ +			immr->im_siu_conf.sc_sipend = v_bit; +		} +	} + +	if (irq_vecs[irq].handler != NULL) { +		irq_vecs[irq].handler (irq_vecs[irq].arg); +	} else { +		printf ("\nBogus External Interrupt IRQ %d Vector %ld\n", +				irq, vec); +		/* turn off the bogus interrupt to avoid it from now */ +		simask &= ~v_bit; +	} +	/* +	 * Re-Enable old Interrupt Mask +	 */ +	immr->im_siu_conf.sc_simask = simask; +} + +/************************************************************************/ + +/* + * CPM interrupt handler + */ +static void cpm_interrupt (void *regs) +{ +	volatile immap_t *immr = (immap_t *) CONFIG_SYS_IMMR; +	uint vec; + +	/* +	 * Get the vector by setting the ACK bit +	 * and then reading the register. +	 */ +	immr->im_cpic.cpic_civr = 1; +	vec = immr->im_cpic.cpic_civr; +	vec >>= 11; + +	if (cpm_vecs[vec].handler != NULL) { +		(*cpm_vecs[vec].handler) (cpm_vecs[vec].arg); +	} else { +		immr->im_cpic.cpic_cimr &= ~(1 << vec); +		printf ("Masking bogus CPM interrupt vector 0x%x\n", vec); +	} +	/* +	 * After servicing the interrupt, +	 * we have to remove the status indicator. +	 */ +	immr->im_cpic.cpic_cisr |= (1 << vec); +} + +/* + * The CPM can generate the error interrupt when there is a race + * condition between generating and masking interrupts. All we have + * to do is ACK it and return. This is a no-op function so we don't + * need any special tests in the interrupt handler. + */ +static void cpm_error_interrupt (void *dummy) +{ +} + +/************************************************************************/ +/* + * Install and free an interrupt handler + */ +void irq_install_handler (int vec, interrupt_handler_t * handler, +						  void *arg) +{ +	volatile immap_t *immr = (immap_t *) CONFIG_SYS_IMMR; + +	if ((vec & CPMVEC_OFFSET) != 0) { +		/* CPM interrupt */ +		vec &= 0xffff; +		if (cpm_vecs[vec].handler != NULL) { +			printf ("CPM interrupt 0x%x replacing 0x%x\n", +				(uint) handler, +				(uint) cpm_vecs[vec].handler); +		} +		cpm_vecs[vec].handler = handler; +		cpm_vecs[vec].arg = arg; +		immr->im_cpic.cpic_cimr |= (1 << vec); +#if 0 +		printf ("Install CPM interrupt for vector %d ==> %p\n", +			vec, handler); +#endif +	} else { +		/* SIU interrupt */ +		if (irq_vecs[vec].handler != NULL) { +			printf ("SIU interrupt %d 0x%x replacing 0x%x\n", +				vec, +				(uint) handler, +				(uint) cpm_vecs[vec].handler); +		} +		irq_vecs[vec].handler = handler; +		irq_vecs[vec].arg = arg; +		immr->im_siu_conf.sc_simask |= 1 << (31 - vec); +#if 0 +		printf ("Install SIU interrupt for vector %d ==> %p\n", +			vec, handler); +#endif +	} +} + +void irq_free_handler (int vec) +{ +	volatile immap_t *immr = (immap_t *) CONFIG_SYS_IMMR; + +	if ((vec & CPMVEC_OFFSET) != 0) { +		/* CPM interrupt */ +		vec &= 0xffff; +#if 0 +		printf ("Free CPM interrupt for vector %d ==> %p\n", +			vec, cpm_vecs[vec].handler); +#endif +		immr->im_cpic.cpic_cimr &= ~(1 << vec); +		cpm_vecs[vec].handler = NULL; +		cpm_vecs[vec].arg = NULL; +	} else { +		/* SIU interrupt */ +#if 0 +		printf ("Free CPM interrupt for vector %d ==> %p\n", +			vec, cpm_vecs[vec].handler); +#endif +		immr->im_siu_conf.sc_simask &= ~(1 << (31 - vec)); +		irq_vecs[vec].handler = NULL; +		irq_vecs[vec].arg = NULL; +	} +} + +/************************************************************************/ + +static void cpm_interrupt_init (void) +{ +	volatile immap_t *immr = (immap_t *) CONFIG_SYS_IMMR; + +	/* +	 * Initialize the CPM interrupt controller. +	 */ + +	immr->im_cpic.cpic_cicr = +		(CICR_SCD_SCC4 | +		 CICR_SCC_SCC3 | +		 CICR_SCB_SCC2 | +		 CICR_SCA_SCC1) | ((CPM_INTERRUPT / 2) << 13) | CICR_HP_MASK; + +	immr->im_cpic.cpic_cimr = 0; + +	/* +	 * Install the error handler. +	 */ +	irq_install_handler (CPMVEC_ERROR, cpm_error_interrupt, NULL); + +	immr->im_cpic.cpic_cicr |= CICR_IEN; + +	/* +	 * Install the cpm interrupt handler +	 */ +	irq_install_handler (CPM_INTERRUPT, cpm_interrupt, NULL); +} + +/************************************************************************/ + +/* + * timer_interrupt - gets called when the decrementer overflows, + * with interrupts disabled. + * Trivial implementation - no need to be really accurate. + */ +void timer_interrupt_cpu (struct pt_regs *regs) +{ +	volatile immap_t *immr = (immap_t *) CONFIG_SYS_IMMR; + +#if 0 +	printf ("*** Timer Interrupt *** "); +#endif +	/* Reset Timer Expired and Timers Interrupt Status */ +	immr->im_clkrstk.cark_plprcrk = KAPWR_KEY; +	__asm__ ("nop"); +	/* +	  Clear TEXPS (and TMIST on older chips). SPLSS (on older +	  chips) is cleared too. + +	  Bitwise OR is a read-modify-write operation so ALL bits +	  which are cleared by writing `1' would be cleared by +	  operations like + +	  immr->im_clkrst.car_plprcr |= PLPRCR_TEXPS; + +	  The same can be achieved by simple writing of the PLPRCR +	  to itself. If a bit value should be preserved, read the +	  register, ZERO the bit and write, not OR, the result back. +	*/ +	immr->im_clkrst.car_plprcr = immr->im_clkrst.car_plprcr; +} + +/************************************************************************/ diff --git a/arch/powerpc/cpu/mpc8xx/kgdb.S b/arch/powerpc/cpu/mpc8xx/kgdb.S new file mode 100644 index 000000000..2cc8fe63c --- /dev/null +++ b/arch/powerpc/cpu/mpc8xx/kgdb.S @@ -0,0 +1,74 @@ +/* + *  Copyright (C) 2000	Murray Jensen <Murray.Jensen@cmst.csiro.au> + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include <config.h> +#include <command.h> +#include <mpc8xx.h> +#include <version.h> + +#define CONFIG_8xx 1		/* needed for Linux kernel header files */ +#define _LINUX_CONFIG_H 1	/* avoid reading Linux autoconf.h file	*/ + +#include <ppc_asm.tmpl> +#include <ppc_defs.h> + +#include <asm/cache.h> +#include <asm/mmu.h> + +#if defined(CONFIG_CMD_KGDB) + + /* + * cache flushing routines for kgdb + */ + +	.globl	kgdb_flush_cache_all +kgdb_flush_cache_all: +	lis	r3, IDC_INVALL@h +	mtspr	DC_CST, r3 +	sync +	lis	r3, IDC_INVALL@h +	mtspr	IC_CST, r3 +	SYNC +	blr + +	.globl	kgdb_flush_cache_range +kgdb_flush_cache_range: +	li	r5,CONFIG_SYS_CACHELINE_SIZE-1 +	andc	r3,r3,r5 +	subf	r4,r3,r4 +	add	r4,r4,r5 +	srwi.	r4,r4,CONFIG_SYS_CACHELINE_SHIFT +	beqlr +	mtctr	r4 +	mr	r6,r3 +1:	dcbst	0,r3 +	addi	r3,r3,CONFIG_SYS_CACHELINE_SIZE +	bdnz	1b +	sync				/* wait for dcbst's to get to ram */ +	mtctr	r4 +2:	icbi	0,r6 +	addi	r6,r6,CONFIG_SYS_CACHELINE_SIZE +	bdnz	2b +	SYNC +	blr + +#endif diff --git a/arch/powerpc/cpu/mpc8xx/lcd.c b/arch/powerpc/cpu/mpc8xx/lcd.c new file mode 100644 index 000000000..4b88b21b3 --- /dev/null +++ b/arch/powerpc/cpu/mpc8xx/lcd.c @@ -0,0 +1,621 @@ +/* + * (C) Copyright 2001-2002 + * Wolfgang Denk, DENX Software Engineering -- wd@denx.de + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +/************************************************************************/ +/* ** HEADER FILES							*/ +/************************************************************************/ + +/* #define DEBUG */ + +#include <config.h> +#include <common.h> +#include <command.h> +#include <watchdog.h> +#include <version.h> +#include <stdarg.h> +#include <lcdvideo.h> +#include <linux/types.h> +#include <stdio_dev.h> +#if defined(CONFIG_POST) +#include <post.h> +#endif +#include <lcd.h> + +#ifdef CONFIG_LCD + +/************************************************************************/ +/* ** CONFIG STUFF -- should be moved to board config file		*/ +/************************************************************************/ +#ifndef CONFIG_LCD_INFO +#define CONFIG_LCD_INFO		/* Display Logo, (C) and system info	*/ +#endif + +#if defined(CONFIG_V37) || defined(CONFIG_EDT32F10) +#undef CONFIG_LCD_LOGO +#undef CONFIG_LCD_INFO +#endif + +/*----------------------------------------------------------------------*/ +#ifdef CONFIG_KYOCERA_KCS057QV1AJ +/* + *  Kyocera KCS057QV1AJ-G23. Passive, color, single scan. + */ +#define LCD_BPP	LCD_COLOR4 + +vidinfo_t panel_info = { +    640, 480, 132, 99, CONFIG_SYS_HIGH, CONFIG_SYS_HIGH, CONFIG_SYS_HIGH, CONFIG_SYS_HIGH, CONFIG_SYS_HIGH, +    LCD_BPP, 1, 0, 1, 0,  5, 0, 0, 0 +		/* wbl, vpw, lcdac, wbf */ +}; +#endif /* CONFIG_KYOCERA_KCS057QV1AJ */ +/*----------------------------------------------------------------------*/ + +/*----------------------------------------------------------------------*/ +#ifdef CONFIG_HITACHI_SP19X001_Z1A +/* + *  Hitachi SP19X001-. Active, color, single scan. + */ +vidinfo_t panel_info = { +    640, 480, 154, 116, CONFIG_SYS_HIGH, CONFIG_SYS_HIGH, CONFIG_SYS_HIGH, CONFIG_SYS_HIGH, CONFIG_SYS_HIGH, +    LCD_COLOR8, 1, 0, 1, 0, 0, 0, 0, 0 +		/* wbl, vpw, lcdac, wbf */ +}; +#endif /* CONFIG_HITACHI_SP19X001_Z1A */ +/*----------------------------------------------------------------------*/ + +/*----------------------------------------------------------------------*/ +#ifdef CONFIG_NEC_NL6448AC33 +/* + *  NEC NL6448AC33-18. Active, color, single scan. + */ +vidinfo_t panel_info = { +    640, 480, 132, 99, CONFIG_SYS_HIGH, CONFIG_SYS_HIGH, CONFIG_SYS_LOW, CONFIG_SYS_LOW, CONFIG_SYS_HIGH, +    3, 0, 0, 1, 1, 144, 2, 0, 33 +		/* wbl, vpw, lcdac, wbf */ +}; +#endif /* CONFIG_NEC_NL6448AC33 */ +/*----------------------------------------------------------------------*/ + +#ifdef CONFIG_NEC_NL6448BC20 +/* + *  NEC NL6448BC20-08.  6.5", 640x480. Active, color, single scan. + */ +vidinfo_t panel_info = { +    640, 480, 132, 99, CONFIG_SYS_HIGH, CONFIG_SYS_HIGH, CONFIG_SYS_LOW, CONFIG_SYS_LOW, CONFIG_SYS_HIGH, +    3, 0, 0, 1, 1, 144, 2, 0, 33 +		/* wbl, vpw, lcdac, wbf */ +}; +#endif /* CONFIG_NEC_NL6448BC20 */ +/*----------------------------------------------------------------------*/ + +#ifdef CONFIG_NEC_NL6448BC33_54 +/* + *  NEC NL6448BC33-54. 10.4", 640x480. Active, color, single scan. + */ +vidinfo_t panel_info = { +    640, 480, 212, 158, CONFIG_SYS_HIGH, CONFIG_SYS_HIGH, CONFIG_SYS_LOW, CONFIG_SYS_LOW, CONFIG_SYS_HIGH, +    3, 0, 0, 1, 1, 144, 2, 0, 33 +		/* wbl, vpw, lcdac, wbf */ +}; +#endif /* CONFIG_NEC_NL6448BC33_54 */ +/*----------------------------------------------------------------------*/ + +#ifdef CONFIG_SHARP_LQ104V7DS01 +/* + *  SHARP LQ104V7DS01. 6.5", 640x480. Active, color, single scan. + */ +vidinfo_t panel_info = { +    640, 480, 132, 99, CONFIG_SYS_HIGH, CONFIG_SYS_HIGH, CONFIG_SYS_LOW, CONFIG_SYS_LOW, CONFIG_SYS_LOW, +    3, 0, 0, 1, 1, 25, 1, 0, 33 +		/* wbl, vpw, lcdac, wbf */ +}; +#endif /* CONFIG_SHARP_LQ104V7DS01 */ +/*----------------------------------------------------------------------*/ + +#ifdef CONFIG_SHARP_16x9 +/* + * Sharp 320x240. Active, color, single scan.  It isn't 16x9, and I am + * not sure what it is....... + */ +vidinfo_t panel_info = { +    320, 240, 0, 0, CONFIG_SYS_HIGH, CONFIG_SYS_HIGH, CONFIG_SYS_HIGH, CONFIG_SYS_HIGH, CONFIG_SYS_HIGH, +    3, 0, 0, 1, 1, 15, 4, 0, 3 +}; +#endif /* CONFIG_SHARP_16x9 */ +/*----------------------------------------------------------------------*/ + +#ifdef CONFIG_SHARP_LQ057Q3DC02 +/* + * Sharp LQ057Q3DC02 display. Active, color, single scan. + */ +#undef LCD_DF +#define LCD_DF 12 + +vidinfo_t panel_info = { +    320, 240, 0, 0, CONFIG_SYS_HIGH, CONFIG_SYS_HIGH, CONFIG_SYS_LOW, CONFIG_SYS_LOW, CONFIG_SYS_HIGH, +    3, 0, 0, 1, 1, 15, 4, 0, 3 +		/* wbl, vpw, lcdac, wbf */ +}; +#define CONFIG_LCD_INFO_BELOW_LOGO +#endif /* CONFIG_SHARP_LQ057Q3DC02 */ +/*----------------------------------------------------------------------*/ + +#ifdef CONFIG_SHARP_LQ64D341 +/* + * Sharp LQ64D341 display, 640x480. Active, color, single scan. + */ +vidinfo_t panel_info = { +    640, 480, 0, 0, CONFIG_SYS_HIGH, CONFIG_SYS_HIGH, CONFIG_SYS_LOW, CONFIG_SYS_LOW, CONFIG_SYS_HIGH, +    3, 0, 0, 1, 1, 128, 16, 0, 32 +		/* wbl, vpw, lcdac, wbf */ +}; +#endif /* CONFIG_SHARP_LQ64D341 */ + +#ifdef CONFIG_SHARP_LQ065T9DR51U +/* + * Sharp LQ065T9DR51U display, 400x240. Active, color, single scan. + */ +vidinfo_t panel_info = { +    400, 240, 143, 79, CONFIG_SYS_HIGH, CONFIG_SYS_HIGH, CONFIG_SYS_HIGH, CONFIG_SYS_HIGH, CONFIG_SYS_HIGH, +    3, 0, 0, 1, 1, 248, 4, 0, 35 +		/* wbl, vpw, lcdac, wbf */ +}; +#define CONFIG_LCD_INFO_BELOW_LOGO +#endif /* CONFIG_SHARP_LQ065T9DR51U */ + +#ifdef CONFIG_SHARP_LQ084V1DG21 +/* + * Sharp LQ084V1DG21 display, 640x480. Active, color, single scan. + */ +vidinfo_t panel_info = { +    640, 480, 171, 129, CONFIG_SYS_HIGH, CONFIG_SYS_HIGH, CONFIG_SYS_LOW, CONFIG_SYS_LOW, CONFIG_SYS_LOW, +    3, 0, 0, 1, 1, 160, 3, 0, 48 +		/* wbl, vpw, lcdac, wbf */ +}; +#endif /* CONFIG_SHARP_LQ084V1DG21 */ + +/*----------------------------------------------------------------------*/ + +#ifdef CONFIG_HLD1045 +/* + * HLD1045 display, 640x480. Active, color, single scan. + */ +vidinfo_t panel_info = { +    640, 480, 0, 0, CONFIG_SYS_HIGH, CONFIG_SYS_HIGH, CONFIG_SYS_LOW, CONFIG_SYS_LOW, CONFIG_SYS_HIGH, +    3, 0, 0, 1, 1, 160, 3, 0, 48 +		/* wbl, vpw, lcdac, wbf */ +}; +#endif /* CONFIG_HLD1045 */ +/*----------------------------------------------------------------------*/ + +#ifdef CONFIG_PRIMEVIEW_V16C6448AC +/* + * Prime View V16C6448AC + */ +vidinfo_t panel_info = { +    640, 480, 130, 98, CONFIG_SYS_HIGH, CONFIG_SYS_HIGH, CONFIG_SYS_LOW, CONFIG_SYS_LOW, CONFIG_SYS_HIGH, +    3, 0, 0, 1, 1, 144, 2, 0, 35 +		/* wbl, vpw, lcdac, wbf */ +}; +#endif /* CONFIG_PRIMEVIEW_V16C6448AC */ + +/*----------------------------------------------------------------------*/ + +#ifdef CONFIG_OPTREX_BW +/* + * Optrex   CBL50840-2 NF-FW 99 22 M5 + * or + * Hitachi  LMG6912RPFC-00T + * or + * Hitachi  SP14Q002 + * + * 320x240. Black & white. + */ +#define OPTREX_BPP	0	/* 0 - monochrome,     1 bpp */ +				/* 1 -  4 grey levels, 2 bpp */ +				/* 2 - 16 grey levels, 4 bpp */ +vidinfo_t panel_info = { +    320, 240, 0, 0, CONFIG_SYS_HIGH, CONFIG_SYS_HIGH, CONFIG_SYS_HIGH, CONFIG_SYS_HIGH, CONFIG_SYS_LOW, +    OPTREX_BPP, 0, 0, 0, 0, 0, 0, 0, 0, 4 +}; +#endif /* CONFIG_OPTREX_BW */ + +/*-----------------------------------------------------------------*/ +#ifdef CONFIG_EDT32F10 +/* + * Emerging Display Technologies 320x240. Passive, monochrome, single scan. + */ +#define LCD_BPP		LCD_MONOCHROME +#define LCD_DF		10 + +vidinfo_t panel_info = { +    320, 240, 0, 0, CONFIG_SYS_HIGH, CONFIG_SYS_HIGH, CONFIG_SYS_HIGH, CONFIG_SYS_HIGH, CONFIG_SYS_LOW, +    LCD_BPP,  0, 0, 0, 0, 33, 0, 0, 0 +}; +#endif +/*----------------------------------------------------------------------*/ + + +int lcd_line_length; + +int lcd_color_fg; +int lcd_color_bg; + +/* + * Frame buffer memory information + */ +void *lcd_base;			/* Start of framebuffer memory	*/ +void *lcd_console_address;	/* Start of console buffer	*/ + +short console_col; +short console_row; + +/************************************************************************/ + +void lcd_ctrl_init (void *lcdbase); +void lcd_enable (void); +#if LCD_BPP == LCD_COLOR8 +void lcd_setcolreg (ushort regno, +				ushort red, ushort green, ushort blue); +#endif +#if LCD_BPP == LCD_MONOCHROME +void lcd_initcolregs (void); +#endif + +#if defined(CONFIG_RBC823) +void lcd_disable (void); +#endif + +/************************************************************************/ + +/************************************************************************/ +/* ----------------- chipset specific functions ----------------------- */ +/************************************************************************/ + +/* + * Calculate fb size for VIDEOLFB_ATAG. + */ +ulong calc_fbsize (void) +{ +	ulong size; +	int line_length = (panel_info.vl_col * NBITS (panel_info.vl_bpix)) / 8; + +	size = line_length * panel_info.vl_row; + +	return size; +} + +void lcd_ctrl_init (void *lcdbase) +{ +	volatile immap_t *immr = (immap_t *) CONFIG_SYS_IMMR; +	volatile lcd823_t *lcdp = &immr->im_lcd; + +	uint lccrtmp; +	uint lchcr_hpc_tmp; + +	/* Initialize the LCD control register according to the LCD +	 * parameters defined.  We do everything here but enable +	 * the controller. +	 */ + +#ifdef CONFIG_RPXLITE +	/* This is special for RPXlite_DW Software Development Platform **[Sam]** */ +	panel_info.vl_dp = CONFIG_SYS_LOW; +#endif + +	lccrtmp  = LCDBIT (LCCR_BNUM_BIT, +		   (((panel_info.vl_row * panel_info.vl_col) * (1 << LCD_BPP)) / 128)); + +	lccrtmp |= LCDBIT (LCCR_CLKP_BIT, panel_info.vl_clkp)	| +		   LCDBIT (LCCR_OEP_BIT,  panel_info.vl_oep)	| +		   LCDBIT (LCCR_HSP_BIT,  panel_info.vl_hsp)	| +		   LCDBIT (LCCR_VSP_BIT,  panel_info.vl_vsp)	| +		   LCDBIT (LCCR_DP_BIT,   panel_info.vl_dp)	| +		   LCDBIT (LCCR_BPIX_BIT, panel_info.vl_bpix)	| +		   LCDBIT (LCCR_LBW_BIT,  panel_info.vl_lbw)	| +		   LCDBIT (LCCR_SPLT_BIT, panel_info.vl_splt)	| +		   LCDBIT (LCCR_CLOR_BIT, panel_info.vl_clor)	| +		   LCDBIT (LCCR_TFT_BIT,  panel_info.vl_tft); + +#if 0 +	lccrtmp |= ((SIU_LEVEL5 / 2) << 12); +	lccrtmp |= LCCR_EIEN; +#endif + +	lcdp->lcd_lccr = lccrtmp; +	lcdp->lcd_lcsr = 0xFF;		/* Clear pending interrupts */ + +	/* Initialize LCD controller bus priorities. +	 */ +#ifdef CONFIG_RBC823 +	immr->im_siu_conf.sc_sdcr = (immr->im_siu_conf.sc_sdcr & ~0x0f) | 1;	/* RAID = 01, LAID = 00 */ +#else +	immr->im_siu_conf.sc_sdcr &= ~0x0f;	/* RAID = LAID = 0 */ + +	/* set SHFT/CLOCK division factor 4 +	 * This needs to be set based upon display type and processor +	 * speed.  The TFT displays run about 20 to 30 MHz. +	 * I was running 64 MHz processor speed. +	 * The value for this divider must be chosen so the result is +	 * an integer of the processor speed (i.e., divide by 3 with +	 * 64 MHz would be bad). +	 */ +	immr->im_clkrst.car_sccr &= ~0x1F; +	immr->im_clkrst.car_sccr |= LCD_DF;	/* was 8 */ + +#endif /* CONFIG_RBC823 */ + +#if defined(CONFIG_RBC823) +	/* Enable LCD on port D. +	 */ +	immr->im_ioport.iop_pddat &= 0x0300; +	immr->im_ioport.iop_pdpar |= 0x1CFF; +	immr->im_ioport.iop_pddir |= 0x1CFF; + +	/* Configure LCD_ON, VEE_ON, CCFL_ON on port B. +	 */ +	immr->im_cpm.cp_pbdat &= ~0x00005001; +	immr->im_cpm.cp_pbpar &= ~0x00005001; +	immr->im_cpm.cp_pbdir |=  0x00005001; +#elif !defined(CONFIG_EDT32F10) +	/* Enable LCD on port D. +	 */ +	immr->im_ioport.iop_pdpar |= 0x1FFF; +	immr->im_ioport.iop_pddir |= 0x1FFF; + +	/* Enable LCD_A/B/C on port B. +	 */ +	immr->im_cpm.cp_pbpar |= 0x00005001; +	immr->im_cpm.cp_pbdir |= 0x00005001; +#else +	/* Enable LCD on port D. +	 */ +	immr->im_ioport.iop_pdpar |= 0x1DFF; +	immr->im_ioport.iop_pdpar &= ~0x0200; +	immr->im_ioport.iop_pddir |= 0x1FFF; +	immr->im_ioport.iop_pddat |= 0x0200; +#endif + +	/* Load the physical address of the linear frame buffer +	 * into the LCD controller. +	 * BIG NOTE:  This has to be modified to load A and B depending +	 * upon the split mode of the LCD. +	 */ +	lcdp->lcd_lcfaa = (ulong)lcd_base; +	lcdp->lcd_lcfba = (ulong)lcd_base; + +	/* MORE HACKS...This must be updated according to 823 manual +	 * for different panels. +	 * Udi Finkelstein - done - see below: +	 * Note: You better not try unsupported combinations such as +	 * 4-bit wide passive dual scan LCD at 4/8 Bit color. +	 */ +	lchcr_hpc_tmp = +		(panel_info.vl_col * +		 (panel_info.vl_tft ? 8 : +			(((2 - panel_info.vl_lbw) << /* 4 bit=2, 8-bit = 1 */ +			 /* use << to mult by: single scan = 1, dual scan = 2 */ +			  panel_info.vl_splt) * +			 (panel_info.vl_bpix | 1)))) >> 3; /* 2/4 BPP = 1, 8/16 BPP = 3 */ + +	lcdp->lcd_lchcr = LCHCR_BO | +			  LCDBIT (LCHCR_AT_BIT, 4) | +			  LCDBIT (LCHCR_HPC_BIT, lchcr_hpc_tmp) | +			  panel_info.vl_wbl; + +	lcdp->lcd_lcvcr = LCDBIT (LCVCR_VPW_BIT, panel_info.vl_vpw) | +			  LCDBIT (LCVCR_LCD_AC_BIT, panel_info.vl_lcdac) | +			  LCDBIT (LCVCR_VPC_BIT, panel_info.vl_row) | +			  panel_info.vl_wbf; + +} + +/*----------------------------------------------------------------------*/ + +#ifdef	NOT_USED_SO_FAR +static void +lcd_getcolreg (ushort regno, ushort *red, ushort *green, ushort *blue) +{ +	volatile immap_t *immr = (immap_t *) CONFIG_SYS_IMMR; +	volatile cpm8xx_t *cp = &(immr->im_cpm); +	unsigned short colreg, *cmap_ptr; + +	cmap_ptr = (unsigned short *)&cp->lcd_cmap[regno * 2]; + +	colreg = *cmap_ptr; +#ifdef	CONFIG_SYS_INVERT_COLORS +	colreg ^= 0x0FFF; +#endif + +	*red   = (colreg >> 8) & 0x0F; +	*green = (colreg >> 4) & 0x0F; +	*blue  =  colreg       & 0x0F; +} +#endif	/* NOT_USED_SO_FAR */ + +/*----------------------------------------------------------------------*/ + +#if LCD_BPP == LCD_COLOR8 +void +lcd_setcolreg (ushort regno, ushort red, ushort green, ushort blue) +{ +	volatile immap_t *immr = (immap_t *) CONFIG_SYS_IMMR; +	volatile cpm8xx_t *cp = &(immr->im_cpm); +	unsigned short colreg, *cmap_ptr; + +	cmap_ptr = (unsigned short *)&cp->lcd_cmap[regno * 2]; + +	colreg = ((red   & 0x0F) << 8) | +		 ((green & 0x0F) << 4) | +		  (blue  & 0x0F) ; +#ifdef	CONFIG_SYS_INVERT_COLORS +	colreg ^= 0x0FFF; +#endif +	*cmap_ptr = colreg; + +	debug ("setcolreg: reg %2d @ %p: R=%02X G=%02X B=%02X => %02X%02X\n", +		regno, &(cp->lcd_cmap[regno * 2]), +		red, green, blue, +		cp->lcd_cmap[ regno * 2 ], cp->lcd_cmap[(regno * 2) + 1]); +} +#endif	/* LCD_COLOR8 */ + +/*----------------------------------------------------------------------*/ + +#if LCD_BPP == LCD_MONOCHROME +static +void lcd_initcolregs (void) +{ +	volatile immap_t *immr = (immap_t *) CONFIG_SYS_IMMR; +	volatile cpm8xx_t *cp = &(immr->im_cpm); +	ushort regno; + +	for (regno = 0; regno < 16; regno++) { +		cp->lcd_cmap[regno * 2] = 0; +		cp->lcd_cmap[(regno * 2) + 1] = regno & 0x0f; +	} +} +#endif + +/*----------------------------------------------------------------------*/ + +void lcd_enable (void) +{ +	volatile immap_t *immr = (immap_t *) CONFIG_SYS_IMMR; +	volatile lcd823_t *lcdp = &immr->im_lcd; + +	/* Enable the LCD panel */ +#ifndef CONFIG_RBC823 +	immr->im_siu_conf.sc_sdcr |= (1 << (31 - 25));		/* LAM = 1 */ +#endif +	lcdp->lcd_lccr |= LCCR_PON; + +#ifdef CONFIG_V37 +	/* Turn on display backlight */ +	immr->im_cpm.cp_pbpar |= 0x00008000; +	immr->im_cpm.cp_pbdir |= 0x00008000; +#elif defined(CONFIG_RBC823) +	/* Turn on display backlight */ +	immr->im_cpm.cp_pbdat |= 0x00004000; +#endif + +#if defined(CONFIG_LWMON) +    {	uchar c = pic_read (0x60); +#if defined(CONFIG_LCD) && defined(CONFIG_LWMON) && (CONFIG_POST & CONFIG_SYS_POST_SYSMON) +	/* Enable LCD later in sysmon test, only if temperature is OK */ +#else +	c |= 0x07;	/* Power on CCFL, Enable CCFL, Chip Enable LCD */ +#endif +	pic_write (0x60, c); +    } +#endif /* CONFIG_LWMON */ + +#if defined(CONFIG_R360MPI) +    { +	extern void r360_i2c_lcd_write (uchar data0, uchar data1); +	unsigned long bgi, ctr; +	char *p; + +	if ((p = getenv("lcdbgi")) != NULL) { +		bgi = simple_strtoul (p, 0, 10) & 0xFFF; +	} else { +		bgi = 0xFFF; +	} + +	if ((p = getenv("lcdctr")) != NULL) { +		ctr = simple_strtoul (p, 0, 10) & 0xFFF; +	} else { +		ctr=0x7FF; +	} + +	r360_i2c_lcd_write(0x10, 0x01); +	r360_i2c_lcd_write(0x20, 0x01); +	r360_i2c_lcd_write(0x30 | ((bgi>>8) & 0xF), bgi & 0xFF); +	r360_i2c_lcd_write(0x40 | ((ctr>>8) & 0xF), ctr & 0xFF); +    } +#endif /* CONFIG_R360MPI */ +#ifdef CONFIG_RBC823 +	udelay(200000); /* wait 200ms */ +	/* Turn VEE_ON first */ +	immr->im_cpm.cp_pbdat |= 0x00000001; +	udelay(200000); /* wait 200ms */ +	/* Now turn on LCD_ON */ +	immr->im_cpm.cp_pbdat |= 0x00001000; +#endif +#ifdef CONFIG_RRVISION +	debug ("PC4->Output(1): enable LVDS\n"); +	debug ("PC5->Output(0): disable PAL clock\n"); +	immr->im_ioport.iop_pddir |=  0x1000; +	immr->im_ioport.iop_pcpar &= ~(0x0C00); +	immr->im_ioport.iop_pcdir |=   0x0C00 ; +	immr->im_ioport.iop_pcdat |=   0x0800 ; +	immr->im_ioport.iop_pcdat &= ~(0x0400); +	debug ("PDPAR=0x%04X PDDIR=0x%04X PDDAT=0x%04X\n", +	       immr->im_ioport.iop_pdpar, +	       immr->im_ioport.iop_pddir, +	       immr->im_ioport.iop_pddat); +	debug ("PCPAR=0x%04X PCDIR=0x%04X PCDAT=0x%04X\n", +	       immr->im_ioport.iop_pcpar, +	       immr->im_ioport.iop_pcdir, +	       immr->im_ioport.iop_pcdat); +#endif +} + +/*----------------------------------------------------------------------*/ + +#if defined (CONFIG_RBC823) +void lcd_disable (void) +{ +	volatile immap_t *immr = (immap_t *) CONFIG_SYS_IMMR; +	volatile lcd823_t *lcdp = &immr->im_lcd; + +#if defined(CONFIG_LWMON) +    {	uchar c = pic_read (0x60); +	c &= ~0x07;	/* Power off CCFL, Disable CCFL, Chip Disable LCD */ +	pic_write (0x60, c); +    } +#elif defined(CONFIG_R360MPI) +    { +	extern void r360_i2c_lcd_write (uchar data0, uchar data1); + +	r360_i2c_lcd_write(0x10, 0x00); +	r360_i2c_lcd_write(0x20, 0x00); +	r360_i2c_lcd_write(0x30, 0x00); +	r360_i2c_lcd_write(0x40, 0x00); +    } +#endif /* CONFIG_LWMON */ +	/* Disable the LCD panel */ +	lcdp->lcd_lccr &= ~LCCR_PON; +#ifdef CONFIG_RBC823 +	/* Turn off display backlight, VEE and LCD_ON */ +	immr->im_cpm.cp_pbdat &= ~0x00005001; +#else +	immr->im_siu_conf.sc_sdcr &= ~(1 << (31 - 25));	/* LAM = 0 */ +#endif /* CONFIG_RBC823 */ +} +#endif	/* NOT_USED_SO_FAR || CONFIG_RBC823 */ + + +/************************************************************************/ + +#endif /* CONFIG_LCD */ diff --git a/arch/powerpc/cpu/mpc8xx/plprcr_write.S b/arch/powerpc/cpu/mpc8xx/plprcr_write.S new file mode 100644 index 000000000..e32567114 --- /dev/null +++ b/arch/powerpc/cpu/mpc8xx/plprcr_write.S @@ -0,0 +1,135 @@ +/* + * (C) Copyright 2004 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include <mpc8xx.h> +#include <ppc_asm.tmpl> +#include <asm/cache.h> + +#define CACHE_CMD_ENABLE	0x02000000 +#define CACHE_CMD_DISABLE	0x04000000 +#define CACHE_CMD_LOAD_LOCK	0x06000000 +#define CACHE_CMD_UNLOCK_LINE	0x08000000 +#define CACHE_CMD_UNLOCK_ALL	0x0A000000 +#define CACHE_CMD_INVALIDATE	0x0C000000 +#define SPEED_PLPRCR_WAIT_5CYC	150 +#define _CACHE_ALIGN_SIZE	16 + + +	.text +	.align 2 +	.globl plprcr_write_866 + +/* + * void plprcr_write_866 (long plprcr) + * Write PLPRCR, including workaround for device errata SIU4 and SIU9. + */ + +plprcr_write_866: +	mfspr	r10, LR		/* save the Link Register value */ + +	/* turn instruction cache on (no MMU required for instructions) +	 */ +	lis	r4, CACHE_CMD_ENABLE@h +	ori	r4, r4, CACHE_CMD_ENABLE@l +	mtspr	IC_CST, r4 +	isync + +	/* clear IC_CST error bits +	 */ +	mfspr	r4, IC_CST + +	bl	plprcr_here + +plprcr_here: +	mflr	r5 + +	/* calculate relocation offset +	 */ +	lis	r4, plprcr_here@h +	ori	r4, r4, plprcr_here@l +	sub	r5, r5, r4 + +	/* calculate first address of this function +	 */ +	lis	r6, plprcr_write_866@h +	ori	r6, r6, plprcr_write_866@l +	add	r6, r6, r5 + +	/* calculate end address of this function +	 */ +	lis	r7, plprcr_end@h +	ori	r7, r7, plprcr_end@l +	add	r7, r7, r5 + +	/* load and lock code addresses +	 */ +	mr	r5, r6 + +plprcr_loop: +	mtspr	IC_ADR, r5 +	addi	r5, r5, _CACHE_ALIGN_SIZE	/* increment by one line */ + +	lis	r4, CACHE_CMD_LOAD_LOCK@h +	ori	r4, r4, CACHE_CMD_LOAD_LOCK@l +	mtspr	IC_CST, r4 +	isync + +	cmpw	r5, r7 +	blt	plprcr_loop + +	/* IC_CST error bits not evaluated +	 */ + +	/* switch PLPRCR +	 */ +	mfspr	r4, IMMR		/* read IMMR */ +	rlwinm	r4, r4, 0, 0, 15	/* only high 16 bits count */ + +	/* write sequence according to MPC866 Errata +	 */ +	stw	r3, PLPRCR(r4) +	isync + +	lis	r3, SPEED_PLPRCR_WAIT_5CYC@h +	ori	r3, r3, SPEED_PLPRCR_WAIT_5CYC@l + +plprcr_wait: +	cmpwi	r3, 0 +	beq	plprcr_wait_end +	nop +	subi	r3, r3, 1 +	b	plprcr_wait + +plprcr_wait_end: + +	/* unlock instruction cache but leave it enabled +	 */ +	lis	r4, CACHE_CMD_UNLOCK_ALL@h +	ori	r4, r4, CACHE_CMD_UNLOCK_ALL@l +	mtspr	IC_CST, r4 +	isync + +	mtspr	LR, r10		/* restore original Link Register value */ +	blr + +plprcr_end: diff --git a/arch/powerpc/cpu/mpc8xx/scc.c b/arch/powerpc/cpu/mpc8xx/scc.c new file mode 100644 index 000000000..effb967e3 --- /dev/null +++ b/arch/powerpc/cpu/mpc8xx/scc.c @@ -0,0 +1,572 @@ +/* + * File:  scc.c + * Description: + *	Basic ET HW initialization and packet RX/TX routines + * + * NOTE  <<<IMPORTANT:  PLEASE READ>>>: + *     Do not cache Rx/Tx buffers! + */ + +/* + * MPC823 <-> MC68160 Connections: + * + * Setup MPC823 to work with MC68160 Enhanced Ethernet + * Serial Tranceiver as follows: + * + * MPC823 Signal                MC68160  Comments + * ------ ------                -------  -------- + * PA-12 ETHTX    -------->   TX       Eth. Port Transmit Data + * PB-18 E_TENA   -------->   TENA     Eth. Transmit Port Enable + * PA-5 ETHTCK    <--------   TCLK     Eth. Port Transmit Clock + * PA-13 ETHRX    <--------   RX       Eth. Port Receive Data + * PC-8 E_RENA    <--------   RENA     Eth. Receive Enable + * PA-6 ETHRCK    <--------   RCLK     Eth. Port Receive Clock + * PC-9 E_CLSN    <--------   CLSN     Eth. Port Collision Indication + * + * FADS Board Signal              MC68160  Comments + * -----------------              -------  -------- + * (BCSR1) ETHEN*     -------->  CS2      Eth. Port Enable + * (BSCR4) TPSQEL*    -------->  TPSQEL   Twisted Pair Signal Quality Error Test Enable + * (BCSR4) TPFLDL*    -------->  TPFLDL   Twisted Pair Full-Duplex + * (BCSR4) ETHLOOP    -------->  LOOP     Eth. Port Diagnostic Loop-Back + * + */ + +#include <common.h> +#include <malloc.h> +#include <commproc.h> +#include <net.h> +#include <command.h> + +#if defined(CONFIG_CMD_NET) && defined(SCC_ENET) + +/* Ethernet Transmit and Receive Buffers */ +#define DBUF_LENGTH  1520 + +#define TX_BUF_CNT 2 + +#define TOUT_LOOP 10000	/* 10 ms to have a packet sent */ + +static char txbuf[DBUF_LENGTH]; + +static uint rxIdx;	/* index of the current RX buffer */ +static uint txIdx;	/* index of the current TX buffer */ + +/* +  * SCC Ethernet Tx and Rx buffer descriptors allocated at the +  *  immr->udata_bd address on Dual-Port RAM +  * Provide for Double Buffering +  */ + +typedef volatile struct CommonBufferDescriptor { +    cbd_t rxbd[PKTBUFSRX];	/* Rx BD */ +    cbd_t txbd[TX_BUF_CNT];	/* Tx BD */ +} RTXBD; + +static RTXBD *rtx; + +static int scc_send(struct eth_device* dev, volatile void *packet, int length); +static int scc_recv(struct eth_device* dev); +static int scc_init (struct eth_device* dev, bd_t * bd); +static void scc_halt(struct eth_device* dev); + +int scc_initialize(bd_t *bis) +{ +	struct eth_device* dev; + +	dev = (struct eth_device*) malloc(sizeof *dev); +	memset(dev, 0, sizeof *dev); + +	sprintf(dev->name, "SCC ETHERNET"); +	dev->iobase = 0; +	dev->priv   = 0; +	dev->init   = scc_init; +	dev->halt   = scc_halt; +	dev->send   = scc_send; +	dev->recv   = scc_recv; + +	eth_register(dev); + +	return 1; +} + +static int scc_send(struct eth_device* dev, volatile void *packet, int length) +{ +	int i, j=0; +#if 0 +	volatile char *in, *out; +#endif + +	/* section 16.9.23.3 +	 * Wait for ready +	 */ +#if 0 +	while (rtx->txbd[txIdx].cbd_sc & BD_ENET_TX_READY); +	out = (char *)(rtx->txbd[txIdx].cbd_bufaddr); +	in = packet; +	for(i = 0; i < length; i++) { +		*out++ = *in++; +	} +	rtx->txbd[txIdx].cbd_datlen = length; +	rtx->txbd[txIdx].cbd_sc |= (BD_ENET_TX_READY | BD_ENET_TX_LAST); +	while (rtx->txbd[txIdx].cbd_sc & BD_ENET_TX_READY) j++; + +#ifdef ET_DEBUG +	printf("cycles: %d    status: %x\n", j, rtx->txbd[txIdx].cbd_sc); +#endif +	i = (rtx->txbd[txIdx++].cbd_sc & BD_ENET_TX_STATS) /* return only status bits */; + +	/* wrap around buffer index when necessary */ +	if (txIdx >= TX_BUF_CNT) txIdx = 0; +#endif + +	while ((rtx->txbd[txIdx].cbd_sc & BD_ENET_TX_READY) && (j<TOUT_LOOP)) { +		udelay (1);	/* will also trigger Wd if needed */ +		j++; +	} +	if (j>=TOUT_LOOP) printf("TX not ready\n"); +	rtx->txbd[txIdx].cbd_bufaddr = (uint)packet; +	rtx->txbd[txIdx].cbd_datlen = length; +	rtx->txbd[txIdx].cbd_sc |= (BD_ENET_TX_READY | BD_ENET_TX_LAST |BD_ENET_TX_WRAP); +	while ((rtx->txbd[txIdx].cbd_sc & BD_ENET_TX_READY) && (j<TOUT_LOOP)) { +		udelay (1);	/* will also trigger Wd if needed */ +		j++; +	} +	if (j>=TOUT_LOOP) printf("TX timeout\n"); +#ifdef ET_DEBUG +	printf("cycles: %d    status: %x\n", j, rtx->txbd[txIdx].cbd_sc); +#endif +	i = (rtx->txbd[txIdx].cbd_sc & BD_ENET_TX_STATS) /* return only status bits */; +	return i; +} + +static int scc_recv (struct eth_device *dev) +{ +	int length; + +	for (;;) { +		/* section 16.9.23.2 */ +		if (rtx->rxbd[rxIdx].cbd_sc & BD_ENET_RX_EMPTY) { +			length = -1; +			break;	/* nothing received - leave for() loop */ +		} + +		length = rtx->rxbd[rxIdx].cbd_datlen; + +		if (rtx->rxbd[rxIdx].cbd_sc & 0x003f) { +#ifdef ET_DEBUG +			printf ("err: %x\n", rtx->rxbd[rxIdx].cbd_sc); +#endif +		} else { +			/* Pass the packet up to the protocol layers. */ +			NetReceive (NetRxPackets[rxIdx], length - 4); +		} + + +		/* Give the buffer back to the SCC. */ +		rtx->rxbd[rxIdx].cbd_datlen = 0; + +		/* wrap around buffer index when necessary */ +		if ((rxIdx + 1) >= PKTBUFSRX) { +			rtx->rxbd[PKTBUFSRX - 1].cbd_sc = +				(BD_ENET_RX_WRAP | BD_ENET_RX_EMPTY); +			rxIdx = 0; +		} else { +			rtx->rxbd[rxIdx].cbd_sc = BD_ENET_RX_EMPTY; +			rxIdx++; +		} +	} +	return length; +} + +/************************************************************** +  * +  * SCC Ethernet Initialization Routine +  * +  *************************************************************/ + +static int scc_init (struct eth_device *dev, bd_t * bis) +{ + +	int i; +	scc_enet_t *pram_ptr; + +	volatile immap_t *immr = (immap_t *) CONFIG_SYS_IMMR; + +#if defined(CONFIG_LWMON) +	reset_phy(); +#endif + +#ifdef CONFIG_FADS +#if defined(CONFIG_MPC86xADS) || defined(CONFIG_MPC860T) +	/* The MPC86xADS/FADS860T don't use the MODEM_EN or DATA_VOICE signals. */ +	*((uint *) BCSR4) &= ~BCSR4_ETHLOOP; +	*((uint *) BCSR4) |= BCSR4_TFPLDL | BCSR4_TPSQEL; +	*((uint *) BCSR1) &= ~BCSR1_ETHEN; +#else +	*((uint *) BCSR4) &= ~(BCSR4_ETHLOOP | BCSR4_MODEM_EN); +	*((uint *) BCSR4) |= BCSR4_TFPLDL | BCSR4_TPSQEL | BCSR4_DATA_VOICE; +	*((uint *) BCSR1) &= ~BCSR1_ETHEN; +#endif +#endif + +	pram_ptr = (scc_enet_t *) & (immr->im_cpm.cp_dparam[PROFF_ENET]); + +	rxIdx = 0; +	txIdx = 0; + +	if (!rtx) { +#ifdef CONFIG_SYS_ALLOC_DPRAM +		rtx = (RTXBD *) (immr->im_cpm.cp_dpmem + +				 dpram_alloc_align (sizeof (RTXBD), 8)); +#else +		rtx = (RTXBD *) (immr->im_cpm.cp_dpmem + CPM_SCC_BASE); +#endif +	} + +#if (defined(PA_ENET_RXD) && defined(PA_ENET_TXD)) +	/* Configure port A pins for Txd and Rxd. +	 */ +	immr->im_ioport.iop_papar |= (PA_ENET_RXD | PA_ENET_TXD); +	immr->im_ioport.iop_padir &= ~(PA_ENET_RXD | PA_ENET_TXD); +	immr->im_ioport.iop_paodr &= ~PA_ENET_TXD; +#elif (defined(PB_ENET_RXD) && defined(PB_ENET_TXD)) +	/* Configure port B pins for Txd and Rxd. +	 */ +	immr->im_cpm.cp_pbpar |= (PB_ENET_RXD | PB_ENET_TXD); +	immr->im_cpm.cp_pbdir &= ~(PB_ENET_RXD | PB_ENET_TXD); +	immr->im_cpm.cp_pbodr &= ~PB_ENET_TXD; +#else +#error Configuration Error: exactly ONE of PA_ENET_[RT]XD, PB_ENET_[RT]XD must be defined +#endif + +#if defined(PC_ENET_LBK) +	/* Configure port C pins to disable External Loopback +	 */ +	immr->im_ioport.iop_pcpar &= ~PC_ENET_LBK; +	immr->im_ioport.iop_pcdir |= PC_ENET_LBK; +	immr->im_ioport.iop_pcso &= ~PC_ENET_LBK; +	immr->im_ioport.iop_pcdat &= ~PC_ENET_LBK;	/* Disable Loopback */ +#endif /* PC_ENET_LBK */ + +	/* Configure port C pins to enable CLSN and RENA. +	 */ +	immr->im_ioport.iop_pcpar &= ~(PC_ENET_CLSN | PC_ENET_RENA); +	immr->im_ioport.iop_pcdir &= ~(PC_ENET_CLSN | PC_ENET_RENA); +	immr->im_ioport.iop_pcso |= (PC_ENET_CLSN | PC_ENET_RENA); + +	/* Configure port A for TCLK and RCLK. +	 */ +	immr->im_ioport.iop_papar |= (PA_ENET_TCLK | PA_ENET_RCLK); +	immr->im_ioport.iop_padir &= ~(PA_ENET_TCLK | PA_ENET_RCLK); + +	/* +	 * Configure Serial Interface clock routing -- see section 16.7.5.3 +	 * First, clear all SCC bits to zero, then set the ones we want. +	 */ + +	immr->im_cpm.cp_sicr &= ~SICR_ENET_MASK; +	immr->im_cpm.cp_sicr |= SICR_ENET_CLKRT; + + +	/* +	 * Initialize SDCR -- see section 16.9.23.7 +	 * SDMA configuration register +	 */ +	immr->im_siu_conf.sc_sdcr = 0x01; + + +	/* +	 * Setup SCC Ethernet Parameter RAM +	 */ + +	pram_ptr->sen_genscc.scc_rfcr = 0x18;	/* Normal Operation and Mot byte ordering */ +	pram_ptr->sen_genscc.scc_tfcr = 0x18;	/* Mot byte ordering, Normal access */ + +	pram_ptr->sen_genscc.scc_mrblr = DBUF_LENGTH;	/* max. ET package len 1520 */ + +	pram_ptr->sen_genscc.scc_rbase = (unsigned int) (&rtx->rxbd[0]);	/* Set RXBD tbl start at Dual Port */ +	pram_ptr->sen_genscc.scc_tbase = (unsigned int) (&rtx->txbd[0]);	/* Set TXBD tbl start at Dual Port */ + +	/* +	 * Setup Receiver Buffer Descriptors (13.14.24.18) +	 * Settings: +	 *     Empty, Wrap +	 */ + +	for (i = 0; i < PKTBUFSRX; i++) { +		rtx->rxbd[i].cbd_sc = BD_ENET_RX_EMPTY; +		rtx->rxbd[i].cbd_datlen = 0;	/* Reset */ +		rtx->rxbd[i].cbd_bufaddr = (uint) NetRxPackets[i]; +	} + +	rtx->rxbd[PKTBUFSRX - 1].cbd_sc |= BD_ENET_RX_WRAP; + +	/* +	 * Setup Ethernet Transmitter Buffer Descriptors (13.14.24.19) +	 * Settings: +	 *    Add PADs to Short FRAMES, Wrap, Last, Tx CRC +	 */ + +	for (i = 0; i < TX_BUF_CNT; i++) { +		rtx->txbd[i].cbd_sc = +			(BD_ENET_TX_PAD | BD_ENET_TX_LAST | BD_ENET_TX_TC); +		rtx->txbd[i].cbd_datlen = 0;	/* Reset */ +		rtx->txbd[i].cbd_bufaddr = (uint) (&txbuf[0]); +	} + +	rtx->txbd[TX_BUF_CNT - 1].cbd_sc |= BD_ENET_TX_WRAP; + +	/* +	 * Enter Command:  Initialize Rx Params for SCC +	 */ + +	do {			/* Spin until ready to issue command    */ +		__asm__ ("eieio"); +	} while (immr->im_cpm.cp_cpcr & CPM_CR_FLG); +	/* Issue command */ +	immr->im_cpm.cp_cpcr = +		((CPM_CR_INIT_RX << 8) | (CPM_CR_ENET << 4) | CPM_CR_FLG); +	do {			/* Spin until command processed         */ +		__asm__ ("eieio"); +	} while (immr->im_cpm.cp_cpcr & CPM_CR_FLG); + +	/* +	 * Ethernet Specific Parameter RAM +	 *     see table 13-16, pg. 660, +	 *     pg. 681 (example with suggested settings) +	 */ + +	pram_ptr->sen_cpres = ~(0x0);	/* Preset CRC */ +	pram_ptr->sen_cmask = 0xdebb20e3;	/* Constant Mask for CRC */ +	pram_ptr->sen_crcec = 0x0;	/* Error Counter CRC (unused) */ +	pram_ptr->sen_alec = 0x0;	/* Alignment Error Counter (unused) */ +	pram_ptr->sen_disfc = 0x0;	/* Discard Frame Counter (unused) */ +	pram_ptr->sen_pads = 0x8888;	/* Short Frame PAD Characters */ + +	pram_ptr->sen_retlim = 15;	/* Retry Limit Threshold */ +	pram_ptr->sen_maxflr = 1518;	/* MAX Frame Length Register */ +	pram_ptr->sen_minflr = 64;	/* MIN Frame Length Register */ + +	pram_ptr->sen_maxd1 = DBUF_LENGTH;	/* MAX DMA1 Length Register */ +	pram_ptr->sen_maxd2 = DBUF_LENGTH;	/* MAX DMA2 Length Register */ + +	pram_ptr->sen_gaddr1 = 0x0;	/* Group Address Filter 1 (unused) */ +	pram_ptr->sen_gaddr2 = 0x0;	/* Group Address Filter 2 (unused) */ +	pram_ptr->sen_gaddr3 = 0x0;	/* Group Address Filter 3 (unused) */ +	pram_ptr->sen_gaddr4 = 0x0;	/* Group Address Filter 4 (unused) */ + +#define ea eth_get_dev()->enetaddr +	pram_ptr->sen_paddrh = (ea[5] << 8) + ea[4]; +	pram_ptr->sen_paddrm = (ea[3] << 8) + ea[2]; +	pram_ptr->sen_paddrl = (ea[1] << 8) + ea[0]; +#undef ea + +	pram_ptr->sen_pper = 0x0;	/* Persistence (unused) */ +	pram_ptr->sen_iaddr1 = 0x0;	/* Individual Address Filter 1 (unused) */ +	pram_ptr->sen_iaddr2 = 0x0;	/* Individual Address Filter 2 (unused) */ +	pram_ptr->sen_iaddr3 = 0x0;	/* Individual Address Filter 3 (unused) */ +	pram_ptr->sen_iaddr4 = 0x0;	/* Individual Address Filter 4 (unused) */ +	pram_ptr->sen_taddrh = 0x0;	/* Tmp Address (MSB) (unused) */ +	pram_ptr->sen_taddrm = 0x0;	/* Tmp Address (unused) */ +	pram_ptr->sen_taddrl = 0x0;	/* Tmp Address (LSB) (unused) */ + +	/* +	 * Enter Command:  Initialize Tx Params for SCC +	 */ + +	do {			/* Spin until ready to issue command    */ +		__asm__ ("eieio"); +	} while (immr->im_cpm.cp_cpcr & CPM_CR_FLG); +	/* Issue command */ +	immr->im_cpm.cp_cpcr = +		((CPM_CR_INIT_TX << 8) | (CPM_CR_ENET << 4) | CPM_CR_FLG); +	do {			/* Spin until command processed         */ +		__asm__ ("eieio"); +	} while (immr->im_cpm.cp_cpcr & CPM_CR_FLG); + +	/* +	 * Mask all Events in SCCM - we use polling mode +	 */ +	immr->im_cpm.cp_scc[SCC_ENET].scc_sccm = 0; + +	/* +	 * Clear Events in SCCE -- Clear bits by writing 1's +	 */ + +	immr->im_cpm.cp_scc[SCC_ENET].scc_scce = ~(0x0); + + +	/* +	 * Initialize GSMR High 32-Bits +	 * Settings:  Normal Mode +	 */ + +	immr->im_cpm.cp_scc[SCC_ENET].scc_gsmrh = 0; + +	/* +	 * Initialize GSMR Low 32-Bits, but do not Enable Transmit/Receive +	 * Settings: +	 *     TCI = Invert +	 *     TPL =  48 bits +	 *     TPP = Repeating 10's +	 *     MODE = Ethernet +	 */ + +	immr->im_cpm.cp_scc[SCC_ENET].scc_gsmrl = (SCC_GSMRL_TCI | +						   SCC_GSMRL_TPL_48 | +						   SCC_GSMRL_TPP_10 | +						   SCC_GSMRL_MODE_ENET); + +	/* +	 * Initialize the DSR -- see section 13.14.4 (pg. 513) v0.4 +	 */ + +	immr->im_cpm.cp_scc[SCC_ENET].scc_dsr = 0xd555; + +	/* +	 * Initialize the PSMR +	 * Settings: +	 *  CRC = 32-Bit CCITT +	 *  NIB = Begin searching for SFD 22 bits after RENA +	 *  FDE = Full Duplex Enable +	 *  LPB = Loopback Enable (Needed when FDE is set) +	 *  BRO = Reject broadcast packets +	 *  PROMISCOUS = Catch all packets regardless of dest. MAC adress +	 */ +	immr->im_cpm.cp_scc[SCC_ENET].scc_psmr = SCC_PSMR_ENCRC | +		SCC_PSMR_NIB22 | +#if defined(CONFIG_SCC_ENET_FULL_DUPLEX) +		SCC_PSMR_FDE | SCC_PSMR_LPB | +#endif +#if defined(CONFIG_SCC_ENET_NO_BROADCAST) +		SCC_PSMR_BRO | +#endif +#if defined(CONFIG_SCC_ENET_PROMISCOUS) +		SCC_PSMR_PRO | +#endif +		0; + +	/* +	 * Configure Ethernet TENA Signal +	 */ + +#if (defined(PC_ENET_TENA) && !defined(PB_ENET_TENA)) +	immr->im_ioport.iop_pcpar |= PC_ENET_TENA; +	immr->im_ioport.iop_pcdir &= ~PC_ENET_TENA; +#elif (defined(PB_ENET_TENA) && !defined(PC_ENET_TENA)) +	immr->im_cpm.cp_pbpar |= PB_ENET_TENA; +	immr->im_cpm.cp_pbdir |= PB_ENET_TENA; +#else +#error Configuration Error: exactly ONE of PB_ENET_TENA, PC_ENET_TENA must be defined +#endif + +#if defined(CONFIG_ADS) && defined(CONFIG_MPC860) +	/* +	 * Port C is used to control the PHY,MC68160. +	 */ +	immr->im_ioport.iop_pcdir |= +		(PC_ENET_ETHLOOP | PC_ENET_TPFLDL | PC_ENET_TPSQEL); + +	immr->im_ioport.iop_pcdat |= PC_ENET_TPFLDL; +	immr->im_ioport.iop_pcdat &= ~(PC_ENET_ETHLOOP | PC_ENET_TPSQEL); +	*((uint *) BCSR1) &= ~BCSR1_ETHEN; +#endif /* MPC860ADS */ + +#if defined(CONFIG_AMX860) +	/* +	 * Port B is used to control the PHY,MC68160. +	 */ +	immr->im_cpm.cp_pbdir |= +		(PB_ENET_ETHLOOP | PB_ENET_TPFLDL | PB_ENET_TPSQEL); + +	immr->im_cpm.cp_pbdat |= PB_ENET_TPFLDL; +	immr->im_cpm.cp_pbdat &= ~(PB_ENET_ETHLOOP | PB_ENET_TPSQEL); + +	immr->im_ioport.iop_pddir |= PD_ENET_ETH_EN; +	immr->im_ioport.iop_pddat &= ~PD_ENET_ETH_EN; +#endif /* AMX860 */ + +#ifdef CONFIG_RPXCLASSIC +	*((uchar *) BCSR0) &= ~BCSR0_ETHLPBK; +	*((uchar *) BCSR0) |= (BCSR0_ETHEN | BCSR0_COLTEST | BCSR0_FULLDPLX); +#endif + +#ifdef CONFIG_RPXLITE +	*((uchar *) BCSR0) |= BCSR0_ETHEN; +#endif + +#if defined(CONFIG_QS860T) +	/* +	 * PB27=FDE-, set output low for full duplex +	 * PB26=Link Test Enable, normally high output +	 */ +	immr->im_cpm.cp_pbdir |= 0x00000030; +	immr->im_cpm.cp_pbdat |= 0x00000020; +	immr->im_cpm.cp_pbdat &= ~0x00000010; +#endif /* QS860T */ + +#ifdef CONFIG_MBX +	board_ether_init (); +#endif + +#if defined(CONFIG_NETVIA) +#if defined(PA_ENET_PDN) +	immr->im_ioport.iop_papar &= ~PA_ENET_PDN; +	immr->im_ioport.iop_padir |= PA_ENET_PDN; +	immr->im_ioport.iop_padat |= PA_ENET_PDN; +#elif defined(PB_ENET_PDN) +	immr->im_cpm.cp_pbpar &= ~PB_ENET_PDN; +	immr->im_cpm.cp_pbdir |= PB_ENET_PDN; +	immr->im_cpm.cp_pbdat |= PB_ENET_PDN; +#elif defined(PC_ENET_PDN) +	immr->im_ioport.iop_pcpar &= ~PC_ENET_PDN; +	immr->im_ioport.iop_pcdir |= PC_ENET_PDN; +	immr->im_ioport.iop_pcdat |= PC_ENET_PDN; +#elif defined(PD_ENET_PDN) +	immr->im_ioport.iop_pdpar &= ~PD_ENET_PDN; +	immr->im_ioport.iop_pddir |= PD_ENET_PDN; +	immr->im_ioport.iop_pddat |= PD_ENET_PDN; +#endif +#endif + +	/* +	 * Set the ENT/ENR bits in the GSMR Low -- Enable Transmit/Receive +	 */ + +	immr->im_cpm.cp_scc[SCC_ENET].scc_gsmrl |= +		(SCC_GSMRL_ENR | SCC_GSMRL_ENT); + +	/* +	 * Work around transmit problem with first eth packet +	 */ +#if defined (CONFIG_FADS) +	udelay (10000);		/* wait 10 ms */ +#elif defined (CONFIG_AMX860) || defined(CONFIG_RPXCLASSIC) +	udelay (100000);	/* wait 100 ms */ +#endif + +	return 1; +} + + +static void scc_halt (struct eth_device *dev) +{ +	volatile immap_t *immr = (immap_t *) CONFIG_SYS_IMMR; + +	immr->im_cpm.cp_scc[SCC_ENET].scc_gsmrl &= +		~(SCC_GSMRL_ENR | SCC_GSMRL_ENT); + +	immr->im_ioport.iop_pcso  &=  ~(PC_ENET_CLSN | PC_ENET_RENA); +} + +#if 0 +void restart (void) +{ +	volatile immap_t *immr = (immap_t *) CONFIG_SYS_IMMR; + +	immr->im_cpm.cp_scc[SCC_ENET].scc_gsmrl |= +		(SCC_GSMRL_ENR | SCC_GSMRL_ENT); +} +#endif +#endif diff --git a/arch/powerpc/cpu/mpc8xx/serial.c b/arch/powerpc/cpu/mpc8xx/serial.c new file mode 100644 index 000000000..664db65a5 --- /dev/null +++ b/arch/powerpc/cpu/mpc8xx/serial.c @@ -0,0 +1,745 @@ +/* + * (C) Copyright 2000 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include <common.h> +#include <commproc.h> +#include <command.h> +#include <serial.h> +#include <watchdog.h> + +DECLARE_GLOBAL_DATA_PTR; + +#if !defined(CONFIG_8xx_CONS_NONE)	/* No Console at all */ + +#if defined(CONFIG_8xx_CONS_SMC1)	/* Console on SMC1 */ +#define	SMC_INDEX	0 +#define PROFF_SMC	PROFF_SMC1 +#define CPM_CR_CH_SMC	CPM_CR_CH_SMC1 + +#elif defined(CONFIG_8xx_CONS_SMC2)	/* Console on SMC2 */ +#define SMC_INDEX	1 +#define PROFF_SMC	PROFF_SMC2 +#define CPM_CR_CH_SMC	CPM_CR_CH_SMC2 + +#endif /* CONFIG_8xx_CONS_SMCx */ + +#if defined(CONFIG_8xx_CONS_SCC1)	/* Console on SCC1 */ +#define SCC_INDEX	0 +#define PROFF_SCC	PROFF_SCC1 +#define CPM_CR_CH_SCC	CPM_CR_CH_SCC1 + +#elif defined(CONFIG_8xx_CONS_SCC2)	/* Console on SCC2 */ +#define SCC_INDEX	1 +#define PROFF_SCC	PROFF_SCC2 +#define CPM_CR_CH_SCC	CPM_CR_CH_SCC2 + +#elif defined(CONFIG_8xx_CONS_SCC3)	/* Console on SCC3 */ +#define SCC_INDEX	2 +#define PROFF_SCC	PROFF_SCC3 +#define CPM_CR_CH_SCC	CPM_CR_CH_SCC3 + +#elif defined(CONFIG_8xx_CONS_SCC4)	/* Console on SCC4 */ +#define SCC_INDEX	3 +#define PROFF_SCC	PROFF_SCC4 +#define CPM_CR_CH_SCC	CPM_CR_CH_SCC4 + +#endif /* CONFIG_8xx_CONS_SCCx */ + +#if !defined(CONFIG_SYS_SMC_RXBUFLEN) +#define CONFIG_SYS_SMC_RXBUFLEN	1 +#define CONFIG_SYS_MAXIDLE	0 +#else +#if !defined(CONFIG_SYS_MAXIDLE) +#error "you must define CONFIG_SYS_MAXIDLE" +#endif +#endif + +typedef volatile struct serialbuffer { +	cbd_t	rxbd;		/* Rx BD */ +	cbd_t	txbd;		/* Tx BD */ +	uint	rxindex;	/* index for next character to read */ +	volatile uchar	rxbuf[CONFIG_SYS_SMC_RXBUFLEN];/* rx buffers */ +	volatile uchar	txbuf;	/* tx buffers */ +} serialbuffer_t; + +static void serial_setdivisor(volatile cpm8xx_t *cp) +{ +	int divisor=(gd->cpu_clk + 8*gd->baudrate)/16/gd->baudrate; + +	if(divisor/16>0x1000) { +		/* bad divisor, assume 50MHz clock and 9600 baud */ +		divisor=(50*1000*1000 + 8*9600)/16/9600; +	} + +#ifdef CONFIG_SYS_BRGCLK_PRESCALE +	divisor /= CONFIG_SYS_BRGCLK_PRESCALE; +#endif + +	if(divisor<=0x1000) { +		cp->cp_brgc1=((divisor-1)<<1) | CPM_BRG_EN; +	} else { +		cp->cp_brgc1=((divisor/16-1)<<1) | CPM_BRG_EN | CPM_BRG_DIV16; +	} +} + +#if (defined (CONFIG_8xx_CONS_SMC1) || defined (CONFIG_8xx_CONS_SMC2)) + +/* + * Minimal serial functions needed to use one of the SMC ports + * as serial console interface. + */ + +static void smc_setbrg (void) +{ +	volatile immap_t *im = (immap_t *)CONFIG_SYS_IMMR; +	volatile cpm8xx_t *cp = &(im->im_cpm); + +	/* Set up the baud rate generator. +	 * See 8xx_io/commproc.c for details. +	 * +	 * Wire BRG1 to SMCx +	 */ + +	cp->cp_simode = 0x00000000; + +	serial_setdivisor(cp); +} + +static int smc_init (void) +{ +	volatile immap_t *im = (immap_t *)CONFIG_SYS_IMMR; +	volatile smc_t *sp; +	volatile smc_uart_t *up; +	volatile cpm8xx_t *cp = &(im->im_cpm); +#if (!defined(CONFIG_8xx_CONS_SMC1)) && (defined(CONFIG_MPC823) || defined(CONFIG_MPC850)) +	volatile iop8xx_t *ip = (iop8xx_t *)&(im->im_ioport); +#endif +	uint	dpaddr; +	volatile serialbuffer_t *rtx; + +	/* initialize pointers to SMC */ + +	sp = (smc_t *) &(cp->cp_smc[SMC_INDEX]); +	up = (smc_uart_t *) &cp->cp_dparam[PROFF_SMC]; +#ifdef CONFIG_SYS_SMC_UCODE_PATCH +	up = (smc_uart_t *) &cp->cp_dpmem[up->smc_rpbase]; +#else +	/* Disable relocation */ +	up->smc_rpbase = 0; +#endif + +	/* Disable transmitter/receiver. */ +	sp->smc_smcmr &= ~(SMCMR_REN | SMCMR_TEN); + +	/* Enable SDMA. */ +	im->im_siu_conf.sc_sdcr = 1; + +	/* clear error conditions */ +#ifdef	CONFIG_SYS_SDSR +	im->im_sdma.sdma_sdsr = CONFIG_SYS_SDSR; +#else +	im->im_sdma.sdma_sdsr = 0x83; +#endif + +	/* clear SDMA interrupt mask */ +#ifdef	CONFIG_SYS_SDMR +	im->im_sdma.sdma_sdmr = CONFIG_SYS_SDMR; +#else +	im->im_sdma.sdma_sdmr = 0x00; +#endif + +#if defined(CONFIG_8xx_CONS_SMC1) +	/* Use Port B for SMC1 instead of other functions. */ +	cp->cp_pbpar |=  0x000000c0; +	cp->cp_pbdir &= ~0x000000c0; +	cp->cp_pbodr &= ~0x000000c0; +#else	/* CONFIG_8xx_CONS_SMC2 */ +# if defined(CONFIG_MPC823) || defined(CONFIG_MPC850) +	/* Use Port A for SMC2 instead of other functions. */ +	ip->iop_papar |=  0x00c0; +	ip->iop_padir &= ~0x00c0; +	ip->iop_paodr &= ~0x00c0; +# else	/* must be a 860 then */ +	/* Use Port B for SMC2 instead of other functions. +	 */ +	cp->cp_pbpar |=  0x00000c00; +	cp->cp_pbdir &= ~0x00000c00; +	cp->cp_pbodr &= ~0x00000c00; +# endif +#endif + +#if defined(CONFIG_FADS) || defined(CONFIG_ADS) +	/* Enable RS232 */ +#if defined(CONFIG_8xx_CONS_SMC1) +	*((uint *) BCSR1) &= ~BCSR1_RS232EN_1; +#else +	*((uint *) BCSR1) &= ~BCSR1_RS232EN_2; +#endif +#endif	/* CONFIG_FADS */ + +#if defined(CONFIG_RPXLITE) || defined(CONFIG_RPXCLASSIC) +	/* Enable Monitor Port Transceiver */ +	*((uchar *) BCSR0) |= BCSR0_ENMONXCVR ; +#endif /* CONFIG_RPXLITE */ + +	/* Set the physical address of the host memory buffers in +	 * the buffer descriptors. +	 */ + +#ifdef CONFIG_SYS_ALLOC_DPRAM +	/* allocate +	 * size of struct serialbuffer with bd rx/tx, buffer rx/tx and rx index +	 */ +	dpaddr = dpram_alloc_align((sizeof(serialbuffer_t)), 8); +#else +	dpaddr = CPM_SERIAL_BASE ; +#endif + +	rtx = (serialbuffer_t *)&cp->cp_dpmem[dpaddr]; +	/* Allocate space for two buffer descriptors in the DP ram. +	 * For now, this address seems OK, but it may have to +	 * change with newer versions of the firmware. +	 * damm: allocating space after the two buffers for rx/tx data +	 */ + +	rtx->rxbd.cbd_bufaddr = (uint) &rtx->rxbuf; +	rtx->rxbd.cbd_sc      = 0; + +	rtx->txbd.cbd_bufaddr = (uint) &rtx->txbuf; +	rtx->txbd.cbd_sc      = 0; + +	/* Set up the uart parameters in the parameter ram. */ +	up->smc_rbase = dpaddr; +	up->smc_tbase = dpaddr+sizeof(cbd_t); +	up->smc_rfcr = SMC_EB; +	up->smc_tfcr = SMC_EB; +#if defined (CONFIG_SYS_SMC_UCODE_PATCH) +	up->smc_rbptr = up->smc_rbase; +	up->smc_tbptr = up->smc_tbase; +	up->smc_rstate = 0; +	up->smc_tstate = 0; +#endif + +#if defined(CONFIG_MBX) +	board_serial_init(); +#endif	/* CONFIG_MBX */ + +	/* Set UART mode, 8 bit, no parity, one stop. +	 * Enable receive and transmit. +	 */ +	sp->smc_smcmr = smcr_mk_clen(9) |  SMCMR_SM_UART; + +	/* Mask all interrupts and remove anything pending. +	*/ +	sp->smc_smcm = 0; +	sp->smc_smce = 0xff; + +#ifdef CONFIG_SYS_SPC1920_SMC1_CLK4 +	/* clock source is PLD */ + +	/* set freq to 19200 Baud */ +	*((volatile uchar *) CONFIG_SYS_SPC1920_PLD_BASE+6) = 0x3; +	/* configure clk4 as input */ +	im->im_ioport.iop_pdpar |= 0x800; +	im->im_ioport.iop_pddir &= ~0x800; + +	cp->cp_simode = ((cp->cp_simode & ~0xf000) | 0x7000); +#else +	/* Set up the baud rate generator */ +	smc_setbrg (); +#endif + +	/* Make the first buffer the only buffer. */ +	rtx->txbd.cbd_sc |= BD_SC_WRAP; +	rtx->rxbd.cbd_sc |= BD_SC_EMPTY | BD_SC_WRAP; + +	/* single/multi character receive. */ +	up->smc_mrblr = CONFIG_SYS_SMC_RXBUFLEN; +	up->smc_maxidl = CONFIG_SYS_MAXIDLE; +	rtx->rxindex = 0; + +	/* Initialize Tx/Rx parameters.	*/ +	while (cp->cp_cpcr & CPM_CR_FLG)  /* wait if cp is busy */ +	  ; + +	cp->cp_cpcr = mk_cr_cmd(CPM_CR_CH_SMC, CPM_CR_INIT_TRX) | CPM_CR_FLG; + +	while (cp->cp_cpcr & CPM_CR_FLG)  /* wait if cp is busy */ +	  ; + +	/* Enable transmitter/receiver.	*/ +	sp->smc_smcmr |= SMCMR_REN | SMCMR_TEN; + +	return (0); +} + +static void +smc_putc(const char c) +{ +	volatile smc_uart_t	*up; +	volatile immap_t	*im = (immap_t *)CONFIG_SYS_IMMR; +	volatile cpm8xx_t	*cpmp = &(im->im_cpm); +	volatile serialbuffer_t	*rtx; + +#ifdef CONFIG_MODEM_SUPPORT +	if (gd->be_quiet) +		return; +#endif + +	if (c == '\n') +		smc_putc ('\r'); + +	up = (smc_uart_t *)&cpmp->cp_dparam[PROFF_SMC]; +#ifdef CONFIG_SYS_SMC_UCODE_PATCH +	up = (smc_uart_t *) &cpmp->cp_dpmem[up->smc_rpbase]; +#endif + +	rtx = (serialbuffer_t *)&cpmp->cp_dpmem[up->smc_rbase]; + +	/* Wait for last character to go. */ +	rtx->txbuf = c; +	rtx->txbd.cbd_datlen = 1; +	rtx->txbd.cbd_sc |= BD_SC_READY; +	__asm__("eieio"); + +	while (rtx->txbd.cbd_sc & BD_SC_READY) { +		WATCHDOG_RESET (); +		__asm__("eieio"); +	} +} + +static void +smc_puts (const char *s) +{ +	while (*s) { +		smc_putc (*s++); +	} +} + +static int +smc_getc(void) +{ +	volatile smc_uart_t	*up; +	volatile immap_t	*im = (immap_t *)CONFIG_SYS_IMMR; +	volatile cpm8xx_t	*cpmp = &(im->im_cpm); +	volatile serialbuffer_t	*rtx; +	unsigned char  c; + +	up = (smc_uart_t *)&cpmp->cp_dparam[PROFF_SMC]; +#ifdef CONFIG_SYS_SMC_UCODE_PATCH +	up = (smc_uart_t *) &cpmp->cp_dpmem[up->smc_rpbase]; +#endif +	rtx = (serialbuffer_t *)&cpmp->cp_dpmem[up->smc_rbase]; + +	/* Wait for character to show up. */ +	while (rtx->rxbd.cbd_sc & BD_SC_EMPTY) +		WATCHDOG_RESET (); + +	/* the characters are read one by one, +	 * use the rxindex to know the next char to deliver +	 */ +	c = *(unsigned char *) (rtx->rxbd.cbd_bufaddr+rtx->rxindex); +	rtx->rxindex++; + +	/* check if all char are readout, then make prepare for next receive */ +	if (rtx->rxindex >= rtx->rxbd.cbd_datlen) { +		rtx->rxindex = 0; +		rtx->rxbd.cbd_sc |= BD_SC_EMPTY; +	} +	return(c); +} + +static int +smc_tstc(void) +{ +	volatile smc_uart_t	*up; +	volatile immap_t	*im = (immap_t *)CONFIG_SYS_IMMR; +	volatile cpm8xx_t	*cpmp = &(im->im_cpm); +	volatile serialbuffer_t	*rtx; + +	up = (smc_uart_t *)&cpmp->cp_dparam[PROFF_SMC]; +#ifdef CONFIG_SYS_SMC_UCODE_PATCH +	up = (smc_uart_t *) &cpmp->cp_dpmem[up->smc_rpbase]; +#endif + +	rtx = (serialbuffer_t *)&cpmp->cp_dpmem[up->smc_rbase]; + +	return !(rtx->rxbd.cbd_sc & BD_SC_EMPTY); +} + +struct serial_device serial_smc_device = +{ +	"serial_smc", +	"SMC", +	smc_init, +	smc_setbrg, +	smc_getc, +	smc_tstc, +	smc_putc, +	smc_puts, +}; + +#endif /* CONFIG_8xx_CONS_SMC1 || CONFIG_8xx_CONS_SMC2 */ + +#if defined(CONFIG_8xx_CONS_SCC1) || defined(CONFIG_8xx_CONS_SCC2) || \ +    defined(CONFIG_8xx_CONS_SCC3) || defined(CONFIG_8xx_CONS_SCC4) + +static void +scc_setbrg (void) +{ +	volatile immap_t *im = (immap_t *)CONFIG_SYS_IMMR; +	volatile cpm8xx_t *cp = &(im->im_cpm); + +	/* Set up the baud rate generator. +	 * See 8xx_io/commproc.c for details. +	 * +	 * Wire BRG1 to SCCx +	 */ + +	cp->cp_sicr &= ~(0x000000FF << (8 * SCC_INDEX)); + +	serial_setdivisor(cp); +} + +static int scc_init (void) +{ +	volatile immap_t *im = (immap_t *)CONFIG_SYS_IMMR; +	volatile scc_t *sp; +	volatile scc_uart_t *up; +	volatile cbd_t *tbdf, *rbdf; +	volatile cpm8xx_t *cp = &(im->im_cpm); +	uint	 dpaddr; +#if (SCC_INDEX != 2) || !defined(CONFIG_MPC850) +	volatile iop8xx_t *ip = (iop8xx_t *)&(im->im_ioport); +#endif + +	/* initialize pointers to SCC */ + +	sp = (scc_t *) &(cp->cp_scc[SCC_INDEX]); +	up = (scc_uart_t *) &cp->cp_dparam[PROFF_SCC]; + +#if defined(CONFIG_LWMON) && defined(CONFIG_8xx_CONS_SCC2) +    {	/* Disable Ethernet, enable Serial */ +	uchar c; + +	c = pic_read  (0x61); +	c &= ~0x40;	/* enable COM3 */ +	c |=  0x80;	/* disable Ethernet */ +	pic_write (0x61, c); + +	/* enable RTS2 */ +	cp->cp_pbpar |=  0x2000; +	cp->cp_pbdat |=  0x2000; +	cp->cp_pbdir |=  0x2000; +    } +#endif	/* CONFIG_LWMON */ + +	/* Disable transmitter/receiver. */ +	sp->scc_gsmrl &= ~(SCC_GSMRL_ENR | SCC_GSMRL_ENT); + +#if (SCC_INDEX == 2) && defined(CONFIG_MPC850) +	/* +	 * The MPC850 has SCC3 on Port B +	 */ +	cp->cp_pbpar |=  0x06; +	cp->cp_pbdir &= ~0x06; +	cp->cp_pbodr &= ~0x06; + +#elif (SCC_INDEX < 2) || !defined(CONFIG_IP860) +	/* +	 * Standard configuration for SCC's is on Part A +	 */ +	ip->iop_papar |=  ((3 << (2 * SCC_INDEX))); +	ip->iop_padir &= ~((3 << (2 * SCC_INDEX))); +	ip->iop_paodr &= ~((3 << (2 * SCC_INDEX))); +#else +	/* +	 * The IP860 has SCC3 and SCC4 on Port D +	 */ +	ip->iop_pdpar |=  ((3 << (2 * SCC_INDEX))); +#endif + +	/* Allocate space for two buffer descriptors in the DP ram. */ + +#ifdef CONFIG_SYS_ALLOC_DPRAM +	dpaddr = dpram_alloc_align (sizeof(cbd_t)*2 + 2, 8) ; +#else +	dpaddr = CPM_SERIAL2_BASE ; +#endif + +	/* Enable SDMA.	*/ +	im->im_siu_conf.sc_sdcr = 0x0001; + +	/* Set the physical address of the host memory buffers in +	 * the buffer descriptors. +	 */ + +	rbdf = (cbd_t *)&cp->cp_dpmem[dpaddr]; +	rbdf->cbd_bufaddr = (uint) (rbdf+2); +	rbdf->cbd_sc = 0; +	tbdf = rbdf + 1; +	tbdf->cbd_bufaddr = ((uint) (rbdf+2)) + 1; +	tbdf->cbd_sc = 0; + +	/* Set up the baud rate generator. */ +	scc_setbrg (); + +	/* Set up the uart parameters in the parameter ram. */ +	up->scc_genscc.scc_rbase = dpaddr; +	up->scc_genscc.scc_tbase = dpaddr+sizeof(cbd_t); + +	/* Initialize Tx/Rx parameters. */ +	while (cp->cp_cpcr & CPM_CR_FLG)  /* wait if cp is busy */ +		; +	cp->cp_cpcr = mk_cr_cmd(CPM_CR_CH_SCC, CPM_CR_INIT_TRX) | CPM_CR_FLG; + +	while (cp->cp_cpcr & CPM_CR_FLG)  /* wait if cp is busy */ +		; + +	up->scc_genscc.scc_rfcr  = SCC_EB | 0x05; +	up->scc_genscc.scc_tfcr  = SCC_EB | 0x05; + +	up->scc_genscc.scc_mrblr = 1;	/* Single character receive */ +	up->scc_maxidl = 0;		/* disable max idle */ +	up->scc_brkcr  = 1;		/* send one break character on stop TX */ +	up->scc_parec  = 0; +	up->scc_frmec  = 0; +	up->scc_nosec  = 0; +	up->scc_brkec  = 0; +	up->scc_uaddr1 = 0; +	up->scc_uaddr2 = 0; +	up->scc_toseq  = 0; +	up->scc_char1  = 0x8000; +	up->scc_char2  = 0x8000; +	up->scc_char3  = 0x8000; +	up->scc_char4  = 0x8000; +	up->scc_char5  = 0x8000; +	up->scc_char6  = 0x8000; +	up->scc_char7  = 0x8000; +	up->scc_char8  = 0x8000; +	up->scc_rccm   = 0xc0ff; + +	/* Set low latency / small fifo. */ +	sp->scc_gsmrh = SCC_GSMRH_RFW; + +	/* Set SCC(x) clock mode to 16x +	 * See 8xx_io/commproc.c for details. +	 * +	 * Wire BRG1 to SCCn +	 */ + +	/* Set UART mode, clock divider 16 on Tx and Rx */ +	sp->scc_gsmrl &= ~0xF; +	sp->scc_gsmrl |= +		(SCC_GSMRL_MODE_UART | SCC_GSMRL_TDCR_16 | SCC_GSMRL_RDCR_16); + +	sp->scc_psmr  = 0; +	sp->scc_psmr  |= SCU_PSMR_CL; + +	/* Mask all interrupts and remove anything pending. */ +	sp->scc_sccm = 0; +	sp->scc_scce = 0xffff; +	sp->scc_dsr  = 0x7e7e; +	sp->scc_psmr = 0x3000; + +	/* Make the first buffer the only buffer. */ +	tbdf->cbd_sc |= BD_SC_WRAP; +	rbdf->cbd_sc |= BD_SC_EMPTY | BD_SC_WRAP; + +	/* Enable transmitter/receiver.	*/ +	sp->scc_gsmrl |= (SCC_GSMRL_ENR | SCC_GSMRL_ENT); + +	return (0); +} + +static void +scc_putc(const char c) +{ +	volatile cbd_t		*tbdf; +	volatile char		*buf; +	volatile scc_uart_t	*up; +	volatile immap_t	*im = (immap_t *)CONFIG_SYS_IMMR; +	volatile cpm8xx_t	*cpmp = &(im->im_cpm); + +#ifdef CONFIG_MODEM_SUPPORT +	if (gd->be_quiet) +		return; +#endif + +	if (c == '\n') +		scc_putc ('\r'); + +	up = (scc_uart_t *)&cpmp->cp_dparam[PROFF_SCC]; + +	tbdf = (cbd_t *)&cpmp->cp_dpmem[up->scc_genscc.scc_tbase]; + +	/* Wait for last character to go. */ + +	buf = (char *)tbdf->cbd_bufaddr; + +	*buf = c; +	tbdf->cbd_datlen = 1; +	tbdf->cbd_sc |= BD_SC_READY; +	__asm__("eieio"); + +	while (tbdf->cbd_sc & BD_SC_READY) { +		__asm__("eieio"); +		WATCHDOG_RESET (); +	} +} + +static void +scc_puts (const char *s) +{ +	while (*s) { +		scc_putc (*s++); +	} +} + +static int +scc_getc(void) +{ +	volatile cbd_t		*rbdf; +	volatile unsigned char	*buf; +	volatile scc_uart_t	*up; +	volatile immap_t	*im = (immap_t *)CONFIG_SYS_IMMR; +	volatile cpm8xx_t	*cpmp = &(im->im_cpm); +	unsigned char		c; + +	up = (scc_uart_t *)&cpmp->cp_dparam[PROFF_SCC]; + +	rbdf = (cbd_t *)&cpmp->cp_dpmem[up->scc_genscc.scc_rbase]; + +	/* Wait for character to show up. */ +	buf = (unsigned char *)rbdf->cbd_bufaddr; + +	while (rbdf->cbd_sc & BD_SC_EMPTY) +		WATCHDOG_RESET (); + +	c = *buf; +	rbdf->cbd_sc |= BD_SC_EMPTY; + +	return(c); +} + +static int +scc_tstc(void) +{ +	volatile cbd_t		*rbdf; +	volatile scc_uart_t	*up; +	volatile immap_t	*im = (immap_t *)CONFIG_SYS_IMMR; +	volatile cpm8xx_t	*cpmp = &(im->im_cpm); + +	up = (scc_uart_t *)&cpmp->cp_dparam[PROFF_SCC]; + +	rbdf = (cbd_t *)&cpmp->cp_dpmem[up->scc_genscc.scc_rbase]; + +	return(!(rbdf->cbd_sc & BD_SC_EMPTY)); +} + +struct serial_device serial_scc_device = +{ +	"serial_scc", +	"SCC", +	scc_init, +	scc_setbrg, +	scc_getc, +	scc_tstc, +	scc_putc, +	scc_puts, +}; + +#endif	/* CONFIG_8xx_CONS_SCCx */ + +#ifdef CONFIG_MODEM_SUPPORT +void disable_putc(void) +{ +	gd->be_quiet = 1; +} + +void enable_putc(void) +{ +	gd->be_quiet = 0; +} +#endif + +#if defined(CONFIG_CMD_KGDB) + +void +kgdb_serial_init(void) +{ +	int i = -1; + +	if (strcmp(default_serial_console()->ctlr, "SMC") == 0) +	{ +#if defined(CONFIG_8xx_CONS_SMC1) +		i = 1; +#elif defined(CONFIG_8xx_CONS_SMC2) +		i = 2; +#endif +	} +	else if (strcmp(default_serial_console()->ctlr, "SMC") == 0) +	{ +#if defined(CONFIG_8xx_CONS_SCC1) +		i = 1; +#elif defined(CONFIG_8xx_CONS_SCC2) +		i = 2; +#elif defined(CONFIG_8xx_CONS_SCC3) +		i = 3; +#elif defined(CONFIG_8xx_CONS_SCC4) +		i = 4; +#endif +	} + +	if (i >= 0) +	{ +		serial_printf("[on %s%d] ", default_serial_console()->ctlr, i); +	} +} + +void +putDebugChar (int c) +{ +	serial_putc (c); +} + +void +putDebugStr (const char *str) +{ +	serial_puts (str); +} + +int +getDebugChar (void) +{ +	return serial_getc(); +} + +void +kgdb_interruptible (int yes) +{ +	return; +} +#endif + +#endif	/* CONFIG_8xx_CONS_NONE */ diff --git a/arch/powerpc/cpu/mpc8xx/speed.c b/arch/powerpc/cpu/mpc8xx/speed.c new file mode 100644 index 000000000..f309f29c0 --- /dev/null +++ b/arch/powerpc/cpu/mpc8xx/speed.c @@ -0,0 +1,416 @@ +/* + * (C) Copyright 2000-2004 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include <common.h> +#include <mpc8xx.h> +#include <asm/processor.h> + +DECLARE_GLOBAL_DATA_PTR; + +#if !defined(CONFIG_8xx_CPUCLK_DEFAULT) || defined(CONFIG_SYS_MEASURE_CPUCLK) || defined(DEBUG) + +#define PITC_SHIFT 16 +#define PITR_SHIFT 16 +/* pitc values to time for 58/8192 seconds (about 70.8 milliseconds) */ +#define SPEED_PIT_COUNTS 58 +#define SPEED_PITC	 ((SPEED_PIT_COUNTS - 1) << PITC_SHIFT) +#define SPEED_PITC_INIT	 ((SPEED_PIT_COUNTS + 1) << PITC_SHIFT) + +/* Access functions for the Machine State Register */ +static __inline__ unsigned long get_msr(void) +{ +	unsigned long msr; + +	asm volatile("mfmsr %0" : "=r" (msr) :); +	return msr; +} + +static __inline__ void set_msr(unsigned long msr) +{ +	asm volatile("mtmsr %0" : : "r" (msr)); +} + +/* ------------------------------------------------------------------------- */ + +/* + * Measure CPU clock speed (core clock GCLK1, GCLK2), + * also determine bus clock speed (checking bus divider factor) + * + * (Approx. GCLK frequency in Hz) + * + * Initializes timer 2 and PIT, but disables them before return. + * [Use timer 2, because MPC823 CPUs mask 0.x do not have timers 3 and 4] + * + * When measuring the CPU clock against the PIT, we count cpu clocks + * for 58/8192 seconds with a prescale divide by 177 for the cpu clock. + * These strange values for the timing interval and prescaling are used + * because the formula for the CPU clock is: + * + *    CPU clock = count * (177 * (8192 / 58)) + * + *		= count * 24999.7241 + * + *    which is very close to + * + *		= count * 25000 + * + * Since the count gives the CPU clock divided by 25000, we can get + * the CPU clock rounded to the nearest 0.1 MHz by + * + *    CPU clock = ((count + 2) / 4) * 100000; + * + * The rounding is important since the measurement is sometimes going + * to be high or low by 0.025 MHz, depending on exactly how the clocks + * and counters interact. By rounding we get the exact answer for any + * CPU clock that is an even multiple of 0.1 MHz. + */ + +unsigned long measure_gclk(void) +{ +	volatile immap_t *immr = (immap_t *) CONFIG_SYS_IMMR; +	volatile cpmtimer8xx_t *timerp = &immr->im_cpmtimer; +	ulong timer2_val; +	ulong msr_val; + +#ifdef CONFIG_SYS_8XX_XIN +	/* dont use OSCM, only use EXTCLK/512 */ +	immr->im_clkrst.car_sccr |= SCCR_RTSEL | SCCR_RTDIV; +#else +	immr->im_clkrst.car_sccr &= ~(SCCR_RTSEL | SCCR_RTDIV); +#endif + +	/* Reset + Stop Timer 2, no cascading +	 */ +	timerp->cpmt_tgcr &= ~(TGCR_CAS2 | TGCR_RST2); + +	/* Keep stopped, halt in debug mode +	 */ +	timerp->cpmt_tgcr |= (TGCR_FRZ2 | TGCR_STP2); + +	/* Timer 2 setup: +	 * Output ref. interrupt disable, int. clock +	 * Prescale by 177. Note that prescaler divides by value + 1 +	 * so we must subtract 1 here. +	 */ +	timerp->cpmt_tmr2 = ((177 - 1) << TMR_PS_SHIFT) | TMR_ICLK_IN_GEN; + +	timerp->cpmt_tcn2 = 0;		/* reset state		*/ +	timerp->cpmt_tgcr |= TGCR_RST2;	/* enable timer 2	*/ + +	/* +	 * PIT setup: +	 * +	 * We want to time for SPEED_PITC_COUNTS counts (of 8192 Hz), +	 * so the count value would be SPEED_PITC_COUNTS - 1. +	 * But there would be an uncertainty in the start time of 1/4 +	 * count since when we enable the PIT the count is not +	 * synchronized to the 32768 Hz oscillator. The trick here is +	 * to start the count higher and wait until the PIT count +	 * changes to the required value before starting timer 2. +	 * +	 * One count high should be enough, but occasionally the start +	 * is off by 1 or 2 counts of 32768 Hz. With the start value +	 * set two counts high it seems very reliable. +	 */ + +	immr->im_sitk.sitk_pitck = KAPWR_KEY;	/* PIT initialization */ +	immr->im_sit.sit_pitc = SPEED_PITC_INIT; + +	immr->im_sitk.sitk_piscrk = KAPWR_KEY; +	immr->im_sit.sit_piscr = CONFIG_SYS_PISCR; + +	/* +	 * Start measurement - disable interrupts, just in case +	 */ +	msr_val = get_msr (); +	set_msr (msr_val & ~MSR_EE); + +	immr->im_sit.sit_piscr |= PISCR_PTE; + +	/* spin until get exact count when we want to start */ +	while (immr->im_sit.sit_pitr > SPEED_PITC); + +	timerp->cpmt_tgcr &= ~TGCR_STP2;	/* Start Timer 2	*/ +	while ((immr->im_sit.sit_piscr & PISCR_PS) == 0); +	timerp->cpmt_tgcr |= TGCR_STP2;		/* Stop  Timer 2	*/ + +	/* re-enable external interrupts if they were on */ +	set_msr (msr_val); + +	/* Disable timer and PIT +	 */ +	timer2_val = timerp->cpmt_tcn2;		/* save before reset timer */ + +	timerp->cpmt_tgcr &= ~(TGCR_RST2 | TGCR_FRZ2 | TGCR_STP2); +	immr->im_sit.sit_piscr &= ~PISCR_PTE; + +#if defined(CONFIG_SYS_8XX_XIN) +	/* not using OSCM, using XIN, so scale appropriately */ +	return (((timer2_val + 2) / 4) * (CONFIG_SYS_8XX_XIN/512))/8192 * 100000L; +#else +	return ((timer2_val + 2) / 4) * 100000L;	/* convert to Hz	*/ +#endif +} + +#endif + +void get_brgclk(uint sccr) +{ +	uint divider = 0; + +	switch((sccr&SCCR_DFBRG11)>>11){ +		case 0: +			divider = 1; +			break; +		case 1: +			divider = 4; +			break; +		case 2: +			divider = 16; +			break; +		case 3: +			divider = 64; +			break; +	} +	gd->brg_clk = gd->cpu_clk/divider; +} + +#if !defined(CONFIG_8xx_CPUCLK_DEFAULT) + +/* + * get_clocks() fills in gd->cpu_clock depending on CONFIG_8xx_GCLK_FREQ + * or (if it is not defined) measure_gclk() (which uses the ref clock) + * from above. + */ +int get_clocks (void) +{ +	uint immr = get_immr (0);	/* Return full IMMR contents */ +	volatile immap_t *immap = (immap_t *) (immr & 0xFFFF0000); +	uint sccr = immap->im_clkrst.car_sccr; +	/* +	 * If for some reason measuring the gclk frequency won't +	 * work, we return the hardwired value. +	 * (For example, the cogent CMA286-60 CPU module has no +	 * separate oscillator for PITRTCLK) +	 */ +#if defined(CONFIG_8xx_GCLK_FREQ) +	gd->cpu_clk = CONFIG_8xx_GCLK_FREQ; +#elif defined(CONFIG_8xx_OSCLK) +#define PLPRCR_val(a) ((pll & PLPRCR_ ## a ## _MSK) >> PLPRCR_ ## a ## _SHIFT) +	uint pll = immap->im_clkrst.car_plprcr; +	uint clk; + +	if ((immr & 0x0FFF) >= MPC8xx_NEW_CLK) { /* MPC866/87x/88x series */ +		clk = ((CONFIG_8xx_OSCLK / (PLPRCR_val(PDF)+1)) * +		       (PLPRCR_val(MFI) + PLPRCR_val(MFN) / (PLPRCR_val(MFD)+1))) / +			(1<<PLPRCR_val(S)); +	} else { +		clk = CONFIG_8xx_OSCLK * (PLPRCR_val(MF)+1); +	} +	if (pll & PLPRCR_CSRC) {	/* Low frequency division factor is used  */ +		gd->cpu_clk = clk / (2 << ((sccr >> 8) & 7)); +	} else {			/* High frequency division factor is used */ +		gd->cpu_clk = clk / (1 << ((sccr >> 5) & 7)); +	} +#else +	gd->cpu_clk = measure_gclk(); +#endif /* CONFIG_8xx_GCLK_FREQ */ + +	if ((sccr & SCCR_EBDF11) == 0) { +		/* No Bus Divider active */ +		gd->bus_clk = gd->cpu_clk; +	} else { +		/* The MPC8xx has only one BDF: half clock speed */ +		gd->bus_clk = gd->cpu_clk / 2; +	} + +	get_brgclk(sccr); + +	return (0); +} + +#else /* CONFIG_8xx_CPUCLK_DEFAULT defined, use dynamic clock setting */ + +static long init_pll_866 (long clk); + +/* This function sets up PLL (init_pll_866() is called) and + * fills gd->cpu_clk and gd->bus_clk according to the environment + * variable 'cpuclk' or to CONFIG_8xx_CPUCLK_DEFAULT (if 'cpuclk' + * contains invalid value). + * This functions requires an MPC866 or newer series CPU. + */ +int get_clocks_866 (void) +{ +	volatile immap_t *immr = (immap_t *) CONFIG_SYS_IMMR; +	char		  tmp[64]; +	long		  cpuclk = 0; +	long		  sccr_reg; + +	if (getenv_r ("cpuclk", tmp, sizeof (tmp)) > 0) +		cpuclk = simple_strtoul (tmp, NULL, 10) * 1000000; + +	if ((CONFIG_SYS_8xx_CPUCLK_MIN > cpuclk) || (CONFIG_SYS_8xx_CPUCLK_MAX < cpuclk)) +		cpuclk = CONFIG_8xx_CPUCLK_DEFAULT; + +	gd->cpu_clk = init_pll_866 (cpuclk); +#if defined(CONFIG_SYS_MEASURE_CPUCLK) +	gd->cpu_clk = measure_gclk (); +#endif + +	get_brgclk(immr->im_clkrst.car_sccr); + +	/* if cpu clock <= 66 MHz then set bus division factor to 1, +	 * otherwise set it to 2 +	 */ +	sccr_reg = immr->im_clkrst.car_sccr; +	sccr_reg &= ~SCCR_EBDF11; + +	if (gd->cpu_clk <= 66000000) { +		sccr_reg |= SCCR_EBDF00;	/* bus division factor = 1 */ +		gd->bus_clk = gd->cpu_clk; +	} else { +		sccr_reg |= SCCR_EBDF01;	/* bus division factor = 2 */ +		gd->bus_clk = gd->cpu_clk / 2; +	} +	immr->im_clkrst.car_sccr = sccr_reg; + +	return (0); +} + +/* Adjust sdram refresh rate to actual CPU clock. + */ +int sdram_adjust_866 (void) +{ +	volatile immap_t *immr = (immap_t *) CONFIG_SYS_IMMR; +	long		  mamr; + +	mamr = immr->im_memctl.memc_mamr; +	mamr &= ~MAMR_PTA_MSK; +	mamr |= ((gd->cpu_clk / CONFIG_SYS_PTA_PER_CLK) << MAMR_PTA_SHIFT); +	immr->im_memctl.memc_mamr = mamr; + +	return (0); +} + +/* Configure PLL for MPC866/859/885 CPU series + * PLL multiplication factor is set to the value nearest to the desired clk, + * assuming a oscclk of 10 MHz. + */ +static long init_pll_866 (long clk) +{ +	extern void plprcr_write_866 (long); + +	volatile immap_t *immr = (immap_t *) CONFIG_SYS_IMMR; +	long		  n, plprcr; +	char		  mfi, mfn, mfd, s, pdf; +	long		  step_mfi, step_mfn; + +	if (clk < 20000000) { +		clk *= 2; +		pdf = 1; +	} else { +		pdf = 0; +	} + +	if (clk < 40000000) { +		s = 2; +		step_mfi = CONFIG_8xx_OSCLK / 4; +		mfd = 7; +		step_mfn = CONFIG_8xx_OSCLK / 30; +	} else if (clk < 80000000) { +		s = 1; +		step_mfi = CONFIG_8xx_OSCLK / 2; +		mfd = 14; +		step_mfn = CONFIG_8xx_OSCLK / 30; +	} else { +		s = 0; +		step_mfi = CONFIG_8xx_OSCLK; +		mfd = 29; +		step_mfn = CONFIG_8xx_OSCLK / 30; +	} + +	/* Calculate integer part of multiplication factor +	 */ +	n = clk / step_mfi; +	mfi = (char)n; + +	/* Calculate numerator of fractional part of multiplication factor +	 */ +	n = clk - (n * step_mfi); +	mfn = (char)(n / step_mfn); + +	/* Calculate effective clk +	 */ +	n = ((mfi * step_mfi) + (mfn * step_mfn)) / (pdf + 1); + +	immr->im_clkrstk.cark_plprcrk = KAPWR_KEY; + +	plprcr = (immr->im_clkrst.car_plprcr & ~(PLPRCR_MFN_MSK +			| PLPRCR_MFD_MSK | PLPRCR_S_MSK +			| PLPRCR_MFI_MSK | PLPRCR_DBRMO +			| PLPRCR_PDF_MSK)) +			| (mfn << PLPRCR_MFN_SHIFT) +			| (mfd << PLPRCR_MFD_SHIFT) +			| (s << PLPRCR_S_SHIFT) +			| (mfi << PLPRCR_MFI_SHIFT) +			| (pdf << PLPRCR_PDF_SHIFT); + +	if( (mfn > 0) && ((mfd / mfn) > 10) ) +		plprcr |= PLPRCR_DBRMO; + +	plprcr_write_866 (plprcr);		/* set value using SIU4/9 workaround */ +	immr->im_clkrstk.cark_plprcrk = 0x00000000; + +	return (n); +} + +#endif /* CONFIG_8xx_CPUCLK_DEFAULT */ + +#if defined(CONFIG_TQM8xxL) && !defined(CONFIG_TQM866M) \ +    && !defined(CONFIG_TQM885D) +/* + * Adjust sdram refresh rate to actual CPU clock + * and set timebase source according to actual CPU clock + */ +int adjust_sdram_tbs_8xx (void) +{ +	volatile immap_t *immr = (immap_t *) CONFIG_SYS_IMMR; +	long		  mamr; +	long              sccr; + +	mamr = immr->im_memctl.memc_mamr; +	mamr &= ~MAMR_PTA_MSK; +	mamr |= ((gd->cpu_clk / CONFIG_SYS_PTA_PER_CLK) << MAMR_PTA_SHIFT); +	immr->im_memctl.memc_mamr = mamr; + +	if (gd->cpu_clk < 67000000) { +		sccr = immr->im_clkrst.car_sccr; +		sccr |= SCCR_TBS; +		immr->im_clkrst.car_sccr = sccr; +	} + +	return (0); +} +#endif /* CONFIG_TQM8xxL/M, !TQM866M, !TQM885D */ + +/* ------------------------------------------------------------------------- */ diff --git a/arch/powerpc/cpu/mpc8xx/spi.c b/arch/powerpc/cpu/mpc8xx/spi.c new file mode 100644 index 000000000..b2ac23e5e --- /dev/null +++ b/arch/powerpc/cpu/mpc8xx/spi.c @@ -0,0 +1,560 @@ +/* + * Copyright (c) 2001 Navin Boppuri / Prashant Patel + *	<nboppuri@trinetcommunication.com>, + *	<pmpatel@trinetcommunication.com> + * Copyright (c) 2001 Gerd Mennchen <Gerd.Mennchen@icn.siemens.de> + * Copyright (c) 2001 Wolfgang Denk, DENX Software Engineering, <wd@denx.de>. + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +/* + * MPC8xx CPM SPI interface. + * + * Parts of this code are probably not portable and/or specific to + * the board which I used for the tests. Please send fixes/complaints + * to wd@denx.de + * + */ + +#include <common.h> +#include <mpc8xx.h> +#include <commproc.h> +#include <linux/ctype.h> +#include <malloc.h> +#include <post.h> +#include <serial.h> + +#if (defined(CONFIG_SPI)) || (CONFIG_POST & CONFIG_SYS_POST_SPI) + +/* Warning: + * You cannot enable DEBUG for early system initalization, i. e. when + * this driver is used to read environment parameters like "baudrate" + * from EEPROM which are used to initialize the serial port which is + * needed to print the debug messages... + */ +#undef	DEBUG + +#define SPI_EEPROM_WREN		0x06 +#define SPI_EEPROM_RDSR		0x05 +#define SPI_EEPROM_READ		0x03 +#define SPI_EEPROM_WRITE	0x02 + +/* --------------------------------------------------------------- + * Offset for initial SPI buffers in DPRAM: + * We need a 520 byte scratch DPRAM area to use at an early stage. + * It is used between the two initialization calls (spi_init_f() + * and spi_init_r()). + * The value 0xb00 makes it far enough from the start of the data + * area (as well as from the stack pointer). + * --------------------------------------------------------------- */ +#ifndef	CONFIG_SYS_SPI_INIT_OFFSET +#define	CONFIG_SYS_SPI_INIT_OFFSET	0xB00 +#endif + +#ifdef	DEBUG + +#define	DPRINT(a)	printf a; +/* ----------------------------------------------- + * Helper functions to peek into tx and rx buffers + * ----------------------------------------------- */ +static const char * const hex_digit = "0123456789ABCDEF"; + +static char quickhex (int i) +{ +	return hex_digit[i]; +} + +static void memdump (void *pv, int num) +{ +	int i; +	unsigned char *pc = (unsigned char *) pv; + +	for (i = 0; i < num; i++) +		printf ("%c%c ", quickhex (pc[i] >> 4), quickhex (pc[i] & 0x0f)); +	printf ("\t"); +	for (i = 0; i < num; i++) +		printf ("%c", isprint (pc[i]) ? pc[i] : '.'); +	printf ("\n"); +} +#else	/* !DEBUG */ + +#define	DPRINT(a) + +#endif	/* DEBUG */ + +/* ------------------- + * Function prototypes + * ------------------- */ +void spi_init (void); + +ssize_t spi_read (uchar *, int, uchar *, int); +ssize_t spi_write (uchar *, int, uchar *, int); +ssize_t spi_xfer (size_t); + +/* ------------------- + * Variables + * ------------------- */ + +#define MAX_BUFFER	0x104 + +/* ---------------------------------------------------------------------- + * Initially we place the RX and TX buffers at a fixed location in DPRAM! + * ---------------------------------------------------------------------- */ +static uchar *rxbuf = +  (uchar *)&((cpm8xx_t *)&((immap_t *)CONFIG_SYS_IMMR)->im_cpm)->cp_dpmem +			[CONFIG_SYS_SPI_INIT_OFFSET]; +static uchar *txbuf = +  (uchar *)&((cpm8xx_t *)&((immap_t *)CONFIG_SYS_IMMR)->im_cpm)->cp_dpmem +			[CONFIG_SYS_SPI_INIT_OFFSET+MAX_BUFFER]; + +/* ************************************************************************** + * + *  Function:    spi_init_f + * + *  Description: Init SPI-Controller (ROM part) + * + *  return:      --- + * + * *********************************************************************** */ +void spi_init_f (void) +{ +	unsigned int dpaddr; + +	volatile spi_t *spi; +	volatile immap_t *immr; +	volatile cpic8xx_t *cpi; +	volatile cpm8xx_t *cp; +	volatile iop8xx_t *iop; +	volatile cbd_t *tbdf, *rbdf; + +	immr = (immap_t *)  CONFIG_SYS_IMMR; +	cpi  = (cpic8xx_t *)&immr->im_cpic; +	iop  = (iop8xx_t *) &immr->im_ioport; +	cp   = (cpm8xx_t *) &immr->im_cpm; + +#ifdef CONFIG_SYS_SPI_UCODE_PATCH +	spi  = (spi_t *)&cp->cp_dpmem[spi->spi_rpbase]; +#else +	spi  = (spi_t *)&cp->cp_dparam[PROFF_SPI]; +	/* Disable relocation */ +	spi->spi_rpbase = 0; +#endif + +/* 1 */ +	/* ------------------------------------------------ +	 * Initialize Port B SPI pins -> page 34-8 MPC860UM +	 * (we are only in Master Mode !) +	 * ------------------------------------------------ */ + +	/* -------------------------------------------- +	 * GPIO or per. Function +	 * PBPAR[28] = 1 [0x00000008] -> PERI: (SPIMISO) +	 * PBPAR[29] = 1 [0x00000004] -> PERI: (SPIMOSI) +	 * PBPAR[30] = 1 [0x00000002] -> PERI: (SPICLK) +	 * PBPAR[31] = 0 [0x00000001] -> GPIO: (CS for PCUE/CCM-EEPROM) +	 * -------------------------------------------- */ +	cp->cp_pbpar |=  0x0000000E;	/* set  bits	*/ +	cp->cp_pbpar &= ~0x00000001;	/* reset bit	*/ + +	/* ---------------------------------------------- +	 * In/Out or per. Function 0/1 +	 * PBDIR[28] = 1 [0x00000008] -> PERI1: SPIMISO +	 * PBDIR[29] = 1 [0x00000004] -> PERI1: SPIMOSI +	 * PBDIR[30] = 1 [0x00000002] -> PERI1: SPICLK +	 * PBDIR[31] = 1 [0x00000001] -> GPIO OUT: CS for PCUE/CCM-EEPROM +	 * ---------------------------------------------- */ +	cp->cp_pbdir |= 0x0000000F; + +	/* ---------------------------------------------- +	 * open drain or active output +	 * PBODR[28] = 1 [0x00000008] -> open drain: SPIMISO +	 * PBODR[29] = 0 [0x00000004] -> active output SPIMOSI +	 * PBODR[30] = 0 [0x00000002] -> active output: SPICLK +	 * PBODR[31] = 0 [0x00000001] -> active output: GPIO OUT: CS for PCUE/CCM +	 * ---------------------------------------------- */ + +	cp->cp_pbodr |=  0x00000008; +	cp->cp_pbodr &= ~0x00000007; + +	/* Initialize the parameter ram. +	 * We need to make sure many things are initialized to zero +	 */ +	spi->spi_rstate	= 0; +	spi->spi_rdp	= 0; +	spi->spi_rbptr	= 0; +	spi->spi_rbc	= 0; +	spi->spi_rxtmp	= 0; +	spi->spi_tstate	= 0; +	spi->spi_tdp	= 0; +	spi->spi_tbptr	= 0; +	spi->spi_tbc	= 0; +	spi->spi_txtmp	= 0; + +	/* Allocate space for one transmit and one receive buffer +	 * descriptor in the DP ram +	 */ +#ifdef CONFIG_SYS_ALLOC_DPRAM +	dpaddr = dpram_alloc_align (sizeof(cbd_t)*2, 8); +#else +	dpaddr = CPM_SPI_BASE; +#endif + +/* 3 */ +	/* Set up the SPI parameters in the parameter ram */ +	spi->spi_rbase = dpaddr; +	spi->spi_tbase = dpaddr + sizeof (cbd_t); + +	/***********IMPORTANT******************/ + +	/* +	 * Setting transmit and receive buffer descriptor pointers +	 * initially to rbase and tbase. Only the microcode patches +	 * documentation talks about initializing this pointer. This +	 * is missing from the sample I2C driver. If you dont +	 * initialize these pointers, the kernel hangs. +	 */ +	spi->spi_rbptr = spi->spi_rbase; +	spi->spi_tbptr = spi->spi_tbase; + +/* 4 */ +#ifdef CONFIG_SYS_SPI_UCODE_PATCH +	/* +	 *  Initialize required parameters if using microcode patch. +	 */ +	spi->spi_rstate = 0; +	spi->spi_tstate = 0; +#else +	/* Init SPI Tx + Rx Parameters */ +	while (cp->cp_cpcr & CPM_CR_FLG) +		; +	cp->cp_cpcr = mk_cr_cmd(CPM_CR_CH_SPI, CPM_CR_INIT_TRX) | CPM_CR_FLG; +	while (cp->cp_cpcr & CPM_CR_FLG) +		; +#endif	/* CONFIG_SYS_SPI_UCODE_PATCH */ + +/* 5 */ +	/* Set SDMA configuration register */ +	immr->im_siu_conf.sc_sdcr = 0x0001; + +/* 6 */ +	/* Set to big endian. */ +	spi->spi_tfcr = SMC_EB; +	spi->spi_rfcr = SMC_EB; + +/* 7 */ +	/* Set maximum receive size. */ +	spi->spi_mrblr = MAX_BUFFER; + +/* 8 + 9 */ +	/* tx and rx buffer descriptors */ +	tbdf = (cbd_t *) & cp->cp_dpmem[spi->spi_tbase]; +	rbdf = (cbd_t *) & cp->cp_dpmem[spi->spi_rbase]; + +	tbdf->cbd_sc &= ~BD_SC_READY; +	rbdf->cbd_sc &= ~BD_SC_EMPTY; + +	/* Set the bd's rx and tx buffer address pointers */ +	rbdf->cbd_bufaddr = (ulong) rxbuf; +	tbdf->cbd_bufaddr = (ulong) txbuf; + +/* 10 + 11 */ +	cp->cp_spim = 0;			/* Mask  all SPI events */ +	cp->cp_spie = SPI_EMASK;		/* Clear all SPI events	*/ + +	return; +} + +/* ************************************************************************** + * + *  Function:    spi_init_r + * + *  Description: Init SPI-Controller (RAM part) - + *		 The malloc engine is ready and we can move our buffers to + *		 normal RAM + * + *  return:      --- + * + * *********************************************************************** */ +void spi_init_r (void) +{ +	volatile cpm8xx_t *cp; +	volatile spi_t *spi; +	volatile immap_t *immr; +	volatile cbd_t *tbdf, *rbdf; + +	immr = (immap_t *)  CONFIG_SYS_IMMR; +	cp   = (cpm8xx_t *) &immr->im_cpm; + +#ifdef CONFIG_SYS_SPI_UCODE_PATCH +	spi  = (spi_t *)&cp->cp_dpmem[spi->spi_rpbase]; +#else +	spi  = (spi_t *)&cp->cp_dparam[PROFF_SPI]; +	/* Disable relocation */ +	spi->spi_rpbase = 0; +#endif + +	/* tx and rx buffer descriptors */ +	tbdf = (cbd_t *) & cp->cp_dpmem[spi->spi_tbase]; +	rbdf = (cbd_t *) & cp->cp_dpmem[spi->spi_rbase]; + +	/* Allocate memory for RX and TX buffers */ +	rxbuf = (uchar *) malloc (MAX_BUFFER); +	txbuf = (uchar *) malloc (MAX_BUFFER); + +	rbdf->cbd_bufaddr = (ulong) rxbuf; +	tbdf->cbd_bufaddr = (ulong) txbuf; + +	return; +} + +/**************************************************************************** + *  Function:    spi_write + **************************************************************************** */ +ssize_t spi_write (uchar *addr, int alen, uchar *buffer, int len) +{ +	int i; + +	memset(rxbuf, 0, MAX_BUFFER); +	memset(txbuf, 0, MAX_BUFFER); +	*txbuf = SPI_EEPROM_WREN;		/* write enable		*/ +	spi_xfer(1); +	memcpy(txbuf, addr, alen); +	*txbuf = SPI_EEPROM_WRITE;		/* WRITE memory array	*/ +	memcpy(alen + txbuf, buffer, len); +	spi_xfer(alen + len); +						/* ignore received data	*/ +	for (i = 0; i < 1000; i++) { +		*txbuf = SPI_EEPROM_RDSR;	/* read status		*/ +		txbuf[1] = 0; +		spi_xfer(2); +		if (!(rxbuf[1] & 1)) { +			break; +		} +		udelay(1000); +	} +	if (i >= 1000) { +		printf ("*** spi_write: Time out while writing!\n"); +	} + +	return len; +} + +/**************************************************************************** + *  Function:    spi_read + **************************************************************************** */ +ssize_t spi_read (uchar *addr, int alen, uchar *buffer, int len) +{ +	memset(rxbuf, 0, MAX_BUFFER); +	memset(txbuf, 0, MAX_BUFFER); +	memcpy(txbuf, addr, alen); +	*txbuf = SPI_EEPROM_READ;		/* READ memory array	*/ + +	/* +	 * There is a bug in 860T (?) that cuts the last byte of input +	 * if we're reading into DPRAM. The solution we choose here is +	 * to always read len+1 bytes (we have one extra byte at the +	 * end of the buffer). +	 */ +	spi_xfer(alen + len + 1); +	memcpy(buffer, alen + rxbuf, len); + +	return len; +} + +/**************************************************************************** + *  Function:    spi_xfer + **************************************************************************** */ +ssize_t spi_xfer (size_t count) +{ +	volatile immap_t *immr; +	volatile cpm8xx_t *cp; +	volatile spi_t *spi; +	cbd_t *tbdf, *rbdf; +	ushort loop; +	int tm; + +	DPRINT (("*** spi_xfer entered ***\n")); + +	immr = (immap_t *) CONFIG_SYS_IMMR; +	cp   = (cpm8xx_t *) &immr->im_cpm; + +#ifdef CONFIG_SYS_SPI_UCODE_PATCH +	spi  = (spi_t *)&cp->cp_dpmem[spi->spi_rpbase]; +#else +	spi  = (spi_t *)&cp->cp_dparam[PROFF_SPI]; +	/* Disable relocation */ +	spi->spi_rpbase = 0; +#endif + +	tbdf = (cbd_t *) & cp->cp_dpmem[spi->spi_tbase]; +	rbdf = (cbd_t *) & cp->cp_dpmem[spi->spi_rbase]; + +	/* Set CS for device */ +	cp->cp_pbdat &= ~0x0001; + +	/* Setting tx bd status and data length */ +	tbdf->cbd_sc  = BD_SC_READY | BD_SC_LAST | BD_SC_WRAP; +	tbdf->cbd_datlen = count; + +	DPRINT (("*** spi_xfer: Bytes to be xferred: %d ***\n", +							tbdf->cbd_datlen)); + +	/* Setting rx bd status and data length */ +	rbdf->cbd_sc = BD_SC_EMPTY | BD_SC_WRAP; +	rbdf->cbd_datlen = 0;	 /* rx length has no significance */ + +	loop = cp->cp_spmode & SPMODE_LOOP; +	cp->cp_spmode = /*SPMODE_DIV16	|*/	/* BRG/16 mode not used here */ +			loop		| +			SPMODE_REV	| +			SPMODE_MSTR	| +			SPMODE_EN	| +			SPMODE_LEN(8)	|	/* 8 Bits per char */ +			SPMODE_PM(0x8) ;	/* medium speed */ +	cp->cp_spim = 0;			/* Mask  all SPI events */ +	cp->cp_spie = SPI_EMASK;		/* Clear all SPI events	*/ + +	/* start spi transfer */ +	DPRINT (("*** spi_xfer: Performing transfer ...\n")); +	cp->cp_spcom |= SPI_STR;		/* Start transmit */ + +	/* -------------------------------- +	 * Wait for SPI transmit to get out +	 * or time out (1 second = 1000 ms) +	 * -------------------------------- */ +	for (tm=0; tm<1000; ++tm) { +		if (cp->cp_spie & SPI_TXB) {	/* Tx Buffer Empty */ +			DPRINT (("*** spi_xfer: Tx buffer empty\n")); +			break; +		} +		if ((tbdf->cbd_sc & BD_SC_READY) == 0) { +			DPRINT (("*** spi_xfer: Tx BD done\n")); +			break; +		} +		udelay (1000); +	} +	if (tm >= 1000) { +		printf ("*** spi_xfer: Time out while xferring to/from SPI!\n"); +	} +	DPRINT (("*** spi_xfer: ... transfer ended\n")); + +#ifdef	DEBUG +	printf ("\nspi_xfer: txbuf after xfer\n"); +	memdump ((void *) txbuf, 16);	/* dump of txbuf before transmit */ +	printf ("spi_xfer: rxbuf after xfer\n"); +	memdump ((void *) rxbuf, 16);	/* dump of rxbuf after transmit */ +	printf ("\n"); +#endif + +	/* Clear CS for device */ +	cp->cp_pbdat |= 0x0001; + +	return count; +} +#endif	/* CONFIG_SPI || (CONFIG_POST & CONFIG_SYS_POST_SPI) */ + +/* + * SPI test + * + * The Serial Peripheral Interface (SPI) is tested in the local loopback mode. + * The interface is configured accordingly and several packets + * are transfered. The configurable test parameters are: + *   TEST_MIN_LENGTH - minimum size of packet to transfer + *   TEST_MAX_LENGTH - maximum size of packet to transfer + *   TEST_NUM - number of tests + */ + +#if CONFIG_POST & CONFIG_SYS_POST_SPI + +#define TEST_MIN_LENGTH		1 +#define TEST_MAX_LENGTH		MAX_BUFFER +#define TEST_NUM		1 + +static void packet_fill (char * packet, int length) +{ +	char c = (char) length; +	int i; + +	for (i = 0; i < length; i++) +	{ +	    packet[i] = c++; +	} +} + +static int packet_check (char * packet, int length) +{ +	char c = (char) length; +	int i; + +	for (i = 0; i < length; i++) { +	    if (packet[i] != c++) return -1; +	} + +	return 0; +} + +int spi_post_test (int flags) +{ +	int res = -1; +	volatile immap_t *immr = (immap_t *) CONFIG_SYS_IMMR; +	volatile cpm8xx_t *cp = (cpm8xx_t *) & immr->im_cpm; +	int i; +	int l; + +	spi_init_f (); +	spi_init_r (); + +	cp->cp_spmode |= SPMODE_LOOP; + +	for (i = 0; i < TEST_NUM; i++) { +		for (l = TEST_MIN_LENGTH; l <= TEST_MAX_LENGTH; l += 8) { +			packet_fill ((char *)txbuf, l); + +			spi_xfer (l); + +			if (packet_check ((char *)rxbuf, l) < 0) { +				goto Done; +			} +		} +	} + +	res = 0; + +      Done: + +	cp->cp_spmode &= ~SPMODE_LOOP; + +	/* +	 * SCC2 parameter RAM space overlaps +	 * the SPI parameter RAM space. So we need to restore +	 * the SCC2 configuration if it is used by UART. +	 */ + +#if !defined(CONFIG_8xx_CONS_NONE) +	serial_reinit_all (); +#endif + +	if (res != 0) { +		post_log ("SPI test failed\n"); +	} + +	return res; +} +#endif	/* CONFIG_POST & CONFIG_SYS_POST_SPI */ diff --git a/arch/powerpc/cpu/mpc8xx/start.S b/arch/powerpc/cpu/mpc8xx/start.S new file mode 100644 index 000000000..7cf602fd4 --- /dev/null +++ b/arch/powerpc/cpu/mpc8xx/start.S @@ -0,0 +1,684 @@ +/* + *  Copyright (C) 1998	Dan Malek <dmalek@jlc.net> + *  Copyright (C) 1999	Magnus Damm <kieraypc01.p.y.kie.era.ericsson.se> + *  Copyright (C) 2000,2001,2002 Wolfgang Denk <wd@denx.de> + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +/*  U-Boot - Startup Code for PowerPC based Embedded Boards + * + * + *  The processor starts at 0x00000100 and the code is executed + *  from flash. The code is organized to be at an other address + *  in memory, but as long we don't jump around before relocating, + *  board_init lies at a quite high address and when the cpu has + *  jumped there, everything is ok. + *  This works because the cpu gives the FLASH (CS0) the whole + *  address space at startup, and board_init lies as a echo of + *  the flash somewhere up there in the memory map. + * + *  board_init will change CS0 to be positioned at the correct + *  address and (s)dram will be positioned at address 0 + */ +#include <config.h> +#include <mpc8xx.h> +#include <timestamp.h> +#include <version.h> + +#define CONFIG_8xx 1		/* needed for Linux kernel header files */ +#define _LINUX_CONFIG_H 1	/* avoid reading Linux autoconf.h file	*/ + +#include <ppc_asm.tmpl> +#include <ppc_defs.h> + +#include <asm/cache.h> +#include <asm/mmu.h> + +#ifndef  CONFIG_IDENT_STRING +#define  CONFIG_IDENT_STRING "" +#endif + +/* We don't want the  MMU yet. +*/ +#undef	MSR_KERNEL +#define MSR_KERNEL ( MSR_ME | MSR_RI )	/* Machine Check and Recoverable Interr. */ + +/* + * Set up GOT: Global Offset Table + * + * Use r12 to access the GOT + */ +	START_GOT +	GOT_ENTRY(_GOT2_TABLE_) +	GOT_ENTRY(_FIXUP_TABLE_) + +	GOT_ENTRY(_start) +	GOT_ENTRY(_start_of_vectors) +	GOT_ENTRY(_end_of_vectors) +	GOT_ENTRY(transfer_to_handler) + +	GOT_ENTRY(__init_end) +	GOT_ENTRY(_end) +	GOT_ENTRY(__bss_start) +	END_GOT + +/* + * r3 - 1st arg to board_init(): IMMP pointer + * r4 - 2nd arg to board_init(): boot flag + */ +	.text +	.long	0x27051956		/* U-Boot Magic Number			*/ +	.globl	version_string +version_string: +	.ascii U_BOOT_VERSION +	.ascii " (", U_BOOT_DATE, " - ", U_BOOT_TIME, ")" +	.ascii CONFIG_IDENT_STRING, "\0" + +	. = EXC_OFF_SYS_RESET +	.globl	_start +_start: +	lis	r3, CONFIG_SYS_IMMR@h		/* position IMMR */ +	mtspr	638, r3 +	li	r21, BOOTFLAG_COLD	/* Normal Power-On: Boot from FLASH	*/ +	b	boot_cold + +	. = EXC_OFF_SYS_RESET + 0x10 + +	.globl	_start_warm +_start_warm: +	li	r21, BOOTFLAG_WARM	/* Software reboot			*/ +	b	boot_warm + +boot_cold: +boot_warm: + +	/* Initialize machine status; enable machine check interrupt		*/ +	/*----------------------------------------------------------------------*/ +	li	r3, MSR_KERNEL		/* Set ME, RI flags */ +	mtmsr	r3 +	mtspr	SRR1, r3		/* Make SRR1 match MSR */ + +	mfspr	r3, ICR			/* clear Interrupt Cause Register */ + +	/* Initialize debug port registers					*/ +	/*----------------------------------------------------------------------*/ +	xor	r0, r0, r0		/* Clear R0 */ +	mtspr	LCTRL1, r0		/* Initialize debug port regs */ +	mtspr	LCTRL2, r0 +	mtspr	COUNTA, r0 +	mtspr	COUNTB, r0 + +	/* Reset the caches							*/ +	/*----------------------------------------------------------------------*/ + +	mfspr	r3, IC_CST		/* Clear error bits */ +	mfspr	r3, DC_CST + +	lis	r3, IDC_UNALL@h		/* Unlock all */ +	mtspr	IC_CST, r3 +	mtspr	DC_CST, r3 + +	lis	r3, IDC_INVALL@h	/* Invalidate all */ +	mtspr	IC_CST, r3 +	mtspr	DC_CST, r3 + +	lis	r3, IDC_DISABLE@h	/* Disable data cache */ +	mtspr	DC_CST, r3 + +#if !defined(CONFIG_SYS_DELAYED_ICACHE) +					/* On IP860 and PCU E, +					 * we cannot enable IC yet +					 */ +	lis	r3, IDC_ENABLE@h	/* Enable instruction cache */ +#endif +	mtspr	IC_CST, r3 + +	/* invalidate all tlb's							*/ +	/*----------------------------------------------------------------------*/ + +	tlbia +	isync + +	/* +	 * Calculate absolute address in FLASH and jump there +	 *----------------------------------------------------------------------*/ + +	lis	r3, CONFIG_SYS_MONITOR_BASE@h +	ori	r3, r3, CONFIG_SYS_MONITOR_BASE@l +	addi	r3, r3, in_flash - _start + EXC_OFF_SYS_RESET +	mtlr	r3 +	blr + +in_flash: + +	/* initialize some SPRs that are hard to access from C			*/ +	/*----------------------------------------------------------------------*/ + +	lis	r3, CONFIG_SYS_IMMR@h		/* pass IMMR as arg1 to C routine */ +	ori	r1, r3, CONFIG_SYS_INIT_SP_OFFSET /* set up the stack in internal DPRAM */ +	/* Note: R0 is still 0 here */ +	stwu	r0, -4(r1)		/* clear final stack frame so that	*/ +	stwu	r0, -4(r1)		/* stack backtraces terminate cleanly	*/ + +	/* +	 * Disable serialized ifetch and show cycles +	 * (i.e. set processor to normal mode). +	 * This is also a silicon bug workaround, see errata +	 */ + +	li	r2, 0x0007 +	mtspr	ICTRL, r2 + +	/* Set up debug mode entry */ + +	lis	r2, CONFIG_SYS_DER@h +	ori	r2, r2, CONFIG_SYS_DER@l +	mtspr	DER, r2 + +	/* let the C-code set up the rest					*/ +	/*									*/ +	/* Be careful to keep code relocatable !				*/ +	/*----------------------------------------------------------------------*/ + +	GET_GOT			/* initialize GOT access			*/ + +	/* r3: IMMR */ +	bl	cpu_init_f	/* run low-level CPU init code     (from Flash)	*/ + +	mr	r3, r21 +	/* r3: BOOTFLAG */ +	bl	board_init_f	/* run 1st part of board init code (from Flash) */ + + +	.globl	_start_of_vectors +_start_of_vectors: + +/* Machine check */ +	STD_EXCEPTION(0x200, MachineCheck, MachineCheckException) + +/* Data Storage exception.  "Never" generated on the 860. */ +	STD_EXCEPTION(0x300, DataStorage, UnknownException) + +/* Instruction Storage exception.  "Never" generated on the 860. */ +	STD_EXCEPTION(0x400, InstStorage, UnknownException) + +/* External Interrupt exception. */ +	STD_EXCEPTION(0x500, ExtInterrupt, external_interrupt) + +/* Alignment exception. */ +	. = 0x600 +Alignment: +	EXCEPTION_PROLOG(SRR0, SRR1) +	mfspr	r4,DAR +	stw	r4,_DAR(r21) +	mfspr	r5,DSISR +	stw	r5,_DSISR(r21) +	addi	r3,r1,STACK_FRAME_OVERHEAD +	EXC_XFER_TEMPLATE(Alignment, AlignmentException, MSR_KERNEL, COPY_EE) + +/* Program check exception */ +	. = 0x700 +ProgramCheck: +	EXCEPTION_PROLOG(SRR0, SRR1) +	addi	r3,r1,STACK_FRAME_OVERHEAD +	EXC_XFER_TEMPLATE(ProgramCheck, ProgramCheckException, +		MSR_KERNEL, COPY_EE) + +	/* No FPU on MPC8xx.  This exception is not supposed to happen. +	*/ +	STD_EXCEPTION(0x800, FPUnavailable, UnknownException) + +	/* I guess we could implement decrementer, and may have +	 * to someday for timekeeping. +	 */ +	STD_EXCEPTION(0x900, Decrementer, timer_interrupt) +	STD_EXCEPTION(0xa00, Trap_0a, UnknownException) +	STD_EXCEPTION(0xb00, Trap_0b, UnknownException) +	STD_EXCEPTION(0xc00, SystemCall, UnknownException) +	STD_EXCEPTION(0xd00, SingleStep, UnknownException) + +	STD_EXCEPTION(0xe00, Trap_0e, UnknownException) +	STD_EXCEPTION(0xf00, Trap_0f, UnknownException) + +	/* On the MPC8xx, this is a software emulation interrupt.  It occurs +	 * for all unimplemented and illegal instructions. +	 */ +	STD_EXCEPTION(0x1000, SoftEmu, SoftEmuException) + +	STD_EXCEPTION(0x1100, InstructionTLBMiss, UnknownException) +	STD_EXCEPTION(0x1200, DataTLBMiss, UnknownException) +	STD_EXCEPTION(0x1300, InstructionTLBError, UnknownException) +	STD_EXCEPTION(0x1400, DataTLBError, UnknownException) + +	STD_EXCEPTION(0x1500, Reserved5, UnknownException) +	STD_EXCEPTION(0x1600, Reserved6, UnknownException) +	STD_EXCEPTION(0x1700, Reserved7, UnknownException) +	STD_EXCEPTION(0x1800, Reserved8, UnknownException) +	STD_EXCEPTION(0x1900, Reserved9, UnknownException) +	STD_EXCEPTION(0x1a00, ReservedA, UnknownException) +	STD_EXCEPTION(0x1b00, ReservedB, UnknownException) + +	STD_EXCEPTION(0x1c00, DataBreakpoint, UnknownException) +	STD_EXCEPTION(0x1d00, InstructionBreakpoint, DebugException) +	STD_EXCEPTION(0x1e00, PeripheralBreakpoint, UnknownException) +	STD_EXCEPTION(0x1f00, DevPortBreakpoint, UnknownException) + + +	.globl	_end_of_vectors +_end_of_vectors: + + +	. = 0x2000 + +/* + * This code finishes saving the registers to the exception frame + * and jumps to the appropriate handler for the exception. + * Register r21 is pointer into trap frame, r1 has new stack pointer. + */ +	.globl	transfer_to_handler +transfer_to_handler: +	stw	r22,_NIP(r21) +	lis	r22,MSR_POW@h +	andc	r23,r23,r22 +	stw	r23,_MSR(r21) +	SAVE_GPR(7, r21) +	SAVE_4GPRS(8, r21) +	SAVE_8GPRS(12, r21) +	SAVE_8GPRS(24, r21) +	mflr	r23 +	andi.	r24,r23,0x3f00		/* get vector offset */ +	stw	r24,TRAP(r21) +	li	r22,0 +	stw	r22,RESULT(r21) +	mtspr	SPRG2,r22		/* r1 is now kernel sp */ +	lwz	r24,0(r23)		/* virtual address of handler */ +	lwz	r23,4(r23)		/* where to go when done */ +	mtspr	SRR0,r24 +	mtspr	SRR1,r20 +	mtlr	r23 +	SYNC +	rfi				/* jump to handler, enable MMU */ + +int_return: +	mfmsr	r28			/* Disable interrupts */ +	li	r4,0 +	ori	r4,r4,MSR_EE +	andc	r28,r28,r4 +	SYNC				/* Some chip revs need this... */ +	mtmsr	r28 +	SYNC +	lwz	r2,_CTR(r1) +	lwz	r0,_LINK(r1) +	mtctr	r2 +	mtlr	r0 +	lwz	r2,_XER(r1) +	lwz	r0,_CCR(r1) +	mtspr	XER,r2 +	mtcrf	0xFF,r0 +	REST_10GPRS(3, r1) +	REST_10GPRS(13, r1) +	REST_8GPRS(23, r1) +	REST_GPR(31, r1) +	lwz	r2,_NIP(r1)		/* Restore environment */ +	lwz	r0,_MSR(r1) +	mtspr	SRR0,r2 +	mtspr	SRR1,r0 +	lwz	r0,GPR0(r1) +	lwz	r2,GPR2(r1) +	lwz	r1,GPR1(r1) +	SYNC +	rfi + +/* Cache functions. +*/ +	.globl	icache_enable +icache_enable: +	SYNC +	lis	r3, IDC_INVALL@h +	mtspr	IC_CST, r3 +	lis	r3, IDC_ENABLE@h +	mtspr	IC_CST, r3 +	blr + +	.globl	icache_disable +icache_disable: +	SYNC +	lis	r3, IDC_DISABLE@h +	mtspr	IC_CST, r3 +	blr + +	.globl	icache_status +icache_status: +	mfspr	r3, IC_CST +	srwi	r3, r3, 31	/* >>31 => select bit 0 */ +	blr + +	.globl	dcache_enable +dcache_enable: +#if 0 +	SYNC +#endif +#if 1 +	lis	r3, 0x0400		/* Set cache mode with MMU off */ +	mtspr	MD_CTR, r3 +#endif + +	lis	r3, IDC_INVALL@h +	mtspr	DC_CST, r3 +#if 0 +	lis	r3, DC_SFWT@h +	mtspr	DC_CST, r3 +#endif +	lis	r3, IDC_ENABLE@h +	mtspr	DC_CST, r3 +	blr + +	.globl	dcache_disable +dcache_disable: +	SYNC +	lis	r3, IDC_DISABLE@h +	mtspr	DC_CST, r3 +	lis	r3, IDC_INVALL@h +	mtspr	DC_CST, r3 +	blr + +	.globl	dcache_status +dcache_status: +	mfspr	r3, DC_CST +	srwi	r3, r3, 31	/* >>31 => select bit 0 */ +	blr + +	.globl	dc_read +dc_read: +	mtspr	DC_ADR, r3 +	mfspr	r3, DC_DAT +	blr + +/* + * unsigned int get_immr (unsigned int mask) + * + * return (mask ? (IMMR & mask) : IMMR); + */ +	.globl	get_immr +get_immr: +	mr	r4,r3		/* save mask */ +	mfspr	r3, IMMR	/* IMMR */ +	cmpwi	0,r4,0		/* mask != 0 ? */ +	beq	4f +	and	r3,r3,r4	/* IMMR & mask */ +4: +	blr + +	.globl get_pvr +get_pvr: +	mfspr	r3, PVR +	blr + + +	.globl wr_ic_cst +wr_ic_cst: +	mtspr	IC_CST, r3 +	blr + +	.globl rd_ic_cst +rd_ic_cst: +	mfspr	r3, IC_CST +	blr + +	.globl wr_ic_adr +wr_ic_adr: +	mtspr	IC_ADR, r3 +	blr + + +	.globl wr_dc_cst +wr_dc_cst: +	mtspr	DC_CST, r3 +	blr + +	.globl rd_dc_cst +rd_dc_cst: +	mfspr	r3, DC_CST +	blr + +	.globl wr_dc_adr +wr_dc_adr: +	mtspr	DC_ADR, r3 +	blr + +/*------------------------------------------------------------------------------*/ + +/* + * void relocate_code (addr_sp, gd, addr_moni) + * + * This "function" does not return, instead it continues in RAM + * after relocating the monitor code. + * + * r3 = dest + * r4 = src + * r5 = length in bytes + * r6 = cachelinesize + */ +	.globl	relocate_code +relocate_code: +	mr	r1,  r3		/* Set new stack pointer		*/ +	mr	r9,  r4		/* Save copy of Global Data pointer	*/ +	mr	r10, r5		/* Save copy of Destination Address	*/ + +	GET_GOT +	mr	r3,  r5				/* Destination Address	*/ +	lis	r4, CONFIG_SYS_MONITOR_BASE@h		/* Source      Address	*/ +	ori	r4, r4, CONFIG_SYS_MONITOR_BASE@l +	lwz	r5, GOT(__init_end) +	sub	r5, r5, r4 +	li	r6, CONFIG_SYS_CACHELINE_SIZE		/* Cache Line Size	*/ + +	/* +	 * Fix GOT pointer: +	 * +	 * New GOT-PTR = (old GOT-PTR - CONFIG_SYS_MONITOR_BASE) + Destination Address +	 * +	 * Offset: +	 */ +	sub	r15, r10, r4 + +	/* First our own GOT */ +	add	r12, r12, r15 +	/* then the one used by the C code */ +	add	r30, r30, r15 + +	/* +	 * Now relocate code +	 */ + +	cmplw	cr1,r3,r4 +	addi	r0,r5,3 +	srwi.	r0,r0,2 +	beq	cr1,4f		/* In place copy is not necessary	*/ +	beq	7f		/* Protect against 0 count		*/ +	mtctr	r0 +	bge	cr1,2f + +	la	r8,-4(r4) +	la	r7,-4(r3) +1:	lwzu	r0,4(r8) +	stwu	r0,4(r7) +	bdnz	1b +	b	4f + +2:	slwi	r0,r0,2 +	add	r8,r4,r0 +	add	r7,r3,r0 +3:	lwzu	r0,-4(r8) +	stwu	r0,-4(r7) +	bdnz	3b + +/* + * Now flush the cache: note that we must start from a cache aligned + * address. Otherwise we might miss one cache line. + */ +4:	cmpwi	r6,0 +	add	r5,r3,r5 +	beq	7f		/* Always flush prefetch queue in any case */ +	subi	r0,r6,1 +	andc	r3,r3,r0 +	mr	r4,r3 +5:	dcbst	0,r4 +	add	r4,r4,r6 +	cmplw	r4,r5 +	blt	5b +	sync			/* Wait for all dcbst to complete on bus */ +	mr	r4,r3 +6:	icbi	0,r4 +	add	r4,r4,r6 +	cmplw	r4,r5 +	blt	6b +7:	sync			/* Wait for all icbi to complete on bus	*/ +	isync + +/* + * We are done. Do not return, instead branch to second part of board + * initialization, now running from RAM. + */ + +	addi	r0, r10, in_ram - _start + EXC_OFF_SYS_RESET +	mtlr	r0 +	blr + +in_ram: + +	/* +	 * Relocation Function, r12 point to got2+0x8000 +	 * +	 * Adjust got2 pointers, no need to check for 0, this code +	 * already puts a few entries in the table. +	 */ +	li	r0,__got2_entries@sectoff@l +	la	r3,GOT(_GOT2_TABLE_) +	lwz	r11,GOT(_GOT2_TABLE_) +	mtctr	r0 +	sub	r11,r3,r11 +	addi	r3,r3,-4 +1:	lwzu	r0,4(r3) +	cmpwi	r0,0 +	beq-	2f +	add	r0,r0,r11 +	stw	r0,0(r3) +2:	bdnz	1b + +	/* +	 * Now adjust the fixups and the pointers to the fixups +	 * in case we need to move ourselves again. +	 */ +	li	r0,__fixup_entries@sectoff@l +	lwz	r3,GOT(_FIXUP_TABLE_) +	cmpwi	r0,0 +	mtctr	r0 +	addi	r3,r3,-4 +	beq	4f +3:	lwzu	r4,4(r3) +	lwzux	r0,r4,r11 +	add	r0,r0,r11 +	stw	r10,0(r3) +	stw	r0,0(r4) +	bdnz	3b +4: +clear_bss: +	/* +	 * Now clear BSS segment +	 */ +	lwz	r3,GOT(__bss_start) +	lwz	r4,GOT(_end) + +	cmplw	0, r3, r4 +	beq	6f + +	li	r0, 0 +5: +	stw	r0, 0(r3) +	addi	r3, r3, 4 +	cmplw	0, r3, r4 +	bne	5b +6: + +	mr	r3, r9		/* Global Data pointer		*/ +	mr	r4, r10		/* Destination Address		*/ +	bl	board_init_r + +	/* +	 * Copy exception vector code to low memory +	 * +	 * r3: dest_addr +	 * r7: source address, r8: end address, r9: target address +	 */ +	.globl	trap_init +trap_init: +	mflr	r4			/* save link register		*/ +	GET_GOT +	lwz	r7, GOT(_start) +	lwz	r8, GOT(_end_of_vectors) + +	li	r9, 0x100		/* reset vector always at 0x100 */ + +	cmplw	0, r7, r8 +	bgelr				/* return if r7>=r8 - just in case */ +1: +	lwz	r0, 0(r7) +	stw	r0, 0(r9) +	addi	r7, r7, 4 +	addi	r9, r9, 4 +	cmplw	0, r7, r8 +	bne	1b + +	/* +	 * relocate `hdlr' and `int_return' entries +	 */ +	li	r7, .L_MachineCheck - _start + EXC_OFF_SYS_RESET +	li	r8, Alignment - _start + EXC_OFF_SYS_RESET +2: +	bl	trap_reloc +	addi	r7, r7, 0x100		/* next exception vector	*/ +	cmplw	0, r7, r8 +	blt	2b + +	li	r7, .L_Alignment - _start + EXC_OFF_SYS_RESET +	bl	trap_reloc + +	li	r7, .L_ProgramCheck - _start + EXC_OFF_SYS_RESET +	bl	trap_reloc + +	li	r7, .L_FPUnavailable - _start + EXC_OFF_SYS_RESET +	li	r8, SystemCall - _start + EXC_OFF_SYS_RESET +3: +	bl	trap_reloc +	addi	r7, r7, 0x100		/* next exception vector	*/ +	cmplw	0, r7, r8 +	blt	3b + +	li	r7, .L_SingleStep - _start + EXC_OFF_SYS_RESET +	li	r8, _end_of_vectors - _start + EXC_OFF_SYS_RESET +4: +	bl	trap_reloc +	addi	r7, r7, 0x100		/* next exception vector	*/ +	cmplw	0, r7, r8 +	blt	4b + +	mtlr	r4			/* restore link register	*/ +	blr diff --git a/arch/powerpc/cpu/mpc8xx/traps.c b/arch/powerpc/cpu/mpc8xx/traps.c new file mode 100644 index 000000000..343dced31 --- /dev/null +++ b/arch/powerpc/cpu/mpc8xx/traps.c @@ -0,0 +1,241 @@ +/* + * linux/arch/powerpc/kernel/traps.c + * + * Copyright (C) 1995-1996  Gary Thomas (gdt@linuxppc.org) + * + * Modified by Cort Dougan (cort@cs.nmt.edu) + * and Paul Mackerras (paulus@cs.anu.edu.au) + * + * (C) Copyright 2000 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +/* + * This file handles the architecture-dependent parts of hardware exceptions + */ + +#include <common.h> +#include <command.h> +#include <kgdb.h> +#include <asm/processor.h> + +#if defined(CONFIG_CMD_BEDBUG) +extern void do_bedbug_breakpoint(struct pt_regs *); +#endif + +/* Returns 0 if exception not found and fixup otherwise.  */ +extern unsigned long search_exception_table(unsigned long); + +/* THIS NEEDS CHANGING to use the board info structure. +*/ +#define END_OF_MEM	0x02000000 + +/* + * Trap & Exception support + */ + +void +print_backtrace(unsigned long *sp) +{ +	int cnt = 0; +	unsigned long i; + +	printf("Call backtrace: "); +	while (sp) { +		if ((uint)sp > END_OF_MEM) +			break; + +		i = sp[1]; +		if (cnt++ % 7 == 0) +			printf("\n"); +		printf("%08lX ", i); +		if (cnt > 32) break; +		sp = (unsigned long *)*sp; +	} +	printf("\n"); +} + +void show_regs(struct pt_regs * regs) +{ +	int i; + +	printf("NIP: %08lX XER: %08lX LR: %08lX REGS: %p TRAP: %04lx DAR: %08lX\n", +	       regs->nip, regs->xer, regs->link, regs, regs->trap, regs->dar); +	printf("MSR: %08lx EE: %01x PR: %01x FP: %01x ME: %01x IR/DR: %01x%01x\n", +	       regs->msr, regs->msr&MSR_EE ? 1 : 0, regs->msr&MSR_PR ? 1 : 0, +	       regs->msr & MSR_FP ? 1 : 0,regs->msr&MSR_ME ? 1 : 0, +	       regs->msr&MSR_IR ? 1 : 0, +	       regs->msr&MSR_DR ? 1 : 0); + +	printf("\n"); +	for (i = 0;  i < 32;  i++) { +		if ((i % 8) == 0) +		{ +			printf("GPR%02d: ", i); +		} + +		printf("%08lX ", regs->gpr[i]); +		if ((i % 8) == 7) +		{ +			printf("\n"); +		} +	} +} + + +void +_exception(int signr, struct pt_regs *regs) +{ +	show_regs(regs); +	print_backtrace((unsigned long *)regs->gpr[1]); +	panic("Exception in kernel pc %lx signal %d",regs->nip,signr); +} + +void +MachineCheckException(struct pt_regs *regs) +{ +	unsigned long fixup; + +	/* Probing PCI using config cycles cause this exception +	 * when a device is not present.  Catch it and return to +	 * the PCI exception handler. +	 */ +	if ((fixup = search_exception_table(regs->nip)) != 0) { +		regs->nip = fixup; +		return; +	} + +#if defined(CONFIG_CMD_KGDB) +	if (debugger_exception_handler && (*debugger_exception_handler)(regs)) +		return; +#endif + +	printf("Machine check in kernel mode.\n"); +	printf("Caused by (from msr): "); +	printf("regs %p ",regs); +	switch( regs->msr & 0x000F0000) { +	case (0x80000000>>12): +		printf("Machine check signal - probably due to mm fault\n" +			"with mmu off\n"); +		break; +	case (0x80000000>>13): +		printf("Transfer error ack signal\n"); +		break; +	case (0x80000000>>14): +		printf("Data parity signal\n"); +		break; +	case (0x80000000>>15): +		printf("Address parity signal\n"); +		break; +	default: +		printf("Unknown values in msr\n"); +	} +	show_regs(regs); +	print_backtrace((unsigned long *)regs->gpr[1]); +	panic("machine check"); +} + +void +AlignmentException(struct pt_regs *regs) +{ +#if defined(CONFIG_CMD_KGDB) +	if (debugger_exception_handler && (*debugger_exception_handler)(regs)) +		return; +#endif +	show_regs(regs); +	print_backtrace((unsigned long *)regs->gpr[1]); +	panic("Alignment Exception"); +} + +void +ProgramCheckException(struct pt_regs *regs) +{ +#if defined(CONFIG_CMD_KGDB) +	if (debugger_exception_handler && (*debugger_exception_handler)(regs)) +		return; +#endif +	show_regs(regs); +	print_backtrace((unsigned long *)regs->gpr[1]); +	panic("Program Check Exception"); +} + +void +SoftEmuException(struct pt_regs *regs) +{ +#if defined(CONFIG_CMD_KGDB) +	if (debugger_exception_handler && (*debugger_exception_handler)(regs)) +		return; +#endif +	show_regs(regs); +	print_backtrace((unsigned long *)regs->gpr[1]); +	panic("Software Emulation Exception"); +} + + +void +UnknownException(struct pt_regs *regs) +{ +#if defined(CONFIG_CMD_KGDB) +	if (debugger_exception_handler && (*debugger_exception_handler)(regs)) +		return; +#endif +	printf("Bad trap at PC: %lx, SR: %lx, vector=%lx\n", +	       regs->nip, regs->msr, regs->trap); +	_exception(0, regs); +} + +void +DebugException(struct pt_regs *regs) +{ +  printf("Debugger trap at @ %lx\n", regs->nip ); +  show_regs(regs); +#if defined(CONFIG_CMD_BEDBUG) +  do_bedbug_breakpoint( regs ); +#endif +} + +/* Probe an address by reading.  If not present, return -1, otherwise + * return 0. + */ +int +addr_probe(uint *addr) +{ +#if 0 +	int	retval; + +	__asm__ __volatile__(			\ +		"1:	lwz %0,0(%1)\n"		\ +		"	eieio\n"		\ +		"	li %0,0\n"		\ +		"2:\n"				\ +		".section .fixup,\"ax\"\n"	\ +		"3:	li %0,-1\n"		\ +		"	b 2b\n"			\ +		".section __ex_table,\"a\"\n"	\ +		"	.align 2\n"		\ +		"	.long 1b,3b\n"		\ +		".text"				\ +		: "=r" (retval) : "r"(addr)); + +	return (retval); +#endif +	return 0; +} diff --git a/arch/powerpc/cpu/mpc8xx/upatch.c b/arch/powerpc/cpu/mpc8xx/upatch.c new file mode 100644 index 000000000..a8cb735ab --- /dev/null +++ b/arch/powerpc/cpu/mpc8xx/upatch.c @@ -0,0 +1,194 @@ +#include <common.h> +#include <commproc.h> + +#if defined(CONFIG_SYS_I2C_UCODE_PATCH) || defined(CONFIG_SYS_SPI_UCODE_PATCH) || \ +    defined(CONFIG_SYS_SMC_UCODE_PATCH) + +static void UcodeCopy (volatile cpm8xx_t *cpm); + +void cpm_load_patch (volatile immap_t *immr) +{ +	immr->im_cpm.cp_rccr &= ~0x0003;	/* Disable microcode program area */ + +	UcodeCopy ((cpm8xx_t *)&immr->im_cpm);	/* Copy ucode patch to DPRAM   */ +#ifdef CONFIG_SYS_SPI_UCODE_PATCH +    { +	volatile spi_t *spi = (spi_t *) & immr->im_cpm.cp_dparam[PROFF_SPI]; +	/* Activate the microcode per the instructions in the microcode manual */ +	/* NOTE:  We're only relocating the SPI parameters (not I2C).          */ +	immr->im_cpm.cp_cpmcr1 = 0x802a;	/* Write Trap register 1 value */ +	immr->im_cpm.cp_cpmcr2 = 0x8028;	/* Write Trap register 2 value */ +	spi->spi_rpbase = CONFIG_SYS_SPI_DPMEM_OFFSET;	/* Where to relocte SPI params */ +    } +#endif + +#ifdef CONFIG_SYS_I2C_UCODE_PATCH +    { +	volatile iic_t *iip = (iic_t *) & immr->im_cpm.cp_dparam[PROFF_IIC]; +	/* Activate the microcode per the instructions in the microcode manual */ +	/* NOTE:  We're only relocating the I2C parameters (not SPI).          */ +	immr->im_cpm.cp_cpmcr3 = 0x802e;	/* Write Trap register 3 value */ +	immr->im_cpm.cp_cpmcr4 = 0x802c;	/* Write Trap register 4 value */ +	iip->iic_rpbase = CONFIG_SYS_I2C_DPMEM_OFFSET;	/* Where to relocte I2C params */ +    } +#endif + +#ifdef CONFIG_SYS_SMC_UCODE_PATCH +    { +	volatile smc_uart_t *up = (smc_uart_t *) & immr->im_cpm.cp_dparam[PROFF_SMC1]; +	/* Activate the microcode per the instructions in the microcode manual */ +	/* NOTE:  We're only relocating the SMC parameters.                    */ +	immr->im_cpm.cp_cpmcr1 = 0x8080;	/* Write Trap register 1 value */ +	immr->im_cpm.cp_cpmcr2 = 0x8088;	/* Write Trap register 2 value */ +	up->smc_rpbase = CONFIG_SYS_SMC_DPMEM_OFFSET;	/* Where to relocte SMC params */ +    } +#endif + +	/* +	 * Enable DPRAM microcode to execute from the first 512 bytes +	 * and a 256 byte extension of DPRAM. +	 */ +#ifdef CONFIG_SYS_SMC_UCODE_PATCH +	immr->im_cpm.cp_rccr |= 0x0002; +#else +	immr->im_cpm.cp_rccr |= 0x0001; +#endif +} + +#if defined(CONFIG_SYS_I2C_UCODE_PATCH) || defined(CONFIG_SYS_SPI_UCODE_PATCh) +static ulong patch_2000[] = { +	0x7FFFEFD9, 0x3FFD0000, 0x7FFB49F7, 0x7FF90000, +	0x5FEFADF7, 0x5F88ADF7, 0x5FEFAFF7, 0x5F88AFF7, +	0x3A9CFBC8, 0x77CAE1BB, 0xF4DE7FAD, 0xABAE9330, +	0x4E08FDCF, 0x6E0FAFF8, 0x7CCF76CF, 0xFDAFF9CF, +	0xABF88DC8, 0xAB5879F7, 0xB0927383, 0xDFD079F7, +	0xB090E6BB, 0xE5BBE74F, 0xB3FA6F0F, 0x6FFB76CE, +	0xEE0CF9CF, 0x2BFBEFEF, 0xCFEEF9CF, 0x76CEAD23, +	0x90B3DF99, 0x7FDDD0C1, 0x4BF847FD, 0x7CCF76CE, +	0xCFEF77CA, 0x7EAF7FAD, 0x7DFDF0B7, 0xEF7A7FCA, +	0x77CAFBC8, 0x6079E722, 0xFBC85FFF, 0xDFFF5FB3, +	0xFFFBFBC8, 0xF3C894A5, 0xE7C9EDF9, 0x7F9A7FAD, +	0x5F36AFE8, 0x5F5BFFDF, 0xDF95CB9E, 0xAF7D5FC3, +	0xAFED8C1B, 0x5FC3AFDD, 0x5FC5DF99, 0x7EFDB0B3, +	0x5FB3FFFE, 0xABAE5FB3, 0xFFFE5FD0, 0x600BE6BB, +	0x600B5FD0, 0xDFC827FB, 0xEFDF5FCA, 0xCFDE3A9C, +	0xE7C9EDF9, 0xF3C87F9E, 0x54CA7FED, 0x2D3A3637, +	0x756F7E9A, 0xF1CE37EF, 0x2E677FEE, 0x10EBADF8, +	0xEFDECFEA, 0xE52F7D9F, 0xE12BF1CE, 0x5F647E9A, +	0x4DF8CFEA, 0x5F717D9B, 0xEFEECFEA, 0x5F73E522, +	0xEFDE5F73, 0xCFDA0B61, 0x7385DF61, 0xE7C9EDF9, +	0x7E9A30D5, 0x1458BFFF, 0xF3C85FFF, 0xDFFFA7F8, +	0x5F5BBFFE, 0x7F7D10D0, 0x144D5F33, 0xBFFFAF78, +	0x5F5BBFFD, 0xA7F85F33, 0xBFFE77FD, 0x30BD4E08, +	0xFDCFE5FF, 0x6E0FAFF8, 0x7EEF7E9F, 0xFDEFF1CF, +	0x5F17ABF8, 0x0D5B5F5B, 0xFFEF79F7, 0x309EAFDD, +	0x5F3147F8, 0x5F31AFED, 0x7FDD50AF, 0x497847FD, +	0x7F9E7FED, 0x7DFD70A9, 0xEF7E7ECE, 0x6BA07F9E, +	0x2D227EFD, 0x30DB5F5B, 0xFFFD5F5B, 0xFFEF5F5B, +	0xFFDF0C9C, 0xAFED0A9A, 0xAFDD0C37, 0x5F37AFBD, +	0x7FBDB081, 0x5F8147F8, +}; + +static ulong patch_2F00[] = { +	0x3E303430, 0x34343737, 0xABBF9B99, 0x4B4FBDBD, +	0x59949334, 0x9FFF37FB, 0x9B177DD9, 0x936956BB, +	0xFBDD697B, 0xDD2FD113, 0x1DB9F7BB, 0x36313963, +	0x79373369, 0x3193137F, 0x7331737A, 0xF7BB9B99, +	0x9BB19795, 0x77FDFD3D, 0x573B773F, 0x737933F7, +	0xB991D115, 0x31699315, 0x31531694, 0xBF4FBDBD, +	0x35931497, 0x35376956, 0xBD697B9D, 0x96931313, +	0x19797937, 0x69350000, +}; +#else + +static ulong patch_2000[] = { +	0x3fff0000, 0x3ffd0000, 0x3ffb0000, 0x3ff90000, +	0x5fefeff8, 0x5f91eff8, 0x3ff30000, 0x3ff10000, +	0x3a11e710, 0xedf0ccb9, 0xf318ed66, 0x7f0e5fe2, +	0x7fedbb38, 0x3afe7468, 0x7fedf4d8, 0x8ffbb92d, +	0xb83b77fd, 0xb0bb5eb9, 0xdfda7fed, 0x90bde74d, +	0x6f0dcbd3, 0xe7decfed, 0xcb50cfed, 0xcfeddf6d, +	0x914d4f74, 0x5eaedfcb, 0x9ee0e7df, 0xefbb6ffb, +	0xe7ef7f0e, 0x9ee57fed, 0xebb7effa, 0xeb30affb, +	0x7fea90b3, 0x7e0cf09f, 0xbffff318, 0x5fffdfff, +	0xac35efea, 0x7fce1fc1, 0xe2ff5fbd, 0xaffbe2ff, +	0x5fbfaffb, 0xf9a87d0f, 0xaef8770f, 0x7d0fb0a2, +	0xeffbbfff, 0xcfef5fba, 0x7d0fbfff, 0x5fba4cf8, +	0x7fddd09b, 0x49f847fd, 0x7efdf097, 0x7fedfffd, +	0x7dfdf093, 0xef7e7e1e, 0x5fba7f0e, 0x3a11e710, +	0xedf0cc87, 0xfb18ad0a, 0x1f85bbb8, 0x74283b7e, +	0x7375e4bb, 0x2ab64fb8, 0x5c7de4bb, 0x32fdffbf, +	0x5f0843f8, 0x7ce3e1bb, 0xe74f7ded, 0x6f0f4fe8, +	0xc7ba32be, 0x73f2efeb, 0x600b4f78, 0xe5bb760b, +	0x5388aef8, 0x4ef80b6a, 0xcfef9ee5, 0xabf8751f, +	0xefef5b88, 0x741f4fe8, 0x751e760d, 0x7fdb70dd, +	0x741cafce, 0xefcc7fce, 0x751e7088, 0x741ce7bb, +	0x334ecfed, 0xafdbefeb, 0xe5bb760b, 0x53ceaef8, +	0xafe8e7eb, 0x4bf8771e, 0x7e007fed, 0x4fcbe2cc, +	0x7fbc3085, 0x7b0f7a0f, 0x34b177fd, 0xb0e75e93, +	0xdf313e3b, 0xaf78741f, 0x741f30cc, 0xcfef5f08, +	0x741f3e88, 0xafb8771e, 0x5f437fed, 0x0bafe2cc, +	0x741ccfec, 0xe5ca53a9, 0x6fcb4f74, 0x5e89df27, +	0x2a923d14, 0x4b8fdf0c, 0x751f741c, 0x6c1eeffa, +	0xefea7fce, 0x6ffc309a, 0xefec3fca, 0x308fdf0a, +	0xadf85e7a, 0xaf7daefd, 0x5e7adf0a, 0x5e7aafdd, +	0x761f1088, 0x1e7c7efd, 0x3089fffe, 0x4908fb18, +	0x5fffdfff, 0xafbbf0f7, 0x4ef85f43, 0xadf81489, +	0x7a0f7089, 0xcfef5089, 0x7a0fdf0c, 0x5e7cafed, +	0xbc6e780f, 0xefef780f, 0xefef790f, 0xa7f85eeb, +	0xffef790f, 0xefef790f, 0x1489df0a, 0x5e7aadfd, +	0x5f09fffb, 0xe79aded9, 0xeff96079, 0x607ae79a, +	0xded8eff9, 0x60795edb, 0x607acfef, 0xefefefdf, +	0xefbfef7f, 0xeeffedff, 0xebffe7ff, 0xafefafdf, +	0xafbfaf7f, 0xaeffadff, 0xabffa7ff, 0x6fef6fdf, +	0x6fbf6f7f, 0x6eff6dff, 0x6bff67ff, 0x2fef2fdf, +	0x2fbf2f7f, 0x2eff2dff, 0x2bff27ff, 0x4e08fd1f, +	0xe5ff6e0f, 0xaff87eef, 0x7e0ffdef, 0xf11f6079, +	0xabf8f51e, 0x7e0af11c, 0x37cfae16, 0x7fec909a, +	0xadf8efdc, 0xcfeae52f, 0x7d0fe12b, 0xf11c6079, +	0x7e0a4df8, 0xcfea5ea0, 0x7d0befec, 0xcfea5ea2, +	0xe522efdc, 0x5ea2cfda, 0x4e08fd1f, 0x6e0faff8, +	0x7c1f761f, 0xfdeff91f, 0x6079abf8, 0x761cee00, +	0xf91f2bfb, 0xefefcfec, 0xf91f6079, 0x761c27fb, +	0xefdf5e83, 0xcfdc7fdd, 0x50f84bf8, 0x47fd7c1f, +	0x761ccfcf, 0x7eef7fed, 0x7dfd70ef, 0xef7e7f1e, +	0x771efb18, 0x6079e722, 0xe6bbe5bb, 0x2e66e5bb, +	0x600b2ee1, 0xe2bbe2bb, 0xe2bbe2bb, 0x2f5ee2bb, +	0xe2bb2ff9, 0x6079e2bb, +}; + +static ulong patch_2F00[] = { +	0x30303030, 0x3e3e3030, 0xaf79b9b3, 0xbaa3b979, +	0x9693369f, 0x79f79777, 0x97333fff, 0xfb3b9e9f, +	0x79b91d11, 0x9e13f3ff, 0x3f9b6bd9, 0xe173d136, +	0x695669d1, 0x697b3daf, 0x79b93a3a, 0x3f979f91, +	0x379ff976, 0xf99777fd, 0x9779737d, 0xe9d6bbf9, +	0xbfffd9df, 0x97f7fd97, 0x6f7b9bff, 0xf9bd9683, +	0x397db973, 0xd97b3b9f, 0xd7f9f733, 0x9993bb9e, +	0xe1f9ef93, 0x73773337, 0xb936917d, 0x11f87379, +	0xb979d336, 0x8b7ded73, 0x1b7d9337, 0x31f3f22f, +	0x3f2327ee, 0xeeeeeeee, 0xeeeeeeee, 0xeeeeeeee, +	0xeeeeee4b, 0xf4fbdbd2, 0x58bb1878, 0x577fdfd2, +	0xd573b773, 0xf7374b4f, 0xbdbd25b8, 0xb177d2d1, +	0x7376856b, 0xbfdd687b, 0xdd2fff8f, 0x78ffff8f, +	0xf22f0000, +}; +#endif + +static void UcodeCopy (volatile cpm8xx_t *cpm) +{ +	vu_long *p; +	int i; + +	p = (vu_long *)&(cpm->cp_dpmem[0x0000]); +	for (i=0; i < sizeof(patch_2000)/4; ++i) { +		p[i] = patch_2000[i]; +	} + +	p = (vu_long *)&(cpm->cp_dpmem[0x0F00]); +	for (i=0; i < sizeof(patch_2F00)/4; ++i) { +		p[i] = patch_2F00[i]; +	} +} + +#endif	/* CONFIG_SYS_I2C_UCODE_PATCH, CONFIG_SYS_SPI_UCODE_PATCH */ diff --git a/arch/powerpc/cpu/mpc8xx/video.c b/arch/powerpc/cpu/mpc8xx/video.c new file mode 100644 index 000000000..c79c499b6 --- /dev/null +++ b/arch/powerpc/cpu/mpc8xx/video.c @@ -0,0 +1,1331 @@ +/* + * (C) Copyright 2000 + * Paolo Scaffardi, AIRVENT SAM s.p.a - RIMINI(ITALY), arsenio@tin.it + * (C) Copyright 2002 + * Wolfgang Denk, wd@denx.de + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +/* #define DEBUG */ + +/************************************************************************/ +/* ** HEADER FILES							*/ +/************************************************************************/ + +#include <stdarg.h> +#include <common.h> +#include <config.h> +#include <version.h> +#include <timestamp.h> +#include <i2c.h> +#include <linux/types.h> +#include <stdio_dev.h> + +#ifdef CONFIG_VIDEO + +DECLARE_GLOBAL_DATA_PTR; + +/************************************************************************/ +/* ** DEBUG SETTINGS							*/ +/************************************************************************/ + +#if 0 +#define VIDEO_DEBUG_COLORBARS	/* Force colorbars output */ +#endif + +/************************************************************************/ +/* ** VIDEO MODE SETTINGS						*/ +/************************************************************************/ + +#if 0 +#define VIDEO_MODE_EXTENDED		/* Allow screen size bigger than visible area */ +#define VIDEO_MODE_NTSC +#endif + +#define VIDEO_MODE_PAL + +#if 0 +#define VIDEO_BLINK			/* This enables cursor blinking (under construction) */ +#endif + +#define VIDEO_INFO			/* Show U-Boot information */ +#define VIDEO_INFO_X		VIDEO_LOGO_WIDTH+8 +#define VIDEO_INFO_Y		16 + +/************************************************************************/ +/* ** VIDEO ENCODER CONSTANTS						*/ +/************************************************************************/ + +#ifdef CONFIG_VIDEO_ENCODER_AD7176 + +#include <video_ad7176.h>	/* Sets encoder data, mode, and visible and active area */ + +#define VIDEO_I2C		1 +#define VIDEO_I2C_ADDR		CONFIG_VIDEO_ENCODER_AD7176_ADDR +#endif + +#ifdef CONFIG_VIDEO_ENCODER_AD7177 + +#include <video_ad7177.h>	/* Sets encoder data, mode, and visible and active area */ + +#define VIDEO_I2C		1 +#define VIDEO_I2C_ADDR		CONFIG_VIDEO_ENCODER_AD7177_ADDR +#endif + +#ifdef CONFIG_VIDEO_ENCODER_AD7179 + +#include <video_ad7179.h>	/* Sets encoder data, mode, and visible and active area */ + +#define VIDEO_I2C		1 +#define VIDEO_I2C_ADDR		CONFIG_VIDEO_ENCODER_AD7179_ADDR +#endif + +/************************************************************************/ +/* ** VIDEO MODE CONSTANTS						*/ +/************************************************************************/ + +#ifdef VIDEO_MODE_EXTENDED +#define VIDEO_COLS	VIDEO_ACTIVE_COLS +#define VIDEO_ROWS	VIDEO_ACTIVE_ROWS +#else +#define VIDEO_COLS	VIDEO_VISIBLE_COLS +#define VIDEO_ROWS	VIDEO_VISIBLE_ROWS +#endif + +#define VIDEO_PIXEL_SIZE	(VIDEO_MODE_BPP/8) +#define VIDEO_SIZE		(VIDEO_ROWS*VIDEO_COLS*VIDEO_PIXEL_SIZE)	/* Total size of buffer */ +#define VIDEO_PIX_BLOCKS	(VIDEO_SIZE >> 2)	/* Number of ints */ +#define VIDEO_LINE_LEN		(VIDEO_COLS*VIDEO_PIXEL_SIZE)	/* Number of bytes per line */ +#define VIDEO_BURST_LEN		(VIDEO_COLS/8) + +#ifdef VIDEO_MODE_YUYV +#define VIDEO_BG_COL	0x80D880D8	/* Background color in YUYV format */ +#else +#define VIDEO_BG_COL	0xF8F8F8F8	/* Background color in RGB format */ +#endif + +/************************************************************************/ +/* ** FONT AND LOGO DATA						*/ +/************************************************************************/ + +#include <video_font.h>			/* Get font data, width and height */ + +#ifdef CONFIG_VIDEO_LOGO +#include <video_logo.h>			/* Get logo data, width and height */ + +#define VIDEO_LOGO_WIDTH	DEF_U_BOOT_LOGO_WIDTH +#define VIDEO_LOGO_HEIGHT	DEF_U_BOOT_LOGO_HEIGHT +#define VIDEO_LOGO_ADDR		&u_boot_logo +#endif + +/************************************************************************/ +/* ** VIDEO CONTROLLER CONSTANTS					*/ +/************************************************************************/ + +/* VCCR - VIDEO CONTROLLER CONFIGURATION REGISTER */ + +#define VIDEO_VCCR_VON	0		/* Video controller ON */ +#define VIDEO_VCCR_CSRC	1		/* Clock source */ +#define VIDEO_VCCR_PDF	13		/* Pixel display format */ +#define VIDEO_VCCR_IEN	11		/* Interrupt enable */ + +/* VSR - VIDEO STATUS REGISTER */ + +#define VIDEO_VSR_CAS	6		/* Active set */ +#define VIDEO_VSR_EOF	0		/* End of frame */ + +/* VCMR - VIDEO COMMAND REGISTER */ + +#define VIDEO_VCMR_BD	0		/* Blank display */ +#define VIDEO_VCMR_ASEL	1		/* Active set selection */ + +/* VBCB - VIDEO BACKGROUND COLOR BUFFER REGISTER */ + +#define VIDEO_BCSR4_RESET_BIT	21	/* BCSR4 - Extern video encoder reset */ +#define VIDEO_BCSR4_EXTCLK_BIT	22	/* BCSR4 - Extern clock enable */ +#define VIDEO_BCSR4_VIDLED_BIT	23	/* BCSR4 - Video led disable */ + +/************************************************************************/ +/* ** CONSOLE CONSTANTS							*/ +/************************************************************************/ + +#ifdef	CONFIG_VIDEO_LOGO +#define CONSOLE_ROWS		((VIDEO_ROWS - VIDEO_LOGO_HEIGHT) / VIDEO_FONT_HEIGHT) +#define VIDEO_LOGO_SKIP		(VIDEO_COLS - VIDEO_LOGO_WIDTH) +#else +#define CONSOLE_ROWS		(VIDEO_ROWS / VIDEO_FONT_HEIGHT) +#endif + +#define CONSOLE_COLS		(VIDEO_COLS / VIDEO_FONT_WIDTH) +#define CONSOLE_ROW_SIZE	(VIDEO_FONT_HEIGHT * VIDEO_LINE_LEN) +#define CONSOLE_ROW_FIRST	(video_console_address) +#define CONSOLE_ROW_SECOND	(video_console_address + CONSOLE_ROW_SIZE) +#define CONSOLE_ROW_LAST	(video_console_address + CONSOLE_SIZE - CONSOLE_ROW_SIZE) +#define CONSOLE_SIZE		(CONSOLE_ROW_SIZE * CONSOLE_ROWS) +#define CONSOLE_SCROLL_SIZE	(CONSOLE_SIZE - CONSOLE_ROW_SIZE) + +/* + * Simple color definitions + */ +#define CONSOLE_COLOR_BLACK	 0 +#define CONSOLE_COLOR_RED	 1 +#define CONSOLE_COLOR_GREEN	 2 +#define CONSOLE_COLOR_YELLOW	 3 +#define CONSOLE_COLOR_BLUE	 4 +#define CONSOLE_COLOR_MAGENTA	 5 +#define CONSOLE_COLOR_CYAN	 6 +#define CONSOLE_COLOR_GREY	13 +#define CONSOLE_COLOR_GREY2	14 +#define CONSOLE_COLOR_WHITE	15	/* Must remain last / highest */ + +/************************************************************************/ +/* ** BITOPS MACROS							*/ +/************************************************************************/ + +#define HISHORT(i)	((i >> 16)&0xffff) +#define LOSHORT(i)	(i & 0xffff) +#define HICHAR(s)	((i >> 8)&0xff) +#define LOCHAR(s)	(i & 0xff) +#define HI(c)		((c >> 4)&0xf) +#define LO(c)		(c & 0xf) +#define SWAPINT(i)	(HISHORT(i) | (LOSHORT(i) << 16)) +#define SWAPSHORT(s)	(HICHAR(s) | (LOCHAR(s) << 8)) +#define SWAPCHAR(c)	(HI(c) | (LO(c) << 4)) +#define BITMASK(b)	(1 << (b)) +#define GETBIT(v,b)	(((v) & BITMASK(b)) > 0) +#define SETBIT(v,b,d)	(v = (((d)>0) ? (v) | BITMASK(b): (v) & ~BITMASK(b))) + +/************************************************************************/ +/* ** STRUCTURES							*/ +/************************************************************************/ + +typedef struct { +	unsigned char V, Y1, U, Y2; +} tYUYV; + +/* This structure is based on the Video Ram in the MPC823. */ +typedef struct VRAM { +	unsigned	hx:2,		/* Horizontal sync */ +			vx:2,		/* Vertical sync */ +			fx:2,		/* Frame */ +			bx:2,		/* Blank */ +			res1:6,		/* Reserved */ +			vds:2,		/* Video Data Select */ +			inter:1,	/* Interrupt */ +			res2:2,		/* Reserved */ +			lcyc:11,	/* Loop/video cycles */ +			lp:1,		/* Loop start/end */ +			lst:1;		/* Last entry */ +} VRAM; + +/************************************************************************/ +/* ** VARIABLES								*/ +/************************************************************************/ + +static int +	video_panning_range_x = 0,	/* Video mode invisible pixels x range */ +	video_panning_range_y = 0,	/* Video mode invisible pixels y range */ +	video_panning_value_x = 0,	/* Video mode x panning value (absolute) */ +	video_panning_value_y = 0,	/* Video mode y panning value (absolute) */ +	video_panning_factor_x = 0,	/* Video mode x panning value (-127 +127) */ +	video_panning_factor_y = 0,	/* Video mode y panning value (-127 +127) */ +	console_col = 0,		/* Cursor col */ +	console_row = 0,		/* Cursor row */ +	video_palette[16];		/* Our palette */ + +static const int video_font_draw_table[] = +	{ 0x00000000, 0x0000ffff, 0xffff0000, 0xffffffff }; + +static char +	video_color_fg = 0,		/* Current fg color index (0-15) */ +	video_color_bg = 0,		/* Current bg color index (0-15) */ +	video_enable = 0;		/* Video has been initialized? */ + +static void +	*video_fb_address,		/* Frame buffer address */ +	*video_console_address;		/* Console frame buffer start address */ + +/************************************************************************/ +/* ** MEMORY FUNCTIONS (32bit)						*/ +/************************************************************************/ + +static void memsetl (int *p, int c, int v) +{ +	while (c--) +		*(p++) = v; +} + +static void memcpyl (int *d, int *s, int c) +{ +	while (c--) +		*(d++) = *(s++); +} + +/************************************************************************/ +/* ** VIDEO DRAWING AND COLOR FUNCTIONS					*/ +/************************************************************************/ + +static int video_maprgb (int r, int g, int b) +{ +#ifdef VIDEO_MODE_YUYV +	unsigned int pR, pG, pB; +	tYUYV YUYV; +	unsigned int *ret = (unsigned int *) &YUYV; + +	/* Transform (0-255) components to (0-100) */ + +	pR = r * 100 / 255; +	pG = g * 100 / 255; +	pB = b * 100 / 255; + +	/* Calculate YUV values (0-255) from RGB beetween 0-100 */ + +	YUYV.Y1 = YUYV.Y2 = 209 * (pR + pG + pB) / 300 + 16; +	YUYV.U	= pR - (pG * 3 / 4) - (pB / 4) + 128; +	YUYV.V	= pB - (pR / 4) - (pG * 3 / 4) + 128; +	return *ret; +#endif +#ifdef VIDEO_MODE_RGB +	return ((r >> 3) << 11) | ((g > 2) << 6) | (b >> 3); +#endif +} + +static void video_setpalette (int color, int r, int g, int b) +{ +	color &= 0xf; + +	video_palette[color] = video_maprgb (r, g, b); + +	/* Swap values if our panning offset is odd */ +	if (video_panning_value_x & 1) +		video_palette[color] = SWAPINT (video_palette[color]); +} + +static void video_fill (int color) +{ +	memsetl (video_fb_address, VIDEO_PIX_BLOCKS, color); +} + +static void video_setfgcolor (int i) +{ +	video_color_fg = i & 0xf; +} + +static void video_setbgcolor (int i) +{ +	video_color_bg = i & 0xf; +} + +static int video_pickcolor (int i) +{ +	return video_palette[i & 0xf]; +} + +/* Absolute console plotting functions */ + +#ifdef VIDEO_BLINK +static void video_revchar (int xx, int yy) +{ +	int rows; +	u8 *dest; + +	dest = video_fb_address + yy * VIDEO_LINE_LEN + xx * 2; + +	for (rows = VIDEO_FONT_HEIGHT; rows--; dest += VIDEO_LINE_LEN) { +		switch (VIDEO_FONT_WIDTH) { +		case 16: +			((u32 *) dest)[6] ^= 0xffffffff; +			((u32 *) dest)[7] ^= 0xffffffff; +			/* FALL THROUGH */ +		case 12: +			((u32 *) dest)[4] ^= 0xffffffff; +			((u32 *) dest)[5] ^= 0xffffffff; +			/* FALL THROUGH */ +		case 8: +			((u32 *) dest)[2] ^= 0xffffffff; +			((u32 *) dest)[3] ^= 0xffffffff; +			/* FALL THROUGH */ +		case 4: +			((u32 *) dest)[0] ^= 0xffffffff; +			((u32 *) dest)[1] ^= 0xffffffff; +		} +	} +} +#endif + +static void video_drawchars (int xx, int yy, unsigned char *s, int count) +{ +	u8 *cdat, *dest, *dest0; +	int rows, offset, c; +	u32 eorx, fgx, bgx; + +	offset = yy * VIDEO_LINE_LEN + xx * 2; +	dest0 = video_fb_address + offset; + +	fgx = video_pickcolor (video_color_fg); +	bgx = video_pickcolor (video_color_bg); + +	if (xx & 1) { +		fgx = SWAPINT (fgx); +		bgx = SWAPINT (bgx); +	} + +	eorx = fgx ^ bgx; + +	switch (VIDEO_FONT_WIDTH) { +	case 4: +	case 8: +		while (count--) { +			c = *s; +			cdat = video_fontdata + c * VIDEO_FONT_HEIGHT; +			for (rows = VIDEO_FONT_HEIGHT, dest = dest0; +			     rows--; +			     dest += VIDEO_LINE_LEN) { +				u8 bits = *cdat++; + +				((u32 *) dest)[0] = +					(video_font_draw_table[bits >> 6] & eorx) ^ bgx; +				((u32 *) dest)[1] = +					(video_font_draw_table[bits >> 4 & 3] & eorx) ^ bgx; +				if (VIDEO_FONT_WIDTH == 8) { +					((u32 *) dest)[2] = +						(video_font_draw_table[bits >> 2 & 3] & eorx) ^ bgx; +					((u32 *) dest)[3] = +						(video_font_draw_table[bits & 3] & eorx) ^ bgx; +				} +			} +			dest0 += VIDEO_FONT_WIDTH * 2; +			s++; +		} +		break; +	case 12: +	case 16: +		while (count--) { +			cdat = video_fontdata + (*s) * (VIDEO_FONT_HEIGHT << 1); +			for (rows = VIDEO_FONT_HEIGHT, dest = dest0; rows--; +				 dest += VIDEO_LINE_LEN) { +				u8 bits = *cdat++; + +				((u32 *) dest)[0] = +					(video_font_draw_table[bits >> 6] & eorx) ^ bgx; +				((u32 *) dest)[1] = +					(video_font_draw_table[bits >> 4 & 3] & eorx) ^ bgx; +				((u32 *) dest)[2] = +					(video_font_draw_table[bits >> 2 & 3] & eorx) ^ bgx; +				((u32 *) dest)[3] = +					(video_font_draw_table[bits & 3] & eorx) ^ bgx; +				bits = *cdat++; +				((u32 *) dest)[4] = +					(video_font_draw_table[bits >> 6] & eorx) ^ bgx; +				((u32 *) dest)[5] = +					(video_font_draw_table[bits >> 4 & 3] & eorx) ^ bgx; +				if (VIDEO_FONT_WIDTH == 16) { +					((u32 *) dest)[6] = +						(video_font_draw_table[bits >> 2 & 3] & eorx) ^ bgx; +					((u32 *) dest)[7] = +						(video_font_draw_table[bits & 3] & eorx) ^ bgx; +				} +			} +			s++; +			dest0 += VIDEO_FONT_WIDTH * 2; +		} +		break; +	} +} + +static inline void video_drawstring (int xx, int yy, char *s) +{ +	video_drawchars (xx, yy, (unsigned char *)s, strlen (s)); +} + +/* Relative to console plotting functions */ + +static void video_putchars (int xx, int yy, unsigned char *s, int count) +{ +#ifdef CONFIG_VIDEO_LOGO +	video_drawchars (xx, yy + VIDEO_LOGO_HEIGHT, s, count); +#else +	video_drawchars (xx, yy, s, count); +#endif +} + +static void video_putchar (int xx, int yy, unsigned char c) +{ +#ifdef CONFIG_VIDEO_LOGO +	video_drawchars (xx, yy + VIDEO_LOGO_HEIGHT, &c, 1); +#else +	video_drawchars (xx, yy, &c, 1); +#endif +} + +static inline void video_putstring (int xx, int yy, unsigned char *s) +{ +	video_putchars (xx, yy, (unsigned char *)s, strlen ((char *)s)); +} + +/************************************************************************/ +/* ** VIDEO CONTROLLER LOW-LEVEL FUNCTIONS				*/ +/************************************************************************/ + +#if !defined(CONFIG_RRVISION) +static void video_mode_dupefield (VRAM * source, VRAM * dest, int entries) +{ +	int i; + +	for (i = 0; i < entries; i++) { +		dest[i] = source[i];	/* Copy the entire record */ +		dest[i].fx = (!dest[i].fx) * 3;	/* Negate field bit */ +	} + +	dest[0].lcyc++;			/* Add a cycle to the first entry */ +	dest[entries - 1].lst = 1;	/* Set end of ram entries */ +} +#endif + +static void inline video_mode_addentry (VRAM * vr, +	int Hx, int Vx, int Fx, int Bx, +	int VDS, int INT, int LCYC, int LP, int LST) +{ +	vr->hx = Hx; +	vr->vx = Vx; +	vr->fx = Fx; +	vr->bx = Bx; +	vr->vds = VDS; +	vr->inter = INT; +	vr->lcyc = LCYC; +	vr->lp = LP; +	vr->lst = LST; +} + +#define ADDENTRY(a,b,c,d,e,f,g,h,i)	video_mode_addentry(&vr[entry++],a,b,c,d,e,f,g,h,i) + +static int video_mode_generate (void) +{ +	immap_t *immap = (immap_t *) CONFIG_SYS_IMMR; +	VRAM *vr = (VRAM *) (((void *) immap) + 0xb00);	/* Pointer to the VRAM table */ +	int DX, X1, X2, DY, Y1, Y2, entry = 0, fifo; + +	/* CHECKING PARAMETERS */ + +	if (video_panning_factor_y < -128) +		video_panning_factor_y = -128; + +	if (video_panning_factor_y > 128) +		video_panning_factor_y = 128; + +	if (video_panning_factor_x < -128) +		video_panning_factor_x = -128; + +	if (video_panning_factor_x > 128) +		video_panning_factor_x = 128; + +	/* Setting panning */ + +	DX = video_panning_range_x = (VIDEO_ACTIVE_COLS - VIDEO_COLS) * 2; +	DY = video_panning_range_y = (VIDEO_ACTIVE_ROWS - VIDEO_ROWS) / 2; + +	video_panning_value_x = (video_panning_factor_x + 128) * DX / 256; +	video_panning_value_y = (video_panning_factor_y + 128) * DY / 256; + +	/* We assume these are burst units (multiplied by 2, we need it pari) */ +	X1 = video_panning_value_x & 0xfffe; +	X2 = DX - X1; + +	/* We assume these are field line units (divided by 2, we need it pari) */ +	Y1 = video_panning_value_y & 0xfffe; +	Y2 = DY - Y1; + +	debug("X1=%d, X2=%d, Y1=%d, Y2=%d, DX=%d, DY=%d VIDEO_COLS=%d \n", +	      X1, X2, Y1, Y2, DX, DY, VIDEO_COLS); + +#ifdef VIDEO_MODE_NTSC +/* + *	     Hx Vx Fx Bx VDS INT LCYC LP LST + * + * Retrace blanking + */ +	ADDENTRY (0, 0, 3, 0, 1, 0, 3, 1, 0); +	ADDENTRY (3, 0, 3, 0, 1, 0, 243, 0, 0); +	ADDENTRY (3, 0, 3, 0, 1, 0, 1440, 0, 0); +	ADDENTRY (3, 0, 3, 0, 1, 0, 32, 1, 0); +/* + * Vertical blanking + */ +	ADDENTRY (0, 0, 0, 0, 1, 0, 18, 1, 0); +	ADDENTRY (3, 0, 0, 0, 1, 0, 243, 0, 0); +	ADDENTRY (3, 0, 0, 0, 1, 0, 1440, 0, 0); +	ADDENTRY (3, 0, 0, 0, 1, 0, 32, 1, 0); +/* + * Odd field active area (TOP) + */ +	if (Y1 > 0) { +		ADDENTRY (0, 0, 0, 0, 1, 0, Y1, 1, 0); +		ADDENTRY (3, 0, 0, 0, 1, 0, 235, 0, 0); +		ADDENTRY (3, 0, 0, 3, 1, 0, 1448, 0, 0); +		ADDENTRY (3, 0, 0, 0, 1, 0, 32, 1, 0); +	} +/* + * Odd field active area + */ +	ADDENTRY (0, 0, 0, 0, 1, 0, 240 - DY, 1, 0); +	ADDENTRY (3, 0, 0, 0, 1, 0, 235, 0, 0); +	ADDENTRY (3, 0, 0, 3, 1, 0, 8 + X1, 0, 0); +	ADDENTRY (3, 0, 0, 3, 0, 0, VIDEO_COLS * 2, 0, 0); + +	if (X2 > 0) +		ADDENTRY (3, 0, 0, 3, 1, 0, X2, 0, 0); + +	ADDENTRY (3, 0, 0, 0, 1, 0, 32, 1, 0); + +/* + * Odd field active area (BOTTOM) + */ +	if (Y1 > 0) { +		ADDENTRY (0, 0, 0, 0, 1, 0, Y2, 1, 0); +		ADDENTRY (3, 0, 0, 0, 1, 0, 235, 0, 0); +		ADDENTRY (3, 0, 0, 3, 1, 0, 1448, 0, 0); +		ADDENTRY (3, 0, 0, 0, 1, 0, 32, 1, 0); +	} +/* + * Vertical blanking + */ +	ADDENTRY (0, 0, 0, 0, 1, 0, 4, 1, 0); +	ADDENTRY (3, 0, 0, 0, 1, 0, 243, 0, 0); +	ADDENTRY (3, 0, 0, 0, 1, 0, 1440, 0, 0); +	ADDENTRY (3, 0, 0, 0, 1, 0, 32, 1, 0); +/* + * Vertical blanking + */ +	ADDENTRY (0, 0, 3, 0, 1, 0, 19, 1, 0); +	ADDENTRY (3, 0, 3, 0, 1, 0, 243, 0, 0); +	ADDENTRY (3, 0, 3, 0, 1, 0, 1440, 0, 0); +	ADDENTRY (3, 0, 3, 0, 1, 0, 32, 1, 0); +/* + * Even field active area (TOP) + */ +	if (Y1 > 0) { +		ADDENTRY (0, 0, 3, 0, 1, 0, Y1, 1, 0); +		ADDENTRY (3, 0, 3, 0, 1, 0, 235, 0, 0); +		ADDENTRY (3, 0, 3, 3, 1, 0, 1448, 0, 0); +		ADDENTRY (3, 0, 3, 0, 1, 0, 32, 1, 0); +	} +/* + * Even field active area (CENTER) + */ +	ADDENTRY (0, 0, 3, 0, 1, 0, 240 - DY, 1, 0); +	ADDENTRY (3, 0, 3, 0, 1, 0, 235, 0, 0); +	ADDENTRY (3, 0, 3, 3, 1, 0, 8 + X1, 0, 0); +	ADDENTRY (3, 0, 3, 3, 0, 0, VIDEO_COLS * 2, 0, 0); + +	if (X2 > 0) +		ADDENTRY (3, 0, 3, 3, 1, 0, X2, 0, 0); + +	ADDENTRY (3, 0, 3, 0, 1, 0, 32, 1, 0); +/* + * Even field active area (BOTTOM) + */ +	if (Y1 > 0) { +		ADDENTRY (0, 0, 3, 0, 1, 0, Y2, 1, 0); +		ADDENTRY (3, 0, 3, 0, 1, 0, 235, 0, 0); +		ADDENTRY (3, 0, 3, 3, 1, 0, 1448, 0, 0); +		ADDENTRY (3, 0, 3, 0, 1, 0, 32, 1, 0); +	} +/* + * Vertical blanking + */ +	ADDENTRY (0, 0, 3, 0, 1, 0, 1, 1, 0); +	ADDENTRY (3, 0, 3, 0, 1, 0, 243, 0, 0); +	ADDENTRY (3, 0, 3, 0, 1, 0, 1440, 0, 0); +	ADDENTRY (3, 0, 3, 0, 1, 1, 32, 1, 1); +#endif + +#ifdef VIDEO_MODE_PAL + +#if defined(CONFIG_RRVISION) + +#define HPW   160  /* horizontal pulse width (was 139)	*/ +#define VPW	2  /* vertical pulse width		*/ +#define HBP   104  /* horizontal back porch (was 112)	*/ +#define VBP    19  /* vertical back porch (was 19)	*/ +#define VID_R 240  /* number of rows			*/ + +	debug ("[VIDEO CTRL] Starting to add controller entries..."); +/* + * Even field + */ +	ADDENTRY (0, 3, 0, 3, 1, 0, 2, 0, 0); +	ADDENTRY (0, 0, 0, 3, 1, 0, HPW, 0, 0); +	ADDENTRY (3, 0, 0, 3, 1, 0, HBP + (VIDEO_COLS * 2) + 72, 0, 0); + +	ADDENTRY (0, 0, 0, 3, 1, 0, VPW, 1, 0); +	ADDENTRY (0, 0, 0, 3, 1, 0, HPW-1, 0, 0); +	ADDENTRY (3, 0, 0, 3, 1, 0, HBP + (VIDEO_COLS * 2) + 72, 1, 0); + +	ADDENTRY (0, 3, 0, 3, 1, 0, VBP, 1, 0); +	ADDENTRY (0, 3, 0, 3, 1, 0, HPW-1, 0, 0); +	ADDENTRY (3, 3, 0, 3, 1, 0, HBP + (VIDEO_COLS * 2) + 72, 1, 0); +/* + * Active area + */ +	ADDENTRY (0, 3, 0, 3, 1, 0, VID_R , 1, 0); +	ADDENTRY (0, 3, 0, 3, 1, 0, HPW-1, 0, 0); +	ADDENTRY (3, 3, 0, 3, 1, 0, HBP, 0, 0); +	ADDENTRY (3, 3, 0, 3, 0, 0, VIDEO_COLS*2, 0, 0); +	ADDENTRY (3, 3, 0, 3, 1, 0, 72, 1, 1); + +	ADDENTRY (0, 3, 0, 3, 1, 0, 51, 1, 0); +	ADDENTRY (0, 3, 0, 3, 1, 0, HPW-1, 0, 0); +	ADDENTRY (3, 3, 0, 3, 1, 0, HBP +(VIDEO_COLS * 2) + 72 , 1, 0); +/* + * Odd field + */ +	ADDENTRY (0, 3, 0, 3, 1, 0, 2, 0, 0); +	ADDENTRY (0, 0, 0, 3, 1, 0, HPW, 0, 0); +	ADDENTRY (3, 0, 0, 3, 1, 0, HBP + (VIDEO_COLS * 2) + 72, 0, 0); + +	ADDENTRY (0, 0, 0, 3, 1, 0, VPW+1, 1, 0); +	ADDENTRY (0, 0, 0, 3, 1, 0, HPW-1, 0, 0); +	ADDENTRY (3, 0, 0, 3, 1, 0, HBP + (VIDEO_COLS * 2) + 72, 1, 0); + +	ADDENTRY (0, 3, 0, 3, 1, 0, VBP, 1, 0); +	ADDENTRY (0, 3, 0, 3, 1, 0, HPW-1, 0, 0); +	ADDENTRY (3, 3, 0, 3, 1, 0, HBP + (VIDEO_COLS * 2) + 72, 1, 0); +/* + * Active area + */ +	ADDENTRY (0, 3, 0, 3, 1, 0, VID_R , 1, 0); +	ADDENTRY (0, 3, 0, 3, 1, 0, HPW-1, 0, 0); +	ADDENTRY (3, 3, 0, 3, 1, 0, HBP, 0, 0); +	ADDENTRY (3, 3, 0, 3, 0, 0, VIDEO_COLS*2, 0, 0); +	ADDENTRY (3, 3, 0, 3, 1, 0, 72, 1, 1); + +	ADDENTRY (0, 3, 0, 3, 1, 0, 51, 1, 0); +	ADDENTRY (0, 3, 0, 3, 1, 0, HPW-1, 0, 0); +	ADDENTRY (3, 3, 0, 3, 1, 0, HBP +(VIDEO_COLS * 2) + 72 , 1, 0); + +	debug ("done\n"); + +#else  /* !CONFIG_RRVISION */ + +/* + *	Hx Vx Fx Bx VDS INT LCYC LP LST + * + * vertical; blanking + */ +	ADDENTRY (0, 0, 0, 0, 1, 0, 22, 1, 0); +	ADDENTRY (3, 0, 0, 0, 1, 0, 263, 0, 0); +	ADDENTRY (3, 0, 0, 0, 1, 0, 1440, 0, 0); +	ADDENTRY (3, 0, 0, 0, 1, 0, 24, 1, 0); +/* + * active area (TOP) + */ +	if (Y1 > 0) { +		ADDENTRY (0, 0, 0, 0, 1, 0, Y1, 1, 0);	/* 11? */ +		ADDENTRY (3, 0, 0, 0, 1, 0, 255, 0, 0); +		ADDENTRY (3, 0, 0, 3, 1, 0, 1448, 0, 0); +		ADDENTRY (3, 0, 0, 0, 1, 0, 24, 1, 0); +	} +/* + * field active area (CENTER) + */ +	ADDENTRY (0, 0, 0, 0, 1, 0, 288 - DY, 1, 0);	/* 265? */ +	ADDENTRY (3, 0, 0, 0, 1, 0, 255, 0, 0); +	ADDENTRY (3, 0, 0, 3, 1, 0, 8 + X1, 0, 0); +	ADDENTRY (3, 0, 0, 3, 0, 0, VIDEO_COLS * 2, 0, 0); + +	if (X2 > 0) +		ADDENTRY (3, 0, 0, 1, 1, 0, X2, 0, 0); + +	ADDENTRY (3, 0, 0, 0, 1, 0, 24, 1, 0); +/* + * field active area (BOTTOM) + */ +	if (Y2 > 0) { +		ADDENTRY (0, 0, 0, 0, 1, 0, Y2, 1, 0);	/* 12? */ +		ADDENTRY (3, 0, 0, 0, 1, 0, 255, 0, 0); +		ADDENTRY (3, 0, 0, 3, 1, 0, 1448, 0, 0); +		ADDENTRY (3, 0, 0, 0, 1, 0, 24, 1, 0); +	} +/* + * field vertical; blanking + */ +	ADDENTRY (0, 0, 0, 0, 1, 0, 2, 1, 0); +	ADDENTRY (3, 0, 0, 0, 1, 0, 263, 0, 0); +	ADDENTRY (3, 0, 0, 0, 1, 0, 1440, 0, 0); +	ADDENTRY (3, 0, 0, 0, 1, 0, 24, 1, 0); +/* + * Create the other field (like this, but whit other field selected, + * one more cycle loop and a last identifier) + */ +	video_mode_dupefield (vr, &vr[entry], entry); +#endif /* CONFIG_RRVISION */ + +#endif /* VIDEO_MODE_PAL */ + +	/* See what FIFO are we using */ +	fifo = GETBIT (immap->im_vid.vid_vsr, VIDEO_VSR_CAS); + +	/* Set number of lines and burst (only one frame for now) */ +	if (fifo) { +		immap->im_vid.vid_vfcr0 = VIDEO_BURST_LEN | +			(VIDEO_BURST_LEN << 8) | ((VIDEO_ROWS / 2) << 19); +	} else { +		immap->im_vid.vid_vfcr1 = VIDEO_BURST_LEN | +			(VIDEO_BURST_LEN << 8) | ((VIDEO_ROWS / 2) << 19); +	} + +	SETBIT (immap->im_vid.vid_vcmr, VIDEO_VCMR_ASEL, !fifo); + +/* + * Wait until changes are applied (not done) + * while (GETBIT(immap->im_vid.vid_vsr, VIDEO_VSR_CAS) == fifo) ; + */ + +	/* Return number of VRAM entries */ +	return entry * 2; +} + +static void video_encoder_init (void) +{ +#ifdef VIDEO_I2C +	int rc; + +	/* Initialize the I2C */ +	debug ("[VIDEO ENCODER] Initializing I2C bus...\n"); +	i2c_init (CONFIG_SYS_I2C_SPEED, CONFIG_SYS_I2C_SLAVE); + +#ifdef CONFIG_FADS +	/* Reset ADV7176 chip */ +	debug ("[VIDEO ENCODER] Resetting encoder...\n"); +	(*(int *) BCSR4) &= ~(1 << 21); + +	/* Wait for 5 ms inside the reset */ +	debug ("[VIDEO ENCODER] Waiting for encoder reset...\n"); +	udelay (5000); + +	/* Take ADV7176 out of reset */ +	(*(int *) BCSR4) |= 1 << 21; + +	/* Wait for 5 ms after the reset */ +	udelay (5000); +#endif	/* CONFIG_FADS */ + +	/* Send configuration */ +#ifdef DEBUG +	{ +		int i; + +		puts ("[VIDEO ENCODER] Configuring the encoder...\n"); + +		printf ("Sending %zu bytes (@ %08lX) to I2C 0x%lX:\n   ", +			sizeof(video_encoder_data), +			(ulong)video_encoder_data, +			(ulong)VIDEO_I2C_ADDR); +		for (i=0; i<sizeof(video_encoder_data); ++i) { +			printf(" %02X", video_encoder_data[i]); +		} +		putc ('\n'); +	} +#endif	/* DEBUG */ + +	if ((rc = i2c_write (VIDEO_I2C_ADDR, 0, 1, +			 video_encoder_data, +			 sizeof(video_encoder_data))) != 0) { +		printf ("i2c_send error: rc=%d\n", rc); +		return; +	} +#endif	/* VIDEO_I2C */ +	return; +} + +static void video_ctrl_init (void *memptr) +{ +	immap_t *immap = (immap_t *) CONFIG_SYS_IMMR; + +	video_fb_address = memptr; + +	/* Set background */ +	debug ("[VIDEO CTRL] Setting background color...\n"); +	immap->im_vid.vid_vbcb = VIDEO_BG_COL; + +	/* Show the background */ +	debug ("[VIDEO CTRL] Forcing background...\n"); +	SETBIT (immap->im_vid.vid_vcmr, VIDEO_VCMR_BD, 1); + +	/* Turn off video controller */ +	debug ("[VIDEO CTRL] Turning off video controller...\n"); +	SETBIT (immap->im_vid.vid_vccr, VIDEO_VCCR_VON, 0); + +#ifdef CONFIG_FADS +	/* Turn on Video Port LED */ +	debug ("[VIDEO CTRL] Turning off video port led...\n"); +	SETBIT (*(int *) BCSR4, VIDEO_BCSR4_VIDLED_BIT, 1); + +	/* Disable internal clock */ +	debug ("[VIDEO CTRL] Disabling internal clock...\n"); +	SETBIT (*(int *) BCSR4, VIDEO_BCSR4_EXTCLK_BIT, 0); +#endif + +	/* Generate and make active a new video mode */ +	debug ("[VIDEO CTRL] Generating video mode...\n"); +	video_mode_generate (); + +	/* Start of frame buffer (even and odd frame, to make it working with */ +	/* any selected active set) */ +	debug ("[VIDEO CTRL] Setting frame buffer address...\n"); +	immap->im_vid.vid_vfaa1 = +		immap->im_vid.vid_vfaa0 = (u32) video_fb_address; +	immap->im_vid.vid_vfba1 = +	immap->im_vid.vid_vfba0 = +		(u32) video_fb_address + VIDEO_LINE_LEN; + +	/* YUV, Big endian, SHIFT/CLK/CLK input (BEFORE ENABLING 27MHZ EXT CLOCK) */ +	debug ("[VIDEO CTRL] Setting pixel mode and clocks...\n"); +	immap->im_vid.vid_vccr = 0x2042; + +	/* Configure port pins */ +	debug ("[VIDEO CTRL] Configuring input/output pins...\n"); +	immap->im_ioport.iop_pdpar = 0x1fff; +	immap->im_ioport.iop_pddir = 0x0000; + +#ifdef CONFIG_FADS +	/* Turn on Video Port Clock - ONLY AFTER SET VCCR TO ENABLE EXTERNAL CLOCK */ +	debug ("[VIDEO CTRL] Turning on video clock...\n"); +	SETBIT (*(int *) BCSR4, VIDEO_BCSR4_EXTCLK_BIT, 1); + +	/* Turn on Video Port LED */ +	debug ("[VIDEO CTRL] Turning on video port led...\n"); +	SETBIT (*(int *) BCSR4, VIDEO_BCSR4_VIDLED_BIT, 0); +#endif +#ifdef CONFIG_RRVISION +	debug ("PC5->Output(1): enable PAL clock"); +	immap->im_ioport.iop_pcpar &= ~(0x0400); +	immap->im_ioport.iop_pcdir |=   0x0400 ; +	immap->im_ioport.iop_pcdat |=   0x0400 ; +	debug ("PDPAR=0x%04X PDDIR=0x%04X PDDAT=0x%04X\n", +	       immap->im_ioport.iop_pdpar, +	       immap->im_ioport.iop_pddir, +	       immap->im_ioport.iop_pddat); +	debug ("PCPAR=0x%04X PCDIR=0x%04X PCDAT=0x%04X\n", +	       immap->im_ioport.iop_pcpar, +	       immap->im_ioport.iop_pcdir, +	       immap->im_ioport.iop_pcdat); +#endif	/* CONFIG_RRVISION */ + +	/* Blanking the screen. */ +	debug ("[VIDEO CTRL] Blanking the screen...\n"); +	video_fill (VIDEO_BG_COL); + +	/* +	 * Turns on Aggressive Mode. Normally, turning on the caches +	 * will cause the screen to flicker when the caches try to +	 * fill. This gives the FIFO's for the Video Controller +	 * higher priority and prevents flickering because of +	 * underrun. This may still be an issue when using FLASH, +	 * since accessing data from Flash is so slow. +	 */ +	debug ("[VIDEO CTRL] Turning on aggressive mode...\n"); +	immap->im_siu_conf.sc_sdcr = 0x40; + +	/* Turn on video controller */ +	debug ("[VIDEO CTRL] Turning on video controller...\n"); +	SETBIT (immap->im_vid.vid_vccr, VIDEO_VCCR_VON, 1); + +	/* Show the display */ +	debug ("[VIDEO CTRL] Enabling the video...\n"); +	SETBIT (immap->im_vid.vid_vcmr, VIDEO_VCMR_BD, 0); +} + +/************************************************************************/ +/* ** CONSOLE FUNCTIONS							*/ +/************************************************************************/ + +static void console_scrollup (void) +{ +	/* Copy up rows ignoring the first one */ +	memcpyl (CONSOLE_ROW_FIRST, CONSOLE_ROW_SECOND, CONSOLE_SCROLL_SIZE >> 2); + +	/* Clear the last one */ +	memsetl (CONSOLE_ROW_LAST, CONSOLE_ROW_SIZE >> 2, VIDEO_BG_COL); +} + +static inline void console_back (void) +{ +	console_col--; + +	if (console_col < 0) { +		console_col = CONSOLE_COLS - 1; +		console_row--; +		if (console_row < 0) +			console_row = 0; +	} + +	video_putchar ( console_col * VIDEO_FONT_WIDTH, +			console_row * VIDEO_FONT_HEIGHT, ' '); +} + +static inline void console_newline (void) +{ +	console_row++; +	console_col = 0; + +	/* Check if we need to scroll the terminal */ +	if (console_row >= CONSOLE_ROWS) { +		/* Scroll everything up */ +		console_scrollup (); + +		/* Decrement row number */ +		console_row--; +	} +} + +void video_putc (const char c) +{ +	if (!video_enable) { +		serial_putc (c); +		return; +	} + +	switch (c) { +	case 13:			/* Simply ignore this */ +		break; + +	case '\n':			/* Next line, please */ +		console_newline (); +		break; + +	case 9:				/* Tab (8 chars alignment) */ +		console_col |= 0x0008;	/* Next 8 chars boundary */ +		console_col &= ~0x0007;	/* Set this bit to zero */ + +		if (console_col >= CONSOLE_COLS) +			console_newline (); +		break; + +	case 8:				/* Eat last character */ +		console_back (); +		break; + +	default:			/* Add to the console */ +		video_putchar ( console_col * VIDEO_FONT_WIDTH, +				console_row * VIDEO_FONT_HEIGHT, c); +		console_col++; +		/* Check if we need to go to next row */ +		if (console_col >= CONSOLE_COLS) +			console_newline (); +	} +} + +void video_puts (const char *s) +{ +	int count = strlen (s); + +	if (!video_enable) +		while (count--) +			serial_putc (*s++); +	else +		while (count--) +			video_putc (*s++); +} + +/************************************************************************/ +/* ** CURSOR BLINKING FUNCTIONS						*/ +/************************************************************************/ + +#ifdef VIDEO_BLINK + +#define BLINK_TIMER_ID		0 +#define BLINK_TIMER_HZ		2 + +static unsigned char blink_enabled = 0; +static timer_t blink_timer; + +static void blink_update (void) +{ +	static int blink_row = -1, blink_col = -1, blink_old = 0; + +	/* Check if we have a new position to invert */ +	if ((console_row != blink_row) || (console_col != blink_col)) { +		/* Check if we need to reverse last character */ +		if (blink_old) +			video_revchar ( blink_col * VIDEO_FONT_WIDTH, +					(blink_row +#ifdef CONFIG_VIDEO_LOGO +					 + VIDEO_LOGO_HEIGHT +#endif +					) * VIDEO_FONT_HEIGHT); + +		/* Update values */ +		blink_row = console_row; +		blink_col = console_col; +		blink_old = 0; +	} + +/* Reverse this character */ +	blink_old = !blink_old; +	video_revchar ( console_col * VIDEO_FONT_WIDTH, +			(console_row +#ifdef CONFIG_VIDEO_LOGO +			+ VIDEO_LOGO_HEIGHT +#endif +			) * VIDEO_FONT_HEIGHT); + +} + +/* + * Handler for blinking cursor + */ +static void blink_handler (void *arg) +{ +/* Blink */ +	blink_update (); +/* Ack the timer */ +	timer_ack (&blink_timer); +} + +int blink_set (int blink) +{ +	int ret = blink_enabled; + +	if (blink) +		timer_enable (&blink_timer); +	else +		timer_disable (&blink_timer); + +	blink_enabled = blink; + +	return ret; +} + +static inline void blink_close (void) +{ +	timer_close (&blink_timer); +} + +static inline void blink_init (void) +{ +	timer_init (&blink_timer, +			BLINK_TIMER_ID, BLINK_TIMER_HZ, +			blink_handler); +} +#endif + +/************************************************************************/ +/* ** LOGO PLOTTING FUNCTIONS						*/ +/************************************************************************/ + +#ifdef CONFIG_VIDEO_LOGO +void easylogo_plot (fastimage_t * image, void *screen, int width, int x, +					int y) +{ +	int skip = width - image->width, xcount, ycount = image->height; + +#ifdef VIDEO_MODE_YUYV +	ushort *source = (ushort *) image->data; +	ushort *dest   = (ushort *) screen + y * width + x; + +	while (ycount--) { +		xcount = image->width; +		while (xcount--) +			*dest++ = *source++; +		dest += skip; +	} +#endif +#ifdef VIDEO_MODE_RGB +	unsigned char +	*source = (unsigned short *) image->data, +			*dest = (unsigned short *) screen + ((y * width) + x) * 3; + +	while (ycount--) { +		xcount = image->width * 3; +		memcpy (dest, source, xcount); +		source += xcount; +		dest += ycount; +	} +#endif +} + +static void *video_logo (void) +{ +	u16 *screen = video_fb_address, width = VIDEO_COLS; +#ifdef VIDEO_INFO +# ifndef CONFIG_FADS +	char temp[32]; +# endif +	char info[80]; +#endif /* VIDEO_INFO */ + +	easylogo_plot (VIDEO_LOGO_ADDR, screen, width, 0, 0); + +#ifdef VIDEO_INFO +	sprintf (info, "%s (%s - %s) ", +		 U_BOOT_VERSION, U_BOOT_DATE, U_BOOT_TIME); +	video_drawstring (VIDEO_INFO_X, VIDEO_INFO_Y, info); + +	sprintf (info, "(C) 2002 DENX Software Engineering"); +	video_drawstring (VIDEO_INFO_X, VIDEO_INFO_Y + VIDEO_FONT_HEIGHT, +					info); + +	sprintf (info, "    Wolfgang DENK, wd@denx.de"); +	video_drawstring (VIDEO_INFO_X, VIDEO_INFO_Y + VIDEO_FONT_HEIGHT * 2, +					info); +#ifndef CONFIG_FADS		/* all normal boards */ +	/* leave one blank line */ + +	sprintf (info, "MPC823 CPU at %s MHz, %ld MB RAM, %ld MB Flash", +		strmhz(temp, gd->cpu_clk), +		gd->ram_size >> 20, +		gd->bd->bi_flashsize >> 20 ); +	video_drawstring (VIDEO_INFO_X, VIDEO_INFO_Y + VIDEO_FONT_HEIGHT * 4, +					info); +#else				/* FADS :-( */ +	sprintf (info, "MPC823 CPU at 50 MHz on FADS823 board"); +	video_drawstring (VIDEO_INFO_X, VIDEO_INFO_Y + VIDEO_FONT_HEIGHT, +					  info); + +	sprintf (info, "2MB FLASH - 8MB DRAM - 4MB SRAM"); +	video_drawstring (VIDEO_INFO_X, VIDEO_INFO_Y + VIDEO_FONT_HEIGHT * 2, +					  info); +#endif +#endif + +	return video_fb_address + VIDEO_LOGO_HEIGHT * VIDEO_LINE_LEN; +} +#endif + +/************************************************************************/ +/* ** VIDEO HIGH-LEVEL FUNCTIONS					*/ +/************************************************************************/ + +static int video_init (void *videobase) +{ +	/* Initialize the encoder */ +	debug ("[VIDEO] Initializing video encoder...\n"); +	video_encoder_init (); + +	/* Initialize the video controller */ +	debug ("[VIDEO] Initializing video controller at %08x...\n", +		   (int) videobase); +	video_ctrl_init (videobase); + +	/* Setting the palette */ +	video_setpalette  (CONSOLE_COLOR_BLACK,	     0,	   0,	 0); +	video_setpalette  (CONSOLE_COLOR_RED,	  0xFF,	   0,	 0); +	video_setpalette  (CONSOLE_COLOR_GREEN,	     0, 0xFF,	 0); +	video_setpalette  (CONSOLE_COLOR_YELLOW,  0xFF, 0xFF,	 0); +	video_setpalette  (CONSOLE_COLOR_BLUE,	     0,	   0, 0xFF); +	video_setpalette  (CONSOLE_COLOR_MAGENTA, 0xFF,	   0, 0xFF); +	video_setpalette  (CONSOLE_COLOR_CYAN,	     0, 0xFF, 0xFF); +	video_setpalette  (CONSOLE_COLOR_GREY,	  0xAA, 0xAA, 0xAA); +	video_setpalette  (CONSOLE_COLOR_GREY2,	  0xF8, 0xF8, 0xF8); +	video_setpalette  (CONSOLE_COLOR_WHITE,	  0xFF, 0xFF, 0xFF); + +#ifndef CONFIG_SYS_WHITE_ON_BLACK +	video_setfgcolor (CONSOLE_COLOR_BLACK); +	video_setbgcolor (CONSOLE_COLOR_GREY2); +#else +	video_setfgcolor (CONSOLE_COLOR_GREY2); +	video_setbgcolor (CONSOLE_COLOR_BLACK); +#endif	/* CONFIG_SYS_WHITE_ON_BLACK */ + +#ifdef CONFIG_VIDEO_LOGO +	/* Paint the logo and retrieve tv base address */ +	debug ("[VIDEO] Drawing the logo...\n"); +	video_console_address = video_logo (); +#else +	video_console_address = video_fb_address; +#endif + +#ifdef VIDEO_BLINK +	/* Enable the blinking (under construction) */ +	blink_init (); +	blink_set (0);				/* To Fix! */ +#endif + +	/* Initialize the console */ +	console_col = 0; +	console_row = 0; +	video_enable = 1; + +#ifdef VIDEO_MODE_PAL +# define VIDEO_MODE_TMP1	"PAL" +#endif +#ifdef VIDEO_MODE_NTSC +# define VIDEO_MODE_TMP1	"NTSC" +#endif +#ifdef VIDEO_MODE_YUYV +# define VIDEO_MODE_TMP2	"YCbYCr" +#endif +#ifdef VIDEO_MODE_RGB +# define VIDEO_MODE_TMP2	"RGB" +#endif +	debug ( VIDEO_MODE_TMP1 +		" %dx%dx%d (" VIDEO_MODE_TMP2 ") on %s - console %dx%d\n", +			VIDEO_COLS, VIDEO_ROWS, VIDEO_MODE_BPP, +			VIDEO_ENCODER_NAME, CONSOLE_COLS, CONSOLE_ROWS); +	return 0; +} + +int drv_video_init (void) +{ +	int error, devices = 1; + +	struct stdio_dev videodev; + +	video_init ((void *)(gd->fb_base));	/* Video initialization */ + +/* Device initialization */ + +	memset (&videodev, 0, sizeof (videodev)); + +	strcpy (videodev.name, "video"); +	videodev.ext = DEV_EXT_VIDEO;	/* Video extensions */ +	videodev.flags = DEV_FLAGS_OUTPUT;	/* Output only */ +	videodev.putc = video_putc;	/* 'putc' function */ +	videodev.puts = video_puts;	/* 'puts' function */ + +	error = stdio_register (&videodev); + +	return (error == 0) ? devices : error; +} + +/************************************************************************/ +/* ** ROM capable initialization part - needed to reserve FB memory	*/ +/************************************************************************/ + +/* + * This is called early in the system initialization to grab memory + * for the video controller. + * Returns new address for monitor, after reserving video buffer memory + * + * Note that this is running from ROM, so no write access to global data. + */ +ulong video_setmem (ulong addr) +{ +	/* Allocate pages for the frame buffer. */ +	addr -= VIDEO_SIZE; + +	debug ("Reserving %dk for Video Framebuffer at: %08lx\n", +		VIDEO_SIZE>>10, addr); + +	return (addr); +} + +#endif diff --git a/arch/powerpc/cpu/mpc8xx/wlkbd.c b/arch/powerpc/cpu/mpc8xx/wlkbd.c new file mode 100644 index 000000000..13009e264 --- /dev/null +++ b/arch/powerpc/cpu/mpc8xx/wlkbd.c @@ -0,0 +1,36 @@ +/* + * (C) Copyright 2000 + * Paolo Scaffardi, AIRVENT SAM s.p.a - RIMINI(ITALY), arsenio@tin.it + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include <common.h> +#include <config.h> + +#ifdef CONFIG_WL_4PPM_KEYBOARD + +/* WIP: Wireless keyboard on SMC + */ +int	drv_wlkbd_init (void) +{ +    return 0 ; +} + +#endif	/* CONFIG_WL_4PPM_KEYBOARD */ |