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