[U-Boot] [RFC/PATCH 5/7] drivers: mtd: qspi: Add quad read support

Sourav Poddar sourav.poddar at ti.com
Wed Jul 10 13:25:39 CEST 2013


From: Ravikumar Kattekola <rk at ti.com>

Add Quad read mode (6 pin interface) support to spi flash
and ti qspi driver.

Quad mode (0x6bh on spansion) uses two extra pins (D2 and D3) for
data transfer apart from the usual D0 and D1 pins thus transfering
4 bits per cycle.

Signed-off-by: Ravikumar Kattekola <rk at ti.com>
Signed-off-by: Sourav Poddar <sourav.poddar at ti.com>
---
 drivers/mtd/spi/spi_flash.c          |  110 +++++++++++++++++++++++++++++++++-
 drivers/mtd/spi/spi_flash_internal.h |    2 +
 drivers/spi/ti_qspi.c                |   18 ++++--
 include/configs/dra7xx_evm.h         |    1 +
 include/spi.h                        |    2 +
 5 files changed, 127 insertions(+), 6 deletions(-)

diff --git a/drivers/mtd/spi/spi_flash.c b/drivers/mtd/spi/spi_flash.c
index 5cc5669..f3094a2 100644
--- a/drivers/mtd/spi/spi_flash.c
+++ b/drivers/mtd/spi/spi_flash.c
@@ -42,7 +42,12 @@ static int spi_flash_read_write(struct spi_slave *spi,
 		debug("SF: Failed to send command (%zu bytes): %d\n",
 				cmd_len, ret);
 	} else if (data_len != 0) {
-		ret = spi_xfer(spi, data_len * 8, data_out, data_in, SPI_XFER_END);
+		if (spi->quad_enable)
+			flags = SPI_6WIRE;
+		else
+			flags = 0;
+
+		ret = spi_xfer(spi, data_len * 8, data_out, data_in, flags | SPI_XFER_END);
 		if (ret)
 			debug("SF: Failed to transfer %zu bytes of data: %d\n",
 					data_len, ret);
@@ -198,6 +203,51 @@ int spi_flash_cmd_read_fast(struct spi_flash *flash, u32 offset,
 #endif
 }
 
+int spi_flash_cmd_read_quad(struct spi_flash *flash, u32 offset,
+               size_t len, void *data)
+{
+	struct spi_slave *spi = flash->spi;
+
+	unsigned long page_addr, byte_addr, page_size;
+	size_t chunk_len, actual;
+	int ret = 0;
+	u8 cmd[5];
+
+	spi->quad_enable = 1;
+	/* Handle memory-mapped SPI */
+	if (flash->memory_map)
+		memcpy(data, flash->memory_map + offset, len);
+
+	page_size = flash->page_size;
+	page_addr = offset / page_size;
+	byte_addr = offset % page_size;
+
+	cmd[0] = CMD_READ_ARRAY_QUAD;
+	for (actual = 0; actual < len; actual += chunk_len) {
+		chunk_len = min(len - actual, page_size - byte_addr);
+
+		cmd[1] = page_addr >> 8;
+		cmd[2] = page_addr;
+		cmd[3] = byte_addr;
+		cmd[4] = 0x0;
+
+		ret = spi_flash_read_common(flash, cmd, sizeof(cmd),
+				data + actual, chunk_len);
+		if (ret < 0) {
+			debug("SF: read failed");
+			break;
+		}
+
+		byte_addr += chunk_len;
+		if (byte_addr == page_size) {
+			page_addr++;
+			byte_addr = 0;
+		}
+	}
+
+	return ret;
+}
+
 int spi_flash_cmd_poll_bit(struct spi_flash *flash, unsigned long timeout,
 			   u8 cmd, u8 poll_bit)
 {
@@ -320,6 +370,56 @@ int spi_flash_cmd_write_status(struct spi_flash *flash, u8 sr)
 	return 0;
 }
 
+int spi_flash_en_quad_mode(struct spi_flash *flash)
+{
+	u8 stat, con, cd;
+	u16 cr;
+	int ret;
+	cd = CMD_WRITE_STATUS;
+
+	ret = spi_flash_cmd_write_enable(flash);
+	if (ret < 0) {
+		debug("SF: enabling write failed\n");
+		goto out;
+	}
+	ret = spi_flash_cmd(flash->spi, CMD_READ_STATUS, &stat, 1);
+	ret = spi_flash_cmd(flash->spi, CMD_READ_CONFIG, &con, 1);
+	if (ret < 0) {
+		debug("%s: SF: read CR failed\n", __func__);
+		goto out;
+	}
+	/* Byte 1 - status reg, Byte 2 - config reg */
+	cr = ((con | 0x1 << 1) << 8) | (stat << 0);
+
+	ret = spi_flash_cmd_write(flash->spi, &cd, 1, &cr, 2);
+	if (ret) {
+		debug("SF: fail to write conf register\n");
+		goto out;
+	}
+
+	ret = spi_flash_cmd_wait_ready(flash, SPI_FLASH_PROG_TIMEOUT);
+	if (ret < 0) {
+		debug("SF: write conf register timed out\n");
+		goto out;
+	}
+
+	ret = spi_flash_cmd(flash->spi, CMD_READ_STATUS, &stat, 1);
+	ret = spi_flash_cmd(flash->spi, CMD_READ_CONFIG, &con, 1);
+	if (ret < 0) {
+		debug("%s: SF: read CR failed\n", __func__);
+		goto out;
+	}
+	debug("%s: *** CR = %x\n", __func__, con);
+
+	ret = spi_flash_cmd_write_disable(flash);
+	if (ret < 0) {
+		debug("SF: disabling write failed\n");
+		goto out;
+	}
+out:
+	return ret;
+}
+
 #ifdef CONFIG_OF_CONTROL
 int spi_flash_decode_fdt(const void *blob, struct spi_flash *flash)
 {
@@ -464,6 +564,10 @@ struct spi_flash *spi_flash_probe(unsigned int bus, unsigned int cs,
 		goto err_manufacturer_probe;
 	}
 
+#ifdef CONFIG_SF_QUAD_RD
+	spi_flash_en_quad_mode(flash);
+#endif
+
 #ifdef CONFIG_OF_CONTROL
 	if (spi_flash_decode_fdt(gd->fdt_blob, flash)) {
 		debug("SF: FDT decode error\n");
@@ -507,7 +611,11 @@ void *spi_flash_do_alloc(int offset, int size, struct spi_slave *spi,
 	flash->spi = spi;
 	flash->name = name;
 
+#ifdef CONFIG_SF_QUAD_RD
+	flash->read = spi_flash_cmd_read_quad;
+#else
 	flash->read = spi_flash_cmd_read_fast;
+#endif
 	flash->write = spi_flash_cmd_write_multi;
 	flash->erase = spi_flash_cmd_erase;
 
diff --git a/drivers/mtd/spi/spi_flash_internal.h b/drivers/mtd/spi/spi_flash_internal.h
index 141cfa8..6dc2d6d 100644
--- a/drivers/mtd/spi/spi_flash_internal.h
+++ b/drivers/mtd/spi/spi_flash_internal.h
@@ -17,11 +17,13 @@
 
 #define CMD_READ_ARRAY_SLOW		0x03
 #define CMD_READ_ARRAY_FAST		0x0b
+#define CMD_READ_ARRAY_QUAD		0x6b
 
 #define CMD_WRITE_STATUS		0x01
 #define CMD_PAGE_PROGRAM		0x02
 #define CMD_WRITE_DISABLE		0x04
 #define CMD_READ_STATUS			0x05
+#define CMD_READ_CONFIG			0x35
 #define CMD_WRITE_ENABLE		0x06
 #define CMD_ERASE_4K			0x20
 #define CMD_ERASE_32K			0x52
diff --git a/drivers/spi/ti_qspi.c b/drivers/spi/ti_qspi.c
index 1973b85..12bba11 100644
--- a/drivers/spi/ti_qspi.c
+++ b/drivers/spi/ti_qspi.c
@@ -72,6 +72,7 @@ static struct qspi_regs *qspi = (struct qspi_regs *)QSPI_BASE;
 #define QSPI_RD_SNGL			(1 << 16)
 #define QSPI_WR_SNGL			(2 << 16)
 #define QSPI_INVAL			(4 << 16)
+#define QSPI_RD_QUAD			(7 << 16)
 
 /* Device Control */
 #define QSPI_DD(m, n)			(m << (3 + n*8))
@@ -235,10 +236,17 @@ int spi_xfer(struct spi_slave *slave, unsigned int bitlen, const void *dout,
 			debug("tx done, status %08x\n", status);
 		}
 		if (rxp) {
-			debug("rx cmd %08x dc %08x\n",
-			      qslave->cmd | QSPI_RD_SNGL, qslave->dc);
-			writel(qslave->dc, &qspi->spi_dc);
-			writel(qslave->cmd | QSPI_RD_SNGL, &qspi->spi_cmd);
+			if (flags & SPI_6WIRE) {
+				debug("rx cmd %08x dc %08x\n",
+				      qslave->cmd | QSPI_RD_QUAD, qslave->dc);
+				writel(qslave->cmd | QSPI_RD_QUAD,
+				       &qspi->spi_cmd);
+			} else {
+				debug("rx cmd %08x dc %08x\n",
+				      qslave->cmd | QSPI_RD_SNGL, qslave->dc);
+				writel(qslave->cmd | QSPI_RD_SNGL,
+				       &qspi->spi_cmd);
+			}
 			status = readl(&qspi->spi_status);
 			timeout = QSPI_TIMEOUT;
 			while ((status & QSPI_WC_BUSY) != QSPI_XFER_DONE) {
@@ -250,7 +258,7 @@ int spi_xfer(struct spi_slave *slave, unsigned int bitlen, const void *dout,
 			}
 			*rxp++ = readl(&qspi->spi_data);
 			debug("rx done, status %08x, read %02x\n",
-			      status, *(rxp-1));
+				 status, *(rxp-1));
 		}
 	}
 
diff --git a/include/configs/dra7xx_evm.h b/include/configs/dra7xx_evm.h
index 0583858..f16920b 100644
--- a/include/configs/dra7xx_evm.h
+++ b/include/configs/dra7xx_evm.h
@@ -57,6 +57,7 @@
 #define CONFIG_SPI_FLASH_SPANSION
 #define CONFIG_CMD_SF
 #define CONFIG_CMD_SPI
+#define CONFIG_SF_QUAD_RD
 #define CONFIG_SF_DEFAULT_SPEED		12000000
 #define CONFIG_DEFAULT_SPI_MODE		SPI_MODE_3
 
diff --git a/include/spi.h b/include/spi.h
index 3fe2e1e..13280dc 100644
--- a/include/spi.h
+++ b/include/spi.h
@@ -37,6 +37,7 @@
 #define	SPI_LSB_FIRST	0x08			/* per-word bits-on-wire */
 #define	SPI_3WIRE	0x10			/* SI/SO signals shared */
 #define	SPI_LOOP	0x20			/* loopback mode */
+#define	SPI_6WIRE	0x10			/* SI/SO signals shared */
 
 /* SPI transfer flags */
 #define SPI_XFER_BEGIN	0x01			/* Assert CS before transfer */
@@ -56,6 +57,7 @@ struct spi_slave {
 	unsigned int	bus;
 	unsigned int	cs;
 	unsigned int max_write_size;
+	bool quad_enable;
 };
 
 /*-----------------------------------------------------------------------
-- 
1.7.1



More information about the U-Boot mailing list