Skip to content
Snippets Groups Projects
cfi_flash.c 52.9 KiB
Newer Older
  • Learn to ignore specific revisions
  • Wolfgang Denk's avatar
    Wolfgang Denk committed
     * (C) Copyright 2002-2004
    
     * Brad Kemp, Seranoa Networks, Brad.Kemp@seranoa.com
     *
     * Copyright (C) 2003 Arabella Software Ltd.
     * Yuli Barcohen <yuli@arabellasw.com>
     *
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
     * Copyright (C) 2004
     * Ed Okerson
    
     *
     * Copyright (C) 2006
     * Tolunay Orkun <listmember@orkun.us>
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
     *
    
     * 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
     *
     */
    
    /* The DEBUG define must be before common to enable debugging */
    
    /* #define DEBUG	*/
    
    
    #include <common.h>
    #include <asm/processor.h>
    
    #include <asm/byteorder.h>
    
    #include <environment.h>
    
     * This file implements a Common Flash Interface (CFI) driver for
     * U-Boot.
     *
     * The width of the port and the width of the chips are determined at
     * initialization.  These widths are used to calculate the address for
     * access CFI data structures.
    
     *
     * References
     * JEDEC Standard JESD68 - Common Flash Interface (CFI)
     * JEDEC Standard JEP137-A Common Flash Interface (CFI) ID Codes
     * Intel Application Note 646 Common Flash Interface (CFI) and Command Sets
     * Intel 290667-008 3 Volt Intel StrataFlash Memory datasheet
    
     * AMD CFI Specification, Release 2.0 December 1, 2001
     * AMD/Spansion Application Note: Migration from Single-byte to Three-byte
     *   Device IDs, Publication Number 25538 Revision A, November 8, 2001
    
     * Define CFG_WRITE_SWAPPED_DATA, if you have to swap the Bytes between
    
     * reading and writing ... (yes there is such a Hardware).
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    #ifndef CFG_FLASH_BANKS_LIST
    #define CFG_FLASH_BANKS_LIST { CFG_FLASH_BASE }
    #endif
    
    
    #define FLASH_CMD_CFI			0x98
    #define FLASH_CMD_READ_ID		0x90
    #define FLASH_CMD_RESET			0xff
    #define FLASH_CMD_BLOCK_ERASE		0x20
    #define FLASH_CMD_ERASE_CONFIRM		0xD0
    #define FLASH_CMD_WRITE			0x40
    #define FLASH_CMD_PROTECT		0x60
    #define FLASH_CMD_PROTECT_SET		0x01
    #define FLASH_CMD_PROTECT_CLEAR		0xD0
    #define FLASH_CMD_CLEAR_STATUS		0x50
    
    #define FLASH_CMD_READ_STATUS		0x70
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    #define FLASH_CMD_WRITE_TO_BUFFER	0xE8
    
    #define FLASH_CMD_WRITE_BUFFER_PROG	0xE9
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    #define FLASH_CMD_WRITE_BUFFER_CONFIRM	0xD0
    
    
    #define FLASH_STATUS_DONE		0x80
    #define FLASH_STATUS_ESS		0x40
    #define FLASH_STATUS_ECLBS		0x20
    #define FLASH_STATUS_PSLBS		0x10
    #define FLASH_STATUS_VPENS		0x08
    #define FLASH_STATUS_PSS		0x04
    #define FLASH_STATUS_DPS		0x02
    #define FLASH_STATUS_R			0x01
    #define FLASH_STATUS_PROTECT		0x01
    
    #define AMD_CMD_RESET			0xF0
    #define AMD_CMD_WRITE			0xA0
    #define AMD_CMD_ERASE_START		0x80
    #define AMD_CMD_ERASE_SECTOR		0x30
    
    #define AMD_CMD_UNLOCK_START		0xAA
    #define AMD_CMD_UNLOCK_ACK		0x55
    
    #define AMD_CMD_WRITE_TO_BUFFER		0x25
    #define AMD_CMD_WRITE_BUFFER_CONFIRM	0x29
    
    
    #define AMD_STATUS_TOGGLE		0x40
    #define AMD_STATUS_ERROR		0x20
    
    #define ATM_CMD_UNLOCK_SECT		0x70
    #define ATM_CMD_SOFTLOCK_START		0x80
    #define ATM_CMD_LOCK_SECT		0x40
    
    
    #define FLASH_OFFSET_MANUFACTURER_ID	0x00
    #define FLASH_OFFSET_DEVICE_ID		0x01
    #define FLASH_OFFSET_DEVICE_ID2		0x0E
    #define FLASH_OFFSET_DEVICE_ID3		0x0F
    
    #define FLASH_OFFSET_CFI		0x55
    
    #define FLASH_OFFSET_CFI_ALT		0x555
    
    #define FLASH_OFFSET_CFI_RESP		0x10
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    #define FLASH_OFFSET_PRIMARY_VENDOR	0x13
    
    /* extended query table primary address */
    #define FLASH_OFFSET_EXT_QUERY_T_P_ADDR	0x15
    
    #define FLASH_OFFSET_WTOUT		0x1F
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    #define FLASH_OFFSET_WBTOUT		0x20
    
    #define FLASH_OFFSET_ETOUT		0x21
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    #define FLASH_OFFSET_CETOUT		0x22
    
    #define FLASH_OFFSET_WMAX_TOUT		0x23
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    #define FLASH_OFFSET_WBMAX_TOUT		0x24
    
    #define FLASH_OFFSET_EMAX_TOUT		0x25
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    #define FLASH_OFFSET_CEMAX_TOUT		0x26
    
    #define FLASH_OFFSET_SIZE		0x27
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    #define FLASH_OFFSET_INTERFACE		0x28
    #define FLASH_OFFSET_BUFFER_SIZE	0x2A
    
    #define FLASH_OFFSET_NUM_ERASE_REGIONS	0x2C
    #define FLASH_OFFSET_ERASE_REGIONS	0x2D
    #define FLASH_OFFSET_PROTECT		0x02
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    #define FLASH_OFFSET_USER_PROTECTION	0x85
    #define FLASH_OFFSET_INTEL_PROTECTION	0x81
    
    #define CFI_CMDSET_NONE			0
    #define CFI_CMDSET_INTEL_EXTENDED	1
    #define CFI_CMDSET_AMD_STANDARD		2
    #define CFI_CMDSET_INTEL_STANDARD	3
    #define CFI_CMDSET_AMD_EXTENDED		4
    #define CFI_CMDSET_MITSU_STANDARD	256
    #define CFI_CMDSET_MITSU_EXTENDED	257
    #define CFI_CMDSET_SST			258
    
    #define CFI_CMDSET_INTEL_PROG_REGIONS	512
    
    #ifdef CFG_FLASH_CFI_AMD_RESET /* needed for STM_ID_29W320DB on UC100 */
    # undef  FLASH_CMD_RESET
    
    # define FLASH_CMD_RESET	AMD_CMD_RESET /* use AMD-Reset instead */
    
    typedef union {
    	unsigned char c;
    	unsigned short w;
    	unsigned long l;
    	unsigned long long ll;
    } cfiword_t;
    
    
    #define NUM_ERASE_REGIONS	4 /* max. number of erase regions */
    
    static uint flash_offset_cfi[2] = { FLASH_OFFSET_CFI, FLASH_OFFSET_CFI_ALT };
    
    /* use CFG_MAX_FLASH_BANKS_DETECT if defined */
    #ifdef CFG_MAX_FLASH_BANKS_DETECT
    
    # define CFI_MAX_FLASH_BANKS	CFG_MAX_FLASH_BANKS_DETECT
    
    # define CFI_MAX_FLASH_BANKS	CFG_MAX_FLASH_BANKS
    
    flash_info_t flash_info[CFI_MAX_FLASH_BANKS];	/* FLASH chips info */
    
    
    /*
     * Check if chip width is defined. If not, start detecting with 8bit.
     */
    #ifndef CFG_FLASH_CFI_WIDTH
    #define CFG_FLASH_CFI_WIDTH	FLASH_CFI_8BIT
    #endif
    
    
    typedef unsigned long flash_sect_t;
    
    /* CFI standard query structure */
    struct cfi_qry {
    	u8	qry[3];
    	u16	p_id;
    	u16	p_adr;
    	u16	a_id;
    	u16	a_adr;
    	u8	vcc_min;
    	u8	vcc_max;
    	u8	vpp_min;
    	u8	vpp_max;
    	u8	word_write_timeout_typ;
    	u8	buf_write_timeout_typ;
    	u8	block_erase_timeout_typ;
    	u8	chip_erase_timeout_typ;
    	u8	word_write_timeout_max;
    	u8	buf_write_timeout_max;
    	u8	block_erase_timeout_max;
    	u8	chip_erase_timeout_max;
    	u8	dev_size;
    	u16	interface_desc;
    	u16	max_buf_write_size;
    	u8	num_erase_regions;
    	u32	erase_region_info[NUM_ERASE_REGIONS];
    } __attribute__((packed));
    
    struct cfi_pri_hdr {
    	u8	pri[3];
    	u8	major_version;
    	u8	minor_version;
    } __attribute__((packed));
    
    
    static void flash_write8(u8 value, void *addr)
    {
    	__raw_writeb(value, addr);
    }
    
    static void flash_write16(u16 value, void *addr)
    {
    	__raw_writew(value, addr);
    }
    
    static void flash_write32(u32 value, void *addr)
    {
    	__raw_writel(value, addr);
    }
    
    static void flash_write64(u64 value, void *addr)
    {
    	/* No architectures currently implement __raw_writeq() */
    	*(volatile u64 *)addr = value;
    }
    
    static u8 flash_read8(void *addr)
    {
    	return __raw_readb(addr);
    }
    
    static u16 flash_read16(void *addr)
    {
    	return __raw_readw(addr);
    }
    
    static u32 flash_read32(void *addr)
    {
    	return __raw_readl(addr);
    }
    
    
    static u64 __flash_read64(void *addr)
    
    {
    	/* No architectures currently implement __raw_readq() */
    	return *(volatile u64 *)addr;
    }
    
    
    u64 flash_read64(void *addr)__attribute__((weak, alias("__flash_read64")));
    
    
    /*-----------------------------------------------------------------------
     */
    
    #if defined(CFG_ENV_IS_IN_FLASH) || defined(CFG_ENV_ADDR_REDUND) || (CFG_MONITOR_BASE >= CFG_FLASH_BASE)
    static flash_info_t *flash_get_info(ulong base)
    {
    	int i;
    	flash_info_t * info = 0;
    
    	for (i = 0; i < CFG_MAX_FLASH_BANKS; i++) {
    		info = & flash_info[i];
    		if (info->size && info->start[0] <= base &&
    		    base <= info->start[0] + info->size - 1)
    			break;
    	}
    
    	return i == CFG_MAX_FLASH_BANKS ? 0 : info;
    }
    
    unsigned long flash_sector_size(flash_info_t *info, flash_sect_t sect)
    {
    	if (sect != (info->sector_count - 1))
    		return info->start[sect + 1] - info->start[sect];
    	else
    		return info->start[0] + info->size - info->start[sect];
    }
    
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    /*-----------------------------------------------------------------------
     * create an address based on the offset and the port width
     */
    
    static inline void *
    flash_map (flash_info_t * info, flash_sect_t sect, uint offset)
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    {
    
    	unsigned int byte_offset = offset * info->portwidth;
    
    	return map_physmem(info->start[sect] + byte_offset,
    			flash_sector_size(info, sect) - byte_offset,
    			MAP_NOCACHE);
    }
    
    static inline void flash_unmap(flash_info_t *info, flash_sect_t sect,
    		unsigned int offset, void *addr)
    {
    	unsigned int byte_offset = offset * info->portwidth;
    
    	unmap_physmem(addr, flash_sector_size(info, sect) - byte_offset);
    
    /*-----------------------------------------------------------------------
     * make a proper sized command based on the port and chip widths
     */
    
    static void flash_make_cmd(flash_info_t *info, u32 cmd, void *cmdbuf)
    
    	int cword_offset;
    	int cp_offset;
    
    #if defined(__LITTLE_ENDIAN) || defined(CFG_WRITE_SWAPPED_DATA)
    	u32 cmd_le = cpu_to_le32(cmd);
    #endif
    
    	for (i = info->portwidth; i > 0; i--){
    		cword_offset = (info->portwidth-i)%info->chipwidth;
    
    #if defined(__LITTLE_ENDIAN) || defined(CFG_WRITE_SWAPPED_DATA)
    
    		cp_offset = info->portwidth - i;
    
    		val = *((uchar*)&cmd_le + cword_offset);
    
    		cp_offset = i - 1;
    
    		val = *((uchar*)&cmd + sizeof(u32) - cword_offset - 1);
    
    		cp[cp_offset] = (cword_offset >= sizeof(u32)) ? 0x00 : val;
    
    #ifdef DEBUG
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    /*-----------------------------------------------------------------------
     * Debug support
     */
    
    static void print_longlong (char *str, unsigned long long data)
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    
    	cp = (unsigned char *) &data;
    	for (i = 0; i < 8; i++)
    		sprintf (&str[i * 2], "%2.2x", *cp++);
    }
    
    static void flash_printqry (struct cfi_qry *qry)
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    {
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    	int x, y;
    
    
    	for (x = 0; x < sizeof(struct cfi_qry); x += 16) {
    		debug("%02x : ", x);
    		for (y = 0; y < 16; y++)
    			debug("%2.2x ", p[x + y]);
    		debug(" ");
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    		for (y = 0; y < 16; y++) {
    
    			unsigned char c = p[x + y];
    			if (c >= 0x20 && c <= 0x7e)
    				debug("%c", c);
    			else
    				debug(".");
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    		}
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    	}
    
    }
    #endif
    
    
    /*-----------------------------------------------------------------------
     * read a character at a port width address
     */
    
    static inline uchar flash_read_uchar (flash_info_t * info, uint offset)
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    
    
    	cp = flash_map (info, 0, offset);
    
    #if defined(__LITTLE_ENDIAN) || defined(CFG_WRITE_SWAPPED_DATA)
    
    	retval = flash_read8(cp);
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    #else
    
    	retval = flash_read8(cp + info->portwidth - 1);
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    #endif
    
    	flash_unmap (info, 0, offset, cp);
    	return retval;
    
    /*-----------------------------------------------------------------------
     * read a word at a port width address, assume 16bit bus
     */
    static inline ushort flash_read_word (flash_info_t * info, uint offset)
    {
    	ushort *addr, retval;
    
    	addr = flash_map (info, 0, offset);
    	retval = flash_read16 (addr);
    	flash_unmap (info, 0, offset, addr);
    	return retval;
    }
    
    
    
    /*-----------------------------------------------------------------------
    
     * read a long word by picking the least significant byte of each maximum
    
     * port size word. Swap for ppc format.
     */
    
    static ulong flash_read_long (flash_info_t * info, flash_sect_t sect,
    			      uint offset)
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    	uchar *addr;
    	ulong retval;
    
    #ifdef DEBUG
    	int x;
    #endif
    
    	addr = flash_map (info, sect, offset);
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    #ifdef DEBUG
    	debug ("long addr is at %p info->portwidth = %d\n", addr,
    	       info->portwidth);
    	for (x = 0; x < 4 * info->portwidth; x++) {
    
    		debug ("addr[%x] = 0x%x\n", x, flash_read8(addr + x));
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    	}
    #endif
    
    #if defined(__LITTLE_ENDIAN) || defined(CFG_WRITE_SWAPPED_DATA)
    
    	retval = ((flash_read8(addr) << 16) |
    		  (flash_read8(addr + info->portwidth) << 24) |
    		  (flash_read8(addr + 2 * info->portwidth)) |
    		  (flash_read8(addr + 3 * info->portwidth) << 8));
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    #else
    
    	retval = ((flash_read8(addr + 2 * info->portwidth - 1) << 24) |
    		  (flash_read8(addr + info->portwidth - 1) << 16) |
    		  (flash_read8(addr + 4 * info->portwidth - 1) << 8) |
    		  (flash_read8(addr + 3 * info->portwidth - 1)));
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    #endif
    
    	flash_unmap(info, sect, offset, addr);
    
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    	return retval;
    
    /*
     * Write a proper sized command to the correct address
    
    static void flash_write_cmd (flash_info_t * info, flash_sect_t sect,
    
    			     uint offset, u32 cmd)
    
    	addr = flash_map (info, sect, offset);
    
    	flash_make_cmd (info, cmd, &cword);
    	switch (info->portwidth) {
    	case FLASH_CFI_8BIT:
    
    		debug ("fwc addr %p cmd %x %x 8bit x %d bit\n", addr, cmd,
    
    		       cword.c, info->chipwidth << CFI_FLASH_SHIFT_WIDTH);
    
    		flash_write8(cword.c, addr);
    
    		debug ("fwc addr %p cmd %x %4.4x 16bit x %d bit\n", addr,
    
    		       cmd, cword.w,
    		       info->chipwidth << CFI_FLASH_SHIFT_WIDTH);
    
    		flash_write16(cword.w, addr);
    
    		debug ("fwc addr %p cmd %x %8.8lx 32bit x %d bit\n", addr,
    
    		       cmd, cword.l,
    		       info->chipwidth << CFI_FLASH_SHIFT_WIDTH);
    
    		flash_write32(cword.l, addr);
    
    		break;
    	case FLASH_CFI_64BIT:
    #ifdef DEBUG
    		{
    			char str[20];
    
    			print_longlong (str, cword.ll);
    
    			debug ("fwrite addr %p cmd %x %s 64 bit x %d bit\n",
    
    			       info->chipwidth << CFI_FLASH_SHIFT_WIDTH);
    
    		flash_write64(cword.ll, addr);
    
    
    	/* Ensure all the instructions are fully finished */
    	sync();
    
    
    	flash_unmap(info, sect, offset, addr);
    
    
    static void flash_unlock_seq (flash_info_t * info, flash_sect_t sect)
    
    	flash_write_cmd (info, sect, info->addr_unlock1, AMD_CMD_UNLOCK_START);
    	flash_write_cmd (info, sect, info->addr_unlock2, AMD_CMD_UNLOCK_ACK);
    
    /*-----------------------------------------------------------------------
     */
    
    static int flash_isequal (flash_info_t * info, flash_sect_t sect,
    			  uint offset, uchar cmd)
    
    	addr = flash_map (info, sect, offset);
    
    	flash_make_cmd (info, cmd, &cword);
    
    	debug ("is= cmd %x(%c) addr %p ", cmd, cmd, addr);
    
    	switch (info->portwidth) {
    	case FLASH_CFI_8BIT:
    
    		debug ("is= %x %x\n", flash_read8(addr), cword.c);
    		retval = (flash_read8(addr) == cword.c);
    
    		debug ("is= %4.4x %4.4x\n", flash_read16(addr), cword.w);
    		retval = (flash_read16(addr) == cword.w);
    
    		debug ("is= %8.8lx %8.8lx\n", flash_read32(addr), cword.l);
    		retval = (flash_read32(addr) == cword.l);
    
    		break;
    	case FLASH_CFI_64BIT:
    #ifdef DEBUG
    		{
    			char str1[20];
    			char str2[20];
    
    			print_longlong (str1, flash_read64(addr));
    
    			print_longlong (str2, cword.ll);
    			debug ("is= %s %s\n", str1, str2);
    
    		retval = (flash_read64(addr) == cword.ll);
    
    	flash_unmap(info, sect, offset, addr);
    
    
    /*-----------------------------------------------------------------------
     */
    static int flash_isset (flash_info_t * info, flash_sect_t sect,
    			uint offset, uchar cmd)
    {
    
    	addr = flash_map (info, sect, offset);
    
    	flash_make_cmd (info, cmd, &cword);
    	switch (info->portwidth) {
    	case FLASH_CFI_8BIT:
    
    		retval = ((flash_read8(addr) & cword.c) == cword.c);
    
    		retval = ((flash_read16(addr) & cword.w) == cword.w);
    
    		retval = ((flash_read32(addr) & cword.l) == cword.l);
    
    		retval = ((flash_read64(addr) & cword.ll) == cword.ll);
    
    	flash_unmap(info, sect, offset, addr);
    
    
    /*-----------------------------------------------------------------------
     */
    static int flash_toggle (flash_info_t * info, flash_sect_t sect,
    			 uint offset, uchar cmd)
    {
    
    	addr = flash_map (info, sect, offset);
    
    	flash_make_cmd (info, cmd, &cword);
    	switch (info->portwidth) {
    	case FLASH_CFI_8BIT:
    
    		retval = flash_read8(addr) != flash_read8(addr);
    
    		retval = flash_read16(addr) != flash_read16(addr);
    
    		retval = flash_read32(addr) != flash_read32(addr);
    
    		retval = flash_read64(addr) != flash_read64(addr);
    
    	flash_unmap(info, sect, offset, addr);
    
    
    /*
     * flash_is_busy - check to see if the flash is busy
     *
     * This routine checks the status of the chip and returns true if the
     * chip is busy.
    
    static int flash_is_busy (flash_info_t * info, flash_sect_t sect)
    
    	case CFI_CMDSET_INTEL_PROG_REGIONS:
    
    	case CFI_CMDSET_INTEL_STANDARD:
    	case CFI_CMDSET_INTEL_EXTENDED:
    		retval = !flash_isset (info, sect, 0, FLASH_STATUS_DONE);
    		break;
    	case CFI_CMDSET_AMD_STANDARD:
    	case CFI_CMDSET_AMD_EXTENDED:
    #ifdef CONFIG_FLASH_CFI_LEGACY
    	case CFI_CMDSET_AMD_LEGACY:
    #endif
    		retval = flash_toggle (info, sect, 0, AMD_STATUS_TOGGLE);
    		break;
    	default:
    		retval = 0;
    
    	debug ("flash_is_busy: %d\n", retval);
    	return retval;
    
    /*-----------------------------------------------------------------------
    
     *  wait for XSR.7 to be set. Time out with an error if it does not.
     *  This routine does not set the flash to read-array mode.
    
    static int flash_status_check (flash_info_t * info, flash_sect_t sector,
    			       ulong tout, char *prompt)
    
    #if CFG_HZ != 1000
    	tout *= CFG_HZ/1000;
    #endif
    
    	/* Wait for command completion */
    	start = get_timer (0);
    	while (flash_is_busy (info, sector)) {
    		if (get_timer (start) > tout) {
    			printf ("Flash %s timeout at address %lx data %lx\n",
    				prompt, info->start[sector],
    				flash_read_long (info, sector, 0));
    			flash_write_cmd (info, sector, 0, info->cmd_reset);
    			return ERR_TIMOUT;
    
    		udelay (1);		/* also triggers watchdog */
    
    /*-----------------------------------------------------------------------
     * Wait for XSR.7 to be set, if it times out print an error, otherwise
     * do a full status check.
     *
     * This routine sets the flash to read-array mode.
     */
    static int flash_full_status_check (flash_info_t * info, flash_sect_t sector,
    				    ulong tout, char *prompt)
    {
    	int retcode;
    
    	retcode = flash_status_check (info, sector, tout, prompt);
    	switch (info->vendor) {
    
    	case CFI_CMDSET_INTEL_PROG_REGIONS:
    
    	case CFI_CMDSET_INTEL_EXTENDED:
    	case CFI_CMDSET_INTEL_STANDARD:
    		if ((retcode == ERR_OK)
    		    && !flash_isequal (info, sector, 0, FLASH_STATUS_DONE)) {
    			retcode = ERR_INVAL;
    			printf ("Flash %s error at address %lx\n", prompt,
    				info->start[sector]);
    			if (flash_isset (info, sector, 0, FLASH_STATUS_ECLBS |
    					 FLASH_STATUS_PSLBS)) {
    				puts ("Command Sequence Error.\n");
    			} else if (flash_isset (info, sector, 0,
    						FLASH_STATUS_ECLBS)) {
    				puts ("Block Erase Error.\n");
    				retcode = ERR_NOT_ERASED;
    			} else if (flash_isset (info, sector, 0,
    						FLASH_STATUS_PSLBS)) {
    				puts ("Locking Error\n");
    
    			if (flash_isset (info, sector, 0, FLASH_STATUS_DPS)) {
    				puts ("Block locked.\n");
    				retcode = ERR_PROTECTED;
    			}
    			if (flash_isset (info, sector, 0, FLASH_STATUS_VPENS))
    				puts ("Vpp Low Error.\n");
    
    		flash_write_cmd (info, sector, 0, info->cmd_reset);
    		break;
    	default:
    		break;
    
    }
    
    /*-----------------------------------------------------------------------
     */
    
    static void flash_add_byte (flash_info_t * info, cfiword_t * cword, uchar c)
    
    #if defined(__LITTLE_ENDIAN) && !defined(CFG_WRITE_SWAPPED_DATA)
    	unsigned short	w;
    	unsigned int	l;
    	unsigned long long ll;
    #endif
    
    	switch (info->portwidth) {
    	case FLASH_CFI_8BIT:
    		cword->c = c;
    		break;
    	case FLASH_CFI_16BIT:
    #if defined(__LITTLE_ENDIAN) && !defined(CFG_WRITE_SWAPPED_DATA)
    		w = c;
    		w <<= 8;
    		cword->w = (cword->w >> 8) | w;
    #else
    		cword->w = (cword->w << 8) | c;
    
    		break;
    	case FLASH_CFI_32BIT:
    #if defined(__LITTLE_ENDIAN) && !defined(CFG_WRITE_SWAPPED_DATA)
    		l = c;
    		l <<= 24;
    		cword->l = (cword->l >> 8) | l;
    #else
    		cword->l = (cword->l << 8) | c;
    #endif
    		break;
    	case FLASH_CFI_64BIT:
    #if defined(__LITTLE_ENDIAN) && !defined(CFG_WRITE_SWAPPED_DATA)
    		ll = c;
    		ll <<= 56;
    		cword->ll = (cword->ll >> 8) | ll;
    #else
    		cword->ll = (cword->ll << 8) | c;
    #endif
    		break;
    
    /* loop through the sectors from the highest address when the passed
     * address is greater or equal to the sector address we have a match
     */
    static flash_sect_t find_sector (flash_info_t * info, ulong addr)
    {
    	flash_sect_t sector;
    
    	for (sector = info->sector_count - 1; sector >= 0; sector--) {
    		if (addr >= info->start[sector])
    			break;
    
    }
    
    /*-----------------------------------------------------------------------
     */
    
    static int flash_write_cfiword (flash_info_t * info, ulong dest,
    				cfiword_t cword)
    
    	dstaddr = map_physmem(dest, info->portwidth, MAP_NOCACHE);
    
    	/* Check if Flash is (sufficiently) erased */
    	switch (info->portwidth) {
    	case FLASH_CFI_8BIT:
    
    		flag = ((flash_read8(dstaddr) & cword.c) == cword.c);
    
    		flag = ((flash_read16(dstaddr) & cword.w) == cword.w);
    
    		flag = ((flash_read32(dstaddr) & cword.l) == cword.l);
    
    		flag = ((flash_read64(dstaddr) & cword.ll) == cword.ll);
    
    	if (!flag) {
    		unmap_physmem(dstaddr, info->portwidth);
    
    	/* Disable interrupts which might cause a timeout here */
    	flag = disable_interrupts ();
    
    	case CFI_CMDSET_INTEL_PROG_REGIONS:
    
    	case CFI_CMDSET_INTEL_EXTENDED:
    	case CFI_CMDSET_INTEL_STANDARD:
    		flash_write_cmd (info, 0, 0, FLASH_CMD_CLEAR_STATUS);
    		flash_write_cmd (info, 0, 0, FLASH_CMD_WRITE);
    		break;
    	case CFI_CMDSET_AMD_EXTENDED:
    	case CFI_CMDSET_AMD_STANDARD:
    #ifdef CONFIG_FLASH_CFI_LEGACY
    	case CFI_CMDSET_AMD_LEGACY:
    #endif
    		flash_unlock_seq (info, 0);
    		flash_write_cmd (info, 0, info->addr_unlock1, AMD_CMD_WRITE);
    		break;
    
    	switch (info->portwidth) {
    	case FLASH_CFI_8BIT:
    
    		flash_write8(cword.c, dstaddr);
    
    		flash_write16(cword.w, dstaddr);
    
    		flash_write32(cword.l, dstaddr);
    
    		flash_write64(cword.ll, dstaddr);
    
    	/* re-enable interrupts if necessary */
    	if (flag)
    		enable_interrupts ();
    
    	unmap_physmem(dstaddr, info->portwidth);
    
    
    	return flash_full_status_check (info, find_sector (info, dest),
    					info->write_tout, "write");
    
    #ifdef CFG_FLASH_USE_BUFFER_WRITE
    
    static int flash_write_cfibuffer (flash_info_t * info, ulong dest, uchar * cp,
    				  int len)
    
    	flash_sect_t sector;
    	int cnt;
    	int retcode;
    
    	void *dst = map_physmem(dest, len, MAP_NOCACHE);
    
    	switch (info->portwidth) {
    	case FLASH_CFI_8BIT:
    
    	while ((cnt-- > 0) && (flag == 0)) {
    		switch (info->portwidth) {
    		case FLASH_CFI_8BIT:
    			flag = ((flash_read8(dst2) & flash_read8(src)) ==
    				flash_read8(src));
    			src += 1, dst2 += 1;
    			break;
    		case FLASH_CFI_16BIT:
    			flag = ((flash_read16(dst2) & flash_read16(src)) ==
    				flash_read16(src));
    			src += 2, dst2 += 2;
    			break;
    		case FLASH_CFI_32BIT:
    			flag = ((flash_read32(dst2) & flash_read32(src)) ==
    				flash_read32(src));
    			src += 4, dst2 += 4;
    			break;
    		case FLASH_CFI_64BIT:
    			flag = ((flash_read64(dst2) & flash_read64(src)) ==
    				flash_read64(src));
    			src += 8, dst2 += 8;
    			break;
    		}
    	}
    	if (!flag) {
    		retcode = ERR_NOT_ERASED;
    		goto out_unmap;
    	}
    
    	src = cp;
    
    	sector = find_sector (info, dest);
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    
    	switch (info->vendor) {
    
    	case CFI_CMDSET_INTEL_PROG_REGIONS:
    
    	case CFI_CMDSET_INTEL_STANDARD:
    	case CFI_CMDSET_INTEL_EXTENDED:
    
    		write_cmd = (info->vendor == CFI_CMDSET_INTEL_PROG_REGIONS) ?
    					FLASH_CMD_WRITE_BUFFER_PROG : FLASH_CMD_WRITE_TO_BUFFER;
    
    		flash_write_cmd (info, sector, 0, FLASH_CMD_CLEAR_STATUS);
    
    		flash_write_cmd (info, sector, 0, FLASH_CMD_READ_STATUS);
    		flash_write_cmd (info, sector, 0, write_cmd);
    
    		retcode = flash_status_check (info, sector,
    					      info->buffer_write_tout,
    					      "write to buffer");
    		if (retcode == ERR_OK) {
    			/* reduce the number of loops by the width of
    			 * the port */
    
    			flash_write_cmd (info, sector, 0, cnt - 1);
    
    			while (cnt-- > 0) {
    				switch (info->portwidth) {
    				case FLASH_CFI_8BIT:
    
    					flash_write8(flash_read8(src), dst);
    					src += 1, dst += 1;
    
    					flash_write16(flash_read16(src), dst);
    					src += 2, dst += 2;
    
    					flash_write32(flash_read32(src), dst);
    					src += 4, dst += 4;
    
    					flash_write64(flash_read64(src), dst);
    					src += 8, dst += 8;
    
    					retcode = ERR_INVAL;
    					goto out_unmap;
    
    				}
    			}
    			flash_write_cmd (info, sector, 0,
    					 FLASH_CMD_WRITE_BUFFER_CONFIRM);
    			retcode = flash_full_status_check (
    				info, sector, info->buffer_write_tout,
    				"buffer write");
    		}
    
    	case CFI_CMDSET_AMD_STANDARD:
    	case CFI_CMDSET_AMD_EXTENDED:
    
    
    #ifdef CONFIG_FLASH_SPANSION_S29WS_N
    		offset = ((unsigned long)dst - info->start[sector]) >> shift;
    #endif
    		flash_write_cmd(info, sector, offset, AMD_CMD_WRITE_TO_BUFFER);
    		cnt = len >> shift;
    		flash_write_cmd(info, sector, offset, (uchar)cnt - 1);
    
    
    		switch (info->portwidth) {
    		case FLASH_CFI_8BIT:
    
    			while (cnt-- > 0) {
    				flash_write8(flash_read8(src), dst);
    				src += 1, dst += 1;
    			}
    
    			while (cnt-- > 0) {
    				flash_write16(flash_read16(src), dst);
    				src += 2, dst += 2;
    			}
    
    			while (cnt-- > 0) {
    				flash_write32(flash_read32(src), dst);
    				src += 4, dst += 4;
    			}
    
    			while (cnt-- > 0) {
    				flash_write64(flash_read64(src), dst);