diff options
author | Uwe Kleine-König <u.kleine-koenig@pengutronix.de> | 2018-09-27 03:48:08 -0400 |
---|---|---|
committer | Linus Walleij <linus.walleij@linaro.org> | 2018-09-28 03:30:42 -0400 |
commit | be8c8facc707d48f367d9313f5972353d24f19be (patch) | |
tree | 97cc0403cf11968d50a68ab53dfcacfdb8235ce4 | |
parent | 9aa64f0be3c8681c66f4a9e3b6ab0c79a5e8f3ac (diff) |
gpio: new driver to work with a 8x12 siox
This driver controls a SIOX device that provides 20 I/O lines. The first
twelve are fixed inputs, the remaining eight are outputs.
Acked-by: Gavin Schenk <g.schenk@eckelmann.de>
Signed-off-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de>
Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
-rw-r--r-- | MAINTAINERS | 1 | ||||
-rw-r--r-- | drivers/gpio/Kconfig | 8 | ||||
-rw-r--r-- | drivers/gpio/Makefile | 1 | ||||
-rw-r--r-- | drivers/gpio/gpio-siox.c | 293 |
4 files changed, 303 insertions, 0 deletions
diff --git a/MAINTAINERS b/MAINTAINERS index 0937f2131f73..920c0f92bef4 100644 --- a/MAINTAINERS +++ b/MAINTAINERS | |||
@@ -13289,6 +13289,7 @@ M: Uwe Kleine-König <u.kleine-koenig@pengutronix.de> | |||
13289 | R: Pengutronix Kernel Team <kernel@pengutronix.de> | 13289 | R: Pengutronix Kernel Team <kernel@pengutronix.de> |
13290 | S: Supported | 13290 | S: Supported |
13291 | F: drivers/siox/* | 13291 | F: drivers/siox/* |
13292 | F: drivers/gpio/gpio-siox.c | ||
13292 | F: include/trace/events/siox.h | 13293 | F: include/trace/events/siox.h |
13293 | 13294 | ||
13294 | SIS 190 ETHERNET DRIVER | 13295 | SIS 190 ETHERNET DRIVER |
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index b8cbf5081389..88c25677eeff 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig | |||
@@ -429,6 +429,14 @@ config GPIO_REG | |||
429 | A 32-bit single register GPIO fixed in/out implementation. This | 429 | A 32-bit single register GPIO fixed in/out implementation. This |
430 | can be used to represent any register as a set of GPIO signals. | 430 | can be used to represent any register as a set of GPIO signals. |
431 | 431 | ||
432 | config GPIO_SIOX | ||
433 | tristate "SIOX GPIO support" | ||
434 | depends on SIOX | ||
435 | select GPIOLIB_IRQCHIP | ||
436 | help | ||
437 | Say yes here to support SIOX I/O devices. These are units connected | ||
438 | via a SIOX bus and have a number of fixed-direction I/O lines. | ||
439 | |||
432 | config GPIO_SPEAR_SPICS | 440 | config GPIO_SPEAR_SPICS |
433 | bool "ST SPEAr13xx SPI Chip Select as GPIO support" | 441 | bool "ST SPEAr13xx SPI Chip Select as GPIO support" |
434 | depends on PLAT_SPEAR | 442 | depends on PLAT_SPEAR |
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index f3a2efbf858b..e85e770045fb 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile | |||
@@ -124,6 +124,7 @@ obj-$(CONFIG_GPIO_TEGRA186) += gpio-tegra186.o | |||
124 | obj-$(CONFIG_GPIO_THUNDERX) += gpio-thunderx.o | 124 | obj-$(CONFIG_GPIO_THUNDERX) += gpio-thunderx.o |
125 | obj-$(CONFIG_GPIO_TIMBERDALE) += gpio-timberdale.o | 125 | obj-$(CONFIG_GPIO_TIMBERDALE) += gpio-timberdale.o |
126 | obj-$(CONFIG_GPIO_PALMAS) += gpio-palmas.o | 126 | obj-$(CONFIG_GPIO_PALMAS) += gpio-palmas.o |
127 | obj-$(CONFIG_GPIO_SIOX) += gpio-siox.o | ||
127 | obj-$(CONFIG_GPIO_TPIC2810) += gpio-tpic2810.o | 128 | obj-$(CONFIG_GPIO_TPIC2810) += gpio-tpic2810.o |
128 | obj-$(CONFIG_GPIO_TPS65086) += gpio-tps65086.o | 129 | obj-$(CONFIG_GPIO_TPS65086) += gpio-tps65086.o |
129 | obj-$(CONFIG_GPIO_TPS65218) += gpio-tps65218.o | 130 | obj-$(CONFIG_GPIO_TPS65218) += gpio-tps65218.o |
diff --git a/drivers/gpio/gpio-siox.c b/drivers/gpio/gpio-siox.c new file mode 100644 index 000000000000..571b2a81c6de --- /dev/null +++ b/drivers/gpio/gpio-siox.c | |||
@@ -0,0 +1,293 @@ | |||
1 | // SPDX-License-Identifier: GPL-2.0 | ||
2 | /* | ||
3 | * Copyright (C) 2015-2018 Pengutronix, Uwe Kleine-König <kernel@pengutronix.de> | ||
4 | */ | ||
5 | |||
6 | #include <linux/module.h> | ||
7 | #include <linux/siox.h> | ||
8 | #include <linux/gpio/driver.h> | ||
9 | #include <linux/of.h> | ||
10 | |||
11 | struct gpio_siox_ddata { | ||
12 | struct gpio_chip gchip; | ||
13 | struct irq_chip ichip; | ||
14 | struct mutex lock; | ||
15 | u8 setdata[1]; | ||
16 | u8 getdata[3]; | ||
17 | |||
18 | spinlock_t irqlock; | ||
19 | u32 irq_enable; | ||
20 | u32 irq_status; | ||
21 | u32 irq_type[20]; | ||
22 | }; | ||
23 | |||
24 | /* | ||
25 | * Note that this callback only sets the value that is clocked out in the next | ||
26 | * cycle. | ||
27 | */ | ||
28 | static int gpio_siox_set_data(struct siox_device *sdevice, u8 status, u8 buf[]) | ||
29 | { | ||
30 | struct gpio_siox_ddata *ddata = dev_get_drvdata(&sdevice->dev); | ||
31 | |||
32 | mutex_lock(&ddata->lock); | ||
33 | buf[0] = ddata->setdata[0]; | ||
34 | mutex_unlock(&ddata->lock); | ||
35 | |||
36 | return 0; | ||
37 | } | ||
38 | |||
39 | static int gpio_siox_get_data(struct siox_device *sdevice, const u8 buf[]) | ||
40 | { | ||
41 | struct gpio_siox_ddata *ddata = dev_get_drvdata(&sdevice->dev); | ||
42 | size_t offset; | ||
43 | u32 trigger; | ||
44 | |||
45 | mutex_lock(&ddata->lock); | ||
46 | |||
47 | spin_lock_irq(&ddata->irqlock); | ||
48 | |||
49 | for (offset = 0; offset < 12; ++offset) { | ||
50 | unsigned int bitpos = 11 - offset; | ||
51 | unsigned int gpiolevel = buf[bitpos / 8] & (1 << bitpos % 8); | ||
52 | unsigned int prev_level = | ||
53 | ddata->getdata[bitpos / 8] & (1 << (bitpos % 8)); | ||
54 | u32 irq_type = ddata->irq_type[offset]; | ||
55 | |||
56 | if (gpiolevel) { | ||
57 | if ((irq_type & IRQ_TYPE_LEVEL_HIGH) || | ||
58 | ((irq_type & IRQ_TYPE_EDGE_RISING) && !prev_level)) | ||
59 | ddata->irq_status |= 1 << offset; | ||
60 | } else { | ||
61 | if ((irq_type & IRQ_TYPE_LEVEL_LOW) || | ||
62 | ((irq_type & IRQ_TYPE_EDGE_FALLING) && prev_level)) | ||
63 | ddata->irq_status |= 1 << offset; | ||
64 | } | ||
65 | } | ||
66 | |||
67 | trigger = ddata->irq_status & ddata->irq_enable; | ||
68 | |||
69 | spin_unlock_irq(&ddata->irqlock); | ||
70 | |||
71 | ddata->getdata[0] = buf[0]; | ||
72 | ddata->getdata[1] = buf[1]; | ||
73 | ddata->getdata[2] = buf[2]; | ||
74 | |||
75 | mutex_unlock(&ddata->lock); | ||
76 | |||
77 | for (offset = 0; offset < 12; ++offset) { | ||
78 | if (trigger & (1 << offset)) { | ||
79 | struct irq_domain *irqdomain = ddata->gchip.irq.domain; | ||
80 | unsigned int irq = irq_find_mapping(irqdomain, offset); | ||
81 | |||
82 | /* | ||
83 | * Conceptually handle_nested_irq should call the flow | ||
84 | * handler of the irq chip. But it doesn't, so we have | ||
85 | * to clean the irq_status here. | ||
86 | */ | ||
87 | spin_lock_irq(&ddata->irqlock); | ||
88 | ddata->irq_status &= ~(1 << offset); | ||
89 | spin_unlock_irq(&ddata->irqlock); | ||
90 | |||
91 | handle_nested_irq(irq); | ||
92 | } | ||
93 | } | ||
94 | |||
95 | return 0; | ||
96 | } | ||
97 | |||
98 | static void gpio_siox_irq_ack(struct irq_data *d) | ||
99 | { | ||
100 | struct irq_chip *ic = irq_data_get_irq_chip(d); | ||
101 | struct gpio_siox_ddata *ddata = | ||
102 | container_of(ic, struct gpio_siox_ddata, ichip); | ||
103 | |||
104 | spin_lock_irq(&ddata->irqlock); | ||
105 | ddata->irq_status &= ~(1 << d->hwirq); | ||
106 | spin_unlock_irq(&ddata->irqlock); | ||
107 | } | ||
108 | |||
109 | static void gpio_siox_irq_mask(struct irq_data *d) | ||
110 | { | ||
111 | struct irq_chip *ic = irq_data_get_irq_chip(d); | ||
112 | struct gpio_siox_ddata *ddata = | ||
113 | container_of(ic, struct gpio_siox_ddata, ichip); | ||
114 | |||
115 | spin_lock_irq(&ddata->irqlock); | ||
116 | ddata->irq_enable &= ~(1 << d->hwirq); | ||
117 | spin_unlock_irq(&ddata->irqlock); | ||
118 | } | ||
119 | |||
120 | static void gpio_siox_irq_unmask(struct irq_data *d) | ||
121 | { | ||
122 | struct irq_chip *ic = irq_data_get_irq_chip(d); | ||
123 | struct gpio_siox_ddata *ddata = | ||
124 | container_of(ic, struct gpio_siox_ddata, ichip); | ||
125 | |||
126 | spin_lock_irq(&ddata->irqlock); | ||
127 | ddata->irq_enable |= 1 << d->hwirq; | ||
128 | spin_unlock_irq(&ddata->irqlock); | ||
129 | } | ||
130 | |||
131 | static int gpio_siox_irq_set_type(struct irq_data *d, u32 type) | ||
132 | { | ||
133 | struct irq_chip *ic = irq_data_get_irq_chip(d); | ||
134 | struct gpio_siox_ddata *ddata = | ||
135 | container_of(ic, struct gpio_siox_ddata, ichip); | ||
136 | |||
137 | spin_lock_irq(&ddata->irqlock); | ||
138 | ddata->irq_type[d->hwirq] = type; | ||
139 | spin_unlock_irq(&ddata->irqlock); | ||
140 | |||
141 | return 0; | ||
142 | } | ||
143 | |||
144 | static int gpio_siox_get(struct gpio_chip *chip, unsigned int offset) | ||
145 | { | ||
146 | struct gpio_siox_ddata *ddata = | ||
147 | container_of(chip, struct gpio_siox_ddata, gchip); | ||
148 | int ret; | ||
149 | |||
150 | mutex_lock(&ddata->lock); | ||
151 | |||
152 | if (offset >= 12) { | ||
153 | unsigned int bitpos = 19 - offset; | ||
154 | |||
155 | ret = ddata->setdata[0] & (1 << bitpos); | ||
156 | } else { | ||
157 | unsigned int bitpos = 11 - offset; | ||
158 | |||
159 | ret = ddata->getdata[bitpos / 8] & (1 << (bitpos % 8)); | ||
160 | } | ||
161 | |||
162 | mutex_unlock(&ddata->lock); | ||
163 | |||
164 | return ret; | ||
165 | } | ||
166 | |||
167 | static void gpio_siox_set(struct gpio_chip *chip, | ||
168 | unsigned int offset, int value) | ||
169 | { | ||
170 | struct gpio_siox_ddata *ddata = | ||
171 | container_of(chip, struct gpio_siox_ddata, gchip); | ||
172 | u8 mask = 1 << (19 - offset); | ||
173 | |||
174 | mutex_lock(&ddata->lock); | ||
175 | |||
176 | if (value) | ||
177 | ddata->setdata[0] |= mask; | ||
178 | else | ||
179 | ddata->setdata[0] &= ~mask; | ||
180 | |||
181 | mutex_unlock(&ddata->lock); | ||
182 | } | ||
183 | |||
184 | static int gpio_siox_direction_input(struct gpio_chip *chip, | ||
185 | unsigned int offset) | ||
186 | { | ||
187 | if (offset >= 12) | ||
188 | return -EINVAL; | ||
189 | |||
190 | return 0; | ||
191 | } | ||
192 | |||
193 | static int gpio_siox_direction_output(struct gpio_chip *chip, | ||
194 | unsigned int offset, int value) | ||
195 | { | ||
196 | if (offset < 12) | ||
197 | return -EINVAL; | ||
198 | |||
199 | gpio_siox_set(chip, offset, value); | ||
200 | return 0; | ||
201 | } | ||
202 | |||
203 | static int gpio_siox_get_direction(struct gpio_chip *chip, unsigned int offset) | ||
204 | { | ||
205 | if (offset < 12) | ||
206 | return 1; /* input */ | ||
207 | else | ||
208 | return 0; /* output */ | ||
209 | } | ||
210 | |||
211 | static int gpio_siox_probe(struct siox_device *sdevice) | ||
212 | { | ||
213 | struct gpio_siox_ddata *ddata; | ||
214 | int ret; | ||
215 | |||
216 | ddata = devm_kzalloc(&sdevice->dev, sizeof(*ddata), GFP_KERNEL); | ||
217 | if (!ddata) | ||
218 | return -ENOMEM; | ||
219 | |||
220 | dev_set_drvdata(&sdevice->dev, ddata); | ||
221 | |||
222 | mutex_init(&ddata->lock); | ||
223 | spin_lock_init(&ddata->irqlock); | ||
224 | |||
225 | ddata->gchip.base = -1; | ||
226 | ddata->gchip.can_sleep = 1; | ||
227 | ddata->gchip.parent = &sdevice->dev; | ||
228 | ddata->gchip.owner = THIS_MODULE; | ||
229 | ddata->gchip.get = gpio_siox_get; | ||
230 | ddata->gchip.set = gpio_siox_set; | ||
231 | ddata->gchip.direction_input = gpio_siox_direction_input; | ||
232 | ddata->gchip.direction_output = gpio_siox_direction_output; | ||
233 | ddata->gchip.get_direction = gpio_siox_get_direction; | ||
234 | ddata->gchip.ngpio = 20; | ||
235 | |||
236 | ddata->ichip.name = "siox-gpio"; | ||
237 | ddata->ichip.irq_ack = gpio_siox_irq_ack; | ||
238 | ddata->ichip.irq_mask = gpio_siox_irq_mask; | ||
239 | ddata->ichip.irq_unmask = gpio_siox_irq_unmask; | ||
240 | ddata->ichip.irq_set_type = gpio_siox_irq_set_type; | ||
241 | |||
242 | ret = gpiochip_add(&ddata->gchip); | ||
243 | if (ret) { | ||
244 | dev_err(&sdevice->dev, | ||
245 | "Failed to register gpio chip (%d)\n", ret); | ||
246 | goto err_gpiochip; | ||
247 | } | ||
248 | |||
249 | ret = gpiochip_irqchip_add(&ddata->gchip, &ddata->ichip, | ||
250 | 0, handle_level_irq, IRQ_TYPE_EDGE_RISING); | ||
251 | if (ret) { | ||
252 | dev_err(&sdevice->dev, | ||
253 | "Failed to register irq chip (%d)\n", ret); | ||
254 | err_gpiochip: | ||
255 | gpiochip_remove(&ddata->gchip); | ||
256 | } | ||
257 | |||
258 | return ret; | ||
259 | } | ||
260 | |||
261 | static int gpio_siox_remove(struct siox_device *sdevice) | ||
262 | { | ||
263 | struct gpio_siox_ddata *ddata = dev_get_drvdata(&sdevice->dev); | ||
264 | |||
265 | gpiochip_remove(&ddata->gchip); | ||
266 | return 0; | ||
267 | } | ||
268 | |||
269 | static struct siox_driver gpio_siox_driver = { | ||
270 | .probe = gpio_siox_probe, | ||
271 | .remove = gpio_siox_remove, | ||
272 | .set_data = gpio_siox_set_data, | ||
273 | .get_data = gpio_siox_get_data, | ||
274 | .driver = { | ||
275 | .name = "gpio-siox", | ||
276 | }, | ||
277 | }; | ||
278 | |||
279 | static int __init gpio_siox_init(void) | ||
280 | { | ||
281 | return siox_driver_register(&gpio_siox_driver); | ||
282 | } | ||
283 | module_init(gpio_siox_init); | ||
284 | |||
285 | static void __exit gpio_siox_exit(void) | ||
286 | { | ||
287 | siox_driver_unregister(&gpio_siox_driver); | ||
288 | } | ||
289 | module_exit(gpio_siox_exit); | ||
290 | |||
291 | MODULE_AUTHOR("Uwe Kleine-Koenig <u.kleine-koenig@pengutronix.de>"); | ||
292 | MODULE_DESCRIPTION("SIOX gpio driver"); | ||
293 | MODULE_LICENSE("GPL v2"); | ||