[U-Boot] [PATCH v3 13/29] dm: spi: Add soft_spi implementation

Simon Glass sjg at chromium.org
Mon Sep 29 21:35:10 CEST 2014


Add a new implementation of soft_spi that uses device tree to specify the
GPIOs. This will replace soft_spi_legacy for boards which use driver model.

Signed-off-by: Simon Glass <sjg at chromium.org>
---

Changes in v3:
- Word-wrap the first paragraph of the binding

Changes in v2: None

 doc/device-tree-bindings/spi/soft-spi.txt |  34 ++++
 drivers/spi/Makefile                      |   1 +
 drivers/spi/soft_spi.c                    | 252 ++++++++++++++++++++++++++++++
 3 files changed, 287 insertions(+)
 create mode 100644 doc/device-tree-bindings/spi/soft-spi.txt
 create mode 100644 drivers/spi/soft_spi.c

diff --git a/doc/device-tree-bindings/spi/soft-spi.txt b/doc/device-tree-bindings/spi/soft-spi.txt
new file mode 100644
index 0000000..d09c1a5
--- /dev/null
+++ b/doc/device-tree-bindings/spi/soft-spi.txt
@@ -0,0 +1,34 @@
+Soft SPI
+
+The soft SPI bus implementation allows the use of GPIO pins to simulate a
+SPI bus. No SPI host is required for this to work. The down-side is that the
+performance will typically be much lower than a real SPI bus.
+
+The soft SPI node requires the following properties:
+
+compatible: "u-boot,soft-spi"
+soft_spi_cs: GPIO number to use for SPI chip select (output)
+soft_spi_sclk: GPIO number to use for SPI clock (output)
+soft_spi_mosi: GPIO number to use for SPI MOSI line (output)
+soft_spi_miso GPIO number to use for SPI MISO line (input)
+spi-delay-us: Number of microseconds of delay between each CS transition
+
+The GPIOs should be specified as required by the GPIO controller referenced.
+The first cell holds the phandle of the controller and the second cell
+typically holds the GPIO number.
+
+
+Example:
+
+	soft-spi {
+		compatible = "u-boot,soft-spi";
+		cs-gpio = <&gpio 235 0>;	/* Y43 */
+		sclk-gpio = <&gpio 225 0>;	/* Y31 */
+		mosi-gpio = <&gpio 227 0>;	/* Y33 */
+		miso-gpio = <&gpio 224 0>;	/* Y30 */
+		spi-delay-us = <1>;
+		#address-cells = <1>;
+		#size-cells = <0>;
+		cs at 0 {
+		};
+	};
diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile
index 5cc8655..e718528 100644
--- a/drivers/spi/Makefile
+++ b/drivers/spi/Makefile
@@ -9,6 +9,7 @@
 ifdef CONFIG_DM_SPI
 obj-y += spi-uclass.o
 obj-$(CONFIG_SANDBOX) += spi-emul-uclass.o
+obj-$(CONFIG_SOFT_SPI) += soft_spi.o
 else
 obj-y += spi.o
 obj-$(CONFIG_SOFT_SPI) += soft_spi_legacy.o
