aspeed_vic.c (10661B)
1/* 2 * ASPEED Interrupt Controller (New) 3 * 4 * Andrew Jeffery <andrew@aj.id.au> 5 * 6 * Copyright 2015, 2016 IBM Corp. 7 * 8 * This code is licensed under the GPL version 2 or later. See 9 * the COPYING file in the top-level directory. 10 */ 11 12/* The hardware exposes two register sets, a legacy set and a 'new' set. The 13 * model implements the 'new' register set, and logs warnings on accesses to 14 * the legacy IO space. 15 * 16 * The hardware uses 32bit registers to manage 51 IRQs, with low and high 17 * registers for each conceptual register. The device model's implementation 18 * uses 64bit data types to store both low and high register values (in the one 19 * member), but must cope with access offset values in multiples of 4 passed to 20 * the callbacks. As such the read() and write() implementations process the 21 * provided offset to understand whether the access is requesting the lower or 22 * upper 32 bits of the 64bit member. 23 * 24 * Additionally, the "Interrupt Enable", "Edge Status" and "Software Interrupt" 25 * fields have separate "enable"/"status" and "clear" registers, where set bits 26 * are written to one or the other to change state (avoiding a 27 * read-modify-write sequence). 28 */ 29 30#include "qemu/osdep.h" 31#include "hw/intc/aspeed_vic.h" 32#include "hw/irq.h" 33#include "migration/vmstate.h" 34#include "qemu/bitops.h" 35#include "qemu/log.h" 36#include "qemu/module.h" 37#include "trace.h" 38 39#define AVIC_NEW_BASE_OFFSET 0x80 40 41#define AVIC_L_MASK 0xFFFFFFFFU 42#define AVIC_H_MASK 0x0007FFFFU 43#define AVIC_EVENT_W_MASK (0x78000ULL << 32) 44 45static void aspeed_vic_update(AspeedVICState *s) 46{ 47 uint64_t new = (s->raw & s->enable); 48 uint64_t flags; 49 50 flags = new & s->select; 51 trace_aspeed_vic_update_fiq(!!flags); 52 qemu_set_irq(s->fiq, !!flags); 53 54 flags = new & ~s->select; 55 trace_aspeed_vic_update_irq(!!flags); 56 qemu_set_irq(s->irq, !!flags); 57} 58 59static void aspeed_vic_set_irq(void *opaque, int irq, int level) 60{ 61 uint64_t irq_mask; 62 bool raise; 63 AspeedVICState *s = (AspeedVICState *)opaque; 64 65 if (irq > ASPEED_VIC_NR_IRQS) { 66 qemu_log_mask(LOG_GUEST_ERROR, "%s: Invalid interrupt number: %d\n", 67 __func__, irq); 68 return; 69 } 70 71 trace_aspeed_vic_set_irq(irq, level); 72 73 irq_mask = BIT(irq); 74 if (s->sense & irq_mask) { 75 /* level-triggered */ 76 if (s->event & irq_mask) { 77 /* high-sensitive */ 78 raise = level; 79 } else { 80 /* low-sensitive */ 81 raise = !level; 82 } 83 s->raw = deposit64(s->raw, irq, 1, raise); 84 } else { 85 uint64_t old_level = s->level & irq_mask; 86 87 /* edge-triggered */ 88 if (s->dual_edge & irq_mask) { 89 raise = (!!old_level) != (!!level); 90 } else { 91 if (s->event & irq_mask) { 92 /* rising-sensitive */ 93 raise = !old_level && level; 94 } else { 95 /* falling-sensitive */ 96 raise = old_level && !level; 97 } 98 } 99 if (raise) { 100 s->raw = deposit64(s->raw, irq, 1, raise); 101 } 102 } 103 s->level = deposit64(s->level, irq, 1, level); 104 aspeed_vic_update(s); 105} 106 107static uint64_t aspeed_vic_read(void *opaque, hwaddr offset, unsigned size) 108{ 109 AspeedVICState *s = (AspeedVICState *)opaque; 110 hwaddr n_offset; 111 uint64_t val; 112 bool high; 113 114 if (offset < AVIC_NEW_BASE_OFFSET) { 115 high = false; 116 n_offset = offset; 117 } else { 118 high = !!(offset & 0x4); 119 n_offset = (offset & ~0x4); 120 } 121 122 switch (n_offset) { 123 case 0x80: /* IRQ Status */ 124 case 0x00: 125 val = s->raw & ~s->select & s->enable; 126 break; 127 case 0x88: /* FIQ Status */ 128 case 0x04: 129 val = s->raw & s->select & s->enable; 130 break; 131 case 0x90: /* Raw Interrupt Status */ 132 case 0x08: 133 val = s->raw; 134 break; 135 case 0x98: /* Interrupt Selection */ 136 case 0x0c: 137 val = s->select; 138 break; 139 case 0xa0: /* Interrupt Enable */ 140 case 0x10: 141 val = s->enable; 142 break; 143 case 0xb0: /* Software Interrupt */ 144 case 0x18: 145 val = s->trigger; 146 break; 147 case 0xc0: /* Interrupt Sensitivity */ 148 case 0x24: 149 val = s->sense; 150 break; 151 case 0xc8: /* Interrupt Both Edge Trigger Control */ 152 case 0x28: 153 val = s->dual_edge; 154 break; 155 case 0xd0: /* Interrupt Event */ 156 case 0x2c: 157 val = s->event; 158 break; 159 case 0xe0: /* Edge Triggered Interrupt Status */ 160 val = s->raw & ~s->sense; 161 break; 162 /* Illegal */ 163 case 0xa8: /* Interrupt Enable Clear */ 164 case 0xb8: /* Software Interrupt Clear */ 165 case 0xd8: /* Edge Triggered Interrupt Clear */ 166 qemu_log_mask(LOG_GUEST_ERROR, 167 "%s: Read of write-only register with offset 0x%" 168 HWADDR_PRIx "\n", __func__, offset); 169 val = 0; 170 break; 171 default: 172 qemu_log_mask(LOG_GUEST_ERROR, 173 "%s: Bad register at offset 0x%" HWADDR_PRIx "\n", 174 __func__, offset); 175 val = 0; 176 break; 177 } 178 if (high) { 179 val = extract64(val, 32, 19); 180 } else { 181 val = extract64(val, 0, 32); 182 } 183 trace_aspeed_vic_read(offset, size, val); 184 return val; 185} 186 187static void aspeed_vic_write(void *opaque, hwaddr offset, uint64_t data, 188 unsigned size) 189{ 190 AspeedVICState *s = (AspeedVICState *)opaque; 191 hwaddr n_offset; 192 bool high; 193 194 if (offset < AVIC_NEW_BASE_OFFSET) { 195 high = false; 196 n_offset = offset; 197 } else { 198 high = !!(offset & 0x4); 199 n_offset = (offset & ~0x4); 200 } 201 202 trace_aspeed_vic_write(offset, size, data); 203 204 /* Given we have members using separate enable/clear registers, deposit64() 205 * isn't quite the tool for the job. Instead, relocate the incoming bits to 206 * the required bit offset based on the provided access address 207 */ 208 if (high) { 209 data &= AVIC_H_MASK; 210 data <<= 32; 211 } else { 212 data &= AVIC_L_MASK; 213 } 214 215 switch (n_offset) { 216 case 0x98: /* Interrupt Selection */ 217 case 0x0c: 218 /* Register has deposit64() semantics - overwrite requested 32 bits */ 219 if (high) { 220 s->select &= AVIC_L_MASK; 221 } else { 222 s->select &= ((uint64_t) AVIC_H_MASK) << 32; 223 } 224 s->select |= data; 225 break; 226 case 0xa0: /* Interrupt Enable */ 227 case 0x10: 228 s->enable |= data; 229 break; 230 case 0xa8: /* Interrupt Enable Clear */ 231 case 0x14: 232 s->enable &= ~data; 233 break; 234 case 0xb0: /* Software Interrupt */ 235 case 0x18: 236 qemu_log_mask(LOG_UNIMP, "%s: Software interrupts unavailable. " 237 "IRQs requested: 0x%016" PRIx64 "\n", __func__, data); 238 break; 239 case 0xb8: /* Software Interrupt Clear */ 240 case 0x1c: 241 qemu_log_mask(LOG_UNIMP, "%s: Software interrupts unavailable. " 242 "IRQs to be cleared: 0x%016" PRIx64 "\n", __func__, data); 243 break; 244 case 0xd0: /* Interrupt Event */ 245 /* Register has deposit64() semantics - overwrite the top four valid 246 * IRQ bits, as only the top four IRQs (GPIOs) can change their event 247 * type */ 248 if (high) { 249 s->event &= ~AVIC_EVENT_W_MASK; 250 s->event |= (data & AVIC_EVENT_W_MASK); 251 } else { 252 qemu_log_mask(LOG_GUEST_ERROR, 253 "Ignoring invalid write to interrupt event register"); 254 } 255 break; 256 case 0xd8: /* Edge Triggered Interrupt Clear */ 257 case 0x38: 258 s->raw &= ~(data & ~s->sense); 259 break; 260 case 0x80: /* IRQ Status */ 261 case 0x00: 262 case 0x88: /* FIQ Status */ 263 case 0x04: 264 case 0x90: /* Raw Interrupt Status */ 265 case 0x08: 266 case 0xc0: /* Interrupt Sensitivity */ 267 case 0x24: 268 case 0xc8: /* Interrupt Both Edge Trigger Control */ 269 case 0x28: 270 case 0xe0: /* Edge Triggered Interrupt Status */ 271 qemu_log_mask(LOG_GUEST_ERROR, 272 "%s: Write of read-only register with offset 0x%" 273 HWADDR_PRIx "\n", __func__, offset); 274 break; 275 276 default: 277 qemu_log_mask(LOG_GUEST_ERROR, 278 "%s: Bad register at offset 0x%" HWADDR_PRIx "\n", 279 __func__, offset); 280 break; 281 } 282 aspeed_vic_update(s); 283} 284 285static const MemoryRegionOps aspeed_vic_ops = { 286 .read = aspeed_vic_read, 287 .write = aspeed_vic_write, 288 .endianness = DEVICE_LITTLE_ENDIAN, 289 .valid.min_access_size = 4, 290 .valid.max_access_size = 4, 291 .valid.unaligned = false, 292}; 293 294static void aspeed_vic_reset(DeviceState *dev) 295{ 296 AspeedVICState *s = ASPEED_VIC(dev); 297 298 s->level = 0; 299 s->raw = 0; 300 s->select = 0; 301 s->enable = 0; 302 s->trigger = 0; 303 s->sense = 0x1F07FFF8FFFFULL; 304 s->dual_edge = 0xF800070000ULL; 305 s->event = 0x5F07FFF8FFFFULL; 306} 307 308#define AVIC_IO_REGION_SIZE 0x20000 309 310static void aspeed_vic_realize(DeviceState *dev, Error **errp) 311{ 312 SysBusDevice *sbd = SYS_BUS_DEVICE(dev); 313 AspeedVICState *s = ASPEED_VIC(dev); 314 315 memory_region_init_io(&s->iomem, OBJECT(s), &aspeed_vic_ops, s, 316 TYPE_ASPEED_VIC, AVIC_IO_REGION_SIZE); 317 318 sysbus_init_mmio(sbd, &s->iomem); 319 320 qdev_init_gpio_in(dev, aspeed_vic_set_irq, ASPEED_VIC_NR_IRQS); 321 sysbus_init_irq(sbd, &s->irq); 322 sysbus_init_irq(sbd, &s->fiq); 323} 324 325static const VMStateDescription vmstate_aspeed_vic = { 326 .name = "aspeed.new-vic", 327 .version_id = 1, 328 .minimum_version_id = 1, 329 .fields = (VMStateField[]) { 330 VMSTATE_UINT64(level, AspeedVICState), 331 VMSTATE_UINT64(raw, AspeedVICState), 332 VMSTATE_UINT64(select, AspeedVICState), 333 VMSTATE_UINT64(enable, AspeedVICState), 334 VMSTATE_UINT64(trigger, AspeedVICState), 335 VMSTATE_UINT64(sense, AspeedVICState), 336 VMSTATE_UINT64(dual_edge, AspeedVICState), 337 VMSTATE_UINT64(event, AspeedVICState), 338 VMSTATE_END_OF_LIST() 339 } 340}; 341 342static void aspeed_vic_class_init(ObjectClass *klass, void *data) 343{ 344 DeviceClass *dc = DEVICE_CLASS(klass); 345 dc->realize = aspeed_vic_realize; 346 dc->reset = aspeed_vic_reset; 347 dc->desc = "ASPEED Interrupt Controller (New)"; 348 dc->vmsd = &vmstate_aspeed_vic; 349} 350 351static const TypeInfo aspeed_vic_info = { 352 .name = TYPE_ASPEED_VIC, 353 .parent = TYPE_SYS_BUS_DEVICE, 354 .instance_size = sizeof(AspeedVICState), 355 .class_init = aspeed_vic_class_init, 356}; 357 358static void aspeed_vic_register_types(void) 359{ 360 type_register_static(&aspeed_vic_info); 361} 362 363type_init(aspeed_vic_register_types);