aboutsummaryrefslogtreecommitdiff
path: root/hw/intc
diff options
context:
space:
mode:
authorRichard Henderson <richard.henderson@linaro.org>2021-09-21 10:57:48 -0700
committerRichard Henderson <richard.henderson@linaro.org>2021-09-21 10:57:48 -0700
commit2c3e83f92d93fbab071b8a96b8ab769b01902475 (patch)
treed6462933ac8d7dff5598c3d688c5f8e9aae7e8ee /hw/intc
parent81ceb36b965c9d5ed5b1eb0ed80e23705802de15 (diff)
parented481d9837250aa682f5156528bc923e1b214f76 (diff)
Merge remote-tracking branch 'remotes/alistair23/tags/pull-riscv-to-apply-20210921' into staging
Second RISC-V PR for QEMU 6.2 - ePMP CSR address updates - Convert internal interrupts to use QEMU GPIO lines - SiFive PWM support - Support for RISC-V ACLINT - SiFive PDMA fixes - Update to u-boot instructions for sifive_u - mstatus.SD bug fix for hypervisor extensions - OpenTitan fix for USB dev address # gpg: Signature made Mon 20 Sep 2021 11:52:26 PM PDT # gpg: using RSA key F6C4AC46D4934868D3B8CE8F21E10D29DF977054 # gpg: Good signature from "Alistair Francis <alistair@alistair23.me>" [unknown] # gpg: WARNING: This key is not certified with a trusted signature! # gpg: There is no indication that the signature belongs to the owner. # Primary key fingerprint: F6C4 AC46 D493 4868 D3B8 CE8F 21E1 0D29 DF97 7054 * remotes/alistair23/tags/pull-riscv-to-apply-20210921: (21 commits) hw/riscv: opentitan: Correct the USB Dev address target/riscv: csr: Rename HCOUNTEREN_CY and friends target/riscv: Backup/restore mstatus.SD bit when virtual register swapped docs/system/riscv: sifive_u: Update U-Boot instructions hw/dma: sifive_pdma: don't set Control.error if 0 bytes to transfer hw/dma: sifive_pdma: allow non-multiple transaction size transactions hw/dma: sifive_pdma: claim bit must be set before DMA transactions hw/dma: sifive_pdma: reset Next* registers when Control.claim is set hw/riscv: virt: Add optional ACLINT support to virt machine hw/riscv: virt: Re-factor FDT generation hw/intc: Upgrade the SiFive CLINT implementation to RISC-V ACLINT hw/intc: Rename sifive_clint sources to riscv_aclint sources sifive_u: Connect the SiFive PWM device hw/timer: Add SiFive PWM support hw/intc: ibex_timer: Convert the timer to use RISC-V CPU GPIO lines hw/intc: sifive_plic: Convert the PLIC to use RISC-V CPU GPIO lines hw/intc: ibex_plic: Convert the PLIC to use RISC-V CPU GPIO lines hw/intc: sifive_clint: Use RISC-V CPU GPIO lines target/riscv: Expose interrupt pending bits as GPIO lines target/riscv: Fix satp write ... Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
Diffstat (limited to 'hw/intc')
-rw-r--r--hw/intc/Kconfig2
-rw-r--r--hw/intc/ibex_plic.c17
-rw-r--r--hw/intc/meson.build2
-rw-r--r--hw/intc/riscv_aclint.c460
-rw-r--r--hw/intc/sifive_clint.c287
-rw-r--r--hw/intc/sifive_plic.c30
6 files changed, 491 insertions, 307 deletions
diff --git a/hw/intc/Kconfig b/hw/intc/Kconfig
index f4694088a4..78aed93c45 100644
--- a/hw/intc/Kconfig
+++ b/hw/intc/Kconfig
@@ -62,7 +62,7 @@ config RX_ICU
config LOONGSON_LIOINTC
bool
-config SIFIVE_CLINT
+config RISCV_ACLINT
bool
config SIFIVE_PLIC
diff --git a/hw/intc/ibex_plic.c b/hw/intc/ibex_plic.c
index edf76e4f61..ff430356f8 100644
--- a/hw/intc/ibex_plic.c
+++ b/hw/intc/ibex_plic.c
@@ -27,6 +27,7 @@
#include "target/riscv/cpu_bits.h"
#include "target/riscv/cpu.h"
#include "hw/intc/ibex_plic.h"
+#include "hw/irq.h"
static bool addr_between(uint32_t addr, uint32_t base, uint32_t num)
{
@@ -92,19 +93,10 @@ static bool ibex_plic_irqs_pending(IbexPlicState *s, uint32_t context)
static void ibex_plic_update(IbexPlicState *s)
{
- CPUState *cpu;
- int level, i;
+ int i;
for (i = 0; i < s->num_cpus; i++) {
- cpu = qemu_get_cpu(i);
-
- if (!cpu) {
- continue;
- }
-
- level = ibex_plic_irqs_pending(s, 0);
-
- riscv_cpu_update_mip(RISCV_CPU(cpu), MIP_MEIP, BOOL_TO_MASK(level));
+ qemu_set_irq(s->external_irqs[i], ibex_plic_irqs_pending(s, 0));
}
}
@@ -268,6 +260,9 @@ static void ibex_plic_realize(DeviceState *dev, Error **errp)
qdev_init_gpio_in(dev, ibex_plic_irq_request, s->num_sources);
+ s->external_irqs = g_malloc(sizeof(qemu_irq) * s->num_cpus);
+ qdev_init_gpio_out(dev, s->external_irqs, s->num_cpus);
+
/*
* We can't allow the supervisor to control SEIP as this would allow the
* supervisor to clear a pending external interrupt which will result in
diff --git a/hw/intc/meson.build b/hw/intc/meson.build
index 4dcfea6aa8..a1d00aa48d 100644
--- a/hw/intc/meson.build
+++ b/hw/intc/meson.build
@@ -47,7 +47,7 @@ specific_ss.add(when: 'CONFIG_RX_ICU', if_true: files('rx_icu.c'))
specific_ss.add(when: 'CONFIG_S390_FLIC', if_true: files('s390_flic.c'))
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_RISCV_ACLINT', if_true: files('riscv_aclint.c'))
specific_ss.add(when: 'CONFIG_SIFIVE_PLIC', if_true: files('sifive_plic.c'))
specific_ss.add(when: 'CONFIG_XICS', if_true: files('xics.c'))
specific_ss.add(when: ['CONFIG_KVM', 'CONFIG_XICS'],
diff --git a/hw/intc/riscv_aclint.c b/hw/intc/riscv_aclint.c
new file mode 100644
index 0000000000..f1a5d3d284
--- /dev/null
+++ b/hw/intc/riscv_aclint.c
@@ -0,0 +1,460 @@
+/*
+ * RISC-V ACLINT (Advanced Core Local Interruptor)
+ * URL: https://github.com/riscv/riscv-aclint
+ *
+ * Copyright (c) 2016-2017 Sagar Karandikar, sagark@eecs.berkeley.edu
+ * Copyright (c) 2017 SiFive, Inc.
+ * Copyright (c) 2021 Western Digital Corporation or its affiliates.
+ *
+ * This provides real-time clock, timer and interprocessor interrupts.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2 or later, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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 "qapi/error.h"
+#include "qemu/error-report.h"
+#include "qemu/log.h"
+#include "qemu/module.h"
+#include "hw/sysbus.h"
+#include "target/riscv/cpu.h"
+#include "hw/qdev-properties.h"
+#include "hw/intc/riscv_aclint.h"
+#include "qemu/timer.h"
+#include "hw/irq.h"
+
+typedef struct riscv_aclint_mtimer_callback {
+ RISCVAclintMTimerState *s;
+ int num;
+} riscv_aclint_mtimer_callback;
+
+static uint64_t cpu_riscv_read_rtc(uint32_t timebase_freq)
+{
+ return muldiv64(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL),
+ timebase_freq, NANOSECONDS_PER_SECOND);
+}
+
+/*
+ * Called when timecmp is written to update the QEMU timer or immediately
+ * trigger timer interrupt if mtimecmp <= current timer value.
+ */
+static void riscv_aclint_mtimer_write_timecmp(RISCVAclintMTimerState *mtimer,
+ RISCVCPU *cpu,
+ int hartid,
+ uint64_t value,
+ uint32_t timebase_freq)
+{
+ uint64_t next;
+ uint64_t diff;
+
+ uint64_t rtc_r = cpu_riscv_read_rtc(timebase_freq);
+
+ cpu->env.timecmp = value;
+ if (cpu->env.timecmp <= rtc_r) {
+ /*
+ * If we're setting an MTIMECMP value in the "past",
+ * immediately raise the timer interrupt
+ */
+ qemu_irq_raise(mtimer->timer_irqs[hartid - mtimer->hartid_base]);
+ return;
+ }
+
+ /* otherwise, set up the future timer interrupt */
+ qemu_irq_lower(mtimer->timer_irqs[hartid - mtimer->hartid_base]);
+ diff = cpu->env.timecmp - rtc_r;
+ /* back to ns (note args switched in muldiv64) */
+ uint64_t ns_diff = muldiv64(diff, NANOSECONDS_PER_SECOND, timebase_freq);
+
+ /*
+ * check if ns_diff overflowed and check if the addition would potentially
+ * overflow
+ */
+ if ((NANOSECONDS_PER_SECOND > timebase_freq && ns_diff < diff) ||
+ ns_diff > INT64_MAX) {
+ next = INT64_MAX;
+ } else {
+ /*
+ * as it is very unlikely qemu_clock_get_ns will return a value
+ * greater than INT64_MAX, no additional check is needed for an
+ * unsigned integer overflow.
+ */
+ next = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + ns_diff;
+ /*
+ * if ns_diff is INT64_MAX next may still be outside the range
+ * of a signed integer.
+ */
+ next = MIN(next, INT64_MAX);
+ }
+
+ timer_mod(cpu->env.timer, next);
+}
+
+/*
+ * Callback used when the timer set using timer_mod expires.
+ * Should raise the timer interrupt line
+ */
+static void riscv_aclint_mtimer_cb(void *opaque)
+{
+ riscv_aclint_mtimer_callback *state = opaque;
+
+ qemu_irq_raise(state->s->timer_irqs[state->num]);
+}
+
+/* CPU read MTIMER register */
+static uint64_t riscv_aclint_mtimer_read(void *opaque, hwaddr addr,
+ unsigned size)
+{
+ RISCVAclintMTimerState *mtimer = opaque;
+
+ if (addr >= mtimer->timecmp_base &&
+ addr < (mtimer->timecmp_base + (mtimer->num_harts << 3))) {
+ size_t hartid = mtimer->hartid_base +
+ ((addr - mtimer->timecmp_base) >> 3);
+ CPUState *cpu = qemu_get_cpu(hartid);
+ CPURISCVState *env = cpu ? cpu->env_ptr : NULL;
+ if (!env) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "aclint-mtimer: invalid hartid: %zu", hartid);
+ } else if ((addr & 0x7) == 0) {
+ /* timecmp_lo */
+ uint64_t timecmp = env->timecmp;
+ return timecmp & 0xFFFFFFFF;
+ } else if ((addr & 0x7) == 4) {
+ /* timecmp_hi */
+ uint64_t timecmp = env->timecmp;
+ return (timecmp >> 32) & 0xFFFFFFFF;
+ } else {
+ qemu_log_mask(LOG_UNIMP,
+ "aclint-mtimer: invalid read: %08x", (uint32_t)addr);
+ return 0;
+ }
+ } else if (addr == mtimer->time_base) {
+ /* time_lo */
+ return cpu_riscv_read_rtc(mtimer->timebase_freq) & 0xFFFFFFFF;
+ } else if (addr == mtimer->time_base + 4) {
+ /* time_hi */
+ return (cpu_riscv_read_rtc(mtimer->timebase_freq) >> 32) & 0xFFFFFFFF;
+ }
+
+ qemu_log_mask(LOG_UNIMP,
+ "aclint-mtimer: invalid read: %08x", (uint32_t)addr);
+ return 0;
+}
+
+/* CPU write MTIMER register */
+static void riscv_aclint_mtimer_write(void *opaque, hwaddr addr,
+ uint64_t value, unsigned size)
+{
+ RISCVAclintMTimerState *mtimer = opaque;
+
+ if (addr >= mtimer->timecmp_base &&
+ addr < (mtimer->timecmp_base + (mtimer->num_harts << 3))) {
+ size_t hartid = mtimer->hartid_base +
+ ((addr - mtimer->timecmp_base) >> 3);
+ CPUState *cpu = qemu_get_cpu(hartid);
+ CPURISCVState *env = cpu ? cpu->env_ptr : NULL;
+ if (!env) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "aclint-mtimer: invalid hartid: %zu", hartid);
+ } else if ((addr & 0x7) == 0) {
+ /* timecmp_lo */
+ uint64_t timecmp_hi = env->timecmp >> 32;
+ riscv_aclint_mtimer_write_timecmp(mtimer, RISCV_CPU(cpu), hartid,
+ timecmp_hi << 32 | (value & 0xFFFFFFFF),
+ mtimer->timebase_freq);
+ return;
+ } else if ((addr & 0x7) == 4) {
+ /* timecmp_hi */
+ uint64_t timecmp_lo = env->timecmp;
+ riscv_aclint_mtimer_write_timecmp(mtimer, RISCV_CPU(cpu), hartid,
+ value << 32 | (timecmp_lo & 0xFFFFFFFF),
+ mtimer->timebase_freq);
+ } else {
+ qemu_log_mask(LOG_UNIMP,
+ "aclint-mtimer: invalid timecmp write: %08x",
+ (uint32_t)addr);
+ }
+ return;
+ } else if (addr == mtimer->time_base) {
+ /* time_lo */
+ qemu_log_mask(LOG_UNIMP,
+ "aclint-mtimer: time_lo write not implemented");
+ return;
+ } else if (addr == mtimer->time_base + 4) {
+ /* time_hi */
+ qemu_log_mask(LOG_UNIMP,
+ "aclint-mtimer: time_hi write not implemented");
+ return;
+ }
+
+ qemu_log_mask(LOG_UNIMP,
+ "aclint-mtimer: invalid write: %08x", (uint32_t)addr);
+}
+
+static const MemoryRegionOps riscv_aclint_mtimer_ops = {
+ .read = riscv_aclint_mtimer_read,
+ .write = riscv_aclint_mtimer_write,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+ .valid = {
+ .min_access_size = 4,
+ .max_access_size = 8
+ }
+};
+
+static Property riscv_aclint_mtimer_properties[] = {
+ DEFINE_PROP_UINT32("hartid-base", RISCVAclintMTimerState,
+ hartid_base, 0),
+ DEFINE_PROP_UINT32("num-harts", RISCVAclintMTimerState, num_harts, 1),
+ DEFINE_PROP_UINT32("timecmp-base", RISCVAclintMTimerState,
+ timecmp_base, RISCV_ACLINT_DEFAULT_MTIMECMP),
+ DEFINE_PROP_UINT32("time-base", RISCVAclintMTimerState,
+ time_base, RISCV_ACLINT_DEFAULT_MTIME),
+ DEFINE_PROP_UINT32("aperture-size", RISCVAclintMTimerState,
+ aperture_size, RISCV_ACLINT_DEFAULT_MTIMER_SIZE),
+ DEFINE_PROP_UINT32("timebase-freq", RISCVAclintMTimerState,
+ timebase_freq, 0),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void riscv_aclint_mtimer_realize(DeviceState *dev, Error **errp)
+{
+ RISCVAclintMTimerState *s = RISCV_ACLINT_MTIMER(dev);
+ int i;
+
+ memory_region_init_io(&s->mmio, OBJECT(dev), &riscv_aclint_mtimer_ops,
+ s, TYPE_RISCV_ACLINT_MTIMER, s->aperture_size);
+ sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->mmio);
+
+ s->timer_irqs = g_malloc(sizeof(qemu_irq) * s->num_harts);
+ qdev_init_gpio_out(dev, s->timer_irqs, s->num_harts);
+
+ /* Claim timer interrupt bits */
+ for (i = 0; i < s->num_harts; i++) {
+ RISCVCPU *cpu = RISCV_CPU(qemu_get_cpu(s->hartid_base + i));
+ if (riscv_cpu_claim_interrupts(cpu, MIP_MTIP) < 0) {
+ error_report("MTIP already claimed");
+ exit(1);
+ }
+ }
+}
+
+static void riscv_aclint_mtimer_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ dc->realize = riscv_aclint_mtimer_realize;
+ device_class_set_props(dc, riscv_aclint_mtimer_properties);
+}
+
+static const TypeInfo riscv_aclint_mtimer_info = {
+ .name = TYPE_RISCV_ACLINT_MTIMER,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(RISCVAclintMTimerState),
+ .class_init = riscv_aclint_mtimer_class_init,
+};
+
+/*
+ * Create ACLINT MTIMER device.
+ */
+DeviceState *riscv_aclint_mtimer_create(hwaddr addr, hwaddr size,
+ uint32_t hartid_base, uint32_t num_harts,
+ uint32_t timecmp_base, uint32_t time_base, uint32_t timebase_freq,
+ bool provide_rdtime)
+{
+ int i;
+ DeviceState *dev = qdev_new(TYPE_RISCV_ACLINT_MTIMER);
+
+ assert(num_harts <= RISCV_ACLINT_MAX_HARTS);
+ assert(!(addr & 0x7));
+ assert(!(timecmp_base & 0x7));
+ assert(!(time_base & 0x7));
+
+ qdev_prop_set_uint32(dev, "hartid-base", hartid_base);
+ qdev_prop_set_uint32(dev, "num-harts", num_harts);
+ qdev_prop_set_uint32(dev, "timecmp-base", timecmp_base);
+ qdev_prop_set_uint32(dev, "time-base", time_base);
+ qdev_prop_set_uint32(dev, "aperture-size", size);
+ qdev_prop_set_uint32(dev, "timebase-freq", timebase_freq);
+ sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal);
+ sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, addr);
+
+ for (i = 0; i < num_harts; i++) {
+ CPUState *cpu = qemu_get_cpu(hartid_base + i);
+ RISCVCPU *rvcpu = RISCV_CPU(cpu);
+ CPURISCVState *env = cpu ? cpu->env_ptr : NULL;
+ riscv_aclint_mtimer_callback *cb =
+ g_malloc0(sizeof(riscv_aclint_mtimer_callback));
+
+ if (!env) {
+ g_free(cb);
+ continue;
+ }
+ if (provide_rdtime) {
+ riscv_cpu_set_rdtime_fn(env, cpu_riscv_read_rtc, timebase_freq);
+ }
+
+ cb->s = RISCV_ACLINT_MTIMER(dev);
+ cb->num = i;
+ env->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL,
+ &riscv_aclint_mtimer_cb, cb);
+ env->timecmp = 0;
+
+ qdev_connect_gpio_out(dev, i,
+ qdev_get_gpio_in(DEVICE(rvcpu), IRQ_M_TIMER));
+ }
+
+ return dev;
+}
+
+/* CPU read [M|S]SWI register */
+static uint64_t riscv_aclint_swi_read(void *opaque, hwaddr addr,
+ unsigned size)
+{
+ RISCVAclintSwiState *swi = opaque;
+
+ if (addr < (swi->num_harts << 2)) {
+ size_t hartid = swi->hartid_base + (addr >> 2);
+ CPUState *cpu = qemu_get_cpu(hartid);
+ CPURISCVState *env = cpu ? cpu->env_ptr : NULL;
+ if (!env) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "aclint-swi: invalid hartid: %zu", hartid);
+ } else if ((addr & 0x3) == 0) {
+ return (swi->sswi) ? 0 : ((env->mip & MIP_MSIP) > 0);
+ }
+ }
+
+ qemu_log_mask(LOG_UNIMP,
+ "aclint-swi: invalid read: %08x", (uint32_t)addr);
+ return 0;
+}
+
+/* CPU write [M|S]SWI register */
+static void riscv_aclint_swi_write(void *opaque, hwaddr addr, uint64_t value,
+ unsigned size)
+{
+ RISCVAclintSwiState *swi = opaque;
+
+ if (addr < (swi->num_harts << 2)) {
+ size_t hartid = swi->hartid_base + (addr >> 2);
+ CPUState *cpu = qemu_get_cpu(hartid);
+ CPURISCVState *env = cpu ? cpu->env_ptr : NULL;
+ if (!env) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "aclint-swi: invalid hartid: %zu", hartid);
+ } else if ((addr & 0x3) == 0) {
+ if (value & 0x1) {
+ qemu_irq_raise(swi->soft_irqs[hartid - swi->hartid_base]);
+ } else {
+ if (!swi->sswi) {
+ qemu_irq_lower(swi->soft_irqs[hartid - swi->hartid_base]);
+ }
+ }
+ return;
+ }
+ }
+
+ qemu_log_mask(LOG_UNIMP,
+ "aclint-swi: invalid write: %08x", (uint32_t)addr);
+}
+
+static const MemoryRegionOps riscv_aclint_swi_ops = {
+ .read = riscv_aclint_swi_read,
+ .write = riscv_aclint_swi_write,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+ .valid = {
+ .min_access_size = 4,
+ .max_access_size = 4
+ }
+};
+
+static Property riscv_aclint_swi_properties[] = {
+ DEFINE_PROP_UINT32("hartid-base", RISCVAclintSwiState, hartid_base, 0),
+ DEFINE_PROP_UINT32("num-harts", RISCVAclintSwiState, num_harts, 1),
+ DEFINE_PROP_UINT32("sswi", RISCVAclintSwiState, sswi, false),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void riscv_aclint_swi_realize(DeviceState *dev, Error **errp)
+{
+ RISCVAclintSwiState *swi = RISCV_ACLINT_SWI(dev);
+ int i;
+
+ memory_region_init_io(&swi->mmio, OBJECT(dev), &riscv_aclint_swi_ops, swi,
+ TYPE_RISCV_ACLINT_SWI, RISCV_ACLINT_SWI_SIZE);
+ sysbus_init_mmio(SYS_BUS_DEVICE(dev), &swi->mmio);
+
+ swi->soft_irqs = g_malloc(sizeof(qemu_irq) * swi->num_harts);
+ qdev_init_gpio_out(dev, swi->soft_irqs, swi->num_harts);
+
+ /* Claim software interrupt bits */
+ for (i = 0; i < swi->num_harts; i++) {
+ RISCVCPU *cpu = RISCV_CPU(qemu_get_cpu(swi->hartid_base + i));
+ /* We don't claim mip.SSIP because it is writeable by software */
+ if (riscv_cpu_claim_interrupts(cpu, swi->sswi ? 0 : MIP_MSIP) < 0) {
+ error_report("MSIP already claimed");
+ exit(1);
+ }
+ }
+}
+
+static void riscv_aclint_swi_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ dc->realize = riscv_aclint_swi_realize;
+ device_class_set_props(dc, riscv_aclint_swi_properties);
+}
+
+static const TypeInfo riscv_aclint_swi_info = {
+ .name = TYPE_RISCV_ACLINT_SWI,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(RISCVAclintSwiState),
+ .class_init = riscv_aclint_swi_class_init,
+};
+
+/*
+ * Create ACLINT [M|S]SWI device.
+ */
+DeviceState *riscv_aclint_swi_create(hwaddr addr, uint32_t hartid_base,
+ uint32_t num_harts, bool sswi)
+{
+ int i;
+ DeviceState *dev = qdev_new(TYPE_RISCV_ACLINT_SWI);
+
+ assert(num_harts <= RISCV_ACLINT_MAX_HARTS);
+ assert(!(addr & 0x3));
+
+ qdev_prop_set_uint32(dev, "hartid-base", hartid_base);
+ qdev_prop_set_uint32(dev, "num-harts", num_harts);
+ qdev_prop_set_uint32(dev, "sswi", sswi ? true : false);
+ sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal);
+ sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, addr);
+
+ for (i = 0; i < num_harts; i++) {
+ CPUState *cpu = qemu_get_cpu(hartid_base + i);
+ RISCVCPU *rvcpu = RISCV_CPU(cpu);
+
+ qdev_connect_gpio_out(dev, i,
+ qdev_get_gpio_in(DEVICE(rvcpu),
+ (sswi) ? IRQ_S_SOFT : IRQ_M_SOFT));
+ }
+
+ return dev;
+}
+
+static void riscv_aclint_register_types(void)
+{
+ type_register_static(&riscv_aclint_mtimer_info);
+ type_register_static(&riscv_aclint_swi_info);
+}
+
+type_init(riscv_aclint_register_types)
diff --git a/hw/intc/sifive_clint.c b/hw/intc/sifive_clint.c
deleted file mode 100644
index 99c870ced2..0000000000
--- a/hw/intc/sifive_clint.c
+++ /dev/null
@@ -1,287 +0,0 @@
-/*
- * SiFive CLINT (Core Local Interruptor)
- *
- * Copyright (c) 2016-2017 Sagar Karandikar, sagark@eecs.berkeley.edu
- * Copyright (c) 2017 SiFive, Inc.
- *
- * This provides real-time clock, timer and interprocessor interrupts.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2 or later, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope 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 "qapi/error.h"
-#include "qemu/error-report.h"
-#include "qemu/module.h"
-#include "hw/sysbus.h"
-#include "target/riscv/cpu.h"
-#include "hw/qdev-properties.h"
-#include "hw/intc/sifive_clint.h"
-#include "qemu/timer.h"
-
-static uint64_t cpu_riscv_read_rtc(uint32_t timebase_freq)
-{
- return muldiv64(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL),
- timebase_freq, NANOSECONDS_PER_SECOND);
-}
-
-/*
- * Called when timecmp is written to update the QEMU timer or immediately
- * trigger timer interrupt if mtimecmp <= current timer value.
- */
-static void sifive_clint_write_timecmp(RISCVCPU *cpu, uint64_t value,
- uint32_t timebase_freq)
-{
- uint64_t next;
- uint64_t diff;
-
- uint64_t rtc_r = cpu_riscv_read_rtc(timebase_freq);
-
- cpu->env.timecmp = value;
- if (cpu->env.timecmp <= rtc_r) {
- /* if we're setting an MTIMECMP value in the "past",
- immediately raise the timer interrupt */
- riscv_cpu_update_mip(cpu, MIP_MTIP, BOOL_TO_MASK(1));
- return;
- }
-
- /* otherwise, set up the future timer interrupt */
- riscv_cpu_update_mip(cpu, MIP_MTIP, BOOL_TO_MASK(0));
- diff = cpu->env.timecmp - rtc_r;
- /* back to ns (note args switched in muldiv64) */
- uint64_t ns_diff = muldiv64(diff, NANOSECONDS_PER_SECOND, timebase_freq);
-
- /*
- * check if ns_diff overflowed and check if the addition would potentially
- * overflow
- */
- if ((NANOSECONDS_PER_SECOND > timebase_freq && ns_diff < diff) ||
- ns_diff > INT64_MAX) {
- next = INT64_MAX;
- } else {
- /*
- * as it is very unlikely qemu_clock_get_ns will return a value
- * greater than INT64_MAX, no additional check is needed for an
- * unsigned integer overflow.
- */
- next = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + ns_diff;
- /*
- * if ns_diff is INT64_MAX next may still be outside the range
- * of a signed integer.
- */
- next = MIN(next, INT64_MAX);
- }
-
- timer_mod(cpu->env.timer, next);
-}
-
-/*
- * Callback used when the timer set using timer_mod expires.
- * Should raise the timer interrupt line
- */
-static void sifive_clint_timer_cb(void *opaque)
-{
- RISCVCPU *cpu = opaque;
- riscv_cpu_update_mip(cpu, MIP_MTIP, BOOL_TO_MASK(1));
-}
-
-/* CPU wants to read rtc or timecmp register */
-static uint64_t sifive_clint_read(void *opaque, hwaddr addr, unsigned size)
-{
- SiFiveCLINTState *clint = opaque;
- if (addr >= clint->sip_base &&
- addr < clint->sip_base + (clint->num_harts << 2)) {
- size_t hartid = clint->hartid_base + ((addr - clint->sip_base) >> 2);
- CPUState *cpu = qemu_get_cpu(hartid);
- CPURISCVState *env = cpu ? cpu->env_ptr : NULL;
- if (!env) {
- error_report("clint: invalid timecmp hartid: %zu", hartid);
- } else if ((addr & 0x3) == 0) {
- return (env->mip & MIP_MSIP) > 0;
- } else {
- error_report("clint: invalid read: %08x", (uint32_t)addr);
- return 0;
- }
- } else if (addr >= clint->timecmp_base &&
- addr < clint->timecmp_base + (clint->num_harts << 3)) {
- size_t hartid = clint->hartid_base +
- ((addr - clint->timecmp_base) >> 3);
- CPUState *cpu = qemu_get_cpu(hartid);
- CPURISCVState *env = cpu ? cpu->env_ptr : NULL;
- if (!env) {
- error_report("clint: invalid timecmp hartid: %zu", hartid);
- } else if ((addr & 0x7) == 0) {
- /* timecmp_lo */
- uint64_t timecmp = env->timecmp;
- return timecmp & 0xFFFFFFFF;
- } else if ((addr & 0x7) == 4) {
- /* timecmp_hi */
- uint64_t timecmp = env->timecmp;
- return (timecmp >> 32) & 0xFFFFFFFF;
- } else {
- error_report("clint: invalid read: %08x", (uint32_t)addr);
- return 0;
- }
- } else if (addr == clint->time_base) {
- /* time_lo */
- return cpu_riscv_read_rtc(clint->timebase_freq) & 0xFFFFFFFF;
- } else if (addr == clint->time_base + 4) {
- /* time_hi */
- return (cpu_riscv_read_rtc(clint->timebase_freq) >> 32) & 0xFFFFFFFF;
- }
-
- error_report("clint: invalid read: %08x", (uint32_t)addr);
- return 0;
-}
-
-/* CPU wrote to rtc or timecmp register */
-static void sifive_clint_write(void *opaque, hwaddr addr, uint64_t value,
- unsigned size)
-{
- SiFiveCLINTState *clint = opaque;
-
- if (addr >= clint->sip_base &&
- addr < clint->sip_base + (clint->num_harts << 2)) {
- size_t hartid = clint->hartid_base + ((addr - clint->sip_base) >> 2);
- CPUState *cpu = qemu_get_cpu(hartid);
- CPURISCVState *env = cpu ? cpu->env_ptr : NULL;
- if (!env) {
- error_report("clint: invalid timecmp hartid: %zu", hartid);
- } else if ((addr & 0x3) == 0) {
- riscv_cpu_update_mip(RISCV_CPU(cpu), MIP_MSIP, BOOL_TO_MASK(value));
- } else {
- error_report("clint: invalid sip write: %08x", (uint32_t)addr);
- }
- return;
- } else if (addr >= clint->timecmp_base &&
- addr < clint->timecmp_base + (clint->num_harts << 3)) {
- size_t hartid = clint->hartid_base +
- ((addr - clint->timecmp_base) >> 3);
- CPUState *cpu = qemu_get_cpu(hartid);
- CPURISCVState *env = cpu ? cpu->env_ptr : NULL;
- if (!env) {
- error_report("clint: invalid timecmp hartid: %zu", hartid);
- } else if ((addr & 0x7) == 0) {
- /* timecmp_lo */
- uint64_t timecmp_hi = env->timecmp >> 32;
- sifive_clint_write_timecmp(RISCV_CPU(cpu),
- timecmp_hi << 32 | (value & 0xFFFFFFFF), clint->timebase_freq);
- return;
- } else if ((addr & 0x7) == 4) {
- /* timecmp_hi */
- uint64_t timecmp_lo = env->timecmp;
- sifive_clint_write_timecmp(RISCV_CPU(cpu),
- value << 32 | (timecmp_lo & 0xFFFFFFFF), clint->timebase_freq);
- } else {
- error_report("clint: invalid timecmp write: %08x", (uint32_t)addr);
- }
- return;
- } else if (addr == clint->time_base) {
- /* time_lo */
- error_report("clint: time_lo write not implemented");
- return;
- } else if (addr == clint->time_base + 4) {
- /* time_hi */
- error_report("clint: time_hi write not implemented");
- return;
- }
-
- error_report("clint: invalid write: %08x", (uint32_t)addr);
-}
-
-static const MemoryRegionOps sifive_clint_ops = {
- .read = sifive_clint_read,
- .write = sifive_clint_write,
- .endianness = DEVICE_LITTLE_ENDIAN,
- .valid = {
- .min_access_size = 4,
- .max_access_size = 8
- }
-};
-
-static Property sifive_clint_properties[] = {
- DEFINE_PROP_UINT32("hartid-base", SiFiveCLINTState, hartid_base, 0),
- DEFINE_PROP_UINT32("num-harts", SiFiveCLINTState, num_harts, 0),
- DEFINE_PROP_UINT32("sip-base", SiFiveCLINTState, sip_base, 0),
- DEFINE_PROP_UINT32("timecmp-base", SiFiveCLINTState, timecmp_base, 0),
- DEFINE_PROP_UINT32("time-base", SiFiveCLINTState, time_base, 0),
- DEFINE_PROP_UINT32("aperture-size", SiFiveCLINTState, aperture_size, 0),
- DEFINE_PROP_UINT32("timebase-freq", SiFiveCLINTState, timebase_freq, 0),
- DEFINE_PROP_END_OF_LIST(),
-};
-
-static void sifive_clint_realize(DeviceState *dev, Error **errp)
-{
- SiFiveCLINTState *s = SIFIVE_CLINT(dev);
- memory_region_init_io(&s->mmio, OBJECT(dev), &sifive_clint_ops, s,
- TYPE_SIFIVE_CLINT, s->aperture_size);
- sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->mmio);
-}
-
-static void sifive_clint_class_init(ObjectClass *klass, void *data)
-{
- DeviceClass *dc = DEVICE_CLASS(klass);
- dc->realize = sifive_clint_realize;
- device_class_set_props(dc, sifive_clint_properties);
-}
-
-static const TypeInfo sifive_clint_info = {
- .name = TYPE_SIFIVE_CLINT,
- .parent = TYPE_SYS_BUS_DEVICE,
- .instance_size = sizeof(SiFiveCLINTState),
- .class_init = sifive_clint_class_init,
-};
-
-static void sifive_clint_register_types(void)
-{
- type_register_static(&sifive_clint_info);
-}
-
-type_init(sifive_clint_register_types)
-
-
-/*
- * Create CLINT device.
- */
-DeviceState *sifive_clint_create(hwaddr addr, hwaddr size,
- uint32_t hartid_base, uint32_t num_harts, uint32_t sip_base,
- uint32_t timecmp_base, uint32_t time_base, uint32_t timebase_freq,
- bool provide_rdtime)
-{
- int i;
- for (i = 0; i < num_harts; i++) {
- CPUState *cpu = qemu_get_cpu(hartid_base + i);
- CPURISCVState *env = cpu ? cpu->env_ptr : NULL;
- if (!env) {
- continue;
- }
- if (provide_rdtime) {
- riscv_cpu_set_rdtime_fn(env, cpu_riscv_read_rtc, timebase_freq);
- }
- env->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL,
- &sifive_clint_timer_cb, cpu);
- env->timecmp = 0;
- }
-
- DeviceState *dev = qdev_new(TYPE_SIFIVE_CLINT);
- qdev_prop_set_uint32(dev, "hartid-base", hartid_base);
- qdev_prop_set_uint32(dev, "num-harts", num_harts);
- qdev_prop_set_uint32(dev, "sip-base", sip_base);
- qdev_prop_set_uint32(dev, "timecmp-base", timecmp_base);
- qdev_prop_set_uint32(dev, "time-base", time_base);
- qdev_prop_set_uint32(dev, "aperture-size", size);
- qdev_prop_set_uint32(dev, "timebase-freq", timebase_freq);
- sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal);
- sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, addr);
- return dev;
-}
diff --git a/hw/intc/sifive_plic.c b/hw/intc/sifive_plic.c
index 78903beb06..9ba36dc0b3 100644
--- a/hw/intc/sifive_plic.c
+++ b/hw/intc/sifive_plic.c
@@ -29,6 +29,7 @@
#include "hw/intc/sifive_plic.h"
#include "target/riscv/cpu.h"
#include "migration/vmstate.h"
+#include "hw/irq.h"
#define RISCV_DEBUG_PLIC 0
@@ -139,18 +140,14 @@ static void sifive_plic_update(SiFivePLICState *plic)
for (addrid = 0; addrid < plic->num_addrs; addrid++) {
uint32_t hartid = plic->addr_config[addrid].hartid;
PLICMode mode = plic->addr_config[addrid].mode;
- CPUState *cpu = qemu_get_cpu(hartid);
- CPURISCVState *env = cpu ? cpu->env_ptr : NULL;
- if (!env) {
- continue;
- }
int level = sifive_plic_irqs_pending(plic, addrid);
+
switch (mode) {
case PLICMode_M:
- riscv_cpu_update_mip(RISCV_CPU(cpu), MIP_MEIP, BOOL_TO_MASK(level));
+ qemu_set_irq(plic->m_external_irqs[hartid - plic->hartid_base], level);
break;
case PLICMode_S:
- riscv_cpu_update_mip(RISCV_CPU(cpu), MIP_SEIP, BOOL_TO_MASK(level));
+ qemu_set_irq(plic->s_external_irqs[hartid - plic->hartid_base], level);
break;
default:
break;
@@ -456,6 +453,12 @@ static void sifive_plic_realize(DeviceState *dev, Error **errp)
sysbus_init_mmio(SYS_BUS_DEVICE(dev), &plic->mmio);
qdev_init_gpio_in(dev, sifive_plic_irq_request, plic->num_sources);
+ plic->s_external_irqs = g_malloc(sizeof(qemu_irq) * plic->num_harts);
+ qdev_init_gpio_out(dev, plic->s_external_irqs, plic->num_harts);
+
+ plic->m_external_irqs = g_malloc(sizeof(qemu_irq) * plic->num_harts);
+ qdev_init_gpio_out(dev, plic->m_external_irqs, plic->num_harts);
+
/* We can't allow the supervisor to control SEIP as this would allow the
* supervisor to clear a pending external interrupt which will result in
* lost a interrupt in the case a PLIC is attached. The SEIP bit must be
@@ -520,6 +523,7 @@ type_init(sifive_plic_register_types)
* Create PLIC device.
*/
DeviceState *sifive_plic_create(hwaddr addr, char *hart_config,
+ uint32_t num_harts,
uint32_t hartid_base, uint32_t num_sources,
uint32_t num_priorities, uint32_t priority_base,
uint32_t pending_base, uint32_t enable_base,
@@ -527,6 +531,8 @@ DeviceState *sifive_plic_create(hwaddr addr, char *hart_config,
uint32_t context_stride, uint32_t aperture_size)
{
DeviceState *dev = qdev_new(TYPE_SIFIVE_PLIC);
+ int i;
+
assert(enable_stride == (enable_stride & -enable_stride));
assert(context_stride == (context_stride & -context_stride));
qdev_prop_set_string(dev, "hart-config", hart_config);
@@ -542,5 +548,15 @@ DeviceState *sifive_plic_create(hwaddr addr, char *hart_config,
qdev_prop_set_uint32(dev, "aperture-size", aperture_size);
sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal);
sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, addr);
+
+ for (i = 0; i < num_harts; i++) {
+ CPUState *cpu = qemu_get_cpu(hartid_base + i);
+
+ qdev_connect_gpio_out(dev, i,
+ qdev_get_gpio_in(DEVICE(cpu), IRQ_S_EXT));
+ qdev_connect_gpio_out(dev, num_harts + i,
+ qdev_get_gpio_in(DEVICE(cpu), IRQ_M_EXT));
+ }
+
return dev;
}