[U-Boot] [PATCH 0/7] SPI flash updates for 2009.05

These patches have all been posted at one point or another, I'm just collecting them all in my tree here: git://git.denx.de/u-boot-blackfin.git sf
So if you have any complaints, now's the time !
Jean-Christophe PLAGNIOL-VILLARD (1): mtd: add some at45 spi flash support
Mike Frysinger (5): sf: drop DEBUG defines sf: add driver for SST flashes sf: stmicro: drop redundant id read sf: always read 5 bytes for the idcode sf: stmicro: use common page timeout define
Mingkai Hu (1): mtd: SPI Flash: Support the Spansion Flash
drivers/mtd/spi/Makefile | 2 + drivers/mtd/spi/atmel.c | 50 +++++- drivers/mtd/spi/spansion.c | 350 +++++++++++++++++++++++++++++++++ drivers/mtd/spi/spi_flash.c | 13 +- drivers/mtd/spi/spi_flash_internal.h | 1 + drivers/mtd/spi/sst.c | 358 ++++++++++++++++++++++++++++++++++ drivers/mtd/spi/stmicro.c | 11 +- 7 files changed, 771 insertions(+), 14 deletions(-) create mode 100644 drivers/mtd/spi/spansion.c create mode 100644 drivers/mtd/spi/sst.c

