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
 -----BEGIN PGP SIGNATURE-----
 
 iQEzBAABCAAdFiEE9sSsRtSTSGjTuM6PIeENKd+XcFQFAmFJgSoACgkQIeENKd+X
 cFQTOwf8DC7rBqOWQS3v/r+H2hlfDqW+4G3pPPBcoyCEiqO+cL26ox+EmTHDbieh
 +0yWyp7L6SU/zcJ86oBAFNGH46ltXuUKOYWhkfA1QwlGzAwjZ82hnZ3jJqXf1jin
 Wq0ElzKk6rvcRkHTVhdjkGvoxskaXPQ/kFzyTHrxMDlkmHO3L4IaYe0xsamRI11D
 E7UJC97YmpSAsCNUc5irpkeLyiFobyR8TEL3nBEPK/6Xj0ojRT4zoGe1EotC7+sN
 zL8a9ZuU0bL3rQH8Ai7wnXBP8D2PQa0tZQV6wne/BzeEUSpKrC/rGW73vQCz0Pps
 U8VNkIlbAqD1s6aXlqE24H535x10Mw==
 =WYF5
 -----END PGP SIGNATURE-----

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
commit 2c3e83f92d
34 changed files with 1839 additions and 664 deletions

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:

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
--------------------

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);
}

View File

@ -62,7 +62,7 @@ config RX_ICU
config LOONGSON_LIOINTC
bool
config SIFIVE_CLINT
config RISCV_ACLINT
bool
config SIFIVE_PLIC

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

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
hw/intc/riscv_aclint.c Normal file
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)

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;
}

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;
}

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

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,

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);

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)) {

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);

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);

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) */

View File

