[U-Boot] [PATCH 1/4] spi_nor: add m25p32 spi nor driver

Terry Lv r65388 at freescale.com
Tue Aug 14 12:58:31 CEST 2012


This driver is ported from kernel and can handle a series of spi nor
flash including m25pxx, m45pxx, w25xxx, cat25xxx, sst25vf0xxx,
sst25wfxxx, s25xxx, mx25xxx, at25xxx, at26xxx, en25xxx, xxxs33b.

Signed-off-by: Terry Lv <r65388 at freescale.com>
---
 drivers/mtd/spi/Makefile             |    1 +
 drivers/mtd/spi/m25p80.c             |  896 ++++++++++++++++++++++++++++++++++
 drivers/mtd/spi/spi_flash.c          |   10 +
 drivers/mtd/spi/spi_flash_internal.h |    1 +
 4 files changed, 908 insertions(+), 0 deletions(-)
 create mode 100644 drivers/mtd/spi/m25p80.c

diff --git a/drivers/mtd/spi/Makefile b/drivers/mtd/spi/Makefile
index 57112af..1621a61 100644
--- a/drivers/mtd/spi/Makefile
+++ b/drivers/mtd/spi/Makefile
@@ -28,6 +28,7 @@ LIB	:= $(obj)libspi_flash.o
 COBJS-$(CONFIG_SPI_FLASH)	+= spi_flash.o
 COBJS-$(CONFIG_SPI_FLASH_ATMEL)	+= atmel.o
 COBJS-$(CONFIG_SPI_FLASH_EON)	+= eon.o
+COBJS-$(CONFIG_SPI_FLASH_M25P80)	+= m25p80.o
 COBJS-$(CONFIG_SPI_FLASH_MACRONIX)	+= macronix.o
 COBJS-$(CONFIG_SPI_FLASH_SPANSION)	+= spansion.o
 COBJS-$(CONFIG_SPI_FLASH_SST)	+= sst.o
