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 "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);
 364