qemu-devel.nongnu.org archive mirror
 help / color / mirror / 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
                   ` (12 more replies)
  0 siblings, 13 replies; 67+ 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] 67+ 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-10  9:02   ` Philippe Mathieu-Daudé
  2019-12-02 21:09 ` [PATCH 02/10] hw: arm: add Xunlong Orange Pi PC machine Niek Linnenbank
                   ` (11 subsequent siblings)
  12 siblings, 2 replies; 67+ 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 related	[flat|nested] 67+ 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
                   ` (10 subsequent siblings)
  12 siblings, 1 reply; 67+ 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 related	[flat|nested] 67+ 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-13  0:03   ` Philippe Mathieu-Daudé
  2019-12-02 21:09 ` [PATCH 04/10] arm: allwinner-h3: add USB host controller Niek Linnenbank
                   ` (9 subsequent siblings)
  12 siblings, 1 reply; 67+ 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 related	[flat|nested] 67+ 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-10  7:56   ` Philippe Mathieu-Daudé
  2019-12-02 21:09 ` [PATCH 05/10] arm: allwinner-h3: add System Control module Niek Linnenbank
                   ` (8 subsequent siblings)
  12 siblings, 2 replies; 67+ 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 related	[flat|nested] 67+ 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-13  0:09   ` Philippe Mathieu-Daudé
  2019-12-02 21:09 ` [PATCH 06/10] arm/arm-powerctl: set NSACR.{CP11, CP10} bits in arm_set_cpu_on() Niek Linnenbank
                   ` (7 subsequent siblings)
  12 siblings, 1 reply; 67+ 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 related	[flat|nested] 67+ 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
                   ` (6 subsequent siblings)
  12 siblings, 1 reply; 67+ 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 related	[flat|nested] 67+ 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
                   ` (5 subsequent siblings)
  12 siblings, 0 replies; 67+ 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 related	[flat|nested] 67+ 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
                   ` (4 subsequent siblings)
  12 siblings, 1 reply; 67+ 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 related	[flat|nested] 67+ 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-11 22:34   ` Niek Linnenbank
  2019-12-02 21:09 ` [PATCH 10/10] arm: allwinner-h3: add EMAC ethernet device Niek Linnenbank
                   ` (3 subsequent siblings)
  12 siblings, 1 reply; 67+ 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 related	[flat|nested] 67+ 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é
                   ` (2 subsequent siblings)
  12 siblings, 1 reply; 67+ 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 related	[flat|nested] 67+ 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-09 21:37   ` Niek Linnenbank
  2019-12-03  9:02 ` Philippe Mathieu-Daudé
  2019-12-10 10:34 ` KONRAD Frederic
  12 siblings, 2 replies; 67+ 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] 67+ 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
  2019-12-10 10:34 ` KONRAD Frederic
  12 siblings, 1 reply; 67+ 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] 67+ 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; 67+ 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] 67+ 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; 67+ 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] 67+ 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
  2019-12-10  8:40     ` Philippe Mathieu-Daudé
  2019-12-09 21:37   ` Niek Linnenbank
  1 sibling, 1 reply; 67+ 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 --]

^ permalink raw reply	[flat|nested] 67+ 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; 67+ 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 --]

^ permalink raw reply	[flat|nested] 67+ 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; 67+ 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 --]

^ permalink raw reply	[flat|nested] 67+ 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; 67+ 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 --]

^ permalink raw reply	[flat|nested] 67+ 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; 67+ 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] 67+ 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; 67+ 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] 67+ 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; 67+ 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] 67+ 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
  2019-12-10  7:56   ` Philippe Mathieu-Daudé
  1 sibling, 1 reply; 67+ 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 --]

^ permalink raw reply	[flat|nested] 67+ 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
  2019-12-10  9:02   ` Philippe Mathieu-Daudé
  1 sibling, 1 reply; 67+ 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] 67+ 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; 67+ 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 --]

^ permalink raw reply	[flat|nested] 67+ 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; 67+ 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 --]

^ permalink raw reply	[flat|nested] 67+ 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; 67+ 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 --]

^ permalink raw reply	[flat|nested] 67+ 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; 67+ 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 --]

^ permalink raw reply	[flat|nested] 67+ 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é
  2019-12-06 22:15         ` Niek Linnenbank
  0 siblings, 1 reply; 67+ 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] 67+ 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
  2019-12-09 22:24       ` Aleksandar Markovic
  0 siblings, 1 reply; 67+ 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] 67+ 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
  2019-12-06 20:01     ` Niek Linnenbank
  0 siblings, 1 reply; 67+ 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] 67+ 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; 67+ 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] 67+ 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é
  2019-12-06 20:20       ` Niek Linnenbank
  0 siblings, 1 reply; 67+ 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] 67+ messages in thread

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

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

Hey Peter,

On Fri, Dec 6, 2019 at 3:25 PM Peter Maydell <peter.maydell@linaro.org>
wrote:

> 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!
>

Sure, I'm happy to help. Note that I only tested this fix with
the Allwinner H3 SoC patches that I'm working on.

OK, I'll keep an eye out for it. Once it is solved in master, I'll remove
this patch from the patch series.

Regards,
Niek

>
> -- PMM
>


-- 
Niek Linnenbank

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

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

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

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

Hey Peter, Philippe,

On Fri, Dec 6, 2019 at 5:35 PM Philippe Mathieu-Daudé <philmd@redhat.com>
wrote:

> 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.
>

Thank you both for clarifying this. OK, I'll update this patch such that
the identifier is fixed,
using the functions Philippe suggested.

The reason I originally chose to make it randomized is that U-Boot mainline
reads out the
SID data in order to create a MAC address for the ethernet device. So when
a user runs multiple
QEMU machines inside a virtualized network, they will get the same MACs if
the SID isnt unique.
However this problem can also be solved very easy with U-Boot itself by
just overriding the ethaddr environment variable.

For your interest, in the U-Boot source you can see this behaviour in the
file
arch/arm/mach-sunxi/cpu_info.c:139 in sunxi_get_sid(), sun8i_efuse_read()
where it reads the SID
and in board/sunxi/board.c:782 in setup_environment() where it uses the SID
to create the MAC address.

Regards,
Niek

-- 
Niek Linnenbank

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

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

* Re: [PATCH 02/10] hw: arm: add Xunlong Orange Pi PC machine
  2019-12-06  5:41       ` Philippe Mathieu-Daudé
@ 2019-12-06 22:15         ` Niek Linnenbank
  2019-12-10  8:59           ` Philippe Mathieu-Daudé
  0 siblings, 1 reply; 67+ messages in thread
From: Niek Linnenbank @ 2019-12-06 22:15 UTC (permalink / raw)
  To: Philippe Mathieu-Daudé
  Cc: Beniamino Galvani, Peter Maydell, qemu-arm, QEMU Developers

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

Hi Philippe,

On Fri, Dec 6, 2019 at 6:41 AM Philippe Mathieu-Daudé <philmd@redhat.com>
wrote:

> 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.
>

Hehe its okey, I can change it to whichever is preferable: the minimum set
with unimplemented device entries to get a working linux kernel / u-boot or
just cover the full memory space from the datasheet. My initial thought was
that if
we only provide the minimum set, and the linux kernel later adds a new
driver for a device
which is not marked unimplemented, it will trigger the data abort and
potentially resulting in a non-booting kernel.

But I'm not sure what is normally done here. I do see other board files
using the create_unimplemented_device() function,
but I dont know if they are covering the whole memory space or not.

Any thoughts? :-)


>
> > 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?
>
>
I discovered what goes wrong here. See this kernel oops message:

[    1.084985] [f08600f8] *pgd=6f00a811, *pte=01c28653, *ppte=01c28453
[    1.085564] Internal error: : 8 [#1] SMP ARM
[    1.085698] Modules linked in:
[    1.085940] CPU: 0 PID: 1 Comm: swapper/0 Not tainted
5.4.0-11747-g2f13437b8917 #4
[    1.085968] Hardware name: Allwinner sun8i Family
[    1.086447] PC is at dw8250_setup_port+0x10/0x10c
[    1.086478] LR is at dw8250_probe+0x500/0x56c

It tries to access the UART0 at base address 0x01c28400, which I did
provide. The strange
thing is that is accesses offset 0xf8, thus address 0x01c284f8. The
datasheet does not mention this register
but if we provide the 1KiB (0x400) I/O space it should at least read as
zero and writes ignored. Unfortunately the emulated
serial driver only maps a small portion until 0x1f:

(qemu) info mtree
...
    0000000001c28000-0000000001c2801f (prio 0, i/o): serial
    0000000001c28400-0000000001c2841f (prio 0, i/o): serial
    0000000001c28800-0000000001c2881f (prio 0, i/o): serial


Apparently, the register that the mainline linux kernel is using is
DesignWare specific:

drivers/tty/serial/8250/8250_dwlib.c:13:

/* Offsets for the DesignWare specific registers */
#define DW_UART_DLF<--->0xc0 /* Divisor Latch Fraction Register */
#define DW_UART_CPR<--->0xf4 /* Component Parameter Register */
#define DW_UART_UCV<--->0xf8 /* UART Component Version */

I tried to find a way to increase the memory mapped size of the serial
device I created with serial_mm_init(),
but I don't think its possible with that interface.

I did manage to get it working by overlaying the UART0 with another
unimplemented device
that does have an I/O size of 0x400, but I guess that is probably not the
solution we are looking for?

I wonder, did any of the other SoC / boards have this problem when removing
mc->ignore_memory_transaction_failures?

Regards,
Niek

> 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
> >
>
>

-- 
Niek Linnenbank

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

^ permalink raw reply	[flat|nested] 67+ 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
@ 2019-12-09 21:37   ` Niek Linnenbank
  2019-12-10  8:26     ` Philippe Mathieu-Daudé
  1 sibling, 1 reply; 67+ messages in thread
From: Niek Linnenbank @ 2019-12-09 21:37 UTC (permalink / raw)
  To: Philippe Mathieu-Daudé
  Cc: Beniamino Galvani, Peter Maydell, qemu-arm, Richard Henderson,
	QEMU Developers

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

Hi Philippe,

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

I'm trying to reproduce the error you reported here with my patch set on
latest master,
but so far without any result. The host OS I'm using is Ubuntu 18.04.3 LTS
on x86_64.
I ran several times using the same 4.20.7-sunxi kernel and same command
line.

Some questions that might help:
1) Are there any specific steps you did in order to produce this error?
2) Could this be a known / existing issue?
3) How many times did you see this error?
4) Are you also using Ubuntu 18.04.3 LTS on x86_64, or a different host OS?

Regards,
Niek


>
> (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

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

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

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

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

On Friday, December 6, 2019, Peter Maydell <peter.maydell@linaro.org> wrote:

> 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"...
>
>
Peter,

This is one of the longest definitions of a category (in fact, most likely,
the longest) that I have heard in my life. ;))

Aleksandar



> thanks
> -- PMM
>
>

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

^ permalink raw reply	[flat|nested] 67+ 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-10  7:56   ` Philippe Mathieu-Daudé
  2019-12-10  8:29     ` Gerd Hoffmann
  1 sibling, 1 reply; 67+ messages in thread
From: Philippe Mathieu-Daudé @ 2019-12-10  7:56 UTC (permalink / raw)
  To: Niek Linnenbank, qemu-devel, Gerd Hoffmann
  Cc: b.galvani, peter.maydell, qemu-arm

On 12/2/19 10:09 PM, Niek Linnenbank 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>
> ---
>   hw/arm/allwinner-h3.c    | 20 ++++++++++++++++++++
>   hw/usb/hcd-ehci-sysbus.c | 17 +++++++++++++++++
>   hw/usb/hcd-ehci.h        |  1 +

Cc'ing Gerd, the maintainer of these files.

Reviewed-by: Philippe Mathieu-Daudé <philmd@redhat.com>

>   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"
> 



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

