aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPeter Maydell <peter.maydell@linaro.org>2020-09-14 16:03:08 +0100
committerPeter Maydell <peter.maydell@linaro.org>2020-09-14 16:03:08 +0100
commit2d2c73d0e3d504a61f868e46e6abd5643f38091b (patch)
treec4d2919e4a72d08810d3ebcbc3b487eb73165844
parenta68694cd1f3e5448cca814ff39b871f9ebd71ed5 (diff)
parent4fe986dd4480308ecf07200cfbd3c3d494a0f639 (diff)
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>
-rw-r--r--.gitmodules3
-rw-r--r--MAINTAINERS10
-rwxr-xr-xconfigure2
-rw-r--r--default-configs/arm-softmmu.mak1
-rw-r--r--docs/system/arm/mps2.rst20
-rw-r--r--docs/system/arm/nuvoton.rst92
-rw-r--r--docs/system/deprecated.rst32
-rw-r--r--docs/system/target-arm.rst1
-rw-r--r--hw/arm/Kconfig9
-rw-r--r--hw/arm/meson.build1
-rw-r--r--hw/arm/mps2.c97
-rw-r--r--hw/arm/npcm7xx.c532
-rw-r--r--hw/arm/npcm7xx_boards.c197
-rw-r--r--hw/arm/xlnx-versal-virt.c2
-rw-r--r--hw/mem/meson.build1
-rw-r--r--hw/mem/npcm7xx_mc.c84
-rw-r--r--hw/misc/a9scu.c59
-rw-r--r--hw/misc/meson.build4
-rw-r--r--hw/misc/npcm7xx_clk.c266
-rw-r--r--hw/misc/npcm7xx_gcr.c269
-rw-r--r--hw/misc/trace-events8
-rw-r--r--hw/nvram/meson.build1
-rw-r--r--hw/nvram/npcm7xx_otp.c440
-rw-r--r--hw/ssi/meson.build1
-rw-r--r--hw/ssi/npcm7xx_fiu.c572
-rw-r--r--hw/ssi/trace-events11
-rw-r--r--hw/timer/armv7m_systick.c8
-rw-r--r--hw/timer/meson.build1
-rw-r--r--hw/timer/npcm7xx_timer.c543
-rw-r--r--hw/timer/trace-events5
-rw-r--r--include/hw/arm/npcm7xx.h112
-rw-r--r--include/hw/mem/npcm7xx_mc.h36
-rw-r--r--include/hw/misc/npcm7xx_clk.h48
-rw-r--r--include/hw/misc/npcm7xx_gcr.h43
-rw-r--r--include/hw/nvram/npcm7xx_otp.h79
-rw-r--r--include/hw/ssi/npcm7xx_fiu.h73
-rw-r--r--include/hw/timer/npcm7xx_timer.h78
-rw-r--r--pc-bios/README6
-rw-r--r--pc-bios/meson.build1
-rw-r--r--pc-bios/npcm7xx_bootrom.binbin0 -> 768 bytes
-rw-r--r--roms/Makefile7
m---------roms/vbootrom0
-rw-r--r--scripts/decodetree.py46
-rw-r--r--target/arm/cpu.c113
-rw-r--r--target/arm/helper.c2
-rw-r--r--target/arm/kvm-consts.h7
-rw-r--r--target/arm/kvm.c7
-rw-r--r--target/arm/kvm32.c595
-rw-r--r--target/arm/kvm_arm.h6
-rw-r--r--target/arm/meson.build5
-rw-r--r--target/arm/neon-dp.decode18
-rw-r--r--target/arm/neon-shared.decode18
-rw-r--r--target/arm/translate-neon.c.inc42
-rw-r--r--tests/acceptance/boot_linux_console.py83
-rw-r--r--tests/decode/succ_ident1.decode7
55 files changed, 3915 insertions, 789 deletions
diff --git a/.gitmodules b/.gitmodules
index ce979398a8..9ffb9f3f4f 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -61,3 +61,6 @@
[submodule "meson"]
path = meson
url = https://github.com/mesonbuild/meson/
+[submodule "roms/vbootrom"]
+ path = roms/vbootrom
+ url = https://github.com/google/vbootrom.git
diff --git a/MAINTAINERS b/MAINTAINERS
index d817ee6c6f..3d17cad19a 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -750,6 +750,16 @@ S: Odd Fixes
F: hw/arm/musicpal.c
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
M: Andrzej Zaborowski <balrogg@gmail.com>
M: Peter Maydell <peter.maydell@linaro.org>
diff --git a/configure b/configure
index 2b6a1196da..ce27eafb0a 100755
--- a/configure
+++ b/configure
@@ -234,7 +234,7 @@ supported_kvm_target() {
test "$kvm" = "yes" || return 1
glob "$1" "*-softmmu" || return 1
case "${1%-softmmu}:$cpu" in
- arm:arm | aarch64:aarch64 | \
+ aarch64:aarch64 | \
i386:i386 | i386:x86_64 | i386:x32 | \
x86_64:i386 | x86_64:x86_64 | x86_64:x32 | \
mips:mips | mipsel:mips | mips64:mips | mips64el:mips | \
diff --git a/default-configs/arm-softmmu.mak b/default-configs/arm-softmmu.mak
index 8fc09a4a51..9a94ebd0be 100644
--- a/default-configs/arm-softmmu.mak
+++ b/default-configs/arm-softmmu.mak
@@ -27,6 +27,7 @@ CONFIG_GUMSTIX=y
CONFIG_SPITZ=y
CONFIG_TOSA=y
CONFIG_Z2=y
+CONFIG_NPCM7XX=y
CONFIG_COLLIE=y
CONFIG_ASPEED_SOC=y
CONFIG_NETDUINO2=y
diff --git a/docs/system/arm/mps2.rst b/docs/system/arm/mps2.rst
index 3a98cb59b0..8c5b5f1fe0 100644
--- a/docs/system/arm/mps2.rst
+++ b/docs/system/arm/mps2.rst
@@ -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.
@@ -11,17 +11,21 @@ as seen by the guest depend significantly on the FPGA image.
QEMU models the following FPGA images:
``mps2-an385``
- Cortex-M3 as documented in ARM Application Note AN385
-``mps2-an511``
- Cortex-M3 'DesignStart' as documented in AN511
+ 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-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``
- 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:
-- 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
if zbt_boot_ctrl is always zero)
- QEMU provides a LAN9118 ethernet rather than LAN9220; the only guest
diff --git a/docs/system/arm/nuvoton.rst b/docs/system/arm/nuvoton.rst
new file mode 100644
index 0000000000..e3e1a3a3a7
--- /dev/null
+++ b/docs/system/arm/nuvoton.rst
@@ -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``.
diff --git a/docs/system/deprecated.rst b/docs/system/deprecated.rst
index a158e765c3..0cb8b01424 100644
--- a/docs/system/deprecated.rst
+++ b/docs/system/deprecated.rst
@@ -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 ``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
-----------------------
@@ -416,6 +408,22 @@ The above, converted to the current supported format::
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)
'''''''''''''''''''''''''''''
@@ -543,6 +551,14 @@ should be used instead of the 1.09.1 version.
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)
'''''''''''''''''''''''''''''''''''''''''
diff --git a/docs/system/target-arm.rst b/docs/system/target-arm.rst
index afdb37e738..fdcf25c237 100644
--- a/docs/system/target-arm.rst
+++ b/docs/system/target-arm.rst
@@ -86,6 +86,7 @@ undocumented; you can get a complete list by running
arm/musicpal
arm/gumstix
arm/nseries
+ arm/nuvoton
arm/orangepi
arm/palm
arm/xscale
diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig
index bc3a423940..f303c6bead 100644
--- a/hw/arm/Kconfig
+++ b/hw/arm/Kconfig
@@ -355,6 +355,15 @@ config XLNX_VERSAL
select VIRTIO_MMIO
select UNIMP
+config NPCM7XX
+ bool
+ select A9MPCORE
+ select ARM_GIC
+ select PL310 # cache controller
+ select SERIAL
+ select SSI
+ select UNIMP
+
config FSL_IMX25
bool
select IMX
diff --git a/hw/arm/meson.build b/hw/arm/meson.build
index 8480b7f37d..be39117b9b 100644
--- a/hw/arm/meson.build
+++ b/hw/arm/meson.build
@@ -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_NETDUINO2', if_true: files('netduino2.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_SX1', if_true: files('omap_sx1.c'))
arm_ss.add(when: 'CONFIG_CHEETAH', if_true: files('palm.c'))
diff --git a/hw/arm/mps2.c b/hw/arm/mps2.c
index 4ee5c38459..5d47160850 100644
--- a/hw/arm/mps2.c
+++ b/hw/arm/mps2.c
@@ -15,6 +15,8 @@
* as seen by the guest depend significantly on the FPGA image.
* We model the following FPGA images:
* "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
*
* Links to the TRM for the board itself and to the various Application
@@ -48,6 +50,8 @@
typedef enum MPS2FPGAType {
FPGA_AN385,
+ FPGA_AN386,
+ FPGA_AN500,
FPGA_AN511,
} MPS2FPGAType;
@@ -55,6 +59,9 @@ struct MPS2MachineClass {
MachineClass parent;
MPS2FPGAType fpga_type;
uint32_t scc_id;
+ bool has_block_ram;
+ hwaddr ethernet_base;
+ hwaddr psram_base;
};
typedef struct MPS2MachineClass MPS2MachineClass;
@@ -82,6 +89,8 @@ typedef struct MPS2MachineState MPS2MachineState;
#define TYPE_MPS2_MACHINE "mps2"
#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")
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
* call the 16MB our "system memory", as it's the largest lump.
*
- * Common to both boards:
- * 0x21000000..0x21ffffff : PSRAM (16MB)
- * AN385 only:
+ * AN385/AN386/AN511:
+ * 0x21000000 .. 0x21ffffff : PSRAM (16MB)
+ * AN385/AN386/AN500:
* 0x00000000 .. 0x003fffff : ZBT SSRAM1
* 0x00400000 .. 0x007fffff : mirror of ZBT SSRAM1
* 0x20000000 .. 0x203fffff : ZBT SSRAM 2&3
* 0x20400000 .. 0x207fffff : mirror of ZBT SSRAM 2&3
+ * AN385/AN386 only:
* 0x01000000 .. 0x01003fff : block RAM (16K)
* 0x01004000 .. 0x01007fff : mirror of above
* 0x01008000 .. 0x0100bfff : mirror of above
@@ -155,21 +165,17 @@ static void mps2_common_init(MachineState *machine)
* 0x00400000 .. 0x007fffff : ZBT SSRAM1
* 0x20000000 .. 0x2001ffff : SRAM
* 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.
* This is of no use for QEMU so we don't implement it (as if
* 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) {
- 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);
+ if (mmc->has_block_ram) {
make_ram(&mms->blockram, "mps.blockram", 0x01000000, 0x4000);
make_ram_alias(&mms->blockram_m1, "mps.blockram_m1",
&mms->blockram, 0x01004000);
@@ -177,6 +183,17 @@ static void mps2_common_init(MachineState *machine)
&mms->blockram, 0x01008000);
make_ram_alias(&mms->blockram_m3, "mps.blockram_m3",
&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;
case FPGA_AN511:
make_ram(&mms->blockram, "mps.blockram", 0x0, 0x40000);
@@ -192,6 +209,8 @@ static void mps2_common_init(MachineState *machine)
armv7m = DEVICE(&mms->armv7m);
switch (mmc->fpga_type) {
case FPGA_AN385:
+ case FPGA_AN386:
+ case FPGA_AN500:
qdev_prop_set_uint32(armv7m, "num-irq", 32);
break;
case FPGA_AN511:
@@ -228,6 +247,8 @@ static void mps2_common_init(MachineState *machine)
switch (mmc->fpga_type) {
case FPGA_AN385:
+ case FPGA_AN386:
+ case FPGA_AN500:
{
/* The overflow IRQs for UARTs 0, 1 and 2 are ORed together.
* 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
* 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,
- mmc->fpga_type == FPGA_AN385 ? 13 : 47));
+ mmc->fpga_type == FPGA_AN511 ? 47 : 13));
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;
mc->default_cpu_type = ARM_CPU_TYPE_NAME("cortex-m3");
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)
@@ -417,6 +469,9 @@ static void mps2_an511_class_init(ObjectClass *oc, void *data)
mmc->fpga_type = FPGA_AN511;
mc->default_cpu_type = ARM_CPU_TYPE_NAME("cortex-m3");
mmc->scc_id = 0x41045110;
+ mmc->psram_base = 0x21000000;
+ mmc->ethernet_base = 0x40200000;
+ mmc->has_block_ram = false;
}
static const TypeInfo mps2_info = {
@@ -434,6 +489,18 @@ static const TypeInfo mps2_an385_info = {
.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 = {
.name = TYPE_MPS2_AN511_MACHINE,
.parent = TYPE_MPS2_MACHINE,
@@ -444,6 +511,8 @@ static void mps2_machine_init(void)
{
type_register_static(&mps2_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);
}
diff --git a/hw/arm/npcm7xx.c b/hw/arm/npcm7xx.c
new file mode 100644
index 0000000000..037f3a26f2
--- /dev/null
+++ b/hw/arm/npcm7xx.c
@@ -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);
diff --git a/hw/arm/npcm7xx_boards.c b/hw/arm/npcm7xx_boards.c
new file mode 100644
index 0000000000..79e2e2744c
--- /dev/null
+++ b/hw/arm/npcm7xx_boards.c
@@ -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)
diff --git a/hw/arm/xlnx-versal-virt.c b/hw/arm/xlnx-versal-virt.c
index 5a01e856fd..1f9409eb32 100644
--- a/hw/arm/xlnx-versal-virt.c
+++ b/hw/arm/xlnx-versal-virt.c
@@ -214,7 +214,7 @@ static void fdt_add_gem_nodes(VersalVirt *s)
s->phandle.ethernet_phy[i]);
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_125Mhz, s->phandle.clk_125Mhz);
qemu_fdt_setprop(s->fdt, name, "clock-names",
clocknames, sizeof(clocknames));
qemu_fdt_setprop_cells(s->fdt, name, "interrupts",
diff --git a/hw/mem/meson.build b/hw/mem/meson.build
index ba424622bb..0d22f2b572 100644
--- a/hw/mem/meson.build
+++ b/hw/mem/meson.build
@@ -1,6 +1,7 @@
mem_ss = ss.source_set()
mem_ss.add(files('memory-device.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'))
softmmu_ss.add_all(when: 'CONFIG_MEM_DEVICE', if_true: mem_ss)
diff --git a/hw/mem/npcm7xx_mc.c b/hw/mem/npcm7xx_mc.c
new file mode 100644
index 0000000000..0435d06ab4
--- /dev/null
+++ b/hw/mem/npcm7xx_mc.c
@@ -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);
diff --git a/hw/misc/a9scu.c b/hw/misc/a9scu.c
index 324371a1c0..a375ebc987 100644
--- a/hw/misc/a9scu.c
+++ b/hw/misc/a9scu.c
@@ -12,8 +12,12 @@
#include "hw/misc/a9scu.h"
#include "hw/qdev-properties.h"
#include "migration/vmstate.h"
+#include "qapi/error.h"
+#include "qemu/log.h"
#include "qemu/module.h"
+#define A9_SCU_CPU_MAX 4
+
static uint64_t a9_scu_read(void *opaque, hwaddr offset,
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);
case 0x08: /* CPU Power 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 */
return 0;
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 */
/* unimplemented, fall through */
default:
+ qemu_log_mask(LOG_UNIMP, "%s: Unsupported offset 0x%"HWADDR_PRIx"\n",
+ __func__, offset);
return 0;
}
}
@@ -49,23 +49,6 @@ static void a9_scu_write(void *opaque, hwaddr offset,
uint64_t value, unsigned size)
{
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) {
case 0x00: /* Control */
@@ -74,9 +57,7 @@ static void a9_scu_write(void *opaque, hwaddr offset,
case 0x4: /* Configuration: RO */
break;
case 0x08: case 0x09: case 0x0A: case 0x0B: /* Power Control */
- shift = (offset - 0x8) * 8;
- s->status &= ~(mask << shift);
- s->status |= ((value & mask) << shift);
+ s->status = value;
break;
case 0x0c: /* Invalidate All Registers In Secure State */
/* 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 */
/* unimplemented, fall through */
default:
+ qemu_log_mask(LOG_UNIMP, "%s: Unsupported offset 0x%"HWADDR_PRIx
+ " value 0x%"PRIx64"\n",
+ __func__, offset, value);
break;
}
}
@@ -96,6 +80,14 @@ static void a9_scu_write(void *opaque, hwaddr offset,
static const MemoryRegionOps a9_scu_ops = {
.read = a9_scu_read,
.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,
};
@@ -105,12 +97,17 @@ static void a9_scu_reset(DeviceState *dev)
s->control = 0;
}
-static void a9_scu_init(Object *obj)
+static void a9_scu_realize(DeviceState *dev, Error **errp)
{
- A9SCUState *s = A9_SCU(obj);
- SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
+ A9SCUState *s = A9_SCU(dev);
+ SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
+
+ 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, obj, &a9_scu_ops, s,
+ memory_region_init_io(&s->iomem, OBJECT(s), &a9_scu_ops, s,
"a9-scu", 0x100);
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);
dc->vmsd = &vmstate_a9_scu;
dc->reset = a9_scu_reset;
+ dc->realize = a9_scu_realize;
}
static const TypeInfo a9_scu_info = {
.name = TYPE_A9_SCU,
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(A9SCUState),
- .instance_init = a9_scu_init,
.class_init = a9_scu_class_init,
};
diff --git a/hw/misc/meson.build b/hw/misc/meson.build
index bd24132757..793d45b1dc 100644
--- a/hw/misc/meson.build
+++ b/hw/misc/meson.build
@@ -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_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(
'omap_clk.c',
'omap_gpmc.c',
diff --git a/hw/misc/npcm7xx_clk.c b/hw/misc/npcm7xx_clk.c
new file mode 100644
index 0000000000..21ab4200d1
--- /dev/null
+++ b/hw/misc/npcm7xx_clk.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);
diff --git a/hw/misc/npcm7xx_gcr.c b/hw/misc/npcm7xx_gcr.c
new file mode 100644
index 0000000000..745f690809
--- /dev/null
+++ b/hw/misc/npcm7xx_gcr.c
@@ -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);
diff --git a/hw/misc/trace-events b/hw/misc/trace-events
index 56a622d1e9..6054f9adf3 100644
--- a/hw/misc/trace-events
+++ b/hw/misc/trace-events
@@ -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_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_set_irq(int gpio, int line, int level) "Interupt: GPIO: %d, Line: %d; Level: %d"
stm32f4xx_pulse_exti(int irq) "Pulse EXTI: %d"
diff --git a/hw/nvram/meson.build b/hw/nvram/meson.build
index ba214558ac..1f2ed013b2 100644
--- a/hw/nvram/meson.build
+++ b/hw/nvram/meson.build
@@ -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_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_NPCM7XX', if_true: files('npcm7xx_otp.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'))
diff --git a/hw/nvram/npcm7xx_otp.c b/hw/nvram/npcm7xx_otp.c
new file mode 100644
index 0000000000..b16ca530ba
--- /dev/null
+++ b/hw/nvram/npcm7xx_otp.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);
diff --git a/hw/ssi/meson.build b/hw/ssi/meson.build
index f1f5c287d0..dee00c0da6 100644
--- a/hw/ssi/meson.build
+++ b/hw/ssi/meson.build
@@ -1,5 +1,6 @@
softmmu_ss.add(when: 'CONFIG_ASPEED_SOC', if_true: files('aspeed_smc.c'))
softmmu_ss.add(when: 'CONFIG_MSF2', if_true: files('mss-spi.c'))
+softmmu_ss.add(when: 'CONFIG_NPCM7XX', if_true: files('npcm7xx_fiu.c'))
softmmu_ss.add(when: 'CONFIG_PL022', if_true: files('pl022.c'))
softmmu_ss.add(when: 'CONFIG_SSI', if_true: files('ssi.c'))
softmmu_ss.add(when: 'CONFIG_STM32F2XX_SPI', if_true: files('stm32f2xx_spi.c'))
diff --git a/hw/ssi/npcm7xx_fiu.c b/hw/ssi/npcm7xx_fiu.c
new file mode 100644
index 0000000000..104e8f2b96
--- /dev/null
+++ b/hw/ssi/npcm7xx_fiu.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);
diff --git a/hw/ssi/trace-events b/hw/ssi/trace-events
index 0ea498de91..2f83ef833f 100644
--- a/hw/ssi/trace-events
+++ b/hw/ssi/trace-events
@@ -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_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"
+
+# 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
diff --git a/hw/timer/armv7m_systick.c b/hw/timer/armv7m_systick.c
index 74c58bcf24..a8cec7eb56 100644
--- a/hw/timer/armv7m_systick.c
+++ b/hw/timer/armv7m_systick.c
@@ -202,6 +202,14 @@ static void systick_reset(DeviceState *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->reload = 0;
s->tick = 0;
diff --git a/hw/timer/meson.build b/hw/timer/meson.build
index 9f0a267c83..be343f68fe 100644
--- a/hw/timer/meson.build
+++ b/hw/timer/meson.build
@@ -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_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_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_OMAP', if_true: files('omap_gptimer.c'))
softmmu_ss.add(when: 'CONFIG_OMAP', if_true: files('omap_synctimer.c'))
diff --git a/hw/timer/npcm7xx_timer.c b/hw/timer/npcm7xx_timer.c
new file mode 100644
index 0000000000..5703e43d40
--- /dev/null
+++ b/hw/timer/npcm7xx_timer.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);
diff --git a/hw/timer/trace-events b/hw/timer/trace-events
index 1537c3e6ec..b996d99200 100644
--- a/hw/timer/trace-events
+++ b/hw/timer/trace-events
@@ -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_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_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"
diff --git a/include/hw/arm/npcm7xx.h b/include/hw/arm/npcm7xx.h
new file mode 100644
index 0000000000..13106af215
--- /dev/null
+++ b/include/hw/arm/npcm7xx.h
@@ -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 */
diff --git a/include/hw/mem/npcm7xx_mc.h b/include/hw/mem/npcm7xx_mc.h
new file mode 100644
index 0000000000..7ed38be243
--- /dev/null
+++ b/include/hw/mem/npcm7xx_mc.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 */
diff --git a/include/hw/misc/npcm7xx_clk.h b/include/hw/misc/npcm7xx_clk.h
new file mode 100644
index 0000000000..cdcc9e8534
--- /dev/null
+++ b/include/hw/misc/npcm7xx_clk.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 */
diff --git a/include/hw/misc/npcm7xx_gcr.h b/include/hw/misc/npcm7xx_gcr.h
new file mode 100644
index 0000000000..13109d9d32
--- /dev/null
+++ b/include/hw/misc/npcm7xx_gcr.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 */
diff --git a/include/hw/nvram/npcm7xx_otp.h b/include/hw/nvram/npcm7xx_otp.h
new file mode 100644
index 0000000000..156bbd151a
--- /dev/null
+++ b/include/hw/nvram/npcm7xx_otp.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 */
diff --git a/include/hw/ssi/npcm7xx_fiu.h b/include/hw/ssi/npcm7xx_fiu.h
new file mode 100644
index 0000000000..a3a1704289
--- /dev/null
+++ b/include/hw/ssi/npcm7xx_fiu.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 */
diff --git a/include/hw/timer/npcm7xx_timer.h b/include/hw/timer/npcm7xx_timer.h
new file mode 100644
index 0000000000..878a365a79
--- /dev/null
+++ b/include/hw/timer/npcm7xx_timer.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 */
diff --git a/pc-bios/README b/pc-bios/README
index 2e49be607e..33f9754ad3 100644
--- a/pc-bios/README
+++ b/pc-bios/README
@@ -71,3 +71,9 @@
("Simplified BSD License" or "FreeBSD License", SPDX: BSD-2-Clause). OpenSBI
source code also contains code reused from other projects desribed here:
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
diff --git a/pc-bios/meson.build b/pc-bios/meson.build
index 8087e5c0a7..182d5ebb35 100644
--- a/pc-bios/meson.build
+++ b/pc-bios/meson.build
@@ -81,6 +81,7 @@ blobs = files(
'opensbi-riscv64-generic-fw_dynamic.bin',
'opensbi-riscv32-generic-fw_dynamic.elf',
'opensbi-riscv64-generic-fw_dynamic.elf',
+ 'npcm7xx_bootrom.bin',
)
if install_blobs
diff --git a/pc-bios/npcm7xx_bootrom.bin b/pc-bios/npcm7xx_bootrom.bin
new file mode 100644
index 0000000000..38f89d1b97
--- /dev/null
+++ b/pc-bios/npcm7xx_bootrom.bin
Binary files differ
diff --git a/roms/Makefile b/roms/Makefile
index d3a3228359..3726f06fe7 100644
--- a/roms/Makefile
+++ b/roms/Makefile
@@ -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
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)
powerpc_cross_prefix := $(call find-cross-prefix,powerpc)
x86_64_cross_prefix := $(call find-cross-prefix,x86_64)
@@ -63,6 +64,7 @@ default help:
@echo " skiboot -- update skiboot.lid"
@echo " u-boot.e500 -- update u-boot.e500"
@echo " u-boot.sam460 -- update u-boot.sam460"
+ @echo " npcm7xx_bootrom -- update vbootrom for npcm7xx"
@echo " efi -- update UEFI (edk2) platform firmware"
@echo " opensbi32-generic -- update OpenSBI for 32-bit generic machine"
@echo " opensbi64-generic -- update OpenSBI for 64-bit generic machine"
@@ -185,6 +187,10 @@ bios-microvm:
$(MAKE) -C qboot
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:
rm -rf seabios/.config seabios/out seabios/builds
$(MAKE) -C sgabios clean
@@ -198,3 +204,4 @@ clean:
$(MAKE) -f Makefile.edk2 clean
$(MAKE) -C opensbi clean
$(MAKE) -C qboot clean
+ $(MAKE) -C vbootrom clean
diff --git a/roms/vbootrom b/roms/vbootrom
new file mode 160000
+Subproject 0c37a43527f0ee2b9584e7fb2fdc805e902635a
diff --git a/scripts/decodetree.py b/scripts/decodetree.py
index 4cd1e10904..c02de9865b 100644
--- a/scripts/decodetree.py
+++ b/scripts/decodetree.py
@@ -42,8 +42,14 @@ output_fd = None
insntype = 'uint32_t'
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):
"""Print an error message from file:line and args and exit."""
@@ -632,7 +638,6 @@ class ExcMultiPattern(MultiPattern):
def parse_field(lineno, name, toks):
"""Parse one instruction field from TOKS at LINENO"""
global fields
- global re_ident
global insnwidth
# A "simple" field will have only one entry;
@@ -641,7 +646,7 @@ def parse_field(lineno, name, toks):
width = 0
func = None
for t in toks:
- if re.fullmatch('!function=' + re_ident, t):
+ if re.match('^!function=', t):
if func:
error(lineno, 'duplicate function')
func = t.split('=')
@@ -695,7 +700,7 @@ def parse_field(lineno, name, toks):
def parse_arguments(lineno, name, toks):
"""Parse one argument set from TOKS at LINENO"""
global arguments
- global re_ident
+ global re_C_ident
global anyextern
flds = []
@@ -705,7 +710,7 @@ def parse_arguments(lineno, name, toks):
extern = True
anyextern = True
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))
if t in flds:
error(lineno, 'duplicate argument "{0}"'.format(t))
@@ -791,7 +796,10 @@ def parse_generic(lineno, parent_pat, name, toks):
global arguments
global formats
global allpatterns
- global re_ident
+ global re_arg_ident
+ global re_fld_ident
+ global re_fmt_ident
+ global re_C_ident
global insnwidth
global insnmask
global variablewidth
@@ -807,7 +815,7 @@ def parse_generic(lineno, parent_pat, name, toks):
fmt = None
for t in toks:
# '&Foo' gives a format an explcit argument set.
- if t[0] == '&':
+ if re.fullmatch(re_arg_ident, t):
tt = t[1:]
if arg:
error(lineno, 'multiple argument sets')
@@ -818,7 +826,7 @@ def parse_generic(lineno, parent_pat, name, toks):
continue
# '@Foo' gives a pattern an explicit format.
- if t[0] == '@':
+ if re.fullmatch(re_fmt_ident, t):
tt = t[1:]
if fmt:
error(lineno, 'multiple formats')
@@ -829,19 +837,19 @@ def parse_generic(lineno, parent_pat, name, toks):
continue
# '%Foo' imports a field.
- if t[0] == '%':
+ if re.fullmatch(re_fld_ident, t):
tt = t[1:]
flds = add_field_byname(lineno, flds, tt, tt)
continue
# '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('=%')
flds = add_field_byname(lineno, flds, fname, iname)
continue
# '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('=')
value = int(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
undefmask = (undefmask << shift) | ubm
# 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(':')
sign = False
if flen[0] == 's':
@@ -971,6 +979,10 @@ def parse_generic(lineno, parent_pat, name, toks):
def parse_file(f, parent_pat):
"""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
# ending in backslash; discard empty lines and comments.
@@ -1063,14 +1075,16 @@ def parse_file(f, parent_pat):
continue
# 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)
- elif name[0] == '&':
+ elif re.fullmatch(re_arg_ident, name):
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)
- else:
+ elif re.fullmatch(re_pat_ident, name):
parse_generic(start_lineno, parent_pat, name, toks)
+ else:
+ error(lineno, 'invalid token "{0}"'.format(name))
toks = []
if nesting != 0:
diff --git a/target/arm/cpu.c b/target/arm/cpu.c
index 6b4e708c08..7b5ea65fab 100644
--- a/target/arm/cpu.c
+++ b/target/arm/cpu.c
@@ -2098,72 +2098,69 @@ static void cortex_a15_initfn(Object *obj)
}
#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;
- * 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)
{
ARMCPU *cpu = ARM_CPU(obj);
- if (kvm_enabled()) {
- kvm_arm_set_cpu_features_from_host(cpu);
- } else {
- cortex_a15_initfn(obj);
+ cortex_a15_initfn(obj);
- /* old-style VFP short-vector support */
- cpu->isar.mvfr0 = FIELD_DP32(cpu->isar.mvfr0, MVFR0, FPSHVEC, 1);
+ /* old-style VFP short-vector support */
+ cpu->isar.mvfr0 = FIELD_DP32(cpu->isar.mvfr0, MVFR0, FPSHVEC, 1);
#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
- * advertise them.
- */
- set_feature(&cpu->env, ARM_FEATURE_V8);
- {
- uint32_t t;
-
- t = cpu->isar.id_isar5;
- t = FIELD_DP32(t, ID_ISAR5, AES, 2);
- t = FIELD_DP32(t, ID_ISAR5, SHA1, 1);
- t = FIELD_DP32(t, ID_ISAR5, SHA2, 1);
- t = FIELD_DP32(t, ID_ISAR5, CRC32, 1);
- t = FIELD_DP32(t, ID_ISAR5, RDM, 1);
- t = FIELD_DP32(t, ID_ISAR5, VCMA, 1);
- cpu->isar.id_isar5 = t;
-
- t = cpu->isar.id_isar6;
- t = FIELD_DP32(t, ID_ISAR6, JSCVT, 1);
- t = FIELD_DP32(t, ID_ISAR6, DP, 1);
- t = FIELD_DP32(t, ID_ISAR6, FHM, 1);
- t = FIELD_DP32(t, ID_ISAR6, SB, 1);
- t = FIELD_DP32(t, ID_ISAR6, SPECRES, 1);
- cpu->isar.id_isar6 = t;
-
- t = cpu->isar.mvfr1;
- t = FIELD_DP32(t, MVFR1, FPHP, 3); /* v8.2-FP16 */
- t = FIELD_DP32(t, MVFR1, SIMDHP, 2); /* v8.2-FP16 */
- cpu->isar.mvfr1 = t;
-
- t = cpu->isar.mvfr2;
- t = FIELD_DP32(t, MVFR2, SIMDMISC, 3); /* SIMD MaxNum */
- t = FIELD_DP32(t, MVFR2, FPMISC, 4); /* FP MaxNum */
- cpu->isar.mvfr2 = t;
-
- t = cpu->isar.id_mmfr3;
- t = FIELD_DP32(t, ID_MMFR3, PAN, 2); /* ATS1E1 */
- cpu->isar.id_mmfr3 = t;
-
- t = cpu->isar.id_mmfr4;
- 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, CNP, 1); /* TTCNP */
- t = FIELD_DP32(t, ID_MMFR4, XNX, 1); /* TTS2UXN */
- cpu->isar.id_mmfr4 = t;
- }
-#endif
+ /*
+ * We don't set these in system emulation mode for the moment,
+ * since we don't correctly set (all of) the ID registers to
+ * advertise them.
+ */
+ set_feature(&cpu->env, ARM_FEATURE_V8);
+ {
+ uint32_t t;
+
+ t = cpu->isar.id_isar5;
+ t = FIELD_DP32(t, ID_ISAR5, AES, 2);
+ t = FIELD_DP32(t, ID_ISAR5, SHA1, 1);
+ t = FIELD_DP32(t, ID_ISAR5, SHA2, 1);
+ t = FIELD_DP32(t, ID_ISAR5, CRC32, 1);
+ t = FIELD_DP32(t, ID_ISAR5, RDM, 1);
+ t = FIELD_DP32(t, ID_ISAR5, VCMA, 1);
+ cpu->isar.id_isar5 = t;
+
+ t = cpu->isar.id_isar6;
+ t = FIELD_DP32(t, ID_ISAR6, JSCVT, 1);
+ t = FIELD_DP32(t, ID_ISAR6, DP, 1);
+ t = FIELD_DP32(t, ID_ISAR6, FHM, 1);
+ t = FIELD_DP32(t, ID_ISAR6, SB, 1);
+ t = FIELD_DP32(t, ID_ISAR6, SPECRES, 1);
+ cpu->isar.id_isar6 = t;
+
+ t = cpu->isar.mvfr1;
+ t = FIELD_DP32(t, MVFR1, FPHP, 3); /* v8.2-FP16 */
+ t = FIELD_DP32(t, MVFR1, SIMDHP, 2); /* v8.2-FP16 */
+ cpu->isar.mvfr1 = t;
+
+ t = cpu->isar.mvfr2;
+ t = FIELD_DP32(t, MVFR2, SIMDMISC, 3); /* SIMD MaxNum */
+ t = FIELD_DP32(t, MVFR2, FPMISC, 4); /* FP MaxNum */
+ cpu->isar.mvfr2 = t;
+
+ t = cpu->isar.id_mmfr3;
+ t = FIELD_DP32(t, ID_MMFR3, PAN, 2); /* ATS1E1 */
+ cpu->isar.id_mmfr3 = t;
+
+ t = cpu->isar.id_mmfr4;
+ 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, CNP, 1); /* TTCNP */
+ t = FIELD_DP32(t, ID_MMFR4, XNX, 1); /* TTS2UXN */
+ cpu->isar.id_mmfr4 = t;
}
+#endif
}
#endif
@@ -2267,11 +2264,7 @@ static void arm_host_initfn(Object *obj)
static const TypeInfo host_arm_cpu_type_info = {
.name = TYPE_ARM_HOST_CPU,
-#ifdef TARGET_AARCH64
.parent = TYPE_AARCH64_CPU,
-#else
- .parent = TYPE_ARM_CPU,
-#endif
.instance_init = arm_host_initfn,
};
diff --git a/target/arm/helper.c b/target/arm/helper.c
index 44d666627a..88bd9dd35d 100644
--- a/target/arm/helper.c
+++ b/target/arm/helper.c
@@ -1452,7 +1452,7 @@ static bool pmu_counter_enabled(CPUARMState *env, uint8_t counter)
}
} else {
prohibited = arm_feature(env, ARM_FEATURE_EL3) &&
- (env->cp15.mdcr_el3 & MDCR_SPME);
+ !(env->cp15.mdcr_el3 & MDCR_SPME);
}
if (prohibited && counter == 31) {
diff --git a/target/arm/kvm-consts.h b/target/arm/kvm-consts.h
index aad28258a3..580f1c1fee 100644
--- a/target/arm/kvm-consts.h
+++ b/target/arm/kvm-consts.h
@@ -136,16 +136,11 @@ MISMATCH_CHECK(QEMU_PSCI_RET_DISABLED, PSCI_RET_DISABLED);
*/
#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_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_XGENE_POTENZA, KVM_ARM_TARGET_XGENE_POTENZA);
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_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 */
#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_ARM_COPROC_MASK, KVM_REG_ARM_COPROC_MASK);
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_OP2_MASK, KVM_REG_ARM64_SYSREG_OP2_MASK);
MISMATCH_CHECK(CP_REG_ARM64_SYSREG_OP2_SHIFT, KVM_REG_ARM64_SYSREG_OP2_SHIFT);
-#endif
#undef MISMATCH_CHECK
diff --git a/target/arm/kvm.c b/target/arm/kvm.c
index 8bb7318378..2eae73315d 100644
--- a/target/arm/kvm.c
+++ b/target/arm/kvm.c
@@ -918,22 +918,15 @@ int kvm_arch_process_async_events(CPUState *cs)
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)
{
-#ifdef KVM_GUESTDBG_USE_SW_BP
if (kvm_sw_breakpoints_active(cs)) {
dbg->control |= KVM_GUESTDBG_ENABLE | KVM_GUESTDBG_USE_SW_BP;
}
-#endif
-#ifdef KVM_GUESTDBG_USE_HW
if (kvm_arm_hw_debug_active(cs)) {
dbg->control |= KVM_GUESTDBG_ENABLE | KVM_GUESTDBG_USE_HW;
kvm_arm_copy_hw_debug_data(&dbg->arch);
}
-#endif
}
void kvm_arch_init_irq_routing(KVMState *s)
diff --git a/target/arm/kvm32.c b/target/arm/kvm32.c
deleted file mode 100644
index 1f2b8f8b7a..0000000000
--- a/target/arm/kvm32.c
+++ /dev/null
@@ -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;
-}
diff --git a/target/arm/kvm_arm.h b/target/arm/kvm_arm.h
index adb38514bf..bc178eeb84 100644
--- a/target/arm/kvm_arm.h
+++ b/target/arm/kvm_arm.h
@@ -406,13 +406,7 @@ static inline const char *gic_class_name(void)
static inline const char *gicv3_class_name(void)
{
if (kvm_irqchip_in_kernel()) {
-#ifdef TARGET_AARCH64
return "kvm-arm-gicv3";
-#else
- error_report("KVM GICv3 acceleration is not supported on this "
- "platform");
- exit(1);
-#endif
} else {
if (kvm_enabled()) {
error_report("Userspace GICv3 is not supported with KVM");
diff --git a/target/arm/meson.build b/target/arm/meson.build
index 8990090712..f5de2a77b8 100644
--- a/target/arm/meson.build
+++ b/target/arm/meson.build
@@ -34,10 +34,7 @@ arm_ss.add(zlib)
arm_ss.add(when: 'CONFIG_TCG', if_true: files('arm-semi.c'))
-kvm_ss = ss.source_set()
-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: 'CONFIG_KVM', if_true: files('kvm.c', 'kvm64.c'), if_false: files('kvm-stub.c'))
arm_ss.add(when: 'TARGET_AARCH64', if_true: files(
'cpu64.c',
diff --git a/target/arm/neon-dp.decode b/target/arm/neon-dp.decode
index 1e9e859291..51aa0f0819 100644
--- a/target/arm/neon-dp.decode
+++ b/target/arm/neon-dp.decode
@@ -45,11 +45,16 @@
@3same_q0 .... ... . . . size:2 .... .... .... . 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
-@3same_fp .... ... . . . . size:1 .... .... .... . q:1 . . .... \
- &3same vm=%vm_dp vn=%vn_dp vd=%vd_dp
-@3same_fp_q0 .... ... . . . . size:1 .... .... .... . 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,
+# and the 'size' bit is 0 for 32-bit float and 1 for 16-bit float.
+# This converts this encoding to the same MO_8/16/32/64 values that the
+# integer neon insns use.
+%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_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_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_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_shift vm=%vm_dp vd=%vd_dp size=1 shift=%neon_rshift_i4
diff --git a/target/arm/neon-shared.decode b/target/arm/neon-shared.decode
index f297ba8cdf..a9d010880d 100644
--- a/target/arm/neon-shared.decode
+++ b/target/arm/neon-shared.decode
@@ -34,11 +34,17 @@
%vd_dp 22:1 12:4
%vd_sp 12:4 22:1
-VCMLA 1111 110 rot:2 . 1 size:1 .... .... 1000 . q:1 . 0 .... \
- vm=%vm_dp vn=%vn_dp vd=%vd_dp
+# For VCMLA/VCADD insns, convert the single-bit size field
+# 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 .... \
- vm=%vm_dp vn=%vn_dp vd=%vd_dp
+VCMLA 1111 110 rot:2 . 1 . .... .... 1000 . q:1 . 0 .... \
+ 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
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
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 .... \
- 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 \
vm=%vm_dp vn=%vn_dp vd=%vd_dp
diff --git a/target/arm/translate-neon.c.inc b/target/arm/translate-neon.c.inc
index 2d4926316a..4d1a292981 100644
--- a/target/arm/translate-neon.c.inc
+++ b/target/arm/translate-neon.c.inc
@@ -49,6 +49,12 @@ static inline int rsub_8(DisasContext *s, int 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 "decode-neon-dp.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;
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;
}
@@ -181,8 +187,9 @@ static bool trans_VCMLA(DisasContext *s, arg_VCMLA *a)
}
opr_sz = (1 + a->q) * 8;
- fpst = fpstatus_ptr(a->size == 0 ? FPST_STD_F16 : FPST_STD);
- fn_gvec_ptr = a->size ? gen_helper_gvec_fcmlas : gen_helper_gvec_fcmlah;
+ fpst = fpstatus_ptr(a->size == MO_16 ? FPST_STD_F16 : FPST_STD);
+ 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),
vfp_reg_offset(1, a->vn),
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;
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;
}
@@ -218,8 +225,9 @@ static bool trans_VCADD(DisasContext *s, arg_VCADD *a)
}
opr_sz = (1 + a->q) * 8;
- fpst = fpstatus_ptr(a->size == 0 ? FPST_STD_F16 : FPST_STD);
- fn_gvec_ptr = a->size ? gen_helper_gvec_fcadds : gen_helper_gvec_fcaddh;
+ fpst = fpstatus_ptr(a->size == MO_16 ? FPST_STD_F16 : FPST_STD);
+ 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),
vfp_reg_offset(1, a->vn),
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)) {
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;
}
@@ -319,10 +327,10 @@ static bool trans_VCMLA_scalar(DisasContext *s, arg_VCMLA_scalar *a)
return true;
}
- fn_gvec_ptr = (a->size ? gen_helper_gvec_fcmlas_idx
- : gen_helper_gvec_fcmlah_idx);
+ fn_gvec_ptr = (a->size == MO_16) ?
+ gen_helper_gvec_fcmlah_idx : gen_helper_gvec_fcmlas_idx;
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),
vfp_reg_offset(1, a->vn),
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) \
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)) { \
return false; \
} \
@@ -1088,7 +1096,7 @@ static bool trans_VMAXNM_fp_3s(DisasContext *s, arg_3same *a)
return false;
}
- if (a->size != 0) {
+ if (a->size == MO_16) {
if (!dc_isar_feature(aa32_fp16_arith, s)) {
return false;
}
@@ -1103,7 +1111,7 @@ static bool trans_VMINNM_fp_3s(DisasContext *s, arg_3same *a)
return false;
}
- if (a->size != 0) {
+ if (a->size == MO_16) {
if (!dc_isar_feature(aa32_fp16_arith, s)) {
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 */
- 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),
vfp_reg_offset(1, a->vn),
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) \
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)) { \
return false; \
} \
@@ -1620,7 +1628,7 @@ static bool do_fp_2sh(DisasContext *s, arg_2reg_shift *a,
return false;
}
- if (a->size != 0) {
+ if (a->size == MO_16) {
if (!dc_isar_feature(aa32_fp16_arith, s)) {
return false;
}
@@ -1640,7 +1648,7 @@ static bool do_fp_2sh(DisasContext *s, arg_2reg_shift *a,
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_temp_free_ptr(fpst);
return true;
diff --git a/tests/acceptance/boot_linux_console.py b/tests/acceptance/boot_linux_console.py
index aaa781a581..4a366ce93e 100644
--- a/tests/acceptance/boot_linux_console.py
+++ b/tests/acceptance/boot_linux_console.py
@@ -568,6 +568,89 @@ class BootLinuxConsole(LinuxKernelTest):
'sda')
# 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):
"""
:avocado: tags=arch:arm
diff --git a/tests/decode/succ_ident1.decode b/tests/decode/succ_ident1.decode
new file mode 100644
index 0000000000..f15cfbe147
--- /dev/null
+++ b/tests/decode/succ_ident1.decode
@@ -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