[U-Boot] [PATCH] Fix CFI flash driver for 8-bit bus support

Albert ARIBAUD albert.aribaud at free.fr
Sat Apr 2 13:23:17 CEST 2011


Hi Aaron,

Le 02/04/2011 09:17, Aaron Williams a écrit :
> This patch corrects the addresses used when working with Spansion/AMD FLASH chips.
> Addressing for 8 and 16 bits is almost identical except in the 16-bit case the
> LSB of the address is always 0.  The confusion arose because the addresses
> in the datasheet for 16-bit mode are word addresses but this code assumed it was
> byte addresses.
>
> I have only been able to test this on our Octeon boards which use either an 8-bit
> or 16-bit bus.  I have not tested the case where there's an 8-bit part on a 16-bit
> bus.
>
> This patch also adds some delays as suggested by Spansion.
>
> If a part can be both 8 and 16-bits, it forces it to work in 8-bit mode if an
> 8-bit bus is detected.
>
> -Aaron Williams
>
>
> Signed-off-by: Aaron Williams<aaron.williams at caviumnetworks.com>

Comments related to testing and 'signatures' should not be part of the 
commit message and thus should go below the commit message delimiter.

> ---
>   drivers/mtd/cfi_flash.c |   93 ++++++++++++++++++++++++++++++++++-------------
>   include/mtd/cfi_flash.h |   40 ++++++++++----------
>   2 files changed, 87 insertions(+), 46 deletions(-)
>
> diff --git a/drivers/mtd/cfi_flash.c b/drivers/mtd/cfi_flash.c
> index 0909fe7..eab1fbe 100644
> --- a/drivers/mtd/cfi_flash.c
> +++ b/drivers/mtd/cfi_flash.c
> @@ -10,6 +10,9 @@
>    *
>    * Copyright (C) 2006
>    * Tolunay Orkun<listmember at orkun.us>
> + *

There is a space between the asterisk and end of line. You can check if 
your patch is whitespace-clean by applying it locally as if you were 
another u-boot user; 'git apply' will tell you where in the patch there 
are undue whitespace.

> + * Copyright (C) 2011 Cavium Networks, Inc.
> + * Aaron Williams<Aaron.Williams at caviumnetworks.com>
>    *
>    * See file CREDITS for list of people who contributed to this
>    * project.
> @@ -32,7 +35,6 @@
>    */
>
>   /* The DEBUG define must be before common to enable debugging */
> -/* #define DEBUG	*/
>
>   #include<common.h>
>   #include<asm/processor.h>
> @@ -209,9 +211,11 @@ unsigned long flash_sector_size(flash_info_t *info, flash_sect_t sect)
>   static inline void *
>   flash_map (flash_info_t * info, flash_sect_t sect, uint offset)
>   {
> -	unsigned int byte_offset = offset * info->portwidth;
> -

Whitespace (see 'git apply' comment above).

> -	return (void *)(info->start[sect] + byte_offset);
> +	unsigned int byte_offset = offset * info->portwidth / info->chipwidth;
> +	unsigned int addr = (info->start[sect] + byte_offset);
> +	unsigned int mask = 0xffffffff<<  (info->portwidth - 1);
> +	
> +	return (void *)(addr&  mask);
>   }
>
>   static inline void flash_unmap(flash_info_t *info, flash_sect_t sect,
> @@ -397,6 +401,8 @@ void flash_write_cmd (flash_info_t * info, flash_sect_t sect,
>   #endif
>   		flash_write64(cword.ll, addr);
>   		break;
> +	default:
> +		debug ("fwc: Unknown port width %d\n", info->portwidth);
>   	}
>
>   	/* Ensure all the instructions are fully finished */
> @@ -581,6 +587,7 @@ static int flash_status_check (flash_info_t * info, flash_sect_t sector,
>   				prompt, info->start[sector],
>   				flash_read_long (info, sector, 0));
>   			flash_write_cmd (info, sector, 0, info->cmd_reset);
> +			udelay(1);
>   			return ERR_TIMOUT;
>   		}
>   		udelay (1);		/* also triggers watchdog */
> @@ -628,6 +635,7 @@ static int flash_full_status_check (flash_info_t * info, flash_sect_t sector,
>   				puts ("Vpp Low Error.\n");
>   		}
>   		flash_write_cmd (info, sector, 0, info->cmd_reset);
> +		udelay(1);
>   		break;
>   	default:
>   		break;
> @@ -744,12 +752,8 @@ static void flash_add_byte (flash_info_t * info, cfiword_t * cword, uchar c)
>   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++;
> @@ -761,7 +765,6 @@ static flash_sect_t find_sector (flash_info_t * info, ulong addr)
>   		sector--;
>
>   	saved_sector = sector;
> -	saved_info = info;
>   	return sector;
>   }
>
> @@ -825,12 +828,15 @@ static int flash_write_cfiword (flash_info_t * info, ulong dest,
>
>   	switch (info->portwidth) {
>   	case FLASH_CFI_8BIT:
> +		debug("%s: 8-bit 0x%02x\n", __func__, cword.c);
>   		flash_write8(cword.c, dstaddr);
>   		break;
>   	case FLASH_CFI_16BIT:
> +		debug("%s: 16-bit 0x%04x\n", __func__, cword.w);
>   		flash_write16(cword.w, dstaddr);
>   		break;
>   	case FLASH_CFI_32BIT:
> +		debug("%s: 32-bit 0x%08lx\n", __func__, cword.l);
>   		flash_write32(cword.l, dstaddr);
>   		break;
>   	case FLASH_CFI_64BIT:
> @@ -1043,6 +1049,8 @@ int flash_erase (flash_info_t * info, int s_first, int s_last)
>   	int prot;
>   	flash_sect_t sect;
>   	int st;
> +	

Whitespace (see 'git apply' comment above).

> +	debug("%s: erasing sectors %d to %d\n", __func__, s_first, s_last);
>
>   	if (info->flash_id != FLASH_MAN_CFI) {
>   		puts ("Can't erase unknown flash type - aborted\n");
> @@ -1082,6 +1090,7 @@ int flash_erase (flash_info_t * info, int s_first, int s_last)
>   				break;
>   			case CFI_CMDSET_AMD_STANDARD:
>   			case CFI_CMDSET_AMD_EXTENDED:
> +				flash_write_cmd (info, 0, 0, AMD_CMD_RESET);
>   				flash_unlock_seq (info, sect);
>   				flash_write_cmd (info, sect,
>   						info->addr_unlock1,
> @@ -1121,6 +1130,8 @@ int flash_erase (flash_info_t * info, int s_first, int s_last)
>   				rcode = 1;
>   			else if (flash_verbose)
>   				putc ('.');
> +		} else {
> +			debug("\nSector %d is protected.\n", sect);
>   		}
>   	}
>
> @@ -1490,6 +1501,7 @@ void flash_read_user_serial (flash_info_t * info, void *buffer, int offset,
>   	flash_write_cmd (info, 0, 0, FLASH_CMD_READ_ID);
>   	memcpy (dst, src + offset, len);
>   	flash_write_cmd (info, 0, 0, info->cmd_reset);
> +	udelay(1);
>   	flash_unmap(info, 0, FLASH_OFFSET_USER_PROTECTION, src);
>   }
>
> @@ -1505,6 +1517,7 @@ void flash_read_factory_serial (flash_info_t * info, void *buffer, int offset,
>   	flash_write_cmd (info, 0, 0, FLASH_CMD_READ_ID);
>   	memcpy (buffer, src + offset, len);
>   	flash_write_cmd (info, 0, 0, info->cmd_reset);
> +	udelay(1);
>   	flash_unmap(info, 0, FLASH_OFFSET_INTEL_PROTECTION, src);
>   }
>
> @@ -1536,6 +1549,7 @@ static void cfi_reverse_geometry(struct cfi_qry *qry)
>   static void cmdset_intel_read_jedec_ids(flash_info_t *info)
>   {
>   	flash_write_cmd(info, 0, 0, FLASH_CMD_RESET);
> +	udelay(1);
>   	flash_write_cmd(info, 0, 0, FLASH_CMD_READ_ID);
>   	udelay(1000); /* some flash are slow to respond */
>   	info->manufacturer_id = flash_read_uchar (info,
> @@ -1573,8 +1587,7 @@ static void cmdset_amd_read_jedec_ids(flash_info_t *info)
>   	flash_unlock_seq(info, 0);
>   	flash_write_cmd(info, 0, info->addr_unlock1, FLASH_CMD_READ_ID);
>   	udelay(1000); /* some flash are slow to respond */
> -
> -	manuId = flash_read_uchar (info, FLASH_OFFSET_MANUFACTURER_ID);
> +	manuId = flash_read_uchar(info, FLASH_OFFSET_MANUFACTURER_ID);
>   	/* JEDEC JEP106Z specifies ID codes up to bank 7 */
>   	while (manuId == FLASH_CONTINUATION_CODE&&  bankId<  0x800) {
>   		bankId += 0x100;
> @@ -1604,6 +1617,7 @@ static void cmdset_amd_read_jedec_ids(flash_info_t *info)
>   		break;
>   	}
>   	flash_write_cmd(info, 0, 0, AMD_CMD_RESET);
> +	udelay(1);
>   }
>
>   static int cmdset_amd_init(flash_info_t *info, struct cfi_qry *qry)
> @@ -1719,7 +1733,7 @@ static void flash_read_cfi (flash_info_t *info, void *buf,
>   	unsigned int i;
>
>   	for (i = 0; i<  len; i++)
> -		p[i] = flash_read_uchar(info, start + i);
> +		p[i] = flash_read_uchar(info, start + (i * 2));

This "2" seems a bit magical to me -- apparently the semantics of 
flash_read_uchar seem to change with your patch If they do, please add 
comments before the definition of flash_read_uchar() to make the meaning 
of its arguments completely clear.

Besides, does this still work with pure 8 bit devices, where the CFI 
area is byte-spaced?

>   }
>
>   void __flash_cmd_reset(flash_info_t *info)
> @@ -1730,6 +1744,7 @@ void __flash_cmd_reset(flash_info_t *info)
>   	 * that AMD flash roms ignore the Intel command.
>   	 */
>   	flash_write_cmd(info, 0, 0, AMD_CMD_RESET);
> +	udelay(1);
>   	flash_write_cmd(info, 0, 0, FLASH_CMD_RESET);
>   }
>   void flash_cmd_reset(flash_info_t *info)
> @@ -1739,21 +1754,38 @@ static int __flash_detect_cfi (flash_info_t * info, struct cfi_qry *qry)
>   {
>   	int cfi_offset;
>
> -	/* Issue FLASH reset command */
> -	flash_cmd_reset(info);
> -
>   	for (cfi_offset=0;
>   	     cfi_offset<  sizeof(flash_offset_cfi) / sizeof(uint);
>   	     cfi_offset++) {
> +		/* Issue FLASH reset command */
> +		flash_cmd_reset(info);
>   		flash_write_cmd (info, 0, flash_offset_cfi[cfi_offset],
>   				 FLASH_CMD_CFI);
>   		if (flash_isequal (info, 0, FLASH_OFFSET_CFI_RESP, 'Q')
> -		&&  flash_isequal (info, 0, FLASH_OFFSET_CFI_RESP + 1, 'R')
> -		&&  flash_isequal (info, 0, FLASH_OFFSET_CFI_RESP + 2, 'Y')) {
> +		&&  flash_isequal (info, 0, FLASH_OFFSET_CFI_RESP + 2, 'R')
> +		&&  flash_isequal (info, 0, FLASH_OFFSET_CFI_RESP + 4, 'Y')) {

Here too, the change requires that the semantics of flash_isequal be 
explicited in a comment before the definition of flash_isequal() to make 
the meaning of its arguments completely clear.

>   			flash_read_cfi(info, qry, FLASH_OFFSET_CFI_RESP,
>   					sizeof(struct cfi_qry));
> +#ifdef CONFIG_SYS_FLASH_INTERFACE_WIDTH
> +			info->interface = CONFIG_SYS_FLASH_INTERFACE_WIDTH;
> +#else
>   			info->interface	= le16_to_cpu(qry->interface_desc);
> -
> +			/* Some flash chips can support multiple bus widths.
> +			 * In this case, override the interface width and
> +			 * limit it to the port width.
> +			 */
> +			if ((info->interface == FLASH_CFI_X8X16)&&
> +			    (info->portwidth == FLASH_CFI_8BIT)) {
> +				debug("Overriding 16-bit interface width to "
> +				      " 8-bit port width.\n");
> +				info->interface = FLASH_CFI_X8;
> +			} else if ((info->interface == FLASH_CFI_X16X32)&&
> +				 (info->portwidth == FLASH_CFI_16BIT)) {
> +				debug("Overriding 16-bit interface width to "
> +				      " 16-bit port width.\n");
> +				info->interface = FLASH_CFI_X16;
> +			}
> +#endif
>   			info->cfi_offset = flash_offset_cfi[cfi_offset];
>   			debug ("device interface is %d\n",
>   			       info->interface);
> @@ -1764,8 +1796,8 @@ static int __flash_detect_cfi (flash_info_t * info, struct cfi_qry *qry)
>   			       info->chipwidth<<  CFI_FLASH_SHIFT_WIDTH);
>
>   			/* calculate command offsets as in the Linux driver */
> -			info->addr_unlock1 = 0x555;
> -			info->addr_unlock2 = 0x2aa;
> +			info->addr_unlock1 = 0xaaa;
> +			info->addr_unlock2 = 0x555;
>
>   			/*
>   			 * modify the unlock address if we are
> @@ -1799,8 +1831,11 @@ static int flash_detect_cfi (flash_info_t * info, struct cfi_qry *qry)
>   		for (info->chipwidth = FLASH_CFI_BY8;
>   		     info->chipwidth<= info->portwidth;
>   		     info->chipwidth<<= 1)
> -			if (__flash_detect_cfi(info, qry))
> +			if (__flash_detect_cfi(info, qry)) {
> +				debug("Found CFI flash, portwidth %d, chipwidth %d\n",
> +				      info->portwidth, info->chipwidth);
>   				return 1;
> +			}
>   	}
>   	debug ("not found\n");
>   	return 0;
> @@ -1819,7 +1854,7 @@ static void flash_fixup_amd(flash_info_t *info, struct cfi_qry *qry)
>   			/* CFI<  1.1, try to guess from device id */
>   			if ((info->device_id&  0x80) != 0)
>   				cfi_reverse_geometry(qry);
> -		} else if (flash_read_uchar(info, info->ext_addr + 0xf) == 3) {
> +		} else if (flash_read_uchar(info, info->ext_addr + 0x1e) == 3) {

Please add comments to explain what the '0x1e' actually means -- that's 
not a fault of your patch, btw, as the 0xf in the original code could 
already have used some commenting; since you're at it, you might as well 
improve on code understandability as well. :)

>   			/* CFI>= 1.1, deduct from top/bottom flag */
>   			/* note: ext_addr is valid since cfi_version>  0 */
>   			cfi_reverse_geometry(qry);
> @@ -1891,14 +1926,15 @@ ulong flash_get_size (phys_addr_t base, int banknum)
>
>   	if (flash_detect_cfi (info,&qry)) {
>   		info->vendor = le16_to_cpu(qry.p_id);
> -		info->ext_addr = le16_to_cpu(qry.p_adr);
> +		info->ext_addr = le16_to_cpu(qry.p_adr) * 2;
> +		debug("extended address is 0x%x\n", info->ext_addr);
>   		num_erase_regions = qry.num_erase_regions;
>
>   		if (info->ext_addr) {
>   			info->cfi_version = (ushort) flash_read_uchar (info,
> -						info->ext_addr + 3)<<  8;
> +						info->ext_addr + 6)<<  8;
>   			info->cfi_version |= (ushort) flash_read_uchar (info,
> -						info->ext_addr + 4);
> +						info->ext_addr + 8);
>   		}

Same remark re: "magic" for the changes above: tell readers what units 
(bytes, words, something else) these offsets are expressed in.

>   #ifdef DEBUG
> @@ -1945,13 +1981,16 @@ ulong flash_get_size (phys_addr_t base, int banknum)
>   		debug ("device id is 0x%x\n", info->device_id);
>   		debug ("device id2 is 0x%x\n", info->device_id2);
>   		debug ("cfi version is 0x%04x\n", info->cfi_version);
> -
> +		debug ("port width: %d, chipwidth: %d, interface: %d\n",
> +		       info->portwidth, info->chipwidth, info->interface);
>   		size_ratio = info->portwidth / info->chipwidth;
> +#if 0
>   		/* if the chip is x8/x16 reduce the ratio by half */
>   		if ((info->interface == FLASH_CFI_X8X16)
>   		&&  (info->chipwidth == FLASH_CFI_BY8)) {
>   			size_ratio>>= 1;
>   		}
> +#endif

NAK on this one. If you want to remove some code, remove it. If you 
still want it, dont comment it out.

>   		debug ("size_ratio %d port %d bits chip %d bits\n",
>   		       size_ratio, info->portwidth<<  CFI_FLASH_SHIFT_WIDTH,
>   		       info->chipwidth<<  CFI_FLASH_SHIFT_WIDTH);
> @@ -2023,6 +2062,8 @@ ulong flash_get_size (phys_addr_t base, int banknum)
>   				sect_cnt++;
>   			}
>   		}
> +		debug ("port width: %d, chipwidth: %d, interface: %d\n",
> +		       info->portwidth, info->chipwidth, info->interface);
>
>   		info->sector_count = sect_cnt;
>   		info->buffer_size = 1<<  le16_to_cpu(qry.max_buf_write_size);
> diff --git a/include/mtd/cfi_flash.h b/include/mtd/cfi_flash.h
> index 3245b44..edcf9be 100644
> --- a/include/mtd/cfi_flash.h
> +++ b/include/mtd/cfi_flash.h

Overall remark on the .h file: please explain what unit the values are 
expressed in (byte offets, word offsets, width-independent, whatever.

>

Apart from this, I am happy to report that this code works well with my 
ED Mini V2, allowing me to remove the CONFIG_FLASH_CFI_LEGACY config 
option and use the standard driver, so:

Tested-by: Albert ARIBAUD <albert.aribaud at free.fr>

Thanks a lot, Aaron!

Amicalement,
-- 
Albert.


More information about the U-Boot mailing list