summaryrefslogtreecommitdiff
path: root/package/etrax-tools/src/e100boot/cbl/src/flash.c
diff options
context:
space:
mode:
Diffstat (limited to 'package/etrax-tools/src/e100boot/cbl/src/flash.c')
-rw-r--r--package/etrax-tools/src/e100boot/cbl/src/flash.c1125
1 files changed, 1125 insertions, 0 deletions
diff --git a/package/etrax-tools/src/e100boot/cbl/src/flash.c b/package/etrax-tools/src/e100boot/cbl/src/flash.c
new file mode 100644
index 000000000..892e98ad7
--- /dev/null
+++ b/package/etrax-tools/src/e100boot/cbl/src/flash.c
@@ -0,0 +1,1125 @@
+/* $Id: flash.c,v 1.39 2004/04/20 07:57:57 jonashg Exp $
+ *
+ * Stolen from the eLinux kernel and stripped down.
+ *
+ * HISTORY:
+ *
+ * $Log: flash.c,v $
+ * Revision 1.39 2004/04/20 07:57:57 jonashg
+ * Clear flash_status fields to make it possible to flash several images
+ * sequentially.
+ *
+ * Revision 1.38 2003/12/16 09:04:07 magnusmn
+ * Removed FLASHFILL command
+ *
+ * Revision 1.37 2003/12/16 08:49:01 magnusmn
+ * Merging change_branch--fast_flash
+ *
+ * Revision 1.36.2.6 2003/12/15 17:21:27 magnusmn
+ * Reset counter when continuing with operations the next sector.
+ *
+ * Revision 1.36.2.5 2003/12/15 11:35:57 magnusmn
+ * Bail out if we try to erase the same sector more that 10 times
+ *
+ * Revision 1.36.2.4 2003/12/12 12:07:10 magnusmn
+ * FIX for ST M29W320DT
+ * Some chip need a reset to bring them back to read mode again.
+ *
+ * Revision 1.36.2.3 2003/11/10 16:38:04 orjanf
+ * Unified Erasing/Writing messages
+ *
+ * Revision 1.36.2.2 2003/11/10 15:52:34 magnusmn
+ * More info on a sector basis
+ *
+ * Revision 1.36.2.1 2003/11/07 16:23:20 magnusmn
+ * o Only erase a flash sector if we need to, that is if the source content isn't already is in place.
+ * o Don't erase a flash sector that already contain ones.
+ * o Don't write ones to a (d)word that already contain ones.
+ * o If there are two flashes, switch flash after an erase operation is started on one of them.
+ * o Flash fill doesn't work yet.
+ * o No timeout implemented, we will continue to erase/program until we succeed.
+ * o Interleave not tested.
+ *
+ * Revision 1.36 2003/10/16 17:08:51 jonashg
+ * Bugfix: reversed CFI-tables wasn't handled correctly since regions support was
+ * merged.
+ *
+ * Revision 1.35 2003/10/14 13:43:41 pkj
+ * Fixed compiler warnings.
+ *
+ * Revision 1.34 2003/10/14 10:48:13 magnusmn
+ * No need to write ones to a (d)word where there already are ones. This will save time during flash programming.
+ *
+ * Revision 1.33 2003/10/10 11:46:25 jonashg
+ * Merged change_branch--regions_support.
+ *
+ * Revision 1.32.2.3 2003/10/10 09:38:13 jonashg
+ * Corrected calculation of current region and sector before erase.
+ *
+ * Revision 1.32.2.2 2003/10/09 16:31:26 jonashg
+ * Regions support in JEDEC probe.
+ *
+ * Revision 1.32.2.1 2003/09/19 15:28:22 jonashg
+ * Support for unusual region layouts. It only works for CFI compliant chips (yet).
+ *
+ * Revision 1.32 2002/12/13 15:55:54 jonashg
+ * Fix for ST M29W160ET. It seems to need a reset before erase (even though the
+ * probe functions did reset it).
+ *
+ * Revision 1.31 2002/07/01 14:37:25 pkj
+ * Merged with the ASIC version of e100boot. Main difference is that
+ * information about the executed commands are sent back to e100boot
+ * instead of being sent to the debug port. This means there is no
+ * longer any need to use different boot loaders for different
+ * debug ports.
+ *
+ * Revision 1.30 2002/06/26 13:28:29 pkj
+ * flash_write() can now be used to erase an area (by specifying
+ * source as NULL), and to fill an area with the first udword of
+ * source by setting do_fill to TRUE).
+ *
+ * Revision 1.29 2002/06/26 13:19:37 pkj
+ * * flash_write() now returns a status code.
+ * * timeout is now decremented correctly in flash_write_part() to
+ * actually be able to trigger the timeout message.
+ * * Fixed all compiler warnings.
+ *
+ * Revision 1.28 2002/06/20 12:58:18 pkj
+ * Changed svinto_boot.h to e100boot.h
+ *
+ * Revision 1.27 2002/06/19 14:00:29 pkj
+ * * Broke out the probing of the flash chips from flash_write()
+ * into flash_probe_chips().
+ * * flash_probe_chips() is not limited to two chips or that the
+ * first chip exists.
+ *
+ * Revision 1.26 2002/02/21 14:37:52 jonashg
+ * Optimized away my sanity. It's back now I think.
+ *
+ * Revision 1.25 2002/02/21 14:28:24 jonashg
+ * Added support for Atmel AT49?V16?T (had to optimize a bit to make room).
+ *
+ * Revision 1.24 2002/01/31 14:36:14 jonashg
+ * * Added support for Atmel AT49[BL]V16[01] (the chip used in the ETRAX MCM).
+ * * Replaced concurrent sector erase with sequential (we have found three
+ * different chips that cannot erase multiple sectors at the same time,
+ * one of the is the chip in the MCM). I haven't noticed any performance
+ * loss on chips (CFI and non-CFI) that can erase all sectors at the same
+ * time either (maybe they don't really erase them at the same time in
+ * hardware).
+ * * Added check for manufacturer id as well as device id (should have been
+ * done a long time ago).
+ *
+ * Revision 1.23 2001/11/21 15:52:44 jonashg
+ * Almost readable.
+ *
+ * Revision 1.22 2001/11/21 15:24:38 jonashg
+ * Increased readability and decreased size some 40bytes.
+ *
+ * Revision 1.21 2001/11/20 13:40:12 starvik
+ * Corrected handling for CFI capable bottom boot flashes
+ * Shorted some strings to make more space available
+ *
+ * Revision 1.20 2001/08/08 17:51:28 pkj
+ * Made it possible to flash at a start offset other than zero when
+ * there are more than one physical flash chip available. Previously
+ * it always started flashing from the start of the first flash if
+ * there were more than one, even though the start offset was set to
+ * something else...
+ *
+ * Revision 1.19 2001/06/19 14:51:17 jonashg
+ * Added support for non-CFI flash Toshiba TC58FVT800.
+ *
+ * Revision 1.18 2001/04/05 06:32:39 starvik
+ * Works with flashes with multiple banks
+ *
+ * Revision 1.17 2001/03/06 15:21:16 jonashg
+ * More output to user.
+ *
+ * Revision 1.16 2001/03/06 14:11:16 jonashg
+ * * Switch to second device correctly when flashing images that extend past the
+ * first device.
+ * * Only enter autoselect mode once saves a few bytes (not needed before reading
+ * device id, since it was done before reading manufacturer id).
+ * * A few unnecessary resets removed to save another few bytes.
+ *
+ * Revision 1.15 2001/02/28 14:52:43 jonashg
+ * * Reverted to old sector erase sequence (that was correct).
+ * * A bit of executable size optimization (a few hundred bytes).
+ * * Cleanup.
+ *
+ * Revision 1.14 2001/02/27 14:18:59 jonashg
+ * * Write full erase command sequence to all sectors that should be erased.
+ * * Write 16bit erase command to non-interleaved chips.
+ *
+ * Revision 1.13 2001/02/23 11:03:41 jonashg
+ * Added support for 2 x 16Mb flashes (32-bits buswidth).
+ * The CFI probe does not detect two parallel flash devices, but the normal
+ * probe does (it should be easy to add that in the CFI-probe, but I didn't
+ * have any hardware to try it on and the size of the executable is getting
+ * pretty close to the size of the ETRAX cache).
+ *
+ * Revision 1.12 2001/02/12 13:59:00 jonashg
+ * Bugfix: pointer arithmetics made bootsector calculation go wrong.
+ *
+ * Revision 1.11 2000/11/10 08:02:23 starvik
+ * Added CFI support
+ *
+ * Revision 1.10 2000/10/26 13:47:32 johana
+ * Added support for Fujitsu flash 16MBit (2MByte) MBM29LV160BE and MBM29LV160TE.
+ * NOT VERIFIED YET!
+ *
+ * Revision 1.9 2000/06/28 13:02:50 bjornw
+ * * Added support for SST39LF800 and SST39LF160 flashes
+ * * Fixed some indentation issues
+ *
+ * Revision 1.8 2000/06/13 11:51:11 starvik
+ * Support for two flashes. Second flash is erased and programmed if program
+ * is larger than first flash.
+ *
+ * Revision 1.7 2000/04/13 16:06:15 macce
+ * See if flash is empty before erasing it. Might save some production time.
+ *
+ * Revision 1.6 2000/01/27 17:52:07 bjornw
+ * * Added Toshiba flashes
+ * * Added proper bootblock erase for the different flashes
+ * (this caused the verify errors when trying to do ./flashitall before)
+ *
+ * Revision 1.5 2000/01/20 11:41:28 finn
+ * Improved the verify error printouts in flash_write.
+ *
+ * Revision 1.4 1999/12/21 19:32:53 bjornw
+ * Dont choke on full chip erases even though we dont implement it efficiently.
+ *
+ * Revision 1.3 1999/11/12 01:30:04 bjornw
+ * Added wait for busy to be ready. Removed some warnings.
+ *
+ * Revision 1.2 1999/10/27 07:42:42 johana
+ * Added support for ST M29W800T flash used in 5600
+ *
+ * Revision 1.1 1999/10/27 01:37:12 bjornw
+ * Wrote routines to erase and flash data into a flash ROM.
+ *
+ */
+
+#include "e100boot.h"
+
+//#define DEBUG
+
+#ifdef DEBUG
+#define FDEBUG(x) x
+#else
+#define FDEBUG(x)
+#endif
+
+/* Try turning of some of these if you run into space problems. */
+#define CFI_PROBE
+#define JEDEC_PROBE
+#define INTERLEAVE
+
+#define TYPE_X16 (16 / 8)
+
+#define nop() __asm__("nop")
+
+#define safe_printk send_string
+
+static char *message_bottom_boot_8 = "8Mb BB";
+static char *message_top_boot_8 = "8Mb TB";
+static char *message_bottom_boot_16 = "16Mb BB";
+static char *message_top_boot_16 = "16Mb TB";
+static char *message_top_boot_32 = "32Mb TB";
+
+enum {
+ /* Addresses */
+ ADDR_UNLOCK_1 = 0x0555,
+ ADDR_UNLOCK_2 = 0x02AA,
+ ADDR_MANUFACTURER = 0x0000,
+ ADDR_DEVICE_ID = 0x0001,
+ ADDR_CFI_QUERY = 0x0055,
+
+ /* Commands */
+ CMD_UNLOCK_DATA_1 = 0x00AA,
+ CMD_UNLOCK_DATA_2 = 0x0055,
+ CMD_MANUFACTURER_UNLOCK_DATA = 0x0090,
+ CMD_PROGRAM_UNLOCK_DATA = 0x00A0,
+ CMD_RESET_DATA = 0x00F0,
+ CMD_SECTOR_ERASE_UNLOCK_DATA_1 = 0x0080,
+ CMD_SECTOR_ERASE_UNLOCK_DATA_2 = 0x0030,
+ CMD_CFI_QUERY_DATA = 0x0098,
+
+ /* Offsets */
+ OFFSET_CFI_ID = 0x10,
+ OFFSET_CFI_SIZE = 0x27,
+ OFFSET_CFI_BLOCK_COUNT = 0x2C,
+ OFFSET_CFI_BLOCK = 0x2D,
+
+ /* Manufacturers */
+ MANUFACTURER_AMD = 0x01,
+ MANUFACTURER_ATMEL = 0x1F,
+ MANUFACTURER_FUJITSU = 0x04,
+ MANUFACTURER_SST = 0xBF,
+ MANUFACTURER_ST = 0x20,
+ MANUFACTURER_TOSHIBA = 0x98,
+
+
+ /* To save precious space we store mfr and dev id together */
+
+ /* AMD devices */
+ AM29F800BB = 0x00012258,
+ AM29F800BT = 0x000122D6,
+ AM29LV800BB = 0x0001225B,
+ AM29LV800BT = 0x000122DA,
+ AM29LV160BT = 0x000122C4,
+
+ /* Atmel devices */
+ AT49xV16x = 0x001F00C0,
+ AT49xV16xT = 0x001F00C2,
+ AT49BV32xAT = 0x001F00C9,
+
+ /* Fujitsu devices */
+ MBM29LV160TE = 0x000422C4,
+ MBM29LV160BE = 0x00042249,
+
+ /* SST devices */
+ SST39LF800 = 0x00BF2781,
+ SST39LF160 = 0x00BF2782,
+
+ /* ST devices */
+ M29W800T = 0x002000D7, /* Used in 5600, similar
+ * to AM29LV800, but no
+ * unlock bypass
+ */
+ /* Toshiba devices */
+ TC58FVT160 = 0x009800C2,
+ TC58FVB160 = 0x00980043,
+ TC58FVT800 = 0x0098004F,
+
+ /* Toggle bit mask */
+ D6_MASK = 0x40
+};
+
+struct region {
+ unsigned long offset;
+ unsigned int sector_size;
+ unsigned int numsectors;
+};
+
+#define MAXREGIONS 8
+
+struct chip {
+ volatile unsigned char *base;
+#ifdef INTERLEAVE
+ byte interleave;
+ byte buswidth;
+#endif
+ unsigned int size;
+ unsigned short numregions;
+ struct region regions[MAXREGIONS];
+};
+
+/* Allocate flash structures and initialize base. */
+static struct chip chips[2] = {
+ { (unsigned char *)0x80000000,
+#ifdef INTERLEAVE
+ 0, 0,
+#endif
+ 0, 0, { } },
+ { (unsigned char *)0x84000000,
+#ifdef INTERLEAVE
+ 0, 0,
+#endif
+ 0, 0, { } }
+};
+
+
+
+static unsigned int
+wide_read(struct chip *flash, unsigned long offset)
+{
+#ifdef INTERLEAVE
+ switch (flash->buswidth) {
+ case 2:
+#endif
+ return *((uword *)(flash->base + offset));
+
+#ifdef INTERLEAVE
+ case 4:
+ return *((udword *)(flash->base + offset));
+ }
+
+ return 0;
+#endif
+}
+
+static int
+wide_write_chunk(struct chip *flash, unsigned long offset, const void *chunk)
+{
+#ifdef INTERLEAVE
+ switch (flash->buswidth) {
+ case 2:
+#endif
+ *((uword *)(flash->base + offset)) = *((uword *)chunk);
+ return 2;
+
+#ifdef INTERLEAVE
+ case 4:
+ *((udword *)(flash->base + offset)) = *((udword *)chunk);
+ return 4;
+ }
+
+ return 0;
+#endif
+}
+
+static void
+wide_cmd(struct chip *flash, udword cmd, unsigned long offset)
+{
+#ifdef INTERLEAVE
+ if (flash->interleave == 1) {
+#endif
+ offset <<= 1;
+#ifdef INTERLEAVE
+ } else if (flash->interleave == 2) {
+ cmd |= (cmd << 16);
+ offset <<= 2;
+ } else {
+ safe_printk("Unsupported interleave!\n");
+ return;
+ }
+#endif
+
+ wide_write_chunk(flash, offset, &cmd);
+}
+
+static void
+flash_unlock(struct chip *flash)
+{
+ wide_cmd(flash, CMD_UNLOCK_DATA_1, ADDR_UNLOCK_1);
+ wide_cmd(flash, CMD_UNLOCK_DATA_2, ADDR_UNLOCK_2);
+}
+
+static int
+flash_is_busy(struct chip *flash, unsigned long offset)
+{
+#ifdef INTERLEAVE
+ if (flash->interleave == 2) {
+ udword read1, read2;
+
+ read1 = wide_read(flash, offset);
+ read2 = wide_read(flash, offset);
+ return (((read1 >> 16) & D6_MASK) !=
+ ((read2 >> 16) & D6_MASK)) ||
+ (((read1 & 0xffff) & D6_MASK) !=
+ ((read2 & 0xffff) & D6_MASK));
+ }
+#endif
+
+ return ((wide_read(flash, offset) & D6_MASK) !=
+ (wide_read(flash, offset) & D6_MASK));
+}
+
+
+
+#ifdef CFI_PROBE
+static int
+try_cfi(struct chip *flash)
+{
+ int offset_shift = 1;
+
+#ifdef INTERLEAVE
+ if (flash->interleave == 2) {
+ offset_shift = 2;
+ }
+#endif
+
+ /* Enter CFI mode */
+ wide_cmd(flash, CMD_CFI_QUERY_DATA, ADDR_CFI_QUERY);
+
+ /* Check if flash responds correctly */
+ if ((byte)wide_read(flash, (OFFSET_CFI_ID+0) << offset_shift) == 'Q' &&
+ (byte)wide_read(flash, (OFFSET_CFI_ID+1) << offset_shift) == 'R' &&
+ (byte)wide_read(flash, (OFFSET_CFI_ID+2) << offset_shift) == 'Y') {
+ int block; /* Current block */
+ int block_count; /* Number of blocks */
+ unsigned int offset = 0; /* Offset into flash */
+ int reverse = 0; /* Reverse block table */
+ int primary; /* Offset to vendor specific table */
+
+ safe_printk("Found 1 x CFI at ");
+ send_hex((udword)flash->base, NL);
+
+ flash->size =
+ 1 << wide_read(flash, OFFSET_CFI_SIZE << offset_shift);
+
+ /* CFI stores flash organization in blocks. Each block contains
+ * a number of sectors with the same size
+ */
+ block_count = wide_read(flash, OFFSET_CFI_BLOCK_COUNT <<
+ offset_shift);
+
+ /* Check if table is reversed */
+ primary = wide_read(flash, (OFFSET_CFI_ID+5) << offset_shift);
+ /* For CFI version 1.0 we don't know. Assume that id & 0x80 */
+ /* indicates top boot */
+ if ((byte)wide_read(flash, (primary+4) << offset_shift) == 0x30)
+ {
+ /* read device id */
+ wide_cmd(flash, CMD_RESET_DATA, ADDR_UNLOCK_1);
+ flash_unlock(flash);
+ wide_cmd(flash, CMD_MANUFACTURER_UNLOCK_DATA,
+ ADDR_UNLOCK_1);
+ reverse = wide_read(flash, ADDR_DEVICE_ID * TYPE_X16
+#ifdef INTERLEAVE
+ * flash->interleave
+#endif
+ ) & 0x80;
+ wide_cmd(flash, CMD_CFI_QUERY_DATA, ADDR_CFI_QUERY);
+ } else {
+ reverse = ((byte)wide_read(flash,
+ (primary+15) << offset_shift) == 3);
+ }
+
+ flash->numregions = block_count;
+ if (block_count > MAXREGIONS) {
+ safe_printk("Too many regions on chip!\n");
+ return 0;
+ }
+
+ /* Blocks are stored backwards compared to flash organization */
+ for (block = reverse ? block_count - 1 : 0;
+ reverse ? block >= 0 : block < block_count;
+ reverse ? block-- : block++) {
+ int region;
+
+ /* Size of each sector in block. Size is stored as
+ * sector_size / 256.
+ */
+ int sector_size =
+ (wide_read(flash, (OFFSET_CFI_BLOCK+block * 4+2) <<
+ offset_shift)
+ |
+ (wide_read(flash, (OFFSET_CFI_BLOCK+block * 4+3) <<
+ offset_shift) << 8)
+ ) << 8;
+
+ /* Number of sectors */
+ int sector_count =
+ (wide_read(flash, (OFFSET_CFI_BLOCK+block * 4+0) <<
+ offset_shift)
+ |
+ (wide_read(flash, (OFFSET_CFI_BLOCK+block * 4+1) <<
+ offset_shift) << 8)
+ ) + 1;
+
+ region = reverse? block_count - 1 - block : block;
+ flash->regions[region].offset = offset;
+ flash->regions[region].sector_size = sector_size;
+ flash->regions[region].numsectors = sector_count;
+
+ /* Can't use multiplication (we have no lib). */
+ {
+ int temp;
+ for (temp = 0 ; temp < sector_count ; temp++) {
+ offset += sector_size;
+ }
+ }
+
+FDEBUG(
+ if (reverse) {
+ safe_printk("NOTE! reversed table:\n");
+ }
+ safe_printk("region: ");
+ send_hex((udword)region, NL);
+ safe_printk(" offset: ");
+ send_hex((udword)flash->regions[region].offset, NL);
+ safe_printk(" sector_size: ");
+ send_hex((udword)flash->regions[region].sector_size, NL);
+ safe_printk(" numsectors: ");
+ send_hex((udword)flash->regions[region].numsectors, NL);
+)
+
+ /* Some flashes (SST) store information about alternate
+ * block sizes. Ignore those by breaking when the sum
+ * of the sector sizes == flash size.
+ */
+ if (offset == flash->size) {
+ break;
+ }
+ }
+
+ /* reset */
+ wide_cmd(flash, CMD_RESET_DATA, ADDR_UNLOCK_1);
+
+ return 1;
+ }
+
+ /* reset */
+ wide_cmd(flash, CMD_RESET_DATA, ADDR_UNLOCK_1);
+
+ return 0;
+}
+#endif
+
+
+
+static int
+flash_probe(struct chip *flash)
+{
+ char *message;
+ udword dev_id;
+ udword mfr_id;
+ udword id;
+
+ if (flash->size
+#ifdef CFI_PROBE
+ || try_cfi(flash)
+#endif
+ ) {
+ return 1;
+ }
+
+#ifdef JEDEC_PROBE
+ /* Read manufacturer ID. */
+ flash_unlock(flash);
+ wide_cmd(flash, CMD_MANUFACTURER_UNLOCK_DATA, ADDR_UNLOCK_1);
+ mfr_id = wide_read(flash, ADDR_MANUFACTURER * TYPE_X16
+#ifdef INTERLEAVE
+ * flash->interleave
+#endif
+ );
+ /* Read device ID. */
+ dev_id = wide_read(flash, ADDR_DEVICE_ID * TYPE_X16
+#ifdef INTERLEAVE
+ * flash->interleave
+#endif
+ );
+FDEBUG(
+ safe_printk("mfr_id: ");
+ send_hex(mfr_id, NL);
+ safe_printk("dev_id: ");
+ send_hex(dev_id, NL);
+)
+
+#ifdef INTERLEAVE
+ if ((flash->interleave == 2) &&
+ ((mfr_id >> 16) == (mfr_id & 0xffff)) &&
+ ((dev_id >> 16) == (dev_id & 0xffff))) {
+ mfr_id &= 0xffff;
+ dev_id &= 0xffff;
+ }
+#endif
+
+ id = (mfr_id << 16) | dev_id;
+
+ /* reset */
+ wide_cmd(flash, CMD_RESET_DATA, ADDR_UNLOCK_1);
+
+ /* Check device type and fill in correct sizes. */
+ switch (id) {
+ case AM29LV160BT:
+ case TC58FVT160:
+ // case MBM29LV160TE: /* This is same id as AM29LV160BT */
+ message = message_top_boot_16;
+
+ flash->size = 0x00200000;
+
+ flash->regions[0].offset = 0x00000000;
+ flash->regions[0].sector_size = 0x10000;
+ flash->regions[0].numsectors = 31;
+
+ flash->regions[1].offset = 0x001F0000;
+ flash->regions[1].sector_size = 0x08000;
+ flash->regions[1].numsectors = 1;
+
+ flash->regions[2].offset = 0x001F8000;
+ flash->regions[2].sector_size = 0x02000;
+ flash->regions[2].numsectors = 2;
+
+ flash->regions[3].offset = 0x001FC000;
+ flash->regions[3].sector_size = 0x04000;
+ flash->regions[3].numsectors = 1;
+ break;
+
+ // case AM29LV160BB:
+ case TC58FVB160:
+ case MBM29LV160BE:
+ message = message_bottom_boot_16;
+
+ flash->size = 0x00200000;
+
+ flash->regions[0].offset = 0x00000000;
+ flash->regions[0].sector_size = 0x04000;
+ flash->regions[0].numsectors = 1;
+
+ flash->regions[1].offset = 0x00004000;
+ flash->regions[1].sector_size = 0x02000;
+ flash->regions[1].numsectors = 2;
+
+ flash->regions[2].offset = 0x00008000;
+ flash->regions[2].sector_size = 0x08000;
+ flash->regions[2].numsectors = 1;
+
+ flash->regions[3].offset = 0x00010000;
+ flash->regions[3].sector_size = 0x10000;
+ flash->regions[3].numsectors = 31;
+ break;
+
+ case AM29LV800BB:
+ case AM29F800BB:
+ message = message_bottom_boot_8;
+
+ flash->size = 0x00100000;
+
+ flash->regions[0].offset = 0x00000000;
+ flash->regions[0].sector_size = 0x04000;
+ flash->regions[0].numsectors = 1;
+
+ flash->regions[1].offset = 0x00004000;
+ flash->regions[1].sector_size = 0x02000;
+ flash->regions[1].numsectors = 2;
+
+ flash->regions[2].offset = 0x00008000;
+ flash->regions[2].sector_size = 0x08000;
+ flash->regions[2].numsectors = 1;
+
+ flash->regions[3].offset = 0x00010000;
+ flash->regions[3].sector_size = 0x10000;
+ flash->regions[3].numsectors = 15;
+ break;
+
+ case M29W800T:
+ case AM29LV800BT:
+ case AM29F800BT:
+ case TC58FVT800:
+ message = message_top_boot_8;
+
+ flash->size = 0x00100000;
+
+ flash->regions[0].offset = 0x00000000;
+ flash->regions[0].sector_size = 0x10000;
+ flash->regions[0].numsectors = 15;
+
+ flash->regions[1].offset = 0x000F0000;
+ flash->regions[1].sector_size = 0x08000;
+ flash->regions[1].numsectors = 1;
+
+ flash->regions[2].offset = 0x000F8000;
+ flash->regions[2].sector_size = 0x02000;
+ flash->regions[2].numsectors = 2;
+
+ flash->regions[3].offset = 0x000FC000;
+ flash->regions[3].sector_size = 0x04000;
+ flash->regions[3].numsectors = 1;
+
+ break;
+
+ case AT49xV16x:
+ message = message_bottom_boot_16;
+
+ flash->size = 0x00200000;
+
+ flash->regions[0].offset = 0x00000000;
+ flash->regions[0].sector_size = 0x02000;
+ flash->regions[0].numsectors = 8;
+
+ flash->regions[1].offset = 0x00010000;
+ flash->regions[1].sector_size = 0x10000;
+ flash->regions[1].numsectors = 31;
+
+ break;
+
+ case AT49xV16xT:
+ message = message_top_boot_16;
+
+ flash->size = 0x00200000;
+
+ flash->regions[0].offset = 0x00000000;
+ flash->regions[0].sector_size = 0x10000;
+ flash->regions[0].numsectors = 31;
+
+ flash->regions[1].offset = 0x001F0000;
+ flash->regions[1].sector_size = 0x02000;
+ flash->regions[1].numsectors = 8;
+
+ break;
+
+ case AT49BV32xAT:
+ message = message_top_boot_32;
+
+ flash->size = 0x00400000;
+
+ flash->regions[0].offset = 0x00000000;
+ flash->regions[0].sector_size = 0x10000;
+ flash->regions[0].numsectors = 63;
+
+ flash->regions[1].offset = 0x001F0000;
+ flash->regions[1].sector_size = 0x02000;
+ flash->regions[1].numsectors = 8;
+
+ break;
+
+ default:
+#endif
+#ifdef INTERLEAVE
+ if (flash->interleave == 1) {
+#endif
+ safe_printk("No single x16 at ");
+#ifdef INTERLEAVE
+ } else {
+ safe_printk("No interleaved x16 at ");
+ }
+#endif
+ send_hex((udword)flash->base, NL);
+
+ return 0;
+#ifdef JEDEC_PROBE
+ }
+
+ safe_printk("Found ");
+#ifdef INTERLEAVE
+ if (flash->interleave == 1) {
+#endif
+ safe_printk("1");
+#ifdef INTERLEAVE
+ }
+ if (flash->interleave == 2) {
+ int count = 0;
+
+ flash->size <<= 1;
+ while (count < MAXREGIONS) {
+ flash->regions[count].offset <<= 1;
+ flash->regions[count].sector_size <<= 1;
+ count++;
+ }
+ safe_printk("2");
+ }
+#endif
+ safe_printk(" x ");
+ safe_printk(message);
+ safe_printk(" at ");
+ send_hex((udword)flash->base, NL);
+
+ return 1;
+#endif
+}
+
+/* Start erase of a sector but do no wait for completion */
+static void
+start_sector_erase(struct chip *flash, unsigned long offset)
+{
+ flash_unlock(flash);
+ wide_cmd(flash, CMD_SECTOR_ERASE_UNLOCK_DATA_1, ADDR_UNLOCK_1);
+ flash_unlock(flash);
+
+#ifdef INTERLEAVE
+ if (flash->interleave == 2) {
+ *(udword *)(flash->base+offset) = (CMD_SECTOR_ERASE_UNLOCK_DATA_2 << 16) |
+ CMD_SECTOR_ERASE_UNLOCK_DATA_2;
+ } else {
+#endif
+ *(uword *)(flash->base+offset) = CMD_SECTOR_ERASE_UNLOCK_DATA_2;
+#ifdef INTERLEAVE
+ }
+#endif
+}
+
+/* Return the size of the sector at the given offset */
+static int
+find_sector_size(struct chip *flash, unsigned long offset)
+{
+ unsigned int i, j;
+ int region_size;
+ /* Sanity check */
+ if (offset >= flash->size)
+ return 0;
+
+ for(i=0; i < MAXREGIONS; i++)
+ if (offset >= flash->regions[i].offset) {
+ region_size=0;
+ for (j=0; j < flash->regions[i].numsectors; j++)
+ region_size += flash->regions[i].sector_size;
+ if (offset < flash->regions[i].offset + region_size)
+ return flash->regions[i].sector_size;
+ }
+
+ /* Should not happen */
+ return 0;
+}
+
+/* Check and see if we need to erase the sector */
+/* The return values mean */
+/* 0: The source and destination are the same. */
+/* 1: The source and destination are not the same, but flash sector already contains only ones. */
+/* 2: The source and destination are not the same and the flash sector is tainted by some zeroes. */
+static char
+need_to_erase(struct chip *flash, unsigned long offset, const unsigned char *source, int size)
+{
+ int i;
+ unsigned long j;
+
+ for (i = 0; i < size; i+=2)
+ if (*(uword*)(flash->base + i + offset) != *(uword*)(source + i)) {
+ /* Check if the sector only contain zeroes */
+ for (j = offset; j < (size + offset); j+=2) {
+ if (*(uword*)(flash->base + j) != 0xffff)
+ return 2;
+ }
+ return 1;
+ }
+
+ /* The source is equal to the destination */
+ return 0;
+}
+
+static unsigned int
+flash_probe_chips(void)
+{
+ unsigned int tot_size = 0;
+ unsigned int i = 0;
+
+ for (; i < sizeof chips/sizeof *chips; i++) {
+#ifdef INTERLEAVE
+ byte interleave;
+
+ for (interleave = 1; interleave < 4; interleave *= 2) {
+ chips[i].interleave = interleave;
+ if (interleave == 1) {
+ chips[i].buswidth = sizeof(uword);
+ } else {
+ chips[i].buswidth = sizeof(udword);
+ }
+
+ if (flash_probe(&chips[i])) {
+ break;
+ }
+ }
+#else
+ flash_probe(&chips[i]);
+#endif
+
+ tot_size += chips[i].size;
+ }
+
+ return tot_size;
+}
+
+/* Program a sector (given by size) at the given offset. Do not write only ones. */
+static void
+program_sector(struct chip *flash, unsigned long offset, const unsigned char *source, int size)
+{
+ int chunk_size = 0;
+ int bytes_written = 0;
+
+
+ while (bytes_written < size) {
+ if (
+#ifdef INTERLEAVE
+ (flash->buswidth == 2) &&
+#endif
+ *(uword*)(source + bytes_written) == 0xffff) {
+ chunk_size=2;
+ }
+#ifdef INTERLEAVE
+ else if ((flash->buswidth == 4) && *(udword*)(source + bytes_written) == 0xffffffff) {
+ chunk_size=4;
+ }
+#endif
+ else {
+ flash_unlock(flash);
+ wide_cmd(flash, CMD_PROGRAM_UNLOCK_DATA, ADDR_UNLOCK_1);
+ chunk_size = wide_write_chunk(flash, offset + bytes_written, source + bytes_written);
+ while(flash_is_busy(flash, offset + bytes_written))
+ /* Nothing */
+ ;
+ }
+
+ bytes_written += chunk_size;
+ }
+}
+
+int
+flash_write(const unsigned char *source, unsigned int offset, unsigned int size)
+{
+ struct flash_status {
+ unsigned char busy; /* Indicates if the flash is busy */
+ const unsigned char *src; /* From where to get the source info */
+ unsigned long offset; /* Start operations in flash at this offset */
+ unsigned int size; /* Size to erase/program (if needed) */
+ unsigned int bytes_done; /* Bytes written (if needed) */
+ unsigned int erase_attempts; /* Keep track how many times we try to erase the same sector */
+ };
+
+ unsigned int tot_size = flash_probe_chips();
+ unsigned int i, j;
+ unsigned int current_sector_size;
+ unsigned long current_offset;
+ const unsigned char *current_src;
+ char need_erase;
+ struct flash_status *current_flash = NULL;
+
+ static struct flash_status flash_status[2] = {
+ { 0, NULL, 0, 0, 0, 0 },
+ { 0, NULL, 0, 0, 0, 0 }
+ };
+
+ if (!tot_size) {
+ /* No chips found, bail out. */
+ return ERR_FLASH_NONE;
+ }
+
+ if (offset + size > tot_size) {
+ safe_printk("Fatal: flash is too small.\n");
+ return ERR_FLASH_TOO_SMALL;
+ }
+
+ /* Initiate the flash_status structs so that we can keep track of what needs to be done
+ on the different flash chips */
+
+ /* Operations only on flash chip 1 */
+ if (offset >= (&chips[0])->size) {
+ flash_status[0].size = 0;
+ flash_status[1].src = source;
+ flash_status[1].offset = offset - (&chips[0])->size;
+ flash_status[1].size = size;
+ }
+ /* Operations on both flash chips */
+ else if ((offset < (&chips[0])->size) && ((offset+size) > (&chips[0])->size)) {
+ flash_status[0].src = source;
+ flash_status[0].offset = offset;
+ flash_status[0].size = (&chips[0])->size - offset;
+ flash_status[1].src = source + flash_status[0].size;
+ flash_status[1].offset = 0;
+ flash_status[1].size = size - flash_status[0].size;
+ }
+ /* Operations only on flash chip 0 */
+ else {
+ flash_status[0].src = source;
+ flash_status[0].offset = offset;
+ flash_status[0].size = size;
+ flash_status[1].size = 0;
+ }
+ flash_status[0].busy = 0;
+ flash_status[0].bytes_done = 0;
+ flash_status[0].erase_attempts = 0;
+ flash_status[1].busy = 0;
+ flash_status[1].bytes_done = 0;
+ flash_status[1].erase_attempts = 0;
+#if 0
+ for (i = 0; i < 2; i++) {
+ safe_printk("\nFlash ");
+ send_hex(i, NL);
+ safe_printk("src:\t");
+ send_hex((int)flash_status[i].src, NL);
+ safe_printk("offset:\t");
+ send_hex(flash_status[i].offset, NL);
+ safe_printk("size:\t");
+ send_hex(flash_status[i].size, NL);
+ safe_printk("\n");
+ }
+#endif
+
+ /* Erase and write */
+
+ i = 0; /* Start operations on flash 0 */
+
+#define CHANGE_FLASH
+
+ while (((&flash_status[0])->bytes_done + (&flash_status[1])->bytes_done) < size) {
+
+ struct flash_status *previous_flash = &flash_status[i ? 0 : 1];
+ current_flash = &flash_status[i];
+
+#ifdef CHANGE_FLASH
+ /* Change flash only if:
+ - There is a flash to change to and operations should be made on that flash *AND*
+ - There is more to write to the previous flash *AND*
+ - Operations should be made on the current flash *OR*
+ - The current flash is busy *OR*
+ - All has been written to the current flash */
+
+ if (previous_flash->size && (previous_flash->bytes_done < previous_flash->size) &&
+ (!current_flash->size || current_flash->busy ||
+ current_flash->bytes_done == current_flash->size))
+ i = i ? 0 : 1; /* Change flash chip */
+#else
+ /* Finish one flash chip before continuing on the next one */
+
+ if ((&flash_status[i])->bytes_done == (&flash_status[i])->size)
+ i = i ? 0 : 1; /* Change flash chip */
+#endif
+ /* Bail out if we have tried to erase the same sector more that 10 times. */
+ if(current_flash->erase_attempts > 10) {
+ safe_printk("Sector erase error\n");
+ return ERR_FLASH_ERASE;
+ }
+
+ /* Get the current status from the chip we are about to access */
+ current_flash = &flash_status[i];
+ current_offset = current_flash->offset + current_flash->bytes_done;
+ current_src = current_flash->src + current_flash->bytes_done;
+ current_sector_size = find_sector_size(&chips[i], current_offset);
+
+ /* Make sure that the chip we are about to access has finished erasing */
+ if (current_flash->busy) {
+ while (flash_is_busy(&chips[i], current_offset))
+ /* nothing */
+ ;
+ current_flash->busy = 0;
+ }
+
+ /* Some flash chip need a reset to bring them back to read mode again. */
+ wide_cmd(&chips[i], CMD_RESET_DATA, ADDR_UNLOCK_1);
+
+ /* Find out if we need to erase the sector or not */
+ need_erase = need_to_erase(&chips[i], current_offset, current_src, current_sector_size);
+
+ if (need_erase == 0) {
+ current_flash->bytes_done += current_sector_size;
+ current_flash->erase_attempts = 0;
+ send_hex((int)(&chips[i])->base + current_offset, 0);
+ safe_printk(": No need to write\n");
+ continue;
+ } else if (need_erase == 1) {
+ /* Erased, not worth printing. */
+ }
+ else if (need_erase == 2) {
+ send_hex((int)(&chips[i])->base + current_offset, 0);
+ safe_printk(": Erasing ");
+ send_hex(current_sector_size, 0);
+ safe_printk(" bytes\n");
+ start_sector_erase(&chips[i], current_offset);
+ current_flash->busy=1;
+ current_flash->erase_attempts++;
+ continue;
+ }
+
+ /* The sector is ready to be programmed */
+ send_hex((int)(&chips[i])->base + current_offset, 0);
+ safe_printk(": Writing ");
+ send_hex(current_sector_size, 0);
+ safe_printk(" bytes\n");
+ program_sector(&chips[i], current_offset, current_src, current_sector_size);
+ current_flash->bytes_done += current_sector_size;
+ current_flash->erase_attempts = 0;
+ }
+
+ /* Verify that the flash chip(s) have the correct content */
+ for (i = 0; i < 2; i++) {
+ current_flash = &flash_status[i];
+ if (!current_flash->size)
+ continue;
+ send_hex((int)(&chips[i])->base, 0);
+ safe_printk(": Verifying...");
+ for (j = 0; j < current_flash->size; j+=2) {
+ if (*(uword*)(current_flash->offset + j + (&chips[i])->base) !=
+ *(uword*)(current_flash->src + j)) {
+ safe_printk("Error at ");
+ send_hex(j, NL);
+ return ERR_FLASH_VERIFY;
+ }
+ }
+ safe_printk("OK\n");
+ }
+
+ return ERR_FLASH_OK;
+}