diff --git a/drivers/spi/soft_spi.c b/drivers/spi/soft_spi.c
new file mode 100644
index 0000000..5588036
--- /dev/null
+++ b/drivers/spi/soft_spi.c
@@ -0,0 +1,252 @@
+/*
+ * Copyright (c) 2014 Google, Inc
+ *
+ * (C) Copyright 2002
+ * Gerald Van Baren, Custom IDEAS, vanbaren at cideas.com.
+ *
+ * Influenced by code from:
+ * Wolfgang Denk, DENX Software Engineering, wd at denx.de.
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <errno.h>
+#include <fdtdec.h>
+#include <malloc.h>
+#include <spi.h>
+#include <asm/gpio.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+struct soft_spi_platdata {
+	struct fdt_gpio_state cs;
+	struct fdt_gpio_state sclk;
+	struct fdt_gpio_state mosi;
+	struct fdt_gpio_state miso;
+	int spi_delay_us;
+};
+
+struct soft_spi_priv {
+	unsigned int mode;
+};
+
+static int soft_spi_scl(struct udevice *dev, int bit)
+{
+	struct soft_spi_platdata *plat = dev->platdata;
+	struct soft_spi_priv *priv = dev_get_priv(dev);
+
+	gpio_set_value(plat->sclk.gpio, priv->mode & SPI_CPOL ? bit : !bit);
+
+	return 0;
+}
+
+static int soft_spi_sda(struct udevice *dev, int bit)
+{
+	struct soft_spi_platdata *plat = dev->platdata;
+
+	gpio_set_value(plat->mosi.gpio, bit);
+
+	return 0;
+}
+
+static int soft_spi_cs_activate(struct udevice *dev)
+{
+	struct soft_spi_platdata *plat = dev->platdata;
+	struct soft_spi_priv *priv = dev_get_priv(dev);
+
+	gpio_set_value(plat->cs.gpio, !(priv->mode & SPI_CS_HIGH));
+	gpio_set_value(plat->sclk.gpio, priv->mode & SPI_CPOL);
+	gpio_set_value(plat->cs.gpio, priv->mode & SPI_CS_HIGH);
+
+	return 0;
+}
+
+static int soft_spi_cs_deactivate(struct udevice *dev)
+{
+	struct soft_spi_platdata *plat = dev->platdata;
+	struct soft_spi_priv *priv = dev_get_priv(dev);
+
+	gpio_set_value(plat->cs.gpio, !(priv->mode & SPI_CS_HIGH));
+
+	return 0;
+}
+
+static int soft_spi_claim_bus(struct udevice *dev)
+{
+	/*
+	 * Make sure the SPI clock is in idle state as defined for
+	 * this slave.
+	 */
+	return soft_spi_scl(dev, 0);
+}
+
+static int soft_spi_release_bus(struct udevice *dev)
+{
+	/* Nothing to do */
+	return 0;
+}
+
+/*-----------------------------------------------------------------------
+ * SPI transfer
+ *
+ * This writes "bitlen" bits out the SPI MOSI port and simultaneously clocks
+ * "bitlen" bits in the SPI MISO port.  That's just the way SPI works.
+ *
+ * The source of the outgoing bits is the "dout" parameter and the
+ * destination of the input bits is the "din" parameter.  Note that "dout"
+ * and "din" can point to the same memory location, in which case the
+ * input data overwrites the output data (since both are buffered by
+ * temporary variables, this is OK).
+ */
+static int soft_spi_xfer(struct udevice *dev, unsigned int bitlen,
+			 const void *dout, void *din, unsigned long flags)
+{
+	struct soft_spi_priv *priv = dev_get_priv(dev);
+	struct soft_spi_platdata *plat = dev->platdata;
+	uchar		tmpdin  = 0;
+	uchar		tmpdout = 0;
+	const u8	*txd = dout;
+	u8		*rxd = din;
+	int		cpol = priv->mode & SPI_CPOL;
+	int		cpha = priv->mode & SPI_CPHA;
+	unsigned int	j;
+
+	debug("spi_xfer: slave %s:%s dout %08X din %08X bitlen %u\n",
+	      dev->parent->name, dev->name, *(uint *)txd, *(uint *)rxd,
+	      bitlen);
+
+	if (flags & SPI_XFER_BEGIN)
+		soft_spi_cs_activate(dev);
+
+	for (j = 0; j < bitlen; j++) {
+		/*
+		 * Check if it is time to work on a new byte.
+		 */
+		if ((j % 8) == 0) {
+			if (txd)
+				tmpdout = *txd++;
+			else
+				tmpdout = 0;
+			if (j != 0) {
+				if (rxd)
+					*rxd++ = tmpdin;
+			}
+			tmpdin  = 0;
+		}
+
+		if (!cpha)
+			soft_spi_scl(dev, !cpol);
+		soft_spi_sda(dev, tmpdout & 0x80);
+		udelay(plat->spi_delay_us);
+		if (cpha)
+			soft_spi_scl(dev, !cpol);
+		else
+			soft_spi_scl(dev, cpol);
+		tmpdin	<<= 1;
+		tmpdin	|= gpio_get_value(plat->miso.gpio);
+		tmpdout	<<= 1;
+		udelay(plat->spi_delay_us);
+		if (cpha)
+			soft_spi_scl(dev, cpol);
+	}
+	/*
+	 * If the number of bits isn't a multiple of 8, shift the last
+	 * bits over to left-justify them.  Then store the last byte
+	 * read in.
+	 */
+	if (rxd) {
+		if ((bitlen % 8) != 0)
+			tmpdin <<= 8 - (bitlen % 8);
+		*rxd++ = tmpdin;
+	}
+
+	if (flags & SPI_XFER_END)
+		soft_spi_cs_deactivate(dev);
+
+	return 0;
+}
+
+static int soft_spi_set_speed(struct udevice *dev, unsigned int speed)
+{
+	/* Accept any speed */
+	return 0;
+}
+
+static int soft_spi_set_mode(struct udevice *dev, unsigned int mode)
+{
+	struct soft_spi_priv *priv = dev_get_priv(dev);
+
+	priv->mode = mode;
+
+	return 0;
+}
+
+static int soft_spi_child_pre_probe(struct udevice *dev)
+{
+	struct spi_slave *slave = dev_get_parentdata(dev);
+
+	slave->dev = dev;
+	return spi_ofdata_to_platdata(gd->fdt_blob, dev->of_offset, slave);
+}
+
+static const struct dm_spi_ops soft_spi_ops = {
+	.claim_bus	= soft_spi_claim_bus,
+	.release_bus	= soft_spi_release_bus,
+	.xfer		= soft_spi_xfer,
+	.set_speed	= soft_spi_set_speed,
+	.set_mode	= soft_spi_set_mode,
+};
+
+static int soft_spi_ofdata_to_platdata(struct udevice *dev)
+{
+	struct soft_spi_platdata *plat = dev->platdata;
+	const void *blob = gd->fdt_blob;
+	int node = dev->of_offset;
+
+	if (fdtdec_decode_gpio(blob, node, "cs-gpio", &plat->cs) ||
+	    fdtdec_decode_gpio(blob, node, "sclk-gpio", &plat->sclk) ||
+	    fdtdec_decode_gpio(blob, node, "mosi-gpio", &plat->mosi) ||
+	    fdtdec_decode_gpio(blob, node, "miso-gpio", &plat->miso))
+		return -EINVAL;
+	plat->spi_delay_us = fdtdec_get_int(blob, node, "spi-delay-us", 0);
+
+	return 0;
+}
+
+static int soft_spi_probe(struct udevice *dev)
+{
+	struct spi_slave *slave = dev_get_parentdata(dev);
+	struct soft_spi_platdata *plat = dev->platdata;
+
+	gpio_request(plat->cs.gpio, "soft_spi_cs");
+	gpio_request(plat->sclk.gpio, "soft_spi_sclk");
+	gpio_request(plat->mosi.gpio, "soft_spi_mosi");
+	gpio_request(plat->miso.gpio, "soft_spi_miso");
+
+	gpio_direction_output(plat->sclk.gpio, slave->mode & SPI_CPOL);
+	gpio_direction_output(plat->mosi.gpio, 1);
+	gpio_direction_input(plat->miso.gpio);
+	gpio_direction_output(plat->cs.gpio, !(slave->mode & SPI_CS_HIGH));
+
+	return 0;
+}
+
+static const struct udevice_id soft_spi_ids[] = {
+	{ .compatible = "u-boot,soft-spi" },
+	{ }
+};
+
+U_BOOT_DRIVER(soft_spi) = {
+	.name	= "soft_spi",
+	.id	= UCLASS_SPI,
+	.of_match = soft_spi_ids,
+	.ops	= &soft_spi_ops,
+	.ofdata_to_platdata = soft_spi_ofdata_to_platdata,
+	.platdata_auto_alloc_size = sizeof(struct soft_spi_platdata),
+	.priv_auto_alloc_size = sizeof(struct soft_spi_priv),
+	.per_child_auto_alloc_size	= sizeof(struct spi_slave),
+	.probe	= soft_spi_probe,
+	.child_pre_probe	= soft_spi_child_pre_probe,
+};
-- 
2.1.0.rc2.206.gedb03e5



More information about the U-Boot mailing list