@ -32,7 +32,7 @@
#include "hw/riscv/virt.h"
#include "hw/riscv/boot.h"
#include "hw/riscv/numa.h"
#include "hw/intc/sifive_clint.h"
#include "hw/intc/riscv_aclint.h"
#include "hw/intc/sifive_plic.h"
#include "hw/misc/sifive_test.h"
#include "chardev/char.h"
@ -48,6 +48,7 @@ static const MemMapEntry virt_memmap[] = {
[VIRT_TEST] = { 0x100000, 0x1000 },
[VIRT_RTC] = { 0x101000, 0x1000 },
[VIRT_CLINT] = { 0x2000000, 0x10000 },
[VIRT_ACLINT_SSWI] = { 0x2F00000, 0x4000 },
[VIRT_PCIE_PIO] = { 0x3000000, 0x10000 },
[VIRT_PLIC] = { 0xc000000, VIRT_PLIC_SIZE(VIRT_CPUS_MAX * 2) },
[VIRT_UART0] = { 0x10000000, 0x100 },
@ -176,214 +177,342 @@ static void create_pcie_irq_map(void *fdt, char *nodename,
0x1800, 0, 0, 0x7);
}
static void create_fdt(RISCVVirtState *s, const MemMapEntry *memmap,
uint64_t mem_size, const char *cmdline, bool is_32_bit)
static void create_fdt_socket_cpus(RISCVVirtState *s, int socket,
char *clust_name, uint32_t *phandle,
bool is_32_bit, uint32_t *intc_phandles)
{
void *fdt;
int i, cpu, socket;
int cpu;
uint32_t cpu_phandle;
MachineState *mc = MACHINE(s);
char *name, *cpu_name, *core_name, *intc_name;
for (cpu = s->soc[socket].num_harts - 1; cpu >= 0; cpu--) {
cpu_phandle = (*phandle)++;
cpu_name = g_strdup_printf("/cpus/cpu@%d",
s->soc[socket].hartid_base + cpu);
qemu_fdt_add_subnode(mc->fdt, cpu_name);
qemu_fdt_setprop_string(mc->fdt, cpu_name, "mmu-type",
(is_32_bit) ? "riscv,sv32" : "riscv,sv48");
name = riscv_isa_string(&s->soc[socket].harts[cpu]);
qemu_fdt_setprop_string(mc->fdt, cpu_name, "riscv,isa", name);
g_free(name);
qemu_fdt_setprop_string(mc->fdt, cpu_name, "compatible", "riscv");
qemu_fdt_setprop_string(mc->fdt, cpu_name, "status", "okay");
qemu_fdt_setprop_cell(mc->fdt, cpu_name, "reg",
s->soc[socket].hartid_base + cpu);
qemu_fdt_setprop_string(mc->fdt, cpu_name, "device_type", "cpu");
riscv_socket_fdt_write_id(mc, mc->fdt, cpu_name, socket);
qemu_fdt_setprop_cell(mc->fdt, cpu_name, "phandle", cpu_phandle);
intc_phandles[cpu] = (*phandle)++;
intc_name = g_strdup_printf("%s/interrupt-controller", cpu_name);
qemu_fdt_add_subnode(mc->fdt, intc_name);
qemu_fdt_setprop_cell(mc->fdt, intc_name, "phandle",
intc_phandles[cpu]);
qemu_fdt_setprop_string(mc->fdt, intc_name, "compatible",
"riscv,cpu-intc");
qemu_fdt_setprop(mc->fdt, intc_name, "interrupt-controller", NULL, 0);
qemu_fdt_setprop_cell(mc->fdt, intc_name, "#interrupt-cells", 1);
core_name = g_strdup_printf("%s/core%d", clust_name, cpu);
qemu_fdt_add_subnode(mc->fdt, core_name);
qemu_fdt_setprop_cell(mc->fdt, core_name, "cpu", cpu_phandle);
g_free(core_name);
g_free(intc_name);
g_free(cpu_name);
}
}
static void create_fdt_socket_memory(RISCVVirtState *s,
const MemMapEntry *memmap, int socket)
{
char *mem_name;
uint64_t addr, size;
uint32_t *clint_cells, *plic_cells;
unsigned long clint_addr, plic_addr;
uint32_t plic_phandle[MAX_NODES];
uint32_t cpu_phandle, intc_phandle, test_phandle;
uint32_t phandle = 1, plic_mmio_phandle = 1;
uint32_t plic_pcie_phandle = 1, plic_virtio_phandle = 1;
char *mem_name, *cpu_name, *core_name, *intc_name;
char *name, *clint_name, *plic_name, *clust_name;
hwaddr flashsize = virt_memmap[VIRT_FLASH].size / 2;
hwaddr flashbase = virt_memmap[VIRT_FLASH].base;
MachineState *mc = MACHINE(s);
addr = memmap[VIRT_DRAM].base + riscv_socket_mem_offset(mc, socket);
size = riscv_socket_mem_size(mc, socket);
mem_name = g_strdup_printf("/memory@%lx", (long)addr);
qemu_fdt_add_subnode(mc->fdt, mem_name);
qemu_fdt_setprop_cells(mc->fdt, mem_name, "reg",
addr >> 32, addr, size >> 32, size);
qemu_fdt_setprop_string(mc->fdt, mem_name, "device_type", "memory");
riscv_socket_fdt_write_id(mc, mc->fdt, mem_name, socket);
g_free(mem_name);
}
static void create_fdt_socket_clint(RISCVVirtState *s,
const MemMapEntry *memmap, int socket,
uint32_t *intc_phandles)
{
int cpu;
char *clint_name;
uint32_t *clint_cells;
unsigned long clint_addr;
MachineState *mc = MACHINE(s);
static const char * const clint_compat[2] = {
"sifive,clint0", "riscv,clint0"
};
clint_cells = g_new0(uint32_t, s->soc[socket].num_harts * 4);
for (cpu = 0; cpu < s->soc[socket].num_harts; cpu++) {
clint_cells[cpu * 4 + 0] = cpu_to_be32(intc_phandles[cpu]);
clint_cells[cpu * 4 + 1] = cpu_to_be32(IRQ_M_SOFT);
clint_cells[cpu * 4 + 2] = cpu_to_be32(intc_phandles[cpu]);
clint_cells[cpu * 4 + 3] = cpu_to_be32(IRQ_M_TIMER);
}
clint_addr = memmap[VIRT_CLINT].base + (memmap[VIRT_CLINT].size * socket);
clint_name = g_strdup_printf("/soc/clint@%lx", clint_addr);
qemu_fdt_add_subnode(mc->fdt, clint_name);
qemu_fdt_setprop_string_array(mc->fdt, clint_name, "compatible",
(char **)&clint_compat,
ARRAY_SIZE(clint_compat));
qemu_fdt_setprop_cells(mc->fdt, clint_name, "reg",
0x0, clint_addr, 0x0, memmap[VIRT_CLINT].size);
qemu_fdt_setprop(mc->fdt, clint_name, "interrupts-extended",
clint_cells, s->soc[socket].num_harts * sizeof(uint32_t) * 4);
riscv_socket_fdt_write_id(mc, mc->fdt, clint_name, socket);
g_free(clint_name);
g_free(clint_cells);
}
static void create_fdt_socket_aclint(RISCVVirtState *s,
const MemMapEntry *memmap, int socket,
uint32_t *intc_phandles)
{
int cpu;
char *name;
unsigned long addr;
uint32_t aclint_cells_size;
uint32_t *aclint_mswi_cells;
uint32_t *aclint_sswi_cells;
uint32_t *aclint_mtimer_cells;
MachineState *mc = MACHINE(s);
aclint_mswi_cells = g_new0(uint32_t, s->soc[socket].num_harts * 2);
aclint_mtimer_cells = g_new0(uint32_t, s->soc[socket].num_harts * 2);
aclint_sswi_cells = g_new0(uint32_t, s->soc[socket].num_harts * 2);
for (cpu = 0; cpu < s->soc[socket].num_harts; cpu++) {
aclint_mswi_cells[cpu * 2 + 0] = cpu_to_be32(intc_phandles[cpu]);
aclint_mswi_cells[cpu * 2 + 1] = cpu_to_be32(IRQ_M_SOFT);
aclint_mtimer_cells[cpu * 2 + 0] = cpu_to_be32(intc_phandles[cpu]);
aclint_mtimer_cells[cpu * 2 + 1] = cpu_to_be32(IRQ_M_TIMER);
aclint_sswi_cells[cpu * 2 + 0] = cpu_to_be32(intc_phandles[cpu]);
aclint_sswi_cells[cpu * 2 + 1] = cpu_to_be32(IRQ_S_SOFT);
}
aclint_cells_size = s->soc[socket].num_harts * sizeof(uint32_t) * 2;
addr = memmap[VIRT_CLINT].base + (memmap[VIRT_CLINT].size * socket);
name = g_strdup_printf("/soc/mswi@%lx", addr);
qemu_fdt_add_subnode(mc->fdt, name);
qemu_fdt_setprop_string(mc->fdt, name, "compatible", "riscv,aclint-mswi");
qemu_fdt_setprop_cells(mc->fdt, name, "reg",
0x0, addr, 0x0, RISCV_ACLINT_SWI_SIZE);
qemu_fdt_setprop(mc->fdt, name, "interrupts-extended",
aclint_mswi_cells, aclint_cells_size);
qemu_fdt_setprop(mc->fdt, name, "interrupt-controller", NULL, 0);
qemu_fdt_setprop_cell(mc->fdt, name, "#interrupt-cells", 0);
riscv_socket_fdt_write_id(mc, mc->fdt, name, socket);
g_free(name);
addr = memmap[VIRT_CLINT].base + RISCV_ACLINT_SWI_SIZE +
(memmap[VIRT_CLINT].size * socket);
name = g_strdup_printf("/soc/mtimer@%lx", addr);
qemu_fdt_add_subnode(mc->fdt, name);
qemu_fdt_setprop_string(mc->fdt, name, "compatible",
"riscv,aclint-mtimer");
qemu_fdt_setprop_cells(mc->fdt, name, "reg",
0x0, addr + RISCV_ACLINT_DEFAULT_MTIME,
0x0, memmap[VIRT_CLINT].size - RISCV_ACLINT_SWI_SIZE -
RISCV_ACLINT_DEFAULT_MTIME,
0x0, addr + RISCV_ACLINT_DEFAULT_MTIMECMP,
0x0, RISCV_ACLINT_DEFAULT_MTIME);
qemu_fdt_setprop(mc->fdt, name, "interrupts-extended",
aclint_mtimer_cells, aclint_cells_size);
riscv_socket_fdt_write_id(mc, mc->fdt, name, socket);
g_free(name);
addr = memmap[VIRT_ACLINT_SSWI].base +
(memmap[VIRT_ACLINT_SSWI].size * socket);
name = g_strdup_printf("/soc/sswi@%lx", addr);
qemu_fdt_add_subnode(mc->fdt, name);
qemu_fdt_setprop_string(mc->fdt, name, "compatible", "riscv,aclint-sswi");
qemu_fdt_setprop_cells(mc->fdt, name, "reg",
0x0, addr, 0x0, memmap[VIRT_ACLINT_SSWI].size);
qemu_fdt_setprop(mc->fdt, name, "interrupts-extended",
aclint_sswi_cells, aclint_cells_size);
qemu_fdt_setprop(mc->fdt, name, "interrupt-controller", NULL, 0);
qemu_fdt_setprop_cell(mc->fdt, name, "#interrupt-cells", 0);
riscv_socket_fdt_write_id(mc, mc->fdt, name, socket);
g_free(name);
g_free(aclint_mswi_cells);
g_free(aclint_mtimer_cells);
g_free(aclint_sswi_cells);
}
static void create_fdt_socket_plic(RISCVVirtState *s,
const MemMapEntry *memmap, int socket,
uint32_t *phandle, uint32_t *intc_phandles,
uint32_t *plic_phandles)
{
int cpu;
char *plic_name;
uint32_t *plic_cells;
unsigned long plic_addr;
MachineState *mc = MACHINE(s);
static const char * const plic_compat[2] = {
"sifive,plic-1.0.0", "riscv,plic0"
};
if (mc->dtb) {
fdt = mc->fdt = load_device_tree(mc->dtb, &s->fdt_size);
if (!fdt) {
error_report("load_device_tree() failed");
exit(1);
}
goto update_bootargs;
} else {
fdt = mc->fdt = create_device_tree(&s->fdt_size);
if (!fdt) {
error_report("create_device_tree() failed");
exit(1);
}
plic_cells = g_new0(uint32_t, s->soc[socket].num_harts * 4);
for (cpu = 0; cpu < s->soc[socket].num_harts; cpu++) {
plic_cells[cpu * 4 + 0] = cpu_to_be32(intc_phandles[cpu]);
plic_cells[cpu * 4 + 1] = cpu_to_be32(IRQ_M_EXT);
plic_cells[cpu * 4 + 2] = cpu_to_be32(intc_phandles[cpu]);
plic_cells[cpu * 4 + 3] = cpu_to_be32(IRQ_S_EXT);
}
qemu_fdt_setprop_string(fdt, "/", "model", "riscv-virtio,qemu");
qemu_fdt_setprop_string(fdt, "/", "compatible", "riscv-virtio");
qemu_fdt_setprop_cell(fdt, "/", "#size-cells", 0x2);
qemu_fdt_setprop_cell(fdt, "/", "#address-cells", 0x2);
plic_phandles[socket] = (*phandle)++;
plic_addr = memmap[VIRT_PLIC].base + (memmap[VIRT_PLIC].size * socket);
plic_name = g_strdup_printf("/soc/plic@%lx", plic_addr);
qemu_fdt_add_subnode(mc->fdt, plic_name);
qemu_fdt_setprop_cell(mc->fdt, plic_name,
"#address-cells", FDT_PLIC_ADDR_CELLS);
qemu_fdt_setprop_cell(mc->fdt, plic_name,
"#interrupt-cells", FDT_PLIC_INT_CELLS);
qemu_fdt_setprop_string_array(mc->fdt, plic_name, "compatible",
(char **)&plic_compat,
ARRAY_SIZE(plic_compat));
qemu_fdt_setprop(mc->fdt, plic_name, "interrupt-controller", NULL, 0);
qemu_fdt_setprop(mc->fdt, plic_name, "interrupts-extended",
plic_cells, s->soc[socket].num_harts * sizeof(uint32_t) * 4);
qemu_fdt_setprop_cells(mc->fdt, plic_name, "reg",
0x0, plic_addr, 0x0, memmap[VIRT_PLIC].size);
qemu_fdt_setprop_cell(mc->fdt, plic_name, "riscv,ndev", VIRTIO_NDEV);
riscv_socket_fdt_write_id(mc, mc->fdt, plic_name, socket);
qemu_fdt_setprop_cell(mc->fdt, plic_name, "phandle",
plic_phandles[socket]);
g_free(plic_name);
qemu_fdt_add_subnode(fdt, "/soc");
qemu_fdt_setprop(fdt, "/soc", "ranges", NULL, 0);
qemu_fdt_setprop_string(fdt, "/soc", "compatible", "simple-bus");
qemu_fdt_setprop_cell(fdt, "/soc", "#size-cells", 0x2);
qemu_fdt_setprop_cell(fdt, "/soc", "#address-cells", 0x2);
g_free(plic_cells);
}
qemu_fdt_add_subnode(fdt, "/cpus");
qemu_fdt_setprop_cell(fdt, "/cpus", "timebase-frequency",
SIFIVE_CLINT_TIMEBASE_FREQ);
qemu_fdt_setprop_cell(fdt, "/cpus", "#size-cells", 0x0);
qemu_fdt_setprop_cell(fdt, "/cpus", "#address-cells", 0x1);
qemu_fdt_add_subnode(fdt, "/cpus/cpu-map");
static void create_fdt_sockets(RISCVVirtState *s, const MemMapEntry *memmap,
bool is_32_bit, uint32_t *phandle,
uint32_t *irq_mmio_phandle,
uint32_t *irq_pcie_phandle,
uint32_t *irq_virtio_phandle)
{
int socket;
char *clust_name;
uint32_t *intc_phandles;
MachineState *mc = MACHINE(s);
uint32_t xplic_phandles[MAX_NODES];
qemu_fdt_add_subnode(mc->fdt, "/cpus");
qemu_fdt_setprop_cell(mc->fdt, "/cpus", "timebase-frequency",
RISCV_ACLINT_DEFAULT_TIMEBASE_FREQ);
qemu_fdt_setprop_cell(mc->fdt, "/cpus", "#size-cells", 0x0);
qemu_fdt_setprop_cell(mc->fdt, "/cpus", "#address-cells", 0x1);
qemu_fdt_add_subnode(mc->fdt, "/cpus/cpu-map");
for (socket = (riscv_socket_count(mc) - 1); socket >= 0; socket--) {
clust_name = g_strdup_printf("/cpus/cpu-map/cluster%d", socket);
qemu_fdt_add_subnode(fdt, clust_name);
qemu_fdt_add_subnode(mc->fdt, clust_name);
plic_cells = g_new0(uint32_t, s->soc[socket].num_harts * 4);
clint_cells = g_new0(uint32_t, s->soc[socket].num_harts * 4);
intc_phandles = g_new0(uint32_t, s->soc[socket].num_harts);
for (cpu = s->soc[socket].num_harts - 1; cpu >= 0; cpu--) {
cpu_phandle = phandle++;
create_fdt_socket_cpus(s, socket, clust_name, phandle,
is_32_bit, intc_phandles);
cpu_name = g_strdup_printf("/cpus/cpu@%d",
s->soc[socket].hartid_base + cpu);
qemu_fdt_add_subnode(fdt, cpu_name);
if (is_32_bit) {
qemu_fdt_setprop_string(fdt, cpu_name, "mmu-type", "riscv,sv32");
} else {
qemu_fdt_setprop_string(fdt, cpu_name, "mmu-type", "riscv,sv48");
}
name = riscv_isa_string(&s->soc[socket].harts[cpu]);
qemu_fdt_setprop_string(fdt, cpu_name, "riscv,isa", name);
g_free(name);
qemu_fdt_setprop_string(fdt, cpu_name, "compatible", "riscv");
qemu_fdt_setprop_string(fdt, cpu_name, "status", "okay");
qemu_fdt_setprop_cell(fdt, cpu_name, "reg",
s->soc[socket].hartid_base + cpu);
qemu_fdt_setprop_string(fdt, cpu_name, "device_type", "cpu");
riscv_socket_fdt_write_id(mc, fdt, cpu_name, socket);
qemu_fdt_setprop_cell(fdt, cpu_name, "phandle", cpu_phandle);
create_fdt_socket_memory(s, memmap, socket);
intc_name = g_strdup_printf("%s/interrupt-controller", cpu_name);
qemu_fdt_add_subnode(fdt, intc_name);
intc_phandle = phandle++;
qemu_fdt_setprop_cell(fdt, intc_name, "phandle", intc_phandle);
qemu_fdt_setprop_string(fdt, intc_name, "compatible",
"riscv,cpu-intc");
qemu_fdt_setprop(fdt, intc_name, "interrupt-controller", NULL, 0);
qemu_fdt_setprop_cell(fdt, intc_name, "#interrupt-cells", 1);
clint_cells[cpu * 4 + 0] = cpu_to_be32(intc_phandle);
clint_cells[cpu * 4 + 1] = cpu_to_be32(IRQ_M_SOFT);
clint_cells[cpu * 4 + 2] = cpu_to_be32(intc_phandle);
clint_cells[cpu * 4 + 3] = cpu_to_be32(IRQ_M_TIMER);
plic_cells[cpu * 4 + 0] = cpu_to_be32(intc_phandle);
plic_cells[cpu * 4 + 1] = cpu_to_be32(IRQ_M_EXT);
plic_cells[cpu * 4 + 2] = cpu_to_be32(intc_phandle);
plic_cells[cpu * 4 + 3] = cpu_to_be32(IRQ_S_EXT);
core_name = g_strdup_printf("%s/core%d", clust_name, cpu);
qemu_fdt_add_subnode(fdt, core_name);
qemu_fdt_setprop_cell(fdt, core_name, "cpu", cpu_phandle);
g_free(core_name);
g_free(intc_name);
g_free(cpu_name);
if (s->have_aclint) {
create_fdt_socket_aclint(s, memmap, socket, intc_phandles);
} else {
create_fdt_socket_clint(s, memmap, socket, intc_phandles);
}
addr = memmap[VIRT_DRAM].base + riscv_socket_mem_offset(mc, socket);
size = riscv_socket_mem_size(mc, socket);
mem_name = g_strdup_printf("/memory@%lx", (long)addr);
qemu_fdt_add_subnode(fdt, mem_name);
qemu_fdt_setprop_cells(fdt, mem_name, "reg",
addr >> 32, addr, size >> 32, size);
qemu_fdt_setprop_string(fdt, mem_name, "device_type", "memory");
riscv_socket_fdt_write_id(mc, fdt, mem_name, socket);
g_free(mem_name);
create_fdt_socket_plic(s, memmap, socket, phandle,
intc_phandles, xplic_phandles);
clint_addr = memmap[VIRT_CLINT].base +
(memmap[VIRT_CLINT].size * socket);
clint_name = g_strdup_printf("/soc/clint@%lx", clint_addr);
qemu_fdt_add_subnode(fdt, clint_name);
qemu_fdt_setprop_string_array(fdt, clint_name, "compatible",
(char **)&clint_compat, ARRAY_SIZE(clint_compat));
qemu_fdt_setprop_cells(fdt, clint_name, "reg",
0x0, clint_addr, 0x0, memmap[VIRT_CLINT].size);
qemu_fdt_setprop(fdt, clint_name, "interrupts-extended",
clint_cells, s->soc[socket].num_harts * sizeof(uint32_t) * 4);
riscv_socket_fdt_write_id(mc, fdt, clint_name, socket);
g_free(clint_name);
plic_phandle[socket] = phandle++;
plic_addr = memmap[VIRT_PLIC].base + (memmap[VIRT_PLIC].size * socket);
plic_name = g_strdup_printf("/soc/plic@%lx", plic_addr);
qemu_fdt_add_subnode(fdt, plic_name);
qemu_fdt_setprop_cell(fdt, plic_name,
"#address-cells", FDT_PLIC_ADDR_CELLS);
qemu_fdt_setprop_cell(fdt, plic_name,
"#interrupt-cells", FDT_PLIC_INT_CELLS);
qemu_fdt_setprop_string_array(fdt, plic_name, "compatible",
(char **)&plic_compat, ARRAY_SIZE(plic_compat));
qemu_fdt_setprop(fdt, plic_name, "interrupt-controller", NULL, 0);
qemu_fdt_setprop(fdt, plic_name, "interrupts-extended",
plic_cells, s->soc[socket].num_harts * sizeof(uint32_t) * 4);
qemu_fdt_setprop_cells(fdt, plic_name, "reg",
0x0, plic_addr, 0x0, memmap[VIRT_PLIC].size);
qemu_fdt_setprop_cell(fdt, plic_name, "riscv,ndev", VIRTIO_NDEV);
riscv_socket_fdt_write_id(mc, fdt, plic_name, socket);
qemu_fdt_setprop_cell(fdt, plic_name, "phandle", plic_phandle[socket]);
g_free(plic_name);
g_free(clint_cells);
g_free(plic_cells);
g_free(intc_phandles);
g_free(clust_name);
}
for (socket = 0; socket < riscv_socket_count(mc); socket++) {
if (socket == 0) {
plic_mmio_phandle = plic_phandle[socket];
plic_virtio_phandle = plic_phandle[socket];
plic_pcie_phandle = plic_phandle[socket];
*irq_mmio_phandle = xplic_phandles[socket];
*irq_virtio_phandle = xplic_phandles[socket];
*irq_pcie_phandle = xplic_phandles[socket];
}
if (socket == 1) {
plic_virtio_phandle = plic_phandle[socket];
plic_pcie_phandle = plic_phandle[socket];
*irq_virtio_phandle = xplic_phandles[socket];
*irq_pcie_phandle = xplic_phandles[socket];
}
if (socket == 2) {
plic_pcie_phandle = plic_phandle[socket];
*irq_pcie_phandle = xplic_phandles[socket];
}
}
riscv_socket_fdt_write_distance_matrix(mc, fdt);
riscv_socket_fdt_write_distance_matrix(mc, mc->fdt);
}
static void create_fdt_virtio(RISCVVirtState *s, const MemMapEntry *memmap,
uint32_t irq_virtio_phandle)
{
int i;
char *name;
MachineState *mc = MACHINE(s);
for (i = 0; i < VIRTIO_COUNT; i++) {
name = g_strdup_printf("/soc/virtio_mmio@%lx",
(long)(memmap[VIRT_VIRTIO].base + i * memmap[VIRT_VIRTIO].size));
qemu_fdt_add_subnode(fdt, name);
qemu_fdt_setprop_string(fdt, name, "compatible", "virtio,mmio");
qemu_fdt_setprop_cells(fdt, name, "reg",
qemu_fdt_add_subnode(mc->fdt, name);
qemu_fdt_setprop_string(mc->fdt, name, "compatible", "virtio,mmio");
qemu_fdt_setprop_cells(mc->fdt, name, "reg",
0x0, memmap[VIRT_VIRTIO].base + i * memmap[VIRT_VIRTIO].size,
0x0, memmap[VIRT_VIRTIO].size);
qemu_fdt_setprop_cell(fdt, name, "interrupt-parent",
plic_virtio_phandle);
qemu_fdt_setprop_cell(fdt, name, "interrupts", VIRTIO_IRQ + i);
qemu_fdt_setprop_cell(mc->fdt, name, "interrupt-parent",
irq_virtio_phandle);
qemu_fdt_setprop_cell(mc->fdt, name, "interrupts", VIRTIO_IRQ + i);
g_free(name);
}
}
static void create_fdt_pcie(RISCVVirtState *s, const MemMapEntry *memmap,
uint32_t irq_pcie_phandle)
{
char *name;
MachineState *mc = MACHINE(s);
name = g_strdup_printf("/soc/pci@%lx",
(long) memmap[VIRT_PCIE_ECAM].base);
qemu_fdt_add_subnode(fdt, name);
qemu_fdt_setprop_cell(fdt, name, "#address-cells", FDT_PCI_ADDR_CELLS);
qemu_fdt_setprop_cell(fdt, name, "#interrupt-cells", FDT_PCI_INT_CELLS);
qemu_fdt_setprop_cell(fdt, name, "#size-cells", 0x2);
qemu_fdt_setprop_string(fdt, name, "compatible", "pci-host-ecam-generic");
qemu_fdt_setprop_string(fdt, name, "device_type", "pci");
qemu_fdt_setprop_cell(fdt, name, "linux,pci-domain", 0);
qemu_fdt_setprop_cells(fdt, name, "bus-range", 0,
qemu_fdt_add_subnode(mc->fdt, name);
qemu_fdt_setprop_cell(mc->fdt, name, "#address-cells",
FDT_PCI_ADDR_CELLS);
qemu_fdt_setprop_cell(mc->fdt, name, "#interrupt-cells",
FDT_PCI_INT_CELLS);
qemu_fdt_setprop_cell(mc->fdt, name, "#size-cells", 0x2);
qemu_fdt_setprop_string(mc->fdt, name, "compatible",
"pci-host-ecam-generic");
qemu_fdt_setprop_string(mc->fdt, name, "device_type", "pci");
qemu_fdt_setprop_cell(mc->fdt, name, "linux,pci-domain", 0);
qemu_fdt_setprop_cells(mc->fdt, name, "bus-range", 0,
memmap[VIRT_PCIE_ECAM].size / PCIE_MMCFG_SIZE_MIN - 1);
qemu_fdt_setprop(fdt, name, "dma-coherent", NULL, 0);
qemu_fdt_setprop_cells(fdt, name, "reg", 0,
qemu_fdt_setprop(mc->fdt, name, "dma-coherent", NULL, 0);
qemu_fdt_setprop_cells(mc->fdt, name, "reg", 0,
memmap[VIRT_PCIE_ECAM].base, 0, memmap[VIRT_PCIE_ECAM].size);
qemu_fdt_setprop_sized_cells(fdt, name, "ranges",
qemu_fdt_setprop_sized_cells(mc->fdt, name, "ranges",
1, FDT_PCI_RANGE_IOPORT, 2, 0,
2, memmap[VIRT_PCIE_PIO].base, 2, memmap[VIRT_PCIE_PIO].size,
1, FDT_PCI_RANGE_MMIO,
@ -393,66 +522,96 @@ static void create_fdt(RISCVVirtState *s, const MemMapEntry *memmap,
2, virt_high_pcie_memmap.base,
2, virt_high_pcie_memmap.base, 2, virt_high_pcie_memmap.size);
create_pcie_irq_map(fdt, name, plic_pcie_phandle);
create_pcie_irq_map(mc->fdt, name, irq_pcie_phandle);
g_free(name);
}
test_phandle = phandle++;
static void create_fdt_reset(RISCVVirtState *s, const MemMapEntry *memmap,
uint32_t *phandle)
{
char *name;
uint32_t test_phandle;
MachineState *mc = MACHINE(s);
test_phandle = (*phandle)++;
name = g_strdup_printf("/soc/test@%lx",
(long)memmap[VIRT_TEST].base);
qemu_fdt_add_subnode(fdt, name);
qemu_fdt_add_subnode(mc->fdt, name);
{
static const char * const compat[3] = {
"sifive,test1", "sifive,test0", "syscon"
};
qemu_fdt_setprop_string_array(fdt, name, "compatible", (char **)&compat,
ARRAY_SIZE(compat));
qemu_fdt_setprop_string_array(mc->fdt, name, "compatible",
(char **)&compat, ARRAY_SIZE(compat));
}
qemu_fdt_setprop_cells(fdt, name, "reg",
0x0, memmap[VIRT_TEST].base,
0x0, memmap[VIRT_TEST].size);
qemu_fdt_setprop_cell(fdt, name, "phandle", test_phandle);
test_phandle = qemu_fdt_get_phandle(fdt, name);
qemu_fdt_setprop_cells(mc->fdt, name, "reg",
0x0, memmap[VIRT_TEST].base, 0x0, memmap[VIRT_TEST].size);
qemu_fdt_setprop_cell(mc->fdt, name, "phandle", test_phandle);
test_phandle = qemu_fdt_get_phandle(mc->fdt, name);
g_free(name);
name = g_strdup_printf("/soc/reboot");
qemu_fdt_add_subnode(fdt, name);
qemu_fdt_setprop_string(fdt, name, "compatible", "syscon-reboot");
qemu_fdt_setprop_cell(fdt, name, "regmap", test_phandle);
qemu_fdt_setprop_cell(fdt, name, "offset", 0x0);
qemu_fdt_setprop_cell(fdt, name, "value", FINISHER_RESET);
qemu_fdt_add_subnode(mc->fdt, name);
qemu_fdt_setprop_string(mc->fdt, name, "compatible", "syscon-reboot");
qemu_fdt_setprop_cell(mc->fdt, name, "regmap", test_phandle);
qemu_fdt_setprop_cell(mc->fdt, name, "offset", 0x0);
qemu_fdt_setprop_cell(mc->fdt, name, "value", FINISHER_RESET);
g_free(name);
name = g_strdup_printf("/soc/poweroff");
qemu_fdt_add_subnode(fdt, name);
qemu_fdt_setprop_string(fdt, name, "compatible", "syscon-poweroff");
qemu_fdt_setprop_cell(fdt, name, "regmap", test_phandle);
qemu_fdt_setprop_cell(fdt, name, "offset", 0x0);
qemu_fdt_setprop_cell(fdt, name, "value", FINISHER_PASS);
qemu_fdt_add_subnode(mc->fdt, name);
qemu_fdt_setprop_string(mc->fdt, name, "compatible", "syscon-poweroff");
qemu_fdt_setprop_cell(mc->fdt, name, "regmap", test_phandle);
qemu_fdt_setprop_cell(mc->fdt, name, "offset", 0x0);
qemu_fdt_setprop_cell(mc->fdt, name, "value", FINISHER_PASS);
g_free(name);
}
static void create_fdt_uart(RISCVVirtState *s, const MemMapEntry *memmap,
uint32_t irq_mmio_phandle)
{
char *name;
MachineState *mc = MACHINE(s);
name = g_strdup_printf("/soc/uart@%lx", (long)memmap[VIRT_UART0].base);
qemu_fdt_add_subnode(fdt, name);
qemu_fdt_setprop_string(fdt, name, "compatible", "ns16550a");
qemu_fdt_setprop_cells(fdt, name, "reg",
qemu_fdt_add_subnode(mc->fdt, name);
qemu_fdt_setprop_string(mc->fdt, name, "compatible", "ns16550a");
qemu_fdt_setprop_cells(mc->fdt, name, "reg",
0x0, memmap[VIRT_UART0].base,
0x0, memmap[VIRT_UART0].size);
qemu_fdt_setprop_cell(fdt, name, "clock-frequency", 3686400);
qemu_fdt_setprop_cell(fdt, name, "interrupt-parent", plic_mmio_phandle);
qemu_fdt_setprop_cell(fdt, name, "interrupts", UART0_IRQ);
qemu_fdt_setprop_cell(mc->fdt, name, "clock-frequency", 3686400);
qemu_fdt_setprop_cell(mc->fdt, name, "interrupt-parent", irq_mmio_phandle);
qemu_fdt_setprop_cell(mc->fdt, name, "interrupts", UART0_IRQ);
qemu_fdt_add_subnode(fdt, "/chosen");
qemu_fdt_setprop_string(fdt, "/chosen", "stdout-path", name);
qemu_fdt_add_subnode(mc->fdt, "/chosen");
qemu_fdt_setprop_string(mc->fdt, "/chosen", "stdout-path", name);
g_free(name);
}
static void create_fdt_rtc(RISCVVirtState *s, const MemMapEntry *memmap,
uint32_t irq_mmio_phandle)
{
char *name;
MachineState *mc = MACHINE(s);
name = g_strdup_printf("/soc/rtc@%lx", (long)memmap[VIRT_RTC].base);
qemu_fdt_add_subnode(fdt, name);
qemu_fdt_setprop_string(fdt, name, "compatible", "google,goldfish-rtc");
qemu_fdt_setprop_cells(fdt, name, "reg",
0x0, memmap[VIRT_RTC].base,
0x0, memmap[VIRT_RTC].size);
qemu_fdt_setprop_cell(fdt, name, "interrupt-parent", plic_mmio_phandle);
qemu_fdt_setprop_cell(fdt, name, "interrupts", RTC_IRQ);
qemu_fdt_add_subnode(mc->fdt, name);
qemu_fdt_setprop_string(mc->fdt, name, "compatible",
"google,goldfish-rtc");
qemu_fdt_setprop_cells(mc->fdt, name, "reg",
0x0, memmap[VIRT_RTC].base, 0x0, memmap[VIRT_RTC].size);
qemu_fdt_setprop_cell(mc->fdt, name, "interrupt-parent",
irq_mmio_phandle);
qemu_fdt_setprop_cell(mc->fdt, name, "interrupts", RTC_IRQ);
g_free(name);
}
static void create_fdt_flash(RISCVVirtState *s, const MemMapEntry *memmap)
{
char *name;
MachineState *mc = MACHINE(s);
hwaddr flashsize = virt_memmap[VIRT_FLASH].size / 2;
hwaddr flashbase = virt_memmap[VIRT_FLASH].base;
name = g_strdup_printf("/flash@%" PRIx64, flashbase);
qemu_fdt_add_subnode(mc->fdt, name);
@ -462,10 +621,59 @@ static void create_fdt(RISCVVirtState *s, const MemMapEntry *memmap,
2, flashbase + flashsize, 2, flashsize);
qemu_fdt_setprop_cell(mc->fdt, name, "bank-width", 4);
g_free(name);
}
static void create_fdt(RISCVVirtState *s, const MemMapEntry *memmap,
uint64_t mem_size, const char *cmdline, bool is_32_bit)
{
MachineState *mc = MACHINE(s);
uint32_t phandle = 1, irq_mmio_phandle = 1;
uint32_t irq_pcie_phandle = 1, irq_virtio_phandle = 1;
if (mc->dtb) {
mc->fdt = load_device_tree(mc->dtb, &s->fdt_size);
if (!mc->fdt) {
error_report("load_device_tree() failed");
exit(1);
}
goto update_bootargs;
} else {
mc->fdt = create_device_tree(&s->fdt_size);
if (!mc->fdt) {
error_report("create_device_tree() failed");
exit(1);
}
}
qemu_fdt_setprop_string(mc->fdt, "/", "model", "riscv-virtio,qemu");
qemu_fdt_setprop_string(mc->fdt, "/", "compatible", "riscv-virtio");
qemu_fdt_setprop_cell(mc->fdt, "/", "#size-cells", 0x2);
qemu_fdt_setprop_cell(mc->fdt, "/", "#address-cells", 0x2);
qemu_fdt_add_subnode(mc->fdt, "/soc");
qemu_fdt_setprop(mc->fdt, "/soc", "ranges", NULL, 0);
qemu_fdt_setprop_string(mc->fdt, "/soc", "compatible", "simple-bus");
qemu_fdt_setprop_cell(mc->fdt, "/soc", "#size-cells", 0x2);
qemu_fdt_setprop_cell(mc->fdt, "/soc", "#address-cells", 0x2);
create_fdt_sockets(s, memmap, is_32_bit, &phandle,
&irq_mmio_phandle, &irq_pcie_phandle, &irq_virtio_phandle);
create_fdt_virtio(s, memmap, irq_virtio_phandle);
create_fdt_pcie(s, memmap, irq_pcie_phandle);
create_fdt_reset(s, memmap, &phandle);
create_fdt_uart(s, memmap, irq_mmio_phandle);
create_fdt_rtc(s, memmap, irq_mmio_phandle);
create_fdt_flash(s, memmap);
update_bootargs:
if (cmdline) {
qemu_fdt_setprop_string(fdt, "/chosen", "bootargs", cmdline);
qemu_fdt_setprop_string(mc->fdt, "/chosen", "bootargs", cmdline);
}
}
@ -613,11 +821,23 @@ static void virt_machine_init(MachineState *machine)
sysbus_realize(SYS_BUS_DEVICE(&s->soc[i]), &error_abort);
/* Per-socket CLINT */
sifive_clint_create(
riscv_aclint_swi_create(
memmap[VIRT_CLINT].base + i * memmap[VIRT_CLINT].size,
memmap[VIRT_CLINT].size, base_hartid, hart_count,
SIFIVE_SIP_BASE, SIFIVE_TIMECMP_BASE, SIFIVE_TIME_BASE,
SIFIVE_CLINT_TIMEBASE_FREQ, true);
base_hartid, hart_count, false);
riscv_aclint_mtimer_create(
memmap[VIRT_CLINT].base + i * memmap[VIRT_CLINT].size +
RISCV_ACLINT_SWI_SIZE,
RISCV_ACLINT_DEFAULT_MTIMER_SIZE, base_hartid, hart_count,
RISCV_ACLINT_DEFAULT_MTIMECMP, RISCV_ACLINT_DEFAULT_MTIME,
RISCV_ACLINT_DEFAULT_TIMEBASE_FREQ, true);
/* Per-socket ACLINT SSWI */
if (s->have_aclint) {
riscv_aclint_swi_create(
memmap[VIRT_ACLINT_SSWI].base +
i * memmap[VIRT_ACLINT_SSWI].size,
base_hartid, hart_count, true);
}
/* Per-socket PLIC hart topology configuration string */
plic_hart_config = plic_hart_config_string(hart_count);
@ -625,7 +845,7 @@ static void virt_machine_init(MachineState *machine)
/* Per-socket PLIC */
s->plic[i] = sifive_plic_create(
memmap[VIRT_PLIC].base + i * memmap[VIRT_PLIC].size,
plic_hart_config, base_hartid,
plic_hart_config, hart_count, base_hartid,
VIRT_PLIC_NUM_SOURCES,
VIRT_PLIC_NUM_PRIORITIES,
VIRT_PLIC_PRIORITY_BASE,
@ -783,6 +1003,22 @@ static void virt_machine_instance_init(Object *obj)
{
}
static bool virt_get_aclint(Object *obj, Error **errp)
{
MachineState *ms = MACHINE(obj);
RISCVVirtState *s = RISCV_VIRT_MACHINE(ms);
return s->have_aclint;
}
static void virt_set_aclint(Object *obj, bool value, Error **errp)
{
MachineState *ms = MACHINE(obj);
RISCVVirtState *s = RISCV_VIRT_MACHINE(ms);
s->have_aclint = value;
}
static void virt_machine_class_init(ObjectClass *oc, void *data)
{
MachineClass *mc = MACHINE_CLASS(oc);
@ -798,6 +1034,12 @@ static void virt_machine_class_init(ObjectClass *oc, void *data)
mc->numa_mem_supported = true;
machine_class_allow_dynamic_sysbus_dev(mc, TYPE_RAMFB_DEVICE);
object_class_property_add_bool(oc, "aclint", virt_get_aclint,
virt_set_aclint);
object_class_property_set_description(oc, "aclint",
"Set on/off to enable/disable "
"emulating ACLINT devices");
}
static const TypeInfo virt_machine_typeinfo = {

View File

@ -25,6 +25,9 @@ config ALLWINNER_A10_PIT
bool
select PTIMER
config SIFIVE_PWM
bool
config STM32F2XX_TIMER
bool

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);
}

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'))

468
hw/timer/sifive_pwm.c Normal file
View File

@ -0,0 +1,468 @@
/*
* SiFive PWM
*
* Copyright (c) 2020 Western Digital
*
* Author: Alistair Francis <alistair.francis@wdc.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "qemu/osdep.h"
#include "trace.h"
#include "hw/irq.h"
#include "hw/timer/sifive_pwm.h"
#include "hw/qdev-properties.h"
#include "hw/registerfields.h"
#include "migration/vmstate.h"
#include "qemu/log.h"
#include "qemu/module.h"
#define HAS_PWM_EN_BITS(cfg) ((cfg & R_CONFIG_ENONESHOT_MASK) || \
(cfg & R_CONFIG_ENALWAYS_MASK))
#define PWMCMP_MASK 0xFFFF
#define PWMCOUNT_MASK 0x7FFFFFFF
REG32(CONFIG, 0x00)
FIELD(CONFIG, SCALE, 0, 4)
FIELD(CONFIG, STICKY, 8, 1)
FIELD(CONFIG, ZEROCMP, 9, 1)
FIELD(CONFIG, DEGLITCH, 10, 1)
FIELD(CONFIG, ENALWAYS, 12, 1)
FIELD(CONFIG, ENONESHOT, 13, 1)
FIELD(CONFIG, CMP0CENTER, 16, 1)
FIELD(CONFIG, CMP1CENTER, 17, 1)
FIELD(CONFIG, CMP2CENTER, 18, 1)
FIELD(CONFIG, CMP3CENTER, 19, 1)
FIELD(CONFIG, CMP0GANG, 24, 1)
FIELD(CONFIG, CMP1GANG, 25, 1)
FIELD(CONFIG, CMP2GANG, 26, 1)
FIELD(CONFIG, CMP3GANG, 27, 1)
FIELD(CONFIG, CMP0IP, 28, 1)
FIELD(CONFIG, CMP1IP, 29, 1)
FIELD(CONFIG, CMP2IP, 30, 1)
FIELD(CONFIG, CMP3IP, 31, 1)
REG32(COUNT, 0x08)
REG32(PWMS, 0x10)
REG32(PWMCMP0, 0x20)
REG32(PWMCMP1, 0x24)
REG32(PWMCMP2, 0x28)
REG32(PWMCMP3, 0x2C)
static inline uint64_t sifive_pwm_ns_to_ticks(SiFivePwmState *s,
uint64_t time)
{
return muldiv64(time, s->freq_hz, NANOSECONDS_PER_SECOND);
}
static inline uint64_t sifive_pwm_ticks_to_ns(SiFivePwmState *s,
uint64_t ticks)
{
return muldiv64(ticks, NANOSECONDS_PER_SECOND, s->freq_hz);
}
static inline uint64_t sifive_pwm_compute_scale(SiFivePwmState *s)
{
return s->pwmcfg & R_CONFIG_SCALE_MASK;
}
static void sifive_pwm_set_alarms(SiFivePwmState *s)
{
uint64_t now_ns = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
if (HAS_PWM_EN_BITS(s->pwmcfg)) {
/*
* Subtract ticks from number of ticks when the timer was zero
* and mask to the register width.
*/
uint64_t pwmcount = (sifive_pwm_ns_to_ticks(s, now_ns) -
s->tick_offset) & PWMCOUNT_MASK;
uint64_t scale = sifive_pwm_compute_scale(s);
/* PWMs only contains PWMCMP_MASK bits starting at scale */
uint64_t pwms = (pwmcount & (PWMCMP_MASK << scale)) >> scale;
for (int i = 0; i < SIFIVE_PWM_CHANS; i++) {
uint64_t pwmcmp = s->pwmcmp[i] & PWMCMP_MASK;
uint64_t pwmcmp_ticks = pwmcmp << scale;
/*
* Per circuit diagram and spec, both cases raises corresponding
* IP bit one clock cycle after time expires.
*/
if (pwmcmp > pwms) {
uint64_t offset = pwmcmp_ticks - pwmcount + 1;
uint64_t when_to_fire = now_ns +
sifive_pwm_ticks_to_ns(s, offset);
trace_sifive_pwm_set_alarm(when_to_fire, now_ns);
timer_mod(&s->timer[i], when_to_fire);
} else {
/* Schedule interrupt for next cycle */
trace_sifive_pwm_set_alarm(now_ns + 1, now_ns);
timer_mod(&s->timer[i], now_ns + 1);
}
}
} else {
/*
* If timer incrementing disabled, just do pwms > pwmcmp check since
* a write may have happened to PWMs.
*/
uint64_t pwmcount = (s->tick_offset) & PWMCOUNT_MASK;
uint64_t scale = sifive_pwm_compute_scale(s);
uint64_t pwms = (pwmcount & (PWMCMP_MASK << scale)) >> scale;
for (int i = 0; i < SIFIVE_PWM_CHANS; i++) {
uint64_t pwmcmp = s->pwmcmp[i] & PWMCMP_MASK;
if (pwms >= pwmcmp) {
trace_sifive_pwm_set_alarm(now_ns + 1, now_ns);
timer_mod(&s->timer[i], now_ns + 1);
} else {
/* Effectively disable timer by scheduling far in future. */
trace_sifive_pwm_set_alarm(0xFFFFFFFFFFFFFF, now_ns);
timer_mod(&s->timer[i], 0xFFFFFFFFFFFFFF);
}
}
}
}
static void sifive_pwm_interrupt(SiFivePwmState *s, int num)
{
uint64_t now = sifive_pwm_ns_to_ticks(s,
qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL));
bool was_incrementing = HAS_PWM_EN_BITS(s->pwmcfg);
trace_sifive_pwm_interrupt(num);
s->pwmcfg |= R_CONFIG_CMP0IP_MASK << num;
qemu_irq_raise(s->irqs[num]);
/*
* If the zerocmp is set and pwmcmp0 raised the interrupt
* reset the zero ticks.
*/
if ((s->pwmcfg & R_CONFIG_ZEROCMP_MASK) && (num == 0)) {
/* If reset signal conditions, disable ENONESHOT. */
s->pwmcfg &= ~R_CONFIG_ENONESHOT_MASK;
if (was_incrementing) {
/* If incrementing, time in ticks is when pwmcount is zero */
s->tick_offset = now;
} else {
/* If not incrementing, pwmcount = 0 */
s->tick_offset = 0;
}
}
/*
* If carryout bit set, which we discern via looking for overflow,
* also reset ENONESHOT.
*/
if (was_incrementing &&
((now & PWMCOUNT_MASK) < (s->tick_offset & PWMCOUNT_MASK))) {
s->pwmcfg &= ~R_CONFIG_ENONESHOT_MASK;
}
/* Schedule or disable interrupts */
sifive_pwm_set_alarms(s);
/* If was enabled, and now not enabled, switch tick rep */
if (was_incrementing && !HAS_PWM_EN_BITS(s->pwmcfg)) {
s->tick_offset = (now - s->tick_offset) & PWMCOUNT_MASK;
}
}
static void sifive_pwm_interrupt_0(void *opaque)
{
SiFivePwmState *s = opaque;
sifive_pwm_interrupt(s, 0);
}
static void sifive_pwm_interrupt_1(void *opaque)
{
SiFivePwmState *s = opaque;
sifive_pwm_interrupt(s, 1);
}
static void sifive_pwm_interrupt_2(void *opaque)
{
SiFivePwmState *s = opaque;
sifive_pwm_interrupt(s, 2);
}
static void sifive_pwm_interrupt_3(void *opaque)
{
SiFivePwmState *s = opaque;
sifive_pwm_interrupt(s, 3);
}
static uint64_t sifive_pwm_read(void *opaque, hwaddr addr,
unsigned int size)
{
SiFivePwmState *s = opaque;
uint64_t cur_time, scale;
uint64_t now = sifive_pwm_ns_to_ticks(s,
qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL));
trace_sifive_pwm_read(addr);
switch (addr) {
case A_CONFIG:
return s->pwmcfg;
case A_COUNT:
cur_time = s->tick_offset;
if (HAS_PWM_EN_BITS(s->pwmcfg)) {
cur_time = now - cur_time;
}
/*
* Return the value in the counter with bit 31 always 0
* This is allowed to wrap around so we don't need to check that.
*/
return cur_time & PWMCOUNT_MASK;
case A_PWMS:
cur_time = s->tick_offset;
scale = sifive_pwm_compute_scale(s);
if (HAS_PWM_EN_BITS(s->pwmcfg)) {
cur_time = now - cur_time;
}
return ((cur_time & PWMCOUNT_MASK) >> scale) & PWMCMP_MASK;
case A_PWMCMP0:
return s->pwmcmp[0] & PWMCMP_MASK;
case A_PWMCMP1:
return s->pwmcmp[1] & PWMCMP_MASK;
case A_PWMCMP2:
return s->pwmcmp[2] & PWMCMP_MASK;
case A_PWMCMP3:
return s->pwmcmp[3] & PWMCMP_MASK;
default:
qemu_log_mask(LOG_GUEST_ERROR,
"%s: Bad offset 0x%"HWADDR_PRIx"\n", __func__, addr);
return 0;
}
return 0;
}
static void sifive_pwm_write(void *opaque, hwaddr addr,
uint64_t val64, unsigned int size)
{
SiFivePwmState *s = opaque;
uint32_t value = val64;
uint64_t new_offset, scale;
uint64_t now = sifive_pwm_ns_to_ticks(s,
qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL));
trace_sifive_pwm_write(value, addr);
switch (addr) {
case A_CONFIG:
if (value & (R_CONFIG_CMP0CENTER_MASK | R_CONFIG_CMP1CENTER_MASK |
R_CONFIG_CMP2CENTER_MASK | R_CONFIG_CMP3CENTER_MASK)) {
qemu_log_mask(LOG_UNIMP, "%s: CMPxCENTER is not supported\n",
__func__);
}
if (value & (R_CONFIG_CMP0GANG_MASK | R_CONFIG_CMP1GANG_MASK |
R_CONFIG_CMP2GANG_MASK | R_CONFIG_CMP3GANG_MASK)) {
qemu_log_mask(LOG_UNIMP, "%s: CMPxGANG is not supported\n",
__func__);
}
if (value & (R_CONFIG_CMP0IP_MASK | R_CONFIG_CMP1IP_MASK |
R_CONFIG_CMP2IP_MASK | R_CONFIG_CMP3IP_MASK)) {
qemu_log_mask(LOG_UNIMP, "%s: CMPxIP is not supported\n",
__func__);
}
if (!(value & R_CONFIG_CMP0IP_MASK)) {
qemu_irq_lower(s->irqs[0]);
}
if (!(value & R_CONFIG_CMP1IP_MASK)) {
qemu_irq_lower(s->irqs[1]);
}
if (!(value & R_CONFIG_CMP2IP_MASK)) {
qemu_irq_lower(s->irqs[2]);
}
if (!(value & R_CONFIG_CMP3IP_MASK)) {
qemu_irq_lower(s->irqs[3]);
}
/*
* If this write enables the timer increment
* set the time when pwmcount was zero to be cur_time - pwmcount.
* If this write disables the timer increment
* convert back from pwmcount to the time in ticks
* when pwmcount was zero.
*/
if ((!HAS_PWM_EN_BITS(s->pwmcfg) && HAS_PWM_EN_BITS(value)) ||
(HAS_PWM_EN_BITS(s->pwmcfg) && !HAS_PWM_EN_BITS(value))) {
s->tick_offset = (now - s->tick_offset) & PWMCOUNT_MASK;
}
s->pwmcfg = value;
break;
case A_COUNT:
/* The guest changed the counter, updated the offset value. */
new_offset = value;
if (HAS_PWM_EN_BITS(s->pwmcfg)) {
new_offset = now - new_offset;
}
s->tick_offset = new_offset;
break;
case A_PWMS:
scale = sifive_pwm_compute_scale(s);
new_offset = (((value & PWMCMP_MASK) << scale) & PWMCOUNT_MASK);
if (HAS_PWM_EN_BITS(s->pwmcfg)) {
new_offset = now - new_offset;
}
s->tick_offset = new_offset;
break;
case A_PWMCMP0:
s->pwmcmp[0] = value & PWMCMP_MASK;
break;
case A_PWMCMP1:
s->pwmcmp[1] = value & PWMCMP_MASK;
break;
case A_PWMCMP2:
s->pwmcmp[2] = value & PWMCMP_MASK;
break;
case A_PWMCMP3:
s->pwmcmp[3] = value & PWMCMP_MASK;
break;
default:
qemu_log_mask(LOG_GUEST_ERROR,
"%s: Bad offset 0x%"HWADDR_PRIx"\n", __func__, addr);
}
/* Update the alarms to reflect possible updated values */
sifive_pwm_set_alarms(s);
}
static void sifive_pwm_reset(DeviceState *dev)
{
SiFivePwmState *s = SIFIVE_PWM(dev);
uint64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
s->pwmcfg = 0x00000000;
s->pwmcmp[0] = 0x00000000;
s->pwmcmp[1] = 0x00000000;
s->pwmcmp[2] = 0x00000000;
s->pwmcmp[3] = 0x00000000;
s->tick_offset = sifive_pwm_ns_to_ticks(s, now);
}
static const MemoryRegionOps sifive_pwm_ops = {
.read = sifive_pwm_read,
.write = sifive_pwm_write,
.endianness = DEVICE_NATIVE_ENDIAN,
};
static const VMStateDescription vmstate_sifive_pwm = {
.name = TYPE_SIFIVE_PWM,
.version_id = 1,
.minimum_version_id = 1,
.fields = (VMStateField[]) {
VMSTATE_TIMER_ARRAY(timer, SiFivePwmState, 4),
VMSTATE_UINT64(tick_offset, SiFivePwmState),
VMSTATE_UINT32(pwmcfg, SiFivePwmState),
VMSTATE_UINT32_ARRAY(pwmcmp, SiFivePwmState, 4),
VMSTATE_END_OF_LIST()
}
};
static Property sifive_pwm_properties[] = {
/* 0.5Ghz per spec after FSBL */
DEFINE_PROP_UINT64("clock-frequency", struct SiFivePwmState,
freq_hz, 500000000ULL),
DEFINE_PROP_END_OF_LIST(),
};
static void sifive_pwm_init(Object *obj)
{
SiFivePwmState *s = SIFIVE_PWM(obj);
int i;
for (i = 0; i < SIFIVE_PWM_IRQS; i++) {
sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->irqs[i]);
}
memory_region_init_io(&s->mmio, obj, &sifive_pwm_ops, s,
TYPE_SIFIVE_PWM, 0x100);
sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmio);
}
static void sifive_pwm_realize(DeviceState *dev, Error **errp)
{
SiFivePwmState *s = SIFIVE_PWM(dev);
timer_init_ns(&s->timer[0], QEMU_CLOCK_VIRTUAL,
sifive_pwm_interrupt_0, s);
timer_init_ns(&s->timer[1], QEMU_CLOCK_VIRTUAL,
sifive_pwm_interrupt_1, s);
timer_init_ns(&s->timer[2], QEMU_CLOCK_VIRTUAL,
sifive_pwm_interrupt_2, s);
timer_init_ns(&s->timer[3], QEMU_CLOCK_VIRTUAL,
sifive_pwm_interrupt_3, s);
}
static void sifive_pwm_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
dc->reset = sifive_pwm_reset;
device_class_set_props(dc, sifive_pwm_properties);
dc->vmsd = &vmstate_sifive_pwm;
dc->realize = sifive_pwm_realize;
}
static const TypeInfo sifive_pwm_info = {
.name = TYPE_SIFIVE_PWM,
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(SiFivePwmState),
.instance_init = sifive_pwm_init,
.class_init = sifive_pwm_class_init,
};
static void sifive_pwm_register_types(void)
{
type_register_static(&sifive_pwm_info);
}
type_init(sifive_pwm_register_types)

