qemu/hw/intc/imx_avic.c
<<
>>
Prefs
   1/*
   2 * i.MX31 Vectored Interrupt Controller
   3 *
   4 * Note this is NOT the PL192 provided by ARM, but
   5 * a custom implementation by Freescale.
   6 *
   7 * Copyright (c) 2008 OKL
   8 * Copyright (c) 2011 NICTA Pty Ltd
   9 * Originally written by Hans Jiang
  10 * Updated by Jean-Christophe Dubois <jcd@tribudubois.net>
  11 *
  12 * This code is licensed under the GPL version 2 or later.  See
  13 * the COPYING file in the top-level directory.
  14 *
  15 * TODO: implement vectors.
  16 */
  17
  18#include "qemu/osdep.h"
  19#include "hw/intc/imx_avic.h"
  20#include "hw/irq.h"
  21#include "migration/vmstate.h"
  22#include "qemu/log.h"
  23#include "qemu/module.h"
  24
  25#ifndef DEBUG_IMX_AVIC
  26#define DEBUG_IMX_AVIC 0
  27#endif
  28
  29#define DPRINTF(fmt, args...) \
  30    do { \
  31        if (DEBUG_IMX_AVIC) { \
  32            fprintf(stderr, "[%s]%s: " fmt , TYPE_IMX_AVIC, \
  33                                             __func__, ##args); \
  34        } \
  35    } while (0)
  36
  37static const VMStateDescription vmstate_imx_avic = {
  38    .name = TYPE_IMX_AVIC,
  39    .version_id = 1,
  40    .minimum_version_id = 1,
  41    .fields = (VMStateField[]) {
  42        VMSTATE_UINT64(pending, IMXAVICState),
  43        VMSTATE_UINT64(enabled, IMXAVICState),
  44        VMSTATE_UINT64(is_fiq, IMXAVICState),
  45        VMSTATE_UINT32(intcntl, IMXAVICState),
  46        VMSTATE_UINT32(intmask, IMXAVICState),
  47        VMSTATE_UINT32_ARRAY(prio, IMXAVICState, PRIO_WORDS),
  48        VMSTATE_END_OF_LIST()
  49    },
  50};
  51
  52static inline int imx_avic_prio(IMXAVICState *s, int irq)
  53{
  54    uint32_t word = irq / PRIO_PER_WORD;
  55    uint32_t part = 4 * (irq % PRIO_PER_WORD);
  56    return 0xf & (s->prio[word] >> part);
  57}
  58
  59/* Update interrupts.  */
  60static void imx_avic_update(IMXAVICState *s)
  61{
  62    int i;
  63    uint64_t new = s->pending & s->enabled;
  64    uint64_t flags;
  65
  66    flags = new & s->is_fiq;
  67    qemu_set_irq(s->fiq, !!flags);
  68
  69    flags = new & ~s->is_fiq;
  70    if (!flags || (s->intmask == 0x1f)) {
  71        qemu_set_irq(s->irq, !!flags);
  72        return;
  73    }
  74
  75    /*
  76     * Take interrupt if there's a pending interrupt with
  77     * priority higher than the value of intmask
  78     */
  79    for (i = 0; i < IMX_AVIC_NUM_IRQS; i++) {
  80        if (flags & (1UL << i)) {
  81            if (imx_avic_prio(s, i) > s->intmask) {
  82                qemu_set_irq(s->irq, 1);
  83                return;
  84            }
  85        }
  86    }
  87    qemu_set_irq(s->irq, 0);
  88}
  89
  90static void imx_avic_set_irq(void *opaque, int irq, int level)
  91{
  92    IMXAVICState *s = (IMXAVICState *)opaque;
  93
  94    if (level) {
  95        DPRINTF("Raising IRQ %d, prio %d\n",
  96                irq, imx_avic_prio(s, irq));
  97        s->pending |= (1ULL << irq);
  98    } else {
  99        DPRINTF("Clearing IRQ %d, prio %d\n",
 100                irq, imx_avic_prio(s, irq));
 101        s->pending &= ~(1ULL << irq);
 102    }
 103
 104    imx_avic_update(s);
 105}
 106
 107
 108static uint64_t imx_avic_read(void *opaque,
 109                             hwaddr offset, unsigned size)
 110{
 111    IMXAVICState *s = (IMXAVICState *)opaque;
 112
 113    DPRINTF("read(offset = 0x%" HWADDR_PRIx ")\n", offset);
 114
 115    switch (offset >> 2) {
 116    case 0: /* INTCNTL */
 117        return s->intcntl;
 118
 119    case 1: /* Normal Interrupt Mask Register, NIMASK */
 120        return s->intmask;
 121
 122    case 2: /* Interrupt Enable Number Register, INTENNUM */
 123    case 3: /* Interrupt Disable Number Register, INTDISNUM */
 124        return 0;
 125
 126    case 4: /* Interrupt Enabled Number Register High */
 127        return s->enabled >> 32;
 128
 129    case 5: /* Interrupt Enabled Number Register Low */
 130        return s->enabled & 0xffffffffULL;
 131
 132    case 6: /* Interrupt Type Register High */
 133        return s->is_fiq >> 32;
 134
 135    case 7: /* Interrupt Type Register Low */
 136        return s->is_fiq & 0xffffffffULL;
 137
 138    case 8: /* Normal Interrupt Priority Register 7 */
 139    case 9: /* Normal Interrupt Priority Register 6 */
 140    case 10:/* Normal Interrupt Priority Register 5 */
 141    case 11:/* Normal Interrupt Priority Register 4 */
 142    case 12:/* Normal Interrupt Priority Register 3 */
 143    case 13:/* Normal Interrupt Priority Register 2 */
 144    case 14:/* Normal Interrupt Priority Register 1 */
 145    case 15:/* Normal Interrupt Priority Register 0 */
 146        return s->prio[15-(offset>>2)];
 147
 148    case 16: /* Normal interrupt vector and status register */
 149    {
 150        /*
 151         * This returns the highest priority
 152         * outstanding interrupt.  Where there is more than
 153         * one pending IRQ with the same priority,
 154         * take the highest numbered one.
 155         */
 156        uint64_t flags = s->pending & s->enabled & ~s->is_fiq;
 157        int i;
 158        int prio = -1;
 159        int irq = -1;
 160        for (i = 63; i >= 0; --i) {
 161            if (flags & (1ULL<<i)) {
 162                int irq_prio = imx_avic_prio(s, i);
 163                if (irq_prio > prio) {
 164                    irq = i;
 165                    prio = irq_prio;
 166                }
 167            }
 168        }
 169        if (irq >= 0) {
 170            imx_avic_set_irq(s, irq, 0);
 171            return irq << 16 | prio;
 172        }
 173        return 0xffffffffULL;
 174    }
 175    case 17:/* Fast Interrupt vector and status register */
 176    {
 177        uint64_t flags = s->pending & s->enabled & s->is_fiq;
 178        int i = ctz64(flags);
 179        if (i < 64) {
 180            imx_avic_set_irq(opaque, i, 0);
 181            return i;
 182        }
 183        return 0xffffffffULL;
 184    }
 185    case 18:/* Interrupt source register high */
 186        return s->pending >> 32;
 187
 188    case 19:/* Interrupt source register low */
 189        return s->pending & 0xffffffffULL;
 190
 191    case 20:/* Interrupt Force Register high */
 192    case 21:/* Interrupt Force Register low */
 193        return 0;
 194
 195    case 22:/* Normal Interrupt Pending Register High */
 196        return (s->pending & s->enabled & ~s->is_fiq) >> 32;
 197
 198    case 23:/* Normal Interrupt Pending Register Low */
 199        return (s->pending & s->enabled & ~s->is_fiq) & 0xffffffffULL;
 200
 201    case 24: /* Fast Interrupt Pending Register High  */
 202        return (s->pending & s->enabled & s->is_fiq) >> 32;
 203
 204    case 25: /* Fast Interrupt Pending Register Low  */
 205        return (s->pending & s->enabled & s->is_fiq) & 0xffffffffULL;
 206
 207    case 0x40:            /* AVIC vector 0, use for WFI WAR */
 208        return 0x4;
 209
 210    default:
 211        qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad register at offset 0x%"
 212                      HWADDR_PRIx "\n", TYPE_IMX_AVIC, __func__, offset);
 213        return 0;
 214    }
 215}
 216
 217static void imx_avic_write(void *opaque, hwaddr offset,
 218                          uint64_t val, unsigned size)
 219{
 220    IMXAVICState *s = (IMXAVICState *)opaque;
 221
 222    /* Vector Registers not yet supported */
 223    if (offset >= 0x100 && offset <= 0x2fc) {
 224        qemu_log_mask(LOG_UNIMP, "[%s]%s: vector %d ignored\n",
 225                      TYPE_IMX_AVIC, __func__, (int)((offset - 0x100) >> 2));
 226        return;
 227    }
 228
 229    DPRINTF("(0x%" HWADDR_PRIx ") = 0x%x\n", offset, (unsigned int)val);
 230
 231    switch (offset >> 2) {
 232    case 0: /* Interrupt Control Register, INTCNTL */
 233        s->intcntl = val & (ABFEN | NIDIS | FIDIS | NIAD | FIAD | NM);
 234        if (s->intcntl & ABFEN) {
 235            s->intcntl &= ~(val & ABFLAG);
 236        }
 237        break;
 238
 239    case 1: /* Normal Interrupt Mask Register, NIMASK */
 240        s->intmask = val & 0x1f;
 241        break;
 242
 243    case 2: /* Interrupt Enable Number Register, INTENNUM */
 244        DPRINTF("enable(%d)\n", (int)val);
 245        val &= 0x3f;
 246        s->enabled |= (1ULL << val);
 247        break;
 248
 249    case 3: /* Interrupt Disable Number Register, INTDISNUM */
 250        DPRINTF("disable(%d)\n", (int)val);
 251        val &= 0x3f;
 252        s->enabled &= ~(1ULL << val);
 253        break;
 254
 255    case 4: /* Interrupt Enable Number Register High */
 256        s->enabled = (s->enabled & 0xffffffffULL) | (val << 32);
 257        break;
 258
 259    case 5: /* Interrupt Enable Number Register Low */
 260        s->enabled = (s->enabled & 0xffffffff00000000ULL) | val;
 261        break;
 262
 263    case 6: /* Interrupt Type Register High */
 264        s->is_fiq = (s->is_fiq & 0xffffffffULL) | (val << 32);
 265        break;
 266
 267    case 7: /* Interrupt Type Register Low */
 268        s->is_fiq = (s->is_fiq & 0xffffffff00000000ULL) | val;
 269        break;
 270
 271    case 8: /* Normal Interrupt Priority Register 7 */
 272    case 9: /* Normal Interrupt Priority Register 6 */
 273    case 10:/* Normal Interrupt Priority Register 5 */
 274    case 11:/* Normal Interrupt Priority Register 4 */
 275    case 12:/* Normal Interrupt Priority Register 3 */
 276    case 13:/* Normal Interrupt Priority Register 2 */
 277    case 14:/* Normal Interrupt Priority Register 1 */
 278    case 15:/* Normal Interrupt Priority Register 0 */
 279        s->prio[15-(offset>>2)] = val;
 280        break;
 281
 282        /* Read-only registers, writes ignored */
 283    case 16:/* Normal Interrupt Vector and Status register */
 284    case 17:/* Fast Interrupt vector and status register */
 285    case 18:/* Interrupt source register high */
 286    case 19:/* Interrupt source register low */
 287        return;
 288
 289    case 20:/* Interrupt Force Register high */
 290        s->pending = (s->pending & 0xffffffffULL) | (val << 32);
 291        break;
 292
 293    case 21:/* Interrupt Force Register low */
 294        s->pending = (s->pending & 0xffffffff00000000ULL) | val;
 295        break;
 296
 297    case 22:/* Normal Interrupt Pending Register High */
 298    case 23:/* Normal Interrupt Pending Register Low */
 299    case 24: /* Fast Interrupt Pending Register High  */
 300    case 25: /* Fast Interrupt Pending Register Low  */
 301        return;
 302
 303    default:
 304        qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad register at offset 0x%"
 305                      HWADDR_PRIx "\n", TYPE_IMX_AVIC, __func__, offset);
 306    }
 307    imx_avic_update(s);
 308}
 309
 310static const MemoryRegionOps imx_avic_ops = {
 311    .read = imx_avic_read,
 312    .write = imx_avic_write,
 313    .endianness = DEVICE_NATIVE_ENDIAN,
 314};
 315
 316static void imx_avic_reset(DeviceState *dev)
 317{
 318    IMXAVICState *s = IMX_AVIC(dev);
 319
 320    s->pending = 0;
 321    s->enabled = 0;
 322    s->is_fiq = 0;
 323    s->intmask = 0x1f;
 324    s->intcntl = 0;
 325    memset(s->prio, 0, sizeof s->prio);
 326}
 327
 328static void imx_avic_init(Object *obj)
 329{
 330    DeviceState *dev = DEVICE(obj);
 331    IMXAVICState *s = IMX_AVIC(obj);
 332    SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
 333
 334    memory_region_init_io(&s->iomem, obj, &imx_avic_ops, s,
 335                          TYPE_IMX_AVIC, 0x1000);
 336    sysbus_init_mmio(sbd, &s->iomem);
 337
 338    qdev_init_gpio_in(dev, imx_avic_set_irq, IMX_AVIC_NUM_IRQS);
 339    sysbus_init_irq(sbd, &s->irq);
 340    sysbus_init_irq(sbd, &s->fiq);
 341}
 342
 343
 344static void imx_avic_class_init(ObjectClass *klass, void *data)
 345{
 346    DeviceClass *dc = DEVICE_CLASS(klass);
 347
 348    dc->vmsd = &vmstate_imx_avic;
 349    dc->reset = imx_avic_reset;
 350    dc->desc = "i.MX Advanced Vector Interrupt Controller";
 351}
 352
 353static const TypeInfo imx_avic_info = {
 354    .name = TYPE_IMX_AVIC,
 355    .parent = TYPE_SYS_BUS_DEVICE,
 356    .instance_size = sizeof(IMXAVICState),
 357    .instance_init = imx_avic_init,
 358    .class_init = imx_avic_class_init,
 359};
 360
 361static void imx_avic_register_types(void)
 362{
 363    type_register_static(&imx_avic_info);
 364}
 365
 366type_init(imx_avic_register_types)
 367