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:
Richard Henderson
2021-09-21 10:57:48 -07:00
34 changed files with 1839 additions and 664 deletions
+27 -23
View File
@@ -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:
+10
View File
@@ -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
View File
@@ -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
View File
@@ -62,7 +62,7 @@ config RX_ICU
config LOONGSON_LIOINTC
bool
config SIFIVE_CLINT
config RISCV_ACLINT
bool
config SIFIVE_PLIC
+6 -11
View File
@@ -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
View File
@@ -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'],
+460
View File
@@ -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)
-287
View File
@@ -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
View File
@@ -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
View File
@@ -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
+8 -5
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
File diff suppressed because it is too large Load Diff
+3
View File
@@ -25,6 +25,9 @@ config ALLWINNER_A10_PIT
bool
select PTIMER
config SIFIVE_PWM
bool
config STM32F2XX_TIMER
bool
+12 -5
View File
@@ -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);
}
+1
View File
@@ -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