/* * (C) Copyright 2000, 2001 * Wolfgang Denk, DENX Software Engineering, wd@denx.de. * * (C) Copyright 2001, Stuart Hughes, Lineo Inc, stuarth@lineo.com * Add support the Sharp chips on the mpc8260ads. * I started with board/ip860/flash.c and made changes I found in * the MTD project by David Schleef. * * (C) Copyright 2003 Arabella Software Ltd. * Yuli Barcohen * Re-written to support multi-bank flash SIMMs. * Added support for real protection and JFFS2. * * 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 /* Intel-compatible flash ID */ #define INTEL_COMPAT 0x89898989 #define INTEL_ALT 0xB0B0B0B0 /* Intel-compatible flash commands */ #define INTEL_PROGRAM 0x10101010 #define INTEL_ERASE 0x20202020 #define INTEL_CLEAR 0x50505050 #define INTEL_LOCKBIT 0x60606060 #define INTEL_PROTECT 0x01010101 #define INTEL_STATUS 0x70707070 #define INTEL_READID 0x90909090 #define INTEL_CONFIRM 0xD0D0D0D0 #define INTEL_RESET 0xFFFFFFFF /* Intel-compatible flash status bits */ #define INTEL_FINISHED 0x80808080 #define INTEL_OK 0x80808080 flash_info_t flash_info[CFG_MAX_FLASH_BANKS]; /* info for FLASH chips */ /*----------------------------------------------------------------------- * This board supports 32-bit wide flash SIMMs (4x8-bit configuration.) * Up to 32MB of flash supported (up to 4 banks.) * BCSR is used for flash presence detect (page 4-65 of the User's Manual) * * The following code can not run from flash! */ unsigned long flash_init (void) { ulong size = 0, sect_start, sect_size = 0, bank_size; ushort sect_count = 0; int i, j, nbanks; vu_long *addr = (vu_long *)CFG_FLASH_BASE; vu_long *bcsr = (vu_long *)CFG_BCSR; switch (bcsr[2] & 0xF) { case 0: nbanks = 4; break; case 1: nbanks = 2; break; case 2: nbanks = 1; break; default: /* Unsupported configurations */ nbanks = CFG_MAX_FLASH_BANKS; } if (nbanks > CFG_MAX_FLASH_BANKS) nbanks = CFG_MAX_FLASH_BANKS; for (i = 0; i < nbanks; i++) { *addr = INTEL_READID; /* Read Intelligent Identifier */ if ((addr[0] == INTEL_COMPAT) || (addr[0] == INTEL_ALT)) { switch (addr[1]) { case SHARP_ID_28F016SCL: case SHARP_ID_28F016SCZ: flash_info[i].flash_id = FLASH_MAN_SHARP | FLASH_LH28F016SCT; sect_count = 32; sect_size = 0x40000; break; default: flash_info[i].flash_id = FLASH_UNKNOWN; sect_count = CFG_MAX_FLASH_SECT; sect_size = CFG_FLASH_SIZE / CFG_MAX_FLASH_BANKS / CFG_MAX_FLASH_SECT; } } else flash_info[i].flash_id = FLASH_UNKNOWN; if (flash_info[i].flash_id == FLASH_UNKNOWN) { printf("### Unknown flash ID %08lX %08lX at address %08lX ###\n", addr[0], addr[1], (ulong)addr); size = 0; *addr = INTEL_RESET; /* Reset bank to Read Array mode */ break; } flash_info[i].sector_count = sect_count; flash_info[i].size = bank_size = sect_size * sect_count; size += bank_size; sect_start = (ulong)addr; for (j = 0; j < sect_count; j++) { addr = (vu_long *)sect_start; flash_info[i].start[j] = sect_start; flash_info[i].protect[j] = (addr[2] == 0x01010101); sect_start += sect_size; } *addr = INTEL_RESET; /* Reset bank to Read Array mode */ addr = (vu_long *)sect_start; } if (size == 0) { /* Unknown flash, fill with hard-coded values */ sect_start = CFG_FLASH_BASE; for (i = 0; i < CFG_MAX_FLASH_BANKS; i++) { flash_info[i].flash_id = FLASH_UNKNOWN; flash_info[i].size = CFG_FLASH_SIZE / CFG_MAX_FLASH_BANKS; flash_info[i].sector_count = sect_count; for (j = 0; j < sect_count; j++) { flash_info[i].start[j] = sect_start; flash_info[i].protect[j] = 0; sect_start += sect_size; } } size = CFG_FLASH_SIZE; } else for (i = nbanks; i < CFG_MAX_FLASH_BANKS; i++) { flash_info[i].flash_id = FLASH_UNKNOWN; flash_info[i].size = 0; flash_info[i].sector_count = 0; } #if CFG_MONITOR_BASE >= CFG_FLASH_BASE /* monitor protection ON by default */ flash_protect(FLAG_PROTECT_SET, CFG_MONITOR_BASE, CFG_MONITOR_BASE+monitor_flash_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 (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_INTEL: printf ("Intel "); break; case FLASH_MAN_SHARP: printf ("Sharp "); break; default: printf ("Unknown Vendor "); break; } switch (info->flash_id & FLASH_TYPEMASK) { case FLASH_28F016SV: printf ("28F016SV (16 Mbit, 32 x 64k)\n"); break; case FLASH_28F160S3: printf ("28F160S3 (16 Mbit, 32 x 512K)\n"); break; case FLASH_28F320S3: printf ("28F320S3 (32 Mbit, 64 x 512K)\n"); break; case FLASH_LH28F016SCT: printf ("28F016SC (16 Mbit, 32 x 64K)\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; isector_count; ++i) { if ((i % 5) == 0) printf ("\n "); printf (" %08lX%s", info->start[i], info->protect[i] ? " (RO)" : " " ); } printf ("\n"); } /*----------------------------------------------------------------------- */ int flash_erase (flash_info_t *info, int s_first, int s_last) { int flag, prot, 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_VENDMASK) != FLASH_MAN_INTEL) && ((info->flash_id & FLASH_VENDMASK) != FLASH_MAN_SHARP) ) { 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 erase on unprotected sectors */ for (sect = s_first; sect<=s_last; sect++) { if (info->protect[sect] == 0) { /* not protected */ vu_long *addr = (vu_long *)(info->start[sect]); last = start = get_timer (0); /* Disable interrupts which might cause a timeout here */ flag = disable_interrupts(); /* Clear Status Register */ *addr = INTEL_CLEAR; /* Single Block Erase Command */ *addr = INTEL_ERASE; /* Confirm */ *addr = INTEL_CONFIRM; if((info->flash_id & FLASH_TYPEMASK) != FLASH_LH28F016SCT) { /* Resume Command, as per errata update */ *addr = INTEL_CONFIRM; } /* re-enable interrupts if necessary */ if (flag) enable_interrupts(); while ((*addr & INTEL_FINISHED) != INTEL_FINISHED) { if ((now=get_timer(start)) > CFG_FLASH_ERASE_TOUT) { printf ("Timeout\n"); *addr = INTEL_RESET; /* reset bank */ return 1; } /* show that we're waiting */ if ((now - last) > 1000) { /* every second */ putc ('.'); last = now; } } if (*addr != INTEL_OK) { printf("Block erase failed at %08X, CSR=%08X\n", (uint)addr, (uint)*addr); *addr = INTEL_RESET; /* reset bank */ return 1; } /* reset to read mode */ *addr = INTEL_RESET; } } printf (" done\n"); return 0; } /*----------------------------------------------------------------------- * 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) { ulong start; int rc = 0; int flag; vu_long *addr = (vu_long *)dest; /* Check if Flash is (sufficiently) erased */ if ((*addr & data) != data) { return (2); } *addr = INTEL_CLEAR; /* Clear status register */ /* Disable interrupts which might cause a timeout here */ flag = disable_interrupts(); /* Write Command */ *addr = INTEL_PROGRAM; /* Write Data */ *addr = data; /* re-enable interrupts if necessary */ if (flag) enable_interrupts(); /* data polling for D7 */ start = get_timer (0); while ((*addr & INTEL_FINISHED) != INTEL_FINISHED) { if (get_timer(start) > CFG_FLASH_WRITE_TOUT) { printf("Write timed out\n"); rc = 1; break; } } if (*addr != INTEL_OK) { printf ("Write failed at %08X, CSR=%08X\n", (uint)addr, (uint)*addr); rc = 1; } *addr = INTEL_RESET; /* Reset to read array mode */ 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 */ *(vu_long *)wp = INTEL_RESET; /* Reset to read array mode */ /* * handle unaligned start bytes */ if ((l = addr - wp) != 0) { data = 0; for (i=0, cp=wp; i0; ++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); } rc = write_word(info, wp, data); return rc; } /*----------------------------------------------------------------------- * Set/Clear sector's lock bit, returns: * 0 - OK * 1 - Error (timeout, voltage problems, etc.) */ int flash_real_protect(flash_info_t *info, long sector, int prot) { ulong start; int i; int rc = 0; vu_long *addr = (vu_long *)(info->start[sector]); int flag = disable_interrupts(); *addr = INTEL_CLEAR; /* Clear status register */ if (prot) { /* Set sector lock bit */ *addr = INTEL_LOCKBIT; /* Sector lock bit */ *addr = INTEL_PROTECT; /* set */ } else { /* Clear sector lock bit */ *addr = INTEL_LOCKBIT; /* All sectors lock bits */ *addr = INTEL_CONFIRM; /* clear */ } start = get_timer(0); while ((*addr & INTEL_FINISHED) != INTEL_FINISHED) { if (get_timer(start) > CFG_FLASH_UNLOCK_TOUT) { printf("Flash lock bit operation timed out\n"); rc = 1; break; } } if (*addr != INTEL_OK) { printf("Flash lock bit operation failed at %08X, CSR=%08X\n", (uint)addr, (uint)*addr); rc = 1; } if (!rc) info->protect[sector] = prot; /* * Clear lock bit command clears all sectors lock bits, so * we have to restore lock bits of protected sectors. */ if (!prot) for (i = 0; i < info->sector_count; i++) if (info->protect[i]) { addr = (vu_long *)(info->start[i]); *addr = INTEL_LOCKBIT; /* Sector lock bit */ *addr = INTEL_PROTECT; /* set */ udelay(CFG_FLASH_LOCK_TOUT * 1000); } if (flag) enable_interrupts(); *addr = INTEL_RESET; /* Reset to read array mode */ return rc; } /*----------------------------------------------------------------------- * Support for flash file system (JFFS2) * * We use custom partition info function because we have to fit the * file system image between first sector (containing hard reset * configuration word) and the sector containing U-Boot image. Standard * partition info function does not allow for last sector specification * and assumes that the file system occupies flash bank up to and * including bank's last sector. */ #if (CONFIG_COMMANDS & CFG_CMD_JFFS2) && defined(CFG_JFFS_CUSTOM_PART) #ifndef CFG_JFFS2_FIRST_SECTOR #define CFG_JFFS2_FIRST_SECTOR 0 #endif #ifndef CFG_JFFS2_FIRST_BANK #define CFG_JFFS2_FIRST_BANK 0 #endif #ifndef CFG_JFFS2_NUM_BANKS #define CFG_JFFS2_NUM_BANKS 1 #endif #define CFG_JFFS2_LAST_BANK (CFG_JFFS2_FIRST_BANK + CFG_JFFS2_NUM_BANKS - 1) #include static struct part_info partition; struct part_info *jffs2_part_info(int part_num) { int i; if (part_num == 0) { if (partition.usr_priv == 0) { partition.offset = (unsigned char *) flash_info[CFG_JFFS2_FIRST_BANK].start[CFG_JFFS2_FIRST_SECTOR]; for (i = CFG_JFFS2_FIRST_BANK; i <= CFG_JFFS2_LAST_BANK; i++) partition.size += flash_info[i].size; partition.size -= flash_info[CFG_JFFS2_FIRST_BANK].start[CFG_JFFS2_FIRST_SECTOR] - flash_info[CFG_JFFS2_FIRST_BANK].start[0]; #ifdef CFG_JFFS2_LAST_SECTOR i = flash_info[CFG_JFFS2_LAST_BANK].sector_count - 1; partition.size -= flash_info[CFG_JFFS2_LAST_BANK].start[i] - flash_info[CFG_JFFS2_LAST_BANK].start[CFG_JFFS2_LAST_SECTOR]; #endif partition.usr_priv = (void *)1; } return &partition; } return 0; } #endif /* JFFS2 */