headers: remove sched.h from interrupt.h
[safe/jmp/linux-2.6] / drivers / mtd / devices / m25p80.c
index 334e078..4c19269 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * MTD SPI driver for ST M25Pxx flash chips
+ * MTD SPI driver for ST M25Pxx (and similar) serial flash chips
  *
  * Author: Mike Lavender, mike@steroidmicros.com
  *
 #include <linux/module.h>
 #include <linux/device.h>
 #include <linux/interrupt.h>
-#include <linux/interrupt.h>
+#include <linux/mutex.h>
+#include <linux/math64.h>
+#include <linux/sched.h>
+
 #include <linux/mtd/mtd.h>
 #include <linux/mtd/partitions.h>
+
 #include <linux/spi/spi.h>
 #include <linux/spi/flash.h>
 
-#include <asm/semaphore.h>
-
-
-/* NOTE: AT 25F and SST 25LF series are very similar,
- * but commands for sector erase and chip id differ...
- */
 
 #define FLASH_PAGESIZE         256
 
 /* Flash opcodes. */
-#define        OPCODE_WREN             6       /* Write enable */
-#define        OPCODE_RDSR             5       /* Read status register */
-#define        OPCODE_READ             3       /* Read data bytes */
-#define        OPCODE_PP               2       /* Page program */
-#define        OPCODE_SE               0xd8    /* Sector erase */
-#define        OPCODE_RES              0xab    /* Read Electronic Signature */
+#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 */
+
 /* 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_COUNT    100000
-
+#define        MAX_READY_WAIT_JIFFIES  (40 * HZ)       /* M25P16 specs 40s max chip erase */
+#define        CMD_SIZE                4
 
-#ifdef CONFIG_MTD_PARTITIONS
-#define        mtd_has_partitions()    (1)
+#ifdef CONFIG_M25PXX_USE_FAST_READ
+#define OPCODE_READ    OPCODE_FAST_READ
+#define FAST_READ_DUMMY_BYTE 1
 #else
-#define        mtd_has_partitions()    (0)
+#define OPCODE_READ    OPCODE_NORM_READ
+#define FAST_READ_DUMMY_BYTE 0
 #endif
 
 /****************************************************************************/
 
 struct m25p {
        struct spi_device       *spi;
-       struct semaphore        lock;
+       struct mutex            lock;
        struct mtd_info         mtd;
-       unsigned                partitioned;
-       u8                      command[4];
+       unsigned                partitioned:1;
+       u8                      erase_opcode;
+       u8                      command[CMD_SIZE + FAST_READ_DUMMY_BYTE];
 };
 
 static inline struct m25p *mtd_to_m25p(struct mtd_info *mtd)
@@ -104,6 +115,17 @@ static int read_sr(struct m25p *flash)
        return val;
 }
 
+/*
+ * Write status register 1 byte
+ * Returns negative if error occurred.
+ */
+static int write_sr(struct m25p *flash, u8 val)
+{
+       flash->command[0] = OPCODE_WRSR;
+       flash->command[1] = val;
+
+       return spi_write(flash->spi, flash->command, 2);
+}
 
 /*
  * Set write enable latch with Write Enable command.
@@ -116,6 +138,15 @@ static inline int write_enable(struct m25p *flash)
        return spi_write_then_read(flash->spi, &code, 1, NULL, 0);
 }
 
+/*
+ * Send write disble instruction to the chip.
+ */
+static inline int write_disable(struct m25p *flash)
+{
+       u8      code = OPCODE_WRDI;
+
+       return spi_write_then_read(flash->spi, &code, 1, NULL, 0);
+}
 
 /*
  * Service routine to read status register until ready, or timeout occurs.
@@ -123,24 +154,49 @@ static inline int write_enable(struct m25p *flash)
  */
 static int wait_till_ready(struct m25p *flash)
 {
-       int count;
+       unsigned long deadline;
        int sr;
 
-       /* one chip guarantees max 5 msec wait here after page writes,
-        * but potentially three seconds (!) after page erase.
-        */
-       for (count = 0; count < MAX_READY_WAIT_COUNT; count++) {
+       deadline = jiffies + MAX_READY_WAIT_JIFFIES;
+
+       do {
                if ((sr = read_sr(flash)) < 0)
                        break;
                else if (!(sr & SR_WIP))
                        return 0;
 
-               /* REVISIT sometimes sleeping would be best */
-       }
+               cond_resched();
+
+       } while (!time_after_eq(jiffies, deadline));
 
        return 1;
 }
 
