From: Cyrille Pitchen <cyrille.pitchen@atmel.com>
To: <computersforpeace@gmail.com>, <linux-mtd@lists.infradead.org>
Cc: <nicolas.ferre@atmel.com>, <marex@denx.de>, <vigneshr@ti.com>,
<linux-kernel@vger.kernel.org>,
<linux-arm-kernel@lists.infradead.org>,
<devicetree@vger.kernel.org>, <robh+dt@kernel.org>,
<pawel.moll@arm.com>, <mark.rutland@arm.com>,
<ijc+devicetree@hellion.org.uk>, <galak@codeaurora.org>,
Cyrille Pitchen <cyrille.pitchen@atmel.com>
Subject: [PATCH linux-next 3/5] mtd: m25p80: add support of dual and quad spi protocols to all commands
Date: Mon, 7 Dec 2015 15:09:12 +0100 [thread overview]
Message-ID: <3adb9e15b7d1e3b4e3bc5b53788ad679409b1025.1449494420.git.cyrille.pitchen@atmel.com> (raw)
In-Reply-To: <cover.1449494420.git.cyrille.pitchen@atmel.com>
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.
However once their Quad mode enabled, Micron and Macronix spi-nor memories
expect all commands to use the SPI 4-4-4 protocol.
Also, once their Dual mode enabled, Micron spi-nor memories expect all
commands to use the SPI-2-2-2 protocol.
So 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 | 233 +++++++++++++++++++++++++++++++++++--------
1 file changed, 193 insertions(+), 40 deletions(-)
diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c
index c9c3b7fa3051..8b09f77eeffb 100644
--- a/drivers/mtd/devices/m25p80.c
+++ b/drivers/mtd/devices/m25p80.c
@@ -27,22 +27,114 @@
#include <linux/spi/flash.h>
#include <linux/mtd/spi-nor.h>
-#define MAX_CMD_SIZE 6
+#define MAX_CMD_SIZE 8
struct m25p {
struct spi_device *spi;
struct spi_nor spi_nor;
u8 command[MAX_CMD_SIZE];
};
+static inline int m25p80_proto2nbits(enum spi_protocol proto,
+ unsigned *code_nbits,
+ unsigned *addr_nbits,
+ unsigned *data_nbits)
+{
+ unsigned code, addr, data;
+
+ switch (proto) {
+ case SPI_PROTO_1_1_1:
+ code = SPI_NBITS_SINGLE;
+ addr = SPI_NBITS_SINGLE;
+ data = SPI_NBITS_SINGLE;
+ break;
+
+ case SPI_PROTO_1_1_2:
+ code = SPI_NBITS_SINGLE;
+ addr = SPI_NBITS_SINGLE;
+ data = SPI_NBITS_DUAL;
+ break;
+
+ case SPI_PROTO_1_1_4:
+ code = SPI_NBITS_SINGLE;
+ addr = SPI_NBITS_SINGLE;
+ data = SPI_NBITS_QUAD;
+ break;
+
+ case SPI_PROTO_1_2_2:
+ code = SPI_NBITS_SINGLE;
+ addr = SPI_NBITS_DUAL;
+ data = SPI_NBITS_DUAL;
+ break;
+
+ case SPI_PROTO_1_4_4:
+ code = SPI_NBITS_SINGLE;
+ addr = SPI_NBITS_QUAD;
+ data = SPI_NBITS_QUAD;
+ break;
+
+ case SPI_PROTO_2_2_2:
+ code = SPI_NBITS_DUAL;
+ addr = SPI_NBITS_DUAL;
+ data = SPI_NBITS_DUAL;
+ break;
+
+ case SPI_PROTO_4_4_4:
+ code = SPI_NBITS_QUAD;
+ addr = SPI_NBITS_QUAD;
+ data = SPI_NBITS_QUAD;
+ break;
+
+ default:
+ return -EINVAL;
+
+ }
+
+ if (code_nbits)
+ *code_nbits = code;
+ if (addr_nbits)
+ *addr_nbits = addr;
+ if (data_nbits)
+ *data_nbits = data;
+
+ 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 +157,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 void m25p80_write(struct spi_nor *nor, loff_t to, size_t len,
@@ -78,43 +200,54 @@ static void 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);
-
- spi_message_init(&m);
+ int ret, cmd_sz = m25p_cmdsz(nor);
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) {
+ *retlen = 0;
+ return;
+ }
- 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));
- t[1].tx_buf = buf;
- t[1].len = len;
- spi_message_add_tail(&t[1], &m);
+ 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++;
+ }
+ }
+
+ 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);
spi_sync(spi, &m);
*retlen += m.actual_length - cmd_sz;
}
-static inline unsigned int m25p80_rx_nbits(struct spi_nor *nor)
-{
- switch (nor->flash_read) {
- case SPI_NOR_DUAL:
- return 2;
- case SPI_NOR_QUAD:
- 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.
@@ -124,28 +257,48 @@ static int 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;
+ struct spi_transfer xfers[3];
+ struct spi_message m;
+ int ret;
+
+ /* Get transfer protocols. */
+ ret = m25p80_proto2nbits(nor->read_proto,
+ &code_nbits, &addr_nbits, &data_nbits);
+ if (ret < 0) {
+ *retlen = 0;
+ return ret;
+ }
/* convert the dummy cycles to the number of bytes */
dummy /= 8;
- 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);
+ 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 = len;
- 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);
spi_sync(spi, &m);
*retlen = m.actual_length - m25p_cmdsz(nor) - dummy;
--
1.8.2.2
next prev parent reply other threads:[~2015-12-07 14:10 UTC|newest]
Thread overview: 26+ messages / expand[flat|nested] mbox.gz Atom feed top
2015-12-07 14:09 [PATCH linux-next 0/5] mtd: spi-nor: add driver for Atmel QSPI controller Cyrille Pitchen
2015-12-07 14:09 ` [PATCH linux-next 1/5] mtd: spi-nor: properly detect the memory when it boots in Quad or Dual mode Cyrille Pitchen
2015-12-18 1:55 ` Brian Norris
2015-12-18 11:19 ` Cyrille Pitchen
2016-01-04 16:50 ` Cyrille Pitchen
2015-12-18 2:08 ` Brian Norris
2015-12-07 14:09 ` [PATCH linux-next 2/5] mtd: spi-nor: fix Quad SPI mode support for Spansion, Micron and Macronix Cyrille Pitchen
2015-12-18 2:18 ` Brian Norris
2016-01-04 16:12 ` Cyrille Pitchen
2015-12-07 14:09 ` Cyrille Pitchen [this message]
2015-12-07 14:09 ` [PATCH linux-next 4/5] Documentation: atmel-quadspi: add binding file for Atmel QSPI driver Cyrille Pitchen
2015-12-09 3:16 ` Rob Herring
2015-12-11 9:26 ` Nicolas Ferre
2015-12-07 14:09 ` [PATCH linux-next 5/5] mtd: atmel-quadspi: add driver for Atmel QSPI controller Cyrille Pitchen
2015-12-07 15:25 ` [PATCH] mtd: atmel-quadspi: fix compare_const_fl.cocci warnings kbuild test robot
2015-12-07 15:25 ` [PATCH linux-next 5/5] mtd: atmel-quadspi: add driver for Atmel QSPI controller kbuild test robot
2015-12-07 15:25 ` [PATCH] mtd: atmel-quadspi: fix odd_ptr_err.cocci warnings kbuild test robot
2015-12-11 14:50 ` [PATCH linux-next 5/5] mtd: atmel-quadspi: add driver for Atmel QSPI controller Nicolas Ferre
2015-12-07 19:34 ` [PATCH linux-next 0/5] mtd: spi-nor: " Brian Norris
2015-12-08 6:21 ` Bean Huo 霍斌斌 (beanhuo)
2015-12-18 0:29 ` Brian Norris
2015-12-18 0:41 ` Brian Norris
2016-01-20 3:41 ` Bean Huo 霍斌斌 (beanhuo)
2015-12-08 6:44 ` Bean Huo 霍斌斌 (beanhuo)
2015-12-08 10:25 ` Cyrille Pitchen
2015-12-08 14:32 ` Cyrille Pitchen
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=3adb9e15b7d1e3b4e3bc5b53788ad679409b1025.1449494420.git.cyrille.pitchen@atmel.com \
--to=cyrille.pitchen@atmel.com \
--cc=computersforpeace@gmail.com \
--cc=devicetree@vger.kernel.org \
--cc=galak@codeaurora.org \
--cc=ijc+devicetree@hellion.org.uk \
--cc=linux-arm-kernel@lists.infradead.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-mtd@lists.infradead.org \
--cc=marex@denx.de \
--cc=mark.rutland@arm.com \
--cc=nicolas.ferre@atmel.com \
--cc=pawel.moll@arm.com \
--cc=robh+dt@kernel.org \
--cc=vigneshr@ti.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).