Michael Sevakis | a7af9e4 | 2008-04-12 16:56:45 +0000 | [diff] [blame] | 1 | /*************************************************************************** |
| 2 | * __________ __ ___. |
| 3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ |
| 4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / |
| 5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < |
| 6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ |
| 7 | * \/ \/ \/ \/ \/ |
| 8 | * $Id$ |
| 9 | * |
| 10 | * Copyright (c) 2008 by Michael Sevakis |
| 11 | * |
| 12 | * IMX31 GPIO event manager |
| 13 | * |
Daniel Stenberg | 2acc0ac | 2008-06-28 18:10:04 +0000 | [diff] [blame^] | 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. |
Michael Sevakis | a7af9e4 | 2008-04-12 16:56:45 +0000 | [diff] [blame] | 18 | * |
| 19 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY |
| 20 | * KIND, either express or implied. |
| 21 | * |
| 22 | ****************************************************************************/ |
| 23 | #include "config.h" |
| 24 | #include "system.h" |
| 25 | #include "avic-imx31.h" |
| 26 | #include "gpio-imx31.h" |
| 27 | |
| 28 | /* UIE vector found in avic-imx31.c */ |
| 29 | extern void UIE_VECTOR(void); |
| 30 | |
| 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_list gpio1_event_list; |
| 35 | #endif |
| 36 | |
| 37 | #if (GPIO_EVENT_MASK & USE_GPIO2_EVENTS) |
| 38 | static __attribute__((interrupt("IRQ"))) void GPIO2_HANDLER(void); |
| 39 | extern const struct gpio_event_list gpio2_event_list; |
| 40 | #endif |
| 41 | |
| 42 | #if (GPIO_EVENT_MASK & USE_GPIO3_EVENTS) |
| 43 | static __attribute__((interrupt("IRQ"))) void GPIO3_HANDLER(void); |
| 44 | extern const struct gpio_event_list gpio3_event_list; |
| 45 | #endif |
| 46 | |
| 47 | static struct gpio_module_descriptor |
| 48 | { |
Michael Sevakis | 88451d5 | 2008-04-27 21:32:10 +0000 | [diff] [blame] | 49 | struct gpio_map * const base; /* Module base address */ |
Michael Sevakis | a7af9e4 | 2008-04-12 16:56:45 +0000 | [diff] [blame] | 50 | enum IMX31_INT_LIST ints; /* AVIC int number */ |
| 51 | void (*handler)(void); /* Interrupt function */ |
| 52 | const struct gpio_event_list *list; /* Event handler list */ |
| 53 | } gpio_descs[GPIO_NUM_GPIO] = |
| 54 | { |
| 55 | #if (GPIO_EVENT_MASK & USE_GPIO1_EVENTS) |
| 56 | { |
Michael Sevakis | 88451d5 | 2008-04-27 21:32:10 +0000 | [diff] [blame] | 57 | .base = (struct gpio_map *)GPIO1_BASE_ADDR, |
Michael Sevakis | a7af9e4 | 2008-04-12 16:56:45 +0000 | [diff] [blame] | 58 | .ints = GPIO1, |
| 59 | .handler = GPIO1_HANDLER, |
| 60 | }, |
| 61 | #endif |
| 62 | #if (GPIO_EVENT_MASK & USE_GPIO2_EVENTS) |
| 63 | { |
Michael Sevakis | 88451d5 | 2008-04-27 21:32:10 +0000 | [diff] [blame] | 64 | .base = (struct gpio_map *)GPIO2_BASE_ADDR, |
Michael Sevakis | a7af9e4 | 2008-04-12 16:56:45 +0000 | [diff] [blame] | 65 | .ints = GPIO2, |
| 66 | .handler = GPIO2_HANDLER, |
| 67 | }, |
| 68 | #endif |
| 69 | #if (GPIO_EVENT_MASK & USE_GPIO3_EVENTS) |
| 70 | { |
Michael Sevakis | 88451d5 | 2008-04-27 21:32:10 +0000 | [diff] [blame] | 71 | .base = (struct gpio_map *)GPIO3_BASE_ADDR, |
Michael Sevakis | a7af9e4 | 2008-04-12 16:56:45 +0000 | [diff] [blame] | 72 | .ints = GPIO3, |
| 73 | .handler = GPIO3_HANDLER, |
| 74 | }, |
| 75 | #endif |
| 76 | }; |
| 77 | |
Michael Sevakis | a9c20f5 | 2008-05-21 08:42:11 +0000 | [diff] [blame] | 78 | static void gpio_call_events(const struct gpio_module_descriptor * const desc) |
Michael Sevakis | a7af9e4 | 2008-04-12 16:56:45 +0000 | [diff] [blame] | 79 | { |
Michael Sevakis | a7af9e4 | 2008-04-12 16:56:45 +0000 | [diff] [blame] | 80 | const struct gpio_event_list * const list = desc->list; |
Michael Sevakis | 88451d5 | 2008-04-27 21:32:10 +0000 | [diff] [blame] | 81 | struct gpio_map * const base = desc->base; |
Michael Sevakis | a9c20f5 | 2008-05-21 08:42:11 +0000 | [diff] [blame] | 82 | const struct gpio_event * event, *event_last; |
Michael Sevakis | a7af9e4 | 2008-04-12 16:56:45 +0000 | [diff] [blame] | 83 | |
| 84 | /* Intersect pending and unmasked bits */ |
Michael Sevakis | a9c20f5 | 2008-05-21 08:42:11 +0000 | [diff] [blame] | 85 | uint32_t pnd = base->isr & base->imr; |
| 86 | |
| 87 | event = list->events; |
| 88 | event_last = event + list->count; |
Michael Sevakis | a7af9e4 | 2008-04-12 16:56:45 +0000 | [diff] [blame] | 89 | |
| 90 | /* Call each event handler in order */ |
Michael Sevakis | a9c20f5 | 2008-05-21 08:42:11 +0000 | [diff] [blame] | 91 | /* .count is surely expected to be > 0 */ |
| 92 | do |
Michael Sevakis | a7af9e4 | 2008-04-12 16:56:45 +0000 | [diff] [blame] | 93 | { |
Michael Sevakis | a9c20f5 | 2008-05-21 08:42:11 +0000 | [diff] [blame] | 94 | uint32_t mask = event->mask; |
Michael Sevakis | a7af9e4 | 2008-04-12 16:56:45 +0000 | [diff] [blame] | 95 | |
Michael Sevakis | a9c20f5 | 2008-05-21 08:42:11 +0000 | [diff] [blame] | 96 | if (pnd & mask) |
| 97 | { |
| 98 | event->callback(); |
| 99 | pnd &= ~mask; |
| 100 | } |
| 101 | |
| 102 | if (pnd == 0) |
| 103 | break; /* Teminate early if nothing more to service */ |
Michael Sevakis | a7af9e4 | 2008-04-12 16:56:45 +0000 | [diff] [blame] | 104 | } |
Michael Sevakis | a9c20f5 | 2008-05-21 08:42:11 +0000 | [diff] [blame] | 105 | while (++event < event_last); |
Michael Sevakis | a7af9e4 | 2008-04-12 16:56:45 +0000 | [diff] [blame] | 106 | |
Michael Sevakis | a9c20f5 | 2008-05-21 08:42:11 +0000 | [diff] [blame] | 107 | if (pnd != 0) |
Michael Sevakis | a7af9e4 | 2008-04-12 16:56:45 +0000 | [diff] [blame] | 108 | { |
Michael Sevakis | a9c20f5 | 2008-05-21 08:42:11 +0000 | [diff] [blame] | 109 | /* One or more weren't handled */ |
Michael Sevakis | a7af9e4 | 2008-04-12 16:56:45 +0000 | [diff] [blame] | 110 | UIE_VECTOR(); |
| 111 | } |
| 112 | } |
| 113 | |
| 114 | #if (GPIO_EVENT_MASK & USE_GPIO1_EVENTS) |
| 115 | static __attribute__((interrupt("IRQ"))) void GPIO1_HANDLER(void) |
| 116 | { |
Michael Sevakis | a9c20f5 | 2008-05-21 08:42:11 +0000 | [diff] [blame] | 117 | gpio_call_events(&gpio_descs[GPIO1_NUM]); |
Michael Sevakis | a7af9e4 | 2008-04-12 16:56:45 +0000 | [diff] [blame] | 118 | } |
| 119 | #endif |
| 120 | |
| 121 | #if (GPIO_EVENT_MASK & USE_GPIO2_EVENTS) |
| 122 | static __attribute__((interrupt("IRQ"))) void GPIO2_HANDLER(void) |
| 123 | { |
Michael Sevakis | a9c20f5 | 2008-05-21 08:42:11 +0000 | [diff] [blame] | 124 | gpio_call_events(&gpio_descs[GPIO2_NUM]); |
Michael Sevakis | a7af9e4 | 2008-04-12 16:56:45 +0000 | [diff] [blame] | 125 | } |
| 126 | #endif |
| 127 | |
| 128 | #if (GPIO_EVENT_MASK & USE_GPIO3_EVENTS) |
| 129 | static __attribute__((interrupt("IRQ"))) void GPIO3_HANDLER(void) |
| 130 | { |
Michael Sevakis | a9c20f5 | 2008-05-21 08:42:11 +0000 | [diff] [blame] | 131 | gpio_call_events(&gpio_descs[GPIO3_NUM]); |
Michael Sevakis | a7af9e4 | 2008-04-12 16:56:45 +0000 | [diff] [blame] | 132 | } |
| 133 | #endif |
| 134 | |
| 135 | void gpio_init(void) |
| 136 | { |
| 137 | /* Mask-out GPIO interrupts - enable what's wanted later */ |
| 138 | GPIO1_IMR = 0; |
| 139 | GPIO2_IMR = 0; |
| 140 | GPIO3_IMR = 0; |
| 141 | |
| 142 | /* Init the externally-defined event lists for each port */ |
| 143 | #if (GPIO_EVENT_MASK & USE_GPIO1_EVENTS) |
| 144 | gpio_descs[GPIO1_NUM].list = &gpio1_event_list; |
| 145 | #endif |
| 146 | #if (GPIO_EVENT_MASK & USE_GPIO2_EVENTS) |
| 147 | gpio_descs[GPIO2_NUM].list = &gpio2_event_list; |
| 148 | #endif |
| 149 | #if (GPIO_EVENT_MASK & USE_GPIO3_EVENTS) |
| 150 | gpio_descs[GPIO3_NUM].list = &gpio3_event_list; |
| 151 | #endif |
| 152 | } |
| 153 | |
Michael Sevakis | a9c20f5 | 2008-05-21 08:42:11 +0000 | [diff] [blame] | 154 | bool gpio_enable_event(enum gpio_event_ids id) |
Michael Sevakis | a7af9e4 | 2008-04-12 16:56:45 +0000 | [diff] [blame] | 155 | { |
Michael Sevakis | a9c20f5 | 2008-05-21 08:42:11 +0000 | [diff] [blame] | 156 | const struct gpio_module_descriptor * const desc = &gpio_descs[id >> 5]; |
| 157 | const struct gpio_event * const event = &desc->list->events[id & 31]; |
Michael Sevakis | 88451d5 | 2008-04-27 21:32:10 +0000 | [diff] [blame] | 158 | struct gpio_map * const base = desc->base; |
| 159 | volatile uint32_t *icr; |
Michael Sevakis | a9c20f5 | 2008-05-21 08:42:11 +0000 | [diff] [blame] | 160 | uint32_t mask, line; |
Michael Sevakis | 88451d5 | 2008-04-27 21:32:10 +0000 | [diff] [blame] | 161 | uint32_t imr; |
Michael Sevakis | a7af9e4 | 2008-04-12 16:56:45 +0000 | [diff] [blame] | 162 | int shift; |
| 163 | |
Michael Sevakis | a7af9e4 | 2008-04-12 16:56:45 +0000 | [diff] [blame] | 164 | int oldlevel = disable_irq_save(); |
| 165 | |
Michael Sevakis | 88451d5 | 2008-04-27 21:32:10 +0000 | [diff] [blame] | 166 | imr = base->imr; |
Michael Sevakis | a7af9e4 | 2008-04-12 16:56:45 +0000 | [diff] [blame] | 167 | |
| 168 | if (imr == 0) |
| 169 | { |
| 170 | /* First enabled interrupt for this GPIO */ |
Michael Sevakis | a9c20f5 | 2008-05-21 08:42:11 +0000 | [diff] [blame] | 171 | avic_enable_int(desc->ints, IRQ, desc->list->ints_priority, |
Michael Sevakis | a7af9e4 | 2008-04-12 16:56:45 +0000 | [diff] [blame] | 172 | desc->handler); |
| 173 | } |
| 174 | |
| 175 | /* Set the line sense */ |
Michael Sevakis | a9c20f5 | 2008-05-21 08:42:11 +0000 | [diff] [blame] | 176 | line = find_first_set_bit(event->mask); |
| 177 | icr = &base->icr[line >> 4]; |
| 178 | shift = (line & 15) << 1; |
Michael Sevakis | a7af9e4 | 2008-04-12 16:56:45 +0000 | [diff] [blame] | 179 | mask = GPIO_SENSE_CONFIG_MASK << shift; |
| 180 | |
| 181 | *icr = (*icr & ~mask) | ((event->sense << shift) & mask); |
| 182 | |
| 183 | /* Unmask the line */ |
Michael Sevakis | a9c20f5 | 2008-05-21 08:42:11 +0000 | [diff] [blame] | 184 | base->imr = imr | event->mask; |
Michael Sevakis | a7af9e4 | 2008-04-12 16:56:45 +0000 | [diff] [blame] | 185 | |
| 186 | restore_irq(oldlevel); |
| 187 | |
| 188 | return true; |
| 189 | } |
| 190 | |
Michael Sevakis | a9c20f5 | 2008-05-21 08:42:11 +0000 | [diff] [blame] | 191 | void gpio_disable_event(enum gpio_event_ids id) |
Michael Sevakis | a7af9e4 | 2008-04-12 16:56:45 +0000 | [diff] [blame] | 192 | { |
Michael Sevakis | a9c20f5 | 2008-05-21 08:42:11 +0000 | [diff] [blame] | 193 | const struct gpio_module_descriptor * const desc = &gpio_descs[id >> 5]; |
| 194 | const struct gpio_event * const event = &desc->list->events[id & 31]; |
Michael Sevakis | 88451d5 | 2008-04-27 21:32:10 +0000 | [diff] [blame] | 195 | struct gpio_map * const base = desc->base; |
| 196 | uint32_t imr; |
Michael Sevakis | a7af9e4 | 2008-04-12 16:56:45 +0000 | [diff] [blame] | 197 | |
Michael Sevakis | a7af9e4 | 2008-04-12 16:56:45 +0000 | [diff] [blame] | 198 | int oldlevel = disable_irq_save(); |
| 199 | |
| 200 | /* Remove bit from mask */ |
Michael Sevakis | a9c20f5 | 2008-05-21 08:42:11 +0000 | [diff] [blame] | 201 | imr = base->imr & ~event->mask; |
Michael Sevakis | a7af9e4 | 2008-04-12 16:56:45 +0000 | [diff] [blame] | 202 | |
| 203 | /* Mask the line */ |
Michael Sevakis | 88451d5 | 2008-04-27 21:32:10 +0000 | [diff] [blame] | 204 | base->imr = imr; |
Michael Sevakis | a7af9e4 | 2008-04-12 16:56:45 +0000 | [diff] [blame] | 205 | |
| 206 | if (imr == 0) |
| 207 | { |
| 208 | /* No events remain enabled */ |
| 209 | avic_disable_int(desc->ints); |
| 210 | } |
| 211 | |
| 212 | restore_irq(oldlevel); |
| 213 | } |