aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorStefan Agner <stefan@agner.ch>2014-10-16 15:47:58 -0400
committerLinus Walleij <linus.walleij@linaro.org>2014-10-24 08:18:05 -0400
commit7f2691a19627a8d7723909b6e82468fd4437e445 (patch)
tree858d901c3767e590a68618d1d5eff6d3014861a8
parent3eebd61322835565bbcb1745bf9bd44e86ff80ca (diff)
gpio: vf610: add gpiolib/IRQ chip driver for Vybrid
Add a gpiolib and IRQ chip driver for Vybrid ARM SoC using the Vybrid's GPIO and PORT module. The driver is instanced once per each GPIO/PORT module pair and handles 32 GPIO's. Signed-off-by: Stefan Agner <stefan@agner.ch> Acked-by: Shawn Guo <shawn.guo@freescale.com> Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
-rw-r--r--drivers/gpio/Kconfig7
-rw-r--r--drivers/gpio/Makefile1
-rw-r--r--drivers/gpio/gpio-vf610.c295
3 files changed, 303 insertions, 0 deletions
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index cd3313436b3a..690ad0b20414 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -347,6 +347,13 @@ config GPIO_TZ1090_PDC
347 help 347 help
348 Say yes here to support Toumaz Xenif TZ1090 PDC GPIOs. 348 Say yes here to support Toumaz Xenif TZ1090 PDC GPIOs.
349 349
350config GPIO_VF610
351 def_bool y
352 depends on ARCH_MXC && SOC_VF610
353 select GPIOLIB_IRQCHIP
354 help
355 Say yes here to support Vybrid vf610 GPIOs.
356
350config GPIO_XGENE 357config GPIO_XGENE
351 bool "APM X-Gene GPIO controller support" 358 bool "APM X-Gene GPIO controller support"
352 depends on ARM64 && OF_GPIO 359 depends on ARM64 && OF_GPIO
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
index 4486bbd2dad7..aaea751fa845 100644
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -96,6 +96,7 @@ obj-$(CONFIG_GPIO_TWL6040) += gpio-twl6040.o
96obj-$(CONFIG_GPIO_TZ1090) += gpio-tz1090.o 96obj-$(CONFIG_GPIO_TZ1090) += gpio-tz1090.o
97obj-$(CONFIG_GPIO_TZ1090_PDC) += gpio-tz1090-pdc.o 97obj-$(CONFIG_GPIO_TZ1090_PDC) += gpio-tz1090-pdc.o
98obj-$(CONFIG_GPIO_UCB1400) += gpio-ucb1400.o 98obj-$(CONFIG_GPIO_UCB1400) += gpio-ucb1400.o
99obj-$(CONFIG_GPIO_VF610) += gpio-vf610.o
99obj-$(CONFIG_GPIO_VIPERBOARD) += gpio-viperboard.o 100obj-$(CONFIG_GPIO_VIPERBOARD) += gpio-viperboard.o
100obj-$(CONFIG_GPIO_VR41XX) += gpio-vr41xx.o 101obj-$(CONFIG_GPIO_VR41XX) += gpio-vr41xx.o
101obj-$(CONFIG_GPIO_VX855) += gpio-vx855.o 102obj-$(CONFIG_GPIO_VX855) += gpio-vx855.o
diff --git a/drivers/gpio/gpio-vf610.c b/drivers/gpio/gpio-vf610.c
new file mode 100644
index 000000000000..4ee4cee832ec
--- /dev/null
+++ b/drivers/gpio/gpio-vf610.c
@@ -0,0 +1,295 @@
1/*
2 * vf610 GPIO support through PORT and GPIO module
3 *
4 * Copyright (c) 2014 Toradex AG.
5 *
6 * Author: Stefan Agner <stefan@agner.ch>.
7 *
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License
10 * as published by the Free Software Foundation; either version 2
11 * of the License, or (at your option) any later version.
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
18#include <linux/bitops.h>
19#include <linux/err.h>
20#include <linux/gpio.h>
21#include <linux/init.h>
22#include <linux/interrupt.h>
23#include <linux/io.h>
24#include <linux/ioport.h>
25#include <linux/irq.h>
26#include <linux/module.h>
27#include <linux/platform_device.h>
28#include <linux/of.h>
29#include <linux/of_device.h>
30#include <linux/of_irq.h>
31
32#define VF610_GPIO_PER_PORT 32
33
34struct vf610_gpio_port {
35 struct gpio_chip gc;
36 void __iomem *base;
37 void __iomem *gpio_base;
38 u8 irqc[VF610_GPIO_PER_PORT];
39 int irq;
40};
41
42#define GPIO_PDOR 0x00
43#define GPIO_PSOR 0x04
44#define GPIO_PCOR 0x08
45#define GPIO_PTOR 0x0c
46#define GPIO_PDIR 0x10
47
48#define PORT_PCR(n) ((n) * 0x4)
49#define PORT_PCR_IRQC_OFFSET 16
50
51#define PORT_ISFR 0xa0
52#define PORT_DFER 0xc0
53#define PORT_DFCR 0xc4
54#define PORT_DFWR 0xc8
55
56#define PORT_INT_OFF 0x0
57#define PORT_INT_LOGIC_ZERO 0x8
58#define PORT_INT_RISING_EDGE 0x9
59#define PORT_INT_FALLING_EDGE 0xa
60#define PORT_INT_EITHER_EDGE 0xb
61#define PORT_INT_LOGIC_ONE 0xc
62
63static const struct of_device_id vf610_gpio_dt_ids[] = {
64 { .compatible = "fsl,vf610-gpio" },
65 { /* sentinel */ }
66};
67
68static inline void vf610_gpio_writel(u32 val, void __iomem *reg)
69{
70 writel_relaxed(val, reg);
71}
72
73static inline u32 vf610_gpio_readl(void __iomem *reg)
74{
75 return readl_relaxed(reg);
76}
77
78static int vf610_gpio_request(struct gpio_chip *chip, unsigned offset)
79{
80 return pinctrl_request_gpio(chip->base + offset);
81}
82
83static void vf610_gpio_free(struct gpio_chip *chip, unsigned offset)
84{
85 pinctrl_free_gpio(chip->base + offset);
86}
87
88static int vf610_gpio_get(struct gpio_chip *gc, unsigned int gpio)
89{
90 struct vf610_gpio_port *port =
91 container_of(gc, struct vf610_gpio_port, gc);
92
93 return !!(vf610_gpio_readl(port->gpio_base + GPIO_PDIR) & BIT(gpio));
94}
95
96static void vf610_gpio_set(struct gpio_chip *gc, unsigned int gpio, int val)
97{
98 struct vf610_gpio_port *port =
99 container_of(gc, struct vf610_gpio_port, gc);
100 unsigned long mask = BIT(gpio);
101
102 if (val)
103 vf610_gpio_writel(mask, port->gpio_base + GPIO_PSOR);
104 else
105 vf610_gpio_writel(mask, port->gpio_base + GPIO_PCOR);
106}
107
108static int vf610_gpio_direction_input(struct gpio_chip *chip, unsigned gpio)
109{
110 return pinctrl_gpio_direction_input(chip->base + gpio);
111}
112
113static int vf610_gpio_direction_output(struct gpio_chip *chip, unsigned gpio,
114 int value)
115{
116 vf610_gpio_set(chip, gpio, value);
117
118 return pinctrl_gpio_direction_output(chip->base + gpio);
119}
120
121static void vf610_gpio_irq_handler(u32 irq, struct irq_desc *desc)
122{
123 struct vf610_gpio_port *port = irq_get_handler_data(irq);
124 struct irq_chip *chip = irq_desc_get_chip(desc);
125 int pin;
126 unsigned long irq_isfr;
127
128 chained_irq_enter(chip, desc);
129
130 irq_isfr = vf610_gpio_readl(port->base + PORT_ISFR);
131
132 for_each_set_bit(pin, &irq_isfr, VF610_GPIO_PER_PORT) {
133 vf610_gpio_writel(BIT(pin), port->base + PORT_ISFR);
134
135 generic_handle_irq(irq_find_mapping(port->gc.irqdomain, pin));
136 }
137
138 chained_irq_exit(chip, desc);
139}
140
141static void vf610_gpio_irq_ack(struct irq_data *d)
142{
143 struct vf610_gpio_port *port = irq_data_get_irq_chip_data(d);
144 int gpio = d->hwirq;
145
146 vf610_gpio_writel(BIT(gpio), port->base + PORT_ISFR);
147}
148
149static int vf610_gpio_irq_set_type(struct irq_data *d, u32 type)
150{
151 struct vf610_gpio_port *port = irq_data_get_irq_chip_data(d);
152 u8 irqc;
153
154 switch (type) {
155 case IRQ_TYPE_EDGE_RISING:
156 irqc = PORT_INT_RISING_EDGE;
157 break;
158 case IRQ_TYPE_EDGE_FALLING:
159 irqc = PORT_INT_FALLING_EDGE;
160 break;
161 case IRQ_TYPE_EDGE_BOTH:
162 irqc = PORT_INT_EITHER_EDGE;
163 break;
164 case IRQ_TYPE_LEVEL_LOW:
165 irqc = PORT_INT_LOGIC_ZERO;
166 break;
167 case IRQ_TYPE_LEVEL_HIGH:
168 irqc = PORT_INT_LOGIC_ONE;
169 break;
170 default:
171 return -EINVAL;
172 }
173
174 port->irqc[d->hwirq] = irqc;
175
176 return 0;
177}
178
179static void vf610_gpio_irq_mask(struct irq_data *d)
180{
181 struct vf610_gpio_port *port = irq_data_get_irq_chip_data(d);
182 void __iomem *pcr_base = port->base + PORT_PCR(d->hwirq);
183
184 vf610_gpio_writel(0, pcr_base);
185}
186
187static void vf610_gpio_irq_unmask(struct irq_data *d)
188{
189 struct vf610_gpio_port *port = irq_data_get_irq_chip_data(d);
190 void __iomem *pcr_base = port->base + PORT_PCR(d->hwirq);
191
192 vf610_gpio_writel(port->irqc[d->hwirq] << PORT_PCR_IRQC_OFFSET,
193 pcr_base);
194}
195
196static int vf610_gpio_irq_set_wake(struct irq_data *d, u32 enable)
197{
198 struct vf610_gpio_port *port = irq_data_get_irq_chip_data(d);
199
200 if (enable)
201 enable_irq_wake(port->irq);
202 else
203 disable_irq_wake(port->irq);
204
205 return 0;
206}
207
208static struct irq_chip vf610_gpio_irq_chip = {
209 .name = "gpio-vf610",
210 .irq_ack = vf610_gpio_irq_ack,
211 .irq_mask = vf610_gpio_irq_mask,
212 .irq_unmask = vf610_gpio_irq_unmask,
213 .irq_set_type = vf610_gpio_irq_set_type,
214 .irq_set_wake = vf610_gpio_irq_set_wake,
215};
216
217static int vf610_gpio_probe(struct platform_device *pdev)
218{
219 struct device *dev = &pdev->dev;
220 struct device_node *np = dev->of_node;
221 struct vf610_gpio_port *port;
222 struct resource *iores;
223 struct gpio_chip *gc;
224 int ret;
225
226 port = devm_kzalloc(&pdev->dev, sizeof(*port), GFP_KERNEL);
227 if (!port)
228 return -ENOMEM;
229
230 iores = platform_get_resource(pdev, IORESOURCE_MEM, 0);
231 port->base = devm_ioremap_resource(dev, iores);
232 if (IS_ERR(port->base))
233 return PTR_ERR(port->base);
234
235 iores = platform_get_resource(pdev, IORESOURCE_MEM, 1);
236 port->gpio_base = devm_ioremap_resource(dev, iores);
237 if (IS_ERR(port->gpio_base))
238 return PTR_ERR(port->gpio_base);
239
240 port->irq = platform_get_irq(pdev, 0);
241 if (port->irq < 0)
242 return port->irq;
243
244 gc = &port->gc;
245 gc->of_node = np;
246 gc->dev = dev;
247 gc->label = "vf610-gpio",
248 gc->ngpio = VF610_GPIO_PER_PORT,
249 gc->base = of_alias_get_id(np, "gpio") * VF610_GPIO_PER_PORT;
250
251 gc->request = vf610_gpio_request,
252 gc->free = vf610_gpio_free,
253 gc->direction_input = vf610_gpio_direction_input,
254 gc->get = vf610_gpio_get,
255 gc->direction_output = vf610_gpio_direction_output,
256 gc->set = vf610_gpio_set,
257
258 ret = gpiochip_add(gc);
259 if (ret < 0)
260 return ret;
261
262 /* Clear the interrupt status register for all GPIO's */
263 vf610_gpio_writel(~0, port->base + PORT_ISFR);
264
265 ret = gpiochip_irqchip_add(gc, &vf610_gpio_irq_chip, 0,
266 handle_simple_irq, IRQ_TYPE_NONE);
267 if (ret) {
268 dev_err(dev, "failed to add irqchip\n");
269 gpiochip_remove(gc);
270 return ret;
271 }
272 gpiochip_set_chained_irqchip(gc, &vf610_gpio_irq_chip, port->irq,
273 vf610_gpio_irq_handler);
274
275 return 0;
276}
277
278static struct platform_driver vf610_gpio_driver = {
279 .driver = {
280 .name = "gpio-vf610",
281 .owner = THIS_MODULE,
282 .of_match_table = vf610_gpio_dt_ids,
283 },
284 .probe = vf610_gpio_probe,
285};
286
287static int __init gpio_vf610_init(void)
288{
289 return platform_driver_register(&vf610_gpio_driver);
290}
291device_initcall(gpio_vf610_init);
292
293MODULE_AUTHOR("Stefan Agner <stefan@agner.ch>");
294MODULE_DESCRIPTION("Freescale VF610 GPIO");
295MODULE_LICENSE("GPL v2");