diff options
Diffstat (limited to 'board/LEOX/elpt860/flash.c')
| -rw-r--r-- | board/LEOX/elpt860/flash.c | 615 | 
1 files changed, 615 insertions, 0 deletions
| diff --git a/board/LEOX/elpt860/flash.c b/board/LEOX/elpt860/flash.c new file mode 100644 index 000000000..a9238e16c --- /dev/null +++ b/board/LEOX/elpt860/flash.c @@ -0,0 +1,615 @@ +/* +**===================================================================== +** +** Copyright (C) 2000, 2001, 2002, 2003 +** The LEOX team <team@leox.org>, http://www.leox.org +** +** LEOX.org is about the development of free hardware and software resources +**   for system on chip. +** +** Description: U-Boot port on the LEOX's ELPT860 CPU board +** ~~~~~~~~~~~ +** +**===================================================================== +** +** This program is free software; you can redistribute it and/or +** modify it under the terms of the 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 +** +**===================================================================== +*/ + +/* +** Note 1: In this file, you have to provide the following variable: +** ------ +**              flash_info_t    flash_info[CFG_MAX_FLASH_BANKS] +** 'flash_info_t' structure is defined into 'include/flash.h' +** and defined as extern into 'common/cmd_flash.c' +** +** Note 2: In this file, you have to provide the following functions: +** ------ +**              unsigned long   flash_init(void) +** called from 'board_init_r()' into 'common/board.c' +** +**              void            flash_print_info(flash_info_t *info) +** called from 'do_flinfo()' into 'common/cmd_flash.c' +** +**              int             flash_erase(flash_info_t *info, +**                                          int           s_first, +**                                          int           s_last) +** called from 'do_flerase()' & 'flash_sect_erase()' into 'common/cmd_flash.c' +** +**              int             write_buff (flash_info_t *info, +**                                          uchar        *src, +**                                          ulong         addr, +**                                          ulong         cnt) +** called from 'flash_write()' into 'common/cmd_flash.c' +*/ + +#include <common.h> +#include <mpc8xx.h> + + +#ifndef	CFG_ENV_ADDR +#  define CFG_ENV_ADDR	(CFG_FLASH_BASE + CFG_ENV_OFFSET) +#endif + +flash_info_t	flash_info[CFG_MAX_FLASH_BANKS]; /* info for FLASH chips */ + +/*----------------------------------------------------------------------- + * Internal Functions + */ +static void   flash_get_offsets (ulong base, flash_info_t *info); +static ulong  flash_get_size (volatile unsigned char *addr, flash_info_t *info); + +static int write_word (flash_info_t *info, ulong dest, ulong data); +static int write_byte (flash_info_t *info, ulong dest, uchar data); + +/*----------------------------------------------------------------------- + */ + +unsigned long  +flash_init (void) +{ +  volatile immap_t     *immap  = (immap_t *)CFG_IMMR; +  volatile memctl8xx_t *memctl = &immap->im_memctl; +  unsigned long         size_b0; +  int i; +   +  /* Init: no FLASHes known */ +  for (i=0; i<CFG_MAX_FLASH_BANKS; ++i)  +    { +      flash_info[i].flash_id = FLASH_UNKNOWN; +    } +   +  /* Static FLASH Bank configuration here - FIXME XXX */ +   +  size_b0 = flash_get_size ((volatile unsigned char *)FLASH_BASE0_PRELIM,  +			    &flash_info[0]); +   +  if ( flash_info[0].flash_id == FLASH_UNKNOWN )  +    { +      printf ("## Unknown FLASH on Bank 0 - Size = 0x%08lx = %ld MB\n", +	      size_b0, size_b0<<20); +    } +   +  /* Remap FLASH according to real size */ +  memctl->memc_or0 = CFG_OR_TIMING_FLASH | (-size_b0 & OR_AM_MSK); +  memctl->memc_br0 = (CFG_FLASH_BASE & BR_BA_MSK) | BR_MS_GPCM | BR_PS_8 | BR_V; +   +  /* Re-do sizing to get full correct info */ +  size_b0 = flash_get_size ((volatile unsigned char *)CFG_FLASH_BASE,  +			    &flash_info[0]); +   +  flash_get_offsets (CFG_FLASH_BASE, &flash_info[0]); +   +#if CFG_MONITOR_BASE >= CFG_FLASH_BASE +  /* monitor protection ON by default */ +  flash_protect (FLAG_PROTECT_SET, +		 CFG_MONITOR_BASE, +		 CFG_MONITOR_BASE + CFG_MONITOR_LEN-1, +		 &flash_info[0]); +#endif +   +#ifdef	CFG_ENV_IS_IN_FLASH +  /* ENV protection ON by default */ +  flash_protect(FLAG_PROTECT_SET, +		CFG_ENV_ADDR, +		CFG_ENV_ADDR + CFG_ENV_SIZE-1, +		&flash_info[0]); +#endif + +  flash_info[0].size = size_b0; +   +  return (size_b0); +} + +/*----------------------------------------------------------------------- + */ +static void  +flash_get_offsets (ulong          base,  +		   flash_info_t  *info) +{ +  int i; + +#define SECTOR_64KB    0x00010000 + +  /* set up sector start adress table */ +  for (i = 0; i < info->sector_count; i++)  +    { +      info->start[i] = base + (i * SECTOR_64KB); +    } +} + +/*----------------------------------------------------------------------- + */ +void  +flash_print_info (flash_info_t *info) +{ +  int i; +   +  if ( info->flash_id == FLASH_UNKNOWN )  +    { +      printf ("missing or unknown FLASH type\n"); +      return; +    } + +  switch ( info->flash_id & FLASH_VENDMASK )  +    { +    case FLASH_MAN_AMD:	printf ("AMD ");		break; +    case FLASH_MAN_FUJ:	printf ("FUJITSU ");		break; +    case FLASH_MAN_STM: printf ("STM (Thomson) ");      break; +    default:		printf ("Unknown Vendor ");	break; +    } +   +  switch ( info->flash_id & FLASH_TYPEMASK )  +    { +    case FLASH_AM040:   printf ("AM29F040   (4 Mbits)\n"); +      break; +    default:	        printf ("Unknown Chip Type\n"); +      break; +    } +   +  printf ("  Size: %ld KB in %d Sectors\n",  +	  info->size >> 10, info->sector_count); +   +  printf ("  Sector Start Addresses:"); +  for (i=0; i<info->sector_count; ++i)  +    { +      if ((i % 5) == 0) +	printf ("\n   "); +      printf (" %08lX%s", +	      info->start[i], +	      info->protect[i] ? " (RO)" : "     " +	      ); +    } +  printf ("\n"); + +  return; +} + +/*----------------------------------------------------------------------- + */ + + +/*----------------------------------------------------------------------- + */ + +/* + * The following code cannot be run from FLASH! + */ + +static ulong  +flash_get_size (volatile unsigned char *addr,  +                flash_info_t           *info) +{ +  short i; +  uchar value; +  ulong base = (ulong)addr; + +  /* Write auto select command: read Manufacturer ID */ +  addr[0x0555] = 0xAA; +  addr[0x02AA] = 0x55; +  addr[0x0555] = 0x90; +   +  value = addr[0]; + +  switch ( value )  +    { +      /*    case AMD_MANUFACT: */ +    case 0x01: +      info->flash_id = FLASH_MAN_AMD; +      break; +      /*    case FUJ_MANUFACT: */ +    case 0x04: +      info->flash_id = FLASH_MAN_FUJ; +      break; +      /*    case STM_MANUFACT: */ +    case 0x20: +      info->flash_id = FLASH_MAN_STM; +      break; + +    default: +      info->flash_id = FLASH_UNKNOWN; +      info->sector_count = 0; +      info->size = 0; +      return (0);			/* no or unknown flash	*/ +    } +   +  value = addr[1];			/* device ID		*/ +   +  switch ( value )  +    { +    case STM_ID_F040B: +    case AMD_ID_F040B: +      info->flash_id += FLASH_AM040;    /* 4 Mbits = 512k * 8  */ +      info->sector_count = 8; +      info->size = 0x00080000; +      break; + +    default: +      info->flash_id = FLASH_UNKNOWN; +      return (0);			/* => no or unknown flash */ +    } + +  /* set up sector start adress table */ +  for (i = 0; i < info->sector_count; i++)  +    { +      info->start[i] = base + (i * 0x00010000); +    } +   +  /* check for protected sectors */ +  for (i = 0; i < info->sector_count; i++)  +    { +      /* read sector protection at sector address, (A7 .. A0) = 0x02 */ +      /* D0 = 1 if protected */ +      addr = (volatile unsigned char *)(info->start[i]); +      info->protect[i] = addr[2] & 1; +    } + +  /* +   * Prevent writes to uninitialized FLASH. +   */ +  if ( info->flash_id != FLASH_UNKNOWN )  +    { +      addr = (volatile unsigned char *)info->start[0]; +       +      *addr = 0xF0;	/* reset bank */ +    } +   +  return (info->size); +} + + +/*----------------------------------------------------------------------- + */ + +int +flash_erase (flash_info_t  *info,  +	     int            s_first,  +	     int            s_last) +{ +  volatile unsigned char *addr = (volatile unsigned char *)(info->start[0]); +  int flag, prot, sect, l_sect; +  ulong start, now, last; +   +  if ( (s_first < 0) || (s_first > s_last) )  +    { +      if ( info->flash_id == FLASH_UNKNOWN )  +	{ +	  printf ("- missing\n"); +	}  +      else  +	{ +	  printf ("- no sectors to erase\n"); +	} +      return ( 1 ); +    } + +  if ( (info->flash_id == FLASH_UNKNOWN) || +       (info->flash_id > FLASH_AMD_COMP) )  +    { +      printf ("Can't erase unknown flash type %08lx - aborted\n", +	      info->flash_id); +      return ( 1 ); +    } +   +  prot = 0; +  for (sect=s_first; sect<=s_last; ++sect)  +    { +      if ( info->protect[sect] )  +	{ +	  prot++; +	} +    } +   +  if ( prot )  +    { +      printf ("- Warning: %d protected sectors will not be erased!\n", prot); +    }  +  else  +    { +      printf ("\n"); +    } +   +  l_sect = -1; +   +  /* Disable interrupts which might cause a timeout here */ +  flag = disable_interrupts(); +   +  addr[0x0555] = 0xAA; +  addr[0x02AA] = 0x55; +  addr[0x0555] = 0x80; +  addr[0x0555] = 0xAA; +  addr[0x02AA] = 0x55; +   +  /* Start erase on unprotected sectors */ +  for (sect = s_first; sect<=s_last; sect++)  +    { +      if (info->protect[sect] == 0)    /* not protected */ +	{ +	  addr = (volatile unsigned char *)(info->start[sect]); +	  addr[0] = 0x30; +	  l_sect = sect; +	} +    } +   +  /* re-enable interrupts if necessary */ +  if ( flag ) +    enable_interrupts(); +   +  /* wait at least 80us - let's wait 1 ms */ +  udelay (1000); +   +  /* +   * We wait for the last triggered sector +   */ +  if ( l_sect < 0 ) +    goto DONE; +   +  start = get_timer (0); +  last  = start; +  addr = (volatile unsigned char *)(info->start[l_sect]); +  while ( (addr[0] & 0x80) != 0x80 )  +    { +      if ( (now = get_timer(start)) > CFG_FLASH_ERASE_TOUT )  +	{ +	  printf ("Timeout\n"); +	  return ( 1 ); +	} +      /* show that we're waiting */ +      if ( (now - last) > 1000 )     /* every second */ +	{ +	  putc ('.'); +	  last = now; +	} +    } +   +DONE: +  /* reset to read mode */ +  addr = (volatile unsigned char *)info->start[0]; +  addr[0] = 0xF0;	/* reset bank */ +   +  printf (" done\n"); +   +  return ( 0 ); +} + +/*----------------------------------------------------------------------- + * Copy memory to flash, returns: + * 0 - OK + * 1 - write timeout + * 2 - Flash not erased + */ + +int  +write_buff (flash_info_t  *info,  +	    uchar         *src,  +	    ulong          addr,  +	    ulong          cnt) +{ +  ulong cp, wp, data; +  uchar bdata; +  int i, l, rc; + +  if ( (info->flash_id & FLASH_TYPEMASK) == FLASH_AM040 ) +    { +      /* Width of the data bus: 8 bits */ + +      wp = addr; +       +      while ( cnt ) +	{ +	  bdata = *src++; +	   +	  if ( (rc = write_byte(info, wp, bdata)) != 0 ) +	    { +	      return (rc); +	    } +	   +	  ++wp; +	  --cnt; +	} + +      return ( 0 ); +    } +  else +    { +      /* Width of the data bus: 32 bits */ + +      wp = (addr & ~3);	/* get lower word aligned address */ +       +      /* +       * handle unaligned start bytes +       */ +      if ( (l = addr - wp) != 0 )  +	{ +	  data = 0; +	  for (i=0, cp=wp; i<l; ++i, ++cp)  +	    { +	      data = (data << 8) | (*(uchar *)cp); +	    } +	  for (; i<4 && cnt>0; ++i)  +	    { +	      data = (data << 8) | *src++; +	      --cnt; +	      ++cp; +	    } +	  for (; cnt==0 && i<4; ++i, ++cp)  +	    { +	      data = (data << 8) | (*(uchar *)cp); +	    } +	   +	  if ( (rc = write_word(info, wp, data)) != 0 )  +	    { +	      return (rc); +	    } +	  wp += 4; +	} +       +      /* +       * handle word aligned part +       */ +      while ( cnt >= 4 )  +	{ +	  data = 0; +	  for (i=0; i<4; ++i)  +	    { +	      data = (data << 8) | *src++; +	    } +	  if ( (rc = write_word(info, wp, data)) != 0 )  +	    { +	      return (rc); +	    } +	  wp  += 4; +	  cnt -= 4; +	} +       +      if ( cnt == 0 )  +	{ +	  return (0); +	} +       +      /* +       * handle unaligned tail bytes +       */ +      data = 0; +      for (i=0, cp=wp; i<4 && cnt>0; ++i, ++cp)  +	{ +	  data = (data << 8) | *src++; +	  --cnt; +	} +      for (; i<4; ++i, ++cp)  +	{ +	  data = (data << 8) | (*(uchar *)cp); +	} +       +      return (write_word(info, wp, data)); +    } +} + +/*----------------------------------------------------------------------- + * Write a word to Flash, returns: + * 0 - OK + * 1 - write timeout + * 2 - Flash not erased + */ +static int  +write_word (flash_info_t  *info,  +	    ulong          dest,  +	    ulong          data) +{ +  vu_long *addr = (vu_long*)(info->start[0]); +  ulong start; +  int flag; +   +  /* Check if Flash is (sufficiently) erased */ +  if ( (*((vu_long *)dest) & data) != data )  +    { +      return (2); +    } +  /* Disable interrupts which might cause a timeout here */ +  flag = disable_interrupts(); +   +  addr[0x0555] = 0x00AA00AA; +  addr[0x02AA] = 0x00550055; +  addr[0x0555] = 0x00A000A0; +   +  *((vu_long *)dest) = data; +   +  /* re-enable interrupts if necessary */ +  if ( flag ) +    enable_interrupts(); + +  /* data polling for D7 */ +  start = get_timer (0); +  while ( (*((vu_long *)dest) & 0x00800080) != (data & 0x00800080) )  +    { +      if ( get_timer(start) > CFG_FLASH_WRITE_TOUT )  +	{ +	  return (1); +	} +    } + +  return (0); +} + +/*----------------------------------------------------------------------- + * Write a byte to Flash, returns: + * 0 - OK + * 1 - write timeout + * 2 - Flash not erased + */ +static int  +write_byte (flash_info_t  *info,  +	    ulong          dest,  +	    uchar          data) +{ +  volatile unsigned char *addr = (volatile unsigned char *)(info->start[0]); +  ulong  start; +  int    flag; +   +  /* Check if Flash is (sufficiently) erased */ +  if ( (*((volatile unsigned char *)dest) & data) != data )  +    { +      return (2); +    } +  /* Disable interrupts which might cause a timeout here */ +  flag = disable_interrupts(); +   +  addr[0x0555] = 0xAA; +  addr[0x02AA] = 0x55; +  addr[0x0555] = 0xA0; +   +  *((volatile unsigned char *)dest) = data; +   +  /* re-enable interrupts if necessary */ +  if ( flag ) +    enable_interrupts(); + +  /* data polling for D7 */ +  start = get_timer (0); +  while ( (*((volatile unsigned char *)dest) & 0x80) != (data & 0x80) )  +    { +      if ( get_timer(start) > CFG_FLASH_WRITE_TOUT )  +	{ +	  return (1); +	} +    } + +  return (0); +} + +/*----------------------------------------------------------------------- + */ |