Merge git://git.infradead.org/mtd-2.6
[cascardo/linux.git] / drivers / mtd / devices / m25p80.c
index 4c19269..f3f4768 100644 (file)
@@ -22,6 +22,7 @@
 #include <linux/mutex.h>
 #include <linux/math64.h>
 #include <linux/sched.h>
+#include <linux/mod_devicetable.h>
 
 #include <linux/mtd/mtd.h>
 #include <linux/mtd/partitions.h>
@@ -29,9 +30,6 @@
 #include <linux/spi/spi.h>
 #include <linux/spi/flash.h>
 
-
-#define FLASH_PAGESIZE         256
-
 /* Flash opcodes. */
 #define        OPCODE_WREN             0x06    /* Write enable */
 #define        OPCODE_RDSR             0x05    /* Read status register */
@@ -61,7 +59,7 @@
 
 /* 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        CMD_SIZE                4
+#define        MAX_CMD_SIZE            4
 
 #ifdef CONFIG_M25PXX_USE_FAST_READ
 #define OPCODE_READ    OPCODE_FAST_READ
@@ -78,8 +76,10 @@ struct m25p {
        struct mutex            lock;
        struct mtd_info         mtd;
        unsigned                partitioned:1;
+       u16                     page_size;
+       u16                     addr_width;
        u8                      erase_opcode;
-       u8                      command[CMD_SIZE + FAST_READ_DUMMY_BYTE];
+       u8                      *command;
 };
 
 static inline struct m25p *mtd_to_m25p(struct mtd_info *mtd)
@@ -198,6 +198,19 @@ static int erase_chip(struct m25p *flash)
        return 0;
 }
 
+static void m25p_addr2cmd(struct m25p *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);
+}
+
+static int m25p_cmdsz(struct m25p *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.
@@ -219,11 +232,9 @@ static int erase_sector(struct m25p *flash, u32 offset)
 
        /* Set up command buffer. */
        flash->command[0] = flash->erase_opcode;
-       flash->command[1] = offset >> 16;
-       flash->command[2] = offset >> 8;
-       flash->command[3] = offset;
+       m25p_addr2cmd(flash, offset, flash->command);
 
-       spi_write(flash->spi, flash->command, CMD_SIZE);
+       spi_write(flash->spi, flash->command, m25p_cmdsz(flash));
 
        return 0;
 }
