SPI: DUAL/QUAD support

yuhang wang wangyuhang2014 at gmail.com
Fri Jul 5 06:24:40 EDT 2013


2013/7/5 Sourav Poddar <sourav.poddar at ti.com>:
> On Friday 05 July 2013 02:47 PM, yuhang wang wrote:
>>
>> 2013/7/5 Sourav Poddar<sourav.poddar at ti.com>:
>>>
>>> On Friday 05 July 2013 02:37 PM, yuhang wang wrote:
>>>>>
>>>>>                       if (!quad_mode)
>>>>>                               dra7xxx_writel(qspi, qspi->cmd |
>>>>> QSPI_RD_SNGL,
>>>>>                                               QSPI_SPI_CMD_REG);
>>>>>                       else
>>>>>                               dra7xxx_writel(qspi, qspi->cmd |
>>>>> QSPI_RD_QUAD,
>>>>>                                               QSPI_SPI_CMD_REG);
>>>>>                    .......
>>>>>                   }
>>>>
>>>> So what do you based on to set variable quad_mode.
>>>
>>> Best way should be to check for flash device id and manufacture data.
>>> Based
>>> on which you
>>> can decide whether your flash supports quad bits or not.
>>
>> Perhaps I did not said it clearly. Your flash supports quad and you
>> set it into quad
>> mode. But how can your controller driver notice that. In other word,
>> In which way
>> provide this information from flash to spi controller.
>
> Perhaps, I understood you initial patch wrong. :(
> Yes, this is little confusing, and I am trying to figure out that.
> As of now, I have tested it with some extern variable, though
> we need to see how it can be done cleanly.
> I am trying to see if your patch can be used in some way now.
>

Got it. In fact the kernel version I am working in is 3.0.77.
And I modified m25p80.c in Ver 3.0.90 to fit to the dual and quad.
But the driver is not general because I don't find any standard for
serial-flash. Also to the member name, just as Mark said, the
meaning is not suitable enough. Finally, do not support device tree.

2 Questions here:
1.In m25p80.c probe, the kmalloc below I feel very strange.
      flash->command = kmalloc(MAX_CMD_SIZE + (flash->fast_read ? 1 : 0),
                                      GFP_KERNEL);

 (flash->fast_read ? 1 : 0) must be 0! So why do it like that.

2.Is that any need to add wait_till_ready in the erase function to wait for the
  end of erase. But it will really take a long time.

Signed-off-by: wangyuhang <wangyuhang2014 at gmail.com>
---
 drivers/mtd/devices/m25p80.c |   93 ++++++++++++++++++++++++++++++++----------
 drivers/spi/spi.c            |    2 +
 include/linux/spi/spi.h      |    8 ++++
 3 files changed, 82 insertions(+), 21 deletions(-)

diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c
index 5b6b072..267c8af 100644
--- a/drivers/mtd/devices/m25p80.c
+++ b/drivers/mtd/devices/m25p80.c
@@ -47,6 +47,10 @@
 #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_DOR              0x3B    /* Dual Output Read */
+#define        OPCODE_QOR              0x6B    /* Quad Output Read */
+#define        OPCODE_QPP              0x32    /* Quad Page Programming */
+#define        OPCODE_QEN              0x02 /* Quad Mode Enable Flag */

 /* Used for SST flashes only. */
 #define        OPCODE_BP               0x02    /* Byte program */
@@ -73,6 +77,14 @@
 #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)

 /****************************************************************************/
@@ -85,7 +97,9 @@ struct m25p {
        u16                     addr_width;
        u8                      erase_opcode;
        u8                      *command;
-       bool                    fast_read;
+       u8                      read_opcode;
+       u8                      read_dummy;
+       u8                      write_opcode;
 };

 static inline struct m25p *mtd_to_m25p(struct mtd_info *mtd)
@@ -94,6 +108,18 @@ static inline struct m25p *mtd_to_m25p(struct mtd_info *mtd)
 }

 /****************************************************************************/
+/*
+ * Write configuration register 2 byte
+ * Returns negative if error occurred.
+ */
+static int write_cr(struct m25p *flash, u16 val)
+{
+       flash->command[0] = OPCODE_WRSR;
+       flash->command[1] = val >> 8;
+       flash->command[2] = val;
+
+       return spi_write(flash->spi, flash->command, 3);
+}

 /*
  * Internal helper functions
@@ -336,7 +362,6 @@ static int m25p80_read(struct mtd_info *mtd,
loff_t from, size_t len,
        struct m25p *flash = mtd_to_m25p(mtd);
        struct spi_transfer t[2];
        struct spi_message m;
-       uint8_t opcode;

        pr_debug("%s: %s from 0x%08x, len %zd\n", dev_name(&flash->spi->dev),
                        __func__, (u32)from, len);
@@ -349,11 +374,12 @@ 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 = m25p_cmdsz(flash) + (flash->fast_read ? 1 : 0);
+       t[0].len = m25p_cmdsz(flash) + flash->read_dummy;
        spi_message_add_tail(&t[0], &m);

        t[1].rx_buf = buf;
        t[1].len = len;
+       t[1].bitwidth = flash->spi->rx_bitwidth;
        spi_message_add_tail(&t[1], &m);

        mutex_lock(&flash->lock);
@@ -370,15 +396,13 @@ static int m25p80_read(struct mtd_info *mtd,
loff_t from, size_t len,
         * supports that opcode.
         */

-       /* Set up the write data buffer. */
-       opcode = flash->fast_read ? OPCODE_FAST_READ : OPCODE_NORM_READ;
-       flash->command[0] = opcode;
+       /* Set up opcode in read buffer */
+       flash->command[0] = flash->read_opcode;
        m25p_addr2cmd(flash, from, flash->command);

        spi_sync(flash->spi, &m);

-       *retlen = m.actual_length - m25p_cmdsz(flash) -
-                       (flash->fast_read ? 1 : 0);
+       *retlen = m.actual_length - m25p_cmdsz(flash) - flash->read_dummy;

        mutex_unlock(&flash->lock);

