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