📄 flash.c
字号:
/* $Id: flash.c,v 1.23 2001/11/21 15:52:44 jonashg Exp $ * June 15, 2002 - made flash length with MSB set - compare only) * May, 1, 2002 - support for Intel StrataFlash (and Micron) * modified by Andrey Filippov for Intel support 12/17/2001 (define MYINTEL) * * Stolen from the eLinux kernel and stripped down. * * HISTORY: * * $Log: flash.c,v $ * 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. * * * TODO: * implement Unlock Bypass programming, to double the write speed * NOTE: ST M29W800T does not support Unlock bypass! * fix various loops for partitions than span more than one chip */#include "svinto_boot.h"#define MYINTEL// #define DEBUG#ifdef DEBUG#define FDEBUG(x) x#else#define FDEBUG(x)#endif#define MEM_DRAM_START 0x40000000 /* This is not a flash address */#define TYPE_X16 (16 / 8)#define nop() __asm__("nop")#define safe_printk send_serial_string/* Intel status bits */#define INTEL_SRB_WSMS 0x80#define INTEL_SRB_ESS 0x40#define INTEL_SRB_ES 0x20#define INTEL_SRB_PS 0x10#define INTEL_SRB_VPPS 0x08#define INTEL_SRB_PSS 0x04#define INTEL_SRB_BLS 0x02/* Intel extended status bits */#define INTEL_XSRB_WBS 0x80 /* Intel block lock status */#define BLOCK_CONFIG_MASK 0xFFFFF000#define BLOCK_CONFIG_ADDR 0x02enum { /* 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, /* Intel CMDs */ CMDI_READ_ARRAY = 0xFF, CMDI_READ_CONFIG = 0x90, CMDI_READ_QRY = 0x98, CMDI_READ_STATUS = 0x70, CMDI_CLEAR_STATUS = 0x50, CMDI_PROGRAM = 0x40, CMDI_BLOCK_ERASE = 0x20, CMDI_BLOCK_CONFIRM = 0xD0, CMDI_SUSPEND = 0xB0, CMDI_RESUME = 0xD0, CMDI_LOCK_CONFIG = 0x60, CMDI_LOCK = 0x01, CMDI_UNLOCK = 0xD0, CMDI_LOCKDOWN = 0x2F, CMDI_PROTECT_PROGRAM = 0xC0, CMDI_WRITE_BUFFER = 0xE8, CMDI_WRITE_BUFFER_CONFIRM = 0xD0, /* Offsets */ OFFSET_CFI_ID = 0x10, OFFSET_CFI_SIZE = 0x27, OFFSET_CFI_BUFFER_SIZE = 0x2A, OFFSET_CFI_BLOCK_COUNT = 0x2C, OFFSET_CFI_BLOCK = 0x2D, /* Manufacturers */ MANUFACTURER_AMD = 0x01, MANUFACTURER_FUJITSU = 0x04, MANUFACTURER_SST = 0xBF, MANUFACTURER_ST = 0x20, MANUFACTURER_TOSHIBA = 0x98, MANUFACTURER_INTEL = 0x89, /* Intel devices */ TE28F800C3T = 0x88C0, TE28F800C3B = 0x88C1, TE28F160C3T = 0x88C2, TE28F160C3B = 0x88C3, TE28F320C3T = 0x88C4, TE28F320C3B = 0x88C5, TE28F640C3T = 0x88CC, TE28F640C3B = 0x88CD, TE28F320J3A = 0x0016, TE28F640J3A = 0x0017, TE28F128J3A = 0x0018, /* AMD devices */ AM29F800BB = 0x2258, AM29F800BT = 0x22D6, AM29LV800BB = 0x225B, AM29LV800BT = 0x22DA, AM29LV160BT = 0x22C4, /* Fujitsu devices */ MBM29LV160TE = 0x22C4, MBM29LV160BE = 0x2249, /* SST devices */ SST39LF800 = 0x2781, SST39LF160 = 0x2782, /* ST devices */ M29W800T = 0x00D7, /* Used in 5600, similar to * AM29LV800, but no unlock * bypass */ /* Toshiba devices */ TC58FVT160 = 0x00C2, TC58FVB160 = 0x0043, TC58FVT800 = 0x004F, /* Toggle bit mask */ D6_MASK = 0x40};typedef struct flash_type { volatile unsigned char *base; udword type; byte interleave; byte buswidth; volatile unsigned char *boot_sector; udword mfr; udword dev_id; unsigned int size; unsigned int sector_size; unsigned int boot_sector_size[8]; unsigned int buffer_size; // StrataFlash} flash_t;/* Allocate flash structures and initialize base. */static flash_t flashes[2] = { { (unsigned char *)0x80000000 }, { (unsigned char *)0x84000000 }};static intwide_read(flash_t *flash, unsigned long offset) { switch (flash->buswidth) { case 2:FDEBUG(send_serial_hex((uword *)(flash->base + offset), 0);)FDEBUG(safe_printk(" => ");)FDEBUG(send_serial_hex(*((uword *)(flash->base + offset)), NL);) return *((uword *)(flash->base + offset)); case 4:FDEBUG(send_serial_hex((udword *)(flash->base + offset), 0);)FDEBUG(safe_printk(" => ");)FDEBUG(send_serial_hex(*((udword *)(flash->base + offset)), NL);) return *((udword *)(flash->base + offset)); } return 0;}static intwide_write_chunk(flash_t *flash, unsigned long offset, const unsigned char *chunk) { switch (flash->buswidth) { case 2: *((uword *)(flash->base + offset)) = *((uword *)chunk);FDEBUG(send_serial_hex(((uword *)(flash->base + offset)), 0);)FDEBUG(safe_printk(" <= ");)FDEBUG(send_serial_hex(*((uword *)chunk), NL);) return 2; case 4: *((udword *)(flash->base + offset)) = *((udword *)chunk);FDEBUG(send_serial_hex(((udword *)(flash->base + offset)), 0);)FDEBUG(safe_printk(" <= ");)FDEBUG(send_serial_hex(*((udword *)chunk), NL);) return 4; } return 0;}static voidwide_cmd(flash_t *flash, udword cmd, unsigned long offset) { if ((flash->interleave == 1) && (flash->type == TYPE_X16)) { offset <<= 1; } else if ((flash->interleave == 2) && (flash->type == TYPE_X16)) { cmd |= (cmd << 16); offset <<= 2; } else { safe_printk("Unsupported!\n"); return; }FDEBUG(send_serial_hex(flash->base + offset, 0);)FDEBUG(safe_printk(" cmd ");)FDEBUG(send_serial_hex(cmd, NL);) switch (flash->buswidth) { case 2: *((uword *)(flash->base + offset)) = (uword)cmd; break; case 4: *((udword *)(flash->base + offset)) = cmd; break; }}// differs from here#ifdef MYINTELstatic intwait_flash_ready(flash_t *flash){ udword data32; /* give time for busy signal to be ready */ nop(); nop(); nop(); nop(); nop(); nop(); nop(); nop(); nop(); nop(); if (flash->interleave == 2) { // wait write operation is over while ((~(data32=wide_read(flash, 0))) & ((INTEL_SRB_WSMS) | (INTEL_SRB_WSMS << 16))); return (data32 | (data32>>16)) & ( INTEL_SRB_ESS | INTEL_SRB_ES | INTEL_SRB_PS | INTEL_SRB_VPPS | INTEL_SRB_PSS | INTEL_SRB_BLS ); } else { while ((~(data32=wide_read(flash, 0))) & INTEL_SRB_WSMS); return data32 & ( INTEL_SRB_ESS | INTEL_SRB_ES | INTEL_SRB_PS | INTEL_SRB_VPPS | INTEL_SRB_PSS | INTEL_SRB_BLS ); } }#elsestatic voidflash_unlock(flash_t *flash) { wide_cmd(flash, CMD_UNLOCK_DATA_1, ADDR_UNLOCK_1); wide_cmd(flash, CMD_UNLOCK_DATA_2, ADDR_UNLOCK_2);}static intflash_is_busy(flash_t *flash, unsigned long offset){ 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)); } return ((wide_read(flash, offset) & D6_MASK) != (wide_read(flash, offset) & D6_MASK));}#endifstatic int try_cfi(flash_t *flash){ int offset_shift = 1; if (flash->interleave == 2) { offset_shift = 2; } /* 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 */ char boot_sector = 0; /* Current boot sector */ int offset = 0; /* Offset into flash */ int reverse = 0; /* Reverse block table */ int primary; /* Offset to vendor specific table */ safe_printk("CFI dev "); send_serial_hex((udword)flash->base, NL); flash->size = 1 << wide_read(flash, OFFSET_CFI_SIZE << offset_shift);/* Buffer write support */ flash->buffer_size = 1 << wide_read(flash, OFFSET_CFI_BUFFER_SIZE << offset_shift); flash->buffer_size >>=1; // (bytes -> words) /* 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 */ #ifdef MYINTEL reverse=0;#else 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 * flash->interleave) & 0x80; wide_cmd(flash, CMD_CFI_QUERY_DATA, ADDR_CFI_QUERY); } else { reverse = ((byte)wide_read(flash, (primary+15) << offset_shift) == 3); }#endif /* Blocks are stored backwards compared to flash organization */ for (block = reverse ? block_count - 1 : 0; reverse ? block >= 0 : block < block_count; reverse ? block-- : block++) { /* 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; if (sector_count > 8) { /* Not boot sectors */ int temp; flash->sector_size = sector_size; /* Can't use multiplication (we have no lib). */ for (temp = 0 ; temp < sector_count ; temp++) { offset += sector_size; } } else { /* Boot sectors */ if (boot_sector == 0) { /* Set boot sector start */ flash->boot_sector = flash->base + offset; } for (; sector_count > 0; sector_count--) { flash->boot_sector_size[ (udword)(boot_sector++) ] = sector_size; offset += sector_size; } } /* 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 */#ifdef MYINTEL /* switch to read array mode */ wide_cmd(flash, CMDI_READ_ARRAY, 0);#else flash_unlock(flash); wide_cmd(flash, CMD_RESET_DATA, ADDR_UNLOCK_1);#endif return 1; } /* reset */#ifdef MYINTEL /* switch to read array mode */ wide_cmd(flash, CMDI_READ_ARRAY, 0);#else flash_unlock(flash); wide_cmd(flash, CMD_RESET_DATA, ADDR_UNLOCK_1);#endif return 0;}static intflash_probe(flash_t *flash){#ifndef MYINTEL char *message;#endif if (try_cfi(flash)) { return 1; } else if (flash->interleave == 1) { safe_printk("No 1x CFI at "); send_serial_hex((udword)flash->base, NL); }#ifdef MYINTEL return 0; // no cfi found#else /* read manufacturer */ flash_unlock(flash); wide_cmd(flash, CMD_MANUFACTURER_UNLOCK_DATA, ADDR_UNLOCK_1);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -