[U-Boot] [PATCH 2/3] sf: add method to support memory size above 128Mib

Cyrille Pitchen cyrille.pitchen at wedev4u.fr
Wed Dec 20 13:12:37 UTC 2017


Hi Prabhakar,

Le 20/12/2017 à 11:32, Prabhakar Kushwaha a écrit :
> This patch add support of memories with size above 128Mib. It has
> been ported from Linux commit "mtd: spi-nor: add a
> stateless method to support memory size above 128Mib".
> 
> It convert 3byte opcode into 4byte opcode based upon
> OPCODES_4B or Spansion flash. Also the commands are malloc'ed
> at run time based on 3byte or 4byte address opcode requirement.
> 
> Signed-off-by: Prabhakar Kushwaha <prabhakar.kushwaha at nxp.com>
> CC: Cyrille Pitchen <cyrille.pitchen at wedev4u.fr>
> CC: Marek Vasut <marek.vasut at gmail.com>
> CC: Vignesh R <vigneshr at ti.com>
> ---
> depends upon https://patchwork.ozlabs.org/patch/826919/
> 
>  drivers/mtd/spi/sf_internal.h |  28 ++++++++-
>  drivers/mtd/spi/spi_flash.c   | 136 ++++++++++++++++++++++++++++++++++++------
>  include/spi_flash.h           |   2 +
>  3 files changed, 146 insertions(+), 20 deletions(-)
> 
> diff --git a/drivers/mtd/spi/sf_internal.h b/drivers/mtd/spi/sf_internal.h
> index 06dee0a..164b0ea 100644
> --- a/drivers/mtd/spi/sf_internal.h
> +++ b/drivers/mtd/spi/sf_internal.h
> @@ -27,7 +27,8 @@ enum spi_nor_option_flags {
>  };
>  
>  #define SPI_FLASH_3B_ADDR_LEN		3
> -#define SPI_FLASH_CMD_LEN		(1 + SPI_FLASH_3B_ADDR_LEN)
> +#define SPI_FLASH_4B_ADDR_LEN		4
> +#define SPI_FLASH_MAX_ADDR_WIDTH	4
>  #define SPI_FLASH_16MB_BOUN		0x1000000
>  
>  /* CFI Manufacture ID's */
> @@ -57,13 +58,30 @@ enum spi_nor_option_flags {
>  #define CMD_READ_DUAL_IO_FAST		0xbb
>  #define CMD_READ_QUAD_OUTPUT_FAST	0x6b
>  #define CMD_READ_QUAD_IO_FAST		0xeb
> +
> +/* 4B READ commands */
> +#define CMD_READ_ARRAY_SLOW_4B		0x13
> +#define CMD_READ_ARRAY_FAST_4B		0x0c
> +#define CMD_READ_DUAL_OUTPUT_FAST_4B	0x3c
> +#define CMD_READ_DUAL_IO_FAST_4B	0xbc
> +#define CMD_READ_QUAD_OUTPUT_FAST_4B	0x6c
> +#define CMD_READ_QUAD_IO_FAST_4B	0xec
> +
> +/* 4B Write commands */
> +#define CMD_PAGE_PROGRAM_4B		0x12
> +
> +/* 4B Erase commands */
> +#define CMD_ERASE_4K_4B			0x21
> +#define CMD_ERASE_CHIP_4B		0x5c
> +#define CMD_ERASE_64K_4B		0xdc
> +
>  #define CMD_READ_ID			0x9f
>  #define CMD_READ_STATUS			0x05
>  #define CMD_READ_STATUS1		0x35
>  #define CMD_READ_CONFIG			0x35
>  #define CMD_FLAG_STATUS			0x70
> -#define CMD_EN4B				0xB7
> -#define CMD_EX4B				0xE9
> +#define CMD_EN4B			0xB7
> +#define CMD_EX4B			0xE9
>  
>  /* Bank addr access commands */
>  #ifdef CONFIG_SPI_FLASH_BAR
> @@ -133,6 +151,10 @@ struct spi_flash_info {
>  #define RD_DUAL			BIT(5)	/* use Dual Read */
>  #define RD_QUADIO		BIT(6)	/* use Quad IO Read */
>  #define RD_DUALIO		BIT(7)	/* use Dual IO Read */
> +#define OPCODES_4B		BIT(8)	/*
> +					 * Use dedicated 4byte address op codes
> +					 * to support memory size above 128Mib.
> +					 */
>  #define RD_FULL			(RD_QUAD | RD_DUAL | RD_QUADIO | RD_DUALIO)
>  };
>  
> diff --git a/drivers/mtd/spi/spi_flash.c b/drivers/mtd/spi/spi_flash.c
> index 7581622..4d2e58e 100644
> --- a/drivers/mtd/spi/spi_flash.c
> +++ b/drivers/mtd/spi/spi_flash.c
> @@ -22,12 +22,28 @@
>  
>  DECLARE_GLOBAL_DATA_PTR;
>  
> -static void spi_flash_addr(u32 addr, u8 *cmd)
> +static void spi_flash_addr(struct spi_flash *flash, u32 addr, u8 *cmd)
>  {
>  	/* cmd[0] is actual command */
> -	cmd[1] = addr >> 16;
> -	cmd[2] = addr >> 8;
> -	cmd[3] = addr >> 0;
> +
> +	switch (flash->addr_width) {
> +	case SPI_FLASH_3B_ADDR_LEN:
> +		cmd[1] = addr >> 16;
> +		cmd[2] = addr >> 8;
> +		cmd[3] = addr >> 0;
> +		break;
> +
> +	case SPI_FLASH_4B_ADDR_LEN:
> +		cmd[1] = addr >> 24;
> +		cmd[2] = addr >> 16;
> +		cmd[3] = addr >> 8;
> +		cmd[4] = addr >> 0;
> +		break;
> +
> +	default:
> +		debug("SF: Wrong opcode size\n");
> +		break;
> +	}

Not a big deal and mostly a question of personal taste but you can have a
look at what's done by the m25p80 driver from linux (m25p_addr2cmd()
function):

	cmd[1] = addr >> (flash->addr_width * 8 -  8);
	cmd[2] = addr >> (flash->addr_width * 8 - 16);
	cmd[3] = addr >> (flash->addr_width * 8 - 24);
	cmd[4] = addr >> (flash->addr_width * 8 - 32);

>  }
>  
>  static int read_sr(struct spi_flash *flash, u8 *rs)
> @@ -74,6 +90,64 @@ static int write_sr(struct spi_flash *flash, u8 ws)
>  	return 0;
>  }
>  
> +static u8 spi_flash_convert_opcode(u8 opcode, const u8 table[][2], size_t size)
> +{
> +	size_t i;
> +
> +	for (i = 0; i < size; i++)
> +		if (table[i][0] == opcode)
> +			return table[i][1];
> +
> +	/* No conversion found, keep input op code. */
> +	return opcode;
> +}
> +
> +static inline u8 spi_flash_convert_3to4_read(u8 opcode)
> +{
> +	static const u8 spi_flash_3to4_read[][2] = {
> +		{ CMD_READ_ARRAY_SLOW,          CMD_READ_ARRAY_SLOW_4B },
> +		{ CMD_READ_ARRAY_FAST,          CMD_READ_ARRAY_FAST_4B },
> +		{ CMD_READ_DUAL_OUTPUT_FAST,    CMD_READ_DUAL_OUTPUT_FAST_4B },
> +		{ CMD_READ_DUAL_IO_FAST,        CMD_READ_DUAL_IO_FAST_4B },
> +		{ CMD_READ_QUAD_OUTPUT_FAST,    CMD_READ_QUAD_OUTPUT_FAST_4B },
> +		{ CMD_READ_QUAD_IO_FAST,        CMD_READ_QUAD_IO_FAST_4B },
> +
> +	};
> +
> +	return spi_flash_convert_opcode(opcode, spi_flash_3to4_read,
> +				      ARRAY_SIZE(spi_flash_3to4_read));
> +}
> +
> +static inline u8 spi_flash_convert_3to4_program(u8 opcode)
> +{
> +	static const u8 spi_flash_3to4_program[][2] = {
> +		{ CMD_PAGE_PROGRAM, CMD_PAGE_PROGRAM_4B },
> +	};
> +
> +	return spi_flash_convert_opcode(opcode, spi_flash_3to4_program,
> +				      ARRAY_SIZE(spi_flash_3to4_program));
> +}
> +
> +static inline u8 spi_flash_convert_3to4_erase(u8 opcode)
> +{
> +	static const u8 spi_flash_3to4_erase[][2] = {
> +		{ CMD_ERASE_4K,	   CMD_ERASE_4K_4B },
> +		{ CMD_ERASE_CHIP,  CMD_ERASE_CHIP_4B },
> +		{ CMD_ERASE_64K,   CMD_ERASE_64K_4B },
> +	};
> +
> +	return spi_flash_convert_opcode(opcode, spi_flash_3to4_erase,
> +				      ARRAY_SIZE(spi_flash_3to4_erase));
> +}
> +
> +static void spi_flash_set_4byte_opcodes(struct spi_flash *flash,
> +					const struct spi_flash_info *info)
> +{
> +	flash->read_cmd = spi_flash_convert_3to4_read(flash->read_cmd);
> +	flash->write_cmd = spi_flash_convert_3to4_program(flash->write_cmd);
> +	flash->erase_cmd = spi_flash_convert_3to4_erase(flash->erase_cmd);
> +}
> +
>  #if defined(CONFIG_SPI_FLASH_SPANSION) || defined(CONFIG_SPI_FLASH_WINBOND)
>  static int read_cr(struct spi_flash *flash, u8 *rc)
>  {
> @@ -364,7 +438,7 @@ int spi_flash_write_common(struct spi_flash *flash, const u8 *cmd,
>  int spi_flash_cmd_erase_ops(struct spi_flash *flash, u32 offset, size_t len)
>  {
>  	u32 erase_size, erase_addr;
> -	u8 cmd[SPI_FLASH_CMD_LEN];
> +	u8 *cmd, cmdsz;
>  	int ret = -1;
>  
>  	erase_size = flash->erase_size;
> @@ -381,6 +455,13 @@ int spi_flash_cmd_erase_ops(struct spi_flash *flash, u32 offset, size_t len)
>  		}
>  	}
>  
> +	cmdsz = 1 + flash->addr_width;
> +	cmd = calloc(1, cmdsz);

calloc() but no free() ? Though we're not supposed to run u-boot forever,
it's still a memory leak. Why don't you simply keep u8
cmd[SPI_FLASH_CMD_LEN] as earlier, only changing the definition of
SPI_FLASH_CMD_LEN?

-#define SPI_FLASH_CMD_LEN		(1 + SPI_FLASH_3B_ADDR_LEN)
+#define SPI_FLASH_CMD_LEN		(1 + SPI_FLASH_4B_ADDR_LEN)

Of course, you still need cmdsz instead of sizeof(cmd) as you do below,
when calling spi_flash_write_common().

Best regards,

Cyrille

> +	if (!cmd) {
> +		debug("SF: Failed to allocate cmd\n");
> +		return -ENOMEM;
> +	}
> +
>  	cmd[0] = flash->erase_cmd;
>  	while (len) {
>  		erase_addr = offset;
> @@ -394,12 +475,12 @@ int spi_flash_cmd_erase_ops(struct spi_flash *flash, u32 offset, size_t len)
>  		if (ret < 0)
>  			return ret;
>  #endif
> -		spi_flash_addr(erase_addr, cmd);
> +		spi_flash_addr(flash, erase_addr, cmd);
>  
>  		debug("SF: erase %2x %2x %2x %2x (%x)\n", cmd[0], cmd[1],
>  		      cmd[2], cmd[3], erase_addr);
>  
> -		ret = spi_flash_write_common(flash, cmd, sizeof(cmd), NULL, 0);
> +		ret = spi_flash_write_common(flash, cmd, cmdsz, NULL, 0);
>  		if (ret < 0) {
>  			debug("SF: erase failed\n");
>  			break;
> @@ -423,7 +504,7 @@ int spi_flash_cmd_write_ops(struct spi_flash *flash, u32 offset,
>  	unsigned long byte_addr, page_size;
>  	u32 write_addr;
>  	size_t chunk_len, actual;
> -	u8 cmd[SPI_FLASH_CMD_LEN];
> +	u8 *cmd, cmdsz;
>  	int ret = -1;
>  
>  	page_size = flash->page_size;
> @@ -436,6 +517,13 @@ int spi_flash_cmd_write_ops(struct spi_flash *flash, u32 offset,
>  		}
>  	}
>  
> +	cmdsz = 1 + flash->addr_width;
> +	cmd = calloc(1, cmdsz);
> +	if (!cmd) {
> +		debug("SF: Failed to allocate cmd\n");
> +		return -ENOMEM;
> +	}
> +
>  	cmd[0] = flash->write_cmd;
>  	for (actual = 0; actual < len; actual += chunk_len) {
>  		write_addr = offset;
> @@ -456,13 +544,13 @@ int spi_flash_cmd_write_ops(struct spi_flash *flash, u32 offset,
>  			chunk_len = min(chunk_len,
>  					(size_t)spi->max_write_size);
>  
> -		spi_flash_addr(write_addr, cmd);
> +		spi_flash_addr(flash, write_addr, cmd);
>  
>  		debug("SF: 0x%p => cmd = { 0x%02x 0x%02x%02x%02x } chunk_len = %zu\n",
>  		      buf + actual, cmd[0], cmd[1], cmd[2], cmd[3], chunk_len);
>  
> -		ret = spi_flash_write_common(flash, cmd, sizeof(cmd),
> -					buf + actual, chunk_len);
> +		ret = spi_flash_write_common(flash, cmd, cmdsz,
> +					     buf + actual, chunk_len);
>  		if (ret < 0) {
>  			debug("SF: write failed\n");
>  			break;
> @@ -537,7 +625,7 @@ int spi_flash_cmd_read_ops(struct spi_flash *flash, u32 offset,
>  		return 0;
>  	}
>  
> -	cmdsz = SPI_FLASH_CMD_LEN + flash->dummy_byte;
> +	cmdsz = 1 + flash->addr_width + flash->dummy_byte;
>  	cmd = calloc(1, cmdsz);
>  	if (!cmd) {
>  		debug("SF: Failed to allocate cmd\n");
> @@ -565,7 +653,7 @@ int spi_flash_cmd_read_ops(struct spi_flash *flash, u32 offset,
>  		else
>  			read_len = remain_len;
>  
> -		spi_flash_addr(read_addr, cmd);
> +		spi_flash_addr(flash, read_addr, cmd);
>  
>  		ret = spi_flash_read_common(flash, cmd, cmdsz, data, read_len);
>  		if (ret < 0) {
> @@ -1172,10 +1260,6 @@ int spi_flash_scan(struct spi_flash *flash)
>  		flash->flags |= SNOR_F_USE_FSR;
>  #endif
>  
> -	/* disable 4-byte addressing if the device exceeds 16MiB */
> -	if (flash->size > SPI_FLASH_16MB_BOUN)
> -		set_4byte(flash, info, 0);
> -
>  	/* Configure the BAR - discover bank cmds and read current bank */
>  #ifdef CONFIG_SPI_FLASH_BAR
>  	ret = read_bar(flash, info);
> @@ -1211,5 +1295,23 @@ int spi_flash_scan(struct spi_flash *flash)
>  	}
>  #endif
>  
> +	flash->addr_width = SPI_FLASH_3B_ADDR_LEN;
> +
> +	if (flash->size > SPI_FLASH_16MB_BOUN) {
> +		/* enable 4-byte addressing if the device exceeds 16MiB */
> +		flash->addr_width = SPI_FLASH_4B_ADDR_LEN;
> +		if (JEDEC_MFR(info) == SPI_FLASH_CFI_MFR_SPANSION ||
> +		    info->flags & OPCODES_4B)
> +			spi_flash_set_4byte_opcodes(flash, info);
> +		else
> +			set_4byte(flash, info, 1);
> +	}
> +
> +	if (flash->addr_width > SPI_FLASH_MAX_ADDR_WIDTH) {
> +		dev_err(dev, "address width is too large: %u\n",
> +			flash->addr_width);
> +		return -EINVAL;
> +	}
> +
>  	return 0;
>  }
> diff --git a/include/spi_flash.h b/include/spi_flash.h
> index be2fe3f..5a91671 100644
> --- a/include/spi_flash.h
> +++ b/include/spi_flash.h
> @@ -48,6 +48,7 @@ struct spi_slave;
>   * @read_cmd:		Read cmd - Array Fast, Extn read and quad read.
>   * @write_cmd:		Write cmd - page and quad program.
>   * @dummy_byte:		Dummy cycles for read operation.
> + * @addr_width:		number of address bytes
>   * @memory_map:		Address of read-only SPI flash access
>   * @flash_lock:		lock a region of the SPI Flash
>   * @flash_unlock:	unlock a region of the SPI Flash
> @@ -84,6 +85,7 @@ struct spi_flash {
>  	u8 write_cmd;
>  	u8 dummy_byte;
>  
> +	u8 addr_width;
>  	void *memory_map;
>  
>  	int (*flash_lock)(struct spi_flash *flash, u32 ofs, size_t len);
> 



More information about the U-Boot mailing list