mirror of https://github.com/xemu-project/xemu.git
* hw/misc/a9scu: Do not allow invalid CPU count
* hw/misc/a9scu: Minor cleanups * hw/timer/armv7m_systick: assert that board code set system_clock_scale * decodetree: Improve identifier matching * target/arm: Clean up neon fp insn size field decode * target/arm: Remove KVM support for 32-bit Arm hosts * hw/arm/mps2: New board models mps2-an386, mps2-an500 * Deprecate Unicore32 port * Deprecate lm32 port * target/arm: Count PMU events when MDCR.SPME is set * hw/arm: versal-virt: Correct the tx/rx GEM clocks * New Nuvoton iBMC board models npcm750-evb, quanta-gsj -----BEGIN PGP SIGNATURE----- iQJNBAABCAA3FiEE4aXFk81BneKOgxXPPCUl7RQ2DN4FAl9fhe4ZHHBldGVyLm1h eWRlbGxAbGluYXJvLm9yZwAKCRA8JSXtFDYM3q+XD/47GZyfreMdEZecVVnpJIPx mUUy9IdaQddeB7WDqtB5rSRRSGUa2gtvu9bsRgLUjqVLwWHq++JeZabJYB0chCzn u0yAO3eMjeffTBooSJ318aBc2pENGoZPwg1JnMpZyL4W/JvXoPD5CUhqXZOmKBoz wSIS7nVp6U68G6kAICDOME9z7uXiTrIwA6tFoT87IQxy5BaWcK69e05vJO3coa/d GfmV1G13DNNRov9DjVVOFVY9Zwovo9rvZIKsD0+qr/cTWRAHxr7tq9XD9xyMwxsM AM3KqH9OiA3U5h771vkbch/NHTte65mYC7cGplpiq+l+4DP0txErPn3hwNX2CE2g nsYJbL+NKmbIh9Gj6xVaRgQZyzsl0KgsgS+ZOo9gRyIH8bjgdIsnphffYEnurFnA R1f8UHPxZwdJXkcS6RXNb3PYXUb4qcJDrsx1AGpccgQ9jyvBzWiGgOUaaqv6l/Ds btehspmxOl/FQd9s7YufhN/azLfsuKP5xxWdE9oYWbC2GcsmghBcyR1P00TcZQRH WERWzGUw31OQfXJhGjoBOMkw2PVIoGMVa1/BGb+wO7G8oRg9uFo7BVFz4F5YBGQB 5Ai5BmqcLlWtAQJNCHJZH5oqRtQIQN6A7poBk52ifSENlZAcocYPIKsxqf6HH9gQ CYhEsgk7IYP2YdnQyi5Lhg== =mWuB -----END PGP SIGNATURE----- Merge remote-tracking branch 'remotes/pmaydell/tags/pull-target-arm-20200914-1' into staging * hw/misc/a9scu: Do not allow invalid CPU count * hw/misc/a9scu: Minor cleanups * hw/timer/armv7m_systick: assert that board code set system_clock_scale * decodetree: Improve identifier matching * target/arm: Clean up neon fp insn size field decode * target/arm: Remove KVM support for 32-bit Arm hosts * hw/arm/mps2: New board models mps2-an386, mps2-an500 * Deprecate Unicore32 port * Deprecate lm32 port * target/arm: Count PMU events when MDCR.SPME is set * hw/arm: versal-virt: Correct the tx/rx GEM clocks * New Nuvoton iBMC board models npcm750-evb, quanta-gsj # gpg: Signature made Mon 14 Sep 2020 16:02:06 BST # gpg: using RSA key E1A5C593CD419DE28E8315CF3C2525ED14360CDE # gpg: issuer "peter.maydell@linaro.org" # gpg: Good signature from "Peter Maydell <peter.maydell@linaro.org>" [ultimate] # gpg: aka "Peter Maydell <pmaydell@gmail.com>" [ultimate] # gpg: aka "Peter Maydell <pmaydell@chiark.greenend.org.uk>" [ultimate] # Primary key fingerprint: E1A5 C593 CD41 9DE2 8E83 15CF 3C25 25ED 1436 0CDE * remotes/pmaydell/tags/pull-target-arm-20200914-1: (32 commits) tests/acceptance: console boot tests for quanta-gsj docs/system: Add Nuvoton machine documentation hw/arm/npcm7xx: add board setup stub for CPU and UART clocks hw/arm: Wire up BMC boot flash for npcm750-evb and quanta-gsj hw/ssi: NPCM7xx Flash Interface Unit device model hw/mem: Stubbed out NPCM7xx Memory Controller model hw/nvram: NPCM7xx OTP device model hw/arm: Load -bios image as a boot ROM for npcm7xx roms: Add virtual Boot ROM for NPCM7xx SoCs hw/arm: Add two NPCM7xx-based machines hw/arm: Add NPCM730 and NPCM750 SoC models hw/timer: Add NPCM7xx Timer device model hw/misc: Add NPCM7xx Clock Controller device model hw/misc: Add NPCM7xx System Global Control Registers device model hw/arm: versal-virt: Correct the tx/rx GEM clocks target/arm: Count PMU events when MDCR.SPME is set Deprecate lm32 port Deprecate Unicore32 port docs/system/arm/mps2.rst: Make board list consistent hw/arm/mps2: New board model mps2-an500 ... Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
commit
2d2c73d0e3
|
@ -61,3 +61,6 @@
|
||||||
[submodule "meson"]
|
[submodule "meson"]
|
||||||
path = meson
|
path = meson
|
||||||
url = https://github.com/mesonbuild/meson/
|
url = https://github.com/mesonbuild/meson/
|
||||||
|
[submodule "roms/vbootrom"]
|
||||||
|
path = roms/vbootrom
|
||||||
|
url = https://github.com/google/vbootrom.git
|
||||||
|
|
10
MAINTAINERS
10
MAINTAINERS
|
@ -750,6 +750,16 @@ S: Odd Fixes
|
||||||
F: hw/arm/musicpal.c
|
F: hw/arm/musicpal.c
|
||||||
F: docs/system/arm/musicpal.rst
|
F: docs/system/arm/musicpal.rst
|
||||||
|
|
||||||
|
Nuvoton NPCM7xx
|
||||||
|
M: Havard Skinnemoen <hskinnemoen@google.com>
|
||||||
|
M: Tyrone Ting <kfting@nuvoton.com>
|
||||||
|
L: qemu-arm@nongnu.org
|
||||||
|
S: Supported
|
||||||
|
F: hw/*/npcm7xx*
|
||||||
|
F: include/hw/*/npcm7xx*
|
||||||
|
F: pc-bios/npcm7xx_bootrom.bin
|
||||||
|
F: roms/vbootrom
|
||||||
|
|
||||||
nSeries
|
nSeries
|
||||||
M: Andrzej Zaborowski <balrogg@gmail.com>
|
M: Andrzej Zaborowski <balrogg@gmail.com>
|
||||||
M: Peter Maydell <peter.maydell@linaro.org>
|
M: Peter Maydell <peter.maydell@linaro.org>
|
||||||
|
|
|
@ -234,7 +234,7 @@ supported_kvm_target() {
|
||||||
test "$kvm" = "yes" || return 1
|
test "$kvm" = "yes" || return 1
|
||||||
glob "$1" "*-softmmu" || return 1
|
glob "$1" "*-softmmu" || return 1
|
||||||
case "${1%-softmmu}:$cpu" in
|
case "${1%-softmmu}:$cpu" in
|
||||||
arm:arm | aarch64:aarch64 | \
|
aarch64:aarch64 | \
|
||||||
i386:i386 | i386:x86_64 | i386:x32 | \
|
i386:i386 | i386:x86_64 | i386:x32 | \
|
||||||
x86_64:i386 | x86_64:x86_64 | x86_64:x32 | \
|
x86_64:i386 | x86_64:x86_64 | x86_64:x32 | \
|
||||||
mips:mips | mipsel:mips | mips64:mips | mips64el:mips | \
|
mips:mips | mipsel:mips | mips64:mips | mips64el:mips | \
|
||||||
|
|
|
@ -27,6 +27,7 @@ CONFIG_GUMSTIX=y
|
||||||
CONFIG_SPITZ=y
|
CONFIG_SPITZ=y
|
||||||
CONFIG_TOSA=y
|
CONFIG_TOSA=y
|
||||||
CONFIG_Z2=y
|
CONFIG_Z2=y
|
||||||
|
CONFIG_NPCM7XX=y
|
||||||
CONFIG_COLLIE=y
|
CONFIG_COLLIE=y
|
||||||
CONFIG_ASPEED_SOC=y
|
CONFIG_ASPEED_SOC=y
|
||||||
CONFIG_NETDUINO2=y
|
CONFIG_NETDUINO2=y
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
Arm MPS2 boards (``mps2-an385``, ``mps2-an505``, ``mps2-an511``, ``mps2-an521``)
|
Arm MPS2 boards (``mps2-an385``, ``mps2-an386``, ``mps2-an500``, ``mps2-an505``, ``mps2-an511``, ``mps2-an521``)
|
||||||
================================================================================
|
================================================================================================================
|
||||||
|
|
||||||
These board models all use Arm M-profile CPUs.
|
These board models all use Arm M-profile CPUs.
|
||||||
|
|
||||||
|
@ -11,17 +11,21 @@ as seen by the guest depend significantly on the FPGA image.
|
||||||
QEMU models the following FPGA images:
|
QEMU models the following FPGA images:
|
||||||
|
|
||||||
``mps2-an385``
|
``mps2-an385``
|
||||||
Cortex-M3 as documented in ARM Application Note AN385
|
Cortex-M3 as documented in Arm Application Note AN385
|
||||||
``mps2-an511``
|
``mps2-an386``
|
||||||
Cortex-M3 'DesignStart' as documented in AN511
|
Cortex-M4 as documented in Arm Application Note AN386
|
||||||
|
``mps2-an500``
|
||||||
|
Cortex-M7 as documented in Arm Application Note AN500
|
||||||
``mps2-an505``
|
``mps2-an505``
|
||||||
Cortex-M33 as documented in ARM Application Note AN505
|
Cortex-M33 as documented in Arm Application Note AN505
|
||||||
|
``mps2-an511``
|
||||||
|
Cortex-M3 'DesignStart' as documented in Arm Application Note AN511
|
||||||
``mps2-an521``
|
``mps2-an521``
|
||||||
Dual Cortex-M33 as documented in Application Note AN521
|
Dual Cortex-M33 as documented in Arm Application Note AN521
|
||||||
|
|
||||||
Differences between QEMU and real hardware:
|
Differences between QEMU and real hardware:
|
||||||
|
|
||||||
- AN385 remapping of low 16K of memory to either ZBT SSRAM1 or to
|
- AN385/AN386 remapping of low 16K of memory to either ZBT SSRAM1 or to
|
||||||
block RAM is unimplemented (QEMU always maps this to ZBT SSRAM1, as
|
block RAM is unimplemented (QEMU always maps this to ZBT SSRAM1, as
|
||||||
if zbt_boot_ctrl is always zero)
|
if zbt_boot_ctrl is always zero)
|
||||||
- QEMU provides a LAN9118 ethernet rather than LAN9220; the only guest
|
- QEMU provides a LAN9118 ethernet rather than LAN9220; the only guest
|
||||||
|
|
|
@ -0,0 +1,92 @@
|
||||||
|
Nuvoton iBMC boards (``npcm750-evb``, ``quanta-gsj``)
|
||||||
|
=====================================================
|
||||||
|
|
||||||
|
The `Nuvoton iBMC`_ chips (NPCM7xx) are a family of ARM-based SoCs that are
|
||||||
|
designed to be used as Baseboard Management Controllers (BMCs) in various
|
||||||
|
servers. They all feature one or two ARM Cortex A9 CPU cores, as well as an
|
||||||
|
assortment of peripherals targeted for either Enterprise or Data Center /
|
||||||
|
Hyperscale applications. The former is a superset of the latter, so NPCM750 has
|
||||||
|
all the peripherals of NPCM730 and more.
|
||||||
|
|
||||||
|
.. _Nuvoton iBMC: https://www.nuvoton.com/products/cloud-computing/ibmc/
|
||||||
|
|
||||||
|
The NPCM750 SoC has two Cortex A9 cores and is targeted for the Enterprise
|
||||||
|
segment. The following machines are based on this chip :
|
||||||
|
|
||||||
|
- ``npcm750-evb`` Nuvoton NPCM750 Evaluation board
|
||||||
|
|
||||||
|
The NPCM730 SoC has two Cortex A9 cores and is targeted for Data Center and
|
||||||
|
Hyperscale applications. The following machines are based on this chip :
|
||||||
|
|
||||||
|
- ``quanta-gsj`` Quanta GSJ server BMC
|
||||||
|
|
||||||
|
There are also two more SoCs, NPCM710 and NPCM705, which are single-core
|
||||||
|
variants of NPCM750 and NPCM730, respectively. These are currently not
|
||||||
|
supported by QEMU.
|
||||||
|
|
||||||
|
Supported devices
|
||||||
|
-----------------
|
||||||
|
|
||||||
|
* SMP (Dual Core Cortex-A9)
|
||||||
|
* Cortex-A9MPCore built-in peripherals: SCU, GIC, Global Timer, Private Timer
|
||||||
|
and Watchdog.
|
||||||
|
* SRAM, ROM and DRAM mappings
|
||||||
|
* System Global Control Registers (GCR)
|
||||||
|
* Clock and reset controller (CLK)
|
||||||
|
* Timer controller (TIM)
|
||||||
|
* Serial ports (16550-based)
|
||||||
|
* DDR4 memory controller (dummy interface indicating memory training is done)
|
||||||
|
* OTP controllers (no protection features)
|
||||||
|
* Flash Interface Unit (FIU; no protection features)
|
||||||
|
|
||||||
|
Missing devices
|
||||||
|
---------------
|
||||||
|
|
||||||
|
* GPIO controller
|
||||||
|
* LPC/eSPI host-to-BMC interface, including
|
||||||
|
|
||||||
|
* Keyboard and mouse controller interface (KBCI)
|
||||||
|
* Keyboard Controller Style (KCS) channels
|
||||||
|
* BIOS POST code FIFO
|
||||||
|
* System Wake-up Control (SWC)
|
||||||
|
* Shared memory (SHM)
|
||||||
|
* eSPI slave interface
|
||||||
|
|
||||||
|
* Ethernet controllers (GMAC and EMC)
|
||||||
|
* USB host (USBH)
|
||||||
|
* USB device (USBD)
|
||||||
|
* SMBus controller (SMBF)
|
||||||
|
* Peripheral SPI controller (PSPI)
|
||||||
|
* Analog to Digital Converter (ADC)
|
||||||
|
* SD/MMC host
|
||||||
|
* Random Number Generator (RNG)
|
||||||
|
* PECI interface
|
||||||
|
* Pulse Width Modulation (PWM)
|
||||||
|
* Tachometer
|
||||||
|
* PCI and PCIe root complex and bridges
|
||||||
|
* VDM and MCTP support
|
||||||
|
* Serial I/O expansion
|
||||||
|
* LPC/eSPI host
|
||||||
|
* Coprocessor
|
||||||
|
* Graphics
|
||||||
|
* Video capture
|
||||||
|
* Encoding compression engine
|
||||||
|
* Security features
|
||||||
|
|
||||||
|
Boot options
|
||||||
|
------------
|
||||||
|
|
||||||
|
The Nuvoton machines can boot from an OpenBMC firmware image, or directly into
|
||||||
|
a kernel using the ``-kernel`` option. OpenBMC images for `quanta-gsj` and
|
||||||
|
possibly others can be downloaded from the OpenPOWER jenkins :
|
||||||
|
|
||||||
|
https://openpower.xyz/
|
||||||
|
|
||||||
|
The firmware image should be attached as an MTD drive. Example :
|
||||||
|
|
||||||
|
.. code-block:: bash
|
||||||
|
|
||||||
|
$ qemu-system-arm -machine quanta-gsj -nographic \
|
||||||
|
-drive file=image-bmc,if=mtd,bus=0,unit=0,format=raw
|
||||||
|
|
||||||
|
The default root password for test images is usually ``0penBmc``.
|
|
@ -329,14 +329,6 @@ The ``compat`` property used to set backwards compatibility modes for
|
||||||
the processor has been deprecated. The ``max-cpu-compat`` property of
|
the processor has been deprecated. The ``max-cpu-compat`` property of
|
||||||
the ``pseries`` machine type should be used instead.
|
the ``pseries`` machine type should be used instead.
|
||||||
|
|
||||||
KVM guest support on 32-bit Arm hosts (since 5.0)
|
|
||||||
'''''''''''''''''''''''''''''''''''''''''''''''''
|
|
||||||
|
|
||||||
The Linux kernel has dropped support for allowing 32-bit Arm systems
|
|
||||||
to host KVM guests as of the 5.7 kernel. Accordingly, QEMU is deprecating
|
|
||||||
its support for this configuration and will remove it in a future version.
|
|
||||||
Running 32-bit guests on a 64-bit Arm host remains supported.
|
|
||||||
|
|
||||||
System emulator devices
|
System emulator devices
|
||||||
-----------------------
|
-----------------------
|
||||||
|
|
||||||
|
@ -416,6 +408,22 @@ The above, converted to the current supported format::
|
||||||
linux-user mode CPUs
|
linux-user mode CPUs
|
||||||
--------------------
|
--------------------
|
||||||
|
|
||||||
|
``lm32`` CPUs (since 5.2.0)
|
||||||
|
'''''''''''''''''''''''''''
|
||||||
|
|
||||||
|
The ``lm32`` guest CPU support is deprecated and will be removed in
|
||||||
|
a future version of QEMU. The only public user of this architecture
|
||||||
|
was the milkymist project, which has been dead for years; there was
|
||||||
|
never an upstream Linux port.
|
||||||
|
|
||||||
|
``unicore32`` CPUs (since 5.2.0)
|
||||||
|
''''''''''''''''''''''''''''''''
|
||||||
|
|
||||||
|
The ``unicore32`` guest CPU support is deprecated and will be removed in
|
||||||
|
a future version of QEMU. Support for this CPU was removed from the
|
||||||
|
upstream Linux kernel, and there is no available upstream toolchain
|
||||||
|
to build binaries for it.
|
||||||
|
|
||||||
``tilegx`` CPUs (since 5.1.0)
|
``tilegx`` CPUs (since 5.1.0)
|
||||||
'''''''''''''''''''''''''''''
|
'''''''''''''''''''''''''''''
|
||||||
|
|
||||||
|
@ -543,6 +551,14 @@ should be used instead of the 1.09.1 version.
|
||||||
System emulator CPUS
|
System emulator CPUS
|
||||||
--------------------
|
--------------------
|
||||||
|
|
||||||
|
KVM guest support on 32-bit Arm hosts (removed in 5.2)
|
||||||
|
''''''''''''''''''''''''''''''''''''''''''''''''''''''
|
||||||
|
|
||||||
|
The Linux kernel has dropped support for allowing 32-bit Arm systems
|
||||||
|
to host KVM guests as of the 5.7 kernel. Accordingly, QEMU is deprecating
|
||||||
|
its support for this configuration and will remove it in a future version.
|
||||||
|
Running 32-bit guests on a 64-bit Arm host remains supported.
|
||||||
|
|
||||||
RISC-V ISA Specific CPUs (removed in 5.1)
|
RISC-V ISA Specific CPUs (removed in 5.1)
|
||||||
'''''''''''''''''''''''''''''''''''''''''
|
'''''''''''''''''''''''''''''''''''''''''
|
||||||
|
|
||||||
|
|
|
@ -86,6 +86,7 @@ undocumented; you can get a complete list by running
|
||||||
arm/musicpal
|
arm/musicpal
|
||||||
arm/gumstix
|
arm/gumstix
|
||||||
arm/nseries
|
arm/nseries
|
||||||
|
arm/nuvoton
|
||||||
arm/orangepi
|
arm/orangepi
|
||||||
arm/palm
|
arm/palm
|
||||||
arm/xscale
|
arm/xscale
|
||||||
|
|
|
@ -355,6 +355,15 @@ config XLNX_VERSAL
|
||||||
select VIRTIO_MMIO
|
select VIRTIO_MMIO
|
||||||
select UNIMP
|
select UNIMP
|
||||||
|
|
||||||
|
config NPCM7XX
|
||||||
|
bool
|
||||||
|
select A9MPCORE
|
||||||
|
select ARM_GIC
|
||||||
|
select PL310 # cache controller
|
||||||
|
select SERIAL
|
||||||
|
select SSI
|
||||||
|
select UNIMP
|
||||||
|
|
||||||
config FSL_IMX25
|
config FSL_IMX25
|
||||||
bool
|
bool
|
||||||
select IMX
|
select IMX
|
||||||
|
|
|
@ -13,6 +13,7 @@ arm_ss.add(when: 'CONFIG_MICROBIT', if_true: files('microbit.c'))
|
||||||
arm_ss.add(when: 'CONFIG_MUSICPAL', if_true: files('musicpal.c'))
|
arm_ss.add(when: 'CONFIG_MUSICPAL', if_true: files('musicpal.c'))
|
||||||
arm_ss.add(when: 'CONFIG_NETDUINO2', if_true: files('netduino2.c'))
|
arm_ss.add(when: 'CONFIG_NETDUINO2', if_true: files('netduino2.c'))
|
||||||
arm_ss.add(when: 'CONFIG_NETDUINOPLUS2', if_true: files('netduinoplus2.c'))
|
arm_ss.add(when: 'CONFIG_NETDUINOPLUS2', if_true: files('netduinoplus2.c'))
|
||||||
|
arm_ss.add(when: 'CONFIG_NPCM7XX', if_true: files('npcm7xx.c', 'npcm7xx_boards.c'))
|
||||||
arm_ss.add(when: 'CONFIG_NSERIES', if_true: files('nseries.c'))
|
arm_ss.add(when: 'CONFIG_NSERIES', if_true: files('nseries.c'))
|
||||||
arm_ss.add(when: 'CONFIG_SX1', if_true: files('omap_sx1.c'))
|
arm_ss.add(when: 'CONFIG_SX1', if_true: files('omap_sx1.c'))
|
||||||
arm_ss.add(when: 'CONFIG_CHEETAH', if_true: files('palm.c'))
|
arm_ss.add(when: 'CONFIG_CHEETAH', if_true: files('palm.c'))
|
||||||
|
|
|
@ -15,6 +15,8 @@
|
||||||
* as seen by the guest depend significantly on the FPGA image.
|
* as seen by the guest depend significantly on the FPGA image.
|
||||||
* We model the following FPGA images:
|
* We model the following FPGA images:
|
||||||
* "mps2-an385" -- Cortex-M3 as documented in ARM Application Note AN385
|
* "mps2-an385" -- Cortex-M3 as documented in ARM Application Note AN385
|
||||||
|
* "mps2-an386" -- Cortex-M4 as documented in ARM Application Note AN386
|
||||||
|
* "mps2-an500" -- Cortex-M7 as documented in ARM Application Note AN500
|
||||||
* "mps2-an511" -- Cortex-M3 'DesignStart' as documented in AN511
|
* "mps2-an511" -- Cortex-M3 'DesignStart' as documented in AN511
|
||||||
*
|
*
|
||||||
* Links to the TRM for the board itself and to the various Application
|
* Links to the TRM for the board itself and to the various Application
|
||||||
|
@ -48,6 +50,8 @@
|
||||||
|
|
||||||
typedef enum MPS2FPGAType {
|
typedef enum MPS2FPGAType {
|
||||||
FPGA_AN385,
|
FPGA_AN385,
|
||||||
|
FPGA_AN386,
|
||||||
|
FPGA_AN500,
|
||||||
FPGA_AN511,
|
FPGA_AN511,
|
||||||
} MPS2FPGAType;
|
} MPS2FPGAType;
|
||||||
|
|
||||||
|
@ -55,6 +59,9 @@ struct MPS2MachineClass {
|
||||||
MachineClass parent;
|
MachineClass parent;
|
||||||
MPS2FPGAType fpga_type;
|
MPS2FPGAType fpga_type;
|
||||||
uint32_t scc_id;
|
uint32_t scc_id;
|
||||||
|
bool has_block_ram;
|
||||||
|
hwaddr ethernet_base;
|
||||||
|
hwaddr psram_base;
|
||||||
};
|
};
|
||||||
typedef struct MPS2MachineClass MPS2MachineClass;
|
typedef struct MPS2MachineClass MPS2MachineClass;
|
||||||
|
|
||||||
|
@ -82,6 +89,8 @@ typedef struct MPS2MachineState MPS2MachineState;
|
||||||
|
|
||||||
#define TYPE_MPS2_MACHINE "mps2"
|
#define TYPE_MPS2_MACHINE "mps2"
|
||||||
#define TYPE_MPS2_AN385_MACHINE MACHINE_TYPE_NAME("mps2-an385")
|
#define TYPE_MPS2_AN385_MACHINE MACHINE_TYPE_NAME("mps2-an385")
|
||||||
|
#define TYPE_MPS2_AN386_MACHINE MACHINE_TYPE_NAME("mps2-an386")
|
||||||
|
#define TYPE_MPS2_AN500_MACHINE MACHINE_TYPE_NAME("mps2-an500")
|
||||||
#define TYPE_MPS2_AN511_MACHINE MACHINE_TYPE_NAME("mps2-an511")
|
#define TYPE_MPS2_AN511_MACHINE MACHINE_TYPE_NAME("mps2-an511")
|
||||||
|
|
||||||
DECLARE_OBJ_CHECKERS(MPS2MachineState, MPS2MachineClass,
|
DECLARE_OBJ_CHECKERS(MPS2MachineState, MPS2MachineClass,
|
||||||
|
@ -139,13 +148,14 @@ static void mps2_common_init(MachineState *machine)
|
||||||
* tradeoffs. For QEMU they're all just RAM, though. We arbitrarily
|
* tradeoffs. For QEMU they're all just RAM, though. We arbitrarily
|
||||||
* call the 16MB our "system memory", as it's the largest lump.
|
* call the 16MB our "system memory", as it's the largest lump.
|
||||||
*
|
*
|
||||||
* Common to both boards:
|
* AN385/AN386/AN511:
|
||||||
* 0x21000000..0x21ffffff : PSRAM (16MB)
|
* 0x21000000 .. 0x21ffffff : PSRAM (16MB)
|
||||||
* AN385 only:
|
* AN385/AN386/AN500:
|
||||||
* 0x00000000 .. 0x003fffff : ZBT SSRAM1
|
* 0x00000000 .. 0x003fffff : ZBT SSRAM1
|
||||||
* 0x00400000 .. 0x007fffff : mirror of ZBT SSRAM1
|
* 0x00400000 .. 0x007fffff : mirror of ZBT SSRAM1
|
||||||
* 0x20000000 .. 0x203fffff : ZBT SSRAM 2&3
|
* 0x20000000 .. 0x203fffff : ZBT SSRAM 2&3
|
||||||
* 0x20400000 .. 0x207fffff : mirror of ZBT SSRAM 2&3
|
* 0x20400000 .. 0x207fffff : mirror of ZBT SSRAM 2&3
|
||||||
|
* AN385/AN386 only:
|
||||||
* 0x01000000 .. 0x01003fff : block RAM (16K)
|
* 0x01000000 .. 0x01003fff : block RAM (16K)
|
||||||
* 0x01004000 .. 0x01007fff : mirror of above
|
* 0x01004000 .. 0x01007fff : mirror of above
|
||||||
* 0x01008000 .. 0x0100bfff : mirror of above
|
* 0x01008000 .. 0x0100bfff : mirror of above
|
||||||
|
@ -155,21 +165,17 @@ static void mps2_common_init(MachineState *machine)
|
||||||
* 0x00400000 .. 0x007fffff : ZBT SSRAM1
|
* 0x00400000 .. 0x007fffff : ZBT SSRAM1
|
||||||
* 0x20000000 .. 0x2001ffff : SRAM
|
* 0x20000000 .. 0x2001ffff : SRAM
|
||||||
* 0x20400000 .. 0x207fffff : ZBT SSRAM 2&3
|
* 0x20400000 .. 0x207fffff : ZBT SSRAM 2&3
|
||||||
|
* AN500 only:
|
||||||
|
* 0x60000000 .. 0x60ffffff : PSRAM (16MB)
|
||||||
*
|
*
|
||||||
* The AN385 has a feature where the lowest 16K can be mapped
|
* The AN385/AN386 has a feature where the lowest 16K can be mapped
|
||||||
* either to the bottom of the ZBT SSRAM1 or to the block RAM.
|
* either to the bottom of the ZBT SSRAM1 or to the block RAM.
|
||||||
* This is of no use for QEMU so we don't implement it (as if
|
* This is of no use for QEMU so we don't implement it (as if
|
||||||
* zbt_boot_ctrl is always zero).
|
* zbt_boot_ctrl is always zero).
|
||||||
*/
|
*/
|
||||||
memory_region_add_subregion(system_memory, 0x21000000, machine->ram);
|
memory_region_add_subregion(system_memory, mmc->psram_base, machine->ram);
|
||||||
|
|
||||||
switch (mmc->fpga_type) {
|
if (mmc->has_block_ram) {
|
||||||
case FPGA_AN385:
|
|
||||||
make_ram(&mms->ssram1, "mps.ssram1", 0x0, 0x400000);
|
|
||||||
make_ram_alias(&mms->ssram1_m, "mps.ssram1_m", &mms->ssram1, 0x400000);
|
|
||||||
make_ram(&mms->ssram23, "mps.ssram23", 0x20000000, 0x400000);
|
|
||||||
make_ram_alias(&mms->ssram23_m, "mps.ssram23_m",
|
|
||||||
&mms->ssram23, 0x20400000);
|
|
||||||
make_ram(&mms->blockram, "mps.blockram", 0x01000000, 0x4000);
|
make_ram(&mms->blockram, "mps.blockram", 0x01000000, 0x4000);
|
||||||
make_ram_alias(&mms->blockram_m1, "mps.blockram_m1",
|
make_ram_alias(&mms->blockram_m1, "mps.blockram_m1",
|
||||||
&mms->blockram, 0x01004000);
|
&mms->blockram, 0x01004000);
|
||||||
|
@ -177,6 +183,17 @@ static void mps2_common_init(MachineState *machine)
|
||||||
&mms->blockram, 0x01008000);
|
&mms->blockram, 0x01008000);
|
||||||
make_ram_alias(&mms->blockram_m3, "mps.blockram_m3",
|
make_ram_alias(&mms->blockram_m3, "mps.blockram_m3",
|
||||||
&mms->blockram, 0x0100c000);
|
&mms->blockram, 0x0100c000);
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (mmc->fpga_type) {
|
||||||
|
case FPGA_AN385:
|
||||||
|
case FPGA_AN386:
|
||||||
|
case FPGA_AN500:
|
||||||
|
make_ram(&mms->ssram1, "mps.ssram1", 0x0, 0x400000);
|
||||||
|
make_ram_alias(&mms->ssram1_m, "mps.ssram1_m", &mms->ssram1, 0x400000);
|
||||||
|
make_ram(&mms->ssram23, "mps.ssram23", 0x20000000, 0x400000);
|
||||||
|
make_ram_alias(&mms->ssram23_m, "mps.ssram23_m",
|
||||||
|
&mms->ssram23, 0x20400000);
|
||||||
break;
|
break;
|
||||||
case FPGA_AN511:
|
case FPGA_AN511:
|
||||||
make_ram(&mms->blockram, "mps.blockram", 0x0, 0x40000);
|
make_ram(&mms->blockram, "mps.blockram", 0x0, 0x40000);
|
||||||
|
@ -192,6 +209,8 @@ static void mps2_common_init(MachineState *machine)
|
||||||
armv7m = DEVICE(&mms->armv7m);
|
armv7m = DEVICE(&mms->armv7m);
|
||||||
switch (mmc->fpga_type) {
|
switch (mmc->fpga_type) {
|
||||||
case FPGA_AN385:
|
case FPGA_AN385:
|
||||||
|
case FPGA_AN386:
|
||||||
|
case FPGA_AN500:
|
||||||
qdev_prop_set_uint32(armv7m, "num-irq", 32);
|
qdev_prop_set_uint32(armv7m, "num-irq", 32);
|
||||||
break;
|
break;
|
||||||
case FPGA_AN511:
|
case FPGA_AN511:
|
||||||
|
@ -228,6 +247,8 @@ static void mps2_common_init(MachineState *machine)
|
||||||
|
|
||||||
switch (mmc->fpga_type) {
|
switch (mmc->fpga_type) {
|
||||||
case FPGA_AN385:
|
case FPGA_AN385:
|
||||||
|
case FPGA_AN386:
|
||||||
|
case FPGA_AN500:
|
||||||
{
|
{
|
||||||
/* The overflow IRQs for UARTs 0, 1 and 2 are ORed together.
|
/* The overflow IRQs for UARTs 0, 1 and 2 are ORed together.
|
||||||
* Overflow for UARTs 4 and 5 doesn't trigger any interrupt.
|
* Overflow for UARTs 4 and 5 doesn't trigger any interrupt.
|
||||||
|
@ -377,9 +398,9 @@ static void mps2_common_init(MachineState *machine)
|
||||||
/* In hardware this is a LAN9220; the LAN9118 is software compatible
|
/* In hardware this is a LAN9220; the LAN9118 is software compatible
|
||||||
* except that it doesn't support the checksum-offload feature.
|
* except that it doesn't support the checksum-offload feature.
|
||||||
*/
|
*/
|
||||||
lan9118_init(&nd_table[0], 0x40200000,
|
lan9118_init(&nd_table[0], mmc->ethernet_base,
|
||||||
qdev_get_gpio_in(armv7m,
|
qdev_get_gpio_in(armv7m,
|
||||||
mmc->fpga_type == FPGA_AN385 ? 13 : 47));
|
mmc->fpga_type == FPGA_AN511 ? 47 : 13));
|
||||||
|
|
||||||
system_clock_scale = NANOSECONDS_PER_SECOND / SYSCLK_FRQ;
|
system_clock_scale = NANOSECONDS_PER_SECOND / SYSCLK_FRQ;
|
||||||
|
|
||||||
|
@ -406,6 +427,37 @@ static void mps2_an385_class_init(ObjectClass *oc, void *data)
|
||||||
mmc->fpga_type = FPGA_AN385;
|
mmc->fpga_type = FPGA_AN385;
|
||||||
mc->default_cpu_type = ARM_CPU_TYPE_NAME("cortex-m3");
|
mc->default_cpu_type = ARM_CPU_TYPE_NAME("cortex-m3");
|
||||||
mmc->scc_id = 0x41043850;
|
mmc->scc_id = 0x41043850;
|
||||||
|
mmc->psram_base = 0x21000000;
|
||||||
|
mmc->ethernet_base = 0x40200000;
|
||||||
|
mmc->has_block_ram = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void mps2_an386_class_init(ObjectClass *oc, void *data)
|
||||||
|
{
|
||||||
|
MachineClass *mc = MACHINE_CLASS(oc);
|
||||||
|
MPS2MachineClass *mmc = MPS2_MACHINE_CLASS(oc);
|
||||||
|
|
||||||
|
mc->desc = "ARM MPS2 with AN386 FPGA image for Cortex-M4";
|
||||||
|
mmc->fpga_type = FPGA_AN386;
|
||||||
|
mc->default_cpu_type = ARM_CPU_TYPE_NAME("cortex-m4");
|
||||||
|
mmc->scc_id = 0x41043860;
|
||||||
|
mmc->psram_base = 0x21000000;
|
||||||
|
mmc->ethernet_base = 0x40200000;
|
||||||
|
mmc->has_block_ram = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void mps2_an500_class_init(ObjectClass *oc, void *data)
|
||||||
|
{
|
||||||
|
MachineClass *mc = MACHINE_CLASS(oc);
|
||||||
|
MPS2MachineClass *mmc = MPS2_MACHINE_CLASS(oc);
|
||||||
|
|
||||||
|
mc->desc = "ARM MPS2 with AN500 FPGA image for Cortex-M7";
|
||||||
|
mmc->fpga_type = FPGA_AN500;
|
||||||
|
mc->default_cpu_type = ARM_CPU_TYPE_NAME("cortex-m7");
|
||||||
|
mmc->scc_id = 0x41045000;
|
||||||
|
mmc->psram_base = 0x60000000;
|
||||||
|
mmc->ethernet_base = 0xa0000000;
|
||||||
|
mmc->has_block_ram = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void mps2_an511_class_init(ObjectClass *oc, void *data)
|
static void mps2_an511_class_init(ObjectClass *oc, void *data)
|
||||||
|
@ -417,6 +469,9 @@ static void mps2_an511_class_init(ObjectClass *oc, void *data)
|
||||||
mmc->fpga_type = FPGA_AN511;
|
mmc->fpga_type = FPGA_AN511;
|
||||||
mc->default_cpu_type = ARM_CPU_TYPE_NAME("cortex-m3");
|
mc->default_cpu_type = ARM_CPU_TYPE_NAME("cortex-m3");
|
||||||
mmc->scc_id = 0x41045110;
|
mmc->scc_id = 0x41045110;
|
||||||
|
mmc->psram_base = 0x21000000;
|
||||||
|
mmc->ethernet_base = 0x40200000;
|
||||||
|
mmc->has_block_ram = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const TypeInfo mps2_info = {
|
static const TypeInfo mps2_info = {
|
||||||
|
@ -434,6 +489,18 @@ static const TypeInfo mps2_an385_info = {
|
||||||
.class_init = mps2_an385_class_init,
|
.class_init = mps2_an385_class_init,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static const TypeInfo mps2_an386_info = {
|
||||||
|
.name = TYPE_MPS2_AN386_MACHINE,
|
||||||
|
.parent = TYPE_MPS2_MACHINE,
|
||||||
|
.class_init = mps2_an386_class_init,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const TypeInfo mps2_an500_info = {
|
||||||
|
.name = TYPE_MPS2_AN500_MACHINE,
|
||||||
|
.parent = TYPE_MPS2_MACHINE,
|
||||||
|
.class_init = mps2_an500_class_init,
|
||||||
|
};
|
||||||
|
|
||||||
static const TypeInfo mps2_an511_info = {
|
static const TypeInfo mps2_an511_info = {
|
||||||
.name = TYPE_MPS2_AN511_MACHINE,
|
.name = TYPE_MPS2_AN511_MACHINE,
|
||||||
.parent = TYPE_MPS2_MACHINE,
|
.parent = TYPE_MPS2_MACHINE,
|
||||||
|
@ -444,6 +511,8 @@ static void mps2_machine_init(void)
|
||||||
{
|
{
|
||||||
type_register_static(&mps2_info);
|
type_register_static(&mps2_info);
|
||||||
type_register_static(&mps2_an385_info);
|
type_register_static(&mps2_an385_info);
|
||||||
|
type_register_static(&mps2_an386_info);
|
||||||
|
type_register_static(&mps2_an500_info);
|
||||||
type_register_static(&mps2_an511_info);
|
type_register_static(&mps2_an511_info);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,532 @@
|
||||||
|
/*
|
||||||
|
* Nuvoton NPCM7xx SoC family.
|
||||||
|
*
|
||||||
|
* Copyright 2020 Google LLC
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "qemu/osdep.h"
|
||||||
|
|
||||||
|
#include "exec/address-spaces.h"
|
||||||
|
#include "hw/arm/boot.h"
|
||||||
|
#include "hw/arm/npcm7xx.h"
|
||||||
|
#include "hw/char/serial.h"
|
||||||
|
#include "hw/loader.h"
|
||||||
|
#include "hw/misc/unimp.h"
|
||||||
|
#include "hw/qdev-properties.h"
|
||||||
|
#include "qapi/error.h"
|
||||||
|
#include "qemu/units.h"
|
||||||
|
#include "sysemu/sysemu.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This covers the whole MMIO space. We'll use this to catch any MMIO accesses
|
||||||
|
* that aren't handled by any device.
|
||||||
|
*/
|
||||||
|
#define NPCM7XX_MMIO_BA (0x80000000)
|
||||||
|
#define NPCM7XX_MMIO_SZ (0x7ffd0000)
|
||||||
|
|
||||||
|
/* OTP key storage and fuse strap array */
|
||||||
|
#define NPCM7XX_OTP1_BA (0xf0189000)
|
||||||
|
#define NPCM7XX_OTP2_BA (0xf018a000)
|
||||||
|
|
||||||
|
/* Core system modules. */
|
||||||
|
#define NPCM7XX_L2C_BA (0xf03fc000)
|
||||||
|
#define NPCM7XX_CPUP_BA (0xf03fe000)
|
||||||
|
#define NPCM7XX_GCR_BA (0xf0800000)
|
||||||
|
#define NPCM7XX_CLK_BA (0xf0801000)
|
||||||
|
#define NPCM7XX_MC_BA (0xf0824000)
|
||||||
|
|
||||||
|
/* Internal AHB SRAM */
|
||||||
|
#define NPCM7XX_RAM3_BA (0xc0008000)
|
||||||
|
#define NPCM7XX_RAM3_SZ (4 * KiB)
|
||||||
|
|
||||||
|
/* Memory blocks at the end of the address space */
|
||||||
|
#define NPCM7XX_RAM2_BA (0xfffd0000)
|
||||||
|
#define NPCM7XX_RAM2_SZ (128 * KiB)
|
||||||
|
#define NPCM7XX_ROM_BA (0xffff0000)
|
||||||
|
#define NPCM7XX_ROM_SZ (64 * KiB)
|
||||||
|
|
||||||
|
/* Clock configuration values to be fixed up when bypassing bootloader */
|
||||||
|
|
||||||
|
/* Run PLL1 at 1600 MHz */
|
||||||
|
#define NPCM7XX_PLLCON1_FIXUP_VAL (0x00402101)
|
||||||
|
/* Run the CPU from PLL1 and UART from PLL2 */
|
||||||
|
#define NPCM7XX_CLKSEL_FIXUP_VAL (0x004aaba9)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Interrupt lines going into the GIC. This does not include internal Cortex-A9
|
||||||
|
* interrupts.
|
||||||
|
*/
|
||||||
|
enum NPCM7xxInterrupt {
|
||||||
|
NPCM7XX_UART0_IRQ = 2,
|
||||||
|
NPCM7XX_UART1_IRQ,
|
||||||
|
NPCM7XX_UART2_IRQ,
|
||||||
|
NPCM7XX_UART3_IRQ,
|
||||||
|
NPCM7XX_TIMER0_IRQ = 32, /* Timer Module 0 */
|
||||||
|
NPCM7XX_TIMER1_IRQ,
|
||||||
|
NPCM7XX_TIMER2_IRQ,
|
||||||
|
NPCM7XX_TIMER3_IRQ,
|
||||||
|
NPCM7XX_TIMER4_IRQ,
|
||||||
|
NPCM7XX_TIMER5_IRQ, /* Timer Module 1 */
|
||||||
|
NPCM7XX_TIMER6_IRQ,
|
||||||
|
NPCM7XX_TIMER7_IRQ,
|
||||||
|
NPCM7XX_TIMER8_IRQ,
|
||||||
|
NPCM7XX_TIMER9_IRQ,
|
||||||
|
NPCM7XX_TIMER10_IRQ, /* Timer Module 2 */
|
||||||
|
NPCM7XX_TIMER11_IRQ,
|
||||||
|
NPCM7XX_TIMER12_IRQ,
|
||||||
|
NPCM7XX_TIMER13_IRQ,
|
||||||
|
NPCM7XX_TIMER14_IRQ,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Total number of GIC interrupts, including internal Cortex-A9 interrupts. */
|
||||||
|
#define NPCM7XX_NUM_IRQ (160)
|
||||||
|
|
||||||
|
/* Register base address for each Timer Module */
|
||||||
|
static const hwaddr npcm7xx_tim_addr[] = {
|
||||||
|
0xf0008000,
|
||||||
|
0xf0009000,
|
||||||
|
0xf000a000,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Register base address for each 16550 UART */
|
||||||
|
static const hwaddr npcm7xx_uart_addr[] = {
|
||||||
|
0xf0001000,
|
||||||
|
0xf0002000,
|
||||||
|
0xf0003000,
|
||||||
|
0xf0004000,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Direct memory-mapped access to SPI0 CS0-1. */
|
||||||
|
static const hwaddr npcm7xx_fiu0_flash_addr[] = {
|
||||||
|
0x80000000, /* CS0 */
|
||||||
|
0x88000000, /* CS1 */
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Direct memory-mapped access to SPI3 CS0-3. */
|
||||||
|
static const hwaddr npcm7xx_fiu3_flash_addr[] = {
|
||||||
|
0xa0000000, /* CS0 */
|
||||||
|
0xa8000000, /* CS1 */
|
||||||
|
0xb0000000, /* CS2 */
|
||||||
|
0xb8000000, /* CS3 */
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct {
|
||||||
|
const char *name;
|
||||||
|
hwaddr regs_addr;
|
||||||
|
int cs_count;
|
||||||
|
const hwaddr *flash_addr;
|
||||||
|
} npcm7xx_fiu[] = {
|
||||||
|
{
|
||||||
|
.name = "fiu0",
|
||||||
|
.regs_addr = 0xfb000000,
|
||||||
|
.cs_count = ARRAY_SIZE(npcm7xx_fiu0_flash_addr),
|
||||||
|
.flash_addr = npcm7xx_fiu0_flash_addr,
|
||||||
|
}, {
|
||||||
|
.name = "fiu3",
|
||||||
|
.regs_addr = 0xc0000000,
|
||||||
|
.cs_count = ARRAY_SIZE(npcm7xx_fiu3_flash_addr),
|
||||||
|
.flash_addr = npcm7xx_fiu3_flash_addr,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
static void npcm7xx_write_board_setup(ARMCPU *cpu,
|
||||||
|
const struct arm_boot_info *info)
|
||||||
|
{
|
||||||
|
uint32_t board_setup[] = {
|
||||||
|
0xe59f0010, /* ldr r0, clk_base_addr */
|
||||||
|
0xe59f1010, /* ldr r1, pllcon1_value */
|
||||||
|
0xe5801010, /* str r1, [r0, #16] */
|
||||||
|
0xe59f100c, /* ldr r1, clksel_value */
|
||||||
|
0xe5801004, /* str r1, [r0, #4] */
|
||||||
|
0xe12fff1e, /* bx lr */
|
||||||
|
NPCM7XX_CLK_BA,
|
||||||
|
NPCM7XX_PLLCON1_FIXUP_VAL,
|
||||||
|
NPCM7XX_CLKSEL_FIXUP_VAL,
|
||||||
|
};
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < ARRAY_SIZE(board_setup); i++) {
|
||||||
|
board_setup[i] = tswap32(board_setup[i]);
|
||||||
|
}
|
||||||
|
rom_add_blob_fixed("board-setup", board_setup, sizeof(board_setup),
|
||||||
|
info->board_setup_addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void npcm7xx_write_secondary_boot(ARMCPU *cpu,
|
||||||
|
const struct arm_boot_info *info)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* The default smpboot stub halts the secondary CPU with a 'wfi'
|
||||||
|
* instruction, but the arch/arm/mach-npcm/platsmp.c in the Linux kernel
|
||||||
|
* does not send an IPI to wake it up, so the second CPU fails to boot. So
|
||||||
|
* we need to provide our own smpboot stub that can not use 'wfi', it has
|
||||||
|
* to spin the secondary CPU until the first CPU writes to the SCRPAD reg.
|
||||||
|
*/
|
||||||
|
uint32_t smpboot[] = {
|
||||||
|
0xe59f2018, /* ldr r2, bootreg_addr */
|
||||||
|
0xe3a00000, /* mov r0, #0 */
|
||||||
|
0xe5820000, /* str r0, [r2] */
|
||||||
|
0xe320f002, /* wfe */
|
||||||
|
0xe5921000, /* ldr r1, [r2] */
|
||||||
|
0xe1110001, /* tst r1, r1 */
|
||||||
|
0x0afffffb, /* beq <wfe> */
|
||||||
|
0xe12fff11, /* bx r1 */
|
||||||
|
NPCM7XX_SMP_BOOTREG_ADDR,
|
||||||
|
};
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < ARRAY_SIZE(smpboot); i++) {
|
||||||
|
smpboot[i] = tswap32(smpboot[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
rom_add_blob_fixed("smpboot", smpboot, sizeof(smpboot),
|
||||||
|
NPCM7XX_SMP_LOADER_START);
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct arm_boot_info npcm7xx_binfo = {
|
||||||
|
.loader_start = NPCM7XX_LOADER_START,
|
||||||
|
.smp_loader_start = NPCM7XX_SMP_LOADER_START,
|
||||||
|
.smp_bootreg_addr = NPCM7XX_SMP_BOOTREG_ADDR,
|
||||||
|
.gic_cpu_if_addr = NPCM7XX_GIC_CPU_IF_ADDR,
|
||||||
|
.write_secondary_boot = npcm7xx_write_secondary_boot,
|
||||||
|
.board_id = -1,
|
||||||
|
.board_setup_addr = NPCM7XX_BOARD_SETUP_ADDR,
|
||||||
|
.write_board_setup = npcm7xx_write_board_setup,
|
||||||
|
};
|
||||||
|
|
||||||
|
void npcm7xx_load_kernel(MachineState *machine, NPCM7xxState *soc)
|
||||||
|
{
|
||||||
|
NPCM7xxClass *sc = NPCM7XX_GET_CLASS(soc);
|
||||||
|
|
||||||
|
npcm7xx_binfo.ram_size = machine->ram_size;
|
||||||
|
npcm7xx_binfo.nb_cpus = sc->num_cpus;
|
||||||
|
|
||||||
|
arm_load_kernel(&soc->cpu[0], machine, &npcm7xx_binfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void npcm7xx_init_fuses(NPCM7xxState *s)
|
||||||
|
{
|
||||||
|
NPCM7xxClass *nc = NPCM7XX_GET_CLASS(s);
|
||||||
|
uint32_t value;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The initial mask of disabled modules indicates the chip derivative (e.g.
|
||||||
|
* NPCM750 or NPCM730).
|
||||||
|
*/
|
||||||
|
value = tswap32(nc->disabled_modules);
|
||||||
|
npcm7xx_otp_array_write(&s->fuse_array, &value, NPCM7XX_FUSE_DERIVATIVE,
|
||||||
|
sizeof(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
static qemu_irq npcm7xx_irq(NPCM7xxState *s, int n)
|
||||||
|
{
|
||||||
|
return qdev_get_gpio_in(DEVICE(&s->a9mpcore), n);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void npcm7xx_init(Object *obj)
|
||||||
|
{
|
||||||
|
NPCM7xxState *s = NPCM7XX(obj);
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < NPCM7XX_MAX_NUM_CPUS; i++) {
|
||||||
|
object_initialize_child(obj, "cpu[*]", &s->cpu[i],
|
||||||
|
ARM_CPU_TYPE_NAME("cortex-a9"));
|
||||||
|
}
|
||||||
|
|
||||||
|
object_initialize_child(obj, "a9mpcore", &s->a9mpcore, TYPE_A9MPCORE_PRIV);
|
||||||
|
object_initialize_child(obj, "gcr", &s->gcr, TYPE_NPCM7XX_GCR);
|
||||||
|
object_property_add_alias(obj, "power-on-straps", OBJECT(&s->gcr),
|
||||||
|
"power-on-straps");
|
||||||
|
object_initialize_child(obj, "clk", &s->clk, TYPE_NPCM7XX_CLK);
|
||||||
|
object_initialize_child(obj, "otp1", &s->key_storage,
|
||||||
|
TYPE_NPCM7XX_KEY_STORAGE);
|
||||||
|
object_initialize_child(obj, "otp2", &s->fuse_array,
|
||||||
|
TYPE_NPCM7XX_FUSE_ARRAY);
|
||||||
|
object_initialize_child(obj, "mc", &s->mc, TYPE_NPCM7XX_MC);
|
||||||
|
|
||||||
|
for (i = 0; i < ARRAY_SIZE(s->tim); i++) {
|
||||||
|
object_initialize_child(obj, "tim[*]", &s->tim[i], TYPE_NPCM7XX_TIMER);
|
||||||
|
}
|
||||||
|
|
||||||
|
QEMU_BUILD_BUG_ON(ARRAY_SIZE(npcm7xx_fiu) != ARRAY_SIZE(s->fiu));
|
||||||
|
for (i = 0; i < ARRAY_SIZE(s->fiu); i++) {
|
||||||
|
object_initialize_child(obj, npcm7xx_fiu[i].name, &s->fiu[i],
|
||||||
|
TYPE_NPCM7XX_FIU);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void npcm7xx_realize(DeviceState *dev, Error **errp)
|
||||||
|
{
|
||||||
|
NPCM7xxState *s = NPCM7XX(dev);
|
||||||
|
NPCM7xxClass *nc = NPCM7XX_GET_CLASS(s);
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (memory_region_size(s->dram) > NPCM7XX_DRAM_SZ) {
|
||||||
|
error_setg(errp, "%s: NPCM7xx cannot address more than %" PRIu64
|
||||||
|
" MiB of DRAM", __func__, NPCM7XX_DRAM_SZ / MiB);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* CPUs */
|
||||||
|
for (i = 0; i < nc->num_cpus; i++) {
|
||||||
|
object_property_set_int(OBJECT(&s->cpu[i]), "mp-affinity",
|
||||||
|
arm_cpu_mp_affinity(i, NPCM7XX_MAX_NUM_CPUS),
|
||||||
|
&error_abort);
|
||||||
|
object_property_set_int(OBJECT(&s->cpu[i]), "reset-cbar",
|
||||||
|
NPCM7XX_GIC_CPU_IF_ADDR, &error_abort);
|
||||||
|
object_property_set_bool(OBJECT(&s->cpu[i]), "reset-hivecs", true,
|
||||||
|
&error_abort);
|
||||||
|
|
||||||
|
/* Disable security extensions. */
|
||||||
|
object_property_set_bool(OBJECT(&s->cpu[i]), "has_el3", false,
|
||||||
|
&error_abort);
|
||||||
|
|
||||||
|
if (!qdev_realize(DEVICE(&s->cpu[i]), NULL, errp)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* A9MPCORE peripherals. Can only fail if we pass bad parameters here. */
|
||||||
|
object_property_set_int(OBJECT(&s->a9mpcore), "num-cpu", nc->num_cpus,
|
||||||
|
&error_abort);
|
||||||
|
object_property_set_int(OBJECT(&s->a9mpcore), "num-irq", NPCM7XX_NUM_IRQ,
|
||||||
|
&error_abort);
|
||||||
|
sysbus_realize(SYS_BUS_DEVICE(&s->a9mpcore), &error_abort);
|
||||||
|
sysbus_mmio_map(SYS_BUS_DEVICE(&s->a9mpcore), 0, NPCM7XX_CPUP_BA);
|
||||||
|
|
||||||
|
for (i = 0; i < nc->num_cpus; i++) {
|
||||||
|
sysbus_connect_irq(SYS_BUS_DEVICE(&s->a9mpcore), i,
|
||||||
|
qdev_get_gpio_in(DEVICE(&s->cpu[i]), ARM_CPU_IRQ));
|
||||||
|
sysbus_connect_irq(SYS_BUS_DEVICE(&s->a9mpcore), i + nc->num_cpus,
|
||||||
|
qdev_get_gpio_in(DEVICE(&s->cpu[i]), ARM_CPU_FIQ));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* L2 cache controller */
|
||||||
|
sysbus_create_simple("l2x0", NPCM7XX_L2C_BA, NULL);
|
||||||
|
|
||||||
|
/* System Global Control Registers (GCR). Can fail due to user input. */
|
||||||
|
object_property_set_int(OBJECT(&s->gcr), "disabled-modules",
|
||||||
|
nc->disabled_modules, &error_abort);
|
||||||
|
object_property_add_const_link(OBJECT(&s->gcr), "dram-mr", OBJECT(s->dram));
|
||||||
|
if (!sysbus_realize(SYS_BUS_DEVICE(&s->gcr), errp)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
sysbus_mmio_map(SYS_BUS_DEVICE(&s->gcr), 0, NPCM7XX_GCR_BA);
|
||||||
|
|
||||||
|
/* Clock Control Registers (CLK). Cannot fail. */
|
||||||
|
sysbus_realize(SYS_BUS_DEVICE(&s->clk), &error_abort);
|
||||||
|
sysbus_mmio_map(SYS_BUS_DEVICE(&s->clk), 0, NPCM7XX_CLK_BA);
|
||||||
|
|
||||||
|
/* OTP key storage and fuse strap array. Cannot fail. */
|
||||||
|
sysbus_realize(SYS_BUS_DEVICE(&s->key_storage), &error_abort);
|
||||||
|
sysbus_mmio_map(SYS_BUS_DEVICE(&s->key_storage), 0, NPCM7XX_OTP1_BA);
|
||||||
|
sysbus_realize(SYS_BUS_DEVICE(&s->fuse_array), &error_abort);
|
||||||
|
sysbus_mmio_map(SYS_BUS_DEVICE(&s->fuse_array), 0, NPCM7XX_OTP2_BA);
|
||||||
|
npcm7xx_init_fuses(s);
|
||||||
|
|
||||||
|
/* Fake Memory Controller (MC). Cannot fail. */
|
||||||
|
sysbus_realize(SYS_BUS_DEVICE(&s->mc), &error_abort);
|
||||||
|
sysbus_mmio_map(SYS_BUS_DEVICE(&s->mc), 0, NPCM7XX_MC_BA);
|
||||||
|
|
||||||
|
/* Timer Modules (TIM). Cannot fail. */
|
||||||
|
QEMU_BUILD_BUG_ON(ARRAY_SIZE(npcm7xx_tim_addr) != ARRAY_SIZE(s->tim));
|
||||||
|
for (i = 0; i < ARRAY_SIZE(s->tim); i++) {
|
||||||
|
SysBusDevice *sbd = SYS_BUS_DEVICE(&s->tim[i]);
|
||||||
|
int first_irq;
|
||||||
|
int j;
|
||||||
|
|
||||||
|
sysbus_realize(sbd, &error_abort);
|
||||||
|
sysbus_mmio_map(sbd, 0, npcm7xx_tim_addr[i]);
|
||||||
|
|
||||||
|
first_irq = NPCM7XX_TIMER0_IRQ + i * NPCM7XX_TIMERS_PER_CTRL;
|
||||||
|
for (j = 0; j < NPCM7XX_TIMERS_PER_CTRL; j++) {
|
||||||
|
qemu_irq irq = npcm7xx_irq(s, first_irq + j);
|
||||||
|
sysbus_connect_irq(sbd, j, irq);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* UART0..3 (16550 compatible) */
|
||||||
|
for (i = 0; i < ARRAY_SIZE(npcm7xx_uart_addr); i++) {
|
||||||
|
serial_mm_init(get_system_memory(), npcm7xx_uart_addr[i], 2,
|
||||||
|
npcm7xx_irq(s, NPCM7XX_UART0_IRQ + i), 115200,
|
||||||
|
serial_hd(i), DEVICE_LITTLE_ENDIAN);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Flash Interface Unit (FIU). Can fail if incorrect number of chip selects
|
||||||
|
* specified, but this is a programming error.
|
||||||
|
*/
|
||||||
|
QEMU_BUILD_BUG_ON(ARRAY_SIZE(npcm7xx_fiu) != ARRAY_SIZE(s->fiu));
|
||||||
|
for (i = 0; i < ARRAY_SIZE(s->fiu); i++) {
|
||||||
|
SysBusDevice *sbd = SYS_BUS_DEVICE(&s->fiu[i]);
|
||||||
|
int j;
|
||||||
|
|
||||||
|
object_property_set_int(OBJECT(sbd), "cs-count",
|
||||||
|
npcm7xx_fiu[i].cs_count, &error_abort);
|
||||||
|
sysbus_realize(sbd, &error_abort);
|
||||||
|
|
||||||
|
sysbus_mmio_map(sbd, 0, npcm7xx_fiu[i].regs_addr);
|
||||||
|
for (j = 0; j < npcm7xx_fiu[i].cs_count; j++) {
|
||||||
|
sysbus_mmio_map(sbd, j + 1, npcm7xx_fiu[i].flash_addr[j]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* RAM2 (SRAM) */
|
||||||
|
memory_region_init_ram(&s->sram, OBJECT(dev), "ram2",
|
||||||
|
NPCM7XX_RAM2_SZ, &error_abort);
|
||||||
|
memory_region_add_subregion(get_system_memory(), NPCM7XX_RAM2_BA, &s->sram);
|
||||||
|
|
||||||
|
/* RAM3 (SRAM) */
|
||||||
|
memory_region_init_ram(&s->ram3, OBJECT(dev), "ram3",
|
||||||
|
NPCM7XX_RAM3_SZ, &error_abort);
|
||||||
|
memory_region_add_subregion(get_system_memory(), NPCM7XX_RAM3_BA, &s->ram3);
|
||||||
|
|
||||||
|
/* Internal ROM */
|
||||||
|
memory_region_init_rom(&s->irom, OBJECT(dev), "irom", NPCM7XX_ROM_SZ,
|
||||||
|
&error_abort);
|
||||||
|
memory_region_add_subregion(get_system_memory(), NPCM7XX_ROM_BA, &s->irom);
|
||||||
|
|
||||||
|
create_unimplemented_device("npcm7xx.shm", 0xc0001000, 4 * KiB);
|
||||||
|
create_unimplemented_device("npcm7xx.vdmx", 0xe0800000, 4 * KiB);
|
||||||
|
create_unimplemented_device("npcm7xx.pcierc", 0xe1000000, 64 * KiB);
|
||||||
|
create_unimplemented_device("npcm7xx.kcs", 0xf0007000, 4 * KiB);
|
||||||
|
create_unimplemented_device("npcm7xx.rng", 0xf000b000, 4 * KiB);
|
||||||
|
create_unimplemented_device("npcm7xx.adc", 0xf000c000, 4 * KiB);
|
||||||
|
create_unimplemented_device("npcm7xx.gfxi", 0xf000e000, 4 * KiB);
|
||||||
|
create_unimplemented_device("npcm7xx.gpio[0]", 0xf0010000, 4 * KiB);
|
||||||
|
create_unimplemented_device("npcm7xx.gpio[1]", 0xf0011000, 4 * KiB);
|
||||||
|
create_unimplemented_device("npcm7xx.gpio[2]", 0xf0012000, 4 * KiB);
|
||||||
|
create_unimplemented_device("npcm7xx.gpio[3]", 0xf0013000, 4 * KiB);
|
||||||
|
create_unimplemented_device("npcm7xx.gpio[4]", 0xf0014000, 4 * KiB);
|
||||||
|
create_unimplemented_device("npcm7xx.gpio[5]", 0xf0015000, 4 * KiB);
|
||||||
|
create_unimplemented_device("npcm7xx.gpio[6]", 0xf0016000, 4 * KiB);
|
||||||
|
create_unimplemented_device("npcm7xx.gpio[7]", 0xf0017000, 4 * KiB);
|
||||||
|
create_unimplemented_device("npcm7xx.smbus[0]", 0xf0080000, 4 * KiB);
|
||||||
|
create_unimplemented_device("npcm7xx.smbus[1]", 0xf0081000, 4 * KiB);
|
||||||
|
create_unimplemented_device("npcm7xx.smbus[2]", 0xf0082000, 4 * KiB);
|
||||||
|
create_unimplemented_device("npcm7xx.smbus[3]", 0xf0083000, 4 * KiB);
|
||||||
|
create_unimplemented_device("npcm7xx.smbus[4]", 0xf0084000, 4 * KiB);
|
||||||
|
create_unimplemented_device("npcm7xx.smbus[5]", 0xf0085000, 4 * KiB);
|
||||||
|
create_unimplemented_device("npcm7xx.smbus[6]", 0xf0086000, 4 * KiB);
|
||||||
|
create_unimplemented_device("npcm7xx.smbus[7]", 0xf0087000, 4 * KiB);
|
||||||
|
create_unimplemented_device("npcm7xx.smbus[8]", 0xf0088000, 4 * KiB);
|
||||||
|
create_unimplemented_device("npcm7xx.smbus[9]", 0xf0089000, 4 * KiB);
|
||||||
|
create_unimplemented_device("npcm7xx.smbus[10]", 0xf008a000, 4 * KiB);
|
||||||
|
create_unimplemented_device("npcm7xx.smbus[11]", 0xf008b000, 4 * KiB);
|
||||||
|
create_unimplemented_device("npcm7xx.smbus[12]", 0xf008c000, 4 * KiB);
|
||||||
|
create_unimplemented_device("npcm7xx.smbus[13]", 0xf008d000, 4 * KiB);
|
||||||
|
create_unimplemented_device("npcm7xx.smbus[14]", 0xf008e000, 4 * KiB);
|
||||||
|
create_unimplemented_device("npcm7xx.smbus[15]", 0xf008f000, 4 * KiB);
|
||||||
|
create_unimplemented_device("npcm7xx.espi", 0xf009f000, 4 * KiB);
|
||||||
|
create_unimplemented_device("npcm7xx.peci", 0xf0100000, 4 * KiB);
|
||||||
|
create_unimplemented_device("npcm7xx.siox[1]", 0xf0101000, 4 * KiB);
|
||||||
|
create_unimplemented_device("npcm7xx.siox[2]", 0xf0102000, 4 * KiB);
|
||||||
|
create_unimplemented_device("npcm7xx.pwm[0]", 0xf0103000, 4 * KiB);
|
||||||
|
create_unimplemented_device("npcm7xx.pwm[1]", 0xf0104000, 4 * KiB);
|
||||||
|
create_unimplemented_device("npcm7xx.mft[0]", 0xf0180000, 4 * KiB);
|
||||||
|
create_unimplemented_device("npcm7xx.mft[1]", 0xf0181000, 4 * KiB);
|
||||||
|
create_unimplemented_device("npcm7xx.mft[2]", 0xf0182000, 4 * KiB);
|
||||||
|
create_unimplemented_device("npcm7xx.mft[3]", 0xf0183000, 4 * KiB);
|
||||||
|
create_unimplemented_device("npcm7xx.mft[4]", 0xf0184000, 4 * KiB);
|
||||||
|
create_unimplemented_device("npcm7xx.mft[5]", 0xf0185000, 4 * KiB);
|
||||||
|
create_unimplemented_device("npcm7xx.mft[6]", 0xf0186000, 4 * KiB);
|
||||||
|
create_unimplemented_device("npcm7xx.mft[7]", 0xf0187000, 4 * KiB);
|
||||||
|
create_unimplemented_device("npcm7xx.pspi1", 0xf0200000, 4 * KiB);
|
||||||
|
create_unimplemented_device("npcm7xx.pspi2", 0xf0201000, 4 * KiB);
|
||||||
|
create_unimplemented_device("npcm7xx.ahbpci", 0xf0400000, 1 * MiB);
|
||||||
|
create_unimplemented_device("npcm7xx.mcphy", 0xf05f0000, 64 * KiB);
|
||||||
|
create_unimplemented_device("npcm7xx.gmac1", 0xf0802000, 8 * KiB);
|
||||||
|
create_unimplemented_device("npcm7xx.gmac2", 0xf0804000, 8 * KiB);
|
||||||
|
create_unimplemented_device("npcm7xx.ehci", 0xf0806000, 4 * KiB);
|
||||||
|
create_unimplemented_device("npcm7xx.ohci", 0xf0807000, 4 * KiB);
|
||||||
|
create_unimplemented_device("npcm7xx.vcd", 0xf0810000, 64 * KiB);
|
||||||
|
create_unimplemented_device("npcm7xx.ece", 0xf0820000, 8 * KiB);
|
||||||
|
create_unimplemented_device("npcm7xx.vdma", 0xf0822000, 8 * KiB);
|
||||||
|
create_unimplemented_device("npcm7xx.emc1", 0xf0825000, 4 * KiB);
|
||||||
|
create_unimplemented_device("npcm7xx.emc2", 0xf0826000, 4 * KiB);
|
||||||
|
create_unimplemented_device("npcm7xx.usbd[0]", 0xf0830000, 4 * KiB);
|
||||||
|
create_unimplemented_device("npcm7xx.usbd[1]", 0xf0831000, 4 * KiB);
|
||||||
|
create_unimplemented_device("npcm7xx.usbd[2]", 0xf0832000, 4 * KiB);
|
||||||
|
create_unimplemented_device("npcm7xx.usbd[3]", 0xf0833000, 4 * KiB);
|
||||||
|
create_unimplemented_device("npcm7xx.usbd[4]", 0xf0834000, 4 * KiB);
|
||||||
|
create_unimplemented_device("npcm7xx.usbd[5]", 0xf0835000, 4 * KiB);
|
||||||
|
create_unimplemented_device("npcm7xx.usbd[6]", 0xf0836000, 4 * KiB);
|
||||||
|
create_unimplemented_device("npcm7xx.usbd[7]", 0xf0837000, 4 * KiB);
|
||||||
|
create_unimplemented_device("npcm7xx.usbd[8]", 0xf0838000, 4 * KiB);
|
||||||
|
create_unimplemented_device("npcm7xx.usbd[9]", 0xf0839000, 4 * KiB);
|
||||||
|
create_unimplemented_device("npcm7xx.sd", 0xf0840000, 8 * KiB);
|
||||||
|
create_unimplemented_device("npcm7xx.mmc", 0xf0842000, 8 * KiB);
|
||||||
|
create_unimplemented_device("npcm7xx.pcimbx", 0xf0848000, 512 * KiB);
|
||||||
|
create_unimplemented_device("npcm7xx.aes", 0xf0858000, 4 * KiB);
|
||||||
|
create_unimplemented_device("npcm7xx.des", 0xf0859000, 4 * KiB);
|
||||||
|
create_unimplemented_device("npcm7xx.sha", 0xf085a000, 4 * KiB);
|
||||||
|
create_unimplemented_device("npcm7xx.secacc", 0xf085b000, 4 * KiB);
|
||||||
|
create_unimplemented_device("npcm7xx.spixcs0", 0xf8000000, 16 * MiB);
|
||||||
|
create_unimplemented_device("npcm7xx.spixcs1", 0xf9000000, 16 * MiB);
|
||||||
|
create_unimplemented_device("npcm7xx.spix", 0xfb001000, 4 * KiB);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Property npcm7xx_properties[] = {
|
||||||
|
DEFINE_PROP_LINK("dram-mr", NPCM7xxState, dram, TYPE_MEMORY_REGION,
|
||||||
|
MemoryRegion *),
|
||||||
|
DEFINE_PROP_END_OF_LIST(),
|
||||||
|
};
|
||||||
|
|
||||||
|
static void npcm7xx_class_init(ObjectClass *oc, void *data)
|
||||||
|
{
|
||||||
|
DeviceClass *dc = DEVICE_CLASS(oc);
|
||||||
|
|
||||||
|
dc->realize = npcm7xx_realize;
|
||||||
|
dc->user_creatable = false;
|
||||||
|
device_class_set_props(dc, npcm7xx_properties);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void npcm730_class_init(ObjectClass *oc, void *data)
|
||||||
|
{
|
||||||
|
NPCM7xxClass *nc = NPCM7XX_CLASS(oc);
|
||||||
|
|
||||||
|
/* NPCM730 is optimized for data center use, so no graphics, etc. */
|
||||||
|
nc->disabled_modules = 0x00300395;
|
||||||
|
nc->num_cpus = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void npcm750_class_init(ObjectClass *oc, void *data)
|
||||||
|
{
|
||||||
|
NPCM7xxClass *nc = NPCM7XX_CLASS(oc);
|
||||||
|
|
||||||
|
/* NPCM750 has 2 cores and a full set of peripherals */
|
||||||
|
nc->disabled_modules = 0x00000000;
|
||||||
|
nc->num_cpus = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const TypeInfo npcm7xx_soc_types[] = {
|
||||||
|
{
|
||||||
|
.name = TYPE_NPCM7XX,
|
||||||
|
.parent = TYPE_DEVICE,
|
||||||
|
.instance_size = sizeof(NPCM7xxState),
|
||||||
|
.instance_init = npcm7xx_init,
|
||||||
|
.class_size = sizeof(NPCM7xxClass),
|
||||||
|
.class_init = npcm7xx_class_init,
|
||||||
|
.abstract = true,
|
||||||
|
}, {
|
||||||
|
.name = TYPE_NPCM730,
|
||||||
|
.parent = TYPE_NPCM7XX,
|
||||||
|
.class_init = npcm730_class_init,
|
||||||
|
}, {
|
||||||
|
.name = TYPE_NPCM750,
|
||||||
|
.parent = TYPE_NPCM7XX,
|
||||||
|
.class_init = npcm750_class_init,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
DEFINE_TYPES(npcm7xx_soc_types);
|
|
@ -0,0 +1,197 @@
|
||||||
|
/*
|
||||||
|
* Machine definitions for boards featuring an NPCM7xx SoC.
|
||||||
|
*
|
||||||
|
* Copyright 2020 Google LLC
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "qemu/osdep.h"
|
||||||
|
|
||||||
|
#include "exec/address-spaces.h"
|
||||||
|
#include "hw/arm/npcm7xx.h"
|
||||||
|
#include "hw/core/cpu.h"
|
||||||
|
#include "hw/loader.h"
|
||||||
|
#include "hw/qdev-properties.h"
|
||||||
|
#include "qapi/error.h"
|
||||||
|
#include "qemu-common.h"
|
||||||
|
#include "qemu/units.h"
|
||||||
|
#include "sysemu/sysemu.h"
|
||||||
|
|
||||||
|
#define NPCM750_EVB_POWER_ON_STRAPS 0x00001ff7
|
||||||
|
#define QUANTA_GSJ_POWER_ON_STRAPS 0x00001fff
|
||||||
|
|
||||||
|
static const char npcm7xx_default_bootrom[] = "npcm7xx_bootrom.bin";
|
||||||
|
|
||||||
|
static void npcm7xx_load_bootrom(MachineState *machine, NPCM7xxState *soc)
|
||||||
|
{
|
||||||
|
g_autofree char *filename = NULL;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (!bios_name) {
|
||||||
|
bios_name = npcm7xx_default_bootrom;
|
||||||
|
}
|
||||||
|
|
||||||
|
filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name);
|
||||||
|
if (!filename) {
|
||||||
|
error_report("Could not find ROM image '%s'", bios_name);
|
||||||
|
if (!machine->kernel_filename) {
|
||||||
|
/* We can't boot without a bootrom or a kernel image. */
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ret = load_image_mr(filename, &soc->irom);
|
||||||
|
if (ret < 0) {
|
||||||
|
error_report("Failed to load ROM image '%s'", filename);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void npcm7xx_connect_flash(NPCM7xxFIUState *fiu, int cs_no,
|
||||||
|
const char *flash_type, DriveInfo *dinfo)
|
||||||
|
{
|
||||||
|
DeviceState *flash;
|
||||||
|
qemu_irq flash_cs;
|
||||||
|
|
||||||
|
flash = qdev_new(flash_type);
|
||||||
|
if (dinfo) {
|
||||||
|
qdev_prop_set_drive(flash, "drive", blk_by_legacy_dinfo(dinfo));
|
||||||
|
}
|
||||||
|
qdev_realize_and_unref(flash, BUS(fiu->spi), &error_fatal);
|
||||||
|
|
||||||
|
flash_cs = qdev_get_gpio_in_named(flash, SSI_GPIO_CS, 0);
|
||||||
|
qdev_connect_gpio_out_named(DEVICE(fiu), "cs", cs_no, flash_cs);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void npcm7xx_connect_dram(NPCM7xxState *soc, MemoryRegion *dram)
|
||||||
|
{
|
||||||
|
memory_region_add_subregion(get_system_memory(), NPCM7XX_DRAM_BA, dram);
|
||||||
|
|
||||||
|
object_property_set_link(OBJECT(soc), "dram-mr", OBJECT(dram),
|
||||||
|
&error_abort);
|
||||||
|
}
|
||||||
|
|
||||||
|
static NPCM7xxState *npcm7xx_create_soc(MachineState *machine,
|
||||||
|
uint32_t hw_straps)
|
||||||
|
{
|
||||||
|
NPCM7xxMachineClass *nmc = NPCM7XX_MACHINE_GET_CLASS(machine);
|
||||||
|
MachineClass *mc = &nmc->parent;
|
||||||
|
Object *obj;
|
||||||
|
|
||||||
|
if (strcmp(machine->cpu_type, mc->default_cpu_type) != 0) {
|
||||||
|
error_report("This board can only be used with %s",
|
||||||
|
mc->default_cpu_type);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
obj = object_new_with_props(nmc->soc_type, OBJECT(machine), "soc",
|
||||||
|
&error_abort, NULL);
|
||||||
|
object_property_set_uint(obj, "power-on-straps", hw_straps, &error_abort);
|
||||||
|
|
||||||
|
return NPCM7XX(obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void npcm750_evb_init(MachineState *machine)
|
||||||
|
{
|
||||||
|
NPCM7xxState *soc;
|
||||||
|
|
||||||
|
soc = npcm7xx_create_soc(machine, NPCM750_EVB_POWER_ON_STRAPS);
|
||||||
|
npcm7xx_connect_dram(soc, machine->ram);
|
||||||
|
qdev_realize(DEVICE(soc), NULL, &error_fatal);
|
||||||
|
|
||||||
|
npcm7xx_load_bootrom(machine, soc);
|
||||||
|
npcm7xx_connect_flash(&soc->fiu[0], 0, "w25q256", drive_get(IF_MTD, 0, 0));
|
||||||
|
npcm7xx_load_kernel(machine, soc);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void quanta_gsj_init(MachineState *machine)
|
||||||
|
{
|
||||||
|
NPCM7xxState *soc;
|
||||||
|
|
||||||
|
soc = npcm7xx_create_soc(machine, QUANTA_GSJ_POWER_ON_STRAPS);
|
||||||
|
npcm7xx_connect_dram(soc, machine->ram);
|
||||||
|
qdev_realize(DEVICE(soc), NULL, &error_fatal);
|
||||||
|
|
||||||
|
npcm7xx_load_bootrom(machine, soc);
|
||||||
|
npcm7xx_connect_flash(&soc->fiu[0], 0, "mx25l25635e",
|
||||||
|
drive_get(IF_MTD, 0, 0));
|
||||||
|
npcm7xx_load_kernel(machine, soc);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void npcm7xx_set_soc_type(NPCM7xxMachineClass *nmc, const char *type)
|
||||||
|
{
|
||||||
|
NPCM7xxClass *sc = NPCM7XX_CLASS(object_class_by_name(type));
|
||||||
|
MachineClass *mc = MACHINE_CLASS(nmc);
|
||||||
|
|
||||||
|
nmc->soc_type = type;
|
||||||
|
mc->default_cpus = mc->min_cpus = mc->max_cpus = sc->num_cpus;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void npcm7xx_machine_class_init(ObjectClass *oc, void *data)
|
||||||
|
{
|
||||||
|
MachineClass *mc = MACHINE_CLASS(oc);
|
||||||
|
|
||||||
|
mc->no_floppy = 1;
|
||||||
|
mc->no_cdrom = 1;
|
||||||
|
mc->no_parallel = 1;
|
||||||
|
mc->default_ram_id = "ram";
|
||||||
|
mc->default_cpu_type = ARM_CPU_TYPE_NAME("cortex-a9");
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Schematics:
|
||||||
|
* https://github.com/Nuvoton-Israel/nuvoton-info/blob/master/npcm7xx-poleg/evaluation-board/board_deliverables/NPCM750x_EB_ver.A1.1_COMPLETE.pdf
|
||||||
|
*/
|
||||||
|
static void npcm750_evb_machine_class_init(ObjectClass *oc, void *data)
|
||||||
|
{
|
||||||
|
NPCM7xxMachineClass *nmc = NPCM7XX_MACHINE_CLASS(oc);
|
||||||
|
MachineClass *mc = MACHINE_CLASS(oc);
|
||||||
|
|
||||||
|
npcm7xx_set_soc_type(nmc, TYPE_NPCM750);
|
||||||
|
|
||||||
|
mc->desc = "Nuvoton NPCM750 Evaluation Board (Cortex A9)";
|
||||||
|
mc->init = npcm750_evb_init;
|
||||||
|
mc->default_ram_size = 512 * MiB;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void gsj_machine_class_init(ObjectClass *oc, void *data)
|
||||||
|
{
|
||||||
|
NPCM7xxMachineClass *nmc = NPCM7XX_MACHINE_CLASS(oc);
|
||||||
|
MachineClass *mc = MACHINE_CLASS(oc);
|
||||||
|
|
||||||
|
npcm7xx_set_soc_type(nmc, TYPE_NPCM730);
|
||||||
|
|
||||||
|
mc->desc = "Quanta GSJ (Cortex A9)";
|
||||||
|
mc->init = quanta_gsj_init;
|
||||||
|
mc->default_ram_size = 512 * MiB;
|
||||||
|
};
|
||||||
|
|
||||||
|
static const TypeInfo npcm7xx_machine_types[] = {
|
||||||
|
{
|
||||||
|
.name = TYPE_NPCM7XX_MACHINE,
|
||||||
|
.parent = TYPE_MACHINE,
|
||||||
|
.instance_size = sizeof(NPCM7xxMachine),
|
||||||
|
.class_size = sizeof(NPCM7xxMachineClass),
|
||||||
|
.class_init = npcm7xx_machine_class_init,
|
||||||
|
.abstract = true,
|
||||||
|
}, {
|
||||||
|
.name = MACHINE_TYPE_NAME("npcm750-evb"),
|
||||||
|
.parent = TYPE_NPCM7XX_MACHINE,
|
||||||
|
.class_init = npcm750_evb_machine_class_init,
|
||||||
|
}, {
|
||||||
|
.name = MACHINE_TYPE_NAME("quanta-gsj"),
|
||||||
|
.parent = TYPE_NPCM7XX_MACHINE,
|
||||||
|
.class_init = gsj_machine_class_init,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
DEFINE_TYPES(npcm7xx_machine_types)
|
|
@ -214,7 +214,7 @@ static void fdt_add_gem_nodes(VersalVirt *s)
|
||||||
s->phandle.ethernet_phy[i]);
|
s->phandle.ethernet_phy[i]);
|
||||||
qemu_fdt_setprop_cells(s->fdt, name, "clocks",
|
qemu_fdt_setprop_cells(s->fdt, name, "clocks",
|
||||||
s->phandle.clk_25Mhz, s->phandle.clk_25Mhz,
|
s->phandle.clk_25Mhz, s->phandle.clk_25Mhz,
|
||||||
s->phandle.clk_25Mhz, s->phandle.clk_25Mhz);
|
s->phandle.clk_125Mhz, s->phandle.clk_125Mhz);
|
||||||
qemu_fdt_setprop(s->fdt, name, "clock-names",
|
qemu_fdt_setprop(s->fdt, name, "clock-names",
|
||||||
clocknames, sizeof(clocknames));
|
clocknames, sizeof(clocknames));
|
||||||
qemu_fdt_setprop_cells(s->fdt, name, "interrupts",
|
qemu_fdt_setprop_cells(s->fdt, name, "interrupts",
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
mem_ss = ss.source_set()
|
mem_ss = ss.source_set()
|
||||||
mem_ss.add(files('memory-device.c'))
|
mem_ss.add(files('memory-device.c'))
|
||||||
mem_ss.add(when: 'CONFIG_DIMM', if_true: files('pc-dimm.c'))
|
mem_ss.add(when: 'CONFIG_DIMM', if_true: files('pc-dimm.c'))
|
||||||
|
mem_ss.add(when: 'CONFIG_NPCM7XX', if_true: files('npcm7xx_mc.c'))
|
||||||
mem_ss.add(when: 'CONFIG_NVDIMM', if_true: files('nvdimm.c'))
|
mem_ss.add(when: 'CONFIG_NVDIMM', if_true: files('nvdimm.c'))
|
||||||
|
|
||||||
softmmu_ss.add_all(when: 'CONFIG_MEM_DEVICE', if_true: mem_ss)
|
softmmu_ss.add_all(when: 'CONFIG_MEM_DEVICE', if_true: mem_ss)
|
||||||
|
|
|
@ -0,0 +1,84 @@
|
||||||
|
/*
|
||||||
|
* Nuvoton NPCM7xx Memory Controller stub
|
||||||
|
*
|
||||||
|
* Copyright 2020 Google LLC
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "qemu/osdep.h"
|
||||||
|
|
||||||
|
#include "hw/mem/npcm7xx_mc.h"
|
||||||
|
#include "qapi/error.h"
|
||||||
|
#include "qemu/log.h"
|
||||||
|
#include "qemu/module.h"
|
||||||
|
#include "qemu/units.h"
|
||||||
|
|
||||||
|
#define NPCM7XX_MC_REGS_SIZE (4 * KiB)
|
||||||
|
|
||||||
|
static uint64_t npcm7xx_mc_read(void *opaque, hwaddr addr, unsigned int size)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* If bits 8..11 @ offset 0 are not zero, the boot block thinks the memory
|
||||||
|
* controller has already been initialized and will skip DDR training.
|
||||||
|
*/
|
||||||
|
if (addr == 0) {
|
||||||
|
return 0x100;
|
||||||
|
}
|
||||||
|
|
||||||
|
qemu_log_mask(LOG_UNIMP, "%s: mostly unimplemented\n", __func__);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void npcm7xx_mc_write(void *opaque, hwaddr addr, uint64_t v,
|
||||||
|
unsigned int size)
|
||||||
|
{
|
||||||
|
qemu_log_mask(LOG_UNIMP, "%s: mostly unimplemented\n", __func__);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const MemoryRegionOps npcm7xx_mc_ops = {
|
||||||
|
.read = npcm7xx_mc_read,
|
||||||
|
.write = npcm7xx_mc_write,
|
||||||
|
.endianness = DEVICE_LITTLE_ENDIAN,
|
||||||
|
.valid = {
|
||||||
|
.min_access_size = 4,
|
||||||
|
.max_access_size = 4,
|
||||||
|
.unaligned = false,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
static void npcm7xx_mc_realize(DeviceState *dev, Error **errp)
|
||||||
|
{
|
||||||
|
NPCM7xxMCState *s = NPCM7XX_MC(dev);
|
||||||
|
|
||||||
|
memory_region_init_io(&s->mmio, OBJECT(s), &npcm7xx_mc_ops, s, "regs",
|
||||||
|
NPCM7XX_MC_REGS_SIZE);
|
||||||
|
sysbus_init_mmio(&s->parent, &s->mmio);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void npcm7xx_mc_class_init(ObjectClass *klass, void *data)
|
||||||
|
{
|
||||||
|
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||||
|
|
||||||
|
dc->desc = "NPCM7xx Memory Controller stub";
|
||||||
|
dc->realize = npcm7xx_mc_realize;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const TypeInfo npcm7xx_mc_types[] = {
|
||||||
|
{
|
||||||
|
.name = TYPE_NPCM7XX_MC,
|
||||||
|
.parent = TYPE_SYS_BUS_DEVICE,
|
||||||
|
.instance_size = sizeof(NPCM7xxMCState),
|
||||||
|
.class_init = npcm7xx_mc_class_init,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
DEFINE_TYPES(npcm7xx_mc_types);
|
|
@ -12,8 +12,12 @@
|
||||||
#include "hw/misc/a9scu.h"
|
#include "hw/misc/a9scu.h"
|
||||||
#include "hw/qdev-properties.h"
|
#include "hw/qdev-properties.h"
|
||||||
#include "migration/vmstate.h"
|
#include "migration/vmstate.h"
|
||||||
|
#include "qapi/error.h"
|
||||||
|
#include "qemu/log.h"
|
||||||
#include "qemu/module.h"
|
#include "qemu/module.h"
|
||||||
|
|
||||||
|
#define A9_SCU_CPU_MAX 4
|
||||||
|
|
||||||
static uint64_t a9_scu_read(void *opaque, hwaddr offset,
|
static uint64_t a9_scu_read(void *opaque, hwaddr offset,
|
||||||
unsigned size)
|
unsigned size)
|
||||||
{
|
{
|
||||||
|
@ -25,12 +29,6 @@ static uint64_t a9_scu_read(void *opaque, hwaddr offset,
|
||||||
return (((1 << s->num_cpu) - 1) << 4) | (s->num_cpu - 1);
|
return (((1 << s->num_cpu) - 1) << 4) | (s->num_cpu - 1);
|
||||||
case 0x08: /* CPU Power Status */
|
case 0x08: /* CPU Power Status */
|
||||||
return s->status;
|
return s->status;
|
||||||
case 0x09: /* CPU status. */
|
|
||||||
return s->status >> 8;
|
|
||||||
case 0x0a: /* CPU status. */
|
|
||||||
return s->status >> 16;
|
|
||||||
case 0x0b: /* CPU status. */
|
|
||||||
return s->status >> 24;
|
|
||||||
case 0x0c: /* Invalidate All Registers In Secure State */
|
case 0x0c: /* Invalidate All Registers In Secure State */
|
||||||
return 0;
|
return 0;
|
||||||
case 0x40: /* Filtering Start Address Register */
|
case 0x40: /* Filtering Start Address Register */
|
||||||
|
@ -41,6 +39,8 @@ static uint64_t a9_scu_read(void *opaque, hwaddr offset,
|
||||||
case 0x54: /* SCU Non-secure Access Control Register */
|
case 0x54: /* SCU Non-secure Access Control Register */
|
||||||
/* unimplemented, fall through */
|
/* unimplemented, fall through */
|
||||||
default:
|
default:
|
||||||
|
qemu_log_mask(LOG_UNIMP, "%s: Unsupported offset 0x%"HWADDR_PRIx"\n",
|
||||||
|
__func__, offset);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -49,23 +49,6 @@ static void a9_scu_write(void *opaque, hwaddr offset,
|
||||||
uint64_t value, unsigned size)
|
uint64_t value, unsigned size)
|
||||||
{
|
{
|
||||||
A9SCUState *s = (A9SCUState *)opaque;
|
A9SCUState *s = (A9SCUState *)opaque;
|
||||||
uint32_t mask;
|
|
||||||
uint32_t shift;
|
|
||||||
switch (size) {
|
|
||||||
case 1:
|
|
||||||
mask = 0xff;
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
mask = 0xffff;
|
|
||||||
break;
|
|
||||||
case 4:
|
|
||||||
mask = 0xffffffff;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
fprintf(stderr, "Invalid size %u in write to a9 scu register %x\n",
|
|
||||||
size, (unsigned)offset);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (offset) {
|
switch (offset) {
|
||||||
case 0x00: /* Control */
|
case 0x00: /* Control */
|
||||||
|
@ -74,9 +57,7 @@ static void a9_scu_write(void *opaque, hwaddr offset,
|
||||||
case 0x4: /* Configuration: RO */
|
case 0x4: /* Configuration: RO */
|
||||||
break;
|
break;
|
||||||
case 0x08: case 0x09: case 0x0A: case 0x0B: /* Power Control */
|
case 0x08: case 0x09: case 0x0A: case 0x0B: /* Power Control */
|
||||||
shift = (offset - 0x8) * 8;
|
s->status = value;
|
||||||
s->status &= ~(mask << shift);
|
|
||||||
s->status |= ((value & mask) << shift);
|
|
||||||
break;
|
break;
|
||||||
case 0x0c: /* Invalidate All Registers In Secure State */
|
case 0x0c: /* Invalidate All Registers In Secure State */
|
||||||
/* no-op as we do not implement caches */
|
/* no-op as we do not implement caches */
|
||||||
|
@ -89,6 +70,9 @@ static void a9_scu_write(void *opaque, hwaddr offset,
|
||||||
case 0x54: /* SCU Non-secure Access Control Register */
|
case 0x54: /* SCU Non-secure Access Control Register */
|
||||||
/* unimplemented, fall through */
|
/* unimplemented, fall through */
|
||||||
default:
|
default:
|
||||||
|
qemu_log_mask(LOG_UNIMP, "%s: Unsupported offset 0x%"HWADDR_PRIx
|
||||||
|
" value 0x%"PRIx64"\n",
|
||||||
|
__func__, offset, value);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -96,6 +80,14 @@ static void a9_scu_write(void *opaque, hwaddr offset,
|
||||||
static const MemoryRegionOps a9_scu_ops = {
|
static const MemoryRegionOps a9_scu_ops = {
|
||||||
.read = a9_scu_read,
|
.read = a9_scu_read,
|
||||||
.write = a9_scu_write,
|
.write = a9_scu_write,
|
||||||
|
.impl = {
|
||||||
|
.min_access_size = 4,
|
||||||
|
.max_access_size = 4,
|
||||||
|
},
|
||||||
|
.valid = {
|
||||||
|
.min_access_size = 1,
|
||||||
|
.max_access_size = 4,
|
||||||
|
},
|
||||||
.endianness = DEVICE_NATIVE_ENDIAN,
|
.endianness = DEVICE_NATIVE_ENDIAN,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -105,12 +97,17 @@ static void a9_scu_reset(DeviceState *dev)
|
||||||
s->control = 0;
|
s->control = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void a9_scu_init(Object *obj)
|
static void a9_scu_realize(DeviceState *dev, Error **errp)
|
||||||
{
|
{
|
||||||
A9SCUState *s = A9_SCU(obj);
|
A9SCUState *s = A9_SCU(dev);
|
||||||
SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
|
SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
|
||||||
|
|
||||||
memory_region_init_io(&s->iomem, obj, &a9_scu_ops, s,
|
if (!s->num_cpu || s->num_cpu > A9_SCU_CPU_MAX) {
|
||||||
|
error_setg(errp, "Illegal CPU count: %u", s->num_cpu);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
memory_region_init_io(&s->iomem, OBJECT(s), &a9_scu_ops, s,
|
||||||
"a9-scu", 0x100);
|
"a9-scu", 0x100);
|
||||||
sysbus_init_mmio(sbd, &s->iomem);
|
sysbus_init_mmio(sbd, &s->iomem);
|
||||||
}
|
}
|
||||||
|
@ -138,13 +135,13 @@ static void a9_scu_class_init(ObjectClass *klass, void *data)
|
||||||
device_class_set_props(dc, a9_scu_properties);
|
device_class_set_props(dc, a9_scu_properties);
|
||||||
dc->vmsd = &vmstate_a9_scu;
|
dc->vmsd = &vmstate_a9_scu;
|
||||||
dc->reset = a9_scu_reset;
|
dc->reset = a9_scu_reset;
|
||||||
|
dc->realize = a9_scu_realize;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const TypeInfo a9_scu_info = {
|
static const TypeInfo a9_scu_info = {
|
||||||
.name = TYPE_A9_SCU,
|
.name = TYPE_A9_SCU,
|
||||||
.parent = TYPE_SYS_BUS_DEVICE,
|
.parent = TYPE_SYS_BUS_DEVICE,
|
||||||
.instance_size = sizeof(A9SCUState),
|
.instance_size = sizeof(A9SCUState),
|
||||||
.instance_init = a9_scu_init,
|
|
||||||
.class_init = a9_scu_class_init,
|
.class_init = a9_scu_class_init,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -56,6 +56,10 @@ softmmu_ss.add(when: 'CONFIG_IMX', if_true: files(
|
||||||
))
|
))
|
||||||
softmmu_ss.add(when: 'CONFIG_MILKYMIST', if_true: files('milkymist-hpdmc.c', 'milkymist-pfpu.c'))
|
softmmu_ss.add(when: 'CONFIG_MILKYMIST', if_true: files('milkymist-hpdmc.c', 'milkymist-pfpu.c'))
|
||||||
softmmu_ss.add(when: 'CONFIG_MAINSTONE', if_true: files('mst_fpga.c'))
|
softmmu_ss.add(when: 'CONFIG_MAINSTONE', if_true: files('mst_fpga.c'))
|
||||||
|
softmmu_ss.add(when: 'CONFIG_NPCM7XX', if_true: files(
|
||||||
|
'npcm7xx_clk.c',
|
||||||
|
'npcm7xx_gcr.c',
|
||||||
|
))
|
||||||
softmmu_ss.add(when: 'CONFIG_OMAP', if_true: files(
|
softmmu_ss.add(when: 'CONFIG_OMAP', if_true: files(
|
||||||
'omap_clk.c',
|
'omap_clk.c',
|
||||||
'omap_gpmc.c',
|
'omap_gpmc.c',
|
||||||
|
|
|
@ -0,0 +1,266 @@
|
||||||
|
/*
|
||||||
|
* Nuvoton NPCM7xx Clock Control Registers.
|
||||||
|
*
|
||||||
|
* Copyright 2020 Google LLC
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "qemu/osdep.h"
|
||||||
|
|
||||||
|
#include "hw/misc/npcm7xx_clk.h"
|
||||||
|
#include "migration/vmstate.h"
|
||||||
|
#include "qemu/error-report.h"
|
||||||
|
#include "qemu/log.h"
|
||||||
|
#include "qemu/module.h"
|
||||||
|
#include "qemu/timer.h"
|
||||||
|
#include "qemu/units.h"
|
||||||
|
#include "trace.h"
|
||||||
|
|
||||||
|
#define PLLCON_LOKI BIT(31)
|
||||||
|
#define PLLCON_LOKS BIT(30)
|
||||||
|
#define PLLCON_PWDEN BIT(12)
|
||||||
|
|
||||||
|
enum NPCM7xxCLKRegisters {
|
||||||
|
NPCM7XX_CLK_CLKEN1,
|
||||||
|
NPCM7XX_CLK_CLKSEL,
|
||||||
|
NPCM7XX_CLK_CLKDIV1,
|
||||||
|
NPCM7XX_CLK_PLLCON0,
|
||||||
|
NPCM7XX_CLK_PLLCON1,
|
||||||
|
NPCM7XX_CLK_SWRSTR,
|
||||||
|
NPCM7XX_CLK_IPSRST1 = 0x20 / sizeof(uint32_t),
|
||||||
|
NPCM7XX_CLK_IPSRST2,
|
||||||
|
NPCM7XX_CLK_CLKEN2,
|
||||||
|
NPCM7XX_CLK_CLKDIV2,
|
||||||
|
NPCM7XX_CLK_CLKEN3,
|
||||||
|
NPCM7XX_CLK_IPSRST3,
|
||||||
|
NPCM7XX_CLK_WD0RCR,
|
||||||
|
NPCM7XX_CLK_WD1RCR,
|
||||||
|
NPCM7XX_CLK_WD2RCR,
|
||||||
|
NPCM7XX_CLK_SWRSTC1,
|
||||||
|
NPCM7XX_CLK_SWRSTC2,
|
||||||
|
NPCM7XX_CLK_SWRSTC3,
|
||||||
|
NPCM7XX_CLK_SWRSTC4,
|
||||||
|
NPCM7XX_CLK_PLLCON2,
|
||||||
|
NPCM7XX_CLK_CLKDIV3,
|
||||||
|
NPCM7XX_CLK_CORSTC,
|
||||||
|
NPCM7XX_CLK_PLLCONG,
|
||||||
|
NPCM7XX_CLK_AHBCKFI,
|
||||||
|
NPCM7XX_CLK_SECCNT,
|
||||||
|
NPCM7XX_CLK_CNTR25M,
|
||||||
|
NPCM7XX_CLK_REGS_END,
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* These reset values were taken from version 0.91 of the NPCM750R data sheet.
|
||||||
|
*
|
||||||
|
* All are loaded on power-up reset. CLKENx and SWRSTR should also be loaded on
|
||||||
|
* core domain reset, but this reset type is not yet supported by QEMU.
|
||||||
|
*/
|
||||||
|
static const uint32_t cold_reset_values[NPCM7XX_CLK_NR_REGS] = {
|
||||||
|
[NPCM7XX_CLK_CLKEN1] = 0xffffffff,
|
||||||
|
[NPCM7XX_CLK_CLKSEL] = 0x004aaaaa,
|
||||||
|
[NPCM7XX_CLK_CLKDIV1] = 0x5413f855,
|
||||||
|
[NPCM7XX_CLK_PLLCON0] = 0x00222101 | PLLCON_LOKI,
|
||||||
|
[NPCM7XX_CLK_PLLCON1] = 0x00202101 | PLLCON_LOKI,
|
||||||
|
[NPCM7XX_CLK_IPSRST1] = 0x00001000,
|
||||||
|
[NPCM7XX_CLK_IPSRST2] = 0x80000000,
|
||||||
|
[NPCM7XX_CLK_CLKEN2] = 0xffffffff,
|
||||||
|
[NPCM7XX_CLK_CLKDIV2] = 0xaa4f8f9f,
|
||||||
|
[NPCM7XX_CLK_CLKEN3] = 0xffffffff,
|
||||||
|
[NPCM7XX_CLK_IPSRST3] = 0x03000000,
|
||||||
|
[NPCM7XX_CLK_WD0RCR] = 0xffffffff,
|
||||||
|
[NPCM7XX_CLK_WD1RCR] = 0xffffffff,
|
||||||
|
[NPCM7XX_CLK_WD2RCR] = 0xffffffff,
|
||||||
|
[NPCM7XX_CLK_SWRSTC1] = 0x00000003,
|
||||||
|
[NPCM7XX_CLK_PLLCON2] = 0x00c02105 | PLLCON_LOKI,
|
||||||
|
[NPCM7XX_CLK_CORSTC] = 0x04000003,
|
||||||
|
[NPCM7XX_CLK_PLLCONG] = 0x01228606 | PLLCON_LOKI,
|
||||||
|
[NPCM7XX_CLK_AHBCKFI] = 0x000000c8,
|
||||||
|
};
|
||||||
|
|
||||||
|
static uint64_t npcm7xx_clk_read(void *opaque, hwaddr offset, unsigned size)
|
||||||
|
{
|
||||||
|
uint32_t reg = offset / sizeof(uint32_t);
|
||||||
|
NPCM7xxCLKState *s = opaque;
|
||||||
|
int64_t now_ns;
|
||||||
|
uint32_t value = 0;
|
||||||
|
|
||||||
|
if (reg >= NPCM7XX_CLK_NR_REGS) {
|
||||||
|
qemu_log_mask(LOG_GUEST_ERROR,
|
||||||
|
"%s: offset 0x%04" HWADDR_PRIx " out of range\n",
|
||||||
|
__func__, offset);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (reg) {
|
||||||
|
case NPCM7XX_CLK_SWRSTR:
|
||||||
|
qemu_log_mask(LOG_GUEST_ERROR,
|
||||||
|
"%s: register @ 0x%04" HWADDR_PRIx " is write-only\n",
|
||||||
|
__func__, offset);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case NPCM7XX_CLK_SECCNT:
|
||||||
|
now_ns = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
|
||||||
|
value = (now_ns - s->ref_ns) / NANOSECONDS_PER_SECOND;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case NPCM7XX_CLK_CNTR25M:
|
||||||
|
now_ns = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
|
||||||
|
/*
|
||||||
|
* This register counts 25 MHz cycles, updating every 640 ns. It rolls
|
||||||
|
* over to zero every second.
|
||||||
|
*
|
||||||
|
* The 4 LSBs are always zero: (1e9 / 640) << 4 = 25000000.
|
||||||
|
*/
|
||||||
|
value = (((now_ns - s->ref_ns) / 640) << 4) % NPCM7XX_TIMER_REF_HZ;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
value = s->regs[reg];
|
||||||
|
break;
|
||||||
|
};
|
||||||
|
|
||||||
|
trace_npcm7xx_clk_read(offset, value);
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void npcm7xx_clk_write(void *opaque, hwaddr offset,
|
||||||
|
uint64_t v, unsigned size)
|
||||||
|
{
|
||||||
|
uint32_t reg = offset / sizeof(uint32_t);
|
||||||
|
NPCM7xxCLKState *s = opaque;
|
||||||
|
uint32_t value = v;
|
||||||
|
|
||||||
|
trace_npcm7xx_clk_write(offset, value);
|
||||||
|
|
||||||
|
if (reg >= NPCM7XX_CLK_NR_REGS) {
|
||||||
|
qemu_log_mask(LOG_GUEST_ERROR,
|
||||||
|
"%s: offset 0x%04" HWADDR_PRIx " out of range\n",
|
||||||
|
__func__, offset);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (reg) {
|
||||||
|
case NPCM7XX_CLK_SWRSTR:
|
||||||
|
qemu_log_mask(LOG_UNIMP, "%s: SW reset not implemented: 0x%02x\n",
|
||||||
|
__func__, value);
|
||||||
|
value = 0;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case NPCM7XX_CLK_PLLCON0:
|
||||||
|
case NPCM7XX_CLK_PLLCON1:
|
||||||
|
case NPCM7XX_CLK_PLLCON2:
|
||||||
|
case NPCM7XX_CLK_PLLCONG:
|
||||||
|
if (value & PLLCON_PWDEN) {
|
||||||
|
/* Power down -- clear lock and indicate loss of lock */
|
||||||
|
value &= ~PLLCON_LOKI;
|
||||||
|
value |= PLLCON_LOKS;
|
||||||
|
} else {
|
||||||
|
/* Normal mode -- assume always locked */
|
||||||
|
value |= PLLCON_LOKI;
|
||||||
|
/* Keep LOKS unchanged unless cleared by writing 1 */
|
||||||
|
if (value & PLLCON_LOKS) {
|
||||||
|
value &= ~PLLCON_LOKS;
|
||||||
|
} else {
|
||||||
|
value |= (value & PLLCON_LOKS);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case NPCM7XX_CLK_CNTR25M:
|
||||||
|
qemu_log_mask(LOG_GUEST_ERROR,
|
||||||
|
"%s: register @ 0x%04" HWADDR_PRIx " is read-only\n",
|
||||||
|
__func__, offset);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
s->regs[reg] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct MemoryRegionOps npcm7xx_clk_ops = {
|
||||||
|
.read = npcm7xx_clk_read,
|
||||||
|
.write = npcm7xx_clk_write,
|
||||||
|
.endianness = DEVICE_LITTLE_ENDIAN,
|
||||||
|
.valid = {
|
||||||
|
.min_access_size = 4,
|
||||||
|
.max_access_size = 4,
|
||||||
|
.unaligned = false,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
static void npcm7xx_clk_enter_reset(Object *obj, ResetType type)
|
||||||
|
{
|
||||||
|
NPCM7xxCLKState *s = NPCM7XX_CLK(obj);
|
||||||
|
|
||||||
|
QEMU_BUILD_BUG_ON(sizeof(s->regs) != sizeof(cold_reset_values));
|
||||||
|
|
||||||
|
switch (type) {
|
||||||
|
case RESET_TYPE_COLD:
|
||||||
|
memcpy(s->regs, cold_reset_values, sizeof(cold_reset_values));
|
||||||
|
s->ref_ns = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* A small number of registers need to be reset on a core domain reset,
|
||||||
|
* but no such reset type exists yet.
|
||||||
|
*/
|
||||||
|
qemu_log_mask(LOG_UNIMP, "%s: reset type %d not implemented.",
|
||||||
|
__func__, type);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void npcm7xx_clk_init(Object *obj)
|
||||||
|
{
|
||||||
|
NPCM7xxCLKState *s = NPCM7XX_CLK(obj);
|
||||||
|
|
||||||
|
memory_region_init_io(&s->iomem, obj, &npcm7xx_clk_ops, s,
|
||||||
|
TYPE_NPCM7XX_CLK, 4 * KiB);
|
||||||
|
sysbus_init_mmio(&s->parent, &s->iomem);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const VMStateDescription vmstate_npcm7xx_clk = {
|
||||||
|
.name = "npcm7xx-clk",
|
||||||
|
.version_id = 0,
|
||||||
|
.minimum_version_id = 0,
|
||||||
|
.fields = (VMStateField[]) {
|
||||||
|
VMSTATE_UINT32_ARRAY(regs, NPCM7xxCLKState, NPCM7XX_CLK_NR_REGS),
|
||||||
|
VMSTATE_INT64(ref_ns, NPCM7xxCLKState),
|
||||||
|
VMSTATE_END_OF_LIST(),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
static void npcm7xx_clk_class_init(ObjectClass *klass, void *data)
|
||||||
|
{
|
||||||
|
ResettableClass *rc = RESETTABLE_CLASS(klass);
|
||||||
|
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||||
|
|
||||||
|
QEMU_BUILD_BUG_ON(NPCM7XX_CLK_REGS_END > NPCM7XX_CLK_NR_REGS);
|
||||||
|
|
||||||
|
dc->desc = "NPCM7xx Clock Control Registers";
|
||||||
|
dc->vmsd = &vmstate_npcm7xx_clk;
|
||||||
|
rc->phases.enter = npcm7xx_clk_enter_reset;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const TypeInfo npcm7xx_clk_info = {
|
||||||
|
.name = TYPE_NPCM7XX_CLK,
|
||||||
|
.parent = TYPE_SYS_BUS_DEVICE,
|
||||||
|
.instance_size = sizeof(NPCM7xxCLKState),
|
||||||
|
.instance_init = npcm7xx_clk_init,
|
||||||
|
.class_init = npcm7xx_clk_class_init,
|
||||||
|
};
|
||||||
|
|
||||||
|
static void npcm7xx_clk_register_type(void)
|
||||||
|
{
|
||||||
|
type_register_static(&npcm7xx_clk_info);
|
||||||
|
}
|
||||||
|
type_init(npcm7xx_clk_register_type);
|
|
@ -0,0 +1,269 @@
|
||||||
|
/*
|
||||||
|
* Nuvoton NPCM7xx System Global Control Registers.
|
||||||
|
*
|
||||||
|
* Copyright 2020 Google LLC
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "qemu/osdep.h"
|
||||||
|
|
||||||
|
#include "hw/misc/npcm7xx_gcr.h"
|
||||||
|
#include "hw/qdev-properties.h"
|
||||||
|
#include "migration/vmstate.h"
|
||||||
|
#include "qapi/error.h"
|
||||||
|
#include "qemu/cutils.h"
|
||||||
|
#include "qemu/log.h"
|
||||||
|
#include "qemu/module.h"
|
||||||
|
#include "qemu/units.h"
|
||||||
|
|
||||||
|
#include "trace.h"
|
||||||
|
|
||||||
|
#define NPCM7XX_GCR_MIN_DRAM_SIZE (128 * MiB)
|
||||||
|
#define NPCM7XX_GCR_MAX_DRAM_SIZE (2 * GiB)
|
||||||
|
|
||||||
|
enum NPCM7xxGCRRegisters {
|
||||||
|
NPCM7XX_GCR_PDID,
|
||||||
|
NPCM7XX_GCR_PWRON,
|
||||||
|
NPCM7XX_GCR_MFSEL1 = 0x0c / sizeof(uint32_t),
|
||||||
|
NPCM7XX_GCR_MFSEL2,
|
||||||
|
NPCM7XX_GCR_MISCPE,
|
||||||
|
NPCM7XX_GCR_SPSWC = 0x038 / sizeof(uint32_t),
|
||||||
|
NPCM7XX_GCR_INTCR,
|
||||||
|
NPCM7XX_GCR_INTSR,
|
||||||
|
NPCM7XX_GCR_HIFCR = 0x050 / sizeof(uint32_t),
|
||||||
|
NPCM7XX_GCR_INTCR2 = 0x060 / sizeof(uint32_t),
|
||||||
|
NPCM7XX_GCR_MFSEL3,
|
||||||
|
NPCM7XX_GCR_SRCNT,
|
||||||
|
NPCM7XX_GCR_RESSR,
|
||||||
|
NPCM7XX_GCR_RLOCKR1,
|
||||||
|
NPCM7XX_GCR_FLOCKR1,
|
||||||
|
NPCM7XX_GCR_DSCNT,
|
||||||
|
NPCM7XX_GCR_MDLR,
|
||||||
|
NPCM7XX_GCR_SCRPAD3,
|
||||||
|
NPCM7XX_GCR_SCRPAD2,
|
||||||
|
NPCM7XX_GCR_DAVCLVLR = 0x098 / sizeof(uint32_t),
|
||||||
|
NPCM7XX_GCR_INTCR3,
|
||||||
|
NPCM7XX_GCR_VSINTR = 0x0ac / sizeof(uint32_t),
|
||||||
|
NPCM7XX_GCR_MFSEL4,
|
||||||
|
NPCM7XX_GCR_CPBPNTR = 0x0c4 / sizeof(uint32_t),
|
||||||
|
NPCM7XX_GCR_CPCTL = 0x0d0 / sizeof(uint32_t),
|
||||||
|
NPCM7XX_GCR_CP2BST,
|
||||||
|
NPCM7XX_GCR_B2CPNT,
|
||||||
|
NPCM7XX_GCR_CPPCTL,
|
||||||
|
NPCM7XX_GCR_I2CSEGSEL,
|
||||||
|
NPCM7XX_GCR_I2CSEGCTL,
|
||||||
|
NPCM7XX_GCR_VSRCR,
|
||||||
|
NPCM7XX_GCR_MLOCKR,
|
||||||
|
NPCM7XX_GCR_SCRPAD = 0x013c / sizeof(uint32_t),
|
||||||
|
NPCM7XX_GCR_USB1PHYCTL,
|
||||||
|
NPCM7XX_GCR_USB2PHYCTL,
|
||||||
|
NPCM7XX_GCR_REGS_END,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const uint32_t cold_reset_values[NPCM7XX_GCR_NR_REGS] = {
|
||||||
|
[NPCM7XX_GCR_PDID] = 0x04a92750, /* Poleg A1 */
|
||||||
|
[NPCM7XX_GCR_MISCPE] = 0x0000ffff,
|
||||||
|
[NPCM7XX_GCR_SPSWC] = 0x00000003,
|
||||||
|
[NPCM7XX_GCR_INTCR] = 0x0000035e,
|
||||||
|
[NPCM7XX_GCR_HIFCR] = 0x0000004e,
|
||||||
|
[NPCM7XX_GCR_INTCR2] = (1U << 19), /* DDR initialized */
|
||||||
|
[NPCM7XX_GCR_RESSR] = 0x80000000,
|
||||||
|
[NPCM7XX_GCR_DSCNT] = 0x000000c0,
|
||||||
|
[NPCM7XX_GCR_DAVCLVLR] = 0x5a00f3cf,
|
||||||
|
[NPCM7XX_GCR_SCRPAD] = 0x00000008,
|
||||||
|
[NPCM7XX_GCR_USB1PHYCTL] = 0x034730e4,
|
||||||
|
[NPCM7XX_GCR_USB2PHYCTL] = 0x034730e4,
|
||||||
|
};
|
||||||
|
|
||||||
|
static uint64_t npcm7xx_gcr_read(void *opaque, hwaddr offset, unsigned size)
|
||||||
|
{
|
||||||
|
uint32_t reg = offset / sizeof(uint32_t);
|
||||||
|
NPCM7xxGCRState *s = opaque;
|
||||||
|
|
||||||
|
if (reg >= NPCM7XX_GCR_NR_REGS) {
|
||||||
|
qemu_log_mask(LOG_GUEST_ERROR,
|
||||||
|
"%s: offset 0x%04" HWADDR_PRIx " out of range\n",
|
||||||
|
__func__, offset);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
trace_npcm7xx_gcr_read(offset, s->regs[reg]);
|
||||||
|
|
||||||
|
return s->regs[reg];
|
||||||
|
}
|
||||||
|
|
||||||
|
static void npcm7xx_gcr_write(void *opaque, hwaddr offset,
|
||||||
|
uint64_t v, unsigned size)
|
||||||
|
{
|
||||||
|
uint32_t reg = offset / sizeof(uint32_t);
|
||||||
|
NPCM7xxGCRState *s = opaque;
|
||||||
|
uint32_t value = v;
|
||||||
|
|
||||||
|
trace_npcm7xx_gcr_write(offset, value);
|
||||||
|
|
||||||
|
if (reg >= NPCM7XX_GCR_NR_REGS) {
|
||||||
|
qemu_log_mask(LOG_GUEST_ERROR,
|
||||||
|
"%s: offset 0x%04" HWADDR_PRIx " out of range\n",
|
||||||
|
__func__, offset);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (reg) {
|
||||||
|
case NPCM7XX_GCR_PDID:
|
||||||
|
case NPCM7XX_GCR_PWRON:
|
||||||
|
case NPCM7XX_GCR_INTSR:
|
||||||
|
qemu_log_mask(LOG_GUEST_ERROR,
|
||||||
|
"%s: register @ 0x%04" HWADDR_PRIx " is read-only\n",
|
||||||
|
__func__, offset);
|
||||||
|
return;
|
||||||
|
|
||||||
|
case NPCM7XX_GCR_RESSR:
|
||||||
|
case NPCM7XX_GCR_CP2BST:
|
||||||
|
/* Write 1 to clear */
|
||||||
|
value = s->regs[reg] & ~value;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case NPCM7XX_GCR_RLOCKR1:
|
||||||
|
case NPCM7XX_GCR_MDLR:
|
||||||
|
/* Write 1 to set */
|
||||||
|
value |= s->regs[reg];
|
||||||
|
break;
|
||||||
|
};
|
||||||
|
|
||||||
|
s->regs[reg] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct MemoryRegionOps npcm7xx_gcr_ops = {
|
||||||
|
.read = npcm7xx_gcr_read,
|
||||||
|
.write = npcm7xx_gcr_write,
|
||||||
|
.endianness = DEVICE_LITTLE_ENDIAN,
|
||||||
|
.valid = {
|
||||||
|
.min_access_size = 4,
|
||||||
|
.max_access_size = 4,
|
||||||
|
.unaligned = false,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
static void npcm7xx_gcr_enter_reset(Object *obj, ResetType type)
|
||||||
|
{
|
||||||
|
NPCM7xxGCRState *s = NPCM7XX_GCR(obj);
|
||||||
|
|
||||||
|
QEMU_BUILD_BUG_ON(sizeof(s->regs) != sizeof(cold_reset_values));
|
||||||
|
|
||||||
|
switch (type) {
|
||||||
|
case RESET_TYPE_COLD:
|
||||||
|
memcpy(s->regs, cold_reset_values, sizeof(s->regs));
|
||||||
|
s->regs[NPCM7XX_GCR_PWRON] = s->reset_pwron;
|
||||||
|
s->regs[NPCM7XX_GCR_MDLR] = s->reset_mdlr;
|
||||||
|
s->regs[NPCM7XX_GCR_INTCR3] = s->reset_intcr3;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void npcm7xx_gcr_realize(DeviceState *dev, Error **errp)
|
||||||
|
{
|
||||||
|
ERRP_GUARD();
|
||||||
|
NPCM7xxGCRState *s = NPCM7XX_GCR(dev);
|
||||||
|
uint64_t dram_size;
|
||||||
|
Object *obj;
|
||||||
|
|
||||||
|
obj = object_property_get_link(OBJECT(dev), "dram-mr", errp);
|
||||||
|
if (!obj) {
|
||||||
|
error_prepend(errp, "%s: required dram-mr link not found: ", __func__);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
dram_size = memory_region_size(MEMORY_REGION(obj));
|
||||||
|
if (!is_power_of_2(dram_size) ||
|
||||||
|
dram_size < NPCM7XX_GCR_MIN_DRAM_SIZE ||
|
||||||
|
dram_size > NPCM7XX_GCR_MAX_DRAM_SIZE) {
|
||||||
|
g_autofree char *sz = size_to_str(dram_size);
|
||||||
|
g_autofree char *min_sz = size_to_str(NPCM7XX_GCR_MIN_DRAM_SIZE);
|
||||||
|
g_autofree char *max_sz = size_to_str(NPCM7XX_GCR_MAX_DRAM_SIZE);
|
||||||
|
error_setg(errp, "%s: unsupported DRAM size %s", __func__, sz);
|
||||||
|
error_append_hint(errp,
|
||||||
|
"DRAM size must be a power of two between %s and %s,"
|
||||||
|
" inclusive.\n", min_sz, max_sz);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Power-on reset value */
|
||||||
|
s->reset_intcr3 = 0x00001002;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The GMMAP (Graphics Memory Map) field is used by u-boot to detect the
|
||||||
|
* DRAM size, and is normally initialized by the boot block as part of DRAM
|
||||||
|
* training. However, since we don't have a complete emulation of the
|
||||||
|
* memory controller and try to make it look like it has already been
|
||||||
|
* initialized, the boot block will skip this initialization, and we need
|
||||||
|
* to make sure this field is set correctly up front.
|
||||||
|
*
|
||||||
|
* WARNING: some versions of u-boot only looks at bits 8 and 9, so 2 GiB of
|
||||||
|
* DRAM will be interpreted as 128 MiB.
|
||||||
|
*
|
||||||
|
* https://github.com/Nuvoton-Israel/u-boot/blob/2aef993bd2aafeb5408dbaad0f3ce099ee40c4aa/board/nuvoton/poleg/poleg.c#L244
|
||||||
|
*/
|
||||||
|
s->reset_intcr3 |= ctz64(dram_size / NPCM7XX_GCR_MIN_DRAM_SIZE) << 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void npcm7xx_gcr_init(Object *obj)
|
||||||
|
{
|
||||||
|
NPCM7xxGCRState *s = NPCM7XX_GCR(obj);
|
||||||
|
|
||||||
|
memory_region_init_io(&s->iomem, obj, &npcm7xx_gcr_ops, s,
|
||||||
|
TYPE_NPCM7XX_GCR, 4 * KiB);
|
||||||
|
sysbus_init_mmio(&s->parent, &s->iomem);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const VMStateDescription vmstate_npcm7xx_gcr = {
|
||||||
|
.name = "npcm7xx-gcr",
|
||||||
|
.version_id = 0,
|
||||||
|
.minimum_version_id = 0,
|
||||||
|
.fields = (VMStateField[]) {
|
||||||
|
VMSTATE_UINT32_ARRAY(regs, NPCM7xxGCRState, NPCM7XX_GCR_NR_REGS),
|
||||||
|
VMSTATE_END_OF_LIST(),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
static Property npcm7xx_gcr_properties[] = {
|
||||||
|
DEFINE_PROP_UINT32("disabled-modules", NPCM7xxGCRState, reset_mdlr, 0),
|
||||||
|
DEFINE_PROP_UINT32("power-on-straps", NPCM7xxGCRState, reset_pwron, 0),
|
||||||
|
DEFINE_PROP_END_OF_LIST(),
|
||||||
|
};
|
||||||
|
|
||||||
|
static void npcm7xx_gcr_class_init(ObjectClass *klass, void *data)
|
||||||
|
{
|
||||||
|
ResettableClass *rc = RESETTABLE_CLASS(klass);
|
||||||
|
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||||
|
|
||||||
|
QEMU_BUILD_BUG_ON(NPCM7XX_GCR_REGS_END > NPCM7XX_GCR_NR_REGS);
|
||||||
|
|
||||||
|
dc->desc = "NPCM7xx System Global Control Registers";
|
||||||
|
dc->realize = npcm7xx_gcr_realize;
|
||||||
|
dc->vmsd = &vmstate_npcm7xx_gcr;
|
||||||
|
rc->phases.enter = npcm7xx_gcr_enter_reset;
|
||||||
|
|
||||||
|
device_class_set_props(dc, npcm7xx_gcr_properties);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const TypeInfo npcm7xx_gcr_info = {
|
||||||
|
.name = TYPE_NPCM7XX_GCR,
|
||||||
|
.parent = TYPE_SYS_BUS_DEVICE,
|
||||||
|
.instance_size = sizeof(NPCM7xxGCRState),
|
||||||
|
.instance_init = npcm7xx_gcr_init,
|
||||||
|
.class_init = npcm7xx_gcr_class_init,
|
||||||
|
};
|
||||||
|
|
||||||
|
static void npcm7xx_gcr_register_type(void)
|
||||||
|
{
|
||||||
|
type_register_static(&npcm7xx_gcr_info);
|
||||||
|
}
|
||||||
|
type_init(npcm7xx_gcr_register_type);
|
|
@ -110,6 +110,14 @@ mos6522_set_sr_int(void) "set sr_int"
|
||||||
mos6522_write(uint64_t addr, uint64_t val) "reg=0x%"PRIx64 " val=0x%"PRIx64
|
mos6522_write(uint64_t addr, uint64_t val) "reg=0x%"PRIx64 " val=0x%"PRIx64
|
||||||
mos6522_read(uint64_t addr, unsigned val) "reg=0x%"PRIx64 " val=0x%x"
|
mos6522_read(uint64_t addr, unsigned val) "reg=0x%"PRIx64 " val=0x%x"
|
||||||
|
|
||||||
|
# npcm7xx_clk.c
|
||||||
|
npcm7xx_clk_read(uint64_t offset, uint32_t value) " offset: 0x%04" PRIx64 " value: 0x%08" PRIx32
|
||||||
|
npcm7xx_clk_write(uint64_t offset, uint32_t value) "offset: 0x%04" PRIx64 " value: 0x%08" PRIx32
|
||||||
|
|
||||||
|
# npcm7xx_gcr.c
|
||||||
|
npcm7xx_gcr_read(uint64_t offset, uint32_t value) " offset: 0x%04" PRIx64 " value: 0x%08" PRIx32
|
||||||
|
npcm7xx_gcr_write(uint64_t offset, uint32_t value) "offset: 0x%04" PRIx64 " value: 0x%08" PRIx32
|
||||||
|
|
||||||
# stm32f4xx_syscfg.c
|
# stm32f4xx_syscfg.c
|
||||||
stm32f4xx_syscfg_set_irq(int gpio, int line, int level) "Interupt: GPIO: %d, Line: %d; Level: %d"
|
stm32f4xx_syscfg_set_irq(int gpio, int line, int level) "Interupt: GPIO: %d, Line: %d; Level: %d"
|
||||||
stm32f4xx_pulse_exti(int irq) "Pulse EXTI: %d"
|
stm32f4xx_pulse_exti(int irq) "Pulse EXTI: %d"
|
||||||
|
|
|
@ -4,6 +4,7 @@ softmmu_ss.add(when: 'CONFIG_DS1225Y', if_true: files('ds1225y.c'))
|
||||||
softmmu_ss.add(when: 'CONFIG_NMC93XX_EEPROM', if_true: files('eeprom93xx.c'))
|
softmmu_ss.add(when: 'CONFIG_NMC93XX_EEPROM', if_true: files('eeprom93xx.c'))
|
||||||
softmmu_ss.add(when: 'CONFIG_AT24C', if_true: files('eeprom_at24c.c'))
|
softmmu_ss.add(when: 'CONFIG_AT24C', if_true: files('eeprom_at24c.c'))
|
||||||
softmmu_ss.add(when: 'CONFIG_MAC_NVRAM', if_true: files('mac_nvram.c'))
|
softmmu_ss.add(when: 'CONFIG_MAC_NVRAM', if_true: files('mac_nvram.c'))
|
||||||
|
softmmu_ss.add(when: 'CONFIG_NPCM7XX', if_true: files('npcm7xx_otp.c'))
|
||||||
softmmu_ss.add(when: 'CONFIG_NRF51_SOC', if_true: files('nrf51_nvm.c'))
|
softmmu_ss.add(when: 'CONFIG_NRF51_SOC', if_true: files('nrf51_nvm.c'))
|
||||||
|
|
||||||
specific_ss.add(when: 'CONFIG_PSERIES', if_true: files('spapr_nvram.c'))
|
specific_ss.add(when: 'CONFIG_PSERIES', if_true: files('spapr_nvram.c'))
|
||||||
|
|
|
@ -0,0 +1,440 @@
|
||||||
|
/*
|
||||||
|
* Nuvoton NPCM7xx OTP (Fuse Array) Interface
|
||||||
|
*
|
||||||
|
* Copyright 2020 Google LLC
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "qemu/osdep.h"
|
||||||
|
|
||||||
|
#include "hw/nvram/npcm7xx_otp.h"
|
||||||
|
#include "migration/vmstate.h"
|
||||||
|
#include "qapi/error.h"
|
||||||
|
#include "qemu/bitops.h"
|
||||||
|
#include "qemu/log.h"
|
||||||
|
#include "qemu/module.h"
|
||||||
|
#include "qemu/units.h"
|
||||||
|
|
||||||
|
/* Each module has 4 KiB of register space. Only a fraction of it is used. */
|
||||||
|
#define NPCM7XX_OTP_REGS_SIZE (4 * KiB)
|
||||||
|
|
||||||
|
/* 32-bit register indices. */
|
||||||
|
typedef enum NPCM7xxOTPRegister {
|
||||||
|
NPCM7XX_OTP_FST,
|
||||||
|
NPCM7XX_OTP_FADDR,
|
||||||
|
NPCM7XX_OTP_FDATA,
|
||||||
|
NPCM7XX_OTP_FCFG,
|
||||||
|
/* Offset 0x10 is FKEYIND in OTP1, FUSTRAP in OTP2 */
|
||||||
|
NPCM7XX_OTP_FKEYIND = 0x0010 / sizeof(uint32_t),
|
||||||
|
NPCM7XX_OTP_FUSTRAP = 0x0010 / sizeof(uint32_t),
|
||||||
|
NPCM7XX_OTP_FCTL,
|
||||||
|
NPCM7XX_OTP_REGS_END,
|
||||||
|
} NPCM7xxOTPRegister;
|
||||||
|
|
||||||
|
/* Register field definitions. */
|
||||||
|
#define FST_RIEN BIT(2)
|
||||||
|
#define FST_RDST BIT(1)
|
||||||
|
#define FST_RDY BIT(0)
|
||||||
|
#define FST_RO_MASK (FST_RDST | FST_RDY)
|
||||||
|
|
||||||
|
#define FADDR_BYTEADDR(rv) extract32((rv), 0, 10)
|
||||||
|
#define FADDR_BITPOS(rv) extract32((rv), 10, 3)
|
||||||
|
|
||||||
|
#define FDATA_CLEAR 0x00000001
|
||||||
|
|
||||||
|
#define FCFG_FDIS BIT(31)
|
||||||
|
#define FCFG_FCFGLK_MASK 0x00ff0000
|
||||||
|
|
||||||
|
#define FCTL_PROG_CMD1 0x00000001
|
||||||
|
#define FCTL_PROG_CMD2 0xbf79e5d0
|
||||||
|
#define FCTL_READ_CMD 0x00000002
|
||||||
|
|
||||||
|
/**
|
||||||
|
* struct NPCM7xxOTPClass - OTP module class.
|
||||||
|
* @parent: System bus device class.
|
||||||
|
* @mmio_ops: MMIO register operations for this type of module.
|
||||||
|
*
|
||||||
|
* The two OTP modules (key-storage and fuse-array) have slightly different
|
||||||
|
* behavior, so we give them different MMIO register operations.
|
||||||
|
*/
|
||||||
|
struct NPCM7xxOTPClass {
|
||||||
|
SysBusDeviceClass parent;
|
||||||
|
|
||||||
|
const MemoryRegionOps *mmio_ops;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define NPCM7XX_OTP_CLASS(klass) \
|
||||||
|
OBJECT_CLASS_CHECK(NPCM7xxOTPClass, (klass), TYPE_NPCM7XX_OTP)
|
||||||
|
#define NPCM7XX_OTP_GET_CLASS(obj) \
|
||||||
|
OBJECT_GET_CLASS(NPCM7xxOTPClass, (obj), TYPE_NPCM7XX_OTP)
|
||||||
|
|
||||||
|
static uint8_t ecc_encode_nibble(uint8_t n)
|
||||||
|
{
|
||||||
|
uint8_t result = n;
|
||||||
|
|
||||||
|
result |= (((n >> 0) & 1) ^ ((n >> 1) & 1)) << 4;
|
||||||
|
result |= (((n >> 2) & 1) ^ ((n >> 3) & 1)) << 5;
|
||||||
|
result |= (((n >> 0) & 1) ^ ((n >> 2) & 1)) << 6;
|
||||||
|
result |= (((n >> 1) & 1) ^ ((n >> 3) & 1)) << 7;
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void npcm7xx_otp_array_write(NPCM7xxOTPState *s, const void *data,
|
||||||
|
unsigned int offset, unsigned int len)
|
||||||
|
{
|
||||||
|
const uint8_t *src = data;
|
||||||
|
uint8_t *dst = &s->array[offset];
|
||||||
|
|
||||||
|
while (len-- > 0) {
|
||||||
|
uint8_t c = *src++;
|
||||||
|
|
||||||
|
*dst++ = ecc_encode_nibble(extract8(c, 0, 4));
|
||||||
|
*dst++ = ecc_encode_nibble(extract8(c, 4, 4));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Common register read handler for both OTP classes. */
|
||||||
|
static uint64_t npcm7xx_otp_read(NPCM7xxOTPState *s, NPCM7xxOTPRegister reg)
|
||||||
|
{
|
||||||
|
uint32_t value = 0;
|
||||||
|
|
||||||
|
switch (reg) {
|
||||||
|
case NPCM7XX_OTP_FST:
|
||||||
|
case NPCM7XX_OTP_FADDR:
|
||||||
|
case NPCM7XX_OTP_FDATA:
|
||||||
|
case NPCM7XX_OTP_FCFG:
|
||||||
|
value = s->regs[reg];
|
||||||
|
break;
|
||||||
|
|
||||||
|
case NPCM7XX_OTP_FCTL:
|
||||||
|
qemu_log_mask(LOG_GUEST_ERROR,
|
||||||
|
"%s: read from write-only FCTL register\n",
|
||||||
|
DEVICE(s)->canonical_path);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
qemu_log_mask(LOG_GUEST_ERROR, "%s: read from invalid offset 0x%zx\n",
|
||||||
|
DEVICE(s)->canonical_path, reg * sizeof(uint32_t));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Read a byte from the OTP array into the data register. */
|
||||||
|
static void npcm7xx_otp_read_array(NPCM7xxOTPState *s)
|
||||||
|
{
|
||||||
|
uint32_t faddr = s->regs[NPCM7XX_OTP_FADDR];
|
||||||
|
|
||||||
|
s->regs[NPCM7XX_OTP_FDATA] = s->array[FADDR_BYTEADDR(faddr)];
|
||||||
|
s->regs[NPCM7XX_OTP_FST] |= FST_RDST | FST_RDY;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Program a byte from the data register into the OTP array. */
|
||||||
|
static void npcm7xx_otp_program_array(NPCM7xxOTPState *s)
|
||||||
|
{
|
||||||
|
uint32_t faddr = s->regs[NPCM7XX_OTP_FADDR];
|
||||||
|
|
||||||
|
/* Bits can only go 0->1, never 1->0. */
|
||||||
|
s->array[FADDR_BYTEADDR(faddr)] |= (1U << FADDR_BITPOS(faddr));
|
||||||
|
s->regs[NPCM7XX_OTP_FST] |= FST_RDST | FST_RDY;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Compute the next value of the FCFG register. */
|
||||||
|
static uint32_t npcm7xx_otp_compute_fcfg(uint32_t cur_value, uint32_t new_value)
|
||||||
|
{
|
||||||
|
uint32_t lock_mask;
|
||||||
|
uint32_t value;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* FCFGLK holds sticky bits 16..23, indicating which bits in FPRGLK (8..15)
|
||||||
|
* and FRDLK (0..7) that are read-only.
|
||||||
|
*/
|
||||||
|
lock_mask = (cur_value & FCFG_FCFGLK_MASK) >> 8;
|
||||||
|
lock_mask |= lock_mask >> 8;
|
||||||
|
/* FDIS and FCFGLK bits are sticky (write 1 to set; can't clear). */
|
||||||
|
value = cur_value & (FCFG_FDIS | FCFG_FCFGLK_MASK);
|
||||||
|
/* Preserve read-only bits in FPRGLK and FRDLK */
|
||||||
|
value |= cur_value & lock_mask;
|
||||||
|
/* Set all bits that aren't read-only. */
|
||||||
|
value |= new_value & ~lock_mask;
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Common register write handler for both OTP classes. */
|
||||||
|
static void npcm7xx_otp_write(NPCM7xxOTPState *s, NPCM7xxOTPRegister reg,
|
||||||
|
uint32_t value)
|
||||||
|
{
|
||||||
|
switch (reg) {
|
||||||
|
case NPCM7XX_OTP_FST:
|
||||||
|
/* RDST is cleared by writing 1 to it. */
|
||||||
|
if (value & FST_RDST) {
|
||||||
|
s->regs[NPCM7XX_OTP_FST] &= ~FST_RDST;
|
||||||
|
}
|
||||||
|
/* Preserve read-only and write-one-to-clear bits */
|
||||||
|
value &= ~FST_RO_MASK;
|
||||||
|
value |= s->regs[NPCM7XX_OTP_FST] & FST_RO_MASK;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case NPCM7XX_OTP_FADDR:
|
||||||
|
break;
|
||||||
|
|
||||||
|
case NPCM7XX_OTP_FDATA:
|
||||||
|
/*
|
||||||
|
* This register is cleared by writing a magic value to it; no other
|
||||||
|
* values can be written.
|
||||||
|
*/
|
||||||
|
if (value == FDATA_CLEAR) {
|
||||||
|
value = 0;
|
||||||
|
} else {
|
||||||
|
value = s->regs[NPCM7XX_OTP_FDATA];
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case NPCM7XX_OTP_FCFG:
|
||||||
|
value = npcm7xx_otp_compute_fcfg(s->regs[NPCM7XX_OTP_FCFG], value);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case NPCM7XX_OTP_FCTL:
|
||||||
|
switch (value) {
|
||||||
|
case FCTL_READ_CMD:
|
||||||
|
npcm7xx_otp_read_array(s);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case FCTL_PROG_CMD1:
|
||||||
|
/*
|
||||||
|
* Programming requires writing two separate magic values to this
|
||||||
|
* register; this is the first one. Just store it so it can be
|
||||||
|
* verified later when the second magic value is received.
|
||||||
|
*/
|
||||||
|
break;
|
||||||
|
|
||||||
|
case FCTL_PROG_CMD2:
|
||||||
|
/*
|
||||||
|
* Only initiate programming if we received the first half of the
|
||||||
|
* command immediately before this one.
|
||||||
|
*/
|
||||||
|
if (s->regs[NPCM7XX_OTP_FCTL] == FCTL_PROG_CMD1) {
|
||||||
|
npcm7xx_otp_program_array(s);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
qemu_log_mask(LOG_GUEST_ERROR,
|
||||||
|
"%s: unrecognized FCNTL value 0x%" PRIx32 "\n",
|
||||||
|
DEVICE(s)->canonical_path, value);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (value != FCTL_PROG_CMD1) {
|
||||||
|
value = 0;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
qemu_log_mask(LOG_GUEST_ERROR, "%s: write to invalid offset 0x%zx\n",
|
||||||
|
DEVICE(s)->canonical_path, reg * sizeof(uint32_t));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
s->regs[reg] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Register read handler specific to the fuse array OTP module. */
|
||||||
|
static uint64_t npcm7xx_fuse_array_read(void *opaque, hwaddr addr,
|
||||||
|
unsigned int size)
|
||||||
|
{
|
||||||
|
NPCM7xxOTPRegister reg = addr / sizeof(uint32_t);
|
||||||
|
NPCM7xxOTPState *s = opaque;
|
||||||
|
uint32_t value;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Only the Fuse Strap register needs special handling; all other registers
|
||||||
|
* work the same way for both kinds of OTP modules.
|
||||||
|
*/
|
||||||
|
if (reg != NPCM7XX_OTP_FUSTRAP) {
|
||||||
|
value = npcm7xx_otp_read(s, reg);
|
||||||
|
} else {
|
||||||
|
/* FUSTRAP is stored as three copies in the OTP array. */
|
||||||
|
uint32_t fustrap[3];
|
||||||
|
|
||||||
|
memcpy(fustrap, &s->array[0], sizeof(fustrap));
|
||||||
|
|
||||||
|
/* Determine value by a majority vote on each bit. */
|
||||||
|
value = (fustrap[0] & fustrap[1]) | (fustrap[0] & fustrap[2]) |
|
||||||
|
(fustrap[1] & fustrap[2]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Register write handler specific to the fuse array OTP module. */
|
||||||
|
static void npcm7xx_fuse_array_write(void *opaque, hwaddr addr, uint64_t v,
|
||||||
|
unsigned int size)
|
||||||
|
{
|
||||||
|
NPCM7xxOTPRegister reg = addr / sizeof(uint32_t);
|
||||||
|
NPCM7xxOTPState *s = opaque;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The Fuse Strap register is read-only. Other registers are handled by
|
||||||
|
* common code.
|
||||||
|
*/
|
||||||
|
if (reg != NPCM7XX_OTP_FUSTRAP) {
|
||||||
|
npcm7xx_otp_write(s, reg, v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static const MemoryRegionOps npcm7xx_fuse_array_ops = {
|
||||||
|
.read = npcm7xx_fuse_array_read,
|
||||||
|
.write = npcm7xx_fuse_array_write,
|
||||||
|
.endianness = DEVICE_LITTLE_ENDIAN,
|
||||||
|
.valid = {
|
||||||
|
.min_access_size = 4,
|
||||||
|
.max_access_size = 4,
|
||||||
|
.unaligned = false,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Register read handler specific to the key storage OTP module. */
|
||||||
|
static uint64_t npcm7xx_key_storage_read(void *opaque, hwaddr addr,
|
||||||
|
unsigned int size)
|
||||||
|
{
|
||||||
|
NPCM7xxOTPRegister reg = addr / sizeof(uint32_t);
|
||||||
|
NPCM7xxOTPState *s = opaque;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Only the Fuse Key Index register needs special handling; all other
|
||||||
|
* registers work the same way for both kinds of OTP modules.
|
||||||
|
*/
|
||||||
|
if (reg != NPCM7XX_OTP_FKEYIND) {
|
||||||
|
return npcm7xx_otp_read(s, reg);
|
||||||
|
}
|
||||||
|
|
||||||
|
qemu_log_mask(LOG_UNIMP, "%s: FKEYIND is not implemented\n", __func__);
|
||||||
|
|
||||||
|
return s->regs[NPCM7XX_OTP_FKEYIND];
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Register write handler specific to the key storage OTP module. */
|
||||||
|
static void npcm7xx_key_storage_write(void *opaque, hwaddr addr, uint64_t v,
|
||||||
|
unsigned int size)
|
||||||
|
{
|
||||||
|
NPCM7xxOTPRegister reg = addr / sizeof(uint32_t);
|
||||||
|
NPCM7xxOTPState *s = opaque;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Only the Fuse Key Index register needs special handling; all other
|
||||||
|
* registers work the same way for both kinds of OTP modules.
|
||||||
|
*/
|
||||||
|
if (reg != NPCM7XX_OTP_FKEYIND) {
|
||||||
|
npcm7xx_otp_write(s, reg, v);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
qemu_log_mask(LOG_UNIMP, "%s: FKEYIND is not implemented\n", __func__);
|
||||||
|
|
||||||
|
s->regs[NPCM7XX_OTP_FKEYIND] = v;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const MemoryRegionOps npcm7xx_key_storage_ops = {
|
||||||
|
.read = npcm7xx_key_storage_read,
|
||||||
|
.write = npcm7xx_key_storage_write,
|
||||||
|
.endianness = DEVICE_LITTLE_ENDIAN,
|
||||||
|
.valid = {
|
||||||
|
.min_access_size = 4,
|
||||||
|
.max_access_size = 4,
|
||||||
|
.unaligned = false,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
static void npcm7xx_otp_enter_reset(Object *obj, ResetType type)
|
||||||
|
{
|
||||||
|
NPCM7xxOTPState *s = NPCM7XX_OTP(obj);
|
||||||
|
|
||||||
|
memset(s->regs, 0, sizeof(s->regs));
|
||||||
|
|
||||||
|
s->regs[NPCM7XX_OTP_FST] = 0x00000001;
|
||||||
|
s->regs[NPCM7XX_OTP_FCFG] = 0x20000000;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void npcm7xx_otp_realize(DeviceState *dev, Error **errp)
|
||||||
|
{
|
||||||
|
NPCM7xxOTPClass *oc = NPCM7XX_OTP_GET_CLASS(dev);
|
||||||
|
NPCM7xxOTPState *s = NPCM7XX_OTP(dev);
|
||||||
|
SysBusDevice *sbd = &s->parent;
|
||||||
|
|
||||||
|
memset(s->array, 0, sizeof(s->array));
|
||||||
|
|
||||||
|
memory_region_init_io(&s->mmio, OBJECT(s), oc->mmio_ops, s, "regs",
|
||||||
|
NPCM7XX_OTP_REGS_SIZE);
|
||||||
|
sysbus_init_mmio(sbd, &s->mmio);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const VMStateDescription vmstate_npcm7xx_otp = {
|
||||||
|
.name = "npcm7xx-otp",
|
||||||
|
.version_id = 0,
|
||||||
|
.minimum_version_id = 0,
|
||||||
|
.fields = (VMStateField[]) {
|
||||||
|
VMSTATE_UINT32_ARRAY(regs, NPCM7xxOTPState, NPCM7XX_OTP_NR_REGS),
|
||||||
|
VMSTATE_UINT8_ARRAY(array, NPCM7xxOTPState, NPCM7XX_OTP_ARRAY_BYTES),
|
||||||
|
VMSTATE_END_OF_LIST(),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
static void npcm7xx_otp_class_init(ObjectClass *klass, void *data)
|
||||||
|
{
|
||||||
|
ResettableClass *rc = RESETTABLE_CLASS(klass);
|
||||||
|
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||||
|
|
||||||
|
QEMU_BUILD_BUG_ON(NPCM7XX_OTP_REGS_END > NPCM7XX_OTP_NR_REGS);
|
||||||
|
|
||||||
|
dc->realize = npcm7xx_otp_realize;
|
||||||
|
dc->vmsd = &vmstate_npcm7xx_otp;
|
||||||
|
rc->phases.enter = npcm7xx_otp_enter_reset;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void npcm7xx_key_storage_class_init(ObjectClass *klass, void *data)
|
||||||
|
{
|
||||||
|
NPCM7xxOTPClass *oc = NPCM7XX_OTP_CLASS(klass);
|
||||||
|
|
||||||
|
oc->mmio_ops = &npcm7xx_key_storage_ops;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void npcm7xx_fuse_array_class_init(ObjectClass *klass, void *data)
|
||||||
|
{
|
||||||
|
NPCM7xxOTPClass *oc = NPCM7XX_OTP_CLASS(klass);
|
||||||
|
|
||||||
|
oc->mmio_ops = &npcm7xx_fuse_array_ops;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const TypeInfo npcm7xx_otp_types[] = {
|
||||||
|
{
|
||||||
|
.name = TYPE_NPCM7XX_OTP,
|
||||||
|
.parent = TYPE_SYS_BUS_DEVICE,
|
||||||
|
.instance_size = sizeof(NPCM7xxOTPState),
|
||||||
|
.class_size = sizeof(NPCM7xxOTPClass),
|
||||||
|
.class_init = npcm7xx_otp_class_init,
|
||||||
|
.abstract = true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.name = TYPE_NPCM7XX_KEY_STORAGE,
|
||||||
|
.parent = TYPE_NPCM7XX_OTP,
|
||||||
|
.class_init = npcm7xx_key_storage_class_init,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.name = TYPE_NPCM7XX_FUSE_ARRAY,
|
||||||
|
.parent = TYPE_NPCM7XX_OTP,
|
||||||
|
.class_init = npcm7xx_fuse_array_class_init,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
DEFINE_TYPES(npcm7xx_otp_types);
|
|
@ -1,5 +1,6 @@
|
||||||
softmmu_ss.add(when: 'CONFIG_ASPEED_SOC', if_true: files('aspeed_smc.c'))
|
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_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_PL022', if_true: files('pl022.c'))
|
||||||
softmmu_ss.add(when: 'CONFIG_SSI', if_true: files('ssi.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_STM32F2XX_SPI', if_true: files('stm32f2xx_spi.c'))
|
||||||
|
|
|
@ -0,0 +1,572 @@
|
||||||
|
/*
|
||||||
|
* Nuvoton NPCM7xx Flash Interface Unit (FIU)
|
||||||
|
*
|
||||||
|
* Copyright 2020 Google LLC
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "qemu/osdep.h"
|
||||||
|
|
||||||
|
#include "hw/irq.h"
|
||||||
|
#include "hw/qdev-properties.h"
|
||||||
|
#include "hw/ssi/npcm7xx_fiu.h"
|
||||||
|
#include "migration/vmstate.h"
|
||||||
|
#include "qapi/error.h"
|
||||||
|
#include "qemu/error-report.h"
|
||||||
|
#include "qemu/log.h"
|
||||||
|
#include "qemu/module.h"
|
||||||
|
#include "qemu/units.h"
|
||||||
|
|
||||||
|
#include "trace.h"
|
||||||
|
|
||||||
|
/* Up to 128 MiB of flash may be accessed directly as memory. */
|
||||||
|
#define NPCM7XX_FIU_FLASH_WINDOW_SIZE (128 * MiB)
|
||||||
|
|
||||||
|
/* Each module has 4 KiB of register space. Only a fraction of it is used. */
|
||||||
|
#define NPCM7XX_FIU_CTRL_REGS_SIZE (4 * KiB)
|
||||||
|
|
||||||
|
/* 32-bit FIU register indices. */
|
||||||
|
enum NPCM7xxFIURegister {
|
||||||
|
NPCM7XX_FIU_DRD_CFG,
|
||||||
|
NPCM7XX_FIU_DWR_CFG,
|
||||||
|
NPCM7XX_FIU_UMA_CFG,
|
||||||
|
NPCM7XX_FIU_UMA_CTS,
|
||||||
|
NPCM7XX_FIU_UMA_CMD,
|
||||||
|
NPCM7XX_FIU_UMA_ADDR,
|
||||||
|
NPCM7XX_FIU_PRT_CFG,
|
||||||
|
NPCM7XX_FIU_UMA_DW0 = 0x0020 / sizeof(uint32_t),
|
||||||
|
NPCM7XX_FIU_UMA_DW1,
|
||||||
|
NPCM7XX_FIU_UMA_DW2,
|
||||||
|
NPCM7XX_FIU_UMA_DW3,
|
||||||
|
NPCM7XX_FIU_UMA_DR0,
|
||||||
|
NPCM7XX_FIU_UMA_DR1,
|
||||||
|
NPCM7XX_FIU_UMA_DR2,
|
||||||
|
NPCM7XX_FIU_UMA_DR3,
|
||||||
|
NPCM7XX_FIU_PRT_CMD0,
|
||||||
|
NPCM7XX_FIU_PRT_CMD1,
|
||||||
|
NPCM7XX_FIU_PRT_CMD2,
|
||||||
|
NPCM7XX_FIU_PRT_CMD3,
|
||||||
|
NPCM7XX_FIU_PRT_CMD4,
|
||||||
|
NPCM7XX_FIU_PRT_CMD5,
|
||||||
|
NPCM7XX_FIU_PRT_CMD6,
|
||||||
|
NPCM7XX_FIU_PRT_CMD7,
|
||||||
|
NPCM7XX_FIU_PRT_CMD8,
|
||||||
|
NPCM7XX_FIU_PRT_CMD9,
|
||||||
|
NPCM7XX_FIU_CFG = 0x78 / sizeof(uint32_t),
|
||||||
|
NPCM7XX_FIU_REGS_END,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* FIU_{DRD,DWR,UMA,PTR}_CFG cannot be written when this bit is set. */
|
||||||
|
#define NPCM7XX_FIU_CFG_LCK BIT(31)
|
||||||
|
|
||||||
|
/* Direct Read configuration register fields. */
|
||||||
|
#define FIU_DRD_CFG_ADDSIZ(rv) extract32(rv, 16, 2)
|
||||||
|
#define FIU_ADDSIZ_3BYTES 0
|
||||||
|
#define FIU_ADDSIZ_4BYTES 1
|
||||||
|
#define FIU_DRD_CFG_DBW(rv) extract32(rv, 12, 2)
|
||||||
|
#define FIU_DRD_CFG_ACCTYPE(rv) extract32(rv, 8, 2)
|
||||||
|
#define FIU_DRD_CFG_RDCMD(rv) extract32(rv, 0, 8)
|
||||||
|
|
||||||
|
/* Direct Write configuration register fields. */
|
||||||
|
#define FIU_DWR_CFG_ADDSIZ(rv) extract32(rv, 16, 2)
|
||||||
|
#define FIU_DWR_CFG_WRCMD(rv) extract32(rv, 0, 8)
|
||||||
|
|
||||||
|
/* User-Mode Access register fields. */
|
||||||
|
|
||||||
|
/* Command Mode Lock and the bits protected by it. */
|
||||||
|
#define FIU_UMA_CFG_CMMLCK BIT(30)
|
||||||
|
#define FIU_UMA_CFG_CMMLCK_MASK 0x00000403
|
||||||
|
|
||||||
|
#define FIU_UMA_CFG_RDATSIZ(rv) extract32(rv, 24, 5)
|
||||||
|
#define FIU_UMA_CFG_DBSIZ(rv) extract32(rv, 21, 3)
|
||||||
|
#define FIU_UMA_CFG_WDATSIZ(rv) extract32(rv, 16, 5)
|
||||||
|
#define FIU_UMA_CFG_ADDSIZ(rv) extract32(rv, 11, 3)
|
||||||
|
#define FIU_UMA_CFG_CMDSIZ(rv) extract32(rv, 10, 1)
|
||||||
|
#define FIU_UMA_CFG_DBPCK(rv) extract32(rv, 6, 2)
|
||||||
|
|
||||||
|
#define FIU_UMA_CTS_RDYIE BIT(25)
|
||||||
|
#define FIU_UMA_CTS_RDYST BIT(24)
|
||||||
|
#define FIU_UMA_CTS_SW_CS BIT(16)
|
||||||
|
#define FIU_UMA_CTS_DEV_NUM(rv) extract32(rv, 8, 2)
|
||||||
|
#define FIU_UMA_CTS_EXEC_DONE BIT(0)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Returns the index of flash in the fiu->flash array. This corresponds to the
|
||||||
|
* chip select ID of the flash.
|
||||||
|
*/
|
||||||
|
static int npcm7xx_fiu_cs_index(NPCM7xxFIUState *fiu, NPCM7xxFIUFlash *flash)
|
||||||
|
{
|
||||||
|
int index = flash - fiu->flash;
|
||||||
|
|
||||||
|
g_assert(index >= 0 && index < fiu->cs_count);
|
||||||
|
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Assert the chip select specified in the UMA Control/Status Register. */
|
||||||
|
static void npcm7xx_fiu_select(NPCM7xxFIUState *s, int cs_id)
|
||||||
|
{
|
||||||
|
trace_npcm7xx_fiu_select(DEVICE(s)->canonical_path, cs_id);
|
||||||
|
|
||||||
|
if (cs_id < s->cs_count) {
|
||||||
|
qemu_irq_lower(s->cs_lines[cs_id]);
|
||||||
|
} else {
|
||||||
|
qemu_log_mask(LOG_GUEST_ERROR,
|
||||||
|
"%s: UMA to CS%d; this module has only %d chip selects",
|
||||||
|
DEVICE(s)->canonical_path, cs_id, s->cs_count);
|
||||||
|
cs_id = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
s->active_cs = cs_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Deassert the currently active chip select. */
|
||||||
|
static void npcm7xx_fiu_deselect(NPCM7xxFIUState *s)
|
||||||
|
{
|
||||||
|
if (s->active_cs < 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
trace_npcm7xx_fiu_deselect(DEVICE(s)->canonical_path, s->active_cs);
|
||||||
|
|
||||||
|
qemu_irq_raise(s->cs_lines[s->active_cs]);
|
||||||
|
s->active_cs = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Direct flash memory read handler. */
|
||||||
|
static uint64_t npcm7xx_fiu_flash_read(void *opaque, hwaddr addr,
|
||||||
|
unsigned int size)
|
||||||
|
{
|
||||||
|
NPCM7xxFIUFlash *f = opaque;
|
||||||
|
NPCM7xxFIUState *fiu = f->fiu;
|
||||||
|
uint64_t value = 0;
|
||||||
|
uint32_t drd_cfg;
|
||||||
|
int dummy_cycles;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (fiu->active_cs != -1) {
|
||||||
|
qemu_log_mask(LOG_GUEST_ERROR,
|
||||||
|
"%s: direct flash read with CS%d already active",
|
||||||
|
DEVICE(fiu)->canonical_path, fiu->active_cs);
|
||||||
|
}
|
||||||
|
|
||||||
|
npcm7xx_fiu_select(fiu, npcm7xx_fiu_cs_index(fiu, f));
|
||||||
|
|
||||||
|
drd_cfg = fiu->regs[NPCM7XX_FIU_DRD_CFG];
|
||||||
|
ssi_transfer(fiu->spi, FIU_DRD_CFG_RDCMD(drd_cfg));
|
||||||
|
|
||||||
|
switch (FIU_DRD_CFG_ADDSIZ(drd_cfg)) {
|
||||||
|
case FIU_ADDSIZ_4BYTES:
|
||||||
|
ssi_transfer(fiu->spi, extract32(addr, 24, 8));
|
||||||
|
/* fall through */
|
||||||
|
case FIU_ADDSIZ_3BYTES:
|
||||||
|
ssi_transfer(fiu->spi, extract32(addr, 16, 8));
|
||||||
|
ssi_transfer(fiu->spi, extract32(addr, 8, 8));
|
||||||
|
ssi_transfer(fiu->spi, extract32(addr, 0, 8));
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
qemu_log_mask(LOG_GUEST_ERROR, "%s: bad address size %d\n",
|
||||||
|
DEVICE(fiu)->canonical_path, FIU_DRD_CFG_ADDSIZ(drd_cfg));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Flash chip model expects one transfer per dummy bit, not byte */
|
||||||
|
dummy_cycles =
|
||||||
|
(FIU_DRD_CFG_DBW(drd_cfg) * 8) >> FIU_DRD_CFG_ACCTYPE(drd_cfg);
|
||||||
|
for (i = 0; i < dummy_cycles; i++) {
|
||||||
|
ssi_transfer(fiu->spi, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < size; i++) {
|
||||||
|
value = deposit64(value, 8 * i, 8, ssi_transfer(fiu->spi, 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
trace_npcm7xx_fiu_flash_read(DEVICE(fiu)->canonical_path, fiu->active_cs,
|
||||||
|
addr, size, value);
|
||||||
|
|
||||||
|
npcm7xx_fiu_deselect(fiu);
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Direct flash memory write handler. */
|
||||||
|
static void npcm7xx_fiu_flash_write(void *opaque, hwaddr addr, uint64_t v,
|
||||||
|
unsigned int size)
|
||||||
|
{
|
||||||
|
NPCM7xxFIUFlash *f = opaque;
|
||||||
|
NPCM7xxFIUState *fiu = f->fiu;
|
||||||
|
uint32_t dwr_cfg;
|
||||||
|
int cs_id;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (fiu->active_cs != -1) {
|
||||||
|
qemu_log_mask(LOG_GUEST_ERROR,
|
||||||
|
"%s: direct flash write with CS%d already active",
|
||||||
|
DEVICE(fiu)->canonical_path, fiu->active_cs);
|
||||||
|
}
|
||||||
|
|
||||||
|
cs_id = npcm7xx_fiu_cs_index(fiu, f);
|
||||||
|
trace_npcm7xx_fiu_flash_write(DEVICE(fiu)->canonical_path, cs_id, addr,
|
||||||
|
size, v);
|
||||||
|
npcm7xx_fiu_select(fiu, cs_id);
|
||||||
|
|
||||||
|
dwr_cfg = fiu->regs[NPCM7XX_FIU_DWR_CFG];
|
||||||
|
ssi_transfer(fiu->spi, FIU_DWR_CFG_WRCMD(dwr_cfg));
|
||||||
|
|
||||||
|
switch (FIU_DWR_CFG_ADDSIZ(dwr_cfg)) {
|
||||||
|
case FIU_ADDSIZ_4BYTES:
|
||||||
|
ssi_transfer(fiu->spi, extract32(addr, 24, 8));
|
||||||
|
/* fall through */
|
||||||
|
case FIU_ADDSIZ_3BYTES:
|
||||||
|
ssi_transfer(fiu->spi, extract32(addr, 16, 8));
|
||||||
|
ssi_transfer(fiu->spi, extract32(addr, 8, 8));
|
||||||
|
ssi_transfer(fiu->spi, extract32(addr, 0, 8));
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
qemu_log_mask(LOG_GUEST_ERROR, "%s: bad address size %d\n",
|
||||||
|
DEVICE(fiu)->canonical_path, FIU_DWR_CFG_ADDSIZ(dwr_cfg));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < size; i++) {
|
||||||
|
ssi_transfer(fiu->spi, extract64(v, i * 8, 8));
|
||||||
|
}
|
||||||
|
|
||||||
|
npcm7xx_fiu_deselect(fiu);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const MemoryRegionOps npcm7xx_fiu_flash_ops = {
|
||||||
|
.read = npcm7xx_fiu_flash_read,
|
||||||
|
.write = npcm7xx_fiu_flash_write,
|
||||||
|
.endianness = DEVICE_LITTLE_ENDIAN,
|
||||||
|
.valid = {
|
||||||
|
.min_access_size = 1,
|
||||||
|
.max_access_size = 8,
|
||||||
|
.unaligned = true,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Control register read handler. */
|
||||||
|
static uint64_t npcm7xx_fiu_ctrl_read(void *opaque, hwaddr addr,
|
||||||
|
unsigned int size)
|
||||||
|
{
|
||||||
|
hwaddr reg = addr / sizeof(uint32_t);
|
||||||
|
NPCM7xxFIUState *s = opaque;
|
||||||
|
uint32_t value;
|
||||||
|
|
||||||
|
if (reg < NPCM7XX_FIU_NR_REGS) {
|
||||||
|
value = s->regs[reg];
|
||||||
|
} else {
|
||||||
|
qemu_log_mask(LOG_GUEST_ERROR,
|
||||||
|
"%s: read from invalid offset 0x%" PRIx64 "\n",
|
||||||
|
DEVICE(s)->canonical_path, addr);
|
||||||
|
value = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
trace_npcm7xx_fiu_ctrl_read(DEVICE(s)->canonical_path, addr, value);
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Send the specified number of address bytes from the UMA address register. */
|
||||||
|
static void send_address(SSIBus *spi, unsigned int addsiz, uint32_t addr)
|
||||||
|
{
|
||||||
|
switch (addsiz) {
|
||||||
|
case 4:
|
||||||
|
ssi_transfer(spi, extract32(addr, 24, 8));
|
||||||
|
/* fall through */
|
||||||
|
case 3:
|
||||||
|
ssi_transfer(spi, extract32(addr, 16, 8));
|
||||||
|
/* fall through */
|
||||||
|
case 2:
|
||||||
|
ssi_transfer(spi, extract32(addr, 8, 8));
|
||||||
|
/* fall through */
|
||||||
|
case 1:
|
||||||
|
ssi_transfer(spi, extract32(addr, 0, 8));
|
||||||
|
/* fall through */
|
||||||
|
case 0:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Send the number of dummy bits specified in the UMA config register. */
|
||||||
|
static void send_dummy_bits(SSIBus *spi, uint32_t uma_cfg, uint32_t uma_cmd)
|
||||||
|
{
|
||||||
|
unsigned int bits_per_clock = 1U << FIU_UMA_CFG_DBPCK(uma_cfg);
|
||||||
|
unsigned int i;
|
||||||
|
|
||||||
|
for (i = 0; i < FIU_UMA_CFG_DBSIZ(uma_cfg); i++) {
|
||||||
|
/* Use bytes 0 and 1 first, then keep repeating byte 2 */
|
||||||
|
unsigned int field = (i < 2) ? ((i + 1) * 8) : 24;
|
||||||
|
unsigned int j;
|
||||||
|
|
||||||
|
for (j = 0; j < 8; j += bits_per_clock) {
|
||||||
|
ssi_transfer(spi, extract32(uma_cmd, field + j, bits_per_clock));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Perform a User-Mode Access transaction. */
|
||||||
|
static void npcm7xx_fiu_uma_transaction(NPCM7xxFIUState *s)
|
||||||
|
{
|
||||||
|
uint32_t uma_cts = s->regs[NPCM7XX_FIU_UMA_CTS];
|
||||||
|
uint32_t uma_cfg;
|
||||||
|
unsigned int i;
|
||||||
|
|
||||||
|
/* SW_CS means the CS is already forced low, so don't touch it. */
|
||||||
|
if (uma_cts & FIU_UMA_CTS_SW_CS) {
|
||||||
|
int cs_id = FIU_UMA_CTS_DEV_NUM(s->regs[NPCM7XX_FIU_UMA_CTS]);
|
||||||
|
npcm7xx_fiu_select(s, cs_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Send command, if present. */
|
||||||
|
uma_cfg = s->regs[NPCM7XX_FIU_UMA_CFG];
|
||||||
|
if (FIU_UMA_CFG_CMDSIZ(uma_cfg) > 0) {
|
||||||
|
ssi_transfer(s->spi, extract32(s->regs[NPCM7XX_FIU_UMA_CMD], 0, 8));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Send address, if present. */
|
||||||
|
send_address(s->spi, FIU_UMA_CFG_ADDSIZ(uma_cfg),
|
||||||
|
s->regs[NPCM7XX_FIU_UMA_ADDR]);
|
||||||
|
|
||||||
|
/* Write data, if present. */
|
||||||
|
for (i = 0; i < FIU_UMA_CFG_WDATSIZ(uma_cfg); i++) {
|
||||||
|
unsigned int reg =
|
||||||
|
(i < 16) ? (NPCM7XX_FIU_UMA_DW0 + i / 4) : NPCM7XX_FIU_UMA_DW3;
|
||||||
|
unsigned int field = (i % 4) * 8;
|
||||||
|
|
||||||
|
ssi_transfer(s->spi, extract32(s->regs[reg], field, 8));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Send dummy bits, if present. */
|
||||||
|
send_dummy_bits(s->spi, uma_cfg, s->regs[NPCM7XX_FIU_UMA_CMD]);
|
||||||
|
|
||||||
|
/* Read data, if present. */
|
||||||
|
for (i = 0; i < FIU_UMA_CFG_RDATSIZ(uma_cfg); i++) {
|
||||||
|
unsigned int reg = NPCM7XX_FIU_UMA_DR0 + i / 4;
|
||||||
|
unsigned int field = (i % 4) * 8;
|
||||||
|
uint8_t c;
|
||||||
|
|
||||||
|
c = ssi_transfer(s->spi, 0);
|
||||||
|
if (reg <= NPCM7XX_FIU_UMA_DR3) {
|
||||||
|
s->regs[reg] = deposit32(s->regs[reg], field, 8, c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Again, don't touch CS if the user is forcing it low. */
|
||||||
|
if (uma_cts & FIU_UMA_CTS_SW_CS) {
|
||||||
|
npcm7xx_fiu_deselect(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* RDYST means a command has completed since it was cleared. */
|
||||||
|
s->regs[NPCM7XX_FIU_UMA_CTS] |= FIU_UMA_CTS_RDYST;
|
||||||
|
/* EXEC_DONE means Execute Command / Not Done, so clear it here. */
|
||||||
|
s->regs[NPCM7XX_FIU_UMA_CTS] &= ~FIU_UMA_CTS_EXEC_DONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Control register write handler. */
|
||||||
|
static void npcm7xx_fiu_ctrl_write(void *opaque, hwaddr addr, uint64_t v,
|
||||||
|
unsigned int size)
|
||||||
|
{
|
||||||
|
hwaddr reg = addr / sizeof(uint32_t);
|
||||||
|
NPCM7xxFIUState *s = opaque;
|
||||||
|
uint32_t value = v;
|
||||||
|
|
||||||
|
trace_npcm7xx_fiu_ctrl_write(DEVICE(s)->canonical_path, addr, value);
|
||||||
|
|
||||||
|
switch (reg) {
|
||||||
|
case NPCM7XX_FIU_UMA_CFG:
|
||||||
|
if (s->regs[reg] & FIU_UMA_CFG_CMMLCK) {
|
||||||
|
value &= ~FIU_UMA_CFG_CMMLCK_MASK;
|
||||||
|
value |= (s->regs[reg] & FIU_UMA_CFG_CMMLCK_MASK);
|
||||||
|
}
|
||||||
|
/* fall through */
|
||||||
|
case NPCM7XX_FIU_DRD_CFG:
|
||||||
|
case NPCM7XX_FIU_DWR_CFG:
|
||||||
|
if (s->regs[reg] & NPCM7XX_FIU_CFG_LCK) {
|
||||||
|
qemu_log_mask(LOG_GUEST_ERROR,
|
||||||
|
"%s: write to locked register @ 0x%" PRIx64 "\n",
|
||||||
|
DEVICE(s)->canonical_path, addr);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
s->regs[reg] = value;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case NPCM7XX_FIU_UMA_CTS:
|
||||||
|
if (value & FIU_UMA_CTS_RDYST) {
|
||||||
|
value &= ~FIU_UMA_CTS_RDYST;
|
||||||
|
} else {
|
||||||
|
value |= s->regs[reg] & FIU_UMA_CTS_RDYST;
|
||||||
|
}
|
||||||
|
if ((s->regs[reg] ^ value) & FIU_UMA_CTS_SW_CS) {
|
||||||
|
if (value & FIU_UMA_CTS_SW_CS) {
|
||||||
|
/*
|
||||||
|
* Don't drop CS if there's a transfer in progress, or we're
|
||||||
|
* about to start one.
|
||||||
|
*/
|
||||||
|
if (!((value | s->regs[reg]) & FIU_UMA_CTS_EXEC_DONE)) {
|
||||||
|
npcm7xx_fiu_deselect(s);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
int cs_id = FIU_UMA_CTS_DEV_NUM(s->regs[NPCM7XX_FIU_UMA_CTS]);
|
||||||
|
npcm7xx_fiu_select(s, cs_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
s->regs[reg] = value | (s->regs[reg] & FIU_UMA_CTS_EXEC_DONE);
|
||||||
|
if (value & FIU_UMA_CTS_EXEC_DONE) {
|
||||||
|
npcm7xx_fiu_uma_transaction(s);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case NPCM7XX_FIU_UMA_DR0 ... NPCM7XX_FIU_UMA_DR3:
|
||||||
|
qemu_log_mask(LOG_GUEST_ERROR,
|
||||||
|
"%s: write to read-only register @ 0x%" PRIx64 "\n",
|
||||||
|
DEVICE(s)->canonical_path, addr);
|
||||||
|
return;
|
||||||
|
|
||||||
|
case NPCM7XX_FIU_PRT_CFG:
|
||||||
|
case NPCM7XX_FIU_PRT_CMD0 ... NPCM7XX_FIU_PRT_CMD9:
|
||||||
|
qemu_log_mask(LOG_UNIMP, "%s: PRT is not implemented\n", __func__);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case NPCM7XX_FIU_UMA_CMD:
|
||||||
|
case NPCM7XX_FIU_UMA_ADDR:
|
||||||
|
case NPCM7XX_FIU_UMA_DW0 ... NPCM7XX_FIU_UMA_DW3:
|
||||||
|
case NPCM7XX_FIU_CFG:
|
||||||
|
s->regs[reg] = value;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
qemu_log_mask(LOG_GUEST_ERROR,
|
||||||
|
"%s: write to invalid offset 0x%" PRIx64 "\n",
|
||||||
|
DEVICE(s)->canonical_path, addr);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static const MemoryRegionOps npcm7xx_fiu_ctrl_ops = {
|
||||||
|
.read = npcm7xx_fiu_ctrl_read,
|
||||||
|
.write = npcm7xx_fiu_ctrl_write,
|
||||||
|
.endianness = DEVICE_LITTLE_ENDIAN,
|
||||||
|
.valid = {
|
||||||
|
.min_access_size = 4,
|
||||||
|
.max_access_size = 4,
|
||||||
|
.unaligned = false,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
static void npcm7xx_fiu_enter_reset(Object *obj, ResetType type)
|
||||||
|
{
|
||||||
|
NPCM7xxFIUState *s = NPCM7XX_FIU(obj);
|
||||||
|
|
||||||
|
trace_npcm7xx_fiu_enter_reset(DEVICE(obj)->canonical_path, type);
|
||||||
|
|
||||||
|
memset(s->regs, 0, sizeof(s->regs));
|
||||||
|
|
||||||
|
s->regs[NPCM7XX_FIU_DRD_CFG] = 0x0300100b;
|
||||||
|
s->regs[NPCM7XX_FIU_DWR_CFG] = 0x03000002;
|
||||||
|
s->regs[NPCM7XX_FIU_UMA_CFG] = 0x00000400;
|
||||||
|
s->regs[NPCM7XX_FIU_UMA_CTS] = 0x00010000;
|
||||||
|
s->regs[NPCM7XX_FIU_UMA_CMD] = 0x0000000b;
|
||||||
|
s->regs[NPCM7XX_FIU_PRT_CFG] = 0x00000400;
|
||||||
|
s->regs[NPCM7XX_FIU_CFG] = 0x0000000b;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void npcm7xx_fiu_hold_reset(Object *obj)
|
||||||
|
{
|
||||||
|
NPCM7xxFIUState *s = NPCM7XX_FIU(obj);
|
||||||
|
int i;
|
||||||
|
|
||||||
|
trace_npcm7xx_fiu_hold_reset(DEVICE(obj)->canonical_path);
|
||||||
|
|
||||||
|
for (i = 0; i < s->cs_count; i++) {
|
||||||
|
qemu_irq_raise(s->cs_lines[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void npcm7xx_fiu_realize(DeviceState *dev, Error **errp)
|
||||||
|
{
|
||||||
|
NPCM7xxFIUState *s = NPCM7XX_FIU(dev);
|
||||||
|
SysBusDevice *sbd = &s->parent;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (s->cs_count <= 0) {
|
||||||
|
error_setg(errp, "%s: %d chip selects specified, need at least one",
|
||||||
|
dev->canonical_path, s->cs_count);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
s->spi = ssi_create_bus(dev, "spi");
|
||||||
|
s->cs_lines = g_new0(qemu_irq, s->cs_count);
|
||||||
|
qdev_init_gpio_out_named(DEVICE(s), s->cs_lines, "cs", s->cs_count);
|
||||||
|
s->flash = g_new0(NPCM7xxFIUFlash, s->cs_count);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Register the control registers region first. It may be followed by one
|
||||||
|
* or more direct flash access regions.
|
||||||
|
*/
|
||||||
|
memory_region_init_io(&s->mmio, OBJECT(s), &npcm7xx_fiu_ctrl_ops, s, "ctrl",
|
||||||
|
NPCM7XX_FIU_CTRL_REGS_SIZE);
|
||||||
|
sysbus_init_mmio(sbd, &s->mmio);
|
||||||
|
|
||||||
|
for (i = 0; i < s->cs_count; i++) {
|
||||||
|
NPCM7xxFIUFlash *flash = &s->flash[i];
|
||||||
|
flash->fiu = s;
|
||||||
|
memory_region_init_io(&flash->direct_access, OBJECT(s),
|
||||||
|
&npcm7xx_fiu_flash_ops, &s->flash[i], "flash",
|
||||||
|
NPCM7XX_FIU_FLASH_WINDOW_SIZE);
|
||||||
|
sysbus_init_mmio(sbd, &flash->direct_access);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static const VMStateDescription vmstate_npcm7xx_fiu = {
|
||||||
|
.name = "npcm7xx-fiu",
|
||||||
|
.version_id = 0,
|
||||||
|
.minimum_version_id = 0,
|
||||||
|
.fields = (VMStateField[]) {
|
||||||
|
VMSTATE_INT32(active_cs, NPCM7xxFIUState),
|
||||||
|
VMSTATE_UINT32_ARRAY(regs, NPCM7xxFIUState, NPCM7XX_FIU_NR_REGS),
|
||||||
|
VMSTATE_END_OF_LIST(),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
static Property npcm7xx_fiu_properties[] = {
|
||||||
|
DEFINE_PROP_INT32("cs-count", NPCM7xxFIUState, cs_count, 0),
|
||||||
|
DEFINE_PROP_END_OF_LIST(),
|
||||||
|
};
|
||||||
|
|
||||||
|
static void npcm7xx_fiu_class_init(ObjectClass *klass, void *data)
|
||||||
|
{
|
||||||
|
ResettableClass *rc = RESETTABLE_CLASS(klass);
|
||||||
|
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||||
|
|
||||||
|
QEMU_BUILD_BUG_ON(NPCM7XX_FIU_REGS_END > NPCM7XX_FIU_NR_REGS);
|
||||||
|
|
||||||
|
dc->desc = "NPCM7xx Flash Interface Unit";
|
||||||
|
dc->realize = npcm7xx_fiu_realize;
|
||||||
|
dc->vmsd = &vmstate_npcm7xx_fiu;
|
||||||
|
rc->phases.enter = npcm7xx_fiu_enter_reset;
|
||||||
|
rc->phases.hold = npcm7xx_fiu_hold_reset;
|
||||||
|
device_class_set_props(dc, npcm7xx_fiu_properties);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const TypeInfo npcm7xx_fiu_types[] = {
|
||||||
|
{
|
||||||
|
.name = TYPE_NPCM7XX_FIU,
|
||||||
|
.parent = TYPE_SYS_BUS_DEVICE,
|
||||||
|
.instance_size = sizeof(NPCM7xxFIUState),
|
||||||
|
.class_init = npcm7xx_fiu_class_init,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
DEFINE_TYPES(npcm7xx_fiu_types);
|
|
@ -9,3 +9,14 @@ aspeed_smc_dma_checksum(uint32_t addr, uint32_t data) "0x%08x: 0x%08x"
|
||||||
aspeed_smc_dma_rw(const char *dir, uint32_t flash_addr, uint32_t dram_addr, uint32_t size) "%s flash:@0x%08x dram:@0x%08x size:0x%08x"
|
aspeed_smc_dma_rw(const char *dir, uint32_t flash_addr, uint32_t dram_addr, uint32_t size) "%s flash:@0x%08x dram:@0x%08x size:0x%08x"
|
||||||
aspeed_smc_write(uint64_t addr, uint32_t size, uint64_t data) "@0x%" PRIx64 " size %u: 0x%" PRIx64
|
aspeed_smc_write(uint64_t addr, uint32_t size, uint64_t data) "@0x%" PRIx64 " size %u: 0x%" PRIx64
|
||||||
aspeed_smc_flash_select(int cs, const char *prefix) "CS%d %sselect"
|
aspeed_smc_flash_select(int cs, const char *prefix) "CS%d %sselect"
|
||||||
|
|
||||||
|
# npcm7xx_fiu.c
|
||||||
|
|
||||||
|
npcm7xx_fiu_enter_reset(const char *id, int reset_type) "%s reset type: %d"
|
||||||
|
npcm7xx_fiu_hold_reset(const char *id) "%s"
|
||||||
|
npcm7xx_fiu_select(const char *id, int cs) "%s select CS%d"
|
||||||
|
npcm7xx_fiu_deselect(const char *id, int cs) "%s deselect CS%d"
|
||||||
|
npcm7xx_fiu_ctrl_read(const char *id, uint64_t addr, uint32_t data) "%s offset: 0x%04" PRIx64 " value: 0x%08" PRIx32
|
||||||
|
npcm7xx_fiu_ctrl_write(const char *id, uint64_t addr, uint32_t data) "%s offset: 0x%04" PRIx64 " value: 0x%08" PRIx32
|
||||||
|
npcm7xx_fiu_flash_read(const char *id, int cs, uint64_t addr, unsigned int size, uint64_t value) "%s[%d] offset: 0x%08" PRIx64 " size: %u value: 0x%" PRIx64
|
||||||
|
npcm7xx_fiu_flash_write(const char *id, int cs, uint64_t addr, unsigned int size, uint64_t value) "%s[%d] offset: 0x%08" PRIx64 " size: %u value: 0x%" PRIx64
|
||||||
|
|
|
@ -202,6 +202,14 @@ static void systick_reset(DeviceState *dev)
|
||||||
{
|
{
|
||||||
SysTickState *s = SYSTICK(dev);
|
SysTickState *s = SYSTICK(dev);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Forgetting to set system_clock_scale is always a board code
|
||||||
|
* bug. We can't check this earlier because for some boards
|
||||||
|
* (like stellaris) it is not yet configured at the point where
|
||||||
|
* the systick device is realized.
|
||||||
|
*/
|
||||||
|
assert(system_clock_scale != 0);
|
||||||
|
|
||||||
s->control = 0;
|
s->control = 0;
|
||||||
s->reload = 0;
|
s->reload = 0;
|
||||||
s->tick = 0;
|
s->tick = 0;
|
||||||
|
|
|
@ -23,6 +23,7 @@ softmmu_ss.add(when: 'CONFIG_LM32', if_true: files('lm32_timer.c'))
|
||||||
softmmu_ss.add(when: 'CONFIG_MILKYMIST', if_true: files('milkymist-sysctl.c'))
|
softmmu_ss.add(when: 'CONFIG_MILKYMIST', if_true: files('milkymist-sysctl.c'))
|
||||||
softmmu_ss.add(when: 'CONFIG_MIPS_CPS', if_true: files('mips_gictimer.c'))
|
softmmu_ss.add(when: 'CONFIG_MIPS_CPS', if_true: files('mips_gictimer.c'))
|
||||||
softmmu_ss.add(when: 'CONFIG_MSF2', if_true: files('mss-timer.c'))
|
softmmu_ss.add(when: 'CONFIG_MSF2', if_true: files('mss-timer.c'))
|
||||||
|
softmmu_ss.add(when: 'CONFIG_NPCM7XX', if_true: files('npcm7xx_timer.c'))
|
||||||
softmmu_ss.add(when: 'CONFIG_NRF51_SOC', if_true: files('nrf51_timer.c'))
|
softmmu_ss.add(when: 'CONFIG_NRF51_SOC', if_true: files('nrf51_timer.c'))
|
||||||
softmmu_ss.add(when: 'CONFIG_OMAP', if_true: files('omap_gptimer.c'))
|
softmmu_ss.add(when: 'CONFIG_OMAP', if_true: files('omap_gptimer.c'))
|
||||||
softmmu_ss.add(when: 'CONFIG_OMAP', if_true: files('omap_synctimer.c'))
|
softmmu_ss.add(when: 'CONFIG_OMAP', if_true: files('omap_synctimer.c'))
|
||||||
|
|
|
@ -0,0 +1,543 @@
|
||||||
|
/*
|
||||||
|
* Nuvoton NPCM7xx Timer Controller
|
||||||
|
*
|
||||||
|
* Copyright 2020 Google LLC
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "qemu/osdep.h"
|
||||||
|
|
||||||
|
#include "hw/irq.h"
|
||||||
|
#include "hw/misc/npcm7xx_clk.h"
|
||||||
|
#include "hw/timer/npcm7xx_timer.h"
|
||||||
|
#include "migration/vmstate.h"
|
||||||
|
#include "qemu/bitops.h"
|
||||||
|
#include "qemu/error-report.h"
|
||||||
|
#include "qemu/log.h"
|
||||||
|
#include "qemu/module.h"
|
||||||
|
#include "qemu/timer.h"
|
||||||
|
#include "qemu/units.h"
|
||||||
|
#include "trace.h"
|
||||||
|
|
||||||
|
/* 32-bit register indices. */
|
||||||
|
enum NPCM7xxTimerRegisters {
|
||||||
|
NPCM7XX_TIMER_TCSR0,
|
||||||
|
NPCM7XX_TIMER_TCSR1,
|
||||||
|
NPCM7XX_TIMER_TICR0,
|
||||||
|
NPCM7XX_TIMER_TICR1,
|
||||||
|
NPCM7XX_TIMER_TDR0,
|
||||||
|
NPCM7XX_TIMER_TDR1,
|
||||||
|
NPCM7XX_TIMER_TISR,
|
||||||
|
NPCM7XX_TIMER_WTCR,
|
||||||
|
NPCM7XX_TIMER_TCSR2,
|
||||||
|
NPCM7XX_TIMER_TCSR3,
|
||||||
|
NPCM7XX_TIMER_TICR2,
|
||||||
|
NPCM7XX_TIMER_TICR3,
|
||||||
|
NPCM7XX_TIMER_TDR2,
|
||||||
|
NPCM7XX_TIMER_TDR3,
|
||||||
|
NPCM7XX_TIMER_TCSR4 = 0x0040 / sizeof(uint32_t),
|
||||||
|
NPCM7XX_TIMER_TICR4 = 0x0048 / sizeof(uint32_t),
|
||||||
|
NPCM7XX_TIMER_TDR4 = 0x0050 / sizeof(uint32_t),
|
||||||
|
NPCM7XX_TIMER_REGS_END,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Register field definitions. */
|
||||||
|
#define NPCM7XX_TCSR_CEN BIT(30)
|
||||||
|
#define NPCM7XX_TCSR_IE BIT(29)
|
||||||
|
#define NPCM7XX_TCSR_PERIODIC BIT(27)
|
||||||
|
#define NPCM7XX_TCSR_CRST BIT(26)
|
||||||
|
#define NPCM7XX_TCSR_CACT BIT(25)
|
||||||
|
#define NPCM7XX_TCSR_RSVD 0x01ffff00
|
||||||
|
#define NPCM7XX_TCSR_PRESCALE_START 0
|
||||||
|
#define NPCM7XX_TCSR_PRESCALE_LEN 8
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Returns the index of timer in the tc->timer array. This can be used to
|
||||||
|
* locate the registers that belong to this timer.
|
||||||
|
*/
|
||||||
|
static int npcm7xx_timer_index(NPCM7xxTimerCtrlState *tc, NPCM7xxTimer *timer)
|
||||||
|
{
|
||||||
|
int index = timer - tc->timer;
|
||||||
|
|
||||||
|
g_assert(index >= 0 && index < NPCM7XX_TIMERS_PER_CTRL);
|
||||||
|
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Return the value by which to divide the reference clock rate. */
|
||||||
|
static uint32_t npcm7xx_tcsr_prescaler(uint32_t tcsr)
|
||||||
|
{
|
||||||
|
return extract32(tcsr, NPCM7XX_TCSR_PRESCALE_START,
|
||||||
|
NPCM7XX_TCSR_PRESCALE_LEN) + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Convert a timer cycle count to a time interval in nanoseconds. */
|
||||||
|
static int64_t npcm7xx_timer_count_to_ns(NPCM7xxTimer *t, uint32_t count)
|
||||||
|
{
|
||||||
|
int64_t ns = count;
|
||||||
|
|
||||||
|
ns *= NANOSECONDS_PER_SECOND / NPCM7XX_TIMER_REF_HZ;
|
||||||
|
ns *= npcm7xx_tcsr_prescaler(t->tcsr);
|
||||||
|
|
||||||
|
return ns;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Convert a time interval in nanoseconds to a timer cycle count. */
|
||||||
|
static uint32_t npcm7xx_timer_ns_to_count(NPCM7xxTimer *t, int64_t ns)
|
||||||
|
{
|
||||||
|
int64_t count;
|
||||||
|
|
||||||
|
count = ns / (NANOSECONDS_PER_SECOND / NPCM7XX_TIMER_REF_HZ);
|
||||||
|
count /= npcm7xx_tcsr_prescaler(t->tcsr);
|
||||||
|
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Raise the interrupt line if there's a pending interrupt and interrupts are
|
||||||
|
* enabled for this timer. If not, lower it.
|
||||||
|
*/
|
||||||
|
static void npcm7xx_timer_check_interrupt(NPCM7xxTimer *t)
|
||||||
|
{
|
||||||
|
NPCM7xxTimerCtrlState *tc = t->ctrl;
|
||||||
|
int index = npcm7xx_timer_index(tc, t);
|
||||||
|
bool pending = (t->tcsr & NPCM7XX_TCSR_IE) && (tc->tisr & BIT(index));
|
||||||
|
|
||||||
|
qemu_set_irq(t->irq, pending);
|
||||||
|
trace_npcm7xx_timer_irq(DEVICE(tc)->canonical_path, index, pending);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Start or resume the timer. */
|
||||||
|
static void npcm7xx_timer_start(NPCM7xxTimer *t)
|
||||||
|
{
|
||||||
|
int64_t now;
|
||||||
|
|
||||||
|
now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
|
||||||
|
t->expires_ns = now + t->remaining_ns;
|
||||||
|
timer_mod(&t->qtimer, t->expires_ns);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Called when the counter reaches zero. Sets the interrupt flag, and either
|
||||||
|
* restarts or disables the timer.
|
||||||
|
*/
|
||||||
|
static void npcm7xx_timer_reached_zero(NPCM7xxTimer *t)
|
||||||
|
{
|
||||||
|
NPCM7xxTimerCtrlState *tc = t->ctrl;
|
||||||
|
int index = npcm7xx_timer_index(tc, t);
|
||||||
|
|
||||||
|
tc->tisr |= BIT(index);
|
||||||
|
|
||||||
|
if (t->tcsr & NPCM7XX_TCSR_PERIODIC) {
|
||||||
|
t->remaining_ns = npcm7xx_timer_count_to_ns(t, t->ticr);
|
||||||
|
if (t->tcsr & NPCM7XX_TCSR_CEN) {
|
||||||
|
npcm7xx_timer_start(t);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
t->tcsr &= ~(NPCM7XX_TCSR_CEN | NPCM7XX_TCSR_CACT);
|
||||||
|
}
|
||||||
|
|
||||||
|
npcm7xx_timer_check_interrupt(t);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Stop counting. Record the time remaining so we can continue later. */
|
||||||
|
static void npcm7xx_timer_pause(NPCM7xxTimer *t)
|
||||||
|
{
|
||||||
|
int64_t now;
|
||||||
|
|
||||||
|
timer_del(&t->qtimer);
|
||||||
|
now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
|
||||||
|
t->remaining_ns = t->expires_ns - now;
|
||||||
|
if (t->remaining_ns <= 0) {
|
||||||
|
npcm7xx_timer_reached_zero(t);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Restart the timer from its initial value. If the timer was enabled and stays
|
||||||
|
* enabled, adjust the QEMU timer according to the new count. If the timer is
|
||||||
|
* transitioning from disabled to enabled, the caller is expected to start the
|
||||||
|
* timer later.
|
||||||
|
*/
|
||||||
|
static void npcm7xx_timer_restart(NPCM7xxTimer *t, uint32_t old_tcsr)
|
||||||
|
{
|
||||||
|
t->remaining_ns = npcm7xx_timer_count_to_ns(t, t->ticr);
|
||||||
|
|
||||||
|
if (old_tcsr & t->tcsr & NPCM7XX_TCSR_CEN) {
|
||||||
|
npcm7xx_timer_start(t);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Register read and write handlers */
|
||||||
|
|
||||||
|
static uint32_t npcm7xx_timer_read_tdr(NPCM7xxTimer *t)
|
||||||
|
{
|
||||||
|
if (t->tcsr & NPCM7XX_TCSR_CEN) {
|
||||||
|
int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
|
||||||
|
|
||||||
|
return npcm7xx_timer_ns_to_count(t, t->expires_ns - now);
|
||||||
|
}
|
||||||
|
|
||||||
|
return npcm7xx_timer_ns_to_count(t, t->remaining_ns);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void npcm7xx_timer_write_tcsr(NPCM7xxTimer *t, uint32_t new_tcsr)
|
||||||
|
{
|
||||||
|
uint32_t old_tcsr = t->tcsr;
|
||||||
|
uint32_t tdr;
|
||||||
|
|
||||||
|
if (new_tcsr & NPCM7XX_TCSR_RSVD) {
|
||||||
|
qemu_log_mask(LOG_GUEST_ERROR, "%s: reserved bits in 0x%08x ignored\n",
|
||||||
|
__func__, new_tcsr);
|
||||||
|
new_tcsr &= ~NPCM7XX_TCSR_RSVD;
|
||||||
|
}
|
||||||
|
if (new_tcsr & NPCM7XX_TCSR_CACT) {
|
||||||
|
qemu_log_mask(LOG_GUEST_ERROR, "%s: read-only bits in 0x%08x ignored\n",
|
||||||
|
__func__, new_tcsr);
|
||||||
|
new_tcsr &= ~NPCM7XX_TCSR_CACT;
|
||||||
|
}
|
||||||
|
if ((new_tcsr & NPCM7XX_TCSR_CRST) && (new_tcsr & NPCM7XX_TCSR_CEN)) {
|
||||||
|
qemu_log_mask(LOG_GUEST_ERROR,
|
||||||
|
"%s: both CRST and CEN set; ignoring CEN.\n",
|
||||||
|
__func__);
|
||||||
|
new_tcsr &= ~NPCM7XX_TCSR_CEN;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Calculate the value of TDR before potentially changing the prescaler. */
|
||||||
|
tdr = npcm7xx_timer_read_tdr(t);
|
||||||
|
|
||||||
|
t->tcsr = (t->tcsr & NPCM7XX_TCSR_CACT) | new_tcsr;
|
||||||
|
|
||||||
|
if (npcm7xx_tcsr_prescaler(old_tcsr) != npcm7xx_tcsr_prescaler(new_tcsr)) {
|
||||||
|
/* Recalculate time remaining based on the current TDR value. */
|
||||||
|
t->remaining_ns = npcm7xx_timer_count_to_ns(t, tdr);
|
||||||
|
if (old_tcsr & t->tcsr & NPCM7XX_TCSR_CEN) {
|
||||||
|
npcm7xx_timer_start(t);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((old_tcsr ^ new_tcsr) & NPCM7XX_TCSR_IE) {
|
||||||
|
npcm7xx_timer_check_interrupt(t);
|
||||||
|
}
|
||||||
|
if (new_tcsr & NPCM7XX_TCSR_CRST) {
|
||||||
|
npcm7xx_timer_restart(t, old_tcsr);
|
||||||
|
t->tcsr &= ~NPCM7XX_TCSR_CRST;
|
||||||
|
}
|
||||||
|
if ((old_tcsr ^ new_tcsr) & NPCM7XX_TCSR_CEN) {
|
||||||
|
if (new_tcsr & NPCM7XX_TCSR_CEN) {
|
||||||
|
t->tcsr |= NPCM7XX_TCSR_CACT;
|
||||||
|
npcm7xx_timer_start(t);
|
||||||
|
} else {
|
||||||
|
t->tcsr &= ~NPCM7XX_TCSR_CACT;
|
||||||
|
npcm7xx_timer_pause(t);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void npcm7xx_timer_write_ticr(NPCM7xxTimer *t, uint32_t new_ticr)
|
||||||
|
{
|
||||||
|
t->ticr = new_ticr;
|
||||||
|
|
||||||
|
npcm7xx_timer_restart(t, t->tcsr);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void npcm7xx_timer_write_tisr(NPCM7xxTimerCtrlState *s, uint32_t value)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
s->tisr &= ~value;
|
||||||
|
for (i = 0; i < ARRAY_SIZE(s->timer); i++) {
|
||||||
|
if (value & (1U << i)) {
|
||||||
|
npcm7xx_timer_check_interrupt(&s->timer[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static hwaddr npcm7xx_tcsr_index(hwaddr reg)
|
||||||
|
{
|
||||||
|
switch (reg) {
|
||||||
|
case NPCM7XX_TIMER_TCSR0:
|
||||||
|
return 0;
|
||||||
|
case NPCM7XX_TIMER_TCSR1:
|
||||||
|
return 1;
|
||||||
|
case NPCM7XX_TIMER_TCSR2:
|
||||||
|
return 2;
|
||||||
|
case NPCM7XX_TIMER_TCSR3:
|
||||||
|
return 3;
|
||||||
|
case NPCM7XX_TIMER_TCSR4:
|
||||||
|
return 4;
|
||||||
|
default:
|
||||||
|
g_assert_not_reached();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static hwaddr npcm7xx_ticr_index(hwaddr reg)
|
||||||
|
{
|
||||||
|
switch (reg) {
|
||||||
|
case NPCM7XX_TIMER_TICR0:
|
||||||
|
return 0;
|
||||||
|
case NPCM7XX_TIMER_TICR1:
|
||||||
|
return 1;
|
||||||
|
case NPCM7XX_TIMER_TICR2:
|
||||||
|
return 2;
|
||||||
|
case NPCM7XX_TIMER_TICR3:
|
||||||
|
return 3;
|
||||||
|
case NPCM7XX_TIMER_TICR4:
|
||||||
|
return 4;
|
||||||
|
default:
|
||||||
|
g_assert_not_reached();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static hwaddr npcm7xx_tdr_index(hwaddr reg)
|
||||||
|
{
|
||||||
|
switch (reg) {
|
||||||
|
case NPCM7XX_TIMER_TDR0:
|
||||||
|
return 0;
|
||||||
|
case NPCM7XX_TIMER_TDR1:
|
||||||
|
return 1;
|
||||||
|
case NPCM7XX_TIMER_TDR2:
|
||||||
|
return 2;
|
||||||
|
case NPCM7XX_TIMER_TDR3:
|
||||||
|
return 3;
|
||||||
|
case NPCM7XX_TIMER_TDR4:
|
||||||
|
return 4;
|
||||||
|
default:
|
||||||
|
g_assert_not_reached();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint64_t npcm7xx_timer_read(void *opaque, hwaddr offset, unsigned size)
|
||||||
|
{
|
||||||
|
NPCM7xxTimerCtrlState *s = opaque;
|
||||||
|
uint64_t value = 0;
|
||||||
|
hwaddr reg;
|
||||||
|
|
||||||
|
reg = offset / sizeof(uint32_t);
|
||||||
|
switch (reg) {
|
||||||
|
case NPCM7XX_TIMER_TCSR0:
|
||||||
|
case NPCM7XX_TIMER_TCSR1:
|
||||||
|
case NPCM7XX_TIMER_TCSR2:
|
||||||
|
case NPCM7XX_TIMER_TCSR3:
|
||||||
|
case NPCM7XX_TIMER_TCSR4:
|
||||||
|
value = s->timer[npcm7xx_tcsr_index(reg)].tcsr;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case NPCM7XX_TIMER_TICR0:
|
||||||
|
case NPCM7XX_TIMER_TICR1:
|
||||||
|
case NPCM7XX_TIMER_TICR2:
|
||||||
|
case NPCM7XX_TIMER_TICR3:
|
||||||
|
case NPCM7XX_TIMER_TICR4:
|
||||||
|
value = s->timer[npcm7xx_ticr_index(reg)].ticr;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case NPCM7XX_TIMER_TDR0:
|
||||||
|
case NPCM7XX_TIMER_TDR1:
|
||||||
|
case NPCM7XX_TIMER_TDR2:
|
||||||
|
case NPCM7XX_TIMER_TDR3:
|
||||||
|
case NPCM7XX_TIMER_TDR4:
|
||||||
|
value = npcm7xx_timer_read_tdr(&s->timer[npcm7xx_tdr_index(reg)]);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case NPCM7XX_TIMER_TISR:
|
||||||
|
value = s->tisr;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case NPCM7XX_TIMER_WTCR:
|
||||||
|
value = s->wtcr;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
qemu_log_mask(LOG_GUEST_ERROR,
|
||||||
|
"%s: invalid offset 0x%04" HWADDR_PRIx "\n",
|
||||||
|
__func__, offset);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
trace_npcm7xx_timer_read(DEVICE(s)->canonical_path, offset, value);
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void npcm7xx_timer_write(void *opaque, hwaddr offset,
|
||||||
|
uint64_t v, unsigned size)
|
||||||
|
{
|
||||||
|
uint32_t reg = offset / sizeof(uint32_t);
|
||||||
|
NPCM7xxTimerCtrlState *s = opaque;
|
||||||
|
uint32_t value = v;
|
||||||
|
|
||||||
|
trace_npcm7xx_timer_write(DEVICE(s)->canonical_path, offset, value);
|
||||||
|
|
||||||
|
switch (reg) {
|
||||||
|
case NPCM7XX_TIMER_TCSR0:
|
||||||
|
case NPCM7XX_TIMER_TCSR1:
|
||||||
|
case NPCM7XX_TIMER_TCSR2:
|
||||||
|
case NPCM7XX_TIMER_TCSR3:
|
||||||
|
case NPCM7XX_TIMER_TCSR4:
|
||||||
|
npcm7xx_timer_write_tcsr(&s->timer[npcm7xx_tcsr_index(reg)], value);
|
||||||
|
return;
|
||||||
|
|
||||||
|
case NPCM7XX_TIMER_TICR0:
|
||||||
|
case NPCM7XX_TIMER_TICR1:
|
||||||
|
case NPCM7XX_TIMER_TICR2:
|
||||||
|
case NPCM7XX_TIMER_TICR3:
|
||||||
|
case NPCM7XX_TIMER_TICR4:
|
||||||
|
npcm7xx_timer_write_ticr(&s->timer[npcm7xx_ticr_index(reg)], value);
|
||||||
|
return;
|
||||||
|
|
||||||
|
case NPCM7XX_TIMER_TDR0:
|
||||||
|
case NPCM7XX_TIMER_TDR1:
|
||||||
|
case NPCM7XX_TIMER_TDR2:
|
||||||
|
case NPCM7XX_TIMER_TDR3:
|
||||||
|
case NPCM7XX_TIMER_TDR4:
|
||||||
|
qemu_log_mask(LOG_GUEST_ERROR,
|
||||||
|
"%s: register @ 0x%04" HWADDR_PRIx " is read-only\n",
|
||||||
|
__func__, offset);
|
||||||
|
return;
|
||||||
|
|
||||||
|
case NPCM7XX_TIMER_TISR:
|
||||||
|
npcm7xx_timer_write_tisr(s, value);
|
||||||
|
return;
|
||||||
|
|
||||||
|
case NPCM7XX_TIMER_WTCR:
|
||||||
|
qemu_log_mask(LOG_UNIMP, "%s: WTCR write not implemented: 0x%08x\n",
|
||||||
|
__func__, value);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
qemu_log_mask(LOG_GUEST_ERROR,
|
||||||
|
"%s: invalid offset 0x%04" HWADDR_PRIx "\n",
|
||||||
|
__func__, offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct MemoryRegionOps npcm7xx_timer_ops = {
|
||||||
|
.read = npcm7xx_timer_read,
|
||||||
|
.write = npcm7xx_timer_write,
|
||||||
|
.endianness = DEVICE_LITTLE_ENDIAN,
|
||||||
|
.valid = {
|
||||||
|
.min_access_size = 4,
|
||||||
|
.max_access_size = 4,
|
||||||
|
.unaligned = false,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Called when the QEMU timer expires. */
|
||||||
|
static void npcm7xx_timer_expired(void *opaque)
|
||||||
|
{
|
||||||
|
NPCM7xxTimer *t = opaque;
|
||||||
|
|
||||||
|
if (t->tcsr & NPCM7XX_TCSR_CEN) {
|
||||||
|
npcm7xx_timer_reached_zero(t);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void npcm7xx_timer_enter_reset(Object *obj, ResetType type)
|
||||||
|
{
|
||||||
|
NPCM7xxTimerCtrlState *s = NPCM7XX_TIMER(obj);
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < NPCM7XX_TIMERS_PER_CTRL; i++) {
|
||||||
|
NPCM7xxTimer *t = &s->timer[i];
|
||||||
|
|
||||||
|
timer_del(&t->qtimer);
|
||||||
|
t->expires_ns = 0;
|
||||||
|
t->remaining_ns = 0;
|
||||||
|
t->tcsr = 0x00000005;
|
||||||
|
t->ticr = 0x00000000;
|
||||||
|
}
|
||||||
|
|
||||||
|
s->tisr = 0x00000000;
|
||||||
|
s->wtcr = 0x00000400;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void npcm7xx_timer_hold_reset(Object *obj)
|
||||||
|
{
|
||||||
|
NPCM7xxTimerCtrlState *s = NPCM7XX_TIMER(obj);
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < NPCM7XX_TIMERS_PER_CTRL; i++) {
|
||||||
|
qemu_irq_lower(s->timer[i].irq);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void npcm7xx_timer_realize(DeviceState *dev, Error **errp)
|
||||||
|
{
|
||||||
|
NPCM7xxTimerCtrlState *s = NPCM7XX_TIMER(dev);
|
||||||
|
SysBusDevice *sbd = &s->parent;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < NPCM7XX_TIMERS_PER_CTRL; i++) {
|
||||||
|
NPCM7xxTimer *t = &s->timer[i];
|
||||||
|
t->ctrl = s;
|
||||||
|
timer_init_ns(&t->qtimer, QEMU_CLOCK_VIRTUAL, npcm7xx_timer_expired, t);
|
||||||
|
sysbus_init_irq(sbd, &t->irq);
|
||||||
|
}
|
||||||
|
|
||||||
|
memory_region_init_io(&s->iomem, OBJECT(s), &npcm7xx_timer_ops, s,
|
||||||
|
TYPE_NPCM7XX_TIMER, 4 * KiB);
|
||||||
|
sysbus_init_mmio(sbd, &s->iomem);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const VMStateDescription vmstate_npcm7xx_timer = {
|
||||||
|
.name = "npcm7xx-timer",
|
||||||
|
.version_id = 0,
|
||||||
|
.minimum_version_id = 0,
|
||||||
|
.fields = (VMStateField[]) {
|
||||||
|
VMSTATE_TIMER(qtimer, NPCM7xxTimer),
|
||||||
|
VMSTATE_INT64(expires_ns, NPCM7xxTimer),
|
||||||
|
VMSTATE_INT64(remaining_ns, NPCM7xxTimer),
|
||||||
|
VMSTATE_UINT32(tcsr, NPCM7xxTimer),
|
||||||
|
VMSTATE_UINT32(ticr, NPCM7xxTimer),
|
||||||
|
VMSTATE_END_OF_LIST(),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
static const VMStateDescription vmstate_npcm7xx_timer_ctrl = {
|
||||||
|
.name = "npcm7xx-timer-ctrl",
|
||||||
|
.version_id = 0,
|
||||||
|
.minimum_version_id = 0,
|
||||||
|
.fields = (VMStateField[]) {
|
||||||
|
VMSTATE_UINT32(tisr, NPCM7xxTimerCtrlState),
|
||||||
|
VMSTATE_UINT32(wtcr, NPCM7xxTimerCtrlState),
|
||||||
|
VMSTATE_STRUCT_ARRAY(timer, NPCM7xxTimerCtrlState,
|
||||||
|
NPCM7XX_TIMERS_PER_CTRL, 0, vmstate_npcm7xx_timer,
|
||||||
|
NPCM7xxTimer),
|
||||||
|
VMSTATE_END_OF_LIST(),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
static void npcm7xx_timer_class_init(ObjectClass *klass, void *data)
|
||||||
|
{
|
||||||
|
ResettableClass *rc = RESETTABLE_CLASS(klass);
|
||||||
|
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||||
|
|
||||||
|
QEMU_BUILD_BUG_ON(NPCM7XX_TIMER_REGS_END > NPCM7XX_TIMER_NR_REGS);
|
||||||
|
|
||||||
|
dc->desc = "NPCM7xx Timer Controller";
|
||||||
|
dc->realize = npcm7xx_timer_realize;
|
||||||
|
dc->vmsd = &vmstate_npcm7xx_timer_ctrl;
|
||||||
|
rc->phases.enter = npcm7xx_timer_enter_reset;
|
||||||
|
rc->phases.hold = npcm7xx_timer_hold_reset;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const TypeInfo npcm7xx_timer_info = {
|
||||||
|
.name = TYPE_NPCM7XX_TIMER,
|
||||||
|
.parent = TYPE_SYS_BUS_DEVICE,
|
||||||
|
.instance_size = sizeof(NPCM7xxTimerCtrlState),
|
||||||
|
.class_init = npcm7xx_timer_class_init,
|
||||||
|
};
|
||||||
|
|
||||||
|
static void npcm7xx_timer_register_type(void)
|
||||||
|
{
|
||||||
|
type_register_static(&npcm7xx_timer_info);
|
||||||
|
}
|
||||||
|
type_init(npcm7xx_timer_register_type);
|
|
@ -66,6 +66,11 @@ cmsdk_apb_dualtimer_read(uint64_t offset, uint64_t data, unsigned size) "CMSDK A
|
||||||
cmsdk_apb_dualtimer_write(uint64_t offset, uint64_t data, unsigned size) "CMSDK APB dualtimer write: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u"
|
cmsdk_apb_dualtimer_write(uint64_t offset, uint64_t data, unsigned size) "CMSDK APB dualtimer write: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u"
|
||||||
cmsdk_apb_dualtimer_reset(void) "CMSDK APB dualtimer: reset"
|
cmsdk_apb_dualtimer_reset(void) "CMSDK APB dualtimer: reset"
|
||||||
|
|
||||||
|
# npcm7xx_timer.c
|
||||||
|
npcm7xx_timer_read(const char *id, uint64_t offset, uint64_t value) " %s offset: 0x%04" PRIx64 " value 0x%08" PRIx64
|
||||||
|
npcm7xx_timer_write(const char *id, uint64_t offset, uint64_t value) "%s offset: 0x%04" PRIx64 " value 0x%08" PRIx64
|
||||||
|
npcm7xx_timer_irq(const char *id, int timer, int state) "%s timer %d state %d"
|
||||||
|
|
||||||
# nrf51_timer.c
|
# nrf51_timer.c
|
||||||
nrf51_timer_read(uint8_t timer_id, uint64_t addr, uint32_t value, unsigned size) "timer %u read addr 0x%" PRIx64 " data 0x%" PRIx32 " size %u"
|
nrf51_timer_read(uint8_t timer_id, uint64_t addr, uint32_t value, unsigned size) "timer %u read addr 0x%" PRIx64 " data 0x%" PRIx32 " size %u"
|
||||||
nrf51_timer_write(uint8_t timer_id, uint64_t addr, uint32_t value, unsigned size) "timer %u write addr 0x%" PRIx64 " data 0x%" PRIx32 " size %u"
|
nrf51_timer_write(uint8_t timer_id, uint64_t addr, uint32_t value, unsigned size) "timer %u write addr 0x%" PRIx64 " data 0x%" PRIx32 " size %u"
|
||||||
|
|
|
@ -0,0 +1,112 @@
|
||||||
|
/*
|
||||||
|
* Nuvoton NPCM7xx SoC family.
|
||||||
|
*
|
||||||
|
* Copyright 2020 Google LLC
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that 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.
|
||||||
|
*/
|
||||||
|
#ifndef NPCM7XX_H
|
||||||
|
#define NPCM7XX_H
|
||||||
|
|
||||||
|
#include "hw/boards.h"
|
||||||
|
#include "hw/cpu/a9mpcore.h"
|
||||||
|
#include "hw/mem/npcm7xx_mc.h"
|
||||||
|
#include "hw/misc/npcm7xx_clk.h"
|
||||||
|
#include "hw/misc/npcm7xx_gcr.h"
|
||||||
|
#include "hw/nvram/npcm7xx_otp.h"
|
||||||
|
#include "hw/timer/npcm7xx_timer.h"
|
||||||
|
#include "hw/ssi/npcm7xx_fiu.h"
|
||||||
|
#include "target/arm/cpu.h"
|
||||||
|
|
||||||
|
#define NPCM7XX_MAX_NUM_CPUS (2)
|
||||||
|
|
||||||
|
/* The first half of the address space is reserved for DDR4 DRAM. */
|
||||||
|
#define NPCM7XX_DRAM_BA (0x00000000)
|
||||||
|
#define NPCM7XX_DRAM_SZ (2 * GiB)
|
||||||
|
|
||||||
|
/* Magic addresses for setting up direct kernel booting and SMP boot stubs. */
|
||||||
|
#define NPCM7XX_LOADER_START (0x00000000) /* Start of SDRAM */
|
||||||
|
#define NPCM7XX_SMP_LOADER_START (0xffff0000) /* Boot ROM */
|
||||||
|
#define NPCM7XX_SMP_BOOTREG_ADDR (0xf080013c) /* GCR.SCRPAD */
|
||||||
|
#define NPCM7XX_GIC_CPU_IF_ADDR (0xf03fe100) /* GIC within A9 */
|
||||||
|
#define NPCM7XX_BOARD_SETUP_ADDR (0xffff1000) /* Boot ROM */
|
||||||
|
|
||||||
|
typedef struct NPCM7xxMachine {
|
||||||
|
MachineState parent;
|
||||||
|
} NPCM7xxMachine;
|
||||||
|
|
||||||
|
#define TYPE_NPCM7XX_MACHINE MACHINE_TYPE_NAME("npcm7xx")
|
||||||
|
#define NPCM7XX_MACHINE(obj) \
|
||||||
|
OBJECT_CHECK(NPCM7xxMachine, (obj), TYPE_NPCM7XX_MACHINE)
|
||||||
|
|
||||||
|
typedef struct NPCM7xxMachineClass {
|
||||||
|
MachineClass parent;
|
||||||
|
|
||||||
|
const char *soc_type;
|
||||||
|
} NPCM7xxMachineClass;
|
||||||
|
|
||||||
|
#define NPCM7XX_MACHINE_CLASS(klass) \
|
||||||
|
OBJECT_CLASS_CHECK(NPCM7xxMachineClass, (klass), TYPE_NPCM7XX_MACHINE)
|
||||||
|
#define NPCM7XX_MACHINE_GET_CLASS(obj) \
|
||||||
|
OBJECT_GET_CLASS(NPCM7xxMachineClass, (obj), TYPE_NPCM7XX_MACHINE)
|
||||||
|
|
||||||
|
typedef struct NPCM7xxState {
|
||||||
|
DeviceState parent;
|
||||||
|
|
||||||
|
ARMCPU cpu[NPCM7XX_MAX_NUM_CPUS];
|
||||||
|
A9MPPrivState a9mpcore;
|
||||||
|
|
||||||
|
MemoryRegion sram;
|
||||||
|
MemoryRegion irom;
|
||||||
|
MemoryRegion ram3;
|
||||||
|
MemoryRegion *dram;
|
||||||
|
|
||||||
|
NPCM7xxGCRState gcr;
|
||||||
|
NPCM7xxCLKState clk;
|
||||||
|
NPCM7xxTimerCtrlState tim[3];
|
||||||
|
NPCM7xxOTPState key_storage;
|
||||||
|
NPCM7xxOTPState fuse_array;
|
||||||
|
NPCM7xxMCState mc;
|
||||||
|
NPCM7xxFIUState fiu[2];
|
||||||
|
} NPCM7xxState;
|
||||||
|
|
||||||
|
#define TYPE_NPCM7XX "npcm7xx"
|
||||||
|
#define NPCM7XX(obj) OBJECT_CHECK(NPCM7xxState, (obj), TYPE_NPCM7XX)
|
||||||
|
|
||||||
|
#define TYPE_NPCM730 "npcm730"
|
||||||
|
#define TYPE_NPCM750 "npcm750"
|
||||||
|
|
||||||
|
typedef struct NPCM7xxClass {
|
||||||
|
DeviceClass parent;
|
||||||
|
|
||||||
|
/* Bitmask of modules that are permanently disabled on this chip. */
|
||||||
|
uint32_t disabled_modules;
|
||||||
|
/* Number of CPU cores enabled in this SoC class (may be 1 or 2). */
|
||||||
|
uint32_t num_cpus;
|
||||||
|
} NPCM7xxClass;
|
||||||
|
|
||||||
|
#define NPCM7XX_CLASS(klass) \
|
||||||
|
OBJECT_CLASS_CHECK(NPCM7xxClass, (klass), TYPE_NPCM7XX)
|
||||||
|
#define NPCM7XX_GET_CLASS(obj) \
|
||||||
|
OBJECT_GET_CLASS(NPCM7xxClass, (obj), TYPE_NPCM7XX)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* npcm7xx_load_kernel - Loads memory with everything needed to boot
|
||||||
|
* @machine - The machine containing the SoC to be booted.
|
||||||
|
* @soc - The SoC containing the CPU to be booted.
|
||||||
|
*
|
||||||
|
* This will set up the ARM boot info structure for the specific NPCM7xx
|
||||||
|
* derivative and call arm_load_kernel() to set up loading of the kernel, etc.
|
||||||
|
* into memory, if requested by the user.
|
||||||
|
*/
|
||||||
|
void npcm7xx_load_kernel(MachineState *machine, NPCM7xxState *soc);
|
||||||
|
|
||||||
|
#endif /* NPCM7XX_H */
|
|
@ -0,0 +1,36 @@
|
||||||
|
/*
|
||||||
|
* Nuvoton NPCM7xx Memory Controller stub
|
||||||
|
*
|
||||||
|
* Copyright 2020 Google LLC
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that 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.
|
||||||
|
*/
|
||||||
|
#ifndef NPCM7XX_MC_H
|
||||||
|
#define NPCM7XX_MC_H
|
||||||
|
|
||||||
|
#include "exec/memory.h"
|
||||||
|
#include "hw/sysbus.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* struct NPCM7xxMCState - Device state for the memory controller.
|
||||||
|
* @parent: System bus device.
|
||||||
|
* @mmio: Memory region through which registers are accessed.
|
||||||
|
*/
|
||||||
|
typedef struct NPCM7xxMCState {
|
||||||
|
SysBusDevice parent;
|
||||||
|
|
||||||
|
MemoryRegion mmio;
|
||||||
|
} NPCM7xxMCState;
|
||||||
|
|
||||||
|
#define TYPE_NPCM7XX_MC "npcm7xx-mc"
|
||||||
|
#define NPCM7XX_MC(obj) OBJECT_CHECK(NPCM7xxMCState, (obj), TYPE_NPCM7XX_MC)
|
||||||
|
|
||||||
|
#endif /* NPCM7XX_MC_H */
|
|
@ -0,0 +1,48 @@
|
||||||
|
/*
|
||||||
|
* Nuvoton NPCM7xx Clock Control Registers.
|
||||||
|
*
|
||||||
|
* Copyright 2020 Google LLC
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that 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.
|
||||||
|
*/
|
||||||
|
#ifndef NPCM7XX_CLK_H
|
||||||
|
#define NPCM7XX_CLK_H
|
||||||
|
|
||||||
|
#include "exec/memory.h"
|
||||||
|
#include "hw/sysbus.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The reference clock frequency for the timer modules, and the SECCNT and
|
||||||
|
* CNTR25M registers in this module, is always 25 MHz.
|
||||||
|
*/
|
||||||
|
#define NPCM7XX_TIMER_REF_HZ (25000000)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Number of registers in our device state structure. Don't change this without
|
||||||
|
* incrementing the version_id in the vmstate.
|
||||||
|
*/
|
||||||
|
#define NPCM7XX_CLK_NR_REGS (0x70 / sizeof(uint32_t))
|
||||||
|
|
||||||
|
typedef struct NPCM7xxCLKState {
|
||||||
|
SysBusDevice parent;
|
||||||
|
|
||||||
|
MemoryRegion iomem;
|
||||||
|
|
||||||
|
uint32_t regs[NPCM7XX_CLK_NR_REGS];
|
||||||
|
|
||||||
|
/* Time reference for SECCNT and CNTR25M, initialized by power on reset */
|
||||||
|
int64_t ref_ns;
|
||||||
|
} NPCM7xxCLKState;
|
||||||
|
|
||||||
|
#define TYPE_NPCM7XX_CLK "npcm7xx-clk"
|
||||||
|
#define NPCM7XX_CLK(obj) OBJECT_CHECK(NPCM7xxCLKState, (obj), TYPE_NPCM7XX_CLK)
|
||||||
|
|
||||||
|
#endif /* NPCM7XX_CLK_H */
|
|
@ -0,0 +1,43 @@
|
||||||
|
/*
|
||||||
|
* Nuvoton NPCM7xx System Global Control Registers.
|
||||||
|
*
|
||||||
|
* Copyright 2020 Google LLC
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that 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.
|
||||||
|
*/
|
||||||
|
#ifndef NPCM7XX_GCR_H
|
||||||
|
#define NPCM7XX_GCR_H
|
||||||
|
|
||||||
|
#include "exec/memory.h"
|
||||||
|
#include "hw/sysbus.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Number of registers in our device state structure. Don't change this without
|
||||||
|
* incrementing the version_id in the vmstate.
|
||||||
|
*/
|
||||||
|
#define NPCM7XX_GCR_NR_REGS (0x148 / sizeof(uint32_t))
|
||||||
|
|
||||||
|
typedef struct NPCM7xxGCRState {
|
||||||
|
SysBusDevice parent;
|
||||||
|
|
||||||
|
MemoryRegion iomem;
|
||||||
|
|
||||||
|
uint32_t regs[NPCM7XX_GCR_NR_REGS];
|
||||||
|
|
||||||
|
uint32_t reset_pwron;
|
||||||
|
uint32_t reset_mdlr;
|
||||||
|
uint32_t reset_intcr3;
|
||||||
|
} NPCM7xxGCRState;
|
||||||
|
|
||||||
|
#define TYPE_NPCM7XX_GCR "npcm7xx-gcr"
|
||||||
|
#define NPCM7XX_GCR(obj) OBJECT_CHECK(NPCM7xxGCRState, (obj), TYPE_NPCM7XX_GCR)
|
||||||
|
|
||||||
|
#endif /* NPCM7XX_GCR_H */
|
|
@ -0,0 +1,79 @@
|
||||||
|
/*
|
||||||
|
* Nuvoton NPCM7xx OTP (Fuse Array) Interface
|
||||||
|
*
|
||||||
|
* Copyright 2020 Google LLC
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that 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.
|
||||||
|
*/
|
||||||
|
#ifndef NPCM7XX_OTP_H
|
||||||
|
#define NPCM7XX_OTP_H
|
||||||
|
|
||||||
|
#include "exec/memory.h"
|
||||||
|
#include "hw/sysbus.h"
|
||||||
|
|
||||||
|
/* Each OTP module holds 8192 bits of one-time programmable storage */
|
||||||
|
#define NPCM7XX_OTP_ARRAY_BITS (8192)
|
||||||
|
#define NPCM7XX_OTP_ARRAY_BYTES (NPCM7XX_OTP_ARRAY_BITS / BITS_PER_BYTE)
|
||||||
|
|
||||||
|
/* Fuse array offsets */
|
||||||
|
#define NPCM7XX_FUSE_FUSTRAP (0)
|
||||||
|
#define NPCM7XX_FUSE_CP_FUSTRAP (12)
|
||||||
|
#define NPCM7XX_FUSE_DAC_CALIB (16)
|
||||||
|
#define NPCM7XX_FUSE_ADC_CALIB (24)
|
||||||
|
#define NPCM7XX_FUSE_DERIVATIVE (64)
|
||||||
|
#define NPCM7XX_FUSE_TEST_SIG (72)
|
||||||
|
#define NPCM7XX_FUSE_DIE_LOCATION (74)
|
||||||
|
#define NPCM7XX_FUSE_GP1 (80)
|
||||||
|
#define NPCM7XX_FUSE_GP2 (128)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Number of registers in our device state structure. Don't change this without
|
||||||
|
* incrementing the version_id in the vmstate.
|
||||||
|
*/
|
||||||
|
#define NPCM7XX_OTP_NR_REGS (0x18 / sizeof(uint32_t))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* struct NPCM7xxOTPState - Device state for one OTP module.
|
||||||
|
* @parent: System bus device.
|
||||||
|
* @mmio: Memory region through which registers are accessed.
|
||||||
|
* @regs: Register contents.
|
||||||
|
* @array: OTP storage array.
|
||||||
|
*/
|
||||||
|
typedef struct NPCM7xxOTPState {
|
||||||
|
SysBusDevice parent;
|
||||||
|
|
||||||
|
MemoryRegion mmio;
|
||||||
|
uint32_t regs[NPCM7XX_OTP_NR_REGS];
|
||||||
|
uint8_t array[NPCM7XX_OTP_ARRAY_BYTES];
|
||||||
|
} NPCM7xxOTPState;
|
||||||
|
|
||||||
|
#define TYPE_NPCM7XX_OTP "npcm7xx-otp"
|
||||||
|
#define NPCM7XX_OTP(obj) OBJECT_CHECK(NPCM7xxOTPState, (obj), TYPE_NPCM7XX_OTP)
|
||||||
|
|
||||||
|
#define TYPE_NPCM7XX_KEY_STORAGE "npcm7xx-key-storage"
|
||||||
|
#define TYPE_NPCM7XX_FUSE_ARRAY "npcm7xx-fuse-array"
|
||||||
|
|
||||||
|
typedef struct NPCM7xxOTPClass NPCM7xxOTPClass;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* npcm7xx_otp_array_write - ECC encode and write data to OTP array.
|
||||||
|
* @s: OTP module.
|
||||||
|
* @data: Data to be encoded and written.
|
||||||
|
* @offset: Offset of first byte to be written in the OTP array.
|
||||||
|
* @len: Number of bytes before ECC encoding.
|
||||||
|
*
|
||||||
|
* Each nibble of data is encoded into a byte, so the number of bytes written
|
||||||
|
* to the array will be @len * 2.
|
||||||
|
*/
|
||||||
|
extern void npcm7xx_otp_array_write(NPCM7xxOTPState *s, const void *data,
|
||||||
|
unsigned int offset, unsigned int len);
|
||||||
|
|
||||||
|
#endif /* NPCM7XX_OTP_H */
|
|
@ -0,0 +1,73 @@
|
||||||
|
/*
|
||||||
|
* Nuvoton NPCM7xx Flash Interface Unit (FIU)
|
||||||
|
*
|
||||||
|
* Copyright 2020 Google LLC
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that 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.
|
||||||
|
*/
|
||||||
|
#ifndef NPCM7XX_FIU_H
|
||||||
|
#define NPCM7XX_FIU_H
|
||||||
|
|
||||||
|
#include "hw/ssi/ssi.h"
|
||||||
|
#include "hw/sysbus.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Number of registers in our device state structure. Don't change this without
|
||||||
|
* incrementing the version_id in the vmstate.
|
||||||
|
*/
|
||||||
|
#define NPCM7XX_FIU_NR_REGS (0x7c / sizeof(uint32_t))
|
||||||
|
|
||||||
|
typedef struct NPCM7xxFIUState NPCM7xxFIUState;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* struct NPCM7xxFIUFlash - Per-chipselect flash controller state.
|
||||||
|
* @direct_access: Memory region for direct flash access.
|
||||||
|
* @fiu: Pointer to flash controller shared state.
|
||||||
|
*/
|
||||||
|
typedef struct NPCM7xxFIUFlash {
|
||||||
|
MemoryRegion direct_access;
|
||||||
|
NPCM7xxFIUState *fiu;
|
||||||
|
} NPCM7xxFIUFlash;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* NPCM7xxFIUState - Device state for one Flash Interface Unit.
|
||||||
|
* @parent: System bus device.
|
||||||
|
* @mmio: Memory region for register access.
|
||||||
|
* @cs_count: Number of flash chips that may be connected to this module.
|
||||||
|
* @active_cs: Currently active chip select, or -1 if no chip is selected.
|
||||||
|
* @cs_lines: GPIO lines that may be wired to flash chips.
|
||||||
|
* @flash: Array of @cs_count per-flash-chip state objects.
|
||||||
|
* @spi: The SPI bus mastered by this controller.
|
||||||
|
* @regs: Register contents.
|
||||||
|
*
|
||||||
|
* Each FIU has a shared bank of registers, and controls up to four chip
|
||||||
|
* selects. Each chip select has a dedicated memory region which may be used to
|
||||||
|
* read and write the flash connected to that chip select as if it were memory.
|
||||||
|
*/
|
||||||
|
struct NPCM7xxFIUState {
|
||||||
|
SysBusDevice parent;
|
||||||
|
|
||||||
|
MemoryRegion mmio;
|
||||||
|
|
||||||
|
int32_t cs_count;
|
||||||
|
int32_t active_cs;
|
||||||
|
qemu_irq *cs_lines;
|
||||||
|
NPCM7xxFIUFlash *flash;
|
||||||
|
|
||||||
|
SSIBus *spi;
|
||||||
|
|
||||||
|
uint32_t regs[NPCM7XX_FIU_NR_REGS];
|
||||||
|
};
|
||||||
|
|
||||||
|
#define TYPE_NPCM7XX_FIU "npcm7xx-fiu"
|
||||||
|
#define NPCM7XX_FIU(obj) OBJECT_CHECK(NPCM7xxFIUState, (obj), TYPE_NPCM7XX_FIU)
|
||||||
|
|
||||||
|
#endif /* NPCM7XX_FIU_H */
|
|
@ -0,0 +1,78 @@
|
||||||
|
/*
|
||||||
|
* Nuvoton NPCM7xx Timer Controller
|
||||||
|
*
|
||||||
|
* Copyright 2020 Google LLC
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that 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.
|
||||||
|
*/
|
||||||
|
#ifndef NPCM7XX_TIMER_H
|
||||||
|
#define NPCM7XX_TIMER_H
|
||||||
|
|
||||||
|
#include "exec/memory.h"
|
||||||
|
#include "hw/sysbus.h"
|
||||||
|
#include "qemu/timer.h"
|
||||||
|
|
||||||
|
/* Each Timer Module (TIM) instance holds five 25 MHz timers. */
|
||||||
|
#define NPCM7XX_TIMERS_PER_CTRL (5)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Number of registers in our device state structure. Don't change this without
|
||||||
|
* incrementing the version_id in the vmstate.
|
||||||
|
*/
|
||||||
|
#define NPCM7XX_TIMER_NR_REGS (0x54 / sizeof(uint32_t))
|
||||||
|
|
||||||
|
typedef struct NPCM7xxTimerCtrlState NPCM7xxTimerCtrlState;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* struct NPCM7xxTimer - Individual timer state.
|
||||||
|
* @irq: GIC interrupt line to fire on expiration (if enabled).
|
||||||
|
* @qtimer: QEMU timer that notifies us on expiration.
|
||||||
|
* @expires_ns: Absolute virtual expiration time.
|
||||||
|
* @remaining_ns: Remaining time until expiration if timer is paused.
|
||||||
|
* @tcsr: The Timer Control and Status Register.
|
||||||
|
* @ticr: The Timer Initial Count Register.
|
||||||
|
*/
|
||||||
|
typedef struct NPCM7xxTimer {
|
||||||
|
NPCM7xxTimerCtrlState *ctrl;
|
||||||
|
|
||||||
|
qemu_irq irq;
|
||||||
|
QEMUTimer qtimer;
|
||||||
|
int64_t expires_ns;
|
||||||
|
int64_t remaining_ns;
|
||||||
|
|
||||||
|
uint32_t tcsr;
|
||||||
|
uint32_t ticr;
|
||||||
|
} NPCM7xxTimer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* struct NPCM7xxTimerCtrlState - Timer Module device state.
|
||||||
|
* @parent: System bus device.
|
||||||
|
* @iomem: Memory region through which registers are accessed.
|
||||||
|
* @tisr: The Timer Interrupt Status Register.
|
||||||
|
* @wtcr: The Watchdog Timer Control Register.
|
||||||
|
* @timer: The five individual timers managed by this module.
|
||||||
|
*/
|
||||||
|
struct NPCM7xxTimerCtrlState {
|
||||||
|
SysBusDevice parent;
|
||||||
|
|
||||||
|
MemoryRegion iomem;
|
||||||
|
|
||||||
|
uint32_t tisr;
|
||||||
|
uint32_t wtcr;
|
||||||
|
|
||||||
|
NPCM7xxTimer timer[NPCM7XX_TIMERS_PER_CTRL];
|
||||||
|
};
|
||||||
|
|
||||||
|
#define TYPE_NPCM7XX_TIMER "npcm7xx-timer"
|
||||||
|
#define NPCM7XX_TIMER(obj) \
|
||||||
|
OBJECT_CHECK(NPCM7xxTimerCtrlState, (obj), TYPE_NPCM7XX_TIMER)
|
||||||
|
|
||||||
|
#endif /* NPCM7XX_TIMER_H */
|
|
@ -71,3 +71,9 @@
|
||||||
("Simplified BSD License" or "FreeBSD License", SPDX: BSD-2-Clause). OpenSBI
|
("Simplified BSD License" or "FreeBSD License", SPDX: BSD-2-Clause). OpenSBI
|
||||||
source code also contains code reused from other projects desribed here:
|
source code also contains code reused from other projects desribed here:
|
||||||
https://github.com/riscv/opensbi/blob/master/ThirdPartyNotices.md.
|
https://github.com/riscv/opensbi/blob/master/ThirdPartyNotices.md.
|
||||||
|
|
||||||
|
- npcm7xx_bootrom.bin is a simplified, free (Apache 2.0) boot ROM for Nuvoton
|
||||||
|
NPCM7xx BMC devices. It currently implements the bare minimum to load, parse,
|
||||||
|
initialize and run boot images stored in SPI flash, but may grow more
|
||||||
|
features over time as needed. The source code is available at:
|
||||||
|
https://github.com/google/vbootrom
|
||||||
|
|
|
@ -81,6 +81,7 @@ blobs = files(
|
||||||
'opensbi-riscv64-generic-fw_dynamic.bin',
|
'opensbi-riscv64-generic-fw_dynamic.bin',
|
||||||
'opensbi-riscv32-generic-fw_dynamic.elf',
|
'opensbi-riscv32-generic-fw_dynamic.elf',
|
||||||
'opensbi-riscv64-generic-fw_dynamic.elf',
|
'opensbi-riscv64-generic-fw_dynamic.elf',
|
||||||
|
'npcm7xx_bootrom.bin',
|
||||||
)
|
)
|
||||||
|
|
||||||
if install_blobs
|
if install_blobs
|
||||||
|
|
Binary file not shown.
|
@ -34,6 +34,7 @@ find-cross-gcc = $(firstword $(wildcard $(patsubst %ld,%gcc,$(call find-cross-ld
|
||||||
# finally strip off path + toolname so we get the prefix
|
# finally strip off path + toolname so we get the prefix
|
||||||
find-cross-prefix = $(subst gcc,,$(notdir $(call find-cross-gcc,$(1))))
|
find-cross-prefix = $(subst gcc,,$(notdir $(call find-cross-gcc,$(1))))
|
||||||
|
|
||||||
|
arm_cross_prefix := $(call find-cross-prefix,arm)
|
||||||
powerpc64_cross_prefix := $(call find-cross-prefix,powerpc64)
|
powerpc64_cross_prefix := $(call find-cross-prefix,powerpc64)
|
||||||
powerpc_cross_prefix := $(call find-cross-prefix,powerpc)
|
powerpc_cross_prefix := $(call find-cross-prefix,powerpc)
|
||||||
x86_64_cross_prefix := $(call find-cross-prefix,x86_64)
|
x86_64_cross_prefix := $(call find-cross-prefix,x86_64)
|
||||||
|
@ -63,6 +64,7 @@ default help:
|
||||||
@echo " skiboot -- update skiboot.lid"
|
@echo " skiboot -- update skiboot.lid"
|
||||||
@echo " u-boot.e500 -- update u-boot.e500"
|
@echo " u-boot.e500 -- update u-boot.e500"
|
||||||
@echo " u-boot.sam460 -- update u-boot.sam460"
|
@echo " u-boot.sam460 -- update u-boot.sam460"
|
||||||
|
@echo " npcm7xx_bootrom -- update vbootrom for npcm7xx"
|
||||||
@echo " efi -- update UEFI (edk2) platform firmware"
|
@echo " efi -- update UEFI (edk2) platform firmware"
|
||||||
@echo " opensbi32-generic -- update OpenSBI for 32-bit generic machine"
|
@echo " opensbi32-generic -- update OpenSBI for 32-bit generic machine"
|
||||||
@echo " opensbi64-generic -- update OpenSBI for 64-bit generic machine"
|
@echo " opensbi64-generic -- update OpenSBI for 64-bit generic machine"
|
||||||
|
@ -185,6 +187,10 @@ bios-microvm:
|
||||||
$(MAKE) -C qboot
|
$(MAKE) -C qboot
|
||||||
cp qboot/bios.bin ../pc-bios/bios-microvm.bin
|
cp qboot/bios.bin ../pc-bios/bios-microvm.bin
|
||||||
|
|
||||||
|
npcm7xx_bootrom:
|
||||||
|
$(MAKE) -C vbootrom CROSS_COMPILE=$(arm_cross_prefix)
|
||||||
|
cp vbootrom/npcm7xx_bootrom.bin ../pc-bios/npcm7xx_bootrom.bin
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -rf seabios/.config seabios/out seabios/builds
|
rm -rf seabios/.config seabios/out seabios/builds
|
||||||
$(MAKE) -C sgabios clean
|
$(MAKE) -C sgabios clean
|
||||||
|
@ -198,3 +204,4 @@ clean:
|
||||||
$(MAKE) -f Makefile.edk2 clean
|
$(MAKE) -f Makefile.edk2 clean
|
||||||
$(MAKE) -C opensbi clean
|
$(MAKE) -C opensbi clean
|
||||||
$(MAKE) -C qboot clean
|
$(MAKE) -C qboot clean
|
||||||
|
$(MAKE) -C vbootrom clean
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
Subproject commit 0c37a43527f0ee2b9584e7fb2fdc805e902635ac
|
|
@ -42,8 +42,14 @@ output_fd = None
|
||||||
insntype = 'uint32_t'
|
insntype = 'uint32_t'
|
||||||
decode_function = 'decode'
|
decode_function = 'decode'
|
||||||
|
|
||||||
re_ident = '[a-zA-Z][a-zA-Z0-9_]*'
|
# An identifier for C.
|
||||||
|
re_C_ident = '[a-zA-Z][a-zA-Z0-9_]*'
|
||||||
|
|
||||||
|
# Identifiers for Arguments, Fields, Formats and Patterns.
|
||||||
|
re_arg_ident = '&[a-zA-Z0-9_]*'
|
||||||
|
re_fld_ident = '%[a-zA-Z0-9_]*'
|
||||||
|
re_fmt_ident = '@[a-zA-Z0-9_]*'
|
||||||
|
re_pat_ident = '[a-zA-Z0-9_]*'
|
||||||
|
|
||||||
def error_with_file(file, lineno, *args):
|
def error_with_file(file, lineno, *args):
|
||||||
"""Print an error message from file:line and args and exit."""
|
"""Print an error message from file:line and args and exit."""
|
||||||
|
@ -632,7 +638,6 @@ class ExcMultiPattern(MultiPattern):
|
||||||
def parse_field(lineno, name, toks):
|
def parse_field(lineno, name, toks):
|
||||||
"""Parse one instruction field from TOKS at LINENO"""
|
"""Parse one instruction field from TOKS at LINENO"""
|
||||||
global fields
|
global fields
|
||||||
global re_ident
|
|
||||||
global insnwidth
|
global insnwidth
|
||||||
|
|
||||||
# A "simple" field will have only one entry;
|
# A "simple" field will have only one entry;
|
||||||
|
@ -641,7 +646,7 @@ def parse_field(lineno, name, toks):
|
||||||
width = 0
|
width = 0
|
||||||
func = None
|
func = None
|
||||||
for t in toks:
|
for t in toks:
|
||||||
if re.fullmatch('!function=' + re_ident, t):
|
if re.match('^!function=', t):
|
||||||
if func:
|
if func:
|
||||||
error(lineno, 'duplicate function')
|
error(lineno, 'duplicate function')
|
||||||
func = t.split('=')
|
func = t.split('=')
|
||||||
|
@ -695,7 +700,7 @@ def parse_field(lineno, name, toks):
|
||||||
def parse_arguments(lineno, name, toks):
|
def parse_arguments(lineno, name, toks):
|
||||||
"""Parse one argument set from TOKS at LINENO"""
|
"""Parse one argument set from TOKS at LINENO"""
|
||||||
global arguments
|
global arguments
|
||||||
global re_ident
|
global re_C_ident
|
||||||
global anyextern
|
global anyextern
|
||||||
|
|
||||||
flds = []
|
flds = []
|
||||||
|
@ -705,7 +710,7 @@ def parse_arguments(lineno, name, toks):
|
||||||
extern = True
|
extern = True
|
||||||
anyextern = True
|
anyextern = True
|
||||||
continue
|
continue
|
||||||
if not re.fullmatch(re_ident, t):
|
if not re.fullmatch(re_C_ident, t):
|
||||||
error(lineno, 'invalid argument set token "{0}"'.format(t))
|
error(lineno, 'invalid argument set token "{0}"'.format(t))
|
||||||
if t in flds:
|
if t in flds:
|
||||||
error(lineno, 'duplicate argument "{0}"'.format(t))
|
error(lineno, 'duplicate argument "{0}"'.format(t))
|
||||||
|
@ -791,7 +796,10 @@ def parse_generic(lineno, parent_pat, name, toks):
|
||||||
global arguments
|
global arguments
|
||||||
global formats
|
global formats
|
||||||
global allpatterns
|
global allpatterns
|
||||||
global re_ident
|
global re_arg_ident
|
||||||
|
global re_fld_ident
|
||||||
|
global re_fmt_ident
|
||||||
|
global re_C_ident
|
||||||
global insnwidth
|
global insnwidth
|
||||||
global insnmask
|
global insnmask
|
||||||
global variablewidth
|
global variablewidth
|
||||||
|
@ -807,7 +815,7 @@ def parse_generic(lineno, parent_pat, name, toks):
|
||||||
fmt = None
|
fmt = None
|
||||||
for t in toks:
|
for t in toks:
|
||||||
# '&Foo' gives a format an explcit argument set.
|
# '&Foo' gives a format an explcit argument set.
|
||||||
if t[0] == '&':
|
if re.fullmatch(re_arg_ident, t):
|
||||||
tt = t[1:]
|
tt = t[1:]
|
||||||
if arg:
|
if arg:
|
||||||
error(lineno, 'multiple argument sets')
|
error(lineno, 'multiple argument sets')
|
||||||
|
@ -818,7 +826,7 @@ def parse_generic(lineno, parent_pat, name, toks):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# '@Foo' gives a pattern an explicit format.
|
# '@Foo' gives a pattern an explicit format.
|
||||||
if t[0] == '@':
|
if re.fullmatch(re_fmt_ident, t):
|
||||||
tt = t[1:]
|
tt = t[1:]
|
||||||
if fmt:
|
if fmt:
|
||||||
error(lineno, 'multiple formats')
|
error(lineno, 'multiple formats')
|
||||||
|
@ -829,19 +837,19 @@ def parse_generic(lineno, parent_pat, name, toks):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# '%Foo' imports a field.
|
# '%Foo' imports a field.
|
||||||
if t[0] == '%':
|
if re.fullmatch(re_fld_ident, t):
|
||||||
tt = t[1:]
|
tt = t[1:]
|
||||||
flds = add_field_byname(lineno, flds, tt, tt)
|
flds = add_field_byname(lineno, flds, tt, tt)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# 'Foo=%Bar' imports a field with a different name.
|
# 'Foo=%Bar' imports a field with a different name.
|
||||||
if re.fullmatch(re_ident + '=%' + re_ident, t):
|
if re.fullmatch(re_C_ident + '=' + re_fld_ident, t):
|
||||||
(fname, iname) = t.split('=%')
|
(fname, iname) = t.split('=%')
|
||||||
flds = add_field_byname(lineno, flds, fname, iname)
|
flds = add_field_byname(lineno, flds, fname, iname)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# 'Foo=number' sets an argument field to a constant value
|
# 'Foo=number' sets an argument field to a constant value
|
||||||
if re.fullmatch(re_ident + '=[+-]?[0-9]+', t):
|
if re.fullmatch(re_C_ident + '=[+-]?[0-9]+', t):
|
||||||
(fname, value) = t.split('=')
|
(fname, value) = t.split('=')
|
||||||
value = int(value)
|
value = int(value)
|
||||||
flds = add_field(lineno, flds, fname, ConstField(value))
|
flds = add_field(lineno, flds, fname, ConstField(value))
|
||||||
|
@ -866,7 +874,7 @@ def parse_generic(lineno, parent_pat, name, toks):
|
||||||
fixedmask = (fixedmask << shift) | fms
|
fixedmask = (fixedmask << shift) | fms
|
||||||
undefmask = (undefmask << shift) | ubm
|
undefmask = (undefmask << shift) | ubm
|
||||||
# Otherwise, fieldname:fieldwidth
|
# Otherwise, fieldname:fieldwidth
|
||||||
elif re.fullmatch(re_ident + ':s?[0-9]+', t):
|
elif re.fullmatch(re_C_ident + ':s?[0-9]+', t):
|
||||||
(fname, flen) = t.split(':')
|
(fname, flen) = t.split(':')
|
||||||
sign = False
|
sign = False
|
||||||
if flen[0] == 's':
|
if flen[0] == 's':
|
||||||
|
@ -971,6 +979,10 @@ def parse_generic(lineno, parent_pat, name, toks):
|
||||||
|
|
||||||
def parse_file(f, parent_pat):
|
def parse_file(f, parent_pat):
|
||||||
"""Parse all of the patterns within a file"""
|
"""Parse all of the patterns within a file"""
|
||||||
|
global re_arg_ident
|
||||||
|
global re_fld_ident
|
||||||
|
global re_fmt_ident
|
||||||
|
global re_pat_ident
|
||||||
|
|
||||||
# Read all of the lines of the file. Concatenate lines
|
# Read all of the lines of the file. Concatenate lines
|
||||||
# ending in backslash; discard empty lines and comments.
|
# ending in backslash; discard empty lines and comments.
|
||||||
|
@ -1063,14 +1075,16 @@ def parse_file(f, parent_pat):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# Determine the type of object needing to be parsed.
|
# Determine the type of object needing to be parsed.
|
||||||
if name[0] == '%':
|
if re.fullmatch(re_fld_ident, name):
|
||||||
parse_field(start_lineno, name[1:], toks)
|
parse_field(start_lineno, name[1:], toks)
|
||||||
elif name[0] == '&':
|
elif re.fullmatch(re_arg_ident, name):
|
||||||
parse_arguments(start_lineno, name[1:], toks)
|
parse_arguments(start_lineno, name[1:], toks)
|
||||||
elif name[0] == '@':
|
elif re.fullmatch(re_fmt_ident, name):
|
||||||
parse_generic(start_lineno, None, name[1:], toks)
|
parse_generic(start_lineno, None, name[1:], toks)
|
||||||
else:
|
elif re.fullmatch(re_pat_ident, name):
|
||||||
parse_generic(start_lineno, parent_pat, name, toks)
|
parse_generic(start_lineno, parent_pat, name, toks)
|
||||||
|
else:
|
||||||
|
error(lineno, 'invalid token "{0}"'.format(name))
|
||||||
toks = []
|
toks = []
|
||||||
|
|
||||||
if nesting != 0:
|
if nesting != 0:
|
||||||
|
|
101
target/arm/cpu.c
101
target/arm/cpu.c
|
@ -2098,72 +2098,69 @@ static void cortex_a15_initfn(Object *obj)
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifndef TARGET_AARCH64
|
#ifndef TARGET_AARCH64
|
||||||
/* -cpu max: if KVM is enabled, like -cpu host (best possible with this host);
|
/*
|
||||||
* otherwise, a CPU with as many features enabled as our emulation supports.
|
* -cpu max: a CPU with as many features enabled as our emulation supports.
|
||||||
* The version of '-cpu max' for qemu-system-aarch64 is defined in cpu64.c;
|
* The version of '-cpu max' for qemu-system-aarch64 is defined in cpu64.c;
|
||||||
* this only needs to handle 32 bits.
|
* this only needs to handle 32 bits, and need not care about KVM.
|
||||||
*/
|
*/
|
||||||
static void arm_max_initfn(Object *obj)
|
static void arm_max_initfn(Object *obj)
|
||||||
{
|
{
|
||||||
ARMCPU *cpu = ARM_CPU(obj);
|
ARMCPU *cpu = ARM_CPU(obj);
|
||||||
|
|
||||||
if (kvm_enabled()) {
|
cortex_a15_initfn(obj);
|
||||||
kvm_arm_set_cpu_features_from_host(cpu);
|
|
||||||
} else {
|
|
||||||
cortex_a15_initfn(obj);
|
|
||||||
|
|
||||||
/* old-style VFP short-vector support */
|
/* old-style VFP short-vector support */
|
||||||
cpu->isar.mvfr0 = FIELD_DP32(cpu->isar.mvfr0, MVFR0, FPSHVEC, 1);
|
cpu->isar.mvfr0 = FIELD_DP32(cpu->isar.mvfr0, MVFR0, FPSHVEC, 1);
|
||||||
|
|
||||||
#ifdef CONFIG_USER_ONLY
|
#ifdef CONFIG_USER_ONLY
|
||||||
/* We don't set these in system emulation mode for the moment,
|
/*
|
||||||
* since we don't correctly set (all of) the ID registers to
|
* We don't set these in system emulation mode for the moment,
|
||||||
* advertise them.
|
* since we don't correctly set (all of) the ID registers to
|
||||||
*/
|
* advertise them.
|
||||||
set_feature(&cpu->env, ARM_FEATURE_V8);
|
*/
|
||||||
{
|
set_feature(&cpu->env, ARM_FEATURE_V8);
|
||||||
uint32_t t;
|
{
|
||||||
|
uint32_t t;
|
||||||
|
|
||||||
t = cpu->isar.id_isar5;
|
t = cpu->isar.id_isar5;
|
||||||
t = FIELD_DP32(t, ID_ISAR5, AES, 2);
|
t = FIELD_DP32(t, ID_ISAR5, AES, 2);
|
||||||
t = FIELD_DP32(t, ID_ISAR5, SHA1, 1);
|
t = FIELD_DP32(t, ID_ISAR5, SHA1, 1);
|
||||||
t = FIELD_DP32(t, ID_ISAR5, SHA2, 1);
|
t = FIELD_DP32(t, ID_ISAR5, SHA2, 1);
|
||||||
t = FIELD_DP32(t, ID_ISAR5, CRC32, 1);
|
t = FIELD_DP32(t, ID_ISAR5, CRC32, 1);
|
||||||
t = FIELD_DP32(t, ID_ISAR5, RDM, 1);
|
t = FIELD_DP32(t, ID_ISAR5, RDM, 1);
|
||||||
t = FIELD_DP32(t, ID_ISAR5, VCMA, 1);
|
t = FIELD_DP32(t, ID_ISAR5, VCMA, 1);
|
||||||
cpu->isar.id_isar5 = t;
|
cpu->isar.id_isar5 = t;
|
||||||
|
|
||||||
t = cpu->isar.id_isar6;
|
t = cpu->isar.id_isar6;
|
||||||
t = FIELD_DP32(t, ID_ISAR6, JSCVT, 1);
|
t = FIELD_DP32(t, ID_ISAR6, JSCVT, 1);
|
||||||
t = FIELD_DP32(t, ID_ISAR6, DP, 1);
|
t = FIELD_DP32(t, ID_ISAR6, DP, 1);
|
||||||
t = FIELD_DP32(t, ID_ISAR6, FHM, 1);
|
t = FIELD_DP32(t, ID_ISAR6, FHM, 1);
|
||||||
t = FIELD_DP32(t, ID_ISAR6, SB, 1);
|
t = FIELD_DP32(t, ID_ISAR6, SB, 1);
|
||||||
t = FIELD_DP32(t, ID_ISAR6, SPECRES, 1);
|
t = FIELD_DP32(t, ID_ISAR6, SPECRES, 1);
|
||||||
cpu->isar.id_isar6 = t;
|
cpu->isar.id_isar6 = t;
|
||||||
|
|
||||||
t = cpu->isar.mvfr1;
|
t = cpu->isar.mvfr1;
|
||||||
t = FIELD_DP32(t, MVFR1, FPHP, 3); /* v8.2-FP16 */
|
t = FIELD_DP32(t, MVFR1, FPHP, 3); /* v8.2-FP16 */
|
||||||
t = FIELD_DP32(t, MVFR1, SIMDHP, 2); /* v8.2-FP16 */
|
t = FIELD_DP32(t, MVFR1, SIMDHP, 2); /* v8.2-FP16 */
|
||||||
cpu->isar.mvfr1 = t;
|
cpu->isar.mvfr1 = t;
|
||||||
|
|
||||||
t = cpu->isar.mvfr2;
|
t = cpu->isar.mvfr2;
|
||||||
t = FIELD_DP32(t, MVFR2, SIMDMISC, 3); /* SIMD MaxNum */
|
t = FIELD_DP32(t, MVFR2, SIMDMISC, 3); /* SIMD MaxNum */
|
||||||
t = FIELD_DP32(t, MVFR2, FPMISC, 4); /* FP MaxNum */
|
t = FIELD_DP32(t, MVFR2, FPMISC, 4); /* FP MaxNum */
|
||||||
cpu->isar.mvfr2 = t;
|
cpu->isar.mvfr2 = t;
|
||||||
|
|
||||||
t = cpu->isar.id_mmfr3;
|
t = cpu->isar.id_mmfr3;
|
||||||
t = FIELD_DP32(t, ID_MMFR3, PAN, 2); /* ATS1E1 */
|
t = FIELD_DP32(t, ID_MMFR3, PAN, 2); /* ATS1E1 */
|
||||||
cpu->isar.id_mmfr3 = t;
|
cpu->isar.id_mmfr3 = t;
|
||||||
|
|
||||||
t = cpu->isar.id_mmfr4;
|
t = cpu->isar.id_mmfr4;
|
||||||
t = FIELD_DP32(t, ID_MMFR4, HPDS, 1); /* AA32HPD */
|
t = FIELD_DP32(t, ID_MMFR4, HPDS, 1); /* AA32HPD */
|
||||||
t = FIELD_DP32(t, ID_MMFR4, AC2, 1); /* ACTLR2, HACTLR2 */
|
t = FIELD_DP32(t, ID_MMFR4, AC2, 1); /* ACTLR2, HACTLR2 */
|
||||||
t = FIELD_DP32(t, ID_MMFR4, CNP, 1); /* TTCNP */
|
t = FIELD_DP32(t, ID_MMFR4, CNP, 1); /* TTCNP */
|
||||||
t = FIELD_DP32(t, ID_MMFR4, XNX, 1); /* TTS2UXN */
|
t = FIELD_DP32(t, ID_MMFR4, XNX, 1); /* TTS2UXN */
|
||||||
cpu->isar.id_mmfr4 = t;
|
cpu->isar.id_mmfr4 = t;
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -2267,11 +2264,7 @@ static void arm_host_initfn(Object *obj)
|
||||||
|
|
||||||
static const TypeInfo host_arm_cpu_type_info = {
|
static const TypeInfo host_arm_cpu_type_info = {
|
||||||
.name = TYPE_ARM_HOST_CPU,
|
.name = TYPE_ARM_HOST_CPU,
|
||||||
#ifdef TARGET_AARCH64
|
|
||||||
.parent = TYPE_AARCH64_CPU,
|
.parent = TYPE_AARCH64_CPU,
|
||||||
#else
|
|
||||||
.parent = TYPE_ARM_CPU,
|
|
||||||
#endif
|
|
||||||
.instance_init = arm_host_initfn,
|
.instance_init = arm_host_initfn,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1452,7 +1452,7 @@ static bool pmu_counter_enabled(CPUARMState *env, uint8_t counter)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
prohibited = arm_feature(env, ARM_FEATURE_EL3) &&
|
prohibited = arm_feature(env, ARM_FEATURE_EL3) &&
|
||||||
(env->cp15.mdcr_el3 & MDCR_SPME);
|
!(env->cp15.mdcr_el3 & MDCR_SPME);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (prohibited && counter == 31) {
|
if (prohibited && counter == 31) {
|
||||||
|
|
|
@ -136,16 +136,11 @@ MISMATCH_CHECK(QEMU_PSCI_RET_DISABLED, PSCI_RET_DISABLED);
|
||||||
*/
|
*/
|
||||||
#define QEMU_KVM_ARM_TARGET_NONE UINT_MAX
|
#define QEMU_KVM_ARM_TARGET_NONE UINT_MAX
|
||||||
|
|
||||||
#ifdef TARGET_AARCH64
|
|
||||||
MISMATCH_CHECK(QEMU_KVM_ARM_TARGET_AEM_V8, KVM_ARM_TARGET_AEM_V8);
|
MISMATCH_CHECK(QEMU_KVM_ARM_TARGET_AEM_V8, KVM_ARM_TARGET_AEM_V8);
|
||||||
MISMATCH_CHECK(QEMU_KVM_ARM_TARGET_FOUNDATION_V8, KVM_ARM_TARGET_FOUNDATION_V8);
|
MISMATCH_CHECK(QEMU_KVM_ARM_TARGET_FOUNDATION_V8, KVM_ARM_TARGET_FOUNDATION_V8);
|
||||||
MISMATCH_CHECK(QEMU_KVM_ARM_TARGET_CORTEX_A57, KVM_ARM_TARGET_CORTEX_A57);
|
MISMATCH_CHECK(QEMU_KVM_ARM_TARGET_CORTEX_A57, KVM_ARM_TARGET_CORTEX_A57);
|
||||||
MISMATCH_CHECK(QEMU_KVM_ARM_TARGET_XGENE_POTENZA, KVM_ARM_TARGET_XGENE_POTENZA);
|
MISMATCH_CHECK(QEMU_KVM_ARM_TARGET_XGENE_POTENZA, KVM_ARM_TARGET_XGENE_POTENZA);
|
||||||
MISMATCH_CHECK(QEMU_KVM_ARM_TARGET_CORTEX_A53, KVM_ARM_TARGET_CORTEX_A53);
|
MISMATCH_CHECK(QEMU_KVM_ARM_TARGET_CORTEX_A53, KVM_ARM_TARGET_CORTEX_A53);
|
||||||
#else
|
|
||||||
MISMATCH_CHECK(QEMU_KVM_ARM_TARGET_CORTEX_A15, KVM_ARM_TARGET_CORTEX_A15);
|
|
||||||
MISMATCH_CHECK(QEMU_KVM_ARM_TARGET_CORTEX_A7, KVM_ARM_TARGET_CORTEX_A7);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define CP_REG_ARM64 0x6000000000000000ULL
|
#define CP_REG_ARM64 0x6000000000000000ULL
|
||||||
#define CP_REG_ARM_COPROC_MASK 0x000000000FFF0000
|
#define CP_REG_ARM_COPROC_MASK 0x000000000FFF0000
|
||||||
|
@ -165,7 +160,6 @@ MISMATCH_CHECK(QEMU_KVM_ARM_TARGET_CORTEX_A7, KVM_ARM_TARGET_CORTEX_A7);
|
||||||
/* No kernel define but it's useful to QEMU */
|
/* No kernel define but it's useful to QEMU */
|
||||||
#define CP_REG_ARM64_SYSREG_CP (CP_REG_ARM64_SYSREG >> CP_REG_ARM_COPROC_SHIFT)
|
#define CP_REG_ARM64_SYSREG_CP (CP_REG_ARM64_SYSREG >> CP_REG_ARM_COPROC_SHIFT)
|
||||||
|
|
||||||
#ifdef TARGET_AARCH64
|
|
||||||
MISMATCH_CHECK(CP_REG_ARM64, KVM_REG_ARM64);
|
MISMATCH_CHECK(CP_REG_ARM64, KVM_REG_ARM64);
|
||||||
MISMATCH_CHECK(CP_REG_ARM_COPROC_MASK, KVM_REG_ARM_COPROC_MASK);
|
MISMATCH_CHECK(CP_REG_ARM_COPROC_MASK, KVM_REG_ARM_COPROC_MASK);
|
||||||
MISMATCH_CHECK(CP_REG_ARM_COPROC_SHIFT, KVM_REG_ARM_COPROC_SHIFT);
|
MISMATCH_CHECK(CP_REG_ARM_COPROC_SHIFT, KVM_REG_ARM_COPROC_SHIFT);
|
||||||
|
@ -180,7 +174,6 @@ MISMATCH_CHECK(CP_REG_ARM64_SYSREG_CRM_MASK, KVM_REG_ARM64_SYSREG_CRM_MASK);
|
||||||
MISMATCH_CHECK(CP_REG_ARM64_SYSREG_CRM_SHIFT, KVM_REG_ARM64_SYSREG_CRM_SHIFT);
|
MISMATCH_CHECK(CP_REG_ARM64_SYSREG_CRM_SHIFT, KVM_REG_ARM64_SYSREG_CRM_SHIFT);
|
||||||
MISMATCH_CHECK(CP_REG_ARM64_SYSREG_OP2_MASK, KVM_REG_ARM64_SYSREG_OP2_MASK);
|
MISMATCH_CHECK(CP_REG_ARM64_SYSREG_OP2_MASK, KVM_REG_ARM64_SYSREG_OP2_MASK);
|
||||||
MISMATCH_CHECK(CP_REG_ARM64_SYSREG_OP2_SHIFT, KVM_REG_ARM64_SYSREG_OP2_SHIFT);
|
MISMATCH_CHECK(CP_REG_ARM64_SYSREG_OP2_SHIFT, KVM_REG_ARM64_SYSREG_OP2_SHIFT);
|
||||||
#endif
|
|
||||||
|
|
||||||
#undef MISMATCH_CHECK
|
#undef MISMATCH_CHECK
|
||||||
|
|
||||||
|
|
|
@ -918,22 +918,15 @@ int kvm_arch_process_async_events(CPUState *cs)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* The #ifdef protections are until 32bit headers are imported and can
|
|
||||||
* be removed once both 32 and 64 bit reach feature parity.
|
|
||||||
*/
|
|
||||||
void kvm_arch_update_guest_debug(CPUState *cs, struct kvm_guest_debug *dbg)
|
void kvm_arch_update_guest_debug(CPUState *cs, struct kvm_guest_debug *dbg)
|
||||||
{
|
{
|
||||||
#ifdef KVM_GUESTDBG_USE_SW_BP
|
|
||||||
if (kvm_sw_breakpoints_active(cs)) {
|
if (kvm_sw_breakpoints_active(cs)) {
|
||||||
dbg->control |= KVM_GUESTDBG_ENABLE | KVM_GUESTDBG_USE_SW_BP;
|
dbg->control |= KVM_GUESTDBG_ENABLE | KVM_GUESTDBG_USE_SW_BP;
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
#ifdef KVM_GUESTDBG_USE_HW
|
|
||||||
if (kvm_arm_hw_debug_active(cs)) {
|
if (kvm_arm_hw_debug_active(cs)) {
|
||||||
dbg->control |= KVM_GUESTDBG_ENABLE | KVM_GUESTDBG_USE_HW;
|
dbg->control |= KVM_GUESTDBG_ENABLE | KVM_GUESTDBG_USE_HW;
|
||||||
kvm_arm_copy_hw_debug_data(&dbg->arch);
|
kvm_arm_copy_hw_debug_data(&dbg->arch);
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void kvm_arch_init_irq_routing(KVMState *s)
|
void kvm_arch_init_irq_routing(KVMState *s)
|
||||||
|
|
|
@ -1,595 +0,0 @@
|
||||||
/*
|
|
||||||
* ARM implementation of KVM hooks, 32 bit specific code.
|
|
||||||
*
|
|
||||||
* Copyright Christoffer Dall 2009-2010
|
|
||||||
*
|
|
||||||
* This work is licensed under the terms of the GNU GPL, version 2 or later.
|
|
||||||
* See the COPYING file in the top-level directory.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "qemu/osdep.h"
|
|
||||||
#include <sys/ioctl.h>
|
|
||||||
|
|
||||||
#include <linux/kvm.h>
|
|
||||||
|
|
||||||
#include "qemu-common.h"
|
|
||||||
#include "cpu.h"
|
|
||||||
#include "qemu/timer.h"
|
|
||||||
#include "sysemu/runstate.h"
|
|
||||||
#include "sysemu/kvm.h"
|
|
||||||
#include "kvm_arm.h"
|
|
||||||
#include "internals.h"
|
|
||||||
#include "qemu/log.h"
|
|
||||||
|
|
||||||
static int read_sys_reg32(int fd, uint32_t *pret, uint64_t id)
|
|
||||||
{
|
|
||||||
struct kvm_one_reg idreg = { .id = id, .addr = (uintptr_t)pret };
|
|
||||||
|
|
||||||
assert((id & KVM_REG_SIZE_MASK) == KVM_REG_SIZE_U32);
|
|
||||||
return ioctl(fd, KVM_GET_ONE_REG, &idreg);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool kvm_arm_get_host_cpu_features(ARMHostCPUFeatures *ahcf)
|
|
||||||
{
|
|
||||||
/* Identify the feature bits corresponding to the host CPU, and
|
|
||||||
* fill out the ARMHostCPUClass fields accordingly. To do this
|
|
||||||
* we have to create a scratch VM, create a single CPU inside it,
|
|
||||||
* and then query that CPU for the relevant ID registers.
|
|
||||||
*/
|
|
||||||
int err = 0, fdarray[3];
|
|
||||||
uint32_t midr, id_pfr0;
|
|
||||||
uint64_t features = 0;
|
|
||||||
|
|
||||||
/* Old kernels may not know about the PREFERRED_TARGET ioctl: however
|
|
||||||
* we know these will only support creating one kind of guest CPU,
|
|
||||||
* which is its preferred CPU type.
|
|
||||||
*/
|
|
||||||
static const uint32_t cpus_to_try[] = {
|
|
||||||
QEMU_KVM_ARM_TARGET_CORTEX_A15,
|
|
||||||
QEMU_KVM_ARM_TARGET_NONE
|
|
||||||
};
|
|
||||||
/*
|
|
||||||
* target = -1 informs kvm_arm_create_scratch_host_vcpu()
|
|
||||||
* to use the preferred target
|
|
||||||
*/
|
|
||||||
struct kvm_vcpu_init init = { .target = -1, };
|
|
||||||
|
|
||||||
if (!kvm_arm_create_scratch_host_vcpu(cpus_to_try, fdarray, &init)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
ahcf->target = init.target;
|
|
||||||
|
|
||||||
/* This is not strictly blessed by the device tree binding docs yet,
|
|
||||||
* but in practice the kernel does not care about this string so
|
|
||||||
* there is no point maintaining an KVM_ARM_TARGET_* -> string table.
|
|
||||||
*/
|
|
||||||
ahcf->dtb_compatible = "arm,arm-v7";
|
|
||||||
|
|
||||||
err |= read_sys_reg32(fdarray[2], &midr, ARM_CP15_REG32(0, 0, 0, 0));
|
|
||||||
err |= read_sys_reg32(fdarray[2], &id_pfr0, ARM_CP15_REG32(0, 0, 1, 0));
|
|
||||||
|
|
||||||
err |= read_sys_reg32(fdarray[2], &ahcf->isar.id_isar0,
|
|
||||||
ARM_CP15_REG32(0, 0, 2, 0));
|
|
||||||
err |= read_sys_reg32(fdarray[2], &ahcf->isar.id_isar1,
|
|
||||||
ARM_CP15_REG32(0, 0, 2, 1));
|
|
||||||
err |= read_sys_reg32(fdarray[2], &ahcf->isar.id_isar2,
|
|
||||||
ARM_CP15_REG32(0, 0, 2, 2));
|
|
||||||
err |= read_sys_reg32(fdarray[2], &ahcf->isar.id_isar3,
|
|
||||||
ARM_CP15_REG32(0, 0, 2, 3));
|
|
||||||
err |= read_sys_reg32(fdarray[2], &ahcf->isar.id_isar4,
|
|
||||||
ARM_CP15_REG32(0, 0, 2, 4));
|
|
||||||
err |= read_sys_reg32(fdarray[2], &ahcf->isar.id_isar5,
|
|
||||||
ARM_CP15_REG32(0, 0, 2, 5));
|
|
||||||
if (read_sys_reg32(fdarray[2], &ahcf->isar.id_isar6,
|
|
||||||
ARM_CP15_REG32(0, 0, 2, 7))) {
|
|
||||||
/*
|
|
||||||
* Older kernels don't support reading ID_ISAR6. This register was
|
|
||||||
* only introduced in ARMv8, so we can assume that it is zero on a
|
|
||||||
* CPU that a kernel this old is running on.
|
|
||||||
*/
|
|
||||||
ahcf->isar.id_isar6 = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
err |= read_sys_reg32(fdarray[2], &ahcf->isar.id_dfr0,
|
|
||||||
ARM_CP15_REG32(0, 0, 1, 2));
|
|
||||||
|
|
||||||
err |= read_sys_reg32(fdarray[2], &ahcf->isar.mvfr0,
|
|
||||||
KVM_REG_ARM | KVM_REG_SIZE_U32 |
|
|
||||||
KVM_REG_ARM_VFP | KVM_REG_ARM_VFP_MVFR0);
|
|
||||||
err |= read_sys_reg32(fdarray[2], &ahcf->isar.mvfr1,
|
|
||||||
KVM_REG_ARM | KVM_REG_SIZE_U32 |
|
|
||||||
KVM_REG_ARM_VFP | KVM_REG_ARM_VFP_MVFR1);
|
|
||||||
/*
|
|
||||||
* FIXME: There is not yet a way to read MVFR2.
|
|
||||||
* Fortunately there is not yet anything in there that affects migration.
|
|
||||||
*/
|
|
||||||
|
|
||||||
err |= read_sys_reg32(fdarray[2], &ahcf->isar.id_mmfr0,
|
|
||||||
ARM_CP15_REG32(0, 0, 1, 4));
|
|
||||||
err |= read_sys_reg32(fdarray[2], &ahcf->isar.id_mmfr1,
|
|
||||||
ARM_CP15_REG32(0, 0, 1, 5));
|
|
||||||
err |= read_sys_reg32(fdarray[2], &ahcf->isar.id_mmfr2,
|
|
||||||
ARM_CP15_REG32(0, 0, 1, 6));
|
|
||||||
err |= read_sys_reg32(fdarray[2], &ahcf->isar.id_mmfr3,
|
|
||||||
ARM_CP15_REG32(0, 0, 1, 7));
|
|
||||||
if (read_sys_reg32(fdarray[2], &ahcf->isar.id_mmfr4,
|
|
||||||
ARM_CP15_REG32(0, 0, 2, 6))) {
|
|
||||||
/*
|
|
||||||
* Older kernels don't support reading ID_MMFR4 (a new in v8
|
|
||||||
* register); assume it's zero.
|
|
||||||
*/
|
|
||||||
ahcf->isar.id_mmfr4 = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* There is no way to read DBGDIDR, because currently 32-bit KVM
|
|
||||||
* doesn't implement debug at all. Leave it at zero.
|
|
||||||
*/
|
|
||||||
|
|
||||||
kvm_arm_destroy_scratch_host_vcpu(fdarray);
|
|
||||||
|
|
||||||
if (err < 0) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Now we've retrieved all the register information we can
|
|
||||||
* set the feature bits based on the ID register fields.
|
|
||||||
* We can assume any KVM supporting CPU is at least a v7
|
|
||||||
* with VFPv3, virtualization extensions, and the generic
|
|
||||||
* timers; this in turn implies most of the other feature
|
|
||||||
* bits, but a few must be tested.
|
|
||||||
*/
|
|
||||||
features |= 1ULL << ARM_FEATURE_V7VE;
|
|
||||||
features |= 1ULL << ARM_FEATURE_GENERIC_TIMER;
|
|
||||||
|
|
||||||
if (extract32(id_pfr0, 12, 4) == 1) {
|
|
||||||
features |= 1ULL << ARM_FEATURE_THUMB2EE;
|
|
||||||
}
|
|
||||||
if (extract32(ahcf->isar.mvfr1, 12, 4) == 1) {
|
|
||||||
features |= 1ULL << ARM_FEATURE_NEON;
|
|
||||||
}
|
|
||||||
|
|
||||||
ahcf->features = features;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool kvm_arm_reg_syncs_via_cpreg_list(uint64_t regidx)
|
|
||||||
{
|
|
||||||
/* Return true if the regidx is a register we should synchronize
|
|
||||||
* via the cpreg_tuples array (ie is not a core reg we sync by
|
|
||||||
* hand in kvm_arch_get/put_registers())
|
|
||||||
*/
|
|
||||||
switch (regidx & KVM_REG_ARM_COPROC_MASK) {
|
|
||||||
case KVM_REG_ARM_CORE:
|
|
||||||
case KVM_REG_ARM_VFP:
|
|
||||||
return false;
|
|
||||||
default:
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
typedef struct CPRegStateLevel {
|
|
||||||
uint64_t regidx;
|
|
||||||
int level;
|
|
||||||
} CPRegStateLevel;
|
|
||||||
|
|
||||||
/* All coprocessor registers not listed in the following table are assumed to
|
|
||||||
* be of the level KVM_PUT_RUNTIME_STATE. If a register should be written less
|
|
||||||
* often, you must add it to this table with a state of either
|
|
||||||
* KVM_PUT_RESET_STATE or KVM_PUT_FULL_STATE.
|
|
||||||
*/
|
|
||||||
static const CPRegStateLevel non_runtime_cpregs[] = {
|
|
||||||
{ KVM_REG_ARM_TIMER_CNT, KVM_PUT_FULL_STATE },
|
|
||||||
};
|
|
||||||
|
|
||||||
int kvm_arm_cpreg_level(uint64_t regidx)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
|
|
||||||
for (i = 0; i < ARRAY_SIZE(non_runtime_cpregs); i++) {
|
|
||||||
const CPRegStateLevel *l = &non_runtime_cpregs[i];
|
|
||||||
if (l->regidx == regidx) {
|
|
||||||
return l->level;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return KVM_PUT_RUNTIME_STATE;
|
|
||||||
}
|
|
||||||
|
|
||||||
#define ARM_CPU_ID_MPIDR 0, 0, 0, 5
|
|
||||||
|
|
||||||
int kvm_arch_init_vcpu(CPUState *cs)
|
|
||||||
{
|
|
||||||
int ret;
|
|
||||||
uint64_t v;
|
|
||||||
uint32_t mpidr;
|
|
||||||
struct kvm_one_reg r;
|
|
||||||
ARMCPU *cpu = ARM_CPU(cs);
|
|
||||||
|
|
||||||
if (cpu->kvm_target == QEMU_KVM_ARM_TARGET_NONE) {
|
|
||||||
fprintf(stderr, "KVM is not supported for this guest CPU type\n");
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
qemu_add_vm_change_state_handler(kvm_arm_vm_state_change, cs);
|
|
||||||
|
|
||||||
/* Determine init features for this CPU */
|
|
||||||
memset(cpu->kvm_init_features, 0, sizeof(cpu->kvm_init_features));
|
|
||||||
if (cs->start_powered_off) {
|
|
||||||
cpu->kvm_init_features[0] |= 1 << KVM_ARM_VCPU_POWER_OFF;
|
|
||||||
}
|
|
||||||
if (kvm_check_extension(cs->kvm_state, KVM_CAP_ARM_PSCI_0_2)) {
|
|
||||||
cpu->psci_version = 2;
|
|
||||||
cpu->kvm_init_features[0] |= 1 << KVM_ARM_VCPU_PSCI_0_2;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Do KVM_ARM_VCPU_INIT ioctl */
|
|
||||||
ret = kvm_arm_vcpu_init(cs);
|
|
||||||
if (ret) {
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Query the kernel to make sure it supports 32 VFP
|
|
||||||
* registers: QEMU's "cortex-a15" CPU is always a
|
|
||||||
* VFP-D32 core. The simplest way to do this is just
|
|
||||||
* to attempt to read register d31.
|
|
||||||
*/
|
|
||||||
r.id = KVM_REG_ARM | KVM_REG_SIZE_U64 | KVM_REG_ARM_VFP | 31;
|
|
||||||
r.addr = (uintptr_t)(&v);
|
|
||||||
ret = kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, &r);
|
|
||||||
if (ret == -ENOENT) {
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* When KVM is in use, PSCI is emulated in-kernel and not by qemu.
|
|
||||||
* Currently KVM has its own idea about MPIDR assignment, so we
|
|
||||||
* override our defaults with what we get from KVM.
|
|
||||||
*/
|
|
||||||
ret = kvm_get_one_reg(cs, ARM_CP15_REG32(ARM_CPU_ID_MPIDR), &mpidr);
|
|
||||||
if (ret) {
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
cpu->mp_affinity = mpidr & ARM32_AFFINITY_MASK;
|
|
||||||
|
|
||||||
/* Check whether userspace can specify guest syndrome value */
|
|
||||||
kvm_arm_init_serror_injection(cs);
|
|
||||||
|
|
||||||
return kvm_arm_init_cpreg_list(cpu);
|
|
||||||
}
|
|
||||||
|
|
||||||
int kvm_arch_destroy_vcpu(CPUState *cs)
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
typedef struct Reg {
|
|
||||||
uint64_t id;
|
|
||||||
int offset;
|
|
||||||
} Reg;
|
|
||||||
|
|
||||||
#define COREREG(KERNELNAME, QEMUFIELD) \
|
|
||||||
{ \
|
|
||||||
KVM_REG_ARM | KVM_REG_SIZE_U32 | \
|
|
||||||
KVM_REG_ARM_CORE | KVM_REG_ARM_CORE_REG(KERNELNAME), \
|
|
||||||
offsetof(CPUARMState, QEMUFIELD) \
|
|
||||||
}
|
|
||||||
|
|
||||||
#define VFPSYSREG(R) \
|
|
||||||
{ \
|
|
||||||
KVM_REG_ARM | KVM_REG_SIZE_U32 | KVM_REG_ARM_VFP | \
|
|
||||||
KVM_REG_ARM_VFP_##R, \
|
|
||||||
offsetof(CPUARMState, vfp.xregs[ARM_VFP_##R]) \
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Like COREREG, but handle fields which are in a uint64_t in CPUARMState. */
|
|
||||||
#define COREREG64(KERNELNAME, QEMUFIELD) \
|
|
||||||
{ \
|
|
||||||
KVM_REG_ARM | KVM_REG_SIZE_U32 | \
|
|
||||||
KVM_REG_ARM_CORE | KVM_REG_ARM_CORE_REG(KERNELNAME), \
|
|
||||||
offsetoflow32(CPUARMState, QEMUFIELD) \
|
|
||||||
}
|
|
||||||
|
|
||||||
static const Reg regs[] = {
|
|
||||||
/* R0_usr .. R14_usr */
|
|
||||||
COREREG(usr_regs.uregs[0], regs[0]),
|
|
||||||
COREREG(usr_regs.uregs[1], regs[1]),
|
|
||||||
COREREG(usr_regs.uregs[2], regs[2]),
|
|
||||||
COREREG(usr_regs.uregs[3], regs[3]),
|
|
||||||
COREREG(usr_regs.uregs[4], regs[4]),
|
|
||||||
COREREG(usr_regs.uregs[5], regs[5]),
|
|
||||||
COREREG(usr_regs.uregs[6], regs[6]),
|
|
||||||
COREREG(usr_regs.uregs[7], regs[7]),
|
|
||||||
COREREG(usr_regs.uregs[8], usr_regs[0]),
|
|
||||||
COREREG(usr_regs.uregs[9], usr_regs[1]),
|
|
||||||
COREREG(usr_regs.uregs[10], usr_regs[2]),
|
|
||||||
COREREG(usr_regs.uregs[11], usr_regs[3]),
|
|
||||||
COREREG(usr_regs.uregs[12], usr_regs[4]),
|
|
||||||
COREREG(usr_regs.uregs[13], banked_r13[BANK_USRSYS]),
|
|
||||||
COREREG(usr_regs.uregs[14], banked_r14[BANK_USRSYS]),
|
|
||||||
/* R13, R14, SPSR for SVC, ABT, UND, IRQ banks */
|
|
||||||
COREREG(svc_regs[0], banked_r13[BANK_SVC]),
|
|
||||||
COREREG(svc_regs[1], banked_r14[BANK_SVC]),
|
|
||||||
COREREG64(svc_regs[2], banked_spsr[BANK_SVC]),
|
|
||||||
COREREG(abt_regs[0], banked_r13[BANK_ABT]),
|
|
||||||
COREREG(abt_regs[1], banked_r14[BANK_ABT]),
|
|
||||||
COREREG64(abt_regs[2], banked_spsr[BANK_ABT]),
|
|
||||||
COREREG(und_regs[0], banked_r13[BANK_UND]),
|
|
||||||
COREREG(und_regs[1], banked_r14[BANK_UND]),
|
|
||||||
COREREG64(und_regs[2], banked_spsr[BANK_UND]),
|
|
||||||
COREREG(irq_regs[0], banked_r13[BANK_IRQ]),
|
|
||||||
COREREG(irq_regs[1], banked_r14[BANK_IRQ]),
|
|
||||||
COREREG64(irq_regs[2], banked_spsr[BANK_IRQ]),
|
|
||||||
/* R8_fiq .. R14_fiq and SPSR_fiq */
|
|
||||||
COREREG(fiq_regs[0], fiq_regs[0]),
|
|
||||||
COREREG(fiq_regs[1], fiq_regs[1]),
|
|
||||||
COREREG(fiq_regs[2], fiq_regs[2]),
|
|
||||||
COREREG(fiq_regs[3], fiq_regs[3]),
|
|
||||||
COREREG(fiq_regs[4], fiq_regs[4]),
|
|
||||||
COREREG(fiq_regs[5], banked_r13[BANK_FIQ]),
|
|
||||||
COREREG(fiq_regs[6], banked_r14[BANK_FIQ]),
|
|
||||||
COREREG64(fiq_regs[7], banked_spsr[BANK_FIQ]),
|
|
||||||
/* R15 */
|
|
||||||
COREREG(usr_regs.uregs[15], regs[15]),
|
|
||||||
/* VFP system registers */
|
|
||||||
VFPSYSREG(FPSID),
|
|
||||||
VFPSYSREG(MVFR1),
|
|
||||||
VFPSYSREG(MVFR0),
|
|
||||||
VFPSYSREG(FPEXC),
|
|
||||||
VFPSYSREG(FPINST),
|
|
||||||
VFPSYSREG(FPINST2),
|
|
||||||
};
|
|
||||||
|
|
||||||
int kvm_arch_put_registers(CPUState *cs, int level)
|
|
||||||
{
|
|
||||||
ARMCPU *cpu = ARM_CPU(cs);
|
|
||||||
CPUARMState *env = &cpu->env;
|
|
||||||
struct kvm_one_reg r;
|
|
||||||
int mode, bn;
|
|
||||||
int ret, i;
|
|
||||||
uint32_t cpsr, fpscr;
|
|
||||||
|
|
||||||
/* Make sure the banked regs are properly set */
|
|
||||||
mode = env->uncached_cpsr & CPSR_M;
|
|
||||||
bn = bank_number(mode);
|
|
||||||
if (mode == ARM_CPU_MODE_FIQ) {
|
|
||||||
memcpy(env->fiq_regs, env->regs + 8, 5 * sizeof(uint32_t));
|
|
||||||
} else {
|
|
||||||
memcpy(env->usr_regs, env->regs + 8, 5 * sizeof(uint32_t));
|
|
||||||
}
|
|
||||||
env->banked_r13[bn] = env->regs[13];
|
|
||||||
env->banked_spsr[bn] = env->spsr;
|
|
||||||
env->banked_r14[r14_bank_number(mode)] = env->regs[14];
|
|
||||||
|
|
||||||
/* Now we can safely copy stuff down to the kernel */
|
|
||||||
for (i = 0; i < ARRAY_SIZE(regs); i++) {
|
|
||||||
r.id = regs[i].id;
|
|
||||||
r.addr = (uintptr_t)(env) + regs[i].offset;
|
|
||||||
ret = kvm_vcpu_ioctl(cs, KVM_SET_ONE_REG, &r);
|
|
||||||
if (ret) {
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Special cases which aren't a single CPUARMState field */
|
|
||||||
cpsr = cpsr_read(env);
|
|
||||||
r.id = KVM_REG_ARM | KVM_REG_SIZE_U32 |
|
|
||||||
KVM_REG_ARM_CORE | KVM_REG_ARM_CORE_REG(usr_regs.ARM_cpsr);
|
|
||||||
r.addr = (uintptr_t)(&cpsr);
|
|
||||||
ret = kvm_vcpu_ioctl(cs, KVM_SET_ONE_REG, &r);
|
|
||||||
if (ret) {
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* VFP registers */
|
|
||||||
r.id = KVM_REG_ARM | KVM_REG_SIZE_U64 | KVM_REG_ARM_VFP;
|
|
||||||
for (i = 0; i < 32; i++) {
|
|
||||||
r.addr = (uintptr_t)aa32_vfp_dreg(env, i);
|
|
||||||
ret = kvm_vcpu_ioctl(cs, KVM_SET_ONE_REG, &r);
|
|
||||||
if (ret) {
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
r.id++;
|
|
||||||
}
|
|
||||||
|
|
||||||
r.id = KVM_REG_ARM | KVM_REG_SIZE_U32 | KVM_REG_ARM_VFP |
|
|
||||||
KVM_REG_ARM_VFP_FPSCR;
|
|
||||||
fpscr = vfp_get_fpscr(env);
|
|
||||||
r.addr = (uintptr_t)&fpscr;
|
|
||||||
ret = kvm_vcpu_ioctl(cs, KVM_SET_ONE_REG, &r);
|
|
||||||
if (ret) {
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
write_cpustate_to_list(cpu, true);
|
|
||||||
|
|
||||||
if (!write_list_to_kvmstate(cpu, level)) {
|
|
||||||
return EINVAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Setting VCPU events should be triggered after syncing the registers
|
|
||||||
* to avoid overwriting potential changes made by KVM upon calling
|
|
||||||
* KVM_SET_VCPU_EVENTS ioctl
|
|
||||||
*/
|
|
||||||
ret = kvm_put_vcpu_events(cpu);
|
|
||||||
if (ret) {
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
kvm_arm_sync_mpstate_to_kvm(cpu);
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
int kvm_arch_get_registers(CPUState *cs)
|
|
||||||
{
|
|
||||||
ARMCPU *cpu = ARM_CPU(cs);
|
|
||||||
CPUARMState *env = &cpu->env;
|
|
||||||
struct kvm_one_reg r;
|
|
||||||
int mode, bn;
|
|
||||||
int ret, i;
|
|
||||||
uint32_t cpsr, fpscr;
|
|
||||||
|
|
||||||
for (i = 0; i < ARRAY_SIZE(regs); i++) {
|
|
||||||
r.id = regs[i].id;
|
|
||||||
r.addr = (uintptr_t)(env) + regs[i].offset;
|
|
||||||
ret = kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, &r);
|
|
||||||
if (ret) {
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Special cases which aren't a single CPUARMState field */
|
|
||||||
r.id = KVM_REG_ARM | KVM_REG_SIZE_U32 |
|
|
||||||
KVM_REG_ARM_CORE | KVM_REG_ARM_CORE_REG(usr_regs.ARM_cpsr);
|
|
||||||
r.addr = (uintptr_t)(&cpsr);
|
|
||||||
ret = kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, &r);
|
|
||||||
if (ret) {
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
cpsr_write(env, cpsr, 0xffffffff, CPSRWriteRaw);
|
|
||||||
|
|
||||||
/* Make sure the current mode regs are properly set */
|
|
||||||
mode = env->uncached_cpsr & CPSR_M;
|
|
||||||
bn = bank_number(mode);
|
|
||||||
if (mode == ARM_CPU_MODE_FIQ) {
|
|
||||||
memcpy(env->regs + 8, env->fiq_regs, 5 * sizeof(uint32_t));
|
|
||||||
} else {
|
|
||||||
memcpy(env->regs + 8, env->usr_regs, 5 * sizeof(uint32_t));
|
|
||||||
}
|
|
||||||
env->regs[13] = env->banked_r13[bn];
|
|
||||||
env->spsr = env->banked_spsr[bn];
|
|
||||||
env->regs[14] = env->banked_r14[r14_bank_number(mode)];
|
|
||||||
|
|
||||||
/* VFP registers */
|
|
||||||
r.id = KVM_REG_ARM | KVM_REG_SIZE_U64 | KVM_REG_ARM_VFP;
|
|
||||||
for (i = 0; i < 32; i++) {
|
|
||||||
r.addr = (uintptr_t)aa32_vfp_dreg(env, i);
|
|
||||||
ret = kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, &r);
|
|
||||||
if (ret) {
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
r.id++;
|
|
||||||
}
|
|
||||||
|
|
||||||
r.id = KVM_REG_ARM | KVM_REG_SIZE_U32 | KVM_REG_ARM_VFP |
|
|
||||||
KVM_REG_ARM_VFP_FPSCR;
|
|
||||||
r.addr = (uintptr_t)&fpscr;
|
|
||||||
ret = kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, &r);
|
|
||||||
if (ret) {
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
vfp_set_fpscr(env, fpscr);
|
|
||||||
|
|
||||||
ret = kvm_get_vcpu_events(cpu);
|
|
||||||
if (ret) {
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!write_kvmstate_to_list(cpu)) {
|
|
||||||
return EINVAL;
|
|
||||||
}
|
|
||||||
/* Note that it's OK to have registers which aren't in CPUState,
|
|
||||||
* so we can ignore a failure return here.
|
|
||||||
*/
|
|
||||||
write_list_to_cpustate(cpu);
|
|
||||||
|
|
||||||
kvm_arm_sync_mpstate_to_qemu(cpu);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int kvm_arch_insert_sw_breakpoint(CPUState *cs, struct kvm_sw_breakpoint *bp)
|
|
||||||
{
|
|
||||||
qemu_log_mask(LOG_UNIMP, "%s: guest debug not yet implemented\n", __func__);
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
int kvm_arch_remove_sw_breakpoint(CPUState *cs, struct kvm_sw_breakpoint *bp)
|
|
||||||
{
|
|
||||||
qemu_log_mask(LOG_UNIMP, "%s: guest debug not yet implemented\n", __func__);
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool kvm_arm_handle_debug(CPUState *cs, struct kvm_debug_exit_arch *debug_exit)
|
|
||||||
{
|
|
||||||
qemu_log_mask(LOG_UNIMP, "%s: guest debug not yet implemented\n", __func__);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
int kvm_arch_insert_hw_breakpoint(target_ulong addr,
|
|
||||||
target_ulong len, int type)
|
|
||||||
{
|
|
||||||
qemu_log_mask(LOG_UNIMP, "%s: not implemented\n", __func__);
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
int kvm_arch_remove_hw_breakpoint(target_ulong addr,
|
|
||||||
target_ulong len, int type)
|
|
||||||
{
|
|
||||||
qemu_log_mask(LOG_UNIMP, "%s: not implemented\n", __func__);
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
void kvm_arch_remove_all_hw_breakpoints(void)
|
|
||||||
{
|
|
||||||
qemu_log_mask(LOG_UNIMP, "%s: not implemented\n", __func__);
|
|
||||||
}
|
|
||||||
|
|
||||||
void kvm_arm_copy_hw_debug_data(struct kvm_guest_debug_arch *ptr)
|
|
||||||
{
|
|
||||||
qemu_log_mask(LOG_UNIMP, "%s: not implemented\n", __func__);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool kvm_arm_hw_debug_active(CPUState *cs)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void kvm_arm_pmu_set_irq(CPUState *cs, int irq)
|
|
||||||
{
|
|
||||||
qemu_log_mask(LOG_UNIMP, "%s: not implemented\n", __func__);
|
|
||||||
}
|
|
||||||
|
|
||||||
void kvm_arm_pmu_init(CPUState *cs)
|
|
||||||
{
|
|
||||||
qemu_log_mask(LOG_UNIMP, "%s: not implemented\n", __func__);
|
|
||||||
}
|
|
||||||
|
|
||||||
#define ARM_REG_DFSR ARM_CP15_REG32(0, 5, 0, 0)
|
|
||||||
#define ARM_REG_TTBCR ARM_CP15_REG32(0, 2, 0, 2)
|
|
||||||
/*
|
|
||||||
*DFSR:
|
|
||||||
* TTBCR.EAE == 0
|
|
||||||
* FS[4] - DFSR[10]
|
|
||||||
* FS[3:0] - DFSR[3:0]
|
|
||||||
* TTBCR.EAE == 1
|
|
||||||
* FS, bits [5:0]
|
|
||||||
*/
|
|
||||||
#define DFSR_FSC(lpae, v) \
|
|
||||||
((lpae) ? ((v) & 0x3F) : (((v) >> 6) | ((v) & 0x1F)))
|
|
||||||
|
|
||||||
#define DFSC_EXTABT(lpae) ((lpae) ? 0x10 : 0x08)
|
|
||||||
|
|
||||||
bool kvm_arm_verify_ext_dabt_pending(CPUState *cs)
|
|
||||||
{
|
|
||||||
uint32_t dfsr_val;
|
|
||||||
|
|
||||||
if (!kvm_get_one_reg(cs, ARM_REG_DFSR, &dfsr_val)) {
|
|
||||||
ARMCPU *cpu = ARM_CPU(cs);
|
|
||||||
CPUARMState *env = &cpu->env;
|
|
||||||
uint32_t ttbcr;
|
|
||||||
int lpae = 0;
|
|
||||||
|
|
||||||
if (!kvm_get_one_reg(cs, ARM_REG_TTBCR, &ttbcr)) {
|
|
||||||
lpae = arm_feature(env, ARM_FEATURE_LPAE) && (ttbcr & TTBCR_EAE);
|
|
||||||
}
|
|
||||||
/* The verification is based on FS filed of the DFSR reg only*/
|
|
||||||
return (DFSR_FSC(lpae, dfsr_val) == DFSC_EXTABT(lpae));
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
|
@ -406,13 +406,7 @@ static inline const char *gic_class_name(void)
|
||||||
static inline const char *gicv3_class_name(void)
|
static inline const char *gicv3_class_name(void)
|
||||||
{
|
{
|
||||||
if (kvm_irqchip_in_kernel()) {
|
if (kvm_irqchip_in_kernel()) {
|
||||||
#ifdef TARGET_AARCH64
|
|
||||||
return "kvm-arm-gicv3";
|
return "kvm-arm-gicv3";
|
||||||
#else
|
|
||||||
error_report("KVM GICv3 acceleration is not supported on this "
|
|
||||||
"platform");
|
|
||||||
exit(1);
|
|
||||||
#endif
|
|
||||||
} else {
|
} else {
|
||||||
if (kvm_enabled()) {
|
if (kvm_enabled()) {
|
||||||
error_report("Userspace GICv3 is not supported with KVM");
|
error_report("Userspace GICv3 is not supported with KVM");
|
||||||
|
|
|
@ -34,10 +34,7 @@ arm_ss.add(zlib)
|
||||||
|
|
||||||
arm_ss.add(when: 'CONFIG_TCG', if_true: files('arm-semi.c'))
|
arm_ss.add(when: 'CONFIG_TCG', if_true: files('arm-semi.c'))
|
||||||
|
|
||||||
kvm_ss = ss.source_set()
|
arm_ss.add(when: 'CONFIG_KVM', if_true: files('kvm.c', 'kvm64.c'), if_false: files('kvm-stub.c'))
|
||||||
kvm_ss.add(when: 'TARGET_AARCH64', if_true: files('kvm64.c'), if_false: files('kvm32.c'))
|
|
||||||
arm_ss.add_all(when: 'CONFIG_KVM', if_true: kvm_ss)
|
|
||||||
arm_ss.add(when: 'CONFIG_KVM', if_true: files('kvm.c'), if_false: files('kvm-stub.c'))
|
|
||||||
|
|
||||||
arm_ss.add(when: 'TARGET_AARCH64', if_true: files(
|
arm_ss.add(when: 'TARGET_AARCH64', if_true: files(
|
||||||
'cpu64.c',
|
'cpu64.c',
|
||||||
|
|
|
@ -45,11 +45,16 @@
|
||||||
@3same_q0 .... ... . . . size:2 .... .... .... . 0 . . .... \
|
@3same_q0 .... ... . . . size:2 .... .... .... . 0 . . .... \
|
||||||
&3same vm=%vm_dp vn=%vn_dp vd=%vd_dp q=0
|
&3same vm=%vm_dp vn=%vn_dp vd=%vd_dp q=0
|
||||||
|
|
||||||
# For FP insns the high bit of 'size' is used as part of opcode decode
|
# For FP insns the high bit of 'size' is used as part of opcode decode,
|
||||||
@3same_fp .... ... . . . . size:1 .... .... .... . q:1 . . .... \
|
# and the 'size' bit is 0 for 32-bit float and 1 for 16-bit float.
|
||||||
&3same vm=%vm_dp vn=%vn_dp vd=%vd_dp
|
# This converts this encoding to the same MO_8/16/32/64 values that the
|
||||||
@3same_fp_q0 .... ... . . . . size:1 .... .... .... . 0 . . .... \
|
# integer neon insns use.
|
||||||
&3same vm=%vm_dp vn=%vn_dp vd=%vd_dp q=0
|
%3same_fp_size 20:1 !function=neon_3same_fp_size
|
||||||
|
|
||||||
|
@3same_fp .... ... . . . . . .... .... .... . q:1 . . .... \
|
||||||
|
&3same vm=%vm_dp vn=%vn_dp vd=%vd_dp size=%3same_fp_size
|
||||||
|
@3same_fp_q0 .... ... . . . . . .... .... .... . 0 . . .... \
|
||||||
|
&3same vm=%vm_dp vn=%vn_dp vd=%vd_dp q=0 size=%3same_fp_size
|
||||||
|
|
||||||
VHADD_S_3s 1111 001 0 0 . .. .... .... 0000 . . . 0 .... @3same
|
VHADD_S_3s 1111 001 0 0 . .. .... .... 0000 . . . 0 .... @3same
|
||||||
VHADD_U_3s 1111 001 1 0 . .. .... .... 0000 . . . 0 .... @3same
|
VHADD_U_3s 1111 001 1 0 . .. .... .... 0000 . . . 0 .... @3same
|
||||||
|
@ -251,9 +256,8 @@ VMINNM_fp_3s 1111 001 1 0 . 1 . .... .... 1111 ... 1 .... @3same_fp
|
||||||
@2reg_shll_b .... ... . . . 001 shift:3 .... .... 0 . . . .... \
|
@2reg_shll_b .... ... . . . 001 shift:3 .... .... 0 . . . .... \
|
||||||
&2reg_shift vm=%vm_dp vd=%vd_dp size=0 q=0
|
&2reg_shift vm=%vm_dp vd=%vd_dp size=0 q=0
|
||||||
|
|
||||||
# We use size=0 for fp32 and size=1 for fp16 to match the 3-same encodings.
|
|
||||||
@2reg_vcvt .... ... . . . 1 ..... .... .... . q:1 . . .... \
|
@2reg_vcvt .... ... . . . 1 ..... .... .... . q:1 . . .... \
|
||||||
&2reg_shift vm=%vm_dp vd=%vd_dp size=0 shift=%neon_rshift_i5
|
&2reg_shift vm=%vm_dp vd=%vd_dp size=2 shift=%neon_rshift_i5
|
||||||
@2reg_vcvt_f16 .... ... . . . 11 .... .... .... . q:1 . . .... \
|
@2reg_vcvt_f16 .... ... . . . 11 .... .... .... . q:1 . . .... \
|
||||||
&2reg_shift vm=%vm_dp vd=%vd_dp size=1 shift=%neon_rshift_i4
|
&2reg_shift vm=%vm_dp vd=%vd_dp size=1 shift=%neon_rshift_i4
|
||||||
|
|
||||||
|
|
|
@ -34,11 +34,17 @@
|
||||||
%vd_dp 22:1 12:4
|
%vd_dp 22:1 12:4
|
||||||
%vd_sp 12:4 22:1
|
%vd_sp 12:4 22:1
|
||||||
|
|
||||||
VCMLA 1111 110 rot:2 . 1 size:1 .... .... 1000 . q:1 . 0 .... \
|
# For VCMLA/VCADD insns, convert the single-bit size field
|
||||||
vm=%vm_dp vn=%vn_dp vd=%vd_dp
|
# which is 0 for fp16 and 1 for fp32 into a MO_* constant.
|
||||||
|
# (Note that this is the reverse of the sense of the 1-bit size
|
||||||
|
# field in the 3same_fp Neon insns.)
|
||||||
|
%vcadd_size 20:1 !function=plus1
|
||||||
|
|
||||||
VCADD 1111 110 rot:1 1 . 0 size:1 .... .... 1000 . q:1 . 0 .... \
|
VCMLA 1111 110 rot:2 . 1 . .... .... 1000 . q:1 . 0 .... \
|
||||||
vm=%vm_dp vn=%vn_dp vd=%vd_dp
|
vm=%vm_dp vn=%vn_dp vd=%vd_dp size=%vcadd_size
|
||||||
|
|
||||||
|
VCADD 1111 110 rot:1 1 . 0 . .... .... 1000 . q:1 . 0 .... \
|
||||||
|
vm=%vm_dp vn=%vn_dp vd=%vd_dp size=%vcadd_size
|
||||||
|
|
||||||
# VUDOT and VSDOT
|
# VUDOT and VSDOT
|
||||||
VDOT 1111 110 00 . 10 .... .... 1101 . q:1 . u:1 .... \
|
VDOT 1111 110 00 . 10 .... .... 1101 . q:1 . u:1 .... \
|
||||||
|
@ -51,9 +57,9 @@ VFML 1111 110 0 s:1 . 10 .... .... 1000 . 1 . 1 .... \
|
||||||
vm=%vm_dp vn=%vn_dp vd=%vd_dp q=1
|
vm=%vm_dp vn=%vn_dp vd=%vd_dp q=1
|
||||||
|
|
||||||
VCMLA_scalar 1111 1110 0 . rot:2 .... .... 1000 . q:1 index:1 0 vm:4 \
|
VCMLA_scalar 1111 1110 0 . rot:2 .... .... 1000 . q:1 index:1 0 vm:4 \
|
||||||
vn=%vn_dp vd=%vd_dp size=0
|
vn=%vn_dp vd=%vd_dp size=1
|
||||||
VCMLA_scalar 1111 1110 1 . rot:2 .... .... 1000 . q:1 . 0 .... \
|
VCMLA_scalar 1111 1110 1 . rot:2 .... .... 1000 . q:1 . 0 .... \
|
||||||
vm=%vm_dp vn=%vn_dp vd=%vd_dp size=1 index=0
|
vm=%vm_dp vn=%vn_dp vd=%vd_dp size=2 index=0
|
||||||
|
|
||||||
VDOT_scalar 1111 1110 0 . 10 .... .... 1101 . q:1 index:1 u:1 rm:4 \
|
VDOT_scalar 1111 1110 0 . 10 .... .... 1101 . q:1 index:1 u:1 rm:4 \
|
||||||
vm=%vm_dp vn=%vn_dp vd=%vd_dp
|
vm=%vm_dp vn=%vn_dp vd=%vd_dp
|
||||||
|
|
|
@ -49,6 +49,12 @@ static inline int rsub_8(DisasContext *s, int x)
|
||||||
return 8 - x;
|
return 8 - x;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline int neon_3same_fp_size(DisasContext *s, int x)
|
||||||
|
{
|
||||||
|
/* Convert 0==fp32, 1==fp16 into a MO_* value */
|
||||||
|
return MO_32 - x;
|
||||||
|
}
|
||||||
|
|
||||||
/* Include the generated Neon decoder */
|
/* Include the generated Neon decoder */
|
||||||
#include "decode-neon-dp.c.inc"
|
#include "decode-neon-dp.c.inc"
|
||||||
#include "decode-neon-ls.c.inc"
|
#include "decode-neon-ls.c.inc"
|
||||||
|
@ -162,7 +168,7 @@ static bool trans_VCMLA(DisasContext *s, arg_VCMLA *a)
|
||||||
gen_helper_gvec_3_ptr *fn_gvec_ptr;
|
gen_helper_gvec_3_ptr *fn_gvec_ptr;
|
||||||
|
|
||||||
if (!dc_isar_feature(aa32_vcma, s)
|
if (!dc_isar_feature(aa32_vcma, s)
|
||||||
|| (!a->size && !dc_isar_feature(aa32_fp16_arith, s))) {
|
|| (a->size == MO_16 && !dc_isar_feature(aa32_fp16_arith, s))) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -181,8 +187,9 @@ static bool trans_VCMLA(DisasContext *s, arg_VCMLA *a)
|
||||||
}
|
}
|
||||||
|
|
||||||
opr_sz = (1 + a->q) * 8;
|
opr_sz = (1 + a->q) * 8;
|
||||||
fpst = fpstatus_ptr(a->size == 0 ? FPST_STD_F16 : FPST_STD);
|
fpst = fpstatus_ptr(a->size == MO_16 ? FPST_STD_F16 : FPST_STD);
|
||||||
fn_gvec_ptr = a->size ? gen_helper_gvec_fcmlas : gen_helper_gvec_fcmlah;
|
fn_gvec_ptr = (a->size == MO_16) ?
|
||||||
|
gen_helper_gvec_fcmlah : gen_helper_gvec_fcmlas;
|
||||||
tcg_gen_gvec_3_ptr(vfp_reg_offset(1, a->vd),
|
tcg_gen_gvec_3_ptr(vfp_reg_offset(1, a->vd),
|
||||||
vfp_reg_offset(1, a->vn),
|
vfp_reg_offset(1, a->vn),
|
||||||
vfp_reg_offset(1, a->vm),
|
vfp_reg_offset(1, a->vm),
|
||||||
|
@ -199,7 +206,7 @@ static bool trans_VCADD(DisasContext *s, arg_VCADD *a)
|
||||||
gen_helper_gvec_3_ptr *fn_gvec_ptr;
|
gen_helper_gvec_3_ptr *fn_gvec_ptr;
|
||||||
|
|
||||||
if (!dc_isar_feature(aa32_vcma, s)
|
if (!dc_isar_feature(aa32_vcma, s)
|
||||||
|| (!a->size && !dc_isar_feature(aa32_fp16_arith, s))) {
|
|| (a->size == MO_16 && !dc_isar_feature(aa32_fp16_arith, s))) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -218,8 +225,9 @@ static bool trans_VCADD(DisasContext *s, arg_VCADD *a)
|
||||||
}
|
}
|
||||||
|
|
||||||
opr_sz = (1 + a->q) * 8;
|
opr_sz = (1 + a->q) * 8;
|
||||||
fpst = fpstatus_ptr(a->size == 0 ? FPST_STD_F16 : FPST_STD);
|
fpst = fpstatus_ptr(a->size == MO_16 ? FPST_STD_F16 : FPST_STD);
|
||||||
fn_gvec_ptr = a->size ? gen_helper_gvec_fcadds : gen_helper_gvec_fcaddh;
|
fn_gvec_ptr = (a->size == MO_16) ?
|
||||||
|
gen_helper_gvec_fcaddh : gen_helper_gvec_fcadds;
|
||||||
tcg_gen_gvec_3_ptr(vfp_reg_offset(1, a->vd),
|
tcg_gen_gvec_3_ptr(vfp_reg_offset(1, a->vd),
|
||||||
vfp_reg_offset(1, a->vn),
|
vfp_reg_offset(1, a->vn),
|
||||||
vfp_reg_offset(1, a->vm),
|
vfp_reg_offset(1, a->vm),
|
||||||
|
@ -301,7 +309,7 @@ static bool trans_VCMLA_scalar(DisasContext *s, arg_VCMLA_scalar *a)
|
||||||
if (!dc_isar_feature(aa32_vcma, s)) {
|
if (!dc_isar_feature(aa32_vcma, s)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (a->size == 0 && !dc_isar_feature(aa32_fp16_arith, s)) {
|
if (a->size == MO_16 && !dc_isar_feature(aa32_fp16_arith, s)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -319,10 +327,10 @@ static bool trans_VCMLA_scalar(DisasContext *s, arg_VCMLA_scalar *a)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn_gvec_ptr = (a->size ? gen_helper_gvec_fcmlas_idx
|
fn_gvec_ptr = (a->size == MO_16) ?
|
||||||
: gen_helper_gvec_fcmlah_idx);
|
gen_helper_gvec_fcmlah_idx : gen_helper_gvec_fcmlas_idx;
|
||||||
opr_sz = (1 + a->q) * 8;
|
opr_sz = (1 + a->q) * 8;
|
||||||
fpst = fpstatus_ptr(a->size == 0 ? FPST_STD_F16 : FPST_STD);
|
fpst = fpstatus_ptr(a->size == MO_16 ? FPST_STD_F16 : FPST_STD);
|
||||||
tcg_gen_gvec_3_ptr(vfp_reg_offset(1, a->vd),
|
tcg_gen_gvec_3_ptr(vfp_reg_offset(1, a->vd),
|
||||||
vfp_reg_offset(1, a->vn),
|
vfp_reg_offset(1, a->vn),
|
||||||
vfp_reg_offset(1, a->vm),
|
vfp_reg_offset(1, a->vm),
|
||||||
|
@ -1049,7 +1057,7 @@ DO_3SAME_VQDMULH(VQRDMULH, qrdmulh)
|
||||||
WRAP_FP_GVEC(gen_##INSN##_fp16_3s, FPST_STD_F16, HFUNC) \
|
WRAP_FP_GVEC(gen_##INSN##_fp16_3s, FPST_STD_F16, HFUNC) \
|
||||||
static bool trans_##INSN##_fp_3s(DisasContext *s, arg_3same *a) \
|
static bool trans_##INSN##_fp_3s(DisasContext *s, arg_3same *a) \
|
||||||
{ \
|
{ \
|
||||||
if (a->size != 0) { \
|
if (a->size == MO_16) { \
|
||||||
if (!dc_isar_feature(aa32_fp16_arith, s)) { \
|
if (!dc_isar_feature(aa32_fp16_arith, s)) { \
|
||||||
return false; \
|
return false; \
|
||||||
} \
|
} \
|
||||||
|
@ -1088,7 +1096,7 @@ static bool trans_VMAXNM_fp_3s(DisasContext *s, arg_3same *a)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (a->size != 0) {
|
if (a->size == MO_16) {
|
||||||
if (!dc_isar_feature(aa32_fp16_arith, s)) {
|
if (!dc_isar_feature(aa32_fp16_arith, s)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -1103,7 +1111,7 @@ static bool trans_VMINNM_fp_3s(DisasContext *s, arg_3same *a)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (a->size != 0) {
|
if (a->size == MO_16) {
|
||||||
if (!dc_isar_feature(aa32_fp16_arith, s)) {
|
if (!dc_isar_feature(aa32_fp16_arith, s)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -1135,7 +1143,7 @@ static bool do_3same_fp_pair(DisasContext *s, arg_3same *a,
|
||||||
assert(a->q == 0); /* enforced by decode patterns */
|
assert(a->q == 0); /* enforced by decode patterns */
|
||||||
|
|
||||||
|
|
||||||
fpstatus = fpstatus_ptr(a->size != 0 ? FPST_STD_F16 : FPST_STD);
|
fpstatus = fpstatus_ptr(a->size == MO_16 ? FPST_STD_F16 : FPST_STD);
|
||||||
tcg_gen_gvec_3_ptr(vfp_reg_offset(1, a->vd),
|
tcg_gen_gvec_3_ptr(vfp_reg_offset(1, a->vd),
|
||||||
vfp_reg_offset(1, a->vn),
|
vfp_reg_offset(1, a->vn),
|
||||||
vfp_reg_offset(1, a->vm),
|
vfp_reg_offset(1, a->vm),
|
||||||
|
@ -1152,7 +1160,7 @@ static bool do_3same_fp_pair(DisasContext *s, arg_3same *a,
|
||||||
#define DO_3S_FP_PAIR(INSN,FUNC) \
|
#define DO_3S_FP_PAIR(INSN,FUNC) \
|
||||||
static bool trans_##INSN##_fp_3s(DisasContext *s, arg_3same *a) \
|
static bool trans_##INSN##_fp_3s(DisasContext *s, arg_3same *a) \
|
||||||
{ \
|
{ \
|
||||||
if (a->size != 0) { \
|
if (a->size == MO_16) { \
|
||||||
if (!dc_isar_feature(aa32_fp16_arith, s)) { \
|
if (!dc_isar_feature(aa32_fp16_arith, s)) { \
|
||||||
return false; \
|
return false; \
|
||||||
} \
|
} \
|
||||||
|
@ -1620,7 +1628,7 @@ static bool do_fp_2sh(DisasContext *s, arg_2reg_shift *a,
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (a->size != 0) {
|
if (a->size == MO_16) {
|
||||||
if (!dc_isar_feature(aa32_fp16_arith, s)) {
|
if (!dc_isar_feature(aa32_fp16_arith, s)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -1640,7 +1648,7 @@ static bool do_fp_2sh(DisasContext *s, arg_2reg_shift *a,
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
fpst = fpstatus_ptr(a->size ? FPST_STD_F16 : FPST_STD);
|
fpst = fpstatus_ptr(a->size == MO_16 ? FPST_STD_F16 : FPST_STD);
|
||||||
tcg_gen_gvec_2_ptr(rd_ofs, rm_ofs, fpst, vec_size, vec_size, a->shift, fn);
|
tcg_gen_gvec_2_ptr(rd_ofs, rm_ofs, fpst, vec_size, vec_size, a->shift, fn);
|
||||||
tcg_temp_free_ptr(fpst);
|
tcg_temp_free_ptr(fpst);
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -568,6 +568,89 @@ class BootLinuxConsole(LinuxKernelTest):
|
||||||
'sda')
|
'sda')
|
||||||
# cubieboard's reboot is not functioning; omit reboot test.
|
# cubieboard's reboot is not functioning; omit reboot test.
|
||||||
|
|
||||||
|
def test_arm_quanta_gsj(self):
|
||||||
|
"""
|
||||||
|
:avocado: tags=arch:arm
|
||||||
|
:avocado: tags=machine:quanta-gsj
|
||||||
|
"""
|
||||||
|
# 25 MiB compressed, 32 MiB uncompressed.
|
||||||
|
image_url = (
|
||||||
|
'https://github.com/hskinnemoen/openbmc/releases/download/'
|
||||||
|
'20200711-gsj-qemu-0/obmc-phosphor-image-gsj.static.mtd.gz')
|
||||||
|
image_hash = '14895e634923345cb5c8776037ff7876df96f6b1'
|
||||||
|
image_path_gz = self.fetch_asset(image_url, asset_hash=image_hash)
|
||||||
|
image_name = 'obmc.mtd'
|
||||||
|
image_path = os.path.join(self.workdir, image_name)
|
||||||
|
archive.gzip_uncompress(image_path_gz, image_path)
|
||||||
|
|
||||||
|
self.vm.set_console()
|
||||||
|
drive_args = 'file=' + image_path + ',if=mtd,bus=0,unit=0'
|
||||||
|
self.vm.add_args('-drive', drive_args)
|
||||||
|
self.vm.launch()
|
||||||
|
|
||||||
|
# Disable drivers and services that stall for a long time during boot,
|
||||||
|
# to avoid running past the 90-second timeout. These may be removed
|
||||||
|
# as the corresponding device support is added.
|
||||||
|
kernel_command_line = self.KERNEL_COMMON_COMMAND_LINE + (
|
||||||
|
'console=${console} '
|
||||||
|
'mem=${mem} '
|
||||||
|
'initcall_blacklist=npcm_i2c_bus_driver_init '
|
||||||
|
'systemd.mask=systemd-random-seed.service '
|
||||||
|
'systemd.mask=dropbearkey.service '
|
||||||
|
)
|
||||||
|
|
||||||
|
self.wait_for_console_pattern('> BootBlock by Nuvoton')
|
||||||
|
self.wait_for_console_pattern('>Device: Poleg BMC NPCM730')
|
||||||
|
self.wait_for_console_pattern('>Skip DDR init.')
|
||||||
|
self.wait_for_console_pattern('U-Boot ')
|
||||||
|
interrupt_interactive_console_until_pattern(
|
||||||
|
self, 'Hit any key to stop autoboot:', 'U-Boot>')
|
||||||
|
exec_command_and_wait_for_pattern(
|
||||||
|
self, "setenv bootargs ${bootargs} " + kernel_command_line,
|
||||||
|
'U-Boot>')
|
||||||
|
exec_command_and_wait_for_pattern(
|
||||||
|
self, 'run romboot', 'Booting Kernel from flash')
|
||||||
|
self.wait_for_console_pattern('Booting Linux on physical CPU 0x0')
|
||||||
|
self.wait_for_console_pattern('CPU1: thread -1, cpu 1, socket 0')
|
||||||
|
self.wait_for_console_pattern('OpenBMC Project Reference Distro')
|
||||||
|
self.wait_for_console_pattern('gsj login:')
|
||||||
|
|
||||||
|
def test_arm_quanta_gsj_initrd(self):
|
||||||
|
"""
|
||||||
|
:avocado: tags=arch:arm
|
||||||
|
:avocado: tags=machine:quanta-gsj
|
||||||
|
"""
|
||||||
|
initrd_url = (
|
||||||
|
'https://github.com/hskinnemoen/openbmc/releases/download/'
|
||||||
|
'20200711-gsj-qemu-0/obmc-phosphor-initramfs-gsj.cpio.xz')
|
||||||
|
initrd_hash = '98fefe5d7e56727b1eb17d5c00311b1b5c945300'
|
||||||
|
initrd_path = self.fetch_asset(initrd_url, asset_hash=initrd_hash)
|
||||||
|
kernel_url = (
|
||||||
|
'https://github.com/hskinnemoen/openbmc/releases/download/'
|
||||||
|
'20200711-gsj-qemu-0/uImage-gsj.bin')
|
||||||
|
kernel_hash = 'fa67b2f141d56d39b3c54305c0e8a899c99eb2c7'
|
||||||
|
kernel_path = self.fetch_asset(kernel_url, asset_hash=kernel_hash)
|
||||||
|
dtb_url = (
|
||||||
|
'https://github.com/hskinnemoen/openbmc/releases/download/'
|
||||||
|
'20200711-gsj-qemu-0/nuvoton-npcm730-gsj.dtb')
|
||||||
|
dtb_hash = '18315f7006d7b688d8312d5c727eecd819aa36a4'
|
||||||
|
dtb_path = self.fetch_asset(dtb_url, asset_hash=dtb_hash)
|
||||||
|
|
||||||
|
self.vm.set_console()
|
||||||
|
kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE +
|
||||||
|
'console=ttyS0,115200n8 '
|
||||||
|
'earlycon=uart8250,mmio32,0xf0001000')
|
||||||
|
self.vm.add_args('-kernel', kernel_path,
|
||||||
|
'-initrd', initrd_path,
|
||||||
|
'-dtb', dtb_path,
|
||||||
|
'-append', kernel_command_line)
|
||||||
|
self.vm.launch()
|
||||||
|
|
||||||
|
self.wait_for_console_pattern('Booting Linux on physical CPU 0x0')
|
||||||
|
self.wait_for_console_pattern('CPU1: thread -1, cpu 1, socket 0')
|
||||||
|
self.wait_for_console_pattern(
|
||||||
|
'Give root password for system maintenance')
|
||||||
|
|
||||||
def test_arm_orangepi(self):
|
def test_arm_orangepi(self):
|
||||||
"""
|
"""
|
||||||
:avocado: tags=arch:arm
|
:avocado: tags=arch:arm
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
%1f 0:8
|
||||||
|
%2f 8:8
|
||||||
|
%3f 16:8
|
||||||
|
|
||||||
|
&3arg a b c
|
||||||
|
@3arg ........ ........ ........ ........ &3arg a=%1f b=%2f c=%3f
|
||||||
|
3insn 00000000 ........ ........ ........ @3arg
|
Loading…
Reference in New Issue