diff options
Diffstat (limited to 'board/modnet50/flash.c')
-rw-r--r-- | board/modnet50/flash.c | 536 |
1 files changed, 536 insertions, 0 deletions
diff --git a/board/modnet50/flash.c b/board/modnet50/flash.c new file mode 100644 index 0000000..fc04d91 --- /dev/null +++ b/board/modnet50/flash.c @@ -0,0 +1,536 @@ +/* + * (C) Copyright 2002 + * MAZeT GmbH <www.mazet.de> + * Stephan Linz <linz@mazet.de>, <linz@li-pro.net> + * + * The most stuff comes from PPCBoot and Linux. + * + * IMMS gGmbH <www.imms.de> + * Thomas Elste <info@elste.org> + * + * Modifications for ModNET50 Board + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include <common.h> +#include <asm/arch/netarm_registers.h> + +#define SCR (*(volatile unsigned int *)(NETARM_GEN_MODULE_BASE + NETARM_GEN_SYSTEM_CONTROL)) + +#define ALIGN_ABORT_OFF SCR = SCR & ~NETARM_GEN_SYS_CFG_ALIGN_ABORT +#define ALIGN_ABORT_ON SCR = SCR | NETARM_GEN_SYS_CFG_ALIGN_ABORT + +#define PROG_ADDR (0x555*2) +#define SETUP_ADDR (0x555*2) +#define ID_ADDR (0x555*2) +#define UNLOCK_ADDR1 (0x555*2) +#define UNLOCK_ADDR2 (0x2AA*2) + +#define UNLOCK_CMD1 (0xAA) +#define UNLOCK_CMD2 (0x55) +#define ERASE_SUSPEND_CMD (0xB0) +#define ERASE_RESUME_CMD (0x30) +#define RESET_CMD (0xF0) +#define ID_CMD (0x90) +#define SECERASE_CMD (0x30) +#define CHIPERASE_CMD (0x10) +#define PROG_CMD (0xa0) +#define SETUP_CMD (0x80) + +#define DQ2 (0x04) +#define DQ3 (DQ2*2) +#define DQ5 (DQ3*4) +#define DQ6 (DQ5*2) + +#define WRITE_UNLOCK(addr) { \ + *(volatile __u16*)(addr + UNLOCK_ADDR1) = (__u16)UNLOCK_CMD1; \ + *(volatile __u16*)(addr + UNLOCK_ADDR2) = (__u16)UNLOCK_CMD2; \ +} + +#define CONFIG_AM29_RESERVED (0) +#define K (1024) +#define MB (4) + +#define CELL_SIZE (64*K) +#define DEVICE_SIZE (MB*K*K) +#define CELLS_PER_DEVICE (DEVICE_SIZE/CELL_SIZE) +#define RESERVED_CELLS (CONFIG_AM29_RESERVED*K)/CELL_SIZE +#define MAX_FLASH_DEVICES (1) +#define AVAIL_SIZE (DEVICE_SIZE*MAX_FLASH_DEVICES - RESERVED_CELLS*CELL_SIZE) + + +flash_info_t flash_info[CFG_MAX_FLASH_BANKS]; +static __u16 toggling_bits; + + +/*----------------------------------------------------------------------- + */ +ulong flash_get_size (ulong baseaddr, flash_info_t * info) +{ + short i; + __u16 flashtest; + + /* Write auto select command sequence and test FLASH answer */ + WRITE_UNLOCK (baseaddr); + *(volatile __u16 *) (baseaddr + ID_ADDR) = (__u16) ID_CMD; + flashtest /* manufacturer ID */ = *(volatile __u16 *) (baseaddr); + *(volatile __u16 *) (baseaddr + ID_ADDR) = (__u16) RESET_CMD; + + switch ((__u32) ((flashtest << 16) + flashtest)) { + case AMD_MANUFACT: + info->flash_id = FLASH_MAN_AMD & FLASH_VENDMASK; + break; + case FUJ_MANUFACT: + info->flash_id = FLASH_MAN_FUJ & FLASH_VENDMASK; + break; + default: + info->flash_id = FLASH_UNKNOWN; + info->sector_count = 0; + info->size = 0; + return (0); /* no or unknown flash */ + } + + /* Write auto select command sequence and test FLASH answer */ + WRITE_UNLOCK (baseaddr); + *(volatile __u16 *) (baseaddr + ID_ADDR) = (__u16) ID_CMD; + flashtest /* device ID */ = *(volatile __u16 *) (baseaddr + 2); + *(volatile __u16 *) (baseaddr + ID_ADDR) = (__u16) RESET_CMD; + + /* toggling_bits = (flashtest == TOSHIBA)?(DQ6):(DQ2|DQ6); */ + toggling_bits = (DQ2 | DQ6); + + switch ((__u32) ((flashtest << 16) + flashtest)) { + case AMD_ID_LV160B: + info->flash_id += + (FLASH_AM160LV | FLASH_AM160B) & FLASH_TYPEMASK; + info->sector_count = CFG_MAX_FLASH_SECT; + info->size = CFG_FLASH_SIZE; + /* 1*16K Boot Block + 2*8K Parameter Block + 1*32K Small Main Block */ + info->start[0] = baseaddr; + info->start[1] = baseaddr + 0x4000; + info->start[2] = baseaddr + 0x6000; + info->start[3] = baseaddr + 0x8000; + for (i = 1; i < info->sector_count; i++) + info->start[3 + i] = baseaddr + i * CFG_MAIN_SECT_SIZE; + break; + default: + info->flash_id = FLASH_UNKNOWN; + return (0); /* no or unknown flash */ + } + + for (i = 0; i < info->sector_count; i++) { + /* Write auto select command sequence and test FLASH answer */ + WRITE_UNLOCK (info->start[i]); + *(volatile __u16 *) (info->start[i] + ID_ADDR) = (__u16) ID_CMD; + flashtest /* protected verify */ = *(volatile __u16 *) (info->start[i] + 4); + *(volatile __u16 *) (info->start[i] + ID_ADDR) = (__u16) RESET_CMD; + if (flashtest & 0x0001) { + info->protect[i] = 1; /* D0 = 1 if protected */ + } else { + info->protect[i] = 0; + } + } + return (info->size); +} + +/*----------------------------------------------------------------------- + */ +ulong flash_init (void) +{ + ulong size = 0; + 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 (only one bank) */ + size = flash_get_size (CFG_FLASH_BASE, &flash_info[0]); + + if (flash_info[0].flash_id == FLASH_UNKNOWN || size == 0) { + printf ("## Unknown FLASH on Bank 0 - Size = 0x%08lx = %ld MB\n", + size, size >> 20); + } + + /* + * protect monitor and environment sectors + */ + flash_protect (FLAG_PROTECT_SET, + CFG_FLASH_BASE, + CFG_FLASH_BASE + monitor_flash_len - 1, + &flash_info[0]); + + flash_protect (FLAG_PROTECT_SET, + CFG_ENV_ADDR, + CFG_ENV_ADDR + CFG_ENV_SIZE - 1, &flash_info[0]); + + return size; +} + +/*----------------------------------------------------------------------- + */ +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; + default: + printf ("Unknown Vendor "); + break; + } + + switch (info->flash_id & FLASH_TYPEMASK) { + case FLASH_AMDL323B: + printf ("29DL323B (32 M, bottom sector)\n"); + break; + case (FLASH_AM160LV | FLASH_AM160B): + printf ("29LV160BE (1M x 16, bottom sector)\n"); + break; + default: + printf ("Unknown Chip Type\n"); + break; + } + + printf (" Size: %ld MB in %d Sectors\n", + info->size >> 20, info->sector_count); + printf (" Sector Start Addresses:"); + for (i = 0; i < info->sector_count; i++) { + if ((i % 4) == 0) + printf ("\n "); + printf (" S%02d @ 0x%08lX%s", i, + info->start[i], info->protect[i] ? " !" : " "); + } + printf ("\n"); + return; +} + +/*----------------------------------------------------------------------- + */ +int flash_check_protection (flash_info_t * info, int s_first, int s_last) +{ + int sect, prot = 0; + + for (sect = s_first; sect <= s_last; sect++) + if (info->protect[sect]) + prot++; + if (prot) + printf ("- can't erase %d protected sectors\n", prot); + return prot; +} + +/*----------------------------------------------------------------------- + */ +int flash_check_erase_amd (ulong start) +{ + __u16 v1, v2; + + v1 = *(volatile __u16 *) (start); + v2 = *(volatile __u16 *) (start); + + if (((v1 ^ v2) & toggling_bits) == toggling_bits) { + if (((v1 | v2) & DQ5) == DQ5) { + printf ("[DQ5] "); + /* OOPS: exceeded timing limits */ + + v1 = *(volatile __u16 *) (start); + v2 = *(volatile __u16 *) (start); + + if (((v1 ^ v2) & toggling_bits) == toggling_bits) { + + printf ("[%s] ", + ((toggling_bits & (DQ2 | DQ6)) == + (DQ2 | DQ6)) ? "DQ2,DQ6" : "DQ6"); + + /* OOPS: there is an erasure in progress, + * try to reset chip */ + *(volatile __u16 *) (start) = + (__u16) RESET_CMD; + + return 1; /* still busy */ + } + } + return 1; /* still busy */ + } + return 0; /* be free */ +} + +/*----------------------------------------------------------------------- + */ +int flash_erase (flash_info_t * info, int s_first, int s_last) +{ + int flag, sect, setup_offset = 0; + int rc = ERR_OK; + + if (info->flash_id == FLASH_UNKNOWN) { + printf ("- missing\n"); + return ERR_UNKNOWN_FLASH_TYPE; + } + + if ((s_first < 0) || (s_first > s_last)) { + printf ("- no sectors to erase\n"); + return ERR_INVAL; + } + + if (flash_check_protection (info, s_first, s_last)) + return ERR_PROTECTED; + + switch (info->flash_id & FLASH_VENDMASK) { + case FLASH_MAN_FUJ: + case FLASH_MAN_AMD: + switch (info->flash_id & FLASH_TYPEMASK) { + case (FLASH_AM160LV | FLASH_AM160B): + setup_offset = UNLOCK_ADDR1; /* just the adress for setup_cmd differs */ + case FLASH_AMDL323B: + /* + * Disable interrupts which might cause a timeout + * here. Remember that our exception vectors are + * at address 0 in the flash, and we don't want a + * (ticker) exception to happen while the flash + * chip is in programming mode. + */ + flag = disable_interrupts (); + /* Start erase on unprotected sectors */ + for (sect = s_first; sect <= s_last && !ctrlc (); + sect++) { + printf ("Erasing sector %2d ... ", sect); + + if (info->protect[sect] == 0) { + /* not protected */ + /* Write sector erase command sequence */ + WRITE_UNLOCK (info->start[0]); + *(volatile __u16 *) (info->start[0] + + setup_offset) = + (__u16) SETUP_CMD; + WRITE_UNLOCK (info->start[0]); + *(volatile __u16 *) (info-> + start[sect]) = + (__u16) SECERASE_CMD; + + /* wait some time */ + reset_timer_masked (); + while (get_timer_masked () < 1000) { + } + + /* arm simple, non interrupt dependent timer */ + reset_timer_masked (); + while (flash_check_erase_amd (info->start[sect])) { + if (get_timer_masked () > CFG_FLASH_ERASE_TOUT) { + printf ("timeout!\n"); + /* OOPS: reach timeout, + * try to reset chip + */ + *(volatile __u16 *) (info-> start[sect]) = (__u16) RESET_CMD; + rc = ERR_TIMOUT; + goto outahere_323B; + } + } + printf ("ok.\n"); + } else { + printf ("protected!\n"); + } + } + if (ctrlc ()) + printf ("User Interrupt!\n"); +outahere_323B: + /* allow flash to settle - wait 10 ms */ + udelay_masked (10000); + if (flag) + enable_interrupts (); + return rc; + default: + printf ("- unknown chip type\n"); + return ERR_UNKNOWN_FLASH_TYPE; + } + break; + default: + printf ("- unknown vendor "); + return ERR_UNKNOWN_FLASH_VENDOR; + } +} + +/*----------------------------------------------------------------------- + */ +int flash_check_write_amd (ulong dest) +{ + __u16 v1, v2; + + v1 = *(volatile __u16 *) (dest); + v2 = *(volatile __u16 *) (dest); + + /* DQ6 toggles during write */ + if (((v1 ^ v2) & DQ6) == DQ6) { + if (((v1 | v2) & DQ5) == DQ5) { + printf ("[DQ5] @ %08lX\n", dest); + + /* OOPS: exceeded timing limits, + * try to reset chip */ + *(volatile __u16 *) (dest) = (__u16) RESET_CMD; + return 0; /* be free */ + } + return 1; /* still busy */ + } + + return 0; /* be free */ +} + +/*----------------------------------------------------------------------- + * Copy memory to flash + */ +static int write_word (flash_info_t * info, ulong dest, ushort data) +{ + int rc = ERR_OK; + int flag; + + /* Check if Flash is (sufficiently) erased */ + if ((*(__u16 *) (dest) & data) != data) + return ERR_NOT_ERASED; + + /* + * Disable interrupts which might cause a timeout + * here. Remember that our exception vectors are + * at address 0 in the flash, and we don't want a + * (ticker) exception to happen while the flash + * chip is in programming mode. + */ + flag = disable_interrupts (); + + /* Write program command sequence */ + WRITE_UNLOCK (info->start[0]); + + /* Flash dependend program seqence */ + switch (info->flash_id & FLASH_VENDMASK) { + case FLASH_MAN_FUJ: + case FLASH_MAN_AMD: + switch (info->flash_id & FLASH_TYPEMASK) { + case (FLASH_AM160LV | FLASH_AM160B): + *(volatile __u16 *) (info->start[0] + UNLOCK_ADDR1) = + (__u16) PROG_CMD; + *(volatile __u16 *) (dest) = (__u16) data; + break; + case FLASH_AMDL323B: + *(volatile __u16 *) (dest) = (__u16) PROG_CMD; + *(volatile __u16 *) (dest) = (__u16) data; + break; + } + } + + /* arm simple, non interrupt dependent timer */ + reset_timer_masked (); + + while (flash_check_write_amd (dest)) { + if (get_timer_masked () > CFG_FLASH_WRITE_TOUT) { + printf ("timeout! @ %08lX\n", dest); + /* OOPS: reach timeout, + * try to reset chip */ + *(volatile __u16 *) (dest) = (__u16) RESET_CMD; + + rc = ERR_TIMOUT; + goto outahere_323B; + } + } + + /* Check if Flash was (accurately) written */ + if (*(__u16 *) (dest) != data) + rc = ERR_PROG_ERROR; + +outahere_323B: + if (flag) + enable_interrupts (); + return rc; +} + +/*----------------------------------------------------------------------- + * Copy memory to flash. + */ +int write_buff (flash_info_t * info, uchar * src, ulong addr, ulong cnt) +{ + ulong cp, wp; + ushort data; + int l; + int i, rc; + + wp = (addr & ~1); /* 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 << 8); + } + for (; i < 2 && cnt > 0; ++i) { + data = (data >> 8) | (*src++ << 8); + --cnt; + ++cp; + } + for (; cnt == 0 && i < 2; ++i, ++cp) { + data = (data >> 8) | (*(uchar *) cp << 8); + } + + if ((rc = write_word (info, wp, data)) != 0) { + return (rc); + } + wp += 2; + } + + /* + * handle word aligned part + */ + while (cnt >= 2) { + data = *((ushort *) src); + if ((rc = write_word (info, wp, data)) != 0) + return (rc); + src += 2; + wp += 2; + cnt -= 2; + } + + if (cnt == 0) + return ERR_OK; + + /* + * handle unaligned tail bytes + */ + data = 0; + for (i = 0, cp = wp; i < 2 && cnt > 0; ++i, ++cp) { + data = (data >> 8) | (*src++ << 8); + --cnt; + } + for (; i < 2; ++i, ++cp) { + data = (data >> 8) | (*(uchar *) cp << 8); + } + + return write_word (info, wp, data); +} |