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

target-arm queue:
 * Fix various bugs that might result in an assert() due to
   incorrect hflags for M-profile CPUs
 * Fix Aspeed SMC Controller user-mode select handling
 * Report correct (with-tag) address in fault address register
   when TBI is enabled
 * cubieboard: make sure SOC object isn't leaked
 * fsl-imx25: Wire up eSDHC controllers
 * fsl-imx25: Wire up USB controllers
 * New board model: orangepi-pc (OrangePi PC)
 * ARM/KVM: if user doesn't select GIC version and the
   host kernel can only provide GICv3, use that, rather
   than defaulting to "fail because GICv2 isn't possible"
 * kvm: Only do KVM_SET_VCPU_EVENTS at the last stage of sync

# gpg: Signature made Thu 12 Mar 2020 16:43:46 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]
# Primary key fingerprint: E1A5 C593 CD41 9DE2 8E83  15CF 3C25 25ED 1436 0CDE

* remotes/pmaydell/tags/pull-target-arm-20200312: (36 commits)
  target/arm: kvm: Inject events at the last stage of sync
  hw/arm/virt: kvm: allow gicv3 by default if v2 cannot work
  hw/arm/virt: kvm: Restructure finalize_gic_version()
  target/arm/kvm: Let kvm_arm_vgic_probe() return a bitmap
  hw/arm/virt: Introduce finalize_gic_version()
  hw/arm/virt: Introduce VirtGICType enum type
  hw/arm/virt: Document 'max' value in gic-version property description
  docs: add Orange Pi PC document
  tests/boot_linux_console: Test booting NetBSD via U-Boot on OrangePi PC
  tests/boot_linux_console: Add a SLOW test booting Ubuntu on OrangePi PC
  tests/boot_linux_console: Add a SD card test for the OrangePi PC board
  tests/boot_linux_console: Add initrd test for the Orange Pi PC board
  tests/boot_linux_console: Add a quick test for the OrangePi PC board
  hw/arm/allwinner: add RTC device support
  hw/arm/allwinner-h3: add SDRAM controller device
  hw/arm/allwinner-h3: add Boot ROM support
  hw/arm/allwinner-h3: add EMAC ethernet device
  hw/arm/allwinner: add SD/MMC host controller
  hw/arm/allwinner: add Security Identifier device
  hw/arm/allwinner: add CPU Configuration module
  ...

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
Peter Maydell
2020-03-12 17:34:34 +00:00
57 changed files with 5787 additions and 74 deletions
+9
View File
@@ -492,6 +492,15 @@ F: hw/*/allwinner*
F: include/hw/*/allwinner*
F: hw/arm/cubieboard.c
Allwinner-h3
M: Niek Linnenbank <nieklinnenbank@gmail.com>
L: qemu-arm@nongnu.org
S: Maintained
F: hw/*/allwinner-h3*
F: include/hw/*/allwinner-h3*
F: hw/arm/orangepi.c
F: docs/system/orangepi.rst
ARM PrimeCell and CMSDK devices
M: Peter Maydell <peter.maydell@linaro.org>
L: qemu-arm@nongnu.org
+1
View File
@@ -175,6 +175,7 @@ trace-events-subdirs += hw/scsi
trace-events-subdirs += hw/sd
trace-events-subdirs += hw/sparc
trace-events-subdirs += hw/sparc64
trace-events-subdirs += hw/ssi
trace-events-subdirs += hw/timer
trace-events-subdirs += hw/tpm
trace-events-subdirs += hw/usb
+1
View File
@@ -41,3 +41,4 @@ CONFIG_FSL_IMX25=y
CONFIG_FSL_IMX7=y
CONFIG_FSL_IMX6UL=y
CONFIG_SEMIHOSTING=y
CONFIG_ALLWINNER_H3=y
+253
View File
@@ -0,0 +1,253 @@
Orange Pi PC (``orangepi-pc``)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
The Xunlong Orange Pi PC is an Allwinner H3 System on Chip
based embedded computer with mainline support in both U-Boot
and Linux. The board comes with a Quad Core Cortex-A7 @ 1.3GHz,
1GiB RAM, 100Mbit ethernet, USB, SD/MMC, USB, HDMI and
various other I/O.
Supported devices
"""""""""""""""""
The Orange Pi PC machine supports the following devices:
* SMP (Quad Core Cortex-A7)
* Generic Interrupt Controller configuration
* SRAM mappings
* SDRAM controller
* Real Time Clock
* Timer device (re-used from Allwinner A10)
* UART
* SD/MMC storage controller
* EMAC ethernet
* USB 2.0 interfaces
* Clock Control Unit
* System Control module
* Security Identifier device
Limitations
"""""""""""
Currently, Orange Pi PC does *not* support the following features:
- Graphical output via HDMI, GPU and/or the Display Engine
- Audio output
- Hardware Watchdog
Also see the 'unimplemented' array in the Allwinner H3 SoC module
for a complete list of unimplemented I/O devices: ``./hw/arm/allwinner-h3.c``
Boot options
""""""""""""
The Orange Pi PC machine can start using the standard -kernel functionality
for loading a Linux kernel or ELF executable. Additionally, the Orange Pi PC
machine can also emulate the BootROM which is present on an actual Allwinner H3
based SoC, which loads the bootloader from a SD card, specified via the -sd argument
to qemu-system-arm.
Machine-specific options
""""""""""""""""""""""""
The following machine-specific options are supported:
- allwinner-rtc.base-year=YYYY
The Allwinner RTC device is automatically created by the Orange Pi PC machine
and uses a default base year value which can be overridden using the 'base-year' property.
The base year is the actual represented year when the RTC year value is zero.
This option can be used in case the target operating system driver uses a different
base year value. The minimum value for the base year is 1900.
- allwinner-sid.identifier=abcd1122-a000-b000-c000-12345678ffff
The Security Identifier value can be read by the guest.
For example, U-Boot uses it to determine a unique MAC address.
The above machine-specific options can be specified in qemu-system-arm
via the '-global' argument, for example:
.. code-block:: bash
$ qemu-system-arm -M orangepi-pc -sd mycard.img \
-global allwinner-rtc.base-year=2000
Running mainline Linux
""""""""""""""""""""""
Mainline Linux kernels from 4.19 up to latest master are known to work.
To build a Linux mainline kernel that can be booted by the Orange Pi PC machine,
simply configure the kernel using the sunxi_defconfig configuration:
.. code-block:: bash
$ ARCH=arm CROSS_COMPILE=arm-linux-gnueabi- make mrproper
$ ARCH=arm CROSS_COMPILE=arm-linux-gnueabi- make sunxi_defconfig
To be able to use USB storage, you need to manually enable the corresponding
configuration item. Start the kconfig configuration tool:
.. code-block:: bash
$ ARCH=arm CROSS_COMPILE=arm-linux-gnueabi- make menuconfig
Navigate to the following item, enable it and save your configuration:
Device Drivers > USB support > USB Mass Storage support
Build the Linux kernel with:
.. code-block:: bash
$ ARCH=arm CROSS_COMPILE=arm-linux-gnueabi- make
To boot the newly build linux kernel in QEMU with the Orange Pi PC machine, use:
.. code-block:: bash
$ qemu-system-arm -M orangepi-pc -nic user -nographic \
-kernel /path/to/linux/arch/arm/boot/zImage \
-append 'console=ttyS0,115200' \
-dtb /path/to/linux/arch/arm/boot/dts/sun8i-h3-orangepi-pc.dtb
Orange Pi PC images
"""""""""""""""""""
Note that the mainline kernel does not have a root filesystem. You may provide it
with an official Orange Pi PC image from the official website:
http://www.orangepi.org/downloadresources/
Another possibility is to run an Armbian image for Orange Pi PC which
can be downloaded from:
https://www.armbian.com/orange-pi-pc/
Alternatively, you can also choose to build you own image with buildroot
using the orangepi_pc_defconfig. Also see https://buildroot.org for more information.
You can choose to attach the selected image either as an SD card or as USB mass storage.
For example, to boot using the Orange Pi PC Debian image on SD card, simply add the -sd
argument and provide the proper root= kernel parameter:
.. code-block:: bash
$ qemu-system-arm -M orangepi-pc -nic user -nographic \
-kernel /path/to/linux/arch/arm/boot/zImage \
-append 'console=ttyS0,115200 root=/dev/mmcblk0p2' \
-dtb /path/to/linux/arch/arm/boot/dts/sun8i-h3-orangepi-pc.dtb \
-sd OrangePi_pc_debian_stretch_server_linux5.3.5_v1.0.img
To attach the image as an USB mass storage device to the machine,
simply append to the command:
.. code-block:: bash
-drive if=none,id=stick,file=myimage.img \
-device usb-storage,bus=usb-bus.0,drive=stick
Instead of providing a custom Linux kernel via the -kernel command you may also
choose to let the Orange Pi PC machine load the bootloader from SD card, just like
a real board would do using the BootROM. Simply pass the selected image via the -sd
argument and remove the -kernel, -append, -dbt and -initrd arguments:
.. code-block:: bash
$ qemu-system-arm -M orangepi-pc -nic user -nographic \
-sd Armbian_19.11.3_Orangepipc_buster_current_5.3.9.img
Note that both the official Orange Pi PC images and Armbian images start
a lot of userland programs via systemd. Depending on the host hardware and OS,
they may be slow to emulate, especially due to emulating the 4 cores.
To help reduce the performance slow down due to emulating the 4 cores, you can
give the following kernel parameters via U-Boot (or via -append):
.. code-block:: bash
=> setenv extraargs 'systemd.default_timeout_start_sec=9000 loglevel=7 nosmp console=ttyS0,115200'
Running U-Boot
""""""""""""""
U-Boot mainline can be build and configured using the orangepi_pc_defconfig
using similar commands as describe above for Linux. Note that it is recommended
for development/testing to select the following configuration setting in U-Boot:
Device Tree Control > Provider for DTB for DT Control > Embedded DTB
To start U-Boot using the Orange Pi PC machine, provide the
u-boot binary to the -kernel argument:
.. code-block:: bash
$ qemu-system-arm -M orangepi-pc -nic user -nographic \
-kernel /path/to/uboot/u-boot -sd disk.img
Use the following U-boot commands to load and boot a Linux kernel from SD card:
.. code-block:: bash
=> setenv bootargs console=ttyS0,115200
=> ext2load mmc 0 0x42000000 zImage
=> ext2load mmc 0 0x43000000 sun8i-h3-orangepi-pc.dtb
=> bootz 0x42000000 - 0x43000000
Running NetBSD
""""""""""""""
The NetBSD operating system also includes support for Allwinner H3 based boards,
including the Orange Pi PC. NetBSD 9.0 is known to work best for the Orange Pi PC
board and provides a fully working system with serial console, networking and storage.
For the Orange Pi PC machine, get the 'evbarm-earmv7hf' based image from:
https://cdn.netbsd.org/pub/NetBSD/NetBSD-9.0/evbarm-earmv7hf/binary/gzimg/armv7.img.gz
The image requires manually installing U-Boot in the image. Build U-Boot with
the orangepi_pc_defconfig configuration as described in the previous section.
Next, unzip the NetBSD image and write the U-Boot binary including SPL using:
.. code-block:: bash
$ gunzip armv7.img.gz
$ dd if=/path/to/u-boot-sunxi-with-spl.bin of=armv7.img bs=1024 seek=8 conv=notrunc
Finally, before starting the machine the SD image must be extended such
that the NetBSD kernel will not conclude the NetBSD partition is larger than
the emulated SD card:
.. code-block:: bash
$ dd if=/dev/zero bs=1M count=64 >> armv7.img
Start the machine using the following command:
.. code-block:: bash
$ qemu-system-arm -M orangepi-pc -nic user -nographic \
-sd armv7.img -global allwinner-rtc.base-year=2000
At the U-Boot stage, interrupt the automatic boot process by pressing a key
and set the following environment variables before booting:
.. code-block:: bash
=> setenv bootargs root=ld0a
=> setenv kernel netbsd-GENERIC.ub
=> setenv fdtfile dtb/sun8i-h3-orangepi-pc.dtb
=> setenv bootcmd 'fatload mmc 0:1 ${kernel_addr_r} ${kernel}; fatload mmc 0:1 ${fdt_addr_r} ${fdtfile}; fdt addr ${fdt_addr_r}; bootm ${kernel_addr_r} - ${fdt_addr_r}'
Optionally you may save the environment variables to SD card with 'saveenv'.
To continue booting simply give the 'boot' command and NetBSD boots.
Orange Pi PC acceptance tests
"""""""""""""""""""""""""""""
The Orange Pi PC machine has several acceptance tests included.
To run the whole set of tests, build QEMU from source and simply
provide the following command:
.. code-block:: bash
$ AVOCADO_ALLOW_LARGE_STORAGE=yes avocado --show=app,console run \
-t machine:orangepi-pc tests/acceptance/boot_linux_console.py
+2
View File
@@ -68,6 +68,7 @@ undocumented; you can get a complete list by running
``qemu-system-aarch64 --machine help``.
.. toctree::
:maxdepth: 1
arm/integratorcp
arm/versatile
@@ -78,6 +79,7 @@ undocumented; you can get a complete list by running
arm/stellaris
arm/musicpal
arm/sx1
arm/orangepi
Arm CPU features
================
+12
View File
@@ -297,6 +297,18 @@ config ALLWINNER_A10
select SERIAL
select UNIMP
config ALLWINNER_H3
bool
select ALLWINNER_A10_PIT
select ALLWINNER_SUN8I_EMAC
select SERIAL
select ARM_TIMER
select ARM_GIC
select UNIMP
select USB_OHCI
select USB_EHCI_SYSBUS
select SD
config RASPI
bool
select FRAMEBUFFER
+1
View File
@@ -35,6 +35,7 @@ obj-$(CONFIG_DIGIC) += digic.o
obj-$(CONFIG_OMAP) += omap1.o omap2.o
obj-$(CONFIG_STRONGARM) += strongarm.o
obj-$(CONFIG_ALLWINNER_A10) += allwinner-a10.o cubieboard.o
obj-$(CONFIG_ALLWINNER_H3) += allwinner-h3.o orangepi.o
obj-$(CONFIG_RASPI) += bcm2835_peripherals.o bcm2836.o raspi.o
obj-$(CONFIG_STM32F205_SOC) += stm32f205_soc.o
obj-$(CONFIG_STM32F405_SOC) += stm32f405_soc.o
+19
View File
@@ -27,6 +27,7 @@
#include "hw/boards.h"
#include "hw/usb/hcd-ohci.h"
#define AW_A10_MMC0_BASE 0x01c0f000
#define AW_A10_PIC_REG_BASE 0x01c20400
#define AW_A10_PIT_REG_BASE 0x01c20c00
#define AW_A10_UART0_REG_BASE 0x01c28000
@@ -34,6 +35,7 @@
#define AW_A10_EHCI_BASE 0x01c14000
#define AW_A10_OHCI_BASE 0x01c14400
#define AW_A10_SATA_BASE 0x01c18000
#define AW_A10_RTC_BASE 0x01c20d00
static void aw_a10_init(Object *obj)
{
@@ -64,6 +66,12 @@ static void aw_a10_init(Object *obj)
sizeof(s->ohci[i]), TYPE_SYSBUS_OHCI);
}
}
sysbus_init_child_obj(obj, "mmc0", &s->mmc0, sizeof(s->mmc0),
TYPE_AW_SDHOST_SUN4I);
sysbus_init_child_obj(obj, "rtc", &s->rtc, sizeof(s->rtc),
TYPE_AW_RTC_SUN4I);
}
static void aw_a10_realize(DeviceState *dev, Error **errp)
@@ -164,6 +172,17 @@ static void aw_a10_realize(DeviceState *dev, Error **errp)
qdev_get_gpio_in(dev, 64 + i));
}
}
/* SD/MMC */
qdev_init_nofail(DEVICE(&s->mmc0));
sysbus_mmio_map(SYS_BUS_DEVICE(&s->mmc0), 0, AW_A10_MMC0_BASE);
sysbus_connect_irq(SYS_BUS_DEVICE(&s->mmc0), 0, qdev_get_gpio_in(dev, 32));
object_property_add_alias(OBJECT(s), "sd-bus", OBJECT(&s->mmc0),
"sd-bus", &error_abort);
/* RTC */
qdev_init_nofail(DEVICE(&s->rtc));
sysbus_mmio_map_overlap(SYS_BUS_DEVICE(&s->rtc), 0, AW_A10_RTC_BASE, 10);
}
static void aw_a10_class_init(ObjectClass *oc, void *data)
+465
View File
@@ -0,0 +1,465 @@
/*
* Allwinner H3 System on Chip emulation
*
* Copyright (C) 2019 Niek Linnenbank <nieklinnenbank@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "qemu/osdep.h"
#include "exec/address-spaces.h"
#include "qapi/error.h"
#include "qemu/error-report.h"
#include "qemu/module.h"
#include "qemu/units.h"
#include "hw/qdev-core.h"
#include "cpu.h"
#include "hw/sysbus.h"
#include "hw/char/serial.h"
#include "hw/misc/unimp.h"
#include "hw/usb/hcd-ehci.h"
#include "hw/loader.h"
#include "sysemu/sysemu.h"
#include "hw/arm/allwinner-h3.h"
/* Memory map */
const hwaddr allwinner_h3_memmap[] = {
[AW_H3_SRAM_A1] = 0x00000000,
[AW_H3_SRAM_A2] = 0x00044000,
[AW_H3_SRAM_C] = 0x00010000,
[AW_H3_SYSCTRL] = 0x01c00000,
[AW_H3_MMC0] = 0x01c0f000,
[AW_H3_SID] = 0x01c14000,
[AW_H3_EHCI0] = 0x01c1a000,
[AW_H3_OHCI0] = 0x01c1a400,
[AW_H3_EHCI1] = 0x01c1b000,
[AW_H3_OHCI1] = 0x01c1b400,
[AW_H3_EHCI2] = 0x01c1c000,
[AW_H3_OHCI2] = 0x01c1c400,
[AW_H3_EHCI3] = 0x01c1d000,
[AW_H3_OHCI3] = 0x01c1d400,
[AW_H3_CCU] = 0x01c20000,
[AW_H3_PIT] = 0x01c20c00,
[AW_H3_UART0] = 0x01c28000,
[AW_H3_UART1] = 0x01c28400,
[AW_H3_UART2] = 0x01c28800,
[AW_H3_UART3] = 0x01c28c00,
[AW_H3_EMAC] = 0x01c30000,
[AW_H3_DRAMCOM] = 0x01c62000,
[AW_H3_DRAMCTL] = 0x01c63000,
[AW_H3_DRAMPHY] = 0x01c65000,
[AW_H3_GIC_DIST] = 0x01c81000,
[AW_H3_GIC_CPU] = 0x01c82000,
[AW_H3_GIC_HYP] = 0x01c84000,
[AW_H3_GIC_VCPU] = 0x01c86000,
[AW_H3_RTC] = 0x01f00000,
[AW_H3_CPUCFG] = 0x01f01c00,
[AW_H3_SDRAM] = 0x40000000
};
/* List of unimplemented devices */
struct AwH3Unimplemented {
const char *device_name;
hwaddr base;
hwaddr size;
} unimplemented[] = {
{ "d-engine", 0x01000000, 4 * MiB },
{ "d-inter", 0x01400000, 128 * KiB },
{ "dma", 0x01c02000, 4 * KiB },
{ "nfdc", 0x01c03000, 4 * KiB },
{ "ts", 0x01c06000, 4 * KiB },
{ "keymem", 0x01c0b000, 4 * KiB },
{ "lcd0", 0x01c0c000, 4 * KiB },
{ "lcd1", 0x01c0d000, 4 * KiB },
{ "ve", 0x01c0e000, 4 * KiB },
{ "mmc1", 0x01c10000, 4 * KiB },
{ "mmc2", 0x01c11000, 4 * KiB },
{ "crypto", 0x01c15000, 4 * KiB },
{ "msgbox", 0x01c17000, 4 * KiB },
{ "spinlock", 0x01c18000, 4 * KiB },
{ "usb0-otg", 0x01c19000, 4 * KiB },
{ "usb0-phy", 0x01c1a000, 4 * KiB },
{ "usb1-phy", 0x01c1b000, 4 * KiB },
{ "usb2-phy", 0x01c1c000, 4 * KiB },
{ "usb3-phy", 0x01c1d000, 4 * KiB },
{ "smc", 0x01c1e000, 4 * KiB },
{ "pio", 0x01c20800, 1 * KiB },
{ "owa", 0x01c21000, 1 * KiB },
{ "pwm", 0x01c21400, 1 * KiB },
{ "keyadc", 0x01c21800, 1 * KiB },
{ "pcm0", 0x01c22000, 1 * KiB },
{ "pcm1", 0x01c22400, 1 * KiB },
{ "pcm2", 0x01c22800, 1 * KiB },
{ "audio", 0x01c22c00, 2 * KiB },
{ "smta", 0x01c23400, 1 * KiB },
{ "ths", 0x01c25000, 1 * KiB },
{ "uart0", 0x01c28000, 1 * KiB },
{ "uart1", 0x01c28400, 1 * KiB },
{ "uart2", 0x01c28800, 1 * KiB },
{ "uart3", 0x01c28c00, 1 * KiB },
{ "twi0", 0x01c2ac00, 1 * KiB },
{ "twi1", 0x01c2b000, 1 * KiB },
{ "twi2", 0x01c2b400, 1 * KiB },
{ "scr", 0x01c2c400, 1 * KiB },
{ "gpu", 0x01c40000, 64 * KiB },
{ "hstmr", 0x01c60000, 4 * KiB },
{ "spi0", 0x01c68000, 4 * KiB },
{ "spi1", 0x01c69000, 4 * KiB },
{ "csi", 0x01cb0000, 320 * KiB },
{ "tve", 0x01e00000, 64 * KiB },
{ "hdmi", 0x01ee0000, 128 * KiB },
{ "r_timer", 0x01f00800, 1 * KiB },
{ "r_intc", 0x01f00c00, 1 * KiB },
{ "r_wdog", 0x01f01000, 1 * KiB },
{ "r_prcm", 0x01f01400, 1 * KiB },
{ "r_twd", 0x01f01800, 1 * KiB },
{ "r_cir-rx", 0x01f02000, 1 * KiB },
{ "r_twi", 0x01f02400, 1 * KiB },
{ "r_uart", 0x01f02800, 1 * KiB },
{ "r_pio", 0x01f02c00, 1 * KiB },
{ "r_pwm", 0x01f03800, 1 * KiB },
{ "core-dbg", 0x3f500000, 128 * KiB },
{ "tsgen-ro", 0x3f506000, 4 * KiB },
{ "tsgen-ctl", 0x3f507000, 4 * KiB },
{ "ddr-mem", 0x40000000, 2 * GiB },
{ "n-brom", 0xffff0000, 32 * KiB },
{ "s-brom", 0xffff0000, 64 * KiB }
};
/* Per Processor Interrupts */
enum {
AW_H3_GIC_PPI_MAINT = 9,
AW_H3_GIC_PPI_HYPTIMER = 10,
AW_H3_GIC_PPI_VIRTTIMER = 11,
AW_H3_GIC_PPI_SECTIMER = 13,
AW_H3_GIC_PPI_PHYSTIMER = 14
};
/* Shared Processor Interrupts */
enum {
AW_H3_GIC_SPI_UART0 = 0,
AW_H3_GIC_SPI_UART1 = 1,
AW_H3_GIC_SPI_UART2 = 2,
AW_H3_GIC_SPI_UART3 = 3,
AW_H3_GIC_SPI_TIMER0 = 18,
AW_H3_GIC_SPI_TIMER1 = 19,
AW_H3_GIC_SPI_MMC0 = 60,
AW_H3_GIC_SPI_EHCI0 = 72,
AW_H3_GIC_SPI_OHCI0 = 73,
AW_H3_GIC_SPI_EHCI1 = 74,
AW_H3_GIC_SPI_OHCI1 = 75,
AW_H3_GIC_SPI_EHCI2 = 76,
AW_H3_GIC_SPI_OHCI2 = 77,
AW_H3_GIC_SPI_EHCI3 = 78,
AW_H3_GIC_SPI_OHCI3 = 79,
AW_H3_GIC_SPI_EMAC = 82
};
/* Allwinner H3 general constants */
enum {
AW_H3_GIC_NUM_SPI = 128
};
void allwinner_h3_bootrom_setup(AwH3State *s, BlockBackend *blk)
{
const int64_t rom_size = 32 * KiB;
g_autofree uint8_t *buffer = g_new0(uint8_t, rom_size);
if (blk_pread(blk, 8 * KiB, buffer, rom_size) < 0) {
error_setg(&error_fatal, "%s: failed to read BlockBackend data",
__func__);
return;
}
rom_add_blob("allwinner-h3.bootrom", buffer, rom_size,
rom_size, s->memmap[AW_H3_SRAM_A1],
NULL, NULL, NULL, NULL, false);
}
static void allwinner_h3_init(Object *obj)
{
AwH3State *s = AW_H3(obj);
s->memmap = allwinner_h3_memmap;
for (int i = 0; i < AW_H3_NUM_CPUS; i++) {
object_initialize_child(obj, "cpu[*]", &s->cpus[i], sizeof(s->cpus[i]),
ARM_CPU_TYPE_NAME("cortex-a7"),
&error_abort, NULL);
}
sysbus_init_child_obj(obj, "gic", &s->gic, sizeof(s->gic),
TYPE_ARM_GIC);
sysbus_init_child_obj(obj, "timer", &s->timer, sizeof(s->timer),
TYPE_AW_A10_PIT);
object_property_add_alias(obj, "clk0-freq", OBJECT(&s->timer),
"clk0-freq", &error_abort);
object_property_add_alias(obj, "clk1-freq", OBJECT(&s->timer),
"clk1-freq", &error_abort);
sysbus_init_child_obj(obj, "ccu", &s->ccu, sizeof(s->ccu),
TYPE_AW_H3_CCU);
sysbus_init_child_obj(obj, "sysctrl", &s->sysctrl, sizeof(s->sysctrl),
TYPE_AW_H3_SYSCTRL);
sysbus_init_child_obj(obj, "cpucfg", &s->cpucfg, sizeof(s->cpucfg),
TYPE_AW_CPUCFG);
sysbus_init_child_obj(obj, "sid", &s->sid, sizeof(s->sid),
TYPE_AW_SID);
object_property_add_alias(obj, "identifier", OBJECT(&s->sid),
"identifier", &error_abort);
sysbus_init_child_obj(obj, "mmc0", &s->mmc0, sizeof(s->mmc0),
TYPE_AW_SDHOST_SUN5I);
sysbus_init_child_obj(obj, "emac", &s->emac, sizeof(s->emac),
TYPE_AW_SUN8I_EMAC);
sysbus_init_child_obj(obj, "dramc", &s->dramc, sizeof(s->dramc),
TYPE_AW_H3_DRAMC);
object_property_add_alias(obj, "ram-addr", OBJECT(&s->dramc),
"ram-addr", &error_abort);
object_property_add_alias(obj, "ram-size", OBJECT(&s->dramc),
"ram-size", &error_abort);
sysbus_init_child_obj(obj, "rtc", &s->rtc, sizeof(s->rtc),
TYPE_AW_RTC_SUN6I);
}
static void allwinner_h3_realize(DeviceState *dev, Error **errp)
{
AwH3State *s = AW_H3(dev);
unsigned i;
/* CPUs */
for (i = 0; i < AW_H3_NUM_CPUS; i++) {
/* Provide Power State Coordination Interface */
qdev_prop_set_int32(DEVICE(&s->cpus[i]), "psci-conduit",
QEMU_PSCI_CONDUIT_HVC);
/* Disable secondary CPUs */
qdev_prop_set_bit(DEVICE(&s->cpus[i]), "start-powered-off",
i > 0);
/* All exception levels required */
qdev_prop_set_bit(DEVICE(&s->cpus[i]), "has_el3", true);
qdev_prop_set_bit(DEVICE(&s->cpus[i]), "has_el2", true);
/* Mark realized */
qdev_init_nofail(DEVICE(&s->cpus[i]));
}
/* Generic Interrupt Controller */
qdev_prop_set_uint32(DEVICE(&s->gic), "num-irq", AW_H3_GIC_NUM_SPI +
GIC_INTERNAL);
qdev_prop_set_uint32(DEVICE(&s->gic), "revision", 2);
qdev_prop_set_uint32(DEVICE(&s->gic), "num-cpu", AW_H3_NUM_CPUS);
qdev_prop_set_bit(DEVICE(&s->gic), "has-security-extensions", false);
qdev_prop_set_bit(DEVICE(&s->gic), "has-virtualization-extensions", true);
qdev_init_nofail(DEVICE(&s->gic));
sysbus_mmio_map(SYS_BUS_DEVICE(&s->gic), 0, s->memmap[AW_H3_GIC_DIST]);
sysbus_mmio_map(SYS_BUS_DEVICE(&s->gic), 1, s->memmap[AW_H3_GIC_CPU]);
sysbus_mmio_map(SYS_BUS_DEVICE(&s->gic), 2, s->memmap[AW_H3_GIC_HYP]);
sysbus_mmio_map(SYS_BUS_DEVICE(&s->gic), 3, s->memmap[AW_H3_GIC_VCPU]);
/*
* Wire the outputs from each CPU's generic timer and the GICv3
* maintenance interrupt signal to the appropriate GIC PPI inputs,
* and the GIC's IRQ/FIQ/VIRQ/VFIQ interrupt outputs to the CPU's inputs.
*/
for (i = 0; i < AW_H3_NUM_CPUS; i++) {
DeviceState *cpudev = DEVICE(&s->cpus[i]);
int ppibase = AW_H3_GIC_NUM_SPI + i * GIC_INTERNAL + GIC_NR_SGIS;
int irq;
/*
* Mapping from the output timer irq lines from the CPU to the
* GIC PPI inputs used for this board.
*/
const int timer_irq[] = {
[GTIMER_PHYS] = AW_H3_GIC_PPI_PHYSTIMER,
[GTIMER_VIRT] = AW_H3_GIC_PPI_VIRTTIMER,
[GTIMER_HYP] = AW_H3_GIC_PPI_HYPTIMER,
[GTIMER_SEC] = AW_H3_GIC_PPI_SECTIMER,
};
/* Connect CPU timer outputs to GIC PPI inputs */
for (irq = 0; irq < ARRAY_SIZE(timer_irq); irq++) {
qdev_connect_gpio_out(cpudev, irq,
qdev_get_gpio_in(DEVICE(&s->gic),
ppibase + timer_irq[irq]));
}
/* Connect GIC outputs to CPU interrupt inputs */
sysbus_connect_irq(SYS_BUS_DEVICE(&s->gic), i,
qdev_get_gpio_in(cpudev, ARM_CPU_IRQ));
sysbus_connect_irq(SYS_BUS_DEVICE(&s->gic), i + AW_H3_NUM_CPUS,
qdev_get_gpio_in(cpudev, ARM_CPU_FIQ));
sysbus_connect_irq(SYS_BUS_DEVICE(&s->gic), i + (2 * AW_H3_NUM_CPUS),
qdev_get_gpio_in(cpudev, ARM_CPU_VIRQ));
sysbus_connect_irq(SYS_BUS_DEVICE(&s->gic), i + (3 * AW_H3_NUM_CPUS),
qdev_get_gpio_in(cpudev, ARM_CPU_VFIQ));
/* GIC maintenance signal */
sysbus_connect_irq(SYS_BUS_DEVICE(&s->gic), i + (4 * AW_H3_NUM_CPUS),
qdev_get_gpio_in(DEVICE(&s->gic),
ppibase + AW_H3_GIC_PPI_MAINT));
}
/* Timer */
qdev_init_nofail(DEVICE(&s->timer));
sysbus_mmio_map(SYS_BUS_DEVICE(&s->timer), 0, s->memmap[AW_H3_PIT]);
sysbus_connect_irq(SYS_BUS_DEVICE(&s->timer), 0,
qdev_get_gpio_in(DEVICE(&s->gic), AW_H3_GIC_SPI_TIMER0));
sysbus_connect_irq(SYS_BUS_DEVICE(&s->timer), 1,
qdev_get_gpio_in(DEVICE(&s->gic), AW_H3_GIC_SPI_TIMER1));
/* SRAM */
memory_region_init_ram(&s->sram_a1, OBJECT(dev), "sram A1",
64 * KiB, &error_abort);
memory_region_init_ram(&s->sram_a2, OBJECT(dev), "sram A2",
32 * KiB, &error_abort);
memory_region_init_ram(&s->sram_c, OBJECT(dev), "sram C",
44 * KiB, &error_abort);
memory_region_add_subregion(get_system_memory(), s->memmap[AW_H3_SRAM_A1],
&s->sram_a1);
memory_region_add_subregion(get_system_memory(), s->memmap[AW_H3_SRAM_A2],
&s->sram_a2);
memory_region_add_subregion(get_system_memory(), s->memmap[AW_H3_SRAM_C],
&s->sram_c);
/* Clock Control Unit */
qdev_init_nofail(DEVICE(&s->ccu));
sysbus_mmio_map(SYS_BUS_DEVICE(&s->ccu), 0, s->memmap[AW_H3_CCU]);
/* System Control */
qdev_init_nofail(DEVICE(&s->sysctrl));
sysbus_mmio_map(SYS_BUS_DEVICE(&s->sysctrl), 0, s->memmap[AW_H3_SYSCTRL]);
/* CPU Configuration */
qdev_init_nofail(DEVICE(&s->cpucfg));
sysbus_mmio_map(SYS_BUS_DEVICE(&s->cpucfg), 0, s->memmap[AW_H3_CPUCFG]);
/* Security Identifier */
qdev_init_nofail(DEVICE(&s->sid));
sysbus_mmio_map(SYS_BUS_DEVICE(&s->sid), 0, s->memmap[AW_H3_SID]);
/* SD/MMC */
qdev_init_nofail(DEVICE(&s->mmc0));
sysbus_mmio_map(SYS_BUS_DEVICE(&s->mmc0), 0, s->memmap[AW_H3_MMC0]);
sysbus_connect_irq(SYS_BUS_DEVICE(&s->mmc0), 0,
qdev_get_gpio_in(DEVICE(&s->gic), AW_H3_GIC_SPI_MMC0));
object_property_add_alias(OBJECT(s), "sd-bus", OBJECT(&s->mmc0),
"sd-bus", &error_abort);
/* EMAC */
if (nd_table[0].used) {
qemu_check_nic_model(&nd_table[0], TYPE_AW_SUN8I_EMAC);
qdev_set_nic_properties(DEVICE(&s->emac), &nd_table[0]);
}
qdev_init_nofail(DEVICE(&s->emac));
sysbus_mmio_map(SYS_BUS_DEVICE(&s->emac), 0, s->memmap[AW_H3_EMAC]);
sysbus_connect_irq(SYS_BUS_DEVICE(&s->emac), 0,
qdev_get_gpio_in(DEVICE(&s->gic), AW_H3_GIC_SPI_EMAC));
/* Universal Serial Bus */
sysbus_create_simple(TYPE_AW_H3_EHCI, s->memmap[AW_H3_EHCI0],
qdev_get_gpio_in(DEVICE(&s->gic),
AW_H3_GIC_SPI_EHCI0));
sysbus_create_simple(TYPE_AW_H3_EHCI, s->memmap[AW_H3_EHCI1],
qdev_get_gpio_in(DEVICE(&s->gic),
AW_H3_GIC_SPI_EHCI1));
sysbus_create_simple(TYPE_AW_H3_EHCI, s->memmap[AW_H3_EHCI2],
qdev_get_gpio_in(DEVICE(&s->gic),
AW_H3_GIC_SPI_EHCI2));
sysbus_create_simple(TYPE_AW_H3_EHCI, s->memmap[AW_H3_EHCI3],
qdev_get_gpio_in(DEVICE(&s->gic),
AW_H3_GIC_SPI_EHCI3));
sysbus_create_simple("sysbus-ohci", s->memmap[AW_H3_OHCI0],
qdev_get_gpio_in(DEVICE(&s->gic),
AW_H3_GIC_SPI_OHCI0));
sysbus_create_simple("sysbus-ohci", s->memmap[AW_H3_OHCI1],
qdev_get_gpio_in(DEVICE(&s->gic),
AW_H3_GIC_SPI_OHCI1));
sysbus_create_simple("sysbus-ohci", s->memmap[AW_H3_OHCI2],
qdev_get_gpio_in(DEVICE(&s->gic),
AW_H3_GIC_SPI_OHCI2));
sysbus_create_simple("sysbus-ohci", s->memmap[AW_H3_OHCI3],
qdev_get_gpio_in(DEVICE(&s->gic),
AW_H3_GIC_SPI_OHCI3));
/* UART0. For future clocktree API: All UARTS are connected to APB2_CLK. */
serial_mm_init(get_system_memory(), s->memmap[AW_H3_UART0], 2,
qdev_get_gpio_in(DEVICE(&s->gic), AW_H3_GIC_SPI_UART0),
115200, serial_hd(0), DEVICE_NATIVE_ENDIAN);
/* UART1 */
serial_mm_init(get_system_memory(), s->memmap[AW_H3_UART1], 2,
qdev_get_gpio_in(DEVICE(&s->gic), AW_H3_GIC_SPI_UART1),
115200, serial_hd(1), DEVICE_NATIVE_ENDIAN);
/* UART2 */
serial_mm_init(get_system_memory(), s->memmap[AW_H3_UART2], 2,
qdev_get_gpio_in(DEVICE(&s->gic), AW_H3_GIC_SPI_UART2),
115200, serial_hd(2), DEVICE_NATIVE_ENDIAN);
/* UART3 */
serial_mm_init(get_system_memory(), s->memmap[AW_H3_UART3], 2,
qdev_get_gpio_in(DEVICE(&s->gic), AW_H3_GIC_SPI_UART3),
115200, serial_hd(3), DEVICE_NATIVE_ENDIAN);
/* DRAMC */
qdev_init_nofail(DEVICE(&s->dramc));
sysbus_mmio_map(SYS_BUS_DEVICE(&s->dramc), 0, s->memmap[AW_H3_DRAMCOM]);
sysbus_mmio_map(SYS_BUS_DEVICE(&s->dramc), 1, s->memmap[AW_H3_DRAMCTL]);
sysbus_mmio_map(SYS_BUS_DEVICE(&s->dramc), 2, s->memmap[AW_H3_DRAMPHY]);
/* RTC */
qdev_init_nofail(DEVICE(&s->rtc));
sysbus_mmio_map(SYS_BUS_DEVICE(&s->rtc), 0, s->memmap[AW_H3_RTC]);
/* Unimplemented devices */
for (i = 0; i < ARRAY_SIZE(unimplemented); i++) {
create_unimplemented_device(unimplemented[i].device_name,
unimplemented[i].base,
unimplemented[i].size);
}
}
static void allwinner_h3_class_init(ObjectClass *oc, void *data)
{
DeviceClass *dc = DEVICE_CLASS(oc);
dc->realize = allwinner_h3_realize;
/* Reason: uses serial_hd() in realize function */
dc->user_creatable = false;
}
static const TypeInfo allwinner_h3_type_info = {
.name = TYPE_AW_H3,
.parent = TYPE_DEVICE,
.instance_size = sizeof(AwH3State),
.instance_init = allwinner_h3_init,
.class_init = allwinner_h3_class_init,
};
static void allwinner_h3_register_types(void)
{
type_register_static(&allwinner_h3_type_info);
}
type_init(allwinner_h3_register_types)
+18
View File
@@ -22,6 +22,7 @@
#include "sysemu/sysemu.h"
#include "hw/sysbus.h"
#include "hw/boards.h"
#include "hw/qdev-properties.h"
#include "hw/arm/allwinner-a10.h"
static struct arm_boot_info cubieboard_binfo = {
@@ -33,6 +34,10 @@ static void cubieboard_init(MachineState *machine)
{
AwA10State *a10;
Error *err = NULL;
DriveInfo *di;
BlockBackend *blk;
BusState *bus;
DeviceState *carddev;
/* BIOS is not supported by this board */
if (bios_name) {
@@ -54,6 +59,9 @@ static void cubieboard_init(MachineState *machine)
}
a10 = AW_A10(object_new(TYPE_AW_A10));
object_property_add_child(OBJECT(machine), "soc", OBJECT(a10),
&error_abort);
object_unref(OBJECT(a10));
object_property_set_int(OBJECT(&a10->emac), 1, "phy-addr", &err);
if (err != NULL) {
@@ -79,6 +87,16 @@ static void cubieboard_init(MachineState *machine)
exit(1);
}
/* Retrieve SD bus */
di = drive_get_next(IF_SD);
blk = di ? blk_by_legacy_dinfo(di) : NULL;
bus = qdev_get_child_bus(DEVICE(a10), "sd-bus");
/* Plug in SD card */
carddev = qdev_create(bus, TYPE_SD_CARD);
qdev_prop_set_drive(carddev, "drive", blk, &error_fatal);
object_property_set_bool(OBJECT(carddev), true, "realized", &error_fatal);
memory_region_add_subregion(get_system_memory(), AW_A10_SDRAM_BASE,
machine->ram);
+56
View File
@@ -31,6 +31,8 @@
#include "hw/qdev-properties.h"
#include "chardev/char.h"
#define IMX25_ESDHC_CAPABILITIES 0x07e20000
static void fsl_imx25_init(Object *obj)
{
FslIMX25State *s = FSL_IMX25(obj);
@@ -74,6 +76,17 @@ static void fsl_imx25_init(Object *obj)
sysbus_init_child_obj(obj, "gpio[*]", &s->gpio[i], sizeof(s->gpio[i]),
TYPE_IMX_GPIO);
}
for (i = 0; i < FSL_IMX25_NUM_ESDHCS; i++) {
sysbus_init_child_obj(obj, "sdhc[*]", &s->esdhc[i], sizeof(s->esdhc[i]),
TYPE_IMX_USDHC);
}
for (i = 0; i < FSL_IMX25_NUM_USBS; i++) {
sysbus_init_child_obj(obj, "usb[*]", &s->usb[i], sizeof(s->usb[i]),
TYPE_CHIPIDEA);
}
}
static void fsl_imx25_realize(DeviceState *dev, Error **errp)
@@ -246,6 +259,49 @@ static void fsl_imx25_realize(DeviceState *dev, Error **errp)
gpio_table[i].irq));
}
/* Initialize all SDHC */
for (i = 0; i < FSL_IMX25_NUM_ESDHCS; i++) {
static const struct {
hwaddr addr;
unsigned int irq;
} esdhc_table[FSL_IMX25_NUM_ESDHCS] = {
{ FSL_IMX25_ESDHC1_ADDR, FSL_IMX25_ESDHC1_IRQ },
{ FSL_IMX25_ESDHC2_ADDR, FSL_IMX25_ESDHC2_IRQ },
};
object_property_set_uint(OBJECT(&s->esdhc[i]), 2, "sd-spec-version",
&err);
object_property_set_uint(OBJECT(&s->esdhc[i]), IMX25_ESDHC_CAPABILITIES,
"capareg", &err);
object_property_set_bool(OBJECT(&s->esdhc[i]), true, "realized", &err);
if (err) {
error_propagate(errp, err);
return;
}
sysbus_mmio_map(SYS_BUS_DEVICE(&s->esdhc[i]), 0, esdhc_table[i].addr);
sysbus_connect_irq(SYS_BUS_DEVICE(&s->esdhc[i]), 0,
qdev_get_gpio_in(DEVICE(&s->avic),
esdhc_table[i].irq));
}
/* USB */
for (i = 0; i < FSL_IMX25_NUM_USBS; i++) {
static const struct {
hwaddr addr;
unsigned int irq;
} usb_table[FSL_IMX25_NUM_USBS] = {
{ FSL_IMX25_USB1_ADDR, FSL_IMX25_USB1_IRQ },
{ FSL_IMX25_USB2_ADDR, FSL_IMX25_USB2_IRQ },
};
object_property_set_bool(OBJECT(&s->usb[i]), true, "realized",
&error_abort);
sysbus_mmio_map(SYS_BUS_DEVICE(&s->usb[i]), 0, usb_table[i].addr);
sysbus_connect_irq(SYS_BUS_DEVICE(&s->usb[i]), 0,
qdev_get_gpio_in(DEVICE(&s->avic),
usb_table[i].irq));
}
/* initialize 2 x 16 KB ROM */
memory_region_init_rom(&s->rom[0], NULL,
"imx25.rom0", FSL_IMX25_ROM0_SIZE, &err);
+16
View File
@@ -26,6 +26,7 @@
#include "qemu/osdep.h"
#include "qapi/error.h"
#include "cpu.h"
#include "hw/qdev-properties.h"
#include "hw/arm/fsl-imx25.h"
#include "hw/boards.h"
#include "qemu/error-report.h"
@@ -120,6 +121,21 @@ static void imx25_pdk_init(MachineState *machine)
imx25_pdk_binfo.board_id = 1771,
imx25_pdk_binfo.nb_cpus = 1;
for (i = 0; i < FSL_IMX25_NUM_ESDHCS; i++) {
BusState *bus;
DeviceState *carddev;
DriveInfo *di;
BlockBackend *blk;
di = drive_get_next(IF_SD);
blk = di ? blk_by_legacy_dinfo(di) : NULL;
bus = qdev_get_child_bus(DEVICE(&s->soc.esdhc[i]), "sd-bus");
carddev = qdev_create(bus, TYPE_SD_CARD);
qdev_prop_set_drive(carddev, "drive", blk, &error_fatal);
object_property_set_bool(OBJECT(carddev), true,
"realized", &error_fatal);
}
/*
* We test explicitly for qtest here as it is not done (yet?) in
* arm_load_kernel(). Without this the "make check" command would
+130
View File
@@ -0,0 +1,130 @@
/*
* Orange Pi emulation
*
* Copyright (C) 2019 Niek Linnenbank <nieklinnenbank@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "qemu/osdep.h"
#include "qemu/units.h"
#include "exec/address-spaces.h"
#include "qapi/error.h"
#include "cpu.h"
#include "hw/sysbus.h"
#include "hw/boards.h"
#include "hw/qdev-properties.h"
#include "hw/arm/allwinner-h3.h"
#include "sysemu/sysemu.h"
static struct arm_boot_info orangepi_binfo = {
.nb_cpus = AW_H3_NUM_CPUS,
};
static void orangepi_init(MachineState *machine)
{
AwH3State *h3;
DriveInfo *di;
BlockBackend *blk;
BusState *bus;
DeviceState *carddev;
/* BIOS is not supported by this board */
if (bios_name) {
error_report("BIOS not supported for this machine");
exit(1);
}
/* This board has fixed size RAM */
if (machine->ram_size != 1 * GiB) {
error_report("This machine can only be used with 1GiB of RAM");
exit(1);
}
/* Only allow Cortex-A7 for this board */
if (strcmp(machine->cpu_type, ARM_CPU_TYPE_NAME("cortex-a7")) != 0) {
error_report("This board can only be used with cortex-a7 CPU");
exit(1);
}
h3 = AW_H3(object_new(TYPE_AW_H3));
object_property_add_child(OBJECT(machine), "soc", OBJECT(h3),
&error_abort);
object_unref(OBJECT(h3));
/* Setup timer properties */
object_property_set_int(OBJECT(h3), 32768, "clk0-freq",
&error_abort);
object_property_set_int(OBJECT(h3), 24 * 1000 * 1000, "clk1-freq",
&error_abort);
/* Setup SID properties. Currently using a default fixed SID identifier. */
if (qemu_uuid_is_null(&h3->sid.identifier)) {
qdev_prop_set_string(DEVICE(h3), "identifier",
"02c00081-1111-2222-3333-000044556677");
} else if (ldl_be_p(&h3->sid.identifier.data[0]) != 0x02c00081) {
warn_report("Security Identifier value does not include H3 prefix");
}
/* Setup EMAC properties */
object_property_set_int(OBJECT(&h3->emac), 1, "phy-addr", &error_abort);
/* DRAMC */
object_property_set_uint(OBJECT(h3), h3->memmap[AW_H3_SDRAM],
"ram-addr", &error_abort);
object_property_set_int(OBJECT(h3), machine->ram_size / MiB, "ram-size",
&error_abort);
/* Mark H3 object realized */
object_property_set_bool(OBJECT(h3), true, "realized", &error_abort);
/* Retrieve SD bus */
di = drive_get_next(IF_SD);
blk = di ? blk_by_legacy_dinfo(di) : NULL;
bus = qdev_get_child_bus(DEVICE(h3), "sd-bus");
/* Plug in SD card */
carddev = qdev_create(bus, TYPE_SD_CARD);
qdev_prop_set_drive(carddev, "drive", blk, &error_fatal);
object_property_set_bool(OBJECT(carddev), true, "realized", &error_fatal);
/* SDRAM */
memory_region_add_subregion(get_system_memory(), h3->memmap[AW_H3_SDRAM],
machine->ram);
/* Load target kernel or start using BootROM */
if (!machine->kernel_filename && blk_is_available(blk)) {
/* Use Boot ROM to copy data from SD card to SRAM */
allwinner_h3_bootrom_setup(h3, blk);
}
orangepi_binfo.loader_start = h3->memmap[AW_H3_SDRAM];
orangepi_binfo.ram_size = machine->ram_size;
arm_load_kernel(ARM_CPU(first_cpu), machine, &orangepi_binfo);
}
static void orangepi_machine_init(MachineClass *mc)
{
mc->desc = "Orange Pi PC";
mc->init = orangepi_init;
mc->block_default_type = IF_SD;
mc->units_per_default_bus = 1;
mc->min_cpus = AW_H3_NUM_CPUS;
mc->max_cpus = AW_H3_NUM_CPUS;
mc->default_cpus = AW_H3_NUM_CPUS;
mc->default_cpu_type = ARM_CPU_TYPE_NAME("cortex-a7");
mc->default_ram_size = 1 * GiB;
mc->default_ram_id = "orangepi.ram";
}
DEFINE_MACHINE("orangepi-pc", orangepi_machine_init)
+113 -32
View File
@@ -299,7 +299,7 @@ static void fdt_add_timer_nodes(const VirtMachineState *vms)
irqflags = GIC_FDT_IRQ_FLAGS_EDGE_LO_HI;
}
if (vms->gic_version == 2) {
if (vms->gic_version == VIRT_GIC_VERSION_2) {
irqflags = deposit32(irqflags, GIC_FDT_IRQ_PPI_CPU_START,
GIC_FDT_IRQ_PPI_CPU_WIDTH,
(1 << vms->smp_cpus) - 1);
@@ -440,7 +440,7 @@ static void fdt_add_gic_node(VirtMachineState *vms)
qemu_fdt_setprop_cell(vms->fdt, nodename, "#address-cells", 0x2);
qemu_fdt_setprop_cell(vms->fdt, nodename, "#size-cells", 0x2);
qemu_fdt_setprop(vms->fdt, nodename, "ranges", NULL, 0);
if (vms->gic_version == 3) {
if (vms->gic_version == VIRT_GIC_VERSION_3) {
int nb_redist_regions = virt_gicv3_redist_region_count(vms);
qemu_fdt_setprop_string(vms->fdt, nodename, "compatible",
@@ -519,7 +519,7 @@ static void fdt_add_pmu_nodes(const VirtMachineState *vms)
}
}
if (vms->gic_version == 2) {
if (vms->gic_version == VIRT_GIC_VERSION_2) {
irqflags = deposit32(irqflags, GIC_FDT_IRQ_PPI_CPU_START,
GIC_FDT_IRQ_PPI_CPU_WIDTH,
(1 << vms->smp_cpus) - 1);
@@ -1470,7 +1470,7 @@ static uint64_t virt_cpu_mp_affinity(VirtMachineState *vms, int idx)
* purposes are to make TCG consistent (with 64-bit KVM hosts)
* and to improve SGI efficiency.
*/
if (vms->gic_version == 3) {
if (vms->gic_version == VIRT_GIC_VERSION_3) {
clustersz = GICV3_TARGETLIST_BITS;
} else {
clustersz = GIC_TARGETLIST_BITS;
@@ -1535,6 +1535,105 @@ static void virt_set_memmap(VirtMachineState *vms)
}
}
/*
* finalize_gic_version - Determines the final gic_version
* according to the gic-version property
*
* Default GIC type is v2
*/
static void finalize_gic_version(VirtMachineState *vms)
{
unsigned int max_cpus = MACHINE(vms)->smp.max_cpus;
if (kvm_enabled()) {
int probe_bitmap;
if (!kvm_irqchip_in_kernel()) {
switch (vms->gic_version) {
case VIRT_GIC_VERSION_HOST:
warn_report(
"gic-version=host not relevant with kernel-irqchip=off "
"as only userspace GICv2 is supported. Using v2 ...");
return;
case VIRT_GIC_VERSION_MAX:
case VIRT_GIC_VERSION_NOSEL:
vms->gic_version = VIRT_GIC_VERSION_2;
return;
case VIRT_GIC_VERSION_2:
return;
case VIRT_GIC_VERSION_3:
error_report(
"gic-version=3 is not supported with kernel-irqchip=off");
exit(1);
}
}
probe_bitmap = kvm_arm_vgic_probe();
if (!probe_bitmap) {
error_report("Unable to determine GIC version supported by host");
exit(1);
}
switch (vms->gic_version) {
case VIRT_GIC_VERSION_HOST:
case VIRT_GIC_VERSION_MAX:
if (probe_bitmap & KVM_ARM_VGIC_V3) {
vms->gic_version = VIRT_GIC_VERSION_3;
} else {
vms->gic_version = VIRT_GIC_VERSION_2;
}
return;
case VIRT_GIC_VERSION_NOSEL:
if ((probe_bitmap & KVM_ARM_VGIC_V2) && max_cpus <= GIC_NCPU) {
vms->gic_version = VIRT_GIC_VERSION_2;
} else if (probe_bitmap & KVM_ARM_VGIC_V3) {
/*
* in case the host does not support v2 in-kernel emulation or
* the end-user requested more than 8 VCPUs we now default
* to v3. In any case defaulting to v2 would be broken.
*/
vms->gic_version = VIRT_GIC_VERSION_3;
} else if (max_cpus > GIC_NCPU) {
error_report("host only supports in-kernel GICv2 emulation "
"but more than 8 vcpus are requested");
exit(1);
}
break;
case VIRT_GIC_VERSION_2:
case VIRT_GIC_VERSION_3:
break;
}
/* Check chosen version is effectively supported by the host */
if (vms->gic_version == VIRT_GIC_VERSION_2 &&
!(probe_bitmap & KVM_ARM_VGIC_V2)) {
error_report("host does not support in-kernel GICv2 emulation");
exit(1);
} else if (vms->gic_version == VIRT_GIC_VERSION_3 &&
!(probe_bitmap & KVM_ARM_VGIC_V3)) {
error_report("host does not support in-kernel GICv3 emulation");
exit(1);
}
return;
}
/* TCG mode */
switch (vms->gic_version) {
case VIRT_GIC_VERSION_NOSEL:
vms->gic_version = VIRT_GIC_VERSION_2;
break;
case VIRT_GIC_VERSION_MAX:
vms->gic_version = VIRT_GIC_VERSION_3;
break;
case VIRT_GIC_VERSION_HOST:
error_report("gic-version=host requires KVM");
exit(1);
case VIRT_GIC_VERSION_2:
case VIRT_GIC_VERSION_3:
break;
}
}
static void machvirt_init(MachineState *machine)
{
VirtMachineState *vms = VIRT_MACHINE(machine);
@@ -1561,25 +1660,7 @@ static void machvirt_init(MachineState *machine)
/* We can probe only here because during property set
* KVM is not available yet
*/
if (vms->gic_version <= 0) {
/* "host" or "max" */
if (!kvm_enabled()) {
if (vms->gic_version == 0) {
error_report("gic-version=host requires KVM");
exit(1);
} else {
/* "max": currently means 3 for TCG */
vms->gic_version = 3;
}
} else {
vms->gic_version = kvm_arm_vgic_probe();
if (!vms->gic_version) {
error_report(
"Unable to determine GIC version supported by host");
exit(1);
}
}
}
finalize_gic_version(vms);
if (!cpu_type_valid(machine->cpu_type)) {
error_report("mach-virt: CPU type %s not supported", machine->cpu_type);
@@ -1628,7 +1709,7 @@ static void machvirt_init(MachineState *machine)
/* The maximum number of CPUs depends on the GIC version, or on how
* many redistributors we can fit into the memory map.
*/
if (vms->gic_version == 3) {
if (vms->gic_version == VIRT_GIC_VERSION_3) {
virt_max_cpus =
vms->memmap[VIRT_GIC_REDIST].size / GICV3_REDIST_SIZE;
virt_max_cpus +=
@@ -1856,7 +1937,7 @@ static void virt_set_its(Object *obj, bool value, Error **errp)
static char *virt_get_gic_version(Object *obj, Error **errp)
{
VirtMachineState *vms = VIRT_MACHINE(obj);
const char *val = vms->gic_version == 3 ? "3" : "2";
const char *val = vms->gic_version == VIRT_GIC_VERSION_3 ? "3" : "2";
return g_strdup(val);
}
@@ -1866,13 +1947,13 @@ static void virt_set_gic_version(Object *obj, const char *value, Error **errp)
VirtMachineState *vms = VIRT_MACHINE(obj);
if (!strcmp(value, "3")) {
vms->gic_version = 3;
vms->gic_version = VIRT_GIC_VERSION_3;
} else if (!strcmp(value, "2")) {
vms->gic_version = 2;
vms->gic_version = VIRT_GIC_VERSION_2;
} else if (!strcmp(value, "host")) {
vms->gic_version = 0; /* Will probe later */
vms->gic_version = VIRT_GIC_VERSION_HOST; /* Will probe later */
} else if (!strcmp(value, "max")) {
vms->gic_version = -1; /* Will probe later */
vms->gic_version = VIRT_GIC_VERSION_MAX; /* Will probe later */
} else {
error_setg(errp, "Invalid gic-version value");
error_append_hint(errp, "Valid values are 3, 2, host, max.\n");
@@ -2140,13 +2221,13 @@ static void virt_instance_init(Object *obj)
"Set on/off to enable/disable using "
"physical address space above 32 bits",
NULL);
/* Default GIC type is v2 */
vms->gic_version = 2;
vms->gic_version = VIRT_GIC_VERSION_NOSEL;
object_property_add_str(obj, "gic-version", virt_get_gic_version,
virt_set_gic_version, NULL);
object_property_set_description(obj, "gic-version",
"Set GIC version. "
"Valid values are 2, 3 and host", NULL);
"Valid values are 2, 3, host and max",
NULL);
vms->highmem_ecam = !vmc->no_highmem_ecam;
+6
View File
@@ -2593,6 +2593,12 @@ static void armv7m_nvic_reset(DeviceState *dev)
s->itns[i] = true;
}
}
/*
* We updated state that affects the CPU's MMUidx and thus its hflags;
* and we can't guarantee that we run before the CPU reset function.
*/
arm_rebuild_hflags(&s->cpu->env);
}
static void nvic_systick_trigger(void *opaque, int n, int level)
+5
View File
@@ -28,6 +28,11 @@ common-obj-$(CONFIG_MACIO) += macio/
common-obj-$(CONFIG_IVSHMEM_DEVICE) += ivshmem.o
common-obj-$(CONFIG_ALLWINNER_H3) += allwinner-h3-ccu.o
obj-$(CONFIG_ALLWINNER_H3) += allwinner-cpucfg.o
common-obj-$(CONFIG_ALLWINNER_H3) += allwinner-h3-dramc.o
common-obj-$(CONFIG_ALLWINNER_H3) += allwinner-h3-sysctrl.o
common-obj-$(CONFIG_ALLWINNER_H3) += allwinner-sid.o
common-obj-$(CONFIG_REALVIEW) += arm_sysctl.o
common-obj-$(CONFIG_NSERIES) += cbus.o
common-obj-$(CONFIG_ECCMEMCTL) += eccmemctl.o
+282
View File
@@ -0,0 +1,282 @@
/*
* Allwinner CPU Configuration Module emulation
*
* Copyright (C) 2019 Niek Linnenbank <nieklinnenbank@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "qemu/osdep.h"
#include "qemu/units.h"
#include "hw/sysbus.h"
#include "migration/vmstate.h"
#include "qemu/log.h"
#include "qemu/module.h"
#include "qemu/error-report.h"
#include "qemu/timer.h"
#include "hw/core/cpu.h"
#include "target/arm/arm-powerctl.h"
#include "target/arm/cpu.h"
#include "hw/misc/allwinner-cpucfg.h"
#include "trace.h"
/* CPUCFG register offsets */
enum {
REG_CPUS_RST_CTRL = 0x0000, /* CPUs Reset Control */
REG_CPU0_RST_CTRL = 0x0040, /* CPU#0 Reset Control */
REG_CPU0_CTRL = 0x0044, /* CPU#0 Control */
REG_CPU0_STATUS = 0x0048, /* CPU#0 Status */
REG_CPU1_RST_CTRL = 0x0080, /* CPU#1 Reset Control */
REG_CPU1_CTRL = 0x0084, /* CPU#1 Control */
REG_CPU1_STATUS = 0x0088, /* CPU#1 Status */
REG_CPU2_RST_CTRL = 0x00C0, /* CPU#2 Reset Control */
REG_CPU2_CTRL = 0x00C4, /* CPU#2 Control */
REG_CPU2_STATUS = 0x00C8, /* CPU#2 Status */
REG_CPU3_RST_CTRL = 0x0100, /* CPU#3 Reset Control */
REG_CPU3_CTRL = 0x0104, /* CPU#3 Control */
REG_CPU3_STATUS = 0x0108, /* CPU#3 Status */
REG_CPU_SYS_RST = 0x0140, /* CPU System Reset */
REG_CLK_GATING = 0x0144, /* CPU Clock Gating */
REG_GEN_CTRL = 0x0184, /* General Control */
REG_SUPER_STANDBY = 0x01A0, /* Super Standby Flag */
REG_ENTRY_ADDR = 0x01A4, /* Reset Entry Address */
REG_DBG_EXTERN = 0x01E4, /* Debug External */
REG_CNT64_CTRL = 0x0280, /* 64-bit Counter Control */
REG_CNT64_LOW = 0x0284, /* 64-bit Counter Low */
REG_CNT64_HIGH = 0x0288, /* 64-bit Counter High */
};
/* CPUCFG register flags */
enum {
CPUX_RESET_RELEASED = ((1 << 1) | (1 << 0)),
CPUX_STATUS_SMP = (1 << 0),
CPU_SYS_RESET_RELEASED = (1 << 0),
CLK_GATING_ENABLE = ((1 << 8) | 0xF),
};
/* CPUCFG register reset values */
enum {
REG_CLK_GATING_RST = 0x0000010F,
REG_GEN_CTRL_RST = 0x00000020,
REG_SUPER_STANDBY_RST = 0x0,
REG_CNT64_CTRL_RST = 0x0,
};
/* CPUCFG constants */
enum {
CPU_EXCEPTION_LEVEL_ON_RESET = 3, /* EL3 */
};
static void allwinner_cpucfg_cpu_reset(AwCpuCfgState *s, uint8_t cpu_id)
{
int ret;
trace_allwinner_cpucfg_cpu_reset(cpu_id, s->entry_addr);
ARMCPU *target_cpu = ARM_CPU(arm_get_cpu_by_id(cpu_id));
if (!target_cpu) {
/*
* Called with a bogus value for cpu_id. Guest error will
* already have been logged, we can simply return here.
*/
return;
}
bool target_aa64 = arm_feature(&target_cpu->env, ARM_FEATURE_AARCH64);
ret = arm_set_cpu_on(cpu_id, s->entry_addr, 0,
CPU_EXCEPTION_LEVEL_ON_RESET, target_aa64);
if (ret != QEMU_ARM_POWERCTL_RET_SUCCESS) {
error_report("%s: failed to bring up CPU %d: err %d",
__func__, cpu_id, ret);
return;
}
}
static uint64_t allwinner_cpucfg_read(void *opaque, hwaddr offset,
unsigned size)
{
const AwCpuCfgState *s = AW_CPUCFG(opaque);
uint64_t val = 0;
switch (offset) {
case REG_CPUS_RST_CTRL: /* CPUs Reset Control */
case REG_CPU_SYS_RST: /* CPU System Reset */
val = CPU_SYS_RESET_RELEASED;
break;
case REG_CPU0_RST_CTRL: /* CPU#0 Reset Control */
case REG_CPU1_RST_CTRL: /* CPU#1 Reset Control */
case REG_CPU2_RST_CTRL: /* CPU#2 Reset Control */
case REG_CPU3_RST_CTRL: /* CPU#3 Reset Control */
val = CPUX_RESET_RELEASED;
break;
case REG_CPU0_CTRL: /* CPU#0 Control */
case REG_CPU1_CTRL: /* CPU#1 Control */
case REG_CPU2_CTRL: /* CPU#2 Control */
case REG_CPU3_CTRL: /* CPU#3 Control */
val = 0;
break;
case REG_CPU0_STATUS: /* CPU#0 Status */
case REG_CPU1_STATUS: /* CPU#1 Status */
case REG_CPU2_STATUS: /* CPU#2 Status */
case REG_CPU3_STATUS: /* CPU#3 Status */
val = CPUX_STATUS_SMP;
break;
case REG_CLK_GATING: /* CPU Clock Gating */
val = CLK_GATING_ENABLE;
break;
case REG_GEN_CTRL: /* General Control */
val = s->gen_ctrl;
break;
case REG_SUPER_STANDBY: /* Super Standby Flag */
val = s->super_standby;
break;
case REG_ENTRY_ADDR: /* Reset Entry Address */
val = s->entry_addr;
break;
case REG_DBG_EXTERN: /* Debug External */
case REG_CNT64_CTRL: /* 64-bit Counter Control */
case REG_CNT64_LOW: /* 64-bit Counter Low */
case REG_CNT64_HIGH: /* 64-bit Counter High */
qemu_log_mask(LOG_UNIMP, "%s: unimplemented register at 0x%04x\n",
__func__, (uint32_t)offset);
break;
default:
qemu_log_mask(LOG_GUEST_ERROR, "%s: out-of-bounds offset 0x%04x\n",
__func__, (uint32_t)offset);
break;
}
trace_allwinner_cpucfg_read(offset, val, size);
return val;
}
static void allwinner_cpucfg_write(void *opaque, hwaddr offset,
uint64_t val, unsigned size)
{
AwCpuCfgState *s = AW_CPUCFG(opaque);
trace_allwinner_cpucfg_write(offset, val, size);
switch (offset) {
case REG_CPUS_RST_CTRL: /* CPUs Reset Control */
case REG_CPU_SYS_RST: /* CPU System Reset */
break;
case REG_CPU0_RST_CTRL: /* CPU#0 Reset Control */
case REG_CPU1_RST_CTRL: /* CPU#1 Reset Control */
case REG_CPU2_RST_CTRL: /* CPU#2 Reset Control */
case REG_CPU3_RST_CTRL: /* CPU#3 Reset Control */
if (val) {
allwinner_cpucfg_cpu_reset(s, (offset - REG_CPU0_RST_CTRL) >> 6);
}
break;
case REG_CPU0_CTRL: /* CPU#0 Control */
case REG_CPU1_CTRL: /* CPU#1 Control */
case REG_CPU2_CTRL: /* CPU#2 Control */
case REG_CPU3_CTRL: /* CPU#3 Control */
case REG_CPU0_STATUS: /* CPU#0 Status */
case REG_CPU1_STATUS: /* CPU#1 Status */
case REG_CPU2_STATUS: /* CPU#2 Status */
case REG_CPU3_STATUS: /* CPU#3 Status */
case REG_CLK_GATING: /* CPU Clock Gating */
break;
case REG_GEN_CTRL: /* General Control */
s->gen_ctrl = val;
break;
case REG_SUPER_STANDBY: /* Super Standby Flag */
s->super_standby = val;
break;
case REG_ENTRY_ADDR: /* Reset Entry Address */
s->entry_addr = val;
break;
case REG_DBG_EXTERN: /* Debug External */
case REG_CNT64_CTRL: /* 64-bit Counter Control */
case REG_CNT64_LOW: /* 64-bit Counter Low */
case REG_CNT64_HIGH: /* 64-bit Counter High */
qemu_log_mask(LOG_UNIMP, "%s: unimplemented register at 0x%04x\n",
__func__, (uint32_t)offset);
break;
default:
qemu_log_mask(LOG_GUEST_ERROR, "%s: out-of-bounds offset 0x%04x\n",
__func__, (uint32_t)offset);
break;
}
}
static const MemoryRegionOps allwinner_cpucfg_ops = {
.read = allwinner_cpucfg_read,
.write = allwinner_cpucfg_write,
.endianness = DEVICE_NATIVE_ENDIAN,
.valid = {
.min_access_size = 4,
.max_access_size = 4,
},
.impl.min_access_size = 4,
};
static void allwinner_cpucfg_reset(DeviceState *dev)
{
AwCpuCfgState *s = AW_CPUCFG(dev);
/* Set default values for registers */
s->gen_ctrl = REG_GEN_CTRL_RST;
s->super_standby = REG_SUPER_STANDBY_RST;
s->entry_addr = 0;
}
static void allwinner_cpucfg_init(Object *obj)
{
SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
AwCpuCfgState *s = AW_CPUCFG(obj);
/* Memory mapping */
memory_region_init_io(&s->iomem, OBJECT(s), &allwinner_cpucfg_ops, s,
TYPE_AW_CPUCFG, 1 * KiB);
sysbus_init_mmio(sbd, &s->iomem);
}
static const VMStateDescription allwinner_cpucfg_vmstate = {
.name = "allwinner-cpucfg",
.version_id = 1,
.minimum_version_id = 1,
.fields = (VMStateField[]) {
VMSTATE_UINT32(gen_ctrl, AwCpuCfgState),
VMSTATE_UINT32(super_standby, AwCpuCfgState),
VMSTATE_UINT32(entry_addr, AwCpuCfgState),
VMSTATE_END_OF_LIST()
}
};
static void allwinner_cpucfg_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
dc->reset = allwinner_cpucfg_reset;
dc->vmsd = &allwinner_cpucfg_vmstate;
}
static const TypeInfo allwinner_cpucfg_info = {
.name = TYPE_AW_CPUCFG,
.parent = TYPE_SYS_BUS_DEVICE,
.instance_init = allwinner_cpucfg_init,
.instance_size = sizeof(AwCpuCfgState),
.class_init = allwinner_cpucfg_class_init,
};
static void allwinner_cpucfg_register(void)
{
type_register_static(&allwinner_cpucfg_info);
}
type_init(allwinner_cpucfg_register)
+242
View File
@@ -0,0 +1,242 @@
/*
* Allwinner H3 Clock Control Unit emulation
*
* Copyright (C) 2019 Niek Linnenbank <nieklinnenbank@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "qemu/osdep.h"
#include "qemu/units.h"
#include "hw/sysbus.h"
#include "migration/vmstate.h"
#include "qemu/log.h"
#include "qemu/module.h"
#include "hw/misc/allwinner-h3-ccu.h"
/* CCU register offsets */
enum {
REG_PLL_CPUX = 0x0000, /* PLL CPUX Control */
REG_PLL_AUDIO = 0x0008, /* PLL Audio Control */
REG_PLL_VIDEO = 0x0010, /* PLL Video Control */
REG_PLL_VE = 0x0018, /* PLL VE Control */
REG_PLL_DDR = 0x0020, /* PLL DDR Control */
REG_PLL_PERIPH0 = 0x0028, /* PLL Peripherals 0 Control */
REG_PLL_GPU = 0x0038, /* PLL GPU Control */
REG_PLL_PERIPH1 = 0x0044, /* PLL Peripherals 1 Control */
REG_PLL_DE = 0x0048, /* PLL Display Engine Control */
REG_CPUX_AXI = 0x0050, /* CPUX/AXI Configuration */
REG_APB1 = 0x0054, /* ARM Peripheral Bus 1 Config */
REG_APB2 = 0x0058, /* ARM Peripheral Bus 2 Config */
REG_DRAM_CFG = 0x00F4, /* DRAM Configuration */
REG_MBUS = 0x00FC, /* MBUS Reset */
REG_PLL_TIME0 = 0x0200, /* PLL Stable Time 0 */
REG_PLL_TIME1 = 0x0204, /* PLL Stable Time 1 */
REG_PLL_CPUX_BIAS = 0x0220, /* PLL CPUX Bias */
REG_PLL_AUDIO_BIAS = 0x0224, /* PLL Audio Bias */
REG_PLL_VIDEO_BIAS = 0x0228, /* PLL Video Bias */
REG_PLL_VE_BIAS = 0x022C, /* PLL VE Bias */
REG_PLL_DDR_BIAS = 0x0230, /* PLL DDR Bias */
REG_PLL_PERIPH0_BIAS = 0x0234, /* PLL Peripherals 0 Bias */
REG_PLL_GPU_BIAS = 0x023C, /* PLL GPU Bias */
REG_PLL_PERIPH1_BIAS = 0x0244, /* PLL Peripherals 1 Bias */
REG_PLL_DE_BIAS = 0x0248, /* PLL Display Engine Bias */
REG_PLL_CPUX_TUNING = 0x0250, /* PLL CPUX Tuning */
REG_PLL_DDR_TUNING = 0x0260, /* PLL DDR Tuning */
};
#define REG_INDEX(offset) (offset / sizeof(uint32_t))
/* CCU register flags */
enum {
REG_DRAM_CFG_UPDATE = (1 << 16),
};
enum {
REG_PLL_ENABLE = (1 << 31),
REG_PLL_LOCK = (1 << 28),
};
/* CCU register reset values */
enum {
REG_PLL_CPUX_RST = 0x00001000,
REG_PLL_AUDIO_RST = 0x00035514,
REG_PLL_VIDEO_RST = 0x03006207,
REG_PLL_VE_RST = 0x03006207,
REG_PLL_DDR_RST = 0x00001000,
REG_PLL_PERIPH0_RST = 0x00041811,
REG_PLL_GPU_RST = 0x03006207,
REG_PLL_PERIPH1_RST = 0x00041811,
REG_PLL_DE_RST = 0x03006207,
REG_CPUX_AXI_RST = 0x00010000,
REG_APB1_RST = 0x00001010,
REG_APB2_RST = 0x01000000,
REG_DRAM_CFG_RST = 0x00000000,
REG_MBUS_RST = 0x80000000,
REG_PLL_TIME0_RST = 0x000000FF,
REG_PLL_TIME1_RST = 0x000000FF,
REG_PLL_CPUX_BIAS_RST = 0x08100200,
REG_PLL_AUDIO_BIAS_RST = 0x10100000,
REG_PLL_VIDEO_BIAS_RST = 0x10100000,
REG_PLL_VE_BIAS_RST = 0x10100000,
REG_PLL_DDR_BIAS_RST = 0x81104000,
REG_PLL_PERIPH0_BIAS_RST = 0x10100010,
REG_PLL_GPU_BIAS_RST = 0x10100000,
REG_PLL_PERIPH1_BIAS_RST = 0x10100010,
REG_PLL_DE_BIAS_RST = 0x10100000,
REG_PLL_CPUX_TUNING_RST = 0x0A101000,
REG_PLL_DDR_TUNING_RST = 0x14880000,
};
static uint64_t allwinner_h3_ccu_read(void *opaque, hwaddr offset,
unsigned size)
{
const AwH3ClockCtlState *s = AW_H3_CCU(opaque);
const uint32_t idx = REG_INDEX(offset);
switch (offset) {
case 0x308 ... AW_H3_CCU_IOSIZE:
qemu_log_mask(LOG_GUEST_ERROR, "%s: out-of-bounds offset 0x%04x\n",
__func__, (uint32_t)offset);
return 0;
}
return s->regs[idx];
}
static void allwinner_h3_ccu_write(void *opaque, hwaddr offset,
uint64_t val, unsigned size)
{
AwH3ClockCtlState *s = AW_H3_CCU(opaque);
const uint32_t idx = REG_INDEX(offset);
switch (offset) {
case REG_DRAM_CFG: /* DRAM Configuration */
val &= ~REG_DRAM_CFG_UPDATE;
break;
case REG_PLL_CPUX: /* PLL CPUX Control */
case REG_PLL_AUDIO: /* PLL Audio Control */
case REG_PLL_VIDEO: /* PLL Video Control */
case REG_PLL_VE: /* PLL VE Control */
case REG_PLL_DDR: /* PLL DDR Control */
case REG_PLL_PERIPH0: /* PLL Peripherals 0 Control */
case REG_PLL_GPU: /* PLL GPU Control */
case REG_PLL_PERIPH1: /* PLL Peripherals 1 Control */
case REG_PLL_DE: /* PLL Display Engine Control */
if (val & REG_PLL_ENABLE) {
val |= REG_PLL_LOCK;
}
break;
case 0x308 ... AW_H3_CCU_IOSIZE:
qemu_log_mask(LOG_GUEST_ERROR, "%s: out-of-bounds offset 0x%04x\n",
__func__, (uint32_t)offset);
break;
default:
qemu_log_mask(LOG_UNIMP, "%s: unimplemented write offset 0x%04x\n",
__func__, (uint32_t)offset);
break;
}
s->regs[idx] = (uint32_t) val;
}
static const MemoryRegionOps allwinner_h3_ccu_ops = {
.read = allwinner_h3_ccu_read,
.write = allwinner_h3_ccu_write,
.endianness = DEVICE_NATIVE_ENDIAN,
.valid = {
.min_access_size = 4,
.max_access_size = 4,
},
.impl.min_access_size = 4,
};
static void allwinner_h3_ccu_reset(DeviceState *dev)
{
AwH3ClockCtlState *s = AW_H3_CCU(dev);
/* Set default values for registers */
s->regs[REG_INDEX(REG_PLL_CPUX)] = REG_PLL_CPUX_RST;
s->regs[REG_INDEX(REG_PLL_AUDIO)] = REG_PLL_AUDIO_RST;
s->regs[REG_INDEX(REG_PLL_VIDEO)] = REG_PLL_VIDEO_RST;
s->regs[REG_INDEX(REG_PLL_VE)] = REG_PLL_VE_RST;
s->regs[REG_INDEX(REG_PLL_DDR)] = REG_PLL_DDR_RST;
s->regs[REG_INDEX(REG_PLL_PERIPH0)] = REG_PLL_PERIPH0_RST;
s->regs[REG_INDEX(REG_PLL_GPU)] = REG_PLL_GPU_RST;
s->regs[REG_INDEX(REG_PLL_PERIPH1)] = REG_PLL_PERIPH1_RST;
s->regs[REG_INDEX(REG_PLL_DE)] = REG_PLL_DE_RST;
s->regs[REG_INDEX(REG_CPUX_AXI)] = REG_CPUX_AXI_RST;
s->regs[REG_INDEX(REG_APB1)] = REG_APB1_RST;
s->regs[REG_INDEX(REG_APB2)] = REG_APB2_RST;
s->regs[REG_INDEX(REG_DRAM_CFG)] = REG_DRAM_CFG_RST;
s->regs[REG_INDEX(REG_MBUS)] = REG_MBUS_RST;
s->regs[REG_INDEX(REG_PLL_TIME0)] = REG_PLL_TIME0_RST;
s->regs[REG_INDEX(REG_PLL_TIME1)] = REG_PLL_TIME1_RST;
s->regs[REG_INDEX(REG_PLL_CPUX_BIAS)] = REG_PLL_CPUX_BIAS_RST;
s->regs[REG_INDEX(REG_PLL_AUDIO_BIAS)] = REG_PLL_AUDIO_BIAS_RST;
s->regs[REG_INDEX(REG_PLL_VIDEO_BIAS)] = REG_PLL_VIDEO_BIAS_RST;
s->regs[REG_INDEX(REG_PLL_VE_BIAS)] = REG_PLL_VE_BIAS_RST;
s->regs[REG_INDEX(REG_PLL_DDR_BIAS)] = REG_PLL_DDR_BIAS_RST;
s->regs[REG_INDEX(REG_PLL_PERIPH0_BIAS)] = REG_PLL_PERIPH0_BIAS_RST;
s->regs[REG_INDEX(REG_PLL_GPU_BIAS)] = REG_PLL_GPU_BIAS_RST;
s->regs[REG_INDEX(REG_PLL_PERIPH1_BIAS)] = REG_PLL_PERIPH1_BIAS_RST;
s->regs[REG_INDEX(REG_PLL_DE_BIAS)] = REG_PLL_DE_BIAS_RST;
s->regs[REG_INDEX(REG_PLL_CPUX_TUNING)] = REG_PLL_CPUX_TUNING_RST;
s->regs[REG_INDEX(REG_PLL_DDR_TUNING)] = REG_PLL_DDR_TUNING_RST;
}
static void allwinner_h3_ccu_init(Object *obj)
{
SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
AwH3ClockCtlState *s = AW_H3_CCU(obj);
/* Memory mapping */
memory_region_init_io(&s->iomem, OBJECT(s), &allwinner_h3_ccu_ops, s,
TYPE_AW_H3_CCU, AW_H3_CCU_IOSIZE);
sysbus_init_mmio(sbd, &s->iomem);
}
static const VMStateDescription allwinner_h3_ccu_vmstate = {
.name = "allwinner-h3-ccu",
.version_id = 1,
.minimum_version_id = 1,
.fields = (VMStateField[]) {
VMSTATE_UINT32_ARRAY(regs, AwH3ClockCtlState, AW_H3_CCU_REGS_NUM),
VMSTATE_END_OF_LIST()
}
};
static void allwinner_h3_ccu_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
dc->reset = allwinner_h3_ccu_reset;
dc->vmsd = &allwinner_h3_ccu_vmstate;
}
static const TypeInfo allwinner_h3_ccu_info = {
.name = TYPE_AW_H3_CCU,
.parent = TYPE_SYS_BUS_DEVICE,
.instance_init = allwinner_h3_ccu_init,
.instance_size = sizeof(AwH3ClockCtlState),
.class_init = allwinner_h3_ccu_class_init,
};
static void allwinner_h3_ccu_register(void)
{
type_register_static(&allwinner_h3_ccu_info);
}
type_init(allwinner_h3_ccu_register)
+358
View File
@@ -0,0 +1,358 @@
/*
* Allwinner H3 SDRAM Controller emulation
*
* Copyright (C) 2019 Niek Linnenbank <nieklinnenbank@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "qemu/osdep.h"
#include "qemu/units.h"
#include "qemu/error-report.h"
#include "hw/sysbus.h"
#include "migration/vmstate.h"
#include "qemu/log.h"
#include "qemu/module.h"
#include "exec/address-spaces.h"
#include "hw/qdev-properties.h"
#include "qapi/error.h"
#include "hw/misc/allwinner-h3-dramc.h"
#include "trace.h"
#define REG_INDEX(offset) (offset / sizeof(uint32_t))
/* DRAMCOM register offsets */
enum {
REG_DRAMCOM_CR = 0x0000, /* Control Register */
};
/* DRAMCTL register offsets */
enum {
REG_DRAMCTL_PIR = 0x0000, /* PHY Initialization Register */
REG_DRAMCTL_PGSR = 0x0010, /* PHY General Status Register */
REG_DRAMCTL_STATR = 0x0018, /* Status Register */
};
/* DRAMCTL register flags */
enum {
REG_DRAMCTL_PGSR_INITDONE = (1 << 0),
};
enum {
REG_DRAMCTL_STATR_ACTIVE = (1 << 0),
};
static void allwinner_h3_dramc_map_rows(AwH3DramCtlState *s, uint8_t row_bits,
uint8_t bank_bits, uint16_t page_size)
{
/*
* This function simulates row addressing behavior when bootloader
* software attempts to detect the amount of available SDRAM. In U-Boot
* the controller is configured with the widest row addressing available.
* Then a pattern is written to RAM at an offset on the row boundary size.
* If the value read back equals the value read back from the
* start of RAM, the bootloader knows the amount of row bits.
*
* This function inserts a mirrored memory region when the configured row
* bits are not matching the actual emulated memory, to simulate the
* same behavior on hardware as expected by the bootloader.
*/
uint8_t row_bits_actual = 0;
/* Calculate the actual row bits using the ram_size property */
for (uint8_t i = 8; i < 12; i++) {
if (1 << i == s->ram_size) {
row_bits_actual = i + 3;
break;
}
}
if (s->ram_size == (1 << (row_bits - 3))) {
/* When row bits is the expected value, remove the mirror */
memory_region_set_enabled(&s->row_mirror_alias, false);
trace_allwinner_h3_dramc_rowmirror_disable();
} else if (row_bits_actual) {
/* Row bits not matching ram_size, install the rows mirror */
hwaddr row_mirror = s->ram_addr + ((1 << (row_bits_actual +
bank_bits)) * page_size);
memory_region_set_enabled(&s->row_mirror_alias, true);
memory_region_set_address(&s->row_mirror_alias, row_mirror);
trace_allwinner_h3_dramc_rowmirror_enable(row_mirror);
}
}
static uint64_t allwinner_h3_dramcom_read(void *opaque, hwaddr offset,
unsigned size)
{
const AwH3DramCtlState *s = AW_H3_DRAMC(opaque);
const uint32_t idx = REG_INDEX(offset);
if (idx >= AW_H3_DRAMCOM_REGS_NUM) {
qemu_log_mask(LOG_GUEST_ERROR, "%s: out-of-bounds offset 0x%04x\n",
__func__, (uint32_t)offset);
return 0;
}
trace_allwinner_h3_dramcom_read(offset, s->dramcom[idx], size);
return s->dramcom[idx];
}
static void allwinner_h3_dramcom_write(void *opaque, hwaddr offset,
uint64_t val, unsigned size)
{
AwH3DramCtlState *s = AW_H3_DRAMC(opaque);
const uint32_t idx = REG_INDEX(offset);
trace_allwinner_h3_dramcom_write(offset, val, size);
if (idx >= AW_H3_DRAMCOM_REGS_NUM) {
qemu_log_mask(LOG_GUEST_ERROR, "%s: out-of-bounds offset 0x%04x\n",
__func__, (uint32_t)offset);
return;
}
switch (offset) {
case REG_DRAMCOM_CR: /* Control Register */
allwinner_h3_dramc_map_rows(s, ((val >> 4) & 0xf) + 1,
((val >> 2) & 0x1) + 2,
1 << (((val >> 8) & 0xf) + 3));
break;
default:
break;
};
s->dramcom[idx] = (uint32_t) val;
}
static uint64_t allwinner_h3_dramctl_read(void *opaque, hwaddr offset,
unsigned size)
{
const AwH3DramCtlState *s = AW_H3_DRAMC(opaque);
const uint32_t idx = REG_INDEX(offset);
if (idx >= AW_H3_DRAMCTL_REGS_NUM) {
qemu_log_mask(LOG_GUEST_ERROR, "%s: out-of-bounds offset 0x%04x\n",
__func__, (uint32_t)offset);
return 0;
}
trace_allwinner_h3_dramctl_read(offset, s->dramctl[idx], size);
return s->dramctl[idx];
}
static void allwinner_h3_dramctl_write(void *opaque, hwaddr offset,
uint64_t val, unsigned size)
{
AwH3DramCtlState *s = AW_H3_DRAMC(opaque);
const uint32_t idx = REG_INDEX(offset);
trace_allwinner_h3_dramctl_write(offset, val, size);
if (idx >= AW_H3_DRAMCTL_REGS_NUM) {
qemu_log_mask(LOG_GUEST_ERROR, "%s: out-of-bounds offset 0x%04x\n",
__func__, (uint32_t)offset);
return;
}
switch (offset) {
case REG_DRAMCTL_PIR: /* PHY Initialization Register */
s->dramctl[REG_INDEX(REG_DRAMCTL_PGSR)] |= REG_DRAMCTL_PGSR_INITDONE;
s->dramctl[REG_INDEX(REG_DRAMCTL_STATR)] |= REG_DRAMCTL_STATR_ACTIVE;
break;
default:
break;
}
s->dramctl[idx] = (uint32_t) val;
}
static uint64_t allwinner_h3_dramphy_read(void *opaque, hwaddr offset,
unsigned size)
{
const AwH3DramCtlState *s = AW_H3_DRAMC(opaque);
const uint32_t idx = REG_INDEX(offset);
if (idx >= AW_H3_DRAMPHY_REGS_NUM) {
qemu_log_mask(LOG_GUEST_ERROR, "%s: out-of-bounds offset 0x%04x\n",
__func__, (uint32_t)offset);
return 0;
}
trace_allwinner_h3_dramphy_read(offset, s->dramphy[idx], size);
return s->dramphy[idx];
}
static void allwinner_h3_dramphy_write(void *opaque, hwaddr offset,
uint64_t val, unsigned size)
{
AwH3DramCtlState *s = AW_H3_DRAMC(opaque);
const uint32_t idx = REG_INDEX(offset);
trace_allwinner_h3_dramphy_write(offset, val, size);
if (idx >= AW_H3_DRAMPHY_REGS_NUM) {
qemu_log_mask(LOG_GUEST_ERROR, "%s: out-of-bounds offset 0x%04x\n",
__func__, (uint32_t)offset);
return;
}
s->dramphy[idx] = (uint32_t) val;
}
static const MemoryRegionOps allwinner_h3_dramcom_ops = {
.read = allwinner_h3_dramcom_read,
.write = allwinner_h3_dramcom_write,
.endianness = DEVICE_NATIVE_ENDIAN,
.valid = {
.min_access_size = 4,
.max_access_size = 4,
},
.impl.min_access_size = 4,
};
static const MemoryRegionOps allwinner_h3_dramctl_ops = {
.read = allwinner_h3_dramctl_read,
.write = allwinner_h3_dramctl_write,
.endianness = DEVICE_NATIVE_ENDIAN,
.valid = {
.min_access_size = 4,
.max_access_size = 4,
},
.impl.min_access_size = 4,
};
static const MemoryRegionOps allwinner_h3_dramphy_ops = {
.read = allwinner_h3_dramphy_read,
.write = allwinner_h3_dramphy_write,
.endianness = DEVICE_NATIVE_ENDIAN,
.valid = {
.min_access_size = 4,
.max_access_size = 4,
},
.impl.min_access_size = 4,
};
static void allwinner_h3_dramc_reset(DeviceState *dev)
{
AwH3DramCtlState *s = AW_H3_DRAMC(dev);
/* Set default values for registers */
memset(&s->dramcom, 0, sizeof(s->dramcom));
memset(&s->dramctl, 0, sizeof(s->dramctl));
memset(&s->dramphy, 0, sizeof(s->dramphy));
}
static void allwinner_h3_dramc_realize(DeviceState *dev, Error **errp)
{
AwH3DramCtlState *s = AW_H3_DRAMC(dev);
/* Only power of 2 RAM sizes from 256MiB up to 2048MiB are supported */
for (uint8_t i = 8; i < 13; i++) {
if (1 << i == s->ram_size) {
break;
} else if (i == 12) {
error_report("%s: ram-size %u MiB is not supported",
__func__, s->ram_size);
exit(1);
}
}
/* Setup row mirror mappings */
memory_region_init_ram(&s->row_mirror, OBJECT(s),
"allwinner-h3-dramc.row-mirror",
4 * KiB, &error_abort);
memory_region_add_subregion_overlap(get_system_memory(), s->ram_addr,
&s->row_mirror, 10);
memory_region_init_alias(&s->row_mirror_alias, OBJECT(s),
"allwinner-h3-dramc.row-mirror-alias",
&s->row_mirror, 0, 4 * KiB);
memory_region_add_subregion_overlap(get_system_memory(),
s->ram_addr + 1 * MiB,
&s->row_mirror_alias, 10);
memory_region_set_enabled(&s->row_mirror_alias, false);
}
static void allwinner_h3_dramc_init(Object *obj)
{
SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
AwH3DramCtlState *s = AW_H3_DRAMC(obj);
/* DRAMCOM registers */
memory_region_init_io(&s->dramcom_iomem, OBJECT(s),
&allwinner_h3_dramcom_ops, s,
TYPE_AW_H3_DRAMC, 4 * KiB);
sysbus_init_mmio(sbd, &s->dramcom_iomem);
/* DRAMCTL registers */
memory_region_init_io(&s->dramctl_iomem, OBJECT(s),
&allwinner_h3_dramctl_ops, s,
TYPE_AW_H3_DRAMC, 4 * KiB);
sysbus_init_mmio(sbd, &s->dramctl_iomem);
/* DRAMPHY registers */
memory_region_init_io(&s->dramphy_iomem, OBJECT(s),
&allwinner_h3_dramphy_ops, s,
TYPE_AW_H3_DRAMC, 4 * KiB);
sysbus_init_mmio(sbd, &s->dramphy_iomem);
}
static Property allwinner_h3_dramc_properties[] = {
DEFINE_PROP_UINT64("ram-addr", AwH3DramCtlState, ram_addr, 0x0),
DEFINE_PROP_UINT32("ram-size", AwH3DramCtlState, ram_size, 256 * MiB),
DEFINE_PROP_END_OF_LIST()
};
static const VMStateDescription allwinner_h3_dramc_vmstate = {
.name = "allwinner-h3-dramc",
.version_id = 1,
.minimum_version_id = 1,
.fields = (VMStateField[]) {
VMSTATE_UINT32_ARRAY(dramcom, AwH3DramCtlState, AW_H3_DRAMCOM_REGS_NUM),
VMSTATE_UINT32_ARRAY(dramctl, AwH3DramCtlState, AW_H3_DRAMCTL_REGS_NUM),
VMSTATE_UINT32_ARRAY(dramphy, AwH3DramCtlState, AW_H3_DRAMPHY_REGS_NUM),
VMSTATE_END_OF_LIST()
}
};
static void allwinner_h3_dramc_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
dc->reset = allwinner_h3_dramc_reset;
dc->vmsd = &allwinner_h3_dramc_vmstate;
dc->realize = allwinner_h3_dramc_realize;
device_class_set_props(dc, allwinner_h3_dramc_properties);
}
static const TypeInfo allwinner_h3_dramc_info = {
.name = TYPE_AW_H3_DRAMC,
.parent = TYPE_SYS_BUS_DEVICE,
.instance_init = allwinner_h3_dramc_init,
.instance_size = sizeof(AwH3DramCtlState),
.class_init = allwinner_h3_dramc_class_init,
};
static void allwinner_h3_dramc_register(void)
{
type_register_static(&allwinner_h3_dramc_info);
}
type_init(allwinner_h3_dramc_register)
+140
View File
@@ -0,0 +1,140 @@
/*
* Allwinner H3 System Control emulation
*
* Copyright (C) 2019 Niek Linnenbank <nieklinnenbank@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "qemu/osdep.h"
#include "qemu/units.h"
#include "hw/sysbus.h"
#include "migration/vmstate.h"
#include "qemu/log.h"
#include "qemu/module.h"
#include "hw/misc/allwinner-h3-sysctrl.h"
/* System Control register offsets */
enum {
REG_VER = 0x24, /* Version */
REG_EMAC_PHY_CLK = 0x30, /* EMAC PHY Clock */
};
#define REG_INDEX(offset) (offset / sizeof(uint32_t))
/* System Control register reset values */
enum {
REG_VER_RST = 0x0,
REG_EMAC_PHY_CLK_RST = 0x58000,
};
static uint64_t allwinner_h3_sysctrl_read(void *opaque, hwaddr offset,
unsigned size)
{
const AwH3SysCtrlState *s = AW_H3_SYSCTRL(opaque);
const uint32_t idx = REG_INDEX(offset);
if (idx >= AW_H3_SYSCTRL_REGS_NUM) {
qemu_log_mask(LOG_GUEST_ERROR, "%s: out-of-bounds offset 0x%04x\n",
__func__, (uint32_t)offset);
return 0;
}
return s->regs[idx];
}
static void allwinner_h3_sysctrl_write(void *opaque, hwaddr offset,
uint64_t val, unsigned size)
{
AwH3SysCtrlState *s = AW_H3_SYSCTRL(opaque);
const uint32_t idx = REG_INDEX(offset);
if (idx >= AW_H3_SYSCTRL_REGS_NUM) {
qemu_log_mask(LOG_GUEST_ERROR, "%s: out-of-bounds offset 0x%04x\n",
__func__, (uint32_t)offset);
return;
}
switch (offset) {
case REG_VER: /* Version */
break;
default:
s->regs[idx] = (uint32_t) val;
break;
}
}
static const MemoryRegionOps allwinner_h3_sysctrl_ops = {
.read = allwinner_h3_sysctrl_read,
.write = allwinner_h3_sysctrl_write,
.endianness = DEVICE_NATIVE_ENDIAN,
.valid = {
.min_access_size = 4,
.max_access_size = 4,
},
.impl.min_access_size = 4,
};
static void allwinner_h3_sysctrl_reset(DeviceState *dev)
{
AwH3SysCtrlState *s = AW_H3_SYSCTRL(dev);
/* Set default values for registers */
s->regs[REG_INDEX(REG_VER)] = REG_VER_RST;
s->regs[REG_INDEX(REG_EMAC_PHY_CLK)] = REG_EMAC_PHY_CLK_RST;
}
static void allwinner_h3_sysctrl_init(Object *obj)
{
SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
AwH3SysCtrlState *s = AW_H3_SYSCTRL(obj);
/* Memory mapping */
memory_region_init_io(&s->iomem, OBJECT(s), &allwinner_h3_sysctrl_ops, s,
TYPE_AW_H3_SYSCTRL, 4 * KiB);
sysbus_init_mmio(sbd, &s->iomem);
}
static const VMStateDescription allwinner_h3_sysctrl_vmstate = {
.name = "allwinner-h3-sysctrl",
.version_id = 1,
.minimum_version_id = 1,
.fields = (VMStateField[]) {
VMSTATE_UINT32_ARRAY(regs, AwH3SysCtrlState, AW_H3_SYSCTRL_REGS_NUM),
VMSTATE_END_OF_LIST()
}
};
static void allwinner_h3_sysctrl_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
dc->reset = allwinner_h3_sysctrl_reset;
dc->vmsd = &allwinner_h3_sysctrl_vmstate;
}
static const TypeInfo allwinner_h3_sysctrl_info = {
.name = TYPE_AW_H3_SYSCTRL,
.parent = TYPE_SYS_BUS_DEVICE,
.instance_init = allwinner_h3_sysctrl_init,
.instance_size = sizeof(AwH3SysCtrlState),
.class_init = allwinner_h3_sysctrl_class_init,
};
static void allwinner_h3_sysctrl_register(void)
{
type_register_static(&allwinner_h3_sysctrl_info);
}
type_init(allwinner_h3_sysctrl_register)

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