lkml.org 
[lkml]   [2016]   [Oct]   [4]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[PATCH 5/9] mtd: m25p80: add support of dual and quad spi protocols to all commands
    Date
    Before this patch, m25p80_read() supported few SPI protocols:
    - regular SPI 1-1-1
    - SPI Dual Output 1-1-2
    - SPI Quad Output 1-1-4
    On the other hand, all other m25p80_*() hooks only supported SPI 1-1-1.

    This patch adds support to all currently existing SPI protocols to
    cover as many protocols as possible.

    Signed-off-by: Cyrille Pitchen <cyrille.pitchen@atmel.com>
    ---
    drivers/mtd/devices/m25p80.c | 193 +++++++++++++++++++++++++++++++++----------
    1 file changed, 149 insertions(+), 44 deletions(-)

    diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c
    index f0a55c01406b..38778eac6c21 100644
    --- a/drivers/mtd/devices/m25p80.c
    +++ b/drivers/mtd/devices/m25p80.c
    @@ -27,22 +27,64 @@
    #include <linux/spi/flash.h>
    #include <linux/mtd/spi-nor.h>

    -#define MAX_CMD_SIZE 6
    +#define MAX_CMD_SIZE 16
    struct m25p {
    struct spi_device *spi;
    struct spi_nor spi_nor;
    u8 command[MAX_CMD_SIZE];
    };

    +static inline int m25p80_proto2nbits(enum spi_nor_protocol proto,
    + unsigned *code_nbits,
    + unsigned *addr_nbits,
    + unsigned *data_nbits)
    +{
    + if (code_nbits)
    + *code_nbits = SNOR_PROTO_CODE_FROM_PROTO(proto);
    + if (addr_nbits)
    + *addr_nbits = SNOR_PROTO_ADDR_FROM_PROTO(proto);
    + if (data_nbits)
    + *data_nbits = SNOR_PROTO_DATA_FROM_PROTO(proto);
    +
    + return 0;
    +}
    +
    static int m25p80_read_reg(struct spi_nor *nor, u8 code, u8 *val, int len)
    {
    struct m25p *flash = nor->priv;
    struct spi_device *spi = flash->spi;
    + unsigned code_nbits, data_nbits;
    + struct spi_transfer xfers[2];
    int ret;

    - ret = spi_write_then_read(spi, &code, 1, val, len);
    + /* Check the total length of command op code and data. */
    + if (len + 1 > MAX_CMD_SIZE)
    + return -EINVAL;
    +
    + /* Get transfer protocols (addr_nbits is not relevant here). */
    + ret = m25p80_proto2nbits(nor->reg_proto,
    + &code_nbits, NULL, &data_nbits);
    + if (ret < 0)
    + return ret;
    +
    + /* Set up transfers. */
    + memset(xfers, 0, sizeof(xfers));
    +
    + flash->command[0] = code;
    + xfers[0].len = 1;
    + xfers[0].tx_buf = flash->command;
    + xfers[0].tx_nbits = code_nbits;
    +
    + xfers[1].len = len;
    + xfers[1].rx_buf = &flash->command[1];
    + xfers[1].rx_nbits = data_nbits;
    +
    + /* Process command. */
    + ret = spi_sync_transfer(spi, xfers, 2);
    if (ret < 0)
    dev_err(&spi->dev, "error %d reading %x\n", ret, code);
    + else
    + memcpy(val, &flash->command[1], len);

    return ret;
    }
    @@ -65,12 +107,42 @@ static int m25p80_write_reg(struct spi_nor *nor, u8 opcode, u8 *buf, int len)
    {
    struct m25p *flash = nor->priv;
    struct spi_device *spi = flash->spi;
    + unsigned code_nbits, data_nbits, num_xfers = 1;
    + struct spi_transfer xfers[2];
    + int ret;
    +
    + /* Check the total length of command op code and data. */
    + if (buf && (len + 1 > MAX_CMD_SIZE))
    + return -EINVAL;
    +
    + /* Get transfer protocols (addr_nbits is not relevant here). */
    + ret = m25p80_proto2nbits(nor->reg_proto,
    + &code_nbits, NULL, &data_nbits);
    + if (ret < 0)
    + return ret;
    +
    + /* Set up transfer(s). */
    + memset(xfers, 0, sizeof(xfers));

    flash->command[0] = opcode;
    - if (buf)
    + xfers[0].len = 1;
    + xfers[0].tx_buf = flash->command;
    + xfers[0].tx_nbits = code_nbits;
    +
    + if (buf) {
    memcpy(&flash->command[1], buf, len);
    + if (data_nbits == code_nbits) {
    + xfers[0].len += len;
    + } else {
    + xfers[1].len = len;
    + xfers[1].tx_buf = &flash->command[1];
    + xfers[1].tx_nbits = data_nbits;
    + num_xfers++;
    + }
    + }

    - return spi_write(spi, flash->command, len + 1);
    + /* Process command. */
    + return spi_sync_transfer(spi, xfers, num_xfers);
    }

    static ssize_t m25p80_write(struct spi_nor *nor, loff_t to, size_t len,
    @@ -78,27 +150,48 @@ static ssize_t m25p80_write(struct spi_nor *nor, loff_t to, size_t len,
    {
    struct m25p *flash = nor->priv;
    struct spi_device *spi = flash->spi;
    - struct spi_transfer t[2] = {};
    + unsigned code_nbits, addr_nbits, data_nbits, num_xfers = 1;
    + struct spi_transfer xfers[3];
    struct spi_message m;
    int cmd_sz = m25p_cmdsz(nor);
    ssize_t ret;

    - spi_message_init(&m);
    -
    if (nor->program_opcode == SPINOR_OP_AAI_WP && nor->sst_write_second)
    cmd_sz = 1;

    - flash->command[0] = nor->program_opcode;
    - m25p_addr2cmd(nor, to, flash->command);
    + /* Get transfer protocols. */
    + ret = m25p80_proto2nbits(nor->write_proto,
    + &code_nbits, &addr_nbits, &data_nbits);
    + if (ret < 0)
    + return ret;

    - t[0].tx_buf = flash->command;
    - t[0].len = cmd_sz;
    - spi_message_add_tail(&t[0], &m);
    + /* Set up transfers. */
    + memset(xfers, 0, sizeof(xfers));
    +
    + flash->command[0] = nor->program_opcode;
    + xfers[0].len = 1;
    + xfers[0].tx_buf = flash->command;
    + xfers[0].tx_nbits = code_nbits;
    +
    + if (cmd_sz > 1) {
    + m25p_addr2cmd(nor, to, flash->command);
    + if (addr_nbits == code_nbits) {
    + xfers[0].len += nor->addr_width;
    + } else {
    + xfers[1].len = nor->addr_width;
    + xfers[1].tx_buf = &flash->command[1];
    + xfers[1].tx_nbits = addr_nbits;
    + num_xfers++;
    + }
    + }

    - t[1].tx_buf = buf;
    - t[1].len = len;
    - spi_message_add_tail(&t[1], &m);
    + xfers[num_xfers].len = len;
    + xfers[num_xfers].tx_buf = buf;
    + xfers[num_xfers].tx_nbits = data_nbits;
    + num_xfers++;

    + /* Process command. */
    + spi_message_init_with_transfers(&m, xfers, num_xfers);
    ret = spi_sync(spi, &m);
    if (ret)
    return ret;
    @@ -109,18 +202,6 @@ static ssize_t m25p80_write(struct spi_nor *nor, loff_t to, size_t len,
    return ret;
    }

    -static inline unsigned int m25p80_rx_nbits(struct spi_nor *nor)
    -{
    - switch (SNOR_PROTO_DATA_FROM_PROTO(nor->read_proto)) {
    - case 2:
    - return 2;
    - case 4:
    - return 4;
    - default:
    - return 0;
    - }
    -}
    -
    /*
    * Read an address range from the nor chip. The address range
    * may be any size provided it is within the physical boundaries.
    @@ -130,14 +211,22 @@ static ssize_t m25p80_read(struct spi_nor *nor, loff_t from, size_t len,
    {
    struct m25p *flash = nor->priv;
    struct spi_device *spi = flash->spi;
    - struct spi_transfer t[2];
    - struct spi_message m;
    + unsigned code_nbits, addr_nbits, data_nbits, num_xfers = 1;
    unsigned int dummy = nor->read_dummy;
    ssize_t ret;
    + struct spi_transfer xfers[3];
    + struct spi_message m;
    +
    + /* Get transfer protocols. */
    + ret = m25p80_proto2nbits(nor->read_proto,
    + &code_nbits, &addr_nbits, &data_nbits);
    + if (ret < 0)
    + return ret;

    /* convert the dummy cycles to the number of bytes */
    - dummy /= 8;
    + dummy = (dummy * addr_nbits) / 8;

    + /* Use the SPI flash API if supported. */
    if (spi_flash_read_supported(spi)) {
    struct spi_flash_read_message msg;

    @@ -149,10 +238,9 @@ static ssize_t m25p80_read(struct spi_nor *nor, loff_t from, size_t len,
    msg.read_opcode = nor->read_opcode;
    msg.addr_width = nor->addr_width;
    msg.dummy_bytes = dummy;
    - /* TODO: Support other combinations */
    - msg.opcode_nbits = SPI_NBITS_SINGLE;
    - msg.addr_nbits = SPI_NBITS_SINGLE;
    - msg.data_nbits = m25p80_rx_nbits(nor);
    + msg.opcode_nbits = code_nbits;
    + msg.addr_nbits = addr_nbits;
    + msg.data_nbits = data_nbits;

    ret = spi_flash_read(spi, &msg);
    if (ret < 0)
    @@ -160,21 +248,38 @@ static ssize_t m25p80_read(struct spi_nor *nor, loff_t from, size_t len,
    return msg.retlen;
    }

    - spi_message_init(&m);
    - memset(t, 0, (sizeof t));
    + /* Set up transfers. */
    + memset(xfers, 0, sizeof(xfers));

    flash->command[0] = nor->read_opcode;
    - m25p_addr2cmd(nor, from, flash->command);
    + xfers[0].len = 1;
    + xfers[0].tx_buf = flash->command;
    + xfers[0].tx_nbits = code_nbits;

    - t[0].tx_buf = flash->command;
    - t[0].len = m25p_cmdsz(nor) + dummy;
    - spi_message_add_tail(&t[0], &m);
    + m25p_addr2cmd(nor, from, flash->command);
    + /*
    + * Clear all dummy/mode cycle bits to avoid sending some manufacturer
    + * specific pattern, which might make the memory enter its Continuous
    + * Read mode by mistake.
    + */
    + memset(flash->command + 1 + nor->addr_width, 0, dummy);
    +
    + if (addr_nbits == code_nbits) {
    + xfers[0].len += nor->addr_width + dummy;
    + } else {
    + xfers[1].len = nor->addr_width + dummy;
    + xfers[1].tx_buf = &flash->command[1];
    + xfers[1].tx_nbits = addr_nbits;
    + num_xfers++;
    + }

    - t[1].rx_buf = buf;
    - t[1].rx_nbits = m25p80_rx_nbits(nor);
    - t[1].len = min(len, spi_max_transfer_size(spi));
    - spi_message_add_tail(&t[1], &m);
    + xfers[num_xfers].len = len;
    + xfers[num_xfers].rx_buf = buf;
    + xfers[num_xfers].rx_nbits = data_nbits;
    + num_xfers++;

    + /* Process command. */
    + spi_message_init_with_transfers(&m, xfers, num_xfers);
    ret = spi_sync(spi, &m);
    if (ret)
    return ret;
    --
    2.7.4
    \
     
     \ /
      Last update: 2016-10-04 18:37    [W:2.685 / U:0.032 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site