Skip to content
Snippets Groups Projects
cfi_flash.c 58 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>
    
    #include <mtd/cfi_flash.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 CONFIG_SYS_WRITE_SWAPPED_DATA, if you have to swap the Bytes between
    
     * reading and writing ... (yes there is such a Hardware).
    
    static uint flash_offset_cfi[2] = { FLASH_OFFSET_CFI, FLASH_OFFSET_CFI_ALT };
    
    #ifdef CONFIG_FLASH_CFI_MTD
    
    static uint flash_verbose = 1;
    
    #else
    #define flash_verbose 1
    #endif
    
    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 CONFIG_SYS_FLASH_CFI_WIDTH
    #define CONFIG_SYS_FLASH_CFI_WIDTH	FLASH_CFI_8BIT
    
    /*
     * 0xffff is an undefined value for the configuration register. When
     * this value is returned, the configuration register shall not be
     * written at all (default mode).
     */
    static u16 cfi_flash_config_reg(int i)
    {
    #ifdef CONFIG_SYS_CFI_FLASH_CONFIG_REGS
    	return ((u16 [])CONFIG_SYS_CFI_FLASH_CONFIG_REGS)[i];
    #else
    	return 0xffff;
    #endif
    }
    
    
    #if defined(CONFIG_SYS_MAX_FLASH_BANKS_DETECT)
    int cfi_flash_num_flash_banks = CONFIG_SYS_MAX_FLASH_BANKS_DETECT;
    #endif
    
    
    static phys_addr_t __cfi_flash_bank_addr(int i)
    {
    	return ((phys_addr_t [])CONFIG_SYS_FLASH_BANKS_LIST)[i];
    }
    phys_addr_t cfi_flash_bank_addr(int i)
    	__attribute__((weak, alias("__cfi_flash_bank_addr")));
    
    
    static unsigned long __cfi_flash_bank_size(int i)
    {
    #ifdef CONFIG_SYS_FLASH_BANKS_SIZES
    	return ((unsigned long [])CONFIG_SYS_FLASH_BANKS_SIZES)[i];
    #else
    	return 0;
    #endif
    }
    unsigned long cfi_flash_bank_size(int i)
    	__attribute__((weak, alias("__cfi_flash_bank_size")));
    
    
    static void __flash_write8(u8 value, void *addr)
    
    static void __flash_write16(u16 value, void *addr)
    
    static void __flash_write32(u32 value, void *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)
    
    static u16 __flash_read16(void *addr)
    
    static u32 __flash_read32(void *addr)
    
    static u64 __flash_read64(void *addr)
    
    {
    	/* No architectures currently implement __raw_readq() */
    	return *(volatile u64 *)addr;
    }
    
    
    #ifdef CONFIG_CFI_FLASH_USE_WEAK_ACCESSORS
    void flash_write8(u8 value, void *addr)__attribute__((weak, alias("__flash_write8")));
    void flash_write16(u16 value, void *addr)__attribute__((weak, alias("__flash_write16")));
    void flash_write32(u32 value, void *addr)__attribute__((weak, alias("__flash_write32")));
    void flash_write64(u64 value, void *addr)__attribute__((weak, alias("__flash_write64")));
    u8 flash_read8(void *addr)__attribute__((weak, alias("__flash_read8")));
    u16 flash_read16(void *addr)__attribute__((weak, alias("__flash_read16")));
    u32 flash_read32(void *addr)__attribute__((weak, alias("__flash_read32")));
    
    u64 flash_read64(void *addr)__attribute__((weak, alias("__flash_read64")));
    
    #else
    #define flash_write8	__flash_write8
    #define flash_write16	__flash_write16
    #define flash_write32	__flash_write32
    #define flash_write64	__flash_write64
    #define flash_read8	__flash_read8
    #define flash_read16	__flash_read16
    #define flash_read32	__flash_read32
    #define flash_read64	__flash_read64
    #endif
    
    /*-----------------------------------------------------------------------
     */
    
    #if defined(CONFIG_ENV_IS_IN_FLASH) || defined(CONFIG_ENV_ADDR_REDUND) || (CONFIG_SYS_MONITOR_BASE >= CONFIG_SYS_FLASH_BASE)
    
    flash_info_t *flash_get_info(ulong base)
    
    	flash_info_t *info = NULL;
    
    	for (i = 0; i < CONFIG_SYS_MAX_FLASH_BANKS; i++) {
    
    		info = & flash_info[i];
    		if (info->size && info->start[0] <= base &&
    		    base <= info->start[0] + info->size - 1)
    			break;
    	}
    
    	return 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 (void *)(info->start[sect] + byte_offset);
    
    }
    
    static inline void flash_unmap(flash_info_t *info, flash_sect_t sect,
    		unsigned int offset, void *addr)
    {
    
    /*-----------------------------------------------------------------------
     * 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(CONFIG_SYS_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(CONFIG_SYS_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
    
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    	cp = (char *) &data;
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    	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(CONFIG_SYS_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(CONFIG_SYS_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
    
    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.8x %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_read32( addr ) != flash_read32( addr )) ||
    			   (flash_read32(addr+4) != flash_read32(addr+4)) );
    
    	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 CONFIG_SYS_HZ != 1000
    
    	if ((ulong)CONFIG_SYS_HZ > 100000)
    		tout *= (ulong)CONFIG_SYS_HZ / 1000;  /* for a big HZ, avoid overflow */
    	else
    		tout = DIV_ROUND_UP(tout * (ulong)CONFIG_SYS_HZ, 1000);
    
    	/* 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);
    
    		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);
    
    static int use_flash_status_poll(flash_info_t *info)
    {
    #ifdef CONFIG_SYS_CFI_FLASH_STATUS_POLL
    	if (info->vendor == CFI_CMDSET_AMD_EXTENDED ||
    	    info->vendor == CFI_CMDSET_AMD_STANDARD)
    		return 1;
    #endif
    	return 0;
    }
    
    static int flash_status_poll(flash_info_t *info, void *src, void *dst,
    			     ulong tout, char *prompt)
    {
    #ifdef CONFIG_SYS_CFI_FLASH_STATUS_POLL
    	ulong start;
    	int ready;
    
    #if CONFIG_SYS_HZ != 1000
    	if ((ulong)CONFIG_SYS_HZ > 100000)
    		tout *= (ulong)CONFIG_SYS_HZ / 1000;  /* for a big HZ, avoid overflow */
    	else
    		tout = DIV_ROUND_UP(tout * (ulong)CONFIG_SYS_HZ, 1000);
    #endif
    
    	/* Wait for command completion */
    
    	start = get_timer(0);
    	while (1) {
    		switch (info->portwidth) {
    		case FLASH_CFI_8BIT:
    			ready = flash_read8(dst) == flash_read8(src);
    			break;
    		case FLASH_CFI_16BIT:
    			ready = flash_read16(dst) == flash_read16(src);
    			break;
    		case FLASH_CFI_32BIT:
    			ready = flash_read32(dst) == flash_read32(src);
    			break;
    		case FLASH_CFI_64BIT:
    			ready = flash_read64(dst) == flash_read64(src);
    			break;
    		default:
    			ready = 0;
    			break;
    		}
    		if (ready)
    			break;
    		if (get_timer(start) > tout) {
    			printf("Flash %s timeout at address %lx data %lx\n",
    			       prompt, (ulong)dst, (ulong)flash_read8(dst));
    			return ERR_TIMOUT;
    		}
    		udelay(1);		/* also triggers watchdog */
    	}
    #endif /* CONFIG_SYS_CFI_FLASH_STATUS_POLL */
    	return ERR_OK;
    }
    
    
    /*-----------------------------------------------------------------------
     */
    
    static void flash_add_byte (flash_info_t * info, cfiword_t * cword, uchar c)
    
    #if defined(__LITTLE_ENDIAN) && !defined(CONFIG_SYS_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(CONFIG_SYS_WRITE_SWAPPED_DATA)
    
    		w = c;
    		w <<= 8;
    		cword->w = (cword->w >> 8) | w;
    #else
    		cword->w = (cword->w << 8) | c;
    
    #if defined(__LITTLE_ENDIAN) && !defined(CONFIG_SYS_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(CONFIG_SYS_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 sector table starting from the previously found sector.
     * Searches forwards or backwards, dependent on the passed address.
    
     */
    static flash_sect_t find_sector (flash_info_t * info, ulong addr)
    {
    
    	static flash_sect_t saved_sector = 0; /* previously found sector */
    
    	static flash_info_t *saved_info = 0; /* previously used flash bank */
    
    	flash_sect_t sector = saved_sector;
    
    
    	if ((info != saved_info) || (sector >= info->sector_count))
    		sector = 0;
    
    
    	while ((info->start[sector] < addr)
    			&& (sector < info->sector_count - 1))
    		sector++;
    	while ((info->start[sector] > addr) && (sector > 0))
    		/*
    		 * also decrements the sector in case of an overshot
    		 * in the first loop
    		 */
    		sector--;
    
    	saved_sector = sector;
    
    }
    
    /*-----------------------------------------------------------------------
     */
    
    static int flash_write_cfiword (flash_info_t * info, ulong dest,
    				cfiword_t cword)
    
    	void *dstaddr = (void *)dest;
    
    	flash_sect_t sect = 0;
    	char sect_found = 0;
    
    	/* 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);
    
    	/* 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:
    
    		sect = find_sector(info, dest);
    		flash_unlock_seq (info, sect);
    		flash_write_cmd (info, sect, info->addr_unlock1, AMD_CMD_WRITE);
    
    #ifdef CONFIG_FLASH_CFI_LEGACY
    	case CFI_CMDSET_AMD_LEGACY:
    		sect = find_sector(info, dest);
    		flash_unlock_seq (info, 0);
    		flash_write_cmd (info, 0, info->addr_unlock1, AMD_CMD_WRITE);
    		sect_found = 1;
    		break;
    #endif
    
    	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 ();
    
    	if (!sect_found)
    		sect = find_sector (info, dest);
    
    
    	if (use_flash_status_poll(info))
    		return flash_status_poll(info, &cword, dstaddr,
    					 info->write_tout, "write");
    	else
    		return flash_full_status_check(info, sect,
    					       info->write_tout, "write");
    
    #ifdef CONFIG_SYS_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 = (void *)dest;
    
    	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, 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;