From: Mingkai Hu Mingkai.hu@freescale.com
Add MTD SPI Flash support for S25FL008A, S25FL016A, S25FL032A, S25FL064A, S25FL128P.
Signed-off-by: Mingkai Hu Mingkai.hu@freescale.com Signed-off-by: Mike Frysinger vapier@gentoo.org --- drivers/mtd/spi/Makefile | 1 + drivers/mtd/spi/spansion.c | 356 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 357 insertions(+), 0 deletions(-) create mode 100644 drivers/mtd/spi/spansion.c
diff --git a/drivers/mtd/spi/Makefile b/drivers/mtd/spi/Makefile index 3d4f892..6ca6073 100644 --- a/drivers/mtd/spi/Makefile +++ b/drivers/mtd/spi/Makefile @@ -27,6 +27,7 @@ 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_STMICRO) += stmicro.o
COBJS := $(COBJS-y) diff --git a/drivers/mtd/spi/spansion.c b/drivers/mtd/spi/spansion.c new file mode 100644 index 0000000..3dcccd3 --- /dev/null +++ b/drivers/mtd/spi/spansion.c @@ -0,0 +1,356 @@ +/* + * 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; + int ret; + u8 id[5] = {0}; + + ret = spi_flash_cmd(spi, CMD_READ_ID, id, sizeof(id)); + if (ret) + return NULL; + + jedec = id[1] << 8 | id[2]; + ext_jedec = id[3] << 8 | id[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; +}

From: Jean-Christophe PLAGNIOL-VILLARD plagnioj@jcrosoft.com
- AT45DB321D - AT45DB161D - AT45DB081D - AT45DB041D - AT45DB021D - AT45DB011D
Signed-off-by: Jean-Christophe PLAGNIOL-VILLARD plagnioj@jcrosoft.com Signed-off-by: Mike Frysinger vapier@gentoo.org --- drivers/mtd/spi/atmel.c | 48 +++++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 48 insertions(+), 0 deletions(-)
diff --git a/drivers/mtd/spi/atmel.c b/drivers/mtd/spi/atmel.c index a5f51ca..fc924a2 100644 --- a/drivers/mtd/spi/atmel.c +++ b/drivers/mtd/spi/atmel.c @@ -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,

Signed-off-by: Mike Frysinger vapier@gentoo.org Acked-by: Haavard Skinnemoen haavard.skinnemoen@atmel.com --- drivers/mtd/spi/atmel.c | 2 +- drivers/mtd/spi/spi_flash.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/mtd/spi/atmel.c b/drivers/mtd/spi/atmel.c index fc924a2..c3b936f 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> diff --git a/drivers/mtd/spi/spi_flash.c b/drivers/mtd/spi/spi_flash.c index d1d81af..f002c0e 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>

Signed-off-by: Mike Frysinger vapier@gentoo.org Acked-by: Haavard Skinnemoen haavard.skinnemoen@atmel.com --- drivers/mtd/spi/Makefile | 1 + drivers/mtd/spi/spi_flash.c | 5 + drivers/mtd/spi/spi_flash_internal.h | 1 + drivers/mtd/spi/sst.c | 358 ++++++++++++++++++++++++++++++++++ 4 files changed, 365 insertions(+), 0 deletions(-) create mode 100644 drivers/mtd/spi/sst.c
diff --git a/drivers/mtd/spi/Makefile b/drivers/mtd/spi/Makefile index 6ca6073..a71b16e 100644 --- a/drivers/mtd/spi/Makefile +++ b/drivers/mtd/spi/Makefile @@ -28,6 +28,7 @@ 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/spi_flash.c b/drivers/mtd/spi/spi_flash.c index f002c0e..21ba5f9 100644 --- a/drivers/mtd/spi/spi_flash.c +++ b/drivers/mtd/spi/spi_flash.c @@ -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 75f5900..2d020c3 100644 --- a/drivers/mtd/spi/spi_flash_internal.h +++ b/drivers/mtd/spi/spi_flash_internal.h @@ -43,4 +43,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 0000000..62236d4 --- /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; +}

The common SPI flash code reads the idcode and passes it down to the SPI flash driver, so there is no need to read it again ourselves.
Signed-off-by: Mike Frysinger vapier@gentoo.org Acked-by: Haavard Skinnemoen haavard.skinnemoen@atmel.com CC: Jason McMullan mcmullan@netapp.com CC: TsiChung Liew Tsi-Chung.Liew@freescale.com --- drivers/mtd/spi/stmicro.c | 8 +------- 1 files changed, 1 insertions(+), 7 deletions(-)
diff --git a/drivers/mtd/spi/stmicro.c b/drivers/mtd/spi/stmicro.c index e7dda91..e401cd0 100644 --- a/drivers/mtd/spi/stmicro.c +++ b/drivers/mtd/spi/stmicro.c @@ -315,12 +315,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 +324,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; }

Some SPI flash drivers like to have extended id information available (like the spansion flash), so rather than making it re-issue the ID cmd to get at the last 2 bytes, have the common code read 5 bytes rather than just 3. This also matches the Linux behavior where it always reads 5 id bytes from all flashes.
Signed-off-by: Mike Frysinger vapier@gentoo.org Acked-by: Haavard Skinnemoen haavard.skinnemoen@atmel.com CC: Mingkai Hu Mingkai.hu@freescale.com --- drivers/mtd/spi/spansion.c | 10 ++-------- drivers/mtd/spi/spi_flash.c | 6 +++--- 2 files changed, 5 insertions(+), 11 deletions(-)
diff --git a/drivers/mtd/spi/spansion.c b/drivers/mtd/spi/spansion.c index 3dcccd3..fdb7917 100644 --- a/drivers/mtd/spi/spansion.c +++ b/drivers/mtd/spi/spansion.c @@ -310,15 +310,9 @@ struct spi_flash *spi_flash_probe_spansion(struct spi_slave *spi, u8 *idcode) struct spansion_spi_flash *spsn; unsigned int i; unsigned short jedec, ext_jedec; - int ret; - u8 id[5] = {0}; - - ret = spi_flash_cmd(spi, CMD_READ_ID, id, sizeof(id)); - if (ret) - return NULL;
- jedec = id[1] << 8 | id[2]; - ext_jedec = id[3] << 8 | id[4]; + 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]; diff --git a/drivers/mtd/spi/spi_flash.c b/drivers/mtd/spi/spi_flash.c index 21ba5f9..274895a 100644 --- a/drivers/mtd/spi/spi_flash.c +++ b/drivers/mtd/spi/spi_flash.c @@ -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

Signed-off-by: Mike Frysinger vapier@gentoo.org CC: Haavard Skinnemoen haavard.skinnemoen@atmel.com --- drivers/mtd/spi/stmicro.c | 3 +-- 1 files changed, 1 insertions(+), 2 deletions(-)
diff --git a/drivers/mtd/spi/stmicro.c b/drivers/mtd/spi/stmicro.c index e401cd0..b43e9f4 100644 --- a/drivers/mtd/spi/stmicro.c +++ b/drivers/mtd/spi/stmicro.c @@ -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;

Mike Frysinger wrote:
/* Up to 2 seconds */
ret = stmicro_wait_ready(flash, 2 * CONFIG_SYS_HZ);
ret = stmicro_wait_ready(flash, SPI_FLASH_PAGE_ERASE_TIMEOUT);
50 ms is an awful lot less than 2 seconds. Sure this is safe?
Haavard

