drivers: mtd: m25p80: add quad read support
authorSourav Poddar <sourav.poddar@ti.com>
Wed, 6 Nov 2013 14:35:35 +0000 (20:05 +0530)
committerBrian Norris <computersforpeace@gmail.com>
Fri, 3 Jan 2014 19:22:07 +0000 (11:22 -0800)
Some flash also support quad read mode. Adding support for quad read
mode in m25p80 for Spansion and Macronix flash.

[Tweaked by Brian]

With this patch, quad-read support will override fast-read and
normal-read, if the SPI controller and flash chip both support it.

Signed-off-by: Sourav Poddar <sourav.poddar@ti.com>
Tested-by: Sourav Poddar <sourav.poddar@ti.com>
Reviewed-by: Marek Vasut <marex@denx.de>
Signed-off-by: Brian Norris <computersforpeace@gmail.com>
drivers/mtd/devices/m25p80.c

index 04f8a245ffefaaf2b2a6cf6e4dc8a04b90a4ce4b..7dc2c147720aba6fa30dd17fef26f3593d53895b 100644 (file)
@@ -41,6 +41,7 @@
 #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_QUAD_READ        0x6b    /* Read data bytes */
 #define        OPCODE_PP               0x02    /* Page program (up to 256 bytes) */
 #define        OPCODE_BE_4K            0x20    /* Erase 4KiB block */
 #define        OPCODE_BE_4K_PMC        0xd7    /* Erase 4KiB block on PMC chips */
 #define        OPCODE_CHIP_ERASE       0xc7    /* Erase whole flash chip */
 #define        OPCODE_SE               0xd8    /* Sector erase (usually 64KiB) */
 #define        OPCODE_RDID             0x9f    /* Read JEDEC ID */
+#define        OPCODE_RDCR             0x35    /* Read configuration register */
 
 /* 4-byte address opcodes - used on Spansion and some Macronix flashes. */
 #define        OPCODE_NORM_READ_4B     0x13    /* Read data bytes (low frequency) */
 #define        OPCODE_FAST_READ_4B     0x0c    /* Read data bytes (high frequency) */
+#define        OPCODE_QUAD_READ_4B     0x6c    /* Read data bytes */
 #define        OPCODE_PP_4B            0x12    /* Page program (up to 256 bytes) */
 #define        OPCODE_SE_4B            0xdc    /* Sector erase (usually 64KiB) */
 
 #define        SR_BP2                  0x10    /* Block protect 2 */
 #define        SR_SRWD                 0x80    /* SR write protect */
 
