[U-Boot-Users] [PATCH] SPI Flash: Support the ST Microelectronics M25P80 and M25P40 SPI Flash

Jason McMullan mcmullan at netapp.com
Tue Jun 10 04:42:48 CEST 2008


This commit adds MTD support for the M25P80 (1Mx8) and the M25P40 (512kx8)
SPI Flash components from ST Microelectronics.

Tested with the M25P40, but should work for the M25P80 according
to the spec sheet.

Signed-off-by: Jason McMullan <mcmullan at netapp.com>
---
 drivers/mtd/spi/Makefile             |    1 +
 drivers/mtd/spi/spi_flash.c          |    5 +
 drivers/mtd/spi/spi_flash_internal.h |    1 +
 drivers/mtd/spi/stmicro.c            |  326 ++++++++++++++++++++++++++++++++++
 4 files changed, 333 insertions(+), 0 deletions(-)
 create mode 100644 drivers/mtd/spi/stmicro.c

diff --git a/drivers/mtd/spi/Makefile b/drivers/mtd/spi/Makefile
index af6af97..01b288a 100644
--- a/drivers/mtd/spi/Makefile
+++ b/drivers/mtd/spi/Makefile
@@ -27,6 +27,7 @@ LIB	:= $(obj)libspi_flash.a
 
 COBJS-$(CONFIG_SPI_FLASH)	+= spi_flash.o
 COBJS-$(CONFIG_SPI_FLASH_ATMEL)	+= atmel.o
+COBJS-$(CONFIG_SPI_FLASH_STMICRO) += stmicro.o
 
 COBJS	:= $(COBJS-y)
 SRCS	:= $(COBJS:.o=.c)
