[PATCH v4 2/2] gpio: gpio-mux-input: add generic gpio input multiplexer

From: Mauri Sandberg
Date: Sun May 30 2021 - 12:50:13 EST


Adds support for a generic GPIO multiplexer. To drive the multiplexer a
mux-controller is needed. The output pin of the multiplexer is a GPIO
pin.

Reported-by: kernel test robot <lkp@xxxxxxxxx>
Signed-off-by: Mauri Sandberg <maukka@xxxxxxxxxxxx>
Tested-by: Drew Fustini <drew@xxxxxxxxxxxxxxx>
Reviewed-by: Drew Fustini <drew@xxxxxxxxxxxxxxx>
---
v3 -> v4:
- Changed author email
- Included Tested-by and Reviewed-by from Drew
v2 -> v3:
- use managed device resources
- update Kconfig description
v1 -> v2:
- removed .owner from platform_driver as per test bot's instruction
- added MODULE_AUTHOR, MODULE_DESCRIPTION, MODULE_LICENSE
- added gpio_mux_input_get_direction as it's recommended for all chips
- removed because this is input only chip: gpio_mux_input_set_value
- removed because they are not needed for input/output only chips:
gpio_mux_input_direction_input
gpio_mux_input_direction_output
- fixed typo in an error message
- added info message about successful registration
- removed can_sleep flag as this does not sleep while getting GPIO value
like I2C or SPI do
- Updated description in Kconfig
---
drivers/gpio/Kconfig | 16 +++++
drivers/gpio/Makefile | 1 +
drivers/gpio/gpio-mux-input.c | 124 ++++++++++++++++++++++++++++++++++
3 files changed, 141 insertions(+)
create mode 100644 drivers/gpio/gpio-mux-input.c

diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index 1dd0ec6727fd..8a41a283ba42 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -1657,4 +1657,20 @@ config GPIO_MOCKUP

endmenu

+comment "Other GPIO expanders"
+
+config GPIO_MUX_INPUT
+ tristate "General GPIO input multiplexer"
+ depends on OF_GPIO
+ select MULTIPLEXER
+ select MUX_GPIO
+ help
+ Say yes here to enable support for generic GPIO input multiplexer.
+
+ This driver uses a mux-controller to drive the multiplexer and has a
+ single output pin for reading the inputs to the mux. The driver can
+ be used in situations when GPIO pins are used to select what
+ multiplexer pin should be used for reading input and the output pin
+ of the multiplexer is connected to a GPIO input pin.
+
endif
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
index d7c81e1611a4..ff2b530d8ef4 100644
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -104,6 +104,7 @@ obj-$(CONFIG_GPIO_MPC5200) += gpio-mpc5200.o
obj-$(CONFIG_GPIO_MPC8XXX) += gpio-mpc8xxx.o
obj-$(CONFIG_GPIO_MSC313) += gpio-msc313.o
obj-$(CONFIG_GPIO_MT7621) += gpio-mt7621.o
+obj-$(CONFIG_GPIO_MUX_INPUT) += gpio-mux-input.o
obj-$(CONFIG_GPIO_MVEBU) += gpio-mvebu.o
obj-$(CONFIG_GPIO_MXC) += gpio-mxc.o
obj-$(CONFIG_GPIO_MXS) += gpio-mxs.o
diff --git a/drivers/gpio/gpio-mux-input.c b/drivers/gpio/gpio-mux-input.c
new file mode 100644
index 000000000000..e0739640541e
--- /dev/null
+++ b/drivers/gpio/gpio-mux-input.c
@@ -0,0 +1,124 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * A generic GPIO input multiplexer driver
+ *
+ * Copyright (C) 2021 Mauri Sandberg <maukka@xxxxxxxxxxxx>
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/gpio/consumer.h>
+#include <linux/gpio/driver.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/mux/consumer.h>
+
+struct gpio_mux_input {
+ struct device *parent;
+ struct gpio_chip gpio_chip;
+ struct mux_control *mux_control;
+ struct gpio_desc *mux_pin;
+};
+
+static struct gpio_mux_input *gpio_to_mux(struct gpio_chip *gc)
+{
+ return container_of(gc, struct gpio_mux_input, gpio_chip);
+}
+
+static int gpio_mux_input_get_direction(struct gpio_chip *gc,
+ unsigned int offset)
+{
+ return GPIO_LINE_DIRECTION_IN;
+}
+
+static int gpio_mux_input_get_value(struct gpio_chip *gc, unsigned int offset)
+{
+ struct gpio_mux_input *mux;
+ int ret;
+
+ mux = gpio_to_mux(gc);
+ ret = mux_control_select(mux->mux_control, offset);
+ if (ret)
+ return ret;
+
+ ret = gpiod_get_value(mux->mux_pin);
+ mux_control_deselect(mux->mux_control);
+ return ret;
+}
+
+static int gpio_mux_input_probe(struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node;
+ struct device *dev = &pdev->dev;
+ struct gpio_mux_input *mux;
+ struct mux_control *mc;
+ struct gpio_desc *pin;
+ struct gpio_chip *gc;
+ int err;
+
+ mux = devm_kzalloc(dev, sizeof(struct gpio_mux_input), GFP_KERNEL);
+ if (mux == NULL)
+ return -ENOMEM;
+
+ mc = devm_mux_control_get(dev, NULL);
+ if (IS_ERR(mc)) {
+ err = (int) PTR_ERR(mc);
+ if (err != -EPROBE_DEFER)
+ dev_err(dev, "unable to get mux-control: %d\n", err);
+ return err;
+ }
+
+ mux->mux_control = mc;
+ pin = devm_gpiod_get(dev, "pin", GPIOD_IN);
+ if (IS_ERR(pin)) {
+ err = (int) PTR_ERR(pin);
+ dev_err(dev, "unable to claim output pin: %d\n", err);
+ return err;
+ }
+
+ mux->mux_pin = pin;
+ mux->parent = dev;
+
+ gc = &mux->gpio_chip;
+ gc->get = gpio_mux_input_get_value;
+ gc->get_direction = gpio_mux_input_get_direction;
+
+ gc->base = -1;
+ gc->ngpio = mux_control_states(mc);
+ gc->label = dev_name(mux->parent);
+ gc->parent = mux->parent;
+ gc->owner = THIS_MODULE;
+ gc->of_node = np;
+
+ err = gpiochip_add(&mux->gpio_chip);
+ if (err) {
+ dev_err(dev, "unable to add gpio chip, err=%d\n", err);
+ return err;
+ }
+
+ platform_set_drvdata(pdev, mux);
+ dev_info(dev, "registered %u input GPIOs\n", gc->ngpio);
+ return 0;
+}
+
+static const struct of_device_id gpio_mux_input_id[] = {
+ {
+ .compatible = "gpio-mux-input",
+ .data = NULL,
+ },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, gpio_mux_input_id);
+
+static struct platform_driver gpio_mux_input_driver = {
+ .driver = {
+ .name = "gpio-mux-input",
+ .of_match_table = gpio_mux_input_id,
+ },
+ .probe = gpio_mux_input_probe,
+};
+module_platform_driver(gpio_mux_input_driver);
+
+MODULE_AUTHOR("Mauri Sandberg <maukka@xxxxxxxxxxxx>");
+MODULE_DESCRIPTION("Generic GPIO input multiplexer");
+MODULE_LICENSE("GPL");
--
2.25.1