1 /* Driver for Calypso IRQ controller */
3 /* (C) 2010 by Harald Welte <laforge@gnumonks.org>
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (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.
17 * You should have received a copy of the GNU General Public License along
18 * with this program; if not, write to the Free Software Foundation, Inc.,
19 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
29 #include <calypso/irq.h>
31 #define BASE_ADDR_IRQ 0xfffffa00
43 #define ILR_IRQ(x) (0x20 + (x*2))
44 #define IRQ_REG(x) ((void *)BASE_ADDR_IRQ + (x))
48 static uint8_t default_irq_prio[] = {
49 [IRQ_WATCHDOG] = 0xff,
54 [IRQ_TPU_PAGE] = 0xff,
57 [IRQ_KEYPAD_GPIO] = 4,
59 [IRQ_RTC_ALARM_I2C] = 10,
60 [IRQ_ULPD_GAUGING] = 2,
66 [IRQ_EXTERNAL_FIQ] = 7,
68 [IRQ_ULPD_GSM_TIMER] = 1,
72 static irq_handler *irq_handlers[NR_IRQS];
74 static void _irq_enable(enum irq_nr nr, int enable)
76 uint16_t *reg = IRQ_REG(MASK_IT_REG1);
80 reg = IRQ_REG(MASK_IT_REG2);
92 void irq_enable(enum irq_nr nr)
97 void irq_disable(enum irq_nr nr)
102 void irq_config(enum irq_nr nr, int fiq, int edge, int8_t prio)
107 prio = default_irq_prio[nr];
118 writew(val, IRQ_REG(ILR_IRQ(nr)));
121 /* Entry point for interrupts */
125 irq_handler *handler;
128 /* Hardware interrupt detection mode */
129 num = readb(IRQ_REG(IRQ_NUM)) & 0x1f;
131 printd("i%02x\n", num);
133 handler = irq_handlers[num];
138 /* Software interrupt detection mode */
140 uint16_t it_reg, mask_reg;
143 it_reg = readw(IRQ_REG(IT_REG1));
144 mask_reg = readw(IRQ_REG(MASK_IT_REG1));
145 irqs = it_reg & ~mask_reg;
147 it_reg = readw(IRQ_REG(IT_REG2));
148 mask_reg = readw(IRQ_REG(MASK_IT_REG2));
149 irqs |= (it_reg & ~mask_reg) << 16;
151 for (num = 0; num < 32; num++) {
152 if (irqs & (1 << num)) {
153 printd("i%d\n", num);
154 handler = irq_handlers[num];
157 /* clear this interrupt */
159 writew(~(1 << num), IRQ_REG(IT_REG1));
161 writew(~(1 << (num-16)), IRQ_REG(IT_REG2));
167 /* Start new IRQ agreement */
168 tmp = readb(IRQ_REG(IRQ_CTRL));
170 writeb(tmp, IRQ_REG(IRQ_CTRL));
173 /* Entry point for FIQs */
177 irq_handler *handler;
179 num = readb(IRQ_REG(FIQ_NUM)) & 0x1f;
181 printd("f%02x\n", num);
184 handler = irq_handlers[num];
189 /* Start new FIQ agreement */
190 tmp = readb(IRQ_REG(IRQ_CTRL));
192 writeb(tmp, IRQ_REG(IRQ_CTRL));
195 void irq_register_handler(enum irq_nr nr, irq_handler *handler)
200 irq_handlers[nr] = handler;
203 #define BASE_ADDR_IBOOT_EXC 0x0080001C
204 extern uint32_t _exceptions;
206 /* Install the exception handlers to where the ROM loader jumps */
207 void calypso_exceptions_install(void)
209 uint32_t *exceptions_dst = (uint32_t *) BASE_ADDR_IBOOT_EXC;
210 uint32_t *exceptions_src = &_exceptions;
213 for (i = 0; i < 7; i++)
214 *exceptions_dst++ = *exceptions_src++;
218 static void set_default_priorities(void)
222 for (i = 0; i < ARRAY_SIZE(default_irq_prio); i++) {
224 uint8_t prio = default_irq_prio[i];
228 val = readw(IRQ_REG(ILR_IRQ(i)));
231 writew(val, IRQ_REG(ILR_IRQ(i)));
235 static uint32_t irq_nest_mask;
236 /* mask off all interrupts that have a lower priority than irq_nr */
237 static void mask_all_lower_prio_irqs(enum irq_nr irq)
239 uint8_t our_prio = readb(IRQ_REG(ILR_IRQ(irq))) >> 2;
242 for (i = 0; i < _NR_IRQ; i++) {
248 prio = readb(IRQ_REG(ILR_IRQ(i))) >> 2;
249 if (prio >= our_prio)
250 irq_nest_mask |= (1 << i);
256 /* set default priorities */
257 set_default_priorities();
258 /* mask all interrupts off */
259 writew(0xffff, IRQ_REG(MASK_IT_REG1));
260 writew(0xffff, IRQ_REG(MASK_IT_REG2));
261 /* clear all pending interrupts */
262 writew(0, IRQ_REG(IT_REG1));
263 writew(0, IRQ_REG(IT_REG2));
264 /* enable interrupts globally to the ARM core */
265 arm_enable_interrupts();