[U-Boot] [PATCH V2 5/8] mtd/spi: add sst25l driver
Mikhail Kshevetskiy
mikhail.kshevetskiy at gmail.com
Mon Jul 9 05:31:30 CEST 2012
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 at gmail.com>
---
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 at denx.de.
+ * Copyright 2008, Network Appliance Inc.
+ * Jason McMullan <mcmullan at netapp.com>
+ * 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 <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;
+}
--
1.7.10.4
More information about the U-Boot
mailing list