On Thursday 02 April 2009 07:20:13 Haavard Skinnemoen wrote:
Mike Frysinger wrote:
/* Up to 2 seconds */
ret = stmicro_wait_ready(flash, 2 * CONFIG_SYS_HZ);
ret = stmicro_wait_ready(flash, SPI_FLASH_PAGE_ERASE_TIMEOUT);
50 ms is an awful lot less than 2 seconds. Sure this is safe?
the 2 sec mark was copied in all spi flash drivers based on the original port rather than referring to a datasheet, and it works on my Blackfin boards that have stmicro parts.
personally i think the timeouts in the current spi flash common code is too low in general, so i'd propose we raise them. after all, the timeouts only matter in when something goes wrong, so they wouldnt be reached. and the low threshold seems like it makes the presumption of faster SPI bus speeds.
i.e. how about raising the timeout values 50x or 100x ? -mike

Mike Frysinger wrote:
On Thursday 02 April 2009 07:20:13 Haavard Skinnemoen wrote:
Mike Frysinger wrote:
/* Up to 2 seconds */
ret = stmicro_wait_ready(flash, 2 * CONFIG_SYS_HZ);
ret = stmicro_wait_ready(flash, SPI_FLASH_PAGE_ERASE_TIMEOUT);
50 ms is an awful lot less than 2 seconds. Sure this is safe?
the 2 sec mark was copied in all spi flash drivers based on the original port rather than referring to a datasheet, and it works on my Blackfin boards that have stmicro parts.
Ok, that's good enough for me.
personally i think the timeouts in the current spi flash common code is too low in general, so i'd propose we raise them. after all, the timeouts only matter in when something goes wrong, so they wouldnt be reached. and the low threshold seems like it makes the presumption of faster SPI bus speeds.
i.e. how about raising the timeout values 50x or 100x ?
I think that sounds like a good idea.
Haavard

Since timeouts are only hit when there is a problem in the system, we don't want to prematurely timeout on a functioning setup. Thus having low timeouts (in milliseconds) doesn't gain us anything in the production case, but rather increases likely hood of causing problems where none otherwise exist.
Signed-off-by: Mike Frysinger vapier@gentoo.org CC: Haavard Skinnemoen haavard.skinnemoen@atmel.com --- drivers/mtd/spi/spi_flash_internal.h | 9 ++++++--- 1 files changed, 6 insertions(+), 3 deletions(-)
diff --git a/drivers/mtd/spi/spi_flash_internal.h b/drivers/mtd/spi/spi_flash_internal.h index 2d020c3..5d1e395 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 */

Mike Frysinger wrote:
Since timeouts are only hit when there is a problem in the system, we don't want to prematurely timeout on a functioning setup. Thus having low timeouts (in milliseconds) doesn't gain us anything in the production case, but rather increases likely hood of causing problems where none otherwise exist.
Signed-off-by: Mike Frysinger vapier@gentoo.org CC: Haavard Skinnemoen haavard.skinnemoen@atmel.com
Acked-by: Haavard Skinnemoen haavard.skinnemoen@atmel.com
participants (2)
-
Haavard Skinnemoen
-
Mike Frysinger