mirror of
https://github.com/izzy2lost/xemu.git
synced 2026-07-06 00:20:22 -07:00
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>
This commit is contained in:
@@ -24,6 +24,7 @@ The ``sifive_u`` machine supports the following devices:
|
||||
* 2 QSPI controllers
|
||||
* 1 ISSI 25WP256 flash
|
||||
* 1 SD card in SPI mode
|
||||
* PWM0 and PWM1
|
||||
|
||||
Please note the real world HiFive Unleashed board has a fixed configuration of
|
||||
1 E51 core and 4 U54 core combination and the RISC-V core boots in 64-bit mode.
|
||||
@@ -209,15 +210,16 @@ command line options with ``qemu-system-riscv32``.
|
||||
Running U-Boot
|
||||
--------------
|
||||
|
||||
U-Boot mainline v2021.01 release is tested at the time of writing. To build a
|
||||
U-Boot mainline v2021.07 release is tested at the time of writing. To build a
|
||||
U-Boot mainline bootloader that can be booted by the ``sifive_u`` machine, use
|
||||
the sifive_fu540_defconfig with similar commands as described above for Linux:
|
||||
the sifive_unleashed_defconfig with similar commands as described above for
|
||||
Linux:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ export CROSS_COMPILE=riscv64-linux-
|
||||
$ export OPENSBI=/path/to/opensbi-riscv64-generic-fw_dynamic.bin
|
||||
$ make sifive_fu540_defconfig
|
||||
$ make sifive_unleashed_defconfig
|
||||
|
||||
You will get spl/u-boot-spl.bin and u-boot.itb file in the build tree.
|
||||
|
||||
@@ -312,31 +314,29 @@ board on QEMU ``sifive_u`` machine out of the box. This allows users to
|
||||
develop and test the recommended RISC-V boot flow with a real world use
|
||||
case: ZSBL (in QEMU) loads U-Boot SPL from SD card or SPI flash to L2LIM,
|
||||
then U-Boot SPL loads the combined payload image of OpenSBI fw_dynamic
|
||||
firmware and U-Boot proper. However sometimes we want to have a quick test
|
||||
of booting U-Boot on QEMU without the needs of preparing the SPI flash or
|
||||
SD card images, an alternate way can be used, which is to create a U-Boot
|
||||
S-mode image by modifying the configuration of U-Boot:
|
||||
firmware and U-Boot proper.
|
||||
|
||||
However sometimes we want to have a quick test of booting U-Boot on QEMU
|
||||
without the needs of preparing the SPI flash or SD card images, an alternate
|
||||
way can be used, which is to create a U-Boot S-mode image by modifying the
|
||||
configuration of U-Boot:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ export CROSS_COMPILE=riscv64-linux-
|
||||
$ make sifive_unleashed_defconfig
|
||||
$ make menuconfig
|
||||
|
||||
then manually select the following configuration in U-Boot:
|
||||
then manually select the following configuration:
|
||||
|
||||
Device Tree Control > Provider of DTB for DT Control > Prior Stage bootloader DTB
|
||||
* Device Tree Control ---> Provider of DTB for DT Control ---> Prior Stage bootloader DTB
|
||||
|
||||
This lets U-Boot to use the QEMU generated device tree blob. During the build,
|
||||
a build error will be seen below:
|
||||
and unselect the following configuration:
|
||||
|
||||
.. code-block:: none
|
||||
* Library routines ---> Allow access to binman information in the device tree
|
||||
|
||||
MKIMAGE u-boot.img
|
||||
./tools/mkimage: Can't open arch/riscv/dts/hifive-unleashed-a00.dtb: No such file or directory
|
||||
./tools/mkimage: failed to build FIT
|
||||
make: *** [Makefile:1440: u-boot.img] Error 1
|
||||
|
||||
The above errors can be safely ignored as we don't run U-Boot SPL under QEMU
|
||||
in this alternate configuration.
|
||||
This changes U-Boot to use the QEMU generated device tree blob, and bypass
|
||||
running the U-Boot SPL stage.
|
||||
|
||||
Boot the 64-bit U-Boot S-mode image directly:
|
||||
|
||||
@@ -351,14 +351,18 @@ It's possible to create a 32-bit U-Boot S-mode image as well.
|
||||
.. code-block:: bash
|
||||
|
||||
$ export CROSS_COMPILE=riscv64-linux-
|
||||
$ make sifive_fu540_defconfig
|
||||
$ make sifive_unleashed_defconfig
|
||||
$ make menuconfig
|
||||
|
||||
then manually update the following configuration in U-Boot:
|
||||
|
||||
Device Tree Control > Provider of DTB for DT Control > Prior Stage bootloader DTB
|
||||
RISC-V architecture > Base ISA > RV32I
|
||||
Boot images > Text Base > 0x80400000
|
||||
* Device Tree Control ---> Provider of DTB for DT Control ---> Prior Stage bootloader DTB
|
||||
* RISC-V architecture ---> Base ISA ---> RV32I
|
||||
* Boot options ---> Boot images ---> Text Base ---> 0x80400000
|
||||
|
||||
and unselect the following configuration:
|
||||
|
||||
* Library routines ---> Allow access to binman information in the device tree
|
||||
|
||||
Use the same command line options to boot the 32-bit U-Boot S-mode image:
|
||||
|
||||
|
||||
@@ -53,6 +53,16 @@ with the default OpenSBI firmware image as the -bios. It also supports
|
||||
the recommended RISC-V bootflow: U-Boot SPL (M-mode) loads OpenSBI fw_dynamic
|
||||
firmware and U-Boot proper (S-mode), using the standard -bios functionality.
|
||||
|
||||
Machine-specific options
|
||||
------------------------
|
||||
|
||||
The following machine-specific options are supported:
|
||||
|
||||
- aclint=[on|off]
|
||||
|
||||
When this option is "on", ACLINT devices will be emulated instead of
|
||||
SiFive CLINT. When not specified, this option is assumed to be "off".
|
||||
|
||||
Running Linux kernel
|
||||
--------------------
|
||||
|
||||
|
||||
+43
-11
@@ -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);
|
||||
}
|
||||
|
||||
+1
-1
@@ -62,7 +62,7 @@ config RX_ICU
|
||||
config LOONGSON_LIOINTC
|
||||
bool
|
||||
|
||||
config SIFIVE_CLINT
|
||||
config RISCV_ACLINT
|
||||
bool
|
||||
|
||||
config SIFIVE_PLIC
|
||||
|
||||
+6
-11
@@ -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
|
||||
|
||||
+1
-1
@@ -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'],
|
||||
|
||||
@@ -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)
|
||||
@@ -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;
|
||||
}
|
||||
+23
-7
@@ -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;
|
||||
}
|
||||
|
||||
+7
-6
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
+12
-1
@@ -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);
|
||||
|
||||
+10
-6
@@ -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)) {
|
||||
|
||||
+9
-6
@@ -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);
|
||||
|
||||
+62
-6
@@ -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);
|
||||
|
||||
|
||||
+10
-6
@@ -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) */
|
||||
|
||||
+443
-201
File diff suppressed because it is too large
Load Diff
@@ -25,6 +25,9 @@ config ALLWINNER_A10_PIT
|
||||
bool
|
||||
select PTIMER
|
||||
|
||||
config SIFIVE_PWM
|
||||
bool
|
||||
|
||||
config STM32F2XX_TIMER
|
||||
bool
|
||||
|
||||
|
||||
+12
-5
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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'))
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user