aboutsummaryrefslogtreecommitdiff
path: root/nixos/modules/kernel-patches/0077-gpio-gpio-cascade-add-generic-GPIO-cascade.patch
blob: 98dfc249c37a74616e00646eff63c90d74c5beac (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
From f5105edd6729159f332579da5416aa3a26c84ad6 Mon Sep 17 00:00:00 2001
From: Mauri Sandberg <maukka@ext.kapsi.fi>
Date: Thu, 25 Mar 2021 11:48:05 +0200
Subject: [PATCH 77/96] gpio: gpio-cascade: add generic GPIO cascade

Adds support for building cascades of GPIO lines. That is, it allows
setups when there is one upstream line and multiple cascaded lines, out
of which one can be chosen at a time. The status of the upstream line
can be conveyed to the selected cascaded line or, vice versa, the status
of the cascaded line can be conveyed to the upstream line.

A multiplexer is being used to select, which cascaded GPIO line is being
used at any given time.

At the moment only input direction is supported. In future it should be
possible to add support for output direction, too.

Signed-off-by: Mauri Sandberg <maukka@ext.kapsi.fi>
Reviewed-by: Linus Walleij <linus.walleij@linaro.org>
Reviewed-by: Andy Shevchenko <andy.shevchenko@gmail.com>
---
 drivers/gpio/Kconfig        |  15 +++++
 drivers/gpio/Makefile       |   1 +
 drivers/gpio/gpio-cascade.c | 117 ++++++++++++++++++++++++++++++++++++
 3 files changed, 133 insertions(+)
 create mode 100644 drivers/gpio/gpio-cascade.c

diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index b01961999ced..b88980948950 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -1710,4 +1710,19 @@ config GPIO_SIM
 
 endmenu
 
+comment "Other GPIO expanders"
+
+config GPIO_CASCADE
+	tristate "General GPIO cascade"
+	select MULTIPLEXER
+	help
+	  Say yes here to enable support for generic GPIO cascade.
+
+	  This allows building one-to-many cascades of GPIO lines using
+	  different types of multiplexers readily available. At the
+	  moment only input lines are supported.
+
+	  To build the driver as a module choose 'm' and the resulting module
+	  will be called 'gpio-cascade'.
+
 endif
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
index 14352f6dfe8e..561c060b0d9f 100644
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -44,6 +44,7 @@ obj-$(CONFIG_GPIO_BD9571MWV)		+= gpio-bd9571mwv.o
 obj-$(CONFIG_GPIO_BRCMSTB)		+= gpio-brcmstb.o
 obj-$(CONFIG_GPIO_BT8XX)		+= gpio-bt8xx.o
 obj-$(CONFIG_GPIO_CADENCE)		+= gpio-cadence.o
+obj-$(CONFIG_GPIO_CASCADE)		+= gpio-cascade.o
 obj-$(CONFIG_GPIO_CLPS711X)		+= gpio-clps711x.o
 obj-$(CONFIG_GPIO_SNPS_CREG)		+= gpio-creg-snps.o
 obj-$(CONFIG_GPIO_CRYSTAL_COVE)		+= gpio-crystalcove.o
diff --git a/drivers/gpio/gpio-cascade.c b/drivers/gpio/gpio-cascade.c
new file mode 100644
index 000000000000..5cbda882d79a
--- /dev/null
+++ b/drivers/gpio/gpio-cascade.c
@@ -0,0 +1,117 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ *  A generic GPIO cascade driver
+ *
+ *  Copyright (C) 2021 Mauri Sandberg <maukka@ext.kapsi.fi>
+ *
+ * This allows building cascades of GPIO lines in a manner illustrated
+ * below:
+ *
+ *                 /|---- Cascaded GPIO line 0
+ *  Upstream      | |---- Cascaded GPIO line 1
+ *  GPIO line ----+ | .
+ *                | | .
+ *                 \|---- Cascaded GPIO line n
+ *
+ * A multiplexer is being used to select, which cascaded line is being
+ * addressed at any given time.
+ *
+ * At the moment only input mode is supported due to lack of means for
+ * testing output functionality. At least theoretically output should be
+ * possible with open drain constructions.
+ */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/mux/consumer.h>
+
+#include <linux/gpio/consumer.h>
+#include <linux/gpio/driver.h>
+
+struct gpio_cascade {
+	struct gpio_chip	gpio_chip;
+	struct device		*parent;
+	struct mux_control	*mux_control;
+	struct gpio_desc	*upstream_line;
+};
+
+static struct gpio_cascade *chip_to_cascade(struct gpio_chip *gc)
+{
+	return container_of(gc, struct gpio_cascade, gpio_chip);
+}
+
+static int gpio_cascade_get_direction(struct gpio_chip *gc, unsigned int offset)
+{
+	return GPIO_LINE_DIRECTION_IN;
+}
+
+static int gpio_cascade_get_value(struct gpio_chip *gc, unsigned int offset)
+{
+	struct gpio_cascade *cas = chip_to_cascade(gc);
+	int ret;
+
+	ret = mux_control_select(cas->mux_control, offset);
+	if (ret)
+		return ret;
+
+	ret = gpiod_get_value(cas->upstream_line);
+	mux_control_deselect(cas->mux_control);
+	return ret;
+}
+
+static int gpio_cascade_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct gpio_cascade *cas;
+	struct mux_control *mc;
+	struct gpio_desc *upstream;
+	struct gpio_chip *gc;
+
+	cas = devm_kzalloc(dev, sizeof(*cas), GFP_KERNEL);
+	if (!cas)
+		return -ENOMEM;
+
+	mc = devm_mux_control_get(dev, NULL);
+	if (IS_ERR(mc))
+		return dev_err_probe(dev, PTR_ERR(mc), "unable to get mux-control\n");
+
+	cas->mux_control = mc;
+	upstream = devm_gpiod_get(dev, "upstream",  GPIOD_IN);
+	if (IS_ERR(upstream))
+		return dev_err_probe(dev, PTR_ERR(upstream), "unable to claim upstream GPIO line\n");
+
+	cas->upstream_line = upstream;
+	cas->parent = dev;
+
+	gc = &cas->gpio_chip;
+	gc->get = gpio_cascade_get_value;
+	gc->get_direction = gpio_cascade_get_direction;
+	gc->base = -1;
+	gc->ngpio = mux_control_states(mc);
+	gc->label = dev_name(cas->parent);
+	gc->parent = cas->parent;
+	gc->owner = THIS_MODULE;
+
+	platform_set_drvdata(pdev, cas);
+	return devm_gpiochip_add_data(dev, &cas->gpio_chip, NULL);
+}
+
+static const struct of_device_id gpio_cascade_id[] = {
+	{ .compatible = "gpio-cascade" },
+	{ /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, gpio_cascade_id);
+
+static struct platform_driver gpio_cascade_driver = {
+	.driver	= {
+		.name		= "gpio-cascade",
+		.of_match_table = gpio_cascade_id,
+	},
+	.probe	= gpio_cascade_probe,
+};
+module_platform_driver(gpio_cascade_driver);
+
+MODULE_AUTHOR("Mauri Sandberg <maukka@ext.kapsi.fi>");
+MODULE_DESCRIPTION("Generic GPIO cascade");
+MODULE_LICENSE("GPL");
-- 
2.37.2