[U-Boot] [PATCH V3 2/2] mtd/spi: add sst25l driver

Mikhail Kshevetskiy mikhail.kshevetskiy at gmail.com
Mon Jul 9 20:53:21 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 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 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