qemu/hw/intc/nios2_vic.c
<<
>>
Prefs
   1/*
   2 * Vectored Interrupt Controller for nios2 processor
   3 *
   4 * Copyright (c) 2022 Neuroblade
   5 *
   6 * Interface:
   7 * QOM property "cpu": link to the Nios2 CPU (must be set)
   8 * Unnamed GPIO inputs 0..NIOS2_VIC_MAX_IRQ-1: input IRQ lines
   9 * IRQ should be connected to nios2 IRQ0.
  10 *
  11 * Reference: "Embedded Peripherals IP User Guide
  12 *             for Intel® Quartus® Prime Design Suite: 21.4"
  13 * Chapter 38 "Vectored Interrupt Controller Core"
  14 * See: https://www.intel.com/content/www/us/en/docs/programmable/683130/21-4/vectored-interrupt-controller-core.html
  15 *
  16 * Permission is hereby granted, free of charge, to any person obtaining a copy
  17 * of this software and associated documentation files (the "Software"), to deal
  18 * in the Software without restriction, including without limitation the rights
  19 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  20 * copies of the Software, and to permit persons to whom the Software is
  21 * furnished to do so, subject to the following conditions:
  22 *
  23 * The above copyright notice and this permission notice shall be included in
  24 * all copies or substantial portions of the Software.
  25 *
  26 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  27 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  28 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
  29 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  30 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  31 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  32 * THE SOFTWARE.
  33 */
  34
  35#include "qemu/osdep.h"
  36
  37#include "hw/irq.h"
  38#include "hw/qdev-properties.h"
  39#include "hw/sysbus.h"
  40#include "migration/vmstate.h"
  41#include "qapi/error.h"
  42#include "qemu/bitops.h"
  43#include "qemu/log.h"
  44#include "qom/object.h"
  45#include "hw/intc/nios2_vic.h"
  46#include "cpu.h"
  47
  48
  49enum {
  50    INT_CONFIG0 = 0,
  51    INT_CONFIG31 = 31,
  52    INT_ENABLE = 32,
  53    INT_ENABLE_SET = 33,
  54    INT_ENABLE_CLR = 34,
  55    INT_PENDING = 35,
  56    INT_RAW_STATUS = 36,
  57    SW_INTERRUPT = 37,
  58    SW_INTERRUPT_SET = 38,
  59    SW_INTERRUPT_CLR = 39,
  60    VIC_CONFIG = 40,
  61    VIC_STATUS = 41,
  62    VEC_TBL_BASE = 42,
  63    VEC_TBL_ADDR = 43,
  64    CSR_COUNT /* Last! */
  65};
  66
  67/* Requested interrupt level (INT_CONFIG[0:5]) */
  68static inline uint32_t vic_int_config_ril(const Nios2VIC *vic, int irq_num)
  69{
  70    return extract32(vic->int_config[irq_num], 0, 6);
  71}
  72
  73/* Requested NMI (INT_CONFIG[6]) */
  74static inline uint32_t vic_int_config_rnmi(const Nios2VIC *vic, int irq_num)
  75{
  76    return extract32(vic->int_config[irq_num], 6, 1);
  77}
  78
  79/* Requested register set (INT_CONFIG[7:12]) */
  80static inline uint32_t vic_int_config_rrs(const Nios2VIC *vic, int irq_num)
  81{
  82    return extract32(vic->int_config[irq_num], 7, 6);
  83}
  84
  85static inline uint32_t vic_config_vec_size(const Nios2VIC *vic)
  86{
  87    return 1 << (2 + extract32(vic->vic_config, 0, 3));
  88}
  89
  90static inline uint32_t vic_int_pending(const Nios2VIC *vic)
  91{
  92    return (vic->int_raw_status | vic->sw_int) & vic->int_enable;
  93}
  94
  95static void vic_update_irq(Nios2VIC *vic)
  96{
  97    Nios2CPU *cpu = NIOS2_CPU(vic->cpu);
  98    uint32_t pending = vic_int_pending(vic);
  99    int irq = -1;
 100    int max_ril = 0;
 101    /* Note that if RIL is 0 for an interrupt it is effectively disabled */
 102
 103    vic->vec_tbl_addr = 0;
 104    vic->vic_status = 0;
 105
 106    if (pending == 0) {
 107        qemu_irq_lower(vic->output_int);
 108        return;
 109    }
 110
 111    for (int i = 0; i < NIOS2_VIC_MAX_IRQ; i++) {
 112        if (pending & BIT(i)) {
 113            int ril = vic_int_config_ril(vic, i);
 114            if (ril > max_ril) {
 115                irq = i;
 116                max_ril = ril;
 117            }
 118        }
 119    }
 120
 121    if (irq < 0) {
 122        qemu_irq_lower(vic->output_int);
 123        return;
 124    }
 125
 126    vic->vec_tbl_addr = irq * vic_config_vec_size(vic) + vic->vec_tbl_base;
 127    vic->vic_status = irq | BIT(31);
 128
 129    /*
 130     * In hardware, the interface between the VIC and the CPU is via the
 131     * External Interrupt Controller interface, where the interrupt controller
 132     * presents the CPU with a packet of data containing:
 133     *  - Requested Handler Address (RHA): 32 bits
 134     *  - Requested Register Set (RRS) : 6 bits
 135     *  - Requested Interrupt Level (RIL) : 6 bits
 136     *  - Requested NMI flag (RNMI) : 1 bit
 137     * In our emulation, we implement this by writing the data directly to
 138     * fields in the CPU object and then raising the IRQ line to tell
 139     * the CPU that we've done so.
 140     */
 141
 142    cpu->rha = vic->vec_tbl_addr;
 143    cpu->ril = max_ril;
 144    cpu->rrs = vic_int_config_rrs(vic, irq);
 145    cpu->rnmi = vic_int_config_rnmi(vic, irq);
 146
 147    qemu_irq_raise(vic->output_int);
 148}
 149
 150static void vic_set_irq(void *opaque, int irq_num, int level)
 151{
 152    Nios2VIC *vic = opaque;
 153
 154    vic->int_raw_status = deposit32(vic->int_raw_status, irq_num, 1, !!level);
 155    vic_update_irq(vic);
 156}
 157
 158static void nios2_vic_reset(DeviceState *dev)
 159{
 160    Nios2VIC *vic = NIOS2_VIC(dev);
 161
 162    memset(&vic->int_config, 0, sizeof(vic->int_config));
 163    vic->vic_config = 0;
 164    vic->int_raw_status = 0;
 165    vic->int_enable = 0;
 166    vic->sw_int = 0;
 167    vic->vic_status = 0;
 168    vic->vec_tbl_base = 0;
 169    vic->vec_tbl_addr = 0;
 170}
 171
 172static uint64_t nios2_vic_csr_read(void *opaque, hwaddr offset, unsigned size)
 173{
 174    Nios2VIC *vic = opaque;
 175    int index = offset / 4;
 176
 177    switch (index) {
 178    case INT_CONFIG0 ... INT_CONFIG31:
 179        return vic->int_config[index - INT_CONFIG0];
 180    case INT_ENABLE:
 181        return vic->int_enable;
 182    case INT_PENDING:
 183        return vic_int_pending(vic);
 184    case INT_RAW_STATUS:
 185        return vic->int_raw_status;
 186    case SW_INTERRUPT:
 187        return vic->sw_int;
 188    case VIC_CONFIG:
 189        return vic->vic_config;
 190    case VIC_STATUS:
 191        return vic->vic_status;
 192    case VEC_TBL_BASE:
 193        return vic->vec_tbl_base;
 194    case VEC_TBL_ADDR:
 195        return vic->vec_tbl_addr;
 196    default:
 197        return 0;
 198    }
 199}
 200
 201static void nios2_vic_csr_write(void *opaque, hwaddr offset, uint64_t value,
 202                                unsigned size)
 203{
 204    Nios2VIC *vic = opaque;
 205    int index = offset / 4;
 206
 207    switch (index) {
 208    case INT_CONFIG0 ... INT_CONFIG31:
 209        vic->int_config[index - INT_CONFIG0] = value;
 210        break;
 211    case INT_ENABLE:
 212        vic->int_enable = value;
 213        break;
 214    case INT_ENABLE_SET:
 215        vic->int_enable |= value;
 216        break;
 217    case INT_ENABLE_CLR:
 218        vic->int_enable &= ~value;
 219        break;
 220    case SW_INTERRUPT:
 221        vic->sw_int = value;
 222        break;
 223    case SW_INTERRUPT_SET:
 224        vic->sw_int |= value;
 225        break;
 226    case SW_INTERRUPT_CLR:
 227        vic->sw_int &= ~value;
 228        break;
 229    case VIC_CONFIG:
 230        vic->vic_config = value;
 231        break;
 232    case VEC_TBL_BASE:
 233        vic->vec_tbl_base = value;
 234        break;
 235    default:
 236        qemu_log_mask(LOG_GUEST_ERROR,
 237                      "nios2-vic: write to invalid CSR address %#"
 238                      HWADDR_PRIx "\n", offset);
 239    }
 240
 241    vic_update_irq(vic);
 242}
 243
 244static const MemoryRegionOps nios2_vic_csr_ops = {
 245    .read = nios2_vic_csr_read,
 246    .write = nios2_vic_csr_write,
 247    .endianness = DEVICE_LITTLE_ENDIAN,
 248    .valid = { .min_access_size = 4, .max_access_size = 4 }
 249};
 250
 251static void nios2_vic_realize(DeviceState *dev, Error **errp)
 252{
 253    Nios2VIC *vic = NIOS2_VIC(dev);
 254
 255    if (!vic->cpu) {
 256        /* This is a programming error in the code using this device */
 257        error_setg(errp, "nios2-vic 'cpu' link property was not set");
 258        return;
 259    }
 260
 261    sysbus_init_irq(SYS_BUS_DEVICE(dev), &vic->output_int);
 262    qdev_init_gpio_in(dev, vic_set_irq, NIOS2_VIC_MAX_IRQ);
 263
 264    memory_region_init_io(&vic->csr, OBJECT(dev), &nios2_vic_csr_ops, vic,
 265                          "nios2.vic.csr", CSR_COUNT * sizeof(uint32_t));
 266    sysbus_init_mmio(SYS_BUS_DEVICE(dev), &vic->csr);
 267}
 268
 269static Property nios2_vic_properties[] = {
 270    DEFINE_PROP_LINK("cpu", Nios2VIC, cpu, TYPE_CPU, CPUState *),
 271    DEFINE_PROP_END_OF_LIST()
 272};
 273
 274static const VMStateDescription nios2_vic_vmstate = {
 275    .name = "nios2-vic",
 276    .version_id = 1,
 277    .minimum_version_id = 1,
 278    .fields = (VMStateField[]){
 279        VMSTATE_UINT32_ARRAY(int_config, Nios2VIC, 32),
 280        VMSTATE_UINT32(vic_config, Nios2VIC),
 281        VMSTATE_UINT32(int_raw_status, Nios2VIC),
 282        VMSTATE_UINT32(int_enable, Nios2VIC),
 283        VMSTATE_UINT32(sw_int, Nios2VIC),
 284        VMSTATE_UINT32(vic_status, Nios2VIC),
 285        VMSTATE_UINT32(vec_tbl_base, Nios2VIC),
 286        VMSTATE_UINT32(vec_tbl_addr, Nios2VIC),
 287        VMSTATE_END_OF_LIST()
 288    },
 289};
 290
 291static void nios2_vic_class_init(ObjectClass *klass, void *data)
 292{
 293    DeviceClass *dc = DEVICE_CLASS(klass);
 294
 295    dc->reset = nios2_vic_reset;
 296    dc->realize = nios2_vic_realize;
 297    dc->vmsd = &nios2_vic_vmstate;
 298    device_class_set_props(dc, nios2_vic_properties);
 299}
 300
 301static const TypeInfo nios2_vic_info = {
 302    .name = TYPE_NIOS2_VIC,
 303    .parent = TYPE_SYS_BUS_DEVICE,
 304    .instance_size = sizeof(Nios2VIC),
 305    .class_init = nios2_vic_class_init,
 306};
 307
 308static void nios2_vic_register_types(void)
 309{
 310    type_register_static(&nios2_vic_info);
 311}
 312
 313type_init(nios2_vic_register_types);
 314