qemu/hw/intc/bcm2836_control.c
<<
>>
Prefs
   1/*
   2 * Rasperry Pi 2 emulation ARM control logic module.
   3 * Copyright (c) 2015, Microsoft
   4 * Written by Andrew Baumann
   5 *
   6 * Based on bcm2835_ic.c (Raspberry Pi emulation) (c) 2012 Gregory Estrade
   7 * This code is licensed under the GNU GPLv2 and later.
   8 *
   9 * At present, only implements interrupt routing, and mailboxes (i.e.,
  10 * not PMU interrupt, or AXI counters).
  11 *
  12 * ARM Local Timer IRQ Copyright (c) 2019. Zoltán Baldaszti
  13 *
  14 * Ref:
  15 * https://www.raspberrypi.org/documentation/hardware/raspberrypi/bcm2836/QA7_rev3.4.pdf
  16 */
  17
  18#include "qemu/osdep.h"
  19#include "hw/intc/bcm2836_control.h"
  20#include "hw/irq.h"
  21#include "migration/vmstate.h"
  22#include "qemu/log.h"
  23#include "qemu/module.h"
  24
  25#define REG_GPU_ROUTE           0x0c
  26#define REG_LOCALTIMERROUTING   0x24
  27#define REG_LOCALTIMERCONTROL   0x34
  28#define REG_LOCALTIMERACK       0x38
  29#define REG_TIMERCONTROL        0x40
  30#define REG_MBOXCONTROL         0x50
  31#define REG_IRQSRC              0x60
  32#define REG_FIQSRC              0x70
  33#define REG_MBOX0_WR            0x80
  34#define REG_MBOX0_RDCLR         0xc0
  35#define REG_LIMIT              0x100
  36
  37#define IRQ_BIT(cntrl, num) (((cntrl) & (1 << (num))) != 0)
  38#define FIQ_BIT(cntrl, num) (((cntrl) & (1 << ((num) + 4))) != 0)
  39
  40#define IRQ_CNTPSIRQ    0
  41#define IRQ_CNTPNSIRQ   1
  42#define IRQ_CNTHPIRQ    2
  43#define IRQ_CNTVIRQ     3
  44#define IRQ_MAILBOX0    4
  45#define IRQ_MAILBOX1    5
  46#define IRQ_MAILBOX2    6
  47#define IRQ_MAILBOX3    7
  48#define IRQ_GPU         8
  49#define IRQ_PMU         9
  50#define IRQ_AXI         10
  51#define IRQ_TIMER       11
  52#define IRQ_MAX         IRQ_TIMER
  53
  54#define LOCALTIMER_FREQ      38400000
  55#define LOCALTIMER_INTFLAG   (1 << 31)
  56#define LOCALTIMER_RELOAD    (1 << 30)
  57#define LOCALTIMER_INTENABLE (1 << 29)
  58#define LOCALTIMER_ENABLE    (1 << 28)
  59#define LOCALTIMER_VALUE(x)  ((x) & 0xfffffff)
  60
  61static void deliver_local(BCM2836ControlState *s, uint8_t core, uint8_t irq,
  62                          uint32_t controlreg, uint8_t controlidx)
  63{
  64    if (FIQ_BIT(controlreg, controlidx)) {
  65        /* deliver a FIQ */
  66        s->fiqsrc[core] |= (uint32_t)1 << irq;
  67    } else if (IRQ_BIT(controlreg, controlidx)) {
  68        /* deliver an IRQ */
  69        s->irqsrc[core] |= (uint32_t)1 << irq;
  70    } else {
  71        /* the interrupt is masked */
  72    }
  73}
  74
  75/* Update interrupts.  */
  76static void bcm2836_control_update(BCM2836ControlState *s)
  77{
  78    int i, j;
  79
  80    /* reset pending IRQs/FIQs */
  81    for (i = 0; i < BCM2836_NCORES; i++) {
  82        s->irqsrc[i] = s->fiqsrc[i] = 0;
  83    }
  84
  85    /* apply routing logic, update status regs */
  86    if (s->gpu_irq) {
  87        assert(s->route_gpu_irq < BCM2836_NCORES);
  88        s->irqsrc[s->route_gpu_irq] |= (uint32_t)1 << IRQ_GPU;
  89    }
  90
  91    if (s->gpu_fiq) {
  92        assert(s->route_gpu_fiq < BCM2836_NCORES);
  93        s->fiqsrc[s->route_gpu_fiq] |= (uint32_t)1 << IRQ_GPU;
  94    }
  95
  96    /*
  97     * handle the control module 'local timer' interrupt for one of the
  98     * cores' IRQ/FIQ;  this is distinct from the per-CPU timer
  99     * interrupts handled below.
 100     */
 101    if ((s->local_timer_control & LOCALTIMER_INTENABLE) &&
 102        (s->local_timer_control & LOCALTIMER_INTFLAG)) {
 103        if (s->route_localtimer & 4) {
 104            s->fiqsrc[(s->route_localtimer & 3)] |= (uint32_t)1 << IRQ_TIMER;
 105        } else {
 106            s->irqsrc[(s->route_localtimer & 3)] |= (uint32_t)1 << IRQ_TIMER;
 107        }
 108    }
 109
 110    for (i = 0; i < BCM2836_NCORES; i++) {
 111        /* handle local timer interrupts for this core */
 112        if (s->timerirqs[i]) {
 113            assert(s->timerirqs[i] < (1 << (IRQ_CNTVIRQ + 1))); /* sane mask? */
 114            for (j = 0; j <= IRQ_CNTVIRQ; j++) {
 115                if ((s->timerirqs[i] & (1 << j)) != 0) {
 116                    /* local interrupt j is set */
 117                    deliver_local(s, i, j, s->timercontrol[i], j);
 118                }
 119            }
 120        }
 121
 122        /* handle mailboxes for this core */
 123        for (j = 0; j < BCM2836_MBPERCORE; j++) {
 124            if (s->mailboxes[i * BCM2836_MBPERCORE + j] != 0) {
 125                /* mailbox j is set */
 126                deliver_local(s, i, j + IRQ_MAILBOX0, s->mailboxcontrol[i], j);
 127            }
 128        }
 129    }
 130
 131    /* call set_irq appropriately for each output */
 132    for (i = 0; i < BCM2836_NCORES; i++) {
 133        qemu_set_irq(s->irq[i], s->irqsrc[i] != 0);
 134        qemu_set_irq(s->fiq[i], s->fiqsrc[i] != 0);
 135    }
 136}
 137
 138static void bcm2836_control_set_local_irq(void *opaque, int core, int local_irq,
 139                                          int level)
 140{
 141    BCM2836ControlState *s = opaque;
 142
 143    assert(core >= 0 && core < BCM2836_NCORES);
 144    assert(local_irq >= 0 && local_irq <= IRQ_CNTVIRQ);
 145
 146    s->timerirqs[core] = deposit32(s->timerirqs[core], local_irq, 1, !!level);
 147
 148    bcm2836_control_update(s);
 149}
 150
 151/* XXX: the following wrapper functions are a kludgy workaround,
 152 * needed because I can't seem to pass useful information in the "irq"
 153 * parameter when using named interrupts. Feel free to clean this up!
 154 */
 155
 156static void bcm2836_control_set_local_irq0(void *opaque, int core, int level)
 157{
 158    bcm2836_control_set_local_irq(opaque, core, 0, level);
 159}
 160
 161static void bcm2836_control_set_local_irq1(void *opaque, int core, int level)
 162{
 163    bcm2836_control_set_local_irq(opaque, core, 1, level);
 164}
 165
 166static void bcm2836_control_set_local_irq2(void *opaque, int core, int level)
 167{
 168    bcm2836_control_set_local_irq(opaque, core, 2, level);
 169}
 170
 171static void bcm2836_control_set_local_irq3(void *opaque, int core, int level)
 172{
 173    bcm2836_control_set_local_irq(opaque, core, 3, level);
 174}
 175
 176static void bcm2836_control_set_gpu_irq(void *opaque, int irq, int level)
 177{
 178    BCM2836ControlState *s = opaque;
 179
 180    s->gpu_irq = level;
 181
 182    bcm2836_control_update(s);
 183}
 184
 185static void bcm2836_control_set_gpu_fiq(void *opaque, int irq, int level)
 186{
 187    BCM2836ControlState *s = opaque;
 188
 189    s->gpu_fiq = level;
 190
 191    bcm2836_control_update(s);
 192}
 193
 194static void bcm2836_control_local_timer_set_next(void *opaque)
 195{
 196    BCM2836ControlState *s = opaque;
 197    uint64_t next_event;
 198
 199    assert(LOCALTIMER_VALUE(s->local_timer_control) > 0);
 200
 201    next_event = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) +
 202        muldiv64(LOCALTIMER_VALUE(s->local_timer_control),
 203            NANOSECONDS_PER_SECOND, LOCALTIMER_FREQ);
 204    timer_mod(&s->timer, next_event);
 205}
 206
 207static void bcm2836_control_local_timer_tick(void *opaque)
 208{
 209    BCM2836ControlState *s = opaque;
 210
 211    bcm2836_control_local_timer_set_next(s);
 212
 213    s->local_timer_control |= LOCALTIMER_INTFLAG;
 214    bcm2836_control_update(s);
 215}
 216
 217static void bcm2836_control_local_timer_control(void *opaque, uint32_t val)
 218{
 219    BCM2836ControlState *s = opaque;
 220
 221    s->local_timer_control = val;
 222    if (val & LOCALTIMER_ENABLE) {
 223        bcm2836_control_local_timer_set_next(s);
 224    } else {
 225        timer_del(&s->timer);
 226    }
 227}
 228
 229static void bcm2836_control_local_timer_ack(void *opaque, uint32_t val)
 230{
 231    BCM2836ControlState *s = opaque;
 232
 233    if (val & LOCALTIMER_INTFLAG) {
 234        s->local_timer_control &= ~LOCALTIMER_INTFLAG;
 235    }
 236    if ((val & LOCALTIMER_RELOAD) &&
 237        (s->local_timer_control & LOCALTIMER_ENABLE)) {
 238            bcm2836_control_local_timer_set_next(s);
 239    }
 240}
 241
 242static uint64_t bcm2836_control_read(void *opaque, hwaddr offset, unsigned size)
 243{
 244    BCM2836ControlState *s = opaque;
 245
 246    if (offset == REG_GPU_ROUTE) {
 247        assert(s->route_gpu_fiq < BCM2836_NCORES
 248               && s->route_gpu_irq < BCM2836_NCORES);
 249        return ((uint32_t)s->route_gpu_fiq << 2) | s->route_gpu_irq;
 250    } else if (offset == REG_LOCALTIMERROUTING) {
 251        return s->route_localtimer;
 252    } else if (offset == REG_LOCALTIMERCONTROL) {
 253        return s->local_timer_control;
 254    } else if (offset == REG_LOCALTIMERACK) {
 255        return 0;
 256    } else if (offset >= REG_TIMERCONTROL && offset < REG_MBOXCONTROL) {
 257        return s->timercontrol[(offset - REG_TIMERCONTROL) >> 2];
 258    } else if (offset >= REG_MBOXCONTROL && offset < REG_IRQSRC) {
 259        return s->mailboxcontrol[(offset - REG_MBOXCONTROL) >> 2];
 260    } else if (offset >= REG_IRQSRC && offset < REG_FIQSRC) {
 261        return s->irqsrc[(offset - REG_IRQSRC) >> 2];
 262    } else if (offset >= REG_FIQSRC && offset < REG_MBOX0_WR) {
 263        return s->fiqsrc[(offset - REG_FIQSRC) >> 2];
 264    } else if (offset >= REG_MBOX0_RDCLR && offset < REG_LIMIT) {
 265        return s->mailboxes[(offset - REG_MBOX0_RDCLR) >> 2];
 266    } else {
 267        qemu_log_mask(LOG_UNIMP, "%s: Unsupported offset 0x%"HWADDR_PRIx"\n",
 268                      __func__, offset);
 269        return 0;
 270    }
 271}
 272
 273static void bcm2836_control_write(void *opaque, hwaddr offset,
 274                                  uint64_t val, unsigned size)
 275{
 276    BCM2836ControlState *s = opaque;
 277
 278    if (offset == REG_GPU_ROUTE) {
 279        s->route_gpu_irq = val & 0x3;
 280        s->route_gpu_fiq = (val >> 2) & 0x3;
 281    } else if (offset == REG_LOCALTIMERROUTING) {
 282        s->route_localtimer = val & 7;
 283    } else if (offset == REG_LOCALTIMERCONTROL) {
 284        bcm2836_control_local_timer_control(s, val);
 285    } else if (offset == REG_LOCALTIMERACK) {
 286        bcm2836_control_local_timer_ack(s, val);
 287    } else if (offset >= REG_TIMERCONTROL && offset < REG_MBOXCONTROL) {
 288        s->timercontrol[(offset - REG_TIMERCONTROL) >> 2] = val & 0xff;
 289    } else if (offset >= REG_MBOXCONTROL && offset < REG_IRQSRC) {
 290        s->mailboxcontrol[(offset - REG_MBOXCONTROL) >> 2] = val & 0xff;
 291    } else if (offset >= REG_MBOX0_WR && offset < REG_MBOX0_RDCLR) {
 292        s->mailboxes[(offset - REG_MBOX0_WR) >> 2] |= val;
 293    } else if (offset >= REG_MBOX0_RDCLR && offset < REG_LIMIT) {
 294        s->mailboxes[(offset - REG_MBOX0_RDCLR) >> 2] &= ~val;
 295    } else {
 296        qemu_log_mask(LOG_UNIMP, "%s: Unsupported offset 0x%"HWADDR_PRIx
 297                                 " value 0x%"PRIx64"\n",
 298                      __func__, offset, val);
 299        return;
 300    }
 301
 302    bcm2836_control_update(s);
 303}
 304
 305static const MemoryRegionOps bcm2836_control_ops = {
 306    .read = bcm2836_control_read,
 307    .write = bcm2836_control_write,
 308    .endianness = DEVICE_NATIVE_ENDIAN,
 309    .valid.min_access_size = 4,
 310    .valid.max_access_size = 4,
 311};
 312
 313static void bcm2836_control_reset(DeviceState *d)
 314{
 315    BCM2836ControlState *s = BCM2836_CONTROL(d);
 316    int i;
 317
 318    s->route_gpu_irq = s->route_gpu_fiq = 0;
 319
 320    timer_del(&s->timer);
 321    s->route_localtimer = 0;
 322    s->local_timer_control = 0;
 323
 324    for (i = 0; i < BCM2836_NCORES; i++) {
 325        s->timercontrol[i] = 0;
 326        s->mailboxcontrol[i] = 0;
 327    }
 328
 329    for (i = 0; i < BCM2836_NCORES * BCM2836_MBPERCORE; i++) {
 330        s->mailboxes[i] = 0;
 331    }
 332}
 333
 334static void bcm2836_control_init(Object *obj)
 335{
 336    BCM2836ControlState *s = BCM2836_CONTROL(obj);
 337    DeviceState *dev = DEVICE(obj);
 338
 339    memory_region_init_io(&s->iomem, obj, &bcm2836_control_ops, s,
 340                          TYPE_BCM2836_CONTROL, REG_LIMIT);
 341    sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->iomem);
 342
 343    /* inputs from each CPU core */
 344    qdev_init_gpio_in_named(dev, bcm2836_control_set_local_irq0, "cntpsirq",
 345                            BCM2836_NCORES);
 346    qdev_init_gpio_in_named(dev, bcm2836_control_set_local_irq1, "cntpnsirq",
 347                            BCM2836_NCORES);
 348    qdev_init_gpio_in_named(dev, bcm2836_control_set_local_irq2, "cnthpirq",
 349                            BCM2836_NCORES);
 350    qdev_init_gpio_in_named(dev, bcm2836_control_set_local_irq3, "cntvirq",
 351                            BCM2836_NCORES);
 352
 353    /* IRQ and FIQ inputs from upstream bcm2835 controller */
 354    qdev_init_gpio_in_named(dev, bcm2836_control_set_gpu_irq, "gpu-irq", 1);
 355    qdev_init_gpio_in_named(dev, bcm2836_control_set_gpu_fiq, "gpu-fiq", 1);
 356
 357    /* outputs to CPU cores */
 358    qdev_init_gpio_out_named(dev, s->irq, "irq", BCM2836_NCORES);
 359    qdev_init_gpio_out_named(dev, s->fiq, "fiq", BCM2836_NCORES);
 360
 361    /* create a qemu virtual timer */
 362    timer_init_ns(&s->timer, QEMU_CLOCK_VIRTUAL,
 363                  bcm2836_control_local_timer_tick, s);
 364}
 365
 366static const VMStateDescription vmstate_bcm2836_control = {
 367    .name = TYPE_BCM2836_CONTROL,
 368    .version_id = 2,
 369    .minimum_version_id = 1,
 370    .fields = (VMStateField[]) {
 371        VMSTATE_UINT32_ARRAY(mailboxes, BCM2836ControlState,
 372                             BCM2836_NCORES * BCM2836_MBPERCORE),
 373        VMSTATE_UINT8(route_gpu_irq, BCM2836ControlState),
 374        VMSTATE_UINT8(route_gpu_fiq, BCM2836ControlState),
 375        VMSTATE_UINT32_ARRAY(timercontrol, BCM2836ControlState, BCM2836_NCORES),
 376        VMSTATE_UINT32_ARRAY(mailboxcontrol, BCM2836ControlState,
 377                             BCM2836_NCORES),
 378        VMSTATE_TIMER_V(timer, BCM2836ControlState, 2),
 379        VMSTATE_UINT32_V(local_timer_control, BCM2836ControlState, 2),
 380        VMSTATE_UINT8_V(route_localtimer, BCM2836ControlState, 2),
 381        VMSTATE_END_OF_LIST()
 382    }
 383};
 384
 385static void bcm2836_control_class_init(ObjectClass *klass, void *data)
 386{
 387    DeviceClass *dc = DEVICE_CLASS(klass);
 388
 389    dc->reset = bcm2836_control_reset;
 390    dc->vmsd = &vmstate_bcm2836_control;
 391}
 392
 393static TypeInfo bcm2836_control_info = {
 394    .name          = TYPE_BCM2836_CONTROL,
 395    .parent        = TYPE_SYS_BUS_DEVICE,
 396    .instance_size = sizeof(BCM2836ControlState),
 397    .class_init    = bcm2836_control_class_init,
 398    .instance_init = bcm2836_control_init,
 399};
 400
 401static void bcm2836_control_register_types(void)
 402{
 403    type_register_static(&bcm2836_control_info);
 404}
 405
 406type_init(bcm2836_control_register_types)
 407