linux-spi.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH] spi: Driver for GPIO controlled SPI multiplexer
@ 2013-02-27 13:58 Peter Korsgaard
       [not found] ` <20130302035043.GE6610@opensource.wolfsonmicro.com>
       [not found] ` <1361973519-30633-1-git-send-email-peter.korsgaard-ob4gmnvZ1/cAvxtiuMwx3w@public.gmane.org>
  0 siblings, 2 replies; 9+ messages in thread
From: Peter Korsgaard @ 2013-02-27 13:58 UTC (permalink / raw)
  To: grant.likely-s3s/WqlpOiPyB63q8FvJNQ,
	broonie-yzvPICuk2AATkU/dhu1WVueM+bqZidxxQQ4Iyu8u01E,
	spi-devel-general-5NWGOfrQmneRv+LV9MX5uipxlwaOVQ5f
  Cc: Dries Van Puymbroeck

From: Dries Van Puymbroeck <Dries.VanPuymbroeck-ob4gmnvZ1/cAvxtiuMwx3w@public.gmane.org>

This patch contains a driver for a gpio controlled multiplexer on an
SPI bus. This can be useful if a board requires more SPI devices,
and thus more chip selects, than the SPI controller on the processor
has available.

The mux device is added in the device tree as a child node of the
SPI master. Then, devices can be added as children of the mux node.
The mux will appear as if it was a SPI master device, and child
nodes will appear as chip selects on the mux bus. A bindings file is
provided for the device tree bindings.

Since at least some SPI master drivers queue messages from the
attached devices, the mux can only send 1 message at a time from its
own queue to the master, because otherwise there would not be a
guarantee that the mux settings will be correct when the real master
does the transfer.

Signed-off-by: Dries Van Puymbroeck <Dries.VanPuymbroeck-ob4gmnvZ1/cAvxtiuMwx3w@public.gmane.org>
---
 .../devicetree/bindings/spi/spi-mux-gpio.txt       |  113 +++++++
 drivers/spi/Kconfig                                |    9 +
 drivers/spi/Makefile                               |    1 +
 drivers/spi/spi-mux-gpio.c                         |  319 ++++++++++++++++++++
 4 files changed, 442 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/spi/spi-mux-gpio.txt
 create mode 100644 drivers/spi/spi-mux-gpio.c

diff --git a/Documentation/devicetree/bindings/spi/spi-mux-gpio.txt b/Documentation/devicetree/bindings/spi/spi-mux-gpio.txt
new file mode 100644
index 0000000..534a667
--- /dev/null
+++ b/Documentation/devicetree/bindings/spi/spi-mux-gpio.txt
@@ -0,0 +1,113 @@
+GPIO-based SPI Chip Select Mux
+
+This binding describes a SPI bus multiplexer that uses GPIOs to route
+the SPI chip select signals. This can be used when you need more
+devices than the SPI controller has chip selects available.
+
+      MOSI /---------------------------+--------+--------+--------\
+      MISO |/-------------------------+|-------+|-------+|-------\|
+       SCL ||/-----------------------+||------+||------+||------\||
+           |||                       |||      |||      |||      |||
+    +------------+                   |||      |||      |||      |||
+    | SoC  |||   |                 +-+++-+  +-+++-+  +-+++-+  +-+++-+
+    |      |||   |                 | dev |  | dev |  | dev |  | dev |
+    |   +--+++-+ |  +------+\      +--+--+  +--+--+  +--+--+  +--+--+
+    |   | SPI  +-|--| Mux  |\\   CS-0 |        |        |        |
+    |   +------+ |  +--++--+\\\-------/   CS-1 |        |        |
+    |            |     ||   \\\----------------/   CS-2 |        |
+    |   +------+ |     ||    \\-------------------------/   CS-3 |
+    |   | GPIO |-|-----/|     \----------------------------------/
+    |   |      |-|------/
+    |   +------+ |
+    +------------+
+
+Required properties:
+- compatible: "spi-mux-gpio"
+- #address-cells: <1> (as for any SPI master device)
+- #size-cells: <0> (as for any SPI master device)
+- reg: chip select of the mux on the parent SPI master
+- spi-max-frequency: the maximum frequency allowed for any devices on
+  this mux
+- mux-gpios: list of gpios used to control the muxer
+* SPI child nodes, as if the mux is a real spi master
+
+A new SPI bus will be created. Then for each child node, a SPI device
+is created, with a virtual chip select on this bus according to the
+reg property.
+
+Whenever an access is made to a child device, the value set in the
+revelant node's reg property is interpreted as a bitmask defining the
+state of the mux-gpios gpio pins, with the least significant bit
+defining the state of first gpio, the next bit the state of the second
+gpio and so forth.
+
+The property spi-max-frequency is conceptually not needed, as each
+child node holds the maximum frequency specific to that
+device. However, the SPI core code wants every device in the tree to
+specify a maximum frequency. So because the mux is a device to a
+parent SPI master, you need to set a maximum frequency.  It's best to
+set this high, as the driver will take the minimum of this value and
+the child's maximum frequency value when doing a transfer to that
+child device.
+
+Example:
+	/*
+	 * An SPI mux on chip select 1 of the spi1 peripheral controller of an
+
+	 * am33xx soc. Chip select 0 is taken by another device, and the mux is
+	 * on chip select 1. Behind the mux are 4 devices which are defined as
+	 * if the spi-mux is a master.
+	 */
+
+	spi1 {
+		compatible = "ti,omap4-mcspi";
+		status = "enabled";
+
+		spi-flash@0 {
+			compatible = "m25p40";
+			reg = <0>;
+			spi-max-frequency = <10000000>;
+		};
+
+		spi-mux {
+			compatible = "spi-mux-gpio";
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			reg = <1>;
+			spi-max-frequency = <100000000>;
+
+			mux-gpios = <&gpio2 30 0 &gpio2 31 0>;
+
+			spi-flash@0 {
+				#address-cells = <1>;
+				#size-cells = <1>;
+				compatible = "sst,sst25vf016b";
+				spi-max-frequency = <40000000>;
+				reg = <0>;
+			};
+
+			spi-device@1 {
+				compatible = "spidev";
+				reg = <1>;
+				spi-max-frequency = <10000000>;
+			};
+
+			spi-flash@2 {
+				compatible = "winbond,w25q32";
+				reg = <2>;
+				spi-max-frequency = <20000000>;
+			};
+
+			mc13892@3 {
+				#address-cells = <1>;
+				#size-cells = <0>;
+				compatible = "fsl,mc13892";
+				spi-max-frequency = <6000000>;
+				reg = <3>;
+
+				/* more settings... */
+			}
+
+		};
+	};
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
index f80eee7..df0390d 100644
--- a/drivers/spi/Kconfig
+++ b/drivers/spi/Kconfig
@@ -481,6 +481,15 @@ config SPI_DW_MMIO
 	tristate "Memory-mapped io interface driver for DW SPI core"
 	depends on SPI_DESIGNWARE && HAVE_CLK
 
+config SPI_MUX_GPIO
+	tristate "GPIO-based SPI multiplexer support"
+	depends on GENERIC_GPIO
+	help
+	  This adds support for a GPIO based multiplexer on one or more of the
+	  SPI busses. The mux will be accessible as an extra master bus, the
+	  devices behind the mux will appear to be chip selects on this master
+	  bus.
+
 #
 # There are lots of SPI device types, with sensors and memory
 # being probably the most widely used ones.
diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile
index e53c309..732670a 100644
--- a/drivers/spi/Makefile
+++ b/drivers/spi/Makefile
@@ -37,6 +37,7 @@ obj-$(CONFIG_SPI_LM70_LLP)		+= spi-lm70llp.o
 obj-$(CONFIG_SPI_MPC512x_PSC)		+= spi-mpc512x-psc.o
 obj-$(CONFIG_SPI_MPC52xx_PSC)		+= spi-mpc52xx-psc.o
 obj-$(CONFIG_SPI_MPC52xx)		+= spi-mpc52xx.o
+obj-$(CONFIG_SPI_MUX_GPIO)		+= spi-mux-gpio.o
 obj-$(CONFIG_SPI_MXS)			+= spi-mxs.o
 obj-$(CONFIG_SPI_NUC900)		+= spi-nuc900.o
 obj-$(CONFIG_SPI_OC_TINY)		+= spi-oc-tiny.o
