[PATCH RFC] spi: add support for dual and quad IO modes

Johannes Stezenbach js at sig21.net
Mon Jun 18 11:06:57 EDT 2012


Some flashes and SPI masters support dual and quad IO modes
where data is transferred in parallel using two or four pins.
For now add this capability for mx25l25635e.

Signed-off-by: Johannes Stezenbach <js at sig21.net>
---

I'm planning to submit a similar patch for Linux sometime
within the next month or so.  Maybe you want to wait
for the review comments there before including it in barebox,
but I'm posting this anyway in case someone wants to comment.
At the moment I'm not able to post the SPI master driver
which implements it.  I'm not sure which if any of the SPI
masters already included in barebox have the dual and
quad mode capability, but it's important for boot time.
BTW, I have checked all users of struct spi_transfer
properly initialize it to zero so adding the
multi_io field should not cause problems.


 drivers/nor/m25p80.c |   33 +++++++++++++++++++++++++++------
 drivers/nor/m25p80.h |    6 ++++++
 drivers/spi/spi.c    |   10 ++++++++++
 include/spi/spi.h    |    9 +++++++++
 4 files changed, 52 insertions(+), 6 deletions(-)

diff --git a/drivers/nor/m25p80.c b/drivers/nor/m25p80.c
index 77669c2..7b28a1b 100644
--- a/drivers/nor/m25p80.c
+++ b/drivers/nor/m25p80.c
@@ -255,7 +255,7 @@ ssize_t m25p80_read(struct cdev *cdev, void *buf, size_t count, ulong offset, ul
 	struct m25p *flash = cdev->priv;
 	struct spi_transfer t[2];
 	struct spi_message m;
-	ssize_t retlen;
+	ssize_t retlen, cmdlen;
 
 	/* sanity checks */
 	if (!count)
@@ -271,12 +271,21 @@ ssize_t m25p80_read(struct cdev *cdev, void *buf, size_t count, ulong offset, ul
 	 * OPCODE_FAST_READ (if available) is faster.
 	 * Should add 1 byte DUMMY_BYTE.
 	 */
+	cmdlen = m25p_cmdsz(flash);
+	if (flash->dual_io || flash->quad_io)
+		cmdlen++;
+	else
+		cmdlen += FAST_READ_DUMMY_BYTE;
 	t[0].tx_buf = flash->command;
-	t[0].len = m25p_cmdsz(flash) + FAST_READ_DUMMY_BYTE;
+	t[0].len = cmdlen;
 	spi_message_add_tail(&t[0], &m);
 
 	t[1].rx_buf = buf;
 	t[1].len = count;
+	if (flash->quad_io)
+		t[1].multi_io = SPI_QUAD_IO;
+	else if (flash->dual_io)
+		t[1].multi_io = SPI_DUAL_IO;
 	spi_message_add_tail(&t[1], &m);
 
 	/* Byte count starts at zero. */
@@ -292,12 +301,17 @@ ssize_t m25p80_read(struct cdev *cdev, void *buf, size_t count, ulong offset, ul
 	 */
 
 	/* Set up the write data buffer. */
-	flash->command[0] = OPCODE_READ;
+	if (flash->quad_io)
+		flash->command[0] = OPCODE_QUAD_READ;
+	else if (flash->dual_io)
+		flash->command[0] = OPCODE_DUAL_READ;
+	else
+		flash->command[0] = OPCODE_READ;
 	m25p_addr2cmd(flash, offset, flash->command);
 
 	spi_sync(flash->spi, &m);
 
-	retlen = m.actual_length - m25p_cmdsz(flash) - FAST_READ_DUMMY_BYTE;
+	retlen = m.actual_length - cmdlen;
 
 	return retlen;
 }
@@ -552,7 +566,7 @@ static const struct spi_device_id m25p_ids[] = {
 	{ "mx25l6405d",  INFO(0xc22017, 0, 64 * 1024, 128, 0) },
 	{ "mx25l12805d", INFO(0xc22018, 0, 64 * 1024, 256, 0) },
 	{ "mx25l12855e", INFO(0xc22618, 0, 64 * 1024, 256, 0) },
-	{ "mx25l25635e", INFO(0xc22019, 0, 64 * 1024, 512, 0) },
+	{ "mx25l25635e", INFO(0xc22019, 0, 64 * 1024, 512, DUAL_IO | QUAD_IO) },
 	{ "mx25l25655e", INFO(0xc22619, 0, 64 * 1024, 512, 0) },
 
 	/* Spansion -- single (large) sector size only, at least
@@ -788,6 +802,10 @@ static int m25p_probe(struct device_d *dev)
 	} else {
 		flash->erase_opcode = OPCODE_SE;
 	}
+	if ((info->flags & DUAL_IO) && (spi->master->flags & SPI_MASTER_DUAL_IO))
+		flash->dual_io = 1;
+	if ((info->flags & QUAD_IO) && (spi->master->flags & SPI_MASTER_QUAD_IO))
+		flash->quad_io = 1;
 
 	flash->page_size = info->page_size;
 
@@ -802,7 +820,10 @@ static int m25p_probe(struct device_d *dev)
 			flash->addr_width = 3;
 	}
 
-	dev_info(dev, "%s (%lld Kbytes)\n", id->name, (long long)flash->size >> 10);
+	dev_info(dev, "%s (%lld Kbytes%s%s)\n", id->name,
+		 (long long)flash->size >> 10,
+		 flash->dual_io ? ", 2x" : "",
+		 flash->quad_io ? ", 4x" : "");
 
 	devfs_create(&flash->cdev);
 
diff --git a/drivers/nor/m25p80.h b/drivers/nor/m25p80.h
index 3f9dd9c..333cac2 100644
--- a/drivers/nor/m25p80.h
+++ b/drivers/nor/m25p80.h
@@ -7,6 +7,8 @@
 #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_DUAL_READ	0x3b	/* Read data bytes (2 IO lanes) */
+#define	OPCODE_QUAD_READ	0x6b	/* Read data bytes (4 IO lanes) */
 #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 */
@@ -61,6 +63,8 @@ struct m25p {
 	u16			page_size;
 	u16			addr_width;
 	u8			erase_opcode;
+	u8			dual_io:1;
+	u8			quad_io:1;
 	u8			*command;
 	u32			size;
 };
@@ -85,6 +89,8 @@ struct flash_info {
 	u16		flags;
 #define	SECT_4K		0x01		/* OPCODE_BE_4K works uniformly */
 #define	M25P_NO_ERASE	0x02		/* No erase command needed */
+#define DUAL_IO		0x04		/* can do 2 lane data transfers */
+#define QUAD_IO		0x08		/* can do 4 lane data transfers */
 };
 
 #endif
diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c
index a7fe10c..d5b521b 100644
--- a/drivers/spi/spi.c
+++ b/drivers/spi/spi.c
@@ -196,6 +196,16 @@ EXPORT_SYMBOL(spi_register_master);
 
 int spi_sync(struct spi_device *spi, struct spi_message *message)
 {
+	struct spi_transfer *t;
+
+	list_for_each_entry(t, &message->transfers, transfer_list) {
+		if ((t->multi_io == SPI_DUAL_IO) &&
+		    !(spi->master->flags & SPI_MASTER_DUAL_IO))
+			return -EINVAL;
+		if ((t->multi_io == SPI_QUAD_IO) &&
+		    !(spi->master->flags & SPI_MASTER_QUAD_IO))
+			return -EINVAL;
+	}
 	return spi->master->transfer(spi, message);
 }
 
diff --git a/include/spi/spi.h b/include/spi/spi.h
index 9d01d06..7ff444d 100644
--- a/include/spi/spi.h
+++ b/include/spi/spi.h
@@ -136,6 +136,10 @@ struct spi_master {
 	 */
 	u16			num_chipselect;
 
+	u16			flags;
+#define SPI_MASTER_DUAL_IO	BIT(0)	/* can do 2 lane data transfers */
+#define SPI_MASTER_QUAD_IO	BIT(1)	/* can do 4 lane data transfers */
+
 	/* setup mode and clock, etc (spi driver may call many times) */
 	int			(*setup)(struct spi_device *spi);
 
@@ -256,6 +260,11 @@ struct spi_transfer {
 	unsigned	len;
 
 	unsigned	cs_change:1;
+#define SPI_SINGLE_IO 0
+#define SPI_DUAL_IO 1
+#define SPI_QUAD_IO 2
+	unsigned	multi_io:2;
+
 	u8		bits_per_word;
 	u16		delay_usecs;
 	u32		speed_hz;
-- 
1.7.10.4




More information about the barebox mailing list