All of lore.kernel.org
 help / color / mirror / Atom feed
From: Alistair Francis <alistair23@gmail.com>
To: wangjunqiang <wangjunqiang@iscas.ac.cn>
Cc: liweiwei@iscas.ac.cn, "open list:RISC-V" <qemu-riscv@nongnu.org>,
	Bin Meng <bin.meng@windriver.com>,
	"qemu-devel@nongnu.org Developers" <qemu-devel@nongnu.org>,
	Alistair Francis <Alistair.Francis@wdc.com>,
	alapha23@gmail.com, Palmer Dabbelt <palmer@dabbelt.com>
Subject: Re: [RFC PATCH 2/5] hw/intc: Add Nuclei ECLIC device
Date: Mon, 10 May 2021 12:20:37 +1000	[thread overview]
Message-ID: <CAKmqyKPVYuESf0TQM8M-KE-72PftMeNCfmQLdEj4Siy9TZxuBA@mail.gmail.com> (raw)
In-Reply-To: <20210507081654.11056-3-wangjunqiang@iscas.ac.cn>

On Fri, May 7, 2021 at 11:24 PM wangjunqiang <wangjunqiang@iscas.ac.cn> wrote:
>
> This patch provides an implementation of Nuclei ECLIC Device.
> Nuclei processor core have been equipped with an Enhanced Core Local
> Interrupt Controller (ECLIC), which is optimized based on the RISC-V
> standard CLIC, to manage all interrupt sources.
>
> https://doc.nucleisys.com/nuclei_spec/isa/eclic.html

Hello,

There are patches on the QEMU list adding support for the CLIC. How
different is the ECLIC from the CLIC? Could you use the CLIC as a
starting point instead of implementing a new interrupt controller?

Alistair