View File

@ -88,3 +88,9 @@ sse_counter_reset(void) "SSE system counter: reset"
sse_timer_read(uint64_t offset, uint64_t data, unsigned size) "SSE system timer read: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u"
sse_timer_write(uint64_t offset, uint64_t data, unsigned size) "SSE system timer write: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u"
sse_timer_reset(void) "SSE system timer: reset"
# sifive_pwm.c
sifive_pwm_set_alarm(uint64_t alarm, uint64_t now) "Setting alarm to: 0x%" PRIx64 ", now: 0x%" PRIx64
sifive_pwm_interrupt(int num) "Interrupt %d"
sifive_pwm_read(uint64_t offset) "Read at address: 0x%" PRIx64
sifive_pwm_write(uint64_t data, uint64_t offset) "Write 0x%" PRIx64 " at address: 0x%" PRIx64

View File

@ -60,6 +60,8 @@ struct IbexPlicState {
uint32_t threshold_base;
uint32_t claim_base;
qemu_irq *external_irqs;
};
#endif /* HW_IBEX_PLIC_H */

View File

@ -0,0 +1,80 @@
/*
* RISC-V ACLINT (Advanced Core Local Interruptor) interface
*
* 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 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/>.
*/
#ifndef HW_RISCV_ACLINT_H
#define HW_RISCV_ACLINT_H
#include "hw/sysbus.h"
#define TYPE_RISCV_ACLINT_MTIMER "riscv.aclint.mtimer"
#define RISCV_ACLINT_MTIMER(obj) \
OBJECT_CHECK(RISCVAclintMTimerState, (obj), TYPE_RISCV_ACLINT_MTIMER)
typedef struct RISCVAclintMTimerState {
/*< private >*/
SysBusDevice parent_obj;
/*< public >*/
MemoryRegion mmio;
uint32_t hartid_base;
uint32_t num_harts;
uint32_t timecmp_base;
uint32_t time_base;
uint32_t aperture_size;
uint32_t timebase_freq;
qemu_irq *timer_irqs;
} RISCVAclintMTimerState;
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);
#define TYPE_RISCV_ACLINT_SWI "riscv.aclint.swi"
#define RISCV_ACLINT_SWI(obj) \
OBJECT_CHECK(RISCVAclintSwiState, (obj), TYPE_RISCV_ACLINT_SWI)
typedef struct RISCVAclintSwiState {
/*< private >*/
SysBusDevice parent_obj;
/*< public >*/
MemoryRegion mmio;
uint32_t hartid_base;
uint32_t num_harts;
uint32_t sswi;
qemu_irq *soft_irqs;
} RISCVAclintSwiState;
DeviceState *riscv_aclint_swi_create(hwaddr addr, uint32_t hartid_base,
uint32_t num_harts, bool sswi);
enum {
RISCV_ACLINT_DEFAULT_MTIMECMP = 0x0,
RISCV_ACLINT_DEFAULT_MTIME = 0x7ff8,
RISCV_ACLINT_DEFAULT_MTIMER_SIZE = 0x8000,
RISCV_ACLINT_DEFAULT_TIMEBASE_FREQ = 10000000,
RISCV_ACLINT_MAX_HARTS = 4095,
RISCV_ACLINT_SWI_SIZE = 0x4000
};
#endif

