]> err.no Git - linux-2.6/blobdiff - drivers/mtd/devices/m25p80.c
Merge git://git.kernel.org/pub/scm/linux/kernel/git/bart/ide-2.6
[linux-2.6] / drivers / mtd / devices / m25p80.c
index 7eaa61862a00fd68d0e2ea7e9a5b065abdc16dff..b402269301f6f64e4d22108ffc148f8220108b03 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/spi/flash.h>
 
 
-/* NOTE: AT 25F and SST 25LF series are very similar,
- * as are other newer Atmel dataflash chips (AT26),
- * 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_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_SE               0xd8    /* Sector erase (usually 64KiB) */
 #define        OPCODE_RDID             0x9f    /* Read JEDEC ID */
 
 /* 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 max times to check status register before we give up. */
 #define        MAX_READY_WAIT_COUNT    100000
+#define        CMD_SIZE                4
 
+#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
 
 #ifdef CONFIG_MTD_PARTITIONS
 #define        mtd_has_partitions()    (1)
@@ -68,8 +74,9 @@ struct m25p {
        struct spi_device       *spi;
        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)
@@ -151,8 +158,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",
+                       flash->spi->dev.bus_id, __func__,
+                       flash->mtd.erasesize / 1024, offset);
 
        /* Wait until finished previous write command. */
        if (wait_till_ready(flash))
@@ -162,12 +170,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;
 }
@@ -188,7 +196,7 @@ static int m25p80_erase(struct mtd_info *mtd, struct erase_info *instr)
        u32 addr,len;
 
        DEBUG(MTD_DEBUG_LEVEL2, "%s: %s %s 0x%08x, len %d\n",
-                       flash->spi->dev.bus_id, __FUNCTION__, "at",
+                       flash->spi->dev.bus_id, __func__, "at",
                        (u32)instr->addr, instr->len);
 
        /* sanity checks */
@@ -204,6 +212,10 @@ static int m25p80_erase(struct mtd_info *mtd, struct erase_info *instr)
 
        mutex_lock(&flash->lock);
 
+       /* REVISIT in some cases we could speed up erasing large regions
+        * by using OPCODE_SE instead of OPCODE_BE_4K
+        */
+
        /* now erase those sectors */
        while (len) {
                if (erase_sector(flash, addr)) {
@@ -236,7 +248,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",
+                       flash->spi->dev.bus_id, __func__, "from",
                        (u32)from, len);
 
        /* sanity checks */
@@ -249,8 +261,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;
@@ -270,7 +286,10 @@ static int m25p80_read(struct mtd_info *mtd, loff_t from, size_t len,
                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;
@@ -280,7 +299,7 @@ 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;
 
        mutex_unlock(&flash->lock);
 
@@ -301,7 +320,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",
+                       flash->spi->dev.bus_id, __func__, "to",
                        (u32)to, len);
 
        if (retlen)
@@ -318,7 +337,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 = sizeof(flash->command);
+       t[0].len = CMD_SIZE;
        spi_message_add_tail(&t[0], &m);
 
        t[1].tx_buf = buf;
@@ -327,8 +346,10 @@ static int m25p80_write(struct mtd_info *mtd, loff_t to, size_t len,
        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);
 
@@ -347,7 +368,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;
 
@@ -357,7 +378,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) {
@@ -380,8 +401,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
-                                       - sizeof(flash->command);
+                               *retlen += m.actual_length - CMD_SIZE;
                }
        }
 
