aboutsummaryrefslogtreecommitdiff
path: root/hw
diff options
context:
space:
mode:
authorPeter Maydell <peter.maydell@linaro.org>2024-02-27 15:34:41 +0000
committerPeter Maydell <peter.maydell@linaro.org>2024-02-27 15:34:41 +0000
commit158a054c4d1a40179f5e83cd7e1cfe65de457b92 (patch)
tree9ea0b0ef7cc79286585c014b6075fe77f5a83f9b /hw
parentb48ff1bfacd6982e26eb8718b61b1b9c3e3e18fe (diff)
parent9c5c959dd748f2972e46a84b8d5f77794f515014 (diff)
downloadqemu-158a054c4d1a40179f5e83cd7e1cfe65de457b92.zip
qemu-158a054c4d1a40179f5e83cd7e1cfe65de457b92.tar.gz
qemu-158a054c4d1a40179f5e83cd7e1cfe65de457b92.tar.bz2
Merge tag 'pull-target-arm-20240227-1' of https://git.linaro.org/people/pmaydell/qemu-arm into staging
target-arm queue: * Handle atomic updates of page tables entries in MMIO during PTW * Advertise Cortex-A53 erratum #843419 fix via REVIDR * MAINTAINERS: Cover hw/ide/ahci-allwinner.c with AllWinner A10 machine * misc: m48t59: replace qemu_system_reset_request() call with watchdog_perform_action() * misc: pxa2xx_timer: replace qemu_system_reset_request() call with watchdog_perform_action() * xlnx-versal-ospi: disable reentrancy detection for iomem_dac * sbsa-ref: Simplify init since PCIe is always enabled * stm32l4x5: Use TYPE_OR_IRQ when connecting STM32L4x5 EXTI fan-in IRQs * pl031: Update last RTCLR value on write in case it's read back * block: m25p80: Add support of mt35xu02gbba * xlnx-versal-virt: Add machine property ospi-flash * reset: refactor system reset to be three-phase aware * new board model raspi4b # -----BEGIN PGP SIGNATURE----- # # iQJNBAABCAA3FiEE4aXFk81BneKOgxXPPCUl7RQ2DN4FAmXeAMEZHHBldGVyLm1h # eWRlbGxAbGluYXJvLm9yZwAKCRA8JSXtFDYM3syyD/4lJzzstbDIAsu94Z4Hi0So # CFLAMJFsPy3fMsU2IqVP+TDTyhUeMPebwfj7sQHUtQcXVh5i1/HlYgdUgXsnjGWQ # pc6BxycpW6uJWYb7Ma3CdSGS+hxEpQ+U8Qeijwqg0kAqhjNtrSIkTRQ4u8p8T+kN # dWtQzp7D15BpEVhWl/2dLWWJwV3H6TThmr1FbK5wl/c7hJzy2uaXqmmCvercU0Zo # 6ab3SnGyhaujdd/FsDvhnVEYqcmcO2p9UtSnGAbdfw0zsf4p8cS2Q6M9q4DHBFYn # 6Bt51DFP5D+114VpqRSXF2Lv9K8swjTgqhDld9vCoios6pS3LMwcTAcONUxE8JU+ # uD7kXTN/lv3atNEy4MTFkTeNtKgbYJJuPwWrDRNdbVXPwrEHGWN3+ZYISmuYb+p+ # XL2/7HeP7/qEVMW2d18+7OCriZwKiBRZRKUrtG7mQSBZEMetbhpA+mLcxAZT0FAl # 18O/mcvEJrrE7x6Bqyv96b8PE0/er5cVg/b/wrkKS8DL4NWU9bJSjJNRrvt9bvvl # jSzPGo4ngHlfO0OpurLoFOZCVxKWVXgaKkQ3pOz301nsDyhEndNLeCxrITac8G2Q # C/WQuMaeOoV1x7N2MzaCQmyRzy8yGkG9av0aI/8feobfV/Yg4wPsfhcEn/XQWXKv # NUJ4/z78FbJlI2JeDP2QSA== # =xaMv # -----END PGP SIGNATURE----- # gpg: Signature made Tue 27 Feb 2024 15:33:21 GMT # gpg: using RSA key E1A5C593CD419DE28E8315CF3C2525ED14360CDE # gpg: issuer "peter.maydell@linaro.org" # gpg: Good signature from "Peter Maydell <peter.maydell@linaro.org>" [ultimate] # gpg: aka "Peter Maydell <pmaydell@gmail.com>" [ultimate] # gpg: aka "Peter Maydell <pmaydell@chiark.greenend.org.uk>" [ultimate] # gpg: aka "Peter Maydell <peter@archaic.org.uk>" [ultimate] # Primary key fingerprint: E1A5 C593 CD41 9DE2 8E83 15CF 3C25 25ED 1436 0CDE * tag 'pull-target-arm-20240227-1' of https://git.linaro.org/people/pmaydell/qemu-arm: (36 commits) docs/system/arm: Add RPi4B to raspi.rst hw/misc/bcm2835_property: Add missed BCM2835 properties tests/avocado/boot_linux_console.py: Add Rpi4b boot tests hw/arm/bcm2838_peripherals: Add clock_isp stub hw/arm: Add memory region for BCM2837 RPiVid ASB hw/arm/raspi4b: Temporarily disable unimplemented rpi4b devices hw/arm: Introduce Raspberry PI 4 machine hw/arm: Add GPIO and SD to BCM2838 periph hw/gpio: Connect SD controller to BCM2838 GPIO hw/gpio: Implement BCM2838 GPIO functionality hw/gpio: Add BCM2838 GPIO stub hw/arm/bcm2838: Add GIC-400 to BCM2838 SoC hw/arm: Introduce BCM2838 SoC hw/arm/raspi: Split out raspi machine common part hw/arm/bcm2853_peripherals: Split out common part of peripherals hw/arm/bcm2836: Split out common part of BCM283X classes docs/devel/reset: Update to discuss system reset hw/core/machine: Use qemu_register_resettable for sysbus reset hw/core/reset: Implement qemu_register_reset via qemu_register_resettable hw/core/reset: Add qemu_{register, unregister}_resettable() ... Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Diffstat (limited to 'hw')
-rw-r--r--hw/arm/bcm2835_peripherals.c215
-rw-r--r--hw/arm/bcm2836.c117
-rw-r--r--hw/arm/bcm2838.c263
-rw-r--r--hw/arm/bcm2838_peripherals.c224
-rw-r--r--hw/arm/meson.build2
-rw-r--r--hw/arm/raspi.c130
-rw-r--r--hw/arm/raspi4b.c132
-rw-r--r--hw/arm/sbsa-ref.c5
-rw-r--r--hw/arm/stm32l4x5_soc.c80
-rw-r--r--hw/arm/trace-events3
-rw-r--r--hw/arm/xlnx-versal-virt.c44
-rw-r--r--hw/block/m25p80.c3
-rw-r--r--hw/block/m25p80_sfdp.c36
-rw-r--r--hw/block/m25p80_sfdp.h1
-rw-r--r--hw/core/machine.c7
-rw-r--r--hw/core/meson.build1
-rw-r--r--hw/core/reset.c170
-rw-r--r--hw/core/resetcontainer.c77
-rw-r--r--hw/gpio/bcm2838_gpio.c390
-rw-r--r--hw/gpio/meson.build5
-rw-r--r--hw/misc/bcm2835_property.c21
-rw-r--r--hw/rtc/m48t59.c4
-rw-r--r--hw/rtc/pl031.c1
-rw-r--r--hw/ssi/xlnx-versal-ospi.c6
-rw-r--r--hw/timer/pxa2xx_timer.c3
25 files changed, 1692 insertions, 248 deletions
diff --git a/hw/arm/bcm2835_peripherals.c b/hw/arm/bcm2835_peripherals.c
index d5573fd..a0bbe76 100644
--- a/hw/arm/bcm2835_peripherals.c
+++ b/hw/arm/bcm2835_peripherals.c
@@ -30,9 +30,9 @@
#define SEPARATE_DMA_IRQ_MAX 10
#define ORGATED_DMA_IRQ_COUNT 4
-static void create_unimp(BCM2835PeripheralState *ps,
- UnimplementedDeviceState *uds,
- const char *name, hwaddr ofs, hwaddr size)
+void create_unimp(BCMSocPeripheralBaseState *ps,
+ UnimplementedDeviceState *uds,
+ const char *name, hwaddr ofs, hwaddr size)
{
object_initialize_child(OBJECT(ps), name, uds, TYPE_UNIMPLEMENTED_DEVICE);
qdev_prop_set_string(DEVICE(uds), "name", name);
@@ -45,9 +45,36 @@ static void create_unimp(BCM2835PeripheralState *ps,
static void bcm2835_peripherals_init(Object *obj)
{
BCM2835PeripheralState *s = BCM2835_PERIPHERALS(obj);
+ BCMSocPeripheralBaseState *s_base = BCM_SOC_PERIPHERALS_BASE(obj);
+
+ /* Random Number Generator */
+ object_initialize_child(obj, "rng", &s->rng, TYPE_BCM2835_RNG);
+
+ /* Thermal */
+ object_initialize_child(obj, "thermal", &s->thermal, TYPE_BCM2835_THERMAL);
+
+ /* GPIO */
+ object_initialize_child(obj, "gpio", &s->gpio, TYPE_BCM2835_GPIO);
+
+ object_property_add_const_link(OBJECT(&s->gpio), "sdbus-sdhci",
+ OBJECT(&s_base->sdhci.sdbus));
+ object_property_add_const_link(OBJECT(&s->gpio), "sdbus-sdhost",
+ OBJECT(&s_base->sdhost.sdbus));
+
+ /* Gated DMA interrupts */
+ object_initialize_child(obj, "orgated-dma-irq",
+ &s_base->orgated_dma_irq, TYPE_OR_IRQ);
+ object_property_set_int(OBJECT(&s_base->orgated_dma_irq), "num-lines",
+ ORGATED_DMA_IRQ_COUNT, &error_abort);
+}
+
+static void raspi_peripherals_base_init(Object *obj)
+{
+ BCMSocPeripheralBaseState *s = BCM_SOC_PERIPHERALS_BASE(obj);
+ BCMSocPeripheralBaseClass *bc = BCM_SOC_PERIPHERALS_BASE_GET_CLASS(obj);
/* Memory region for peripheral devices, which we export to our parent */
- memory_region_init(&s->peri_mr, obj,"bcm2835-peripherals", 0x1000000);
+ memory_region_init(&s->peri_mr, obj, "bcm2835-peripherals", bc->peri_size);
sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->peri_mr);
/* Internal memory region for peripheral bus addresses (not exported) */
@@ -81,6 +108,7 @@ static void bcm2835_peripherals_init(Object *obj)
/* Framebuffer */
object_initialize_child(obj, "fb", &s->fb, TYPE_BCM2835_FB);
object_property_add_alias(obj, "vcram-size", OBJECT(&s->fb), "vcram-size");
+ object_property_add_alias(obj, "vcram-base", OBJECT(&s->fb), "vcram-base");
object_property_add_const_link(OBJECT(&s->fb), "dma-mr",
OBJECT(&s->gpu_bus_mr));
@@ -98,9 +126,6 @@ static void bcm2835_peripherals_init(Object *obj)
object_property_add_const_link(OBJECT(&s->property), "dma-mr",
OBJECT(&s->gpu_bus_mr));
- /* Random Number Generator */
- object_initialize_child(obj, "rng", &s->rng, TYPE_BCM2835_RNG);
-
/* Extended Mass Media Controller */
object_initialize_child(obj, "sdhci", &s->sdhci, TYPE_SYSBUS_SDHCI);
@@ -110,25 +135,9 @@ static void bcm2835_peripherals_init(Object *obj)
/* DMA Channels */
object_initialize_child(obj, "dma", &s->dma, TYPE_BCM2835_DMA);
- object_initialize_child(obj, "orgated-dma-irq",
- &s->orgated_dma_irq, TYPE_OR_IRQ);
- object_property_set_int(OBJECT(&s->orgated_dma_irq), "num-lines",
- ORGATED_DMA_IRQ_COUNT, &error_abort);
-
object_property_add_const_link(OBJECT(&s->dma), "dma-mr",
OBJECT(&s->gpu_bus_mr));
- /* Thermal */
- object_initialize_child(obj, "thermal", &s->thermal, TYPE_BCM2835_THERMAL);
-
- /* GPIO */
- object_initialize_child(obj, "gpio", &s->gpio, TYPE_BCM2835_GPIO);
-
- object_property_add_const_link(OBJECT(&s->gpio), "sdbus-sdhci",
- OBJECT(&s->sdhci.sdbus));
- object_property_add_const_link(OBJECT(&s->gpio), "sdbus-sdhost",
- OBJECT(&s->sdhost.sdbus));
-
/* Mphi */
object_initialize_child(obj, "mphi", &s->mphi, TYPE_BCM2835_MPHI);
@@ -152,11 +161,76 @@ static void bcm2835_peripherals_init(Object *obj)
static void bcm2835_peripherals_realize(DeviceState *dev, Error **errp)
{
+ MemoryRegion *mphi_mr;
BCM2835PeripheralState *s = BCM2835_PERIPHERALS(dev);
+ BCMSocPeripheralBaseState *s_base = BCM_SOC_PERIPHERALS_BASE(dev);
+ int n;
+
+ bcm_soc_peripherals_common_realize(dev, errp);
+
+ /* Extended Mass Media Controller */
+ sysbus_connect_irq(SYS_BUS_DEVICE(&s_base->sdhci), 0,
+ qdev_get_gpio_in_named(DEVICE(&s_base->ic), BCM2835_IC_GPU_IRQ,
+ INTERRUPT_ARASANSDIO));
+
+ /* Connect DMA 0-12 to the interrupt controller */
+ for (n = 0; n <= SEPARATE_DMA_IRQ_MAX; n++) {
+ sysbus_connect_irq(SYS_BUS_DEVICE(&s_base->dma), n,
+ qdev_get_gpio_in_named(DEVICE(&s_base->ic),
+ BCM2835_IC_GPU_IRQ,
+ INTERRUPT_DMA0 + n));
+ }
+
+ if (!qdev_realize(DEVICE(&s_base->orgated_dma_irq), NULL, errp)) {
+ return;
+ }
+ for (n = 0; n < ORGATED_DMA_IRQ_COUNT; n++) {
+ sysbus_connect_irq(SYS_BUS_DEVICE(&s_base->dma),
+ SEPARATE_DMA_IRQ_MAX + 1 + n,
+ qdev_get_gpio_in(DEVICE(&s_base->orgated_dma_irq), n));
+ }
+ qdev_connect_gpio_out(DEVICE(&s_base->orgated_dma_irq), 0,
+ qdev_get_gpio_in_named(DEVICE(&s_base->ic),
+ BCM2835_IC_GPU_IRQ,
+ INTERRUPT_DMA0 + SEPARATE_DMA_IRQ_MAX + 1));
+
+ /* Random Number Generator */
+ if (!sysbus_realize(SYS_BUS_DEVICE(&s->rng), errp)) {
+ return;
+ }
+ memory_region_add_subregion(
+ &s_base->peri_mr, RNG_OFFSET,
+ sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->rng), 0));
+
+ /* THERMAL */
+ if (!sysbus_realize(SYS_BUS_DEVICE(&s->thermal), errp)) {
+ return;
+ }
+ memory_region_add_subregion(&s_base->peri_mr, THERMAL_OFFSET,
+ sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->thermal), 0));
+
+ /* Map MPHI to the peripherals memory map */
+ mphi_mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(&s_base->mphi), 0);
+ memory_region_add_subregion(&s_base->peri_mr, MPHI_OFFSET, mphi_mr);
+
+ /* GPIO */
+ if (!sysbus_realize(SYS_BUS_DEVICE(&s->gpio), errp)) {
+ return;
+ }
+ memory_region_add_subregion(
+ &s_base->peri_mr, GPIO_OFFSET,
+ sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->gpio), 0));
+
+ object_property_add_alias(OBJECT(s), "sd-bus", OBJECT(&s->gpio), "sd-bus");
+}
+
+void bcm_soc_peripherals_common_realize(DeviceState *dev, Error **errp)
+{
+ BCMSocPeripheralBaseState *s = BCM_SOC_PERIPHERALS_BASE(dev);
Object *obj;
MemoryRegion *ram;
Error *err = NULL;
- uint64_t ram_size, vcram_size;
+ uint64_t ram_size, vcram_size, vcram_base;
int n;
obj = object_property_get_link(OBJECT(dev), "ram", &error_abort);
@@ -260,11 +334,21 @@ static void bcm2835_peripherals_realize(DeviceState *dev, Error **errp)
return;
}
- if (!object_property_set_uint(OBJECT(&s->fb), "vcram-base",
- ram_size - vcram_size, errp)) {
+ vcram_base = object_property_get_uint(OBJECT(s), "vcram-base", &err);
+ if (err) {
+ error_propagate(errp, err);
return;
}
+ if (vcram_base == 0) {
+ vcram_base = ram_size - vcram_size;
+ }
+ vcram_base = MIN(vcram_base, UPPER_RAM_BASE - vcram_size);
+
+ if (!object_property_set_uint(OBJECT(&s->fb), "vcram-base", vcram_base,
+ errp)) {
+ return;
+ }
if (!sysbus_realize(SYS_BUS_DEVICE(&s->fb), errp)) {
return;
}
@@ -285,14 +369,6 @@ static void bcm2835_peripherals_realize(DeviceState *dev, Error **errp)
sysbus_connect_irq(SYS_BUS_DEVICE(&s->property), 0,
qdev_get_gpio_in(DEVICE(&s->mboxes), MBOX_CHAN_PROPERTY));
- /* Random Number Generator */
- if (!sysbus_realize(SYS_BUS_DEVICE(&s->rng), errp)) {
- return;
- }
-
- memory_region_add_subregion(&s->peri_mr, RNG_OFFSET,
- sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->rng), 0));
-
/* Extended Mass Media Controller
*
* Compatible with:
@@ -315,9 +391,6 @@ static void bcm2835_peripherals_realize(DeviceState *dev, Error **errp)
memory_region_add_subregion(&s->peri_mr, EMMC1_OFFSET,
sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->sdhci), 0));
- sysbus_connect_irq(SYS_BUS_DEVICE(&s->sdhci), 0,
- qdev_get_gpio_in_named(DEVICE(&s->ic), BCM2835_IC_GPU_IRQ,
- INTERRUPT_ARASANSDIO));
/* SDHOST */
if (!sysbus_realize(SYS_BUS_DEVICE(&s->sdhost), errp)) {
@@ -340,49 +413,11 @@ static void bcm2835_peripherals_realize(DeviceState *dev, Error **errp)
memory_region_add_subregion(&s->peri_mr, DMA15_OFFSET,
sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->dma), 1));
- for (n = 0; n <= SEPARATE_DMA_IRQ_MAX; n++) {
- sysbus_connect_irq(SYS_BUS_DEVICE(&s->dma), n,
- qdev_get_gpio_in_named(DEVICE(&s->ic),
- BCM2835_IC_GPU_IRQ,
- INTERRUPT_DMA0 + n));
- }
- if (!qdev_realize(DEVICE(&s->orgated_dma_irq), NULL, errp)) {
- return;
- }
- for (n = 0; n < ORGATED_DMA_IRQ_COUNT; n++) {
- sysbus_connect_irq(SYS_BUS_DEVICE(&s->dma),
- SEPARATE_DMA_IRQ_MAX + 1 + n,
- qdev_get_gpio_in(DEVICE(&s->orgated_dma_irq), n));
- }
- qdev_connect_gpio_out(DEVICE(&s->orgated_dma_irq), 0,
- qdev_get_gpio_in_named(DEVICE(&s->ic),
- BCM2835_IC_GPU_IRQ,
- INTERRUPT_DMA0 + SEPARATE_DMA_IRQ_MAX + 1));
-
- /* THERMAL */
- if (!sysbus_realize(SYS_BUS_DEVICE(&s->thermal), errp)) {
- return;
- }
- memory_region_add_subregion(&s->peri_mr, THERMAL_OFFSET,
- sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->thermal), 0));
-
- /* GPIO */
- if (!sysbus_realize(SYS_BUS_DEVICE(&s->gpio), errp)) {
- return;
- }
-
- memory_region_add_subregion(&s->peri_mr, GPIO_OFFSET,
- sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->gpio), 0));
-
- object_property_add_alias(OBJECT(s), "sd-bus", OBJECT(&s->gpio), "sd-bus");
-
/* Mphi */
if (!sysbus_realize(SYS_BUS_DEVICE(&s->mphi), errp)) {
return;
}
- memory_region_add_subregion(&s->peri_mr, MPHI_OFFSET,
- sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->mphi), 0));
sysbus_connect_irq(SYS_BUS_DEVICE(&s->mphi), 0,
qdev_get_gpio_in_named(DEVICE(&s->ic), BCM2835_IC_GPU_IRQ,
INTERRUPT_HOSTPORT));
@@ -436,21 +471,27 @@ static void bcm2835_peripherals_realize(DeviceState *dev, Error **errp)
static void bcm2835_peripherals_class_init(ObjectClass *oc, void *data)
{
DeviceClass *dc = DEVICE_CLASS(oc);
+ BCMSocPeripheralBaseClass *bc = BCM_SOC_PERIPHERALS_BASE_CLASS(oc);
+ bc->peri_size = 0x1000000;
dc->realize = bcm2835_peripherals_realize;
}
-static const TypeInfo bcm2835_peripherals_type_info = {
- .name = TYPE_BCM2835_PERIPHERALS,
- .parent = TYPE_SYS_BUS_DEVICE,
- .instance_size = sizeof(BCM2835PeripheralState),
- .instance_init = bcm2835_peripherals_init,
- .class_init = bcm2835_peripherals_class_init,
+static const TypeInfo bcm2835_peripherals_types[] = {
+ {
+ .name = TYPE_BCM2835_PERIPHERALS,
+ .parent = TYPE_BCM_SOC_PERIPHERALS_BASE,
+ .instance_size = sizeof(BCM2835PeripheralState),
+ .instance_init = bcm2835_peripherals_init,
+ .class_init = bcm2835_peripherals_class_init,
+ }, {
+ .name = TYPE_BCM_SOC_PERIPHERALS_BASE,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(BCMSocPeripheralBaseState),
+ .instance_init = raspi_peripherals_base_init,
+ .class_size = sizeof(BCMSocPeripheralBaseClass),
+ .abstract = true,
+ }
};
-static void bcm2835_peripherals_register_types(void)
-{
- type_register_static(&bcm2835_peripherals_type_info);
-}
-
-type_init(bcm2835_peripherals_register_types)
+DEFINE_TYPES(bcm2835_peripherals_types)
diff --git a/hw/arm/bcm2836.c b/hw/arm/bcm2836.c
index e3ba18a..db19166 100644
--- a/hw/arm/bcm2836.c
+++ b/hw/arm/bcm2836.c
@@ -31,12 +31,12 @@ struct BCM283XClass {
};
static Property bcm2836_enabled_cores_property =
- DEFINE_PROP_UINT32("enabled-cpus", BCM283XState, enabled_cpus, 0);
+ DEFINE_PROP_UINT32("enabled-cpus", BCM283XBaseState, enabled_cpus, 0);
-static void bcm2836_init(Object *obj)
+static void bcm283x_base_init(Object *obj)
{
- BCM283XState *s = BCM283X(obj);
- BCM283XClass *bc = BCM283X_GET_CLASS(obj);
+ BCM283XBaseState *s = BCM283X_BASE(obj);
+ BCM283XBaseClass *bc = BCM283X_BASE_GET_CLASS(obj);
int n;
for (n = 0; n < bc->core_count; n++) {
@@ -52,6 +52,11 @@ static void bcm2836_init(Object *obj)
object_initialize_child(obj, "control", &s->control,
TYPE_BCM2836_CONTROL);
}
+}
+
+static void bcm283x_init(Object *obj)
+{
+ BCM283XState *s = BCM283X(obj);
object_initialize_child(obj, "peripherals", &s->peripherals,
TYPE_BCM2835_PERIPHERALS);
@@ -61,108 +66,116 @@ static void bcm2836_init(Object *obj)
"command-line");
object_property_add_alias(obj, "vcram-size", OBJECT(&s->peripherals),
"vcram-size");
+ object_property_add_alias(obj, "vcram-base", OBJECT(&s->peripherals),
+ "vcram-base");
}
-static bool bcm283x_common_realize(DeviceState *dev, Error **errp)
+bool bcm283x_common_realize(DeviceState *dev, BCMSocPeripheralBaseState *ps,
+ Error **errp)
{
- BCM283XState *s = BCM283X(dev);
- BCM283XClass *bc = BCM283X_GET_CLASS(dev);
+ BCM283XBaseState *s = BCM283X_BASE(dev);
+ BCM283XBaseClass *bc = BCM283X_BASE_GET_CLASS(dev);
Object *obj;
/* common peripherals from bcm2835 */
obj = object_property_get_link(OBJECT(dev), "ram", &error_abort);
- object_property_add_const_link(OBJECT(&s->peripherals), "ram", obj);
+ object_property_add_const_link(OBJECT(ps), "ram", obj);
- if (!sysbus_realize(SYS_BUS_DEVICE(&s->peripherals), errp)) {
+ if (!sysbus_realize(SYS_BUS_DEVICE(ps), errp)) {
return false;
}
- object_property_add_alias(OBJECT(s), "sd-bus", OBJECT(&s->peripherals),
- "sd-bus");
+ object_property_add_alias(OBJECT(s), "sd-bus", OBJECT(ps), "sd-bus");
- sysbus_mmio_map_overlap(SYS_BUS_DEVICE(&s->peripherals), 0,
- bc->peri_base, 1);
+ sysbus_mmio_map_overlap(SYS_BUS_DEVICE(ps), 0, bc->peri_base, 1);
return true;
}
static void bcm2835_realize(DeviceState *dev, Error **errp)
{
BCM283XState *s = BCM283X(dev);
+ BCM283XBaseState *s_base = BCM283X_BASE(dev);
+ BCMSocPeripheralBaseState *ps_base
+ = BCM_SOC_PERIPHERALS_BASE(&s->peripherals);
- if (!bcm283x_common_realize(dev, errp)) {
+ if (!bcm283x_common_realize(dev, ps_base, errp)) {
return;
}
- if (!qdev_realize(DEVICE(&s->cpu[0].core), NULL, errp)) {
+ if (!qdev_realize(DEVICE(&s_base->cpu[0].core), NULL, errp)) {
return;
}
/* Connect irq/fiq outputs from the interrupt controller. */
sysbus_connect_irq(SYS_BUS_DEVICE(&s->peripherals), 0,
- qdev_get_gpio_in(DEVICE(&s->cpu[0].core), ARM_CPU_IRQ));
+ qdev_get_gpio_in(DEVICE(&s_base->cpu[0].core), ARM_CPU_IRQ));
sysbus_connect_irq(SYS_BUS_DEVICE(&s->peripherals), 1,
- qdev_get_gpio_in(DEVICE(&s->cpu[0].core), ARM_CPU_FIQ));
+ qdev_get_gpio_in(DEVICE(&s_base->cpu[0].core), ARM_CPU_FIQ));
}
static void bcm2836_realize(DeviceState *dev, Error **errp)
{
- BCM283XState *s = BCM283X(dev);
- BCM283XClass *bc = BCM283X_GET_CLASS(dev);
int n;
+ BCM283XState *s = BCM283X(dev);
+ BCM283XBaseState *s_base = BCM283X_BASE(dev);
+ BCM283XBaseClass *bc = BCM283X_BASE_GET_CLASS(dev);
+ BCMSocPeripheralBaseState *ps_base
+ = BCM_SOC_PERIPHERALS_BASE(&s->peripherals);
- if (!bcm283x_common_realize(dev, errp)) {
+ if (!bcm283x_common_realize(dev, ps_base, errp)) {
return;
}
/* bcm2836 interrupt controller (and mailboxes, etc.) */
- if (!sysbus_realize(SYS_BUS_DEVICE(&s->control), errp)) {
+ if (!sysbus_realize(SYS_BUS_DEVICE(&s_base->control), errp)) {
return;
}
- sysbus_mmio_map(SYS_BUS_DEVICE(&s->control), 0, bc->ctrl_base);
+ sysbus_mmio_map(SYS_BUS_DEVICE(&s_base->control), 0, bc->ctrl_base);
sysbus_connect_irq(SYS_BUS_DEVICE(&s->peripherals), 0,
- qdev_get_gpio_in_named(DEVICE(&s->control), "gpu-irq", 0));
+ qdev_get_gpio_in_named(DEVICE(&s_base->control), "gpu-irq", 0));
sysbus_connect_irq(SYS_BUS_DEVICE(&s->peripherals), 1,
- qdev_get_gpio_in_named(DEVICE(&s->control), "gpu-fiq", 0));
+ qdev_get_gpio_in_named(DEVICE(&s_base->control), "gpu-fiq", 0));
for (n = 0; n < BCM283X_NCPUS; n++) {
- object_property_set_int(OBJECT(&s->cpu[n].core), "mp-affinity",
+ object_property_set_int(OBJECT(&s_base->cpu[n].core), "mp-affinity",
(bc->clusterid << 8) | n, &error_abort);
/* set periphbase/CBAR value for CPU-local registers */
- object_property_set_int(OBJECT(&s->cpu[n].core), "reset-cbar",
+ object_property_set_int(OBJECT(&s_base->cpu[n].core), "reset-cbar",
bc->peri_base, &error_abort);
/* start powered off if not enabled */
- object_property_set_bool(OBJECT(&s->cpu[n].core), "start-powered-off",
- n >= s->enabled_cpus, &error_abort);
+ object_property_set_bool(OBJECT(&s_base->cpu[n].core),
+ "start-powered-off",
+ n >= s_base->enabled_cpus, &error_abort);
- if (!qdev_realize(DEVICE(&s->cpu[n].core), NULL, errp)) {
+ if (!qdev_realize(DEVICE(&s_base->cpu[n].core), NULL, errp)) {
return;
}
/* Connect irq/fiq outputs from the interrupt controller. */
- qdev_connect_gpio_out_named(DEVICE(&s->control), "irq", n,
- qdev_get_gpio_in(DEVICE(&s->cpu[n].core), ARM_CPU_IRQ));
- qdev_connect_gpio_out_named(DEVICE(&s->control), "fiq", n,
- qdev_get_gpio_in(DEVICE(&s->cpu[n].core), ARM_CPU_FIQ));
+ qdev_connect_gpio_out_named(DEVICE(&s_base->control), "irq", n,
+ qdev_get_gpio_in(DEVICE(&s_base->cpu[n].core), ARM_CPU_IRQ));
+ qdev_connect_gpio_out_named(DEVICE(&s_base->control), "fiq", n,
+ qdev_get_gpio_in(DEVICE(&s_base->cpu[n].core), ARM_CPU_FIQ));
/* Connect timers from the CPU to the interrupt controller */
- qdev_connect_gpio_out(DEVICE(&s->cpu[n].core), GTIMER_PHYS,
- qdev_get_gpio_in_named(DEVICE(&s->control), "cntpnsirq", n));
- qdev_connect_gpio_out(DEVICE(&s->cpu[n].core), GTIMER_VIRT,
- qdev_get_gpio_in_named(DEVICE(&s->control), "cntvirq", n));
- qdev_connect_gpio_out(DEVICE(&s->cpu[n].core), GTIMER_HYP,
- qdev_get_gpio_in_named(DEVICE(&s->control), "cnthpirq", n));
- qdev_connect_gpio_out(DEVICE(&s->cpu[n].core), GTIMER_SEC,
- qdev_get_gpio_in_named(DEVICE(&s->control), "cntpsirq", n));
+ qdev_connect_gpio_out(DEVICE(&s_base->cpu[n].core), GTIMER_PHYS,
+ qdev_get_gpio_in_named(DEVICE(&s_base->control), "cntpnsirq", n));
+ qdev_connect_gpio_out(DEVICE(&s_base->cpu[n].core), GTIMER_VIRT,
+ qdev_get_gpio_in_named(DEVICE(&s_base->control), "cntvirq", n));
+ qdev_connect_gpio_out(DEVICE(&s_base->cpu[n].core), GTIMER_HYP,
+ qdev_get_gpio_in_named(DEVICE(&s_base->control), "cnthpirq", n));
+ qdev_connect_gpio_out(DEVICE(&s_base->cpu[n].core), GTIMER_SEC,
+ qdev_get_gpio_in_named(DEVICE(&s_base->control), "cntpsirq", n));
}
}
-static void bcm283x_class_init(ObjectClass *oc, void *data)
+static void bcm283x_base_class_init(ObjectClass *oc, void *data)
{
DeviceClass *dc = DEVICE_CLASS(oc);
@@ -173,7 +186,7 @@ static void bcm283x_class_init(ObjectClass *oc, void *data)
static void bcm2835_class_init(ObjectClass *oc, void *data)
{
DeviceClass *dc = DEVICE_CLASS(oc);
- BCM283XClass *bc = BCM283X_CLASS(oc);
+ BCM283XBaseClass *bc = BCM283X_BASE_CLASS(oc);
bc->cpu_type = ARM_CPU_TYPE_NAME("arm1176");
bc->core_count = 1;
@@ -184,7 +197,7 @@ static void bcm2835_class_init(ObjectClass *oc, void *data)
static void bcm2836_class_init(ObjectClass *oc, void *data)
{
DeviceClass *dc = DEVICE_CLASS(oc);
- BCM283XClass *bc = BCM283X_CLASS(oc);
+ BCM283XBaseClass *bc = BCM283X_BASE_CLASS(oc);
bc->cpu_type = ARM_CPU_TYPE_NAME("cortex-a7");
bc->core_count = BCM283X_NCPUS;
@@ -198,7 +211,7 @@ static void bcm2836_class_init(ObjectClass *oc, void *data)
static void bcm2837_class_init(ObjectClass *oc, void *data)
{
DeviceClass *dc = DEVICE_CLASS(oc);
- BCM283XClass *bc = BCM283X_CLASS(oc);
+ BCM283XBaseClass *bc = BCM283X_BASE_CLASS(oc);
bc->cpu_type = ARM_CPU_TYPE_NAME("cortex-a53");
bc->core_count = BCM283X_NCPUS;
@@ -226,11 +239,17 @@ static const TypeInfo bcm283x_types[] = {
#endif
}, {
.name = TYPE_BCM283X,
- .parent = TYPE_DEVICE,
+ .parent = TYPE_BCM283X_BASE,
.instance_size = sizeof(BCM283XState),
- .instance_init = bcm2836_init,
- .class_size = sizeof(BCM283XClass),
- .class_init = bcm283x_class_init,
+ .instance_init = bcm283x_init,
+ .abstract = true,
+ }, {
+ .name = TYPE_BCM283X_BASE,
+ .parent = TYPE_DEVICE,
+ .instance_size = sizeof(BCM283XBaseState),
+ .instance_init = bcm283x_base_init,
+ .class_size = sizeof(BCM283XBaseClass),
+ .class_init = bcm283x_base_class_init,
.abstract = true,
}
};
diff --git a/hw/arm/bcm2838.c b/hw/arm/bcm2838.c
new file mode 100644
index 0000000..ddb7c5f
--- /dev/null
+++ b/hw/arm/bcm2838.c
@@ -0,0 +1,263 @@
+/*
+ * BCM2838 SoC emulation
+ *
+ * Copyright (C) 2022 Ovchinnikov Vitalii <vitalii.ovchinnikov@auriga.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "qemu/module.h"
+#include "hw/arm/raspi_platform.h"
+#include "hw/sysbus.h"
+#include "hw/arm/bcm2838.h"
+#include "trace.h"
+
+#define GIC400_MAINTENANCE_IRQ 9
+#define GIC400_TIMER_NS_EL2_IRQ 10
+#define GIC400_TIMER_VIRT_IRQ 11
+#define GIC400_LEGACY_FIQ 12
+#define GIC400_TIMER_S_EL1_IRQ 13
+#define GIC400_TIMER_NS_EL1_IRQ 14
+#define GIC400_LEGACY_IRQ 15
+
+/* Number of external interrupt lines to configure the GIC with */
+#define GIC_NUM_IRQS 192
+
+#define PPI(cpu, irq) (GIC_NUM_IRQS + (cpu) * GIC_INTERNAL + GIC_NR_SGIS + irq)
+
+#define GIC_BASE_OFS 0x0000
+#define GIC_DIST_OFS 0x1000
+#define GIC_CPU_OFS 0x2000
+#define GIC_VIFACE_THIS_OFS 0x4000
+#define GIC_VIFACE_OTHER_OFS(cpu) (0x5000 + (cpu) * 0x200)
+#define GIC_VCPU_OFS 0x6000
+
+#define VIRTUAL_PMU_IRQ 7
+
+static void bcm2838_gic_set_irq(void *opaque, int irq, int level)
+{
+ BCM2838State *s = (BCM2838State *)opaque;
+
+ trace_bcm2838_gic_set_irq(irq, level);
+ qemu_set_irq(qdev_get_gpio_in(DEVICE(&s->gic), irq), level);
+}
+
+static void bcm2838_init(Object *obj)
+{
+ BCM2838State *s = BCM2838(obj);
+
+ object_initialize_child(obj, "peripherals", &s->peripherals,
+ TYPE_BCM2838_PERIPHERALS);
+ object_property_add_alias(obj, "board-rev", OBJECT(&s->peripherals),
+ "board-rev");
+ object_property_add_alias(obj, "vcram-size", OBJECT(&s->peripherals),
+ "vcram-size");
+ object_property_add_alias(obj, "vcram-base", OBJECT(&s->peripherals),
+ "vcram-base");
+ object_property_add_alias(obj, "command-line", OBJECT(&s->peripherals),
+ "command-line");
+
+ object_initialize_child(obj, "gic", &s->gic, TYPE_ARM_GIC);
+}
+
+static void bcm2838_realize(DeviceState *dev, Error **errp)
+{
+ BCM2838State *s = BCM2838(dev);
+ BCM283XBaseState *s_base = BCM283X_BASE(dev);
+ BCM283XBaseClass *bc_base = BCM283X_BASE_GET_CLASS(dev);
+ BCM2838PeripheralState *ps = BCM2838_PERIPHERALS(&s->peripherals);
+ BCMSocPeripheralBaseState *ps_base =
+ BCM_SOC_PERIPHERALS_BASE(&s->peripherals);
+
+ DeviceState *gicdev = NULL;
+
+ if (!bcm283x_common_realize(dev, ps_base, errp)) {
+ return;
+ }
+ sysbus_mmio_map_overlap(SYS_BUS_DEVICE(ps), 1, BCM2838_PERI_LOW_BASE, 1);
+
+ /* bcm2836 interrupt controller (and mailboxes, etc.) */
+ if (!sysbus_realize(SYS_BUS_DEVICE(&s_base->control), errp)) {
+ return;
+ }
+ sysbus_mmio_map(SYS_BUS_DEVICE(&s_base->control), 0, bc_base->ctrl_base);
+
+ /* Create cores */
+ for (int n = 0; n < bc_base->core_count; n++) {
+
+ object_property_set_int(OBJECT(&s_base->cpu[n].core), "mp-affinity",
+ (bc_base->clusterid << 8) | n, &error_abort);
+
+ /* set periphbase/CBAR value for CPU-local registers */
+ object_property_set_int(OBJECT(&s_base->cpu[n].core), "reset-cbar",
+ bc_base->peri_base, &error_abort);
+
+ /* start powered off if not enabled */
+ object_property_set_bool(OBJECT(&s_base->cpu[n].core),
+ "start-powered-off",
+ n >= s_base->enabled_cpus, &error_abort);
+
+ if (!qdev_realize(DEVICE(&s_base->cpu[n].core), NULL, errp)) {
+ return;
+ }
+ }
+
+ if (!object_property_set_uint(OBJECT(&s->gic), "revision", 2, errp)) {
+ return;
+ }
+
+ if (!object_property_set_uint(OBJECT(&s->gic), "num-cpu", BCM283X_NCPUS,
+ errp)) {
+ return;
+ }
+
+ if (!object_property_set_uint(OBJECT(&s->gic), "num-irq",
+ GIC_NUM_IRQS + GIC_INTERNAL, errp)) {
+ return;
+ }
+
+ if (!object_property_set_bool(OBJECT(&s->gic),
+ "has-virtualization-extensions", true,
+ errp)) {
+ return;
+ }
+
+ if (!sysbus_realize(SYS_BUS_DEVICE(&s->gic), errp)) {
+ return;
+ }
+
+ sysbus_mmio_map(SYS_BUS_DEVICE(&s->gic), 0,
+ bc_base->ctrl_base + BCM2838_GIC_BASE + GIC_DIST_OFS);
+ sysbus_mmio_map(SYS_BUS_DEVICE(&s->gic), 1,
+ bc_base->ctrl_base + BCM2838_GIC_BASE + GIC_CPU_OFS);
+ sysbus_mmio_map(SYS_BUS_DEVICE(&s->gic), 2,
+ bc_base->ctrl_base + BCM2838_GIC_BASE + GIC_VIFACE_THIS_OFS);
+ sysbus_mmio_map(SYS_BUS_DEVICE(&s->gic), 3,
+ bc_base->ctrl_base + BCM2838_GIC_BASE + GIC_VCPU_OFS);
+
+ for (int n = 0; n < BCM283X_NCPUS; n++) {
+ sysbus_mmio_map(SYS_BUS_DEVICE(&s->gic), 4 + n,
+ bc_base->ctrl_base + BCM2838_GIC_BASE
+ + GIC_VIFACE_OTHER_OFS(n));
+ }
+
+ gicdev = DEVICE(&s->gic);
+
+ for (int n = 0; n < BCM283X_NCPUS; n++) {
+ DeviceState *cpudev = DEVICE(&s_base->cpu[n]);
+
+ /* Connect the GICv2 outputs to the CPU */
+ sysbus_connect_irq(SYS_BUS_DEVICE(&s->gic), n,
+ qdev_get_gpio_in(cpudev, ARM_CPU_IRQ));
+ sysbus_connect_irq(SYS_BUS_DEVICE(&s->gic), n + BCM283X_NCPUS,
+ qdev_get_gpio_in(cpudev, ARM_CPU_FIQ));
+ sysbus_connect_irq(SYS_BUS_DEVICE(&s->gic), n + 2 * BCM283X_NCPUS,
+ qdev_get_gpio_in(cpudev, ARM_CPU_VIRQ));
+ sysbus_connect_irq(SYS_BUS_DEVICE(&s->gic), n + 3 * BCM283X_NCPUS,
+ qdev_get_gpio_in(cpudev, ARM_CPU_VFIQ));
+
+ sysbus_connect_irq(SYS_BUS_DEVICE(&s->gic), n + 4 * BCM283X_NCPUS,
+ qdev_get_gpio_in(gicdev,
+ PPI(n, GIC400_MAINTENANCE_IRQ)));
+
+ /* Connect timers from the CPU to the interrupt controller */
+ qdev_connect_gpio_out(cpudev, GTIMER_PHYS,
+ qdev_get_gpio_in(gicdev, PPI(n, GIC400_TIMER_NS_EL1_IRQ)));
+ qdev_connect_gpio_out(cpudev, GTIMER_VIRT,
+ qdev_get_gpio_in(gicdev, PPI(n, GIC400_TIMER_VIRT_IRQ)));
+ qdev_connect_gpio_out(cpudev, GTIMER_HYP,
+ qdev_get_gpio_in(gicdev, PPI(n, GIC400_TIMER_NS_EL2_IRQ)));
+ qdev_connect_gpio_out(cpudev, GTIMER_SEC,
+ qdev_get_gpio_in(gicdev, PPI(n, GIC400_TIMER_S_EL1_IRQ)));
+ /* PMU interrupt */
+ qdev_connect_gpio_out_named(cpudev, "pmu-interrupt", 0,
+ qdev_get_gpio_in(gicdev, PPI(n, VIRTUAL_PMU_IRQ)));
+ }
+
+ /* Connect UART0 to the interrupt controller */
+ sysbus_connect_irq(SYS_BUS_DEVICE(&ps_base->uart0), 0,
+ qdev_get_gpio_in(gicdev, GIC_SPI_INTERRUPT_UART0));
+
+ /* Connect AUX / UART1 to the interrupt controller */
+ sysbus_connect_irq(SYS_BUS_DEVICE(&ps_base->aux), 0,
+ qdev_get_gpio_in(gicdev, GIC_SPI_INTERRUPT_AUX_UART1));
+
+ /* Connect VC mailbox to the interrupt controller */
+ sysbus_connect_irq(SYS_BUS_DEVICE(&ps_base->mboxes), 0,
+ qdev_get_gpio_in(gicdev, GIC_SPI_INTERRUPT_MBOX));
+
+ /* Connect SD host to the interrupt controller */
+ sysbus_connect_irq(SYS_BUS_DEVICE(&ps_base->sdhost), 0,
+ qdev_get_gpio_in(gicdev, GIC_SPI_INTERRUPT_SDHOST));
+
+ /* According to DTS, EMMC and EMMC2 share one irq */
+ DeviceState *mmc_irq_orgate = DEVICE(&ps->mmc_irq_orgate);
+
+ /* Connect EMMC and EMMC2 to the interrupt controller */
+ qdev_connect_gpio_out(mmc_irq_orgate, 0,
+ qdev_get_gpio_in(gicdev, GIC_SPI_INTERRUPT_EMMC_EMMC2));
+
+ /* Connect USB OTG and MPHI to the interrupt controller */
+ sysbus_connect_irq(SYS_BUS_DEVICE(&ps_base->mphi), 0,
+ qdev_get_gpio_in(gicdev, GIC_SPI_INTERRUPT_MPHI));
+ sysbus_connect_irq(SYS_BUS_DEVICE(&ps_base->dwc2), 0,
+ qdev_get_gpio_in(gicdev, GIC_SPI_INTERRUPT_DWC2));
+
+ /* Connect DMA 0-6 to the interrupt controller */
+ for (int n = GIC_SPI_INTERRUPT_DMA_0; n <= GIC_SPI_INTERRUPT_DMA_6; n++) {
+ sysbus_connect_irq(SYS_BUS_DEVICE(&ps_base->dma),
+ n - GIC_SPI_INTERRUPT_DMA_0,
+ qdev_get_gpio_in(gicdev, n));
+ }
+
+ /* According to DTS, DMA 7 and 8 share one irq */
+ DeviceState *dma_7_8_irq_orgate = DEVICE(&ps->dma_7_8_irq_orgate);
+
+ /* Connect DMA 7-8 to the interrupt controller */
+ qdev_connect_gpio_out(dma_7_8_irq_orgate, 0,
+ qdev_get_gpio_in(gicdev, GIC_SPI_INTERRUPT_DMA_7_8));
+
+ /* According to DTS, DMA 9 and 10 share one irq */
+ DeviceState *dma_9_10_irq_orgate = DEVICE(&ps->dma_9_10_irq_orgate);
+
+ /* Connect DMA 9-10 to the interrupt controller */
+ qdev_connect_gpio_out(dma_9_10_irq_orgate, 0,
+ qdev_get_gpio_in(gicdev, GIC_SPI_INTERRUPT_DMA_9_10));
+
+ /* Pass through inbound GPIO lines to the GIC */
+ qdev_init_gpio_in(dev, bcm2838_gic_set_irq, GIC_NUM_IRQS);
+
+ /* Pass through outbound IRQ lines from the GIC */
+ qdev_pass_gpios(DEVICE(&s->gic), DEVICE(&s->peripherals), NULL);
+}
+
+static void bcm2838_class_init(ObjectClass *oc, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(oc);
+ BCM283XBaseClass *bc_base = BCM283X_BASE_CLASS(oc);
+
+ bc_base->cpu_type = ARM_CPU_TYPE_NAME("cortex-a72");
+ bc_base->core_count = BCM283X_NCPUS;
+ bc_base->peri_base = 0xfe000000;
+ bc_base->ctrl_base = 0xff800000;
+ bc_base->clusterid = 0x0;
+ dc->realize = bcm2838_realize;
+}
+
+static const TypeInfo bcm2838_type = {
+ .name = TYPE_BCM2838,
+ .parent = TYPE_BCM283X_BASE,
+ .instance_size = sizeof(BCM2838State),
+ .instance_init = bcm2838_init,
+ .class_size = sizeof(BCM283XBaseClass),
+ .class_init = bcm2838_class_init,
+};
+
+static void bcm2838_register_types(void)
+{
+ type_register_static(&bcm2838_type);
+}
+
+type_init(bcm2838_register_types);
diff --git a/hw/arm/bcm2838_peripherals.c b/hw/arm/bcm2838_peripherals.c
new file mode 100644
index 0000000..e28bef4
--- /dev/null
+++ b/hw/arm/bcm2838_peripherals.c
@@ -0,0 +1,224 @@
+/*
+ * BCM2838 peripherals emulation
+ *
+ * Copyright (C) 2022 Ovchinnikov Vitalii <vitalii.ovchinnikov@auriga.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "qemu/module.h"
+#include "hw/arm/raspi_platform.h"
+#include "hw/arm/bcm2838_peripherals.h"
+
+#define CLOCK_ISP_OFFSET 0xc11000
+#define CLOCK_ISP_SIZE 0x100
+
+/* Lower peripheral base address on the VC (GPU) system bus */
+#define BCM2838_VC_PERI_LOW_BASE 0x7c000000
+
+/* Capabilities for SD controller: no DMA, high-speed, default clocks etc. */
+#define BCM2835_SDHC_CAPAREG 0x52134b4
+
+static void bcm2838_peripherals_init(Object *obj)
+{
+ BCM2838PeripheralState *s = BCM2838_PERIPHERALS(obj);
+ BCM2838PeripheralClass *bc = BCM2838_PERIPHERALS_GET_CLASS(obj);
+ BCMSocPeripheralBaseState *s_base = BCM_SOC_PERIPHERALS_BASE(obj);
+
+ /* Lower memory region for peripheral devices (exported to the Soc) */
+ memory_region_init(&s->peri_low_mr, obj, "bcm2838-peripherals",
+ bc->peri_low_size);
+ sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->peri_low_mr);
+
+ /* Extended Mass Media Controller 2 */
+ object_initialize_child(obj, "emmc2", &s->emmc2, TYPE_SYSBUS_SDHCI);
+
+ /* GPIO */
+ object_initialize_child(obj, "gpio", &s->gpio, TYPE_BCM2838_GPIO);
+
+ object_property_add_const_link(OBJECT(&s->gpio), "sdbus-sdhci",
+ OBJECT(&s_base->sdhci.sdbus));
+ object_property_add_const_link(OBJECT(&s->gpio), "sdbus-sdhost",
+ OBJECT(&s_base->sdhost.sdbus));
+
+ object_initialize_child(obj, "mmc_irq_orgate", &s->mmc_irq_orgate,
+ TYPE_OR_IRQ);
+ object_property_set_int(OBJECT(&s->mmc_irq_orgate), "num-lines", 2,
+ &error_abort);
+
+ object_initialize_child(obj, "dma_7_8_irq_orgate", &s->dma_7_8_irq_orgate,
+ TYPE_OR_IRQ);
+ object_property_set_int(OBJECT(&s->dma_7_8_irq_orgate), "num-lines", 2,
+ &error_abort);
+
+ object_initialize_child(obj, "dma_9_10_irq_orgate", &s->dma_9_10_irq_orgate,
+ TYPE_OR_IRQ);
+ object_property_set_int(OBJECT(&s->dma_9_10_irq_orgate), "num-lines", 2,
+ &error_abort);
+}
+
+static void bcm2838_peripherals_realize(DeviceState *dev, Error **errp)
+{
+ DeviceState *mmc_irq_orgate;
+ DeviceState *dma_7_8_irq_orgate;
+ DeviceState *dma_9_10_irq_orgate;
+ MemoryRegion *mphi_mr;
+ BCM2838PeripheralState *s = BCM2838_PERIPHERALS(dev);
+ BCMSocPeripheralBaseState *s_base = BCM_SOC_PERIPHERALS_BASE(dev);
+ int n;
+
+ bcm_soc_peripherals_common_realize(dev, errp);
+
+ /* Map lower peripherals into the GPU address space */
+ memory_region_init_alias(&s->peri_low_mr_alias, OBJECT(s),
+ "bcm2838-peripherals", &s->peri_low_mr, 0,
+ memory_region_size(&s->peri_low_mr));
+ memory_region_add_subregion_overlap(&s_base->gpu_bus_mr,
+ BCM2838_VC_PERI_LOW_BASE,
+ &s->peri_low_mr_alias, 1);
+
+ /* Extended Mass Media Controller 2 */
+ object_property_set_uint(OBJECT(&s->emmc2), "sd-spec-version", 3,
+ &error_abort);
+ object_property_set_uint(OBJECT(&s->emmc2), "capareg",
+ BCM2835_SDHC_CAPAREG, &error_abort);
+ object_property_set_bool(OBJECT(&s->emmc2), "pending-insert-quirk", true,
+ &error_abort);
+ if (!sysbus_realize(SYS_BUS_DEVICE(&s->emmc2), errp)) {
+ return;
+ }
+
+ memory_region_add_subregion(&s_base->peri_mr, EMMC2_OFFSET,
+ sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->emmc2),
+ 0));
+
+ /* According to DTS, EMMC and EMMC2 share one irq */
+ if (!qdev_realize(DEVICE(&s->mmc_irq_orgate), NULL, errp)) {
+ return;
+ }
+
+ mmc_irq_orgate = DEVICE(&s->mmc_irq_orgate);
+ sysbus_connect_irq(SYS_BUS_DEVICE(&s->emmc2), 0,
+ qdev_get_gpio_in(mmc_irq_orgate, 0));
+
+ sysbus_connect_irq(SYS_BUS_DEVICE(&s_base->sdhci), 0,
+ qdev_get_gpio_in(mmc_irq_orgate, 1));
+
+ /* Connect EMMC and EMMC2 to the interrupt controller */
+ qdev_connect_gpio_out(mmc_irq_orgate, 0,
+ qdev_get_gpio_in_named(DEVICE(&s_base->ic),
+ BCM2835_IC_GPU_IRQ,
+ INTERRUPT_ARASANSDIO));
+
+ /* Connect DMA 0-6 to the interrupt controller */
+ for (n = 0; n < 7; n++) {
+ sysbus_connect_irq(SYS_BUS_DEVICE(&s_base->dma), n,
+ qdev_get_gpio_in_named(DEVICE(&s_base->ic),
+ BCM2835_IC_GPU_IRQ,
+ GPU_INTERRUPT_DMA0 + n));
+ }
+
+ /* According to DTS, DMA 7 and 8 share one irq */
+ if (!qdev_realize(DEVICE(&s->dma_7_8_irq_orgate), NULL, errp)) {
+ return;
+ }
+ dma_7_8_irq_orgate = DEVICE(&s->dma_7_8_irq_orgate);
+
+ /* Connect DMA 7-8 to the interrupt controller */
+ sysbus_connect_irq(SYS_BUS_DEVICE(&s_base->dma), 7,
+ qdev_get_gpio_in(dma_7_8_irq_orgate, 0));
+ sysbus_connect_irq(SYS_BUS_DEVICE(&s_base->dma), 8,
+ qdev_get_gpio_in(dma_7_8_irq_orgate, 1));
+
+ qdev_connect_gpio_out(dma_7_8_irq_orgate, 0,
+ qdev_get_gpio_in_named(DEVICE(&s_base->ic),
+ BCM2835_IC_GPU_IRQ,
+ GPU_INTERRUPT_DMA7_8));
+
+ /* According to DTS, DMA 9 and 10 share one irq */
+ if (!qdev_realize(DEVICE(&s->dma_9_10_irq_orgate), NULL, errp)) {
+ return;
+ }
+ dma_9_10_irq_orgate = DEVICE(&s->dma_9_10_irq_orgate);
+
+ /* Connect DMA 9-10 to the interrupt controller */
+ sysbus_connect_irq(SYS_BUS_DEVICE(&s_base->dma), 9,
+ qdev_get_gpio_in(dma_9_10_irq_orgate, 0));
+ sysbus_connect_irq(SYS_BUS_DEVICE(&s_base->dma), 10,
+ qdev_get_gpio_in(dma_9_10_irq_orgate, 1));
+
+ qdev_connect_gpio_out(dma_9_10_irq_orgate, 0,
+ qdev_get_gpio_in_named(DEVICE(&s_base->ic),
+ BCM2835_IC_GPU_IRQ,
+ GPU_INTERRUPT_DMA9_10));
+
+ /* Connect DMA 11-14 to the interrupt controller */
+ for (n = 11; n < 15; n++) {
+ sysbus_connect_irq(SYS_BUS_DEVICE(&s_base->dma), n,
+ qdev_get_gpio_in_named(DEVICE(&s_base->ic),
+ BCM2835_IC_GPU_IRQ,
+ GPU_INTERRUPT_DMA11 + n
+ - 11));
+ }
+
+ /*
+ * Connect DMA 15 to the interrupt controller, it is physically removed
+ * from other DMA channels and exclusively used by the GPU
+ */
+ sysbus_connect_irq(SYS_BUS_DEVICE(&s_base->dma), 15,
+ qdev_get_gpio_in_named(DEVICE(&s_base->ic),
+ BCM2835_IC_GPU_IRQ,
+ GPU_INTERRUPT_DMA15));
+
+ /* Map MPHI to BCM2838 memory map */
+ mphi_mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(&s_base->mphi), 0);
+ memory_region_init_alias(&s->mphi_mr_alias, OBJECT(s), "mphi", mphi_mr, 0,
+ BCM2838_MPHI_SIZE);
+ memory_region_add_subregion(&s_base->peri_mr, BCM2838_MPHI_OFFSET,
+ &s->mphi_mr_alias);
+
+ create_unimp(s_base, &s->clkisp, "bcm2835-clkisp", CLOCK_ISP_OFFSET,
+ CLOCK_ISP_SIZE);
+
+ /* GPIO */
+ if (!sysbus_realize(SYS_BUS_DEVICE(&s->gpio), errp)) {
+ return;
+ }
+ memory_region_add_subregion(
+ &s_base->peri_mr, GPIO_OFFSET,
+ sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->gpio), 0));
+
+ object_property_add_alias(OBJECT(s), "sd-bus", OBJECT(&s->gpio), "sd-bus");
+
+ /* BCM2838 RPiVid ASB must be mapped to prevent kernel crash */
+ create_unimp(s_base, &s->asb, "bcm2838-asb", BRDG_OFFSET, 0x24);
+}
+
+static void bcm2838_peripherals_class_init(ObjectClass *oc, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(oc);
+ BCM2838PeripheralClass *bc = BCM2838_PERIPHERALS_CLASS(oc);
+ BCMSocPeripheralBaseClass *bc_base = BCM_SOC_PERIPHERALS_BASE_CLASS(oc);
+
+ bc->peri_low_size = 0x2000000;
+ bc_base->peri_size = 0x1800000;
+ dc->realize = bcm2838_peripherals_realize;
+}
+
+static const TypeInfo bcm2838_peripherals_type_info = {
+ .name = TYPE_BCM2838_PERIPHERALS,
+ .parent = TYPE_BCM_SOC_PERIPHERALS_BASE,
+ .instance_size = sizeof(BCM2838PeripheralState),
+ .instance_init = bcm2838_peripherals_init,
+ .class_size = sizeof(BCM2838PeripheralClass),
+ .class_init = bcm2838_peripherals_class_init,
+};
+
+static void bcm2838_peripherals_register_types(void)
+{
+ type_register_static(&bcm2838_peripherals_type_info);
+}
+
+type_init(bcm2838_peripherals_register_types)
diff --git a/hw/arm/meson.build b/hw/arm/meson.build
index a16d347..6808135 100644
--- a/hw/arm/meson.build
+++ b/hw/arm/meson.build
@@ -30,6 +30,7 @@ arm_ss.add(when: 'CONFIG_ALLWINNER_A10', if_true: files('allwinner-a10.c', 'cubi
arm_ss.add(when: 'CONFIG_ALLWINNER_H3', if_true: files('allwinner-h3.c', 'orangepi.c'))
arm_ss.add(when: 'CONFIG_ALLWINNER_R40', if_true: files('allwinner-r40.c', 'bananapi_m2u.c'))
arm_ss.add(when: 'CONFIG_RASPI', if_true: files('bcm2836.c', 'raspi.c'))
+arm_ss.add(when: ['CONFIG_RASPI', 'TARGET_AARCH64'], if_true: files('bcm2838.c', 'raspi4b.c'))
arm_ss.add(when: 'CONFIG_STM32F100_SOC', if_true: files('stm32f100_soc.c'))
arm_ss.add(when: 'CONFIG_STM32F205_SOC', if_true: files('stm32f205_soc.c'))
arm_ss.add(when: 'CONFIG_STM32F405_SOC', if_true: files('stm32f405_soc.c'))
@@ -67,6 +68,7 @@ system_ss.add(when: 'CONFIG_GUMSTIX', if_true: files('gumstix.c'))
system_ss.add(when: 'CONFIG_NETDUINO2', if_true: files('netduino2.c'))
system_ss.add(when: 'CONFIG_OMAP', if_true: files('omap2.c'))
system_ss.add(when: 'CONFIG_RASPI', if_true: files('bcm2835_peripherals.c'))
+system_ss.add(when: 'CONFIG_RASPI', if_true: files('bcm2838_peripherals.c'))
system_ss.add(when: 'CONFIG_SPITZ', if_true: files('spitz.c'))
system_ss.add(when: 'CONFIG_STRONGARM', if_true: files('strongarm.c'))
system_ss.add(when: 'CONFIG_SX1', if_true: files('omap_sx1.c'))
diff --git a/hw/arm/raspi.c b/hw/arm/raspi.c
index cc4c4ec..a7a662f 100644
--- a/hw/arm/raspi.c
+++ b/hw/arm/raspi.c
@@ -18,6 +18,8 @@
#include "qapi/error.h"
#include "hw/arm/boot.h"
#include "hw/arm/bcm2836.h"
+#include "hw/arm/bcm2838.h"
+#include "hw/arm/raspi_platform.h"
#include "hw/registerfields.h"
#include "qemu/error-report.h"
#include "hw/boards.h"
@@ -25,6 +27,9 @@
#include "hw/arm/boot.h"
#include "qom/object.h"
+#define TYPE_RASPI_MACHINE MACHINE_TYPE_NAME("raspi-common")
+OBJECT_DECLARE_SIMPLE_TYPE(RaspiMachineState, RASPI_MACHINE)
+
#define SMPBOOT_ADDR 0x300 /* this should leave enough space for ATAGS */
#define MVBAR_ADDR 0x400 /* secure vectors */
#define BOARDSETUP_ADDR (MVBAR_ADDR + 0x20) /* board setup code */
@@ -32,30 +37,12 @@
#define FIRMWARE_ADDR_3 0x80000 /* Pi 3 loads kernel.img here by default */
#define SPINTABLE_ADDR 0xd8 /* Pi 3 bootloader spintable */
-/* Registered machine type (matches RPi Foundation bootloader and U-Boot) */
-#define MACH_TYPE_BCM2708 3138
-
struct RaspiMachineState {
/*< private >*/
- MachineState parent_obj;
+ RaspiBaseMachineState parent_obj;
/*< public >*/
BCM283XState soc;
- struct arm_boot_info binfo;
-};
-typedef struct RaspiMachineState RaspiMachineState;
-
-struct RaspiMachineClass {
- /*< private >*/
- MachineClass parent_obj;
- /*< public >*/
- uint32_t board_rev;
};
-typedef struct RaspiMachineClass RaspiMachineClass;
-
-#define TYPE_RASPI_MACHINE MACHINE_TYPE_NAME("raspi-common")
-DECLARE_OBJ_CHECKERS(RaspiMachineState, RaspiMachineClass,
- RASPI_MACHINE, TYPE_RASPI_MACHINE)
-
/*
* Board revision codes:
@@ -72,6 +59,7 @@ typedef enum RaspiProcessorId {
PROCESSOR_ID_BCM2835 = 0,
PROCESSOR_ID_BCM2836 = 1,
PROCESSOR_ID_BCM2837 = 2,
+ PROCESSOR_ID_BCM2838 = 3,
} RaspiProcessorId;
static const struct {
@@ -81,9 +69,10 @@ static const struct {
[PROCESSOR_ID_BCM2835] = {TYPE_BCM2835, 1},
[PROCESSOR_ID_BCM2836] = {TYPE_BCM2836, BCM283X_NCPUS},
[PROCESSOR_ID_BCM2837] = {TYPE_BCM2837, BCM283X_NCPUS},
+ [PROCESSOR_ID_BCM2838] = {TYPE_BCM2838, BCM283X_NCPUS},
};
-static uint64_t board_ram_size(uint32_t board_rev)
+uint64_t board_ram_size(uint32_t board_rev)
{
assert(FIELD_EX32(board_rev, REV_CODE, STYLE)); /* Only new style */
return 256 * MiB << FIELD_EX32(board_rev, REV_CODE, MEMORY_SIZE);
@@ -99,7 +88,7 @@ static RaspiProcessorId board_processor_id(uint32_t board_rev)
return proc_id;
}
-static const char *board_soc_type(uint32_t board_rev)
+const char *board_soc_type(uint32_t board_rev)
{
return soc_property[board_processor_id(board_rev)].type;
}
@@ -200,13 +189,12 @@ static void reset_secondary(ARMCPU *cpu, const struct arm_boot_info *info)
cpu_set_pc(cs, info->smp_loader_start);
}
-static void setup_boot(MachineState *machine, RaspiProcessorId processor_id,
- size_t ram_size)
+static void setup_boot(MachineState *machine, ARMCPU *cpu,
+ RaspiProcessorId processor_id, size_t ram_size)
{
- RaspiMachineState *s = RASPI_MACHINE(machine);
+ RaspiBaseMachineState *s = RASPI_BASE_MACHINE(machine);
int r;
- s->binfo.board_id = MACH_TYPE_BCM2708;
s->binfo.ram_size = ram_size;
if (processor_id <= PROCESSOR_ID_BCM2836) {
@@ -252,16 +240,17 @@ static void setup_boot(MachineState *machine, RaspiProcessorId processor_id,
s->binfo.firmware_loaded = true;
}
- arm_load_kernel(&s->soc.cpu[0].core, machine, &s->binfo);
+ arm_load_kernel(cpu, machine, &s->binfo);
}
-static void raspi_machine_init(MachineState *machine)
+void raspi_base_machine_init(MachineState *machine,
+ BCM283XBaseState *soc)
{
- RaspiMachineClass *mc = RASPI_MACHINE_GET_CLASS(machine);
- RaspiMachineState *s = RASPI_MACHINE(machine);
+ RaspiBaseMachineClass *mc = RASPI_BASE_MACHINE_GET_CLASS(machine);
uint32_t board_rev = mc->board_rev;
uint64_t ram_size = board_ram_size(board_rev);
- uint32_t vcram_size;
+ uint32_t vcram_base, vcram_size;
+ size_t boot_ram_size;
DriveInfo *di;
BlockBackend *blk;
BusState *bus;
@@ -279,19 +268,17 @@ static void raspi_machine_init(MachineState *machine)
machine->ram, 0);
/* Setup the SOC */
- object_initialize_child(OBJECT(machine), "soc", &s->soc,
- board_soc_type(board_rev));
- object_property_add_const_link(OBJECT(&s->soc), "ram", OBJECT(machine->ram));
- object_property_set_int(OBJECT(&s->soc), "board-rev", board_rev,
+ object_property_add_const_link(OBJECT(soc), "ram", OBJECT(machine->ram));
+ object_property_set_int(OBJECT(soc), "board-rev", board_rev,
&error_abort);
- object_property_set_str(OBJECT(&s->soc), "command-line",
+ object_property_set_str(OBJECT(soc), "command-line",
machine->kernel_cmdline, &error_abort);
- qdev_realize(DEVICE(&s->soc), NULL, &error_fatal);
+ qdev_realize(DEVICE(soc), NULL, &error_fatal);
/* Create and plug in the SD cards */
di = drive_get(IF_SD, 0, 0);
blk = di ? blk_by_legacy_dinfo(di) : NULL;
- bus = qdev_get_child_bus(DEVICE(&s->soc), "sd-bus");
+ bus = qdev_get_child_bus(DEVICE(soc), "sd-bus");
if (bus == NULL) {
error_report("No SD bus found in SOC object");
exit(1);
@@ -300,19 +287,40 @@ static void raspi_machine_init(MachineState *machine)
qdev_prop_set_drive_err(carddev, "drive", blk, &error_fatal);
qdev_realize_and_unref(carddev, bus, &error_fatal);
- vcram_size = object_property_get_uint(OBJECT(&s->soc), "vcram-size",
+ vcram_size = object_property_get_uint(OBJECT(soc), "vcram-size",
&error_abort);
- setup_boot(machine, board_processor_id(mc->board_rev),
- machine->ram_size - vcram_size);
+ vcram_base = object_property_get_uint(OBJECT(soc), "vcram-base",
+ &error_abort);
+
+ if (vcram_base == 0) {
+ vcram_base = ram_size - vcram_size;
+ }
+ boot_ram_size = MIN(vcram_base, UPPER_RAM_BASE - vcram_size);
+
+ setup_boot(machine, &soc->cpu[0].core, board_processor_id(board_rev),
+ boot_ram_size);
}
-static void raspi_machine_class_common_init(MachineClass *mc,
- uint32_t board_rev)
+void raspi_machine_init(MachineState *machine)
+{
+ RaspiMachineState *s = RASPI_MACHINE(machine);
+ RaspiBaseMachineState *s_base = RASPI_BASE_MACHINE(machine);
+ RaspiBaseMachineClass *mc = RASPI_BASE_MACHINE_GET_CLASS(machine);
+ BCM283XState *soc = &s->soc;
+
+ s_base->binfo.board_id = MACH_TYPE_BCM2708;
+
+ object_initialize_child(OBJECT(machine), "soc", soc,
+ board_soc_type(mc->board_rev));
+ raspi_base_machine_init(machine, &soc->parent_obj);
+}
+
+void raspi_machine_class_common_init(MachineClass *mc,
+ uint32_t board_rev)
{
mc->desc = g_strdup_printf("Raspberry Pi %s (revision 1.%u)",
board_type(board_rev),
FIELD_EX32(board_rev, REV_CODE, REVISION));
- mc->init = raspi_machine_init;
mc->block_default_type = IF_SD;
mc->no_parallel = 1;
mc->no_floppy = 1;
@@ -322,50 +330,57 @@ static void raspi_machine_class_common_init(MachineClass *mc,
mc->default_ram_id = "ram";
};
+static void raspi_machine_class_init(MachineClass *mc,
+ uint32_t board_rev)
+{
+ raspi_machine_class_common_init(mc, board_rev);
+ mc->init = raspi_machine_init;
+};
+
static void raspi0_machine_class_init(ObjectClass *oc, void *data)
{
MachineClass *mc = MACHINE_CLASS(oc);
- RaspiMachineClass *rmc = RASPI_MACHINE_CLASS(oc);
+ RaspiBaseMachineClass *rmc = RASPI_BASE_MACHINE_CLASS(oc);
rmc->board_rev = 0x920092; /* Revision 1.2 */
- raspi_machine_class_common_init(mc, rmc->board_rev);
+ raspi_machine_class_init(mc, rmc->board_rev);
};
static void raspi1ap_machine_class_init(ObjectClass *oc, void *data)
{
MachineClass *mc = MACHINE_CLASS(oc);
- RaspiMachineClass *rmc = RASPI_MACHINE_CLASS(oc);
+ RaspiBaseMachineClass *rmc = RASPI_BASE_MACHINE_CLASS(oc);
rmc->board_rev = 0x900021; /* Revision 1.1 */
- raspi_machine_class_common_init(mc, rmc->board_rev);
+ raspi_machine_class_init(mc, rmc->board_rev);
};
static void raspi2b_machine_class_init(ObjectClass *oc, void *data)
{
MachineClass *mc = MACHINE_CLASS(oc);
- RaspiMachineClass *rmc = RASPI_MACHINE_CLASS(oc);
+ RaspiBaseMachineClass *rmc = RASPI_BASE_MACHINE_CLASS(oc);
rmc->board_rev = 0xa21041;
- raspi_machine_class_common_init(mc, rmc->board_rev);
+ raspi_machine_class_init(mc, rmc->board_rev);
};
#ifdef TARGET_AARCH64
static void raspi3ap_machine_class_init(ObjectClass *oc, void *data)
{
MachineClass *mc = MACHINE_CLASS(oc);
- RaspiMachineClass *rmc = RASPI_MACHINE_CLASS(oc);
+ RaspiBaseMachineClass *rmc = RASPI_BASE_MACHINE_CLASS(oc);
rmc->board_rev = 0x9020e0; /* Revision 1.0 */
- raspi_machine_class_common_init(mc, rmc->board_rev);
+ raspi_machine_class_init(mc, rmc->board_rev);
};
static void raspi3b_machine_class_init(ObjectClass *oc, void *data)
{
MachineClass *mc = MACHINE_CLASS(oc);
- RaspiMachineClass *rmc = RASPI_MACHINE_CLASS(oc);
+ RaspiBaseMachineClass *rmc = RASPI_BASE_MACHINE_CLASS(oc);
rmc->board_rev = 0xa02082;
- raspi_machine_class_common_init(mc, rmc->board_rev);
+ raspi_machine_class_init(mc, rmc->board_rev);
};
#endif /* TARGET_AARCH64 */
@@ -394,9 +409,14 @@ static const TypeInfo raspi_machine_types[] = {
#endif
}, {
.name = TYPE_RASPI_MACHINE,
- .parent = TYPE_MACHINE,
+ .parent = TYPE_RASPI_BASE_MACHINE,
.instance_size = sizeof(RaspiMachineState),
- .class_size = sizeof(RaspiMachineClass),
+ .abstract = true,
+ }, {
+ .name = TYPE_RASPI_BASE_MACHINE,
+ .parent = TYPE_MACHINE,
+ .instance_size = sizeof(RaspiBaseMachineState),
+ .class_size = sizeof(RaspiBaseMachineClass),
.abstract = true,
}
};
diff --git a/hw/arm/raspi4b.c b/hw/arm/raspi4b.c
new file mode 100644
index 0000000..cb1b1f2
--- /dev/null
+++ b/hw/arm/raspi4b.c
@@ -0,0 +1,132 @@
+/*
+ * Raspberry Pi 4B emulation
+ *
+ * Copyright (C) 2022 Ovchinnikov Vitalii <vitalii.ovchinnikov@auriga.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/units.h"
+#include "qemu/cutils.h"
+#include "qapi/error.h"
+#include "qapi/visitor.h"
+#include "hw/arm/raspi_platform.h"
+#include "hw/display/bcm2835_fb.h"
+#include "hw/registerfields.h"
+#include "qemu/error-report.h"
+#include "sysemu/device_tree.h"
+#include "hw/boards.h"
+#include "hw/loader.h"
+#include "hw/arm/boot.h"
+#include "qom/object.h"
+#include "hw/arm/bcm2838.h"
+#include <libfdt.h>
+
+#define TYPE_RASPI4B_MACHINE MACHINE_TYPE_NAME("raspi4b")
+OBJECT_DECLARE_SIMPLE_TYPE(Raspi4bMachineState, RASPI4B_MACHINE)
+
+struct Raspi4bMachineState {
+ RaspiBaseMachineState parent_obj;
+ BCM2838State soc;
+};
+
+/*
+ * Add second memory region if board RAM amount exceeds VC base address
+ * (see https://datasheets.raspberrypi.com/bcm2711/bcm2711-peripherals.pdf
+ * 1.2 Address Map)
+ */
+static int raspi_add_memory_node(void *fdt, hwaddr mem_base, hwaddr mem_len)
+{
+ int ret;
+ uint32_t acells, scells;
+ char *nodename = g_strdup_printf("/memory@%" PRIx64, mem_base);
+
+ acells = qemu_fdt_getprop_cell(fdt, "/", "#address-cells",
+ NULL, &error_fatal);
+ scells = qemu_fdt_getprop_cell(fdt, "/", "#size-cells",
+ NULL, &error_fatal);
+ if (acells == 0 || scells == 0) {
+ fprintf(stderr, "dtb file invalid (#address-cells or #size-cells 0)\n");
+ ret = -1;
+ } else {
+ qemu_fdt_add_subnode(fdt, nodename);
+ qemu_fdt_setprop_string(fdt, nodename, "device_type", "memory");
+ ret = qemu_fdt_setprop_sized_cells(fdt, nodename, "reg",
+ acells, mem_base,
+ scells, mem_len);
+ }
+
+ g_free(nodename);
+ return ret;
+}
+
+static void raspi4_modify_dtb(const struct arm_boot_info *info, void *fdt)
+{
+ uint64_t ram_size;
+
+ /* Temporarily disable following devices until they are implemented */
+ const char *nodes_to_remove[] = {
+ "brcm,bcm2711-pcie",
+ "brcm,bcm2711-rng200",
+ "brcm,bcm2711-thermal",
+ "brcm,bcm2711-genet-v5",
+ };
+
+ for (int i = 0; i < ARRAY_SIZE(nodes_to_remove); i++) {
+ const char *dev_str = nodes_to_remove[i];
+
+ int offset = fdt_node_offset_by_compatible(fdt, -1, dev_str);
+ if (offset >= 0) {
+ if (!fdt_nop_node(fdt, offset)) {
+ warn_report("bcm2711 dtc: %s has been disabled!", dev_str);
+ }
+ }
+ }
+
+ ram_size = board_ram_size(info->board_id);
+
+ if (info->ram_size > UPPER_RAM_BASE) {
+ raspi_add_memory_node(fdt, UPPER_RAM_BASE, ram_size - UPPER_RAM_BASE);
+ }
+}
+
+static void raspi4b_machine_init(MachineState *machine)
+{
+ Raspi4bMachineState *s = RASPI4B_MACHINE(machine);
+ RaspiBaseMachineState *s_base = RASPI_BASE_MACHINE(machine);
+ RaspiBaseMachineClass *mc = RASPI_BASE_MACHINE_GET_CLASS(machine);
+ BCM2838State *soc = &s->soc;
+
+ s_base->binfo.modify_dtb = raspi4_modify_dtb;
+ s_base->binfo.board_id = mc->board_rev;
+
+ object_initialize_child(OBJECT(machine), "soc", soc,
+ board_soc_type(mc->board_rev));
+
+ raspi_base_machine_init(machine, &soc->parent_obj);
+}
+
+static void raspi4b_machine_class_init(ObjectClass *oc, void *data)
+{
+ MachineClass *mc = MACHINE_CLASS(oc);
+ RaspiBaseMachineClass *rmc = RASPI_BASE_MACHINE_CLASS(oc);
+
+ rmc->board_rev = 0xb03115; /* Revision 1.5, 2 Gb RAM */
+ raspi_machine_class_common_init(mc, rmc->board_rev);
+ mc->init = raspi4b_machine_init;
+}
+
+static const TypeInfo raspi4b_machine_type = {
+ .name = TYPE_RASPI4B_MACHINE,
+ .parent = TYPE_RASPI_BASE_MACHINE,
+ .instance_size = sizeof(Raspi4bMachineState),
+ .class_init = raspi4b_machine_class_init,
+};
+
+static void raspi4b_machine_register_type(void)
+{
+ type_register_static(&raspi4b_machine_type);
+}
+
+type_init(raspi4b_machine_register_type)
diff --git a/hw/arm/sbsa-ref.c b/hw/arm/sbsa-ref.c
index 13dde50..f5709d6 100644
--- a/hw/arm/sbsa-ref.c
+++ b/hw/arm/sbsa-ref.c
@@ -664,9 +664,8 @@ static void create_pcie(SBSAMachineState *sms)
}
pci = PCI_HOST_BRIDGE(dev);
- if (pci->bus) {
- pci_init_nic_devices(pci->bus, mc->default_nic);
- }
+
+ pci_init_nic_devices(pci->bus, mc->default_nic);
pci_create_simple(pci->bus, -1, "bochs-display");
diff --git a/hw/arm/stm32l4x5_soc.c b/hw/arm/stm32l4x5_soc.c
index f470ff7..d1786e0 100644
--- a/hw/arm/stm32l4x5_soc.c
+++ b/hw/arm/stm32l4x5_soc.c
@@ -26,6 +26,7 @@
#include "qapi/error.h"
#include "exec/address-spaces.h"
#include "sysemu/sysemu.h"
+#include "hw/or-irq.h"
#include "hw/arm/stm32l4x5_soc.h"
#include "hw/qdev-clock.h"
#include "hw/misc/unimp.h"
@@ -42,21 +43,24 @@
#define NUM_EXTI_IRQ 40
/* Match exti line connections with their CPU IRQ number */
/* See Vector Table (Reference Manual p.396) */
+/*
+ * Some IRQs are connected to the same CPU IRQ (denoted by -1)
+ * and require an intermediary OR gate to function correctly.
+ */
static const int exti_irq[NUM_EXTI_IRQ] = {
6, /* GPIO[0] */
7, /* GPIO[1] */
8, /* GPIO[2] */
9, /* GPIO[3] */
10, /* GPIO[4] */
- 23, 23, 23, 23, 23, /* GPIO[5..9] */
- 40, 40, 40, 40, 40, 40, /* GPIO[10..15] */
- 1, /* PVD */
+ -1, -1, -1, -1, -1, /* GPIO[5..9] OR gate 23 */
+ -1, -1, -1, -1, -1, -1, /* GPIO[10..15] OR gate 40 */
+ -1, /* PVD OR gate 1 */
67, /* OTG_FS_WKUP, Direct */
41, /* RTC_ALARM */
2, /* RTC_TAMP_STAMP2/CSS_LSE */
3, /* RTC wakeup timer */
- 63, /* COMP1 */
- 63, /* COMP2 */
+ -1, -1, /* COMP[1..2] OR gate 63 */
31, /* I2C1 wakeup, Direct */
33, /* I2C2 wakeup, Direct */
72, /* I2C3 wakeup, Direct */
@@ -69,18 +73,39 @@ static const int exti_irq[NUM_EXTI_IRQ] = {
65, /* LPTIM1, Direct */
66, /* LPTIM2, Direct */
76, /* SWPMI1 wakeup, Direct */
- 1, /* PVM1 wakeup */
- 1, /* PVM2 wakeup */
- 1, /* PVM3 wakeup */
- 1, /* PVM4 wakeup */
+ -1, -1, -1, -1, /* PVM[1..4] OR gate 1 */
78 /* LCD wakeup, Direct */
};
+static const int exti_or_gates_out[NUM_EXTI_OR_GATES] = {
+ 23, 40, 63, 1,
+};
+
+static const int exti_or_gates_num_lines_in[NUM_EXTI_OR_GATES] = {
+ 5, 6, 2, 5,
+};
+
+/* 3 OR gates with consecutive inputs */
+#define NUM_EXTI_SIMPLE_OR_GATES 3
+static const int exti_or_gates_first_line_in[NUM_EXTI_SIMPLE_OR_GATES] = {
+ 5, 10, 21,
+};
+
+/* 1 OR gate with non-consecutive inputs */
+#define EXTI_OR_GATE1_NUM_LINES_IN 5
+static const int exti_or_gate1_lines_in[EXTI_OR_GATE1_NUM_LINES_IN] = {
+ 16, 35, 36, 37, 38,
+};
+
static void stm32l4x5_soc_initfn(Object *obj)
{
Stm32l4x5SocState *s = STM32L4X5_SOC(obj);
object_initialize_child(obj, "exti", &s->exti, TYPE_STM32L4X5_EXTI);
+ for (unsigned i = 0; i < NUM_EXTI_OR_GATES; i++) {
+ object_initialize_child(obj, "exti_or_gates[*]", &s->exti_or_gates[i],
+ TYPE_OR_IRQ);
+ }
object_initialize_child(obj, "syscfg", &s->syscfg, TYPE_STM32L4X5_SYSCFG);
s->sysclk = qdev_init_clock_in(DEVICE(s), "sysclk", NULL, NULL, 0);
@@ -175,8 +200,43 @@ static void stm32l4x5_soc_realize(DeviceState *dev_soc, Error **errp)
return;
}
sysbus_mmio_map(busdev, 0, EXTI_ADDR);
+
+ /* IRQs with fan-in that require an OR gate */
+ for (unsigned i = 0; i < NUM_EXTI_OR_GATES; i++) {
+ if (!object_property_set_int(OBJECT(&s->exti_or_gates[i]), "num-lines",
+ exti_or_gates_num_lines_in[i], errp)) {
+ return;
+ }
+ if (!qdev_realize(DEVICE(&s->exti_or_gates[i]), NULL, errp)) {
+ return;
+ }
+
+ qdev_connect_gpio_out(DEVICE(&s->exti_or_gates[i]), 0,
+ qdev_get_gpio_in(armv7m, exti_or_gates_out[i]));
+
+ if (i < NUM_EXTI_SIMPLE_OR_GATES) {
+ /* consecutive inputs for OR gates 23, 40, 63 */
+ for (unsigned j = 0; j < exti_or_gates_num_lines_in[i]; j++) {
+ sysbus_connect_irq(SYS_BUS_DEVICE(&s->exti),
+ exti_or_gates_first_line_in[i] + j,
+ qdev_get_gpio_in(DEVICE(&s->exti_or_gates[i]), j));
+ }
+ } else {
+ /* non-consecutive inputs for OR gate 1 */
+ for (unsigned j = 0; j < EXTI_OR_GATE1_NUM_LINES_IN; j++) {
+ sysbus_connect_irq(SYS_BUS_DEVICE(&s->exti),
+ exti_or_gate1_lines_in[j],
+ qdev_get_gpio_in(DEVICE(&s->exti_or_gates[i]), j));
+ }
+ }
+ }
+
+ /* IRQs that don't require fan-in */
for (unsigned i = 0; i < NUM_EXTI_IRQ; i++) {
- sysbus_connect_irq(busdev, i, qdev_get_gpio_in(armv7m, exti_irq[i]));
+ if (exti_irq[i] != -1) {
+ sysbus_connect_irq(busdev, i,
+ qdev_get_gpio_in(armv7m, exti_irq[i]));
+ }
}
for (unsigned i = 0; i < 16; i++) {
diff --git a/hw/arm/trace-events b/hw/arm/trace-events
index fd0d927..f1a54a0 100644
--- a/hw/arm/trace-events
+++ b/hw/arm/trace-events
@@ -70,3 +70,6 @@ z2_aer915_event(int8_t event, int8_t len) "i2c event =0x%x len=%d bytes"
xen_create_virtio_mmio_devices(int i, int irq, uint64_t base) "Created virtio-mmio device %d: irq %d base 0x%"PRIx64
xen_init_ram(uint64_t machine_ram_size) "Initialized xen ram with size 0x%"PRIx64
xen_enable_tpm(uint64_t addr) "Connected tpmdev at address 0x%"PRIx64
+
+# bcm2838.c
+bcm2838_gic_set_irq(int irq, int level) "gic irq:%d lvl:%d"
diff --git a/hw/arm/xlnx-versal-virt.c b/hw/arm/xlnx-versal-virt.c
index 94942c5..bfaed1a 100644
--- a/hw/arm/xlnx-versal-virt.c
+++ b/hw/arm/xlnx-versal-virt.c
@@ -49,6 +49,7 @@ struct VersalVirt {
struct {
bool secure;
} cfg;
+ char *ospi_model;
};
static void fdt_create(VersalVirt *s)
@@ -638,6 +639,22 @@ static void sd_plugin_card(SDHCIState *sd, DriveInfo *di)
&error_fatal);
}
+static char *versal_get_ospi_model(Object *obj, Error **errp)
+{
+ VersalVirt *s = XLNX_VERSAL_VIRT_MACHINE(obj);
+
+ return g_strdup(s->ospi_model);
+}
+
+static void versal_set_ospi_model(Object *obj, const char *value, Error **errp)
+{
+ VersalVirt *s = XLNX_VERSAL_VIRT_MACHINE(obj);
+
+ g_free(s->ospi_model);
+ s->ospi_model = g_strdup(value);
+}
+
+
static void versal_virt_init(MachineState *machine)
{
VersalVirt *s = XLNX_VERSAL_VIRT_MACHINE(machine);
@@ -732,12 +749,25 @@ static void versal_virt_init(MachineState *machine)
for (i = 0; i < XLNX_VERSAL_NUM_OSPI_FLASH; i++) {
BusState *spi_bus;
DeviceState *flash_dev;
+ ObjectClass *flash_klass;
qemu_irq cs_line;
DriveInfo *dinfo = drive_get(IF_MTD, 0, i);
spi_bus = qdev_get_child_bus(DEVICE(&s->soc.pmc.iou.ospi), "spi0");
- flash_dev = qdev_new("mt35xu01g");
+ if (s->ospi_model) {
+ flash_klass = object_class_by_name(s->ospi_model);
+ if (!flash_klass ||
+ object_class_is_abstract(flash_klass) ||
+ !object_class_dynamic_cast(flash_klass, "m25p80-generic")) {
+ error_setg(&error_fatal, "'%s' is either abstract or"
+ " not a subtype of m25p80", s->ospi_model);
+ return;
+ }
+ }
+
+ flash_dev = qdev_new(s->ospi_model ? s->ospi_model : "mt35xu01g");
+
if (dinfo) {
qdev_prop_set_drive_err(flash_dev, "drive",
blk_by_legacy_dinfo(dinfo), &error_fatal);
@@ -770,6 +800,13 @@ static void versal_virt_machine_instance_init(Object *obj)
0);
}
+static void versal_virt_machine_finalize(Object *obj)
+{
+ VersalVirt *s = XLNX_VERSAL_VIRT_MACHINE(obj);
+
+ g_free(s->ospi_model);
+}
+
static void versal_virt_machine_class_init(ObjectClass *oc, void *data)
{
MachineClass *mc = MACHINE_CLASS(oc);
@@ -781,6 +818,10 @@ static void versal_virt_machine_class_init(ObjectClass *oc, void *data)
mc->default_cpus = XLNX_VERSAL_NR_ACPUS + XLNX_VERSAL_NR_RCPUS;
mc->no_cdrom = true;
mc->default_ram_id = "ddr";
+ object_class_property_add_str(oc, "ospi-flash", versal_get_ospi_model,
+ versal_set_ospi_model);
+ object_class_property_set_description(oc, "ospi-flash",
+ "Change the OSPI Flash model");
}
static const TypeInfo versal_virt_machine_init_typeinfo = {
@@ -789,6 +830,7 @@ static const TypeInfo versal_virt_machine_init_typeinfo = {
.class_init = versal_virt_machine_class_init,
.instance_init = versal_virt_machine_instance_init,
.instance_size = sizeof(VersalVirt),
+ .instance_finalize = versal_virt_machine_finalize,
};
static void versal_virt_machine_init_register_types(void)
diff --git a/hw/block/m25p80.c b/hw/block/m25p80.c
index 0a12030..08a00a6 100644
--- a/hw/block/m25p80.c
+++ b/hw/block/m25p80.c
@@ -267,6 +267,9 @@ static const FlashPartInfo known_devices[] = {
{ INFO("mt25ql512ab", 0x20ba20, 0x1044, 64 << 10, 1024, ER_4K | ER_32K) },
{ INFO_STACKED("mt35xu01g", 0x2c5b1b, 0x104100, 128 << 10, 1024,
ER_4K | ER_32K, 2) },
+ { INFO_STACKED("mt35xu02gbba", 0x2c5b1c, 0x104100, 128 << 10, 2048,
+ ER_4K | ER_32K, 4),
+ .sfdp_read = m25p80_sfdp_mt35xu02g },
{ INFO_STACKED("n25q00", 0x20ba21, 0x1000, 64 << 10, 2048, ER_4K, 4) },
{ INFO_STACKED("n25q00a", 0x20bb21, 0x1000, 64 << 10, 2048, ER_4K, 4) },
{ INFO_STACKED("mt25ql01g", 0x20ba21, 0x1040, 64 << 10, 2048, ER_4K, 2) },
diff --git a/hw/block/m25p80_sfdp.c b/hw/block/m25p80_sfdp.c
index b33811a..6ee2cfa 100644
--- a/hw/block/m25p80_sfdp.c
+++ b/hw/block/m25p80_sfdp.c
@@ -57,6 +57,42 @@ static const uint8_t sfdp_n25q256a[] = {
};
define_sfdp_read(n25q256a);
+static const uint8_t sfdp_mt35xu02g[] = {
+ 0x53, 0x46, 0x44, 0x50, 0x06, 0x01, 0x01, 0xff,
+ 0x00, 0x06, 0x01, 0x10, 0x30, 0x00, 0x00, 0xff,
+ 0x84, 0x00, 0x01, 0x02, 0x80, 0x00, 0x00, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xe5, 0x20, 0x8a, 0xff, 0xff, 0xff, 0xff, 0x7f,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xee, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00,
+ 0xff, 0xff, 0x00, 0x00, 0x0c, 0x20, 0x11, 0xd8,
+ 0x0f, 0x52, 0x00, 0x00, 0x24, 0x5a, 0x99, 0x00,
+ 0x8b, 0x8e, 0x03, 0xe1, 0xac, 0x01, 0x27, 0x38,
+ 0x7a, 0x75, 0x7a, 0x75, 0xfb, 0xbd, 0xd5, 0x5c,
+ 0x00, 0x00, 0x70, 0xff, 0x81, 0xb0, 0x38, 0x36,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0x43, 0x0e, 0xff, 0xff, 0x21, 0xdc, 0x5c, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+};
+
+define_sfdp_read(mt35xu02g);
/*
* Matronix
diff --git a/hw/block/m25p80_sfdp.h b/hw/block/m25p80_sfdp.h
index 011a880..1733b56 100644
--- a/hw/block/m25p80_sfdp.h
+++ b/hw/block/m25p80_sfdp.h
@@ -16,6 +16,7 @@
#define M25P80_SFDP_MAX_SIZE (1 << 24)
uint8_t m25p80_sfdp_n25q256a(uint32_t addr);
+uint8_t m25p80_sfdp_mt35xu02g(uint32_t addr);
uint8_t m25p80_sfdp_mx25l25635e(uint32_t addr);
uint8_t m25p80_sfdp_mx25l25635f(uint32_t addr);
diff --git a/hw/core/machine.c b/hw/core/machine.c
index fb5afdc..9ac5d53 100644
--- a/hw/core/machine.c
+++ b/hw/core/machine.c
@@ -1577,14 +1577,13 @@ void qdev_machine_creation_done(void)
/* TODO: once all bus devices are qdevified, this should be done
* when bus is created by qdev.c */
/*
- * TODO: If we had a main 'reset container' that the whole system
- * lived in, we could reset that using the multi-phase reset
- * APIs. For the moment, we just reset the sysbus, which will cause
+ * This is where we arrange for the sysbus to be reset when the
+ * whole simulation is reset. In turn, resetting the sysbus will cause
* all devices hanging off it (and all their child buses, recursively)
* to be reset. Note that this will *not* reset any Device objects
* which are not attached to some part of the qbus tree!
*/
- qemu_register_reset(resettable_cold_reset_fn, sysbus_get_default());
+ qemu_register_resettable(OBJECT(sysbus_get_default()));
notifier_list_notify(&machine_init_done_notifiers, NULL);
diff --git a/hw/core/meson.build b/hw/core/meson.build
index 67dad04..e26f2e0 100644
--- a/hw/core/meson.build
+++ b/hw/core/meson.build
@@ -4,6 +4,7 @@ hwcore_ss.add(files(
'qdev-properties.c',
'qdev.c',
'reset.c',
+ 'resetcontainer.c',
'resettable.c',
'vmstate-if.c',
# irq.c needed for qdev GPIO handling:
diff --git a/hw/core/reset.c b/hw/core/reset.c
index d3263b6..d50da7e 100644
--- a/hw/core/reset.c
+++ b/hw/core/reset.c
@@ -24,64 +24,164 @@
*/
#include "qemu/osdep.h"
-#include "qemu/queue.h"
#include "sysemu/reset.h"
+#include "hw/resettable.h"
+#include "hw/core/resetcontainer.h"
-/* reset/shutdown handler */
+/*
+ * Return a pointer to the singleton container that holds all the Resettable
+ * items that will be reset when qemu_devices_reset() is called.
+ */
+static ResettableContainer *get_root_reset_container(void)
+{
+ static ResettableContainer *root_reset_container;
-typedef struct QEMUResetEntry {
- QTAILQ_ENTRY(QEMUResetEntry) entry;
+ if (!root_reset_container) {
+ root_reset_container =
+ RESETTABLE_CONTAINER(object_new(TYPE_RESETTABLE_CONTAINER));
+ }
+ return root_reset_container;
+}
+
+/*
+ * Reason why the currently in-progress qemu_devices_reset() was called.
+ * If we made at least SHUTDOWN_CAUSE_SNAPSHOT_LOAD have a corresponding
+ * ResetType we could perhaps avoid the need for this global.
+ */
+static ShutdownCause device_reset_reason;
+
+/*
+ * This is an Object which implements Resettable simply to call the
+ * callback function in the hold phase.
+ */
+#define TYPE_LEGACY_RESET "legacy-reset"
+OBJECT_DECLARE_SIMPLE_TYPE(LegacyReset, LEGACY_RESET)
+
+struct LegacyReset {
+ Object parent;
+ ResettableState reset_state;
QEMUResetHandler *func;
void *opaque;
bool skip_on_snapshot_load;
-} QEMUResetEntry;
+};
+
+OBJECT_DEFINE_SIMPLE_TYPE_WITH_INTERFACES(LegacyReset, legacy_reset, LEGACY_RESET, OBJECT, { TYPE_RESETTABLE_INTERFACE }, { })
+
+static ResettableState *legacy_reset_get_state(Object *obj)
+{
+ LegacyReset *lr = LEGACY_RESET(obj);
+ return &lr->reset_state;
+}
+
+static void legacy_reset_hold(Object *obj)
+{
+ LegacyReset *lr = LEGACY_RESET(obj);
+
+ if (device_reset_reason == SHUTDOWN_CAUSE_SNAPSHOT_LOAD &&
+ lr->skip_on_snapshot_load) {
+ return;
+ }
+ lr->func(lr->opaque);
+}
+
+static void legacy_reset_init(Object *obj)
+{
+}
+
+static void legacy_reset_finalize(Object *obj)
+{
+}
+
+static void legacy_reset_class_init(ObjectClass *klass, void *data)
+{
+ ResettableClass *rc = RESETTABLE_CLASS(klass);
-static QTAILQ_HEAD(, QEMUResetEntry) reset_handlers =
- QTAILQ_HEAD_INITIALIZER(reset_handlers);
+ rc->get_state = legacy_reset_get_state;
+ rc->phases.hold = legacy_reset_hold;
+}
void qemu_register_reset(QEMUResetHandler *func, void *opaque)
{
- QEMUResetEntry *re = g_new0(QEMUResetEntry, 1);
+ Object *obj = object_new(TYPE_LEGACY_RESET);
+ LegacyReset *lr = LEGACY_RESET(obj);
- re->func = func;
- re->opaque = opaque;
- QTAILQ_INSERT_TAIL(&reset_handlers, re, entry);
+ lr->func = func;
+ lr->opaque = opaque;
+ qemu_register_resettable(obj);
}
void qemu_register_reset_nosnapshotload(QEMUResetHandler *func, void *opaque)
{
- QEMUResetEntry *re = g_new0(QEMUResetEntry, 1);
+ Object *obj = object_new(TYPE_LEGACY_RESET);
+ LegacyReset *lr = LEGACY_RESET(obj);
- re->func = func;
- re->opaque = opaque;
- re->skip_on_snapshot_load = true;
- QTAILQ_INSERT_TAIL(&reset_handlers, re, entry);
+ lr->func = func;
+ lr->opaque = opaque;
+ lr->skip_on_snapshot_load = true;
+ qemu_register_resettable(obj);
}
-void qemu_unregister_reset(QEMUResetHandler *func, void *opaque)
+typedef struct FindLegacyInfo {
+ QEMUResetHandler *func;
+ void *opaque;
+ LegacyReset *lr;
+} FindLegacyInfo;
+
+static void find_legacy_reset_cb(Object *obj, void *opaque, ResetType type)
{
- QEMUResetEntry *re;
-
- QTAILQ_FOREACH(re, &reset_handlers, entry) {
- if (re->func == func && re->opaque == opaque) {
- QTAILQ_REMOVE(&reset_handlers, re, entry);
- g_free(re);
- return;
- }
+ LegacyReset *lr;
+ FindLegacyInfo *fli = opaque;
+
+ /* Not everything in the ResettableContainer will be a LegacyReset */
+ lr = LEGACY_RESET(object_dynamic_cast(obj, TYPE_LEGACY_RESET));
+ if (lr && lr->func == fli->func && lr->opaque == fli->opaque) {
+ fli->lr = lr;
}
}
-void qemu_devices_reset(ShutdownCause reason)
+static LegacyReset *find_legacy_reset(QEMUResetHandler *func, void *opaque)
+{
+ /*
+ * Find the LegacyReset with the specified func and opaque,
+ * by getting the ResettableContainer to call our callback for
+ * every item in it.
+ */
+ ResettableContainer *rootcon = get_root_reset_container();
+ ResettableClass *rc = RESETTABLE_GET_CLASS(rootcon);
+ FindLegacyInfo fli;
+
+ fli.func = func;
+ fli.opaque = opaque;
+ fli.lr = NULL;
+ rc->child_foreach(OBJECT(rootcon), find_legacy_reset_cb,
+ &fli, RESET_TYPE_COLD);
+ return fli.lr;
+}
+
+void qemu_unregister_reset(QEMUResetHandler *func, void *opaque)
{
- QEMUResetEntry *re, *nre;
-
- /* reset all devices */
- QTAILQ_FOREACH_SAFE(re, &reset_handlers, entry, nre) {
- if (reason == SHUTDOWN_CAUSE_SNAPSHOT_LOAD &&
- re->skip_on_snapshot_load) {
- continue;
- }
- re->func(re->opaque);
+ Object *obj = OBJECT(find_legacy_reset(func, opaque));
+
+ if (obj) {
+ qemu_unregister_resettable(obj);
+ object_unref(obj);
}
}
+void qemu_register_resettable(Object *obj)
+{
+ resettable_container_add(get_root_reset_container(), obj);
+}
+
+void qemu_unregister_resettable(Object *obj)
+{
+ resettable_container_remove(get_root_reset_container(), obj);
+}
+
+void qemu_devices_reset(ShutdownCause reason)
+{
+ device_reset_reason = reason;
+
+ /* Reset the simulation */
+ resettable_reset(OBJECT(get_root_reset_container()), RESET_TYPE_COLD);
+}
diff --git a/hw/core/resetcontainer.c b/hw/core/resetcontainer.c
new file mode 100644
index 0000000..e4ece68
--- /dev/null
+++ b/hw/core/resetcontainer.c
@@ -0,0 +1,77 @@
+/*
+ * Reset container
+ *
+ * Copyright (c) 2024 Linaro, Ltd
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+/*
+ * The "reset container" is an object which implements the Resettable
+ * interface. It contains a list of arbitrary other objects which also
+ * implement Resettable. Resetting the reset container resets all the
+ * objects in it.
+ */
+
+#include "qemu/osdep.h"
+#include "hw/resettable.h"
+#include "hw/core/resetcontainer.h"
+
+struct ResettableContainer {
+ Object parent;
+ ResettableState reset_state;
+ GPtrArray *children;
+};
+
+OBJECT_DEFINE_SIMPLE_TYPE_WITH_INTERFACES(ResettableContainer, resettable_container, RESETTABLE_CONTAINER, OBJECT, { TYPE_RESETTABLE_INTERFACE }, { })
+
+void resettable_container_add(ResettableContainer *rc, Object *obj)
+{
+ INTERFACE_CHECK(void, obj, TYPE_RESETTABLE_INTERFACE);
+ g_ptr_array_add(rc->children, obj);
+}
+
+void resettable_container_remove(ResettableContainer *rc, Object *obj)
+{
+ g_ptr_array_remove(rc->children, obj);
+}
+
+static ResettableState *resettable_container_get_state(Object *obj)
+{
+ ResettableContainer *rc = RESETTABLE_CONTAINER(obj);
+ return &rc->reset_state;
+}
+
+static void resettable_container_child_foreach(Object *obj,
+ ResettableChildCallback cb,
+ void *opaque, ResetType type)
+{
+ ResettableContainer *rc = RESETTABLE_CONTAINER(obj);
+ unsigned int len = rc->children->len;
+
+ for (unsigned int i = 0; i < len; i++) {
+ cb(g_ptr_array_index(rc->children, i), opaque, type);
+ /* Detect callbacks trying to unregister themselves */
+ assert(len == rc->children->len);
+ }
+}
+
+static void resettable_container_init(Object *obj)
+{
+ ResettableContainer *rc = RESETTABLE_CONTAINER(obj);
+
+ rc->children = g_ptr_array_new();
+}
+
+static void resettable_container_finalize(Object *obj)
+{
+}
+
+static void resettable_container_class_init(ObjectClass *klass, void *data)
+{
+ ResettableClass *rc = RESETTABLE_CLASS(klass);
+
+ rc->get_state = resettable_container_get_state;
+ rc->child_foreach = resettable_container_child_foreach;
+}
diff --git a/hw/gpio/bcm2838_gpio.c b/hw/gpio/bcm2838_gpio.c
new file mode 100644
index 0000000..2ddf62f
--- /dev/null
+++ b/hw/gpio/bcm2838_gpio.c
@@ -0,0 +1,390 @@
+/*
+ * Raspberry Pi (BCM2838) GPIO Controller
+ * This implementation is based on bcm2835_gpio (hw/gpio/bcm2835_gpio.c)
+ *
+ * Copyright (c) 2022 Auriga LLC
+ *
+ * Authors:
+ * Lotosh, Aleksey <aleksey.lotosh@auriga.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/log.h"
+#include "qemu/module.h"
+#include "qemu/timer.h"
+#include "qapi/error.h"
+#include "hw/sysbus.h"
+#include "migration/vmstate.h"
+#include "hw/sd/sd.h"
+#include "hw/gpio/bcm2838_gpio.h"
+#include "hw/irq.h"
+
+#define GPFSEL0 0x00
+#define GPFSEL1 0x04
+#define GPFSEL2 0x08
+#define GPFSEL3 0x0C
+#define GPFSEL4 0x10
+#define GPFSEL5 0x14
+#define GPSET0 0x1C
+#define GPSET1 0x20
+#define GPCLR0 0x28
+#define GPCLR1 0x2C
+#define GPLEV0 0x34
+#define GPLEV1 0x38
+#define GPEDS0 0x40
+#define GPEDS1 0x44
+#define GPREN0 0x4C
+#define GPREN1 0x50
+#define GPFEN0 0x58
+#define GPFEN1 0x5C
+#define GPHEN0 0x64
+#define GPHEN1 0x68
+#define GPLEN0 0x70
+#define GPLEN1 0x74
+#define GPAREN0 0x7C
+#define GPAREN1 0x80
+#define GPAFEN0 0x88
+#define GPAFEN1 0x8C
+
+#define GPIO_PUP_PDN_CNTRL_REG0 0xE4
+#define GPIO_PUP_PDN_CNTRL_REG1 0xE8
+#define GPIO_PUP_PDN_CNTRL_REG2 0xEC
+#define GPIO_PUP_PDN_CNTRL_REG3 0xF0
+
+#define RESET_VAL_CNTRL_REG0 0xAAA95555
+#define RESET_VAL_CNTRL_REG1 0xA0AAAAAA
+#define RESET_VAL_CNTRL_REG2 0x50AAA95A
+#define RESET_VAL_CNTRL_REG3 0x00055555
+
+#define NUM_FSELN_IN_GPFSELN 10
+#define NUM_BITS_FSELN 3
+#define MASK_FSELN 0x7
+
+#define BYTES_IN_WORD 4
+
+/* bcm,function property */
+#define BCM2838_FSEL_GPIO_IN 0
+#define BCM2838_FSEL_GPIO_OUT 1
+#define BCM2838_FSEL_ALT5 2
+#define BCM2838_FSEL_ALT4 3
+#define BCM2838_FSEL_ALT0 4
+#define BCM2838_FSEL_ALT1 5
+#define BCM2838_FSEL_ALT2 6
+#define BCM2838_FSEL_ALT3 7
+
+static uint32_t gpfsel_get(BCM2838GpioState *s, uint8_t reg)
+{
+ int i;
+ uint32_t value = 0;
+ for (i = 0; i < NUM_FSELN_IN_GPFSELN; i++) {
+ uint32_t index = NUM_FSELN_IN_GPFSELN * reg + i;
+ if (index < sizeof(s->fsel)) {
+ value |= (s->fsel[index] & MASK_FSELN) << (NUM_BITS_FSELN * i);
+ }
+ }
+ return value;
+}
+
+static void gpfsel_set(BCM2838GpioState *s, uint8_t reg, uint32_t value)
+{
+ int i;
+ for (i = 0; i < NUM_FSELN_IN_GPFSELN; i++) {
+ uint32_t index = NUM_FSELN_IN_GPFSELN * reg + i;
+ if (index < sizeof(s->fsel)) {
+ int fsel = (value >> (NUM_BITS_FSELN * i)) & MASK_FSELN;
+ s->fsel[index] = fsel;
+ }
+ }
+
+ /* SD controller selection (48-53) */
+ if (s->sd_fsel != BCM2838_FSEL_GPIO_IN
+ && (s->fsel[48] == BCM2838_FSEL_GPIO_IN)
+ && (s->fsel[49] == BCM2838_FSEL_GPIO_IN)
+ && (s->fsel[50] == BCM2838_FSEL_GPIO_IN)
+ && (s->fsel[51] == BCM2838_FSEL_GPIO_IN)
+ && (s->fsel[52] == BCM2838_FSEL_GPIO_IN)
+ && (s->fsel[53] == BCM2838_FSEL_GPIO_IN)
+ ) {
+ /* SDHCI controller selected */
+ sdbus_reparent_card(s->sdbus_sdhost, s->sdbus_sdhci);
+ s->sd_fsel = BCM2838_FSEL_GPIO_IN;
+ } else if (s->sd_fsel != BCM2838_FSEL_ALT0
+ && (s->fsel[48] == BCM2838_FSEL_ALT0) /* SD_CLK_R */
+ && (s->fsel[49] == BCM2838_FSEL_ALT0) /* SD_CMD_R */
+ && (s->fsel[50] == BCM2838_FSEL_ALT0) /* SD_DATA0_R */
+ && (s->fsel[51] == BCM2838_FSEL_ALT0) /* SD_DATA1_R */
+ && (s->fsel[52] == BCM2838_FSEL_ALT0) /* SD_DATA2_R */
+ && (s->fsel[53] == BCM2838_FSEL_ALT0) /* SD_DATA3_R */
+ ) {
+ /* SDHost controller selected */
+ sdbus_reparent_card(s->sdbus_sdhci, s->sdbus_sdhost);
+ s->sd_fsel = BCM2838_FSEL_ALT0;
+ }
+}
+
+static int gpfsel_is_out(BCM2838GpioState *s, int index)
+{
+ if (index >= 0 && index < BCM2838_GPIO_NUM) {
+ return s->fsel[index] == 1;
+ }
+ return 0;
+}
+
+static void gpset(BCM2838GpioState *s, uint32_t val, uint8_t start,
+ uint8_t count, uint32_t *lev)
+{
+ uint32_t changes = val & ~*lev;
+ uint32_t cur = 1;
+
+ int i;
+ for (i = 0; i < count; i++) {
+ if ((changes & cur) && (gpfsel_is_out(s, start + i))) {
+ qemu_set_irq(s->out[start + i], 1);
+ }
+ cur <<= 1;
+ }
+
+ *lev |= val;
+}
+
+static void gpclr(BCM2838GpioState *s, uint32_t val, uint8_t start,
+ uint8_t count, uint32_t *lev)
+{
+ uint32_t changes = val & *lev;
+ uint32_t cur = 1;
+
+ int i;
+ for (i = 0; i < count; i++) {
+ if ((changes & cur) && (gpfsel_is_out(s, start + i))) {
+ qemu_set_irq(s->out[start + i], 0);
+ }
+ cur <<= 1;
+ }
+
+ *lev &= ~val;
+}
+
+static uint64_t bcm2838_gpio_read(void *opaque, hwaddr offset, unsigned size)
+{
+ BCM2838GpioState *s = (BCM2838GpioState *)opaque;
+ uint64_t value = 0;
+
+ switch (offset) {
+ case GPFSEL0:
+ case GPFSEL1:
+ case GPFSEL2:
+ case GPFSEL3:
+ case GPFSEL4:
+ case GPFSEL5:
+ value = gpfsel_get(s, offset / BYTES_IN_WORD);
+ break;
+ case GPSET0:
+ case GPSET1:
+ case GPCLR0:
+ case GPCLR1:
+ /* Write Only */
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: %s: Attempt reading from write only"
+ " register. 0x%"PRIx64" will be returned."
+ " Address 0x%"HWADDR_PRIx", size %u\n",
+ TYPE_BCM2838_GPIO, __func__, value, offset, size);
+ break;
+ case GPLEV0:
+ value = s->lev0;
+ break;
+ case GPLEV1:
+ value = s->lev1;
+ break;
+ case GPEDS0:
+ case GPEDS1:
+ case GPREN0:
+ case GPREN1:
+ case GPFEN0:
+ case GPFEN1:
+ case GPHEN0:
+ case GPHEN1:
+ case GPLEN0:
+ case GPLEN1:
+ case GPAREN0:
+ case GPAREN1:
+ case GPAFEN0:
+ case GPAFEN1:
+ /* Not implemented */
+ qemu_log_mask(LOG_UNIMP, "%s: %s: not implemented for %"HWADDR_PRIx"\n",
+ TYPE_BCM2838_GPIO, __func__, offset);
+ break;
+ case GPIO_PUP_PDN_CNTRL_REG0:
+ case GPIO_PUP_PDN_CNTRL_REG1:
+ case GPIO_PUP_PDN_CNTRL_REG2:
+ case GPIO_PUP_PDN_CNTRL_REG3:
+ value = s->pup_cntrl_reg[(offset - GPIO_PUP_PDN_CNTRL_REG0)
+ / sizeof(s->pup_cntrl_reg[0])];
+ break;
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: %s: bad offset %"HWADDR_PRIx"\n",
+ TYPE_BCM2838_GPIO, __func__, offset);
+ break;
+ }
+
+ return value;
+}
+
+static void bcm2838_gpio_write(void *opaque, hwaddr offset, uint64_t value,
+ unsigned size)
+{
+ BCM2838GpioState *s = (BCM2838GpioState *)opaque;
+
+ switch (offset) {
+ case GPFSEL0:
+ case GPFSEL1:
+ case GPFSEL2:
+ case GPFSEL3:
+ case GPFSEL4:
+ case GPFSEL5:
+ gpfsel_set(s, offset / BYTES_IN_WORD, value);
+ break;
+ case GPSET0:
+ gpset(s, value, 0, 32, &s->lev0);
+ break;
+ case GPSET1:
+ gpset(s, value, 32, 22, &s->lev1);
+ break;
+ case GPCLR0:
+ gpclr(s, value, 0, 32, &s->lev0);
+ break;
+ case GPCLR1:
+ gpclr(s, value, 32, 22, &s->lev1);
+ break;
+ case GPLEV0:
+ case GPLEV1:
+ /* Read Only */
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: %s: Attempt writing 0x%"PRIx64""
+ " to read only register. Ignored."
+ " Address 0x%"HWADDR_PRIx", size %u\n",
+ TYPE_BCM2838_GPIO, __func__, value, offset, size);
+ break;
+ case GPEDS0:
+ case GPEDS1:
+ case GPREN0:
+ case GPREN1:
+ case GPFEN0:
+ case GPFEN1:
+ case GPHEN0:
+ case GPHEN1:
+ case GPLEN0:
+ case GPLEN1:
+ case GPAREN0:
+ case GPAREN1:
+ case GPAFEN0:
+ case GPAFEN1:
+ /* Not implemented */
+ qemu_log_mask(LOG_UNIMP, "%s: %s: not implemented for %"HWADDR_PRIx"\n",
+ TYPE_BCM2838_GPIO, __func__, offset);
+ break;
+ case GPIO_PUP_PDN_CNTRL_REG0:
+ case GPIO_PUP_PDN_CNTRL_REG1:
+ case GPIO_PUP_PDN_CNTRL_REG2:
+ case GPIO_PUP_PDN_CNTRL_REG3:
+ s->pup_cntrl_reg[(offset - GPIO_PUP_PDN_CNTRL_REG0)
+ / sizeof(s->pup_cntrl_reg[0])] = value;
+ break;
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: %s: bad offset %"HWADDR_PRIx"\n",
+ TYPE_BCM2838_GPIO, __func__, offset);
+ }
+ return;
+}
+
+static void bcm2838_gpio_reset(DeviceState *dev)
+{
+ BCM2838GpioState *s = BCM2838_GPIO(dev);
+
+ memset(s->fsel, 0, sizeof(s->fsel));
+
+ s->sd_fsel = 0;
+
+ /* SDHCI is selected by default */
+ sdbus_reparent_card(&s->sdbus, s->sdbus_sdhci);
+
+ s->lev0 = 0;
+ s->lev1 = 0;
+
+ memset(s->fsel, 0, sizeof(s->fsel));
+
+ s->pup_cntrl_reg[0] = RESET_VAL_CNTRL_REG0;
+ s->pup_cntrl_reg[1] = RESET_VAL_CNTRL_REG1;
+ s->pup_cntrl_reg[2] = RESET_VAL_CNTRL_REG2;
+ s->pup_cntrl_reg[3] = RESET_VAL_CNTRL_REG3;
+}
+
+static const MemoryRegionOps bcm2838_gpio_ops = {
+ .read = bcm2838_gpio_read,
+ .write = bcm2838_gpio_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static const VMStateDescription vmstate_bcm2838_gpio = {
+ .name = "bcm2838_gpio",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT8_ARRAY(fsel, BCM2838GpioState, BCM2838_GPIO_NUM),
+ VMSTATE_UINT32(lev0, BCM2838GpioState),
+ VMSTATE_UINT32(lev1, BCM2838GpioState),
+ VMSTATE_UINT8(sd_fsel, BCM2838GpioState),
+ VMSTATE_UINT32_ARRAY(pup_cntrl_reg, BCM2838GpioState,
+ GPIO_PUP_PDN_CNTRL_NUM),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static void bcm2838_gpio_init(Object *obj)
+{
+ BCM2838GpioState *s = BCM2838_GPIO(obj);
+ DeviceState *dev = DEVICE(obj);
+ SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
+
+ qbus_init(&s->sdbus, sizeof(s->sdbus), TYPE_SD_BUS, DEVICE(s), "sd-bus");
+
+ memory_region_init_io(&s->iomem, obj, &bcm2838_gpio_ops, s,
+ "bcm2838_gpio", BCM2838_GPIO_REGS_SIZE);
+ sysbus_init_mmio(sbd, &s->iomem);
+ qdev_init_gpio_out(dev, s->out, BCM2838_GPIO_NUM);
+}
+
+static void bcm2838_gpio_realize(DeviceState *dev, Error **errp)
+{
+ BCM2838GpioState *s = BCM2838_GPIO(dev);
+ Object *obj;
+
+ obj = object_property_get_link(OBJECT(dev), "sdbus-sdhci", &error_abort);
+ s->sdbus_sdhci = SD_BUS(obj);
+
+ obj = object_property_get_link(OBJECT(dev), "sdbus-sdhost", &error_abort);
+ s->sdbus_sdhost = SD_BUS(obj);
+}
+
+static void bcm2838_gpio_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+
+ dc->vmsd = &vmstate_bcm2838_gpio;
+ dc->realize = &bcm2838_gpio_realize;
+ dc->reset = &bcm2838_gpio_reset;
+}
+
+static const TypeInfo bcm2838_gpio_info = {
+ .name = TYPE_BCM2838_GPIO,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(BCM2838GpioState),
+ .instance_init = bcm2838_gpio_init,
+ .class_init = bcm2838_gpio_class_init,
+};
+
+static void bcm2838_gpio_register_types(void)
+{
+ type_register_static(&bcm2838_gpio_info);
+}
+
+type_init(bcm2838_gpio_register_types)
diff --git a/hw/gpio/meson.build b/hw/gpio/meson.build
index 066ea96..8a8d03d 100644
--- a/hw/gpio/meson.build
+++ b/hw/gpio/meson.build
@@ -9,6 +9,9 @@ system_ss.add(when: 'CONFIG_IMX', if_true: files('imx_gpio.c'))
system_ss.add(when: 'CONFIG_NPCM7XX', if_true: files('npcm7xx_gpio.c'))
system_ss.add(when: 'CONFIG_NRF51_SOC', if_true: files('nrf51_gpio.c'))
system_ss.add(when: 'CONFIG_OMAP', if_true: files('omap_gpio.c'))
-system_ss.add(when: 'CONFIG_RASPI', if_true: files('bcm2835_gpio.c'))
+system_ss.add(when: 'CONFIG_RASPI', if_true: files(
+ 'bcm2835_gpio.c',
+ 'bcm2838_gpio.c'
+))
system_ss.add(when: 'CONFIG_ASPEED_SOC', if_true: files('aspeed_gpio.c'))
system_ss.add(when: 'CONFIG_SIFIVE_GPIO', if_true: files('sifive_gpio.c'))
diff --git a/hw/misc/bcm2835_property.c b/hw/misc/bcm2835_property.c
index 5c48f8d..bdd9a6b 100644
--- a/hw/misc/bcm2835_property.c
+++ b/hw/misc/bcm2835_property.c
@@ -19,6 +19,8 @@
#include "trace.h"
#include "hw/arm/raspi_platform.h"
+#define VCHI_BUSADDR_SIZE sizeof(uint32_t)
+
/* https://github.com/raspberrypi/firmware/wiki/Mailbox-property-interface */
static void bcm2835_property_mbox_push(BCM2835PropertyState *s, uint32_t value)
@@ -138,6 +140,13 @@ static void bcm2835_property_mbox_push(BCM2835PropertyState *s, uint32_t value)
resplen = 8;
break;
+ case RPI_FWREQ_GET_CLOCKS:
+ /* TODO: add more clock IDs if needed */
+ stl_le_phys(&s->dma_as, value + 12, 0);
+ stl_le_phys(&s->dma_as, value + 16, RPI_FIRMWARE_ARM_CLK_ID);
+ resplen = 8;
+ break;
+
case RPI_FWREQ_SET_CLOCK_RATE:
case RPI_FWREQ_SET_MAX_CLOCK_RATE:
case RPI_FWREQ_SET_MIN_CLOCK_RATE:
@@ -276,6 +285,7 @@ static void bcm2835_property_mbox_push(BCM2835PropertyState *s, uint32_t value)
stl_le_phys(&s->dma_as, value + 12, 0);
resplen = 4;
break;
+
case RPI_FWREQ_FRAMEBUFFER_GET_NUM_DISPLAYS:
stl_le_phys(&s->dma_as, value + 12, 1);
resplen = 4;
@@ -301,6 +311,17 @@ static void bcm2835_property_mbox_push(BCM2835PropertyState *s, uint32_t value)
resplen);
break;
+ case RPI_FWREQ_GET_THROTTLED:
+ stl_le_phys(&s->dma_as, value + 12, 0);
+ resplen = 4;
+ break;
+
+ case RPI_FWREQ_VCHIQ_INIT:
+ stl_le_phys(&s->dma_as,
+ value + offsetof(rpi_firmware_prop_request_t, payload),
+ 0);
+ resplen = VCHI_BUSADDR_SIZE;
+ break;
default:
qemu_log_mask(LOG_UNIMP,
"bcm2835_property: unhandled tag 0x%08x\n", tag);
diff --git a/hw/rtc/m48t59.c b/hw/rtc/m48t59.c
index aa44c4b..1585a2d 100644
--- a/hw/rtc/m48t59.c
+++ b/hw/rtc/m48t59.c
@@ -36,6 +36,7 @@
#include "qemu/bcd.h"
#include "qemu/module.h"
#include "trace.h"
+#include "sysemu/watchdog.h"
#include "m48t59-internal.h"
#include "migration/vmstate.h"
@@ -163,8 +164,7 @@ static void watchdog_cb (void *opaque)
if (NVRAM->buffer[0x1FF7] & 0x80) {
NVRAM->buffer[0x1FF7] = 0x00;
NVRAM->buffer[0x1FFC] &= ~0x40;
- /* May it be a hw CPU Reset instead ? */
- qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET);
+ watchdog_perform_action();
} else {
qemu_set_irq(NVRAM->IRQ, 1);
qemu_set_irq(NVRAM->IRQ, 0);
diff --git a/hw/rtc/pl031.c b/hw/rtc/pl031.c
index 837b0bd..563bb4b 100644
--- a/hw/rtc/pl031.c
+++ b/hw/rtc/pl031.c
@@ -141,6 +141,7 @@ static void pl031_write(void * opaque, hwaddr offset,
g_autofree const char *qom_path = object_get_canonical_path(opaque);
struct tm tm;
+ s->lr = value;
s->tick_offset += value - pl031_get_count(s);
qemu_get_timedate(&tm, s->tick_offset);
diff --git a/hw/ssi/xlnx-versal-ospi.c b/hw/ssi/xlnx-versal-ospi.c
index c7b95b1..c479138 100644
--- a/hw/ssi/xlnx-versal-ospi.c
+++ b/hw/ssi/xlnx-versal-ospi.c
@@ -1772,6 +1772,12 @@ static void xlnx_versal_ospi_init(Object *obj)
memory_region_init_io(&s->iomem_dac, obj, &ospi_dac_ops, s,
TYPE_XILINX_VERSAL_OSPI "-dac", 0x20000000);
sysbus_init_mmio(sbd, &s->iomem_dac);
+ /*
+ * The OSPI DMA reads flash data through the OSPI linear address space (the
+ * iomem_dac region), because of this the reentrancy guard needs to be
+ * disabled.
+ */
+ s->iomem_dac.disable_reentrancy_guard = true;
sysbus_init_irq(sbd, &s->irq);
diff --git a/hw/timer/pxa2xx_timer.c b/hw/timer/pxa2xx_timer.c
index 6a7d555..6479ab1 100644
--- a/hw/timer/pxa2xx_timer.c
+++ b/hw/timer/pxa2xx_timer.c
@@ -18,6 +18,7 @@
#include "qemu/log.h"
#include "qemu/module.h"
#include "qom/object.h"
+#include "sysemu/watchdog.h"
#define OSMR0 0x00
#define OSMR1 0x04
@@ -417,7 +418,7 @@ static void pxa2xx_timer_tick(void *opaque)
if (t->num == 3)
if (i->reset3 & 1) {
i->reset3 = 0;
- qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET);
+ watchdog_perform_action();
}
}