17702e47cSPaolo Bonzini /* 27702e47cSPaolo Bonzini * TI OMAP interrupt controller emulation. 37702e47cSPaolo Bonzini * 47702e47cSPaolo Bonzini * Copyright (C) 2006-2008 Andrzej Zaborowski <balrog@zabor.org> 57702e47cSPaolo Bonzini * Copyright (C) 2007-2008 Nokia Corporation 67702e47cSPaolo Bonzini * 77702e47cSPaolo Bonzini * This program is free software; you can redistribute it and/or 87702e47cSPaolo Bonzini * modify it under the terms of the GNU General Public License as 97702e47cSPaolo Bonzini * published by the Free Software Foundation; either version 2 or 107702e47cSPaolo Bonzini * (at your option) version 3 of the License. 117702e47cSPaolo Bonzini * 127702e47cSPaolo Bonzini * This program is distributed in the hope that it will be useful, 137702e47cSPaolo Bonzini * but WITHOUT ANY WARRANTY; without even the implied warranty of 147702e47cSPaolo Bonzini * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 157702e47cSPaolo Bonzini * GNU General Public License for more details. 167702e47cSPaolo Bonzini * 177702e47cSPaolo Bonzini * You should have received a copy of the GNU General Public License along 187702e47cSPaolo Bonzini * with this program; if not, see <http://www.gnu.org/licenses/>. 197702e47cSPaolo Bonzini */ 207702e47cSPaolo Bonzini #include "hw/hw.h" 217702e47cSPaolo Bonzini #include "hw/arm/omap.h" 227702e47cSPaolo Bonzini #include "hw/sysbus.h" 237702e47cSPaolo Bonzini 247702e47cSPaolo Bonzini /* Interrupt Handlers */ 257702e47cSPaolo Bonzini struct omap_intr_handler_bank_s { 267702e47cSPaolo Bonzini uint32_t irqs; 277702e47cSPaolo Bonzini uint32_t inputs; 287702e47cSPaolo Bonzini uint32_t mask; 297702e47cSPaolo Bonzini uint32_t fiq; 307702e47cSPaolo Bonzini uint32_t sens_edge; 317702e47cSPaolo Bonzini uint32_t swi; 327702e47cSPaolo Bonzini unsigned char priority[32]; 337702e47cSPaolo Bonzini }; 347702e47cSPaolo Bonzini 3547edc5a4SAndreas Färber #define TYPE_OMAP_INTC "common-omap-intc" 3647edc5a4SAndreas Färber #define OMAP_INTC(obj) \ 3747edc5a4SAndreas Färber OBJECT_CHECK(struct omap_intr_handler_s, (obj), TYPE_OMAP_INTC) 3847edc5a4SAndreas Färber 397702e47cSPaolo Bonzini struct omap_intr_handler_s { 4047edc5a4SAndreas Färber SysBusDevice parent_obj; 4147edc5a4SAndreas Färber 427702e47cSPaolo Bonzini qemu_irq *pins; 437702e47cSPaolo Bonzini qemu_irq parent_intr[2]; 447702e47cSPaolo Bonzini MemoryRegion mmio; 457702e47cSPaolo Bonzini void *iclk; 467702e47cSPaolo Bonzini void *fclk; 477702e47cSPaolo Bonzini unsigned char nbanks; 487702e47cSPaolo Bonzini int level_only; 497702e47cSPaolo Bonzini uint32_t size; 507702e47cSPaolo Bonzini 517702e47cSPaolo Bonzini uint8_t revision; 527702e47cSPaolo Bonzini 537702e47cSPaolo Bonzini /* state */ 547702e47cSPaolo Bonzini uint32_t new_agr[2]; 557702e47cSPaolo Bonzini int sir_intr[2]; 567702e47cSPaolo Bonzini int autoidle; 577702e47cSPaolo Bonzini uint32_t mask; 587702e47cSPaolo Bonzini struct omap_intr_handler_bank_s bank[3]; 597702e47cSPaolo Bonzini }; 607702e47cSPaolo Bonzini 617702e47cSPaolo Bonzini static void omap_inth_sir_update(struct omap_intr_handler_s *s, int is_fiq) 627702e47cSPaolo Bonzini { 63*41074f3dSPaolo Bonzini int i, j, sir_intr, p_intr, p; 647702e47cSPaolo Bonzini uint32_t level; 657702e47cSPaolo Bonzini sir_intr = 0; 667702e47cSPaolo Bonzini p_intr = 255; 677702e47cSPaolo Bonzini 687702e47cSPaolo Bonzini /* Find the interrupt line with the highest dynamic priority. 697702e47cSPaolo Bonzini * Note: 0 denotes the hightest priority. 707702e47cSPaolo Bonzini * If all interrupts have the same priority, the default order is IRQ_N, 717702e47cSPaolo Bonzini * IRQ_N-1,...,IRQ_0. */ 727702e47cSPaolo Bonzini for (j = 0; j < s->nbanks; ++j) { 737702e47cSPaolo Bonzini level = s->bank[j].irqs & ~s->bank[j].mask & 747702e47cSPaolo Bonzini (is_fiq ? s->bank[j].fiq : ~s->bank[j].fiq); 75*41074f3dSPaolo Bonzini 76*41074f3dSPaolo Bonzini while (level != 0) { 77*41074f3dSPaolo Bonzini i = ctz32(level); 787702e47cSPaolo Bonzini p = s->bank[j].priority[i]; 797702e47cSPaolo Bonzini if (p <= p_intr) { 807702e47cSPaolo Bonzini p_intr = p; 817702e47cSPaolo Bonzini sir_intr = 32 * j + i; 827702e47cSPaolo Bonzini } 83*41074f3dSPaolo Bonzini level &= level - 1; 847702e47cSPaolo Bonzini } 857702e47cSPaolo Bonzini } 867702e47cSPaolo Bonzini s->sir_intr[is_fiq] = sir_intr; 877702e47cSPaolo Bonzini } 887702e47cSPaolo Bonzini 897702e47cSPaolo Bonzini static inline void omap_inth_update(struct omap_intr_handler_s *s, int is_fiq) 907702e47cSPaolo Bonzini { 917702e47cSPaolo Bonzini int i; 927702e47cSPaolo Bonzini uint32_t has_intr = 0; 937702e47cSPaolo Bonzini 947702e47cSPaolo Bonzini for (i = 0; i < s->nbanks; ++i) 957702e47cSPaolo Bonzini has_intr |= s->bank[i].irqs & ~s->bank[i].mask & 967702e47cSPaolo Bonzini (is_fiq ? s->bank[i].fiq : ~s->bank[i].fiq); 977702e47cSPaolo Bonzini 987702e47cSPaolo Bonzini if (s->new_agr[is_fiq] & has_intr & s->mask) { 997702e47cSPaolo Bonzini s->new_agr[is_fiq] = 0; 1007702e47cSPaolo Bonzini omap_inth_sir_update(s, is_fiq); 1017702e47cSPaolo Bonzini qemu_set_irq(s->parent_intr[is_fiq], 1); 1027702e47cSPaolo Bonzini } 1037702e47cSPaolo Bonzini } 1047702e47cSPaolo Bonzini 1057702e47cSPaolo Bonzini #define INT_FALLING_EDGE 0 1067702e47cSPaolo Bonzini #define INT_LOW_LEVEL 1 1077702e47cSPaolo Bonzini 1087702e47cSPaolo Bonzini static void omap_set_intr(void *opaque, int irq, int req) 1097702e47cSPaolo Bonzini { 1107702e47cSPaolo Bonzini struct omap_intr_handler_s *ih = (struct omap_intr_handler_s *) opaque; 1117702e47cSPaolo Bonzini uint32_t rise; 1127702e47cSPaolo Bonzini 1137702e47cSPaolo Bonzini struct omap_intr_handler_bank_s *bank = &ih->bank[irq >> 5]; 1147702e47cSPaolo Bonzini int n = irq & 31; 1157702e47cSPaolo Bonzini 1167702e47cSPaolo Bonzini if (req) { 1177702e47cSPaolo Bonzini rise = ~bank->irqs & (1 << n); 1187702e47cSPaolo Bonzini if (~bank->sens_edge & (1 << n)) 1197702e47cSPaolo Bonzini rise &= ~bank->inputs; 1207702e47cSPaolo Bonzini 1217702e47cSPaolo Bonzini bank->inputs |= (1 << n); 1227702e47cSPaolo Bonzini if (rise) { 1237702e47cSPaolo Bonzini bank->irqs |= rise; 1247702e47cSPaolo Bonzini omap_inth_update(ih, 0); 1257702e47cSPaolo Bonzini omap_inth_update(ih, 1); 1267702e47cSPaolo Bonzini } 1277702e47cSPaolo Bonzini } else { 1287702e47cSPaolo Bonzini rise = bank->sens_edge & bank->irqs & (1 << n); 1297702e47cSPaolo Bonzini bank->irqs &= ~rise; 1307702e47cSPaolo Bonzini bank->inputs &= ~(1 << n); 1317702e47cSPaolo Bonzini } 1327702e47cSPaolo Bonzini } 1337702e47cSPaolo Bonzini 1347702e47cSPaolo Bonzini /* Simplified version with no edge detection */ 1357702e47cSPaolo Bonzini static void omap_set_intr_noedge(void *opaque, int irq, int req) 1367702e47cSPaolo Bonzini { 1377702e47cSPaolo Bonzini struct omap_intr_handler_s *ih = (struct omap_intr_handler_s *) opaque; 1387702e47cSPaolo Bonzini uint32_t rise; 1397702e47cSPaolo Bonzini 1407702e47cSPaolo Bonzini struct omap_intr_handler_bank_s *bank = &ih->bank[irq >> 5]; 1417702e47cSPaolo Bonzini int n = irq & 31; 1427702e47cSPaolo Bonzini 1437702e47cSPaolo Bonzini if (req) { 1447702e47cSPaolo Bonzini rise = ~bank->inputs & (1 << n); 1457702e47cSPaolo Bonzini if (rise) { 1467702e47cSPaolo Bonzini bank->irqs |= bank->inputs |= rise; 1477702e47cSPaolo Bonzini omap_inth_update(ih, 0); 1487702e47cSPaolo Bonzini omap_inth_update(ih, 1); 1497702e47cSPaolo Bonzini } 1507702e47cSPaolo Bonzini } else 1517702e47cSPaolo Bonzini bank->irqs = (bank->inputs &= ~(1 << n)) | bank->swi; 1527702e47cSPaolo Bonzini } 1537702e47cSPaolo Bonzini 1547702e47cSPaolo Bonzini static uint64_t omap_inth_read(void *opaque, hwaddr addr, 1557702e47cSPaolo Bonzini unsigned size) 1567702e47cSPaolo Bonzini { 1577702e47cSPaolo Bonzini struct omap_intr_handler_s *s = (struct omap_intr_handler_s *) opaque; 1587702e47cSPaolo Bonzini int i, offset = addr; 1597702e47cSPaolo Bonzini int bank_no = offset >> 8; 1607702e47cSPaolo Bonzini int line_no; 1617702e47cSPaolo Bonzini struct omap_intr_handler_bank_s *bank = &s->bank[bank_no]; 1627702e47cSPaolo Bonzini offset &= 0xff; 1637702e47cSPaolo Bonzini 1647702e47cSPaolo Bonzini switch (offset) { 1657702e47cSPaolo Bonzini case 0x00: /* ITR */ 1667702e47cSPaolo Bonzini return bank->irqs; 1677702e47cSPaolo Bonzini 1687702e47cSPaolo Bonzini case 0x04: /* MIR */ 1697702e47cSPaolo Bonzini return bank->mask; 1707702e47cSPaolo Bonzini 1717702e47cSPaolo Bonzini case 0x10: /* SIR_IRQ_CODE */ 1727702e47cSPaolo Bonzini case 0x14: /* SIR_FIQ_CODE */ 1737702e47cSPaolo Bonzini if (bank_no != 0) 1747702e47cSPaolo Bonzini break; 1757702e47cSPaolo Bonzini line_no = s->sir_intr[(offset - 0x10) >> 2]; 1767702e47cSPaolo Bonzini bank = &s->bank[line_no >> 5]; 1777702e47cSPaolo Bonzini i = line_no & 31; 1787702e47cSPaolo Bonzini if (((bank->sens_edge >> i) & 1) == INT_FALLING_EDGE) 1797702e47cSPaolo Bonzini bank->irqs &= ~(1 << i); 1807702e47cSPaolo Bonzini return line_no; 1817702e47cSPaolo Bonzini 1827702e47cSPaolo Bonzini case 0x18: /* CONTROL_REG */ 1837702e47cSPaolo Bonzini if (bank_no != 0) 1847702e47cSPaolo Bonzini break; 1857702e47cSPaolo Bonzini return 0; 1867702e47cSPaolo Bonzini 1877702e47cSPaolo Bonzini case 0x1c: /* ILR0 */ 1887702e47cSPaolo Bonzini case 0x20: /* ILR1 */ 1897702e47cSPaolo Bonzini case 0x24: /* ILR2 */ 1907702e47cSPaolo Bonzini case 0x28: /* ILR3 */ 1917702e47cSPaolo Bonzini case 0x2c: /* ILR4 */ 1927702e47cSPaolo Bonzini case 0x30: /* ILR5 */ 1937702e47cSPaolo Bonzini case 0x34: /* ILR6 */ 1947702e47cSPaolo Bonzini case 0x38: /* ILR7 */ 1957702e47cSPaolo Bonzini case 0x3c: /* ILR8 */ 1967702e47cSPaolo Bonzini case 0x40: /* ILR9 */ 1977702e47cSPaolo Bonzini case 0x44: /* ILR10 */ 1987702e47cSPaolo Bonzini case 0x48: /* ILR11 */ 1997702e47cSPaolo Bonzini case 0x4c: /* ILR12 */ 2007702e47cSPaolo Bonzini case 0x50: /* ILR13 */ 2017702e47cSPaolo Bonzini case 0x54: /* ILR14 */ 2027702e47cSPaolo Bonzini case 0x58: /* ILR15 */ 2037702e47cSPaolo Bonzini case 0x5c: /* ILR16 */ 2047702e47cSPaolo Bonzini case 0x60: /* ILR17 */ 2057702e47cSPaolo Bonzini case 0x64: /* ILR18 */ 2067702e47cSPaolo Bonzini case 0x68: /* ILR19 */ 2077702e47cSPaolo Bonzini case 0x6c: /* ILR20 */ 2087702e47cSPaolo Bonzini case 0x70: /* ILR21 */ 2097702e47cSPaolo Bonzini case 0x74: /* ILR22 */ 2107702e47cSPaolo Bonzini case 0x78: /* ILR23 */ 2117702e47cSPaolo Bonzini case 0x7c: /* ILR24 */ 2127702e47cSPaolo Bonzini case 0x80: /* ILR25 */ 2137702e47cSPaolo Bonzini case 0x84: /* ILR26 */ 2147702e47cSPaolo Bonzini case 0x88: /* ILR27 */ 2157702e47cSPaolo Bonzini case 0x8c: /* ILR28 */ 2167702e47cSPaolo Bonzini case 0x90: /* ILR29 */ 2177702e47cSPaolo Bonzini case 0x94: /* ILR30 */ 2187702e47cSPaolo Bonzini case 0x98: /* ILR31 */ 2197702e47cSPaolo Bonzini i = (offset - 0x1c) >> 2; 2207702e47cSPaolo Bonzini return (bank->priority[i] << 2) | 2217702e47cSPaolo Bonzini (((bank->sens_edge >> i) & 1) << 1) | 2227702e47cSPaolo Bonzini ((bank->fiq >> i) & 1); 2237702e47cSPaolo Bonzini 2247702e47cSPaolo Bonzini case 0x9c: /* ISR */ 2257702e47cSPaolo Bonzini return 0x00000000; 2267702e47cSPaolo Bonzini 2277702e47cSPaolo Bonzini } 2287702e47cSPaolo Bonzini OMAP_BAD_REG(addr); 2297702e47cSPaolo Bonzini return 0; 2307702e47cSPaolo Bonzini } 2317702e47cSPaolo Bonzini 2327702e47cSPaolo Bonzini static void omap_inth_write(void *opaque, hwaddr addr, 2337702e47cSPaolo Bonzini uint64_t value, unsigned size) 2347702e47cSPaolo Bonzini { 2357702e47cSPaolo Bonzini struct omap_intr_handler_s *s = (struct omap_intr_handler_s *) opaque; 2367702e47cSPaolo Bonzini int i, offset = addr; 2377702e47cSPaolo Bonzini int bank_no = offset >> 8; 2387702e47cSPaolo Bonzini struct omap_intr_handler_bank_s *bank = &s->bank[bank_no]; 2397702e47cSPaolo Bonzini offset &= 0xff; 2407702e47cSPaolo Bonzini 2417702e47cSPaolo Bonzini switch (offset) { 2427702e47cSPaolo Bonzini case 0x00: /* ITR */ 2437702e47cSPaolo Bonzini /* Important: ignore the clearing if the IRQ is level-triggered and 2447702e47cSPaolo Bonzini the input bit is 1 */ 2457702e47cSPaolo Bonzini bank->irqs &= value | (bank->inputs & bank->sens_edge); 2467702e47cSPaolo Bonzini return; 2477702e47cSPaolo Bonzini 2487702e47cSPaolo Bonzini case 0x04: /* MIR */ 2497702e47cSPaolo Bonzini bank->mask = value; 2507702e47cSPaolo Bonzini omap_inth_update(s, 0); 2517702e47cSPaolo Bonzini omap_inth_update(s, 1); 2527702e47cSPaolo Bonzini return; 2537702e47cSPaolo Bonzini 2547702e47cSPaolo Bonzini case 0x10: /* SIR_IRQ_CODE */ 2557702e47cSPaolo Bonzini case 0x14: /* SIR_FIQ_CODE */ 2567702e47cSPaolo Bonzini OMAP_RO_REG(addr); 2577702e47cSPaolo Bonzini break; 2587702e47cSPaolo Bonzini 2597702e47cSPaolo Bonzini case 0x18: /* CONTROL_REG */ 2607702e47cSPaolo Bonzini if (bank_no != 0) 2617702e47cSPaolo Bonzini break; 2627702e47cSPaolo Bonzini if (value & 2) { 2637702e47cSPaolo Bonzini qemu_set_irq(s->parent_intr[1], 0); 2647702e47cSPaolo Bonzini s->new_agr[1] = ~0; 2657702e47cSPaolo Bonzini omap_inth_update(s, 1); 2667702e47cSPaolo Bonzini } 2677702e47cSPaolo Bonzini if (value & 1) { 2687702e47cSPaolo Bonzini qemu_set_irq(s->parent_intr[0], 0); 2697702e47cSPaolo Bonzini s->new_agr[0] = ~0; 2707702e47cSPaolo Bonzini omap_inth_update(s, 0); 2717702e47cSPaolo Bonzini } 2727702e47cSPaolo Bonzini return; 2737702e47cSPaolo Bonzini 2747702e47cSPaolo Bonzini case 0x1c: /* ILR0 */ 2757702e47cSPaolo Bonzini case 0x20: /* ILR1 */ 2767702e47cSPaolo Bonzini case 0x24: /* ILR2 */ 2777702e47cSPaolo Bonzini case 0x28: /* ILR3 */ 2787702e47cSPaolo Bonzini case 0x2c: /* ILR4 */ 2797702e47cSPaolo Bonzini case 0x30: /* ILR5 */ 2807702e47cSPaolo Bonzini case 0x34: /* ILR6 */ 2817702e47cSPaolo Bonzini case 0x38: /* ILR7 */ 2827702e47cSPaolo Bonzini case 0x3c: /* ILR8 */ 2837702e47cSPaolo Bonzini case 0x40: /* ILR9 */ 2847702e47cSPaolo Bonzini case 0x44: /* ILR10 */ 2857702e47cSPaolo Bonzini case 0x48: /* ILR11 */ 2867702e47cSPaolo Bonzini case 0x4c: /* ILR12 */ 2877702e47cSPaolo Bonzini case 0x50: /* ILR13 */ 2887702e47cSPaolo Bonzini case 0x54: /* ILR14 */ 2897702e47cSPaolo Bonzini case 0x58: /* ILR15 */ 2907702e47cSPaolo Bonzini case 0x5c: /* ILR16 */ 2917702e47cSPaolo Bonzini case 0x60: /* ILR17 */ 2927702e47cSPaolo Bonzini case 0x64: /* ILR18 */ 2937702e47cSPaolo Bonzini case 0x68: /* ILR19 */ 2947702e47cSPaolo Bonzini case 0x6c: /* ILR20 */ 2957702e47cSPaolo Bonzini case 0x70: /* ILR21 */ 2967702e47cSPaolo Bonzini case 0x74: /* ILR22 */ 2977702e47cSPaolo Bonzini case 0x78: /* ILR23 */ 2987702e47cSPaolo Bonzini case 0x7c: /* ILR24 */ 2997702e47cSPaolo Bonzini case 0x80: /* ILR25 */ 3007702e47cSPaolo Bonzini case 0x84: /* ILR26 */ 3017702e47cSPaolo Bonzini case 0x88: /* ILR27 */ 3027702e47cSPaolo Bonzini case 0x8c: /* ILR28 */ 3037702e47cSPaolo Bonzini case 0x90: /* ILR29 */ 3047702e47cSPaolo Bonzini case 0x94: /* ILR30 */ 3057702e47cSPaolo Bonzini case 0x98: /* ILR31 */ 3067702e47cSPaolo Bonzini i = (offset - 0x1c) >> 2; 3077702e47cSPaolo Bonzini bank->priority[i] = (value >> 2) & 0x1f; 3087702e47cSPaolo Bonzini bank->sens_edge &= ~(1 << i); 3097702e47cSPaolo Bonzini bank->sens_edge |= ((value >> 1) & 1) << i; 3107702e47cSPaolo Bonzini bank->fiq &= ~(1 << i); 3117702e47cSPaolo Bonzini bank->fiq |= (value & 1) << i; 3127702e47cSPaolo Bonzini return; 3137702e47cSPaolo Bonzini 3147702e47cSPaolo Bonzini case 0x9c: /* ISR */ 3157702e47cSPaolo Bonzini for (i = 0; i < 32; i ++) 3167702e47cSPaolo Bonzini if (value & (1 << i)) { 3177702e47cSPaolo Bonzini omap_set_intr(s, 32 * bank_no + i, 1); 3187702e47cSPaolo Bonzini return; 3197702e47cSPaolo Bonzini } 3207702e47cSPaolo Bonzini return; 3217702e47cSPaolo Bonzini } 3227702e47cSPaolo Bonzini OMAP_BAD_REG(addr); 3237702e47cSPaolo Bonzini } 3247702e47cSPaolo Bonzini 3257702e47cSPaolo Bonzini static const MemoryRegionOps omap_inth_mem_ops = { 3267702e47cSPaolo Bonzini .read = omap_inth_read, 3277702e47cSPaolo Bonzini .write = omap_inth_write, 3287702e47cSPaolo Bonzini .endianness = DEVICE_NATIVE_ENDIAN, 3297702e47cSPaolo Bonzini .valid = { 3307702e47cSPaolo Bonzini .min_access_size = 4, 3317702e47cSPaolo Bonzini .max_access_size = 4, 3327702e47cSPaolo Bonzini }, 3337702e47cSPaolo Bonzini }; 3347702e47cSPaolo Bonzini 3357702e47cSPaolo Bonzini static void omap_inth_reset(DeviceState *dev) 3367702e47cSPaolo Bonzini { 33747edc5a4SAndreas Färber struct omap_intr_handler_s *s = OMAP_INTC(dev); 3387702e47cSPaolo Bonzini int i; 3397702e47cSPaolo Bonzini 3407702e47cSPaolo Bonzini for (i = 0; i < s->nbanks; ++i){ 3417702e47cSPaolo Bonzini s->bank[i].irqs = 0x00000000; 3427702e47cSPaolo Bonzini s->bank[i].mask = 0xffffffff; 3437702e47cSPaolo Bonzini s->bank[i].sens_edge = 0x00000000; 3447702e47cSPaolo Bonzini s->bank[i].fiq = 0x00000000; 3457702e47cSPaolo Bonzini s->bank[i].inputs = 0x00000000; 3467702e47cSPaolo Bonzini s->bank[i].swi = 0x00000000; 3477702e47cSPaolo Bonzini memset(s->bank[i].priority, 0, sizeof(s->bank[i].priority)); 3487702e47cSPaolo Bonzini 3497702e47cSPaolo Bonzini if (s->level_only) 3507702e47cSPaolo Bonzini s->bank[i].sens_edge = 0xffffffff; 3517702e47cSPaolo Bonzini } 3527702e47cSPaolo Bonzini 3537702e47cSPaolo Bonzini s->new_agr[0] = ~0; 3547702e47cSPaolo Bonzini s->new_agr[1] = ~0; 3557702e47cSPaolo Bonzini s->sir_intr[0] = 0; 3567702e47cSPaolo Bonzini s->sir_intr[1] = 0; 3577702e47cSPaolo Bonzini s->autoidle = 0; 3587702e47cSPaolo Bonzini s->mask = ~0; 3597702e47cSPaolo Bonzini 3607702e47cSPaolo Bonzini qemu_set_irq(s->parent_intr[0], 0); 3617702e47cSPaolo Bonzini qemu_set_irq(s->parent_intr[1], 0); 3627702e47cSPaolo Bonzini } 3637702e47cSPaolo Bonzini 36447edc5a4SAndreas Färber static int omap_intc_init(SysBusDevice *sbd) 3657702e47cSPaolo Bonzini { 36647edc5a4SAndreas Färber DeviceState *dev = DEVICE(sbd); 36747edc5a4SAndreas Färber struct omap_intr_handler_s *s = OMAP_INTC(dev); 36847edc5a4SAndreas Färber 3697702e47cSPaolo Bonzini if (!s->iclk) { 3707702e47cSPaolo Bonzini hw_error("omap-intc: clk not connected\n"); 3717702e47cSPaolo Bonzini } 3727702e47cSPaolo Bonzini 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); 3761437c94bSPaolo Bonzini memory_region_init_io(&s->mmio, OBJECT(s), &omap_inth_mem_ops, s, 3777702e47cSPaolo Bonzini "omap-intc", s->size); 37847edc5a4SAndreas Färber sysbus_init_mmio(sbd, &s->mmio); 3797702e47cSPaolo Bonzini return 0; 3807702e47cSPaolo Bonzini } 3817702e47cSPaolo Bonzini 3827702e47cSPaolo Bonzini static Property omap_intc_properties[] = { 3837702e47cSPaolo Bonzini DEFINE_PROP_UINT32("size", struct omap_intr_handler_s, size, 0x100), 3847702e47cSPaolo Bonzini DEFINE_PROP_PTR("clk", struct omap_intr_handler_s, iclk), 3857702e47cSPaolo Bonzini DEFINE_PROP_END_OF_LIST(), 3867702e47cSPaolo Bonzini }; 3877702e47cSPaolo Bonzini 3887702e47cSPaolo Bonzini static void omap_intc_class_init(ObjectClass *klass, void *data) 3897702e47cSPaolo Bonzini { 3907702e47cSPaolo Bonzini DeviceClass *dc = DEVICE_CLASS(klass); 3917702e47cSPaolo Bonzini SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); 3927702e47cSPaolo Bonzini 3937702e47cSPaolo Bonzini k->init = omap_intc_init; 3947702e47cSPaolo Bonzini dc->reset = omap_inth_reset; 3957702e47cSPaolo Bonzini dc->props = omap_intc_properties; 3961b111dc1SMarkus Armbruster /* Reason: pointer property "clk" */ 3971b111dc1SMarkus Armbruster dc->cannot_instantiate_with_device_add_yet = true; 3987702e47cSPaolo Bonzini } 3997702e47cSPaolo Bonzini 4007702e47cSPaolo Bonzini static const TypeInfo omap_intc_info = { 4017702e47cSPaolo Bonzini .name = "omap-intc", 40247edc5a4SAndreas Färber .parent = TYPE_OMAP_INTC, 4037702e47cSPaolo Bonzini .class_init = omap_intc_class_init, 4047702e47cSPaolo Bonzini }; 4057702e47cSPaolo Bonzini 4067702e47cSPaolo Bonzini static uint64_t omap2_inth_read(void *opaque, hwaddr addr, 4077702e47cSPaolo Bonzini unsigned size) 4087702e47cSPaolo Bonzini { 4097702e47cSPaolo Bonzini struct omap_intr_handler_s *s = (struct omap_intr_handler_s *) opaque; 4107702e47cSPaolo Bonzini int offset = addr; 4117702e47cSPaolo Bonzini int bank_no, line_no; 4127702e47cSPaolo Bonzini struct omap_intr_handler_bank_s *bank = NULL; 4137702e47cSPaolo Bonzini 4147702e47cSPaolo Bonzini if ((offset & 0xf80) == 0x80) { 4157702e47cSPaolo Bonzini bank_no = (offset & 0x60) >> 5; 4167702e47cSPaolo Bonzini if (bank_no < s->nbanks) { 4177702e47cSPaolo Bonzini offset &= ~0x60; 4187702e47cSPaolo Bonzini bank = &s->bank[bank_no]; 4197702e47cSPaolo Bonzini } else { 4207702e47cSPaolo Bonzini OMAP_BAD_REG(addr); 4217702e47cSPaolo Bonzini return 0; 4227702e47cSPaolo Bonzini } 4237702e47cSPaolo Bonzini } 4247702e47cSPaolo Bonzini 4257702e47cSPaolo Bonzini switch (offset) { 4267702e47cSPaolo Bonzini case 0x00: /* INTC_REVISION */ 4277702e47cSPaolo Bonzini return s->revision; 4287702e47cSPaolo Bonzini 4297702e47cSPaolo Bonzini case 0x10: /* INTC_SYSCONFIG */ 4307702e47cSPaolo Bonzini return (s->autoidle >> 2) & 1; 4317702e47cSPaolo Bonzini 4327702e47cSPaolo Bonzini case 0x14: /* INTC_SYSSTATUS */ 4337702e47cSPaolo Bonzini return 1; /* RESETDONE */ 4347702e47cSPaolo Bonzini 4357702e47cSPaolo Bonzini case 0x40: /* INTC_SIR_IRQ */ 4367702e47cSPaolo Bonzini return s->sir_intr[0]; 4377702e47cSPaolo Bonzini 4387702e47cSPaolo Bonzini case 0x44: /* INTC_SIR_FIQ */ 4397702e47cSPaolo Bonzini return s->sir_intr[1]; 4407702e47cSPaolo Bonzini 4417702e47cSPaolo Bonzini case 0x48: /* INTC_CONTROL */ 4427702e47cSPaolo Bonzini return (!s->mask) << 2; /* GLOBALMASK */ 4437702e47cSPaolo Bonzini 4447702e47cSPaolo Bonzini case 0x4c: /* INTC_PROTECTION */ 4457702e47cSPaolo Bonzini return 0; 4467702e47cSPaolo Bonzini 4477702e47cSPaolo Bonzini case 0x50: /* INTC_IDLE */ 4487702e47cSPaolo Bonzini return s->autoidle & 3; 4497702e47cSPaolo Bonzini 4507702e47cSPaolo Bonzini /* Per-bank registers */ 4517702e47cSPaolo Bonzini case 0x80: /* INTC_ITR */ 4527702e47cSPaolo Bonzini return bank->inputs; 4537702e47cSPaolo Bonzini 4547702e47cSPaolo Bonzini case 0x84: /* INTC_MIR */ 4557702e47cSPaolo Bonzini return bank->mask; 4567702e47cSPaolo Bonzini 4577702e47cSPaolo Bonzini case 0x88: /* INTC_MIR_CLEAR */ 4587702e47cSPaolo Bonzini case 0x8c: /* INTC_MIR_SET */ 4597702e47cSPaolo Bonzini return 0; 4607702e47cSPaolo Bonzini 4617702e47cSPaolo Bonzini case 0x90: /* INTC_ISR_SET */ 4627702e47cSPaolo Bonzini return bank->swi; 4637702e47cSPaolo Bonzini 4647702e47cSPaolo Bonzini case 0x94: /* INTC_ISR_CLEAR */ 4657702e47cSPaolo Bonzini return 0; 4667702e47cSPaolo Bonzini 4677702e47cSPaolo Bonzini case 0x98: /* INTC_PENDING_IRQ */ 4687702e47cSPaolo Bonzini return bank->irqs & ~bank->mask & ~bank->fiq; 4697702e47cSPaolo Bonzini 4707702e47cSPaolo Bonzini case 0x9c: /* INTC_PENDING_FIQ */ 4717702e47cSPaolo Bonzini return bank->irqs & ~bank->mask & bank->fiq; 4727702e47cSPaolo Bonzini 4737702e47cSPaolo Bonzini /* Per-line registers */ 4747702e47cSPaolo Bonzini case 0x100 ... 0x300: /* INTC_ILR */ 4757702e47cSPaolo Bonzini bank_no = (offset - 0x100) >> 7; 4767702e47cSPaolo Bonzini if (bank_no > s->nbanks) 4777702e47cSPaolo Bonzini break; 4787702e47cSPaolo Bonzini bank = &s->bank[bank_no]; 4797702e47cSPaolo Bonzini line_no = (offset & 0x7f) >> 2; 4807702e47cSPaolo Bonzini return (bank->priority[line_no] << 2) | 4817702e47cSPaolo Bonzini ((bank->fiq >> line_no) & 1); 4827702e47cSPaolo Bonzini } 4837702e47cSPaolo Bonzini OMAP_BAD_REG(addr); 4847702e47cSPaolo Bonzini return 0; 4857702e47cSPaolo Bonzini } 4867702e47cSPaolo Bonzini 4877702e47cSPaolo Bonzini static void omap2_inth_write(void *opaque, hwaddr addr, 4887702e47cSPaolo Bonzini uint64_t value, unsigned size) 4897702e47cSPaolo Bonzini { 4907702e47cSPaolo Bonzini struct omap_intr_handler_s *s = (struct omap_intr_handler_s *) opaque; 4917702e47cSPaolo Bonzini int offset = addr; 4927702e47cSPaolo Bonzini int bank_no, line_no; 4937702e47cSPaolo Bonzini struct omap_intr_handler_bank_s *bank = NULL; 4947702e47cSPaolo Bonzini 4957702e47cSPaolo Bonzini if ((offset & 0xf80) == 0x80) { 4967702e47cSPaolo Bonzini bank_no = (offset & 0x60) >> 5; 4977702e47cSPaolo Bonzini if (bank_no < s->nbanks) { 4987702e47cSPaolo Bonzini offset &= ~0x60; 4997702e47cSPaolo Bonzini bank = &s->bank[bank_no]; 5007702e47cSPaolo Bonzini } else { 5017702e47cSPaolo Bonzini OMAP_BAD_REG(addr); 5027702e47cSPaolo Bonzini return; 5037702e47cSPaolo Bonzini } 5047702e47cSPaolo Bonzini } 5057702e47cSPaolo Bonzini 5067702e47cSPaolo Bonzini switch (offset) { 5077702e47cSPaolo Bonzini case 0x10: /* INTC_SYSCONFIG */ 5087702e47cSPaolo Bonzini s->autoidle &= 4; 5097702e47cSPaolo Bonzini s->autoidle |= (value & 1) << 2; 51047edc5a4SAndreas Färber if (value & 2) { /* SOFTRESET */ 51147edc5a4SAndreas Färber omap_inth_reset(DEVICE(s)); 51247edc5a4SAndreas Färber } 5137702e47cSPaolo Bonzini return; 5147702e47cSPaolo Bonzini 5157702e47cSPaolo Bonzini case 0x48: /* INTC_CONTROL */ 5167702e47cSPaolo Bonzini s->mask = (value & 4) ? 0 : ~0; /* GLOBALMASK */ 5177702e47cSPaolo Bonzini if (value & 2) { /* NEWFIQAGR */ 5187702e47cSPaolo Bonzini qemu_set_irq(s->parent_intr[1], 0); 5197702e47cSPaolo Bonzini s->new_agr[1] = ~0; 5207702e47cSPaolo Bonzini omap_inth_update(s, 1); 5217702e47cSPaolo Bonzini } 5227702e47cSPaolo Bonzini if (value & 1) { /* NEWIRQAGR */ 5237702e47cSPaolo Bonzini qemu_set_irq(s->parent_intr[0], 0); 5247702e47cSPaolo Bonzini s->new_agr[0] = ~0; 5257702e47cSPaolo Bonzini omap_inth_update(s, 0); 5267702e47cSPaolo Bonzini } 5277702e47cSPaolo Bonzini return; 5287702e47cSPaolo Bonzini 5297702e47cSPaolo Bonzini case 0x4c: /* INTC_PROTECTION */ 5307702e47cSPaolo Bonzini /* TODO: Make a bitmap (or sizeof(char)map) of access privileges 5317702e47cSPaolo Bonzini * for every register, see Chapter 3 and 4 for privileged mode. */ 5327702e47cSPaolo Bonzini if (value & 1) 5337702e47cSPaolo Bonzini fprintf(stderr, "%s: protection mode enable attempt\n", 5347702e47cSPaolo Bonzini __FUNCTION__); 5357702e47cSPaolo Bonzini return; 5367702e47cSPaolo Bonzini 5377702e47cSPaolo Bonzini case 0x50: /* INTC_IDLE */ 5387702e47cSPaolo Bonzini s->autoidle &= ~3; 5397702e47cSPaolo Bonzini s->autoidle |= value & 3; 5407702e47cSPaolo Bonzini return; 5417702e47cSPaolo Bonzini 5427702e47cSPaolo Bonzini /* Per-bank registers */ 5437702e47cSPaolo Bonzini case 0x84: /* INTC_MIR */ 5447702e47cSPaolo Bonzini bank->mask = value; 5457702e47cSPaolo Bonzini omap_inth_update(s, 0); 5467702e47cSPaolo Bonzini omap_inth_update(s, 1); 5477702e47cSPaolo Bonzini return; 5487702e47cSPaolo Bonzini 5497702e47cSPaolo Bonzini case 0x88: /* INTC_MIR_CLEAR */ 5507702e47cSPaolo Bonzini bank->mask &= ~value; 5517702e47cSPaolo Bonzini omap_inth_update(s, 0); 5527702e47cSPaolo Bonzini omap_inth_update(s, 1); 5537702e47cSPaolo Bonzini return; 5547702e47cSPaolo Bonzini 5557702e47cSPaolo Bonzini case 0x8c: /* INTC_MIR_SET */ 5567702e47cSPaolo Bonzini bank->mask |= value; 5577702e47cSPaolo Bonzini return; 5587702e47cSPaolo Bonzini 5597702e47cSPaolo Bonzini case 0x90: /* INTC_ISR_SET */ 5607702e47cSPaolo Bonzini bank->irqs |= bank->swi |= value; 5617702e47cSPaolo Bonzini omap_inth_update(s, 0); 5627702e47cSPaolo Bonzini omap_inth_update(s, 1); 5637702e47cSPaolo Bonzini return; 5647702e47cSPaolo Bonzini 5657702e47cSPaolo Bonzini case 0x94: /* INTC_ISR_CLEAR */ 5667702e47cSPaolo Bonzini bank->swi &= ~value; 5677702e47cSPaolo Bonzini bank->irqs = bank->swi & bank->inputs; 5687702e47cSPaolo Bonzini return; 5697702e47cSPaolo Bonzini 5707702e47cSPaolo Bonzini /* Per-line registers */ 5717702e47cSPaolo Bonzini case 0x100 ... 0x300: /* INTC_ILR */ 5727702e47cSPaolo Bonzini bank_no = (offset - 0x100) >> 7; 5737702e47cSPaolo Bonzini if (bank_no > s->nbanks) 5747702e47cSPaolo Bonzini break; 5757702e47cSPaolo Bonzini bank = &s->bank[bank_no]; 5767702e47cSPaolo Bonzini line_no = (offset & 0x7f) >> 2; 5777702e47cSPaolo Bonzini bank->priority[line_no] = (value >> 2) & 0x3f; 5787702e47cSPaolo Bonzini bank->fiq &= ~(1 << line_no); 5797702e47cSPaolo Bonzini bank->fiq |= (value & 1) << line_no; 5807702e47cSPaolo Bonzini return; 5817702e47cSPaolo Bonzini 5827702e47cSPaolo Bonzini case 0x00: /* INTC_REVISION */ 5837702e47cSPaolo Bonzini case 0x14: /* INTC_SYSSTATUS */ 5847702e47cSPaolo Bonzini case 0x40: /* INTC_SIR_IRQ */ 5857702e47cSPaolo Bonzini case 0x44: /* INTC_SIR_FIQ */ 5867702e47cSPaolo Bonzini case 0x80: /* INTC_ITR */ 5877702e47cSPaolo Bonzini case 0x98: /* INTC_PENDING_IRQ */ 5887702e47cSPaolo Bonzini case 0x9c: /* INTC_PENDING_FIQ */ 5897702e47cSPaolo Bonzini OMAP_RO_REG(addr); 5907702e47cSPaolo Bonzini return; 5917702e47cSPaolo Bonzini } 5927702e47cSPaolo Bonzini OMAP_BAD_REG(addr); 5937702e47cSPaolo Bonzini } 5947702e47cSPaolo Bonzini 5957702e47cSPaolo Bonzini static const MemoryRegionOps omap2_inth_mem_ops = { 5967702e47cSPaolo Bonzini .read = omap2_inth_read, 5977702e47cSPaolo Bonzini .write = omap2_inth_write, 5987702e47cSPaolo Bonzini .endianness = DEVICE_NATIVE_ENDIAN, 5997702e47cSPaolo Bonzini .valid = { 6007702e47cSPaolo Bonzini .min_access_size = 4, 6017702e47cSPaolo Bonzini .max_access_size = 4, 6027702e47cSPaolo Bonzini }, 6037702e47cSPaolo Bonzini }; 6047702e47cSPaolo Bonzini 60547edc5a4SAndreas Färber static int omap2_intc_init(SysBusDevice *sbd) 6067702e47cSPaolo Bonzini { 60747edc5a4SAndreas Färber DeviceState *dev = DEVICE(sbd); 60847edc5a4SAndreas Färber struct omap_intr_handler_s *s = OMAP_INTC(dev); 60947edc5a4SAndreas Färber 6107702e47cSPaolo Bonzini if (!s->iclk) { 6117702e47cSPaolo Bonzini hw_error("omap2-intc: iclk not connected\n"); 6127702e47cSPaolo Bonzini } 6137702e47cSPaolo Bonzini if (!s->fclk) { 6147702e47cSPaolo Bonzini hw_error("omap2-intc: fclk not connected\n"); 6157702e47cSPaolo Bonzini } 6167702e47cSPaolo Bonzini s->level_only = 1; 6177702e47cSPaolo Bonzini s->nbanks = 3; 61847edc5a4SAndreas Färber sysbus_init_irq(sbd, &s->parent_intr[0]); 61947edc5a4SAndreas Färber sysbus_init_irq(sbd, &s->parent_intr[1]); 62047edc5a4SAndreas Färber qdev_init_gpio_in(dev, omap_set_intr_noedge, s->nbanks * 32); 6211437c94bSPaolo Bonzini memory_region_init_io(&s->mmio, OBJECT(s), &omap2_inth_mem_ops, s, 6227702e47cSPaolo Bonzini "omap2-intc", 0x1000); 62347edc5a4SAndreas Färber sysbus_init_mmio(sbd, &s->mmio); 6247702e47cSPaolo Bonzini return 0; 6257702e47cSPaolo Bonzini } 6267702e47cSPaolo Bonzini 6277702e47cSPaolo Bonzini static Property omap2_intc_properties[] = { 6287702e47cSPaolo Bonzini DEFINE_PROP_UINT8("revision", struct omap_intr_handler_s, 6297702e47cSPaolo Bonzini revision, 0x21), 6307702e47cSPaolo Bonzini DEFINE_PROP_PTR("iclk", struct omap_intr_handler_s, iclk), 6317702e47cSPaolo Bonzini DEFINE_PROP_PTR("fclk", struct omap_intr_handler_s, fclk), 6327702e47cSPaolo Bonzini DEFINE_PROP_END_OF_LIST(), 6337702e47cSPaolo Bonzini }; 6347702e47cSPaolo Bonzini 6357702e47cSPaolo Bonzini static void omap2_intc_class_init(ObjectClass *klass, void *data) 6367702e47cSPaolo Bonzini { 6377702e47cSPaolo Bonzini DeviceClass *dc = DEVICE_CLASS(klass); 6387702e47cSPaolo Bonzini SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); 6397702e47cSPaolo Bonzini 6407702e47cSPaolo Bonzini k->init = omap2_intc_init; 6417702e47cSPaolo Bonzini dc->reset = omap_inth_reset; 6427702e47cSPaolo Bonzini dc->props = omap2_intc_properties; 6431b111dc1SMarkus Armbruster /* Reason: pointer property "iclk", "fclk" */ 6441b111dc1SMarkus Armbruster dc->cannot_instantiate_with_device_add_yet = true; 6457702e47cSPaolo Bonzini } 6467702e47cSPaolo Bonzini 6477702e47cSPaolo Bonzini static const TypeInfo omap2_intc_info = { 6487702e47cSPaolo Bonzini .name = "omap2-intc", 64947edc5a4SAndreas Färber .parent = TYPE_OMAP_INTC, 65047edc5a4SAndreas Färber .class_init = omap2_intc_class_init, 65147edc5a4SAndreas Färber }; 65247edc5a4SAndreas Färber 65347edc5a4SAndreas Färber static const TypeInfo omap_intc_type_info = { 65447edc5a4SAndreas Färber .name = TYPE_OMAP_INTC, 6557702e47cSPaolo Bonzini .parent = TYPE_SYS_BUS_DEVICE, 6567702e47cSPaolo Bonzini .instance_size = sizeof(struct omap_intr_handler_s), 65747edc5a4SAndreas Färber .abstract = true, 6587702e47cSPaolo Bonzini }; 6597702e47cSPaolo Bonzini 6607702e47cSPaolo Bonzini static void omap_intc_register_types(void) 6617702e47cSPaolo Bonzini { 66247edc5a4SAndreas Färber type_register_static(&omap_intc_type_info); 6637702e47cSPaolo Bonzini type_register_static(&omap_intc_info); 6647702e47cSPaolo Bonzini type_register_static(&omap2_intc_info); 6657702e47cSPaolo Bonzini } 6667702e47cSPaolo Bonzini 6677702e47cSPaolo Bonzini type_init(omap_intc_register_types) 668