xref: /openbmc/qemu/hw/intc/omap_intc.c (revision ca5aa28e)
1 /*
2  * TI OMAP interrupt controller emulation.
3  *
4  * Copyright (C) 2006-2008 Andrzej Zaborowski  <balrog@zabor.org>
5  * Copyright (C) 2007-2008 Nokia Corporation
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License as
9  * published by the Free Software Foundation; either version 2 or
10  * (at your option) version 3 of the License.
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, see <http://www.gnu.org/licenses/>.
19  */
20 
21 #include "qemu/osdep.h"
22 #include "hw/irq.h"
23 #include "hw/qdev-properties.h"
24 #include "hw/arm/omap.h"
25 #include "hw/sysbus.h"
26 #include "qemu/error-report.h"
27 #include "qemu/module.h"
28 #include "qapi/error.h"
29 
30 /* Interrupt Handlers */
31 struct omap_intr_handler_bank_s {
32     uint32_t irqs;
33     uint32_t inputs;
34     uint32_t mask;
35     uint32_t fiq;
36     uint32_t sens_edge;
37     uint32_t swi;
38     unsigned char priority[32];
39 };
40 
41 struct OMAPIntcState {
42     SysBusDevice parent_obj;
43 
44     qemu_irq *pins;
45     qemu_irq parent_intr[2];
46     MemoryRegion mmio;
47     void *iclk;
48     void *fclk;
49     unsigned char nbanks;
50     int level_only;
51     uint32_t size;
52 
53     /* state */
54     uint32_t new_agr[2];
55     int sir_intr[2];
56     int autoidle;
57     uint32_t mask;
58     struct omap_intr_handler_bank_s bank[3];
59 };
60 
61 static void omap_inth_sir_update(OMAPIntcState *s, int is_fiq)
62 {
63     int i, j, sir_intr, p_intr, p;
64     uint32_t level;
65     sir_intr = 0;
66     p_intr = 255;
67 
68     /* Find the interrupt line with the highest dynamic priority.
69      * Note: 0 denotes the highest priority.
70      * If all interrupts have the same priority, the default order is IRQ_N,
71      * IRQ_N-1,...,IRQ_0. */
72     for (j = 0; j < s->nbanks; ++j) {
73         level = s->bank[j].irqs & ~s->bank[j].mask &
74                 (is_fiq ? s->bank[j].fiq : ~s->bank[j].fiq);
75 
76         while (level != 0) {
77             i = ctz32(level);
78             p = s->bank[j].priority[i];
79             if (p <= p_intr) {
80                 p_intr = p;
81                 sir_intr = 32 * j + i;
82             }
83             level &= level - 1;
84         }
85     }
86     s->sir_intr[is_fiq] = sir_intr;
87 }
88 
89 static inline void omap_inth_update(OMAPIntcState *s, int is_fiq)
90 {
91     int i;
92     uint32_t has_intr = 0;
93 
94     for (i = 0; i < s->nbanks; ++i)
95         has_intr |= s->bank[i].irqs & ~s->bank[i].mask &
96                 (is_fiq ? s->bank[i].fiq : ~s->bank[i].fiq);
97 
98     if (s->new_agr[is_fiq] & has_intr & s->mask) {
99         s->new_agr[is_fiq] = 0;
100         omap_inth_sir_update(s, is_fiq);
101         qemu_set_irq(s->parent_intr[is_fiq], 1);
102     }
103 }
104 
105 #define INT_FALLING_EDGE	0
106 #define INT_LOW_LEVEL		1
107 
108 static void omap_set_intr(void *opaque, int irq, int req)
109 {
110     OMAPIntcState *ih = opaque;
111     uint32_t rise;
112 
113     struct omap_intr_handler_bank_s *bank = &ih->bank[irq >> 5];
114     int n = irq & 31;
115 
116     if (req) {
117         rise = ~bank->irqs & (1 << n);
118         if (~bank->sens_edge & (1 << n))
119             rise &= ~bank->inputs;
120 
121         bank->inputs |= (1 << n);
122         if (rise) {
123             bank->irqs |= rise;
124             omap_inth_update(ih, 0);
125             omap_inth_update(ih, 1);
126         }
127     } else {
128         rise = bank->sens_edge & bank->irqs & (1 << n);
129         bank->irqs &= ~rise;
130         bank->inputs &= ~(1 << n);
131     }
132 }
133 
134 static uint64_t omap_inth_read(void *opaque, hwaddr addr,
135                                unsigned size)
136 {
137     OMAPIntcState *s = opaque;
138     int i, offset = addr;
139     int bank_no = offset >> 8;
140     int line_no;
141     struct omap_intr_handler_bank_s *bank = &s->bank[bank_no];
142     offset &= 0xff;
143 
144     switch (offset) {
145     case 0x00:	/* ITR */
146         return bank->irqs;
147 
148     case 0x04:	/* MIR */
149         return bank->mask;
150 
151     case 0x10:	/* SIR_IRQ_CODE */
152     case 0x14:  /* SIR_FIQ_CODE */
153         if (bank_no != 0)
154             break;
155         line_no = s->sir_intr[(offset - 0x10) >> 2];
156         bank = &s->bank[line_no >> 5];
157         i = line_no & 31;
158         if (((bank->sens_edge >> i) & 1) == INT_FALLING_EDGE)
159             bank->irqs &= ~(1 << i);
160         return line_no;
161 
162     case 0x18:	/* CONTROL_REG */
163         if (bank_no != 0)
164             break;
165         return 0;
166 
167     case 0x1c:	/* ILR0 */
168     case 0x20:	/* ILR1 */
169     case 0x24:	/* ILR2 */
170     case 0x28:	/* ILR3 */
171     case 0x2c:	/* ILR4 */
172     case 0x30:	/* ILR5 */
173     case 0x34:	/* ILR6 */
174     case 0x38:	/* ILR7 */
175     case 0x3c:	/* ILR8 */
176     case 0x40:	/* ILR9 */
177     case 0x44:	/* ILR10 */
178     case 0x48:	/* ILR11 */
179     case 0x4c:	/* ILR12 */
180     case 0x50:	/* ILR13 */
181     case 0x54:	/* ILR14 */
182     case 0x58:	/* ILR15 */
183     case 0x5c:	/* ILR16 */
184     case 0x60:	/* ILR17 */
185     case 0x64:	/* ILR18 */
186     case 0x68:	/* ILR19 */
187     case 0x6c:	/* ILR20 */
188     case 0x70:	/* ILR21 */
189     case 0x74:	/* ILR22 */
190     case 0x78:	/* ILR23 */
191     case 0x7c:	/* ILR24 */
192     case 0x80:	/* ILR25 */
193     case 0x84:	/* ILR26 */
194     case 0x88:	/* ILR27 */
195     case 0x8c:	/* ILR28 */
196     case 0x90:	/* ILR29 */
197     case 0x94:	/* ILR30 */
198     case 0x98:	/* ILR31 */
199         i = (offset - 0x1c) >> 2;
200         return (bank->priority[i] << 2) |
201                 (((bank->sens_edge >> i) & 1) << 1) |
202                 ((bank->fiq >> i) & 1);
203 
204     case 0x9c:	/* ISR */
205         return 0x00000000;
206 
207     }
208     OMAP_BAD_REG(addr);
209     return 0;
210 }
211 
212 static void omap_inth_write(void *opaque, hwaddr addr,
213                             uint64_t value, unsigned size)
214 {
215     OMAPIntcState *s = opaque;
216     int i, offset = addr;
217     int bank_no = offset >> 8;
218     struct omap_intr_handler_bank_s *bank = &s->bank[bank_no];
219     offset &= 0xff;
220 
221     switch (offset) {
222     case 0x00:	/* ITR */
223         /* Important: ignore the clearing if the IRQ is level-triggered and
224            the input bit is 1 */
225         bank->irqs &= value | (bank->inputs & bank->sens_edge);
226         return;
227 
228     case 0x04:	/* MIR */
229         bank->mask = value;
230         omap_inth_update(s, 0);
231         omap_inth_update(s, 1);
232         return;
233 
234     case 0x10:	/* SIR_IRQ_CODE */
235     case 0x14:	/* SIR_FIQ_CODE */
236         OMAP_RO_REG(addr);
237         break;
238 
239     case 0x18:	/* CONTROL_REG */
240         if (bank_no != 0)
241             break;
242         if (value & 2) {
243             qemu_set_irq(s->parent_intr[1], 0);
244             s->new_agr[1] = ~0;
245             omap_inth_update(s, 1);
246         }
247         if (value & 1) {
248             qemu_set_irq(s->parent_intr[0], 0);
249             s->new_agr[0] = ~0;
250             omap_inth_update(s, 0);
251         }
252         return;
253 
254     case 0x1c:	/* ILR0 */
255     case 0x20:	/* ILR1 */
256     case 0x24:	/* ILR2 */
257     case 0x28:	/* ILR3 */
258     case 0x2c:	/* ILR4 */
259     case 0x30:	/* ILR5 */
260     case 0x34:	/* ILR6 */
261     case 0x38:	/* ILR7 */
262     case 0x3c:	/* ILR8 */
263     case 0x40:	/* ILR9 */
264     case 0x44:	/* ILR10 */
265     case 0x48:	/* ILR11 */
266     case 0x4c:	/* ILR12 */
267     case 0x50:	/* ILR13 */
268     case 0x54:	/* ILR14 */
269     case 0x58:	/* ILR15 */
270     case 0x5c:	/* ILR16 */
271     case 0x60:	/* ILR17 */
272     case 0x64:	/* ILR18 */
273     case 0x68:	/* ILR19 */
274     case 0x6c:	/* ILR20 */
275     case 0x70:	/* ILR21 */
276     case 0x74:	/* ILR22 */
277     case 0x78:	/* ILR23 */
278     case 0x7c:	/* ILR24 */
279     case 0x80:	/* ILR25 */
280     case 0x84:	/* ILR26 */
281     case 0x88:	/* ILR27 */
282     case 0x8c:	/* ILR28 */
283     case 0x90:	/* ILR29 */
284     case 0x94:	/* ILR30 */
285     case 0x98:	/* ILR31 */
286         i = (offset - 0x1c) >> 2;
287         bank->priority[i] = (value >> 2) & 0x1f;
288         bank->sens_edge &= ~(1 << i);
289         bank->sens_edge |= ((value >> 1) & 1) << i;
290         bank->fiq &= ~(1 << i);
291         bank->fiq |= (value & 1) << i;
292         return;
293 
294     case 0x9c:	/* ISR */
295         for (i = 0; i < 32; i ++)
296             if (value & (1 << i)) {
297                 omap_set_intr(s, 32 * bank_no + i, 1);
298                 return;
299             }
300         return;
301     }
302     OMAP_BAD_REG(addr);
303 }
304 
305 static const MemoryRegionOps omap_inth_mem_ops = {
306     .read = omap_inth_read,
307     .write = omap_inth_write,
308     .endianness = DEVICE_NATIVE_ENDIAN,
309     .valid = {
310         .min_access_size = 4,
311         .max_access_size = 4,
312     },
313 };
314 
315 static void omap_inth_reset(DeviceState *dev)
316 {
317     OMAPIntcState *s = OMAP_INTC(dev);
318     int i;
319 
320     for (i = 0; i < s->nbanks; ++i){
321         s->bank[i].irqs = 0x00000000;
322         s->bank[i].mask = 0xffffffff;
323         s->bank[i].sens_edge = 0x00000000;
324         s->bank[i].fiq = 0x00000000;
325         s->bank[i].inputs = 0x00000000;
326         s->bank[i].swi = 0x00000000;
327         memset(s->bank[i].priority, 0, sizeof(s->bank[i].priority));
328 
329         if (s->level_only)
330             s->bank[i].sens_edge = 0xffffffff;
331     }
332 
333     s->new_agr[0] = ~0;
334     s->new_agr[1] = ~0;
335     s->sir_intr[0] = 0;
336     s->sir_intr[1] = 0;
337     s->autoidle = 0;
338     s->mask = ~0;
339 
340     qemu_set_irq(s->parent_intr[0], 0);
341     qemu_set_irq(s->parent_intr[1], 0);
342 }
343 
344 static void omap_intc_init(Object *obj)
345 {
346     DeviceState *dev = DEVICE(obj);
347     OMAPIntcState *s = OMAP_INTC(obj);
348     SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
349 
350     s->nbanks = 1;
351     sysbus_init_irq(sbd, &s->parent_intr[0]);
352     sysbus_init_irq(sbd, &s->parent_intr[1]);
353     qdev_init_gpio_in(dev, omap_set_intr, s->nbanks * 32);
354     memory_region_init_io(&s->mmio, obj, &omap_inth_mem_ops, s,
355                           "omap-intc", s->size);
356     sysbus_init_mmio(sbd, &s->mmio);
357 }
358 
359 static void omap_intc_realize(DeviceState *dev, Error **errp)
360 {
361     OMAPIntcState *s = OMAP_INTC(dev);
362 
363     if (!s->iclk) {
364         error_setg(errp, "omap-intc: clk not connected");
365     }
366 }
367 
368 void omap_intc_set_iclk(OMAPIntcState *intc, omap_clk clk)
369 {
370     intc->iclk = clk;
371 }
372 
373 void omap_intc_set_fclk(OMAPIntcState *intc, omap_clk clk)
374 {
375     intc->fclk = clk;
376 }
377 
378 static Property omap_intc_properties[] = {
379     DEFINE_PROP_UINT32("size", OMAPIntcState, size, 0x100),
380     DEFINE_PROP_END_OF_LIST(),
381 };
382 
383 static void omap_intc_class_init(ObjectClass *klass, void *data)
384 {
385     DeviceClass *dc = DEVICE_CLASS(klass);
386 
387     device_class_set_legacy_reset(dc, omap_inth_reset);
388     device_class_set_props(dc, omap_intc_properties);
389     /* Reason: pointer property "clk" */
390     dc->user_creatable = false;
391     dc->realize = omap_intc_realize;
392 }
393 
394 static const TypeInfo omap_intc_info = {
395     .name          = TYPE_OMAP_INTC,
396     .parent        = TYPE_SYS_BUS_DEVICE,
397     .instance_size = sizeof(OMAPIntcState),
398     .instance_init = omap_intc_init,
399     .class_init    = omap_intc_class_init,
400 };
401 
402 static void omap_intc_register_types(void)
403 {
404     type_register_static(&omap_intc_info);
405 }
406 
407 type_init(omap_intc_register_types)
408