linux-spi.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Chris Packham <chris.packham@alliedtelesis.co.nz>
To: broonie@kernel.org, robh+dt@kernel.org, mark.rutland@arm.com
Cc: Hamish Martin <hamish.martin@alliedtelesis.co.nz>,
	linux-spi@vger.kernel.org, devicetree@vger.kernel.org,
	linux-kernel@vger.kernel.org,
	Chris Packham <chris.packham@alliedtelesis.co.nz>
Subject: [PATCH 3/3] spi: Add SPI bus gpio multiplexer
Date: Fri, 12 Apr 2019 17:02:13 +1200	[thread overview]
Message-ID: <20190412050213.17698-4-chris.packham@alliedtelesis.co.nz> (raw)
In-Reply-To: <20190412050213.17698-1-chris.packham@alliedtelesis.co.nz>

This add support for a gpio based multiplexer for SPI buses. This can be
used in situations where the cs-gpios property does not work with the
hardware design. In particular this support situations where a single
gpio is used to select between two possible devices.

Signed-off-by: Chris Packham <chris.packham@alliedtelesis.co.nz>
---
 drivers/spi/Kconfig        |   7 ++
 drivers/spi/Makefile       |   1 +
 drivers/spi/spi-mux-gpio.c | 169 +++++++++++++++++++++++++++++++++++++
 3 files changed, 177 insertions(+)
 create mode 100644 drivers/spi/spi-mux-gpio.c

diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
index f761655e2a36..bfb4bd5bc2f3 100644
--- a/drivers/spi/Kconfig
+++ b/drivers/spi/Kconfig
@@ -426,6 +426,13 @@ config SPI_MT65XX
 	  say Y or M here.If you are not sure, say N.
 	  SPI drivers for Mediatek MT65XX and MT81XX series ARM SoCs.
 
+config SPI_MUX_GPIO
+	tristate "SPI bus gpio multiplexer"
+	help
+	  This selects the SPI bus gpio multiplexer.
+	  If you have a hardware design that requires multiplexing
+	  on the SPI bus say Y or M here. If you are not sure, say N.
+
 config SPI_NPCM_PSPI
 	tristate "Nuvoton NPCM PSPI Controller"
 	depends on ARCH_NPCM || COMPILE_TEST
diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile
index d8fc03c9faa2..32d831374e1f 100644
--- a/drivers/spi/Makefile
+++ b/drivers/spi/Makefile
@@ -62,6 +62,7 @@ obj-$(CONFIG_SPI_MPC52xx)		+= spi-mpc52xx.o
 obj-$(CONFIG_SPI_MT65XX)                += spi-mt65xx.o
 obj-$(CONFIG_SPI_MXIC)			+= spi-mxic.o
 obj-$(CONFIG_SPI_MXS)			+= spi-mxs.o
+obj-$(CONFIG_SPI_MUX_GPIO)		+= spi-mux-gpio.o
 obj-$(CONFIG_SPI_NPCM_PSPI)		+= spi-npcm-pspi.o
 obj-$(CONFIG_SPI_NUC900)		+= spi-nuc900.o
 obj-$(CONFIG_SPI_NXP_FLEXSPI)		+= spi-nxp-fspi.o