> ---
>  hw/intc/Kconfig                |   3 +
>  hw/intc/meson.build            |   1 +
>  hw/intc/nuclei_eclic.c         | 437 +++++++++++++++++++++++++++++++++
>  include/hw/intc/nuclei_eclic.h | 115 +++++++++
>  4 files changed, 556 insertions(+)
>  create mode 100644 hw/intc/nuclei_eclic.c
>  create mode 100644 include/hw/intc/nuclei_eclic.h
>
> diff --git a/hw/intc/Kconfig b/hw/intc/Kconfig
> index f4694088a4..eab30f6ffd 100644
> --- a/hw/intc/Kconfig
> +++ b/hw/intc/Kconfig
> @@ -73,3 +73,6 @@ config GOLDFISH_PIC
>
>  config M68K_IRQC
>      bool
> +
> +config NUCLEI_ECLIC
> +    bool
> diff --git a/hw/intc/meson.build b/hw/intc/meson.build
> index 1c299039f6..7577ba69d2 100644
> --- a/hw/intc/meson.build
> +++ b/hw/intc/meson.build
> @@ -50,6 +50,7 @@ specific_ss.add(when: 'CONFIG_S390_FLIC_KVM', if_true: files('s390_flic_kvm.c'))
>  specific_ss.add(when: 'CONFIG_SH_INTC', if_true: files('sh_intc.c'))
>  specific_ss.add(when: 'CONFIG_SIFIVE_CLINT', if_true: files('sifive_clint.c'))
>  specific_ss.add(when: 'CONFIG_SIFIVE_PLIC', if_true: files('sifive_plic.c'))
> +specific_ss.add(when: 'CONFIG_NUCLEI_ECLIC', if_true: files('nuclei_eclic.c'))
>  specific_ss.add(when: 'CONFIG_XICS', if_true: files('xics.c'))
>  specific_ss.add(when: ['CONFIG_KVM', 'CONFIG_XICS'],
>                 if_true: files('xics_kvm.c'))
> diff --git a/hw/intc/nuclei_eclic.c b/hw/intc/nuclei_eclic.c
> new file mode 100644
> index 0000000000..52de83cb1d
> --- /dev/null
> +++ b/hw/intc/nuclei_eclic.c
> @@ -0,0 +1,437 @@
> +/*
> + * NUCLEI ECLIC(Enhanced Core Local Interrupt Controller)
> + *
> + * Copyright (c) 2020 Gao ZhiYuan <alapha23@gmail.com>
> + * Copyright (c) 2020-2021 PLCT Lab.All rights reserved.
> + *
> + * This provides a parameterizable interrupt controller based on NucLei's ECLIC.
> + *
> + * This program is free software: you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation, either version 3 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + *  You should have received a copy of the GNU General Public License
> + *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#include "qemu/osdep.h"
> +#include "qemu/log.h"
> +#include "qemu/module.h"
> +#include "qemu/error-report.h"
> +#include "hw/sysbus.h"
> +#include "hw/pci/msi.h"
> +#include "hw/boards.h"
> +#include "hw/qdev-properties.h"
> +#include "target/riscv/cpu.h"
> +#include "sysemu/sysemu.h"
> +#include "hw/intc/nuclei_eclic.h"
> +#include "qapi/error.h"
> +
> +#define RISCV_DEBUG_ECLIC 0
> +
> +static void riscv_cpu_eclic_interrupt(RISCVCPU *cpu, int exccode)
> +{
> +    CPURISCVState *env = &cpu->env;
> +    bool locked = false;
> +
> +    env->exccode = exccode;
> +
> +    if (!qemu_mutex_iothread_locked()) {
> +        locked = true;
> +        qemu_mutex_lock_iothread();
> +    }
> +
> +    if (exccode != -1) {
> +        env->irq_pending = true;
> +        cpu_interrupt(CPU(cpu), CPU_INTERRUPT_ECLIC);
> +    } else {
> +        env->irq_pending = false;
> +        cpu_reset_interrupt(CPU(cpu), CPU_INTERRUPT_ECLIC);
> +    }
> +
> +    if (locked) {
> +        qemu_mutex_unlock_iothread();
> +    }
> +}
> +
> +static int level_compare(NucLeiECLICState *eclic,
> +                         ECLICPendingInterrupt *irq1,
> +                         ECLICPendingInterrupt *irq2)
> +{
> +    if (irq1->level == irq2->level) {
> +        if (irq1->prio == irq2->prio) {
> +            if (irq1->irq >= irq2->irq) {
> +                return 0;
> +            } else {
> +                return 1;
> +            }
> +        } else if (irq1->prio > irq2->level) {
> +            return 0;
> +        } else {
> +            return 1;
> +        }
> +    } else if (irq1->level > irq2->level) {
> +        return 0;
> +    } else {
> +        return 1;
> +    }
> +}
> +
> +static void nuclei_eclic_next_interrupt(void *eclic_ptr)
> +{
> +    RISCVCPU *cpu = RISCV_CPU(qemu_get_cpu(0));
> +    CPURISCVState *env = &cpu->env;
> +    NucLeiECLICState *eclic = (NucLeiECLICState *)eclic_ptr;
> +    ECLICPendingInterrupt *active;
> +    target_ulong mil;
> +    int shv;
> +
> +    QLIST_FOREACH(active, &eclic->pending_list, next)
> +    {
> +        if (active->enable) {
> +            mil = get_field(env->mintstatus, MINTSTATUS_MIL);
> +            if (active->level >= eclic->mth && active->level > mil) {
> +                shv = eclic->clicintattr[active->irq] & 0x1;
> +                eclic->active_count++;
> +                riscv_cpu_eclic_interrupt(cpu,
> +                                          (active->irq & 0xFFF) | (shv << 12) | (active->level << 13));
> +                return;
> +            }
> +        }
> +    }
> +}
> +
> +static void nuclei_eclic_update_intmth(NucLeiECLICState *eclic,
> +                                       int irq, int mth)
> +{
> +    eclic->mth = mth;
> +    nuclei_eclic_next_interrupt(eclic);
> +}
> +
> +static void update_eclic_int_info(NucLeiECLICState *eclic, int irq)
> +{
> +    int level_width = (eclic->cliccfg >> 1) & 0xF;
> +    if (level_width > CLICINTCTLBITS) {
> +        level_width = CLICINTCTLBITS;
> +    }
> +    int prio_width = CLICINTCTLBITS - level_width;
> +
> +    if (level_width == 0) {
> +        eclic->clicintlist[irq].level = 255;
> +    } else {
> +        eclic->clicintlist[irq].level = ((
> +                                             (eclic->clicintctl[irq] >> (8 - level_width)) &
> +                                             ~((char)0x80 >> (8 - level_width)))
> +                                         << (8 - level_width)) |
> +                                        (0xff >> level_width);
> +    }
> +
> +    if (prio_width == 0) {
> +        eclic->clicintlist[irq].prio = 0;
> +    } else {
> +        eclic->clicintlist[irq].prio =
> +            (eclic->clicintctl[irq] >> (8 - level_width)) &
> +            ~(0x80 >> (8 - prio_width));
> +    }
> +
> +    eclic->clicintlist[irq].enable = eclic->clicintie[irq] & 0x1;
> +    eclic->clicintlist[irq].trigger = (eclic->clicintattr[irq] >> 1) & 0x3;
> +}
> +
> +static void eclic_remove_pending_list(NucLeiECLICState *eclic, int irq)
> +{
> +    QLIST_REMOVE(&eclic->clicintlist[irq], next);
> +}
> +
> +static void eclic_insert_pending_list(NucLeiECLICState *eclic, int irq)
> +{
> +    ECLICPendingInterrupt *node;
> +    if (QLIST_EMPTY(&eclic->pending_list)) {
> +        QLIST_INSERT_HEAD(&eclic->pending_list, &eclic->clicintlist[irq], next);
> +    } else {
> +        QLIST_FOREACH(node, &eclic->pending_list, next)
> +        {
> +            if (level_compare(eclic, node, &eclic->clicintlist[irq])) {
> +                QLIST_INSERT_BEFORE(node, &eclic->clicintlist[irq], next);
> +                break;
> +            } else if (node->next.le_next == NULL) {
> +                QLIST_INSERT_AFTER(node, &eclic->clicintlist[irq], next);
> +                break;
> +            }
> +        }
> +    }
> +}
> +
> +static void nuclei_eclic_update_intip(NucLeiECLICState *eclic, int irq, int new_intip)
> +{
> +
> +    int old_intip = eclic->clicintlist[irq].sig;
> +    int trigger = (eclic->clicintattr[irq] >> 1) & 0x3;
> +    if (((trigger == 0) && new_intip) ||
> +        ((trigger == 1) && !old_intip && new_intip) ||
> +        ((trigger == 3) && old_intip && !new_intip)) {
> +        eclic->clicintip[irq] = 1;
> +        eclic->clicintlist[irq].sig = new_intip;
> +        eclic_insert_pending_list(eclic, irq);
> +    } else {
> +        if (eclic->clicintip[irq]) {
> +            eclic_remove_pending_list(eclic, irq);
> +        }
> +        eclic->clicintip[irq] = 0;
> +        eclic->clicintlist[irq].sig = new_intip;
> +    }
> +
> +    nuclei_eclic_next_interrupt(eclic);
> +}
> +
> +static void nuclei_eclic_update_intie(NucLeiECLICState *eclic,
> +                                      int irq, int new_intie)
> +{
> +    eclic->clicintie[irq] = new_intie;
> +    update_eclic_int_info(eclic, irq);
> +    nuclei_eclic_next_interrupt(eclic);
> +}
> +
> +static void nuclei_eclic_update_intattr(NucLeiECLICState *eclic,
> +                                        int irq, int new_intattr)
> +{
> +    eclic->clicintattr[irq] = new_intattr;
> +    update_eclic_int_info(eclic, irq);
> +    nuclei_eclic_next_interrupt(eclic);
> +}
> +
> +static void nuclei_eclic_update_intctl(NucLeiECLICState *eclic,
> +                                       int irq, int new_intctl)
> +{
> +    eclic->clicintctl[irq] = new_intctl;
> +    update_eclic_int_info(eclic, irq);
> +    nuclei_eclic_next_interrupt(eclic);
> +}
> +
> +qemu_irq nuclei_eclic_get_irq(DeviceState *dev, int irq)
> +{
> +    NucLeiECLICState *eclic = NUCLEI_ECLIC(dev);
> +    return eclic->irqs[irq];
> +}
> +
> +static uint64_t nuclei_eclic_read(void *opaque, hwaddr offset, unsigned size)
> +{
> +    NucLeiECLICState *eclic = NUCLEI_ECLIC(opaque);
> +    uint64_t value = 0;
> +    uint32_t id = 0;
> +    if (offset >= NUCLEI_ECLIC_REG_CLICINTIP_BASE) {
> +        if ((offset - 0x1000) % 4 == 0) {
> +            id = (offset - 0x1000) / 4;
> +        } else if ((offset - 0x1001) % 4 == 0) {
> +            id = (offset - 0x1001) / 4;
> +        } else if ((offset - 0x1002) % 4 == 0) {
> +            id = (offset - 0x1002) / 4;
> +        } else if ((offset - 0x1003) % 4 == 0) {
> +            id = (offset - 0x1003) / 4;
> +        }
> +        offset = offset - 4 * id;
> +    }
> +
> +    switch (offset) {
> +    case NUCLEI_ECLIC_REG_CLICCFG:
> +        value = eclic->cliccfg & 0xFF;
> +        break;
> +    case NUCLEI_ECLIC_REG_CLICINFO:
> +        value = (CLICINTCTLBITS << 21) & 0xFFFFFFFF;
> +        break;
> +    case NUCLEI_ECLIC_REG_MTH:
> +        value = eclic->mth & 0xFF;
> +        break;
> +    case NUCLEI_ECLIC_REG_CLICINTIP_BASE:
> +        value = eclic->clicintip[id] & 0xFF;
> +        break;
> +    case NUCLEI_ECLIC_REG_CLICINTIE_BASE:
> +        value = eclic->clicintie[id] & 0xFF;
> +        break;
> +    case NUCLEI_ECLIC_REG_CLICINTATTR_BASE:
> +        value = eclic->clicintattr[id] & 0xFF;
> +        break;
> +    case NUCLEI_ECLIC_REG_CLICINTCTL_BASE:
> +        value = eclic->clicintctl[id] & 0xFF;
> +        break;
> +    default:
> +        break;
> +    }
> +
> +    return value;
> +}
> +
> +static void nuclei_eclic_write(void *opaque, hwaddr offset, uint64_t value,
> +                               unsigned size)
> +{
> +    NucLeiECLICState *eclic = NUCLEI_ECLIC(opaque);
> +    uint32_t id = 0;
> +    if (offset >= NUCLEI_ECLIC_REG_CLICINTIP_BASE) {
> +
> +        if ((offset - 0x1000) % 4 == 0) {
> +            id = (offset - 0x1000) / 4;
> +        } else if ((offset - 0x1001) % 4 == 0) {
> +            id = (offset - 0x1001) / 4;
> +        } else if ((offset - 0x1002) % 4 == 0) {
> +            id = (offset - 0x1002) / 4;
> +        } else if ((offset - 0x1003) % 4 == 0) {
> +            id = (offset - 0x1003) / 4;
> +        }
> +        offset = offset - 4 * id;
> +    }
> +    switch (offset) {
> +    case NUCLEI_ECLIC_REG_CLICCFG:
> +        eclic->cliccfg = value & 0xFF;
> +        for (id = 0; id < eclic->num_sources; id++) {
> +            update_eclic_int_info(eclic, id);
> +        }
> +        break;
> +    case NUCLEI_ECLIC_REG_MTH:
> +        nuclei_eclic_update_intmth(eclic, id, value & 0xFF);
> +        break;
> +    case NUCLEI_ECLIC_REG_CLICINTIP_BASE:
> +        if ((eclic->clicintlist[id].trigger & 0x1) != 0) {
> +            if ((eclic->clicintip[id] == 0) && (value & 0x1) == 1) {
> +                eclic->clicintip[id] = 1;
> +                eclic_insert_pending_list(eclic, id);
> +            } else if ((eclic->clicintip[id] == 1) && (value & 0x1) == 0) {
> +                eclic->clicintip[id] = 0;
> +                eclic_remove_pending_list(eclic, id);
> +            }
> +        }
> +        nuclei_eclic_next_interrupt(eclic);
> +        break;
> +    case NUCLEI_ECLIC_REG_CLICINTIE_BASE:
> +        nuclei_eclic_update_intie(eclic, id, value & 0xFF);
> +        break;
> +    case NUCLEI_ECLIC_REG_CLICINTATTR_BASE:
> +        nuclei_eclic_update_intattr(eclic, id, value & 0xFF);
> +        break;
> +    case NUCLEI_ECLIC_REG_CLICINTCTL_BASE:
> +        nuclei_eclic_update_intctl(eclic, id, value & 0xFF);
> +        break;
> +    default:
> +        break;
> +    }
> +}
> +
> +static const MemoryRegionOps nuclei_eclic_ops = {
> +    .read = nuclei_eclic_read,
> +    .write = nuclei_eclic_write,
> +    .endianness = DEVICE_LITTLE_ENDIAN,
> +};
> +
> +void riscv_cpu_eclic_clean_pending(void *eclic_ptr, int irq)
> +{
> +    NucLeiECLICState *eclic = (NucLeiECLICState *)eclic_ptr;
> +    if ((eclic->clicintlist[irq].trigger & 0x1) != 0 && irq >= 0) {
> +        eclic->clicintip[irq] = 0;
> +        eclic_remove_pending_list(eclic, irq);
> +    }
> +}
> +
> +void riscv_cpu_eclic_get_next_interrupt(void *eclic_ptr)
> +{
> +    NucLeiECLICState *eclic = (NucLeiECLICState *)eclic_ptr;
> +    nuclei_eclic_next_interrupt(eclic);
> +}
> +
> +
> +
> +static void nuclei_eclic_irq_request(void *opaque, int id, int new_intip)
> +{
> +    NucLeiECLICState *eclic = NUCLEI_ECLIC(opaque);
> +    nuclei_eclic_update_intip(eclic, id, new_intip);
> +}
> +
> +static void nuclei_eclic_realize(DeviceState *dev, Error **errp)
> +{
> +    NucLeiECLICState *eclic = NUCLEI_ECLIC(dev);
> +    int id;
> +
> +    memory_region_init_io(&eclic->mmio, OBJECT(dev), &nuclei_eclic_ops, eclic,
> +                          TYPE_NUCLEI_ECLIC, eclic->aperture_size);
> +    sysbus_init_mmio(SYS_BUS_DEVICE(dev), &eclic->mmio);
> +
> +    eclic->clicintip = g_new0(uint8_t, eclic->num_sources);
> +    eclic->clicintlist = g_new0(ECLICPendingInterrupt, eclic->num_sources);
> +    eclic->clicintie = g_new0(uint8_t, eclic->num_sources);
> +    eclic->clicintattr = g_new0(uint8_t, eclic->num_sources);
> +    eclic->clicintctl = g_new0(uint8_t, eclic->num_sources);
> +    eclic->irqs = g_new0(qemu_irq, eclic->num_sources);
> +    QLIST_INIT(&eclic->pending_list);
> +    for (id = 0; id < eclic->num_sources; id++) {
> +        eclic->clicintlist[id].irq = id;
> +        update_eclic_int_info(eclic, id);
> +    }
> +    eclic->active_count = 0;
> +
> +    /* Init ECLIC IRQ */
> +    eclic->irqs[Internal_SysTimerSW_IRQn] =
> +        qemu_allocate_irq(nuclei_eclic_irq_request,
> +                          eclic, Internal_SysTimerSW_IRQn);
> +    eclic->irqs[Internal_SysTimer_IRQn] =
> +        qemu_allocate_irq(nuclei_eclic_irq_request,
> +                          eclic, Internal_SysTimer_IRQn);
> +
> +    for (id = Internal_Reserved_Max_IRQn; id < eclic->num_sources; id++) {
> +        eclic->irqs[id] = qemu_allocate_irq(nuclei_eclic_irq_request,
> +                                            eclic, id);
> +    }
> +
> +    RISCVCPU *cpu = RISCV_CPU(qemu_get_cpu(0));
> +    cpu->env.eclic = eclic;
> +}
> +
> +static Property nuclei_eclic_properties[] = {
> +    DEFINE_PROP_UINT32("aperture-size", NucLeiECLICState, aperture_size, 0),
> +    DEFINE_PROP_UINT32("num-sources", NucLeiECLICState, num_sources, 0),
> +    DEFINE_PROP_END_OF_LIST(),
> +};
> +
> +static void nuclei_eclic_class_init(ObjectClass *klass, void *data)
> +{
> +    DeviceClass *dc = DEVICE_CLASS(klass);
> +
> +    device_class_set_props(dc, nuclei_eclic_properties);
> +    dc->realize = nuclei_eclic_realize;
> +}
> +
> +static const TypeInfo nuclei_eclic_info = {
> +    .name = TYPE_NUCLEI_ECLIC,
> +    .parent = TYPE_SYS_BUS_DEVICE,
> +    .instance_size = sizeof(NucLeiECLICState),
> +    .class_init = nuclei_eclic_class_init,
> +};
> +
> +static void nuclei_eclic_register_types(void)
> +{
> +    type_register_static(&nuclei_eclic_info);
> +}
> +
> +type_init(nuclei_eclic_register_types);
> +
> +void nuclei_eclic_systimer_cb(DeviceState *dev)
> +{
> +    NucLeiECLICState *eclic = NUCLEI_ECLIC(dev);
> +    nuclei_eclic_irq_request(eclic, Internal_SysTimer_IRQn, 1);
> +}
> +
> +DeviceState *nuclei_eclic_create(hwaddr addr,
> +                                 uint32_t aperture_size, uint32_t num_sources)
> +{
> +    DeviceState *dev = qdev_new(TYPE_NUCLEI_ECLIC);
> +
> +    qdev_prop_set_uint32(dev, "aperture-size", aperture_size);
> +    qdev_prop_set_uint32(dev, "num-sources", num_sources);
> +
> +    sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal);
> +    sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, addr);
> +    return dev;
> +}
> diff --git a/include/hw/intc/nuclei_eclic.h b/include/hw/intc/nuclei_eclic.h
> new file mode 100644
> index 0000000000..18b25485b9
> --- /dev/null
> +++ b/include/hw/intc/nuclei_eclic.h
> @@ -0,0 +1,115 @@
> +/*
> + * NUCLEI ECLIC (Enhanced Core Local Interrupt Controller) interface
> + *
> + * Copyright (c) 2020 Gao ZhiYuan <alapha23@gmail.com>
> + * Copyright (c) 2020-2021 PLCT Lab.All rights reserved.
> + *
> + * This provides a parameterizable interrupt controller based on NucLei's ECLIC.
> + *
> + * This program is free software: you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation, either version 3 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + *  You should have received a copy of the GNU General Public License
> + *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
> + */
> +#ifndef HW_NUCLEI_ECLIC_H
> +#define HW_NUCLEI_ECLIC_H
> +
> +#include "hw/irq.h"
> +#include "hw/sysbus.h"
> +
> +#define TYPE_NUCLEI_ECLIC "riscv.nuclei.eclic"
> +
> +#define INTERRUPT_SOURCE_MIN_ID (18)
> +#define INTERRUPT_SOURCE_MAX_ID (4096)
> +
> +typedef struct NucLeiECLICState NucLeiECLICState;
> +DECLARE_INSTANCE_CHECKER(NucLeiECLICState, NUCLEI_ECLIC,
> +                         TYPE_NUCLEI_ECLIC)
> +
> +typedef struct ECLICPendingInterrupt {
> +    int irq;
> +    int prio;
> +    int level;
> +    int enable;
> +    int trigger;
> +    int sig;
> +    QLIST_ENTRY(ECLICPendingInterrupt) next;
> +} ECLICPendingInterrupt;
> +
> +#define NUCLEI_ECLIC_REG_CLICCFG          0x0000
> +#define NUCLEI_ECLIC_REG_CLICINFO         0x0004
> +#define NUCLEI_ECLIC_REG_MTH              0x000b
> +#define NUCLEI_ECLIC_REG_CLICINTIP_BASE   0x1000
> +#define NUCLEI_ECLIC_REG_CLICINTIE_BASE   0x1001
> +#define NUCLEI_ECLIC_REG_CLICINTATTR_BASE 0x1002
> +#define NUCLEI_ECLIC_REG_CLICINTCTL_BASE  0x1003
> +
> +#define CLICINTCTLBITS 0x6
> +
> +typedef struct NucLeiECLICState {
> +    /*< private >*/
> +    SysBusDevice parent_obj;
> +
> +    /*< public >*/
> +    MemoryRegion mmio;
> +
> +    uint32_t num_sources; /* 4-1024 */
> +
> +    /* config */
> +    uint32_t sources_id;
> +    uint8_t cliccfg; /*  nlbits(1~4) */
> +    uint32_t clicinfo;
> +    uint8_t mth; /* mth(0~7) */
> +    uint8_t *clicintip;
> +    uint8_t *clicintie;
> +    uint8_t *clicintattr; /* shv(0) trig(1~2)*/
> +    uint8_t *clicintctl;
> +    ECLICPendingInterrupt *clicintlist;
> +    uint32_t aperture_size;
> +
> +    QLIST_HEAD(, ECLICPendingInterrupt)
> +    pending_list;
> +    size_t active_count;
> +
> +    /* ECLIC IRQ handlers */
> +    qemu_irq *irqs;
> +
> +} NucLeiECLICState;
> +
> +enum {
> +    Internal_Reserved0_IRQn = 0,     /*!<  Internal reserved */
> +    Internal_Reserved1_IRQn = 1,     /*!<  Internal reserved */
> +    Internal_Reserved2_IRQn = 2,     /*!<  Internal reserved */
> +    Internal_SysTimerSW_IRQn = 3,    /*!<  System Timer SW interrupt */
> +    Internal_Reserved3_IRQn = 4,     /*!<  Internal reserved */
> +    Internal_Reserved4_IRQn = 5,     /*!<  Internal reserved */
> +    Internal_Reserved5_IRQn = 6,     /*!<  Internal reserved */
> +    Internal_SysTimer_IRQn = 7,      /*!<  System Timer Interrupt */
> +    Internal_Reserved6_IRQn = 8,     /*!<  Internal reserved */
> +    Internal_Reserved7_IRQn = 9,     /*!<  Internal reserved */
> +    Internal_Reserved8_IRQn = 10,    /*!<  Internal reserved */
> +    Internal_Reserved9_IRQn = 11,    /*!<  Internal reserved */
> +    Internal_Reserved10_IRQn = 12,   /*!<  Internal reserved */
> +    Internal_Reserved11_IRQn = 13,   /*!<  Internal reserved */
> +    Internal_Reserved12_IRQn = 14,   /*!<  Internal reserved */
> +    Internal_Reserved13_IRQn = 15,   /*!<  Internal reserved */
> +    Internal_Reserved14_IRQn = 16,   /*!<  Internal reserved */
> +    Internal_BusError_IRQn = 17,     /*!<  Bus Error interrupt */
> +    Internal_PerfMon_IRQn = 18,      /*!<  Performance Monitor */
> +    Internal_Reserved_Max_IRQn = 19, /*!<  Internal reserved  Max */
> +};
> +
> +DeviceState *nuclei_eclic_create(hwaddr addr,
> +                                 uint32_t aperture_size, uint32_t num_sources);
> +qemu_irq nuclei_eclic_get_irq(DeviceState *dev, int irq);
> +void nuclei_eclic_systimer_cb(DeviceState *dev);
> +
> +#endif
> --
> 2.17.1
>
>


