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" 23a27bd6c7SMarkus 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 41bded15c9SPhilippe Mathieu-Daudé struct OMAPIntcState { 4247edc5a4SAndreas Färber SysBusDevice parent_obj; 4347edc5a4SAndreas Färber 447f132a21Scmchao qemu_irq *pins; 457f132a21Scmchao qemu_irq parent_intr[2]; 4653bb614eSPeter Maydell MemoryRegion mmio; 470919ac78SPeter Maydell void *iclk; 480919ac78SPeter Maydell void *fclk; 497f132a21Scmchao unsigned char nbanks; 507f132a21Scmchao int level_only; 510919ac78SPeter Maydell uint32_t size; 520919ac78SPeter Maydell 530919ac78SPeter Maydell uint8_t revision; 547f132a21Scmchao 557f132a21Scmchao /* state */ 567f132a21Scmchao uint32_t new_agr[2]; 577f132a21Scmchao int sir_intr[2]; 587f132a21Scmchao int autoidle; 597f132a21Scmchao uint32_t mask; 600919ac78SPeter Maydell struct omap_intr_handler_bank_s bank[3]; 617f132a21Scmchao }; 627f132a21Scmchao 63bded15c9SPhilippe Mathieu-Daudé static void omap_inth_sir_update(OMAPIntcState *s, int is_fiq) 647f132a21Scmchao { 6541074f3dSPaolo Bonzini int i, j, sir_intr, p_intr, p; 667f132a21Scmchao uint32_t level; 677f132a21Scmchao sir_intr = 0; 687f132a21Scmchao p_intr = 255; 697f132a21Scmchao 707f132a21Scmchao /* Find the interrupt line with the highest dynamic priority. 719b4b4e51SMichael Tokarev * Note: 0 denotes the highest priority. 727f132a21Scmchao * If all interrupts have the same priority, the default order is IRQ_N, 737f132a21Scmchao * IRQ_N-1,...,IRQ_0. */ 747f132a21Scmchao for (j = 0; j < s->nbanks; ++j) { 757f132a21Scmchao level = s->bank[j].irqs & ~s->bank[j].mask & 767f132a21Scmchao (is_fiq ? s->bank[j].fiq : ~s->bank[j].fiq); 7741074f3dSPaolo Bonzini 7841074f3dSPaolo Bonzini while (level != 0) { 7941074f3dSPaolo Bonzini i = ctz32(level); 807f132a21Scmchao p = s->bank[j].priority[i]; 817f132a21Scmchao if (p <= p_intr) { 827f132a21Scmchao p_intr = p; 837f132a21Scmchao sir_intr = 32 * j + i; 847f132a21Scmchao } 8541074f3dSPaolo Bonzini level &= level - 1; 867f132a21Scmchao } 877f132a21Scmchao } 887f132a21Scmchao s->sir_intr[is_fiq] = sir_intr; 897f132a21Scmchao } 907f132a21Scmchao 91bded15c9SPhilippe Mathieu-Daudé static inline void omap_inth_update(OMAPIntcState *s, int is_fiq) 927f132a21Scmchao { 937f132a21Scmchao int i; 947f132a21Scmchao uint32_t has_intr = 0; 957f132a21Scmchao 967f132a21Scmchao for (i = 0; i < s->nbanks; ++i) 977f132a21Scmchao has_intr |= s->bank[i].irqs & ~s->bank[i].mask & 987f132a21Scmchao (is_fiq ? s->bank[i].fiq : ~s->bank[i].fiq); 997f132a21Scmchao 1007f132a21Scmchao if (s->new_agr[is_fiq] & has_intr & s->mask) { 1017f132a21Scmchao s->new_agr[is_fiq] = 0; 1027f132a21Scmchao omap_inth_sir_update(s, is_fiq); 1037f132a21Scmchao qemu_set_irq(s->parent_intr[is_fiq], 1); 1047f132a21Scmchao } 1057f132a21Scmchao } 1067f132a21Scmchao 1077f132a21Scmchao #define INT_FALLING_EDGE 0 1087f132a21Scmchao #define INT_LOW_LEVEL 1 1097f132a21Scmchao 1107f132a21Scmchao static void omap_set_intr(void *opaque, int irq, int req) 1117f132a21Scmchao { 112bded15c9SPhilippe Mathieu-Daudé OMAPIntcState *ih = opaque; 1137f132a21Scmchao uint32_t rise; 1147f132a21Scmchao 1157f132a21Scmchao struct omap_intr_handler_bank_s *bank = &ih->bank[irq >> 5]; 1167f132a21Scmchao int n = irq & 31; 1177f132a21Scmchao 1187f132a21Scmchao if (req) { 1197f132a21Scmchao rise = ~bank->irqs & (1 << n); 1207f132a21Scmchao if (~bank->sens_edge & (1 << n)) 1217f132a21Scmchao rise &= ~bank->inputs; 1227f132a21Scmchao 1237f132a21Scmchao bank->inputs |= (1 << n); 1247f132a21Scmchao if (rise) { 1257f132a21Scmchao bank->irqs |= rise; 1267f132a21Scmchao omap_inth_update(ih, 0); 1277f132a21Scmchao omap_inth_update(ih, 1); 1287f132a21Scmchao } 1297f132a21Scmchao } else { 1307f132a21Scmchao rise = bank->sens_edge & bank->irqs & (1 << n); 1317f132a21Scmchao bank->irqs &= ~rise; 1327f132a21Scmchao bank->inputs &= ~(1 << n); 1337f132a21Scmchao } 1347f132a21Scmchao } 1357f132a21Scmchao 1367f132a21Scmchao /* Simplified version with no edge detection */ 1377f132a21Scmchao static void omap_set_intr_noedge(void *opaque, int irq, int req) 1387f132a21Scmchao { 139bded15c9SPhilippe Mathieu-Daudé OMAPIntcState *ih = opaque; 1407f132a21Scmchao uint32_t rise; 1417f132a21Scmchao 1427f132a21Scmchao struct omap_intr_handler_bank_s *bank = &ih->bank[irq >> 5]; 1437f132a21Scmchao int n = irq & 31; 1447f132a21Scmchao 1457f132a21Scmchao if (req) { 1467f132a21Scmchao rise = ~bank->inputs & (1 << n); 1477f132a21Scmchao if (rise) { 1487f132a21Scmchao bank->irqs |= bank->inputs |= rise; 1497f132a21Scmchao omap_inth_update(ih, 0); 1507f132a21Scmchao omap_inth_update(ih, 1); 1517f132a21Scmchao } 1527f132a21Scmchao } else 1537f132a21Scmchao bank->irqs = (bank->inputs &= ~(1 << n)) | bank->swi; 1547f132a21Scmchao } 1557f132a21Scmchao 156a8170e5eSAvi Kivity static uint64_t omap_inth_read(void *opaque, hwaddr addr, 15753bb614eSPeter Maydell unsigned size) 1587f132a21Scmchao { 159bded15c9SPhilippe Mathieu-Daudé OMAPIntcState *s = opaque; 1607f132a21Scmchao int i, offset = addr; 1617f132a21Scmchao int bank_no = offset >> 8; 1627f132a21Scmchao int line_no; 1637f132a21Scmchao struct omap_intr_handler_bank_s *bank = &s->bank[bank_no]; 1647f132a21Scmchao offset &= 0xff; 1657f132a21Scmchao 1667f132a21Scmchao switch (offset) { 1677f132a21Scmchao case 0x00: /* ITR */ 1687f132a21Scmchao return bank->irqs; 1697f132a21Scmchao 1707f132a21Scmchao case 0x04: /* MIR */ 1717f132a21Scmchao return bank->mask; 1727f132a21Scmchao 1737f132a21Scmchao case 0x10: /* SIR_IRQ_CODE */ 1747f132a21Scmchao case 0x14: /* SIR_FIQ_CODE */ 1757f132a21Scmchao if (bank_no != 0) 1767f132a21Scmchao break; 1777f132a21Scmchao line_no = s->sir_intr[(offset - 0x10) >> 2]; 1787f132a21Scmchao bank = &s->bank[line_no >> 5]; 1797f132a21Scmchao i = line_no & 31; 1807f132a21Scmchao if (((bank->sens_edge >> i) & 1) == INT_FALLING_EDGE) 1817f132a21Scmchao bank->irqs &= ~(1 << i); 1827f132a21Scmchao return line_no; 1837f132a21Scmchao 1847f132a21Scmchao case 0x18: /* CONTROL_REG */ 1857f132a21Scmchao if (bank_no != 0) 1867f132a21Scmchao break; 1877f132a21Scmchao return 0; 1887f132a21Scmchao 1897f132a21Scmchao case 0x1c: /* ILR0 */ 1907f132a21Scmchao case 0x20: /* ILR1 */ 1917f132a21Scmchao case 0x24: /* ILR2 */ 1927f132a21Scmchao case 0x28: /* ILR3 */ 1937f132a21Scmchao case 0x2c: /* ILR4 */ 1947f132a21Scmchao case 0x30: /* ILR5 */ 1957f132a21Scmchao case 0x34: /* ILR6 */ 1967f132a21Scmchao case 0x38: /* ILR7 */ 1977f132a21Scmchao case 0x3c: /* ILR8 */ 1987f132a21Scmchao case 0x40: /* ILR9 */ 1997f132a21Scmchao case 0x44: /* ILR10 */ 2007f132a21Scmchao case 0x48: /* ILR11 */ 2017f132a21Scmchao case 0x4c: /* ILR12 */ 2027f132a21Scmchao case 0x50: /* ILR13 */ 2037f132a21Scmchao case 0x54: /* ILR14 */ 2047f132a21Scmchao case 0x58: /* ILR15 */ 2057f132a21Scmchao case 0x5c: /* ILR16 */ 2067f132a21Scmchao case 0x60: /* ILR17 */ 2077f132a21Scmchao case 0x64: /* ILR18 */ 2087f132a21Scmchao case 0x68: /* ILR19 */ 2097f132a21Scmchao case 0x6c: /* ILR20 */ 2107f132a21Scmchao case 0x70: /* ILR21 */ 2117f132a21Scmchao case 0x74: /* ILR22 */ 2127f132a21Scmchao case 0x78: /* ILR23 */ 2137f132a21Scmchao case 0x7c: /* ILR24 */ 2147f132a21Scmchao case 0x80: /* ILR25 */ 2157f132a21Scmchao case 0x84: /* ILR26 */ 2167f132a21Scmchao case 0x88: /* ILR27 */ 2177f132a21Scmchao case 0x8c: /* ILR28 */ 2187f132a21Scmchao case 0x90: /* ILR29 */ 2197f132a21Scmchao case 0x94: /* ILR30 */ 2207f132a21Scmchao case 0x98: /* ILR31 */ 2217f132a21Scmchao i = (offset - 0x1c) >> 2; 2227f132a21Scmchao return (bank->priority[i] << 2) | 2237f132a21Scmchao (((bank->sens_edge >> i) & 1) << 1) | 2247f132a21Scmchao ((bank->fiq >> i) & 1); 2257f132a21Scmchao 2267f132a21Scmchao case 0x9c: /* ISR */ 2277f132a21Scmchao return 0x00000000; 2287f132a21Scmchao 2297f132a21Scmchao } 2307f132a21Scmchao OMAP_BAD_REG(addr); 2317f132a21Scmchao return 0; 2327f132a21Scmchao } 2337f132a21Scmchao 234a8170e5eSAvi Kivity static void omap_inth_write(void *opaque, hwaddr addr, 23553bb614eSPeter Maydell uint64_t value, unsigned size) 2367f132a21Scmchao { 237bded15c9SPhilippe Mathieu-Daudé OMAPIntcState *s = opaque; 2387f132a21Scmchao int i, offset = addr; 2397f132a21Scmchao int bank_no = offset >> 8; 2407f132a21Scmchao struct omap_intr_handler_bank_s *bank = &s->bank[bank_no]; 2417f132a21Scmchao offset &= 0xff; 2427f132a21Scmchao 2437f132a21Scmchao switch (offset) { 2447f132a21Scmchao case 0x00: /* ITR */ 2457f132a21Scmchao /* Important: ignore the clearing if the IRQ is level-triggered and 2467f132a21Scmchao the input bit is 1 */ 2477f132a21Scmchao bank->irqs &= value | (bank->inputs & bank->sens_edge); 2487f132a21Scmchao return; 2497f132a21Scmchao 2507f132a21Scmchao case 0x04: /* MIR */ 2517f132a21Scmchao bank->mask = value; 2527f132a21Scmchao omap_inth_update(s, 0); 2537f132a21Scmchao omap_inth_update(s, 1); 2547f132a21Scmchao return; 2557f132a21Scmchao 2567f132a21Scmchao case 0x10: /* SIR_IRQ_CODE */ 2577f132a21Scmchao case 0x14: /* SIR_FIQ_CODE */ 2587f132a21Scmchao OMAP_RO_REG(addr); 2597f132a21Scmchao break; 2607f132a21Scmchao 2617f132a21Scmchao case 0x18: /* CONTROL_REG */ 2627f132a21Scmchao if (bank_no != 0) 2637f132a21Scmchao break; 2647f132a21Scmchao if (value & 2) { 2657f132a21Scmchao qemu_set_irq(s->parent_intr[1], 0); 2667f132a21Scmchao s->new_agr[1] = ~0; 2677f132a21Scmchao omap_inth_update(s, 1); 2687f132a21Scmchao } 2697f132a21Scmchao if (value & 1) { 2707f132a21Scmchao qemu_set_irq(s->parent_intr[0], 0); 2717f132a21Scmchao s->new_agr[0] = ~0; 2727f132a21Scmchao omap_inth_update(s, 0); 2737f132a21Scmchao } 2747f132a21Scmchao return; 2757f132a21Scmchao 2767f132a21Scmchao case 0x1c: /* ILR0 */ 2777f132a21Scmchao case 0x20: /* ILR1 */ 2787f132a21Scmchao case 0x24: /* ILR2 */ 2797f132a21Scmchao case 0x28: /* ILR3 */ 2807f132a21Scmchao case 0x2c: /* ILR4 */ 2817f132a21Scmchao case 0x30: /* ILR5 */ 2827f132a21Scmchao case 0x34: /* ILR6 */ 2837f132a21Scmchao case 0x38: /* ILR7 */ 2847f132a21Scmchao case 0x3c: /* ILR8 */ 2857f132a21Scmchao case 0x40: /* ILR9 */ 2867f132a21Scmchao case 0x44: /* ILR10 */ 2877f132a21Scmchao case 0x48: /* ILR11 */ 2887f132a21Scmchao case 0x4c: /* ILR12 */ 2897f132a21Scmchao case 0x50: /* ILR13 */ 2907f132a21Scmchao case 0x54: /* ILR14 */ 2917f132a21Scmchao case 0x58: /* ILR15 */ 2927f132a21Scmchao case 0x5c: /* ILR16 */ 2937f132a21Scmchao case 0x60: /* ILR17 */ 2947f132a21Scmchao case 0x64: /* ILR18 */ 2957f132a21Scmchao case 0x68: /* ILR19 */ 2967f132a21Scmchao case 0x6c: /* ILR20 */ 2977f132a21Scmchao case 0x70: /* ILR21 */ 2987f132a21Scmchao case 0x74: /* ILR22 */ 2997f132a21Scmchao case 0x78: /* ILR23 */ 3007f132a21Scmchao case 0x7c: /* ILR24 */ 3017f132a21Scmchao case 0x80: /* ILR25 */ 3027f132a21Scmchao case 0x84: /* ILR26 */ 3037f132a21Scmchao case 0x88: /* ILR27 */ 3047f132a21Scmchao case 0x8c: /* ILR28 */ 3057f132a21Scmchao case 0x90: /* ILR29 */ 3067f132a21Scmchao case 0x94: /* ILR30 */ 3077f132a21Scmchao case 0x98: /* ILR31 */ 3087f132a21Scmchao i = (offset - 0x1c) >> 2; 3097f132a21Scmchao bank->priority[i] = (value >> 2) & 0x1f; 3107f132a21Scmchao bank->sens_edge &= ~(1 << i); 3117f132a21Scmchao bank->sens_edge |= ((value >> 1) & 1) << i; 3127f132a21Scmchao bank->fiq &= ~(1 << i); 3137f132a21Scmchao bank->fiq |= (value & 1) << i; 3147f132a21Scmchao return; 3157f132a21Scmchao 3167f132a21Scmchao case 0x9c: /* ISR */ 3177f132a21Scmchao for (i = 0; i < 32; i ++) 3187f132a21Scmchao if (value & (1 << i)) { 3197f132a21Scmchao omap_set_intr(s, 32 * bank_no + i, 1); 3207f132a21Scmchao return; 3217f132a21Scmchao } 3227f132a21Scmchao return; 3237f132a21Scmchao } 3247f132a21Scmchao OMAP_BAD_REG(addr); 3257f132a21Scmchao } 3267f132a21Scmchao 32753bb614eSPeter Maydell static const MemoryRegionOps omap_inth_mem_ops = { 32853bb614eSPeter Maydell .read = omap_inth_read, 32953bb614eSPeter Maydell .write = omap_inth_write, 33053bb614eSPeter Maydell .endianness = DEVICE_NATIVE_ENDIAN, 33153bb614eSPeter Maydell .valid = { 33253bb614eSPeter Maydell .min_access_size = 4, 33353bb614eSPeter Maydell .max_access_size = 4, 33453bb614eSPeter Maydell }, 3357f132a21Scmchao }; 3367f132a21Scmchao 3370919ac78SPeter Maydell static void omap_inth_reset(DeviceState *dev) 3387f132a21Scmchao { 339bded15c9SPhilippe Mathieu-Daudé OMAPIntcState *s = OMAP_INTC(dev); 3407f132a21Scmchao int i; 3417f132a21Scmchao 3427f132a21Scmchao for (i = 0; i < s->nbanks; ++i){ 3437f132a21Scmchao s->bank[i].irqs = 0x00000000; 3447f132a21Scmchao s->bank[i].mask = 0xffffffff; 3457f132a21Scmchao s->bank[i].sens_edge = 0x00000000; 3467f132a21Scmchao s->bank[i].fiq = 0x00000000; 3477f132a21Scmchao s->bank[i].inputs = 0x00000000; 3487f132a21Scmchao s->bank[i].swi = 0x00000000; 3497f132a21Scmchao memset(s->bank[i].priority, 0, sizeof(s->bank[i].priority)); 3507f132a21Scmchao 3517f132a21Scmchao if (s->level_only) 3527f132a21Scmchao s->bank[i].sens_edge = 0xffffffff; 3537f132a21Scmchao } 3547f132a21Scmchao 3557f132a21Scmchao s->new_agr[0] = ~0; 3567f132a21Scmchao s->new_agr[1] = ~0; 3577f132a21Scmchao s->sir_intr[0] = 0; 3587f132a21Scmchao s->sir_intr[1] = 0; 3597f132a21Scmchao s->autoidle = 0; 3607f132a21Scmchao s->mask = ~0; 3617f132a21Scmchao 3627f132a21Scmchao qemu_set_irq(s->parent_intr[0], 0); 3637f132a21Scmchao qemu_set_irq(s->parent_intr[1], 0); 3647f132a21Scmchao } 3657f132a21Scmchao 3660a750e2aSxiaoqiang zhao static void omap_intc_init(Object *obj) 3677f132a21Scmchao { 3680a750e2aSxiaoqiang zhao DeviceState *dev = DEVICE(obj); 369bded15c9SPhilippe Mathieu-Daudé OMAPIntcState *s = OMAP_INTC(obj); 3700a750e2aSxiaoqiang zhao SysBusDevice *sbd = SYS_BUS_DEVICE(obj); 37147edc5a4SAndreas Färber 3720919ac78SPeter Maydell s->nbanks = 1; 37347edc5a4SAndreas Färber sysbus_init_irq(sbd, &s->parent_intr[0]); 37447edc5a4SAndreas Färber sysbus_init_irq(sbd, &s->parent_intr[1]); 37547edc5a4SAndreas Färber qdev_init_gpio_in(dev, omap_set_intr, s->nbanks * 32); 3760a750e2aSxiaoqiang zhao memory_region_init_io(&s->mmio, obj, &omap_inth_mem_ops, s, 3770919ac78SPeter Maydell "omap-intc", s->size); 37847edc5a4SAndreas Färber sysbus_init_mmio(sbd, &s->mmio); 3790a750e2aSxiaoqiang zhao } 3800a750e2aSxiaoqiang zhao 3810a750e2aSxiaoqiang zhao static void omap_intc_realize(DeviceState *dev, Error **errp) 3820a750e2aSxiaoqiang zhao { 383bded15c9SPhilippe Mathieu-Daudé OMAPIntcState *s = OMAP_INTC(dev); 3840a750e2aSxiaoqiang zhao 3850a750e2aSxiaoqiang zhao if (!s->iclk) { 3860a750e2aSxiaoqiang zhao error_setg(errp, "omap-intc: clk not connected"); 3870a750e2aSxiaoqiang zhao } 3880919ac78SPeter Maydell } 3890919ac78SPeter Maydell 390bded15c9SPhilippe Mathieu-Daudé void omap_intc_set_iclk(OMAPIntcState *intc, omap_clk clk) 391bab592a2SMarc-André Lureau { 392bab592a2SMarc-André Lureau intc->iclk = clk; 393bab592a2SMarc-André Lureau } 394bab592a2SMarc-André Lureau 395bded15c9SPhilippe Mathieu-Daudé void omap_intc_set_fclk(OMAPIntcState *intc, omap_clk clk) 396bab592a2SMarc-André Lureau { 397bab592a2SMarc-André Lureau intc->fclk = clk; 398bab592a2SMarc-André Lureau } 399bab592a2SMarc-André Lureau 400999e12bbSAnthony Liguori static Property omap_intc_properties[] = { 401bded15c9SPhilippe Mathieu-Daudé DEFINE_PROP_UINT32("size", OMAPIntcState, size, 0x100), 402999e12bbSAnthony Liguori DEFINE_PROP_END_OF_LIST(), 403999e12bbSAnthony Liguori }; 404999e12bbSAnthony Liguori 405999e12bbSAnthony Liguori static void omap_intc_class_init(ObjectClass *klass, void *data) 406999e12bbSAnthony Liguori { 40739bffca2SAnthony Liguori DeviceClass *dc = DEVICE_CLASS(klass); 408999e12bbSAnthony Liguori 409*e3d08143SPeter Maydell device_class_set_legacy_reset(dc, omap_inth_reset); 4104f67d30bSMarc-André Lureau device_class_set_props(dc, omap_intc_properties); 4111b111dc1SMarkus Armbruster /* Reason: pointer property "clk" */ 412e90f2a8cSEduardo Habkost dc->user_creatable = false; 4130a750e2aSxiaoqiang zhao dc->realize = omap_intc_realize; 4140919ac78SPeter Maydell } 415999e12bbSAnthony Liguori 4168c43a6f0SAndreas Färber static const TypeInfo omap_intc_info = { 417999e12bbSAnthony Liguori .name = "omap-intc", 41847edc5a4SAndreas Färber .parent = TYPE_OMAP_INTC, 4190a750e2aSxiaoqiang zhao .instance_init = omap_intc_init, 420999e12bbSAnthony Liguori .class_init = omap_intc_class_init, 4210919ac78SPeter Maydell }; 4227f132a21Scmchao 423a8170e5eSAvi Kivity static uint64_t omap2_inth_read(void *opaque, hwaddr addr, 42453bb614eSPeter Maydell unsigned size) 4257f132a21Scmchao { 426bded15c9SPhilippe Mathieu-Daudé OMAPIntcState *s = opaque; 4277f132a21Scmchao int offset = addr; 4287f132a21Scmchao int bank_no, line_no; 4297f132a21Scmchao struct omap_intr_handler_bank_s *bank = NULL; 4307f132a21Scmchao 4317f132a21Scmchao if ((offset & 0xf80) == 0x80) { 4327f132a21Scmchao bank_no = (offset & 0x60) >> 5; 4337f132a21Scmchao if (bank_no < s->nbanks) { 4347f132a21Scmchao offset &= ~0x60; 4357f132a21Scmchao bank = &s->bank[bank_no]; 436096685fcSPeter Maydell } else { 437096685fcSPeter Maydell OMAP_BAD_REG(addr); 438096685fcSPeter Maydell return 0; 4397f132a21Scmchao } 4407f132a21Scmchao } 4417f132a21Scmchao 4427f132a21Scmchao switch (offset) { 4437f132a21Scmchao case 0x00: /* INTC_REVISION */ 4440919ac78SPeter Maydell return s->revision; 4457f132a21Scmchao 4467f132a21Scmchao case 0x10: /* INTC_SYSCONFIG */ 4477f132a21Scmchao return (s->autoidle >> 2) & 1; 4487f132a21Scmchao 4497f132a21Scmchao case 0x14: /* INTC_SYSSTATUS */ 4507f132a21Scmchao return 1; /* RESETDONE */ 4517f132a21Scmchao 4527f132a21Scmchao case 0x40: /* INTC_SIR_IRQ */ 4537f132a21Scmchao return s->sir_intr[0]; 4547f132a21Scmchao 4557f132a21Scmchao case 0x44: /* INTC_SIR_FIQ */ 4567f132a21Scmchao return s->sir_intr[1]; 4577f132a21Scmchao 4587f132a21Scmchao case 0x48: /* INTC_CONTROL */ 4597f132a21Scmchao return (!s->mask) << 2; /* GLOBALMASK */ 4607f132a21Scmchao 4617f132a21Scmchao case 0x4c: /* INTC_PROTECTION */ 4627f132a21Scmchao return 0; 4637f132a21Scmchao 4647f132a21Scmchao case 0x50: /* INTC_IDLE */ 4657f132a21Scmchao return s->autoidle & 3; 4667f132a21Scmchao 4677f132a21Scmchao /* Per-bank registers */ 4687f132a21Scmchao case 0x80: /* INTC_ITR */ 4697f132a21Scmchao return bank->inputs; 4707f132a21Scmchao 4717f132a21Scmchao case 0x84: /* INTC_MIR */ 4727f132a21Scmchao return bank->mask; 4737f132a21Scmchao 4747f132a21Scmchao case 0x88: /* INTC_MIR_CLEAR */ 4757f132a21Scmchao case 0x8c: /* INTC_MIR_SET */ 4767f132a21Scmchao return 0; 4777f132a21Scmchao 4787f132a21Scmchao case 0x90: /* INTC_ISR_SET */ 4797f132a21Scmchao return bank->swi; 4807f132a21Scmchao 4817f132a21Scmchao case 0x94: /* INTC_ISR_CLEAR */ 4827f132a21Scmchao return 0; 4837f132a21Scmchao 4847f132a21Scmchao case 0x98: /* INTC_PENDING_IRQ */ 4857f132a21Scmchao return bank->irqs & ~bank->mask & ~bank->fiq; 4867f132a21Scmchao 4877f132a21Scmchao case 0x9c: /* INTC_PENDING_FIQ */ 4887f132a21Scmchao return bank->irqs & ~bank->mask & bank->fiq; 4897f132a21Scmchao 4907f132a21Scmchao /* Per-line registers */ 4917f132a21Scmchao case 0x100 ... 0x300: /* INTC_ILR */ 4927f132a21Scmchao bank_no = (offset - 0x100) >> 7; 4937f132a21Scmchao if (bank_no > s->nbanks) 4947f132a21Scmchao break; 4957f132a21Scmchao bank = &s->bank[bank_no]; 4967f132a21Scmchao line_no = (offset & 0x7f) >> 2; 4977f132a21Scmchao return (bank->priority[line_no] << 2) | 4987f132a21Scmchao ((bank->fiq >> line_no) & 1); 4997f132a21Scmchao } 5007f132a21Scmchao OMAP_BAD_REG(addr); 5017f132a21Scmchao return 0; 5027f132a21Scmchao } 5037f132a21Scmchao 504a8170e5eSAvi Kivity static void omap2_inth_write(void *opaque, hwaddr addr, 50553bb614eSPeter Maydell uint64_t value, unsigned size) 5067f132a21Scmchao { 507bded15c9SPhilippe Mathieu-Daudé OMAPIntcState *s = opaque; 5087f132a21Scmchao int offset = addr; 5097f132a21Scmchao int bank_no, line_no; 5107f132a21Scmchao struct omap_intr_handler_bank_s *bank = NULL; 5117f132a21Scmchao 5127f132a21Scmchao if ((offset & 0xf80) == 0x80) { 5137f132a21Scmchao bank_no = (offset & 0x60) >> 5; 5147f132a21Scmchao if (bank_no < s->nbanks) { 5157f132a21Scmchao offset &= ~0x60; 5167f132a21Scmchao bank = &s->bank[bank_no]; 517096685fcSPeter Maydell } else { 518096685fcSPeter Maydell OMAP_BAD_REG(addr); 519096685fcSPeter Maydell return; 5207f132a21Scmchao } 5217f132a21Scmchao } 5227f132a21Scmchao 5237f132a21Scmchao switch (offset) { 5247f132a21Scmchao case 0x10: /* INTC_SYSCONFIG */ 5257f132a21Scmchao s->autoidle &= 4; 5267f132a21Scmchao s->autoidle |= (value & 1) << 2; 52747edc5a4SAndreas Färber if (value & 2) { /* SOFTRESET */ 52847edc5a4SAndreas Färber omap_inth_reset(DEVICE(s)); 52947edc5a4SAndreas Färber } 5307f132a21Scmchao return; 5317f132a21Scmchao 5327f132a21Scmchao case 0x48: /* INTC_CONTROL */ 5337f132a21Scmchao s->mask = (value & 4) ? 0 : ~0; /* GLOBALMASK */ 5347f132a21Scmchao if (value & 2) { /* NEWFIQAGR */ 5357f132a21Scmchao qemu_set_irq(s->parent_intr[1], 0); 5367f132a21Scmchao s->new_agr[1] = ~0; 5377f132a21Scmchao omap_inth_update(s, 1); 5387f132a21Scmchao } 5397f132a21Scmchao if (value & 1) { /* NEWIRQAGR */ 5407f132a21Scmchao qemu_set_irq(s->parent_intr[0], 0); 5417f132a21Scmchao s->new_agr[0] = ~0; 5427f132a21Scmchao omap_inth_update(s, 0); 5437f132a21Scmchao } 5447f132a21Scmchao return; 5457f132a21Scmchao 5467f132a21Scmchao case 0x4c: /* INTC_PROTECTION */ 5477f132a21Scmchao /* TODO: Make a bitmap (or sizeof(char)map) of access privileges 5487f132a21Scmchao * for every register, see Chapter 3 and 4 for privileged mode. */ 5497f132a21Scmchao if (value & 1) 5507f132a21Scmchao fprintf(stderr, "%s: protection mode enable attempt\n", 551a89f364aSAlistair Francis __func__); 5527f132a21Scmchao return; 5537f132a21Scmchao 5547f132a21Scmchao case 0x50: /* INTC_IDLE */ 5557f132a21Scmchao s->autoidle &= ~3; 5567f132a21Scmchao s->autoidle |= value & 3; 5577f132a21Scmchao return; 5587f132a21Scmchao 5597f132a21Scmchao /* Per-bank registers */ 5607f132a21Scmchao case 0x84: /* INTC_MIR */ 5617f132a21Scmchao bank->mask = value; 5627f132a21Scmchao omap_inth_update(s, 0); 5637f132a21Scmchao omap_inth_update(s, 1); 5647f132a21Scmchao return; 5657f132a21Scmchao 5667f132a21Scmchao case 0x88: /* INTC_MIR_CLEAR */ 5677f132a21Scmchao bank->mask &= ~value; 5687f132a21Scmchao omap_inth_update(s, 0); 5697f132a21Scmchao omap_inth_update(s, 1); 5707f132a21Scmchao return; 5717f132a21Scmchao 5727f132a21Scmchao case 0x8c: /* INTC_MIR_SET */ 5737f132a21Scmchao bank->mask |= value; 5747f132a21Scmchao return; 5757f132a21Scmchao 5767f132a21Scmchao case 0x90: /* INTC_ISR_SET */ 5777f132a21Scmchao bank->irqs |= bank->swi |= value; 5787f132a21Scmchao omap_inth_update(s, 0); 5797f132a21Scmchao omap_inth_update(s, 1); 5807f132a21Scmchao return; 5817f132a21Scmchao 5827f132a21Scmchao case 0x94: /* INTC_ISR_CLEAR */ 5837f132a21Scmchao bank->swi &= ~value; 5847f132a21Scmchao bank->irqs = bank->swi & bank->inputs; 5857f132a21Scmchao return; 5867f132a21Scmchao 5877f132a21Scmchao /* Per-line registers */ 5887f132a21Scmchao case 0x100 ... 0x300: /* INTC_ILR */ 5897f132a21Scmchao bank_no = (offset - 0x100) >> 7; 5907f132a21Scmchao if (bank_no > s->nbanks) 5917f132a21Scmchao break; 5927f132a21Scmchao bank = &s->bank[bank_no]; 5937f132a21Scmchao line_no = (offset & 0x7f) >> 2; 5947f132a21Scmchao bank->priority[line_no] = (value >> 2) & 0x3f; 5957f132a21Scmchao bank->fiq &= ~(1 << line_no); 5967f132a21Scmchao bank->fiq |= (value & 1) << line_no; 5977f132a21Scmchao return; 5987f132a21Scmchao 5997f132a21Scmchao case 0x00: /* INTC_REVISION */ 6007f132a21Scmchao case 0x14: /* INTC_SYSSTATUS */ 6017f132a21Scmchao case 0x40: /* INTC_SIR_IRQ */ 6027f132a21Scmchao case 0x44: /* INTC_SIR_FIQ */ 6037f132a21Scmchao case 0x80: /* INTC_ITR */ 6047f132a21Scmchao case 0x98: /* INTC_PENDING_IRQ */ 6057f132a21Scmchao case 0x9c: /* INTC_PENDING_FIQ */ 6067f132a21Scmchao OMAP_RO_REG(addr); 6077f132a21Scmchao return; 6087f132a21Scmchao } 6097f132a21Scmchao OMAP_BAD_REG(addr); 6107f132a21Scmchao } 6117f132a21Scmchao 61253bb614eSPeter Maydell static const MemoryRegionOps omap2_inth_mem_ops = { 61353bb614eSPeter Maydell .read = omap2_inth_read, 61453bb614eSPeter Maydell .write = omap2_inth_write, 61553bb614eSPeter Maydell .endianness = DEVICE_NATIVE_ENDIAN, 61653bb614eSPeter Maydell .valid = { 61753bb614eSPeter Maydell .min_access_size = 4, 61853bb614eSPeter Maydell .max_access_size = 4, 61953bb614eSPeter Maydell }, 6207f132a21Scmchao }; 6217f132a21Scmchao 6220a750e2aSxiaoqiang zhao static void omap2_intc_init(Object *obj) 6237f132a21Scmchao { 6240a750e2aSxiaoqiang zhao DeviceState *dev = DEVICE(obj); 625bded15c9SPhilippe Mathieu-Daudé OMAPIntcState *s = OMAP_INTC(obj); 6260a750e2aSxiaoqiang zhao SysBusDevice *sbd = SYS_BUS_DEVICE(obj); 62747edc5a4SAndreas Färber 6280919ac78SPeter Maydell s->level_only = 1; 6290919ac78SPeter Maydell s->nbanks = 3; 63047edc5a4SAndreas Färber sysbus_init_irq(sbd, &s->parent_intr[0]); 63147edc5a4SAndreas Färber sysbus_init_irq(sbd, &s->parent_intr[1]); 63247edc5a4SAndreas Färber qdev_init_gpio_in(dev, omap_set_intr_noedge, s->nbanks * 32); 6330a750e2aSxiaoqiang zhao memory_region_init_io(&s->mmio, obj, &omap2_inth_mem_ops, s, 6340919ac78SPeter Maydell "omap2-intc", 0x1000); 63547edc5a4SAndreas Färber sysbus_init_mmio(sbd, &s->mmio); 6360a750e2aSxiaoqiang zhao } 6370a750e2aSxiaoqiang zhao 6380a750e2aSxiaoqiang zhao static void omap2_intc_realize(DeviceState *dev, Error **errp) 6390a750e2aSxiaoqiang zhao { 640bded15c9SPhilippe Mathieu-Daudé OMAPIntcState *s = OMAP_INTC(dev); 6410a750e2aSxiaoqiang zhao 6420a750e2aSxiaoqiang zhao if (!s->iclk) { 6430a750e2aSxiaoqiang zhao error_setg(errp, "omap2-intc: iclk not connected"); 6440a750e2aSxiaoqiang zhao return; 6450a750e2aSxiaoqiang zhao } 6460a750e2aSxiaoqiang zhao if (!s->fclk) { 6470a750e2aSxiaoqiang zhao error_setg(errp, "omap2-intc: fclk not connected"); 6480a750e2aSxiaoqiang zhao return; 6490a750e2aSxiaoqiang zhao } 6500919ac78SPeter Maydell } 6510919ac78SPeter Maydell 652999e12bbSAnthony Liguori static Property omap2_intc_properties[] = { 653bded15c9SPhilippe Mathieu-Daudé DEFINE_PROP_UINT8("revision", OMAPIntcState, 6540919ac78SPeter Maydell revision, 0x21), 655999e12bbSAnthony Liguori DEFINE_PROP_END_OF_LIST(), 656999e12bbSAnthony Liguori }; 657999e12bbSAnthony Liguori 658999e12bbSAnthony Liguori static void omap2_intc_class_init(ObjectClass *klass, void *data) 659999e12bbSAnthony Liguori { 66039bffca2SAnthony Liguori DeviceClass *dc = DEVICE_CLASS(klass); 661999e12bbSAnthony Liguori 662*e3d08143SPeter Maydell device_class_set_legacy_reset(dc, omap_inth_reset); 6634f67d30bSMarc-André Lureau device_class_set_props(dc, omap2_intc_properties); 6641b111dc1SMarkus Armbruster /* Reason: pointer property "iclk", "fclk" */ 665e90f2a8cSEduardo Habkost dc->user_creatable = false; 6660a750e2aSxiaoqiang zhao dc->realize = omap2_intc_realize; 6670919ac78SPeter Maydell } 668999e12bbSAnthony Liguori 6698c43a6f0SAndreas Färber static const TypeInfo omap2_intc_info = { 670999e12bbSAnthony Liguori .name = "omap2-intc", 67147edc5a4SAndreas Färber .parent = TYPE_OMAP_INTC, 6720a750e2aSxiaoqiang zhao .instance_init = omap2_intc_init, 67347edc5a4SAndreas Färber .class_init = omap2_intc_class_init, 67447edc5a4SAndreas Färber }; 67547edc5a4SAndreas Färber 67647edc5a4SAndreas Färber static const TypeInfo omap_intc_type_info = { 67747edc5a4SAndreas Färber .name = TYPE_OMAP_INTC, 67839bffca2SAnthony Liguori .parent = TYPE_SYS_BUS_DEVICE, 679bded15c9SPhilippe Mathieu-Daudé .instance_size = sizeof(OMAPIntcState), 68047edc5a4SAndreas Färber .abstract = true, 6810919ac78SPeter Maydell }; 6820919ac78SPeter Maydell 68383f7d43aSAndreas Färber static void omap_intc_register_types(void) 6840919ac78SPeter Maydell { 68547edc5a4SAndreas Färber type_register_static(&omap_intc_type_info); 68639bffca2SAnthony Liguori type_register_static(&omap_intc_info); 68739bffca2SAnthony Liguori type_register_static(&omap2_intc_info); 6880919ac78SPeter Maydell } 6890919ac78SPeter Maydell 69083f7d43aSAndreas Färber type_init(omap_intc_register_types) 691