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 */ 2090191d07SPeter Maydell #include "qemu/osdep.h" 2183c9f4caSPaolo Bonzini #include "hw/hw.h" 220d09e41aSPaolo Bonzini #include "hw/arm/omap.h" 2383c9f4caSPaolo Bonzini #include "hw/sysbus.h" 2484a3a53cSMarkus Armbruster #include "qemu/error-report.h" 25*0a750e2aSxiaoqiang zhao #include "qapi/error.h" 267f132a21Scmchao 277f132a21Scmchao /* Interrupt Handlers */ 287f132a21Scmchao struct omap_intr_handler_bank_s { 297f132a21Scmchao uint32_t irqs; 307f132a21Scmchao uint32_t inputs; 317f132a21Scmchao uint32_t mask; 327f132a21Scmchao uint32_t fiq; 337f132a21Scmchao uint32_t sens_edge; 347f132a21Scmchao uint32_t swi; 357f132a21Scmchao unsigned char priority[32]; 367f132a21Scmchao }; 377f132a21Scmchao 3847edc5a4SAndreas Färber #define TYPE_OMAP_INTC "common-omap-intc" 3947edc5a4SAndreas Färber #define OMAP_INTC(obj) \ 4047edc5a4SAndreas Färber OBJECT_CHECK(struct omap_intr_handler_s, (obj), TYPE_OMAP_INTC) 4147edc5a4SAndreas Färber 427f132a21Scmchao struct omap_intr_handler_s { 4347edc5a4SAndreas Färber SysBusDevice parent_obj; 4447edc5a4SAndreas Färber 457f132a21Scmchao qemu_irq *pins; 467f132a21Scmchao qemu_irq parent_intr[2]; 4753bb614eSPeter Maydell MemoryRegion mmio; 480919ac78SPeter Maydell void *iclk; 490919ac78SPeter Maydell void *fclk; 507f132a21Scmchao unsigned char nbanks; 517f132a21Scmchao int level_only; 520919ac78SPeter Maydell uint32_t size; 530919ac78SPeter Maydell 540919ac78SPeter Maydell uint8_t revision; 557f132a21Scmchao 567f132a21Scmchao /* state */ 577f132a21Scmchao uint32_t new_agr[2]; 587f132a21Scmchao int sir_intr[2]; 597f132a21Scmchao int autoidle; 607f132a21Scmchao uint32_t mask; 610919ac78SPeter Maydell struct omap_intr_handler_bank_s bank[3]; 627f132a21Scmchao }; 637f132a21Scmchao 647f132a21Scmchao static void omap_inth_sir_update(struct omap_intr_handler_s *s, int is_fiq) 657f132a21Scmchao { 6641074f3dSPaolo Bonzini int i, j, sir_intr, p_intr, p; 677f132a21Scmchao uint32_t level; 687f132a21Scmchao sir_intr = 0; 697f132a21Scmchao p_intr = 255; 707f132a21Scmchao 717f132a21Scmchao /* Find the interrupt line with the highest dynamic priority. 727f132a21Scmchao * Note: 0 denotes the hightest priority. 737f132a21Scmchao * If all interrupts have the same priority, the default order is IRQ_N, 747f132a21Scmchao * IRQ_N-1,...,IRQ_0. */ 757f132a21Scmchao for (j = 0; j < s->nbanks; ++j) { 767f132a21Scmchao level = s->bank[j].irqs & ~s->bank[j].mask & 777f132a21Scmchao (is_fiq ? s->bank[j].fiq : ~s->bank[j].fiq); 7841074f3dSPaolo Bonzini 7941074f3dSPaolo Bonzini while (level != 0) { 8041074f3dSPaolo Bonzini i = ctz32(level); 817f132a21Scmchao p = s->bank[j].priority[i]; 827f132a21Scmchao if (p <= p_intr) { 837f132a21Scmchao p_intr = p; 847f132a21Scmchao sir_intr = 32 * j + i; 857f132a21Scmchao } 8641074f3dSPaolo Bonzini level &= level - 1; 877f132a21Scmchao } 887f132a21Scmchao } 897f132a21Scmchao s->sir_intr[is_fiq] = sir_intr; 907f132a21Scmchao } 917f132a21Scmchao 927f132a21Scmchao static inline void omap_inth_update(struct omap_intr_handler_s *s, int is_fiq) 937f132a21Scmchao { 947f132a21Scmchao int i; 957f132a21Scmchao uint32_t has_intr = 0; 967f132a21Scmchao 977f132a21Scmchao for (i = 0; i < s->nbanks; ++i) 987f132a21Scmchao has_intr |= s->bank[i].irqs & ~s->bank[i].mask & 997f132a21Scmchao (is_fiq ? s->bank[i].fiq : ~s->bank[i].fiq); 1007f132a21Scmchao 1017f132a21Scmchao if (s->new_agr[is_fiq] & has_intr & s->mask) { 1027f132a21Scmchao s->new_agr[is_fiq] = 0; 1037f132a21Scmchao omap_inth_sir_update(s, is_fiq); 1047f132a21Scmchao qemu_set_irq(s->parent_intr[is_fiq], 1); 1057f132a21Scmchao } 1067f132a21Scmchao } 1077f132a21Scmchao 1087f132a21Scmchao #define INT_FALLING_EDGE 0 1097f132a21Scmchao #define INT_LOW_LEVEL 1 1107f132a21Scmchao 1117f132a21Scmchao static void omap_set_intr(void *opaque, int irq, int req) 1127f132a21Scmchao { 1137f132a21Scmchao struct omap_intr_handler_s *ih = (struct omap_intr_handler_s *) opaque; 1147f132a21Scmchao uint32_t rise; 1157f132a21Scmchao 1167f132a21Scmchao struct omap_intr_handler_bank_s *bank = &ih->bank[irq >> 5]; 1177f132a21Scmchao int n = irq & 31; 1187f132a21Scmchao 1197f132a21Scmchao if (req) { 1207f132a21Scmchao rise = ~bank->irqs & (1 << n); 1217f132a21Scmchao if (~bank->sens_edge & (1 << n)) 1227f132a21Scmchao rise &= ~bank->inputs; 1237f132a21Scmchao 1247f132a21Scmchao bank->inputs |= (1 << n); 1257f132a21Scmchao if (rise) { 1267f132a21Scmchao bank->irqs |= rise; 1277f132a21Scmchao omap_inth_update(ih, 0); 1287f132a21Scmchao omap_inth_update(ih, 1); 1297f132a21Scmchao } 1307f132a21Scmchao } else { 1317f132a21Scmchao rise = bank->sens_edge & bank->irqs & (1 << n); 1327f132a21Scmchao bank->irqs &= ~rise; 1337f132a21Scmchao bank->inputs &= ~(1 << n); 1347f132a21Scmchao } 1357f132a21Scmchao } 1367f132a21Scmchao 1377f132a21Scmchao /* Simplified version with no edge detection */ 1387f132a21Scmchao static void omap_set_intr_noedge(void *opaque, int irq, int req) 1397f132a21Scmchao { 1407f132a21Scmchao struct omap_intr_handler_s *ih = (struct omap_intr_handler_s *) opaque; 1417f132a21Scmchao uint32_t rise; 1427f132a21Scmchao 1437f132a21Scmchao struct omap_intr_handler_bank_s *bank = &ih->bank[irq >> 5]; 1447f132a21Scmchao int n = irq & 31; 1457f132a21Scmchao 1467f132a21Scmchao if (req) { 1477f132a21Scmchao rise = ~bank->inputs & (1 << n); 1487f132a21Scmchao if (rise) { 1497f132a21Scmchao bank->irqs |= bank->inputs |= rise; 1507f132a21Scmchao omap_inth_update(ih, 0); 1517f132a21Scmchao omap_inth_update(ih, 1); 1527f132a21Scmchao } 1537f132a21Scmchao } else 1547f132a21Scmchao bank->irqs = (bank->inputs &= ~(1 << n)) | bank->swi; 1557f132a21Scmchao } 1567f132a21Scmchao 157a8170e5eSAvi Kivity static uint64_t omap_inth_read(void *opaque, hwaddr addr, 15853bb614eSPeter Maydell unsigned size) 1597f132a21Scmchao { 1607f132a21Scmchao struct omap_intr_handler_s *s = (struct omap_intr_handler_s *) opaque; 1617f132a21Scmchao int i, offset = addr; 1627f132a21Scmchao int bank_no = offset >> 8; 1637f132a21Scmchao int line_no; 1647f132a21Scmchao struct omap_intr_handler_bank_s *bank = &s->bank[bank_no]; 1657f132a21Scmchao offset &= 0xff; 1667f132a21Scmchao 1677f132a21Scmchao switch (offset) { 1687f132a21Scmchao case 0x00: /* ITR */ 1697f132a21Scmchao return bank->irqs; 1707f132a21Scmchao 1717f132a21Scmchao case 0x04: /* MIR */ 1727f132a21Scmchao return bank->mask; 1737f132a21Scmchao 1747f132a21Scmchao case 0x10: /* SIR_IRQ_CODE */ 1757f132a21Scmchao case 0x14: /* SIR_FIQ_CODE */ 1767f132a21Scmchao if (bank_no != 0) 1777f132a21Scmchao break; 1787f132a21Scmchao line_no = s->sir_intr[(offset - 0x10) >> 2]; 1797f132a21Scmchao bank = &s->bank[line_no >> 5]; 1807f132a21Scmchao i = line_no & 31; 1817f132a21Scmchao if (((bank->sens_edge >> i) & 1) == INT_FALLING_EDGE) 1827f132a21Scmchao bank->irqs &= ~(1 << i); 1837f132a21Scmchao return line_no; 1847f132a21Scmchao 1857f132a21Scmchao case 0x18: /* CONTROL_REG */ 1867f132a21Scmchao if (bank_no != 0) 1877f132a21Scmchao break; 1887f132a21Scmchao return 0; 1897f132a21Scmchao 1907f132a21Scmchao case 0x1c: /* ILR0 */ 1917f132a21Scmchao case 0x20: /* ILR1 */ 1927f132a21Scmchao case 0x24: /* ILR2 */ 1937f132a21Scmchao case 0x28: /* ILR3 */ 1947f132a21Scmchao case 0x2c: /* ILR4 */ 1957f132a21Scmchao case 0x30: /* ILR5 */ 1967f132a21Scmchao case 0x34: /* ILR6 */ 1977f132a21Scmchao case 0x38: /* ILR7 */ 1987f132a21Scmchao case 0x3c: /* ILR8 */ 1997f132a21Scmchao case 0x40: /* ILR9 */ 2007f132a21Scmchao case 0x44: /* ILR10 */ 2017f132a21Scmchao case 0x48: /* ILR11 */ 2027f132a21Scmchao case 0x4c: /* ILR12 */ 2037f132a21Scmchao case 0x50: /* ILR13 */ 2047f132a21Scmchao case 0x54: /* ILR14 */ 2057f132a21Scmchao case 0x58: /* ILR15 */ 2067f132a21Scmchao case 0x5c: /* ILR16 */ 2077f132a21Scmchao case 0x60: /* ILR17 */ 2087f132a21Scmchao case 0x64: /* ILR18 */ 2097f132a21Scmchao case 0x68: /* ILR19 */ 2107f132a21Scmchao case 0x6c: /* ILR20 */ 2117f132a21Scmchao case 0x70: /* ILR21 */ 2127f132a21Scmchao case 0x74: /* ILR22 */ 2137f132a21Scmchao case 0x78: /* ILR23 */ 2147f132a21Scmchao case 0x7c: /* ILR24 */ 2157f132a21Scmchao case 0x80: /* ILR25 */ 2167f132a21Scmchao case 0x84: /* ILR26 */ 2177f132a21Scmchao case 0x88: /* ILR27 */ 2187f132a21Scmchao case 0x8c: /* ILR28 */ 2197f132a21Scmchao case 0x90: /* ILR29 */ 2207f132a21Scmchao case 0x94: /* ILR30 */ 2217f132a21Scmchao case 0x98: /* ILR31 */ 2227f132a21Scmchao i = (offset - 0x1c) >> 2; 2237f132a21Scmchao return (bank->priority[i] << 2) | 2247f132a21Scmchao (((bank->sens_edge >> i) & 1) << 1) | 2257f132a21Scmchao ((bank->fiq >> i) & 1); 2267f132a21Scmchao 2277f132a21Scmchao case 0x9c: /* ISR */ 2287f132a21Scmchao return 0x00000000; 2297f132a21Scmchao 2307f132a21Scmchao } 2317f132a21Scmchao OMAP_BAD_REG(addr); 2327f132a21Scmchao return 0; 2337f132a21Scmchao } 2347f132a21Scmchao 235a8170e5eSAvi Kivity static void omap_inth_write(void *opaque, hwaddr addr, 23653bb614eSPeter Maydell uint64_t value, unsigned size) 2377f132a21Scmchao { 2387f132a21Scmchao struct omap_intr_handler_s *s = (struct omap_intr_handler_s *) opaque; 2397f132a21Scmchao int i, offset = addr; 2407f132a21Scmchao int bank_no = offset >> 8; 2417f132a21Scmchao struct omap_intr_handler_bank_s *bank = &s->bank[bank_no]; 2427f132a21Scmchao offset &= 0xff; 2437f132a21Scmchao 2447f132a21Scmchao switch (offset) { 2457f132a21Scmchao case 0x00: /* ITR */ 2467f132a21Scmchao /* Important: ignore the clearing if the IRQ is level-triggered and 2477f132a21Scmchao the input bit is 1 */ 2487f132a21Scmchao bank->irqs &= value | (bank->inputs & bank->sens_edge); 2497f132a21Scmchao return; 2507f132a21Scmchao 2517f132a21Scmchao case 0x04: /* MIR */ 2527f132a21Scmchao bank->mask = value; 2537f132a21Scmchao omap_inth_update(s, 0); 2547f132a21Scmchao omap_inth_update(s, 1); 2557f132a21Scmchao return; 2567f132a21Scmchao 2577f132a21Scmchao case 0x10: /* SIR_IRQ_CODE */ 2587f132a21Scmchao case 0x14: /* SIR_FIQ_CODE */ 2597f132a21Scmchao OMAP_RO_REG(addr); 2607f132a21Scmchao break; 2617f132a21Scmchao 2627f132a21Scmchao case 0x18: /* CONTROL_REG */ 2637f132a21Scmchao if (bank_no != 0) 2647f132a21Scmchao break; 2657f132a21Scmchao if (value & 2) { 2667f132a21Scmchao qemu_set_irq(s->parent_intr[1], 0); 2677f132a21Scmchao s->new_agr[1] = ~0; 2687f132a21Scmchao omap_inth_update(s, 1); 2697f132a21Scmchao } 2707f132a21Scmchao if (value & 1) { 2717f132a21Scmchao qemu_set_irq(s->parent_intr[0], 0); 2727f132a21Scmchao s->new_agr[0] = ~0; 2737f132a21Scmchao omap_inth_update(s, 0); 2747f132a21Scmchao } 2757f132a21Scmchao return; 2767f132a21Scmchao 2777f132a21Scmchao case 0x1c: /* ILR0 */ 2787f132a21Scmchao case 0x20: /* ILR1 */ 2797f132a21Scmchao case 0x24: /* ILR2 */ 2807f132a21Scmchao case 0x28: /* ILR3 */ 2817f132a21Scmchao case 0x2c: /* ILR4 */ 2827f132a21Scmchao case 0x30: /* ILR5 */ 2837f132a21Scmchao case 0x34: /* ILR6 */ 2847f132a21Scmchao case 0x38: /* ILR7 */ 2857f132a21Scmchao case 0x3c: /* ILR8 */ 2867f132a21Scmchao case 0x40: /* ILR9 */ 2877f132a21Scmchao case 0x44: /* ILR10 */ 2887f132a21Scmchao case 0x48: /* ILR11 */ 2897f132a21Scmchao case 0x4c: /* ILR12 */ 2907f132a21Scmchao case 0x50: /* ILR13 */ 2917f132a21Scmchao case 0x54: /* ILR14 */ 2927f132a21Scmchao case 0x58: /* ILR15 */ 2937f132a21Scmchao case 0x5c: /* ILR16 */ 2947f132a21Scmchao case 0x60: /* ILR17 */ 2957f132a21Scmchao case 0x64: /* ILR18 */ 2967f132a21Scmchao case 0x68: /* ILR19 */ 2977f132a21Scmchao case 0x6c: /* ILR20 */ 2987f132a21Scmchao case 0x70: /* ILR21 */ 2997f132a21Scmchao case 0x74: /* ILR22 */ 3007f132a21Scmchao case 0x78: /* ILR23 */ 3017f132a21Scmchao case 0x7c: /* ILR24 */ 3027f132a21Scmchao case 0x80: /* ILR25 */ 3037f132a21Scmchao case 0x84: /* ILR26 */ 3047f132a21Scmchao case 0x88: /* ILR27 */ 3057f132a21Scmchao case 0x8c: /* ILR28 */ 3067f132a21Scmchao case 0x90: /* ILR29 */ 3077f132a21Scmchao case 0x94: /* ILR30 */ 3087f132a21Scmchao case 0x98: /* ILR31 */ 3097f132a21Scmchao i = (offset - 0x1c) >> 2; 3107f132a21Scmchao bank->priority[i] = (value >> 2) & 0x1f; 3117f132a21Scmchao bank->sens_edge &= ~(1 << i); 3127f132a21Scmchao bank->sens_edge |= ((value >> 1) & 1) << i; 3137f132a21Scmchao bank->fiq &= ~(1 << i); 3147f132a21Scmchao bank->fiq |= (value & 1) << i; 3157f132a21Scmchao return; 3167f132a21Scmchao 3177f132a21Scmchao case 0x9c: /* ISR */ 3187f132a21Scmchao for (i = 0; i < 32; i ++) 3197f132a21Scmchao if (value & (1 << i)) { 3207f132a21Scmchao omap_set_intr(s, 32 * bank_no + i, 1); 3217f132a21Scmchao return; 3227f132a21Scmchao } 3237f132a21Scmchao return; 3247f132a21Scmchao } 3257f132a21Scmchao OMAP_BAD_REG(addr); 3267f132a21Scmchao } 3277f132a21Scmchao 32853bb614eSPeter Maydell static const MemoryRegionOps omap_inth_mem_ops = { 32953bb614eSPeter Maydell .read = omap_inth_read, 33053bb614eSPeter Maydell .write = omap_inth_write, 33153bb614eSPeter Maydell .endianness = DEVICE_NATIVE_ENDIAN, 33253bb614eSPeter Maydell .valid = { 33353bb614eSPeter Maydell .min_access_size = 4, 33453bb614eSPeter Maydell .max_access_size = 4, 33553bb614eSPeter Maydell }, 3367f132a21Scmchao }; 3377f132a21Scmchao 3380919ac78SPeter Maydell static void omap_inth_reset(DeviceState *dev) 3397f132a21Scmchao { 34047edc5a4SAndreas Färber struct omap_intr_handler_s *s = OMAP_INTC(dev); 3417f132a21Scmchao int i; 3427f132a21Scmchao 3437f132a21Scmchao for (i = 0; i < s->nbanks; ++i){ 3447f132a21Scmchao s->bank[i].irqs = 0x00000000; 3457f132a21Scmchao s->bank[i].mask = 0xffffffff; 3467f132a21Scmchao s->bank[i].sens_edge = 0x00000000; 3477f132a21Scmchao s->bank[i].fiq = 0x00000000; 3487f132a21Scmchao s->bank[i].inputs = 0x00000000; 3497f132a21Scmchao s->bank[i].swi = 0x00000000; 3507f132a21Scmchao memset(s->bank[i].priority, 0, sizeof(s->bank[i].priority)); 3517f132a21Scmchao 3527f132a21Scmchao if (s->level_only) 3537f132a21Scmchao s->bank[i].sens_edge = 0xffffffff; 3547f132a21Scmchao } 3557f132a21Scmchao 3567f132a21Scmchao s->new_agr[0] = ~0; 3577f132a21Scmchao s->new_agr[1] = ~0; 3587f132a21Scmchao s->sir_intr[0] = 0; 3597f132a21Scmchao s->sir_intr[1] = 0; 3607f132a21Scmchao s->autoidle = 0; 3617f132a21Scmchao s->mask = ~0; 3627f132a21Scmchao 3637f132a21Scmchao qemu_set_irq(s->parent_intr[0], 0); 3647f132a21Scmchao qemu_set_irq(s->parent_intr[1], 0); 3657f132a21Scmchao } 3667f132a21Scmchao 367*0a750e2aSxiaoqiang zhao static void omap_intc_init(Object *obj) 3687f132a21Scmchao { 369*0a750e2aSxiaoqiang zhao DeviceState *dev = DEVICE(obj); 370*0a750e2aSxiaoqiang zhao struct omap_intr_handler_s *s = OMAP_INTC(obj); 371*0a750e2aSxiaoqiang zhao SysBusDevice *sbd = SYS_BUS_DEVICE(obj); 37247edc5a4SAndreas Färber 3730919ac78SPeter Maydell s->nbanks = 1; 37447edc5a4SAndreas Färber sysbus_init_irq(sbd, &s->parent_intr[0]); 37547edc5a4SAndreas Färber sysbus_init_irq(sbd, &s->parent_intr[1]); 37647edc5a4SAndreas Färber qdev_init_gpio_in(dev, omap_set_intr, s->nbanks * 32); 377*0a750e2aSxiaoqiang zhao memory_region_init_io(&s->mmio, obj, &omap_inth_mem_ops, s, 3780919ac78SPeter Maydell "omap-intc", s->size); 37947edc5a4SAndreas Färber sysbus_init_mmio(sbd, &s->mmio); 380*0a750e2aSxiaoqiang zhao } 381*0a750e2aSxiaoqiang zhao 382*0a750e2aSxiaoqiang zhao static void omap_intc_realize(DeviceState *dev, Error **errp) 383*0a750e2aSxiaoqiang zhao { 384*0a750e2aSxiaoqiang zhao struct omap_intr_handler_s *s = OMAP_INTC(dev); 385*0a750e2aSxiaoqiang zhao 386*0a750e2aSxiaoqiang zhao if (!s->iclk) { 387*0a750e2aSxiaoqiang zhao error_setg(errp, "omap-intc: clk not connected"); 388*0a750e2aSxiaoqiang zhao } 3890919ac78SPeter Maydell } 3900919ac78SPeter Maydell 391999e12bbSAnthony Liguori static Property omap_intc_properties[] = { 3920919ac78SPeter Maydell DEFINE_PROP_UINT32("size", struct omap_intr_handler_s, size, 0x100), 3930919ac78SPeter Maydell DEFINE_PROP_PTR("clk", struct omap_intr_handler_s, iclk), 394999e12bbSAnthony Liguori DEFINE_PROP_END_OF_LIST(), 395999e12bbSAnthony Liguori }; 396999e12bbSAnthony Liguori 397999e12bbSAnthony Liguori static void omap_intc_class_init(ObjectClass *klass, void *data) 398999e12bbSAnthony Liguori { 39939bffca2SAnthony Liguori DeviceClass *dc = DEVICE_CLASS(klass); 400999e12bbSAnthony Liguori 40139bffca2SAnthony Liguori dc->reset = omap_inth_reset; 40239bffca2SAnthony Liguori dc->props = omap_intc_properties; 4031b111dc1SMarkus Armbruster /* Reason: pointer property "clk" */ 4041b111dc1SMarkus Armbruster dc->cannot_instantiate_with_device_add_yet = true; 405*0a750e2aSxiaoqiang zhao dc->realize = omap_intc_realize; 4060919ac78SPeter Maydell } 407999e12bbSAnthony Liguori 4088c43a6f0SAndreas Färber static const TypeInfo omap_intc_info = { 409999e12bbSAnthony Liguori .name = "omap-intc", 41047edc5a4SAndreas Färber .parent = TYPE_OMAP_INTC, 411*0a750e2aSxiaoqiang zhao .instance_init = omap_intc_init, 412999e12bbSAnthony Liguori .class_init = omap_intc_class_init, 4130919ac78SPeter Maydell }; 4147f132a21Scmchao 415a8170e5eSAvi Kivity static uint64_t omap2_inth_read(void *opaque, hwaddr addr, 41653bb614eSPeter Maydell unsigned size) 4177f132a21Scmchao { 4187f132a21Scmchao struct omap_intr_handler_s *s = (struct omap_intr_handler_s *) opaque; 4197f132a21Scmchao int offset = addr; 4207f132a21Scmchao int bank_no, line_no; 4217f132a21Scmchao struct omap_intr_handler_bank_s *bank = NULL; 4227f132a21Scmchao 4237f132a21Scmchao if ((offset & 0xf80) == 0x80) { 4247f132a21Scmchao bank_no = (offset & 0x60) >> 5; 4257f132a21Scmchao if (bank_no < s->nbanks) { 4267f132a21Scmchao offset &= ~0x60; 4277f132a21Scmchao bank = &s->bank[bank_no]; 428096685fcSPeter Maydell } else { 429096685fcSPeter Maydell OMAP_BAD_REG(addr); 430096685fcSPeter Maydell return 0; 4317f132a21Scmchao } 4327f132a21Scmchao } 4337f132a21Scmchao 4347f132a21Scmchao switch (offset) { 4357f132a21Scmchao case 0x00: /* INTC_REVISION */ 4360919ac78SPeter Maydell return s->revision; 4377f132a21Scmchao 4387f132a21Scmchao case 0x10: /* INTC_SYSCONFIG */ 4397f132a21Scmchao return (s->autoidle >> 2) & 1; 4407f132a21Scmchao 4417f132a21Scmchao case 0x14: /* INTC_SYSSTATUS */ 4427f132a21Scmchao return 1; /* RESETDONE */ 4437f132a21Scmchao 4447f132a21Scmchao case 0x40: /* INTC_SIR_IRQ */ 4457f132a21Scmchao return s->sir_intr[0]; 4467f132a21Scmchao 4477f132a21Scmchao case 0x44: /* INTC_SIR_FIQ */ 4487f132a21Scmchao return s->sir_intr[1]; 4497f132a21Scmchao 4507f132a21Scmchao case 0x48: /* INTC_CONTROL */ 4517f132a21Scmchao return (!s->mask) << 2; /* GLOBALMASK */ 4527f132a21Scmchao 4537f132a21Scmchao case 0x4c: /* INTC_PROTECTION */ 4547f132a21Scmchao return 0; 4557f132a21Scmchao 4567f132a21Scmchao case 0x50: /* INTC_IDLE */ 4577f132a21Scmchao return s->autoidle & 3; 4587f132a21Scmchao 4597f132a21Scmchao /* Per-bank registers */ 4607f132a21Scmchao case 0x80: /* INTC_ITR */ 4617f132a21Scmchao return bank->inputs; 4627f132a21Scmchao 4637f132a21Scmchao case 0x84: /* INTC_MIR */ 4647f132a21Scmchao return bank->mask; 4657f132a21Scmchao 4667f132a21Scmchao case 0x88: /* INTC_MIR_CLEAR */ 4677f132a21Scmchao case 0x8c: /* INTC_MIR_SET */ 4687f132a21Scmchao return 0; 4697f132a21Scmchao 4707f132a21Scmchao case 0x90: /* INTC_ISR_SET */ 4717f132a21Scmchao return bank->swi; 4727f132a21Scmchao 4737f132a21Scmchao case 0x94: /* INTC_ISR_CLEAR */ 4747f132a21Scmchao return 0; 4757f132a21Scmchao 4767f132a21Scmchao case 0x98: /* INTC_PENDING_IRQ */ 4777f132a21Scmchao return bank->irqs & ~bank->mask & ~bank->fiq; 4787f132a21Scmchao 4797f132a21Scmchao case 0x9c: /* INTC_PENDING_FIQ */ 4807f132a21Scmchao return bank->irqs & ~bank->mask & bank->fiq; 4817f132a21Scmchao 4827f132a21Scmchao /* Per-line registers */ 4837f132a21Scmchao case 0x100 ... 0x300: /* INTC_ILR */ 4847f132a21Scmchao bank_no = (offset - 0x100) >> 7; 4857f132a21Scmchao if (bank_no > s->nbanks) 4867f132a21Scmchao break; 4877f132a21Scmchao bank = &s->bank[bank_no]; 4887f132a21Scmchao line_no = (offset & 0x7f) >> 2; 4897f132a21Scmchao return (bank->priority[line_no] << 2) | 4907f132a21Scmchao ((bank->fiq >> line_no) & 1); 4917f132a21Scmchao } 4927f132a21Scmchao OMAP_BAD_REG(addr); 4937f132a21Scmchao return 0; 4947f132a21Scmchao } 4957f132a21Scmchao 496a8170e5eSAvi Kivity static void omap2_inth_write(void *opaque, hwaddr addr, 49753bb614eSPeter Maydell uint64_t value, unsigned size) 4987f132a21Scmchao { 4997f132a21Scmchao struct omap_intr_handler_s *s = (struct omap_intr_handler_s *) opaque; 5007f132a21Scmchao int offset = addr; 5017f132a21Scmchao int bank_no, line_no; 5027f132a21Scmchao struct omap_intr_handler_bank_s *bank = NULL; 5037f132a21Scmchao 5047f132a21Scmchao if ((offset & 0xf80) == 0x80) { 5057f132a21Scmchao bank_no = (offset & 0x60) >> 5; 5067f132a21Scmchao if (bank_no < s->nbanks) { 5077f132a21Scmchao offset &= ~0x60; 5087f132a21Scmchao bank = &s->bank[bank_no]; 509096685fcSPeter Maydell } else { 510096685fcSPeter Maydell OMAP_BAD_REG(addr); 511096685fcSPeter Maydell return; 5127f132a21Scmchao } 5137f132a21Scmchao } 5147f132a21Scmchao 5157f132a21Scmchao switch (offset) { 5167f132a21Scmchao case 0x10: /* INTC_SYSCONFIG */ 5177f132a21Scmchao s->autoidle &= 4; 5187f132a21Scmchao s->autoidle |= (value & 1) << 2; 51947edc5a4SAndreas Färber if (value & 2) { /* SOFTRESET */ 52047edc5a4SAndreas Färber omap_inth_reset(DEVICE(s)); 52147edc5a4SAndreas Färber } 5227f132a21Scmchao return; 5237f132a21Scmchao 5247f132a21Scmchao case 0x48: /* INTC_CONTROL */ 5257f132a21Scmchao s->mask = (value & 4) ? 0 : ~0; /* GLOBALMASK */ 5267f132a21Scmchao if (value & 2) { /* NEWFIQAGR */ 5277f132a21Scmchao qemu_set_irq(s->parent_intr[1], 0); 5287f132a21Scmchao s->new_agr[1] = ~0; 5297f132a21Scmchao omap_inth_update(s, 1); 5307f132a21Scmchao } 5317f132a21Scmchao if (value & 1) { /* NEWIRQAGR */ 5327f132a21Scmchao qemu_set_irq(s->parent_intr[0], 0); 5337f132a21Scmchao s->new_agr[0] = ~0; 5347f132a21Scmchao omap_inth_update(s, 0); 5357f132a21Scmchao } 5367f132a21Scmchao return; 5377f132a21Scmchao 5387f132a21Scmchao case 0x4c: /* INTC_PROTECTION */ 5397f132a21Scmchao /* TODO: Make a bitmap (or sizeof(char)map) of access privileges 5407f132a21Scmchao * for every register, see Chapter 3 and 4 for privileged mode. */ 5417f132a21Scmchao if (value & 1) 5427f132a21Scmchao fprintf(stderr, "%s: protection mode enable attempt\n", 5437f132a21Scmchao __FUNCTION__); 5447f132a21Scmchao return; 5457f132a21Scmchao 5467f132a21Scmchao case 0x50: /* INTC_IDLE */ 5477f132a21Scmchao s->autoidle &= ~3; 5487f132a21Scmchao s->autoidle |= value & 3; 5497f132a21Scmchao return; 5507f132a21Scmchao 5517f132a21Scmchao /* Per-bank registers */ 5527f132a21Scmchao case 0x84: /* INTC_MIR */ 5537f132a21Scmchao bank->mask = value; 5547f132a21Scmchao omap_inth_update(s, 0); 5557f132a21Scmchao omap_inth_update(s, 1); 5567f132a21Scmchao return; 5577f132a21Scmchao 5587f132a21Scmchao case 0x88: /* INTC_MIR_CLEAR */ 5597f132a21Scmchao bank->mask &= ~value; 5607f132a21Scmchao omap_inth_update(s, 0); 5617f132a21Scmchao omap_inth_update(s, 1); 5627f132a21Scmchao return; 5637f132a21Scmchao 5647f132a21Scmchao case 0x8c: /* INTC_MIR_SET */ 5657f132a21Scmchao bank->mask |= value; 5667f132a21Scmchao return; 5677f132a21Scmchao 5687f132a21Scmchao case 0x90: /* INTC_ISR_SET */ 5697f132a21Scmchao bank->irqs |= bank->swi |= value; 5707f132a21Scmchao omap_inth_update(s, 0); 5717f132a21Scmchao omap_inth_update(s, 1); 5727f132a21Scmchao return; 5737f132a21Scmchao 5747f132a21Scmchao case 0x94: /* INTC_ISR_CLEAR */ 5757f132a21Scmchao bank->swi &= ~value; 5767f132a21Scmchao bank->irqs = bank->swi & bank->inputs; 5777f132a21Scmchao return; 5787f132a21Scmchao 5797f132a21Scmchao /* Per-line registers */ 5807f132a21Scmchao case 0x100 ... 0x300: /* INTC_ILR */ 5817f132a21Scmchao bank_no = (offset - 0x100) >> 7; 5827f132a21Scmchao if (bank_no > s->nbanks) 5837f132a21Scmchao break; 5847f132a21Scmchao bank = &s->bank[bank_no]; 5857f132a21Scmchao line_no = (offset & 0x7f) >> 2; 5867f132a21Scmchao bank->priority[line_no] = (value >> 2) & 0x3f; 5877f132a21Scmchao bank->fiq &= ~(1 << line_no); 5887f132a21Scmchao bank->fiq |= (value & 1) << line_no; 5897f132a21Scmchao return; 5907f132a21Scmchao 5917f132a21Scmchao case 0x00: /* INTC_REVISION */ 5927f132a21Scmchao case 0x14: /* INTC_SYSSTATUS */ 5937f132a21Scmchao case 0x40: /* INTC_SIR_IRQ */ 5947f132a21Scmchao case 0x44: /* INTC_SIR_FIQ */ 5957f132a21Scmchao case 0x80: /* INTC_ITR */ 5967f132a21Scmchao case 0x98: /* INTC_PENDING_IRQ */ 5977f132a21Scmchao case 0x9c: /* INTC_PENDING_FIQ */ 5987f132a21Scmchao OMAP_RO_REG(addr); 5997f132a21Scmchao return; 6007f132a21Scmchao } 6017f132a21Scmchao OMAP_BAD_REG(addr); 6027f132a21Scmchao } 6037f132a21Scmchao 60453bb614eSPeter Maydell static const MemoryRegionOps omap2_inth_mem_ops = { 60553bb614eSPeter Maydell .read = omap2_inth_read, 60653bb614eSPeter Maydell .write = omap2_inth_write, 60753bb614eSPeter Maydell .endianness = DEVICE_NATIVE_ENDIAN, 60853bb614eSPeter Maydell .valid = { 60953bb614eSPeter Maydell .min_access_size = 4, 61053bb614eSPeter Maydell .max_access_size = 4, 61153bb614eSPeter Maydell }, 6127f132a21Scmchao }; 6137f132a21Scmchao 614*0a750e2aSxiaoqiang zhao static void omap2_intc_init(Object *obj) 6157f132a21Scmchao { 616*0a750e2aSxiaoqiang zhao DeviceState *dev = DEVICE(obj); 617*0a750e2aSxiaoqiang zhao struct omap_intr_handler_s *s = OMAP_INTC(obj); 618*0a750e2aSxiaoqiang zhao SysBusDevice *sbd = SYS_BUS_DEVICE(obj); 61947edc5a4SAndreas Färber 6200919ac78SPeter Maydell s->level_only = 1; 6210919ac78SPeter Maydell s->nbanks = 3; 62247edc5a4SAndreas Färber sysbus_init_irq(sbd, &s->parent_intr[0]); 62347edc5a4SAndreas Färber sysbus_init_irq(sbd, &s->parent_intr[1]); 62447edc5a4SAndreas Färber qdev_init_gpio_in(dev, omap_set_intr_noedge, s->nbanks * 32); 625*0a750e2aSxiaoqiang zhao memory_region_init_io(&s->mmio, obj, &omap2_inth_mem_ops, s, 6260919ac78SPeter Maydell "omap2-intc", 0x1000); 62747edc5a4SAndreas Färber sysbus_init_mmio(sbd, &s->mmio); 628*0a750e2aSxiaoqiang zhao } 629*0a750e2aSxiaoqiang zhao 630*0a750e2aSxiaoqiang zhao static void omap2_intc_realize(DeviceState *dev, Error **errp) 631*0a750e2aSxiaoqiang zhao { 632*0a750e2aSxiaoqiang zhao struct omap_intr_handler_s *s = OMAP_INTC(dev); 633*0a750e2aSxiaoqiang zhao 634*0a750e2aSxiaoqiang zhao if (!s->iclk) { 635*0a750e2aSxiaoqiang zhao error_setg(errp, "omap2-intc: iclk not connected"); 636*0a750e2aSxiaoqiang zhao return; 637*0a750e2aSxiaoqiang zhao } 638*0a750e2aSxiaoqiang zhao if (!s->fclk) { 639*0a750e2aSxiaoqiang zhao error_setg(errp, "omap2-intc: fclk not connected"); 640*0a750e2aSxiaoqiang zhao return; 641*0a750e2aSxiaoqiang zhao } 6420919ac78SPeter Maydell } 6430919ac78SPeter Maydell 644999e12bbSAnthony Liguori static Property omap2_intc_properties[] = { 6450919ac78SPeter Maydell DEFINE_PROP_UINT8("revision", struct omap_intr_handler_s, 6460919ac78SPeter Maydell revision, 0x21), 6470919ac78SPeter Maydell DEFINE_PROP_PTR("iclk", struct omap_intr_handler_s, iclk), 6480919ac78SPeter Maydell DEFINE_PROP_PTR("fclk", struct omap_intr_handler_s, fclk), 649999e12bbSAnthony Liguori DEFINE_PROP_END_OF_LIST(), 650999e12bbSAnthony Liguori }; 651999e12bbSAnthony Liguori 652999e12bbSAnthony Liguori static void omap2_intc_class_init(ObjectClass *klass, void *data) 653999e12bbSAnthony Liguori { 65439bffca2SAnthony Liguori DeviceClass *dc = DEVICE_CLASS(klass); 655999e12bbSAnthony Liguori 65639bffca2SAnthony Liguori dc->reset = omap_inth_reset; 65739bffca2SAnthony Liguori dc->props = omap2_intc_properties; 6581b111dc1SMarkus Armbruster /* Reason: pointer property "iclk", "fclk" */ 6591b111dc1SMarkus Armbruster dc->cannot_instantiate_with_device_add_yet = true; 660*0a750e2aSxiaoqiang zhao dc->realize = omap2_intc_realize; 6610919ac78SPeter Maydell } 662999e12bbSAnthony Liguori 6638c43a6f0SAndreas Färber static const TypeInfo omap2_intc_info = { 664999e12bbSAnthony Liguori .name = "omap2-intc", 66547edc5a4SAndreas Färber .parent = TYPE_OMAP_INTC, 666*0a750e2aSxiaoqiang zhao .instance_init = omap2_intc_init, 66747edc5a4SAndreas Färber .class_init = omap2_intc_class_init, 66847edc5a4SAndreas Färber }; 66947edc5a4SAndreas Färber 67047edc5a4SAndreas Färber static const TypeInfo omap_intc_type_info = { 67147edc5a4SAndreas Färber .name = TYPE_OMAP_INTC, 67239bffca2SAnthony Liguori .parent = TYPE_SYS_BUS_DEVICE, 67339bffca2SAnthony Liguori .instance_size = sizeof(struct omap_intr_handler_s), 67447edc5a4SAndreas Färber .abstract = true, 6750919ac78SPeter Maydell }; 6760919ac78SPeter Maydell 67783f7d43aSAndreas Färber static void omap_intc_register_types(void) 6780919ac78SPeter Maydell { 67947edc5a4SAndreas Färber type_register_static(&omap_intc_type_info); 68039bffca2SAnthony Liguori type_register_static(&omap_intc_info); 68139bffca2SAnthony Liguori type_register_static(&omap2_intc_info); 6820919ac78SPeter Maydell } 6830919ac78SPeter Maydell 68483f7d43aSAndreas Färber type_init(omap_intc_register_types) 685