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----- 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>
This commit is contained in:
commit
158a054c4d
11
MAINTAINERS
11
MAINTAINERS
@ -642,6 +642,7 @@ R: Strahinja Jankovic <strahinja.p.jankovic@gmail.com>
|
||||
L: qemu-arm@nongnu.org
|
||||
S: Odd Fixes
|
||||
F: hw/*/allwinner*
|
||||
F: hw/ide/ahci-allwinner.c
|
||||
F: include/hw/*/allwinner*
|
||||
F: hw/arm/cubieboard.c
|
||||
F: docs/system/arm/cubieboard.rst
|
||||
@ -3675,6 +3676,16 @@ F: hw/core/clock-vmstate.c
|
||||
F: hw/core/qdev-clock.c
|
||||
F: docs/devel/clocks.rst
|
||||
|
||||
Reset framework
|
||||
M: Peter Maydell <peter.maydell@linaro.org>
|
||||
S: Maintained
|
||||
F: include/hw/resettable.h
|
||||
F: include/hw/core/resetcontainer.h
|
||||
F: include/sysemu/reset.h
|
||||
F: hw/core/reset.c
|
||||
F: hw/core/resettable.c
|
||||
F: hw/core/resetcontainer.c
|
||||
|
||||
Usermode Emulation
|
||||
------------------
|
||||
Overall usermode emulation
|
||||
|
@ -348,12 +348,14 @@ used. This does the same as OBJECT_DECLARE_SIMPLE_TYPE(), but without
|
||||
the 'struct MyDeviceClass' definition.
|
||||
|
||||
To implement the type, the OBJECT_DEFINE macro family is available.
|
||||
In the simple case the OBJECT_DEFINE_TYPE macro is suitable:
|
||||
For the simplest case of a leaf class which doesn't need any of its
|
||||
own virtual functions (i.e. which was declared with OBJECT_DECLARE_SIMPLE_TYPE)
|
||||
the OBJECT_DEFINE_SIMPLE_TYPE macro is suitable:
|
||||
|
||||
.. code-block:: c
|
||||
:caption: Defining a simple type
|
||||
|
||||
OBJECT_DEFINE_TYPE(MyDevice, my_device, MY_DEVICE, DEVICE)
|
||||
OBJECT_DEFINE_SIMPLE_TYPE(MyDevice, my_device, MY_DEVICE, DEVICE)
|
||||
|
||||
This is equivalent to the following:
|
||||
|
||||
@ -370,7 +372,6 @@ This is equivalent to the following:
|
||||
.instance_size = sizeof(MyDevice),
|
||||
.instance_init = my_device_init,
|
||||
.instance_finalize = my_device_finalize,
|
||||
.class_size = sizeof(MyDeviceClass),
|
||||
.class_init = my_device_class_init,
|
||||
};
|
||||
|
||||
@ -385,13 +386,36 @@ This is sufficient to get the type registered with the type
|
||||
system, and the three standard methods now need to be implemented
|
||||
along with any other logic required for the type.
|
||||
|
||||
If the class needs its own virtual methods, or has some other
|
||||
per-class state it needs to store in its own class struct,
|
||||
then you can use the OBJECT_DEFINE_TYPE macro. This does the
|
||||
same thing as OBJECT_DEFINE_SIMPLE_TYPE, but it also sets the
|
||||
class_size of the type to the size of the class struct.
|
||||
|
||||
.. code-block:: c
|
||||
:caption: Defining a type which needs a class struct
|
||||
|
||||
OBJECT_DEFINE_TYPE(MyDevice, my_device, MY_DEVICE, DEVICE)
|
||||
|
||||
If the type needs to implement one or more interfaces, then the
|
||||
OBJECT_DEFINE_TYPE_WITH_INTERFACES() macro can be used instead.
|
||||
This accepts an array of interface type names.
|
||||
OBJECT_DEFINE_SIMPLE_TYPE_WITH_INTERFACES() and
|
||||
OBJECT_DEFINE_TYPE_WITH_INTERFACES() macros can be used instead.
|
||||
These accept an array of interface type names. The difference between
|
||||
them is that the former is for simple leaf classes that don't need
|
||||
a class struct, and the latter is for when you will be defining
|
||||
a class struct.
|
||||
|
||||
.. code-block:: c
|
||||
:caption: Defining a simple type implementing interfaces
|
||||
|
||||
OBJECT_DEFINE_SIMPLE_TYPE_WITH_INTERFACES(MyDevice, my_device,
|
||||
MY_DEVICE, DEVICE,
|
||||
{ TYPE_USER_CREATABLE },
|
||||
{ NULL })
|
||||
|
||||
.. code-block:: c
|
||||
:caption: Defining a type implementing interfaces
|
||||
|
||||
OBJECT_DEFINE_TYPE_WITH_INTERFACES(MyDevice, my_device,
|
||||
MY_DEVICE, DEVICE,
|
||||
{ TYPE_USER_CREATABLE },
|
||||
|
@ -11,8 +11,8 @@ whole group can be reset consistently. Each individual member object does not
|
||||
have to care about others; in particular, problems of order (which object is
|
||||
reset first) are addressed.
|
||||
|
||||
As of now DeviceClass and BusClass implement this interface.
|
||||
|
||||
The main object types which implement this interface are DeviceClass
|
||||
and BusClass.
|
||||
|
||||
Triggering reset
|
||||
----------------
|
||||
@ -288,3 +288,43 @@ There is currently 2 cases where this function is used:
|
||||
2. *hot bus change*; it means an existing live device is added, moved or
|
||||
removed in the bus hierarchy. At the moment, it occurs only in the raspi
|
||||
machines for changing the sdbus used by sd card.
|
||||
|
||||
Reset of the complete system
|
||||
----------------------------
|
||||
|
||||
Reset of the complete system is a little complicated. The typical
|
||||
flow is:
|
||||
|
||||
1. Code which wishes to reset the entire system does so by calling
|
||||
``qemu_system_reset_request()``. This schedules a reset, but the
|
||||
reset will happen asynchronously after the function returns.
|
||||
That makes this safe to call from, for example, device models.
|
||||
|
||||
2. The function which is called to make the reset happen is
|
||||
``qemu_system_reset()``. Generally only core system code should
|
||||
call this directly.
|
||||
|
||||
3. ``qemu_system_reset()`` calls the ``MachineClass::reset`` method of
|
||||
the current machine, if it has one. That method must call
|
||||
``qemu_devices_reset()``. If the machine has no reset method,
|
||||
``qemu_system_reset()`` calls ``qemu_devices_reset()`` directly.
|
||||
|
||||
4. ``qemu_devices_reset()`` performs a reset of the system, using
|
||||
the three-phase mechanism listed above. It resets all objects
|
||||
that were registered with it using ``qemu_register_resettable()``.
|
||||
It also calls all the functions registered with it using
|
||||
``qemu_register_reset()``. Those functions are called during the
|
||||
"hold" phase of this reset.
|
||||
|
||||
5. The most important object that this reset resets is the
|
||||
'sysbus' bus. The sysbus bus is the root of the qbus tree. This
|
||||
means that all devices on the sysbus are reset, and all their
|
||||
child buses, and all the devices on those child buses.
|
||||
|
||||
6. Devices which are not on the qbus tree are *not* automatically
|
||||
reset! (The most obvious example of this is CPU objects, but
|
||||
anything that directly inherits from ``TYPE_OBJECT`` or ``TYPE_DEVICE``
|
||||
rather than from ``TYPE_SYS_BUS_DEVICE`` or some other plugs-into-a-bus
|
||||
type will be in this category.) You need to therefore arrange for these
|
||||
to be reset in some other way (e.g. using ``qemu_register_resettable()``
|
||||
or ``qemu_register_reset()``).
|
||||
|
@ -1,5 +1,5 @@
|
||||
Raspberry Pi boards (``raspi0``, ``raspi1ap``, ``raspi2b``, ``raspi3ap``, ``raspi3b``)
|
||||
======================================================================================
|
||||
Raspberry Pi boards (``raspi0``, ``raspi1ap``, ``raspi2b``, ``raspi3ap``, ``raspi3b``, ``raspi4b``)
|
||||
===================================================================================================
|
||||
|
||||
|
||||
QEMU provides models of the following Raspberry Pi boards:
|
||||
@ -12,12 +12,13 @@ QEMU provides models of the following Raspberry Pi boards:
|
||||
Cortex-A53 (4 cores), 512 MiB of RAM
|
||||
``raspi3b``
|
||||
Cortex-A53 (4 cores), 1 GiB of RAM
|
||||
|
||||
``raspi4b``
|
||||
Cortex-A72 (4 cores), 2 GiB of RAM
|
||||
|
||||
Implemented devices
|
||||
-------------------
|
||||
|
||||
* ARM1176JZF-S, Cortex-A7 or Cortex-A53 CPU
|
||||
* ARM1176JZF-S, Cortex-A7, Cortex-A53 or Cortex-A72 CPU
|
||||
* Interrupt controller
|
||||
* DMA controller
|
||||
* Clock and reset controller (CPRMAN)
|
||||
@ -35,9 +36,10 @@ Implemented devices
|
||||
* VideoCore firmware (property)
|
||||
* Peripheral SPI controller (SPI)
|
||||
|
||||
|
||||
Missing devices
|
||||
---------------
|
||||
|
||||
* Analog to Digital Converter (ADC)
|
||||
* Pulse Width Modulation (PWM)
|
||||
* PCIE Root Port (raspi4b)
|
||||
* GENET Ethernet Controller (raspi4b)
|
||||
|
@ -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)
|
||||
|
117
hw/arm/bcm2836.c
117
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,
|
||||
}
|
||||
};
|
||||
|
263
hw/arm/bcm2838.c
Normal file
263
hw/arm/bcm2838.c
Normal file
@ -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);
|
224
hw/arm/bcm2838_peripherals.c
Normal file
224
hw/arm/bcm2838_peripherals.c
Normal file
@ -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)
|
@ -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'))
|
||||
|
130
hw/arm/raspi.c
130
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,
|
||||
}
|
||||
};
|
||||
|
132
hw/arm/raspi4b.c
Normal file
132
hw/arm/raspi4b.c
Normal file
@ -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)
|
@ -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");
|
||||
|
||||
|
@ -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++) {
|
||||
|
@ -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"
|
||||
|
@ -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)
|
||||
|
@ -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) },
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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:
|
||||
|
166
hw/core/reset.c
166
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;
|
||||
};
|
||||
|
||||
static QTAILQ_HEAD(, QEMUResetEntry) reset_handlers =
|
||||
QTAILQ_HEAD_INITIALIZER(reset_handlers);
|
||||
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);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
typedef struct FindLegacyInfo {
|
||||
QEMUResetHandler *func;
|
||||
void *opaque;
|
||||
LegacyReset *lr;
|
||||
} FindLegacyInfo;
|
||||
|
||||
static void find_legacy_reset_cb(Object *obj, void *opaque, ResetType type)
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
Object *obj = OBJECT(find_legacy_reset(func, opaque));
|
||||
|
||||
QTAILQ_FOREACH(re, &reset_handlers, entry) {
|
||||
if (re->func == func && re->opaque == opaque) {
|
||||
QTAILQ_REMOVE(&reset_handlers, re, entry);
|
||||
g_free(re);
|
||||
return;
|
||||
}
|
||||
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)
|
||||
{
|
||||
QEMUResetEntry *re, *nre;
|
||||
device_reset_reason = reason;
|
||||
|
||||
/* 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);
|
||||
}
|
||||
/* Reset the simulation */
|
||||
resettable_reset(OBJECT(get_root_reset_container()), RESET_TYPE_COLD);
|
||||
}
|
||||
|
||||
|
77
hw/core/resetcontainer.c
Normal file
77
hw/core/resetcontainer.c
Normal file
@ -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;
|
||||
}
|
390
hw/gpio/bcm2838_gpio.c
Normal file
390
hw/gpio/bcm2838_gpio.c
Normal file
@ -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)
|
@ -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'))
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -35,10 +35,13 @@
|
||||
#include "hw/misc/unimp.h"
|
||||
#include "qom/object.h"
|
||||
|
||||
#define TYPE_BCM_SOC_PERIPHERALS_BASE "bcm-soc-peripherals-base"
|
||||
OBJECT_DECLARE_TYPE(BCMSocPeripheralBaseState, BCMSocPeripheralBaseClass,
|
||||
BCM_SOC_PERIPHERALS_BASE)
|
||||
#define TYPE_BCM2835_PERIPHERALS "bcm2835-peripherals"
|
||||
OBJECT_DECLARE_SIMPLE_TYPE(BCM2835PeripheralState, BCM2835_PERIPHERALS)
|
||||
|
||||
struct BCM2835PeripheralState {
|
||||
struct BCMSocPeripheralBaseState {
|
||||
/*< private >*/
|
||||
SysBusDevice parent_obj;
|
||||
/*< public >*/
|
||||
@ -60,12 +63,9 @@ struct BCM2835PeripheralState {
|
||||
OrIRQState orgated_dma_irq;
|
||||
BCM2835ICState ic;
|
||||
BCM2835PropertyState property;
|
||||
BCM2835RngState rng;
|
||||
BCM2835MboxState mboxes;
|
||||
SDHCIState sdhci;
|
||||
BCM2835SDHostState sdhost;
|
||||
BCM2835GpioState gpio;
|
||||
Bcm2835ThermalState thermal;
|
||||
UnimplementedDeviceState i2s;
|
||||
BCM2835SPIState spi[1];
|
||||
UnimplementedDeviceState i2c[3];
|
||||
@ -79,4 +79,25 @@ struct BCM2835PeripheralState {
|
||||
UnimplementedDeviceState sdramc;
|
||||
};
|
||||
|
||||
struct BCMSocPeripheralBaseClass {
|
||||
/*< private >*/
|
||||
SysBusDeviceClass parent_class;
|
||||
/*< public >*/
|
||||
uint64_t peri_size; /* Peripheral range size */
|
||||
};
|
||||
|
||||
struct BCM2835PeripheralState {
|
||||
/*< private >*/
|
||||
BCMSocPeripheralBaseState parent_obj;
|
||||
/*< public >*/
|
||||
BCM2835RngState rng;
|
||||
Bcm2835ThermalState thermal;
|
||||
BCM2835GpioState gpio;
|
||||
};
|
||||
|
||||
void create_unimp(BCMSocPeripheralBaseState *ps,
|
||||
UnimplementedDeviceState *uds,
|
||||
const char *name, hwaddr ofs, hwaddr size);
|
||||
void bcm_soc_peripherals_common_realize(DeviceState *dev, Error **errp);
|
||||
|
||||
#endif /* BCM2835_PERIPHERALS_H */
|
||||
|
@ -17,8 +17,10 @@
|
||||
#include "target/arm/cpu.h"
|
||||
#include "qom/object.h"
|
||||
|
||||
#define TYPE_BCM283X_BASE "bcm283x-base"
|
||||
OBJECT_DECLARE_TYPE(BCM283XBaseState, BCM283XBaseClass, BCM283X_BASE)
|
||||
#define TYPE_BCM283X "bcm283x"
|
||||
OBJECT_DECLARE_TYPE(BCM283XState, BCM283XClass, BCM283X)
|
||||
OBJECT_DECLARE_SIMPLE_TYPE(BCM283XState, BCM283X)
|
||||
|
||||
#define BCM283X_NCPUS 4
|
||||
|
||||
@ -30,7 +32,7 @@ OBJECT_DECLARE_TYPE(BCM283XState, BCM283XClass, BCM283X)
|
||||
#define TYPE_BCM2836 "bcm2836"
|
||||
#define TYPE_BCM2837 "bcm2837"
|
||||
|
||||
struct BCM283XState {
|
||||
struct BCM283XBaseState {
|
||||
/*< private >*/
|
||||
DeviceState parent_obj;
|
||||
/*< public >*/
|
||||
@ -41,7 +43,28 @@ struct BCM283XState {
|
||||
ARMCPU core;
|
||||
} cpu[BCM283X_NCPUS];
|
||||
BCM2836ControlState control;
|
||||
};
|
||||
|
||||
struct BCM283XBaseClass {
|
||||
/*< private >*/
|
||||
DeviceClass parent_class;
|
||||
/*< public >*/
|
||||
const char *name;
|
||||
const char *cpu_type;
|
||||
unsigned core_count;
|
||||
hwaddr peri_base; /* Peripheral base address seen by the CPU */
|
||||
hwaddr ctrl_base; /* Interrupt controller and mailboxes etc. */
|
||||
int clusterid;
|
||||
};
|
||||
|
||||
struct BCM283XState {
|
||||
/*< private >*/
|
||||
BCM283XBaseState parent_obj;
|
||||
/*< public >*/
|
||||
BCM2835PeripheralState peripherals;
|
||||
};
|
||||
|
||||
bool bcm283x_common_realize(DeviceState *dev, BCMSocPeripheralBaseState *ps,
|
||||
Error **errp);
|
||||
|
||||
#endif /* BCM2836_H */
|
||||
|
31
include/hw/arm/bcm2838.h
Normal file
31
include/hw/arm/bcm2838.h
Normal file
@ -0,0 +1,31 @@
|
||||
/*
|
||||
* BCM2838 SoC emulation
|
||||
*
|
||||
* Copyright (C) 2022 Ovchinnikov Vitalii <vitalii.ovchinnikov@auriga.com>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef BCM2838_H
|
||||
#define BCM2838_H
|
||||
|
||||
#include "hw/arm/bcm2836.h"
|
||||
#include "hw/intc/arm_gic.h"
|
||||
#include "hw/arm/bcm2838_peripherals.h"
|
||||
|
||||
#define BCM2838_PERI_LOW_BASE 0xfc000000
|
||||
#define BCM2838_GIC_BASE 0x40000
|
||||
|
||||
#define TYPE_BCM2838 "bcm2838"
|
||||
|
||||
OBJECT_DECLARE_TYPE(BCM2838State, BCM2838Class, BCM2838)
|
||||
|
||||
struct BCM2838State {
|
||||
/*< private >*/
|
||||
BCM283XBaseState parent_obj;
|
||||
/*< public >*/
|
||||
BCM2838PeripheralState peripherals;
|
||||
GICState gic;
|
||||
};
|
||||
|
||||
#endif /* BCM2838_H */
|
84
include/hw/arm/bcm2838_peripherals.h
Normal file
84
include/hw/arm/bcm2838_peripherals.h
Normal file
@ -0,0 +1,84 @@
|
||||
/*
|
||||
* BCM2838 peripherals emulation
|
||||
*
|
||||
* Copyright (C) 2022 Ovchinnikov Vitalii <vitalii.ovchinnikov@auriga.com>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef BCM2838_PERIPHERALS_H
|
||||
#define BCM2838_PERIPHERALS_H
|
||||
|
||||
#include "hw/arm/bcm2835_peripherals.h"
|
||||
#include "hw/sd/sdhci.h"
|
||||
#include "hw/gpio/bcm2838_gpio.h"
|
||||
|
||||
/* SPI */
|
||||
#define GIC_SPI_INTERRUPT_MBOX 33
|
||||
#define GIC_SPI_INTERRUPT_MPHI 40
|
||||
#define GIC_SPI_INTERRUPT_DWC2 73
|
||||
#define GIC_SPI_INTERRUPT_DMA_0 80
|
||||
#define GIC_SPI_INTERRUPT_DMA_6 86
|
||||
#define GIC_SPI_INTERRUPT_DMA_7_8 87
|
||||
#define GIC_SPI_INTERRUPT_DMA_9_10 88
|
||||
#define GIC_SPI_INTERRUPT_AUX_UART1 93
|
||||
#define GIC_SPI_INTERRUPT_SDHOST 120
|
||||
#define GIC_SPI_INTERRUPT_UART0 121
|
||||
#define GIC_SPI_INTERRUPT_RNG200 125
|
||||
#define GIC_SPI_INTERRUPT_EMMC_EMMC2 126
|
||||
#define GIC_SPI_INTERRUPT_PCI_INT_A 143
|
||||
#define GIC_SPI_INTERRUPT_GENET_A 157
|
||||
#define GIC_SPI_INTERRUPT_GENET_B 158
|
||||
|
||||
|
||||
/* GPU (legacy) DMA interrupts */
|
||||
#define GPU_INTERRUPT_DMA0 16
|
||||
#define GPU_INTERRUPT_DMA1 17
|
||||
#define GPU_INTERRUPT_DMA2 18
|
||||
#define GPU_INTERRUPT_DMA3 19
|
||||
#define GPU_INTERRUPT_DMA4 20
|
||||
#define GPU_INTERRUPT_DMA5 21
|
||||
#define GPU_INTERRUPT_DMA6 22
|
||||
#define GPU_INTERRUPT_DMA7_8 23
|
||||
#define GPU_INTERRUPT_DMA9_10 24
|
||||
#define GPU_INTERRUPT_DMA11 25
|
||||
#define GPU_INTERRUPT_DMA12 26
|
||||
#define GPU_INTERRUPT_DMA13 27
|
||||
#define GPU_INTERRUPT_DMA14 28
|
||||
#define GPU_INTERRUPT_DMA15 31
|
||||
|
||||
#define BCM2838_MPHI_OFFSET 0xb200
|
||||
#define BCM2838_MPHI_SIZE 0x200
|
||||
|
||||
#define TYPE_BCM2838_PERIPHERALS "bcm2838-peripherals"
|
||||
OBJECT_DECLARE_TYPE(BCM2838PeripheralState, BCM2838PeripheralClass,
|
||||
BCM2838_PERIPHERALS)
|
||||
|
||||
struct BCM2838PeripheralState {
|
||||
/*< private >*/
|
||||
BCMSocPeripheralBaseState parent_obj;
|
||||
|
||||
/*< public >*/
|
||||
MemoryRegion peri_low_mr;
|
||||
MemoryRegion peri_low_mr_alias;
|
||||
MemoryRegion mphi_mr_alias;
|
||||
|
||||
SDHCIState emmc2;
|
||||
BCM2838GpioState gpio;
|
||||
|
||||
OrIRQState mmc_irq_orgate;
|
||||
OrIRQState dma_7_8_irq_orgate;
|
||||
OrIRQState dma_9_10_irq_orgate;
|
||||
|
||||
UnimplementedDeviceState asb;
|
||||
UnimplementedDeviceState clkisp;
|
||||
};
|
||||
|
||||
struct BCM2838PeripheralClass {
|
||||
/*< private >*/
|
||||
BCMSocPeripheralBaseClass parent_class;
|
||||
/*< public >*/
|
||||
uint64_t peri_low_size; /* Peripheral lower range size */
|
||||
};
|
||||
|
||||
#endif /* BCM2838_PERIPHERALS_H */
|
@ -159,4 +159,15 @@ enum rpi_firmware_clk_id {
|
||||
RPI_FIRMWARE_NUM_CLK_ID,
|
||||
};
|
||||
|
||||
struct rpi_firmware_property_tag_header {
|
||||
uint32_t tag;
|
||||
uint32_t buf_size;
|
||||
uint32_t req_resp_size;
|
||||
};
|
||||
|
||||
typedef struct rpi_firmware_prop_request {
|
||||
struct rpi_firmware_property_tag_header hdr;
|
||||
uint8_t payload[0];
|
||||
} rpi_firmware_prop_request_t;
|
||||
|
||||
#endif /* INCLUDE_HW_MISC_RASPBERRYPI_FW_DEFS_H_ */
|
||||
|
@ -28,6 +28,42 @@
|
||||
#ifndef HW_ARM_RASPI_PLATFORM_H
|
||||
#define HW_ARM_RASPI_PLATFORM_H
|
||||
|
||||
#include "hw/boards.h"
|
||||
#include "hw/arm/boot.h"
|
||||
|
||||
/* Registered machine type (matches RPi Foundation bootloader and U-Boot) */
|
||||
#define MACH_TYPE_BCM2708 3138
|
||||
|
||||
#define TYPE_RASPI_BASE_MACHINE MACHINE_TYPE_NAME("raspi-base")
|
||||
OBJECT_DECLARE_TYPE(RaspiBaseMachineState, RaspiBaseMachineClass,
|
||||
RASPI_BASE_MACHINE)
|
||||
|
||||
struct RaspiBaseMachineState {
|
||||
/*< private >*/
|
||||
MachineState parent_obj;
|
||||
/*< public >*/
|
||||
struct arm_boot_info binfo;
|
||||
};
|
||||
|
||||
struct RaspiBaseMachineClass {
|
||||
/*< private >*/
|
||||
MachineClass parent_obj;
|
||||
/*< public >*/
|
||||
uint32_t board_rev;
|
||||
};
|
||||
|
||||
/* Common functions for raspberry pi machines */
|
||||
const char *board_soc_type(uint32_t board_rev);
|
||||
void raspi_machine_init(MachineState *machine);
|
||||
|
||||
typedef struct BCM283XBaseState BCM283XBaseState;
|
||||
void raspi_base_machine_init(MachineState *machine,
|
||||
BCM283XBaseState *soc);
|
||||
|
||||
void raspi_machine_class_common_init(MachineClass *mc,
|
||||
uint32_t board_rev);
|
||||
uint64_t board_ram_size(uint32_t board_rev);
|
||||
|
||||
#define MSYNC_OFFSET 0x0000 /* Multicore Sync Block */
|
||||
#define CCPT_OFFSET 0x1000 /* Compact Camera Port 2 TX */
|
||||
#define INTE_OFFSET 0x2000 /* VC Interrupt controller */
|
||||
@ -37,7 +73,7 @@
|
||||
#define MPHI_OFFSET 0x6000 /* Message-based Parallel Host Intf. */
|
||||
#define DMA_OFFSET 0x7000 /* DMA controller, channels 0-14 */
|
||||
#define ARBA_OFFSET 0x9000
|
||||
#define BRDG_OFFSET 0xa000
|
||||
#define BRDG_OFFSET 0xa000 /* RPiVid ASB for BCM2838 (BCM2711) */
|
||||
#define ARM_OFFSET 0xB000 /* ARM control block */
|
||||
#define ARMCTRL_OFFSET (ARM_OFFSET + 0x000)
|
||||
#define ARMCTRL_IC_OFFSET (ARM_OFFSET + 0x200) /* Interrupt controller */
|
||||
|
@ -26,6 +26,7 @@
|
||||
|
||||
#include "exec/memory.h"
|
||||
#include "hw/arm/armv7m.h"
|
||||
#include "hw/or-irq.h"
|
||||
#include "hw/misc/stm32l4x5_syscfg.h"
|
||||
#include "hw/misc/stm32l4x5_exti.h"
|
||||
#include "qom/object.h"
|
||||
@ -36,12 +37,15 @@
|
||||
#define TYPE_STM32L4X5XG_SOC "stm32l4x5xg-soc"
|
||||
OBJECT_DECLARE_TYPE(Stm32l4x5SocState, Stm32l4x5SocClass, STM32L4X5_SOC)
|
||||
|
||||
#define NUM_EXTI_OR_GATES 4
|
||||
|
||||
struct Stm32l4x5SocState {
|
||||
SysBusDevice parent_obj;
|
||||
|
||||
ARMv7MState armv7m;
|
||||
|
||||
Stm32l4x5ExtiState exti;
|
||||
OrIRQState exti_or_gates[NUM_EXTI_OR_GATES];
|
||||
Stm32l4x5SyscfgState syscfg;
|
||||
|
||||
MemoryRegion sram1;
|
||||
|
48
include/hw/core/resetcontainer.h
Normal file
48
include/hw/core/resetcontainer.h
Normal file
@ -0,0 +1,48 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef HW_RESETCONTAINER_H
|
||||
#define HW_RESETCONTAINER_H
|
||||
|
||||
/*
|
||||
* 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 "qom/object.h"
|
||||
|
||||
#define TYPE_RESETTABLE_CONTAINER "resettable-container"
|
||||
OBJECT_DECLARE_TYPE(ResettableContainer, ResettableContainerClass, RESETTABLE_CONTAINER)
|
||||
|
||||
/**
|
||||
* resettable_container_add: Add a resettable object to the container
|
||||
* @rc: container
|
||||
* @obj: object to add to the container
|
||||
*
|
||||
* Add @obj to the ResettableContainer @rc. @obj must implement the
|
||||
* Resettable interface.
|
||||
*
|
||||
* When @rc is reset, it will reset every object that has been added
|
||||
* to it, in the order they were added.
|
||||
*/
|
||||
void resettable_container_add(ResettableContainer *rc, Object *obj);
|
||||
|
||||
/**
|
||||
* resettable_container_remove: Remove an object from the container
|
||||
* @rc: container
|
||||
* @obj: object to remove from the container
|
||||
*
|
||||
* Remove @obj from the ResettableContainer @rc. @obj must have been
|
||||
* previously added to this container.
|
||||
*/
|
||||
void resettable_container_remove(ResettableContainer *rc, Object *obj);
|
||||
|
||||
#endif
|
@ -16,6 +16,8 @@
|
||||
#include "ui/console.h"
|
||||
#include "qom/object.h"
|
||||
|
||||
#define UPPER_RAM_BASE 0x40000000
|
||||
|
||||
#define TYPE_BCM2835_FB "bcm2835-fb"
|
||||
OBJECT_DECLARE_SIMPLE_TYPE(BCM2835FBState, BCM2835_FB)
|
||||
|
||||
|
45
include/hw/gpio/bcm2838_gpio.h
Normal file
45
include/hw/gpio/bcm2838_gpio.h
Normal file
@ -0,0 +1,45 @@
|
||||
/*
|
||||
* 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>
|
||||
*
|
||||
* This work is licensed under the terms of the GNU GPL, version 2 or later.
|
||||
* See the COPYING file in the top-level directory.
|
||||
*/
|
||||
|
||||
#ifndef BCM2838_GPIO_H
|
||||
#define BCM2838_GPIO_H
|
||||
|
||||
#include "hw/sd/sd.h"
|
||||
#include "hw/sysbus.h"
|
||||
#include "qom/object.h"
|
||||
|
||||
#define TYPE_BCM2838_GPIO "bcm2838-gpio"
|
||||
OBJECT_DECLARE_SIMPLE_TYPE(BCM2838GpioState, BCM2838_GPIO)
|
||||
|
||||
#define BCM2838_GPIO_REGS_SIZE 0x1000
|
||||
#define BCM2838_GPIO_NUM 58
|
||||
#define GPIO_PUP_PDN_CNTRL_NUM 4
|
||||
|
||||
struct BCM2838GpioState {
|
||||
SysBusDevice parent_obj;
|
||||
|
||||
MemoryRegion iomem;
|
||||
|
||||
/* SDBus selector */
|
||||
SDBus sdbus;
|
||||
SDBus *sdbus_sdhci;
|
||||
SDBus *sdbus_sdhost;
|
||||
|
||||
uint8_t fsel[BCM2838_GPIO_NUM];
|
||||
uint32_t lev0, lev1;
|
||||
uint8_t sd_fsel;
|
||||
qemu_irq out[BCM2838_GPIO_NUM];
|
||||
uint32_t pup_cntrl_reg[GPIO_PUP_PDN_CNTRL_NUM];
|
||||
};
|
||||
|
||||
#endif
|
@ -258,6 +258,51 @@ struct Object
|
||||
DECLARE_INSTANCE_CHECKER(InstanceType, MODULE_OBJ_NAME, TYPE_##MODULE_OBJ_NAME)
|
||||
|
||||
|
||||
/**
|
||||
* DO_OBJECT_DEFINE_TYPE_EXTENDED:
|
||||
* @ModuleObjName: the object name with initial caps
|
||||
* @module_obj_name: the object name in lowercase with underscore separators
|
||||
* @MODULE_OBJ_NAME: the object name in uppercase with underscore separators
|
||||
* @PARENT_MODULE_OBJ_NAME: the parent object name in uppercase with underscore
|
||||
* separators
|
||||
* @ABSTRACT: boolean flag to indicate whether the object can be instantiated
|
||||
* @CLASS_SIZE: size of the type's class
|
||||
* @...: list of initializers for "InterfaceInfo" to declare implemented interfaces
|
||||
*
|
||||
* This is the base macro used to implement all the OBJECT_DEFINE_*
|
||||
* macros. It should never be used directly in a source file.
|
||||
*/
|
||||
#define DO_OBJECT_DEFINE_TYPE_EXTENDED(ModuleObjName, module_obj_name, \
|
||||
MODULE_OBJ_NAME, \
|
||||
PARENT_MODULE_OBJ_NAME, \
|
||||
ABSTRACT, CLASS_SIZE, ...) \
|
||||
static void \
|
||||
module_obj_name##_finalize(Object *obj); \
|
||||
static void \
|
||||
module_obj_name##_class_init(ObjectClass *oc, void *data); \
|
||||
static void \
|
||||
module_obj_name##_init(Object *obj); \
|
||||
\
|
||||
static const TypeInfo module_obj_name##_info = { \
|
||||
.parent = TYPE_##PARENT_MODULE_OBJ_NAME, \
|
||||
.name = TYPE_##MODULE_OBJ_NAME, \
|
||||
.instance_size = sizeof(ModuleObjName), \
|
||||
.instance_align = __alignof__(ModuleObjName), \
|
||||
.instance_init = module_obj_name##_init, \
|
||||
.instance_finalize = module_obj_name##_finalize, \
|
||||
.class_size = CLASS_SIZE, \
|
||||
.class_init = module_obj_name##_class_init, \
|
||||
.abstract = ABSTRACT, \
|
||||
.interfaces = (InterfaceInfo[]) { __VA_ARGS__ } , \
|
||||
}; \
|
||||
\
|
||||
static void \
|
||||
module_obj_name##_register_types(void) \
|
||||
{ \
|
||||
type_register_static(&module_obj_name##_info); \
|
||||
} \
|
||||
type_init(module_obj_name##_register_types);
|
||||
|
||||
/**
|
||||
* OBJECT_DEFINE_TYPE_EXTENDED:
|
||||
* @ModuleObjName: the object name with initial caps
|
||||
@ -284,32 +329,10 @@ struct Object
|
||||
#define OBJECT_DEFINE_TYPE_EXTENDED(ModuleObjName, module_obj_name, \
|
||||
MODULE_OBJ_NAME, PARENT_MODULE_OBJ_NAME, \
|
||||
ABSTRACT, ...) \
|
||||
static void \
|
||||
module_obj_name##_finalize(Object *obj); \
|
||||
static void \
|
||||
module_obj_name##_class_init(ObjectClass *oc, void *data); \
|
||||
static void \
|
||||
module_obj_name##_init(Object *obj); \
|
||||
\
|
||||
static const TypeInfo module_obj_name##_info = { \
|
||||
.parent = TYPE_##PARENT_MODULE_OBJ_NAME, \
|
||||
.name = TYPE_##MODULE_OBJ_NAME, \
|
||||
.instance_size = sizeof(ModuleObjName), \
|
||||
.instance_align = __alignof__(ModuleObjName), \
|
||||
.instance_init = module_obj_name##_init, \
|
||||
.instance_finalize = module_obj_name##_finalize, \
|
||||
.class_size = sizeof(ModuleObjName##Class), \
|
||||
.class_init = module_obj_name##_class_init, \
|
||||
.abstract = ABSTRACT, \
|
||||
.interfaces = (InterfaceInfo[]) { __VA_ARGS__ } , \
|
||||
}; \
|
||||
\
|
||||
static void \
|
||||
module_obj_name##_register_types(void) \
|
||||
{ \
|
||||
type_register_static(&module_obj_name##_info); \
|
||||
} \
|
||||
type_init(module_obj_name##_register_types);
|
||||
DO_OBJECT_DEFINE_TYPE_EXTENDED(ModuleObjName, module_obj_name, \
|
||||
MODULE_OBJ_NAME, PARENT_MODULE_OBJ_NAME, \
|
||||
ABSTRACT, sizeof(ModuleObjName##Class), \
|
||||
__VA_ARGS__)
|
||||
|
||||
/**
|
||||
* OBJECT_DEFINE_TYPE:
|
||||
@ -368,6 +391,45 @@ struct Object
|
||||
MODULE_OBJ_NAME, PARENT_MODULE_OBJ_NAME, \
|
||||
true, { NULL })
|
||||
|
||||
/**
|
||||
* OBJECT_DEFINE_SIMPLE_TYPE_WITH_INTERFACES:
|
||||
* @ModuleObjName: the object name with initial caps
|
||||
* @module_obj_name: the object name in lowercase with underscore separators
|
||||
* @MODULE_OBJ_NAME: the object name in uppercase with underscore separators
|
||||
* @PARENT_MODULE_OBJ_NAME: the parent object name in uppercase with underscore
|
||||
* separators
|
||||
*
|
||||
* This is a variant of OBJECT_DEFINE_TYPE_EXTENDED, which is suitable for
|
||||
* the case of a non-abstract type, with interfaces, and with no requirement
|
||||
* for a class struct.
|
||||
*/
|
||||
#define OBJECT_DEFINE_SIMPLE_TYPE_WITH_INTERFACES(ModuleObjName, \
|
||||
module_obj_name, \
|
||||
MODULE_OBJ_NAME, \
|
||||
PARENT_MODULE_OBJ_NAME, ...) \
|
||||
DO_OBJECT_DEFINE_TYPE_EXTENDED(ModuleObjName, module_obj_name, \
|
||||
MODULE_OBJ_NAME, PARENT_MODULE_OBJ_NAME, \
|
||||
false, 0, __VA_ARGS__)
|
||||
|
||||
/**
|
||||
* OBJECT_DEFINE_SIMPLE_TYPE:
|
||||
* @ModuleObjName: the object name with initial caps
|
||||
* @module_obj_name: the object name in lowercase with underscore separators
|
||||
* @MODULE_OBJ_NAME: the object name in uppercase with underscore separators
|
||||
* @PARENT_MODULE_OBJ_NAME: the parent object name in uppercase with underscore
|
||||
* separators
|
||||
*
|
||||
* This is a variant of OBJECT_DEFINE_TYPE_EXTENDED, which is suitable for
|
||||
* the common case of a non-abstract type, without any interfaces, and with
|
||||
* no requirement for a class struct. If you declared your type with
|
||||
* OBJECT_DECLARE_SIMPLE_TYPE then this is probably the right choice for
|
||||
* defining it.
|
||||
*/
|
||||
#define OBJECT_DEFINE_SIMPLE_TYPE(ModuleObjName, module_obj_name, \
|
||||
MODULE_OBJ_NAME, PARENT_MODULE_OBJ_NAME) \
|
||||
OBJECT_DEFINE_SIMPLE_TYPE_WITH_INTERFACES(ModuleObjName, module_obj_name, \
|
||||
MODULE_OBJ_NAME, PARENT_MODULE_OBJ_NAME, { NULL })
|
||||
|
||||
/**
|
||||
* struct TypeInfo:
|
||||
* @name: The name of the type.
|
||||
|
@ -1,3 +1,29 @@
|
||||
/*
|
||||
* Reset handlers.
|
||||
*
|
||||
* Copyright (c) 2003-2008 Fabrice Bellard
|
||||
* Copyright (c) 2016 Red Hat, Inc.
|
||||
* Copyright (c) 2024 Linaro, Ltd.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef QEMU_SYSEMU_RESET_H
|
||||
#define QEMU_SYSEMU_RESET_H
|
||||
|
||||
@ -5,9 +31,96 @@
|
||||
|
||||
typedef void QEMUResetHandler(void *opaque);
|
||||
|
||||
/**
|
||||
* qemu_register_resettable: Register an object to be reset
|
||||
* @obj: object to be reset: it must implement the Resettable interface
|
||||
*
|
||||
* Register @obj on the list of objects which will be reset when the
|
||||
* simulation is reset. These objects will be reset in the order
|
||||
* they were added, using the three-phase Resettable protocol,
|
||||
* so first all objects go through the enter phase, then all objects
|
||||
* go through the hold phase, and then finally all go through the
|
||||
* exit phase.
|
||||
*
|
||||
* It is not permitted to register or unregister reset functions or
|
||||
* resettable objects from within any of the reset phase methods of @obj.
|
||||
*
|
||||
* We assume that the caller holds the BQL.
|
||||
*/
|
||||
void qemu_register_resettable(Object *obj);
|
||||
|
||||
/**
|
||||
* qemu_unregister_resettable: Unregister an object to be reset
|
||||
* @obj: object to unregister
|
||||
*
|
||||
* Remove @obj from the list of objects which are reset when the
|
||||
* simulation is reset. It must have been previously added to
|
||||
* the list via qemu_register_resettable().
|
||||
*
|
||||
* We assume that the caller holds the BQL.
|
||||
*/
|
||||
void qemu_unregister_resettable(Object *obj);
|
||||
|
||||
/**
|
||||
* qemu_register_reset: Register a callback for system reset
|
||||
* @func: function to call
|
||||
* @opaque: opaque data to pass to @func
|
||||
*
|
||||
* Register @func on the list of functions which are called when the
|
||||
* entire system is reset. Functions registered with this API and
|
||||
* Resettable objects registered with qemu_register_resettable() are
|
||||
* handled together, in the order in which they were registered.
|
||||
* Functions registered with this API are called in the 'hold' phase
|
||||
* of the 3-phase reset.
|
||||
*
|
||||
* In general this function should not be used in new code where possible;
|
||||
* for instance, device model reset is better accomplished using the
|
||||
* methods on DeviceState.
|
||||
*
|
||||
* It is not permitted to register or unregister reset functions or
|
||||
* resettable objects from within the @func callback.
|
||||
*
|
||||
* We assume that the caller holds the BQL.
|
||||
*/
|
||||
void qemu_register_reset(QEMUResetHandler *func, void *opaque);
|
||||
|
||||
/**
|
||||
* qemu_register_reset_nosnapshotload: Register a callback for system reset
|
||||
* @func: function to call
|
||||
* @opaque: opaque data to pass to @func
|
||||
*
|
||||
* This is the same as qemu_register_reset(), except that @func is
|
||||
* not called if the reason that the system is being reset is to
|
||||
* put it into a clean state prior to loading a snapshot (i.e. for
|
||||
* SHUTDOWN_CAUSE_SNAPSHOT_LOAD).
|
||||
*/
|
||||
void qemu_register_reset_nosnapshotload(QEMUResetHandler *func, void *opaque);
|
||||
|
||||
/**
|
||||
* qemu_unregister_reset: Unregister a system reset callback
|
||||
* @func: function registered with qemu_register_reset()
|
||||
* @opaque: the same opaque data that was passed to qemu_register_reset()
|
||||
*
|
||||
* Undo the effects of a qemu_register_reset(). The @func and @opaque
|
||||
* must both match the arguments originally used with qemu_register_reset().
|
||||
*
|
||||
* We assume that the caller holds the BQL.
|
||||
*/
|
||||
void qemu_unregister_reset(QEMUResetHandler *func, void *opaque);
|
||||
|
||||
/**
|
||||
* qemu_devices_reset: Perform a complete system reset
|
||||
* @reason: reason for the reset
|
||||
*
|
||||
* This function performs the low-level work needed to do a complete reset
|
||||
* of the system (calling all the callbacks registered with
|
||||
* qemu_register_reset() and resetting all the Resettable objects registered
|
||||
* with qemu_register_resettable()). It should only be called by the code in a
|
||||
* MachineClass reset method.
|
||||
*
|
||||
* If you want to trigger a system reset from, for instance, a device
|
||||
* model, don't use this function. Use qemu_system_reset_request().
|
||||
*/
|
||||
void qemu_devices_reset(ShutdownCause reason);
|
||||
|
||||
#endif
|
||||
|
@ -101,20 +101,23 @@ void validate_bootdevices(const char *devices, Error **errp)
|
||||
void restore_boot_order(void *opaque)
|
||||
{
|
||||
char *normal_boot_order = opaque;
|
||||
static int first = 1;
|
||||
static int bootcount;
|
||||
|
||||
/* Restore boot order and remove ourselves after the first boot */
|
||||
if (first) {
|
||||
first = 0;
|
||||
switch (bootcount++) {
|
||||
case 0:
|
||||
/* First boot: use the one-time config */
|
||||
return;
|
||||
case 1:
|
||||
/* Second boot: restore normal boot order */
|
||||
if (boot_set_handler) {
|
||||
qemu_boot_set(normal_boot_order, &error_abort);
|
||||
}
|
||||
g_free(normal_boot_order);
|
||||
return;
|
||||
default:
|
||||
/* Subsequent boots: keep using normal boot order */
|
||||
return;
|
||||
}
|
||||
|
||||
if (boot_set_handler) {
|
||||
qemu_boot_set(normal_boot_order, &error_abort);
|
||||
}
|
||||
|
||||
qemu_unregister_reset(restore_boot_order, normal_boot_order);
|
||||
g_free(normal_boot_order);
|
||||
}
|
||||
|
||||
void check_boot_index(int32_t bootindex, Error **errp)
|
||||
|
@ -663,7 +663,7 @@ static void aarch64_a53_initfn(Object *obj)
|
||||
set_feature(&cpu->env, ARM_FEATURE_PMU);
|
||||
cpu->kvm_target = QEMU_KVM_ARM_TARGET_CORTEX_A53;
|
||||
cpu->midr = 0x410fd034;
|
||||
cpu->revidr = 0x00000000;
|
||||
cpu->revidr = 0x00000100;
|
||||
cpu->reset_fpsid = 0x41034070;
|
||||
cpu->isar.mvfr0 = 0x10110222;
|
||||
cpu->isar.mvfr1 = 0x12111111;
|
||||
|
@ -711,8 +711,68 @@ static uint64_t arm_casq_ptw(CPUARMState *env, uint64_t old_val,
|
||||
void *host = ptw->out_host;
|
||||
|
||||
if (unlikely(!host)) {
|
||||
fi->type = ARMFault_UnsuppAtomicUpdate;
|
||||
return 0;
|
||||
/* Page table in MMIO Memory Region */
|
||||
CPUState *cs = env_cpu(env);
|
||||
MemTxAttrs attrs = {
|
||||
.space = ptw->out_space,
|
||||
.secure = arm_space_is_secure(ptw->out_space),
|
||||
};
|
||||
AddressSpace *as = arm_addressspace(cs, attrs);
|
||||
MemTxResult result = MEMTX_OK;
|
||||
bool need_lock = !bql_locked();
|
||||
|
||||
if (need_lock) {
|
||||
bql_lock();
|
||||
}
|
||||
if (ptw->out_be) {
|
||||
cur_val = address_space_ldq_be(as, ptw->out_phys, attrs, &result);
|
||||
if (unlikely(result != MEMTX_OK)) {
|
||||
fi->type = ARMFault_SyncExternalOnWalk;
|
||||
fi->ea = arm_extabort_type(result);
|
||||
if (need_lock) {
|
||||
bql_unlock();
|
||||
}
|
||||
return old_val;
|
||||
}
|
||||
if (cur_val == old_val) {
|
||||
address_space_stq_be(as, ptw->out_phys, new_val, attrs, &result);
|
||||
if (unlikely(result != MEMTX_OK)) {
|
||||
fi->type = ARMFault_SyncExternalOnWalk;
|
||||
fi->ea = arm_extabort_type(result);
|
||||
if (need_lock) {
|
||||
bql_unlock();
|
||||
}
|
||||
return old_val;
|
||||
}
|
||||
cur_val = new_val;
|
||||
}
|
||||
} else {
|
||||
cur_val = address_space_ldq_le(as, ptw->out_phys, attrs, &result);
|
||||
if (unlikely(result != MEMTX_OK)) {
|
||||
fi->type = ARMFault_SyncExternalOnWalk;
|
||||
fi->ea = arm_extabort_type(result);
|
||||
if (need_lock) {
|
||||
bql_unlock();
|
||||
}
|
||||
return old_val;
|
||||
}
|
||||
if (cur_val == old_val) {
|
||||
address_space_stq_le(as, ptw->out_phys, new_val, attrs, &result);
|
||||
if (unlikely(result != MEMTX_OK)) {
|
||||
fi->type = ARMFault_SyncExternalOnWalk;
|
||||
fi->ea = arm_extabort_type(result);
|
||||
if (need_lock) {
|
||||
bql_unlock();
|
||||
}
|
||||
return old_val;
|
||||
}
|
||||
cur_val = new_val;
|
||||
}
|
||||
}
|
||||
if (need_lock) {
|
||||
bql_unlock();
|
||||
}
|
||||
return cur_val;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -501,6 +501,103 @@ class BootLinuxConsole(LinuxKernelTest):
|
||||
# Wait for VM to shut down gracefully
|
||||
self.vm.wait()
|
||||
|
||||
def test_arm_raspi4(self):
|
||||
"""
|
||||
:avocado: tags=arch:aarch64
|
||||
:avocado: tags=machine:raspi4b
|
||||
:avocado: tags=device:pl011
|
||||
:avocado: tags=accel:tcg
|
||||
:avocado: tags=rpi4b
|
||||
|
||||
The kernel can be rebuilt using the kernel source referenced
|
||||
and following the instructions on the on:
|
||||
https://www.raspberrypi.org/documentation/linux/kernel/building.md
|
||||
"""
|
||||
|
||||
deb_url = ('http://archive.raspberrypi.org/debian/'
|
||||
'pool/main/r/raspberrypi-firmware/'
|
||||
'raspberrypi-kernel_1.20230106-1_arm64.deb')
|
||||
deb_hash = '08dc55696535b18a6d4fe6fa10d4c0d905cbb2ed'
|
||||
deb_path = self.fetch_asset(deb_url, asset_hash=deb_hash)
|
||||
kernel_path = self.extract_from_deb(deb_path, '/boot/kernel8.img')
|
||||
dtb_path = self.extract_from_deb(deb_path, '/boot/bcm2711-rpi-4-b.dtb')
|
||||
|
||||
self.vm.set_console()
|
||||
kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE +
|
||||
'earlycon=pl011,mmio32,0xfe201000 ' +
|
||||
'console=ttyAMA0,115200 ' +
|
||||
'root=/dev/mmcblk1p2 rootwait ' +
|
||||
'dwc_otg.fiq_fsm_enable=0')
|
||||
self.vm.add_args('-kernel', kernel_path,
|
||||
'-dtb', dtb_path,
|
||||
'-append', kernel_command_line)
|
||||
# When PCI is supported we can add a USB controller:
|
||||
# '-device', 'qemu-xhci,bus=pcie.1,id=xhci',
|
||||
# '-device', 'usb-kbd,bus=xhci.0',
|
||||
self.vm.launch()
|
||||
console_pattern = 'Kernel command line: %s' % kernel_command_line
|
||||
self.wait_for_console_pattern(console_pattern)
|
||||
# When USB is enabled we can look for this
|
||||
# console_pattern = 'Product: QEMU USB Keyboard'
|
||||
# self.wait_for_console_pattern(console_pattern)
|
||||
console_pattern = 'Waiting for root device'
|
||||
self.wait_for_console_pattern(console_pattern)
|
||||
|
||||
|
||||
def test_arm_raspi4_initrd(self):
|
||||
"""
|
||||
:avocado: tags=arch:aarch64
|
||||
:avocado: tags=machine:raspi4b
|
||||
:avocado: tags=device:pl011
|
||||
:avocado: tags=accel:tcg
|
||||
:avocado: tags=rpi4b
|
||||
|
||||
The kernel can be rebuilt using the kernel source referenced
|
||||
and following the instructions on the on:
|
||||
https://www.raspberrypi.org/documentation/linux/kernel/building.md
|
||||
"""
|
||||
deb_url = ('http://archive.raspberrypi.org/debian/'
|
||||
'pool/main/r/raspberrypi-firmware/'
|
||||
'raspberrypi-kernel_1.20230106-1_arm64.deb')
|
||||
deb_hash = '08dc55696535b18a6d4fe6fa10d4c0d905cbb2ed'
|
||||
deb_path = self.fetch_asset(deb_url, asset_hash=deb_hash)
|
||||
kernel_path = self.extract_from_deb(deb_path, '/boot/kernel8.img')
|
||||
dtb_path = self.extract_from_deb(deb_path, '/boot/bcm2711-rpi-4-b.dtb')
|
||||
|
||||
initrd_url = ('https://github.com/groeck/linux-build-test/raw/'
|
||||
'86b2be1384d41c8c388e63078a847f1e1c4cb1de/rootfs/'
|
||||
'arm64/rootfs.cpio.gz')
|
||||
initrd_hash = 'f3d4f9fa92a49aa542f1b44d34be77bbf8ca5b9d'
|
||||
initrd_path_gz = self.fetch_asset(initrd_url, asset_hash=initrd_hash)
|
||||
initrd_path = os.path.join(self.workdir, 'rootfs.cpio')
|
||||
archive.gzip_uncompress(initrd_path_gz, initrd_path)
|
||||
|
||||
self.vm.set_console()
|
||||
kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE +
|
||||
'earlycon=pl011,mmio32,0xfe201000 ' +
|
||||
'console=ttyAMA0,115200 ' +
|
||||
'panic=-1 noreboot ' +
|
||||
'dwc_otg.fiq_fsm_enable=0')
|
||||
self.vm.add_args('-kernel', kernel_path,
|
||||
'-dtb', dtb_path,
|
||||
'-initrd', initrd_path,
|
||||
'-append', kernel_command_line,
|
||||
'-no-reboot')
|
||||
# When PCI is supported we can add a USB controller:
|
||||
# '-device', 'qemu-xhci,bus=pcie.1,id=xhci',
|
||||
# '-device', 'usb-kbd,bus=xhci.0',
|
||||
self.vm.launch()
|
||||
self.wait_for_console_pattern('Boot successful.')
|
||||
|
||||
exec_command_and_wait_for_pattern(self, 'cat /proc/cpuinfo',
|
||||
'BCM2835')
|
||||
exec_command_and_wait_for_pattern(self, 'cat /proc/iomem',
|
||||
'cprman@7e101000')
|
||||
exec_command_and_wait_for_pattern(self, 'halt', 'reboot: System halted')
|
||||
# TODO: Raspberry Pi4 doesn't shut down properly with recent kernels
|
||||
# Wait for VM to shut down gracefully
|
||||
#self.vm.wait()
|
||||
|
||||
def test_arm_exynos4210_initrd(self):
|
||||
"""
|
||||
:avocado: tags=arch:arm
|
||||
|
@ -31,6 +31,7 @@
|
||||
|
||||
#define EXTI0_IRQ 6
|
||||
#define EXTI1_IRQ 7
|
||||
#define EXTI5_9_IRQ 23
|
||||
#define EXTI35_IRQ 1
|
||||
|
||||
static void enable_nvic_irq(unsigned int n)
|
||||
@ -499,6 +500,40 @@ static void test_interrupt(void)
|
||||
g_assert_false(check_nvic_pending(EXTI1_IRQ));
|
||||
}
|
||||
|
||||
static void test_orred_interrupts(void)
|
||||
{
|
||||
/*
|
||||
* For lines EXTI5..9 (fanned-in to NVIC irq 23),
|
||||
* test that raising the line pends interrupt
|
||||
* 23 in NVIC.
|
||||
*/
|
||||
enable_nvic_irq(EXTI5_9_IRQ);
|
||||
/* Check that there are no interrupts already pending in PR */
|
||||
g_assert_cmpuint(exti_readl(EXTI_PR1), ==, 0x00000000);
|
||||
/* Check that this specific interrupt isn't pending in NVIC */
|
||||
g_assert_false(check_nvic_pending(EXTI5_9_IRQ));
|
||||
|
||||
/* Enable interrupt lines EXTI[5..9] */
|
||||
exti_writel(EXTI_IMR1, (0x1F << 5));
|
||||
|
||||
/* Configure interrupt on rising edge */
|
||||
exti_writel(EXTI_RTSR1, (0x1F << 5));
|
||||
|
||||
/* Raise GPIO line i, check that the interrupt is pending */
|
||||
for (unsigned i = 5; i < 10; i++) {
|
||||
exti_set_irq(i, 1);
|
||||
g_assert_cmpuint(exti_readl(EXTI_PR1), ==, 1 << i);
|
||||
g_assert_true(check_nvic_pending(EXTI5_9_IRQ));
|
||||
|
||||
exti_writel(EXTI_PR1, 1 << i);
|
||||
g_assert_cmpuint(exti_readl(EXTI_PR1), ==, 0x00000000);
|
||||
g_assert_true(check_nvic_pending(EXTI5_9_IRQ));
|
||||
|
||||
unpend_nvic_irq(EXTI5_9_IRQ);
|
||||
g_assert_false(check_nvic_pending(EXTI5_9_IRQ));
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
int ret;
|
||||
@ -515,6 +550,8 @@ int main(int argc, char **argv)
|
||||
qtest_add_func("stm32l4x5/exti/masked_interrupt", test_masked_interrupt);
|
||||
qtest_add_func("stm32l4x5/exti/interrupt", test_interrupt);
|
||||
qtest_add_func("stm32l4x5/exti/test_edge_selector", test_edge_selector);
|
||||
qtest_add_func("stm32l4x5/exti/test_orred_interrupts",
|
||||
test_orred_interrupts);
|
||||
|
||||
qtest_start("-machine b-l475e-iot01a");
|
||||
ret = g_test_run();
|
||||
|
Loading…
Reference in New Issue
Block a user