+#define SR_QUAD_EN_MX           0x40    /* Macronix Quad I/O */
+
+/* Configuration Register bits. */
+#define CR_QUAD_EN_SPAN                0x2     /* Spansion Quad I/O */
+
 /* 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            6
@@ -87,6 +95,7 @@
 enum read_type {
        M25P80_NORMAL = 0,
        M25P80_FAST,
+       M25P80_QUAD,
 };
 
 struct m25p {
@@ -135,6 +144,26 @@ static int read_sr(struct m25p *flash)
        return val;
 }
 
+/*
+ * Read configuration register, returning its value in the
+ * location. Return the configuration register value.
+ * Returns negative if error occured.
+ */
+static int read_cr(struct m25p *flash)
+{
+       u8 code = OPCODE_RDCR;
+       int ret;
+       u8 val;
+
+       ret = spi_write_then_read(flash->spi, &code, 1, &val, 1);
+       if (ret < 0) {
+               dev_err(&flash->spi->dev, "error %d reading CR\n", ret);
+               return ret;
+       }
+
+       return val;
+}
+
 /*
  * Write status register 1 byte
  * Returns negative if error occurred.
@@ -224,6 +253,93 @@ static int wait_till_ready(struct m25p *flash)
        return 1;
 }
 
+/*
+ * Write status Register and configuration register with 2 bytes
+ * The first byte will be written to the status register, while the
+ * second byte will be written to the configuration register.
+ * Return negative if error occured.
+ */
+static int write_sr_cr(struct m25p *flash, u16 val)
+{
+       flash->command[0] = OPCODE_WRSR;
+       flash->command[1] = val & 0xff;
+       flash->command[2] = (val >> 8);
+
+       return spi_write(flash->spi, flash->command, 3);
+}
+
+static int macronix_quad_enable(struct m25p *flash)
+{
+       int ret, val;
+       u8 cmd[2];
+       cmd[0] = OPCODE_WRSR;
+
+       val = read_sr(flash);
+       cmd[1] = val | SR_QUAD_EN_MX;
+       write_enable(flash);
+
+       spi_write(flash->spi, &cmd, 2);
+
+       if (wait_till_ready(flash))
+               return 1;
+
+       ret = read_sr(flash);
+       if (!(ret > 0 && (ret & SR_QUAD_EN_MX))) {
+               dev_err(&flash->spi->dev, "Macronix Quad bit not set\n");
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int spansion_quad_enable(struct m25p *flash)
+{
+       int ret;
+       int quad_en = CR_QUAD_EN_SPAN << 8;
+
+       write_enable(flash);
+
+       ret = write_sr_cr(flash, quad_en);
+       if (ret < 0) {
+               dev_err(&flash->spi->dev,
+                       "error while writing configuration register\n");
+               return -EINVAL;
+       }
+
+       /* read back and check it */
+       ret = read_cr(flash);
+       if (!(ret > 0 && (ret & CR_QUAD_EN_SPAN))) {
+               dev_err(&flash->spi->dev, "Spansion Quad bit not set\n");
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int set_quad_mode(struct m25p *flash, u32 jedec_id)
+{
+       int status;
+
+       switch (JEDEC_MFR(jedec_id)) {
+       case CFI_MFR_MACRONIX:
+               status = macronix_quad_enable(flash);
+               if (status) {
+                       dev_err(&flash->spi->dev,
+                               "Macronix quad-read not enabled\n");
+                       return -EINVAL;
+               }
+               return status;
+       default:
+               status = spansion_quad_enable(flash);
+               if (status) {
+                       dev_err(&flash->spi->dev,
+                               "Spansion quad-read not enabled\n");
+                       return -EINVAL;
+               }
+               return status;
+       }
+}
+
 /*
  * Erase the whole flash memory
  *
@@ -363,6 +479,7 @@ static inline int m25p80_dummy_cycles_read(struct m25p *flash)
 {
        switch (flash->flash_read) {
        case M25P80_FAST:
+       case M25P80_QUAD:
                return 1;
        case M25P80_NORMAL:
                return 0;
@@ -727,6 +844,7 @@ struct flash_info {
 #define        SST_WRITE       0x04            /* use SST byte programming */
 #define        M25P_NO_FR      0x08            /* Can't do fastread */
 #define        SECT_4K_PMC     0x10            /* OPCODE_BE_4K_PMC works uniformly */
+#define        M25P80_QUAD_READ        0x20    /* Flash supports Quad Read */
 };
 
 #define INFO(_jedec_id, _ext_id, _sector_size, _n_sectors, _flags)     \
@@ -804,7 +922,7 @@ static const struct spi_device_id m25p_ids[] = {
        { "mx25l12855e", INFO(0xc22618, 0, 64 * 1024, 256, 0) },
        { "mx25l25635e", INFO(0xc22019, 0, 64 * 1024, 512, 0) },
        { "mx25l25655e", INFO(0xc22619, 0, 64 * 1024, 512, 0) },
-       { "mx66l51235l", INFO(0xc2201a, 0, 64 * 1024, 1024, 0) },
+       { "mx66l51235l", INFO(0xc2201a, 0, 64 * 1024, 1024, M25P80_QUAD_READ) },
 
        /* Micron */
        { "n25q064",     INFO(0x20ba17, 0, 64 * 1024,  128, 0) },
@@ -824,7 +942,7 @@ static const struct spi_device_id m25p_ids[] = {
        { "s25sl032p",  INFO(0x010215, 0x4d00,  64 * 1024,  64, 0) },
        { "s25sl064p",  INFO(0x010216, 0x4d00,  64 * 1024, 128, 0) },
        { "s25fl256s0", INFO(0x010219, 0x4d00, 256 * 1024, 128, 0) },
-       { "s25fl256s1", INFO(0x010219, 0x4d01,  64 * 1024, 512, 0) },
+       { "s25fl256s1", INFO(0x010219, 0x4d01,  64 * 1024, 512, M25P80_QUAD_READ) },
        { "s25fl512s",  INFO(0x010220, 0x4d00, 256 * 1024, 256, 0) },
        { "s70fl01gs",  INFO(0x010221, 0x4d00, 256 * 1024, 256, 0) },
        { "s25sl12800", INFO(0x012018, 0x0300, 256 * 1024,  64, 0) },
@@ -966,6 +1084,7 @@ static int m25p_probe(struct spi_device *spi)
        unsigned                        i;
        struct mtd_part_parser_data     ppdata;
        struct device_node *np = spi->dev.of_node;
+       int ret;
 
        /* Platform data helps sort out which chip type we have, as
         * well as how this board partitions it.  If we don't have
@@ -1093,8 +1212,21 @@ static int m25p_probe(struct spi_device *spi)
        if (info->flags & M25P_NO_FR)
                flash->flash_read = M25P80_NORMAL;
 
+       /* Quad-read mode takes precedence over fast/normal */
+       if (spi->mode & SPI_RX_QUAD && info->flags & M25P80_QUAD_READ) {
+               ret = set_quad_mode(flash, info->jedec_id);
+               if (ret) {
+                       dev_err(&flash->spi->dev, "quad mode not supported\n");
+                       return ret;
+               }
+               flash->flash_read = M25P80_QUAD;
+       }
+
        /* Default commands */
        switch (flash->flash_read) {
+       case M25P80_QUAD:
+               flash->read_opcode = OPCODE_QUAD_READ;
+               break;
        case M25P80_FAST:
                flash->read_opcode = OPCODE_FAST_READ;
                break;
@@ -1116,6 +1248,9 @@ static int m25p_probe(struct spi_device *spi)
                if (JEDEC_MFR(info->jedec_id) == CFI_MFR_AMD) {
                        /* Dedicated 4-byte command set */
                        switch (flash->flash_read) {
+                       case M25P80_QUAD:
+                               flash->read_opcode = OPCODE_QUAD_READ;
+                               break;
                        case M25P80_FAST:
                                flash->read_opcode = OPCODE_FAST_READ_4B;
                                break;