diff --git a/drivers/spi/spi-mux-gpio.c b/drivers/spi/spi-mux-gpio.c
new file mode 100644
index 0000000..983565b
--- /dev/null
+++ b/drivers/spi/spi-mux-gpio.c
@@ -0,0 +1,319 @@
+/*
+ * SPI multiplexer using GPIO API
+ *
+ * Dries Van Puymbroeck <Dries.VanPuymbroeck-ob4gmnvZ1/cAvxtiuMwx3w@public.gmane.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License (not later!)
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not,  see <http://www.gnu.org/licenses>
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/gpio.h>
+#include <linux/of_gpio.h>
+#include <linux/wait.h>
+#include <linux/spi/spi.h>
+
+/*
+ * This driver supports a MUX on an SPI bus. This can be useful when you need
+ * more chip selects than the hardware peripherals support, or than are
+ * available in a particular board setup.
+ *
+ * This particular implementation also assumes the MUX can set up 2^n channels,
+ * where n is the number of GPIO's connected to set the MUX.
+ *
+ * The driver will create an additional master bus. Devices added under the mux
+ * will be handled as 'chip selects' on the mux bus.
+ */
+
+/**
+ * struct spi_mux_gpio - the basic spi_mux_gpio structure
+ * @spi_device:		pointer to the device struct attached to the parent
+ *			spi master
+ * @gpios:		Array of GPIO numbers used to control MUX
+ * @n_gpios:		Number of GPIOs used to control MUX
+ * @values:		Array of bitmasks of GPIO settings (low/high) for each
+ *			position
+ * @current_cs:		The current chip select set in the mux
+ * @xfer_complete_wq:	A wait queue to wait for the parent spi master to
+ *			finish one message so this driver knows when it is safe
+ *			to switch the mux
+ * @xfer_complete:	The wait condition for the wait queue
+ */
+struct spi_mux_gpio {
+	struct spi_device	*spi;
+	unsigned int		*gpios;
+	int			n_gpios;
+	unsigned int		*values;
+	int			current_cs;
+	wait_queue_head_t	xfer_complete_wq;
+	bool			xfer_complete;
+};
+
+static int spi_mux_gpio_select(struct spi_device *spi)
+{
+	struct spi_mux_gpio *mux = spi_master_get_devdata(spi->master);
+	int i;
+
+	for (i = 0; i < mux->n_gpios; i++) {
+		gpio_set_value(mux->gpios[i],
+			       mux->values[spi->chip_select] & (1 << i));
+	}
+
+	return 0;
+}
+
+/* should not get called when our master is doing a transfer */
+static int spi_mux_gpio_setup_mux(struct spi_device *spi)
+{
+	struct spi_mux_gpio *mux = spi_master_get_devdata(spi->master);
+	int ret = 0;
+
+	if (mux->current_cs != spi->chip_select) {
+		dev_dbg(&mux->spi->dev,
+			"setting up the mux for cs %d\n",
+			spi->chip_select);
+
+		/* copy the child device's settings except for the cs */
+		if (spi->max_speed_hz < mux->spi->max_speed_hz)
+			mux->spi->max_speed_hz = spi->max_speed_hz;
+		mux->spi->mode = spi->mode;
+		mux->spi->bits_per_word = spi->bits_per_word;
+
+		ret = spi_mux_gpio_select(spi);
+		if (ret)
+			return ret;
+
+		mux->current_cs = spi->chip_select;
+	}
+
+	return ret;
+}
+
+static int spi_mux_gpio_setup(struct spi_device *spi)
+{
+	struct spi_mux_gpio *mux = spi_master_get_devdata(spi->master);
+
+	/*
+	 * can be called multiple times, won't do a valid setup now but we will
+	 * change the settings when we do a transfer (necessary because we
+	 * can't predict from which device it will be anyway)
+	 */
+	return spi_setup(mux->spi);
+}
+
+static void spi_mux_gpio_complete_cb(void *context)
+{
+	struct spi_mux_gpio *mux = (struct spi_mux_gpio *)context;
+
+	/* allow transfer function to continue */
+	mux->xfer_complete = true;
+	wake_up_interruptible(&mux->xfer_complete_wq);
+}
+
+static int spi_mux_gpio_transfer_one_message(struct spi_master *master,
+						struct spi_message *m)
+{
+	struct spi_mux_gpio *mux = spi_master_get_devdata(master);
+	struct spi_device *spi = m->spi;
+
+	void (*child_mesg_complete)(void *context);
+	void *child_mesg_context;
+	struct spi_device *child_mesg_dev;
+
+	int ret = 0;
+
+	ret = spi_mux_gpio_setup_mux(spi);
+	if (ret)
+		return ret;
+
+	/*
+	 * Replace the complete callback, context and spi_device with our own
+	 * pointers. Save originals
+	 */
+	child_mesg_complete = m->complete;
+	child_mesg_context = m->context;
+	child_mesg_dev = m->spi;
+
+	m->complete = spi_mux_gpio_complete_cb;
+	m->context = mux;
+	m->spi = mux->spi;
+
+	/* do the transfer + wait until it is done */
+	mux->xfer_complete = false;
+	spi_async(mux->spi, m);
+
+	ret = wait_event_interruptible(mux->xfer_complete_wq,
+				       mux->xfer_complete);
+
+	/*
+	 * restore callback, context, spi_device and do finalize, even if
+	 * ret != 0. In that case, m->actual_length will hold the bytes
+	 * actually transferred.
+	 */
+	m->complete = child_mesg_complete;
+	m->context = child_mesg_context;
+	m->spi = child_mesg_dev;
+	spi_finalize_current_message(master);
+
+	return ret;
+}
+
+static int spi_mux_gpio_probe_dt(struct spi_mux_gpio *mux)
+{
+	struct device_node *np = mux->spi->dev.of_node;
+	struct device_node *child;
+
+	int n_values, i;
+
+	if (!np)
+		return -ENODEV;
+
+	n_values = of_get_child_count(np);
+
+	mux->values = devm_kzalloc(&mux->spi->dev,
+			      sizeof(*mux->values) * n_values,
+			      GFP_KERNEL);
+	if (!mux->values) {
+		dev_err(&mux->spi->dev, "Cannot allocate values array");
+		return -ENOMEM;
+	}
+
+	i = 0;
+	for_each_child_of_node(np, child) {
+		of_property_read_u32(child, "reg", mux->values + i);
+		i++;
+	}
+
+	mux->n_gpios = of_gpio_named_count(np, "mux-gpios");
+	if (mux->n_gpios < 0) {
+		dev_err(&mux->spi->dev,
+			"Missing mux-gpios property in the DT.\n");
+		return -EINVAL;
+	}
+
+	mux->gpios = devm_kzalloc(&mux->spi->dev,
+			     sizeof(*mux->gpios) * mux->n_gpios,
+			     GFP_KERNEL);
+	if (!mux->gpios) {
+		dev_err(&mux->spi->dev, "Cannot allocate gpios array");
+		return -ENOMEM;
+	}
+
+	for (i = 0; i < mux->n_gpios; i++)
+		mux->gpios[i] = of_get_named_gpio(np, "mux-gpios", i);
+
+	/*
+	 * when we register our mux as an spi master, it will parse the
+	 * the children of this node and add them as devices.
+	 * So we don't need to parse the child nodes here.
+	 */
+
+	return 0;
+}
+
+static int spi_mux_gpio_probe(struct spi_device *spi)
+{
+	struct spi_master *master;
+	struct spi_mux_gpio *mux;
+	int ret = 0, i;
+	unsigned int initial_state;
+
+	master = spi_alloc_master(&spi->dev, sizeof(*mux));
+	if (master == NULL) {
+		dev_dbg(&spi->dev, "master allocation failed\n");
+		return -ENOMEM;
+	}
+
+	dev_set_drvdata(&spi->dev, master);
+	mux = spi_master_get_devdata(master);
+	mux->spi = spi;
+
+	ret = spi_mux_gpio_probe_dt(mux);
+	if (ret < 0)
+		goto err_probe_dt;
+
+	initial_state = mux->values[0];
+	mux->current_cs = 0;
+
+	for (i = 0; i < mux->n_gpios; i++) {
+		devm_gpio_request(&spi->dev, mux->gpios[i], "spi-mux-gpio");
+		gpio_direction_output(mux->gpios[i], initial_state & (1 << i));
+	}
+
+	mux->xfer_complete = true;
+	init_waitqueue_head(&mux->xfer_complete_wq);
+
+	/* supported modes are the same as our parent's */
+	master->mode_bits = mux->spi->master->mode_bits;
+
+	master->setup = spi_mux_gpio_setup;
+	master->transfer_one_message = spi_mux_gpio_transfer_one_message;
+
+	/* the mux can have 2 ^ <nr_gpio_used_for_muxing> chip selects */
+	master->num_chipselect = 1 << mux->n_gpios;
+	master->dev.of_node = spi->dev.of_node;
+
+	/* register master -> this also adds the devices behind the mux */
+	ret = spi_register_master(master);
+	if (ret < 0)
+		goto err_register_master;
+
+	return ret;
+
+err_register_master:
+err_probe_dt:
+	spi_master_put(master);
+
+	return ret;
+}
+
+static int spi_mux_gpio_remove(struct spi_device *spi)
+{
+	struct spi_master *master = spi_get_drvdata(spi);
+
+	spi_unregister_master(master);
+	spi_master_put(master);
+
+	return 0;
+}
+
+static const struct of_device_id spi_mux_gpio_of_match[] = {
+	{ .compatible = "spi-mux-gpio", },
+	{},
+};
+MODULE_DEVICE_TABLE(of, spi_mux_gpio_of_match);
+
+static struct spi_driver spi_mux_gpio_driver = {
+	.probe	= spi_mux_gpio_probe,
+	.remove	= spi_mux_gpio_remove,
+	.driver	= {
+		.owner	= THIS_MODULE,
+		.name	= "spi-mux-gpio",
+		.of_match_table = of_match_ptr(spi_mux_gpio_of_match),
+	},
+};
+
+module_spi_driver(spi_mux_gpio_driver);
+
+MODULE_DESCRIPTION("GPIO-based SPI multiplexer driver");
+MODULE_AUTHOR("Dries Van Puymbroeck <Dries.VanPuymbroeck-ob4gmnvZ1/cAvxtiuMwx3w@public.gmane.org>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("spi:spi-mux-gpio");
-- 
1.7.10.4


------------------------------------------------------------------------------
Everyone hates slow websites. So do we.
Make your web apps faster with AppDynamics
Download AppDynamics Lite for free today:
http://p.sf.net/sfu/appdyn_d2d_feb

^ permalink raw reply related	[flat|nested] 9+ messages in thread

* Re: [PATCH] spi: Driver for GPIO controlled SPI multiplexer
       [not found]   ` <20130302035043.GE6610-yzvPICuk2AATkU/dhu1WVueM+bqZidxxQQ4Iyu8u01E@public.gmane.org>
@ 2013-03-02 10:43     ` Peter Korsgaard
       [not found]       ` <20130302104853.GA31872@opensource.wolfsonmicro.com>
  0 siblings, 1 reply; 9+ messages in thread
From: Peter Korsgaard @ 2013-03-02 10:43 UTC (permalink / raw)
  To: Mark Brown
  Cc: spi-devel-general-5NWGOfrQmneRv+LV9MX5uipxlwaOVQ5f,
	Van Puymbroeck, Dries

>>>>> "MB" == Mark Brown <broonie-yzvPICuk2AATkU/dhu1WVueM+bqZidxxQQ4Iyu8u01E@public.gmane.org> writes:

Hi,

>> +The property spi-max-frequency is conceptually not needed, as each
>> +child node holds the maximum frequency specific to that
>> +device. However, the SPI core code wants every device in the tree to
>> +specify a maximum frequency. So because the mux is a device to a
>> +parent SPI master, you need to set a maximum frequency.  It's best to
>> +set this high, as the driver will take the minimum of this value and
>> +the child's maximum frequency value when doing a transfer to that
>> +child device.

MB> Can't the device just enumerate its children and DTRT?

Agreed.


>> +static const struct of_device_id spi_mux_gpio_of_match[] = {
>> +	{ .compatible = "spi-mux-gpio", },
>> +	{},
>> +};
>> +MODULE_DEVICE_TABLE(of, spi_mux_gpio_of_match);

MB> This looks wrong without a vendor name in it but I'm not sure what we're
MB> supposed to do for generic stuff like this.

It's atleast in line with what we do elsewhere (spi-gpio, i2c-gpio,
i2c-mux-gpio, ..)

-- 
Bye, Peter Korsgaard


DISCLAIMER:
Unless indicated otherwise, the information contained in this message is privileged and confidential, and is intended only for the use of the addressee(s) named above and others who have been specifically authorized to receive it. If you are not the intended recipient, you are hereby notified that any dissemination, distribution or copying of this message and/or attachments is strictly prohibited. The company accepts no liability for any damage caused by any virus transmitted by this email. Furthermore, the company does not warrant a proper and complete transmission of this information, nor does it accept liability for any delays. If you have received this message in error, please contact the sender and delete the message. Thank you.

------------------------------------------------------------------------------
Everyone hates slow websites. So do we.
Make your web apps faster with AppDynamics
Download AppDynamics Lite for free today:
http://p.sf.net/sfu/appdyn_d2d_feb

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH] spi: Driver for GPIO controlled SPI multiplexer
       [not found]         ` <20130302104853.GA31872-yzvPICuk2AATkU/dhu1WVueM+bqZidxxQQ4Iyu8u01E@public.gmane.org>
@ 2013-03-02 11:15           ` Peter Korsgaard
  2013-03-02 14:31           ` Grant Likely
  1 sibling, 0 replies; 9+ messages in thread
From: Peter Korsgaard @ 2013-03-02 11:15 UTC (permalink / raw)
  To: Mark Brown
  Cc: spi-devel-general-5NWGOfrQmneRv+LV9MX5uipxlwaOVQ5f,
	Van Puymbroeck, Dries

>>>>> "MB" == Mark Brown <broonie-yzvPICuk2AATkU/dhu1WVueM+bqZidxxQQ4Iyu8u01E@public.gmane.org> writes:

MB> On Sat, Mar 02, 2013 at 11:43:58AM +0100, Peter Korsgaard wrote:

MB> This looks wrong without a vendor name in it but I'm not sure what we're
MB> supposed to do for generic stuff like this.

>> It's atleast in line with what we do elsewhere (spi-gpio, i2c-gpio,
>> i2c-mux-gpio, ..)

MB> Yup, it just feels wrong.  Not a problem though.  Will you respin for
MB> the max speed thing?

Yes, I'll ask Dries to do so on Monday. I also noticed he added a LGPL
file header by accident.

-- 
Sorry about disclaimer - It's out of my control.
Bye, Peter Korsgaard


DISCLAIMER:
Unless indicated otherwise, the information contained in this message is privileged and confidential, and is intended only for the use of the addressee(s) named above and others who have been specifically authorized to receive it. If you are not the intended recipient, you are hereby notified that any dissemination, distribution or copying of this message and/or attachments is strictly prohibited. The company accepts no liability for any damage caused by any virus transmitted by this email. Furthermore, the company does not warrant a proper and complete transmission of this information, nor does it accept liability for any delays. If you have received this message in error, please contact the sender and delete the message. Thank you.

------------------------------------------------------------------------------
Everyone hates slow websites. So do we.
Make your web apps faster with AppDynamics
Download AppDynamics Lite for free today:
http://p.sf.net/sfu/appdyn_d2d_feb

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH] spi: Driver for GPIO controlled SPI multiplexer
       [not found]         ` <20130302104853.GA31872-yzvPICuk2AATkU/dhu1WVueM+bqZidxxQQ4Iyu8u01E@public.gmane.org>
  2013-03-02 11:15           ` Peter Korsgaard
@ 2013-03-02 14:31           ` Grant Likely
  1 sibling, 0 replies; 9+ messages in thread
From: Grant Likely @ 2013-03-02 14:31 UTC (permalink / raw)
  To: Mark Brown
  Cc: spi-devel-general-5NWGOfrQmneRv+LV9MX5uipxlwaOVQ5f,
	Van Puymbroeck, Dries, Peter Korsgaard

On Sat, Mar 2, 2013 at 10:48 AM, Mark Brown
<broonie-yzvPICuk2AATkU/dhu1WVueM+bqZidxxQQ4Iyu8u01E@public.gmane.org> wrote:
> On Sat, Mar 02, 2013 at 11:43:58AM +0100, Peter Korsgaard wrote:
>
>> MB> This looks wrong without a vendor name in it but I'm not sure what we're
>> MB> supposed to do for generic stuff like this.
>
>> It's atleast in line with what we do elsewhere (spi-gpio, i2c-gpio,
>> i2c-mux-gpio, ..)
>
> Yup, it just feels wrong.  Not a problem though.  Will you respin for
> the max speed thing?

It's not quite the same though. spi-gpio and i2c-gpio are very well
defined things. An SPI multiplexer on the other hand can be
implemented in several different ways. I would still be more specific
here. You can be specific to your specific hardware now, and a more
generic value can be added later.

g.



-- 
Grant Likely, B.Sc., P.Eng.
Secret Lab Technologies Ltd.

------------------------------------------------------------------------------
Everyone hates slow websites. So do we.
Make your web apps faster with AppDynamics
Download AppDynamics Lite for free today:
http://p.sf.net/sfu/appdyn_d2d_feb

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH] spi: Driver for GPIO controlled SPI multiplexer
       [not found] ` <1361973519-30633-1-git-send-email-peter.korsgaard-ob4gmnvZ1/cAvxtiuMwx3w@public.gmane.org>
@ 2013-03-02 17:32   ` Grant Likely
  2013-03-03  8:08     ` Peter Korsgaard
       [not found]     ` <20130303074209.GA19020@opensource.wolfsonmicro.com>
  2013-03-26 21:14   ` [PATCH] spi: Add SPI mux core and GPIO-based mux driver Dries Van Puymbroeck
  1 sibling, 2 replies; 9+ messages in thread
From: Grant Likely @ 2013-03-02 17:32 UTC (permalink / raw)
  To: Peter Korsgaard,
	broonie-yzvPICuk2AATkU/dhu1WVueM+bqZidxxQQ4Iyu8u01E,
	spi-devel-general-5NWGOfrQmneRv+LV9MX5uipxlwaOVQ5f
  Cc: Dries Van Puymbroeck

On Wed, 27 Feb 2013 14:58:39 +0100, Peter Korsgaard <peter.korsgaard-ob4gmnvZ1/cAvxtiuMwx3w@public.gmane.org> wrote:
> From: Dries Van Puymbroeck <Dries.VanPuymbroeck-ob4gmnvZ1/cAvxtiuMwx3w@public.gmane.org>
> 
> This patch contains a driver for a gpio controlled multiplexer on an
> SPI bus. This can be useful if a board requires more SPI devices,
> and thus more chip selects, than the SPI controller on the processor
> has available.
> 
> The mux device is added in the device tree as a child node of the
> SPI master. Then, devices can be added as children of the mux node.
> The mux will appear as if it was a SPI master device, and child
> nodes will appear as chip selects on the mux bus. A bindings file is
> provided for the device tree bindings.
> 
> Since at least some SPI master drivers queue messages from the
> attached devices, the mux can only send 1 message at a time from its
> own queue to the master, because otherwise there would not be a
> guarantee that the mux settings will be correct when the real master
> does the transfer.

