Merge remote-tracking branch 'remotes/pmaydell/tags/pull-target-arm-20200914-1' into staging

* hw/misc/a9scu: Do not allow invalid CPU count
 * hw/misc/a9scu: Minor cleanups
 * hw/timer/armv7m_systick: assert that board code set system_clock_scale
 * decodetree: Improve identifier matching
 * target/arm: Clean up neon fp insn size field decode
 * target/arm: Remove KVM support for 32-bit Arm hosts
 * hw/arm/mps2: New board models mps2-an386, mps2-an500
 * Deprecate Unicore32 port
 * Deprecate lm32 port
 * target/arm: Count PMU events when MDCR.SPME is set
 * hw/arm: versal-virt: Correct the tx/rx GEM clocks
 * New Nuvoton iBMC board models npcm750-evb, quanta-gsj

# gpg: Signature made Mon 14 Sep 2020 16:02:06 BST
# gpg:                using RSA key E1A5C593CD419DE28E8315CF3C2525ED14360CDE
# gpg:                issuer "peter.maydell@linaro.org"
# gpg: Good signature from "Peter Maydell <peter.maydell@linaro.org>" [ultimate]
# gpg:                 aka "Peter Maydell <pmaydell@gmail.com>" [ultimate]
# gpg:                 aka "Peter Maydell <pmaydell@chiark.greenend.org.uk>" [ultimate]
# Primary key fingerprint: E1A5 C593 CD41 9DE2 8E83  15CF 3C25 25ED 1436 0CDE

* remotes/pmaydell/tags/pull-target-arm-20200914-1: (32 commits)
  tests/acceptance: console boot tests for quanta-gsj
  docs/system: Add Nuvoton machine documentation
  hw/arm/npcm7xx: add board setup stub for CPU and UART clocks
  hw/arm: Wire up BMC boot flash for npcm750-evb and quanta-gsj
  hw/ssi: NPCM7xx Flash Interface Unit device model
  hw/mem: Stubbed out NPCM7xx Memory Controller model
  hw/nvram: NPCM7xx OTP device model
  hw/arm: Load -bios image as a boot ROM for npcm7xx
  roms: Add virtual Boot ROM for NPCM7xx SoCs
  hw/arm: Add two NPCM7xx-based machines
  hw/arm: Add NPCM730 and NPCM750 SoC models
  hw/timer: Add NPCM7xx Timer device model
  hw/misc: Add NPCM7xx Clock Controller device model
  hw/misc: Add NPCM7xx System Global Control Registers device model
  hw/arm: versal-virt: Correct the tx/rx GEM clocks
  target/arm: Count PMU events when MDCR.SPME is set
  Deprecate lm32 port
  Deprecate Unicore32 port
  docs/system/arm/mps2.rst: Make board list consistent
  hw/arm/mps2: New board model mps2-an500
  ...

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
Peter Maydell
2020-09-14 16:03:08 +01:00
55 changed files with 3910 additions and 783 deletions
+3
View File
@@ -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
+10
View File
@@ -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>
Vendored
+1 -1
View File
@@ -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 | \
+1
View File
@@ -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
+12 -8
View File
@@ -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
+92
View File
@@ -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``.
+24 -8
View File
@@ -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)
'''''''''''''''''''''''''''''''''''''''''
+1
View File
@@ -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
+9
View File
@@ -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
+1
View File
@@ -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'))
+83 -14
View File
@@ -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);
}
+532
View File
File diff suppressed because it is too large Load Diff
+197
View File
@@ -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)
+1 -1
View File
@@ -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",
+1
View File
@@ -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)
+84
View File
@@ -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);
+28 -31
View File
@@ -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);
memory_region_init_io(&s->iomem, obj, &a9_scu_ops, s,
if (!s->num_cpu || s->num_cpu > A9_SCU_CPU_MAX) {
error_setg(errp, "Illegal CPU count: %u", s->num_cpu);
return;
}
memory_region_init_io(&s->iomem, OBJECT(s), &a9_scu_ops, s,
"a9-scu", 0x100);
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,
};
+4
View File
@@ -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',
+266
View File
@@ -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);
+269
View File
@@ -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);

Some files were not shown because too many files have changed in this diff Show More