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 108ef94f0bSPeter Maydell #include "qemu/osdep.h" 11*64552b6bSMarkus Armbruster #include "hw/irq.h" 1283c9f4caSPaolo Bonzini #include "hw/sysbus.h" 1303dd024fSPaolo Bonzini #include "qemu/log.h" 140b8fa32fSMarkus Armbruster #include "qemu/module.h" 15cdbdb648Spbrook 16cdbdb648Spbrook /* The number of virtual priority levels. 16 user vectors plus the 17cdbdb648Spbrook unvectored IRQ. Chained interrupts would require an additional level 18cdbdb648Spbrook if implemented. */ 19cdbdb648Spbrook 20cdbdb648Spbrook #define PL190_NUM_PRIO 17 21cdbdb648Spbrook 227fc3266fSAndreas Färber #define TYPE_PL190 "pl190" 237fc3266fSAndreas Färber #define PL190(obj) OBJECT_CHECK(PL190State, (obj), TYPE_PL190) 247fc3266fSAndreas Färber 25aefbc256SAndreas Färber typedef struct PL190State { 267fc3266fSAndreas Färber SysBusDevice parent_obj; 277fc3266fSAndreas Färber 287f8293bfSAvi Kivity MemoryRegion iomem; 29cdbdb648Spbrook uint32_t level; 30cdbdb648Spbrook uint32_t soft_level; 31cdbdb648Spbrook uint32_t irq_enable; 32cdbdb648Spbrook uint32_t fiq_select; 33cdbdb648Spbrook uint8_t vect_control[16]; 34cdbdb648Spbrook uint32_t vect_addr[PL190_NUM_PRIO]; 35cdbdb648Spbrook /* Mask containing interrupts with higher priority than this one. */ 36cdbdb648Spbrook uint32_t prio_mask[PL190_NUM_PRIO + 1]; 37cdbdb648Spbrook int protected; 38cdbdb648Spbrook /* Current priority level. */ 39cdbdb648Spbrook int priority; 40cdbdb648Spbrook int prev_prio[PL190_NUM_PRIO]; 41d537cf6cSpbrook qemu_irq irq; 42d537cf6cSpbrook qemu_irq fiq; 43aefbc256SAndreas Färber } PL190State; 44cdbdb648Spbrook 45cdbdb648Spbrook static const unsigned char pl190_id[] = 46cdbdb648Spbrook { 0x90, 0x11, 0x04, 0x00, 0x0D, 0xf0, 0x05, 0xb1 }; 47cdbdb648Spbrook 48aefbc256SAndreas Färber static inline uint32_t pl190_irq_level(PL190State *s) 49cdbdb648Spbrook { 50cdbdb648Spbrook return (s->level | s->soft_level) & s->irq_enable & ~s->fiq_select; 51cdbdb648Spbrook } 52cdbdb648Spbrook 53cdbdb648Spbrook /* Update interrupts. */ 54aefbc256SAndreas Färber static void pl190_update(PL190State *s) 55cdbdb648Spbrook { 56cdbdb648Spbrook uint32_t level = pl190_irq_level(s); 57cdbdb648Spbrook int set; 58cdbdb648Spbrook 59cdbdb648Spbrook set = (level & s->prio_mask[s->priority]) != 0; 60d537cf6cSpbrook qemu_set_irq(s->irq, set); 61cdbdb648Spbrook set = ((s->level | s->soft_level) & s->fiq_select) != 0; 62d537cf6cSpbrook qemu_set_irq(s->fiq, set); 63cdbdb648Spbrook } 64cdbdb648Spbrook 65cdbdb648Spbrook static void pl190_set_irq(void *opaque, int irq, int level) 66cdbdb648Spbrook { 67aefbc256SAndreas Färber PL190State *s = (PL190State *)opaque; 68cdbdb648Spbrook 69cdbdb648Spbrook if (level) 70cdbdb648Spbrook s->level |= 1u << irq; 71cdbdb648Spbrook else 72cdbdb648Spbrook s->level &= ~(1u << irq); 73cdbdb648Spbrook pl190_update(s); 74cdbdb648Spbrook } 75cdbdb648Spbrook 76aefbc256SAndreas Färber static void pl190_update_vectors(PL190State *s) 77cdbdb648Spbrook { 78cdbdb648Spbrook uint32_t mask; 79cdbdb648Spbrook int i; 80cdbdb648Spbrook int n; 81cdbdb648Spbrook 82cdbdb648Spbrook mask = 0; 83cdbdb648Spbrook for (i = 0; i < 16; i++) 84cdbdb648Spbrook { 85cdbdb648Spbrook s->prio_mask[i] = mask; 86cdbdb648Spbrook if (s->vect_control[i] & 0x20) 87cdbdb648Spbrook { 88cdbdb648Spbrook n = s->vect_control[i] & 0x1f; 89cdbdb648Spbrook mask |= 1 << n; 90cdbdb648Spbrook } 91cdbdb648Spbrook } 92cdbdb648Spbrook s->prio_mask[16] = mask; 93cdbdb648Spbrook pl190_update(s); 94cdbdb648Spbrook } 95cdbdb648Spbrook 96a8170e5eSAvi Kivity static uint64_t pl190_read(void *opaque, hwaddr offset, 977f8293bfSAvi Kivity unsigned size) 98cdbdb648Spbrook { 99aefbc256SAndreas Färber PL190State *s = (PL190State *)opaque; 100cdbdb648Spbrook int i; 101cdbdb648Spbrook 102cdbdb648Spbrook if (offset >= 0xfe0 && offset < 0x1000) { 103cdbdb648Spbrook return pl190_id[(offset - 0xfe0) >> 2]; 104cdbdb648Spbrook } 105cdbdb648Spbrook if (offset >= 0x100 && offset < 0x140) { 106cdbdb648Spbrook return s->vect_addr[(offset - 0x100) >> 2]; 107cdbdb648Spbrook } 108cdbdb648Spbrook if (offset >= 0x200 && offset < 0x240) { 109cdbdb648Spbrook return s->vect_control[(offset - 0x200) >> 2]; 110cdbdb648Spbrook } 111cdbdb648Spbrook switch (offset >> 2) { 112cdbdb648Spbrook case 0: /* IRQSTATUS */ 113cdbdb648Spbrook return pl190_irq_level(s); 114cdbdb648Spbrook case 1: /* FIQSATUS */ 115cdbdb648Spbrook return (s->level | s->soft_level) & s->fiq_select; 116cdbdb648Spbrook case 2: /* RAWINTR */ 117cdbdb648Spbrook return s->level | s->soft_level; 118cdbdb648Spbrook case 3: /* INTSELECT */ 119cdbdb648Spbrook return s->fiq_select; 120cdbdb648Spbrook case 4: /* INTENABLE */ 121cdbdb648Spbrook return s->irq_enable; 122cdbdb648Spbrook case 6: /* SOFTINT */ 123cdbdb648Spbrook return s->soft_level; 124cdbdb648Spbrook case 8: /* PROTECTION */ 125cdbdb648Spbrook return s->protected; 126cdbdb648Spbrook case 12: /* VECTADDR */ 127cdbdb648Spbrook /* Read vector address at the start of an ISR. Increases the 12814c126baSBrendan Fennell * current priority level to that of the current interrupt. 12914c126baSBrendan Fennell * 13014c126baSBrendan Fennell * Since an enabled interrupt X at priority P causes prio_mask[Y] 13114c126baSBrendan Fennell * to have bit X set for all Y > P, this loop will stop with 13214c126baSBrendan Fennell * i == the priority of the highest priority set interrupt. 13314c126baSBrendan Fennell */ 13414c126baSBrendan Fennell for (i = 0; i < s->priority; i++) { 13514c126baSBrendan Fennell if ((s->level | s->soft_level) & s->prio_mask[i + 1]) { 136cdbdb648Spbrook break; 137cdbdb648Spbrook } 13814c126baSBrendan Fennell } 13914c126baSBrendan Fennell 140cdbdb648Spbrook /* Reading this value with no pending interrupts is undefined. 141cdbdb648Spbrook We return the default address. */ 142cdbdb648Spbrook if (i == PL190_NUM_PRIO) 143cdbdb648Spbrook return s->vect_addr[16]; 144cdbdb648Spbrook if (i < s->priority) 145cdbdb648Spbrook { 146cdbdb648Spbrook s->prev_prio[i] = s->priority; 147cdbdb648Spbrook s->priority = i; 148cdbdb648Spbrook pl190_update(s); 149cdbdb648Spbrook } 150cdbdb648Spbrook return s->vect_addr[s->priority]; 151cdbdb648Spbrook case 13: /* DEFVECTADDR */ 152cdbdb648Spbrook return s->vect_addr[16]; 153cdbdb648Spbrook default: 154fd271e81SPeter Maydell qemu_log_mask(LOG_GUEST_ERROR, 155fd271e81SPeter Maydell "pl190_read: Bad offset %x\n", (int)offset); 156cdbdb648Spbrook return 0; 157cdbdb648Spbrook } 158cdbdb648Spbrook } 159cdbdb648Spbrook 160a8170e5eSAvi Kivity static void pl190_write(void *opaque, hwaddr offset, 1617f8293bfSAvi Kivity uint64_t val, unsigned size) 162cdbdb648Spbrook { 163aefbc256SAndreas Färber PL190State *s = (PL190State *)opaque; 164cdbdb648Spbrook 165cdbdb648Spbrook if (offset >= 0x100 && offset < 0x140) { 166cdbdb648Spbrook s->vect_addr[(offset - 0x100) >> 2] = val; 167cdbdb648Spbrook pl190_update_vectors(s); 168cdbdb648Spbrook return; 169cdbdb648Spbrook } 170cdbdb648Spbrook if (offset >= 0x200 && offset < 0x240) { 171cdbdb648Spbrook s->vect_control[(offset - 0x200) >> 2] = val; 172cdbdb648Spbrook pl190_update_vectors(s); 173cdbdb648Spbrook return; 174cdbdb648Spbrook } 175cdbdb648Spbrook switch (offset >> 2) { 176cdbdb648Spbrook case 0: /* SELECT */ 177cdbdb648Spbrook /* This is a readonly register, but linux tries to write to it 178cdbdb648Spbrook anyway. Ignore the write. */ 179cdbdb648Spbrook break; 180cdbdb648Spbrook case 3: /* INTSELECT */ 181cdbdb648Spbrook s->fiq_select = val; 182cdbdb648Spbrook break; 183cdbdb648Spbrook case 4: /* INTENABLE */ 184cdbdb648Spbrook s->irq_enable |= val; 185cdbdb648Spbrook break; 186cdbdb648Spbrook case 5: /* INTENCLEAR */ 187cdbdb648Spbrook s->irq_enable &= ~val; 188cdbdb648Spbrook break; 189cdbdb648Spbrook case 6: /* SOFTINT */ 190cdbdb648Spbrook s->soft_level |= val; 191cdbdb648Spbrook break; 192cdbdb648Spbrook case 7: /* SOFTINTCLEAR */ 193cdbdb648Spbrook s->soft_level &= ~val; 194cdbdb648Spbrook break; 195cdbdb648Spbrook case 8: /* PROTECTION */ 196cdbdb648Spbrook /* TODO: Protection (supervisor only access) is not implemented. */ 197cdbdb648Spbrook s->protected = val & 1; 198cdbdb648Spbrook break; 199cdbdb648Spbrook case 12: /* VECTADDR */ 200cdbdb648Spbrook /* Restore the previous priority level. The value written is 201cdbdb648Spbrook ignored. */ 202cdbdb648Spbrook if (s->priority < PL190_NUM_PRIO) 203cdbdb648Spbrook s->priority = s->prev_prio[s->priority]; 204cdbdb648Spbrook break; 205cdbdb648Spbrook case 13: /* DEFVECTADDR */ 206730986e4SPeter Maydell s->vect_addr[16] = val; 207cdbdb648Spbrook break; 208cdbdb648Spbrook case 0xc0: /* ITCR */ 2092ac71179SPaul Brook if (val) { 2102d746989SPeter Maydell qemu_log_mask(LOG_UNIMP, "pl190: Test mode not implemented\n"); 2112ac71179SPaul Brook } 212cdbdb648Spbrook break; 213cdbdb648Spbrook default: 214fd271e81SPeter Maydell qemu_log_mask(LOG_GUEST_ERROR, 215fd271e81SPeter Maydell "pl190_write: Bad offset %x\n", (int)offset); 216cdbdb648Spbrook return; 217cdbdb648Spbrook } 218cdbdb648Spbrook pl190_update(s); 219cdbdb648Spbrook } 220cdbdb648Spbrook 2217f8293bfSAvi Kivity static const MemoryRegionOps pl190_ops = { 2227f8293bfSAvi Kivity .read = pl190_read, 2237f8293bfSAvi Kivity .write = pl190_write, 2247f8293bfSAvi Kivity .endianness = DEVICE_NATIVE_ENDIAN, 225cdbdb648Spbrook }; 226cdbdb648Spbrook 227ac49d750SPeter Maydell static void pl190_reset(DeviceState *d) 228cdbdb648Spbrook { 2297fc3266fSAndreas Färber PL190State *s = PL190(d); 230cdbdb648Spbrook int i; 231cdbdb648Spbrook 2327fc3266fSAndreas Färber for (i = 0; i < 16; i++) { 233cdbdb648Spbrook s->vect_addr[i] = 0; 234cdbdb648Spbrook s->vect_control[i] = 0; 235cdbdb648Spbrook } 236cdbdb648Spbrook s->vect_addr[16] = 0; 237cdbdb648Spbrook s->prio_mask[17] = 0xffffffff; 238cdbdb648Spbrook s->priority = PL190_NUM_PRIO; 239cdbdb648Spbrook pl190_update_vectors(s); 240cdbdb648Spbrook } 241cdbdb648Spbrook 242e3be8b4fSxiaoqiang.zhao static void pl190_init(Object *obj) 243cdbdb648Spbrook { 244e3be8b4fSxiaoqiang.zhao DeviceState *dev = DEVICE(obj); 245e3be8b4fSxiaoqiang.zhao PL190State *s = PL190(obj); 246e3be8b4fSxiaoqiang.zhao SysBusDevice *sbd = SYS_BUS_DEVICE(obj); 247cdbdb648Spbrook 248e3be8b4fSxiaoqiang.zhao memory_region_init_io(&s->iomem, obj, &pl190_ops, s, "pl190", 0x1000); 2497fc3266fSAndreas Färber sysbus_init_mmio(sbd, &s->iomem); 2507fc3266fSAndreas Färber qdev_init_gpio_in(dev, pl190_set_irq, 32); 2517fc3266fSAndreas Färber sysbus_init_irq(sbd, &s->irq); 2527fc3266fSAndreas Färber sysbus_init_irq(sbd, &s->fiq); 253cdbdb648Spbrook } 25497aff481SPaul Brook 255ac49d750SPeter Maydell static const VMStateDescription vmstate_pl190 = { 256ac49d750SPeter Maydell .name = "pl190", 257ac49d750SPeter Maydell .version_id = 1, 258ac49d750SPeter Maydell .minimum_version_id = 1, 259ac49d750SPeter Maydell .fields = (VMStateField[]) { 260aefbc256SAndreas Färber VMSTATE_UINT32(level, PL190State), 261aefbc256SAndreas Färber VMSTATE_UINT32(soft_level, PL190State), 262aefbc256SAndreas Färber VMSTATE_UINT32(irq_enable, PL190State), 263aefbc256SAndreas Färber VMSTATE_UINT32(fiq_select, PL190State), 264aefbc256SAndreas Färber VMSTATE_UINT8_ARRAY(vect_control, PL190State, 16), 265aefbc256SAndreas Färber VMSTATE_UINT32_ARRAY(vect_addr, PL190State, PL190_NUM_PRIO), 266aefbc256SAndreas Färber VMSTATE_UINT32_ARRAY(prio_mask, PL190State, PL190_NUM_PRIO+1), 267aefbc256SAndreas Färber VMSTATE_INT32(protected, PL190State), 268aefbc256SAndreas Färber VMSTATE_INT32(priority, PL190State), 269aefbc256SAndreas Färber VMSTATE_INT32_ARRAY(prev_prio, PL190State, PL190_NUM_PRIO), 270ac49d750SPeter Maydell VMSTATE_END_OF_LIST() 271ac49d750SPeter Maydell } 272ac49d750SPeter Maydell }; 273ac49d750SPeter Maydell 274999e12bbSAnthony Liguori static void pl190_class_init(ObjectClass *klass, void *data) 275999e12bbSAnthony Liguori { 27639bffca2SAnthony Liguori DeviceClass *dc = DEVICE_CLASS(klass); 277999e12bbSAnthony Liguori 27839bffca2SAnthony Liguori dc->reset = pl190_reset; 27939bffca2SAnthony Liguori dc->vmsd = &vmstate_pl190; 280999e12bbSAnthony Liguori } 281999e12bbSAnthony Liguori 2828c43a6f0SAndreas Färber static const TypeInfo pl190_info = { 2837fc3266fSAndreas Färber .name = TYPE_PL190, 28439bffca2SAnthony Liguori .parent = TYPE_SYS_BUS_DEVICE, 285aefbc256SAndreas Färber .instance_size = sizeof(PL190State), 286e3be8b4fSxiaoqiang.zhao .instance_init = pl190_init, 287999e12bbSAnthony Liguori .class_init = pl190_class_init, 288ac49d750SPeter Maydell }; 289ac49d750SPeter Maydell 29083f7d43aSAndreas Färber static void pl190_register_types(void) 29197aff481SPaul Brook { 29239bffca2SAnthony Liguori type_register_static(&pl190_info); 29397aff481SPaul Brook } 29497aff481SPaul Brook 29583f7d43aSAndreas Färber type_init(pl190_register_types) 296