[U-Boot] [PATCH v2 2/2] sunxi_spi: add support for dual-IO flashes

Philipp Tomsich philipp.tomsich at theobroma-systems.com
Wed Mar 1 21:29:07 UTC 2017


The SPI controller in the Allwinner A64 SoC supports dual-IO for
the RX phase of transmission.  This can be used with the command
'fast read dual output' (cmd, addr and dummy byte are transmitted
in single-IO mode; data is received in dual-IO mode) to quickly
read out SPI flashes, when the device-tree marks the flash as
having 'spi-rx-bus-width = <2>'.

Unfortunately, the SPI-NOR flash layer in U-Boot does not manage
the single-IO and dual-IO transition (partially due to the fact
that spi_xfer(...) does not allow to convery such information),
but correctly chooses the FAST_READ_DUAL_OUTPUT (0x3b) opcode.
The net result of this is that a dual-IO read is initiated, but
the data reception will capture only every other bit...

This change puts a temporary fix in place, which identifies a
0x3b opcode being sent in a transaction with a SPI flash and then
manages the switching to dual-IO within the driver.

This change should be reverted, once more permanent solutions
in the higher layers and in the SPI driver model have been agreed
on and have been put in place.

Tested on an A64 (sun50iw1p1) against a Winbond W25Q80DV flash at
up to 100MHz (i.e. 200MBit/s read bursts).

X-AffectedPlatforms: A64-uQ7
Signed-off-by: Philipp Tomsich <philipp.tomsich at theobroma-systems.com>
---
 drivers/spi/sunxi_spi.c | 68 ++++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 67 insertions(+), 1 deletion(-)

diff --git a/drivers/spi/sunxi_spi.c b/drivers/spi/sunxi_spi.c
index f26becf..871c88d 100644
--- a/drivers/spi/sunxi_spi.c
+++ b/drivers/spi/sunxi_spi.c
@@ -24,6 +24,9 @@
 
 DECLARE_GLOBAL_DATA_PTR;
 
+/* The SPI flash opcode for a FAST READ DUAL OUTPUT operation. */
+#define CMD_READ_DUAL_OUTPUT_FAST 0x3b
+
 struct sunxi_spi_platdata {
 	void *base;
 	unsigned int max_hz;
@@ -47,7 +50,13 @@ struct sunxi_spi_driverdata {
 	unsigned int  fifo_depth;
 };
 
+enum {
+	NONE = 0,
+	OPC_READ_DUAL_CMD,
+};
+
 struct sunxi_spi_privdata {
+	int  transaction_type;
 	ulong last_transaction_us;	/* Time of last transaction end */
 	unsigned int hz_requested;      /* last requested bitrate */
 	unsigned int hz_actual;         /* currently set bitrate */
@@ -211,6 +220,43 @@ static inline uint8_t *spi_drain_readfifo(struct sunxi_spi_reg *spi,
 	return din;
 }
 
+static int sunxi_spi_trans_setup(struct sunxi_spi_privdata *priv,
+				 const uint8_t *dout,
+				 const uint8_t *din,
+				 unsigned int n_bytes)
+{
+	if (!dout) {
+		error("%s: SPI flash command requires at least an opcode\n",
+		      __func__);
+		return -EPROTO;
+	}
+
+	/* Detect dual-IO read commands */
+	if (dout[0] == CMD_READ_DUAL_OUTPUT_FAST) {
+		/* This is always called as two xfer-requests from the
+		 * higher layers:
+		 *  1. a write-only request with the 1-byte opcode,
+		 *     4-byte address and a dummy byte
+		 *  2. a read-only for the requested amount of data
+		 */
+
+		/* TODO: The "cmd, addr, dummy" sequence should be
+		 *       changed to "cmd, addr" w/ the controller
+		 *       generating the dummy cycles, so the Hi-Z
+		 *       state for IO0 and IO1 can already be
+		 *       generated during the dummy cycles.
+		 */
+		priv->transaction_type = OPC_READ_DUAL_CMD;
+	}
+
+	return 0;
+}
+
+static void sunxi_spi_trans_end(struct sunxi_spi_privdata *priv)
+{
+	priv->transaction_type = NONE;
+}
+
 static int sunxi_spi_xfer(struct udevice *dev, unsigned int bitlen,
 			  const void *out, void *in, unsigned long flags)
 {
@@ -239,6 +285,18 @@ static int sunxi_spi_xfer(struct udevice *dev, unsigned int bitlen,
 	      dev->name, __func__, spi, bitlen, din, flags, fifo_depth);
 
 	if (flags & SPI_XFER_BEGIN) {
+		/* For dual-IO support, we need to detect flash read
+		 * commands here... this is actually a layering
+		 * violation, but can't be fixed non-intrusively now
+		 * and other drivers (e.g. Freescale QSPI, Intel ICH)
+		 * follow this pattern as well.
+		 */
+		if (device_get_uclass_id(dev) == UCLASS_SPI_FLASH) {
+			ret = sunxi_spi_trans_setup(priv, dout, din, n_bytes);
+			if (ret < 0)
+				return ret;
+		}
+
 		ret = sunxi_spi_cs_activate(dev, slave->cs);
 		if (ret < 0) {
 			error("%s: failed to activate chip-select %d\n",
@@ -263,6 +321,12 @@ static int sunxi_spi_xfer(struct udevice *dev, unsigned int bitlen,
 	if (!din)
 		setbits_le32(&spi->TCR, TCR_DHB);
 
+	/* Set the dual-mode input bit */
+	if (priv->transaction_type == OPC_READ_DUAL_CMD)
+		setbits_le32(&spi->BCC, BIT(28));
+	else
+		clrbits_le32(&spi->BCC, BIT(28));
+
 	/* Transfer in blocks of FIFO_DEPTH */
 	while (n_bytes > 0) {
 		int cnt = (n_bytes < fifo_depth) ? n_bytes : fifo_depth;
@@ -300,8 +364,10 @@ static int sunxi_spi_xfer(struct udevice *dev, unsigned int bitlen,
 	}
 
  fail:
-	if (flags & SPI_XFER_END)
+	if (flags & SPI_XFER_END) {
 		sunxi_spi_cs_deactivate(dev, slave->cs);
+		sunxi_spi_trans_end(priv);
+	}
 
 	return 0;
 };
-- 
1.9.1



More information about the U-Boot mailing list