@@ -409,6 +433,7 @@ static int m25p80_write(struct mtd_info *mtd,
loff_t to, size_t len,
        spi_message_add_tail(&t[0], &m);

        t[1].tx_buf = buf;
+       t[1].bitwidth = flash->spi->tx_bitwidth;
        spi_message_add_tail(&t[1], &m);

        mutex_lock(&flash->lock);
@@ -422,7 +447,7 @@ static int m25p80_write(struct mtd_info *mtd,
loff_t to, size_t len,
        write_enable(flash);

        /* Set up the opcode in the write buffer. */
-       flash->command[0] = OPCODE_PP;
+       flash->command[0] = flash->write_opcode;
        m25p_addr2cmd(flash, to, flash->command);

        page_offset = to & (flash->page_size - 1);
@@ -958,8 +983,7 @@ static int m25p_probe(struct spi_device *spi)
        flash = kzalloc(sizeof *flash, GFP_KERNEL);
        if (!flash)
                return -ENOMEM;
-       flash->command = kmalloc(MAX_CMD_SIZE + (flash->fast_read ? 1 : 0),
-                                       GFP_KERNEL);
+       flash->command = kmalloc(MAX_CMD_SIZE + FAST_READ_DUMMY_BYTE,
GFP_KERNEL);
        if (!flash->command) {
                kfree(flash);
                return -ENOMEM;
@@ -1022,16 +1046,6 @@ static int m25p_probe(struct spi_device *spi)
        flash->page_size = info->page_size;
        flash->mtd.writebufsize = flash->page_size;

-       flash->fast_read = false;
-#ifdef CONFIG_OF
-       if (np && of_property_read_bool(np, "m25p,fast-read"))
-               flash->fast_read = true;
-#endif
-
-#ifdef CONFIG_M25PXX_USE_FAST_READ
-       flash->fast_read = true;
-#endif
-
        if (info->addr_width)
                flash->addr_width = info->addr_width;
        else {
@@ -1063,6 +1077,43 @@ static int m25p_probe(struct spi_device *spi)
                                flash->mtd.eraseregions[i].erasesize / 1024,
                                flash->mtd.eraseregions[i].numblocks);

+       if ((flash->spi->rx_bitwidth == SPI_BITWIDTH_QUAD) ||
+               (flash->spi->tx_bitwidth == SPI_BITWIDTH_QUAD)) {
+               write_enable(flash);
+               write_cr(flash, OPCODE_QEN);
+       }
+
+       switch (flash->spi->rx_bitwidth) {
+       case SPI_BITWIDTH_SINGLE:
+               flash->read_opcode = OPCODE_READ;
+               flash->read_dummy = FAST_READ_DUMMY_BYTE;
+               break;
+       case SPI_BITWIDTH_DUAL:
+               flash->read_opcode = OPCODE_DOR;
+               flash->read_dummy = 1;
+               break;
+       case SPI_BITWIDTH_QUAD:
+               flash->read_opcode = OPCODE_QOR;
+               flash->read_dummy = 1;
+               break;
+       default:
+               dev_err(&spi->dev, "invalid rx_bitwidth %d\n",
+                       flash->spi->rx_bitwidth);
+               return -EINVAL;
+       }
+
+       switch (flash->spi->tx_bitwidth) {
+       case SPI_BITWIDTH_SINGLE:
+               flash->write_opcode = OPCODE_PP;
+               break;
+       case SPI_BITWIDTH_QUAD:
+               flash->write_opcode = OPCODE_QPP;
+               break;
+       default:
+               dev_err(&spi->dev, "invalid tx_bitwidth %d\n",
+                       flash->spi->tx_bitwidth);
+               return -EINVAL;
+       }

        /* partitions should match sector boundaries; and it may be good to
         * use readonly partitions for writeprotected sectors (BP2..BP0).
diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c
index 004b10f..cd99022 100644
--- a/drivers/spi/spi.c
+++ b/drivers/spi/spi.c
@@ -452,6 +452,8 @@ struct spi_device *spi_new_device(struct spi_master *master,
        proxy->irq = chip->irq;
        strlcpy(proxy->modalias, chip->modalias, sizeof(proxy->modalias));
        proxy->dev.platform_data = (void *) chip->platform_data;
+       proxy->rx_bitwidth = chip->rx_bitwidth;
+       proxy->tx_bitwidth = chip->tx_bitwidth;
        proxy->controller_data = chip->controller_data;
        proxy->controller_state = NULL;

diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h
index 38c2b92..ddcf308 100644
--- a/include/linux/spi/spi.h
+++ b/include/linux/spi/spi.h
@@ -93,6 +93,8 @@ struct spi_device {
        void                    *controller_data;
        char                    modalias[SPI_NAME_SIZE];
        int                     cs_gpio;        /* chip select gpio */
+       u8                      rx_bitwidth;
+       u8                      tx_bitwidth;

        /*
         * likely need more hooks for more protocol options affecting how
@@ -511,6 +513,10 @@ struct spi_transfer {
        dma_addr_t      rx_dma;

        unsigned        cs_change:1;
+       u8              bitwidth;
+#define        SPI_BITWIDTH_SINGLE     0x01; /* 1bit transfer */
+#define        SPI_BITWIDTH_DUAL       0x02; /* 2bits transfer */
+#define        SPI_BITWIDTH_QUAD       0x03; /* 4bits transfer */
        u8              bits_per_word;
        u16             delay_usecs;
        u32             speed_hz;
@@ -859,6 +865,8 @@ struct spi_board_info {
         * where the default of SPI_CS_HIGH = 0 is wrong.
         */
        u8              mode;
+       u8              rx_bitwidth;
+       u8              tx_bitwidth;

        /* ... may need additional spi_device chip config data here.
         * avoid stuff protocol drivers can set; but include stuff



More information about the linux-arm-kernel mailing list