View File

@ -1,60 +0,0 @@
/*
* SiFive CLINT (Core Local Interruptor) interface
*
* Copyright (c) 2016-2017 Sagar Karandikar, sagark@eecs.berkeley.edu
* Copyright (c) 2017 SiFive, Inc.
*
* 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/>.
*/
#ifndef HW_SIFIVE_CLINT_H
#define HW_SIFIVE_CLINT_H
#include "hw/sysbus.h"
#define TYPE_SIFIVE_CLINT "riscv.sifive.clint"
#define SIFIVE_CLINT(obj) \
OBJECT_CHECK(SiFiveCLINTState, (obj), TYPE_SIFIVE_CLINT)
typedef struct SiFiveCLINTState {
/*< private >*/
SysBusDevice parent_obj;
/*< public >*/
MemoryRegion mmio;
uint32_t hartid_base;
uint32_t num_harts;
uint32_t sip_base;
uint32_t timecmp_base;
uint32_t time_base;
uint32_t aperture_size;
uint32_t timebase_freq;
} SiFiveCLINTState;
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);
enum {
SIFIVE_SIP_BASE = 0x0,
SIFIVE_TIMECMP_BASE = 0x4000,
SIFIVE_TIME_BASE = 0xBFF8
};
enum {
SIFIVE_CLINT_TIMEBASE_FREQ = 10000000
};
#endif

