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 <r65...@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, m...@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


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

Reply via email to