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*7fc3266fSAndreas Färber #define TYPE_PL190 "pl190" 19*7fc3266fSAndreas Färber #define PL190(obj) OBJECT_CHECK(PL190State, (obj), TYPE_PL190) 20*7fc3266fSAndreas Färber 21aefbc256SAndreas Färber typedef struct PL190State { 22*7fc3266fSAndreas Färber SysBusDevice parent_obj; 23*7fc3266fSAndreas Färber 247f8293bfSAvi Kivity MemoryRegion iomem; 25cdbdb648Spbrook uint32_t level; 26cdbdb648Spbrook uint32_t soft_level; 27cdbdb648Spbrook uint32_t irq_enable; 28cdbdb648Spbrook uint32_t fiq_select; 29cdbdb648Spbrook uint8_t vect_control[16]; 30cdbdb648Spbrook uint32_t vect_addr[PL190_NUM_PRIO]; 31cdbdb648Spbrook /* Mask containing interrupts with higher priority than this one. */ 32cdbdb648Spbrook uint32_t prio_mask[PL190_NUM_PRIO + 1]; 33cdbdb648Spbrook int protected; 34cdbdb648Spbrook /* Current priority level. */ 35cdbdb648Spbrook int priority; 36cdbdb648Spbrook int prev_prio[PL190_NUM_PRIO]; 37d537cf6cSpbrook qemu_irq irq; 38d537cf6cSpbrook qemu_irq fiq; 39aefbc256SAndreas Färber } PL190State; 40cdbdb648Spbrook 41cdbdb648Spbrook static const unsigned char pl190_id[] = 42cdbdb648Spbrook { 0x90, 0x11, 0x04, 0x00, 0x0D, 0xf0, 0x05, 0xb1 }; 43cdbdb648Spbrook 44aefbc256SAndreas Färber static inline uint32_t pl190_irq_level(PL190State *s) 45cdbdb648Spbrook { 46cdbdb648Spbrook return (s->level | s->soft_level) & s->irq_enable & ~s->fiq_select; 47cdbdb648Spbrook } 48cdbdb648Spbrook 49cdbdb648Spbrook /* Update interrupts. */ 50aefbc256SAndreas Färber static void pl190_update(PL190State *s) 51cdbdb648Spbrook { 52cdbdb648Spbrook uint32_t level = pl190_irq_level(s); 53cdbdb648Spbrook int set; 54cdbdb648Spbrook 55cdbdb648Spbrook set = (level & s->prio_mask[s->priority]) != 0; 56d537cf6cSpbrook qemu_set_irq(s->irq, set); 57cdbdb648Spbrook set = ((s->level | s->soft_level) & s->fiq_select) != 0; 58d537cf6cSpbrook qemu_set_irq(s->fiq, set); 59cdbdb648Spbrook } 60cdbdb648Spbrook 61cdbdb648Spbrook static void pl190_set_irq(void *opaque, int irq, int level) 62cdbdb648Spbrook { 63aefbc256SAndreas Färber PL190State *s = (PL190State *)opaque; 64cdbdb648Spbrook 65cdbdb648Spbrook if (level) 66cdbdb648Spbrook s->level |= 1u << irq; 67cdbdb648Spbrook else 68cdbdb648Spbrook s->level &= ~(1u << irq); 69cdbdb648Spbrook pl190_update(s); 70cdbdb648Spbrook } 71cdbdb648Spbrook 72aefbc256SAndreas Färber static void pl190_update_vectors(PL190State *s) 73cdbdb648Spbrook { 74cdbdb648Spbrook uint32_t mask; 75cdbdb648Spbrook int i; 76cdbdb648Spbrook int n; 77cdbdb648Spbrook 78cdbdb648Spbrook mask = 0; 79cdbdb648Spbrook for (i = 0; i < 16; i++) 80cdbdb648Spbrook { 81cdbdb648Spbrook s->prio_mask[i] = mask; 82cdbdb648Spbrook if (s->vect_control[i] & 0x20) 83cdbdb648Spbrook { 84cdbdb648Spbrook n = s->vect_control[i] & 0x1f; 85cdbdb648Spbrook mask |= 1 << n; 86cdbdb648Spbrook } 87cdbdb648Spbrook } 88cdbdb648Spbrook s->prio_mask[16] = mask; 89cdbdb648Spbrook pl190_update(s); 90cdbdb648Spbrook } 91cdbdb648Spbrook 92a8170e5eSAvi Kivity static uint64_t pl190_read(void *opaque, hwaddr offset, 937f8293bfSAvi Kivity unsigned size) 94cdbdb648Spbrook { 95aefbc256SAndreas Färber PL190State *s = (PL190State *)opaque; 96cdbdb648Spbrook int i; 97cdbdb648Spbrook 98cdbdb648Spbrook if (offset >= 0xfe0 && offset < 0x1000) { 99cdbdb648Spbrook return pl190_id[(offset - 0xfe0) >> 2]; 100cdbdb648Spbrook } 101cdbdb648Spbrook if (offset >= 0x100 && offset < 0x140) { 102cdbdb648Spbrook return s->vect_addr[(offset - 0x100) >> 2]; 103cdbdb648Spbrook } 104cdbdb648Spbrook if (offset >= 0x200 && offset < 0x240) { 105cdbdb648Spbrook return s->vect_control[(offset - 0x200) >> 2]; 106cdbdb648Spbrook } 107cdbdb648Spbrook switch (offset >> 2) { 108cdbdb648Spbrook case 0: /* IRQSTATUS */ 109cdbdb648Spbrook return pl190_irq_level(s); 110cdbdb648Spbrook case 1: /* FIQSATUS */ 111cdbdb648Spbrook return (s->level | s->soft_level) & s->fiq_select; 112cdbdb648Spbrook case 2: /* RAWINTR */ 113cdbdb648Spbrook return s->level | s->soft_level; 114cdbdb648Spbrook case 3: /* INTSELECT */ 115cdbdb648Spbrook return s->fiq_select; 116cdbdb648Spbrook case 4: /* INTENABLE */ 117cdbdb648Spbrook return s->irq_enable; 118cdbdb648Spbrook case 6: /* SOFTINT */ 119cdbdb648Spbrook return s->soft_level; 120cdbdb648Spbrook case 8: /* PROTECTION */ 121cdbdb648Spbrook return s->protected; 122cdbdb648Spbrook case 12: /* VECTADDR */ 123cdbdb648Spbrook /* Read vector address at the start of an ISR. Increases the 12414c126baSBrendan Fennell * current priority level to that of the current interrupt. 12514c126baSBrendan Fennell * 12614c126baSBrendan Fennell * Since an enabled interrupt X at priority P causes prio_mask[Y] 12714c126baSBrendan Fennell * to have bit X set for all Y > P, this loop will stop with 12814c126baSBrendan Fennell * i == the priority of the highest priority set interrupt. 12914c126baSBrendan Fennell */ 13014c126baSBrendan Fennell for (i = 0; i < s->priority; i++) { 13114c126baSBrendan Fennell if ((s->level | s->soft_level) & s->prio_mask[i + 1]) { 132cdbdb648Spbrook break; 133cdbdb648Spbrook } 13414c126baSBrendan Fennell } 13514c126baSBrendan Fennell 136cdbdb648Spbrook /* Reading this value with no pending interrupts is undefined. 137cdbdb648Spbrook We return the default address. */ 138cdbdb648Spbrook if (i == PL190_NUM_PRIO) 139cdbdb648Spbrook return s->vect_addr[16]; 140cdbdb648Spbrook if (i < s->priority) 141cdbdb648Spbrook { 142cdbdb648Spbrook s->prev_prio[i] = s->priority; 143cdbdb648Spbrook s->priority = i; 144cdbdb648Spbrook pl190_update(s); 145cdbdb648Spbrook } 146cdbdb648Spbrook return s->vect_addr[s->priority]; 147cdbdb648Spbrook case 13: /* DEFVECTADDR */ 148cdbdb648Spbrook return s->vect_addr[16]; 149cdbdb648Spbrook default: 150fd271e81SPeter Maydell qemu_log_mask(LOG_GUEST_ERROR, 151fd271e81SPeter Maydell "pl190_read: Bad offset %x\n", (int)offset); 152cdbdb648Spbrook return 0; 153cdbdb648Spbrook } 154cdbdb648Spbrook } 155cdbdb648Spbrook 156a8170e5eSAvi Kivity static void pl190_write(void *opaque, hwaddr offset, 1577f8293bfSAvi Kivity uint64_t val, unsigned size) 158cdbdb648Spbrook { 159aefbc256SAndreas Färber PL190State *s = (PL190State *)opaque; 160cdbdb648Spbrook 161cdbdb648Spbrook if (offset >= 0x100 && offset < 0x140) { 162cdbdb648Spbrook s->vect_addr[(offset - 0x100) >> 2] = val; 163cdbdb648Spbrook pl190_update_vectors(s); 164cdbdb648Spbrook return; 165cdbdb648Spbrook } 166cdbdb648Spbrook if (offset >= 0x200 && offset < 0x240) { 167cdbdb648Spbrook s->vect_control[(offset - 0x200) >> 2] = val; 168cdbdb648Spbrook pl190_update_vectors(s); 169cdbdb648Spbrook return; 170cdbdb648Spbrook } 171cdbdb648Spbrook switch (offset >> 2) { 172cdbdb648Spbrook case 0: /* SELECT */ 173cdbdb648Spbrook /* This is a readonly register, but linux tries to write to it 174cdbdb648Spbrook anyway. Ignore the write. */ 175cdbdb648Spbrook break; 176cdbdb648Spbrook case 3: /* INTSELECT */ 177cdbdb648Spbrook s->fiq_select = val; 178cdbdb648Spbrook break; 179cdbdb648Spbrook case 4: /* INTENABLE */ 180cdbdb648Spbrook s->irq_enable |= val; 181cdbdb648Spbrook break; 182cdbdb648Spbrook case 5: /* INTENCLEAR */ 183cdbdb648Spbrook s->irq_enable &= ~val; 184cdbdb648Spbrook break; 185cdbdb648Spbrook case 6: /* SOFTINT */ 186cdbdb648Spbrook s->soft_level |= val; 187cdbdb648Spbrook break; 188cdbdb648Spbrook case 7: /* SOFTINTCLEAR */ 189cdbdb648Spbrook s->soft_level &= ~val; 190cdbdb648Spbrook break; 191cdbdb648Spbrook case 8: /* PROTECTION */ 192cdbdb648Spbrook /* TODO: Protection (supervisor only access) is not implemented. */ 193cdbdb648Spbrook s->protected = val & 1; 194cdbdb648Spbrook break; 195cdbdb648Spbrook case 12: /* VECTADDR */ 196cdbdb648Spbrook /* Restore the previous priority level. The value written is 197cdbdb648Spbrook ignored. */ 198cdbdb648Spbrook if (s->priority < PL190_NUM_PRIO) 199cdbdb648Spbrook s->priority = s->prev_prio[s->priority]; 200cdbdb648Spbrook break; 201cdbdb648Spbrook case 13: /* DEFVECTADDR */ 202730986e4SPeter Maydell s->vect_addr[16] = val; 203cdbdb648Spbrook break; 204cdbdb648Spbrook case 0xc0: /* ITCR */ 2052ac71179SPaul Brook if (val) { 2062d746989SPeter Maydell qemu_log_mask(LOG_UNIMP, "pl190: Test mode not implemented\n"); 2072ac71179SPaul Brook } 208cdbdb648Spbrook break; 209cdbdb648Spbrook default: 210fd271e81SPeter Maydell qemu_log_mask(LOG_GUEST_ERROR, 211fd271e81SPeter Maydell "pl190_write: Bad offset %x\n", (int)offset); 212cdbdb648Spbrook return; 213cdbdb648Spbrook } 214cdbdb648Spbrook pl190_update(s); 215cdbdb648Spbrook } 216cdbdb648Spbrook 2177f8293bfSAvi Kivity static const MemoryRegionOps pl190_ops = { 2187f8293bfSAvi Kivity .read = pl190_read, 2197f8293bfSAvi Kivity .write = pl190_write, 2207f8293bfSAvi Kivity .endianness = DEVICE_NATIVE_ENDIAN, 221cdbdb648Spbrook }; 222cdbdb648Spbrook 223ac49d750SPeter Maydell static void pl190_reset(DeviceState *d) 224cdbdb648Spbrook { 225*7fc3266fSAndreas Färber PL190State *s = PL190(d); 226cdbdb648Spbrook int i; 227cdbdb648Spbrook 228*7fc3266fSAndreas Färber for (i = 0; i < 16; i++) { 229cdbdb648Spbrook s->vect_addr[i] = 0; 230cdbdb648Spbrook s->vect_control[i] = 0; 231cdbdb648Spbrook } 232cdbdb648Spbrook s->vect_addr[16] = 0; 233cdbdb648Spbrook s->prio_mask[17] = 0xffffffff; 234cdbdb648Spbrook s->priority = PL190_NUM_PRIO; 235cdbdb648Spbrook pl190_update_vectors(s); 236cdbdb648Spbrook } 237cdbdb648Spbrook 238*7fc3266fSAndreas Färber static int pl190_init(SysBusDevice *sbd) 239cdbdb648Spbrook { 240*7fc3266fSAndreas Färber DeviceState *dev = DEVICE(sbd); 241*7fc3266fSAndreas Färber PL190State *s = PL190(dev); 242cdbdb648Spbrook 2431437c94bSPaolo Bonzini memory_region_init_io(&s->iomem, OBJECT(s), &pl190_ops, s, "pl190", 0x1000); 244*7fc3266fSAndreas Färber sysbus_init_mmio(sbd, &s->iomem); 245*7fc3266fSAndreas Färber qdev_init_gpio_in(dev, pl190_set_irq, 32); 246*7fc3266fSAndreas Färber sysbus_init_irq(sbd, &s->irq); 247*7fc3266fSAndreas Färber sysbus_init_irq(sbd, &s->fiq); 24881a322d4SGerd Hoffmann return 0; 249cdbdb648Spbrook } 25097aff481SPaul Brook 251ac49d750SPeter Maydell static const VMStateDescription vmstate_pl190 = { 252ac49d750SPeter Maydell .name = "pl190", 253ac49d750SPeter Maydell .version_id = 1, 254ac49d750SPeter Maydell .minimum_version_id = 1, 255ac49d750SPeter Maydell .fields = (VMStateField[]) { 256aefbc256SAndreas Färber VMSTATE_UINT32(level, PL190State), 257aefbc256SAndreas Färber VMSTATE_UINT32(soft_level, PL190State), 258aefbc256SAndreas Färber VMSTATE_UINT32(irq_enable, PL190State), 259aefbc256SAndreas Färber VMSTATE_UINT32(fiq_select, PL190State), 260aefbc256SAndreas Färber VMSTATE_UINT8_ARRAY(vect_control, PL190State, 16), 261aefbc256SAndreas Färber VMSTATE_UINT32_ARRAY(vect_addr, PL190State, PL190_NUM_PRIO), 262aefbc256SAndreas Färber VMSTATE_UINT32_ARRAY(prio_mask, PL190State, PL190_NUM_PRIO+1), 263aefbc256SAndreas Färber VMSTATE_INT32(protected, PL190State), 264aefbc256SAndreas Färber VMSTATE_INT32(priority, PL190State), 265aefbc256SAndreas Färber VMSTATE_INT32_ARRAY(prev_prio, PL190State, PL190_NUM_PRIO), 266ac49d750SPeter Maydell VMSTATE_END_OF_LIST() 267ac49d750SPeter Maydell } 268ac49d750SPeter Maydell }; 269ac49d750SPeter Maydell 270999e12bbSAnthony Liguori static void pl190_class_init(ObjectClass *klass, void *data) 271999e12bbSAnthony Liguori { 27239bffca2SAnthony Liguori DeviceClass *dc = DEVICE_CLASS(klass); 273999e12bbSAnthony Liguori SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); 274999e12bbSAnthony Liguori 275999e12bbSAnthony Liguori k->init = pl190_init; 27639bffca2SAnthony Liguori dc->no_user = 1; 27739bffca2SAnthony Liguori dc->reset = pl190_reset; 27839bffca2SAnthony Liguori dc->vmsd = &vmstate_pl190; 279999e12bbSAnthony Liguori } 280999e12bbSAnthony Liguori 2818c43a6f0SAndreas Färber static const TypeInfo pl190_info = { 282*7fc3266fSAndreas Färber .name = TYPE_PL190, 28339bffca2SAnthony Liguori .parent = TYPE_SYS_BUS_DEVICE, 284aefbc256SAndreas Färber .instance_size = sizeof(PL190State), 285999e12bbSAnthony Liguori .class_init = pl190_class_init, 286ac49d750SPeter Maydell }; 287ac49d750SPeter Maydell 28883f7d43aSAndreas Färber static void pl190_register_types(void) 28997aff481SPaul Brook { 29039bffca2SAnthony Liguori type_register_static(&pl190_info); 29197aff481SPaul Brook } 29297aff481SPaul Brook 29383f7d43aSAndreas Färber type_init(pl190_register_types) 294