xref: /qemu/hw/intc/omap_intc.c (revision a27bd6c779badb8d76e4430d810ef710a1b98f4e)
17f132a21Scmchao /*
27f132a21Scmchao  * TI OMAP interrupt controller emulation.
37f132a21Scmchao  *
47f132a21Scmchao  * Copyright (C) 2006-2008 Andrzej Zaborowski  <balrog@zabor.org>
57f132a21Scmchao  * Copyright (C) 2007-2008 Nokia Corporation
67f132a21Scmchao  *
77f132a21Scmchao  * This program is free software; you can redistribute it and/or
87f132a21Scmchao  * modify it under the terms of the GNU General Public License as
97f132a21Scmchao  * published by the Free Software Foundation; either version 2 or
107f132a21Scmchao  * (at your option) version 3 of the License.
117f132a21Scmchao  *
127f132a21Scmchao  * This program is distributed in the hope that it will be useful,
137f132a21Scmchao  * but WITHOUT ANY WARRANTY; without even the implied warranty of
147f132a21Scmchao  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
157f132a21Scmchao  * GNU General Public License for more details.
167f132a21Scmchao  *
177f132a21Scmchao  * You should have received a copy of the GNU General Public License along
187f132a21Scmchao  * with this program; if not, see <http://www.gnu.org/licenses/>.
197f132a21Scmchao  */
200b8fa32fSMarkus Armbruster 
2190191d07SPeter Maydell #include "qemu/osdep.h"
2264552b6bSMarkus Armbruster #include "hw/irq.h"
23*a27bd6c7SMarkus Armbruster #include "hw/qdev-properties.h"
240d09e41aSPaolo Bonzini #include "hw/arm/omap.h"
2583c9f4caSPaolo Bonzini #include "hw/sysbus.h"
2684a3a53cSMarkus Armbruster #include "qemu/error-report.h"
270b8fa32fSMarkus Armbruster #include "qemu/module.h"
280a750e2aSxiaoqiang zhao #include "qapi/error.h"
297f132a21Scmchao 
307f132a21Scmchao /* Interrupt Handlers */
317f132a21Scmchao struct omap_intr_handler_bank_s {
327f132a21Scmchao     uint32_t irqs;
337f132a21Scmchao     uint32_t inputs;
347f132a21Scmchao     uint32_t mask;
357f132a21Scmchao     uint32_t fiq;
367f132a21Scmchao     uint32_t sens_edge;
377f132a21Scmchao     uint32_t swi;
387f132a21Scmchao     unsigned char priority[32];
397f132a21Scmchao };
407f132a21Scmchao 
4147edc5a4SAndreas Färber #define TYPE_OMAP_INTC "common-omap-intc"
4247edc5a4SAndreas Färber #define OMAP_INTC(obj) \
4347edc5a4SAndreas Färber     OBJECT_CHECK(struct omap_intr_handler_s, (obj), TYPE_OMAP_INTC)
4447edc5a4SAndreas Färber 
457f132a21Scmchao struct omap_intr_handler_s {
4647edc5a4SAndreas Färber     SysBusDevice parent_obj;
4747edc5a4SAndreas Färber 
487f132a21Scmchao     qemu_irq *pins;
497f132a21Scmchao     qemu_irq parent_intr[2];
5053bb614eSPeter Maydell     MemoryRegion mmio;
510919ac78SPeter Maydell     void *iclk;
520919ac78SPeter Maydell     void *fclk;
537f132a21Scmchao     unsigned char nbanks;
547f132a21Scmchao     int level_only;
550919ac78SPeter Maydell     uint32_t size;
560919ac78SPeter Maydell 
570919ac78SPeter Maydell     uint8_t revision;
587f132a21Scmchao 
597f132a21Scmchao     /* state */
607f132a21Scmchao     uint32_t new_agr[2];
617f132a21Scmchao     int sir_intr[2];
627f132a21Scmchao     int autoidle;
637f132a21Scmchao     uint32_t mask;
640919ac78SPeter Maydell     struct omap_intr_handler_bank_s bank[3];
657f132a21Scmchao };
667f132a21Scmchao 
677f132a21Scmchao static void omap_inth_sir_update(struct omap_intr_handler_s *s, int is_fiq)
687f132a21Scmchao {
6941074f3dSPaolo Bonzini     int i, j, sir_intr, p_intr, p;
707f132a21Scmchao     uint32_t level;
717f132a21Scmchao     sir_intr = 0;
727f132a21Scmchao     p_intr = 255;
737f132a21Scmchao 
747f132a21Scmchao     /* Find the interrupt line with the highest dynamic priority.
757f132a21Scmchao      * Note: 0 denotes the hightest priority.
767f132a21Scmchao      * If all interrupts have the same priority, the default order is IRQ_N,
777f132a21Scmchao      * IRQ_N-1,...,IRQ_0. */
787f132a21Scmchao     for (j = 0; j < s->nbanks; ++j) {
797f132a21Scmchao         level = s->bank[j].irqs & ~s->bank[j].mask &
807f132a21Scmchao                 (is_fiq ? s->bank[j].fiq : ~s->bank[j].fiq);
8141074f3dSPaolo Bonzini 
8241074f3dSPaolo Bonzini         while (level != 0) {
8341074f3dSPaolo Bonzini             i = ctz32(level);
847f132a21Scmchao             p = s->bank[j].priority[i];
857f132a21Scmchao             if (p <= p_intr) {
867f132a21Scmchao                 p_intr = p;
877f132a21Scmchao                 sir_intr = 32 * j + i;
887f132a21Scmchao             }
8941074f3dSPaolo Bonzini             level &= level - 1;
907f132a21Scmchao         }
917f132a21Scmchao     }
927f132a21Scmchao     s->sir_intr[is_fiq] = sir_intr;
937f132a21Scmchao }
947f132a21Scmchao 
957f132a21Scmchao static inline void omap_inth_update(struct omap_intr_handler_s *s, int is_fiq)
967f132a21Scmchao {
977f132a21Scmchao     int i;
987f132a21Scmchao     uint32_t has_intr = 0;
997f132a21Scmchao 
1007f132a21Scmchao     for (i = 0; i < s->nbanks; ++i)
1017f132a21Scmchao         has_intr |= s->bank[i].irqs & ~s->bank[i].mask &
1027f132a21Scmchao                 (is_fiq ? s->bank[i].fiq : ~s->bank[i].fiq);
1037f132a21Scmchao 
1047f132a21Scmchao     if (s->new_agr[is_fiq] & has_intr & s->mask) {
1057f132a21Scmchao         s->new_agr[is_fiq] = 0;
1067f132a21Scmchao         omap_inth_sir_update(s, is_fiq);
1077f132a21Scmchao         qemu_set_irq(s->parent_intr[is_fiq], 1);
1087f132a21Scmchao     }
1097f132a21Scmchao }
1107f132a21Scmchao 
1117f132a21Scmchao #define INT_FALLING_EDGE	0
1127f132a21Scmchao #define INT_LOW_LEVEL		1
1137f132a21Scmchao 
1147f132a21Scmchao static void omap_set_intr(void *opaque, int irq, int req)
1157f132a21Scmchao {
1167f132a21Scmchao     struct omap_intr_handler_s *ih = (struct omap_intr_handler_s *) opaque;
1177f132a21Scmchao     uint32_t rise;
1187f132a21Scmchao 
1197f132a21Scmchao     struct omap_intr_handler_bank_s *bank = &ih->bank[irq >> 5];
1207f132a21Scmchao     int n = irq & 31;
1217f132a21Scmchao 
1227f132a21Scmchao     if (req) {
1237f132a21Scmchao         rise = ~bank->irqs & (1 << n);
1247f132a21Scmchao         if (~bank->sens_edge & (1 << n))
1257f132a21Scmchao             rise &= ~bank->inputs;
1267f132a21Scmchao 
1277f132a21Scmchao         bank->inputs |= (1 << n);
1287f132a21Scmchao         if (rise) {
1297f132a21Scmchao             bank->irqs |= rise;
1307f132a21Scmchao             omap_inth_update(ih, 0);
1317f132a21Scmchao             omap_inth_update(ih, 1);
1327f132a21Scmchao         }
1337f132a21Scmchao     } else {
1347f132a21Scmchao         rise = bank->sens_edge & bank->irqs & (1 << n);
1357f132a21Scmchao         bank->irqs &= ~rise;
1367f132a21Scmchao         bank->inputs &= ~(1 << n);
1377f132a21Scmchao     }
1387f132a21Scmchao }
1397f132a21Scmchao 
1407f132a21Scmchao /* Simplified version with no edge detection */
1417f132a21Scmchao static void omap_set_intr_noedge(void *opaque, int irq, int req)
1427f132a21Scmchao {
1437f132a21Scmchao     struct omap_intr_handler_s *ih = (struct omap_intr_handler_s *) opaque;
1447f132a21Scmchao     uint32_t rise;
1457f132a21Scmchao 
1467f132a21Scmchao     struct omap_intr_handler_bank_s *bank = &ih->bank[irq >> 5];
1477f132a21Scmchao     int n = irq & 31;
1487f132a21Scmchao 
1497f132a21Scmchao     if (req) {
1507f132a21Scmchao         rise = ~bank->inputs & (1 << n);
1517f132a21Scmchao         if (rise) {
1527f132a21Scmchao             bank->irqs |= bank->inputs |= rise;
1537f132a21Scmchao             omap_inth_update(ih, 0);
1547f132a21Scmchao             omap_inth_update(ih, 1);
1557f132a21Scmchao         }
1567f132a21Scmchao     } else
1577f132a21Scmchao         bank->irqs = (bank->inputs &= ~(1 << n)) | bank->swi;
1587f132a21Scmchao }
1597f132a21Scmchao 
160a8170e5eSAvi Kivity static uint64_t omap_inth_read(void *opaque, hwaddr addr,
16153bb614eSPeter Maydell                                unsigned size)
1627f132a21Scmchao {
1637f132a21Scmchao     struct omap_intr_handler_s *s = (struct omap_intr_handler_s *) opaque;
1647f132a21Scmchao     int i, offset = addr;
1657f132a21Scmchao     int bank_no = offset >> 8;
1667f132a21Scmchao     int line_no;
1677f132a21Scmchao     struct omap_intr_handler_bank_s *bank = &s->bank[bank_no];
1687f132a21Scmchao     offset &= 0xff;
1697f132a21Scmchao 
1707f132a21Scmchao     switch (offset) {
1717f132a21Scmchao     case 0x00:	/* ITR */
1727f132a21Scmchao         return bank->irqs;
1737f132a21Scmchao 
1747f132a21Scmchao     case 0x04:	/* MIR */
1757f132a21Scmchao         return bank->mask;
1767f132a21Scmchao 
1777f132a21Scmchao     case 0x10:	/* SIR_IRQ_CODE */
1787f132a21Scmchao     case 0x14:  /* SIR_FIQ_CODE */
1797f132a21Scmchao         if (bank_no != 0)
1807f132a21Scmchao             break;
1817f132a21Scmchao         line_no = s->sir_intr[(offset - 0x10) >> 2];
1827f132a21Scmchao         bank = &s->bank[line_no >> 5];
1837f132a21Scmchao         i = line_no & 31;
1847f132a21Scmchao         if (((bank->sens_edge >> i) & 1) == INT_FALLING_EDGE)
1857f132a21Scmchao             bank->irqs &= ~(1 << i);
1867f132a21Scmchao         return line_no;
1877f132a21Scmchao 
1887f132a21Scmchao     case 0x18:	/* CONTROL_REG */
1897f132a21Scmchao         if (bank_no != 0)
1907f132a21Scmchao             break;
1917f132a21Scmchao         return 0;
1927f132a21Scmchao 
1937f132a21Scmchao     case 0x1c:	/* ILR0 */
1947f132a21Scmchao     case 0x20:	/* ILR1 */
1957f132a21Scmchao     case 0x24:	/* ILR2 */
1967f132a21Scmchao     case 0x28:	/* ILR3 */
1977f132a21Scmchao     case 0x2c:	/* ILR4 */
1987f132a21Scmchao     case 0x30:	/* ILR5 */
1997f132a21Scmchao     case 0x34:	/* ILR6 */
2007f132a21Scmchao     case 0x38:	/* ILR7 */
2017f132a21Scmchao     case 0x3c:	/* ILR8 */
2027f132a21Scmchao     case 0x40:	/* ILR9 */
2037f132a21Scmchao     case 0x44:	/* ILR10 */
2047f132a21Scmchao     case 0x48:	/* ILR11 */
2057f132a21Scmchao     case 0x4c:	/* ILR12 */
2067f132a21Scmchao     case 0x50:	/* ILR13 */
2077f132a21Scmchao     case 0x54:	/* ILR14 */
2087f132a21Scmchao     case 0x58:	/* ILR15 */
2097f132a21Scmchao     case 0x5c:	/* ILR16 */
2107f132a21Scmchao     case 0x60:	/* ILR17 */
2117f132a21Scmchao     case 0x64:	/* ILR18 */
2127f132a21Scmchao     case 0x68:	/* ILR19 */
2137f132a21Scmchao     case 0x6c:	/* ILR20 */
2147f132a21Scmchao     case 0x70:	/* ILR21 */
2157f132a21Scmchao     case 0x74:	/* ILR22 */
2167f132a21Scmchao     case 0x78:	/* ILR23 */
2177f132a21Scmchao     case 0x7c:	/* ILR24 */
2187f132a21Scmchao     case 0x80:	/* ILR25 */
2197f132a21Scmchao     case 0x84:	/* ILR26 */
2207f132a21Scmchao     case 0x88:	/* ILR27 */
2217f132a21Scmchao     case 0x8c:	/* ILR28 */
2227f132a21Scmchao     case 0x90:	/* ILR29 */
2237f132a21Scmchao     case 0x94:	/* ILR30 */
2247f132a21Scmchao     case 0x98:	/* ILR31 */
2257f132a21Scmchao         i = (offset - 0x1c) >> 2;
2267f132a21Scmchao         return (bank->priority[i] << 2) |
2277f132a21Scmchao                 (((bank->sens_edge >> i) & 1) << 1) |
2287f132a21Scmchao                 ((bank->fiq >> i) & 1);
2297f132a21Scmchao 
2307f132a21Scmchao     case 0x9c:	/* ISR */
2317f132a21Scmchao         return 0x00000000;
2327f132a21Scmchao 
2337f132a21Scmchao     }
2347f132a21Scmchao     OMAP_BAD_REG(addr);
2357f132a21Scmchao     return 0;
2367f132a21Scmchao }
2377f132a21Scmchao 
238a8170e5eSAvi Kivity static void omap_inth_write(void *opaque, hwaddr addr,
23953bb614eSPeter Maydell                             uint64_t value, unsigned size)
2407f132a21Scmchao {
2417f132a21Scmchao     struct omap_intr_handler_s *s = (struct omap_intr_handler_s *) opaque;
2427f132a21Scmchao     int i, offset = addr;
2437f132a21Scmchao     int bank_no = offset >> 8;
2447f132a21Scmchao     struct omap_intr_handler_bank_s *bank = &s->bank[bank_no];
2457f132a21Scmchao     offset &= 0xff;
2467f132a21Scmchao 
2477f132a21Scmchao     switch (offset) {
2487f132a21Scmchao     case 0x00:	/* ITR */
2497f132a21Scmchao         /* Important: ignore the clearing if the IRQ is level-triggered and
2507f132a21Scmchao            the input bit is 1 */
2517f132a21Scmchao         bank->irqs &= value | (bank->inputs & bank->sens_edge);
2527f132a21Scmchao         return;
2537f132a21Scmchao 
2547f132a21Scmchao     case 0x04:	/* MIR */
2557f132a21Scmchao         bank->mask = value;
2567f132a21Scmchao         omap_inth_update(s, 0);
2577f132a21Scmchao         omap_inth_update(s, 1);
2587f132a21Scmchao         return;
2597f132a21Scmchao 
2607f132a21Scmchao     case 0x10:	/* SIR_IRQ_CODE */
2617f132a21Scmchao     case 0x14:	/* SIR_FIQ_CODE */
2627f132a21Scmchao         OMAP_RO_REG(addr);
2637f132a21Scmchao         break;
2647f132a21Scmchao 
2657f132a21Scmchao     case 0x18:	/* CONTROL_REG */
2667f132a21Scmchao         if (bank_no != 0)
2677f132a21Scmchao             break;
2687f132a21Scmchao         if (value & 2) {
2697f132a21Scmchao             qemu_set_irq(s->parent_intr[1], 0);
2707f132a21Scmchao             s->new_agr[1] = ~0;
2717f132a21Scmchao             omap_inth_update(s, 1);
2727f132a21Scmchao         }
2737f132a21Scmchao         if (value & 1) {
2747f132a21Scmchao             qemu_set_irq(s->parent_intr[0], 0);
2757f132a21Scmchao             s->new_agr[0] = ~0;
2767f132a21Scmchao             omap_inth_update(s, 0);
2777f132a21Scmchao         }
2787f132a21Scmchao         return;
2797f132a21Scmchao 
2807f132a21Scmchao     case 0x1c:	/* ILR0 */
2817f132a21Scmchao     case 0x20:	/* ILR1 */
2827f132a21Scmchao     case 0x24:	/* ILR2 */
2837f132a21Scmchao     case 0x28:	/* ILR3 */
2847f132a21Scmchao     case 0x2c:	/* ILR4 */
2857f132a21Scmchao     case 0x30:	/* ILR5 */
2867f132a21Scmchao     case 0x34:	/* ILR6 */
2877f132a21Scmchao     case 0x38:	/* ILR7 */
2887f132a21Scmchao     case 0x3c:	/* ILR8 */
2897f132a21Scmchao     case 0x40:	/* ILR9 */
2907f132a21Scmchao     case 0x44:	/* ILR10 */
2917f132a21Scmchao     case 0x48:	/* ILR11 */
2927f132a21Scmchao     case 0x4c:	/* ILR12 */
2937f132a21Scmchao     case 0x50:	/* ILR13 */
2947f132a21Scmchao     case 0x54:	/* ILR14 */
2957f132a21Scmchao     case 0x58:	/* ILR15 */
2967f132a21Scmchao     case 0x5c:	/* ILR16 */
2977f132a21Scmchao     case 0x60:	/* ILR17 */
2987f132a21Scmchao     case 0x64:	/* ILR18 */
2997f132a21Scmchao     case 0x68:	/* ILR19 */
3007f132a21Scmchao     case 0x6c:	/* ILR20 */
3017f132a21Scmchao     case 0x70:	/* ILR21 */
3027f132a21Scmchao     case 0x74:	/* ILR22 */
3037f132a21Scmchao     case 0x78:	/* ILR23 */
3047f132a21Scmchao     case 0x7c:	/* ILR24 */
3057f132a21Scmchao     case 0x80:	/* ILR25 */
3067f132a21Scmchao     case 0x84:	/* ILR26 */
3077f132a21Scmchao     case 0x88:	/* ILR27 */
3087f132a21Scmchao     case 0x8c:	/* ILR28 */
3097f132a21Scmchao     case 0x90:	/* ILR29 */
3107f132a21Scmchao     case 0x94:	/* ILR30 */
3117f132a21Scmchao     case 0x98:	/* ILR31 */
3127f132a21Scmchao         i = (offset - 0x1c) >> 2;
3137f132a21Scmchao         bank->priority[i] = (value >> 2) & 0x1f;
3147f132a21Scmchao         bank->sens_edge &= ~(1 << i);
3157f132a21Scmchao         bank->sens_edge |= ((value >> 1) & 1) << i;
3167f132a21Scmchao         bank->fiq &= ~(1 << i);
3177f132a21Scmchao         bank->fiq |= (value & 1) << i;
3187f132a21Scmchao         return;
3197f132a21Scmchao 
3207f132a21Scmchao     case 0x9c:	/* ISR */
3217f132a21Scmchao         for (i = 0; i < 32; i ++)
3227f132a21Scmchao             if (value & (1 << i)) {
3237f132a21Scmchao                 omap_set_intr(s, 32 * bank_no + i, 1);
3247f132a21Scmchao                 return;
3257f132a21Scmchao             }
3267f132a21Scmchao         return;
3277f132a21Scmchao     }
3287f132a21Scmchao     OMAP_BAD_REG(addr);
3297f132a21Scmchao }
3307f132a21Scmchao 
33153bb614eSPeter Maydell static const MemoryRegionOps omap_inth_mem_ops = {
33253bb614eSPeter Maydell     .read = omap_inth_read,
33353bb614eSPeter Maydell     .write = omap_inth_write,
33453bb614eSPeter Maydell     .endianness = DEVICE_NATIVE_ENDIAN,
33553bb614eSPeter Maydell     .valid = {
33653bb614eSPeter Maydell         .min_access_size = 4,
33753bb614eSPeter Maydell         .max_access_size = 4,
33853bb614eSPeter Maydell     },
3397f132a21Scmchao };
3407f132a21Scmchao 
3410919ac78SPeter Maydell static void omap_inth_reset(DeviceState *dev)
3427f132a21Scmchao {
34347edc5a4SAndreas Färber     struct omap_intr_handler_s *s = OMAP_INTC(dev);
3447f132a21Scmchao     int i;
3457f132a21Scmchao 
3467f132a21Scmchao     for (i = 0; i < s->nbanks; ++i){
3477f132a21Scmchao         s->bank[i].irqs = 0x00000000;
3487f132a21Scmchao         s->bank[i].mask = 0xffffffff;
3497f132a21Scmchao         s->bank[i].sens_edge = 0x00000000;
3507f132a21Scmchao         s->bank[i].fiq = 0x00000000;
3517f132a21Scmchao         s->bank[i].inputs = 0x00000000;
3527f132a21Scmchao         s->bank[i].swi = 0x00000000;
3537f132a21Scmchao         memset(s->bank[i].priority, 0, sizeof(s->bank[i].priority));
3547f132a21Scmchao 
3557f132a21Scmchao         if (s->level_only)
3567f132a21Scmchao             s->bank[i].sens_edge = 0xffffffff;
3577f132a21Scmchao     }
3587f132a21Scmchao 
3597f132a21Scmchao     s->new_agr[0] = ~0;
3607f132a21Scmchao     s->new_agr[1] = ~0;
3617f132a21Scmchao     s->sir_intr[0] = 0;
3627f132a21Scmchao     s->sir_intr[1] = 0;
3637f132a21Scmchao     s->autoidle = 0;
3647f132a21Scmchao     s->mask = ~0;
3657f132a21Scmchao 
3667f132a21Scmchao     qemu_set_irq(s->parent_intr[0], 0);
3677f132a21Scmchao     qemu_set_irq(s->parent_intr[1], 0);
3687f132a21Scmchao }
3697f132a21Scmchao 
3700a750e2aSxiaoqiang zhao static void omap_intc_init(Object *obj)
3717f132a21Scmchao {
3720a750e2aSxiaoqiang zhao     DeviceState *dev = DEVICE(obj);
3730a750e2aSxiaoqiang zhao     struct omap_intr_handler_s *s = OMAP_INTC(obj);
3740a750e2aSxiaoqiang zhao     SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
37547edc5a4SAndreas Färber 
3760919ac78SPeter Maydell     s->nbanks = 1;
37747edc5a4SAndreas Färber     sysbus_init_irq(sbd, &s->parent_intr[0]);
37847edc5a4SAndreas Färber     sysbus_init_irq(sbd, &s->parent_intr[1]);
37947edc5a4SAndreas Färber     qdev_init_gpio_in(dev, omap_set_intr, s->nbanks * 32);
3800a750e2aSxiaoqiang zhao     memory_region_init_io(&s->mmio, obj, &omap_inth_mem_ops, s,
3810919ac78SPeter Maydell                           "omap-intc", s->size);
38247edc5a4SAndreas Färber     sysbus_init_mmio(sbd, &s->mmio);
3830a750e2aSxiaoqiang zhao }
3840a750e2aSxiaoqiang zhao 
3850a750e2aSxiaoqiang zhao static void omap_intc_realize(DeviceState *dev, Error **errp)
3860a750e2aSxiaoqiang zhao {
3870a750e2aSxiaoqiang zhao     struct omap_intr_handler_s *s = OMAP_INTC(dev);
3880a750e2aSxiaoqiang zhao 
3890a750e2aSxiaoqiang zhao     if (!s->iclk) {
3900a750e2aSxiaoqiang zhao         error_setg(errp, "omap-intc: clk not connected");
3910a750e2aSxiaoqiang zhao     }
3920919ac78SPeter Maydell }
3930919ac78SPeter Maydell 
394999e12bbSAnthony Liguori static Property omap_intc_properties[] = {
3950919ac78SPeter Maydell     DEFINE_PROP_UINT32("size", struct omap_intr_handler_s, size, 0x100),
3960919ac78SPeter Maydell     DEFINE_PROP_PTR("clk", struct omap_intr_handler_s, iclk),
397999e12bbSAnthony Liguori     DEFINE_PROP_END_OF_LIST(),
398999e12bbSAnthony Liguori };
399999e12bbSAnthony Liguori 
400999e12bbSAnthony Liguori static void omap_intc_class_init(ObjectClass *klass, void *data)
401999e12bbSAnthony Liguori {
40239bffca2SAnthony Liguori     DeviceClass *dc = DEVICE_CLASS(klass);
403999e12bbSAnthony Liguori 
40439bffca2SAnthony Liguori     dc->reset = omap_inth_reset;
40539bffca2SAnthony Liguori     dc->props = omap_intc_properties;
4061b111dc1SMarkus Armbruster     /* Reason: pointer property "clk" */
407e90f2a8cSEduardo Habkost     dc->user_creatable = false;
4080a750e2aSxiaoqiang zhao     dc->realize = omap_intc_realize;
4090919ac78SPeter Maydell }
410999e12bbSAnthony Liguori 
4118c43a6f0SAndreas Färber static const TypeInfo omap_intc_info = {
412999e12bbSAnthony Liguori     .name          = "omap-intc",
41347edc5a4SAndreas Färber     .parent        = TYPE_OMAP_INTC,
4140a750e2aSxiaoqiang zhao     .instance_init = omap_intc_init,
415999e12bbSAnthony Liguori     .class_init    = omap_intc_class_init,
4160919ac78SPeter Maydell };
4177f132a21Scmchao 
418a8170e5eSAvi Kivity static uint64_t omap2_inth_read(void *opaque, hwaddr addr,
41953bb614eSPeter Maydell                                 unsigned size)
4207f132a21Scmchao {
4217f132a21Scmchao     struct omap_intr_handler_s *s = (struct omap_intr_handler_s *) opaque;
4227f132a21Scmchao     int offset = addr;
4237f132a21Scmchao     int bank_no, line_no;
4247f132a21Scmchao     struct omap_intr_handler_bank_s *bank = NULL;
4257f132a21Scmchao 
4267f132a21Scmchao     if ((offset & 0xf80) == 0x80) {
4277f132a21Scmchao         bank_no = (offset & 0x60) >> 5;
4287f132a21Scmchao         if (bank_no < s->nbanks) {
4297f132a21Scmchao             offset &= ~0x60;
4307f132a21Scmchao             bank = &s->bank[bank_no];
431096685fcSPeter Maydell         } else {
432096685fcSPeter Maydell             OMAP_BAD_REG(addr);
433096685fcSPeter Maydell             return 0;
4347f132a21Scmchao         }
4357f132a21Scmchao     }
4367f132a21Scmchao 
4377f132a21Scmchao     switch (offset) {
4387f132a21Scmchao     case 0x00:	/* INTC_REVISION */
4390919ac78SPeter Maydell         return s->revision;
4407f132a21Scmchao 
4417f132a21Scmchao     case 0x10:	/* INTC_SYSCONFIG */
4427f132a21Scmchao         return (s->autoidle >> 2) & 1;
4437f132a21Scmchao 
4447f132a21Scmchao     case 0x14:	/* INTC_SYSSTATUS */
4457f132a21Scmchao         return 1;						/* RESETDONE */
4467f132a21Scmchao 
4477f132a21Scmchao     case 0x40:	/* INTC_SIR_IRQ */
4487f132a21Scmchao         return s->sir_intr[0];
4497f132a21Scmchao 
4507f132a21Scmchao     case 0x44:	/* INTC_SIR_FIQ */
4517f132a21Scmchao         return s->sir_intr[1];
4527f132a21Scmchao 
4537f132a21Scmchao     case 0x48:	/* INTC_CONTROL */
4547f132a21Scmchao         return (!s->mask) << 2;					/* GLOBALMASK */
4557f132a21Scmchao 
4567f132a21Scmchao     case 0x4c:	/* INTC_PROTECTION */
4577f132a21Scmchao         return 0;
4587f132a21Scmchao 
4597f132a21Scmchao     case 0x50:	/* INTC_IDLE */
4607f132a21Scmchao         return s->autoidle & 3;
4617f132a21Scmchao 
4627f132a21Scmchao     /* Per-bank registers */
4637f132a21Scmchao     case 0x80:	/* INTC_ITR */
4647f132a21Scmchao         return bank->inputs;
4657f132a21Scmchao 
4667f132a21Scmchao     case 0x84:	/* INTC_MIR */
4677f132a21Scmchao         return bank->mask;
4687f132a21Scmchao 
4697f132a21Scmchao     case 0x88:	/* INTC_MIR_CLEAR */
4707f132a21Scmchao     case 0x8c:	/* INTC_MIR_SET */
4717f132a21Scmchao         return 0;
4727f132a21Scmchao 
4737f132a21Scmchao     case 0x90:	/* INTC_ISR_SET */
4747f132a21Scmchao         return bank->swi;
4757f132a21Scmchao 
4767f132a21Scmchao     case 0x94:	/* INTC_ISR_CLEAR */
4777f132a21Scmchao         return 0;
4787f132a21Scmchao 
4797f132a21Scmchao     case 0x98:	/* INTC_PENDING_IRQ */
4807f132a21Scmchao         return bank->irqs & ~bank->mask & ~bank->fiq;
4817f132a21Scmchao 
4827f132a21Scmchao     case 0x9c:	/* INTC_PENDING_FIQ */
4837f132a21Scmchao         return bank->irqs & ~bank->mask & bank->fiq;
4847f132a21Scmchao 
4857f132a21Scmchao     /* Per-line registers */
4867f132a21Scmchao     case 0x100 ... 0x300:	/* INTC_ILR */
4877f132a21Scmchao         bank_no = (offset - 0x100) >> 7;
4887f132a21Scmchao         if (bank_no > s->nbanks)
4897f132a21Scmchao             break;
4907f132a21Scmchao         bank = &s->bank[bank_no];
4917f132a21Scmchao         line_no = (offset & 0x7f) >> 2;
4927f132a21Scmchao         return (bank->priority[line_no] << 2) |
4937f132a21Scmchao                 ((bank->fiq >> line_no) & 1);
4947f132a21Scmchao     }
4957f132a21Scmchao     OMAP_BAD_REG(addr);
4967f132a21Scmchao     return 0;
4977f132a21Scmchao }
4987f132a21Scmchao 
499a8170e5eSAvi Kivity static void omap2_inth_write(void *opaque, hwaddr addr,
50053bb614eSPeter Maydell                              uint64_t value, unsigned size)
5017f132a21Scmchao {
5027f132a21Scmchao     struct omap_intr_handler_s *s = (struct omap_intr_handler_s *) opaque;
5037f132a21Scmchao     int offset = addr;
5047f132a21Scmchao     int bank_no, line_no;
5057f132a21Scmchao     struct omap_intr_handler_bank_s *bank = NULL;
5067f132a21Scmchao 
5077f132a21Scmchao     if ((offset & 0xf80) == 0x80) {
5087f132a21Scmchao         bank_no = (offset & 0x60) >> 5;
5097f132a21Scmchao         if (bank_no < s->nbanks) {
5107f132a21Scmchao             offset &= ~0x60;
5117f132a21Scmchao             bank = &s->bank[bank_no];
512096685fcSPeter Maydell         } else {
513096685fcSPeter Maydell             OMAP_BAD_REG(addr);
514096685fcSPeter Maydell             return;
5157f132a21Scmchao         }
5167f132a21Scmchao     }
5177f132a21Scmchao 
5187f132a21Scmchao     switch (offset) {
5197f132a21Scmchao     case 0x10:	/* INTC_SYSCONFIG */
5207f132a21Scmchao         s->autoidle &= 4;
5217f132a21Scmchao         s->autoidle |= (value & 1) << 2;
52247edc5a4SAndreas Färber         if (value & 2) {                                        /* SOFTRESET */
52347edc5a4SAndreas Färber             omap_inth_reset(DEVICE(s));
52447edc5a4SAndreas Färber         }
5257f132a21Scmchao         return;
5267f132a21Scmchao 
5277f132a21Scmchao     case 0x48:	/* INTC_CONTROL */
5287f132a21Scmchao         s->mask = (value & 4) ? 0 : ~0;				/* GLOBALMASK */
5297f132a21Scmchao         if (value & 2) {					/* NEWFIQAGR */
5307f132a21Scmchao             qemu_set_irq(s->parent_intr[1], 0);
5317f132a21Scmchao             s->new_agr[1] = ~0;
5327f132a21Scmchao             omap_inth_update(s, 1);
5337f132a21Scmchao         }
5347f132a21Scmchao         if (value & 1) {					/* NEWIRQAGR */
5357f132a21Scmchao             qemu_set_irq(s->parent_intr[0], 0);
5367f132a21Scmchao             s->new_agr[0] = ~0;
5377f132a21Scmchao             omap_inth_update(s, 0);
5387f132a21Scmchao         }
5397f132a21Scmchao         return;
5407f132a21Scmchao 
5417f132a21Scmchao     case 0x4c:	/* INTC_PROTECTION */
5427f132a21Scmchao         /* TODO: Make a bitmap (or sizeof(char)map) of access privileges
5437f132a21Scmchao          * for every register, see Chapter 3 and 4 for privileged mode.  */
5447f132a21Scmchao         if (value & 1)
5457f132a21Scmchao             fprintf(stderr, "%s: protection mode enable attempt\n",
546a89f364aSAlistair Francis                             __func__);
5477f132a21Scmchao         return;
5487f132a21Scmchao 
5497f132a21Scmchao     case 0x50:	/* INTC_IDLE */
5507f132a21Scmchao         s->autoidle &= ~3;
5517f132a21Scmchao         s->autoidle |= value & 3;
5527f132a21Scmchao         return;
5537f132a21Scmchao 
5547f132a21Scmchao     /* Per-bank registers */
5557f132a21Scmchao     case 0x84:	/* INTC_MIR */
5567f132a21Scmchao         bank->mask = value;
5577f132a21Scmchao         omap_inth_update(s, 0);
5587f132a21Scmchao         omap_inth_update(s, 1);
5597f132a21Scmchao         return;
5607f132a21Scmchao 
5617f132a21Scmchao     case 0x88:	/* INTC_MIR_CLEAR */
5627f132a21Scmchao         bank->mask &= ~value;
5637f132a21Scmchao         omap_inth_update(s, 0);
5647f132a21Scmchao         omap_inth_update(s, 1);
5657f132a21Scmchao         return;
5667f132a21Scmchao 
5677f132a21Scmchao     case 0x8c:	/* INTC_MIR_SET */
5687f132a21Scmchao         bank->mask |= value;
5697f132a21Scmchao         return;
5707f132a21Scmchao 
5717f132a21Scmchao     case 0x90:	/* INTC_ISR_SET */
5727f132a21Scmchao         bank->irqs |= bank->swi |= value;
5737f132a21Scmchao         omap_inth_update(s, 0);
5747f132a21Scmchao         omap_inth_update(s, 1);
5757f132a21Scmchao         return;
5767f132a21Scmchao 
5777f132a21Scmchao     case 0x94:	/* INTC_ISR_CLEAR */
5787f132a21Scmchao         bank->swi &= ~value;
5797f132a21Scmchao         bank->irqs = bank->swi & bank->inputs;
5807f132a21Scmchao         return;
5817f132a21Scmchao 
5827f132a21Scmchao     /* Per-line registers */
5837f132a21Scmchao     case 0x100 ... 0x300:	/* INTC_ILR */
5847f132a21Scmchao         bank_no = (offset - 0x100) >> 7;
5857f132a21Scmchao         if (bank_no > s->nbanks)
5867f132a21Scmchao             break;
5877f132a21Scmchao         bank = &s->bank[bank_no];
5887f132a21Scmchao         line_no = (offset & 0x7f) >> 2;
5897f132a21Scmchao         bank->priority[line_no] = (value >> 2) & 0x3f;
5907f132a21Scmchao         bank->fiq &= ~(1 << line_no);
5917f132a21Scmchao         bank->fiq |= (value & 1) << line_no;
5927f132a21Scmchao         return;
5937f132a21Scmchao 
5947f132a21Scmchao     case 0x00:	/* INTC_REVISION */
5957f132a21Scmchao     case 0x14:	/* INTC_SYSSTATUS */
5967f132a21Scmchao     case 0x40:	/* INTC_SIR_IRQ */
5977f132a21Scmchao     case 0x44:	/* INTC_SIR_FIQ */
5987f132a21Scmchao     case 0x80:	/* INTC_ITR */
5997f132a21Scmchao     case 0x98:	/* INTC_PENDING_IRQ */
6007f132a21Scmchao     case 0x9c:	/* INTC_PENDING_FIQ */
6017f132a21Scmchao         OMAP_RO_REG(addr);
6027f132a21Scmchao         return;
6037f132a21Scmchao     }
6047f132a21Scmchao     OMAP_BAD_REG(addr);
6057f132a21Scmchao }
6067f132a21Scmchao 
60753bb614eSPeter Maydell static const MemoryRegionOps omap2_inth_mem_ops = {
60853bb614eSPeter Maydell     .read = omap2_inth_read,
60953bb614eSPeter Maydell     .write = omap2_inth_write,
61053bb614eSPeter Maydell     .endianness = DEVICE_NATIVE_ENDIAN,
61153bb614eSPeter Maydell     .valid = {
61253bb614eSPeter Maydell         .min_access_size = 4,
61353bb614eSPeter Maydell         .max_access_size = 4,
61453bb614eSPeter Maydell     },
6157f132a21Scmchao };
6167f132a21Scmchao 
6170a750e2aSxiaoqiang zhao static void omap2_intc_init(Object *obj)
6187f132a21Scmchao {
6190a750e2aSxiaoqiang zhao     DeviceState *dev = DEVICE(obj);
6200a750e2aSxiaoqiang zhao     struct omap_intr_handler_s *s = OMAP_INTC(obj);
6210a750e2aSxiaoqiang zhao     SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
62247edc5a4SAndreas Färber 
6230919ac78SPeter Maydell     s->level_only = 1;
6240919ac78SPeter Maydell     s->nbanks = 3;
62547edc5a4SAndreas Färber     sysbus_init_irq(sbd, &s->parent_intr[0]);
62647edc5a4SAndreas Färber     sysbus_init_irq(sbd, &s->parent_intr[1]);
62747edc5a4SAndreas Färber     qdev_init_gpio_in(dev, omap_set_intr_noedge, s->nbanks * 32);
6280a750e2aSxiaoqiang zhao     memory_region_init_io(&s->mmio, obj, &omap2_inth_mem_ops, s,
6290919ac78SPeter Maydell                           "omap2-intc", 0x1000);
63047edc5a4SAndreas Färber     sysbus_init_mmio(sbd, &s->mmio);
6310a750e2aSxiaoqiang zhao }
6320a750e2aSxiaoqiang zhao 
6330a750e2aSxiaoqiang zhao static void omap2_intc_realize(DeviceState *dev, Error **errp)
6340a750e2aSxiaoqiang zhao {
6350a750e2aSxiaoqiang zhao     struct omap_intr_handler_s *s = OMAP_INTC(dev);
6360a750e2aSxiaoqiang zhao 
6370a750e2aSxiaoqiang zhao     if (!s->iclk) {
6380a750e2aSxiaoqiang zhao         error_setg(errp, "omap2-intc: iclk not connected");
6390a750e2aSxiaoqiang zhao         return;
6400a750e2aSxiaoqiang zhao     }
6410a750e2aSxiaoqiang zhao     if (!s->fclk) {
6420a750e2aSxiaoqiang zhao         error_setg(errp, "omap2-intc: fclk not connected");
6430a750e2aSxiaoqiang zhao         return;
6440a750e2aSxiaoqiang zhao     }
6450919ac78SPeter Maydell }
6460919ac78SPeter Maydell 
647999e12bbSAnthony Liguori static Property omap2_intc_properties[] = {
6480919ac78SPeter Maydell     DEFINE_PROP_UINT8("revision", struct omap_intr_handler_s,
6490919ac78SPeter Maydell     revision, 0x21),
6500919ac78SPeter Maydell     DEFINE_PROP_PTR("iclk", struct omap_intr_handler_s, iclk),
6510919ac78SPeter Maydell     DEFINE_PROP_PTR("fclk", struct omap_intr_handler_s, fclk),
652999e12bbSAnthony Liguori     DEFINE_PROP_END_OF_LIST(),
653999e12bbSAnthony Liguori };
654999e12bbSAnthony Liguori 
655999e12bbSAnthony Liguori static void omap2_intc_class_init(ObjectClass *klass, void *data)
656999e12bbSAnthony Liguori {
65739bffca2SAnthony Liguori     DeviceClass *dc = DEVICE_CLASS(klass);
658999e12bbSAnthony Liguori 
65939bffca2SAnthony Liguori     dc->reset = omap_inth_reset;
66039bffca2SAnthony Liguori     dc->props = omap2_intc_properties;
6611b111dc1SMarkus Armbruster     /* Reason: pointer property "iclk", "fclk" */
662e90f2a8cSEduardo Habkost     dc->user_creatable = false;
6630a750e2aSxiaoqiang zhao     dc->realize = omap2_intc_realize;
6640919ac78SPeter Maydell }
665999e12bbSAnthony Liguori 
6668c43a6f0SAndreas Färber static const TypeInfo omap2_intc_info = {
667999e12bbSAnthony Liguori     .name          = "omap2-intc",
66847edc5a4SAndreas Färber     .parent        = TYPE_OMAP_INTC,
6690a750e2aSxiaoqiang zhao     .instance_init = omap2_intc_init,
67047edc5a4SAndreas Färber     .class_init    = omap2_intc_class_init,
67147edc5a4SAndreas Färber };
67247edc5a4SAndreas Färber 
67347edc5a4SAndreas Färber static const TypeInfo omap_intc_type_info = {
67447edc5a4SAndreas Färber     .name          = TYPE_OMAP_INTC,
67539bffca2SAnthony Liguori     .parent        = TYPE_SYS_BUS_DEVICE,
67639bffca2SAnthony Liguori     .instance_size = sizeof(struct omap_intr_handler_s),
67747edc5a4SAndreas Färber     .abstract      = true,
6780919ac78SPeter Maydell };
6790919ac78SPeter Maydell 
68083f7d43aSAndreas Färber static void omap_intc_register_types(void)
6810919ac78SPeter Maydell {
68247edc5a4SAndreas Färber     type_register_static(&omap_intc_type_info);
68339bffca2SAnthony Liguori     type_register_static(&omap_intc_info);
68439bffca2SAnthony Liguori     type_register_static(&omap2_intc_info);
6850919ac78SPeter Maydell }
6860919ac78SPeter Maydell 
68783f7d43aSAndreas Färber type_init(omap_intc_register_types)
688