[PATCH 4/4] spi: orion: Software control for inter-word delays

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Add a SW-controlled timer for inserting delays between individual words
as transmitted over the SPI bus. The DT property name is loosely
modelled after a similar, but HW-based feature in spi-davinci.c (commit
365a7bb32e09) -- hence the DT property name.

My HW sucks. One of the less-serious troubles is that it requires a 3us
delay between all SPI words. It also requires "big" transfers, easily
around 40kB, which is 20k of 16-bit words. It's also a specialised,
proprietary thing with no need for an in-kernel driver, so I'm using
spidev to access it.

There's a limit in spidev's SPI_IOC_MESSAGE and ioctl architecture in
general which means that I can stash at most 512 individual
spi_ioc_transfer into one ioctl. When I needed to transfer small
pockets, that was enough -- I could simply build a series of hundreds of
spi_ioc_trnasfers, two bytes each, with a proper delay_usecs to persuade
the SPI core to implement these delays for me. However, this won't work
if I need to send more than 1kB of data that way.

It seems that there's nothing generic in Linux to implement this
feature. The TI's spi-davinci.c can do something liek this in HW. People
at various forums apparently want to do something similar on Zynq and on
RPis, perhaps in SW. I wasn't able to find any ready-made patches,
though. My SoC (88F68xx, Marvell Armada A38x) apparently cannot do this
natively, so this patch simply adds a call to udelay which does the
trick for me.

Signed-off-by: Jan Kundrát <jan.kundrat@xxxxxxxxx>
---
 Documentation/devicetree/bindings/spi/spi-orion.txt | 20 ++++++++++++++++++++
 drivers/spi/spi-orion.c                             | 19 ++++++++++++++++++-
 2 files changed, 38 insertions(+), 1 deletion(-)

diff --git a/Documentation/devicetree/bindings/spi/spi-orion.txt b/Documentation/devicetree/bindings/spi/spi-orion.txt
index 8434a65fc12a..ed35cba1bc33 100644
--- a/Documentation/devicetree/bindings/spi/spi-orion.txt
+++ b/Documentation/devicetree/bindings/spi/spi-orion.txt
@@ -29,6 +29,10 @@ Optional properties:
 		used, the name must be "core", and "axi" (the latter
 		is only for Armada 7K/8K).
 
+Optional properties of child nodes (SPI slave devices):
+- linux,spi-wdelay : If present and non-zero, specifies a delay in
+		     microseconds between words transferred over the SPI bus.
+
 
 Example:
        spi@10600 {
@@ -77,3 +81,19 @@ are used in the default indirect (PIO) mode):
 For further information on the MBus bindings, please see the MBus
 DT documentation:
 Documentation/devicetree/bindings/bus/mvebu-mbus.txt
+
+Example of a per-child inter-word delay:
+
+	spi0: spi@10600 {
+		/* ... */
+
+		some_slave_device@2 {
+			reg = <2>;
+			compatible = "something";
+			/* ... */
+
+			/* Wait 3 microseconds between all words within all
+			SPI transactions */
+			linux,spi-wdelay = /bits/ 16 <3>;
+		};
+	};
diff --git a/drivers/spi/spi-orion.c b/drivers/spi/spi-orion.c
index 1eccc2287079..1a9475857808 100644
--- a/drivers/spi/spi-orion.c
+++ b/drivers/spi/spi-orion.c
@@ -92,6 +92,7 @@ struct orion_direct_acc {
 
 struct orion_child_options {
 	struct orion_direct_acc direct_access;
+	u16			word_delay;
 };
 
 struct orion_spi {
@@ -469,6 +470,8 @@ orion_spi_write_read(struct spi_device *spi, struct spi_transfer *xfer)
 			if (orion_spi_write_read_8bit(spi, &tx, &rx) < 0)
 				goto out;
 			count--;
+			if (orion_spi->child[cs].word_delay)
+				udelay(orion_spi->child[cs].word_delay);
 		} while (count);
 	} else if (word_len == 16) {
 		const u16 *tx = xfer->tx_buf;
@@ -477,6 +480,8 @@ orion_spi_write_read(struct spi_device *spi, struct spi_transfer *xfer)
 		do {
 			if (orion_spi_write_read_16bit(spi, &tx, &rx) < 0)
 				goto out;
+			if (orion_spi->child[cs].word_delay)
+				udelay(orion_spi->child[cs].word_delay);
 			count -= 2;
 		} while (count);
 	}
@@ -681,7 +686,7 @@ static int orion_spi_probe(struct platform_device *pdev)
 		goto out_rel_axi_clk;
 	}
 
-	/* Scan all SPI devices of this controller for direct mapped devices */
+	/* Scan all SPI devices of this controller for direct mapped devices and word delay */
 	for_each_available_child_of_node(pdev->dev.of_node, np) {
 		u32 cs;
 
@@ -694,6 +699,12 @@ static int orion_spi_probe(struct platform_device *pdev)
 			continue;
 		}
 
+		spi->child[cs].word_delay = 0;
+		if (!of_property_read_u16(np, "linux,spi-wdelay",
+					&spi->child[cs].word_delay))
+			dev_info(&pdev->dev, "%pOF: %dus delay between words\n",
+					np, spi->child[cs].word_delay);
+
 		/*
 		 * Check if an address is configured for this SPI device. If
 		 * not, the MBus mapping via the 'ranges' property in the 'soc'
@@ -705,6 +716,12 @@ static int orion_spi_probe(struct platform_device *pdev)
 		if (status)
 			continue;
 
+		if (spi->child[cs].word_delay) {
+			dev_warn(&pdev->dev,
+				"%pOF linux,spi-wdelay takes preference over a direct-mode", np);
+			continue;
+		}
+
 		/*
 		 * Only map one page for direct access. This is enough for the
 		 * simple TX transfer which only writes to the first word.
-- 
2.14.3


--
To unsubscribe from this list: send the line "unsubscribe linux-spi" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html



[Index of Archives]     [Linux Kernel]     [Linux ARM (vger)]     [Linux ARM MSM]     [Linux Omap]     [Linux Arm]     [Linux Tegra]     [Fedora ARM]     [Linux for Samsung SOC]     [eCos]     [Linux Fastboot]     [Gcc Help]     [Git]     [DCCP]     [IETF Announce]     [Security]     [Linux MIPS]     [Yosemite Campsites]

  Powered by Linux