Hi Peter,

Nice, pass on thanks to Dries for looking at this please. I've got other
hardware that wants to do what you've got here. It's a nice tightly
written driver too. Well done! Comments below...

> Signed-off-by: Dries Van Puymbroeck <Dries.VanPuymbroeck-ob4gmnvZ1/cAvxtiuMwx3w@public.gmane.org>

Peter, you forgot to add your s-o-b line here. If you're passing on a
patch, you are responsible to it meets the sign-off critiria in
Documentation/SubmittingPatches which is what your s-o-b line is meant
to assert.

> ---
>  .../devicetree/bindings/spi/spi-mux-gpio.txt       |  113 +++++++
>  drivers/spi/Kconfig                                |    9 +
>  drivers/spi/Makefile                               |    1 +
>  drivers/spi/spi-mux-gpio.c                         |  319 ++++++++++++++++++++
>  4 files changed, 442 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/spi/spi-mux-gpio.txt
>  create mode 100644 drivers/spi/spi-mux-gpio.c
> 
> diff --git a/Documentation/devicetree/bindings/spi/spi-mux-gpio.txt b/Documentation/devicetree/bindings/spi/spi-mux-gpio.txt
> new file mode 100644
> index 0000000..534a667
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/spi/spi-mux-gpio.txt
> @@ -0,0 +1,113 @@
> +GPIO-based SPI Chip Select Mux
> +
> +This binding describes a SPI bus multiplexer that uses GPIOs to route
> +the SPI chip select signals. This can be used when you need more
> +devices than the SPI controller has chip selects available.
> +
> +      MOSI /---------------------------+--------+--------+--------\
> +      MISO |/-------------------------+|-------+|-------+|-------\|
> +       SCL ||/-----------------------+||------+||------+||------\||
> +           |||                       |||      |||      |||      |||
> +    +------------+                   |||      |||      |||      |||
> +    | SoC  |||   |                 +-+++-+  +-+++-+  +-+++-+  +-+++-+
> +    |      |||   |                 | dev |  | dev |  | dev |  | dev |
> +    |   +--+++-+ |  +------+\      +--+--+  +--+--+  +--+--+  +--+--+
> +    |   | SPI  +-|--| Mux  |\\   CS-0 |        |        |        |
> +    |   +------+ |  +--++--+\\\-------/   CS-1 |        |        |
> +    |            |     ||   \\\----------------/   CS-2 |        |
> +    |   +------+ |     ||    \\-------------------------/   CS-3 |
> +    |   | GPIO |-|-----/|     \----------------------------------/
> +    |   |      |-|------/
> +    |   +------+ |
> +    +------------+

Other variants would be an second spi device with a register for the
active CS to use instead of GPIOs. Something to keep in mind as to how
it could be extended for other methods of setting the CS. I've got
hardware exactly like that.

> +
> +Required properties:
> +- compatible: "spi-mux-gpio"

As already commented on, please use a more specific compatible string
here.

> +- #address-cells: <1> (as for any SPI master device)
> +- #size-cells: <0> (as for any SPI master device)
> +- reg: chip select of the mux on the parent SPI master
> +- spi-max-frequency: the maximum frequency allowed for any devices on
> +  this mux
> +- mux-gpios: list of gpios used to control the muxer

Simply 'gpios' is sufficient here I think

> +* SPI child nodes, as if the mux is a real spi master
> +
> +A new SPI bus will be created. Then for each child node, a SPI device
> +is created, with a virtual chip select on this bus according to the
> +reg property.
> +
> +Whenever an access is made to a child device, the value set in the
> +revelant node's reg property is interpreted as a bitmask defining the
> +state of the mux-gpios gpio pins, with the least significant bit
> +defining the state of first gpio, the next bit the state of the second
> +gpio and so forth.
> +
> +The property spi-max-frequency is conceptually not needed, as each
> +child node holds the maximum frequency specific to that
> +device. However, the SPI core code wants every device in the tree to
> +specify a maximum frequency. So because the mux is a device to a
> +parent SPI master, you need to set a maximum frequency.  It's best to
> +set this high, as the driver will take the minimum of this value and
> +the child's maximum frequency value when doing a transfer to that
> +child device.

It would be worth having an exception that takes the speed from the spi
child device. I'm not requiring you to do this, but it would be helpful
if you could check if it can be done.

> diff --git a/drivers/spi/spi-mux-gpio.c b/drivers/spi/spi-mux-gpio.c
> new file mode 100644
> index 0000000..983565b
> --- /dev/null
> +++ b/drivers/spi/spi-mux-gpio.c
> @@ -0,0 +1,319 @@

> +static int spi_mux_gpio_select(struct spi_device *spi)
> +{
> +	struct spi_mux_gpio *mux = spi_master_get_devdata(spi->master);
> +	int i;
> +
> +	for (i = 0; i < mux->n_gpios; i++) {
> +		gpio_set_value(mux->gpios[i],
> +			       mux->values[spi->chip_select] & (1 << i));
> +	}
> +
> +	return 0;
> +}

The spi_mux_gpio_select() function could be a callback allowing other
mechanisms for setting the muxed CS#. In doing so, you can also rename
the driver simply "spi_mux". I think that makes more sense for what you're
doing.

> +static void spi_mux_gpio_complete_cb(void *context)
> +{
> +	struct spi_mux_gpio *mux = (struct spi_mux_gpio *)context;
> +
> +	/* allow transfer function to continue */
> +	mux->xfer_complete = true;
> +	wake_up_interruptible(&mux->xfer_complete_wq);
> +}
> +
> +static int spi_mux_gpio_transfer_one_message(struct spi_master *master,
> +						struct spi_message *m)
> +{
> +	struct spi_mux_gpio *mux = spi_master_get_devdata(master);
> +	struct spi_device *spi = m->spi;
> +
> +	void (*child_mesg_complete)(void *context);
> +	void *child_mesg_context;
> +	struct spi_device *child_mesg_dev;
> +
> +	int ret = 0;
> +
> +	ret = spi_mux_gpio_setup_mux(spi);
> +	if (ret)
> +		return ret;
> +
> +	/*
> +	 * Replace the complete callback, context and spi_device with our own
> +	 * pointers. Save originals
> +	 */
> +	child_mesg_complete = m->complete;
> +	child_mesg_context = m->context;
> +	child_mesg_dev = m->spi;
> +
> +	m->complete = spi_mux_gpio_complete_cb;
> +	m->context = mux;
> +	m->spi = mux->spi;
> +
> +	/* do the transfer + wait until it is done */
> +	mux->xfer_complete = false;
> +	spi_async(mux->spi, m);
> +
> +	ret = wait_event_interruptible(mux->xfer_complete_wq,
> +				       mux->xfer_complete);
> +
> +	/*
> +	 * restore callback, context, spi_device and do finalize, even if
> +	 * ret != 0. In that case, m->actual_length will hold the bytes
> +	 * actually transferred.
> +	 */
> +	m->complete = child_mesg_complete;
> +	m->context = child_mesg_context;
> +	m->spi = child_mesg_dev;
> +	spi_finalize_current_message(master);
> +
> +	return ret;
> +}

I think it has gotten two complex here.  spi_mux_gpio_transfer_one_message()
is doing a bunch of work to set up a wait event to finish up with the
transfer. However, it really doesn't need to do this. It can return
immediately after accepting and passing on the transfer.
spi_finalize_current_message() can be called directly in
spi_mux_gpio_complete_cb(). The spi core will make sure transfer doesn't
get called again if there is already a transfer in-flight.

Mark commented on this too, but I disagree with him on one point; it
will actually be simpler if you finish up the transfer in the callback,
and that it really should be implemented that way from day one.

xfer_complete and xfer_complete_wq can be dropped, and
child_mesg_complete, child_mesg_context and child_mesg_dev will move
into struct spi_mux_gpio.

> +
> +static int spi_mux_gpio_probe_dt(struct spi_mux_gpio *mux)

There really isn't any good reason other than function length to have
this separate from the spi_mux_gpio_probe(). Probe functions are a bit
linear sequence of setups anyway. Just roll it into the main function.

> +{
> +	struct device_node *np = mux->spi->dev.of_node;
> +	struct device_node *child;
> +
> +	int n_values, i;
> +
> +	if (!np)
> +		return -ENODEV;
> +
> +	n_values = of_get_child_count(np);
> +
> +	mux->values = devm_kzalloc(&mux->spi->dev,
> +			      sizeof(*mux->values) * n_values,
> +			      GFP_KERNEL);
> +	if (!mux->values) {
> +		dev_err(&mux->spi->dev, "Cannot allocate values array");
> +		return -ENOMEM;
> +	}

Whoops! this is a bug. The CS#s may be sparse, ie 1, 2, 5, and 7 may be
valid CS# numbers, but that would be only a count of 4.

Instead of a values array, can the CS# itself in the child spi_client be
used as the bitmapped GPIO value here? You're already storing that value
directly into the values array anyway.  :-)

