10c69996eSAndrew Jeffery /* 20c69996eSAndrew Jeffery * ASPEED Interrupt Controller (New) 30c69996eSAndrew Jeffery * 40c69996eSAndrew Jeffery * Andrew Jeffery <andrew@aj.id.au> 50c69996eSAndrew Jeffery * 60c69996eSAndrew Jeffery * Copyright 2015, 2016 IBM Corp. 70c69996eSAndrew Jeffery * 80c69996eSAndrew Jeffery * This code is licensed under the GPL version 2 or later. See 90c69996eSAndrew Jeffery * the COPYING file in the top-level directory. 100c69996eSAndrew Jeffery */ 110c69996eSAndrew Jeffery 120c69996eSAndrew Jeffery /* The hardware exposes two register sets, a legacy set and a 'new' set. The 130c69996eSAndrew Jeffery * model implements the 'new' register set, and logs warnings on accesses to 140c69996eSAndrew Jeffery * the legacy IO space. 150c69996eSAndrew Jeffery * 160c69996eSAndrew Jeffery * The hardware uses 32bit registers to manage 51 IRQs, with low and high 170c69996eSAndrew Jeffery * registers for each conceptual register. The device model's implementation 180c69996eSAndrew Jeffery * uses 64bit data types to store both low and high register values (in the one 190c69996eSAndrew Jeffery * member), but must cope with access offset values in multiples of 4 passed to 200c69996eSAndrew Jeffery * the callbacks. As such the read() and write() implementations process the 210c69996eSAndrew Jeffery * provided offset to understand whether the access is requesting the lower or 220c69996eSAndrew Jeffery * upper 32 bits of the 64bit member. 230c69996eSAndrew Jeffery * 240c69996eSAndrew Jeffery * Additionally, the "Interrupt Enable", "Edge Status" and "Software Interrupt" 250c69996eSAndrew Jeffery * fields have separate "enable"/"status" and "clear" registers, where set bits 260c69996eSAndrew Jeffery * are written to one or the other to change state (avoiding a 270c69996eSAndrew Jeffery * read-modify-write sequence). 280c69996eSAndrew Jeffery */ 290c69996eSAndrew Jeffery 300c69996eSAndrew Jeffery #include "qemu/osdep.h" 310c69996eSAndrew Jeffery #include <inttypes.h> 320c69996eSAndrew Jeffery #include "hw/intc/aspeed_vic.h" 330c69996eSAndrew Jeffery #include "qemu/bitops.h" 34*22b31af2SPaolo Bonzini #include "qemu/log.h" 350c69996eSAndrew Jeffery #include "trace.h" 360c69996eSAndrew Jeffery 370c69996eSAndrew Jeffery #define AVIC_NEW_BASE_OFFSET 0x80 380c69996eSAndrew Jeffery 390c69996eSAndrew Jeffery #define AVIC_L_MASK 0xFFFFFFFFU 400c69996eSAndrew Jeffery #define AVIC_H_MASK 0x0007FFFFU 410c69996eSAndrew Jeffery #define AVIC_EVENT_W_MASK (0x78000ULL << 32) 420c69996eSAndrew Jeffery 430c69996eSAndrew Jeffery static void aspeed_vic_update(AspeedVICState *s) 440c69996eSAndrew Jeffery { 450c69996eSAndrew Jeffery uint64_t new = (s->raw & s->enable); 460c69996eSAndrew Jeffery uint64_t flags; 470c69996eSAndrew Jeffery 480c69996eSAndrew Jeffery flags = new & s->select; 490c69996eSAndrew Jeffery trace_aspeed_vic_update_fiq(!!flags); 500c69996eSAndrew Jeffery qemu_set_irq(s->fiq, !!flags); 510c69996eSAndrew Jeffery 520c69996eSAndrew Jeffery flags = new & ~s->select; 530c69996eSAndrew Jeffery trace_aspeed_vic_update_irq(!!flags); 540c69996eSAndrew Jeffery qemu_set_irq(s->irq, !!flags); 550c69996eSAndrew Jeffery } 560c69996eSAndrew Jeffery 570c69996eSAndrew Jeffery static void aspeed_vic_set_irq(void *opaque, int irq, int level) 580c69996eSAndrew Jeffery { 590c69996eSAndrew Jeffery uint64_t irq_mask; 600c69996eSAndrew Jeffery bool raise; 610c69996eSAndrew Jeffery AspeedVICState *s = (AspeedVICState *)opaque; 620c69996eSAndrew Jeffery 630c69996eSAndrew Jeffery if (irq > ASPEED_VIC_NR_IRQS) { 640c69996eSAndrew Jeffery qemu_log_mask(LOG_GUEST_ERROR, "%s: Invalid interrupt number: %d\n", 650c69996eSAndrew Jeffery __func__, irq); 660c69996eSAndrew Jeffery return; 670c69996eSAndrew Jeffery } 680c69996eSAndrew Jeffery 690c69996eSAndrew Jeffery trace_aspeed_vic_set_irq(irq, level); 700c69996eSAndrew Jeffery 710c69996eSAndrew Jeffery irq_mask = BIT(irq); 720c69996eSAndrew Jeffery if (s->sense & irq_mask) { 730c69996eSAndrew Jeffery /* level-triggered */ 740c69996eSAndrew Jeffery if (s->event & irq_mask) { 750c69996eSAndrew Jeffery /* high-sensitive */ 760c69996eSAndrew Jeffery raise = level; 770c69996eSAndrew Jeffery } else { 780c69996eSAndrew Jeffery /* low-sensitive */ 790c69996eSAndrew Jeffery raise = !level; 800c69996eSAndrew Jeffery } 810c69996eSAndrew Jeffery s->raw = deposit64(s->raw, irq, 1, raise); 820c69996eSAndrew Jeffery } else { 830c69996eSAndrew Jeffery uint64_t old_level = s->level & irq_mask; 840c69996eSAndrew Jeffery 850c69996eSAndrew Jeffery /* edge-triggered */ 860c69996eSAndrew Jeffery if (s->dual_edge & irq_mask) { 870c69996eSAndrew Jeffery raise = (!!old_level) != (!!level); 880c69996eSAndrew Jeffery } else { 890c69996eSAndrew Jeffery if (s->event & irq_mask) { 900c69996eSAndrew Jeffery /* rising-sensitive */ 910c69996eSAndrew Jeffery raise = !old_level && level; 920c69996eSAndrew Jeffery } else { 930c69996eSAndrew Jeffery /* falling-sensitive */ 940c69996eSAndrew Jeffery raise = old_level && !level; 950c69996eSAndrew Jeffery } 960c69996eSAndrew Jeffery } 970c69996eSAndrew Jeffery if (raise) { 980c69996eSAndrew Jeffery s->raw = deposit64(s->raw, irq, 1, raise); 990c69996eSAndrew Jeffery } 1000c69996eSAndrew Jeffery } 1010c69996eSAndrew Jeffery s->level = deposit64(s->level, irq, 1, level); 1020c69996eSAndrew Jeffery aspeed_vic_update(s); 1030c69996eSAndrew Jeffery } 1040c69996eSAndrew Jeffery 1050c69996eSAndrew Jeffery static uint64_t aspeed_vic_read(void *opaque, hwaddr offset, unsigned size) 1060c69996eSAndrew Jeffery { 1070c69996eSAndrew Jeffery uint64_t val; 1080c69996eSAndrew Jeffery const bool high = !!(offset & 0x4); 1090c69996eSAndrew Jeffery hwaddr n_offset = (offset & ~0x4); 1100c69996eSAndrew Jeffery AspeedVICState *s = (AspeedVICState *)opaque; 1110c69996eSAndrew Jeffery 1120c69996eSAndrew Jeffery if (offset < AVIC_NEW_BASE_OFFSET) { 1130c69996eSAndrew Jeffery qemu_log_mask(LOG_UNIMP, "%s: Ignoring read from legacy registers " 1140c69996eSAndrew Jeffery "at 0x%" HWADDR_PRIx "[%u]\n", __func__, offset, size); 1150c69996eSAndrew Jeffery return 0; 1160c69996eSAndrew Jeffery } 1170c69996eSAndrew Jeffery 1180c69996eSAndrew Jeffery n_offset -= AVIC_NEW_BASE_OFFSET; 1190c69996eSAndrew Jeffery 1200c69996eSAndrew Jeffery switch (n_offset) { 1210c69996eSAndrew Jeffery case 0x0: /* IRQ Status */ 1220c69996eSAndrew Jeffery val = s->raw & ~s->select & s->enable; 1230c69996eSAndrew Jeffery break; 1240c69996eSAndrew Jeffery case 0x08: /* FIQ Status */ 1250c69996eSAndrew Jeffery val = s->raw & s->select & s->enable; 1260c69996eSAndrew Jeffery break; 1270c69996eSAndrew Jeffery case 0x10: /* Raw Interrupt Status */ 1280c69996eSAndrew Jeffery val = s->raw; 1290c69996eSAndrew Jeffery break; 1300c69996eSAndrew Jeffery case 0x18: /* Interrupt Selection */ 1310c69996eSAndrew Jeffery val = s->select; 1320c69996eSAndrew Jeffery break; 1330c69996eSAndrew Jeffery case 0x20: /* Interrupt Enable */ 1340c69996eSAndrew Jeffery val = s->enable; 1350c69996eSAndrew Jeffery break; 1360c69996eSAndrew Jeffery case 0x30: /* Software Interrupt */ 1370c69996eSAndrew Jeffery val = s->trigger; 1380c69996eSAndrew Jeffery break; 1390c69996eSAndrew Jeffery case 0x40: /* Interrupt Sensitivity */ 1400c69996eSAndrew Jeffery val = s->sense; 1410c69996eSAndrew Jeffery break; 1420c69996eSAndrew Jeffery case 0x48: /* Interrupt Both Edge Trigger Control */ 1430c69996eSAndrew Jeffery val = s->dual_edge; 1440c69996eSAndrew Jeffery break; 1450c69996eSAndrew Jeffery case 0x50: /* Interrupt Event */ 1460c69996eSAndrew Jeffery val = s->event; 1470c69996eSAndrew Jeffery break; 1480c69996eSAndrew Jeffery case 0x60: /* Edge Triggered Interrupt Status */ 1490c69996eSAndrew Jeffery val = s->raw & ~s->sense; 1500c69996eSAndrew Jeffery break; 1510c69996eSAndrew Jeffery /* Illegal */ 1520c69996eSAndrew Jeffery case 0x28: /* Interrupt Enable Clear */ 1530c69996eSAndrew Jeffery case 0x38: /* Software Interrupt Clear */ 1540c69996eSAndrew Jeffery case 0x58: /* Edge Triggered Interrupt Clear */ 1550c69996eSAndrew Jeffery qemu_log_mask(LOG_GUEST_ERROR, 1560c69996eSAndrew Jeffery "%s: Read of write-only register with offset 0x%" 1570c69996eSAndrew Jeffery HWADDR_PRIx "\n", __func__, offset); 1580c69996eSAndrew Jeffery val = 0; 1590c69996eSAndrew Jeffery break; 1600c69996eSAndrew Jeffery default: 1610c69996eSAndrew Jeffery qemu_log_mask(LOG_GUEST_ERROR, 1620c69996eSAndrew Jeffery "%s: Bad register at offset 0x%" HWADDR_PRIx "\n", 1630c69996eSAndrew Jeffery __func__, offset); 1640c69996eSAndrew Jeffery val = 0; 1650c69996eSAndrew Jeffery break; 1660c69996eSAndrew Jeffery } 1670c69996eSAndrew Jeffery if (high) { 1680c69996eSAndrew Jeffery val = extract64(val, 32, 19); 1690c69996eSAndrew Jeffery } 1700c69996eSAndrew Jeffery trace_aspeed_vic_read(offset, size, val); 1710c69996eSAndrew Jeffery return val; 1720c69996eSAndrew Jeffery } 1730c69996eSAndrew Jeffery 1740c69996eSAndrew Jeffery static void aspeed_vic_write(void *opaque, hwaddr offset, uint64_t data, 1750c69996eSAndrew Jeffery unsigned size) 1760c69996eSAndrew Jeffery { 1770c69996eSAndrew Jeffery const bool high = !!(offset & 0x4); 1780c69996eSAndrew Jeffery hwaddr n_offset = (offset & ~0x4); 1790c69996eSAndrew Jeffery AspeedVICState *s = (AspeedVICState *)opaque; 1800c69996eSAndrew Jeffery 1810c69996eSAndrew Jeffery if (offset < AVIC_NEW_BASE_OFFSET) { 1820c69996eSAndrew Jeffery qemu_log_mask(LOG_UNIMP, 1830c69996eSAndrew Jeffery "%s: Ignoring write to legacy registers at 0x%" 1840c69996eSAndrew Jeffery HWADDR_PRIx "[%u] <- 0x%" PRIx64 "\n", __func__, offset, 1850c69996eSAndrew Jeffery size, data); 1860c69996eSAndrew Jeffery return; 1870c69996eSAndrew Jeffery } 1880c69996eSAndrew Jeffery 1890c69996eSAndrew Jeffery n_offset -= AVIC_NEW_BASE_OFFSET; 1900c69996eSAndrew Jeffery trace_aspeed_vic_write(offset, size, data); 1910c69996eSAndrew Jeffery 1920c69996eSAndrew Jeffery /* Given we have members using separate enable/clear registers, deposit64() 1930c69996eSAndrew Jeffery * isn't quite the tool for the job. Instead, relocate the incoming bits to 1940c69996eSAndrew Jeffery * the required bit offset based on the provided access address 1950c69996eSAndrew Jeffery */ 1960c69996eSAndrew Jeffery if (high) { 1970c69996eSAndrew Jeffery data &= AVIC_H_MASK; 1980c69996eSAndrew Jeffery data <<= 32; 1990c69996eSAndrew Jeffery } else { 2000c69996eSAndrew Jeffery data &= AVIC_L_MASK; 2010c69996eSAndrew Jeffery } 2020c69996eSAndrew Jeffery 2030c69996eSAndrew Jeffery switch (n_offset) { 2040c69996eSAndrew Jeffery case 0x18: /* Interrupt Selection */ 2050c69996eSAndrew Jeffery /* Register has deposit64() semantics - overwrite requested 32 bits */ 2060c69996eSAndrew Jeffery if (high) { 2070c69996eSAndrew Jeffery s->select &= AVIC_L_MASK; 2080c69996eSAndrew Jeffery } else { 2090c69996eSAndrew Jeffery s->select &= ((uint64_t) AVIC_H_MASK) << 32; 2100c69996eSAndrew Jeffery } 2110c69996eSAndrew Jeffery s->select |= data; 2120c69996eSAndrew Jeffery break; 2130c69996eSAndrew Jeffery case 0x20: /* Interrupt Enable */ 2140c69996eSAndrew Jeffery s->enable |= data; 2150c69996eSAndrew Jeffery break; 2160c69996eSAndrew Jeffery case 0x28: /* Interrupt Enable Clear */ 2170c69996eSAndrew Jeffery s->enable &= ~data; 2180c69996eSAndrew Jeffery break; 2190c69996eSAndrew Jeffery case 0x30: /* Software Interrupt */ 2200c69996eSAndrew Jeffery qemu_log_mask(LOG_UNIMP, "%s: Software interrupts unavailable. " 2210c69996eSAndrew Jeffery "IRQs requested: 0x%016" PRIx64 "\n", __func__, data); 2220c69996eSAndrew Jeffery break; 2230c69996eSAndrew Jeffery case 0x38: /* Software Interrupt Clear */ 2240c69996eSAndrew Jeffery qemu_log_mask(LOG_UNIMP, "%s: Software interrupts unavailable. " 2250c69996eSAndrew Jeffery "IRQs to be cleared: 0x%016" PRIx64 "\n", __func__, data); 2260c69996eSAndrew Jeffery break; 2270c69996eSAndrew Jeffery case 0x50: /* Interrupt Event */ 2280c69996eSAndrew Jeffery /* Register has deposit64() semantics - overwrite the top four valid 2290c69996eSAndrew Jeffery * IRQ bits, as only the top four IRQs (GPIOs) can change their event 2300c69996eSAndrew Jeffery * type */ 2310c69996eSAndrew Jeffery if (high) { 2320c69996eSAndrew Jeffery s->event &= ~AVIC_EVENT_W_MASK; 2330c69996eSAndrew Jeffery s->event |= (data & AVIC_EVENT_W_MASK); 2340c69996eSAndrew Jeffery } else { 2350c69996eSAndrew Jeffery qemu_log_mask(LOG_GUEST_ERROR, 2360c69996eSAndrew Jeffery "Ignoring invalid write to interrupt event register"); 2370c69996eSAndrew Jeffery } 2380c69996eSAndrew Jeffery break; 2390c69996eSAndrew Jeffery case 0x58: /* Edge Triggered Interrupt Clear */ 2400c69996eSAndrew Jeffery s->raw &= ~(data & ~s->sense); 2410c69996eSAndrew Jeffery break; 2420c69996eSAndrew Jeffery case 0x00: /* IRQ Status */ 2430c69996eSAndrew Jeffery case 0x08: /* FIQ Status */ 2440c69996eSAndrew Jeffery case 0x10: /* Raw Interrupt Status */ 2450c69996eSAndrew Jeffery case 0x40: /* Interrupt Sensitivity */ 2460c69996eSAndrew Jeffery case 0x48: /* Interrupt Both Edge Trigger Control */ 2470c69996eSAndrew Jeffery case 0x60: /* Edge Triggered Interrupt Status */ 2480c69996eSAndrew Jeffery qemu_log_mask(LOG_GUEST_ERROR, 2490c69996eSAndrew Jeffery "%s: Write of read-only register with offset 0x%" 2500c69996eSAndrew Jeffery HWADDR_PRIx "\n", __func__, offset); 2510c69996eSAndrew Jeffery break; 2520c69996eSAndrew Jeffery 2530c69996eSAndrew Jeffery default: 2540c69996eSAndrew Jeffery qemu_log_mask(LOG_GUEST_ERROR, 2550c69996eSAndrew Jeffery "%s: Bad register at offset 0x%" HWADDR_PRIx "\n", 2560c69996eSAndrew Jeffery __func__, offset); 2570c69996eSAndrew Jeffery break; 2580c69996eSAndrew Jeffery } 2590c69996eSAndrew Jeffery aspeed_vic_update(s); 2600c69996eSAndrew Jeffery } 2610c69996eSAndrew Jeffery 2620c69996eSAndrew Jeffery static const MemoryRegionOps aspeed_vic_ops = { 2630c69996eSAndrew Jeffery .read = aspeed_vic_read, 2640c69996eSAndrew Jeffery .write = aspeed_vic_write, 2650c69996eSAndrew Jeffery .endianness = DEVICE_LITTLE_ENDIAN, 2660c69996eSAndrew Jeffery .valid.min_access_size = 4, 2670c69996eSAndrew Jeffery .valid.max_access_size = 4, 2680c69996eSAndrew Jeffery .valid.unaligned = false, 2690c69996eSAndrew Jeffery }; 2700c69996eSAndrew Jeffery 2710c69996eSAndrew Jeffery static void aspeed_vic_reset(DeviceState *dev) 2720c69996eSAndrew Jeffery { 2730c69996eSAndrew Jeffery AspeedVICState *s = ASPEED_VIC(dev); 2740c69996eSAndrew Jeffery 2750c69996eSAndrew Jeffery s->level = 0; 2760c69996eSAndrew Jeffery s->raw = 0; 2770c69996eSAndrew Jeffery s->select = 0; 2780c69996eSAndrew Jeffery s->enable = 0; 2790c69996eSAndrew Jeffery s->trigger = 0; 2800c69996eSAndrew Jeffery s->sense = 0x1F07FFF8FFFFULL; 2810c69996eSAndrew Jeffery s->dual_edge = 0xF800070000ULL; 2820c69996eSAndrew Jeffery s->event = 0x5F07FFF8FFFFULL; 2830c69996eSAndrew Jeffery } 2840c69996eSAndrew Jeffery 2850c69996eSAndrew Jeffery #define AVIC_IO_REGION_SIZE 0x20000 2860c69996eSAndrew Jeffery 2870c69996eSAndrew Jeffery static void aspeed_vic_realize(DeviceState *dev, Error **errp) 2880c69996eSAndrew Jeffery { 2890c69996eSAndrew Jeffery SysBusDevice *sbd = SYS_BUS_DEVICE(dev); 2900c69996eSAndrew Jeffery AspeedVICState *s = ASPEED_VIC(dev); 2910c69996eSAndrew Jeffery 2920c69996eSAndrew Jeffery memory_region_init_io(&s->iomem, OBJECT(s), &aspeed_vic_ops, s, 2930c69996eSAndrew Jeffery TYPE_ASPEED_VIC, AVIC_IO_REGION_SIZE); 2940c69996eSAndrew Jeffery 2950c69996eSAndrew Jeffery sysbus_init_mmio(sbd, &s->iomem); 2960c69996eSAndrew Jeffery 2970c69996eSAndrew Jeffery qdev_init_gpio_in(dev, aspeed_vic_set_irq, ASPEED_VIC_NR_IRQS); 2980c69996eSAndrew Jeffery sysbus_init_irq(sbd, &s->irq); 2990c69996eSAndrew Jeffery sysbus_init_irq(sbd, &s->fiq); 3000c69996eSAndrew Jeffery } 3010c69996eSAndrew Jeffery 3020c69996eSAndrew Jeffery static const VMStateDescription vmstate_aspeed_vic = { 3030c69996eSAndrew Jeffery .name = "aspeed.new-vic", 3040c69996eSAndrew Jeffery .version_id = 1, 3050c69996eSAndrew Jeffery .minimum_version_id = 1, 3060c69996eSAndrew Jeffery .fields = (VMStateField[]) { 3070c69996eSAndrew Jeffery VMSTATE_UINT64(level, AspeedVICState), 3080c69996eSAndrew Jeffery VMSTATE_UINT64(raw, AspeedVICState), 3090c69996eSAndrew Jeffery VMSTATE_UINT64(select, AspeedVICState), 3100c69996eSAndrew Jeffery VMSTATE_UINT64(enable, AspeedVICState), 3110c69996eSAndrew Jeffery VMSTATE_UINT64(trigger, AspeedVICState), 3120c69996eSAndrew Jeffery VMSTATE_UINT64(sense, AspeedVICState), 3130c69996eSAndrew Jeffery VMSTATE_UINT64(dual_edge, AspeedVICState), 3140c69996eSAndrew Jeffery VMSTATE_UINT64(event, AspeedVICState), 3150c69996eSAndrew Jeffery VMSTATE_END_OF_LIST() 3160c69996eSAndrew Jeffery } 3170c69996eSAndrew Jeffery }; 3180c69996eSAndrew Jeffery 3190c69996eSAndrew Jeffery static void aspeed_vic_class_init(ObjectClass *klass, void *data) 3200c69996eSAndrew Jeffery { 3210c69996eSAndrew Jeffery DeviceClass *dc = DEVICE_CLASS(klass); 3220c69996eSAndrew Jeffery dc->realize = aspeed_vic_realize; 3230c69996eSAndrew Jeffery dc->reset = aspeed_vic_reset; 3240c69996eSAndrew Jeffery dc->desc = "ASPEED Interrupt Controller (New)"; 3250c69996eSAndrew Jeffery dc->vmsd = &vmstate_aspeed_vic; 3260c69996eSAndrew Jeffery } 3270c69996eSAndrew Jeffery 3280c69996eSAndrew Jeffery static const TypeInfo aspeed_vic_info = { 3290c69996eSAndrew Jeffery .name = TYPE_ASPEED_VIC, 3300c69996eSAndrew Jeffery .parent = TYPE_SYS_BUS_DEVICE, 3310c69996eSAndrew Jeffery .instance_size = sizeof(AspeedVICState), 3320c69996eSAndrew Jeffery .class_init = aspeed_vic_class_init, 3330c69996eSAndrew Jeffery }; 3340c69996eSAndrew Jeffery 3350c69996eSAndrew Jeffery static void aspeed_vic_register_types(void) 3360c69996eSAndrew Jeffery { 3370c69996eSAndrew Jeffery type_register_static(&aspeed_vic_info); 3380c69996eSAndrew Jeffery } 3390c69996eSAndrew Jeffery 3400c69996eSAndrew Jeffery type_init(aspeed_vic_register_types); 341