View File

@ -72,9 +72,13 @@ struct SiFivePLICState {
uint32_t context_base;
uint32_t context_stride;
uint32_t aperture_size;
qemu_irq *m_external_irqs;
qemu_irq *s_external_irqs;
};
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,

View File

@ -27,6 +27,7 @@
#include "hw/misc/sifive_u_otp.h"
#include "hw/misc/sifive_u_prci.h"
#include "hw/ssi/sifive_spi.h"
#include "hw/timer/sifive_pwm.h"
#define TYPE_RISCV_U_SOC "riscv.sifive.u.soc"
#define RISCV_U_SOC(obj) \
@ -49,6 +50,7 @@ typedef struct SiFiveUSoCState {
SiFiveSPIState spi0;
SiFiveSPIState spi2;
CadenceGEMState gem;
SiFivePwmState pwm[2];
uint32_t serial;
char *cpu_type;
@ -92,7 +94,9 @@ enum {
SIFIVE_U_DEV_FLASH0,
SIFIVE_U_DEV_DRAM,
SIFIVE_U_DEV_GEM,
SIFIVE_U_DEV_GEM_MGMT
SIFIVE_U_DEV_GEM_MGMT,
SIFIVE_U_DEV_PWM0,
SIFIVE_U_DEV_PWM1
};
enum {
@ -126,6 +130,14 @@ enum {
SIFIVE_U_PDMA_IRQ5 = 28,
SIFIVE_U_PDMA_IRQ6 = 29,
SIFIVE_U_PDMA_IRQ7 = 30,
SIFIVE_U_PWM0_IRQ0 = 42,
SIFIVE_U_PWM0_IRQ1 = 43,
SIFIVE_U_PWM0_IRQ2 = 44,
SIFIVE_U_PWM0_IRQ3 = 45,
SIFIVE_U_PWM1_IRQ0 = 46,
SIFIVE_U_PWM1_IRQ1 = 47,
SIFIVE_U_PWM1_IRQ2 = 48,
SIFIVE_U_PWM1_IRQ3 = 49,
SIFIVE_U_QSPI0_IRQ = 51,
SIFIVE_U_GEM_IRQ = 53
};

View File

@ -43,6 +43,7 @@ struct RISCVVirtState {
FWCfgState *fw_cfg;
int fdt_size;
bool have_aclint;
};
enum {
@ -51,6 +52,7 @@ enum {
VIRT_TEST,
VIRT_RTC,
VIRT_CLINT,
VIRT_ACLINT_SSWI,
VIRT_PLIC,
VIRT_UART0,
VIRT_VIRTIO,

View File

@ -48,5 +48,7 @@ struct IbexTimerState {
uint32_t timebase_freq;
qemu_irq irq;
qemu_irq m_timer_irq;
};
#endif /* HW_IBEX_TIMER_H */

View File

@ -0,0 +1,62 @@
/*
* SiFive PWM
*
* Copyright (c) 2020 Western Digital
*
* Author: Alistair Francis <alistair.francis@wdc.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#ifndef HW_SIFIVE_PWM_H
#define HW_SIFIVE_PWM_H
#include "hw/sysbus.h"
#include "qemu/timer.h"
#include "qom/object.h"
#define TYPE_SIFIVE_PWM "sifive-pwm"
#define SIFIVE_PWM(obj) \
OBJECT_CHECK(SiFivePwmState, (obj), TYPE_SIFIVE_PWM)
#define SIFIVE_PWM_CHANS 4
#define SIFIVE_PWM_IRQS SIFIVE_PWM_CHANS
typedef struct SiFivePwmState {
/* <private> */
SysBusDevice parent_obj;
/* <public> */
MemoryRegion mmio;
QEMUTimer timer[SIFIVE_PWM_CHANS];
/*
* if en bit(s) set, is the number of ticks when pwmcount was 0
* if en bit(s) not set, is the number of ticks in pwmcount
*/
uint64_t tick_offset;
uint64_t freq_hz;
uint32_t pwmcfg;
uint32_t pwmcmp[SIFIVE_PWM_CHANS];
qemu_irq irqs[SIFIVE_PWM_IRQS];
} SiFivePwmState;
#endif /* HW_SIFIVE_PWM_H */

View File

@ -567,11 +567,41 @@ static void riscv_cpu_realize(DeviceState *dev, Error **errp)
mcc->parent_realize(dev, errp);
}
#ifndef CONFIG_USER_ONLY
static void riscv_cpu_set_irq(void *opaque, int irq, int level)
{
RISCVCPU *cpu = RISCV_CPU(opaque);
switch (irq) {
case IRQ_U_SOFT:
case IRQ_S_SOFT:
case IRQ_VS_SOFT:
case IRQ_M_SOFT:
case IRQ_U_TIMER:
case IRQ_S_TIMER:
case IRQ_VS_TIMER:
case IRQ_M_TIMER:
case IRQ_U_EXT:
case IRQ_S_EXT:
case IRQ_VS_EXT:
case IRQ_M_EXT:
riscv_cpu_update_mip(cpu, 1 << irq, BOOL_TO_MASK(level));
break;
default:
g_assert_not_reached();
}
}
#endif /* CONFIG_USER_ONLY */
static void riscv_cpu_init(Object *obj)
{
RISCVCPU *cpu = RISCV_CPU(obj);
cpu_set_cpustate_pointers(cpu);
#ifndef CONFIG_USER_ONLY
qdev_init_gpio_in(DEVICE(cpu), riscv_cpu_set_irq, 12);
#endif /* CONFIG_USER_ONLY */
}
static Property riscv_cpu_properties[] = {
@ -599,6 +629,7 @@ static Property riscv_cpu_properties[] = {
DEFINE_PROP_UINT16("elen", RISCVCPU, cfg.elen, 64),
DEFINE_PROP_BOOL("mmu", RISCVCPU, cfg.mmu, true),
DEFINE_PROP_BOOL("pmp", RISCVCPU, cfg.pmp, true),
/* ePMP 0.9.3 */
DEFINE_PROP_BOOL("x-epmp", RISCVCPU, cfg.epmp, false),
DEFINE_PROP_UINT64("resetvec", RISCVCPU, cfg.resetvec, DEFAULT_RSTVEC),

View File

@ -210,8 +210,8 @@
#define CSR_MTVAL2 0x34b
/* Enhanced Physical Memory Protection (ePMP) */
#define CSR_MSECCFG 0x390
#define CSR_MSECCFGH 0x391
#define CSR_MSECCFG 0x747
#define CSR_MSECCFGH 0x757
/* Physical Memory Protection */
#define CSR_PMPCFG0 0x3a0
#define CSR_PMPCFG1 0x3a1
@ -397,10 +397,10 @@
#define HSTATUS32_WPRI 0xFF8FF87E
#define HSTATUS64_WPRI 0xFFFFFFFFFF8FF87EULL
#define HCOUNTEREN_CY (1 << 0)
#define HCOUNTEREN_TM (1 << 1)
#define HCOUNTEREN_IR (1 << 2)
#define HCOUNTEREN_HPM3 (1 << 3)
#define COUNTEREN_CY (1 << 0)
#define COUNTEREN_TM (1 << 1)
#define COUNTEREN_IR (1 << 2)
#define COUNTEREN_HPM3 (1 << 3)
/* Privilege modes */
#define PRV_U 0

View File

@ -106,9 +106,10 @@ bool riscv_cpu_fp_enabled(CPURISCVState *env)
void riscv_cpu_swap_hypervisor_regs(CPURISCVState *env)
{
uint64_t sd = riscv_cpu_is_32bit(env) ? MSTATUS32_SD : MSTATUS64_SD;
uint64_t mstatus_mask = MSTATUS_MXR | MSTATUS_SUM | MSTATUS_FS |
MSTATUS_SPP | MSTATUS_SPIE | MSTATUS_SIE |
MSTATUS64_UXL;
MSTATUS64_UXL | sd;
bool current_virt = riscv_cpu_virt_enabled(env);
g_assert(riscv_has_ext(env, RVH));

View File

@ -71,20 +71,20 @@ static RISCVException ctr(CPURISCVState *env, int csrno)
if (riscv_cpu_virt_enabled(env)) {
switch (csrno) {
case CSR_CYCLE:
if (!get_field(env->hcounteren, HCOUNTEREN_CY) &&
get_field(env->mcounteren, HCOUNTEREN_CY)) {
if (!get_field(env->hcounteren, COUNTEREN_CY) &&
get_field(env->mcounteren, COUNTEREN_CY)) {
return RISCV_EXCP_VIRT_INSTRUCTION_FAULT;
}
break;
case CSR_TIME:
if (!get_field(env->hcounteren, HCOUNTEREN_TM) &&
get_field(env->mcounteren, HCOUNTEREN_TM)) {
if (!get_field(env->hcounteren, COUNTEREN_TM) &&
get_field(env->mcounteren, COUNTEREN_TM)) {
return RISCV_EXCP_VIRT_INSTRUCTION_FAULT;
}
break;
case CSR_INSTRET:
if (!get_field(env->hcounteren, HCOUNTEREN_IR) &&
get_field(env->mcounteren, HCOUNTEREN_IR)) {
if (!get_field(env->hcounteren, COUNTEREN_IR) &&
get_field(env->mcounteren, COUNTEREN_IR)) {
return RISCV_EXCP_VIRT_INSTRUCTION_FAULT;
}
break;
@ -98,20 +98,20 @@ static RISCVException ctr(CPURISCVState *env, int csrno)
if (riscv_cpu_is_32bit(env)) {
switch (csrno) {
case CSR_CYCLEH:
if (!get_field(env->hcounteren, HCOUNTEREN_CY) &&
get_field(env->mcounteren, HCOUNTEREN_CY)) {
if (!get_field(env->hcounteren, COUNTEREN_CY) &&
get_field(env->mcounteren, COUNTEREN_CY)) {
return RISCV_EXCP_VIRT_INSTRUCTION_FAULT;
}
break;
case CSR_TIMEH:
if (!get_field(env->hcounteren, HCOUNTEREN_TM) &&
get_field(env->mcounteren, HCOUNTEREN_TM)) {
if (!get_field(env->hcounteren, COUNTEREN_TM) &&
get_field(env->mcounteren, COUNTEREN_TM)) {
return RISCV_EXCP_VIRT_INSTRUCTION_FAULT;
}
break;
case CSR_INSTRETH:
if (!get_field(env->hcounteren, HCOUNTEREN_IR) &&
get_field(env->mcounteren, HCOUNTEREN_IR)) {
if (!get_field(env->hcounteren, COUNTEREN_IR) &&
get_field(env->mcounteren, COUNTEREN_IR)) {
return RISCV_EXCP_VIRT_INSTRUCTION_FAULT;
}
break;
@ -986,7 +986,7 @@ static RISCVException read_satp(CPURISCVState *env, int csrno,
static RISCVException write_satp(CPURISCVState *env, int csrno,
target_ulong val)
{
int vm, mask, asid;
target_ulong vm, mask, asid;
if (!riscv_feature(env, RISCV_FEATURE_MMU)) {
return RISCV_EXCP_NONE;