diff options
Diffstat (limited to 'drivers/mtd')
| -rw-r--r-- | drivers/mtd/cfi_flash.c | 7 | ||||
| -rw-r--r-- | drivers/mtd/nand/Makefile | 1 | ||||
| -rw-r--r-- | drivers/mtd/nand/fsl_ifc_spl.c | 258 | ||||
| -rw-r--r-- | drivers/mtd/spi/Makefile | 1 | ||||
| -rw-r--r-- | drivers/mtd/spi/gigadevice.c | 81 | ||||
| -rw-r--r-- | drivers/mtd/spi/spansion.c | 9 | ||||
| -rw-r--r-- | drivers/mtd/spi/spi_flash.c | 379 | ||||
| -rw-r--r-- | drivers/mtd/spi/spi_flash_internal.h | 40 | ||||
| -rw-r--r-- | drivers/mtd/spi/stmicro.c | 28 | ||||
| -rw-r--r-- | drivers/mtd/spi/winbond.c | 24 | 
10 files changed, 689 insertions, 139 deletions
diff --git a/drivers/mtd/cfi_flash.c b/drivers/mtd/cfi_flash.c index 25f875202..25a571075 100644 --- a/drivers/mtd/cfi_flash.c +++ b/drivers/mtd/cfi_flash.c @@ -1797,7 +1797,7 @@ static int flash_detect_legacy(phys_addr_t base, int banknum)  			};  			int i; -			for (i = 0; i < sizeof(modes) / sizeof(modes[0]); i++) { +			for (i = 0; i < ARRAY_SIZE(modes); i++) {  				info->vendor = modes[i];  				info->start[0] =  					(ulong)map_physmem(base, @@ -1883,8 +1883,7 @@ static int __flash_detect_cfi (flash_info_t * info, struct cfi_qry *qry)  	/* Issue FLASH reset command */  	flash_cmd_reset(info); -	for (cfi_offset=0; -	     cfi_offset < sizeof(flash_offset_cfi) / sizeof(uint); +	for (cfi_offset = 0; cfi_offset < ARRAY_SIZE(flash_offset_cfi);  	     cfi_offset++) {  		flash_write_cmd (info, 0, flash_offset_cfi[cfi_offset],  				 FLASH_CMD_CFI); @@ -2336,7 +2335,7 @@ void flash_protect_default(void)  #endif  #if defined(CONFIG_SYS_FLASH_AUTOPROTECT_LIST) -	for (i = 0; i < (sizeof(apl) / sizeof(struct apl_s)); i++) { +	for (i = 0; i < ARRAY_SIZE(apl); i++) {  		debug("autoprotecting from %08lx to %08lx\n",  		      apl[i].start, apl[i].start + apl[i].size - 1);  		flash_protect(FLAG_PROTECT_SET, diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile index 882170491..bb81e8411 100644 --- a/drivers/mtd/nand/Makefile +++ b/drivers/mtd/nand/Makefile @@ -83,6 +83,7 @@ COBJS-$(CONFIG_NAND_DOCG4) += docg4.o  else  # minimal SPL drivers  COBJS-$(CONFIG_NAND_FSL_ELBC) += fsl_elbc_spl.o +COBJS-$(CONFIG_NAND_FSL_IFC) += fsl_ifc_spl.o  COBJS-$(CONFIG_NAND_MXC) += mxc_nand_spl.o  endif # drivers diff --git a/drivers/mtd/nand/fsl_ifc_spl.c b/drivers/mtd/nand/fsl_ifc_spl.c new file mode 100644 index 000000000..8537c4c6f --- /dev/null +++ b/drivers/mtd/nand/fsl_ifc_spl.c @@ -0,0 +1,258 @@ +/* + * NAND boot for Freescale Integrated Flash Controller, NAND FCM + * + * Copyright 2011 Freescale Semiconductor, Inc. + * Author: Dipen Dudhat <dipen.dudhat@freescale.com> + * + * 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 + */ + +#include <common.h> +#include <asm/io.h> +#include <asm/fsl_ifc.h> +#include <linux/mtd/nand.h> + +static inline int is_blank(uchar *addr, int page_size) +{ +	int i; + +	for (i = 0; i < page_size; i++) { +		if (__raw_readb(&addr[i]) != 0xff) +			return 0; +	} + +	/* +	 * For the SPL, don't worry about uncorrectable errors +	 * where the main area is all FFs but shouldn't be. +	 */ +	return 1; +} + +/* returns nonzero if entire page is blank */ +static inline int check_read_ecc(uchar *buf, u32 *eccstat, +				 unsigned int bufnum, int page_size) +{ +	u32 reg = eccstat[bufnum / 4]; +	int errors = (reg >> ((3 - bufnum % 4) * 8)) & 0xf; + +	if (errors == 0xf) { /* uncorrectable */ +		/* Blank pages fail hw ECC checks */ +		if (is_blank(buf, page_size)) +			return 1; + +		puts("ecc error\n"); +		for (;;) +			; +	} + +	return 0; +} + +static inline void nand_wait(uchar *buf, int bufnum, int page_size) +{ +	struct fsl_ifc *ifc = IFC_BASE_ADDR; +	u32 status; +	u32 eccstat[4]; +	int bufperpage = page_size / 512; +	int bufnum_end, i; + +	bufnum *= bufperpage; +	bufnum_end = bufnum + bufperpage - 1; + +	do { +		status = in_be32(&ifc->ifc_nand.nand_evter_stat); +	} while (!(status & IFC_NAND_EVTER_STAT_OPC)); + +	if (status & IFC_NAND_EVTER_STAT_FTOER) { +		puts("flash time out error\n"); +		for (;;) +			; +	} + +	for (i = bufnum / 4; i <= bufnum_end / 4; i++) +		eccstat[i] = in_be32(&ifc->ifc_nand.nand_eccstat[i]); + +	for (i = bufnum; i <= bufnum_end; i++) { +		if (check_read_ecc(buf, eccstat, i, page_size)) +			break; +	} + +	out_be32(&ifc->ifc_nand.nand_evter_stat, status); +} + +static inline int bad_block(uchar *marker, int port_size) +{ +	if (port_size == 8) +		return __raw_readb(marker) != 0xff; +	else +		return __raw_readw((u16 *)marker) != 0xffff; +} + +static void nand_load(unsigned int offs, int uboot_size, uchar *dst) +{ +	struct fsl_ifc *ifc = IFC_BASE_ADDR; +	uchar *buf = (uchar *)CONFIG_SYS_NAND_BASE; +	int page_size; +	int port_size; +	int pages_per_blk; +	int blk_size; +	int bad_marker = 0; +	int bufnum_mask, bufnum; + +	int csor, cspr; +	int pos = 0; +	int j = 0; + +	int sram_addr; +	int pg_no; + +	/* Get NAND Flash configuration */ +	csor = CONFIG_SYS_NAND_CSOR; +	cspr = CONFIG_SYS_NAND_CSPR; + +	port_size = (cspr & CSPR_PORT_SIZE_16) ? 16 : 8; + +	if (csor & CSOR_NAND_PGS_4K) { +		page_size = 4096; +		bufnum_mask = 0x1; +	} else if (csor & CSOR_NAND_PGS_2K) { +		page_size = 2048; +		bufnum_mask = 0x3; +	} else { +		page_size = 512; +		bufnum_mask = 0xf; + +		if (port_size == 8) +			bad_marker = 5; +	} + +	pages_per_blk = +		32 << ((csor & CSOR_NAND_PB_MASK) >> CSOR_NAND_PB_SHIFT); + +	blk_size = pages_per_blk * page_size; + +	/* Open Full SRAM mapping for spare are access */ +	out_be32(&ifc->ifc_nand.ncfgr, 0x0); + +	/* Clear Boot events */ +	out_be32(&ifc->ifc_nand.nand_evter_stat, 0xffffffff); + +	/* Program FIR/FCR for Large/Small page */ +	if (page_size > 512) { +		out_be32(&ifc->ifc_nand.nand_fir0, +			 (IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) | +			 (IFC_FIR_OP_CA0 << IFC_NAND_FIR0_OP1_SHIFT) | +			 (IFC_FIR_OP_RA0 << IFC_NAND_FIR0_OP2_SHIFT) | +			 (IFC_FIR_OP_CMD1 << IFC_NAND_FIR0_OP3_SHIFT) | +			 (IFC_FIR_OP_BTRD << IFC_NAND_FIR0_OP4_SHIFT)); +		out_be32(&ifc->ifc_nand.nand_fir1, 0x0); + +		out_be32(&ifc->ifc_nand.nand_fcr0, +			 (NAND_CMD_READ0 << IFC_NAND_FCR0_CMD0_SHIFT) | +			 (NAND_CMD_READSTART << IFC_NAND_FCR0_CMD1_SHIFT)); +	} else { +		out_be32(&ifc->ifc_nand.nand_fir0, +			 (IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) | +			 (IFC_FIR_OP_CA0 << IFC_NAND_FIR0_OP1_SHIFT) | +			 (IFC_FIR_OP_RA0  << IFC_NAND_FIR0_OP2_SHIFT) | +			 (IFC_FIR_OP_BTRD << IFC_NAND_FIR0_OP3_SHIFT)); +		out_be32(&ifc->ifc_nand.nand_fir1, 0x0); + +		out_be32(&ifc->ifc_nand.nand_fcr0, +			 NAND_CMD_READ0 << IFC_NAND_FCR0_CMD0_SHIFT); +	} + +	/* Program FBCR = 0 for full page read */ +	out_be32(&ifc->ifc_nand.nand_fbcr, 0); + +	/* Read and copy u-boot on SDRAM from NAND device, In parallel +	 * check for Bad block if found skip it and read continue to +	 * next Block +	 */ +	while (pos < uboot_size) { +		int i = 0; +		do { +			pg_no = offs / page_size; +			bufnum = pg_no & bufnum_mask; +			sram_addr = bufnum * page_size * 2; + +			out_be32(&ifc->ifc_nand.row0, pg_no); +			out_be32(&ifc->ifc_nand.col0, 0); +			/* start read */ +			out_be32(&ifc->ifc_nand.nandseq_strt, +				 IFC_NAND_SEQ_STRT_FIR_STRT); + +			/* wait for read to complete */ +			nand_wait(&buf[sram_addr], bufnum, page_size); + +			/* +			 * If either of the first two pages are marked bad, +			 * continue to the next block. +			 */ +			if (i++ < 2 && +			    bad_block(&buf[sram_addr + page_size + bad_marker], +				      port_size)) { +				puts("skipping\n"); +				offs = (offs + blk_size) & ~(blk_size - 1); +				pos &= ~(blk_size - 1); +				break; +			} + +			for (j = 0; j < page_size; j++) +				dst[pos + j] = __raw_readb(&buf[sram_addr + j]); + +			pos += page_size; +			offs += page_size; +		} while ((offs & (blk_size - 1)) && (pos < uboot_size)); +	} +} + +/* + * Main entrypoint for NAND Boot. It's necessary that SDRAM is already + * configured and available since this code loads the main U-boot image + * from NAND into SDRAM and starts from there. + */ +void nand_boot(void) +{ +	__attribute__((noreturn)) void (*uboot)(void); +	/* +	 * Load U-Boot image from NAND into RAM +	 */ +	nand_load(CONFIG_SYS_NAND_U_BOOT_OFFS, CONFIG_SYS_NAND_U_BOOT_SIZE, +		  (uchar *)CONFIG_SYS_NAND_U_BOOT_DST); + +#ifdef CONFIG_NAND_ENV_DST +	nand_load(CONFIG_ENV_OFFSET, CONFIG_ENV_SIZE, +		  (uchar *)CONFIG_NAND_ENV_DST); + +#ifdef CONFIG_ENV_OFFSET_REDUND +	nand_load(CONFIG_ENV_OFFSET_REDUND, CONFIG_ENV_SIZE, +		  (uchar *)CONFIG_NAND_ENV_DST + CONFIG_ENV_SIZE); +#endif +#endif +	/* +	 * Jump to U-Boot image +	 */ +#ifdef CONFIG_SPL_FLUSH_IMAGE +	/* +	 * Clean d-cache and invalidate i-cache, to +	 * make sure that no stale data is executed. +	 */ +	flush_cache(CONFIG_SYS_NAND_U_BOOT_DST, CONFIG_SYS_NAND_U_BOOT_SIZE); +#endif +	uboot = (void *)CONFIG_SYS_NAND_U_BOOT_START; +	uboot(); +} diff --git a/drivers/mtd/spi/Makefile b/drivers/mtd/spi/Makefile index 90f83924e..ecbb2108f 100644 --- a/drivers/mtd/spi/Makefile +++ b/drivers/mtd/spi/Makefile @@ -32,6 +32,7 @@ endif  COBJS-$(CONFIG_SPI_FLASH)	+= spi_flash.o  COBJS-$(CONFIG_SPI_FLASH_ATMEL)	+= atmel.o  COBJS-$(CONFIG_SPI_FLASH_EON)	+= eon.o +COBJS-$(CONFIG_SPI_FLASH_GIGADEVICE)	+= gigadevice.o  COBJS-$(CONFIG_SPI_FLASH_MACRONIX)	+= macronix.o  COBJS-$(CONFIG_SPI_FLASH_SPANSION)	+= spansion.o  COBJS-$(CONFIG_SPI_FLASH_SST)	+= sst.o diff --git a/drivers/mtd/spi/gigadevice.c b/drivers/mtd/spi/gigadevice.c new file mode 100644 index 000000000..b5e1ebedf --- /dev/null +++ b/drivers/mtd/spi/gigadevice.c @@ -0,0 +1,81 @@ +/* + * Gigadevice SPI flash driver + * Copyright 2013, Samsung Electronics Co., Ltd. + * Author: Banajit Goswami <banajit.g@samsung.com> + * + * 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 + */ + +#include <common.h> +#include <malloc.h> +#include <spi_flash.h> + +#include "spi_flash_internal.h" + +struct gigadevice_spi_flash_params { +	uint16_t	id; +	uint16_t	nr_blocks; +	const char	*name; +}; + +static const struct gigadevice_spi_flash_params gigadevice_spi_flash_table[] = { +	{ +		.id			= 0x6016, +		.nr_blocks		= 64, +		.name			= "GD25LQ", +	}, +	{ +		.id			= 0x4017, +		.nr_blocks		= 128, +		.name			= "GD25Q64B", +	}, +}; + +struct spi_flash *spi_flash_probe_gigadevice(struct spi_slave *spi, u8 *idcode) +{ +	const struct gigadevice_spi_flash_params *params; +	struct spi_flash *flash; +	unsigned int i; + +	for (i = 0; i < ARRAY_SIZE(gigadevice_spi_flash_table); i++) { +		params = &gigadevice_spi_flash_table[i]; +		if (params->id == ((idcode[1] << 8) | idcode[2])) +			break; +	} + +	if (i == ARRAY_SIZE(gigadevice_spi_flash_table)) { +		debug("SF: Unsupported Gigadevice ID %02x%02x\n", +				idcode[1], idcode[2]); +		return NULL; +	} + +	flash = spi_flash_alloc_base(spi, params->name); +	if (!flash) { +		debug("SF: Failed to allocate memory\n"); +		return NULL; +	} +	/* page_size */ +	flash->page_size = 256; +	/* sector_size = page_size * pages_per_sector */ +	flash->sector_size = flash->page_size * 16; +	/* size = sector_size * sector_per_block * number of blocks */ +	flash->size = flash->sector_size * 16 * params->nr_blocks; + +	return flash; +} diff --git a/drivers/mtd/spi/spansion.c b/drivers/mtd/spi/spansion.c index b3ef90f13..3ec2151b3 100644 --- a/drivers/mtd/spi/spansion.c +++ b/drivers/mtd/spi/spansion.c @@ -101,7 +101,7 @@ static const struct spansion_spi_flash_params spansion_spi_flash_table[] = {  		.idcode2 = 0x4d01,  		.pages_per_sector = 256,  		.nr_sectors = 256, -		.name = "S25FL129P_64K/S25FL128S", +		.name = "S25FL129P_64K/S25FL128S_64K",  	},  	{  		.idcode1 = 0x0219, @@ -110,6 +110,13 @@ static const struct spansion_spi_flash_params spansion_spi_flash_table[] = {  		.nr_sectors = 512,  		.name = "S25FL256S_64K",  	}, +	{ +		.idcode1 = 0x0220, +		.idcode2 = 0x4d01, +		.pages_per_sector = 256, +		.nr_sectors = 1024, +		.name = "S25FL512S_64K", +	},  };  struct spi_flash *spi_flash_probe_spansion(struct spi_slave *spi, u8 *idcode) diff --git a/drivers/mtd/spi/spi_flash.c b/drivers/mtd/spi/spi_flash.c index 0e38f5948..6a6fe37e0 100644 --- a/drivers/mtd/spi/spi_flash.c +++ b/drivers/mtd/spi/spi_flash.c @@ -68,17 +68,60 @@ int spi_flash_cmd_write(struct spi_slave *spi, const u8 *cmd, size_t cmd_len,  	return spi_flash_read_write(spi, cmd, cmd_len, data, NULL, data_len);  } -int spi_flash_cmd_write_multi(struct spi_flash *flash, u32 offset, -		size_t len, const void *buf) +int spi_flash_cmd_wait_ready(struct spi_flash *flash, unsigned long timeout)  { -	unsigned long page_addr, byte_addr, page_size; -	size_t chunk_len, actual; +	struct spi_slave *spi = flash->spi; +	unsigned long timebase;  	int ret; -	u8 cmd[4]; +	u8 status; +	u8 check_status = 0x0; +	u8 poll_bit = STATUS_WIP; +	u8 cmd = flash->poll_cmd; -	page_size = flash->page_size; -	page_addr = offset / page_size; -	byte_addr = offset % page_size; +	if (cmd == CMD_FLAG_STATUS) { +		poll_bit = STATUS_PEC; +		check_status = poll_bit; +	} + +	ret = spi_xfer(spi, 8, &cmd, NULL, SPI_XFER_BEGIN); +	if (ret) { +		debug("SF: fail to read %s status register\n", +			cmd == CMD_READ_STATUS ? "read" : "flag"); +		return ret; +	} + +	timebase = get_timer(0); +	do { +		WATCHDOG_RESET(); + +		ret = spi_xfer(spi, 8, NULL, &status, 0); +		if (ret) +			return -1; + +		if ((status & poll_bit) == check_status) +			break; + +	} while (get_timer(timebase) < timeout); + +	spi_xfer(spi, 0, NULL, NULL, SPI_XFER_END); + +	if ((status & poll_bit) == check_status) +		return 0; + +	/* Timed out */ +	debug("SF: time out!\n"); +	return -1; +} + +int spi_flash_write_common(struct spi_flash *flash, const u8 *cmd, +		size_t cmd_len, const void *buf, size_t buf_len) +{ +	struct spi_slave *spi = flash->spi; +	unsigned long timeout = SPI_FLASH_PROG_TIMEOUT; +	int ret; + +	if (buf == NULL) +		timeout = SPI_FLASH_PAGE_ERASE_TIMEOUT;  	ret = spi_claim_bus(flash->spi);  	if (ret) { @@ -86,45 +129,122 @@ int spi_flash_cmd_write_multi(struct spi_flash *flash, u32 offset,  		return ret;  	} +	ret = spi_flash_cmd_write_enable(flash); +	if (ret < 0) { +		debug("SF: enabling write failed\n"); +		return ret; +	} + +	ret = spi_flash_cmd_write(spi, cmd, cmd_len, buf, buf_len); +	if (ret < 0) { +		debug("SF: write cmd failed\n"); +		return ret; +	} + +	ret = spi_flash_cmd_wait_ready(flash, timeout); +	if (ret < 0) { +		debug("SF: write %s timed out\n", +			timeout == SPI_FLASH_PROG_TIMEOUT ? +			"program" : "page erase"); +		return ret; +	} + +	spi_release_bus(spi); + +	return ret; +} + +int spi_flash_cmd_erase(struct spi_flash *flash, u32 offset, size_t len) +{ +	u32 erase_size; +	u8 cmd[4]; +	int ret = -1; + +	erase_size = flash->sector_size; +	if (offset % erase_size || len % erase_size) { +		debug("SF: Erase offset/length not multiple of erase size\n"); +		return -1; +	} + +	if (erase_size == 4096) +		cmd[0] = CMD_ERASE_4K; +	else +		cmd[0] = CMD_ERASE_64K; + +	while (len) { +#ifdef CONFIG_SPI_FLASH_BAR +		u8 bank_sel; + +		bank_sel = offset / SPI_FLASH_16MB_BOUN; + +		ret = spi_flash_cmd_bankaddr_write(flash, bank_sel); +		if (ret) { +			debug("SF: fail to set bank%d\n", bank_sel); +			return ret; +		} +#endif +		spi_flash_addr(offset, cmd); + +		debug("SF: erase %2x %2x %2x %2x (%x)\n", cmd[0], cmd[1], +		      cmd[2], cmd[3], offset); + +		ret = spi_flash_write_common(flash, cmd, sizeof(cmd), NULL, 0); +		if (ret < 0) { +			debug("SF: erase failed\n"); +			break; +		} + +		offset += erase_size; +		len -= erase_size; +	} + +	return ret; +} + +int spi_flash_cmd_write_multi(struct spi_flash *flash, u32 offset, +		size_t len, const void *buf) +{ +	unsigned long byte_addr, page_size; +	size_t chunk_len, actual; +	u8 cmd[4]; +	int ret = -1; + +	page_size = flash->page_size; +  	cmd[0] = CMD_PAGE_PROGRAM;  	for (actual = 0; actual < len; actual += chunk_len) { +#ifdef CONFIG_SPI_FLASH_BAR +		u8 bank_sel; + +		bank_sel = offset / SPI_FLASH_16MB_BOUN; + +		ret = spi_flash_cmd_bankaddr_write(flash, bank_sel); +		if (ret) { +			debug("SF: fail to set bank%d\n", bank_sel); +			return ret; +		} +#endif +		byte_addr = offset % page_size;  		chunk_len = min(len - actual, page_size - byte_addr);  		if (flash->spi->max_write_size)  			chunk_len = min(chunk_len, flash->spi->max_write_size); -		cmd[1] = page_addr >> 8; -		cmd[2] = page_addr; -		cmd[3] = byte_addr; +		spi_flash_addr(offset, cmd);  		debug("PP: 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_cmd_write_enable(flash); -		if (ret < 0) { -			debug("SF: enabling write failed\n"); -			break; -		} - -		ret = spi_flash_cmd_write(flash->spi, cmd, 4, -					  buf + actual, chunk_len); +		ret = spi_flash_write_common(flash, cmd, sizeof(cmd), +					buf + actual, chunk_len);  		if (ret < 0) {  			debug("SF: write failed\n");  			break;  		} -		ret = spi_flash_cmd_wait_ready(flash, SPI_FLASH_PROG_TIMEOUT); -		if (ret) -			break; - -		byte_addr += chunk_len; -		if (byte_addr == page_size) { -			page_addr++; -			byte_addr = 0; -		} +		offset += chunk_len;  	} -	spi_release_bus(flash->spi);  	return ret;  } @@ -134,8 +254,18 @@ int spi_flash_read_common(struct spi_flash *flash, const u8 *cmd,  	struct spi_slave *spi = flash->spi;  	int ret; -	spi_claim_bus(spi); +	ret = spi_claim_bus(flash->spi); +	if (ret) { +		debug("SF: unable to claim SPI bus\n"); +		return ret; +	} +  	ret = spi_flash_cmd_read(spi, cmd, cmd_len, data, data_len); +	if (ret < 0) { +		debug("SF: read cmd failed\n"); +		return ret; +	} +  	spi_release_bus(spi);  	return ret; @@ -144,7 +274,9 @@ int spi_flash_read_common(struct spi_flash *flash, const u8 *cmd,  int spi_flash_cmd_read_fast(struct spi_flash *flash, u32 offset,  		size_t len, void *data)  { -	u8 cmd[5]; +	u8 cmd[5], bank_sel = 0; +	u32 remain_len, read_len; +	int ret = -1;  	/* Handle memory-mapped SPI */  	if (flash->memory_map) { @@ -153,130 +285,114 @@ int spi_flash_cmd_read_fast(struct spi_flash *flash, u32 offset,  	}  	cmd[0] = CMD_READ_ARRAY_FAST; -	spi_flash_addr(offset, cmd);  	cmd[4] = 0x00; -	return spi_flash_read_common(flash, cmd, sizeof(cmd), data, len); -} - -int spi_flash_cmd_poll_bit(struct spi_flash *flash, unsigned long timeout, -			   u8 cmd, u8 poll_bit) -{ -	struct spi_slave *spi = flash->spi; -	unsigned long timebase; -	int ret; -	u8 status; - -	ret = spi_xfer(spi, 8, &cmd, NULL, SPI_XFER_BEGIN); -	if (ret) { -		debug("SF: Failed to send command %02x: %d\n", cmd, ret); -		return ret; -	} +	while (len) { +#ifdef CONFIG_SPI_FLASH_BAR +		bank_sel = offset / SPI_FLASH_16MB_BOUN; -	timebase = get_timer(0); -	do { -		WATCHDOG_RESET(); +		ret = spi_flash_cmd_bankaddr_write(flash, bank_sel); +		if (ret) { +			debug("SF: fail to set bank%d\n", bank_sel); +			return ret; +		} +#endif +		remain_len = (SPI_FLASH_16MB_BOUN * (bank_sel + 1) - offset); +		if (len < remain_len) +			read_len = len; +		else +			read_len = remain_len; -		ret = spi_xfer(spi, 8, NULL, &status, 0); -		if (ret) -			return -1; +		spi_flash_addr(offset, cmd); -		if ((status & poll_bit) == 0) +		ret = spi_flash_read_common(flash, cmd, sizeof(cmd), +							data, read_len); +		if (ret < 0) { +			debug("SF: read failed\n");  			break; +		} -	} while (get_timer(timebase) < timeout); - -	spi_xfer(spi, 0, NULL, NULL, SPI_XFER_END); - -	if ((status & poll_bit) == 0) -		return 0; - -	/* Timed out */ -	debug("SF: time out!\n"); -	return -1; -} +		offset += read_len; +		len -= read_len; +		data += read_len; +	} -int spi_flash_cmd_wait_ready(struct spi_flash *flash, unsigned long timeout) -{ -	return spi_flash_cmd_poll_bit(flash, timeout, -		CMD_READ_STATUS, STATUS_WIP); +	return ret;  } -int spi_flash_cmd_erase(struct spi_flash *flash, u32 offset, size_t len) +int spi_flash_cmd_write_status(struct spi_flash *flash, u8 sr)  { -	u32 end, erase_size; +	u8 cmd;  	int ret; -	u8 cmd[4]; - -	erase_size = flash->sector_size; -	if (offset % erase_size || len % erase_size) { -		debug("SF: Erase offset/length not multiple of erase size\n"); -		return -1; -	} -	ret = spi_claim_bus(flash->spi); -	if (ret) { -		debug("SF: Unable to claim SPI bus\n"); +	cmd = CMD_WRITE_STATUS; +	ret = spi_flash_write_common(flash, &cmd, 1, &sr, 1); +	if (ret < 0) { +		debug("SF: fail to write status register\n");  		return ret;  	} -	if (erase_size == 4096) -		cmd[0] = CMD_ERASE_4K; -	else -		cmd[0] = CMD_ERASE_64K; -	end = offset + len; - -	while (offset < end) { -		spi_flash_addr(offset, cmd); -		offset += erase_size; - -		debug("SF: erase %2x %2x %2x %2x (%x)\n", cmd[0], cmd[1], -		      cmd[2], cmd[3], offset); - -		ret = spi_flash_cmd_write_enable(flash); -		if (ret) -			goto out; - -		ret = spi_flash_cmd_write(flash->spi, cmd, sizeof(cmd), NULL, 0); -		if (ret) -			goto out; - -		ret = spi_flash_cmd_wait_ready(flash, SPI_FLASH_PAGE_ERASE_TIMEOUT); -		if (ret) -			goto out; -	} - - out: -	spi_release_bus(flash->spi); -	return ret; +	return 0;  } -int spi_flash_cmd_write_status(struct spi_flash *flash, u8 sr) +#ifdef CONFIG_SPI_FLASH_BAR +int spi_flash_cmd_bankaddr_write(struct spi_flash *flash, u8 bank_sel)  {  	u8 cmd;  	int ret; -	ret = spi_flash_cmd_write_enable(flash); +	if (flash->bank_curr == bank_sel) { +		debug("SF: not require to enable bank%d\n", bank_sel); +		return 0; +	} + +	cmd = flash->bank_write_cmd; +	ret = spi_flash_write_common(flash, &cmd, 1, &bank_sel, 1);  	if (ret < 0) { -		debug("SF: enabling write failed\n"); +		debug("SF: fail to write bank register\n");  		return ret;  	} +	flash->bank_curr = bank_sel; -	cmd = CMD_WRITE_STATUS; -	ret = spi_flash_cmd_write(flash->spi, &cmd, 1, &sr, 1); -	if (ret) { -		debug("SF: fail to write status register\n"); -		return ret; +	return 0; +} + +int spi_flash_bank_config(struct spi_flash *flash, u8 idcode0) +{ +	u8 cmd; +	u8 curr_bank = 0; + +	/* discover bank cmds */ +	switch (idcode0) { +	case SPI_FLASH_SPANSION_IDCODE0: +		flash->bank_read_cmd = CMD_BANKADDR_BRRD; +		flash->bank_write_cmd = CMD_BANKADDR_BRWR; +		break; +	case SPI_FLASH_STMICRO_IDCODE0: +	case SPI_FLASH_WINBOND_IDCODE0: +		flash->bank_read_cmd = CMD_EXTNADDR_RDEAR; +		flash->bank_write_cmd = CMD_EXTNADDR_WREAR; +		break; +	default: +		printf("SF: Unsupported bank commands %02x\n", idcode0); +		return -1;  	} -	ret = spi_flash_cmd_wait_ready(flash, SPI_FLASH_PROG_TIMEOUT); -	if (ret < 0) { -		debug("SF: write status register timed out\n"); -		return ret; +	/* read the bank reg - on which bank the flash is in currently */ +	cmd = flash->bank_read_cmd; +	if (flash->size > SPI_FLASH_16MB_BOUN) { +		if (spi_flash_read_common(flash, &cmd, 1, &curr_bank, 1)) { +			debug("SF: fail to read bank addr register\n"); +			return -1; +		} +		flash->bank_curr = curr_bank; +	} else { +		flash->bank_curr = curr_bank;  	}  	return 0;  } +#endif  #ifdef CONFIG_OF_CONTROL  int spi_flash_decode_fdt(const void *blob, struct spi_flash *flash) @@ -342,6 +458,9 @@ static const struct {  #ifdef CONFIG_SPI_FLASH_EON  	{ 0, 0x1c, spi_flash_probe_eon, },  #endif +#ifdef CONFIG_SPI_FLASH_GIGADEVICE +	{ 0, 0xc8, spi_flash_probe_gigadevice, }, +#endif  #ifdef CONFIG_SPI_FLASH_MACRONIX  	{ 0, 0xc2, spi_flash_probe_macronix, },  #endif @@ -422,6 +541,13 @@ struct spi_flash *spi_flash_probe(unsigned int bus, unsigned int cs,  		goto err_manufacturer_probe;  	} +#ifdef CONFIG_SPI_FLASH_BAR +	/* Configure the BAR - disover bank cmds and read current bank  */ +	ret = spi_flash_bank_config(flash, *idp); +	if (ret < 0) +		goto err_manufacturer_probe; +#endif +  #ifdef CONFIG_OF_CONTROL  	if (spi_flash_decode_fdt(gd->fdt_blob, flash)) {  		debug("SF: FDT decode error\n"); @@ -434,6 +560,12 @@ struct spi_flash *spi_flash_probe(unsigned int bus, unsigned int cs,  	if (flash->memory_map)  		printf(", mapped at %p", flash->memory_map);  	puts("\n"); +#ifndef CONFIG_SPI_FLASH_BAR +	if (flash->size > SPI_FLASH_16MB_BOUN) { +		puts("SF: Warning - Only lower 16MiB accessible,"); +		puts(" Full access #define CONFIG_SPI_FLASH_BAR\n"); +	} +#endif  	spi_release_bus(spi); @@ -464,6 +596,7 @@ void *spi_flash_do_alloc(int offset, int size, struct spi_slave *spi,  	/* Set up some basic fields - caller will sort out sizes */  	flash->spi = spi;  	flash->name = name; +	flash->poll_cmd = CMD_READ_STATUS;  	flash->read = spi_flash_cmd_read_fast;  	flash->write = spi_flash_cmd_write_multi; diff --git a/drivers/mtd/spi/spi_flash_internal.h b/drivers/mtd/spi/spi_flash_internal.h index 141cfa8b2..af1afa96c 100644 --- a/drivers/mtd/spi/spi_flash_internal.h +++ b/drivers/mtd/spi/spi_flash_internal.h @@ -22,14 +22,31 @@  #define CMD_PAGE_PROGRAM		0x02  #define CMD_WRITE_DISABLE		0x04  #define CMD_READ_STATUS			0x05 +#define CMD_FLAG_STATUS			0x70  #define CMD_WRITE_ENABLE		0x06  #define CMD_ERASE_4K			0x20  #define CMD_ERASE_32K			0x52  #define CMD_ERASE_64K			0xd8  #define CMD_ERASE_CHIP			0xc7 +#define SPI_FLASH_16MB_BOUN		0x1000000 + +/* Manufacture ID's */ +#define SPI_FLASH_SPANSION_IDCODE0	0x01 +#define SPI_FLASH_STMICRO_IDCODE0	0x20 +#define SPI_FLASH_WINBOND_IDCODE0	0xef + +#ifdef CONFIG_SPI_FLASH_BAR +/* Bank addr access commands */ +# define CMD_BANKADDR_BRWR		0x17 +# define CMD_BANKADDR_BRRD		0x16 +# define CMD_EXTNADDR_WREAR		0xC5 +# define CMD_EXTNADDR_RDEAR		0xC8 +#endif +  /* Common status */  #define STATUS_WIP			0x01 +#define STATUS_PEC			0x80  /* Send a single-byte command to the device and read the response */  int spi_flash_cmd(struct spi_slave *spi, u8 cmd, void *response, size_t len); @@ -77,16 +94,30 @@ static inline int spi_flash_cmd_write_disable(struct spi_flash *flash)  /* Program the status register. */  int spi_flash_cmd_write_status(struct spi_flash *flash, u8 sr); +#ifdef CONFIG_SPI_FLASH_BAR +/* Program the bank address register */ +int spi_flash_cmd_bankaddr_write(struct spi_flash *flash, u8 bank_sel); + +/* Configure the BAR - discover the bank cmds */ +int spi_flash_bank_config(struct spi_flash *flash, u8 idcode0); +#endif +  /*   * Same as spi_flash_cmd_read() except it also claims/releases the SPI   * bus. Used as common part of the ->read() operation.   */  int spi_flash_read_common(struct spi_flash *flash, const u8 *cmd,  		size_t cmd_len, void *data, size_t data_len); - -/* Send a command to the device and wait for some bit to clear itself. */ -int spi_flash_cmd_poll_bit(struct spi_flash *flash, unsigned long timeout, -			   u8 cmd, u8 poll_bit); +/* + * Used for spi_flash write operation + * - SPI claim + * - spi_flash_cmd_write_enable + * - spi_flash_cmd_write + * - spi_flash_cmd_wait_ready + * - SPI release + */ +int spi_flash_write_common(struct spi_flash *flash, const u8 *cmd, +		size_t cmd_len, const void *buf, size_t buf_len);  /*   * Send the read status command to the device and wait for the wip @@ -106,3 +137,4 @@ struct spi_flash *spi_flash_probe_sst(struct spi_slave *spi, u8 *idcode);  struct spi_flash *spi_flash_probe_stmicro(struct spi_slave *spi, u8 *idcode);  struct spi_flash *spi_flash_probe_winbond(struct spi_slave *spi, u8 *idcode);  struct spi_flash *spi_fram_probe_ramtron(struct spi_slave *spi, u8 *idcode); +struct spi_flash *spi_flash_probe_gigadevice(struct spi_slave *spi, u8 *idcode); diff --git a/drivers/mtd/spi/stmicro.c b/drivers/mtd/spi/stmicro.c index 2a9972bd4..7e41ee132 100644 --- a/drivers/mtd/spi/stmicro.c +++ b/drivers/mtd/spi/stmicro.c @@ -140,6 +140,30 @@ static const struct stmicro_spi_flash_params stmicro_spi_flash_table[] = {  		.nr_sectors = 512,  		.name = "N25Q256A",  	}, +	{ +		.id = 0xba20, +		.pages_per_sector = 256, +		.nr_sectors = 1024, +		.name = "N25Q512", +	}, +	{ +		.id = 0xbb20, +		.pages_per_sector = 256, +		.nr_sectors = 1024, +		.name = "N25Q512A", +	}, +	{ +		.id = 0xba21, +		.pages_per_sector = 256, +		.nr_sectors = 2048, +		.name = "N25Q1024", +	}, +	{ +		.id = 0xbb21, +		.pages_per_sector = 256, +		.nr_sectors = 2048, +		.name = "N25Q1024A", +	},  };  struct spi_flash *spi_flash_probe_stmicro(struct spi_slave *spi, u8 * idcode) @@ -186,5 +210,9 @@ struct spi_flash *spi_flash_probe_stmicro(struct spi_slave *spi, u8 * idcode)  	flash->sector_size = 256 * params->pages_per_sector;  	flash->size = flash->sector_size * params->nr_sectors; +	/* for >= 512MiB flashes, use flag status instead of read_status */ +	if (flash->size >= 0x4000000) +		flash->poll_cmd = CMD_FLAG_STATUS; +  	return flash;  } diff --git a/drivers/mtd/spi/winbond.c b/drivers/mtd/spi/winbond.c index 845780849..c399bf14d 100644 --- a/drivers/mtd/spi/winbond.c +++ b/drivers/mtd/spi/winbond.c @@ -55,27 +55,27 @@ static const struct winbond_spi_flash_params winbond_spi_flash_table[] = {  	{  		.id			= 0x4014,  		.nr_blocks		= 16, -		.name			= "W25Q80BL", +		.name			= "W25Q80BL/W25Q80BV",  	},  	{  		.id			= 0x4015,  		.nr_blocks		= 32, -		.name			= "W25Q16", +		.name			= "W25Q16CL/W25Q16DV",  	},  	{  		.id			= 0x4016,  		.nr_blocks		= 64, -		.name			= "W25Q32", +		.name			= "W25Q32BV/W25Q32FV_SPI",  	},  	{  		.id			= 0x4017,  		.nr_blocks		= 128, -		.name			= "W25Q64", +		.name			= "W25Q64CV/W25Q64FV_SPI",  	},  	{  		.id			= 0x4018,  		.nr_blocks		= 256, -		.name			= "W25Q128", +		.name			= "W25Q128BV/W25Q128FV_SPI",  	},  	{  		.id			= 0x4019, @@ -88,14 +88,24 @@ static const struct winbond_spi_flash_params winbond_spi_flash_table[] = {  		.name			= "W25Q80BW",  	},  	{ +		.id			= 0x6015, +		.nr_blocks		= 32, +		.name			= "W25Q16DW", +	}, +	{  		.id			= 0x6016,  		.nr_blocks		= 64, -		.name			= "W25Q32DW", +		.name			= "W25Q32DW/W25Q32FV_QPI",  	},  	{  		.id			= 0x6017,  		.nr_blocks		= 128, -		.name			= "W25Q64DW", +		.name			= "W25Q64DW/W25Q64FV_QPI", +	}, +	{ +		.id			= 0x6018, +		.nr_blocks		= 256, +		.name			= "W25Q128FW/W25Q128FV_QPI",  	},  };  |