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 <mikhail.kshevets...@gmail.com>
---
 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             |  363 ++++++++++++++++++++++++++++++++++
 4 files changed, 369 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 5c931da..846b9b0 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..6c2cc0f
--- /dev/null
+++ b/drivers/mtd/spi/sst25l.c
@@ -0,0 +1,363 @@
+/*
+ * Driver for SST25L SPI Flash chips
+ *
+ * (C) Copyright 2000-2002
+ * Wolfgang Denk, DENX Software Engineering, w...@denx.de.
+ * Copyright 2008, Network Appliance Inc.
+ * Jason McMullan <mcmul...@netapp.com>
+ * Copyright (C) 2004-2007 Freescale Semiconductor, Inc.
+ * TsiChung Liew (tsi-chung.l...@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

_______________________________________________
U-Boot mailing list
U-Boot@lists.denx.de
http://lists.denx.de/mailman/listinfo/u-boot

Reply via email to