+/*
+ * Erase the whole flash memory
+ *
+ * Returns 0 if successful, non-zero otherwise.
+ */
+static int erase_chip(struct m25p *flash)
+{
+       DEBUG(MTD_DEBUG_LEVEL3, "%s: %s %lldKiB\n",
+             dev_name(&flash->spi->dev), __func__,
+             (long long)(flash->mtd.size >> 10));
+
+       /* Wait until finished previous write command. */
+       if (wait_till_ready(flash))
+               return 1;
+
+       /* Send write enable, then erase commands. */
+       write_enable(flash);
+
+       /* Set up command buffer. */
+       flash->command[0] = OPCODE_CHIP_ERASE;
+
+       spi_write(flash->spi, flash->command, 1);
+
+       return 0;
+}
 
 /*
  * Erase one sector of flash memory at offset ``offset'' which is any
@@ -150,8 +206,9 @@ static int wait_till_ready(struct m25p *flash)
  */
 static int erase_sector(struct m25p *flash, u32 offset)
 {
-       DEBUG(MTD_DEBUG_LEVEL3, "%s: %s at 0x%08x\n", flash->spi->dev.bus_id,
-                       __FUNCTION__, offset);
+       DEBUG(MTD_DEBUG_LEVEL3, "%s: %s %dKiB at 0x%08x\n",
+                       dev_name(&flash->spi->dev), __func__,
+                       flash->mtd.erasesize / 1024, offset);
 
        /* Wait until finished previous write command. */
        if (wait_till_ready(flash))
@@ -161,12 +218,12 @@ static int erase_sector(struct m25p *flash, u32 offset)
        write_enable(flash);
 
        /* Set up command buffer. */
-       flash->command[0] = OPCODE_SE;
+       flash->command[0] = flash->erase_opcode;
        flash->command[1] = offset >> 16;
        flash->command[2] = offset >> 8;
        flash->command[3] = offset;
 
-       spi_write(flash->spi, flash->command, sizeof(flash->command));
+       spi_write(flash->spi, flash->command, CMD_SIZE);
 
        return 0;
 }