* Re: [PATCH 00/10] Add Allwinner H3 SoC and Orange Pi PC Machine
  2019-12-09 21:37   ` Niek Linnenbank
@ 2019-12-10  8:26     ` Philippe Mathieu-Daudé
  2019-12-10 20:12       ` Niek Linnenbank
  0 siblings, 1 reply; 67+ messages in thread
From: Philippe Mathieu-Daudé @ 2019-12-10  8:26 UTC (permalink / raw)
  To: Niek Linnenbank
  Cc: Beniamino Galvani, Peter Maydell, qemu-arm, Richard Henderson,
	QEMU Developers

On 12/9/19 10:37 PM, Niek Linnenbank wrote:
> Hi Philippe,
> 
> On Tue, Dec 3, 2019 at 9:47 AM Philippe Mathieu-Daudé <philmd@redhat.com 
> <mailto: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)
> 
> 
> I'm trying to reproduce the error you reported here with my patch set on 
> latest master,
> but so far without any result. The host OS I'm using is Ubuntu 18.04.3 
> LTS on x86_64.
> I ran several times using the same 4.20.7-sunxi kernel and same command 
> line.
> 
> Some questions that might help:
> 1) Are there any specific steps you did in order to produce this error?

I build QEMU with:

./configure --enable-trace-backends=log --extra-cflags=-ggdb --enable-debug

> 2) Could this be a known / existing issue?
> 3) How many times did you see this error?

Always

> 4) Are you also using Ubuntu 18.04.3 LTS on x86_64, or a different host OS?

Host is Fedora 30.

> 
> Regards,
> Niek



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

* Re: [PATCH 04/10] arm: allwinner-h3: add USB host controller
  2019-12-10  7:56   ` Philippe Mathieu-Daudé
@ 2019-12-10  8:29     ` Gerd Hoffmann
  2019-12-10 19:11       ` Niek Linnenbank
  0 siblings, 1 reply; 67+ messages in thread
From: Gerd Hoffmann @ 2019-12-10  8:29 UTC (permalink / raw)
  To: Philippe Mathieu-Daudé
  Cc: b.galvani, peter.maydell, Niek Linnenbank, qemu-arm, qemu-devel

On Tue, Dec 10, 2019 at 08:56:02AM +0100, Philippe Mathieu-Daudé wrote:
> On 12/2/19 10:09 PM, Niek Linnenbank 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>
> > ---
> >   hw/arm/allwinner-h3.c    | 20 ++++++++++++++++++++
> >   hw/usb/hcd-ehci-sysbus.c | 17 +++++++++++++++++
> >   hw/usb/hcd-ehci.h        |  1 +
> 
> Cc'ing Gerd, the maintainer of these files.

Looks all reasonable.
Reviewed-by: Gerd Hoffmann <kraxel@redhat.com>

(assuming this will be merged through arm tree not usb).

> 
> Reviewed-by: Philippe Mathieu-Daudé <philmd@redhat.com>
> 
> >   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"
> > 
> 



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

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

Cc'ing Alex.

On 12/3/19 8:25 PM, Niek Linnenbank wrote:
> 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 
> <mailto: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)

To have ELF debug info I built a Linux kernel around v5.5-rc1 (commit 
2f13437b8) with:

$ make ARCH=arm CROSS_COMPILE=arm-linux-gnu- sunxi_defconfig zImage

Then I applied Alex patch and mine on top of your series:
https://www.mail-archive.com/qemu-devel@nongnu.org/msg663850.html
https://www.mail-archive.com/qemu-devel@nongnu.org/msg663843.html

Running with -d in_asm,cpu:

----------------
IN:
0xc0102128:  e281c018  add      ip, r1, #0x18
0xc010212c:  e8ac6ff0  stm      ip!, {r4, r5, r6, r7, r8, sb, sl, fp, 
sp, lr}
0xc0102130:  e592405c  ldr      r4, [r2, #0x5c]
0xc0102134:  e5925060  ldr      r5, [r2, #0x60]
0xc0102138:  ee1d7f50  mrc      p15, #0, r7, c13, c0, #2
0xc010213c:  ee0d4f70  mcr      p15, #0, r4, c13, c0, #3

R00=c0a07c40 R01=c0a00000 R02=c685a000 R03=00000000
R04=c6ea7cc0 R05=c0a07c40 R06=c68405c0 R07=c0958cc0
R08=00000000 R09=0654f000 R10=c0701e8c R11=c0a01f94
R12=00000000 R13=c0a01f50 R14=c06df2fc R15=c0102128
PSR=600000d3 -ZC- A NS svc32
----------------
IN:
0xc0102140:  ee0d5f50  mcr      p15, #0, r5, c13, c0, #2

R00=c0a07c40 R01=c0a00000 R02=c685a000 R03=00000000
R04=00000000 R05=00000000 R06=c68405c0 R07=00000000
R08=00000000 R09=0654f000 R10=c0701e8c R11=c0a01f94
R12=c0a00040 R13=c0a01f50 R14=c06df2fc R15=c0102140
PSR=600000d3 -ZC- A NS svc32
----------------
TCG hflags mismatch (current:0x33600000 rebuilt:0x20200040)
Aborted (core dumped)

(gdb) disas __switch_to
Dump of assembler code for function __switch_to:
    0xc0102128 <+0>:     add     r12, r1, #24
    0xc010212c <+4>:     stmia   r12!, {r4, r5, r6, r7, r8, r9, r10, 
r11, sp, lr}
    0xc0102130 <+8>:     ldr     r4, [r2, #92]   ; 0x5c
    0xc0102134 <+12>:    ldr     r5, [r2, #96]   ; 0x60
    0xc0102138 <+16>:    mrc     15, 0, r7, cr13, cr0, {2}
    0xc010213c <+20>:    mcr     15, 0, r4, cr13, cr0, {3}
    0xc0102140 <+24>:    mcr     15, 0, r5, cr13, cr0, {2}
    0xc0102144 <+28>:    str     r7, [r1, #96]   ; 0x60
    0xc0102148 <+32>:    mov     r5, r0
    0xc010214c <+36>:    add     r4, r2, #24
    0xc0102150 <+40>:    ldr     r0, [pc, #12]   ; 0xc0102164
    0xc0102154 <+44>:    mov     r1, #2
    0xc0102158 <+48>:    bl      0xc013e348 <atomic_notifier_call_chain>
    0xc010215c <+52>:    mov     r0, r5
    0xc0102160 <+56>:    ldm     r4, {r4, r5, r6, r7, r8, r9, r10, r11, 
sp, pc}
(gdb) x/10i 0xc06df2f0 - 12
    0xc06df2e4 <__schedule+532>: mov     r0, r5
    0xc06df2e8 <__schedule+536>: bic     r3, r3, #3
    0xc06df2ec <__schedule+540>: str     r3, [r4, #1376] ; 0x560
    0xc06df2f0 <__schedule+544>: ldr     r2, [r6, #4]
    0xc06df2f4 <__schedule+548>: ldr     r1, [r5, #4]
    0xc06df2f8 <__schedule+552>: bl      0xc0102128 <__switch_to>
    0xc06df2fc <__schedule+556>: bl      0xc0141f00 <finish_task_switch>
    0xc06df300 <__schedule+560>: mov     r4, r0
    0xc06df304 <__schedule+564>: ldr     r3, [r4, #1452] ; 0x5ac
    0xc06df308 <__schedule+568>: cmp     r3, #0

Note from patch #1:

CPU is cortex-a7 with:

     object_property_set_bool(cpuobj,
                              true, "has_el3", NULL);
     object_property_set_bool(cpuobj,
                              true, "has_el2", NULL);

> 
>     (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/



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

* Re: [PATCH 02/10] hw: arm: add Xunlong Orange Pi PC machine
  2019-12-06 22:15         ` Niek Linnenbank
@ 2019-12-10  8:59           ` Philippe Mathieu-Daudé
  2019-12-10 19:14             ` Niek Linnenbank
  0 siblings, 1 reply; 67+ messages in thread
From: Philippe Mathieu-Daudé @ 2019-12-10  8:59 UTC (permalink / raw)
  To: Niek Linnenbank
  Cc: Beniamino Galvani, Peter Maydell, qemu-arm, QEMU Developers

On 12/6/19 11:15 PM, Niek Linnenbank wrote:
[...]
>      >      > +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.
> 
> 
> Hehe its okey, I can change it to whichever is preferable: the minimum set
> with unimplemented device entries to get a working linux kernel / u-boot or
> just cover the full memory space from the datasheet. My initial thought 
> was that if
> we only provide the minimum set, and the linux kernel later adds a new 
> driver for a device
> which is not marked unimplemented, it will trigger the data abort and 
> potentially resulting in a non-booting kernel.
> 
> But I'm not sure what is normally done here. I do see other board files 
> using the create_unimplemented_device() function,
> but I dont know if they are covering the whole memory space or not.

If they don't cover all memory regions, the guest code can trigger a 
data abort indeed. Since we don't know the memory layout of old board, 
they are still supported with ignore_memory_transaction_failures=true, 
so guest still run unaffected.
We expect new boards to implement the minimum layout.
As long as your guest is happy and usable, UNIMP devices are fine, 
either as big region or individual device (this requires someone with 
access to the datasheet to verify). If you can add a UNIMP for each 
device - which is what you did - it is even better because the the unimp 
access log will be more useful, having finer granularity.

 > I added all the missing devices as unimplemented and removed the
 > ignore_memory_transaction_failures flag

IOW, you already did the best you could do :)

>      > 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?
> 
> 
> I discovered what goes wrong here. See this kernel oops message:
> 
> [    1.084985] [f08600f8] *pgd=6f00a811, *pte=01c28653, *ppte=01c28453
> [    1.085564] Internal error: : 8 [#1] SMP ARM
> [    1.085698] Modules linked in:
> [    1.085940] CPU: 0 PID: 1 Comm: swapper/0 Not tainted 5.4.0-11747-g2f13437b8917 #4
> [    1.085968] Hardware name: Allwinner sun8i Family
> [    1.086447] PC is at dw8250_setup_port+0x10/0x10c
> [    1.086478] LR is at dw8250_probe+0x500/0x56c
> 
> It tries to access the UART0 at base address 0x01c28400, which I did 
> provide. The strange
> thing is that is accesses offset 0xf8, thus address 0x01c284f8. The 
> datasheet does not mention this register
> but if we provide the 1KiB (0x400) I/O space it should at least read as 
> zero and writes ignored. Unfortunately the emulated
> serial driver only maps a small portion until 0x1f:
> 
> (qemu) info mtree
> ...
>      0000000001c28000-0000000001c2801f (prio 0, i/o): serial
>      0000000001c28400-0000000001c2841f (prio 0, i/o): serial
>      0000000001c28800-0000000001c2881f (prio 0, i/o): serial
> 
> 
> Apparently, the register that the mainline linux kernel is using is 
> DesignWare specific:
> 
> drivers/tty/serial/8250/8250_dwlib.c:13:
> 
> /* Offsets for the DesignWare specific registers */
> #define DW_UART_DLF<--->0xc0 /* Divisor Latch Fraction Register */
> #define DW_UART_CPR<--->0xf4 /* Component Parameter Register */
> #define DW_UART_UCV<--->0xf8 /* UART Component Version */
> 
> I tried to find a way to increase the memory mapped size of the serial 
> device I created with serial_mm_init(),
> but I don't think its possible with that interface.
> 
> I did manage to get it working by overlaying the UART0 with another 
> unimplemented device
> that does have an I/O size of 0x400, but I guess that is probably not 
> the solution we are looking for?

IMHO this is the correct solution :)

Memory regions have priority. By default a device has priority 0, and 
UNIMP device has priority -1000.

So you can use:

    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);
    create_unimplemented_device("DesignWare-uart",\
                                AW_H3_UART0_REG_BASE,
                                0x400);

(Or cleaner, add a create_designware_uart(...) function that does both).

(qemu) info mtree
...
    0000000001c28000-0000000001c2801f (prio 0, i/o): serial
    0000000001c28000-0000000001c283ff (prio -1000, i/o): DesignWare-uart

You could create an UNIMP region of 0x400 - 0x20 = 0x3e0, but that would 
appear this is a different device, so I don't recommend that.

 > I wonder, did any of the other SoC / boards have this problem when
 > removing mc->ignore_memory_transaction_failures?



^ permalink raw reply	[flat|nested] 67+ 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-10  9:02   ` Philippe Mathieu-Daudé
  2019-12-10 19:17     ` Niek Linnenbank
  1 sibling, 1 reply; 67+ messages in thread
From: Philippe Mathieu-Daudé @ 2019-12-10  9:02 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 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>
> ---
[...]
> +
> +    /* UART */
> +    if (serial_hd(0)) {

As the uart0 is always mapped in the SoC, don't use 'if serial_hd()', 
instead map it regardless a console is connected.

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



^ permalink raw reply	[flat|nested] 67+ 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
                   ` (11 preceding siblings ...)
  2019-12-03  9:02 ` Philippe Mathieu-Daudé
@ 2019-12-10 10:34 ` KONRAD Frederic
  2019-12-10 19:55   ` Niek Linnenbank
  12 siblings, 1 reply; 67+ messages in thread
From: KONRAD Frederic @ 2019-12-10 10:34 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 :
> 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/

Works well on my side with vanilla linux-4.9.13 built with gcc-8.3.0 + busybox
and sun8i-h3-orangepi-one.dtb.

Tested-by: KONRAD Frederic <frederic.konrad@adacore.com>

> 
> 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
> 


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

* Re: [PATCH 04/10] arm: allwinner-h3: add USB host controller
  2019-12-10  8:29     ` Gerd Hoffmann
@ 2019-12-10 19:11       ` Niek Linnenbank
  0 siblings, 0 replies; 67+ messages in thread
From: Niek Linnenbank @ 2019-12-10 19:11 UTC (permalink / raw)
  To: Gerd Hoffmann
  Cc: Beniamino Galvani, Peter Maydell, qemu-arm,
	Philippe Mathieu-Daudé,
	QEMU Developers

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

Hi Gerd,

On Tue, Dec 10, 2019 at 9:29 AM Gerd Hoffmann <kraxel@redhat.com> wrote:

> On Tue, Dec 10, 2019 at 08:56:02AM +0100, Philippe Mathieu-Daudé wrote:
> > On 12/2/19 10:09 PM, Niek Linnenbank 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>
> > > ---
> > >   hw/arm/allwinner-h3.c    | 20 ++++++++++++++++++++
> > >   hw/usb/hcd-ehci-sysbus.c | 17 +++++++++++++++++
> > >   hw/usb/hcd-ehci.h        |  1 +
> >
> > Cc'ing Gerd, the maintainer of these files.
>
> Looks all reasonable.
> Reviewed-by: Gerd Hoffmann <kraxel@redhat.com>
>
> (assuming this will be merged through arm tree not usb).
>

Thanks for reviewing! I'll add the tag to the commit message.

Regards,
Niek

>
> >
> > Reviewed-by: Philippe Mathieu-Daudé <philmd@redhat.com>
> >
> > >   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"
> > >
> >
>
>

-- 
Niek Linnenbank

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

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

* Re: [PATCH 02/10] hw: arm: add Xunlong Orange Pi PC machine
  2019-12-10  8:59           ` Philippe Mathieu-Daudé
@ 2019-12-10 19:14             ` Niek Linnenbank
  0 siblings, 0 replies; 67+ messages in thread
From: Niek Linnenbank @ 2019-12-10 19:14 UTC (permalink / raw)
  To: Philippe Mathieu-Daudé
  Cc: Beniamino Galvani, Peter Maydell, qemu-arm, QEMU Developers

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

Hi Philippe,

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

> On 12/6/19 11:15 PM, Niek Linnenbank wrote:
> [...]
> >      >      > +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.
> >
> >
> > Hehe its okey, I can change it to whichever is preferable: the minimum
> set
> > with unimplemented device entries to get a working linux kernel / u-boot
> or
> > just cover the full memory space from the datasheet. My initial thought
> > was that if
> > we only provide the minimum set, and the linux kernel later adds a new
> > driver for a device
> > which is not marked unimplemented, it will trigger the data abort and
> > potentially resulting in a non-booting kernel.
> >
> > But I'm not sure what is normally done here. I do see other board files
> > using the create_unimplemented_device() function,
> > but I dont know if they are covering the whole memory space or not.
>
> If they don't cover all memory regions, the guest code can trigger a
> data abort indeed. Since we don't know the memory layout of old board,
> they are still supported with ignore_memory_transaction_failures=true,
> so guest still run unaffected.
> We expect new boards to implement the minimum layout.
> As long as your guest is happy and usable, UNIMP devices are fine,
> either as big region or individual device (this requires someone with
> access to the datasheet to verify). If you can add a UNIMP for each
> device - which is what you did - it is even better because the the unimp
> access log will be more useful, having finer granularity.
>
>  > I added all the missing devices as unimplemented and removed the
>  > ignore_memory_transaction_failures flag
>
> IOW, you already did the best you could do :)
>
> >      > 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?
> >
> >
> > I discovered what goes wrong here. See this kernel oops message:
> >
> > [    1.084985] [f08600f8] *pgd=6f00a811, *pte=01c28653, *ppte=01c28453
> > [    1.085564] Internal error: : 8 [#1] SMP ARM
> > [    1.085698] Modules linked in:
> > [    1.085940] CPU: 0 PID: 1 Comm: swapper/0 Not tainted
> 5.4.0-11747-g2f13437b8917 #4
> > [    1.085968] Hardware name: Allwinner sun8i Family
> > [    1.086447] PC is at dw8250_setup_port+0x10/0x10c
> > [    1.086478] LR is at dw8250_probe+0x500/0x56c
> >
> > It tries to access the UART0 at base address 0x01c28400, which I did
> > provide. The strange
> > thing is that is accesses offset 0xf8, thus address 0x01c284f8. The
> > datasheet does not mention this register
> > but if we provide the 1KiB (0x400) I/O space it should at least read as
> > zero and writes ignored. Unfortunately the emulated
> > serial driver only maps a small portion until 0x1f:
> >
> > (qemu) info mtree
> > ...
> >      0000000001c28000-0000000001c2801f (prio 0, i/o): serial
> >      0000000001c28400-0000000001c2841f (prio 0, i/o): serial
> >      0000000001c28800-0000000001c2881f (prio 0, i/o): serial
> >
> >
> > Apparently, the register that the mainline linux kernel is using is
> > DesignWare specific:
> >
> > drivers/tty/serial/8250/8250_dwlib.c:13:
> >
> > /* Offsets for the DesignWare specific registers */
> > #define DW_UART_DLF<--->0xc0 /* Divisor Latch Fraction Register */
> > #define DW_UART_CPR<--->0xf4 /* Component Parameter Register */
> > #define DW_UART_UCV<--->0xf8 /* UART Component Version */
> >
> > I tried to find a way to increase the memory mapped size of the serial
> > device I created with serial_mm_init(),
> > but I don't think its possible with that interface.
> >
> > I did manage to get it working by overlaying the UART0 with another
> > unimplemented device
> > that does have an I/O size of 0x400, but I guess that is probably not
> > the solution we are looking for?
>
> IMHO this is the correct solution :)
>
> Memory regions have priority. By default a device has priority 0, and
> UNIMP device has priority -1000.
>
> So you can use:
>
>     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);
>     create_unimplemented_device("DesignWare-uart",\
>                                 AW_H3_UART0_REG_BASE,
>                                 0x400);
>
>
Now it makes much more sense to me, thanks a lot for explaining this!

Allright, I'll use this approach to finish the work for removing
mc->ignore_memory_transaction_failures.

Regards,
Niek


> (Or cleaner, add a create_designware_uart(...) function that does both).
>
> (qemu) info mtree
> ...
>     0000000001c28000-0000000001c2801f (prio 0, i/o): serial
>     0000000001c28000-0000000001c283ff (prio -1000, i/o): DesignWare-uart
>
> You could create an UNIMP region of 0x400 - 0x20 = 0x3e0, but that would
> appear this is a different device, so I don't recommend that.
>
>  > I wonder, did any of the other SoC / boards have this problem when
>  > removing mc->ignore_memory_transaction_failures?
>
>

-- 
Niek Linnenbank

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

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

* Re: [PATCH 01/10] hw: arm: add Allwinner H3 System-on-Chip
  2019-12-10  9:02   ` Philippe Mathieu-Daudé
@ 2019-12-10 19:17     ` Niek Linnenbank
  0 siblings, 0 replies; 67+ messages in thread
From: Niek Linnenbank @ 2019-12-10 19:17 UTC (permalink / raw)
  To: Philippe Mathieu-Daudé
  Cc: Beniamino Galvani, Peter Maydell, qemu-arm, QEMU Developers

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

Hi Philippe,

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

> 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>
> > ---
> [...]
> > +
> > +    /* UART */
> > +    if (serial_hd(0)) {
>
> As the uart0 is always mapped in the SoC, don't use 'if serial_hd()',
> instead map it regardless a console is connected.
>

Indeed, the UARTs should always be mapped for this SoC.
Noted, I'll solve this too for in the v2 patch update.

Regards,
Niek


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

-- 
Niek Linnenbank

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

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

* Re: [PATCH 00/10] Add Allwinner H3 SoC and Orange Pi PC Machine
  2019-12-10 10:34 ` KONRAD Frederic
@ 2019-12-10 19:55   ` Niek Linnenbank
  0 siblings, 0 replies; 67+ messages in thread
From: Niek Linnenbank @ 2019-12-10 19:55 UTC (permalink / raw)
  To: KONRAD Frederic
  Cc: Beniamino Galvani, Peter Maydell, qemu-arm, QEMU Developers

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

Hi Frederic,

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

>
>
> Le 12/2/19 à 10:09 PM, Niek Linnenbank a écrit :
> > 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/
>
> Works well on my side with vanilla linux-4.9.13 built with gcc-8.3.0 +
> busybox
> and sun8i-h3-orangepi-one.dtb.
>

> Tested-by: KONRAD Frederic <frederic.konrad@adacore.com>
>
> Thank you for testing! Great, I'll add the tag for the next v2 of the
patches.

Regards,
Niek


> >
> > 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
> >
>


-- 
Niek Linnenbank

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

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

* Re: [PATCH 00/10] Add Allwinner H3 SoC and Orange Pi PC Machine
  2019-12-10  8:26     ` Philippe Mathieu-Daudé
@ 2019-12-10 20:12       ` Niek Linnenbank
  2019-12-12 23:07         ` Niek Linnenbank
  0 siblings, 1 reply; 67+ messages in thread
From: Niek Linnenbank @ 2019-12-10 20:12 UTC (permalink / raw)
  To: Philippe Mathieu-Daudé
  Cc: Beniamino Galvani, Peter Maydell, qemu-arm, Richard Henderson,
	QEMU Developers

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

Hi Philippe,

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

> On 12/9/19 10:37 PM, Niek Linnenbank wrote:
> > Hi Philippe,
> >
> > On Tue, Dec 3, 2019 at 9:47 AM Philippe Mathieu-Daudé <philmd@redhat.com
> > <mailto: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)
> >
> >
> > I'm trying to reproduce the error you reported here with my patch set on
> > latest master,
> > but so far without any result. The host OS I'm using is Ubuntu 18.04.3
> > LTS on x86_64.
> > I ran several times using the same 4.20.7-sunxi kernel and same command
> > line.
> >
> > Some questions that might help:
> > 1) Are there any specific steps you did in order to produce this error?
>
> I build QEMU with:
>
> ./configure --enable-trace-backends=log --extra-cflags=-ggdb --enable-debug
>
> > 2) Could this be a known / existing issue?
> > 3) How many times did you see this error?
>
> Always
>
> > 4) Are you also using Ubuntu 18.04.3 LTS on x86_64, or a different host
> OS?
>
> Host is Fedora 30.
>

OK thanks, I will try again using the info above after I finished reworking
the other patch comments.

Niek


>
> >
> > Regards,
> > Niek
>
>

-- 
Niek Linnenbank

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

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

* Re: [PATCH 09/10] arm: allwinner-h3: add SD/MMC host controller
  2019-12-02 21:09 ` [PATCH 09/10] arm: allwinner-h3: add SD/MMC host controller Niek Linnenbank
@ 2019-12-11 22:34   ` Niek Linnenbank
  2019-12-12 23:56     ` Philippe Mathieu-Daudé
  0 siblings, 1 reply; 67+ messages in thread
From: Niek Linnenbank @ 2019-12-11 22:34 UTC (permalink / raw)
  To: QEMU Developers, Philippe Mathieu-Daudé
  Cc: Beniamino Galvani, Peter Maydell, qemu-arm

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

Ping!

Anyone would like to comment on this driver?

I finished the rework on all previous comments in this series.

Currently debugging the hflags error reported by Philippe.
After that, I'm ready to send out v2 of these patches.

Regards,
Niek

On Mon, Dec 2, 2019 at 10:10 PM Niek Linnenbank <nieklinnenbank@gmail.com>
wrote:

> 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
>
>

-- 
Niek Linnenbank

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

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

* Re: [PATCH 00/10] Add Allwinner H3 SoC and Orange Pi PC Machine
  2019-12-10 20:12       ` Niek Linnenbank
@ 2019-12-12 23:07         ` Niek Linnenbank
  2019-12-12 23:25           ` Philippe Mathieu-Daudé
  0 siblings, 1 reply; 67+ messages in thread
From: Niek Linnenbank @ 2019-12-12 23:07 UTC (permalink / raw)
  To: Philippe Mathieu-Daudé
  Cc: Beniamino Galvani, Peter Maydell, qemu-arm, Richard Henderson,
	QEMU Developers

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

Hi Philippe,

I have discovered that the hflags assertion error you reported is not
caused by the Allwinner H3
patches but actually an existing problem. What I did is to use the latest
master (v4.2.0 tag) without any patches applied.
and tried to boot the raspi2 machine with and without debugging enabled.
Without debuggin, the raspi2
machine runs fine and can boot the 5.4.2 linux kernel. With debugging
enabled, the same hflags error shows.

To reproduce it, build Linux 5.4.2 with the bmc2835_defconfig:

$ ARCH=arm CROSS_COMPILE=arm-linux-gnueabi- make mrproper
$ ARCH=arm CROSS_COMPILE=arm-linux-gnueabi- make bcm2835_defconfig
$ ARCH=arm CROSS_COMPILE=arm-linux-gnueabi- make -j5
...
First build QEMU without debugging and try to boot linux:
$ ./configure --target-list=arm-softmmu; make clean; make -j5
$ ./arm-softmmu/qemu-system-arm -M raspi2 \
  -kernel $HOME/linux-5.4.2/arch/arm/boot/zImage \
  -append 'console=ttyAMA0,115200 earlyprintk debug' \
  -dtb $HOME/linux-5.4.2/arch/arm/boot/dts/bcm2836-rpi-2-b.dtb \
  -m 1024 -nographic -s
[    0.000000] Booting Linux on physical CPU 0x0
[    0.000000] Linux version 5.4.2 (me@host) (gcc version 7.4.0
(Ubuntu/Linaro 7.4.0-1ubuntu1~18.04.1)) #1 Thu Dec 12 22:49:14 CET
2019
[    0.000000] CPU: ARMv7 Processor [410fc075] revision 5 (ARMv7), cr=10c53c7d
...
Then rebuild QEMU with debugging enabled and again try to boot linux:
$ ./configure --target-list=arm-softmmu --enable-debug
--extra-cflags=-ggdb; make clean; make -j5
$ ./arm-softmmu/qemu-system-arm -M raspi2 \
  -kernel $HOME/linux-5.4.2/arch/arm/boot/zImage \
  -append 'console=ttyAMA0,115200 earlyprintk debug' \
  -dtb $HOME/linux-5.4.2/arch/arm/boot/dts/bcm2836-rpi-2-b.dtb \
  -m 1024 -nographic -s
qemu-system-arm: /home/me/qemu/target/arm/helper.c:11359:
cpu_get_tb_cpu_state: Assertion `flags ==
rebuild_hflags_internal(env)' failed.
qemu-system-arm: /home/me/qemu/target/arm/helper.c:11359:
cpu_get_tb_cpu_state: Assertion `flags ==
rebuild_hflags_internal(env)' failed.
qemu-system-arm: /home/me/qemu/target/arm/helper.c:11359:
cpu_get_tb_cpu_state: Assertion `flags ==
rebuild_hflags_internal(env)' failed.
Aborted (core dumped)

$ git describe
v4.2.0


What should be the next step? Should this be reported as a bug?

Regards,
Niek

On Tue, Dec 10, 2019 at 9:12 PM Niek Linnenbank <nieklinnenbank@gmail.com>
wrote:

> Hi Philippe,
>
> On Tue, Dec 10, 2019 at 9:26 AM Philippe Mathieu-Daudé <philmd@redhat.com>
> wrote:
>
>> On 12/9/19 10:37 PM, Niek Linnenbank wrote:
>> > Hi Philippe,
>> >
>> > On Tue, Dec 3, 2019 at 9:47 AM Philippe Mathieu-Daudé <
>> philmd@redhat.com
>> > <mailto: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)
>> >
>> >
>> > I'm trying to reproduce the error you reported here with my patch set
>> on
>> > latest master,
>> > but so far without any result. The host OS I'm using is Ubuntu 18.04.3
>> > LTS on x86_64.
>> > I ran several times using the same 4.20.7-sunxi kernel and same command
>> > line.
>> >
>> > Some questions that might help:
>> > 1) Are there any specific steps you did in order to produce this error?
>>
>> I build QEMU with:
>>
>> ./configure --enable-trace-backends=log --extra-cflags=-ggdb
>> --enable-debug
>>
>> > 2) Could this be a known / existing issue?
>> > 3) How many times did you see this error?
>>
>> Always
>>
>> > 4) Are you also using Ubuntu 18.04.3 LTS on x86_64, or a different host
>> OS?
>>
>> Host is Fedora 30.
>>
>
> OK thanks, I will try again using the info above after I finished
> reworking the other patch comments.
>
> Niek
>
>
>>
>> >
>> > Regards,
>> > Niek
>>
>>
>
> --
> Niek Linnenbank
>
>

-- 
Niek Linnenbank

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

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

* Re: [PATCH 00/10] Add Allwinner H3 SoC and Orange Pi PC Machine
  2019-12-12 23:07         ` Niek Linnenbank
@ 2019-12-12 23:25           ` Philippe Mathieu-Daudé
  2019-12-13 20:45             ` Niek Linnenbank
  0 siblings, 1 reply; 67+ messages in thread
From: Philippe Mathieu-Daudé @ 2019-12-12 23:25 UTC (permalink / raw)
  To: Niek Linnenbank, Alex Bennée
  Cc: Beniamino Galvani, Peter Maydell, qemu-arm, Richard Henderson,
	QEMU Developers

Cc'ing Alex.

On 12/13/19 12:07 AM, Niek Linnenbank wrote:
> Hi Philippe,
> 
> I have discovered that the hflags assertion error you reported is not 
> caused by the Allwinner H3
> patches but actually an existing problem. What I did is to use the 
> latest master (v4.2.0 tag) without any patches applied.
> and tried to boot the raspi2 machine with and without debugging enabled. 
> Without debuggin, the raspi2
> machine runs fine and can boot the 5.4.2 linux kernel. With debugging 
> enabled, the same hflags error shows.

This might be the same bug I hit last week... Alex suggested a patch:
https://www.mail-archive.com/qemu-devel@nongnu.org/msg664500.html

Do you mind to try it?

If it still fails, you might also add this one on top:
https://www.mail-archive.com/qemu-devel@nongnu.org/msg663843.html
and report the error.

> 
> To reproduce it, build Linux 5.4.2 with the bmc2835_defconfig:
> 
> $ ARCH=arm CROSS_COMPILE=arm-linux-gnueabi- make mrproper
> $ ARCH=arm CROSS_COMPILE=arm-linux-gnueabi- make bcm2835_defconfig
> $ ARCH=arm CROSS_COMPILE=arm-linux-gnueabi- make -j5
> ...
> 
> First build QEMU without debugging and try to boot linux:
> $ ./configure --target-list=arm-softmmu; make clean; make -j5
> $ ./arm-softmmu/qemu-system-arm -M raspi2 \
>    -kernel $HOME/linux-5.4.2/arch/arm/boot/zImage \
>    -append 'console=ttyAMA0,115200 earlyprintk debug' \
>    -dtb $HOME/linux-5.4.2/arch/arm/boot/dts/bcm2836-rpi-2-b.dtb \
>    -m 1024 -nographic -s
> [    0.000000] Booting Linux on physical CPU 0x0
> [    0.000000] Linux version 5.4.2 (me@host) (gcc version 7.4.0 (Ubuntu/Linaro 7.4.0-1ubuntu1~18.04.1)) #1 Thu Dec 12 22:49:14 CET 2019
> [    0.000000] CPU: ARMv7 Processor [410fc075] revision 5 (ARMv7), cr=10c53c7d
> ...
> 
> Then rebuild QEMU with debugging enabled and again try to boot linux:
> $ ./configure --target-list=arm-softmmu --enable-debug --extra-cflags=-ggdb; make clean; make -j5
> $ ./arm-softmmu/qemu-system-arm -M raspi2 \
>    -kernel $HOME/linux-5.4.2/arch/arm/boot/zImage \
>    -append 'console=ttyAMA0,115200 earlyprintk debug' \
>    -dtb $HOME/linux-5.4.2/arch/arm/boot/dts/bcm2836-rpi-2-b.dtb \
>    -m 1024 -nographic -s
> qemu-system-arm: /home/me/qemu/target/arm/helper.c:11359: cpu_get_tb_cpu_state: Assertion `flags == rebuild_hflags_internal(env)' failed.
> qemu-system-arm: /home/me/qemu/target/arm/helper.c:11359: cpu_get_tb_cpu_state: Assertion `flags == rebuild_hflags_internal(env)' failed.
> qemu-system-arm: /home/me/qemu/target/arm/helper.c:11359: cpu_get_tb_cpu_state: Assertion `flags == rebuild_hflags_internal(env)' failed.
> Aborted (core dumped)
> 
> $ git describe
> v4.2.0
> 
> 
> What should be the next step? Should this be reported as a bug?

In this case we might already have the fix, but if Alex patch doesn't 
help, you are always welcome to open a bug report:
https://bugs.launchpad.net/qemu/+filebug
This help to have notes/progress gathered.

> On Tue, Dec 10, 2019 at 9:12 PM Niek Linnenbank 
> <nieklinnenbank@gmail.com <mailto:nieklinnenbank@gmail.com>> wrote:
> 
>     Hi Philippe,
> 
>     On Tue, Dec 10, 2019 at 9:26 AM Philippe Mathieu-Daudé
>     <philmd@redhat.com <mailto:philmd@redhat.com>> wrote:
> 
>         On 12/9/19 10:37 PM, Niek Linnenbank wrote:
>          > Hi Philippe,
>          >
>          > On Tue, Dec 3, 2019 at 9:47 AM Philippe Mathieu-Daudé
>         <philmd@redhat.com <mailto:philmd@redhat.com>
>          > <mailto:philmd@redhat.com <mailto: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)
>          >
>          >
>          > I'm trying to reproduce the error you reported here with my
>         patch set on
>          > latest master,
>          > but so far without any result. The host OS I'm using is
>         Ubuntu 18.04.3
>          > LTS on x86_64.
>          > I ran several times using the same 4.20.7-sunxi kernel and
>         same command
>          > line.
>          >
>          > Some questions that might help:
>          > 1) Are there any specific steps you did in order to produce
>         this error?
> 
>         I build QEMU with:
> 
>         ./configure --enable-trace-backends=log --extra-cflags=-ggdb
>         --enable-debug
> 
>          > 2) Could this be a known / existing issue?
>          > 3) How many times did you see this error?
> 
>         Always
> 
>          > 4) Are you also using Ubuntu 18.04.3 LTS on x86_64, or a
>         different host OS?
> 
>         Host is Fedora 30.
> 
> 
>     OK thanks, I will try again using the info above after I finished
>     reworking the other patch comments.
> 
>     Niek



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

* Re: [PATCH 09/10] arm: allwinner-h3: add SD/MMC host controller
  2019-12-11 22:34   ` Niek Linnenbank
@ 2019-12-12 23:56     ` Philippe Mathieu-Daudé
  2019-12-13 21:00       ` Niek Linnenbank
  2019-12-15 23:07       ` Niek Linnenbank
  0 siblings, 2 replies; 67+ messages in thread
From: Philippe Mathieu-Daudé @ 2019-12-12 23:56 UTC (permalink / raw)
  To: Niek Linnenbank, QEMU Developers
  Cc: Beniamino Galvani, Peter Maydell, qemu-arm

Hi Niek,

On 12/11/19 11:34 PM, Niek Linnenbank wrote:
> Ping!
> 
> Anyone would like to comment on this driver?
> 
> I finished the rework on all previous comments in this series.
> 
> Currently debugging the hflags error reported by Philippe.
> After that, I'm ready to send out v2 of these patches.
> 
> Regards,
> Niek
> 
> On Mon, Dec 2, 2019 at 10:10 PM Niek Linnenbank 
> <nieklinnenbank@gmail.com <mailto:nieklinnenbank@gmail.com>> wrote:
> 
>     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
>     <mailto: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);
>     +    }

Your device always creates a bus, so I don't think the if(bus) check is 
worthwhile. Eventually use an assert(bus)?

>     +    carddev = qdev_create(bus, TYPE_SD_CARD);
>     +    qdev_prop_set_drive(carddev, "drive", blk, &error_fatal);
>     +    object_property_set_bool(OBJECT(carddev), true, "realized",
>     &error_fatal);
>     +
>           /* 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
>     <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 "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;

The previous line is confuse, either use parenthesis or a if statement.

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

Maybe remove previous if...

>     +            if (rlen == 4) {
>     +                s->response[0] = ldl_be_p(&resp[0]);
>     +                s->response[1] = s->response[2] = s->response[3] = 0;
>     +            } else {

...

                    } else if (rlen == 16) { ...

>     +                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]);

...

                    } else {
                        goto error;

>     +            }
>     +        }
>     +    }
>     +
>     +    /* 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));

Should we worry about endianess here?

>     +    if (desc->size == 0) {
>     +        desc->size = 0xffff + 1;

Why not write '64 * KiB'?

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

(Related to previous endianess question).

>     +
>     +    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,

I haven't checked .valid accesses from the datasheet.

However due to:

   res = s->data_crc[((offset - REG_SD_DATA7_CRC) / sizeof(uint32_t))];

You seem to expect:

            .impl.min_access_size = 4,

.impl.max_access_size unset is 8, which should works.

>     +};
>     +
>     +static const VMStateDescription vmstate_aw_h3_sdhost = {
>     +    .name = TYPE_AW_H3_SDHOST,

Do not use TYPE name in VMStateDescription.name, because we might change 
the value of TYPE, but the migration state has to keep the same name.

>     +    .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++) {

Please use ARRAY_SIZE(s->response).

>     +        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++) {

ARRAY_SIZE

>     +        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"

Please also use PRIu32 for desc_size/max_bytes.

>     +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"

PRIx32

>     +
>       # 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
>     <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/>.
>     + */
>     +
>     +#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)

Move this definition to the source file.

>     +
>     +#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

I haven't checked the datasheet for all the registers/bits.

Patch very clean, chapeau!

Regards,

Phil.



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

* Re: [PATCH 03/10] arm: allwinner-h3: add Clock Control Unit
  2019-12-02 21:09 ` [PATCH 03/10] arm: allwinner-h3: add Clock Control Unit Niek Linnenbank
@ 2019-12-13  0:03   ` Philippe Mathieu-Daudé
  0 siblings, 0 replies; 67+ messages in thread
From: Philippe Mathieu-Daudé @ 2019-12-13  0:03 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 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))

You might want to have a look at the macros from "hw/registerfields.h".

> +
> +/* 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:

Maybe:

            qemu_log_mask(LOG_UNIMP, ...

> +        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,

Use plain name, not TYPE_NAME.

> +    .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)

Move AW_H3_CLK_REGS_MEM_SIZE to source.

> +
> +#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
> 

Patch looks good.



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

* Re: [PATCH 05/10] arm: allwinner-h3: add System Control module
  2019-12-02 21:09 ` [PATCH 05/10] arm: allwinner-h3: add System Control module Niek Linnenbank
@ 2019-12-13  0:09   ` Philippe Mathieu-Daudé
  2019-12-15 23:27     ` Niek Linnenbank
  0 siblings, 1 reply; 67+ messages in thread
From: Philippe Mathieu-Daudé @ 2019-12-13  0:09 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 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,

Can you point me to the datasheet page that says this region is 
restricted to 32-bit accesses? Maybe you want .valid -> .impl instead?

> +        .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);

This definition isn't very helpful IMO, I'd use the value in place: '4 * 
KiB'.

> +    sysbus_init_mmio(sbd, &s->iomem);
> +}
> +
> +static const VMStateDescription allwinner_h3_syscon_vmstate = {
> +    .name = TYPE_AW_H3_SYSCON,

Plain name.

> +    .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)

"4.1. Memory Mapping" the System Control is 4KiB, isn't it?

> +
> +#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
> 



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

* Re: [PATCH 00/10] Add Allwinner H3 SoC and Orange Pi PC Machine
  2019-12-12 23:25           ` Philippe Mathieu-Daudé
@ 2019-12-13 20:45             ` Niek Linnenbank
  0 siblings, 0 replies; 67+ messages in thread
From: Niek Linnenbank @ 2019-12-13 20:45 UTC (permalink / raw)
  To: Philippe Mathieu-Daudé
  Cc: Peter Maydell, Richard Henderson, QEMU Developers,
	Beniamino Galvani, qemu-arm, Alex Bennée

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

Hi Philippe,

On Fri, Dec 13, 2019 at 12:25 AM Philippe Mathieu-Daudé <philmd@redhat.com>
wrote:

> Cc'ing Alex.
>
> On 12/13/19 12:07 AM, Niek Linnenbank wrote:
> > Hi Philippe,
> >
> > I have discovered that the hflags assertion error you reported is not
> > caused by the Allwinner H3
> > patches but actually an existing problem. What I did is to use the
> > latest master (v4.2.0 tag) without any patches applied.
> > and tried to boot the raspi2 machine with and without debugging enabled.
> > Without debuggin, the raspi2
> > machine runs fine and can boot the 5.4.2 linux kernel. With debugging
> > enabled, the same hflags error shows.
>
> This might be the same bug I hit last week... Alex suggested a patch:
> https://www.mail-archive.com/qemu-devel@nongnu.org/msg664500.html
>
> Do you mind to try it?
>

Ahh OK, I was not aware that this was already seen and solved!
Sometimes I try use the lists.gnu.org site to keep an eye out for relevant
emails going to qemu-devel,
but I totally missed this fix. Too many e-mails. Perhaps instead I should
just subscribe to the mailing list and use filters.

I retried with the raspi2 machine and alex's patch, and indeed the hflags
error is gone and
the machine starts fine with debugging enabled.

Ofcourse, I also retried with the Allwinner H3 patches + alex's fix applied
and the orangepi-pc machine,
and unfortunately, there the hflags assertion did still show up.

Then I looked further to try and understand what is going on, and it looked
to me that the hflags is a
state variable, that needs to be rebuild after changing some other fields
inside the ARM cpu object.
And in my patch #0006 I did just that: I tried to resolve the undefined
exceptions I got using arm_set_cpu_on(),
by setting CP10,CP11 bits. So I tried to use the arm_rebuild_hflags()
function after applying the CP10,CP11 bits,
and that solved the assertion issue (see below).

Can you verify if this change also resolves the hflags assertion on your
side?

I'll also reply to the mail for patch #0006 with this info.

Regards,
Niek

diff --git a/target/arm/arm-powerctl.c b/target/arm/arm-powerctl.c
index f77a950db6..cf2f3d69ab 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
@@ -124,6 +127,9 @@ static void arm_set_cpu_on_async_work(CPUState
*target_cpu_state,
         target_cpu->env.regs[0] = info->context_id;
     }

+    /* Ensure hflags is rebuild */
+    arm_rebuild_hflags(&target_cpu->env);
+
     /* Start the new CPU at the requested address */
     cpu_set_pc(target_cpu_state, info->entry);




>
> If it still fails, you might also add this one on top:
> https://www.mail-archive.com/qemu-devel@nongnu.org/msg663843.html
> and report the error.
>
> That patch is indeed very helpful


> >
> > To reproduce it, build Linux 5.4.2 with the bmc2835_defconfig:
> >
> > $ ARCH=arm CROSS_COMPILE=arm-linux-gnueabi- make mrproper
> > $ ARCH=arm CROSS_COMPILE=arm-linux-gnueabi- make bcm2835_defconfig
> > $ ARCH=arm CROSS_COMPILE=arm-linux-gnueabi- make -j5
> > ...
> >
> > First build QEMU without debugging and try to boot linux:
> > $ ./configure --target-list=arm-softmmu; make clean; make -j5
> > $ ./arm-softmmu/qemu-system-arm -M raspi2 \
> >    -kernel $HOME/linux-5.4.2/arch/arm/boot/zImage \
> >    -append 'console=ttyAMA0,115200 earlyprintk debug' \
> >    -dtb $HOME/linux-5.4.2/arch/arm/boot/dts/bcm2836-rpi-2-b.dtb \
> >    -m 1024 -nographic -s
> > [    0.000000] Booting Linux on physical CPU 0x0
> > [    0.000000] Linux version 5.4.2 (me@host) (gcc version 7.4.0
> (Ubuntu/Linaro 7.4.0-1ubuntu1~18.04.1)) #1 Thu Dec 12 22:49:14 CET 2019
> > [    0.000000] CPU: ARMv7 Processor [410fc075] revision 5 (ARMv7),
> cr=10c53c7d
> > ...
> >
> > Then rebuild QEMU with debugging enabled and again try to boot linux:
> > $ ./configure --target-list=arm-softmmu --enable-debug
> --extra-cflags=-ggdb; make clean; make -j5
> > $ ./arm-softmmu/qemu-system-arm -M raspi2 \
> >    -kernel $HOME/linux-5.4.2/arch/arm/boot/zImage \
> >    -append 'console=ttyAMA0,115200 earlyprintk debug' \
> >    -dtb $HOME/linux-5.4.2/arch/arm/boot/dts/bcm2836-rpi-2-b.dtb \
> >    -m 1024 -nographic -s
> > qemu-system-arm: /home/me/qemu/target/arm/helper.c:11359:
> cpu_get_tb_cpu_state: Assertion `flags == rebuild_hflags_internal(env)'
> failed.
> > qemu-system-arm: /home/me/qemu/target/arm/helper.c:11359:
> cpu_get_tb_cpu_state: Assertion `flags == rebuild_hflags_internal(env)'
> failed.
> > qemu-system-arm: /home/me/qemu/target/arm/helper.c:11359:
> cpu_get_tb_cpu_state: Assertion `flags == rebuild_hflags_internal(env)'
> failed.
> > Aborted (core dumped)
> >
> > $ git describe
> > v4.2.0
> >
> >
> > What should be the next step? Should this be reported as a bug?
>
> In this case we might already have the fix, but if Alex patch doesn't
> help, you are always welcome to open a bug report:
> https://bugs.launchpad.net/qemu/+filebug
> This help to have notes/progress gathered.
>
> > On Tue, Dec 10, 2019 at 9:12 PM Niek Linnenbank
> > <nieklinnenbank@gmail.com <mailto:nieklinnenbank@gmail.com>> wrote:
> >
> >     Hi Philippe,
> >
> >     On Tue, Dec 10, 2019 at 9:26 AM Philippe Mathieu-Daudé
> >     <philmd@redhat.com <mailto:philmd@redhat.com>> wrote:
> >
> >         On 12/9/19 10:37 PM, Niek Linnenbank wrote:
> >          > Hi Philippe,
> >          >
> >          > On Tue, Dec 3, 2019 at 9:47 AM Philippe Mathieu-Daudé
> >         <philmd@redhat.com <mailto:philmd@redhat.com>
> >          > <mailto:philmd@redhat.com <mailto: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)
> >          >
> >          >
> >          > I'm trying to reproduce the error you reported here with my
> >         patch set on
> >          > latest master,
> >          > but so far without any result. The host OS I'm using is
> >         Ubuntu 18.04.3
> >          > LTS on x86_64.
> >          > I ran several times using the same 4.20.7-sunxi kernel and
> >         same command
> >          > line.
> >          >
> >          > Some questions that might help:
> >          > 1) Are there any specific steps you did in order to produce
> >         this error?
> >
> >         I build QEMU with:
> >
> >         ./configure --enable-trace-backends=log --extra-cflags=-ggdb
> >         --enable-debug
> >
> >          > 2) Could this be a known / existing issue?
> >          > 3) How many times did you see this error?
> >
> >         Always
> >
> >          > 4) Are you also using Ubuntu 18.04.3 LTS on x86_64, or a
> >         different host OS?
> >
> >         Host is Fedora 30.
> >
> >
> >     OK thanks, I will try again using the info above after I finished
> >     reworking the other patch comments.
> >
> >     Niek
>
>

-- 
Niek Linnenbank

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

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

* Re: [PATCH 06/10] arm/arm-powerctl: set NSACR.{CP11, CP10} bits in arm_set_cpu_on()
  2019-12-06 20:01     ` Niek Linnenbank
@ 2019-12-13 20:52       ` Niek Linnenbank
  0 siblings, 0 replies; 67+ messages in thread
From: Niek Linnenbank @ 2019-12-13 20:52 UTC (permalink / raw)
  To: Peter Maydell, Philippe Mathieu-Daudé
  Cc: Beniamino Galvani, qemu-arm, QEMU Developers

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

Hi Peter,

Philippe discovered that this patch triggers an hflags assertion error when
building QEMU
with debugging enabled (--enable-debug and --extra-cflags=-ggdb).

See this thread for details:
https://www.mail-archive.com/qemu-devel@nongnu.org/msg665049.html

What I added to resolve that is to call arm_rebuild_hflags() after setting
CP10,CP11.
However I'm not sure of any other side effects because I just don't know
this area of the code very well.

Regards,
Niek

diff --git a/target/arm/arm-powerctl.c b/target/arm/arm-powerctl.c
index f77a950db6..cf2f3d69ab 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
@@ -124,6 +127,9 @@ static void arm_set_cpu_on_async_work(CPUState
*target_cpu_state,
         target_cpu->env.regs[0] = info->context_id;
     }

+    /* Ensure hflags is rebuild */
+    arm_rebuild_hflags(&target_cpu->env);
+
     /* Start the new CPU at the requested address */
     cpu_set_pc(target_cpu_state, info->entry);





On Fri, Dec 6, 2019 at 9:01 PM Niek Linnenbank <nieklinnenbank@gmail.com>
wrote:

> Hey Peter,
>
> On Fri, Dec 6, 2019 at 3:25 PM Peter Maydell <peter.maydell@linaro.org>
> wrote:
>
>> 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!
>>
>
> Sure, I'm happy to help. Note that I only tested this fix with
> the Allwinner H3 SoC patches that I'm working on.
>
> OK, I'll keep an eye out for it. Once it is solved in master, I'll remove
> this patch from the patch series.
>
> Regards,
> Niek
>
>>
>> -- PMM
>>
>
>
> --
> Niek Linnenbank
>
>

-- 
Niek Linnenbank

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

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

* Re: [PATCH 09/10] arm: allwinner-h3: add SD/MMC host controller
  2019-12-12 23:56     ` Philippe Mathieu-Daudé
@ 2019-12-13 21:00       ` Niek Linnenbank
  2019-12-14 13:59         ` Philippe Mathieu-Daudé
  2019-12-15 23:07       ` Niek Linnenbank
  1 sibling, 1 reply; 67+ messages in thread
From: Niek Linnenbank @ 2019-12-13 21:00 UTC (permalink / raw)
  To: Philippe Mathieu-Daudé
  Cc: Beniamino Galvani, Peter Maydell, qemu-arm, QEMU Developers

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

On Fri, Dec 13, 2019 at 12:56 AM Philippe Mathieu-Daudé <philmd@redhat.com>
wrote:

> Hi Niek,
>
> On 12/11/19 11:34 PM, Niek Linnenbank wrote:
> > Ping!
> >
> > Anyone would like to comment on this driver?
> >
> > I finished the rework on all previous comments in this series.
> >
> > Currently debugging the hflags error reported by Philippe.
> > After that, I'm ready to send out v2 of these patches.
> >
> > Regards,
> > Niek
> >
> > On Mon, Dec 2, 2019 at 10:10 PM Niek Linnenbank
> > <nieklinnenbank@gmail.com <mailto:nieklinnenbank@gmail.com>> wrote:
> >
> >     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
> >     <mailto: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);
> >     +    }
>
> Your device always creates a bus, so I don't think the if(bus) check is
> worthwhile. Eventually use an assert(bus)?
>
> >     +    carddev = qdev_create(bus, TYPE_SD_CARD);
> >     +    qdev_prop_set_drive(carddev, "drive", blk, &error_fatal);
> >     +    object_property_set_bool(OBJECT(carddev), true, "realized",
> >     &error_fatal);
> >     +
> >           /* 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
> >     <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 "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;
>
> The previous line is confuse, either use parenthesis or a if statement.
>
>      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;
> >     +            }
>
> Maybe remove previous if...
>
> >     +            if (rlen == 4) {
> >     +                s->response[0] = ldl_be_p(&resp[0]);
> >     +                s->response[1] = s->response[2] = s->response[3] =
> 0;
> >     +            } else {
>
> ...
>
>                     } else if (rlen == 16) { ...
>
> >     +                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]);
>
> ...
>
>                     } else {
>                         goto error;
>
> >     +            }
> >     +        }
> >     +    }
> >     +
> >     +    /* 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));
>
> Should we worry about endianess here?
>
> >     +    if (desc->size == 0) {
> >     +        desc->size = 0xffff + 1;
>
> Why not write '64 * KiB'?
>
> >     +    }
> >     +    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));
>
> (Related to previous endianess question).
>
> >     +
> >     +    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,
>
> I haven't checked .valid accesses from the datasheet.
>
> However due to:
>
>    res = s->data_crc[((offset - REG_SD_DATA7_CRC) / sizeof(uint32_t))];
>
> You seem to expect:
>
>             .impl.min_access_size = 4,
>
> .impl.max_access_size unset is 8, which should works.
>
> >     +};
> >     +
> >     +static const VMStateDescription vmstate_aw_h3_sdhost = {
> >     +    .name = TYPE_AW_H3_SDHOST,
>
> Do not use TYPE name in VMStateDescription.name, because we might change
> the value of TYPE, but the migration state has to keep the same name.
>
> >     +    .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++) {
>
> Please use ARRAY_SIZE(s->response).
>
> >     +        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++) {
>
> ARRAY_SIZE
>
> >     +        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"
>
> Please also use PRIu32 for desc_size/max_bytes.
>
> >     +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"
>
> PRIx32
>
> >     +
> >       # 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
> >     <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/>.
> >     + */
> >     +
> >     +#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)
>
> Move this definition to the source file.
>
> >     +
> >     +#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
>
> I haven't checked the datasheet for all the registers/bits.
>

Thanks again for all of your helpful comments Philippe!
I've started to rework the patch.

One question about adding tags in the commit message: should I
already add 'Reviewed-by: ' when I re-send v2 of this patch? Or should
that be added after you have seen the v2 changes?


>
> Patch very clean, chapeau!
>

Thank you :-)

Regards,
Niek

>
> Regards,
>
> Phil.
>
>

-- 
Niek Linnenbank

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

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

* Re: [PATCH 09/10] arm: allwinner-h3: add SD/MMC host controller
  2019-12-13 21:00       ` Niek Linnenbank
@ 2019-12-14 13:59         ` Philippe Mathieu-Daudé
  2019-12-14 20:32           ` Niek Linnenbank
  0 siblings, 1 reply; 67+ messages in thread
From: Philippe Mathieu-Daudé @ 2019-12-14 13:59 UTC (permalink / raw)
  To: Niek Linnenbank
  Cc: Beniamino Galvani, Peter Maydell, qemu-arm, QEMU Developers

On 12/13/19 10:00 PM, Niek Linnenbank wrote:
> On Fri, Dec 13, 2019 at 12:56 AM Philippe Mathieu-Daudé 
> <philmd@redhat.com <mailto:philmd@redhat.com>> wrote:
> 
>     Hi Niek,
> 
>     On 12/11/19 11:34 PM, Niek Linnenbank wrote:
>      > Ping!
>      >
>      > Anyone would like to comment on this driver?
>      >
>      > I finished the rework on all previous comments in this series.
>      >
>      > Currently debugging the hflags error reported by Philippe.
>      > After that, I'm ready to send out v2 of these patches.
>      >
>      > Regards,
>      > Niek
>      >
>      > On Mon, Dec 2, 2019 at 10:10 PM Niek Linnenbank
>      > <nieklinnenbank@gmail.com <mailto:nieklinnenbank@gmail.com>
>     <mailto:nieklinnenbank@gmail.com <mailto:nieklinnenbank@gmail.com>>>
>     wrote:
>      >
>      >     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
>     <mailto:nieklinnenbank@gmail.com>
>      >     <mailto:nieklinnenbank@gmail.com
>     <mailto: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
[...]
> Thanks again for all of your helpful comments Philippe!
> I've started to rework the patch.
> 
> One question about adding tags in the commit message: should I
> already add 'Reviewed-by: ' when I re-send v2 of this patch? Or should
> that be added after you have seen the v2 changes?

You shouldn't add the Reviewed-by tag until explicitly given by the 
reviewer. If the changes are trivial, it is easy to conditionally give 
the tag such "If ... is done: R-b", "With ... fixed: R-b".

Since this is your first contribution, I have been more careful. Also 
since your patch is already of very good quality, I'v been a bit picky 
regarding few details.

Since there are too many comments, so I prefer to fully review the v2 of 
this patch again.

Regards,

Phil.



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

* Re: [PATCH 09/10] arm: allwinner-h3: add SD/MMC host controller
  2019-12-14 13:59         ` Philippe Mathieu-Daudé
@ 2019-12-14 20:32           ` Niek Linnenbank
  0 siblings, 0 replies; 67+ messages in thread
From: Niek Linnenbank @ 2019-12-14 20:32 UTC (permalink / raw)
  To: Philippe Mathieu-Daudé
  Cc: Beniamino Galvani, Peter Maydell, qemu-arm, QEMU Developers

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

On Sat, Dec 14, 2019 at 2:59 PM Philippe Mathieu-Daudé <philmd@redhat.com>
wrote:

> On 12/13/19 10:00 PM, Niek Linnenbank wrote:
> > On Fri, Dec 13, 2019 at 12:56 AM Philippe Mathieu-Daudé
> > <philmd@redhat.com <mailto:philmd@redhat.com>> wrote:
> >
> >     Hi Niek,
> >
> >     On 12/11/19 11:34 PM, Niek Linnenbank wrote:
> >      > Ping!
> >      >
> >      > Anyone would like to comment on this driver?
> >      >
> >      > I finished the rework on all previous comments in this series.
> >      >
> >      > Currently debugging the hflags error reported by Philippe.
> >      > After that, I'm ready to send out v2 of these patches.
> >      >
> >      > Regards,
> >      > Niek
> >      >
> >      > On Mon, Dec 2, 2019 at 10:10 PM Niek Linnenbank
> >      > <nieklinnenbank@gmail.com <mailto:nieklinnenbank@gmail.com>
> >     <mailto:nieklinnenbank@gmail.com <mailto:nieklinnenbank@gmail.com>>>
> >     wrote:
> >      >
> >      >     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
> >     <mailto:nieklinnenbank@gmail.com>
> >      >     <mailto:nieklinnenbank@gmail.com
> >     <mailto: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
> [...]
> > Thanks again for all of your helpful comments Philippe!
> > I've started to rework the patch.
> >
> > One question about adding tags in the commit message: should I
> > already add 'Reviewed-by: ' when I re-send v2 of this patch? Or should
> > that be added after you have seen the v2 changes?
>
> You shouldn't add the Reviewed-by tag until explicitly given by the
> reviewer. If the changes are trivial, it is easy to conditionally give
> the tag such "If ... is done: R-b", "With ... fixed: R-b".
>

OK, thanks for clarifying, I'll keep that in mind.


>
> Since this is your first contribution, I have been more careful. Also
> since your patch is already of very good quality, I'v been a bit picky
> regarding few details.
>

Sure, that makes sense indeed. And I very much appreciate your feedback
Philippe,
so please keep doing that, even about the small details ;-)


>
> Since there are too many comments, so I prefer to fully review the v2 of
> this patch again.
>
> Yes, true indeed.

Regards,
Niek


> Regards,
>
> Phil.
>
>

-- 
Niek Linnenbank

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

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

* Re: [PATCH 09/10] arm: allwinner-h3: add SD/MMC host controller
  2019-12-12 23:56     ` Philippe Mathieu-Daudé
  2019-12-13 21:00       ` Niek Linnenbank
@ 2019-12-15 23:07       ` Niek Linnenbank
  2019-12-16  0:14         ` Philippe Mathieu-Daudé
  1 sibling, 1 reply; 67+ messages in thread
From: Niek Linnenbank @ 2019-12-15 23:07 UTC (permalink / raw)
  To: Philippe Mathieu-Daudé
  Cc: Beniamino Galvani, Peter Maydell, qemu-arm, QEMU Developers

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

On Fri, Dec 13, 2019 at 12:56 AM Philippe Mathieu-Daudé <philmd@redhat.com>
wrote:

> Hi Niek,
>
> On 12/11/19 11:34 PM, Niek Linnenbank wrote:
> > Ping!
> >
> > Anyone would like to comment on this driver?
> >
> > I finished the rework on all previous comments in this series.
> >
> > Currently debugging the hflags error reported by Philippe.
> > After that, I'm ready to send out v2 of these patches.
> >
> > Regards,
> > Niek
> >
> > On Mon, Dec 2, 2019 at 10:10 PM Niek Linnenbank
> > <nieklinnenbank@gmail.com <mailto:nieklinnenbank@gmail.com>> wrote:
> >
> >     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
> >     <mailto: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);
> >     +    }
>
> Your device always creates a bus, so I don't think the if(bus) check is
> worthwhile. Eventually use an assert(bus)?
>
> >     +    carddev = qdev_create(bus, TYPE_SD_CARD);
> >     +    qdev_prop_set_drive(carddev, "drive", blk, &error_fatal);
> >     +    object_property_set_bool(OBJECT(carddev), true, "realized",
> >     &error_fatal);
> >     +
> >           /* 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
> >     <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 "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;
>
> The previous line is confuse, either use parenthesis or a if statement.
>
>      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;
> >     +            }
>
> Maybe remove previous if...
>
> >     +            if (rlen == 4) {
> >     +                s->response[0] = ldl_be_p(&resp[0]);
> >     +                s->response[1] = s->response[2] = s->response[3] =
> 0;
> >     +            } else {
>
> ...
>
>                     } else if (rlen == 16) { ...
>
> >     +                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]);
>
> ...
>
>                     } else {
>                         goto error;
>
> >     +            }
> >     +        }
> >     +    }
> >     +
> >     +    /* 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));
>
> Should we worry about endianess here?
>

I tried to figure out what is expected, but the
Allwinner_H3_Datasheet_V1.2.pdf does not
explicitly mention endianness for any of its I/O devices. Currently it
seems all devices are
happy with using the same endianness as the CPUs. In the MemoryRegionOps
has DEVICE_NATIVE_ENDIAN
set to match the behavior seen.


>
> >     +    if (desc->size == 0) {
> >     +        desc->size = 0xffff + 1;
>
> Why not write '64 * KiB'?
>
> >     +    }
> >     +    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));
>
> (Related to previous endianess question).
>
> >     +
> >     +    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,
>
> I haven't checked .valid accesses from the datasheet.
>
> However due to:
>
>    res = s->data_crc[((offset - REG_SD_DATA7_CRC) / sizeof(uint32_t))];
>
> You seem to expect:
>
>             .impl.min_access_size = 4,
>
> .impl.max_access_size unset is 8, which should works.
>
> It seems that all registers are aligned on at least 32-bit boundaries. And
the section 5.3.5.1 mentions
that the DMA descriptors must be stored in memory 32-bit aligned. So based
on that information,
I think 32-bit is a safe choice. I've verified this with Linux mainline and
U-Boot drivers and both are still working.


> >     +};
> >     +
> >     +static const VMStateDescription vmstate_aw_h3_sdhost = {
> >     +    .name = TYPE_AW_H3_SDHOST,
>
> Do not use TYPE name in VMStateDescription.name, because we might change
> the value of TYPE, but the migration state has to keep the same name.
>
> OK thanks, I will make that changes in the other commits as well.


> >     +    .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++) {
>
> Please use ARRAY_SIZE(s->response).
>
> >     +        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++) {
>
> ARRAY_SIZE
>
> >     +        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"
>
> Please also use PRIu32 for desc_size/max_bytes.
>
> Done. I'll also use PRIu32 / PRIx32 in the other commits that have
trace-events changes.


> >     +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"
>
> PRIx32
>

> >     +
> >       # 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
> >     <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/>.
> >     + */
> >     +
> >     +#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)
>
> Move this definition to the source file.
>
> >     +
> >     +#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
>
> I haven't checked the datasheet for all the registers/bits.
>
> Patch very clean, chapeau!
>
> Regards,
>
> Phil.
>
>

-- 
Niek Linnenbank

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

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

* Re: [PATCH 05/10] arm: allwinner-h3: add System Control module
  2019-12-13  0:09   ` Philippe Mathieu-Daudé
@ 2019-12-15 23:27     ` Niek Linnenbank
  2019-12-16  0:17       ` Philippe Mathieu-Daudé
  0 siblings, 1 reply; 67+ messages in thread
From: Niek Linnenbank @ 2019-12-15 23:27 UTC (permalink / raw)
  To: Philippe Mathieu-Daudé
  Cc: Beniamino Galvani, Peter Maydell, qemu-arm, QEMU Developers

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

On Fri, Dec 13, 2019 at 1:09 AM Philippe Mathieu-Daudé <philmd@redhat.com>
wrote:

> On 12/2/19 10:09 PM, Niek Linnenbank wrote:
> > 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,
>
> Can you point me to the datasheet page that says this region is
> restricted to 32-bit accesses? Maybe you want .valid -> .impl instead?
>
> Hehe well here I can only give the same answer as for the SD/MMC driver:
the datasheet
only provides the base address and register offsets, but nothing
explicitely mentioned about alignment.
I do see that also for this device the registers are 32-bit aligned.

Does that mean I should change MemoryRegionOps to . impl instead?


> > +        .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);
>
> This definition isn't very helpful IMO, I'd use the value in place: '4 *
> KiB'.
>
OK, I'll apply that too in the other drivers.


>
> > +    sysbus_init_mmio(sbd, &s->iomem);
> > +}
> > +
> > +static const VMStateDescription allwinner_h3_syscon_vmstate = {
> > +    .name = TYPE_AW_H3_SYSCON,
>
> Plain name.
>
> > +    .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)
>
> "4.1. Memory Mapping" the System Control is 4KiB, isn't it?
>

Correct, I made a mistake there. Thanks, I'll change it and re-check the
other files as well.


>
> > +
> > +#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
> >
>
>

-- 
Niek Linnenbank

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

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

* Re: [PATCH 09/10] arm: allwinner-h3: add SD/MMC host controller
  2019-12-15 23:07       ` Niek Linnenbank
@ 2019-12-16  0:14         ` Philippe Mathieu-Daudé
  2019-12-16 19:46           ` Niek Linnenbank
  0 siblings, 1 reply; 67+ messages in thread
From: Philippe Mathieu-Daudé @ 2019-12-16  0:14 UTC (permalink / raw)
  To: Niek Linnenbank
  Cc: Beniamino Galvani, Peter Maydell, qemu-arm, QEMU Developers

On 12/16/19 12:07 AM, Niek Linnenbank wrote:
> 
> 
> On Fri, Dec 13, 2019 at 12:56 AM Philippe Mathieu-Daudé 
> <philmd@redhat.com <mailto:philmd@redhat.com>> wrote:
> 
>     Hi Niek,
> 
>     On 12/11/19 11:34 PM, Niek Linnenbank wrote:
[...]
>      >     +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));
> 
>     Should we worry about endianess here?
> 
> 
> I tried to figure out what is expected, but the 
> Allwinner_H3_Datasheet_V1.2.pdf does not
> explicitly mention endianness for any of its I/O devices. Currently it 
> seems all devices are
> happy with using the same endianness as the CPUs. In the MemoryRegionOps 
> has DEVICE_NATIVE_ENDIAN
> set to match the behavior seen.