diff --git a/drivers/spi/spi-mux-gpio.c b/drivers/spi/spi-mux-gpio.c
new file mode 100644
index 000000000000..3666863a4e3f
--- /dev/null
+++ b/drivers/spi/spi-mux-gpio.c
@@ -0,0 +1,169 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2019 Allied Telesis
+ */
+
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/platform_device.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_device.h>
+#include <linux/spi/spi.h>
+#include <linux/gpio/consumer.h>
+
+struct spi_mux_gpio {
+	struct gpio_descs *gpios;
+	struct spi_controller *ctlr;
+	struct spi_controller *parent_ctlr;
+	int chip_select;
+};
+
+static void spi_mux_set_cs(struct spi_device *spi, bool enable)
+{
+	DECLARE_BITMAP(values, BITS_PER_TYPE(spi->chip_select));
+	struct spi_mux_gpio *mux = spi_master_get_devdata(spi->controller);
+	struct spi_device spidev = *spi;
+
+	values[0] = spi->chip_select;
+
+	gpiod_set_array_value_cansleep(mux->gpios->ndescs,
+				       mux->gpios->desc,
+				       mux->gpios->info, values);
+
+	spidev.controller = mux->parent_ctlr;
+	spidev.master = mux->parent_ctlr;
+	spidev.chip_select = mux->chip_select;
+
+	mux->parent_ctlr->set_cs(&spidev, enable);
+}
+
+static int spi_mux_transfer_one(struct spi_controller *ctlr,
+				struct spi_device *spi,
+				struct spi_transfer *transfer)
+{
+	struct spi_mux_gpio *mux = spi_master_get_devdata(ctlr);
+	struct spi_device spidev = *spi;
+
+	spidev.controller = mux->parent_ctlr;
+	spidev.master = mux->parent_ctlr;
+	spidev.chip_select = mux->chip_select;
+
+	return mux->parent_ctlr->transfer_one(mux->parent_ctlr, &spidev, transfer);
+
+}
+
+static int spi_mux_setup(struct spi_device *spi)
+{
+	struct spi_mux_gpio *mux = spi_master_get_devdata(spi->controller);
+	struct spi_device spidev = *spi;
+
+	spidev.controller = mux->parent_ctlr;
+	spidev.master = mux->parent_ctlr;
+	spidev.chip_select = mux->chip_select;
+
+	return mux->parent_ctlr->setup(&spidev);
+}
+
+static int spi_mux_gpio_probe(struct platform_device *pdev)
+{
+	struct device_node *np = pdev->dev.of_node;
+	struct device_node *parent;
+	struct spi_controller *parent_ctlr;
+	struct spi_controller *ctlr;
+	struct spi_mux_gpio *mux;
+	struct gpio_descs *gpios;
+	int ret;
+
+	gpios = devm_gpiod_get_array(&pdev->dev, NULL, GPIOD_OUT_LOW);
+	if (IS_ERR(gpios))
+		return PTR_ERR(gpios);
+
+	parent = of_parse_phandle(np, "spi-parent-bus", 0);
+	if (!parent)
+		return -ENODEV;
+
+	parent_ctlr = of_find_spi_controller_by_node(parent);
+	if (!parent_ctlr) {
+		ret = -EPROBE_DEFER;
+		goto err_put_node;
+	}
+
+	ctlr = spi_alloc_master(&pdev->dev, sizeof(*mux));
+	if (!ctlr) {
+		ret = -ENOMEM;
+		goto err_put_device;
+	}
+	mux = spi_master_get_devdata(ctlr);
+	platform_set_drvdata(pdev, mux);
+
+	ctlr->mode_bits = parent_ctlr->mode_bits;
+	ctlr->bits_per_word_mask = parent_ctlr->bits_per_word_mask;
+	ctlr->auto_runtime_pm = parent_ctlr->auto_runtime_pm;
+	ctlr->flags = parent_ctlr->flags;
+	ctlr->set_cs = spi_mux_set_cs;
+	ctlr->transfer_one = spi_mux_transfer_one;
+	ctlr->setup = spi_mux_setup;
+	ctlr->num_chipselect = of_get_available_child_count(np);
+	ctlr->bus_num = -1;
+
+	mux->gpios = gpios;
+	mux->ctlr = ctlr;
+	mux->parent_ctlr = parent_ctlr;
+	ret = of_property_read_u32(np, "spi-parent-cs",
+				   &mux->chip_select);
+	if (ret)
+		mux->chip_select = 0;
+
+	ctlr->dev.of_node = np;
+	ret = devm_spi_register_controller(&pdev->dev, ctlr);
+	if (ret) {
+		dev_err(&pdev->dev, "Error: failed to register SPI bus %pOF %d\n",
+			np, ret);
+		goto err_put_ctlr;
+	}
+
+	return 0;
+
+err_put_ctlr:
+	spi_controller_put(ctlr);
+err_put_device:
+	put_device(&parent_ctlr->dev);
+err_put_node:
+	of_node_put(parent);
+
+	return ret;
+}
+
+static int spi_mux_gpio_remove(struct platform_device *pdev)
+{
+	struct spi_mux_gpio *mux = platform_get_drvdata(pdev);
+
+	spi_controller_put(mux->ctlr);
+	put_device(&mux->parent_ctlr->dev);
+
+	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 platform_driver spi_mux_gpio_driver = {
+	.probe	= spi_mux_gpio_probe,
+	.remove	= spi_mux_gpio_remove,
+	.driver	= {
+		.name	= "spi-mux-gpio",
+		.of_match_table = spi_mux_gpio_of_match,
+	},
+};
+
+module_platform_driver(spi_mux_gpio_driver);
+
+MODULE_DESCRIPTION("SPI bus mutliplexer driver");
+MODULE_AUTHOR("Allied Telesis");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:spi-mux-gpio");
-- 
2.21.0

  parent reply	other threads:[~2019-04-12  5:02 UTC|newest]

Thread overview: 9+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2019-04-12  5:02 [PATCH 0/3] spi: SPI bus multiplexer Chris Packham
2019-04-12  5:02 ` [PATCH 1/3] dt-bindings: spi: Add spi-mux-gpio Chris Packham
2019-04-29 21:37   ` Rob Herring
2020-01-13  3:43     ` Chris Packham
2019-04-12  5:02 ` [PATCH 2/3] spi: Make of_find_spi_controller_by_node visible Chris Packham
2019-04-12  5:02 ` Chris Packham [this message]
2019-04-12  8:29 ` [PATCH 0/3] spi: SPI bus multiplexer Mark Brown
2019-04-28 22:28   ` Chris Packham
2019-05-02  2:45     ` Mark Brown

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20190412050213.17698-4-chris.packham@alliedtelesis.co.nz \
    --to=chris.packham@alliedtelesis.co.nz \
    --cc=broonie@kernel.org \
    --cc=devicetree@vger.kernel.org \
    --cc=hamish.martin@alliedtelesis.co.nz \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-spi@vger.kernel.org \
    --cc=mark.rutland@arm.com \
    --cc=robh+dt@kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
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).