aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/gpio/gpio-tb10x.c
diff options
context:
space:
mode:
authorChristian Ruppert <christian.ruppert@abilis.com>2013-10-08 08:25:22 -0400
committerLinus Walleij <linus.walleij@linaro.org>2013-10-25 17:42:34 -0400
commitc6ce2b6bffe5740d572fdc5b5e690d5261abee51 (patch)
tree65355784311db2401eefb232c55bbd47770d6a30 /drivers/gpio/gpio-tb10x.c
parent61e310a1edf6f325939245c05d3e9b662c70a43d (diff)
gpio: add TB10x GPIO driver
The GPIO driver for the Abilis Systems TB10x series of SOCs based on ARC700 CPUs. It supports GPIO control and GPIO interrupt generation. This driver works in conjunction with the TB10x pinctrl driver. Signed-off-by: Sascha Leuenberger <sascha.leuenberger@abilis.com> Signed-off-by: Christian Ruppert <christian.ruppert@abilis.com> Acked-by: Kumar Gala <galak@codeaurora.org> Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
Diffstat (limited to 'drivers/gpio/gpio-tb10x.c')
-rw-r--r--drivers/gpio/gpio-tb10x.c341
1 files changed, 341 insertions, 0 deletions
diff --git a/drivers/gpio/gpio-tb10x.c b/drivers/gpio/gpio-tb10x.c
new file mode 100644
index 000000000000..833d0f40d40e
--- /dev/null
+++ b/drivers/gpio/gpio-tb10x.c
@@ -0,0 +1,341 @@
1/* Abilis Systems MODULE DESCRIPTION
2 *
3 * Copyright (C) Abilis Systems 2013
4 *
5 * Authors: Sascha Leuenberger <sascha.leuenberger@abilis.com>
6 * Christian Ruppert <christian.ruppert@abilis.com>
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License version 2 as
10 * published by the Free Software Foundation.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 */
21
22#include <linux/kernel.h>
23#include <linux/module.h>
24#include <linux/platform_device.h>
25#include <linux/gpio.h>
26#include <linux/slab.h>
27#include <linux/irq.h>
28#include <linux/irqdomain.h>
29#include <linux/interrupt.h>
30#include <linux/io.h>
31#include <linux/of.h>
32#include <linux/of_platform.h>
33#include <linux/of_gpio.h>
34#include <linux/spinlock.h>
35#include <linux/bitops.h>
36#include <linux/pinctrl/consumer.h>
37
38#define TB10X_GPIO_DIR_IN (0x00000000)
39#define TB10X_GPIO_DIR_OUT (0x00000001)
40#define OFFSET_TO_REG_DDR (0x00)
41#define OFFSET_TO_REG_DATA (0x04)
42#define OFFSET_TO_REG_INT_EN (0x08)
43#define OFFSET_TO_REG_CHANGE (0x0C)
44#define OFFSET_TO_REG_WRMASK (0x10)
45#define OFFSET_TO_REG_INT_TYPE (0x14)
46
47
48/**
49 * @spinlock: used for atomic read/modify/write of registers
50 * @base: register base address
51 * @domain: IRQ domain of GPIO generated interrupts managed by this controller
52 * @irq: Interrupt line of parent interrupt controller
53 * @gc: gpio_chip structure associated to this GPIO controller
54 */
55struct tb10x_gpio {
56 spinlock_t spinlock;
57 void __iomem *base;
58 struct irq_domain *domain;
59 int irq;
60 struct gpio_chip gc;
61};
62
63static inline u32 tb10x_reg_read(struct tb10x_gpio *gpio, unsigned int offs)
64{
65 return ioread32(gpio->base + offs);
66}
67
68static inline void tb10x_reg_write(struct tb10x_gpio *gpio, unsigned int offs,
69 u32 val)
70{
71 iowrite32(val, gpio->base + offs);
72}
73
74static inline void tb10x_set_bits(struct tb10x_gpio *gpio, unsigned int offs,
75 u32 mask, u32 val)
76{
77 u32 r;
78 unsigned long flags;
79
80 spin_lock_irqsave(&gpio->spinlock, flags);
81
82 r = tb10x_reg_read(gpio, offs);
83 r = (r & ~mask) | (val & mask);
84
85 tb10x_reg_write(gpio, offs, r);
86
87 spin_unlock_irqrestore(&gpio->spinlock, flags);
88}
89
90static inline struct tb10x_gpio *to_tb10x_gpio(struct gpio_chip *chip)
91{
92 return container_of(chip, struct tb10x_gpio, gc);
93}
94
95static int tb10x_gpio_direction_in(struct gpio_chip *chip, unsigned offset)
96{
97 struct tb10x_gpio *tb10x_gpio = to_tb10x_gpio(chip);
98 int mask = BIT(offset);
99 int val = TB10X_GPIO_DIR_IN << offset;
100
101 tb10x_set_bits(tb10x_gpio, OFFSET_TO_REG_DDR, mask, val);
102
103 return 0;
104}
105
106static int tb10x_gpio_get(struct gpio_chip *chip, unsigned offset)
107{
108 struct tb10x_gpio *tb10x_gpio = to_tb10x_gpio(chip);
109 int val;
110
111 val = tb10x_reg_read(tb10x_gpio, OFFSET_TO_REG_DATA);
112
113 if (val & BIT(offset))
114 return 1;
115 else
116 return 0;
117}
118
119static void tb10x_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
120{
121 struct tb10x_gpio *tb10x_gpio = to_tb10x_gpio(chip);
122 int mask = BIT(offset);
123 int val = value << offset;
124
125 tb10x_set_bits(tb10x_gpio, OFFSET_TO_REG_DATA, mask, val);
126}
127
128static int tb10x_gpio_direction_out(struct gpio_chip *chip,
129 unsigned offset, int value)
130{
131 struct tb10x_gpio *tb10x_gpio = to_tb10x_gpio(chip);
132 int mask = BIT(offset);
133 int val = TB10X_GPIO_DIR_OUT << offset;
134
135 tb10x_set_bits(tb10x_gpio, OFFSET_TO_REG_DDR, mask, val);
136
137 return 0;
138}
139
140static int tb10x_gpio_request(struct gpio_chip *chip, unsigned offset)
141{
142 return pinctrl_request_gpio(chip->base + offset);
143}
144
145static void tb10x_gpio_free(struct gpio_chip *chip, unsigned offset)
146{
147 pinctrl_free_gpio(chip->base + offset);
148}
149
150static int tb10x_gpio_to_irq(struct gpio_chip *chip, unsigned offset)
151{
152 struct tb10x_gpio *tb10x_gpio = to_tb10x_gpio(chip);
153
154 return irq_create_mapping(tb10x_gpio->domain, offset);
155}
156
157static int tb10x_gpio_irq_set_type(struct irq_data *data, unsigned int type)
158{
159 if ((type & IRQF_TRIGGER_MASK) != IRQ_TYPE_EDGE_BOTH) {
160 pr_err("Only (both) edge triggered interrupts supported.\n");
161 return -EINVAL;
162 }
163
164 irqd_set_trigger_type(data, type);
165
166 return IRQ_SET_MASK_OK;
167}
168
169static irqreturn_t tb10x_gpio_irq_cascade(int irq, void *data)
170{
171 struct tb10x_gpio *tb10x_gpio = data;
172 u32 r = tb10x_reg_read(tb10x_gpio, OFFSET_TO_REG_CHANGE);
173 u32 m = tb10x_reg_read(tb10x_gpio, OFFSET_TO_REG_INT_EN);
174 const unsigned long bits = r & m;
175 int i;
176
177 for_each_set_bit(i, &bits, 32)
178 generic_handle_irq(irq_find_mapping(tb10x_gpio->domain, i));
179
180 return IRQ_HANDLED;
181}
182
183static int tb10x_gpio_probe(struct platform_device *pdev)
184{
185 struct tb10x_gpio *tb10x_gpio;
186 struct resource *mem;
187 struct device_node *dn = pdev->dev.of_node;
188 int ret = -EBUSY;
189 u32 ngpio;
190
191 if (!dn)
192 return -EINVAL;
193
194 if (of_property_read_u32(dn, "abilis,ngpio", &ngpio))
195 return -EINVAL;
196
197 mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
198 if (!mem) {
199 dev_err(&pdev->dev, "No memory resource defined.\n");
200 return -EINVAL;
201 }
202
203 tb10x_gpio = devm_kzalloc(&pdev->dev, sizeof(*tb10x_gpio), GFP_KERNEL);
204 if (tb10x_gpio == NULL)
205 return -ENOMEM;
206
207 spin_lock_init(&tb10x_gpio->spinlock);
208
209 tb10x_gpio->base = devm_ioremap_resource(&pdev->dev, mem);
210 if (!tb10x_gpio->base) {
211 dev_err(&pdev->dev, "Could not remap reg space.\n");
212 goto fail_ioremap;
213 }
214
215 tb10x_gpio->gc.label = of_node_full_name(dn);
216 tb10x_gpio->gc.dev = &pdev->dev;
217 tb10x_gpio->gc.owner = THIS_MODULE;
218 tb10x_gpio->gc.direction_input = tb10x_gpio_direction_in;
219 tb10x_gpio->gc.get = tb10x_gpio_get;
220 tb10x_gpio->gc.direction_output = tb10x_gpio_direction_out;
221 tb10x_gpio->gc.set = tb10x_gpio_set;
222 tb10x_gpio->gc.request = tb10x_gpio_request;
223 tb10x_gpio->gc.free = tb10x_gpio_free;
224 tb10x_gpio->gc.base = -1;
225 tb10x_gpio->gc.ngpio = ngpio;
226 tb10x_gpio->gc.can_sleep = 0;
227
228
229 ret = gpiochip_add(&tb10x_gpio->gc);
230 if (ret < 0) {
231 dev_err(&pdev->dev, "Could not add gpiochip.\n");
232 goto fail_gpiochip_registration;
233 }
234
235 platform_set_drvdata(pdev, tb10x_gpio);
236
237 if (of_find_property(dn, "interrupt-controller", NULL)) {
238 struct irq_chip_generic *gc;
239
240 ret = platform_get_irq(pdev, 0);
241 if (ret < 0) {
242 dev_err(&pdev->dev, "No interrupt specified.\n");
243 goto fail_get_irq;
244 }
245
246 tb10x_gpio->gc.to_irq = tb10x_gpio_to_irq;
247 tb10x_gpio->irq = ret;
248
249 ret = devm_request_irq(&pdev->dev, ret, tb10x_gpio_irq_cascade,
250 IRQF_TRIGGER_NONE | IRQF_SHARED,
251 dev_name(&pdev->dev), tb10x_gpio);
252 if (ret != 0)
253 goto fail_request_irq;
254
255 tb10x_gpio->domain = irq_domain_add_linear(dn,
256 tb10x_gpio->gc.ngpio,
257 &irq_generic_chip_ops, NULL);
258 if (!tb10x_gpio->domain) {
259 ret = -ENOMEM;
260 goto fail_irq_domain;
261 }
262
263 ret = irq_alloc_domain_generic_chips(tb10x_gpio->domain,
264 tb10x_gpio->gc.ngpio, 1, tb10x_gpio->gc.label,
265 handle_edge_irq, IRQ_NOREQUEST, IRQ_NOPROBE,
266 IRQ_GC_INIT_MASK_CACHE);
267 if (ret)
268 goto fail_irq_domain;
269
270 gc = tb10x_gpio->domain->gc->gc[0];
271 gc->reg_base = tb10x_gpio->base;
272 gc->chip_types[0].type = IRQ_TYPE_EDGE_BOTH;
273 gc->chip_types[0].chip.irq_ack = irq_gc_ack_set_bit;
274 gc->chip_types[0].chip.irq_mask = irq_gc_mask_clr_bit;
275 gc->chip_types[0].chip.irq_unmask = irq_gc_mask_set_bit;
276 gc->chip_types[0].chip.irq_set_type = tb10x_gpio_irq_set_type;
277 gc->chip_types[0].regs.ack = OFFSET_TO_REG_CHANGE;
278 gc->chip_types[0].regs.mask = OFFSET_TO_REG_INT_EN;
279 }
280
281 return 0;
282
283fail_irq_domain:
284fail_request_irq:
285fail_get_irq:
286 gpiochip_remove(&tb10x_gpio->gc);
287fail_gpiochip_registration:
288fail_ioremap:
289 return ret;
290}
291
292static int __exit tb10x_gpio_remove(struct platform_device *pdev)
293{
294 struct tb10x_gpio *tb10x_gpio = platform_get_drvdata(pdev);
295 int ret;
296
297 if (tb10x_gpio->gc.to_irq) {
298 irq_remove_generic_chip(tb10x_gpio->domain->gc->gc[0],
299 BIT(tb10x_gpio->gc.ngpio) - 1, 0, 0);
300 kfree(tb10x_gpio->domain->gc);
301 irq_domain_remove(tb10x_gpio->domain);
302 free_irq(tb10x_gpio->irq, tb10x_gpio);
303 }
304 ret = gpiochip_remove(&tb10x_gpio->gc);
305 if (ret)
306 return ret;
307
308 return 0;
309}
310
311static const struct of_device_id tb10x_gpio_dt_ids[] = {
312 { .compatible = "abilis,tb10x-gpio" },
313 { }
314};
315MODULE_DEVICE_TABLE(of, tb10x_gpio_dt_ids);
316
317static struct platform_driver tb10x_gpio_driver = {
318 .probe = tb10x_gpio_probe,
319 .remove = tb10x_gpio_remove,
320 .driver = {
321 .name = "tb10x-gpio",
322 .of_match_table = of_match_ptr(tb10x_gpio_dt_ids),
323 .owner = THIS_MODULE,
324 }
325};
326
327static int __init ab_gpio_init(void)
328{
329 return platform_driver_register(&tb10x_gpio_driver);
330}
331
332static void __exit ab_gpio_exit(void)
333{
334 platform_driver_unregister(&tb10x_gpio_driver);
335}
336
337module_init(ab_gpio_init);
338module_exit(ab_gpio_exit);
339MODULE_LICENSE("GPL");
340MODULE_DESCRIPTION("tb10x gpio.");
341MODULE_VERSION("0.0.1");