WARNING: multiple messages have this Message-ID (diff)
From: Alistair Francis <alistair23@gmail.com>
To: wangjunqiang <wangjunqiang@iscas.ac.cn>
Cc: "open list:RISC-V" <qemu-riscv@nongnu.org>,
	 "qemu-devel@nongnu.org Developers" <qemu-devel@nongnu.org>,
	liweiwei@iscas.ac.cn,  Bin Meng <bin.meng@windriver.com>,
	Alistair Francis <Alistair.Francis@wdc.com>,
	alapha23@gmail.com, Palmer Dabbelt <palmer@dabbelt.com>
Subject: Re: [RFC PATCH 2/5] hw/intc: Add Nuclei ECLIC device
Date: Mon, 10 May 2021 12:20:37 +1000	[thread overview]
Message-ID: <CAKmqyKPVYuESf0TQM8M-KE-72PftMeNCfmQLdEj4Siy9TZxuBA@mail.gmail.com> (raw)
In-Reply-To: <20210507081654.11056-3-wangjunqiang@iscas.ac.cn>

On Fri, May 7, 2021 at 11:24 PM wangjunqiang <wangjunqiang@iscas.ac.cn> wrote:
>
> This patch provides an implementation of Nuclei ECLIC Device.
> Nuclei processor core have been equipped with an Enhanced Core Local
> Interrupt Controller (ECLIC), which is optimized based on the RISC-V
> standard CLIC, to manage all interrupt sources.
>
> https://doc.nucleisys.com/nuclei_spec/isa/eclic.html

