Commit | Line | Data |
---|---|---|
07bd1a6c JB |
1 | /* |
2 | * MXC GPIO support. (c) 2008 Daniel Mack <daniel@caiaq.de> | |
3 | * Copyright 2008 Juergen Beisert, kernel@pengutronix.de | |
4 | * | |
5 | * Based on code from Freescale, | |
e24798e6 | 6 | * Copyright (C) 2004-2010 Freescale Semiconductor, Inc. All Rights Reserved. |
07bd1a6c JB |
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 | * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |
20 | */ | |
21 | ||
22 | #include <linux/init.h> | |
a3484ffd | 23 | #include <linux/interrupt.h> |
07bd1a6c JB |
24 | #include <linux/io.h> |
25 | #include <linux/irq.h> | |
26 | #include <linux/gpio.h> | |
b78d8e59 SG |
27 | #include <linux/platform_device.h> |
28 | #include <linux/slab.h> | |
2ce420da | 29 | #include <linux/basic_mmio_gpio.h> |
a09e64fb | 30 | #include <mach/hardware.h> |
07bd1a6c JB |
31 | #include <asm-generic/bug.h> |
32 | ||
b78d8e59 SG |
33 | struct mxc_gpio_port { |
34 | struct list_head node; | |
35 | void __iomem *base; | |
36 | int irq; | |
37 | int irq_high; | |
38 | int virtual_irq_start; | |
2ce420da | 39 | struct bgpio_chip bgc; |
b78d8e59 | 40 | u32 both_edges; |
b78d8e59 SG |
41 | }; |
42 | ||
43 | /* | |
44 | * MX2 has one interrupt *for all* gpio ports. The list is used | |
45 | * to save the references to all ports, so that mx2_gpio_irq_handler | |
46 | * can walk through all interrupt status registers. | |
47 | */ | |
48 | static LIST_HEAD(mxc_gpio_ports); | |
07bd1a6c | 49 | |
494f22dd SH |
50 | #define cpu_is_mx1_mx2() (cpu_is_mx1() || cpu_is_mx2()) |
51 | ||
52 | #define GPIO_DR (cpu_is_mx1_mx2() ? 0x1c : 0x00) | |
53 | #define GPIO_GDIR (cpu_is_mx1_mx2() ? 0x00 : 0x04) | |
54 | #define GPIO_PSR (cpu_is_mx1_mx2() ? 0x24 : 0x08) | |
55 | #define GPIO_ICR1 (cpu_is_mx1_mx2() ? 0x28 : 0x0C) | |
56 | #define GPIO_ICR2 (cpu_is_mx1_mx2() ? 0x2C : 0x10) | |
57 | #define GPIO_IMR (cpu_is_mx1_mx2() ? 0x30 : 0x14) | |
58 | #define GPIO_ISR (cpu_is_mx1_mx2() ? 0x34 : 0x18) | |
494f22dd SH |
59 | |
60 | #define GPIO_INT_LOW_LEV (cpu_is_mx1_mx2() ? 0x3 : 0x0) | |
61 | #define GPIO_INT_HIGH_LEV (cpu_is_mx1_mx2() ? 0x2 : 0x1) | |
62 | #define GPIO_INT_RISE_EDGE (cpu_is_mx1_mx2() ? 0x0 : 0x2) | |
63 | #define GPIO_INT_FALL_EDGE (cpu_is_mx1_mx2() ? 0x1 : 0x3) | |
64 | #define GPIO_INT_NONE 0x4 | |
65 | ||
07bd1a6c JB |
66 | /* Note: This driver assumes 32 GPIOs are handled in one register */ |
67 | ||
4d93579f | 68 | static int gpio_set_irq_type(struct irq_data *d, u32 type) |
07bd1a6c | 69 | { |
4d93579f | 70 | u32 gpio = irq_to_gpio(d->irq); |
e4ea9333 SG |
71 | struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d); |
72 | struct mxc_gpio_port *port = gc->private; | |
07bd1a6c JB |
73 | u32 bit, val; |
74 | int edge; | |
75 | void __iomem *reg = port->base; | |
76 | ||
910862ec | 77 | port->both_edges &= ~(1 << (gpio & 31)); |
07bd1a6c | 78 | switch (type) { |
6cab4860 | 79 | case IRQ_TYPE_EDGE_RISING: |
07bd1a6c JB |
80 | edge = GPIO_INT_RISE_EDGE; |
81 | break; | |
6cab4860 | 82 | case IRQ_TYPE_EDGE_FALLING: |
07bd1a6c JB |
83 | edge = GPIO_INT_FALL_EDGE; |
84 | break; | |
910862ec | 85 | case IRQ_TYPE_EDGE_BOTH: |
5523f86b | 86 | val = gpio_get_value(gpio); |
910862ec GL |
87 | if (val) { |
88 | edge = GPIO_INT_LOW_LEV; | |
89 | pr_debug("mxc: set GPIO %d to low trigger\n", gpio); | |
90 | } else { | |
91 | edge = GPIO_INT_HIGH_LEV; | |
92 | pr_debug("mxc: set GPIO %d to high trigger\n", gpio); | |
93 | } | |
94 | port->both_edges |= 1 << (gpio & 31); | |
95 | break; | |
6cab4860 | 96 | case IRQ_TYPE_LEVEL_LOW: |
07bd1a6c JB |
97 | edge = GPIO_INT_LOW_LEV; |
98 | break; | |
6cab4860 | 99 | case IRQ_TYPE_LEVEL_HIGH: |
07bd1a6c JB |
100 | edge = GPIO_INT_HIGH_LEV; |
101 | break; | |
910862ec | 102 | default: |
07bd1a6c JB |
103 | return -EINVAL; |
104 | } | |
105 | ||
106 | reg += GPIO_ICR1 + ((gpio & 0x10) >> 2); /* lower or upper register */ | |
107 | bit = gpio & 0xf; | |
b78d8e59 SG |
108 | val = readl(reg) & ~(0x3 << (bit << 1)); |
109 | writel(val | (edge << (bit << 1)), reg); | |
e4ea9333 | 110 | writel(1 << (gpio & 0x1f), port->base + GPIO_ISR); |
07bd1a6c JB |
111 | |
112 | return 0; | |
113 | } | |
114 | ||
910862ec GL |
115 | static void mxc_flip_edge(struct mxc_gpio_port *port, u32 gpio) |
116 | { | |
117 | void __iomem *reg = port->base; | |
118 | u32 bit, val; | |
119 | int edge; | |
120 | ||
121 | reg += GPIO_ICR1 + ((gpio & 0x10) >> 2); /* lower or upper register */ | |
122 | bit = gpio & 0xf; | |
b78d8e59 | 123 | val = readl(reg); |
910862ec GL |
124 | edge = (val >> (bit << 1)) & 3; |
125 | val &= ~(0x3 << (bit << 1)); | |
3d40f7fe | 126 | if (edge == GPIO_INT_HIGH_LEV) { |
910862ec GL |
127 | edge = GPIO_INT_LOW_LEV; |
128 | pr_debug("mxc: switch GPIO %d to low trigger\n", gpio); | |
3d40f7fe | 129 | } else if (edge == GPIO_INT_LOW_LEV) { |
910862ec GL |
130 | edge = GPIO_INT_HIGH_LEV; |
131 | pr_debug("mxc: switch GPIO %d to high trigger\n", gpio); | |
3d40f7fe | 132 | } else { |
910862ec GL |
133 | pr_err("mxc: invalid configuration for GPIO %d: %x\n", |
134 | gpio, edge); | |
135 | return; | |
136 | } | |
b78d8e59 | 137 | writel(val | (edge << (bit << 1)), reg); |
910862ec GL |
138 | } |
139 | ||
3621f188 | 140 | /* handle 32 interrupts in one status register */ |
07bd1a6c JB |
141 | static void mxc_gpio_irq_handler(struct mxc_gpio_port *port, u32 irq_stat) |
142 | { | |
3621f188 | 143 | u32 gpio_irq_no_base = port->virtual_irq_start; |
07bd1a6c | 144 | |
3621f188 UKK |
145 | while (irq_stat != 0) { |
146 | int irqoffset = fls(irq_stat) - 1; | |
07bd1a6c | 147 | |
3621f188 UKK |
148 | if (port->both_edges & (1 << irqoffset)) |
149 | mxc_flip_edge(port, irqoffset); | |
910862ec | 150 | |
3621f188 | 151 | generic_handle_irq(gpio_irq_no_base + irqoffset); |
910862ec | 152 | |
3621f188 | 153 | irq_stat &= ~(1 << irqoffset); |
07bd1a6c JB |
154 | } |
155 | } | |
156 | ||
cfca8b53 | 157 | /* MX1 and MX3 has one interrupt *per* gpio port */ |
07bd1a6c JB |
158 | static void mx3_gpio_irq_handler(u32 irq, struct irq_desc *desc) |
159 | { | |
160 | u32 irq_stat; | |
6845664a | 161 | struct mxc_gpio_port *port = irq_get_handler_data(irq); |
07bd1a6c | 162 | |
b78d8e59 | 163 | irq_stat = readl(port->base + GPIO_ISR) & readl(port->base + GPIO_IMR); |
e2c97e7f | 164 | |
07bd1a6c JB |
165 | mxc_gpio_irq_handler(port, irq_stat); |
166 | } | |
07bd1a6c | 167 | |
07bd1a6c JB |
168 | /* MX2 has one interrupt *for all* gpio ports */ |
169 | static void mx2_gpio_irq_handler(u32 irq, struct irq_desc *desc) | |
170 | { | |
07bd1a6c | 171 | u32 irq_msk, irq_stat; |
b78d8e59 | 172 | struct mxc_gpio_port *port; |
07bd1a6c JB |
173 | |
174 | /* walk through all interrupt status registers */ | |
b78d8e59 SG |
175 | list_for_each_entry(port, &mxc_gpio_ports, node) { |
176 | irq_msk = readl(port->base + GPIO_IMR); | |
07bd1a6c JB |
177 | if (!irq_msk) |
178 | continue; | |
179 | ||
b78d8e59 | 180 | irq_stat = readl(port->base + GPIO_ISR) & irq_msk; |
07bd1a6c | 181 | if (irq_stat) |
b78d8e59 | 182 | mxc_gpio_irq_handler(port, irq_stat); |
07bd1a6c JB |
183 | } |
184 | } | |
07bd1a6c | 185 | |
a3484ffd DN |
186 | /* |
187 | * Set interrupt number "irq" in the GPIO as a wake-up source. | |
188 | * While system is running, all registered GPIO interrupts need to have | |
189 | * wake-up enabled. When system is suspended, only selected GPIO interrupts | |
190 | * need to have wake-up enabled. | |
191 | * @param irq interrupt source number | |
192 | * @param enable enable as wake-up if equal to non-zero | |
193 | * @return This function returns 0 on success. | |
194 | */ | |
4d93579f | 195 | static int gpio_set_wake_irq(struct irq_data *d, u32 enable) |
a3484ffd | 196 | { |
4d93579f | 197 | u32 gpio = irq_to_gpio(d->irq); |
a3484ffd | 198 | u32 gpio_idx = gpio & 0x1F; |
e4ea9333 SG |
199 | struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d); |
200 | struct mxc_gpio_port *port = gc->private; | |
a3484ffd DN |
201 | |
202 | if (enable) { | |
203 | if (port->irq_high && (gpio_idx >= 16)) | |
204 | enable_irq_wake(port->irq_high); | |
205 | else | |
206 | enable_irq_wake(port->irq); | |
207 | } else { | |
208 | if (port->irq_high && (gpio_idx >= 16)) | |
209 | disable_irq_wake(port->irq_high); | |
210 | else | |
211 | disable_irq_wake(port->irq); | |
212 | } | |
213 | ||
214 | return 0; | |
215 | } | |
216 | ||
e4ea9333 SG |
217 | static void __init mxc_gpio_init_gc(struct mxc_gpio_port *port) |
218 | { | |
219 | struct irq_chip_generic *gc; | |
220 | struct irq_chip_type *ct; | |
221 | ||
222 | gc = irq_alloc_generic_chip("gpio-mxc", 1, port->virtual_irq_start, | |
223 | port->base, handle_level_irq); | |
224 | gc->private = port; | |
225 | ||
226 | ct = gc->chip_types; | |
227 | ct->chip.irq_ack = irq_gc_ack, | |
228 | ct->chip.irq_mask = irq_gc_mask_clr_bit; | |
229 | ct->chip.irq_unmask = irq_gc_mask_set_bit; | |
230 | ct->chip.irq_set_type = gpio_set_irq_type; | |
231 | ct->chip.irq_set_wake = gpio_set_wake_irq, | |
232 | ct->regs.ack = GPIO_ISR; | |
233 | ct->regs.mask = GPIO_IMR; | |
234 | ||
235 | irq_setup_generic_chip(gc, IRQ_MSK(32), IRQ_GC_INIT_NESTED_LOCK, | |
236 | IRQ_NOREQUEST, 0); | |
237 | } | |
b5eee2fd | 238 | |
b78d8e59 | 239 | static int __devinit mxc_gpio_probe(struct platform_device *pdev) |
07bd1a6c | 240 | { |
b78d8e59 SG |
241 | struct mxc_gpio_port *port; |
242 | struct resource *iores; | |
e4ea9333 | 243 | int err; |
b78d8e59 SG |
244 | |
245 | port = kzalloc(sizeof(struct mxc_gpio_port), GFP_KERNEL); | |
246 | if (!port) | |
247 | return -ENOMEM; | |
07bd1a6c | 248 | |
b78d8e59 | 249 | port->virtual_irq_start = MXC_GPIO_IRQ_START + pdev->id * 32; |
07bd1a6c | 250 | |
b78d8e59 SG |
251 | iores = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
252 | if (!iores) { | |
253 | err = -ENODEV; | |
254 | goto out_kfree; | |
255 | } | |
14cb0deb | 256 | |
b78d8e59 SG |
257 | if (!request_mem_region(iores->start, resource_size(iores), |
258 | pdev->name)) { | |
259 | err = -EBUSY; | |
260 | goto out_kfree; | |
261 | } | |
07bd1a6c | 262 | |
b78d8e59 SG |
263 | port->base = ioremap(iores->start, resource_size(iores)); |
264 | if (!port->base) { | |
265 | err = -ENOMEM; | |
266 | goto out_release_mem; | |
267 | } | |
268 | ||
269 | port->irq_high = platform_get_irq(pdev, 1); | |
270 | port->irq = platform_get_irq(pdev, 0); | |
271 | if (port->irq < 0) { | |
272 | err = -EINVAL; | |
273 | goto out_iounmap; | |
274 | } | |
275 | ||
276 | /* disable the interrupt and clear the status */ | |
277 | writel(0, port->base + GPIO_IMR); | |
278 | writel(~0, port->base + GPIO_ISR); | |
279 | ||
e4ea9333 SG |
280 | /* gpio-mxc can be a generic irq chip */ |
281 | mxc_gpio_init_gc(port); | |
8afaada2 SH |
282 | |
283 | if (cpu_is_mx2()) { | |
284 | /* setup one handler for all GPIO interrupts */ | |
b78d8e59 SG |
285 | if (pdev->id == 0) |
286 | irq_set_chained_handler(port->irq, | |
287 | mx2_gpio_irq_handler); | |
288 | } else { | |
289 | /* setup one handler for each entry */ | |
290 | irq_set_chained_handler(port->irq, mx3_gpio_irq_handler); | |
291 | irq_set_handler_data(port->irq, port); | |
292 | if (port->irq_high > 0) { | |
293 | /* setup handler for GPIO 16 to 31 */ | |
294 | irq_set_chained_handler(port->irq_high, | |
295 | mx3_gpio_irq_handler); | |
296 | irq_set_handler_data(port->irq_high, port); | |
297 | } | |
07bd1a6c JB |
298 | } |
299 | ||
2ce420da SG |
300 | err = bgpio_init(&port->bgc, &pdev->dev, 4, |
301 | port->base + GPIO_PSR, | |
302 | port->base + GPIO_DR, NULL, | |
303 | port->base + GPIO_GDIR, NULL, false); | |
304 | if (err) | |
305 | goto out_iounmap; | |
b78d8e59 | 306 | |
2ce420da | 307 | port->bgc.gc.base = pdev->id * 32; |
b78d8e59 | 308 | |
2ce420da | 309 | err = gpiochip_add(&port->bgc.gc); |
b78d8e59 | 310 | if (err) |
2ce420da | 311 | goto out_bgpio_remove; |
b78d8e59 SG |
312 | |
313 | list_add_tail(&port->node, &mxc_gpio_ports); | |
314 | ||
07bd1a6c | 315 | return 0; |
b78d8e59 | 316 | |
2ce420da SG |
317 | out_bgpio_remove: |
318 | bgpio_remove(&port->bgc); | |
b78d8e59 SG |
319 | out_iounmap: |
320 | iounmap(port->base); | |
321 | out_release_mem: | |
322 | release_mem_region(iores->start, resource_size(iores)); | |
323 | out_kfree: | |
324 | kfree(port); | |
325 | dev_info(&pdev->dev, "%s failed with errno %d\n", __func__, err); | |
326 | return err; | |
07bd1a6c | 327 | } |
b78d8e59 SG |
328 | |
329 | static struct platform_driver mxc_gpio_driver = { | |
330 | .driver = { | |
331 | .name = "gpio-mxc", | |
332 | .owner = THIS_MODULE, | |
333 | }, | |
334 | .probe = mxc_gpio_probe, | |
335 | }; | |
336 | ||
337 | static int __init gpio_mxc_init(void) | |
338 | { | |
339 | return platform_driver_register(&mxc_gpio_driver); | |
340 | } | |
341 | postcore_initcall(gpio_mxc_init); | |
342 | ||
343 | MODULE_AUTHOR("Freescale Semiconductor, " | |
344 | "Daniel Mack <danielncaiaq.de>, " | |
345 | "Juergen Beisert <kernel@pengutronix.de>"); | |
346 | MODULE_DESCRIPTION("Freescale MXC GPIO"); | |
347 | MODULE_LICENSE("GPL"); |