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