Hello,

There are patches on the QEMU list adding support for the CLIC. How
different is the ECLIC from the CLIC? Could you use the CLIC as a
starting point instead of implementing a new interrupt controller?

Alistair

> ---
>  hw/intc/Kconfig                |   3 +
>  hw/intc/meson.build            |   1 +
>  hw/intc/nuclei_eclic.c         | 437 +++++++++++++++++++++++++++++++++
>  include/hw/intc/nuclei_eclic.h | 115 +++++++++
>  4 files changed, 556 insertions(+)
>  create mode 100644 hw/intc/nuclei_eclic.c
>  create mode 100644 include/hw/intc/nuclei_eclic.h
>
> diff --git a/hw/intc/Kconfig b/hw/intc/Kconfig
> index f4694088a4..eab30f6ffd 100644
> --- a/hw/intc/Kconfig
> +++ b/hw/intc/Kconfig
> @@ -73,3 +73,6 @@ config GOLDFISH_PIC
>
>  config M68K_IRQC
>      bool
> +
> +config NUCLEI_ECLIC
> +    bool
> diff --git a/hw/intc/meson.build b/hw/intc/meson.build
> index 1c299039f6..7577ba69d2 100644
> --- a/hw/intc/meson.build
> +++ b/hw/intc/meson.build
> @@ -50,6 +50,7 @@ specific_ss.add(when: 'CONFIG_S390_FLIC_KVM', if_true: files('s390_flic_kvm.c'))
>  specific_ss.add(when: 'CONFIG_SH_INTC', if_true: files('sh_intc.c'))
>  specific_ss.add(when: 'CONFIG_SIFIVE_CLINT', if_true: files('sifive_clint.c'))
>  specific_ss.add(when: 'CONFIG_SIFIVE_PLIC', if_true: files('sifive_plic.c'))
> +specific_ss.add(when: 'CONFIG_NUCLEI_ECLIC', if_true: files('nuclei_eclic.c'))
>  specific_ss.add(when: 'CONFIG_XICS', if_true: files('xics.c'))
>  specific_ss.add(when: ['CONFIG_KVM', 'CONFIG_XICS'],
>                 if_true: files('xics_kvm.c'))
> diff --git a/hw/intc/nuclei_eclic.c b/hw/intc/nuclei_eclic.c
> new file mode 100644
> index 0000000000..52de83cb1d
> --- /dev/null
> +++ b/hw/intc/nuclei_eclic.c
> @@ -0,0 +1,437 @@
> +/*
> + * NUCLEI ECLIC(Enhanced Core Local Interrupt Controller)
> + *
> + * Copyright (c) 2020 Gao ZhiYuan <alapha23@gmail.com>
> + * Copyright (c) 2020-2021 PLCT Lab.All rights reserved.
> + *
> + * This provides a parameterizable interrupt controller based on NucLei's ECLIC.
> + *
> + * This program is free software: you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation, either version 3 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + *  You should have received a copy of the GNU General Public License
> + *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#include "qemu/osdep.h"
> +#include "qemu/log.h"
> +#include "qemu/module.h"
> +#include "qemu/error-report.h"
> +#include "hw/sysbus.h"
> +#include "hw/pci/msi.h"
> +#include "hw/boards.h"
> +#include "hw/qdev-properties.h"
> +#include "target/riscv/cpu.h"
> +#include "sysemu/sysemu.h"
> +#include "hw/intc/nuclei_eclic.h"
> +#include "qapi/error.h"
> +
> +#define RISCV_DEBUG_ECLIC 0
> +
> +static void riscv_cpu_eclic_interrupt(RISCVCPU *cpu, int exccode)
> +{
> +    CPURISCVState *env = &cpu->env;
> +    bool locked = false;
> +
> +    env->exccode = exccode;
> +
> +    if (!qemu_mutex_iothread_locked()) {
> +        locked = true;
> +        qemu_mutex_lock_iothread();
> +    }
> +
> +    if (exccode != -1) {
> +        env->irq_pending = true;
> +        cpu_interrupt(CPU(cpu), CPU_INTERRUPT_ECLIC);
> +    } else {
> +        env->irq_pending = false;
> +        cpu_reset_interrupt(CPU(cpu), CPU_INTERRUPT_ECLIC);
> +    }
> +
> +    if (locked) {
> +        qemu_mutex_unlock_iothread();
> +    }
> +}
> +
> +static int level_compare(NucLeiECLICState *eclic,
> +                         ECLICPendingInterrupt *irq1,
> +                         ECLICPendingInterrupt *irq2)
> +{
> +    if (irq1->level == irq2->level) {
> +        if (irq1->prio == irq2->prio) {
> +            if (irq1->irq >= irq2->irq) {
> +                return 0;
> +            } else {
> +                return 1;
> +            }
> +        } else if (irq1->prio > irq2->level) {
> +            return 0;
> +        } else {
> +            return 1;
> +        }
> +    } else if (irq1->level > irq2->level) {
> +        return 0;
> +    } else {
> +        return 1;
> +    }
> +}
> +
> +static void nuclei_eclic_next_interrupt(void *eclic_ptr)
> +{
> +    RISCVCPU *cpu = RISCV_CPU(qemu_get_cpu(0));
> +    CPURISCVState *env = &cpu->env;
> +    NucLeiECLICState *eclic = (NucLeiECLICState *)eclic_ptr;
> +    ECLICPendingInterrupt *active;
> +    target_ulong mil;
> +    int shv;
> +
> +    QLIST_FOREACH(active, &eclic->pending_list, next)
> +    {
> +        if (active->enable) {
> +            mil = get_field(env->mintstatus, MINTSTATUS_MIL);
> +            if (active->level >= eclic->mth && active->level > mil) {
> +                shv = eclic->clicintattr[active->irq] & 0x1;
> +                eclic->active_count++;
> +                riscv_cpu_eclic_interrupt(cpu,
> +                                          (active->irq & 0xFFF) | (shv << 12) | (active->level << 13));
> +                return;
> +            }
> +        }
> +    }
> +}
> +
> +static void nuclei_eclic_update_intmth(NucLeiECLICState *eclic,
> +                                       int irq, int mth)
> +{
> +    eclic->mth = mth;
> +    nuclei_eclic_next_interrupt(eclic);
> +}
> +
> +static void update_eclic_int_info(NucLeiECLICState *eclic, int irq)
> +{
> +    int level_width = (eclic->cliccfg >> 1) & 0xF;
> +    if (level_width > CLICINTCTLBITS) {
> +        level_width = CLICINTCTLBITS;
> +    }
> +    int prio_width = CLICINTCTLBITS - level_width;
> +
> +    if (level_width == 0) {
> +        eclic->clicintlist[irq].level = 255;
> +    } else {
> +        eclic->clicintlist[irq].level = ((
> +                                             (eclic->clicintctl[irq] >> (8 - level_width)) &
> +                                             ~((char)0x80 >> (8 - level_width)))
> +                                         << (8 - level_width)) |
> +                                        (0xff >> level_width);
> +    }
> +
> +    if (prio_width == 0) {
> +        eclic->clicintlist[irq].prio = 0;
> +    } else {
> +        eclic->clicintlist[irq].prio =
> +            (eclic->clicintctl[irq] >> (8 - level_width)) &
> +            ~(0x80 >> (8 - prio_width));
> +    }
> +
> +    eclic->clicintlist[irq].enable = eclic->clicintie[irq] & 0x1;
> +    eclic->clicintlist[irq].trigger = (eclic->clicintattr[irq] >> 1) & 0x3;
> +}
> +
> +static void eclic_remove_pending_list(NucLeiECLICState *eclic, int irq)
> +{
> +    QLIST_REMOVE(&eclic->clicintlist[irq], next);
> +}
> +
> +static void eclic_insert_pending_list(NucLeiECLICState *eclic, int irq)
> +{
> +    ECLICPendingInterrupt *node;
> +    if (QLIST_EMPTY(&eclic->pending_list)) {
> +        QLIST_INSERT_HEAD(&eclic->pending_list, &eclic->clicintlist[irq], next);
> +    } else {
> +        QLIST_FOREACH(node, &eclic->pending_list, next)
> +        {
> +            if (level_compare(eclic, node, &eclic->clicintlist[irq])) {
> +                QLIST_INSERT_BEFORE(node, &eclic->clicintlist[irq], next);
> +                break;
> +            } else if (node->next.le_next == NULL) {
> +                QLIST_INSERT_AFTER(node, &eclic->clicintlist[irq], next);
> +                break;
> +            }
> +        }
> +    }
> +}
> +
> +static void nuclei_eclic_update_intip(NucLeiECLICState *eclic, int irq, int new_intip)
> +{
> +
> +    int old_intip = eclic->clicintlist[irq].sig;
> +    int trigger = (eclic->clicintattr[irq] >> 1) & 0x3;
> +    if (((trigger == 0) && new_intip) ||
> +        ((trigger == 1) && !old_intip && new_intip) ||
> +        ((trigger == 3) && old_intip && !new_intip)) {
> +        eclic->clicintip[irq] = 1;
> +        eclic->clicintlist[irq].sig = new_intip;
> +        eclic_insert_pending_list(eclic, irq);
> +    } else {
> +        if (eclic->clicintip[irq]) {
> +            eclic_remove_pending_list(eclic, irq);
> +        }
> +        eclic->clicintip[irq] = 0;
> +        eclic->clicintlist[irq].sig = new_intip;
> +    }
> +
> +    nuclei_eclic_next_interrupt(eclic);
> +}
> +
> +static void nuclei_eclic_update_intie(NucLeiECLICState *eclic,
> +                                      int irq, int new_intie)
> +{
> +    eclic->clicintie[irq] = new_intie;
> +    update_eclic_int_info(eclic, irq);
> +    nuclei_eclic_next_interrupt(eclic);
> +}
> +
> +static void nuclei_eclic_update_intattr(NucLeiECLICState *eclic,
> +                                        int irq, int new_intattr)
> +{
> +    eclic->clicintattr[irq] = new_intattr;
> +    update_eclic_int_info(eclic, irq);
> +    nuclei_eclic_next_interrupt(eclic);
> +}
> +
> +static void nuclei_eclic_update_intctl(NucLeiECLICState *eclic,
> +                                       int irq, int new_intctl)
> +{
> +    eclic->clicintctl[irq] = new_intctl;
> +    update_eclic_int_info(eclic, irq);
> +    nuclei_eclic_next_interrupt(eclic);
> +}
> +
> +qemu_irq nuclei_eclic_get_irq(DeviceState *dev, int irq)
> +{
> +    NucLeiECLICState *eclic = NUCLEI_ECLIC(dev);
> +    return eclic->irqs[irq];
> +}
> +
> +static uint64_t nuclei_eclic_read(void *opaque, hwaddr offset, unsigned size)
> +{
> +    NucLeiECLICState *eclic = NUCLEI_ECLIC(opaque);
> +    uint64_t value = 0;
> +    uint32_t id = 0;
> +    if (offset >= NUCLEI_ECLIC_REG_CLICINTIP_BASE) {
> +        if ((offset - 0x1000) % 4 == 0) {
> +            id = (offset - 0x1000) / 4;
> +        } else if ((offset - 0x1001) % 4 == 0) {
> +            id = (offset - 0x1001) / 4;
> +        } else if ((offset - 0x1002) % 4 == 0) {
> +            id = (offset - 0x1002) / 4;
> +        } else if ((offset - 0x1003) % 4 == 0) {
> +            id = (offset - 0x1003) / 4;
> +        }
> +        offset = offset - 4 * id;
> +    }
> +
> +    switch (offset) {
> +    case NUCLEI_ECLIC_REG_CLICCFG:
> +        value = eclic->cliccfg & 0xFF;
> +        break;
> +    case NUCLEI_ECLIC_REG_CLICINFO:
> +        value = (CLICINTCTLBITS << 21) & 0xFFFFFFFF;
> +        break;
> +    case NUCLEI_ECLIC_REG_MTH:
> +        value = eclic->mth & 0xFF;
> +        break;
> +    case NUCLEI_ECLIC_REG_CLICINTIP_BASE:
> +        value = eclic->clicintip[id] & 0xFF;
> +        break;
> +    case NUCLEI_ECLIC_REG_CLICINTIE_BASE:
> +        value = eclic->clicintie[id] & 0xFF;
> +        break;
> +    case NUCLEI_ECLIC_REG_CLICINTATTR_BASE:
> +        value = eclic->clicintattr[id] & 0xFF;
> +        break;
> +    case NUCLEI_ECLIC_REG_CLICINTCTL_BASE:
> +        value = eclic->clicintctl[id] & 0xFF;
> +        break;
> +    default:
> +        break;
> +    }
> +
> +    return value;
> +}
> +
> +static void nuclei_eclic_write(void *opaque, hwaddr offset, uint64_t value,
> +                               unsigned size)
> +{
> +    NucLeiECLICState *eclic = NUCLEI_ECLIC(opaque);
> +    uint32_t id = 0;
> +    if (offset >= NUCLEI_ECLIC_REG_CLICINTIP_BASE) {
> +
> +        if ((offset - 0x1000) % 4 == 0) {
> +            id = (offset - 0x1000) / 4;
> +        } else if ((offset - 0x1001) % 4 == 0) {
> +            id = (offset - 0x1001) / 4;
> +        } else if ((offset - 0x1002) % 4 == 0) {
> +            id = (offset - 0x1002) / 4;
> +        } else if ((offset - 0x1003) % 4 == 0) {
> +            id = (offset - 0x1003) / 4;
> +        }
> +        offset = offset - 4 * id;
> +    }
> +    switch (offset) {
> +    case NUCLEI_ECLIC_REG_CLICCFG:
> +        eclic->cliccfg = value & 0xFF;
> +        for (id = 0; id < eclic->num_sources; id++) {
> +            update_eclic_int_info(eclic, id);
> +        }
> +        break;
> +    case NUCLEI_ECLIC_REG_MTH:
> +        nuclei_eclic_update_intmth(eclic, id, value & 0xFF);
> +        break;
> +    case NUCLEI_ECLIC_REG_CLICINTIP_BASE:
> +        if ((eclic->clicintlist[id].trigger & 0x1) != 0) {
> +            if ((eclic->clicintip[id] == 0) && (value & 0x1) == 1) {
> +                eclic->clicintip[id] = 1;
> +                eclic_insert_pending_list(eclic, id);
> +            } else if ((eclic->clicintip[id] == 1) && (value & 0x1) == 0) {
> +                eclic->clicintip[id] = 0;
> +                eclic_remove_pending_list(eclic, id);
> +            }
> +        }
> +        nuclei_eclic_next_interrupt(eclic);
> +        break;
> +    case NUCLEI_ECLIC_REG_CLICINTIE_BASE:
> +        nuclei_eclic_update_intie(eclic, id, value & 0xFF);
> +        break;
> +    case NUCLEI_ECLIC_REG_CLICINTATTR_BASE:
> +        nuclei_eclic_update_intattr(eclic, id, value & 0xFF);
> +        break;
> +    case NUCLEI_ECLIC_REG_CLICINTCTL_BASE:
> +        nuclei_eclic_update_intctl(eclic, id, value & 0xFF);
> +        break;
> +    default:
> +        break;
> +    }
> +}
> +
> +static const MemoryRegionOps nuclei_eclic_ops = {
> +    .read = nuclei_eclic_read,
> +    .write = nuclei_eclic_write,
> +    .endianness = DEVICE_LITTLE_ENDIAN,
> +};
> +
> +void riscv_cpu_eclic_clean_pending(void *eclic_ptr, int irq)
> +{
> +    NucLeiECLICState *eclic = (NucLeiECLICState *)eclic_ptr;
> +    if ((eclic->clicintlist[irq].trigger & 0x1) != 0 && irq >= 0) {
> +        eclic->clicintip[irq] = 0;
> +        eclic_remove_pending_list(eclic, irq);
> +    }
> +}
> +
> +void riscv_cpu_eclic_get_next_interrupt(void *eclic_ptr)
> +{
> +    NucLeiECLICState *eclic = (NucLeiECLICState *)eclic_ptr;
> +    nuclei_eclic_next_interrupt(eclic);
> +}
> +
> +
> +
> +static void nuclei_eclic_irq_request(void *opaque, int id, int new_intip)
> +{
> +    NucLeiECLICState *eclic = NUCLEI_ECLIC(opaque);
> +    nuclei_eclic_update_intip(eclic, id, new_intip);
> +}
> +
> +static void nuclei_eclic_realize(DeviceState *dev, Error **errp)
> +{
> +    NucLeiECLICState *eclic = NUCLEI_ECLIC(dev);
> +    int id;
> +
> +    memory_region_init_io(&eclic->mmio, OBJECT(dev), &nuclei_eclic_ops, eclic,
> +                          TYPE_NUCLEI_ECLIC, eclic->aperture_size);
> +    sysbus_init_mmio(SYS_BUS_DEVICE(dev), &eclic->mmio);
> +
> +    eclic->clicintip = g_new0(uint8_t, eclic->num_sources);
> +    eclic->clicintlist = g_new0(ECLICPendingInterrupt, eclic->num_sources);
> +    eclic->clicintie = g_new0(uint8_t, eclic->num_sources);
> +    eclic->clicintattr = g_new0(uint8_t, eclic->num_sources);
> +    eclic->clicintctl = g_new0(uint8_t, eclic->num_sources);
> +    eclic->irqs = g_new0(qemu_irq, eclic->num_sources);
> +    QLIST_INIT(&eclic->pending_list);
> +    for (id = 0; id < eclic->num_sources; id++) {
> +        eclic->clicintlist[id].irq = id;
> +        update_eclic_int_info(eclic, id);
> +    }
> +    eclic->active_count = 0;
> +
> +    /* Init ECLIC IRQ */
> +    eclic->irqs[Internal_SysTimerSW_IRQn] =
> +        qemu_allocate_irq(nuclei_eclic_irq_request,
> +                          eclic, Internal_SysTimerSW_IRQn);
> +    eclic->irqs[Internal_SysTimer_IRQn] =
> +        qemu_allocate_irq(nuclei_eclic_irq_request,
> +                          eclic, Internal_SysTimer_IRQn);
> +
> +    for (id = Internal_Reserved_Max_IRQn; id < eclic->num_sources; id++) {
> +        eclic->irqs[id] = qemu_allocate_irq(nuclei_eclic_irq_request,
> +                                            eclic, id);
> +    }
> +
> +    RISCVCPU *cpu = RISCV_CPU(qemu_get_cpu(0));
> +    cpu->env.eclic = eclic;
> +}
> +
> +static Property nuclei_eclic_properties[] = {
> +    DEFINE_PROP_UINT32("aperture-size", NucLeiECLICState, aperture_size, 0),
> +    DEFINE_PROP_UINT32("num-sources", NucLeiECLICState, num_sources, 0),
> +    DEFINE_PROP_END_OF_LIST(),
> +};
> +
> +static void nuclei_eclic_class_init(ObjectClass *klass, void *data)
> +{
> +    DeviceClass *dc = DEVICE_CLASS(klass);
> +
> +    device_class_set_props(dc, nuclei_eclic_properties);
> +    dc->realize = nuclei_eclic_realize;
> +}
> +
> +static const TypeInfo nuclei_eclic_info = {
> +    .name = TYPE_NUCLEI_ECLIC,
> +    .parent = TYPE_SYS_BUS_DEVICE,
> +    .instance_size = sizeof(NucLeiECLICState),
> +    .class_init = nuclei_eclic_class_init,
> +};
> +
> +static void nuclei_eclic_register_types(void)
> +{
> +    type_register_static(&nuclei_eclic_info);
> +}
> +
> +type_init(nuclei_eclic_register_types);
> +
> +void nuclei_eclic_systimer_cb(DeviceState *dev)
> +{
> +    NucLeiECLICState *eclic = NUCLEI_ECLIC(dev);
> +    nuclei_eclic_irq_request(eclic, Internal_SysTimer_IRQn, 1);
> +}
> +
> +DeviceState *nuclei_eclic_create(hwaddr addr,
> +                                 uint32_t aperture_size, uint32_t num_sources)
> +{
> +    DeviceState *dev = qdev_new(TYPE_NUCLEI_ECLIC);
> +
> +    qdev_prop_set_uint32(dev, "aperture-size", aperture_size);
> +    qdev_prop_set_uint32(dev, "num-sources", num_sources);
> +
> +    sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal);
> +    sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, addr);
> +    return dev;
> +}
> diff --git a/include/hw/intc/nuclei_eclic.h b/include/hw/intc/nuclei_eclic.h
> new file mode 100644
> index 0000000000..18b25485b9
> --- /dev/null
> +++ b/include/hw/intc/nuclei_eclic.h
> @@ -0,0 +1,115 @@
> +/*
> + * NUCLEI ECLIC (Enhanced Core Local Interrupt Controller) interface
> + *
> + * Copyright (c) 2020 Gao ZhiYuan <alapha23@gmail.com>
> + * Copyright (c) 2020-2021 PLCT Lab.All rights reserved.
> + *
> + * This provides a parameterizable interrupt controller based on NucLei's ECLIC.
> + *
> + * This program is free software: you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation, either version 3 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + *  You should have received a copy of the GNU General Public License
> + *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
> + */
> +#ifndef HW_NUCLEI_ECLIC_H
> +#define HW_NUCLEI_ECLIC_H
> +
> +#include "hw/irq.h"
> +#include "hw/sysbus.h"
> +
> +#define TYPE_NUCLEI_ECLIC "riscv.nuclei.eclic"
> +
> +#define INTERRUPT_SOURCE_MIN_ID (18)
> +#define INTERRUPT_SOURCE_MAX_ID (4096)
> +
> +typedef struct NucLeiECLICState NucLeiECLICState;
> +DECLARE_INSTANCE_CHECKER(NucLeiECLICState, NUCLEI_ECLIC,
> +                         TYPE_NUCLEI_ECLIC)
> +
> +typedef struct ECLICPendingInterrupt {
> +    int irq;
> +    int prio;
> +    int level;
> +    int enable;
> +    int trigger;
> +    int sig;
> +    QLIST_ENTRY(ECLICPendingInterrupt) next;
> +} ECLICPendingInterrupt;
> +
> +#define NUCLEI_ECLIC_REG_CLICCFG          0x0000
> +#define NUCLEI_ECLIC_REG_CLICINFO         0x0004
> +#define NUCLEI_ECLIC_REG_MTH              0x000b
> +#define NUCLEI_ECLIC_REG_CLICINTIP_BASE   0x1000
> +#define NUCLEI_ECLIC_REG_CLICINTIE_BASE   0x1001
> +#define NUCLEI_ECLIC_REG_CLICINTATTR_BASE 0x1002
> +#define NUCLEI_ECLIC_REG_CLICINTCTL_BASE  0x1003
> +
> +#define CLICINTCTLBITS 0x6
> +
> +typedef struct NucLeiECLICState {
> +    /*< private >*/
> +    SysBusDevice parent_obj;
> +
> +    /*< public >*/
> +    MemoryRegion mmio;
> +
> +    uint32_t num_sources; /* 4-1024 */
> +
> +    /* config */
> +    uint32_t sources_id;
> +    uint8_t cliccfg; /*  nlbits(1~4) */
> +    uint32_t clicinfo;
> +    uint8_t mth; /* mth(0~7) */
> +    uint8_t *clicintip;
> +    uint8_t *clicintie;
> +    uint8_t *clicintattr; /* shv(0) trig(1~2)*/
> +    uint8_t *clicintctl;
> +    ECLICPendingInterrupt *clicintlist;
> +    uint32_t aperture_size;
> +
> +    QLIST_HEAD(, ECLICPendingInterrupt)
> +    pending_list;
> +    size_t active_count;
> +
> +    /* ECLIC IRQ handlers */
> +    qemu_irq *irqs;
> +
> +} NucLeiECLICState;
> +
> +enum {
> +    Internal_Reserved0_IRQn = 0,     /*!<  Internal reserved */
> +    Internal_Reserved1_IRQn = 1,     /*!<  Internal reserved */
> +    Internal_Reserved2_IRQn = 2,     /*!<  Internal reserved */
> +    Internal_SysTimerSW_IRQn = 3,    /*!<  System Timer SW interrupt */
> +    Internal_Reserved3_IRQn = 4,     /*!<  Internal reserved */
> +    Internal_Reserved4_IRQn = 5,     /*!<  Internal reserved */
> +    Internal_Reserved5_IRQn = 6,     /*!<  Internal reserved */
> +    Internal_SysTimer_IRQn = 7,      /*!<  System Timer Interrupt */
> +    Internal_Reserved6_IRQn = 8,     /*!<  Internal reserved */
> +    Internal_Reserved7_IRQn = 9,     /*!<  Internal reserved */
> +    Internal_Reserved8_IRQn = 10,    /*!<  Internal reserved */
> +    Internal_Reserved9_IRQn = 11,    /*!<  Internal reserved */
> +    Internal_Reserved10_IRQn = 12,   /*!<  Internal reserved */
> +    Internal_Reserved11_IRQn = 13,   /*!<  Internal reserved */
> +    Internal_Reserved12_IRQn = 14,   /*!<  Internal reserved */
> +    Internal_Reserved13_IRQn = 15,   /*!<  Internal reserved */
> +    Internal_Reserved14_IRQn = 16,   /*!<  Internal reserved */
> +    Internal_BusError_IRQn = 17,     /*!<  Bus Error interrupt */
> +    Internal_PerfMon_IRQn = 18,      /*!<  Performance Monitor */
> +    Internal_Reserved_Max_IRQn = 19, /*!<  Internal reserved  Max */
> +};
> +
> +DeviceState *nuclei_eclic_create(hwaddr addr,
> +                                 uint32_t aperture_size, uint32_t num_sources);
> +qemu_irq nuclei_eclic_get_irq(DeviceState *dev, int irq);
> +void nuclei_eclic_systimer_cb(DeviceState *dev);
> +
> +#endif
> --
> 2.17.1
>
>


  reply	other threads:[~2021-05-10  2:22 UTC|newest]

