diff options
author | Haavard Skinnemoen <hskinnemoen@atmel.com> | 2007-02-05 10:57:13 -0500 |
---|---|---|
committer | Haavard Skinnemoen <hskinnemoen@atmel.com> | 2007-02-09 09:01:58 -0500 |
commit | 6a4e5227a33f60f918b30cf2001fb0bb259d9396 (patch) | |
tree | a3e8d130fdfb88710872afc9908e1e78644b300f | |
parent | 10b50b7dd2716b944299d45452d0875dbeb5f0c2 (diff) |
[AVR32] GPIO API implementation
Arch-neutral GPIO calls for AVR32. GPIO IRQ support written by
David Brownell.
Signed-off-by: Haavard Skinnemoen <hskinnemoen@atmel.com>
-rw-r--r-- | arch/avr32/mach-at32ap/Makefile | 2 | ||||
-rw-r--r-- | arch/avr32/mach-at32ap/at32ap7000.c | 2 | ||||
-rw-r--r-- | arch/avr32/mach-at32ap/pio.c | 233 | ||||
-rw-r--r-- | include/asm-avr32/arch-at32ap/gpio.h | 27 | ||||
-rw-r--r-- | include/asm-avr32/arch-at32ap/irq.h | 14 | ||||
-rw-r--r-- | include/asm-avr32/arch-at32ap/portmux.h | 7 | ||||
-rw-r--r-- | include/asm-avr32/gpio.h | 6 | ||||
-rw-r--r-- | include/asm-avr32/irq.h | 8 |
8 files changed, 278 insertions, 21 deletions
diff --git a/arch/avr32/mach-at32ap/Makefile b/arch/avr32/mach-at32ap/Makefile index f62eb6915510..b21bea9af8b1 100644 --- a/arch/avr32/mach-at32ap/Makefile +++ b/arch/avr32/mach-at32ap/Makefile | |||
@@ -1,2 +1,2 @@ | |||
1 | obj-y += at32ap.o clock.o pio.o intc.o extint.o hsmc.o | 1 | obj-y += at32ap.o clock.o intc.o extint.o pio.o hsmc.o |
2 | obj-$(CONFIG_CPU_AT32AP7000) += at32ap7000.o | 2 | obj-$(CONFIG_CPU_AT32AP7000) += at32ap7000.o |
diff --git a/arch/avr32/mach-at32ap/at32ap7000.c b/arch/avr32/mach-at32ap/at32ap7000.c index 175853a3c32b..21561ab60471 100644 --- a/arch/avr32/mach-at32ap/at32ap7000.c +++ b/arch/avr32/mach-at32ap/at32ap7000.c | |||
@@ -498,7 +498,7 @@ DEV_CLK(mck, pio3, pba, 13); | |||
498 | 498 | ||
499 | void __init at32_add_system_devices(void) | 499 | void __init at32_add_system_devices(void) |
500 | { | 500 | { |
501 | system_manager.eim_first_irq = NR_INTERNAL_IRQS; | 501 | system_manager.eim_first_irq = EIM_IRQ_BASE; |
502 | 502 | ||
503 | platform_device_register(&at32_sm_device); | 503 | platform_device_register(&at32_sm_device); |
504 | platform_device_register(&at32_intc0_device); | 504 | platform_device_register(&at32_intc0_device); |
diff --git a/arch/avr32/mach-at32ap/pio.c b/arch/avr32/mach-at32ap/pio.c index f1280ed8ed6d..17e835de95cb 100644 --- a/arch/avr32/mach-at32ap/pio.c +++ b/arch/avr32/mach-at32ap/pio.c | |||
@@ -12,7 +12,9 @@ | |||
12 | #include <linux/debugfs.h> | 12 | #include <linux/debugfs.h> |
13 | #include <linux/fs.h> | 13 | #include <linux/fs.h> |
14 | #include <linux/platform_device.h> | 14 | #include <linux/platform_device.h> |
15 | #include <linux/irq.h> | ||
15 | 16 | ||
17 | #include <asm/gpio.h> | ||
16 | #include <asm/io.h> | 18 | #include <asm/io.h> |
17 | 19 | ||
18 | #include <asm/arch/portmux.h> | 20 | #include <asm/arch/portmux.h> |
@@ -26,7 +28,8 @@ struct pio_device { | |||
26 | const struct platform_device *pdev; | 28 | const struct platform_device *pdev; |
27 | struct clk *clk; | 29 | struct clk *clk; |
28 | u32 pinmux_mask; | 30 | u32 pinmux_mask; |
29 | char name[32]; | 31 | u32 gpio_mask; |
32 | char name[8]; | ||
30 | }; | 33 | }; |
31 | 34 | ||
32 | static struct pio_device pio_dev[MAX_NR_PIO_DEVICES]; | 35 | static struct pio_device pio_dev[MAX_NR_PIO_DEVICES]; |
@@ -76,6 +79,9 @@ void __init at32_select_periph(unsigned int pin, unsigned int periph, | |||
76 | if (!(flags & AT32_GPIOF_PULLUP)) | 79 | if (!(flags & AT32_GPIOF_PULLUP)) |
77 | pio_writel(pio, PUDR, mask); | 80 | pio_writel(pio, PUDR, mask); |
78 | 81 | ||
82 | /* gpio_request NOT allowed */ | ||
83 | set_bit(pin_index, &pio->gpio_mask); | ||
84 | |||
79 | return; | 85 | return; |
80 | 86 | ||
81 | fail: | 87 | fail: |
@@ -99,19 +105,29 @@ void __init at32_select_gpio(unsigned int pin, unsigned long flags) | |||
99 | goto fail; | 105 | goto fail; |
100 | } | 106 | } |
101 | 107 | ||
102 | pio_writel(pio, PUER, mask); | 108 | if (flags & AT32_GPIOF_OUTPUT) { |
103 | if (flags & AT32_GPIOF_HIGH) | 109 | if (flags & AT32_GPIOF_HIGH) |
104 | pio_writel(pio, SODR, mask); | 110 | pio_writel(pio, SODR, mask); |
105 | else | 111 | else |
106 | pio_writel(pio, CODR, mask); | 112 | pio_writel(pio, CODR, mask); |
107 | if (flags & AT32_GPIOF_OUTPUT) | 113 | pio_writel(pio, PUDR, mask); |
108 | pio_writel(pio, OER, mask); | 114 | pio_writel(pio, OER, mask); |
109 | else | 115 | } else { |
116 | if (flags & AT32_GPIOF_PULLUP) | ||
117 | pio_writel(pio, PUER, mask); | ||
118 | else | ||
119 | pio_writel(pio, PUDR, mask); | ||
120 | if (flags & AT32_GPIOF_DEGLITCH) | ||
121 | pio_writel(pio, IFER, mask); | ||
122 | else | ||
123 | pio_writel(pio, IFDR, mask); | ||
110 | pio_writel(pio, ODR, mask); | 124 | pio_writel(pio, ODR, mask); |
125 | } | ||
111 | 126 | ||
112 | pio_writel(pio, PER, mask); | 127 | pio_writel(pio, PER, mask); |
113 | if (!(flags & AT32_GPIOF_PULLUP)) | 128 | |
114 | pio_writel(pio, PUDR, mask); | 129 | /* gpio_request now allowed */ |
130 | clear_bit(pin_index, &pio->gpio_mask); | ||
115 | 131 | ||
116 | return; | 132 | return; |
117 | 133 | ||
@@ -119,20 +135,199 @@ fail: | |||
119 | dump_stack(); | 135 | dump_stack(); |
120 | } | 136 | } |
121 | 137 | ||
138 | /*--------------------------------------------------------------------------*/ | ||
139 | |||
140 | /*--------------------------------------------------------------------------*/ | ||
141 | |||
142 | /* GPIO API */ | ||
143 | |||
144 | int gpio_request(unsigned int gpio, const char *label) | ||
145 | { | ||
146 | struct pio_device *pio; | ||
147 | unsigned int pin; | ||
148 | |||
149 | pio = gpio_to_pio(gpio); | ||
150 | if (!pio) | ||
151 | return -ENODEV; | ||
152 | |||
153 | pin = gpio & 0x1f; | ||
154 | if (test_and_set_bit(pin, &pio->gpio_mask)) | ||
155 | return -EBUSY; | ||
156 | |||
157 | return 0; | ||
158 | } | ||
159 | EXPORT_SYMBOL(gpio_request); | ||
160 | |||
161 | void gpio_free(unsigned int gpio) | ||
162 | { | ||
163 | struct pio_device *pio; | ||
164 | unsigned int pin; | ||
165 | |||
166 | pio = gpio_to_pio(gpio); | ||
167 | if (!pio) { | ||
168 | printk(KERN_ERR | ||
169 | "gpio: attempted to free invalid pin %u\n", gpio); | ||
170 | return; | ||
171 | } | ||
172 | |||
173 | pin = gpio & 0x1f; | ||
174 | if (!test_and_clear_bit(pin, &pio->gpio_mask)) | ||
175 | printk(KERN_ERR "gpio: freeing free or non-gpio pin %s-%u\n", | ||
176 | pio->name, pin); | ||
177 | } | ||
178 | EXPORT_SYMBOL(gpio_free); | ||
179 | |||
180 | int gpio_direction_input(unsigned int gpio) | ||
181 | { | ||
182 | struct pio_device *pio; | ||
183 | unsigned int pin; | ||
184 | |||
185 | pio = gpio_to_pio(gpio); | ||
186 | if (!pio) | ||
187 | return -ENODEV; | ||
188 | |||
189 | pin = gpio & 0x1f; | ||
190 | pio_writel(pio, ODR, 1 << pin); | ||
191 | |||
192 | return 0; | ||
193 | } | ||
194 | EXPORT_SYMBOL(gpio_direction_input); | ||
195 | |||
196 | int gpio_direction_output(unsigned int gpio) | ||
197 | { | ||
198 | struct pio_device *pio; | ||
199 | unsigned int pin; | ||
200 | |||
201 | pio = gpio_to_pio(gpio); | ||
202 | if (!pio) | ||
203 | return -ENODEV; | ||
204 | |||
205 | pin = gpio & 0x1f; | ||
206 | pio_writel(pio, OER, 1 << pin); | ||
207 | |||
208 | return 0; | ||
209 | } | ||
210 | EXPORT_SYMBOL(gpio_direction_output); | ||
211 | |||
212 | int gpio_get_value(unsigned int gpio) | ||
213 | { | ||
214 | struct pio_device *pio = &pio_dev[gpio >> 5]; | ||
215 | |||
216 | return (pio_readl(pio, PDSR) >> (gpio & 0x1f)) & 1; | ||
217 | } | ||
218 | EXPORT_SYMBOL(gpio_get_value); | ||
219 | |||
220 | void gpio_set_value(unsigned int gpio, int value) | ||
221 | { | ||
222 | struct pio_device *pio = &pio_dev[gpio >> 5]; | ||
223 | u32 mask; | ||
224 | |||
225 | mask = 1 << (gpio & 0x1f); | ||
226 | if (value) | ||
227 | pio_writel(pio, SODR, mask); | ||
228 | else | ||
229 | pio_writel(pio, CODR, mask); | ||
230 | } | ||
231 | EXPORT_SYMBOL(gpio_set_value); | ||
232 | |||
233 | /*--------------------------------------------------------------------------*/ | ||
234 | |||
235 | /* GPIO IRQ support */ | ||
236 | |||
237 | static void gpio_irq_mask(unsigned irq) | ||
238 | { | ||
239 | unsigned gpio = irq_to_gpio(irq); | ||
240 | struct pio_device *pio = &pio_dev[gpio >> 5]; | ||
241 | |||
242 | pio_writel(pio, IDR, 1 << (gpio & 0x1f)); | ||
243 | } | ||
244 | |||
245 | static void gpio_irq_unmask(unsigned irq) | ||
246 | { | ||
247 | unsigned gpio = irq_to_gpio(irq); | ||
248 | struct pio_device *pio = &pio_dev[gpio >> 5]; | ||
249 | |||
250 | pio_writel(pio, IER, 1 << (gpio & 0x1f)); | ||
251 | } | ||
252 | |||
253 | static int gpio_irq_type(unsigned irq, unsigned type) | ||
254 | { | ||
255 | if (type != IRQ_TYPE_EDGE_BOTH && type != IRQ_TYPE_NONE) | ||
256 | return -EINVAL; | ||
257 | |||
258 | return 0; | ||
259 | } | ||
260 | |||
261 | static struct irq_chip gpio_irqchip = { | ||
262 | .name = "gpio", | ||
263 | .mask = gpio_irq_mask, | ||
264 | .unmask = gpio_irq_unmask, | ||
265 | .set_type = gpio_irq_type, | ||
266 | }; | ||
267 | |||
268 | static void gpio_irq_handler(unsigned irq, struct irq_desc *desc) | ||
269 | { | ||
270 | struct pio_device *pio = get_irq_chip_data(irq); | ||
271 | unsigned gpio_irq; | ||
272 | |||
273 | gpio_irq = (unsigned) get_irq_data(irq); | ||
274 | for (;;) { | ||
275 | u32 isr; | ||
276 | struct irq_desc *d; | ||
277 | |||
278 | /* ack pending GPIO interrupts */ | ||
279 | isr = pio_readl(pio, ISR) & pio_readl(pio, IMR); | ||
280 | if (!isr) | ||
281 | break; | ||
282 | do { | ||
283 | int i; | ||
284 | |||
285 | i = ffs(isr) - 1; | ||
286 | isr &= ~(1 << i); | ||
287 | |||
288 | i += gpio_irq; | ||
289 | d = &irq_desc[i]; | ||
290 | |||
291 | d->handle_irq(i, d); | ||
292 | } while (isr); | ||
293 | } | ||
294 | } | ||
295 | |||
296 | static void __init | ||
297 | gpio_irq_setup(struct pio_device *pio, int irq, int gpio_irq) | ||
298 | { | ||
299 | unsigned i; | ||
300 | |||
301 | set_irq_chip_data(irq, pio); | ||
302 | set_irq_data(irq, (void *) gpio_irq); | ||
303 | |||
304 | for (i = 0; i < 32; i++, gpio_irq++) { | ||
305 | set_irq_chip_data(gpio_irq, pio); | ||
306 | set_irq_chip_and_handler(gpio_irq, &gpio_irqchip, | ||
307 | handle_simple_irq); | ||
308 | } | ||
309 | |||
310 | set_irq_chained_handler(irq, gpio_irq_handler); | ||
311 | } | ||
312 | |||
313 | /*--------------------------------------------------------------------------*/ | ||
314 | |||
122 | static int __init pio_probe(struct platform_device *pdev) | 315 | static int __init pio_probe(struct platform_device *pdev) |
123 | { | 316 | { |
124 | struct pio_device *pio = NULL; | 317 | struct pio_device *pio = NULL; |
318 | int irq = platform_get_irq(pdev, 0); | ||
319 | int gpio_irq_base = GPIO_IRQ_BASE + pdev->id * 32; | ||
125 | 320 | ||
126 | BUG_ON(pdev->id >= MAX_NR_PIO_DEVICES); | 321 | BUG_ON(pdev->id >= MAX_NR_PIO_DEVICES); |
127 | pio = &pio_dev[pdev->id]; | 322 | pio = &pio_dev[pdev->id]; |
128 | BUG_ON(!pio->regs); | 323 | BUG_ON(!pio->regs); |
129 | 324 | ||
130 | /* TODO: Interrupts */ | 325 | gpio_irq_setup(pio, irq, gpio_irq_base); |
131 | 326 | ||
132 | platform_set_drvdata(pdev, pio); | 327 | platform_set_drvdata(pdev, pio); |
133 | 328 | ||
134 | printk(KERN_INFO "%s: Atmel Port Multiplexer at 0x%p (irq %d)\n", | 329 | printk(KERN_DEBUG "%s: base 0x%p, irq %d chains %d..%d\n", |
135 | pio->name, pio->regs, platform_get_irq(pdev, 0)); | 330 | pio->name, pio->regs, irq, gpio_irq_base, gpio_irq_base + 31); |
136 | 331 | ||
137 | return 0; | 332 | return 0; |
138 | } | 333 | } |
@@ -148,7 +343,7 @@ static int __init pio_init(void) | |||
148 | { | 343 | { |
149 | return platform_driver_register(&pio_driver); | 344 | return platform_driver_register(&pio_driver); |
150 | } | 345 | } |
151 | subsys_initcall(pio_init); | 346 | postcore_initcall(pio_init); |
152 | 347 | ||
153 | void __init at32_init_pio(struct platform_device *pdev) | 348 | void __init at32_init_pio(struct platform_device *pdev) |
154 | { | 349 | { |
@@ -184,6 +379,16 @@ void __init at32_init_pio(struct platform_device *pdev) | |||
184 | pio->pdev = pdev; | 379 | pio->pdev = pdev; |
185 | pio->regs = ioremap(regs->start, regs->end - regs->start + 1); | 380 | pio->regs = ioremap(regs->start, regs->end - regs->start + 1); |
186 | 381 | ||
382 | /* | ||
383 | * request_gpio() is only valid for pins that have been | ||
384 | * explicitly configured as GPIO and not previously requested | ||
385 | */ | ||
386 | pio->gpio_mask = ~0UL; | ||
387 | |||
187 | pio_writel(pio, ODR, ~0UL); | 388 | pio_writel(pio, ODR, ~0UL); |
188 | pio_writel(pio, PER, ~0UL); | 389 | pio_writel(pio, PER, ~0UL); |
390 | |||
391 | /* start with irqs disabled and acked */ | ||
392 | pio_writel(pio, IDR, ~0UL); | ||
393 | (void) pio_readl(pio, ISR); | ||
189 | } | 394 | } |
diff --git a/include/asm-avr32/arch-at32ap/gpio.h b/include/asm-avr32/arch-at32ap/gpio.h new file mode 100644 index 000000000000..fcb756bdaa8e --- /dev/null +++ b/include/asm-avr32/arch-at32ap/gpio.h | |||
@@ -0,0 +1,27 @@ | |||
1 | #ifndef __ASM_AVR32_ARCH_GPIO_H | ||
2 | #define __ASM_AVR32_ARCH_GPIO_H | ||
3 | |||
4 | #include <linux/compiler.h> | ||
5 | #include <asm/irq.h> | ||
6 | |||
7 | |||
8 | /* Arch-neutral GPIO API */ | ||
9 | int __must_check gpio_request(unsigned int gpio, const char *label); | ||
10 | void gpio_free(unsigned int gpio); | ||
11 | |||
12 | int gpio_direction_input(unsigned int gpio); | ||
13 | int gpio_direction_output(unsigned int gpio); | ||
14 | int gpio_get_value(unsigned int gpio); | ||
15 | void gpio_set_value(unsigned int gpio, int value); | ||
16 | |||
17 | static inline int gpio_to_irq(unsigned int gpio) | ||
18 | { | ||
19 | return gpio + GPIO_IRQ_BASE; | ||
20 | } | ||
21 | |||
22 | static inline int irq_to_gpio(unsigned int irq) | ||
23 | { | ||
24 | return irq - GPIO_IRQ_BASE; | ||
25 | } | ||
26 | |||
27 | #endif /* __ASM_AVR32_ARCH_GPIO_H */ | ||
diff --git a/include/asm-avr32/arch-at32ap/irq.h b/include/asm-avr32/arch-at32ap/irq.h new file mode 100644 index 000000000000..f8f7f4571e44 --- /dev/null +++ b/include/asm-avr32/arch-at32ap/irq.h | |||
@@ -0,0 +1,14 @@ | |||
1 | #ifndef __ASM_AVR32_ARCH_IRQ_H | ||
2 | #define __ASM_AVR32_ARCH_IRQ_H | ||
3 | |||
4 | #define EIM_IRQ_BASE NR_INTERNAL_IRQS | ||
5 | #define NR_EIM_IRQS 32 | ||
6 | |||
7 | #define AT32_EXTINT(n) (EIM_IRQ_BASE + (n)) | ||
8 | |||
9 | #define GPIO_IRQ_BASE (EIM_IRQ_BASE + NR_EIM_IRQS) | ||
10 | #define NR_GPIO_IRQS (4 * 32) | ||
11 | |||
12 | #define NR_IRQS (GPIO_IRQ_BASE + NR_GPIO_IRQS) | ||
13 | |||
14 | #endif /* __ASM_AVR32_ARCH_IRQ_H */ | ||
diff --git a/include/asm-avr32/arch-at32ap/portmux.h b/include/asm-avr32/arch-at32ap/portmux.h index 83c690571322..2ba611e0e134 100644 --- a/include/asm-avr32/arch-at32ap/portmux.h +++ b/include/asm-avr32/arch-at32ap/portmux.h | |||
@@ -15,9 +15,10 @@ | |||
15 | * | 15 | * |
16 | * The following flags determine the initial state of the pin. | 16 | * The following flags determine the initial state of the pin. |
17 | */ | 17 | */ |
18 | #define AT32_GPIOF_PULLUP 0x00000001 /* Enable pull-up */ | 18 | #define AT32_GPIOF_PULLUP 0x00000001 /* (not-OUT) Enable pull-up */ |
19 | #define AT32_GPIOF_OUTPUT 0x00000002 /* Enable output driver */ | 19 | #define AT32_GPIOF_OUTPUT 0x00000002 /* (OUT) Enable output driver */ |
20 | #define AT32_GPIOF_HIGH 0x00000004 /* Set output high */ | 20 | #define AT32_GPIOF_HIGH 0x00000004 /* (OUT) Set output high */ |
21 | #define AT32_GPIOF_DEGLITCH 0x00000008 /* (IN) Filter glitches */ | ||
21 | 22 | ||
22 | void at32_select_periph(unsigned int pin, unsigned int periph, | 23 | void at32_select_periph(unsigned int pin, unsigned int periph, |
23 | unsigned long flags); | 24 | unsigned long flags); |
diff --git a/include/asm-avr32/gpio.h b/include/asm-avr32/gpio.h new file mode 100644 index 000000000000..19e8ccc77db3 --- /dev/null +++ b/include/asm-avr32/gpio.h | |||
@@ -0,0 +1,6 @@ | |||
1 | #ifndef __ASM_AVR32_GPIO_H | ||
2 | #define __ASM_AVR32_GPIO_H | ||
3 | |||
4 | #include <asm/arch/gpio.h> | ||
5 | |||
6 | #endif /* __ASM_AVR32_GPIO_H */ | ||
diff --git a/include/asm-avr32/irq.h b/include/asm-avr32/irq.h index f7e725707dd7..83e6549d7783 100644 --- a/include/asm-avr32/irq.h +++ b/include/asm-avr32/irq.h | |||
@@ -2,8 +2,12 @@ | |||
2 | #define __ASM_AVR32_IRQ_H | 2 | #define __ASM_AVR32_IRQ_H |
3 | 3 | ||
4 | #define NR_INTERNAL_IRQS 64 | 4 | #define NR_INTERNAL_IRQS 64 |
5 | #define NR_EXTERNAL_IRQS 64 | 5 | |
6 | #define NR_IRQS (NR_INTERNAL_IRQS + NR_EXTERNAL_IRQS) | 6 | #include <asm/arch/irq.h> |
7 | |||
8 | #ifndef NR_IRQS | ||
9 | #define NR_IRQS (NR_INTERNAL_IRQS) | ||
10 | #endif | ||
7 | 11 | ||
8 | #define irq_canonicalize(i) (i) | 12 | #define irq_canonicalize(i) (i) |
9 | 13 | ||