@@ -325,7 +336,7 @@ static int m25p80_read(struct mtd_info *mtd, loff_t from, size_t len,
         * Should add 1 byte DUMMY_BYTE.
         */
        t[0].tx_buf = flash->command;
-       t[0].len = CMD_SIZE + FAST_READ_DUMMY_BYTE;
+       t[0].len = m25p_cmdsz(flash) + FAST_READ_DUMMY_BYTE;
        spi_message_add_tail(&t[0], &m);
 
        t[1].rx_buf = buf;
@@ -352,13 +363,11 @@ static int m25p80_read(struct mtd_info *mtd, loff_t from, size_t len,
 
        /* Set up the write data buffer. */
        flash->command[0] = OPCODE_READ;
-       flash->command[1] = from >> 16;
-       flash->command[2] = from >> 8;
-       flash->command[3] = from;
+       m25p_addr2cmd(flash, from, flash->command);
 
        spi_sync(flash->spi, &m);
 
-       *retlen = m.actual_length - CMD_SIZE - FAST_READ_DUMMY_BYTE;
+       *retlen = m.actual_length - m25p_cmdsz(flash) - FAST_READ_DUMMY_BYTE;
 
        mutex_unlock(&flash->lock);
 
@@ -396,7 +405,7 @@ static int m25p80_write(struct mtd_info *mtd, loff_t to, size_t len,
        memset(t, 0, (sizeof t));
 
        t[0].tx_buf = flash->command;
-       t[0].len = CMD_SIZE;
+       t[0].len = m25p_cmdsz(flash);
        spi_message_add_tail(&t[0], &m);
 
        t[1].tx_buf = buf;
@@ -414,41 +423,36 @@ static int m25p80_write(struct mtd_info *mtd, loff_t to, size_t len,
 
        /* Set up the opcode in the write buffer. */
        flash->command[0] = OPCODE_PP;
-       flash->command[1] = to >> 16;
-       flash->command[2] = to >> 8;
-       flash->command[3] = to;
+       m25p_addr2cmd(flash, to, flash->command);
 
-       /* what page do we start with? */
-       page_offset = to % FLASH_PAGESIZE;
+       page_offset = to & (flash->page_size - 1);
 
        /* do all the bytes fit onto one page? */
-       if (page_offset + len <= FLASH_PAGESIZE) {
+       if (page_offset + len <= flash->page_size) {
                t[1].len = len;
 
                spi_sync(flash->spi, &m);
 
-               *retlen = m.actual_length - CMD_SIZE;
+               *retlen = m.actual_length - m25p_cmdsz(flash);
        } else {
                u32 i;
 
                /* the size of data remaining on the first page */
-               page_size = FLASH_PAGESIZE - page_offset;
+               page_size = flash->page_size - page_offset;
 
                t[1].len = page_size;
                spi_sync(flash->spi, &m);
 
-               *retlen = m.actual_length - CMD_SIZE;
+               *retlen = m.actual_length - m25p_cmdsz(flash);
 
-               /* write everything in PAGESIZE chunks */
+               /* write everything in flash->page_size chunks */
                for (i = page_size; i < len; i += page_size) {
                        page_size = len - i;
-                       if (page_size > FLASH_PAGESIZE)
-                               page_size = FLASH_PAGESIZE;
+                       if (page_size > flash->page_size)
+                               page_size = flash->page_size;
 
                        /* write the next page to flash */
-                       flash->command[1] = (to + i) >> 16;
-                       flash->command[2] = (to + i) >> 8;
-                       flash->command[3] = (to + i);
+                       m25p_addr2cmd(flash, to + i, flash->command);
 
                        t[1].tx_buf = buf + i;
                        t[1].len = page_size;
@@ -460,7 +464,7 @@ static int m25p80_write(struct mtd_info *mtd, loff_t to, size_t len,
                        spi_sync(flash->spi, &m);
 
                        if (retlen)
-                               *retlen += m.actual_length - CMD_SIZE;
+                               *retlen += m.actual_length - m25p_cmdsz(flash);
                }
        }
 
@@ -492,7 +496,7 @@ static int sst_write(struct mtd_info *mtd, loff_t to, size_t len,
        memset(t, 0, (sizeof t));
 
        t[0].tx_buf = flash->command;
-       t[0].len = CMD_SIZE;
+       t[0].len = m25p_cmdsz(flash);
        spi_message_add_tail(&t[0], &m);
 
        t[1].tx_buf = buf;
@@ -511,9 +515,7 @@ static int sst_write(struct mtd_info *mtd, loff_t to, size_t len,
        /* Start write from odd address. */
        if (actual) {
                flash->command[0] = OPCODE_BP;
-               flash->command[1] = to >> 16;
-               flash->command[2] = to >> 8;
-               flash->command[3] = to;
+               m25p_addr2cmd(flash, to, flash->command);
 
                /* write one byte. */
                t[1].len = 1;
@@ -521,17 +523,15 @@ static int sst_write(struct mtd_info *mtd, loff_t to, size_t len,
                ret = wait_till_ready(flash);
                if (ret)
                        goto time_out;
-               *retlen += m.actual_length - CMD_SIZE;
+               *retlen += m.actual_length - m25p_cmdsz(flash);
        }
        to += actual;
 
        flash->command[0] = OPCODE_AAI_WP;
-       flash->command[1] = to >> 16;
-       flash->command[2] = to >> 8;
-       flash->command[3] = to;
+       m25p_addr2cmd(flash, to, flash->command);
 
        /* Write out most of the data here. */
-       cmd_sz = CMD_SIZE;
+       cmd_sz = m25p_cmdsz(flash);
        for (; actual < len - 1; actual += 2) {
                t[0].len = cmd_sz;
                /* write two bytes. */
@@ -555,10 +555,8 @@ static int sst_write(struct mtd_info *mtd, loff_t to, size_t len,
        if (actual != len) {
                write_enable(flash);
                flash->command[0] = OPCODE_BP;
-               flash->command[1] = to >> 16;
-               flash->command[2] = to >> 8;
-               flash->command[3] = to;
-               t[0].len = CMD_SIZE;
+               m25p_addr2cmd(flash, to, flash->command);
+               t[0].len = m25p_cmdsz(flash);
                t[1].len = 1;
                t[1].tx_buf = buf + actual;
 
@@ -566,7 +564,7 @@ static int sst_write(struct mtd_info *mtd, loff_t to, size_t len,
                ret = wait_till_ready(flash);
                if (ret)
                        goto time_out;
-               *retlen += m.actual_length - CMD_SIZE;
+               *retlen += m.actual_length - m25p_cmdsz(flash);
                write_disable(flash);
        }
 
@@ -582,8 +580,6 @@ time_out:
  */
 
 struct flash_info {
-       char            *name;
-
        /* 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.
@@ -597,87 +593,119 @@ struct flash_info {
        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 INFO(_jedec_id, _ext_id, _sector_size, _n_sectors, _flags)     \
+       ((kernel_ulong_t)&(struct flash_info) {                         \
+               .jedec_id = (_jedec_id),                                \
+               .ext_id = (_ext_id),                                    \
+               .sector_size = (_sector_size),                          \
+               .n_sectors = (_n_sectors),                              \
+               .page_size = 256,                                       \
+               .addr_width = 3,                                        \
+               .flags = (_flags),                                      \
+       })
+
+#define CAT25_INFO(_sector_size, _n_sectors, _page_size, _addr_width)  \
+       ((kernel_ulong_t)&(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 struct flash_info __devinitdata m25p_data [] = {
-
+static const struct spi_device_id m25p_ids[] = {
        /* Atmel -- some are (confusingly) marketed as "DataFlash" */
-       { "at25fs010",  0x1f6601, 0, 32 * 1024, 4, SECT_4K, },
-       { "at25fs040",  0x1f6604, 0, 64 * 1024, 8, SECT_4K, },
+       { "at25fs010",  INFO(0x1f6601, 0, 32 * 1024,   4, SECT_4K) },
+       { "at25fs040",  INFO(0x1f6604, 0, 64 * 1024,   8, SECT_4K) },
 
-       { "at25df041a", 0x1f4401, 0, 64 * 1024, 8, SECT_4K, },
-       { "at25df641",  0x1f4800, 0, 64 * 1024, 128, SECT_4K, },
+       { "at25df041a", INFO(0x1f4401, 0, 64 * 1024,   8, SECT_4K) },
+       { "at25df641",  INFO(0x1f4800, 0, 64 * 1024, 128, SECT_4K) },
 
-       { "at26f004",   0x1f0400, 0, 64 * 1024, 8, SECT_4K, },
-       { "at26df081a", 0x1f4501, 0, 64 * 1024, 16, SECT_4K, },
-       { "at26df161a", 0x1f4601, 0, 64 * 1024, 32, SECT_4K, },
-       { "at26df321",  0x1f4701, 0, 64 * 1024, 64, 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(0x1f4701, 0, 64 * 1024, 64, SECT_4K) },
 
        /* Macronix */
-       { "mx25l3205d", 0xc22016, 0, 64 * 1024, 64, },
-       { "mx25l6405d", 0xc22017, 0, 64 * 1024, 128, },
-       { "mx25l12805d", 0xc22018, 0, 64 * 1024, 256, },
-       { "mx25l12855e", 0xc22618, 0, 64 * 1024, 256, },
+       { "mx25l4005a",  INFO(0xc22013, 0, 64 * 1024,   8, 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) },
 
        /* Spansion -- single (large) sector size only, at least
         * for the chips listed here (without boot sectors).
         */
-       { "s25sl004a", 0x010212, 0, 64 * 1024, 8, },
-       { "s25sl008a", 0x010213, 0, 64 * 1024, 16, },
-       { "s25sl016a", 0x010214, 0, 64 * 1024, 32, },
-       { "s25sl032a", 0x010215, 0, 64 * 1024, 64, },
-       { "s25sl064a", 0x010216, 0, 64 * 1024, 128, },
-       { "s25sl12800", 0x012018, 0x0300, 256 * 1024, 64, },
-       { "s25sl12801", 0x012018, 0x0301, 64 * 1024, 256, },
-       { "s25fl129p0", 0x012018, 0x4d00, 256 * 1024, 64, },
-       { "s25fl129p1", 0x012018, 0x4d01, 64 * 1024, 256, },
+       { "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) },
+       { "s25sl064a",  INFO(0x010216,      0,  64 * 1024, 128, 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) },
 
        /* SST -- large erase sizes are "overlays", "sectors" are 4K */
-       { "sst25vf040b", 0xbf258d, 0, 64 * 1024, 8, SECT_4K, },
-       { "sst25vf080b", 0xbf258e, 0, 64 * 1024, 16, SECT_4K, },
-       { "sst25vf016b", 0xbf2541, 0, 64 * 1024, 32, SECT_4K, },
-       { "sst25vf032b", 0xbf254a, 0, 64 * 1024, 64, SECT_4K, },
-       { "sst25wf512",  0xbf2501, 0, 64 * 1024, 1, SECT_4K, },
-       { "sst25wf010",  0xbf2502, 0, 64 * 1024, 2, SECT_4K, },
-       { "sst25wf020",  0xbf2503, 0, 64 * 1024, 4, SECT_4K, },
-       { "sst25wf040",  0xbf2504, 0, 64 * 1024, 8, SECT_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",  0x202010,  0, 32 * 1024, 2, },
-       { "m25p10",  0x202011,  0, 32 * 1024, 4, },
-       { "m25p20",  0x202012,  0, 64 * 1024, 4, },
-       { "m25p40",  0x202013,  0, 64 * 1024, 8, },
-       { "m25p80",         0,  0, 64 * 1024, 16, },
-       { "m25p16",  0x202015,  0, 64 * 1024, 32, },
-       { "m25p32",  0x202016,  0, 64 * 1024, 64, },
-       { "m25p64",  0x202017,  0, 64 * 1024, 128, },
-       { "m25p128", 0x202018, 0, 256 * 1024, 64, },
-
-       { "m45pe10", 0x204011,  0, 64 * 1024, 2, },
-       { "m45pe80", 0x204014,  0, 64 * 1024, 16, },
-       { "m45pe16", 0x204015,  0, 64 * 1024, 32, },
-
-       { "m25pe80", 0x208014,  0, 64 * 1024, 16, },
-       { "m25pe16", 0x208015,  0, 64 * 1024, 32, SECT_4K, },
+       { "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) },
+
+       { "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) },
 
        /* Winbond -- w25x "blocks" are 64K, "sectors" are 4KiB */
-       { "w25x10", 0xef3011, 0, 64 * 1024, 2, SECT_4K, },
-       { "w25x20", 0xef3012, 0, 64 * 1024, 4, SECT_4K, },
-       { "w25x40", 0xef3013, 0, 64 * 1024, 8, SECT_4K, },
-       { "w25x80", 0xef3014, 0, 64 * 1024, 16, SECT_4K, },
-       { "w25x16", 0xef3015, 0, 64 * 1024, 32, SECT_4K, },
-       { "w25x32", 0xef3016, 0, 64 * 1024, 64, SECT_4K, },
-       { "w25x64", 0xef3017, 0, 64 * 1024, 128, SECT_4K, },
+       { "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) },
+       { "w25x64", INFO(0xef3017, 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) },
+       { },
 };
+MODULE_DEVICE_TABLE(spi, m25p_ids);
 
-static struct flash_info *__devinit jedec_probe(struct spi_device *spi)
+static const struct spi_device_id *__devinit jedec_probe(struct spi_device *spi)
 {
        int                     tmp;
        u8                      code = OPCODE_RDID;
@@ -702,18 +730,24 @@ static struct flash_info *__devinit jedec_probe(struct spi_device *spi)
        jedec = jedec << 8;
        jedec |= id[2];
 
+       /*
+        * Some chips (like Numonyx M25P80) have JEDEC and non-JEDEC variants,
+        * which depend on technology process. Officially RDID command doesn't
+        * exist for non-JEDEC chips, but for compatibility they return ID 0.
+        */
+       if (jedec == 0)
+               return NULL;
+
        ext_jedec = id[3] << 8 | id[4];
 
-       for (tmp = 0, info = m25p_data;
-                       tmp < ARRAY_SIZE(m25p_data);
-                       tmp++, info++) {
+       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;
-                       return info;
+                       return &m25p_ids[tmp];
                }
        }
-       dev_err(&spi->dev, "unrecognized JEDEC id %06x\n", jedec);
        return NULL;
 }
 
@@ -725,6 +759,7 @@ static struct flash_info *__devinit jedec_probe(struct spi_device *spi)
  */
 static int __devinit m25p_probe(struct spi_device *spi)
 {
+       const struct spi_device_id      *id = spi_get_device_id(spi);
        struct flash_platform_data      *data;
        struct m25p                     *flash;
        struct flash_info               *info;
@@ -737,50 +772,65 @@ static int __devinit m25p_probe(struct spi_device *spi)
         */
        data = spi->dev.platform_data;
        if (data && data->type) {
-               for (i = 0, info = m25p_data;
-                               i < ARRAY_SIZE(m25p_data);
-                               i++, info++) {
-                       if (strcmp(data->type, info->name) == 0)
-                               break;
-               }
+               const struct spi_device_id *plat_id;
 
-               /* unrecognized chip? */
-               if (i == ARRAY_SIZE(m25p_data)) {
-                       DEBUG(MTD_DEBUG_LEVEL0, "%s: unrecognized id %s\n",
-                                       dev_name(&spi->dev), data->type);
-                       info = NULL;
-
-               /* recognized; is that chip really what's there? */
-               } else if (info->jedec_id) {
-                       struct flash_info       *chip = jedec_probe(spi);
-
-                       if (!chip || chip != info) {
-                               dev_warn(&spi->dev, "found %s, expected %s\n",
-                                               chip ? chip->name : "UNKNOWN",
-                                               info->name);
-                               info = NULL;
-                       }
+               for (i = 0; i < ARRAY_SIZE(m25p_ids) - 1; i++) {
+                       plat_id = &m25p_ids[i];
+                       if (strcmp(data->type, plat_id->name))
+                               continue;
+                       break;
                }
-       } else
-               info = jedec_probe(spi);
 
-       if (!info)
-               return -ENODEV;
+               if (plat_id)
+                       id = plat_id;
+               else
+                       dev_warn(&spi->dev, "unrecognized id %s\n", data->type);
+       }
+
+       info = (void *)id->driver_data;
+
+       if (info->jedec_id) {
+               const struct spi_device_id *jid;
+
+               jid = jedec_probe(spi);
+               if (!jid) {
+                       dev_info(&spi->dev, "non-JEDEC variant of %s\n",
+                                id->name);
+               } else if (jid != id) {
+                       /*
+                        * JEDEC knows better, so overwrite platform ID. We
+                        * can't trust partitions any longer, but we'll let
+                        * mtd apply them anyway, since some partitions may be
+                        * marked read-only, and we don't want to lose that
+                        * information, even if it's not 100% accurate.
+                        */
+                       dev_warn(&spi->dev, "found %s, expected %s\n",
+                                jid->name, id->name);
+                       id = jid;
+                       info = (void *)jid->driver_data;
+               }
+       }
 
        flash = kzalloc(sizeof *flash, GFP_KERNEL);
        if (!flash)
                return -ENOMEM;
+       flash->command = kmalloc(MAX_CMD_SIZE + FAST_READ_DUMMY_BYTE, GFP_KERNEL);
+       if (!flash->command) {
+               kfree(flash);
+               return -ENOMEM;
+       }
 
        flash->spi = spi;
        mutex_init(&flash->lock);
        dev_set_drvdata(&spi->dev, flash);
 
        /*
-        * Atmel serial flash tend to power up
-        * with the software protection bits set
+        * Atmel and SST serial flash tend to power
+        * up with the software protection bits set
         */
 
-       if (info->jedec_id >> 16 == 0x1f) {
+       if (info->jedec_id >> 16 == 0x1f ||
+           info->jedec_id >> 16 == 0xbf) {
                write_enable(flash);
                write_sr(flash, 0);
        }
@@ -812,9 +862,14 @@ static int __devinit m25p_probe(struct spi_device *spi)
                flash->mtd.erasesize = info->sector_size;
        }
 
+       if (info->flags & M25P_NO_ERASE)
+               flash->mtd.flags |= MTD_NO_ERASE;
+
        flash->mtd.dev.parent = &spi->dev;
+       flash->page_size = info->page_size;
+       flash->addr_width = info->addr_width;
 
-       dev_info(&spi->dev, "%s (%lld Kbytes)\n", info->name,
+       dev_info(&spi->dev, "%s (%lld Kbytes)\n", id->name,
                        (long long)flash->mtd.size >> 10);
 
        DEBUG(MTD_DEBUG_LEVEL2,
@@ -888,8 +943,10 @@ static int __devexit m25p_remove(struct spi_device *spi)
                status = del_mtd_partitions(&flash->mtd);
        else
                status = del_mtd_device(&flash->mtd);
-       if (status == 0)
+       if (status == 0) {
+               kfree(flash->command);
                kfree(flash);
+       }
        return 0;
 }
 
@@ -900,6 +957,7 @@ static struct spi_driver m25p80_driver = {
                .bus    = &spi_bus_type,
                .owner  = THIS_MODULE,
        },
+       .id_table       = m25p_ids,
        .probe  = m25p_probe,
        .remove = __devexit_p(m25p_remove),