QEMU-Devel Archive on lore.kernel.org
 help / color / Atom feed
* [PATCH 00/10] Add Allwinner H3 SoC and Orange Pi PC Machine
@ 2019-12-02 21:09 Niek Linnenbank
  2019-12-02 21:09 ` [PATCH 01/10] hw: arm: add Allwinner H3 System-on-Chip Niek Linnenbank
                   ` (11 more replies)
  0 siblings, 12 replies; 33+ messages in thread
From: Niek Linnenbank @ 2019-12-02 21:09 UTC (permalink / raw)
  To: qemu-devel; +Cc: b.galvani, peter.maydell, Niek Linnenbank, qemu-arm

Dear QEMU developers,

Hereby I would like to contribute the following set of patches to QEMU
which add support for the Allwinner H3 System on Chip and the
Orange Pi PC machine. The following features and devices are supported:

 * SMP (Quad Core Cortex A7)
 * Generic Interrupt Controller configuration
 * SRAM mappings
 * Timer device (re-used from Allwinner A10)
 * UART
 * SD/MMC storage controller
 * EMAC ethernet connectivity
 * USB 2.0 interfaces
 * Clock Control Unit
 * System Control module
 * Security Identifier device

Functionality related to graphical output such as HDMI, GPU,
Display Engine and audio are not included. Recently released
mainline Linux kernels (4.19 up to latest master) and mainline U-Boot
are known to work. The SD/MMC code is tested using bonnie++ and
various tools such as fsck, dd and fdisk. The EMAC is verified with iperf3
using -netdev socket.

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:
 $ 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:
 $ 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:
 $ ARCH=arm CROSS_COMPILE=arm-linux-gnueabi- make -j5

To boot the newly build linux kernel in QEMU with the Orange Pi PC machine, use:
 $ qemu-system-arm -M orangepi -m 512 -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

Note that this kernel does not have a root filesystem. You may provide it
with an official Orange Pi PC image [1] either as an SD card or as
USB mass storage. To boot using the Orange Pi PC Debian image on SD card,
simply add the -sd argument and provide the proper root= kernel parameter:
 $ qemu-system-arm -M orangepi -m 512 -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

Alternatively, you can also choose to build and boot a recent buildroot [2]
using the orangepi_pc_defconfig or Armbian image [3] for Orange Pi PC.
To attach an USB mass storage device to the machine, simply append to the command:
 -drive if=none,id=stick,file=myimage.img \
 -device usb-storage,bus=usb-bus.0,drive=stick

U-Boot mainline can be build and configured using the orangepi_pc_defconfig
using similar commands as describe above for Linux. To start U-Boot using
the Orange Pi PC machine, provide the u-boot binary to the -kernel argument:
 $ qemu-system-arm -M orangepi -m 512 -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:
 -> setenv bootargs console=ttyS0,115200
 -> ext2load mmc 0 0x42000000 zImage
 -> ext2load mmc 0 0x43000000 sun8i-h2-plus-orangepi-zero.dtb
 -> bootz 0x42000000 - 0x43000000

Looking forward to your review comments. I will do my best
to update the patches where needed.

With kind regards,

Niek Linnenbank

[1] http://www.orangepi.org/downloadresources/
[2] https://buildroot.org/download.html
[3] https://www.armbian.com/orange-pi-pc/

Niek Linnenbank (10):
  hw: arm: add Allwinner H3 System-on-Chip
  hw: arm: add Xunlong Orange Pi PC machine
  arm: allwinner-h3: add Clock Control Unit
  arm: allwinner-h3: add USB host controller
  arm: allwinner-h3: add System Control module
  arm/arm-powerctl: set NSACR.{CP11,CP10} bits in arm_set_cpu_on()
  arm: allwinner-h3: add CPU Configuration module
  arm: allwinner-h3: add Security Identifier device
  arm: allwinner-h3: add SD/MMC host controller
  arm: allwinner-h3: add EMAC ethernet device

 MAINTAINERS                           |   8 +
 default-configs/arm-softmmu.mak       |   1 +
 hw/arm/Kconfig                        |   9 +
 hw/arm/Makefile.objs                  |   1 +
 hw/arm/allwinner-h3.c                 | 316 ++++++++++
 hw/arm/orangepi.c                     | 114 ++++
 hw/misc/Makefile.objs                 |   4 +
 hw/misc/allwinner-h3-clk.c            | 227 ++++++++
 hw/misc/allwinner-h3-cpucfg.c         | 280 +++++++++
 hw/misc/allwinner-h3-sid.c            | 162 ++++++
 hw/misc/allwinner-h3-syscon.c         | 139 +++++
 hw/misc/trace-events                  |   5 +
 hw/net/Kconfig                        |   3 +
 hw/net/Makefile.objs                  |   1 +
 hw/net/allwinner-h3-emac.c            | 786 +++++++++++++++++++++++++
 hw/net/trace-events                   |  10 +
 hw/sd/Makefile.objs                   |   1 +
 hw/sd/allwinner-h3-sdhost.c           | 791 ++++++++++++++++++++++++++
 hw/sd/trace-events                    |   7 +
 hw/usb/hcd-ehci-sysbus.c              |  17 +
 hw/usb/hcd-ehci.h                     |   1 +
 include/hw/arm/allwinner-h3.h         | 130 +++++
 include/hw/misc/allwinner-h3-clk.h    |  41 ++
 include/hw/misc/allwinner-h3-cpucfg.h |  44 ++
 include/hw/misc/allwinner-h3-sid.h    |  42 ++
 include/hw/misc/allwinner-h3-syscon.h |  43 ++
 include/hw/net/allwinner-h3-emac.h    |  69 +++
 include/hw/sd/allwinner-h3-sdhost.h   |  73 +++
 target/arm/arm-powerctl.c             |   3 +
 29 files changed, 3328 insertions(+)
 create mode 100644 hw/arm/allwinner-h3.c
 create mode 100644 hw/arm/orangepi.c
 create mode 100644 hw/misc/allwinner-h3-clk.c
 create mode 100644 hw/misc/allwinner-h3-cpucfg.c
 create mode 100644 hw/misc/allwinner-h3-sid.c
 create mode 100644 hw/misc/allwinner-h3-syscon.c
 create mode 100644 hw/net/allwinner-h3-emac.c
 create mode 100644 hw/sd/allwinner-h3-sdhost.c
 create mode 100644 include/hw/arm/allwinner-h3.h
 create mode 100644 include/hw/misc/allwinner-h3-clk.h
 create mode 100644 include/hw/misc/allwinner-h3-cpucfg.h
 create mode 100644 include/hw/misc/allwinner-h3-sid.h
 create mode 100644 include/hw/misc/allwinner-h3-syscon.h
 create mode 100644 include/hw/net/allwinner-h3-emac.h
 create mode 100644 include/hw/sd/allwinner-h3-sdhost.h

-- 
2.17.1



^ permalink raw reply	[flat|nested] 33+ messages in thread

* [PATCH 01/10] hw: arm: add Allwinner H3 System-on-Chip
  2019-12-02 21:09 [PATCH 00/10] Add Allwinner H3 SoC and Orange Pi PC Machine Niek Linnenbank
@ 2019-12-02 21:09 ` Niek Linnenbank
  2019-12-04 16:53   ` Philippe Mathieu-Daudé
  2019-12-02 21:09 ` [PATCH 02/10] hw: arm: add Xunlong Orange Pi PC machine Niek Linnenbank
                   ` (10 subsequent siblings)
  11 siblings, 1 reply; 33+ messages in thread
From: Niek Linnenbank @ 2019-12-02 21:09 UTC (permalink / raw)
  To: qemu-devel; +Cc: b.galvani, peter.maydell, Niek Linnenbank, qemu-arm

The Allwinner H3 is a System on Chip containing four ARM Cortex A7
processor cores. Features and specifications include DDR2/DDR3 memory,
SD/MMC storage cards, 10/100/1000Mbit ethernet, USB 2.0, HDMI and
various I/O modules. This commit adds support for the Allwinner H3
System on Chip.

Signed-off-by: Niek Linnenbank <nieklinnenbank@gmail.com>
---
 MAINTAINERS                     |   7 ++
 default-configs/arm-softmmu.mak |   1 +
 hw/arm/Kconfig                  |   8 ++
 hw/arm/Makefile.objs            |   1 +
 hw/arm/allwinner-h3.c           | 215 ++++++++++++++++++++++++++++++++
 include/hw/arm/allwinner-h3.h   | 118 ++++++++++++++++++
 6 files changed, 350 insertions(+)
 create mode 100644 hw/arm/allwinner-h3.c
 create mode 100644 include/hw/arm/allwinner-h3.h

diff --git a/MAINTAINERS b/MAINTAINERS
index 5e5e3e52d6..29c9936037 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -479,6 +479,13 @@ 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*
+
 ARM PrimeCell and CMSDK devices
 M: Peter Maydell <peter.maydell@linaro.org>
 L: qemu-arm@nongnu.org
diff --git a/default-configs/arm-softmmu.mak b/default-configs/arm-softmmu.mak
index 1f2e0e7fde..d75a239c2c 100644
--- a/default-configs/arm-softmmu.mak
+++ b/default-configs/arm-softmmu.mak
@@ -40,3 +40,4 @@ CONFIG_FSL_IMX25=y
 CONFIG_FSL_IMX7=y
 CONFIG_FSL_IMX6UL=y
 CONFIG_SEMIHOSTING=y
+CONFIG_ALLWINNER_H3=y
diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig
index c6e7782580..ebf8d2325f 100644
--- a/hw/arm/Kconfig
+++ b/hw/arm/Kconfig
@@ -291,6 +291,14 @@ config ALLWINNER_A10
     select SERIAL
     select UNIMP
 
+config ALLWINNER_H3
+    bool
+    select ALLWINNER_A10_PIT
+    select SERIAL
+    select ARM_TIMER
+    select ARM_GIC
+    select UNIMP
+
 config RASPI
     bool
     select FRAMEBUFFER
diff --git a/hw/arm/Makefile.objs b/hw/arm/Makefile.objs
index fe749f65fd..956e496052 100644
--- a/hw/arm/Makefile.objs
+++ b/hw/arm/Makefile.objs
@@ -34,6 +34,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
 obj-$(CONFIG_RASPI) += bcm2835_peripherals.o bcm2836.o raspi.o
 obj-$(CONFIG_STM32F205_SOC) += stm32f205_soc.o
 obj-$(CONFIG_XLNX_ZYNQMP_ARM) += xlnx-zynqmp.o xlnx-zcu102.o
diff --git a/hw/arm/allwinner-h3.c b/hw/arm/allwinner-h3.c
new file mode 100644
index 0000000000..470fdfebef
--- /dev/null
+++ b/hw/arm/allwinner-h3.c
@@ -0,0 +1,215 @@
+/*
+ * 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/module.h"
+#include "qemu/units.h"
+#include "cpu.h"
+#include "hw/sysbus.h"
+#include "hw/arm/allwinner-h3.h"
+#include "hw/misc/unimp.h"
+#include "sysemu/sysemu.h"
+
+static void aw_h3_init(Object *obj)
+{
+    AwH3State *s = AW_H3(obj);
+
+    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);
+}
+
+static void aw_h3_realize(DeviceState *dev, Error **errp)
+{
+    AwH3State *s = AW_H3(dev);
+    SysBusDevice *sysbusdev = NULL;
+    Error *err = NULL;
+    unsigned i = 0;
+
+    /* CPUs */
+    for (i = 0; i < AW_H3_NUM_CPUS; i++) {
+        Object *cpuobj = object_new(ARM_CPU_TYPE_NAME("cortex-a7"));
+        CPUState *cpustate = CPU(cpuobj);
+
+        /* Set the proper CPU index */
+        cpustate->cpu_index = i;
+
+        /* Provide Power State Coordination Interface */
+        object_property_set_int(cpuobj, QEMU_PSCI_CONDUIT_HVC,
+                                "psci-conduit", &error_abort);
+
+        /* Disable secondary CPUs */
+        object_property_set_bool(cpuobj, i > 0, "start-powered-off", &err);
+        if (err != NULL) {
+            error_propagate(errp, err);
+            return;
+        }
+
+        /* All exception levels required */
+        object_property_set_bool(cpuobj,
+                                 true, "has_el3", NULL);
+        object_property_set_bool(cpuobj,
+                                 true, "has_el2", NULL);
+
+        /* Mark realized */
+        object_property_set_bool(cpuobj, true, "realized", &err);
+        if (err != NULL) {
+            error_propagate(errp, err);
+            return;
+        }
+        object_unref(cpuobj);
+    }
+
+    /* 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);
+
+    object_property_set_bool(OBJECT(&s->gic), true, "realized", &err);
+    if (err) {
+        error_propagate(errp, err);
+        return;
+    }
+
+    sysbusdev = SYS_BUS_DEVICE(&s->gic);
+    sysbus_mmio_map(sysbusdev, 0, AW_H3_GIC_DIST_BASE);
+    sysbus_mmio_map(sysbusdev, 1, AW_H3_GIC_CPU_BASE);
+    sysbus_mmio_map(sysbusdev, 2, AW_H3_GIC_HYP_BASE);
+    sysbus_mmio_map(sysbusdev, 3, AW_H3_GIC_VCPU_BASE);
+
+    /*
+     * 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(qemu_get_cpu(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_ARM_PHYSTIMER,
+            [GTIMER_VIRT] = AW_H3_GIC_PPI_ARM_VIRTTIMER,
+            [GTIMER_HYP]  = AW_H3_GIC_PPI_ARM_HYPTIMER,
+            [GTIMER_SEC]  = AW_H3_GIC_PPI_ARM_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(sysbusdev, i,
+                           qdev_get_gpio_in(cpudev, ARM_CPU_IRQ));
+        sysbus_connect_irq(sysbusdev, i + AW_H3_NUM_CPUS,
+                           qdev_get_gpio_in(cpudev, ARM_CPU_FIQ));
+        sysbus_connect_irq(sysbusdev, i + (2 * AW_H3_NUM_CPUS),
+                           qdev_get_gpio_in(cpudev, ARM_CPU_VIRQ));
+        sysbus_connect_irq(sysbusdev, i + (3 * AW_H3_NUM_CPUS),
+                           qdev_get_gpio_in(cpudev, ARM_CPU_VFIQ));
+
+        /* GIC maintenance signal */
+        sysbus_connect_irq(sysbusdev, i + (4 * AW_H3_NUM_CPUS),
+                           qdev_get_gpio_in(DEVICE(&s->gic),
+                                            ppibase + AW_H3_GIC_PPI_MAINT));
+    }
+
+    for (i = 0; i < AW_H3_GIC_NUM_SPI; i++) {
+        s->irq[i] = qdev_get_gpio_in(DEVICE(&s->gic), i);
+    }
+
+    /* Timer */
+    object_property_set_bool(OBJECT(&s->timer), true, "realized", &err);
+    if (err != NULL) {
+        error_propagate(errp, err);
+        return;
+    }
+    sysbusdev = SYS_BUS_DEVICE(&s->timer);
+    sysbus_mmio_map(sysbusdev, 0, AW_H3_PIT_REG_BASE);
+    sysbus_connect_irq(sysbusdev, 0, s->irq[AW_H3_GIC_SPI_TIMER0]);
+    sysbus_connect_irq(sysbusdev, 1, s->irq[AW_H3_GIC_SPI_TIMER1]);
+
+    /* SRAM */
+    memory_region_init_ram(&s->sram_a1, OBJECT(dev), "sram A1",
+                            AW_H3_SRAM_A1_SIZE, &error_fatal);
+    memory_region_init_ram(&s->sram_a2, OBJECT(dev), "sram A2",
+                            AW_H3_SRAM_A2_SIZE, &error_fatal);
+    memory_region_init_ram(&s->sram_c, OBJECT(dev), "sram C",
+                            AW_H3_SRAM_C_SIZE, &error_fatal);
+    memory_region_add_subregion(get_system_memory(), AW_H3_SRAM_A1_BASE,
+                                &s->sram_a1);
+    memory_region_add_subregion(get_system_memory(), AW_H3_SRAM_A2_BASE,
+                                &s->sram_a2);
+    memory_region_add_subregion(get_system_memory(), AW_H3_SRAM_C_BASE,
+                                &s->sram_c);
+
+    /* UART */
+    if (serial_hd(0)) {
+        serial_mm_init(get_system_memory(), AW_H3_UART0_REG_BASE, 2,
+                       s->irq[AW_H3_GIC_SPI_UART0], 115200, serial_hd(0),
+                       DEVICE_NATIVE_ENDIAN);
+    }
+
+    /* Unimplemented devices */
+    create_unimplemented_device("display-engine", AW_H3_DE_BASE, AW_H3_DE_SIZE);
+    create_unimplemented_device("dma", AW_H3_DMA_BASE, AW_H3_DMA_SIZE);
+    create_unimplemented_device("lcd0", AW_H3_LCD0_BASE, AW_H3_LCD0_SIZE);
+    create_unimplemented_device("lcd1", AW_H3_LCD1_BASE, AW_H3_LCD1_SIZE);
+    create_unimplemented_device("gpu", AW_H3_GPU_BASE, AW_H3_GPU_SIZE);
+    create_unimplemented_device("hdmi", AW_H3_HDMI_BASE, AW_H3_HDMI_SIZE);
+    create_unimplemented_device("rtc", AW_H3_RTC_BASE, AW_H3_RTC_SIZE);
+    create_unimplemented_device("audio-codec", AW_H3_AC_BASE, AW_H3_AC_SIZE);
+}
+
+static void aw_h3_class_init(ObjectClass *oc, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(oc);
+
+    dc->realize = aw_h3_realize;
+    /* Reason: uses serial_hds and nd_table */
+    dc->user_creatable = false;
+}
+
+static const TypeInfo aw_h3_type_info = {
+    .name = TYPE_AW_H3,
+    .parent = TYPE_DEVICE,
+    .instance_size = sizeof(AwH3State),
+    .instance_init = aw_h3_init,
+    .class_init = aw_h3_class_init,
+};
+
+static void aw_h3_register_types(void)
+{
+    type_register_static(&aw_h3_type_info);
+}
+
+type_init(aw_h3_register_types)
diff --git a/include/hw/arm/allwinner-h3.h b/include/hw/arm/allwinner-h3.h
new file mode 100644
index 0000000000..af368c2254
--- /dev/null
+++ b/include/hw/arm/allwinner-h3.h
@@ -0,0 +1,118 @@
+/*
+ * 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/>.
+ */
+
+#ifndef HW_ARM_ALLWINNER_H3_H
+#define HW_ARM_ALLWINNER_H3_H
+
+#include "qemu/error-report.h"
+#include "qemu/units.h"
+#include "hw/char/serial.h"
+#include "hw/arm/boot.h"
+#include "hw/timer/allwinner-a10-pit.h"
+#include "hw/intc/arm_gic.h"
+#include "target/arm/cpu.h"
+
+#define AW_H3_SRAM_A1_BASE     (0x00000000)
+#define AW_H3_SRAM_A2_BASE     (0x00044000)
+#define AW_H3_SRAM_C_BASE      (0x00010000)
+#define AW_H3_DE_BASE          (0x01000000)
+#define AW_H3_SYSCON_BASE      (0x01c00000)
+#define AW_H3_DMA_BASE         (0x01c02000)
+#define AW_H3_LCD0_BASE        (0x01c0c000)
+#define AW_H3_LCD1_BASE        (0x01c0d000)
+#define AW_H3_SID_BASE         (0x01c14000)
+#define AW_H3_CCU_BASE         (0x01c20000)
+#define AW_H3_PIC_REG_BASE     (0x01c20400)
+#define AW_H3_PIT_REG_BASE     (0x01c20c00)
+#define AW_H3_AC_BASE          (0x01c22c00)
+#define AW_H3_UART0_REG_BASE   (0x01c28000)
+#define AW_H3_EMAC_BASE        (0x01c30000)
+#define AW_H3_MMC0_BASE        (0x01c0f000)
+#define AW_H3_EHCI0_BASE       (0x01c1a000)
+#define AW_H3_OHCI0_BASE       (0x01c1a400)
+#define AW_H3_EHCI1_BASE       (0x01c1b000)
+#define AW_H3_OHCI1_BASE       (0x01c1b400)
+#define AW_H3_EHCI2_BASE       (0x01c1c000)
+#define AW_H3_OHCI2_BASE       (0x01c1c400)
+#define AW_H3_EHCI3_BASE       (0x01c1d000)
+#define AW_H3_OHCI3_BASE       (0x01c1d400)
+#define AW_H3_GPU_BASE         (0x01c40000)
+#define AW_H3_GIC_DIST_BASE    (0x01c81000)
+#define AW_H3_GIC_CPU_BASE     (0x01c82000)
+#define AW_H3_GIC_HYP_BASE     (0x01c84000)
+#define AW_H3_GIC_VCPU_BASE    (0x01c86000)
+#define AW_H3_HDMI_BASE        (0x01ee0000)
+#define AW_H3_RTC_BASE         (0x01f00000)
+#define AW_H3_CPUCFG_BASE      (0x01f01c00)
+#define AW_H3_SDRAM_BASE       (0x40000000)
+
+#define AW_H3_SRAM_A1_SIZE     (64 * KiB)
+#define AW_H3_SRAM_A2_SIZE     (32 * KiB)
+#define AW_H3_SRAM_C_SIZE      (44 * KiB)
+#define AW_H3_DE_SIZE          (4 * MiB)
+#define AW_H3_DMA_SIZE         (4 * KiB)
+#define AW_H3_LCD0_SIZE        (4 * KiB)
+#define AW_H3_LCD1_SIZE        (4 * KiB)
+#define AW_H3_GPU_SIZE         (64 * KiB)
+#define AW_H3_HDMI_SIZE        (128 * KiB)
+#define AW_H3_RTC_SIZE         (1 * KiB)
+#define AW_H3_AC_SIZE          (2 * KiB)
+
+#define AW_H3_GIC_PPI_MAINT          (9)
+#define AW_H3_GIC_PPI_ARM_HYPTIMER  (10)
+#define AW_H3_GIC_PPI_ARM_VIRTTIMER (11)
+#define AW_H3_GIC_PPI_ARM_SECTIMER  (13)
+#define AW_H3_GIC_PPI_ARM_PHYSTIMER (14)
+
+#define AW_H3_GIC_SPI_UART0         (0)
+#define AW_H3_GIC_SPI_TIMER0        (18)
+#define AW_H3_GIC_SPI_TIMER1        (19)
+#define AW_H3_GIC_SPI_MMC0          (60)
+#define AW_H3_GIC_SPI_MMC1          (61)
+#define AW_H3_GIC_SPI_MMC2          (62)
+#define AW_H3_GIC_SPI_EHCI0         (72)
+#define AW_H3_GIC_SPI_OHCI0         (73)
+#define AW_H3_GIC_SPI_EHCI1         (74)
+#define AW_H3_GIC_SPI_OHCI1         (75)
+#define AW_H3_GIC_SPI_EHCI2         (76)
+#define AW_H3_GIC_SPI_OHCI2         (77)
+#define AW_H3_GIC_SPI_EHCI3         (78)
+#define AW_H3_GIC_SPI_OHCI3         (79)
+#define AW_H3_GIC_SPI_EMAC          (82)
+
+#define AW_H3_GIC_NUM_SPI           (128)
+#define AW_H3_NUM_CPUS              (4)
+
+#define TYPE_AW_H3 "allwinner-h3"
+#define AW_H3(obj) OBJECT_CHECK(AwH3State, (obj), TYPE_AW_H3)
+
+typedef struct AwH3State {
+    /*< private >*/
+    DeviceState parent_obj;
+    /*< public >*/
+
+    qemu_irq irq[AW_H3_GIC_NUM_SPI];
+    AwA10PITState timer;
+    GICState gic;
+    MemoryRegion sram_a1;
+    MemoryRegion sram_a2;
+    MemoryRegion sram_c;
+} AwH3State;
+
+#endif
-- 
2.17.1



^ permalink raw reply	[flat|nested] 33+ messages in thread

* [PATCH 02/10] hw: arm: add Xunlong Orange Pi PC machine
  2019-12-02 21:09 [PATCH 00/10] Add Allwinner H3 SoC and Orange Pi PC Machine Niek Linnenbank
  2019-12-02 21:09 ` [PATCH 01/10] hw: arm: add Allwinner H3 System-on-Chip Niek Linnenbank
@ 2019-12-02 21:09 ` Niek Linnenbank
  2019-12-03  9:17   ` Philippe Mathieu-Daudé
  2019-12-02 21:09 ` [PATCH 03/10] arm: allwinner-h3: add Clock Control Unit Niek Linnenbank
                   ` (9 subsequent siblings)
  11 siblings, 1 reply; 33+ messages in thread
From: Niek Linnenbank @ 2019-12-02 21:09 UTC (permalink / raw)
  To: qemu-devel; +Cc: b.galvani, peter.maydell, Niek Linnenbank, qemu-arm

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,
512MB RAM, 100Mbit ethernet, USB, SD/MMC, USB, HDMI and
various other I/O. This commit add support for the Xunlong
Orange Pi PC machine.

Signed-off-by: Niek Linnenbank <nieklinnenbank@gmail.com>
---
 MAINTAINERS          |  1 +
 hw/arm/Makefile.objs |  2 +-
 hw/arm/orangepi.c    | 90 ++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 92 insertions(+), 1 deletion(-)
 create mode 100644 hw/arm/orangepi.c

diff --git a/MAINTAINERS b/MAINTAINERS
index 29c9936037..42c913d6cb 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -485,6 +485,7 @@ L: qemu-arm@nongnu.org
 S: Maintained
 F: hw/*/allwinner-h3*
 F: include/hw/*/allwinner-h3*
+F: hw/arm/orangepi.c
 
 ARM PrimeCell and CMSDK devices
 M: Peter Maydell <peter.maydell@linaro.org>
diff --git a/hw/arm/Makefile.objs b/hw/arm/Makefile.objs
index 956e496052..8d5ea453d5 100644
--- a/hw/arm/Makefile.objs
+++ b/hw/arm/Makefile.objs
@@ -34,7 +34,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
+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_XLNX_ZYNQMP_ARM) += xlnx-zynqmp.o xlnx-zcu102.o
diff --git a/hw/arm/orangepi.c b/hw/arm/orangepi.c
new file mode 100644
index 0000000000..5ef2735f81
--- /dev/null
+++ b/hw/arm/orangepi.c
@@ -0,0 +1,90 @@
+/*
+ * 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 "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"
+
+static struct arm_boot_info orangepi_binfo = {
+    .loader_start = AW_H3_SDRAM_BASE,
+    .board_id = -1,
+};
+
+typedef struct OrangePiState {
+    AwH3State *h3;
+    MemoryRegion sdram;
+} OrangePiState;
+
+static void orangepi_init(MachineState *machine)
+{
+    OrangePiState *s = g_new(OrangePiState, 1);
+    Error *err = NULL;
+
+    s->h3 = AW_H3(object_new(TYPE_AW_H3));
+
+    /* Setup timer properties */
+    object_property_set_int(OBJECT(&s->h3->timer), 32768, "clk0-freq", &err);
+    if (err != NULL) {
+        error_reportf_err(err, "Couldn't set clk0 frequency: ");
+        exit(1);
+    }
+
+    object_property_set_int(OBJECT(&s->h3->timer), 24000000, "clk1-freq",
+                            &err);
+    if (err != NULL) {
+        error_reportf_err(err, "Couldn't set clk1 frequency: ");
+        exit(1);
+    }
+
+    /* Mark H3 object realized */
+    object_property_set_bool(OBJECT(s->h3), true, "realized", &err);
+    if (err != NULL) {
+        error_reportf_err(err, "Couldn't realize Allwinner H3: ");
+        exit(1);
+    }
+
+    /* RAM */
+    memory_region_allocate_system_memory(&s->sdram, NULL, "orangepi.ram",
+                                         machine->ram_size);
+    memory_region_add_subregion(get_system_memory(), AW_H3_SDRAM_BASE,
+                                &s->sdram);
+
+    /* Load target kernel */
+    orangepi_binfo.ram_size = machine->ram_size;
+    orangepi_binfo.nb_cpus  = AW_H3_NUM_CPUS;
+    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->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->ignore_memory_transaction_failures = true;
+}
+
+DEFINE_MACHINE("orangepi", orangepi_machine_init)
-- 
2.17.1



^ permalink raw reply	[flat|nested] 33+ messages in thread

* [PATCH 03/10] arm: allwinner-h3: add Clock Control Unit
  2019-12-02 21:09 [PATCH 00/10] Add Allwinner H3 SoC and Orange Pi PC Machine Niek Linnenbank
  2019-12-02 21:09 ` [PATCH 01/10] hw: arm: add Allwinner H3 System-on-Chip Niek Linnenbank
  2019-12-02 21:09 ` [PATCH 02/10] hw: arm: add Xunlong Orange Pi PC machine Niek Linnenbank
@ 2019-12-02 21:09 ` Niek Linnenbank
  2019-12-02 21:09 ` [PATCH 04/10] arm: allwinner-h3: add USB host controller Niek Linnenbank
                   ` (8 subsequent siblings)
  11 siblings, 0 replies; 33+ messages in thread
From: Niek Linnenbank @ 2019-12-02 21:09 UTC (permalink / raw)
  To: qemu-devel; +Cc: b.galvani, peter.maydell, Niek Linnenbank, qemu-arm

The Clock Control Unit is responsible for clock signal generation,
configuration and distribution in the Allwinner H3 System on Chip.
This commit adds support for the Clock Control Unit which emulates
a simple read/write register interface.

Signed-off-by: Niek Linnenbank <nieklinnenbank@gmail.com>
---
 hw/arm/allwinner-h3.c              |  11 ++
 hw/misc/Makefile.objs              |   1 +
 hw/misc/allwinner-h3-clk.c         | 227 +++++++++++++++++++++++++++++
 include/hw/arm/allwinner-h3.h      |   2 +
 include/hw/misc/allwinner-h3-clk.h |  41 ++++++
 5 files changed, 282 insertions(+)
 create mode 100644 hw/misc/allwinner-h3-clk.c
 create mode 100644 include/hw/misc/allwinner-h3-clk.h

diff --git a/hw/arm/allwinner-h3.c b/hw/arm/allwinner-h3.c
index 470fdfebef..5566e979ec 100644
--- a/hw/arm/allwinner-h3.c
+++ b/hw/arm/allwinner-h3.c
@@ -37,6 +37,9 @@ static void aw_h3_init(Object *obj)
 
     sysbus_init_child_obj(obj, "timer", &s->timer, sizeof(s->timer),
                           TYPE_AW_A10_PIT);
+
+    sysbus_init_child_obj(obj, "ccu", &s->ccu, sizeof(s->ccu),
+                          TYPE_AW_H3_CLK);
 }
 
 static void aw_h3_realize(DeviceState *dev, Error **errp)
@@ -172,6 +175,14 @@ static void aw_h3_realize(DeviceState *dev, Error **errp)
     memory_region_add_subregion(get_system_memory(), AW_H3_SRAM_C_BASE,
                                 &s->sram_c);
 
+    /* Clock Control Unit */
+    object_property_set_bool(OBJECT(&s->ccu), true, "realized", &err);
+    if (err) {
+        error_propagate(errp, err);
+        return;
+    }
+    sysbus_mmio_map(SYS_BUS_DEVICE(&s->ccu), 0, AW_H3_CCU_BASE);
+
     /* UART */
     if (serial_hd(0)) {
         serial_mm_init(get_system_memory(), AW_H3_UART0_REG_BASE, 2,
diff --git a/hw/misc/Makefile.objs b/hw/misc/Makefile.objs
index ba898a5781..200ed44ce1 100644
--- a/hw/misc/Makefile.objs
+++ b/hw/misc/Makefile.objs
@@ -28,6 +28,7 @@ common-obj-$(CONFIG_MACIO) += macio/
 
 common-obj-$(CONFIG_IVSHMEM_DEVICE) += ivshmem.o
 
+common-obj-$(CONFIG_ALLWINNER_H3) += allwinner-h3-clk.o
 common-obj-$(CONFIG_REALVIEW) += arm_sysctl.o
 common-obj-$(CONFIG_NSERIES) += cbus.o
 common-obj-$(CONFIG_ECCMEMCTL) += eccmemctl.o
diff --git a/hw/misc/allwinner-h3-clk.c b/hw/misc/allwinner-h3-clk.c
new file mode 100644
index 0000000000..77c55b4f92
--- /dev/null
+++ b/hw/misc/allwinner-h3-clk.c
@@ -0,0 +1,227 @@
+/*
+ * 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 "hw/sysbus.h"
+#include "migration/vmstate.h"
+#include "qemu/log.h"
+#include "qemu/module.h"
+#include "hw/misc/allwinner-h3-clk.h"
+
+/* CCU register offsets */
+#define REG_PLL_CPUX             (0x0000) /* PLL CPUX Control */
+#define REG_PLL_AUDIO            (0x0008) /* PLL Audio Control */
+#define REG_PLL_VIDEO            (0x0010) /* PLL Video Control */
+#define REG_PLL_VE               (0x0018) /* PLL VE Control */
+#define REG_PLL_DDR              (0x0020) /* PLL DDR Control */
+#define REG_PLL_PERIPH0          (0x0028) /* PLL Peripherals 0 Control */
+#define REG_PLL_GPU              (0x0038) /* PLL GPU Control */
+#define REG_PLL_PERIPH1          (0x0044) /* PLL Peripherals 1 Control */
+#define REG_PLL_DE               (0x0048) /* PLL Display Engine Control */
+#define REG_CPUX_AXI             (0x0050) /* CPUX/AXI Configuration */
+#define REG_APB1                 (0x0054) /* ARM Peripheral Bus 1 Config */
+#define REG_APB2                 (0x0058) /* ARM Peripheral Bus 2 Config */
+#define REG_MBUS                 (0x00FC) /* MBUS Reset */
+#define REG_PLL_TIME0            (0x0200) /* PLL Stable Time 0 */
+#define REG_PLL_TIME1            (0x0204) /* PLL Stable Time 1 */
+#define REG_PLL_CPUX_BIAS        (0x0220) /* PLL CPUX Bias */
+#define REG_PLL_AUDIO_BIAS       (0x0224) /* PLL Audio Bias */
+#define REG_PLL_VIDEO_BIAS       (0x0228) /* PLL Video Bias */
+#define REG_PLL_VE_BIAS          (0x022C) /* PLL VE Bias */
+#define REG_PLL_DDR_BIAS         (0x0230) /* PLL DDR Bias */
+#define REG_PLL_PERIPH0_BIAS     (0x0234) /* PLL Peripherals 0 Bias */
+#define REG_PLL_GPU_BIAS         (0x023C) /* PLL GPU Bias */
+#define REG_PLL_PERIPH1_BIAS     (0x0244) /* PLL Peripherals 1 Bias */
+#define REG_PLL_DE_BIAS          (0x0248) /* PLL Display Engine Bias */
+#define REG_PLL_CPUX_TUNING      (0x0250) /* PLL CPUX Tuning */
+#define REG_PLL_DDR_TUNING       (0x0260) /* PLL DDR Tuning */
+#define REG_INDEX(offset)        (offset / sizeof(uint32_t))
+
+/* CCU register flags */
+#define REG_PLL_ENABLE           (1 << 31)
+#define REG_PLL_LOCK             (1 << 28)
+
+/* CCU register reset values */
+#define REG_PLL_CPUX_RST         (0x00001000)
+#define REG_PLL_AUDIO_RST        (0x00035514)
+#define REG_PLL_VIDEO_RST        (0x03006207)
+#define REG_PLL_VE_RST           (0x03006207)
+#define REG_PLL_DDR_RST          (0x00001000)
+#define REG_PLL_PERIPH0_RST      (0x00041811)
+#define REG_PLL_GPU_RST          (0x03006207)
+#define REG_PLL_PERIPH1_RST      (0x00041811)
+#define REG_PLL_DE_RST           (0x03006207)
+#define REG_CPUX_AXI_RST         (0x00010000)
+#define REG_APB1_RST             (0x00001010)
+#define REG_APB2_RST             (0x01000000)
+#define REG_MBUS_RST             (0x80000000)
+#define REG_PLL_TIME0_RST        (0x000000FF)
+#define REG_PLL_TIME1_RST        (0x000000FF)
+#define REG_PLL_CPUX_BIAS_RST    (0x08100200)
+#define REG_PLL_AUDIO_BIAS_RST   (0x10100000)
+#define REG_PLL_VIDEO_BIAS_RST   (0x10100000)
+#define REG_PLL_VE_BIAS_RST      (0x10100000)
+#define REG_PLL_DDR_BIAS_RST     (0x81104000)
+#define REG_PLL_PERIPH0_BIAS_RST (0x10100010)
+#define REG_PLL_GPU_BIAS_RST     (0x10100000)
+#define REG_PLL_PERIPH1_BIAS_RST (0x10100010)
+#define REG_PLL_DE_BIAS_RST      (0x10100000)
+#define REG_PLL_CPUX_TUNING_RST  (0x0A101000)
+#define REG_PLL_DDR_TUNING_RST   (0x14880000)
+
+static uint64_t allwinner_h3_clk_read(void *opaque, hwaddr offset,
+                                      unsigned size)
+{
+    const AwH3ClockState *s = (AwH3ClockState *)opaque;
+    const uint32_t idx = REG_INDEX(offset);
+
+    if (idx >= AW_H3_CLK_REGS_NUM) {
+        qemu_log_mask(LOG_GUEST_ERROR, "%s: bad read offset 0x%04x\n",
+                      __func__, (uint32_t)offset);
+        return 0;
+    }
+
+    return s->regs[idx];
+}
+
+static void allwinner_h3_clk_write(void *opaque, hwaddr offset,
+                                   uint64_t val, unsigned size)
+{
+    AwH3ClockState *s = (AwH3ClockState *)opaque;
+    const uint32_t idx = REG_INDEX(offset);
+
+    if (idx >= AW_H3_CLK_REGS_NUM) {
+        qemu_log_mask(LOG_GUEST_ERROR, "%s: bad write offset 0x%04x\n",
+                      __func__, (uint32_t)offset);
+        return;
+    }
+
+    switch (offset) {
+    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;
+    default:
+        break;
+    }
+
+    s->regs[idx] = (uint32_t) val;
+}
+
+static const MemoryRegionOps allwinner_h3_clk_ops = {
+    .read = allwinner_h3_clk_read,
+    .write = allwinner_h3_clk_write,
+    .endianness = DEVICE_NATIVE_ENDIAN,
+    .valid = {
+        .min_access_size = 4,
+        .max_access_size = 4,
+        .unaligned = false
+    }
+};
+
+static void allwinner_h3_clk_reset(DeviceState *dev)
+{
+    AwH3ClockState *s = AW_H3_CLK(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_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_clk_realize(DeviceState *dev, Error **errp)
+{
+}
+
+static void allwinner_h3_clk_init(Object *obj)
+{
+    SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
+    AwH3ClockState *s = AW_H3_CLK(obj);
+
+    /* Memory mapping */
+    memory_region_init_io(&s->iomem, OBJECT(s), &allwinner_h3_clk_ops, s,
+                          TYPE_AW_H3_CLK, AW_H3_CLK_REGS_MEM_SIZE);
+    sysbus_init_mmio(sbd, &s->iomem);
+}
+
+static const VMStateDescription allwinner_h3_clk_vmstate = {
+    .name = TYPE_AW_H3_CLK,
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .fields = (VMStateField[]) {
+        VMSTATE_UINT32_ARRAY(regs, AwH3ClockState, AW_H3_CLK_REGS_NUM),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static void allwinner_h3_clk_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+
+    dc->reset = allwinner_h3_clk_reset;
+    dc->realize = allwinner_h3_clk_realize;
+    dc->vmsd = &allwinner_h3_clk_vmstate;
+}
+
+static const TypeInfo allwinner_h3_clk_info = {
+    .name          = TYPE_AW_H3_CLK,
+    .parent        = TYPE_SYS_BUS_DEVICE,
+    .instance_init = allwinner_h3_clk_init,
+    .instance_size = sizeof(AwH3ClockState),
+    .class_init    = allwinner_h3_clk_class_init,
+};
+
+static void allwinner_h3_clk_register(void)
+{
+    type_register_static(&allwinner_h3_clk_info);
+}
+
+type_init(allwinner_h3_clk_register)
diff --git a/include/hw/arm/allwinner-h3.h b/include/hw/arm/allwinner-h3.h
index af368c2254..e596516c5c 100644
--- a/include/hw/arm/allwinner-h3.h
+++ b/include/hw/arm/allwinner-h3.h
@@ -26,6 +26,7 @@
 #include "hw/arm/boot.h"
 #include "hw/timer/allwinner-a10-pit.h"
 #include "hw/intc/arm_gic.h"
+#include "hw/misc/allwinner-h3-clk.h"
 #include "target/arm/cpu.h"
 
 #define AW_H3_SRAM_A1_BASE     (0x00000000)
@@ -109,6 +110,7 @@ typedef struct AwH3State {
 
     qemu_irq irq[AW_H3_GIC_NUM_SPI];
     AwA10PITState timer;
+    AwH3ClockState ccu;
     GICState gic;
     MemoryRegion sram_a1;
     MemoryRegion sram_a2;
diff --git a/include/hw/misc/allwinner-h3-clk.h b/include/hw/misc/allwinner-h3-clk.h
new file mode 100644
index 0000000000..69ea559db1
--- /dev/null
+++ b/include/hw/misc/allwinner-h3-clk.h
@@ -0,0 +1,41 @@
+/*
+ * 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/>.
+ */
+
+#ifndef HW_MISC_ALLWINNER_H3_CLK_H
+#define HW_MISC_ALLWINNER_H3_CLK_H
+
+#include "hw/sysbus.h"
+
+#define AW_H3_CLK_REGS_MAX_ADDR (0x304)
+#define AW_H3_CLK_REGS_NUM      (AW_H3_CLK_REGS_MAX_ADDR / sizeof(uint32_t))
+#define AW_H3_CLK_REGS_MEM_SIZE (1024)
+
+#define TYPE_AW_H3_CLK    "allwinner-h3-clk"
+#define AW_H3_CLK(obj)    OBJECT_CHECK(AwH3ClockState, (obj), TYPE_AW_H3_CLK)
+
+typedef struct AwH3ClockState {
+    /*< private >*/
+    SysBusDevice parent_obj;
+    /*< public >*/
+
+    MemoryRegion iomem;
+    uint32_t regs[AW_H3_CLK_REGS_NUM];
+} AwH3ClockState;
+
+#endif
-- 
2.17.1



^ permalink raw reply	[flat|nested] 33+ messages in thread

* [PATCH 04/10] arm: allwinner-h3: add USB host controller
  2019-12-02 21:09 [PATCH 00/10] Add Allwinner H3 SoC and Orange Pi PC Machine Niek Linnenbank
                   ` (2 preceding siblings ...)
  2019-12-02 21:09 ` [PATCH 03/10] arm: allwinner-h3: add Clock Control Unit Niek Linnenbank
@ 2019-12-02 21:09 ` Niek Linnenbank
  2019-12-04 16:11   ` Aleksandar Markovic
  2019-12-02 21:09 ` [PATCH 05/10] arm: allwinner-h3: add System Control module Niek Linnenbank
                   ` (7 subsequent siblings)
  11 siblings, 1 reply; 33+ messages in thread
From: Niek Linnenbank @ 2019-12-02 21:09 UTC (permalink / raw)
  To: qemu-devel; +Cc: b.galvani, peter.maydell, Niek Linnenbank, qemu-arm

The Allwinner H3 System on Chip contains multiple USB 2.0 bus
connections which provide software access using the Enhanced
Host Controller Interface (EHCI) and Open Host Controller
Interface (OHCI) interfaces. This commit adds support for
both interfaces in the Allwinner H3 System on Chip.

Signed-off-by: Niek Linnenbank <nieklinnenbank@gmail.com>
---
 hw/arm/allwinner-h3.c    | 20 ++++++++++++++++++++
 hw/usb/hcd-ehci-sysbus.c | 17 +++++++++++++++++
 hw/usb/hcd-ehci.h        |  1 +
 3 files changed, 38 insertions(+)

diff --git a/hw/arm/allwinner-h3.c b/hw/arm/allwinner-h3.c
index 5566e979ec..afeb49c0ac 100644
--- a/hw/arm/allwinner-h3.c
+++ b/hw/arm/allwinner-h3.c
@@ -26,6 +26,7 @@
 #include "hw/sysbus.h"
 #include "hw/arm/allwinner-h3.h"
 #include "hw/misc/unimp.h"
+#include "hw/usb/hcd-ehci.h"
 #include "sysemu/sysemu.h"
 
 static void aw_h3_init(Object *obj)
@@ -183,6 +184,25 @@ static void aw_h3_realize(DeviceState *dev, Error **errp)
     }
     sysbus_mmio_map(SYS_BUS_DEVICE(&s->ccu), 0, AW_H3_CCU_BASE);
 
+    /* Universal Serial Bus */
+    sysbus_create_simple(TYPE_AW_H3_EHCI, AW_H3_EHCI0_BASE,
+                         s->irq[AW_H3_GIC_SPI_EHCI0]);
+    sysbus_create_simple(TYPE_AW_H3_EHCI, AW_H3_EHCI1_BASE,
+                         s->irq[AW_H3_GIC_SPI_EHCI1]);
+    sysbus_create_simple(TYPE_AW_H3_EHCI, AW_H3_EHCI2_BASE,
+                         s->irq[AW_H3_GIC_SPI_EHCI2]);
+    sysbus_create_simple(TYPE_AW_H3_EHCI, AW_H3_EHCI3_BASE,
+                         s->irq[AW_H3_GIC_SPI_EHCI3]);
+
+    sysbus_create_simple("sysbus-ohci", AW_H3_OHCI0_BASE,
+                         s->irq[AW_H3_GIC_SPI_OHCI0]);
+    sysbus_create_simple("sysbus-ohci", AW_H3_OHCI1_BASE,
+                         s->irq[AW_H3_GIC_SPI_OHCI1]);
+    sysbus_create_simple("sysbus-ohci", AW_H3_OHCI2_BASE,
+                         s->irq[AW_H3_GIC_SPI_OHCI2]);
+    sysbus_create_simple("sysbus-ohci", AW_H3_OHCI3_BASE,
+                         s->irq[AW_H3_GIC_SPI_OHCI3]);
+
     /* UART */
     if (serial_hd(0)) {
         serial_mm_init(get_system_memory(), AW_H3_UART0_REG_BASE, 2,
diff --git a/hw/usb/hcd-ehci-sysbus.c b/hw/usb/hcd-ehci-sysbus.c
index 020211fd10..174c3446ef 100644
--- a/hw/usb/hcd-ehci-sysbus.c
+++ b/hw/usb/hcd-ehci-sysbus.c
@@ -145,6 +145,22 @@ static const TypeInfo ehci_exynos4210_type_info = {
     .class_init    = ehci_exynos4210_class_init,
 };
 
+static void ehci_aw_h3_class_init(ObjectClass *oc, void *data)
+{
+    SysBusEHCIClass *sec = SYS_BUS_EHCI_CLASS(oc);
+    DeviceClass *dc = DEVICE_CLASS(oc);
+
+    sec->capsbase = 0x0;
+    sec->opregbase = 0x10;
+    set_bit(DEVICE_CATEGORY_USB, dc->categories);
+}
+
+static const TypeInfo ehci_aw_h3_type_info = {
+    .name          = TYPE_AW_H3_EHCI,
+    .parent        = TYPE_SYS_BUS_EHCI,
+    .class_init    = ehci_aw_h3_class_init,
+};
+
 static void ehci_tegra2_class_init(ObjectClass *oc, void *data)
 {
     SysBusEHCIClass *sec = SYS_BUS_EHCI_CLASS(oc);
@@ -267,6 +283,7 @@ static void ehci_sysbus_register_types(void)
     type_register_static(&ehci_platform_type_info);
     type_register_static(&ehci_xlnx_type_info);
     type_register_static(&ehci_exynos4210_type_info);
+    type_register_static(&ehci_aw_h3_type_info);
     type_register_static(&ehci_tegra2_type_info);
     type_register_static(&ehci_ppc4xx_type_info);
     type_register_static(&ehci_fusbh200_type_info);
diff --git a/hw/usb/hcd-ehci.h b/hw/usb/hcd-ehci.h
index 0298238f0b..edb59311c4 100644
--- a/hw/usb/hcd-ehci.h
+++ b/hw/usb/hcd-ehci.h
@@ -342,6 +342,7 @@ typedef struct EHCIPCIState {
 #define TYPE_SYS_BUS_EHCI "sysbus-ehci-usb"
 #define TYPE_PLATFORM_EHCI "platform-ehci-usb"
 #define TYPE_EXYNOS4210_EHCI "exynos4210-ehci-usb"
+#define TYPE_AW_H3_EHCI "aw-h3-ehci-usb"
 #define TYPE_TEGRA2_EHCI "tegra2-ehci-usb"
 #define TYPE_PPC4xx_EHCI "ppc4xx-ehci-usb"
 #define TYPE_FUSBH200_EHCI "fusbh200-ehci-usb"
-- 
2.17.1



^ permalink raw reply	[flat|nested] 33+ messages in thread

* [PATCH 05/10] arm: allwinner-h3: add System Control module
  2019-12-02 21:09 [PATCH 00/10] Add Allwinner H3 SoC and Orange Pi PC Machine Niek Linnenbank
                   ` (3 preceding siblings ...)
  2019-12-02 21:09 ` [PATCH 04/10] arm: allwinner-h3: add USB host controller Niek Linnenbank
@ 2019-12-02 21:09 ` Niek Linnenbank
  2019-12-02 21:09 ` [PATCH 06/10] arm/arm-powerctl: set NSACR.{CP11, CP10} bits in arm_set_cpu_on() Niek Linnenbank
                   ` (6 subsequent siblings)
  11 siblings, 0 replies; 33+ messages in thread
From: Niek Linnenbank @ 2019-12-02 21:09 UTC (permalink / raw)
  To: qemu-devel; +Cc: b.galvani, peter.maydell, Niek Linnenbank, qemu-arm

The Allwinner H3 System on Chip has an System Control
module that provides system wide generic controls and
device information. This commit adds support for the
Allwinner H3 System Control module.

Signed-off-by: Niek Linnenbank <nieklinnenbank@gmail.com>
---
 hw/arm/allwinner-h3.c                 |  11 ++
 hw/misc/Makefile.objs                 |   1 +
 hw/misc/allwinner-h3-syscon.c         | 139 ++++++++++++++++++++++++++
 include/hw/arm/allwinner-h3.h         |   2 +
 include/hw/misc/allwinner-h3-syscon.h |  43 ++++++++
 5 files changed, 196 insertions(+)
 create mode 100644 hw/misc/allwinner-h3-syscon.c
 create mode 100644 include/hw/misc/allwinner-h3-syscon.h

diff --git a/hw/arm/allwinner-h3.c b/hw/arm/allwinner-h3.c
index afeb49c0ac..ebd8fde412 100644
--- a/hw/arm/allwinner-h3.c
+++ b/hw/arm/allwinner-h3.c
@@ -41,6 +41,9 @@ static void aw_h3_init(Object *obj)
 
     sysbus_init_child_obj(obj, "ccu", &s->ccu, sizeof(s->ccu),
                           TYPE_AW_H3_CLK);
+
+    sysbus_init_child_obj(obj, "syscon", &s->syscon, sizeof(s->syscon),
+                          TYPE_AW_H3_SYSCON);
 }
 
 static void aw_h3_realize(DeviceState *dev, Error **errp)
@@ -184,6 +187,14 @@ static void aw_h3_realize(DeviceState *dev, Error **errp)
     }
     sysbus_mmio_map(SYS_BUS_DEVICE(&s->ccu), 0, AW_H3_CCU_BASE);
 
+    /* System Control */
+    object_property_set_bool(OBJECT(&s->syscon), true, "realized", &err);
+    if (err) {
+        error_propagate(errp, err);
+        return;
+    }
+    sysbus_mmio_map(SYS_BUS_DEVICE(&s->syscon), 0, AW_H3_SYSCON_BASE);
+
     /* Universal Serial Bus */
     sysbus_create_simple(TYPE_AW_H3_EHCI, AW_H3_EHCI0_BASE,
                          s->irq[AW_H3_GIC_SPI_EHCI0]);
diff --git a/hw/misc/Makefile.objs b/hw/misc/Makefile.objs
index 200ed44ce1..b234aefba5 100644
--- a/hw/misc/Makefile.objs
+++ b/hw/misc/Makefile.objs
@@ -29,6 +29,7 @@ common-obj-$(CONFIG_MACIO) += macio/
 common-obj-$(CONFIG_IVSHMEM_DEVICE) += ivshmem.o
 
 common-obj-$(CONFIG_ALLWINNER_H3) += allwinner-h3-clk.o
+common-obj-$(CONFIG_ALLWINNER_H3) += allwinner-h3-syscon.o
 common-obj-$(CONFIG_REALVIEW) += arm_sysctl.o
 common-obj-$(CONFIG_NSERIES) += cbus.o
 common-obj-$(CONFIG_ECCMEMCTL) += eccmemctl.o
diff --git a/hw/misc/allwinner-h3-syscon.c b/hw/misc/allwinner-h3-syscon.c
new file mode 100644
index 0000000000..66bd518a05
--- /dev/null
+++ b/hw/misc/allwinner-h3-syscon.c
@@ -0,0 +1,139 @@
+/*
+ * 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 "hw/sysbus.h"
+#include "migration/vmstate.h"
+#include "qemu/log.h"
+#include "qemu/module.h"
+#include "hw/misc/allwinner-h3-syscon.h"
+
+/* SYSCON register offsets */
+#define REG_VER                 (0x24)  /* Version */
+#define REG_EMAC_PHY_CLK        (0x30)  /* EMAC PHY Clock */
+#define REG_INDEX(offset)       (offset / sizeof(uint32_t))
+
+/* SYSCON register reset values */
+#define REG_VER_RST             (0x0)
+#define REG_EMAC_PHY_CLK_RST    (0x58000)
+
+static uint64_t allwinner_h3_syscon_read(void *opaque, hwaddr offset,
+                                         unsigned size)
+{
+    const AwH3SysconState *s = (AwH3SysconState *)opaque;
+    const uint32_t idx = REG_INDEX(offset);
+
+    if (idx >= AW_H3_SYSCON_REGS_NUM) {
+        qemu_log_mask(LOG_GUEST_ERROR, "%s: bad read offset 0x%04x\n",
+                      __func__, (uint32_t)offset);
+        return 0;
+    }
+
+    return s->regs[idx];
+}
+
+static void allwinner_h3_syscon_write(void *opaque, hwaddr offset,
+                                      uint64_t val, unsigned size)
+{
+    AwH3SysconState *s = (AwH3SysconState *)opaque;
+    const uint32_t idx = REG_INDEX(offset);
+
+    if (idx >= AW_H3_SYSCON_REGS_NUM) {
+        qemu_log_mask(LOG_GUEST_ERROR, "%s: bad write 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_syscon_ops = {
+    .read = allwinner_h3_syscon_read,
+    .write = allwinner_h3_syscon_write,
+    .endianness = DEVICE_NATIVE_ENDIAN,
+    .valid = {
+        .min_access_size = 4,
+        .max_access_size = 4,
+        .unaligned = false
+    }
+};
+
+static void allwinner_h3_syscon_reset(DeviceState *dev)
+{
+    AwH3SysconState *s = AW_H3_SYSCON(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_syscon_realize(DeviceState *dev, Error **errp)
+{
+}
+
+static void allwinner_h3_syscon_init(Object *obj)
+{
+    SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
+    AwH3SysconState *s = AW_H3_SYSCON(obj);
+
+    /* Memory mapping */
+    memory_region_init_io(&s->iomem, OBJECT(s), &allwinner_h3_syscon_ops, s,
+                          TYPE_AW_H3_SYSCON, AW_H3_SYSCON_REGS_MEM_SIZE);
+    sysbus_init_mmio(sbd, &s->iomem);
+}
+
+static const VMStateDescription allwinner_h3_syscon_vmstate = {
+    .name = TYPE_AW_H3_SYSCON,
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .fields = (VMStateField[]) {
+        VMSTATE_UINT32_ARRAY(regs, AwH3SysconState, AW_H3_SYSCON_REGS_NUM),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static void allwinner_h3_syscon_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+
+    dc->reset = allwinner_h3_syscon_reset;
+    dc->realize = allwinner_h3_syscon_realize;
+    dc->vmsd = &allwinner_h3_syscon_vmstate;
+}
+
+static const TypeInfo allwinner_h3_syscon_info = {
+    .name          = TYPE_AW_H3_SYSCON,
+    .parent        = TYPE_SYS_BUS_DEVICE,
+    .instance_init = allwinner_h3_syscon_init,
+    .instance_size = sizeof(AwH3SysconState),
+    .class_init    = allwinner_h3_syscon_class_init,
+};
+
+static void allwinner_h3_syscon_register(void)
+{
+    type_register_static(&allwinner_h3_syscon_info);
+}
+
+type_init(allwinner_h3_syscon_register)
diff --git a/include/hw/arm/allwinner-h3.h b/include/hw/arm/allwinner-h3.h
index e596516c5c..2bc526b77b 100644
--- a/include/hw/arm/allwinner-h3.h
+++ b/include/hw/arm/allwinner-h3.h
@@ -27,6 +27,7 @@
 #include "hw/timer/allwinner-a10-pit.h"
 #include "hw/intc/arm_gic.h"
 #include "hw/misc/allwinner-h3-clk.h"
+#include "hw/misc/allwinner-h3-syscon.h"
 #include "target/arm/cpu.h"
 
 #define AW_H3_SRAM_A1_BASE     (0x00000000)
@@ -111,6 +112,7 @@ typedef struct AwH3State {
     qemu_irq irq[AW_H3_GIC_NUM_SPI];
     AwA10PITState timer;
     AwH3ClockState ccu;
+    AwH3SysconState syscon;
     GICState gic;
     MemoryRegion sram_a1;
     MemoryRegion sram_a2;
diff --git a/include/hw/misc/allwinner-h3-syscon.h b/include/hw/misc/allwinner-h3-syscon.h
new file mode 100644
index 0000000000..22a2f2a11b
--- /dev/null
+++ b/include/hw/misc/allwinner-h3-syscon.h
@@ -0,0 +1,43 @@
+/*
+ * 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/>.
+ */
+
+#ifndef HW_MISC_ALLWINNER_H3_SYSCON_H
+#define HW_MISC_ALLWINNER_H3_SYSCON_H
+
+#include "hw/sysbus.h"
+
+#define AW_H3_SYSCON_REGS_MAX_ADDR  (0x30)
+#define AW_H3_SYSCON_REGS_NUM       ((AW_H3_SYSCON_REGS_MAX_ADDR / \
+                                      sizeof(uint32_t)) + 1)
+#define AW_H3_SYSCON_REGS_MEM_SIZE  (1024)
+
+#define TYPE_AW_H3_SYSCON    "allwinner-h3-syscon"
+#define AW_H3_SYSCON(obj)    OBJECT_CHECK(AwH3SysconState, (obj), \
+                                          TYPE_AW_H3_SYSCON)
+
+typedef struct AwH3SysconState {
+    /*< private >*/
+    SysBusDevice parent_obj;
+    /*< public >*/
+
+    MemoryRegion iomem;
+    uint32_t regs[AW_H3_SYSCON_REGS_NUM];
+} AwH3SysconState;
+
+#endif
-- 
2.17.1



^ permalink raw reply	[flat|nested] 33+ messages in thread

* [PATCH 06/10] arm/arm-powerctl: set NSACR.{CP11, CP10} bits in arm_set_cpu_on()
  2019-12-02 21:09 [PATCH 00/10] Add Allwinner H3 SoC and Orange Pi PC Machine Niek Linnenbank
                   ` (4 preceding siblings ...)
  2019-12-02 21:09 ` [PATCH 05/10] arm: allwinner-h3: add System Control module Niek Linnenbank
@ 2019-12-02 21:09 ` Niek Linnenbank
  2019-12-06 14:24   ` Peter Maydell
  2019-12-02 21:09 ` [PATCH 07/10] arm: allwinner-h3: add CPU Configuration module Niek Linnenbank
                   ` (5 subsequent siblings)
  11 siblings, 1 reply; 33+ messages in thread
From: Niek Linnenbank @ 2019-12-02 21:09 UTC (permalink / raw)
  To: qemu-devel; +Cc: b.galvani, peter.maydell, Niek Linnenbank, qemu-arm

This change ensures that the FPU can be accessed in Non-Secure mode
when the CPU core is reset using the arm_set_cpu_on() function call.
The NSACR.{CP11,CP10} bits define the exception level required to
access the FPU in Non-Secure mode. Without these bits set, the CPU
will give an undefined exception trap on the first FPU access for the
secondary cores under Linux.

Fixes: fc1120a7f5
Signed-off-by: Niek Linnenbank <nieklinnenbank@gmail.com>
---
 target/arm/arm-powerctl.c | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/target/arm/arm-powerctl.c b/target/arm/arm-powerctl.c
index f77a950db6..b064513d44 100644
--- a/target/arm/arm-powerctl.c
+++ b/target/arm/arm-powerctl.c
@@ -104,6 +104,9 @@ static void arm_set_cpu_on_async_work(CPUState *target_cpu_state,
         /* Processor is not in secure mode */
         target_cpu->env.cp15.scr_el3 |= SCR_NS;
 
+        /* Set NSACR.{CP11,CP10} so NS can access the FPU */
+        target_cpu->env.cp15.nsacr |= 3 << 10;
+
         /*
          * If QEMU is providing the equivalent of EL3 firmware, then we need
          * to make sure a CPU targeting EL2 comes out of reset with a
-- 
2.17.1



^ permalink raw reply	[flat|nested] 33+ messages in thread

* [PATCH 07/10] arm: allwinner-h3: add CPU Configuration module
  2019-12-02 21:09 [PATCH 00/10] Add Allwinner H3 SoC and Orange Pi PC Machine Niek Linnenbank
                   ` (5 preceding siblings ...)
  2019-12-02 21:09 ` [PATCH 06/10] arm/arm-powerctl: set NSACR.{CP11, CP10} bits in arm_set_cpu_on() Niek Linnenbank
@ 2019-12-02 21:09 ` Niek Linnenbank
  2019-12-02 21:09 ` [PATCH 08/10] arm: allwinner-h3: add Security Identifier device Niek Linnenbank
                   ` (4 subsequent siblings)
  11 siblings, 0 replies; 33+ messages in thread
From: Niek Linnenbank @ 2019-12-02 21:09 UTC (permalink / raw)
  To: qemu-devel; +Cc: b.galvani, peter.maydell, Niek Linnenbank, qemu-arm

The Allwinner H3 System on Chip design contains four ARM Cortex A7
processors that can be configured and reset using the CPU Configuration
module interface. This commit adds support for the CPU configuration
interface which emulates the following features:

 * CPU reset
 * Shared 64-bit timer

Signed-off-by: Niek Linnenbank <nieklinnenbank@gmail.com>
---
 hw/arm/allwinner-h3.c                 |  11 +
 hw/misc/Makefile.objs                 |   1 +
 hw/misc/allwinner-h3-cpucfg.c         | 280 ++++++++++++++++++++++++++
 include/hw/arm/allwinner-h3.h         |   2 +
 include/hw/misc/allwinner-h3-cpucfg.h |  44 ++++
 5 files changed, 338 insertions(+)
 create mode 100644 hw/misc/allwinner-h3-cpucfg.c
 create mode 100644 include/hw/misc/allwinner-h3-cpucfg.h

diff --git a/hw/arm/allwinner-h3.c b/hw/arm/allwinner-h3.c
index ebd8fde412..44aba1de6a 100644
--- a/hw/arm/allwinner-h3.c
+++ b/hw/arm/allwinner-h3.c
@@ -44,6 +44,9 @@ static void aw_h3_init(Object *obj)
 
     sysbus_init_child_obj(obj, "syscon", &s->syscon, sizeof(s->syscon),
                           TYPE_AW_H3_SYSCON);
+
+    sysbus_init_child_obj(obj, "cpucfg", &s->cpucfg, sizeof(s->cpucfg),
+                          TYPE_AW_H3_CPUCFG);
 }
 
 static void aw_h3_realize(DeviceState *dev, Error **errp)
@@ -195,6 +198,14 @@ static void aw_h3_realize(DeviceState *dev, Error **errp)
     }
     sysbus_mmio_map(SYS_BUS_DEVICE(&s->syscon), 0, AW_H3_SYSCON_BASE);
 
+    /* CPU Configuration */
+    object_property_set_bool(OBJECT(&s->cpucfg), true, "realized", &err);
+    if (err) {
+        error_propagate(errp, err);
+        return;
+    }
+    sysbus_mmio_map(SYS_BUS_DEVICE(&s->cpucfg), 0, AW_H3_CPUCFG_BASE);
+
     /* Universal Serial Bus */
     sysbus_create_simple(TYPE_AW_H3_EHCI, AW_H3_EHCI0_BASE,
                          s->irq[AW_H3_GIC_SPI_EHCI0]);
diff --git a/hw/misc/Makefile.objs b/hw/misc/Makefile.objs
index b234aefba5..c4ca2ed689 100644
--- a/hw/misc/Makefile.objs
+++ b/hw/misc/Makefile.objs
@@ -29,6 +29,7 @@ common-obj-$(CONFIG_MACIO) += macio/
 common-obj-$(CONFIG_IVSHMEM_DEVICE) += ivshmem.o
 
 common-obj-$(CONFIG_ALLWINNER_H3) += allwinner-h3-clk.o
+obj-$(CONFIG_ALLWINNER_H3) += allwinner-h3-cpucfg.o
 common-obj-$(CONFIG_ALLWINNER_H3) += allwinner-h3-syscon.o
 common-obj-$(CONFIG_REALVIEW) += arm_sysctl.o
 common-obj-$(CONFIG_NSERIES) += cbus.o
diff --git a/hw/misc/allwinner-h3-cpucfg.c b/hw/misc/allwinner-h3-cpucfg.c
new file mode 100644
index 0000000000..b47feebd73
--- /dev/null
+++ b/hw/misc/allwinner-h3-cpucfg.c
@@ -0,0 +1,280 @@
+/*
+ * Allwinner H3 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 "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 "arm-powerctl.h"
+#include "hw/misc/allwinner-h3-cpucfg.h"
+#include "trace.h"
+
+/* CPUCFG register offsets */
+#define REG_CPUS_RST_CTRL       (0x0000) /* CPUs Reset Control */
+#define REG_CPU0_RST_CTRL       (0x0040) /* CPU#0 Reset Control */
+#define REG_CPU0_CTRL           (0x0044) /* CPU#0 Control */
+#define REG_CPU0_STATUS         (0x0048) /* CPU#0 Status */
+#define REG_CPU1_RST_CTRL       (0x0080) /* CPU#1 Reset Control */
+#define REG_CPU1_CTRL           (0x0084) /* CPU#1 Control */
+#define REG_CPU1_STATUS         (0x0088) /* CPU#1 Status */
+#define REG_CPU2_RST_CTRL       (0x00C0) /* CPU#2 Reset Control */
+#define REG_CPU2_CTRL           (0x00C4) /* CPU#2 Control */
+#define REG_CPU2_STATUS         (0x00C8) /* CPU#2 Status */
+#define REG_CPU3_RST_CTRL       (0x0100) /* CPU#3 Reset Control */
+#define REG_CPU3_CTRL           (0x0104) /* CPU#3 Control */
+#define REG_CPU3_STATUS         (0x0108) /* CPU#3 Status */
+#define REG_CPU_SYS_RST         (0x0140) /* CPU System Reset */
+#define REG_CLK_GATING          (0x0144) /* CPU Clock Gating */
+#define REG_GEN_CTRL            (0x0184) /* General Control */
+#define REG_SUPER_STANDBY       (0x01A0) /* Super Standby Flag */
+#define REG_ENTRY_ADDR          (0x01A4) /* Reset Entry Address */
+#define REG_DBG_EXTERN          (0x01E4) /* Debug External */
+#define REG_CNT64_CTRL          (0x0280) /* 64-bit Counter Control */
+#define REG_CNT64_LOW           (0x0284) /* 64-bit Counter Low */
+#define REG_CNT64_HIGH          (0x0288) /* 64-bit Counter High */
+
+/* CPUCFG register flags */
+#define CPUX_RESET_RELEASED     ((1 << 1) | (1 << 0))
+#define CPUX_STATUS_SMP         (1 << 0)
+#define CPU_SYS_RESET_RELEASED  (1 << 0)
+#define CLK_GATING_ENABLE       ((1 << 8) | 0xF)
+
+/* CPUCFG register reset values */
+#define REG_CLK_GATING_RST      (0x0000010F)
+#define REG_GEN_CTRL_RST        (0x00000020)
+#define REG_SUPER_STANDBY_RST   (0x0)
+#define REG_CNT64_CTRL_RST      (0x0)
+
+static void allwinner_h3_cpucfg_cpu_reset(AwH3CpuCfgState *s, uint8_t cpu_id)
+{
+    int ret;
+
+    trace_allwinner_h3_cpucfg_cpu_reset(cpu_id, s->entry_addr);
+
+    ret = arm_set_cpu_on(cpu_id, s->entry_addr, 0, 3, false);
+    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_h3_cpucfg_read(void *opaque, hwaddr offset,
+                                         unsigned size)
+{
+    const AwH3CpuCfgState *s = (AwH3CpuCfgState *)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 */
+        break;
+    case REG_CNT64_CTRL:        /* 64-bit Counter Control */
+        val = s->counter_ctrl;
+        break;
+    case REG_CNT64_LOW:         /* 64-bit Counter Low */
+        val = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) & 0xffffffff;
+        break;
+    case REG_CNT64_HIGH:        /* 64-bit Counter High */
+        val = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) >> 32;
+        break;
+    default:
+        qemu_log_mask(LOG_GUEST_ERROR, "%s: bad read offset 0x%04x\n",
+                      __func__, (uint32_t)offset);
+        return 0;
+    }
+
+    trace_allwinner_h3_cpucfg_read(offset, val, size);
+
+    return val;
+}
+
+static void allwinner_h3_cpucfg_write(void *opaque, hwaddr offset,
+                                      uint64_t val, unsigned size)
+{
+    AwH3CpuCfgState *s = (AwH3CpuCfgState *)opaque;
+
+    trace_allwinner_h3_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 */
+        if (val) {
+            allwinner_h3_cpucfg_cpu_reset(s, 0);
+        }
+        break;
+    case REG_CPU1_RST_CTRL:     /* CPU#1 Reset Control */
+        if (val) {
+            allwinner_h3_cpucfg_cpu_reset(s, 1);
+        }
+        break;
+    case REG_CPU2_RST_CTRL:     /* CPU#2 Reset Control */
+        if (val) {
+            allwinner_h3_cpucfg_cpu_reset(s, 2);
+        }
+        break;
+    case REG_CPU3_RST_CTRL:     /* CPU#3 Reset Control */
+        if (val) {
+            allwinner_h3_cpucfg_cpu_reset(s, 3);
+        }
+        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 */
+    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 */
+        break;
+    case REG_CNT64_CTRL:        /* 64-bit Counter Control */
+        s->counter_ctrl = val;
+        break;
+    case REG_CNT64_LOW:         /* 64-bit Counter Low */
+    case REG_CNT64_HIGH:        /* 64-bit Counter High */
+        break;
+    default:
+        qemu_log_mask(LOG_GUEST_ERROR, "%s: bad write offset 0x%04x\n",
+                      __func__, (uint32_t)offset);
+        return;
+    }
+}
+
+static const MemoryRegionOps allwinner_h3_cpucfg_ops = {
+    .read = allwinner_h3_cpucfg_read,
+    .write = allwinner_h3_cpucfg_write,
+    .endianness = DEVICE_NATIVE_ENDIAN,
+    .valid = {
+        .min_access_size = 4,
+        .max_access_size = 4,
+        .unaligned = false
+    }
+};
+
+static void allwinner_h3_cpucfg_reset(DeviceState *dev)
+{
+    AwH3CpuCfgState *s = AW_H3_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;
+    s->counter_ctrl = REG_CNT64_CTRL_RST;
+}
+
+static void allwinner_h3_cpucfg_realize(DeviceState *dev, Error **errp)
+{
+}
+
+static void allwinner_h3_cpucfg_init(Object *obj)
+{
+    SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
+    AwH3CpuCfgState *s = AW_H3_CPUCFG(obj);
+
+    /* Memory mapping */
+    memory_region_init_io(&s->iomem, OBJECT(s), &allwinner_h3_cpucfg_ops, s,
+                          TYPE_AW_H3_CPUCFG, AW_H3_CPUCFG_REGS_MEM_SIZE);
+    sysbus_init_mmio(sbd, &s->iomem);
+}
+
+static const VMStateDescription allwinner_h3_cpucfg_vmstate = {
+    .name = TYPE_AW_H3_CPUCFG,
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .fields = (VMStateField[]) {
+        VMSTATE_UINT32(gen_ctrl, AwH3CpuCfgState),
+        VMSTATE_UINT32(super_standby, AwH3CpuCfgState),
+        VMSTATE_UINT32(counter_ctrl, AwH3CpuCfgState),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static void allwinner_h3_cpucfg_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+
+    dc->reset = allwinner_h3_cpucfg_reset;
+    dc->realize = allwinner_h3_cpucfg_realize;
+    dc->vmsd = &allwinner_h3_cpucfg_vmstate;
+}
+
+static const TypeInfo allwinner_h3_cpucfg_info = {
+    .name          = TYPE_AW_H3_CPUCFG,
+    .parent        = TYPE_SYS_BUS_DEVICE,
+    .instance_init = allwinner_h3_cpucfg_init,
+    .instance_size = sizeof(AwH3CpuCfgState),
+    .class_init    = allwinner_h3_cpucfg_class_init,
+};
+
+static void allwinner_h3_cpucfg_register(void)
+{
+    type_register_static(&allwinner_h3_cpucfg_info);
+}
+
+type_init(allwinner_h3_cpucfg_register)
diff --git a/include/hw/arm/allwinner-h3.h b/include/hw/arm/allwinner-h3.h
index 2bc526b77b..374061b550 100644
--- a/include/hw/arm/allwinner-h3.h
+++ b/include/hw/arm/allwinner-h3.h
@@ -27,6 +27,7 @@
 #include "hw/timer/allwinner-a10-pit.h"
 #include "hw/intc/arm_gic.h"
 #include "hw/misc/allwinner-h3-clk.h"
+#include "hw/misc/allwinner-h3-cpucfg.h"
 #include "hw/misc/allwinner-h3-syscon.h"
 #include "target/arm/cpu.h"
 
@@ -112,6 +113,7 @@ typedef struct AwH3State {
     qemu_irq irq[AW_H3_GIC_NUM_SPI];
     AwA10PITState timer;
     AwH3ClockState ccu;
+    AwH3CpuCfgState cpucfg;
     AwH3SysconState syscon;
     GICState gic;
     MemoryRegion sram_a1;
diff --git a/include/hw/misc/allwinner-h3-cpucfg.h b/include/hw/misc/allwinner-h3-cpucfg.h
new file mode 100644
index 0000000000..808aaa90f6
--- /dev/null
+++ b/include/hw/misc/allwinner-h3-cpucfg.h
@@ -0,0 +1,44 @@
+/*
+ * Allwinner H3 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/>.
+ */
+
+#ifndef HW_MISC_ALLWINNER_H3_CPUCFG_H
+#define HW_MISC_ALLWINNER_H3_CPUCFG_H
+
+#include "hw/sysbus.h"
+
+#define AW_H3_CPUCFG_REGS_MEM_SIZE  (1024)
+
+#define TYPE_AW_H3_CPUCFG   "allwinner-h3-cpucfg"
+#define AW_H3_CPUCFG(obj)   OBJECT_CHECK(AwH3CpuCfgState, (obj), \
+                                         TYPE_AW_H3_CPUCFG)
+
+typedef struct AwH3CpuCfgState {
+    /*< private >*/
+    SysBusDevice parent_obj;
+    /*< public >*/
+
+    MemoryRegion iomem;
+    uint32_t gen_ctrl;
+    uint32_t super_standby;
+    uint32_t entry_addr;
+    uint32_t counter_ctrl;
+
+} AwH3CpuCfgState;
+
+#endif
-- 
2.17.1



^ permalink raw reply	[flat|nested] 33+ messages in thread

* [PATCH 08/10] arm: allwinner-h3: add Security Identifier device
  2019-12-02 21:09 [PATCH 00/10] Add Allwinner H3 SoC and Orange Pi PC Machine Niek Linnenbank
                   ` (6 preceding siblings ...)
  2019-12-02 21:09 ` [PATCH 07/10] arm: allwinner-h3: add CPU Configuration module Niek Linnenbank
@ 2019-12-02 21:09 ` Niek Linnenbank
  2019-12-06 14:27   ` Peter Maydell
  2019-12-02 21:09 ` [PATCH 09/10] arm: allwinner-h3: add SD/MMC host controller Niek Linnenbank
                   ` (3 subsequent siblings)
  11 siblings, 1 reply; 33+ messages in thread
From: Niek Linnenbank @ 2019-12-02 21:09 UTC (permalink / raw)
  To: qemu-devel; +Cc: b.galvani, peter.maydell, Niek Linnenbank, qemu-arm

The Security Identifier device in Allwinner H3 System on Chip
gives applications a per-board unique identifier. This commit
adds support for the Allwinner H3 Security Identifier using
randomized data as input.

Signed-off-by: Niek Linnenbank <nieklinnenbank@gmail.com>
---
 hw/arm/allwinner-h3.c              |  11 ++
 hw/misc/Makefile.objs              |   1 +
 hw/misc/allwinner-h3-sid.c         | 162 +++++++++++++++++++++++++++++
 hw/misc/trace-events               |   5 +
 include/hw/arm/allwinner-h3.h      |   2 +
 include/hw/misc/allwinner-h3-sid.h |  42 ++++++++
 6 files changed, 223 insertions(+)
 create mode 100644 hw/misc/allwinner-h3-sid.c
 create mode 100644 include/hw/misc/allwinner-h3-sid.h

diff --git a/hw/arm/allwinner-h3.c b/hw/arm/allwinner-h3.c
index 44aba1de6a..4fc4c8c725 100644
--- a/hw/arm/allwinner-h3.c
+++ b/hw/arm/allwinner-h3.c
@@ -47,6 +47,9 @@ static void aw_h3_init(Object *obj)
 
     sysbus_init_child_obj(obj, "cpucfg", &s->cpucfg, sizeof(s->cpucfg),
                           TYPE_AW_H3_CPUCFG);
+
+    sysbus_init_child_obj(obj, "sid", &s->sid, sizeof(s->sid),
+                          TYPE_AW_H3_SID);
 }
 
 static void aw_h3_realize(DeviceState *dev, Error **errp)
@@ -206,6 +209,14 @@ static void aw_h3_realize(DeviceState *dev, Error **errp)
     }
     sysbus_mmio_map(SYS_BUS_DEVICE(&s->cpucfg), 0, AW_H3_CPUCFG_BASE);
 
+    /* Security Identifier */
+    object_property_set_bool(OBJECT(&s->sid), true, "realized", &err);
+    if (err) {
+        error_propagate(errp, err);
+        return;
+    }
+    sysbus_mmio_map(SYS_BUS_DEVICE(&s->sid), 0, AW_H3_SID_BASE);
+
     /* Universal Serial Bus */
     sysbus_create_simple(TYPE_AW_H3_EHCI, AW_H3_EHCI0_BASE,
                          s->irq[AW_H3_GIC_SPI_EHCI0]);
diff --git a/hw/misc/Makefile.objs b/hw/misc/Makefile.objs
index c4ca2ed689..f3620eee4e 100644
--- a/hw/misc/Makefile.objs
+++ b/hw/misc/Makefile.objs
@@ -31,6 +31,7 @@ common-obj-$(CONFIG_IVSHMEM_DEVICE) += ivshmem.o
 common-obj-$(CONFIG_ALLWINNER_H3) += allwinner-h3-clk.o
 obj-$(CONFIG_ALLWINNER_H3) += allwinner-h3-cpucfg.o
 common-obj-$(CONFIG_ALLWINNER_H3) += allwinner-h3-syscon.o
+common-obj-$(CONFIG_ALLWINNER_H3) += allwinner-h3-sid.o
 common-obj-$(CONFIG_REALVIEW) += arm_sysctl.o
 common-obj-$(CONFIG_NSERIES) += cbus.o
 common-obj-$(CONFIG_ECCMEMCTL) += eccmemctl.o
diff --git a/hw/misc/allwinner-h3-sid.c b/hw/misc/allwinner-h3-sid.c
new file mode 100644
index 0000000000..e13e0d9887
--- /dev/null
+++ b/hw/misc/allwinner-h3-sid.c
@@ -0,0 +1,162 @@
+/*
+ * Allwinner H3 Security ID 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 "hw/sysbus.h"
+#include "migration/vmstate.h"
+#include "qemu/log.h"
+#include "qemu/module.h"
+#include "qemu/guest-random.h"
+#include "qapi/error.h"
+#include "hw/misc/allwinner-h3-sid.h"
+
+/* SID register offsets */
+#define REG_PRCTL         (0x40) /* Control */
+#define REG_RDKEY         (0x60) /* Read Key */
+
+/* SID register flags */
+#define REG_PRCTL_WRITE   (0x2)  /* Unknown write flag */
+#define REG_PRCTL_OP_LOCK (0xAC) /* Lock operation */
+
+static uint64_t allwinner_h3_sid_read(void *opaque, hwaddr offset,
+                                      unsigned size)
+{
+    const AwH3SidState *s = (AwH3SidState *)opaque;
+    uint64_t val = 0;
+
+    switch (offset) {
+    case REG_PRCTL:    /* Control */
+        val = s->control;
+        break;
+    case REG_RDKEY:    /* Read Key */
+        val = s->rdkey;
+        break;
+    default:
+        qemu_log_mask(LOG_GUEST_ERROR, "%s: bad read offset 0x%04x\n",
+                      __func__, (uint32_t)offset);
+        return 0;
+    }
+
+    return val;
+}
+
+static void allwinner_h3_sid_write(void *opaque, hwaddr offset,
+                                      uint64_t val, unsigned size)
+{
+    AwH3SidState *s = (AwH3SidState *)opaque;
+
+    switch (offset) {
+    case REG_PRCTL:    /* Control */
+        s->control = val & ~(REG_PRCTL_WRITE);
+        if (!(s->control & REG_PRCTL_OP_LOCK)) {
+            uint32_t id = (s->control >> 16) / sizeof(uint32_t);
+            if (id < AW_H3_SID_NUM_IDS) {
+                s->rdkey = s->identifier[id];
+            }
+        }
+        break;
+    case REG_RDKEY:    /* Read Key */
+        break;
+    default:
+        qemu_log_mask(LOG_GUEST_ERROR, "%s: bad write offset 0x%04x\n",
+                      __func__, (uint32_t)offset);
+        break;
+    }
+}
+
+static const MemoryRegionOps allwinner_h3_sid_ops = {
+    .read = allwinner_h3_sid_read,
+    .write = allwinner_h3_sid_write,
+    .endianness = DEVICE_NATIVE_ENDIAN,
+    .valid = {
+        .min_access_size = 4,
+        .max_access_size = 4,
+        .unaligned = false
+    }
+};
+
+static void allwinner_h3_sid_reset(DeviceState *dev)
+{
+    AwH3SidState *s = AW_H3_SID(dev);
+    Error *err = NULL;
+
+    /* Set default values for registers */
+    s->control = 0;
+    s->rdkey = 0;
+
+    /* Initialize identifier data */
+    for (int i = 0; i < AW_H3_SID_NUM_IDS; i++) {
+        s->identifier[i] = 0;
+    }
+
+    if (qemu_guest_getrandom(s->identifier, sizeof(s->identifier), &err)) {
+        error_report_err(err);
+    }
+}
+
+static void allwinner_h3_sid_realize(DeviceState *dev, Error **errp)
+{
+}
+
+static void allwinner_h3_sid_init(Object *obj)
+{
+    SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
+    AwH3SidState *s = AW_H3_SID(obj);
+
+    /* Memory mapping */
+    memory_region_init_io(&s->iomem, OBJECT(s), &allwinner_h3_sid_ops, s,
+                          TYPE_AW_H3_SID, AW_H3_SID_REGS_MEM_SIZE);
+    sysbus_init_mmio(sbd, &s->iomem);
+}
+
+static const VMStateDescription allwinner_h3_sid_vmstate = {
+    .name = TYPE_AW_H3_SID,
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .fields = (VMStateField[]) {
+        VMSTATE_UINT32(control, AwH3SidState),
+        VMSTATE_UINT32(rdkey, AwH3SidState),
+        VMSTATE_UINT32_ARRAY(identifier, AwH3SidState, AW_H3_SID_NUM_IDS),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static void allwinner_h3_sid_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+
+    dc->reset = allwinner_h3_sid_reset;
+    dc->realize = allwinner_h3_sid_realize;
+    dc->vmsd = &allwinner_h3_sid_vmstate;
+}
+
+static const TypeInfo allwinner_h3_sid_info = {
+    .name          = TYPE_AW_H3_SID,
+    .parent        = TYPE_SYS_BUS_DEVICE,
+    .instance_init = allwinner_h3_sid_init,
+    .instance_size = sizeof(AwH3SidState),
+    .class_init    = allwinner_h3_sid_class_init,
+};
+
+static void allwinner_h3_sid_register(void)
+{
+    type_register_static(&allwinner_h3_sid_info);
+}
+
+type_init(allwinner_h3_sid_register)
diff --git a/hw/misc/trace-events b/hw/misc/trace-events
index 1deb1d08c1..5d8a95816a 100644
--- a/hw/misc/trace-events
+++ b/hw/misc/trace-events
@@ -1,5 +1,10 @@
 # See docs/devel/tracing.txt for syntax documentation.
 
+# allwinner-h3-cpucfg.c
+allwinner_h3_cpucfg_cpu_reset(uint8_t cpu_id, uint32_t reset_addr) "H3-CPUCFG: cpu_reset: id %u, reset_addr 0x%08x"
+allwinner_h3_cpucfg_read(uint64_t offset, uint64_t data, unsigned size) "H3-CPUCFG: read: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u"
+allwinner_h3_cpucfg_write(uint64_t offset, uint64_t data, unsigned size) "H3-CPUCFG: write: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u"
+
 # eccmemctl.c
 ecc_mem_writel_mer(uint32_t val) "Write memory enable 0x%08x"
 ecc_mem_writel_mdr(uint32_t val) "Write memory delay 0x%08x"
diff --git a/include/hw/arm/allwinner-h3.h b/include/hw/arm/allwinner-h3.h
index 374061b550..33602599eb 100644
--- a/include/hw/arm/allwinner-h3.h
+++ b/include/hw/arm/allwinner-h3.h
@@ -29,6 +29,7 @@
 #include "hw/misc/allwinner-h3-clk.h"
 #include "hw/misc/allwinner-h3-cpucfg.h"
 #include "hw/misc/allwinner-h3-syscon.h"
+#include "hw/misc/allwinner-h3-sid.h"
 #include "target/arm/cpu.h"
 
 #define AW_H3_SRAM_A1_BASE     (0x00000000)
@@ -115,6 +116,7 @@ typedef struct AwH3State {
     AwH3ClockState ccu;
     AwH3CpuCfgState cpucfg;
     AwH3SysconState syscon;
+    AwH3SidState sid;
     GICState gic;
     MemoryRegion sram_a1;
     MemoryRegion sram_a2;
diff --git a/include/hw/misc/allwinner-h3-sid.h b/include/hw/misc/allwinner-h3-sid.h
new file mode 100644
index 0000000000..359cc86dfc
--- /dev/null
+++ b/include/hw/misc/allwinner-h3-sid.h
@@ -0,0 +1,42 @@
+/*
+ * Allwinner H3 Security ID 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/>.
+ */
+
+#ifndef HW_MISC_ALLWINNER_H3_SID_H
+#define HW_MISC_ALLWINNER_H3_SID_H
+
+#include "hw/sysbus.h"
+
+#define AW_H3_SID_NUM_IDS        (4)
+#define AW_H3_SID_REGS_MEM_SIZE  (1024)
+
+#define TYPE_AW_H3_SID    "allwinner-h3-sid"
+#define AW_H3_SID(obj)    OBJECT_CHECK(AwH3SidState, (obj), TYPE_AW_H3_SID)
+
+typedef struct AwH3SidState {
+    /*< private >*/
+    SysBusDevice parent_obj;
+    /*< public >*/
+
+    MemoryRegion iomem;
+    uint32_t control;
+    uint32_t rdkey;
+    uint32_t identifier[AW_H3_SID_NUM_IDS];
+} AwH3SidState;
+
+#endif
-- 
2.17.1



^ permalink raw reply	[flat|nested] 33+ messages in thread

* [PATCH 09/10] arm: allwinner-h3: add SD/MMC host controller
  2019-12-02 21:09 [PATCH 00/10] Add Allwinner H3 SoC and Orange Pi PC Machine Niek Linnenbank
                   ` (7 preceding siblings ...)
  2019-12-02 21:09 ` [PATCH 08/10] arm: allwinner-h3: add Security Identifier device Niek Linnenbank
@ 2019-12-02 21:09 ` Niek Linnenbank
  2019-12-02 21:09 ` [PATCH 10/10] arm: allwinner-h3: add EMAC ethernet device Niek Linnenbank
                   ` (2 subsequent siblings)
  11 siblings, 0 replies; 33+ messages in thread
From: Niek Linnenbank @ 2019-12-02 21:09 UTC (permalink / raw)
  To: qemu-devel; +Cc: b.galvani, peter.maydell, Niek Linnenbank, qemu-arm

The Allwinner H3 System on Chip contains an integrated storage
controller for Secure Digital (SD) and Multi Media Card (MMC)
interfaces. This commit adds support for the Allwinner H3
SD/MMC storage controller with the following emulated features:

 * DMA transfers
 * Direct FIFO I/O
 * Short/Long format command responses
 * Auto-Stop command (CMD12)
 * Insert & remove card detection

Signed-off-by: Niek Linnenbank <nieklinnenbank@gmail.com>
---
 hw/arm/allwinner-h3.c               |  20 +
 hw/arm/orangepi.c                   |  17 +
 hw/sd/Makefile.objs                 |   1 +
 hw/sd/allwinner-h3-sdhost.c         | 791 ++++++++++++++++++++++++++++
 hw/sd/trace-events                  |   7 +
 include/hw/arm/allwinner-h3.h       |   2 +
 include/hw/sd/allwinner-h3-sdhost.h |  73 +++
 7 files changed, 911 insertions(+)
 create mode 100644 hw/sd/allwinner-h3-sdhost.c
 create mode 100644 include/hw/sd/allwinner-h3-sdhost.h

diff --git a/hw/arm/allwinner-h3.c b/hw/arm/allwinner-h3.c
index 4fc4c8c725..c2972caf88 100644
--- a/hw/arm/allwinner-h3.c
+++ b/hw/arm/allwinner-h3.c
@@ -50,6 +50,9 @@ static void aw_h3_init(Object *obj)
 
     sysbus_init_child_obj(obj, "sid", &s->sid, sizeof(s->sid),
                           TYPE_AW_H3_SID);
+
+    sysbus_init_child_obj(obj, "mmc0", &s->mmc0, sizeof(s->mmc0),
+                          TYPE_AW_H3_SDHOST);
 }
 
 static void aw_h3_realize(DeviceState *dev, Error **errp)
@@ -217,6 +220,23 @@ static void aw_h3_realize(DeviceState *dev, Error **errp)
     }
     sysbus_mmio_map(SYS_BUS_DEVICE(&s->sid), 0, AW_H3_SID_BASE);
 
+    /* SD/MMC */
+    object_property_set_bool(OBJECT(&s->mmc0), true, "realized", &err);
+    if (err != NULL) {
+        error_propagate(errp, err);
+        return;
+    }
+    sysbusdev = SYS_BUS_DEVICE(&s->mmc0);
+    sysbus_mmio_map(sysbusdev, 0, AW_H3_MMC0_BASE);
+    sysbus_connect_irq(sysbusdev, 0, s->irq[AW_H3_GIC_SPI_MMC0]);
+
+    object_property_add_alias(OBJECT(s), "sd-bus", OBJECT(&s->mmc0),
+                              "sd-bus", &err);
+    if (err) {
+        error_propagate(errp, err);
+        return;
+    }
+
     /* Universal Serial Bus */
     sysbus_create_simple(TYPE_AW_H3_EHCI, AW_H3_EHCI0_BASE,
                          s->irq[AW_H3_GIC_SPI_EHCI0]);
diff --git a/hw/arm/orangepi.c b/hw/arm/orangepi.c
index 5ef2735f81..dee3efaf08 100644
--- a/hw/arm/orangepi.c
+++ b/hw/arm/orangepi.c
@@ -39,6 +39,10 @@ typedef struct OrangePiState {
 static void orangepi_init(MachineState *machine)
 {
     OrangePiState *s = g_new(OrangePiState, 1);
+    DriveInfo *di;
+    BlockBackend *blk;
+    BusState *bus;
+    DeviceState *carddev;
     Error *err = NULL;
 
     s->h3 = AW_H3(object_new(TYPE_AW_H3));
@@ -64,6 +68,18 @@ static void orangepi_init(MachineState *machine)
         exit(1);
     }
 
+    /* Create and plug in the SD card */
+    di = drive_get_next(IF_SD);
+    blk = di ? blk_by_legacy_dinfo(di) : NULL;
+    bus = qdev_get_child_bus(DEVICE(s->h3), "sd-bus");
+    if (bus == NULL) {
+        error_report("No SD/MMC found in H3 object");
+        exit(1);
+    }
+    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);
+
     /* RAM */
     memory_region_allocate_system_memory(&s->sdram, NULL, "orangepi.ram",
                                          machine->ram_size);
@@ -80,6 +96,7 @@ 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;
diff --git a/hw/sd/Makefile.objs b/hw/sd/Makefile.objs
index a884c238df..e7cc5ab739 100644
--- a/hw/sd/Makefile.objs
+++ b/hw/sd/Makefile.objs
@@ -4,6 +4,7 @@ common-obj-$(CONFIG_SD) += sd.o core.o sdmmc-internal.o
 common-obj-$(CONFIG_SDHCI) += sdhci.o
 common-obj-$(CONFIG_SDHCI_PCI) += sdhci-pci.o
 
+obj-$(CONFIG_ALLWINNER_H3) += allwinner-h3-sdhost.o
 obj-$(CONFIG_MILKYMIST) += milkymist-memcard.o
 obj-$(CONFIG_OMAP) += omap_mmc.o
 obj-$(CONFIG_PXA2XX) += pxa2xx_mmci.o
diff --git a/hw/sd/allwinner-h3-sdhost.c b/hw/sd/allwinner-h3-sdhost.c
new file mode 100644
index 0000000000..26e113a144
--- /dev/null
+++ b/hw/sd/allwinner-h3-sdhost.c
@@ -0,0 +1,791 @@
+/*
+ * Allwinner H3 SD Host 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/log.h"
+#include "qemu/module.h"
+#include "sysemu/blockdev.h"
+#include "hw/irq.h"
+#include "hw/sd/allwinner-h3-sdhost.h"
+#include "migration/vmstate.h"
+#include "trace.h"
+
+#define TYPE_AW_H3_SDHOST_BUS "allwinner-h3-sdhost-bus"
+#define AW_H3_SDHOST_BUS(obj) \
+    OBJECT_CHECK(SDBus, (obj), TYPE_AW_H3_SDHOST_BUS)
+
+/* SD Host register offsets */
+#define REG_SD_GCTL        (0x00)  /* Global Control */
+#define REG_SD_CKCR        (0x04)  /* Clock Control */
+#define REG_SD_TMOR        (0x08)  /* Timeout */
+#define REG_SD_BWDR        (0x0C)  /* Bus Width */
+#define REG_SD_BKSR        (0x10)  /* Block Size */
+#define REG_SD_BYCR        (0x14)  /* Byte Count */
+#define REG_SD_CMDR        (0x18)  /* Command */
+#define REG_SD_CAGR        (0x1C)  /* Command Argument */
+#define REG_SD_RESP0       (0x20)  /* Response Zero */
+#define REG_SD_RESP1       (0x24)  /* Response One */
+#define REG_SD_RESP2       (0x28)  /* Response Two */
+#define REG_SD_RESP3       (0x2C)  /* Response Three */
+#define REG_SD_IMKR        (0x30)  /* Interrupt Mask */
+#define REG_SD_MISR        (0x34)  /* Masked Interrupt Status */
+#define REG_SD_RISR        (0x38)  /* Raw Interrupt Status */
+#define REG_SD_STAR        (0x3C)  /* Status */
+#define REG_SD_FWLR        (0x40)  /* FIFO Water Level */
+#define REG_SD_FUNS        (0x44)  /* FIFO Function Select */
+#define REG_SD_DBGC        (0x50)  /* Debug Enable */
+#define REG_SD_A12A        (0x58)  /* Auto command 12 argument */
+#define REG_SD_NTSR        (0x5C)  /* SD NewTiming Set */
+#define REG_SD_SDBG        (0x60)  /* SD newTiming Set Debug */
+#define REG_SD_HWRST       (0x78)  /* Hardware Reset Register */
+#define REG_SD_DMAC        (0x80)  /* Internal DMA Controller Control */
+#define REG_SD_DLBA        (0x84)  /* Descriptor List Base Address */
+#define REG_SD_IDST        (0x88)  /* Internal DMA Controller Status */
+#define REG_SD_IDIE        (0x8C)  /* Internal DMA Controller IRQ Enable */
+#define REG_SD_THLDC       (0x100) /* Card Threshold Control */
+#define REG_SD_DSBD        (0x10C) /* eMMC DDR Start Bit Detection Control */
+#define REG_SD_RES_CRC     (0x110) /* Response CRC from card/eMMC */
+#define REG_SD_DATA7_CRC   (0x114) /* CRC Data 7 from card/eMMC */
+#define REG_SD_DATA6_CRC   (0x118) /* CRC Data 6 from card/eMMC */
+#define REG_SD_DATA5_CRC   (0x11C) /* CRC Data 5 from card/eMMC */
+#define REG_SD_DATA4_CRC   (0x120) /* CRC Data 4 from card/eMMC */
+#define REG_SD_DATA3_CRC   (0x124) /* CRC Data 3 from card/eMMC */
+#define REG_SD_DATA2_CRC   (0x128) /* CRC Data 2 from card/eMMC */
+#define REG_SD_DATA1_CRC   (0x12C) /* CRC Data 1 from card/eMMC */
+#define REG_SD_DATA0_CRC   (0x130) /* CRC Data 0 from card/eMMC */
+#define REG_SD_CRC_STA     (0x134) /* CRC status from card/eMMC during write */
+#define REG_SD_FIFO        (0x200) /* Read/Write FIFO */
+
+/* SD Host register flags */
+#define SD_GCTL_FIFO_AC_MOD     (1 << 31)
+#define SD_GCTL_DDR_MOD_SEL     (1 << 10)
+#define SD_GCTL_CD_DBC_ENB      (1 << 8)
+#define SD_GCTL_DMA_ENB         (1 << 5)
+#define SD_GCTL_INT_ENB         (1 << 4)
+#define SD_GCTL_DMA_RST         (1 << 2)
+#define SD_GCTL_FIFO_RST        (1 << 1)
+#define SD_GCTL_SOFT_RST        (1 << 0)
+
+#define SD_CMDR_LOAD            (1 << 31)
+#define SD_CMDR_CLKCHANGE       (1 << 21)
+#define SD_CMDR_WRITE           (1 << 10)
+#define SD_CMDR_AUTOSTOP        (1 << 12)
+#define SD_CMDR_DATA            (1 << 9)
+#define SD_CMDR_RESPONSE_LONG   (1 << 7)
+#define SD_CMDR_RESPONSE        (1 << 6)
+#define SD_CMDR_CMDID_MASK      (0x3f)
+
+#define SD_RISR_CARD_REMOVE     (1 << 31)
+#define SD_RISR_CARD_INSERT     (1 << 30)
+#define SD_RISR_AUTOCMD_DONE    (1 << 14)
+#define SD_RISR_DATA_COMPLETE   (1 << 3)
+#define SD_RISR_CMD_COMPLETE    (1 << 2)
+#define SD_RISR_NO_RESPONSE     (1 << 1)
+
+#define SD_STAR_CARD_PRESENT    (1 << 8)
+
+#define SD_IDST_SUM_RECEIVE_IRQ (1 << 8)
+#define SD_IDST_RECEIVE_IRQ     (1 << 1)
+#define SD_IDST_TRANSMIT_IRQ    (1 << 0)
+#define SD_IDST_IRQ_MASK        (SD_IDST_RECEIVE_IRQ | SD_IDST_TRANSMIT_IRQ | \
+                                 SD_IDST_SUM_RECEIVE_IRQ)
+#define SD_IDST_WR_MASK         (0x3ff)
+
+/* SD Host register reset values */
+#define REG_SD_GCTL_RST         (0x00000300)
+#define REG_SD_CKCR_RST         (0x0)
+#define REG_SD_TMOR_RST         (0xFFFFFF40)
+#define REG_SD_BWDR_RST         (0x0)
+#define REG_SD_BKSR_RST         (0x00000200)
+#define REG_SD_BYCR_RST         (0x00000200)
+#define REG_SD_CMDR_RST         (0x0)
+#define REG_SD_CAGR_RST         (0x0)
+#define REG_SD_RESP_RST         (0x0)
+#define REG_SD_IMKR_RST         (0x0)
+#define REG_SD_MISR_RST         (0x0)
+#define REG_SD_RISR_RST         (0x0)
+#define REG_SD_STAR_RST         (0x00000100)
+#define REG_SD_FWLR_RST         (0x000F0000)
+#define REG_SD_FUNS_RST         (0x0)
+#define REG_SD_DBGC_RST         (0x0)
+#define REG_SD_A12A_RST         (0x0000FFFF)
+#define REG_SD_NTSR_RST         (0x00000001)
+#define REG_SD_SDBG_RST         (0x0)
+#define REG_SD_HWRST_RST        (0x00000001)
+#define REG_SD_DMAC_RST         (0x0)
+#define REG_SD_DLBA_RST         (0x0)
+#define REG_SD_IDST_RST         (0x0)
+#define REG_SD_IDIE_RST         (0x0)
+#define REG_SD_THLDC_RST        (0x0)
+#define REG_SD_DSBD_RST         (0x0)
+#define REG_SD_RES_CRC_RST      (0x0)
+#define REG_SD_DATA_CRC_RST     (0x0)
+#define REG_SD_CRC_STA_RST      (0x0)
+#define REG_SD_FIFO_RST         (0x0)
+
+/* Data transfer descriptor for DMA */
+typedef struct TransferDescriptor {
+    uint32_t status; /* Status flags */
+    uint32_t size;   /* Data buffer size */
+    uint32_t addr;   /* Data buffer address */
+    uint32_t next;   /* Physical address of next descriptor */
+} TransferDescriptor;
+
+/* Data transfer descriptor flags */
+#define DESC_STATUS_HOLD   (1 << 31) /* Set when descriptor is in use by DMA */
+#define DESC_STATUS_ERROR  (1 << 30) /* Set when DMA transfer error occurred */
+#define DESC_STATUS_CHAIN  (1 << 4)  /* Indicates chained descriptor. */
+#define DESC_STATUS_FIRST  (1 << 3)  /* Set on the first descriptor */
+#define DESC_STATUS_LAST   (1 << 2)  /* Set on the last descriptor */
+#define DESC_STATUS_NOIRQ  (1 << 1)  /* Skip raising interrupt after transfer */
+
+#define DESC_SIZE_MASK     (0xfffffffc)
+
+static void aw_h3_sdhost_update_irq(AwH3SDHostState *s)
+{
+    uint32_t irq_en = s->global_ctl & SD_GCTL_INT_ENB;
+    uint32_t irq = irq_en ? s->irq_status & s->irq_mask : 0;
+
+    trace_aw_h3_sdhost_update_irq(irq);
+    qemu_set_irq(s->irq, irq);
+}
+
+static void aw_h3_sdhost_update_transfer_cnt(AwH3SDHostState *s, uint32_t bytes)
+{
+    if (s->transfer_cnt > bytes) {
+        s->transfer_cnt -= bytes;
+    } else {
+        s->transfer_cnt = 0;
+    }
+
+    if (!s->transfer_cnt) {
+        s->irq_status |= SD_RISR_DATA_COMPLETE | SD_RISR_AUTOCMD_DONE;
+    }
+}
+
+static void aw_h3_sdhost_set_inserted(DeviceState *dev, bool inserted)
+{
+    AwH3SDHostState *s = AW_H3_SDHOST(dev);
+
+    trace_aw_h3_sdhost_set_inserted(inserted);
+
+    if (inserted) {
+        s->irq_status |= SD_RISR_CARD_INSERT;
+        s->irq_status &= ~SD_RISR_CARD_REMOVE;
+        s->status |= SD_STAR_CARD_PRESENT;
+    } else {
+        s->irq_status &= ~SD_RISR_CARD_INSERT;
+        s->irq_status |= SD_RISR_CARD_REMOVE;
+        s->status &= ~SD_STAR_CARD_PRESENT;
+    }
+
+    aw_h3_sdhost_update_irq(s);
+}
+
+static void aw_h3_sdhost_send_command(AwH3SDHostState *s)
+{
+    SDRequest request;
+    uint8_t resp[16];
+    int rlen;
+
+    /* Auto clear load flag */
+    s->command &= ~SD_CMDR_LOAD;
+
+    /* Clock change does not actually interact with the SD bus */
+    if (!(s->command & SD_CMDR_CLKCHANGE)) {
+
+        /* Prepare request */
+        request.cmd = s->command & SD_CMDR_CMDID_MASK;
+        request.arg = s->command_arg;
+
+        /* Send request to SD bus */
+        rlen = sdbus_do_command(&s->sdbus, &request, resp);
+        if (rlen < 0) {
+            goto error;
+        }
+
+        /* If the command has a response, store it in the response registers */
+        if ((s->command & SD_CMDR_RESPONSE)) {
+            if (rlen == 0 ||
+               (rlen == 4 && (s->command & SD_CMDR_RESPONSE_LONG))) {
+                goto error;
+            }
+            if (rlen != 4 && rlen != 16) {
+                goto error;
+            }
+            if (rlen == 4) {
+                s->response[0] = ldl_be_p(&resp[0]);
+                s->response[1] = s->response[2] = s->response[3] = 0;
+            } else {
+                s->response[0] = ldl_be_p(&resp[12]);
+                s->response[1] = ldl_be_p(&resp[8]);
+                s->response[2] = ldl_be_p(&resp[4]);
+                s->response[3] = ldl_be_p(&resp[0]);
+            }
+        }
+    }
+
+    /* Set interrupt status bits */
+    s->irq_status |= SD_RISR_CMD_COMPLETE;
+    return;
+
+error:
+    s->irq_status |= SD_RISR_NO_RESPONSE;
+}
+
+static void aw_h3_sdhost_auto_stop(AwH3SDHostState *s)
+{
+    /*
+     * The stop command (CMD12) ensures the SD bus
+     * returns to the transfer state.
+     */
+    if ((s->command & SD_CMDR_AUTOSTOP) && (s->transfer_cnt == 0)) {
+        /* First save current command registers */
+        uint32_t saved_cmd = s->command;
+        uint32_t saved_arg = s->command_arg;
+
+        /* Prepare stop command (CMD12) */
+        s->command &= ~SD_CMDR_CMDID_MASK;
+        s->command |= 12; /* CMD12 */
+        s->command_arg = 0;
+
+        /* Put the command on SD bus */
+        aw_h3_sdhost_send_command(s);
+
+        /* Restore command values */
+        s->command = saved_cmd;
+        s->command_arg = saved_arg;
+    }
+}
+
+static uint32_t aw_h3_sdhost_process_desc(AwH3SDHostState *s,
+                                          hwaddr desc_addr,
+                                          TransferDescriptor *desc,
+                                          bool is_write, uint32_t max_bytes)
+{
+    uint32_t num_done = 0;
+    uint32_t num_bytes = max_bytes;
+    uint8_t buf[1024];
+
+    /* Read descriptor */
+    cpu_physical_memory_read(desc_addr, desc, sizeof(*desc));
+    if (desc->size == 0) {
+        desc->size = 0xffff + 1;
+    }
+    if (desc->size < num_bytes) {
+        num_bytes = desc->size;
+    }
+
+    trace_aw_h3_sdhost_process_desc(desc_addr, desc->size, is_write, max_bytes);
+
+    while (num_done < num_bytes) {
+        /* Try to completely fill the local buffer */
+        uint32_t buf_bytes = num_bytes - num_done;
+        if (buf_bytes > sizeof(buf)) {
+            buf_bytes = sizeof(buf);
+        }
+
+        /* Write to SD bus */
+        if (is_write) {
+            cpu_physical_memory_read((desc->addr & DESC_SIZE_MASK) + num_done,
+                                      buf, buf_bytes);
+
+            for (uint32_t i = 0; i < buf_bytes; i++) {
+                sdbus_write_data(&s->sdbus, buf[i]);
+            }
+
+        /* Read from SD bus */
+        } else {
+            for (uint32_t i = 0; i < buf_bytes; i++) {
+                buf[i] = sdbus_read_data(&s->sdbus);
+            }
+            cpu_physical_memory_write((desc->addr & DESC_SIZE_MASK) + num_done,
+                                       buf, buf_bytes);
+        }
+        num_done += buf_bytes;
+    }
+
+    /* Clear hold flag and flush descriptor */
+    desc->status &= ~DESC_STATUS_HOLD;
+    cpu_physical_memory_write(desc_addr, desc, sizeof(*desc));
+
+    return num_done;
+}
+
+static void aw_h3_sdhost_dma(AwH3SDHostState *s)
+{
+    TransferDescriptor desc;
+    hwaddr desc_addr = s->desc_base;
+    bool is_write = (s->command & SD_CMDR_WRITE);
+    uint32_t bytes_done = 0;
+
+    /* Check if DMA can be performed */
+    if (s->byte_count == 0 || s->block_size == 0 ||
+      !(s->global_ctl & SD_GCTL_DMA_ENB)) {
+        return;
+    }
+
+    /*
+     * For read operations, data must be available on the SD bus
+     * If not, it is an error and we should not act at all
+     */
+    if (!is_write && !sdbus_data_ready(&s->sdbus)) {
+        return;
+    }
+
+    /* Process the DMA descriptors until all data is copied */
+    while (s->byte_count > 0) {
+        bytes_done = aw_h3_sdhost_process_desc(s, desc_addr, &desc,
+                                               is_write, s->byte_count);
+        aw_h3_sdhost_update_transfer_cnt(s, bytes_done);
+
+        if (bytes_done <= s->byte_count) {
+            s->byte_count -= bytes_done;
+        } else {
+            s->byte_count = 0;
+        }
+
+        if (desc.status & DESC_STATUS_LAST) {
+            break;
+        } else {
+            desc_addr = desc.next;
+        }
+    }
+
+    /* Raise IRQ to signal DMA is completed */
+    s->irq_status |= SD_RISR_DATA_COMPLETE | SD_RISR_AUTOCMD_DONE;
+
+    /* Update DMAC bits */
+    if (is_write) {
+        s->dmac_status |= SD_IDST_TRANSMIT_IRQ;
+    } else {
+        s->dmac_status |= (SD_IDST_SUM_RECEIVE_IRQ | SD_IDST_RECEIVE_IRQ);
+    }
+}
+
+static uint64_t aw_h3_sdhost_read(void *opaque, hwaddr offset,
+                                  unsigned size)
+{
+    AwH3SDHostState *s = (AwH3SDHostState *)opaque;
+    uint32_t res = 0;
+
+    switch (offset) {
+    case REG_SD_GCTL:      /* Global Control */
+        res = s->global_ctl;
+        break;
+    case REG_SD_CKCR:      /* Clock Control */
+        res = s->clock_ctl;
+        break;
+    case REG_SD_TMOR:      /* Timeout */
+        res = s->timeout;
+        break;
+    case REG_SD_BWDR:      /* Bus Width */
+        res = s->bus_width;
+        break;
+    case REG_SD_BKSR:      /* Block Size */
+        res = s->block_size;
+        break;
+    case REG_SD_BYCR:      /* Byte Count */
+        res = s->byte_count;
+        break;
+    case REG_SD_CMDR:      /* Command */
+        res = s->command;
+        break;
+    case REG_SD_CAGR:      /* Command Argument */
+        res = s->command_arg;
+        break;
+    case REG_SD_RESP0:     /* Response Zero */
+        res = s->response[0];
+        break;
+    case REG_SD_RESP1:     /* Response One */
+        res = s->response[1];
+        break;
+    case REG_SD_RESP2:     /* Response Two */
+        res = s->response[2];
+        break;
+    case REG_SD_RESP3:     /* Response Three */
+        res = s->response[3];
+        break;
+    case REG_SD_IMKR:      /* Interrupt Mask */
+        res = s->irq_mask;
+        break;
+    case REG_SD_MISR:      /* Masked Interrupt Status */
+        res = s->irq_status & s->irq_mask;
+        break;
+    case REG_SD_RISR:      /* Raw Interrupt Status */
+        res = s->irq_status;
+        break;
+    case REG_SD_STAR:      /* Status */
+        res = s->status;
+        break;
+    case REG_SD_FWLR:      /* FIFO Water Level */
+        res = s->fifo_wlevel;
+        break;
+    case REG_SD_FUNS:      /* FIFO Function Select */
+        res = s->fifo_func_sel;
+        break;
+    case REG_SD_DBGC:      /* Debug Enable */
+        res = s->debug_enable;
+        break;
+    case REG_SD_A12A:      /* Auto command 12 argument */
+        res = s->auto12_arg;
+        break;
+    case REG_SD_NTSR:      /* SD NewTiming Set */
+        res = s->newtiming_set;
+        break;
+    case REG_SD_SDBG:      /* SD newTiming Set Debug */
+        res = s->newtiming_debug;
+        break;
+    case REG_SD_HWRST:     /* Hardware Reset Register */
+        res = s->hardware_rst;
+        break;
+    case REG_SD_DMAC:      /* Internal DMA Controller Control */
+        res = s->dmac;
+        break;
+    case REG_SD_DLBA:      /* Descriptor List Base Address */
+        res = s->desc_base;
+        break;
+    case REG_SD_IDST:      /* Internal DMA Controller Status */
+        res = s->dmac_status;
+        break;
+    case REG_SD_IDIE:      /* Internal DMA Controller Interrupt Enable */
+        res = s->dmac_irq;
+        break;
+    case REG_SD_THLDC:     /* Card Threshold Control */
+        res = s->card_threshold;
+        break;
+    case REG_SD_DSBD:      /* eMMC DDR Start Bit Detection Control */
+        res = s->startbit_detect;
+        break;
+    case REG_SD_RES_CRC:   /* Response CRC from card/eMMC */
+        res = s->response_crc;
+        break;
+    case REG_SD_DATA7_CRC: /* CRC Data 7 from card/eMMC */
+    case REG_SD_DATA6_CRC: /* CRC Data 6 from card/eMMC */
+    case REG_SD_DATA5_CRC: /* CRC Data 5 from card/eMMC */
+    case REG_SD_DATA4_CRC: /* CRC Data 4 from card/eMMC */
+    case REG_SD_DATA3_CRC: /* CRC Data 3 from card/eMMC */
+    case REG_SD_DATA2_CRC: /* CRC Data 2 from card/eMMC */
+    case REG_SD_DATA1_CRC: /* CRC Data 1 from card/eMMC */
+    case REG_SD_DATA0_CRC: /* CRC Data 0 from card/eMMC */
+        res = s->data_crc[((offset - REG_SD_DATA7_CRC) / sizeof(uint32_t))];
+        break;
+    case REG_SD_CRC_STA:   /* CRC status from card/eMMC in write operation */
+        res = s->status_crc;
+        break;
+    case REG_SD_FIFO:      /* Read/Write FIFO */
+        if (sdbus_data_ready(&s->sdbus)) {
+            res = sdbus_read_data(&s->sdbus);
+            res |= sdbus_read_data(&s->sdbus) << 8;
+            res |= sdbus_read_data(&s->sdbus) << 16;
+            res |= sdbus_read_data(&s->sdbus) << 24;
+            aw_h3_sdhost_update_transfer_cnt(s, sizeof(uint32_t));
+            aw_h3_sdhost_auto_stop(s);
+            aw_h3_sdhost_update_irq(s);
+        } else {
+            qemu_log_mask(LOG_GUEST_ERROR, "%s: no data ready on SD bus\n",
+                          __func__);
+        }
+        break;
+    default:
+        qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %"HWADDR_PRIx"\n",
+                      __func__, offset);
+        res = 0;
+        break;
+    }
+
+    trace_aw_h3_sdhost_read(offset, res, size);
+    return res;
+}
+
+static void aw_h3_sdhost_write(void *opaque, hwaddr offset,
+                               uint64_t value, unsigned size)
+{
+    AwH3SDHostState *s = (AwH3SDHostState *)opaque;
+
+    trace_aw_h3_sdhost_write(offset, value, size);
+
+    switch (offset) {
+    case REG_SD_GCTL:      /* Global Control */
+        s->global_ctl = value;
+        s->global_ctl &= ~(SD_GCTL_DMA_RST | SD_GCTL_FIFO_RST |
+                           SD_GCTL_SOFT_RST);
+        aw_h3_sdhost_update_irq(s);
+        break;
+    case REG_SD_CKCR:      /* Clock Control */
+        s->clock_ctl = value;
+        break;
+    case REG_SD_TMOR:      /* Timeout */
+        s->timeout = value;
+        break;
+    case REG_SD_BWDR:      /* Bus Width */
+        s->bus_width = value;
+        break;
+    case REG_SD_BKSR:      /* Block Size */
+        s->block_size = value;
+        break;
+    case REG_SD_BYCR:      /* Byte Count */
+        s->byte_count = value;
+        s->transfer_cnt = value;
+        break;
+    case REG_SD_CMDR:      /* Command */
+        s->command = value;
+        if (value & SD_CMDR_LOAD) {
+            aw_h3_sdhost_send_command(s);
+            aw_h3_sdhost_dma(s);
+            aw_h3_sdhost_auto_stop(s);
+        }
+        aw_h3_sdhost_update_irq(s);
+        break;
+    case REG_SD_CAGR:      /* Command Argument */
+        s->command_arg = value;
+        break;
+    case REG_SD_RESP0:     /* Response Zero */
+        s->response[0] = value;
+        break;
+    case REG_SD_RESP1:     /* Response One */
+        s->response[1] = value;
+        break;
+    case REG_SD_RESP2:     /* Response Two */
+        s->response[2] = value;
+        break;
+    case REG_SD_RESP3:     /* Response Three */
+        s->response[3] = value;
+        break;
+    case REG_SD_IMKR:      /* Interrupt Mask */
+        s->irq_mask = value;
+        aw_h3_sdhost_update_irq(s);
+        break;
+    case REG_SD_MISR:      /* Masked Interrupt Status */
+    case REG_SD_RISR:      /* Raw Interrupt Status */
+        s->irq_status &= ~value;
+        aw_h3_sdhost_update_irq(s);
+        break;
+    case REG_SD_STAR:      /* Status */
+        s->status &= ~value;
+        aw_h3_sdhost_update_irq(s);
+        break;
+    case REG_SD_FWLR:      /* FIFO Water Level */
+        s->fifo_wlevel = value;
+        break;
+    case REG_SD_FUNS:      /* FIFO Function Select */
+        s->fifo_func_sel = value;
+        break;
+    case REG_SD_DBGC:      /* Debug Enable */
+        s->debug_enable = value;
+        break;
+    case REG_SD_A12A:      /* Auto command 12 argument */
+        s->auto12_arg = value;
+        break;
+    case REG_SD_NTSR:      /* SD NewTiming Set */
+        s->newtiming_set = value;
+        break;
+    case REG_SD_SDBG:      /* SD newTiming Set Debug */
+        s->newtiming_debug = value;
+        break;
+    case REG_SD_HWRST:     /* Hardware Reset Register */
+        s->hardware_rst = value;
+        break;
+    case REG_SD_DMAC:      /* Internal DMA Controller Control */
+        s->dmac = value;
+        aw_h3_sdhost_update_irq(s);
+        break;
+    case REG_SD_DLBA:      /* Descriptor List Base Address */
+        s->desc_base = value;
+        break;
+    case REG_SD_IDST:      /* Internal DMA Controller Status */
+        s->dmac_status &= (~SD_IDST_WR_MASK) | (~value & SD_IDST_WR_MASK);
+        aw_h3_sdhost_update_irq(s);
+        break;
+    case REG_SD_IDIE:      /* Internal DMA Controller Interrupt Enable */
+        s->dmac_irq = value;
+        aw_h3_sdhost_update_irq(s);
+        break;
+    case REG_SD_THLDC:     /* Card Threshold Control */
+        s->card_threshold = value;
+        break;
+    case REG_SD_DSBD:      /* eMMC DDR Start Bit Detection Control */
+        s->startbit_detect = value;
+        break;
+    case REG_SD_FIFO:      /* Read/Write FIFO */
+        sdbus_write_data(&s->sdbus, value & 0xff);
+        sdbus_write_data(&s->sdbus, (value >> 8) & 0xff);
+        sdbus_write_data(&s->sdbus, (value >> 16) & 0xff);
+        sdbus_write_data(&s->sdbus, (value >> 24) & 0xff);
+        aw_h3_sdhost_update_transfer_cnt(s, sizeof(uint32_t));
+        aw_h3_sdhost_auto_stop(s);
+        aw_h3_sdhost_update_irq(s);
+        break;
+    case REG_SD_RES_CRC:   /* Response CRC from card/eMMC */
+    case REG_SD_DATA7_CRC: /* CRC Data 7 from card/eMMC */
+    case REG_SD_DATA6_CRC: /* CRC Data 6 from card/eMMC */
+    case REG_SD_DATA5_CRC: /* CRC Data 5 from card/eMMC */
+    case REG_SD_DATA4_CRC: /* CRC Data 4 from card/eMMC */
+    case REG_SD_DATA3_CRC: /* CRC Data 3 from card/eMMC */
+    case REG_SD_DATA2_CRC: /* CRC Data 2 from card/eMMC */
+    case REG_SD_DATA1_CRC: /* CRC Data 1 from card/eMMC */
+    case REG_SD_DATA0_CRC: /* CRC Data 0 from card/eMMC */
+    case REG_SD_CRC_STA:   /* CRC status from card/eMMC in write operation */
+        break;
+    default:
+        qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %"HWADDR_PRIx"\n",
+                      __func__, offset);
+        break;
+    }
+}
+
+static const MemoryRegionOps aw_h3_sdhost_ops = {
+    .read = aw_h3_sdhost_read,
+    .write = aw_h3_sdhost_write,
+    .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static const VMStateDescription vmstate_aw_h3_sdhost = {
+    .name = TYPE_AW_H3_SDHOST,
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .fields = (VMStateField[]) {
+        VMSTATE_UINT32(global_ctl, AwH3SDHostState),
+        VMSTATE_UINT32(clock_ctl, AwH3SDHostState),
+        VMSTATE_UINT32(timeout, AwH3SDHostState),
+        VMSTATE_UINT32(bus_width, AwH3SDHostState),
+        VMSTATE_UINT32(block_size, AwH3SDHostState),
+        VMSTATE_UINT32(byte_count, AwH3SDHostState),
+        VMSTATE_UINT32(transfer_cnt, AwH3SDHostState),
+        VMSTATE_UINT32(command, AwH3SDHostState),
+        VMSTATE_UINT32(command_arg, AwH3SDHostState),
+        VMSTATE_UINT32_ARRAY(response, AwH3SDHostState, 4),
+        VMSTATE_UINT32(irq_mask, AwH3SDHostState),
+        VMSTATE_UINT32(irq_status, AwH3SDHostState),
+        VMSTATE_UINT32(status, AwH3SDHostState),
+        VMSTATE_UINT32(fifo_wlevel, AwH3SDHostState),
+        VMSTATE_UINT32(fifo_func_sel, AwH3SDHostState),
+        VMSTATE_UINT32(debug_enable, AwH3SDHostState),
+        VMSTATE_UINT32(auto12_arg, AwH3SDHostState),
+        VMSTATE_UINT32(newtiming_set, AwH3SDHostState),
+        VMSTATE_UINT32(newtiming_debug, AwH3SDHostState),
+        VMSTATE_UINT32(hardware_rst, AwH3SDHostState),
+        VMSTATE_UINT32(dmac, AwH3SDHostState),
+        VMSTATE_UINT32(desc_base, AwH3SDHostState),
+        VMSTATE_UINT32(dmac_status, AwH3SDHostState),
+        VMSTATE_UINT32(dmac_irq, AwH3SDHostState),
+        VMSTATE_UINT32(card_threshold, AwH3SDHostState),
+        VMSTATE_UINT32(startbit_detect, AwH3SDHostState),
+        VMSTATE_UINT32(response_crc, AwH3SDHostState),
+        VMSTATE_UINT32_ARRAY(data_crc, AwH3SDHostState, 8),
+        VMSTATE_UINT32(status_crc, AwH3SDHostState),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static void aw_h3_sdhost_init(Object *obj)
+{
+    AwH3SDHostState *s = AW_H3_SDHOST(obj);
+
+    qbus_create_inplace(&s->sdbus, sizeof(s->sdbus),
+                        TYPE_AW_H3_SDHOST_BUS, DEVICE(s), "sd-bus");
+
+    memory_region_init_io(&s->iomem, obj, &aw_h3_sdhost_ops, s,
+                          TYPE_AW_H3_SDHOST, AW_H3_SDHOST_REGS_MEM_SIZE);
+    sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->iomem);
+    sysbus_init_irq(SYS_BUS_DEVICE(s), &s->irq);
+}
+
+static void aw_h3_sdhost_reset(DeviceState *dev)
+{
+    AwH3SDHostState *s = AW_H3_SDHOST(dev);
+
+    s->global_ctl = REG_SD_GCTL_RST;
+    s->clock_ctl = REG_SD_CKCR_RST;
+    s->timeout = REG_SD_TMOR_RST;
+    s->bus_width = REG_SD_BWDR_RST;
+    s->block_size = REG_SD_BKSR_RST;
+    s->byte_count = REG_SD_BYCR_RST;
+    s->transfer_cnt = 0;
+
+    s->command = REG_SD_CMDR_RST;
+    s->command_arg = REG_SD_CAGR_RST;
+
+    for (int i = 0; i < sizeof(s->response) / sizeof(s->response[0]); i++) {
+        s->response[i] = REG_SD_RESP_RST;
+    }
+
+    s->irq_mask = REG_SD_IMKR_RST;
+    s->irq_status = REG_SD_RISR_RST;
+    s->status = REG_SD_STAR_RST;
+
+    s->fifo_wlevel = REG_SD_FWLR_RST;
+    s->fifo_func_sel = REG_SD_FUNS_RST;
+    s->debug_enable = REG_SD_DBGC_RST;
+    s->auto12_arg = REG_SD_A12A_RST;
+    s->newtiming_set = REG_SD_NTSR_RST;
+    s->newtiming_debug = REG_SD_SDBG_RST;
+    s->hardware_rst = REG_SD_HWRST_RST;
+    s->dmac = REG_SD_DMAC_RST;
+    s->desc_base = REG_SD_DLBA_RST;
+    s->dmac_status = REG_SD_IDST_RST;
+    s->dmac_irq = REG_SD_IDIE_RST;
+    s->card_threshold = REG_SD_THLDC_RST;
+    s->startbit_detect = REG_SD_DSBD_RST;
+    s->response_crc = REG_SD_RES_CRC_RST;
+
+    for (int i = 0; i < sizeof(s->data_crc) / sizeof(s->data_crc[0]); i++) {
+        s->data_crc[i] = REG_SD_DATA_CRC_RST;
+    }
+
+    s->status_crc = REG_SD_CRC_STA_RST;
+}
+
+static void aw_h3_sdhost_bus_class_init(ObjectClass *klass, void *data)
+{
+    SDBusClass *sbc = SD_BUS_CLASS(klass);
+
+    sbc->set_inserted = aw_h3_sdhost_set_inserted;
+}
+
+static void aw_h3_sdhost_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+
+    dc->reset = aw_h3_sdhost_reset;
+    dc->vmsd = &vmstate_aw_h3_sdhost;
+}
+
+static TypeInfo aw_h3_sdhost_info = {
+    .name          = TYPE_AW_H3_SDHOST,
+    .parent        = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(AwH3SDHostState),
+    .class_init    = aw_h3_sdhost_class_init,
+    .instance_init = aw_h3_sdhost_init,
+};
+
+static const TypeInfo aw_h3_sdhost_bus_info = {
+    .name = TYPE_AW_H3_SDHOST_BUS,
+    .parent = TYPE_SD_BUS,
+    .instance_size = sizeof(SDBus),
+    .class_init = aw_h3_sdhost_bus_class_init,
+};
+
+static void aw_h3_sdhost_register_types(void)
+{
+    type_register_static(&aw_h3_sdhost_info);
+    type_register_static(&aw_h3_sdhost_bus_info);
+}
+
+type_init(aw_h3_sdhost_register_types)
diff --git a/hw/sd/trace-events b/hw/sd/trace-events
index efcff666a2..c672a201b5 100644
--- a/hw/sd/trace-events
+++ b/hw/sd/trace-events
@@ -1,5 +1,12 @@
 # See docs/devel/tracing.txt for syntax documentation.
 
+# allwinner-h3-sdhost.c
+aw_h3_sdhost_set_inserted(bool inserted) "inserted %u"
+aw_h3_sdhost_process_desc(uint64_t desc_addr, uint32_t desc_size, bool is_write, uint32_t max_bytes) "desc_addr 0x%" PRIx64 " desc_size %u is_write %u max_bytes %u"
+aw_h3_sdhost_read(uint64_t offset, uint64_t data, unsigned size) "offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u"
+aw_h3_sdhost_write(uint64_t offset, uint64_t data, unsigned size) "offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u"
+aw_h3_sdhost_update_irq(uint32_t irq) "IRQ bits 0x%x"
+
 # bcm2835_sdhost.c
 bcm2835_sdhost_read(uint64_t offset, uint64_t data, unsigned size) "offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u"
 bcm2835_sdhost_write(uint64_t offset, uint64_t data, unsigned size) "offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u"
diff --git a/include/hw/arm/allwinner-h3.h b/include/hw/arm/allwinner-h3.h
index 33602599eb..7aff4ebbd2 100644
--- a/include/hw/arm/allwinner-h3.h
+++ b/include/hw/arm/allwinner-h3.h
@@ -30,6 +30,7 @@
 #include "hw/misc/allwinner-h3-cpucfg.h"
 #include "hw/misc/allwinner-h3-syscon.h"
 #include "hw/misc/allwinner-h3-sid.h"
+#include "hw/sd/allwinner-h3-sdhost.h"
 #include "target/arm/cpu.h"
 
 #define AW_H3_SRAM_A1_BASE     (0x00000000)
@@ -117,6 +118,7 @@ typedef struct AwH3State {
     AwH3CpuCfgState cpucfg;
     AwH3SysconState syscon;
     AwH3SidState sid;
+    AwH3SDHostState mmc0;
     GICState gic;
     MemoryRegion sram_a1;
     MemoryRegion sram_a2;
diff --git a/include/hw/sd/allwinner-h3-sdhost.h b/include/hw/sd/allwinner-h3-sdhost.h
new file mode 100644
index 0000000000..6c898a3c84
--- /dev/null
+++ b/include/hw/sd/allwinner-h3-sdhost.h
@@ -0,0 +1,73 @@
+/*
+ * Allwinner H3 SD Host 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/>.
+ */
+
+#ifndef ALLWINNER_H3_SDHOST_H
+#define ALLWINNER_H3_SDHOST_H
+
+#include "hw/sysbus.h"
+#include "hw/sd/sd.h"
+
+#define AW_H3_SDHOST_REGS_MEM_SIZE  (1024)
+
+#define TYPE_AW_H3_SDHOST "allwinner-h3-sdhost"
+#define AW_H3_SDHOST(obj) \
+        OBJECT_CHECK(AwH3SDHostState, (obj), TYPE_AW_H3_SDHOST)
+
+typedef struct {
+    SysBusDevice busdev;
+    SDBus sdbus;
+    MemoryRegion iomem;
+
+    uint32_t global_ctl;
+    uint32_t clock_ctl;
+    uint32_t timeout;
+    uint32_t bus_width;
+    uint32_t block_size;
+    uint32_t byte_count;
+    uint32_t transfer_cnt;
+
+    uint32_t command;
+    uint32_t command_arg;
+    uint32_t response[4];
+
+    uint32_t irq_mask;
+    uint32_t irq_status;
+    uint32_t status;
+
+    uint32_t fifo_wlevel;
+    uint32_t fifo_func_sel;
+    uint32_t debug_enable;
+    uint32_t auto12_arg;
+    uint32_t newtiming_set;
+    uint32_t newtiming_debug;
+    uint32_t hardware_rst;
+    uint32_t dmac;
+    uint32_t desc_base;
+    uint32_t dmac_status;
+    uint32_t dmac_irq;
+    uint32_t card_threshold;
+    uint32_t startbit_detect;
+    uint32_t response_crc;
+    uint32_t data_crc[8];
+    uint32_t status_crc;
+
+    qemu_irq irq;
+} AwH3SDHostState;
+
+#endif
-- 
2.17.1



^ permalink raw reply	[flat|nested] 33+ messages in thread

* [PATCH 10/10] arm: allwinner-h3: add EMAC ethernet device
  2019-12-02 21:09 [PATCH 00/10] Add Allwinner H3 SoC and Orange Pi PC Machine Niek Linnenbank
                   ` (8 preceding siblings ...)
  2019-12-02 21:09 ` [PATCH 09/10] arm: allwinner-h3: add SD/MMC host controller Niek Linnenbank
@ 2019-12-02 21:09 ` Niek Linnenbank
  2019-12-03  9:33   ` KONRAD Frederic
  2019-12-03  8:47 ` [PATCH 00/10] Add Allwinner H3 SoC and Orange Pi PC Machine Philippe Mathieu-Daudé
  2019-12-03  9:02 ` Philippe Mathieu-Daudé
  11 siblings, 1 reply; 33+ messages in thread
From: Niek Linnenbank @ 2019-12-02 21:09 UTC (permalink / raw)
  To: qemu-devel; +Cc: b.galvani, peter.maydell, Niek Linnenbank, qemu-arm

The Allwinner H3 System on Chip includes an Ethernet MAC (EMAC)
which provides 10M/100M/1000M Ethernet connectivity. This commit
adds support for the Allwinner H3 EMAC, including emulation for
the following functionality:

 * DMA transfers
 * MII interface
 * Transmit CRC calculation

Signed-off-by: Niek Linnenbank <nieklinnenbank@gmail.com>
---
 hw/arm/Kconfig                     |   1 +
 hw/arm/allwinner-h3.c              |  17 +
 hw/arm/orangepi.c                  |   7 +
 hw/net/Kconfig                     |   3 +
 hw/net/Makefile.objs               |   1 +
 hw/net/allwinner-h3-emac.c         | 786 +++++++++++++++++++++++++++++
 hw/net/trace-events                |  10 +
 include/hw/arm/allwinner-h3.h      |   2 +
 include/hw/net/allwinner-h3-emac.h |  69 +++
 9 files changed, 896 insertions(+)
 create mode 100644 hw/net/allwinner-h3-emac.c
 create mode 100644 include/hw/net/allwinner-h3-emac.h

diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig
index ebf8d2325f..551cff3442 100644
--- a/hw/arm/Kconfig
+++ b/hw/arm/Kconfig
@@ -294,6 +294,7 @@ config ALLWINNER_A10
 config ALLWINNER_H3
     bool
     select ALLWINNER_A10_PIT
+    select ALLWINNER_H3_EMAC
     select SERIAL
     select ARM_TIMER
     select ARM_GIC
diff --git a/hw/arm/allwinner-h3.c b/hw/arm/allwinner-h3.c
index c2972caf88..274b8548c0 100644
--- a/hw/arm/allwinner-h3.c
+++ b/hw/arm/allwinner-h3.c
@@ -53,6 +53,9 @@ static void aw_h3_init(Object *obj)
 
     sysbus_init_child_obj(obj, "mmc0", &s->mmc0, sizeof(s->mmc0),
                           TYPE_AW_H3_SDHOST);
+
+    sysbus_init_child_obj(obj, "emac", &s->emac, sizeof(s->emac),
+                          TYPE_AW_H3_EMAC);
 }
 
 static void aw_h3_realize(DeviceState *dev, Error **errp)
@@ -237,6 +240,20 @@ static void aw_h3_realize(DeviceState *dev, Error **errp)
         return;
     }
 
+    /* EMAC */
+    if (nd_table[0].used) {
+        qemu_check_nic_model(&nd_table[0], TYPE_AW_H3_EMAC);
+        qdev_set_nic_properties(DEVICE(&s->emac), &nd_table[0]);
+    }
+    object_property_set_bool(OBJECT(&s->emac), true, "realized", &err);
+    if (err != NULL) {
+        error_propagate(errp, err);
+        return;
+    }
+    sysbusdev = SYS_BUS_DEVICE(&s->emac);
+    sysbus_mmio_map(sysbusdev, 0, AW_H3_EMAC_BASE);
+    sysbus_connect_irq(sysbusdev, 0, s->irq[AW_H3_GIC_SPI_EMAC]);
+
     /* Universal Serial Bus */
     sysbus_create_simple(TYPE_AW_H3_EHCI, AW_H3_EHCI0_BASE,
                          s->irq[AW_H3_GIC_SPI_EHCI0]);
diff --git a/hw/arm/orangepi.c b/hw/arm/orangepi.c
index dee3efaf08..8a61eb0e69 100644
--- a/hw/arm/orangepi.c
+++ b/hw/arm/orangepi.c
@@ -61,6 +61,13 @@ static void orangepi_init(MachineState *machine)
         exit(1);
     }
 
+    /* Setup EMAC properties */
+    object_property_set_int(OBJECT(&s->h3->emac), 1, "phy-addr", &err);
+    if (err != NULL) {
+        error_reportf_err(err, "Couldn't set phy address: ");
+        exit(1);
+    }
+
     /* Mark H3 object realized */
     object_property_set_bool(OBJECT(s->h3), true, "realized", &err);
     if (err != NULL) {
diff --git a/hw/net/Kconfig b/hw/net/Kconfig
index 3856417d42..36d3923992 100644
--- a/hw/net/Kconfig
+++ b/hw/net/Kconfig
@@ -74,6 +74,9 @@ config MIPSNET
 config ALLWINNER_EMAC
     bool
 
+config ALLWINNER_H3_EMAC
+    bool
+
 config IMX_FEC
     bool
 
diff --git a/hw/net/Makefile.objs b/hw/net/Makefile.objs
index 7907d2c199..5548deb07a 100644
--- a/hw/net/Makefile.objs
+++ b/hw/net/Makefile.objs
@@ -23,6 +23,7 @@ common-obj-$(CONFIG_XGMAC) += xgmac.o
 common-obj-$(CONFIG_MIPSNET) += mipsnet.o
 common-obj-$(CONFIG_XILINX_AXI) += xilinx_axienet.o
 common-obj-$(CONFIG_ALLWINNER_EMAC) += allwinner_emac.o
+common-obj-$(CONFIG_ALLWINNER_H3_EMAC) += allwinner-h3-emac.o
 common-obj-$(CONFIG_IMX_FEC) += imx_fec.o
 
 common-obj-$(CONFIG_CADENCE) += cadence_gem.o
diff --git a/hw/net/allwinner-h3-emac.c b/hw/net/allwinner-h3-emac.c
new file mode 100644
index 0000000000..37f6f44406
--- /dev/null
+++ b/hw/net/allwinner-h3-emac.c
@@ -0,0 +1,786 @@
+/*
+ * Allwinner H3 EMAC 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 "hw/sysbus.h"
+#include "migration/vmstate.h"
+#include "net/net.h"
+#include "hw/irq.h"
+#include "hw/qdev-properties.h"
+#include "qemu/log.h"
+#include "trace.h"
+#include "net/checksum.h"
+#include "qemu/module.h"
+#include "exec/cpu-common.h"
+#include "hw/net/allwinner-h3-emac.h"
+
+/* EMAC register offsets */
+#define REG_BASIC_CTL_0        (0x0000) /* Basic Control 0 */
+#define REG_BASIC_CTL_1        (0x0004) /* Basic Control 1 */
+#define REG_INT_STA            (0x0008) /* Interrupt Status */
+#define REG_INT_EN             (0x000C) /* Interrupt Enable */
+#define REG_TX_CTL_0           (0x0010) /* Transmit Control 0 */
+#define REG_TX_CTL_1           (0x0014) /* Transmit Control 1 */
+#define REG_TX_FLOW_CTL        (0x001C) /* Transmit Flow Control */
+#define REG_TX_DMA_DESC_LIST   (0x0020) /* Transmit Descriptor List Address */
+#define REG_RX_CTL_0           (0x0024) /* Receive Control 0 */
+#define REG_RX_CTL_1           (0x0028) /* Receive Control 1 */
+#define REG_RX_DMA_DESC_LIST   (0x0034) /* Receive Descriptor List Address */
+#define REG_FRM_FLT            (0x0038) /* Receive Frame Filter */
+#define REG_RX_HASH_0          (0x0040) /* Receive Hash Table 0 */
+#define REG_RX_HASH_1          (0x0044) /* Receive Hash Table 1 */
+#define REG_MII_CMD            (0x0048) /* Management Interface Command */
+#define REG_MII_DATA           (0x004C) /* Management Interface Data */
+#define REG_ADDR_HIGH          (0x0050) /* MAC Address High */
+#define REG_ADDR_LOW           (0x0054) /* MAC Address Low */
+#define REG_TX_DMA_STA         (0x00B0) /* Transmit DMA Status */
+#define REG_TX_CUR_DESC        (0x00B4) /* Transmit Current Descriptor */
+#define REG_TX_CUR_BUF         (0x00B8) /* Transmit Current Buffer */
+#define REG_RX_DMA_STA         (0x00C0) /* Receive DMA Status */
+#define REG_RX_CUR_DESC        (0x00C4) /* Receive Current Descriptor */
+#define REG_RX_CUR_BUF         (0x00C8) /* Receive Current Buffer */
+#define REG_RGMII_STA          (0x00D0) /* RGMII Status */
+
+/* EMAC register flags */
+#define BASIC_CTL0_100Mbps     (0b11 << 2)
+#define BASIC_CTL0_FD          (1 << 0)
+#define BASIC_CTL1_SOFTRST     (1 << 0)
+
+#define INT_STA_RGMII_LINK     (1 << 16)
+#define INT_STA_RX_EARLY       (1 << 13)
+#define INT_STA_RX_OVERFLOW    (1 << 12)
+#define INT_STA_RX_TIMEOUT     (1 << 11)
+#define INT_STA_RX_DMA_STOP    (1 << 10)
+#define INT_STA_RX_BUF_UA      (1 << 9)
+#define INT_STA_RX             (1 << 8)
+#define INT_STA_TX_EARLY       (1 << 5)
+#define INT_STA_TX_UNDERFLOW   (1 << 4)
+#define INT_STA_TX_TIMEOUT     (1 << 3)
+#define INT_STA_TX_BUF_UA      (1 << 2)
+#define INT_STA_TX_DMA_STOP    (1 << 1)
+#define INT_STA_TX             (1 << 0)
+
+#define INT_EN_RX_EARLY        (1 << 13)
+#define INT_EN_RX_OVERFLOW     (1 << 12)
+#define INT_EN_RX_TIMEOUT      (1 << 11)
+#define INT_EN_RX_DMA_STOP     (1 << 10)
+#define INT_EN_RX_BUF_UA       (1 << 9)
+#define INT_EN_RX              (1 << 8)
+#define INT_EN_TX_EARLY        (1 << 5)
+#define INT_EN_TX_UNDERFLOW    (1 << 4)
+#define INT_EN_TX_TIMEOUT      (1 << 3)
+#define INT_EN_TX_BUF_UA       (1 << 2)
+#define INT_EN_TX_DMA_STOP     (1 << 1)
+#define INT_EN_TX              (1 << 0)
+
+#define TX_CTL0_TX_EN          (1 << 31)
+#define TX_CTL1_TX_DMA_START   (1 << 31)
+#define TX_CTL1_TX_DMA_EN      (1 << 30)
+#define TX_CTL1_TX_FLUSH       (1 << 0)
+
+#define RX_CTL0_RX_EN          (1 << 31)
+#define RX_CTL0_STRIP_FCS      (1 << 28)
+#define RX_CTL0_CRC_IPV4       (1 << 27)
+
+#define RX_CTL1_RX_DMA_START   (1 << 31)
+#define RX_CTL1_RX_DMA_EN      (1 << 30)
+#define RX_CTL1_RX_MD          (1 << 1)
+
+#define RX_FRM_FLT_DIS_ADDR    (1 << 31)
+
+#define MII_CMD_PHY_ADDR_SHIFT (12)
+#define MII_CMD_PHY_ADDR_MASK  (0xf000)
+#define MII_CMD_PHY_REG_SHIFT  (4)
+#define MII_CMD_PHY_REG_MASK   (0xf0)
+#define MII_CMD_PHY_RW         (1 << 1)
+#define MII_CMD_PHY_BUSY       (1 << 0)
+
+#define TX_DMA_STA_STOP        (0b000)
+#define TX_DMA_STA_RUN_FETCH   (0b001)
+#define TX_DMA_STA_WAIT_STA    (0b010)
+
+#define RX_DMA_STA_STOP        (0b000)
+#define RX_DMA_STA_RUN_FETCH   (0b001)
+#define RX_DMA_STA_WAIT_FRM    (0b011)
+
+#define RGMII_LINK_UP          (1 << 3)
+#define RGMII_FD               (1 << 0)
+
+/* EMAC register reset values */
+#define REG_BASIC_CTL_1_RST    (0x08000000)
+
+/* EMAC constants */
+#define AW_H3_EMAC_MIN_PKT_SZ  (64)
+
+/* Transmit/receive frame descriptor */
+typedef struct FrameDescriptor {
+    uint32_t status;
+    uint32_t status2;
+    uint32_t addr;
+    uint32_t next;
+} FrameDescriptor;
+
+/* Frame descriptor flags */
+#define DESC_STATUS_CTL                 (1 << 31)
+#define DESC_STATUS2_BUF_SIZE_MASK      (0x7ff)
+
+/* Transmit frame descriptor flags */
+#define TX_DESC_STATUS_LENGTH_ERR       (1 << 14)
+#define TX_DESC_STATUS2_FIRST_DESC      (1 << 29)
+#define TX_DESC_STATUS2_LAST_DESC       (1 << 30)
+#define TX_DESC_STATUS2_CHECKSUM_MASK   (0x3 << 27)
+
+/* Receive frame descriptor flags */
+#define RX_DESC_STATUS_FIRST_DESC       (1 << 9)
+#define RX_DESC_STATUS_LAST_DESC        (1 << 8)
+#define RX_DESC_STATUS_FRM_LEN_MASK     (0x3fff0000)
+#define RX_DESC_STATUS_FRM_LEN_SHIFT    (16)
+#define RX_DESC_STATUS_NO_BUF           (1 << 14)
+#define RX_DESC_STATUS_HEADER_ERR       (1 << 7)
+#define RX_DESC_STATUS_LENGTH_ERR       (1 << 4)
+#define RX_DESC_STATUS_CRC_ERR          (1 << 1)
+#define RX_DESC_STATUS_PAYLOAD_ERR      (1 << 0)
+#define RX_DESC_STATUS2_RX_INT_CTL      (1 << 31)
+
+/* MII register offsets */
+#define MII_REG_CR                      (0x0)
+#define MII_REG_ST                      (0x1)
+#define MII_REG_ID_HIGH                 (0x2)
+#define MII_REG_ID_LOW                  (0x3)
+
+/* MII register flags */
+#define MII_REG_CR_RESET                (1 << 15)
+#define MII_REG_CR_POWERDOWN            (1 << 11)
+#define MII_REG_CR_10Mbit               (0)
+#define MII_REG_CR_100Mbit              (1 << 13)
+#define MII_REG_CR_1000Mbit             (1 << 6)
+#define MII_REG_CR_AUTO_NEG             (1 << 12)
+#define MII_REG_CR_AUTO_NEG_RESTART     (1 << 9)
+#define MII_REG_CR_FULLDUPLEX           (1 << 8)
+
+#define MII_REG_ST_100BASE_T4           (1 << 15)
+#define MII_REG_ST_100BASE_X_FD         (1 << 14)
+#define MII_REG_ST_100BASE_X_HD         (1 << 13)
+#define MII_REG_ST_10_FD                (1 << 12)
+#define MII_REG_ST_10_HD                (1 << 11)
+#define MII_REG_ST_100BASE_T2_FD        (1 << 10)
+#define MII_REG_ST_100BASE_T2_HD        (1 << 9)
+#define MII_REG_ST_AUTONEG_COMPLETE     (1 << 5)
+#define MII_REG_ST_AUTONEG_AVAIL        (1 << 3)
+#define MII_REG_ST_LINK_UP              (1 << 2)
+
+/* MII constants */
+#define MII_PHY_ID_HIGH                 (0x0044)
+#define MII_PHY_ID_LOW                  (0x1400)
+
+static void aw_h3_emac_mii_set_link(AwH3EmacState *s, bool link_active)
+{
+    if (link_active) {
+        s->mii_st |= MII_REG_ST_LINK_UP;
+    } else {
+        s->mii_st &= ~MII_REG_ST_LINK_UP;
+    }
+}
+
+static void aw_h3_emac_mii_reset(AwH3EmacState *s, bool link_active)
+{
+    s->mii_cr = MII_REG_CR_100Mbit | MII_REG_CR_AUTO_NEG |
+                MII_REG_CR_FULLDUPLEX;
+    s->mii_st = MII_REG_ST_100BASE_T4 | MII_REG_ST_100BASE_X_FD |
+                MII_REG_ST_100BASE_X_HD | MII_REG_ST_10_FD | MII_REG_ST_10_HD |
+                MII_REG_ST_100BASE_T2_FD | MII_REG_ST_100BASE_T2_HD |
+                MII_REG_ST_AUTONEG_COMPLETE | MII_REG_ST_AUTONEG_AVAIL;
+
+    aw_h3_emac_mii_set_link(s, link_active);
+}
+
+static void aw_h3_emac_mii_cmd(AwH3EmacState *s)
+{
+    uint8_t addr, reg;
+
+    addr = (s->mii_cmd & MII_CMD_PHY_ADDR_MASK) >> MII_CMD_PHY_ADDR_SHIFT;
+    reg = (s->mii_cmd & MII_CMD_PHY_REG_MASK) >> MII_CMD_PHY_REG_SHIFT;
+
+    if (addr != s->mii_phy_addr) {
+        return;
+    }
+
+    /* Read or write a PHY register? */
+    if (s->mii_cmd & MII_CMD_PHY_RW) {
+        trace_aw_h3_emac_mii_write_reg(reg, s->mii_data);
+
+        switch (reg) {
+        case MII_REG_CR:
+            if (s->mii_data & MII_REG_CR_RESET) {
+                aw_h3_emac_mii_reset(s, s->mii_st & MII_REG_ST_LINK_UP);
+            } else {
+                s->mii_cr = s->mii_data & ~(MII_REG_CR_RESET |
+                                            MII_REG_CR_AUTO_NEG_RESTART);
+            }
+            break;
+        default:
+            qemu_log_mask(LOG_UNIMP, "allwinner-h3-emac: write access to "
+                                     "unknown MII register 0x%x\n", reg);
+            break;
+        }
+    } else {
+        switch (reg) {
+        case MII_REG_CR:
+            s->mii_data = s->mii_cr;
+            break;
+        case MII_REG_ST:
+            s->mii_data = s->mii_st;
+            break;
+        case MII_REG_ID_HIGH:
+            s->mii_data = MII_PHY_ID_HIGH;
+            break;
+        case MII_REG_ID_LOW:
+            s->mii_data = MII_PHY_ID_LOW;
+            break;
+        default:
+            qemu_log_mask(LOG_UNIMP, "allwinner-h3-emac: read access to "
+                                     "unknown MII register 0x%x\n", reg);
+            s->mii_data = 0;
+            break;
+        }
+
+        trace_aw_h3_emac_mii_read_reg(reg, s->mii_data);
+    }
+}
+
+static void aw_h3_emac_update_irq(AwH3EmacState *s)
+{
+    qemu_set_irq(s->irq, (s->int_sta & s->int_en) != 0);
+}
+
+static uint32_t aw_h3_emac_next_desc(FrameDescriptor *desc, size_t min_size)
+{
+    uint32_t paddr = desc->next;
+
+    cpu_physical_memory_read(paddr, desc, sizeof(*desc));
+
+    if ((desc->status & DESC_STATUS_CTL) &&
+        (desc->status2 & DESC_STATUS2_BUF_SIZE_MASK) >= min_size) {
+        return paddr;
+    } else {
+        return 0;
+    }
+}
+
+static uint32_t aw_h3_emac_get_desc(FrameDescriptor *desc, uint32_t start_addr,
+                                    size_t min_size)
+{
+    uint32_t desc_addr = start_addr;
+
+    /* Note that the list is a cycle. Last entry points back to the head. */
+    while (desc_addr != 0) {
+        cpu_physical_memory_read(desc_addr, desc, sizeof(*desc));
+
+        if ((desc->status & DESC_STATUS_CTL) &&
+            (desc->status2 & DESC_STATUS2_BUF_SIZE_MASK) >= min_size) {
+            return desc_addr;
+        } else if (desc->next == start_addr) {
+            break;
+        } else {
+            desc_addr = desc->next;
+        }
+    }
+
+    return 0;
+}
+
+static uint32_t aw_h3_emac_get_rx_desc(AwH3EmacState *s, FrameDescriptor *desc,
+                                       size_t min_size)
+{
+    return aw_h3_emac_get_desc(desc, s->rx_desc_curr, min_size);
+}
+
+static uint32_t aw_h3_emac_get_tx_desc(AwH3EmacState *s, FrameDescriptor *desc,
+                                       size_t min_size)
+{
+    return aw_h3_emac_get_desc(desc, s->tx_desc_head, min_size);
+}
+
+static void aw_h3_emac_flush_desc(FrameDescriptor *desc, uint32_t phys_addr)
+{
+    cpu_physical_memory_write(phys_addr, desc, sizeof(*desc));
+}
+
+static int aw_h3_emac_can_receive(NetClientState *nc)
+{
+    AwH3EmacState *s = qemu_get_nic_opaque(nc);
+    FrameDescriptor desc;
+
+    return (s->rx_ctl0 & RX_CTL0_RX_EN) &&
+           (aw_h3_emac_get_rx_desc(s, &desc, 0) != 0);
+}
+
+static ssize_t aw_h3_emac_receive(NetClientState *nc, const uint8_t *buf,
+                                  size_t size)
+{
+    AwH3EmacState *s = qemu_get_nic_opaque(nc);
+    FrameDescriptor desc;
+    size_t bytes_left = size;
+    size_t desc_bytes = 0;
+    size_t pad_fcs_size = 4;
+    size_t padding = 0;
+
+    if (!(s->rx_ctl0 & RX_CTL0_RX_EN)) {
+        return -1;
+    }
+
+    s->rx_desc_curr = aw_h3_emac_get_rx_desc(s, &desc, AW_H3_EMAC_MIN_PKT_SZ);
+    if (!s->rx_desc_curr) {
+        s->int_sta |= INT_STA_RX_BUF_UA;
+    }
+
+    /* Keep filling RX descriptors until the whole frame is written */
+    while (s->rx_desc_curr && bytes_left > 0) {
+        desc.status &= ~DESC_STATUS_CTL;
+        desc.status &= ~RX_DESC_STATUS_FRM_LEN_MASK;
+
+        if (bytes_left == size) {
+            desc.status |= RX_DESC_STATUS_FIRST_DESC;
+        }
+
+        if ((desc.status2 & DESC_STATUS2_BUF_SIZE_MASK) <
+            (bytes_left + pad_fcs_size)) {
+            desc_bytes = desc.status2 & DESC_STATUS2_BUF_SIZE_MASK;
+            desc.status |= desc_bytes << RX_DESC_STATUS_FRM_LEN_SHIFT;
+        } else {
+            padding = pad_fcs_size;
+            if (bytes_left < AW_H3_EMAC_MIN_PKT_SZ) {
+                padding += (AW_H3_EMAC_MIN_PKT_SZ - bytes_left);
+            }
+
+            desc_bytes = (bytes_left);
+            desc.status |= RX_DESC_STATUS_LAST_DESC;
+            desc.status |= (bytes_left + padding)
+                            << RX_DESC_STATUS_FRM_LEN_SHIFT;
+        }
+
+        cpu_physical_memory_write(desc.addr, buf, desc_bytes);
+        aw_h3_emac_flush_desc(&desc, s->rx_desc_curr);
+        trace_aw_h3_emac_receive(s->rx_desc_curr, desc.addr, desc_bytes);
+
+        /* Check if frame needs to raise the receive interrupt */
+        if (!(desc.status2 & RX_DESC_STATUS2_RX_INT_CTL)) {
+            s->int_sta |= INT_STA_RX;
+        }
+
+        /* Increment variables */
+        buf += desc_bytes;
+        bytes_left -= desc_bytes;
+
+        /* Move to the next descriptor */
+        s->rx_desc_curr = aw_h3_emac_next_desc(&desc, 64);
+        if (!s->rx_desc_curr) {
+            /* Not enough buffer space available */
+            s->int_sta |= INT_STA_RX_BUF_UA;
+            s->rx_desc_curr = s->rx_desc_head;
+            break;
+        }
+    }
+
+    /* Report receive DMA is finished */
+    s->rx_ctl1 &= ~RX_CTL1_RX_DMA_START;
+    aw_h3_emac_update_irq(s);
+
+    return size;
+}
+
+static void aw_h3_emac_transmit(AwH3EmacState *s)
+{
+    NetClientState *nc = qemu_get_queue(s->nic);
+    FrameDescriptor desc;
+    size_t bytes = 0;
+    size_t packet_bytes = 0;
+    size_t transmitted = 0;
+    static uint8_t packet_buf[2048];
+
+    s->tx_desc_curr = aw_h3_emac_get_tx_desc(s, &desc, 0);
+
+    /* Read all transmit descriptors */
+    while (s->tx_desc_curr != 0) {
+
+        /* Read from physical memory into packet buffer */
+        bytes = desc.status2 & DESC_STATUS2_BUF_SIZE_MASK;
+        if (bytes + packet_bytes > sizeof(packet_buf)) {
+            desc.status |= TX_DESC_STATUS_LENGTH_ERR;
+            break;
+        }
+        cpu_physical_memory_read(desc.addr, packet_buf + packet_bytes, bytes);
+        packet_bytes += bytes;
+        desc.status &= ~DESC_STATUS_CTL;
+        aw_h3_emac_flush_desc(&desc, s->tx_desc_curr);
+
+        /* After the last descriptor, send the packet */
+        if (desc.status2 & TX_DESC_STATUS2_LAST_DESC) {
+            if (desc.status2 & TX_DESC_STATUS2_CHECKSUM_MASK) {
+                net_checksum_calculate(packet_buf, packet_bytes);
+            }
+
+            qemu_send_packet(nc, packet_buf, packet_bytes);
+            trace_aw_h3_emac_transmit(s->tx_desc_curr, desc.addr, bytes);
+
+            packet_bytes = 0;
+            transmitted++;
+        }
+        s->tx_desc_curr = aw_h3_emac_next_desc(&desc, 0);
+    }
+
+    /* Raise transmit completed interrupt */
+    if (transmitted > 0) {
+        s->int_sta |= INT_STA_TX;
+        s->tx_ctl1 &= ~TX_CTL1_TX_DMA_START;
+        aw_h3_emac_update_irq(s);
+    }
+}
+
+static void aw_h3_emac_reset(DeviceState *dev)
+{
+    AwH3EmacState *s = AW_H3_EMAC(dev);
+    NetClientState *nc = qemu_get_queue(s->nic);
+
+    trace_aw_h3_emac_reset();
+
+    s->mii_cmd = 0;
+    s->mii_data = 0;
+    s->basic_ctl0 = 0;
+    s->basic_ctl1 = 0;
+    s->int_en = 0;
+    s->int_sta = 0;
+    s->frm_flt = 0;
+    s->rx_ctl0 = 0;
+    s->rx_ctl1 = RX_CTL1_RX_MD;
+    s->rx_desc_head = 0;
+    s->rx_desc_curr = 0;
+    s->tx_ctl0 = 0;
+    s->tx_ctl1 = 0;
+    s->tx_desc_head = 0;
+    s->tx_desc_curr = 0;
+    s->tx_flowctl = 0;
+
+    aw_h3_emac_mii_reset(s, !nc->link_down);
+}
+
+static uint64_t aw_h3_emac_read(void *opaque, hwaddr offset, unsigned size)
+{
+    AwH3EmacState *s = opaque;
+    uint64_t value = 0;
+    FrameDescriptor desc;
+
+    switch (offset) {
+    case REG_BASIC_CTL_0:       /* Basic Control 0 */
+        value = s->basic_ctl0;
+        break;
+    case REG_BASIC_CTL_1:       /* Basic Control 1 */
+        value = s->basic_ctl1;
+        break;
+    case REG_INT_STA:           /* Interrupt Status */
+        value = s->int_sta;
+        break;
+    case REG_INT_EN:            /* Interupt Enable */
+        value = s->int_en;
+        break;
+    case REG_TX_CTL_0:          /* Transmit Control 0 */
+        value = s->tx_ctl0;
+        break;
+    case REG_TX_CTL_1:          /* Transmit Control 1 */
+        value = s->tx_ctl1;
+        break;
+    case REG_TX_FLOW_CTL:       /* Transmit Flow Control */
+        value = s->tx_flowctl;
+        break;
+    case REG_TX_DMA_DESC_LIST:  /* Transmit Descriptor List Address */
+        value = s->tx_desc_head;
+        break;
+    case REG_RX_CTL_0:          /* Receive Control 0 */
+        value = s->rx_ctl0;
+        break;
+    case REG_RX_CTL_1:          /* Receive Control 1 */
+        value = s->rx_ctl1;
+        break;
+    case REG_RX_DMA_DESC_LIST:  /* Receive Descriptor List Address */
+        value = s->rx_desc_head;
+        break;
+    case REG_FRM_FLT:           /* Receive Frame Filter */
+        value = s->frm_flt;
+        break;
+    case REG_RX_HASH_0:         /* Receive Hash Table 0 */
+    case REG_RX_HASH_1:         /* Receive Hash Table 1 */
+        break;
+    case REG_MII_CMD:           /* Management Interface Command */
+        value = s->mii_cmd;
+        break;
+    case REG_MII_DATA:          /* Management Interface Data */
+        value = s->mii_data;
+        break;
+    case REG_ADDR_HIGH:         /* MAC Address High */
+        value = *(((uint32_t *) (s->conf.macaddr.a)) + 1);
+        break;
+    case REG_ADDR_LOW:          /* MAC Address Low */
+        value = *(uint32_t *) (s->conf.macaddr.a);
+        break;
+    case REG_TX_DMA_STA:        /* Transmit DMA Status */
+        break;
+    case REG_TX_CUR_DESC:       /* Transmit Current Descriptor */
+        value = s->tx_desc_curr;
+        break;
+    case REG_TX_CUR_BUF:        /* Transmit Current Buffer */
+        if (s->tx_desc_curr != 0) {
+            cpu_physical_memory_read(s->tx_desc_curr, &desc, sizeof(desc));
+            value = desc.addr;
+        } else {
+            value = 0;
+        }
+        break;
+    case REG_RX_DMA_STA:        /* Receive DMA Status */
+        break;
+    case REG_RX_CUR_DESC:       /* Receive Current Descriptor */
+        value = s->rx_desc_curr;
+        break;
+    case REG_RX_CUR_BUF:        /* Receive Current Buffer */
+        if (s->rx_desc_curr != 0) {
+            cpu_physical_memory_read(s->rx_desc_curr, &desc, sizeof(desc));
+            value = desc.addr;
+        } else {
+            value = 0;
+        }
+        break;
+    default:
+        qemu_log_mask(LOG_UNIMP, "allwinner-h3-emac: read access to unknown "
+                                 "EMAC register 0x" TARGET_FMT_plx "\n",
+                                  offset);
+    }
+
+    trace_aw_h3_emac_read(offset, value);
+    return value;
+}
+
+static void aw_h3_emac_write(void *opaque, hwaddr offset, uint64_t value,
+                             unsigned size)
+{
+    AwH3EmacState *s = opaque;
+    NetClientState *nc = qemu_get_queue(s->nic);
+
+    trace_aw_h3_emac_write(offset, value);
+
+    switch (offset) {
+    case REG_BASIC_CTL_0:       /* Basic Control 0 */
+        s->basic_ctl0 = value;
+        break;
+    case REG_BASIC_CTL_1:       /* Basic Control 1 */
+        if (value & BASIC_CTL1_SOFTRST) {
+            aw_h3_emac_reset(DEVICE(s));
+            value &= ~BASIC_CTL1_SOFTRST;
+        }
+        s->basic_ctl1 = value;
+        if (aw_h3_emac_can_receive(nc)) {
+            qemu_flush_queued_packets(nc);
+        }
+        break;
+    case REG_INT_STA:           /* Interrupt Status */
+        s->int_sta &= ~value;
+        aw_h3_emac_update_irq(s);
+        break;
+    case REG_INT_EN:            /* Interrupt Enable */
+        s->int_en = value;
+        aw_h3_emac_update_irq(s);
+        break;
+    case REG_TX_CTL_0:          /* Transmit Control 0 */
+        s->tx_ctl0 = value;
+        break;
+    case REG_TX_CTL_1:          /* Transmit Control 1 */
+        s->tx_ctl1 = value;
+        if (value & TX_CTL1_TX_DMA_EN) {
+            aw_h3_emac_transmit(s);
+        }
+        break;
+    case REG_TX_FLOW_CTL:       /* Transmit Flow Control */
+        s->tx_flowctl = value;
+        break;
+    case REG_TX_DMA_DESC_LIST:  /* Transmit Descriptor List Address */
+        s->tx_desc_head = value;
+        s->tx_desc_curr = value;
+        break;
+    case REG_RX_CTL_0:          /* Receive Control 0 */
+        s->rx_ctl0 = value;
+        break;
+    case REG_RX_CTL_1:          /* Receive Control 1 */
+        s->rx_ctl1 = value | RX_CTL1_RX_MD;
+        if ((value & RX_CTL1_RX_DMA_EN) && aw_h3_emac_can_receive(nc)) {
+            qemu_flush_queued_packets(nc);
+        }
+        break;
+    case REG_RX_DMA_DESC_LIST:  /* Receive Descriptor List Address */
+        s->rx_desc_head = value;
+        s->rx_desc_curr = value;
+        break;
+    case REG_FRM_FLT:           /* Receive Frame Filter */
+        s->frm_flt = value;
+        break;
+    case REG_RX_HASH_0:         /* Receive Hash Table 0 */
+    case REG_RX_HASH_1:         /* Receive Hash Table 1 */
+        break;
+    case REG_MII_CMD:           /* Management Interface Command */
+        s->mii_cmd = value & ~MII_CMD_PHY_BUSY;
+        aw_h3_emac_mii_cmd(s);
+        break;
+    case REG_MII_DATA:          /* Management Interface Data */
+        s->mii_data = value;
+        break;
+    case REG_ADDR_HIGH:         /* MAC Address High */
+        s->conf.macaddr.a[4] = (value & 0xff);
+        s->conf.macaddr.a[5] = (value & 0xff00) >> 8;
+        break;
+    case REG_ADDR_LOW:          /* MAC Address Low */
+        s->conf.macaddr.a[0] = (value & 0xff);
+        s->conf.macaddr.a[1] = (value & 0xff00) >> 8;
+        s->conf.macaddr.a[2] = (value & 0xff0000) >> 16;
+        s->conf.macaddr.a[3] = (value & 0xff000000) >> 24;
+        break;
+    case REG_TX_DMA_STA:        /* Transmit DMA Status */
+    case REG_TX_CUR_DESC:       /* Transmit Current Descriptor */
+    case REG_TX_CUR_BUF:        /* Transmit Current Buffer */
+    case REG_RX_DMA_STA:        /* Receive DMA Status */
+    case REG_RX_CUR_DESC:       /* Receive Current Descriptor */
+    case REG_RX_CUR_BUF:        /* Receive Current Buffer */
+    case REG_RGMII_STA:         /* RGMII Status */
+        break;
+    default:
+        qemu_log_mask(LOG_UNIMP, "allwinner-h3-emac: write access to unknown "
+                                 "EMAC register 0x" TARGET_FMT_plx "\n",
+                                  offset);
+    }
+}
+
+static void aw_h3_emac_set_link(NetClientState *nc)
+{
+    AwH3EmacState *s = qemu_get_nic_opaque(nc);
+
+    trace_aw_h3_emac_set_link(!nc->link_down);
+    aw_h3_emac_mii_set_link(s, !nc->link_down);
+}
+
+static const MemoryRegionOps aw_h3_emac_mem_ops = {
+    .read = aw_h3_emac_read,
+    .write = aw_h3_emac_write,
+    .endianness = DEVICE_NATIVE_ENDIAN,
+    .valid = {
+        .min_access_size = 4,
+        .max_access_size = 4,
+    },
+};
+
+static NetClientInfo net_aw_h3_emac_info = {
+    .type = NET_CLIENT_DRIVER_NIC,
+    .size = sizeof(NICState),
+    .can_receive = aw_h3_emac_can_receive,
+    .receive = aw_h3_emac_receive,
+    .link_status_changed = aw_h3_emac_set_link,
+};
+
+static void aw_h3_emac_init(Object *obj)
+{
+    SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
+    AwH3EmacState *s = AW_H3_EMAC(obj);
+
+    memory_region_init_io(&s->iomem, OBJECT(s), &aw_h3_emac_mem_ops, s,
+                          TYPE_AW_H3_EMAC, AW_H3_EMAC_REGS_MEM_SIZE);
+    sysbus_init_mmio(sbd, &s->iomem);
+    sysbus_init_irq(sbd, &s->irq);
+}
+
+static void aw_h3_emac_realize(DeviceState *dev, Error **errp)
+{
+    AwH3EmacState *s = AW_H3_EMAC(dev);
+
+    qemu_macaddr_default_if_unset(&s->conf.macaddr);
+    s->nic = qemu_new_nic(&net_aw_h3_emac_info, &s->conf,
+                          object_get_typename(OBJECT(dev)), dev->id, s);
+    qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a);
+}
+
+static Property aw_h3_emac_properties[] = {
+    DEFINE_NIC_PROPERTIES(AwH3EmacState, conf),
+    DEFINE_PROP_UINT8("phy-addr", AwH3EmacState, mii_phy_addr, 0),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static int aw_h3_emac_post_load(void *opaque, int version_id)
+{
+    AwH3EmacState *s = opaque;
+
+    aw_h3_emac_set_link(qemu_get_queue(s->nic));
+
+    return 0;
+}
+
+static const VMStateDescription vmstate_aw_emac = {
+    .name = TYPE_AW_H3_EMAC,
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .post_load = aw_h3_emac_post_load,
+    .fields = (VMStateField[]) {
+        VMSTATE_UINT8(mii_phy_addr, AwH3EmacState),
+        VMSTATE_UINT32(mii_cmd, AwH3EmacState),
+        VMSTATE_UINT32(mii_data, AwH3EmacState),
+        VMSTATE_UINT32(basic_ctl0, AwH3EmacState),
+        VMSTATE_UINT32(basic_ctl1, AwH3EmacState),
+        VMSTATE_UINT32(int_en, AwH3EmacState),
+        VMSTATE_UINT32(int_sta, AwH3EmacState),
+        VMSTATE_UINT32(frm_flt, AwH3EmacState),
+        VMSTATE_UINT32(rx_ctl0, AwH3EmacState),
+        VMSTATE_UINT32(rx_ctl1, AwH3EmacState),
+        VMSTATE_UINT32(rx_desc_head, AwH3EmacState),
+        VMSTATE_UINT32(rx_desc_curr, AwH3EmacState),
+        VMSTATE_UINT32(tx_ctl0, AwH3EmacState),
+        VMSTATE_UINT32(tx_ctl1, AwH3EmacState),
+        VMSTATE_UINT32(tx_desc_head, AwH3EmacState),
+        VMSTATE_UINT32(tx_desc_curr, AwH3EmacState),
+        VMSTATE_UINT32(tx_flowctl, AwH3EmacState),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static void aw_h3_emac_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+
+    dc->realize = aw_h3_emac_realize;
+    dc->props = aw_h3_emac_properties;
+    dc->reset = aw_h3_emac_reset;
+    dc->vmsd = &vmstate_aw_emac;
+}
+
+static const TypeInfo aw_h3_emac_info = {
+    .name           = TYPE_AW_H3_EMAC,
+    .parent         = TYPE_SYS_BUS_DEVICE,
+    .instance_size  = sizeof(AwH3EmacState),
+    .instance_init  = aw_h3_emac_init,
+    .class_init     = aw_h3_emac_class_init,
+};
+
+static void aw_h3_emac_register_types(void)
+{
+    type_register_static(&aw_h3_emac_info);
+}
+
+type_init(aw_h3_emac_register_types)
diff --git a/hw/net/trace-events b/hw/net/trace-events
index e70f12bee1..e9e2f26f68 100644
--- a/hw/net/trace-events
+++ b/hw/net/trace-events
@@ -1,5 +1,15 @@
 # See docs/devel/tracing.txt for syntax documentation.
 
+# allwinner-h3-emac.c
+aw_h3_emac_mii_write_reg(uint32_t reg, uint32_t value) "MII write: reg=0x%x value=0x%x"
+aw_h3_emac_mii_read_reg(uint32_t reg, uint32_t value) "MII read: reg=0x%x value=0x%x"
+aw_h3_emac_receive(uint32_t desc, uint32_t paddr, uint32_t bytes) "RX packet: desc=0x%08x paddr=0x%08x bytes=%u"
+aw_h3_emac_transmit(uint32_t desc, uint32_t paddr, uint32_t bytes) "TX packet: desc=0x%08x paddr=0x%08x bytes=%u"
+aw_h3_emac_reset(void) "HW reset"
+aw_h3_emac_set_link(bool active) "Set link: active=%u"
+aw_h3_emac_read(uint64_t offset, uint64_t val) "MMIO read: offset=0x%" PRIx64 " value=0x%" PRIx64
+aw_h3_emac_write(uint64_t offset, uint64_t val) "MMIO write: offset=0x%" PRIx64 " value=0x%" PRIx64
+
 # etraxfs_eth.c
 mdio_phy_read(int regnum, uint16_t value) "read phy_reg:%d value:0x%04x"
 mdio_phy_write(int regnum, uint16_t value) "write phy_reg:%d value:0x%04x"
diff --git a/include/hw/arm/allwinner-h3.h b/include/hw/arm/allwinner-h3.h
index 7aff4ebbd2..b964a60f41 100644
--- a/include/hw/arm/allwinner-h3.h
+++ b/include/hw/arm/allwinner-h3.h
@@ -31,6 +31,7 @@
 #include "hw/misc/allwinner-h3-syscon.h"
 #include "hw/misc/allwinner-h3-sid.h"
 #include "hw/sd/allwinner-h3-sdhost.h"
+#include "hw/net/allwinner-h3-emac.h"
 #include "target/arm/cpu.h"
 
 #define AW_H3_SRAM_A1_BASE     (0x00000000)
@@ -119,6 +120,7 @@ typedef struct AwH3State {
     AwH3SysconState syscon;
     AwH3SidState sid;
     AwH3SDHostState mmc0;
+    AwH3EmacState emac;
     GICState gic;
     MemoryRegion sram_a1;
     MemoryRegion sram_a2;
diff --git a/include/hw/net/allwinner-h3-emac.h b/include/hw/net/allwinner-h3-emac.h
new file mode 100644
index 0000000000..a007d54472
--- /dev/null
+++ b/include/hw/net/allwinner-h3-emac.h
@@ -0,0 +1,69 @@
+/*
+ * Allwinner H3 EMAC 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/>.
+ */
+
+#ifndef ALLWINNER_H3_EMAC_H
+#define ALLWINNER_H3_EMAC_H
+
+#include "qemu/units.h"
+#include "net/net.h"
+#include "qemu/fifo8.h"
+#include "hw/net/mii.h"
+#include "hw/sysbus.h"
+
+#define AW_H3_EMAC_REGS_MEM_SIZE  (1024)
+
+#define TYPE_AW_H3_EMAC "allwinner-h3-emac"
+#define AW_H3_EMAC(obj) OBJECT_CHECK(AwH3EmacState, (obj), TYPE_AW_H3_EMAC)
+
+typedef struct AwH3EmacState {
+    /*< private >*/
+    SysBusDevice  parent_obj;
+    /*< public >*/
+
+    MemoryRegion iomem;
+    qemu_irq     irq;
+    NICState     *nic;
+    NICConf      conf;
+
+    uint8_t      mii_phy_addr;
+    uint32_t     mii_cmd;
+    uint32_t     mii_data;
+    uint32_t     mii_cr;
+    uint32_t     mii_st;
+
+    uint32_t     basic_ctl0;
+    uint32_t     basic_ctl1;
+    uint32_t     int_en;
+    uint32_t     int_sta;
+    uint32_t     frm_flt;
+
+    uint32_t     rx_ctl0;
+    uint32_t     rx_ctl1;
+    uint32_t     rx_desc_head;
+    uint32_t     rx_desc_curr;
+
+    uint32_t     tx_ctl0;
+    uint32_t     tx_ctl1;
+    uint32_t     tx_desc_head;
+    uint32_t     tx_desc_curr;
+    uint32_t     tx_flowctl;
+
+} AwH3EmacState;
+
+#endif
-- 
2.17.1



^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH 00/10] Add Allwinner H3 SoC and Orange Pi PC Machine
  2019-12-02 21:09 [PATCH 00/10] Add Allwinner H3 SoC and Orange Pi PC Machine Niek Linnenbank
                   ` (9 preceding siblings ...)
  2019-12-02 21:09 ` [PATCH 10/10] arm: allwinner-h3: add EMAC ethernet device Niek Linnenbank
@ 2019-12-03  8:47 ` Philippe Mathieu-Daudé
  2019-12-03 19:25   ` Niek Linnenbank
  2019-12-03  9:02 ` Philippe Mathieu-Daudé
  11 siblings, 1 reply; 33+ messages in thread
From: Philippe Mathieu-Daudé @ 2019-12-03  8:47 UTC (permalink / raw)
  To: Niek Linnenbank, qemu-devel
  Cc: b.galvani, peter.maydell, qemu-arm, Richard Henderson

On 12/2/19 10:09 PM, Niek Linnenbank wrote:
> Dear QEMU developers,
> 
> Hereby I would like to contribute the following set of patches to QEMU
> which add support for the Allwinner H3 System on Chip and the
> Orange Pi PC machine. The following features and devices are supported:
> 
>   * SMP (Quad Core Cortex A7)
>   * Generic Interrupt Controller configuration
>   * SRAM mappings
>   * Timer device (re-used from Allwinner A10)
>   * UART
>   * SD/MMC storage controller
>   * EMAC ethernet connectivity
>   * USB 2.0 interfaces
>   * Clock Control Unit
>   * System Control module
>   * Security Identifier device

Awesome!

> Functionality related to graphical output such as HDMI, GPU,
> Display Engine and audio are not included. Recently released
> mainline Linux kernels (4.19 up to latest master) and mainline U-Boot
> are known to work. The SD/MMC code is tested using bonnie++ and
> various tools such as fsck, dd and fdisk. The EMAC is verified with iperf3
> using -netdev socket.
> 
> 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:
>   $ 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:
>   $ 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:
>   $ ARCH=arm CROSS_COMPILE=arm-linux-gnueabi- make -j5
> 
> To boot the newly build linux kernel in QEMU with the Orange Pi PC machine, use:
>   $ qemu-system-arm -M orangepi -m 512 -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
> 
> Note that this kernel does not have a root filesystem. You may provide it
> with an official Orange Pi PC image [1] either as an SD card or as
> USB mass storage. To boot using the Orange Pi PC Debian image on SD card,
> simply add the -sd argument and provide the proper root= kernel parameter:
>   $ qemu-system-arm -M orangepi -m 512 -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
> 
> Alternatively, you can also choose to build and boot a recent buildroot [2]
> using the orangepi_pc_defconfig or Armbian image [3] for Orange Pi PC.

Richard, trying the Armbian image from 
https://apt.armbian.com/pool/main/l/linux-4.20.7-sunxi/ I get:

$ arm-softmmu/qemu-system-arm -M orangepi -m 512 -nic user \
   -append 'console=ttyS0,115200' \
   -kernel boot/vmlinuz-4.20.7-sunxi \
   -dtb usr/lib/linux-image-dev-sunxi/sun8i-h3-orangepi-pc.dtb \
   -serial stdio -d unimp
Uncompressing Linux... done, booting the kernel.
rtc: unimplemented device write (size 4, value 0x16aa0001, offset 0x0)
rtc: unimplemented device read (size 4, offset 0x0)
rtc: unimplemented device read (size 4, offset 0x0)
rtc: unimplemented device read (size 4, offset 0x8)
qemu-system-arm: target/arm/helper.c:11359: cpu_get_tb_cpu_state: 
Assertion `flags == rebuild_hflags_internal(env)' failed.
Aborted (core dumped)

(gdb) bt
#0  0x00007f6c1fa2ce35 in raise () at /lib64/libc.so.6
#1  0x00007f6c1fa17895 in abort () at /lib64/libc.so.6
#2  0x00007f6c1fa17769 in _nl_load_domain.cold () at /lib64/libc.so.6
#3  0x00007f6c1fa25566 in annobin_assert.c_end () at /lib64/libc.so.6
#4  0x00005590657e2685 in cpu_get_tb_cpu_state (env=0x5590686899b0, 
pc=0x7f6c07ffa718, cs_base=0x7f6c07ffa714, pflags=0x7f6c07ffa71c) at 
target/arm/helper.c:11359
#5  0x000055906569f962 in tb_lookup__cpu_state (cpu=0x5590686808b0, 
pc=0x7f6c07ffa718, cs_base=0x7f6c07ffa714, flags=0x7f6c07ffa71c, 
cf_mask=524288) at include/exec/tb-lookup.h:28
#6  0x00005590656a084c in tb_find (cpu=0x5590686808b0, last_tb=0x0, 
tb_exit=0, cf_mask=524288) at accel/tcg/cpu-exec.c:403
#7  0x00005590656a114a in cpu_exec (cpu=0x5590686808b0) at 
accel/tcg/cpu-exec.c:730
#8  0x000055906565f6af in tcg_cpu_exec (cpu=0x5590686808b0) at cpus.c:1473
#9  0x000055906565ff05 in qemu_tcg_cpu_thread_fn (arg=0x5590686808b0) at 
cpus.c:1781
#10 0x0000559065d54aa6 in qemu_thread_start (args=0x5590687d8c20) at 
util/qemu-thread-posix.c:519
#11 0x00007f6c1fbc54c0 in start_thread () at /lib64/libpthread.so.0
#12 0x00007f6c1faf1553 in clone () at /lib64/libc.so.6

(gdb) p/x flags
$1 = 0x33600000

(gdb) p/x *env
$2 = {regs = {0x0 <repeats 15 times>, 0x40102448}, xregs = {0x0 <repeats 
32 times>}, pc = 0x0, pstate = 0x0, aarch64 = 0x0, hflags = 0x33600000, 
uncached_cpsr = 0x1a, spsr = 0x0, banked_spsr = {0x0, 0x0, 0x0, 0x0, 
0x0, 0x0, 0x0, 0x0},
   banked_r13 = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, banked_r14 = 
{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, usr_regs = {0x0, 0x0, 0x0, 
0x0, 0x0}, fiq_regs = {0x0, 0x0, 0x0, 0x0, 0x0}, CF = 0x0, VF = 0x0, NF 
= 0x0, ZF = 0x0,
   QF = 0x0, GE = 0x0, thumb = 0x1, condexec_bits = 0x0, btype = 0x0, 
daif = 0x3c0, elr_el = {0x0, 0x0, 0x0, 0x0}, sp_el = {0x0, 0x0, 0x0, 
0x0}, cp15 = {c0_cpuid = 0x410fc075, {{_unused_csselr0 = 0x0, csselr_ns 
= 0x0,
         _unused_csselr1 = 0x0, csselr_s = 0x0}, csselr_el = {0x0, 0x0, 
0x0, 0x0}}, {{_unused_sctlr = 0x0, sctlr_ns = 0xc50078, hsctlr = 0x0, 
sctlr_s = 0xc50078}, sctlr_el = {0x0, 0xc50078, 0x0, 0xc50078}}, 
cpacr_el1 = 0x0, cptr_el = {
       0x0, 0x0, 0x0, 0x0}, c1_xscaleauxcr = 0x0, sder = 0x0, nsacr = 
0xc00, {{_unused_ttbr0_0 = 0x0, ttbr0_ns = 0x0, _unused_ttbr0_1 = 0x0, 
ttbr0_s = 0x0}, ttbr0_el = {0x0, 0x0, 0x0, 0x0}}, {{_unused_ttbr1_0 = 
0x0, ttbr1_ns = 0x0,
         _unused_ttbr1_1 = 0x0, ttbr1_s = 0x0}, ttbr1_el = {0x0, 0x0, 
0x0, 0x0}}, vttbr_el2 = 0x0, tcr_el = {{raw_tcr = 0x0, mask = 0x0, 
base_mask = 0x0}, {raw_tcr = 0x0, mask = 0x0, base_mask = 0xffffc000}, 
{raw_tcr = 0x0, mask = 0x0,
         base_mask = 0x0}, {raw_tcr = 0x0, mask = 0x0, base_mask = 
0xffffc000}}, vtcr_el2 = {raw_tcr = 0x0, mask = 0x0, base_mask = 0x0}, 
c2_data = 0x0, c2_insn = 0x0, {{dacr_ns = 0x0, dacr_s = 0x0}, 
{dacr32_el2 = 0x0}},
     pmsav5_data_ap = 0x0, pmsav5_insn_ap = 0x0, hcr_el2 = 0x0, scr_el3 
= 0x101, {{ifsr_ns = 0x0, ifsr_s = 0x0}, {ifsr32_el2 = 0x0}}, 
{{_unused_dfsr = 0x0, dfsr_ns = 0x0, hsr = 0x0, dfsr_s = 0x0}, esr_el = 
{0x0, 0x0, 0x0, 0x0}},
     c6_region = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, 
{{_unused_far0 = 0x0, dfar_ns = 0x0, ifar_ns = 0x0, dfar_s = 0x0, ifar_s 
= 0x0, _unused_far3 = 0x0}, far_el = {0x0, 0x0, 0x0, 0x0}}, hpfar_el2 = 
0x0, hstr_el2 = 0x0, {{
         _unused_par_0 = 0x0, par_ns = 0x0, _unused_par_1 = 0x0, par_s = 
0x0}, par_el = {0x0, 0x0, 0x0, 0x0}}, c9_insn = 0x0, c9_data = 0x0, 
c9_pmcr = 0x41002000, c9_pmcnten = 0x0, c9_pmovsr = 0x0, c9_pmuserenr = 
0x0, c9_pmselr = 0x0,
     c9_pminten = 0x0, {{_unused_mair_0 = 0x0, mair0_ns = 0x0, mair1_ns 
= 0x0, _unused_mair_1 = 0x0, mair0_s = 0x0, mair1_s = 0x0}, mair_el = 
{0x0, 0x0, 0x0, 0x0}}, {{_unused_vbar = 0x0, vbar_ns = 0x0, hvbar = 0x0, 
vbar_s = 0x0},
       vbar_el = {0x0, 0x0, 0x0, 0x0}}, mvbar = 0x0, {fcseidr_ns = 0x0, 
fcseidr_s = 0x0}, {{_unused_contextidr_0 = 0x0, contextidr_ns = 0x0, 
_unused_contextidr_1 = 0x0, contextidr_s = 0x0}, contextidr_el = {0x0, 
0x0, 0x0, 0x0}}, {{
         tpidrurw_ns = 0x0, tpidrprw_ns = 0x0, htpidr = 0x0, _tpidr_el3 
= 0x0}, tpidr_el = {0x0, 0x0, 0x0, 0x0}}, tpidrurw_s = 0x0, tpidrprw_s = 
0x0, tpidruro_s = 0x0, {tpidruro_ns = 0x0, tpidrro_el = {0x0}}, 
c14_cntfrq = 0x3b9aca0,
     c14_cntkctl = 0x0, cnthctl_el2 = 0x3, cntvoff_el2 = 0x0, c14_timer 
= {{cval = 0x0, ctl = 0x0}, {cval = 0x0, ctl = 0x0}, {cval = 0x0, ctl = 
0x0}, {cval = 0x0, ctl = 0x0}}, c15_cpar = 0x0, c15_ticonfig = 0x0, 
c15_i_max = 0x0,
     c15_i_min = 0x0, c15_threadid = 0x0, c15_config_base_address = 0x0, 
c15_diagnostic = 0x0, c15_power_diagnostic = 0x0, c15_power_control = 
0x0, dbgbvr = {0x0 <repeats 16 times>}, dbgbcr = {0x0 <repeats 16 
times>}, dbgwvr = {
       0x0 <repeats 16 times>}, dbgwcr = {0x0 <repeats 16 times>}, 
mdscr_el1 = 0x0, oslsr_el1 = 0xa, mdcr_el2 = 0x0, mdcr_el3 = 0x0, 
c15_ccnt = 0x0, c15_ccnt_delta = 0x0, c14_pmevcntr = {0x0 <repeats 31 
times>}, c14_pmevcntr_delta = {
       0x0 <repeats 31 times>}, c14_pmevtyper = {0x0 <repeats 31 
times>}, pmccfiltr_el0 = 0x0, vpidr_el2 = 0x410fc075, vmpidr_el2 = 
0x80000001}, v7m = {other_sp = 0x0, other_ss_msp = 0x0, other_ss_psp = 
0x0, vecbase = {0x0, 0x0},
     basepri = {0x0, 0x0}, control = {0x0, 0x0}, ccr = {0x0, 0x0}, cfsr 
= {0x0, 0x0}, hfsr = 0x0, dfsr = 0x0, sfsr = 0x0, mmfar = {0x0, 0x0}, 
bfar = 0x0, sfar = 0x0, mpu_ctrl = {0x0, 0x0}, exception = 0x0, primask 
= {0x0, 0x0},
     faultmask = {0x0, 0x0}, aircr = 0x0, secure = 0x0, csselr = {0x0, 
0x0}, scr = {0x0, 0x0}, msplim = {0x0, 0x0}, psplim = {0x0, 0x0}, fpcar 
= {0x0, 0x0}, fpccr = {0x0, 0x0}, fpdscr = {0x0, 0x0}, cpacr = {0x0, 
0x0}, nsacr = 0x0},
   exception = {syndrome = 0x0, fsr = 0x0, vaddress = 0x0, target_el = 
0x0}, serror = {pending = 0x0, has_esr = 0x0, esr = 0x0}, irq_line_state 
= 0x0, teecr = 0x0, teehbr = 0x0, vfp = {zregs = {{d = {0x0, 0x0}} 
<repeats 32 times>},
     qc = {0x0, 0x0, 0x0, 0x0}, vec_len = 0x0, vec_stride = 0x0, xregs = 
{0x41023075, 0x0, 0x0, 0x0, 0x0, 0x0, 0x11111111, 0x10110222, 0x0, 0x0, 
0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, scratch = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 
0x0, 0x0},
     fp_status = {float_detect_tininess = 0x1, float_rounding_mode = 
0x0, float_exception_flags = 0x0, floatx80_rounding_precision = 0x0, 
flush_to_zero = 0x0, flush_inputs_to_zero = 0x0, default_nan_mode = 0x0, 
snan_bit_is_one = 0x0},
     fp_status_f16 = {float_detect_tininess = 0x1, float_rounding_mode = 
0x0, float_exception_flags = 0x0, floatx80_rounding_precision = 0x0, 
flush_to_zero = 0x0, flush_inputs_to_zero = 0x0, default_nan_mode = 0x0,
       snan_bit_is_one = 0x0}, standard_fp_status = 
{float_detect_tininess = 0x1, float_rounding_mode = 0x0, 
float_exception_flags = 0x0, floatx80_rounding_precision = 0x0, 
flush_to_zero = 0x1, flush_inputs_to_zero = 0x1,
       default_nan_mode = 0x1, snan_bit_is_one = 0x0}, zcr_el = {0x0, 
0x0, 0x0, 0x0}}, exclusive_addr = 0xffffffffffffffff, exclusive_val = 
0x0, exclusive_high = 0x0, iwmmxt = {regs = {0x0 <repeats 16 times>}, 
val = 0x0, cregs = {
       0x0 <repeats 16 times>}}, cpu_breakpoint = {0x0 <repeats 16 
times>}, cpu_watchpoint = {0x0 <repeats 16 times>}, end_reset_fields = 
{<No data fields>}, features = 0xfd38fbe6f3, pmsav7 = {drbar = 0x0, drsr 
= 0x0, dracr = 0x0,
     rnr = {0x0, 0x0}}, pmsav8 = {rbar = {0x0, 0x0}, rlar = {0x0, 0x0}, 
mair0 = {0x0, 0x0}, mair1 = {0x0, 0x0}}, sau = {rbar = 0x0, rlar = 0x0, 
rnr = 0x0, ctrl = 0x0}, nvic = 0x0, boot_info = 0x5622af3a17a0, 
gicv3state = 0x0}

> [1] http://www.orangepi.org/downloadresources/
> [2] https://buildroot.org/download.html
> [3] https://www.armbian.com/orange-pi-pc/



^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH 00/10] Add Allwinner H3 SoC and Orange Pi PC Machine
  2019-12-02 21:09 [PATCH 00/10] Add Allwinner H3 SoC and Orange Pi PC Machine Niek Linnenbank
                   ` (10 preceding siblings ...)
  2019-12-03  8:47 ` [PATCH 00/10] Add Allwinner H3 SoC and Orange Pi PC Machine Philippe Mathieu-Daudé
@ 2019-12-03  9:02 ` Philippe Mathieu-Daudé
  2019-12-03 19:32   ` Niek Linnenbank
  11 siblings, 1 reply; 33+ messages in thread
From: Philippe Mathieu-Daudé @ 2019-12-03  9:02 UTC (permalink / raw)
  To: Niek Linnenbank, qemu-devel
  Cc: peter.maydell, Alistair Francis, Richard Henderson,
	Niccolò Izzo, b.galvani, KONRAD Frederic, qemu-arm

On 12/2/19 10:09 PM, Niek Linnenbank wrote:
> Dear QEMU developers,
> 
> Hereby I would like to contribute the following set of patches to QEMU
> which add support for the Allwinner H3 System on Chip and the
> Orange Pi PC machine. The following features and devices are supported:
> 
>   * SMP (Quad Core Cortex A7)
>   * Generic Interrupt Controller configuration
>   * SRAM mappings
>   * Timer device (re-used from Allwinner A10)
>   * UART
>   * SD/MMC storage controller
>   * EMAC ethernet connectivity
>   * USB 2.0 interfaces
>   * Clock Control Unit
>   * System Control module
>   * Security Identifier device
> 
> Functionality related to graphical output such as HDMI, GPU,
> Display Engine and audio are not included.

I'd love to see the OpenRISC AR100 core instantiated in this SoC.

Your contribution makes another good example of multi-arch/single-binary 
QEMU (here 4x ARM + 1x OpenRISC).



^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH 02/10] hw: arm: add Xunlong Orange Pi PC machine
  2019-12-02 21:09 ` [PATCH 02/10] hw: arm: add Xunlong Orange Pi PC machine Niek Linnenbank
@ 2019-12-03  9:17   ` Philippe Mathieu-Daudé
  2019-12-03 19:33     ` Niek Linnenbank
  2019-12-05 22:15     ` Niek Linnenbank
  0 siblings, 2 replies; 33+ messages in thread
From: Philippe Mathieu-Daudé @ 2019-12-03  9:17 UTC (permalink / raw)
  To: Niek Linnenbank, qemu-devel; +Cc: b.galvani, peter.maydell, qemu-arm

On 12/2/19 10:09 PM, Niek Linnenbank wrote:
> 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,
> 512MB RAM, 100Mbit ethernet, USB, SD/MMC, USB, HDMI and
> various other I/O. This commit add support for the Xunlong
> Orange Pi PC machine.
> 
> Signed-off-by: Niek Linnenbank <nieklinnenbank@gmail.com>
> ---
>   MAINTAINERS          |  1 +
>   hw/arm/Makefile.objs |  2 +-
>   hw/arm/orangepi.c    | 90 ++++++++++++++++++++++++++++++++++++++++++++
>   3 files changed, 92 insertions(+), 1 deletion(-)
>   create mode 100644 hw/arm/orangepi.c
> 
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 29c9936037..42c913d6cb 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -485,6 +485,7 @@ L: qemu-arm@nongnu.org
>   S: Maintained
>   F: hw/*/allwinner-h3*
>   F: include/hw/*/allwinner-h3*
> +F: hw/arm/orangepi.c
>   
>   ARM PrimeCell and CMSDK devices
>   M: Peter Maydell <peter.maydell@linaro.org>
> diff --git a/hw/arm/Makefile.objs b/hw/arm/Makefile.objs
> index 956e496052..8d5ea453d5 100644
> --- a/hw/arm/Makefile.objs
> +++ b/hw/arm/Makefile.objs
> @@ -34,7 +34,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
> +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_XLNX_ZYNQMP_ARM) += xlnx-zynqmp.o xlnx-zcu102.o
> diff --git a/hw/arm/orangepi.c b/hw/arm/orangepi.c
> new file mode 100644
> index 0000000000..5ef2735f81
> --- /dev/null
> +++ b/hw/arm/orangepi.c
> @@ -0,0 +1,90 @@
> +/*
> + * 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 "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"
> +
> +static struct arm_boot_info orangepi_binfo = {
> +    .loader_start = AW_H3_SDRAM_BASE,
> +    .board_id = -1,
> +};
> +
> +typedef struct OrangePiState {
> +    AwH3State *h3;
> +    MemoryRegion sdram;
> +} OrangePiState;
> +
> +static void orangepi_init(MachineState *machine)
> +{
> +    OrangePiState *s = g_new(OrangePiState, 1);
> +    Error *err = NULL;
> +

Here I'd add:

       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);
       }

> +    s->h3 = AW_H3(object_new(TYPE_AW_H3));
> +
> +    /* Setup timer properties */
> +    object_property_set_int(OBJECT(&s->h3->timer), 32768, "clk0-freq", &err);
> +    if (err != NULL) {
> +        error_reportf_err(err, "Couldn't set clk0 frequency: ");
> +        exit(1);
> +    }
> +
> +    object_property_set_int(OBJECT(&s->h3->timer), 24000000, "clk1-freq",
> +                            &err);
> +    if (err != NULL) {
> +        error_reportf_err(err, "Couldn't set clk1 frequency: ");
> +        exit(1);
> +    }
> +
> +    /* Mark H3 object realized */
> +    object_property_set_bool(OBJECT(s->h3), true, "realized", &err);

I'm not sure if that's correct but I'd simply use &error_abort here.

> +    if (err != NULL) {
> +        error_reportf_err(err, "Couldn't realize Allwinner H3: ");
> +        exit(1);
> +    }
> +
> +    /* RAM */
> +    memory_region_allocate_system_memory(&s->sdram, NULL, "orangepi.ram",
> +                                         machine->ram_size);

I'd only allow machine->ram_size == 1 * GiB here, since the onboard DRAM 
is not upgradable.

> +    memory_region_add_subregion(get_system_memory(), AW_H3_SDRAM_BASE,
> +                                &s->sdram);
> +
> +    /* Load target kernel */
> +    orangepi_binfo.ram_size = machine->ram_size;
> +    orangepi_binfo.nb_cpus  = AW_H3_NUM_CPUS;
> +    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->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->ignore_memory_transaction_failures = true;

You should not use this flag in new design. See the documentation in 
include/hw/boards.h:

  * @ignore_memory_transaction_failures:
  *    [...] New board models
  *    should instead use "unimplemented-device" for all memory ranges where
  *    the guest will attempt to probe for a device that QEMU doesn't
  *    implement and a stub device is required.

You already use the "unimplemented-device".

> +}
> +
> +DEFINE_MACHINE("orangepi", orangepi_machine_init)

Can you name it 'orangepi-pc'? So we can add other orangepi models.

Thanks,

Phil.



^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH 10/10] arm: allwinner-h3: add EMAC ethernet device
  2019-12-02 21:09 ` [PATCH 10/10] arm: allwinner-h3: add EMAC ethernet device Niek Linnenbank
@ 2019-12-03  9:33   ` KONRAD Frederic
  2019-12-03 19:41     ` Niek Linnenbank
  2019-12-04 15:14     ` Philippe Mathieu-Daudé
  0 siblings, 2 replies; 33+ messages in thread
From: KONRAD Frederic @ 2019-12-03  9:33 UTC (permalink / raw)
  To: Niek Linnenbank, qemu-devel; +Cc: b.galvani, peter.maydell, qemu-arm



Le 12/2/19 à 10:09 PM, Niek Linnenbank a écrit :
> The Allwinner H3 System on Chip includes an Ethernet MAC (EMAC)
> which provides 10M/100M/1000M Ethernet connectivity. This commit
> adds support for the Allwinner H3 EMAC, including emulation for
> the following functionality:
> 
>   * DMA transfers
>   * MII interface
>   * Transmit CRC calculation
> 
> Signed-off-by: Niek Linnenbank <nieklinnenbank@gmail.com>
> ---
>   hw/arm/Kconfig                     |   1 +
>   hw/arm/allwinner-h3.c              |  17 +
>   hw/arm/orangepi.c                  |   7 +
>   hw/net/Kconfig                     |   3 +
>   hw/net/Makefile.objs               |   1 +
>   hw/net/allwinner-h3-emac.c         | 786 +++++++++++++++++++++++++++++
>   hw/net/trace-events                |  10 +
>   include/hw/arm/allwinner-h3.h      |   2 +
>   include/hw/net/allwinner-h3-emac.h |  69 +++
>   9 files changed, 896 insertions(+)
>   create mode 100644 hw/net/allwinner-h3-emac.c
>   create mode 100644 include/hw/net/allwinner-h3-emac.h
> 
> diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig
> index ebf8d2325f..551cff3442 100644
> --- a/hw/arm/Kconfig
> +++ b/hw/arm/Kconfig
> @@ -294,6 +294,7 @@ config ALLWINNER_A10
>   config ALLWINNER_H3
>       bool
>       select ALLWINNER_A10_PIT
> +    select ALLWINNER_H3_EMAC
>       select SERIAL
>       select ARM_TIMER
>       select ARM_GIC
> diff --git a/hw/arm/allwinner-h3.c b/hw/arm/allwinner-h3.c
> index c2972caf88..274b8548c0 100644
> --- a/hw/arm/allwinner-h3.c
> +++ b/hw/arm/allwinner-h3.c
> @@ -53,6 +53,9 @@ static void aw_h3_init(Object *obj)
>   
>       sysbus_init_child_obj(obj, "mmc0", &s->mmc0, sizeof(s->mmc0),
>                             TYPE_AW_H3_SDHOST);
> +
> +    sysbus_init_child_obj(obj, "emac", &s->emac, sizeof(s->emac),
> +                          TYPE_AW_H3_EMAC);
>   }
>   
>   static void aw_h3_realize(DeviceState *dev, Error **errp)
> @@ -237,6 +240,20 @@ static void aw_h3_realize(DeviceState *dev, Error **errp)
>           return;
>       }
>   
> +    /* EMAC */
> +    if (nd_table[0].used) {
> +        qemu_check_nic_model(&nd_table[0], TYPE_AW_H3_EMAC);
> +        qdev_set_nic_properties(DEVICE(&s->emac), &nd_table[0]);
> +    }
> +    object_property_set_bool(OBJECT(&s->emac), true, "realized", &err);
> +    if (err != NULL) {
> +        error_propagate(errp, err);
> +        return;
> +    }
> +    sysbusdev = SYS_BUS_DEVICE(&s->emac);
> +    sysbus_mmio_map(sysbusdev, 0, AW_H3_EMAC_BASE);
> +    sysbus_connect_irq(sysbusdev, 0, s->irq[AW_H3_GIC_SPI_EMAC]);
> +
>       /* Universal Serial Bus */
>       sysbus_create_simple(TYPE_AW_H3_EHCI, AW_H3_EHCI0_BASE,
>                            s->irq[AW_H3_GIC_SPI_EHCI0]);
> diff --git a/hw/arm/orangepi.c b/hw/arm/orangepi.c
> index dee3efaf08..8a61eb0e69 100644
> --- a/hw/arm/orangepi.c
> +++ b/hw/arm/orangepi.c
> @@ -61,6 +61,13 @@ static void orangepi_init(MachineState *machine)
>           exit(1);
>       }
>   
> +    /* Setup EMAC properties */
> +    object_property_set_int(OBJECT(&s->h3->emac), 1, "phy-addr", &err);
> +    if (err != NULL) {
> +        error_reportf_err(err, "Couldn't set phy address: ");
> +        exit(1);
> +    }
> +
>       /* Mark H3 object realized */
>       object_property_set_bool(OBJECT(s->h3), true, "realized", &err);
>       if (err != NULL) {
> diff --git a/hw/net/Kconfig b/hw/net/Kconfig
> index 3856417d42..36d3923992 100644
> --- a/hw/net/Kconfig
> +++ b/hw/net/Kconfig
> @@ -74,6 +74,9 @@ config MIPSNET
>   config ALLWINNER_EMAC
>       bool
>   
> +config ALLWINNER_H3_EMAC
> +    bool
> +
>   config IMX_FEC
>       bool
>   
> diff --git a/hw/net/Makefile.objs b/hw/net/Makefile.objs
> index 7907d2c199..5548deb07a 100644
> --- a/hw/net/Makefile.objs
> +++ b/hw/net/Makefile.objs
> @@ -23,6 +23,7 @@ common-obj-$(CONFIG_XGMAC) += xgmac.o
>   common-obj-$(CONFIG_MIPSNET) += mipsnet.o
>   common-obj-$(CONFIG_XILINX_AXI) += xilinx_axienet.o
>   common-obj-$(CONFIG_ALLWINNER_EMAC) += allwinner_emac.o
> +common-obj-$(CONFIG_ALLWINNER_H3_EMAC) += allwinner-h3-emac.o
>   common-obj-$(CONFIG_IMX_FEC) += imx_fec.o
>   
>   common-obj-$(CONFIG_CADENCE) += cadence_gem.o
> diff --git a/hw/net/allwinner-h3-emac.c b/hw/net/allwinner-h3-emac.c
> new file mode 100644
> index 0000000000..37f6f44406
> --- /dev/null
> +++ b/hw/net/allwinner-h3-emac.c
> @@ -0,0 +1,786 @@
> +/*
> + * Allwinner H3 EMAC 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 "hw/sysbus.h"
> +#include "migration/vmstate.h"
> +#include "net/net.h"
> +#include "hw/irq.h"
> +#include "hw/qdev-properties.h"
> +#include "qemu/log.h"
> +#include "trace.h"
> +#include "net/checksum.h"
> +#include "qemu/module.h"
> +#include "exec/cpu-common.h"
> +#include "hw/net/allwinner-h3-emac.h"
> +
> +/* EMAC register offsets */
> +#define REG_BASIC_CTL_0        (0x0000) /* Basic Control 0 */
> +#define REG_BASIC_CTL_1        (0x0004) /* Basic Control 1 */
> +#define REG_INT_STA            (0x0008) /* Interrupt Status */
> +#define REG_INT_EN             (0x000C) /* Interrupt Enable */
> +#define REG_TX_CTL_0           (0x0010) /* Transmit Control 0 */
> +#define REG_TX_CTL_1           (0x0014) /* Transmit Control 1 */
> +#define REG_TX_FLOW_CTL        (0x001C) /* Transmit Flow Control */
> +#define REG_TX_DMA_DESC_LIST   (0x0020) /* Transmit Descriptor List Address */
> +#define REG_RX_CTL_0           (0x0024) /* Receive Control 0 */
> +#define REG_RX_CTL_1           (0x0028) /* Receive Control 1 */
> +#define REG_RX_DMA_DESC_LIST   (0x0034) /* Receive Descriptor List Address */
> +#define REG_FRM_FLT            (0x0038) /* Receive Frame Filter */
> +#define REG_RX_HASH_0          (0x0040) /* Receive Hash Table 0 */
> +#define REG_RX_HASH_1          (0x0044) /* Receive Hash Table 1 */
> +#define REG_MII_CMD            (0x0048) /* Management Interface Command */
> +#define REG_MII_DATA           (0x004C) /* Management Interface Data */
> +#define REG_ADDR_HIGH          (0x0050) /* MAC Address High */
> +#define REG_ADDR_LOW           (0x0054) /* MAC Address Low */
> +#define REG_TX_DMA_STA         (0x00B0) /* Transmit DMA Status */
> +#define REG_TX_CUR_DESC        (0x00B4) /* Transmit Current Descriptor */
> +#define REG_TX_CUR_BUF         (0x00B8) /* Transmit Current Buffer */
> +#define REG_RX_DMA_STA         (0x00C0) /* Receive DMA Status */
> +#define REG_RX_CUR_DESC        (0x00C4) /* Receive Current Descriptor */
> +#define REG_RX_CUR_BUF         (0x00C8) /* Receive Current Buffer */
> +#define REG_RGMII_STA          (0x00D0) /* RGMII Status */
> +
> +/* EMAC register flags */
> +#define BASIC_CTL0_100Mbps     (0b11 << 2)
> +#define BASIC_CTL0_FD          (1 << 0)
> +#define BASIC_CTL1_SOFTRST     (1 << 0)
> +
> +#define INT_STA_RGMII_LINK     (1 << 16)
> +#define INT_STA_RX_EARLY       (1 << 13)
> +#define INT_STA_RX_OVERFLOW    (1 << 12)
> +#define INT_STA_RX_TIMEOUT     (1 << 11)
> +#define INT_STA_RX_DMA_STOP    (1 << 10)
> +#define INT_STA_RX_BUF_UA      (1 << 9)
> +#define INT_STA_RX             (1 << 8)
> +#define INT_STA_TX_EARLY       (1 << 5)
> +#define INT_STA_TX_UNDERFLOW   (1 << 4)
> +#define INT_STA_TX_TIMEOUT     (1 << 3)
> +#define INT_STA_TX_BUF_UA      (1 << 2)
> +#define INT_STA_TX_DMA_STOP    (1 << 1)
> +#define INT_STA_TX             (1 << 0)
> +
> +#define INT_EN_RX_EARLY        (1 << 13)
> +#define INT_EN_RX_OVERFLOW     (1 << 12)
> +#define INT_EN_RX_TIMEOUT      (1 << 11)
> +#define INT_EN_RX_DMA_STOP     (1 << 10)
> +#define INT_EN_RX_BUF_UA       (1 << 9)
> +#define INT_EN_RX              (1 << 8)
> +#define INT_EN_TX_EARLY        (1 << 5)
> +#define INT_EN_TX_UNDERFLOW    (1 << 4)
> +#define INT_EN_TX_TIMEOUT      (1 << 3)
> +#define INT_EN_TX_BUF_UA       (1 << 2)
> +#define INT_EN_TX_DMA_STOP     (1 << 1)
> +#define INT_EN_TX              (1 << 0)
> +
> +#define TX_CTL0_TX_EN          (1 << 31)
> +#define TX_CTL1_TX_DMA_START   (1 << 31)
> +#define TX_CTL1_TX_DMA_EN      (1 << 30)
> +#define TX_CTL1_TX_FLUSH       (1 << 0)
> +
> +#define RX_CTL0_RX_EN          (1 << 31)
> +#define RX_CTL0_STRIP_FCS      (1 << 28)
> +#define RX_CTL0_CRC_IPV4       (1 << 27)
> +
> +#define RX_CTL1_RX_DMA_START   (1 << 31)
> +#define RX_CTL1_RX_DMA_EN      (1 << 30)
> +#define RX_CTL1_RX_MD          (1 << 1)
> +
> +#define RX_FRM_FLT_DIS_ADDR    (1 << 31)
> +
> +#define MII_CMD_PHY_ADDR_SHIFT (12)
> +#define MII_CMD_PHY_ADDR_MASK  (0xf000)
> +#define MII_CMD_PHY_REG_SHIFT  (4)
> +#define MII_CMD_PHY_REG_MASK   (0xf0)
> +#define MII_CMD_PHY_RW         (1 << 1)
> +#define MII_CMD_PHY_BUSY       (1 << 0)
> +
> +#define TX_DMA_STA_STOP        (0b000)
> +#define TX_DMA_STA_RUN_FETCH   (0b001)
> +#define TX_DMA_STA_WAIT_STA    (0b010)
> +
> +#define RX_DMA_STA_STOP        (0b000)
> +#define RX_DMA_STA_RUN_FETCH   (0b001)
> +#define RX_DMA_STA_WAIT_FRM    (0b011)
> +
> +#define RGMII_LINK_UP          (1 << 3)
> +#define RGMII_FD               (1 << 0)
> +
> +/* EMAC register reset values */
> +#define REG_BASIC_CTL_1_RST    (0x08000000)
> +
> +/* EMAC constants */
> +#define AW_H3_EMAC_MIN_PKT_SZ  (64)
> +
> +/* Transmit/receive frame descriptor */
> +typedef struct FrameDescriptor {
> +    uint32_t status;
> +    uint32_t status2;
> +    uint32_t addr;
> +    uint32_t next;
> +} FrameDescriptor;
> +
> +/* Frame descriptor flags */
> +#define DESC_STATUS_CTL                 (1 << 31)
> +#define DESC_STATUS2_BUF_SIZE_MASK      (0x7ff)
> +
> +/* Transmit frame descriptor flags */
> +#define TX_DESC_STATUS_LENGTH_ERR       (1 << 14)
> +#define TX_DESC_STATUS2_FIRST_DESC      (1 << 29)
> +#define TX_DESC_STATUS2_LAST_DESC       (1 << 30)
> +#define TX_DESC_STATUS2_CHECKSUM_MASK   (0x3 << 27)
> +
> +/* Receive frame descriptor flags */
> +#define RX_DESC_STATUS_FIRST_DESC       (1 << 9)
> +#define RX_DESC_STATUS_LAST_DESC        (1 << 8)
> +#define RX_DESC_STATUS_FRM_LEN_MASK     (0x3fff0000)
> +#define RX_DESC_STATUS_FRM_LEN_SHIFT    (16)
> +#define RX_DESC_STATUS_NO_BUF           (1 << 14)
> +#define RX_DESC_STATUS_HEADER_ERR       (1 << 7)
> +#define RX_DESC_STATUS_LENGTH_ERR       (1 << 4)
> +#define RX_DESC_STATUS_CRC_ERR          (1 << 1)
> +#define RX_DESC_STATUS_PAYLOAD_ERR      (1 << 0)
> +#define RX_DESC_STATUS2_RX_INT_CTL      (1 << 31)
> +
> +/* MII register offsets */
> +#define MII_REG_CR                      (0x0)
> +#define MII_REG_ST                      (0x1)
> +#define MII_REG_ID_HIGH                 (0x2)
> +#define MII_REG_ID_LOW                  (0x3)
> +
> +/* MII register flags */
> +#define MII_REG_CR_RESET                (1 << 15)
> +#define MII_REG_CR_POWERDOWN            (1 << 11)
> +#define MII_REG_CR_10Mbit               (0)
> +#define MII_REG_CR_100Mbit              (1 << 13)
> +#define MII_REG_CR_1000Mbit             (1 << 6)
> +#define MII_REG_CR_AUTO_NEG             (1 << 12)
> +#define MII_REG_CR_AUTO_NEG_RESTART     (1 << 9)
> +#define MII_REG_CR_FULLDUPLEX           (1 << 8)
> +
> +#define MII_REG_ST_100BASE_T4           (1 << 15)
> +#define MII_REG_ST_100BASE_X_FD         (1 << 14)
> +#define MII_REG_ST_100BASE_X_HD         (1 << 13)
> +#define MII_REG_ST_10_FD                (1 << 12)
> +#define MII_REG_ST_10_HD                (1 << 11)
> +#define MII_REG_ST_100BASE_T2_FD        (1 << 10)
> +#define MII_REG_ST_100BASE_T2_HD        (1 << 9)
> +#define MII_REG_ST_AUTONEG_COMPLETE     (1 << 5)
> +#define MII_REG_ST_AUTONEG_AVAIL        (1 << 3)
> +#define MII_REG_ST_LINK_UP              (1 << 2)
> +
> +/* MII constants */
> +#define MII_PHY_ID_HIGH                 (0x0044)
> +#define MII_PHY_ID_LOW                  (0x1400)

I wonder if we can't share all those mii stuff accross the network adapters
instead of redoing the work everytime. I've some patches about it I may post
them sometimes.

> +
> +static void aw_h3_emac_mii_set_link(AwH3EmacState *s, bool link_active)
> +{
> +    if (link_active) {
> +        s->mii_st |= MII_REG_ST_LINK_UP;
> +    } else {
> +        s->mii_st &= ~MII_REG_ST_LINK_UP;
> +    }
> +}
> +
> +static void aw_h3_emac_mii_reset(AwH3EmacState *s, bool link_active)
> +{
> +    s->mii_cr = MII_REG_CR_100Mbit | MII_REG_CR_AUTO_NEG |
> +                MII_REG_CR_FULLDUPLEX;
> +    s->mii_st = MII_REG_ST_100BASE_T4 | MII_REG_ST_100BASE_X_FD |
> +                MII_REG_ST_100BASE_X_HD | MII_REG_ST_10_FD | MII_REG_ST_10_HD |
> +                MII_REG_ST_100BASE_T2_FD | MII_REG_ST_100BASE_T2_HD |
> +                MII_REG_ST_AUTONEG_COMPLETE | MII_REG_ST_AUTONEG_AVAIL;
> +
> +    aw_h3_emac_mii_set_link(s, link_active);
> +}
> +
> +static void aw_h3_emac_mii_cmd(AwH3EmacState *s)
> +{
> +    uint8_t addr, reg;
> +
> +    addr = (s->mii_cmd & MII_CMD_PHY_ADDR_MASK) >> MII_CMD_PHY_ADDR_SHIFT;
> +    reg = (s->mii_cmd & MII_CMD_PHY_REG_MASK) >> MII_CMD_PHY_REG_SHIFT;
> +
> +    if (addr != s->mii_phy_addr) {
> +        return;
> +    }
> +
> +    /* Read or write a PHY register? */
> +    if (s->mii_cmd & MII_CMD_PHY_RW) {
> +        trace_aw_h3_emac_mii_write_reg(reg, s->mii_data);
> +
> +        switch (reg) {
> +        case MII_REG_CR:
> +            if (s->mii_data & MII_REG_CR_RESET) {
> +                aw_h3_emac_mii_reset(s, s->mii_st & MII_REG_ST_LINK_UP);
> +            } else {
> +                s->mii_cr = s->mii_data & ~(MII_REG_CR_RESET |
> +                                            MII_REG_CR_AUTO_NEG_RESTART);
> +            }
> +            break;
> +        default:
> +            qemu_log_mask(LOG_UNIMP, "allwinner-h3-emac: write access to "
> +                                     "unknown MII register 0x%x\n", reg);
> +            break;
> +        }
> +    } else {
> +        switch (reg) {
> +        case MII_REG_CR:
> +            s->mii_data = s->mii_cr;
> +            break;
> +        case MII_REG_ST:
> +            s->mii_data = s->mii_st;
> +            break;
> +        case MII_REG_ID_HIGH:
> +            s->mii_data = MII_PHY_ID_HIGH;
> +            break;
> +        case MII_REG_ID_LOW:
> +            s->mii_data = MII_PHY_ID_LOW;
> +            break;
> +        default:
> +            qemu_log_mask(LOG_UNIMP, "allwinner-h3-emac: read access to "
> +                                     "unknown MII register 0x%x\n", reg);
> +            s->mii_data = 0;
> +            break;
> +        }
> +
> +        trace_aw_h3_emac_mii_read_reg(reg, s->mii_data);
> +    }
> +}
> +
> +static void aw_h3_emac_update_irq(AwH3EmacState *s)
> +{
> +    qemu_set_irq(s->irq, (s->int_sta & s->int_en) != 0);
> +}
> +
> +static uint32_t aw_h3_emac_next_desc(FrameDescriptor *desc, size_t min_size)
> +{
> +    uint32_t paddr = desc->next;
> +
> +    cpu_physical_memory_read(paddr, desc, sizeof(*desc));
> +
> +    if ((desc->status & DESC_STATUS_CTL) &&
> +        (desc->status2 & DESC_STATUS2_BUF_SIZE_MASK) >= min_size) {
> +        return paddr;
> +    } else {
> +        return 0;
> +    }
> +}
> +
> +static uint32_t aw_h3_emac_get_desc(FrameDescriptor *desc, uint32_t start_addr,
> +                                    size_t min_size)
> +{
> +    uint32_t desc_addr = start_addr;
> +
> +    /* Note that the list is a cycle. Last entry points back to the head. */
> +    while (desc_addr != 0) {
> +        cpu_physical_memory_read(desc_addr, desc, sizeof(*desc));
> +
> +        if ((desc->status & DESC_STATUS_CTL) &&
> +            (desc->status2 & DESC_STATUS2_BUF_SIZE_MASK) >= min_size) {
> +            return desc_addr;
> +        } else if (desc->next == start_addr) {
> +            break;
> +        } else {
> +            desc_addr = desc->next;
> +        }
> +    }
> +
> +    return 0;
> +}
> +
> +static uint32_t aw_h3_emac_get_rx_desc(AwH3EmacState *s, FrameDescriptor *desc,
> +                                       size_t min_size)
> +{
> +    return aw_h3_emac_get_desc(desc, s->rx_desc_curr, min_size);
> +}
> +
> +static uint32_t aw_h3_emac_get_tx_desc(AwH3EmacState *s, FrameDescriptor *desc,
> +                                       size_t min_size)
> +{
> +    return aw_h3_emac_get_desc(desc, s->tx_desc_head, min_size);
> +}
> +
> +static void aw_h3_emac_flush_desc(FrameDescriptor *desc, uint32_t phys_addr)
> +{
> +    cpu_physical_memory_write(phys_addr, desc, sizeof(*desc));
> +}
> +
> +static int aw_h3_emac_can_receive(NetClientState *nc)
> +{
> +    AwH3EmacState *s = qemu_get_nic_opaque(nc);
> +    FrameDescriptor desc;
> +
> +    return (s->rx_ctl0 & RX_CTL0_RX_EN) &&
> +           (aw_h3_emac_get_rx_desc(s, &desc, 0) != 0);
> +}
> +
> +static ssize_t aw_h3_emac_receive(NetClientState *nc, const uint8_t *buf,
> +                                  size_t size)
> +{
> +    AwH3EmacState *s = qemu_get_nic_opaque(nc);
> +    FrameDescriptor desc;
> +    size_t bytes_left = size;
> +    size_t desc_bytes = 0;
> +    size_t pad_fcs_size = 4;
> +    size_t padding = 0;
> +
> +    if (!(s->rx_ctl0 & RX_CTL0_RX_EN)) {
> +        return -1;
> +    }
> +
> +    s->rx_desc_curr = aw_h3_emac_get_rx_desc(s, &desc, AW_H3_EMAC_MIN_PKT_SZ);
> +    if (!s->rx_desc_curr) {
> +        s->int_sta |= INT_STA_RX_BUF_UA;
> +    }
> +
> +    /* Keep filling RX descriptors until the whole frame is written */
> +    while (s->rx_desc_curr && bytes_left > 0) {
> +        desc.status &= ~DESC_STATUS_CTL;
> +        desc.status &= ~RX_DESC_STATUS_FRM_LEN_MASK;
> +
> +        if (bytes_left == size) {
> +            desc.status |= RX_DESC_STATUS_FIRST_DESC;
> +        }
> +
> +        if ((desc.status2 & DESC_STATUS2_BUF_SIZE_MASK) <
> +            (bytes_left + pad_fcs_size)) {
> +            desc_bytes = desc.status2 & DESC_STATUS2_BUF_SIZE_MASK;
> +            desc.status |= desc_bytes << RX_DESC_STATUS_FRM_LEN_SHIFT;
> +        } else {
> +            padding = pad_fcs_size;
> +            if (bytes_left < AW_H3_EMAC_MIN_PKT_SZ) {
> +                padding += (AW_H3_EMAC_MIN_PKT_SZ - bytes_left);
> +            }
> +
> +            desc_bytes = (bytes_left);
> +            desc.status |= RX_DESC_STATUS_LAST_DESC;
> +            desc.status |= (bytes_left + padding)
> +                            << RX_DESC_STATUS_FRM_LEN_SHIFT;
> +        }
> +
> +        cpu_physical_memory_write(desc.addr, buf, desc_bytes);
> +        aw_h3_emac_flush_desc(&desc, s->rx_desc_curr);
> +        trace_aw_h3_emac_receive(s->rx_desc_curr, desc.addr, desc_bytes);
> +
> +        /* Check if frame needs to raise the receive interrupt */
> +        if (!(desc.status2 & RX_DESC_STATUS2_RX_INT_CTL)) {
> +            s->int_sta |= INT_STA_RX;
> +        }
> +
> +        /* Increment variables */
> +        buf += desc_bytes;
> +        bytes_left -= desc_bytes;
> +
> +        /* Move to the next descriptor */
> +        s->rx_desc_curr = aw_h3_emac_next_desc(&desc, 64);
> +        if (!s->rx_desc_curr) {
> +            /* Not enough buffer space available */
> +            s->int_sta |= INT_STA_RX_BUF_UA;
> +            s->rx_desc_curr = s->rx_desc_head;
> +            break;
> +        }
> +    }
> +
> +    /* Report receive DMA is finished */
> +    s->rx_ctl1 &= ~RX_CTL1_RX_DMA_START;
> +    aw_h3_emac_update_irq(s);
> +
> +    return size;
> +}
> +
> +static void aw_h3_emac_transmit(AwH3EmacState *s)
> +{
> +    NetClientState *nc = qemu_get_queue(s->nic);
> +    FrameDescriptor desc;
> +    size_t bytes = 0;
> +    size_t packet_bytes = 0;
> +    size_t transmitted = 0;
> +    static uint8_t packet_buf[2048];
> +
> +    s->tx_desc_curr = aw_h3_emac_get_tx_desc(s, &desc, 0);
> +
> +    /* Read all transmit descriptors */
> +    while (s->tx_desc_curr != 0) {
> +
> +        /* Read from physical memory into packet buffer */
> +        bytes = desc.status2 & DESC_STATUS2_BUF_SIZE_MASK;
> +        if (bytes + packet_bytes > sizeof(packet_buf)) {
> +            desc.status |= TX_DESC_STATUS_LENGTH_ERR;
> +            break;
> +        }
> +        cpu_physical_memory_read(desc.addr, packet_buf + packet_bytes, bytes);
> +        packet_bytes += bytes;
> +        desc.status &= ~DESC_STATUS_CTL;
> +        aw_h3_emac_flush_desc(&desc, s->tx_desc_curr);
> +
> +        /* After the last descriptor, send the packet */
> +        if (desc.status2 & TX_DESC_STATUS2_LAST_DESC) {
> +            if (desc.status2 & TX_DESC_STATUS2_CHECKSUM_MASK) {
> +                net_checksum_calculate(packet_buf, packet_bytes);
> +            }
> +
> +            qemu_send_packet(nc, packet_buf, packet_bytes);
> +            trace_aw_h3_emac_transmit(s->tx_desc_curr, desc.addr, bytes);
> +
> +            packet_bytes = 0;
> +            transmitted++;
> +        }
> +        s->tx_desc_curr = aw_h3_emac_next_desc(&desc, 0);
> +    }
> +
> +    /* Raise transmit completed interrupt */
> +    if (transmitted > 0) {
> +        s->int_sta |= INT_STA_TX;
> +        s->tx_ctl1 &= ~TX_CTL1_TX_DMA_START;
> +        aw_h3_emac_update_irq(s);
> +    }
> +}
> +
> +static void aw_h3_emac_reset(DeviceState *dev)
> +{
> +    AwH3EmacState *s = AW_H3_EMAC(dev);
> +    NetClientState *nc = qemu_get_queue(s->nic);
> +
> +    trace_aw_h3_emac_reset();
> +
> +    s->mii_cmd = 0;
> +    s->mii_data = 0;
> +    s->basic_ctl0 = 0;
> +    s->basic_ctl1 = 0;
> +    s->int_en = 0;
> +    s->int_sta = 0;
> +    s->frm_flt = 0;
> +    s->rx_ctl0 = 0;
> +    s->rx_ctl1 = RX_CTL1_RX_MD;
> +    s->rx_desc_head = 0;
> +    s->rx_desc_curr = 0;
> +    s->tx_ctl0 = 0;
> +    s->tx_ctl1 = 0;
> +    s->tx_desc_head = 0;
> +    s->tx_desc_curr = 0;
> +    s->tx_flowctl = 0;
> +
> +    aw_h3_emac_mii_reset(s, !nc->link_down);
> +}
> +
> +static uint64_t aw_h3_emac_read(void *opaque, hwaddr offset, unsigned size)
> +{
> +    AwH3EmacState *s = opaque;

I'd put AW_H3_EMAC(opaque) here.

> +    uint64_t value = 0;
> +    FrameDescriptor desc;
> +
> +    switch (offset) {
> +    case REG_BASIC_CTL_0:       /* Basic Control 0 */
> +        value = s->basic_ctl0;
> +        break;
> +    case REG_BASIC_CTL_1:       /* Basic Control 1 */
> +        value = s->basic_ctl1;
> +        break;
> +    case REG_INT_STA:           /* Interrupt Status */
> +        value = s->int_sta;
> +        break;
> +    case REG_INT_EN:            /* Interupt Enable */
> +        value = s->int_en;
> +        break;
> +    case REG_TX_CTL_0:          /* Transmit Control 0 */
> +        value = s->tx_ctl0;
> +        break;
> +    case REG_TX_CTL_1:          /* Transmit Control 1 */
> +        value = s->tx_ctl1;
> +        break;
> +    case REG_TX_FLOW_CTL:       /* Transmit Flow Control */
> +        value = s->tx_flowctl;
> +        break;
> +    case REG_TX_DMA_DESC_LIST:  /* Transmit Descriptor List Address */
> +        value = s->tx_desc_head;
> +        break;
> +    case REG_RX_CTL_0:          /* Receive Control 0 */
> +        value = s->rx_ctl0;
> +        break;
> +    case REG_RX_CTL_1:          /* Receive Control 1 */
> +        value = s->rx_ctl1;
> +        break;
> +    case REG_RX_DMA_DESC_LIST:  /* Receive Descriptor List Address */
> +        value = s->rx_desc_head;
> +        break;
> +    case REG_FRM_FLT:           /* Receive Frame Filter */
> +        value = s->frm_flt;
> +        break;
> +    case REG_RX_HASH_0:         /* Receive Hash Table 0 */
> +    case REG_RX_HASH_1:         /* Receive Hash Table 1 */
> +        break;
> +    case REG_MII_CMD:           /* Management Interface Command */
> +        value = s->mii_cmd;
> +        break;
> +    case REG_MII_DATA:          /* Management Interface Data */
> +        value = s->mii_data;
> +        break;
> +    case REG_ADDR_HIGH:         /* MAC Address High */
> +        value = *(((uint32_t *) (s->conf.macaddr.a)) + 1);
> +        break;
> +    case REG_ADDR_LOW:          /* MAC Address Low */
> +        value = *(uint32_t *) (s->conf.macaddr.a);
> +        break;
> +    case REG_TX_DMA_STA:        /* Transmit DMA Status */
> +        break;
> +    case REG_TX_CUR_DESC:       /* Transmit Current Descriptor */
> +        value = s->tx_desc_curr;
> +        break;
> +    case REG_TX_CUR_BUF:        /* Transmit Current Buffer */
> +        if (s->tx_desc_curr != 0) {
> +            cpu_physical_memory_read(s->tx_desc_curr, &desc, sizeof(desc));
> +            value = desc.addr;
> +        } else {
> +            value = 0;
> +        }
> +        break;
> +    case REG_RX_DMA_STA:        /* Receive DMA Status */
> +        break;
> +    case REG_RX_CUR_DESC:       /* Receive Current Descriptor */
> +        value = s->rx_desc_curr;
> +        break;
> +    case REG_RX_CUR_BUF:        /* Receive Current Buffer */
> +        if (s->rx_desc_curr != 0) {
> +            cpu_physical_memory_read(s->rx_desc_curr, &desc, sizeof(desc));
> +            value = desc.addr;
> +        } else {
> +            value = 0;
> +        }
> +        break;
> +    default:
> +        qemu_log_mask(LOG_UNIMP, "allwinner-h3-emac: read access to unknown "
> +                                 "EMAC register 0x" TARGET_FMT_plx "\n",
> +                                  offset);
> +    }
> +
> +    trace_aw_h3_emac_read(offset, value);
> +    return value;
> +}
> +
> +static void aw_h3_emac_write(void *opaque, hwaddr offset, uint64_t value,
> +                             unsigned size)
> +{
> +    AwH3EmacState *s = opaque;

The same.

> +    NetClientState *nc = qemu_get_queue(s->nic);
> +
> +    trace_aw_h3_emac_write(offset, value);
> +
> +    switch (offset) {
> +    case REG_BASIC_CTL_0:       /* Basic Control 0 */
> +        s->basic_ctl0 = value;
> +        break;
> +    case REG_BASIC_CTL_1:       /* Basic Control 1 */
> +        if (value & BASIC_CTL1_SOFTRST) {
> +            aw_h3_emac_reset(DEVICE(s));
> +            value &= ~BASIC_CTL1_SOFTRST;
> +        }
> +        s->basic_ctl1 = value;
> +        if (aw_h3_emac_can_receive(nc)) {
> +            qemu_flush_queued_packets(nc);
> +        }
> +        break;
> +    case REG_INT_STA:           /* Interrupt Status */
> +        s->int_sta &= ~value;
> +        aw_h3_emac_update_irq(s);
> +        break;
> +    case REG_INT_EN:            /* Interrupt Enable */
> +        s->int_en = value;
> +        aw_h3_emac_update_irq(s);
> +        break;
> +    case REG_TX_CTL_0:          /* Transmit Control 0 */
> +        s->tx_ctl0 = value;
> +        break;
> +    case REG_TX_CTL_1:          /* Transmit Control 1 */
> +        s->tx_ctl1 = value;
> +        if (value & TX_CTL1_TX_DMA_EN) {
> +            aw_h3_emac_transmit(s);
> +        }
> +        break;
> +    case REG_TX_FLOW_CTL:       /* Transmit Flow Control */
> +        s->tx_flowctl = value;
> +        break;
> +    case REG_TX_DMA_DESC_LIST:  /* Transmit Descriptor List Address */
> +        s->tx_desc_head = value;
> +        s->tx_desc_curr = value;
> +        break;
> +    case REG_RX_CTL_0:          /* Receive Control 0 */
> +        s->rx_ctl0 = value;
> +        break;
> +    case REG_RX_CTL_1:          /* Receive Control 1 */
> +        s->rx_ctl1 = value | RX_CTL1_RX_MD;
> +        if ((value & RX_CTL1_RX_DMA_EN) && aw_h3_emac_can_receive(nc)) {
> +            qemu_flush_queued_packets(nc);
> +        }
> +        break;
> +    case REG_RX_DMA_DESC_LIST:  /* Receive Descriptor List Address */
> +        s->rx_desc_head = value;
> +        s->rx_desc_curr = value;
> +        break;
> +    case REG_FRM_FLT:           /* Receive Frame Filter */
> +        s->frm_flt = value;
> +        break;
> +    case REG_RX_HASH_0:         /* Receive Hash Table 0 */
> +    case REG_RX_HASH_1:         /* Receive Hash Table 1 */
> +        break;
> +    case REG_MII_CMD:           /* Management Interface Command */
> +        s->mii_cmd = value & ~MII_CMD_PHY_BUSY;
> +        aw_h3_emac_mii_cmd(s);
> +        break;
> +    case REG_MII_DATA:          /* Management Interface Data */
> +        s->mii_data = value;
> +        break;
> +    case REG_ADDR_HIGH:         /* MAC Address High */
> +        s->conf.macaddr.a[4] = (value & 0xff);
> +        s->conf.macaddr.a[5] = (value & 0xff00) >> 8;
> +        break;
> +    case REG_ADDR_LOW:          /* MAC Address Low */
> +        s->conf.macaddr.a[0] = (value & 0xff);
> +        s->conf.macaddr.a[1] = (value & 0xff00) >> 8;
> +        s->conf.macaddr.a[2] = (value & 0xff0000) >> 16;
> +        s->conf.macaddr.a[3] = (value & 0xff000000) >> 24;
> +        break;
> +    case REG_TX_DMA_STA:        /* Transmit DMA Status */
> +    case REG_TX_CUR_DESC:       /* Transmit Current Descriptor */
> +    case REG_TX_CUR_BUF:        /* Transmit Current Buffer */
> +    case REG_RX_DMA_STA:        /* Receive DMA Status */
> +    case REG_RX_CUR_DESC:       /* Receive Current Descriptor */
> +    case REG_RX_CUR_BUF:        /* Receive Current Buffer */
> +    case REG_RGMII_STA:         /* RGMII Status */
> +        break;
> +    default:
> +        qemu_log_mask(LOG_UNIMP, "allwinner-h3-emac: write access to unknown "
> +                                 "EMAC register 0x" TARGET_FMT_plx "\n",
> +                                  offset);
> +    }
> +}
> +
> +static void aw_h3_emac_set_link(NetClientState *nc)
> +{
> +    AwH3EmacState *s = qemu_get_nic_opaque(nc);
> +
> +    trace_aw_h3_emac_set_link(!nc->link_down);
> +    aw_h3_emac_mii_set_link(s, !nc->link_down);
> +}
> +
> +static const MemoryRegionOps aw_h3_emac_mem_ops = {
> +    .read = aw_h3_emac_read,
> +    .write = aw_h3_emac_write,
> +    .endianness = DEVICE_NATIVE_ENDIAN,
> +    .valid = {
> +        .min_access_size = 4,
> +        .max_access_size = 4,
> +    },
> +};
> +
> +static NetClientInfo net_aw_h3_emac_info = {
> +    .type = NET_CLIENT_DRIVER_NIC,
> +    .size = sizeof(NICState),
> +    .can_receive = aw_h3_emac_can_receive,
> +    .receive = aw_h3_emac_receive,
> +    .link_status_changed = aw_h3_emac_set_link,
> +};
> +
> +static void aw_h3_emac_init(Object *obj)
> +{
> +    SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
> +    AwH3EmacState *s = AW_H3_EMAC(obj);
> +
> +    memory_region_init_io(&s->iomem, OBJECT(s), &aw_h3_emac_mem_ops, s,
> +                          TYPE_AW_H3_EMAC, AW_H3_EMAC_REGS_MEM_SIZE);
> +    sysbus_init_mmio(sbd, &s->iomem);
> +    sysbus_init_irq(sbd, &s->irq);
> +}
> +
> +static void aw_h3_emac_realize(DeviceState *dev, Error **errp)
> +{
> +    AwH3EmacState *s = AW_H3_EMAC(dev);
> +
> +    qemu_macaddr_default_if_unset(&s->conf.macaddr);
> +    s->nic = qemu_new_nic(&net_aw_h3_emac_info, &s->conf,
> +                          object_get_typename(OBJECT(dev)), dev->id, s);
> +    qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a);
> +}
> +
> +static Property aw_h3_emac_properties[] = {
> +    DEFINE_NIC_PROPERTIES(AwH3EmacState, conf),
> +    DEFINE_PROP_UINT8("phy-addr", AwH3EmacState, mii_phy_addr, 0),
> +    DEFINE_PROP_END_OF_LIST(),
> +};
> +
> +static int aw_h3_emac_post_load(void *opaque, int version_id)
> +{
> +    AwH3EmacState *s = opaque;
> +
> +    aw_h3_emac_set_link(qemu_get_queue(s->nic));
> +
> +    return 0;
> +}
> +
> +static const VMStateDescription vmstate_aw_emac = {
> +    .name = TYPE_AW_H3_EMAC,
> +    .version_id = 1,
> +    .minimum_version_id = 1,
> +    .post_load = aw_h3_emac_post_load,
> +    .fields = (VMStateField[]) {
> +        VMSTATE_UINT8(mii_phy_addr, AwH3EmacState),
> +        VMSTATE_UINT32(mii_cmd, AwH3EmacState),
> +        VMSTATE_UINT32(mii_data, AwH3EmacState),
> +        VMSTATE_UINT32(basic_ctl0, AwH3EmacState),
> +        VMSTATE_UINT32(basic_ctl1, AwH3EmacState),
> +        VMSTATE_UINT32(int_en, AwH3EmacState),
> +        VMSTATE_UINT32(int_sta, AwH3EmacState),
> +        VMSTATE_UINT32(frm_flt, AwH3EmacState),
> +        VMSTATE_UINT32(rx_ctl0, AwH3EmacState),
> +        VMSTATE_UINT32(rx_ctl1, AwH3EmacState),
> +        VMSTATE_UINT32(rx_desc_head, AwH3EmacState),
> +        VMSTATE_UINT32(rx_desc_curr, AwH3EmacState),
> +        VMSTATE_UINT32(tx_ctl0, AwH3EmacState),
> +        VMSTATE_UINT32(tx_ctl1, AwH3EmacState),
> +        VMSTATE_UINT32(tx_desc_head, AwH3EmacState),
> +        VMSTATE_UINT32(tx_desc_curr, AwH3EmacState),
> +        VMSTATE_UINT32(tx_flowctl, AwH3EmacState),
> +        VMSTATE_END_OF_LIST()
> +    }
> +};
> +
> +static void aw_h3_emac_class_init(ObjectClass *klass, void *data)
> +{
> +    DeviceClass *dc = DEVICE_CLASS(klass);
> +
> +    dc->realize = aw_h3_emac_realize;
> +    dc->props = aw_h3_emac_properties;
> +    dc->reset = aw_h3_emac_reset;
> +    dc->vmsd = &vmstate_aw_emac;
> +}
> +
> +static const TypeInfo aw_h3_emac_info = {
> +    .name           = TYPE_AW_H3_EMAC,
> +    .parent         = TYPE_SYS_BUS_DEVICE,
> +    .instance_size  = sizeof(AwH3EmacState),
> +    .instance_init  = aw_h3_emac_init,
> +    .class_init     = aw_h3_emac_class_init,
> +};
> +
> +static void aw_h3_emac_register_types(void)
> +{
> +    type_register_static(&aw_h3_emac_info);
> +}
> +
> +type_init(aw_h3_emac_register_types)
> diff --git a/hw/net/trace-events b/hw/net/trace-events
> index e70f12bee1..e9e2f26f68 100644
> --- a/hw/net/trace-events
> +++ b/hw/net/trace-events
> @@ -1,5 +1,15 @@
>   # See docs/devel/tracing.txt for syntax documentation.
>   
> +# allwinner-h3-emac.c
> +aw_h3_emac_mii_write_reg(uint32_t reg, uint32_t value) "MII write: reg=0x%x value=0x%x"
> +aw_h3_emac_mii_read_reg(uint32_t reg, uint32_t value) "MII read: reg=0x%x value=0x%x"
> +aw_h3_emac_receive(uint32_t desc, uint32_t paddr, uint32_t bytes) "RX packet: desc=0x%08x paddr=0x%08x bytes=%u"
> +aw_h3_emac_transmit(uint32_t desc, uint32_t paddr, uint32_t bytes) "TX packet: desc=0x%08x paddr=0x%08x bytes=%u"
> +aw_h3_emac_reset(void) "HW reset"
> +aw_h3_emac_set_link(bool active) "Set link: active=%u"
> +aw_h3_emac_read(uint64_t offset, uint64_t val) "MMIO read: offset=0x%" PRIx64 " value=0x%" PRIx64
> +aw_h3_emac_write(uint64_t offset, uint64_t val) "MMIO write: offset=0x%" PRIx64 " value=0x%" PRIx64
> +
>   # etraxfs_eth.c
>   mdio_phy_read(int regnum, uint16_t value) "read phy_reg:%d value:0x%04x"
>   mdio_phy_write(int regnum, uint16_t value) "write phy_reg:%d value:0x%04x"
> diff --git a/include/hw/arm/allwinner-h3.h b/include/hw/arm/allwinner-h3.h
> index 7aff4ebbd2..b964a60f41 100644
> --- a/include/hw/arm/allwinner-h3.h
> +++ b/include/hw/arm/allwinner-h3.h
> @@ -31,6 +31,7 @@
>   #include "hw/misc/allwinner-h3-syscon.h"
>   #include "hw/misc/allwinner-h3-sid.h"
>   #include "hw/sd/allwinner-h3-sdhost.h"
> +#include "hw/net/allwinner-h3-emac.h"
>   #include "target/arm/cpu.h"
>   
>   #define AW_H3_SRAM_A1_BASE     (0x00000000)
> @@ -119,6 +120,7 @@ typedef struct AwH3State {
>       AwH3SysconState syscon;
>       AwH3SidState sid;
>       AwH3SDHostState mmc0;
> +    AwH3EmacState emac;
>       GICState gic;
>       MemoryRegion sram_a1;
>       MemoryRegion sram_a2;
> diff --git a/include/hw/net/allwinner-h3-emac.h b/include/hw/net/allwinner-h3-emac.h
> new file mode 100644
> index 0000000000..a007d54472
> --- /dev/null
> +++ b/include/hw/net/allwinner-h3-emac.h
> @@ -0,0 +1,69 @@
> +/*
> + * Allwinner H3 EMAC 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/>.
> + */
> +
> +#ifndef ALLWINNER_H3_EMAC_H
> +#define ALLWINNER_H3_EMAC_H
> +
> +#include "qemu/units.h"
> +#include "net/net.h"
> +#include "qemu/fifo8.h"
> +#include "hw/net/mii.h"
> +#include "hw/sysbus.h"
> +
> +#define AW_H3_EMAC_REGS_MEM_SIZE  (1024)
> +
> +#define TYPE_AW_H3_EMAC "allwinner-h3-emac"
> +#define AW_H3_EMAC(obj) OBJECT_CHECK(AwH3EmacState, (obj), TYPE_AW_H3_EMAC)
> +
> +typedef struct AwH3EmacState {
> +    /*< private >*/
> +    SysBusDevice  parent_obj;
> +    /*< public >*/
> +
> +    MemoryRegion iomem;
> +    qemu_irq     irq;
> +    NICState     *nic;
> +    NICConf      conf;
> +
> +    uint8_t      mii_phy_addr;
> +    uint32_t     mii_cmd;
> +    uint32_t     mii_data;
> +    uint32_t     mii_cr;
> +    uint32_t     mii_st;
> +
> +    uint32_t     basic_ctl0;
> +    uint32_t     basic_ctl1;
> +    uint32_t     int_en;
> +    uint32_t     int_sta;
> +    uint32_t     frm_flt;
> +
> +    uint32_t     rx_ctl0;
> +    uint32_t     rx_ctl1;
> +    uint32_t     rx_desc_head;
> +    uint32_t     rx_desc_curr;
> +
> +    uint32_t     tx_ctl0;
> +    uint32_t     tx_ctl1;
> +    uint32_t     tx_desc_head;
> +    uint32_t     tx_desc_curr;
> +    uint32_t     tx_flowctl;
> +
> +} AwH3EmacState;
> +
> +#endif
> 

The rest seems ok to me. Thanks for the contribution :)!

Cheers,
Fred



^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH 00/10] Add Allwinner H3 SoC and Orange Pi PC Machine
  2019-12-03  8:47 ` [PATCH 00/10] Add Allwinner H3 SoC and Orange Pi PC Machine Philippe Mathieu-Daudé
@ 2019-12-03 19:25   ` Niek Linnenbank
  0 siblings, 0 replies; 33+ messages in thread
From: Niek Linnenbank @ 2019-12-03 19:25 UTC (permalink / raw)
  To: Philippe Mathieu-Daudé
  Cc: b.galvani, Peter Maydell, qemu-arm, Richard Henderson, qemu-devel

[-- Attachment #1: Type: text/plain, Size: 12881 bytes --]

Hi Philippe,

Thanks for your very quick response!
I remember I have seen this error before while working on the patches, in
particular
on the SMP part. I'll try to reproduce this error with the 4.20 sunxi
kernel you used and debug it.

Could it be related to the change I made in patch 0006 for the CP10/CP11
bits?
Basically I needed to add that to get the CPUCFG module working. It is an
interface
that U-Boot uses to reset the secondary cores for PSCI functionality. I used
the arm_set_cpu_on() function there to reset the cores at the desired start
address,
but Im not sure if that function is the right choice. At some point while
rebasing the patches,
I got undefined exceptions which turned out to be because of the CP10/CP11
bits missing.
If I made an obvious mistake there, please let me know and I'll correct it.

Regards,
Niek


On Tue, Dec 3, 2019 at 9:47 AM Philippe Mathieu-Daudé <philmd@redhat.com>
wrote:

> On 12/2/19 10:09 PM, Niek Linnenbank wrote:
> > Dear QEMU developers,
> >
> > Hereby I would like to contribute the following set of patches to QEMU
> > which add support for the Allwinner H3 System on Chip and the
> > Orange Pi PC machine. The following features and devices are supported:
> >
> >   * SMP (Quad Core Cortex A7)
> >   * Generic Interrupt Controller configuration
> >   * SRAM mappings
> >   * Timer device (re-used from Allwinner A10)
> >   * UART
> >   * SD/MMC storage controller
> >   * EMAC ethernet connectivity
> >   * USB 2.0 interfaces
> >   * Clock Control Unit
> >   * System Control module
> >   * Security Identifier device
>
> Awesome!
>
> > Functionality related to graphical output such as HDMI, GPU,
> > Display Engine and audio are not included. Recently released
> > mainline Linux kernels (4.19 up to latest master) and mainline U-Boot
> > are known to work. The SD/MMC code is tested using bonnie++ and
> > various tools such as fsck, dd and fdisk. The EMAC is verified with
> iperf3
> > using -netdev socket.
> >
> > 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:
> >   $ 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:
> >   $ 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:
> >   $ ARCH=arm CROSS_COMPILE=arm-linux-gnueabi- make -j5
> >
> > To boot the newly build linux kernel in QEMU with the Orange Pi PC
> machine, use:
> >   $ qemu-system-arm -M orangepi -m 512 -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
> >
> > Note that this kernel does not have a root filesystem. You may provide it
> > with an official Orange Pi PC image [1] either as an SD card or as
> > USB mass storage. To boot using the Orange Pi PC Debian image on SD card,
> > simply add the -sd argument and provide the proper root= kernel
> parameter:
> >   $ qemu-system-arm -M orangepi -m 512 -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
> >
> > Alternatively, you can also choose to build and boot a recent buildroot
> [2]
> > using the orangepi_pc_defconfig or Armbian image [3] for Orange Pi PC.
>
> Richard, trying the Armbian image from
> https://apt.armbian.com/pool/main/l/linux-4.20.7-sunxi/ I get:
>
> $ arm-softmmu/qemu-system-arm -M orangepi -m 512 -nic user \
>    -append 'console=ttyS0,115200' \
>    -kernel boot/vmlinuz-4.20.7-sunxi \
>    -dtb usr/lib/linux-image-dev-sunxi/sun8i-h3-orangepi-pc.dtb \
>    -serial stdio -d unimp
> Uncompressing Linux... done, booting the kernel.
> rtc: unimplemented device write (size 4, value 0x16aa0001, offset 0x0)
> rtc: unimplemented device read (size 4, offset 0x0)
> rtc: unimplemented device read (size 4, offset 0x0)
> rtc: unimplemented device read (size 4, offset 0x8)
> qemu-system-arm: target/arm/helper.c:11359: cpu_get_tb_cpu_state:
> Assertion `flags == rebuild_hflags_internal(env)' failed.
> Aborted (core dumped)
>
> (gdb) bt
> #0  0x00007f6c1fa2ce35 in raise () at /lib64/libc.so.6
> #1  0x00007f6c1fa17895 in abort () at /lib64/libc.so.6
> #2  0x00007f6c1fa17769 in _nl_load_domain.cold () at /lib64/libc.so.6
> #3  0x00007f6c1fa25566 in annobin_assert.c_end () at /lib64/libc.so.6
> #4  0x00005590657e2685 in cpu_get_tb_cpu_state (env=0x5590686899b0,
> pc=0x7f6c07ffa718, cs_base=0x7f6c07ffa714, pflags=0x7f6c07ffa71c) at
> target/arm/helper.c:11359
> #5  0x000055906569f962 in tb_lookup__cpu_state (cpu=0x5590686808b0,
> pc=0x7f6c07ffa718, cs_base=0x7f6c07ffa714, flags=0x7f6c07ffa71c,
> cf_mask=524288) at include/exec/tb-lookup.h:28
> #6  0x00005590656a084c in tb_find (cpu=0x5590686808b0, last_tb=0x0,
> tb_exit=0, cf_mask=524288) at accel/tcg/cpu-exec.c:403
> #7  0x00005590656a114a in cpu_exec (cpu=0x5590686808b0) at
> accel/tcg/cpu-exec.c:730
> #8  0x000055906565f6af in tcg_cpu_exec (cpu=0x5590686808b0) at cpus.c:1473
> #9  0x000055906565ff05 in qemu_tcg_cpu_thread_fn (arg=0x5590686808b0) at
> cpus.c:1781
> #10 0x0000559065d54aa6 in qemu_thread_start (args=0x5590687d8c20) at
> util/qemu-thread-posix.c:519
> #11 0x00007f6c1fbc54c0 in start_thread () at /lib64/libpthread.so.0
> #12 0x00007f6c1faf1553 in clone () at /lib64/libc.so.6
>
> (gdb) p/x flags
> $1 = 0x33600000
>
> (gdb) p/x *env
> $2 = {regs = {0x0 <repeats 15 times>, 0x40102448}, xregs = {0x0 <repeats
> 32 times>}, pc = 0x0, pstate = 0x0, aarch64 = 0x0, hflags = 0x33600000,
> uncached_cpsr = 0x1a, spsr = 0x0, banked_spsr = {0x0, 0x0, 0x0, 0x0,
> 0x0, 0x0, 0x0, 0x0},
>    banked_r13 = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, banked_r14 =
> {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, usr_regs = {0x0, 0x0, 0x0,
> 0x0, 0x0}, fiq_regs = {0x0, 0x0, 0x0, 0x0, 0x0}, CF = 0x0, VF = 0x0, NF
> = 0x0, ZF = 0x0,
>    QF = 0x0, GE = 0x0, thumb = 0x1, condexec_bits = 0x0, btype = 0x0,
> daif = 0x3c0, elr_el = {0x0, 0x0, 0x0, 0x0}, sp_el = {0x0, 0x0, 0x0,
> 0x0}, cp15 = {c0_cpuid = 0x410fc075, {{_unused_csselr0 = 0x0, csselr_ns
> = 0x0,
>          _unused_csselr1 = 0x0, csselr_s = 0x0}, csselr_el = {0x0, 0x0,
> 0x0, 0x0}}, {{_unused_sctlr = 0x0, sctlr_ns = 0xc50078, hsctlr = 0x0,
> sctlr_s = 0xc50078}, sctlr_el = {0x0, 0xc50078, 0x0, 0xc50078}},
> cpacr_el1 = 0x0, cptr_el = {
>        0x0, 0x0, 0x0, 0x0}, c1_xscaleauxcr = 0x0, sder = 0x0, nsacr =
> 0xc00, {{_unused_ttbr0_0 = 0x0, ttbr0_ns = 0x0, _unused_ttbr0_1 = 0x0,
> ttbr0_s = 0x0}, ttbr0_el = {0x0, 0x0, 0x0, 0x0}}, {{_unused_ttbr1_0 =
> 0x0, ttbr1_ns = 0x0,
>          _unused_ttbr1_1 = 0x0, ttbr1_s = 0x0}, ttbr1_el = {0x0, 0x0,
> 0x0, 0x0}}, vttbr_el2 = 0x0, tcr_el = {{raw_tcr = 0x0, mask = 0x0,
> base_mask = 0x0}, {raw_tcr = 0x0, mask = 0x0, base_mask = 0xffffc000},
> {raw_tcr = 0x0, mask = 0x0,
>          base_mask = 0x0}, {raw_tcr = 0x0, mask = 0x0, base_mask =
> 0xffffc000}}, vtcr_el2 = {raw_tcr = 0x0, mask = 0x0, base_mask = 0x0},
> c2_data = 0x0, c2_insn = 0x0, {{dacr_ns = 0x0, dacr_s = 0x0},
> {dacr32_el2 = 0x0}},
>      pmsav5_data_ap = 0x0, pmsav5_insn_ap = 0x0, hcr_el2 = 0x0, scr_el3
> = 0x101, {{ifsr_ns = 0x0, ifsr_s = 0x0}, {ifsr32_el2 = 0x0}},
> {{_unused_dfsr = 0x0, dfsr_ns = 0x0, hsr = 0x0, dfsr_s = 0x0}, esr_el =
> {0x0, 0x0, 0x0, 0x0}},
>      c6_region = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
> {{_unused_far0 = 0x0, dfar_ns = 0x0, ifar_ns = 0x0, dfar_s = 0x0, ifar_s
> = 0x0, _unused_far3 = 0x0}, far_el = {0x0, 0x0, 0x0, 0x0}}, hpfar_el2 =
> 0x0, hstr_el2 = 0x0, {{
>          _unused_par_0 = 0x0, par_ns = 0x0, _unused_par_1 = 0x0, par_s =
> 0x0}, par_el = {0x0, 0x0, 0x0, 0x0}}, c9_insn = 0x0, c9_data = 0x0,
> c9_pmcr = 0x41002000, c9_pmcnten = 0x0, c9_pmovsr = 0x0, c9_pmuserenr =
> 0x0, c9_pmselr = 0x0,
>      c9_pminten = 0x0, {{_unused_mair_0 = 0x0, mair0_ns = 0x0, mair1_ns
> = 0x0, _unused_mair_1 = 0x0, mair0_s = 0x0, mair1_s = 0x0}, mair_el =
> {0x0, 0x0, 0x0, 0x0}}, {{_unused_vbar = 0x0, vbar_ns = 0x0, hvbar = 0x0,
> vbar_s = 0x0},
>        vbar_el = {0x0, 0x0, 0x0, 0x0}}, mvbar = 0x0, {fcseidr_ns = 0x0,
> fcseidr_s = 0x0}, {{_unused_contextidr_0 = 0x0, contextidr_ns = 0x0,
> _unused_contextidr_1 = 0x0, contextidr_s = 0x0}, contextidr_el = {0x0,
> 0x0, 0x0, 0x0}}, {{
>          tpidrurw_ns = 0x0, tpidrprw_ns = 0x0, htpidr = 0x0, _tpidr_el3
> = 0x0}, tpidr_el = {0x0, 0x0, 0x0, 0x0}}, tpidrurw_s = 0x0, tpidrprw_s =
> 0x0, tpidruro_s = 0x0, {tpidruro_ns = 0x0, tpidrro_el = {0x0}},
> c14_cntfrq = 0x3b9aca0,
>      c14_cntkctl = 0x0, cnthctl_el2 = 0x3, cntvoff_el2 = 0x0, c14_timer
> = {{cval = 0x0, ctl = 0x0}, {cval = 0x0, ctl = 0x0}, {cval = 0x0, ctl =
> 0x0}, {cval = 0x0, ctl = 0x0}}, c15_cpar = 0x0, c15_ticonfig = 0x0,
> c15_i_max = 0x0,
>      c15_i_min = 0x0, c15_threadid = 0x0, c15_config_base_address = 0x0,
> c15_diagnostic = 0x0, c15_power_diagnostic = 0x0, c15_power_control =
> 0x0, dbgbvr = {0x0 <repeats 16 times>}, dbgbcr = {0x0 <repeats 16
> times>}, dbgwvr = {
>        0x0 <repeats 16 times>}, dbgwcr = {0x0 <repeats 16 times>},
> mdscr_el1 = 0x0, oslsr_el1 = 0xa, mdcr_el2 = 0x0, mdcr_el3 = 0x0,
> c15_ccnt = 0x0, c15_ccnt_delta = 0x0, c14_pmevcntr = {0x0 <repeats 31
> times>}, c14_pmevcntr_delta = {
>        0x0 <repeats 31 times>}, c14_pmevtyper = {0x0 <repeats 31
> times>}, pmccfiltr_el0 = 0x0, vpidr_el2 = 0x410fc075, vmpidr_el2 =
> 0x80000001}, v7m = {other_sp = 0x0, other_ss_msp = 0x0, other_ss_psp =
> 0x0, vecbase = {0x0, 0x0},
>      basepri = {0x0, 0x0}, control = {0x0, 0x0}, ccr = {0x0, 0x0}, cfsr
> = {0x0, 0x0}, hfsr = 0x0, dfsr = 0x0, sfsr = 0x0, mmfar = {0x0, 0x0},
> bfar = 0x0, sfar = 0x0, mpu_ctrl = {0x0, 0x0}, exception = 0x0, primask
> = {0x0, 0x0},
>      faultmask = {0x0, 0x0}, aircr = 0x0, secure = 0x0, csselr = {0x0,
> 0x0}, scr = {0x0, 0x0}, msplim = {0x0, 0x0}, psplim = {0x0, 0x0}, fpcar
> = {0x0, 0x0}, fpccr = {0x0, 0x0}, fpdscr = {0x0, 0x0}, cpacr = {0x0,
> 0x0}, nsacr = 0x0},
>    exception = {syndrome = 0x0, fsr = 0x0, vaddress = 0x0, target_el =
> 0x0}, serror = {pending = 0x0, has_esr = 0x0, esr = 0x0}, irq_line_state
> = 0x0, teecr = 0x0, teehbr = 0x0, vfp = {zregs = {{d = {0x0, 0x0}}
> <repeats 32 times>},
>      qc = {0x0, 0x0, 0x0, 0x0}, vec_len = 0x0, vec_stride = 0x0, xregs =
> {0x41023075, 0x0, 0x0, 0x0, 0x0, 0x0, 0x11111111, 0x10110222, 0x0, 0x0,
> 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, scratch = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
> 0x0, 0x0},
>      fp_status = {float_detect_tininess = 0x1, float_rounding_mode =
> 0x0, float_exception_flags = 0x0, floatx80_rounding_precision = 0x0,
> flush_to_zero = 0x0, flush_inputs_to_zero = 0x0, default_nan_mode = 0x0,
> snan_bit_is_one = 0x0},
>      fp_status_f16 = {float_detect_tininess = 0x1, float_rounding_mode =
> 0x0, float_exception_flags = 0x0, floatx80_rounding_precision = 0x0,
> flush_to_zero = 0x0, flush_inputs_to_zero = 0x0, default_nan_mode = 0x0,
>        snan_bit_is_one = 0x0}, standard_fp_status =
> {float_detect_tininess = 0x1, float_rounding_mode = 0x0,
> float_exception_flags = 0x0, floatx80_rounding_precision = 0x0,
> flush_to_zero = 0x1, flush_inputs_to_zero = 0x1,
>        default_nan_mode = 0x1, snan_bit_is_one = 0x0}, zcr_el = {0x0,
> 0x0, 0x0, 0x0}}, exclusive_addr = 0xffffffffffffffff, exclusive_val =
> 0x0, exclusive_high = 0x0, iwmmxt = {regs = {0x0 <repeats 16 times>},
> val = 0x0, cregs = {
>        0x0 <repeats 16 times>}}, cpu_breakpoint = {0x0 <repeats 16
> times>}, cpu_watchpoint = {0x0 <repeats 16 times>}, end_reset_fields =
> {<No data fields>}, features = 0xfd38fbe6f3, pmsav7 = {drbar = 0x0, drsr
> = 0x0, dracr = 0x0,
>      rnr = {0x0, 0x0}}, pmsav8 = {rbar = {0x0, 0x0}, rlar = {0x0, 0x0},
> mair0 = {0x0, 0x0}, mair1 = {0x0, 0x0}}, sau = {rbar = 0x0, rlar = 0x0,
> rnr = 0x0, ctrl = 0x0}, nvic = 0x0, boot_info = 0x5622af3a17a0,
> gicv3state = 0x0}
>
> > [1] http://www.orangepi.org/downloadresources/
> > [2] https://buildroot.org/download.html
> > [3] https://www.armbian.com/orange-pi-pc/
>
>

-- 
Niek Linnenbank

WWW: http://www.nieklinnenbank.nl/
BLOG: http://nieklinnenbank.wordpress.com/
FUN:    http://www.FreeNOS.org/

[-- Attachment #2: Type: text/html, Size: 14959 bytes --]

<div dir="ltr"><div>Hi Philippe,</div><div><br></div><div>Thanks for your very quick response!</div><div>I remember I have seen this error before while working on the patches, in particular</div><div>on the SMP part. I&#39;ll try to reproduce this error with the 4.20 sunxi kernel you used and debug it.</div><div> <br></div><div>Could it be related to the change I made in patch 0006 for the CP10/CP11 bits? <br></div><div>Basically I needed to add that to get the CPUCFG module working. It is an interface</div><div>that U-Boot uses to reset the secondary cores for PSCI functionality. I used</div><div>the arm_set_cpu_on() function there to reset the cores at the desired start address,</div><div>but Im not sure if that function is the right choice. At some point while rebasing the patches,</div><div>I got undefined exceptions which turned out to be because of the CP10/CP11 bits missing.</div><div>If I made an obvious mistake there, please let me know and I&#39;ll correct it.<br></div><div><br></div><div>Regards,</div><div>Niek<br></div><div><br></div></div><br><div class="gmail_quote"><div dir="ltr" class="gmail_attr">On Tue, Dec 3, 2019 at 9:47 AM Philippe Mathieu-Daudé &lt;<a href="mailto:philmd@redhat.com">philmd@redhat.com</a>&gt; wrote:<br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">On 12/2/19 10:09 PM, Niek Linnenbank wrote:<br>
&gt; Dear QEMU developers,<br>
&gt; <br>
&gt; Hereby I would like to contribute the following set of patches to QEMU<br>
&gt; which add support for the Allwinner H3 System on Chip and the<br>
&gt; Orange Pi PC machine. The following features and devices are supported:<br>
&gt; <br>
&gt;   * SMP (Quad Core Cortex A7)<br>
&gt;   * Generic Interrupt Controller configuration<br>
&gt;   * SRAM mappings<br>
&gt;   * Timer device (re-used from Allwinner A10)<br>
&gt;   * UART<br>
&gt;   * SD/MMC storage controller<br>
&gt;   * EMAC ethernet connectivity<br>
&gt;   * USB 2.0 interfaces<br>
&gt;   * Clock Control Unit<br>
&gt;   * System Control module<br>
&gt;   * Security Identifier device<br>
<br>
Awesome!<br>
<br>
&gt; Functionality related to graphical output such as HDMI, GPU,<br>
&gt; Display Engine and audio are not included. Recently released<br>
&gt; mainline Linux kernels (4.19 up to latest master) and mainline U-Boot<br>
&gt; are known to work. The SD/MMC code is tested using bonnie++ and<br>
&gt; various tools such as fsck, dd and fdisk. The EMAC is verified with iperf3<br>
&gt; using -netdev socket.<br>
&gt; <br>
&gt; To build a Linux mainline kernel that can be booted by the Orange Pi PC<br>
&gt; machine, simply configure the kernel using the sunxi_defconfig configuration:<br>
&gt;   $ ARCH=arm CROSS_COMPILE=arm-linux-gnueabi- make mrproper<br>
&gt;   $ ARCH=arm CROSS_COMPILE=arm-linux-gnueabi- make sunxi_defconfig<br>
&gt; <br>
&gt; To be able to use USB storage, you need to manually enable the corresponding<br>
&gt; configuration item. Start the kconfig configuration tool:<br>
&gt;   $ ARCH=arm CROSS_COMPILE=arm-linux-gnueabi- make menuconfig<br>
&gt; <br>
&gt; Navigate to the following item, enable it and save your configuration:<br>
&gt;   Device Drivers &gt; USB support &gt; USB Mass Storage support<br>
&gt; <br>
&gt; Build the Linux kernel with:<br>
&gt;   $ ARCH=arm CROSS_COMPILE=arm-linux-gnueabi- make -j5<br>
&gt; <br>
&gt; To boot the newly build linux kernel in QEMU with the Orange Pi PC machine, use:<br>
&gt;   $ qemu-system-arm -M orangepi -m 512 -nic user -nographic \<br>
&gt;       -kernel /path/to/linux/arch/arm/boot/zImage \<br>
&gt;       -append &#39;console=ttyS0,115200&#39; \<br>
&gt;       -dtb /path/to/linux/arch/arm/boot/dts/sun8i-h3-orangepi-pc.dtb<br>
&gt; <br>
&gt; Note that this kernel does not have a root filesystem. You may provide it<br>
&gt; with an official Orange Pi PC image [1] either as an SD card or as<br>
&gt; USB mass storage. To boot using the Orange Pi PC Debian image on SD card,<br>
&gt; simply add the -sd argument and provide the proper root= kernel parameter:<br>
&gt;   $ qemu-system-arm -M orangepi -m 512 -nic user -nographic \<br>
&gt;       -kernel /path/to/linux/arch/arm/boot/zImage \<br>
&gt;       -append &#39;console=ttyS0,115200 root=/dev/mmcblk0p2&#39; \<br>
&gt;       -dtb /path/to/linux/arch/arm/boot/dts/sun8i-h3-orangepi-pc.dtb \<br>
&gt;       -sd OrangePi_pc_debian_stretch_server_linux5.3.5_v1.0.img<br>
&gt; <br>
&gt; Alternatively, you can also choose to build and boot a recent buildroot [2]<br>
&gt; using the orangepi_pc_defconfig or Armbian image [3] for Orange Pi PC.<br>
<br>
Richard, trying the Armbian image from <br>
<a href="https://apt.armbian.com/pool/main/l/linux-4.20.7-sunxi/" rel="noreferrer" target="_blank">https://apt.armbian.com/pool/main/l/linux-4.20.7-sunxi/</a> I get:<br>
<br>
$ arm-softmmu/qemu-system-arm -M orangepi -m 512 -nic user \<br>
   -append &#39;console=ttyS0,115200&#39; \<br>
   -kernel boot/vmlinuz-4.20.7-sunxi \<br>
   -dtb usr/lib/linux-image-dev-sunxi/sun8i-h3-orangepi-pc.dtb \<br>
   -serial stdio -d unimp<br>
Uncompressing Linux... done, booting the kernel.<br>
rtc: unimplemented device write (size 4, value 0x16aa0001, offset 0x0)<br>
rtc: unimplemented device read (size 4, offset 0x0)<br>
rtc: unimplemented device read (size 4, offset 0x0)<br>
rtc: unimplemented device read (size 4, offset 0x8)<br>
qemu-system-arm: target/arm/helper.c:11359: cpu_get_tb_cpu_state: <br>
Assertion `flags == rebuild_hflags_internal(env)&#39; failed.<br>
Aborted (core dumped)<br>
<br>
(gdb) bt<br>
#0  0x00007f6c1fa2ce35 in raise () at /lib64/libc.so.6<br>
#1  0x00007f6c1fa17895 in abort () at /lib64/libc.so.6<br>
#2  0x00007f6c1fa17769 in _nl_load_domain.cold () at /lib64/libc.so.6<br>
#3  0x00007f6c1fa25566 in annobin_assert.c_end () at /lib64/libc.so.6<br>
#4  0x00005590657e2685 in cpu_get_tb_cpu_state (env=0x5590686899b0, <br>
pc=0x7f6c07ffa718, cs_base=0x7f6c07ffa714, pflags=0x7f6c07ffa71c) at <br>
target/arm/helper.c:11359<br>
#5  0x000055906569f962 in tb_lookup__cpu_state (cpu=0x5590686808b0, <br>
pc=0x7f6c07ffa718, cs_base=0x7f6c07ffa714, flags=0x7f6c07ffa71c, <br>
cf_mask=524288) at include/exec/tb-lookup.h:28<br>
#6  0x00005590656a084c in tb_find (cpu=0x5590686808b0, last_tb=0x0, <br>
tb_exit=0, cf_mask=524288) at accel/tcg/cpu-exec.c:403<br>
#7  0x00005590656a114a in cpu_exec (cpu=0x5590686808b0) at <br>
accel/tcg/cpu-exec.c:730<br>
#8  0x000055906565f6af in tcg_cpu_exec (cpu=0x5590686808b0) at cpus.c:1473<br>
#9  0x000055906565ff05 in qemu_tcg_cpu_thread_fn (arg=0x5590686808b0) at <br>
cpus.c:1781<br>
#10 0x0000559065d54aa6 in qemu_thread_start (args=0x5590687d8c20) at <br>
util/qemu-thread-posix.c:519<br>
#11 0x00007f6c1fbc54c0 in start_thread () at /lib64/libpthread.so.0<br>
#12 0x00007f6c1faf1553 in clone () at /lib64/libc.so.6<br>
<br>
(gdb) p/x flags<br>
$1 = 0x33600000<br>
<br>
(gdb) p/x *env<br>
$2 = {regs = {0x0 &lt;repeats 15 times&gt;, 0x40102448}, xregs = {0x0 &lt;repeats <br>
32 times&gt;}, pc = 0x0, pstate = 0x0, aarch64 = 0x0, hflags = 0x33600000, <br>
uncached_cpsr = 0x1a, spsr = 0x0, banked_spsr = {0x0, 0x0, 0x0, 0x0, <br>
0x0, 0x0, 0x0, 0x0},<br>
   banked_r13 = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, banked_r14 = <br>
{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, usr_regs = {0x0, 0x0, 0x0, <br>
0x0, 0x0}, fiq_regs = {0x0, 0x0, 0x0, 0x0, 0x0}, CF = 0x0, VF = 0x0, NF <br>
= 0x0, ZF = 0x0,<br>
   QF = 0x0, GE = 0x0, thumb = 0x1, condexec_bits = 0x0, btype = 0x0, <br>
daif = 0x3c0, elr_el = {0x0, 0x0, 0x0, 0x0}, sp_el = {0x0, 0x0, 0x0, <br>
0x0}, cp15 = {c0_cpuid = 0x410fc075, {{_unused_csselr0 = 0x0, csselr_ns <br>
= 0x0,<br>
         _unused_csselr1 = 0x0, csselr_s = 0x0}, csselr_el = {0x0, 0x0, <br>
0x0, 0x0}}, {{_unused_sctlr = 0x0, sctlr_ns = 0xc50078, hsctlr = 0x0, <br>
sctlr_s = 0xc50078}, sctlr_el = {0x0, 0xc50078, 0x0, 0xc50078}}, <br>
cpacr_el1 = 0x0, cptr_el = {<br>
       0x0, 0x0, 0x0, 0x0}, c1_xscaleauxcr = 0x0, sder = 0x0, nsacr = <br>
0xc00, {{_unused_ttbr0_0 = 0x0, ttbr0_ns = 0x0, _unused_ttbr0_1 = 0x0, <br>
ttbr0_s = 0x0}, ttbr0_el = {0x0, 0x0, 0x0, 0x0}}, {{_unused_ttbr1_0 = <br>
0x0, ttbr1_ns = 0x0,<br>
         _unused_ttbr1_1 = 0x0, ttbr1_s = 0x0}, ttbr1_el = {0x0, 0x0, <br>
0x0, 0x0}}, vttbr_el2 = 0x0, tcr_el = {{raw_tcr = 0x0, mask = 0x0, <br>
base_mask = 0x0}, {raw_tcr = 0x0, mask = 0x0, base_mask = 0xffffc000}, <br>
{raw_tcr = 0x0, mask = 0x0,<br>
         base_mask = 0x0}, {raw_tcr = 0x0, mask = 0x0, base_mask = <br>
0xffffc000}}, vtcr_el2 = {raw_tcr = 0x0, mask = 0x0, base_mask = 0x0}, <br>
c2_data = 0x0, c2_insn = 0x0, {{dacr_ns = 0x0, dacr_s = 0x0}, <br>
{dacr32_el2 = 0x0}},<br>
     pmsav5_data_ap = 0x0, pmsav5_insn_ap = 0x0, hcr_el2 = 0x0, scr_el3 <br>
= 0x101, {{ifsr_ns = 0x0, ifsr_s = 0x0}, {ifsr32_el2 = 0x0}}, <br>
{{_unused_dfsr = 0x0, dfsr_ns = 0x0, hsr = 0x0, dfsr_s = 0x0}, esr_el = <br>
{0x0, 0x0, 0x0, 0x0}},<br>
     c6_region = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, <br>
{{_unused_far0 = 0x0, dfar_ns = 0x0, ifar_ns = 0x0, dfar_s = 0x0, ifar_s <br>
= 0x0, _unused_far3 = 0x0}, far_el = {0x0, 0x0, 0x0, 0x0}}, hpfar_el2 = <br>
0x0, hstr_el2 = 0x0, {{<br>
         _unused_par_0 = 0x0, par_ns = 0x0, _unused_par_1 = 0x0, par_s = <br>
0x0}, par_el = {0x0, 0x0, 0x0, 0x0}}, c9_insn = 0x0, c9_data = 0x0, <br>
c9_pmcr = 0x41002000, c9_pmcnten = 0x0, c9_pmovsr = 0x0, c9_pmuserenr = <br>
0x0, c9_pmselr = 0x0,<br>
     c9_pminten = 0x0, {{_unused_mair_0 = 0x0, mair0_ns = 0x0, mair1_ns <br>
= 0x0, _unused_mair_1 = 0x0, mair0_s = 0x0, mair1_s = 0x0}, mair_el = <br>
{0x0, 0x0, 0x0, 0x0}}, {{_unused_vbar = 0x0, vbar_ns = 0x0, hvbar = 0x0, <br>
vbar_s = 0x0},<br>
       vbar_el = {0x0, 0x0, 0x0, 0x0}}, mvbar = 0x0, {fcseidr_ns = 0x0, <br>
fcseidr_s = 0x0}, {{_unused_contextidr_0 = 0x0, contextidr_ns = 0x0, <br>
_unused_contextidr_1 = 0x0, contextidr_s = 0x0}, contextidr_el = {0x0, <br>
0x0, 0x0, 0x0}}, {{<br>
         tpidrurw_ns = 0x0, tpidrprw_ns = 0x0, htpidr = 0x0, _tpidr_el3 <br>
= 0x0}, tpidr_el = {0x0, 0x0, 0x0, 0x0}}, tpidrurw_s = 0x0, tpidrprw_s = <br>
0x0, tpidruro_s = 0x0, {tpidruro_ns = 0x0, tpidrro_el = {0x0}}, <br>
c14_cntfrq = 0x3b9aca0,<br>
     c14_cntkctl = 0x0, cnthctl_el2 = 0x3, cntvoff_el2 = 0x0, c14_timer <br>
= {{cval = 0x0, ctl = 0x0}, {cval = 0x0, ctl = 0x0}, {cval = 0x0, ctl = <br>
0x0}, {cval = 0x0, ctl = 0x0}}, c15_cpar = 0x0, c15_ticonfig = 0x0, <br>
c15_i_max = 0x0,<br>
     c15_i_min = 0x0, c15_threadid = 0x0, c15_config_base_address = 0x0, <br>
c15_diagnostic = 0x0, c15_power_diagnostic = 0x0, c15_power_control = <br>
0x0, dbgbvr = {0x0 &lt;repeats 16 times&gt;}, dbgbcr = {0x0 &lt;repeats 16 <br>
times&gt;}, dbgwvr = {<br>
       0x0 &lt;repeats 16 times&gt;}, dbgwcr = {0x0 &lt;repeats 16 times&gt;}, <br>
mdscr_el1 = 0x0, oslsr_el1 = 0xa, mdcr_el2 = 0x0, mdcr_el3 = 0x0, <br>
c15_ccnt = 0x0, c15_ccnt_delta = 0x0, c14_pmevcntr = {0x0 &lt;repeats 31 <br>
times&gt;}, c14_pmevcntr_delta = {<br>
       0x0 &lt;repeats 31 times&gt;}, c14_pmevtyper = {0x0 &lt;repeats 31 <br>
times&gt;}, pmccfiltr_el0 = 0x0, vpidr_el2 = 0x410fc075, vmpidr_el2 = <br>
0x80000001}, v7m = {other_sp = 0x0, other_ss_msp = 0x0, other_ss_psp = <br>
0x0, vecbase = {0x0, 0x0},<br>
     basepri = {0x0, 0x0}, control = {0x0, 0x0}, ccr = {0x0, 0x0}, cfsr <br>
= {0x0, 0x0}, hfsr = 0x0, dfsr = 0x0, sfsr = 0x0, mmfar = {0x0, 0x0}, <br>
bfar = 0x0, sfar = 0x0, mpu_ctrl = {0x0, 0x0}, exception = 0x0, primask <br>
= {0x0, 0x0},<br>
     faultmask = {0x0, 0x0}, aircr = 0x0, secure = 0x0, csselr = {0x0, <br>
0x0}, scr = {0x0, 0x0}, msplim = {0x0, 0x0}, psplim = {0x0, 0x0}, fpcar <br>
= {0x0, 0x0}, fpccr = {0x0, 0x0}, fpdscr = {0x0, 0x0}, cpacr = {0x0, <br>
0x0}, nsacr = 0x0},<br>
   exception = {syndrome = 0x0, fsr = 0x0, vaddress = 0x0, target_el = <br>
0x0}, serror = {pending = 0x0, has_esr = 0x0, esr = 0x0}, irq_line_state <br>
= 0x0, teecr = 0x0, teehbr = 0x0, vfp = {zregs = {{d = {0x0, 0x0}} <br>
&lt;repeats 32 times&gt;},<br>
     qc = {0x0, 0x0, 0x0, 0x0}, vec_len = 0x0, vec_stride = 0x0, xregs = <br>
{0x41023075, 0x0, 0x0, 0x0, 0x0, 0x0, 0x11111111, 0x10110222, 0x0, 0x0, <br>
0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, scratch = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, <br>
0x0, 0x0},<br>
     fp_status = {float_detect_tininess = 0x1, float_rounding_mode = <br>
0x0, float_exception_flags = 0x0, floatx80_rounding_precision = 0x0, <br>
flush_to_zero = 0x0, flush_inputs_to_zero = 0x0, default_nan_mode = 0x0, <br>
snan_bit_is_one = 0x0},<br>
     fp_status_f16 = {float_detect_tininess = 0x1, float_rounding_mode = <br>
0x0, float_exception_flags = 0x0, floatx80_rounding_precision = 0x0, <br>
flush_to_zero = 0x0, flush_inputs_to_zero = 0x0, default_nan_mode = 0x0,<br>
       snan_bit_is_one = 0x0}, standard_fp_status = <br>
{float_detect_tininess = 0x1, float_rounding_mode = 0x0, <br>
float_exception_flags = 0x0, floatx80_rounding_precision = 0x0, <br>
flush_to_zero = 0x1, flush_inputs_to_zero = 0x1,<br>
       default_nan_mode = 0x1, snan_bit_is_one = 0x0}, zcr_el = {0x0, <br>
0x0, 0x0, 0x0}}, exclusive_addr = 0xffffffffffffffff, exclusive_val = <br>
0x0, exclusive_high = 0x0, iwmmxt = {regs = {0x0 &lt;repeats 16 times&gt;}, <br>
val = 0x0, cregs = {<br>
       0x0 &lt;repeats 16 times&gt;}}, cpu_breakpoint = {0x0 &lt;repeats 16 <br>
times&gt;}, cpu_watchpoint = {0x0 &lt;repeats 16 times&gt;}, end_reset_fields = <br>
{&lt;No data fields&gt;}, features = 0xfd38fbe6f3, pmsav7 = {drbar = 0x0, drsr <br>
= 0x0, dracr = 0x0,<br>
     rnr = {0x0, 0x0}}, pmsav8 = {rbar = {0x0, 0x0}, rlar = {0x0, 0x0}, <br>
mair0 = {0x0, 0x0}, mair1 = {0x0, 0x0}}, sau = {rbar = 0x0, rlar = 0x0, <br>
rnr = 0x0, ctrl = 0x0}, nvic = 0x0, boot_info = 0x5622af3a17a0, <br>
gicv3state = 0x0}<br>
<br>
&gt; [1] <a href="http://www.orangepi.org/downloadresources/" rel="noreferrer" target="_blank">http://www.orangepi.org/downloadresources/</a><br>
&gt; [2] <a href="https://buildroot.org/download.html" rel="noreferrer" target="_blank">https://buildroot.org/download.html</a><br>
&gt; [3] <a href="https://www.armbian.com/orange-pi-pc/" rel="noreferrer" target="_blank">https://www.armbian.com/orange-pi-pc/</a><br>
<br>
</blockquote></div><br clear="all"><br>-- <br><div dir="ltr" class="gmail_signature">Niek Linnenbank<br><br>WWW: <a href="http://www.nieklinnenbank.nl/" target="_blank">http://www.nieklinnenbank.nl/</a><br>BLOG: <a href="http://nieklinnenbank.wordpress.com/" target="_blank">http://nieklinnenbank.wordpress.com/</a><br>FUN:    <a href="http://www.FreeNOS.org/" target="_blank">http://www.FreeNOS.org/</a><br></div>

^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH 00/10] Add Allwinner H3 SoC and Orange Pi PC Machine
  2019-12-03  9:02 ` Philippe Mathieu-Daudé
@ 2019-12-03 19:32   ` Niek Linnenbank
  2019-12-06 14:16     ` Peter Maydell
  0 siblings, 1 reply; 33+ messages in thread
From: Niek Linnenbank @ 2019-12-03 19:32 UTC (permalink / raw)
  To: Philippe Mathieu-Daudé
  Cc: Peter Maydell, Alistair Francis, Richard Henderson, qemu-devel,
	Niccolò Izzo, b.galvani, KONRAD Frederic, qemu-arm

[-- Attachment #1: Type: text/plain, Size: 1501 bytes --]

Hello Philippe,

On Tue, Dec 3, 2019 at 10:02 AM Philippe Mathieu-Daudé <philmd@redhat.com>
wrote:

> On 12/2/19 10:09 PM, Niek Linnenbank wrote:
> > Dear QEMU developers,
> >
> > Hereby I would like to contribute the following set of patches to QEMU
> > which add support for the Allwinner H3 System on Chip and the
> > Orange Pi PC machine. The following features and devices are supported:
> >
> >   * SMP (Quad Core Cortex A7)
> >   * Generic Interrupt Controller configuration
> >   * SRAM mappings
> >   * Timer device (re-used from Allwinner A10)
> >   * UART
> >   * SD/MMC storage controller
> >   * EMAC ethernet connectivity
> >   * USB 2.0 interfaces
> >   * Clock Control Unit
> >   * System Control module
> >   * Security Identifier device
> >
> > Functionality related to graphical output such as HDMI, GPU,
> > Display Engine and audio are not included.
>
> I'd love to see the OpenRISC AR100 core instantiated in this SoC.
>
> Your contribution makes another good example of multi-arch/single-binary
> QEMU (here 4x ARM + 1x OpenRISC).
>
>
Indeed that sounds like an interesting combination. Are there plans to
build a multi-arch/single-binary QEMU?
I have not looked yet at that part of the H3, but there is some documention
available here on this wiki:
  https://linux-sunxi.org/AR100

Regards,
Niek

-- 
Niek Linnenbank

WWW: http://www.nieklinnenbank.nl/
BLOG: http://nieklinnenbank.wordpress.com/
FUN:    http://www.FreeNOS.org/

[-- Attachment #2: Type: text/html, Size: 2298 bytes --]

<div dir="ltr"><div>Hello Philippe,<br></div><br><div class="gmail_quote"><div dir="ltr" class="gmail_attr">On Tue, Dec 3, 2019 at 10:02 AM Philippe Mathieu-Daudé &lt;<a href="mailto:philmd@redhat.com">philmd@redhat.com</a>&gt; wrote:<br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">On 12/2/19 10:09 PM, Niek Linnenbank wrote:<br>
&gt; Dear QEMU developers,<br>
&gt; <br>
&gt; Hereby I would like to contribute the following set of patches to QEMU<br>
&gt; which add support for the Allwinner H3 System on Chip and the<br>
&gt; Orange Pi PC machine. The following features and devices are supported:<br>
&gt; <br>
&gt;   * SMP (Quad Core Cortex A7)<br>
&gt;   * Generic Interrupt Controller configuration<br>
&gt;   * SRAM mappings<br>
&gt;   * Timer device (re-used from Allwinner A10)<br>
&gt;   * UART<br>
&gt;   * SD/MMC storage controller<br>
&gt;   * EMAC ethernet connectivity<br>
&gt;   * USB 2.0 interfaces<br>
&gt;   * Clock Control Unit<br>
&gt;   * System Control module<br>
&gt;   * Security Identifier device<br>
&gt; <br>
&gt; Functionality related to graphical output such as HDMI, GPU,<br>
&gt; Display Engine and audio are not included.<br>
<br>
I&#39;d love to see the OpenRISC AR100 core instantiated in this SoC.<br>
<br>
Your contribution makes another good example of multi-arch/single-binary <br>
QEMU (here 4x ARM + 1x OpenRISC).<br>
<br>
</blockquote></div><div><br></div><div>Indeed that sounds like an interesting combination. Are there plans to build a multi-arch/single-binary QEMU?<br></div><div>I have not looked yet at that part of the H3, but there is some documention available here on this wiki:</div><div>  <a href="https://linux-sunxi.org/AR100">https://linux-sunxi.org/AR100</a></div><div><br></div><div>Regards,</div><div>Niek<br></div><br>-- <br><div dir="ltr" class="gmail_signature">Niek Linnenbank<br><br>WWW: <a href="http://www.nieklinnenbank.nl/" target="_blank">http://www.nieklinnenbank.nl/</a><br>BLOG: <a href="http://nieklinnenbank.wordpress.com/" target="_blank">http://nieklinnenbank.wordpress.com/</a><br>FUN:    <a href="http://www.FreeNOS.org/" target="_blank">http://www.FreeNOS.org/</a><br></div></div>

^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH 02/10] hw: arm: add Xunlong Orange Pi PC machine
  2019-12-03  9:17   ` Philippe Mathieu-Daudé
@ 2019-12-03 19:33     ` Niek Linnenbank
  2019-12-04  9:03       ` Philippe Mathieu-Daudé
  2019-12-05 22:15     ` Niek Linnenbank
  1 sibling, 1 reply; 33+ messages in thread
From: Niek Linnenbank @ 2019-12-03 19:33 UTC (permalink / raw)
  To: Philippe Mathieu-Daudé
  Cc: b.galvani, Peter Maydell, qemu-arm, qemu-devel

[-- Attachment #1: Type: text/plain, Size: 6758 bytes --]

Hello Philippe,

Thanks for your quick review comments!
I'll start working on a v2 of the patches and include the changes you
suggested.

Regards,
Niek

On Tue, Dec 3, 2019 at 10:18 AM Philippe Mathieu-Daudé <philmd@redhat.com>
wrote:

> On 12/2/19 10:09 PM, Niek Linnenbank wrote:
> > 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,
> > 512MB RAM, 100Mbit ethernet, USB, SD/MMC, USB, HDMI and
> > various other I/O. This commit add support for the Xunlong
> > Orange Pi PC machine.
> >
> > Signed-off-by: Niek Linnenbank <nieklinnenbank@gmail.com>
> > ---
> >   MAINTAINERS          |  1 +
> >   hw/arm/Makefile.objs |  2 +-
> >   hw/arm/orangepi.c    | 90 ++++++++++++++++++++++++++++++++++++++++++++
> >   3 files changed, 92 insertions(+), 1 deletion(-)
> >   create mode 100644 hw/arm/orangepi.c
> >
> > diff --git a/MAINTAINERS b/MAINTAINERS
> > index 29c9936037..42c913d6cb 100644
> > --- a/MAINTAINERS
> > +++ b/MAINTAINERS
> > @@ -485,6 +485,7 @@ L: qemu-arm@nongnu.org
> >   S: Maintained
> >   F: hw/*/allwinner-h3*
> >   F: include/hw/*/allwinner-h3*
> > +F: hw/arm/orangepi.c
> >
> >   ARM PrimeCell and CMSDK devices
> >   M: Peter Maydell <peter.maydell@linaro.org>
> > diff --git a/hw/arm/Makefile.objs b/hw/arm/Makefile.objs
> > index 956e496052..8d5ea453d5 100644
> > --- a/hw/arm/Makefile.objs
> > +++ b/hw/arm/Makefile.objs
> > @@ -34,7 +34,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
> > +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_XLNX_ZYNQMP_ARM) += xlnx-zynqmp.o xlnx-zcu102.o
> > diff --git a/hw/arm/orangepi.c b/hw/arm/orangepi.c
> > new file mode 100644
> > index 0000000000..5ef2735f81
> > --- /dev/null
> > +++ b/hw/arm/orangepi.c
> > @@ -0,0 +1,90 @@
> > +/*
> > + * 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 "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"
> > +
> > +static struct arm_boot_info orangepi_binfo = {
> > +    .loader_start = AW_H3_SDRAM_BASE,
> > +    .board_id = -1,
> > +};
> > +
> > +typedef struct OrangePiState {
> > +    AwH3State *h3;
> > +    MemoryRegion sdram;
> > +} OrangePiState;
> > +
> > +static void orangepi_init(MachineState *machine)
> > +{
> > +    OrangePiState *s = g_new(OrangePiState, 1);
> > +    Error *err = NULL;
> > +
>
> Here I'd add:
>
>        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);
>        }
>
> > +    s->h3 = AW_H3(object_new(TYPE_AW_H3));
> > +
> > +    /* Setup timer properties */
> > +    object_property_set_int(OBJECT(&s->h3->timer), 32768, "clk0-freq",
> &err);
> > +    if (err != NULL) {
> > +        error_reportf_err(err, "Couldn't set clk0 frequency: ");
> > +        exit(1);
> > +    }
> > +
> > +    object_property_set_int(OBJECT(&s->h3->timer), 24000000,
> "clk1-freq",
> > +                            &err);
> > +    if (err != NULL) {
> > +        error_reportf_err(err, "Couldn't set clk1 frequency: ");
> > +        exit(1);
> > +    }
> > +
> > +    /* Mark H3 object realized */
> > +    object_property_set_bool(OBJECT(s->h3), true, "realized", &err);
>
> I'm not sure if that's correct but I'd simply use &error_abort here.
>
> > +    if (err != NULL) {
> > +        error_reportf_err(err, "Couldn't realize Allwinner H3: ");
> > +        exit(1);
> > +    }
> > +
> > +    /* RAM */
> > +    memory_region_allocate_system_memory(&s->sdram, NULL,
> "orangepi.ram",
> > +                                         machine->ram_size);
>
> I'd only allow machine->ram_size == 1 * GiB here, since the onboard DRAM
> is not upgradable.
>
> > +    memory_region_add_subregion(get_system_memory(), AW_H3_SDRAM_BASE,
> > +                                &s->sdram);
> > +
> > +    /* Load target kernel */
> > +    orangepi_binfo.ram_size = machine->ram_size;
> > +    orangepi_binfo.nb_cpus  = AW_H3_NUM_CPUS;
> > +    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->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->ignore_memory_transaction_failures = true;
>
> You should not use this flag in new design. See the documentation in
> include/hw/boards.h:
>
>   * @ignore_memory_transaction_failures:
>   *    [...] New board models
>   *    should instead use "unimplemented-device" for all memory ranges
> where
>   *    the guest will attempt to probe for a device that QEMU doesn't
>   *    implement and a stub device is required.
>
> You already use the "unimplemented-device".
>
> > +}
> > +
> > +DEFINE_MACHINE("orangepi", orangepi_machine_init)
>
> Can you name it 'orangepi-pc'? So we can add other orangepi models.
>
> Thanks,
>
> Phil.
>
>

-- 
Niek Linnenbank

WWW: http://www.nieklinnenbank.nl/
BLOG: http://nieklinnenbank.wordpress.com/
FUN:    http://www.FreeNOS.org/

[-- Attachment #2: Type: text/html, Size: 9051 bytes --]

<div dir="ltr"><div>Hello Philippe,</div><div><br></div><div>Thanks for your quick review comments!</div><div>I&#39;ll start working on a v2 of the patches and include the changes you suggested.</div><div><br></div><div>Regards,</div><div>Niek<br></div><br><div class="gmail_quote"><div dir="ltr" class="gmail_attr">On Tue, Dec 3, 2019 at 10:18 AM Philippe Mathieu-Daudé &lt;<a href="mailto:philmd@redhat.com">philmd@redhat.com</a>&gt; wrote:<br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">On 12/2/19 10:09 PM, Niek Linnenbank wrote:<br>
&gt; The Xunlong Orange Pi PC is an Allwinner H3 System on Chip<br>
&gt; based embedded computer with mainline support in both U-Boot<br>
&gt; and Linux. The board comes with a Quad Core Cortex A7 @ 1.3GHz,<br>
&gt; 512MB RAM, 100Mbit ethernet, USB, SD/MMC, USB, HDMI and<br>
&gt; various other I/O. This commit add support for the Xunlong<br>
&gt; Orange Pi PC machine.<br>
&gt; <br>
&gt; Signed-off-by: Niek Linnenbank &lt;<a href="mailto:nieklinnenbank@gmail.com" target="_blank">nieklinnenbank@gmail.com</a>&gt;<br>
&gt; ---<br>
&gt;   MAINTAINERS          |  1 +<br>
&gt;   hw/arm/Makefile.objs |  2 +-<br>
&gt;   hw/arm/orangepi.c    | 90 ++++++++++++++++++++++++++++++++++++++++++++<br>
&gt;   3 files changed, 92 insertions(+), 1 deletion(-)<br>
&gt;   create mode 100644 hw/arm/orangepi.c<br>
&gt; <br>
&gt; diff --git a/MAINTAINERS b/MAINTAINERS<br>
&gt; index 29c9936037..42c913d6cb 100644<br>
&gt; --- a/MAINTAINERS<br>
&gt; +++ b/MAINTAINERS<br>
&gt; @@ -485,6 +485,7 @@ L: <a href="mailto:qemu-arm@nongnu.org" target="_blank">qemu-arm@nongnu.org</a><br>
&gt;   S: Maintained<br>
&gt;   F: hw/*/allwinner-h3*<br>
&gt;   F: include/hw/*/allwinner-h3*<br>
&gt; +F: hw/arm/orangepi.c<br>
&gt;   <br>
&gt;   ARM PrimeCell and CMSDK devices<br>
&gt;   M: Peter Maydell &lt;<a href="mailto:peter.maydell@linaro.org" target="_blank">peter.maydell@linaro.org</a>&gt;<br>
&gt; diff --git a/hw/arm/Makefile.objs b/hw/arm/Makefile.objs<br>
&gt; index 956e496052..8d5ea453d5 100644<br>
&gt; --- a/hw/arm/Makefile.objs<br>
&gt; +++ b/hw/arm/Makefile.objs<br>
&gt; @@ -34,7 +34,7 @@ obj-$(CONFIG_DIGIC) += digic.o<br>
&gt;   obj-$(CONFIG_OMAP) += omap1.o omap2.o<br>
&gt;   obj-$(CONFIG_STRONGARM) += strongarm.o<br>
&gt;   obj-$(CONFIG_ALLWINNER_A10) += allwinner-a10.o cubieboard.o<br>
&gt; -obj-$(CONFIG_ALLWINNER_H3) += allwinner-h3.o<br>
&gt; +obj-$(CONFIG_ALLWINNER_H3) += allwinner-h3.o orangepi.o<br>
&gt;   obj-$(CONFIG_RASPI) += bcm2835_peripherals.o bcm2836.o raspi.o<br>
&gt;   obj-$(CONFIG_STM32F205_SOC) += stm32f205_soc.o<br>
&gt;   obj-$(CONFIG_XLNX_ZYNQMP_ARM) += xlnx-zynqmp.o xlnx-zcu102.o<br>
&gt; diff --git a/hw/arm/orangepi.c b/hw/arm/orangepi.c<br>
&gt; new file mode 100644<br>
&gt; index 0000000000..5ef2735f81<br>
&gt; --- /dev/null<br>
&gt; +++ b/hw/arm/orangepi.c<br>
&gt; @@ -0,0 +1,90 @@<br>
&gt; +/*<br>
&gt; + * Orange Pi emulation<br>
&gt; + *<br>
&gt; + * Copyright (C) 2019 Niek Linnenbank &lt;<a href="mailto:nieklinnenbank@gmail.com" target="_blank">nieklinnenbank@gmail.com</a>&gt;<br>
&gt; + *<br>
&gt; + * This program is free software: you can redistribute it and/or modify<br>
&gt; + * it under the terms of the GNU General Public License as published by<br>
&gt; + * the Free Software Foundation, either version 2 of the License, or<br>
&gt; + * (at your option) any later version.<br>
&gt; + *<br>
&gt; + * This program is distributed in the hope that it will be useful,<br>
&gt; + * but WITHOUT ANY WARRANTY; without even the implied warranty of<br>
&gt; + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the<br>
&gt; + * GNU General Public License for more details.<br>
&gt; + *<br>
&gt; + * You should have received a copy of the GNU General Public License<br>
&gt; + * along with this program.  If not, see &lt;<a href="http://www.gnu.org/licenses/" rel="noreferrer" target="_blank">http://www.gnu.org/licenses/</a>&gt;.<br>
&gt; + */<br>
&gt; +<br>
&gt; +#include &quot;qemu/osdep.h&quot;<br>
&gt; +#include &quot;exec/address-spaces.h&quot;<br>
&gt; +#include &quot;qapi/error.h&quot;<br>
&gt; +#include &quot;cpu.h&quot;<br>
&gt; +#include &quot;hw/sysbus.h&quot;<br>
&gt; +#include &quot;hw/boards.h&quot;<br>
&gt; +#include &quot;hw/qdev-properties.h&quot;<br>
&gt; +#include &quot;hw/arm/allwinner-h3.h&quot;<br>
&gt; +<br>
&gt; +static struct arm_boot_info orangepi_binfo = {<br>
&gt; +    .loader_start = AW_H3_SDRAM_BASE,<br>
&gt; +    .board_id = -1,<br>
&gt; +};<br>
&gt; +<br>
&gt; +typedef struct OrangePiState {<br>
&gt; +    AwH3State *h3;<br>
&gt; +    MemoryRegion sdram;<br>
&gt; +} OrangePiState;<br>
&gt; +<br>
&gt; +static void orangepi_init(MachineState *machine)<br>
&gt; +{<br>
&gt; +    OrangePiState *s = g_new(OrangePiState, 1);<br>
&gt; +    Error *err = NULL;<br>
&gt; +<br>
<br>
Here I&#39;d add:<br>
<br>
       if (strcmp(machine-&gt;cpu_type, ARM_CPU_TYPE_NAME(&quot;cortex-a7&quot;)) != 0) {<br>
           error_report(&quot;This board can only be used with cortex-a7 CPU&quot;);<br>
           exit(1);<br>
       }<br>
<br>
&gt; +    s-&gt;h3 = AW_H3(object_new(TYPE_AW_H3));<br>
&gt; +<br>
&gt; +    /* Setup timer properties */<br>
&gt; +    object_property_set_int(OBJECT(&amp;s-&gt;h3-&gt;timer), 32768, &quot;clk0-freq&quot;, &amp;err);<br>
&gt; +    if (err != NULL) {<br>
&gt; +        error_reportf_err(err, &quot;Couldn&#39;t set clk0 frequency: &quot;);<br>
&gt; +        exit(1);<br>
&gt; +    }<br>
&gt; +<br>
&gt; +    object_property_set_int(OBJECT(&amp;s-&gt;h3-&gt;timer), 24000000, &quot;clk1-freq&quot;,<br>
&gt; +                            &amp;err);<br>
&gt; +    if (err != NULL) {<br>
&gt; +        error_reportf_err(err, &quot;Couldn&#39;t set clk1 frequency: &quot;);<br>
&gt; +        exit(1);<br>
&gt; +    }<br>
&gt; +<br>
&gt; +    /* Mark H3 object realized */<br>
&gt; +    object_property_set_bool(OBJECT(s-&gt;h3), true, &quot;realized&quot;, &amp;err);<br>
<br>
I&#39;m not sure if that&#39;s correct but I&#39;d simply use &amp;error_abort here.<br>
<br>
&gt; +    if (err != NULL) {<br>
&gt; +        error_reportf_err(err, &quot;Couldn&#39;t realize Allwinner H3: &quot;);<br>
&gt; +        exit(1);<br>
&gt; +    }<br>
&gt; +<br>
&gt; +    /* RAM */<br>
&gt; +    memory_region_allocate_system_memory(&amp;s-&gt;sdram, NULL, &quot;orangepi.ram&quot;,<br>
&gt; +                                         machine-&gt;ram_size);<br>
<br>
I&#39;d only allow machine-&gt;ram_size == 1 * GiB here, since the onboard DRAM <br>
is not upgradable.<br>
<br>
&gt; +    memory_region_add_subregion(get_system_memory(), AW_H3_SDRAM_BASE,<br>
&gt; +                                &amp;s-&gt;sdram);<br>
&gt; +<br>
&gt; +    /* Load target kernel */<br>
&gt; +    orangepi_binfo.ram_size = machine-&gt;ram_size;<br>
&gt; +    orangepi_binfo.nb_cpus  = AW_H3_NUM_CPUS;<br>
&gt; +    arm_load_kernel(ARM_CPU(first_cpu), machine, &amp;orangepi_binfo);<br>
&gt; +}<br>
&gt; +<br>
&gt; +static void orangepi_machine_init(MachineClass *mc)<br>
&gt; +{<br>
&gt; +    mc-&gt;desc = &quot;Orange Pi PC&quot;;<br>
&gt; +    mc-&gt;init = orangepi_init;<br>
&gt; +    mc-&gt;units_per_default_bus = 1;<br>
&gt; +    mc-&gt;min_cpus = AW_H3_NUM_CPUS;<br>
&gt; +    mc-&gt;max_cpus = AW_H3_NUM_CPUS;<br>
&gt; +    mc-&gt;default_cpus = AW_H3_NUM_CPUS;<br>
<br>
        mc-&gt;default_cpu_type = ARM_CPU_TYPE_NAME(&quot;cortex-a7&quot;);<br>
<br>
&gt; +    mc-&gt;ignore_memory_transaction_failures = true;<br>
<br>
You should not use this flag in new design. See the documentation in <br>
include/hw/boards.h:<br>
<br>
  * @ignore_memory_transaction_failures:<br>
  *    [...] New board models<br>
  *    should instead use &quot;unimplemented-device&quot; for all memory ranges where<br>
  *    the guest will attempt to probe for a device that QEMU doesn&#39;t<br>
  *    implement and a stub device is required.<br>
<br>
You already use the &quot;unimplemented-device&quot;.<br>
<br>
&gt; +}<br>
&gt; +<br>
&gt; +DEFINE_MACHINE(&quot;orangepi&quot;, orangepi_machine_init)<br>
<br>
Can you name it &#39;orangepi-pc&#39;? So we can add other orangepi models.<br>
<br>
Thanks,<br>
<br>
Phil.<br>
<br>
</blockquote></div><br clear="all"><br>-- <br><div dir="ltr" class="gmail_signature">Niek Linnenbank<br><br>WWW: <a href="http://www.nieklinnenbank.nl/" target="_blank">http://www.nieklinnenbank.nl/</a><br>BLOG: <a href="http://nieklinnenbank.wordpress.com/" target="_blank">http://nieklinnenbank.wordpress.com/</a><br>FUN:    <a href="http://www.FreeNOS.org/" target="_blank">http://www.FreeNOS.org/</a><br></div></div>

^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH 10/10] arm: allwinner-h3: add EMAC ethernet device
  2019-12-03  9:33   ` KONRAD Frederic
@ 2019-12-03 19:41     ` Niek Linnenbank
  2019-12-04 15:14     ` Philippe Mathieu-Daudé
  1 sibling, 0 replies; 33+ messages in thread
From: Niek Linnenbank @ 2019-12-03 19:41 UTC (permalink / raw)
  To: KONRAD Frederic; +Cc: b.galvani, Peter Maydell, qemu-arm, qemu-devel

[-- Attachment #1: Type: text/plain, Size: 41485 bytes --]

Hello Frederic,

Thank you for your quick review comments!
I'll start working on v2 of the patches and include the changes you
suggested.

On Tue, Dec 3, 2019 at 10:33 AM KONRAD Frederic <frederic.konrad@adacore.com>
wrote:

>
>
> Le 12/2/19 à 10:09 PM, Niek Linnenbank a écrit :
> > The Allwinner H3 System on Chip includes an Ethernet MAC (EMAC)
> > which provides 10M/100M/1000M Ethernet connectivity. This commit
> > adds support for the Allwinner H3 EMAC, including emulation for
> > the following functionality:
> >
> >   * DMA transfers
> >   * MII interface
> >   * Transmit CRC calculation
> >
> > Signed-off-by: Niek Linnenbank <nieklinnenbank@gmail.com>
> > ---
> >   hw/arm/Kconfig                     |   1 +
> >   hw/arm/allwinner-h3.c              |  17 +
> >   hw/arm/orangepi.c                  |   7 +
> >   hw/net/Kconfig                     |   3 +
> >   hw/net/Makefile.objs               |   1 +
> >   hw/net/allwinner-h3-emac.c         | 786 +++++++++++++++++++++++++++++
> >   hw/net/trace-events                |  10 +
> >   include/hw/arm/allwinner-h3.h      |   2 +
> >   include/hw/net/allwinner-h3-emac.h |  69 +++
> >   9 files changed, 896 insertions(+)
> >   create mode 100644 hw/net/allwinner-h3-emac.c
> >   create mode 100644 include/hw/net/allwinner-h3-emac.h
> >
> > diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig
> > index ebf8d2325f..551cff3442 100644
> > --- a/hw/arm/Kconfig
> > +++ b/hw/arm/Kconfig
> > @@ -294,6 +294,7 @@ config ALLWINNER_A10
> >   config ALLWINNER_H3
> >       bool
> >       select ALLWINNER_A10_PIT
> > +    select ALLWINNER_H3_EMAC
> >       select SERIAL
> >       select ARM_TIMER
> >       select ARM_GIC
> > diff --git a/hw/arm/allwinner-h3.c b/hw/arm/allwinner-h3.c
> > index c2972caf88..274b8548c0 100644
> > --- a/hw/arm/allwinner-h3.c
> > +++ b/hw/arm/allwinner-h3.c
> > @@ -53,6 +53,9 @@ static void aw_h3_init(Object *obj)
> >
> >       sysbus_init_child_obj(obj, "mmc0", &s->mmc0, sizeof(s->mmc0),
> >                             TYPE_AW_H3_SDHOST);
> > +
> > +    sysbus_init_child_obj(obj, "emac", &s->emac, sizeof(s->emac),
> > +                          TYPE_AW_H3_EMAC);
> >   }
> >
> >   static void aw_h3_realize(DeviceState *dev, Error **errp)
> > @@ -237,6 +240,20 @@ static void aw_h3_realize(DeviceState *dev, Error
> **errp)
> >           return;
> >       }
> >
> > +    /* EMAC */
> > +    if (nd_table[0].used) {
> > +        qemu_check_nic_model(&nd_table[0], TYPE_AW_H3_EMAC);
> > +        qdev_set_nic_properties(DEVICE(&s->emac), &nd_table[0]);
> > +    }
> > +    object_property_set_bool(OBJECT(&s->emac), true, "realized", &err);
> > +    if (err != NULL) {
> > +        error_propagate(errp, err);
> > +        return;
> > +    }
> > +    sysbusdev = SYS_BUS_DEVICE(&s->emac);
> > +    sysbus_mmio_map(sysbusdev, 0, AW_H3_EMAC_BASE);
> > +    sysbus_connect_irq(sysbusdev, 0, s->irq[AW_H3_GIC_SPI_EMAC]);
> > +
> >       /* Universal Serial Bus */
> >       sysbus_create_simple(TYPE_AW_H3_EHCI, AW_H3_EHCI0_BASE,
> >                            s->irq[AW_H3_GIC_SPI_EHCI0]);
> > diff --git a/hw/arm/orangepi.c b/hw/arm/orangepi.c
> > index dee3efaf08..8a61eb0e69 100644
> > --- a/hw/arm/orangepi.c
> > +++ b/hw/arm/orangepi.c
> > @@ -61,6 +61,13 @@ static void orangepi_init(MachineState *machine)
> >           exit(1);
> >       }
> >
> > +    /* Setup EMAC properties */
> > +    object_property_set_int(OBJECT(&s->h3->emac), 1, "phy-addr", &err);
> > +    if (err != NULL) {
> > +        error_reportf_err(err, "Couldn't set phy address: ");
> > +        exit(1);
> > +    }
> > +
> >       /* Mark H3 object realized */
> >       object_property_set_bool(OBJECT(s->h3), true, "realized", &err);
> >       if (err != NULL) {
> > diff --git a/hw/net/Kconfig b/hw/net/Kconfig
> > index 3856417d42..36d3923992 100644
> > --- a/hw/net/Kconfig
> > +++ b/hw/net/Kconfig
> > @@ -74,6 +74,9 @@ config MIPSNET
> >   config ALLWINNER_EMAC
> >       bool
> >
> > +config ALLWINNER_H3_EMAC
> > +    bool
> > +
> >   config IMX_FEC
> >       bool
> >
> > diff --git a/hw/net/Makefile.objs b/hw/net/Makefile.objs
> > index 7907d2c199..5548deb07a 100644
> > --- a/hw/net/Makefile.objs
> > +++ b/hw/net/Makefile.objs
> > @@ -23,6 +23,7 @@ common-obj-$(CONFIG_XGMAC) += xgmac.o
> >   common-obj-$(CONFIG_MIPSNET) += mipsnet.o
> >   common-obj-$(CONFIG_XILINX_AXI) += xilinx_axienet.o
> >   common-obj-$(CONFIG_ALLWINNER_EMAC) += allwinner_emac.o
> > +common-obj-$(CONFIG_ALLWINNER_H3_EMAC) += allwinner-h3-emac.o
> >   common-obj-$(CONFIG_IMX_FEC) += imx_fec.o
> >
> >   common-obj-$(CONFIG_CADENCE) += cadence_gem.o
> > diff --git a/hw/net/allwinner-h3-emac.c b/hw/net/allwinner-h3-emac.c
> > new file mode 100644
> > index 0000000000..37f6f44406
> > --- /dev/null
> > +++ b/hw/net/allwinner-h3-emac.c
> > @@ -0,0 +1,786 @@
> > +/*
> > + * Allwinner H3 EMAC 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 "hw/sysbus.h"
> > +#include "migration/vmstate.h"
> > +#include "net/net.h"
> > +#include "hw/irq.h"
> > +#include "hw/qdev-properties.h"
> > +#include "qemu/log.h"
> > +#include "trace.h"
> > +#include "net/checksum.h"
> > +#include "qemu/module.h"
> > +#include "exec/cpu-common.h"
> > +#include "hw/net/allwinner-h3-emac.h"
> > +
> > +/* EMAC register offsets */
> > +#define REG_BASIC_CTL_0        (0x0000) /* Basic Control 0 */
> > +#define REG_BASIC_CTL_1        (0x0004) /* Basic Control 1 */
> > +#define REG_INT_STA            (0x0008) /* Interrupt Status */
> > +#define REG_INT_EN             (0x000C) /* Interrupt Enable */
> > +#define REG_TX_CTL_0           (0x0010) /* Transmit Control 0 */
> > +#define REG_TX_CTL_1           (0x0014) /* Transmit Control 1 */
> > +#define REG_TX_FLOW_CTL        (0x001C) /* Transmit Flow Control */
> > +#define REG_TX_DMA_DESC_LIST   (0x0020) /* Transmit Descriptor List
> Address */
> > +#define REG_RX_CTL_0           (0x0024) /* Receive Control 0 */
> > +#define REG_RX_CTL_1           (0x0028) /* Receive Control 1 */
> > +#define REG_RX_DMA_DESC_LIST   (0x0034) /* Receive Descriptor List
> Address */
> > +#define REG_FRM_FLT            (0x0038) /* Receive Frame Filter */
> > +#define REG_RX_HASH_0          (0x0040) /* Receive Hash Table 0 */
> > +#define REG_RX_HASH_1          (0x0044) /* Receive Hash Table 1 */
> > +#define REG_MII_CMD            (0x0048) /* Management Interface Command
> */
> > +#define REG_MII_DATA           (0x004C) /* Management Interface Data */
> > +#define REG_ADDR_HIGH          (0x0050) /* MAC Address High */
> > +#define REG_ADDR_LOW           (0x0054) /* MAC Address Low */
> > +#define REG_TX_DMA_STA         (0x00B0) /* Transmit DMA Status */
> > +#define REG_TX_CUR_DESC        (0x00B4) /* Transmit Current Descriptor
> */
> > +#define REG_TX_CUR_BUF         (0x00B8) /* Transmit Current Buffer */
> > +#define REG_RX_DMA_STA         (0x00C0) /* Receive DMA Status */
> > +#define REG_RX_CUR_DESC        (0x00C4) /* Receive Current Descriptor */
> > +#define REG_RX_CUR_BUF         (0x00C8) /* Receive Current Buffer */
> > +#define REG_RGMII_STA          (0x00D0) /* RGMII Status */
> > +
> > +/* EMAC register flags */
> > +#define BASIC_CTL0_100Mbps     (0b11 << 2)
> > +#define BASIC_CTL0_FD          (1 << 0)
> > +#define BASIC_CTL1_SOFTRST     (1 << 0)
> > +
> > +#define INT_STA_RGMII_LINK     (1 << 16)
> > +#define INT_STA_RX_EARLY       (1 << 13)
> > +#define INT_STA_RX_OVERFLOW    (1 << 12)
> > +#define INT_STA_RX_TIMEOUT     (1 << 11)
> > +#define INT_STA_RX_DMA_STOP    (1 << 10)
> > +#define INT_STA_RX_BUF_UA      (1 << 9)
> > +#define INT_STA_RX             (1 << 8)
> > +#define INT_STA_TX_EARLY       (1 << 5)
> > +#define INT_STA_TX_UNDERFLOW   (1 << 4)
> > +#define INT_STA_TX_TIMEOUT     (1 << 3)
> > +#define INT_STA_TX_BUF_UA      (1 << 2)
> > +#define INT_STA_TX_DMA_STOP    (1 << 1)
> > +#define INT_STA_TX             (1 << 0)
> > +
> > +#define INT_EN_RX_EARLY        (1 << 13)
> > +#define INT_EN_RX_OVERFLOW     (1 << 12)
> > +#define INT_EN_RX_TIMEOUT      (1 << 11)
> > +#define INT_EN_RX_DMA_STOP     (1 << 10)
> > +#define INT_EN_RX_BUF_UA       (1 << 9)
> > +#define INT_EN_RX              (1 << 8)
> > +#define INT_EN_TX_EARLY        (1 << 5)
> > +#define INT_EN_TX_UNDERFLOW    (1 << 4)
> > +#define INT_EN_TX_TIMEOUT      (1 << 3)
> > +#define INT_EN_TX_BUF_UA       (1 << 2)
> > +#define INT_EN_TX_DMA_STOP     (1 << 1)
> > +#define INT_EN_TX              (1 << 0)
> > +
> > +#define TX_CTL0_TX_EN          (1 << 31)
> > +#define TX_CTL1_TX_DMA_START   (1 << 31)
> > +#define TX_CTL1_TX_DMA_EN      (1 << 30)
> > +#define TX_CTL1_TX_FLUSH       (1 << 0)
> > +
> > +#define RX_CTL0_RX_EN          (1 << 31)
> > +#define RX_CTL0_STRIP_FCS      (1 << 28)
> > +#define RX_CTL0_CRC_IPV4       (1 << 27)
> > +
> > +#define RX_CTL1_RX_DMA_START   (1 << 31)
> > +#define RX_CTL1_RX_DMA_EN      (1 << 30)
> > +#define RX_CTL1_RX_MD          (1 << 1)
> > +
> > +#define RX_FRM_FLT_DIS_ADDR    (1 << 31)
> > +
> > +#define MII_CMD_PHY_ADDR_SHIFT (12)
> > +#define MII_CMD_PHY_ADDR_MASK  (0xf000)
> > +#define MII_CMD_PHY_REG_SHIFT  (4)
> > +#define MII_CMD_PHY_REG_MASK   (0xf0)
> > +#define MII_CMD_PHY_RW         (1 << 1)
> > +#define MII_CMD_PHY_BUSY       (1 << 0)
> > +
> > +#define TX_DMA_STA_STOP        (0b000)
> > +#define TX_DMA_STA_RUN_FETCH   (0b001)
> > +#define TX_DMA_STA_WAIT_STA    (0b010)
> > +
> > +#define RX_DMA_STA_STOP        (0b000)
> > +#define RX_DMA_STA_RUN_FETCH   (0b001)
> > +#define RX_DMA_STA_WAIT_FRM    (0b011)
> > +
> > +#define RGMII_LINK_UP          (1 << 3)
> > +#define RGMII_FD               (1 << 0)
> > +
> > +/* EMAC register reset values */
> > +#define REG_BASIC_CTL_1_RST    (0x08000000)
> > +
> > +/* EMAC constants */
> > +#define AW_H3_EMAC_MIN_PKT_SZ  (64)
> > +
> > +/* Transmit/receive frame descriptor */
> > +typedef struct FrameDescriptor {
> > +    uint32_t status;
> > +    uint32_t status2;
> > +    uint32_t addr;
> > +    uint32_t next;
> > +} FrameDescriptor;
> > +
> > +/* Frame descriptor flags */
> > +#define DESC_STATUS_CTL                 (1 << 31)
> > +#define DESC_STATUS2_BUF_SIZE_MASK      (0x7ff)
> > +
> > +/* Transmit frame descriptor flags */
> > +#define TX_DESC_STATUS_LENGTH_ERR       (1 << 14)
> > +#define TX_DESC_STATUS2_FIRST_DESC      (1 << 29)
> > +#define TX_DESC_STATUS2_LAST_DESC       (1 << 30)
> > +#define TX_DESC_STATUS2_CHECKSUM_MASK   (0x3 << 27)
> > +
> > +/* Receive frame descriptor flags */
> > +#define RX_DESC_STATUS_FIRST_DESC       (1 << 9)
> > +#define RX_DESC_STATUS_LAST_DESC        (1 << 8)
> > +#define RX_DESC_STATUS_FRM_LEN_MASK     (0x3fff0000)
> > +#define RX_DESC_STATUS_FRM_LEN_SHIFT    (16)
> > +#define RX_DESC_STATUS_NO_BUF           (1 << 14)
> > +#define RX_DESC_STATUS_HEADER_ERR       (1 << 7)
> > +#define RX_DESC_STATUS_LENGTH_ERR       (1 << 4)
> > +#define RX_DESC_STATUS_CRC_ERR          (1 << 1)
> > +#define RX_DESC_STATUS_PAYLOAD_ERR      (1 << 0)
> > +#define RX_DESC_STATUS2_RX_INT_CTL      (1 << 31)
> > +
> > +/* MII register offsets */
> > +#define MII_REG_CR                      (0x0)
> > +#define MII_REG_ST                      (0x1)
> > +#define MII_REG_ID_HIGH                 (0x2)
> > +#define MII_REG_ID_LOW                  (0x3)
> > +
> > +/* MII register flags */
> > +#define MII_REG_CR_RESET                (1 << 15)
> > +#define MII_REG_CR_POWERDOWN            (1 << 11)
> > +#define MII_REG_CR_10Mbit               (0)
> > +#define MII_REG_CR_100Mbit              (1 << 13)
> > +#define MII_REG_CR_1000Mbit             (1 << 6)
> > +#define MII_REG_CR_AUTO_NEG             (1 << 12)
> > +#define MII_REG_CR_AUTO_NEG_RESTART     (1 << 9)
> > +#define MII_REG_CR_FULLDUPLEX           (1 << 8)
> > +
> > +#define MII_REG_ST_100BASE_T4           (1 << 15)
> > +#define MII_REG_ST_100BASE_X_FD         (1 << 14)
> > +#define MII_REG_ST_100BASE_X_HD         (1 << 13)
> > +#define MII_REG_ST_10_FD                (1 << 12)
> > +#define MII_REG_ST_10_HD                (1 << 11)
> > +#define MII_REG_ST_100BASE_T2_FD        (1 << 10)
> > +#define MII_REG_ST_100BASE_T2_HD        (1 << 9)
> > +#define MII_REG_ST_AUTONEG_COMPLETE     (1 << 5)
> > +#define MII_REG_ST_AUTONEG_AVAIL        (1 << 3)
> > +#define MII_REG_ST_LINK_UP              (1 << 2)
> > +
> > +/* MII constants */
> > +#define MII_PHY_ID_HIGH                 (0x0044)
> > +#define MII_PHY_ID_LOW                  (0x1400)
>
> I wonder if we can't share all those mii stuff accross the network adapters
> instead of redoing the work everytime. I've some patches about it I may
> post
> them sometimes.
>
>
Indeed, that would be a nice improvement. In fact, I was looking for
somekind of
MII library inside QEMU and could not find it. Then I saw that the other
emulated ethernet cards
all have that code inside each file, so that is why I did the same here.


> > +
> > +static void aw_h3_emac_mii_set_link(AwH3EmacState *s, bool link_active)
> > +{
> > +    if (link_active) {
> > +        s->mii_st |= MII_REG_ST_LINK_UP;
> > +    } else {
> > +        s->mii_st &= ~MII_REG_ST_LINK_UP;
> > +    }
> > +}
> > +
> > +static void aw_h3_emac_mii_reset(AwH3EmacState *s, bool link_active)
> > +{
> > +    s->mii_cr = MII_REG_CR_100Mbit | MII_REG_CR_AUTO_NEG |
> > +                MII_REG_CR_FULLDUPLEX;
> > +    s->mii_st = MII_REG_ST_100BASE_T4 | MII_REG_ST_100BASE_X_FD |
> > +                MII_REG_ST_100BASE_X_HD | MII_REG_ST_10_FD |
> MII_REG_ST_10_HD |
> > +                MII_REG_ST_100BASE_T2_FD | MII_REG_ST_100BASE_T2_HD |
> > +                MII_REG_ST_AUTONEG_COMPLETE | MII_REG_ST_AUTONEG_AVAIL;
> > +
> > +    aw_h3_emac_mii_set_link(s, link_active);
> > +}
> > +
> > +static void aw_h3_emac_mii_cmd(AwH3EmacState *s)
> > +{
> > +    uint8_t addr, reg;
> > +
> > +    addr = (s->mii_cmd & MII_CMD_PHY_ADDR_MASK) >>
> MII_CMD_PHY_ADDR_SHIFT;
> > +    reg = (s->mii_cmd & MII_CMD_PHY_REG_MASK) >> MII_CMD_PHY_REG_SHIFT;
> > +
> > +    if (addr != s->mii_phy_addr) {
> > +        return;
> > +    }
> > +
> > +    /* Read or write a PHY register? */
> > +    if (s->mii_cmd & MII_CMD_PHY_RW) {
> > +        trace_aw_h3_emac_mii_write_reg(reg, s->mii_data);
> > +
> > +        switch (reg) {
> > +        case MII_REG_CR:
> > +            if (s->mii_data & MII_REG_CR_RESET) {
> > +                aw_h3_emac_mii_reset(s, s->mii_st & MII_REG_ST_LINK_UP);
> > +            } else {
> > +                s->mii_cr = s->mii_data & ~(MII_REG_CR_RESET |
> > +
> MII_REG_CR_AUTO_NEG_RESTART);
> > +            }
> > +            break;
> > +        default:
> > +            qemu_log_mask(LOG_UNIMP, "allwinner-h3-emac: write access
> to "
> > +                                     "unknown MII register 0x%x\n",
> reg);
> > +            break;
> > +        }
> > +    } else {
> > +        switch (reg) {
> > +        case MII_REG_CR:
> > +            s->mii_data = s->mii_cr;
> > +            break;
> > +        case MII_REG_ST:
> > +            s->mii_data = s->mii_st;
> > +            break;
> > +        case MII_REG_ID_HIGH:
> > +            s->mii_data = MII_PHY_ID_HIGH;
> > +            break;
> > +        case MII_REG_ID_LOW:
> > +            s->mii_data = MII_PHY_ID_LOW;
> > +            break;
> > +        default:
> > +            qemu_log_mask(LOG_UNIMP, "allwinner-h3-emac: read access to
> "
> > +                                     "unknown MII register 0x%x\n",
> reg);
> > +            s->mii_data = 0;
> > +            break;
> > +        }
> > +
> > +        trace_aw_h3_emac_mii_read_reg(reg, s->mii_data);
> > +    }
> > +}
> > +
> > +static void aw_h3_emac_update_irq(AwH3EmacState *s)
> > +{
> > +    qemu_set_irq(s->irq, (s->int_sta & s->int_en) != 0);
> > +}
> > +
> > +static uint32_t aw_h3_emac_next_desc(FrameDescriptor *desc, size_t
> min_size)
> > +{
> > +    uint32_t paddr = desc->next;
> > +
> > +    cpu_physical_memory_read(paddr, desc, sizeof(*desc));
> > +
> > +    if ((desc->status & DESC_STATUS_CTL) &&
> > +        (desc->status2 & DESC_STATUS2_BUF_SIZE_MASK) >= min_size) {
> > +        return paddr;
> > +    } else {
> > +        return 0;
> > +    }
> > +}
> > +
> > +static uint32_t aw_h3_emac_get_desc(FrameDescriptor *desc, uint32_t
> start_addr,
> > +                                    size_t min_size)
> > +{
> > +    uint32_t desc_addr = start_addr;
> > +
> > +    /* Note that the list is a cycle. Last entry points back to the
> head. */
> > +    while (desc_addr != 0) {
> > +        cpu_physical_memory_read(desc_addr, desc, sizeof(*desc));
> > +
> > +        if ((desc->status & DESC_STATUS_CTL) &&
> > +            (desc->status2 & DESC_STATUS2_BUF_SIZE_MASK) >= min_size) {
> > +            return desc_addr;
> > +        } else if (desc->next == start_addr) {
> > +            break;
> > +        } else {
> > +            desc_addr = desc->next;
> > +        }
> > +    }
> > +
> > +    return 0;
> > +}
> > +
> > +static uint32_t aw_h3_emac_get_rx_desc(AwH3EmacState *s,
> FrameDescriptor *desc,
> > +                                       size_t min_size)
> > +{
> > +    return aw_h3_emac_get_desc(desc, s->rx_desc_curr, min_size);
> > +}
> > +
> > +static uint32_t aw_h3_emac_get_tx_desc(AwH3EmacState *s,
> FrameDescriptor *desc,
> > +                                       size_t min_size)
> > +{
> > +    return aw_h3_emac_get_desc(desc, s->tx_desc_head, min_size);
> > +}
> > +
> > +static void aw_h3_emac_flush_desc(FrameDescriptor *desc, uint32_t
> phys_addr)
> > +{
> > +    cpu_physical_memory_write(phys_addr, desc, sizeof(*desc));
> > +}
> > +
> > +static int aw_h3_emac_can_receive(NetClientState *nc)
> > +{
> > +    AwH3EmacState *s = qemu_get_nic_opaque(nc);
> > +    FrameDescriptor desc;
> > +
> > +    return (s->rx_ctl0 & RX_CTL0_RX_EN) &&
> > +           (aw_h3_emac_get_rx_desc(s, &desc, 0) != 0);
> > +}
> > +
> > +static ssize_t aw_h3_emac_receive(NetClientState *nc, const uint8_t
> *buf,
> > +                                  size_t size)
> > +{
> > +    AwH3EmacState *s = qemu_get_nic_opaque(nc);
> > +    FrameDescriptor desc;
> > +    size_t bytes_left = size;
> > +    size_t desc_bytes = 0;
> > +    size_t pad_fcs_size = 4;
> > +    size_t padding = 0;
> > +
> > +    if (!(s->rx_ctl0 & RX_CTL0_RX_EN)) {
> > +        return -1;
> > +    }
> > +
> > +    s->rx_desc_curr = aw_h3_emac_get_rx_desc(s, &desc,
> AW_H3_EMAC_MIN_PKT_SZ);
> > +    if (!s->rx_desc_curr) {
> > +        s->int_sta |= INT_STA_RX_BUF_UA;
> > +    }
> > +
> > +    /* Keep filling RX descriptors until the whole frame is written */
> > +    while (s->rx_desc_curr && bytes_left > 0) {
> > +        desc.status &= ~DESC_STATUS_CTL;
> > +        desc.status &= ~RX_DESC_STATUS_FRM_LEN_MASK;
> > +
> > +        if (bytes_left == size) {
> > +            desc.status |= RX_DESC_STATUS_FIRST_DESC;
> > +        }
> > +
> > +        if ((desc.status2 & DESC_STATUS2_BUF_SIZE_MASK) <
> > +            (bytes_left + pad_fcs_size)) {
> > +            desc_bytes = desc.status2 & DESC_STATUS2_BUF_SIZE_MASK;
> > +            desc.status |= desc_bytes << RX_DESC_STATUS_FRM_LEN_SHIFT;
> > +        } else {
> > +            padding = pad_fcs_size;
> > +            if (bytes_left < AW_H3_EMAC_MIN_PKT_SZ) {
> > +                padding += (AW_H3_EMAC_MIN_PKT_SZ - bytes_left);
> > +            }
> > +
> > +            desc_bytes = (bytes_left);
> > +            desc.status |= RX_DESC_STATUS_LAST_DESC;
> > +            desc.status |= (bytes_left + padding)
> > +                            << RX_DESC_STATUS_FRM_LEN_SHIFT;
> > +        }
> > +
> > +        cpu_physical_memory_write(desc.addr, buf, desc_bytes);
> > +        aw_h3_emac_flush_desc(&desc, s->rx_desc_curr);
> > +        trace_aw_h3_emac_receive(s->rx_desc_curr, desc.addr,
> desc_bytes);
> > +
> > +        /* Check if frame needs to raise the receive interrupt */
> > +        if (!(desc.status2 & RX_DESC_STATUS2_RX_INT_CTL)) {
> > +            s->int_sta |= INT_STA_RX;
> > +        }
> > +
> > +        /* Increment variables */
> > +        buf += desc_bytes;
> > +        bytes_left -= desc_bytes;
> > +
> > +        /* Move to the next descriptor */
> > +        s->rx_desc_curr = aw_h3_emac_next_desc(&desc, 64);
> > +        if (!s->rx_desc_curr) {
> > +            /* Not enough buffer space available */
> > +            s->int_sta |= INT_STA_RX_BUF_UA;
> > +            s->rx_desc_curr = s->rx_desc_head;
> > +            break;
> > +        }
> > +    }
> > +
> > +    /* Report receive DMA is finished */
> > +    s->rx_ctl1 &= ~RX_CTL1_RX_DMA_START;
> > +    aw_h3_emac_update_irq(s);
> > +
> > +    return size;
> > +}
> > +
> > +static void aw_h3_emac_transmit(AwH3EmacState *s)
> > +{
> > +    NetClientState *nc = qemu_get_queue(s->nic);
> > +    FrameDescriptor desc;
> > +    size_t bytes = 0;
> > +    size_t packet_bytes = 0;
> > +    size_t transmitted = 0;
> > +    static uint8_t packet_buf[2048];
> > +
> > +    s->tx_desc_curr = aw_h3_emac_get_tx_desc(s, &desc, 0);
> > +
> > +    /* Read all transmit descriptors */
> > +    while (s->tx_desc_curr != 0) {
> > +
> > +        /* Read from physical memory into packet buffer */
> > +        bytes = desc.status2 & DESC_STATUS2_BUF_SIZE_MASK;
> > +        if (bytes + packet_bytes > sizeof(packet_buf)) {
> > +            desc.status |= TX_DESC_STATUS_LENGTH_ERR;
> > +            break;
> > +        }
> > +        cpu_physical_memory_read(desc.addr, packet_buf + packet_bytes,
> bytes);
> > +        packet_bytes += bytes;
> > +        desc.status &= ~DESC_STATUS_CTL;
> > +        aw_h3_emac_flush_desc(&desc, s->tx_desc_curr);
> > +
> > +        /* After the last descriptor, send the packet */
> > +        if (desc.status2 & TX_DESC_STATUS2_LAST_DESC) {
> > +            if (desc.status2 & TX_DESC_STATUS2_CHECKSUM_MASK) {
> > +                net_checksum_calculate(packet_buf, packet_bytes);
> > +            }
> > +
> > +            qemu_send_packet(nc, packet_buf, packet_bytes);
> > +            trace_aw_h3_emac_transmit(s->tx_desc_curr, desc.addr,
> bytes);
> > +
> > +            packet_bytes = 0;
> > +            transmitted++;
> > +        }
> > +        s->tx_desc_curr = aw_h3_emac_next_desc(&desc, 0);
> > +    }
> > +
> > +    /* Raise transmit completed interrupt */
> > +    if (transmitted > 0) {
> > +        s->int_sta |= INT_STA_TX;
> > +        s->tx_ctl1 &= ~TX_CTL1_TX_DMA_START;
> > +        aw_h3_emac_update_irq(s);
> > +    }
> > +}
> > +
> > +static void aw_h3_emac_reset(DeviceState *dev)
> > +{
> > +    AwH3EmacState *s = AW_H3_EMAC(dev);
> > +    NetClientState *nc = qemu_get_queue(s->nic);
> > +
> > +    trace_aw_h3_emac_reset();
> > +
> > +    s->mii_cmd = 0;
> > +    s->mii_data = 0;
> > +    s->basic_ctl0 = 0;
> > +    s->basic_ctl1 = 0;
> > +    s->int_en = 0;
> > +    s->int_sta = 0;
> > +    s->frm_flt = 0;
> > +    s->rx_ctl0 = 0;
> > +    s->rx_ctl1 = RX_CTL1_RX_MD;
> > +    s->rx_desc_head = 0;
> > +    s->rx_desc_curr = 0;
> > +    s->tx_ctl0 = 0;
> > +    s->tx_ctl1 = 0;
> > +    s->tx_desc_head = 0;
> > +    s->tx_desc_curr = 0;
> > +    s->tx_flowctl = 0;
> > +
> > +    aw_h3_emac_mii_reset(s, !nc->link_down);
> > +}
> > +
> > +static uint64_t aw_h3_emac_read(void *opaque, hwaddr offset, unsigned
> size)
> > +{
> > +    AwH3EmacState *s = opaque;
>
> I'd put AW_H3_EMAC(opaque) here.
>
> > +    uint64_t value = 0;
> > +    FrameDescriptor desc;
> > +
> > +    switch (offset) {
> > +    case REG_BASIC_CTL_0:       /* Basic Control 0 */
> > +        value = s->basic_ctl0;
> > +        break;
> > +    case REG_BASIC_CTL_1:       /* Basic Control 1 */
> > +        value = s->basic_ctl1;
> > +        break;
> > +    case REG_INT_STA:           /* Interrupt Status */
> > +        value = s->int_sta;
> > +        break;
> > +    case REG_INT_EN:            /* Interupt Enable */
> > +        value = s->int_en;
> > +        break;
> > +    case REG_TX_CTL_0:          /* Transmit Control 0 */
> > +        value = s->tx_ctl0;
> > +        break;
> > +    case REG_TX_CTL_1:          /* Transmit Control 1 */
> > +        value = s->tx_ctl1;
> > +        break;
> > +    case REG_TX_FLOW_CTL:       /* Transmit Flow Control */
> > +        value = s->tx_flowctl;
> > +        break;
> > +    case REG_TX_DMA_DESC_LIST:  /* Transmit Descriptor List Address */
> > +        value = s->tx_desc_head;
> > +        break;
> > +    case REG_RX_CTL_0:          /* Receive Control 0 */
> > +        value = s->rx_ctl0;
> > +        break;
> > +    case REG_RX_CTL_1:          /* Receive Control 1 */
> > +        value = s->rx_ctl1;
> > +        break;
> > +    case REG_RX_DMA_DESC_LIST:  /* Receive Descriptor List Address */
> > +        value = s->rx_desc_head;
> > +        break;
> > +    case REG_FRM_FLT:           /* Receive Frame Filter */
> > +        value = s->frm_flt;
> > +        break;
> > +    case REG_RX_HASH_0:         /* Receive Hash Table 0 */
> > +    case REG_RX_HASH_1:         /* Receive Hash Table 1 */
> > +        break;
> > +    case REG_MII_CMD:           /* Management Interface Command */
> > +        value = s->mii_cmd;
> > +        break;
> > +    case REG_MII_DATA:          /* Management Interface Data */
> > +        value = s->mii_data;
> > +        break;
> > +    case REG_ADDR_HIGH:         /* MAC Address High */
> > +        value = *(((uint32_t *) (s->conf.macaddr.a)) + 1);
> > +        break;
> > +    case REG_ADDR_LOW:          /* MAC Address Low */
> > +        value = *(uint32_t *) (s->conf.macaddr.a);
> > +        break;
> > +    case REG_TX_DMA_STA:        /* Transmit DMA Status */
> > +        break;
> > +    case REG_TX_CUR_DESC:       /* Transmit Current Descriptor */
> > +        value = s->tx_desc_curr;
> > +        break;
> > +    case REG_TX_CUR_BUF:        /* Transmit Current Buffer */
> > +        if (s->tx_desc_curr != 0) {
> > +            cpu_physical_memory_read(s->tx_desc_curr, &desc,
> sizeof(desc));
> > +            value = desc.addr;
> > +        } else {
> > +            value = 0;
> > +        }
> > +        break;
> > +    case REG_RX_DMA_STA:        /* Receive DMA Status */
> > +        break;
> > +    case REG_RX_CUR_DESC:       /* Receive Current Descriptor */
> > +        value = s->rx_desc_curr;
> > +        break;
> > +    case REG_RX_CUR_BUF:        /* Receive Current Buffer */
> > +        if (s->rx_desc_curr != 0) {
> > +            cpu_physical_memory_read(s->rx_desc_curr, &desc,
> sizeof(desc));
> > +            value = desc.addr;
> > +        } else {
> > +            value = 0;
> > +        }
> > +        break;
> > +    default:
> > +        qemu_log_mask(LOG_UNIMP, "allwinner-h3-emac: read access to
> unknown "
> > +                                 "EMAC register 0x" TARGET_FMT_plx "\n",
> > +                                  offset);
> > +    }
> > +
> > +    trace_aw_h3_emac_read(offset, value);
> > +    return value;
> > +}
> > +
> > +static void aw_h3_emac_write(void *opaque, hwaddr offset, uint64_t
> value,
> > +                             unsigned size)
> > +{
> > +    AwH3EmacState *s = opaque;
>
> The same.
>
> > +    NetClientState *nc = qemu_get_queue(s->nic);
> > +
> > +    trace_aw_h3_emac_write(offset, value);
> > +
> > +    switch (offset) {
> > +    case REG_BASIC_CTL_0:       /* Basic Control 0 */
> > +        s->basic_ctl0 = value;
> > +        break;
> > +    case REG_BASIC_CTL_1:       /* Basic Control 1 */
> > +        if (value & BASIC_CTL1_SOFTRST) {
> > +            aw_h3_emac_reset(DEVICE(s));
> > +            value &= ~BASIC_CTL1_SOFTRST;
> > +        }
> > +        s->basic_ctl1 = value;
> > +        if (aw_h3_emac_can_receive(nc)) {
> > +            qemu_flush_queued_packets(nc);
> > +        }
> > +        break;
> > +    case REG_INT_STA:           /* Interrupt Status */
> > +        s->int_sta &= ~value;
> > +        aw_h3_emac_update_irq(s);
> > +        break;
> > +    case REG_INT_EN:            /* Interrupt Enable */
> > +        s->int_en = value;
> > +        aw_h3_emac_update_irq(s);
> > +        break;
> > +    case REG_TX_CTL_0:          /* Transmit Control 0 */
> > +        s->tx_ctl0 = value;
> > +        break;
> > +    case REG_TX_CTL_1:          /* Transmit Control 1 */
> > +        s->tx_ctl1 = value;
> > +        if (value & TX_CTL1_TX_DMA_EN) {
> > +            aw_h3_emac_transmit(s);
> > +        }
> > +        break;
> > +    case REG_TX_FLOW_CTL:       /* Transmit Flow Control */
> > +        s->tx_flowctl = value;
> > +        break;
> > +    case REG_TX_DMA_DESC_LIST:  /* Transmit Descriptor List Address */
> > +        s->tx_desc_head = value;
> > +        s->tx_desc_curr = value;
> > +        break;
> > +    case REG_RX_CTL_0:          /* Receive Control 0 */
> > +        s->rx_ctl0 = value;
> > +        break;
> > +    case REG_RX_CTL_1:          /* Receive Control 1 */
> > +        s->rx_ctl1 = value | RX_CTL1_RX_MD;
> > +        if ((value & RX_CTL1_RX_DMA_EN) && aw_h3_emac_can_receive(nc)) {
> > +            qemu_flush_queued_packets(nc);
> > +        }
> > +        break;
> > +    case REG_RX_DMA_DESC_LIST:  /* Receive Descriptor List Address */
> > +        s->rx_desc_head = value;
> > +        s->rx_desc_curr = value;
> > +        break;
> > +    case REG_FRM_FLT:           /* Receive Frame Filter */
> > +        s->frm_flt = value;
> > +        break;
> > +    case REG_RX_HASH_0:         /* Receive Hash Table 0 */
> > +    case REG_RX_HASH_1:         /* Receive Hash Table 1 */
> > +        break;
> > +    case REG_MII_CMD:           /* Management Interface Command */
> > +        s->mii_cmd = value & ~MII_CMD_PHY_BUSY;
> > +        aw_h3_emac_mii_cmd(s);
> > +        break;
> > +    case REG_MII_DATA:          /* Management Interface Data */
> > +        s->mii_data = value;
> > +        break;
> > +    case REG_ADDR_HIGH:         /* MAC Address High */
> > +        s->conf.macaddr.a[4] = (value & 0xff);
> > +        s->conf.macaddr.a[5] = (value & 0xff00) >> 8;
> > +        break;
> > +    case REG_ADDR_LOW:          /* MAC Address Low */
> > +        s->conf.macaddr.a[0] = (value & 0xff);
> > +        s->conf.macaddr.a[1] = (value & 0xff00) >> 8;
> > +        s->conf.macaddr.a[2] = (value & 0xff0000) >> 16;
> > +        s->conf.macaddr.a[3] = (value & 0xff000000) >> 24;
> > +        break;
> > +    case REG_TX_DMA_STA:        /* Transmit DMA Status */
> > +    case REG_TX_CUR_DESC:       /* Transmit Current Descriptor */
> > +    case REG_TX_CUR_BUF:        /* Transmit Current Buffer */
> > +    case REG_RX_DMA_STA:        /* Receive DMA Status */
> > +    case REG_RX_CUR_DESC:       /* Receive Current Descriptor */
> > +    case REG_RX_CUR_BUF:        /* Receive Current Buffer */
> > +    case REG_RGMII_STA:         /* RGMII Status */
> > +        break;
> > +    default:
> > +        qemu_log_mask(LOG_UNIMP, "allwinner-h3-emac: write access to
> unknown "
> > +                                 "EMAC register 0x" TARGET_FMT_plx "\n",
> > +                                  offset);
> > +    }
> > +}
> > +
> > +static void aw_h3_emac_set_link(NetClientState *nc)
> > +{
> > +    AwH3EmacState *s = qemu_get_nic_opaque(nc);
> > +
> > +    trace_aw_h3_emac_set_link(!nc->link_down);
> > +    aw_h3_emac_mii_set_link(s, !nc->link_down);
> > +}
> > +
> > +static const MemoryRegionOps aw_h3_emac_mem_ops = {
> > +    .read = aw_h3_emac_read,
> > +    .write = aw_h3_emac_write,
> > +    .endianness = DEVICE_NATIVE_ENDIAN,
> > +    .valid = {
> > +        .min_access_size = 4,
> > +        .max_access_size = 4,
> > +    },
> > +};
> > +
> > +static NetClientInfo net_aw_h3_emac_info = {
> > +    .type = NET_CLIENT_DRIVER_NIC,
> > +    .size = sizeof(NICState),
> > +    .can_receive = aw_h3_emac_can_receive,
> > +    .receive = aw_h3_emac_receive,
> > +    .link_status_changed = aw_h3_emac_set_link,
> > +};
> > +
> > +static void aw_h3_emac_init(Object *obj)
> > +{
> > +    SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
> > +    AwH3EmacState *s = AW_H3_EMAC(obj);
> > +
> > +    memory_region_init_io(&s->iomem, OBJECT(s), &aw_h3_emac_mem_ops, s,
> > +                          TYPE_AW_H3_EMAC, AW_H3_EMAC_REGS_MEM_SIZE);
> > +    sysbus_init_mmio(sbd, &s->iomem);
> > +    sysbus_init_irq(sbd, &s->irq);
> > +}
> > +
> > +static void aw_h3_emac_realize(DeviceState *dev, Error **errp)
> > +{
> > +    AwH3EmacState *s = AW_H3_EMAC(dev);
> > +
> > +    qemu_macaddr_default_if_unset(&s->conf.macaddr);
> > +    s->nic = qemu_new_nic(&net_aw_h3_emac_info, &s->conf,
> > +                          object_get_typename(OBJECT(dev)), dev->id, s);
> > +    qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a);
> > +}
> > +
> > +static Property aw_h3_emac_properties[] = {
> > +    DEFINE_NIC_PROPERTIES(AwH3EmacState, conf),
> > +    DEFINE_PROP_UINT8("phy-addr", AwH3EmacState, mii_phy_addr, 0),
> > +    DEFINE_PROP_END_OF_LIST(),
> > +};
> > +
> > +static int aw_h3_emac_post_load(void *opaque, int version_id)
> > +{
> > +    AwH3EmacState *s = opaque;
> > +
> > +    aw_h3_emac_set_link(qemu_get_queue(s->nic));
> > +
> > +    return 0;
> > +}
> > +
> > +static const VMStateDescription vmstate_aw_emac = {
> > +    .name = TYPE_AW_H3_EMAC,
> > +    .version_id = 1,
> > +    .minimum_version_id = 1,
> > +    .post_load = aw_h3_emac_post_load,
> > +    .fields = (VMStateField[]) {
> > +        VMSTATE_UINT8(mii_phy_addr, AwH3EmacState),
> > +        VMSTATE_UINT32(mii_cmd, AwH3EmacState),
> > +        VMSTATE_UINT32(mii_data, AwH3EmacState),
> > +        VMSTATE_UINT32(basic_ctl0, AwH3EmacState),
> > +        VMSTATE_UINT32(basic_ctl1, AwH3EmacState),
> > +        VMSTATE_UINT32(int_en, AwH3EmacState),
> > +        VMSTATE_UINT32(int_sta, AwH3EmacState),
> > +        VMSTATE_UINT32(frm_flt, AwH3EmacState),
> > +        VMSTATE_UINT32(rx_ctl0, AwH3EmacState),
> > +        VMSTATE_UINT32(rx_ctl1, AwH3EmacState),
> > +        VMSTATE_UINT32(rx_desc_head, AwH3EmacState),
> > +        VMSTATE_UINT32(rx_desc_curr, AwH3EmacState),
> > +        VMSTATE_UINT32(tx_ctl0, AwH3EmacState),
> > +        VMSTATE_UINT32(tx_ctl1, AwH3EmacState),
> > +        VMSTATE_UINT32(tx_desc_head, AwH3EmacState),
> > +        VMSTATE_UINT32(tx_desc_curr, AwH3EmacState),
> > +        VMSTATE_UINT32(tx_flowctl, AwH3EmacState),
> > +        VMSTATE_END_OF_LIST()
> > +    }
> > +};
> > +
> > +static void aw_h3_emac_class_init(ObjectClass *klass, void *data)
> > +{
> > +    DeviceClass *dc = DEVICE_CLASS(klass);
> > +
> > +    dc->realize = aw_h3_emac_realize;
> > +    dc->props = aw_h3_emac_properties;
> > +    dc->reset = aw_h3_emac_reset;
> > +    dc->vmsd = &vmstate_aw_emac;
> > +}
> > +
> > +static const TypeInfo aw_h3_emac_info = {
> > +    .name           = TYPE_AW_H3_EMAC,
> > +    .parent         = TYPE_SYS_BUS_DEVICE,
> > +    .instance_size  = sizeof(AwH3EmacState),
> > +    .instance_init  = aw_h3_emac_init,
> > +    .class_init     = aw_h3_emac_class_init,
> > +};
> > +
> > +static void aw_h3_emac_register_types(void)
> > +{
> > +    type_register_static(&aw_h3_emac_info);
> > +}
> > +
> > +type_init(aw_h3_emac_register_types)
> > diff --git a/hw/net/trace-events b/hw/net/trace-events
> > index e70f12bee1..e9e2f26f68 100644
> > --- a/hw/net/trace-events
> > +++ b/hw/net/trace-events
> > @@ -1,5 +1,15 @@
> >   # See docs/devel/tracing.txt for syntax documentation.
> >
> > +# allwinner-h3-emac.c
> > +aw_h3_emac_mii_write_reg(uint32_t reg, uint32_t value) "MII write:
> reg=0x%x value=0x%x"
> > +aw_h3_emac_mii_read_reg(uint32_t reg, uint32_t value) "MII read:
> reg=0x%x value=0x%x"
> > +aw_h3_emac_receive(uint32_t desc, uint32_t paddr, uint32_t bytes) "RX
> packet: desc=0x%08x paddr=0x%08x bytes=%u"
> > +aw_h3_emac_transmit(uint32_t desc, uint32_t paddr, uint32_t bytes) "TX
> packet: desc=0x%08x paddr=0x%08x bytes=%u"
> > +aw_h3_emac_reset(void) "HW reset"
> > +aw_h3_emac_set_link(bool active) "Set link: active=%u"
> > +aw_h3_emac_read(uint64_t offset, uint64_t val) "MMIO read: offset=0x%"
> PRIx64 " value=0x%" PRIx64
> > +aw_h3_emac_write(uint64_t offset, uint64_t val) "MMIO write:
> offset=0x%" PRIx64 " value=0x%" PRIx64
> > +
> >   # etraxfs_eth.c
> >   mdio_phy_read(int regnum, uint16_t value) "read phy_reg:%d
> value:0x%04x"
> >   mdio_phy_write(int regnum, uint16_t value) "write phy_reg:%d
> value:0x%04x"
> > diff --git a/include/hw/arm/allwinner-h3.h
> b/include/hw/arm/allwinner-h3.h
> > index 7aff4ebbd2..b964a60f41 100644
> > --- a/include/hw/arm/allwinner-h3.h
> > +++ b/include/hw/arm/allwinner-h3.h
> > @@ -31,6 +31,7 @@
> >   #include "hw/misc/allwinner-h3-syscon.h"
> >   #include "hw/misc/allwinner-h3-sid.h"
> >   #include "hw/sd/allwinner-h3-sdhost.h"
> > +#include "hw/net/allwinner-h3-emac.h"
> >   #include "target/arm/cpu.h"
> >
> >   #define AW_H3_SRAM_A1_BASE     (0x00000000)
> > @@ -119,6 +120,7 @@ typedef struct AwH3State {
> >       AwH3SysconState syscon;
> >       AwH3SidState sid;
> >       AwH3SDHostState mmc0;
> > +    AwH3EmacState emac;
> >       GICState gic;
> >       MemoryRegion sram_a1;
> >       MemoryRegion sram_a2;
> > diff --git a/include/hw/net/allwinner-h3-emac.h
> b/include/hw/net/allwinner-h3-emac.h
> > new file mode 100644
> > index 0000000000..a007d54472
> > --- /dev/null
> > +++ b/include/hw/net/allwinner-h3-emac.h
> > @@ -0,0 +1,69 @@
> > +/*
> > + * Allwinner H3 EMAC 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/
> >.
> > + */
> > +
> > +#ifndef ALLWINNER_H3_EMAC_H
> > +#define ALLWINNER_H3_EMAC_H
> > +
> > +#include "qemu/units.h"
> > +#include "net/net.h"
> > +#include "qemu/fifo8.h"
> > +#include "hw/net/mii.h"
> > +#include "hw/sysbus.h"
> > +
> > +#define AW_H3_EMAC_REGS_MEM_SIZE  (1024)
> > +
> > +#define TYPE_AW_H3_EMAC "allwinner-h3-emac"
> > +#define AW_H3_EMAC(obj) OBJECT_CHECK(AwH3EmacState, (obj),
> TYPE_AW_H3_EMAC)
> > +
> > +typedef struct AwH3EmacState {
> > +    /*< private >*/
> > +    SysBusDevice  parent_obj;
> > +    /*< public >*/
> > +
> > +    MemoryRegion iomem;
> > +    qemu_irq     irq;
> > +    NICState     *nic;
> > +    NICConf      conf;
> > +
> > +    uint8_t      mii_phy_addr;
> > +    uint32_t     mii_cmd;
> > +    uint32_t     mii_data;
> > +    uint32_t     mii_cr;
> > +    uint32_t     mii_st;
> > +
> > +    uint32_t     basic_ctl0;
> > +    uint32_t     basic_ctl1;
> > +    uint32_t     int_en;
> > +    uint32_t     int_sta;
> > +    uint32_t     frm_flt;
> > +
> > +    uint32_t     rx_ctl0;
> > +    uint32_t     rx_ctl1;
> > +    uint32_t     rx_desc_head;
> > +    uint32_t     rx_desc_curr;
> > +
> > +    uint32_t     tx_ctl0;
> > +    uint32_t     tx_ctl1;
> > +    uint32_t     tx_desc_head;
> > +    uint32_t     tx_desc_curr;
> > +    uint32_t     tx_flowctl;
> > +
> > +} AwH3EmacState;
> > +
> > +#endif
> >
>
> The rest seems ok to me. Thanks for the contribution :)!
>

Thanks! :-)

Regards,
Niek


> Cheers,
> Fred
>
>

-- 
Niek Linnenbank

WWW: http://www.nieklinnenbank.nl/
BLOG: http://nieklinnenbank.wordpress.com/
FUN:    http://www.FreeNOS.org/

[-- Attachment #2: Type: text/html, Size: 52784 bytes --]

<div dir="ltr"><div dir="ltr"><div>Hello Frederic,</div><div><br></div><div>Thank you for your quick review comments!</div><div>I&#39;ll start working on v2 of the patches and include the changes you suggested.<br></div></div><br><div class="gmail_quote"><div dir="ltr" class="gmail_attr">On Tue, Dec 3, 2019 at 10:33 AM KONRAD Frederic &lt;<a href="mailto:frederic.konrad@adacore.com">frederic.konrad@adacore.com</a>&gt; wrote:<br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><br>
<br>
Le 12/2/19 à 10:09 PM, Niek Linnenbank a écrit :<br>
&gt; The Allwinner H3 System on Chip includes an Ethernet MAC (EMAC)<br>
&gt; which provides 10M/100M/1000M Ethernet connectivity. This commit<br>
&gt; adds support for the Allwinner H3 EMAC, including emulation for<br>
&gt; the following functionality:<br>
&gt; <br>
&gt;   * DMA transfers<br>
&gt;   * MII interface<br>
&gt;   * Transmit CRC calculation<br>
&gt; <br>
&gt; Signed-off-by: Niek Linnenbank &lt;<a href="mailto:nieklinnenbank@gmail.com" target="_blank">nieklinnenbank@gmail.com</a>&gt;<br>
&gt; ---<br>
&gt;   hw/arm/Kconfig                     |   1 +<br>
&gt;   hw/arm/allwinner-h3.c              |  17 +<br>
&gt;   hw/arm/orangepi.c                  |   7 +<br>
&gt;   hw/net/Kconfig                     |   3 +<br>
&gt;   hw/net/Makefile.objs               |   1 +<br>
&gt;   hw/net/allwinner-h3-emac.c         | 786 +++++++++++++++++++++++++++++<br>
&gt;   hw/net/trace-events                |  10 +<br>
&gt;   include/hw/arm/allwinner-h3.h      |   2 +<br>
&gt;   include/hw/net/allwinner-h3-emac.h |  69 +++<br>
&gt;   9 files changed, 896 insertions(+)<br>
&gt;   create mode 100644 hw/net/allwinner-h3-emac.c<br>
&gt;   create mode 100644 include/hw/net/allwinner-h3-emac.h<br>
&gt; <br>
&gt; diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig<br>
&gt; index ebf8d2325f..551cff3442 100644<br>
&gt; --- a/hw/arm/Kconfig<br>
&gt; +++ b/hw/arm/Kconfig<br>
&gt; @@ -294,6 +294,7 @@ config ALLWINNER_A10<br>
&gt;   config ALLWINNER_H3<br>
&gt;       bool<br>
&gt;       select ALLWINNER_A10_PIT<br>
&gt; +    select ALLWINNER_H3_EMAC<br>
&gt;       select SERIAL<br>
&gt;       select ARM_TIMER<br>
&gt;       select ARM_GIC<br>
&gt; diff --git a/hw/arm/allwinner-h3.c b/hw/arm/allwinner-h3.c<br>
&gt; index c2972caf88..274b8548c0 100644<br>
&gt; --- a/hw/arm/allwinner-h3.c<br>
&gt; +++ b/hw/arm/allwinner-h3.c<br>
&gt; @@ -53,6 +53,9 @@ static void aw_h3_init(Object *obj)<br>
&gt;   <br>
&gt;       sysbus_init_child_obj(obj, &quot;mmc0&quot;, &amp;s-&gt;mmc0, sizeof(s-&gt;mmc0),<br>
&gt;                             TYPE_AW_H3_SDHOST);<br>
&gt; +<br>
&gt; +    sysbus_init_child_obj(obj, &quot;emac&quot;, &amp;s-&gt;emac, sizeof(s-&gt;emac),<br>
&gt; +                          TYPE_AW_H3_EMAC);<br>
&gt;   }<br>
&gt;   <br>
&gt;   static void aw_h3_realize(DeviceState *dev, Error **errp)<br>
&gt; @@ -237,6 +240,20 @@ static void aw_h3_realize(DeviceState *dev, Error **errp)<br>
&gt;           return;<br>
&gt;       }<br>
&gt;   <br>
&gt; +    /* EMAC */<br>
&gt; +    if (nd_table[0].used) {<br>
&gt; +        qemu_check_nic_model(&amp;nd_table[0], TYPE_AW_H3_EMAC);<br>
&gt; +        qdev_set_nic_properties(DEVICE(&amp;s-&gt;emac), &amp;nd_table[0]);<br>
&gt; +    }<br>
&gt; +    object_property_set_bool(OBJECT(&amp;s-&gt;emac), true, &quot;realized&quot;, &amp;err);<br>
&gt; +    if (err != NULL) {<br>
&gt; +        error_propagate(errp, err);<br>
&gt; +        return;<br>
&gt; +    }<br>
&gt; +    sysbusdev = SYS_BUS_DEVICE(&amp;s-&gt;emac);<br>
&gt; +    sysbus_mmio_map(sysbusdev, 0, AW_H3_EMAC_BASE);<br>
&gt; +    sysbus_connect_irq(sysbusdev, 0, s-&gt;irq[AW_H3_GIC_SPI_EMAC]);<br>
&gt; +<br>
&gt;       /* Universal Serial Bus */<br>
&gt;       sysbus_create_simple(TYPE_AW_H3_EHCI, AW_H3_EHCI0_BASE,<br>
&gt;                            s-&gt;irq[AW_H3_GIC_SPI_EHCI0]);<br>
&gt; diff --git a/hw/arm/orangepi.c b/hw/arm/orangepi.c<br>
&gt; index dee3efaf08..8a61eb0e69 100644<br>
&gt; --- a/hw/arm/orangepi.c<br>
&gt; +++ b/hw/arm/orangepi.c<br>
&gt; @@ -61,6 +61,13 @@ static void orangepi_init(MachineState *machine)<br>
&gt;           exit(1);<br>
&gt;       }<br>
&gt;   <br>
&gt; +    /* Setup EMAC properties */<br>
&gt; +    object_property_set_int(OBJECT(&amp;s-&gt;h3-&gt;emac), 1, &quot;phy-addr&quot;, &amp;err);<br>
&gt; +    if (err != NULL) {<br>
&gt; +        error_reportf_err(err, &quot;Couldn&#39;t set phy address: &quot;);<br>
&gt; +        exit(1);<br>
&gt; +    }<br>
&gt; +<br>
&gt;       /* Mark H3 object realized */<br>
&gt;       object_property_set_bool(OBJECT(s-&gt;h3), true, &quot;realized&quot;, &amp;err);<br>
&gt;       if (err != NULL) {<br>
&gt; diff --git a/hw/net/Kconfig b/hw/net/Kconfig<br>
&gt; index 3856417d42..36d3923992 100644<br>
&gt; --- a/hw/net/Kconfig<br>
&gt; +++ b/hw/net/Kconfig<br>
&gt; @@ -74,6 +74,9 @@ config MIPSNET<br>
&gt;   config ALLWINNER_EMAC<br>
&gt;       bool<br>
&gt;   <br>
&gt; +config ALLWINNER_H3_EMAC<br>
&gt; +    bool<br>
&gt; +<br>
&gt;   config IMX_FEC<br>
&gt;       bool<br>
&gt;   <br>
&gt; diff --git a/hw/net/Makefile.objs b/hw/net/Makefile.objs<br>
&gt; index 7907d2c199..5548deb07a 100644<br>
&gt; --- a/hw/net/Makefile.objs<br>
&gt; +++ b/hw/net/Makefile.objs<br>
&gt; @@ -23,6 +23,7 @@ common-obj-$(CONFIG_XGMAC) += xgmac.o<br>
&gt;   common-obj-$(CONFIG_MIPSNET) += mipsnet.o<br>
&gt;   common-obj-$(CONFIG_XILINX_AXI) += xilinx_axienet.o<br>
&gt;   common-obj-$(CONFIG_ALLWINNER_EMAC) += allwinner_emac.o<br>
&gt; +common-obj-$(CONFIG_ALLWINNER_H3_EMAC) += allwinner-h3-emac.o<br>
&gt;   common-obj-$(CONFIG_IMX_FEC) += imx_fec.o<br>
&gt;   <br>
&gt;   common-obj-$(CONFIG_CADENCE) += cadence_gem.o<br>
&gt; diff --git a/hw/net/allwinner-h3-emac.c b/hw/net/allwinner-h3-emac.c<br>
&gt; new file mode 100644<br>
&gt; index 0000000000..37f6f44406<br>
&gt; --- /dev/null<br>
&gt; +++ b/hw/net/allwinner-h3-emac.c<br>
&gt; @@ -0,0 +1,786 @@<br>
&gt; +/*<br>
&gt; + * Allwinner H3 EMAC emulation<br>
&gt; + *<br>
&gt; + * Copyright (C) 2019 Niek Linnenbank &lt;<a href="mailto:nieklinnenbank@gmail.com" target="_blank">nieklinnenbank@gmail.com</a>&gt;<br>
&gt; + *<br>
&gt; + * This program is free software: you can redistribute it and/or modify<br>
&gt; + * it under the terms of the GNU General Public License as published by<br>
&gt; + * the Free Software Foundation, either version 2 of the License, or<br>
&gt; + * (at your option) any later version.<br>
&gt; + *<br>
&gt; + * This program is distributed in the hope that it will be useful,<br>
&gt; + * but WITHOUT ANY WARRANTY; without even the implied warranty of<br>
&gt; + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the<br>
&gt; + * GNU General Public License for more details.<br>
&gt; + *<br>
&gt; + * You should have received a copy of the GNU General Public License<br>
&gt; + * along with this program.  If not, see &lt;<a href="http://www.gnu.org/licenses/" rel="noreferrer" target="_blank">http://www.gnu.org/licenses/</a>&gt;.<br>
&gt; + */<br>
&gt; +<br>
&gt; +#include &quot;qemu/osdep.h&quot;<br>
&gt; +#include &quot;hw/sysbus.h&quot;<br>
&gt; +#include &quot;migration/vmstate.h&quot;<br>
&gt; +#include &quot;net/net.h&quot;<br>
&gt; +#include &quot;hw/irq.h&quot;<br>
&gt; +#include &quot;hw/qdev-properties.h&quot;<br>
&gt; +#include &quot;qemu/log.h&quot;<br>
&gt; +#include &quot;trace.h&quot;<br>
&gt; +#include &quot;net/checksum.h&quot;<br>
&gt; +#include &quot;qemu/module.h&quot;<br>
&gt; +#include &quot;exec/cpu-common.h&quot;<br>
&gt; +#include &quot;hw/net/allwinner-h3-emac.h&quot;<br>
&gt; +<br>
&gt; +/* EMAC register offsets */<br>
&gt; +#define REG_BASIC_CTL_0        (0x0000) /* Basic Control 0 */<br>
&gt; +#define REG_BASIC_CTL_1        (0x0004) /* Basic Control 1 */<br>
&gt; +#define REG_INT_STA            (0x0008) /* Interrupt Status */<br>
&gt; +#define REG_INT_EN             (0x000C) /* Interrupt Enable */<br>
&gt; +#define REG_TX_CTL_0           (0x0010) /* Transmit Control 0 */<br>
&gt; +#define REG_TX_CTL_1           (0x0014) /* Transmit Control 1 */<br>
&gt; +#define REG_TX_FLOW_CTL        (0x001C) /* Transmit Flow Control */<br>
&gt; +#define REG_TX_DMA_DESC_LIST   (0x0020) /* Transmit Descriptor List Address */<br>
&gt; +#define REG_RX_CTL_0           (0x0024) /* Receive Control 0 */<br>
&gt; +#define REG_RX_CTL_1           (0x0028) /* Receive Control 1 */<br>
&gt; +#define REG_RX_DMA_DESC_LIST   (0x0034) /* Receive Descriptor List Address */<br>
&gt; +#define REG_FRM_FLT            (0x0038) /* Receive Frame Filter */<br>
&gt; +#define REG_RX_HASH_0          (0x0040) /* Receive Hash Table 0 */<br>
&gt; +#define REG_RX_HASH_1          (0x0044) /* Receive Hash Table 1 */<br>
&gt; +#define REG_MII_CMD            (0x0048) /* Management Interface Command */<br>
&gt; +#define REG_MII_DATA           (0x004C) /* Management Interface Data */<br>
&gt; +#define REG_ADDR_HIGH          (0x0050) /* MAC Address High */<br>
&gt; +#define REG_ADDR_LOW           (0x0054) /* MAC Address Low */<br>
&gt; +#define REG_TX_DMA_STA         (0x00B0) /* Transmit DMA Status */<br>
&gt; +#define REG_TX_CUR_DESC        (0x00B4) /* Transmit Current Descriptor */<br>
&gt; +#define REG_TX_CUR_BUF         (0x00B8) /* Transmit Current Buffer */<br>
&gt; +#define REG_RX_DMA_STA         (0x00C0) /* Receive DMA Status */<br>
&gt; +#define REG_RX_CUR_DESC        (0x00C4) /* Receive Current Descriptor */<br>
&gt; +#define REG_RX_CUR_BUF         (0x00C8) /* Receive Current Buffer */<br>
&gt; +#define REG_RGMII_STA          (0x00D0) /* RGMII Status */<br>
&gt; +<br>
&gt; +/* EMAC register flags */<br>
&gt; +#define BASIC_CTL0_100Mbps     (0b11 &lt;&lt; 2)<br>
&gt; +#define BASIC_CTL0_FD          (1 &lt;&lt; 0)<br>
&gt; +#define BASIC_CTL1_SOFTRST     (1 &lt;&lt; 0)<br>
&gt; +<br>
&gt; +#define INT_STA_RGMII_LINK     (1 &lt;&lt; 16)<br>
&gt; +#define INT_STA_RX_EARLY       (1 &lt;&lt; 13)<br>
&gt; +#define INT_STA_RX_OVERFLOW    (1 &lt;&lt; 12)<br>
&gt; +#define INT_STA_RX_TIMEOUT     (1 &lt;&lt; 11)<br>
&gt; +#define INT_STA_RX_DMA_STOP    (1 &lt;&lt; 10)<br>
&gt; +#define INT_STA_RX_BUF_UA      (1 &lt;&lt; 9)<br>
&gt; +#define INT_STA_RX             (1 &lt;&lt; 8)<br>
&gt; +#define INT_STA_TX_EARLY       (1 &lt;&lt; 5)<br>
&gt; +#define INT_STA_TX_UNDERFLOW   (1 &lt;&lt; 4)<br>
&gt; +#define INT_STA_TX_TIMEOUT     (1 &lt;&lt; 3)<br>
&gt; +#define INT_STA_TX_BUF_UA      (1 &lt;&lt; 2)<br>
&gt; +#define INT_STA_TX_DMA_STOP    (1 &lt;&lt; 1)<br>
&gt; +#define INT_STA_TX             (1 &lt;&lt; 0)<br>
&gt; +<br>
&gt; +#define INT_EN_RX_EARLY        (1 &lt;&lt; 13)<br>
&gt; +#define INT_EN_RX_OVERFLOW     (1 &lt;&lt; 12)<br>
&gt; +#define INT_EN_RX_TIMEOUT      (1 &lt;&lt; 11)<br>
&gt; +#define INT_EN_RX_DMA_STOP     (1 &lt;&lt; 10)<br>
&gt; +#define INT_EN_RX_BUF_UA       (1 &lt;&lt; 9)<br>
&gt; +#define INT_EN_RX              (1 &lt;&lt; 8)<br>
&gt; +#define INT_EN_TX_EARLY        (1 &lt;&lt; 5)<br>
&gt; +#define INT_EN_TX_UNDERFLOW    (1 &lt;&lt; 4)<br>
&gt; +#define INT_EN_TX_TIMEOUT      (1 &lt;&lt; 3)<br>
&gt; +#define INT_EN_TX_BUF_UA       (1 &lt;&lt; 2)<br>
&gt; +#define INT_EN_TX_DMA_STOP     (1 &lt;&lt; 1)<br>
&gt; +#define INT_EN_TX              (1 &lt;&lt; 0)<br>
&gt; +<br>
&gt; +#define TX_CTL0_TX_EN          (1 &lt;&lt; 31)<br>
&gt; +#define TX_CTL1_TX_DMA_START   (1 &lt;&lt; 31)<br>
&gt; +#define TX_CTL1_TX_DMA_EN      (1 &lt;&lt; 30)<br>
&gt; +#define TX_CTL1_TX_FLUSH       (1 &lt;&lt; 0)<br>
&gt; +<br>
&gt; +#define RX_CTL0_RX_EN          (1 &lt;&lt; 31)<br>
&gt; +#define RX_CTL0_STRIP_FCS      (1 &lt;&lt; 28)<br>
&gt; +#define RX_CTL0_CRC_IPV4       (1 &lt;&lt; 27)<br>
&gt; +<br>
&gt; +#define RX_CTL1_RX_DMA_START   (1 &lt;&lt; 31)<br>
&gt; +#define RX_CTL1_RX_DMA_EN      (1 &lt;&lt; 30)<br>
&gt; +#define RX_CTL1_RX_MD          (1 &lt;&lt; 1)<br>
&gt; +<br>
&gt; +#define RX_FRM_FLT_DIS_ADDR    (1 &lt;&lt; 31)<br>
&gt; +<br>
&gt; +#define MII_CMD_PHY_ADDR_SHIFT (12)<br>
&gt; +#define MII_CMD_PHY_ADDR_MASK  (0xf000)<br>
&gt; +#define MII_CMD_PHY_REG_SHIFT  (4)<br>
&gt; +#define MII_CMD_PHY_REG_MASK   (0xf0)<br>
&gt; +#define MII_CMD_PHY_RW         (1 &lt;&lt; 1)<br>
&gt; +#define MII_CMD_PHY_BUSY       (1 &lt;&lt; 0)<br>
&gt; +<br>
&gt; +#define TX_DMA_STA_STOP        (0b000)<br>
&gt; +#define TX_DMA_STA_RUN_FETCH   (0b001)<br>
&gt; +#define TX_DMA_STA_WAIT_STA    (0b010)<br>
&gt; +<br>
&gt; +#define RX_DMA_STA_STOP        (0b000)<br>
&gt; +#define RX_DMA_STA_RUN_FETCH   (0b001)<br>
&gt; +#define RX_DMA_STA_WAIT_FRM    (0b011)<br>
&gt; +<br>
&gt; +#define RGMII_LINK_UP          (1 &lt;&lt; 3)<br>
&gt; +#define RGMII_FD               (1 &lt;&lt; 0)<br>
&gt; +<br>
&gt; +/* EMAC register reset values */<br>
&gt; +#define REG_BASIC_CTL_1_RST    (0x08000000)<br>
&gt; +<br>
&gt; +/* EMAC constants */<br>
&gt; +#define AW_H3_EMAC_MIN_PKT_SZ  (64)<br>
&gt; +<br>
&gt; +/* Transmit/receive frame descriptor */<br>
&gt; +typedef struct FrameDescriptor {<br>
&gt; +    uint32_t status;<br>
&gt; +    uint32_t status2;<br>
&gt; +    uint32_t addr;<br>
&gt; +    uint32_t next;<br>
&gt; +} FrameDescriptor;<br>
&gt; +<br>
&gt; +/* Frame descriptor flags */<br>
&gt; +#define DESC_STATUS_CTL                 (1 &lt;&lt; 31)<br>
&gt; +#define DESC_STATUS2_BUF_SIZE_MASK      (0x7ff)<br>
&gt; +<br>
&gt; +/* Transmit frame descriptor flags */<br>
&gt; +#define TX_DESC_STATUS_LENGTH_ERR       (1 &lt;&lt; 14)<br>
&gt; +#define TX_DESC_STATUS2_FIRST_DESC      (1 &lt;&lt; 29)<br>
&gt; +#define TX_DESC_STATUS2_LAST_DESC       (1 &lt;&lt; 30)<br>
&gt; +#define TX_DESC_STATUS2_CHECKSUM_MASK   (0x3 &lt;&lt; 27)<br>
&gt; +<br>
&gt; +/* Receive frame descriptor flags */<br>
&gt; +#define RX_DESC_STATUS_FIRST_DESC       (1 &lt;&lt; 9)<br>
&gt; +#define RX_DESC_STATUS_LAST_DESC        (1 &lt;&lt; 8)<br>
&gt; +#define RX_DESC_STATUS_FRM_LEN_MASK     (0x3fff0000)<br>
&gt; +#define RX_DESC_STATUS_FRM_LEN_SHIFT    (16)<br>
&gt; +#define RX_DESC_STATUS_NO_BUF           (1 &lt;&lt; 14)<br>
&gt; +#define RX_DESC_STATUS_HEADER_ERR       (1 &lt;&lt; 7)<br>
&gt; +#define RX_DESC_STATUS_LENGTH_ERR       (1 &lt;&lt; 4)<br>
&gt; +#define RX_DESC_STATUS_CRC_ERR          (1 &lt;&lt; 1)<br>
&gt; +#define RX_DESC_STATUS_PAYLOAD_ERR      (1 &lt;&lt; 0)<br>
&gt; +#define RX_DESC_STATUS2_RX_INT_CTL      (1 &lt;&lt; 31)<br>
&gt; +<br>
&gt; +/* MII register offsets */<br>
&gt; +#define MII_REG_CR                      (0x0)<br>
&gt; +#define MII_REG_ST                      (0x1)<br>
&gt; +#define MII_REG_ID_HIGH                 (0x2)<br>
&gt; +#define MII_REG_ID_LOW                  (0x3)<br>
&gt; +<br>
&gt; +/* MII register flags */<br>
&gt; +#define MII_REG_CR_RESET                (1 &lt;&lt; 15)<br>
&gt; +#define MII_REG_CR_POWERDOWN            (1 &lt;&lt; 11)<br>
&gt; +#define MII_REG_CR_10Mbit               (0)<br>
&gt; +#define MII_REG_CR_100Mbit              (1 &lt;&lt; 13)<br>
&gt; +#define MII_REG_CR_1000Mbit             (1 &lt;&lt; 6)<br>
&gt; +#define MII_REG_CR_AUTO_NEG             (1 &lt;&lt; 12)<br>
&gt; +#define MII_REG_CR_AUTO_NEG_RESTART     (1 &lt;&lt; 9)<br>
&gt; +#define MII_REG_CR_FULLDUPLEX           (1 &lt;&lt; 8)<br>
&gt; +<br>
&gt; +#define MII_REG_ST_100BASE_T4           (1 &lt;&lt; 15)<br>
&gt; +#define MII_REG_ST_100BASE_X_FD         (1 &lt;&lt; 14)<br>
&gt; +#define MII_REG_ST_100BASE_X_HD         (1 &lt;&lt; 13)<br>
&gt; +#define MII_REG_ST_10_FD                (1 &lt;&lt; 12)<br>
&gt; +#define MII_REG_ST_10_HD                (1 &lt;&lt; 11)<br>
&gt; +#define MII_REG_ST_100BASE_T2_FD        (1 &lt;&lt; 10)<br>
&gt; +#define MII_REG_ST_100BASE_T2_HD        (1 &lt;&lt; 9)<br>
&gt; +#define MII_REG_ST_AUTONEG_COMPLETE     (1 &lt;&lt; 5)<br>
&gt; +#define MII_REG_ST_AUTONEG_AVAIL        (1 &lt;&lt; 3)<br>
&gt; +#define MII_REG_ST_LINK_UP              (1 &lt;&lt; 2)<br>
&gt; +<br>
&gt; +/* MII constants */<br>
&gt; +#define MII_PHY_ID_HIGH                 (0x0044)<br>
&gt; +#define MII_PHY_ID_LOW                  (0x1400)<br>
<br>
I wonder if we can&#39;t share all those mii stuff accross the network adapters<br>
instead of redoing the work everytime. I&#39;ve some patches about it I may post<br>
them sometimes.<br>
<br></blockquote><div><br></div><div>Indeed, that would be a nice improvement. In fact, I was looking for somekind of</div><div>MII library inside QEMU and could not find it. Then I saw that the other emulated ethernet cards</div><div>all have that code inside each file, so that is why I did the same here.</div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
&gt; +<br>
&gt; +static void aw_h3_emac_mii_set_link(AwH3EmacState *s, bool link_active)<br>
&gt; +{<br>
&gt; +    if (link_active) {<br>
&gt; +        s-&gt;mii_st |= MII_REG_ST_LINK_UP;<br>
&gt; +    } else {<br>
&gt; +        s-&gt;mii_st &amp;= ~MII_REG_ST_LINK_UP;<br>
&gt; +    }<br>
&gt; +}<br>
&gt; +<br>
&gt; +static void aw_h3_emac_mii_reset(AwH3EmacState *s, bool link_active)<br>
&gt; +{<br>
&gt; +    s-&gt;mii_cr = MII_REG_CR_100Mbit | MII_REG_CR_AUTO_NEG |<br>
&gt; +                MII_REG_CR_FULLDUPLEX;<br>
&gt; +    s-&gt;mii_st = MII_REG_ST_100BASE_T4 | MII_REG_ST_100BASE_X_FD |<br>
&gt; +                MII_REG_ST_100BASE_X_HD | MII_REG_ST_10_FD | MII_REG_ST_10_HD |<br>
&gt; +                MII_REG_ST_100BASE_T2_FD | MII_REG_ST_100BASE_T2_HD |<br>
&gt; +                MII_REG_ST_AUTONEG_COMPLETE | MII_REG_ST_AUTONEG_AVAIL;<br>
&gt; +<br>
&gt; +    aw_h3_emac_mii_set_link(s, link_active);<br>
&gt; +}<br>
&gt; +<br>
&gt; +static void aw_h3_emac_mii_cmd(AwH3EmacState *s)<br>
&gt; +{<br>
&gt; +    uint8_t addr, reg;<br>
&gt; +<br>
&gt; +    addr = (s-&gt;mii_cmd &amp; MII_CMD_PHY_ADDR_MASK) &gt;&gt; MII_CMD_PHY_ADDR_SHIFT;<br>
&gt; +    reg = (s-&gt;mii_cmd &amp; MII_CMD_PHY_REG_MASK) &gt;&gt; MII_CMD_PHY_REG_SHIFT;<br>
&gt; +<br>
&gt; +    if (addr != s-&gt;mii_phy_addr) {<br>
&gt; +        return;<br>
&gt; +    }<br>
&gt; +<br>
&gt; +    /* Read or write a PHY register? */<br>
&gt; +    if (s-&gt;mii_cmd &amp; MII_CMD_PHY_RW) {<br>
&gt; +        trace_aw_h3_emac_mii_write_reg(reg, s-&gt;mii_data);<br>
&gt; +<br>
&gt; +        switch (reg) {<br>
&gt; +        case MII_REG_CR:<br>
&gt; +            if (s-&gt;mii_data &amp; MII_REG_CR_RESET) {<br>
&gt; +                aw_h3_emac_mii_reset(s, s-&gt;mii_st &amp; MII_REG_ST_LINK_UP);<br>
&gt; +            } else {<br>
&gt; +                s-&gt;mii_cr = s-&gt;mii_data &amp; ~(MII_REG_CR_RESET |<br>
&gt; +                                            MII_REG_CR_AUTO_NEG_RESTART);<br>
&gt; +            }<br>
&gt; +            break;<br>
&gt; +        default:<br>
&gt; +            qemu_log_mask(LOG_UNIMP, &quot;allwinner-h3-emac: write access to &quot;<br>
&gt; +                                     &quot;unknown MII register 0x%x\n&quot;, reg);<br>
&gt; +            break;<br>
&gt; +        }<br>
&gt; +    } else {<br>
&gt; +        switch (reg) {<br>
&gt; +        case MII_REG_CR:<br>
&gt; +            s-&gt;mii_data = s-&gt;mii_cr;<br>
&gt; +            break;<br>
&gt; +        case MII_REG_ST:<br>
&gt; +            s-&gt;mii_data = s-&gt;mii_st;<br>
&gt; +            break;<br>
&gt; +        case MII_REG_ID_HIGH:<br>
&gt; +            s-&gt;mii_data = MII_PHY_ID_HIGH;<br>
&gt; +            break;<br>
&gt; +        case MII_REG_ID_LOW:<br>
&gt; +            s-&gt;mii_data = MII_PHY_ID_LOW;<br>
&gt; +            break;<br>
&gt; +        default:<br>
&gt; +            qemu_log_mask(LOG_UNIMP, &quot;allwinner-h3-emac: read access to &quot;<br>
&gt; +                                     &quot;unknown MII register 0x%x\n&quot;, reg);<br>
&gt; +            s-&gt;mii_data = 0;<br>
&gt; +            break;<br>
&gt; +        }<br>
&gt; +<br>
&gt; +        trace_aw_h3_emac_mii_read_reg(reg, s-&gt;mii_data);<br>
&gt; +    }<br>
&gt; +}<br>
&gt; +<br>
&gt; +static void aw_h3_emac_update_irq(AwH3EmacState *s)<br>
&gt; +{<br>
&gt; +    qemu_set_irq(s-&gt;irq, (s-&gt;int_sta &amp; s-&gt;int_en) != 0);<br>
&gt; +}<br>
&gt; +<br>
&gt; +static uint32_t aw_h3_emac_next_desc(FrameDescriptor *desc, size_t min_size)<br>
&gt; +{<br>
&gt; +    uint32_t paddr = desc-&gt;next;<br>
&gt; +<br>
&gt; +    cpu_physical_memory_read(paddr, desc, sizeof(*desc));<br>
&gt; +<br>
&gt; +    if ((desc-&gt;status &amp; DESC_STATUS_CTL) &amp;&amp;<br>
&gt; +        (desc-&gt;status2 &amp; DESC_STATUS2_BUF_SIZE_MASK) &gt;= min_size) {<br>
&gt; +        return paddr;<br>
&gt; +    } else {<br>
&gt; +        return 0;<br>
&gt; +    }<br>
&gt; +}<br>
&gt; +<br>
&gt; +static uint32_t aw_h3_emac_get_desc(FrameDescriptor *desc, uint32_t start_addr,<br>
&gt; +                                    size_t min_size)<br>
&gt; +{<br>
&gt; +    uint32_t desc_addr = start_addr;<br>
&gt; +<br>
&gt; +    /* Note that the list is a cycle. Last entry points back to the head. */<br>
&gt; +    while (desc_addr != 0) {<br>
&gt; +        cpu_physical_memory_read(desc_addr, desc, sizeof(*desc));<br>
&gt; +<br>
&gt; +        if ((desc-&gt;status &amp; DESC_STATUS_CTL) &amp;&amp;<br>
&gt; +            (desc-&gt;status2 &amp; DESC_STATUS2_BUF_SIZE_MASK) &gt;= min_size) {<br>
&gt; +            return desc_addr;<br>
&gt; +        } else if (desc-&gt;next == start_addr) {<br>
&gt; +            break;<br>
&gt; +        } else {<br>
&gt; +            desc_addr = desc-&gt;next;<br>
&gt; +        }<br>
&gt; +    }<br>
&gt; +<br>
&gt; +    return 0;<br>
&gt; +}<br>
&gt; +<br>
&gt; +static uint32_t aw_h3_emac_get_rx_desc(AwH3EmacState *s, FrameDescriptor *desc,<br>
&gt; +                                       size_t min_size)<br>
&gt; +{<br>
&gt; +    return aw_h3_emac_get_desc(desc, s-&gt;rx_desc_curr, min_size);<br>
&gt; +}<br>
&gt; +<br>
&gt; +static uint32_t aw_h3_emac_get_tx_desc(AwH3EmacState *s, FrameDescriptor *desc,<br>
&gt; +                                       size_t min_size)<br>
&gt; +{<br>
&gt; +    return aw_h3_emac_get_desc(desc, s-&gt;tx_desc_head, min_size);<br>
&gt; +}<br>
&gt; +<br>
&gt; +static void aw_h3_emac_flush_desc(FrameDescriptor *desc, uint32_t phys_addr)<br>
&gt; +{<br>
&gt; +    cpu_physical_memory_write(phys_addr, desc, sizeof(*desc));<br>
&gt; +}<br>
&gt; +<br>
&gt; +static int aw_h3_emac_can_receive(NetClientState *nc)<br>
&gt; +{<br>
&gt; +    AwH3EmacState *s = qemu_get_nic_opaque(nc);<br>
&gt; +    FrameDescriptor desc;<br>
&gt; +<br>
&gt; +    return (s-&gt;rx_ctl0 &amp; RX_CTL0_RX_EN) &amp;&amp;<br>
&gt; +           (aw_h3_emac_get_rx_desc(s, &amp;desc, 0) != 0);<br>
&gt; +}<br>
&gt; +<br>
&gt; +static ssize_t aw_h3_emac_receive(NetClientState *nc, const uint8_t *buf,<br>
&gt; +                                  size_t size)<br>
&gt; +{<br>
&gt; +    AwH3EmacState *s = qemu_get_nic_opaque(nc);<br>
&gt; +    FrameDescriptor desc;<br>
&gt; +    size_t bytes_left = size;<br>
&gt; +    size_t desc_bytes = 0;<br>
&gt; +    size_t pad_fcs_size = 4;<br>
&gt; +    size_t padding = 0;<br>
&gt; +<br>
&gt; +    if (!(s-&gt;rx_ctl0 &amp; RX_CTL0_RX_EN)) {<br>
&gt; +        return -1;<br>
&gt; +    }<br>
&gt; +<br>
&gt; +    s-&gt;rx_desc_curr = aw_h3_emac_get_rx_desc(s, &amp;desc, AW_H3_EMAC_MIN_PKT_SZ);<br>
&gt; +    if (!s-&gt;rx_desc_curr) {<br>
&gt; +        s-&gt;int_sta |= INT_STA_RX_BUF_UA;<br>
&gt; +    }<br>
&gt; +<br>
&gt; +    /* Keep filling RX descriptors until the whole frame is written */<br>
&gt; +    while (s-&gt;rx_desc_curr &amp;&amp; bytes_left &gt; 0) {<br>
&gt; +        desc.status &amp;= ~DESC_STATUS_CTL;<br>
&gt; +        desc.status &amp;= ~RX_DESC_STATUS_FRM_LEN_MASK;<br>
&gt; +<br>
&gt; +        if (bytes_left == size) {<br>
&gt; +            desc.status |= RX_DESC_STATUS_FIRST_DESC;<br>
&gt; +        }<br>
&gt; +<br>
&gt; +        if ((desc.status2 &amp; DESC_STATUS2_BUF_SIZE_MASK) &lt;<br>
&gt; +            (bytes_left + pad_fcs_size)) {<br>
&gt; +            desc_bytes = desc.status2 &amp; DESC_STATUS2_BUF_SIZE_MASK;<br>
&gt; +            desc.status |= desc_bytes &lt;&lt; RX_DESC_STATUS_FRM_LEN_SHIFT;<br>
&gt; +        } else {<br>
&gt; +            padding = pad_fcs_size;<br>
&gt; +            if (bytes_left &lt; AW_H3_EMAC_MIN_PKT_SZ) {<br>
&gt; +                padding += (AW_H3_EMAC_MIN_PKT_SZ - bytes_left);<br>
&gt; +            }<br>
&gt; +<br>
&gt; +            desc_bytes = (bytes_left);<br>
&gt; +            desc.status |= RX_DESC_STATUS_LAST_DESC;<br>
&gt; +            desc.status |= (bytes_left + padding)<br>
&gt; +                            &lt;&lt; RX_DESC_STATUS_FRM_LEN_SHIFT;<br>
&gt; +        }<br>
&gt; +<br>
&gt; +        cpu_physical_memory_write(desc.addr, buf, desc_bytes);<br>
&gt; +        aw_h3_emac_flush_desc(&amp;desc, s-&gt;rx_desc_curr);<br>
&gt; +        trace_aw_h3_emac_receive(s-&gt;rx_desc_curr, desc.addr, desc_bytes);<br>
&gt; +<br>
&gt; +        /* Check if frame needs to raise the receive interrupt */<br>
&gt; +        if (!(desc.status2 &amp; RX_DESC_STATUS2_RX_INT_CTL)) {<br>
&gt; +            s-&gt;int_sta |= INT_STA_RX;<br>
&gt; +        }<br>
&gt; +<br>
&gt; +        /* Increment variables */<br>
&gt; +        buf += desc_bytes;<br>
&gt; +        bytes_left -= desc_bytes;<br>
&gt; +<br>
&gt; +        /* Move to the next descriptor */<br>
&gt; +        s-&gt;rx_desc_curr = aw_h3_emac_next_desc(&amp;desc, 64);<br>
&gt; +        if (!s-&gt;rx_desc_curr) {<br>
&gt; +            /* Not enough buffer space available */<br>
&gt; +            s-&gt;int_sta |= INT_STA_RX_BUF_UA;<br>
&gt; +            s-&gt;rx_desc_curr = s-&gt;rx_desc_head;<br>
&gt; +            break;<br>
&gt; +        }<br>
&gt; +    }<br>
&gt; +<br>
&gt; +    /* Report receive DMA is finished */<br>
&gt; +    s-&gt;rx_ctl1 &amp;= ~RX_CTL1_RX_DMA_START;<br>
&gt; +    aw_h3_emac_update_irq(s);<br>
&gt; +<br>
&gt; +    return size;<br>
&gt; +}<br>
&gt; +<br>
&gt; +static void aw_h3_emac_transmit(AwH3EmacState *s)<br>
&gt; +{<br>
&gt; +    NetClientState *nc = qemu_get_queue(s-&gt;nic);<br>
&gt; +    FrameDescriptor desc;<br>
&gt; +    size_t bytes = 0;<br>
&gt; +    size_t packet_bytes = 0;<br>
&gt; +    size_t transmitted = 0;<br>
&gt; +    static uint8_t packet_buf[2048];<br>
&gt; +<br>
&gt; +    s-&gt;tx_desc_curr = aw_h3_emac_get_tx_desc(s, &amp;desc, 0);<br>
&gt; +<br>
&gt; +    /* Read all transmit descriptors */<br>
&gt; +    while (s-&gt;tx_desc_curr != 0) {<br>
&gt; +<br>
&gt; +        /* Read from physical memory into packet buffer */<br>
&gt; +        bytes = desc.status2 &amp; DESC_STATUS2_BUF_SIZE_MASK;<br>
&gt; +        if (bytes + packet_bytes &gt; sizeof(packet_buf)) {<br>
&gt; +            desc.status |= TX_DESC_STATUS_LENGTH_ERR;<br>
&gt; +            break;<br>
&gt; +        }<br>
&gt; +        cpu_physical_memory_read(desc.addr, packet_buf + packet_bytes, bytes);<br>
&gt; +        packet_bytes += bytes;<br>
&gt; +        desc.status &amp;= ~DESC_STATUS_CTL;<br>
&gt; +        aw_h3_emac_flush_desc(&amp;desc, s-&gt;tx_desc_curr);<br>
&gt; +<br>
&gt; +        /* After the last descriptor, send the packet */<br>
&gt; +        if (desc.status2 &amp; TX_DESC_STATUS2_LAST_DESC) {<br>
&gt; +            if (desc.status2 &amp; TX_DESC_STATUS2_CHECKSUM_MASK) {<br>
&gt; +                net_checksum_calculate(packet_buf, packet_bytes);<br>
&gt; +            }<br>
&gt; +<br>
&gt; +            qemu_send_packet(nc, packet_buf, packet_bytes);<br>
&gt; +            trace_aw_h3_emac_transmit(s-&gt;tx_desc_curr, desc.addr, bytes);<br>
&gt; +<br>
&gt; +            packet_bytes = 0;<br>
&gt; +            transmitted++;<br>
&gt; +        }<br>
&gt; +        s-&gt;tx_desc_curr = aw_h3_emac_next_desc(&amp;desc, 0);<br>
&gt; +    }<br>
&gt; +<br>
&gt; +    /* Raise transmit completed interrupt */<br>
&gt; +    if (transmitted &gt; 0) {<br>
&gt; +        s-&gt;int_sta |= INT_STA_TX;<br>
&gt; +        s-&gt;tx_ctl1 &amp;= ~TX_CTL1_TX_DMA_START;<br>
&gt; +        aw_h3_emac_update_irq(s);<br>
&gt; +    }<br>
&gt; +}<br>
&gt; +<br>
&gt; +static void aw_h3_emac_reset(DeviceState *dev)<br>
&gt; +{<br>
&gt; +    AwH3EmacState *s = AW_H3_EMAC(dev);<br>
&gt; +    NetClientState *nc = qemu_get_queue(s-&gt;nic);<br>
&gt; +<br>
&gt; +    trace_aw_h3_emac_reset();<br>
&gt; +<br>
&gt; +    s-&gt;mii_cmd = 0;<br>
&gt; +    s-&gt;mii_data = 0;<br>
&gt; +    s-&gt;basic_ctl0 = 0;<br>
&gt; +    s-&gt;basic_ctl1 = 0;<br>
&gt; +    s-&gt;int_en = 0;<br>
&gt; +    s-&gt;int_sta = 0;<br>
&gt; +    s-&gt;frm_flt = 0;<br>
&gt; +    s-&gt;rx_ctl0 = 0;<br>
&gt; +    s-&gt;rx_ctl1 = RX_CTL1_RX_MD;<br>
&gt; +    s-&gt;rx_desc_head = 0;<br>
&gt; +    s-&gt;rx_desc_curr = 0;<br>
&gt; +    s-&gt;tx_ctl0 = 0;<br>
&gt; +    s-&gt;tx_ctl1 = 0;<br>
&gt; +    s-&gt;tx_desc_head = 0;<br>
&gt; +    s-&gt;tx_desc_curr = 0;<br>
&gt; +    s-&gt;tx_flowctl = 0;<br>
&gt; +<br>
&gt; +    aw_h3_emac_mii_reset(s, !nc-&gt;link_down);<br>
&gt; +}<br>
&gt; +<br>
&gt; +static uint64_t aw_h3_emac_read(void *opaque, hwaddr offset, unsigned size)<br>
&gt; +{<br>
&gt; +    AwH3EmacState *s = opaque;<br>
<br>
I&#39;d put AW_H3_EMAC(opaque) here.<br>
<br>
&gt; +    uint64_t value = 0;<br>
&gt; +    FrameDescriptor desc;<br>
&gt; +<br>
&gt; +    switch (offset) {<br>
&gt; +    case REG_BASIC_CTL_0:       /* Basic Control 0 */<br>
&gt; +        value = s-&gt;basic_ctl0;<br>
&gt; +        break;<br>
&gt; +    case REG_BASIC_CTL_1:       /* Basic Control 1 */<br>
&gt; +        value = s-&gt;basic_ctl1;<br>
&gt; +        break;<br>
&gt; +    case REG_INT_STA:           /* Interrupt Status */<br>
&gt; +        value = s-&gt;int_sta;<br>
&gt; +        break;<br>
&gt; +    case REG_INT_EN:            /* Interupt Enable */<br>
&gt; +        value = s-&gt;int_en;<br>
&gt; +        break;<br>
&gt; +    case REG_TX_CTL_0:          /* Transmit Control 0 */<br>
&gt; +        value = s-&gt;tx_ctl0;<br>
&gt; +        break;<br>
&gt; +    case REG_TX_CTL_1:          /* Transmit Control 1 */<br>
&gt; +        value = s-&gt;tx_ctl1;<br>
&gt; +        break;<br>
&gt; +    case REG_TX_FLOW_CTL:       /* Transmit Flow Control */<br>
&gt; +        value = s-&gt;tx_flowctl;<br>
&gt; +        break;<br>
&gt; +    case REG_TX_DMA_DESC_LIST:  /* Transmit Descriptor List Address */<br>
&gt; +        value = s-&gt;tx_desc_head;<br>
&gt; +        break;<br>
&gt; +    case REG_RX_CTL_0:          /* Receive Control 0 */<br>
&gt; +        value = s-&gt;rx_ctl0;<br>
&gt; +        break;<br>
&gt; +    case REG_RX_CTL_1:          /* Receive Control 1 */<br>
&gt; +        value = s-&gt;rx_ctl1;<br>
&gt; +        break;<br>
&gt; +    case REG_RX_DMA_DESC_LIST:  /* Receive Descriptor List Address */<br>
&gt; +        value = s-&gt;rx_desc_head;<br>
&gt; +        break;<br>
&gt; +    case REG_FRM_FLT:           /* Receive Frame Filter */<br>
&gt; +        value = s-&gt;frm_flt;<br>
&gt; +        break;<br>
&gt; +    case REG_RX_HASH_0:         /* Receive Hash Table 0 */<br>
&gt; +    case REG_RX_HASH_1:         /* Receive Hash Table 1 */<br>
&gt; +        break;<br>
&gt; +    case REG_MII_CMD:           /* Management Interface Command */<br>
&gt; +        value = s-&gt;mii_cmd;<br>
&gt; +        break;<br>
&gt; +    case REG_MII_DATA:          /* Management Interface Data */<br>
&gt; +        value = s-&gt;mii_data;<br>
&gt; +        break;<br>
&gt; +    case REG_ADDR_HIGH:         /* MAC Address High */<br>
&gt; +        value = *(((uint32_t *) (s-&gt;conf.macaddr.a)) + 1);<br>
&gt; +        break;<br>
&gt; +    case REG_ADDR_LOW:          /* MAC Address Low */<br>
&gt; +        value = *(uint32_t *) (s-&gt;conf.macaddr.a);<br>
&gt; +        break;<br>
&gt; +    case REG_TX_DMA_STA:        /* Transmit DMA Status */<br>
&gt; +        break;<br>
&gt; +    case REG_TX_CUR_DESC:       /* Transmit Current Descriptor */<br>
&gt; +        value = s-&gt;tx_desc_curr;<br>
&gt; +        break;<br>
&gt; +    case REG_TX_CUR_BUF:        /* Transmit Current Buffer */<br>
&gt; +        if (s-&gt;tx_desc_curr != 0) {<br>
&gt; +            cpu_physical_memory_read(s-&gt;tx_desc_curr, &amp;desc, sizeof(desc));<br>
&gt; +            value = desc.addr;<br>
&gt; +        } else {<br>
&gt; +            value = 0;<br>
&gt; +        }<br>
&gt; +        break;<br>
&gt; +    case REG_RX_DMA_STA:        /* Receive DMA Status */<br>
&gt; +        break;<br>
&gt; +    case REG_RX_CUR_DESC:       /* Receive Current Descriptor */<br>
&gt; +        value = s-&gt;rx_desc_curr;<br>
&gt; +        break;<br>
&gt; +    case REG_RX_CUR_BUF:        /* Receive Current Buffer */<br>
&gt; +        if (s-&gt;rx_desc_curr != 0) {<br>
&gt; +            cpu_physical_memory_read(s-&gt;rx_desc_curr, &amp;desc, sizeof(desc));<br>
&gt; +            value = desc.addr;<br>
&gt; +        } else {<br>
&gt; +            value = 0;<br>
&gt; +        }<br>
&gt; +        break;<br>
&gt; +    default:<br>
&gt; +        qemu_log_mask(LOG_UNIMP, &quot;allwinner-h3-emac: read access to unknown &quot;<br>
&gt; +                                 &quot;EMAC register 0x&quot; TARGET_FMT_plx &quot;\n&quot;,<br>
&gt; +                                  offset);<br>
&gt; +    }<br>
&gt; +<br>
&gt; +    trace_aw_h3_emac_read(offset, value);<br>
&gt; +    return value;<br>
&gt; +}<br>
&gt; +<br>
&gt; +static void aw_h3_emac_write(void *opaque, hwaddr offset, uint64_t value,<br>
&gt; +                             unsigned size)<br>
&gt; +{<br>
&gt; +    AwH3EmacState *s = opaque;<br>
<br>
The same.<br>
<br>
&gt; +    NetClientState *nc = qemu_get_queue(s-&gt;nic);<br>
&gt; +<br>
&gt; +    trace_aw_h3_emac_write(offset, value);<br>
&gt; +<br>
&gt; +    switch (offset) {<br>
&gt; +    case REG_BASIC_CTL_0:       /* Basic Control 0 */<br>
&gt; +        s-&gt;basic_ctl0 = value;<br>
&gt; +        break;<br>
&gt; +    case REG_BASIC_CTL_1:       /* Basic Control 1 */<br>
&gt; +        if (value &amp; BASIC_CTL1_SOFTRST) {<br>
&gt; +            aw_h3_emac_reset(DEVICE(s));<br>
&gt; +            value &amp;= ~BASIC_CTL1_SOFTRST;<br>
&gt; +        }<br>
&gt; +        s-&gt;basic_ctl1 = value;<br>
&gt; +        if (aw_h3_emac_can_receive(nc)) {<br>
&gt; +            qemu_flush_queued_packets(nc);<br>
&gt; +        }<br>
&gt; +        break;<br>
&gt; +    case REG_INT_STA:           /* Interrupt Status */<br>
&gt; +        s-&gt;int_sta &amp;= ~value;<br>
&gt; +        aw_h3_emac_update_irq(s);<br>
&gt; +        break;<br>
&gt; +    case REG_INT_EN:            /* Interrupt Enable */<br>
&gt; +        s-&gt;int_en = value;<br>
&gt; +        aw_h3_emac_update_irq(s);<br>
&gt; +        break;<br>
&gt; +    case REG_TX_CTL_0:          /* Transmit Control 0 */<br>
&gt; +        s-&gt;tx_ctl0 = value;<br>
&gt; +        break;<br>
&gt; +    case REG_TX_CTL_1:          /* Transmit Control 1 */<br>
&gt; +        s-&gt;tx_ctl1 = value;<br>
&gt; +        if (value &amp; TX_CTL1_TX_DMA_EN) {<br>
&gt; +            aw_h3_emac_transmit(s);<br>
&gt; +        }<br>
&gt; +        break;<br>
&gt; +    case REG_TX_FLOW_CTL:       /* Transmit Flow Control */<br>
&gt; +        s-&gt;tx_flowctl = value;<br>
&gt; +        break;<br>
&gt; +    case REG_TX_DMA_DESC_LIST:  /* Transmit Descriptor List Address */<br>
&gt; +        s-&gt;tx_desc_head = value;<br>
&gt; +        s-&gt;tx_desc_curr = value;<br>
&gt; +        break;<br>
&gt; +    case REG_RX_CTL_0:          /* Receive Control 0 */<br>
&gt; +        s-&gt;rx_ctl0 = value;<br>
&gt; +        break;<br>
&gt; +    case REG_RX_CTL_1:          /* Receive Control 1 */<br>
&gt; +        s-&gt;rx_ctl1 = value | RX_CTL1_RX_MD;<br>
&gt; +        if ((value &amp; RX_CTL1_RX_DMA_EN) &amp;&amp; aw_h3_emac_can_receive(nc)) {<br>
&gt; +            qemu_flush_queued_packets(nc);<br>
&gt; +        }<br>
&gt; +        break;<br>
&gt; +    case REG_RX_DMA_DESC_LIST:  /* Receive Descriptor List Address */<br>
&gt; +        s-&gt;rx_desc_head = value;<br>
&gt; +        s-&gt;rx_desc_curr = value;<br>
&gt; +        break;<br>
&gt; +    case REG_FRM_FLT:           /* Receive Frame Filter */<br>
&gt; +        s-&gt;frm_flt = value;<br>
&gt; +        break;<br>
&gt; +    case REG_RX_HASH_0:         /* Receive Hash Table 0 */<br>
&gt; +    case REG_RX_HASH_1:         /* Receive Hash Table 1 */<br>
&gt; +        break;<br>
&gt; +    case REG_MII_CMD:           /* Management Interface Command */<br>
&gt; +        s-&gt;mii_cmd = value &amp; ~MII_CMD_PHY_BUSY;<br>
&gt; +        aw_h3_emac_mii_cmd(s);<br>
&gt; +        break;<br>
&gt; +    case REG_MII_DATA:          /* Management Interface Data */<br>
&gt; +        s-&gt;mii_data = value;<br>
&gt; +        break;<br>
&gt; +    case REG_ADDR_HIGH:         /* MAC Address High */<br>
&gt; +        s-&gt;conf.macaddr.a[4] = (value &amp; 0xff);<br>
&gt; +        s-&gt;conf.macaddr.a[5] = (value &amp; 0xff00) &gt;&gt; 8;<br>
&gt; +        break;<br>
&gt; +    case REG_ADDR_LOW:          /* MAC Address Low */<br>
&gt; +        s-&gt;conf.macaddr.a[0] = (value &amp; 0xff);<br>
&gt; +        s-&gt;conf.macaddr.a[1] = (value &amp; 0xff00) &gt;&gt; 8;<br>
&gt; +        s-&gt;conf.macaddr.a[2] = (value &amp; 0xff0000) &gt;&gt; 16;<br>
&gt; +        s-&gt;conf.macaddr.a[3] = (value &amp; 0xff000000) &gt;&gt; 24;<br>
&gt; +        break;<br>
&gt; +    case REG_TX_DMA_STA:        /* Transmit DMA Status */<br>
&gt; +    case REG_TX_CUR_DESC:       /* Transmit Current Descriptor */<br>
&gt; +    case REG_TX_CUR_BUF:        /* Transmit Current Buffer */<br>
&gt; +    case REG_RX_DMA_STA:        /* Receive DMA Status */<br>
&gt; +    case REG_RX_CUR_DESC:       /* Receive Current Descriptor */<br>
&gt; +    case REG_RX_CUR_BUF:        /* Receive Current Buffer */<br>
&gt; +    case REG_RGMII_STA:         /* RGMII Status */<br>
&gt; +        break;<br>
&gt; +    default:<br>
&gt; +        qemu_log_mask(LOG_UNIMP, &quot;allwinner-h3-emac: write access to unknown &quot;<br>
&gt; +                                 &quot;EMAC register 0x&quot; TARGET_FMT_plx &quot;\n&quot;,<br>
&gt; +                                  offset);<br>
&gt; +    }<br>
&gt; +}<br>
&gt; +<br>
&gt; +static void aw_h3_emac_set_link(NetClientState *nc)<br>
&gt; +{<br>
&gt; +    AwH3EmacState *s = qemu_get_nic_opaque(nc);<br>
&gt; +<br>
&gt; +    trace_aw_h3_emac_set_link(!nc-&gt;link_down);<br>
&gt; +    aw_h3_emac_mii_set_link(s, !nc-&gt;link_down);<br>
&gt; +}<br>
&gt; +<br>
&gt; +static const MemoryRegionOps aw_h3_emac_mem_ops = {<br>
&gt; +    .read = aw_h3_emac_read,<br>
&gt; +    .write = aw_h3_emac_write,<br>
&gt; +    .endianness = DEVICE_NATIVE_ENDIAN,<br>
&gt; +    .valid = {<br>
&gt; +        .min_access_size = 4,<br>
&gt; +        .max_access_size = 4,<br>
&gt; +    },<br>
&gt; +};<br>
&gt; +<br>
&gt; +static NetClientInfo net_aw_h3_emac_info = {<br>
&gt; +    .type = NET_CLIENT_DRIVER_NIC,<br>
&gt; +    .size = sizeof(NICState),<br>
&gt; +    .can_receive = aw_h3_emac_can_receive,<br>
&gt; +    .receive = aw_h3_emac_receive,<br>
&gt; +    .link_status_changed = aw_h3_emac_set_link,<br>
&gt; +};<br>
&gt; +<br>
&gt; +static void aw_h3_emac_init(Object *obj)<br>
&gt; +{<br>
&gt; +    SysBusDevice *sbd = SYS_BUS_DEVICE(obj);<br>
&gt; +    AwH3EmacState *s = AW_H3_EMAC(obj);<br>
&gt; +<br>
&gt; +    memory_region_init_io(&amp;s-&gt;iomem, OBJECT(s), &amp;aw_h3_emac_mem_ops, s,<br>
&gt; +                          TYPE_AW_H3_EMAC, AW_H3_EMAC_REGS_MEM_SIZE);<br>
&gt; +    sysbus_init_mmio(sbd, &amp;s-&gt;iomem);<br>
&gt; +    sysbus_init_irq(sbd, &amp;s-&gt;irq);<br>
&gt; +}<br>
&gt; +<br>
&gt; +static void aw_h3_emac_realize(DeviceState *dev, Error **errp)<br>
&gt; +{<br>
&gt; +    AwH3EmacState *s = AW_H3_EMAC(dev);<br>
&gt; +<br>
&gt; +    qemu_macaddr_default_if_unset(&amp;s-&gt;conf.macaddr);<br>
&gt; +    s-&gt;nic = qemu_new_nic(&amp;net_aw_h3_emac_info, &amp;s-&gt;conf,<br>
&gt; +                          object_get_typename(OBJECT(dev)), dev-&gt;id, s);<br>
&gt; +    qemu_format_nic_info_str(qemu_get_queue(s-&gt;nic), s-&gt;conf.macaddr.a);<br>
&gt; +}<br>
&gt; +<br>
&gt; +static Property aw_h3_emac_properties[] = {<br>
&gt; +    DEFINE_NIC_PROPERTIES(AwH3EmacState, conf),<br>
&gt; +    DEFINE_PROP_UINT8(&quot;phy-addr&quot;, AwH3EmacState, mii_phy_addr, 0),<br>
&gt; +    DEFINE_PROP_END_OF_LIST(),<br>
&gt; +};<br>
&gt; +<br>
&gt; +static int aw_h3_emac_post_load(void *opaque, int version_id)<br>
&gt; +{<br>
&gt; +    AwH3EmacState *s = opaque;<br>
&gt; +<br>
&gt; +    aw_h3_emac_set_link(qemu_get_queue(s-&gt;nic));<br>
&gt; +<br>
&gt; +    return 0;<br>
&gt; +}<br>
&gt; +<br>
&gt; +static const VMStateDescription vmstate_aw_emac = {<br>
&gt; +    .name = TYPE_AW_H3_EMAC,<br>
&gt; +    .version_id = 1,<br>
&gt; +    .minimum_version_id = 1,<br>
&gt; +    .post_load = aw_h3_emac_post_load,<br>
&gt; +    .fields = (VMStateField[]) {<br>
&gt; +        VMSTATE_UINT8(mii_phy_addr, AwH3EmacState),<br>
&gt; +        VMSTATE_UINT32(mii_cmd, AwH3EmacState),<br>
&gt; +        VMSTATE_UINT32(mii_data, AwH3EmacState),<br>
&gt; +        VMSTATE_UINT32(basic_ctl0, AwH3EmacState),<br>
&gt; +        VMSTATE_UINT32(basic_ctl1, AwH3EmacState),<br>
&gt; +        VMSTATE_UINT32(int_en, AwH3EmacState),<br>
&gt; +        VMSTATE_UINT32(int_sta, AwH3EmacState),<br>
&gt; +        VMSTATE_UINT32(frm_flt, AwH3EmacState),<br>
&gt; +        VMSTATE_UINT32(rx_ctl0, AwH3EmacState),<br>
&gt; +        VMSTATE_UINT32(rx_ctl1, AwH3EmacState),<br>
&gt; +        VMSTATE_UINT32(rx_desc_head, AwH3EmacState),<br>
&gt; +        VMSTATE_UINT32(rx_desc_curr, AwH3EmacState),<br>
&gt; +        VMSTATE_UINT32(tx_ctl0, AwH3EmacState),<br>
&gt; +        VMSTATE_UINT32(tx_ctl1, AwH3EmacState),<br>
&gt; +        VMSTATE_UINT32(tx_desc_head, AwH3EmacState),<br>
&gt; +        VMSTATE_UINT32(tx_desc_curr, AwH3EmacState),<br>
&gt; +        VMSTATE_UINT32(tx_flowctl, AwH3EmacState),<br>
&gt; +        VMSTATE_END_OF_LIST()<br>
&gt; +    }<br>
&gt; +};<br>
&gt; +<br>
&gt; +static void aw_h3_emac_class_init(ObjectClass *klass, void *data)<br>
&gt; +{<br>
&gt; +    DeviceClass *dc = DEVICE_CLASS(klass);<br>
&gt; +<br>
&gt; +    dc-&gt;realize = aw_h3_emac_realize;<br>
&gt; +    dc-&gt;props = aw_h3_emac_properties;<br>
&gt; +    dc-&gt;reset = aw_h3_emac_reset;<br>
&gt; +    dc-&gt;vmsd = &amp;vmstate_aw_emac;<br>
&gt; +}<br>
&gt; +<br>
&gt; +static const TypeInfo aw_h3_emac_info = {<br>
&gt; +    .name           = TYPE_AW_H3_EMAC,<br>
&gt; +    .parent         = TYPE_SYS_BUS_DEVICE,<br>
&gt; +    .instance_size  = sizeof(AwH3EmacState),<br>
&gt; +    .instance_init  = aw_h3_emac_init,<br>
&gt; +    .class_init     = aw_h3_emac_class_init,<br>
&gt; +};<br>
&gt; +<br>
&gt; +static void aw_h3_emac_register_types(void)<br>
&gt; +{<br>
&gt; +    type_register_static(&amp;aw_h3_emac_info);<br>
&gt; +}<br>
&gt; +<br>
&gt; +type_init(aw_h3_emac_register_types)<br>
&gt; diff --git a/hw/net/trace-events b/hw/net/trace-events<br>
&gt; index e70f12bee1..e9e2f26f68 100644<br>
&gt; --- a/hw/net/trace-events<br>
&gt; +++ b/hw/net/trace-events<br>
&gt; @@ -1,5 +1,15 @@<br>
&gt;   # See docs/devel/tracing.txt for syntax documentation.<br>
&gt;   <br>
&gt; +# allwinner-h3-emac.c<br>
&gt; +aw_h3_emac_mii_write_reg(uint32_t reg, uint32_t value) &quot;MII write: reg=0x%x value=0x%x&quot;<br>
&gt; +aw_h3_emac_mii_read_reg(uint32_t reg, uint32_t value) &quot;MII read: reg=0x%x value=0x%x&quot;<br>
&gt; +aw_h3_emac_receive(uint32_t desc, uint32_t paddr, uint32_t bytes) &quot;RX packet: desc=0x%08x paddr=0x%08x bytes=%u&quot;<br>
&gt; +aw_h3_emac_transmit(uint32_t desc, uint32_t paddr, uint32_t bytes) &quot;TX packet: desc=0x%08x paddr=0x%08x bytes=%u&quot;<br>
&gt; +aw_h3_emac_reset(void) &quot;HW reset&quot;<br>
&gt; +aw_h3_emac_set_link(bool active) &quot;Set link: active=%u&quot;<br>
&gt; +aw_h3_emac_read(uint64_t offset, uint64_t val) &quot;MMIO read: offset=0x%&quot; PRIx64 &quot; value=0x%&quot; PRIx64<br>
&gt; +aw_h3_emac_write(uint64_t offset, uint64_t val) &quot;MMIO write: offset=0x%&quot; PRIx64 &quot; value=0x%&quot; PRIx64<br>
&gt; +<br>
&gt;   # etraxfs_eth.c<br>
&gt;   mdio_phy_read(int regnum, uint16_t value) &quot;read phy_reg:%d value:0x%04x&quot;<br>
&gt;   mdio_phy_write(int regnum, uint16_t value) &quot;write phy_reg:%d value:0x%04x&quot;<br>
&gt; diff --git a/include/hw/arm/allwinner-h3.h b/include/hw/arm/allwinner-h3.h<br>
&gt; index 7aff4ebbd2..b964a60f41 100644<br>
&gt; --- a/include/hw/arm/allwinner-h3.h<br>
&gt; +++ b/include/hw/arm/allwinner-h3.h<br>
&gt; @@ -31,6 +31,7 @@<br>
&gt;   #include &quot;hw/misc/allwinner-h3-syscon.h&quot;<br>
&gt;   #include &quot;hw/misc/allwinner-h3-sid.h&quot;<br>
&gt;   #include &quot;hw/sd/allwinner-h3-sdhost.h&quot;<br>
&gt; +#include &quot;hw/net/allwinner-h3-emac.h&quot;<br>
&gt;   #include &quot;target/arm/cpu.h&quot;<br>
&gt;   <br>
&gt;   #define AW_H3_SRAM_A1_BASE     (0x00000000)<br>
&gt; @@ -119,6 +120,7 @@ typedef struct AwH3State {<br>
&gt;       AwH3SysconState syscon;<br>
&gt;       AwH3SidState sid;<br>
&gt;       AwH3SDHostState mmc0;<br>
&gt; +    AwH3EmacState emac;<br>
&gt;       GICState gic;<br>
&gt;       MemoryRegion sram_a1;<br>
&gt;       MemoryRegion sram_a2;<br>
&gt; diff --git a/include/hw/net/allwinner-h3-emac.h b/include/hw/net/allwinner-h3-emac.h<br>
&gt; new file mode 100644<br>
&gt; index 0000000000..a007d54472<br>
&gt; --- /dev/null<br>
&gt; +++ b/include/hw/net/allwinner-h3-emac.h<br>
&gt; @@ -0,0 +1,69 @@<br>
&gt; +/*<br>
&gt; + * Allwinner H3 EMAC emulation<br>
&gt; + *<br>
&gt; + * Copyright (C) 2019 Niek Linnenbank &lt;<a href="mailto:nieklinnenbank@gmail.com" target="_blank">nieklinnenbank@gmail.com</a>&gt;<br>
&gt; + *<br>
&gt; + * This program is free software: you can redistribute it and/or modify<br>
&gt; + * it under the terms of the GNU General Public License as published by<br>
&gt; + * the Free Software Foundation, either version 2 of the License, or<br>
&gt; + * (at your option) any later version.<br>
&gt; + *<br>
&gt; + * This program is distributed in the hope that it will be useful,<br>
&gt; + * but WITHOUT ANY WARRANTY; without even the implied warranty of<br>
&gt; + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the<br>
&gt; + * GNU General Public License for more details.<br>
&gt; + *<br>
&gt; + * You should have received a copy of the GNU General Public License<br>
&gt; + * along with this program.  If not, see &lt;<a href="http://www.gnu.org/licenses/" rel="noreferrer" target="_blank">http://www.gnu.org/licenses/</a>&gt;.<br>
&gt; + */<br>
&gt; +<br>
&gt; +#ifndef ALLWINNER_H3_EMAC_H<br>
&gt; +#define ALLWINNER_H3_EMAC_H<br>
&gt; +<br>
&gt; +#include &quot;qemu/units.h&quot;<br>
&gt; +#include &quot;net/net.h&quot;<br>
&gt; +#include &quot;qemu/fifo8.h&quot;<br>
&gt; +#include &quot;hw/net/mii.h&quot;<br>
&gt; +#include &quot;hw/sysbus.h&quot;<br>
&gt; +<br>
&gt; +#define AW_H3_EMAC_REGS_MEM_SIZE  (1024)<br>
&gt; +<br>
&gt; +#define TYPE_AW_H3_EMAC &quot;allwinner-h3-emac&quot;<br>
&gt; +#define AW_H3_EMAC(obj) OBJECT_CHECK(AwH3EmacState, (obj), TYPE_AW_H3_EMAC)<br>
&gt; +<br>
&gt; +typedef struct AwH3EmacState {<br>
&gt; +    /*&lt; private &gt;*/<br>
&gt; +    SysBusDevice  parent_obj;<br>
&gt; +    /*&lt; public &gt;*/<br>
&gt; +<br>
&gt; +    MemoryRegion iomem;<br>
&gt; +    qemu_irq     irq;<br>
&gt; +    NICState     *nic;<br>
&gt; +    NICConf      conf;<br>
&gt; +<br>
&gt; +    uint8_t      mii_phy_addr;<br>
&gt; +    uint32_t     mii_cmd;<br>
&gt; +    uint32_t     mii_data;<br>
&gt; +    uint32_t     mii_cr;<br>
&gt; +    uint32_t     mii_st;<br>
&gt; +<br>
&gt; +    uint32_t     basic_ctl0;<br>
&gt; +    uint32_t     basic_ctl1;<br>
&gt; +    uint32_t     int_en;<br>
&gt; +    uint32_t     int_sta;<br>
&gt; +    uint32_t     frm_flt;<br>
&gt; +<br>
&gt; +    uint32_t     rx_ctl0;<br>
&gt; +    uint32_t     rx_ctl1;<br>
&gt; +    uint32_t     rx_desc_head;<br>
&gt; +    uint32_t     rx_desc_curr;<br>
&gt; +<br>
&gt; +    uint32_t     tx_ctl0;<br>
&gt; +    uint32_t     tx_ctl1;<br>
&gt; +    uint32_t     tx_desc_head;<br>
&gt; +    uint32_t     tx_desc_curr;<br>
&gt; +    uint32_t     tx_flowctl;<br>
&gt; +<br>
&gt; +} AwH3EmacState;<br>
&gt; +<br>
&gt; +#endif<br>
&gt; <br>
<br>
The rest seems ok to me. Thanks for the contribution :)!<br></blockquote><div><br></div><div>Thanks! :-) <br></div><div> </div><div>Regards,</div><div>Niek<br></div><div><br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
<br>
Cheers,<br>
Fred<br>
<br>
</blockquote></div><br clear="all"><br>-- <br><div dir="ltr" class="gmail_signature">Niek Linnenbank<br><br>WWW: <a href="http://www.nieklinnenbank.nl/" target="_blank">http://www.nieklinnenbank.nl/</a><br>BLOG: <a href="http://nieklinnenbank.wordpress.com/" target="_blank">http://nieklinnenbank.wordpress.com/</a><br>FUN:    <a href="http://www.FreeNOS.org/" target="_blank">http://www.FreeNOS.org/</a><br></div></div>

^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH 02/10] hw: arm: add Xunlong Orange Pi PC machine
  2019-12-03 19:33     ` Niek Linnenbank
@ 2019-12-04  9:03       ` Philippe Mathieu-Daudé
  2019-12-04 19:50         ` Niek Linnenbank
  0 siblings, 1 reply; 33+ messages in thread
From: Philippe Mathieu-Daudé @ 2019-12-04  9:03 UTC (permalink / raw)
  To: Niek Linnenbank; +Cc: b.galvani, Peter Maydell, qemu-arm, qemu-devel

On 12/3/19 8:33 PM, Niek Linnenbank wrote:
> Hello Philippe,
> 
> Thanks for your quick review comments!
> I'll start working on a v2 of the patches and include the changes you 
> suggested.

Thanks, but I'd suggest to wait few more days to give time to others 
reviewers. Else having multiple versions of a big series reviewed at the 
same time is very confusing.
I have other minor comments on others patches, but need to find the time 
to continue reviewing.



^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH 10/10] arm: allwinner-h3: add EMAC ethernet device
  2019-12-03  9:33   ` KONRAD Frederic
  2019-12-03 19:41     ` Niek Linnenbank
@ 2019-12-04 15:14     ` Philippe Mathieu-Daudé
  2019-12-04 15:22       ` KONRAD Frederic
  1 sibling, 1 reply; 33+ messages in thread
From: Philippe Mathieu-Daudé @ 2019-12-04 15:14 UTC (permalink / raw)
  To: KONRAD Frederic, Niek Linnenbank, qemu-devel
  Cc: peter.maydell, Jason Wang, Sai Pavan Boddu, Grant Likely,
	b.galvani, qemu-arm, Paolo Bonzini, Sven Schnelle

On 12/3/19 10:33 AM, KONRAD Frederic wrote:
> Le 12/2/19 à 10:09 PM, Niek Linnenbank a écrit :
>> The Allwinner H3 System on Chip includes an Ethernet MAC (EMAC)
>> which provides 10M/100M/1000M Ethernet connectivity. This commit
>> adds support for the Allwinner H3 EMAC, including emulation for
>> the following functionality:
>>
>>   * DMA transfers
>>   * MII interface
>>   * Transmit CRC calculation
>>
>> Signed-off-by: Niek Linnenbank <nieklinnenbank@gmail.com>
>> ---
>>   hw/arm/Kconfig                     |   1 +
>>   hw/arm/allwinner-h3.c              |  17 +
>>   hw/arm/orangepi.c                  |   7 +
>>   hw/net/Kconfig                     |   3 +
>>   hw/net/Makefile.objs               |   1 +
>>   hw/net/allwinner-h3-emac.c         | 786 +++++++++++++++++++++++++++++
>>   hw/net/trace-events                |  10 +
>>   include/hw/arm/allwinner-h3.h      |   2 +
>>   include/hw/net/allwinner-h3-emac.h |  69 +++
>>   9 files changed, 896 insertions(+)
>>   create mode 100644 hw/net/allwinner-h3-emac.c
>>   create mode 100644 include/hw/net/allwinner-h3-emac.h
>>
>> diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig
>> index ebf8d2325f..551cff3442 100644
>> --- a/hw/arm/Kconfig
>> +++ b/hw/arm/Kconfig
>> @@ -294,6 +294,7 @@ config ALLWINNER_A10
>>   config ALLWINNER_H3
>>       bool
>>       select ALLWINNER_A10_PIT
>> +    select ALLWINNER_H3_EMAC
>>       select SERIAL
>>       select ARM_TIMER
>>       select ARM_GIC
>> diff --git a/hw/arm/allwinner-h3.c b/hw/arm/allwinner-h3.c
>> index c2972caf88..274b8548c0 100644
>> --- a/hw/arm/allwinner-h3.c
>> +++ b/hw/arm/allwinner-h3.c
>> @@ -53,6 +53,9 @@ static void aw_h3_init(Object *obj)
>>       sysbus_init_child_obj(obj, "mmc0", &s->mmc0, sizeof(s->mmc0),
>>                             TYPE_AW_H3_SDHOST);
>> +
>> +    sysbus_init_child_obj(obj, "emac", &s->emac, sizeof(s->emac),
>> +                          TYPE_AW_H3_EMAC);
>>   }
>>   static void aw_h3_realize(DeviceState *dev, Error **errp)
>> @@ -237,6 +240,20 @@ static void aw_h3_realize(DeviceState *dev, Error 
>> **errp)
>>           return;
>>       }
>> +    /* EMAC */
>> +    if (nd_table[0].used) {
>> +        qemu_check_nic_model(&nd_table[0], TYPE_AW_H3_EMAC);
>> +        qdev_set_nic_properties(DEVICE(&s->emac), &nd_table[0]);
>> +    }
>> +    object_property_set_bool(OBJECT(&s->emac), true, "realized", &err);
>> +    if (err != NULL) {
>> +        error_propagate(errp, err);
>> +        return;
>> +    }
>> +    sysbusdev = SYS_BUS_DEVICE(&s->emac);
>> +    sysbus_mmio_map(sysbusdev, 0, AW_H3_EMAC_BASE);
>> +    sysbus_connect_irq(sysbusdev, 0, s->irq[AW_H3_GIC_SPI_EMAC]);
>> +
>>       /* Universal Serial Bus */
>>       sysbus_create_simple(TYPE_AW_H3_EHCI, AW_H3_EHCI0_BASE,
>>                            s->irq[AW_H3_GIC_SPI_EHCI0]);
>> diff --git a/hw/arm/orangepi.c b/hw/arm/orangepi.c
>> index dee3efaf08..8a61eb0e69 100644
>> --- a/hw/arm/orangepi.c
>> +++ b/hw/arm/orangepi.c
>> @@ -61,6 +61,13 @@ static void orangepi_init(MachineState *machine)
>>           exit(1);
>>       }
>> +    /* Setup EMAC properties */
>> +    object_property_set_int(OBJECT(&s->h3->emac), 1, "phy-addr", &err);
>> +    if (err != NULL) {
>> +        error_reportf_err(err, "Couldn't set phy address: ");
>> +        exit(1);
>> +    }
>> +
>>       /* Mark H3 object realized */
>>       object_property_set_bool(OBJECT(s->h3), true, "realized", &err);
>>       if (err != NULL) {
>> diff --git a/hw/net/Kconfig b/hw/net/Kconfig
>> index 3856417d42..36d3923992 100644
>> --- a/hw/net/Kconfig
>> +++ b/hw/net/Kconfig
>> @@ -74,6 +74,9 @@ config MIPSNET
>>   config ALLWINNER_EMAC
>>       bool
>> +config ALLWINNER_H3_EMAC
>> +    bool
>> +
>>   config IMX_FEC
>>       bool
>> diff --git a/hw/net/Makefile.objs b/hw/net/Makefile.objs
>> index 7907d2c199..5548deb07a 100644
>> --- a/hw/net/Makefile.objs
>> +++ b/hw/net/Makefile.objs
>> @@ -23,6 +23,7 @@ common-obj-$(CONFIG_XGMAC) += xgmac.o
>>   common-obj-$(CONFIG_MIPSNET) += mipsnet.o
>>   common-obj-$(CONFIG_XILINX_AXI) += xilinx_axienet.o
>>   common-obj-$(CONFIG_ALLWINNER_EMAC) += allwinner_emac.o
>> +common-obj-$(CONFIG_ALLWINNER_H3_EMAC) += allwinner-h3-emac.o
>>   common-obj-$(CONFIG_IMX_FEC) += imx_fec.o
>>   common-obj-$(CONFIG_CADENCE) += cadence_gem.o
>> diff --git a/hw/net/allwinner-h3-emac.c b/hw/net/allwinner-h3-emac.c
>> new file mode 100644
>> index 0000000000..37f6f44406
>> --- /dev/null
>> +++ b/hw/net/allwinner-h3-emac.c
>> @@ -0,0 +1,786 @@
>> +/*
>> + * Allwinner H3 EMAC 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 "hw/sysbus.h"
>> +#include "migration/vmstate.h"
>> +#include "net/net.h"
>> +#include "hw/irq.h"
>> +#include "hw/qdev-properties.h"
>> +#include "qemu/log.h"
>> +#include "trace.h"
>> +#include "net/checksum.h"
>> +#include "qemu/module.h"
>> +#include "exec/cpu-common.h"
>> +#include "hw/net/allwinner-h3-emac.h"
>> +
>> +/* EMAC register offsets */
>> +#define REG_BASIC_CTL_0        (0x0000) /* Basic Control 0 */
>> +#define REG_BASIC_CTL_1        (0x0004) /* Basic Control 1 */
>> +#define REG_INT_STA            (0x0008) /* Interrupt Status */
>> +#define REG_INT_EN             (0x000C) /* Interrupt Enable */
>> +#define REG_TX_CTL_0           (0x0010) /* Transmit Control 0 */
>> +#define REG_TX_CTL_1           (0x0014) /* Transmit Control 1 */
>> +#define REG_TX_FLOW_CTL        (0x001C) /* Transmit Flow Control */
>> +#define REG_TX_DMA_DESC_LIST   (0x0020) /* Transmit Descriptor List 
>> Address */
>> +#define REG_RX_CTL_0           (0x0024) /* Receive Control 0 */
>> +#define REG_RX_CTL_1           (0x0028) /* Receive Control 1 */
>> +#define REG_RX_DMA_DESC_LIST   (0x0034) /* Receive Descriptor List 
>> Address */
>> +#define REG_FRM_FLT            (0x0038) /* Receive Frame Filter */
>> +#define REG_RX_HASH_0          (0x0040) /* Receive Hash Table 0 */
>> +#define REG_RX_HASH_1          (0x0044) /* Receive Hash Table 1 */
>> +#define REG_MII_CMD            (0x0048) /* Management Interface 
>> Command */
>> +#define REG_MII_DATA           (0x004C) /* Management Interface Data */
>> +#define REG_ADDR_HIGH          (0x0050) /* MAC Address High */
>> +#define REG_ADDR_LOW           (0x0054) /* MAC Address Low */
>> +#define REG_TX_DMA_STA         (0x00B0) /* Transmit DMA Status */
>> +#define REG_TX_CUR_DESC        (0x00B4) /* Transmit Current 
>> Descriptor */
>> +#define REG_TX_CUR_BUF         (0x00B8) /* Transmit Current Buffer */
>> +#define REG_RX_DMA_STA         (0x00C0) /* Receive DMA Status */
>> +#define REG_RX_CUR_DESC        (0x00C4) /* Receive Current Descriptor */
>> +#define REG_RX_CUR_BUF         (0x00C8) /* Receive Current Buffer */
>> +#define REG_RGMII_STA          (0x00D0) /* RGMII Status */
>> +
>> +/* EMAC register flags */
>> +#define BASIC_CTL0_100Mbps     (0b11 << 2)
>> +#define BASIC_CTL0_FD          (1 << 0)
>> +#define BASIC_CTL1_SOFTRST     (1 << 0)
>> +
>> +#define INT_STA_RGMII_LINK     (1 << 16)
>> +#define INT_STA_RX_EARLY       (1 << 13)
>> +#define INT_STA_RX_OVERFLOW    (1 << 12)
>> +#define INT_STA_RX_TIMEOUT     (1 << 11)
>> +#define INT_STA_RX_DMA_STOP    (1 << 10)
>> +#define INT_STA_RX_BUF_UA      (1 << 9)
>> +#define INT_STA_RX             (1 << 8)
>> +#define INT_STA_TX_EARLY       (1 << 5)
>> +#define INT_STA_TX_UNDERFLOW   (1 << 4)
>> +#define INT_STA_TX_TIMEOUT     (1 << 3)
>> +#define INT_STA_TX_BUF_UA      (1 << 2)
>> +#define INT_STA_TX_DMA_STOP    (1 << 1)
>> +#define INT_STA_TX             (1 << 0)
>> +
>> +#define INT_EN_RX_EARLY        (1 << 13)
>> +#define INT_EN_RX_OVERFLOW     (1 << 12)
>> +#define INT_EN_RX_TIMEOUT      (1 << 11)
>> +#define INT_EN_RX_DMA_STOP     (1 << 10)
>> +#define INT_EN_RX_BUF_UA       (1 << 9)
>> +#define INT_EN_RX              (1 << 8)
>> +#define INT_EN_TX_EARLY        (1 << 5)
>> +#define INT_EN_TX_UNDERFLOW    (1 << 4)
>> +#define INT_EN_TX_TIMEOUT      (1 << 3)
>> +#define INT_EN_TX_BUF_UA       (1 << 2)
>> +#define INT_EN_TX_DMA_STOP     (1 << 1)
>> +#define INT_EN_TX              (1 << 0)
>> +
>> +#define TX_CTL0_TX_EN          (1 << 31)
>> +#define TX_CTL1_TX_DMA_START   (1 << 31)
>> +#define TX_CTL1_TX_DMA_EN      (1 << 30)
>> +#define TX_CTL1_TX_FLUSH       (1 << 0)
>> +
>> +#define RX_CTL0_RX_EN          (1 << 31)
>> +#define RX_CTL0_STRIP_FCS      (1 << 28)
>> +#define RX_CTL0_CRC_IPV4       (1 << 27)
>> +
>> +#define RX_CTL1_RX_DMA_START   (1 << 31)
>> +#define RX_CTL1_RX_DMA_EN      (1 << 30)
>> +#define RX_CTL1_RX_MD          (1 << 1)
>> +
>> +#define RX_FRM_FLT_DIS_ADDR    (1 << 31)
>> +
>> +#define MII_CMD_PHY_ADDR_SHIFT (12)
>> +#define MII_CMD_PHY_ADDR_MASK  (0xf000)
>> +#define MII_CMD_PHY_REG_SHIFT  (4)
>> +#define MII_CMD_PHY_REG_MASK   (0xf0)
>> +#define MII_CMD_PHY_RW         (1 << 1)
>> +#define MII_CMD_PHY_BUSY       (1 << 0)
>> +
>> +#define TX_DMA_STA_STOP        (0b000)
>> +#define TX_DMA_STA_RUN_FETCH   (0b001)
>> +#define TX_DMA_STA_WAIT_STA    (0b010)
>> +
>> +#define RX_DMA_STA_STOP        (0b000)
>> +#define RX_DMA_STA_RUN_FETCH   (0b001)
>> +#define RX_DMA_STA_WAIT_FRM    (0b011)
>> +
>> +#define RGMII_LINK_UP          (1 << 3)
>> +#define RGMII_FD               (1 << 0)
>> +
>> +/* EMAC register reset values */
>> +#define REG_BASIC_CTL_1_RST    (0x08000000)
>> +
>> +/* EMAC constants */
>> +#define AW_H3_EMAC_MIN_PKT_SZ  (64)
>> +
>> +/* Transmit/receive frame descriptor */
>> +typedef struct FrameDescriptor {
>> +    uint32_t status;
>> +    uint32_t status2;
>> +    uint32_t addr;
>> +    uint32_t next;
>> +} FrameDescriptor;
>> +
>> +/* Frame descriptor flags */
>> +#define DESC_STATUS_CTL                 (1 << 31)
>> +#define DESC_STATUS2_BUF_SIZE_MASK      (0x7ff)
>> +
>> +/* Transmit frame descriptor flags */
>> +#define TX_DESC_STATUS_LENGTH_ERR       (1 << 14)
>> +#define TX_DESC_STATUS2_FIRST_DESC      (1 << 29)
>> +#define TX_DESC_STATUS2_LAST_DESC       (1 << 30)
>> +#define TX_DESC_STATUS2_CHECKSUM_MASK   (0x3 << 27)
>> +
>> +/* Receive frame descriptor flags */
>> +#define RX_DESC_STATUS_FIRST_DESC       (1 << 9)
>> +#define RX_DESC_STATUS_LAST_DESC        (1 << 8)
>> +#define RX_DESC_STATUS_FRM_LEN_MASK     (0x3fff0000)
>> +#define RX_DESC_STATUS_FRM_LEN_SHIFT    (16)
>> +#define RX_DESC_STATUS_NO_BUF           (1 << 14)
>> +#define RX_DESC_STATUS_HEADER_ERR       (1 << 7)
>> +#define RX_DESC_STATUS_LENGTH_ERR       (1 << 4)
>> +#define RX_DESC_STATUS_CRC_ERR          (1 << 1)
>> +#define RX_DESC_STATUS_PAYLOAD_ERR      (1 << 0)
>> +#define RX_DESC_STATUS2_RX_INT_CTL      (1 << 31)
>> +
>> +/* MII register offsets */
>> +#define MII_REG_CR                      (0x0)
>> +#define MII_REG_ST                      (0x1)
>> +#define MII_REG_ID_HIGH                 (0x2)
>> +#define MII_REG_ID_LOW                  (0x3)
>> +
>> +/* MII register flags */
>> +#define MII_REG_CR_RESET                (1 << 15)
>> +#define MII_REG_CR_POWERDOWN            (1 << 11)
>> +#define MII_REG_CR_10Mbit               (0)
>> +#define MII_REG_CR_100Mbit              (1 << 13)
>> +#define MII_REG_CR_1000Mbit             (1 << 6)
>> +#define MII_REG_CR_AUTO_NEG             (1 << 12)
>> +#define MII_REG_CR_AUTO_NEG_RESTART     (1 << 9)
>> +#define MII_REG_CR_FULLDUPLEX           (1 << 8)
>> +
>> +#define MII_REG_ST_100BASE_T4           (1 << 15)
>> +#define MII_REG_ST_100BASE_X_FD         (1 << 14)
>> +#define MII_REG_ST_100BASE_X_HD         (1 << 13)
>> +#define MII_REG_ST_10_FD                (1 << 12)
>> +#define MII_REG_ST_10_HD                (1 << 11)
>> +#define MII_REG_ST_100BASE_T2_FD        (1 << 10)
>> +#define MII_REG_ST_100BASE_T2_HD        (1 << 9)
>> +#define MII_REG_ST_AUTONEG_COMPLETE     (1 << 5)
>> +#define MII_REG_ST_AUTONEG_AVAIL        (1 << 3)
>> +#define MII_REG_ST_LINK_UP              (1 << 2)
>> +
>> +/* MII constants */
>> +#define MII_PHY_ID_HIGH                 (0x0044)
>> +#define MII_PHY_ID_LOW                  (0x1400)
> 
> I wonder if we can't share all those mii stuff accross the network adapters
> instead of redoing the work everytime. I've some patches about it I may 
> post
> them sometimes.

I started that too, salvaging patches from Grant Likely and Sai Pavan 
Boddu, but I'm doing this in my hobbyist time so progress is slow. I 
estimate I'v 80% of the work done.
Basically it adds a MDIO QBUS (as SD/I2C) and we can plug multiple QDEV 
slaves. Benefits are easily tracing of the MDIO activity, and ability to 
interchange the master/slaves for testing (think libqos qgraph).



^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH 10/10] arm: allwinner-h3: add EMAC ethernet device
  2019-12-04 15:14     ` Philippe Mathieu-Daudé
@ 2019-12-04 15:22       ` KONRAD Frederic
  0 siblings, 0 replies; 33+ messages in thread
From: KONRAD Frederic @ 2019-12-04 15:22 UTC (permalink / raw)
  To: Philippe Mathieu-Daudé, Niek Linnenbank, qemu-devel
  Cc: peter.maydell, Jason Wang, Sai Pavan Boddu, Grant Likely,
	b.galvani, qemu-arm, Paolo Bonzini, Sven Schnelle



Le 12/4/19 à 4:14 PM, Philippe Mathieu-Daudé a écrit :
> On 12/3/19 10:33 AM, KONRAD Frederic wrote:
>> Le 12/2/19 à 10:09 PM, Niek Linnenbank a écrit :
>>> The Allwinner H3 System on Chip includes an Ethernet MAC (EMAC)
>>> which provides 10M/100M/1000M Ethernet connectivity. This commit
>>> adds support for the Allwinner H3 EMAC, including emulation for
>>> the following functionality:
>>>
>>>   * DMA transfers
>>>   * MII interface
>>>   * Transmit CRC calculation
>>>
>>> Signed-off-by: Niek Linnenbank <nieklinnenbank@gmail.com>
>>> ---
>>>   hw/arm/Kconfig                     |   1 +
>>>   hw/arm/allwinner-h3.c              |  17 +
>>>   hw/arm/orangepi.c                  |   7 +
>>>   hw/net/Kconfig                     |   3 +
>>>   hw/net/Makefile.objs               |   1 +
>>>   hw/net/allwinner-h3-emac.c         | 786 +++++++++++++++++++++++++++++
>>>   hw/net/trace-events                |  10 +
>>>   include/hw/arm/allwinner-h3.h      |   2 +
>>>   include/hw/net/allwinner-h3-emac.h |  69 +++
>>>   9 files changed, 896 insertions(+)
>>>   create mode 100644 hw/net/allwinner-h3-emac.c
>>>   create mode 100644 include/hw/net/allwinner-h3-emac.h
>>>
>>> diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig
>>> index ebf8d2325f..551cff3442 100644
>>> --- a/hw/arm/Kconfig
>>> +++ b/hw/arm/Kconfig
>>> @@ -294,6 +294,7 @@ config ALLWINNER_A10
>>>   config ALLWINNER_H3
>>>       bool
>>>       select ALLWINNER_A10_PIT
>>> +    select ALLWINNER_H3_EMAC
>>>       select SERIAL
>>>       select ARM_TIMER
>>>       select ARM_GIC
>>> diff --git a/hw/arm/allwinner-h3.c b/hw/arm/allwinner-h3.c
>>> index c2972caf88..274b8548c0 100644
>>> --- a/hw/arm/allwinner-h3.c
>>> +++ b/hw/arm/allwinner-h3.c
>>> @@ -53,6 +53,9 @@ static void aw_h3_init(Object *obj)
>>>       sysbus_init_child_obj(obj, "mmc0", &s->mmc0, sizeof(s->mmc0),
>>>                             TYPE_AW_H3_SDHOST);
>>> +
>>> +    sysbus_init_child_obj(obj, "emac", &s->emac, sizeof(s->emac),
>>> +                          TYPE_AW_H3_EMAC);
>>>   }
>>>   static void aw_h3_realize(DeviceState *dev, Error **errp)
>>> @@ -237,6 +240,20 @@ static void aw_h3_realize(DeviceState *dev, Error **errp)
>>>           return;
>>>       }
>>> +    /* EMAC */
>>> +    if (nd_table[0].used) {
>>> +        qemu_check_nic_model(&nd_table[0], TYPE_AW_H3_EMAC);
>>> +        qdev_set_nic_properties(DEVICE(&s->emac), &nd_table[0]);
>>> +    }
>>> +    object_property_set_bool(OBJECT(&s->emac), true, "realized", &err);
>>> +    if (err != NULL) {
>>> +        error_propagate(errp, err);
>>> +        return;
>>> +    }
>>> +    sysbusdev = SYS_BUS_DEVICE(&s->emac);
>>> +    sysbus_mmio_map(sysbusdev, 0, AW_H3_EMAC_BASE);
>>> +    sysbus_connect_irq(sysbusdev, 0, s->irq[AW_H3_GIC_SPI_EMAC]);
>>> +
>>>       /* Universal Serial Bus */
>>>       sysbus_create_simple(TYPE_AW_H3_EHCI, AW_H3_EHCI0_BASE,
>>>                            s->irq[AW_H3_GIC_SPI_EHCI0]);
>>> diff --git a/hw/arm/orangepi.c b/hw/arm/orangepi.c
>>> index dee3efaf08..8a61eb0e69 100644
>>> --- a/hw/arm/orangepi.c
>>> +++ b/hw/arm/orangepi.c
>>> @@ -61,6 +61,13 @@ static void orangepi_init(MachineState *machine)
>>>           exit(1);
>>>       }
>>> +    /* Setup EMAC properties */
>>> +    object_property_set_int(OBJECT(&s->h3->emac), 1, "phy-addr", &err);
>>> +    if (err != NULL) {
>>> +        error_reportf_err(err, "Couldn't set phy address: ");
>>> +        exit(1);
>>> +    }
>>> +
>>>       /* Mark H3 object realized */
>>>       object_property_set_bool(OBJECT(s->h3), true, "realized", &err);
>>>       if (err != NULL) {
>>> diff --git a/hw/net/Kconfig b/hw/net/Kconfig
>>> index 3856417d42..36d3923992 100644
>>> --- a/hw/net/Kconfig
>>> +++ b/hw/net/Kconfig
>>> @@ -74,6 +74,9 @@ config MIPSNET
>>>   config ALLWINNER_EMAC
>>>       bool
>>> +config ALLWINNER_H3_EMAC
>>> +    bool
>>> +
>>>   config IMX_FEC
>>>       bool
>>> diff --git a/hw/net/Makefile.objs b/hw/net/Makefile.objs
>>> index 7907d2c199..5548deb07a 100644
>>> --- a/hw/net/Makefile.objs
>>> +++ b/hw/net/Makefile.objs
>>> @@ -23,6 +23,7 @@ common-obj-$(CONFIG_XGMAC) += xgmac.o
>>>   common-obj-$(CONFIG_MIPSNET) += mipsnet.o
>>>   common-obj-$(CONFIG_XILINX_AXI) += xilinx_axienet.o
>>>   common-obj-$(CONFIG_ALLWINNER_EMAC) += allwinner_emac.o
>>> +common-obj-$(CONFIG_ALLWINNER_H3_EMAC) += allwinner-h3-emac.o
>>>   common-obj-$(CONFIG_IMX_FEC) += imx_fec.o
>>>   common-obj-$(CONFIG_CADENCE) += cadence_gem.o
>>> diff --git a/hw/net/allwinner-h3-emac.c b/hw/net/allwinner-h3-emac.c
>>> new file mode 100644
>>> index 0000000000..37f6f44406
>>> --- /dev/null
>>> +++ b/hw/net/allwinner-h3-emac.c
>>> @@ -0,0 +1,786 @@
>>> +/*
>>> + * Allwinner H3 EMAC 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 "hw/sysbus.h"
>>> +#include "migration/vmstate.h"
>>> +#include "net/net.h"
>>> +#include "hw/irq.h"
>>> +#include "hw/qdev-properties.h"
>>> +#include "qemu/log.h"
>>> +#include "trace.h"
>>> +#include "net/checksum.h"
>>> +#include "qemu/module.h"
>>> +#include "exec/cpu-common.h"
>>> +#include "hw/net/allwinner-h3-emac.h"
>>> +
>>> +/* EMAC register offsets */
>>> +#define REG_BASIC_CTL_0        (0x0000) /* Basic Control 0 */
>>> +#define REG_BASIC_CTL_1        (0x0004) /* Basic Control 1 */
>>> +#define REG_INT_STA            (0x0008) /* Interrupt Status */
>>> +#define REG_INT_EN             (0x000C) /* Interrupt Enable */
>>> +#define REG_TX_CTL_0           (0x0010) /* Transmit Control 0 */
>>> +#define REG_TX_CTL_1           (0x0014) /* Transmit Control 1 */
>>> +#define REG_TX_FLOW_CTL        (0x001C) /* Transmit Flow Control */
>>> +#define REG_TX_DMA_DESC_LIST   (0x0020) /* Transmit Descriptor List Address */
>>> +#define REG_RX_CTL_0           (0x0024) /* Receive Control 0 */
>>> +#define REG_RX_CTL_1           (0x0028) /* Receive Control 1 */
>>> +#define REG_RX_DMA_DESC_LIST   (0x0034) /* Receive Descriptor List Address */
>>> +#define REG_FRM_FLT            (0x0038) /* Receive Frame Filter */
>>> +#define REG_RX_HASH_0          (0x0040) /* Receive Hash Table 0 */
>>> +#define REG_RX_HASH_1          (0x0044) /* Receive Hash Table 1 */
>>> +#define REG_MII_CMD            (0x0048) /* Management Interface Command */
>>> +#define REG_MII_DATA           (0x004C) /* Management Interface Data */
>>> +#define REG_ADDR_HIGH          (0x0050) /* MAC Address High */
>>> +#define REG_ADDR_LOW           (0x0054) /* MAC Address Low */
>>> +#define REG_TX_DMA_STA         (0x00B0) /* Transmit DMA Status */
>>> +#define REG_TX_CUR_DESC        (0x00B4) /* Transmit Current Descriptor */
>>> +#define REG_TX_CUR_BUF         (0x00B8) /* Transmit Current Buffer */
>>> +#define REG_RX_DMA_STA         (0x00C0) /* Receive DMA Status */
>>> +#define REG_RX_CUR_DESC        (0x00C4) /* Receive Current Descriptor */
>>> +#define REG_RX_CUR_BUF         (0x00C8) /* Receive Current Buffer */
>>> +#define REG_RGMII_STA          (0x00D0) /* RGMII Status */
>>> +
>>> +/* EMAC register flags */
>>> +#define BASIC_CTL0_100Mbps     (0b11 << 2)
>>> +#define BASIC_CTL0_FD          (1 << 0)
>>> +#define BASIC_CTL1_SOFTRST     (1 << 0)
>>> +
>>> +#define INT_STA_RGMII_LINK     (1 << 16)
>>> +#define INT_STA_RX_EARLY       (1 << 13)
>>> +#define INT_STA_RX_OVERFLOW    (1 << 12)
>>> +#define INT_STA_RX_TIMEOUT     (1 << 11)
>>> +#define INT_STA_RX_DMA_STOP    (1 << 10)
>>> +#define INT_STA_RX_BUF_UA      (1 << 9)
>>> +#define INT_STA_RX             (1 << 8)
>>> +#define INT_STA_TX_EARLY       (1 << 5)
>>> +#define INT_STA_TX_UNDERFLOW   (1 << 4)
>>> +#define INT_STA_TX_TIMEOUT     (1 << 3)
>>> +#define INT_STA_TX_BUF_UA      (1 << 2)
>>> +#define INT_STA_TX_DMA_STOP    (1 << 1)
>>> +#define INT_STA_TX             (1 << 0)
>>> +
>>> +#define INT_EN_RX_EARLY        (1 << 13)
>>> +#define INT_EN_RX_OVERFLOW     (1 << 12)
>>> +#define INT_EN_RX_TIMEOUT      (1 << 11)
>>> +#define INT_EN_RX_DMA_STOP     (1 << 10)
>>> +#define INT_EN_RX_BUF_UA       (1 << 9)
>>> +#define INT_EN_RX              (1 << 8)
>>> +#define INT_EN_TX_EARLY        (1 << 5)
>>> +#define INT_EN_TX_UNDERFLOW    (1 << 4)
>>> +#define INT_EN_TX_TIMEOUT      (1 << 3)
>>> +#define INT_EN_TX_BUF_UA       (1 << 2)
>>> +#define INT_EN_TX_DMA_STOP     (1 << 1)
>>> +#define INT_EN_TX              (1 << 0)
>>> +
>>> +#define TX_CTL0_TX_EN          (1 << 31)
>>> +#define TX_CTL1_TX_DMA_START   (1 << 31)
>>> +#define TX_CTL1_TX_DMA_EN      (1 << 30)
>>> +#define TX_CTL1_TX_FLUSH       (1 << 0)
>>> +
>>> +#define RX_CTL0_RX_EN          (1 << 31)
>>> +#define RX_CTL0_STRIP_FCS      (1 << 28)
>>> +#define RX_CTL0_CRC_IPV4       (1 << 27)
>>> +
>>> +#define RX_CTL1_RX_DMA_START   (1 << 31)
>>> +#define RX_CTL1_RX_DMA_EN      (1 << 30)
>>> +#define RX_CTL1_RX_MD          (1 << 1)
>>> +
>>> +#define RX_FRM_FLT_DIS_ADDR    (1 << 31)
>>> +
>>> +#define MII_CMD_PHY_ADDR_SHIFT (12)
>>> +#define MII_CMD_PHY_ADDR_MASK  (0xf000)
>>> +#define MII_CMD_PHY_REG_SHIFT  (4)
>>> +#define MII_CMD_PHY_REG_MASK   (0xf0)
>>> +#define MII_CMD_PHY_RW         (1 << 1)
>>> +#define MII_CMD_PHY_BUSY       (1 << 0)
>>> +
>>> +#define TX_DMA_STA_STOP        (0b000)
>>> +#define TX_DMA_STA_RUN_FETCH   (0b001)
>>> +#define TX_DMA_STA_WAIT_STA    (0b010)
>>> +
>>> +#define RX_DMA_STA_STOP        (0b000)
>>> +#define RX_DMA_STA_RUN_FETCH   (0b001)
>>> +#define RX_DMA_STA_WAIT_FRM    (0b011)
>>> +
>>> +#define RGMII_LINK_UP          (1 << 3)
>>> +#define RGMII_FD               (1 << 0)
>>> +
>>> +/* EMAC register reset values */
>>> +#define REG_BASIC_CTL_1_RST    (0x08000000)
>>> +
>>> +/* EMAC constants */
>>> +#define AW_H3_EMAC_MIN_PKT_SZ  (64)
>>> +
>>> +/* Transmit/receive frame descriptor */
>>> +typedef struct FrameDescriptor {
>>> +    uint32_t status;
>>> +    uint32_t status2;
>>> +    uint32_t addr;
>>> +    uint32_t next;
>>> +} FrameDescriptor;
>>> +
>>> +/* Frame descriptor flags */
>>> +#define DESC_STATUS_CTL                 (1 << 31)
>>> +#define DESC_STATUS2_BUF_SIZE_MASK      (0x7ff)
>>> +
>>> +/* Transmit frame descriptor flags */
>>> +#define TX_DESC_STATUS_LENGTH_ERR       (1 << 14)
>>> +#define TX_DESC_STATUS2_FIRST_DESC      (1 << 29)
>>> +#define TX_DESC_STATUS2_LAST_DESC       (1 << 30)
>>> +#define TX_DESC_STATUS2_CHECKSUM_MASK   (0x3 << 27)
>>> +
>>> +/* Receive frame descriptor flags */
>>> +#define RX_DESC_STATUS_FIRST_DESC       (1 << 9)
>>> +#define RX_DESC_STATUS_LAST_DESC        (1 << 8)
>>> +#define RX_DESC_STATUS_FRM_LEN_MASK     (0x3fff0000)
>>> +#define RX_DESC_STATUS_FRM_LEN_SHIFT    (16)
>>> +#define RX_DESC_STATUS_NO_BUF           (1 << 14)
>>> +#define RX_DESC_STATUS_HEADER_ERR       (1 << 7)
>>> +#define RX_DESC_STATUS_LENGTH_ERR       (1 << 4)
>>> +#define RX_DESC_STATUS_CRC_ERR          (1 << 1)
>>> +#define RX_DESC_STATUS_PAYLOAD_ERR      (1 << 0)
>>> +#define RX_DESC_STATUS2_RX_INT_CTL      (1 << 31)
>>> +
>>> +/* MII register offsets */
>>> +#define MII_REG_CR                      (0x0)
>>> +#define MII_REG_ST                      (0x1)
>>> +#define MII_REG_ID_HIGH                 (0x2)
>>> +#define MII_REG_ID_LOW                  (0x3)
>>> +
>>> +/* MII register flags */
>>> +#define MII_REG_CR_RESET                (1 << 15)
>>> +#define MII_REG_CR_POWERDOWN            (1 << 11)
>>> +#define MII_REG_CR_10Mbit               (0)
>>> +#define MII_REG_CR_100Mbit              (1 << 13)
>>> +#define MII_REG_CR_1000Mbit             (1 << 6)
>>> +#define MII_REG_CR_AUTO_NEG             (1 << 12)
>>> +#define MII_REG_CR_AUTO_NEG_RESTART     (1 << 9)
>>> +#define MII_REG_CR_FULLDUPLEX           (1 << 8)
>>> +
>>> +#define MII_REG_ST_100BASE_T4           (1 << 15)
>>> +#define MII_REG_ST_100BASE_X_FD         (1 << 14)
>>> +#define MII_REG_ST_100BASE_X_HD         (1 << 13)
>>> +#define MII_REG_ST_10_FD                (1 << 12)
>>> +#define MII_REG_ST_10_HD                (1 << 11)
>>> +#define MII_REG_ST_100BASE_T2_FD        (1 << 10)
>>> +#define MII_REG_ST_100BASE_T2_HD        (1 << 9)
>>> +#define MII_REG_ST_AUTONEG_COMPLETE     (1 << 5)
>>> +#define MII_REG_ST_AUTONEG_AVAIL        (1 << 3)
>>> +#define MII_REG_ST_LINK_UP              (1 << 2)
>>> +
>>> +/* MII constants */
>>> +#define MII_PHY_ID_HIGH                 (0x0044)
>>> +#define MII_PHY_ID_LOW                  (0x1400)
>>
>> I wonder if we can't share all those mii stuff accross the network adapters
>> instead of redoing the work everytime. I've some patches about it I may post
>> them sometimes.
> 
> I started that too, salvaging patches from Grant Likely and Sai Pavan Boddu, but 
> I'm doing this in my hobbyist time so progress is slow. I estimate I'v 80% of 
> the work done.
> Basically it adds a MDIO QBUS (as SD/I2C) and we can plug multiple QDEV slaves. 
> Benefits are easily tracing of the MDIO activity, and ability to interchange the 
> master/slaves for testing (think libqos qgraph).
> 

Interesting! I didn't go that far though. The ability to change the device
ID and not rewriting those registers for every network adapters is sufficient
for me :).


^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH 04/10] arm: allwinner-h3: add USB host controller
  2019-12-02 21:09 ` [PATCH 04/10] arm: allwinner-h3: add USB host controller Niek Linnenbank
@ 2019-12-04 16:11   ` Aleksandar Markovic
  2019-12-04 20:20     ` Niek Linnenbank
  0 siblings, 1 reply; 33+ messages in thread
From: Aleksandar Markovic @ 2019-12-04 16:11 UTC (permalink / raw)
  To: Niek Linnenbank; +Cc: b.galvani, peter.maydell, qemu-arm, qemu-devel

[-- Attachment #1: Type: text/plain, Size: 4705 bytes --]

On Monday, December 2, 2019, Niek Linnenbank <nieklinnenbank@gmail.com>
wrote:

> The Allwinner H3 System on Chip contains multiple USB 2.0 bus
> connections which provide software access using the Enhanced
> Host Controller Interface (EHCI) and Open Host Controller
> Interface (OHCI) interfaces. This commit adds support for
> both interfaces in the Allwinner H3 System on Chip.
>
> Signed-off-by: Niek Linnenbank <nieklinnenbank@gmail.com>
> ---


Niek, hi!

I would like to clarify a detail here:

The spec of the SoC enumerates (in 8.5.2.4. USB Host Register List) a
number of registers for reading various USB-related states, but also for
setting some of USB features.

Does this series cover these registers, and interaction with them? If yes,
how and where? If not, do you think it is not necessary at all? Or perhaps
that it is a non-crucial limitation of this series?

Thanks in advance, and congrats for your, it seems, first submission!

Aleksandar


 hw/arm/allwinner-h3.c    | 20 ++++++++++++++++++++
>  hw/usb/hcd-ehci-sysbus.c | 17 +++++++++++++++++
>  hw/usb/hcd-ehci.h        |  1 +
>  3 files changed, 38 insertions(+)
>
> diff --git a/hw/arm/allwinner-h3.c b/hw/arm/allwinner-h3.c
> index 5566e979ec..afeb49c0ac 100644
> --- a/hw/arm/allwinner-h3.c
> +++ b/hw/arm/allwinner-h3.c
> @@ -26,6 +26,7 @@
>  #include "hw/sysbus.h"
>  #include "hw/arm/allwinner-h3.h"
>  #include "hw/misc/unimp.h"
> +#include "hw/usb/hcd-ehci.h"
>  #include "sysemu/sysemu.h"
>
>  static void aw_h3_init(Object *obj)
> @@ -183,6 +184,25 @@ static void aw_h3_realize(DeviceState *dev, Error
> **errp)
>      }
>      sysbus_mmio_map(SYS_BUS_DEVICE(&s->ccu), 0, AW_H3_CCU_BASE);
>
> +    /* Universal Serial Bus */
> +    sysbus_create_simple(TYPE_AW_H3_EHCI, AW_H3_EHCI0_BASE,
> +                         s->irq[AW_H3_GIC_SPI_EHCI0]);
> +    sysbus_create_simple(TYPE_AW_H3_EHCI, AW_H3_EHCI1_BASE,
> +                         s->irq[AW_H3_GIC_SPI_EHCI1]);
> +    sysbus_create_simple(TYPE_AW_H3_EHCI, AW_H3_EHCI2_BASE,
> +                         s->irq[AW_H3_GIC_SPI_EHCI2]);
> +    sysbus_create_simple(TYPE_AW_H3_EHCI, AW_H3_EHCI3_BASE,
> +                         s->irq[AW_H3_GIC_SPI_EHCI3]);
> +
> +    sysbus_create_simple("sysbus-ohci", AW_H3_OHCI0_BASE,
> +                         s->irq[AW_H3_GIC_SPI_OHCI0]);
> +    sysbus_create_simple("sysbus-ohci", AW_H3_OHCI1_BASE,
> +                         s->irq[AW_H3_GIC_SPI_OHCI1]);
> +    sysbus_create_simple("sysbus-ohci", AW_H3_OHCI2_BASE,
> +                         s->irq[AW_H3_GIC_SPI_OHCI2]);
> +    sysbus_create_simple("sysbus-ohci", AW_H3_OHCI3_BASE,
> +                         s->irq[AW_H3_GIC_SPI_OHCI3]);
> +
>      /* UART */
>      if (serial_hd(0)) {
>          serial_mm_init(get_system_memory(), AW_H3_UART0_REG_BASE, 2,
> diff --git a/hw/usb/hcd-ehci-sysbus.c b/hw/usb/hcd-ehci-sysbus.c
> index 020211fd10..174c3446ef 100644
> --- a/hw/usb/hcd-ehci-sysbus.c
> +++ b/hw/usb/hcd-ehci-sysbus.c
> @@ -145,6 +145,22 @@ static const TypeInfo ehci_exynos4210_type_info = {
>      .class_init    = ehci_exynos4210_class_init,
>  };
>
> +static void ehci_aw_h3_class_init(ObjectClass *oc, void *data)
> +{
> +    SysBusEHCIClass *sec = SYS_BUS_EHCI_CLASS(oc);
> +    DeviceClass *dc = DEVICE_CLASS(oc);
> +
> +    sec->capsbase = 0x0;
> +    sec->opregbase = 0x10;
> +    set_bit(DEVICE_CATEGORY_USB, dc->categories);
> +}
> +
> +static const TypeInfo ehci_aw_h3_type_info = {
> +    .name          = TYPE_AW_H3_EHCI,
> +    .parent        = TYPE_SYS_BUS_EHCI,
> +    .class_init    = ehci_aw_h3_class_init,
> +};
> +
>  static void ehci_tegra2_class_init(ObjectClass *oc, void *data)
>  {
>      SysBusEHCIClass *sec = SYS_BUS_EHCI_CLASS(oc);
> @@ -267,6 +283,7 @@ static void ehci_sysbus_register_types(void)
>      type_register_static(&ehci_platform_type_info);
>      type_register_static(&ehci_xlnx_type_info);
>      type_register_static(&ehci_exynos4210_type_info);
> +    type_register_static(&ehci_aw_h3_type_info);
>      type_register_static(&ehci_tegra2_type_info);
>      type_register_static(&ehci_ppc4xx_type_info);
>      type_register_static(&ehci_fusbh200_type_info);
> diff --git a/hw/usb/hcd-ehci.h b/hw/usb/hcd-ehci.h
> index 0298238f0b..edb59311c4 100644
> --- a/hw/usb/hcd-ehci.h
> +++ b/hw/usb/hcd-ehci.h
> @@ -342,6 +342,7 @@ typedef struct EHCIPCIState {
>  #define TYPE_SYS_BUS_EHCI "sysbus-ehci-usb"
>  #define TYPE_PLATFORM_EHCI "platform-ehci-usb"
>  #define TYPE_EXYNOS4210_EHCI "exynos4210-ehci-usb"
> +#define TYPE_AW_H3_EHCI "aw-h3-ehci-usb"
>  #define TYPE_TEGRA2_EHCI "tegra2-ehci-usb"
>  #define TYPE_PPC4xx_EHCI "ppc4xx-ehci-usb"
>  #define TYPE_FUSBH200_EHCI "fusbh200-ehci-usb"
> --
> 2.17.1
>
>
>

[-- Attachment #2: Type: text/html, Size: 6075 bytes --]

<br><br>On Monday, December 2, 2019, Niek Linnenbank &lt;<a href="mailto:nieklinnenbank@gmail.com">nieklinnenbank@gmail.com</a>&gt; wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">The Allwinner H3 System on Chip contains multiple USB 2.0 bus<br>
connections which provide software access using the Enhanced<br>
Host Controller Interface (EHCI) and Open Host Controller<br>
Interface (OHCI) interfaces. This commit adds support for<br>
both interfaces in the Allwinner H3 System on Chip.<br>
<br>
Signed-off-by: Niek Linnenbank &lt;<a href="mailto:nieklinnenbank@gmail.com">nieklinnenbank@gmail.com</a>&gt;<br>
---</blockquote><div><br></div><div>Niek, hi!</div><div><br></div><div>I would like to clarify a detail here:</div><div><br></div><div>The spec of the SoC enumerates (in 8.5.2.4. USB Host Register List) a number of registers for reading various USB-related states, but also for setting some of USB features.</div><div> </div><div>Does this series cover these registers, and interaction with them? If yes, how and where? If not, do you think it is not necessary at all? Or perhaps that it is a non-crucial limitation of this series?</div><div><br></div><div>Thanks in advance, and congrats for your, it seems, first submission!</div><div><br></div><div>Aleksandar</div><div><br></div><div><br></div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
 hw/arm/allwinner-h3.c    | 20 ++++++++++++++++++++<br>
 hw/usb/hcd-ehci-sysbus.c | 17 +++++++++++++++++<br>
 hw/usb/hcd-ehci.h        |  1 +<br>
 3 files changed, 38 insertions(+)<br>
<br>
diff --git a/hw/arm/allwinner-h3.c b/hw/arm/allwinner-h3.c<br>
index 5566e979ec..afeb49c0ac 100644<br>
--- a/hw/arm/allwinner-h3.c<br>
+++ b/hw/arm/allwinner-h3.c<br>
@@ -26,6 +26,7 @@<br>
 #include &quot;hw/sysbus.h&quot;<br>
 #include &quot;hw/arm/allwinner-h3.h&quot;<br>
 #include &quot;hw/misc/unimp.h&quot;<br>
+#include &quot;hw/usb/hcd-ehci.h&quot;<br>
 #include &quot;sysemu/sysemu.h&quot;<br>
<br>
 static void aw_h3_init(Object *obj)<br>
@@ -183,6 +184,25 @@ static void aw_h3_realize(DeviceState *dev, Error **errp)<br>
     }<br>
     sysbus_mmio_map(SYS_BUS_<wbr>DEVICE(&amp;s-&gt;ccu), 0, AW_H3_CCU_BASE);<br>
<br>
+    /* Universal Serial Bus */<br>
+    sysbus_create_simple(TYPE_AW_<wbr>H3_EHCI, AW_H3_EHCI0_BASE,<br>
+                         s-&gt;irq[AW_H3_GIC_SPI_EHCI0]);<br>
+    sysbus_create_simple(TYPE_AW_<wbr>H3_EHCI, AW_H3_EHCI1_BASE,<br>
+                         s-&gt;irq[AW_H3_GIC_SPI_EHCI1]);<br>
+    sysbus_create_simple(TYPE_AW_<wbr>H3_EHCI, AW_H3_EHCI2_BASE,<br>
+                         s-&gt;irq[AW_H3_GIC_SPI_EHCI2]);<br>
+    sysbus_create_simple(TYPE_AW_<wbr>H3_EHCI, AW_H3_EHCI3_BASE,<br>
+                         s-&gt;irq[AW_H3_GIC_SPI_EHCI3]);<br>
+<br>
+    sysbus_create_simple(&quot;sysbus-<wbr>ohci&quot;, AW_H3_OHCI0_BASE,<br>
+                         s-&gt;irq[AW_H3_GIC_SPI_OHCI0]);<br>
+    sysbus_create_simple(&quot;sysbus-<wbr>ohci&quot;, AW_H3_OHCI1_BASE,<br>
+                         s-&gt;irq[AW_H3_GIC_SPI_OHCI1]);<br>
+    sysbus_create_simple(&quot;sysbus-<wbr>ohci&quot;, AW_H3_OHCI2_BASE,<br>
+                         s-&gt;irq[AW_H3_GIC_SPI_OHCI2]);<br>
+    sysbus_create_simple(&quot;sysbus-<wbr>ohci&quot;, AW_H3_OHCI3_BASE,<br>
+                         s-&gt;irq[AW_H3_GIC_SPI_OHCI3]);<br>
+<br>
     /* UART */<br>
     if (serial_hd(0)) {<br>
         serial_mm_init(get_system_<wbr>memory(), AW_H3_UART0_REG_BASE, 2,<br>
diff --git a/hw/usb/hcd-ehci-sysbus.c b/hw/usb/hcd-ehci-sysbus.c<br>
index 020211fd10..174c3446ef 100644<br>
--- a/hw/usb/hcd-ehci-sysbus.c<br>
+++ b/hw/usb/hcd-ehci-sysbus.c<br>
@@ -145,6 +145,22 @@ static const TypeInfo ehci_exynos4210_type_info = {<br>
     .class_init    = ehci_exynos4210_class_init,<br>
 };<br>
<br>
+static void ehci_aw_h3_class_init(<wbr>ObjectClass *oc, void *data)<br>
+{<br>
+    SysBusEHCIClass *sec = SYS_BUS_EHCI_CLASS(oc);<br>
+    DeviceClass *dc = DEVICE_CLASS(oc);<br>
+<br>
+    sec-&gt;capsbase = 0x0;<br>
+    sec-&gt;opregbase = 0x10;<br>
+    set_bit(DEVICE_CATEGORY_USB, dc-&gt;categories);<br>
+}<br>
+<br>
+static const TypeInfo ehci_aw_h3_type_info = {<br>
+    .name          = TYPE_AW_H3_EHCI,<br>
+    .parent        = TYPE_SYS_BUS_EHCI,<br>
+    .class_init    = ehci_aw_h3_class_init,<br>
+};<br>
+<br>
 static void ehci_tegra2_class_init(<wbr>ObjectClass *oc, void *data)<br>
 {<br>
     SysBusEHCIClass *sec = SYS_BUS_EHCI_CLASS(oc);<br>
@@ -267,6 +283,7 @@ static void ehci_sysbus_register_types(<wbr>void)<br>
     type_register_static(&amp;ehci_<wbr>platform_type_info);<br>
     type_register_static(&amp;ehci_<wbr>xlnx_type_info);<br>
     type_register_static(&amp;ehci_<wbr>exynos4210_type_info);<br>
+    type_register_static(&amp;ehci_aw_<wbr>h3_type_info);<br>
     type_register_static(&amp;ehci_<wbr>tegra2_type_info);<br>
     type_register_static(&amp;ehci_<wbr>ppc4xx_type_info);<br>
     type_register_static(&amp;ehci_<wbr>fusbh200_type_info);<br>
diff --git a/hw/usb/hcd-ehci.h b/hw/usb/hcd-ehci.h<br>
index 0298238f0b..edb59311c4 100644<br>
--- a/hw/usb/hcd-ehci.h<br>
+++ b/hw/usb/hcd-ehci.h<br>
@@ -342,6 +342,7 @@ typedef struct EHCIPCIState {<br>
 #define TYPE_SYS_BUS_EHCI &quot;sysbus-ehci-usb&quot;<br>
 #define TYPE_PLATFORM_EHCI &quot;platform-ehci-usb&quot;<br>
 #define TYPE_EXYNOS4210_EHCI &quot;exynos4210-ehci-usb&quot;<br>
+#define TYPE_AW_H3_EHCI &quot;aw-h3-ehci-usb&quot;<br>
 #define TYPE_TEGRA2_EHCI &quot;tegra2-ehci-usb&quot;<br>
 #define TYPE_PPC4xx_EHCI &quot;ppc4xx-ehci-usb&quot;<br>
 #define TYPE_FUSBH200_EHCI &quot;fusbh200-ehci-usb&quot;<br>
-- <br>
2.17.1<br>
<br>
<br>
</blockquote>

^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH 01/10] hw: arm: add Allwinner H3 System-on-Chip
  2019-12-02 21:09 ` [PATCH 01/10] hw: arm: add Allwinner H3 System-on-Chip Niek Linnenbank
@ 2019-12-04 16:53   ` Philippe Mathieu-Daudé
  2019-12-04 20:44     ` Niek Linnenbank
  0 siblings, 1 reply; 33+ messages in thread
From: Philippe Mathieu-Daudé @ 2019-12-04 16:53 UTC (permalink / raw)
  To: Niek Linnenbank, qemu-devel
  Cc: b.galvani, peter.maydell, qemu-arm, Markus Armbruster

Hi Niek,

On 12/2/19 10:09 PM, Niek Linnenbank wrote:
> The Allwinner H3 is a System on Chip containing four ARM Cortex A7
> processor cores. Features and specifications include DDR2/DDR3 memory,
> SD/MMC storage cards, 10/100/1000Mbit ethernet, USB 2.0, HDMI and
> various I/O modules. This commit adds support for the Allwinner H3
> System on Chip.
> 
> Signed-off-by: Niek Linnenbank <nieklinnenbank@gmail.com>
> ---
>   MAINTAINERS                     |   7 ++
>   default-configs/arm-softmmu.mak |   1 +
>   hw/arm/Kconfig                  |   8 ++
>   hw/arm/Makefile.objs            |   1 +
>   hw/arm/allwinner-h3.c           | 215 ++++++++++++++++++++++++++++++++
>   include/hw/arm/allwinner-h3.h   | 118 ++++++++++++++++++
>   6 files changed, 350 insertions(+)
>   create mode 100644 hw/arm/allwinner-h3.c
>   create mode 100644 include/hw/arm/allwinner-h3.h

Since your series changes various files, can you have a look at the 
scripts/git.orderfile file and setup it for your QEMU contributions?

> 
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 5e5e3e52d6..29c9936037 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -479,6 +479,13 @@ 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*
> +
>   ARM PrimeCell and CMSDK devices
>   M: Peter Maydell <peter.maydell@linaro.org>
>   L: qemu-arm@nongnu.org
> diff --git a/default-configs/arm-softmmu.mak b/default-configs/arm-softmmu.mak
> index 1f2e0e7fde..d75a239c2c 100644
> --- a/default-configs/arm-softmmu.mak
> +++ b/default-configs/arm-softmmu.mak
> @@ -40,3 +40,4 @@ CONFIG_FSL_IMX25=y
>   CONFIG_FSL_IMX7=y
>   CONFIG_FSL_IMX6UL=y
>   CONFIG_SEMIHOSTING=y
> +CONFIG_ALLWINNER_H3=y
> diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig
> index c6e7782580..ebf8d2325f 100644
> --- a/hw/arm/Kconfig
> +++ b/hw/arm/Kconfig
> @@ -291,6 +291,14 @@ config ALLWINNER_A10
>       select SERIAL
>       select UNIMP
>   
> +config ALLWINNER_H3
> +    bool
> +    select ALLWINNER_A10_PIT
> +    select SERIAL
> +    select ARM_TIMER
> +    select ARM_GIC
> +    select UNIMP
> +
>   config RASPI
>       bool
>       select FRAMEBUFFER
> diff --git a/hw/arm/Makefile.objs b/hw/arm/Makefile.objs
> index fe749f65fd..956e496052 100644
> --- a/hw/arm/Makefile.objs
> +++ b/hw/arm/Makefile.objs
> @@ -34,6 +34,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
>   obj-$(CONFIG_RASPI) += bcm2835_peripherals.o bcm2836.o raspi.o
>   obj-$(CONFIG_STM32F205_SOC) += stm32f205_soc.o
>   obj-$(CONFIG_XLNX_ZYNQMP_ARM) += xlnx-zynqmp.o xlnx-zcu102.o
> diff --git a/hw/arm/allwinner-h3.c b/hw/arm/allwinner-h3.c
> new file mode 100644
> index 0000000000..470fdfebef
> --- /dev/null
> +++ b/hw/arm/allwinner-h3.c
> @@ -0,0 +1,215 @@
> +/*
> + * 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/module.h"
> +#include "qemu/units.h"
> +#include "cpu.h"
> +#include "hw/sysbus.h"
> +#include "hw/arm/allwinner-h3.h"
> +#include "hw/misc/unimp.h"
> +#include "sysemu/sysemu.h"
> +
> +static void aw_h3_init(Object *obj)
> +{
> +    AwH3State *s = AW_H3(obj);
> +
> +    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);
> +}
> +
> +static void aw_h3_realize(DeviceState *dev, Error **errp)
> +{
> +    AwH3State *s = AW_H3(dev);
> +    SysBusDevice *sysbusdev = NULL;
> +    Error *err = NULL;
> +    unsigned i = 0;
> +
> +    /* CPUs */
> +    for (i = 0; i < AW_H3_NUM_CPUS; i++) {

In https://www.mail-archive.com/qemu-devel@nongnu.org/msg662942.html
Markus noted some incorrect pattern, and apparently you inherited it.
You should initialize 'err' in the loop.

> +        Object *cpuobj = object_new(ARM_CPU_TYPE_NAME("cortex-a7"));
> +        CPUState *cpustate = CPU(cpuobj);

We loose access to the CPUs. Can you use an array of AW_H3_NUM_CPUS cpus 
in AwH3State?

> +
> +        /* Set the proper CPU index */
> +        cpustate->cpu_index = i;
> +
> +        /* Provide Power State Coordination Interface */
> +        object_property_set_int(cpuobj, QEMU_PSCI_CONDUIT_HVC,
> +                                "psci-conduit", &error_abort);

Here you use the error_abort shortcut.

> +
> +        /* Disable secondary CPUs */
> +        object_property_set_bool(cpuobj, i > 0, "start-powered-off", &err);
> +        if (err != NULL) {
> +            error_propagate(errp, err);
> +            return;

Here you return.

> +        }
> +
> +        /* All exception levels required */
> +        object_property_set_bool(cpuobj,
> +                                 true, "has_el3", NULL);
> +        object_property_set_bool(cpuobj,
> +                                 true, "has_el2", NULL);

Here you don't use error.

Cc'ing Markus who is the expert, since he might have better suggestions.

This function is called before the machine starts, and we are not 
handling with user-provided configurations, so I'd say using 
&error_abort in all places is OK.

> +
> +        /* Mark realized */
> +        object_property_set_bool(cpuobj, true, "realized", &err);
> +        if (err != NULL) {
> +            error_propagate(errp, err);
> +            return;
> +        }
> +        object_unref(cpuobj);
> +    }
> +
> +    /* 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);
> +
> +    object_property_set_bool(OBJECT(&s->gic), true, "realized", &err);

Why change API? Can we use qdev_init_nofail() instead?

> +    if (err) {
> +        error_propagate(errp, err);
> +        return;
> +    }
> +
> +    sysbusdev = SYS_BUS_DEVICE(&s->gic);
> +    sysbus_mmio_map(sysbusdev, 0, AW_H3_GIC_DIST_BASE);
> +    sysbus_mmio_map(sysbusdev, 1, AW_H3_GIC_CPU_BASE);
> +    sysbus_mmio_map(sysbusdev, 2, AW_H3_GIC_HYP_BASE);
> +    sysbus_mmio_map(sysbusdev, 3, AW_H3_GIC_VCPU_BASE);
> +
> +    /*
> +     * 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(qemu_get_cpu(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_ARM_PHYSTIMER,
> +            [GTIMER_VIRT] = AW_H3_GIC_PPI_ARM_VIRTTIMER,
> +            [GTIMER_HYP]  = AW_H3_GIC_PPI_ARM_HYPTIMER,
> +            [GTIMER_SEC]  = AW_H3_GIC_PPI_ARM_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(sysbusdev, i,
> +                           qdev_get_gpio_in(cpudev, ARM_CPU_IRQ));
> +        sysbus_connect_irq(sysbusdev, i + AW_H3_NUM_CPUS,
> +                           qdev_get_gpio_in(cpudev, ARM_CPU_FIQ));
> +        sysbus_connect_irq(sysbusdev, i + (2 * AW_H3_NUM_CPUS),
> +                           qdev_get_gpio_in(cpudev, ARM_CPU_VIRQ));
> +        sysbus_connect_irq(sysbusdev, i + (3 * AW_H3_NUM_CPUS),
> +                           qdev_get_gpio_in(cpudev, ARM_CPU_VFIQ));
> +
> +        /* GIC maintenance signal */
> +        sysbus_connect_irq(sysbusdev, i + (4 * AW_H3_NUM_CPUS),
> +                           qdev_get_gpio_in(DEVICE(&s->gic),
> +                                            ppibase + AW_H3_GIC_PPI_MAINT));
> +    }
> +
> +    for (i = 0; i < AW_H3_GIC_NUM_SPI; i++) {
> +        s->irq[i] = qdev_get_gpio_in(DEVICE(&s->gic), i);

Apparently we don't need the irq array in AwH3State, because ...

> +    }
> +
> +    /* Timer */
> +    object_property_set_bool(OBJECT(&s->timer), true, "realized", &err);
> +    if (err != NULL) {
> +        error_propagate(errp, err);
> +        return;
> +    }
> +    sysbusdev = SYS_BUS_DEVICE(&s->timer);
> +    sysbus_mmio_map(sysbusdev, 0, AW_H3_PIT_REG_BASE);
> +    sysbus_connect_irq(sysbusdev, 0, s->irq[AW_H3_GIC_SPI_TIMER0]);
> +    sysbus_connect_irq(sysbusdev, 1, s->irq[AW_H3_GIC_SPI_TIMER1]);

... we can call qdev_get_gpio_in() here directly.

> +
> +    /* SRAM */
> +    memory_region_init_ram(&s->sram_a1, OBJECT(dev), "sram A1",
> +                            AW_H3_SRAM_A1_SIZE, &error_fatal);
> +    memory_region_init_ram(&s->sram_a2, OBJECT(dev), "sram A2",
> +                            AW_H3_SRAM_A2_SIZE, &error_fatal);
> +    memory_region_init_ram(&s->sram_c, OBJECT(dev), "sram C",
> +                            AW_H3_SRAM_C_SIZE, &error_fatal);
> +    memory_region_add_subregion(get_system_memory(), AW_H3_SRAM_A1_BASE,
> +                                &s->sram_a1);
> +    memory_region_add_subregion(get_system_memory(), AW_H3_SRAM_A2_BASE,
> +                                &s->sram_a2);
> +    memory_region_add_subregion(get_system_memory(), AW_H3_SRAM_C_BASE,
> +                                &s->sram_c);
> +
> +    /* UART */
> +    if (serial_hd(0)) {
> +        serial_mm_init(get_system_memory(), AW_H3_UART0_REG_BASE, 2,
> +                       s->irq[AW_H3_GIC_SPI_UART0], 115200, serial_hd(0),

qdev_get_gpio_in() here too.

> +                       DEVICE_NATIVE_ENDIAN);
> +    }
> +
> +    /* Unimplemented devices */
> +    create_unimplemented_device("display-engine", AW_H3_DE_BASE, AW_H3_DE_SIZE);
> +    create_unimplemented_device("dma", AW_H3_DMA_BASE, AW_H3_DMA_SIZE);
> +    create_unimplemented_device("lcd0", AW_H3_LCD0_BASE, AW_H3_LCD0_SIZE);
> +    create_unimplemented_device("lcd1", AW_H3_LCD1_BASE, AW_H3_LCD1_SIZE);
> +    create_unimplemented_device("gpu", AW_H3_GPU_BASE, AW_H3_GPU_SIZE);
> +    create_unimplemented_device("hdmi", AW_H3_HDMI_BASE, AW_H3_HDMI_SIZE);
> +    create_unimplemented_device("rtc", AW_H3_RTC_BASE, AW_H3_RTC_SIZE);
> +    create_unimplemented_device("audio-codec", AW_H3_AC_BASE, AW_H3_AC_SIZE);
> +}
> +
> +static void aw_h3_class_init(ObjectClass *oc, void *data)
> +{
> +    DeviceClass *dc = DEVICE_CLASS(oc);
> +
> +    dc->realize = aw_h3_realize;
> +    /* Reason: uses serial_hds and nd_table */
> +    dc->user_creatable = false;
> +}
> +
> +static const TypeInfo aw_h3_type_info = {
> +    .name = TYPE_AW_H3,
> +    .parent = TYPE_DEVICE,
> +    .instance_size = sizeof(AwH3State),
> +    .instance_init = aw_h3_init,
> +    .class_init = aw_h3_class_init,
> +};
> +
> +static void aw_h3_register_types(void)
> +{
> +    type_register_static(&aw_h3_type_info);
> +}
> +
> +type_init(aw_h3_register_types)
> diff --git a/include/hw/arm/allwinner-h3.h b/include/hw/arm/allwinner-h3.h
> new file mode 100644
> index 0000000000..af368c2254
> --- /dev/null
> +++ b/include/hw/arm/allwinner-h3.h
> @@ -0,0 +1,118 @@
> +/*
> + * 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/>.
> + */
> +
> +#ifndef HW_ARM_ALLWINNER_H3_H
> +#define HW_ARM_ALLWINNER_H3_H
> +
> +#include "qemu/error-report.h"
> +#include "qemu/units.h"
> +#include "hw/char/serial.h"
> +#include "hw/arm/boot.h"
> +#include "hw/timer/allwinner-a10-pit.h"
> +#include "hw/intc/arm_gic.h"
> +#include "target/arm/cpu.h"
> +
> +#define AW_H3_SRAM_A1_BASE     (0x00000000)
> +#define AW_H3_SRAM_A2_BASE     (0x00044000)
> +#define AW_H3_SRAM_C_BASE      (0x00010000)
> +#define AW_H3_DE_BASE          (0x01000000)
> +#define AW_H3_SYSCON_BASE      (0x01c00000)
> +#define AW_H3_DMA_BASE         (0x01c02000)
> +#define AW_H3_LCD0_BASE        (0x01c0c000)
> +#define AW_H3_LCD1_BASE        (0x01c0d000)
> +#define AW_H3_SID_BASE         (0x01c14000)
> +#define AW_H3_CCU_BASE         (0x01c20000)
> +#define AW_H3_PIC_REG_BASE     (0x01c20400)
> +#define AW_H3_PIT_REG_BASE     (0x01c20c00)
> +#define AW_H3_AC_BASE          (0x01c22c00)
> +#define AW_H3_UART0_REG_BASE   (0x01c28000)
> +#define AW_H3_EMAC_BASE        (0x01c30000)
> +#define AW_H3_MMC0_BASE        (0x01c0f000)
> +#define AW_H3_EHCI0_BASE       (0x01c1a000)
> +#define AW_H3_OHCI0_BASE       (0x01c1a400)
> +#define AW_H3_EHCI1_BASE       (0x01c1b000)
> +#define AW_H3_OHCI1_BASE       (0x01c1b400)
> +#define AW_H3_EHCI2_BASE       (0x01c1c000)
> +#define AW_H3_OHCI2_BASE       (0x01c1c400)
> +#define AW_H3_EHCI3_BASE       (0x01c1d000)
> +#define AW_H3_OHCI3_BASE       (0x01c1d400)
> +#define AW_H3_GPU_BASE         (0x01c40000)
> +#define AW_H3_GIC_DIST_BASE    (0x01c81000)
> +#define AW_H3_GIC_CPU_BASE     (0x01c82000)
> +#define AW_H3_GIC_HYP_BASE     (0x01c84000)
> +#define AW_H3_GIC_VCPU_BASE    (0x01c86000)
> +#define AW_H3_HDMI_BASE        (0x01ee0000)
> +#define AW_H3_RTC_BASE         (0x01f00000)
> +#define AW_H3_CPUCFG_BASE      (0x01f01c00)
> +#define AW_H3_SDRAM_BASE       (0x40000000)
> +
> +#define AW_H3_SRAM_A1_SIZE     (64 * KiB)
> +#define AW_H3_SRAM_A2_SIZE     (32 * KiB)
> +#define AW_H3_SRAM_C_SIZE      (44 * KiB)
> +#define AW_H3_DE_SIZE          (4 * MiB)
> +#define AW_H3_DMA_SIZE         (4 * KiB)
> +#define AW_H3_LCD0_SIZE        (4 * KiB)
> +#define AW_H3_LCD1_SIZE        (4 * KiB)
> +#define AW_H3_GPU_SIZE         (64 * KiB)
> +#define AW_H3_HDMI_SIZE        (128 * KiB)
> +#define AW_H3_RTC_SIZE         (1 * KiB)
> +#define AW_H3_AC_SIZE          (2 * KiB)
> +
> +#define AW_H3_GIC_PPI_MAINT          (9)
> +#define AW_H3_GIC_PPI_ARM_HYPTIMER  (10)
> +#define AW_H3_GIC_PPI_ARM_VIRTTIMER (11)
> +#define AW_H3_GIC_PPI_ARM_SECTIMER  (13)
> +#define AW_H3_GIC_PPI_ARM_PHYSTIMER (14)
> +
> +#define AW_H3_GIC_SPI_UART0         (0)
> +#define AW_H3_GIC_SPI_TIMER0        (18)
> +#define AW_H3_GIC_SPI_TIMER1        (19)
> +#define AW_H3_GIC_SPI_MMC0          (60)
> +#define AW_H3_GIC_SPI_MMC1          (61)
> +#define AW_H3_GIC_SPI_MMC2          (62)
> +#define AW_H3_GIC_SPI_EHCI0         (72)
> +#define AW_H3_GIC_SPI_OHCI0         (73)
> +#define AW_H3_GIC_SPI_EHCI1         (74)
> +#define AW_H3_GIC_SPI_OHCI1         (75)
> +#define AW_H3_GIC_SPI_EHCI2         (76)
> +#define AW_H3_GIC_SPI_OHCI2         (77)
> +#define AW_H3_GIC_SPI_EHCI3         (78)
> +#define AW_H3_GIC_SPI_OHCI3         (79)
> +#define AW_H3_GIC_SPI_EMAC          (82)

I'd move half of the previous definitions into allwinner-h3.c, since 
they are only used there.

Also, I'd use an enum for the PPI/SPI.

> +
> +#define AW_H3_GIC_NUM_SPI           (128)
> +#define AW_H3_NUM_CPUS              (4)
> +
> +#define TYPE_AW_H3 "allwinner-h3"
> +#define AW_H3(obj) OBJECT_CHECK(AwH3State, (obj), TYPE_AW_H3)
> +
> +typedef struct AwH3State {
> +    /*< private >*/
> +    DeviceState parent_obj;
> +    /*< public >*/
> +
> +    qemu_irq irq[AW_H3_GIC_NUM_SPI];
> +    AwA10PITState timer;
> +    GICState gic;
> +    MemoryRegion sram_a1;
> +    MemoryRegion sram_a2;
> +    MemoryRegion sram_c;
> +} AwH3State;
> +
> +#endif
> 

Nice clean patch, for a first contribution :)



^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH 02/10] hw: arm: add Xunlong Orange Pi PC machine
  2019-12-04  9:03       ` Philippe Mathieu-Daudé
@ 2019-12-04 19:50         ` Niek Linnenbank
  0 siblings, 0 replies; 33+ messages in thread
From: Niek Linnenbank @ 2019-12-04 19:50 UTC (permalink / raw)
  To: Philippe Mathieu-Daudé
  Cc: b.galvani, Peter Maydell, qemu-arm, qemu-devel

[-- Attachment #1: Type: text/plain, Size: 888 bytes --]

On Wed, Dec 4, 2019 at 10:03 AM Philippe Mathieu-Daudé <philmd@redhat.com>
wrote:

> On 12/3/19 8:33 PM, Niek Linnenbank wrote:
> > Hello Philippe,
> >
> > Thanks for your quick review comments!
> > I'll start working on a v2 of the patches and include the changes you
> > suggested.
>
> Thanks, but I'd suggest to wait few more days to give time to others
> reviewers. Else having multiple versions of a big series reviewed at the
> same time is very confusing.
> I have other minor comments on others patches, but need to find the time
> to continue reviewing.
>
>
OK Philippe, I will follow your advise and wait a few more days before
submitting a new version.
I'll wait at least until you had a chance to review all the patches. I'm
new to the QEMU
community, so I will need to learn the process along the way.

Regards,
Niek





-- 
Niek Linnenbank

[-- Attachment #2: Type: text/html, Size: 1464 bytes --]

<div dir="ltr"><div dir="ltr"><br></div><br><div class="gmail_quote"><div dir="ltr" class="gmail_attr">On Wed, Dec 4, 2019 at 10:03 AM Philippe Mathieu-Daudé &lt;<a href="mailto:philmd@redhat.com">philmd@redhat.com</a>&gt; wrote:<br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">On 12/3/19 8:33 PM, Niek Linnenbank wrote:<br>
&gt; Hello Philippe,<br>
&gt; <br>
&gt; Thanks for your quick review comments!<br>
&gt; I&#39;ll start working on a v2 of the patches and include the changes you <br>
&gt; suggested.<br>
<br>
Thanks, but I&#39;d suggest to wait few more days to give time to others <br>
reviewers. Else having multiple versions of a big series reviewed at the <br>
same time is very confusing.<br>
I have other minor comments on others patches, but need to find the time <br>
to continue reviewing.<br>
<br>
</blockquote></div><div><br></div><div>OK Philippe, I will follow your advise and wait a few more days before submitting a new version.</div><div>I&#39;ll wait at least until you had a chance to review all the patches. I&#39;m new to the QEMU</div><div>community, so I will need to learn the process along the way.</div><div><br></div><div>Regards,</div><div>Niek<br></div><div><br></div><div><br></div><div><br></div><div><br></div><br>-- <br><div dir="ltr" class="gmail_signature"><div dir="ltr"><div>Niek Linnenbank<br><br></div></div></div></div>

^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH 04/10] arm: allwinner-h3: add USB host controller
  2019-12-04 16:11   ` Aleksandar Markovic
@ 2019-12-04 20:20     ` Niek Linnenbank
  0 siblings, 0 replies; 33+ messages in thread
From: Niek Linnenbank @ 2019-12-04 20:20 UTC (permalink / raw)
  To: Aleksandar Markovic; +Cc: b.galvani, peter.maydell, qemu-arm, qemu-devel

[-- Attachment #1: Type: text/plain, Size: 6328 bytes --]

On Wed, Dec 4, 2019 at 5:11 PM Aleksandar Markovic <
aleksandar.m.mail@gmail.com> wrote:

>
>
> On Monday, December 2, 2019, Niek Linnenbank <nieklinnenbank@gmail.com>
> wrote:
>
>> The Allwinner H3 System on Chip contains multiple USB 2.0 bus
>> connections which provide software access using the Enhanced
>> Host Controller Interface (EHCI) and Open Host Controller
>> Interface (OHCI) interfaces. This commit adds support for
>> both interfaces in the Allwinner H3 System on Chip.
>>
>> Signed-off-by: Niek Linnenbank <nieklinnenbank@gmail.com>
>> ---
>
>
> Niek, hi!
>
> I would like to clarify a detail here:
>
> The spec of the SoC enumerates (in 8.5.2.4. USB Host Register List) a
> number of registers for reading various USB-related states, but also for
> setting some of USB features.
>
> Does this series cover these registers, and interaction with them? If yes,
> how and where? If not, do you think it is not necessary at all? Or perhaps
> that it is a non-crucial limitation of this series?
>

Hello Aleksandar!

Very good question, I will try to explain what I did to support USB for the
Allwinner H3 emulation.
EHCI and OHCI are both standardized interfaces to the USB bus and both
provide their own standardized software interface.
Because they are standards, operatings system drivers can implement a
generic driver which uses the defined interface and
re-use it in multiple boards/platforms. Things that can be different
between boards are, for example the base address in
memory where the registers are provided.

In QEMU I found that both the OHCI and EHCI host controllers are already
emulated and used by other boards as well. For example,
you can find the OHCI registers from 8.5.2.4 implemented in the file
hw/usb/hcd-ohci.c:1515 in ohci_mem_read(). So for the Allwinner
H3 I simply had to define the base address for both controllers and create
the objects. At that point, the Linux kernel can access
the USB bus with the generic EHCI/OHCI platform drivers. In the Linux code,
you can see in the file ./arch/arm/boot/dts/sunxi-h3-h5.dtsi:281
the definitions named ehci0-ehci3 and ohci0-ohci3 where it specifies in the
device tree configuration to load the generic drivers.


>
> Thanks in advance, and congrats for your, it seems, first submission!
>
>
Thank you Aleksandar! Indeed, it is my first submission. I will do my best
to
update the patches to comply with the QEMU coding style and best practises.

Regards,
Niek


> Aleksandar
>
>
>  hw/arm/allwinner-h3.c    | 20 ++++++++++++++++++++
>>  hw/usb/hcd-ehci-sysbus.c | 17 +++++++++++++++++
>>  hw/usb/hcd-ehci.h        |  1 +
>>  3 files changed, 38 insertions(+)
>>
>> diff --git a/hw/arm/allwinner-h3.c b/hw/arm/allwinner-h3.c
>> index 5566e979ec..afeb49c0ac 100644
>> --- a/hw/arm/allwinner-h3.c
>> +++ b/hw/arm/allwinner-h3.c
>> @@ -26,6 +26,7 @@
>>  #include "hw/sysbus.h"
>>  #include "hw/arm/allwinner-h3.h"
>>  #include "hw/misc/unimp.h"
>> +#include "hw/usb/hcd-ehci.h"
>>  #include "sysemu/sysemu.h"
>>
>>  static void aw_h3_init(Object *obj)
>> @@ -183,6 +184,25 @@ static void aw_h3_realize(DeviceState *dev, Error
>> **errp)
>>      }
>>      sysbus_mmio_map(SYS_BUS_DEVICE(&s->ccu), 0, AW_H3_CCU_BASE);
>>
>> +    /* Universal Serial Bus */
>> +    sysbus_create_simple(TYPE_AW_H3_EHCI, AW_H3_EHCI0_BASE,
>> +                         s->irq[AW_H3_GIC_SPI_EHCI0]);
>> +    sysbus_create_simple(TYPE_AW_H3_EHCI, AW_H3_EHCI1_BASE,
>> +                         s->irq[AW_H3_GIC_SPI_EHCI1]);
>> +    sysbus_create_simple(TYPE_AW_H3_EHCI, AW_H3_EHCI2_BASE,
>> +                         s->irq[AW_H3_GIC_SPI_EHCI2]);
>> +    sysbus_create_simple(TYPE_AW_H3_EHCI, AW_H3_EHCI3_BASE,
>> +                         s->irq[AW_H3_GIC_SPI_EHCI3]);
>> +
>> +    sysbus_create_simple("sysbus-ohci", AW_H3_OHCI0_BASE,
>> +                         s->irq[AW_H3_GIC_SPI_OHCI0]);
>> +    sysbus_create_simple("sysbus-ohci", AW_H3_OHCI1_BASE,
>> +                         s->irq[AW_H3_GIC_SPI_OHCI1]);
>> +    sysbus_create_simple("sysbus-ohci", AW_H3_OHCI2_BASE,
>> +                         s->irq[AW_H3_GIC_SPI_OHCI2]);
>> +    sysbus_create_simple("sysbus-ohci", AW_H3_OHCI3_BASE,
>> +                         s->irq[AW_H3_GIC_SPI_OHCI3]);
>> +
>>      /* UART */
>>      if (serial_hd(0)) {
>>          serial_mm_init(get_system_memory(), AW_H3_UART0_REG_BASE, 2,
>> diff --git a/hw/usb/hcd-ehci-sysbus.c b/hw/usb/hcd-ehci-sysbus.c
>> index 020211fd10..174c3446ef 100644
>> --- a/hw/usb/hcd-ehci-sysbus.c
>> +++ b/hw/usb/hcd-ehci-sysbus.c
>> @@ -145,6 +145,22 @@ static const TypeInfo ehci_exynos4210_type_info = {
>>      .class_init    = ehci_exynos4210_class_init,
>>  };
>>
>> +static void ehci_aw_h3_class_init(ObjectClass *oc, void *data)
>> +{
>> +    SysBusEHCIClass *sec = SYS_BUS_EHCI_CLASS(oc);
>> +    DeviceClass *dc = DEVICE_CLASS(oc);
>> +
>> +    sec->capsbase = 0x0;
>> +    sec->opregbase = 0x10;
>> +    set_bit(DEVICE_CATEGORY_USB, dc->categories);
>> +}
>> +
>> +static const TypeInfo ehci_aw_h3_type_info = {
>> +    .name          = TYPE_AW_H3_EHCI,
>> +    .parent        = TYPE_SYS_BUS_EHCI,
>> +    .class_init    = ehci_aw_h3_class_init,
>> +};
>> +
>>  static void ehci_tegra2_class_init(ObjectClass *oc, void *data)
>>  {
>>      SysBusEHCIClass *sec = SYS_BUS_EHCI_CLASS(oc);
>> @@ -267,6 +283,7 @@ static void ehci_sysbus_register_types(void)
>>      type_register_static(&ehci_platform_type_info);
>>      type_register_static(&ehci_xlnx_type_info);
>>      type_register_static(&ehci_exynos4210_type_info);
>> +    type_register_static(&ehci_aw_h3_type_info);
>>      type_register_static(&ehci_tegra2_type_info);
>>      type_register_static(&ehci_ppc4xx_type_info);
>>      type_register_static(&ehci_fusbh200_type_info);
>> diff --git a/hw/usb/hcd-ehci.h b/hw/usb/hcd-ehci.h
>> index 0298238f0b..edb59311c4 100644
>> --- a/hw/usb/hcd-ehci.h
>> +++ b/hw/usb/hcd-ehci.h
>> @@ -342,6 +342,7 @@ typedef struct EHCIPCIState {
>>  #define TYPE_SYS_BUS_EHCI "sysbus-ehci-usb"
>>  #define TYPE_PLATFORM_EHCI "platform-ehci-usb"
>>  #define TYPE_EXYNOS4210_EHCI "exynos4210-ehci-usb"
>> +#define TYPE_AW_H3_EHCI "aw-h3-ehci-usb"
>>  #define TYPE_TEGRA2_EHCI "tegra2-ehci-usb"
>>  #define TYPE_PPC4xx_EHCI "ppc4xx-ehci-usb"
>>  #define TYPE_FUSBH200_EHCI "fusbh200-ehci-usb"
>> --
>> 2.17.1
>>
>>
>>

-- 
Niek Linnenbank

[-- Attachment #2: Type: text/html, Size: 8466 bytes --]

<div dir="ltr"><div dir="ltr"><br></div><br><div class="gmail_quote"><div dir="ltr" class="gmail_attr">On Wed, Dec 4, 2019 at 5:11 PM Aleksandar Markovic &lt;<a href="mailto:aleksandar.m.mail@gmail.com">aleksandar.m.mail@gmail.com</a>&gt; wrote:<br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><br><br>On Monday, December 2, 2019, Niek Linnenbank &lt;<a href="mailto:nieklinnenbank@gmail.com" target="_blank">nieklinnenbank@gmail.com</a>&gt; wrote:<br><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">The Allwinner H3 System on Chip contains multiple USB 2.0 bus<br>
connections which provide software access using the Enhanced<br>
Host Controller Interface (EHCI) and Open Host Controller<br>
Interface (OHCI) interfaces. This commit adds support for<br>
both interfaces in the Allwinner H3 System on Chip.<br>
<br>
Signed-off-by: Niek Linnenbank &lt;<a href="mailto:nieklinnenbank@gmail.com" target="_blank">nieklinnenbank@gmail.com</a>&gt;<br>
---</blockquote><div><br></div><div>Niek, hi!</div><div><br></div><div>I would like to clarify a detail here:</div><div><br></div><div>The spec of the SoC enumerates (in 8.5.2.4. USB Host Register List) a number of registers for reading various USB-related states, but also for setting some of USB features.</div><div> </div><div>Does this series cover these registers, and interaction with them? If yes, how and where? If not, do you think it is not necessary at all? Or perhaps that it is a non-crucial limitation of this series?</div></blockquote><div><br></div><div>Hello Aleksandar!</div><div><br></div><div>Very good question, I will try to explain what I did to support USB for the Allwinner H3 emulation.</div><div>EHCI and OHCI are both standardized interfaces to the USB bus and both provide their own standardized software interface.</div><div>Because they are standards, operatings system drivers can implement a generic driver which uses the defined interface and</div><div>re-use it in multiple boards/platforms. Things that can be different between boards are, for example the base address in</div><div>memory where the registers are provided.</div><div><br></div><div>In QEMU I found that both the OHCI and EHCI host controllers are already emulated and used by other boards as well. For example,</div><div>you can find the OHCI registers from 8.5.2.4 implemented in the file hw/usb/hcd-ohci.c:1515 in ohci_mem_read(). So for the Allwinner</div><div>H3 I simply had to define the base address for both controllers and create the objects. At that point, the Linux kernel can access</div><div>the USB bus with the generic EHCI/OHCI platform drivers. In the Linux code, you can see in the file ./arch/arm/boot/dts/sunxi-h3-h5.dtsi:281</div><div>the definitions named ehci0-ehci3 and ohci0-ohci3 where it specifies in the device tree configuration to load the generic drivers.</div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><div><br></div><div>Thanks in advance, and congrats for your, it seems, first submission!</div><div><br></div></blockquote><div><br></div><div>Thank you Aleksandar! Indeed, it is my first submission. I will do my best to</div><div>update the patches to comply with the QEMU coding style and best practises.</div><div><br></div><div>Regards,</div><div>Niek<br></div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><div></div><div>Aleksandar</div><div><br></div><div><br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
 hw/arm/allwinner-h3.c    | 20 ++++++++++++++++++++<br>
 hw/usb/hcd-ehci-sysbus.c | 17 +++++++++++++++++<br>
 hw/usb/hcd-ehci.h        |  1 +<br>
 3 files changed, 38 insertions(+)<br>
<br>
diff --git a/hw/arm/allwinner-h3.c b/hw/arm/allwinner-h3.c<br>
index 5566e979ec..afeb49c0ac 100644<br>
--- a/hw/arm/allwinner-h3.c<br>
+++ b/hw/arm/allwinner-h3.c<br>
@@ -26,6 +26,7 @@<br>
 #include &quot;hw/sysbus.h&quot;<br>
 #include &quot;hw/arm/allwinner-h3.h&quot;<br>
 #include &quot;hw/misc/unimp.h&quot;<br>
+#include &quot;hw/usb/hcd-ehci.h&quot;<br>
 #include &quot;sysemu/sysemu.h&quot;<br>
<br>
 static void aw_h3_init(Object *obj)<br>
@@ -183,6 +184,25 @@ static void aw_h3_realize(DeviceState *dev, Error **errp)<br>
     }<br>
     sysbus_mmio_map(SYS_BUS_DEVICE(&amp;s-&gt;ccu), 0, AW_H3_CCU_BASE);<br>
<br>
+    /* Universal Serial Bus */<br>
+    sysbus_create_simple(TYPE_AW_H3_EHCI, AW_H3_EHCI0_BASE,<br>
+                         s-&gt;irq[AW_H3_GIC_SPI_EHCI0]);<br>
+    sysbus_create_simple(TYPE_AW_H3_EHCI, AW_H3_EHCI1_BASE,<br>
+                         s-&gt;irq[AW_H3_GIC_SPI_EHCI1]);<br>
+    sysbus_create_simple(TYPE_AW_H3_EHCI, AW_H3_EHCI2_BASE,<br>
+                         s-&gt;irq[AW_H3_GIC_SPI_EHCI2]);<br>
+    sysbus_create_simple(TYPE_AW_H3_EHCI, AW_H3_EHCI3_BASE,<br>
+                         s-&gt;irq[AW_H3_GIC_SPI_EHCI3]);<br>
+<br>
+    sysbus_create_simple(&quot;sysbus-ohci&quot;, AW_H3_OHCI0_BASE,<br>
+                         s-&gt;irq[AW_H3_GIC_SPI_OHCI0]);<br>
+    sysbus_create_simple(&quot;sysbus-ohci&quot;, AW_H3_OHCI1_BASE,<br>
+                         s-&gt;irq[AW_H3_GIC_SPI_OHCI1]);<br>
+    sysbus_create_simple(&quot;sysbus-ohci&quot;, AW_H3_OHCI2_BASE,<br>
+                         s-&gt;irq[AW_H3_GIC_SPI_OHCI2]);<br>
+    sysbus_create_simple(&quot;sysbus-ohci&quot;, AW_H3_OHCI3_BASE,<br>
+                         s-&gt;irq[AW_H3_GIC_SPI_OHCI3]);<br>
+<br>
     /* UART */<br>
     if (serial_hd(0)) {<br>
         serial_mm_init(get_system_memory(), AW_H3_UART0_REG_BASE, 2,<br>
diff --git a/hw/usb/hcd-ehci-sysbus.c b/hw/usb/hcd-ehci-sysbus.c<br>
index 020211fd10..174c3446ef 100644<br>
--- a/hw/usb/hcd-ehci-sysbus.c<br>
+++ b/hw/usb/hcd-ehci-sysbus.c<br>
@@ -145,6 +145,22 @@ static const TypeInfo ehci_exynos4210_type_info = {<br>
     .class_init    = ehci_exynos4210_class_init,<br>
 };<br>
<br>
+static void ehci_aw_h3_class_init(ObjectClass *oc, void *data)<br>
+{<br>
+    SysBusEHCIClass *sec = SYS_BUS_EHCI_CLASS(oc);<br>
+    DeviceClass *dc = DEVICE_CLASS(oc);<br>
+<br>
+    sec-&gt;capsbase = 0x0;<br>
+    sec-&gt;opregbase = 0x10;<br>
+    set_bit(DEVICE_CATEGORY_USB, dc-&gt;categories);<br>
+}<br>
+<br>
+static const TypeInfo ehci_aw_h3_type_info = {<br>
+    .name          = TYPE_AW_H3_EHCI,<br>
+    .parent        = TYPE_SYS_BUS_EHCI,<br>
+    .class_init    = ehci_aw_h3_class_init,<br>
+};<br>
+<br>
 static void ehci_tegra2_class_init(ObjectClass *oc, void *data)<br>
 {<br>
     SysBusEHCIClass *sec = SYS_BUS_EHCI_CLASS(oc);<br>
@@ -267,6 +283,7 @@ static void ehci_sysbus_register_types(void)<br>
     type_register_static(&amp;ehci_platform_type_info);<br>
     type_register_static(&amp;ehci_xlnx_type_info);<br>
     type_register_static(&amp;ehci_exynos4210_type_info);<br>
+    type_register_static(&amp;ehci_aw_h3_type_info);<br>
     type_register_static(&amp;ehci_tegra2_type_info);<br>
     type_register_static(&amp;ehci_ppc4xx_type_info);<br>
     type_register_static(&amp;ehci_fusbh200_type_info);<br>
diff --git a/hw/usb/hcd-ehci.h b/hw/usb/hcd-ehci.h<br>
index 0298238f0b..edb59311c4 100644<br>
--- a/hw/usb/hcd-ehci.h<br>
+++ b/hw/usb/hcd-ehci.h<br>
@@ -342,6 +342,7 @@ typedef struct EHCIPCIState {<br>
 #define TYPE_SYS_BUS_EHCI &quot;sysbus-ehci-usb&quot;<br>
 #define TYPE_PLATFORM_EHCI &quot;platform-ehci-usb&quot;<br>
 #define TYPE_EXYNOS4210_EHCI &quot;exynos4210-ehci-usb&quot;<br>
+#define TYPE_AW_H3_EHCI &quot;aw-h3-ehci-usb&quot;<br>
 #define TYPE_TEGRA2_EHCI &quot;tegra2-ehci-usb&quot;<br>
 #define TYPE_PPC4xx_EHCI &quot;ppc4xx-ehci-usb&quot;<br>
 #define TYPE_FUSBH200_EHCI &quot;fusbh200-ehci-usb&quot;<br>
-- <br>
2.17.1<br>
<br>
<br>
</blockquote>
</blockquote></div><br clear="all"><br>-- <br><div dir="ltr" class="gmail_signature"><div dir="ltr"><div>Niek Linnenbank<br><br></div></div></div></div>

^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH 01/10] hw: arm: add Allwinner H3 System-on-Chip
  2019-12-04 16:53   ` Philippe Mathieu-Daudé
@ 2019-12-04 20:44     ` Niek Linnenbank
  0 siblings, 0 replies; 33+ messages in thread
From: Niek Linnenbank @ 2019-12-04 20:44 UTC (permalink / raw)
  To: Philippe Mathieu-Daudé
  Cc: b.galvani, Peter Maydell, qemu-arm, qemu-devel, Markus Armbruster

[-- Attachment #1: Type: text/plain, Size: 19559 bytes --]

Hello Philippe,

On Wed, Dec 4, 2019 at 5:53 PM Philippe Mathieu-Daudé <philmd@redhat.com>
wrote:

> Hi Niek,
>
> On 12/2/19 10:09 PM, Niek Linnenbank wrote:
> > The Allwinner H3 is a System on Chip containing four ARM Cortex A7
> > processor cores. Features and specifications include DDR2/DDR3 memory,
> > SD/MMC storage cards, 10/100/1000Mbit ethernet, USB 2.0, HDMI and
> > various I/O modules. This commit adds support for the Allwinner H3
> > System on Chip.
> >
> > Signed-off-by: Niek Linnenbank <nieklinnenbank@gmail.com>
> > ---
> >   MAINTAINERS                     |   7 ++
> >   default-configs/arm-softmmu.mak |   1 +
> >   hw/arm/Kconfig                  |   8 ++
> >   hw/arm/Makefile.objs            |   1 +
> >   hw/arm/allwinner-h3.c           | 215 ++++++++++++++++++++++++++++++++
> >   include/hw/arm/allwinner-h3.h   | 118 ++++++++++++++++++
> >   6 files changed, 350 insertions(+)
> >   create mode 100644 hw/arm/allwinner-h3.c
> >   create mode 100644 include/hw/arm/allwinner-h3.h
>
> Since your series changes various files, can you have a look at the
> scripts/git.orderfile file and setup it for your QEMU contributions?
>

OK, done! I didn't know such a script existed, thanks.
I ran this command in my local repository:
 $ git config diff.orderFile scripts/git.orderfile
It seems to work, when I re-generate the patches, the order of the diff is
different.



> >
> > diff --git a/MAINTAINERS b/MAINTAINERS
> > index 5e5e3e52d6..29c9936037 100644
> > --- a/MAINTAINERS
> > +++ b/MAINTAINERS
> > @@ -479,6 +479,13 @@ 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*
> > +
> >   ARM PrimeCell and CMSDK devices
> >   M: Peter Maydell <peter.maydell@linaro.org>
> >   L: qemu-arm@nongnu.org
> > diff --git a/default-configs/arm-softmmu.mak
> b/default-configs/arm-softmmu.mak
> > index 1f2e0e7fde..d75a239c2c 100644
> > --- a/default-configs/arm-softmmu.mak
> > +++ b/default-configs/arm-softmmu.mak
> > @@ -40,3 +40,4 @@ CONFIG_FSL_IMX25=y
> >   CONFIG_FSL_IMX7=y
> >   CONFIG_FSL_IMX6UL=y
> >   CONFIG_SEMIHOSTING=y
> > +CONFIG_ALLWINNER_H3=y
> > diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig
> > index c6e7782580..ebf8d2325f 100644
> > --- a/hw/arm/Kconfig
> > +++ b/hw/arm/Kconfig
> > @@ -291,6 +291,14 @@ config ALLWINNER_A10
> >       select SERIAL
> >       select UNIMP
> >
> > +config ALLWINNER_H3
> > +    bool
> > +    select ALLWINNER_A10_PIT
> > +    select SERIAL
> > +    select ARM_TIMER
> > +    select ARM_GIC
> > +    select UNIMP
> > +
> >   config RASPI
> >       bool
> >       select FRAMEBUFFER
> > diff --git a/hw/arm/Makefile.objs b/hw/arm/Makefile.objs
> > index fe749f65fd..956e496052 100644
> > --- a/hw/arm/Makefile.objs
> > +++ b/hw/arm/Makefile.objs
> > @@ -34,6 +34,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
> >   obj-$(CONFIG_RASPI) += bcm2835_peripherals.o bcm2836.o raspi.o
> >   obj-$(CONFIG_STM32F205_SOC) += stm32f205_soc.o
> >   obj-$(CONFIG_XLNX_ZYNQMP_ARM) += xlnx-zynqmp.o xlnx-zcu102.o
> > diff --git a/hw/arm/allwinner-h3.c b/hw/arm/allwinner-h3.c
> > new file mode 100644
> > index 0000000000..470fdfebef
> > --- /dev/null
> > +++ b/hw/arm/allwinner-h3.c
> > @@ -0,0 +1,215 @@
> > +/*
> > + * 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/module.h"
> > +#include "qemu/units.h"
> > +#include "cpu.h"
> > +#include "hw/sysbus.h"
> > +#include "hw/arm/allwinner-h3.h"
> > +#include "hw/misc/unimp.h"
> > +#include "sysemu/sysemu.h"
> > +
> > +static void aw_h3_init(Object *obj)
> > +{
> > +    AwH3State *s = AW_H3(obj);
> > +
> > +    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);
> > +}
> > +
> > +static void aw_h3_realize(DeviceState *dev, Error **errp)
> > +{
> > +    AwH3State *s = AW_H3(dev);
> > +    SysBusDevice *sysbusdev = NULL;
> > +    Error *err = NULL;
> > +    unsigned i = 0;
> > +
> > +    /* CPUs */
> > +    for (i = 0; i < AW_H3_NUM_CPUS; i++) {
>
> In https://www.mail-archive.com/qemu-devel@nongnu.org/msg662942.html
> Markus noted some incorrect pattern, and apparently you inherited it.
> You should initialize 'err' in the loop.
>
> > +        Object *cpuobj = object_new(ARM_CPU_TYPE_NAME("cortex-a7"));
> > +        CPUState *cpustate = CPU(cpuobj);
>
> We loose access to the CPUs. Can you use an array of AW_H3_NUM_CPUS cpus
> in AwH3State?
>
> > +
> > +        /* Set the proper CPU index */
> > +        cpustate->cpu_index = i;
> > +
> > +        /* Provide Power State Coordination Interface */
> > +        object_property_set_int(cpuobj, QEMU_PSCI_CONDUIT_HVC,
> > +                                "psci-conduit", &error_abort);
>
> Here you use the error_abort shortcut.
>
> > +
> > +        /* Disable secondary CPUs */
> > +        object_property_set_bool(cpuobj, i > 0, "start-powered-off",
> &err);
> > +        if (err != NULL) {
> > +            error_propagate(errp, err);
> > +            return;
>
> Here you return.
>
> > +        }
> > +
> > +        /* All exception levels required */
> > +        object_property_set_bool(cpuobj,
> > +                                 true, "has_el3", NULL);
> > +        object_property_set_bool(cpuobj,
> > +                                 true, "has_el2", NULL);
>
> Here you don't use error.
>
> Cc'ing Markus who is the expert, since he might have better suggestions.
>
> This function is called before the machine starts, and we are not
> handling with user-provided configurations, so I'd say using
> &error_abort in all places is OK.
>
> > +
> > +        /* Mark realized */
> > +        object_property_set_bool(cpuobj, true, "realized", &err);
> > +        if (err != NULL) {
> > +            error_propagate(errp, err);
> > +            return;
> > +        }
> > +        object_unref(cpuobj);
> > +    }
> > +
> > +    /* 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);
> > +
> > +    object_property_set_bool(OBJECT(&s->gic), true, "realized", &err);
>
> Why change API? Can we use qdev_init_nofail() instead?
>

> > +    if (err) {
> > +        error_propagate(errp, err);
> > +        return;
> > +    }
> > +
> > +    sysbusdev = SYS_BUS_DEVICE(&s->gic);
> > +    sysbus_mmio_map(sysbusdev, 0, AW_H3_GIC_DIST_BASE);
> > +    sysbus_mmio_map(sysbusdev, 1, AW_H3_GIC_CPU_BASE);
> > +    sysbus_mmio_map(sysbusdev, 2, AW_H3_GIC_HYP_BASE);
> > +    sysbus_mmio_map(sysbusdev, 3, AW_H3_GIC_VCPU_BASE);
> > +
> > +    /*
> > +     * 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(qemu_get_cpu(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_ARM_PHYSTIMER,
> > +            [GTIMER_VIRT] = AW_H3_GIC_PPI_ARM_VIRTTIMER,
> > +            [GTIMER_HYP]  = AW_H3_GIC_PPI_ARM_HYPTIMER,
> > +            [GTIMER_SEC]  = AW_H3_GIC_PPI_ARM_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(sysbusdev, i,
> > +                           qdev_get_gpio_in(cpudev, ARM_CPU_IRQ));
> > +        sysbus_connect_irq(sysbusdev, i + AW_H3_NUM_CPUS,
> > +                           qdev_get_gpio_in(cpudev, ARM_CPU_FIQ));
> > +        sysbus_connect_irq(sysbusdev, i + (2 * AW_H3_NUM_CPUS),
> > +                           qdev_get_gpio_in(cpudev, ARM_CPU_VIRQ));
> > +        sysbus_connect_irq(sysbusdev, i + (3 * AW_H3_NUM_CPUS),
> > +                           qdev_get_gpio_in(cpudev, ARM_CPU_VFIQ));
> > +
> > +        /* GIC maintenance signal */
> > +        sysbus_connect_irq(sysbusdev, i + (4 * AW_H3_NUM_CPUS),
> > +                           qdev_get_gpio_in(DEVICE(&s->gic),
> > +                                            ppibase +
> AW_H3_GIC_PPI_MAINT));
> > +    }
> > +
> > +    for (i = 0; i < AW_H3_GIC_NUM_SPI; i++) {
> > +        s->irq[i] = qdev_get_gpio_in(DEVICE(&s->gic), i);
>
> Apparently we don't need the irq array in AwH3State, because ...
>
> > +    }
> > +
> > +    /* Timer */
> > +    object_property_set_bool(OBJECT(&s->timer), true, "realized", &err);
> > +    if (err != NULL) {
> > +        error_propagate(errp, err);
> > +        return;
> > +    }
> > +    sysbusdev = SYS_BUS_DEVICE(&s->timer);
> > +    sysbus_mmio_map(sysbusdev, 0, AW_H3_PIT_REG_BASE);
> > +    sysbus_connect_irq(sysbusdev, 0, s->irq[AW_H3_GIC_SPI_TIMER0]);
> > +    sysbus_connect_irq(sysbusdev, 1, s->irq[AW_H3_GIC_SPI_TIMER1]);
>
> ... we can call qdev_get_gpio_in() here directly.
>
> > +
> > +    /* SRAM */
> > +    memory_region_init_ram(&s->sram_a1, OBJECT(dev), "sram A1",
> > +                            AW_H3_SRAM_A1_SIZE, &error_fatal);
> > +    memory_region_init_ram(&s->sram_a2, OBJECT(dev), "sram A2",
> > +                            AW_H3_SRAM_A2_SIZE, &error_fatal);
> > +    memory_region_init_ram(&s->sram_c, OBJECT(dev), "sram C",
> > +                            AW_H3_SRAM_C_SIZE, &error_fatal);
> > +    memory_region_add_subregion(get_system_memory(), AW_H3_SRAM_A1_BASE,
> > +                                &s->sram_a1);
> > +    memory_region_add_subregion(get_system_memory(), AW_H3_SRAM_A2_BASE,
> > +                                &s->sram_a2);
> > +    memory_region_add_subregion(get_system_memory(), AW_H3_SRAM_C_BASE,
> > +                                &s->sram_c);
> > +
> > +    /* UART */
> > +    if (serial_hd(0)) {
> > +        serial_mm_init(get_system_memory(), AW_H3_UART0_REG_BASE, 2,
> > +                       s->irq[AW_H3_GIC_SPI_UART0], 115200,
> serial_hd(0),
>
> qdev_get_gpio_in() here too.
>
> > +                       DEVICE_NATIVE_ENDIAN);
> > +    }
> > +
> > +    /* Unimplemented devices */
> > +    create_unimplemented_device("display-engine", AW_H3_DE_BASE,
> AW_H3_DE_SIZE);
> > +    create_unimplemented_device("dma", AW_H3_DMA_BASE, AW_H3_DMA_SIZE);
> > +    create_unimplemented_device("lcd0", AW_H3_LCD0_BASE,
> AW_H3_LCD0_SIZE);
> > +    create_unimplemented_device("lcd1", AW_H3_LCD1_BASE,
> AW_H3_LCD1_SIZE);
> > +    create_unimplemented_device("gpu", AW_H3_GPU_BASE, AW_H3_GPU_SIZE);
> > +    create_unimplemented_device("hdmi", AW_H3_HDMI_BASE,
> AW_H3_HDMI_SIZE);
> > +    create_unimplemented_device("rtc", AW_H3_RTC_BASE, AW_H3_RTC_SIZE);
> > +    create_unimplemented_device("audio-codec", AW_H3_AC_BASE,
> AW_H3_AC_SIZE);
> > +}
> > +
> > +static void aw_h3_class_init(ObjectClass *oc, void *data)
> > +{
> > +    DeviceClass *dc = DEVICE_CLASS(oc);
> > +
> > +    dc->realize = aw_h3_realize;
> > +    /* Reason: uses serial_hds and nd_table */
> > +    dc->user_creatable = false;
> > +}
> > +
> > +static const TypeInfo aw_h3_type_info = {
> > +    .name = TYPE_AW_H3,
> > +    .parent = TYPE_DEVICE,
> > +    .instance_size = sizeof(AwH3State),
> > +    .instance_init = aw_h3_init,
> > +    .class_init = aw_h3_class_init,
> > +};
> > +
> > +static void aw_h3_register_types(void)
> > +{
> > +    type_register_static(&aw_h3_type_info);
> > +}
> > +
> > +type_init(aw_h3_register_types)
> > diff --git a/include/hw/arm/allwinner-h3.h
> b/include/hw/arm/allwinner-h3.h
> > new file mode 100644
> > index 0000000000..af368c2254
> > --- /dev/null
> > +++ b/include/hw/arm/allwinner-h3.h
> > @@ -0,0 +1,118 @@
> > +/*
> > + * 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/
> >.
> > + */
> > +
> > +#ifndef HW_ARM_ALLWINNER_H3_H
> > +#define HW_ARM_ALLWINNER_H3_H
> > +
> > +#include "qemu/error-report.h"
> > +#include "qemu/units.h"
> > +#include "hw/char/serial.h"
> > +#include "hw/arm/boot.h"
> > +#include "hw/timer/allwinner-a10-pit.h"
> > +#include "hw/intc/arm_gic.h"
> > +#include "target/arm/cpu.h"
> > +
> > +#define AW_H3_SRAM_A1_BASE     (0x00000000)
> > +#define AW_H3_SRAM_A2_BASE     (0x00044000)
> > +#define AW_H3_SRAM_C_BASE      (0x00010000)
> > +#define AW_H3_DE_BASE          (0x01000000)
> > +#define AW_H3_SYSCON_BASE      (0x01c00000)
> > +#define AW_H3_DMA_BASE         (0x01c02000)
> > +#define AW_H3_LCD0_BASE        (0x01c0c000)
> > +#define AW_H3_LCD1_BASE        (0x01c0d000)
> > +#define AW_H3_SID_BASE         (0x01c14000)
> > +#define AW_H3_CCU_BASE         (0x01c20000)
> > +#define AW_H3_PIC_REG_BASE     (0x01c20400)
> > +#define AW_H3_PIT_REG_BASE     (0x01c20c00)
> > +#define AW_H3_AC_BASE          (0x01c22c00)
> > +#define AW_H3_UART0_REG_BASE   (0x01c28000)
> > +#define AW_H3_EMAC_BASE        (0x01c30000)
> > +#define AW_H3_MMC0_BASE        (0x01c0f000)
> > +#define AW_H3_EHCI0_BASE       (0x01c1a000)
> > +#define AW_H3_OHCI0_BASE       (0x01c1a400)
> > +#define AW_H3_EHCI1_BASE       (0x01c1b000)
> > +#define AW_H3_OHCI1_BASE       (0x01c1b400)
> > +#define AW_H3_EHCI2_BASE       (0x01c1c000)
> > +#define AW_H3_OHCI2_BASE       (0x01c1c400)
> > +#define AW_H3_EHCI3_BASE       (0x01c1d000)
> > +#define AW_H3_OHCI3_BASE       (0x01c1d400)
> > +#define AW_H3_GPU_BASE         (0x01c40000)
> > +#define AW_H3_GIC_DIST_BASE    (0x01c81000)
> > +#define AW_H3_GIC_CPU_BASE     (0x01c82000)
> > +#define AW_H3_GIC_HYP_BASE     (0x01c84000)
> > +#define AW_H3_GIC_VCPU_BASE    (0x01c86000)
> > +#define AW_H3_HDMI_BASE        (0x01ee0000)
> > +#define AW_H3_RTC_BASE         (0x01f00000)
> > +#define AW_H3_CPUCFG_BASE      (0x01f01c00)
> > +#define AW_H3_SDRAM_BASE       (0x40000000)
> > +
> > +#define AW_H3_SRAM_A1_SIZE     (64 * KiB)
> > +#define AW_H3_SRAM_A2_SIZE     (32 * KiB)
> > +#define AW_H3_SRAM_C_SIZE      (44 * KiB)
> > +#define AW_H3_DE_SIZE          (4 * MiB)
> > +#define AW_H3_DMA_SIZE         (4 * KiB)
> > +#define AW_H3_LCD0_SIZE        (4 * KiB)
> > +#define AW_H3_LCD1_SIZE        (4 * KiB)
> > +#define AW_H3_GPU_SIZE         (64 * KiB)
> > +#define AW_H3_HDMI_SIZE        (128 * KiB)
> > +#define AW_H3_RTC_SIZE         (1 * KiB)
> > +#define AW_H3_AC_SIZE          (2 * KiB)
> > +
> > +#define AW_H3_GIC_PPI_MAINT          (9)
> > +#define AW_H3_GIC_PPI_ARM_HYPTIMER  (10)
> > +#define AW_H3_GIC_PPI_ARM_VIRTTIMER (11)
> > +#define AW_H3_GIC_PPI_ARM_SECTIMER  (13)
> > +#define AW_H3_GIC_PPI_ARM_PHYSTIMER (14)
> > +
> > +#define AW_H3_GIC_SPI_UART0         (0)
> > +#define AW_H3_GIC_SPI_TIMER0        (18)
> > +#define AW_H3_GIC_SPI_TIMER1        (19)
> > +#define AW_H3_GIC_SPI_MMC0          (60)
> > +#define AW_H3_GIC_SPI_MMC1          (61)
> > +#define AW_H3_GIC_SPI_MMC2          (62)
> > +#define AW_H3_GIC_SPI_EHCI0         (72)
> > +#define AW_H3_GIC_SPI_OHCI0         (73)
> > +#define AW_H3_GIC_SPI_EHCI1         (74)
> > +#define AW_H3_GIC_SPI_OHCI1         (75)
> > +#define AW_H3_GIC_SPI_EHCI2         (76)
> > +#define AW_H3_GIC_SPI_OHCI2         (77)
> > +#define AW_H3_GIC_SPI_EHCI3         (78)
> > +#define AW_H3_GIC_SPI_OHCI3         (79)
> > +#define AW_H3_GIC_SPI_EMAC          (82)
>
> I'd move half of the previous definitions into allwinner-h3.c, since
> they are only used there.
>
> Indeed, you are right, I'll move them.

Also, I'd use an enum for the PPI/SPI.
>

Thanks, I will process all of your comments above for the next patch
version.


>
>
> +
> > +#define AW_H3_GIC_NUM_SPI           (128)
> > +#define AW_H3_NUM_CPUS              (4)
> > +
> > +#define TYPE_AW_H3 "allwinner-h3"
> > +#define AW_H3(obj) OBJECT_CHECK(AwH3State, (obj), TYPE_AW_H3)
> > +
> > +typedef struct AwH3State {
> > +    /*< private >*/
> > +    DeviceState parent_obj;
> > +    /*< public >*/
> > +
> > +    qemu_irq irq[AW_H3_GIC_NUM_SPI];
> > +    AwA10PITState timer;
> > +    GICState gic;
> > +    MemoryRegion sram_a1;
> > +    MemoryRegion sram_a2;
> > +    MemoryRegion sram_c;
> > +} AwH3State;
> > +
> > +#endif
> >
>
> Nice clean patch, for a first contribution :)
>

Thank you Philippe!

Regards,
Niek

-- 
Niek Linnenbank

[-- Attachment #2: Type: text/html, Size: 25602 bytes --]

<div dir="ltr"><div>Hello Philippe,<br></div><br><div class="gmail_quote"><div dir="ltr" class="gmail_attr">On Wed, Dec 4, 2019 at 5:53 PM Philippe Mathieu-Daudé &lt;<a href="mailto:philmd@redhat.com">philmd@redhat.com</a>&gt; wrote:<br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">Hi Niek,<br>
<br>
On 12/2/19 10:09 PM, Niek Linnenbank wrote:<br>
&gt; The Allwinner H3 is a System on Chip containing four ARM Cortex A7<br>
&gt; processor cores. Features and specifications include DDR2/DDR3 memory,<br>
&gt; SD/MMC storage cards, 10/100/1000Mbit ethernet, USB 2.0, HDMI and<br>
&gt; various I/O modules. This commit adds support for the Allwinner H3<br>
&gt; System on Chip.<br>
&gt; <br>
&gt; Signed-off-by: Niek Linnenbank &lt;<a href="mailto:nieklinnenbank@gmail.com" target="_blank">nieklinnenbank@gmail.com</a>&gt;<br>
&gt; ---<br>
&gt;   MAINTAINERS                     |   7 ++<br>
&gt;   default-configs/arm-softmmu.mak |   1 +<br>
&gt;   hw/arm/Kconfig                  |   8 ++<br>
&gt;   hw/arm/Makefile.objs            |   1 +<br>
&gt;   hw/arm/allwinner-h3.c           | 215 ++++++++++++++++++++++++++++++++<br>
&gt;   include/hw/arm/allwinner-h3.h   | 118 ++++++++++++++++++<br>
&gt;   6 files changed, 350 insertions(+)<br>
&gt;   create mode 100644 hw/arm/allwinner-h3.c<br>
&gt;   create mode 100644 include/hw/arm/allwinner-h3.h<br>
<br>
Since your series changes various files, can you have a look at the <br>
scripts/git.orderfile file and setup it for your QEMU contributions?<br></blockquote><div><br></div><div>OK, done! I didn&#39;t know such a script existed, thanks.</div><div>I ran this command in my local repository:</div><div> $ git config diff.orderFile scripts/git.orderfile<br></div><div>It seems to work, when I re-generate the patches, the order of the diff is different.<br></div><div> </div><div><br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
<br>
&gt; <br>
&gt; diff --git a/MAINTAINERS b/MAINTAINERS<br>
&gt; index 5e5e3e52d6..29c9936037 100644<br>
&gt; --- a/MAINTAINERS<br>
&gt; +++ b/MAINTAINERS<br>
&gt; @@ -479,6 +479,13 @@ F: hw/*/allwinner*<br>
&gt;   F: include/hw/*/allwinner*<br>
&gt;   F: hw/arm/cubieboard.c<br>
&gt;   <br>
&gt; +Allwinner-h3<br>
&gt; +M: Niek Linnenbank &lt;<a href="mailto:nieklinnenbank@gmail.com" target="_blank">nieklinnenbank@gmail.com</a>&gt;<br>
&gt; +L: <a href="mailto:qemu-arm@nongnu.org" target="_blank">qemu-arm@nongnu.org</a><br>
&gt; +S: Maintained<br>
&gt; +F: hw/*/allwinner-h3*<br>
&gt; +F: include/hw/*/allwinner-h3*<br>
&gt; +<br>
&gt;   ARM PrimeCell and CMSDK devices<br>
&gt;   M: Peter Maydell &lt;<a href="mailto:peter.maydell@linaro.org" target="_blank">peter.maydell@linaro.org</a>&gt;<br>
&gt;   L: <a href="mailto:qemu-arm@nongnu.org" target="_blank">qemu-arm@nongnu.org</a><br>
&gt; diff --git a/default-configs/arm-softmmu.mak b/default-configs/arm-softmmu.mak<br>
&gt; index 1f2e0e7fde..d75a239c2c 100644<br>
&gt; --- a/default-configs/arm-softmmu.mak<br>
&gt; +++ b/default-configs/arm-softmmu.mak<br>
&gt; @@ -40,3 +40,4 @@ CONFIG_FSL_IMX25=y<br>
&gt;   CONFIG_FSL_IMX7=y<br>
&gt;   CONFIG_FSL_IMX6UL=y<br>
&gt;   CONFIG_SEMIHOSTING=y<br>
&gt; +CONFIG_ALLWINNER_H3=y<br>
&gt; diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig<br>
&gt; index c6e7782580..ebf8d2325f 100644<br>
&gt; --- a/hw/arm/Kconfig<br>
&gt; +++ b/hw/arm/Kconfig<br>
&gt; @@ -291,6 +291,14 @@ config ALLWINNER_A10<br>
&gt;       select SERIAL<br>
&gt;       select UNIMP<br>
&gt;   <br>
&gt; +config ALLWINNER_H3<br>
&gt; +    bool<br>
&gt; +    select ALLWINNER_A10_PIT<br>
&gt; +    select SERIAL<br>
&gt; +    select ARM_TIMER<br>
&gt; +    select ARM_GIC<br>
&gt; +    select UNIMP<br>
&gt; +<br>
&gt;   config RASPI<br>
&gt;       bool<br>
&gt;       select FRAMEBUFFER<br>
&gt; diff --git a/hw/arm/Makefile.objs b/hw/arm/Makefile.objs<br>
&gt; index fe749f65fd..956e496052 100644<br>
&gt; --- a/hw/arm/Makefile.objs<br>
&gt; +++ b/hw/arm/Makefile.objs<br>
&gt; @@ -34,6 +34,7 @@ obj-$(CONFIG_DIGIC) += digic.o<br>
&gt;   obj-$(CONFIG_OMAP) += omap1.o omap2.o<br>
&gt;   obj-$(CONFIG_STRONGARM) += strongarm.o<br>
&gt;   obj-$(CONFIG_ALLWINNER_A10) += allwinner-a10.o cubieboard.o<br>
&gt; +obj-$(CONFIG_ALLWINNER_H3) += allwinner-h3.o<br>
&gt;   obj-$(CONFIG_RASPI) += bcm2835_peripherals.o bcm2836.o raspi.o<br>
&gt;   obj-$(CONFIG_STM32F205_SOC) += stm32f205_soc.o<br>
&gt;   obj-$(CONFIG_XLNX_ZYNQMP_ARM) += xlnx-zynqmp.o xlnx-zcu102.o<br>
&gt; diff --git a/hw/arm/allwinner-h3.c b/hw/arm/allwinner-h3.c<br>
&gt; new file mode 100644<br>
&gt; index 0000000000..470fdfebef<br>
&gt; --- /dev/null<br>
&gt; +++ b/hw/arm/allwinner-h3.c<br>
&gt; @@ -0,0 +1,215 @@<br>
&gt; +/*<br>
&gt; + * Allwinner H3 System on Chip emulation<br>
&gt; + *<br>
&gt; + * Copyright (C) 2019 Niek Linnenbank &lt;<a href="mailto:nieklinnenbank@gmail.com" target="_blank">nieklinnenbank@gmail.com</a>&gt;<br>
&gt; + *<br>
&gt; + * This program is free software: you can redistribute it and/or modify<br>
&gt; + * it under the terms of the GNU General Public License as published by<br>
&gt; + * the Free Software Foundation, either version 2 of the License, or<br>
&gt; + * (at your option) any later version.<br>
&gt; + *<br>
&gt; + * This program is distributed in the hope that it will be useful,<br>
&gt; + * but WITHOUT ANY WARRANTY; without even the implied warranty of<br>
&gt; + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the<br>
&gt; + * GNU General Public License for more details.<br>
&gt; + *<br>
&gt; + * You should have received a copy of the GNU General Public License<br>
&gt; + * along with this program.  If not, see &lt;<a href="http://www.gnu.org/licenses/" rel="noreferrer" target="_blank">http://www.gnu.org/licenses/</a>&gt;.<br>
&gt; + */<br>
&gt; +<br>
&gt; +#include &quot;qemu/osdep.h&quot;<br>
&gt; +#include &quot;exec/address-spaces.h&quot;<br>
&gt; +#include &quot;qapi/error.h&quot;<br>
&gt; +#include &quot;qemu/module.h&quot;<br>
&gt; +#include &quot;qemu/units.h&quot;<br>
&gt; +#include &quot;cpu.h&quot;<br>
&gt; +#include &quot;hw/sysbus.h&quot;<br>
&gt; +#include &quot;hw/arm/allwinner-h3.h&quot;<br>
&gt; +#include &quot;hw/misc/unimp.h&quot;<br>
&gt; +#include &quot;sysemu/sysemu.h&quot;<br>
&gt; +<br>
&gt; +static void aw_h3_init(Object *obj)<br>
&gt; +{<br>
&gt; +    AwH3State *s = AW_H3(obj);<br>
&gt; +<br>
&gt; +    sysbus_init_child_obj(obj, &quot;gic&quot;, &amp;s-&gt;gic, sizeof(s-&gt;gic),<br>
&gt; +                          TYPE_ARM_GIC);<br>
&gt; +<br>
&gt; +    sysbus_init_child_obj(obj, &quot;timer&quot;, &amp;s-&gt;timer, sizeof(s-&gt;timer),<br>
&gt; +                          TYPE_AW_A10_PIT);<br>
&gt; +}<br>
&gt; +<br>
&gt; +static void aw_h3_realize(DeviceState *dev, Error **errp)<br>
&gt; +{<br>
&gt; +    AwH3State *s = AW_H3(dev);<br>
&gt; +    SysBusDevice *sysbusdev = NULL;<br>
&gt; +    Error *err = NULL;<br>
&gt; +    unsigned i = 0;<br>
&gt; +<br>
&gt; +    /* CPUs */<br>
&gt; +    for (i = 0; i &lt; AW_H3_NUM_CPUS; i++) {<br>
<br>
In <a href="https://www.mail-archive.com/qemu-devel@nongnu.org/msg662942.html" rel="noreferrer" target="_blank">https://www.mail-archive.com/qemu-devel@nongnu.org/msg662942.html</a><br>
Markus noted some incorrect pattern, and apparently you inherited it.<br>
You should initialize &#39;err&#39; in the loop.<br>
<br>
&gt; +        Object *cpuobj = object_new(ARM_CPU_TYPE_NAME(&quot;cortex-a7&quot;));<br>
&gt; +        CPUState *cpustate = CPU(cpuobj);<br>
<br>
We loose access to the CPUs. Can you use an array of AW_H3_NUM_CPUS cpus <br>
in AwH3State?<br>
<br>
&gt; +<br>
&gt; +        /* Set the proper CPU index */<br>
&gt; +        cpustate-&gt;cpu_index = i;<br>
&gt; +<br>
&gt; +        /* Provide Power State Coordination Interface */<br>
&gt; +        object_property_set_int(cpuobj, QEMU_PSCI_CONDUIT_HVC,<br>
&gt; +                                &quot;psci-conduit&quot;, &amp;error_abort);<br>
<br>
Here you use the error_abort shortcut.<br>
<br>
&gt; +<br>
&gt; +        /* Disable secondary CPUs */<br>
&gt; +        object_property_set_bool(cpuobj, i &gt; 0, &quot;start-powered-off&quot;, &amp;err);<br>
&gt; +        if (err != NULL) {<br>
&gt; +            error_propagate(errp, err);<br>
&gt; +            return;<br>
<br>
Here you return.<br>
<br>
&gt; +        }<br>
&gt; +<br>
&gt; +        /* All exception levels required */<br>
&gt; +        object_property_set_bool(cpuobj,<br>
&gt; +                                 true, &quot;has_el3&quot;, NULL);<br>
&gt; +        object_property_set_bool(cpuobj,<br>
&gt; +                                 true, &quot;has_el2&quot;, NULL);<br>
<br>
Here you don&#39;t use error.<br>
<br>
Cc&#39;ing Markus who is the expert, since he might have better suggestions.<br>
<br>
This function is called before the machine starts, and we are not <br>
handling with user-provided configurations, so I&#39;d say using <br>
&amp;error_abort in all places is OK.<br>
<br>
&gt; +<br>
&gt; +        /* Mark realized */<br>
&gt; +        object_property_set_bool(cpuobj, true, &quot;realized&quot;, &amp;err);<br>
&gt; +        if (err != NULL) {<br>
&gt; +            error_propagate(errp, err);<br>
&gt; +            return;<br>
&gt; +        }<br>
&gt; +        object_unref(cpuobj);<br>
&gt; +    }<br>
&gt; +<br>
&gt; +    /* Generic Interrupt Controller */<br>
&gt; +    qdev_prop_set_uint32(DEVICE(&amp;s-&gt;gic), &quot;num-irq&quot;, AW_H3_GIC_NUM_SPI +<br>
&gt; +                                                     GIC_INTERNAL);<br>
&gt; +    qdev_prop_set_uint32(DEVICE(&amp;s-&gt;gic), &quot;revision&quot;, 2);<br>
&gt; +    qdev_prop_set_uint32(DEVICE(&amp;s-&gt;gic), &quot;num-cpu&quot;, AW_H3_NUM_CPUS);<br>
&gt; +    qdev_prop_set_bit(DEVICE(&amp;s-&gt;gic), &quot;has-security-extensions&quot;, false);<br>
&gt; +    qdev_prop_set_bit(DEVICE(&amp;s-&gt;gic), &quot;has-virtualization-extensions&quot;, true);<br>
&gt; +<br>
&gt; +    object_property_set_bool(OBJECT(&amp;s-&gt;gic), true, &quot;realized&quot;, &amp;err);<br>
<br>
Why change API? Can we use qdev_init_nofail() instead?<br></blockquote><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
<br>
&gt; +    if (err) {<br>
&gt; +        error_propagate(errp, err);<br>
&gt; +        return;<br>
&gt; +    }<br>
&gt; +<br>
&gt; +    sysbusdev = SYS_BUS_DEVICE(&amp;s-&gt;gic);<br>
&gt; +    sysbus_mmio_map(sysbusdev, 0, AW_H3_GIC_DIST_BASE);<br>
&gt; +    sysbus_mmio_map(sysbusdev, 1, AW_H3_GIC_CPU_BASE);<br>
&gt; +    sysbus_mmio_map(sysbusdev, 2, AW_H3_GIC_HYP_BASE);<br>
&gt; +    sysbus_mmio_map(sysbusdev, 3, AW_H3_GIC_VCPU_BASE);<br>
&gt; +<br>
&gt; +    /*<br>
&gt; +     * Wire the outputs from each CPU&#39;s generic timer and the GICv3<br>
&gt; +     * maintenance interrupt signal to the appropriate GIC PPI inputs,<br>
&gt; +     * and the GIC&#39;s IRQ/FIQ/VIRQ/VFIQ interrupt outputs to the CPU&#39;s inputs.<br>
&gt; +     */<br>
&gt; +    for (i = 0; i &lt; AW_H3_NUM_CPUS; i++) {<br>
&gt; +        DeviceState *cpudev = DEVICE(qemu_get_cpu(i));<br>
&gt; +        int ppibase = AW_H3_GIC_NUM_SPI + i * GIC_INTERNAL + GIC_NR_SGIS;<br>
&gt; +        int irq;<br>
&gt; +        /*<br>
&gt; +         * Mapping from the output timer irq lines from the CPU to the<br>
&gt; +         * GIC PPI inputs used for this board.<br>
&gt; +         */<br>
&gt; +        const int timer_irq[] = {<br>
&gt; +            [GTIMER_PHYS] = AW_H3_GIC_PPI_ARM_PHYSTIMER,<br>
&gt; +            [GTIMER_VIRT] = AW_H3_GIC_PPI_ARM_VIRTTIMER,<br>
&gt; +            [GTIMER_HYP]  = AW_H3_GIC_PPI_ARM_HYPTIMER,<br>
&gt; +            [GTIMER_SEC]  = AW_H3_GIC_PPI_ARM_SECTIMER,<br>
&gt; +        };<br>
&gt; +<br>
&gt; +        /* Connect CPU timer outputs to GIC PPI inputs */<br>
&gt; +        for (irq = 0; irq &lt; ARRAY_SIZE(timer_irq); irq++) {<br>
&gt; +            qdev_connect_gpio_out(cpudev, irq,<br>
&gt; +                                  qdev_get_gpio_in(DEVICE(&amp;s-&gt;gic),<br>
&gt; +                                                   ppibase + timer_irq[irq]));<br>
&gt; +        }<br>
&gt; +<br>
&gt; +        /* Connect GIC outputs to CPU interrupt inputs */<br>
&gt; +        sysbus_connect_irq(sysbusdev, i,<br>
&gt; +                           qdev_get_gpio_in(cpudev, ARM_CPU_IRQ));<br>
&gt; +        sysbus_connect_irq(sysbusdev, i + AW_H3_NUM_CPUS,<br>
&gt; +                           qdev_get_gpio_in(cpudev, ARM_CPU_FIQ));<br>
&gt; +        sysbus_connect_irq(sysbusdev, i + (2 * AW_H3_NUM_CPUS),<br>
&gt; +                           qdev_get_gpio_in(cpudev, ARM_CPU_VIRQ));<br>
&gt; +        sysbus_connect_irq(sysbusdev, i + (3 * AW_H3_NUM_CPUS),<br>
&gt; +                           qdev_get_gpio_in(cpudev, ARM_CPU_VFIQ));<br>
&gt; +<br>
&gt; +        /* GIC maintenance signal */<br>
&gt; +        sysbus_connect_irq(sysbusdev, i + (4 * AW_H3_NUM_CPUS),<br>
&gt; +                           qdev_get_gpio_in(DEVICE(&amp;s-&gt;gic),<br>
&gt; +                                            ppibase + AW_H3_GIC_PPI_MAINT));<br>
&gt; +    }<br>
&gt; +<br>
&gt; +    for (i = 0; i &lt; AW_H3_GIC_NUM_SPI; i++) {<br>
&gt; +        s-&gt;irq[i] = qdev_get_gpio_in(DEVICE(&amp;s-&gt;gic), i);<br>
<br>
Apparently we don&#39;t need the irq array in AwH3State, because ...<br>
<br>
&gt; +    }<br>
&gt; +<br>
&gt; +    /* Timer */<br>
&gt; +    object_property_set_bool(OBJECT(&amp;s-&gt;timer), true, &quot;realized&quot;, &amp;err);<br>
&gt; +    if (err != NULL) {<br>
&gt; +        error_propagate(errp, err);<br>
&gt; +        return;<br>
&gt; +    }<br>
&gt; +    sysbusdev = SYS_BUS_DEVICE(&amp;s-&gt;timer);<br>
&gt; +    sysbus_mmio_map(sysbusdev, 0, AW_H3_PIT_REG_BASE);<br>
&gt; +    sysbus_connect_irq(sysbusdev, 0, s-&gt;irq[AW_H3_GIC_SPI_TIMER0]);<br>
&gt; +    sysbus_connect_irq(sysbusdev, 1, s-&gt;irq[AW_H3_GIC_SPI_TIMER1]);<br>
<br>
... we can call qdev_get_gpio_in() here directly.<br>
<br>
&gt; +<br>
&gt; +    /* SRAM */<br>
&gt; +    memory_region_init_ram(&amp;s-&gt;sram_a1, OBJECT(dev), &quot;sram A1&quot;,<br>
&gt; +                            AW_H3_SRAM_A1_SIZE, &amp;error_fatal);<br>
&gt; +    memory_region_init_ram(&amp;s-&gt;sram_a2, OBJECT(dev), &quot;sram A2&quot;,<br>
&gt; +                            AW_H3_SRAM_A2_SIZE, &amp;error_fatal);<br>
&gt; +    memory_region_init_ram(&amp;s-&gt;sram_c, OBJECT(dev), &quot;sram C&quot;,<br>
&gt; +                            AW_H3_SRAM_C_SIZE, &amp;error_fatal);<br>
&gt; +    memory_region_add_subregion(get_system_memory(), AW_H3_SRAM_A1_BASE,<br>
&gt; +                                &amp;s-&gt;sram_a1);<br>
&gt; +    memory_region_add_subregion(get_system_memory(), AW_H3_SRAM_A2_BASE,<br>
&gt; +                                &amp;s-&gt;sram_a2);<br>
&gt; +    memory_region_add_subregion(get_system_memory(), AW_H3_SRAM_C_BASE,<br>
&gt; +                                &amp;s-&gt;sram_c);<br>
&gt; +<br>
&gt; +    /* UART */<br>
&gt; +    if (serial_hd(0)) {<br>
&gt; +        serial_mm_init(get_system_memory(), AW_H3_UART0_REG_BASE, 2,<br>
&gt; +                       s-&gt;irq[AW_H3_GIC_SPI_UART0], 115200, serial_hd(0),<br>
<br>
qdev_get_gpio_in() here too.<br>
<br>
&gt; +                       DEVICE_NATIVE_ENDIAN);<br>
&gt; +    }<br>
&gt; +<br>
&gt; +    /* Unimplemented devices */<br>
&gt; +    create_unimplemented_device(&quot;display-engine&quot;, AW_H3_DE_BASE, AW_H3_DE_SIZE);<br>
&gt; +    create_unimplemented_device(&quot;dma&quot;, AW_H3_DMA_BASE, AW_H3_DMA_SIZE);<br>
&gt; +    create_unimplemented_device(&quot;lcd0&quot;, AW_H3_LCD0_BASE, AW_H3_LCD0_SIZE);<br>
&gt; +    create_unimplemented_device(&quot;lcd1&quot;, AW_H3_LCD1_BASE, AW_H3_LCD1_SIZE);<br>
&gt; +    create_unimplemented_device(&quot;gpu&quot;, AW_H3_GPU_BASE, AW_H3_GPU_SIZE);<br>
&gt; +    create_unimplemented_device(&quot;hdmi&quot;, AW_H3_HDMI_BASE, AW_H3_HDMI_SIZE);<br>
&gt; +    create_unimplemented_device(&quot;rtc&quot;, AW_H3_RTC_BASE, AW_H3_RTC_SIZE);<br>
&gt; +    create_unimplemented_device(&quot;audio-codec&quot;, AW_H3_AC_BASE, AW_H3_AC_SIZE);<br>
&gt; +}<br>
&gt; +<br>
&gt; +static void aw_h3_class_init(ObjectClass *oc, void *data)<br>
&gt; +{<br>
&gt; +    DeviceClass *dc = DEVICE_CLASS(oc);<br>
&gt; +<br>
&gt; +    dc-&gt;realize = aw_h3_realize;<br>
&gt; +    /* Reason: uses serial_hds and nd_table */<br>
&gt; +    dc-&gt;user_creatable = false;<br>
&gt; +}<br>
&gt; +<br>
&gt; +static const TypeInfo aw_h3_type_info = {<br>
&gt; +    .name = TYPE_AW_H3,<br>
&gt; +    .parent = TYPE_DEVICE,<br>
&gt; +    .instance_size = sizeof(AwH3State),<br>
&gt; +    .instance_init = aw_h3_init,<br>
&gt; +    .class_init = aw_h3_class_init,<br>
&gt; +};<br>
&gt; +<br>
&gt; +static void aw_h3_register_types(void)<br>
&gt; +{<br>
&gt; +    type_register_static(&amp;aw_h3_type_info);<br>
&gt; +}<br>
&gt; +<br>
&gt; +type_init(aw_h3_register_types)<br>
&gt; diff --git a/include/hw/arm/allwinner-h3.h b/include/hw/arm/allwinner-h3.h<br>
&gt; new file mode 100644<br>
&gt; index 0000000000..af368c2254<br>
&gt; --- /dev/null<br>
&gt; +++ b/include/hw/arm/allwinner-h3.h<br>
&gt; @@ -0,0 +1,118 @@<br>
&gt; +/*<br>
&gt; + * Allwinner H3 System on Chip emulation<br>
&gt; + *<br>
&gt; + * Copyright (C) 2019 Niek Linnenbank &lt;<a href="mailto:nieklinnenbank@gmail.com" target="_blank">nieklinnenbank@gmail.com</a>&gt;<br>
&gt; + *<br>
&gt; + * This program is free software: you can redistribute it and/or modify<br>
&gt; + * it under the terms of the GNU General Public License as published by<br>
&gt; + * the Free Software Foundation, either version 2 of the License, or<br>
&gt; + * (at your option) any later version.<br>
&gt; + *<br>
&gt; + * This program is distributed in the hope that it will be useful,<br>
&gt; + * but WITHOUT ANY WARRANTY; without even the implied warranty of<br>
&gt; + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the<br>
&gt; + * GNU General Public License for more details.<br>
&gt; + *<br>
&gt; + * You should have received a copy of the GNU General Public License<br>
&gt; + * along with this program.  If not, see &lt;<a href="http://www.gnu.org/licenses/" rel="noreferrer" target="_blank">http://www.gnu.org/licenses/</a>&gt;.<br>
&gt; + */<br>
&gt; +<br>
&gt; +#ifndef HW_ARM_ALLWINNER_H3_H<br>
&gt; +#define HW_ARM_ALLWINNER_H3_H<br>
&gt; +<br>
&gt; +#include &quot;qemu/error-report.h&quot;<br>
&gt; +#include &quot;qemu/units.h&quot;<br>
&gt; +#include &quot;hw/char/serial.h&quot;<br>
&gt; +#include &quot;hw/arm/boot.h&quot;<br>
&gt; +#include &quot;hw/timer/allwinner-a10-pit.h&quot;<br>
&gt; +#include &quot;hw/intc/arm_gic.h&quot;<br>
&gt; +#include &quot;target/arm/cpu.h&quot;<br>
&gt; +<br>
&gt; +#define AW_H3_SRAM_A1_BASE     (0x00000000)<br>
&gt; +#define AW_H3_SRAM_A2_BASE     (0x00044000)<br>
&gt; +#define AW_H3_SRAM_C_BASE      (0x00010000)<br>
&gt; +#define AW_H3_DE_BASE          (0x01000000)<br>
&gt; +#define AW_H3_SYSCON_BASE      (0x01c00000)<br>
&gt; +#define AW_H3_DMA_BASE         (0x01c02000)<br>
&gt; +#define AW_H3_LCD0_BASE        (0x01c0c000)<br>
&gt; +#define AW_H3_LCD1_BASE        (0x01c0d000)<br>
&gt; +#define AW_H3_SID_BASE         (0x01c14000)<br>
&gt; +#define AW_H3_CCU_BASE         (0x01c20000)<br>
&gt; +#define AW_H3_PIC_REG_BASE     (0x01c20400)<br>
&gt; +#define AW_H3_PIT_REG_BASE     (0x01c20c00)<br>
&gt; +#define AW_H3_AC_BASE          (0x01c22c00)<br>
&gt; +#define AW_H3_UART0_REG_BASE   (0x01c28000)<br>
&gt; +#define AW_H3_EMAC_BASE        (0x01c30000)<br>
&gt; +#define AW_H3_MMC0_BASE        (0x01c0f000)<br>
&gt; +#define AW_H3_EHCI0_BASE       (0x01c1a000)<br>
&gt; +#define AW_H3_OHCI0_BASE       (0x01c1a400)<br>
&gt; +#define AW_H3_EHCI1_BASE       (0x01c1b000)<br>
&gt; +#define AW_H3_OHCI1_BASE       (0x01c1b400)<br>
&gt; +#define AW_H3_EHCI2_BASE       (0x01c1c000)<br>
&gt; +#define AW_H3_OHCI2_BASE       (0x01c1c400)<br>
&gt; +#define AW_H3_EHCI3_BASE       (0x01c1d000)<br>
&gt; +#define AW_H3_OHCI3_BASE       (0x01c1d400)<br>
&gt; +#define AW_H3_GPU_BASE         (0x01c40000)<br>
&gt; +#define AW_H3_GIC_DIST_BASE    (0x01c81000)<br>
&gt; +#define AW_H3_GIC_CPU_BASE     (0x01c82000)<br>
&gt; +#define AW_H3_GIC_HYP_BASE     (0x01c84000)<br>
&gt; +#define AW_H3_GIC_VCPU_BASE    (0x01c86000)<br>
&gt; +#define AW_H3_HDMI_BASE        (0x01ee0000)<br>
&gt; +#define AW_H3_RTC_BASE         (0x01f00000)<br>
&gt; +#define AW_H3_CPUCFG_BASE      (0x01f01c00)<br>
&gt; +#define AW_H3_SDRAM_BASE       (0x40000000)<br>
&gt; +<br>
&gt; +#define AW_H3_SRAM_A1_SIZE     (64 * KiB)<br>
&gt; +#define AW_H3_SRAM_A2_SIZE     (32 * KiB)<br>
&gt; +#define AW_H3_SRAM_C_SIZE      (44 * KiB)<br>
&gt; +#define AW_H3_DE_SIZE          (4 * MiB)<br>
&gt; +#define AW_H3_DMA_SIZE         (4 * KiB)<br>
&gt; +#define AW_H3_LCD0_SIZE        (4 * KiB)<br>
&gt; +#define AW_H3_LCD1_SIZE        (4 * KiB)<br>
&gt; +#define AW_H3_GPU_SIZE         (64 * KiB)<br>
&gt; +#define AW_H3_HDMI_SIZE        (128 * KiB)<br>
&gt; +#define AW_H3_RTC_SIZE         (1 * KiB)<br>
&gt; +#define AW_H3_AC_SIZE          (2 * KiB)<br>
&gt; +<br>
&gt; +#define AW_H3_GIC_PPI_MAINT          (9)<br>
&gt; +#define AW_H3_GIC_PPI_ARM_HYPTIMER  (10)<br>
&gt; +#define AW_H3_GIC_PPI_ARM_VIRTTIMER (11)<br>
&gt; +#define AW_H3_GIC_PPI_ARM_SECTIMER  (13)<br>
&gt; +#define AW_H3_GIC_PPI_ARM_PHYSTIMER (14)<br>
&gt; +<br>
&gt; +#define AW_H3_GIC_SPI_UART0         (0)<br>
&gt; +#define AW_H3_GIC_SPI_TIMER0        (18)<br>
&gt; +#define AW_H3_GIC_SPI_TIMER1        (19)<br>
&gt; +#define AW_H3_GIC_SPI_MMC0          (60)<br>
&gt; +#define AW_H3_GIC_SPI_MMC1          (61)<br>
&gt; +#define AW_H3_GIC_SPI_MMC2          (62)<br>
&gt; +#define AW_H3_GIC_SPI_EHCI0         (72)<br>
&gt; +#define AW_H3_GIC_SPI_OHCI0         (73)<br>
&gt; +#define AW_H3_GIC_SPI_EHCI1         (74)<br>
&gt; +#define AW_H3_GIC_SPI_OHCI1         (75)<br>
&gt; +#define AW_H3_GIC_SPI_EHCI2         (76)<br>
&gt; +#define AW_H3_GIC_SPI_OHCI2         (77)<br>
&gt; +#define AW_H3_GIC_SPI_EHCI3         (78)<br>
&gt; +#define AW_H3_GIC_SPI_OHCI3         (79)<br>
&gt; +#define AW_H3_GIC_SPI_EMAC          (82)<br>
<br>
I&#39;d move half of the previous definitions into allwinner-h3.c, since <br>
they are only used there.<br>
<br></blockquote><div>Indeed, you are right, I&#39;ll move them.<br></div><div> <br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
Also, I&#39;d use an enum for the PPI/SPI. <br></blockquote><div><br></div><div></div><div> Thanks, I will process all of your comments above for the next patch version.<br></div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"> 
<br></blockquote><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
&gt; +<br>
&gt; +#define AW_H3_GIC_NUM_SPI           (128)<br>
&gt; +#define AW_H3_NUM_CPUS              (4)<br>
&gt; +<br>
&gt; +#define TYPE_AW_H3 &quot;allwinner-h3&quot;<br>
&gt; +#define AW_H3(obj) OBJECT_CHECK(AwH3State, (obj), TYPE_AW_H3)<br>
&gt; +<br>
&gt; +typedef struct AwH3State {<br>
&gt; +    /*&lt; private &gt;*/<br>
&gt; +    DeviceState parent_obj;<br>
&gt; +    /*&lt; public &gt;*/<br>
&gt; +<br>
&gt; +    qemu_irq irq[AW_H3_GIC_NUM_SPI];<br>
&gt; +    AwA10PITState timer;<br>
&gt; +    GICState gic;<br>
&gt; +    MemoryRegion sram_a1;<br>
&gt; +    MemoryRegion sram_a2;<br>
&gt; +    MemoryRegion sram_c;<br>
&gt; +} AwH3State;<br>
&gt; +<br>
&gt; +#endif<br>
&gt; <br>
<br>
Nice clean patch, for a first contribution :)<br></blockquote><div><br></div><div>Thank you Philippe!  <br></div></div><div><br></div><div>Regards,</div><div>Niek<br></div><br>-- <br><div dir="ltr" class="gmail_signature"><div dir="ltr"><div>Niek Linnenbank<br><br></div></div></div></div>

^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH 02/10] hw: arm: add Xunlong Orange Pi PC machine
  2019-12-03  9:17   ` Philippe Mathieu-Daudé
  2019-12-03 19:33     ` Niek Linnenbank
@ 2019-12-05 22:15     ` Niek Linnenbank
  2019-12-06  5:41       ` Philippe Mathieu-Daudé
  1 sibling, 1 reply; 33+ messages in thread
From: Niek Linnenbank @ 2019-12-05 22:15 UTC (permalink / raw)
  To: Philippe Mathieu-Daudé
  Cc: b.galvani, Peter Maydell, qemu-arm, qemu-devel

[-- Attachment #1: Type: text/plain, Size: 7687 bytes --]

Hello Philippe,

On Tue, Dec 3, 2019 at 10:18 AM Philippe Mathieu-Daudé <philmd@redhat.com>
wrote:

> On 12/2/19 10:09 PM, Niek Linnenbank wrote:
> > 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,
> > 512MB RAM, 100Mbit ethernet, USB, SD/MMC, USB, HDMI and
> > various other I/O. This commit add support for the Xunlong
> > Orange Pi PC machine.
> >
> > Signed-off-by: Niek Linnenbank <nieklinnenbank@gmail.com>
> > ---
> >   MAINTAINERS          |  1 +
> >   hw/arm/Makefile.objs |  2 +-
> >   hw/arm/orangepi.c    | 90 ++++++++++++++++++++++++++++++++++++++++++++
> >   3 files changed, 92 insertions(+), 1 deletion(-)
> >   create mode 100644 hw/arm/orangepi.c
> >
> > diff --git a/MAINTAINERS b/MAINTAINERS
> > index 29c9936037..42c913d6cb 100644
> > --- a/MAINTAINERS
> > +++ b/MAINTAINERS
> > @@ -485,6 +485,7 @@ L: qemu-arm@nongnu.org
> >   S: Maintained
> >   F: hw/*/allwinner-h3*
> >   F: include/hw/*/allwinner-h3*
> > +F: hw/arm/orangepi.c
> >
> >   ARM PrimeCell and CMSDK devices
> >   M: Peter Maydell <peter.maydell@linaro.org>
> > diff --git a/hw/arm/Makefile.objs b/hw/arm/Makefile.objs
> > index 956e496052..8d5ea453d5 100644
> > --- a/hw/arm/Makefile.objs
> > +++ b/hw/arm/Makefile.objs
> > @@ -34,7 +34,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
> > +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_XLNX_ZYNQMP_ARM) += xlnx-zynqmp.o xlnx-zcu102.o
> > diff --git a/hw/arm/orangepi.c b/hw/arm/orangepi.c
> > new file mode 100644
> > index 0000000000..5ef2735f81
> > --- /dev/null
> > +++ b/hw/arm/orangepi.c
> > @@ -0,0 +1,90 @@
> > +/*
> > + * 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 "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"
> > +
> > +static struct arm_boot_info orangepi_binfo = {
> > +    .loader_start = AW_H3_SDRAM_BASE,
> > +    .board_id = -1,
> > +};
> > +
> > +typedef struct OrangePiState {
> > +    AwH3State *h3;
> > +    MemoryRegion sdram;
> > +} OrangePiState;
> > +
> > +static void orangepi_init(MachineState *machine)
> > +{
> > +    OrangePiState *s = g_new(OrangePiState, 1);
> > +    Error *err = NULL;
> > +
>
> Here I'd add:
>
>        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);
>        }
>
> Done!

> +    s->h3 = AW_H3(object_new(TYPE_AW_H3));
> > +
> > +    /* Setup timer properties */
> > +    object_property_set_int(OBJECT(&s->h3->timer), 32768, "clk0-freq",
> &err);
> > +    if (err != NULL) {
> > +        error_reportf_err(err, "Couldn't set clk0 frequency: ");
> > +        exit(1);
> > +    }
> > +
> > +    object_property_set_int(OBJECT(&s->h3->timer), 24000000,
> "clk1-freq",
> > +                            &err);
> > +    if (err != NULL) {
> > +        error_reportf_err(err, "Couldn't set clk1 frequency: ");
> > +        exit(1);
> > +    }
> > +
> > +    /* Mark H3 object realized */
> > +    object_property_set_bool(OBJECT(s->h3), true, "realized", &err);
>
> I'm not sure if that's correct but I'd simply use &error_abort here.
>
> Done, I applied it to all the functions and removed the err variable.

> +    if (err != NULL) {
> > +        error_reportf_err(err, "Couldn't realize Allwinner H3: ");
> > +        exit(1);
> > +    }
> > +
> > +    /* RAM */
> > +    memory_region_allocate_system_memory(&s->sdram, NULL,
> "orangepi.ram",
> > +                                         machine->ram_size);
>
> I'd only allow machine->ram_size == 1 * GiB here, since the onboard DRAM
> is not upgradable.
>

Agree, we should add something for that. Would it be acceptable if we make
the 1GB an upper limit?
I see that the Raspberry Pi is doing that too in hw/arm/raspi.c, like so:

    if (machine->ram_size > 1 * GiB) {
        error_report("Requested ram size is too large for this machine: "
                     "maximum is 1GB");
        exit(1);
    }

I think it would be helpful to allow the flexibility to the user of
reducing the RAM to less than 1GB,
in case resources of the host OS are limited. What do you think?



> > +    memory_region_add_subregion(get_system_memory(), AW_H3_SDRAM_BASE,
> > +                                &s->sdram);
> > +
> > +    /* Load target kernel */
> > +    orangepi_binfo.ram_size = machine->ram_size;
> > +    orangepi_binfo.nb_cpus  = AW_H3_NUM_CPUS;
> > +    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->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->ignore_memory_transaction_failures = true;
>
> You should not use this flag in new design. See the documentation in
> include/hw/boards.h:
>
>   * @ignore_memory_transaction_failures:
>   *    [...] New board models
>   *    should instead use "unimplemented-device" for all memory ranges
> where
>   *    the guest will attempt to probe for a device that QEMU doesn't
>   *    implement and a stub device is required.
>
> You already use the "unimplemented-device".
>
> Thanks, I'm working on this now. I think that at least I'll need to add
all of the devices mentioned in the 4.1 Memory Mapping chapter of the
datasheet
as an unimplemented device. Previously I only added some that I thought
were relevant.

I added all the missing devices as unimplemented and removed the
ignore_memory_transaction_failures flag
from the machine. Now it seems Linux gets a data abort while probing the
uart1 serial device at 0x01c28400,
so I'll need to debug it further. I'll post back when I have more results.

Regards,
Niek



> > +}
> > +
> > +DEFINE_MACHINE("orangepi", orangepi_machine_init)
>
> Can you name it 'orangepi-pc'? So we can add other orangepi models.
>
> Thanks,
>
> Phil.
>
>

-- 
Niek Linnenbank

[-- Attachment #2: Type: text/html, Size: 10645 bytes --]

<div dir="ltr"><div>Hello Philippe,<br></div><br><div class="gmail_quote"><div dir="ltr" class="gmail_attr">On Tue, Dec 3, 2019 at 10:18 AM Philippe Mathieu-Daudé &lt;<a href="mailto:philmd@redhat.com">philmd@redhat.com</a>&gt; wrote:<br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">On 12/2/19 10:09 PM, Niek Linnenbank wrote:<br>
&gt; The Xunlong Orange Pi PC is an Allwinner H3 System on Chip<br>
&gt; based embedded computer with mainline support in both U-Boot<br>
&gt; and Linux. The board comes with a Quad Core Cortex A7 @ 1.3GHz,<br>
&gt; 512MB RAM, 100Mbit ethernet, USB, SD/MMC, USB, HDMI and<br>
&gt; various other I/O. This commit add support for the Xunlong<br>
&gt; Orange Pi PC machine.<br>
&gt; <br>
&gt; Signed-off-by: Niek Linnenbank &lt;<a href="mailto:nieklinnenbank@gmail.com" target="_blank">nieklinnenbank@gmail.com</a>&gt;<br>
&gt; ---<br>
&gt;   MAINTAINERS          |  1 +<br>
&gt;   hw/arm/Makefile.objs |  2 +-<br>
&gt;   hw/arm/orangepi.c    | 90 ++++++++++++++++++++++++++++++++++++++++++++<br>
&gt;   3 files changed, 92 insertions(+), 1 deletion(-)<br>
&gt;   create mode 100644 hw/arm/orangepi.c<br>
&gt; <br>
&gt; diff --git a/MAINTAINERS b/MAINTAINERS<br>
&gt; index 29c9936037..42c913d6cb 100644<br>
&gt; --- a/MAINTAINERS<br>
&gt; +++ b/MAINTAINERS<br>
&gt; @@ -485,6 +485,7 @@ L: <a href="mailto:qemu-arm@nongnu.org" target="_blank">qemu-arm@nongnu.org</a><br>
&gt;   S: Maintained<br>
&gt;   F: hw/*/allwinner-h3*<br>
&gt;   F: include/hw/*/allwinner-h3*<br>
&gt; +F: hw/arm/orangepi.c<br>
&gt;   <br>
&gt;   ARM PrimeCell and CMSDK devices<br>
&gt;   M: Peter Maydell &lt;<a href="mailto:peter.maydell@linaro.org" target="_blank">peter.maydell@linaro.org</a>&gt;<br>
&gt; diff --git a/hw/arm/Makefile.objs b/hw/arm/Makefile.objs<br>
&gt; index 956e496052..8d5ea453d5 100644<br>
&gt; --- a/hw/arm/Makefile.objs<br>
&gt; +++ b/hw/arm/Makefile.objs<br>
&gt; @@ -34,7 +34,7 @@ obj-$(CONFIG_DIGIC) += digic.o<br>
&gt;   obj-$(CONFIG_OMAP) += omap1.o omap2.o<br>
&gt;   obj-$(CONFIG_STRONGARM) += strongarm.o<br>
&gt;   obj-$(CONFIG_ALLWINNER_A10) += allwinner-a10.o cubieboard.o<br>
&gt; -obj-$(CONFIG_ALLWINNER_H3) += allwinner-h3.o<br>
&gt; +obj-$(CONFIG_ALLWINNER_H3) += allwinner-h3.o orangepi.o<br>
&gt;   obj-$(CONFIG_RASPI) += bcm2835_peripherals.o bcm2836.o raspi.o<br>
&gt;   obj-$(CONFIG_STM32F205_SOC) += stm32f205_soc.o<br>
&gt;   obj-$(CONFIG_XLNX_ZYNQMP_ARM) += xlnx-zynqmp.o xlnx-zcu102.o<br>
&gt; diff --git a/hw/arm/orangepi.c b/hw/arm/orangepi.c<br>
&gt; new file mode 100644<br>
&gt; index 0000000000..5ef2735f81<br>
&gt; --- /dev/null<br>
&gt; +++ b/hw/arm/orangepi.c<br>
&gt; @@ -0,0 +1,90 @@<br>
&gt; +/*<br>
&gt; + * Orange Pi emulation<br>
&gt; + *<br>
&gt; + * Copyright (C) 2019 Niek Linnenbank &lt;<a href="mailto:nieklinnenbank@gmail.com" target="_blank">nieklinnenbank@gmail.com</a>&gt;<br>
&gt; + *<br>
&gt; + * This program is free software: you can redistribute it and/or modify<br>
&gt; + * it under the terms of the GNU General Public License as published by<br>
&gt; + * the Free Software Foundation, either version 2 of the License, or<br>
&gt; + * (at your option) any later version.<br>
&gt; + *<br>
&gt; + * This program is distributed in the hope that it will be useful,<br>
&gt; + * but WITHOUT ANY WARRANTY; without even the implied warranty of<br>
&gt; + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the<br>
&gt; + * GNU General Public License for more details.<br>
&gt; + *<br>
&gt; + * You should have received a copy of the GNU General Public License<br>
&gt; + * along with this program.  If not, see &lt;<a href="http://www.gnu.org/licenses/" rel="noreferrer" target="_blank">http://www.gnu.org/licenses/</a>&gt;.<br>
&gt; + */<br>
&gt; +<br>
&gt; +#include &quot;qemu/osdep.h&quot;<br>
&gt; +#include &quot;exec/address-spaces.h&quot;<br>
&gt; +#include &quot;qapi/error.h&quot;<br>
&gt; +#include &quot;cpu.h&quot;<br>
&gt; +#include &quot;hw/sysbus.h&quot;<br>
&gt; +#include &quot;hw/boards.h&quot;<br>
&gt; +#include &quot;hw/qdev-properties.h&quot;<br>
&gt; +#include &quot;hw/arm/allwinner-h3.h&quot;<br>
&gt; +<br>
&gt; +static struct arm_boot_info orangepi_binfo = {<br>
&gt; +    .loader_start = AW_H3_SDRAM_BASE,<br>
&gt; +    .board_id = -1,<br>
&gt; +};<br>
&gt; +<br>
&gt; +typedef struct OrangePiState {<br>
&gt; +    AwH3State *h3;<br>
&gt; +    MemoryRegion sdram;<br>
&gt; +} OrangePiState;<br>
&gt; +<br>
&gt; +static void orangepi_init(MachineState *machine)<br>
&gt; +{<br>
&gt; +    OrangePiState *s = g_new(OrangePiState, 1);<br>
&gt; +    Error *err = NULL;<br>
&gt; +<br>
<br>
Here I&#39;d add:<br>
<br>
       if (strcmp(machine-&gt;cpu_type, ARM_CPU_TYPE_NAME(&quot;cortex-a7&quot;)) != 0) {<br>
           error_report(&quot;This board can only be used with cortex-a7 CPU&quot;);<br>
           exit(1);<br>
       }<br>
<br></blockquote><div>Done!</div><div> <br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
&gt; +    s-&gt;h3 = AW_H3(object_new(TYPE_AW_H3));<br>
&gt; +<br>
&gt; +    /* Setup timer properties */<br>
&gt; +    object_property_set_int(OBJECT(&amp;s-&gt;h3-&gt;timer), 32768, &quot;clk0-freq&quot;, &amp;err);<br>
&gt; +    if (err != NULL) {<br>
&gt; +        error_reportf_err(err, &quot;Couldn&#39;t set clk0 frequency: &quot;);<br>
&gt; +        exit(1);<br>
&gt; +    }<br>
&gt; +<br>
&gt; +    object_property_set_int(OBJECT(&amp;s-&gt;h3-&gt;timer), 24000000, &quot;clk1-freq&quot;,<br>
&gt; +                            &amp;err);<br>
&gt; +    if (err != NULL) {<br>
&gt; +        error_reportf_err(err, &quot;Couldn&#39;t set clk1 frequency: &quot;);<br>
&gt; +        exit(1);<br>
&gt; +    }<br>
&gt; +<br>
&gt; +    /* Mark H3 object realized */<br>
&gt; +    object_property_set_bool(OBJECT(s-&gt;h3), true, &quot;realized&quot;, &amp;err);<br>
<br>
I&#39;m not sure if that&#39;s correct but I&#39;d simply use &amp;error_abort here.<br>
<br></blockquote><div>Done, I applied it to all the functions and removed the err variable.<br></div><div> <br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
&gt; +    if (err != NULL) {<br>
&gt; +        error_reportf_err(err, &quot;Couldn&#39;t realize Allwinner H3: &quot;);<br>
&gt; +        exit(1);<br>
&gt; +    }<br>
&gt; +<br>
&gt; +    /* RAM */<br>
&gt; +    memory_region_allocate_system_memory(&amp;s-&gt;sdram, NULL, &quot;orangepi.ram&quot;,<br>
&gt; +                                         machine-&gt;ram_size);<br>
<br>
I&#39;d only allow machine-&gt;ram_size == 1 * GiB here, since the onboard DRAM <br>
is not upgradable.<br></blockquote><div><br></div><div>Agree, we should add something for that. Would it be acceptable if we make the 1GB an upper limit?</div><div>I see that the Raspberry Pi is doing that too in hw/arm/raspi.c, like so:</div><div><br></div><div>    if (machine-&gt;ram_size &gt; 1 * GiB) {<br>        error_report(&quot;Requested ram size is too large for this machine: &quot;<br>                     &quot;maximum is 1GB&quot;);<br>        exit(1);<br>    }</div><div><br></div><div>I think it would be helpful to allow the flexibility to the user of reducing the RAM to less than 1GB,</div><div>in case resources of the host OS are limited. What do you think?<br></div><div> </div><div><br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
<br>
&gt; +    memory_region_add_subregion(get_system_memory(), AW_H3_SDRAM_BASE,<br>
&gt; +                                &amp;s-&gt;sdram);<br>
&gt; +<br>
&gt; +    /* Load target kernel */<br>
&gt; +    orangepi_binfo.ram_size = machine-&gt;ram_size;<br>
&gt; +    orangepi_binfo.nb_cpus  = AW_H3_NUM_CPUS;<br>
&gt; +    arm_load_kernel(ARM_CPU(first_cpu), machine, &amp;orangepi_binfo);<br>
&gt; +}<br>
&gt; +<br>
&gt; +static void orangepi_machine_init(MachineClass *mc)<br>
&gt; +{<br>
&gt; +    mc-&gt;desc = &quot;Orange Pi PC&quot;;<br>
&gt; +    mc-&gt;init = orangepi_init;<br>
&gt; +    mc-&gt;units_per_default_bus = 1;<br>
&gt; +    mc-&gt;min_cpus = AW_H3_NUM_CPUS;<br>
&gt; +    mc-&gt;max_cpus = AW_H3_NUM_CPUS;<br>
&gt; +    mc-&gt;default_cpus = AW_H3_NUM_CPUS;<br>
<br>
        mc-&gt;default_cpu_type = ARM_CPU_TYPE_NAME(&quot;cortex-a7&quot;);<br>
<br>
&gt; +    mc-&gt;ignore_memory_transaction_failures = true;<br>
<br>
You should not use this flag in new design. See the documentation in <br>
include/hw/boards.h:<br>
<br>
  * @ignore_memory_transaction_failures:<br>
  *    [...] New board models<br>
  *    should instead use &quot;unimplemented-device&quot; for all memory ranges where<br>
  *    the guest will attempt to probe for a device that QEMU doesn&#39;t<br>
  *    implement and a stub device is required.<br>
<br>
You already use the &quot;unimplemented-device&quot;.<br>
<br></blockquote><div>Thanks, I&#39;m working on this now. I think that at least I&#39;ll need to add</div><div>all of the devices mentioned in the 4.1 Memory Mapping chapter of the datasheet</div><div>as an unimplemented device. Previously I only added some that I thought were relevant.</div><div><br></div><div>I added all the missing devices as unimplemented and removed the ignore_memory_transaction_failures flag</div><div>from the machine. Now it seems Linux gets a data abort while probing the uart1 serial device at 0x01c28400,</div><div>so I&#39;ll need to debug it further. I&#39;ll post back when I have more results.</div><div><br></div><div>Regards,</div><div>Niek<br></div><div><br></div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
&gt; +}<br>
&gt; +<br>
&gt; +DEFINE_MACHINE(&quot;orangepi&quot;, orangepi_machine_init)<br>
<br>
Can you name it &#39;orangepi-pc&#39;? So we can add other orangepi models.<br>
<br>
Thanks,<br>
<br>
Phil.<br>
<br>
</blockquote></div><br clear="all"><br>-- <br><div dir="ltr" class="gmail_signature"><div dir="ltr"><div>Niek Linnenbank<br><br></div></div></div></div>

^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH 02/10] hw: arm: add Xunlong Orange Pi PC machine
  2019-12-05 22:15     ` Niek Linnenbank
@ 2019-12-06  5:41       ` Philippe Mathieu-Daudé
  0 siblings, 0 replies; 33+ messages in thread
From: Philippe Mathieu-Daudé @ 2019-12-06  5:41 UTC (permalink / raw)
  To: Niek Linnenbank; +Cc: b.galvani, Peter Maydell, qemu-arm, qemu-devel

On 12/5/19 11:15 PM, Niek Linnenbank wrote:
> Hello Philippe,
> 
> On Tue, Dec 3, 2019 at 10:18 AM Philippe Mathieu-Daudé 
> <philmd@redhat.com <mailto:philmd@redhat.com>> wrote:
> 
>     On 12/2/19 10:09 PM, Niek Linnenbank wrote:
>      > 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,
>      > 512MB RAM, 100Mbit ethernet, USB, SD/MMC, USB, HDMI and
>      > various other I/O. This commit add support for the Xunlong
>      > Orange Pi PC machine.
>      >
>      > Signed-off-by: Niek Linnenbank <nieklinnenbank@gmail.com
>     <mailto:nieklinnenbank@gmail.com>>
>      > ---
>      >   MAINTAINERS          |  1 +
>      >   hw/arm/Makefile.objs |  2 +-
>      >   hw/arm/orangepi.c    | 90
>     ++++++++++++++++++++++++++++++++++++++++++++
>      >   3 files changed, 92 insertions(+), 1 deletion(-)
>      >   create mode 100644 hw/arm/orangepi.c
>      >
>      > diff --git a/MAINTAINERS b/MAINTAINERS
>      > index 29c9936037..42c913d6cb 100644
>      > --- a/MAINTAINERS
>      > +++ b/MAINTAINERS
>      > @@ -485,6 +485,7 @@ L: qemu-arm@nongnu.org
>     <mailto:qemu-arm@nongnu.org>
>      >   S: Maintained
>      >   F: hw/*/allwinner-h3*
>      >   F: include/hw/*/allwinner-h3*
>      > +F: hw/arm/orangepi.c
>      >
>      >   ARM PrimeCell and CMSDK devices
>      >   M: Peter Maydell <peter.maydell@linaro.org
>     <mailto:peter.maydell@linaro.org>>
>      > diff --git a/hw/arm/Makefile.objs b/hw/arm/Makefile.objs
>      > index 956e496052..8d5ea453d5 100644
>      > --- a/hw/arm/Makefile.objs
>      > +++ b/hw/arm/Makefile.objs
>      > @@ -34,7 +34,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
>      > +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_XLNX_ZYNQMP_ARM) += xlnx-zynqmp.o xlnx-zcu102.o
>      > diff --git a/hw/arm/orangepi.c b/hw/arm/orangepi.c
>      > new file mode 100644
>      > index 0000000000..5ef2735f81
>      > --- /dev/null
>      > +++ b/hw/arm/orangepi.c
>      > @@ -0,0 +1,90 @@
>      > +/*
>      > + * Orange Pi emulation
>      > + *
>      > + * Copyright (C) 2019 Niek Linnenbank <nieklinnenbank@gmail.com
>     <mailto: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 "cpu.h"
>      > +#include "hw/sysbus.h"
>      > +#include "hw/boards.h"
>      > +#include "hw/qdev-properties.h"
>      > +#include "hw/arm/allwinner-h3.h"
>      > +
>      > +static struct arm_boot_info orangepi_binfo = {
>      > +    .loader_start = AW_H3_SDRAM_BASE,
>      > +    .board_id = -1,
>      > +};
>      > +
>      > +typedef struct OrangePiState {
>      > +    AwH3State *h3;
>      > +    MemoryRegion sdram;
>      > +} OrangePiState;
>      > +
>      > +static void orangepi_init(MachineState *machine)
>      > +{
>      > +    OrangePiState *s = g_new(OrangePiState, 1);
>      > +    Error *err = NULL;
>      > +
> 
>     Here I'd add:
> 
>             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);
>             }
> 
> Done!
> 
>      > +    s->h3 = AW_H3(object_new(TYPE_AW_H3));
>      > +
>      > +    /* Setup timer properties */
>      > +    object_property_set_int(OBJECT(&s->h3->timer), 32768,
>     "clk0-freq", &err);
>      > +    if (err != NULL) {
>      > +        error_reportf_err(err, "Couldn't set clk0 frequency: ");
>      > +        exit(1);
>      > +    }
>      > +
>      > +    object_property_set_int(OBJECT(&s->h3->timer), 24000000,
>     "clk1-freq",
>      > +                            &err);
>      > +    if (err != NULL) {
>      > +        error_reportf_err(err, "Couldn't set clk1 frequency: ");
>      > +        exit(1);
>      > +    }
>      > +
>      > +    /* Mark H3 object realized */
>      > +    object_property_set_bool(OBJECT(s->h3), true, "realized", &err);
> 
>     I'm not sure if that's correct but I'd simply use &error_abort here.
> 
> Done, I applied it to all the functions and removed the err variable.
> 
>      > +    if (err != NULL) {
>      > +        error_reportf_err(err, "Couldn't realize Allwinner H3: ");
>      > +        exit(1);
>      > +    }
>      > +
>      > +    /* RAM */
>      > +    memory_region_allocate_system_memory(&s->sdram, NULL,
>     "orangepi.ram",
>      > +                                         machine->ram_size);
> 
>     I'd only allow machine->ram_size == 1 * GiB here, since the onboard
>     DRAM
>     is not upgradable.
> 
> 
> Agree, we should add something for that. Would it be acceptable if we 
> make the 1GB an upper limit?
> I see that the Raspberry Pi is doing that too in hw/arm/raspi.c, like so:
> 
>      if (machine->ram_size > 1 * GiB) {
>          error_report("Requested ram size is too large for this machine: "
>                       "maximum is 1GB");
>          exit(1);
>      }
> 
> I think it would be helpful to allow the flexibility to the user of 
> reducing the RAM to less than 1GB,
> in case resources of the host OS are limited. What do you think?

Sure, good idea.

FIY (in case you add more models) we recently noticed there is a problem 
when using 2GiB default on 32-bit hosts, so the workaround is to use <= 
1GiB default.

>      > +    memory_region_add_subregion(get_system_memory(),
>     AW_H3_SDRAM_BASE,
>      > +                                &s->sdram);
>      > +
>      > +    /* Load target kernel */
>      > +    orangepi_binfo.ram_size = machine->ram_size;
>      > +    orangepi_binfo.nb_cpus  = AW_H3_NUM_CPUS;
>      > +    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->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->ignore_memory_transaction_failures = true;
> 
>     You should not use this flag in new design. See the documentation in
>     include/hw/boards.h:
> 
>        * @ignore_memory_transaction_failures:
>        *    [...] New board models
>        *    should instead use "unimplemented-device" for all memory
>     ranges where
>        *    the guest will attempt to probe for a device that QEMU doesn't
>        *    implement and a stub device is required.
> 
>     You already use the "unimplemented-device".
> 
> Thanks, I'm working on this now. I think that at least I'll need to add
> all of the devices mentioned in the 4.1 Memory Mapping chapter of the 
> datasheet
> as an unimplemented device. Previously I only added some that I thought 
> were relevant.
> 
> I added all the missing devices as unimplemented and removed the 
> ignore_memory_transaction_failures flag

I was going to say, "instead of adding *all* the devices regions you can 
add the likely bus decoding regions", probably:

0x01c0.0000   128KiB   AMBA AXI
0x01c2.0000   64KiB    AMBA APB

But too late.

> from the machine. Now it seems Linux gets a data abort while probing the 
> uart1 serial device at 0x01c28400,

Did you add the UART1 as UNIMP or 16550?

> so I'll need to debug it further. I'll post back when I have more results.
> 
> Regards,
> Niek
> 
>      > +}
>      > +
>      > +DEFINE_MACHINE("orangepi", orangepi_machine_init)
> 
>     Can you name it 'orangepi-pc'? So we can add other orangepi models.
> 
>     Thanks,
> 
>     Phil.
> 
> 
> 
> -- 
> Niek Linnenbank
> 



^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH 00/10] Add Allwinner H3 SoC and Orange Pi PC Machine
  2019-12-03 19:32   ` Niek Linnenbank
@ 2019-12-06 14:16     ` Peter Maydell
  0 siblings, 0 replies; 33+ messages in thread
From: Peter Maydell @ 2019-12-06 14:16 UTC (permalink / raw)
  To: Niek Linnenbank
  Cc: Alistair Francis, Richard Henderson, QEMU Developers,
	Niccolò Izzo, Beniamino Galvani, KONRAD Frederic, qemu-arm,
	Philippe Mathieu-Daudé

On Tue, 3 Dec 2019 at 19:32, Niek Linnenbank <nieklinnenbank@gmail.com> wrote:
> Indeed that sounds like an interesting combination. Are there plans to build a multi-arch/single-binary QEMU?

This is in the category of "it would be nice in theory to
support multi-arch guest machines, and we've made some
small steps in that direction and/or tried to keep the
door open for it when designing things, but it would
still be a huge amount of work to actually implement,
so don't hold your breath for it or make anything else
depend on having it happen first"...

thanks
-- PMM


^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH 06/10] arm/arm-powerctl: set NSACR.{CP11, CP10} bits in arm_set_cpu_on()
  2019-12-02 21:09 ` [PATCH 06/10] arm/arm-powerctl: set NSACR.{CP11, CP10} bits in arm_set_cpu_on() Niek Linnenbank
@ 2019-12-06 14:24   ` Peter Maydell
  0 siblings, 0 replies; 33+ messages in thread
From: Peter Maydell @ 2019-12-06 14:24 UTC (permalink / raw)
  To: Niek Linnenbank; +Cc: Beniamino Galvani, qemu-arm, QEMU Developers

On Mon, 2 Dec 2019 at 21:10, Niek Linnenbank <nieklinnenbank@gmail.com> wrote:
>
> This change ensures that the FPU can be accessed in Non-Secure mode
> when the CPU core is reset using the arm_set_cpu_on() function call.
> The NSACR.{CP11,CP10} bits define the exception level required to
> access the FPU in Non-Secure mode. Without these bits set, the CPU
> will give an undefined exception trap on the first FPU access for the
> secondary cores under Linux.
>
> Fixes: fc1120a7f5
> Signed-off-by: Niek Linnenbank <nieklinnenbank@gmail.com>
> ---

Oops, another place where we failed to realise the ramifications
of making NSACR actually do something.

Since this is a bugfix I'm going to fish it out of this patchset
and apply it to target-arm.next with a cc: stable.

Thanks for the catch!

-- PMM


^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH 08/10] arm: allwinner-h3: add Security Identifier device
  2019-12-02 21:09 ` [PATCH 08/10] arm: allwinner-h3: add Security Identifier device Niek Linnenbank
@ 2019-12-06 14:27   ` Peter Maydell
  2019-12-06 16:35     ` Philippe Mathieu-Daudé
  0 siblings, 1 reply; 33+ messages in thread
From: Peter Maydell @ 2019-12-06 14:27 UTC (permalink / raw)
  To: Niek Linnenbank; +Cc: Beniamino Galvani, qemu-arm, QEMU Developers

On Mon, 2 Dec 2019 at 21:10, Niek Linnenbank <nieklinnenbank@gmail.com> wrote:
>
> The Security Identifier device in Allwinner H3 System on Chip
> gives applications a per-board unique identifier. This commit
> adds support for the Allwinner H3 Security Identifier using
> randomized data as input.

If this is a fixed value in hardware, I'm not sure that
having the QEMU model pick a random value is the best
choice. If we just set it to a fixed value in QEMU, is
that going to cause problems?

(Generally it's nice for QEMU to be deterministic, so it
behaves the same way every time you run it. Also if it's
always the same we don't need to bother migrating the
ID value.)

thanks
-- PMM


^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH 08/10] arm: allwinner-h3: add Security Identifier device
  2019-12-06 14:27   ` Peter Maydell
@ 2019-12-06 16:35     ` Philippe Mathieu-Daudé
  0 siblings, 0 replies; 33+ messages in thread
From: Philippe Mathieu-Daudé @ 2019-12-06 16:35 UTC (permalink / raw)
  To: Peter Maydell, Niek Linnenbank
  Cc: Beniamino Galvani, qemu-arm, QEMU Developers

On 12/6/19 3:27 PM, Peter Maydell wrote:
> On Mon, 2 Dec 2019 at 21:10, Niek Linnenbank <nieklinnenbank@gmail.com> wrote:
>>
>> The Security Identifier device in Allwinner H3 System on Chip
>> gives applications a per-board unique identifier. This commit
>> adds support for the Allwinner H3 Security Identifier using
>> randomized data as input.
> 
> If this is a fixed value in hardware, I'm not sure that
> having the QEMU model pick a random value is the best
> choice. If we just set it to a fixed value in QEMU, is
> that going to cause problems?
> 
> (Generally it's nice for QEMU to be deterministic, so it
> behaves the same way every time you run it. Also if it's
> always the same we don't need to bother migrating the
> ID value.)

Agreed. Since the identifier is 128-bit, I'd use DEFINE_PROP_UUID() or, 
to be even safer, DEFINE_PROP_UUID_NODEFAULT().
See how the ipmi-bmc-sim device checks its guid field and fails if unset.



^ permalink raw reply	[flat|nested] 33+ messages in thread

end of thread, back to index

Thread overview: 33+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-12-02 21:09 [PATCH 00/10] Add Allwinner H3 SoC and Orange Pi PC Machine Niek Linnenbank
2019-12-02 21:09 ` [PATCH 01/10] hw: arm: add Allwinner H3 System-on-Chip Niek Linnenbank
2019-12-04 16:53   ` Philippe Mathieu-Daudé
2019-12-04 20:44     ` Niek Linnenbank
2019-12-02 21:09 ` [PATCH 02/10] hw: arm: add Xunlong Orange Pi PC machine Niek Linnenbank
2019-12-03  9:17   ` Philippe Mathieu-Daudé
2019-12-03 19:33     ` Niek Linnenbank
2019-12-04  9:03       ` Philippe Mathieu-Daudé
2019-12-04 19:50         ` Niek Linnenbank
2019-12-05 22:15     ` Niek Linnenbank
2019-12-06  5:41       ` Philippe Mathieu-Daudé
2019-12-02 21:09 ` [PATCH 03/10] arm: allwinner-h3: add Clock Control Unit Niek Linnenbank
2019-12-02 21:09 ` [PATCH 04/10] arm: allwinner-h3: add USB host controller Niek Linnenbank
2019-12-04 16:11   ` Aleksandar Markovic
2019-12-04 20:20     ` Niek Linnenbank
2019-12-02 21:09 ` [PATCH 05/10] arm: allwinner-h3: add System Control module Niek Linnenbank
2019-12-02 21:09 ` [PATCH 06/10] arm/arm-powerctl: set NSACR.{CP11, CP10} bits in arm_set_cpu_on() Niek Linnenbank
2019-12-06 14:24   ` Peter Maydell
2019-12-02 21:09 ` [PATCH 07/10] arm: allwinner-h3: add CPU Configuration module Niek Linnenbank
2019-12-02 21:09 ` [PATCH 08/10] arm: allwinner-h3: add Security Identifier device Niek Linnenbank
2019-12-06 14:27   ` Peter Maydell
2019-12-06 16:35     ` Philippe Mathieu-Daudé
2019-12-02 21:09 ` [PATCH 09/10] arm: allwinner-h3: add SD/MMC host controller Niek Linnenbank
2019-12-02 21:09 ` [PATCH 10/10] arm: allwinner-h3: add EMAC ethernet device Niek Linnenbank
2019-12-03  9:33   ` KONRAD Frederic
2019-12-03 19:41     ` Niek Linnenbank
2019-12-04 15:14     ` Philippe Mathieu-Daudé
2019-12-04 15:22       ` KONRAD Frederic
2019-12-03  8:47 ` [PATCH 00/10] Add Allwinner H3 SoC and Orange Pi PC Machine Philippe Mathieu-Daudé
2019-12-03 19:25   ` Niek Linnenbank
2019-12-03  9:02 ` Philippe Mathieu-Daudé
2019-12-03 19:32   ` Niek Linnenbank
2019-12-06 14:16     ` Peter Maydell

QEMU-Devel Archive on lore.kernel.org

Archives are clonable:
	git clone --mirror https://lore.kernel.org/qemu-devel/0 qemu-devel/git/0.git
	git clone --mirror https://lore.kernel.org/qemu-devel/1 qemu-devel/git/1.git

	# If you have public-inbox 1.1+ installed, you may
	# initialize and index your mirror using the following commands:
	public-inbox-init -V2 qemu-devel qemu-devel/ https://lore.kernel.org/qemu-devel \
		qemu-devel@nongnu.org
	public-inbox-index qemu-devel

Example config snippet for mirrors

Newsgroup available over NNTP:
	nntp://nntp.lore.kernel.org/org.nongnu.qemu-devel


AGPL code for this site: git clone https://public-inbox.org/public-inbox.git