@@ -185,37 +242,52 @@ static int m25p80_erase(struct mtd_info *mtd, struct erase_info *instr)
 {
        struct m25p *flash = mtd_to_m25p(mtd);
        u32 addr,len;
+       uint32_t rem;
 
-       DEBUG(MTD_DEBUG_LEVEL2, "%s: %s %s 0x%08x, len %d\n",
-                       flash->spi->dev.bus_id, __FUNCTION__, "at",
-                       (u32)instr->addr, instr->len);
+       DEBUG(MTD_DEBUG_LEVEL2, "%s: %s %s 0x%llx, len %lld\n",
+             dev_name(&flash->spi->dev), __func__, "at",
+             (long long)instr->addr, (long long)instr->len);
 
        /* sanity checks */
        if (instr->addr + instr->len > flash->mtd.size)
                return -EINVAL;
-       if ((instr->addr % mtd->erasesize) != 0
-                       || (instr->len % mtd->erasesize) != 0) {
+       div_u64_rem(instr->len, mtd->erasesize, &rem);
+       if (rem)
                return -EINVAL;
-       }
 
        addr = instr->addr;
        len = instr->len;
 
-       down(&flash->lock);
+       mutex_lock(&flash->lock);
 
-       /* now erase those sectors */
-       while (len) {
-               if (erase_sector(flash, addr)) {
+       /* whole-chip erase? */
+       if (len == flash->mtd.size) {
+               if (erase_chip(flash)) {
                        instr->state = MTD_ERASE_FAILED;
-                       up(&flash->lock);
+                       mutex_unlock(&flash->lock);
                        return -EIO;
                }
 
-               addr += mtd->erasesize;
-               len -= mtd->erasesize;
+       /* REVISIT in some cases we could speed up erasing large regions
+        * by using OPCODE_SE instead of OPCODE_BE_4K.  We may have set up
+        * to use "small sector erase", but that's not always optimal.
+        */
+
+       /* "sector"-at-a-time erase */
+       } else {
+               while (len) {
+                       if (erase_sector(flash, addr)) {
+                               instr->state = MTD_ERASE_FAILED;
+                               mutex_unlock(&flash->lock);
+                               return -EIO;
+                       }
+
+                       addr += mtd->erasesize;
+                       len -= mtd->erasesize;
+               }
        }
 
-       up(&flash->lock);
+       mutex_unlock(&flash->lock);
 
        instr->state = MTD_ERASE_DONE;
        mtd_erase_callback(instr);
@@ -235,7 +307,7 @@ static int m25p80_read(struct mtd_info *mtd, loff_t from, size_t len,
        struct spi_message m;
 
        DEBUG(MTD_DEBUG_LEVEL2, "%s: %s %s 0x%08x, len %zd\n",
-                       flash->spi->dev.bus_id, __FUNCTION__, "from",
+                       dev_name(&flash->spi->dev), __func__, "from",
                        (u32)from, len);
 
        /* sanity checks */
@@ -248,8 +320,12 @@ static int m25p80_read(struct mtd_info *mtd, loff_t from, size_t len,
        spi_message_init(&m);
        memset(t, 0, (sizeof t));
 
+       /* NOTE:
+        * OPCODE_FAST_READ (if available) is faster.
+        * Should add 1 byte DUMMY_BYTE.
+        */
        t[0].tx_buf = flash->command;
-       t[0].len = sizeof(flash->command);
+       t[0].len = CMD_SIZE + FAST_READ_DUMMY_BYTE;
        spi_message_add_tail(&t[0], &m);
 
        t[1].rx_buf = buf;
@@ -260,16 +336,19 @@ static int m25p80_read(struct mtd_info *mtd, loff_t from, size_t len,
        if (retlen)
                *retlen = 0;
 
-       down(&flash->lock);
+       mutex_lock(&flash->lock);
 
        /* Wait till previous write/erase is done. */
        if (wait_till_ready(flash)) {
                /* REVISIT status return?? */
-               up(&flash->lock);
+               mutex_unlock(&flash->lock);
                return 1;
        }
 
-       /* NOTE:  OPCODE_FAST_READ (if available) is faster... */
+       /* 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. */
        flash->command[0] = OPCODE_READ;
@@ -279,9 +358,9 @@ static int m25p80_read(struct mtd_info *mtd, loff_t from, size_t len,
 
        spi_sync(flash->spi, &m);
 
-       *retlen = m.actual_length - sizeof(flash->command);
+       *retlen = m.actual_length - CMD_SIZE - FAST_READ_DUMMY_BYTE;
 
-       up(&flash->lock);
+       mutex_unlock(&flash->lock);
 
        return 0;
 }
@@ -300,7 +379,7 @@ static int m25p80_write(struct mtd_info *mtd, loff_t to, size_t len,
        struct spi_message m;
 
        DEBUG(MTD_DEBUG_LEVEL2, "%s: %s %s 0x%08x, len %zd\n",
-                       flash->spi->dev.bus_id, __FUNCTION__, "to",
+                       dev_name(&flash->spi->dev), __func__, "to",
                        (u32)to, len);
 
        if (retlen)
@@ -317,17 +396,19 @@ 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 = sizeof(flash->command);
+       t[0].len = CMD_SIZE;
        spi_message_add_tail(&t[0], &m);
 
        t[1].tx_buf = buf;
        spi_message_add_tail(&t[1], &m);
 
-       down(&flash->lock);
+       mutex_lock(&flash->lock);
 
        /* Wait until finished previous write command. */
-       if (wait_till_ready(flash))
+       if (wait_till_ready(flash)) {
+               mutex_unlock(&flash->lock);
                return 1;
+       }
 
        write_enable(flash);
 
@@ -346,7 +427,7 @@ static int m25p80_write(struct mtd_info *mtd, loff_t to, size_t len,
 
                spi_sync(flash->spi, &m);
 
-               *retlen = m.actual_length - sizeof(flash->command);
+               *retlen = m.actual_length - CMD_SIZE;
        } else {
                u32 i;
 
@@ -356,7 +437,7 @@ static int m25p80_write(struct mtd_info *mtd, loff_t to, size_t len,
                t[1].len = page_size;
                spi_sync(flash->spi, &m);
 
-               *retlen = m.actual_length - sizeof(flash->command);
+               *retlen = m.actual_length - CMD_SIZE;
 
                /* write everything in PAGESIZE chunks */
                for (i = page_size; i < len; i += page_size) {
@@ -379,16 +460,120 @@ 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
-                                       - sizeof(flash->command);
-               }
-       }
+                               *retlen += m.actual_length - CMD_SIZE;
+               }
+       }
 
-       up(&flash->lock);
+       mutex_unlock(&flash->lock);
 
        return 0;
 }
 
+static int sst_write(struct mtd_info *mtd, loff_t to, size_t len,
+               size_t *retlen, const u_char *buf)
+{
+       struct m25p *flash = mtd_to_m25p(mtd);
+       struct spi_transfer t[2];
+       struct spi_message m;
+       size_t actual;
+       int cmd_sz, ret;
+
+       if (retlen)
+               *retlen = 0;
+
+       /* sanity checks */
+       if (!len)
+               return 0;
+
+       if (to + len > flash->mtd.size)
+               return -EINVAL;
+
+       spi_message_init(&m);
+       memset(t, 0, (sizeof t));
+
+       t[0].tx_buf = flash->command;
+       t[0].len = CMD_SIZE;
+       spi_message_add_tail(&t[0], &m);
+
+       t[1].tx_buf = buf;
+       spi_message_add_tail(&t[1], &m);
+
+       mutex_lock(&flash->lock);
+
+       /* Wait until finished previous write command. */
+       ret = wait_till_ready(flash);
+       if (ret)
+               goto time_out;
+
+       write_enable(flash);
+
+       actual = to % 2;
+       /* 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;
+
+               /* write one byte. */
+               t[1].len = 1;
+               spi_sync(flash->spi, &m);
+               ret = wait_till_ready(flash);
+               if (ret)
+                       goto time_out;
+               *retlen += m.actual_length - CMD_SIZE;
+       }
+       to += actual;
+
+       flash->command[0] = OPCODE_AAI_WP;
+       flash->command[1] = to >> 16;
+       flash->command[2] = to >> 8;
+       flash->command[3] = to;
+
+       /* Write out most of the data here. */
+       cmd_sz = CMD_SIZE;
+       for (; actual < len - 1; actual += 2) {
+               t[0].len = cmd_sz;
+               /* write two bytes. */
+               t[1].len = 2;
+               t[1].tx_buf = buf + actual;
+
+               spi_sync(flash->spi, &m);
+               ret = wait_till_ready(flash);
+               if (ret)
+                       goto time_out;
+               *retlen += m.actual_length - cmd_sz;
+               cmd_sz = 1;
+               to += 2;
+       }
+       write_disable(flash);
+       ret = wait_till_ready(flash);
+       if (ret)
+               goto time_out;
+
+       /* Write out trailing byte if it exists. */
+       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;
+               t[1].len = 1;
+               t[1].tx_buf = buf + actual;
+
+               spi_sync(flash->spi, &m);
+               ret = wait_till_ready(flash);
+               if (ret)
+                       goto time_out;
+               *retlen += m.actual_length - CMD_SIZE;
+               write_disable(flash);
+       }
+
+time_out:
+       mutex_unlock(&flash->lock);
+       return ret;
+}
 
 /****************************************************************************/
 
@@ -398,24 +583,141 @@ static int m25p80_write(struct mtd_info *mtd, loff_t to, size_t len,
 
 struct flash_info {
        char            *name;
-       u8              id;
-       u16             jedec_id;
+
+       /* 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;
-       unsigned        n_sectors;
+       u16             n_sectors;
+
+       u16             flags;
+#define        SECT_4K         0x01            /* OPCODE_BE_4K works uniformly */
 };
 
+
+/* 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 [] = {
-       /* REVISIT: fill in JEDEC ids, for parts that have them */
-       { "m25p05", 0x05, 0x2010, 32 * 1024, 2 },
-       { "m25p10", 0x10, 0x2011, 32 * 1024, 4 },
-       { "m25p20", 0x11, 0x2012, 64 * 1024, 4 },
-       { "m25p40", 0x12, 0x2013, 64 * 1024, 8 },
-       { "m25p80", 0x13, 0x0000, 64 * 1024, 16 },
-       { "m25p16", 0x14, 0x2015, 64 * 1024, 32 },
-       { "m25p32", 0x15, 0x2016, 64 * 1024, 64 },
-       { "m25p64", 0x16, 0x2017, 64 * 1024, 128 },
+
+       /* Atmel -- some are (confusingly) marketed as "DataFlash" */
+       { "at25fs010",  0x1f6601, 0, 32 * 1024, 4, SECT_4K, },
+       { "at25fs040",  0x1f6604, 0, 64 * 1024, 8, SECT_4K, },
+
+       { "at25df041a", 0x1f4401, 0, 64 * 1024, 8, SECT_4K, },
+       { "at25df641",  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, },
+
+       /* 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, },
+
+       /* 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, },
+
+       /* 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, },
+
+       /* 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, },
+
+       /* 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, },
 };
 
+static struct flash_info *__devinit jedec_probe(struct spi_device *spi)
+{
+       int                     tmp;
+       u8                      code = OPCODE_RDID;
+       u8                      id[5];
+       u32                     jedec;
+       u16                     ext_jedec;
+       struct flash_info       *info;
+
+       /* JEDEC also defines an optional "extended device information"
+        * string for after vendor-specific data, after the three bytes
+        * we use here.  Supporting some chips might require using it.
+        */
+       tmp = spi_write_then_read(spi, &code, 1, id, 5);
+       if (tmp < 0) {
+               DEBUG(MTD_DEBUG_LEVEL0, "%s: error %d reading JEDEC ID\n",
+                       dev_name(&spi->dev), tmp);
+               return NULL;
+       }
+       jedec = id[0];
+       jedec = jedec << 8;
+       jedec |= id[1];
+       jedec = jedec << 8;
+       jedec |= id[2];
+
+       ext_jedec = id[3] << 8 | id[4];
+
+       for (tmp = 0, info = m25p_data;
+                       tmp < ARRAY_SIZE(m25p_data);
+                       tmp++, info++) {
+               if (info->jedec_id == jedec) {
+                       if (info->ext_id != 0 && info->ext_id != ext_jedec)
+                               continue;
+                       return info;
+               }
+       }
+       dev_err(&spi->dev, "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
@@ -429,68 +731,107 @@ static int __devinit m25p_probe(struct spi_device *spi)
        unsigned                        i;
 
        /* Platform data helps sort out which chip type we have, as
-        * well as how this board partitions it.
+        * well as how this board partitions it.  If we don't have
+        * a chip ID, try the JEDEC id commands; they'll work for most
+        * newer chips, even if we don't recognize the particular chip.
         */
        data = spi->dev.platform_data;
-       if (!data || !data->type) {
-               /* FIXME some chips can identify themselves with RES
-                * or JEDEC get-id commands.  Try them ...
-                */
-               DEBUG(MTD_DEBUG_LEVEL1, "%s: no chip id\n",
-                               flash->spi->dev.bus_id);
-               return -ENODEV;
-       }
+       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;
+               }
 
-       for (i = 0, info = m25p_data; i < ARRAY_SIZE(m25p_data); i++, info++) {
-               if (strcmp(data->type, info->name) == 0)
-                       break;
-       }
-       if (i == ARRAY_SIZE(m25p_data)) {
-               DEBUG(MTD_DEBUG_LEVEL1, "%s: unrecognized id %s\n",
-                               flash->spi->dev.bus_id, data->type);
+               /* 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;
+                       }
+               }
+       } else
+               info = jedec_probe(spi);
+
+       if (!info)
                return -ENODEV;
-       }
 
        flash = kzalloc(sizeof *flash, GFP_KERNEL);
        if (!flash)
                return -ENOMEM;
 
        flash->spi = spi;
-       init_MUTEX(&flash->lock);
+       mutex_init(&flash->lock);
        dev_set_drvdata(&spi->dev, flash);
 
-       if (data->name)
+       /*
+        * Atmel serial flash tend to power up
+        * with the software protection bits set
+        */
+
+       if (info->jedec_id >> 16 == 0x1f) {
+               write_enable(flash);
+               write_sr(flash, 0);
+       }
+
+       if (data && data->name)
                flash->mtd.name = data->name;
        else
-               flash->mtd.name = spi->dev.bus_id;
+               flash->mtd.name = dev_name(&spi->dev);
 
        flash->mtd.type = MTD_NORFLASH;
        flash->mtd.writesize = 1;
        flash->mtd.flags = MTD_CAP_NORFLASH;
        flash->mtd.size = info->sector_size * info->n_sectors;
-       flash->mtd.erasesize = info->sector_size;
        flash->mtd.erase = m25p80_erase;
        flash->mtd.read = m25p80_read;
-       flash->mtd.write = m25p80_write;
 
-       dev_info(&spi->dev, "%s (%d Kbytes)\n", info->name,
-                       flash->mtd.size / 1024);
+       /* sst flash chips use AAI word program */
+       if (info->jedec_id >> 16 == 0xbf)
+               flash->mtd.write = sst_write;
+       else
+               flash->mtd.write = m25p80_write;
+
+       /* prefer "small sector" erase if possible */
+       if (info->flags & SECT_4K) {
+               flash->erase_opcode = OPCODE_BE_4K;
+               flash->mtd.erasesize = 4096;
+       } else {
+               flash->erase_opcode = OPCODE_SE;
+               flash->mtd.erasesize = info->sector_size;
+       }
+
+       flash->mtd.dev.parent = &spi->dev;
+
+       dev_info(&spi->dev, "%s (%lld Kbytes)\n", info->name,
+                       (long long)flash->mtd.size >> 10);
 
        DEBUG(MTD_DEBUG_LEVEL2,
-               "mtd .name = %s, .size = 0x%.8x (%uM) "
-                       ".erasesize = 0x%.8x (%uK) .numeraseregions = %d\n",
+               "mtd .name = %s, .size = 0x%llx (%lldMiB) "
+                       ".erasesize = 0x%.8x (%uKiB) .numeraseregions = %d\n",
                flash->mtd.name,
-               flash->mtd.size, flash->mtd.size / (1024*1024),
+               (long long)flash->mtd.size, (long long)(flash->mtd.size >> 20),
                flash->mtd.erasesize, flash->mtd.erasesize / 1024,
                flash->mtd.numeraseregions);
 
        if (flash->mtd.numeraseregions)
                for (i = 0; i < flash->mtd.numeraseregions; i++)
                        DEBUG(MTD_DEBUG_LEVEL2,
-                               "mtd.eraseregions[%d] = { .offset = 0x%.8x, "
-                               ".erasesize = 0x%.8x (%uK), "
+                               "mtd.eraseregions[%d] = { .offset = 0x%llx, "
+                               ".erasesize = 0x%.8x (%uKiB), "
                                ".numblocks = %d }\n",
-                               i, flash->mtd.eraseregions[i].offset,
+                               i, (long long)flash->mtd.eraseregions[i].offset,
                                flash->mtd.eraseregions[i].erasesize,
                                flash->mtd.eraseregions[i].erasesize / 1024,
                                flash->mtd.eraseregions[i].numblocks);
@@ -503,12 +844,13 @@ static int __devinit m25p_probe(struct spi_device *spi)
                struct mtd_partition    *parts = NULL;
                int                     nr_parts = 0;
 
-#ifdef CONFIG_MTD_CMDLINE_PARTS
-               static const char *part_probes[] = { "cmdlinepart", NULL, };
+               if (mtd_has_cmdlinepart()) {
+                       static const char *part_probes[]
+                                       = { "cmdlinepart", NULL, };
 
-               nr_parts = parse_mtd_partitions(&flash->mtd,
-                               part_probes, &parts, 0);
-#endif
+                       nr_parts = parse_mtd_partitions(&flash->mtd,
+                                       part_probes, &parts, 0);
+               }
 
                if (nr_parts <= 0 && data && data->parts) {
                        parts = data->parts;
@@ -516,19 +858,19 @@ static int __devinit m25p_probe(struct spi_device *spi)
                }
 
                if (nr_parts > 0) {
-                       for (i = 0; i < data->nr_parts; i++) {
+                       for (i = 0; i < nr_parts; i++) {
                                DEBUG(MTD_DEBUG_LEVEL2, "partitions[%d] = "
-                                       "{.name = %s, .offset = 0x%.8x, "
-                                               ".size = 0x%.8x (%uK) }\n",
-                                       i, data->parts[i].name,
-                                       data->parts[i].offset,
-                                       data->parts[i].size,
-                                       data->parts[i].size / 1024);
+                                       "{.name = %s, .offset = 0x%llx, "
+                                               ".size = 0x%llx (%lldKiB) }\n",
+                                       i, parts[i].name,
+                                       (long long)parts[i].offset,
+                                       (long long)parts[i].size,
+                                       (long long)(parts[i].size >> 10));
                        }
                        flash->partitioned = 1;
                        return add_mtd_partitions(&flash->mtd, parts, nr_parts);
                }
-       } else if (data->nr_parts)
+       } else if (data && data->nr_parts)
                dev_warn(&spi->dev, "ignoring %d default partitions on %s\n",
                                data->nr_parts, data->name);
 
@@ -560,16 +902,21 @@ static struct spi_driver m25p80_driver = {
        },
        .probe  = m25p_probe,
        .remove = __devexit_p(m25p_remove),
+
+       /* REVISIT: many of these chips have deep power-down modes, which
+        * should clearly be entered on suspend() to minimize power use.
+        * And also when they're otherwise idle...
+        */
 };
 
 
-static int m25p80_init(void)
+static int __init m25p80_init(void)
 {
        return spi_register_driver(&m25p80_driver);
 }
 
 
-static void m25p80_exit(void)
+static void __exit m25p80_exit(void)
 {
        spi_unregister_driver(&m25p80_driver);
 }