[U-Boot] [PATCH V3 1/2] mtd/spi/spi_flash: support CMD_READ_ID=0x90 case

current code does not support spi flashes that have 0x90 read_id command, so fix this
Signed-off-by: Mikhail Kshevetskiy mikhail.kshevetskiy@gmail.com --- Change for v3: * split SPI flash fixes to separate patch series (series 2/3) Change for v2: * fix checkpatch warnings --- drivers/mtd/spi/spi_flash.c | 66 +++++++++++++++++++++++----------- drivers/mtd/spi/spi_flash_internal.h | 1 + 2 files changed, 47 insertions(+), 20 deletions(-)
diff --git a/drivers/mtd/spi/spi_flash.c b/drivers/mtd/spi/spi_flash.c index f689cc4..530b7b3 100644 --- a/drivers/mtd/spi/spi_flash.c +++ b/drivers/mtd/spi/spi_flash.c @@ -306,13 +306,44 @@ static const struct { }; #define IDCODE_LEN (IDCODE_CONT_LEN + IDCODE_PART_LEN)
+struct spi_flash *spi_analize_flash_probe(struct spi_slave *spi, + u8 *idcode, size_t idcode_len, u8 *id) +{ + struct spi_flash *flash = NULL; + int i, shift; + u8 *idp; + +#ifdef DEBUG + printf("SF: Got idcodes\n"); + print_buffer(0, idcode, 1, idcode_len, 0); +#endif + + /* count the number of continuation bytes */ + for (shift = 0, idp = idcode; + shift < idcode_len && *idp == 0x7f; + ++shift, ++idp) + continue; + + *id = *idp; + /* search the table for matches in shift and id */ + for (i = 0; i < ARRAY_SIZE(flashes); ++i) + if (flashes[i].shift == shift && flashes[i].idcode == *idp) { + /* we have a match, call probe */ + flash = flashes[i].probe(spi, idp); + if (flash) + break; + } + + return flash; +} + struct spi_flash *spi_flash_probe(unsigned int bus, unsigned int cs, unsigned int max_hz, unsigned int spi_mode) { struct spi_slave *spi; struct spi_flash *flash = NULL; - int ret, i, shift; - u8 idcode[IDCODE_LEN], *idp; + u8 cmd[4], idcode[IDCODE_LEN], id; + int ret;
spi = spi_setup_slave(bus, cs, max_hz, spi_mode); if (!spi) { @@ -331,28 +362,23 @@ struct spi_flash *spi_flash_probe(unsigned int bus, unsigned int cs, if (ret) goto err_read_id;
-#ifdef DEBUG - printf("SF: Got idcodes\n"); - print_buffer(0, idcode, 1, sizeof(idcode), 0); -#endif + flash = spi_analize_flash_probe(spi, idcode, sizeof(idcode), &id); + if (id == 0xff) { + /* try CMD_READ_ID_NEW command */ + cmd[0] = CMD_READ_ID_NEW; + spi_flash_addr(0x000000, cmd);
- /* count the number of continuation bytes */ - for (shift = 0, idp = idcode; - shift < IDCODE_CONT_LEN && *idp == 0x7f; - ++shift, ++idp) - continue; + ret = spi_flash_cmd_read(spi, cmd, sizeof(cmd), + idcode, sizeof(idcode)); + if (ret) + goto err_read_id;
- /* search the table for matches in shift and id */ - for (i = 0; i < ARRAY_SIZE(flashes); ++i) - if (flashes[i].shift == shift && flashes[i].idcode == *idp) { - /* we have a match, call probe */ - flash = flashes[i].probe(spi, idp); - if (flash) - break; - } + flash = spi_analize_flash_probe(spi, + idcode, sizeof(idcode), &id); + }
if (!flash) { - printf("SF: Unsupported manufacturer %02x\n", *idp); + printf("SF: Unsupported manufacturer %02x\n", id); goto err_manufacturer_probe; }
diff --git a/drivers/mtd/spi/spi_flash_internal.h b/drivers/mtd/spi/spi_flash_internal.h index 91e036a..b8bd5d5 100644 --- a/drivers/mtd/spi/spi_flash_internal.h +++ b/drivers/mtd/spi/spi_flash_internal.h @@ -14,6 +14,7 @@
/* Common commands */ #define CMD_READ_ID 0x9f +#define CMD_READ_ID_NEW 0x90
#define CMD_READ_ARRAY_SLOW 0x03 #define CMD_READ_ARRAY_FAST 0x0b

Current SST driver does not support well this types of flash, so introduce a new one.
This code is a combination of sst.c driver from u-boot and sst25l.c driver from linux-3.3 release. I try to make a code as close to linux driver as it was possible.
Signed-off-by: Mikhail Kshevetskiy mikhail.kshevetskiy@gmail.com --- Change for v3: * split SPI flash fixes to separate patch series (series 2/3) Change for v2: * fix checkpatch warnings * improve patch description --- drivers/mtd/spi/Makefile | 3 +- drivers/mtd/spi/spi_flash.c | 3 + drivers/mtd/spi/spi_flash_internal.h | 1 + drivers/mtd/spi/sst25l.c | 372 ++++++++++++++++++++++++++++++++++ 4 files changed, 378 insertions(+), 1 deletion(-) create mode 100644 drivers/mtd/spi/sst25l.c
diff --git a/drivers/mtd/spi/Makefile b/drivers/mtd/spi/Makefile index 90f8392..9285bf7 100644 --- a/drivers/mtd/spi/Makefile +++ b/drivers/mtd/spi/Makefile @@ -34,7 +34,8 @@ COBJS-$(CONFIG_SPI_FLASH_ATMEL) += atmel.o COBJS-$(CONFIG_SPI_FLASH_EON) += eon.o COBJS-$(CONFIG_SPI_FLASH_MACRONIX) += macronix.o COBJS-$(CONFIG_SPI_FLASH_SPANSION) += spansion.o -COBJS-$(CONFIG_SPI_FLASH_SST) += sst.o +COBJS-$(CONFIG_SPI_FLASH_SST) += sst.o +COBJS-$(CONFIG_SPI_FLASH_SST25L) += sst25l.o COBJS-$(CONFIG_SPI_FLASH_STMICRO) += stmicro.o COBJS-$(CONFIG_SPI_FLASH_WINBOND) += winbond.o COBJS-$(CONFIG_SPI_FRAM_RAMTRON) += ramtron.o diff --git a/drivers/mtd/spi/spi_flash.c b/drivers/mtd/spi/spi_flash.c index 530b7b3..d2da542 100644 --- a/drivers/mtd/spi/spi_flash.c +++ b/drivers/mtd/spi/spi_flash.c @@ -285,6 +285,9 @@ static const struct { #ifdef CONFIG_SPI_FLASH_SST { 0, 0xbf, spi_flash_probe_sst, }, #endif +#ifdef CONFIG_SPI_FLASH_SST25L + { 0, 0xbf, spi_flash_probe_sst25l, }, +#endif #ifdef CONFIG_SPI_FLASH_STMICRO { 0, 0x20, spi_flash_probe_stmicro, }, #endif diff --git a/drivers/mtd/spi/spi_flash_internal.h b/drivers/mtd/spi/spi_flash_internal.h index b8bd5d5..89d9036 100644 --- a/drivers/mtd/spi/spi_flash_internal.h +++ b/drivers/mtd/spi/spi_flash_internal.h @@ -98,6 +98,7 @@ struct spi_flash *spi_flash_probe_atmel(struct spi_slave *spi, u8 *idcode); struct spi_flash *spi_flash_probe_eon(struct spi_slave *spi, u8 *idcode); struct spi_flash *spi_flash_probe_macronix(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_sst25l(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); diff --git a/drivers/mtd/spi/sst25l.c b/drivers/mtd/spi/sst25l.c new file mode 100644 index 0000000..9d7be0d --- /dev/null +++ b/drivers/mtd/spi/sst25l.c @@ -0,0 +1,372 @@ +/* + * Driver for SST25L SPI Flash chips + * + * (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 <watchdog.h> + +#include "spi_flash_internal.h" + +#define SST25L_CMD_WRSR 0x01 /* Write status register */ +#define SST25L_CMD_WRDI 0x04 /* Write disable */ +#define SST25L_CMD_RDSR 0x05 /* Read status register */ +#define SST25L_CMD_WREN 0x06 /* Write enable */ +#define SST25L_CMD_READ 0x03 /* High speed read */ + +#define SST25L_CMD_EWSR 0x50 /* Enable write status register */ +#define SST25L_CMD_SECTOR_ERASE 0x20 /* Erase sector */ +#define SST25L_CMD_READ_ID 0x90 /* Read device ID */ +#define SST25L_CMD_AAI_PROGRAM 0xaf /* Auto address increment */ + +#define SST25L_STATUS_BUSY (1 << 0) /* Chip is busy */ +#define SST25L_STATUS_WREN (1 << 1) /* Write enabled */ +#define SST25L_STATUS_BP0 (1 << 2) /* Block protection 0 */ +#define SST25L_STATUS_BP1 (1 << 3) /* Block protection 1 */ + +struct flash_info { + const char *name; + u16 device_id; + u32 page_size; + u32 nr_pages; + u32 erase_size; +}; + +struct sst25l_spi_flash { + struct spi_flash flash; + const struct flash_info *flash_info; +}; + +#define to_sst25l_spi_flash(x) container_of(x, struct sst25l_spi_flash, flash) + +static struct flash_info sst25l_flash_info[] = { + {"sst25vf010a", 0xbf49, 256, 512, 4096}, + {"sst25lf020a", 0xbf43, 256, 1024, 4096}, + {"sst25lf040a", 0xbf44, 256, 2048, 4096}, +}; + +static inline int spi_write_sync(struct spi_slave *spi, + const u8 *data, size_t len) +{ + return spi_xfer(spi, 8 * len, data, + NULL, SPI_XFER_BEGIN | SPI_XFER_END); +} + +static int sst25l_status(struct spi_flash *flash, int *status) +{ + unsigned char cmd_resp[2]; + int err; + + cmd_resp[0] = SST25L_CMD_RDSR; + cmd_resp[1] = 0xff; + + err = spi_xfer(flash->spi, 8 * sizeof(cmd_resp), cmd_resp, cmd_resp, + SPI_XFER_BEGIN | SPI_XFER_END); + if (err < 0) + return err; + + *status = cmd_resp[1]; + return 0; +} + +static int sst25l_write_enable(struct spi_flash *flash, int enable) +{ + unsigned char command[2]; + int status, err; + + command[0] = enable ? SST25L_CMD_WREN : SST25L_CMD_WRDI; + err = spi_write_sync(flash->spi, command, 1); + if (err) + return err; + + command[0] = SST25L_CMD_EWSR; + err = spi_write_sync(flash->spi, command, 1); + if (err) + return err; + + command[0] = SST25L_CMD_WRSR; + command[1] = enable ? 0 : SST25L_STATUS_BP0 | SST25L_STATUS_BP1; + err = spi_write_sync(flash->spi, command, 2); + if (err) + return err; + + if (enable) { + err = sst25l_status(flash, &status); + if (err) + return err; + if (!(status & SST25L_STATUS_WREN)) + return -1; + } + + return 0; +} + +static int sst25l_wait_till_ready(struct spi_flash *flash, + unsigned long timeout) +{ + unsigned long timebase; + int status, err; + + timebase = get_timer(0); + do { + WATCHDOG_RESET(); + + err = sst25l_status(flash, &status); + if (err) + return err; + if (!(status & SST25L_STATUS_BUSY)) + return 0; + + } while (get_timer(timebase) < timeout); + + return -1; +} + +static int sst25l_erase_sector(struct spi_flash *flash, u32 offset) +{ + unsigned char command[4]; + int err; + + err = sst25l_write_enable(flash, 1); + if (err) + return err; + + command[0] = SST25L_CMD_SECTOR_ERASE; + command[1] = offset >> 16; + command[2] = offset >> 8; + command[3] = offset; + err = spi_write_sync(flash->spi, command, 4); + if (err) + return err; + + err = sst25l_wait_till_ready(flash, SPI_FLASH_SECTOR_ERASE_TIMEOUT); + if (err) + return err; + + return sst25l_write_enable(flash, 0); +} + +static int sst25l_erase(struct spi_flash *flash, u32 offset, size_t len) +{ + struct sst25l_spi_flash *sst25l = to_sst25l_spi_flash(flash); + const struct flash_info *flash_info = sst25l->flash_info; + u32 end = offset + len; + int err; + + /* Sanity checks */ + if (len % flash_info->erase_size) + return -1; + + if (offset % flash_info->erase_size) + return -1; + + err = sst25l_wait_till_ready(flash, SPI_FLASH_PROG_TIMEOUT); + if (err) + return err; + + while (offset < end) { + err = sst25l_erase_sector(flash, offset); + if (err) + return err; + offset += flash_info->erase_size; + } + return 0; +} + +static int sst25l_read(struct spi_flash *flash, + u32 offset, size_t len, void *data) +{ + unsigned char command[4]; + int ret; + + command[0] = SST25L_CMD_READ; + command[1] = offset >> 16; + command[2] = offset >> 8; + command[3] = offset; + + /* Wait for previous write/erase to complete */ + ret = sst25l_wait_till_ready(flash, SPI_FLASH_PROG_TIMEOUT); + if (ret) + return ret; + + ret = spi_xfer(flash->spi, 8 * sizeof(command), command, + NULL, SPI_XFER_BEGIN); + if (ret) + return ret; + + ret = spi_xfer(flash->spi, 8 * len, NULL, data, SPI_XFER_END); + if (ret) + return ret; + + return 0; +} + +static int sst25l_write(struct spi_flash *flash, + u32 offset, size_t len, const char *buf) +{ + struct sst25l_spi_flash *sst25l = to_sst25l_spi_flash(flash); + const struct flash_info *flash_info = sst25l->flash_info; + int i, j, ret, bytes, copied = 0; + unsigned char command[5]; + + if (offset % flash_info->page_size) + return -1; + + ret = sst25l_write_enable(flash, 1); + if (ret) + goto out; + + for (i = 0; i < len; i += flash_info->page_size) { + ret = sst25l_wait_till_ready(flash, SPI_FLASH_PROG_TIMEOUT); + if (ret) + goto out; + + /* Write the first byte of the page */ + command[0] = SST25L_CMD_AAI_PROGRAM; + command[1] = (offset + i) >> 16; + command[2] = (offset + i) >> 8; + command[3] = (offset + i); + command[4] = buf[i]; + ret = spi_write_sync(flash->spi, command, 5); + if (ret < 0) + goto out; + copied++; + + /* + * Write the remaining bytes using auto address + * increment mode + */ + bytes = min(flash_info->page_size, len - i); + for (j = 1; j < bytes; j++, copied++) { + ret = sst25l_wait_till_ready(flash, + SPI_FLASH_PROG_TIMEOUT); + if (ret) + goto out; + + command[1] = buf[i + j]; + ret = spi_write_sync(flash->spi, command, 2); + if (ret) + goto out; + } + } + +out: + ret = sst25l_write_enable(flash, 0); + if (ret) + return ret; + + return (copied == len) ? 0 : -1; +} + +static int sst25l_flash_erase(struct spi_flash *flash, u32 offset, size_t len) +{ + int ret; + + ret = spi_claim_bus(flash->spi); + if (ret) { + debug("SF: unable to claim SPI bus\n"); + return ret; + } + + ret = sst25l_erase(flash, offset, len); + if (ret) { + debug("SF: unable to erase spi flash sector\n"); + return ret; + } + + spi_release_bus(flash->spi); + return ret; +} + +static int sst25l_flash_read(struct spi_flash *flash, + u32 offset, size_t len, void *data) +{ + int ret; + + ret = spi_claim_bus(flash->spi); + if (ret) { + debug("SF: unable to claim SPI bus\n"); + return ret; + } + + ret = sst25l_read(flash, offset, len, data); + if (ret) { + debug("SF: unable to read spi flash\n"); + return ret; + } + + spi_release_bus(flash->spi); + return ret; +} + +static int sst25l_flash_write(struct spi_flash *flash, + u32 offset, size_t len, const void *buf) +{ + int ret; + + ret = spi_claim_bus(flash->spi); + if (ret) { + debug("SF: unable to claim SPI bus\n"); + return ret; + } + + ret = sst25l_write(flash, offset, len, buf); + if (ret) { + debug("SF: unable to write spi flash\n"); + return ret; + } + + spi_release_bus(flash->spi); + return ret; +} + +struct spi_flash * +spi_flash_probe_sst25l(struct spi_slave *spi, u8 *idcode) +{ + const struct flash_info *flash_info = NULL; + struct sst25l_spi_flash *stm; + size_t i; + u16 device_id = ((u16)idcode[0] << 8) + idcode[1]; + + for (i = 0; i < ARRAY_SIZE(sst25l_flash_info); ++i) + if (sst25l_flash_info[i].device_id == device_id) { + flash_info = &sst25l_flash_info[i]; + break; + } + + if (flash_info == NULL) { + debug("SF: Unsupported SST25L ID %04x\n", device_id); + return NULL; + } + + stm = malloc(sizeof(*stm)); + if (!stm) { + debug("SF: Failed to allocate memory\n"); + return NULL; + } + + stm->flash_info = flash_info; + stm->flash.spi = spi; + stm->flash.name = flash_info->name; + + stm->flash.read = sst25l_flash_read; + stm->flash.write = sst25l_flash_write; + stm->flash.erase = sst25l_flash_erase; + + stm->flash.page_size = flash_info->page_size; + stm->flash.sector_size = flash_info->erase_size; + stm->flash.size = flash_info->page_size * flash_info->nr_pages; + + return &stm->flash; +}

On Monday 09 July 2012 14:53:21 Mikhail Kshevetskiy wrote:
Current SST driver does not support well this types of flash, so introduce a new one.
This code is a combination of sst.c driver from u-boot and sst25l.c driver from linux-3.3 release. I try to make a code as close to linux driver as it was possible.
this needs rebasing & rewriting to the latest tree. a lot of cleanup/unification work has gone in (and there's more pending).
along those lines, i don't think it makes sense to write a new driver from scratch, nor use the linux one. check out my sf branch and it should be easy to merge your driver with the existing sst one. -mike

On Monday 09 July 2012 14:53:20 Mikhail Kshevetskiy wrote:
current code does not support spi flashes that have 0x90 read_id command, so fix this
and what SPI flashes exactly is this ? 0x9f is the JEDEC read idcode command and is not SPI flash specific, so you're adding a lot of code that is just wasted for all existing flashes. i also suspect this might break the 0xff fallback flashes (stmicro/ramtron). -mike
participants (2)
-
Mike Frysinger
-
Mikhail Kshevetskiy