aboutsummaryrefslogtreecommitdiff
path: root/hw
diff options
context:
space:
mode:
authorStefan Hajnoczi <stefanha@redhat.com>2025-02-21 07:14:03 +0800
committerStefan Hajnoczi <stefanha@redhat.com>2025-02-21 07:14:03 +0800
commitf41af4c5857b6983766aaffc041580ff170d0679 (patch)
tree9d79ee877ba0cf60f69ea761278ec925d6986d81 /hw
parent40efe733e10cc00e4fb4f9f5790a28e744e63c62 (diff)
parent1c3169179b8242866316108386800379c4e22974 (diff)
downloadqemu-f41af4c5857b6983766aaffc041580ff170d0679.zip
qemu-f41af4c5857b6983766aaffc041580ff170d0679.tar.gz
qemu-f41af4c5857b6983766aaffc041580ff170d0679.tar.bz2
Merge tag 'pull-target-arm-20250220' of https://git.linaro.org/people/pmaydell/qemu-arm into staging
target-arm queue: * Fix some incorrect syndrome values in various sysreg traps * Clean up sysreg trap code to avoid similar future bugs * Make boards/SoCs using a9mpcore and a15mpcore objects specify number of GIC interrupts explicitly * Kconfig: Extract CONFIG_USB_CHIPIDEA from CONFIG_IMX * target/arm: Use uint32_t in t32_expandimm_imm() * New board model: NPCM845 Evaluation board "npcm845-evb" # -----BEGIN PGP SIGNATURE----- # # iQJNBAABCAA3FiEE4aXFk81BneKOgxXPPCUl7RQ2DN4FAme3Vk8ZHHBldGVyLm1h # eWRlbGxAbGluYXJvLm9yZwAKCRA8JSXtFDYM3t8QD/48yOUtFwUMFrwbbszK5rss # Ghhtk0ylKIxPQirgzjLUNq4hV2MZEtdmyiqaEllvA0aS839oWhFW0hbsGmoL/TVF # tqXVP3Y1dMubmNHCGcgiFqaxaInnNSC9S1ALiEm5a37g519706WLXPVLqJJ9t31b # uWHKT1hqxstzWSExhGrEkSEghcgN3u1KyCz0zyq9bk/F3OFWZfHNH6JqutQX18Ua # 5HtcD1Pum6WjayBc3y4AYVYH4xyQclY7LPR+zKNf2d5GuZ+J6MlXMyfCuE2/J//m # wHAtAoeuFhi/HFHR4vQP4L7HrhFrECbjfWha85F/rmiOAo6LnbICyPt4tAPe5So3 # FCtSHfht9ToulBqULE+F/AWVCdt8UeDRgOANSHFsMkxYiUK8QpMv8A2AtwJUqiMp # WbAzw31f6SgANgFQObhoRNE3QyX8V53ZJAsPhDooTxwMiglqVTM3Xux8W2zz9FdU # BTwCy23efBqKf4RWfeHjAXctGshePI1mTBJmvEKG5G5ligMNeg7ZiQqqfRVBagc/ # gpsQKNjpN9MVVds3thUvMCYO/9NOeeAtcVA2vW7qf7HrYaM72UngCPWjhNfAj/9I # 9hxgqEnKC6qoD/zMyFv+XwqNlL1PuD79rbvN8TWFd/f8iBIYOY6WVEYmsi7WGugI # WzYI93RqFaQhrpyHDcRVGw== # =djUd # -----END PGP SIGNATURE----- # gpg: Signature made Fri 21 Feb 2025 00:20:31 HKT # gpg: using RSA key E1A5C593CD419DE28E8315CF3C2525ED14360CDE # gpg: issuer "peter.maydell@linaro.org" # gpg: Good signature from "Peter Maydell <peter.maydell@linaro.org>" [full] # gpg: aka "Peter Maydell <pmaydell@gmail.com>" [full] # gpg: aka "Peter Maydell <pmaydell@chiark.greenend.org.uk>" [full] # gpg: aka "Peter Maydell <peter@archaic.org.uk>" [unknown] # Primary key fingerprint: E1A5 C593 CD41 9DE2 8E83 15CF 3C25 25ED 1436 0CDE * tag 'pull-target-arm-20250220' of https://git.linaro.org/people/pmaydell/qemu-arm: (41 commits) docs/system/arm: Add Description for NPCM8XX SoC hw/arm: Add NPCM845 Evaluation board hw/arm: Add NPCM8XX SoC hw/net: Add NPCM8XX PCS Module hw/misc: Support NPCM8XX CLK Module Registers hw/misc: Add nr_regs and cold_reset_values to NPCM CLK hw/misc: Move NPCM7XX CLK to NPCM CLK hw/misc: Rename npcm7xx_clk to npcm_clk hw/misc: Support 8-bytes memop in NPCM GCR module hw/misc: Store DRAM size in NPCM8XX GCR Module hw/misc: Add support for NPCM8XX GCR hw/misc: Add nr_regs and cold_reset_values to NPCM GCR hw/misc: Move NPCM7XX GCR to NPCM GCR hw/misc: Rename npcm7xx_gcr to npcm_gcr hw/ssi: Make flash size a property in NPCM7XX FIU pc-bios: Add NPCM8XX vBootrom roms: Update vbootrom to 1287b6e target/arm: Use uint32_t in t32_expandimm_imm() Kconfig: Extract CONFIG_USB_CHIPIDEA from CONFIG_IMX hw/cpu/arm_mpcore: Remove default values for GIC external IRQs ... Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
Diffstat (limited to 'hw')
-rw-r--r--hw/arm/Kconfig19
-rw-r--r--hw/arm/exynos4210.c10
-rw-r--r--hw/arm/highbank.c8
-rw-r--r--hw/arm/meson.build1
-rw-r--r--hw/arm/npcm7xx.c6
-rw-r--r--hw/arm/npcm8xx.c805
-rw-r--r--hw/arm/npcm8xx_boards.c254
-rw-r--r--hw/arm/realview.c11
-rw-r--r--hw/arm/vexpress.c7
-rw-r--r--hw/arm/xilinx_zynq.c43
-rw-r--r--hw/cpu/a15mpcore.c18
-rw-r--r--hw/cpu/a9mpcore.c18
-rw-r--r--hw/intc/arm_gicv3_cpuif.c15
-rw-r--r--hw/misc/meson.build4
-rw-r--r--hw/misc/npcm7xx_gcr.c264
-rw-r--r--hw/misc/npcm_clk.c (renamed from hw/misc/npcm7xx_clk.c)235
-rw-r--r--hw/misc/npcm_gcr.c482
-rw-r--r--hw/misc/trace-events12
-rw-r--r--hw/net/meson.build1
-rw-r--r--hw/net/npcm_pcs.c410
-rw-r--r--hw/net/trace-events4
-rw-r--r--hw/ssi/npcm7xx_fiu.c16
-rw-r--r--hw/usb/Kconfig4
-rw-r--r--hw/usb/meson.build2
24 files changed, 2261 insertions, 388 deletions
diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig
index 256013c..504841c 100644
--- a/hw/arm/Kconfig
+++ b/hw/arm/Kconfig
@@ -303,7 +303,7 @@ config ZYNQ
select PL330
select SDHCI
select SSI_M25P80
- select USB_EHCI_SYSBUS
+ select USB_CHIPIDEA
select XILINX # UART
select XILINX_AXI
select XILINX_SPI
@@ -481,6 +481,19 @@ config NPCM7XX
select PCA954X
select USB_OHCI_SYSBUS
+config NPCM8XX
+ bool
+ default y
+ depends on TCG && AARCH64
+ select ARM_GIC
+ select SMBUS
+ select PL310 # cache controller
+ select NPCM7XX
+ select SERIAL
+ select SSI
+ select UNIMP
+
+
config FSL_IMX25
bool
default y
@@ -489,6 +502,7 @@ config FSL_IMX25
select IMX
select IMX_FEC
select IMX_I2C
+ select USB_CHIPIDEA
select WDT_IMX2
select SDHCI
@@ -516,6 +530,7 @@ config FSL_IMX6
select PL310 # cache controller
select PCI_EXPRESS_DESIGNWARE
select SDHCI
+ select USB_CHIPIDEA
select OR_IRQ
config ASPEED_SOC
@@ -576,6 +591,7 @@ config FSL_IMX7
select SDHCI
select OR_IRQ
select UNIMP
+ select USB_CHIPIDEA
config ARM_SMMUV3
bool
@@ -591,6 +607,7 @@ config FSL_IMX6UL
select IMX_I2C
select WDT_IMX2
select SDHCI
+ select USB_CHIPIDEA
select UNIMP
config MICROBIT
diff --git a/hw/arm/exynos4210.c b/hw/arm/exynos4210.c
index dd0edc8..b452470 100644
--- a/hw/arm/exynos4210.c
+++ b/hw/arm/exynos4210.c
@@ -103,6 +103,8 @@
#define EXYNOS4210_PL330_BASE1_ADDR 0x12690000
#define EXYNOS4210_PL330_BASE2_ADDR 0x12850000
+#define GIC_EXT_IRQS 64 /* FIXME: verify for this SoC */
+
enum ExtGicId {
EXT_GIC_ID_MDMA_LCD0 = 66,
EXT_GIC_ID_PDMA0,
@@ -394,7 +396,8 @@ static void exynos4210_init_board_irqs(Exynos4210State *s)
}
if (irq_id) {
qdev_connect_gpio_out(splitter, splitin,
- qdev_get_gpio_in(extgicdev, irq_id - 32));
+ qdev_get_gpio_in(extgicdev,
+ irq_id - GIC_INTERNAL));
}
}
for (; n < EXYNOS4210_MAX_INT_COMBINER_IN_IRQ; n++) {
@@ -421,7 +424,8 @@ static void exynos4210_init_board_irqs(Exynos4210State *s)
s->irq_table[n] = qdev_get_gpio_in(splitter, 0);
qdev_connect_gpio_out(splitter, 0, qdev_get_gpio_in(intcdev, n));
qdev_connect_gpio_out(splitter, 1,
- qdev_get_gpio_in(extgicdev, irq_id - 32));
+ qdev_get_gpio_in(extgicdev,
+ irq_id - GIC_INTERNAL));
} else {
s->irq_table[n] = qdev_get_gpio_in(intcdev, n);
}
@@ -586,6 +590,8 @@ static void exynos4210_realize(DeviceState *socdev, Error **errp)
/* Private memory region and Internal GIC */
qdev_prop_set_uint32(DEVICE(&s->a9mpcore), "num-cpu", EXYNOS4210_NCPUS);
+ qdev_prop_set_uint32(DEVICE(&s->a9mpcore), "num-irq",
+ GIC_EXT_IRQS + GIC_INTERNAL);
busdev = SYS_BUS_DEVICE(&s->a9mpcore);
sysbus_realize(busdev, &error_fatal);
sysbus_mmio_map(busdev, 0, EXYNOS4210_SMP_PRIVATE_BASE_ADDR);
diff --git a/hw/arm/highbank.c b/hw/arm/highbank.c
index 495704d..0f3c207 100644
--- a/hw/arm/highbank.c
+++ b/hw/arm/highbank.c
@@ -45,7 +45,7 @@
#define MVBAR_ADDR 0x200
#define BOARD_SETUP_ADDR (MVBAR_ADDR + 8 * sizeof(uint32_t))
-#define NIRQ_GIC 160
+#define GIC_EXT_IRQS 128 /* EnergyCore ECX-1000 & ECX-2000 */
/* Board init. */
@@ -180,7 +180,7 @@ static void calxeda_init(MachineState *machine, enum cxmachines machine_id)
{
DeviceState *dev = NULL;
SysBusDevice *busdev;
- qemu_irq pic[128];
+ qemu_irq pic[GIC_EXT_IRQS];
int n;
unsigned int smp_cpus = machine->smp.cpus;
qemu_irq cpu_irq[4];
@@ -260,7 +260,7 @@ static void calxeda_init(MachineState *machine, enum cxmachines machine_id)
break;
}
qdev_prop_set_uint32(dev, "num-cpu", smp_cpus);
- qdev_prop_set_uint32(dev, "num-irq", NIRQ_GIC);
+ qdev_prop_set_uint32(dev, "num-irq", GIC_EXT_IRQS + GIC_INTERNAL);
busdev = SYS_BUS_DEVICE(dev);
sysbus_realize_and_unref(busdev, &error_fatal);
sysbus_mmio_map(busdev, 0, MPCORE_PERIPHBASE);
@@ -271,7 +271,7 @@ static void calxeda_init(MachineState *machine, enum cxmachines machine_id)
sysbus_connect_irq(busdev, n + 3 * smp_cpus, cpu_vfiq[n]);
}
- for (n = 0; n < 128; n++) {
+ for (n = 0; n < GIC_EXT_IRQS; n++) {
pic[n] = qdev_get_gpio_in(dev, n);
}
diff --git a/hw/arm/meson.build b/hw/arm/meson.build
index 490234b..465c757 100644
--- a/hw/arm/meson.build
+++ b/hw/arm/meson.build
@@ -12,6 +12,7 @@ arm_ss.add(when: 'CONFIG_MUSICPAL', if_true: files('musicpal.c'))
arm_ss.add(when: 'CONFIG_NETDUINOPLUS2', if_true: files('netduinoplus2.c'))
arm_ss.add(when: 'CONFIG_OLIMEX_STM32_H405', if_true: files('olimex-stm32-h405.c'))
arm_ss.add(when: 'CONFIG_NPCM7XX', if_true: files('npcm7xx.c', 'npcm7xx_boards.c'))
+arm_ss.add(when: 'CONFIG_NPCM8XX', if_true: files('npcm8xx.c', 'npcm8xx_boards.c'))
arm_ss.add(when: 'CONFIG_REALVIEW', if_true: files('realview.c'))
arm_ss.add(when: 'CONFIG_SBSA_REF', if_true: files('sbsa-ref.c'))
arm_ss.add(when: 'CONFIG_STELLARIS', if_true: files('stellaris.c'))
diff --git a/hw/arm/npcm7xx.c b/hw/arm/npcm7xx.c
index 386b2c3..2d6e08b 100644
--- a/hw/arm/npcm7xx.c
+++ b/hw/arm/npcm7xx.c
@@ -292,17 +292,21 @@ static const struct {
hwaddr regs_addr;
int cs_count;
const hwaddr *flash_addr;
+ size_t flash_size;
} npcm7xx_fiu[] = {
{
.name = "fiu0",
.regs_addr = 0xfb000000,
.cs_count = ARRAY_SIZE(npcm7xx_fiu0_flash_addr),
.flash_addr = npcm7xx_fiu0_flash_addr,
+ .flash_size = 128 * MiB,
+
}, {
.name = "fiu3",
.regs_addr = 0xc0000000,
.cs_count = ARRAY_SIZE(npcm7xx_fiu3_flash_addr),
.flash_addr = npcm7xx_fiu3_flash_addr,
+ .flash_size = 128 * MiB,
},
};
@@ -735,6 +739,8 @@ static void npcm7xx_realize(DeviceState *dev, Error **errp)
object_property_set_int(OBJECT(sbd), "cs-count",
npcm7xx_fiu[i].cs_count, &error_abort);
+ object_property_set_int(OBJECT(sbd), "flash-size",
+ npcm7xx_fiu[i].flash_size, &error_abort);
sysbus_realize(sbd, &error_abort);
sysbus_mmio_map(sbd, 0, npcm7xx_fiu[i].regs_addr);
diff --git a/hw/arm/npcm8xx.c b/hw/arm/npcm8xx.c
new file mode 100644
index 0000000..f182acc
--- /dev/null
+++ b/hw/arm/npcm8xx.c
@@ -0,0 +1,805 @@
+/*
+ * Nuvoton NPCM8xx SoC family.
+ *
+ * Copyright 2022 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/boards.h"
+#include "hw/arm/boot.h"
+#include "hw/arm/bsa.h"
+#include "hw/arm/npcm8xx.h"
+#include "hw/char/serial-mm.h"
+#include "hw/intc/arm_gic.h"
+#include "hw/loader.h"
+#include "hw/misc/unimp.h"
+#include "hw/qdev-clock.h"
+#include "hw/qdev-properties.h"
+#include "qapi/error.h"
+#include "qemu/units.h"
+#include "system/system.h"
+
+/*
+ * This covers the whole MMIO space. We'll use this to catch any MMIO accesses
+ * that aren't handled by a device.
+ */
+#define NPCM8XX_MMIO_BA 0x80000000
+#define NPCM8XX_MMIO_SZ 0x7ffd0000
+
+/* OTP fuse array */
+#define NPCM8XX_OTP_BA 0xf0189000
+
+/* GIC Distributor */
+#define NPCM8XX_GICD_BA 0xdfff9000
+#define NPCM8XX_GICC_BA 0xdfffa000
+
+/* Core system modules. */
+#define NPCM8XX_CPUP_BA 0xf03fe000
+#define NPCM8XX_GCR_BA 0xf0800000
+#define NPCM8XX_CLK_BA 0xf0801000
+#define NPCM8XX_MC_BA 0xf0824000
+#define NPCM8XX_RNG_BA 0xf000b000
+
+/* ADC Module */
+#define NPCM8XX_ADC_BA 0xf000c000
+
+/* Internal AHB SRAM */
+#define NPCM8XX_RAM3_BA 0xc0008000
+#define NPCM8XX_RAM3_SZ (4 * KiB)
+
+/* Memory blocks at the end of the address space */
+#define NPCM8XX_RAM2_BA 0xfffb0000
+#define NPCM8XX_RAM2_SZ (256 * KiB)
+#define NPCM8XX_ROM_BA 0xffff0100
+#define NPCM8XX_ROM_SZ (64 * KiB)
+
+/* SDHCI Modules */
+#define NPCM8XX_MMC_BA 0xf0842000
+
+/* Run PLL1 at 1600 MHz */
+#define NPCM8XX_PLLCON1_FIXUP_VAL 0x00402101
+/* Run the CPU from PLL1 and UART from PLL2 */
+#define NPCM8XX_CLKSEL_FIXUP_VAL 0x004aaba9
+
+/* Clock configuration values to be fixed up when bypassing bootloader */
+
+/*
+ * Interrupt lines going into the GIC. This does not include internal Cortex-A35
+ * interrupts.
+ */
+enum NPCM8xxInterrupt {
+ NPCM8XX_ADC_IRQ = 0,
+ NPCM8XX_PECI_IRQ = 6,
+ NPCM8XX_KCS_HIB_IRQ = 9,
+ NPCM8XX_MMC_IRQ = 26,
+ NPCM8XX_TIMER0_IRQ = 32, /* Timer Module 0 */
+ NPCM8XX_TIMER1_IRQ,
+ NPCM8XX_TIMER2_IRQ,
+ NPCM8XX_TIMER3_IRQ,
+ NPCM8XX_TIMER4_IRQ,
+ NPCM8XX_TIMER5_IRQ, /* Timer Module 1 */
+ NPCM8XX_TIMER6_IRQ,
+ NPCM8XX_TIMER7_IRQ,
+ NPCM8XX_TIMER8_IRQ,
+ NPCM8XX_TIMER9_IRQ,
+ NPCM8XX_TIMER10_IRQ, /* Timer Module 2 */
+ NPCM8XX_TIMER11_IRQ,
+ NPCM8XX_TIMER12_IRQ,
+ NPCM8XX_TIMER13_IRQ,
+ NPCM8XX_TIMER14_IRQ,
+ NPCM8XX_WDG0_IRQ = 47, /* Timer Module 0 Watchdog */
+ NPCM8XX_WDG1_IRQ, /* Timer Module 1 Watchdog */
+ NPCM8XX_WDG2_IRQ, /* Timer Module 2 Watchdog */
+ NPCM8XX_EHCI1_IRQ = 61,
+ NPCM8XX_OHCI1_IRQ,
+ NPCM8XX_EHCI2_IRQ,
+ NPCM8XX_OHCI2_IRQ,
+ NPCM8XX_PWM0_IRQ = 93, /* PWM module 0 */
+ NPCM8XX_PWM1_IRQ, /* PWM module 1 */
+ NPCM8XX_MFT0_IRQ = 96, /* MFT module 0 */
+ NPCM8XX_MFT1_IRQ, /* MFT module 1 */
+ NPCM8XX_MFT2_IRQ, /* MFT module 2 */
+ NPCM8XX_MFT3_IRQ, /* MFT module 3 */
+ NPCM8XX_MFT4_IRQ, /* MFT module 4 */
+ NPCM8XX_MFT5_IRQ, /* MFT module 5 */
+ NPCM8XX_MFT6_IRQ, /* MFT module 6 */
+ NPCM8XX_MFT7_IRQ, /* MFT module 7 */
+ NPCM8XX_PCI_MBOX1_IRQ = 105,
+ NPCM8XX_PCI_MBOX2_IRQ,
+ NPCM8XX_GPIO0_IRQ = 116,
+ NPCM8XX_GPIO1_IRQ,
+ NPCM8XX_GPIO2_IRQ,
+ NPCM8XX_GPIO3_IRQ,
+ NPCM8XX_GPIO4_IRQ,
+ NPCM8XX_GPIO5_IRQ,
+ NPCM8XX_GPIO6_IRQ,
+ NPCM8XX_GPIO7_IRQ,
+ NPCM8XX_SMBUS0_IRQ = 128,
+ NPCM8XX_SMBUS1_IRQ,
+ NPCM8XX_SMBUS2_IRQ,
+ NPCM8XX_SMBUS3_IRQ,
+ NPCM8XX_SMBUS4_IRQ,
+ NPCM8XX_SMBUS5_IRQ,
+ NPCM8XX_SMBUS6_IRQ,
+ NPCM8XX_SMBUS7_IRQ,
+ NPCM8XX_SMBUS8_IRQ,
+ NPCM8XX_SMBUS9_IRQ,
+ NPCM8XX_SMBUS10_IRQ,
+ NPCM8XX_SMBUS11_IRQ,
+ NPCM8XX_SMBUS12_IRQ,
+ NPCM8XX_SMBUS13_IRQ,
+ NPCM8XX_SMBUS14_IRQ,
+ NPCM8XX_SMBUS15_IRQ,
+ NPCM8XX_SMBUS16_IRQ,
+ NPCM8XX_SMBUS17_IRQ,
+ NPCM8XX_SMBUS18_IRQ,
+ NPCM8XX_SMBUS19_IRQ,
+ NPCM8XX_SMBUS20_IRQ,
+ NPCM8XX_SMBUS21_IRQ,
+ NPCM8XX_SMBUS22_IRQ,
+ NPCM8XX_SMBUS23_IRQ,
+ NPCM8XX_SMBUS24_IRQ,
+ NPCM8XX_SMBUS25_IRQ,
+ NPCM8XX_SMBUS26_IRQ,
+ NPCM8XX_UART0_IRQ = 192,
+ NPCM8XX_UART1_IRQ,
+ NPCM8XX_UART2_IRQ,
+ NPCM8XX_UART3_IRQ,
+ NPCM8XX_UART4_IRQ,
+ NPCM8XX_UART5_IRQ,
+ NPCM8XX_UART6_IRQ,
+};
+
+/* Total number of GIC interrupts, including internal Cortex-A35 interrupts. */
+#define NPCM8XX_NUM_IRQ (288)
+#define NPCM8XX_PPI_BASE(cpu) \
+ ((NPCM8XX_NUM_IRQ - GIC_INTERNAL) + (cpu) * GIC_INTERNAL)
+
+/* Register base address for each Timer Module */
+static const hwaddr npcm8xx_tim_addr[] = {
+ 0xf0008000,
+ 0xf0009000,
+ 0xf000a000,
+};
+
+/* Register base address for each 16550 UART */
+static const hwaddr npcm8xx_uart_addr[] = {
+ 0xf0000000,
+ 0xf0001000,
+ 0xf0002000,
+ 0xf0003000,
+ 0xf0004000,
+ 0xf0005000,
+ 0xf0006000,
+};
+
+/* Direct memory-mapped access to SPI0 CS0-1. */
+static const hwaddr npcm8xx_fiu0_flash_addr[] = {
+ 0x80000000, /* CS0 */
+ 0x88000000, /* CS1 */
+};
+
+/* Direct memory-mapped access to SPI1 CS0-3. */
+static const hwaddr npcm8xx_fiu1_flash_addr[] = {
+ 0x90000000, /* CS0 */
+ 0x91000000, /* CS1 */
+ 0x92000000, /* CS2 */
+ 0x93000000, /* CS3 */
+};
+
+/* Direct memory-mapped access to SPI3 CS0-3. */
+static const hwaddr npcm8xx_fiu3_flash_addr[] = {
+ 0xa0000000, /* CS0 */
+ 0xa8000000, /* CS1 */
+ 0xb0000000, /* CS2 */
+ 0xb8000000, /* CS3 */
+};
+
+/* Register base address for each PWM Module */
+static const hwaddr npcm8xx_pwm_addr[] = {
+ 0xf0103000,
+ 0xf0104000,
+ 0xf0105000,
+};
+
+/* Register base address for each MFT Module */
+static const hwaddr npcm8xx_mft_addr[] = {
+ 0xf0180000,
+ 0xf0181000,
+ 0xf0182000,
+ 0xf0183000,
+ 0xf0184000,
+ 0xf0185000,
+ 0xf0186000,
+ 0xf0187000,
+};
+
+/* Direct memory-mapped access to each SMBus Module. */
+static const hwaddr npcm8xx_smbus_addr[] = {
+ 0xf0080000,
+ 0xf0081000,
+ 0xf0082000,
+ 0xf0083000,
+ 0xf0084000,
+ 0xf0085000,
+ 0xf0086000,
+ 0xf0087000,
+ 0xf0088000,
+ 0xf0089000,
+ 0xf008a000,
+ 0xf008b000,
+ 0xf008c000,
+ 0xf008d000,
+ 0xf008e000,
+ 0xf008f000,
+ 0xfff00000,
+ 0xfff01000,
+ 0xfff02000,
+ 0xfff03000,
+ 0xfff04000,
+ 0xfff05000,
+ 0xfff06000,
+ 0xfff07000,
+ 0xfff08000,
+ 0xfff09000,
+ 0xfff0a000,
+};
+
+/* Register base address for each USB host EHCI registers */
+static const hwaddr npcm8xx_ehci_addr[] = {
+ 0xf0828100,
+ 0xf082a100,
+};
+
+/* Register base address for each USB host OHCI registers */
+static const hwaddr npcm8xx_ohci_addr[] = {
+ 0xf0829000,
+ 0xf082b000,
+};
+
+static const struct {
+ hwaddr regs_addr;
+ uint32_t reset_pu;
+ uint32_t reset_pd;
+ uint32_t reset_osrc;
+ uint32_t reset_odsc;
+} npcm8xx_gpio[] = {
+ {
+ .regs_addr = 0xf0010000,
+ .reset_pu = 0x00000300,
+ .reset_pd = 0x000f0000,
+ }, {
+ .regs_addr = 0xf0011000,
+ .reset_pu = 0xe0fefe01,
+ .reset_pd = 0x07000000,
+ }, {
+ .regs_addr = 0xf0012000,
+ .reset_pu = 0xc00fffff,
+ .reset_pd = 0x3ff00000,
+ }, {
+ .regs_addr = 0xf0013000,
+ .reset_pd = 0x00003000,
+ }, {
+ .regs_addr = 0xf0014000,
+ .reset_pu = 0xffff0000,
+ }, {
+ .regs_addr = 0xf0015000,
+ .reset_pu = 0xff8387fe,
+ .reset_pd = 0x007c0001,
+ .reset_osrc = 0x08000000,
+ }, {
+ .regs_addr = 0xf0016000,
+ .reset_pu = 0x00000801,
+ .reset_pd = 0x00000302,
+ }, {
+ .regs_addr = 0xf0017000,
+ .reset_pu = 0x000002ff,
+ .reset_pd = 0x00000c00,
+ },
+};
+
+static const struct {
+ const char *name;
+ hwaddr regs_addr;
+ int cs_count;
+ const hwaddr *flash_addr;
+ size_t flash_size;
+} npcm8xx_fiu[] = {
+ {
+ .name = "fiu0",
+ .regs_addr = 0xfb000000,
+ .cs_count = ARRAY_SIZE(npcm8xx_fiu0_flash_addr),
+ .flash_addr = npcm8xx_fiu0_flash_addr,
+ .flash_size = 128 * MiB,
+ },
+ {
+ .name = "fiu1",
+ .regs_addr = 0xfb002000,
+ .cs_count = ARRAY_SIZE(npcm8xx_fiu1_flash_addr),
+ .flash_addr = npcm8xx_fiu1_flash_addr,
+ .flash_size = 16 * MiB,
+ }, {
+ .name = "fiu3",
+ .regs_addr = 0xc0000000,
+ .cs_count = ARRAY_SIZE(npcm8xx_fiu3_flash_addr),
+ .flash_addr = npcm8xx_fiu3_flash_addr,
+ .flash_size = 128 * MiB,
+ },
+};
+
+static struct arm_boot_info npcm8xx_binfo = {
+ .loader_start = NPCM8XX_LOADER_START,
+ .smp_loader_start = NPCM8XX_SMP_LOADER_START,
+ .smp_bootreg_addr = NPCM8XX_SMP_BOOTREG_ADDR,
+ .gic_cpu_if_addr = NPCM8XX_GICC_BA,
+ .secure_boot = false,
+ .board_id = -1,
+ .board_setup_addr = NPCM8XX_BOARD_SETUP_ADDR,
+};
+
+void npcm8xx_load_kernel(MachineState *machine, NPCM8xxState *soc)
+{
+ npcm8xx_binfo.ram_size = machine->ram_size;
+
+ arm_load_kernel(&soc->cpu[0], machine, &npcm8xx_binfo);
+}
+
+static void npcm8xx_init_fuses(NPCM8xxState *s)
+{
+ NPCM8xxClass *nc = NPCM8XX_GET_CLASS(s);
+ uint32_t value;
+
+ /*
+ * The initial mask of disabled modules indicates the chip derivative (e.g.
+ * NPCM750 or NPCM730).
+ */
+ value = cpu_to_le32(nc->disabled_modules);
+ npcm7xx_otp_array_write(&s->fuse_array, &value, NPCM7XX_FUSE_DERIVATIVE,
+ sizeof(value));
+}
+
+static void npcm8xx_write_adc_calibration(NPCM8xxState *s)
+{
+ /* Both ADC and the fuse array must have realized. */
+ QEMU_BUILD_BUG_ON(sizeof(s->adc.calibration_r_values) != 4);
+ npcm7xx_otp_array_write(&s->fuse_array, s->adc.calibration_r_values,
+ NPCM7XX_FUSE_ADC_CALIB, sizeof(s->adc.calibration_r_values));
+}
+
+static qemu_irq npcm8xx_irq(NPCM8xxState *s, int n)
+{
+ return qdev_get_gpio_in(DEVICE(&s->gic), n);
+}
+
+static void npcm8xx_init(Object *obj)
+{
+ NPCM8xxState *s = NPCM8XX(obj);
+ int i;
+
+ object_initialize_child(obj, "cpu-cluster", &s->cpu_cluster,
+ TYPE_CPU_CLUSTER);
+ for (i = 0; i < NPCM8XX_MAX_NUM_CPUS; i++) {
+ object_initialize_child(OBJECT(&s->cpu_cluster), "cpu[*]", &s->cpu[i],
+ ARM_CPU_TYPE_NAME("cortex-a35"));
+ }
+ object_initialize_child(obj, "gic", &s->gic, TYPE_ARM_GIC);
+ object_initialize_child(obj, "gcr", &s->gcr, TYPE_NPCM8XX_GCR);
+ object_property_add_alias(obj, "power-on-straps", OBJECT(&s->gcr),
+ "power-on-straps");
+ object_initialize_child(obj, "clk", &s->clk, TYPE_NPCM8XX_CLK);
+ object_initialize_child(obj, "otp", &s->fuse_array,
+ TYPE_NPCM7XX_FUSE_ARRAY);
+ object_initialize_child(obj, "mc", &s->mc, TYPE_NPCM7XX_MC);
+ object_initialize_child(obj, "rng", &s->rng, TYPE_NPCM7XX_RNG);
+ object_initialize_child(obj, "adc", &s->adc, TYPE_NPCM7XX_ADC);
+
+ for (i = 0; i < ARRAY_SIZE(s->tim); i++) {
+ object_initialize_child(obj, "tim[*]", &s->tim[i], TYPE_NPCM7XX_TIMER);
+ }
+
+ for (i = 0; i < ARRAY_SIZE(s->gpio); i++) {
+ object_initialize_child(obj, "gpio[*]", &s->gpio[i], TYPE_NPCM7XX_GPIO);
+ }
+
+
+ for (i = 0; i < ARRAY_SIZE(s->smbus); i++) {
+ object_initialize_child(obj, "smbus[*]", &s->smbus[i],
+ TYPE_NPCM7XX_SMBUS);
+ DEVICE(&s->smbus[i])->id = g_strdup_printf("smbus[%d]", i);
+ }
+
+ for (i = 0; i < ARRAY_SIZE(s->ehci); i++) {
+ object_initialize_child(obj, "ehci[*]", &s->ehci[i], TYPE_NPCM7XX_EHCI);
+ }
+ for (i = 0; i < ARRAY_SIZE(s->ohci); i++) {
+ object_initialize_child(obj, "ohci[*]", &s->ohci[i], TYPE_SYSBUS_OHCI);
+ }
+
+ QEMU_BUILD_BUG_ON(ARRAY_SIZE(npcm8xx_fiu) != ARRAY_SIZE(s->fiu));
+ for (i = 0; i < ARRAY_SIZE(s->fiu); i++) {
+ object_initialize_child(obj, npcm8xx_fiu[i].name, &s->fiu[i],
+ TYPE_NPCM7XX_FIU);
+ }
+
+ for (i = 0; i < ARRAY_SIZE(s->pwm); i++) {
+ object_initialize_child(obj, "pwm[*]", &s->pwm[i], TYPE_NPCM7XX_PWM);
+ }
+
+ for (i = 0; i < ARRAY_SIZE(s->mft); i++) {
+ object_initialize_child(obj, "mft[*]", &s->mft[i], TYPE_NPCM7XX_MFT);
+ }
+
+ object_initialize_child(obj, "mmc", &s->mmc, TYPE_NPCM7XX_SDHCI);
+}
+
+static void npcm8xx_realize(DeviceState *dev, Error **errp)
+{
+ NPCM8xxState *s = NPCM8XX(dev);
+ NPCM8xxClass *nc = NPCM8XX_GET_CLASS(s);
+ int i;
+
+ if (memory_region_size(s->dram) > NPCM8XX_DRAM_SZ) {
+ error_setg(errp, "%s: NPCM8xx cannot address more than %" PRIu64
+ " MiB of DRAM", __func__, NPCM8XX_DRAM_SZ / MiB);
+ return;
+ }
+
+ /* CPUs */
+ for (i = 0; i < nc->num_cpus; i++) {
+ object_property_set_int(OBJECT(&s->cpu[i]), "mp-affinity",
+ arm_build_mp_affinity(i, NPCM8XX_MAX_NUM_CPUS),
+ &error_abort);
+ object_property_set_bool(OBJECT(&s->cpu[i]), "reset-hivecs", true,
+ &error_abort);
+ object_property_set_int(OBJECT(&s->cpu[i]), "core-count",
+ nc->num_cpus, &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;
+ }
+ }
+
+ /* ARM GIC for Cortex A35. Can only fail if we pass bad parameters here. */
+ object_property_set_uint(OBJECT(&s->gic), "num-cpu", nc->num_cpus, errp);
+ object_property_set_uint(OBJECT(&s->gic), "num-irq", NPCM8XX_NUM_IRQ, errp);
+ object_property_set_uint(OBJECT(&s->gic), "revision", 2, errp);
+ object_property_set_bool(OBJECT(&s->gic), "has-security-extensions", true,
+ errp);
+ if (!sysbus_realize(SYS_BUS_DEVICE(&s->gic), errp)) {
+ return;
+ }
+ for (i = 0; i < nc->num_cpus; i++) {
+ sysbus_connect_irq(SYS_BUS_DEVICE(&s->gic), i,
+ qdev_get_gpio_in(DEVICE(&s->cpu[i]), ARM_CPU_IRQ));
+ sysbus_connect_irq(SYS_BUS_DEVICE(&s->gic), i + nc->num_cpus,
+ qdev_get_gpio_in(DEVICE(&s->cpu[i]), ARM_CPU_FIQ));
+ sysbus_connect_irq(SYS_BUS_DEVICE(&s->gic), i + nc->num_cpus * 2,
+ qdev_get_gpio_in(DEVICE(&s->cpu[i]), ARM_CPU_VIRQ));
+ sysbus_connect_irq(SYS_BUS_DEVICE(&s->gic), i + nc->num_cpus * 3,
+ qdev_get_gpio_in(DEVICE(&s->cpu[i]), ARM_CPU_VFIQ));
+
+ qdev_connect_gpio_out(DEVICE(&s->cpu[i]), GTIMER_PHYS,
+ qdev_get_gpio_in(DEVICE(&s->gic),
+ NPCM8XX_PPI_BASE(i) + ARCH_TIMER_NS_EL1_IRQ));
+ qdev_connect_gpio_out(DEVICE(&s->cpu[i]), GTIMER_VIRT,
+ qdev_get_gpio_in(DEVICE(&s->gic),
+ NPCM8XX_PPI_BASE(i) + ARCH_TIMER_VIRT_IRQ));
+ qdev_connect_gpio_out(DEVICE(&s->cpu[i]), GTIMER_HYP,
+ qdev_get_gpio_in(DEVICE(&s->gic),
+ NPCM8XX_PPI_BASE(i) + ARCH_TIMER_NS_EL2_IRQ));
+ qdev_connect_gpio_out(DEVICE(&s->cpu[i]), GTIMER_SEC,
+ qdev_get_gpio_in(DEVICE(&s->gic),
+ NPCM8XX_PPI_BASE(i) + ARCH_TIMER_S_EL1_IRQ));
+ }
+ sysbus_mmio_map(SYS_BUS_DEVICE(&s->gic), 0, NPCM8XX_GICD_BA);
+ sysbus_mmio_map(SYS_BUS_DEVICE(&s->gic), 1, NPCM8XX_GICC_BA);
+
+ /* CPU cluster */
+ qdev_prop_set_uint32(DEVICE(&s->cpu_cluster), "cluster-id", 0);
+ qdev_realize(DEVICE(&s->cpu_cluster), NULL, &error_fatal);
+
+ /* 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, NPCM8XX_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, NPCM8XX_CLK_BA);
+
+ /* OTP fuse strap array. Cannot fail. */
+ sysbus_realize(SYS_BUS_DEVICE(&s->fuse_array), &error_abort);
+ sysbus_mmio_map(SYS_BUS_DEVICE(&s->fuse_array), 0, NPCM8XX_OTP_BA);
+ npcm8xx_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, NPCM8XX_MC_BA);
+
+ /* ADC Modules. Cannot fail. */
+ qdev_connect_clock_in(DEVICE(&s->adc), "clock", qdev_get_clock_out(
+ DEVICE(&s->clk), "adc-clock"));
+ sysbus_realize(SYS_BUS_DEVICE(&s->adc), &error_abort);
+ sysbus_mmio_map(SYS_BUS_DEVICE(&s->adc), 0, NPCM8XX_ADC_BA);
+ sysbus_connect_irq(SYS_BUS_DEVICE(&s->adc), 0,
+ npcm8xx_irq(s, NPCM8XX_ADC_IRQ));
+ npcm8xx_write_adc_calibration(s);
+
+ /* Timer Modules (TIM). Cannot fail. */
+ QEMU_BUILD_BUG_ON(ARRAY_SIZE(npcm8xx_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;
+
+ /* Connect the timer clock. */
+ qdev_connect_clock_in(DEVICE(&s->tim[i]), "clock", qdev_get_clock_out(
+ DEVICE(&s->clk), "timer-clock"));
+
+ sysbus_realize(sbd, &error_abort);
+ sysbus_mmio_map(sbd, 0, npcm8xx_tim_addr[i]);
+
+ first_irq = NPCM8XX_TIMER0_IRQ + i * NPCM7XX_TIMERS_PER_CTRL;
+ for (j = 0; j < NPCM7XX_TIMERS_PER_CTRL; j++) {
+ qemu_irq irq = npcm8xx_irq(s, first_irq + j);
+ sysbus_connect_irq(sbd, j, irq);
+ }
+
+ /* IRQ for watchdogs */
+ sysbus_connect_irq(sbd, NPCM7XX_TIMERS_PER_CTRL,
+ npcm8xx_irq(s, NPCM8XX_WDG0_IRQ + i));
+ /* GPIO that connects clk module with watchdog */
+ qdev_connect_gpio_out_named(DEVICE(&s->tim[i]),
+ NPCM7XX_WATCHDOG_RESET_GPIO_OUT, 0,
+ qdev_get_gpio_in_named(DEVICE(&s->clk),
+ NPCM7XX_WATCHDOG_RESET_GPIO_IN, i));
+ }
+
+ /* UART0..6 (16550 compatible) */
+ for (i = 0; i < ARRAY_SIZE(npcm8xx_uart_addr); i++) {
+ serial_mm_init(get_system_memory(), npcm8xx_uart_addr[i], 2,
+ npcm8xx_irq(s, NPCM8XX_UART0_IRQ + i), 115200,
+ serial_hd(i), DEVICE_LITTLE_ENDIAN);
+ }
+
+ /* Random Number Generator. Cannot fail. */
+ sysbus_realize(SYS_BUS_DEVICE(&s->rng), &error_abort);
+ sysbus_mmio_map(SYS_BUS_DEVICE(&s->rng), 0, NPCM8XX_RNG_BA);
+
+ /* GPIO modules. Cannot fail. */
+ QEMU_BUILD_BUG_ON(ARRAY_SIZE(npcm8xx_gpio) != ARRAY_SIZE(s->gpio));
+ for (i = 0; i < ARRAY_SIZE(s->gpio); i++) {
+ Object *obj = OBJECT(&s->gpio[i]);
+
+ object_property_set_uint(obj, "reset-pullup",
+ npcm8xx_gpio[i].reset_pu, &error_abort);
+ object_property_set_uint(obj, "reset-pulldown",
+ npcm8xx_gpio[i].reset_pd, &error_abort);
+ object_property_set_uint(obj, "reset-osrc",
+ npcm8xx_gpio[i].reset_osrc, &error_abort);
+ object_property_set_uint(obj, "reset-odsc",
+ npcm8xx_gpio[i].reset_odsc, &error_abort);
+ sysbus_realize(SYS_BUS_DEVICE(obj), &error_abort);
+ sysbus_mmio_map(SYS_BUS_DEVICE(obj), 0, npcm8xx_gpio[i].regs_addr);
+ sysbus_connect_irq(SYS_BUS_DEVICE(obj), 0,
+ npcm8xx_irq(s, NPCM8XX_GPIO0_IRQ + i));
+ }
+
+ /* SMBus modules. Cannot fail. */
+ QEMU_BUILD_BUG_ON(ARRAY_SIZE(npcm8xx_smbus_addr) != ARRAY_SIZE(s->smbus));
+ for (i = 0; i < ARRAY_SIZE(s->smbus); i++) {
+ Object *obj = OBJECT(&s->smbus[i]);
+
+ sysbus_realize(SYS_BUS_DEVICE(obj), &error_abort);
+ sysbus_mmio_map(SYS_BUS_DEVICE(obj), 0, npcm8xx_smbus_addr[i]);
+ sysbus_connect_irq(SYS_BUS_DEVICE(obj), 0,
+ npcm8xx_irq(s, NPCM8XX_SMBUS0_IRQ + i));
+ }
+
+ /* USB Host */
+ QEMU_BUILD_BUG_ON(ARRAY_SIZE(s->ohci) != ARRAY_SIZE(s->ehci));
+ for (i = 0; i < ARRAY_SIZE(s->ehci); i++) {
+ object_property_set_bool(OBJECT(&s->ehci[i]), "companion-enable", true,
+ &error_abort);
+ sysbus_realize(SYS_BUS_DEVICE(&s->ehci[i]), &error_abort);
+ sysbus_mmio_map(SYS_BUS_DEVICE(&s->ehci[i]), 0, npcm8xx_ehci_addr[i]);
+ sysbus_connect_irq(SYS_BUS_DEVICE(&s->ehci[i]), 0,
+ npcm8xx_irq(s, NPCM8XX_EHCI1_IRQ + 2 * i));
+ }
+ for (i = 0; i < ARRAY_SIZE(s->ohci); i++) {
+ object_property_set_str(OBJECT(&s->ohci[i]), "masterbus", "usb-bus.0",
+ &error_abort);
+ object_property_set_uint(OBJECT(&s->ohci[i]), "num-ports", 1,
+ &error_abort);
+ object_property_set_uint(OBJECT(&s->ohci[i]), "firstport", i,
+ &error_abort);
+ sysbus_realize(SYS_BUS_DEVICE(&s->ohci[i]), &error_abort);
+ sysbus_mmio_map(SYS_BUS_DEVICE(&s->ohci[i]), 0, npcm8xx_ohci_addr[i]);
+ sysbus_connect_irq(SYS_BUS_DEVICE(&s->ohci[i]), 0,
+ npcm8xx_irq(s, NPCM8XX_OHCI1_IRQ + 2 * i));
+ }
+
+ /* PWM Modules. Cannot fail. */
+ QEMU_BUILD_BUG_ON(ARRAY_SIZE(npcm8xx_pwm_addr) != ARRAY_SIZE(s->pwm));
+ for (i = 0; i < ARRAY_SIZE(s->pwm); i++) {
+ SysBusDevice *sbd = SYS_BUS_DEVICE(&s->pwm[i]);
+
+ qdev_connect_clock_in(DEVICE(&s->pwm[i]), "clock", qdev_get_clock_out(
+ DEVICE(&s->clk), "apb3-clock"));
+ sysbus_realize(sbd, &error_abort);
+ sysbus_mmio_map(sbd, 0, npcm8xx_pwm_addr[i]);
+ sysbus_connect_irq(sbd, i, npcm8xx_irq(s, NPCM8XX_PWM0_IRQ + i));
+ }
+
+ /* MFT Modules. Cannot fail. */
+ QEMU_BUILD_BUG_ON(ARRAY_SIZE(npcm8xx_mft_addr) != ARRAY_SIZE(s->mft));
+ for (i = 0; i < ARRAY_SIZE(s->mft); i++) {
+ SysBusDevice *sbd = SYS_BUS_DEVICE(&s->mft[i]);
+
+ qdev_connect_clock_in(DEVICE(&s->mft[i]), "clock-in",
+ qdev_get_clock_out(DEVICE(&s->clk),
+ "apb4-clock"));
+ sysbus_realize(sbd, &error_abort);
+ sysbus_mmio_map(sbd, 0, npcm8xx_mft_addr[i]);
+ sysbus_connect_irq(sbd, 0, npcm8xx_irq(s, NPCM8XX_MFT0_IRQ + i));
+ }
+
+ /*
+ * 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(npcm8xx_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",
+ npcm8xx_fiu[i].cs_count, &error_abort);
+ object_property_set_int(OBJECT(sbd), "flash-size",
+ npcm8xx_fiu[i].flash_size, &error_abort);
+ sysbus_realize(sbd, &error_abort);
+
+ sysbus_mmio_map(sbd, 0, npcm8xx_fiu[i].regs_addr);
+ for (j = 0; j < npcm8xx_fiu[i].cs_count; j++) {
+ sysbus_mmio_map(sbd, j + 1, npcm8xx_fiu[i].flash_addr[j]);
+ }
+ }
+
+ /* RAM2 (SRAM) */
+ memory_region_init_ram(&s->sram, OBJECT(dev), "ram2",
+ NPCM8XX_RAM2_SZ, &error_abort);
+ memory_region_add_subregion(get_system_memory(), NPCM8XX_RAM2_BA, &s->sram);
+
+ /* RAM3 (SRAM) */
+ memory_region_init_ram(&s->ram3, OBJECT(dev), "ram3",
+ NPCM8XX_RAM3_SZ, &error_abort);
+ memory_region_add_subregion(get_system_memory(), NPCM8XX_RAM3_BA, &s->ram3);
+
+ /* Internal ROM */
+ memory_region_init_rom(&s->irom, OBJECT(dev), "irom", NPCM8XX_ROM_SZ,
+ &error_abort);
+ memory_region_add_subregion(get_system_memory(), NPCM8XX_ROM_BA, &s->irom);
+
+ /* SDHCI */
+ sysbus_realize(SYS_BUS_DEVICE(&s->mmc), &error_abort);
+ sysbus_mmio_map(SYS_BUS_DEVICE(&s->mmc), 0, NPCM8XX_MMC_BA);
+ sysbus_connect_irq(SYS_BUS_DEVICE(&s->mmc), 0,
+ npcm8xx_irq(s, NPCM8XX_MMC_IRQ));
+
+
+ create_unimplemented_device("npcm8xx.shm", 0xc0001000, 4 * KiB);
+ create_unimplemented_device("npcm8xx.gicextra", 0xdfffa000, 24 * KiB);
+ create_unimplemented_device("npcm8xx.vdmx", 0xe0800000, 4 * KiB);
+ create_unimplemented_device("npcm8xx.pcierc", 0xe1000000, 64 * KiB);
+ create_unimplemented_device("npcm8xx.rootc", 0xe8000000, 128 * MiB);
+ create_unimplemented_device("npcm8xx.kcs", 0xf0007000, 4 * KiB);
+ create_unimplemented_device("npcm8xx.gfxi", 0xf000e000, 4 * KiB);
+ create_unimplemented_device("npcm8xx.fsw", 0xf000f000, 4 * KiB);
+ create_unimplemented_device("npcm8xx.bt", 0xf0030000, 4 * KiB);
+ create_unimplemented_device("npcm8xx.espi", 0xf009f000, 4 * KiB);
+ create_unimplemented_device("npcm8xx.peci", 0xf0100000, 4 * KiB);
+ create_unimplemented_device("npcm8xx.siox[1]", 0xf0101000, 4 * KiB);
+ create_unimplemented_device("npcm8xx.siox[2]", 0xf0102000, 4 * KiB);
+ create_unimplemented_device("npcm8xx.tmps", 0xf0188000, 4 * KiB);
+ create_unimplemented_device("npcm8xx.pspi", 0xf0201000, 4 * KiB);
+ create_unimplemented_device("npcm8xx.viru1", 0xf0204000, 4 * KiB);
+ create_unimplemented_device("npcm8xx.viru2", 0xf0205000, 4 * KiB);
+ create_unimplemented_device("npcm8xx.jtm1", 0xf0208000, 4 * KiB);
+ create_unimplemented_device("npcm8xx.jtm2", 0xf0209000, 4 * KiB);
+ create_unimplemented_device("npcm8xx.flm0", 0xf0210000, 4 * KiB);
+ create_unimplemented_device("npcm8xx.flm1", 0xf0211000, 4 * KiB);
+ create_unimplemented_device("npcm8xx.flm2", 0xf0212000, 4 * KiB);
+ create_unimplemented_device("npcm8xx.flm3", 0xf0213000, 4 * KiB);
+ create_unimplemented_device("npcm8xx.ahbpci", 0xf0400000, 1 * MiB);
+ create_unimplemented_device("npcm8xx.dap", 0xf0500000, 960 * KiB);
+ create_unimplemented_device("npcm8xx.mcphy", 0xf05f0000, 64 * KiB);
+ create_unimplemented_device("npcm8xx.pcs", 0xf0780000, 256 * KiB);
+ create_unimplemented_device("npcm8xx.tsgen", 0xf07fc000, 8 * KiB);
+ create_unimplemented_device("npcm8xx.gmac1", 0xf0802000, 8 * KiB);
+ create_unimplemented_device("npcm8xx.gmac2", 0xf0804000, 8 * KiB);
+ create_unimplemented_device("npcm8xx.gmac3", 0xf0806000, 8 * KiB);
+ create_unimplemented_device("npcm8xx.gmac4", 0xf0808000, 8 * KiB);
+ create_unimplemented_device("npcm8xx.copctl", 0xf080c000, 4 * KiB);
+ create_unimplemented_device("npcm8xx.tipctl", 0xf080d000, 4 * KiB);
+ create_unimplemented_device("npcm8xx.rst", 0xf080e000, 4 * KiB);
+ create_unimplemented_device("npcm8xx.vcd", 0xf0810000, 64 * KiB);
+ create_unimplemented_device("npcm8xx.ece", 0xf0820000, 8 * KiB);
+ create_unimplemented_device("npcm8xx.vdma", 0xf0822000, 8 * KiB);
+ create_unimplemented_device("npcm8xx.usbd[0]", 0xf0830000, 4 * KiB);
+ create_unimplemented_device("npcm8xx.usbd[1]", 0xf0831000, 4 * KiB);
+ create_unimplemented_device("npcm8xx.usbd[2]", 0xf0832000, 4 * KiB);
+ create_unimplemented_device("npcm8xx.usbd[3]", 0xf0833000, 4 * KiB);
+ create_unimplemented_device("npcm8xx.usbd[4]", 0xf0834000, 4 * KiB);
+ create_unimplemented_device("npcm8xx.usbd[5]", 0xf0835000, 4 * KiB);
+ create_unimplemented_device("npcm8xx.usbd[6]", 0xf0836000, 4 * KiB);
+ create_unimplemented_device("npcm8xx.usbd[7]", 0xf0837000, 4 * KiB);
+ create_unimplemented_device("npcm8xx.usbd[8]", 0xf0838000, 4 * KiB);
+ create_unimplemented_device("npcm8xx.usbd[9]", 0xf0839000, 4 * KiB);
+ create_unimplemented_device("npcm8xx.pci_mbox1", 0xf0848000, 64 * KiB);
+ create_unimplemented_device("npcm8xx.gdma0", 0xf0850000, 4 * KiB);
+ create_unimplemented_device("npcm8xx.gdma1", 0xf0851000, 4 * KiB);
+ create_unimplemented_device("npcm8xx.gdma2", 0xf0852000, 4 * KiB);
+ create_unimplemented_device("npcm8xx.aes", 0xf0858000, 4 * KiB);
+ create_unimplemented_device("npcm8xx.des", 0xf0859000, 4 * KiB);
+ create_unimplemented_device("npcm8xx.sha", 0xf085a000, 4 * KiB);
+ create_unimplemented_device("npcm8xx.pci_mbox2", 0xf0868000, 64 * KiB);
+ create_unimplemented_device("npcm8xx.i3c0", 0xfff10000, 4 * KiB);
+ create_unimplemented_device("npcm8xx.i3c1", 0xfff11000, 4 * KiB);
+ create_unimplemented_device("npcm8xx.i3c2", 0xfff12000, 4 * KiB);
+ create_unimplemented_device("npcm8xx.i3c3", 0xfff13000, 4 * KiB);
+ create_unimplemented_device("npcm8xx.i3c4", 0xfff14000, 4 * KiB);
+ create_unimplemented_device("npcm8xx.i3c5", 0xfff15000, 4 * KiB);
+ create_unimplemented_device("npcm8xx.spixcs0", 0xf8000000, 16 * MiB);
+ create_unimplemented_device("npcm8xx.spixcs1", 0xf9000000, 16 * MiB);
+ create_unimplemented_device("npcm8xx.spix", 0xfb001000, 4 * KiB);
+ create_unimplemented_device("npcm8xx.vect", 0xffff0000, 256);
+}
+
+static const Property npcm8xx_properties[] = {
+ DEFINE_PROP_LINK("dram-mr", NPCM8xxState, dram, TYPE_MEMORY_REGION,
+ MemoryRegion *),
+};
+
+static void npcm8xx_class_init(ObjectClass *oc, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(oc);
+ NPCM8xxClass *nc = NPCM8XX_CLASS(oc);
+
+ dc->realize = npcm8xx_realize;
+ dc->user_creatable = false;
+ nc->disabled_modules = 0x00000000;
+ nc->num_cpus = NPCM8XX_MAX_NUM_CPUS;
+ device_class_set_props(dc, npcm8xx_properties);
+}
+
+static const TypeInfo npcm8xx_soc_types[] = {
+ {
+ .name = TYPE_NPCM8XX,
+ .parent = TYPE_DEVICE,
+ .instance_size = sizeof(NPCM8xxState),
+ .instance_init = npcm8xx_init,
+ .class_size = sizeof(NPCM8xxClass),
+ .class_init = npcm8xx_class_init,
+ },
+};
+
+DEFINE_TYPES(npcm8xx_soc_types);
diff --git a/hw/arm/npcm8xx_boards.c b/hw/arm/npcm8xx_boards.c
new file mode 100644
index 0000000..3fb8478
--- /dev/null
+++ b/hw/arm/npcm8xx_boards.c
@@ -0,0 +1,254 @@
+/*
+ * Machine definitions for boards featuring an NPCM8xx SoC.
+ *
+ * Copyright 2021 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 "chardev/char.h"
+#include "hw/boards.h"
+#include "hw/arm/npcm8xx.h"
+#include "hw/core/cpu.h"
+#include "hw/loader.h"
+#include "hw/qdev-core.h"
+#include "hw/qdev-properties.h"
+#include "qapi/error.h"
+#include "qemu/error-report.h"
+#include "qemu/datadir.h"
+#include "qemu/units.h"
+
+#define NPCM845_EVB_POWER_ON_STRAPS 0x000017ff
+
+static const char npcm8xx_default_bootrom[] = "npcm8xx_bootrom.bin";
+
+static void npcm8xx_load_bootrom(MachineState *machine, NPCM8xxState *soc)
+{
+ const char *bios_name = machine->firmware ?: npcm8xx_default_bootrom;
+ g_autofree char *filename = NULL;
+ int ret;
+
+ 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, machine->ram);
+ if (ret < 0) {
+ error_report("Failed to load ROM image '%s'", filename);
+ exit(1);
+ }
+}
+
+static void npcm8xx_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 npcm8xx_connect_dram(NPCM8xxState *soc, MemoryRegion *dram)
+{
+ memory_region_add_subregion(get_system_memory(), NPCM8XX_DRAM_BA, dram);
+
+ object_property_set_link(OBJECT(soc), "dram-mr", OBJECT(dram),
+ &error_abort);
+}
+
+static NPCM8xxState *npcm8xx_create_soc(MachineState *machine,
+ uint32_t hw_straps)
+{
+ NPCM8xxMachineClass *nmc = NPCM8XX_MACHINE_GET_CLASS(machine);
+ Object *obj;
+
+ 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 NPCM8XX(obj);
+}
+
+static I2CBus *npcm8xx_i2c_get_bus(NPCM8xxState *soc, uint32_t num)
+{
+ g_assert(num < ARRAY_SIZE(soc->smbus));
+ return I2C_BUS(qdev_get_child_bus(DEVICE(&soc->smbus[num]), "i2c-bus"));
+}
+
+static void npcm8xx_init_pwm_splitter(NPCM8xxMachine *machine,
+ NPCM8xxState *soc, const int *fan_counts)
+{
+ SplitIRQ *splitters = machine->fan_splitter;
+
+ /*
+ * PWM 0~3 belong to module 0 output 0~3.
+ * PWM 4~7 belong to module 1 output 0~3.
+ */
+ for (int i = 0; i < NPCM8XX_NR_PWM_MODULES; ++i) {
+ for (int j = 0; j < NPCM7XX_PWM_PER_MODULE; ++j) {
+ int splitter_no = i * NPCM7XX_PWM_PER_MODULE + j;
+ DeviceState *splitter;
+
+ if (fan_counts[splitter_no] < 1) {
+ continue;
+ }
+ object_initialize_child(OBJECT(machine), "fan-splitter[*]",
+ &splitters[splitter_no], TYPE_SPLIT_IRQ);
+ splitter = DEVICE(&splitters[splitter_no]);
+ qdev_prop_set_uint16(splitter, "num-lines",
+ fan_counts[splitter_no]);
+ qdev_realize(splitter, NULL, &error_abort);
+ qdev_connect_gpio_out_named(DEVICE(&soc->pwm[i]), "duty-gpio-out",
+ j, qdev_get_gpio_in(splitter, 0));
+ }
+ }
+}
+
+static void npcm8xx_connect_pwm_fan(NPCM8xxState *soc, SplitIRQ *splitter,
+ int fan_no, int output_no)
+{
+ DeviceState *fan;
+ int fan_input;
+ qemu_irq fan_duty_gpio;
+
+ g_assert(fan_no >= 0 && fan_no <= NPCM7XX_MFT_MAX_FAN_INPUT);
+ /*
+ * Fan 0~1 belong to module 0 input 0~1.
+ * Fan 2~3 belong to module 1 input 0~1.
+ * ...
+ * Fan 14~15 belong to module 7 input 0~1.
+ * Fan 16~17 belong to module 0 input 2~3.
+ * Fan 18~19 belong to module 1 input 2~3.
+ */
+ if (fan_no < 16) {
+ fan = DEVICE(&soc->mft[fan_no / 2]);
+ fan_input = fan_no % 2;
+ } else {
+ fan = DEVICE(&soc->mft[(fan_no - 16) / 2]);
+ fan_input = fan_no % 2 + 2;
+ }
+
+ /* Connect the Fan to PWM module */
+ fan_duty_gpio = qdev_get_gpio_in_named(fan, "duty", fan_input);
+ qdev_connect_gpio_out(DEVICE(splitter), output_no, fan_duty_gpio);
+}
+
+static void npcm845_evb_i2c_init(NPCM8xxState *soc)
+{
+ /* tmp100 temperature sensor on SVB, tmp105 is compatible */
+ i2c_slave_create_simple(npcm8xx_i2c_get_bus(soc, 6), "tmp105", 0x48);
+}
+
+static void npcm845_evb_fan_init(NPCM8xxMachine *machine, NPCM8xxState *soc)
+{
+ SplitIRQ *splitter = machine->fan_splitter;
+ static const int fan_counts[] = {2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0};
+
+ npcm8xx_init_pwm_splitter(machine, soc, fan_counts);
+ npcm8xx_connect_pwm_fan(soc, &splitter[0], 0x00, 0);
+ npcm8xx_connect_pwm_fan(soc, &splitter[0], 0x01, 1);
+ npcm8xx_connect_pwm_fan(soc, &splitter[1], 0x02, 0);
+ npcm8xx_connect_pwm_fan(soc, &splitter[1], 0x03, 1);
+ npcm8xx_connect_pwm_fan(soc, &splitter[2], 0x04, 0);
+ npcm8xx_connect_pwm_fan(soc, &splitter[2], 0x05, 1);
+ npcm8xx_connect_pwm_fan(soc, &splitter[3], 0x06, 0);
+ npcm8xx_connect_pwm_fan(soc, &splitter[3], 0x07, 1);
+ npcm8xx_connect_pwm_fan(soc, &splitter[4], 0x08, 0);
+ npcm8xx_connect_pwm_fan(soc, &splitter[4], 0x09, 1);
+ npcm8xx_connect_pwm_fan(soc, &splitter[5], 0x0a, 0);
+ npcm8xx_connect_pwm_fan(soc, &splitter[5], 0x0b, 1);
+ npcm8xx_connect_pwm_fan(soc, &splitter[6], 0x0c, 0);
+ npcm8xx_connect_pwm_fan(soc, &splitter[6], 0x0d, 1);
+ npcm8xx_connect_pwm_fan(soc, &splitter[7], 0x0e, 0);
+ npcm8xx_connect_pwm_fan(soc, &splitter[7], 0x0f, 1);
+}
+
+static void npcm845_evb_init(MachineState *machine)
+{
+ NPCM8xxState *soc;
+
+ soc = npcm8xx_create_soc(machine, NPCM845_EVB_POWER_ON_STRAPS);
+ npcm8xx_connect_dram(soc, machine->ram);
+ qdev_realize(DEVICE(soc), NULL, &error_fatal);
+
+ npcm8xx_load_bootrom(machine, soc);
+ npcm8xx_connect_flash(&soc->fiu[0], 0, "w25q256", drive_get(IF_MTD, 0, 0));
+ npcm845_evb_i2c_init(soc);
+ npcm845_evb_fan_init(NPCM8XX_MACHINE(machine), soc);
+ npcm8xx_load_kernel(machine, soc);
+}
+
+static void npcm8xx_set_soc_type(NPCM8xxMachineClass *nmc, const char *type)
+{
+ NPCM8xxClass *sc = NPCM8XX_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 npcm8xx_machine_class_init(ObjectClass *oc, void *data)
+{
+ MachineClass *mc = MACHINE_CLASS(oc);
+ static const char * const valid_cpu_types[] = {
+ ARM_CPU_TYPE_NAME("cortex-a9"),
+ NULL
+ };
+
+ mc->no_floppy = 1;
+ mc->no_cdrom = 1;
+ mc->no_parallel = 1;
+ mc->default_ram_id = "ram";
+ mc->valid_cpu_types = valid_cpu_types;
+}
+
+static void npcm845_evb_machine_class_init(ObjectClass *oc, void *data)
+{
+ NPCM8xxMachineClass *nmc = NPCM8XX_MACHINE_CLASS(oc);
+ MachineClass *mc = MACHINE_CLASS(oc);
+
+ npcm8xx_set_soc_type(nmc, TYPE_NPCM8XX);
+
+ mc->desc = "Nuvoton NPCM845 Evaluation Board (Cortex-A35)";
+ mc->init = npcm845_evb_init;
+ mc->default_ram_size = 1 * GiB;
+};
+
+static const TypeInfo npcm8xx_machine_types[] = {
+ {
+ .name = TYPE_NPCM8XX_MACHINE,
+ .parent = TYPE_MACHINE,
+ .instance_size = sizeof(NPCM8xxMachine),
+ .class_size = sizeof(NPCM8xxMachineClass),
+ .class_init = npcm8xx_machine_class_init,
+ .abstract = true,
+ }, {
+ .name = MACHINE_TYPE_NAME("npcm845-evb"),
+ .parent = TYPE_NPCM8XX_MACHINE,
+ .class_init = npcm845_evb_machine_class_init,
+ },
+};
+
+DEFINE_TYPES(npcm8xx_machine_types)
diff --git a/hw/arm/realview.c b/hw/arm/realview.c
index 436eef8..008eeaf 100644
--- a/hw/arm/realview.c
+++ b/hw/arm/realview.c
@@ -35,6 +35,8 @@
#define SMP_BOOT_ADDR 0xe0000000
#define SMP_BOOTREG_ADDR 0x10000030
+#define GIC_EXT_IRQS 64 /* Realview PBX-A9 development board */
+
/* Board init. */
static struct arm_boot_info realview_binfo = {
@@ -185,7 +187,12 @@ static void realview_init(MachineState *machine,
sysbus_mmio_map(SYS_BUS_DEVICE(sysctl), 0, 0x10000000);
if (is_mpcore) {
- dev = qdev_new(is_pb ? TYPE_A9MPCORE_PRIV : "realview_mpcore");
+ if (is_pb) {
+ dev = qdev_new(TYPE_A9MPCORE_PRIV);
+ qdev_prop_set_uint32(dev, "num-irq", GIC_EXT_IRQS + GIC_INTERNAL);
+ } else {
+ dev = qdev_new("realview_mpcore");
+ }
qdev_prop_set_uint32(dev, "num-cpu", smp_cpus);
busdev = SYS_BUS_DEVICE(dev);
sysbus_realize_and_unref(busdev, &error_fatal);
@@ -201,7 +208,7 @@ static void realview_init(MachineState *machine,
/* For now just create the nIRQ GIC, and ignore the others. */
dev = sysbus_create_simple(TYPE_REALVIEW_GIC, gic_addr, cpu_irq[0]);
}
- for (n = 0; n < 64; n++) {
+ for (n = 0; n < GIC_EXT_IRQS; n++) {
pic[n] = qdev_get_gpio_in(dev, n);
}
diff --git a/hw/arm/vexpress.c b/hw/arm/vexpress.c
index 48e18a4..76c6107 100644
--- a/hw/arm/vexpress.c
+++ b/hw/arm/vexpress.c
@@ -51,6 +51,8 @@
#define VEXPRESS_FLASH_SIZE (64 * 1024 * 1024)
#define VEXPRESS_FLASH_SECT_SIZE (256 * 1024)
+#define GIC_EXT_IRQS 64 /* Versatile Express A9 development board */
+
/* Number of virtio transports to create (0..8; limited by
* number of available IRQ lines).
*/
@@ -241,6 +243,7 @@ static void init_cpus(MachineState *ms, const char *cpu_type,
*/
dev = qdev_new(privdev);
qdev_prop_set_uint32(dev, "num-cpu", smp_cpus);
+ qdev_prop_set_uint32(dev, "num-irq", GIC_EXT_IRQS + GIC_INTERNAL);
busdev = SYS_BUS_DEVICE(dev);
sysbus_realize_and_unref(busdev, &error_fatal);
sysbus_mmio_map(busdev, 0, periphbase);
@@ -251,7 +254,7 @@ static void init_cpus(MachineState *ms, const char *cpu_type,
* external interrupts starting from 32 (because there
* are internal interrupts 0..31).
*/
- for (n = 0; n < 64; n++) {
+ for (n = 0; n < GIC_EXT_IRQS; n++) {
pic[n] = qdev_get_gpio_in(dev, n);
}
@@ -543,7 +546,7 @@ static void vexpress_common_init(MachineState *machine)
VexpressMachineClass *vmc = VEXPRESS_MACHINE_GET_CLASS(machine);
VEDBoardInfo *daughterboard = vmc->daughterboard;
DeviceState *dev, *sysctl, *pl041;
- qemu_irq pic[64];
+ qemu_irq pic[GIC_EXT_IRQS];
uint32_t sys_id;
DriveInfo *dinfo;
PFlashCFI01 *pflash0;
diff --git a/hw/arm/xilinx_zynq.c b/hw/arm/xilinx_zynq.c
index 3c6a460..b891666 100644
--- a/hw/arm/xilinx_zynq.c
+++ b/hw/arm/xilinx_zynq.c
@@ -54,11 +54,11 @@ OBJECT_DECLARE_SIMPLE_TYPE(ZynqMachineState, ZYNQ_MACHINE)
#define FLASH_SIZE (64 * 1024 * 1024)
#define FLASH_SECTOR_SIZE (128 * 1024)
-#define IRQ_OFFSET 32 /* pic interrupts start from index 32 */
-
#define MPCORE_PERIPHBASE 0xF8F00000
#define ZYNQ_BOARD_MIDR 0x413FC090
+#define GIC_EXT_IRQS 64 /* Zynq 7000 SoC */
+
static const int dma_irqs[8] = {
46, 47, 48, 49, 72, 73, 74, 75
};
@@ -207,7 +207,7 @@ static void zynq_init(MachineState *machine)
MemoryRegion *ocm_ram = g_new(MemoryRegion, 1);
DeviceState *dev, *slcr;
SysBusDevice *busdev;
- qemu_irq pic[64];
+ qemu_irq pic[GIC_EXT_IRQS];
int n;
unsigned int smp_cpus = machine->smp.cpus;
@@ -263,6 +263,7 @@ static void zynq_init(MachineState *machine)
dev = qdev_new(TYPE_A9MPCORE_PRIV);
qdev_prop_set_uint32(dev, "num-cpu", smp_cpus);
+ qdev_prop_set_uint32(dev, "num-irq", GIC_EXT_IRQS + GIC_INTERNAL);
busdev = SYS_BUS_DEVICE(dev);
sysbus_realize_and_unref(busdev, &error_fatal);
sysbus_mmio_map(busdev, 0, MPCORE_PERIPHBASE);
@@ -277,16 +278,16 @@ static void zynq_init(MachineState *machine)
qdev_get_gpio_in(cpudev, ARM_CPU_FIQ));
}
- for (n = 0; n < 64; n++) {
+ for (n = 0; n < GIC_EXT_IRQS; n++) {
pic[n] = qdev_get_gpio_in(dev, n);
}
- n = zynq_init_spi_flashes(0xE0006000, pic[58 - IRQ_OFFSET], false, 0);
- n = zynq_init_spi_flashes(0xE0007000, pic[81 - IRQ_OFFSET], false, n);
- n = zynq_init_spi_flashes(0xE000D000, pic[51 - IRQ_OFFSET], true, n);
+ n = zynq_init_spi_flashes(0xE0006000, pic[58 - GIC_INTERNAL], false, 0);
+ n = zynq_init_spi_flashes(0xE0007000, pic[81 - GIC_INTERNAL], false, n);
+ n = zynq_init_spi_flashes(0xE000D000, pic[51 - GIC_INTERNAL], true, n);
- sysbus_create_simple(TYPE_CHIPIDEA, 0xE0002000, pic[53 - IRQ_OFFSET]);
- sysbus_create_simple(TYPE_CHIPIDEA, 0xE0003000, pic[76 - IRQ_OFFSET]);
+ sysbus_create_simple(TYPE_CHIPIDEA, 0xE0002000, pic[53 - GIC_INTERNAL]);
+ sysbus_create_simple(TYPE_CHIPIDEA, 0xE0003000, pic[76 - GIC_INTERNAL]);
dev = qdev_new(TYPE_CADENCE_UART);
busdev = SYS_BUS_DEVICE(dev);
@@ -295,7 +296,7 @@ static void zynq_init(MachineState *machine)
qdev_get_clock_out(slcr, "uart0_ref_clk"));
sysbus_realize_and_unref(busdev, &error_fatal);
sysbus_mmio_map(busdev, 0, 0xE0000000);
- sysbus_connect_irq(busdev, 0, pic[59 - IRQ_OFFSET]);
+ sysbus_connect_irq(busdev, 0, pic[59 - GIC_INTERNAL]);
dev = qdev_new(TYPE_CADENCE_UART);
busdev = SYS_BUS_DEVICE(dev);
qdev_prop_set_chr(dev, "chardev", serial_hd(1));
@@ -303,15 +304,15 @@ static void zynq_init(MachineState *machine)
qdev_get_clock_out(slcr, "uart1_ref_clk"));
sysbus_realize_and_unref(busdev, &error_fatal);
sysbus_mmio_map(busdev, 0, 0xE0001000);
- sysbus_connect_irq(busdev, 0, pic[82 - IRQ_OFFSET]);
+ sysbus_connect_irq(busdev, 0, pic[82 - GIC_INTERNAL]);
sysbus_create_varargs("cadence_ttc", 0xF8001000,
- pic[42-IRQ_OFFSET], pic[43-IRQ_OFFSET], pic[44-IRQ_OFFSET], NULL);
+ pic[42-GIC_INTERNAL], pic[43-GIC_INTERNAL], pic[44-GIC_INTERNAL], NULL);
sysbus_create_varargs("cadence_ttc", 0xF8002000,
- pic[69-IRQ_OFFSET], pic[70-IRQ_OFFSET], pic[71-IRQ_OFFSET], NULL);
+ pic[69-GIC_INTERNAL], pic[70-GIC_INTERNAL], pic[71-GIC_INTERNAL], NULL);
- gem_init(0xE000B000, pic[54 - IRQ_OFFSET]);
- gem_init(0xE000C000, pic[77 - IRQ_OFFSET]);
+ gem_init(0xE000B000, pic[54 - GIC_INTERNAL]);
+ gem_init(0xE000C000, pic[77 - GIC_INTERNAL]);
for (n = 0; n < 2; n++) {
int hci_irq = n ? 79 : 56;
@@ -330,7 +331,7 @@ static void zynq_init(MachineState *machine)
qdev_prop_set_uint64(dev, "capareg", ZYNQ_SDHCI_CAPABILITIES);
sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal);
sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, hci_addr);
- sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, pic[hci_irq - IRQ_OFFSET]);
+ sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, pic[hci_irq - GIC_INTERNAL]);
di = drive_get(IF_SD, 0, n);
blk = di ? blk_by_legacy_dinfo(di) : NULL;
@@ -343,7 +344,7 @@ static void zynq_init(MachineState *machine)
dev = qdev_new(TYPE_ZYNQ_XADC);
sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal);
sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, 0xF8007100);
- sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, pic[39-IRQ_OFFSET]);
+ sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, pic[39-GIC_INTERNAL]);
dev = qdev_new("pl330");
object_property_set_link(OBJECT(dev), "memory",
@@ -363,15 +364,15 @@ static void zynq_init(MachineState *machine)
busdev = SYS_BUS_DEVICE(dev);
sysbus_realize_and_unref(busdev, &error_fatal);
sysbus_mmio_map(busdev, 0, 0xF8003000);
- sysbus_connect_irq(busdev, 0, pic[45-IRQ_OFFSET]); /* abort irq line */
+ sysbus_connect_irq(busdev, 0, pic[45-GIC_INTERNAL]); /* abort irq line */
for (n = 0; n < ARRAY_SIZE(dma_irqs); ++n) { /* event irqs */
- sysbus_connect_irq(busdev, n + 1, pic[dma_irqs[n] - IRQ_OFFSET]);
+ sysbus_connect_irq(busdev, n + 1, pic[dma_irqs[n] - GIC_INTERNAL]);
}
dev = qdev_new("xlnx.ps7-dev-cfg");
busdev = SYS_BUS_DEVICE(dev);
sysbus_realize_and_unref(busdev, &error_fatal);
- sysbus_connect_irq(busdev, 0, pic[40 - IRQ_OFFSET]);
+ sysbus_connect_irq(busdev, 0, pic[40 - GIC_INTERNAL]);
sysbus_mmio_map(busdev, 0, 0xF8007000);
/*
@@ -460,7 +461,7 @@ static void zynq_machine_class_init(ObjectClass *oc, void *data)
};
MachineClass *mc = MACHINE_CLASS(oc);
ObjectProperty *prop;
- mc->desc = "Xilinx Zynq Platform Baseboard for Cortex-A9";
+ mc->desc = "Xilinx Zynq 7000 Platform Baseboard for Cortex-A9";
mc->init = zynq_init;
mc->max_cpus = ZYNQ_MAX_CPUS;
mc->ignore_memory_transaction_failures = true;
diff --git a/hw/cpu/a15mpcore.c b/hw/cpu/a15mpcore.c
index d24ab0a..676f65a 100644
--- a/hw/cpu/a15mpcore.c
+++ b/hw/cpu/a15mpcore.c
@@ -58,6 +58,11 @@ static void a15mp_priv_realize(DeviceState *dev, Error **errp)
bool has_el2 = false;
Object *cpuobj;
+ if (s->num_irq < 32 || s->num_irq > 256) {
+ error_setg(errp, "Property 'num-irq' must be between 32 and 256");
+ return;
+ }
+
gicdev = DEVICE(&s->gic);
qdev_prop_set_uint32(gicdev, "num-cpu", s->num_cpu);
qdev_prop_set_uint32(gicdev, "num-irq", s->num_irq);
@@ -146,13 +151,14 @@ static void a15mp_priv_realize(DeviceState *dev, Error **errp)
static const Property a15mp_priv_properties[] = {
DEFINE_PROP_UINT32("num-cpu", A15MPPrivState, num_cpu, 1),
- /* The Cortex-A15MP may have anything from 0 to 224 external interrupt
- * IRQ lines (with another 32 internal). We default to 128+32, which
- * is the number provided by the Cortex-A15MP test chip in the
- * Versatile Express A15 development board.
- * Other boards may differ and should set this property appropriately.
+ /*
+ * The Cortex-A15MP may have anything from 0 to 224 external interrupt
+ * lines, plus always 32 internal IRQs. This property sets the total
+ * of internal + external, so the valid range is from 32 to 256.
+ * The board model must set this to whatever the configuration
+ * used for the CPU on that board or SoC is.
*/
- DEFINE_PROP_UINT32("num-irq", A15MPPrivState, num_irq, 160),
+ DEFINE_PROP_UINT32("num-irq", A15MPPrivState, num_irq, 0),
};
static void a15mp_priv_class_init(ObjectClass *klass, void *data)
diff --git a/hw/cpu/a9mpcore.c b/hw/cpu/a9mpcore.c
index 25416c5..1b9f2be 100644
--- a/hw/cpu/a9mpcore.c
+++ b/hw/cpu/a9mpcore.c
@@ -56,6 +56,11 @@ static void a9mp_priv_realize(DeviceState *dev, Error **errp)
CPUState *cpu0;
Object *cpuobj;
+ if (s->num_irq < 32 || s->num_irq > 256) {
+ error_setg(errp, "Property 'num-irq' must be between 32 and 256");
+ return;
+ }
+
cpu0 = qemu_get_cpu(0);
cpuobj = OBJECT(cpu0);
if (strcmp(object_get_typename(cpuobj), ARM_CPU_TYPE_NAME("cortex-a9"))) {
@@ -160,13 +165,14 @@ static void a9mp_priv_realize(DeviceState *dev, Error **errp)
static const Property a9mp_priv_properties[] = {
DEFINE_PROP_UINT32("num-cpu", A9MPPrivState, num_cpu, 1),
- /* The Cortex-A9MP may have anything from 0 to 224 external interrupt
- * IRQ lines (with another 32 internal). We default to 64+32, which
- * is the number provided by the Cortex-A9MP test chip in the
- * Realview PBX-A9 and Versatile Express A9 development boards.
- * Other boards may differ and should set this property appropriately.
+ /*
+ * The Cortex-A9MP may have anything from 0 to 224 external interrupt
+ * lines, plus always 32 internal IRQs. This property sets the total
+ * of internal + external, so the valid range is from 32 to 256.
+ * The board model must set this to whatever the configuration
+ * used for the CPU on that board or SoC is.
*/
- DEFINE_PROP_UINT32("num-irq", A9MPPrivState, num_irq, 96),
+ DEFINE_PROP_UINT32("num-irq", A9MPPrivState, num_irq, 0),
};
static void a9mp_priv_class_init(ObjectClass *klass, void *data)
diff --git a/hw/intc/arm_gicv3_cpuif.c b/hw/intc/arm_gicv3_cpuif.c
index 9cad831..7f1d071 100644
--- a/hw/intc/arm_gicv3_cpuif.c
+++ b/hw/intc/arm_gicv3_cpuif.c
@@ -2291,7 +2291,7 @@ static CPAccessResult gicv3_irqfiq_access(CPUARMState *env,
r = CP_ACCESS_TRAP_EL3;
break;
case 3:
- if (!is_a64(env) && !arm_is_el3_or_mon(env)) {
+ if (!arm_is_el3_or_mon(env)) {
r = CP_ACCESS_TRAP_EL3;
}
break;
@@ -2300,9 +2300,6 @@ static CPAccessResult gicv3_irqfiq_access(CPUARMState *env,
}
}
- if (r == CP_ACCESS_TRAP_EL3 && !arm_el_is_aa64(env, 3)) {
- r = CP_ACCESS_TRAP;
- }
return r;
}
@@ -2356,7 +2353,7 @@ static CPAccessResult gicv3_fiq_access(CPUARMState *env,
r = CP_ACCESS_TRAP_EL3;
break;
case 3:
- if (!is_a64(env) && !arm_is_el3_or_mon(env)) {
+ if (!arm_is_el3_or_mon(env)) {
r = CP_ACCESS_TRAP_EL3;
}
break;
@@ -2365,9 +2362,6 @@ static CPAccessResult gicv3_fiq_access(CPUARMState *env,
}
}
- if (r == CP_ACCESS_TRAP_EL3 && !arm_el_is_aa64(env, 3)) {
- r = CP_ACCESS_TRAP;
- }
return r;
}
@@ -2395,7 +2389,7 @@ static CPAccessResult gicv3_irq_access(CPUARMState *env,
r = CP_ACCESS_TRAP_EL3;
break;
case 3:
- if (!is_a64(env) && !arm_is_el3_or_mon(env)) {
+ if (!arm_is_el3_or_mon(env)) {
r = CP_ACCESS_TRAP_EL3;
}
break;
@@ -2404,9 +2398,6 @@ static CPAccessResult gicv3_irq_access(CPUARMState *env,
}
}
- if (r == CP_ACCESS_TRAP_EL3 && !arm_el_is_aa64(env, 3)) {
- r = CP_ACCESS_TRAP;
- }
return r;
}
diff --git a/hw/misc/meson.build b/hw/misc/meson.build
index 55f4935..edd36a3 100644
--- a/hw/misc/meson.build
+++ b/hw/misc/meson.build
@@ -69,8 +69,8 @@ system_ss.add(when: 'CONFIG_IMX', if_true: files(
'imx_rngc.c',
))
system_ss.add(when: 'CONFIG_NPCM7XX', if_true: files(
- 'npcm7xx_clk.c',
- 'npcm7xx_gcr.c',
+ 'npcm_clk.c',
+ 'npcm_gcr.c',
'npcm7xx_mft.c',
'npcm7xx_pwm.c',
'npcm7xx_rng.c',
diff --git a/hw/misc/npcm7xx_gcr.c b/hw/misc/npcm7xx_gcr.c
deleted file mode 100644
index 07464a4..0000000
--- a/hw/misc/npcm7xx_gcr.c
+++ /dev/null
@@ -1,264 +0,0 @@
-/*
- * 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));
-
- 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;
-}
-
-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(SYS_BUS_DEVICE(s), &s->iomem);
-}
-
-static const VMStateDescription vmstate_npcm7xx_gcr = {
- .name = "npcm7xx-gcr",
- .version_id = 0,
- .minimum_version_id = 0,
- .fields = (const VMStateField[]) {
- VMSTATE_UINT32_ARRAY(regs, NPCM7xxGCRState, NPCM7XX_GCR_NR_REGS),
- VMSTATE_END_OF_LIST(),
- },
-};
-
-static const Property npcm7xx_gcr_properties[] = {
- DEFINE_PROP_UINT32("disabled-modules", NPCM7xxGCRState, reset_mdlr, 0),
- DEFINE_PROP_UINT32("power-on-straps", NPCM7xxGCRState, reset_pwron, 0),
-};
-
-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/npcm7xx_clk.c b/hw/misc/npcm_clk.c
index 46f907b..d1f2975 100644
--- a/hw/misc/npcm7xx_clk.c
+++ b/hw/misc/npcm_clk.c
@@ -1,5 +1,5 @@
/*
- * Nuvoton NPCM7xx Clock Control Registers.
+ * Nuvoton NPCM7xx/8xx Clock Control Registers.
*
* Copyright 2020 Google LLC
*
@@ -16,7 +16,7 @@
#include "qemu/osdep.h"
-#include "hw/misc/npcm7xx_clk.h"
+#include "hw/misc/npcm_clk.h"
#include "hw/timer/npcm7xx_timer.h"
#include "hw/qdev-clock.h"
#include "migration/vmstate.h"
@@ -72,7 +72,57 @@ enum NPCM7xxCLKRegisters {
NPCM7XX_CLK_AHBCKFI,
NPCM7XX_CLK_SECCNT,
NPCM7XX_CLK_CNTR25M,
- NPCM7XX_CLK_REGS_END,
+};
+
+enum NPCM8xxCLKRegisters {
+ NPCM8XX_CLK_CLKEN1,
+ NPCM8XX_CLK_CLKSEL,
+ NPCM8XX_CLK_CLKDIV1,
+ NPCM8XX_CLK_PLLCON0,
+ NPCM8XX_CLK_PLLCON1,
+ NPCM8XX_CLK_SWRSTR,
+ NPCM8XX_CLK_IPSRST1 = 0x20 / sizeof(uint32_t),
+ NPCM8XX_CLK_IPSRST2,
+ NPCM8XX_CLK_CLKEN2,
+ NPCM8XX_CLK_CLKDIV2,
+ NPCM8XX_CLK_CLKEN3,
+ NPCM8XX_CLK_IPSRST3,
+ NPCM8XX_CLK_WD0RCR,
+ NPCM8XX_CLK_WD1RCR,
+ NPCM8XX_CLK_WD2RCR,
+ NPCM8XX_CLK_SWRSTC1,
+ NPCM8XX_CLK_SWRSTC2,
+ NPCM8XX_CLK_SWRSTC3,
+ NPCM8XX_CLK_TIPRSTC,
+ NPCM8XX_CLK_PLLCON2,
+ NPCM8XX_CLK_CLKDIV3,
+ NPCM8XX_CLK_CORSTC,
+ NPCM8XX_CLK_PLLCONG,
+ NPCM8XX_CLK_AHBCKFI,
+ NPCM8XX_CLK_SECCNT,
+ NPCM8XX_CLK_CNTR25M,
+ /* Registers unique to NPCM8XX SoC */
+ NPCM8XX_CLK_CLKEN4,
+ NPCM8XX_CLK_IPSRST4,
+ NPCM8XX_CLK_BUSTO,
+ NPCM8XX_CLK_CLKDIV4,
+ NPCM8XX_CLK_WD0RCRB,
+ NPCM8XX_CLK_WD1RCRB,
+ NPCM8XX_CLK_WD2RCRB,
+ NPCM8XX_CLK_SWRSTC1B,
+ NPCM8XX_CLK_SWRSTC2B,
+ NPCM8XX_CLK_SWRSTC3B,
+ NPCM8XX_CLK_TIPRSTCB,
+ NPCM8XX_CLK_CORSTCB,
+ NPCM8XX_CLK_IPSRSTDIS1,
+ NPCM8XX_CLK_IPSRSTDIS2,
+ NPCM8XX_CLK_IPSRSTDIS3,
+ NPCM8XX_CLK_IPSRSTDIS4,
+ NPCM8XX_CLK_CLKENDIS1,
+ NPCM8XX_CLK_CLKENDIS2,
+ NPCM8XX_CLK_CLKENDIS3,
+ NPCM8XX_CLK_CLKENDIS4,
+ NPCM8XX_CLK_THRTL_CNT,
};
/*
@@ -81,7 +131,7 @@ enum NPCM7xxCLKRegisters {
* 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] = {
+static const uint32_t npcm7xx_cold_reset_values[NPCM7XX_CLK_NR_REGS] = {
[NPCM7XX_CLK_CLKEN1] = 0xffffffff,
[NPCM7XX_CLK_CLKSEL] = 0x004aaaaa,
[NPCM7XX_CLK_CLKDIV1] = 0x5413f855,
@@ -103,6 +153,46 @@ static const uint32_t cold_reset_values[NPCM7XX_CLK_NR_REGS] = {
[NPCM7XX_CLK_AHBCKFI] = 0x000000c8,
};
+/*
+ * These reset values were taken from version 0.92 of the NPCM8xx data sheet.
+ */
+static const uint32_t npcm8xx_cold_reset_values[NPCM8XX_CLK_NR_REGS] = {
+ [NPCM8XX_CLK_CLKEN1] = 0xffffffff,
+ [NPCM8XX_CLK_CLKSEL] = 0x154aaaaa,
+ [NPCM8XX_CLK_CLKDIV1] = 0x5413f855,
+ [NPCM8XX_CLK_PLLCON0] = 0x00222101 | PLLCON_LOKI,
+ [NPCM8XX_CLK_PLLCON1] = 0x00202101 | PLLCON_LOKI,
+ [NPCM8XX_CLK_IPSRST1] = 0x00001000,
+ [NPCM8XX_CLK_IPSRST2] = 0x80000000,
+ [NPCM8XX_CLK_CLKEN2] = 0xffffffff,
+ [NPCM8XX_CLK_CLKDIV2] = 0xaa4f8f9f,
+ [NPCM8XX_CLK_CLKEN3] = 0xffffffff,
+ [NPCM8XX_CLK_IPSRST3] = 0x03000000,
+ [NPCM8XX_CLK_WD0RCR] = 0xffffffff,
+ [NPCM8XX_CLK_WD1RCR] = 0xffffffff,
+ [NPCM8XX_CLK_WD2RCR] = 0xffffffff,
+ [NPCM8XX_CLK_SWRSTC1] = 0x00000003,
+ [NPCM8XX_CLK_SWRSTC2] = 0x00000001,
+ [NPCM8XX_CLK_SWRSTC3] = 0x00000001,
+ [NPCM8XX_CLK_TIPRSTC] = 0x00000001,
+ [NPCM8XX_CLK_PLLCON2] = 0x00c02105 | PLLCON_LOKI,
+ [NPCM8XX_CLK_CLKDIV3] = 0x00009100,
+ [NPCM8XX_CLK_CORSTC] = 0x04000003,
+ [NPCM8XX_CLK_PLLCONG] = 0x01228606 | PLLCON_LOKI,
+ [NPCM8XX_CLK_AHBCKFI] = 0x000000c8,
+ [NPCM8XX_CLK_CLKEN4] = 0xffffffff,
+ [NPCM8XX_CLK_CLKDIV4] = 0x70009000,
+ [NPCM8XX_CLK_IPSRST4] = 0x02000000,
+ [NPCM8XX_CLK_WD0RCRB] = 0xfffffe71,
+ [NPCM8XX_CLK_WD1RCRB] = 0xfffffe71,
+ [NPCM8XX_CLK_WD2RCRB] = 0xfffffe71,
+ [NPCM8XX_CLK_SWRSTC1B] = 0xfffffe71,
+ [NPCM8XX_CLK_SWRSTC2B] = 0xfffffe71,
+ [NPCM8XX_CLK_SWRSTC3B] = 0xfffffe71,
+ [NPCM8XX_CLK_TIPRSTCB] = 0xfffffe71,
+ [NPCM8XX_CLK_CORSTCB] = 0xfffffe71,
+};
+
/* The number of watchdogs that can trigger a reset. */
#define NPCM7XX_NR_WATCHDOGS (3)
@@ -198,7 +288,7 @@ static NPCM7xxClockPLL find_pll_by_reg(enum NPCM7xxCLKRegisters reg)
}
}
-static void npcm7xx_clk_update_all_plls(NPCM7xxCLKState *clk)
+static void npcm7xx_clk_update_all_plls(NPCMCLKState *clk)
{
int i;
@@ -207,7 +297,7 @@ static void npcm7xx_clk_update_all_plls(NPCM7xxCLKState *clk)
}
}
-static void npcm7xx_clk_update_all_sels(NPCM7xxCLKState *clk)
+static void npcm7xx_clk_update_all_sels(NPCMCLKState *clk)
{
int i;
@@ -216,7 +306,7 @@ static void npcm7xx_clk_update_all_sels(NPCM7xxCLKState *clk)
}
}
-static void npcm7xx_clk_update_all_dividers(NPCM7xxCLKState *clk)
+static void npcm7xx_clk_update_all_dividers(NPCMCLKState *clk)
{
int i;
@@ -225,7 +315,7 @@ static void npcm7xx_clk_update_all_dividers(NPCM7xxCLKState *clk)
}
}
-static void npcm7xx_clk_update_all_clocks(NPCM7xxCLKState *clk)
+static void npcm7xx_clk_update_all_clocks(NPCMCLKState *clk)
{
clock_update_hz(clk->clkref, NPCM7XX_CLOCK_REF_HZ);
npcm7xx_clk_update_all_plls(clk);
@@ -635,7 +725,7 @@ static void npcm7xx_clk_divider_init(Object *obj)
}
static void npcm7xx_init_clock_pll(NPCM7xxClockPLLState *pll,
- NPCM7xxCLKState *clk, const PLLInitInfo *init_info)
+ NPCMCLKState *clk, const PLLInitInfo *init_info)
{
pll->name = init_info->name;
pll->clk = clk;
@@ -647,7 +737,7 @@ static void npcm7xx_init_clock_pll(NPCM7xxClockPLLState *pll,
}
static void npcm7xx_init_clock_sel(NPCM7xxClockSELState *sel,
- NPCM7xxCLKState *clk, const SELInitInfo *init_info)
+ NPCMCLKState *clk, const SELInitInfo *init_info)
{
int input_size = init_info->input_size;
@@ -664,7 +754,7 @@ static void npcm7xx_init_clock_sel(NPCM7xxClockSELState *sel,
}
static void npcm7xx_init_clock_divider(NPCM7xxClockDividerState *div,
- NPCM7xxCLKState *clk, const DividerInitInfo *init_info)
+ NPCMCLKState *clk, const DividerInitInfo *init_info)
{
div->name = init_info->name;
div->clk = clk;
@@ -683,7 +773,7 @@ static void npcm7xx_init_clock_divider(NPCM7xxClockDividerState *div,
}
}
-static Clock *npcm7xx_get_clock(NPCM7xxCLKState *clk, ClockSrcType type,
+static Clock *npcm7xx_get_clock(NPCMCLKState *clk, ClockSrcType type,
int index)
{
switch (type) {
@@ -700,7 +790,7 @@ static Clock *npcm7xx_get_clock(NPCM7xxCLKState *clk, ClockSrcType type,
}
}
-static void npcm7xx_connect_clocks(NPCM7xxCLKState *clk)
+static void npcm7xx_connect_clocks(NPCMCLKState *clk)
{
int i, j;
Clock *src;
@@ -724,14 +814,15 @@ static void npcm7xx_connect_clocks(NPCM7xxCLKState *clk)
}
}
-static uint64_t npcm7xx_clk_read(void *opaque, hwaddr offset, unsigned size)
+static uint64_t npcm_clk_read(void *opaque, hwaddr offset, unsigned size)
{
uint32_t reg = offset / sizeof(uint32_t);
- NPCM7xxCLKState *s = opaque;
+ NPCMCLKState *s = opaque;
+ NPCMCLKClass *c = NPCM_CLK_GET_CLASS(s);
int64_t now_ns;
uint32_t value = 0;
- if (reg >= NPCM7XX_CLK_NR_REGS) {
+ if (reg >= c->nr_regs) {
qemu_log_mask(LOG_GUEST_ERROR,
"%s: offset 0x%04" HWADDR_PRIx " out of range\n",
__func__, offset);
@@ -766,21 +857,22 @@ static uint64_t npcm7xx_clk_read(void *opaque, hwaddr offset, unsigned size)
break;
};
- trace_npcm7xx_clk_read(offset, value);
+ trace_npcm_clk_read(offset, value);
return value;
}
-static void npcm7xx_clk_write(void *opaque, hwaddr offset,
+static void npcm_clk_write(void *opaque, hwaddr offset,
uint64_t v, unsigned size)
{
uint32_t reg = offset / sizeof(uint32_t);
- NPCM7xxCLKState *s = opaque;
+ NPCMCLKState *s = opaque;
+ NPCMCLKClass *c = NPCM_CLK_GET_CLASS(s);
uint32_t value = v;
- trace_npcm7xx_clk_write(offset, value);
+ trace_npcm_clk_write(offset, value);
- if (reg >= NPCM7XX_CLK_NR_REGS) {
+ if (reg >= c->nr_regs) {
qemu_log_mask(LOG_GUEST_ERROR,
"%s: offset 0x%04" HWADDR_PRIx " out of range\n",
__func__, offset);
@@ -842,7 +934,7 @@ static void npcm7xx_clk_write(void *opaque, hwaddr offset,
static void npcm7xx_clk_perform_watchdog_reset(void *opaque, int n,
int level)
{
- NPCM7xxCLKState *clk = NPCM7XX_CLK(opaque);
+ NPCMCLKState *clk = NPCM_CLK(opaque);
uint32_t rcr;
g_assert(n >= 0 && n <= NPCM7XX_NR_WATCHDOGS);
@@ -856,9 +948,9 @@ static void npcm7xx_clk_perform_watchdog_reset(void *opaque, int n,
}
}
-static const struct MemoryRegionOps npcm7xx_clk_ops = {
- .read = npcm7xx_clk_read,
- .write = npcm7xx_clk_write,
+static const struct MemoryRegionOps npcm_clk_ops = {
+ .read = npcm_clk_read,
+ .write = npcm_clk_write,
.endianness = DEVICE_LITTLE_ENDIAN,
.valid = {
.min_access_size = 4,
@@ -867,13 +959,13 @@ static const struct MemoryRegionOps npcm7xx_clk_ops = {
},
};
-static void npcm7xx_clk_enter_reset(Object *obj, ResetType type)
+static void npcm_clk_enter_reset(Object *obj, ResetType type)
{
- NPCM7xxCLKState *s = NPCM7XX_CLK(obj);
+ NPCMCLKState *s = NPCM_CLK(obj);
+ NPCMCLKClass *c = NPCM_CLK_GET_CLASS(s);
- QEMU_BUILD_BUG_ON(sizeof(s->regs) != sizeof(cold_reset_values));
-
- memcpy(s->regs, cold_reset_values, sizeof(cold_reset_values));
+ g_assert(sizeof(s->regs) >= c->nr_regs * sizeof(uint32_t));
+ memcpy(s->regs, c->cold_reset_values, sizeof(s->regs));
s->ref_ns = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
npcm7xx_clk_update_all_clocks(s);
/*
@@ -882,7 +974,7 @@ static void npcm7xx_clk_enter_reset(Object *obj, ResetType type)
*/
}
-static void npcm7xx_clk_init_clock_hierarchy(NPCM7xxCLKState *s)
+static void npcm7xx_clk_init_clock_hierarchy(NPCMCLKState *s)
{
int i;
@@ -918,19 +1010,19 @@ static void npcm7xx_clk_init_clock_hierarchy(NPCM7xxCLKState *s)
clock_update_hz(s->clkref, NPCM7XX_CLOCK_REF_HZ);
}
-static void npcm7xx_clk_init(Object *obj)
+static void npcm_clk_init(Object *obj)
{
- NPCM7xxCLKState *s = NPCM7XX_CLK(obj);
+ NPCMCLKState *s = NPCM_CLK(obj);
- memory_region_init_io(&s->iomem, obj, &npcm7xx_clk_ops, s,
- TYPE_NPCM7XX_CLK, 4 * KiB);
+ memory_region_init_io(&s->iomem, obj, &npcm_clk_ops, s,
+ TYPE_NPCM_CLK, 4 * KiB);
sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->iomem);
}
-static int npcm7xx_clk_post_load(void *opaque, int version_id)
+static int npcm_clk_post_load(void *opaque, int version_id)
{
if (version_id >= 1) {
- NPCM7xxCLKState *clk = opaque;
+ NPCMCLKState *clk = opaque;
npcm7xx_clk_update_all_clocks(clk);
}
@@ -938,10 +1030,10 @@ static int npcm7xx_clk_post_load(void *opaque, int version_id)
return 0;
}
-static void npcm7xx_clk_realize(DeviceState *dev, Error **errp)
+static void npcm_clk_realize(DeviceState *dev, Error **errp)
{
int i;
- NPCM7xxCLKState *s = NPCM7XX_CLK(dev);
+ NPCMCLKState *s = NPCM_CLK(dev);
qdev_init_gpio_in_named(DEVICE(s), npcm7xx_clk_perform_watchdog_reset,
NPCM7XX_WATCHDOG_RESET_GPIO_IN, NPCM7XX_NR_WATCHDOGS);
@@ -996,15 +1088,15 @@ static const VMStateDescription vmstate_npcm7xx_clk_divider = {
},
};
-static const VMStateDescription vmstate_npcm7xx_clk = {
- .name = "npcm7xx-clk",
- .version_id = 1,
- .minimum_version_id = 1,
- .post_load = npcm7xx_clk_post_load,
+static const VMStateDescription vmstate_npcm_clk = {
+ .name = "npcm-clk",
+ .version_id = 3,
+ .minimum_version_id = 3,
+ .post_load = npcm_clk_post_load,
.fields = (const VMStateField[]) {
- VMSTATE_UINT32_ARRAY(regs, NPCM7xxCLKState, NPCM7XX_CLK_NR_REGS),
- VMSTATE_INT64(ref_ns, NPCM7xxCLKState),
- VMSTATE_CLOCK(clkref, NPCM7xxCLKState),
+ VMSTATE_UINT32_ARRAY(regs, NPCMCLKState, NPCM_CLK_MAX_NR_REGS),
+ VMSTATE_INT64(ref_ns, NPCMCLKState),
+ VMSTATE_CLOCK(clkref, NPCMCLKState),
VMSTATE_END_OF_LIST(),
},
};
@@ -1033,17 +1125,34 @@ static void npcm7xx_clk_divider_class_init(ObjectClass *klass, void *data)
dc->vmsd = &vmstate_npcm7xx_clk_divider;
}
-static void npcm7xx_clk_class_init(ObjectClass *klass, void *data)
+static void npcm_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->vmsd = &vmstate_npcm_clk;
+ dc->realize = npcm_clk_realize;
+ rc->phases.enter = npcm_clk_enter_reset;
+}
+
+static void npcm7xx_clk_class_init(ObjectClass *klass, void *data)
+{
+ NPCMCLKClass *c = NPCM_CLK_CLASS(klass);
+ DeviceClass *dc = DEVICE_CLASS(klass);
dc->desc = "NPCM7xx Clock Control Registers";
- dc->vmsd = &vmstate_npcm7xx_clk;
- dc->realize = npcm7xx_clk_realize;
- rc->phases.enter = npcm7xx_clk_enter_reset;
+ c->nr_regs = NPCM7XX_CLK_NR_REGS;
+ c->cold_reset_values = npcm7xx_cold_reset_values;
+}
+
+static void npcm8xx_clk_class_init(ObjectClass *klass, void *data)
+{
+ NPCMCLKClass *c = NPCM_CLK_CLASS(klass);
+ DeviceClass *dc = DEVICE_CLASS(klass);
+
+ dc->desc = "NPCM8xx Clock Control Registers";
+ c->nr_regs = NPCM8XX_CLK_NR_REGS;
+ c->cold_reset_values = npcm8xx_cold_reset_values;
}
static const TypeInfo npcm7xx_clk_pll_info = {
@@ -1070,19 +1179,35 @@ static const TypeInfo npcm7xx_clk_divider_info = {
.class_init = npcm7xx_clk_divider_class_init,
};
+static const TypeInfo npcm_clk_info = {
+ .name = TYPE_NPCM_CLK,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(NPCMCLKState),
+ .instance_init = npcm_clk_init,
+ .class_size = sizeof(NPCMCLKClass),
+ .class_init = npcm_clk_class_init,
+ .abstract = true,
+};
+
static const TypeInfo npcm7xx_clk_info = {
.name = TYPE_NPCM7XX_CLK,
- .parent = TYPE_SYS_BUS_DEVICE,
- .instance_size = sizeof(NPCM7xxCLKState),
- .instance_init = npcm7xx_clk_init,
+ .parent = TYPE_NPCM_CLK,
.class_init = npcm7xx_clk_class_init,
};
+static const TypeInfo npcm8xx_clk_info = {
+ .name = TYPE_NPCM8XX_CLK,
+ .parent = TYPE_NPCM_CLK,
+ .class_init = npcm8xx_clk_class_init,
+};
+
static void npcm7xx_clk_register_type(void)
{
type_register_static(&npcm7xx_clk_pll_info);
type_register_static(&npcm7xx_clk_sel_info);
type_register_static(&npcm7xx_clk_divider_info);
+ type_register_static(&npcm_clk_info);
type_register_static(&npcm7xx_clk_info);
+ type_register_static(&npcm8xx_clk_info);
}
type_init(npcm7xx_clk_register_type);
diff --git a/hw/misc/npcm_gcr.c b/hw/misc/npcm_gcr.c
new file mode 100644
index 0000000..4e8ce2c
--- /dev/null
+++ b/hw/misc/npcm_gcr.c
@@ -0,0 +1,482 @@
+/*
+ * Nuvoton NPCM7xx/8xx 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/npcm_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,
+};
+
+static const uint32_t npcm7xx_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,
+};
+
+enum NPCM8xxGCRRegisters {
+ NPCM8XX_GCR_PDID,
+ NPCM8XX_GCR_PWRON,
+ NPCM8XX_GCR_MISCPE = 0x014 / sizeof(uint32_t),
+ NPCM8XX_GCR_FLOCKR2 = 0x020 / sizeof(uint32_t),
+ NPCM8XX_GCR_FLOCKR3,
+ NPCM8XX_GCR_A35_MODE = 0x034 / sizeof(uint32_t),
+ NPCM8XX_GCR_SPSWC,
+ NPCM8XX_GCR_INTCR,
+ NPCM8XX_GCR_INTSR,
+ NPCM8XX_GCR_HIFCR = 0x050 / sizeof(uint32_t),
+ NPCM8XX_GCR_INTCR2 = 0x060 / sizeof(uint32_t),
+ NPCM8XX_GCR_SRCNT = 0x068 / sizeof(uint32_t),
+ NPCM8XX_GCR_RESSR,
+ NPCM8XX_GCR_RLOCKR1,
+ NPCM8XX_GCR_FLOCKR1,
+ NPCM8XX_GCR_DSCNT,
+ NPCM8XX_GCR_MDLR,
+ NPCM8XX_GCR_SCRPAD_C = 0x080 / sizeof(uint32_t),
+ NPCM8XX_GCR_SCRPAD_B,
+ NPCM8XX_GCR_DAVCLVLR = 0x098 / sizeof(uint32_t),
+ NPCM8XX_GCR_INTCR3,
+ NPCM8XX_GCR_PCIRCTL = 0x0a0 / sizeof(uint32_t),
+ NPCM8XX_GCR_VSINTR,
+ NPCM8XX_GCR_SD2SUR1 = 0x0b4 / sizeof(uint32_t),
+ NPCM8XX_GCR_SD2SUR2,
+ NPCM8XX_GCR_INTCR4 = 0x0c0 / sizeof(uint32_t),
+ NPCM8XX_GCR_CPCTL = 0x0d0 / sizeof(uint32_t),
+ NPCM8XX_GCR_CP2BST,
+ NPCM8XX_GCR_B2CPNT,
+ NPCM8XX_GCR_CPPCTL,
+ NPCM8XX_GCR_I2CSEGSEL = 0x0e0 / sizeof(uint32_t),
+ NPCM8XX_GCR_I2CSEGCTL,
+ NPCM8XX_GCR_VSRCR,
+ NPCM8XX_GCR_MLOCKR,
+ NPCM8XX_GCR_SCRPAD = 0x13c / sizeof(uint32_t),
+ NPCM8XX_GCR_USB1PHYCTL,
+ NPCM8XX_GCR_USB2PHYCTL,
+ NPCM8XX_GCR_USB3PHYCTL,
+ NPCM8XX_GCR_MFSEL1 = 0x260 / sizeof(uint32_t),
+ NPCM8XX_GCR_MFSEL2,
+ NPCM8XX_GCR_MFSEL3,
+ NPCM8XX_GCR_MFSEL4,
+ NPCM8XX_GCR_MFSEL5,
+ NPCM8XX_GCR_MFSEL6,
+ NPCM8XX_GCR_MFSEL7,
+ NPCM8XX_GCR_MFSEL_LK1 = 0x280 / sizeof(uint32_t),
+ NPCM8XX_GCR_MFSEL_LK2,
+ NPCM8XX_GCR_MFSEL_LK3,
+ NPCM8XX_GCR_MFSEL_LK4,
+ NPCM8XX_GCR_MFSEL_LK5,
+ NPCM8XX_GCR_MFSEL_LK6,
+ NPCM8XX_GCR_MFSEL_LK7,
+ NPCM8XX_GCR_MFSEL_SET1 = 0x2a0 / sizeof(uint32_t),
+ NPCM8XX_GCR_MFSEL_SET2,
+ NPCM8XX_GCR_MFSEL_SET3,
+ NPCM8XX_GCR_MFSEL_SET4,
+ NPCM8XX_GCR_MFSEL_SET5,
+ NPCM8XX_GCR_MFSEL_SET6,
+ NPCM8XX_GCR_MFSEL_SET7,
+ NPCM8XX_GCR_MFSEL_CLR1 = 0x2c0 / sizeof(uint32_t),
+ NPCM8XX_GCR_MFSEL_CLR2,
+ NPCM8XX_GCR_MFSEL_CLR3,
+ NPCM8XX_GCR_MFSEL_CLR4,
+ NPCM8XX_GCR_MFSEL_CLR5,
+ NPCM8XX_GCR_MFSEL_CLR6,
+ NPCM8XX_GCR_MFSEL_CLR7,
+ NPCM8XX_GCR_WD0RCRLK = 0x400 / sizeof(uint32_t),
+ NPCM8XX_GCR_WD1RCRLK,
+ NPCM8XX_GCR_WD2RCRLK,
+ NPCM8XX_GCR_SWRSTC1LK,
+ NPCM8XX_GCR_SWRSTC2LK,
+ NPCM8XX_GCR_SWRSTC3LK,
+ NPCM8XX_GCR_TIPRSTCLK,
+ NPCM8XX_GCR_CORSTCLK,
+ NPCM8XX_GCR_WD0RCRBLK,
+ NPCM8XX_GCR_WD1RCRBLK,
+ NPCM8XX_GCR_WD2RCRBLK,
+ NPCM8XX_GCR_SWRSTC1BLK,
+ NPCM8XX_GCR_SWRSTC2BLK,
+ NPCM8XX_GCR_SWRSTC3BLK,
+ NPCM8XX_GCR_TIPRSTCBLK,
+ NPCM8XX_GCR_CORSTCBLK,
+ /* 64 scratch pad registers start here. 0xe00 ~ 0xefc */
+ NPCM8XX_GCR_SCRPAD_00 = 0xe00 / sizeof(uint32_t),
+ /* 32 semaphore registers start here. 0xf00 ~ 0xf7c */
+ NPCM8XX_GCR_GP_SEMFR_00 = 0xf00 / sizeof(uint32_t),
+ NPCM8XX_GCR_GP_SEMFR_31 = 0xf7c / sizeof(uint32_t),
+};
+
+static const uint32_t npcm8xx_cold_reset_values[NPCM8XX_GCR_NR_REGS] = {
+ [NPCM8XX_GCR_PDID] = 0x04a35850, /* Arbel A1 */
+ [NPCM8XX_GCR_MISCPE] = 0x0000ffff,
+ [NPCM8XX_GCR_A35_MODE] = 0xfff4ff30,
+ [NPCM8XX_GCR_SPSWC] = 0x00000003,
+ [NPCM8XX_GCR_INTCR] = 0x0010035e,
+ [NPCM8XX_GCR_HIFCR] = 0x0000004e,
+ [NPCM8XX_GCR_SD2SUR1] = 0xfdc80000,
+ [NPCM8XX_GCR_SD2SUR2] = 0x5200b130,
+ [NPCM8XX_GCR_INTCR2] = (1U << 19), /* DDR initialized */
+ [NPCM8XX_GCR_RESSR] = 0x80000000,
+ [NPCM8XX_GCR_DAVCLVLR] = 0x5a00f3cf,
+ [NPCM8XX_GCR_INTCR3] = 0x5e001002,
+ [NPCM8XX_GCR_VSRCR] = 0x00004800,
+ [NPCM8XX_GCR_SCRPAD] = 0x00000008,
+ [NPCM8XX_GCR_USB1PHYCTL] = 0x034730e4,
+ [NPCM8XX_GCR_USB2PHYCTL] = 0x034730e4,
+ [NPCM8XX_GCR_USB3PHYCTL] = 0x034730e4,
+ /* All 32 semaphores should be initialized to 1. */
+ [NPCM8XX_GCR_GP_SEMFR_00...NPCM8XX_GCR_GP_SEMFR_31] = 0x00000001,
+};
+
+static uint64_t npcm_gcr_read(void *opaque, hwaddr offset, unsigned size)
+{
+ uint32_t reg = offset / sizeof(uint32_t);
+ NPCMGCRState *s = opaque;
+ NPCMGCRClass *c = NPCM_GCR_GET_CLASS(s);
+ uint64_t value;
+
+ if (reg >= c->nr_regs) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: offset 0x%04" HWADDR_PRIx " out of range\n",
+ __func__, offset);
+ return 0;
+ }
+
+ switch (size) {
+ case 4:
+ value = s->regs[reg];
+ break;
+
+ case 8:
+ g_assert(!(reg & 1));
+ value = deposit64(s->regs[reg], 32, 32, s->regs[reg + 1]);
+ break;
+
+ default:
+ g_assert_not_reached();
+ }
+
+ trace_npcm_gcr_read(offset, value);
+ return value;
+}
+
+static void npcm_gcr_write(void *opaque, hwaddr offset,
+ uint64_t v, unsigned size)
+{
+ uint32_t reg = offset / sizeof(uint32_t);
+ NPCMGCRState *s = opaque;
+ NPCMGCRClass *c = NPCM_GCR_GET_CLASS(s);
+ uint32_t value = v;
+
+ trace_npcm_gcr_write(offset, v);
+
+ if (reg >= c->nr_regs) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: offset 0x%04" HWADDR_PRIx " out of range\n",
+ __func__, offset);
+ return;
+ }
+
+ switch (size) {
+ case 4:
+ 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;
+ break;
+
+ case 8:
+ g_assert(!(reg & 1));
+ s->regs[reg] = value;
+ s->regs[reg + 1] = extract64(v, 32, 32);
+ break;
+
+ default:
+ g_assert_not_reached();
+ }
+}
+
+static bool npcm_gcr_check_mem_op(void *opaque, hwaddr offset,
+ unsigned size, bool is_write,
+ MemTxAttrs attrs)
+{
+ NPCMGCRClass *c = NPCM_GCR_GET_CLASS(opaque);
+
+ if (offset >= c->nr_regs * sizeof(uint32_t)) {
+ return false;
+ }
+
+ switch (size) {
+ case 4:
+ return true;
+ case 8:
+ if (offset >= NPCM8XX_GCR_SCRPAD_00 * sizeof(uint32_t) &&
+ offset < (NPCM8XX_GCR_NR_REGS - 1) * sizeof(uint32_t)) {
+ return true;
+ } else {
+ return false;
+ }
+ default:
+ return false;
+ }
+}
+
+static const struct MemoryRegionOps npcm_gcr_ops = {
+ .read = npcm_gcr_read,
+ .write = npcm_gcr_write,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+ .valid = {
+ .min_access_size = 4,
+ .max_access_size = 8,
+ .accepts = npcm_gcr_check_mem_op,
+ .unaligned = false,
+ },
+};
+
+static void npcm7xx_gcr_enter_reset(Object *obj, ResetType type)
+{
+ NPCMGCRState *s = NPCM_GCR(obj);
+ NPCMGCRClass *c = NPCM_GCR_GET_CLASS(obj);
+
+ g_assert(sizeof(s->regs) >= sizeof(c->cold_reset_values));
+ g_assert(sizeof(s->regs) >= c->nr_regs * sizeof(uint32_t));
+ memcpy(s->regs, c->cold_reset_values, c->nr_regs * sizeof(uint32_t));
+ /* These 3 registers are at the same location in both 7xx and 8xx. */
+ s->regs[NPCM7XX_GCR_PWRON] = s->reset_pwron;
+ s->regs[NPCM7XX_GCR_MDLR] = s->reset_mdlr;
+ s->regs[NPCM7XX_GCR_INTCR3] = s->reset_intcr3;
+}
+
+static void npcm8xx_gcr_enter_reset(Object *obj, ResetType type)
+{
+ NPCMGCRState *s = NPCM_GCR(obj);
+ NPCMGCRClass *c = NPCM_GCR_GET_CLASS(obj);
+
+ memcpy(s->regs, c->cold_reset_values, c->nr_regs * sizeof(uint32_t));
+ /* These 3 registers are at the same location in both 7xx and 8xx. */
+ s->regs[NPCM8XX_GCR_PWRON] = s->reset_pwron;
+ s->regs[NPCM8XX_GCR_MDLR] = s->reset_mdlr;
+ s->regs[NPCM8XX_GCR_INTCR3] = s->reset_intcr3;
+ s->regs[NPCM8XX_GCR_SCRPAD_B] = s->reset_scrpad_b;
+}
+
+static void npcm_gcr_realize(DeviceState *dev, Error **errp)
+{
+ ERRP_GUARD();
+ NPCMGCRState *s = NPCM_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;
+
+ /*
+ * The boot block starting from 0.0.6 for NPCM8xx SoCs stores the DRAM size
+ * in the SCRPAD2 registers. We need to set this field correctly since
+ * the initialization is skipped as we mentioned above.
+ * https://github.com/Nuvoton-Israel/u-boot/blob/npcm8mnx-v2019.01_tmp/board/nuvoton/arbel/arbel.c#L737
+ */
+ s->reset_scrpad_b = dram_size;
+}
+
+static void npcm_gcr_init(Object *obj)
+{
+ NPCMGCRState *s = NPCM_GCR(obj);
+
+ memory_region_init_io(&s->iomem, obj, &npcm_gcr_ops, s,
+ TYPE_NPCM_GCR, 4 * KiB);
+ sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->iomem);
+}
+
+static const VMStateDescription vmstate_npcm_gcr = {
+ .name = "npcm-gcr",
+ .version_id = 2,
+ .minimum_version_id = 2,
+ .fields = (const VMStateField[]) {
+ VMSTATE_UINT32_ARRAY(regs, NPCMGCRState, NPCM_GCR_MAX_NR_REGS),
+ VMSTATE_END_OF_LIST(),
+ },
+};
+
+static const Property npcm_gcr_properties[] = {
+ DEFINE_PROP_UINT32("disabled-modules", NPCMGCRState, reset_mdlr, 0),
+ DEFINE_PROP_UINT32("power-on-straps", NPCMGCRState, reset_pwron, 0),
+};
+
+static void npcm_gcr_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+
+ dc->realize = npcm_gcr_realize;
+ dc->vmsd = &vmstate_npcm_gcr;
+
+ device_class_set_props(dc, npcm_gcr_properties);
+}
+
+static void npcm7xx_gcr_class_init(ObjectClass *klass, void *data)
+{
+ NPCMGCRClass *c = NPCM_GCR_CLASS(klass);
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ ResettableClass *rc = RESETTABLE_CLASS(klass);
+
+ dc->desc = "NPCM7xx System Global Control Registers";
+ rc->phases.enter = npcm7xx_gcr_enter_reset;
+
+ c->nr_regs = NPCM7XX_GCR_NR_REGS;
+ c->cold_reset_values = npcm7xx_cold_reset_values;
+ rc->phases.enter = npcm7xx_gcr_enter_reset;
+}
+
+static void npcm8xx_gcr_class_init(ObjectClass *klass, void *data)
+{
+ NPCMGCRClass *c = NPCM_GCR_CLASS(klass);
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ ResettableClass *rc = RESETTABLE_CLASS(klass);
+
+ dc->desc = "NPCM8xx System Global Control Registers";
+ c->nr_regs = NPCM8XX_GCR_NR_REGS;
+ c->cold_reset_values = npcm8xx_cold_reset_values;
+ rc->phases.enter = npcm8xx_gcr_enter_reset;
+}
+
+static const TypeInfo npcm_gcr_info[] = {
+ {
+ .name = TYPE_NPCM_GCR,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(NPCMGCRState),
+ .instance_init = npcm_gcr_init,
+ .class_size = sizeof(NPCMGCRClass),
+ .class_init = npcm_gcr_class_init,
+ .abstract = true,
+ },
+ {
+ .name = TYPE_NPCM7XX_GCR,
+ .parent = TYPE_NPCM_GCR,
+ .class_init = npcm7xx_gcr_class_init,
+ },
+ {
+ .name = TYPE_NPCM8XX_GCR,
+ .parent = TYPE_NPCM_GCR,
+ .class_init = npcm8xx_gcr_class_init,
+ },
+};
+DEFINE_TYPES(npcm_gcr_info)
diff --git a/hw/misc/trace-events b/hw/misc/trace-events
index b35b0e7..4383808 100644
--- a/hw/misc/trace-events
+++ b/hw/misc/trace-events
@@ -130,13 +130,13 @@ mos6522_set_sr_int(void) "set sr_int"
mos6522_write(uint64_t addr, const char *name, uint64_t val) "reg=0x%"PRIx64 " [%s] val=0x%"PRIx64
mos6522_read(uint64_t addr, const char *name, unsigned val) "reg=0x%"PRIx64 " [%s] 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
+# npcm_clk.c
+npcm_clk_read(uint64_t offset, uint32_t value) " offset: 0x%04" PRIx64 " value: 0x%08" PRIx32
+npcm_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
+# npcm_gcr.c
+npcm_gcr_read(uint64_t offset, uint64_t value) " offset: 0x%04" PRIx64 " value: 0x%08" PRIx64
+npcm_gcr_write(uint64_t offset, uint64_t value) "offset: 0x%04" PRIx64 " value: 0x%08" PRIx64
# npcm7xx_mft.c
npcm7xx_mft_read(const char *name, uint64_t offset, uint16_t value) "%s: offset: 0x%04" PRIx64 " value: 0x%04" PRIx16
diff --git a/hw/net/meson.build b/hw/net/meson.build
index 3bb5d74..e6759e2 100644
--- a/hw/net/meson.build
+++ b/hw/net/meson.build
@@ -40,6 +40,7 @@ system_ss.add(when: 'CONFIG_SUNHME', if_true: files('sunhme.c'))
system_ss.add(when: 'CONFIG_FTGMAC100', if_true: files('ftgmac100.c'))
system_ss.add(when: 'CONFIG_SUNGEM', if_true: files('sungem.c'))
system_ss.add(when: 'CONFIG_NPCM7XX', if_true: files('npcm7xx_emc.c', 'npcm_gmac.c'))
+system_ss.add(when: 'CONFIG_NPCM8XX', if_true: files('npcm_pcs.c'))
system_ss.add(when: 'CONFIG_COLDFIRE', if_true: files('mcf_fec.c'))
specific_ss.add(when: 'CONFIG_PSERIES', if_true: files('spapr_llan.c'))
diff --git a/hw/net/npcm_pcs.c b/hw/net/npcm_pcs.c
new file mode 100644
index 0000000..ce5034e
--- /dev/null
+++ b/hw/net/npcm_pcs.c
@@ -0,0 +1,410 @@
+/*
+ * Nuvoton NPCM8xx PCS Module
+ *
+ * Copyright 2022 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.
+ */
+
+/*
+ * Disclaimer:
+ * Currently we only implemented the default values of the registers and
+ * the soft reset feature. These are required to boot up the GMAC module
+ * in Linux kernel for NPCM845 boards. Other functionalities are not modeled.
+ */
+
+#include "qemu/osdep.h"
+
+#include "exec/hwaddr.h"
+#include "hw/registerfields.h"
+#include "hw/net/npcm_pcs.h"
+#include "migration/vmstate.h"
+#include "qemu/log.h"
+#include "qemu/units.h"
+#include "trace.h"
+
+#define NPCM_PCS_IND_AC_BA 0x1fe
+#define NPCM_PCS_IND_SR_CTL 0x1e00
+#define NPCM_PCS_IND_SR_MII 0x1f00
+#define NPCM_PCS_IND_SR_TIM 0x1f07
+#define NPCM_PCS_IND_VR_MII 0x1f80
+
+REG16(NPCM_PCS_SR_CTL_ID1, 0x08)
+REG16(NPCM_PCS_SR_CTL_ID2, 0x0a)
+REG16(NPCM_PCS_SR_CTL_STS, 0x10)
+
+REG16(NPCM_PCS_SR_MII_CTRL, 0x00)
+REG16(NPCM_PCS_SR_MII_STS, 0x02)
+REG16(NPCM_PCS_SR_MII_DEV_ID1, 0x04)
+REG16(NPCM_PCS_SR_MII_DEV_ID2, 0x06)
+REG16(NPCM_PCS_SR_MII_AN_ADV, 0x08)
+REG16(NPCM_PCS_SR_MII_LP_BABL, 0x0a)
+REG16(NPCM_PCS_SR_MII_AN_EXPN, 0x0c)
+REG16(NPCM_PCS_SR_MII_EXT_STS, 0x1e)
+
+REG16(NPCM_PCS_SR_TIM_SYNC_ABL, 0x10)
+REG16(NPCM_PCS_SR_TIM_SYNC_TX_MAX_DLY_LWR, 0x12)
+REG16(NPCM_PCS_SR_TIM_SYNC_TX_MAX_DLY_UPR, 0x14)
+REG16(NPCM_PCS_SR_TIM_SYNC_TX_MIN_DLY_LWR, 0x16)
+REG16(NPCM_PCS_SR_TIM_SYNC_TX_MIN_DLY_UPR, 0x18)
+REG16(NPCM_PCS_SR_TIM_SYNC_RX_MAX_DLY_LWR, 0x1a)
+REG16(NPCM_PCS_SR_TIM_SYNC_RX_MAX_DLY_UPR, 0x1c)
+REG16(NPCM_PCS_SR_TIM_SYNC_RX_MIN_DLY_LWR, 0x1e)
+REG16(NPCM_PCS_SR_TIM_SYNC_RX_MIN_DLY_UPR, 0x20)
+
+REG16(NPCM_PCS_VR_MII_MMD_DIG_CTRL1, 0x000)
+REG16(NPCM_PCS_VR_MII_AN_CTRL, 0x002)
+REG16(NPCM_PCS_VR_MII_AN_INTR_STS, 0x004)
+REG16(NPCM_PCS_VR_MII_TC, 0x006)
+REG16(NPCM_PCS_VR_MII_DBG_CTRL, 0x00a)
+REG16(NPCM_PCS_VR_MII_EEE_MCTRL0, 0x00c)
+REG16(NPCM_PCS_VR_MII_EEE_TXTIMER, 0x010)
+REG16(NPCM_PCS_VR_MII_EEE_RXTIMER, 0x012)
+REG16(NPCM_PCS_VR_MII_LINK_TIMER_CTRL, 0x014)
+REG16(NPCM_PCS_VR_MII_EEE_MCTRL1, 0x016)
+REG16(NPCM_PCS_VR_MII_DIG_STS, 0x020)
+REG16(NPCM_PCS_VR_MII_ICG_ERRCNT1, 0x022)
+REG16(NPCM_PCS_VR_MII_MISC_STS, 0x030)
+REG16(NPCM_PCS_VR_MII_RX_LSTS, 0x040)
+REG16(NPCM_PCS_VR_MII_MP_TX_BSTCTRL0, 0x070)
+REG16(NPCM_PCS_VR_MII_MP_TX_LVLCTRL0, 0x074)
+REG16(NPCM_PCS_VR_MII_MP_TX_GENCTRL0, 0x07a)
+REG16(NPCM_PCS_VR_MII_MP_TX_GENCTRL1, 0x07c)
+REG16(NPCM_PCS_VR_MII_MP_TX_STS, 0x090)
+REG16(NPCM_PCS_VR_MII_MP_RX_GENCTRL0, 0x0b0)
+REG16(NPCM_PCS_VR_MII_MP_RX_GENCTRL1, 0x0b2)
+REG16(NPCM_PCS_VR_MII_MP_RX_LOS_CTRL0, 0x0ba)
+REG16(NPCM_PCS_VR_MII_MP_MPLL_CTRL0, 0x0f0)
+REG16(NPCM_PCS_VR_MII_MP_MPLL_CTRL1, 0x0f2)
+REG16(NPCM_PCS_VR_MII_MP_MPLL_STS, 0x110)
+REG16(NPCM_PCS_VR_MII_MP_MISC_CTRL2, 0x126)
+REG16(NPCM_PCS_VR_MII_MP_LVL_CTRL, 0x130)
+REG16(NPCM_PCS_VR_MII_MP_MISC_CTRL0, 0x132)
+REG16(NPCM_PCS_VR_MII_MP_MISC_CTRL1, 0x134)
+REG16(NPCM_PCS_VR_MII_DIG_CTRL2, 0x1c2)
+REG16(NPCM_PCS_VR_MII_DIG_ERRCNT_SEL, 0x1c4)
+
+/* Register Fields */
+#define NPCM_PCS_SR_MII_CTRL_RST BIT(15)
+
+static const uint16_t npcm_pcs_sr_ctl_cold_reset_values[NPCM_PCS_NR_SR_CTLS] = {
+ [R_NPCM_PCS_SR_CTL_ID1] = 0x699e,
+ [R_NPCM_PCS_SR_CTL_STS] = 0x8000,
+};
+
+static const uint16_t npcm_pcs_sr_mii_cold_reset_values[NPCM_PCS_NR_SR_MIIS] = {
+ [R_NPCM_PCS_SR_MII_CTRL] = 0x1140,
+ [R_NPCM_PCS_SR_MII_STS] = 0x0109,
+ [R_NPCM_PCS_SR_MII_DEV_ID1] = 0x699e,
+ [R_NPCM_PCS_SR_MII_DEV_ID2] = 0xced0,
+ [R_NPCM_PCS_SR_MII_AN_ADV] = 0x0020,
+ [R_NPCM_PCS_SR_MII_EXT_STS] = 0xc000,
+};
+
+static const uint16_t npcm_pcs_sr_tim_cold_reset_values[NPCM_PCS_NR_SR_TIMS] = {
+ [R_NPCM_PCS_SR_TIM_SYNC_ABL] = 0x0003,
+ [R_NPCM_PCS_SR_TIM_SYNC_TX_MAX_DLY_LWR] = 0x0038,
+ [R_NPCM_PCS_SR_TIM_SYNC_TX_MIN_DLY_LWR] = 0x0038,
+ [R_NPCM_PCS_SR_TIM_SYNC_RX_MAX_DLY_LWR] = 0x0058,
+ [R_NPCM_PCS_SR_TIM_SYNC_RX_MIN_DLY_LWR] = 0x0048,
+};
+
+static const uint16_t npcm_pcs_vr_mii_cold_reset_values[NPCM_PCS_NR_VR_MIIS] = {
+ [R_NPCM_PCS_VR_MII_MMD_DIG_CTRL1] = 0x2400,
+ [R_NPCM_PCS_VR_MII_AN_INTR_STS] = 0x000a,
+ [R_NPCM_PCS_VR_MII_EEE_MCTRL0] = 0x899c,
+ [R_NPCM_PCS_VR_MII_DIG_STS] = 0x0010,
+ [R_NPCM_PCS_VR_MII_MP_TX_BSTCTRL0] = 0x000a,
+ [R_NPCM_PCS_VR_MII_MP_TX_LVLCTRL0] = 0x007f,
+ [R_NPCM_PCS_VR_MII_MP_TX_GENCTRL0] = 0x0001,
+ [R_NPCM_PCS_VR_MII_MP_RX_GENCTRL0] = 0x0100,
+ [R_NPCM_PCS_VR_MII_MP_RX_GENCTRL1] = 0x1100,
+ [R_NPCM_PCS_VR_MII_MP_RX_LOS_CTRL0] = 0x000e,
+ [R_NPCM_PCS_VR_MII_MP_MPLL_CTRL0] = 0x0100,
+ [R_NPCM_PCS_VR_MII_MP_MPLL_CTRL1] = 0x0032,
+ [R_NPCM_PCS_VR_MII_MP_MPLL_STS] = 0x0001,
+ [R_NPCM_PCS_VR_MII_MP_LVL_CTRL] = 0x0019,
+};
+
+static void npcm_pcs_soft_reset(NPCMPCSState *s)
+{
+ memcpy(s->sr_ctl, npcm_pcs_sr_ctl_cold_reset_values,
+ NPCM_PCS_NR_SR_CTLS * sizeof(uint16_t));
+ memcpy(s->sr_mii, npcm_pcs_sr_mii_cold_reset_values,
+ NPCM_PCS_NR_SR_MIIS * sizeof(uint16_t));
+ memcpy(s->sr_tim, npcm_pcs_sr_tim_cold_reset_values,
+ NPCM_PCS_NR_SR_TIMS * sizeof(uint16_t));
+ memcpy(s->vr_mii, npcm_pcs_vr_mii_cold_reset_values,
+ NPCM_PCS_NR_VR_MIIS * sizeof(uint16_t));
+}
+
+static uint16_t npcm_pcs_read_sr_ctl(NPCMPCSState *s, hwaddr offset)
+{
+ hwaddr regno = offset / sizeof(uint16_t);
+
+ if (regno >= NPCM_PCS_NR_SR_CTLS) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: SR_CTL read offset 0x%04" HWADDR_PRIx
+ " is out of range.\n",
+ DEVICE(s)->canonical_path, offset);
+ return 0;
+ }
+
+ return s->sr_ctl[regno];
+}
+
+static uint16_t npcm_pcs_read_sr_mii(NPCMPCSState *s, hwaddr offset)
+{
+ hwaddr regno = offset / sizeof(uint16_t);
+
+ if (regno >= NPCM_PCS_NR_SR_MIIS) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: SR_MII read offset 0x%04" HWADDR_PRIx
+ " is out of range.\n",
+ DEVICE(s)->canonical_path, offset);
+ return 0;
+ }
+
+ return s->sr_mii[regno];
+}
+
+static uint16_t npcm_pcs_read_sr_tim(NPCMPCSState *s, hwaddr offset)
+{
+ hwaddr regno = offset / sizeof(uint16_t);
+
+ if (regno >= NPCM_PCS_NR_SR_TIMS) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: SR_TIM read offset 0x%04" HWADDR_PRIx
+ " is out of range.\n",
+ DEVICE(s)->canonical_path, offset);
+ return 0;
+ }
+
+ return s->sr_tim[regno];
+}
+
+static uint16_t npcm_pcs_read_vr_mii(NPCMPCSState *s, hwaddr offset)
+{
+ hwaddr regno = offset / sizeof(uint16_t);
+
+ if (regno >= NPCM_PCS_NR_VR_MIIS) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: VR_MII read offset 0x%04" HWADDR_PRIx
+ " is out of range.\n",
+ DEVICE(s)->canonical_path, offset);
+ return 0;
+ }
+
+ return s->vr_mii[regno];
+}
+
+static void npcm_pcs_write_sr_ctl(NPCMPCSState *s, hwaddr offset, uint16_t v)
+{
+ hwaddr regno = offset / sizeof(uint16_t);
+
+ if (regno >= NPCM_PCS_NR_SR_CTLS) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: SR_CTL write offset 0x%04" HWADDR_PRIx
+ " is out of range.\n",
+ DEVICE(s)->canonical_path, offset);
+ return;
+ }
+
+ s->sr_ctl[regno] = v;
+}
+
+static void npcm_pcs_write_sr_mii(NPCMPCSState *s, hwaddr offset, uint16_t v)
+{
+ hwaddr regno = offset / sizeof(uint16_t);
+
+ if (regno >= NPCM_PCS_NR_SR_MIIS) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: SR_MII write offset 0x%04" HWADDR_PRIx
+ " is out of range.\n",
+ DEVICE(s)->canonical_path, offset);
+ return;
+ }
+
+ s->sr_mii[regno] = v;
+
+ if ((offset == A_NPCM_PCS_SR_MII_CTRL) && (v & NPCM_PCS_SR_MII_CTRL_RST)) {
+ /* Trigger a soft reset */
+ npcm_pcs_soft_reset(s);
+ }
+}
+
+static void npcm_pcs_write_sr_tim(NPCMPCSState *s, hwaddr offset, uint16_t v)
+{
+ hwaddr regno = offset / sizeof(uint16_t);
+
+ if (regno >= NPCM_PCS_NR_SR_TIMS) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: SR_TIM write offset 0x%04" HWADDR_PRIx
+ " is out of range.\n",
+ DEVICE(s)->canonical_path, offset);
+ return;
+ }
+
+ s->sr_tim[regno] = v;
+}
+
+static void npcm_pcs_write_vr_mii(NPCMPCSState *s, hwaddr offset, uint16_t v)
+{
+ hwaddr regno = offset / sizeof(uint16_t);
+
+ if (regno >= NPCM_PCS_NR_VR_MIIS) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: VR_MII write offset 0x%04" HWADDR_PRIx
+ " is out of range.\n",
+ DEVICE(s)->canonical_path, offset);
+ return;
+ }
+
+ s->vr_mii[regno] = v;
+}
+
+static uint64_t npcm_pcs_read(void *opaque, hwaddr offset, unsigned size)
+{
+ NPCMPCSState *s = opaque;
+ uint16_t v = 0;
+
+ if (offset == NPCM_PCS_IND_AC_BA) {
+ v = s->indirect_access_base;
+ } else {
+ switch (s->indirect_access_base) {
+ case NPCM_PCS_IND_SR_CTL:
+ v = npcm_pcs_read_sr_ctl(s, offset);
+ break;
+
+ case NPCM_PCS_IND_SR_MII:
+ v = npcm_pcs_read_sr_mii(s, offset);
+ break;
+
+ case NPCM_PCS_IND_SR_TIM:
+ v = npcm_pcs_read_sr_tim(s, offset);
+ break;
+
+ case NPCM_PCS_IND_VR_MII:
+ v = npcm_pcs_read_vr_mii(s, offset);
+ break;
+
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: Read with invalid indirect address base: 0x%"
+ PRIx16 "\n", DEVICE(s)->canonical_path,
+ s->indirect_access_base);
+ }
+ }
+
+ trace_npcm_pcs_reg_read(DEVICE(s)->canonical_path, s->indirect_access_base,
+ offset, v);
+ return v;
+}
+
+static void npcm_pcs_write(void *opaque, hwaddr offset,
+ uint64_t v, unsigned size)
+{
+ NPCMPCSState *s = opaque;
+
+ trace_npcm_pcs_reg_write(DEVICE(s)->canonical_path, s->indirect_access_base,
+ offset, v);
+ if (offset == NPCM_PCS_IND_AC_BA) {
+ s->indirect_access_base = v;
+ } else {
+ switch (s->indirect_access_base) {
+ case NPCM_PCS_IND_SR_CTL:
+ npcm_pcs_write_sr_ctl(s, offset, v);
+ break;
+
+ case NPCM_PCS_IND_SR_MII:
+ npcm_pcs_write_sr_mii(s, offset, v);
+ break;
+
+ case NPCM_PCS_IND_SR_TIM:
+ npcm_pcs_write_sr_tim(s, offset, v);
+ break;
+
+ case NPCM_PCS_IND_VR_MII:
+ npcm_pcs_write_vr_mii(s, offset, v);
+ break;
+
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: Write with invalid indirect address base: 0x%02"
+ PRIx16 "\n", DEVICE(s)->canonical_path,
+ s->indirect_access_base);
+ }
+ }
+}
+
+static void npcm_pcs_enter_reset(Object *obj, ResetType type)
+{
+ NPCMPCSState *s = NPCM_PCS(obj);
+
+ npcm_pcs_soft_reset(s);
+}
+
+static const struct MemoryRegionOps npcm_pcs_ops = {
+ .read = npcm_pcs_read,
+ .write = npcm_pcs_write,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+ .valid = {
+ .min_access_size = 2,
+ .max_access_size = 2,
+ .unaligned = false,
+ },
+};
+
+static void npcm_pcs_realize(DeviceState *dev, Error **errp)
+{
+ NPCMPCSState *pcs = NPCM_PCS(dev);
+ SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
+
+ memory_region_init_io(&pcs->iomem, OBJECT(pcs), &npcm_pcs_ops, pcs,
+ TYPE_NPCM_PCS, 8 * KiB);
+ sysbus_init_mmio(sbd, &pcs->iomem);
+}
+
+static const VMStateDescription vmstate_npcm_pcs = {
+ .name = TYPE_NPCM_PCS,
+ .version_id = 0,
+ .minimum_version_id = 0,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT16(indirect_access_base, NPCMPCSState),
+ VMSTATE_UINT16_ARRAY(sr_ctl, NPCMPCSState, NPCM_PCS_NR_SR_CTLS),
+ VMSTATE_UINT16_ARRAY(sr_mii, NPCMPCSState, NPCM_PCS_NR_SR_MIIS),
+ VMSTATE_UINT16_ARRAY(sr_tim, NPCMPCSState, NPCM_PCS_NR_SR_TIMS),
+ VMSTATE_UINT16_ARRAY(vr_mii, NPCMPCSState, NPCM_PCS_NR_VR_MIIS),
+ VMSTATE_END_OF_LIST(),
+ },
+};
+
+static void npcm_pcs_class_init(ObjectClass *klass, void *data)
+{
+ ResettableClass *rc = RESETTABLE_CLASS(klass);
+ DeviceClass *dc = DEVICE_CLASS(klass);
+
+ set_bit(DEVICE_CATEGORY_MISC, dc->categories);
+ dc->desc = "NPCM PCS Controller";
+ dc->realize = npcm_pcs_realize;
+ dc->vmsd = &vmstate_npcm_pcs;
+ rc->phases.enter = npcm_pcs_enter_reset;
+}
+
+static const TypeInfo npcm_pcs_types[] = {
+ {
+ .name = TYPE_NPCM_PCS,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(NPCMPCSState),
+ .class_init = npcm_pcs_class_init,
+ },
+};
+DEFINE_TYPES(npcm_pcs_types)
diff --git a/hw/net/trace-events b/hw/net/trace-events
index c35bfb2..72b69c4 100644
--- a/hw/net/trace-events
+++ b/hw/net/trace-events
@@ -483,8 +483,8 @@ npcm_gmac_packet_tx_desc_data(const char* name, uint32_t tdes0, uint32_t tdes1)
npcm_gmac_tx_desc_owner(const char* name, uint32_t desc_addr) "%s: TX Descriptor @0x%04" PRIX32 " is owned by software"
# npcm_pcs.c
-npcm_pcs_reg_read(const char *name, uint16_t indirect_access_baes, uint64_t offset, uint16_t value) "%s: IND: 0x%02" PRIx16 " offset: 0x%04" PRIx64 " value: 0x%04" PRIx16
-npcm_pcs_reg_write(const char *name, uint16_t indirect_access_baes, uint64_t offset, uint16_t value) "%s: IND: 0x%02" PRIx16 " offset: 0x%04" PRIx64 " value: 0x%04" PRIx16
+npcm_pcs_reg_read(const char *name, uint16_t indirect_access_base, uint64_t offset, uint16_t value) "%s: IND: 0x%02" PRIx16 " offset: 0x%04" PRIx64 " value: 0x%04" PRIx16
+npcm_pcs_reg_write(const char *name, uint16_t indirect_access_base, uint64_t offset, uint16_t value) "%s: IND: 0x%02" PRIx16 " offset: 0x%04" PRIx64 " value: 0x%04" PRIx16
# dp8398x.c
dp8393x_raise_irq(int isr) "raise irq, isr is 0x%04x"
diff --git a/hw/ssi/npcm7xx_fiu.c b/hw/ssi/npcm7xx_fiu.c
index 21fc489..8df4bec 100644
--- a/hw/ssi/npcm7xx_fiu.c
+++ b/hw/ssi/npcm7xx_fiu.c
@@ -29,7 +29,7 @@
#include "trace.h"
/* Up to 128 MiB of flash may be accessed directly as memory. */
-#define NPCM7XX_FIU_FLASH_WINDOW_SIZE (128 * MiB)
+#define NPCM7XX_FIU_MAX_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)
@@ -507,6 +507,17 @@ static void npcm7xx_fiu_realize(DeviceState *dev, Error **errp)
return;
}
+ if (s->flash_size == 0) {
+ error_setg(errp, "%s: flash size must be set", dev->canonical_path);
+ return;
+ }
+
+ if (s->flash_size > NPCM7XX_FIU_MAX_FLASH_WINDOW_SIZE) {
+ error_setg(errp, "%s: flash size should not exceed 128 MiB",
+ dev->canonical_path);
+ 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);
@@ -525,7 +536,7 @@ static void npcm7xx_fiu_realize(DeviceState *dev, Error **errp)
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);
+ s->flash_size);
sysbus_init_mmio(sbd, &flash->direct_access);
}
}
@@ -543,6 +554,7 @@ static const VMStateDescription vmstate_npcm7xx_fiu = {
static const Property npcm7xx_fiu_properties[] = {
DEFINE_PROP_INT32("cs-count", NPCM7xxFIUState, cs_count, 0),
+ DEFINE_PROP_SIZE("flash-size", NPCM7xxFIUState, flash_size, 0),
};
static void npcm7xx_fiu_class_init(ObjectClass *klass, void *data)
diff --git a/hw/usb/Kconfig b/hw/usb/Kconfig
index 5fbecd2..69c663be 100644
--- a/hw/usb/Kconfig
+++ b/hw/usb/Kconfig
@@ -143,3 +143,7 @@ config USB_DWC3
config XLNX_USB_SUBSYS
bool
select USB_DWC3
+
+config USB_CHIPIDEA
+ bool
+ select USB_EHCI_SYSBUS
diff --git a/hw/usb/meson.build b/hw/usb/meson.build
index 1b4d150..17360a5 100644
--- a/hw/usb/meson.build
+++ b/hw/usb/meson.build
@@ -25,8 +25,8 @@ system_ss.add(when: 'CONFIG_USB_XHCI_SYSBUS', if_true: files('hcd-xhci-sysbus.c'
system_ss.add(when: 'CONFIG_USB_XHCI_NEC', if_true: files('hcd-xhci-nec.c'))
system_ss.add(when: 'CONFIG_USB_DWC2', if_true: files('hcd-dwc2.c'))
system_ss.add(when: 'CONFIG_USB_DWC3', if_true: files('hcd-dwc3.c'))
+system_ss.add(when: 'CONFIG_USB_CHIPIDEA', if_true: files('chipidea.c'))
-system_ss.add(when: 'CONFIG_IMX', if_true: files('chipidea.c'))
system_ss.add(when: 'CONFIG_IMX_USBPHY', if_true: files('imx-usb-phy.c'))
system_ss.add(when: 'CONFIG_VT82C686', if_true: files('vt82c686-uhci-pci.c'))
system_ss.add(when: 'CONFIG_XLNX_VERSAL', if_true: files('xlnx-versal-usb2-ctrl-regs.c'))