From mboxrd@z Thu Jan 1 00:00:00 1970 From: Mikhail Kshevetskiy Date: Tue, 26 Jun 2012 14:33:13 +0400 Subject: [U-Boot] [PATCH 5/6] mtd/spi: add sst25l driver In-Reply-To: <1340706794-2806-1-git-send-email-mikhail.kshevetskiy@gmail.com> References: <1340706794-2806-1-git-send-email-mikhail.kshevetskiy@gmail.com> Message-ID: <1340706794-2806-5-git-send-email-mikhail.kshevetskiy@gmail.com> List-Id: MIME-Version: 1.0 Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit To: u-boot@lists.denx.de current SST driver does not support well this types of flash, so use linux-3.3 code as a base. Signed-off-by: Mikhail Kshevetskiy --- 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 at denx.de. + * Copyright 2008, Network Appliance Inc. + * Jason McMullan + * Copyright (C) 2004-2007 Freescale Semiconductor, Inc. + * TsiChung Liew (Tsi-Chung.Liew at freescale.com) + * Copyright (c) 2008-2009 Analog Devices Inc. + * + * Licensed under the GPL-2 or later. + */ + +#include +#include +#include +#include + +#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; +} -- 1.7.10