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 0000000..a9238e1 --- /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); +} + +/*----------------------------------------------------------------------- + */ |