/* $Id: flash.c,v 1.3 2005/04/26 06:05:07 starvik Exp $ * * Stolen from the eLinux kernel and stripped down. * * HISTORY: * * $Log: flash.c,v $ * Revision 1.3 2005/04/26 06:05:07 starvik * Use the old flash.c since it works better in actual products. * * 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" extern void (*send_ack)(void); extern void (*send_hex)(udword h, byte nl); extern void (*send_string)(char *str); #define DEBUG #ifdef DEBUG #define FDEBUG(x) x #else #define FDEBUG(x) #endif // hack by M.Traxler to get the Intel P30 running #define INTEL_P30 /* 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 { #ifdef INTEL_P30 ADDR_UNLOCK_1 = 0x00, ADDR_UNLOCK_2 = 0x00, ADDR_MANUFACTURER = 0x0000, ADDR_DEVICE_ID = 0x0001, ADDR_CFI_QUERY = 0x0055, #else /* Addresses */ ADDR_UNLOCK_1 = 0x0555, ADDR_UNLOCK_2 = 0x02AA, ADDR_MANUFACTURER = 0x0000, ADDR_DEVICE_ID = 0x0001, ADDR_CFI_QUERY = 0x0055, #endif /* Commands */ #ifdef INTEL_P30 CMD_UNLOCK_DATA_1 = 0x0060, CMD_UNLOCK_DATA_2 = 0x00D0, CMD_MANUFACTURER_UNLOCK_DATA = 0x0090, CMD_PROGRAM_UNLOCK_DATA = 0x0040, CMD_RESET_DATA = 0x00FF, CMD_SECTOR_ERASE_UNLOCK_DATA_1 = 0x0020, CMD_SECTOR_ERASE_UNLOCK_DATA_2 = 0x00D0, CMD_CFI_QUERY_DATA = 0x0098, #else 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, #endif /* 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 #ifdef INTEL_P30 safe_printk("Attention: INTEL_P30 programming activated!\n"); int result2; wide_cmd(flash, CMD_MANUFACTURER_UNLOCK_DATA, ADDR_CFI_QUERY); result2 = (byte)wide_read(flash, ADDR_MANUFACTURER); // intel manufacturer id = 0x89 safe_printk("manufacturer: "); send_hex(result2, NL); wide_cmd(flash, CMD_MANUFACTURER_UNLOCK_DATA , ADDR_CFI_QUERY); result2 = wide_read(flash, ADDR_DEVICE_ID << 1); // device id should be 0x891c for a 256MBit Bottom type safe_printk("device id: "); send_hex(result2, NL); #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 #ifdef INTEL_P30 int result2; // clear status register wide_cmd(flash, 0x50, 0 ); // unlock block // by the way: the offset>>1 is the clue! // took me some hours to find out! wide_cmd(flash, 0x60, offset>>1 ); wide_cmd(flash, 0xd0, offset>>1 ); // read status wide_cmd(flash, 0x70, 0 ); result2=wide_read(flash, 0); safe_printk("after unlock: "); send_hex(result2, NL); while(!wide_read(flash, offset)) ; wide_cmd(flash, 0x50, 0 ); safe_printk("erase offset: "); send_hex(offset, NL); // erase block // important: the offset>>1 wide_cmd(flash, 0x20, offset>>1 ); wide_cmd(flash, 0xd0, offset>>1 ); // read status wide_cmd(flash, 0x70, 0 ); // wait till bit 7 of status register is set while(!wide_read(flash, 0)) ; result2=wide_read(flash, 0); safe_printk("sector erase status: "); send_hex(result2, NL); // status has to be 0x80, all additinal bits tell you that there // was an error while programming, see datasheet of P30 // 0xa2 means: block was locked, so could not erase block #else *(uword *)(flash->base+offset) = CMD_SECTOR_ERASE_UNLOCK_DATA_2; #endif #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 #ifdef INTEL_P30 else { int result; // clear status wide_cmd(flash, 0x50, 0); // //wide_cmd(flash, 0x60, offset); //wide_cmd(flash, 0xD0, offset); // Programming could be much faster, if one uses the // block brogramming: first write 32 words then programm // word programming is quite slow! // write word command wide_cmd(flash, 0x40, offset + bytes_written); chunk_size = wide_write_chunk(flash, offset + bytes_written, source + bytes_written); chunk_size = 2; // reas status wide_cmd(flash, 0x70, 0); result = wide_read(flash, 0); // wait till word is programmed while(result == 0) { result = wide_read(flash, 0); } } #else 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 */ ; } #endif bytes_written += chunk_size; } } int flash_write(const unsigned source, const unsigned int offset, const 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 } }; offset &= ~0x80000000UL; 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 = (const unsigned char*)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 = (const unsigned char*)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 = (const unsigned char*)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); // for testing the block erase need_erase = 2; 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..."); wide_cmd(&chips[i], CMD_RESET_DATA, ADDR_UNLOCK_1); 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; } unsigned virt2phys (unsigned addr) { return (addr); } unsigned preempt_cnt = 0;