1cdbdb648Spbrook /* 2cdbdb648Spbrook * Arm PrimeCell PL190 Vector Interrupt Controller 3cdbdb648Spbrook * 4cdbdb648Spbrook * Copyright (c) 2006 CodeSourcery. 5cdbdb648Spbrook * Written by Paul Brook 6cdbdb648Spbrook * 78e31bf38SMatthew Fernandez * This code is licensed under the GPL. 8cdbdb648Spbrook */ 9cdbdb648Spbrook 1083c9f4caSPaolo Bonzini #include "hw/sysbus.h" 11cdbdb648Spbrook 12cdbdb648Spbrook /* The number of virtual priority levels. 16 user vectors plus the 13cdbdb648Spbrook unvectored IRQ. Chained interrupts would require an additional level 14cdbdb648Spbrook if implemented. */ 15cdbdb648Spbrook 16cdbdb648Spbrook #define PL190_NUM_PRIO 17 17cdbdb648Spbrook 18*aefbc256SAndreas Färber typedef struct PL190State { 1997aff481SPaul Brook SysBusDevice busdev; 207f8293bfSAvi Kivity MemoryRegion iomem; 21cdbdb648Spbrook uint32_t level; 22cdbdb648Spbrook uint32_t soft_level; 23cdbdb648Spbrook uint32_t irq_enable; 24cdbdb648Spbrook uint32_t fiq_select; 25cdbdb648Spbrook uint8_t vect_control[16]; 26cdbdb648Spbrook uint32_t vect_addr[PL190_NUM_PRIO]; 27cdbdb648Spbrook /* Mask containing interrupts with higher priority than this one. */ 28cdbdb648Spbrook uint32_t prio_mask[PL190_NUM_PRIO + 1]; 29cdbdb648Spbrook int protected; 30cdbdb648Spbrook /* Current priority level. */ 31cdbdb648Spbrook int priority; 32cdbdb648Spbrook int prev_prio[PL190_NUM_PRIO]; 33d537cf6cSpbrook qemu_irq irq; 34d537cf6cSpbrook qemu_irq fiq; 35*aefbc256SAndreas Färber } PL190State; 36cdbdb648Spbrook 37cdbdb648Spbrook static const unsigned char pl190_id[] = 38cdbdb648Spbrook { 0x90, 0x11, 0x04, 0x00, 0x0D, 0xf0, 0x05, 0xb1 }; 39cdbdb648Spbrook 40*aefbc256SAndreas Färber static inline uint32_t pl190_irq_level(PL190State *s) 41cdbdb648Spbrook { 42cdbdb648Spbrook return (s->level | s->soft_level) & s->irq_enable & ~s->fiq_select; 43cdbdb648Spbrook } 44cdbdb648Spbrook 45cdbdb648Spbrook /* Update interrupts. */ 46*aefbc256SAndreas Färber static void pl190_update(PL190State *s) 47cdbdb648Spbrook { 48cdbdb648Spbrook uint32_t level = pl190_irq_level(s); 49cdbdb648Spbrook int set; 50cdbdb648Spbrook 51cdbdb648Spbrook set = (level & s->prio_mask[s->priority]) != 0; 52d537cf6cSpbrook qemu_set_irq(s->irq, set); 53cdbdb648Spbrook set = ((s->level | s->soft_level) & s->fiq_select) != 0; 54d537cf6cSpbrook qemu_set_irq(s->fiq, set); 55cdbdb648Spbrook } 56cdbdb648Spbrook 57cdbdb648Spbrook static void pl190_set_irq(void *opaque, int irq, int level) 58cdbdb648Spbrook { 59*aefbc256SAndreas Färber PL190State *s = (PL190State *)opaque; 60cdbdb648Spbrook 61cdbdb648Spbrook if (level) 62cdbdb648Spbrook s->level |= 1u << irq; 63cdbdb648Spbrook else 64cdbdb648Spbrook s->level &= ~(1u << irq); 65cdbdb648Spbrook pl190_update(s); 66cdbdb648Spbrook } 67cdbdb648Spbrook 68*aefbc256SAndreas Färber static void pl190_update_vectors(PL190State *s) 69cdbdb648Spbrook { 70cdbdb648Spbrook uint32_t mask; 71cdbdb648Spbrook int i; 72cdbdb648Spbrook int n; 73cdbdb648Spbrook 74cdbdb648Spbrook mask = 0; 75cdbdb648Spbrook for (i = 0; i < 16; i++) 76cdbdb648Spbrook { 77cdbdb648Spbrook s->prio_mask[i] = mask; 78cdbdb648Spbrook if (s->vect_control[i] & 0x20) 79cdbdb648Spbrook { 80cdbdb648Spbrook n = s->vect_control[i] & 0x1f; 81cdbdb648Spbrook mask |= 1 << n; 82cdbdb648Spbrook } 83cdbdb648Spbrook } 84cdbdb648Spbrook s->prio_mask[16] = mask; 85cdbdb648Spbrook pl190_update(s); 86cdbdb648Spbrook } 87cdbdb648Spbrook 88a8170e5eSAvi Kivity static uint64_t pl190_read(void *opaque, hwaddr offset, 897f8293bfSAvi Kivity unsigned size) 90cdbdb648Spbrook { 91*aefbc256SAndreas Färber PL190State *s = (PL190State *)opaque; 92cdbdb648Spbrook int i; 93cdbdb648Spbrook 94cdbdb648Spbrook if (offset >= 0xfe0 && offset < 0x1000) { 95cdbdb648Spbrook return pl190_id[(offset - 0xfe0) >> 2]; 96cdbdb648Spbrook } 97cdbdb648Spbrook if (offset >= 0x100 && offset < 0x140) { 98cdbdb648Spbrook return s->vect_addr[(offset - 0x100) >> 2]; 99cdbdb648Spbrook } 100cdbdb648Spbrook if (offset >= 0x200 && offset < 0x240) { 101cdbdb648Spbrook return s->vect_control[(offset - 0x200) >> 2]; 102cdbdb648Spbrook } 103cdbdb648Spbrook switch (offset >> 2) { 104cdbdb648Spbrook case 0: /* IRQSTATUS */ 105cdbdb648Spbrook return pl190_irq_level(s); 106cdbdb648Spbrook case 1: /* FIQSATUS */ 107cdbdb648Spbrook return (s->level | s->soft_level) & s->fiq_select; 108cdbdb648Spbrook case 2: /* RAWINTR */ 109cdbdb648Spbrook return s->level | s->soft_level; 110cdbdb648Spbrook case 3: /* INTSELECT */ 111cdbdb648Spbrook return s->fiq_select; 112cdbdb648Spbrook case 4: /* INTENABLE */ 113cdbdb648Spbrook return s->irq_enable; 114cdbdb648Spbrook case 6: /* SOFTINT */ 115cdbdb648Spbrook return s->soft_level; 116cdbdb648Spbrook case 8: /* PROTECTION */ 117cdbdb648Spbrook return s->protected; 118cdbdb648Spbrook case 12: /* VECTADDR */ 119cdbdb648Spbrook /* Read vector address at the start of an ISR. Increases the 12014c126baSBrendan Fennell * current priority level to that of the current interrupt. 12114c126baSBrendan Fennell * 12214c126baSBrendan Fennell * Since an enabled interrupt X at priority P causes prio_mask[Y] 12314c126baSBrendan Fennell * to have bit X set for all Y > P, this loop will stop with 12414c126baSBrendan Fennell * i == the priority of the highest priority set interrupt. 12514c126baSBrendan Fennell */ 12614c126baSBrendan Fennell for (i = 0; i < s->priority; i++) { 12714c126baSBrendan Fennell if ((s->level | s->soft_level) & s->prio_mask[i + 1]) { 128cdbdb648Spbrook break; 129cdbdb648Spbrook } 13014c126baSBrendan Fennell } 13114c126baSBrendan Fennell 132cdbdb648Spbrook /* Reading this value with no pending interrupts is undefined. 133cdbdb648Spbrook We return the default address. */ 134cdbdb648Spbrook if (i == PL190_NUM_PRIO) 135cdbdb648Spbrook return s->vect_addr[16]; 136cdbdb648Spbrook if (i < s->priority) 137cdbdb648Spbrook { 138cdbdb648Spbrook s->prev_prio[i] = s->priority; 139cdbdb648Spbrook s->priority = i; 140cdbdb648Spbrook pl190_update(s); 141cdbdb648Spbrook } 142cdbdb648Spbrook return s->vect_addr[s->priority]; 143cdbdb648Spbrook case 13: /* DEFVECTADDR */ 144cdbdb648Spbrook return s->vect_addr[16]; 145cdbdb648Spbrook default: 146fd271e81SPeter Maydell qemu_log_mask(LOG_GUEST_ERROR, 147fd271e81SPeter Maydell "pl190_read: Bad offset %x\n", (int)offset); 148cdbdb648Spbrook return 0; 149cdbdb648Spbrook } 150cdbdb648Spbrook } 151cdbdb648Spbrook 152a8170e5eSAvi Kivity static void pl190_write(void *opaque, hwaddr offset, 1537f8293bfSAvi Kivity uint64_t val, unsigned size) 154cdbdb648Spbrook { 155*aefbc256SAndreas Färber PL190State *s = (PL190State *)opaque; 156cdbdb648Spbrook 157cdbdb648Spbrook if (offset >= 0x100 && offset < 0x140) { 158cdbdb648Spbrook s->vect_addr[(offset - 0x100) >> 2] = val; 159cdbdb648Spbrook pl190_update_vectors(s); 160cdbdb648Spbrook return; 161cdbdb648Spbrook } 162cdbdb648Spbrook if (offset >= 0x200 && offset < 0x240) { 163cdbdb648Spbrook s->vect_control[(offset - 0x200) >> 2] = val; 164cdbdb648Spbrook pl190_update_vectors(s); 165cdbdb648Spbrook return; 166cdbdb648Spbrook } 167cdbdb648Spbrook switch (offset >> 2) { 168cdbdb648Spbrook case 0: /* SELECT */ 169cdbdb648Spbrook /* This is a readonly register, but linux tries to write to it 170cdbdb648Spbrook anyway. Ignore the write. */ 171cdbdb648Spbrook break; 172cdbdb648Spbrook case 3: /* INTSELECT */ 173cdbdb648Spbrook s->fiq_select = val; 174cdbdb648Spbrook break; 175cdbdb648Spbrook case 4: /* INTENABLE */ 176cdbdb648Spbrook s->irq_enable |= val; 177cdbdb648Spbrook break; 178cdbdb648Spbrook case 5: /* INTENCLEAR */ 179cdbdb648Spbrook s->irq_enable &= ~val; 180cdbdb648Spbrook break; 181cdbdb648Spbrook case 6: /* SOFTINT */ 182cdbdb648Spbrook s->soft_level |= val; 183cdbdb648Spbrook break; 184cdbdb648Spbrook case 7: /* SOFTINTCLEAR */ 185cdbdb648Spbrook s->soft_level &= ~val; 186cdbdb648Spbrook break; 187cdbdb648Spbrook case 8: /* PROTECTION */ 188cdbdb648Spbrook /* TODO: Protection (supervisor only access) is not implemented. */ 189cdbdb648Spbrook s->protected = val & 1; 190cdbdb648Spbrook break; 191cdbdb648Spbrook case 12: /* VECTADDR */ 192cdbdb648Spbrook /* Restore the previous priority level. The value written is 193cdbdb648Spbrook ignored. */ 194cdbdb648Spbrook if (s->priority < PL190_NUM_PRIO) 195cdbdb648Spbrook s->priority = s->prev_prio[s->priority]; 196cdbdb648Spbrook break; 197cdbdb648Spbrook case 13: /* DEFVECTADDR */ 198730986e4SPeter Maydell s->vect_addr[16] = val; 199cdbdb648Spbrook break; 200cdbdb648Spbrook case 0xc0: /* ITCR */ 2012ac71179SPaul Brook if (val) { 2022d746989SPeter Maydell qemu_log_mask(LOG_UNIMP, "pl190: Test mode not implemented\n"); 2032ac71179SPaul Brook } 204cdbdb648Spbrook break; 205cdbdb648Spbrook default: 206fd271e81SPeter Maydell qemu_log_mask(LOG_GUEST_ERROR, 207fd271e81SPeter Maydell "pl190_write: Bad offset %x\n", (int)offset); 208cdbdb648Spbrook return; 209cdbdb648Spbrook } 210cdbdb648Spbrook pl190_update(s); 211cdbdb648Spbrook } 212cdbdb648Spbrook 2137f8293bfSAvi Kivity static const MemoryRegionOps pl190_ops = { 2147f8293bfSAvi Kivity .read = pl190_read, 2157f8293bfSAvi Kivity .write = pl190_write, 2167f8293bfSAvi Kivity .endianness = DEVICE_NATIVE_ENDIAN, 217cdbdb648Spbrook }; 218cdbdb648Spbrook 219ac49d750SPeter Maydell static void pl190_reset(DeviceState *d) 220cdbdb648Spbrook { 221*aefbc256SAndreas Färber PL190State *s = DO_UPCAST(PL190State, busdev.qdev, d); 222cdbdb648Spbrook int i; 223cdbdb648Spbrook 224cdbdb648Spbrook for (i = 0; i < 16; i++) 225cdbdb648Spbrook { 226cdbdb648Spbrook s->vect_addr[i] = 0; 227cdbdb648Spbrook s->vect_control[i] = 0; 228cdbdb648Spbrook } 229cdbdb648Spbrook s->vect_addr[16] = 0; 230cdbdb648Spbrook s->prio_mask[17] = 0xffffffff; 231cdbdb648Spbrook s->priority = PL190_NUM_PRIO; 232cdbdb648Spbrook pl190_update_vectors(s); 233cdbdb648Spbrook } 234cdbdb648Spbrook 23581a322d4SGerd Hoffmann static int pl190_init(SysBusDevice *dev) 236cdbdb648Spbrook { 237*aefbc256SAndreas Färber PL190State *s = FROM_SYSBUS(PL190State, dev); 238cdbdb648Spbrook 2391437c94bSPaolo Bonzini memory_region_init_io(&s->iomem, OBJECT(s), &pl190_ops, s, "pl190", 0x1000); 240750ecd44SAvi Kivity sysbus_init_mmio(dev, &s->iomem); 241067a3ddcSPaul Brook qdev_init_gpio_in(&dev->qdev, pl190_set_irq, 32); 24297aff481SPaul Brook sysbus_init_irq(dev, &s->irq); 24397aff481SPaul Brook sysbus_init_irq(dev, &s->fiq); 24481a322d4SGerd Hoffmann return 0; 245cdbdb648Spbrook } 24697aff481SPaul Brook 247ac49d750SPeter Maydell static const VMStateDescription vmstate_pl190 = { 248ac49d750SPeter Maydell .name = "pl190", 249ac49d750SPeter Maydell .version_id = 1, 250ac49d750SPeter Maydell .minimum_version_id = 1, 251ac49d750SPeter Maydell .fields = (VMStateField[]) { 252*aefbc256SAndreas Färber VMSTATE_UINT32(level, PL190State), 253*aefbc256SAndreas Färber VMSTATE_UINT32(soft_level, PL190State), 254*aefbc256SAndreas Färber VMSTATE_UINT32(irq_enable, PL190State), 255*aefbc256SAndreas Färber VMSTATE_UINT32(fiq_select, PL190State), 256*aefbc256SAndreas Färber VMSTATE_UINT8_ARRAY(vect_control, PL190State, 16), 257*aefbc256SAndreas Färber VMSTATE_UINT32_ARRAY(vect_addr, PL190State, PL190_NUM_PRIO), 258*aefbc256SAndreas Färber VMSTATE_UINT32_ARRAY(prio_mask, PL190State, PL190_NUM_PRIO+1), 259*aefbc256SAndreas Färber VMSTATE_INT32(protected, PL190State), 260*aefbc256SAndreas Färber VMSTATE_INT32(priority, PL190State), 261*aefbc256SAndreas Färber VMSTATE_INT32_ARRAY(prev_prio, PL190State, PL190_NUM_PRIO), 262ac49d750SPeter Maydell VMSTATE_END_OF_LIST() 263ac49d750SPeter Maydell } 264ac49d750SPeter Maydell }; 265ac49d750SPeter Maydell 266999e12bbSAnthony Liguori static void pl190_class_init(ObjectClass *klass, void *data) 267999e12bbSAnthony Liguori { 26839bffca2SAnthony Liguori DeviceClass *dc = DEVICE_CLASS(klass); 269999e12bbSAnthony Liguori SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); 270999e12bbSAnthony Liguori 271999e12bbSAnthony Liguori k->init = pl190_init; 27239bffca2SAnthony Liguori dc->no_user = 1; 27339bffca2SAnthony Liguori dc->reset = pl190_reset; 27439bffca2SAnthony Liguori dc->vmsd = &vmstate_pl190; 275999e12bbSAnthony Liguori } 276999e12bbSAnthony Liguori 2778c43a6f0SAndreas Färber static const TypeInfo pl190_info = { 278999e12bbSAnthony Liguori .name = "pl190", 27939bffca2SAnthony Liguori .parent = TYPE_SYS_BUS_DEVICE, 280*aefbc256SAndreas Färber .instance_size = sizeof(PL190State), 281999e12bbSAnthony Liguori .class_init = pl190_class_init, 282ac49d750SPeter Maydell }; 283ac49d750SPeter Maydell 28483f7d43aSAndreas Färber static void pl190_register_types(void) 28597aff481SPaul Brook { 28639bffca2SAnthony Liguori type_register_static(&pl190_info); 28797aff481SPaul Brook } 28897aff481SPaul Brook 28983f7d43aSAndreas Färber type_init(pl190_register_types) 290