diff --git a/drivers/mtd/spi/spi_flash.c b/drivers/mtd/spi/spi_flash.c
index d581cb3..dd4d130 100644
--- a/drivers/mtd/spi/spi_flash.c
+++ b/drivers/mtd/spi/spi_flash.c
@@ -134,6 +134,11 @@ struct spi_flash *spi_flash_probe(unsigned int bus, unsigned int cs,
 		flash = spi_flash_probe_atmel(spi, idcode);
 		break;
 #endif
+#ifdef CONFIG_SPI_FLASH_STMICRO
+	case 0xff:
+		flash = spi_flash_probe_stmicro(spi, idcode);
+		break;
+#endif
 	default:
 		debug("SF: Unsupported manufacturer %02X\n", idcode[0]);
 		flash = NULL;
diff --git a/drivers/mtd/spi/spi_flash_internal.h b/drivers/mtd/spi/spi_flash_internal.h
index 1438050..e5f758e 100644
--- a/drivers/mtd/spi/spi_flash_internal.h
+++ b/drivers/mtd/spi/spi_flash_internal.h
@@ -43,3 +43,4 @@ int spi_flash_read_common(struct spi_flash *flash, const u8 *cmd,
 /* Manufacturer-specific probe functions */
 struct spi_flash *spi_flash_probe_spansion(struct spi_slave *spi, u8 *idcode);
 struct spi_flash *spi_flash_probe_atmel(struct spi_slave *spi, u8 *idcode);
+struct spi_flash *spi_flash_probe_stmicro(struct spi_slave *spi, u8 *idcode);
diff --git a/drivers/mtd/spi/stmicro.c b/drivers/mtd/spi/stmicro.c
new file mode 100644
index 0000000..cb5a971
--- /dev/null
+++ b/drivers/mtd/spi/stmicro.c
@@ -0,0 +1,326 @@
+/*
+ * $Id$
+ *
+ * Copyright 2008, Network Appliance Inc.
+ * Author: Jason McMullan <mcmullan at netapp.com>
+ *
+ */
+
+#include <common.h>
+#include <malloc.h>
+#include <spi_flash.h>
+
+#include "spi_flash_internal.h"
+
+/* M25Pxx-specific commands */
+#define CMD_M25PXX_WREN		0x06	/* Write Enable */
+#define CMD_M25PXX_WRDI		0x04	/* Write Disable */
+#define CMD_M25PXX_RDSR		0x05	/* Read Status Register */
+#define CMD_M25PXX_WRSR		0x01	/* Write Status Register */
+#define CMD_M25PXX_READ		0x03	/* Read Data Bytes */
+#define CMD_M25PXX_FAST_READ	0x0b	/* Read Data Bytes at Higher Speed */
+#define CMD_M25PXX_PP		0x02	/* Page Program */
+#define CMD_M25PXX_SE		0xd8	/* Sector Erase */
+#define CMD_M25PXX_BE		0xc7	/* Bulk Erase */
+#define CMD_M25PXX_DP		0xb9	/* Deep Power-down */
+#define CMD_M25PXX_RES		0xab	/* Release from DP, and Read Signature */
+
+#define STM_ID_M25P40		0x12
+#define STM_ID_M25P80		0x13
+
+#define STMICRO_SR_WIP		(1 << 0)	/* Write-in-Progress */
+
+struct stmicro_spi_flash_params {
+	uint8_t		idcode1;
+	uint8_t		esig;
+	/* Log2 of page size in power-of-two mode */
+	uint8_t		l2_page_size;
+	uint16_t	pages_per_sector;
+	uint8_t		nr_sectors;
+	const char	*name;
+};
+
+struct stmicro_spi_flash {
+	const struct stmicro_spi_flash_params *params;
+	struct spi_flash flash;
+};
+
+static inline struct stmicro_spi_flash *
+to_stmicro_spi_flash(struct spi_flash *flash)
+{
+	return container_of(flash, struct stmicro_spi_flash, flash);
+}
+
+static const struct stmicro_spi_flash_params stmicro_spi_flash_table[] = {
+	{
+		.idcode1		= 0xff,
+		.esig			= STM_ID_M25P40,
+		.l2_page_size		= 8,
+		.pages_per_sector	= 256,
+		.nr_sectors		= 8,
+		.name			= "M25P40",
+	},
+	{
+		.idcode1		= 0xff,
+		.esig			= STM_ID_M25P80,
+		.l2_page_size		= 8,
+		.pages_per_sector	= 256,
+		.nr_sectors		= 16,
+		.name			= "M25P80",
+	},
+};
+
+static int stmicro_wait_ready(struct spi_flash *flash, unsigned long timeout)
+{
+	struct spi_slave *spi = flash->spi;
+	unsigned long timebase;
+	int ret;
+	u8 status;
+	u8 cmd[4] = { CMD_M25PXX_RDSR, 0xff, 0xff, 0xff };
+
+	ret = spi_xfer(spi, 32, &cmd[0], NULL, SPI_XFER_BEGIN);
+	if (ret) {
+		debug("SF: Failed to send command %02x: %d\n", cmd, ret);
+		return ret;
+	}
+
+	timebase = get_timer(0);
+	do {
+		ret = spi_xfer(spi, 8, NULL, &status, 0);
+		if (ret)
+			return -1;
+
+		if ((status & STMICRO_SR_WIP) == 0)
+			break;
+
+	} while (1 || get_timer(timebase) < timeout);
+
+	spi_xfer(spi, 0, NULL, NULL, SPI_XFER_END);
+
+	if ((status & STMICRO_SR_WIP) == 0)
+		return 0;
+
+	/* Timed out */
+	return -1;
+}
+
+/*
+ * Assemble the address part of a command for STMicro devices in
+ * non-power-of-two page size mode.
+ */
+static void stmicro_build_address(struct stmicro_spi_flash *stm, u8 *cmd, u32 offset)
+{
+	unsigned long page_addr;
+	unsigned long byte_addr;
+	unsigned long page_size;
+	unsigned int page_shift;
+
+	/*
+	 * The "extra" space per page is the power-of-two page size
+	 * divided by 32.
+	 */
+	page_shift = stm->params->l2_page_size;
+	page_size = (1 << page_shift);
+	page_addr = offset / page_size;
+	byte_addr = offset % page_size;
+
+	cmd[0] = page_addr >> (16 - page_shift);
+	cmd[1] = page_addr << (page_shift - 8) | (byte_addr >> 8);
+	cmd[2] = byte_addr;
+}
+
+static int stmicro_read_fast(struct spi_flash *flash,
+		u32 offset, size_t len, void *buf)
+{
+	struct stmicro_spi_flash *stm = to_stmicro_spi_flash(flash);
+	u8 cmd[5];
+
+	cmd[0] = CMD_READ_ARRAY_FAST;
+	stmicro_build_address(stm, cmd + 1, offset);
+	cmd[4] = 0x00;
+
+	return spi_flash_read_common(flash, cmd, sizeof(cmd), buf, len);
+}
+
+static int stmicro_write(struct spi_flash *flash,
+		u32 offset, size_t len, const void *buf)
+{
+	struct stmicro_spi_flash *stm = to_stmicro_spi_flash(flash);
+	unsigned long page_addr;
+	unsigned long byte_addr;
+	unsigned long page_size;
+	unsigned int page_shift;
+	size_t chunk_len;
+	size_t actual;
+	int ret;
+	u8 cmd[4];
+
+	page_shift = stm->params->l2_page_size;
+	page_size = (1 << page_shift);
+	page_addr = offset / page_size;
+	byte_addr = offset % page_size;
+
+	ret = spi_claim_bus(flash->spi);
+	if (ret) {
+		debug("SF: Unable to claim SPI bus\n");
+		return ret;
+	}
+
+	for (actual = 0; actual < len; actual += chunk_len) {
+		chunk_len = min(len - actual, page_size - byte_addr);
+
+		cmd[0] = CMD_M25PXX_PP;
+		cmd[1] = page_addr >> (16 - page_shift);
+		cmd[2] = page_addr << (page_shift - 8) | (byte_addr >> 8);
+		cmd[3] = byte_addr;
+		debug("PP: 0x%p => cmd = { 0x%02x 0x%02x%02x%02x } chunk_len = %d\n",
+			buf + actual,
+			cmd[0], cmd[1], cmd[2], cmd[3], chunk_len);
+
+		ret = spi_flash_cmd(flash->spi, CMD_M25PXX_WREN, NULL, 0);
+		if (ret < 0) {
+			debug("SF: Enabling Write failed\n");
+			goto out;
+		}
+
+		ret = spi_flash_cmd_write(flash->spi, cmd, 4,
+				buf + actual, chunk_len);
+		if (ret < 0) {
+			debug("SF: STMicro Page Program failed\n");
+			goto out;
+		}
+
+		ret = stmicro_wait_ready(flash, SPI_FLASH_PROG_TIMEOUT);
+		if (ret < 0) {
+			debug("SF: STMicro page programming timed out\n");
+			goto out;
+		}
+
+		page_addr++;
+		byte_addr = 0;
+	}
+
+	debug("SF: STMicro: Successfully programmed %u bytes @ 0x%x\n",
+			len, offset);
+	ret = 0;
+
+out:
+	spi_release_bus(flash->spi);
+	return ret;
+}
+
+int stmicro_erase(struct spi_flash *flash, u32 offset, size_t len)
+{
+	struct stmicro_spi_flash *stm = to_stmicro_spi_flash(flash);
+	unsigned long sector_size;
+	unsigned int page_shift;
+	size_t actual;
+	int ret;
+	u8 cmd[4];
+
+	/*
+	 * This function currently uses sector erase only.
+	 * probably speed things up by using bulk erase
+	 * when possible.
+	 */
+
+	page_shift = stm->params->l2_page_size;
+	sector_size = (1 << page_shift) * stm->params->pages_per_sector;
+
+	if (offset % sector_size || len % sector_size) {
+		debug("SF: Erase offset/length not multiple of sector size\n");
+		return -1;
+	}
+
+	len /= sector_size;
+	cmd[0] = CMD_M25PXX_SE;
+	cmd[2] = 0x00;
+	cmd[3] = 0x00;
+
+	ret = spi_claim_bus(flash->spi);
+	if (ret) {
+		debug("SF: Unable to claim SPI bus\n");
+		return ret;
+	}
+
+	for (actual = 0; actual < len; actual++) {
+		cmd[1] = (offset / sector_size) + actual;
+
+		ret = spi_flash_cmd(flash->spi, CMD_M25PXX_WREN, NULL, 0);
+		if (ret < 0) {
+			debug("SF: Enabling Write failed\n");
+			goto out;
+		}
+
+		ret = spi_flash_cmd_write(flash->spi, cmd, 4, NULL, 0);
+		if (ret < 0) {
+			debug("SF: STMicro page erase failed\n");
+			goto out;
+		}
+
+		ret = stmicro_wait_ready(flash, 2 * CFG_HZ);	/* Up to 2 seconds */
+		if (ret < 0) {
+			debug("SF: STMicro page erase timed out\n");
+			goto out;
+		}
+	}
+
+	debug("SF: STMicro: Successfully erased %u bytes @ 0x%x\n",
+			len * sector_size, offset);
+	ret = 0;
+
+out:
+	spi_release_bus(flash->spi);
+	return ret;
+}
+
+struct spi_flash *spi_flash_probe_stmicro(struct spi_slave *spi, u8 *idcode)
+{
+	const struct stmicro_spi_flash_params *params;
+	unsigned long page_size;
+	struct stmicro_spi_flash *stm;
+	unsigned int i;
+	int ret;
+	u8 id[4];
+
+	ret = spi_flash_cmd(spi, CMD_M25PXX_RES, id, 4);
+	if (ret)
+		return NULL;
+
+	for (i = 0; i < ARRAY_SIZE(stmicro_spi_flash_table); i++) {
+		params = &stmicro_spi_flash_table[i];
+		if (params->idcode1 == idcode[1] &&
+		    params->esig == id[3])
+			break;
+	}
+
+	if (i == ARRAY_SIZE(stmicro_spi_flash_table)) {
+		debug("SF: Unsupported STMicro ID %02x\n",
+				id[3]);
+		return NULL;
+	}
+
+	stm = malloc(sizeof(struct stmicro_spi_flash));
+	if (!stm) {
+		debug("SF: Failed to allocate memory\n");
+		return NULL;
+	}
+
+	stm->params = params;
+	stm->flash.spi = spi;
+	stm->flash.name = params->name;
+
+	/* Assuming power-of-two page size initially. */
+	page_size = 1 << params->l2_page_size;
+
+	stm->flash.write = stmicro_write;
+	stm->flash.erase = stmicro_erase;
+	stm->flash.read = stmicro_read_fast;
+	stm->flash.size = page_size * params->pages_per_sector
+				* params->nr_sectors;
+
+	debug("SF: Detected %s with page size %u, total %u bytes\n",
+			params->name, page_size, stm->flash.size);
+
+	return &stm->flash;
+}
-- 
1.5.4.3





More information about the U-Boot mailing list