mirror of https://github.com/xemu-project/xemu.git
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:
commit
2c3e83f92d
|
@ -24,6 +24,7 @@ The ``sifive_u`` machine supports the following devices:
|
|||
* 2 QSPI controllers
|
||||
* 1 ISSI 25WP256 flash
|
||||
* 1 SD card in SPI mode
|
||||
* PWM0 and PWM1
|
||||
|
||||
Please note the real world HiFive Unleashed board has a fixed configuration of
|
||||
1 E51 core and 4 U54 core combination and the RISC-V core boots in 64-bit mode.
|
||||
|
@ -209,15 +210,16 @@ command line options with ``qemu-system-riscv32``.
|
|||
Running U-Boot
|
||||
--------------
|
||||
|
||||
U-Boot mainline v2021.01 release is tested at the time of writing. To build a
|
||||
U-Boot mainline v2021.07 release is tested at the time of writing. To build a
|
||||
U-Boot mainline bootloader that can be booted by the ``sifive_u`` machine, use
|
||||
the sifive_fu540_defconfig with similar commands as described above for Linux:
|
||||
the sifive_unleashed_defconfig with similar commands as described above for
|
||||
Linux:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ export CROSS_COMPILE=riscv64-linux-
|
||||
$ export OPENSBI=/path/to/opensbi-riscv64-generic-fw_dynamic.bin
|
||||
$ make sifive_fu540_defconfig
|
||||
$ make sifive_unleashed_defconfig
|
||||
|
||||
You will get spl/u-boot-spl.bin and u-boot.itb file in the build tree.
|
||||
|
||||
|
@ -312,31 +314,29 @@ board on QEMU ``sifive_u`` machine out of the box. This allows users to
|
|||
develop and test the recommended RISC-V boot flow with a real world use
|
||||
case: ZSBL (in QEMU) loads U-Boot SPL from SD card or SPI flash to L2LIM,
|
||||
then U-Boot SPL loads the combined payload image of OpenSBI fw_dynamic
|
||||
firmware and U-Boot proper. However sometimes we want to have a quick test
|
||||
of booting U-Boot on QEMU without the needs of preparing the SPI flash or
|
||||
SD card images, an alternate way can be used, which is to create a U-Boot
|
||||
S-mode image by modifying the configuration of U-Boot:
|
||||
firmware and U-Boot proper.
|
||||
|
||||
However sometimes we want to have a quick test of booting U-Boot on QEMU
|
||||
without the needs of preparing the SPI flash or SD card images, an alternate
|
||||
way can be used, which is to create a U-Boot S-mode image by modifying the
|
||||
configuration of U-Boot:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ export CROSS_COMPILE=riscv64-linux-
|
||||
$ make sifive_unleashed_defconfig
|
||||
$ make menuconfig
|
||||
|
||||
then manually select the following configuration in U-Boot:
|
||||
then manually select the following configuration:
|
||||
|
||||
Device Tree Control > Provider of DTB for DT Control > Prior Stage bootloader DTB
|
||||
* Device Tree Control ---> Provider of DTB for DT Control ---> Prior Stage bootloader DTB
|
||||
|
||||
This lets U-Boot to use the QEMU generated device tree blob. During the build,
|
||||
a build error will be seen below:
|
||||
and unselect the following configuration:
|
||||
|
||||
.. code-block:: none
|
||||
* Library routines ---> Allow access to binman information in the device tree
|
||||
|
||||
MKIMAGE u-boot.img
|
||||
./tools/mkimage: Can't open arch/riscv/dts/hifive-unleashed-a00.dtb: No such file or directory
|
||||
./tools/mkimage: failed to build FIT
|
||||
make: *** [Makefile:1440: u-boot.img] Error 1
|
||||
|
||||
The above errors can be safely ignored as we don't run U-Boot SPL under QEMU
|
||||
in this alternate configuration.
|
||||
This changes U-Boot to use the QEMU generated device tree blob, and bypass
|
||||
running the U-Boot SPL stage.
|
||||
|
||||
Boot the 64-bit U-Boot S-mode image directly:
|
||||
|
||||
|
@ -351,14 +351,18 @@ It's possible to create a 32-bit U-Boot S-mode image as well.
|
|||
.. code-block:: bash
|
||||
|
||||
$ export CROSS_COMPILE=riscv64-linux-
|
||||
$ make sifive_fu540_defconfig
|
||||
$ make sifive_unleashed_defconfig
|
||||
$ make menuconfig
|
||||
|
||||
then manually update the following configuration in U-Boot:
|
||||
|
||||
Device Tree Control > Provider of DTB for DT Control > Prior Stage bootloader DTB
|
||||
RISC-V architecture > Base ISA > RV32I
|
||||
Boot images > Text Base > 0x80400000
|
||||
* Device Tree Control ---> Provider of DTB for DT Control ---> Prior Stage bootloader DTB
|
||||
* RISC-V architecture ---> Base ISA ---> RV32I
|
||||
* Boot options ---> Boot images ---> Text Base ---> 0x80400000
|
||||
|
||||
and unselect the following configuration:
|
||||
|
||||
* Library routines ---> Allow access to binman information in the device tree
|
||||
|
||||
Use the same command line options to boot the 32-bit U-Boot S-mode image:
|
||||
|
||||
|
|
|
@ -53,6 +53,16 @@ with the default OpenSBI firmware image as the -bios. It also supports
|
|||
the recommended RISC-V bootflow: U-Boot SPL (M-mode) loads OpenSBI fw_dynamic
|
||||
firmware and U-Boot proper (S-mode), using the standard -bios functionality.
|
||||
|
||||
Machine-specific options
|
||||
------------------------
|
||||
|
||||
The following machine-specific options are supported:
|
||||
|
||||
- aclint=[on|off]
|
||||
|
||||
When this option is "on", ACLINT devices will be emulated instead of
|
||||
SiFive CLINT. When not specified, this option is assumed to be "off".
|
||||
|
||||
Running Linux kernel
|
||||
--------------------
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -62,7 +62,7 @@ config RX_ICU
|
|||
config LOONGSON_LIOINTC
|
||||
bool
|
||||
|
||||
config SIFIVE_CLINT
|
||||
config RISCV_ACLINT
|
||||
bool
|
||||
|
||||
config SIFIVE_PLIC
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -47,7 +47,7 @@ specific_ss.add(when: 'CONFIG_RX_ICU', if_true: files('rx_icu.c'))
|
|||
specific_ss.add(when: 'CONFIG_S390_FLIC', if_true: files('s390_flic.c'))
|
||||
specific_ss.add(when: 'CONFIG_S390_FLIC_KVM', if_true: files('s390_flic_kvm.c'))
|
||||
specific_ss.add(when: 'CONFIG_SH_INTC', if_true: files('sh_intc.c'))
|
||||
specific_ss.add(when: 'CONFIG_SIFIVE_CLINT', if_true: files('sifive_clint.c'))
|
||||
specific_ss.add(when: 'CONFIG_RISCV_ACLINT', if_true: files('riscv_aclint.c'))
|
||||
specific_ss.add(when: 'CONFIG_SIFIVE_PLIC', if_true: files('sifive_plic.c'))
|
||||
specific_ss.add(when: 'CONFIG_XICS', if_true: files('xics.c'))
|
||||
specific_ss.add(when: ['CONFIG_KVM', 'CONFIG_XICS'],
|
||||
|
|
|
@ -0,0 +1,460 @@
|
|||
/*
|
||||
* RISC-V ACLINT (Advanced Core Local Interruptor)
|
||||
* URL: https://github.com/riscv/riscv-aclint
|
||||
*
|
||||
* Copyright (c) 2016-2017 Sagar Karandikar, sagark@eecs.berkeley.edu
|
||||
* Copyright (c) 2017 SiFive, Inc.
|
||||
* Copyright (c) 2021 Western Digital Corporation or its affiliates.
|
||||
*
|
||||
* This provides real-time clock, timer and interprocessor interrupts.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2 or later, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qapi/error.h"
|
||||
#include "qemu/error-report.h"
|
||||
#include "qemu/log.h"
|
||||
#include "qemu/module.h"
|
||||
#include "hw/sysbus.h"
|
||||
#include "target/riscv/cpu.h"
|
||||
#include "hw/qdev-properties.h"
|
||||
#include "hw/intc/riscv_aclint.h"
|
||||
#include "qemu/timer.h"
|
||||
#include "hw/irq.h"
|
||||
|
||||
typedef struct riscv_aclint_mtimer_callback {
|
||||
RISCVAclintMTimerState *s;
|
||||
int num;
|
||||
} riscv_aclint_mtimer_callback;
|
||||
|
||||
static uint64_t cpu_riscv_read_rtc(uint32_t timebase_freq)
|
||||
{
|
||||
return muldiv64(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL),
|
||||
timebase_freq, NANOSECONDS_PER_SECOND);
|
||||
}
|
||||
|
||||
/*
|
||||
* Called when timecmp is written to update the QEMU timer or immediately
|
||||
* trigger timer interrupt if mtimecmp <= current timer value.
|
||||
*/
|
||||
static void riscv_aclint_mtimer_write_timecmp(RISCVAclintMTimerState *mtimer,
|
||||
RISCVCPU *cpu,
|
||||
int hartid,
|
||||
uint64_t value,
|
||||
uint32_t timebase_freq)
|
||||
{
|
||||
uint64_t next;
|
||||
uint64_t diff;
|
||||
|
||||
uint64_t rtc_r = cpu_riscv_read_rtc(timebase_freq);
|
||||
|
||||
cpu->env.timecmp = value;
|
||||
if (cpu->env.timecmp <= rtc_r) {
|
||||
/*
|
||||
* If we're setting an MTIMECMP value in the "past",
|
||||
* immediately raise the timer interrupt
|
||||
*/
|
||||
qemu_irq_raise(mtimer->timer_irqs[hartid - mtimer->hartid_base]);
|
||||
return;
|
||||
}
|
||||
|
||||
/* otherwise, set up the future timer interrupt */
|
||||
qemu_irq_lower(mtimer->timer_irqs[hartid - mtimer->hartid_base]);
|
||||
diff = cpu->env.timecmp - rtc_r;
|
||||
/* back to ns (note args switched in muldiv64) */
|
||||
uint64_t ns_diff = muldiv64(diff, NANOSECONDS_PER_SECOND, timebase_freq);
|
||||
|
||||
/*
|
||||
* check if ns_diff overflowed and check if the addition would potentially
|
||||
* overflow
|
||||
*/
|
||||
if ((NANOSECONDS_PER_SECOND > timebase_freq && ns_diff < diff) ||
|
||||
ns_diff > INT64_MAX) {
|
||||
next = INT64_MAX;
|
||||
} else {
|
||||
/*
|
||||
* as it is very unlikely qemu_clock_get_ns will return a value
|
||||
* greater than INT64_MAX, no additional check is needed for an
|
||||
* unsigned integer overflow.
|
||||
*/
|
||||
next = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + ns_diff;
|
||||
/*
|
||||
* if ns_diff is INT64_MAX next may still be outside the range
|
||||
* of a signed integer.
|
||||
*/
|
||||
next = MIN(next, INT64_MAX);
|
||||
}
|
||||
|
||||
timer_mod(cpu->env.timer, next);
|
||||
}
|
||||
|
||||
/*
|
||||
* Callback used when the timer set using timer_mod expires.
|
||||
* Should raise the timer interrupt line
|
||||
*/
|
||||
static void riscv_aclint_mtimer_cb(void *opaque)
|
||||
{
|
||||
riscv_aclint_mtimer_callback *state = opaque;
|
||||
|
||||
qemu_irq_raise(state->s->timer_irqs[state->num]);
|
||||
}
|
||||
|
||||
/* CPU read MTIMER register */
|
||||
static uint64_t riscv_aclint_mtimer_read(void *opaque, hwaddr addr,
|
||||
unsigned size)
|
||||
{
|
||||
RISCVAclintMTimerState *mtimer = opaque;
|
||||
|
||||
if (addr >= mtimer->timecmp_base &&
|
||||
addr < (mtimer->timecmp_base + (mtimer->num_harts << 3))) {
|
||||
size_t hartid = mtimer->hartid_base +
|
||||
((addr - mtimer->timecmp_base) >> 3);
|
||||
CPUState *cpu = qemu_get_cpu(hartid);
|
||||
CPURISCVState *env = cpu ? cpu->env_ptr : NULL;
|
||||
if (!env) {
|
||||
qemu_log_mask(LOG_GUEST_ERROR,
|
||||
"aclint-mtimer: invalid hartid: %zu", hartid);
|
||||
} else if ((addr & 0x7) == 0) {
|
||||
/* timecmp_lo */
|
||||
uint64_t timecmp = env->timecmp;
|
||||
return timecmp & 0xFFFFFFFF;
|
||||
} else if ((addr & 0x7) == 4) {
|
||||
/* timecmp_hi */
|
||||
uint64_t timecmp = env->timecmp;
|
||||
return (timecmp >> 32) & 0xFFFFFFFF;
|
||||
} else {
|
||||
qemu_log_mask(LOG_UNIMP,
|
||||
"aclint-mtimer: invalid read: %08x", (uint32_t)addr);
|
||||
return 0;
|
||||
}
|
||||
} else if (addr == mtimer->time_base) {
|
||||
/* time_lo */
|
||||
return cpu_riscv_read_rtc(mtimer->timebase_freq) & 0xFFFFFFFF;
|
||||
} else if (addr == mtimer->time_base + 4) {
|
||||
/* time_hi */
|
||||
return (cpu_riscv_read_rtc(mtimer->timebase_freq) >> 32) & 0xFFFFFFFF;
|
||||
}
|
||||
|
||||
qemu_log_mask(LOG_UNIMP,
|
||||
"aclint-mtimer: invalid read: %08x", (uint32_t)addr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* CPU write MTIMER register */
|
||||
static void riscv_aclint_mtimer_write(void *opaque, hwaddr addr,
|
||||
uint64_t value, unsigned size)
|
||||
{
|
||||
RISCVAclintMTimerState *mtimer = opaque;
|
||||
|
||||
if (addr >= mtimer->timecmp_base &&
|
||||
addr < (mtimer->timecmp_base + (mtimer->num_harts << 3))) {
|
||||
size_t hartid = mtimer->hartid_base +
|
||||
((addr - mtimer->timecmp_base) >> 3);
|
||||
CPUState *cpu = qemu_get_cpu(hartid);
|
||||
CPURISCVState *env = cpu ? cpu->env_ptr : NULL;
|
||||
if (!env) {
|
||||
qemu_log_mask(LOG_GUEST_ERROR,
|
||||
"aclint-mtimer: invalid hartid: %zu", hartid);
|
||||
} else if ((addr & 0x7) == 0) {
|
||||
/* timecmp_lo */
|
||||
uint64_t timecmp_hi = env->timecmp >> 32;
|
||||
riscv_aclint_mtimer_write_timecmp(mtimer, RISCV_CPU(cpu), hartid,
|
||||
timecmp_hi << 32 | (value & 0xFFFFFFFF),
|
||||
mtimer->timebase_freq);
|
||||
return;
|
||||
} else if ((addr & 0x7) == 4) {
|
||||
/* timecmp_hi */
|
||||
uint64_t timecmp_lo = env->timecmp;
|
||||
riscv_aclint_mtimer_write_timecmp(mtimer, RISCV_CPU(cpu), hartid,
|
||||
value << 32 | (timecmp_lo & 0xFFFFFFFF),
|
||||
mtimer->timebase_freq);
|
||||
} else {
|
||||
qemu_log_mask(LOG_UNIMP,
|
||||
"aclint-mtimer: invalid timecmp write: %08x",
|
||||
(uint32_t)addr);
|
||||
}
|
||||
return;
|
||||
} else if (addr == mtimer->time_base) {
|
||||
/* time_lo */
|
||||
qemu_log_mask(LOG_UNIMP,
|
||||
"aclint-mtimer: time_lo write not implemented");
|
||||
return;
|
||||
} else if (addr == mtimer->time_base + 4) {
|
||||
/* time_hi */
|
||||
qemu_log_mask(LOG_UNIMP,
|
||||
"aclint-mtimer: time_hi write not implemented");
|
||||
return;
|
||||
}
|
||||
|
||||
qemu_log_mask(LOG_UNIMP,
|
||||
"aclint-mtimer: invalid write: %08x", (uint32_t)addr);
|
||||
}
|
||||
|
||||
static const MemoryRegionOps riscv_aclint_mtimer_ops = {
|
||||
.read = riscv_aclint_mtimer_read,
|
||||
.write = riscv_aclint_mtimer_write,
|
||||
.endianness = DEVICE_LITTLE_ENDIAN,
|
||||
.valid = {
|
||||
.min_access_size = 4,
|
||||
.max_access_size = 8
|
||||
}
|
||||
};
|
||||
|
||||
static Property riscv_aclint_mtimer_properties[] = {
|
||||
DEFINE_PROP_UINT32("hartid-base", RISCVAclintMTimerState,
|
||||
hartid_base, 0),
|
||||
DEFINE_PROP_UINT32("num-harts", RISCVAclintMTimerState, num_harts, 1),
|
||||
DEFINE_PROP_UINT32("timecmp-base", RISCVAclintMTimerState,
|
||||
timecmp_base, RISCV_ACLINT_DEFAULT_MTIMECMP),
|
||||
DEFINE_PROP_UINT32("time-base", RISCVAclintMTimerState,
|
||||
time_base, RISCV_ACLINT_DEFAULT_MTIME),
|
||||
DEFINE_PROP_UINT32("aperture-size", RISCVAclintMTimerState,
|
||||
aperture_size, RISCV_ACLINT_DEFAULT_MTIMER_SIZE),
|
||||
DEFINE_PROP_UINT32("timebase-freq", RISCVAclintMTimerState,
|
||||
timebase_freq, 0),
|
||||
DEFINE_PROP_END_OF_LIST(),
|
||||
};
|
||||
|
||||
static void riscv_aclint_mtimer_realize(DeviceState *dev, Error **errp)
|
||||
{
|
||||
RISCVAclintMTimerState *s = RISCV_ACLINT_MTIMER(dev);
|
||||
int i;
|
||||
|
||||
memory_region_init_io(&s->mmio, OBJECT(dev), &riscv_aclint_mtimer_ops,
|
||||
s, TYPE_RISCV_ACLINT_MTIMER, s->aperture_size);
|
||||
sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->mmio);
|
||||
|
||||
s->timer_irqs = g_malloc(sizeof(qemu_irq) * s->num_harts);
|
||||
qdev_init_gpio_out(dev, s->timer_irqs, s->num_harts);
|
||||
|
||||
/* Claim timer interrupt bits */
|
||||
for (i = 0; i < s->num_harts; i++) {
|
||||
RISCVCPU *cpu = RISCV_CPU(qemu_get_cpu(s->hartid_base + i));
|
||||
if (riscv_cpu_claim_interrupts(cpu, MIP_MTIP) < 0) {
|
||||
error_report("MTIP already claimed");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void riscv_aclint_mtimer_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
dc->realize = riscv_aclint_mtimer_realize;
|
||||
device_class_set_props(dc, riscv_aclint_mtimer_properties);
|
||||
}
|
||||
|
||||
static const TypeInfo riscv_aclint_mtimer_info = {
|
||||
.name = TYPE_RISCV_ACLINT_MTIMER,
|
||||
.parent = TYPE_SYS_BUS_DEVICE,
|
||||
.instance_size = sizeof(RISCVAclintMTimerState),
|
||||
.class_init = riscv_aclint_mtimer_class_init,
|
||||
};
|
||||
|
||||
/*
|
||||
* Create ACLINT MTIMER device.
|
||||
*/
|
||||
DeviceState *riscv_aclint_mtimer_create(hwaddr addr, hwaddr size,
|
||||
uint32_t hartid_base, uint32_t num_harts,
|
||||
uint32_t timecmp_base, uint32_t time_base, uint32_t timebase_freq,
|
||||
bool provide_rdtime)
|
||||
{
|
||||
int i;
|
||||
DeviceState *dev = qdev_new(TYPE_RISCV_ACLINT_MTIMER);
|
||||
|
||||
assert(num_harts <= RISCV_ACLINT_MAX_HARTS);
|
||||
assert(!(addr & 0x7));
|
||||
assert(!(timecmp_base & 0x7));
|
||||
assert(!(time_base & 0x7));
|
||||
|
||||
qdev_prop_set_uint32(dev, "hartid-base", hartid_base);
|
||||
qdev_prop_set_uint32(dev, "num-harts", num_harts);
|
||||
qdev_prop_set_uint32(dev, "timecmp-base", timecmp_base);
|
||||
qdev_prop_set_uint32(dev, "time-base", time_base);
|
||||
qdev_prop_set_uint32(dev, "aperture-size", size);
|
||||
qdev_prop_set_uint32(dev, "timebase-freq", timebase_freq);
|
||||
sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal);
|
||||
sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, addr);
|
||||
|
||||
for (i = 0; i < num_harts; i++) {
|
||||
CPUState *cpu = qemu_get_cpu(hartid_base + i);
|
||||
RISCVCPU *rvcpu = RISCV_CPU(cpu);
|
||||
CPURISCVState *env = cpu ? cpu->env_ptr : NULL;
|
||||
riscv_aclint_mtimer_callback *cb =
|
||||
g_malloc0(sizeof(riscv_aclint_mtimer_callback));
|
||||
|
||||
if (!env) {
|
||||
g_free(cb);
|
||||
continue;
|
||||
}
|
||||
if (provide_rdtime) {
|
||||
riscv_cpu_set_rdtime_fn(env, cpu_riscv_read_rtc, timebase_freq);
|
||||
}
|
||||
|
||||
cb->s = RISCV_ACLINT_MTIMER(dev);
|
||||
cb->num = i;
|
||||
env->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL,
|
||||
&riscv_aclint_mtimer_cb, cb);
|
||||
env->timecmp = 0;
|
||||
|
||||
qdev_connect_gpio_out(dev, i,
|
||||
qdev_get_gpio_in(DEVICE(rvcpu), IRQ_M_TIMER));
|
||||
}
|
||||
|
||||
return dev;
|
||||
}
|
||||
|
||||
/* CPU read [M|S]SWI register */
|
||||
static uint64_t riscv_aclint_swi_read(void *opaque, hwaddr addr,
|
||||
unsigned size)
|
||||
{
|
||||
RISCVAclintSwiState *swi = opaque;
|
||||
|
||||
if (addr < (swi->num_harts << 2)) {
|
||||
size_t hartid = swi->hartid_base + (addr >> 2);
|
||||
CPUState *cpu = qemu_get_cpu(hartid);
|
||||
CPURISCVState *env = cpu ? cpu->env_ptr : NULL;
|
||||
if (!env) {
|
||||
qemu_log_mask(LOG_GUEST_ERROR,
|
||||
"aclint-swi: invalid hartid: %zu", hartid);
|
||||
} else if ((addr & 0x3) == 0) {
|
||||
return (swi->sswi) ? 0 : ((env->mip & MIP_MSIP) > 0);
|
||||
}
|
||||
}
|
||||
|
||||
qemu_log_mask(LOG_UNIMP,
|
||||
"aclint-swi: invalid read: %08x", (uint32_t)addr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* CPU write [M|S]SWI register */
|
||||
static void riscv_aclint_swi_write(void *opaque, hwaddr addr, uint64_t value,
|
||||
unsigned size)
|
||||
{
|
||||
RISCVAclintSwiState *swi = opaque;
|
||||
|
||||
if (addr < (swi->num_harts << 2)) {
|
||||
size_t hartid = swi->hartid_base + (addr >> 2);
|
||||
CPUState *cpu = qemu_get_cpu(hartid);
|
||||
CPURISCVState *env = cpu ? cpu->env_ptr : NULL;
|
||||
if (!env) {
|
||||
qemu_log_mask(LOG_GUEST_ERROR,
|
||||
"aclint-swi: invalid hartid: %zu", hartid);
|
||||
} else if ((addr & 0x3) == 0) {
|
||||
if (value & 0x1) {
|
||||
qemu_irq_raise(swi->soft_irqs[hartid - swi->hartid_base]);
|
||||
} else {
|
||||
if (!swi->sswi) {
|
||||
qemu_irq_lower(swi->soft_irqs[hartid - swi->hartid_base]);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
qemu_log_mask(LOG_UNIMP,
|
||||
"aclint-swi: invalid write: %08x", (uint32_t)addr);
|
||||
}
|
||||
|
||||
static const MemoryRegionOps riscv_aclint_swi_ops = {
|
||||
.read = riscv_aclint_swi_read,
|
||||
.write = riscv_aclint_swi_write,
|
||||
.endianness = DEVICE_LITTLE_ENDIAN,
|
||||
.valid = {
|
||||
.min_access_size = 4,
|
||||
.max_access_size = 4
|
||||
}
|
||||
};
|
||||
|
||||
static Property riscv_aclint_swi_properties[] = {
|
||||
DEFINE_PROP_UINT32("hartid-base", RISCVAclintSwiState, hartid_base, 0),
|
||||
DEFINE_PROP_UINT32("num-harts", RISCVAclintSwiState, num_harts, 1),
|
||||
DEFINE_PROP_UINT32("sswi", RISCVAclintSwiState, sswi, false),
|
||||
DEFINE_PROP_END_OF_LIST(),
|
||||
};
|
||||
|
||||
static void riscv_aclint_swi_realize(DeviceState *dev, Error **errp)
|
||||
{
|
||||
RISCVAclintSwiState *swi = RISCV_ACLINT_SWI(dev);
|
||||
int i;
|
||||
|
||||
memory_region_init_io(&swi->mmio, OBJECT(dev), &riscv_aclint_swi_ops, swi,
|
||||
TYPE_RISCV_ACLINT_SWI, RISCV_ACLINT_SWI_SIZE);
|
||||
sysbus_init_mmio(SYS_BUS_DEVICE(dev), &swi->mmio);
|
||||
|
||||
swi->soft_irqs = g_malloc(sizeof(qemu_irq) * swi->num_harts);
|
||||
qdev_init_gpio_out(dev, swi->soft_irqs, swi->num_harts);
|
||||
|
||||
/* Claim software interrupt bits */
|
||||
for (i = 0; i < swi->num_harts; i++) {
|
||||
RISCVCPU *cpu = RISCV_CPU(qemu_get_cpu(swi->hartid_base + i));
|
||||
/* We don't claim mip.SSIP because it is writeable by software */
|
||||
if (riscv_cpu_claim_interrupts(cpu, swi->sswi ? 0 : MIP_MSIP) < 0) {
|
||||
error_report("MSIP already claimed");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void riscv_aclint_swi_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
dc->realize = riscv_aclint_swi_realize;
|
||||
device_class_set_props(dc, riscv_aclint_swi_properties);
|
||||
}
|
||||
|
||||
static const TypeInfo riscv_aclint_swi_info = {
|
||||
.name = TYPE_RISCV_ACLINT_SWI,
|
||||
.parent = TYPE_SYS_BUS_DEVICE,
|
||||
.instance_size = sizeof(RISCVAclintSwiState),
|
||||
.class_init = riscv_aclint_swi_class_init,
|
||||
};
|
||||
|
||||
/*
|
||||
* Create ACLINT [M|S]SWI device.
|
||||
*/
|
||||
DeviceState *riscv_aclint_swi_create(hwaddr addr, uint32_t hartid_base,
|
||||
uint32_t num_harts, bool sswi)
|
||||
{
|
||||
int i;
|
||||
DeviceState *dev = qdev_new(TYPE_RISCV_ACLINT_SWI);
|
||||
|
||||
assert(num_harts <= RISCV_ACLINT_MAX_HARTS);
|
||||
assert(!(addr & 0x3));
|
||||
|
||||
qdev_prop_set_uint32(dev, "hartid-base", hartid_base);
|
||||
qdev_prop_set_uint32(dev, "num-harts", num_harts);
|
||||
qdev_prop_set_uint32(dev, "sswi", sswi ? true : false);
|
||||
sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal);
|
||||
sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, addr);
|
||||
|
||||
for (i = 0; i < num_harts; i++) {
|
||||
CPUState *cpu = qemu_get_cpu(hartid_base + i);
|
||||
RISCVCPU *rvcpu = RISCV_CPU(cpu);
|
||||
|
||||
qdev_connect_gpio_out(dev, i,
|
||||
qdev_get_gpio_in(DEVICE(rvcpu),
|
||||
(sswi) ? IRQ_S_SOFT : IRQ_M_SOFT));
|
||||
}
|
||||
|
||||
return dev;
|
||||
}
|
||||
|
||||
static void riscv_aclint_register_types(void)
|
||||
{
|
||||
type_register_static(&riscv_aclint_mtimer_info);
|
||||
type_register_static(&riscv_aclint_swi_info);
|
||||
}
|
||||
|
||||
type_init(riscv_aclint_register_types)
|
|
@ -1,287 +0,0 @@
|
|||
/*
|
||||
* SiFive CLINT (Core Local Interruptor)
|
||||
*
|
||||
* Copyright (c) 2016-2017 Sagar Karandikar, sagark@eecs.berkeley.edu
|
||||
* Copyright (c) 2017 SiFive, Inc.
|
||||
*
|
||||
* This provides real-time clock, timer and interprocessor interrupts.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2 or later, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qapi/error.h"
|
||||
#include "qemu/error-report.h"
|
||||
#include "qemu/module.h"
|
||||
#include "hw/sysbus.h"
|
||||
#include "target/riscv/cpu.h"
|
||||
#include "hw/qdev-properties.h"
|
||||
#include "hw/intc/sifive_clint.h"
|
||||
#include "qemu/timer.h"
|
||||
|
||||
static uint64_t cpu_riscv_read_rtc(uint32_t timebase_freq)
|
||||
{
|
||||
return muldiv64(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL),
|
||||
timebase_freq, NANOSECONDS_PER_SECOND);
|
||||
}
|
||||
|
||||
/*
|
||||
* Called when timecmp is written to update the QEMU timer or immediately
|
||||
* trigger timer interrupt if mtimecmp <= current timer value.
|
||||
*/
|
||||
static void sifive_clint_write_timecmp(RISCVCPU *cpu, uint64_t value,
|
||||
uint32_t timebase_freq)
|
||||
{
|
||||
uint64_t next;
|
||||
uint64_t diff;
|
||||
|
||||
uint64_t rtc_r = cpu_riscv_read_rtc(timebase_freq);
|
||||
|
||||
cpu->env.timecmp = value;
|
||||
if (cpu->env.timecmp <= rtc_r) {
|
||||
/* if we're setting an MTIMECMP value in the "past",
|
||||
immediately raise the timer interrupt */
|
||||
riscv_cpu_update_mip(cpu, MIP_MTIP, BOOL_TO_MASK(1));
|
||||
return;
|
||||
}
|
||||
|
||||
/* otherwise, set up the future timer interrupt */
|
||||
riscv_cpu_update_mip(cpu, MIP_MTIP, BOOL_TO_MASK(0));
|
||||
diff = cpu->env.timecmp - rtc_r;
|
||||
/* back to ns (note args switched in muldiv64) */
|
||||
uint64_t ns_diff = muldiv64(diff, NANOSECONDS_PER_SECOND, timebase_freq);
|
||||
|
||||
/*
|
||||
* check if ns_diff overflowed and check if the addition would potentially
|
||||
* overflow
|
||||
*/
|
||||
if ((NANOSECONDS_PER_SECOND > timebase_freq && ns_diff < diff) ||
|
||||
ns_diff > INT64_MAX) {
|
||||
next = INT64_MAX;
|
||||
} else {
|
||||
/*
|
||||
* as it is very unlikely qemu_clock_get_ns will return a value
|
||||
* greater than INT64_MAX, no additional check is needed for an
|
||||
* unsigned integer overflow.
|
||||
*/
|
||||
next = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + ns_diff;
|
||||
/*
|
||||
* if ns_diff is INT64_MAX next may still be outside the range
|
||||
* of a signed integer.
|
||||
*/
|
||||
next = MIN(next, INT64_MAX);
|
||||
}
|
||||
|
||||
timer_mod(cpu->env.timer, next);
|
||||
}
|
||||
|
||||
/*
|
||||
* Callback used when the timer set using timer_mod expires.
|
||||
* Should raise the timer interrupt line
|
||||
*/
|
||||
static void sifive_clint_timer_cb(void *opaque)
|
||||
{
|
||||
RISCVCPU *cpu = opaque;
|
||||
riscv_cpu_update_mip(cpu, MIP_MTIP, BOOL_TO_MASK(1));
|
||||
}
|
||||
|
||||
/* CPU wants to read rtc or timecmp register */
|
||||
static uint64_t sifive_clint_read(void *opaque, hwaddr addr, unsigned size)
|
||||
{
|
||||
SiFiveCLINTState *clint = opaque;
|
||||
if (addr >= clint->sip_base &&
|
||||
addr < clint->sip_base + (clint->num_harts << 2)) {
|
||||
size_t hartid = clint->hartid_base + ((addr - clint->sip_base) >> 2);
|
||||
CPUState *cpu = qemu_get_cpu(hartid);
|
||||
CPURISCVState *env = cpu ? cpu->env_ptr : NULL;
|
||||
if (!env) {
|
||||
error_report("clint: invalid timecmp hartid: %zu", hartid);
|
||||
} else if ((addr & 0x3) == 0) {
|
||||
return (env->mip & MIP_MSIP) > 0;
|
||||
} else {
|
||||
error_report("clint: invalid read: %08x", (uint32_t)addr);
|
||||
return 0;
|
||||
}
|
||||
} else if (addr >= clint->timecmp_base &&
|
||||
addr < clint->timecmp_base + (clint->num_harts << 3)) {
|
||||
size_t hartid = clint->hartid_base +
|
||||
((addr - clint->timecmp_base) >> 3);
|
||||
CPUState *cpu = qemu_get_cpu(hartid);
|
||||
CPURISCVState *env = cpu ? cpu->env_ptr : NULL;
|
||||
if (!env) {
|
||||
error_report("clint: invalid timecmp hartid: %zu", hartid);
|
||||
} else if ((addr & 0x7) == 0) {
|
||||
/* timecmp_lo */
|
||||
uint64_t timecmp = env->timecmp;
|
||||
return timecmp & 0xFFFFFFFF;
|
||||
} else if ((addr & 0x7) == 4) {
|
||||
/* timecmp_hi */
|
||||
uint64_t timecmp = env->timecmp;
|
||||
return (timecmp >> 32) & 0xFFFFFFFF;
|
||||
} else {
|
||||
error_report("clint: invalid read: %08x", (uint32_t)addr);
|
||||
return 0;
|
||||
}
|
||||
} else if (addr == clint->time_base) {
|
||||
/* time_lo */
|
||||
return cpu_riscv_read_rtc(clint->timebase_freq) & 0xFFFFFFFF;
|
||||
} else if (addr == clint->time_base + 4) {
|
||||
/* time_hi */
|
||||
return (cpu_riscv_read_rtc(clint->timebase_freq) >> 32) & 0xFFFFFFFF;
|
||||
}
|
||||
|
||||
error_report("clint: invalid read: %08x", (uint32_t)addr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* CPU wrote to rtc or timecmp register */
|
||||
static void sifive_clint_write(void *opaque, hwaddr addr, uint64_t value,
|
||||
unsigned size)
|
||||
{
|
||||
SiFiveCLINTState *clint = opaque;
|
||||
|
||||
if (addr >= clint->sip_base &&
|
||||
addr < clint->sip_base + (clint->num_harts << 2)) {
|
||||
size_t hartid = clint->hartid_base + ((addr - clint->sip_base) >> 2);
|
||||
CPUState *cpu = qemu_get_cpu(hartid);
|
||||
CPURISCVState *env = cpu ? cpu->env_ptr : NULL;
|
||||
if (!env) {
|
||||
error_report("clint: invalid timecmp hartid: %zu", hartid);
|
||||
} else if ((addr & 0x3) == 0) {
|
||||
riscv_cpu_update_mip(RISCV_CPU(cpu), MIP_MSIP, BOOL_TO_MASK(value));
|
||||
} else {
|
||||
error_report("clint: invalid sip write: %08x", (uint32_t)addr);
|
||||
}
|
||||
return;
|
||||
} else if (addr >= clint->timecmp_base &&
|
||||
addr < clint->timecmp_base + (clint->num_harts << 3)) {
|
||||
size_t hartid = clint->hartid_base +
|
||||
((addr - clint->timecmp_base) >> 3);
|
||||
CPUState *cpu = qemu_get_cpu(hartid);
|
||||
CPURISCVState *env = cpu ? cpu->env_ptr : NULL;
|
||||
if (!env) {
|
||||
error_report("clint: invalid timecmp hartid: %zu", hartid);
|
||||
} else if ((addr & 0x7) == 0) {
|
||||
/* timecmp_lo */
|
||||
uint64_t timecmp_hi = env->timecmp >> 32;
|
||||
sifive_clint_write_timecmp(RISCV_CPU(cpu),
|
||||
timecmp_hi << 32 | (value & 0xFFFFFFFF), clint->timebase_freq);
|
||||
return;
|
||||
} else if ((addr & 0x7) == 4) {
|
||||
/* timecmp_hi */
|
||||
uint64_t timecmp_lo = env->timecmp;
|
||||
sifive_clint_write_timecmp(RISCV_CPU(cpu),
|
||||
value << 32 | (timecmp_lo & 0xFFFFFFFF), clint->timebase_freq);
|
||||
} else {
|
||||
error_report("clint: invalid timecmp write: %08x", (uint32_t)addr);
|
||||
}
|
||||
return;
|
||||
} else if (addr == clint->time_base) {
|
||||
/* time_lo */
|
||||
error_report("clint: time_lo write not implemented");
|
||||
return;
|
||||
} else if (addr == clint->time_base + 4) {
|
||||
/* time_hi */
|
||||
error_report("clint: time_hi write not implemented");
|
||||
return;
|
||||
}
|
||||
|
||||
error_report("clint: invalid write: %08x", (uint32_t)addr);
|
||||
}
|
||||
|
||||
static const MemoryRegionOps sifive_clint_ops = {
|
||||
.read = sifive_clint_read,
|
||||
.write = sifive_clint_write,
|
||||
.endianness = DEVICE_LITTLE_ENDIAN,
|
||||
.valid = {
|
||||
.min_access_size = 4,
|
||||
.max_access_size = 8
|
||||
}
|
||||
};
|
||||
|
||||
static Property sifive_clint_properties[] = {
|
||||
DEFINE_PROP_UINT32("hartid-base", SiFiveCLINTState, hartid_base, 0),
|
||||
DEFINE_PROP_UINT32("num-harts", SiFiveCLINTState, num_harts, 0),
|
||||
DEFINE_PROP_UINT32("sip-base", SiFiveCLINTState, sip_base, 0),
|
||||
DEFINE_PROP_UINT32("timecmp-base", SiFiveCLINTState, timecmp_base, 0),
|
||||
DEFINE_PROP_UINT32("time-base", SiFiveCLINTState, time_base, 0),
|
||||
DEFINE_PROP_UINT32("aperture-size", SiFiveCLINTState, aperture_size, 0),
|
||||
DEFINE_PROP_UINT32("timebase-freq", SiFiveCLINTState, timebase_freq, 0),
|
||||
DEFINE_PROP_END_OF_LIST(),
|
||||
};
|
||||
|
||||
static void sifive_clint_realize(DeviceState *dev, Error **errp)
|
||||
{
|
||||
SiFiveCLINTState *s = SIFIVE_CLINT(dev);
|
||||
memory_region_init_io(&s->mmio, OBJECT(dev), &sifive_clint_ops, s,
|
||||
TYPE_SIFIVE_CLINT, s->aperture_size);
|
||||
sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->mmio);
|
||||
}
|
||||
|
||||
static void sifive_clint_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
dc->realize = sifive_clint_realize;
|
||||
device_class_set_props(dc, sifive_clint_properties);
|
||||
}
|
||||
|
||||
static const TypeInfo sifive_clint_info = {
|
||||
.name = TYPE_SIFIVE_CLINT,
|
||||
.parent = TYPE_SYS_BUS_DEVICE,
|
||||
.instance_size = sizeof(SiFiveCLINTState),
|
||||
.class_init = sifive_clint_class_init,
|
||||
};
|
||||
|
||||
static void sifive_clint_register_types(void)
|
||||
{
|
||||
type_register_static(&sifive_clint_info);
|
||||
}
|
||||
|
||||
type_init(sifive_clint_register_types)
|
||||
|
||||
|
||||
/*
|
||||
* Create CLINT device.
|
||||
*/
|
||||
DeviceState *sifive_clint_create(hwaddr addr, hwaddr size,
|
||||
uint32_t hartid_base, uint32_t num_harts, uint32_t sip_base,
|
||||
uint32_t timecmp_base, uint32_t time_base, uint32_t timebase_freq,
|
||||
bool provide_rdtime)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < num_harts; i++) {
|
||||
CPUState *cpu = qemu_get_cpu(hartid_base + i);
|
||||
CPURISCVState *env = cpu ? cpu->env_ptr : NULL;
|
||||
if (!env) {
|
||||
continue;
|
||||
}
|
||||
if (provide_rdtime) {
|
||||
riscv_cpu_set_rdtime_fn(env, cpu_riscv_read_rtc, timebase_freq);
|
||||
}
|
||||
env->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL,
|
||||
&sifive_clint_timer_cb, cpu);
|
||||
env->timecmp = 0;
|
||||
}
|
||||
|
||||
DeviceState *dev = qdev_new(TYPE_SIFIVE_CLINT);
|
||||
qdev_prop_set_uint32(dev, "hartid-base", hartid_base);
|
||||
qdev_prop_set_uint32(dev, "num-harts", num_harts);
|
||||
qdev_prop_set_uint32(dev, "sip-base", sip_base);
|
||||
qdev_prop_set_uint32(dev, "timecmp-base", timecmp_base);
|
||||
qdev_prop_set_uint32(dev, "time-base", time_base);
|
||||
qdev_prop_set_uint32(dev, "aperture-size", size);
|
||||
qdev_prop_set_uint32(dev, "timebase-freq", timebase_freq);
|
||||
sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal);
|
||||
sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, addr);
|
||||
return dev;
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -12,7 +12,7 @@ config MICROCHIP_PFSOC
|
|||
select MCHP_PFSOC_MMUART
|
||||
select MCHP_PFSOC_SYSREG
|
||||
select MSI_NONBROKEN
|
||||
select SIFIVE_CLINT
|
||||
select RISCV_ACLINT
|
||||
select SIFIVE_PDMA
|
||||
select SIFIVE_PLIC
|
||||
select UNIMP
|
||||
|
@ -26,7 +26,7 @@ config SHAKTI_C
|
|||
bool
|
||||
select UNIMP
|
||||
select SHAKTI_UART
|
||||
select SIFIVE_CLINT
|
||||
select RISCV_ACLINT
|
||||
select SIFIVE_PLIC
|
||||
|
||||
config RISCV_VIRT
|
||||
|
@ -41,7 +41,7 @@ config RISCV_VIRT
|
|||
select PCI_EXPRESS_GENERIC_BRIDGE
|
||||
select PFLASH_CFI01
|
||||
select SERIAL
|
||||
select SIFIVE_CLINT
|
||||
select RISCV_ACLINT
|
||||
select SIFIVE_PLIC
|
||||
select SIFIVE_TEST
|
||||
select VIRTIO_MMIO
|
||||
|
@ -50,7 +50,7 @@ config RISCV_VIRT
|
|||
config SIFIVE_E
|
||||
bool
|
||||
select MSI_NONBROKEN
|
||||
select SIFIVE_CLINT
|
||||
select RISCV_ACLINT
|
||||
select SIFIVE_GPIO
|
||||
select SIFIVE_PLIC
|
||||
select SIFIVE_UART
|
||||
|
@ -61,7 +61,7 @@ config SIFIVE_U
|
|||
bool
|
||||
select CADENCE
|
||||
select MSI_NONBROKEN
|
||||
select SIFIVE_CLINT
|
||||
select RISCV_ACLINT
|
||||
select SIFIVE_GPIO
|
||||
select SIFIVE_PDMA
|
||||
select SIFIVE_PLIC
|
||||
|
@ -69,6 +69,7 @@ config SIFIVE_U
|
|||
select SIFIVE_UART
|
||||
select SIFIVE_U_OTP
|
||||
select SIFIVE_U_PRCI
|
||||
select SIFIVE_PWM
|
||||
select SSI_M25P80
|
||||
select SSI_SD
|
||||
select UNIMP
|
||||
|
@ -78,5 +79,5 @@ config SPIKE
|
|||
select RISCV_NUMA
|
||||
select HTIF
|
||||
select MSI_NONBROKEN
|
||||
select SIFIVE_CLINT
|
||||
select RISCV_ACLINT
|
||||
select SIFIVE_PLIC
|
||||
|
|
|
@ -49,7 +49,7 @@
|
|||
#include "hw/riscv/boot.h"
|
||||
#include "hw/riscv/riscv_hart.h"
|
||||
#include "hw/riscv/microchip_pfsoc.h"
|
||||
#include "hw/intc/sifive_clint.h"
|
||||
#include "hw/intc/riscv_aclint.h"
|
||||
#include "hw/intc/sifive_plic.h"
|
||||
#include "sysemu/device_tree.h"
|
||||
#include "sysemu/sysemu.h"
|
||||
|
@ -234,9 +234,12 @@ static void microchip_pfsoc_soc_realize(DeviceState *dev, Error **errp)
|
|||
memmap[MICROCHIP_PFSOC_BUSERR_UNIT4].size);
|
||||
|
||||
/* CLINT */
|
||||
sifive_clint_create(memmap[MICROCHIP_PFSOC_CLINT].base,
|
||||
memmap[MICROCHIP_PFSOC_CLINT].size, 0, ms->smp.cpus,
|
||||
SIFIVE_SIP_BASE, SIFIVE_TIMECMP_BASE, SIFIVE_TIME_BASE,
|
||||
riscv_aclint_swi_create(memmap[MICROCHIP_PFSOC_CLINT].base,
|
||||
0, ms->smp.cpus, false);
|
||||
riscv_aclint_mtimer_create(
|
||||
memmap[MICROCHIP_PFSOC_CLINT].base + RISCV_ACLINT_SWI_SIZE,
|
||||
RISCV_ACLINT_DEFAULT_MTIMER_SIZE, 0, ms->smp.cpus,
|
||||
RISCV_ACLINT_DEFAULT_MTIMECMP, RISCV_ACLINT_DEFAULT_MTIME,
|
||||
CLINT_TIMEBASE_FREQ, false);
|
||||
|
||||
/* L2 cache controller */
|
||||
|
@ -274,7 +277,7 @@ static void microchip_pfsoc_soc_realize(DeviceState *dev, Error **errp)
|
|||
|
||||
/* PLIC */
|
||||
s->plic = sifive_plic_create(memmap[MICROCHIP_PFSOC_PLIC].base,
|
||||
plic_hart_config, 0,
|
||||
plic_hart_config, ms->smp.cpus, 0,
|
||||
MICROCHIP_PFSOC_PLIC_NUM_SOURCES,
|
||||
MICROCHIP_PFSOC_PLIC_NUM_PRIORITIES,
|
||||
MICROCHIP_PFSOC_PLIC_PRIORITY_BASE,
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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)) {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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) */
|
||||
|
|
644
hw/riscv/virt.c
644
hw/riscv/virt.c
|
@ -32,7 +32,7 @@
|
|||
#include "hw/riscv/virt.h"
|
||||
#include "hw/riscv/boot.h"
|
||||
#include "hw/riscv/numa.h"
|
||||
#include "hw/intc/sifive_clint.h"
|
||||
#include "hw/intc/riscv_aclint.h"
|
||||
#include "hw/intc/sifive_plic.h"
|
||||
#include "hw/misc/sifive_test.h"
|
||||
#include "chardev/char.h"
|
||||
|
@ -48,6 +48,7 @@ static const MemMapEntry virt_memmap[] = {
|
|||
[VIRT_TEST] = { 0x100000, 0x1000 },
|
||||
[VIRT_RTC] = { 0x101000, 0x1000 },
|
||||
[VIRT_CLINT] = { 0x2000000, 0x10000 },
|
||||
[VIRT_ACLINT_SSWI] = { 0x2F00000, 0x4000 },
|
||||
[VIRT_PCIE_PIO] = { 0x3000000, 0x10000 },
|
||||
[VIRT_PLIC] = { 0xc000000, VIRT_PLIC_SIZE(VIRT_CPUS_MAX * 2) },
|
||||
[VIRT_UART0] = { 0x10000000, 0x100 },
|
||||
|
@ -176,214 +177,342 @@ static void create_pcie_irq_map(void *fdt, char *nodename,
|
|||
0x1800, 0, 0, 0x7);
|
||||
}
|
||||
|
||||
static void create_fdt(RISCVVirtState *s, const MemMapEntry *memmap,
|
||||
uint64_t mem_size, const char *cmdline, bool is_32_bit)
|
||||
static void create_fdt_socket_cpus(RISCVVirtState *s, int socket,
|
||||
char *clust_name, uint32_t *phandle,
|
||||
bool is_32_bit, uint32_t *intc_phandles)
|
||||
{
|
||||
void *fdt;
|
||||
int i, cpu, socket;
|
||||
int cpu;
|
||||
uint32_t cpu_phandle;
|
||||
MachineState *mc = MACHINE(s);
|
||||
char *name, *cpu_name, *core_name, *intc_name;
|
||||
|
||||
for (cpu = s->soc[socket].num_harts - 1; cpu >= 0; cpu--) {
|
||||
cpu_phandle = (*phandle)++;
|
||||
|
||||
cpu_name = g_strdup_printf("/cpus/cpu@%d",
|
||||
s->soc[socket].hartid_base + cpu);
|
||||
qemu_fdt_add_subnode(mc->fdt, cpu_name);
|
||||
qemu_fdt_setprop_string(mc->fdt, cpu_name, "mmu-type",
|
||||
(is_32_bit) ? "riscv,sv32" : "riscv,sv48");
|
||||
name = riscv_isa_string(&s->soc[socket].harts[cpu]);
|
||||
qemu_fdt_setprop_string(mc->fdt, cpu_name, "riscv,isa", name);
|
||||
g_free(name);
|
||||
qemu_fdt_setprop_string(mc->fdt, cpu_name, "compatible", "riscv");
|
||||
qemu_fdt_setprop_string(mc->fdt, cpu_name, "status", "okay");
|
||||
qemu_fdt_setprop_cell(mc->fdt, cpu_name, "reg",
|
||||
s->soc[socket].hartid_base + cpu);
|
||||
qemu_fdt_setprop_string(mc->fdt, cpu_name, "device_type", "cpu");
|
||||
riscv_socket_fdt_write_id(mc, mc->fdt, cpu_name, socket);
|
||||
qemu_fdt_setprop_cell(mc->fdt, cpu_name, "phandle", cpu_phandle);
|
||||
|
||||
intc_phandles[cpu] = (*phandle)++;
|
||||
|
||||
intc_name = g_strdup_printf("%s/interrupt-controller", cpu_name);
|
||||
qemu_fdt_add_subnode(mc->fdt, intc_name);
|
||||
qemu_fdt_setprop_cell(mc->fdt, intc_name, "phandle",
|
||||
intc_phandles[cpu]);
|
||||
qemu_fdt_setprop_string(mc->fdt, intc_name, "compatible",
|
||||
"riscv,cpu-intc");
|
||||
qemu_fdt_setprop(mc->fdt, intc_name, "interrupt-controller", NULL, 0);
|
||||
qemu_fdt_setprop_cell(mc->fdt, intc_name, "#interrupt-cells", 1);
|
||||
|
||||
core_name = g_strdup_printf("%s/core%d", clust_name, cpu);
|
||||
qemu_fdt_add_subnode(mc->fdt, core_name);
|
||||
qemu_fdt_setprop_cell(mc->fdt, core_name, "cpu", cpu_phandle);
|
||||
|
||||
g_free(core_name);
|
||||
g_free(intc_name);
|
||||
g_free(cpu_name);
|
||||
}
|
||||
}
|
||||
|
||||
static void create_fdt_socket_memory(RISCVVirtState *s,
|
||||
const MemMapEntry *memmap, int socket)
|
||||
{
|
||||
char *mem_name;
|
||||
uint64_t addr, size;
|
||||
uint32_t *clint_cells, *plic_cells;
|
||||
unsigned long clint_addr, plic_addr;
|
||||
uint32_t plic_phandle[MAX_NODES];
|
||||
uint32_t cpu_phandle, intc_phandle, test_phandle;
|
||||
uint32_t phandle = 1, plic_mmio_phandle = 1;
|
||||
uint32_t plic_pcie_phandle = 1, plic_virtio_phandle = 1;
|
||||
char *mem_name, *cpu_name, *core_name, *intc_name;
|
||||
char *name, *clint_name, *plic_name, *clust_name;
|
||||
hwaddr flashsize = virt_memmap[VIRT_FLASH].size / 2;
|
||||
hwaddr flashbase = virt_memmap[VIRT_FLASH].base;
|
||||
MachineState *mc = MACHINE(s);
|
||||
|
||||
addr = memmap[VIRT_DRAM].base + riscv_socket_mem_offset(mc, socket);
|
||||
size = riscv_socket_mem_size(mc, socket);
|
||||
mem_name = g_strdup_printf("/memory@%lx", (long)addr);
|
||||
qemu_fdt_add_subnode(mc->fdt, mem_name);
|
||||
qemu_fdt_setprop_cells(mc->fdt, mem_name, "reg",
|
||||
addr >> 32, addr, size >> 32, size);
|
||||
qemu_fdt_setprop_string(mc->fdt, mem_name, "device_type", "memory");
|
||||
riscv_socket_fdt_write_id(mc, mc->fdt, mem_name, socket);
|
||||
g_free(mem_name);
|
||||
}
|
||||
|
||||
static void create_fdt_socket_clint(RISCVVirtState *s,
|
||||
const MemMapEntry *memmap, int socket,
|
||||
uint32_t *intc_phandles)
|
||||
{
|
||||
int cpu;
|
||||
char *clint_name;
|
||||
uint32_t *clint_cells;
|
||||
unsigned long clint_addr;
|
||||
MachineState *mc = MACHINE(s);
|
||||
static const char * const clint_compat[2] = {
|
||||
"sifive,clint0", "riscv,clint0"
|
||||
};
|
||||
|
||||
clint_cells = g_new0(uint32_t, s->soc[socket].num_harts * 4);
|
||||
|
||||
for (cpu = 0; cpu < s->soc[socket].num_harts; cpu++) {
|
||||
clint_cells[cpu * 4 + 0] = cpu_to_be32(intc_phandles[cpu]);
|
||||
clint_cells[cpu * 4 + 1] = cpu_to_be32(IRQ_M_SOFT);
|
||||
clint_cells[cpu * 4 + 2] = cpu_to_be32(intc_phandles[cpu]);
|
||||
clint_cells[cpu * 4 + 3] = cpu_to_be32(IRQ_M_TIMER);
|
||||
}
|
||||
|
||||
clint_addr = memmap[VIRT_CLINT].base + (memmap[VIRT_CLINT].size * socket);
|
||||
clint_name = g_strdup_printf("/soc/clint@%lx", clint_addr);
|
||||
qemu_fdt_add_subnode(mc->fdt, clint_name);
|
||||
qemu_fdt_setprop_string_array(mc->fdt, clint_name, "compatible",
|
||||
(char **)&clint_compat,
|
||||
ARRAY_SIZE(clint_compat));
|
||||
qemu_fdt_setprop_cells(mc->fdt, clint_name, "reg",
|
||||
0x0, clint_addr, 0x0, memmap[VIRT_CLINT].size);
|
||||
qemu_fdt_setprop(mc->fdt, clint_name, "interrupts-extended",
|
||||
clint_cells, s->soc[socket].num_harts * sizeof(uint32_t) * 4);
|
||||
riscv_socket_fdt_write_id(mc, mc->fdt, clint_name, socket);
|
||||
g_free(clint_name);
|
||||
|
||||
g_free(clint_cells);
|
||||
}
|
||||
|
||||
static void create_fdt_socket_aclint(RISCVVirtState *s,
|
||||
const MemMapEntry *memmap, int socket,
|
||||
uint32_t *intc_phandles)
|
||||
{
|
||||
int cpu;
|
||||
char *name;
|
||||
unsigned long addr;
|
||||
uint32_t aclint_cells_size;
|
||||
uint32_t *aclint_mswi_cells;
|
||||
uint32_t *aclint_sswi_cells;
|
||||
uint32_t *aclint_mtimer_cells;
|
||||
MachineState *mc = MACHINE(s);
|
||||
|
||||
aclint_mswi_cells = g_new0(uint32_t, s->soc[socket].num_harts * 2);
|
||||
aclint_mtimer_cells = g_new0(uint32_t, s->soc[socket].num_harts * 2);
|
||||
aclint_sswi_cells = g_new0(uint32_t, s->soc[socket].num_harts * 2);
|
||||
|
||||
for (cpu = 0; cpu < s->soc[socket].num_harts; cpu++) {
|
||||
aclint_mswi_cells[cpu * 2 + 0] = cpu_to_be32(intc_phandles[cpu]);
|
||||
aclint_mswi_cells[cpu * 2 + 1] = cpu_to_be32(IRQ_M_SOFT);
|
||||
aclint_mtimer_cells[cpu * 2 + 0] = cpu_to_be32(intc_phandles[cpu]);
|
||||
aclint_mtimer_cells[cpu * 2 + 1] = cpu_to_be32(IRQ_M_TIMER);
|
||||
aclint_sswi_cells[cpu * 2 + 0] = cpu_to_be32(intc_phandles[cpu]);
|
||||
aclint_sswi_cells[cpu * 2 + 1] = cpu_to_be32(IRQ_S_SOFT);
|
||||
}
|
||||
aclint_cells_size = s->soc[socket].num_harts * sizeof(uint32_t) * 2;
|
||||
|
||||
addr = memmap[VIRT_CLINT].base + (memmap[VIRT_CLINT].size * socket);
|
||||
name = g_strdup_printf("/soc/mswi@%lx", addr);
|
||||
qemu_fdt_add_subnode(mc->fdt, name);
|
||||
qemu_fdt_setprop_string(mc->fdt, name, "compatible", "riscv,aclint-mswi");
|
||||
qemu_fdt_setprop_cells(mc->fdt, name, "reg",
|
||||
0x0, addr, 0x0, RISCV_ACLINT_SWI_SIZE);
|
||||
qemu_fdt_setprop(mc->fdt, name, "interrupts-extended",
|
||||
aclint_mswi_cells, aclint_cells_size);
|
||||
qemu_fdt_setprop(mc->fdt, name, "interrupt-controller", NULL, 0);
|
||||
qemu_fdt_setprop_cell(mc->fdt, name, "#interrupt-cells", 0);
|
||||
riscv_socket_fdt_write_id(mc, mc->fdt, name, socket);
|
||||
g_free(name);
|
||||
|
||||
addr = memmap[VIRT_CLINT].base + RISCV_ACLINT_SWI_SIZE +
|
||||
(memmap[VIRT_CLINT].size * socket);
|
||||
name = g_strdup_printf("/soc/mtimer@%lx", addr);
|
||||
qemu_fdt_add_subnode(mc->fdt, name);
|
||||
qemu_fdt_setprop_string(mc->fdt, name, "compatible",
|
||||
"riscv,aclint-mtimer");
|
||||
qemu_fdt_setprop_cells(mc->fdt, name, "reg",
|
||||
0x0, addr + RISCV_ACLINT_DEFAULT_MTIME,
|
||||
0x0, memmap[VIRT_CLINT].size - RISCV_ACLINT_SWI_SIZE -
|
||||
RISCV_ACLINT_DEFAULT_MTIME,
|
||||
0x0, addr + RISCV_ACLINT_DEFAULT_MTIMECMP,
|
||||
0x0, RISCV_ACLINT_DEFAULT_MTIME);
|
||||
qemu_fdt_setprop(mc->fdt, name, "interrupts-extended",
|
||||
aclint_mtimer_cells, aclint_cells_size);
|
||||
riscv_socket_fdt_write_id(mc, mc->fdt, name, socket);
|
||||
g_free(name);
|
||||
|
||||
addr = memmap[VIRT_ACLINT_SSWI].base +
|
||||
(memmap[VIRT_ACLINT_SSWI].size * socket);
|
||||
name = g_strdup_printf("/soc/sswi@%lx", addr);
|
||||
qemu_fdt_add_subnode(mc->fdt, name);
|
||||
qemu_fdt_setprop_string(mc->fdt, name, "compatible", "riscv,aclint-sswi");
|
||||
qemu_fdt_setprop_cells(mc->fdt, name, "reg",
|
||||
0x0, addr, 0x0, memmap[VIRT_ACLINT_SSWI].size);
|
||||
qemu_fdt_setprop(mc->fdt, name, "interrupts-extended",
|
||||
aclint_sswi_cells, aclint_cells_size);
|
||||
qemu_fdt_setprop(mc->fdt, name, "interrupt-controller", NULL, 0);
|
||||
qemu_fdt_setprop_cell(mc->fdt, name, "#interrupt-cells", 0);
|
||||
riscv_socket_fdt_write_id(mc, mc->fdt, name, socket);
|
||||
g_free(name);
|
||||
|
||||
g_free(aclint_mswi_cells);
|
||||
g_free(aclint_mtimer_cells);
|
||||
g_free(aclint_sswi_cells);
|
||||
}
|
||||
|
||||
static void create_fdt_socket_plic(RISCVVirtState *s,
|
||||
const MemMapEntry *memmap, int socket,
|
||||
uint32_t *phandle, uint32_t *intc_phandles,
|
||||
uint32_t *plic_phandles)
|
||||
{
|
||||
int cpu;
|
||||
char *plic_name;
|
||||
uint32_t *plic_cells;
|
||||
unsigned long plic_addr;
|
||||
MachineState *mc = MACHINE(s);
|
||||
static const char * const plic_compat[2] = {
|
||||
"sifive,plic-1.0.0", "riscv,plic0"
|
||||
};
|
||||
|
||||
if (mc->dtb) {
|
||||
fdt = mc->fdt = load_device_tree(mc->dtb, &s->fdt_size);
|
||||
if (!fdt) {
|
||||
error_report("load_device_tree() failed");
|
||||
exit(1);
|
||||
}
|
||||
goto update_bootargs;
|
||||
} else {
|
||||
fdt = mc->fdt = create_device_tree(&s->fdt_size);
|
||||
if (!fdt) {
|
||||
error_report("create_device_tree() failed");
|
||||
exit(1);
|
||||
}
|
||||
plic_cells = g_new0(uint32_t, s->soc[socket].num_harts * 4);
|
||||
|
||||
for (cpu = 0; cpu < s->soc[socket].num_harts; cpu++) {
|
||||
plic_cells[cpu * 4 + 0] = cpu_to_be32(intc_phandles[cpu]);
|
||||
plic_cells[cpu * 4 + 1] = cpu_to_be32(IRQ_M_EXT);
|
||||
plic_cells[cpu * 4 + 2] = cpu_to_be32(intc_phandles[cpu]);
|
||||
plic_cells[cpu * 4 + 3] = cpu_to_be32(IRQ_S_EXT);
|
||||
}
|
||||
|
||||
qemu_fdt_setprop_string(fdt, "/", "model", "riscv-virtio,qemu");
|
||||
qemu_fdt_setprop_string(fdt, "/", "compatible", "riscv-virtio");
|
||||
qemu_fdt_setprop_cell(fdt, "/", "#size-cells", 0x2);
|
||||
qemu_fdt_setprop_cell(fdt, "/", "#address-cells", 0x2);
|
||||
plic_phandles[socket] = (*phandle)++;
|
||||
plic_addr = memmap[VIRT_PLIC].base + (memmap[VIRT_PLIC].size * socket);
|
||||
plic_name = g_strdup_printf("/soc/plic@%lx", plic_addr);
|
||||
qemu_fdt_add_subnode(mc->fdt, plic_name);
|
||||
qemu_fdt_setprop_cell(mc->fdt, plic_name,
|
||||
"#address-cells", FDT_PLIC_ADDR_CELLS);
|
||||
qemu_fdt_setprop_cell(mc->fdt, plic_name,
|
||||
"#interrupt-cells", FDT_PLIC_INT_CELLS);
|
||||
qemu_fdt_setprop_string_array(mc->fdt, plic_name, "compatible",
|
||||
(char **)&plic_compat,
|
||||
ARRAY_SIZE(plic_compat));
|
||||
qemu_fdt_setprop(mc->fdt, plic_name, "interrupt-controller", NULL, 0);
|
||||
qemu_fdt_setprop(mc->fdt, plic_name, "interrupts-extended",
|
||||
plic_cells, s->soc[socket].num_harts * sizeof(uint32_t) * 4);
|
||||
qemu_fdt_setprop_cells(mc->fdt, plic_name, "reg",
|
||||
0x0, plic_addr, 0x0, memmap[VIRT_PLIC].size);
|
||||
qemu_fdt_setprop_cell(mc->fdt, plic_name, "riscv,ndev", VIRTIO_NDEV);
|
||||
riscv_socket_fdt_write_id(mc, mc->fdt, plic_name, socket);
|
||||
qemu_fdt_setprop_cell(mc->fdt, plic_name, "phandle",
|
||||
plic_phandles[socket]);
|
||||
g_free(plic_name);
|
||||
|
||||
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 = {
|
||||
|
|
|
@ -25,6 +25,9 @@ config ALLWINNER_A10_PIT
|
|||
bool
|
||||
select PTIMER
|
||||
|
||||
config SIFIVE_PWM
|
||||
bool
|
||||
|
||||
config STM32F2XX_TIMER
|
||||
bool
|
||||
|
||||
|
|
|
@ -77,7 +77,7 @@ static void ibex_timer_update_irqs(IbexTimerState *s)
|
|||
/*
|
||||
* If the mtimecmp was in the past raise the interrupt now.
|
||||
*/
|
||||
riscv_cpu_update_mip(cpu, MIP_MTIP, BOOL_TO_MASK(1));
|
||||
qemu_irq_raise(s->m_timer_irq);
|
||||
if (s->timer_intr_enable & R_INTR_ENABLE_IE_0_MASK) {
|
||||
s->timer_intr_state |= R_INTR_STATE_IS_0_MASK;
|
||||
qemu_set_irq(s->irq, true);
|
||||
|
@ -86,7 +86,7 @@ static void ibex_timer_update_irqs(IbexTimerState *s)
|
|||
}
|
||||
|
||||
/* Setup a timer to trigger the interrupt in the future */
|
||||
riscv_cpu_update_mip(cpu, MIP_MTIP, BOOL_TO_MASK(0));
|
||||
qemu_irq_lower(s->m_timer_irq);
|
||||
qemu_set_irq(s->irq, false);
|
||||
|
||||
diff = cpu->env.timecmp - now;
|
||||
|
@ -106,10 +106,8 @@ static void ibex_timer_update_irqs(IbexTimerState *s)
|
|||
static void ibex_timer_cb(void *opaque)
|
||||
{
|
||||
IbexTimerState *s = opaque;
|
||||
CPUState *cs = qemu_get_cpu(0);
|
||||
RISCVCPU *cpu = RISCV_CPU(cs);
|
||||
|
||||
riscv_cpu_update_mip(cpu, MIP_MTIP, BOOL_TO_MASK(1));
|
||||
qemu_irq_raise(s->m_timer_irq);
|
||||
if (s->timer_intr_enable & R_INTR_ENABLE_IE_0_MASK) {
|
||||
s->timer_intr_state |= R_INTR_STATE_IS_0_MASK;
|
||||
qemu_set_irq(s->irq, true);
|
||||
|
@ -280,12 +278,21 @@ static void ibex_timer_init(Object *obj)
|
|||
sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmio);
|
||||
}
|
||||
|
||||
static void ibex_timer_realize(DeviceState *dev, Error **errp)
|
||||
{
|
||||
IbexTimerState *s = IBEX_TIMER(dev);
|
||||
|
||||
qdev_init_gpio_out(dev, &s->m_timer_irq, 1);
|
||||
}
|
||||
|
||||
|
||||
static void ibex_timer_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
|
||||
dc->reset = ibex_timer_reset;
|
||||
dc->vmsd = &vmstate_ibex_timer;
|
||||
dc->realize = ibex_timer_realize;
|
||||
device_class_set_props(dc, ibex_timer_properties);
|
||||
}
|
||||
|
||||
|
|
|
@ -35,5 +35,6 @@ softmmu_ss.add(when: 'CONFIG_STELLARIS_GPTM', if_true: files('stellaris-gptm.c')
|
|||
softmmu_ss.add(when: 'CONFIG_STM32F2XX_TIMER', if_true: files('stm32f2xx_timer.c'))
|
||||
softmmu_ss.add(when: 'CONFIG_XILINX', if_true: files('xilinx_timer.c'))
|
||||
specific_ss.add(when: 'CONFIG_IBEX', if_true: files('ibex_timer.c'))
|
||||
softmmu_ss.add(when: 'CONFIG_SIFIVE_PWM', if_true: files('sifive_pwm.c'))
|
||||
|
||||
specific_ss.add(when: 'CONFIG_AVR_TIMER16', if_true: files('avr_timer16.c'))
|
||||
|
|
|
@ -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)
|
|
@ -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
|
||||
|
|
|
@ -60,6 +60,8 @@ struct IbexPlicState {
|
|||
uint32_t threshold_base;
|
||||
|
||||
uint32_t claim_base;
|
||||
|
||||
qemu_irq *external_irqs;
|
||||
};
|
||||
|
||||
#endif /* HW_IBEX_PLIC_H */
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
};
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -48,5 +48,7 @@ struct IbexTimerState {
|
|||
uint32_t timebase_freq;
|
||||
|
||||
qemu_irq irq;
|
||||
|
||||
qemu_irq m_timer_irq;
|
||||
};
|
||||
#endif /* HW_IBEX_TIMER_H */
|
||||
|
|
|
@ -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 */
|
|
@ -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),
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue