diff options
Diffstat (limited to 'board/flagadm')
-rw-r--r-- | board/flagadm/flash.c | 697 |
1 files changed, 697 insertions, 0 deletions
diff --git a/board/flagadm/flash.c b/board/flagadm/flash.c new file mode 100644 index 0000000..fc5d963 --- /dev/null +++ b/board/flagadm/flash.c @@ -0,0 +1,697 @@ +/* + * (C) Copyright 2001 + * 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 <mpc8xx.h> +#include <flash.h> + +flash_info_t flash_info[CFG_MAX_FLASH_BANKS]; /* info for FLASH chips */ + +/*----------------------------------------------------------------------- + * Functions + */ +ulong flash_recognize (vu_long *base); +int write_word (flash_info_t *info, ulong dest, ulong data); +void flash_get_geometry (vu_long *base, flash_info_t *info); +void flash_unprotect(flash_info_t *info); +int _flash_real_protect(flash_info_t *info, long idx, int on); + + +unsigned long flash_init (void) +{ + volatile immap_t *immap = (immap_t *)CFG_IMMR; + volatile memctl8xx_t *memctl = &immap->im_memctl; + int i; + int rec; + + for (i=0; i<CFG_MAX_FLASH_BANKS; ++i) { + flash_info[i].flash_id = FLASH_UNKNOWN; + } + + *((vu_short*)CFG_FLASH_BASE) = 0xffff; + + flash_get_geometry ((vu_long*)CFG_FLASH_BASE, &flash_info[0]); + + /* Remap FLASH according to real size */ + memctl->memc_or0 = CFG_OR_TIMING_FLASH | (-flash_info[0].size & 0xFFFF8000); + memctl->memc_br0 = (CFG_FLASH_BASE & BR_BA_MSK) | + (memctl->memc_br0 & ~(BR_BA_MSK)); + + rec = flash_recognize((vu_long*)CFG_FLASH_BASE); + + if (rec == FLASH_UNKNOWN) { + printf ("## Unknown FLASH on Bank 0 - Size = 0x%08lx = %ld MB\n", + flash_info[0].size, flash_info[0].size<<20); + } + +#if CFG_FLASH_PROTECTION + /*Unprotect all the flash memory*/ + flash_unprotect(&flash_info[0]); +#endif + + *((vu_short*)CFG_FLASH_BASE) = 0xffff; + + return (flash_info[0].size); + +#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_OFFSET, + CFG_ENV_OFFSET+CFG_ENV_SIZE-1, + &flash_info[0]); +#endif + return (flash_info[0].size); +} + + +int flash_get_protect_status(flash_info_t * info, long idx) +{ + vu_short * base; + ushort res; + +#ifdef DEBUG + printf("\n Attempting to set protection info with %d sectors\n", info->sector_count); +#endif + + + base = (vu_short*)info->start[idx]; + + *(base) = 0xffff; + + *(base + 0x55) = 0x0098; + res = base[0x2]; + + *(base) = 0xffff; + + if(res != 0) + res = 1; + else + res = 0; + + return res; +} + +void flash_get_geometry (vu_long *base, flash_info_t *info) +{ + int i,j; + ulong ner = 0; + vu_short * sb = (vu_short*)base; + ulong offset = (ulong)base; + + /* Read Device geometry */ + + *sb = 0xffff; + + *sb = 0x0090; + + info->flash_id = ((ulong)base[0x0]); +#ifdef DEBUG + printf("Id is %x\n", (uint)(ulong)info->flash_id); +#endif + + *sb = 0xffff; + + *(sb+0x55) = 0x0098; + + info->size = 1 << (sb[0x27]); /* Read flash size */ + +#ifdef DEBUG + printf("Size is %x\n", (uint)(ulong)info->size); +#endif + + *sb = 0xffff; + + *(sb + 0x55) = 0x0098; + ner = sb[0x2c] ; /*Number of erase regions*/ + +#ifdef DEBUG + printf("Number of erase regions %x\n", (uint)ner); +#endif + + info->sector_count = 0; + + for(i = 0; i < ner; i++) + { + uint s; + uint count; + uint t1,t2,t3,t4; + + *sb = 0xffff; + + *(sb + 0x55) = 0x0098; + + t1 = sb[0x2d + i*4]; + t2 = sb[0x2e + i*4]; + t3 = sb[0x2f + i*4]; + t4 = sb[0x30 + i*4]; + + count = ((t1 & 0x00ff) | (((t2 & 0x00ff) << 8) & 0xff00) )+ 1; /*sector count*/ + s = ((t3 & 0x00ff) | (((t4 & 0x00ff) << 8) & 0xff00)) * 256;; /*Sector size*/ + +#ifdef DEBUG + printf("count and size %x, %x\n", count, s); + printf("sector count for erase region %d is %d\n", i, count); +#endif + for(j = 0; j < count; j++) + { +#ifdef DEBUG + printf("%x, ", (uint)offset); +#endif + info->start[ info->sector_count + j] = offset; + offset += s; + } + info->sector_count += count; + } + + if ((offset - (ulong)base) != info->size) + printf("WARNING reported size %x does not match to calculted size %x.\n" + , (uint)info->size, (uint)(offset - (ulong)base) ); + + /* Next check if there are any sectors protected.*/ + + for(i = 0; i < info->sector_count; i++) + info->protect[i] = flash_get_protect_status(info, i); + + *sb = 0xffff; +} + +/*----------------------------------------------------------------------- + */ +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 INTEL_MANUFACT & FLASH_VENDMASK: + printf ("Intel "); + break; + default: + printf ("Unknown Vendor "); + break; + } + + switch (info->flash_id & FLASH_TYPEMASK) { + case INTEL_ID_28F320C3B & FLASH_TYPEMASK: + printf ("28F320RC3(4 MB)\n"); + break; + case INTEL_ID_28F320J3A: + printf("28F320J3A (4 MB)\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 (" %02d %08lX%s", + i, info->start[i], + info->protect[i]!=0 ? " (RO)" : " " + ); + } + printf ("\n"); + return ; +} + +ulong flash_recognize (vu_long *base) +{ + ulong id; + ulong res = FLASH_UNKNOWN; + vu_short * sb = (vu_short*)base; + + *sb = 0xffff; + + *sb = 0x0090; + id = base[0]; + + switch (id & 0x00FF0000) + { + case (MT_MANUFACT & 0x00FF0000): /* MT or => Intel */ + case (INTEL_ALT_MANU & 0x00FF0000): + res = FLASH_MAN_INTEL; + break; + default: + res = FLASH_UNKNOWN; + } + + *sb = 0xffff; + + return res; +} + +/*-----------------------------------------------------------------------*/ +#define INTEL_FLASH_STATUS_BLS 0x02 +#define INTEL_FLASH_STATUS_PSS 0x04 +#define INTEL_FLASH_STATUS_VPPS 0x08 +#define INTEL_FLASH_STATUS_PS 0x10 +#define INTEL_FLASH_STATUS_ES 0x20 +#define INTEL_FLASH_STATUS_ESS 0x40 +#define INTEL_FLASH_STATUS_WSMS 0x80 + +int flash_decode_status_bits(char status) +{ + int err = 0; + + if(!(status & INTEL_FLASH_STATUS_WSMS)) { + printf("Busy\n"); + err = -1; + } + + if(status & INTEL_FLASH_STATUS_ESS) { + printf("Erase suspended\n"); + err = -1; + } + + if(status & INTEL_FLASH_STATUS_ES) { + printf("Error in block erase\n"); + err = -1; + } + + if(status & INTEL_FLASH_STATUS_PS) { + printf("Error in programming\n"); + err = -1; + } + + if(status & INTEL_FLASH_STATUS_VPPS) { + printf("Vpp low, operation aborted\n"); + err = -1; + } + + if(status & INTEL_FLASH_STATUS_PSS) { + printf("Program is suspended\n"); + err = -1; + } + + if(status & INTEL_FLASH_STATUS_BLS) { + printf("Attempting to program/erase a locked sector\n"); + err = -1; + } + + if((status & INTEL_FLASH_STATUS_PS) && + (status & INTEL_FLASH_STATUS_ES) && + (status & INTEL_FLASH_STATUS_ESS)) { + printf("A command sequence error\n"); + return -1; + } + + return err; +} + +/*----------------------------------------------------------------------- + */ + +int flash_erase (flash_info_t *info, int s_first, int s_last) +{ + vu_short *addr; + int flag, prot, sect; + ulong start, now; + int rcode = 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_VENDMASK) != (INTEL_MANUFACT & FLASH_VENDMASK)) { + 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"); + } + + start = get_timer (0); + + /* Start erase on unprotected sectors */ + for (sect = s_first; sect<=s_last; sect++) { + char tmp; + + if (info->protect[sect] == 0) { /* not protected */ + addr = (vu_short *)(info->start[sect]); + + /* Disable interrupts which might cause a timeout here */ + flag = disable_interrupts(); + + /* Single Block Erase Command */ + *addr = 0x0020; + /* Confirm */ + *addr = 0x00D0; + /* Resume Command, as per errata update */ + *addr = 0x00D0; + + /* re-enable interrupts if necessary */ + if (flag) + enable_interrupts(); + + *addr = 0x70; /*Read status register command*/ + tmp = (short)*addr & 0x00FF; /* Read the status */ + while (!(tmp & INTEL_FLASH_STATUS_WSMS)) { + if ((now=get_timer(start)) > CFG_FLASH_ERASE_TOUT) { + *addr = 0x0050; /* Reset the status register */ + *addr = 0xffff; + printf ("Timeout\n"); + return 1; + } + /* show that we're waiting */ + if ((now - start) > 1000) { /* every second */ + putc ('.'); + } + udelay(100000); /* 100 ms */ + *addr = 0x0070; /*Read status register command*/ + tmp = (short)*addr & 0x00FF; /* Read status */ + start = get_timer(0); + } + if( tmp & INTEL_FLASH_STATUS_ES ) + flash_decode_status_bits(tmp); + + *addr = 0x0050; /* Reset the status register */ + *addr = 0xffff; /* Reset to read mode */ + } + } + + + printf (" done\n"); + return rcode; +} + +void flash_unprotect (flash_info_t *info) +{ + /*We can only unprotect the whole flash at once*/ + /*Therefore we must prevent the _flash_real_protect()*/ + /*from re-protecting sectors, that ware protected before */ + /*we called flash_real_protect();*/ + + int i; + + for(i = 0; i < info->sector_count; i++) + info->protect[i] = 0; + +#ifdef CFG_FLASH_PROTECTION + _flash_real_protect(info, 0, 0); +#endif +} + +/*----------------------------------------------------------------------- + * 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 + */ +int write_word (flash_info_t *info, ulong dest, ulong da) +{ + vu_short *addr = (vu_short *)dest; + ulong start; + char csr; + int flag; + ushort * d = (ushort*)&da; + int i; + + /* Check if Flash is (sufficiently) erased */ + if (((*addr & d[0]) != d[0]) || ((*(addr+1) & d[1]) != d[1])) { + return (2); + } + /* Disable interrupts which might cause a timeout here */ + flag = disable_interrupts(); + + for(i = 0; i < 2; i++) + { + /* Write Command */ + *addr = 0x0010; + + /* Write Data */ + *addr = d[i]; + + /* re-enable interrupts if necessary */ + if (flag) + enable_interrupts(); + + /* data polling for D7 */ + start = get_timer (0); + flag = 0; + *addr = 0x0070; /*Read statusregister command */ + while (((csr = *addr) & INTEL_FLASH_STATUS_WSMS)!=INTEL_FLASH_STATUS_WSMS) { + if (get_timer(start) > CFG_FLASH_WRITE_TOUT) { + flag = 1; + break; + } + *addr = 0x0070; /*Read statusregister command */ + } + if (csr & INTEL_FLASH_STATUS_PSS) { + printf ("CSR indicates write error (%0x) at %08lx\n", + csr, (ulong)addr); + flag = 1; + } + + /* Clear Status Registers Command */ + *addr = 0x0050; + /* Reset to read array mode */ + *addr = 0xffff; + addr++; + } + + return (flag); +} + +int flash_real_protect(flash_info_t *info, long offset, int prot) +{ + int i, idx; + + for(idx = 0; idx < info->sector_count; idx++) + if(info->start[idx] == offset) + break; + + if(idx==info->sector_count) + return -1; + + if(prot == 0) { + /* Unprotect one sector, which means unprotect all flash + * and reprotect the other protected sectors. + */ + _flash_real_protect(info, 0, 0); /* Unprotects the whole flash*/ + info->protect[idx] = 0; + + for(i = 0; i < info->sector_count; i++) + if(info->protect[i]) + _flash_real_protect(info, i, 1); + } + else { + /* We can protect individual sectors */ + _flash_real_protect(info, idx, 1); + } + + for( i = 0; i < info->sector_count; i++) + info->protect[i] = flash_get_protect_status(info, i); + + return 0; +} + +int _flash_real_protect(flash_info_t *info, long idx, int prot) +{ + vu_short *addr; + int flag; + ushort cmd; + ushort tmp; + ulong now, start; + + if ((info->flash_id & FLASH_VENDMASK) != (INTEL_MANUFACT & FLASH_VENDMASK)) { + printf ("Can't change protection for unknown flash type %08lx - aborted\n", + info->flash_id); + return -1; + } + + if(prot == 0) { + /*Unlock the sector*/ + cmd = 0x00D0; + } + else { + /*Lock the sector*/ + cmd = 0x0001; + } + + addr = (vu_short *)(info->start[idx]); + + /* If chip is busy, wait for it */ + start = get_timer(0); + *addr = 0x0070; /*Read status register command*/ + tmp = ((ushort)(*addr))&0x00ff; /*Read the status*/ + while(!(tmp & INTEL_FLASH_STATUS_WSMS)) { + /*Write State Machine Busy*/ + /*Wait untill done or timeout.*/ + if ((now=get_timer(start)) > CFG_FLASH_WRITE_TOUT) { + *addr = 0x0050; /* Reset the status register */ + *addr = 0xffff; /* Reset the chip */ + printf ("TTimeout\n"); + return 1; + } + *addr = 0x0070; + tmp = ((ushort)(*addr))&0x00ff; /*Read the status*/ + start = get_timer(0); + } + + /* Disable interrupts which might cause a timeout here */ + flag = disable_interrupts(); + + /* Unlock block*/ + *addr = 0x0060; + + *addr = cmd; + + /* re-enable interrupts if necessary */ + if (flag) + enable_interrupts(); + + start = get_timer(0); + *addr = 0x0070; /*Read status register command*/ + tmp = ((ushort)(*addr)) & 0x00FF; /* Read the status */ + while (!(tmp & INTEL_FLASH_STATUS_WSMS)) { + /* Write State Machine Busy */ + if ((now=get_timer(start)) > CFG_FLASH_WRITE_TOUT) { + *addr = 0x0050; /* Reset the status register */ + *addr = 0xffff; + printf ("Timeout\n"); + return 1; + } + /* show that we're waiting */ + if ((now - start) > 1000) { /* every second */ + putc ('.'); + } + udelay(100000); /* 100 ms */ + *addr = 0x70; /*Read status register command*/ + tmp = (short)*addr & 0x00FF; /* Read status */ + start = get_timer(0); + } + if( tmp & INTEL_FLASH_STATUS_PS ) + flash_decode_status_bits(tmp); + + *addr =0x0050; /*Clear status register*/ + + /* reset to read mode */ + *addr = 0xffff; + + return 0; +} |