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 | ||
68 | static void _clear_gpio_irqstatus(struct mxc_gpio_port *port, u32 index) | |
69 | { | |
b78d8e59 | 70 | writel(1 << index, port->base + GPIO_ISR); |
07bd1a6c JB |
71 | } |
72 | ||
73 | static void _set_gpio_irqenable(struct mxc_gpio_port *port, u32 index, | |
74 | int enable) | |
75 | { | |
76 | u32 l; | |
77 | ||
b78d8e59 | 78 | l = readl(port->base + GPIO_IMR); |
07bd1a6c | 79 | l = (l & (~(1 << index))) | (!!enable << index); |
b78d8e59 | 80 | writel(l, port->base + GPIO_IMR); |
07bd1a6c JB |
81 | } |
82 | ||
4d93579f | 83 | static void gpio_ack_irq(struct irq_data *d) |
07bd1a6c | 84 | { |
b78d8e59 | 85 | struct mxc_gpio_port *port = irq_data_get_irq_chip_data(d); |
4d93579f | 86 | u32 gpio = irq_to_gpio(d->irq); |
b78d8e59 | 87 | _clear_gpio_irqstatus(port, gpio & 0x1f); |
07bd1a6c JB |
88 | } |
89 | ||
4d93579f | 90 | static void gpio_mask_irq(struct irq_data *d) |
07bd1a6c | 91 | { |
b78d8e59 | 92 | struct mxc_gpio_port *port = irq_data_get_irq_chip_data(d); |
4d93579f | 93 | u32 gpio = irq_to_gpio(d->irq); |
b78d8e59 | 94 | _set_gpio_irqenable(port, gpio & 0x1f, 0); |
07bd1a6c JB |
95 | } |
96 | ||
4d93579f | 97 | static void gpio_unmask_irq(struct irq_data *d) |
07bd1a6c | 98 | { |
b78d8e59 | 99 | struct mxc_gpio_port *port = irq_data_get_irq_chip_data(d); |
4d93579f | 100 | u32 gpio = irq_to_gpio(d->irq); |
b78d8e59 | 101 | _set_gpio_irqenable(port, gpio & 0x1f, 1); |
07bd1a6c JB |
102 | } |
103 | ||
4d93579f | 104 | static int gpio_set_irq_type(struct irq_data *d, u32 type) |
07bd1a6c | 105 | { |
4d93579f | 106 | u32 gpio = irq_to_gpio(d->irq); |
b78d8e59 | 107 | struct mxc_gpio_port *port = irq_data_get_irq_chip_data(d); |
07bd1a6c JB |
108 | u32 bit, val; |
109 | int edge; | |
110 | void __iomem *reg = port->base; | |
111 | ||
910862ec | 112 | port->both_edges &= ~(1 << (gpio & 31)); |
07bd1a6c | 113 | switch (type) { |
6cab4860 | 114 | case IRQ_TYPE_EDGE_RISING: |
07bd1a6c JB |
115 | edge = GPIO_INT_RISE_EDGE; |
116 | break; | |
6cab4860 | 117 | case IRQ_TYPE_EDGE_FALLING: |
07bd1a6c JB |
118 | edge = GPIO_INT_FALL_EDGE; |
119 | break; | |
910862ec | 120 | case IRQ_TYPE_EDGE_BOTH: |
2ce420da | 121 | val = gpio_get_value(gpio & 31); |
910862ec GL |
122 | if (val) { |
123 | edge = GPIO_INT_LOW_LEV; | |
124 | pr_debug("mxc: set GPIO %d to low trigger\n", gpio); | |
125 | } else { | |
126 | edge = GPIO_INT_HIGH_LEV; | |
127 | pr_debug("mxc: set GPIO %d to high trigger\n", gpio); | |
128 | } | |
129 | port->both_edges |= 1 << (gpio & 31); | |
130 | break; | |
6cab4860 | 131 | case IRQ_TYPE_LEVEL_LOW: |
07bd1a6c JB |
132 | edge = GPIO_INT_LOW_LEV; |
133 | break; | |
6cab4860 | 134 | case IRQ_TYPE_LEVEL_HIGH: |
07bd1a6c JB |
135 | edge = GPIO_INT_HIGH_LEV; |
136 | break; | |
910862ec | 137 | default: |
07bd1a6c JB |
138 | return -EINVAL; |
139 | } | |
140 | ||
141 | reg += GPIO_ICR1 + ((gpio & 0x10) >> 2); /* lower or upper register */ | |
142 | bit = gpio & 0xf; | |
b78d8e59 SG |
143 | val = readl(reg) & ~(0x3 << (bit << 1)); |
144 | writel(val | (edge << (bit << 1)), reg); | |
07bd1a6c JB |
145 | _clear_gpio_irqstatus(port, gpio & 0x1f); |
146 | ||
147 | return 0; | |
148 | } | |
149 | ||
910862ec GL |
150 | static void mxc_flip_edge(struct mxc_gpio_port *port, u32 gpio) |
151 | { | |
152 | void __iomem *reg = port->base; | |
153 | u32 bit, val; | |
154 | int edge; | |
155 | ||
156 | reg += GPIO_ICR1 + ((gpio & 0x10) >> 2); /* lower or upper register */ | |
157 | bit = gpio & 0xf; | |
b78d8e59 | 158 | val = readl(reg); |
910862ec GL |
159 | edge = (val >> (bit << 1)) & 3; |
160 | val &= ~(0x3 << (bit << 1)); | |
3d40f7fe | 161 | if (edge == GPIO_INT_HIGH_LEV) { |
910862ec GL |
162 | edge = GPIO_INT_LOW_LEV; |
163 | pr_debug("mxc: switch GPIO %d to low trigger\n", gpio); | |
3d40f7fe | 164 | } else if (edge == GPIO_INT_LOW_LEV) { |
910862ec GL |
165 | edge = GPIO_INT_HIGH_LEV; |
166 | pr_debug("mxc: switch GPIO %d to high trigger\n", gpio); | |
3d40f7fe | 167 | } else { |
910862ec GL |
168 | pr_err("mxc: invalid configuration for GPIO %d: %x\n", |
169 | gpio, edge); | |
170 | return; | |
171 | } | |
b78d8e59 | 172 | writel(val | (edge << (bit << 1)), reg); |
910862ec GL |
173 | } |
174 | ||
3621f188 | 175 | /* handle 32 interrupts in one status register */ |
07bd1a6c JB |
176 | static void mxc_gpio_irq_handler(struct mxc_gpio_port *port, u32 irq_stat) |
177 | { | |
3621f188 | 178 | u32 gpio_irq_no_base = port->virtual_irq_start; |
07bd1a6c | 179 | |
3621f188 UKK |
180 | while (irq_stat != 0) { |
181 | int irqoffset = fls(irq_stat) - 1; | |
07bd1a6c | 182 | |
3621f188 UKK |
183 | if (port->both_edges & (1 << irqoffset)) |
184 | mxc_flip_edge(port, irqoffset); | |
910862ec | 185 | |
3621f188 | 186 | generic_handle_irq(gpio_irq_no_base + irqoffset); |
910862ec | 187 | |
3621f188 | 188 | irq_stat &= ~(1 << irqoffset); |
07bd1a6c JB |
189 | } |
190 | } | |
191 | ||
cfca8b53 | 192 | /* MX1 and MX3 has one interrupt *per* gpio port */ |
07bd1a6c JB |
193 | static void mx3_gpio_irq_handler(u32 irq, struct irq_desc *desc) |
194 | { | |
195 | u32 irq_stat; | |
6845664a | 196 | struct mxc_gpio_port *port = irq_get_handler_data(irq); |
07bd1a6c | 197 | |
b78d8e59 | 198 | irq_stat = readl(port->base + GPIO_ISR) & readl(port->base + GPIO_IMR); |
e2c97e7f | 199 | |
07bd1a6c JB |
200 | mxc_gpio_irq_handler(port, irq_stat); |
201 | } | |
07bd1a6c | 202 | |
07bd1a6c JB |
203 | /* MX2 has one interrupt *for all* gpio ports */ |
204 | static void mx2_gpio_irq_handler(u32 irq, struct irq_desc *desc) | |
205 | { | |
07bd1a6c | 206 | u32 irq_msk, irq_stat; |
b78d8e59 | 207 | struct mxc_gpio_port *port; |
07bd1a6c JB |
208 | |
209 | /* walk through all interrupt status registers */ | |
b78d8e59 SG |
210 | list_for_each_entry(port, &mxc_gpio_ports, node) { |
211 | irq_msk = readl(port->base + GPIO_IMR); | |
07bd1a6c JB |
212 | if (!irq_msk) |
213 | continue; | |
214 | ||
b78d8e59 | 215 | irq_stat = readl(port->base + GPIO_ISR) & irq_msk; |
07bd1a6c | 216 | if (irq_stat) |
b78d8e59 | 217 | mxc_gpio_irq_handler(port, irq_stat); |
07bd1a6c JB |
218 | } |
219 | } | |
07bd1a6c | 220 | |
a3484ffd DN |
221 | /* |
222 | * Set interrupt number "irq" in the GPIO as a wake-up source. | |
223 | * While system is running, all registered GPIO interrupts need to have | |
224 | * wake-up enabled. When system is suspended, only selected GPIO interrupts | |
225 | * need to have wake-up enabled. | |
226 | * @param irq interrupt source number | |
227 | * @param enable enable as wake-up if equal to non-zero | |
228 | * @return This function returns 0 on success. | |
229 | */ | |
4d93579f | 230 | static int gpio_set_wake_irq(struct irq_data *d, u32 enable) |
a3484ffd | 231 | { |
4d93579f | 232 | u32 gpio = irq_to_gpio(d->irq); |
a3484ffd | 233 | u32 gpio_idx = gpio & 0x1F; |
b78d8e59 | 234 | struct mxc_gpio_port *port = irq_data_get_irq_chip_data(d); |
a3484ffd DN |
235 | |
236 | if (enable) { | |
237 | if (port->irq_high && (gpio_idx >= 16)) | |
238 | enable_irq_wake(port->irq_high); | |
239 | else | |
240 | enable_irq_wake(port->irq); | |
241 | } else { | |
242 | if (port->irq_high && (gpio_idx >= 16)) | |
243 | disable_irq_wake(port->irq_high); | |
244 | else | |
245 | disable_irq_wake(port->irq); | |
246 | } | |
247 | ||
248 | return 0; | |
249 | } | |
250 | ||
07bd1a6c | 251 | static struct irq_chip gpio_irq_chip = { |
039c4644 | 252 | .name = "GPIO", |
4d93579f LB |
253 | .irq_ack = gpio_ack_irq, |
254 | .irq_mask = gpio_mask_irq, | |
255 | .irq_unmask = gpio_unmask_irq, | |
256 | .irq_set_type = gpio_set_irq_type, | |
257 | .irq_set_wake = gpio_set_wake_irq, | |
07bd1a6c JB |
258 | }; |
259 | ||
b5eee2fd TG |
260 | /* |
261 | * This lock class tells lockdep that GPIO irqs are in a different | |
262 | * category than their parents, so it won't report false recursion. | |
263 | */ | |
264 | static struct lock_class_key gpio_lock_class; | |
265 | ||
b78d8e59 | 266 | static int __devinit mxc_gpio_probe(struct platform_device *pdev) |
07bd1a6c | 267 | { |
b78d8e59 SG |
268 | struct mxc_gpio_port *port; |
269 | struct resource *iores; | |
270 | int err, i; | |
271 | ||
272 | port = kzalloc(sizeof(struct mxc_gpio_port), GFP_KERNEL); | |
273 | if (!port) | |
274 | return -ENOMEM; | |
07bd1a6c | 275 | |
b78d8e59 | 276 | port->virtual_irq_start = MXC_GPIO_IRQ_START + pdev->id * 32; |
07bd1a6c | 277 | |
b78d8e59 SG |
278 | iores = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
279 | if (!iores) { | |
280 | err = -ENODEV; | |
281 | goto out_kfree; | |
282 | } | |
14cb0deb | 283 | |
b78d8e59 SG |
284 | if (!request_mem_region(iores->start, resource_size(iores), |
285 | pdev->name)) { | |
286 | err = -EBUSY; | |
287 | goto out_kfree; | |
288 | } | |
07bd1a6c | 289 | |
b78d8e59 SG |
290 | port->base = ioremap(iores->start, resource_size(iores)); |
291 | if (!port->base) { | |
292 | err = -ENOMEM; | |
293 | goto out_release_mem; | |
294 | } | |
295 | ||
296 | port->irq_high = platform_get_irq(pdev, 1); | |
297 | port->irq = platform_get_irq(pdev, 0); | |
298 | if (port->irq < 0) { | |
299 | err = -EINVAL; | |
300 | goto out_iounmap; | |
301 | } | |
302 | ||
303 | /* disable the interrupt and clear the status */ | |
304 | writel(0, port->base + GPIO_IMR); | |
305 | writel(~0, port->base + GPIO_ISR); | |
306 | ||
307 | for (i = port->virtual_irq_start; | |
308 | i < port->virtual_irq_start + 32; i++) { | |
309 | irq_set_lockdep_class(i, &gpio_lock_class); | |
310 | irq_set_chip_and_handler(i, &gpio_irq_chip, handle_level_irq); | |
311 | set_irq_flags(i, IRQF_VALID); | |
312 | irq_set_chip_data(i, port); | |
8afaada2 SH |
313 | } |
314 | ||
315 | if (cpu_is_mx2()) { | |
316 | /* setup one handler for all GPIO interrupts */ | |
b78d8e59 SG |
317 | if (pdev->id == 0) |
318 | irq_set_chained_handler(port->irq, | |
319 | mx2_gpio_irq_handler); | |
320 | } else { | |
321 | /* setup one handler for each entry */ | |
322 | irq_set_chained_handler(port->irq, mx3_gpio_irq_handler); | |
323 | irq_set_handler_data(port->irq, port); | |
324 | if (port->irq_high > 0) { | |
325 | /* setup handler for GPIO 16 to 31 */ | |
326 | irq_set_chained_handler(port->irq_high, | |
327 | mx3_gpio_irq_handler); | |
328 | irq_set_handler_data(port->irq_high, port); | |
329 | } | |
07bd1a6c JB |
330 | } |
331 | ||
2ce420da SG |
332 | err = bgpio_init(&port->bgc, &pdev->dev, 4, |
333 | port->base + GPIO_PSR, | |
334 | port->base + GPIO_DR, NULL, | |
335 | port->base + GPIO_GDIR, NULL, false); | |
336 | if (err) | |
337 | goto out_iounmap; | |
b78d8e59 | 338 | |
2ce420da | 339 | port->bgc.gc.base = pdev->id * 32; |
b78d8e59 | 340 | |
2ce420da | 341 | err = gpiochip_add(&port->bgc.gc); |
b78d8e59 | 342 | if (err) |
2ce420da | 343 | goto out_bgpio_remove; |
b78d8e59 SG |
344 | |
345 | list_add_tail(&port->node, &mxc_gpio_ports); | |
346 | ||
07bd1a6c | 347 | return 0; |
b78d8e59 | 348 | |
2ce420da SG |
349 | out_bgpio_remove: |
350 | bgpio_remove(&port->bgc); | |
b78d8e59 SG |
351 | out_iounmap: |
352 | iounmap(port->base); | |
353 | out_release_mem: | |
354 | release_mem_region(iores->start, resource_size(iores)); | |
355 | out_kfree: | |
356 | kfree(port); | |
357 | dev_info(&pdev->dev, "%s failed with errno %d\n", __func__, err); | |
358 | return err; | |
07bd1a6c | 359 | } |
b78d8e59 SG |
360 | |
361 | static struct platform_driver mxc_gpio_driver = { | |
362 | .driver = { | |
363 | .name = "gpio-mxc", | |
364 | .owner = THIS_MODULE, | |
365 | }, | |
366 | .probe = mxc_gpio_probe, | |
367 | }; | |
368 | ||
369 | static int __init gpio_mxc_init(void) | |
370 | { | |
371 | return platform_driver_register(&mxc_gpio_driver); | |
372 | } | |
373 | postcore_initcall(gpio_mxc_init); | |
374 | ||
375 | MODULE_AUTHOR("Freescale Semiconductor, " | |
376 | "Daniel Mack <danielncaiaq.de>, " | |
377 | "Juergen Beisert <kernel@pengutronix.de>"); | |
378 | MODULE_DESCRIPTION("Freescale MXC GPIO"); | |
379 | MODULE_LICENSE("GPL"); |