diff --git a/drivers/mtd/spi/m25p80.c b/drivers/mtd/spi/m25p80.c
new file mode 100644
index 0000000..1d87793
--- /dev/null
+++ b/drivers/mtd/spi/m25p80.c
@@ -0,0 +1,896 @@
+/*
+ * Copyright (C) 2012 Freescale Semiconductor, Inc.
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+/*
+ * MTD SPI driver for ST M25Pxx (and similar) serial flash chips
+ *
+ * Author: Mike Lavender, mike at steroidmicros.com
+ *
+ * Copyright (c) 2005, Intec Automation Inc.
+ *
+ * Some parts are based on lart.c by Abraham Van Der Merwe
+ *
+ * Cleaned up and generalized based on mtd_dataflash.c
+ *
+ * This code is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include <common.h>
+#include <spi.h>
+#include <spi_flash.h>
+#include <asm/errno.h>
+#include <linux/types.h>
+#include <malloc.h>
+
+#include "spi_flash_internal.h"
+
+/* CFI */
+#define CFI_MFR_ANY             0xFFFF
+#define CFI_ID_ANY              0xFFFF
+#define CFI_MFR_CONTINUATION    0x007F
+
+#define CFI_MFR_AMD             0x0001
+#define CFI_MFR_AMIC            0x0037
+#define CFI_MFR_ATMEL           0x001F
+#define CFI_MFR_EON             0x001C
+#define CFI_MFR_FUJITSU         0x0004
+#define CFI_MFR_HYUNDAI         0x00AD
+#define CFI_MFR_INTEL           0x0089
+#define CFI_MFR_MACRONIX        0x00C2
+#define CFI_MFR_NEC             0x0010
+#define CFI_MFR_PMC             0x009D
+#define CFI_MFR_SAMSUNG         0x00EC
+#define CFI_MFR_SHARP           0x00B0
+#define CFI_MFR_SST             0x00BF
+#define CFI_MFR_ST              0x0020 /* STMicroelectronics */
+#define CFI_MFR_TOSHIBA         0x0098
+#define CFI_MFR_WINBOND         0x00DA
+
+/* Flash opcodes. */
+#define	OPCODE_WREN		0x06	/* Write enable */
+#define	OPCODE_RDSR		0x05	/* Read status register */
+#define	OPCODE_WRSR		0x01	/* Write status register 1 byte */
+#define	OPCODE_NORM_READ	0x03	/* Read data bytes (low frequency) */
+#define	OPCODE_FAST_READ	0x0b	/* Read data bytes (high frequency) */
+#define	OPCODE_PP		0x02	/* Page program (up to 256 bytes) */
+#define	OPCODE_BE_4K		0x20	/* Erase 4KiB block */
+#define	OPCODE_BE_32K		0x52	/* Erase 32KiB block */
+#define	OPCODE_CHIP_ERASE	0xc7	/* Erase whole flash chip */
+#define	OPCODE_SE		0xd8	/* Sector erase (usually 64KiB) */
+#define	OPCODE_RDID		0x9f	/* Read JEDEC ID */
+
+/* Used for SST flashes only. */
+#define	OPCODE_BP		0x02	/* Byte program */
+#define	OPCODE_WRDI		0x04	/* Write disable */
+#define	OPCODE_AAI_WP		0xad	/* Auto address increment word program */
+
+/* Used for Macronix flashes only. */
+#define	OPCODE_EN4B		0xb7	/* Enter 4-byte mode */
+#define	OPCODE_EX4B		0xe9	/* Exit 4-byte mode */
+
+/* Used for Spansion flashes only. */
+#define	OPCODE_BRWR		0x17	/* Bank register write */
+
+/* Status Register bits. */
+#define	SR_WIP			1	/* Write in progress */
+#define	SR_WEL			2	/* Write enable latch */
+/* meaning of other SR_* bits may differ between vendors */
+#define	SR_BP0			4	/* Block protect 0 */
+#define	SR_BP1			8	/* Block protect 1 */
+#define	SR_BP2			0x10	/* Block protect 2 */
+#define	SR_SRWD			0x80	/* SR write protect */
+
+/* Define max times to check status register before we give up. */
+#define	MAX_READY_WAIT_JIFFIES	(40 * HZ)	/* M25P16 specs 40s max chip erase */
+#define	MAX_CMD_SIZE		5
+
+#ifdef CONFIG_M25PXX_USE_FAST_READ
+#define OPCODE_READ		OPCODE_FAST_READ
+#define FAST_READ_DUMMY_BYTE	1
+#else
+#define OPCODE_READ		OPCODE_NORM_READ
+#define FAST_READ_DUMMY_BYTE	0
+#endif
+
+#define JEDEC_MFR(_jedec_id)	((_jedec_id) >> 16)
+
+/****************************************************************************/
+
+struct m25p_spi_flash {
+	struct spi_flash	flash;
+	u16			page_size;
+	u16			addr_width;
+	u32			erase_size;
+	u8			erase_opcode;
+};
+
+static inline struct m25p_spi_flash *to_m25p_spi_flash(struct spi_flash *flash)
+{
+	return container_of(flash, struct m25p_spi_flash, flash);
+}
+
+/****************************************************************************/
+
+/*
+ * Internal helper functions
+ */
+
+/*
+ * Read the status register, returning its value in the location
+ * Return the status register value.
+ * Returns negative if error occurred.
+ */
+static int read_sr(struct spi_flash *flash)
+{
+	int ret;
+	u8 code = OPCODE_RDSR;
+	u8 stat = 0;
+
+	ret = spi_flash_cmd(flash->spi, code, &stat, 1);
+	if (ret)
+		return ret;
+
+	debug("flash status: 0x%x\n", stat);
+
+	return (int)stat;
+}
+
+/*
+ * Write status register 1 byte
+ * Returns negative if error occurred.
+ */
+static int write_sr(struct spi_flash *flash, u8 val)
+{
+	u8 cmd[MAX_CMD_SIZE + FAST_READ_DUMMY_BYTE] = { 0 };
+
+	cmd[0] = OPCODE_WRSR;
+	cmd[1] = val;
+
+	return spi_flash_cmd_write(flash->spi, cmd, 2, NULL, 0);
+}
+
+/*
+ * Set write enable latch with Write Enable command.
+ * Returns negative if error occurred.
+ */
+static inline int write_enable(struct spi_flash *flash)
+{
+	u8 code = OPCODE_WREN;
+
+	return spi_flash_cmd(flash->spi, code, NULL, 0);
+}
+
+/*
+ * Send write disble instruction to the chip.
+ */
+static inline int write_disable(struct spi_flash *flash)
+{
+	u8 code = OPCODE_WRDI;
+
+	return spi_flash_cmd(flash->spi, code, NULL, 0);
+}
+
+/*
+ * Enable/disable 4-byte addressing mode.
+ */
+static inline int set_4byte(struct spi_flash *flash, u32 jedec_id, int enable)
+{
+	u8 cmd[MAX_CMD_SIZE + FAST_READ_DUMMY_BYTE] = { 0 };
+
+	switch (JEDEC_MFR(jedec_id)) {
+	case CFI_MFR_MACRONIX:
+		cmd[0] = enable ? OPCODE_EN4B : OPCODE_EX4B;
+		return spi_flash_cmd_write(flash->spi, cmd, 1, NULL, 0);
+	default:
+		/* Spansion style */
+		cmd[0] = OPCODE_BRWR;
+		cmd[1] = enable << 7;
+		return spi_flash_cmd_write(flash->spi, cmd, 2, NULL, 0);
+	}
+}
+
+/*
+ * Service routine to read status register until ready, or timeout occurs.
+ * Returns non-zero if error.
+ */
+static int wait_till_ready(struct spi_flash *flash)
+{
+	int sr;
+	int times = 10000;
+
+	do {
+		sr = read_sr(flash);
+		if (sr < 0)
+			break;
+		else if (!(sr & SR_WIP))
+			return 0;
+
+		udelay(100);
+	} while (times--);
+
+	return 1;
+}
+
+/*
+ * Erase the whole flash memory
+ *
+ * Returns 0 if successful, non-zero otherwise.
+ */
+static int erase_chip(struct spi_flash *flash)
+{
+	s32 ret = 0;
+	u8 cmd[MAX_CMD_SIZE + FAST_READ_DUMMY_BYTE] = { 0 };
+
+	/* Wait until finished previous write command. */
+	if (wait_till_ready(flash)) {
+		printf("SF: timeout for ready!\n");
+		return 1;
+	}
+
+	/* Send write enable, then erase commands. */
+	ret = write_enable(flash);
+	if (ret < 0) {
+		printf("SF: Write enable failed\n");
+		return ret;
+	}
+
+	/* Set up command buffer. */
+	cmd[0] = OPCODE_CHIP_ERASE;
+
+	ret = spi_flash_cmd_write(flash->spi, cmd, 1, NULL, 0);
+	if (ret)
+		printf("SF: whole chip erase failed\n");
+
+	return 0;
+}
+
+static void m25p_addr2cmd(struct m25p_spi_flash *flash,
+		unsigned int addr, u8 *cmd)
+{
+	/* opcode is in cmd[0] */
+	cmd[1] = addr >> (flash->addr_width * 8 -  8);
+	cmd[2] = addr >> (flash->addr_width * 8 - 16);
+	cmd[3] = addr >> (flash->addr_width * 8 - 24);
+	cmd[4] = addr >> (flash->addr_width * 8 - 32);
+}
+
+static int m25p_cmdsz(struct m25p_spi_flash *flash)
+{
+	return 1 + flash->addr_width;
+}
+
+/*
+ * Erase one sector of flash memory at offset ``offset'' which is any
+ * address within the sector which should be erased.
+ *
+ * Returns 0 if successful, non-zero otherwise.
+ */
+static int erase_sector(struct spi_flash *flash, u32 offset)
+{
+	s32 ret = 0;
+	u8 cmd[MAX_CMD_SIZE + FAST_READ_DUMMY_BYTE] = { 0 };
+	struct m25p_spi_flash *m25p = to_m25p_spi_flash(flash);
+
+	/* Wait until finished previous write command. */
+	if (wait_till_ready(flash)) {
+		printf("SF: timeout for ready: 0x%x!\n", offset);
+		return 1;
+	}
+
+	/* Send write enable, then erase commands. */
+	ret = write_enable(flash);
+	if (ret < 0) {
+		printf("SF: Write enable failed\n");
+		return ret;
+	}
+
+	/* Set up command buffer. */
+	cmd[0] = m25p->erase_opcode;
+	m25p_addr2cmd(m25p, offset, cmd);
+
+	ret = spi_flash_cmd_write(flash->spi, cmd, m25p_cmdsz(m25p),
+			NULL, 0);
+	if (ret)
+		printf("SF: page erase failed\n");
+
+	return 0;
+}
+
+/*
+ * Erase an address range on the flash chip.  The address range may extend
+ * one or more erase sectors.  Return an error is there is a problem erasing.
+ */
+static int m25p80_erase(struct spi_flash *flash, u32 offset, size_t len)
+{
+	struct m25p_spi_flash *m25p = to_m25p_spi_flash(flash);
+	int ret = 0;
+
+	/* sanity checks */
+	if (offset + len > flash->size) {
+		printf("Size out of range!\n");
+		return -EINVAL;
+	}
+
+	if (len % m25p->erase_size) {
+		printf("Erase len not 0x%x aligned!", m25p->erase_size);
+		return -EINVAL;
+	}
+
+	ret = spi_claim_bus(flash->spi);
+	if (ret) {
+		printf("SF: Unable to claim SPI bus\n");
+		return ret;
+	}
+
+	/* whole-chip erase? */
+	if (len == flash->size) {
+		ret = erase_chip(flash);
+	} else {
+		while (len) {
+			ret = erase_sector(flash, offset);
+			if (ret)
+				break;
+
+			offset += m25p->erase_size;
+			len -= m25p->erase_size;
+		}
+	}
+
+	spi_release_bus(flash->spi);
+	return ret;
+}
+
+/*
+ * Read an address range from the flash chip.  The address range
+ * may be any size provided it is within the physical boundaries.
+ */
+static int m25p80_read(struct spi_flash *flash, u32 offset, size_t len,
+			void *buf)
+{
+	struct m25p_spi_flash *m25p = to_m25p_spi_flash(flash);
+	u8 cmd[MAX_CMD_SIZE + FAST_READ_DUMMY_BYTE] = { 0 };
+	int cmd_sz;
+
+	/* sanity checks */
+	if (!len)
+		return 0;
+
+	if (offset + len > flash->size) {
+		printf("Size out of range!\n");
+		return -EINVAL;
+	}
+
+	/* NOTE:
+	 * OPCODE_FAST_READ (if available) is faster.
+	 * Should add 1 byte DUMMY_BYTE.
+	 */
+	cmd_sz = m25p_cmdsz(m25p) + FAST_READ_DUMMY_BYTE;
+
+	/* Wait till previous write/erase is done. */
+	if (wait_till_ready(flash)) {
+		printf("SF: timeout for ready!\n");
+		/* REVISIT status return?? */
+		return 1;
+	}
+
+	/* FIXME switch to OPCODE_FAST_READ.  It's required for higher
+	 * clocks; and at this writing, every chip this driver handles
+	 * supports that opcode.
+	 */
+
+	/* Set up the write data buffer. */
+	cmd[0] = OPCODE_READ;
+	m25p_addr2cmd(m25p, offset, cmd);
+
+	return spi_flash_read_common(flash, cmd, cmd_sz, buf, len);
+}
+
+/*
+ * Write an address range to the flash chip.  Data must be written in
+ * FLASH_PAGESIZE chunks.  The address range may be any size provided
+ * it is within the physical boundaries.
+ */
+static int m25p80_write(struct spi_flash *flash, u32 offset, size_t len,
+			const void *buf)
+{
+	struct m25p_spi_flash *m25p = to_m25p_spi_flash(flash);
+	u32 page_offset, page_size;
+	u8 cmd[MAX_CMD_SIZE + FAST_READ_DUMMY_BYTE] = { 0 };
+	int cmd_sz, ret;
+
+	/* sanity checks */
+	if (!len)
+		return 0;
+
+	if (offset + len > flash->size) {
+		printf("Size out of range!\n");
+		return -EINVAL;
+	}
+
+	cmd_sz = m25p_cmdsz(m25p);
+
+	ret = spi_claim_bus(flash->spi);
+	if (ret) {
+		printf("SF: Unable to claim SPI bus\n");
+		return ret;
+	}
+	/* Wait until finished previous write command. */
+	if (wait_till_ready(flash)) {
+		printf("SF: timeout for ready!\n");
+		goto done;
+	}
+
+	ret = write_enable(flash);
+	if (ret < 0) {
+		printf("SF: Write enable failed\n");
+		goto done;
+	}
+
+	/* Set up the opcode in the write buffer. */
+	cmd[0] = OPCODE_PP;
+	m25p_addr2cmd(m25p, offset, cmd);
+
+	page_offset = offset & (m25p->page_size - 1);
+
+	/* do all the bytes fit onto one page? */
+	if (page_offset + len <= m25p->page_size) {
+		ret = spi_flash_cmd_write(flash->spi, cmd, cmd_sz,
+				buf, len);
+		if (ret)
+			printf("SF: m25p program failed\n");
+	} else {
+		u32 i;
+
+		/* the size of data remaining on the first page */
+		page_size = m25p->page_size - page_offset;
+
+		ret = spi_flash_cmd_write(flash->spi, cmd, cmd_sz,
+				buf, page_size);
+		if (ret)
+			printf("SF: m25p program failed\n");
+		/* write everything in flash->page_size chunks */
+		for (i = page_size; i < len; i += page_size) {
+			page_size = len - i;
+			if (page_size > m25p->page_size)
+				page_size = m25p->page_size;
+
+			/* write the next page to flash */
+			m25p_addr2cmd(m25p, offset + i, cmd);
+
+			if (wait_till_ready(flash)) {
+				printf("SF: timeout for ready: 0x%x!\n",
+					(offset + i));
+				goto done;
+			}
+
+			ret = write_enable(flash);
+			if (ret < 0) {
+				printf("SF: Write enable failed\n");
+				goto done;
+			}
+
+			ret = spi_flash_cmd_write(flash->spi, cmd, cmd_sz,
+					buf + i, page_size);
+			if (ret)
+				printf("SF: m25p program failed\n");
+		}
+	}
+
+done:
+	spi_release_bus(flash->spi);
+	return ret;
+}
+
+static int sst_write(struct spi_flash *flash, u32 offset, size_t len,
+		const void *buf)
+{
+	struct m25p_spi_flash *m25p = to_m25p_spi_flash(flash);
+	size_t actual;
+	int cmd_sz, ret;
+	u8 cmd[MAX_CMD_SIZE + FAST_READ_DUMMY_BYTE] = { 0 };
+
+	/* sanity checks */
+	if (!len)
+		return 0;
+
+	if (offset + len > flash->size)
+		return -EINVAL;
+
+	ret = spi_claim_bus(flash->spi);
+	if (ret) {
+		printf("SF: Unable to claim SPI bus\n");
+		return ret;
+	}
+	/* Wait until finished previous write command. */
+	ret = wait_till_ready(flash);
+	if (ret) {
+		printf("SF: timeout for ready!\n");
+		goto time_out;
+	}
+
+	ret = write_enable(flash);
+	if (ret < 0) {
+		printf("SF: Write enable failed\n");
+		goto time_out;
+	}
+
+	actual = offset % 2;
+	/* Start write from odd address. */
+	if (actual) {
+		cmd[0] = OPCODE_BP;
+		m25p_addr2cmd(m25p, offset, cmd);
+		cmd_sz = m25p_cmdsz(m25p);
+
+		/* write one byte. */
+		ret = spi_flash_cmd_write(flash->spi, cmd, cmd_sz,
+				buf, 1);
+		ret = wait_till_ready(flash);
+		if (ret) {
+			printf("SF: timeout for ready!\n");
+			goto time_out;
+		}
+	}
+	offset += actual;
+
+	cmd[0] = OPCODE_AAI_WP;
+	m25p_addr2cmd(m25p, offset, cmd);
+
+	/* Write out most of the data here. */
+	cmd_sz = m25p_cmdsz(m25p);
+	for (; actual < len - 1; actual += 2) {
+		ret = spi_flash_cmd_write(flash->spi, cmd, cmd_sz,
+				buf + actual, 2);
+		if (ret) {
+			printf("SF: sst word program failed\n");
+			break;
+		}
+		ret = wait_till_ready(flash);
+		if (ret) {
+			printf("SF: timeout for ready!\n");
+			goto time_out;
+		}
+		cmd_sz = 1;
+		offset += 2;
+	}
+	write_disable(flash);
+	ret = wait_till_ready(flash);
+	if (ret) {
+		printf("SF: timeout for ready!\n");
+		goto time_out;
+	}
+
+	/* Write out trailing byte if it exists. */
+	if (actual != len) {
+		write_enable(flash);
+		cmd[0] = OPCODE_BP;
+		m25p_addr2cmd(m25p, offset, cmd);
+		cmd_sz = m25p_cmdsz(m25p);
+
+		ret = spi_flash_cmd_write(flash->spi, cmd, cmd_sz,
+				buf + actual, 1);
+		if (ret) {
+			printf("SF: m25p trailing byte program failed\n");
+			goto time_out;
+		}
+		ret = wait_till_ready(flash);
+		if (ret) {
+			printf("SF: timeout for ready!\n");
+			goto time_out;
+		}
+		write_disable(flash);
+	}
+
+time_out:
+	spi_release_bus(flash->spi);
+	return ret;
+}
+
+/****************************************************************************/
+
+/*
+ * SPI device driver setup and teardown
+ */
+
+struct flash_info {
+	/* JEDEC id zero means "no ID" (most older chips); otherwise it has
+	 * a high byte of zero plus three data bytes: the manufacturer id,
+	 * then a two byte device id.
+	 */
+	u32		jedec_id;
+	u16             ext_id;
+
+	/* The size listed here is what works with OPCODE_SE, which isn't
+	 * necessarily called a "sector" by the vendor.
+	 */
+	unsigned	sector_size;
+	u16		n_sectors;
+
+	u16		page_size;
+	u16		addr_width;
+
+	u16		flags;
+#define	SECT_4K		0x01		/* OPCODE_BE_4K works uniformly */
+#define	M25P_NO_ERASE	0x02		/* No erase command needed */
+};
+
+#define SPI_NAME_SIZE   32
+#define SPI_MODULE_PREFIX "spi:"
+
+struct spi_device_id {
+	s8  name[SPI_NAME_SIZE];
+	u32 driver_data;
+};
+
+#define INFO(_jedec_id, _ext_id, _sector_size, _n_sectors, _flags)	\
+	((u32)&(struct flash_info) {				\
+		.jedec_id = (_jedec_id),				\
+		.ext_id = (_ext_id),					\
+		.sector_size = (_sector_size),				\
+		.n_sectors = (_n_sectors),				\
+		.page_size = 256,					\
+		.flags = (_flags),					\
+	})
+
+#define CAT25_INFO(_sector_size, _n_sectors, _page_size, _addr_width)	\
+	((u32)&(struct flash_info) {				\
+		.sector_size = (_sector_size),				\
+		.n_sectors = (_n_sectors),				\
+		.page_size = (_page_size),				\
+		.addr_width = (_addr_width),				\
+		.flags = M25P_NO_ERASE,					\
+	})
+
+/* NOTE: double check command sets and memory organization when you add
+ * more flash chips.  This current list focusses on newer chips, which
+ * have been converging on command sets which including JEDEC ID.
+ */
+static const struct spi_device_id m25p_ids[] = {
+	/* Atmel -- some are (confusingly) marketed as "DataFlash" */
+	{ "at25fs010",  INFO(0x1f6601, 0, 32 * 1024,   4, SECT_4K) },
+	{ "at25fs040",  INFO(0x1f6604, 0, 64 * 1024,   8, SECT_4K) },
+
+	{ "at25df041a", INFO(0x1f4401, 0, 64 * 1024,   8, SECT_4K) },
+	{ "at25df641",  INFO(0x1f4800, 0, 64 * 1024, 128, SECT_4K) },
+
+	{ "at26f004",   INFO(0x1f0400, 0, 64 * 1024,  8, SECT_4K) },
+	{ "at26df081a", INFO(0x1f4501, 0, 64 * 1024, 16, SECT_4K) },
+	{ "at26df161a", INFO(0x1f4601, 0, 64 * 1024, 32, SECT_4K) },
+	{ "at26df321",  INFO(0x1f4700, 0, 64 * 1024, 64, SECT_4K) },
+
+	/* EON -- en25xxx */
+	{ "en25f32", INFO(0x1c3116, 0, 64 * 1024,  64, SECT_4K) },
+	{ "en25p32", INFO(0x1c2016, 0, 64 * 1024,  64, 0) },
+	{ "en25p64", INFO(0x1c2017, 0, 64 * 1024, 128, 0) },
+
+	/* Intel/Numonyx -- xxxs33b */
+	{ "160s33b",  INFO(0x898911, 0, 64 * 1024,  32, 0) },
+	{ "320s33b",  INFO(0x898912, 0, 64 * 1024,  64, 0) },
+	{ "640s33b",  INFO(0x898913, 0, 64 * 1024, 128, 0) },
+
+	/* Macronix */
+	{ "mx25l4005a",  INFO(0xc22013, 0, 64 * 1024,   8, SECT_4K) },
+	{ "mx25l8005",   INFO(0xc22014, 0, 64 * 1024,  16, 0) },
+	{ "mx25l1606e",  INFO(0xc22015, 0, 64 * 1024,  32, SECT_4K) },
+	{ "mx25l3205d",  INFO(0xc22016, 0, 64 * 1024,  64, 0) },
+	{ "mx25l6405d",  INFO(0xc22017, 0, 64 * 1024, 128, 0) },
+	{ "mx25l12805d", INFO(0xc22018, 0, 64 * 1024, 256, 0) },
+	{ "mx25l12855e", INFO(0xc22618, 0, 64 * 1024, 256, 0) },
+	{ "mx25l25635e", INFO(0xc22019, 0, 64 * 1024, 512, 0) },
+	{ "mx25l25655e", INFO(0xc22619, 0, 64 * 1024, 512, 0) },
+
+	/* Spansion -- single (large) sector size only, at least
+	 * for the chips listed here (without boot sectors).
+	 */
+	{ "s25sl004a",  INFO(0x010212,      0,  64 * 1024,   8, 0) },
+	{ "s25sl008a",  INFO(0x010213,      0,  64 * 1024,  16, 0) },
+	{ "s25sl016a",  INFO(0x010214,      0,  64 * 1024,  32, 0) },
+	{ "s25sl032a",  INFO(0x010215,      0,  64 * 1024,  64, 0) },
+	{ "s25sl032p",  INFO(0x010215, 0x4d00,  64 * 1024,  64, SECT_4K) },
+	{ "s25sl064a",  INFO(0x010216,      0,  64 * 1024, 128, 0) },
+	{ "s25fl256s0", INFO(0x010219, 0x4d00, 256 * 1024, 128, 0) },
+	{ "s25fl256s1", INFO(0x010219, 0x4d01,  64 * 1024, 512, 0) },
+	{ "s25fl512s",  INFO(0x010220, 0x4d00, 256 * 1024, 256, 0) },
+	{ "s70fl01gs",  INFO(0x010221, 0x4d00, 256 * 1024, 256, 0) },
+	{ "s25sl12800", INFO(0x012018, 0x0300, 256 * 1024,  64, 0) },
+	{ "s25sl12801", INFO(0x012018, 0x0301,  64 * 1024, 256, 0) },
+	{ "s25fl129p0", INFO(0x012018, 0x4d00, 256 * 1024,  64, 0) },
+	{ "s25fl129p1", INFO(0x012018, 0x4d01,  64 * 1024, 256, 0) },
+	{ "s25fl016k",  INFO(0xef4015,      0,  64 * 1024,  32, SECT_4K) },
+	{ "s25fl064k",  INFO(0xef4017,      0,  64 * 1024, 128, SECT_4K) },
+
+	/* SST -- large erase sizes are "overlays", "sectors" are 4K */
+	{ "sst25vf040b", INFO(0xbf258d, 0, 64 * 1024,  8, SECT_4K) },
+	{ "sst25vf080b", INFO(0xbf258e, 0, 64 * 1024, 16, SECT_4K) },
+	{ "sst25vf016b", INFO(0xbf2541, 0, 64 * 1024, 32, SECT_4K) },
+	{ "sst25vf032b", INFO(0xbf254a, 0, 64 * 1024, 64, SECT_4K) },
+	{ "sst25wf512",  INFO(0xbf2501, 0, 64 * 1024,  1, SECT_4K) },
+	{ "sst25wf010",  INFO(0xbf2502, 0, 64 * 1024,  2, SECT_4K) },
+	{ "sst25wf020",  INFO(0xbf2503, 0, 64 * 1024,  4, SECT_4K) },
+	{ "sst25wf040",  INFO(0xbf2504, 0, 64 * 1024,  8, SECT_4K) },
+
+	/* ST Microelectronics -- newer production may have feature updates */
+	{ "m25p05",  INFO(0x202010,  0,  32 * 1024,   2, 0) },
+	{ "m25p10",  INFO(0x202011,  0,  32 * 1024,   4, 0) },
+	{ "m25p20",  INFO(0x202012,  0,  64 * 1024,   4, 0) },
+	{ "m25p40",  INFO(0x202013,  0,  64 * 1024,   8, 0) },
+	{ "m25p80",  INFO(0x202014,  0,  64 * 1024,  16, 0) },
+	{ "m25p16",  INFO(0x202015,  0,  64 * 1024,  32, 0) },
+	{ "m25p32",  INFO(0x202016,  0,  64 * 1024,  64, 0) },
+	{ "m25p64",  INFO(0x202017,  0,  64 * 1024, 128, 0) },
+	{ "m25p128", INFO(0x202018,  0, 256 * 1024,  64, 0) },
+
+	{ "m25p05-nonjedec",  INFO(0, 0,  32 * 1024,   2, 0) },
+	{ "m25p10-nonjedec",  INFO(0, 0,  32 * 1024,   4, 0) },
+	{ "m25p20-nonjedec",  INFO(0, 0,  64 * 1024,   4, 0) },
+	{ "m25p40-nonjedec",  INFO(0, 0,  64 * 1024,   8, 0) },
+	{ "m25p80-nonjedec",  INFO(0, 0,  64 * 1024,  16, 0) },
+	{ "m25p16-nonjedec",  INFO(0, 0,  64 * 1024,  32, 0) },
+	{ "m25p32-nonjedec",  INFO(0, 0,  64 * 1024,  64, 0) },
+	{ "m25p64-nonjedec",  INFO(0, 0,  64 * 1024, 128, 0) },
+	{ "m25p128-nonjedec", INFO(0, 0, 256 * 1024,  64, 0) },
+
+	{ "m45pe10", INFO(0x204011,  0, 64 * 1024,    2, 0) },
+	{ "m45pe80", INFO(0x204014,  0, 64 * 1024,   16, 0) },
+	{ "m45pe16", INFO(0x204015,  0, 64 * 1024,   32, 0) },
+
+	{ "m25pe80", INFO(0x208014,  0, 64 * 1024, 16,       0) },
+	{ "m25pe16", INFO(0x208015,  0, 64 * 1024, 32, SECT_4K) },
+
+	{ "m25px32",    INFO(0x207116,  0, 64 * 1024, 64, SECT_4K) },
+	{ "m25px32-s0", INFO(0x207316,  0, 64 * 1024, 64, SECT_4K) },
+	{ "m25px32-s1", INFO(0x206316,  0, 64 * 1024, 64, SECT_4K) },
+	{ "m25px64",    INFO(0x207117,  0, 64 * 1024, 128, 0) },
+
+	/* Winbond -- w25x "blocks" are 64K, "sectors" are 4KiB */
+	{ "w25x10", INFO(0xef3011, 0, 64 * 1024,  2,  SECT_4K) },
+	{ "w25x20", INFO(0xef3012, 0, 64 * 1024,  4,  SECT_4K) },
+	{ "w25x40", INFO(0xef3013, 0, 64 * 1024,  8,  SECT_4K) },
+	{ "w25x80", INFO(0xef3014, 0, 64 * 1024,  16, SECT_4K) },
+	{ "w25x16", INFO(0xef3015, 0, 64 * 1024,  32, SECT_4K) },
+	{ "w25x32", INFO(0xef3016, 0, 64 * 1024,  64, SECT_4K) },
+	{ "w25q32", INFO(0xef4016, 0, 64 * 1024,  64, SECT_4K) },
+	{ "w25x64", INFO(0xef3017, 0, 64 * 1024, 128, SECT_4K) },
+	{ "w25q64", INFO(0xef4017, 0, 64 * 1024, 128, SECT_4K) },
+
+	/* Catalyst / On Semiconductor -- non-JEDEC */
+	{ "cat25c11", CAT25_INFO(16,   8, 16, 1) },
+	{ "cat25c03", CAT25_INFO(32,   8, 16, 2) },
+	{ "cat25c09", CAT25_INFO(128,  8, 32, 2) },
+	{ "cat25c17", CAT25_INFO(256,  8, 32, 2) },
+	{ "cat25128", CAT25_INFO(2048, 8, 64, 2) },
+	{ },
+};
+
+static const struct spi_device_id *jedec_probe(u8 *idcode)
+{
+	int			tmp;
+	u32			jedec;
+	u16                     ext_jedec;
+	struct flash_info	*info;
+
+	jedec = idcode[0];
+	jedec = jedec << 8;
+	jedec |= idcode[1];
+	jedec = jedec << 8;
+	jedec |= idcode[2];
+
+	ext_jedec = idcode[3] << 8 | idcode[4];
+
+	for (tmp = 0; tmp < ARRAY_SIZE(m25p_ids) - 1; tmp++) {
+		info = (void *)m25p_ids[tmp].driver_data;
+		if (info->jedec_id == jedec) {
+			if (info->ext_id != 0 && info->ext_id != ext_jedec)
+				continue;
+			printf("JEDEC ID: 0x%x\n", jedec);
+
+			return &m25p_ids[tmp];
+		}
+	}
+	printf("unrecognized JEDEC id %06x\n", jedec);
+	return NULL;
+}
+
+
+/*
+ * board specific setup should have ensured the SPI clock used here
+ * matches what the READ command supports, at least until this driver
+ * understands FAST_READ (for clocks over 25 MHz).
+ */
+struct spi_flash *
+spi_flash_probe_m25p(struct spi_slave *spi, u8 *idcode)
+{
+	struct m25p_spi_flash		*m25p;
+	struct flash_info		*info;
+	const struct spi_device_id	*jid;
+
+	jid = jedec_probe(idcode);
+	if (!jid)
+		return NULL;
+
+	info = (void *)jid->driver_data;
+
+	m25p = malloc(sizeof(*m25p));
+	if (!m25p)
+		return NULL;
+
+	/*
+	 * Atmel, SST and Intel/Numonyx serial flash tend to power
+	 * up with the software protection bits set
+	 */
+
+	if (JEDEC_MFR(info->jedec_id) == CFI_MFR_ATMEL ||
+	    JEDEC_MFR(info->jedec_id) == CFI_MFR_INTEL ||
+	    JEDEC_MFR(info->jedec_id) == CFI_MFR_SST) {
+		if (write_enable(&m25p->flash) < 0) {
+			printf("SF: Write enable failed\n");
+			goto err_rtn;
+		}
+		if (write_sr(&m25p->flash, 0) < 0) {
+			printf("SF: Write sr failed\n");
+			goto err_rtn;
+		}
+	}
+
+	m25p->flash.spi = spi;
+	m25p->flash.name = (const char *)jid->name;
+	m25p->flash.erase = m25p80_erase;
+	m25p->flash.read = m25p80_read;
+	m25p->flash.size = info->sector_size * info->n_sectors;
+
+	/* sst flash chips use AAI word program */
+	if (JEDEC_MFR(info->jedec_id) == CFI_MFR_SST)
+		m25p->flash.write = sst_write;
+	else
+		m25p->flash.write = m25p80_write;
+
+	/* prefer "small sector" erase if possible */
+	if (info->flags & SECT_4K) {
+		m25p->erase_opcode = OPCODE_BE_4K;
+		m25p->erase_size = 4096;
+	} else {
+		m25p->erase_opcode = OPCODE_SE;
+		m25p->erase_size = info->sector_size;
+	}
+
+	m25p->page_size = info->page_size;
+
+	if (info->addr_width)
+		m25p->addr_width = info->addr_width;
+	else {
+		/* enable 4-byte addressing if the device exceeds 16MiB */
+		if (m25p->flash.size > 0x1000000) {
+			m25p->addr_width = 4;
+			set_4byte(&m25p->flash, info->jedec_id, 1);
+		} else
+			m25p->addr_width = 3;
+	}
+
+	printf("%s (%lld Kbytes)\n", jid->name,
+			(long long)m25p->flash.size >> 10);
+
+	return &m25p->flash;
+err_rtn:
+	free(m25p);
+	return NULL;
+}
diff --git a/drivers/mtd/spi/spi_flash.c b/drivers/mtd/spi/spi_flash.c
index ced4c94..a7db8d2 100644
--- a/drivers/mtd/spi/spi_flash.c
+++ b/drivers/mtd/spi/spi_flash.c
@@ -277,6 +277,16 @@ static const struct {
 #ifdef CONFIG_SPI_FLASH_EON
 	{ 0, 0x1c, spi_flash_probe_eon, },
 #endif
+#ifdef CONFIG_SPI_FLASH_M25P80
+	{ 0, 0x01, spi_flash_probe_m25p, },
+	{ 0, 0x1c, spi_flash_probe_m25p, },
+	{ 0, 0x1f, spi_flash_probe_m25p, },
+	{ 0, 0x20, spi_flash_probe_m25p, },
+	{ 0, 0x89, spi_flash_probe_m25p, },
+	{ 0, 0xbf, spi_flash_probe_m25p, },
+	{ 0, 0xc2, spi_flash_probe_m25p, },
+	{ 0, 0xef, spi_flash_probe_m25p, },
+#endif
 #ifdef CONFIG_SPI_FLASH_MACRONIX
 	{ 0, 0xc2, spi_flash_probe_macronix, },
 #endif
diff --git a/drivers/mtd/spi/spi_flash_internal.h b/drivers/mtd/spi/spi_flash_internal.h
index 91e036a..5d75025 100644
--- a/drivers/mtd/spi/spi_flash_internal.h
+++ b/drivers/mtd/spi/spi_flash_internal.h
@@ -95,6 +95,7 @@ int spi_flash_cmd_erase(struct spi_flash *flash, u8 erase_cmd,
 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_eon(struct spi_slave *spi, u8 *idcode);
+struct spi_flash *spi_flash_probe_m25p(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_stmicro(struct spi_slave *spi, u8 *idcode);
-- 
1.7.0.4




More information about the U-Boot mailing list