aboutsummaryrefslogtreecommitdiff
path: root/hw
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
parent81ceb36b965c9d5ed5b1eb0ed80e23705802de15 (diff)
parented481d9837250aa682f5156528bc923e1b214f76 (diff)
downloadqemu-2c3e83f92d93fbab071b8a96b8ab769b01902475.zip
qemu-2c3e83f92d93fbab071b8a96b8ab769b01902475.tar.gz
qemu-2c3e83f92d93fbab071b8a96b8ab769b01902475.tar.bz2
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')
-rw-r--r--hw/dma/sifive_pdma.c54
-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
-rw-r--r--hw/riscv/Kconfig13
-rw-r--r--hw/riscv/microchip_pfsoc.c13
-rw-r--r--hw/riscv/opentitan.c13
-rw-r--r--hw/riscv/shakti_c.c16
-rw-r--r--hw/riscv/sifive_e.c15
-rw-r--r--hw/riscv/sifive_u.c68
-rw-r--r--hw/riscv/spike.c16
-rw-r--r--hw/riscv/virt.c654
-rw-r--r--hw/timer/Kconfig3
-rw-r--r--hw/timer/ibex_timer.c17
-rw-r--r--hw/timer/meson.build1
-rw-r--r--hw/timer/sifive_pwm.c468
-rw-r--r--hw/timer/trace-events6
20 files changed, 1590 insertions, 565 deletions
diff --git a/hw/dma/sifive_pdma.c b/hw/dma/sifive_pdma.c
index 9b2ac20..b4fd405 100644
--- a/hw/dma/sifive_pdma.c
+++ b/hw/dma/sifive_pdma.c
@@ -54,6 +54,13 @@
#define DMA_EXEC_DST 0x110
#define DMA_EXEC_SRC 0x118
+/*
+ * FU540/FU740 docs are incorrect with NextConfig.wsize/rsize reset values.
+ * The reset values tested on Unleashed/Unmatched boards are 6 instead of 0.
+ */
+#define CONFIG_WRSZ_DEFAULT 6
+#define CONFIG_RDSZ_DEFAULT 6
+
enum dma_chan_state {
DMA_CHAN_STATE_IDLE,
DMA_CHAN_STATE_STARTED,
@@ -67,13 +74,13 @@ static void sifive_pdma_run(SiFivePDMAState *s, int ch)
uint64_t dst = s->chan[ch].next_dst;
uint64_t src = s->chan[ch].next_src;
uint32_t config = s->chan[ch].next_config;
- int wsize, rsize, size;
+ int wsize, rsize, size, remainder;
uint8_t buf[64];
int n;
/* do nothing if bytes to transfer is zero */
if (!bytes) {
- goto error;
+ goto done;
}
/*
@@ -99,11 +106,7 @@ static void sifive_pdma_run(SiFivePDMAState *s, int ch)
size = 6;
}
size = 1 << size;
-
- /* the bytes to transfer should be multiple of transaction size */
- if (bytes % size) {
- goto error;
- }
+ remainder = bytes % size;
/* indicate a DMA transfer is started */
s->chan[ch].state = DMA_CHAN_STATE_STARTED;
@@ -124,10 +127,13 @@ static void sifive_pdma_run(SiFivePDMAState *s, int ch)
s->chan[ch].exec_bytes -= size;
}
- /* indicate a DMA transfer is done */
- s->chan[ch].state = DMA_CHAN_STATE_DONE;
- s->chan[ch].control &= ~CONTROL_RUN;
- s->chan[ch].control |= CONTROL_DONE;
+ if (remainder) {
+ cpu_physical_memory_read(s->chan[ch].exec_src, buf, remainder);
+ cpu_physical_memory_write(s->chan[ch].exec_dst, buf, remainder);
+ s->chan[ch].exec_src += remainder;
+ s->chan[ch].exec_dst += remainder;
+ s->chan[ch].exec_bytes -= remainder;
+ }
/* reload exec_ registers if repeat is required */
if (s->chan[ch].next_config & CONFIG_REPEAT) {
@@ -136,6 +142,11 @@ static void sifive_pdma_run(SiFivePDMAState *s, int ch)
s->chan[ch].exec_src = src;
}
+done:
+ /* indicate a DMA transfer is done */
+ s->chan[ch].state = DMA_CHAN_STATE_DONE;
+ s->chan[ch].control &= ~CONTROL_RUN;
+ s->chan[ch].control |= CONTROL_DONE;
return;
error:
@@ -221,6 +232,7 @@ static void sifive_pdma_write(void *opaque, hwaddr offset,
{
SiFivePDMAState *s = opaque;
int ch = SIFIVE_PDMA_CHAN_NO(offset);
+ bool claimed;
if (ch >= SIFIVE_PDMA_CHANS) {
qemu_log_mask(LOG_GUEST_ERROR, "%s: Invalid channel no %d\n",
@@ -231,8 +243,28 @@ static void sifive_pdma_write(void *opaque, hwaddr offset,
offset &= 0xfff;
switch (offset) {
case DMA_CONTROL:
+ claimed = !!s->chan[ch].control & CONTROL_CLAIM;
+
+ if (!claimed && (value & CONTROL_CLAIM)) {
+ /* reset Next* registers */
+ s->chan[ch].next_config = (CONFIG_RDSZ_DEFAULT << CONFIG_RDSZ_SHIFT) |
+ (CONFIG_WRSZ_DEFAULT << CONFIG_WRSZ_SHIFT);
+ s->chan[ch].next_bytes = 0;
+ s->chan[ch].next_dst = 0;
+ s->chan[ch].next_src = 0;
+ }
+
s->chan[ch].control = value;
+ /*
+ * If channel was not claimed before run bit is set,
+ * DMA won't run.
+ */
+ if (!claimed) {
+ s->chan[ch].control &= ~CONTROL_RUN;
+ return;
+ }
+
if (value & CONTROL_RUN) {
sifive_pdma_run(s, ch);
}
diff --git a/hw/intc/Kconfig b/hw/intc/Kconfig
index f469408..78aed93 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 edf76e4..ff43035 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 4dcfea6..a1d00aa 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 0000000..f1a5d3d
--- /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 99c870c..0000000
--- 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 78903be..9ba36dc 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;
}
diff --git a/hw/riscv/Kconfig b/hw/riscv/Kconfig
index ff75add..d2d869a 100644
--- a/hw/riscv/Kconfig
+++ b/hw/riscv/Kconfig
@@ -12,7 +12,7 @@ config MICROCHIP_PFSOC
select MCHP_PFSOC_MMUART
select MCHP_PFSOC_SYSREG
select MSI_NONBROKEN
- select SIFIVE_CLINT
+ select RISCV_ACLINT
select SIFIVE_PDMA
select SIFIVE_PLIC
select UNIMP
@@ -26,7 +26,7 @@ config SHAKTI_C
bool
select UNIMP
select SHAKTI_UART
- select SIFIVE_CLINT
+ select RISCV_ACLINT
select SIFIVE_PLIC
config RISCV_VIRT
@@ -41,7 +41,7 @@ config RISCV_VIRT
select PCI_EXPRESS_GENERIC_BRIDGE
select PFLASH_CFI01
select SERIAL
- select SIFIVE_CLINT
+ select RISCV_ACLINT
select SIFIVE_PLIC
select SIFIVE_TEST
select VIRTIO_MMIO
@@ -50,7 +50,7 @@ config RISCV_VIRT
config SIFIVE_E
bool
select MSI_NONBROKEN
- select SIFIVE_CLINT
+ select RISCV_ACLINT
select SIFIVE_GPIO
select SIFIVE_PLIC
select SIFIVE_UART
@@ -61,7 +61,7 @@ config SIFIVE_U
bool
select CADENCE
select MSI_NONBROKEN
- select SIFIVE_CLINT
+ select RISCV_ACLINT
select SIFIVE_GPIO
select SIFIVE_PDMA
select SIFIVE_PLIC
@@ -69,6 +69,7 @@ config SIFIVE_U
select SIFIVE_UART
select SIFIVE_U_OTP
select SIFIVE_U_PRCI
+ select SIFIVE_PWM
select SSI_M25P80
select SSI_SD
select UNIMP
@@ -78,5 +79,5 @@ config SPIKE
select RISCV_NUMA
select HTIF
select MSI_NONBROKEN
- select SIFIVE_CLINT
+ select RISCV_ACLINT
select SIFIVE_PLIC
diff --git a/hw/riscv/microchip_pfsoc.c b/hw/riscv/microchip_pfsoc.c
index eb8e79e..e475b6d 100644
--- a/hw/riscv/microchip_pfsoc.c
+++ b/hw/riscv/microchip_pfsoc.c
@@ -49,7 +49,7 @@
#include "hw/riscv/boot.h"
#include "hw/riscv/riscv_hart.h"
#include "hw/riscv/microchip_pfsoc.h"
-#include "hw/intc/sifive_clint.h"
+#include "hw/intc/riscv_aclint.h"
#include "hw/intc/sifive_plic.h"
#include "sysemu/device_tree.h"
#include "sysemu/sysemu.h"
@@ -234,9 +234,12 @@ static void microchip_pfsoc_soc_realize(DeviceState *dev, Error **errp)
memmap[MICROCHIP_PFSOC_BUSERR_UNIT4].size);
/* CLINT */
- sifive_clint_create(memmap[MICROCHIP_PFSOC_CLINT].base,
- memmap[MICROCHIP_PFSOC_CLINT].size, 0, ms->smp.cpus,
- SIFIVE_SIP_BASE, SIFIVE_TIMECMP_BASE, SIFIVE_TIME_BASE,
+ riscv_aclint_swi_create(memmap[MICROCHIP_PFSOC_CLINT].base,
+ 0, ms->smp.cpus, false);
+ riscv_aclint_mtimer_create(
+ memmap[MICROCHIP_PFSOC_CLINT].base + RISCV_ACLINT_SWI_SIZE,
+ RISCV_ACLINT_DEFAULT_MTIMER_SIZE, 0, ms->smp.cpus,
+ RISCV_ACLINT_DEFAULT_MTIMECMP, RISCV_ACLINT_DEFAULT_MTIME,
CLINT_TIMEBASE_FREQ, false);
/* L2 cache controller */
@@ -274,7 +277,7 @@ static void microchip_pfsoc_soc_realize(DeviceState *dev, Error **errp)
/* PLIC */
s->plic = sifive_plic_create(memmap[MICROCHIP_PFSOC_PLIC].base,
- plic_hart_config, 0,
+ plic_hart_config, ms->smp.cpus, 0,
MICROCHIP_PFSOC_PLIC_NUM_SOURCES,
MICROCHIP_PFSOC_PLIC_NUM_PRIORITIES,
MICROCHIP_PFSOC_PLIC_PRIORITY_BASE,
diff --git a/hw/riscv/opentitan.c b/hw/riscv/opentitan.c
index 36a41c8..9803ae6 100644
--- a/hw/riscv/opentitan.c
+++ b/hw/riscv/opentitan.c
@@ -39,12 +39,12 @@ static const MemMapEntry ibex_memmap[] = {
[IBEX_DEV_TIMER] = { 0x40100000, 0x1000 },
[IBEX_DEV_SENSOR_CTRL] = { 0x40110000, 0x1000 },
[IBEX_DEV_OTP_CTRL] = { 0x40130000, 0x4000 },
+ [IBEX_DEV_USBDEV] = { 0x40150000, 0x1000 },
[IBEX_DEV_PWRMGR] = { 0x40400000, 0x1000 },
[IBEX_DEV_RSTMGR] = { 0x40410000, 0x1000 },
[IBEX_DEV_CLKMGR] = { 0x40420000, 0x1000 },
[IBEX_DEV_PINMUX] = { 0x40460000, 0x1000 },
[IBEX_DEV_PADCTRL] = { 0x40470000, 0x1000 },
- [IBEX_DEV_USBDEV] = { 0x40500000, 0x1000 },
[IBEX_DEV_FLASH_CTRL] = { 0x41000000, 0x1000 },
[IBEX_DEV_PLIC] = { 0x41010000, 0x1000 },
[IBEX_DEV_AES] = { 0x41100000, 0x1000 },
@@ -118,6 +118,7 @@ static void lowrisc_ibex_soc_realize(DeviceState *dev_soc, Error **errp)
MachineState *ms = MACHINE(qdev_get_machine());
LowRISCIbexSoCState *s = RISCV_IBEX_SOC(dev_soc);
MemoryRegion *sys_mem = get_system_memory();
+ int i;
object_property_set_str(OBJECT(&s->cpus), "cpu-type", ms->cpu_type,
&error_abort);
@@ -149,6 +150,13 @@ static void lowrisc_ibex_soc_realize(DeviceState *dev_soc, Error **errp)
}
sysbus_mmio_map(SYS_BUS_DEVICE(&s->plic), 0, memmap[IBEX_DEV_PLIC].base);
+ for (i = 0; i < ms->smp.cpus; i++) {
+ CPUState *cpu = qemu_get_cpu(i);
+
+ qdev_connect_gpio_out(DEVICE(&s->plic), i,
+ qdev_get_gpio_in(DEVICE(cpu), IRQ_M_EXT));
+ }
+
/* UART */
qdev_prop_set_chr(DEVICE(&(s->uart)), "chardev", serial_hd(0));
if (!sysbus_realize(SYS_BUS_DEVICE(&s->uart), errp)) {
@@ -175,6 +183,9 @@ static void lowrisc_ibex_soc_realize(DeviceState *dev_soc, Error **errp)
sysbus_connect_irq(SYS_BUS_DEVICE(&s->timer),
0, qdev_get_gpio_in(DEVICE(&s->plic),
IBEX_TIMER_TIMEREXPIRED0_0));
+ qdev_connect_gpio_out(DEVICE(&s->timer), 0,
+ qdev_get_gpio_in(DEVICE(qemu_get_cpu(0)),
+ IRQ_M_TIMER));
create_unimplemented_device("riscv.lowrisc.ibex.gpio",
memmap[IBEX_DEV_GPIO].base, memmap[IBEX_DEV_GPIO].size);
diff --git a/hw/riscv/shakti_c.c b/hw/riscv/shakti_c.c
index 18f70fa..2f084d3 100644
--- a/hw/riscv/shakti_c.c
+++ b/hw/riscv/shakti_c.c
@@ -21,7 +21,7 @@
#include "hw/riscv/shakti_c.h"
#include "qapi/error.h"
#include "hw/intc/sifive_plic.h"
-#include "hw/intc/sifive_clint.h"
+#include "hw/intc/riscv_aclint.h"
#include "sysemu/sysemu.h"
#include "hw/qdev-properties.h"
#include "exec/address-spaces.h"
@@ -106,13 +106,14 @@ type_init(shakti_c_machine_type_info_register)
static void shakti_c_soc_state_realize(DeviceState *dev, Error **errp)
{
+ MachineState *ms = MACHINE(qdev_get_machine());
ShaktiCSoCState *sss = RISCV_SHAKTI_SOC(dev);
MemoryRegion *system_memory = get_system_memory();
sysbus_realize(SYS_BUS_DEVICE(&sss->cpus), &error_abort);
sss->plic = sifive_plic_create(shakti_c_memmap[SHAKTI_C_PLIC].base,
- (char *)SHAKTI_C_PLIC_HART_CONFIG, 0,
+ (char *)SHAKTI_C_PLIC_HART_CONFIG, ms->smp.cpus, 0,
SHAKTI_C_PLIC_NUM_SOURCES,
SHAKTI_C_PLIC_NUM_PRIORITIES,
SHAKTI_C_PLIC_PRIORITY_BASE,
@@ -123,10 +124,13 @@ static void shakti_c_soc_state_realize(DeviceState *dev, Error **errp)
SHAKTI_C_PLIC_CONTEXT_STRIDE,
shakti_c_memmap[SHAKTI_C_PLIC].size);
- sifive_clint_create(shakti_c_memmap[SHAKTI_C_CLINT].base,
- shakti_c_memmap[SHAKTI_C_CLINT].size, 0, 1,
- SIFIVE_SIP_BASE, SIFIVE_TIMECMP_BASE, SIFIVE_TIME_BASE,
- SIFIVE_CLINT_TIMEBASE_FREQ, false);
+ riscv_aclint_swi_create(shakti_c_memmap[SHAKTI_C_CLINT].base,
+ 0, 1, false);
+ riscv_aclint_mtimer_create(shakti_c_memmap[SHAKTI_C_CLINT].base +
+ RISCV_ACLINT_SWI_SIZE,
+ RISCV_ACLINT_DEFAULT_MTIMER_SIZE, 0, 1,
+ RISCV_ACLINT_DEFAULT_MTIMECMP, RISCV_ACLINT_DEFAULT_MTIME,
+ RISCV_ACLINT_DEFAULT_TIMEBASE_FREQ, false);
qdev_prop_set_chr(DEVICE(&(sss->uart)), "chardev", serial_hd(0));
if (!sysbus_realize(SYS_BUS_DEVICE(&sss->uart), errp)) {
diff --git a/hw/riscv/sifive_e.c b/hw/riscv/sifive_e.c
index 5b7b245..6e95ea5 100644
--- a/hw/riscv/sifive_e.c
+++ b/hw/riscv/sifive_e.c
@@ -41,7 +41,7 @@
#include "hw/riscv/sifive_e.h"
#include "hw/riscv/boot.h"
#include "hw/char/sifive_uart.h"
-#include "hw/intc/sifive_clint.h"
+#include "hw/intc/riscv_aclint.h"
#include "hw/intc/sifive_plic.h"
#include "hw/misc/sifive_e_prci.h"
#include "chardev/char.h"
@@ -197,7 +197,7 @@ static void sifive_e_soc_realize(DeviceState *dev, Error **errp)
/* MMIO */
s->plic = sifive_plic_create(memmap[SIFIVE_E_DEV_PLIC].base,
- (char *)SIFIVE_E_PLIC_HART_CONFIG, 0,
+ (char *)SIFIVE_E_PLIC_HART_CONFIG, ms->smp.cpus, 0,
SIFIVE_E_PLIC_NUM_SOURCES,
SIFIVE_E_PLIC_NUM_PRIORITIES,
SIFIVE_E_PLIC_PRIORITY_BASE,
@@ -207,10 +207,13 @@ static void sifive_e_soc_realize(DeviceState *dev, Error **errp)
SIFIVE_E_PLIC_CONTEXT_BASE,
SIFIVE_E_PLIC_CONTEXT_STRIDE,
memmap[SIFIVE_E_DEV_PLIC].size);
- sifive_clint_create(memmap[SIFIVE_E_DEV_CLINT].base,
- memmap[SIFIVE_E_DEV_CLINT].size, 0, ms->smp.cpus,
- SIFIVE_SIP_BASE, SIFIVE_TIMECMP_BASE, SIFIVE_TIME_BASE,
- SIFIVE_CLINT_TIMEBASE_FREQ, false);
+ riscv_aclint_swi_create(memmap[SIFIVE_E_DEV_CLINT].base,
+ 0, ms->smp.cpus, false);
+ riscv_aclint_mtimer_create(memmap[SIFIVE_E_DEV_CLINT].base +
+ RISCV_ACLINT_SWI_SIZE,
+ RISCV_ACLINT_DEFAULT_MTIMER_SIZE, 0, ms->smp.cpus,
+ RISCV_ACLINT_DEFAULT_MTIMECMP, RISCV_ACLINT_DEFAULT_MTIME,
+ RISCV_ACLINT_DEFAULT_TIMEBASE_FREQ, false);
create_unimplemented_device("riscv.sifive.e.aon",
memmap[SIFIVE_E_DEV_AON].base, memmap[SIFIVE_E_DEV_AON].size);
sifive_e_prci_create(memmap[SIFIVE_E_DEV_PRCI].base);
diff --git a/hw/riscv/sifive_u.c b/hw/riscv/sifive_u.c
index 6cc1a62..fc5790b 100644
--- a/hw/riscv/sifive_u.c
+++ b/hw/riscv/sifive_u.c
@@ -17,6 +17,7 @@
* 7) DMA (Direct Memory Access Controller)
* 8) SPI0 connected to an SPI flash
* 9) SPI2 connected to an SD card
+ * 10) PWM0 and PWM1
*
* This board currently generates devicetree dynamically that indicates at least
* two harts and up to five harts.
@@ -51,7 +52,7 @@
#include "hw/riscv/sifive_u.h"
#include "hw/riscv/boot.h"
#include "hw/char/sifive_uart.h"
-#include "hw/intc/sifive_clint.h"
+#include "hw/intc/riscv_aclint.h"
#include "hw/intc/sifive_plic.h"
#include "chardev/char.h"
#include "net/eth.h"
@@ -75,6 +76,8 @@ static const MemMapEntry sifive_u_memmap[] = {
[SIFIVE_U_DEV_PRCI] = { 0x10000000, 0x1000 },
[SIFIVE_U_DEV_UART0] = { 0x10010000, 0x1000 },
[SIFIVE_U_DEV_UART1] = { 0x10011000, 0x1000 },
+ [SIFIVE_U_DEV_PWM0] = { 0x10020000, 0x1000 },
+ [SIFIVE_U_DEV_PWM1] = { 0x10021000, 0x1000 },
[SIFIVE_U_DEV_QSPI0] = { 0x10040000, 0x1000 },
[SIFIVE_U_DEV_QSPI2] = { 0x10050000, 0x1000 },
[SIFIVE_U_DEV_GPIO] = { 0x10060000, 0x1000 },
@@ -441,6 +444,38 @@ static void create_fdt(SiFiveUState *s, const MemMapEntry *memmap,
qemu_fdt_setprop_cell(fdt, nodename, "reg", 0x0);
g_free(nodename);
+ nodename = g_strdup_printf("/soc/pwm@%lx",
+ (long)memmap[SIFIVE_U_DEV_PWM0].base);
+ qemu_fdt_add_subnode(fdt, nodename);
+ qemu_fdt_setprop_string(fdt, nodename, "compatible", "sifive,pwm0");
+ qemu_fdt_setprop_cells(fdt, nodename, "reg",
+ 0x0, memmap[SIFIVE_U_DEV_PWM0].base,
+ 0x0, memmap[SIFIVE_U_DEV_PWM0].size);
+ qemu_fdt_setprop_cell(fdt, nodename, "interrupt-parent", plic_phandle);
+ qemu_fdt_setprop_cells(fdt, nodename, "interrupts",
+ SIFIVE_U_PWM0_IRQ0, SIFIVE_U_PWM0_IRQ1,
+ SIFIVE_U_PWM0_IRQ2, SIFIVE_U_PWM0_IRQ3);
+ qemu_fdt_setprop_cells(fdt, nodename, "clocks",
+ prci_phandle, PRCI_CLK_TLCLK);
+ qemu_fdt_setprop_cell(fdt, nodename, "#pwm-cells", 0);
+ g_free(nodename);
+
+ nodename = g_strdup_printf("/soc/pwm@%lx",
+ (long)memmap[SIFIVE_U_DEV_PWM1].base);
+ qemu_fdt_add_subnode(fdt, nodename);
+ qemu_fdt_setprop_string(fdt, nodename, "compatible", "sifive,pwm0");
+ qemu_fdt_setprop_cells(fdt, nodename, "reg",
+ 0x0, memmap[SIFIVE_U_DEV_PWM1].base,
+ 0x0, memmap[SIFIVE_U_DEV_PWM1].size);
+ qemu_fdt_setprop_cell(fdt, nodename, "interrupt-parent", plic_phandle);
+ qemu_fdt_setprop_cells(fdt, nodename, "interrupts",
+ SIFIVE_U_PWM1_IRQ0, SIFIVE_U_PWM1_IRQ1,
+ SIFIVE_U_PWM1_IRQ2, SIFIVE_U_PWM1_IRQ3);
+ qemu_fdt_setprop_cells(fdt, nodename, "clocks",
+ prci_phandle, PRCI_CLK_TLCLK);
+ qemu_fdt_setprop_cell(fdt, nodename, "#pwm-cells", 0);
+ g_free(nodename);
+
nodename = g_strdup_printf("/soc/serial@%lx",
(long)memmap[SIFIVE_U_DEV_UART1].base);
qemu_fdt_add_subnode(fdt, nodename);
@@ -765,6 +800,8 @@ static void sifive_u_soc_instance_init(Object *obj)
object_initialize_child(obj, "pdma", &s->dma, TYPE_SIFIVE_PDMA);
object_initialize_child(obj, "spi0", &s->spi0, TYPE_SIFIVE_SPI);
object_initialize_child(obj, "spi2", &s->spi2, TYPE_SIFIVE_SPI);
+ object_initialize_child(obj, "pwm0", &s->pwm[0], TYPE_SIFIVE_PWM);
+ object_initialize_child(obj, "pwm1", &s->pwm[1], TYPE_SIFIVE_PWM);
}
static void sifive_u_soc_realize(DeviceState *dev, Error **errp)
@@ -777,7 +814,7 @@ static void sifive_u_soc_realize(DeviceState *dev, Error **errp)
MemoryRegion *l2lim_mem = g_new(MemoryRegion, 1);
char *plic_hart_config;
size_t plic_hart_config_len;
- int i;
+ int i, j;
NICInfo *nd = &nd_table[0];
qdev_prop_set_uint32(DEVICE(&s->u_cpus), "num-harts", ms->smp.cpus - 1);
@@ -832,7 +869,7 @@ static void sifive_u_soc_realize(DeviceState *dev, Error **errp)
/* MMIO */
s->plic = sifive_plic_create(memmap[SIFIVE_U_DEV_PLIC].base,
- plic_hart_config, 0,
+ plic_hart_config, ms->smp.cpus, 0,
SIFIVE_U_PLIC_NUM_SOURCES,
SIFIVE_U_PLIC_NUM_PRIORITIES,
SIFIVE_U_PLIC_PRIORITY_BASE,
@@ -847,9 +884,12 @@ static void sifive_u_soc_realize(DeviceState *dev, Error **errp)
serial_hd(0), qdev_get_gpio_in(DEVICE(s->plic), SIFIVE_U_UART0_IRQ));
sifive_uart_create(system_memory, memmap[SIFIVE_U_DEV_UART1].base,
serial_hd(1), qdev_get_gpio_in(DEVICE(s->plic), SIFIVE_U_UART1_IRQ));
- sifive_clint_create(memmap[SIFIVE_U_DEV_CLINT].base,
- memmap[SIFIVE_U_DEV_CLINT].size, 0, ms->smp.cpus,
- SIFIVE_SIP_BASE, SIFIVE_TIMECMP_BASE, SIFIVE_TIME_BASE,
+ riscv_aclint_swi_create(memmap[SIFIVE_U_DEV_CLINT].base, 0,
+ ms->smp.cpus, false);
+ riscv_aclint_mtimer_create(memmap[SIFIVE_U_DEV_CLINT].base +
+ RISCV_ACLINT_SWI_SIZE,
+ RISCV_ACLINT_DEFAULT_MTIMER_SIZE, 0, ms->smp.cpus,
+ RISCV_ACLINT_DEFAULT_MTIMECMP, RISCV_ACLINT_DEFAULT_MTIME,
CLINT_TIMEBASE_FREQ, false);
if (!sysbus_realize(SYS_BUS_DEVICE(&s->prci), errp)) {
@@ -904,6 +944,22 @@ static void sifive_u_soc_realize(DeviceState *dev, Error **errp)
sysbus_connect_irq(SYS_BUS_DEVICE(&s->gem), 0,
qdev_get_gpio_in(DEVICE(s->plic), SIFIVE_U_GEM_IRQ));
+ /* PWM */
+ for (i = 0; i < 2; i++) {
+ if (!sysbus_realize(SYS_BUS_DEVICE(&s->pwm[i]), errp)) {
+ return;
+ }
+ sysbus_mmio_map(SYS_BUS_DEVICE(&s->pwm[i]), 0,
+ memmap[SIFIVE_U_DEV_PWM0].base + (0x1000 * i));
+
+ /* Connect PWM interrupts to the PLIC */
+ for (j = 0; j < SIFIVE_PWM_IRQS; j++) {
+ sysbus_connect_irq(SYS_BUS_DEVICE(&s->pwm[i]), j,
+ qdev_get_gpio_in(DEVICE(s->plic),
+ SIFIVE_U_PWM0_IRQ0 + (i * 4) + j));
+ }
+ }
+
create_unimplemented_device("riscv.sifive.u.gem-mgmt",
memmap[SIFIVE_U_DEV_GEM_MGMT].base, memmap[SIFIVE_U_DEV_GEM_MGMT].size);
diff --git a/hw/riscv/spike.c b/hw/riscv/spike.c
index aae36f2..79ae355 100644
--- a/hw/riscv/spike.c
+++ b/hw/riscv/spike.c
@@ -35,7 +35,7 @@
#include "hw/riscv/boot.h"
#include "hw/riscv/numa.h"
#include "hw/char/riscv_htif.h"
-#include "hw/intc/sifive_clint.h"
+#include "hw/intc/riscv_aclint.h"
#include "chardev/char.h"
#include "sysemu/device_tree.h"
#include "sysemu/sysemu.h"
@@ -84,7 +84,7 @@ static void create_fdt(SpikeState *s, const MemMapEntry *memmap,
qemu_fdt_add_subnode(fdt, "/cpus");
qemu_fdt_setprop_cell(fdt, "/cpus", "timebase-frequency",
- SIFIVE_CLINT_TIMEBASE_FREQ);
+ RISCV_ACLINT_DEFAULT_TIMEBASE_FREQ);
qemu_fdt_setprop_cell(fdt, "/cpus", "#size-cells", 0x0);
qemu_fdt_setprop_cell(fdt, "/cpus", "#address-cells", 0x1);
qemu_fdt_add_subnode(fdt, "/cpus/cpu-map");
@@ -227,11 +227,15 @@ static void spike_board_init(MachineState *machine)
sysbus_realize(SYS_BUS_DEVICE(&s->soc[i]), &error_abort);
/* Core Local Interruptor (timer and IPI) for each socket */
- sifive_clint_create(
+ riscv_aclint_swi_create(
memmap[SPIKE_CLINT].base + i * memmap[SPIKE_CLINT].size,
- memmap[SPIKE_CLINT].size, base_hartid, hart_count,
- SIFIVE_SIP_BASE, SIFIVE_TIMECMP_BASE, SIFIVE_TIME_BASE,
- SIFIVE_CLINT_TIMEBASE_FREQ, false);
+ base_hartid, hart_count, false);
+ riscv_aclint_mtimer_create(
+ memmap[SPIKE_CLINT].base + i * memmap[SPIKE_CLINT].size +
+ RISCV_ACLINT_SWI_SIZE,
+ RISCV_ACLINT_DEFAULT_MTIMER_SIZE, base_hartid, hart_count,
+ RISCV_ACLINT_DEFAULT_MTIMECMP, RISCV_ACLINT_DEFAULT_MTIME,
+ RISCV_ACLINT_DEFAULT_TIMEBASE_FREQ, false);
}
/* register system main memory (actual RAM) */
diff --git a/hw/riscv/virt.c b/hw/riscv/virt.c
index 5624add..ec0cb69 100644
--- a/hw/riscv/virt.c
+++ b/hw/riscv/virt.c
@@ -32,7 +32,7 @@
#include "hw/riscv/virt.h"
#include "hw/riscv/boot.h"
#include "hw/riscv/numa.h"
-#include "hw/intc/sifive_clint.h"
+#include "hw/intc/riscv_aclint.h"
#include "hw/intc/sifive_plic.h"
#include "hw/misc/sifive_test.h"
#include "chardev/char.h"
@@ -48,6 +48,7 @@ static const MemMapEntry virt_memmap[] = {
[VIRT_TEST] = { 0x100000, 0x1000 },
[VIRT_RTC] = { 0x101000, 0x1000 },
[VIRT_CLINT] = { 0x2000000, 0x10000 },
+ [VIRT_ACLINT_SSWI] = { 0x2F00000, 0x4000 },
[VIRT_PCIE_PIO] = { 0x3000000, 0x10000 },
[VIRT_PLIC] = { 0xc000000, VIRT_PLIC_SIZE(VIRT_CPUS_MAX * 2) },
[VIRT_UART0] = { 0x10000000, 0x100 },
@@ -176,214 +177,342 @@ static void create_pcie_irq_map(void *fdt, char *nodename,
0x1800, 0, 0, 0x7);
}
-static void create_fdt(RISCVVirtState *s, const MemMapEntry *memmap,
- uint64_t mem_size, const char *cmdline, bool is_32_bit)
+static void create_fdt_socket_cpus(RISCVVirtState *s, int socket,
+ char *clust_name, uint32_t *phandle,
+ bool is_32_bit, uint32_t *intc_phandles)
{
- void *fdt;
- int i, cpu, socket;
+ int cpu;
+ uint32_t cpu_phandle;
MachineState *mc = MACHINE(s);
+ char *name, *cpu_name, *core_name, *intc_name;
+
+ for (cpu = s->soc[socket].num_harts - 1; cpu >= 0; cpu--) {
+ cpu_phandle = (*phandle)++;
+
+ cpu_name = g_strdup_printf("/cpus/cpu@%d",
+ s->soc[socket].hartid_base + cpu);
+ qemu_fdt_add_subnode(mc->fdt, cpu_name);
+ qemu_fdt_setprop_string(mc->fdt, cpu_name, "mmu-type",
+ (is_32_bit) ? "riscv,sv32" : "riscv,sv48");
+ name = riscv_isa_string(&s->soc[socket].harts[cpu]);
+ qemu_fdt_setprop_string(mc->fdt, cpu_name, "riscv,isa", name);
+ g_free(name);
+ qemu_fdt_setprop_string(mc->fdt, cpu_name, "compatible", "riscv");
+ qemu_fdt_setprop_string(mc->fdt, cpu_name, "status", "okay");
+ qemu_fdt_setprop_cell(mc->fdt, cpu_name, "reg",
+ s->soc[socket].hartid_base + cpu);
+ qemu_fdt_setprop_string(mc->fdt, cpu_name, "device_type", "cpu");
+ riscv_socket_fdt_write_id(mc, mc->fdt, cpu_name, socket);
+ qemu_fdt_setprop_cell(mc->fdt, cpu_name, "phandle", cpu_phandle);
+
+ intc_phandles[cpu] = (*phandle)++;
+
+ intc_name = g_strdup_printf("%s/interrupt-controller", cpu_name);
+ qemu_fdt_add_subnode(mc->fdt, intc_name);
+ qemu_fdt_setprop_cell(mc->fdt, intc_name, "phandle",
+ intc_phandles[cpu]);
+ qemu_fdt_setprop_string(mc->fdt, intc_name, "compatible",
+ "riscv,cpu-intc");
+ qemu_fdt_setprop(mc->fdt, intc_name, "interrupt-controller", NULL, 0);
+ qemu_fdt_setprop_cell(mc->fdt, intc_name, "#interrupt-cells", 1);
+
+ core_name = g_strdup_printf("%s/core%d", clust_name, cpu);
+ qemu_fdt_add_subnode(mc->fdt, core_name);
+ qemu_fdt_setprop_cell(mc->fdt, core_name, "cpu", cpu_phandle);
+
+ g_free(core_name);
+ g_free(intc_name);
+ g_free(cpu_name);
+ }
+}
+
+static void create_fdt_socket_memory(RISCVVirtState *s,
+ const MemMapEntry *memmap, int socket)
+{
+ char *mem_name;
uint64_t addr, size;
- uint32_t *clint_cells, *plic_cells;
- unsigned long clint_addr, plic_addr;
- uint32_t plic_phandle[MAX_NODES];
- uint32_t cpu_phandle, intc_phandle, test_phandle;
- uint32_t phandle = 1, plic_mmio_phandle = 1;
- uint32_t plic_pcie_phandle = 1, plic_virtio_phandle = 1;
- char *mem_name, *cpu_name, *core_name, *intc_name;
- char *name, *clint_name, *plic_name, *clust_name;
- hwaddr flashsize = virt_memmap[VIRT_FLASH].size / 2;
- hwaddr flashbase = virt_memmap[VIRT_FLASH].base;
+ MachineState *mc = MACHINE(s);
+
+ addr = memmap[VIRT_DRAM].base + riscv_socket_mem_offset(mc, socket);
+ size = riscv_socket_mem_size(mc, socket);
+ mem_name = g_strdup_printf("/memory@%lx", (long)addr);
+ qemu_fdt_add_subnode(mc->fdt, mem_name);
+ qemu_fdt_setprop_cells(mc->fdt, mem_name, "reg",
+ addr >> 32, addr, size >> 32, size);
+ qemu_fdt_setprop_string(mc->fdt, mem_name, "device_type", "memory");
+ riscv_socket_fdt_write_id(mc, mc->fdt, mem_name, socket);
+ g_free(mem_name);
+}
+
+static void create_fdt_socket_clint(RISCVVirtState *s,
+ const MemMapEntry *memmap, int socket,
+ uint32_t *intc_phandles)
+{
+ int cpu;
+ char *clint_name;
+ uint32_t *clint_cells;
+ unsigned long clint_addr;
+ MachineState *mc = MACHINE(s);
static const char * const clint_compat[2] = {
"sifive,clint0", "riscv,clint0"
};
+
+ clint_cells = g_new0(uint32_t, s->soc[socket].num_harts * 4);
+
+ for (cpu = 0; cpu < s->soc[socket].num_harts; cpu++) {
+ clint_cells[cpu * 4 + 0] = cpu_to_be32(intc_phandles[cpu]);
+ clint_cells[cpu * 4 + 1] = cpu_to_be32(IRQ_M_SOFT);
+ clint_cells[cpu * 4 + 2] = cpu_to_be32(intc_phandles[cpu]);
+ clint_cells[cpu * 4 + 3] = cpu_to_be32(IRQ_M_TIMER);
+ }
+
+ clint_addr = memmap[VIRT_CLINT].base + (memmap[VIRT_CLINT].size * socket);
+ clint_name = g_strdup_printf("/soc/clint@%lx", clint_addr);
+ qemu_fdt_add_subnode(mc->fdt, clint_name);
+ qemu_fdt_setprop_string_array(mc->fdt, clint_name, "compatible",
+ (char **)&clint_compat,
+ ARRAY_SIZE(clint_compat));
+ qemu_fdt_setprop_cells(mc->fdt, clint_name, "reg",
+ 0x0, clint_addr, 0x0, memmap[VIRT_CLINT].size);
+ qemu_fdt_setprop(mc->fdt, clint_name, "interrupts-extended",
+ clint_cells, s->soc[socket].num_harts * sizeof(uint32_t) * 4);
+ riscv_socket_fdt_write_id(mc, mc->fdt, clint_name, socket);
+ g_free(clint_name);
+
+ g_free(clint_cells);
+}
+
+static void create_fdt_socket_aclint(RISCVVirtState *s,
+ const MemMapEntry *memmap, int socket,
+ uint32_t *intc_phandles)
+{
+ int cpu;
+ char *name;
+ unsigned long addr;
+ uint32_t aclint_cells_size;
+ uint32_t *aclint_mswi_cells;
+ uint32_t *aclint_sswi_cells;
+ uint32_t *aclint_mtimer_cells;
+ MachineState *mc = MACHINE(s);
+
+ aclint_mswi_cells = g_new0(uint32_t, s->soc[socket].num_harts * 2);
+ aclint_mtimer_cells = g_new0(uint32_t, s->soc[socket].num_harts * 2);
+ aclint_sswi_cells = g_new0(uint32_t, s->soc[socket].num_harts * 2);
+
+ for (cpu = 0; cpu < s->soc[socket].num_harts; cpu++) {
+ aclint_mswi_cells[cpu * 2 + 0] = cpu_to_be32(intc_phandles[cpu]);
+ aclint_mswi_cells[cpu * 2 + 1] = cpu_to_be32(IRQ_M_SOFT);
+ aclint_mtimer_cells[cpu * 2 + 0] = cpu_to_be32(intc_phandles[cpu]);
+ aclint_mtimer_cells[cpu * 2 + 1] = cpu_to_be32(IRQ_M_TIMER);
+ aclint_sswi_cells[cpu * 2 + 0] = cpu_to_be32(intc_phandles[cpu]);
+ aclint_sswi_cells[cpu * 2 + 1] = cpu_to_be32(IRQ_S_SOFT);
+ }
+ aclint_cells_size = s->soc[socket].num_harts * sizeof(uint32_t) * 2;
+
+ addr = memmap[VIRT_CLINT].base + (memmap[VIRT_CLINT].size * socket);
+ name = g_strdup_printf("/soc/mswi@%lx", addr);
+ qemu_fdt_add_subnode(mc->fdt, name);
+ qemu_fdt_setprop_string(mc->fdt, name, "compatible", "riscv,aclint-mswi");
+ qemu_fdt_setprop_cells(mc->fdt, name, "reg",
+ 0x0, addr, 0x0, RISCV_ACLINT_SWI_SIZE);
+ qemu_fdt_setprop(mc->fdt, name, "interrupts-extended",
+ aclint_mswi_cells, aclint_cells_size);
+ qemu_fdt_setprop(mc->fdt, name, "interrupt-controller", NULL, 0);
+ qemu_fdt_setprop_cell(mc->fdt, name, "#interrupt-cells", 0);
+ riscv_socket_fdt_write_id(mc, mc->fdt, name, socket);
+ g_free(name);
+
+ addr = memmap[VIRT_CLINT].base + RISCV_ACLINT_SWI_SIZE +
+ (memmap[VIRT_CLINT].size * socket);
+ name = g_strdup_printf("/soc/mtimer@%lx", addr);
+ qemu_fdt_add_subnode(mc->fdt, name);
+ qemu_fdt_setprop_string(mc->fdt, name, "compatible",
+ "riscv,aclint-mtimer");
+ qemu_fdt_setprop_cells(mc->fdt, name, "reg",
+ 0x0, addr + RISCV_ACLINT_DEFAULT_MTIME,
+ 0x0, memmap[VIRT_CLINT].size - RISCV_ACLINT_SWI_SIZE -
+ RISCV_ACLINT_DEFAULT_MTIME,
+ 0x0, addr + RISCV_ACLINT_DEFAULT_MTIMECMP,
+ 0x0, RISCV_ACLINT_DEFAULT_MTIME);
+ qemu_fdt_setprop(mc->fdt, name, "interrupts-extended",
+ aclint_mtimer_cells, aclint_cells_size);
+ riscv_socket_fdt_write_id(mc, mc->fdt, name, socket);
+ g_free(name);
+
+ addr = memmap[VIRT_ACLINT_SSWI].base +
+ (memmap[VIRT_ACLINT_SSWI].size * socket);
+ name = g_strdup_printf("/soc/sswi@%lx", addr);
+ qemu_fdt_add_subnode(mc->fdt, name);
+ qemu_fdt_setprop_string(mc->fdt, name, "compatible", "riscv,aclint-sswi");
+ qemu_fdt_setprop_cells(mc->fdt, name, "reg",
+ 0x0, addr, 0x0, memmap[VIRT_ACLINT_SSWI].size);
+ qemu_fdt_setprop(mc->fdt, name, "interrupts-extended",
+ aclint_sswi_cells, aclint_cells_size);
+ qemu_fdt_setprop(mc->fdt, name, "interrupt-controller", NULL, 0);
+ qemu_fdt_setprop_cell(mc->fdt, name, "#interrupt-cells", 0);
+ riscv_socket_fdt_write_id(mc, mc->fdt, name, socket);
+ g_free(name);
+
+ g_free(aclint_mswi_cells);
+ g_free(aclint_mtimer_cells);
+ g_free(aclint_sswi_cells);
+}
+
+static void create_fdt_socket_plic(RISCVVirtState *s,
+ const MemMapEntry *memmap, int socket,
+ uint32_t *phandle, uint32_t *intc_phandles,
+ uint32_t *plic_phandles)
+{
+ int cpu;
+ char *plic_name;
+ uint32_t *plic_cells;
+ unsigned long plic_addr;
+ MachineState *mc = MACHINE(s);
static const char * const plic_compat[2] = {
"sifive,plic-1.0.0", "riscv,plic0"
};
- if (mc->dtb) {
- fdt = mc->fdt = load_device_tree(mc->dtb, &s->fdt_size);
- if (!fdt) {
- error_report("load_device_tree() failed");
- exit(1);
- }
- goto update_bootargs;
- } else {
- fdt = mc->fdt = create_device_tree(&s->fdt_size);
- if (!fdt) {
- error_report("create_device_tree() failed");
- exit(1);
- }
+ plic_cells = g_new0(uint32_t, s->soc[socket].num_harts * 4);
+
+ for (cpu = 0; cpu < s->soc[socket].num_harts; cpu++) {
+ plic_cells[cpu * 4 + 0] = cpu_to_be32(intc_phandles[cpu]);
+ plic_cells[cpu * 4 + 1] = cpu_to_be32(IRQ_M_EXT);
+ plic_cells[cpu * 4 + 2] = cpu_to_be32(intc_phandles[cpu]);
+ plic_cells[cpu * 4 + 3] = cpu_to_be32(IRQ_S_EXT);
}
- qemu_fdt_setprop_string(fdt, "/", "model", "riscv-virtio,qemu");
- qemu_fdt_setprop_string(fdt, "/", "compatible", "riscv-virtio");
- qemu_fdt_setprop_cell(fdt, "/", "#size-cells", 0x2);
- qemu_fdt_setprop_cell(fdt, "/", "#address-cells", 0x2);
+ plic_phandles[socket] = (*phandle)++;
+ plic_addr = memmap[VIRT_PLIC].base + (memmap[VIRT_PLIC].size * socket);
+ plic_name = g_strdup_printf("/soc/plic@%lx", plic_addr);
+ qemu_fdt_add_subnode(mc->fdt, plic_name);
+ qemu_fdt_setprop_cell(mc->fdt, plic_name,
+ "#address-cells", FDT_PLIC_ADDR_CELLS);
+ qemu_fdt_setprop_cell(mc->fdt, plic_name,
+ "#interrupt-cells", FDT_PLIC_INT_CELLS);
+ qemu_fdt_setprop_string_array(mc->fdt, plic_name, "compatible",
+ (char **)&plic_compat,
+ ARRAY_SIZE(plic_compat));
+ qemu_fdt_setprop(mc->fdt, plic_name, "interrupt-controller", NULL, 0);
+ qemu_fdt_setprop(mc->fdt, plic_name, "interrupts-extended",
+ plic_cells, s->soc[socket].num_harts * sizeof(uint32_t) * 4);
+ qemu_fdt_setprop_cells(mc->fdt, plic_name, "reg",
+ 0x0, plic_addr, 0x0, memmap[VIRT_PLIC].size);
+ qemu_fdt_setprop_cell(mc->fdt, plic_name, "riscv,ndev", VIRTIO_NDEV);
+ riscv_socket_fdt_write_id(mc, mc->fdt, plic_name, socket);
+ qemu_fdt_setprop_cell(mc->fdt, plic_name, "phandle",
+ plic_phandles[socket]);
+ g_free(plic_name);
+
+ g_free(plic_cells);
+}
- qemu_fdt_add_subnode(fdt, "/soc");
- qemu_fdt_setprop(fdt, "/soc", "ranges", NULL, 0);
- qemu_fdt_setprop_string(fdt, "/soc", "compatible", "simple-bus");
- qemu_fdt_setprop_cell(fdt, "/soc", "#size-cells", 0x2);
- qemu_fdt_setprop_cell(fdt, "/soc", "#address-cells", 0x2);
+static void create_fdt_sockets(RISCVVirtState *s, const MemMapEntry *memmap,
+ bool is_32_bit, uint32_t *phandle,
+ uint32_t *irq_mmio_phandle,
+ uint32_t *irq_pcie_phandle,
+ uint32_t *irq_virtio_phandle)
+{
+ int socket;
+ char *clust_name;
+ uint32_t *intc_phandles;
+ MachineState *mc = MACHINE(s);
+ uint32_t xplic_phandles[MAX_NODES];
- qemu_fdt_add_subnode(fdt, "/cpus");
- qemu_fdt_setprop_cell(fdt, "/cpus", "timebase-frequency",
- SIFIVE_CLINT_TIMEBASE_FREQ);
- qemu_fdt_setprop_cell(fdt, "/cpus", "#size-cells", 0x0);
- qemu_fdt_setprop_cell(fdt, "/cpus", "#address-cells", 0x1);
- qemu_fdt_add_subnode(fdt, "/cpus/cpu-map");
+ qemu_fdt_add_subnode(mc->fdt, "/cpus");
+ qemu_fdt_setprop_cell(mc->fdt, "/cpus", "timebase-frequency",
+ RISCV_ACLINT_DEFAULT_TIMEBASE_FREQ);
+ qemu_fdt_setprop_cell(mc->fdt, "/cpus", "#size-cells", 0x0);
+ qemu_fdt_setprop_cell(mc->fdt, "/cpus", "#address-cells", 0x1);
+ qemu_fdt_add_subnode(mc->fdt, "/cpus/cpu-map");
for (socket = (riscv_socket_count(mc) - 1); socket >= 0; socket--) {
clust_name = g_strdup_printf("/cpus/cpu-map/cluster%d", socket);
- qemu_fdt_add_subnode(fdt, clust_name);
-
- plic_cells = g_new0(uint32_t, s->soc[socket].num_harts * 4);
- clint_cells = g_new0(uint32_t, s->soc[socket].num_harts * 4);
-
- for (cpu = s->soc[socket].num_harts - 1; cpu >= 0; cpu--) {
- cpu_phandle = phandle++;
-
- cpu_name = g_strdup_printf("/cpus/cpu@%d",
- s->soc[socket].hartid_base + cpu);
- qemu_fdt_add_subnode(fdt, cpu_name);
- if (is_32_bit) {
- qemu_fdt_setprop_string(fdt, cpu_name, "mmu-type", "riscv,sv32");
- } else {
- qemu_fdt_setprop_string(fdt, cpu_name, "mmu-type", "riscv,sv48");
- }
- name = riscv_isa_string(&s->soc[socket].harts[cpu]);
- qemu_fdt_setprop_string(fdt, cpu_name, "riscv,isa", name);
- g_free(name);
- qemu_fdt_setprop_string(fdt, cpu_name, "compatible", "riscv");
- qemu_fdt_setprop_string(fdt, cpu_name, "status", "okay");
- qemu_fdt_setprop_cell(fdt, cpu_name, "reg",
- s->soc[socket].hartid_base + cpu);
- qemu_fdt_setprop_string(fdt, cpu_name, "device_type", "cpu");
- riscv_socket_fdt_write_id(mc, fdt, cpu_name, socket);
- qemu_fdt_setprop_cell(fdt, cpu_name, "phandle", cpu_phandle);
-
- intc_name = g_strdup_printf("%s/interrupt-controller", cpu_name);
- qemu_fdt_add_subnode(fdt, intc_name);
- intc_phandle = phandle++;
- qemu_fdt_setprop_cell(fdt, intc_name, "phandle", intc_phandle);
- qemu_fdt_setprop_string(fdt, intc_name, "compatible",
- "riscv,cpu-intc");
- qemu_fdt_setprop(fdt, intc_name, "interrupt-controller", NULL, 0);
- qemu_fdt_setprop_cell(fdt, intc_name, "#interrupt-cells", 1);
-
- clint_cells[cpu * 4 + 0] = cpu_to_be32(intc_phandle);
- clint_cells[cpu * 4 + 1] = cpu_to_be32(IRQ_M_SOFT);
- clint_cells[cpu * 4 + 2] = cpu_to_be32(intc_phandle);
- clint_cells[cpu * 4 + 3] = cpu_to_be32(IRQ_M_TIMER);
-
- plic_cells[cpu * 4 + 0] = cpu_to_be32(intc_phandle);
- plic_cells[cpu * 4 + 1] = cpu_to_be32(IRQ_M_EXT);
- plic_cells[cpu * 4 + 2] = cpu_to_be32(intc_phandle);
- plic_cells[cpu * 4 + 3] = cpu_to_be32(IRQ_S_EXT);
-
- core_name = g_strdup_printf("%s/core%d", clust_name, cpu);
- qemu_fdt_add_subnode(fdt, core_name);
- qemu_fdt_setprop_cell(fdt, core_name, "cpu", cpu_phandle);
-
- g_free(core_name);
- g_free(intc_name);
- g_free(cpu_name);
+ qemu_fdt_add_subnode(mc->fdt, clust_name);
+
+ intc_phandles = g_new0(uint32_t, s->soc[socket].num_harts);
+
+ create_fdt_socket_cpus(s, socket, clust_name, phandle,
+ is_32_bit, intc_phandles);
+
+ create_fdt_socket_memory(s, memmap, socket);
+
+ if (s->have_aclint) {
+ create_fdt_socket_aclint(s, memmap, socket, intc_phandles);
+ } else {
+ create_fdt_socket_clint(s, memmap, socket, intc_phandles);
}
- addr = memmap[VIRT_DRAM].base + riscv_socket_mem_offset(mc, socket);
- size = riscv_socket_mem_size(mc, socket);
- mem_name = g_strdup_printf("/memory@%lx", (long)addr);
- qemu_fdt_add_subnode(fdt, mem_name);
- qemu_fdt_setprop_cells(fdt, mem_name, "reg",
- addr >> 32, addr, size >> 32, size);
- qemu_fdt_setprop_string(fdt, mem_name, "device_type", "memory");
- riscv_socket_fdt_write_id(mc, fdt, mem_name, socket);
- g_free(mem_name);
-
- clint_addr = memmap[VIRT_CLINT].base +
- (memmap[VIRT_CLINT].size * socket);
- clint_name = g_strdup_printf("/soc/clint@%lx", clint_addr);
- qemu_fdt_add_subnode(fdt, clint_name);
- qemu_fdt_setprop_string_array(fdt, clint_name, "compatible",
- (char **)&clint_compat, ARRAY_SIZE(clint_compat));
- qemu_fdt_setprop_cells(fdt, clint_name, "reg",
- 0x0, clint_addr, 0x0, memmap[VIRT_CLINT].size);
- qemu_fdt_setprop(fdt, clint_name, "interrupts-extended",
- clint_cells, s->soc[socket].num_harts * sizeof(uint32_t) * 4);
- riscv_socket_fdt_write_id(mc, fdt, clint_name, socket);
- g_free(clint_name);
-
- plic_phandle[socket] = phandle++;
- plic_addr = memmap[VIRT_PLIC].base + (memmap[VIRT_PLIC].size * socket);
- plic_name = g_strdup_printf("/soc/plic@%lx", plic_addr);
- qemu_fdt_add_subnode(fdt, plic_name);
- qemu_fdt_setprop_cell(fdt, plic_name,
- "#address-cells", FDT_PLIC_ADDR_CELLS);
- qemu_fdt_setprop_cell(fdt, plic_name,
- "#interrupt-cells", FDT_PLIC_INT_CELLS);
- qemu_fdt_setprop_string_array(fdt, plic_name, "compatible",
- (char **)&plic_compat, ARRAY_SIZE(plic_compat));
- qemu_fdt_setprop(fdt, plic_name, "interrupt-controller", NULL, 0);
- qemu_fdt_setprop(fdt, plic_name, "interrupts-extended",
- plic_cells, s->soc[socket].num_harts * sizeof(uint32_t) * 4);
- qemu_fdt_setprop_cells(fdt, plic_name, "reg",
- 0x0, plic_addr, 0x0, memmap[VIRT_PLIC].size);
- qemu_fdt_setprop_cell(fdt, plic_name, "riscv,ndev", VIRTIO_NDEV);
- riscv_socket_fdt_write_id(mc, fdt, plic_name, socket);
- qemu_fdt_setprop_cell(fdt, plic_name, "phandle", plic_phandle[socket]);
- g_free(plic_name);
-
- g_free(clint_cells);
- g_free(plic_cells);
+ create_fdt_socket_plic(s, memmap, socket, phandle,
+ intc_phandles, xplic_phandles);
+
+ g_free(intc_phandles);
g_free(clust_name);
}
for (socket = 0; socket < riscv_socket_count(mc); socket++) {
if (socket == 0) {
- plic_mmio_phandle = plic_phandle[socket];
- plic_virtio_phandle = plic_phandle[socket];
- plic_pcie_phandle = plic_phandle[socket];
+ *irq_mmio_phandle = xplic_phandles[socket];
+ *irq_virtio_phandle = xplic_phandles[socket];
+ *irq_pcie_phandle = xplic_phandles[socket];
}
if (socket == 1) {
- plic_virtio_phandle = plic_phandle[socket];
- plic_pcie_phandle = plic_phandle[socket];
+ *irq_virtio_phandle = xplic_phandles[socket];
+ *irq_pcie_phandle = xplic_phandles[socket];
}
if (socket == 2) {
- plic_pcie_phandle = plic_phandle[socket];
+ *irq_pcie_phandle = xplic_phandles[socket];
}
}
- riscv_socket_fdt_write_distance_matrix(mc, fdt);
+ riscv_socket_fdt_write_distance_matrix(mc, mc->fdt);
+}
+
+static void create_fdt_virtio(RISCVVirtState *s, const MemMapEntry *memmap,
+ uint32_t irq_virtio_phandle)
+{
+ int i;
+ char *name;
+ MachineState *mc = MACHINE(s);
for (i = 0; i < VIRTIO_COUNT; i++) {
name = g_strdup_printf("/soc/virtio_mmio@%lx",
(long)(memmap[VIRT_VIRTIO].base + i * memmap[VIRT_VIRTIO].size));
- qemu_fdt_add_subnode(fdt, name);
- qemu_fdt_setprop_string(fdt, name, "compatible", "virtio,mmio");
- qemu_fdt_setprop_cells(fdt, name, "reg",
+ qemu_fdt_add_subnode(mc->fdt, name);
+ qemu_fdt_setprop_string(mc->fdt, name, "compatible", "virtio,mmio");
+ qemu_fdt_setprop_cells(mc->fdt, name, "reg",
0x0, memmap[VIRT_VIRTIO].base + i * memmap[VIRT_VIRTIO].size,
0x0, memmap[VIRT_VIRTIO].size);
- qemu_fdt_setprop_cell(fdt, name, "interrupt-parent",
- plic_virtio_phandle);
- qemu_fdt_setprop_cell(fdt, name, "interrupts", VIRTIO_IRQ + i);
+ qemu_fdt_setprop_cell(mc->fdt, name, "interrupt-parent",
+ irq_virtio_phandle);
+ qemu_fdt_setprop_cell(mc->fdt, name, "interrupts", VIRTIO_IRQ + i);
g_free(name);
}
+}
+
+static void create_fdt_pcie(RISCVVirtState *s, const MemMapEntry *memmap,
+ uint32_t irq_pcie_phandle)
+{
+ char *name;
+ MachineState *mc = MACHINE(s);
name = g_strdup_printf("/soc/pci@%lx",
(long) memmap[VIRT_PCIE_ECAM].base);
- qemu_fdt_add_subnode(fdt, name);
- qemu_fdt_setprop_cell(fdt, name, "#address-cells", FDT_PCI_ADDR_CELLS);
- qemu_fdt_setprop_cell(fdt, name, "#interrupt-cells", FDT_PCI_INT_CELLS);
- qemu_fdt_setprop_cell(fdt, name, "#size-cells", 0x2);
- qemu_fdt_setprop_string(fdt, name, "compatible", "pci-host-ecam-generic");
- qemu_fdt_setprop_string(fdt, name, "device_type", "pci");
- qemu_fdt_setprop_cell(fdt, name, "linux,pci-domain", 0);
- qemu_fdt_setprop_cells(fdt, name, "bus-range", 0,
+ qemu_fdt_add_subnode(mc->fdt, name);
+ qemu_fdt_setprop_cell(mc->fdt, name, "#address-cells",
+ FDT_PCI_ADDR_CELLS);
+ qemu_fdt_setprop_cell(mc->fdt, name, "#interrupt-cells",
+ FDT_PCI_INT_CELLS);
+ qemu_fdt_setprop_cell(mc->fdt, name, "#size-cells", 0x2);
+ qemu_fdt_setprop_string(mc->fdt, name, "compatible",
+ "pci-host-ecam-generic");
+ qemu_fdt_setprop_string(mc->fdt, name, "device_type", "pci");
+ qemu_fdt_setprop_cell(mc->fdt, name, "linux,pci-domain", 0);
+ qemu_fdt_setprop_cells(mc->fdt, name, "bus-range", 0,
memmap[VIRT_PCIE_ECAM].size / PCIE_MMCFG_SIZE_MIN - 1);
- qemu_fdt_setprop(fdt, name, "dma-coherent", NULL, 0);
- qemu_fdt_setprop_cells(fdt, name, "reg", 0,
+ qemu_fdt_setprop(mc->fdt, name, "dma-coherent", NULL, 0);
+ qemu_fdt_setprop_cells(mc->fdt, name, "reg", 0,
memmap[VIRT_PCIE_ECAM].base, 0, memmap[VIRT_PCIE_ECAM].size);
- qemu_fdt_setprop_sized_cells(fdt, name, "ranges",
+ qemu_fdt_setprop_sized_cells(mc->fdt, name, "ranges",
1, FDT_PCI_RANGE_IOPORT, 2, 0,
2, memmap[VIRT_PCIE_PIO].base, 2, memmap[VIRT_PCIE_PIO].size,
1, FDT_PCI_RANGE_MMIO,
@@ -393,66 +522,96 @@ static void create_fdt(RISCVVirtState *s, const MemMapEntry *memmap,
2, virt_high_pcie_memmap.base,
2, virt_high_pcie_memmap.base, 2, virt_high_pcie_memmap.size);
- create_pcie_irq_map(fdt, name, plic_pcie_phandle);
+ create_pcie_irq_map(mc->fdt, name, irq_pcie_phandle);
g_free(name);
+}
+
+static void create_fdt_reset(RISCVVirtState *s, const MemMapEntry *memmap,
+ uint32_t *phandle)
+{
+ char *name;
+ uint32_t test_phandle;
+ MachineState *mc = MACHINE(s);
- test_phandle = phandle++;
+ test_phandle = (*phandle)++;
name = g_strdup_printf("/soc/test@%lx",
(long)memmap[VIRT_TEST].base);
- qemu_fdt_add_subnode(fdt, name);
+ qemu_fdt_add_subnode(mc->fdt, name);
{
static const char * const compat[3] = {
"sifive,test1", "sifive,test0", "syscon"
};
- qemu_fdt_setprop_string_array(fdt, name, "compatible", (char **)&compat,
- ARRAY_SIZE(compat));
+ qemu_fdt_setprop_string_array(mc->fdt, name, "compatible",
+ (char **)&compat, ARRAY_SIZE(compat));
}
- qemu_fdt_setprop_cells(fdt, name, "reg",
- 0x0, memmap[VIRT_TEST].base,
- 0x0, memmap[VIRT_TEST].size);
- qemu_fdt_setprop_cell(fdt, name, "phandle", test_phandle);
- test_phandle = qemu_fdt_get_phandle(fdt, name);
+ qemu_fdt_setprop_cells(mc->fdt, name, "reg",
+ 0x0, memmap[VIRT_TEST].base, 0x0, memmap[VIRT_TEST].size);
+ qemu_fdt_setprop_cell(mc->fdt, name, "phandle", test_phandle);
+ test_phandle = qemu_fdt_get_phandle(mc->fdt, name);
g_free(name);
name = g_strdup_printf("/soc/reboot");
- qemu_fdt_add_subnode(fdt, name);
- qemu_fdt_setprop_string(fdt, name, "compatible", "syscon-reboot");
- qemu_fdt_setprop_cell(fdt, name, "regmap", test_phandle);
- qemu_fdt_setprop_cell(fdt, name, "offset", 0x0);
- qemu_fdt_setprop_cell(fdt, name, "value", FINISHER_RESET);
+ qemu_fdt_add_subnode(mc->fdt, name);
+ qemu_fdt_setprop_string(mc->fdt, name, "compatible", "syscon-reboot");
+ qemu_fdt_setprop_cell(mc->fdt, name, "regmap", test_phandle);
+ qemu_fdt_setprop_cell(mc->fdt, name, "offset", 0x0);
+ qemu_fdt_setprop_cell(mc->fdt, name, "value", FINISHER_RESET);
g_free(name);
name = g_strdup_printf("/soc/poweroff");
- qemu_fdt_add_subnode(fdt, name);
- qemu_fdt_setprop_string(fdt, name, "compatible", "syscon-poweroff");
- qemu_fdt_setprop_cell(fdt, name, "regmap", test_phandle);
- qemu_fdt_setprop_cell(fdt, name, "offset", 0x0);
- qemu_fdt_setprop_cell(fdt, name, "value", FINISHER_PASS);
+ qemu_fdt_add_subnode(mc->fdt, name);
+ qemu_fdt_setprop_string(mc->fdt, name, "compatible", "syscon-poweroff");
+ qemu_fdt_setprop_cell(mc->fdt, name, "regmap", test_phandle);
+ qemu_fdt_setprop_cell(mc->fdt, name, "offset", 0x0);
+ qemu_fdt_setprop_cell(mc->fdt, name, "value", FINISHER_PASS);
g_free(name);
+}
+
+static void create_fdt_uart(RISCVVirtState *s, const MemMapEntry *memmap,
+ uint32_t irq_mmio_phandle)
+{
+ char *name;
+ MachineState *mc = MACHINE(s);
name = g_strdup_printf("/soc/uart@%lx", (long)memmap[VIRT_UART0].base);
- qemu_fdt_add_subnode(fdt, name);
- qemu_fdt_setprop_string(fdt, name, "compatible", "ns16550a");
- qemu_fdt_setprop_cells(fdt, name, "reg",
+ qemu_fdt_add_subnode(mc->fdt, name);
+ qemu_fdt_setprop_string(mc->fdt, name, "compatible", "ns16550a");
+ qemu_fdt_setprop_cells(mc->fdt, name, "reg",
0x0, memmap[VIRT_UART0].base,
0x0, memmap[VIRT_UART0].size);
- qemu_fdt_setprop_cell(fdt, name, "clock-frequency", 3686400);
- qemu_fdt_setprop_cell(fdt, name, "interrupt-parent", plic_mmio_phandle);
- qemu_fdt_setprop_cell(fdt, name, "interrupts", UART0_IRQ);
+ qemu_fdt_setprop_cell(mc->fdt, name, "clock-frequency", 3686400);
+ qemu_fdt_setprop_cell(mc->fdt, name, "interrupt-parent", irq_mmio_phandle);
+ qemu_fdt_setprop_cell(mc->fdt, name, "interrupts", UART0_IRQ);
- qemu_fdt_add_subnode(fdt, "/chosen");
- qemu_fdt_setprop_string(fdt, "/chosen", "stdout-path", name);
+ qemu_fdt_add_subnode(mc->fdt, "/chosen");
+ qemu_fdt_setprop_string(mc->fdt, "/chosen", "stdout-path", name);
g_free(name);
+}
+
+static void create_fdt_rtc(RISCVVirtState *s, const MemMapEntry *memmap,
+ uint32_t irq_mmio_phandle)
+{
+ char *name;
+ MachineState *mc = MACHINE(s);
name = g_strdup_printf("/soc/rtc@%lx", (long)memmap[VIRT_RTC].base);
- qemu_fdt_add_subnode(fdt, name);
- qemu_fdt_setprop_string(fdt, name, "compatible", "google,goldfish-rtc");
- qemu_fdt_setprop_cells(fdt, name, "reg",
- 0x0, memmap[VIRT_RTC].base,
- 0x0, memmap[VIRT_RTC].size);
- qemu_fdt_setprop_cell(fdt, name, "interrupt-parent", plic_mmio_phandle);
- qemu_fdt_setprop_cell(fdt, name, "interrupts", RTC_IRQ);
+ qemu_fdt_add_subnode(mc->fdt, name);
+ qemu_fdt_setprop_string(mc->fdt, name, "compatible",
+ "google,goldfish-rtc");
+ qemu_fdt_setprop_cells(mc->fdt, name, "reg",
+ 0x0, memmap[VIRT_RTC].base, 0x0, memmap[VIRT_RTC].size);
+ qemu_fdt_setprop_cell(mc->fdt, name, "interrupt-parent",
+ irq_mmio_phandle);
+ qemu_fdt_setprop_cell(mc->fdt, name, "interrupts", RTC_IRQ);
g_free(name);
+}
+
+static void create_fdt_flash(RISCVVirtState *s, const MemMapEntry *memmap)
+{
+ char *name;
+ MachineState *mc = MACHINE(s);
+ hwaddr flashsize = virt_memmap[VIRT_FLASH].size / 2;
+ hwaddr flashbase = virt_memmap[VIRT_FLASH].base;
name = g_strdup_printf("/flash@%" PRIx64, flashbase);
qemu_fdt_add_subnode(mc->fdt, name);
@@ -462,10 +621,59 @@ static void create_fdt(RISCVVirtState *s, const MemMapEntry *memmap,
2, flashbase + flashsize, 2, flashsize);
qemu_fdt_setprop_cell(mc->fdt, name, "bank-width", 4);
g_free(name);
+}
+
+static void create_fdt(RISCVVirtState *s, const MemMapEntry *memmap,
+ uint64_t mem_size, const char *cmdline, bool is_32_bit)
+{
+ MachineState *mc = MACHINE(s);
+ uint32_t phandle = 1, irq_mmio_phandle = 1;
+ uint32_t irq_pcie_phandle = 1, irq_virtio_phandle = 1;
+
+ if (mc->dtb) {
+ mc->fdt = load_device_tree(mc->dtb, &s->fdt_size);
+ if (!mc->fdt) {
+ error_report("load_device_tree() failed");
+ exit(1);
+ }
+ goto update_bootargs;
+ } else {
+ mc->fdt = create_device_tree(&s->fdt_size);
+ if (!mc->fdt) {
+ error_report("create_device_tree() failed");
+ exit(1);
+ }
+ }
+
+ qemu_fdt_setprop_string(mc->fdt, "/", "model", "riscv-virtio,qemu");
+ qemu_fdt_setprop_string(mc->fdt, "/", "compatible", "riscv-virtio");
+ qemu_fdt_setprop_cell(mc->fdt, "/", "#size-cells", 0x2);
+ qemu_fdt_setprop_cell(mc->fdt, "/", "#address-cells", 0x2);
+
+ qemu_fdt_add_subnode(mc->fdt, "/soc");
+ qemu_fdt_setprop(mc->fdt, "/soc", "ranges", NULL, 0);
+ qemu_fdt_setprop_string(mc->fdt, "/soc", "compatible", "simple-bus");
+ qemu_fdt_setprop_cell(mc->fdt, "/soc", "#size-cells", 0x2);
+ qemu_fdt_setprop_cell(mc->fdt, "/soc", "#address-cells", 0x2);
+
+ create_fdt_sockets(s, memmap, is_32_bit, &phandle,
+ &irq_mmio_phandle, &irq_pcie_phandle, &irq_virtio_phandle);
+
+ create_fdt_virtio(s, memmap, irq_virtio_phandle);
+
+ create_fdt_pcie(s, memmap, irq_pcie_phandle);
+
+ create_fdt_reset(s, memmap, &phandle);
+
+ create_fdt_uart(s, memmap, irq_mmio_phandle);
+
+ create_fdt_rtc(s, memmap, irq_mmio_phandle);
+
+ create_fdt_flash(s, memmap);
update_bootargs:
if (cmdline) {
- qemu_fdt_setprop_string(fdt, "/chosen", "bootargs", cmdline);
+ qemu_fdt_setprop_string(mc->fdt, "/chosen", "bootargs", cmdline);
}
}
@@ -613,11 +821,23 @@ static void virt_machine_init(MachineState *machine)
sysbus_realize(SYS_BUS_DEVICE(&s->soc[i]), &error_abort);
/* Per-socket CLINT */
- sifive_clint_create(
+ riscv_aclint_swi_create(
memmap[VIRT_CLINT].base + i * memmap[VIRT_CLINT].size,
- memmap[VIRT_CLINT].size, base_hartid, hart_count,
- SIFIVE_SIP_BASE, SIFIVE_TIMECMP_BASE, SIFIVE_TIME_BASE,
- SIFIVE_CLINT_TIMEBASE_FREQ, true);
+ base_hartid, hart_count, false);
+ riscv_aclint_mtimer_create(
+ memmap[VIRT_CLINT].base + i * memmap[VIRT_CLINT].size +
+ RISCV_ACLINT_SWI_SIZE,
+ RISCV_ACLINT_DEFAULT_MTIMER_SIZE, base_hartid, hart_count,
+ RISCV_ACLINT_DEFAULT_MTIMECMP, RISCV_ACLINT_DEFAULT_MTIME,
+ RISCV_ACLINT_DEFAULT_TIMEBASE_FREQ, true);
+
+ /* Per-socket ACLINT SSWI */
+ if (s->have_aclint) {
+ riscv_aclint_swi_create(
+ memmap[VIRT_ACLINT_SSWI].base +
+ i * memmap[VIRT_ACLINT_SSWI].size,
+ base_hartid, hart_count, true);
+ }
/* Per-socket PLIC hart topology configuration string */
plic_hart_config = plic_hart_config_string(hart_count);
@@ -625,7 +845,7 @@ static void virt_machine_init(MachineState *machine)
/* Per-socket PLIC */
s->plic[i] = sifive_plic_create(
memmap[VIRT_PLIC].base + i * memmap[VIRT_PLIC].size,
- plic_hart_config, base_hartid,
+ plic_hart_config, hart_count, base_hartid,
VIRT_PLIC_NUM_SOURCES,
VIRT_PLIC_NUM_PRIORITIES,
VIRT_PLIC_PRIORITY_BASE,
@@ -783,6 +1003,22 @@ static void virt_machine_instance_init(Object *obj)
{
}
+static bool virt_get_aclint(Object *obj, Error **errp)
+{
+ MachineState *ms = MACHINE(obj);
+ RISCVVirtState *s = RISCV_VIRT_MACHINE(ms);
+
+ return s->have_aclint;
+}
+
+static void virt_set_aclint(Object *obj, bool value, Error **errp)
+{
+ MachineState *ms = MACHINE(obj);
+ RISCVVirtState *s = RISCV_VIRT_MACHINE(ms);
+
+ s->have_aclint = value;
+}
+
static void virt_machine_class_init(ObjectClass *oc, void *data)
{
MachineClass *mc = MACHINE_CLASS(oc);
@@ -798,6 +1034,12 @@ static void virt_machine_class_init(ObjectClass *oc, void *data)
mc->numa_mem_supported = true;
machine_class_allow_dynamic_sysbus_dev(mc, TYPE_RAMFB_DEVICE);
+
+ object_class_property_add_bool(oc, "aclint", virt_get_aclint,
+ virt_set_aclint);
+ object_class_property_set_description(oc, "aclint",
+ "Set on/off to enable/disable "
+ "emulating ACLINT devices");
}
static const TypeInfo virt_machine_typeinfo = {
diff --git a/hw/timer/Kconfig b/hw/timer/Kconfig
index 1e73da7..010be7e 100644
--- a/hw/timer/Kconfig
+++ b/hw/timer/Kconfig
@@ -25,6 +25,9 @@ config ALLWINNER_A10_PIT
bool
select PTIMER
+config SIFIVE_PWM
+ bool
+
config STM32F2XX_TIMER
bool
diff --git a/hw/timer/ibex_timer.c b/hw/timer/ibex_timer.c
index 5befb53..66e1f8e 100644
--- a/hw/timer/ibex_timer.c
+++ b/hw/timer/ibex_timer.c
@@ -77,7 +77,7 @@ static void ibex_timer_update_irqs(IbexTimerState *s)
/*
* If the mtimecmp was in the past raise the interrupt now.
*/
- riscv_cpu_update_mip(cpu, MIP_MTIP, BOOL_TO_MASK(1));
+ qemu_irq_raise(s->m_timer_irq);
if (s->timer_intr_enable & R_INTR_ENABLE_IE_0_MASK) {
s->timer_intr_state |= R_INTR_STATE_IS_0_MASK;
qemu_set_irq(s->irq, true);
@@ -86,7 +86,7 @@ static void ibex_timer_update_irqs(IbexTimerState *s)
}
/* Setup a timer to trigger the interrupt in the future */
- riscv_cpu_update_mip(cpu, MIP_MTIP, BOOL_TO_MASK(0));
+ qemu_irq_lower(s->m_timer_irq);
qemu_set_irq(s->irq, false);
diff = cpu->env.timecmp - now;
@@ -106,10 +106,8 @@ static void ibex_timer_update_irqs(IbexTimerState *s)
static void ibex_timer_cb(void *opaque)
{
IbexTimerState *s = opaque;
- CPUState *cs = qemu_get_cpu(0);
- RISCVCPU *cpu = RISCV_CPU(cs);
- riscv_cpu_update_mip(cpu, MIP_MTIP, BOOL_TO_MASK(1));
+ qemu_irq_raise(s->m_timer_irq);
if (s->timer_intr_enable & R_INTR_ENABLE_IE_0_MASK) {
s->timer_intr_state |= R_INTR_STATE_IS_0_MASK;
qemu_set_irq(s->irq, true);
@@ -280,12 +278,21 @@ static void ibex_timer_init(Object *obj)
sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmio);
}
+static void ibex_timer_realize(DeviceState *dev, Error **errp)
+{
+ IbexTimerState *s = IBEX_TIMER(dev);
+
+ qdev_init_gpio_out(dev, &s->m_timer_irq, 1);
+}
+
+
static void ibex_timer_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
dc->reset = ibex_timer_reset;
dc->vmsd = &vmstate_ibex_timer;
+ dc->realize = ibex_timer_realize;
device_class_set_props(dc, ibex_timer_properties);
}
diff --git a/hw/timer/meson.build b/hw/timer/meson.build
index e67478a..03092e2 100644
--- a/hw/timer/meson.build
+++ b/hw/timer/meson.build
@@ -35,5 +35,6 @@ softmmu_ss.add(when: 'CONFIG_STELLARIS_GPTM', if_true: files('stellaris-gptm.c')
softmmu_ss.add(when: 'CONFIG_STM32F2XX_TIMER', if_true: files('stm32f2xx_timer.c'))
softmmu_ss.add(when: 'CONFIG_XILINX', if_true: files('xilinx_timer.c'))
specific_ss.add(when: 'CONFIG_IBEX', if_true: files('ibex_timer.c'))
+softmmu_ss.add(when: 'CONFIG_SIFIVE_PWM', if_true: files('sifive_pwm.c'))
specific_ss.add(when: 'CONFIG_AVR_TIMER16', if_true: files('avr_timer16.c'))
diff --git a/hw/timer/sifive_pwm.c b/hw/timer/sifive_pwm.c
new file mode 100644
index 0000000..c664480
--- /dev/null
+++ b/hw/timer/sifive_pwm.c
@@ -0,0 +1,468 @@
+/*
+ * SiFive PWM
+ *
+ * Copyright (c) 2020 Western Digital
+ *
+ * Author: Alistair Francis <alistair.francis@wdc.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "qemu/osdep.h"
+#include "trace.h"
+#include "hw/irq.h"
+#include "hw/timer/sifive_pwm.h"
+#include "hw/qdev-properties.h"
+#include "hw/registerfields.h"
+#include "migration/vmstate.h"
+#include "qemu/log.h"
+#include "qemu/module.h"
+
+#define HAS_PWM_EN_BITS(cfg) ((cfg & R_CONFIG_ENONESHOT_MASK) || \
+ (cfg & R_CONFIG_ENALWAYS_MASK))
+
+#define PWMCMP_MASK 0xFFFF
+#define PWMCOUNT_MASK 0x7FFFFFFF
+
+REG32(CONFIG, 0x00)
+ FIELD(CONFIG, SCALE, 0, 4)
+ FIELD(CONFIG, STICKY, 8, 1)
+ FIELD(CONFIG, ZEROCMP, 9, 1)
+ FIELD(CONFIG, DEGLITCH, 10, 1)
+ FIELD(CONFIG, ENALWAYS, 12, 1)
+ FIELD(CONFIG, ENONESHOT, 13, 1)
+ FIELD(CONFIG, CMP0CENTER, 16, 1)
+ FIELD(CONFIG, CMP1CENTER, 17, 1)
+ FIELD(CONFIG, CMP2CENTER, 18, 1)
+ FIELD(CONFIG, CMP3CENTER, 19, 1)
+ FIELD(CONFIG, CMP0GANG, 24, 1)
+ FIELD(CONFIG, CMP1GANG, 25, 1)
+ FIELD(CONFIG, CMP2GANG, 26, 1)
+ FIELD(CONFIG, CMP3GANG, 27, 1)
+ FIELD(CONFIG, CMP0IP, 28, 1)
+ FIELD(CONFIG, CMP1IP, 29, 1)
+ FIELD(CONFIG, CMP2IP, 30, 1)
+ FIELD(CONFIG, CMP3IP, 31, 1)
+REG32(COUNT, 0x08)
+REG32(PWMS, 0x10)
+REG32(PWMCMP0, 0x20)
+REG32(PWMCMP1, 0x24)
+REG32(PWMCMP2, 0x28)
+REG32(PWMCMP3, 0x2C)
+
+static inline uint64_t sifive_pwm_ns_to_ticks(SiFivePwmState *s,
+ uint64_t time)
+{
+ return muldiv64(time, s->freq_hz, NANOSECONDS_PER_SECOND);
+}
+
+static inline uint64_t sifive_pwm_ticks_to_ns(SiFivePwmState *s,
+ uint64_t ticks)
+{
+ return muldiv64(ticks, NANOSECONDS_PER_SECOND, s->freq_hz);
+}
+
+static inline uint64_t sifive_pwm_compute_scale(SiFivePwmState *s)
+{
+ return s->pwmcfg & R_CONFIG_SCALE_MASK;
+}
+
+static void sifive_pwm_set_alarms(SiFivePwmState *s)
+{
+ uint64_t now_ns = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
+
+ if (HAS_PWM_EN_BITS(s->pwmcfg)) {
+ /*
+ * Subtract ticks from number of ticks when the timer was zero
+ * and mask to the register width.
+ */
+ uint64_t pwmcount = (sifive_pwm_ns_to_ticks(s, now_ns) -
+ s->tick_offset) & PWMCOUNT_MASK;
+ uint64_t scale = sifive_pwm_compute_scale(s);
+ /* PWMs only contains PWMCMP_MASK bits starting at scale */
+ uint64_t pwms = (pwmcount & (PWMCMP_MASK << scale)) >> scale;
+
+ for (int i = 0; i < SIFIVE_PWM_CHANS; i++) {
+ uint64_t pwmcmp = s->pwmcmp[i] & PWMCMP_MASK;
+ uint64_t pwmcmp_ticks = pwmcmp << scale;
+
+ /*
+ * Per circuit diagram and spec, both cases raises corresponding
+ * IP bit one clock cycle after time expires.
+ */
+ if (pwmcmp > pwms) {
+ uint64_t offset = pwmcmp_ticks - pwmcount + 1;
+ uint64_t when_to_fire = now_ns +
+ sifive_pwm_ticks_to_ns(s, offset);
+
+ trace_sifive_pwm_set_alarm(when_to_fire, now_ns);
+ timer_mod(&s->timer[i], when_to_fire);
+ } else {
+ /* Schedule interrupt for next cycle */
+ trace_sifive_pwm_set_alarm(now_ns + 1, now_ns);
+ timer_mod(&s->timer[i], now_ns + 1);
+ }
+
+ }
+ } else {
+ /*
+ * If timer incrementing disabled, just do pwms > pwmcmp check since
+ * a write may have happened to PWMs.
+ */
+ uint64_t pwmcount = (s->tick_offset) & PWMCOUNT_MASK;
+ uint64_t scale = sifive_pwm_compute_scale(s);
+ uint64_t pwms = (pwmcount & (PWMCMP_MASK << scale)) >> scale;
+
+ for (int i = 0; i < SIFIVE_PWM_CHANS; i++) {
+ uint64_t pwmcmp = s->pwmcmp[i] & PWMCMP_MASK;
+
+ if (pwms >= pwmcmp) {
+ trace_sifive_pwm_set_alarm(now_ns + 1, now_ns);
+ timer_mod(&s->timer[i], now_ns + 1);
+ } else {
+ /* Effectively disable timer by scheduling far in future. */
+ trace_sifive_pwm_set_alarm(0xFFFFFFFFFFFFFF, now_ns);
+ timer_mod(&s->timer[i], 0xFFFFFFFFFFFFFF);
+ }
+ }
+ }
+}
+
+static void sifive_pwm_interrupt(SiFivePwmState *s, int num)
+{
+ uint64_t now = sifive_pwm_ns_to_ticks(s,
+ qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL));
+ bool was_incrementing = HAS_PWM_EN_BITS(s->pwmcfg);
+
+ trace_sifive_pwm_interrupt(num);
+
+ s->pwmcfg |= R_CONFIG_CMP0IP_MASK << num;
+ qemu_irq_raise(s->irqs[num]);
+
+ /*
+ * If the zerocmp is set and pwmcmp0 raised the interrupt
+ * reset the zero ticks.
+ */
+ if ((s->pwmcfg & R_CONFIG_ZEROCMP_MASK) && (num == 0)) {
+ /* If reset signal conditions, disable ENONESHOT. */
+ s->pwmcfg &= ~R_CONFIG_ENONESHOT_MASK;
+
+ if (was_incrementing) {
+ /* If incrementing, time in ticks is when pwmcount is zero */
+ s->tick_offset = now;
+ } else {
+ /* If not incrementing, pwmcount = 0 */
+ s->tick_offset = 0;
+ }
+ }
+
+ /*
+ * If carryout bit set, which we discern via looking for overflow,
+ * also reset ENONESHOT.
+ */
+ if (was_incrementing &&
+ ((now & PWMCOUNT_MASK) < (s->tick_offset & PWMCOUNT_MASK))) {
+ s->pwmcfg &= ~R_CONFIG_ENONESHOT_MASK;
+ }
+
+ /* Schedule or disable interrupts */
+ sifive_pwm_set_alarms(s);
+
+ /* If was enabled, and now not enabled, switch tick rep */
+ if (was_incrementing && !HAS_PWM_EN_BITS(s->pwmcfg)) {
+ s->tick_offset = (now - s->tick_offset) & PWMCOUNT_MASK;
+ }
+}
+
+static void sifive_pwm_interrupt_0(void *opaque)
+{
+ SiFivePwmState *s = opaque;
+
+ sifive_pwm_interrupt(s, 0);
+}
+
+static void sifive_pwm_interrupt_1(void *opaque)
+{
+ SiFivePwmState *s = opaque;
+
+ sifive_pwm_interrupt(s, 1);
+}
+
+static void sifive_pwm_interrupt_2(void *opaque)
+{
+ SiFivePwmState *s = opaque;
+
+ sifive_pwm_interrupt(s, 2);
+}
+
+static void sifive_pwm_interrupt_3(void *opaque)
+{
+ SiFivePwmState *s = opaque;
+
+ sifive_pwm_interrupt(s, 3);
+}
+
+static uint64_t sifive_pwm_read(void *opaque, hwaddr addr,
+ unsigned int size)
+{
+ SiFivePwmState *s = opaque;
+ uint64_t cur_time, scale;
+ uint64_t now = sifive_pwm_ns_to_ticks(s,
+ qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL));
+
+ trace_sifive_pwm_read(addr);
+
+ switch (addr) {
+ case A_CONFIG:
+ return s->pwmcfg;
+ case A_COUNT:
+ cur_time = s->tick_offset;
+
+ if (HAS_PWM_EN_BITS(s->pwmcfg)) {
+ cur_time = now - cur_time;
+ }
+
+ /*
+ * Return the value in the counter with bit 31 always 0
+ * This is allowed to wrap around so we don't need to check that.
+ */
+ return cur_time & PWMCOUNT_MASK;
+ case A_PWMS:
+ cur_time = s->tick_offset;
+ scale = sifive_pwm_compute_scale(s);
+
+ if (HAS_PWM_EN_BITS(s->pwmcfg)) {
+ cur_time = now - cur_time;
+ }
+
+ return ((cur_time & PWMCOUNT_MASK) >> scale) & PWMCMP_MASK;
+ case A_PWMCMP0:
+ return s->pwmcmp[0] & PWMCMP_MASK;
+ case A_PWMCMP1:
+ return s->pwmcmp[1] & PWMCMP_MASK;
+ case A_PWMCMP2:
+ return s->pwmcmp[2] & PWMCMP_MASK;
+ case A_PWMCMP3:
+ return s->pwmcmp[3] & PWMCMP_MASK;
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: Bad offset 0x%"HWADDR_PRIx"\n", __func__, addr);
+ return 0;
+ }
+
+ return 0;
+}
+
+static void sifive_pwm_write(void *opaque, hwaddr addr,
+ uint64_t val64, unsigned int size)
+{
+ SiFivePwmState *s = opaque;
+ uint32_t value = val64;
+ uint64_t new_offset, scale;
+ uint64_t now = sifive_pwm_ns_to_ticks(s,
+ qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL));
+
+ trace_sifive_pwm_write(value, addr);
+
+ switch (addr) {
+ case A_CONFIG:
+ if (value & (R_CONFIG_CMP0CENTER_MASK | R_CONFIG_CMP1CENTER_MASK |
+ R_CONFIG_CMP2CENTER_MASK | R_CONFIG_CMP3CENTER_MASK)) {
+ qemu_log_mask(LOG_UNIMP, "%s: CMPxCENTER is not supported\n",
+ __func__);
+ }
+
+ if (value & (R_CONFIG_CMP0GANG_MASK | R_CONFIG_CMP1GANG_MASK |
+ R_CONFIG_CMP2GANG_MASK | R_CONFIG_CMP3GANG_MASK)) {
+ qemu_log_mask(LOG_UNIMP, "%s: CMPxGANG is not supported\n",
+ __func__);
+ }
+
+ if (value & (R_CONFIG_CMP0IP_MASK | R_CONFIG_CMP1IP_MASK |
+ R_CONFIG_CMP2IP_MASK | R_CONFIG_CMP3IP_MASK)) {
+ qemu_log_mask(LOG_UNIMP, "%s: CMPxIP is not supported\n",
+ __func__);
+ }
+
+ if (!(value & R_CONFIG_CMP0IP_MASK)) {
+ qemu_irq_lower(s->irqs[0]);
+ }
+
+ if (!(value & R_CONFIG_CMP1IP_MASK)) {
+ qemu_irq_lower(s->irqs[1]);
+ }
+
+ if (!(value & R_CONFIG_CMP2IP_MASK)) {
+ qemu_irq_lower(s->irqs[2]);
+ }
+
+ if (!(value & R_CONFIG_CMP3IP_MASK)) {
+ qemu_irq_lower(s->irqs[3]);
+ }
+
+ /*
+ * If this write enables the timer increment
+ * set the time when pwmcount was zero to be cur_time - pwmcount.
+ * If this write disables the timer increment
+ * convert back from pwmcount to the time in ticks
+ * when pwmcount was zero.
+ */
+ if ((!HAS_PWM_EN_BITS(s->pwmcfg) && HAS_PWM_EN_BITS(value)) ||
+ (HAS_PWM_EN_BITS(s->pwmcfg) && !HAS_PWM_EN_BITS(value))) {
+ s->tick_offset = (now - s->tick_offset) & PWMCOUNT_MASK;
+ }
+
+ s->pwmcfg = value;
+ break;
+ case A_COUNT:
+ /* The guest changed the counter, updated the offset value. */
+ new_offset = value;
+
+ if (HAS_PWM_EN_BITS(s->pwmcfg)) {
+ new_offset = now - new_offset;
+ }
+
+ s->tick_offset = new_offset;
+ break;
+ case A_PWMS:
+ scale = sifive_pwm_compute_scale(s);
+ new_offset = (((value & PWMCMP_MASK) << scale) & PWMCOUNT_MASK);
+
+ if (HAS_PWM_EN_BITS(s->pwmcfg)) {
+ new_offset = now - new_offset;
+ }
+
+ s->tick_offset = new_offset;
+ break;
+ case A_PWMCMP0:
+ s->pwmcmp[0] = value & PWMCMP_MASK;
+ break;
+ case A_PWMCMP1:
+ s->pwmcmp[1] = value & PWMCMP_MASK;
+ break;
+ case A_PWMCMP2:
+ s->pwmcmp[2] = value & PWMCMP_MASK;
+ break;
+ case A_PWMCMP3:
+ s->pwmcmp[3] = value & PWMCMP_MASK;
+ break;
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: Bad offset 0x%"HWADDR_PRIx"\n", __func__, addr);
+ }
+
+ /* Update the alarms to reflect possible updated values */
+ sifive_pwm_set_alarms(s);
+}
+
+static void sifive_pwm_reset(DeviceState *dev)
+{
+ SiFivePwmState *s = SIFIVE_PWM(dev);
+ uint64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
+
+ s->pwmcfg = 0x00000000;
+ s->pwmcmp[0] = 0x00000000;
+ s->pwmcmp[1] = 0x00000000;
+ s->pwmcmp[2] = 0x00000000;
+ s->pwmcmp[3] = 0x00000000;
+
+ s->tick_offset = sifive_pwm_ns_to_ticks(s, now);
+}
+
+static const MemoryRegionOps sifive_pwm_ops = {
+ .read = sifive_pwm_read,
+ .write = sifive_pwm_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static const VMStateDescription vmstate_sifive_pwm = {
+ .name = TYPE_SIFIVE_PWM,
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_TIMER_ARRAY(timer, SiFivePwmState, 4),
+ VMSTATE_UINT64(tick_offset, SiFivePwmState),
+ VMSTATE_UINT32(pwmcfg, SiFivePwmState),
+ VMSTATE_UINT32_ARRAY(pwmcmp, SiFivePwmState, 4),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static Property sifive_pwm_properties[] = {
+ /* 0.5Ghz per spec after FSBL */
+ DEFINE_PROP_UINT64("clock-frequency", struct SiFivePwmState,
+ freq_hz, 500000000ULL),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void sifive_pwm_init(Object *obj)
+{
+ SiFivePwmState *s = SIFIVE_PWM(obj);
+ int i;
+
+ for (i = 0; i < SIFIVE_PWM_IRQS; i++) {
+ sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->irqs[i]);
+ }
+
+ memory_region_init_io(&s->mmio, obj, &sifive_pwm_ops, s,
+ TYPE_SIFIVE_PWM, 0x100);
+ sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmio);
+}
+
+static void sifive_pwm_realize(DeviceState *dev, Error **errp)
+{
+ SiFivePwmState *s = SIFIVE_PWM(dev);
+
+ timer_init_ns(&s->timer[0], QEMU_CLOCK_VIRTUAL,
+ sifive_pwm_interrupt_0, s);
+
+ timer_init_ns(&s->timer[1], QEMU_CLOCK_VIRTUAL,
+ sifive_pwm_interrupt_1, s);
+
+ timer_init_ns(&s->timer[2], QEMU_CLOCK_VIRTUAL,
+ sifive_pwm_interrupt_2, s);
+
+ timer_init_ns(&s->timer[3], QEMU_CLOCK_VIRTUAL,
+ sifive_pwm_interrupt_3, s);
+}
+
+static void sifive_pwm_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+
+ dc->reset = sifive_pwm_reset;
+ device_class_set_props(dc, sifive_pwm_properties);
+ dc->vmsd = &vmstate_sifive_pwm;
+ dc->realize = sifive_pwm_realize;
+}
+
+static const TypeInfo sifive_pwm_info = {
+ .name = TYPE_SIFIVE_PWM,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(SiFivePwmState),
+ .instance_init = sifive_pwm_init,
+ .class_init = sifive_pwm_class_init,
+};
+
+static void sifive_pwm_register_types(void)
+{
+ type_register_static(&sifive_pwm_info);
+}
+
+type_init(sifive_pwm_register_types)
diff --git a/hw/timer/trace-events b/hw/timer/trace-events
index 5234c0e..d0edcd2 100644
--- a/hw/timer/trace-events
+++ b/hw/timer/trace-events
@@ -88,3 +88,9 @@ sse_counter_reset(void) "SSE system counter: reset"
sse_timer_read(uint64_t offset, uint64_t data, unsigned size) "SSE system timer read: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u"
sse_timer_write(uint64_t offset, uint64_t data, unsigned size) "SSE system timer write: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u"
sse_timer_reset(void) "SSE system timer: reset"
+
+# sifive_pwm.c
+sifive_pwm_set_alarm(uint64_t alarm, uint64_t now) "Setting alarm to: 0x%" PRIx64 ", now: 0x%" PRIx64
+sifive_pwm_interrupt(int num) "Interrupt %d"
+sifive_pwm_read(uint64_t offset) "Read at address: 0x%" PRIx64
+sifive_pwm_write(uint64_t data, uint64_t offset) "Write 0x%" PRIx64 " at address: 0x%" PRIx64