i.MX31/Gigabeat S minor cleaning: Make HW access more obvious in places I forgot...
[kugel-rb.git] / firmware / target / arm / imx31 / gpio-imx31.c
blob42d0a42188ff1f36a7c8e31138aca6e3414925ce
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
10 * Copyright (c) 2008 by Michael Sevakis
12 * IMX31 GPIO event manager
14 * This program is free software; you can redistribute it and/or
15 * modify it under the terms of the GNU General Public License
16 * as published by the Free Software Foundation; either version 2
17 * of the License, or (at your option) any later version.
19 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
20 * KIND, either express or implied.
22 ****************************************************************************/
23 #include "config.h"
24 #include "system.h"
25 #include "avic-imx31.h"
26 #include "gpio-imx31.h"
28 /* UIE vector found in avic-imx31.c */
29 extern void UIE_VECTOR(void);
31 /* Event lists are allocated for the specific target */
32 #if (GPIO_EVENT_MASK & USE_GPIO1_EVENTS)
33 static __attribute__((interrupt("IRQ"))) void GPIO1_HANDLER(void);
34 extern const struct gpio_event gpio1_events[GPIO1_NUM_EVENTS];
35 #endif
37 #if (GPIO_EVENT_MASK & USE_GPIO2_EVENTS)
38 static __attribute__((interrupt("IRQ"))) void GPIO2_HANDLER(void);
39 extern const struct gpio_event gpio2_events[GPIO2_NUM_EVENTS];
40 #endif
42 #if (GPIO_EVENT_MASK & USE_GPIO3_EVENTS)
43 static __attribute__((interrupt("IRQ"))) void GPIO3_HANDLER(void);
44 extern const struct gpio_event gpio3_events[GPIO3_NUM_EVENTS];
45 #endif
47 #define DR (0x00 / sizeof (unsigned long)) /* 00h */
48 #define GDIR (0x04 / sizeof (unsigned long)) /* 04h */
49 #define PSR (0x08 / sizeof (unsigned long)) /* 08h */
50 #define ICR (0x0C / sizeof (unsigned long)) /* 0Ch ICR1,2 */
51 #define IMR (0x14 / sizeof (unsigned long)) /* 14h */
52 #define ISR (0x18 / sizeof (unsigned long))
54 static const struct gpio_module_desc
56 volatile unsigned long * const base; /* Module base address */
57 void (* const handler)(void); /* Interrupt function */
58 const struct gpio_event * const events; /* Event handler list */
59 const uint8_t ints; /* AVIC int number */
60 const uint8_t int_priority; /* AVIC int priority */
61 const uint8_t count; /* Number of events */
62 } gpio_descs[GPIO_NUM_GPIO] =
64 #if (GPIO_EVENT_MASK & USE_GPIO1_EVENTS)
66 .base = (unsigned long *)GPIO1_BASE_ADDR,
67 .ints = INT_GPIO1,
68 .handler = GPIO1_HANDLER,
69 .events = gpio1_events,
70 .count = GPIO1_NUM_EVENTS,
71 .int_priority = GPIO1_INT_PRIO
73 #endif
74 #if (GPIO_EVENT_MASK & USE_GPIO2_EVENTS)
76 .base = (unsigned long *)GPIO2_BASE_ADDR,
77 .ints = INT_GPIO2,
78 .handler = GPIO2_HANDLER,
79 .events = gpio2_events,
80 .count = GPIO2_NUM_EVENTS,
81 .int_priority = GPIO2_INT_PRIO
83 #endif
84 #if (GPIO_EVENT_MASK & USE_GPIO3_EVENTS)
86 .base = (unsigned long *)GPIO3_BASE_ADDR,
87 .ints = INT_GPIO3,
88 .handler = GPIO3_HANDLER,
89 .events = gpio3_events,
90 .count = GPIO3_NUM_EVENTS,
91 .int_priority = GPIO3_INT_PRIO,
93 #endif
96 static void gpio_call_events(enum gpio_module_number gpio)
98 const struct gpio_module_desc * const desc = &gpio_descs[gpio];
99 volatile unsigned long * const base = desc->base;
100 const struct gpio_event * event, *event_last;
102 event = desc->events;
103 event_last = event + desc->count;
105 /* Intersect pending and unmasked bits */
106 unsigned long pnd = base[ISR] & base[IMR];
108 /* Call each event handler in order */
109 /* .count is surely expected to be > 0 */
112 unsigned long mask = event->mask;
114 if (pnd & mask)
116 event->callback();
117 pnd &= ~mask;
120 if (pnd == 0)
121 break; /* Teminate early if nothing more to service */
123 while (++event < event_last);
125 if (pnd != 0)
127 /* One or more weren't handled */
128 UIE_VECTOR();
132 #if (GPIO_EVENT_MASK & USE_GPIO1_EVENTS)
133 static __attribute__((interrupt("IRQ"))) void GPIO1_HANDLER(void)
135 gpio_call_events(GPIO1_NUM);
137 #endif
139 #if (GPIO_EVENT_MASK & USE_GPIO2_EVENTS)
140 static __attribute__((interrupt("IRQ"))) void GPIO2_HANDLER(void)
142 gpio_call_events(GPIO2_NUM);
144 #endif
146 #if (GPIO_EVENT_MASK & USE_GPIO3_EVENTS)
147 static __attribute__((interrupt("IRQ"))) void GPIO3_HANDLER(void)
149 gpio_call_events(GPIO3_NUM);
151 #endif
153 void gpio_init(void)
155 /* Mask-out GPIO interrupts - enable what's wanted later */
156 int i;
157 for (i = 0; i < GPIO_NUM_GPIO; i++)
158 gpio_descs[i].base[IMR] = 0;
161 bool gpio_enable_event(enum gpio_event_ids id)
163 const struct gpio_module_desc * const desc = &gpio_descs[id >> 5];
164 const struct gpio_event * const event = &desc->events[id & 31];
165 volatile unsigned long * const base = desc->base;
166 volatile unsigned long *icr;
167 unsigned long mask, line;
168 unsigned long imr;
169 int shift;
171 int oldlevel = disable_irq_save();
173 imr = base[IMR];
175 if (imr == 0)
177 /* First enabled interrupt for this GPIO */
178 avic_enable_int(desc->ints, INT_TYPE_IRQ, desc->int_priority,
179 desc->handler);
182 /* Set the line sense */
183 line = find_first_set_bit(event->mask);
184 icr = &base[ICR + (line >> 4)];
185 shift = 2*(line & 15);
186 mask = GPIO_SENSE_CONFIG_MASK << shift;
188 *icr = (*icr & ~mask) | ((event->sense << shift) & mask);
190 /* Unmask the line */
191 base[IMR] = imr | event->mask;
193 restore_irq(oldlevel);
195 return true;
198 void gpio_disable_event(enum gpio_event_ids id)
200 const struct gpio_module_desc * const desc = &gpio_descs[id >> 5];
201 const struct gpio_event * const event = &desc->events[id & 31];
202 volatile unsigned long * const base = desc->base;
203 unsigned long imr;
205 int oldlevel = disable_irq_save();
207 /* Remove bit from mask */
208 imr = base[IMR] & ~event->mask;
210 /* Mask the line */
211 base[IMR] = imr;
213 if (imr == 0)
215 /* No events remain enabled */
216 avic_disable_int(desc->ints);
219 restore_irq(oldlevel);