RISC-V PR for 6.0
This PR is a collection of RISC-V patches: - Improvements to SiFive U OTP - Upgrade OpenSBI to v0.9 - Support the QMP dump-guest-memory - Add support for the SiFive SPI controller (sifive_u) - Initial RISC-V system documentation - A fix for the Goldfish RTC - MAINTAINERS updates - Support for high PCIe memory in the virt machine -----BEGIN PGP SIGNATURE----- iQEyBAABCAAdFiEE9sSsRtSTSGjTuM6PIeENKd+XcFQFAmBA8k8ACgkQIeENKd+X cFRRMgf41R7Zn1ZSB9GHL3WgLDTPqpT4FAMpTnYRD7wPdspawl1SsHWBgaLNmJWk dwSLhvHMM44pOVCQRqdkiRrp5t5fDEm6gdRKyNn5uzWFMn7ibYIjmg7OE+79onbN VES4yTJwuskD/7JRNcrW6RytKRgzKs2au8NxxgtgXprSF0nM9wCu70z4T9XkLfjQ cpJUUaUCw628uUic/nObTxMnaiEmq/C0qU9SRRKPYVRp7jceCsHMVRazqOgdM6uf foNHi/uCNKy6I841+D8Po7h57s51LtT78JDRRVRPfWY+SRQyC5l8Eq3UtRPyg39p 7hhkiAKhG4rs8iCXKvMvTz0S//Cx =BVLq -----END PGP SIGNATURE----- Merge remote-tracking branch 'remotes/alistair/tags/pull-riscv-to-apply-20210304' into staging RISC-V PR for 6.0 This PR is a collection of RISC-V patches: - Improvements to SiFive U OTP - Upgrade OpenSBI to v0.9 - Support the QMP dump-guest-memory - Add support for the SiFive SPI controller (sifive_u) - Initial RISC-V system documentation - A fix for the Goldfish RTC - MAINTAINERS updates - Support for high PCIe memory in the virt machine # gpg: Signature made Thu 04 Mar 2021 14:44:31 GMT # gpg: using RSA key F6C4AC46D4934868D3B8CE8F21E10D29DF977054 # gpg: Good signature from "Alistair Francis <alistair@alistair23.me>" [full] # Primary key fingerprint: F6C4 AC46 D493 4868 D3B8 CE8F 21E1 0D29 DF97 7054 * remotes/alistair/tags/pull-riscv-to-apply-20210304: hw/riscv: virt: Map high mmio for PCIe hw/riscv: virt: Limit RAM size in a 32-bit system hw/riscv: virt: Drop the 'link_up' parameter of gpex_pcie_init() hw/riscv: Drop 'struct MemmapEntry' MAINTAINERS: Add a SiFive machine section goldfish_rtc: re-arm the alarm after migration docs/system: riscv: Add documentation for sifive_u machine docs/system: Add RISC-V documentation docs/system: Sort targets in alphabetical order hw/riscv: sifive_u: Change SIFIVE_U_GEM_IRQ to decimal value hw/riscv: sifive_u: Add QSPI2 controller and connect an SD card hw/riscv: sifive_u: Add QSPI0 controller and connect a flash hw/ssi: Add SiFive SPI controller support hw/block: m25p80: Add various ISSI flash information hw/block: m25p80: Add ISSI SPI flash support target-riscv: support QMP dump-guest-memory roms/opensbi: Upgrade from v0.8 to v0.9 hw/misc: sifive_u_otp: Use error_report() when block operation fails target/riscv: Declare csr_ops[] with a known size Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
commit
9a7beaad3d
@ -1373,6 +1373,15 @@ F: include/hw/misc/mchp_pfsoc_dmc.h
|
||||
F: include/hw/misc/mchp_pfsoc_ioscb.h
|
||||
F: include/hw/misc/mchp_pfsoc_sysreg.h
|
||||
|
||||
SiFive Machines
|
||||
M: Alistair Francis <Alistair.Francis@wdc.com>
|
||||
M: Bin Meng <bin.meng@windriver.com>
|
||||
M: Palmer Dabbelt <palmer@dabbelt.com>
|
||||
L: qemu-riscv@nongnu.org
|
||||
S: Supported
|
||||
F: hw/*/*sifive*.c
|
||||
F: include/hw/*/*sifive*.h
|
||||
|
||||
RX Machines
|
||||
-----------
|
||||
rx-gdbsim
|
||||
|
336
docs/system/riscv/sifive_u.rst
Normal file
336
docs/system/riscv/sifive_u.rst
Normal file
@ -0,0 +1,336 @@
|
||||
SiFive HiFive Unleashed (``sifive_u``)
|
||||
======================================
|
||||
|
||||
SiFive HiFive Unleashed Development Board is the ultimate RISC-V development
|
||||
board featuring the Freedom U540 multi-core RISC-V processor.
|
||||
|
||||
Supported devices
|
||||
-----------------
|
||||
|
||||
The ``sifive_u`` machine supports the following devices:
|
||||
|
||||
* 1 E51 / E31 core
|
||||
* Up to 4 U54 / U34 cores
|
||||
* Core Level Interruptor (CLINT)
|
||||
* Platform-Level Interrupt Controller (PLIC)
|
||||
* Power, Reset, Clock, Interrupt (PRCI)
|
||||
* L2 Loosely Integrated Memory (L2-LIM)
|
||||
* DDR memory controller
|
||||
* 2 UARTs
|
||||
* 1 GEM Ethernet controller
|
||||
* 1 GPIO controller
|
||||
* 1 One-Time Programmable (OTP) memory with stored serial number
|
||||
* 1 DMA controller
|
||||
* 2 QSPI controllers
|
||||
* 1 ISSI 25WP256 flash
|
||||
* 1 SD card in SPI mode
|
||||
|
||||
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.
|
||||
With QEMU, one can create a machine with 1 E51 core and up to 4 U54 cores. It
|
||||
is also possible to create a 32-bit variant with the same peripherals except
|
||||
that the RISC-V cores are replaced by the 32-bit ones (E31 and U34), to help
|
||||
testing of 32-bit guest software.
|
||||
|
||||
Hardware configuration information
|
||||
----------------------------------
|
||||
|
||||
The ``sifive_u`` machine automatically generates a device tree blob ("dtb")
|
||||
which it passes to the guest. This provides information about the addresses,
|
||||
interrupt lines and other configuration of the various devices in the system.
|
||||
Guest software should discover the devices that are present in the generated
|
||||
DTB instead of using a DTB for the real hardware, as some of the devices are
|
||||
not modeled by QEMU and trying to access these devices may cause unexpected
|
||||
behavior.
|
||||
|
||||
Boot options
|
||||
------------
|
||||
|
||||
The ``sifive_u`` machine can start using the standard -kernel functionality
|
||||
for loading a Linux kernel, a VxWorks kernel, a modified U-Boot bootloader
|
||||
(S-mode) or ELF executable with the default OpenSBI firmware image as the
|
||||
-bios. It also supports booting the unmodified U-Boot bootloader using the
|
||||
standard -bios functionality.
|
||||
|
||||
Machine-specific options
|
||||
------------------------
|
||||
|
||||
The following machine-specific options are supported:
|
||||
|
||||
- serial=nnn
|
||||
|
||||
The board serial number. When not given, the default serial number 1 is used.
|
||||
|
||||
SiFive reserves the first 1 KiB of the 16 KiB OTP memory for internal use.
|
||||
The current usage is only used to store the serial number of the board at
|
||||
offset 0xfc. U-Boot reads the serial number from the OTP memory, and uses
|
||||
it to generate a unique MAC address to be programmed to the on-chip GEM
|
||||
Ethernet controller. When multiple QEMU ``sifive_u`` machines are created
|
||||
and connected to the same subnet, they all have the same MAC address hence
|
||||
it creates an unusable network. In such scenario, user should give different
|
||||
values to serial= when creating different ``sifive_u`` machines.
|
||||
|
||||
- start-in-flash
|
||||
|
||||
When given, QEMU's ROM codes jump to QSPI memory-mapped flash directly.
|
||||
Otherwise QEMU will jump to DRAM or L2LIM depending on the msel= value.
|
||||
When not given, it defaults to direct DRAM booting.
|
||||
|
||||
- msel=[6|11]
|
||||
|
||||
Mode Select (MSEL[3:0]) pins value, used to control where to boot from.
|
||||
|
||||
The FU540 SoC supports booting from several sources, which are controlled
|
||||
using the Mode Select pins on the chip. Typically, the boot process runs
|
||||
through several stages before it begins execution of user-provided programs.
|
||||
These stages typically include the following:
|
||||
|
||||
1. Zeroth Stage Boot Loader (ZSBL), which is contained in an on-chip mask
|
||||
ROM and provided by QEMU. Note QEMU implemented ROM codes are not the
|
||||
same as what is programmed in the hardware. The QEMU one is a simplified
|
||||
version, but it provides the same functionality as the hardware.
|
||||
2. First Stage Boot Loader (FSBL), which brings up PLLs and DDR memory.
|
||||
This is U-Boot SPL.
|
||||
3. Second Stage Boot Loader (SSBL), which further initializes additional
|
||||
peripherals as needed. This is U-Boot proper combined with an OpenSBI
|
||||
fw_dynamic firmware image.
|
||||
|
||||
msel=6 means FSBL and SSBL are both on the QSPI flash. msel=11 means FSBL
|
||||
and SSBL are both on the SD card.
|
||||
|
||||
Running Linux kernel
|
||||
--------------------
|
||||
|
||||
Linux mainline v5.10 release is tested at the time of writing. To build a
|
||||
Linux mainline kernel that can be booted by the ``sifive_u`` machine in
|
||||
64-bit mode, simply configure the kernel using the defconfig configuration:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ export ARCH=riscv
|
||||
$ export CROSS_COMPILE=riscv64-linux-
|
||||
$ make defconfig
|
||||
$ make
|
||||
|
||||
To boot the newly built Linux kernel in QEMU with the ``sifive_u`` machine:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ qemu-system-riscv64 -M sifive_u -smp 5 -m 2G \
|
||||
-display none -serial stdio \
|
||||
-kernel arch/riscv/boot/Image \
|
||||
-initrd /path/to/rootfs.ext4 \
|
||||
-append "root=/dev/ram"
|
||||
|
||||
To build a Linux mainline kernel that can be booted by the ``sifive_u`` machine
|
||||
in 32-bit mode, use the rv32_defconfig configuration. A patch is required to
|
||||
fix the 32-bit boot issue for Linux kernel v5.10.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ export ARCH=riscv
|
||||
$ export CROSS_COMPILE=riscv64-linux-
|
||||
$ curl https://patchwork.kernel.org/project/linux-riscv/patch/20201219001356.2887782-1-atish.patra@wdc.com/mbox/ > riscv.patch
|
||||
$ git am riscv.patch
|
||||
$ make rv32_defconfig
|
||||
$ make
|
||||
|
||||
Replace ``qemu-system-riscv64`` with ``qemu-system-riscv32`` in the command
|
||||
line above to boot the 32-bit Linux kernel. A rootfs image containing 32-bit
|
||||
applications shall be used in order for kernel to boot to user space.
|
||||
|
||||
Running VxWorks kernel
|
||||
----------------------
|
||||
|
||||
VxWorks 7 SR0650 release is tested at the time of writing. To build a 64-bit
|
||||
VxWorks mainline kernel that can be booted by the ``sifive_u`` machine, simply
|
||||
create a VxWorks source build project based on the sifive_generic BSP, and a
|
||||
VxWorks image project to generate the bootable VxWorks image, by following the
|
||||
BSP documentation instructions.
|
||||
|
||||
A pre-built 64-bit VxWorks 7 image for HiFive Unleashed board is available as
|
||||
part of the VxWorks SDK for testing as well. Instructions to download the SDK:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ wget https://labs.windriver.com/downloads/wrsdk-vxworks7-sifive-hifive-1.01.tar.bz2
|
||||
$ tar xvf wrsdk-vxworks7-sifive-hifive-1.01.tar.bz2
|
||||
$ ls bsps/sifive_generic_1_0_0_0/uboot/uVxWorks
|
||||
|
||||
To boot the VxWorks kernel in QEMU with the ``sifive_u`` machine, use:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ qemu-system-riscv64 -M sifive_u -smp 5 -m 2G \
|
||||
-display none -serial stdio \
|
||||
-nic tap,ifname=tap0,script=no,downscript=no \
|
||||
-kernel /path/to/vxWorks \
|
||||
-append "gem(0,0)host:vxWorks h=192.168.200.1 e=192.168.200.2:ffffff00 u=target pw=vxTarget f=0x01"
|
||||
|
||||
It is also possible to test 32-bit VxWorks on the ``sifive_u`` machine. Create
|
||||
a 32-bit project to build the 32-bit VxWorks image, and use exact the same
|
||||
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 bootloader that can be booted by the ``sifive_u`` machine, use
|
||||
the sifive_fu540_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
|
||||
|
||||
You will get spl/u-boot-spl.bin and u-boot.itb file in the build tree.
|
||||
|
||||
To start U-Boot using the ``sifive_u`` machine, prepare an SPI flash image, or
|
||||
SD card image that is properly partitioned and populated with correct contents.
|
||||
genimage_ can be used to generate these images.
|
||||
|
||||
A sample configuration file for a 128 MiB SD card image is:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ cat genimage_sdcard.cfg
|
||||
image sdcard.img {
|
||||
size = 128M
|
||||
|
||||
hdimage {
|
||||
gpt = true
|
||||
}
|
||||
|
||||
partition u-boot-spl {
|
||||
image = "u-boot-spl.bin"
|
||||
offset = 17K
|
||||
partition-type-uuid = 5B193300-FC78-40CD-8002-E86C45580B47
|
||||
}
|
||||
|
||||
partition u-boot {
|
||||
image = "u-boot.itb"
|
||||
offset = 1041K
|
||||
partition-type-uuid = 2E54B353-1271-4842-806F-E436D6AF6985
|
||||
}
|
||||
}
|
||||
|
||||
SPI flash image has slightly different partition offsets, and the size has to
|
||||
be 32 MiB to match the ISSI 25WP256 flash on the real board:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ cat genimage_spi-nor.cfg
|
||||
image spi-nor.img {
|
||||
size = 32M
|
||||
|
||||
hdimage {
|
||||
gpt = true
|
||||
}
|
||||
|
||||
partition u-boot-spl {
|
||||
image = "u-boot-spl.bin"
|
||||
offset = 20K
|
||||
partition-type-uuid = 5B193300-FC78-40CD-8002-E86C45580B47
|
||||
}
|
||||
|
||||
partition u-boot {
|
||||
image = "u-boot.itb"
|
||||
offset = 1044K
|
||||
partition-type-uuid = 2E54B353-1271-4842-806F-E436D6AF6985
|
||||
}
|
||||
}
|
||||
|
||||
Assume U-Boot binaries are put in the same directory as the config file,
|
||||
we can generate the image by:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ genimage --config genimage_<boot_src>.cfg --inputpath .
|
||||
|
||||
Boot U-Boot from SD card, by specifying msel=11 and pass the SD card image
|
||||
to QEMU ``sifive_u`` machine:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ qemu-system-riscv64 -M sifive_u,msel=11 -smp 5 -m 8G \
|
||||
-display none -serial stdio \
|
||||
-bios /path/to/u-boot-spl.bin \
|
||||
-drive file=/path/to/sdcard.img,if=sd
|
||||
|
||||
Changing msel= value to 6, allows booting U-Boot from the SPI flash:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ qemu-system-riscv64 -M sifive_u,msel=6 -smp 5 -m 8G \
|
||||
-display none -serial stdio \
|
||||
-bios /path/to/u-boot-spl.bin \
|
||||
-drive file=/path/to/spi-nor.img,if=mtd
|
||||
|
||||
Note when testing U-Boot, QEMU automatically generated device tree blob is
|
||||
not used because U-Boot itself embeds device tree blobs for U-Boot SPL and
|
||||
U-Boot proper. Hence the number of cores and size of memory have to match
|
||||
the real hardware, ie: 5 cores (-smp 5) and 8 GiB memory (-m 8G).
|
||||
|
||||
Above use case is to run upstream U-Boot for the SiFive HiFive Unleashed
|
||||
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:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ make menuconfig
|
||||
|
||||
then manually select the following configuration in U-Boot:
|
||||
|
||||
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:
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
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.
|
||||
|
||||
Boot the 64-bit U-Boot S-mode image directly:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ qemu-system-riscv64 -M sifive_u -smp 5 -m 2G \
|
||||
-display none -serial stdio \
|
||||
-kernel /path/to/u-boot.bin
|
||||
|
||||
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 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
|
||||
|
||||
Use the same command line options to boot the 32-bit U-Boot S-mode image:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ qemu-system-riscv32 -M sifive_u -smp 5 -m 2G \
|
||||
-display none -serial stdio \
|
||||
-kernel /path/to/u-boot.bin
|
||||
|
||||
.. _genimage: https://github.com/pengutronix/genimage
|
72
docs/system/target-riscv.rst
Normal file
72
docs/system/target-riscv.rst
Normal file
@ -0,0 +1,72 @@
|
||||
.. _RISC-V-System-emulator:
|
||||
|
||||
RISC-V System emulator
|
||||
======================
|
||||
|
||||
QEMU can emulate both 32-bit and 64-bit RISC-V CPUs. Use the
|
||||
``qemu-system-riscv64`` executable to simulate a 64-bit RISC-V machine,
|
||||
``qemu-system-riscv32`` executable to simulate a 32-bit RISC-V machine.
|
||||
|
||||
QEMU has generally good support for RISC-V guests. It has support for
|
||||
several different machines. The reason we support so many is that
|
||||
RISC-V hardware is much more widely varying than x86 hardware. RISC-V
|
||||
CPUs are generally built into "system-on-chip" (SoC) designs created by
|
||||
many different companies with different devices, and these SoCs are
|
||||
then built into machines which can vary still further even if they use
|
||||
the same SoC.
|
||||
|
||||
For most boards the CPU type is fixed (matching what the hardware has),
|
||||
so typically you don't need to specify the CPU type by hand, except for
|
||||
special cases like the ``virt`` board.
|
||||
|
||||
Choosing a board model
|
||||
----------------------
|
||||
|
||||
For QEMU's RISC-V system emulation, you must specify which board
|
||||
model you want to use with the ``-M`` or ``--machine`` option;
|
||||
there is no default.
|
||||
|
||||
Because RISC-V systems differ so much and in fundamental ways, typically
|
||||
operating system or firmware images intended to run on one machine
|
||||
will not run at all on any other. This is often surprising for new
|
||||
users who are used to the x86 world where every system looks like a
|
||||
standard PC. (Once the kernel has booted, most user space software
|
||||
cares much less about the detail of the hardware.)
|
||||
|
||||
If you already have a system image or a kernel that works on hardware
|
||||
and you want to boot with QEMU, check whether QEMU lists that machine
|
||||
in its ``-machine help`` output. If it is listed, then you can probably
|
||||
use that board model. If it is not listed, then unfortunately your image
|
||||
will almost certainly not boot on QEMU. (You might be able to
|
||||
extract the file system and use that with a different kernel which
|
||||
boots on a system that QEMU does emulate.)
|
||||
|
||||
If you don't care about reproducing the idiosyncrasies of a particular
|
||||
bit of hardware, such as small amount of RAM, no PCI or other hard
|
||||
disk, etc., and just want to run Linux, the best option is to use the
|
||||
``virt`` board. This is a platform which doesn't correspond to any
|
||||
real hardware and is designed for use in virtual machines. You'll
|
||||
need to compile Linux with a suitable configuration for running on
|
||||
the ``virt`` board. ``virt`` supports PCI, virtio, recent CPUs and
|
||||
large amounts of RAM. It also supports 64-bit CPUs.
|
||||
|
||||
Board-specific documentation
|
||||
----------------------------
|
||||
|
||||
Unfortunately many of the RISC-V boards QEMU supports are currently
|
||||
undocumented; you can get a complete list by running
|
||||
``qemu-system-riscv64 --machine help``, or
|
||||
``qemu-system-riscv32 --machine help``.
|
||||
|
||||
..
|
||||
This table of contents should be kept sorted alphabetically
|
||||
by the title text of each file, which isn't the same ordering
|
||||
as an alphabetical sort by filename.
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
riscv/sifive_u
|
||||
|
||||
RISC-V CPU features
|
||||
-------------------
|
@ -7,16 +7,22 @@ various targets are mentioned in the following sections.
|
||||
|
||||
Contents:
|
||||
|
||||
..
|
||||
This table of contents should be kept sorted alphabetically
|
||||
by the title text of each file, which isn't the same ordering
|
||||
as an alphabetical sort by filename.
|
||||
|
||||
.. toctree::
|
||||
|
||||
target-i386
|
||||
target-arm
|
||||
target-avr
|
||||
target-m68k
|
||||
target-mips
|
||||
target-ppc
|
||||
target-riscv
|
||||
target-rx
|
||||
target-s390x
|
||||
target-sparc
|
||||
target-sparc64
|
||||
target-mips
|
||||
target-arm
|
||||
target-m68k
|
||||
target-i386
|
||||
target-xtensa
|
||||
target-s390x
|
||||
target-rx
|
||||
target-avr
|
||||
|
@ -210,6 +210,19 @@ static const FlashPartInfo known_devices[] = {
|
||||
{ INFO("640s33b", 0x898913, 0, 64 << 10, 128, 0) },
|
||||
{ INFO("n25q064", 0x20ba17, 0, 64 << 10, 128, 0) },
|
||||
|
||||
/* ISSI */
|
||||
{ INFO("is25lq040b", 0x9d4013, 0, 64 << 10, 8, ER_4K) },
|
||||
{ INFO("is25lp080d", 0x9d6014, 0, 64 << 10, 16, ER_4K) },
|
||||
{ INFO("is25lp016d", 0x9d6015, 0, 64 << 10, 32, ER_4K) },
|
||||
{ INFO("is25lp032", 0x9d6016, 0, 64 << 10, 64, ER_4K) },
|
||||
{ INFO("is25lp064", 0x9d6017, 0, 64 << 10, 128, ER_4K) },
|
||||
{ INFO("is25lp128", 0x9d6018, 0, 64 << 10, 256, ER_4K) },
|
||||
{ INFO("is25lp256", 0x9d6019, 0, 64 << 10, 512, ER_4K) },
|
||||
{ INFO("is25wp032", 0x9d7016, 0, 64 << 10, 64, ER_4K) },
|
||||
{ INFO("is25wp064", 0x9d7017, 0, 64 << 10, 128, ER_4K) },
|
||||
{ INFO("is25wp128", 0x9d7018, 0, 64 << 10, 256, ER_4K) },
|
||||
{ INFO("is25wp256", 0x9d7019, 0, 64 << 10, 512, ER_4K) },
|
||||
|
||||
/* Macronix */
|
||||
{ INFO("mx25l2005a", 0xc22012, 0, 64 << 10, 4, ER_4K) },
|
||||
{ INFO("mx25l4005a", 0xc22013, 0, 64 << 10, 8, ER_4K) },
|
||||
@ -412,6 +425,7 @@ typedef enum {
|
||||
MAN_NUMONYX,
|
||||
MAN_WINBOND,
|
||||
MAN_SST,
|
||||
MAN_ISSI,
|
||||
MAN_GENERIC,
|
||||
} Manufacturer;
|
||||
|
||||
@ -487,6 +501,8 @@ static inline Manufacturer get_man(Flash *s)
|
||||
return MAN_MACRONIX;
|
||||
case 0xBF:
|
||||
return MAN_SST;
|
||||
case 0x9D:
|
||||
return MAN_ISSI;
|
||||
default:
|
||||
return MAN_GENERIC;
|
||||
}
|
||||
@ -706,6 +722,9 @@ static void complete_collecting_data(Flash *s)
|
||||
case MAN_SPANSION:
|
||||
s->quad_enable = !!(s->data[1] & 0x02);
|
||||
break;
|
||||
case MAN_ISSI:
|
||||
s->quad_enable = extract32(s->data[0], 6, 1);
|
||||
break;
|
||||
case MAN_MACRONIX:
|
||||
s->quad_enable = extract32(s->data[0], 6, 1);
|
||||
if (s->len > 1) {
|
||||
@ -895,6 +914,19 @@ static void decode_fast_read_cmd(Flash *s)
|
||||
SPANSION_DUMMY_CLK_LEN
|
||||
);
|
||||
break;
|
||||
case MAN_ISSI:
|
||||
/*
|
||||
* The Fast Read instruction code is followed by address bytes and
|
||||
* dummy cycles, transmitted via the SI line.
|
||||
*
|
||||
* The number of dummy cycles is configurable but this is currently
|
||||
* unmodeled, hence the default value 8 is used.
|
||||
*
|
||||
* QPI (Quad Peripheral Interface) mode has different default value
|
||||
* of dummy cycles, but this is unsupported at the time being.
|
||||
*/
|
||||
s->needed_bytes += 1;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@ -934,6 +966,16 @@ static void decode_dio_read_cmd(Flash *s)
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case MAN_ISSI:
|
||||
/*
|
||||
* The Fast Read Dual I/O instruction code is followed by address bytes
|
||||
* and dummy cycles, transmitted via the IO1 and IO0 line.
|
||||
*
|
||||
* The number of dummy cycles is configurable but this is currently
|
||||
* unmodeled, hence the default value 4 is used.
|
||||
*/
|
||||
s->needed_bytes += 1;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@ -974,6 +1016,19 @@ static void decode_qio_read_cmd(Flash *s)
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case MAN_ISSI:
|
||||
/*
|
||||
* The Fast Read Quad I/O instruction code is followed by address bytes
|
||||
* and dummy cycles, transmitted via the IO3, IO2, IO1 and IO0 line.
|
||||
*
|
||||
* The number of dummy cycles is configurable but this is currently
|
||||
* unmodeled, hence the default value 6 is used.
|
||||
*
|
||||
* QPI (Quad Peripheral Interface) mode has different default value
|
||||
* of dummy cycles, but this is unsupported at the time being.
|
||||
*/
|
||||
s->needed_bytes += 3;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@ -1132,7 +1187,7 @@ static void decode_new_cmd(Flash *s, uint32_t value)
|
||||
|
||||
case RDSR:
|
||||
s->data[0] = (!!s->write_enable) << 1;
|
||||
if (get_man(s) == MAN_MACRONIX) {
|
||||
if (get_man(s) == MAN_MACRONIX || get_man(s) == MAN_ISSI) {
|
||||
s->data[0] |= (!!s->quad_enable) << 6;
|
||||
}
|
||||
if (get_man(s) == MAN_SST) {
|
||||
|
@ -23,6 +23,7 @@
|
||||
#include "hw/qdev-properties.h"
|
||||
#include "hw/qdev-properties-system.h"
|
||||
#include "hw/sysbus.h"
|
||||
#include "qemu/error-report.h"
|
||||
#include "qemu/log.h"
|
||||
#include "qemu/module.h"
|
||||
#include "hw/misc/sifive_u_otp.h"
|
||||
@ -65,8 +66,7 @@ static uint64_t sifive_u_otp_read(void *opaque, hwaddr addr, unsigned int size)
|
||||
|
||||
if (blk_pread(s->blk, s->pa * SIFIVE_U_OTP_FUSE_WORD, &buf,
|
||||
SIFIVE_U_OTP_FUSE_WORD) < 0) {
|
||||
qemu_log_mask(LOG_GUEST_ERROR,
|
||||
"read error index<%d>\n", s->pa);
|
||||
error_report("read error index<%d>", s->pa);
|
||||
return 0xff;
|
||||
}
|
||||
|
||||
@ -169,8 +169,7 @@ static void sifive_u_otp_write(void *opaque, hwaddr addr,
|
||||
if (blk_pwrite(s->blk, s->pa * SIFIVE_U_OTP_FUSE_WORD,
|
||||
&s->fuse[s->pa], SIFIVE_U_OTP_FUSE_WORD,
|
||||
0) < 0) {
|
||||
qemu_log_mask(LOG_GUEST_ERROR,
|
||||
"write error index<%d>\n", s->pa);
|
||||
error_report("write error index<%d>", s->pa);
|
||||
}
|
||||
}
|
||||
|
||||
@ -260,15 +259,13 @@ static void sifive_u_otp_reset(DeviceState *dev)
|
||||
serial_data = s->serial;
|
||||
if (blk_pwrite(s->blk, index * SIFIVE_U_OTP_FUSE_WORD,
|
||||
&serial_data, SIFIVE_U_OTP_FUSE_WORD, 0) < 0) {
|
||||
qemu_log_mask(LOG_GUEST_ERROR,
|
||||
"write error index<%d>\n", index);
|
||||
error_report("write error index<%d>", index);
|
||||
}
|
||||
|
||||
serial_data = ~(s->serial);
|
||||
if (blk_pwrite(s->blk, (index + 1) * SIFIVE_U_OTP_FUSE_WORD,
|
||||
&serial_data, SIFIVE_U_OTP_FUSE_WORD, 0) < 0) {
|
||||
qemu_log_mask(LOG_GUEST_ERROR,
|
||||
"write error index<%d>\n", index + 1);
|
||||
error_report("write error index<%d>", index + 1);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -52,9 +52,12 @@ config SIFIVE_U
|
||||
select SIFIVE_GPIO
|
||||
select SIFIVE_PDMA
|
||||
select SIFIVE_PLIC
|
||||
select SIFIVE_SPI
|
||||
select SIFIVE_UART
|
||||
select SIFIVE_U_OTP
|
||||
select SIFIVE_U_PRCI
|
||||
select SSI_M25P80
|
||||
select SSI_SD
|
||||
select UNIMP
|
||||
|
||||
config SPIKE
|
||||
|
@ -86,10 +86,7 @@
|
||||
* - Register Map/PF_SoC_RegMap_V1_1/MPFS250T/mpfs250t_ioscb_memmap_dri.htm
|
||||
* describes the complete IOSCB modules memory maps
|
||||
*/
|
||||
static const struct MemmapEntry {
|
||||
hwaddr base;
|
||||
hwaddr size;
|
||||
} microchip_pfsoc_memmap[] = {
|
||||
static const MemMapEntry microchip_pfsoc_memmap[] = {
|
||||
[MICROCHIP_PFSOC_RSVD0] = { 0x0, 0x100 },
|
||||
[MICROCHIP_PFSOC_DEBUG] = { 0x100, 0xf00 },
|
||||
[MICROCHIP_PFSOC_E51_DTIM] = { 0x1000000, 0x2000 },
|
||||
@ -182,7 +179,7 @@ static void microchip_pfsoc_soc_realize(DeviceState *dev, Error **errp)
|
||||
{
|
||||
MachineState *ms = MACHINE(qdev_get_machine());
|
||||
MicrochipPFSoCState *s = MICROCHIP_PFSOC(dev);
|
||||
const struct MemmapEntry *memmap = microchip_pfsoc_memmap;
|
||||
const MemMapEntry *memmap = microchip_pfsoc_memmap;
|
||||
MemoryRegion *system_memory = get_system_memory();
|
||||
MemoryRegion *rsvd0_mem = g_new(MemoryRegion, 1);
|
||||
MemoryRegion *e51_dtim_mem = g_new(MemoryRegion, 1);
|
||||
@ -451,7 +448,7 @@ type_init(microchip_pfsoc_soc_register_types)
|
||||
static void microchip_icicle_kit_machine_init(MachineState *machine)
|
||||
{
|
||||
MachineClass *mc = MACHINE_GET_CLASS(machine);
|
||||
const struct MemmapEntry *memmap = microchip_pfsoc_memmap;
|
||||
const MemMapEntry *memmap = microchip_pfsoc_memmap;
|
||||
MicrochipIcicleKitState *s = MICROCHIP_ICICLE_KIT_MACHINE(machine);
|
||||
MemoryRegion *system_memory = get_system_memory();
|
||||
MemoryRegion *mem_low = g_new(MemoryRegion, 1);
|
||||
|
@ -28,10 +28,7 @@
|
||||
#include "qemu/units.h"
|
||||
#include "sysemu/sysemu.h"
|
||||
|
||||
static const struct MemmapEntry {
|
||||
hwaddr base;
|
||||
hwaddr size;
|
||||
} ibex_memmap[] = {
|
||||
static const MemMapEntry ibex_memmap[] = {
|
||||
[IBEX_DEV_ROM] = { 0x00008000, 16 * KiB },
|
||||
[IBEX_DEV_RAM] = { 0x10000000, 0x10000 },
|
||||
[IBEX_DEV_FLASH] = { 0x20000000, 0x80000 },
|
||||
@ -66,7 +63,7 @@ static const struct MemmapEntry {
|
||||
|
||||
static void opentitan_board_init(MachineState *machine)
|
||||
{
|
||||
const struct MemmapEntry *memmap = ibex_memmap;
|
||||
const MemMapEntry *memmap = ibex_memmap;
|
||||
OpenTitanState *s = g_new0(OpenTitanState, 1);
|
||||
MemoryRegion *sys_mem = get_system_memory();
|
||||
MemoryRegion *main_mem = g_new(MemoryRegion, 1);
|
||||
@ -114,7 +111,7 @@ static void lowrisc_ibex_soc_init(Object *obj)
|
||||
|
||||
static void lowrisc_ibex_soc_realize(DeviceState *dev_soc, Error **errp)
|
||||
{
|
||||
const struct MemmapEntry *memmap = ibex_memmap;
|
||||
const MemMapEntry *memmap = ibex_memmap;
|
||||
MachineState *ms = MACHINE(qdev_get_machine());
|
||||
LowRISCIbexSoCState *s = RISCV_IBEX_SOC(dev_soc);
|
||||
MemoryRegion *sys_mem = get_system_memory();
|
||||
|
@ -50,10 +50,7 @@
|
||||
#include "sysemu/sysemu.h"
|
||||
#include "exec/address-spaces.h"
|
||||
|
||||
static const struct MemmapEntry {
|
||||
hwaddr base;
|
||||
hwaddr size;
|
||||
} sifive_e_memmap[] = {
|
||||
static MemMapEntry sifive_e_memmap[] = {
|
||||
[SIFIVE_E_DEV_DEBUG] = { 0x0, 0x1000 },
|
||||
[SIFIVE_E_DEV_MROM] = { 0x1000, 0x2000 },
|
||||
[SIFIVE_E_DEV_OTP] = { 0x20000, 0x2000 },
|
||||
@ -77,7 +74,7 @@ static const struct MemmapEntry {
|
||||
|
||||
static void sifive_e_machine_init(MachineState *machine)
|
||||
{
|
||||
const struct MemmapEntry *memmap = sifive_e_memmap;
|
||||
const MemMapEntry *memmap = sifive_e_memmap;
|
||||
|
||||
SiFiveEState *s = RISCV_E_MACHINE(machine);
|
||||
MemoryRegion *sys_mem = get_system_memory();
|
||||
@ -187,7 +184,7 @@ static void sifive_e_soc_init(Object *obj)
|
||||
static void sifive_e_soc_realize(DeviceState *dev, Error **errp)
|
||||
{
|
||||
MachineState *ms = MACHINE(qdev_get_machine());
|
||||
const struct MemmapEntry *memmap = sifive_e_memmap;
|
||||
const MemMapEntry *memmap = sifive_e_memmap;
|
||||
SiFiveESoCState *s = RISCV_E_SOC(dev);
|
||||
MemoryRegion *sys_mem = get_system_memory();
|
||||
|
||||
|
@ -15,6 +15,8 @@
|
||||
* 5) OTP (One-Time Programmable) memory with stored serial number
|
||||
* 6) GEM (Gigabit Ethernet Controller) and management block
|
||||
* 7) DMA (Direct Memory Access Controller)
|
||||
* 8) SPI0 connected to an SPI flash
|
||||
* 9) SPI2 connected to an SD card
|
||||
*
|
||||
* This board currently generates devicetree dynamically that indicates at least
|
||||
* two harts and up to five harts.
|
||||
@ -44,6 +46,7 @@
|
||||
#include "hw/char/serial.h"
|
||||
#include "hw/cpu/cluster.h"
|
||||
#include "hw/misc/unimp.h"
|
||||
#include "hw/ssi/ssi.h"
|
||||
#include "target/riscv/cpu.h"
|
||||
#include "hw/riscv/riscv_hart.h"
|
||||
#include "hw/riscv/sifive_u.h"
|
||||
@ -60,10 +63,7 @@
|
||||
|
||||
#include <libfdt.h>
|
||||
|
||||
static const struct MemmapEntry {
|
||||
hwaddr base;
|
||||
hwaddr size;
|
||||
} sifive_u_memmap[] = {
|
||||
static const MemMapEntry sifive_u_memmap[] = {
|
||||
[SIFIVE_U_DEV_DEBUG] = { 0x0, 0x100 },
|
||||
[SIFIVE_U_DEV_MROM] = { 0x1000, 0xf000 },
|
||||
[SIFIVE_U_DEV_CLINT] = { 0x2000000, 0x10000 },
|
||||
@ -74,6 +74,8 @@ static const struct MemmapEntry {
|
||||
[SIFIVE_U_DEV_PRCI] = { 0x10000000, 0x1000 },
|
||||
[SIFIVE_U_DEV_UART0] = { 0x10010000, 0x1000 },
|
||||
[SIFIVE_U_DEV_UART1] = { 0x10011000, 0x1000 },
|
||||
[SIFIVE_U_DEV_QSPI0] = { 0x10040000, 0x1000 },
|
||||
[SIFIVE_U_DEV_QSPI2] = { 0x10050000, 0x1000 },
|
||||
[SIFIVE_U_DEV_GPIO] = { 0x10060000, 0x1000 },
|
||||
[SIFIVE_U_DEV_OTP] = { 0x10070000, 0x1000 },
|
||||
[SIFIVE_U_DEV_GEM] = { 0x10090000, 0x2000 },
|
||||
@ -86,7 +88,7 @@ static const struct MemmapEntry {
|
||||
#define OTP_SERIAL 1
|
||||
#define GEM_REVISION 0x10070109
|
||||
|
||||
static void create_fdt(SiFiveUState *s, const struct MemmapEntry *memmap,
|
||||
static void create_fdt(SiFiveUState *s, const MemMapEntry *memmap,
|
||||
uint64_t mem_size, const char *cmdline, bool is_32_bit)
|
||||
{
|
||||
MachineState *ms = MACHINE(qdev_get_machine());
|
||||
@ -342,6 +344,57 @@ static void create_fdt(SiFiveUState *s, const struct MemmapEntry *memmap,
|
||||
"sifive,fu540-c000-ccache");
|
||||
g_free(nodename);
|
||||
|
||||
nodename = g_strdup_printf("/soc/spi@%lx",
|
||||
(long)memmap[SIFIVE_U_DEV_QSPI2].base);
|
||||
qemu_fdt_add_subnode(fdt, nodename);
|
||||
qemu_fdt_setprop_cell(fdt, nodename, "#size-cells", 0);
|
||||
qemu_fdt_setprop_cell(fdt, nodename, "#address-cells", 1);
|
||||
qemu_fdt_setprop_cells(fdt, nodename, "clocks",
|
||||
prci_phandle, PRCI_CLK_TLCLK);
|
||||
qemu_fdt_setprop_cell(fdt, nodename, "interrupts", SIFIVE_U_QSPI2_IRQ);
|
||||
qemu_fdt_setprop_cell(fdt, nodename, "interrupt-parent", plic_phandle);
|
||||
qemu_fdt_setprop_cells(fdt, nodename, "reg",
|
||||
0x0, memmap[SIFIVE_U_DEV_QSPI2].base,
|
||||
0x0, memmap[SIFIVE_U_DEV_QSPI2].size);
|
||||
qemu_fdt_setprop_string(fdt, nodename, "compatible", "sifive,spi0");
|
||||
g_free(nodename);
|
||||
|
||||
nodename = g_strdup_printf("/soc/spi@%lx/mmc@0",
|
||||
(long)memmap[SIFIVE_U_DEV_QSPI2].base);
|
||||
qemu_fdt_add_subnode(fdt, nodename);
|
||||
qemu_fdt_setprop(fdt, nodename, "disable-wp", NULL, 0);
|
||||
qemu_fdt_setprop_cells(fdt, nodename, "voltage-ranges", 3300, 3300);
|
||||
qemu_fdt_setprop_cell(fdt, nodename, "spi-max-frequency", 20000000);
|
||||
qemu_fdt_setprop_cell(fdt, nodename, "reg", 0);
|
||||
qemu_fdt_setprop_string(fdt, nodename, "compatible", "mmc-spi-slot");
|
||||
g_free(nodename);
|
||||
|
||||
nodename = g_strdup_printf("/soc/spi@%lx",
|
||||
(long)memmap[SIFIVE_U_DEV_QSPI0].base);
|
||||
qemu_fdt_add_subnode(fdt, nodename);
|
||||
qemu_fdt_setprop_cell(fdt, nodename, "#size-cells", 0);
|
||||
qemu_fdt_setprop_cell(fdt, nodename, "#address-cells", 1);
|
||||
qemu_fdt_setprop_cells(fdt, nodename, "clocks",
|
||||
prci_phandle, PRCI_CLK_TLCLK);
|
||||
qemu_fdt_setprop_cell(fdt, nodename, "interrupts", SIFIVE_U_QSPI0_IRQ);
|
||||
qemu_fdt_setprop_cell(fdt, nodename, "interrupt-parent", plic_phandle);
|
||||
qemu_fdt_setprop_cells(fdt, nodename, "reg",
|
||||
0x0, memmap[SIFIVE_U_DEV_QSPI0].base,
|
||||
0x0, memmap[SIFIVE_U_DEV_QSPI0].size);
|
||||
qemu_fdt_setprop_string(fdt, nodename, "compatible", "sifive,spi0");
|
||||
g_free(nodename);
|
||||
|
||||
nodename = g_strdup_printf("/soc/spi@%lx/flash@0",
|
||||
(long)memmap[SIFIVE_U_DEV_QSPI0].base);
|
||||
qemu_fdt_add_subnode(fdt, nodename);
|
||||
qemu_fdt_setprop_cell(fdt, nodename, "spi-rx-bus-width", 4);
|
||||
qemu_fdt_setprop_cell(fdt, nodename, "spi-tx-bus-width", 4);
|
||||
qemu_fdt_setprop(fdt, nodename, "m25p,fast-read", NULL, 0);
|
||||
qemu_fdt_setprop_cell(fdt, nodename, "spi-max-frequency", 50000000);
|
||||
qemu_fdt_setprop_cell(fdt, nodename, "reg", 0);
|
||||
qemu_fdt_setprop_string(fdt, nodename, "compatible", "jedec,spi-nor");
|
||||
g_free(nodename);
|
||||
|
||||
phy_phandle = phandle++;
|
||||
nodename = g_strdup_printf("/soc/ethernet@%lx",
|
||||
(long)memmap[SIFIVE_U_DEV_GEM].base);
|
||||
@ -428,7 +481,7 @@ static void sifive_u_machine_reset(void *opaque, int n, int level)
|
||||
|
||||
static void sifive_u_machine_init(MachineState *machine)
|
||||
{
|
||||
const struct MemmapEntry *memmap = sifive_u_memmap;
|
||||
const MemMapEntry *memmap = sifive_u_memmap;
|
||||
SiFiveUState *s = RISCV_U_MACHINE(machine);
|
||||
MemoryRegion *system_memory = get_system_memory();
|
||||
MemoryRegion *main_mem = g_new(MemoryRegion, 1);
|
||||
@ -439,6 +492,9 @@ static void sifive_u_machine_init(MachineState *machine)
|
||||
int i;
|
||||
uint32_t fdt_load_addr;
|
||||
uint64_t kernel_entry;
|
||||
DriveInfo *dinfo;
|
||||
DeviceState *flash_dev, *sd_dev;
|
||||
qemu_irq flash_cs, sd_cs;
|
||||
|
||||
/* Initialize SoC */
|
||||
object_initialize_child(OBJECT(machine), "soc", &s->soc, TYPE_RISCV_U_SOC);
|
||||
@ -571,6 +627,25 @@ static void sifive_u_machine_init(MachineState *machine)
|
||||
riscv_rom_copy_firmware_info(machine, memmap[SIFIVE_U_DEV_MROM].base,
|
||||
memmap[SIFIVE_U_DEV_MROM].size,
|
||||
sizeof(reset_vec), kernel_entry);
|
||||
|
||||
/* Connect an SPI flash to SPI0 */
|
||||
flash_dev = qdev_new("is25wp256");
|
||||
dinfo = drive_get_next(IF_MTD);
|
||||
if (dinfo) {
|
||||
qdev_prop_set_drive_err(flash_dev, "drive",
|
||||
blk_by_legacy_dinfo(dinfo),
|
||||
&error_fatal);
|
||||
}
|
||||
qdev_realize_and_unref(flash_dev, BUS(s->soc.spi0.spi), &error_fatal);
|
||||
|
||||
flash_cs = qdev_get_gpio_in_named(flash_dev, SSI_GPIO_CS, 0);
|
||||
sysbus_connect_irq(SYS_BUS_DEVICE(&s->soc.spi0), 1, flash_cs);
|
||||
|
||||
/* Connect an SD card to SPI2 */
|
||||
sd_dev = ssi_create_peripheral(s->soc.spi2.spi, "ssi-sd");
|
||||
|
||||
sd_cs = qdev_get_gpio_in_named(sd_dev, SSI_GPIO_CS, 0);
|
||||
sysbus_connect_irq(SYS_BUS_DEVICE(&s->soc.spi2), 1, sd_cs);
|
||||
}
|
||||
|
||||
static bool sifive_u_machine_get_start_in_flash(Object *obj, Error **errp)
|
||||
@ -680,13 +755,15 @@ static void sifive_u_soc_instance_init(Object *obj)
|
||||
object_initialize_child(obj, "gem", &s->gem, TYPE_CADENCE_GEM);
|
||||
object_initialize_child(obj, "gpio", &s->gpio, TYPE_SIFIVE_GPIO);
|
||||
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);
|
||||
}
|
||||
|
||||
static void sifive_u_soc_realize(DeviceState *dev, Error **errp)
|
||||
{
|
||||
MachineState *ms = MACHINE(qdev_get_machine());
|
||||
SiFiveUSoCState *s = RISCV_U_SOC(dev);
|
||||
const struct MemmapEntry *memmap = sifive_u_memmap;
|
||||
const MemMapEntry *memmap = sifive_u_memmap;
|
||||
MemoryRegion *system_memory = get_system_memory();
|
||||
MemoryRegion *mask_rom = g_new(MemoryRegion, 1);
|
||||
MemoryRegion *l2lim_mem = g_new(MemoryRegion, 1);
|
||||
@ -827,6 +904,17 @@ static void sifive_u_soc_realize(DeviceState *dev, Error **errp)
|
||||
|
||||
create_unimplemented_device("riscv.sifive.u.l2cc",
|
||||
memmap[SIFIVE_U_DEV_L2CC].base, memmap[SIFIVE_U_DEV_L2CC].size);
|
||||
|
||||
sysbus_realize(SYS_BUS_DEVICE(&s->spi0), errp);
|
||||
sysbus_mmio_map(SYS_BUS_DEVICE(&s->spi0), 0,
|
||||
memmap[SIFIVE_U_DEV_QSPI0].base);
|
||||
sysbus_connect_irq(SYS_BUS_DEVICE(&s->spi0), 0,
|
||||
qdev_get_gpio_in(DEVICE(s->plic), SIFIVE_U_QSPI0_IRQ));
|
||||
sysbus_realize(SYS_BUS_DEVICE(&s->spi2), errp);
|
||||
sysbus_mmio_map(SYS_BUS_DEVICE(&s->spi2), 0,
|
||||
memmap[SIFIVE_U_DEV_QSPI2].base);
|
||||
sysbus_connect_irq(SYS_BUS_DEVICE(&s->spi2), 0,
|
||||
qdev_get_gpio_in(DEVICE(s->plic), SIFIVE_U_QSPI2_IRQ));
|
||||
}
|
||||
|
||||
static Property sifive_u_soc_props[] = {
|
||||
|
@ -43,16 +43,13 @@
|
||||
#include "sysemu/qtest.h"
|
||||
#include "sysemu/sysemu.h"
|
||||
|
||||
static const struct MemmapEntry {
|
||||
hwaddr base;
|
||||
hwaddr size;
|
||||
} spike_memmap[] = {
|
||||
static const MemMapEntry spike_memmap[] = {
|
||||
[SPIKE_MROM] = { 0x1000, 0xf000 },
|
||||
[SPIKE_CLINT] = { 0x2000000, 0x10000 },
|
||||
[SPIKE_DRAM] = { 0x80000000, 0x0 },
|
||||
};
|
||||
|
||||
static void create_fdt(SpikeState *s, const struct MemmapEntry *memmap,
|
||||
static void create_fdt(SpikeState *s, const MemMapEntry *memmap,
|
||||
uint64_t mem_size, const char *cmdline, bool is_32_bit)
|
||||
{
|
||||
void *fdt;
|
||||
@ -179,7 +176,7 @@ static void create_fdt(SpikeState *s, const struct MemmapEntry *memmap,
|
||||
|
||||
static void spike_board_init(MachineState *machine)
|
||||
{
|
||||
const struct MemmapEntry *memmap = spike_memmap;
|
||||
const MemMapEntry *memmap = spike_memmap;
|
||||
SpikeState *s = SPIKE_MACHINE(machine);
|
||||
MemoryRegion *system_memory = get_system_memory();
|
||||
MemoryRegion *main_mem = g_new(MemoryRegion, 1);
|
||||
|
@ -43,10 +43,7 @@
|
||||
#include "hw/pci/pci.h"
|
||||
#include "hw/pci-host/gpex.h"
|
||||
|
||||
static const struct MemmapEntry {
|
||||
hwaddr base;
|
||||
hwaddr size;
|
||||
} virt_memmap[] = {
|
||||
static const MemMapEntry virt_memmap[] = {
|
||||
[VIRT_DEBUG] = { 0x0, 0x100 },
|
||||
[VIRT_MROM] = { 0x1000, 0xf000 },
|
||||
[VIRT_TEST] = { 0x100000, 0x1000 },
|
||||
@ -62,6 +59,15 @@ static const struct MemmapEntry {
|
||||
[VIRT_DRAM] = { 0x80000000, 0x0 },
|
||||
};
|
||||
|
||||
/* PCIe high mmio is fixed for RV32 */
|
||||
#define VIRT32_HIGH_PCIE_MMIO_BASE 0x300000000ULL
|
||||
#define VIRT32_HIGH_PCIE_MMIO_SIZE (4 * GiB)
|
||||
|
||||
/* PCIe high mmio for RV64, size is fixed but base depends on top of RAM */
|
||||
#define VIRT64_HIGH_PCIE_MMIO_SIZE (16 * GiB)
|
||||
|
||||
static MemMapEntry virt_high_pcie_memmap;
|
||||
|
||||
#define VIRT_FLASH_SECTOR_SIZE (256 * KiB)
|
||||
|
||||
static PFlashCFI01 *virt_flash_create1(RISCVVirtState *s,
|
||||
@ -170,7 +176,7 @@ static void create_pcie_irq_map(void *fdt, char *nodename,
|
||||
0x1800, 0, 0, 0x7);
|
||||
}
|
||||
|
||||
static void create_fdt(RISCVVirtState *s, const struct MemmapEntry *memmap,
|
||||
static void create_fdt(RISCVVirtState *s, const MemMapEntry *memmap,
|
||||
uint64_t mem_size, const char *cmdline, bool is_32_bit)
|
||||
{
|
||||
void *fdt;
|
||||
@ -374,7 +380,11 @@ static void create_fdt(RISCVVirtState *s, const struct MemmapEntry *memmap,
|
||||
2, memmap[VIRT_PCIE_PIO].base, 2, memmap[VIRT_PCIE_PIO].size,
|
||||
1, FDT_PCI_RANGE_MMIO,
|
||||
2, memmap[VIRT_PCIE_MMIO].base,
|
||||
2, memmap[VIRT_PCIE_MMIO].base, 2, memmap[VIRT_PCIE_MMIO].size);
|
||||
2, memmap[VIRT_PCIE_MMIO].base, 2, memmap[VIRT_PCIE_MMIO].size,
|
||||
1, FDT_PCI_RANGE_MMIO_64BIT,
|
||||
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);
|
||||
g_free(name);
|
||||
|
||||
@ -451,12 +461,14 @@ update_bootargs:
|
||||
static inline DeviceState *gpex_pcie_init(MemoryRegion *sys_mem,
|
||||
hwaddr ecam_base, hwaddr ecam_size,
|
||||
hwaddr mmio_base, hwaddr mmio_size,
|
||||
hwaddr high_mmio_base,
|
||||
hwaddr high_mmio_size,
|
||||
hwaddr pio_base,
|
||||
DeviceState *plic, bool link_up)
|
||||
DeviceState *plic)
|
||||
{
|
||||
DeviceState *dev;
|
||||
MemoryRegion *ecam_alias, *ecam_reg;
|
||||
MemoryRegion *mmio_alias, *mmio_reg;
|
||||
MemoryRegion *mmio_alias, *high_mmio_alias, *mmio_reg;
|
||||
qemu_irq irq;
|
||||
int i;
|
||||
|
||||
@ -476,6 +488,13 @@ static inline DeviceState *gpex_pcie_init(MemoryRegion *sys_mem,
|
||||
mmio_reg, mmio_base, mmio_size);
|
||||
memory_region_add_subregion(get_system_memory(), mmio_base, mmio_alias);
|
||||
|
||||
/* Map high MMIO space */
|
||||
high_mmio_alias = g_new0(MemoryRegion, 1);
|
||||
memory_region_init_alias(high_mmio_alias, OBJECT(dev), "pcie-mmio-high",
|
||||
mmio_reg, high_mmio_base, high_mmio_size);
|
||||
memory_region_add_subregion(get_system_memory(), high_mmio_base,
|
||||
high_mmio_alias);
|
||||
|
||||
sysbus_mmio_map(SYS_BUS_DEVICE(dev), 2, pio_base);
|
||||
|
||||
for (i = 0; i < GPEX_NUM_IRQS; i++) {
|
||||
@ -490,7 +509,7 @@ static inline DeviceState *gpex_pcie_init(MemoryRegion *sys_mem,
|
||||
|
||||
static void virt_machine_init(MachineState *machine)
|
||||
{
|
||||
const struct MemmapEntry *memmap = virt_memmap;
|
||||
const MemMapEntry *memmap = virt_memmap;
|
||||
RISCVVirtState *s = RISCV_VIRT_MACHINE(machine);
|
||||
MemoryRegion *system_memory = get_system_memory();
|
||||
MemoryRegion *main_mem = g_new(MemoryRegion, 1);
|
||||
@ -593,6 +612,23 @@ static void virt_machine_init(MachineState *machine)
|
||||
}
|
||||
}
|
||||
|
||||
if (riscv_is_32bit(&s->soc[0])) {
|
||||
#if HOST_LONG_BITS == 64
|
||||
/* limit RAM size in a 32-bit system */
|
||||
if (machine->ram_size > 10 * GiB) {
|
||||
machine->ram_size = 10 * GiB;
|
||||
error_report("Limiting RAM size to 10 GiB");
|
||||
}
|
||||
#endif
|
||||
virt_high_pcie_memmap.base = VIRT32_HIGH_PCIE_MMIO_BASE;
|
||||
virt_high_pcie_memmap.size = VIRT32_HIGH_PCIE_MMIO_SIZE;
|
||||
} else {
|
||||
virt_high_pcie_memmap.size = VIRT64_HIGH_PCIE_MMIO_SIZE;
|
||||
virt_high_pcie_memmap.base = memmap[VIRT_DRAM].base + machine->ram_size;
|
||||
virt_high_pcie_memmap.base =
|
||||
ROUND_UP(virt_high_pcie_memmap.base, virt_high_pcie_memmap.size);
|
||||
}
|
||||
|
||||
/* register system main memory (actual RAM) */
|
||||
memory_region_init_ram(main_mem, NULL, "riscv_virt_board.ram",
|
||||
machine->ram_size, &error_fatal);
|
||||
@ -672,12 +708,14 @@ static void virt_machine_init(MachineState *machine)
|
||||
}
|
||||
|
||||
gpex_pcie_init(system_memory,
|
||||
memmap[VIRT_PCIE_ECAM].base,
|
||||
memmap[VIRT_PCIE_ECAM].size,
|
||||
memmap[VIRT_PCIE_MMIO].base,
|
||||
memmap[VIRT_PCIE_MMIO].size,
|
||||
memmap[VIRT_PCIE_PIO].base,
|
||||
DEVICE(pcie_plic), true);
|
||||
memmap[VIRT_PCIE_ECAM].base,
|
||||
memmap[VIRT_PCIE_ECAM].size,
|
||||
memmap[VIRT_PCIE_MMIO].base,
|
||||
memmap[VIRT_PCIE_MMIO].size,
|
||||
virt_high_pcie_memmap.base,
|
||||
virt_high_pcie_memmap.size,
|
||||
memmap[VIRT_PCIE_PIO].base,
|
||||
DEVICE(pcie_plic));
|
||||
|
||||
serial_mm_init(system_memory, memmap[VIRT_UART0].base,
|
||||
0, qdev_get_gpio_in(DEVICE(mmio_plic), UART0_IRQ), 399193,
|
||||
|
@ -211,6 +211,8 @@ static int goldfish_rtc_post_load(void *opaque, int version_id)
|
||||
qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
|
||||
s->tick_offset = s->tick_offset_vmstate - delta;
|
||||
|
||||
goldfish_rtc_set_alarm(s);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -2,6 +2,10 @@ config PL022
|
||||
bool
|
||||
select SSI
|
||||
|
||||
config SIFIVE_SPI
|
||||
bool
|
||||
select SSI
|
||||
|
||||
config SSI
|
||||
bool
|
||||
|
||||
|
@ -2,6 +2,7 @@ softmmu_ss.add(when: 'CONFIG_ASPEED_SOC', if_true: files('aspeed_smc.c'))
|
||||
softmmu_ss.add(when: 'CONFIG_MSF2', if_true: files('mss-spi.c'))
|
||||
softmmu_ss.add(when: 'CONFIG_NPCM7XX', if_true: files('npcm7xx_fiu.c'))
|
||||
softmmu_ss.add(when: 'CONFIG_PL022', if_true: files('pl022.c'))
|
||||
softmmu_ss.add(when: 'CONFIG_SIFIVE_SPI', if_true: files('sifive_spi.c'))
|
||||
softmmu_ss.add(when: 'CONFIG_SSI', if_true: files('ssi.c'))
|
||||
softmmu_ss.add(when: 'CONFIG_STM32F2XX_SPI', if_true: files('stm32f2xx_spi.c'))
|
||||
softmmu_ss.add(when: 'CONFIG_XILINX_SPI', if_true: files('xilinx_spi.c'))
|
||||
|
358
hw/ssi/sifive_spi.c
Normal file
358
hw/ssi/sifive_spi.c
Normal file
@ -0,0 +1,358 @@
|
||||
/*
|
||||
* QEMU model of the SiFive SPI Controller
|
||||
*
|
||||
* Copyright (c) 2021 Wind River Systems, Inc.
|
||||
*
|
||||
* Author:
|
||||
* Bin Meng <bin.meng@windriver.com>
|
||||
*
|
||||
* 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 "hw/irq.h"
|
||||
#include "hw/qdev-properties.h"
|
||||
#include "hw/sysbus.h"
|
||||
#include "hw/ssi/ssi.h"
|
||||
#include "sysemu/sysemu.h"
|
||||
#include "qemu/fifo8.h"
|
||||
#include "qemu/log.h"
|
||||
#include "qemu/module.h"
|
||||
#include "hw/ssi/sifive_spi.h"
|
||||
|
||||
#define R_SCKDIV (0x00 / 4)
|
||||
#define R_SCKMODE (0x04 / 4)
|
||||
#define R_CSID (0x10 / 4)
|
||||
#define R_CSDEF (0x14 / 4)
|
||||
#define R_CSMODE (0x18 / 4)
|
||||
#define R_DELAY0 (0x28 / 4)
|
||||
#define R_DELAY1 (0x2C / 4)
|
||||
#define R_FMT (0x40 / 4)
|
||||
#define R_TXDATA (0x48 / 4)
|
||||
#define R_RXDATA (0x4C / 4)
|
||||
#define R_TXMARK (0x50 / 4)
|
||||
#define R_RXMARK (0x54 / 4)
|
||||
#define R_FCTRL (0x60 / 4)
|
||||
#define R_FFMT (0x64 / 4)
|
||||
#define R_IE (0x70 / 4)
|
||||
#define R_IP (0x74 / 4)
|
||||
|
||||
#define FMT_DIR (1 << 3)
|
||||
|
||||
#define TXDATA_FULL (1 << 31)
|
||||
#define RXDATA_EMPTY (1 << 31)
|
||||
|
||||
#define IE_TXWM (1 << 0)
|
||||
#define IE_RXWM (1 << 1)
|
||||
|
||||
#define IP_TXWM (1 << 0)
|
||||
#define IP_RXWM (1 << 1)
|
||||
|
||||
#define FIFO_CAPACITY 8
|
||||
|
||||
static void sifive_spi_txfifo_reset(SiFiveSPIState *s)
|
||||
{
|
||||
fifo8_reset(&s->tx_fifo);
|
||||
|
||||
s->regs[R_TXDATA] &= ~TXDATA_FULL;
|
||||
s->regs[R_IP] &= ~IP_TXWM;
|
||||
}
|
||||
|
||||
static void sifive_spi_rxfifo_reset(SiFiveSPIState *s)
|
||||
{
|
||||
fifo8_reset(&s->rx_fifo);
|
||||
|
||||
s->regs[R_RXDATA] |= RXDATA_EMPTY;
|
||||
s->regs[R_IP] &= ~IP_RXWM;
|
||||
}
|
||||
|
||||
static void sifive_spi_update_cs(SiFiveSPIState *s)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < s->num_cs; i++) {
|
||||
if (s->regs[R_CSDEF] & (1 << i)) {
|
||||
qemu_set_irq(s->cs_lines[i], !(s->regs[R_CSMODE]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void sifive_spi_update_irq(SiFiveSPIState *s)
|
||||
{
|
||||
int level;
|
||||
|
||||
if (fifo8_num_used(&s->tx_fifo) < s->regs[R_TXMARK]) {
|
||||
s->regs[R_IP] |= IP_TXWM;
|
||||
} else {
|
||||
s->regs[R_IP] &= ~IP_TXWM;
|
||||
}
|
||||
|
||||
if (fifo8_num_used(&s->rx_fifo) > s->regs[R_RXMARK]) {
|
||||
s->regs[R_IP] |= IP_RXWM;
|
||||
} else {
|
||||
s->regs[R_IP] &= ~IP_RXWM;
|
||||
}
|
||||
|
||||
level = s->regs[R_IP] & s->regs[R_IE] ? 1 : 0;
|
||||
qemu_set_irq(s->irq, level);
|
||||
}
|
||||
|
||||
static void sifive_spi_reset(DeviceState *d)
|
||||
{
|
||||
SiFiveSPIState *s = SIFIVE_SPI(d);
|
||||
|
||||
memset(s->regs, 0, sizeof(s->regs));
|
||||
|
||||
/* The reset value is high for all implemented CS pins */
|
||||
s->regs[R_CSDEF] = (1 << s->num_cs) - 1;
|
||||
|
||||
/* Populate register with their default value */
|
||||
s->regs[R_SCKDIV] = 0x03;
|
||||
s->regs[R_DELAY0] = 0x1001;
|
||||
s->regs[R_DELAY1] = 0x01;
|
||||
|
||||
sifive_spi_txfifo_reset(s);
|
||||
sifive_spi_rxfifo_reset(s);
|
||||
|
||||
sifive_spi_update_cs(s);
|
||||
sifive_spi_update_irq(s);
|
||||
}
|
||||
|
||||
static void sifive_spi_flush_txfifo(SiFiveSPIState *s)
|
||||
{
|
||||
uint8_t tx;
|
||||
uint8_t rx;
|
||||
|
||||
while (!fifo8_is_empty(&s->tx_fifo)) {
|
||||
tx = fifo8_pop(&s->tx_fifo);
|
||||
rx = ssi_transfer(s->spi, tx);
|
||||
|
||||
if (!fifo8_is_full(&s->rx_fifo)) {
|
||||
if (!(s->regs[R_FMT] & FMT_DIR)) {
|
||||
fifo8_push(&s->rx_fifo, rx);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static bool sifive_spi_is_bad_reg(hwaddr addr, bool allow_reserved)
|
||||
{
|
||||
bool bad;
|
||||
|
||||
switch (addr) {
|
||||
/* reserved offsets */
|
||||
case 0x08:
|
||||
case 0x0C:
|
||||
case 0x1C:
|
||||
case 0x20:
|
||||
case 0x24:
|
||||
case 0x30:
|
||||
case 0x34:
|
||||
case 0x38:
|
||||
case 0x3C:
|
||||
case 0x44:
|
||||
case 0x58:
|
||||
case 0x5C:
|
||||
case 0x68:
|
||||
case 0x6C:
|
||||
bad = allow_reserved ? false : true;
|
||||
break;
|
||||
default:
|
||||
bad = false;
|
||||
}
|
||||
|
||||
if (addr >= (SIFIVE_SPI_REG_NUM << 2)) {
|
||||
bad = true;
|
||||
}
|
||||
|
||||
return bad;
|
||||
}
|
||||
|
||||
static uint64_t sifive_spi_read(void *opaque, hwaddr addr, unsigned int size)
|
||||
{
|
||||
SiFiveSPIState *s = opaque;
|
||||
uint32_t r;
|
||||
|
||||
if (sifive_spi_is_bad_reg(addr, true)) {
|
||||
qemu_log_mask(LOG_GUEST_ERROR, "%s: bad read at address 0x%"
|
||||
HWADDR_PRIx "\n", __func__, addr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
addr >>= 2;
|
||||
switch (addr) {
|
||||
case R_TXDATA:
|
||||
if (fifo8_is_full(&s->tx_fifo)) {
|
||||
return TXDATA_FULL;
|
||||
}
|
||||
r = 0;
|
||||
break;
|
||||
|
||||
case R_RXDATA:
|
||||
if (fifo8_is_empty(&s->rx_fifo)) {
|
||||
return RXDATA_EMPTY;
|
||||
}
|
||||
r = fifo8_pop(&s->rx_fifo);
|
||||
break;
|
||||
|
||||
default:
|
||||
r = s->regs[addr];
|
||||
break;
|
||||
}
|
||||
|
||||
sifive_spi_update_irq(s);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static void sifive_spi_write(void *opaque, hwaddr addr,
|
||||
uint64_t val64, unsigned int size)
|
||||
{
|
||||
SiFiveSPIState *s = opaque;
|
||||
uint32_t value = val64;
|
||||
|
||||
if (sifive_spi_is_bad_reg(addr, false)) {
|
||||
qemu_log_mask(LOG_GUEST_ERROR, "%s: bad write at addr=0x%"
|
||||
HWADDR_PRIx " value=0x%x\n", __func__, addr, value);
|
||||
return;
|
||||
}
|
||||
|
||||
addr >>= 2;
|
||||
switch (addr) {
|
||||
case R_CSID:
|
||||
if (value >= s->num_cs) {
|
||||
qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid csid %d\n",
|
||||
__func__, value);
|
||||
} else {
|
||||
s->regs[R_CSID] = value;
|
||||
sifive_spi_update_cs(s);
|
||||
}
|
||||
break;
|
||||
|
||||
case R_CSDEF:
|
||||
if (value >= (1 << s->num_cs)) {
|
||||
qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid csdef %x\n",
|
||||
__func__, value);
|
||||
} else {
|
||||
s->regs[R_CSDEF] = value;
|
||||
}
|
||||
break;
|
||||
|
||||
case R_CSMODE:
|
||||
if (value > 3) {
|
||||
qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid csmode %x\n",
|
||||
__func__, value);
|
||||
} else {
|
||||
s->regs[R_CSMODE] = value;
|
||||
sifive_spi_update_cs(s);
|
||||
}
|
||||
break;
|
||||
|
||||
case R_TXDATA:
|
||||
if (!fifo8_is_full(&s->tx_fifo)) {
|
||||
fifo8_push(&s->tx_fifo, (uint8_t)value);
|
||||
sifive_spi_flush_txfifo(s);
|
||||
}
|
||||
break;
|
||||
|
||||
case R_RXDATA:
|
||||
case R_IP:
|
||||
qemu_log_mask(LOG_GUEST_ERROR,
|
||||
"%s: invalid write to read-only reigster 0x%"
|
||||
HWADDR_PRIx " with 0x%x\n", __func__, addr << 2, value);
|
||||
break;
|
||||
|
||||
case R_TXMARK:
|
||||
case R_RXMARK:
|
||||
if (value >= FIFO_CAPACITY) {
|
||||
qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid watermark %d\n",
|
||||
__func__, value);
|
||||
} else {
|
||||
s->regs[addr] = value;
|
||||
}
|
||||
break;
|
||||
|
||||
case R_FCTRL:
|
||||
case R_FFMT:
|
||||
qemu_log_mask(LOG_UNIMP,
|
||||
"%s: direct-map flash interface unimplemented\n",
|
||||
__func__);
|
||||
break;
|
||||
|
||||
default:
|
||||
s->regs[addr] = value;
|
||||
break;
|
||||
}
|
||||
|
||||
sifive_spi_update_irq(s);
|
||||
}
|
||||
|
||||
static const MemoryRegionOps sifive_spi_ops = {
|
||||
.read = sifive_spi_read,
|
||||
.write = sifive_spi_write,
|
||||
.endianness = DEVICE_LITTLE_ENDIAN,
|
||||
.valid = {
|
||||
.min_access_size = 4,
|
||||
.max_access_size = 4
|
||||
}
|
||||
};
|
||||
|
||||
static void sifive_spi_realize(DeviceState *dev, Error **errp)
|
||||
{
|
||||
SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
|
||||
SiFiveSPIState *s = SIFIVE_SPI(dev);
|
||||
int i;
|
||||
|
||||
s->spi = ssi_create_bus(dev, "spi");
|
||||
sysbus_init_irq(sbd, &s->irq);
|
||||
|
||||
s->cs_lines = g_new0(qemu_irq, s->num_cs);
|
||||
for (i = 0; i < s->num_cs; i++) {
|
||||
sysbus_init_irq(sbd, &s->cs_lines[i]);
|
||||
}
|
||||
|
||||
memory_region_init_io(&s->mmio, OBJECT(s), &sifive_spi_ops, s,
|
||||
TYPE_SIFIVE_SPI, 0x1000);
|
||||
sysbus_init_mmio(sbd, &s->mmio);
|
||||
|
||||
fifo8_create(&s->tx_fifo, FIFO_CAPACITY);
|
||||
fifo8_create(&s->rx_fifo, FIFO_CAPACITY);
|
||||
}
|
||||
|
||||
static Property sifive_spi_properties[] = {
|
||||
DEFINE_PROP_UINT32("num-cs", SiFiveSPIState, num_cs, 1),
|
||||
DEFINE_PROP_END_OF_LIST(),
|
||||
};
|
||||
|
||||
static void sifive_spi_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
|
||||
device_class_set_props(dc, sifive_spi_properties);
|
||||
dc->reset = sifive_spi_reset;
|
||||
dc->realize = sifive_spi_realize;
|
||||
}
|
||||
|
||||
static const TypeInfo sifive_spi_info = {
|
||||
.name = TYPE_SIFIVE_SPI,
|
||||
.parent = TYPE_SYS_BUS_DEVICE,
|
||||
.instance_size = sizeof(SiFiveSPIState),
|
||||
.class_init = sifive_spi_class_init,
|
||||
};
|
||||
|
||||
static void sifive_spi_register_types(void)
|
||||
{
|
||||
type_register_static(&sifive_spi_info);
|
||||
}
|
||||
|
||||
type_init(sifive_spi_register_types)
|
@ -26,6 +26,7 @@
|
||||
#include "hw/gpio/sifive_gpio.h"
|
||||
#include "hw/misc/sifive_u_otp.h"
|
||||
#include "hw/misc/sifive_u_prci.h"
|
||||
#include "hw/ssi/sifive_spi.h"
|
||||
|
||||
#define TYPE_RISCV_U_SOC "riscv.sifive.u.soc"
|
||||
#define RISCV_U_SOC(obj) \
|
||||
@ -45,6 +46,8 @@ typedef struct SiFiveUSoCState {
|
||||
SIFIVEGPIOState gpio;
|
||||
SiFiveUOTPState otp;
|
||||
SiFivePDMAState dma;
|
||||
SiFiveSPIState spi0;
|
||||
SiFiveSPIState spi2;
|
||||
CadenceGEMState gem;
|
||||
|
||||
uint32_t serial;
|
||||
@ -82,6 +85,8 @@ enum {
|
||||
SIFIVE_U_DEV_UART0,
|
||||
SIFIVE_U_DEV_UART1,
|
||||
SIFIVE_U_DEV_GPIO,
|
||||
SIFIVE_U_DEV_QSPI0,
|
||||
SIFIVE_U_DEV_QSPI2,
|
||||
SIFIVE_U_DEV_OTP,
|
||||
SIFIVE_U_DEV_DMC,
|
||||
SIFIVE_U_DEV_FLASH0,
|
||||
@ -96,6 +101,7 @@ enum {
|
||||
SIFIVE_U_L2CC_IRQ2 = 3,
|
||||
SIFIVE_U_UART0_IRQ = 4,
|
||||
SIFIVE_U_UART1_IRQ = 5,
|
||||
SIFIVE_U_QSPI2_IRQ = 6,
|
||||
SIFIVE_U_GPIO_IRQ0 = 7,
|
||||
SIFIVE_U_GPIO_IRQ1 = 8,
|
||||
SIFIVE_U_GPIO_IRQ2 = 9,
|
||||
@ -120,7 +126,8 @@ enum {
|
||||
SIFIVE_U_PDMA_IRQ5 = 28,
|
||||
SIFIVE_U_PDMA_IRQ6 = 29,
|
||||
SIFIVE_U_PDMA_IRQ7 = 30,
|
||||
SIFIVE_U_GEM_IRQ = 0x35
|
||||
SIFIVE_U_QSPI0_IRQ = 51,
|
||||
SIFIVE_U_GEM_IRQ = 53
|
||||
};
|
||||
|
||||
enum {
|
||||
|
47
include/hw/ssi/sifive_spi.h
Normal file
47
include/hw/ssi/sifive_spi.h
Normal file
@ -0,0 +1,47 @@
|
||||
/*
|
||||
* QEMU model of the SiFive SPI Controller
|
||||
*
|
||||
* Copyright (c) 2021 Wind River Systems, Inc.
|
||||
*
|
||||
* Author:
|
||||
* Bin Meng <bin.meng@windriver.com>
|
||||
*
|
||||
* 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_SPI_H
|
||||
#define HW_SIFIVE_SPI_H
|
||||
|
||||
#define SIFIVE_SPI_REG_NUM (0x78 / 4)
|
||||
|
||||
#define TYPE_SIFIVE_SPI "sifive.spi"
|
||||
#define SIFIVE_SPI(obj) OBJECT_CHECK(SiFiveSPIState, (obj), TYPE_SIFIVE_SPI)
|
||||
|
||||
typedef struct SiFiveSPIState {
|
||||
SysBusDevice parent_obj;
|
||||
|
||||
MemoryRegion mmio;
|
||||
qemu_irq irq;
|
||||
|
||||
uint32_t num_cs;
|
||||
qemu_irq *cs_lines;
|
||||
|
||||
SSIBus *spi;
|
||||
|
||||
Fifo8 tx_fifo;
|
||||
Fifo8 rx_fifo;
|
||||
|
||||
uint32_t regs[SIFIVE_SPI_REG_NUM];
|
||||
} SiFiveSPIState;
|
||||
|
||||
#endif /* HW_SIFIVE_SPI_H */
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -1 +1 @@
|
||||
Subproject commit a98258d0b537a295f517bbc8d813007336731fa9
|
||||
Subproject commit 234ed8e427f4d92903123199f6590d144e0d9351
|
202
target/riscv/arch_dump.c
Normal file
202
target/riscv/arch_dump.c
Normal file
@ -0,0 +1,202 @@
|
||||
/* Support for writing ELF notes for RISC-V architectures
|
||||
*
|
||||
* Copyright (C) 2021 Huawei Technologies Co., Ltd
|
||||
*
|
||||
* 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 "cpu.h"
|
||||
#include "elf.h"
|
||||
#include "sysemu/dump.h"
|
||||
|
||||
/* struct user_regs_struct from arch/riscv/include/uapi/asm/ptrace.h */
|
||||
struct riscv64_user_regs {
|
||||
uint64_t pc;
|
||||
uint64_t regs[31];
|
||||
} QEMU_PACKED;
|
||||
|
||||
QEMU_BUILD_BUG_ON(sizeof(struct riscv64_user_regs) != 256);
|
||||
|
||||
/* struct elf_prstatus from include/linux/elfcore.h */
|
||||
struct riscv64_elf_prstatus {
|
||||
char pad1[32]; /* 32 == offsetof(struct elf_prstatus, pr_pid) */
|
||||
uint32_t pr_pid;
|
||||
char pad2[76]; /* 76 == offsetof(struct elf_prstatus, pr_reg) -
|
||||
offsetof(struct elf_prstatus, pr_ppid) */
|
||||
struct riscv64_user_regs pr_reg;
|
||||
char pad3[8];
|
||||
} QEMU_PACKED;
|
||||
|
||||
QEMU_BUILD_BUG_ON(sizeof(struct riscv64_elf_prstatus) != 376);
|
||||
|
||||
struct riscv64_note {
|
||||
Elf64_Nhdr hdr;
|
||||
char name[8]; /* align_up(sizeof("CORE"), 4) */
|
||||
struct riscv64_elf_prstatus prstatus;
|
||||
} QEMU_PACKED;
|
||||
|
||||
#define RISCV64_NOTE_HEADER_SIZE offsetof(struct riscv64_note, prstatus)
|
||||
#define RISCV64_PRSTATUS_NOTE_SIZE \
|
||||
(RISCV64_NOTE_HEADER_SIZE + sizeof(struct riscv64_elf_prstatus))
|
||||
|
||||
static void riscv64_note_init(struct riscv64_note *note, DumpState *s,
|
||||
const char *name, Elf64_Word namesz,
|
||||
Elf64_Word type, Elf64_Word descsz)
|
||||
{
|
||||
memset(note, 0, sizeof(*note));
|
||||
|
||||
note->hdr.n_namesz = cpu_to_dump32(s, namesz);
|
||||
note->hdr.n_descsz = cpu_to_dump32(s, descsz);
|
||||
note->hdr.n_type = cpu_to_dump32(s, type);
|
||||
|
||||
memcpy(note->name, name, namesz);
|
||||
}
|
||||
|
||||
int riscv_cpu_write_elf64_note(WriteCoreDumpFunction f, CPUState *cs,
|
||||
int cpuid, void *opaque)
|
||||
{
|
||||
struct riscv64_note note;
|
||||
RISCVCPU *cpu = RISCV_CPU(cs);
|
||||
CPURISCVState *env = &cpu->env;
|
||||
DumpState *s = opaque;
|
||||
int ret, i = 0;
|
||||
const char name[] = "CORE";
|
||||
|
||||
riscv64_note_init(¬e, s, name, sizeof(name),
|
||||
NT_PRSTATUS, sizeof(note.prstatus));
|
||||
|
||||
note.prstatus.pr_pid = cpu_to_dump32(s, cpuid);
|
||||
|
||||
note.prstatus.pr_reg.pc = cpu_to_dump64(s, env->pc);
|
||||
|
||||
for (i = 0; i < 31; i++) {
|
||||
note.prstatus.pr_reg.regs[i] = cpu_to_dump64(s, env->gpr[i + 1]);
|
||||
}
|
||||
|
||||
ret = f(¬e, RISCV64_PRSTATUS_NOTE_SIZE, s);
|
||||
if (ret < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
struct riscv32_user_regs {
|
||||
uint32_t pc;
|
||||
uint32_t regs[31];
|
||||
} QEMU_PACKED;
|
||||
|
||||
QEMU_BUILD_BUG_ON(sizeof(struct riscv32_user_regs) != 128);
|
||||
|
||||
struct riscv32_elf_prstatus {
|
||||
char pad1[24]; /* 24 == offsetof(struct elf_prstatus, pr_pid) */
|
||||
uint32_t pr_pid;
|
||||
char pad2[44]; /* 44 == offsetof(struct elf_prstatus, pr_reg) -
|
||||
offsetof(struct elf_prstatus, pr_ppid) */
|
||||
struct riscv32_user_regs pr_reg;
|
||||
char pad3[4];
|
||||
} QEMU_PACKED;
|
||||
|
||||
QEMU_BUILD_BUG_ON(sizeof(struct riscv32_elf_prstatus) != 204);
|
||||
|
||||
struct riscv32_note {
|
||||
Elf32_Nhdr hdr;
|
||||
char name[8]; /* align_up(sizeof("CORE"), 4) */
|
||||
struct riscv32_elf_prstatus prstatus;
|
||||
} QEMU_PACKED;
|
||||
|
||||
#define RISCV32_NOTE_HEADER_SIZE offsetof(struct riscv32_note, prstatus)
|
||||
#define RISCV32_PRSTATUS_NOTE_SIZE \
|
||||
(RISCV32_NOTE_HEADER_SIZE + sizeof(struct riscv32_elf_prstatus))
|
||||
|
||||
static void riscv32_note_init(struct riscv32_note *note, DumpState *s,
|
||||
const char *name, Elf32_Word namesz,
|
||||
Elf32_Word type, Elf32_Word descsz)
|
||||
{
|
||||
memset(note, 0, sizeof(*note));
|
||||
|
||||
note->hdr.n_namesz = cpu_to_dump32(s, namesz);
|
||||
note->hdr.n_descsz = cpu_to_dump32(s, descsz);
|
||||
note->hdr.n_type = cpu_to_dump32(s, type);
|
||||
|
||||
memcpy(note->name, name, namesz);
|
||||
}
|
||||
|
||||
int riscv_cpu_write_elf32_note(WriteCoreDumpFunction f, CPUState *cs,
|
||||
int cpuid, void *opaque)
|
||||
{
|
||||
struct riscv32_note note;
|
||||
RISCVCPU *cpu = RISCV_CPU(cs);
|
||||
CPURISCVState *env = &cpu->env;
|
||||
DumpState *s = opaque;
|
||||
int ret, i;
|
||||
const char name[] = "CORE";
|
||||
|
||||
riscv32_note_init(¬e, s, name, sizeof(name),
|
||||
NT_PRSTATUS, sizeof(note.prstatus));
|
||||
|
||||
note.prstatus.pr_pid = cpu_to_dump32(s, cpuid);
|
||||
|
||||
note.prstatus.pr_reg.pc = cpu_to_dump32(s, env->pc);
|
||||
|
||||
for (i = 0; i < 31; i++) {
|
||||
note.prstatus.pr_reg.regs[i] = cpu_to_dump32(s, env->gpr[i + 1]);
|
||||
}
|
||||
|
||||
ret = f(¬e, RISCV32_PRSTATUS_NOTE_SIZE, s);
|
||||
if (ret < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int cpu_get_dump_info(ArchDumpInfo *info,
|
||||
const GuestPhysBlockList *guest_phys_blocks)
|
||||
{
|
||||
RISCVCPU *cpu;
|
||||
CPURISCVState *env;
|
||||
|
||||
if (first_cpu == NULL) {
|
||||
return -1;
|
||||
}
|
||||
cpu = RISCV_CPU(first_cpu);
|
||||
env = &cpu->env;
|
||||
|
||||
info->d_machine = EM_RISCV;
|
||||
|
||||
#if defined(TARGET_RISCV64)
|
||||
info->d_class = ELFCLASS64;
|
||||
#else
|
||||
info->d_class = ELFCLASS32;
|
||||
#endif
|
||||
|
||||
info->d_endian = (env->mstatus & MSTATUS_UBE) != 0
|
||||
? ELFDATA2MSB : ELFDATA2LSB;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
ssize_t cpu_get_note_size(int class, int machine, int nr_cpus)
|
||||
{
|
||||
size_t note_size;
|
||||
|
||||
if (class == ELFCLASS64) {
|
||||
note_size = RISCV64_PRSTATUS_NOTE_SIZE;
|
||||
} else {
|
||||
note_size = RISCV32_PRSTATUS_NOTE_SIZE;
|
||||
}
|
||||
|
||||
return note_size * nr_cpus;
|
||||
}
|
@ -624,6 +624,8 @@ static void riscv_cpu_class_init(ObjectClass *c, void *data)
|
||||
cc->get_phys_page_debug = riscv_cpu_get_phys_page_debug;
|
||||
/* For now, mark unmigratable: */
|
||||
cc->vmsd = &vmstate_riscv_cpu;
|
||||
cc->write_elf64_note = riscv_cpu_write_elf64_note;
|
||||
cc->write_elf32_note = riscv_cpu_write_elf32_note;
|
||||
#endif
|
||||
cc->gdb_arch_name = riscv_gdb_arch_name;
|
||||
cc->gdb_get_dynamic_xml = riscv_gdb_get_dynamic_xml;
|
||||
|
@ -323,6 +323,10 @@ extern const char * const riscv_intr_names[];
|
||||
|
||||
const char *riscv_cpu_get_trap_name(target_ulong cause, bool async);
|
||||
void riscv_cpu_do_interrupt(CPUState *cpu);
|
||||
int riscv_cpu_write_elf64_note(WriteCoreDumpFunction f, CPUState *cs,
|
||||
int cpuid, void *opaque);
|
||||
int riscv_cpu_write_elf32_note(WriteCoreDumpFunction f, CPUState *cs,
|
||||
int cpuid, void *opaque);
|
||||
int riscv_cpu_gdb_read_register(CPUState *cpu, GByteArray *buf, int reg);
|
||||
int riscv_cpu_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg);
|
||||
bool riscv_cpu_exec_interrupt(CPUState *cs, int interrupt_request);
|
||||
@ -487,7 +491,7 @@ enum {
|
||||
};
|
||||
|
||||
/* CSR function table */
|
||||
extern riscv_csr_operations csr_ops[];
|
||||
extern riscv_csr_operations csr_ops[CSR_TABLE_SIZE];
|
||||
|
||||
void riscv_get_csr_ops(int csrno, riscv_csr_operations *ops);
|
||||
void riscv_set_csr_ops(int csrno, riscv_csr_operations *ops);
|
||||
|
@ -368,6 +368,7 @@
|
||||
#define MSTATUS_MIE 0x00000008
|
||||
#define MSTATUS_UPIE 0x00000010
|
||||
#define MSTATUS_SPIE 0x00000020
|
||||
#define MSTATUS_UBE 0x00000040
|
||||
#define MSTATUS_MPIE 0x00000080
|
||||
#define MSTATUS_SPP 0x00000100
|
||||
#define MSTATUS_MPP 0x00001800
|
||||
|
@ -26,6 +26,7 @@ riscv_ss.add(files(
|
||||
|
||||
riscv_softmmu_ss = ss.source_set()
|
||||
riscv_softmmu_ss.add(files(
|
||||
'arch_dump.c',
|
||||
'pmp.c',
|
||||
'monitor.c',
|
||||
'machine.c'
|
||||
|
Loading…
Reference in New Issue
Block a user