OK.

[...]
>      >     +static const MemoryRegionOps aw_h3_sdhost_ops = {
>      >     +    .read = aw_h3_sdhost_read,
>      >     +    .write = aw_h3_sdhost_write,
>      >     +    .endianness = DEVICE_NATIVE_ENDIAN,
> 
>     I haven't checked .valid accesses from the datasheet.
> 
>     However due to:
> 
>         res = s->data_crc[((offset - REG_SD_DATA7_CRC) / sizeof(uint32_t))];
> 
>     You seem to expect:
> 
>                  .impl.min_access_size = 4,
> 
>     .impl.max_access_size unset is 8, which should works.
> 
> It seems that all registers are aligned on at least 32-bit boundaries. 
> And the section 5.3.5.1 mentions
> that the DMA descriptors must be stored in memory 32-bit aligned. So 
> based on that information,

So you are describing ".valid.min_access_size = 4", which is the minimum 
access size on the bus.
".impl.min_access_size" is different, it is what access sizes is ready 
to handle your model.
Your model read/write handlers expect addresses aligned on 32-bit 
boundary, this is why I suggested to use ".impl.min_access_size = 4". If 
the guest were using a 16-bit access, your model would be buggy. If you 
describe your implementation to accept minimum 32-bit and the guest is 
allowed to use smaller accesses, QEMU will do a 32-bit access to the 
device, and return the 16-bit part to the guest. This way your model is 
safe. This is done by access_with_adjusted_size() in memory.c.
If you restrict with ".valid.min_access_size = 4", you might think we 
don't need ".valid.min_access_size = 4" because all access from guest 
will be at least 32-bit. However keep in mind someone might find this 
device in another datasheet not limited to 32-bit, and let's say change 
to ".valid.min_access_size = 2". Without ".impl.min_access_size = 4" 
your model is buggy. So to be safe I'd use:

   .impl.min_access_size = 4,
   .valid.min_access_size = 4,


> I think 32-bit is a safe choice. I've verified this with Linux mainline 
> and U-Boot drivers and both are still working.

Regards,

Phil.



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

* Re: [PATCH 05/10] arm: allwinner-h3: add System Control module
  2019-12-15 23:27     ` Niek Linnenbank
@ 2019-12-16  0:17       ` Philippe Mathieu-Daudé
  0 siblings, 0 replies; 67+ messages in thread
From: Philippe Mathieu-Daudé @ 2019-12-16  0:17 UTC (permalink / raw)
  To: Niek Linnenbank
  Cc: Beniamino Galvani, Peter Maydell, qemu-arm, QEMU Developers

On 12/16/19 12:27 AM, Niek Linnenbank wrote:
> On Fri, Dec 13, 2019 at 1:09 AM Philippe Mathieu-Daudé 
> <philmd@redhat.com <mailto:philmd@redhat.com>> wrote:
> 
>     On 12/2/19 10:09 PM, Niek Linnenbank wrote:
[...]
>      > +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,
> 
>     Can you point me to the datasheet page that says this region is
>     restricted to 32-bit accesses? Maybe you want .valid -> .impl instead?
> 
> Hehe well here I can only give the same answer as for the SD/MMC driver: 
> the datasheet
> only provides the base address and register offsets, but nothing 
> explicitely mentioned about alignment.
> I do see that also for this device the registers are 32-bit aligned.
> 
> Does that mean I should change MemoryRegionOps to . impl instead?

No, keep them, but add ".impl.min_access_size = 4" (see answer to SD/MMC 
model patch).



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

* Re: [PATCH 09/10] arm: allwinner-h3: add SD/MMC host controller
  2019-12-16  0:14         ` Philippe Mathieu-Daudé
@ 2019-12-16 19:46           ` Niek Linnenbank
  2019-12-16 21:28             ` Philippe Mathieu-Daudé
  0 siblings, 1 reply; 67+ messages in thread
From: Niek Linnenbank @ 2019-12-16 19:46 UTC (permalink / raw)
  To: Philippe Mathieu-Daudé
  Cc: Beniamino Galvani, Peter Maydell, qemu-arm, QEMU Developers

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

On Mon, Dec 16, 2019 at 1:14 AM Philippe Mathieu-Daudé <philmd@redhat.com>
wrote:

> On 12/16/19 12:07 AM, Niek Linnenbank wrote:
> >
> >
> > On Fri, Dec 13, 2019 at 12:56 AM Philippe Mathieu-Daudé
> > <philmd@redhat.com <mailto:philmd@redhat.com>> wrote:
> >
> >     Hi Niek,
> >
> >     On 12/11/19 11:34 PM, Niek Linnenbank wrote:
> [...]
> >      >     +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));
> >
> >     Should we worry about endianess here?
> >
> >
> > I tried to figure out what is expected, but the
> > Allwinner_H3_Datasheet_V1.2.pdf does not
> > explicitly mention endianness for any of its I/O devices. Currently it
> > seems all devices are
> > happy with using the same endianness as the CPUs. In the MemoryRegionOps
> > has DEVICE_NATIVE_ENDIAN
> > set to match the behavior seen.
>
> OK.
>
> [...]
> >      >     +static const MemoryRegionOps aw_h3_sdhost_ops = {
> >      >     +    .read = aw_h3_sdhost_read,
> >      >     +    .write = aw_h3_sdhost_write,
> >      >     +    .endianness = DEVICE_NATIVE_ENDIAN,
> >
> >     I haven't checked .valid accesses from the datasheet.
> >
> >     However due to:
> >
> >         res = s->data_crc[((offset - REG_SD_DATA7_CRC) /
> sizeof(uint32_t))];
> >
> >     You seem to expect:
> >
> >                  .impl.min_access_size = 4,
> >
> >     .impl.max_access_size unset is 8, which should works.
> >
> > It seems that all registers are aligned on at least 32-bit boundaries.
> > And the section 5.3.5.1 mentions
> > that the DMA descriptors must be stored in memory 32-bit aligned. So
> > based on that information,
>
> So you are describing ".valid.min_access_size = 4", which is the minimum
> access size on the bus.
> ".impl.min_access_size" is different, it is what access sizes is ready
> to handle your model.
> Your model read/write handlers expect addresses aligned on 32-bit
> boundary, this is why I suggested to use ".impl.min_access_size = 4". If
> the guest were using a 16-bit access, your model would be buggy. If you
> describe your implementation to accept minimum 32-bit and the guest is
> allowed to use smaller accesses, QEMU will do a 32-bit access to the
> device, and return the 16-bit part to the guest. This way your model is
> safe. This is done by access_with_adjusted_size() in memory.c.
> If you restrict with ".valid.min_access_size = 4", you might think we
> don't need ".valid.min_access_size = 4" because all access from guest
> will be at least 32-bit. However keep in mind someone might find this
> device in another datasheet not limited to 32-bit, and let's say change
> to ".valid.min_access_size = 2". Without ".impl.min_access_size = 4"
> your model is buggy. So to be safe I'd use:
>
>    .impl.min_access_size = 4,
>    .valid.min_access_size = 4,
>

Now it makes more sense to me, thanks Philippe for explaining this!
Great, I'll add .impl.min_access_size = 4.

At this point, I've processed all the feedback that I received for all of
the patches
in this series. Is there anything else you would like to
see/discuss/review, or shall I send the v2 when I finish testing?

Regards,
Niek


>
> > I think 32-bit is a safe choice. I've verified this with Linux mainline
> > and U-Boot drivers and both are still working.
>
> Regards,
>
> Phil.
>
>

-- 
Niek Linnenbank

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

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

* Re: [PATCH 09/10] arm: allwinner-h3: add SD/MMC host controller
  2019-12-16 19:46           ` Niek Linnenbank
@ 2019-12-16 21:28             ` Philippe Mathieu-Daudé
  0 siblings, 0 replies; 67+ messages in thread
From: Philippe Mathieu-Daudé @ 2019-12-16 21:28 UTC (permalink / raw)
  To: Niek Linnenbank
  Cc: Beniamino Galvani, Peter Maydell, qemu-arm,
	Philippe Mathieu-Daudé,
	QEMU Developers

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

Le lun. 16 déc. 2019 20:46, Niek Linnenbank <nieklinnenbank@gmail.com> a
écrit :

>
>
> On Mon, Dec 16, 2019 at 1:14 AM Philippe Mathieu-Daudé <philmd@redhat.com>
> wrote:
>
>> On 12/16/19 12:07 AM, Niek Linnenbank wrote:
>> >
>> >
>> > On Fri, Dec 13, 2019 at 12:56 AM Philippe Mathieu-Daudé
>> > <philmd@redhat.com <mailto:philmd@redhat.com>> wrote:
>> >
>> >     Hi Niek,
>> >
>> >     On 12/11/19 11:34 PM, Niek Linnenbank wrote:
>> [...]
>> >      >     +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));
>> >
>> >     Should we worry about endianess here?
>> >
>> >
>> > I tried to figure out what is expected, but the
>> > Allwinner_H3_Datasheet_V1.2.pdf does not
>> > explicitly mention endianness for any of its I/O devices. Currently it
>> > seems all devices are
>> > happy with using the same endianness as the CPUs. In the
>> MemoryRegionOps
>> > has DEVICE_NATIVE_ENDIAN
>> > set to match the behavior seen.
>>
>> OK.
>>
>> [...]
>> >      >     +static const MemoryRegionOps aw_h3_sdhost_ops = {
>> >      >     +    .read = aw_h3_sdhost_read,
>> >      >     +    .write = aw_h3_sdhost_write,
>> >      >     +    .endianness = DEVICE_NATIVE_ENDIAN,
>> >
>> >     I haven't checked .valid accesses from the datasheet.
>> >
>> >     However due to:
>> >
>> >         res = s->data_crc[((offset - REG_SD_DATA7_CRC) /
>> sizeof(uint32_t))];
>> >
>> >     You seem to expect:
>> >
>> >                  .impl.min_access_size = 4,
>> >
>> >     .impl.max_access_size unset is 8, which should works.
>> >
>> > It seems that all registers are aligned on at least 32-bit boundaries.
>> > And the section 5.3.5.1 mentions
>> > that the DMA descriptors must be stored in memory 32-bit aligned. So
>> > based on that information,
>>
>> So you are describing ".valid.min_access_size = 4", which is the minimum
>> access size on the bus.
>> ".impl.min_access_size" is different, it is what access sizes is ready
>> to handle your model.
>> Your model read/write handlers expect addresses aligned on 32-bit
>> boundary, this is why I suggested to use ".impl.min_access_size = 4". If
>> the guest were using a 16-bit access, your model would be buggy. If you
>> describe your implementation to accept minimum 32-bit and the guest is
>> allowed to use smaller accesses, QEMU will do a 32-bit access to the
>> device, and return the 16-bit part to the guest. This way your model is
>> safe. This is done by access_with_adjusted_size() in memory.c.
>> If you restrict with ".valid.min_access_size = 4", you might think we
>> don't need ".valid.min_access_size = 4" because all access from guest
>> will be at least 32-bit. However keep in mind someone might find this
>> device in another datasheet not limited to 32-bit, and let's say change
>> to ".valid.min_access_size = 2". Without ".impl.min_access_size = 4"
>> your model is buggy. So to be safe I'd use:
>>
>>    .impl.min_access_size = 4,
>>    .valid.min_access_size = 4,
>>
>
> Now it makes more sense to me, thanks Philippe for explaining this!
> Great, I'll add .impl.min_access_size = 4.
>
> At this point, I've processed all the feedback that I received for all of
> the patches
> in this series. Is there anything else you would like to
> see/discuss/review, or shall I send the v2 when I finish testing?
>

Send it! We'll discuss on updated v2 :)

Regards,

Phil.

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

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

end of thread, other threads:[~2019-12-16 21:29 UTC | newest]

Thread overview: 67+ 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-10  9:02   ` Philippe Mathieu-Daudé
2019-12-10 19:17     ` 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-06 22:15         ` Niek Linnenbank
2019-12-10  8:59           ` Philippe Mathieu-Daudé
2019-12-10 19:14             ` Niek Linnenbank
2019-12-02 21:09 ` [PATCH 03/10] arm: allwinner-h3: add Clock Control Unit Niek Linnenbank
2019-12-13  0:03   ` Philippe Mathieu-Daudé
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-10  7:56   ` Philippe Mathieu-Daudé
2019-12-10  8:29     ` Gerd Hoffmann
2019-12-10 19:11       ` Niek Linnenbank
2019-12-02 21:09 ` [PATCH 05/10] arm: allwinner-h3: add System Control module Niek Linnenbank
2019-12-13  0:09   ` Philippe Mathieu-Daudé
2019-12-15 23:27     ` Niek Linnenbank
2019-12-16  0:17       ` Philippe Mathieu-Daudé
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-06 20:01     ` Niek Linnenbank
2019-12-13 20:52       ` Niek Linnenbank
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-06 20:20       ` Niek Linnenbank
2019-12-02 21:09 ` [PATCH 09/10] arm: allwinner-h3: add SD/MMC host controller Niek Linnenbank
2019-12-11 22:34   ` Niek Linnenbank
2019-12-12 23:56     ` Philippe Mathieu-Daudé
2019-12-13 21:00       ` Niek Linnenbank
2019-12-14 13:59         ` Philippe Mathieu-Daudé
2019-12-14 20:32           ` Niek Linnenbank
2019-12-15 23:07       ` Niek Linnenbank
2019-12-16  0:14         ` Philippe Mathieu-Daudé
2019-12-16 19:46           ` Niek Linnenbank
2019-12-16 21:28             ` Philippe Mathieu-Daudé
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-10  8:40     ` Philippe Mathieu-Daudé
2019-12-09 21:37   ` Niek Linnenbank
2019-12-10  8:26     ` Philippe Mathieu-Daudé
2019-12-10 20:12       ` Niek Linnenbank
2019-12-12 23:07         ` Niek Linnenbank
2019-12-12 23:25           ` Philippe Mathieu-Daudé
2019-12-13 20:45             ` Niek Linnenbank
2019-12-03  9:02 ` Philippe Mathieu-Daudé
2019-12-03 19:32   ` Niek Linnenbank
2019-12-06 14:16     ` Peter Maydell
2019-12-09 22:24       ` Aleksandar Markovic
2019-12-10 10:34 ` KONRAD Frederic
2019-12-10 19:55   ` Niek Linnenbank

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).