Initial import of OsmocomBB into git repository
[osmocom-bb.git] / src / target / firmware / calypso / irq.c
1 /* Driver for Calypso IRQ controller */
2
3 /* (C) 2010 by Harald Welte <laforge@gnumonks.org>
4  *
5  * All Rights Reserved
6  *
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.
11  *
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 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.
20  *
21  */
22
23 #include <stdint.h>
24 #include <stdio.h>
25
26 #include <debug.h>
27 #include <memory.h>
28 #include <arm.h>
29 #include <calypso/irq.h>
30
31 #define BASE_ADDR_IRQ   0xfffffa00
32
33 enum irq_reg {
34         IT_REG1         = 0x00,
35         IT_REG2         = 0x02,
36         MASK_IT_REG1    = 0x08,
37         MASK_IT_REG2    = 0x0a,
38         IRQ_NUM         = 0x10,
39         FIQ_NUM         = 0x12,
40         IRQ_CTRL        = 0x14,
41 };
42
43 #define ILR_IRQ(x)      (0x20 + (x*2))
44 #define IRQ_REG(x)      ((void *)BASE_ADDR_IRQ + (x))
45
46 #define NR_IRQS 32
47
48 static uint8_t default_irq_prio[] = {
49         [IRQ_WATCHDOG]          = 0xff,
50         [IRQ_TIMER1]            = 0xff,
51         [IRQ_TIMER2]            = 0xff,
52         [IRQ_TSP_RX]            = 0,
53         [IRQ_TPU_FRAME]         = 3,
54         [IRQ_TPU_PAGE]          = 0xff,
55         [IRQ_SIMCARD]           = 0xff,
56         [IRQ_UART_MODEM]        = 8,
57         [IRQ_KEYPAD_GPIO]       = 4,
58         [IRQ_RTC_TIMER]         = 9,
59         [IRQ_RTC_ALARM_I2C]     = 10,
60         [IRQ_ULPD_GAUGING]      = 2,
61         [IRQ_EXTERNAL]          = 12,
62         [IRQ_SPI]               = 0xff,
63         [IRQ_DMA]               = 0xff,
64         [IRQ_API]               = 0xff,
65         [IRQ_SIM_DETECT]        = 0,
66         [IRQ_EXTERNAL_FIQ]      = 7,
67         [IRQ_UART_IRDA]         = 2,
68         [IRQ_ULPD_GSM_TIMER]    = 1,
69         [IRQ_GEA]               = 0xff,
70 };
71
72 static irq_handler *irq_handlers[NR_IRQS];
73
74 static void _irq_enable(enum irq_nr nr, int enable)
75 {
76         uint16_t *reg = IRQ_REG(MASK_IT_REG1);
77         uint16_t val;
78
79         if (nr > 15) {
80                 reg = IRQ_REG(MASK_IT_REG2);
81                 nr -= 16;
82         }
83
84         val = readw(reg);
85         if (enable)
86                 val &= ~(1 << nr);
87         else
88                 val |= (1 << nr);
89         writew(val, reg);
90 }
91
92 void irq_enable(enum irq_nr nr)
93 {
94         _irq_enable(nr, 1);
95 }
96
97 void irq_disable(enum irq_nr nr)
98 {
99         _irq_enable(nr, 0);
100 }
101
102 void irq_config(enum irq_nr nr, int fiq, int edge, int8_t prio)
103 {
104         uint16_t val;
105
106         if (prio == -1)
107                 prio = default_irq_prio[nr];
108
109         if (prio > 31)
110                 prio = 31;
111
112         val = prio << 2;
113         if (edge)
114                 val |= 0x02;
115         if (fiq)
116                 val |= 0x01;
117
118         writew(val, IRQ_REG(ILR_IRQ(nr)));
119 }
120
121 /* Entry point for interrupts */
122 void irq(void)
123 {
124         uint8_t num, tmp;
125         irq_handler *handler;
126
127 #if 1
128         /* Hardware interrupt detection mode */
129         num = readb(IRQ_REG(IRQ_NUM)) & 0x1f;
130
131         printd("i%02x\n", num);
132
133         handler = irq_handlers[num];
134
135         if (handler)
136                 handler(num);
137 #else
138         /* Software interrupt detection mode */
139         {
140                 uint16_t it_reg, mask_reg;
141                 uint32_t irqs;
142
143                 it_reg = readw(IRQ_REG(IT_REG1));
144                 mask_reg = readw(IRQ_REG(MASK_IT_REG1));
145                 irqs = it_reg & ~mask_reg;
146
147                 it_reg = readw(IRQ_REG(IT_REG2));
148                 mask_reg = readw(IRQ_REG(MASK_IT_REG2));
149                 irqs |= (it_reg & ~mask_reg) << 16;
150
151                 for (num = 0; num < 32; num++) {
152                         if (irqs & (1 << num)) {
153                                 printd("i%d\n", num);
154                                 handler = irq_handlers[num];
155                                 if (handler)
156                                         handler(num);
157                                 /* clear this interrupt */
158                                 if (num < 16)
159                                         writew(~(1 << num), IRQ_REG(IT_REG1));
160                                 else
161                                         writew(~(1 << (num-16)), IRQ_REG(IT_REG2));
162                         }
163                 }
164                 dputchar('\n');
165         }
166 #endif
167         /* Start new IRQ agreement */
168         tmp = readb(IRQ_REG(IRQ_CTRL));
169         tmp |= 0x01;
170         writeb(tmp, IRQ_REG(IRQ_CTRL));
171 }
172
173 /* Entry point for FIQs */
174 void fiq(void)
175 {
176         uint8_t num, tmp;
177         irq_handler *handler;
178
179         num = readb(IRQ_REG(FIQ_NUM)) & 0x1f;
180         if (num) {
181                 printd("f%02x\n", num);
182         }
183
184         handler = irq_handlers[num];
185
186         if (handler)
187                 handler(num);
188
189         /* Start new FIQ agreement */
190         tmp = readb(IRQ_REG(IRQ_CTRL));
191         tmp |= 0x02;
192         writeb(tmp, IRQ_REG(IRQ_CTRL));
193 }
194
195 void irq_register_handler(enum irq_nr nr, irq_handler *handler)
196 {
197         if (nr > NR_IRQS)
198                 return;
199
200         irq_handlers[nr] = handler;
201 }
202
203 #define BASE_ADDR_IBOOT_EXC     0x0080001C
204 extern uint32_t _exceptions;
205
206 /* Install the exception handlers to where the ROM loader jumps */
207 void calypso_exceptions_install(void)
208 {
209         uint32_t *exceptions_dst = (uint32_t *) BASE_ADDR_IBOOT_EXC;
210         uint32_t *exceptions_src = &_exceptions;
211         int i;
212
213         for (i = 0; i < 7; i++)
214                 *exceptions_dst++ = *exceptions_src++;
215
216 }
217
218 static void set_default_priorities(void)
219 {
220         unsigned int i;
221
222         for (i = 0; i < ARRAY_SIZE(default_irq_prio); i++) {
223                 uint16_t val;
224                 uint8_t prio = default_irq_prio[i];
225                 if (prio > 31)
226                         prio = 31;
227
228                 val = readw(IRQ_REG(ILR_IRQ(i)));
229                 val &= ~(0x1f << 2);
230                 val |= prio << 2;
231                 writew(val, IRQ_REG(ILR_IRQ(i)));
232         }
233 }
234
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)
238 {
239         uint8_t our_prio = readb(IRQ_REG(ILR_IRQ(irq))) >> 2;
240         int i;
241
242         for (i = 0; i < _NR_IRQ; i++) {
243                 uint8_t prio;
244
245                 if (i == irq)
246                         continue;
247
248                 prio = readb(IRQ_REG(ILR_IRQ(i))) >> 2;
249                 if (prio >= our_prio)
250                         irq_nest_mask |= (1 << i);
251         }
252 }
253
254 void irq_init(void)
255 {
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();
266 }