Skip to content

Commit 7f2691a

Browse files
agnerslinusw
authored andcommitted
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>
1 parent 3eebd61 commit 7f2691a

File tree

3 files changed

+303
-0
lines changed

3 files changed

+303
-0
lines changed

drivers/gpio/Kconfig

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -347,6 +347,13 @@ config GPIO_TZ1090_PDC
347347
help
348348
Say yes here to support Toumaz Xenif TZ1090 PDC GPIOs.
349349

350+
config 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+
350357
config GPIO_XGENE
351358
bool "APM X-Gene GPIO controller support"
352359
depends on ARM64 && OF_GPIO

drivers/gpio/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@ obj-$(CONFIG_GPIO_TWL6040) += gpio-twl6040.o
9696
obj-$(CONFIG_GPIO_TZ1090) += gpio-tz1090.o
9797
obj-$(CONFIG_GPIO_TZ1090_PDC) += gpio-tz1090-pdc.o
9898
obj-$(CONFIG_GPIO_UCB1400) += gpio-ucb1400.o
99+
obj-$(CONFIG_GPIO_VF610) += gpio-vf610.o
99100
obj-$(CONFIG_GPIO_VIPERBOARD) += gpio-viperboard.o
100101
obj-$(CONFIG_GPIO_VR41XX) += gpio-vr41xx.o
101102
obj-$(CONFIG_GPIO_VX855) += gpio-vx855.o

drivers/gpio/gpio-vf610.c

Lines changed: 295 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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+
34+
struct 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+
63+
static const struct of_device_id vf610_gpio_dt_ids[] = {
64+
{ .compatible = "fsl,vf610-gpio" },
65+
{ /* sentinel */ }
66+
};
67+
68+
static inline void vf610_gpio_writel(u32 val, void __iomem *reg)
69+
{
70+
writel_relaxed(val, reg);
71+
}
72+
73+
static inline u32 vf610_gpio_readl(void __iomem *reg)
74+
{
75+
return readl_relaxed(reg);
76+
}
77+
78+
static int vf610_gpio_request(struct gpio_chip *chip, unsigned offset)
79+
{
80+
return pinctrl_request_gpio(chip->base + offset);
81+
}
82+
83+
static void vf610_gpio_free(struct gpio_chip *chip, unsigned offset)
84+
{
85+
pinctrl_free_gpio(chip->base + offset);
86+
}
87+
88+
static 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+
96+
static 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+
108+
static int vf610_gpio_direction_input(struct gpio_chip *chip, unsigned gpio)
109+
{
110+
return pinctrl_gpio_direction_input(chip->base + gpio);
111+
}
112+
113+
static 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+
121+
static 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+
141+
static 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+
149+
static 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+
179+
static 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+
187+
static 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+
196+
static 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+
208+
static 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+
217+
static 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+
278+
static 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+
287+
static int __init gpio_vf610_init(void)
288+
{
289+
return platform_driver_register(&vf610_gpio_driver);
290+
}
291+
device_initcall(gpio_vf610_init);
292+
293+
MODULE_AUTHOR("Stefan Agner <stefan@agner.ch>");
294+
MODULE_DESCRIPTION("Freescale VF610 GPIO");
295+
MODULE_LICENSE("GPL v2");

0 commit comments

Comments
 (0)