Thread overview: 31+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2021-05-07  8:16 [RFC PATCH 0/5] RISC-V:support Nuclei FPGA Evaluation Kit wangjunqiang
2021-05-07  8:16 ` wangjunqiang
2021-05-07  8:16 ` [RFC PATCH 1/5] target/riscv: Add Nuclei CSR and Update interrupt handling wangjunqiang
2021-05-07  8:16   ` wangjunqiang
2021-05-10  2:17   ` Alistair Francis
2021-05-10  2:17     ` Alistair Francis
2021-05-11  3:14     ` Wang Junqiang
2021-05-11  3:14       ` Wang Junqiang
2021-05-11  3:43       ` Alistair Francis
2021-05-11  3:43         ` Alistair Francis
2021-05-11  4:00         ` Wang Junqiang
2021-05-11  4:00           ` Wang Junqiang
2021-05-11  4:03           ` Alistair Francis
2021-05-11  4:03             ` Alistair Francis
2021-05-07  8:16 ` [RFC PATCH 2/5] hw/intc: Add Nuclei ECLIC device wangjunqiang
2021-05-07  8:16   ` wangjunqiang
2021-05-10  2:20   ` Alistair Francis [this message]
2021-05-10  2:20     ` Alistair Francis
2021-05-10  2:27     ` Bin Meng
2021-05-10  2:27       ` Bin Meng
2021-05-10  5:26       ` Bin Meng
2021-05-10  5:26         ` Bin Meng
2021-05-11  3:18         ` Wang Junqiang
2021-05-07  8:16 ` [RFC PATCH 3/5] hw/intc: Add Nuclei Systimer wangjunqiang
2021-05-07  8:16   ` wangjunqiang
2021-05-07  8:16 ` [RFC PATCH 4/5] hw/char: Add Nuclei Uart wangjunqiang
2021-05-07  8:16   ` wangjunqiang
2021-05-07  8:16 ` [RFC PATCH 5/5] Nuclei FPGA Evaluation Kit MCU Machine wangjunqiang
2021-05-07  8:16   ` wangjunqiang
2021-05-07 13:33 ` [RFC PATCH 0/5] RISC-V:support Nuclei FPGA Evaluation Kit no-reply
2021-05-07 13:33   ` no-reply

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=CAKmqyKPVYuESf0TQM8M-KE-72PftMeNCfmQLdEj4Siy9TZxuBA@mail.gmail.com \
    --to=alistair23@gmail.com \
    --cc=Alistair.Francis@wdc.com \
    --cc=alapha23@gmail.com \
    --cc=bin.meng@windriver.com \
    --cc=liweiwei@iscas.ac.cn \
    --cc=palmer@dabbelt.com \
    --cc=qemu-devel@nongnu.org \
    --cc=qemu-riscv@nongnu.org \
    --cc=wangjunqiang@iscas.ac.cn \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.