diff options
Diffstat (limited to 'board/cogent/flash.c')
-rw-r--r-- | board/cogent/flash.c | 648 |
1 files changed, 648 insertions, 0 deletions
diff --git a/board/cogent/flash.c b/board/cogent/flash.c new file mode 100644 index 00000000000..86da80edb81 --- /dev/null +++ b/board/cogent/flash.c @@ -0,0 +1,648 @@ +/* + * (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 <board/cogent/flash.h> + +flash_info_t flash_info[CFG_MAX_FLASH_BANKS]; /* info for FLASH chips */ + +#if defined(CFG_ENV_IS_IN_FLASH) +# ifndef CFG_ENV_ADDR +# define CFG_ENV_ADDR (CFG_FLASH_BASE + CFG_ENV_OFFSET) +# endif +# ifndef CFG_ENV_SIZE +# define CFG_ENV_SIZE CFG_ENV_SECT_SIZE +# endif +# ifndef CFG_ENV_SECT_SIZE +# define CFG_ENV_SECT_SIZE CFG_ENV_SIZE +# endif +#endif + +/*----------------------------------------------------------------------- + * Functions + */ +static int write_word (flash_info_t *info, ulong dest, ulong data); + +/*----------------------------------------------------------------------- + */ + + +#if defined(CONFIG_CMA302) + +/* + * probe for the existence of flash at address "addr" + * 0 = yes, 1 = bad Manufacturer's Id, 2 = bad Device Id + */ +static int +c302f_probe_word(c302f_addr_t addr) +{ + /* reset the flash */ + *addr = C302F_BNK_CMD_RST; + + /* check the manufacturer id */ + *addr = C302F_BNK_CMD_RD_ID; + if (*C302F_BNK_ADDR_MAN(addr) != C302F_BNK_RD_ID_MAN) + return 1; + + /* check the device id */ + *addr = C302F_BNK_CMD_RD_ID; + if (*C302F_BNK_ADDR_DEV(addr) != C302F_BNK_RD_ID_DEV) + return 2; + +#ifdef FLASH_DEBUG + { + int i; + + printf("\nMaster Lock Config = 0x%08lx\n", + *C302F_BNK_ADDR_CFGM(addr)); + for (i = 0; i < C302F_BNK_NBLOCKS; i++) + printf("Block %2d Lock Config = 0x%08lx\n", + i, *C302F_BNK_ADDR_CFG(i, addr)); + } +#endif + + /* reset the flash again */ + *addr = C302F_BNK_CMD_RST; + + return 0; +} + +/* + * probe for Cogent CMA302 flash module at address "base" and store + * info for any found into flash_info entry "fip". Must find at least + * one bank. + */ +static void +c302f_probe(flash_info_t *fip, c302f_addr_t base) +{ + c302f_addr_t addr, eaddr; + int nbanks; + + fip->size = 0L; + fip->sector_count = 0; + + addr = base; + eaddr = C302F_BNK_ADDR_BASE(addr, C302F_MAX_BANKS); + nbanks = 0; + + while (addr < eaddr) { + c302f_addr_t addrw, eaddrw, addrb; + int i, osc, nsc; + + addrw = addr; + eaddrw = C302F_BNK_ADDR_NEXT_WORD(addrw); + + while (addrw < eaddrw) + if (c302f_probe_word(addrw++) != 0) + goto out; + + /* bank exists - append info for this bank to *fip */ + fip->flash_id = FLASH_MAN_INTEL|FLASH_28F008S5; + fip->size += C302F_BNK_SIZE; + osc = fip->sector_count; + fip->sector_count += C302F_BNK_NBLOCKS; + if ((nsc = fip->sector_count) >= CFG_MAX_FLASH_SECT) + panic("Too many sectors in flash at address 0x%08lx\n", + (unsigned long)base); + + addrb = addr; + for (i = osc; i < nsc; i++) { + fip->start[i] = (ulong)addrb; + fip->protect[i] = 0; + addrb = C302F_BNK_ADDR_NEXT_BLK(addrb); + } + + addr = C302F_BNK_ADDR_NEXT_BNK(addr); + nbanks++; + } + +out: + if (nbanks == 0) + panic("ERROR: no flash found at address 0x%08lx\n", + (unsigned long)base); +} + +static void +c302f_reset(flash_info_t *info, int sect) +{ + c302f_addr_t addrw, eaddrw; + + addrw = (c302f_addr_t)info->start[sect]; + eaddrw = C302F_BNK_ADDR_NEXT_WORD(addrw); + + while (addrw < eaddrw) { +#ifdef FLASH_DEBUG + printf(" writing reset cmd to addr 0x%08lx\n", + (unsigned long)addrw); +#endif + *addrw = C302F_BNK_CMD_RST; + addrw++; + } +} + +static void +c302f_erase_init(flash_info_t *info, int sect) +{ + c302f_addr_t addrw, saddrw, eaddrw; + int flag; + +#ifdef FLASH_DEBUG + printf("0x%08lx C302F_BNK_CMD_PROG\n", C302F_BNK_CMD_PROG); + printf("0x%08lx C302F_BNK_CMD_ERASE1\n", C302F_BNK_CMD_ERASE1); + printf("0x%08lx C302F_BNK_CMD_ERASE2\n", C302F_BNK_CMD_ERASE2); + printf("0x%08lx C302F_BNK_CMD_CLR_STAT\n", C302F_BNK_CMD_CLR_STAT); + printf("0x%08lx C302F_BNK_CMD_RST\n", C302F_BNK_CMD_RST); + printf("0x%08lx C302F_BNK_STAT_RDY\n", C302F_BNK_STAT_RDY); + printf("0x%08lx C302F_BNK_STAT_ERR\n", C302F_BNK_STAT_ERR); +#endif + + saddrw = (c302f_addr_t)info->start[sect]; + eaddrw = C302F_BNK_ADDR_NEXT_WORD(saddrw); + +#ifdef FLASH_DEBUG + printf("erasing sector %d, start addr = 0x%08lx " + "(bank next word addr = 0x%08lx)\n", sect, + (unsigned long)saddrw, (unsigned long)eaddrw); +#endif + + /* Disable intrs which might cause a timeout here */ + flag = disable_interrupts(); + + for (addrw = saddrw; addrw < eaddrw; addrw++) { +#ifdef FLASH_DEBUG + printf(" writing erase cmd to addr 0x%08lx\n", + (unsigned long)addrw); +#endif + *addrw = C302F_BNK_CMD_ERASE1; + *addrw = C302F_BNK_CMD_ERASE2; + } + + /* re-enable interrupts if necessary */ + if (flag) + enable_interrupts(); +} + +static int +c302f_erase_poll(flash_info_t *info, int sect) +{ + c302f_addr_t addrw, saddrw, eaddrw; + int sectdone, haderr; + + saddrw = (c302f_addr_t)info->start[sect]; + eaddrw = C302F_BNK_ADDR_NEXT_WORD(saddrw); + + sectdone = 1; + haderr = 0; + + for (addrw = saddrw; addrw < eaddrw; addrw++) { + c302f_word_t stat = *addrw; + +#ifdef FLASH_DEBUG + printf(" checking status at addr " + "0x%08lx [0x%08lx]\n", + (unsigned long)addrw, stat); +#endif + if ((stat & C302F_BNK_STAT_RDY) != C302F_BNK_STAT_RDY) + sectdone = 0; + else if ((stat & C302F_BNK_STAT_ERR) != 0) { + printf(" failed on sector %d " + "(stat = 0x%08lx) at " + "address 0x%08lx\n", + sect, stat, + (unsigned long)addrw); + *addrw = C302F_BNK_CMD_CLR_STAT; + haderr = 1; + } + } + + if (haderr) + return (-1); + else + return (sectdone); +} + +static int +c302f_write_word(c302f_addr_t addr, c302f_word_t value) +{ + c302f_word_t stat; + ulong start; + int flag, retval; + + /* Disable interrupts which might cause a timeout here */ + flag = disable_interrupts(); + + *addr = C302F_BNK_CMD_PROG; + + *addr = value; + + /* re-enable interrupts if necessary */ + if (flag) + enable_interrupts(); + + retval = 0; + + /* data polling for D7 */ + start = get_timer (0); + do { + if (get_timer(start) > CFG_FLASH_WRITE_TOUT) { + retval = 1; + goto done; + } + stat = *addr; + } while ((stat & C302F_BNK_STAT_RDY) != C302F_BNK_STAT_RDY); + + if ((stat & C302F_BNK_STAT_ERR) != 0) { + printf("flash program failed (stat = 0x%08lx) " + "at address 0x%08lx\n", (ulong)stat, (ulong)addr); + *addr = C302F_BNK_CMD_CLR_STAT; + retval = 3; + } + +done: + /* reset to read mode */ + *addr = C302F_BNK_CMD_RST; + + return (retval); +} + +#endif /* CONFIG_CMA302 */ + +unsigned long +flash_init(void) +{ + unsigned long total; + int i; + flash_info_t *fip; + + /* Init: no FLASHes known */ + for (i=0; i<CFG_MAX_FLASH_BANKS; ++i) { + flash_info[i].flash_id = FLASH_UNKNOWN; + } + + fip = &flash_info[0]; + total = 0L; + +#if defined(CONFIG_CMA302) + c302f_probe(fip, (c302f_addr_t)CFG_FLASH_BASE); + total += fip->size; + fip++; +#endif + +#if (CMA_MB_CAPS & CMA_MB_CAP_FLASH) + /* not yet ... + cmbf_probe(fip, (cmbf_addr_t)CMA_MB_FLASH_BASE); + total += fip->size; + fip++; + */ +#endif + + /* + * protect monitor and environment sectors + */ + +#if CFG_MONITOR_BASE == CFG_FLASH_BASE + 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_SECT_SIZE-1, + &flash_info[0]); +#endif + return total; +} + +/*----------------------------------------------------------------------- + */ +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_INTEL: printf ("INTEL "); break; + default: printf ("Unknown Vendor "); break; + } + + switch (info->flash_id & FLASH_TYPEMASK) { + case FLASH_28F008S5: printf ("28F008S5\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 (" %2d - %08lX%s", i, + info->start[i], + info->protect[i] ? " (RO)" : " " + ); + } + printf ("\n"); + return; +} + +/*----------------------------------------------------------------------- + */ + + +/*----------------------------------------------------------------------- + */ + +/* + * The following code cannot be run from FLASH! + */ + +int +flash_erase(flash_info_t *info, int s_first, int s_last) +{ + int prot, sect, haderr; + ulong start, now, last; + void (*erase_init)(flash_info_t *, int); + int (*erase_poll)(flash_info_t *, int); + void (*reset)(flash_info_t *, int); + int rcode = 0; + +#ifdef FLASH_DEBUG + printf("\nflash_erase: erase %d sectors (%d to %d incl.) from\n" + " Bank # %d: ", s_last - s_first + 1, s_first, s_last, + (info - flash_info) + 1); + flash_print_info(info); +#endif + + 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; + } + + switch (info->flash_id) { + +#if defined(CONFIG_CMA302) + case FLASH_MAN_INTEL|FLASH_28F008S5: + erase_init = c302f_erase_init; + erase_poll = c302f_erase_poll; + reset = c302f_reset; + break; +#endif + +#if (CMA_MB_CAPS & CMA_MB_CAP_FLASH) + case FLASH_MAN_INTEL|FLASH_28F800_B: + case FLASH_MAN_AMD|FLASH_AM29F800B: + /* not yet ... + erase_init = cmbf_erase_init; + erase_poll = cmbf_erase_poll; + reset = cmbf_reset; + break; + */ +#endif + + default: + printf ("Flash type %08lx not supported - 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 sector%s will not be erased!\n", + prot, (prot > 1 ? "s" : "")); + } + + start = get_timer (0); + last = 0; + haderr = 0; + + for (sect = s_first; sect <= s_last; sect++) { + if (info->protect[sect] == 0) { /* not protected */ + ulong estart; + int sectdone; + + (*erase_init)(info, sect); + + /* wait at least 80us - let's wait 1 ms */ + udelay (1000); + + estart = get_timer(start); + + do { + now = get_timer(start); + + if (now - estart > CFG_FLASH_ERASE_TOUT) { + printf ("Timeout (sect %d)\n", sect); + haderr = 1; + break; + } + +#ifndef FLASH_DEBUG + /* show that we're waiting */ + if ((now - last) > 1000) { /* every second */ + putc ('.'); + last = now; + } +#endif + + sectdone = (*erase_poll)(info, sect); + + if (sectdone < 0) { + haderr = 1; + break; + } + + } while (!sectdone); + + if (haderr) + break; + } + } + + if (haderr > 0) { + printf (" failed\n"); + rcode = 1; + } + else + printf (" done\n"); + + /* reset to read mode */ + for (sect = s_first; sect <= s_last; sect++) { + if (info->protect[sect] == 0) { /* not protected */ + (*reset)(info, sect); + } + } + return rcode; +} + +/*----------------------------------------------------------------------- + * Copy memory to flash, returns: + * 0 - OK + * 1 - write timeout + * 2 - Flash not erased + * 3 - write error + */ + +int +write_buff(flash_info_t *info, uchar *src, ulong addr, ulong cnt) +{ + ulong cp, wp, data; + int i, l, rc; + ulong start, now, last; + + 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 + */ + start = get_timer (0); + last = 0; + 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; + + /* show that we're waiting */ + now = get_timer(start); + if ((now - last) > 1000) { /* every second */ + putc ('.'); + last = now; + } + } + + 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 + * 3 - write error + */ +static int +write_word(flash_info_t *info, ulong dest, ulong data) +{ + int retval; + + /* Check if Flash is (sufficiently) erased */ + if ((*(ulong *)dest & data) != data) { + return (2); + } + + switch (info->flash_id) { + +#if defined(CONFIG_CMA302) + case FLASH_MAN_INTEL|FLASH_28F008S5: + retval = c302f_write_word((c302f_addr_t)dest, (c302f_word_t)data); + break; +#endif + +#if (CMA_MB_CAPS & CMA_MB_CAP_FLASH) + case FLASH_MAN_INTEL|FLASH_28F800_B: + case FLASH_MAN_AMD|FLASH_AM29F800B: + /* not yet ... + retval = cmbf_write_word((cmbf_addr_t)dest, (cmbf_word_t)data); + */ + retval = 3; + break; +#endif + + default: + printf ("Flash type %08lx not supported - aborted\n", + info->flash_id); + retval = 3; + break; + } + + return (retval); +} + +/*----------------------------------------------------------------------- + */ |