qemu/hw/intc/loongson_liointc.c
<<
>>
Prefs
   1/*
   2 * QEMU Loongson Local I/O interrupt controler.
   3 *
   4 * Copyright (c) 2020 Huacai Chen <chenhc@lemote.com>
   5 * Copyright (c) 2020 Jiaxun Yang <jiaxun.yang@flygoat.com>
   6 *
   7 * This program is free software: you can redistribute it and/or modify
   8 * it under the terms of the GNU General Public License as published by
   9 * the Free Software Foundation, either version 2 of the License, or
  10 * (at your option) any later version.
  11 *
  12 * This program is distributed in the hope that it will be useful,
  13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15 * GNU General Public License for more details.
  16 *
  17 * You should have received a copy of the GNU General Public License
  18 * along with this program. If not, see <https://www.gnu.org/licenses/>.
  19 *
  20 */
  21
  22#include "qemu/osdep.h"
  23#include "qemu/module.h"
  24#include "qemu/log.h"
  25#include "hw/irq.h"
  26#include "hw/qdev-properties.h"
  27#include "hw/intc/loongson_liointc.h"
  28
  29#define NUM_IRQS                32
  30
  31#define NUM_CORES               4
  32#define NUM_IPS                 4
  33#define NUM_PARENTS             (NUM_CORES * NUM_IPS)
  34#define PARENT_COREx_IPy(x, y)  (NUM_IPS * x + y)
  35
  36#define R_MAPPER_START          0x0
  37#define R_MAPPER_END            0x20
  38#define R_ISR                   R_MAPPER_END
  39#define R_IEN                   0x24
  40#define R_IEN_SET               0x28
  41#define R_IEN_CLR               0x2c
  42#define R_ISR_SIZE              0x8
  43#define R_START                 0x40
  44#define R_END                   (R_START + R_ISR_SIZE * NUM_CORES)
  45
  46struct loongson_liointc {
  47    SysBusDevice parent_obj;
  48
  49    MemoryRegion mmio;
  50    qemu_irq parent_irq[NUM_PARENTS];
  51
  52    uint8_t mapper[NUM_IRQS]; /* 0:3 for core, 4:7 for IP */
  53    uint32_t isr;
  54    uint32_t ien;
  55    uint32_t per_core_isr[NUM_CORES];
  56
  57    /* state of the interrupt input pins */
  58    uint32_t pin_state;
  59    bool parent_state[NUM_PARENTS];
  60};
  61
  62static void update_irq(struct loongson_liointc *p)
  63{
  64    uint32_t irq, core, ip;
  65    uint32_t per_ip_isr[NUM_IPS] = {0};
  66
  67    /* level triggered interrupt */
  68    p->isr = p->pin_state;
  69
  70    /* Clear disabled IRQs */
  71    p->isr &= p->ien;
  72
  73    /* Clear per_core_isr */
  74    for (core = 0; core < NUM_CORES; core++) {
  75        p->per_core_isr[core] = 0;
  76    }
  77
  78    /* Update per_core_isr and per_ip_isr */
  79    for (irq = 0; irq < NUM_IRQS; irq++) {
  80        if (!(p->isr & (1 << irq))) {
  81            continue;
  82        }
  83
  84        for (core = 0; core < NUM_CORES; core++) {
  85            if ((p->mapper[irq] & (1 << core))) {
  86                p->per_core_isr[core] |= (1 << irq);
  87            }
  88        }
  89
  90        for (ip = 0; ip < NUM_IPS; ip++) {
  91            if ((p->mapper[irq] & (1 << (ip + 4)))) {
  92                per_ip_isr[ip] |= (1 << irq);
  93            }
  94        }
  95    }
  96
  97    /* Emit IRQ to parent! */
  98    for (core = 0; core < NUM_CORES; core++) {
  99        for (ip = 0; ip < NUM_IPS; ip++) {
 100            int parent = PARENT_COREx_IPy(core, ip);
 101            if (p->parent_state[parent] !=
 102                (!!p->per_core_isr[core] && !!per_ip_isr[ip])) {
 103                p->parent_state[parent] = !p->parent_state[parent];
 104                qemu_set_irq(p->parent_irq[parent], p->parent_state[parent]);
 105            }
 106        }
 107    }
 108}
 109
 110static uint64_t
 111liointc_read(void *opaque, hwaddr addr, unsigned int size)
 112{
 113    struct loongson_liointc *p = opaque;
 114    uint32_t r = 0;
 115
 116    /* Mapper is 1 byte */
 117    if (size == 1 && addr < R_MAPPER_END) {
 118        r = p->mapper[addr];
 119        goto out;
 120    }
 121
 122    /* Rest are 4 bytes */
 123    if (size != 4 || (addr % 4)) {
 124        goto out;
 125    }
 126
 127    if (addr >= R_START && addr < R_END) {
 128        hwaddr offset = addr - R_START;
 129        int core = offset / R_ISR_SIZE;
 130
 131        if (offset % R_ISR_SIZE) {
 132            goto out;
 133        }
 134        r = p->per_core_isr[core];
 135        goto out;
 136    }
 137
 138    switch (addr) {
 139    case R_ISR:
 140        r = p->isr;
 141        break;
 142    case R_IEN:
 143        r = p->ien;
 144        break;
 145    default:
 146        break;
 147    }
 148
 149out:
 150    qemu_log_mask(CPU_LOG_INT, "%s: size=%d, addr=%"HWADDR_PRIx", val=%x\n",
 151                  __func__, size, addr, r);
 152    return r;
 153}
 154
 155static void
 156liointc_write(void *opaque, hwaddr addr,
 157          uint64_t val64, unsigned int size)
 158{
 159    struct loongson_liointc *p = opaque;
 160    uint32_t value = val64;
 161
 162    qemu_log_mask(CPU_LOG_INT, "%s: size=%d, addr=%"HWADDR_PRIx", val=%x\n",
 163                  __func__, size, addr, value);
 164
 165    /* Mapper is 1 byte */
 166    if (size == 1 && addr < R_MAPPER_END) {
 167        p->mapper[addr] = value;
 168        goto out;
 169    }
 170
 171    /* Rest are 4 bytes */
 172    if (size != 4 || (addr % 4)) {
 173        goto out;
 174    }
 175
 176    if (addr >= R_START && addr < R_END) {
 177        hwaddr offset = addr - R_START;
 178        int core = offset / R_ISR_SIZE;
 179
 180        if (offset % R_ISR_SIZE) {
 181            goto out;
 182        }
 183        p->per_core_isr[core] = value;
 184        goto out;
 185    }
 186
 187    switch (addr) {
 188    case R_IEN_SET:
 189        p->ien |= value;
 190        break;
 191    case R_IEN_CLR:
 192        p->ien &= ~value;
 193        break;
 194    default:
 195        break;
 196    }
 197
 198out:
 199    update_irq(p);
 200}
 201
 202static const MemoryRegionOps pic_ops = {
 203    .read = liointc_read,
 204    .write = liointc_write,
 205    .endianness = DEVICE_NATIVE_ENDIAN,
 206    .valid = {
 207        .min_access_size = 1,
 208        .max_access_size = 4
 209    }
 210};
 211
 212static void irq_handler(void *opaque, int irq, int level)
 213{
 214    struct loongson_liointc *p = opaque;
 215
 216    p->pin_state &= ~(1 << irq);
 217    p->pin_state |= level << irq;
 218    update_irq(p);
 219}
 220
 221static void loongson_liointc_init(Object *obj)
 222{
 223    struct loongson_liointc *p = LOONGSON_LIOINTC(obj);
 224    int i;
 225
 226    qdev_init_gpio_in(DEVICE(obj), irq_handler, 32);
 227
 228    for (i = 0; i < NUM_PARENTS; i++) {
 229        sysbus_init_irq(SYS_BUS_DEVICE(obj), &p->parent_irq[i]);
 230    }
 231
 232    memory_region_init_io(&p->mmio, obj, &pic_ops, p,
 233                         TYPE_LOONGSON_LIOINTC, R_END);
 234    sysbus_init_mmio(SYS_BUS_DEVICE(obj), &p->mmio);
 235}
 236
 237static const TypeInfo loongson_liointc_info = {
 238    .name          = TYPE_LOONGSON_LIOINTC,
 239    .parent        = TYPE_SYS_BUS_DEVICE,
 240    .instance_size = sizeof(struct loongson_liointc),
 241    .instance_init = loongson_liointc_init,
 242};
 243
 244static void loongson_liointc_register_types(void)
 245{
 246    type_register_static(&loongson_liointc_info);
 247}
 248
 249type_init(loongson_liointc_register_types)
 250