diff options
Diffstat (limited to 'board/eltec/elppc/flash.c')
| -rw-r--r-- | board/eltec/elppc/flash.c | 513 | 
1 files changed, 513 insertions, 0 deletions
| diff --git a/board/eltec/elppc/flash.c b/board/eltec/elppc/flash.c new file mode 100644 index 000000000..5834c9999 --- /dev/null +++ b/board/eltec/elppc/flash.c @@ -0,0 +1,513 @@ +/* + * (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 + */ + +/* + * 07-10-2002 Frank Gottschling: added 29F032 flash (ELPPC). + *        fixed monitor protection part + * + * 09-18-2001 Andreas Heppel: Reduced the code in here to the usage + *        of AMD's 29F040 and 29F016 flashes, since the BAB7xx does use + *        any other. + */ + +#include <common.h> +#include <asm/processor.h> +#include <asm/pci_io.h> + +flash_info_t    flash_info[CFG_MAX_FLASH_BANKS]; /* info for FLASH chips */ + +ulong flash_get_size (vu_long *addr, flash_info_t *info); +static int write_word (flash_info_t *info, ulong dest, ulong data); + +/*flash command address offsets*/ + +#define ADDR0           (0x555) +#define ADDR1           (0x2AA) +#define ADDR3           (0x001) + +#define FLASH_WORD_SIZE unsigned char + +/*----------------------------------------------------------------------------*/ + +unsigned long flash_init (void) +{ +    unsigned long size1, size2; +    int i; + +    /* Init: no FLASHes known */ +    for (i=0; i<CFG_MAX_FLASH_BANKS; ++i) +    { +        flash_info[i].flash_id = FLASH_UNKNOWN; +    } + +    /* initialise 1st flash */ +    size1 = flash_get_size((vu_long *)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", +            size1, size1<<20); +    } + +    /* initialise 2nd flash */ +    size2 = flash_get_size((vu_long *)FLASH_BASE1_PRELIM, &flash_info[1]); + +    if (flash_info[1].flash_id == FLASH_UNKNOWN) +    { +        printf ("## Unknown FLASH on Bank 1 - Size = 0x%08lx = %ld MB\n", +            size2, size2<<20); +    } + +    /* monitor protection ON by default */ +    if (size1 == 512*1024) +    { +        (void)flash_protect(FLAG_PROTECT_SET, +                FLASH_BASE0_PRELIM, +                FLASH_BASE0_PRELIM+CFG_MONITOR_LEN-1, +                &flash_info[0]); +    } +    if (size2 == 512*1024) +    { +        (void)flash_protect(FLAG_PROTECT_SET, +                FLASH_BASE1_PRELIM, +                FLASH_BASE1_PRELIM+CFG_MONITOR_LEN-1, +                &flash_info[1]); +    } +    if (size2 == 4*1024*1024) +    { +        (void)flash_protect(FLAG_PROTECT_SET, +                CFG_FLASH_BASE, +                CFG_FLASH_BASE+CFG_MONITOR_LEN-1, +                &flash_info[1]); +    } + +    return (size1 + size2); +} + +/*----------------------------------------------------------------------------*/ + +void flash_print_info  (flash_info_t *info) +{ +    int i; +    int k; +    int size; +    int erased; +    volatile unsigned long *flash; + +    if (info->flash_id == FLASH_UNKNOWN) { +        printf ("missing or unknown FLASH type\n"); +        flash_init(); +    } + +    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; +    default: +        printf ("Unknown Vendor "); +        break; +    } + +    switch (info->flash_id & FLASH_TYPEMASK) { +    case AMD_ID_F040B: +        printf ("AM29F040B (4 Mbit)\n"); +        break; +    case AMD_ID_F016D: +        printf ("AM29F016D (16 Mbit)\n"); +        break; +    case AMD_ID_F032B: +        printf ("AM29F032B (32 Mbit)\n"); +        break; +   default: +        printf ("Unknown Chip Type\n"); +        break; +    } + +    if (info->size >= (1 << 20)) { +        printf ("  Size: %ld MB in %d Sectors\n", info->size >> 20, info->sector_count); +    } else { +        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) { +        /* +        * Check if whole sector is erased +        */ +        if (i != (info->sector_count-1)) +            size = info->start[i+1] - info->start[i]; +        else +            size = info->start[0] + info->size - info->start[i]; + +        erased = 1; +        flash = (volatile unsigned long *)info->start[i]; +        size = size >> 2;        /* divide by 4 for longword access */ +        for (k=0; k<size; k++) { +            if (*flash++ != 0xffffffff) { +                erased = 0; +                break; +            } +        } + +        if ((i % 5) == 0) +            printf ("\n   "); + +        printf (" %08lX%s%s", +            info->start[i], +            erased ? " E" : "  ", +            info->protect[i] ? "RO " : "   "); +    } +    printf ("\n"); +} + +/*----------------------------------------------------------------------------*/ +/* + * The following code cannot be run from FLASH! + */ +ulong flash_get_size (vu_long *addr, flash_info_t *info) +{ +    short i; +    ulong vendor, devid; +    ulong base = (ulong)addr; +    volatile unsigned char *caddr = (unsigned char *)addr; + +#ifdef DEBUG +    printf("flash_get_size for address 0x%lx: \n", (unsigned long)caddr); +#endif + +    /* Write auto select command: read Manufacturer ID */ +    caddr[0] = 0xF0;   /* reset bank */ +    udelay(10); + +    eieio(); +    caddr[0x555] = 0xAA; +    udelay(10); +    caddr[0x2AA] = 0x55; +    udelay(10); +    caddr[0x555] = 0x90; + +    udelay(10); + +    vendor = caddr[0]; +    devid = caddr[1]; + +#ifdef DEBUG +    printf("Manufacturer: 0x%lx\n", vendor); +#endif + +    vendor &= 0xff; +    devid &= 0xff; + +    /* We accept only two AMD types */ +    switch (vendor) { +    case (FLASH_WORD_SIZE)AMD_MANUFACT: +        info->flash_id = FLASH_MAN_AMD; +        break; +    default: +        info->flash_id = FLASH_UNKNOWN; +        info->sector_count = 0; +        info->size = 0; +        return (0);         /* no or unknown flash  */ +    } + +    switch (devid) { +    case (FLASH_WORD_SIZE)AMD_ID_F040B: +        info->flash_id |= AMD_ID_F040B; +        info->sector_count = 8; +        info->size = 0x00080000; +        break;              /* => 0.5 MB      */ + +    case (FLASH_WORD_SIZE)AMD_ID_F016D: +        info->flash_id |= AMD_ID_F016D; +        info->sector_count = 32; +        info->size         = 0x00200000; +        break;              /* => 2 MB      */ + +    case (FLASH_WORD_SIZE)AMD_ID_F032B: +        info->flash_id |= AMD_ID_F032B; +        info->sector_count = 64; +        info->size         = 0x00400000; +        break;              /* => 4 MB      */ + +    default: +        info->flash_id = FLASH_UNKNOWN; +        return (0);         /* => no or unknown flash */ + +    } + +#ifdef DEBUG +    printf("flash id 0x%lx; sector count 0x%x, size 0x%lx\n", info->flash_id, info->sector_count, info->size); +#endif + +    /* check for protected sectors */ +    for (i = 0; i < info->sector_count; i++) { +        /* sector base address */ +        info->start[i] = base + i * (info->size / info->sector_count); +        /* read sector protection at sector address, (A7 .. A0) = 0x02 */ +        /* D0 = 1 if protected */ +        caddr = (volatile unsigned char *)(info->start[i]); +        info->protect[i] = caddr[2] & 1; +    } + +    /* +     * Prevent writes to uninitialized FLASH. +     */ +    if (info->flash_id != FLASH_UNKNOWN) { +        caddr = (volatile unsigned char *)info->start[0]; +        caddr[0] = 0xF0;   /* reset bank */ +    } + +    return (info->size); +} + +/*----------------------------------------------------------------------------*/ + +int flash_erase (flash_info_t *info, int s_first, int s_last) +{ +    volatile FLASH_WORD_SIZE *addr = (FLASH_WORD_SIZE *)(info->start[0]); +    int flag, prot, sect, l_sect; +    ulong start, now, last; +    int rc = 0; + +    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 - aborted\n"); +        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[ADDR0] = (FLASH_WORD_SIZE)0x00AA00AA; +    addr[ADDR1] = (FLASH_WORD_SIZE)0x00550055; +    addr[ADDR0] = (FLASH_WORD_SIZE)0x00800080; +    addr[ADDR0] = (FLASH_WORD_SIZE)0x00AA00AA; +    addr[ADDR1] = (FLASH_WORD_SIZE)0x00550055; + +    /* Start erase on unprotected sectors */ +    for (sect = s_first; sect<=s_last; sect++) { +        if (info->protect[sect] == 0) { /* not protected */ +            addr = (FLASH_WORD_SIZE *)(info->start[sect]); +            if (info->flash_id & FLASH_MAN_SST) { +                addr[ADDR0] = (FLASH_WORD_SIZE)0x00AA00AA; +                addr[ADDR1] = (FLASH_WORD_SIZE)0x00550055; +                addr[ADDR0] = (FLASH_WORD_SIZE)0x00800080; +                addr[ADDR0] = (FLASH_WORD_SIZE)0x00AA00AA; +                addr[ADDR1] = (FLASH_WORD_SIZE)0x00550055; +                addr[0] = (FLASH_WORD_SIZE)0x00500050;  /* block erase */ +                udelay(30000);  /* wait 30 ms */ +            } +            else +                addr[0] = (FLASH_WORD_SIZE)0x00300030;  /* sector erase */ +            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 = (FLASH_WORD_SIZE *)(info->start[l_sect]); +    while ((addr[0] & (FLASH_WORD_SIZE)0x00800080) != (FLASH_WORD_SIZE)0x00800080) { +        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 */ +            serial_putc ('.'); +            last = now; +        } +    } + +DONE: +    /* reset to read mode */ +    addr = (FLASH_WORD_SIZE *)info->start[0]; +    addr[0] = (FLASH_WORD_SIZE)0x00F000F0;  /* reset bank */ + +    printf (" done\n"); +    return rc; +} + +/*----------------------------------------------------------------------------*/ +/* + * 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; +    int i, l, rc; + +    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) +{ +        volatile FLASH_WORD_SIZE *addr2 = (FLASH_WORD_SIZE *)(info->start[0]); +        volatile FLASH_WORD_SIZE *dest2 = (FLASH_WORD_SIZE *)dest; +        volatile FLASH_WORD_SIZE *data2 = (FLASH_WORD_SIZE *)&data; +    ulong start; +    int flag; +        int i; + +    /* Check if Flash is (sufficiently) erased */ +    if ((*((volatile FLASH_WORD_SIZE *)dest) & +             (FLASH_WORD_SIZE)data) != (FLASH_WORD_SIZE)data) { +        return (2); +    } +    /* Disable interrupts which might cause a timeout here */ +    flag = disable_interrupts(); + +        for (i=0; i<4/sizeof(FLASH_WORD_SIZE); i++) +          { +            addr2[ADDR0] = (FLASH_WORD_SIZE)0x00AA00AA; +            addr2[ADDR1] = (FLASH_WORD_SIZE)0x00550055; +            addr2[ADDR0] = (FLASH_WORD_SIZE)0x00A000A0; + +            dest2[i] = data2[i]; + +            /* re-enable interrupts if necessary */ +            if (flag) +              enable_interrupts(); + +            /* data polling for D7 */ +            start = get_timer (0); +            while ((dest2[i] & (FLASH_WORD_SIZE)0x00800080) != +                   (data2[i] & (FLASH_WORD_SIZE)0x00800080)) { +              if (get_timer(start) > CFG_FLASH_WRITE_TOUT) { +                return (1); +              } +            } +          } + +    return (0); +} + +/*----------------------------------------------------------------------------*/ + |