> +
> +	i = 0;
> +	for_each_child_of_node(np, child) {
> +		of_property_read_u32(child, "reg", mux->values + i);
> +		i++;
> +	}
> +
> +	mux->n_gpios = of_gpio_named_count(np, "mux-gpios");
> +	if (mux->n_gpios < 0) {
> +		dev_err(&mux->spi->dev,
> +			"Missing mux-gpios property in the DT.\n");
> +		return -EINVAL;
> +	}
> +
> +	mux->gpios = devm_kzalloc(&mux->spi->dev,
> +			     sizeof(*mux->gpios) * mux->n_gpios,
> +			     GFP_KERNEL);
> +	if (!mux->gpios) {
> +		dev_err(&mux->spi->dev, "Cannot allocate gpios array");
> +		return -ENOMEM;
> +	}
> +
> +	for (i = 0; i < mux->n_gpios; i++)
> +		mux->gpios[i] = of_get_named_gpio(np, "mux-gpios", i);
> +
> +	/*
> +	 * when we register our mux as an spi master, it will parse the
> +	 * the children of this node and add them as devices.
> +	 * So we don't need to parse the child nodes here.
> +	 */
> +
> +	return 0;
> +}
> +
> +static int spi_mux_gpio_probe(struct spi_device *spi)
> +{
> +	struct spi_master *master;
> +	struct spi_mux_gpio *mux;
> +	int ret = 0, i;
> +	unsigned int initial_state;
> +
> +	master = spi_alloc_master(&spi->dev, sizeof(*mux));
> +	if (master == NULL) {
> +		dev_dbg(&spi->dev, "master allocation failed\n");
> +		return -ENOMEM;
> +	}
> +
> +	dev_set_drvdata(&spi->dev, master);
> +	mux = spi_master_get_devdata(master);
> +	mux->spi = spi;
> +
> +	ret = spi_mux_gpio_probe_dt(mux);
> +	if (ret < 0)
> +		goto err_probe_dt;
> +
> +	initial_state = mux->values[0];
> +	mux->current_cs = 0;
> +
> +	for (i = 0; i < mux->n_gpios; i++) {
> +		devm_gpio_request(&spi->dev, mux->gpios[i], "spi-mux-gpio");
> +		gpio_direction_output(mux->gpios[i], initial_state & (1 << i));
> +	}
> +
> +	mux->xfer_complete = true;
> +	init_waitqueue_head(&mux->xfer_complete_wq);
> +
> +	/* supported modes are the same as our parent's */
> +	master->mode_bits = mux->spi->master->mode_bits;
> +
> +	master->setup = spi_mux_gpio_setup;
> +	master->transfer_one_message = spi_mux_gpio_transfer_one_message;
> +
> +	/* the mux can have 2 ^ <nr_gpio_used_for_muxing> chip selects */
> +	master->num_chipselect = 1 << mux->n_gpios;
> +	master->dev.of_node = spi->dev.of_node;
> +
> +	/* register master -> this also adds the devices behind the mux */
> +	ret = spi_register_master(master);
> +	if (ret < 0)
> +		goto err_register_master;
> +
> +	return ret;
> +
> +err_register_master:
> +err_probe_dt:
> +	spi_master_put(master);
> +
> +	return ret;
> +}
> +
> +static int spi_mux_gpio_remove(struct spi_device *spi)
> +{
> +	struct spi_master *master = spi_get_drvdata(spi);
> +
> +	spi_unregister_master(master);
> +	spi_master_put(master);
> +
> +	return 0;
> +}
> +
> +static const struct of_device_id spi_mux_gpio_of_match[] = {
> +	{ .compatible = "spi-mux-gpio", },
> +	{},
> +};
> +MODULE_DEVICE_TABLE(of, spi_mux_gpio_of_match);
> +
> +static struct spi_driver spi_mux_gpio_driver = {
> +	.probe	= spi_mux_gpio_probe,
> +	.remove	= spi_mux_gpio_remove,
> +	.driver	= {
> +		.owner	= THIS_MODULE,
> +		.name	= "spi-mux-gpio",
> +		.of_match_table = of_match_ptr(spi_mux_gpio_of_match),

This is a DT-only driver. The of_match_ptr() bit isn't needed.

g.

------------------------------------------------------------------------------
Everyone hates slow websites. So do we.
Make your web apps faster with AppDynamics
Download AppDynamics Lite for free today:
http://p.sf.net/sfu/appdyn_d2d_feb

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH] spi: Driver for GPIO controlled SPI multiplexer
  2013-03-02 17:32   ` Grant Likely
@ 2013-03-03  8:08     ` Peter Korsgaard
       [not found]     ` <20130303074209.GA19020@opensource.wolfsonmicro.com>
  1 sibling, 0 replies; 9+ messages in thread
From: Peter Korsgaard @ 2013-03-03  8:08 UTC (permalink / raw)
  To: Grant Likely
  Cc: spi-devel-general-5NWGOfrQmneRv+LV9MX5uipxlwaOVQ5f,
	Dries Van Puymbroeck, Peter Korsgaard,
	broonie-yzvPICuk2AATkU/dhu1WVueM+bqZidxxQQ4Iyu8u01E

>>>>> "Grant" == Grant Likely <grant.likely-s3s/WqlpOiPyB63q8FvJNQ@public.gmane.org> writes:

Hi,

 >> Since at least some SPI master drivers queue messages from the
 >> attached devices, the mux can only send 1 message at a time from its
 >> own queue to the master, because otherwise there would not be a
 >> guarantee that the mux settings will be correct when the real master
 >> does the transfer.

 Grant> Hi Peter,

 Grant> Nice, pass on thanks to Dries for looking at this please. I've
 Grant> got other hardware that wants to do what you've got here. It's a
 Grant> nice tightly written driver too. Well done! Comments below...

Thanks for the kind words and review!


 >> Signed-off-by: Dries Van Puymbroeck <Dries.VanPuymbroeck-ob4gmnvZ1/cAvxtiuMwx3w@public.gmane.org>

 Grant> Peter, you forgot to add your s-o-b line here. If you're passing
 Grant> on a patch, you are responsible to it meets the sign-off
 Grant> critiria in Documentation/SubmittingPatches which is what your
 Grant> s-o-b line is meant to assert.

Ok, will add it on v2.


 >> +The property spi-max-frequency is conceptually not needed, as each
 >> +child node holds the maximum frequency specific to that
 >> +device. However, the SPI core code wants every device in the tree to
 >> +specify a maximum frequency. So because the mux is a device to a
 >> +parent SPI master, you need to set a maximum frequency.  It's best to
 >> +set this high, as the driver will take the minimum of this value and
 >> +the child's maximum frequency value when doing a transfer to that
 >> +child device.

 Grant> It would be worth having an exception that takes the speed from
 Grant> the spi child device. I'm not requiring you to do this, but it
 Grant> would be helpful if you could check if it can be done.

Yes, We'll change it to use iterate over the children and return the
hightest max-frequency of them for v2.



 >> diff --git a/drivers/spi/spi-mux-gpio.c b/drivers/spi/spi-mux-gpio.c
 >> new file mode 100644
 >> index 0000000..983565b
 >> --- /dev/null
 >> +++ b/drivers/spi/spi-mux-gpio.c
 >> @@ -0,0 +1,319 @@

 >> +static int spi_mux_gpio_select(struct spi_device *spi)
 >> +{
 >> +	struct spi_mux_gpio *mux = spi_master_get_devdata(spi->master);
 >> +	int i;
 >> +
 >> +	for (i = 0; i < mux->n_gpios; i++) {
 >> +		gpio_set_value(mux->gpios[i],
 >> +			       mux->values[spi->chip_select] & (1 << i));
 >> +	}
 >> +
 >> +	return 0;
 >> +}

 Grant> The spi_mux_gpio_select() function could be a callback allowing
 Grant> other mechanisms for setting the muxed CS#. In doing so, you can
 Grant> also rename the driver simply "spi_mux". I think that makes more
 Grant> sense for what you're doing.

Ok, so something similar to the i2c mux infrastructure?


 >> +static void spi_mux_gpio_complete_cb(void *context)
 >> +{
 >> +	struct spi_mux_gpio *mux = (struct spi_mux_gpio *)context;
 >> +
 >> +	/* allow transfer function to continue */
 >> +	mux->xfer_complete = true;
 >> +	wake_up_interruptible(&mux->xfer_complete_wq);
 >> +}
 >> +
 >> +static int spi_mux_gpio_transfer_one_message(struct spi_master *master,
 >> +						struct spi_message *m)
 >> +{
 >> +	struct spi_mux_gpio *mux = spi_master_get_devdata(master);
 >> +	struct spi_device *spi = m->spi;
 >> +
 >> +	void (*child_mesg_complete)(void *context);
 >> +	void *child_mesg_context;
 >> +	struct spi_device *child_mesg_dev;
 >> +
 >> +	int ret = 0;
 >> +
 >> +	ret = spi_mux_gpio_setup_mux(spi);
 >> +	if (ret)
 >> +		return ret;
 >> +
 >> +	/*
 >> +	 * Replace the complete callback, context and spi_device with our own
 >> +	 * pointers. Save originals
 >> +	 */
 >> +	child_mesg_complete = m->complete;
 >> +	child_mesg_context = m->context;
 >> +	child_mesg_dev = m->spi;
 >> +
 >> +	m->complete = spi_mux_gpio_complete_cb;
 >> +	m->context = mux;
 >> +	m->spi = mux->spi;
 >> +
 >> +	/* do the transfer + wait until it is done */
 >> +	mux->xfer_complete = false;
 >> +	spi_async(mux->spi, m);
 >> +
 >> +	ret = wait_event_interruptible(mux->xfer_complete_wq,
 >> +				       mux->xfer_complete);
 >> +
 >> +	/*
 >> +	 * restore callback, context, spi_device and do finalize, even if
 >> +	 * ret != 0. In that case, m->actual_length will hold the bytes
 >> +	 * actually transferred.
 >> +	 */
 >> +	m->complete = child_mesg_complete;
 >> +	m->context = child_mesg_context;
 >> +	m->spi = child_mesg_dev;
 >> +	spi_finalize_current_message(master);
 >> +
 >> +	return ret;
 >> +}

 Grant> I think it has gotten two complex here.  spi_mux_gpio_transfer_one_message()
 Grant> is doing a bunch of work to set up a wait event to finish up with the
 Grant> transfer. However, it really doesn't need to do this. It can return
 Grant> immediately after accepting and passing on the transfer.
 Grant> spi_finalize_current_message() can be called directly in
 Grant> spi_mux_gpio_complete_cb(). The spi core will make sure transfer doesn't
 Grant> get called again if there is already a transfer in-flight.

 Grant> Mark commented on this too, but I disagree with him on one point; it
 Grant> will actually be simpler if you finish up the transfer in the callback,
 Grant> and that it really should be implemented that way from day one.

 Grant> xfer_complete and xfer_complete_wq can be dropped, and
 Grant> child_mesg_complete, child_mesg_context and child_mesg_dev will move
 Grant> into struct spi_mux_gpio.

Thanks. We weren't quite sure about the logic here, but will change for
v2.



 >> +
 >> +static int spi_mux_gpio_probe_dt(struct spi_mux_gpio *mux)

 Grant> There really isn't any good reason other than function length to have
 Grant> this separate from the spi_mux_gpio_probe(). Probe functions are a bit
 Grant> linear sequence of setups anyway. Just roll it into the main function.

Ok, will do.


 >> +static struct spi_driver spi_mux_gpio_driver = {
 >> +	.probe	= spi_mux_gpio_probe,
 >> +	.remove	= spi_mux_gpio_remove,
 >> +	.driver	= {
 >> +		.owner	= THIS_MODULE,
 >> +		.name	= "spi-mux-gpio",
 >> +		.of_match_table = of_match_ptr(spi_mux_gpio_of_match),

 Grant> This is a DT-only driver. The of_match_ptr() bit isn't needed.


Ok, will fix for v2.

-- 
Bye, Peter Korsgaard

------------------------------------------------------------------------------
Everyone hates slow websites. So do we.
Make your web apps faster with AppDynamics
Download AppDynamics Lite for free today:
http://p.sf.net/sfu/appdyn_d2d_feb

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH] spi: Driver for GPIO controlled SPI multiplexer
       [not found]       ` <20130303074209.GA19020-yzvPICuk2AATkU/dhu1WVueM+bqZidxxQQ4Iyu8u01E@public.gmane.org>
@ 2013-03-04  2:38         ` Grant Likely
  0 siblings, 0 replies; 9+ messages in thread
From: Grant Likely @ 2013-03-04  2:38 UTC (permalink / raw)
  To: Mark Brown
  Cc: spi-devel-general-5NWGOfrQmneRv+LV9MX5uipxlwaOVQ5f,
	Dries Van Puymbroeck, Peter Korsgaard

On Sun, 3 Mar 2013 15:42:39 +0800, Mark Brown <broonie-yzvPICuk2AATkU/dhu1WVueM+bqZidxxQQ4Iyu8u01E@public.gmane.org> wrote:
> On Sat, Mar 02, 2013 at 05:32:44PM +0000, Grant Likely wrote:
> 
> > Mark commented on this too, but I disagree with him on one point; it
> > will actually be simpler if you finish up the transfer in the callback,
> > and that it really should be implemented that way from day one.
> 
> > xfer_complete and xfer_complete_wq can be dropped, and
> > child_mesg_complete, child_mesg_context and child_mesg_dev will move
> > into struct spi_mux_gpio.
> 
> I agree with this, I was just thinking that the waitqueue looked like a
> first stage in allowing multiple in flight transfers on the parent.
> Unless I'm missing something, I didn't think too deeply about
> implementing that, it's just that it's going to be needed at some point
> as it's so important for performance.

Alright, I'll bite. How? What's the scenario where the driver will need
to manage the wait queue directly? The point of the transfer_one_message
interface was pull queue management into the core and allow the driver
to get the next message just when it needs it.

g.

------------------------------------------------------------------------------
Everyone hates slow websites. So do we.
Make your web apps faster with AppDynamics
Download AppDynamics Lite for free today:
http://p.sf.net/sfu/appdyn_d2d_feb

^ permalink raw reply	[flat|nested] 9+ messages in thread

* [PATCH] spi: Add SPI mux core and GPIO-based mux driver
       [not found] ` <1361973519-30633-1-git-send-email-peter.korsgaard-ob4gmnvZ1/cAvxtiuMwx3w@public.gmane.org>
  2013-03-02 17:32   ` Grant Likely
@ 2013-03-26 21:14   ` Dries Van Puymbroeck
       [not found]     ` <1364332460-4808-1-git-send-email-Dries.VanPuymbroeck-ob4gmnvZ1/cAvxtiuMwx3w@public.gmane.org>
  1 sibling, 1 reply; 9+ messages in thread
From: Dries Van Puymbroeck @ 2013-03-26 21:14 UTC (permalink / raw)
  To: grant.likely-s3s/WqlpOiPyB63q8FvJNQ
  Cc: spi-devel-general-5NWGOfrQmneRv+LV9MX5uipxlwaOVQ5f,
	Dries Van Puymbroeck, peter.korsgaard-ob4gmnvZ1/cAvxtiuMwx3w,
	dries.vanpuymbroeck-s4ckItp294LQT0dZR+AlfA

This patch contains the core functionality for a multiplexer on an
SPI bus. This can be useful if a board requires more SPI devices,
and thus more chip selects, than the SPI controller on the processor
has available.

The mux device is added in the device tree as a child node of the
SPI master. Then, devices can be added as children of the mux node.
The mux will appear as if it was a SPI master device, and child
nodes will appear as chip selects on the mux bus. A bindings file is
provided for the device tree bindings.

This patch also includes a driver for a GPIO-based multiplexer, which
makes use of the core code. A bindings file for the GPIO
implementation is also provided for the device tree bindings.

Signed-off-by: Dries Van Puymbroeck <Dries.VanPuymbroeck-ob4gmnvZ1/cAvxtiuMwx3w@public.gmane.org>
---
Change log:

Changes w.r.t. V1:
 - Split the core mux functionality and the specific mux code. Core
   functionality now is in spi-mux.c, while the actual mux we use is
   in spi-barco-orka-mux-gpio.c. Using the latter as an example,
   other users should be able to add different mux implementations,
   reusing the parts in the core. spi-mux.h is also provided to be
   in include/linux/spi/

 - Changed the compatible string of the specific mux implementation
   to "barco,orka-spi-mux-gpio"

 - Changed device tree property "mux-gpios" to "gpios"

 - Changed the spi_mux_transfer_one_message and spi_mux_complete_cb
   functions according to feedback from Grant Likely: removed the
   wait queue, and call the spi_finalize_current_message call
   directly from the spi_mux_complete_cb. This removes xfer_complete
   and xfer_complete_wq, and moves child_mesg_complete
   child_mesg_context and child_mesg_dev into spi_mux_priv.

 - Renamed some structs and functions to be more in line with the
   naming in the code for I2C muxes.

 - Joined the spi_mux_gpio_probe_dt function with the
   spi_mux_gpio_probe function (and renamed to spi_add_mux)

 - Removed the values array per chip select in the specific mux code.
   The reg property is now directly used as chip select and bit mask
   for the gpio mux. (This also removes a bug where the values array
   was incorrectly allocated for sparse CS's

 - Removed of_match_ptr since driver is DT only.

Not changed w.r.t. V1:
 - spi-max-frequency is still a required DT property, even for the
   gpio mux, though it does not mean anything there. However, the
   check for this mandatory property is done by the spi core, before
   the probe function of the mux is called. This means that the mux
   code cannot get the property from its child devices before the
   check is done (and right now the core aborts the initialization if
   spi-max-frequency is not set). The only solution I see is to loosen
   the check in spi core, or requiring that muxes have this property
   too, even if it does not have meaning. If anyone has some ideas
   about this, I would appreciate them!


 .../bindings/spi/barco-orka-spi-mux-gpio.txt       |   35 ++++
 Documentation/devicetree/bindings/spi/spi-mux.txt  |  106 ++++++++++
 drivers/spi/Kconfig                                |   25 +++
 drivers/spi/Makefile                               |    4 +
 drivers/spi/spi-barco-orka-mux-gpio.c              |  142 +++++++++++++
 drivers/spi/spi-mux.c                              |  219 ++++++++++++++++++++
 include/linux/spi/spi-mux.h                        |   58 ++++++
 7 files changed, 589 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/spi/barco-orka-spi-mux-gpio.txt
 create mode 100644 Documentation/devicetree/bindings/spi/spi-mux.txt
 create mode 100644 drivers/spi/spi-barco-orka-mux-gpio.c
 create mode 100644 drivers/spi/spi-mux.c
 create mode 100644 include/linux/spi/spi-mux.h

diff --git a/Documentation/devicetree/bindings/spi/barco-orka-spi-mux-gpio.txt b/Documentation/devicetree/bindings/spi/barco-orka-spi-mux-gpio.txt
new file mode 100644
index 0000000..4604374
--- /dev/null
+++ b/Documentation/devicetree/bindings/spi/barco-orka-spi-mux-gpio.txt
@@ -0,0 +1,35 @@
+GPIO-based SPI Chip Select Mux for Barco Orka board
+
+This binding describes a SPI bus multiplexer that uses GPIOs to route the SPI
+chip select signals.
+
+      MOSI /--------------------------------+--------+--------+--------\
+      MISO |/------------------------------+|-------+|-------+|-------\|
+       SCL ||/----------------------------+||------+||------+||------\||
+           |||                            |||      |||      |||      |||
+    +------------+                        |||      |||      |||      |||
+    | SoC  |||   |                      +-+++-+  +-+++-+  +-+++-+  +-+++-+
+    |      |||   |                      | dev |  | dev |  | dev |  | dev |
+    |   +--+++-+ | CS-X  +------+\      +--+--+  +--+--+  +--+--+  +--+--+
+    |   | SPI  +-|-------+ Mux  |\\   CS-0 |        |        |        |
+    |   +------+ |       +--++--+\\\-------/   CS-1 |        |        |
+    |            |          ||   \\\----------------/   CS-2 |        |
+    |   +------+ |          ||    \\-------------------------/   CS-3 |
+    |   | GPIO +-|----------/|     \----------------------------------/
+    |   |      +-|-----------/
+    |   +------+ |
+    +------------+
+
+Required properties:
+* Standard SPI mux properties. See spi-mux.txt in this directory.
+- compatible: "barco,orka-spi-mux-gpio"
+- gpios: list of gpios used to control the muxer
+
+Whenever an access is made to a child device, the value set in the revelant
+node's reg property is interpreted as a bitmask defining the state of the gpio
+pins, with the least significant bit defining the state of first gpio, the next
+bit the state of the second gpio and so forth. The node's reg property is also
+used by the core SPI code as the (virtual) chip select in this case.
+
+Example: see spi-mux.txt in this directory, which uses an example with this
+specific SPI mux.
diff --git a/Documentation/devicetree/bindings/spi/spi-mux.txt b/Documentation/devicetree/bindings/spi/spi-mux.txt
new file mode 100644
index 0000000..efe1ebe
--- /dev/null
+++ b/Documentation/devicetree/bindings/spi/spi-mux.txt
@@ -0,0 +1,106 @@
+Common SPI multiplexer properties.
+
+This binding describes a SPI bus multiplexer to route the SPI chip select
+signals. This can be used when you need more devices than the SPI controller
+has chip selects available. An example setup is shown in ASCII art; the actual
+setting of the multiplexer to a channel needs to be done by a specific SPI mux
+driver. See barco-orka-spi-mux-gpio.txt for a concrete example.
+
+      MOSI /--------------------------------+--------+--------+--------\
+      MISO |/------------------------------+|-------+|-------+|-------\|
+       SCL ||/----------------------------+||------+||------+||------\||
+           |||                            |||      |||      |||      |||
+    +------------+                        |||      |||      |||      |||
+    | SoC  |||   |                      +-+++-+  +-+++-+  +-+++-+  +-+++-+
+    |      |||   |                      | dev |  | dev |  | dev |  | dev |
+    |   +--+++-+ | CS-X  +------+\      +--+--+  +--+--+  +--+--+  +--+--+
+    |   | SPI  +-|-------+ Mux  |\\   CS-0 |        |        |        |
+    |   +------+ |       +--+---+\\\-------/   CS-1 |        |        |
+    |            |          |    \\\----------------/   CS-2 |        |
+    |   +------+ |          |     \\-------------------------/   CS-3 |
+    |   | ?    +-|----------/      \----------------------------------/
+    |   +------+ |
+    +------------+
+
+Required properties:
+- #address-cells: <1> (as for any SPI master device)
+- #size-cells: <0> (as for any SPI master device)
+- reg: chip select of the mux on the parent SPI master
+- spi-max-frequency: the maximum frequency allowed for any devices on this mux
+* SPI child nodes, as if the mux is a real spi master
+
+Optional properties:
+- Other properties specific to the multiplexer/switch hardware.
+
+A new SPI bus will be created. Then for each child node, a SPI device is
+created, with a virtual chip select on this bus according to the reg property.
+
+The property spi-max-frequency is conceptually not needed, as each child node
+holds the maximum frequency specific to that device. However, the SPI core code
+wants every device in the tree to specify a maximum frequency. So because the
+mux is a device to a parent SPI master, you need to set a maximum frequency.
+It's best to set this high, as the driver will take the minimum of this value
+and the child's maximum frequency value when doing a transfer to that child
+device.
+
+Example:
+	/*
+	 * An SPI mux on chip select 1 of the spi1 peripheral controller of an
+	 * am33xx soc. Chip select 0 is taken by another device, and the mux is
+	 * on chip select 1. Behind the mux are 4 devices which are defined as
+	 * if the spi-mux is a master. The specific mux implementation used is
+	 * barco,orka-spi-mux-gpio
+	 */
+
+	spi1 {
+		compatible = "ti,omap4-mcspi";
+		status = "enabled";
+
+		spi-flash@0 {
+			compatible = "m25p40";
+			reg = <0>;
+			spi-max-frequency = <10000000>;
+		};
+
+		spi-mux {
+			compatible = "barco,orka-spi-mux-gpio";
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			reg = <1>;
+			spi-max-frequency = <100000000>;
+
+			gpios = <&gpio2 30 0 &gpio2 31 0>;
+
+			spi-flash@0 {
+				#address-cells = <1>;
+				#size-cells = <1>;
+				compatible = "sst,sst25vf016b";
+				spi-max-frequency = <40000000>;
+				reg = <0>;
+			};
+
+			spi-device@1 {
+				compatible = "spidev";
+				reg = <1>;
+				spi-max-frequency = <10000000>;
+			};
+
+			spi-flash@2 {
+				compatible = "winbond,w25q32";
+				reg = <2>;
+				spi-max-frequency = <20000000>;
+			};
+
+			mc13892@3 {
+				#address-cells = <1>;
+				#size-cells = <0>;
+				compatible = "fsl,mc13892";
+				spi-max-frequency = <6000000>;
+				reg = <3>;
+
+				/* more settings... */
+			}
+
+		};
+	};
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
index f80eee7..0b6aa58 100644
--- a/drivers/spi/Kconfig
+++ b/drivers/spi/Kconfig
@@ -481,6 +481,31 @@ config SPI_DW_MMIO
 	tristate "Memory-mapped io interface driver for DW SPI core"
 	depends on SPI_DESIGNWARE && HAVE_CLK
 
+comment "SPI Multiplexer support"
+
+config SPI_MUX
+	tristate "SPI multiplexer support"
+	help
+	  This adds support for SPI multiplexers. The mux will be accessible as
+	  an extra master bus, the devices behind the mux will appear to be
+	  chip selects on this master bus. It is still neccessary to select one
+	  or more specific mux drivers.
+
+config SPI_BARCO_ORKA_MUX_GPIO
+	tristate "SPI mux support for the barco orka board, GPIO based"
+	depends on GENERIC_GPIO
+	depends on SPI_MUX
+	help
+	  This adds support for the SPI mux on the Orka board from Barco. The
+	  mux is a simple GPIO based multiplexer where the chip select is
+	  interpreted as a bitmask defining the state of the gpio pins, with
+	  the least significant bit defining the state of first gpio, the next
+	  bit the state of the second gpio and so forth.
+
+#
+# Add new SPI multiplexer drivers in alphabetical order above this line
+#
+
 #
 # There are lots of SPI device types, with sensors and memory
 # being probably the most widely used ones.
diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile
index e53c309..98c0572 100644
--- a/drivers/spi/Makefile
+++ b/drivers/spi/Makefile
@@ -8,6 +8,7 @@ ccflags-$(CONFIG_SPI_DEBUG) := -DDEBUG
 # config declarations into driver model code
 obj-$(CONFIG_SPI_MASTER)		+= spi.o
 obj-$(CONFIG_SPI_SPIDEV)		+= spidev.o
+obj-$(CONFIG_SPI_MUX)			+= spi-mux.o
 
 # SPI master controller drivers (bus)
 obj-$(CONFIG_SPI_ALTERA)		+= spi-altera.o
@@ -71,3 +72,6 @@ obj-$(CONFIG_SPI_TOPCLIFF_PCH)		+= spi-topcliff-pch.o
 obj-$(CONFIG_SPI_TXX9)			+= spi-txx9.o
 obj-$(CONFIG_SPI_XCOMM)		+= spi-xcomm.o
 obj-$(CONFIG_SPI_XILINX)		+= spi-xilinx.o
+
+#SPI mux drivers
+obj-$(CONFIG_SPI_BARCO_ORKA_MUX_GPIO)	+= spi-barco-orka-mux-gpio.o
diff --git a/drivers/spi/spi-barco-orka-mux-gpio.c b/drivers/spi/spi-barco-orka-mux-gpio.c
new file mode 100644
index 0000000..7b0fc89
--- /dev/null
+++ b/drivers/spi/spi-barco-orka-mux-gpio.c
@@ -0,0 +1,142 @@
+/*
+ * SPI multiplexer driver using GPIO API for Barco Orka board
+ *
+ * Dries Van Puymbroeck <Dries.VanPuymbroeck-ob4gmnvZ1/cAvxtiuMwx3w@public.gmane.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not,  see <http://www.gnu.org/licenses>
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/gpio.h>
+#include <linux/of_gpio.h>
+#include <linux/spi/spi.h>
+#include <linux/spi/spi-mux.h>
+
+/**
+ * DOC: Driver description
+ *
+ * This driver supports the mux on the Barco Orka board. The MUX can set up 2^n
+ * channels, where n is the number of GPIO's connected to set the MUX. The chip
+ * select of the child devices will be interpreted as a bitmask for the GPIO's,
+ * with the least significant bit defining the state of first gpio, the next
+ * bit the state of the second gpio and so forth.
+ */
+
+/**
+ * struct barco_orka_spi_mux_gpio - the basic barco_orka_spi_mux_gpio structure
+ * @gpios:		Array of GPIO numbers used to control MUX
+ * @n_gpios:		Number of GPIOs used to control MUX
+ */
+struct barco_orka_spi_mux_gpio {
+	unsigned int	*gpios;
+	int		n_gpios;
+};
+
+static int barco_orka_spi_mux_gpio_select(void *mux_dev, u8 chip_select)
+{
+	struct barco_orka_spi_mux_gpio *mux =
+		(struct barco_orka_spi_mux_gpio *)mux_dev;
+	int i;
+
+	for (i = 0; i < mux->n_gpios; i++) {
+		gpio_set_value(mux->gpios[i],
+			       chip_select & (1 << i));
+	}
+
+	return 0;
+}
+
+static int barco_orka_spi_mux_gpio_probe(struct spi_device *spi)
+{
+	struct barco_orka_spi_mux_gpio *mux;
+	struct device_node *np;
+	int ret = 0, i;
+	u16 num_chipselect;
+
+	mux = devm_kzalloc(&spi->dev, sizeof(*mux), GFP_KERNEL);
+	if (mux == NULL) {
+		dev_err(&spi->dev, "Failed to allocate driver struct\n");
+		return -ENOMEM;
+	}
+
+	np = spi->dev.of_node;
+	if (!np)
+		return -ENODEV;
+
+	mux->n_gpios = of_gpio_named_count(np, "gpios");
+	if (mux->n_gpios < 0) {
+		dev_err(&spi->dev, "Missing gpios property in the DT.\n");
+		return -EINVAL;
+	}
+
+	mux->gpios = devm_kzalloc(&spi->dev,
+			     sizeof(*mux->gpios) * mux->n_gpios,
+			     GFP_KERNEL);
+	if (!mux->gpios) {
+		dev_err(&spi->dev, "Cannot allocate gpios array");
+		return -ENOMEM;
+	}
+
+	for (i = 0; i < mux->n_gpios; i++)
+		mux->gpios[i] = of_get_named_gpio(np, "gpios", i);
+
+	for (i = 0; i < mux->n_gpios; i++) {
+		devm_gpio_request(&spi->dev, mux->gpios[i],
+				  "barco-orka-spi-mux-gpio");
+		gpio_direction_output(mux->gpios[i], 0);
+	}
+
+	/* the mux can have 2 ^ <nr_gpio_used_for_muxing> chip selects */
+	num_chipselect = 1 << mux->n_gpios;
+
+	ret = spi_add_mux(spi, mux, num_chipselect,
+			  barco_orka_spi_mux_gpio_select);
+	return ret;
+}
+
+static int barco_orka_spi_mux_gpio_remove(struct spi_device *spi)
+{
+	spi_del_mux(spi);
+	return 0;
+}
+
+static const struct of_device_id barco_orka_spi_mux_gpio_of_match[] = {
+	{ .compatible = "barco,orka-spi-mux-gpio", },
+	{},
+};
+MODULE_DEVICE_TABLE(of, barco_orka_spi_mux_gpio_of_match);
+
+static struct spi_driver barco_orka_spi_mux_gpio_driver = {
+	.probe	= barco_orka_spi_mux_gpio_probe,
+	.remove	= barco_orka_spi_mux_gpio_remove,
+	.driver	= {
+		.owner	= THIS_MODULE,
+		.name	= "barco,orka-spi-mux-gpio",
+		.of_match_table = barco_orka_spi_mux_gpio_of_match,
+	},
+};
+
+module_spi_driver(barco_orka_spi_mux_gpio_driver);
+
+MODULE_DESCRIPTION("GPIO-based SPI multiplexer driver for Barco Orka");
+MODULE_AUTHOR("Dries Van Puymbroeck <Dries.VanPuymbroeck-ob4gmnvZ1/cAvxtiuMwx3w@public.gmane.org>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("spi:barco,orka-spi-mux-gpio");
diff --git a/drivers/spi/spi-mux.c b/drivers/spi/spi-mux.c
new file mode 100644
index 0000000..9cc9e94
--- /dev/null
+++ b/drivers/spi/spi-mux.c
@@ -0,0 +1,219 @@
+/*
+ * SPI multiplexer core driver
+ *
+ * Dries Van Puymbroeck <Dries.VanPuymbroeck-ob4gmnvZ1/cAvxtiuMwx3w@public.gmane.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not,  see <http://www.gnu.org/licenses>
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+#include <linux/spi/spi.h>
+#include <linux/spi/spi-mux.h>
+
+
+/**
+ * DOC: Driver description
+ *
+ * This driver supports a MUX on an SPI bus. This can be useful when you need
+ * more chip selects than the hardware peripherals support, or than are
+ * available in a particular board setup.
+ *
+ * The driver will create an additional master bus. Devices added under the mux
+ * will be handled as 'chip selects' on the mux bus.
+ *
+ * This is just the core mux driver, you will need an aditional mux-specific
+ * driver which needs to implement the spi_mux_select callback to set the mux.
+ */
+
+/**
+ * struct spi_mux_priv - the basic spi_mux structure
+ * @spi_device:		pointer to the device struct attached to the parent
+ *			spi master
+ * @current_cs:		The current chip select set in the mux
+ * @child_mesg_complete: The mux replaces the complete callback in the child's
+ *			message to its own callback; this field is used by the
+ *			driver to store the child's callback during a transfer
+ * @child_mesg_context: Used to store the child's context to the callback
+ * @child_mesg_dev:	Used to store the spi_device pointer to the child
+ * @spi_mux_select:	Callback to the specific mux implementation to set the
+ *			mux to a chip select
+ * @mux_dev:		Data passed to spi_mux_select callback and returned
+ *			with spi_del_mux
+ */
+struct spi_mux_priv {
+	struct spi_device	*spi;
+	u8			current_cs;
+
+	void			(*child_mesg_complete)(void *context);
+	void			*child_mesg_context;
+	struct spi_device	*child_mesg_dev;
+
+	int	(*spi_mux_select)(void *mux_dev, u8 chip_select);
+	void	*mux_dev;
+};
+
+/* should not get called when the parent master is doing a transfer */
+static int spi_mux_setup_mux(struct spi_device *spi)
+{
+	struct spi_mux_priv *priv = spi_master_get_devdata(spi->master);
+	int ret = 0;
+
+	if (priv->current_cs != spi->chip_select) {
+		dev_dbg(&priv->spi->dev,
+			"setting up the mux for cs %d\n",
+			spi->chip_select);
+
+		/* copy the child device's settings except for the cs */
+		if (spi->max_speed_hz < priv->spi->max_speed_hz)
+			priv->spi->max_speed_hz = spi->max_speed_hz;
+		priv->spi->mode = spi->mode;
+		priv->spi->bits_per_word = spi->bits_per_word;
+
+		ret = priv->spi_mux_select(priv->mux_dev, spi->chip_select);
+		if (ret)
+			return ret;
+
+		priv->current_cs = spi->chip_select;
+	}
+
+	return ret;
+}
+
+static int spi_mux_setup(struct spi_device *spi)
+{
+	struct spi_mux_priv *priv = spi_master_get_devdata(spi->master);
+
+	/*
+	 * can be called multiple times, won't do a valid setup now but we will
+	 * change the settings when we do a transfer (necessary because we
+	 * can't predict from which device it will be anyway)
+	 */
+	return spi_setup(priv->spi);
+}
+
+static void spi_mux_complete_cb(void *context)
+{
+	struct spi_mux_priv *priv = (struct spi_mux_priv *)context;
+	struct spi_master *master = spi_get_drvdata(priv->spi);
+	struct spi_message *m = master->cur_msg;
+
+	m->complete = priv->child_mesg_complete;
+	m->context = priv->child_mesg_context;
+	m->spi = priv->child_mesg_dev;
+	spi_finalize_current_message(master);
+}
+
+static int spi_mux_transfer_one_message(struct spi_master *master,
+						struct spi_message *m)
+{
+	struct spi_mux_priv *priv = spi_master_get_devdata(master);
+	struct spi_device *spi = m->spi;
+	int ret = 0;
+
+	ret = spi_mux_setup_mux(spi);
+	if (ret)
+		return ret;
+
+	/*
+	 * Replace the complete callback, context and spi_device with our own
+	 * pointers. Save originals
+	 */
+	priv->child_mesg_complete = m->complete;
+	priv->child_mesg_context = m->context;
+	priv->child_mesg_dev = m->spi;
+
+	m->complete = spi_mux_complete_cb;
+	m->context = priv;
+	m->spi = priv->spi;
+
+	/* do the transfer */
+	ret = spi_async(priv->spi, m);
+	return ret;
+}
+
+int spi_add_mux(struct spi_device *spi, void *mux_dev, u16 num_chipselect,
+		int (*spi_mux_select)(void *mux_dev, u8 chip_select))
+{
+	struct spi_master *master;
+	struct spi_mux_priv *priv;
+	int ret = 0;
+
+	master = spi_alloc_master(&spi->dev, sizeof(*priv));
+	if (master == NULL) {
+		dev_dbg(&spi->dev, "master allocation failed\n");
+		return -ENOMEM;
+	}
+
+	dev_set_drvdata(&spi->dev, master);
+	priv = spi_master_get_devdata(master);
+	priv->spi = spi;
+	priv->spi_mux_select = spi_mux_select;
+	priv->mux_dev = mux_dev;
+
+	priv->current_cs = 0;
+	ret = priv->spi_mux_select(priv->mux_dev, priv->current_cs);
+	if (ret)
+		goto err_mux_select;
+
+	/* supported modes are the same as our parent's */
+	master->mode_bits = spi->master->mode_bits;
+
+	master->setup = spi_mux_setup;
+	master->transfer_one_message = spi_mux_transfer_one_message;
+	master->num_chipselect = num_chipselect;
+	master->dev.of_node = spi->dev.of_node;
+
+	/*
+	 * when we register our mux as an spi master, it will parse the
+	 * the children of this node and add them as devices.
+	 * So we don't need to parse the child nodes here.
+	 */
+	ret = spi_register_master(master);
+	if (ret < 0)
+		goto err_register_master;
+
+	return ret;
+
+err_mux_select:
+err_register_master:
+	spi_master_put(master);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(spi_add_mux);
+
+void spi_del_mux(struct spi_device *spi)
+{
+	struct spi_master *master = spi_get_drvdata(spi);
+
+	spi_unregister_master(master);
+	spi_master_put(master);
+}
+EXPORT_SYMBOL_GPL(spi_del_mux);
+
+void *spi_get_mux_dev(struct spi_device *spi)
+{
+	struct spi_master *master = spi_get_drvdata(spi);
+	struct spi_mux_priv *priv = spi_master_get_devdata(master);
+	return priv->mux_dev;
+}
+EXPORT_SYMBOL_GPL(spi_get_mux_dev);
+
+MODULE_DESCRIPTION("SPI multiplexer core functions");
+MODULE_AUTHOR("Dries Van Puymbroeck <Dries.VanPuymbroeck-ob4gmnvZ1/cAvxtiuMwx3w@public.gmane.org>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("spi:spi-mux");
diff --git a/include/linux/spi/spi-mux.h b/include/linux/spi/spi-mux.h
new file mode 100644
index 0000000..726d1ef
--- /dev/null
+++ b/include/linux/spi/spi-mux.h
@@ -0,0 +1,58 @@
+/*
+ * SPI multiplexer core driver
+ *
+ * Dries Van Puymbroeck <Dries.VanPuymbroeck-ob4gmnvZ1/cAvxtiuMwx3w@public.gmane.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not,  see <http://www.gnu.org/licenses>
+ */
+
+#ifndef _LINUX_SPI_MUX_H
+#define _LINUX_SPI_MUX_H
+
+#ifdef __KERNEL__
+
+/**
+ * spi_add_mux: - create a SPI bus on a multiplexed bus segment.
+ * @spi: the SPI device struct attached to the parent SPI controller
+ * @mux_dev: pointer to the mux's implementation-dependent data
+ * @num_chipselect: maximum number of chip selects on this mux
+ * @spi_mux_select: callback function to set the mux to a specified chip select
+ *
+ * Description: adds a mux on the chip select of @spi
+ * Return: 0 if success, err otherwise
+ */
+extern int spi_add_mux(struct spi_device *spi,
+		       void *mux_dev,
+		       u16 num_chipselect,
+		       int (*spi_mux_select)(void *mux_dev, u8 chip_select));
+
+/**
+ * spi_del_mux: - delete a SPI mutliplexer previously added by spi_add_mux
+ * @spi: the SPI device struct attached to the parent SPI controller
+ *
+ * Description: deletes the mux on the chip select of @spi
+ */
+extern void spi_del_mux(struct spi_device *spi);
+
+/**
+ * spi_get_mux_dev: - get pointer to the mux's implementation-dependent data
+ * @spi: the SPI device struct attached to the parent SPI controller
+ *
+ * Return: the mux_dev pointer passed in spi_add_mux
+ */
+extern void *spi_get_mux_dev(struct spi_device *spi);
+
+#endif /* __KERNEL__ */
+
+#endif /* _LINUX_SPI_MUX_H */
-- 
1.7.9.5


------------------------------------------------------------------------------
Own the Future-Intel&reg; Level Up Game Demo Contest 2013
Rise to greatness in Intel's independent game demo contest.
Compete for recognition, cash, and the chance to get your game 
on Steam. $5K grand prize plus 10 genre and skill prizes. 
Submit your demo by 6/6/13. http://p.sf.net/sfu/intel_levelupd2d

^ permalink raw reply related	[flat|nested] 9+ messages in thread

* RE: [PATCH] spi: Add SPI mux core and GPIO-based mux driver
       [not found]     ` <1364332460-4808-1-git-send-email-Dries.VanPuymbroeck-ob4gmnvZ1/cAvxtiuMwx3w@public.gmane.org>
@ 2013-04-09  8:19       ` Van Puymbroeck, Dries
  0 siblings, 0 replies; 9+ messages in thread
From: Van Puymbroeck, Dries @ 2013-04-09  8:19 UTC (permalink / raw)
  To: Dries Van Puymbroeck, grant.likely-s3s/WqlpOiPyB63q8FvJNQ
  Cc: spi-devel-general-5NWGOfrQmneRv+LV9MX5uipxlwaOVQ5f, Korsgaard,
	Peter, dries.vanpuymbroeck-s4ckItp294LQT0dZR+AlfA

Hello,

Does anyone have more feedback concerning the reworked patch for the
driver for the SPI multiplexer? There seemed to be some interest in this
when we sent V1 (2013/02/27) and I've tried to incorporate all of the
feedback of V1  into V2. Did I mess something up submitting the V2
patch?

Thanks,

Dries Van Puymbroeck


-----Original Message-----
From: Dries Van Puymbroeck [mailto:dries.van.puymbroeck-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org] 
Sent: dinsdag 26 maart 2013 22:14
To: grant.likely-s3s/WqlpOiPyB63q8FvJNQ@public.gmane.org
Cc: spi-devel-general-5NWGOfrQmneRv+LV9MX5uipxlwaOVQ5f@public.gmane.org;
dries.vanpuymbroeck-s4ckItp294LQT0dZR+AlfA@public.gmane.org; Korsgaard, Peter; Van Puymbroeck, Dries
Subject: [PATCH] spi: Add SPI mux core and GPIO-based mux driver

This patch contains the core functionality for a multiplexer on an SPI
bus. This can be useful if a board requires more SPI devices, and thus
more chip selects, than the SPI controller on the processor has
available.

The mux device is added in the device tree as a child node of the SPI
master. Then, devices can be added as children of the mux node.
The mux will appear as if it was a SPI master device, and child nodes
will appear as chip selects on the mux bus. A bindings file is provided
for the device tree bindings.

This patch also includes a driver for a GPIO-based multiplexer, which
makes use of the core code. A bindings file for the GPIO implementation
is also provided for the device tree bindings.

Signed-off-by: Dries Van Puymbroeck <Dries.VanPuymbroeck-ob4gmnvZ1/cAvxtiuMwx3w@public.gmane.org>
---
Change log:

Changes w.r.t. V1:
 - Split the core mux functionality and the specific mux code. Core
   functionality now is in spi-mux.c, while the actual mux we use is
   in spi-barco-orka-mux-gpio.c. Using the latter as an example,
   other users should be able to add different mux implementations,
   reusing the parts in the core. spi-mux.h is also provided to be
   in include/linux/spi/

 - Changed the compatible string of the specific mux implementation
   to "barco,orka-spi-mux-gpio"

 - Changed device tree property "mux-gpios" to "gpios"

 - Changed the spi_mux_transfer_one_message and spi_mux_complete_cb
   functions according to feedback from Grant Likely: removed the
   wait queue, and call the spi_finalize_current_message call
   directly from the spi_mux_complete_cb. This removes xfer_complete
   and xfer_complete_wq, and moves child_mesg_complete
   child_mesg_context and child_mesg_dev into spi_mux_priv.

 - Renamed some structs and functions to be more in line with the
   naming in the code for I2C muxes.

 - Joined the spi_mux_gpio_probe_dt function with the
   spi_mux_gpio_probe function (and renamed to spi_add_mux)

 - Removed the values array per chip select in the specific mux code.
   The reg property is now directly used as chip select and bit mask
   for the gpio mux. (This also removes a bug where the values array
   was incorrectly allocated for sparse CS's

 - Removed of_match_ptr since driver is DT only.

Not changed w.r.t. V1:
 - spi-max-frequency is still a required DT property, even for the
   gpio mux, though it does not mean anything there. However, the
   check for this mandatory property is done by the spi core, before
   the probe function of the mux is called. This means that the mux
   code cannot get the property from its child devices before the
   check is done (and right now the core aborts the initialization if
   spi-max-frequency is not set). The only solution I see is to loosen
   the check in spi core, or requiring that muxes have this property
   too, even if it does not have meaning. If anyone has some ideas
   about this, I would appreciate them!


 .../bindings/spi/barco-orka-spi-mux-gpio.txt       |   35 ++++
 Documentation/devicetree/bindings/spi/spi-mux.txt  |  106 ++++++++++
 drivers/spi/Kconfig                                |   25 +++
 drivers/spi/Makefile                               |    4 +
 drivers/spi/spi-barco-orka-mux-gpio.c              |  142 +++++++++++++
 drivers/spi/spi-mux.c                              |  219
++++++++++++++++++++
 include/linux/spi/spi-mux.h                        |   58 ++++++
 7 files changed, 589 insertions(+)
 create mode 100644
Documentation/devicetree/bindings/spi/barco-orka-spi-mux-gpio.txt
 create mode 100644 Documentation/devicetree/bindings/spi/spi-mux.txt
 create mode 100644 drivers/spi/spi-barco-orka-mux-gpio.c
 create mode 100644 drivers/spi/spi-mux.c  create mode 100644
include/linux/spi/spi-mux.h

diff --git
a/Documentation/devicetree/bindings/spi/barco-orka-spi-mux-gpio.txt
b/Documentation/devicetree/bindings/spi/barco-orka-spi-mux-gpio.txt
new file mode 100644
index 0000000..4604374
--- /dev/null
+++ b/Documentation/devicetree/bindings/spi/barco-orka-spi-mux-gpio.txt
@@ -0,0 +1,35 @@
+GPIO-based SPI Chip Select Mux for Barco Orka board
+
+This binding describes a SPI bus multiplexer that uses GPIOs to route 
+the SPI chip select signals.
+
+      MOSI
/--------------------------------+--------+--------+--------\
+      MISO
|/------------------------------+|-------+|-------+|-------\|
+       SCL
||/----------------------------+||------+||------+||------\||
+           |||                            |||      |||      |||
|||
+    +------------+                        |||      |||      |||
|||
+    | SoC  |||   |                      +-+++-+  +-+++-+  +-+++-+
+-+++-+
+    |      |||   |                      | dev |  | dev |  | dev |  |
dev |
+    |   +--+++-+ | CS-X  +------+\      +--+--+  +--+--+  +--+--+
+--+--+
+    |   | SPI  +-|-------+ Mux  |\\   CS-0 |        |        |        |
+    |   +------+ |       +--++--+\\\-------/   CS-1 |        |        |
+    |            |          ||   \\\----------------/   CS-2 |        |
+    |   +------+ |          ||    \\-------------------------/   CS-3 |
+    |   | GPIO +-|----------/|     \----------------------------------/
+    |   |      +-|-----------/
+    |   +------+ |
+    +------------+
+
+Required properties:
+* Standard SPI mux properties. See spi-mux.txt in this directory.
+- compatible: "barco,orka-spi-mux-gpio"
+- gpios: list of gpios used to control the muxer
+
+Whenever an access is made to a child device, the value set in the 
+revelant node's reg property is interpreted as a bitmask defining the 
+state of the gpio pins, with the least significant bit defining the 
+state of first gpio, the next bit the state of the second gpio and so 
+forth. The node's reg property is also used by the core SPI code as the
(virtual) chip select in this case.
+
+Example: see spi-mux.txt in this directory, which uses an example with 
+this specific SPI mux.
diff --git a/Documentation/devicetree/bindings/spi/spi-mux.txt
b/Documentation/devicetree/bindings/spi/spi-mux.txt
new file mode 100644
index 0000000..efe1ebe
--- /dev/null
+++ b/Documentation/devicetree/bindings/spi/spi-mux.txt
@@ -0,0 +1,106 @@
+Common SPI multiplexer properties.
+
+This binding describes a SPI bus multiplexer to route the SPI chip 
+select signals. This can be used when you need more devices than the 
+SPI controller has chip selects available. An example setup is shown in

+ASCII art; the actual setting of the multiplexer to a channel needs to 
+be done by a specific SPI mux driver. See barco-orka-spi-mux-gpio.txt
for a concrete example.
+
+      MOSI
/--------------------------------+--------+--------+--------\
+      MISO
|/------------------------------+|-------+|-------+|-------\|
+       SCL
||/----------------------------+||------+||------+||------\||
+           |||                            |||      |||      |||
|||
+    +------------+                        |||      |||      |||
|||
+    | SoC  |||   |                      +-+++-+  +-+++-+  +-+++-+
+-+++-+
+    |      |||   |                      | dev |  | dev |  | dev |  |
dev |
+    |   +--+++-+ | CS-X  +------+\      +--+--+  +--+--+  +--+--+
+--+--+
+    |   | SPI  +-|-------+ Mux  |\\   CS-0 |        |        |        |
+    |   +------+ |       +--+---+\\\-------/   CS-1 |        |        |
+    |            |          |    \\\----------------/   CS-2 |        |
+    |   +------+ |          |     \\-------------------------/   CS-3 |
+    |   | ?    +-|----------/      \----------------------------------/
+    |   +------+ |
+    +------------+
+
+Required properties:
+- #address-cells: <1> (as for any SPI master device)
+- #size-cells: <0> (as for any SPI master device)
+- reg: chip select of the mux on the parent SPI master
+- spi-max-frequency: the maximum frequency allowed for any devices on 
+this mux
+* SPI child nodes, as if the mux is a real spi master
+
+Optional properties:
+- Other properties specific to the multiplexer/switch hardware.
+
+A new SPI bus will be created. Then for each child node, a SPI device 
+is created, with a virtual chip select on this bus according to the reg
property.
+
+The property spi-max-frequency is conceptually not needed, as each 
+child node holds the maximum frequency specific to that device. 
+However, the SPI core code wants every device in the tree to specify a 
+maximum frequency. So because the mux is a device to a parent SPI
master, you need to set a maximum frequency.
+It's best to set this high, as the driver will take the minimum of this

+value and the child's maximum frequency value when doing a transfer to 
+that child device.
+
+Example:
+	/*
+	 * An SPI mux on chip select 1 of the spi1 peripheral controller
of an
+	 * am33xx soc. Chip select 0 is taken by another device, and the
mux is
+	 * on chip select 1. Behind the mux are 4 devices which are
defined as
+	 * if the spi-mux is a master. The specific mux implementation
used is
+	 * barco,orka-spi-mux-gpio
+	 */
+
+	spi1 {
+		compatible = "ti,omap4-mcspi";
+		status = "enabled";
+
+		spi-flash@0 {
+			compatible = "m25p40";
+			reg = <0>;
+			spi-max-frequency = <10000000>;
+		};
+
+		spi-mux {
+			compatible = "barco,orka-spi-mux-gpio";
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			reg = <1>;
+			spi-max-frequency = <100000000>;
+
+			gpios = <&gpio2 30 0 &gpio2 31 0>;
+
+			spi-flash@0 {
+				#address-cells = <1>;
+				#size-cells = <1>;
+				compatible = "sst,sst25vf016b";
+				spi-max-frequency = <40000000>;
+				reg = <0>;
+			};
+
+			spi-device@1 {
+				compatible = "spidev";
+				reg = <1>;
+				spi-max-frequency = <10000000>;
+			};
+
+			spi-flash@2 {
+				compatible = "winbond,w25q32";
+				reg = <2>;
+				spi-max-frequency = <20000000>;
+			};
+
+			mc13892@3 {
+				#address-cells = <1>;
+				#size-cells = <0>;
+				compatible = "fsl,mc13892";
+				spi-max-frequency = <6000000>;
+				reg = <3>;
+
+				/* more settings... */
+			}
+
+		};
+	};
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index
f80eee7..0b6aa58 100644
--- a/drivers/spi/Kconfig
+++ b/drivers/spi/Kconfig
@@ -481,6 +481,31 @@ config SPI_DW_MMIO
 	tristate "Memory-mapped io interface driver for DW SPI core"
 	depends on SPI_DESIGNWARE && HAVE_CLK
 
+comment "SPI Multiplexer support"
+
+config SPI_MUX
+	tristate "SPI multiplexer support"
+	help
+	  This adds support for SPI multiplexers. The mux will be
accessible as
+	  an extra master bus, the devices behind the mux will appear to
be
+	  chip selects on this master bus. It is still neccessary to
select one
+	  or more specific mux drivers.
+
+config SPI_BARCO_ORKA_MUX_GPIO
+	tristate "SPI mux support for the barco orka board, GPIO based"
+	depends on GENERIC_GPIO
+	depends on SPI_MUX
+	help
+	  This adds support for the SPI mux on the Orka board from
Barco. The
+	  mux is a simple GPIO based multiplexer where the chip select
is
+	  interpreted as a bitmask defining the state of the gpio pins,
with
+	  the least significant bit defining the state of first gpio,
the next
+	  bit the state of the second gpio and so forth.
+
+#
+# Add new SPI multiplexer drivers in alphabetical order above this line

+#
+
 #
 # There are lots of SPI device types, with sensors and memory  # being
probably the most widely used ones.
diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index
e53c309..98c0572 100644
--- a/drivers/spi/Makefile
+++ b/drivers/spi/Makefile
@@ -8,6 +8,7 @@ ccflags-$(CONFIG_SPI_DEBUG) := -DDEBUG  # config
declarations into driver model code
 obj-$(CONFIG_SPI_MASTER)		+= spi.o
 obj-$(CONFIG_SPI_SPIDEV)		+= spidev.o
+obj-$(CONFIG_SPI_MUX)			+= spi-mux.o
 
 # SPI master controller drivers (bus)
 obj-$(CONFIG_SPI_ALTERA)		+= spi-altera.o
@@ -71,3 +72,6 @@ obj-$(CONFIG_SPI_TOPCLIFF_PCH)		+=
spi-topcliff-pch.o
 obj-$(CONFIG_SPI_TXX9)			+= spi-txx9.o
 obj-$(CONFIG_SPI_XCOMM)		+= spi-xcomm.o
 obj-$(CONFIG_SPI_XILINX)		+= spi-xilinx.o
+
+#SPI mux drivers
+obj-$(CONFIG_SPI_BARCO_ORKA_MUX_GPIO)	+= spi-barco-orka-mux-gpio.o
diff --git a/drivers/spi/spi-barco-orka-mux-gpio.c
b/drivers/spi/spi-barco-orka-mux-gpio.c
new file mode 100644
index 0000000..7b0fc89
--- /dev/null
+++ b/drivers/spi/spi-barco-orka-mux-gpio.c
@@ -0,0 +1,142 @@
+/*
+ * SPI multiplexer driver using GPIO API for Barco Orka board
+ *
+ * Dries Van Puymbroeck <Dries.VanPuymbroeck-ob4gmnvZ1/cAvxtiuMwx3w@public.gmane.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not,  see 
+<http://www.gnu.org/licenses>  */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/gpio.h>
+#include <linux/of_gpio.h>
+#include <linux/spi/spi.h>
+#include <linux/spi/spi-mux.h>
+
+/**
+ * DOC: Driver description
+ *
+ * This driver supports the mux on the Barco Orka board. The MUX can 
+set up 2^n
+ * channels, where n is the number of GPIO's connected to set the MUX. 
+The chip
+ * select of the child devices will be interpreted as a bitmask for the

+GPIO's,
+ * with the least significant bit defining the state of first gpio, the

+next
+ * bit the state of the second gpio and so forth.
+ */
+
+/**
+ * struct barco_orka_spi_mux_gpio - the basic barco_orka_spi_mux_gpio
structure
+ * @gpios:		Array of GPIO numbers used to control MUX
+ * @n_gpios:		Number of GPIOs used to control MUX
+ */
+struct barco_orka_spi_mux_gpio {
+	unsigned int	*gpios;
+	int		n_gpios;
+};
+
+static int barco_orka_spi_mux_gpio_select(void *mux_dev, u8 
+chip_select) {
+	struct barco_orka_spi_mux_gpio *mux =
+		(struct barco_orka_spi_mux_gpio *)mux_dev;
+	int i;
+
+	for (i = 0; i < mux->n_gpios; i++) {
+		gpio_set_value(mux->gpios[i],
+			       chip_select & (1 << i));
+	}
+
+	return 0;
+}
+
+static int barco_orka_spi_mux_gpio_probe(struct spi_device *spi) {
+	struct barco_orka_spi_mux_gpio *mux;
+	struct device_node *np;
+	int ret = 0, i;
+	u16 num_chipselect;
+
+	mux = devm_kzalloc(&spi->dev, sizeof(*mux), GFP_KERNEL);
+	if (mux == NULL) {
+		dev_err(&spi->dev, "Failed to allocate driver
struct\n");
+		return -ENOMEM;
+	}
+
+	np = spi->dev.of_node;
+	if (!np)
+		return -ENODEV;
+
+	mux->n_gpios = of_gpio_named_count(np, "gpios");
+	if (mux->n_gpios < 0) {
+		dev_err(&spi->dev, "Missing gpios property in the
DT.\n");
+		return -EINVAL;
+	}
+
+	mux->gpios = devm_kzalloc(&spi->dev,
+			     sizeof(*mux->gpios) * mux->n_gpios,
+			     GFP_KERNEL);
+	if (!mux->gpios) {
+		dev_err(&spi->dev, "Cannot allocate gpios array");
+		return -ENOMEM;
+	}
+
+	for (i = 0; i < mux->n_gpios; i++)
+		mux->gpios[i] = of_get_named_gpio(np, "gpios", i);
+
+	for (i = 0; i < mux->n_gpios; i++) {
+		devm_gpio_request(&spi->dev, mux->gpios[i],
+				  "barco-orka-spi-mux-gpio");
+		gpio_direction_output(mux->gpios[i], 0);
+	}
+
+	/* the mux can have 2 ^ <nr_gpio_used_for_muxing> chip selects
*/
+	num_chipselect = 1 << mux->n_gpios;
+
+	ret = spi_add_mux(spi, mux, num_chipselect,
+			  barco_orka_spi_mux_gpio_select);
+	return ret;
+}
+
+static int barco_orka_spi_mux_gpio_remove(struct spi_device *spi) {
+	spi_del_mux(spi);
+	return 0;
+}
+
+static const struct of_device_id barco_orka_spi_mux_gpio_of_match[] = {
+	{ .compatible = "barco,orka-spi-mux-gpio", },
+	{},
+};
+MODULE_DEVICE_TABLE(of, barco_orka_spi_mux_gpio_of_match);
+
+static struct spi_driver barco_orka_spi_mux_gpio_driver = {
+	.probe	= barco_orka_spi_mux_gpio_probe,
+	.remove	= barco_orka_spi_mux_gpio_remove,
+	.driver	= {
+		.owner	= THIS_MODULE,
+		.name	= "barco,orka-spi-mux-gpio",
+		.of_match_table = barco_orka_spi_mux_gpio_of_match,
+	},
+};
+
+module_spi_driver(barco_orka_spi_mux_gpio_driver);
+
+MODULE_DESCRIPTION("GPIO-based SPI multiplexer driver for Barco Orka");

+MODULE_AUTHOR("Dries Van Puymbroeck <Dries.VanPuymbroeck-ob4gmnvZ1/cAvxtiuMwx3w@public.gmane.org>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("spi:barco,orka-spi-mux-gpio");
diff --git a/drivers/spi/spi-mux.c b/drivers/spi/spi-mux.c new file mode
100644 index 0000000..9cc9e94
--- /dev/null
+++ b/drivers/spi/spi-mux.c
@@ -0,0 +1,219 @@
+/*
+ * SPI multiplexer core driver
+ *
+ * Dries Van Puymbroeck <Dries.VanPuymbroeck-ob4gmnvZ1/cAvxtiuMwx3w@public.gmane.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not,  see 
+<http://www.gnu.org/licenses>  */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+#include <linux/spi/spi.h>
+#include <linux/spi/spi-mux.h>
+
+
+/**
+ * DOC: Driver description
+ *
+ * This driver supports a MUX on an SPI bus. This can be useful when 
+you need
+ * more chip selects than the hardware peripherals support, or than are
+ * available in a particular board setup.
+ *
+ * The driver will create an additional master bus. Devices added under

+the mux
+ * will be handled as 'chip selects' on the mux bus.
+ *
+ * This is just the core mux driver, you will need an aditional 
+mux-specific
+ * driver which needs to implement the spi_mux_select callback to set
the mux.
+ */
+
+/**
+ * struct spi_mux_priv - the basic spi_mux structure
+ * @spi_device:		pointer to the device struct attached to
the parent
+ *			spi master
+ * @current_cs:		The current chip select set in the mux
+ * @child_mesg_complete: The mux replaces the complete callback in the
child's
+ *			message to its own callback; this field is used
by the
+ *			driver to store the child's callback during a
transfer
+ * @child_mesg_context: Used to store the child's context to the
callback
+ * @child_mesg_dev:	Used to store the spi_device pointer to the
child
+ * @spi_mux_select:	Callback to the specific mux implementation to
set the
+ *			mux to a chip select
+ * @mux_dev:		Data passed to spi_mux_select callback and
returned
+ *			with spi_del_mux
+ */
+struct spi_mux_priv {
+	struct spi_device	*spi;
+	u8			current_cs;
+
+	void			(*child_mesg_complete)(void *context);
+	void			*child_mesg_context;
+	struct spi_device	*child_mesg_dev;
+
+	int	(*spi_mux_select)(void *mux_dev, u8 chip_select);
+	void	*mux_dev;
+};
+
+/* should not get called when the parent master is doing a transfer */ 
+static int spi_mux_setup_mux(struct spi_device *spi) {
+	struct spi_mux_priv *priv = spi_master_get_devdata(spi->master);
+	int ret = 0;
+
+	if (priv->current_cs != spi->chip_select) {
+		dev_dbg(&priv->spi->dev,
+			"setting up the mux for cs %d\n",
+			spi->chip_select);
+
+		/* copy the child device's settings except for the cs */
+		if (spi->max_speed_hz < priv->spi->max_speed_hz)
+			priv->spi->max_speed_hz = spi->max_speed_hz;
+		priv->spi->mode = spi->mode;
+		priv->spi->bits_per_word = spi->bits_per_word;
+
+		ret = priv->spi_mux_select(priv->mux_dev,
spi->chip_select);
+		if (ret)
+			return ret;
+
+		priv->current_cs = spi->chip_select;
+	}
+
+	return ret;
+}
+
+static int spi_mux_setup(struct spi_device *spi) {
+	struct spi_mux_priv *priv = spi_master_get_devdata(spi->master);
+
+	/*
+	 * can be called multiple times, won't do a valid setup now but
we will
+	 * change the settings when we do a transfer (necessary because
we
+	 * can't predict from which device it will be anyway)
+	 */
+	return spi_setup(priv->spi);
+}
+
+static void spi_mux_complete_cb(void *context) {
+	struct spi_mux_priv *priv = (struct spi_mux_priv *)context;
+	struct spi_master *master = spi_get_drvdata(priv->spi);
+	struct spi_message *m = master->cur_msg;
+
+	m->complete = priv->child_mesg_complete;
+	m->context = priv->child_mesg_context;
+	m->spi = priv->child_mesg_dev;
+	spi_finalize_current_message(master);
+}
+
+static int spi_mux_transfer_one_message(struct spi_master *master,
+						struct spi_message *m)
+{
+	struct spi_mux_priv *priv = spi_master_get_devdata(master);
+	struct spi_device *spi = m->spi;
+	int ret = 0;
+
+	ret = spi_mux_setup_mux(spi);
+	if (ret)
+		return ret;
+
+	/*
+	 * Replace the complete callback, context and spi_device with
our own
+	 * pointers. Save originals
+	 */
+	priv->child_mesg_complete = m->complete;
+	priv->child_mesg_context = m->context;
+	priv->child_mesg_dev = m->spi;
+
+	m->complete = spi_mux_complete_cb;
+	m->context = priv;
+	m->spi = priv->spi;
+
+	/* do the transfer */
+	ret = spi_async(priv->spi, m);
+	return ret;
+}
+
+int spi_add_mux(struct spi_device *spi, void *mux_dev, u16
num_chipselect,
+		int (*spi_mux_select)(void *mux_dev, u8 chip_select)) {
+	struct spi_master *master;
+	struct spi_mux_priv *priv;
+	int ret = 0;
+
+	master = spi_alloc_master(&spi->dev, sizeof(*priv));
+	if (master == NULL) {
+		dev_dbg(&spi->dev, "master allocation failed\n");
+		return -ENOMEM;
+	}
+
+	dev_set_drvdata(&spi->dev, master);
+	priv = spi_master_get_devdata(master);
+	priv->spi = spi;
+	priv->spi_mux_select = spi_mux_select;
+	priv->mux_dev = mux_dev;
+
+	priv->current_cs = 0;
+	ret = priv->spi_mux_select(priv->mux_dev, priv->current_cs);
+	if (ret)
+		goto err_mux_select;
+
+	/* supported modes are the same as our parent's */
+	master->mode_bits = spi->master->mode_bits;
+
+	master->setup = spi_mux_setup;
+	master->transfer_one_message = spi_mux_transfer_one_message;
+	master->num_chipselect = num_chipselect;
+	master->dev.of_node = spi->dev.of_node;
+
+	/*
+	 * when we register our mux as an spi master, it will parse the
+	 * the children of this node and add them as devices.
+	 * So we don't need to parse the child nodes here.
+	 */
+	ret = spi_register_master(master);
+	if (ret < 0)
+		goto err_register_master;
+
+	return ret;
+
+err_mux_select:
+err_register_master:
+	spi_master_put(master);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(spi_add_mux);
+
+void spi_del_mux(struct spi_device *spi) {
+	struct spi_master *master = spi_get_drvdata(spi);
+
+	spi_unregister_master(master);
+	spi_master_put(master);
+}
+EXPORT_SYMBOL_GPL(spi_del_mux);
+
+void *spi_get_mux_dev(struct spi_device *spi) {
+	struct spi_master *master = spi_get_drvdata(spi);
+	struct spi_mux_priv *priv = spi_master_get_devdata(master);
+	return priv->mux_dev;
+}
+EXPORT_SYMBOL_GPL(spi_get_mux_dev);
+
+MODULE_DESCRIPTION("SPI multiplexer core functions"); 
+MODULE_AUTHOR("Dries Van Puymbroeck <Dries.VanPuymbroeck-ob4gmnvZ1/cAvxtiuMwx3w@public.gmane.org>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("spi:spi-mux");
diff --git a/include/linux/spi/spi-mux.h b/include/linux/spi/spi-mux.h
new file mode 100644 index 0000000..726d1ef
--- /dev/null
+++ b/include/linux/spi/spi-mux.h
@@ -0,0 +1,58 @@
+/*
+ * SPI multiplexer core driver
+ *
+ * Dries Van Puymbroeck <Dries.VanPuymbroeck-ob4gmnvZ1/cAvxtiuMwx3w@public.gmane.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not,  see 
+<http://www.gnu.org/licenses>  */
+
+#ifndef _LINUX_SPI_MUX_H
+#define _LINUX_SPI_MUX_H
+
+#ifdef __KERNEL__
+
+/**
+ * spi_add_mux: - create a SPI bus on a multiplexed bus segment.
+ * @spi: the SPI device struct attached to the parent SPI controller
+ * @mux_dev: pointer to the mux's implementation-dependent data
+ * @num_chipselect: maximum number of chip selects on this mux
+ * @spi_mux_select: callback function to set the mux to a specified 
+chip select
+ *
+ * Description: adds a mux on the chip select of @spi
+ * Return: 0 if success, err otherwise
+ */
+extern int spi_add_mux(struct spi_device *spi,
+		       void *mux_dev,
+		       u16 num_chipselect,
+		       int (*spi_mux_select)(void *mux_dev, u8
chip_select));
+
+/**
+ * spi_del_mux: - delete a SPI mutliplexer previously added by 
+spi_add_mux
+ * @spi: the SPI device struct attached to the parent SPI controller
+ *
+ * Description: deletes the mux on the chip select of @spi  */ extern 
+void spi_del_mux(struct spi_device *spi);
+
+/**
+ * spi_get_mux_dev: - get pointer to the mux's implementation-dependent

+data
+ * @spi: the SPI device struct attached to the parent SPI controller
+ *
+ * Return: the mux_dev pointer passed in spi_add_mux  */ extern void 
+*spi_get_mux_dev(struct spi_device *spi);
+
+#endif /* __KERNEL__ */
+
+#endif /* _LINUX_SPI_MUX_H */
--
1.7.9.5



DISCLAIMER:
Unless indicated otherwise, the information contained in this message is privileged and confidential, and is intended only for the use of the addressee(s) named above and others who have been specifically authorized to receive it. If you are not the intended recipient, you are hereby notified that any dissemination, distribution or copying of this message and/or attachments is strictly prohibited. The company accepts no liability for any damage caused by any virus transmitted by this email. Furthermore, the company does not warrant a proper and complete transmission of this information, nor does it accept liability for any delays. If you have received this message in error, please contact the sender and delete the message. Thank you.

------------------------------------------------------------------------------
Precog is a next-generation analytics platform capable of advanced
analytics on semi-structured data. The platform includes APIs for building
apps and a phenomenal toolset for data science. Developers can use
our toolset for easy data analysis & visualization. Get a free account!
http://www2.precog.com/precogplatform/slashdotnewsletter

^ permalink raw reply related	[flat|nested] 9+ messages in thread

end of thread, other threads:[~2013-04-09  8:19 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2013-02-27 13:58 [PATCH] spi: Driver for GPIO controlled SPI multiplexer Peter Korsgaard
     [not found] ` <20130302035043.GE6610@opensource.wolfsonmicro.com>
     [not found]   ` <20130302035043.GE6610-yzvPICuk2AATkU/dhu1WVueM+bqZidxxQQ4Iyu8u01E@public.gmane.org>
2013-03-02 10:43     ` Peter Korsgaard
     [not found]       ` <20130302104853.GA31872@opensource.wolfsonmicro.com>
     [not found]         ` <20130302104853.GA31872-yzvPICuk2AATkU/dhu1WVueM+bqZidxxQQ4Iyu8u01E@public.gmane.org>
2013-03-02 11:15           ` Peter Korsgaard
2013-03-02 14:31           ` Grant Likely
     [not found] ` <1361973519-30633-1-git-send-email-peter.korsgaard-ob4gmnvZ1/cAvxtiuMwx3w@public.gmane.org>
2013-03-02 17:32   ` Grant Likely
2013-03-03  8:08     ` Peter Korsgaard
     [not found]     ` <20130303074209.GA19020@opensource.wolfsonmicro.com>
     [not found]       ` <20130303074209.GA19020-yzvPICuk2AATkU/dhu1WVueM+bqZidxxQQ4Iyu8u01E@public.gmane.org>
2013-03-04  2:38         ` Grant Likely
2013-03-26 21:14   ` [PATCH] spi: Add SPI mux core and GPIO-based mux driver Dries Van Puymbroeck
     [not found]     ` <1364332460-4808-1-git-send-email-Dries.VanPuymbroeck-ob4gmnvZ1/cAvxtiuMwx3w@public.gmane.org>
2013-04-09  8:19       ` Van Puymbroeck, Dries

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).