diff options
| -rw-r--r-- | drivers/mtd/spi/Makefile | 2 | ||||
| -rw-r--r-- | drivers/mtd/spi/atmel.c | 50 | ||||
| -rw-r--r-- | drivers/mtd/spi/spansion.c | 350 | ||||
| -rw-r--r-- | drivers/mtd/spi/spi_flash.c | 13 | ||||
| -rw-r--r-- | drivers/mtd/spi/spi_flash_internal.h | 10 | ||||
| -rw-r--r-- | drivers/mtd/spi/sst.c | 358 | ||||
| -rw-r--r-- | drivers/mtd/spi/stmicro.c | 17 | 
7 files changed, 780 insertions, 20 deletions
| diff --git a/drivers/mtd/spi/Makefile b/drivers/mtd/spi/Makefile index 3d4f8923c..a71b16efc 100644 --- a/drivers/mtd/spi/Makefile +++ b/drivers/mtd/spi/Makefile @@ -27,6 +27,8 @@ LIB	:= $(obj)libspi_flash.a  COBJS-$(CONFIG_SPI_FLASH)	+= spi_flash.o  COBJS-$(CONFIG_SPI_FLASH_ATMEL)	+= atmel.o +COBJS-$(CONFIG_SPI_FLASH_SPANSION)	+= spansion.o +COBJS-$(CONFIG_SPI_FLASH_SST)	+= sst.o  COBJS-$(CONFIG_SPI_FLASH_STMICRO)	+= stmicro.o  COBJS	:= $(COBJS-y) diff --git a/drivers/mtd/spi/atmel.c b/drivers/mtd/spi/atmel.c index a5f51caf4..c3b936f16 100644 --- a/drivers/mtd/spi/atmel.c +++ b/drivers/mtd/spi/atmel.c @@ -3,7 +3,7 @@   *   * Copyright (C) 2008 Atmel Corporation   */ -#define DEBUG +  #include <common.h>  #include <malloc.h>  #include <spi_flash.h> @@ -53,6 +53,54 @@ to_atmel_spi_flash(struct spi_flash *flash)  static const struct atmel_spi_flash_params atmel_spi_flash_table[] = {  	{ +		.idcode1		= 0x22, +		.l2_page_size		= 8, +		.pages_per_block	= 8, +		.blocks_per_sector	= 16, +		.nr_sectors		= 4, +		.name			= "AT45DB011D", +	}, +	{ +		.idcode1		= 0x23, +		.l2_page_size		= 8, +		.pages_per_block	= 8, +		.blocks_per_sector	= 16, +		.nr_sectors		= 8, +		.name			= "AT45DB021D", +	}, +	{ +		.idcode1		= 0x24, +		.l2_page_size		= 8, +		.pages_per_block	= 8, +		.blocks_per_sector	= 32, +		.nr_sectors		= 8, +		.name			= "AT45DB041D", +	}, +	{ +		.idcode1		= 0x25, +		.l2_page_size		= 8, +		.pages_per_block	= 8, +		.blocks_per_sector	= 32, +		.nr_sectors		= 16, +		.name			= "AT45DB081D", +	}, +	{ +		.idcode1		= 0x26, +		.l2_page_size		= 9, +		.pages_per_block	= 8, +		.blocks_per_sector	= 32, +		.nr_sectors		= 16, +		.name			= "AT45DB161D", +	}, +	{ +		.idcode1		= 0x27, +		.l2_page_size		= 9, +		.pages_per_block	= 8, +		.blocks_per_sector	= 64, +		.nr_sectors		= 64, +		.name			= "AT45DB321D", +	}, +	{  		.idcode1		= 0x28,  		.l2_page_size		= 10,  		.pages_per_block	= 8, diff --git a/drivers/mtd/spi/spansion.c b/drivers/mtd/spi/spansion.c new file mode 100644 index 000000000..fdb791798 --- /dev/null +++ b/drivers/mtd/spi/spansion.c @@ -0,0 +1,350 @@ +/* + * Copyright (C) 2009 Freescale Semiconductor, Inc. + * + * Author: Mingkai Hu (Mingkai.hu@freescale.com) + * Based on stmicro.c by Wolfgang Denk (wd@denx.de), + * TsiChung Liew (Tsi-Chung.Liew@freescale.com), + * and  Jason McMullan (mcmullan@netapp.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" + +/* S25FLxx-specific commands */ +#define CMD_S25FLXX_READ	0x03	/* Read Data Bytes */ +#define CMD_S25FLXX_FAST_READ	0x0b	/* Read Data Bytes at Higher Speed */ +#define CMD_S25FLXX_READID	0x90	/* Read Manufacture ID and Device ID */ +#define CMD_S25FLXX_WREN	0x06	/* Write Enable */ +#define CMD_S25FLXX_WRDI	0x04	/* Write Disable */ +#define CMD_S25FLXX_RDSR	0x05	/* Read Status Register */ +#define CMD_S25FLXX_WRSR	0x01	/* Write Status Register */ +#define CMD_S25FLXX_PP		0x02	/* Page Program */ +#define CMD_S25FLXX_SE		0xd8	/* Sector Erase */ +#define CMD_S25FLXX_BE		0xc7	/* Bulk Erase */ +#define CMD_S25FLXX_DP		0xb9	/* Deep Power-down */ +#define CMD_S25FLXX_RES		0xab	/* Release from DP, and Read Signature */ + +#define SPSN_ID_S25FL008A	0x0213 +#define SPSN_ID_S25FL016A	0x0214 +#define SPSN_ID_S25FL032A	0x0215 +#define SPSN_ID_S25FL064A	0x0216 +#define SPSN_ID_S25FL128P	0x2018 +#define SPSN_EXT_ID_S25FL128P_256KB	0x0300 +#define SPSN_EXT_ID_S25FL128P_64KB	0x0301 + +#define SPANSION_SR_WIP		(1 << 0)	/* Write-in-Progress */ + +struct spansion_spi_flash_params { +	u16 idcode1; +	u16 idcode2; +	u16 page_size; +	u16 pages_per_sector; +	u16 nr_sectors; +	const char *name; +}; + +struct spansion_spi_flash { +	struct spi_flash flash; +	const struct spansion_spi_flash_params *params; +}; + +static inline struct spansion_spi_flash *to_spansion_spi_flash(struct spi_flash +							     *flash) +{ +	return container_of(flash, struct spansion_spi_flash, flash); +} + +static const struct spansion_spi_flash_params spansion_spi_flash_table[] = { +	{ +		.idcode1 = SPSN_ID_S25FL008A, +		.idcode2 = 0, +		.page_size = 256, +		.pages_per_sector = 256, +		.nr_sectors = 16, +		.name = "S25FL008A", +	}, +	{ +		.idcode1 = SPSN_ID_S25FL016A, +		.idcode2 = 0, +		.page_size = 256, +		.pages_per_sector = 256, +		.nr_sectors = 32, +		.name = "S25FL016A", +	}, +	{ +		.idcode1 = SPSN_ID_S25FL032A, +		.idcode2 = 0, +		.page_size = 256, +		.pages_per_sector = 256, +		.nr_sectors = 64, +		.name = "S25FL032A", +	}, +	{ +		.idcode1 = SPSN_ID_S25FL064A, +		.idcode2 = 0, +		.page_size = 256, +		.pages_per_sector = 256, +		.nr_sectors = 128, +		.name = "S25FL064A", +	}, +	{ +		.idcode1 = SPSN_ID_S25FL128P, +		.idcode2 = SPSN_EXT_ID_S25FL128P_64KB, +		.page_size = 256, +		.pages_per_sector = 256, +		.nr_sectors = 256, +		.name = "S25FL128P_64K", +	}, +	{ +		.idcode1 = SPSN_ID_S25FL128P, +		.idcode2 = SPSN_EXT_ID_S25FL128P_256KB, +		.page_size = 256, +		.pages_per_sector = 1024, +		.nr_sectors = 64, +		.name = "S25FL128P_256K", +	}, +}; + +static int spansion_wait_ready(struct spi_flash *flash, unsigned long timeout) +{ +	struct spi_slave *spi = flash->spi; +	unsigned long timebase; +	int ret; +	u8 status; + +	timebase = get_timer(0); +	do { +		ret = spi_flash_cmd(spi, CMD_S25FLXX_RDSR, &status, sizeof(status)); +		if (ret) +			return -1; + +		if ((status & SPANSION_SR_WIP) == 0) +			break; + +	} while (get_timer(timebase) < timeout); + + +	if ((status & SPANSION_SR_WIP) == 0) +		return 0; + +	/* Timed out */ +	return -1; +} + +static int spansion_read_fast(struct spi_flash *flash, +			     u32 offset, size_t len, void *buf) +{ +	struct spansion_spi_flash *spsn = to_spansion_spi_flash(flash); +	unsigned long page_addr; +	unsigned long page_size; +	u8 cmd[5]; + +	page_size = spsn->params->page_size; +	page_addr = offset / page_size; + +	cmd[0] = CMD_READ_ARRAY_FAST; +	cmd[1] = page_addr >> 8; +	cmd[2] = page_addr; +	cmd[3] = offset % page_size; +	cmd[4] = 0x00; + +	debug +		("READ: 0x%x => cmd = { 0x%02x 0x%02x%02x%02x%02x } len = 0x%x\n", +		 offset, cmd[0], cmd[1], cmd[2], cmd[3], cmd[4], len); + +	return spi_flash_read_common(flash, cmd, sizeof(cmd), buf, len); +} + +static int spansion_write(struct spi_flash *flash, +			 u32 offset, size_t len, const void *buf) +{ +	struct spansion_spi_flash *spsn = to_spansion_spi_flash(flash); +	unsigned long page_addr; +	unsigned long byte_addr; +	unsigned long page_size; +	size_t chunk_len; +	size_t actual; +	int ret; +	u8 cmd[4]; + +	page_size = spsn->params->page_size; +	page_addr = offset / page_size; +	byte_addr = offset % page_size; + +	ret = spi_claim_bus(flash->spi); +	if (ret) { +		debug("SF: Unable to claim SPI bus\n"); +		return ret; +	} + +	ret = 0; +	for (actual = 0; actual < len; actual += chunk_len) { +		chunk_len = min(len - actual, page_size - byte_addr); + +		cmd[0] = CMD_S25FLXX_PP; +		cmd[1] = page_addr >> 8; +		cmd[2] = page_addr; +		cmd[3] = byte_addr; + +		debug +		    ("PP: 0x%p => cmd = { 0x%02x 0x%02x%02x%02x } chunk_len = %d\n", +		     buf + actual, cmd[0], cmd[1], cmd[2], cmd[3], chunk_len); + +		ret = spi_flash_cmd(flash->spi, CMD_S25FLXX_WREN, NULL, 0); +		if (ret < 0) { +			debug("SF: Enabling Write failed\n"); +			break; +		} + +		ret = spi_flash_cmd_write(flash->spi, cmd, 4, +					  buf + actual, chunk_len); +		if (ret < 0) { +			debug("SF: SPANSION Page Program failed\n"); +			break; +		} + +		ret = spansion_wait_ready(flash, SPI_FLASH_PROG_TIMEOUT); +		if (ret < 0) { +			debug("SF: SPANSION page programming timed out\n"); +			break; +		} + +		page_addr++; +		byte_addr = 0; +	} + +	debug("SF: SPANSION: Successfully programmed %u bytes @ 0x%x\n", +	      len, offset); + +	spi_release_bus(flash->spi); +	return ret; +} + +int spansion_erase(struct spi_flash *flash, u32 offset, size_t len) +{ +	struct spansion_spi_flash *spsn = to_spansion_spi_flash(flash); +	unsigned long sector_size; +	size_t actual; +	int ret; +	u8 cmd[4]; + +	/* +	 * This function currently uses sector erase only. +	 * probably speed things up by using bulk erase +	 * when possible. +	 */ + +	sector_size = spsn->params->page_size * spsn->params->pages_per_sector; + +	if (offset % sector_size || len % sector_size) { +		debug("SF: Erase offset/length not multiple of sector size\n"); +		return -1; +	} + +	len /= sector_size; +	cmd[0] = CMD_S25FLXX_SE; +	cmd[2] = 0x00; +	cmd[3] = 0x00; + +	ret = spi_claim_bus(flash->spi); +	if (ret) { +		debug("SF: Unable to claim SPI bus\n"); +		return ret; +	} + +	ret = 0; +	for (actual = 0; actual < len; actual++) { +		cmd[1] = (offset / sector_size) + actual; + +		ret = spi_flash_cmd(flash->spi, CMD_S25FLXX_WREN, NULL, 0); +		if (ret < 0) { +			debug("SF: Enabling Write failed\n"); +			break; +		} + +		ret = spi_flash_cmd_write(flash->spi, cmd, 4, NULL, 0); +		if (ret < 0) { +			debug("SF: SPANSION page erase failed\n"); +			break; +		} + +		/* Up to 2 seconds */ +		ret = spansion_wait_ready(flash, SPI_FLASH_PAGE_ERASE_TIMEOUT); +		if (ret < 0) { +			debug("SF: SPANSION page erase timed out\n"); +			break; +		} +	} + +	debug("SF: SPANSION: Successfully erased %u bytes @ 0x%x\n", +	      len * sector_size, offset); + +	spi_release_bus(flash->spi); +	return ret; +} + +struct spi_flash *spi_flash_probe_spansion(struct spi_slave *spi, u8 *idcode) +{ +	const struct spansion_spi_flash_params *params; +	struct spansion_spi_flash *spsn; +	unsigned int i; +	unsigned short jedec, ext_jedec; + +	jedec = idcode[1] << 8 | idcode[2]; +	ext_jedec = idcode[3] << 8 | idcode[4]; + +	for (i = 0; i < ARRAY_SIZE(spansion_spi_flash_table); i++) { +		params = &spansion_spi_flash_table[i]; +		if (params->idcode1 == jedec) { +			if (params->idcode2 == ext_jedec) +				break; +		} +	} + +	if (i == ARRAY_SIZE(spansion_spi_flash_table)) { +		debug("SF: Unsupported SPANSION ID %04x %04x\n", jedec, ext_jedec); +		return NULL; +	} + +	spsn = malloc(sizeof(struct spansion_spi_flash)); +	if (!spsn) { +		debug("SF: Failed to allocate memory\n"); +		return NULL; +	} + +	spsn->params = params; +	spsn->flash.spi = spi; +	spsn->flash.name = params->name; + +	spsn->flash.write = spansion_write; +	spsn->flash.erase = spansion_erase; +	spsn->flash.read = spansion_read_fast; +	spsn->flash.size = params->page_size * params->pages_per_sector +	    * params->nr_sectors; + +	debug("SF: Detected %s with page size %u, total %u bytes\n", +	      params->name, params->page_size, spsn->flash.size); + +	return &spsn->flash; +} diff --git a/drivers/mtd/spi/spi_flash.c b/drivers/mtd/spi/spi_flash.c index d1d81af0e..274895ad8 100644 --- a/drivers/mtd/spi/spi_flash.c +++ b/drivers/mtd/spi/spi_flash.c @@ -3,7 +3,7 @@   *   * Copyright (C) 2008 Atmel Corporation   */ -#define DEBUG +  #include <common.h>  #include <malloc.h>  #include <spi.h> @@ -101,7 +101,7 @@ struct spi_flash *spi_flash_probe(unsigned int bus, unsigned int cs,  	struct spi_slave *spi;  	struct spi_flash *flash;  	int ret; -	u8 idcode[3]; +	u8 idcode[5];  	spi = spi_setup_slave(bus, cs, max_hz, spi_mode);  	if (!spi) { @@ -120,8 +120,8 @@ struct spi_flash *spi_flash_probe(unsigned int bus, unsigned int cs,  	if (ret)  		goto err_read_id; -	debug("SF: Got idcode %02x %02x %02x\n", idcode[0], -			idcode[1], idcode[2]); +	debug("SF: Got idcode %02x %02x %02x %02x %02x\n", idcode[0], +			idcode[1], idcode[2], idcode[3], idcode[4]);  	switch (idcode[0]) {  #ifdef CONFIG_SPI_FLASH_SPANSION @@ -139,6 +139,11 @@ struct spi_flash *spi_flash_probe(unsigned int bus, unsigned int cs,  		flash = spi_flash_probe_stmicro(spi, idcode);  		break;  #endif +#ifdef CONFIG_SPI_FLASH_SST +	case 0xBF: +		flash = spi_flash_probe_sst(spi, idcode); +		break; +#endif  	default:  		debug("SF: Unsupported manufacturer %02X\n", idcode[0]);  		flash = NULL; diff --git a/drivers/mtd/spi/spi_flash_internal.h b/drivers/mtd/spi/spi_flash_internal.h index 75f5900dc..5d1e395af 100644 --- a/drivers/mtd/spi/spi_flash_internal.h +++ b/drivers/mtd/spi/spi_flash_internal.h @@ -4,9 +4,12 @@   * Copyright (C) 2008 Atmel Corporation   */ -/* Common parameters */ -#define SPI_FLASH_PROG_TIMEOUT		((10 * CONFIG_SYS_HZ) / 1000) -#define SPI_FLASH_PAGE_ERASE_TIMEOUT	((50 * CONFIG_SYS_HZ) / 1000) +/* Common parameters -- kind of high, but they should only occur when there + * is a problem (and well your system already is broken), so err on the side + * of caution in case we're dealing with slower SPI buses and/or processors. + */ +#define SPI_FLASH_PROG_TIMEOUT		(2 * CONFIG_SYS_HZ) +#define SPI_FLASH_PAGE_ERASE_TIMEOUT	(5 * CONFIG_SYS_HZ)  #define SPI_FLASH_SECTOR_ERASE_TIMEOUT	(10 * CONFIG_SYS_HZ)  /* Common commands */ @@ -43,4 +46,5 @@ int spi_flash_read_common(struct spi_flash *flash, const u8 *cmd,  /* Manufacturer-specific probe functions */  struct spi_flash *spi_flash_probe_spansion(struct spi_slave *spi, u8 *idcode);  struct spi_flash *spi_flash_probe_atmel(struct spi_slave *spi, u8 *idcode); +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); diff --git a/drivers/mtd/spi/sst.c b/drivers/mtd/spi/sst.c new file mode 100644 index 000000000..62236d495 --- /dev/null +++ b/drivers/mtd/spi/sst.c @@ -0,0 +1,358 @@ +/* + * Driver for SST serial flashes + * + * (C) Copyright 2000-2002 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * Copyright 2008, Network Appliance Inc. + * Jason McMullan <mcmullan@netapp.com> + * Copyright (C) 2004-2007 Freescale Semiconductor, Inc. + * TsiChung Liew (Tsi-Chung.Liew@freescale.com) + * Copyright (c) 2008-2009 Analog Devices Inc. + * + * Licensed under the GPL-2 or later. + */ + +#include <common.h> +#include <malloc.h> +#include <spi_flash.h> + +#include "spi_flash_internal.h" + +#define CMD_SST_WREN		0x06	/* Write Enable */ +#define CMD_SST_WRDI		0x04	/* Write Disable */ +#define CMD_SST_RDSR		0x05	/* Read Status Register */ +#define CMD_SST_WRSR		0x01	/* Write Status Register */ +#define CMD_SST_READ		0x03	/* Read Data Bytes */ +#define CMD_SST_FAST_READ	0x0b	/* Read Data Bytes at Higher Speed */ +#define CMD_SST_BP		0x02	/* Byte Program */ +#define CMD_SST_AAI_WP		0xAD	/* Auto Address Increment Word Program */ +#define CMD_SST_SE		0x20	/* Sector Erase */ + +#define SST_SR_WIP		(1 << 0)	/* Write-in-Progress */ +#define SST_SR_WEL		(1 << 1)	/* Write enable */ +#define SST_SR_BP0		(1 << 2)	/* Block Protection 0 */ +#define SST_SR_BP1		(1 << 3)	/* Block Protection 1 */ +#define SST_SR_BP2		(1 << 4)	/* Block Protection 2 */ +#define SST_SR_AAI		(1 << 6)	/* Addressing mode */ +#define SST_SR_BPL		(1 << 7)	/* BP bits lock */ + +struct sst_spi_flash_params { +	u8 idcode1; +	u16 nr_sectors; +	const char *name; +}; + +struct sst_spi_flash { +	struct spi_flash flash; +	const struct sst_spi_flash_params *params; +}; + +static inline struct sst_spi_flash *to_sst_spi_flash(struct spi_flash *flash) +{ +	return container_of(flash, struct sst_spi_flash, flash); +} + +#define SST_SECTOR_SIZE (4 * 1024) +static const struct sst_spi_flash_params sst_spi_flash_table[] = { +	{ +		.idcode1 = 0x01, +		.nr_sectors = 128, +		.name = "SST25WF512", +	},{ +		.idcode1 = 0x02, +		.nr_sectors = 256, +		.name = "SST25WF010", +	},{ +		.idcode1 = 0x03, +		.nr_sectors = 512, +		.name = "SST25WF020", +	},{ +		.idcode1 = 0x04, +		.nr_sectors = 1024, +		.name = "SST25WF040", +	}, +}; + +static int +sst_wait_ready(struct spi_flash *flash, unsigned long timeout) +{ +	struct spi_slave *spi = flash->spi; +	unsigned long timebase; +	int ret; +	u8 byte = CMD_SST_RDSR; + +	ret = spi_xfer(spi, sizeof(byte) * 8, &byte, NULL, SPI_XFER_BEGIN); +	if (ret) { +		debug("SF: Failed to send command %02x: %d\n", byte, ret); +		return ret; +	} + +	timebase = get_timer(0); +	do { +		ret = spi_xfer(spi, sizeof(byte) * 8, NULL, &byte, 0); +		if (ret) +			break; + +		if ((byte & SST_SR_WIP) == 0) +			break; + +	} while (get_timer(timebase) < timeout); + +	spi_xfer(spi, 0, NULL, NULL, SPI_XFER_END); + +	if (!ret && (byte & SST_SR_WIP) != 0) +		ret = -1; + +	if (ret) +		debug("SF: sst wait for ready timed out\n"); +	return ret; +} + +static int +sst_enable_writing(struct spi_flash *flash) +{ +	int ret = spi_flash_cmd(flash->spi, CMD_SST_WREN, NULL, 0); +	if (ret) +		debug("SF: Enabling Write failed\n"); +	return ret; +} + +static int +sst_disable_writing(struct spi_flash *flash) +{ +	int ret = spi_flash_cmd(flash->spi, CMD_SST_WRDI, NULL, 0); +	if (ret) +		debug("SF: Disabling Write failed\n"); +	return ret; +} + +static int +sst_read_fast(struct spi_flash *flash, u32 offset, size_t len, void *buf) +{ +	u8 cmd[5] = { +		CMD_READ_ARRAY_FAST, +		offset >> 16, +		offset >> 8, +		offset, +		0x00, +	}; +	return spi_flash_read_common(flash, cmd, sizeof(cmd), buf, len); +} + +static int +sst_byte_write(struct spi_flash *flash, u32 offset, const void *buf) +{ +	int ret; +	u8 cmd[4] = { +		CMD_SST_BP, +		offset >> 16, +		offset >> 8, +		offset, +	}; + +	debug("BP[%02x]: 0x%p => cmd = { 0x%02x 0x%06x }\n", +		spi_w8r8(flash->spi, CMD_SST_RDSR), buf, cmd[0], offset); + +	ret = sst_enable_writing(flash); +	if (ret) +		return ret; + +	ret = spi_flash_cmd_write(flash->spi, cmd, sizeof(cmd), buf, 1); +	if (ret) +		return ret; + +	return sst_wait_ready(flash, SPI_FLASH_PROG_TIMEOUT); +} + +static int +sst_write(struct spi_flash *flash, u32 offset, size_t len, const void *buf) +{ +	size_t actual, cmd_len; +	int ret; +	u8 cmd[4]; + +	ret = spi_claim_bus(flash->spi); +	if (ret) { +		debug("SF: Unable to claim SPI bus\n"); +		return ret; +	} + +	/* If the data is not word aligned, write out leading single byte */ +	actual = offset % 2; +	if (actual) { +		ret = sst_byte_write(flash, offset, buf); +		if (ret) +			goto done; +	} +	offset += actual; + +	ret = sst_enable_writing(flash); +	if (ret) +		goto done; + +	cmd_len = 4; +	cmd[0] = CMD_SST_AAI_WP; +	cmd[1] = offset >> 16; +	cmd[2] = offset >> 8; +	cmd[3] = offset; + +	for (; actual < len - 1; actual += 2) { +		debug("WP[%02x]: 0x%p => cmd = { 0x%02x 0x%06x }\n", +		     spi_w8r8(flash->spi, CMD_SST_RDSR), buf + actual, cmd[0], +		     offset); + +		ret = spi_flash_cmd_write(flash->spi, cmd, cmd_len, +		                          buf + actual, 2); +		if (ret) { +			debug("SF: sst word program failed\n"); +			break; +		} + +		ret = sst_wait_ready(flash, SPI_FLASH_PROG_TIMEOUT); +		if (ret) +			break; + +		cmd_len = 1; +		offset += 2; +	} + +	if (!ret) +		ret = sst_disable_writing(flash); + +	/* If there is a single trailing byte, write it out */ +	if (!ret && actual != len) +		ret = sst_byte_write(flash, offset, buf + actual); + + done: +	debug("SF: sst: program %s %zu bytes @ 0x%zx\n", +	      ret ? "failure" : "success", len, offset - actual); + +	spi_release_bus(flash->spi); +	return ret; +} + +int +sst_erase(struct spi_flash *flash, u32 offset, size_t len) +{ +	unsigned long sector_size; +	u32 start, end; +	int ret; +	u8 cmd[4]; + +	/* +	 * This function currently uses sector erase only. +	 * Probably speed things up by using bulk erase +	 * when possible. +	 */ + +	sector_size = SST_SECTOR_SIZE; + +	if (offset % sector_size) { +		debug("SF: Erase offset not multiple of sector size\n"); +		return -1; +	} + +	ret = spi_claim_bus(flash->spi); +	if (ret) { +		debug("SF: Unable to claim SPI bus\n"); +		return ret; +	} + +	cmd[0] = CMD_SST_SE; +	cmd[3] = 0; +	start = offset; +	end = start + len; + +	ret = 0; +	while (offset < end) { +		cmd[1] = offset >> 16; +		cmd[2] = offset >> 8; +		offset += sector_size; + +		debug("SF: erase %2x %2x %2x %2x (%x)\n", cmd[0], cmd[1], +		      cmd[2], cmd[3], offset); + +		ret = sst_enable_writing(flash); +		if (ret) +			break; + +		ret = spi_flash_cmd_write(flash->spi, cmd, sizeof(cmd), NULL, 0); +		if (ret) { +			debug("SF: sst page erase failed\n"); +			break; +		} + +		ret = sst_wait_ready(flash, SPI_FLASH_PAGE_ERASE_TIMEOUT); +		if (ret) +			break; +	} + +	debug("SF: sst: Successfully erased %lu bytes @ 0x%x\n", +	      len * sector_size, start); + +	spi_release_bus(flash->spi); +	return ret; +} + +static int +sst_unlock(struct spi_flash *flash) +{ +	int ret; +	u8 cmd, status; + +	ret = sst_enable_writing(flash); +	if (ret) +		return ret; + +	cmd = CMD_SST_WRSR; +	status = 0; +	ret = spi_flash_cmd_write(flash->spi, &cmd, 1, &status, 1); +	if (ret) +		debug("SF: Unable to set status byte\n"); + +	debug("SF: sst: status = %x\n", spi_w8r8(flash->spi, CMD_SST_RDSR)); + +	return ret; +} + +struct spi_flash * +spi_flash_probe_sst(struct spi_slave *spi, u8 *idcode) +{ +	const struct sst_spi_flash_params *params; +	struct sst_spi_flash *stm; +	size_t i; + +	for (i = 0; i < ARRAY_SIZE(sst_spi_flash_table); ++i) { +		params = &sst_spi_flash_table[i]; +		if (params->idcode1 == idcode[2]) +			break; +	} + +	if (i == ARRAY_SIZE(sst_spi_flash_table)) { +		debug("SF: Unsupported SST ID %02x\n", idcode[1]); +		return NULL; +	} + +	stm = malloc(sizeof(*stm)); +	if (!stm) { +		debug("SF: Failed to allocate memory\n"); +		return NULL; +	} + +	stm->params = params; +	stm->flash.spi = spi; +	stm->flash.name = params->name; + +	stm->flash.write = sst_write; +	stm->flash.erase = sst_erase; +	stm->flash.read = sst_read_fast; +	stm->flash.size = SST_SECTOR_SIZE * params->nr_sectors; + +	debug("SF: Detected %s with page size %u, total %u bytes\n", +	      params->name, SST_SECTOR_SIZE, stm->flash.size); + +	/* Flash powers up read-only, so clear BP# bits */ +	sst_unlock(&stm->flash); + +	return &stm->flash; +} diff --git a/drivers/mtd/spi/stmicro.c b/drivers/mtd/spi/stmicro.c index e7dda91a4..9b910c13f 100644 --- a/drivers/mtd/spi/stmicro.c +++ b/drivers/mtd/spi/stmicro.c @@ -133,12 +133,12 @@ static int stmicro_wait_ready(struct spi_flash *flash, unsigned long timeout)  	struct spi_slave *spi = flash->spi;  	unsigned long timebase;  	int ret; +	u8 cmd = CMD_M25PXX_RDSR;  	u8 status; -	u8 cmd[4] = { CMD_M25PXX_RDSR, 0xff, 0xff, 0xff }; -	ret = spi_xfer(spi, 32, &cmd[0], NULL, SPI_XFER_BEGIN); +	ret = spi_xfer(spi, 8, &cmd, NULL, SPI_XFER_BEGIN);  	if (ret) { -		debug("SF: Failed to send command %02x: %d\n", cmd[0], ret); +		debug("SF: Failed to send command %02x: %d\n", cmd, ret);  		return ret;  	} @@ -295,8 +295,7 @@ int stmicro_erase(struct spi_flash *flash, u32 offset, size_t len)  			break;  		} -		/* Up to 2 seconds */ -		ret = stmicro_wait_ready(flash, 2 * CONFIG_SYS_HZ); +		ret = stmicro_wait_ready(flash, SPI_FLASH_PAGE_ERASE_TIMEOUT);  		if (ret < 0) {  			debug("SF: STMicro page erase timed out\n");  			break; @@ -315,12 +314,6 @@ struct spi_flash *spi_flash_probe_stmicro(struct spi_slave *spi, u8 * idcode)  	const struct stmicro_spi_flash_params *params;  	struct stmicro_spi_flash *stm;  	unsigned int i; -	int ret; -	u8 id[3]; - -	ret = spi_flash_cmd(spi, CMD_READ_ID, id, sizeof(id)); -	if (ret) -		return NULL;  	for (i = 0; i < ARRAY_SIZE(stmicro_spi_flash_table); i++) {  		params = &stmicro_spi_flash_table[i]; @@ -330,7 +323,7 @@ struct spi_flash *spi_flash_probe_stmicro(struct spi_slave *spi, u8 * idcode)  	}  	if (i == ARRAY_SIZE(stmicro_spi_flash_table)) { -		debug("SF: Unsupported STMicro ID %02x\n", id[1]); +		debug("SF: Unsupported STMicro ID %02x\n", idcode[1]);  		return NULL;  	} |