@@ -399,24 +419,119 @@ 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;
+
+       /* 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 [] = {
-       /* JEDEC id zero means "has no ID" */
-       { "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, 32 * 1024, 4, SECT_4K, },
+       { "at25fs040",  0x1f6604, 64 * 1024, 8, SECT_4K, },
+
+       { "at25df041a", 0x1f4401, 64 * 1024, 8, SECT_4K, },
+       { "at25df641",  0x1f4800, 64 * 1024, 128, SECT_4K, },
+
+       { "at26f004",   0x1f0400, 64 * 1024, 8, SECT_4K, },
+       { "at26df081a", 0x1f4501, 64 * 1024, 16, SECT_4K, },
+       { "at26df161a", 0x1f4601, 64 * 1024, 32, SECT_4K, },
+       { "at26df321",  0x1f4701, 64 * 1024, 64, SECT_4K, },
+
+       /* Spansion -- single (large) sector size only, at least
+        * for the chips listed here (without boot sectors).
+        */
+       { "s25sl004a", 0x010212, 64 * 1024, 8, },
+       { "s25sl008a", 0x010213, 64 * 1024, 16, },
+       { "s25sl016a", 0x010214, 64 * 1024, 32, },
+       { "s25sl032a", 0x010215, 64 * 1024, 64, },
+       { "s25sl064a", 0x010216, 64 * 1024, 128, },
+
+       /* SST -- large erase sizes are "overlays", "sectors" are 4K */
+       { "sst25vf040b", 0xbf258d, 64 * 1024, 8, SECT_4K, },
+       { "sst25vf080b", 0xbf258e, 64 * 1024, 16, SECT_4K, },
+       { "sst25vf016b", 0xbf2541, 64 * 1024, 32, SECT_4K, },
+       { "sst25vf032b", 0xbf254a, 64 * 1024, 64, SECT_4K, },
+
+       /* ST Microelectronics -- newer production may have feature updates */
+       { "m25p05",  0x202010,  32 * 1024, 2, },
+       { "m25p10",  0x202011,  32 * 1024, 4, },
+       { "m25p20",  0x202012,  64 * 1024, 4, },
+       { "m25p40",  0x202013,  64 * 1024, 8, },
+       { "m25p80",         0,  64 * 1024, 16, },
+       { "m25p16",  0x202015,  64 * 1024, 32, },
+       { "m25p32",  0x202016,  64 * 1024, 64, },
+       { "m25p64",  0x202017,  64 * 1024, 128, },
+       { "m25p128", 0x202018, 256 * 1024, 64, },
+
+       { "m45pe80", 0x204014,  64 * 1024, 16, },
+       { "m45pe16", 0x204015,  64 * 1024, 32, },
+
+       { "m25pe80", 0x208014,  64 * 1024, 16, },
+       { "m25pe16", 0x208015,  64 * 1024, 32, SECT_4K, },
+
+       /* Winbond -- w25x "blocks" are 64K, "sectors" are 4KiB */
+       { "w25x10", 0xef3011, 64 * 1024, 2, SECT_4K, },
+       { "w25x20", 0xef3012, 64 * 1024, 4, SECT_4K, },
+       { "w25x40", 0xef3013, 64 * 1024, 8, SECT_4K, },
+       { "w25x80", 0xef3014, 64 * 1024, 16, SECT_4K, },
+       { "w25x16", 0xef3015, 64 * 1024, 32, SECT_4K, },
+       { "w25x32", 0xef3016, 64 * 1024, 64, SECT_4K, },
+       { "w25x64", 0xef3017, 64 * 1024, 128, SECT_4K, },
 };
 
+static struct flash_info *__devinit jedec_probe(struct spi_device *spi)
+{
+       int                     tmp;
+       u8                      code = OPCODE_RDID;
+       u8                      id[3];
+       u32                     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, 3);
+       if (tmp < 0) {
+               DEBUG(MTD_DEBUG_LEVEL0, "%s: error %d reading JEDEC ID\n",
+                       spi->dev.bus_id, tmp);
+               return NULL;
+       }
+       jedec = id[0];
+       jedec = jedec << 8;
+       jedec |= id[1];
+       jedec = jedec << 8;
+       jedec |= id[2];
+
+       for (tmp = 0, info = m25p_data;
+                       tmp < ARRAY_SIZE(m25p_data);
+                       tmp++, info++) {
+               if (info->jedec_id == jedec)
+                       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
@@ -430,27 +545,41 @@ 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",
-                               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",
-                               spi->dev.bus_id, data->type);
+               /* unrecognized chip? */
+               if (i == ARRAY_SIZE(m25p_data)) {
+                       DEBUG(MTD_DEBUG_LEVEL0, "%s: unrecognized id %s\n",
+                                       spi->dev.bus_id, 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)
@@ -460,7 +589,7 @@ static int __devinit m25p_probe(struct spi_device *spi)
        mutex_init(&flash->lock);
        dev_set_drvdata(&spi->dev, flash);
 
-       if (data->name)
+       if (data && data->name)
                flash->mtd.name = data->name;
        else
                flash->mtd.name = spi->dev.bus_id;
@@ -469,17 +598,25 @@ static int __devinit m25p_probe(struct spi_device *spi)
        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;
 
+       /* 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;
+       }
+
        dev_info(&spi->dev, "%s (%d Kbytes)\n", info->name,
                        flash->mtd.size / 1024);
 
        DEBUG(MTD_DEBUG_LEVEL2,
-               "mtd .name = %s, .size = 0x%.8x (%uM) "
-                       ".erasesize = 0x%.8x (%uK) .numeraseregions = %d\n",
+               "mtd .name = %s, .size = 0x%.8x (%uMiB) "
+                       ".erasesize = 0x%.8x (%uKiB) .numeraseregions = %d\n",
                flash->mtd.name,
                flash->mtd.size, flash->mtd.size / (1024*1024),
                flash->mtd.erasesize, flash->mtd.erasesize / 1024,
@@ -489,7 +626,7 @@ static int __devinit m25p_probe(struct spi_device *spi)
                for (i = 0; i < flash->mtd.numeraseregions; i++)
                        DEBUG(MTD_DEBUG_LEVEL2,
                                "mtd.eraseregions[%d] = { .offset = 0x%.8x, "
-                               ".erasesize = 0x%.8x (%uK), "
+                               ".erasesize = 0x%.8x (%uKiB), "
                                ".numblocks = %d }\n",
                                i, flash->mtd.eraseregions[i].offset,
                                flash->mtd.eraseregions[i].erasesize,
@@ -517,14 +654,14 @@ 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);
+                                               ".size = 0x%.8x (%uKiB) }\n",
+                                       i, parts[i].name,
+                                       parts[i].offset,
+                                       parts[i].size,
+                                       parts[i].size / 1024);
                        }
                        flash->partitioned = 1;
                        return add_mtd_partitions(&flash->mtd, parts, nr_parts);
@@ -561,6 +698,11 @@ 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...
+        */
 };