1 /* 2 * Arm PrimeCell PL190 Vector Interrupt Controller 3 * 4 * Copyright (c) 2006 CodeSourcery. 5 * Written by Paul Brook 6 * 7 * This code is licensed under the GPL. 8 */ 9 10 #include "qemu/osdep.h" 11 #include "hw/sysbus.h" 12 13 /* The number of virtual priority levels. 16 user vectors plus the 14 unvectored IRQ. Chained interrupts would require an additional level 15 if implemented. */ 16 17 #define PL190_NUM_PRIO 17 18 19 #define TYPE_PL190 "pl190" 20 #define PL190(obj) OBJECT_CHECK(PL190State, (obj), TYPE_PL190) 21 22 typedef struct PL190State { 23 SysBusDevice parent_obj; 24 25 MemoryRegion iomem; 26 uint32_t level; 27 uint32_t soft_level; 28 uint32_t irq_enable; 29 uint32_t fiq_select; 30 uint8_t vect_control[16]; 31 uint32_t vect_addr[PL190_NUM_PRIO]; 32 /* Mask containing interrupts with higher priority than this one. */ 33 uint32_t prio_mask[PL190_NUM_PRIO + 1]; 34 int protected; 35 /* Current priority level. */ 36 int priority; 37 int prev_prio[PL190_NUM_PRIO]; 38 qemu_irq irq; 39 qemu_irq fiq; 40 } PL190State; 41 42 static const unsigned char pl190_id[] = 43 { 0x90, 0x11, 0x04, 0x00, 0x0D, 0xf0, 0x05, 0xb1 }; 44 45 static inline uint32_t pl190_irq_level(PL190State *s) 46 { 47 return (s->level | s->soft_level) & s->irq_enable & ~s->fiq_select; 48 } 49 50 /* Update interrupts. */ 51 static void pl190_update(PL190State *s) 52 { 53 uint32_t level = pl190_irq_level(s); 54 int set; 55 56 set = (level & s->prio_mask[s->priority]) != 0; 57 qemu_set_irq(s->irq, set); 58 set = ((s->level | s->soft_level) & s->fiq_select) != 0; 59 qemu_set_irq(s->fiq, set); 60 } 61 62 static void pl190_set_irq(void *opaque, int irq, int level) 63 { 64 PL190State *s = (PL190State *)opaque; 65 66 if (level) 67 s->level |= 1u << irq; 68 else 69 s->level &= ~(1u << irq); 70 pl190_update(s); 71 } 72 73 static void pl190_update_vectors(PL190State *s) 74 { 75 uint32_t mask; 76 int i; 77 int n; 78 79 mask = 0; 80 for (i = 0; i < 16; i++) 81 { 82 s->prio_mask[i] = mask; 83 if (s->vect_control[i] & 0x20) 84 { 85 n = s->vect_control[i] & 0x1f; 86 mask |= 1 << n; 87 } 88 } 89 s->prio_mask[16] = mask; 90 pl190_update(s); 91 } 92 93 static uint64_t pl190_read(void *opaque, hwaddr offset, 94 unsigned size) 95 { 96 PL190State *s = (PL190State *)opaque; 97 int i; 98 99 if (offset >= 0xfe0 && offset < 0x1000) { 100 return pl190_id[(offset - 0xfe0) >> 2]; 101 } 102 if (offset >= 0x100 && offset < 0x140) { 103 return s->vect_addr[(offset - 0x100) >> 2]; 104 } 105 if (offset >= 0x200 && offset < 0x240) { 106 return s->vect_control[(offset - 0x200) >> 2]; 107 } 108 switch (offset >> 2) { 109 case 0: /* IRQSTATUS */ 110 return pl190_irq_level(s); 111 case 1: /* FIQSATUS */ 112 return (s->level | s->soft_level) & s->fiq_select; 113 case 2: /* RAWINTR */ 114 return s->level | s->soft_level; 115 case 3: /* INTSELECT */ 116 return s->fiq_select; 117 case 4: /* INTENABLE */ 118 return s->irq_enable; 119 case 6: /* SOFTINT */ 120 return s->soft_level; 121 case 8: /* PROTECTION */ 122 return s->protected; 123 case 12: /* VECTADDR */ 124 /* Read vector address at the start of an ISR. Increases the 125 * current priority level to that of the current interrupt. 126 * 127 * Since an enabled interrupt X at priority P causes prio_mask[Y] 128 * to have bit X set for all Y > P, this loop will stop with 129 * i == the priority of the highest priority set interrupt. 130 */ 131 for (i = 0; i < s->priority; i++) { 132 if ((s->level | s->soft_level) & s->prio_mask[i + 1]) { 133 break; 134 } 135 } 136 137 /* Reading this value with no pending interrupts is undefined. 138 We return the default address. */ 139 if (i == PL190_NUM_PRIO) 140 return s->vect_addr[16]; 141 if (i < s->priority) 142 { 143 s->prev_prio[i] = s->priority; 144 s->priority = i; 145 pl190_update(s); 146 } 147 return s->vect_addr[s->priority]; 148 case 13: /* DEFVECTADDR */ 149 return s->vect_addr[16]; 150 default: 151 qemu_log_mask(LOG_GUEST_ERROR, 152 "pl190_read: Bad offset %x\n", (int)offset); 153 return 0; 154 } 155 } 156 157 static void pl190_write(void *opaque, hwaddr offset, 158 uint64_t val, unsigned size) 159 { 160 PL190State *s = (PL190State *)opaque; 161 162 if (offset >= 0x100 && offset < 0x140) { 163 s->vect_addr[(offset - 0x100) >> 2] = val; 164 pl190_update_vectors(s); 165 return; 166 } 167 if (offset >= 0x200 && offset < 0x240) { 168 s->vect_control[(offset - 0x200) >> 2] = val; 169 pl190_update_vectors(s); 170 return; 171 } 172 switch (offset >> 2) { 173 case 0: /* SELECT */ 174 /* This is a readonly register, but linux tries to write to it 175 anyway. Ignore the write. */ 176 break; 177 case 3: /* INTSELECT */ 178 s->fiq_select = val; 179 break; 180 case 4: /* INTENABLE */ 181 s->irq_enable |= val; 182 break; 183 case 5: /* INTENCLEAR */ 184 s->irq_enable &= ~val; 185 break; 186 case 6: /* SOFTINT */ 187 s->soft_level |= val; 188 break; 189 case 7: /* SOFTINTCLEAR */ 190 s->soft_level &= ~val; 191 break; 192 case 8: /* PROTECTION */ 193 /* TODO: Protection (supervisor only access) is not implemented. */ 194 s->protected = val & 1; 195 break; 196 case 12: /* VECTADDR */ 197 /* Restore the previous priority level. The value written is 198 ignored. */ 199 if (s->priority < PL190_NUM_PRIO) 200 s->priority = s->prev_prio[s->priority]; 201 break; 202 case 13: /* DEFVECTADDR */ 203 s->vect_addr[16] = val; 204 break; 205 case 0xc0: /* ITCR */ 206 if (val) { 207 qemu_log_mask(LOG_UNIMP, "pl190: Test mode not implemented\n"); 208 } 209 break; 210 default: 211 qemu_log_mask(LOG_GUEST_ERROR, 212 "pl190_write: Bad offset %x\n", (int)offset); 213 return; 214 } 215 pl190_update(s); 216 } 217 218 static const MemoryRegionOps pl190_ops = { 219 .read = pl190_read, 220 .write = pl190_write, 221 .endianness = DEVICE_NATIVE_ENDIAN, 222 }; 223 224 static void pl190_reset(DeviceState *d) 225 { 226 PL190State *s = PL190(d); 227 int i; 228 229 for (i = 0; i < 16; i++) { 230 s->vect_addr[i] = 0; 231 s->vect_control[i] = 0; 232 } 233 s->vect_addr[16] = 0; 234 s->prio_mask[17] = 0xffffffff; 235 s->priority = PL190_NUM_PRIO; 236 pl190_update_vectors(s); 237 } 238 239 static int pl190_init(SysBusDevice *sbd) 240 { 241 DeviceState *dev = DEVICE(sbd); 242 PL190State *s = PL190(dev); 243 244 memory_region_init_io(&s->iomem, OBJECT(s), &pl190_ops, s, "pl190", 0x1000); 245 sysbus_init_mmio(sbd, &s->iomem); 246 qdev_init_gpio_in(dev, pl190_set_irq, 32); 247 sysbus_init_irq(sbd, &s->irq); 248 sysbus_init_irq(sbd, &s->fiq); 249 return 0; 250 } 251 252 static const VMStateDescription vmstate_pl190 = { 253 .name = "pl190", 254 .version_id = 1, 255 .minimum_version_id = 1, 256 .fields = (VMStateField[]) { 257 VMSTATE_UINT32(level, PL190State), 258 VMSTATE_UINT32(soft_level, PL190State), 259 VMSTATE_UINT32(irq_enable, PL190State), 260 VMSTATE_UINT32(fiq_select, PL190State), 261 VMSTATE_UINT8_ARRAY(vect_control, PL190State, 16), 262 VMSTATE_UINT32_ARRAY(vect_addr, PL190State, PL190_NUM_PRIO), 263 VMSTATE_UINT32_ARRAY(prio_mask, PL190State, PL190_NUM_PRIO+1), 264 VMSTATE_INT32(protected, PL190State), 265 VMSTATE_INT32(priority, PL190State), 266 VMSTATE_INT32_ARRAY(prev_prio, PL190State, PL190_NUM_PRIO), 267 VMSTATE_END_OF_LIST() 268 } 269 }; 270 271 static void pl190_class_init(ObjectClass *klass, void *data) 272 { 273 DeviceClass *dc = DEVICE_CLASS(klass); 274 SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); 275 276 k->init = pl190_init; 277 dc->reset = pl190_reset; 278 dc->vmsd = &vmstate_pl190; 279 } 280 281 static const TypeInfo pl190_info = { 282 .name = TYPE_PL190, 283 .parent = TYPE_SYS_BUS_DEVICE, 284 .instance_size = sizeof(PL190State), 285 .class_init = pl190_class_init, 286 }; 287 288 static void pl190_register_types(void) 289 { 290 type_register_static(&pl190_info); 291 } 292 293 type_init(pl190_register_types) 294