qemu/hw/intc/grlib_irqmp.c
<<
>>
Prefs
   1/*
   2 * QEMU GRLIB IRQMP Emulator
   3 *
   4 * (Multiprocessor and extended interrupt not supported)
   5 *
   6 * Copyright (c) 2010-2019 AdaCore
   7 *
   8 * Permission is hereby granted, free of charge, to any person obtaining a copy
   9 * of this software and associated documentation files (the "Software"), to deal
  10 * in the Software without restriction, including without limitation the rights
  11 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  12 * copies of the Software, and to permit persons to whom the Software is
  13 * furnished to do so, subject to the following conditions:
  14 *
  15 * The above copyright notice and this permission notice shall be included in
  16 * all copies or substantial portions of the Software.
  17 *
  18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  19 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  20 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
  21 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  22 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  23 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  24 * THE SOFTWARE.
  25 */
  26
  27#include "qemu/osdep.h"
  28#include "hw/irq.h"
  29#include "hw/sysbus.h"
  30#include "cpu.h"
  31
  32#include "hw/qdev-properties.h"
  33#include "hw/sparc/grlib.h"
  34
  35#include "trace.h"
  36#include "qapi/error.h"
  37#include "qemu/module.h"
  38#include "qom/object.h"
  39
  40#define IRQMP_MAX_CPU 16
  41#define IRQMP_REG_SIZE 256      /* Size of memory mapped registers */
  42
  43/* Memory mapped register offsets */
  44#define LEVEL_OFFSET     0x00
  45#define PENDING_OFFSET   0x04
  46#define FORCE0_OFFSET    0x08
  47#define CLEAR_OFFSET     0x0C
  48#define MP_STATUS_OFFSET 0x10
  49#define BROADCAST_OFFSET 0x14
  50#define MASK_OFFSET      0x40
  51#define FORCE_OFFSET     0x80
  52#define EXTENDED_OFFSET  0xC0
  53
  54#define MAX_PILS 16
  55
  56OBJECT_DECLARE_SIMPLE_TYPE(IRQMP, GRLIB_IRQMP)
  57
  58typedef struct IRQMPState IRQMPState;
  59
  60struct IRQMP {
  61    SysBusDevice parent_obj;
  62
  63    MemoryRegion iomem;
  64
  65    IRQMPState *state;
  66    qemu_irq irq;
  67};
  68
  69struct IRQMPState {
  70    uint32_t level;
  71    uint32_t pending;
  72    uint32_t clear;
  73    uint32_t broadcast;
  74
  75    uint32_t mask[IRQMP_MAX_CPU];
  76    uint32_t force[IRQMP_MAX_CPU];
  77    uint32_t extended[IRQMP_MAX_CPU];
  78
  79    IRQMP    *parent;
  80};
  81
  82static void grlib_irqmp_check_irqs(IRQMPState *state)
  83{
  84    uint32_t      pend   = 0;
  85    uint32_t      level0 = 0;
  86    uint32_t      level1 = 0;
  87
  88    assert(state != NULL);
  89    assert(state->parent != NULL);
  90
  91    /* IRQ for CPU 0 (no SMP support) */
  92    pend = (state->pending | state->force[0])
  93        & state->mask[0];
  94
  95    level0 = pend & ~state->level;
  96    level1 = pend &  state->level;
  97
  98    trace_grlib_irqmp_check_irqs(state->pending, state->force[0],
  99                                 state->mask[0], level1, level0);
 100
 101    /* Trigger level1 interrupt first and level0 if there is no level1 */
 102    qemu_set_irq(state->parent->irq, level1 ?: level0);
 103}
 104
 105static void grlib_irqmp_ack_mask(IRQMPState *state, uint32_t mask)
 106{
 107    /* Clear registers */
 108    state->pending  &= ~mask;
 109    state->force[0] &= ~mask; /* Only CPU 0 (No SMP support) */
 110
 111    grlib_irqmp_check_irqs(state);
 112}
 113
 114void grlib_irqmp_ack(DeviceState *dev, int intno)
 115{
 116    IRQMP        *irqmp = GRLIB_IRQMP(dev);
 117    IRQMPState   *state;
 118    uint32_t      mask;
 119
 120    state = irqmp->state;
 121    assert(state != NULL);
 122
 123    intno &= 15;
 124    mask = 1 << intno;
 125
 126    trace_grlib_irqmp_ack(intno);
 127
 128    grlib_irqmp_ack_mask(state, mask);
 129}
 130
 131static void grlib_irqmp_set_irq(void *opaque, int irq, int level)
 132{
 133    IRQMP      *irqmp = GRLIB_IRQMP(opaque);
 134    IRQMPState *s;
 135    int         i = 0;
 136
 137    s = irqmp->state;
 138    assert(s         != NULL);
 139    assert(s->parent != NULL);
 140
 141
 142    if (level) {
 143        trace_grlib_irqmp_set_irq(irq);
 144
 145        if (s->broadcast & 1 << irq) {
 146            /* Broadcasted IRQ */
 147            for (i = 0; i < IRQMP_MAX_CPU; i++) {
 148                s->force[i] |= 1 << irq;
 149            }
 150        } else {
 151            s->pending |= 1 << irq;
 152        }
 153        grlib_irqmp_check_irqs(s);
 154
 155    }
 156}
 157
 158static uint64_t grlib_irqmp_read(void *opaque, hwaddr addr,
 159                                 unsigned size)
 160{
 161    IRQMP      *irqmp = opaque;
 162    IRQMPState *state;
 163
 164    assert(irqmp != NULL);
 165    state = irqmp->state;
 166    assert(state != NULL);
 167
 168    addr &= 0xff;
 169
 170    /* global registers */
 171    switch (addr) {
 172    case LEVEL_OFFSET:
 173        return state->level;
 174
 175    case PENDING_OFFSET:
 176        return state->pending;
 177
 178    case FORCE0_OFFSET:
 179        /* This register is an "alias" for the force register of CPU 0 */
 180        return state->force[0];
 181
 182    case CLEAR_OFFSET:
 183    case MP_STATUS_OFFSET:
 184        /* Always read as 0 */
 185        return 0;
 186
 187    case BROADCAST_OFFSET:
 188        return state->broadcast;
 189
 190    default:
 191        break;
 192    }
 193
 194    /* mask registers */
 195    if (addr >= MASK_OFFSET && addr < FORCE_OFFSET) {
 196        int cpu = (addr - MASK_OFFSET) / 4;
 197        assert(cpu >= 0 && cpu < IRQMP_MAX_CPU);
 198
 199        return state->mask[cpu];
 200    }
 201
 202    /* force registers */
 203    if (addr >= FORCE_OFFSET && addr < EXTENDED_OFFSET) {
 204        int cpu = (addr - FORCE_OFFSET) / 4;
 205        assert(cpu >= 0 && cpu < IRQMP_MAX_CPU);
 206
 207        return state->force[cpu];
 208    }
 209
 210    /* extended (not supported) */
 211    if (addr >= EXTENDED_OFFSET && addr < IRQMP_REG_SIZE) {
 212        int cpu = (addr - EXTENDED_OFFSET) / 4;
 213        assert(cpu >= 0 && cpu < IRQMP_MAX_CPU);
 214
 215        return state->extended[cpu];
 216    }
 217
 218    trace_grlib_irqmp_readl_unknown(addr);
 219    return 0;
 220}
 221
 222static void grlib_irqmp_write(void *opaque, hwaddr addr,
 223                              uint64_t value, unsigned size)
 224{
 225    IRQMP      *irqmp = opaque;
 226    IRQMPState *state;
 227
 228    assert(irqmp != NULL);
 229    state = irqmp->state;
 230    assert(state != NULL);
 231
 232    addr &= 0xff;
 233
 234    /* global registers */
 235    switch (addr) {
 236    case LEVEL_OFFSET:
 237        value &= 0xFFFF << 1; /* clean up the value */
 238        state->level = value;
 239        return;
 240
 241    case PENDING_OFFSET:
 242        /* Read Only */
 243        return;
 244
 245    case FORCE0_OFFSET:
 246        /* This register is an "alias" for the force register of CPU 0 */
 247
 248        value &= 0xFFFE; /* clean up the value */
 249        state->force[0] = value;
 250        grlib_irqmp_check_irqs(irqmp->state);
 251        return;
 252
 253    case CLEAR_OFFSET:
 254        value &= ~1; /* clean up the value */
 255        grlib_irqmp_ack_mask(state, value);
 256        return;
 257
 258    case MP_STATUS_OFFSET:
 259        /* Read Only (no SMP support) */
 260        return;
 261
 262    case BROADCAST_OFFSET:
 263        value &= 0xFFFE; /* clean up the value */
 264        state->broadcast = value;
 265        return;
 266
 267    default:
 268        break;
 269    }
 270
 271    /* mask registers */
 272    if (addr >= MASK_OFFSET && addr < FORCE_OFFSET) {
 273        int cpu = (addr - MASK_OFFSET) / 4;
 274        assert(cpu >= 0 && cpu < IRQMP_MAX_CPU);
 275
 276        value &= ~1; /* clean up the value */
 277        state->mask[cpu] = value;
 278        grlib_irqmp_check_irqs(irqmp->state);
 279        return;
 280    }
 281
 282    /* force registers */
 283    if (addr >= FORCE_OFFSET && addr < EXTENDED_OFFSET) {
 284        int cpu = (addr - FORCE_OFFSET) / 4;
 285        assert(cpu >= 0 && cpu < IRQMP_MAX_CPU);
 286
 287        uint32_t force = value & 0xFFFE;
 288        uint32_t clear = (value >> 16) & 0xFFFE;
 289        uint32_t old   = state->force[cpu];
 290
 291        state->force[cpu] = (old | force) & ~clear;
 292        grlib_irqmp_check_irqs(irqmp->state);
 293        return;
 294    }
 295
 296    /* extended (not supported) */
 297    if (addr >= EXTENDED_OFFSET && addr < IRQMP_REG_SIZE) {
 298        int cpu = (addr - EXTENDED_OFFSET) / 4;
 299        assert(cpu >= 0 && cpu < IRQMP_MAX_CPU);
 300
 301        value &= 0xF; /* clean up the value */
 302        state->extended[cpu] = value;
 303        return;
 304    }
 305
 306    trace_grlib_irqmp_writel_unknown(addr, value);
 307}
 308
 309static const MemoryRegionOps grlib_irqmp_ops = {
 310    .read = grlib_irqmp_read,
 311    .write = grlib_irqmp_write,
 312    .endianness = DEVICE_NATIVE_ENDIAN,
 313    .valid = {
 314        .min_access_size = 4,
 315        .max_access_size = 4,
 316    },
 317};
 318
 319static void grlib_irqmp_reset(DeviceState *d)
 320{
 321    IRQMP *irqmp = GRLIB_IRQMP(d);
 322    assert(irqmp->state != NULL);
 323
 324    memset(irqmp->state, 0, sizeof *irqmp->state);
 325    irqmp->state->parent = irqmp;
 326}
 327
 328static void grlib_irqmp_init(Object *obj)
 329{
 330    IRQMP *irqmp = GRLIB_IRQMP(obj);
 331    SysBusDevice *dev = SYS_BUS_DEVICE(obj);
 332
 333    qdev_init_gpio_in(DEVICE(obj), grlib_irqmp_set_irq, MAX_PILS);
 334    qdev_init_gpio_out_named(DEVICE(obj), &irqmp->irq, "grlib-irq", 1);
 335    memory_region_init_io(&irqmp->iomem, obj, &grlib_irqmp_ops, irqmp,
 336                          "irqmp", IRQMP_REG_SIZE);
 337
 338    irqmp->state = g_malloc0(sizeof *irqmp->state);
 339
 340    sysbus_init_mmio(dev, &irqmp->iomem);
 341}
 342
 343static void grlib_irqmp_class_init(ObjectClass *klass, void *data)
 344{
 345    DeviceClass *dc = DEVICE_CLASS(klass);
 346
 347    dc->reset = grlib_irqmp_reset;
 348}
 349
 350static const TypeInfo grlib_irqmp_info = {
 351    .name          = TYPE_GRLIB_IRQMP,
 352    .parent        = TYPE_SYS_BUS_DEVICE,
 353    .instance_size = sizeof(IRQMP),
 354    .instance_init = grlib_irqmp_init,
 355    .class_init    = grlib_irqmp_class_init,
 356};
 357
 358static void grlib_irqmp_register_types(void)
 359{
 360    type_register_static(&grlib_irqmp_info);
 361}
 362
 363type_init(grlib_irqmp_register_types)
 364