qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v1 00/11] *** add allwinner-r40 support ***
@ 2023-03-21 10:24 qianfanguijin
  2023-03-21 10:25 ` [PATCH v1 01/11] hw: arm: Add bananapi M2-Ultra and allwinner-r40 support qianfanguijin
                   ` (10 more replies)
  0 siblings, 11 replies; 17+ messages in thread
From: qianfanguijin @ 2023-03-21 10:24 UTC (permalink / raw)
  To: qemu-arm, qemu-devel
  Cc: Strahinja Jankovic, Peter Maydell, Beniamino Galvani,
	Philippe Mathieu-Daudé,
	Niek Linnenbank, qianfan Zhao

From: qianfan Zhao <qianfanguijin@163.com>

*** history ***

# v1: 2023-03-21

The first version which add allwinner-r40 support, supported features:

+ ccu
+ dram controller
+ uart
+ i2c and pmic(axp221)
+ sdcard
+ emac/gmac

Also provide a test case under avocado, running quickly test:

$ AVOCADO_ALLOW_LARGE_STORAGE=yes tests/venv/bin/avocado \
    --verbose --show=app,console run -t machine:bpim2u \
    ../tests/avocado/boot_linux_console.py

qianfan Zhao (11):
  hw: arm: Add bananapi M2-Ultra and allwinner-r40 support
  hw/arm/allwinner-r40: add Clock Control Unit
  hw: allwinner-r40: Complete uart devices
  hw: arm: allwinner-r40: Add 5 TWI controllers
  hw/misc: AXP221 PMU Emulation
  hw/arm/allwinner-r40: add SDRAM controller device
  hw: sd: allwinner-sdhost: Add sun50i-a64 SoC support
  hw: arm: allwinner-r40: Fix the mmc controller's type
  hw: arm: allwinner-r40: Add emac and gmac support
  tests: avocado: boot_linux_console: Add test case for bpim2u
  docs: system: arm: Introduce bananapi_m2u

 configs/devices/arm-softmmu/default.mak |   1 +
 docs/system/arm/bananapi_m2u.rst        | 138 ++++++
 hw/arm/Kconfig                          |  10 +
 hw/arm/allwinner-r40.c                  | 558 ++++++++++++++++++++++++
 hw/arm/bananapi_m2u.c                   | 131 ++++++
 hw/arm/meson.build                      |   1 +
 hw/misc/Kconfig                         |   4 +
 hw/misc/allwinner-r40-ccu.c             | 207 +++++++++
 hw/misc/allwinner-r40-dramc.c           | 499 +++++++++++++++++++++
 hw/misc/axp221.c                        | 196 +++++++++
 hw/misc/meson.build                     |   3 +
 hw/misc/trace-events                    |  19 +
 hw/sd/allwinner-sdhost.c                |  70 ++-
 include/hw/arm/allwinner-r40.h          | 148 +++++++
 include/hw/misc/allwinner-r40-ccu.h     |  65 +++
 include/hw/misc/allwinner-r40-dramc.h   | 108 +++++
 include/hw/sd/allwinner-sdhost.h        |   9 +
 tests/avocado/boot_linux_console.py     | 173 ++++++++
 18 files changed, 2337 insertions(+), 3 deletions(-)
 create mode 100644 docs/system/arm/bananapi_m2u.rst
 create mode 100644 hw/arm/allwinner-r40.c
 create mode 100644 hw/arm/bananapi_m2u.c
 create mode 100644 hw/misc/allwinner-r40-ccu.c
 create mode 100644 hw/misc/allwinner-r40-dramc.c
 create mode 100644 hw/misc/axp221.c
 create mode 100644 include/hw/arm/allwinner-r40.h
 create mode 100644 include/hw/misc/allwinner-r40-ccu.h
 create mode 100644 include/hw/misc/allwinner-r40-dramc.h

-- 
2.25.1



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

* [PATCH v1 01/11] hw: arm: Add bananapi M2-Ultra and allwinner-r40 support
  2023-03-21 10:24 [PATCH v1 00/11] *** add allwinner-r40 support *** qianfanguijin
@ 2023-03-21 10:25 ` qianfanguijin
  2023-03-25 20:59   ` Strahinja Jankovic
  2023-03-21 10:25 ` [PATCH v1 02/11] hw/arm/allwinner-r40: add Clock Control Unit qianfanguijin
                   ` (9 subsequent siblings)
  10 siblings, 1 reply; 17+ messages in thread
From: qianfanguijin @ 2023-03-21 10:25 UTC (permalink / raw)
  To: qemu-arm, qemu-devel
  Cc: Strahinja Jankovic, Peter Maydell, Beniamino Galvani,
	Philippe Mathieu-Daudé,
	Niek Linnenbank, qianfan Zhao

From: qianfan Zhao <qianfanguijin@163.com>

Allwinner R40 (sun8i) SoC features a Quad-Core Cortex-A7 ARM CPU,
and a Mali400 MP2 GPU from ARM. It's also known as the Allwinner T3
for In-Car Entertainment usage, A40i and A40pro are variants that
differ in applicable temperatures range (industrial and military).

This patch is a draft and provides very few features that we will
improve late.

Signed-off-by: qianfan Zhao <qianfanguijin@163.com>
---
 configs/devices/arm-softmmu/default.mak |   1 +
 hw/arm/Kconfig                          |   9 +
 hw/arm/allwinner-r40.c                  | 425 ++++++++++++++++++++++++
 hw/arm/bananapi_m2u.c                   | 116 +++++++
 hw/arm/meson.build                      |   1 +
 include/hw/arm/allwinner-r40.h          | 111 +++++++
 6 files changed, 663 insertions(+)
 create mode 100644 hw/arm/allwinner-r40.c
 create mode 100644 hw/arm/bananapi_m2u.c
 create mode 100644 include/hw/arm/allwinner-r40.h

diff --git a/configs/devices/arm-softmmu/default.mak b/configs/devices/arm-softmmu/default.mak
index 1b49a7830c..76a43add23 100644
--- a/configs/devices/arm-softmmu/default.mak
+++ b/configs/devices/arm-softmmu/default.mak
@@ -43,3 +43,4 @@ CONFIG_FSL_IMX6UL=y
 CONFIG_SEMIHOSTING=y
 CONFIG_ARM_COMPATIBLE_SEMIHOSTING=y
 CONFIG_ALLWINNER_H3=y
+CONFIG_ALLWINNER_R40=y
diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig
index b5aed4aff5..9e14c3427e 100644
--- a/hw/arm/Kconfig
+++ b/hw/arm/Kconfig
@@ -344,6 +344,15 @@ config ALLWINNER_H3
     select USB_EHCI_SYSBUS
     select SD
 
+config ALLWINNER_R40
+    bool
+    select ALLWINNER_A10_PIT
+    select SERIAL
+    select ARM_TIMER
+    select ARM_GIC
+    select UNIMP
+    select SD
+
 config RASPI
     bool
     select FRAMEBUFFER
diff --git a/hw/arm/allwinner-r40.c b/hw/arm/allwinner-r40.c
new file mode 100644
index 0000000000..d0516f4e96
--- /dev/null
+++ b/hw/arm/allwinner-r40.c
@@ -0,0 +1,425 @@
+/*
+ * Allwinner R40/A40i/T3 System on Chip emulation
+ *
+ * Copyright (C) 2023 qianfan Zhao <qianfanguijin@163.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 "qapi/error.h"
+#include "qemu/error-report.h"
+#include "qemu/bswap.h"
+#include "qemu/module.h"
+#include "qemu/units.h"
+#include "hw/qdev-core.h"
+#include "hw/sysbus.h"
+#include "hw/char/serial.h"
+#include "hw/misc/unimp.h"
+#include "hw/usb/hcd-ehci.h"
+#include "hw/loader.h"
+#include "sysemu/sysemu.h"
+#include "hw/arm/allwinner-r40.h"
+
+/* Memory map */
+const hwaddr allwinner_r40_memmap[] = {
+    [AW_R40_DEV_SRAM_A1]    = 0x00000000,
+    [AW_R40_DEV_SRAM_A2]    = 0x00004000,
+    [AW_R40_DEV_SRAM_A3]    = 0x00008000,
+    [AW_R40_DEV_SRAM_A4]    = 0x0000b400,
+    [AW_R40_DEV_MMC0]       = 0x01c0f000,
+    [AW_R40_DEV_MMC1]       = 0x01c10000,
+    [AW_R40_DEV_MMC2]       = 0x01c11000,
+    [AW_R40_DEV_MMC3]       = 0x01c12000,
+    [AW_R40_DEV_PIT]        = 0x01c20c00,
+    [AW_R40_DEV_UART0]      = 0x01c28000,
+    [AW_R40_DEV_GIC_DIST]   = 0x01c81000,
+    [AW_R40_DEV_GIC_CPU]    = 0x01c82000,
+    [AW_R40_DEV_GIC_HYP]    = 0x01c84000,
+    [AW_R40_DEV_GIC_VCPU]   = 0x01c86000,
+    [AW_R40_DEV_SDRAM]      = 0x40000000
+};
+
+/* List of unimplemented devices */
+struct AwR40Unimplemented {
+    const char *device_name;
+    hwaddr base;
+    hwaddr size;
+};
+
+static struct AwR40Unimplemented r40_unimplemented[] = {
+    { "d-engine",   0x01000000, 4 * MiB },
+    { "d-inter",    0x01400000, 128 * KiB },
+    { "sram-c",     0x01c00000, 4 * KiB },
+    { "dma",        0x01c02000, 4 * KiB },
+    { "nfdc",       0x01c03000, 4 * KiB },
+    { "ts",         0x01c04000, 4 * KiB },
+    { "spi0",       0x01c05000, 4 * KiB },
+    { "spi1",       0x01c06000, 4 * KiB },
+    { "cs0",        0x01c09000, 4 * KiB },
+    { "keymem",     0x01c0a000, 4 * KiB },
+    { "emac",       0x01c0b000, 4 * KiB },
+    { "usb0-otg",   0x01c13000, 4 * KiB },
+    { "usb0-host",  0x01c14000, 4 * KiB },
+    { "crypto",     0x01c15000, 4 * KiB },
+    { "spi2",       0x01c17000, 4 * KiB },
+    { "sata",       0x01c18000, 4 * KiB },
+    { "usb1-host",  0x01c19000, 4 * KiB },
+    { "sid",        0x01c1b000, 4 * KiB },
+    { "usb2-host",  0x01c1c000, 4 * KiB },
+    { "cs1",        0x01c1d000, 4 * KiB },
+    { "spi3",       0x01c1f000, 4 * KiB },
+    { "ccu",        0x01c20000, 1 * KiB },
+    { "rtc",        0x01c20400, 1 * KiB },
+    { "pio",        0x01c20800, 1 * KiB },
+    { "owa",        0x01c21000, 1 * KiB },
+    { "ac97",       0x01c21400, 1 * KiB },
+    { "cir0",       0x01c21800, 1 * KiB },
+    { "cir1",       0x01c21c00, 1 * KiB },
+    { "pcm0",       0x01c22000, 1 * KiB },
+    { "pcm1",       0x01c22400, 1 * KiB },
+    { "pcm2",       0x01c22800, 1 * KiB },
+    { "audio",      0x01c22c00, 1 * KiB },
+    { "keypad",     0x01c23000, 1 * KiB },
+    { "pwm",        0x01c23400, 1 * KiB },
+    { "keyadc",     0x01c24400, 1 * KiB },
+    { "ths",        0x01c24c00, 1 * KiB },
+    { "rtp",        0x01c25000, 1 * KiB },
+    { "pmu",        0x01c25400, 1 * KiB },
+    { "cpu-cfg",    0x01c25c00, 1 * KiB },
+    { "uart0",      0x01c28000, 1 * KiB },
+    { "uart1",      0x01c28400, 1 * KiB },
+    { "uart2",      0x01c28800, 1 * KiB },
+    { "uart3",      0x01c28c00, 1 * KiB },
+    { "uart4",      0x01c29000, 1 * KiB },
+    { "uart5",      0x01c29400, 1 * KiB },
+    { "uart6",      0x01c29800, 1 * KiB },
+    { "uart7",      0x01c29c00, 1 * KiB },
+    { "ps20",       0x01c2a000, 1 * KiB },
+    { "ps21",       0x01c2a400, 1 * KiB },
+    { "twi0",       0x01c2ac00, 1 * KiB },
+    { "twi1",       0x01c2b000, 1 * KiB },
+    { "twi2",       0x01c2b400, 1 * KiB },
+    { "twi3",       0x01c2b800, 1 * KiB },
+    { "twi4",       0x01c2c000, 1 * KiB },
+    { "scr",        0x01c2c400, 1 * KiB },
+    { "tvd-top",    0x01c30000, 4 * KiB },
+    { "tvd0",       0x01c31000, 4 * KiB },
+    { "tvd1",       0x01c32000, 4 * KiB },
+    { "tvd2",       0x01c33000, 4 * KiB },
+    { "tvd3",       0x01c34000, 4 * KiB },
+    { "gpu",        0x01c40000, 64 * KiB },
+    { "gmac",       0x01c50000, 64 * KiB },
+    { "hstmr",      0x01c60000, 4 * KiB },
+    { "dram-com",   0x01c62000, 4 * KiB },
+    { "dram-ctl",   0x01c63000, 4 * KiB },
+    { "tcon-top",   0x01c70000, 4 * KiB },
+    { "lcd0",       0x01c71000, 4 * KiB },
+    { "lcd1",       0x01c72000, 4 * KiB },
+    { "tv0",        0x01c73000, 4 * KiB },
+    { "tv1",        0x01c74000, 4 * KiB },
+    { "tve-top",    0x01c90000, 16 * KiB },
+    { "tve0",       0x01c94000, 16 * KiB },
+    { "tve1",       0x01c98000, 16 * KiB },
+    { "mipi_dsi",   0x01ca0000, 4 * KiB },
+    { "mipi_dphy",  0x01ca1000, 4 * KiB },
+    { "ve",         0x01d00000, 1024 * KiB },
+    { "mp",         0x01e80000, 128 * KiB },
+    { "hdmi",       0x01ee0000, 128 * KiB },
+    { "prcm",       0x01f01400, 1 * KiB },
+    { "debug",      0x3f500000, 64 * KiB },
+    { "cpubist",    0x3f501000, 4 * KiB },
+    { "dcu",        0x3fff0000, 64 * KiB },
+    { "hstmr",      0x01c60000, 4 * KiB },
+    { "brom",       0xffff0000, 36 * KiB }
+};
+
+/* Per Processor Interrupts */
+enum {
+    AW_R40_GIC_PPI_MAINT     =  9,
+    AW_R40_GIC_PPI_HYPTIMER  = 10,
+    AW_R40_GIC_PPI_VIRTTIMER = 11,
+    AW_R40_GIC_PPI_SECTIMER  = 13,
+    AW_R40_GIC_PPI_PHYSTIMER = 14
+};
+
+/* Shared Processor Interrupts */
+enum {
+    AW_R40_GIC_SPI_UART0     =  1,
+    AW_R40_GIC_SPI_UART1     =  2,
+    AW_R40_GIC_SPI_UART2     =  3,
+    AW_R40_GIC_SPI_UART3     =  4,
+    AW_R40_GIC_SPI_TIMER0    = 22,
+    AW_R40_GIC_SPI_TIMER1    = 23,
+    AW_R40_GIC_SPI_MMC0      = 32,
+    AW_R40_GIC_SPI_MMC1      = 33,
+    AW_R40_GIC_SPI_MMC2      = 34,
+    AW_R40_GIC_SPI_MMC3      = 35,
+};
+
+/* Allwinner R40 general constants */
+enum {
+    AW_R40_GIC_NUM_SPI       = 128
+};
+
+#define BOOT0_MAGIC             "eGON.BT0"
+
+/* The low 8-bits of the 'boot_media' field in the SPL header */
+#define SUNXI_BOOTED_FROM_MMC0  0
+#define SUNXI_BOOTED_FROM_NAND  1
+#define SUNXI_BOOTED_FROM_MMC2  2
+#define SUNXI_BOOTED_FROM_SPI   3
+
+struct boot_file_head {
+    uint32_t            b_instruction;
+    uint8_t             magic[8];
+    uint32_t            check_sum;
+    uint32_t            length;
+    uint32_t            pub_head_size;
+    uint32_t            fel_script_address;
+    uint32_t            fel_uEnv_length;
+    uint32_t            dt_name_offset;
+    uint32_t            dram_size;
+    uint32_t            boot_media;
+    uint32_t            string_pool[13];
+};
+
+bool allwinner_r40_bootrom_setup(AwR40State *s, BlockBackend *blk, int unit)
+{
+    const int64_t rom_size = 32 * KiB;
+    g_autofree uint8_t *buffer = g_new0(uint8_t, rom_size);
+    struct boot_file_head *head = (struct boot_file_head *)buffer;
+
+    if (blk_pread(blk, 8 * KiB, rom_size, buffer, 0) < 0) {
+        error_setg(&error_fatal, "%s: failed to read BlockBackend data",
+                   __func__);
+        return false;
+    }
+
+    /* we only check the magic string here. */
+    if (memcmp(head->magic, BOOT0_MAGIC, sizeof(head->magic)))
+        return false;
+
+    /* fix the boot media */
+    switch (unit) {
+    case 2:
+        head->boot_media = cpu_to_le32(SUNXI_BOOTED_FROM_MMC2);
+        break;
+    default:
+        head->boot_media = cpu_to_le32(SUNXI_BOOTED_FROM_MMC0);
+        break;
+    }
+
+    rom_add_blob("allwinner-r40.bootrom", buffer, rom_size,
+                  rom_size, s->memmap[AW_R40_DEV_SRAM_A1],
+                  NULL, NULL, NULL, NULL, false);
+    return true;
+}
+
+static void allwinner_r40_init(Object *obj)
+{
+    AwR40State *s = AW_R40(obj);
+
+    s->memmap = allwinner_r40_memmap;
+
+    for (int i = 0; i < AW_R40_NUM_CPUS; i++) {
+        object_initialize_child(obj, "cpu[*]", &s->cpus[i],
+                                ARM_CPU_TYPE_NAME("cortex-a7"));
+    }
+
+    object_initialize_child(obj, "gic", &s->gic, TYPE_ARM_GIC);
+
+    object_initialize_child(obj, "timer", &s->timer, TYPE_AW_A10_PIT);
+    object_property_add_alias(obj, "clk0-freq", OBJECT(&s->timer),
+                              "clk0-freq");
+    object_property_add_alias(obj, "clk1-freq", OBJECT(&s->timer),
+                              "clk1-freq");
+
+    object_initialize_child(obj, "mmc0", &s->mmc0, TYPE_AW_SDHOST_SUN5I);
+    object_initialize_child(obj, "mmc1", &s->mmc1, TYPE_AW_SDHOST_SUN5I);
+    object_initialize_child(obj, "mmc2", &s->mmc2, TYPE_AW_SDHOST_SUN5I);
+    object_initialize_child(obj, "mmc3", &s->mmc3, TYPE_AW_SDHOST_SUN5I);
+}
+
+static void allwinner_r40_realize(DeviceState *dev, Error **errp)
+{
+    AwR40State *s = AW_R40(dev);
+    unsigned i;
+
+    /* CPUs */
+    for (i = 0; i < AW_R40_NUM_CPUS; i++) {
+
+        /*
+         * Disable secondary CPUs. Guest EL3 firmware will start
+         * them via CPU reset control registers.
+         */
+        qdev_prop_set_bit(DEVICE(&s->cpus[i]), "start-powered-off",
+                          i > 0);
+
+        /* All exception levels required */
+        qdev_prop_set_bit(DEVICE(&s->cpus[i]), "has_el3", true);
+        qdev_prop_set_bit(DEVICE(&s->cpus[i]), "has_el2", true);
+
+        /* Mark realized */
+        qdev_realize(DEVICE(&s->cpus[i]), NULL, &error_fatal);
+    }
+
+    /* Generic Interrupt Controller */
+    qdev_prop_set_uint32(DEVICE(&s->gic), "num-irq", AW_R40_GIC_NUM_SPI +
+                                                     GIC_INTERNAL);
+    qdev_prop_set_uint32(DEVICE(&s->gic), "revision", 2);
+    qdev_prop_set_uint32(DEVICE(&s->gic), "num-cpu", AW_R40_NUM_CPUS);
+    qdev_prop_set_bit(DEVICE(&s->gic), "has-security-extensions", false);
+    qdev_prop_set_bit(DEVICE(&s->gic), "has-virtualization-extensions", true);
+    sysbus_realize(SYS_BUS_DEVICE(&s->gic), &error_fatal);
+
+    sysbus_mmio_map(SYS_BUS_DEVICE(&s->gic), 0, s->memmap[AW_R40_DEV_GIC_DIST]);
+    sysbus_mmio_map(SYS_BUS_DEVICE(&s->gic), 1, s->memmap[AW_R40_DEV_GIC_CPU]);
+    sysbus_mmio_map(SYS_BUS_DEVICE(&s->gic), 2, s->memmap[AW_R40_DEV_GIC_HYP]);
+    sysbus_mmio_map(SYS_BUS_DEVICE(&s->gic), 3, s->memmap[AW_R40_DEV_GIC_VCPU]);
+
+    /*
+     * Wire the outputs from each CPU's generic timer and the GICv3
+     * maintenance interrupt signal to the appropriate GIC PPI inputs,
+     * and the GIC's IRQ/FIQ/VIRQ/VFIQ interrupt outputs to the CPU's inputs.
+     */
+    for (i = 0; i < AW_R40_NUM_CPUS; i++) {
+        DeviceState *cpudev = DEVICE(&s->cpus[i]);
+        int ppibase = AW_R40_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_R40_GIC_PPI_PHYSTIMER,
+            [GTIMER_VIRT] = AW_R40_GIC_PPI_VIRTTIMER,
+            [GTIMER_HYP]  = AW_R40_GIC_PPI_HYPTIMER,
+            [GTIMER_SEC]  = AW_R40_GIC_PPI_SECTIMER,
+        };
+
+        /* Connect CPU timer outputs to GIC PPI inputs */
+        for (irq = 0; irq < ARRAY_SIZE(timer_irq); irq++) {
+            qdev_connect_gpio_out(cpudev, irq,
+                                  qdev_get_gpio_in(DEVICE(&s->gic),
+                                                   ppibase + timer_irq[irq]));
+        }
+
+        /* Connect GIC outputs to CPU interrupt inputs */
+        sysbus_connect_irq(SYS_BUS_DEVICE(&s->gic), i,
+                           qdev_get_gpio_in(cpudev, ARM_CPU_IRQ));
+        sysbus_connect_irq(SYS_BUS_DEVICE(&s->gic), i + AW_R40_NUM_CPUS,
+                           qdev_get_gpio_in(cpudev, ARM_CPU_FIQ));
+        sysbus_connect_irq(SYS_BUS_DEVICE(&s->gic), i + (2 * AW_R40_NUM_CPUS),
+                           qdev_get_gpio_in(cpudev, ARM_CPU_VIRQ));
+        sysbus_connect_irq(SYS_BUS_DEVICE(&s->gic), i + (3 * AW_R40_NUM_CPUS),
+                           qdev_get_gpio_in(cpudev, ARM_CPU_VFIQ));
+
+        /* GIC maintenance signal */
+        sysbus_connect_irq(SYS_BUS_DEVICE(&s->gic), i + (4 * AW_R40_NUM_CPUS),
+                           qdev_get_gpio_in(DEVICE(&s->gic),
+                                            ppibase + AW_R40_GIC_PPI_MAINT));
+    }
+
+    /* Timer */
+    sysbus_realize(SYS_BUS_DEVICE(&s->timer), &error_fatal);
+    sysbus_mmio_map(SYS_BUS_DEVICE(&s->timer), 0, s->memmap[AW_R40_DEV_PIT]);
+    sysbus_connect_irq(SYS_BUS_DEVICE(&s->timer), 0,
+                       qdev_get_gpio_in(DEVICE(&s->gic), AW_R40_GIC_SPI_TIMER0));
+    sysbus_connect_irq(SYS_BUS_DEVICE(&s->timer), 1,
+                       qdev_get_gpio_in(DEVICE(&s->gic), AW_R40_GIC_SPI_TIMER1));
+
+    /* SRAM */
+    memory_region_init_ram(&s->sram_a1, OBJECT(dev), "sram A1",
+                            16 * KiB, &error_abort);
+    memory_region_init_ram(&s->sram_a2, OBJECT(dev), "sram A2",
+                            16 * KiB, &error_abort);
+    memory_region_init_ram(&s->sram_a3, OBJECT(dev), "sram A3",
+                            13 * KiB, &error_abort);
+    memory_region_init_ram(&s->sram_a4, OBJECT(dev), "sram A4",
+                            3 * KiB, &error_abort);
+    memory_region_add_subregion(get_system_memory(), s->memmap[AW_R40_DEV_SRAM_A1],
+                                &s->sram_a1);
+    memory_region_add_subregion(get_system_memory(), s->memmap[AW_R40_DEV_SRAM_A2],
+                                &s->sram_a2);
+    memory_region_add_subregion(get_system_memory(), s->memmap[AW_R40_DEV_SRAM_A3],
+                                &s->sram_a3);
+    memory_region_add_subregion(get_system_memory(), s->memmap[AW_R40_DEV_SRAM_A4],
+                                &s->sram_a4);
+
+    /* SD/MMC */
+    object_property_set_link(OBJECT(&s->mmc0), "dma-memory",
+                             OBJECT(get_system_memory()), &error_fatal);
+    sysbus_realize(SYS_BUS_DEVICE(&s->mmc0), &error_fatal);
+    sysbus_mmio_map(SYS_BUS_DEVICE(&s->mmc0), 0, s->memmap[AW_R40_DEV_MMC0]);
+    sysbus_connect_irq(SYS_BUS_DEVICE(&s->mmc0), 0,
+                       qdev_get_gpio_in(DEVICE(&s->gic), AW_R40_GIC_SPI_MMC0));
+
+    object_property_set_link(OBJECT(&s->mmc1), "dma-memory",
+                             OBJECT(get_system_memory()), &error_fatal);
+    sysbus_realize(SYS_BUS_DEVICE(&s->mmc1), &error_fatal);
+    sysbus_mmio_map(SYS_BUS_DEVICE(&s->mmc1), 0, s->memmap[AW_R40_DEV_MMC1]);
+    sysbus_connect_irq(SYS_BUS_DEVICE(&s->mmc1), 0,
+                       qdev_get_gpio_in(DEVICE(&s->gic), AW_R40_DEV_MMC1));
+
+    object_property_set_link(OBJECT(&s->mmc2), "dma-memory",
+                             OBJECT(get_system_memory()), &error_fatal);
+    sysbus_realize(SYS_BUS_DEVICE(&s->mmc2), &error_fatal);
+    sysbus_mmio_map(SYS_BUS_DEVICE(&s->mmc2), 0, s->memmap[AW_R40_DEV_MMC2]);
+    sysbus_connect_irq(SYS_BUS_DEVICE(&s->mmc2), 0,
+                       qdev_get_gpio_in(DEVICE(&s->gic), AW_R40_GIC_SPI_MMC2));
+
+    object_property_set_link(OBJECT(&s->mmc3), "dma-memory",
+                             OBJECT(get_system_memory()), &error_fatal);
+    sysbus_realize(SYS_BUS_DEVICE(&s->mmc3), &error_fatal);
+    sysbus_mmio_map(SYS_BUS_DEVICE(&s->mmc3), 0, s->memmap[AW_R40_DEV_MMC3]);
+    sysbus_connect_irq(SYS_BUS_DEVICE(&s->mmc3), 0,
+                       qdev_get_gpio_in(DEVICE(&s->gic), AW_R40_GIC_SPI_MMC3));
+
+    /* UART0. For future clocktree API: All UARTS are connected to APB2_CLK. */
+    serial_mm_init(get_system_memory(), s->memmap[AW_R40_DEV_UART0], 2,
+                   qdev_get_gpio_in(DEVICE(&s->gic), AW_R40_GIC_SPI_UART0),
+                   115200, serial_hd(0), DEVICE_NATIVE_ENDIAN);
+
+    /* Unimplemented devices */
+    for (i = 0; i < ARRAY_SIZE(r40_unimplemented); i++) {
+        create_unimplemented_device(r40_unimplemented[i].device_name,
+                                    r40_unimplemented[i].base,
+                                    r40_unimplemented[i].size);
+    }
+}
+
+static void allwinner_r40_class_init(ObjectClass *oc, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(oc);
+
+    dc->realize = allwinner_r40_realize;
+    /* Reason: uses serial_hd() in realize function */
+    dc->user_creatable = false;
+}
+
+static const TypeInfo allwinner_r40_type_info = {
+    .name = TYPE_AW_R40,
+    .parent = TYPE_DEVICE,
+    .instance_size = sizeof(AwR40State),
+    .instance_init = allwinner_r40_init,
+    .class_init = allwinner_r40_class_init,
+};
+
+static void allwinner_r40_register_types(void)
+{
+    type_register_static(&allwinner_r40_type_info);
+}
+
+type_init(allwinner_r40_register_types)
diff --git a/hw/arm/bananapi_m2u.c b/hw/arm/bananapi_m2u.c
new file mode 100644
index 0000000000..1b6241719d
--- /dev/null
+++ b/hw/arm/bananapi_m2u.c
@@ -0,0 +1,116 @@
+/*
+ * Bananapi M2U emulation
+ *
+ * Copyright (C) 2023 qianfan Zhao <qianfanguijin@163.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/units.h"
+#include "exec/address-spaces.h"
+#include "qapi/error.h"
+#include "hw/boards.h"
+#include "hw/qdev-properties.h"
+#include "hw/arm/allwinner-r40.h"
+
+static struct arm_boot_info bpim2u_binfo;
+
+static void mmc_attach_drive(AwR40State *s, AwSdHostState *mmc, int unit,
+                             bool load_bootroom, bool *bootroom_loaded)
+{
+    DriveInfo *di = drive_get(IF_SD, 0, unit);
+    BlockBackend *blk = di ? blk_by_legacy_dinfo(di) : NULL;
+    BusState *bus;
+    DeviceState *carddev;
+
+    bus = qdev_get_child_bus(DEVICE(mmc), "sd-bus");
+    if (bus == NULL) {
+        error_report("No SD bus found in SOC object");
+        exit(1);
+    }
+
+    carddev = qdev_new(TYPE_SD_CARD);
+    qdev_prop_set_drive_err(carddev, "drive", blk, &error_fatal);
+    qdev_realize_and_unref(carddev, bus, &error_fatal);
+
+    if (load_bootroom && blk && blk_is_available(blk)) {
+        /* Use Boot ROM to copy data from SD card to SRAM */
+        *bootroom_loaded = allwinner_r40_bootrom_setup(s, blk, unit);
+    }
+}
+
+static void bpim2u_init(MachineState *machine)
+{
+    bool bootroom_loaded = false;
+    AwR40State *r40;
+    I2CBus *i2c;
+
+    /* BIOS is not supported by this board */
+    if (machine->firmware) {
+        error_report("BIOS not supported for this machine");
+        exit(1);
+    }
+
+    /* Only allow Cortex-A7 for this board */
+    if (strcmp(machine->cpu_type, ARM_CPU_TYPE_NAME("cortex-a7")) != 0) {
+        error_report("This board can only be used with cortex-a7 CPU");
+        exit(1);
+    }
+
+    r40 = AW_R40(object_new(TYPE_AW_R40));
+    object_property_add_child(OBJECT(machine), "soc", OBJECT(r40));
+    object_unref(OBJECT(r40));
+
+    /* Setup timer properties */
+    object_property_set_int(OBJECT(r40), "clk0-freq", 32768, &error_abort);
+    object_property_set_int(OBJECT(r40), "clk1-freq", 24 * 1000 * 1000,
+                            &error_abort);
+
+    /* Mark R40 object realized */
+    qdev_realize(DEVICE(r40), NULL, &error_abort);
+
+    /* Plug in SD card and try load bootrom, R40 can boot from mmc0 and mmc2 */
+    mmc_attach_drive(r40, &r40->mmc0, 0,
+                     !machine->kernel_filename && !bootroom_loaded,
+                     &bootroom_loaded);
+    mmc_attach_drive(r40, &r40->mmc1, 1, false, NULL);
+    mmc_attach_drive(r40, &r40->mmc2, 2,
+                     !machine->kernel_filename && !bootroom_loaded,
+                     &bootroom_loaded);
+    mmc_attach_drive(r40, &r40->mmc3, 3, false, NULL);
+
+    /* SDRAM */
+    memory_region_add_subregion(get_system_memory(), r40->memmap[AW_R40_DEV_SDRAM],
+                                machine->ram);
+
+    bpim2u_binfo.loader_start = r40->memmap[AW_R40_DEV_SDRAM];
+    bpim2u_binfo.ram_size = machine->ram_size;
+    bpim2u_binfo.psci_conduit = QEMU_PSCI_CONDUIT_SMC;
+    arm_load_kernel(ARM_CPU(first_cpu), machine, &bpim2u_binfo);
+}
+
+static void bpim2u_machine_init(MachineClass *mc)
+{
+    mc->desc = "Bananapi M2U (Cortex-A7)";
+    mc->init = bpim2u_init;
+    mc->min_cpus = AW_R40_NUM_CPUS;
+    mc->max_cpus = AW_R40_NUM_CPUS;
+    mc->default_cpus = AW_R40_NUM_CPUS;
+    mc->default_cpu_type = ARM_CPU_TYPE_NAME("cortex-a7");
+    mc->default_ram_size = 1 * GiB;
+    mc->default_ram_id = "bpim2u.ram";
+}
+
+DEFINE_MACHINE("bpim2u", bpim2u_machine_init)
diff --git a/hw/arm/meson.build b/hw/arm/meson.build
index b545ba0e4f..870ec67376 100644
--- a/hw/arm/meson.build
+++ b/hw/arm/meson.build
@@ -37,6 +37,7 @@ arm_ss.add(when: 'CONFIG_OMAP', if_true: files('omap1.c', 'omap2.c'))
 arm_ss.add(when: 'CONFIG_STRONGARM', if_true: files('strongarm.c'))
 arm_ss.add(when: 'CONFIG_ALLWINNER_A10', if_true: files('allwinner-a10.c', 'cubieboard.c'))
 arm_ss.add(when: 'CONFIG_ALLWINNER_H3', if_true: files('allwinner-h3.c', 'orangepi.c'))
+arm_ss.add(when: 'CONFIG_ALLWINNER_R40', if_true: files('allwinner-r40.c', 'bananapi_m2u.c'))
 arm_ss.add(when: 'CONFIG_RASPI', if_true: files('bcm2836.c', 'raspi.c'))
 arm_ss.add(when: 'CONFIG_STM32F100_SOC', if_true: files('stm32f100_soc.c'))
 arm_ss.add(when: 'CONFIG_STM32F205_SOC', if_true: files('stm32f205_soc.c'))
diff --git a/include/hw/arm/allwinner-r40.h b/include/hw/arm/allwinner-r40.h
new file mode 100644
index 0000000000..46bda2c54e
--- /dev/null
+++ b/include/hw/arm/allwinner-r40.h
@@ -0,0 +1,111 @@
+/*
+ * Allwinner R40/A40i/T3 System on Chip emulation
+ *
+ * Copyright (C) 2023 qianfan Zhao <qianfanguijin@163.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_R40_H
+#define HW_ARM_ALLWINNER_R40_H
+
+#include "qom/object.h"
+#include "hw/arm/boot.h"
+#include "hw/timer/allwinner-a10-pit.h"
+#include "hw/intc/arm_gic.h"
+#include "hw/sd/allwinner-sdhost.h"
+#include "target/arm/cpu.h"
+#include "sysemu/block-backend.h"
+
+enum {
+    AW_R40_DEV_SRAM_A1,
+    AW_R40_DEV_SRAM_A2,
+    AW_R40_DEV_SRAM_A3,
+    AW_R40_DEV_SRAM_A4,
+    AW_R40_DEV_MMC0,
+    AW_R40_DEV_MMC1,
+    AW_R40_DEV_MMC2,
+    AW_R40_DEV_MMC3,
+    AW_R40_DEV_CCU,
+    AW_R40_DEV_PIT,
+    AW_R40_DEV_UART0,
+    AW_R40_DEV_GIC_DIST,
+    AW_R40_DEV_GIC_CPU,
+    AW_R40_DEV_GIC_HYP,
+    AW_R40_DEV_GIC_VCPU,
+    AW_R40_DEV_SDRAM
+};
+
+#define AW_R40_NUM_CPUS      (4)
+
+/**
+ * Allwinner R40 object model
+ * @{
+ */
+
+/** Object type for the Allwinner R40 SoC */
+#define TYPE_AW_R40 "allwinner-r40"
+
+/** Convert input object to Allwinner R40 state object */
+OBJECT_DECLARE_SIMPLE_TYPE(AwR40State, AW_R40)
+
+/** @} */
+
+/**
+ * Allwinner R40 object
+ *
+ * This struct contains the state of all the devices
+ * which are currently emulated by the R40 SoC code.
+ */
+struct AwR40State {
+    /*< private >*/
+    DeviceState parent_obj;
+    /*< public >*/
+
+    ARMCPU cpus[AW_R40_NUM_CPUS];
+    const hwaddr *memmap;
+    AwA10PITState timer;
+    AwSdHostState mmc0;
+    AwSdHostState mmc1;
+    AwSdHostState mmc2;
+    AwSdHostState mmc3;
+    GICState gic;
+    MemoryRegion sram_a1;
+    MemoryRegion sram_a2;
+    MemoryRegion sram_a3;
+    MemoryRegion sram_a4;
+};
+
+/**
+ * Emulate Boot ROM firmware setup functionality.
+ *
+ * A real Allwinner R40 SoC contains a Boot ROM
+ * which is the first code that runs right after
+ * the SoC is powered on. The Boot ROM is responsible
+ * for loading user code (e.g. a bootloader) from any
+ * of the supported external devices and writing the
+ * downloaded code to internal SRAM. After loading the SoC
+ * begins executing the code written to SRAM.
+ *
+ * This function emulates the Boot ROM by copying 32 KiB
+ * of data from the given block device and writes it to
+ * the start of the first internal SRAM memory.
+ *
+ * @s: Allwinner R40 state object pointer
+ * @blk: Block backend device object pointer
+ * @unit: the mmc control's unit
+ */
+bool allwinner_r40_bootrom_setup(AwR40State *s, BlockBackend *blk, int unit);
+
+#endif /* HW_ARM_ALLWINNER_R40_H */
-- 
2.25.1



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

* [PATCH v1 02/11] hw/arm/allwinner-r40: add Clock Control Unit
  2023-03-21 10:24 [PATCH v1 00/11] *** add allwinner-r40 support *** qianfanguijin
  2023-03-21 10:25 ` [PATCH v1 01/11] hw: arm: Add bananapi M2-Ultra and allwinner-r40 support qianfanguijin
@ 2023-03-21 10:25 ` qianfanguijin
  2023-03-25 21:12   ` Strahinja Jankovic
  2023-03-21 10:25 ` [PATCH v1 03/11] hw: allwinner-r40: Complete uart devices qianfanguijin
                   ` (8 subsequent siblings)
  10 siblings, 1 reply; 17+ messages in thread
From: qianfanguijin @ 2023-03-21 10:25 UTC (permalink / raw)
  To: qemu-arm, qemu-devel
  Cc: Strahinja Jankovic, Peter Maydell, Beniamino Galvani,
	Philippe Mathieu-Daudé,
	Niek Linnenbank, qianfan Zhao

From: qianfan Zhao <qianfanguijin@163.com>

The CCU provides the registers to program the PLLs and the controls
most of the clock generation, division, distribution, synchronization
and gating.

This commit adds support for the Clock Control Unit which emulates
a simple read/write register interface.

Signed-off-by: qianfan Zhao <qianfanguijin@163.com>
---
 hw/arm/allwinner-r40.c              |   7 +-
 hw/misc/allwinner-r40-ccu.c         | 207 ++++++++++++++++++++++++++++
 hw/misc/meson.build                 |   1 +
 include/hw/arm/allwinner-r40.h      |   2 +
 include/hw/misc/allwinner-r40-ccu.h |  65 +++++++++
 5 files changed, 281 insertions(+), 1 deletion(-)
 create mode 100644 hw/misc/allwinner-r40-ccu.c
 create mode 100644 include/hw/misc/allwinner-r40-ccu.h

diff --git a/hw/arm/allwinner-r40.c b/hw/arm/allwinner-r40.c
index d0516f4e96..3517682aed 100644
--- a/hw/arm/allwinner-r40.c
+++ b/hw/arm/allwinner-r40.c
@@ -42,6 +42,7 @@ const hwaddr allwinner_r40_memmap[] = {
     [AW_R40_DEV_MMC1]       = 0x01c10000,
     [AW_R40_DEV_MMC2]       = 0x01c11000,
     [AW_R40_DEV_MMC3]       = 0x01c12000,
+    [AW_R40_DEV_CCU]        = 0x01c20000,
     [AW_R40_DEV_PIT]        = 0x01c20c00,
     [AW_R40_DEV_UART0]      = 0x01c28000,
     [AW_R40_DEV_GIC_DIST]   = 0x01c81000,
@@ -80,7 +81,6 @@ static struct AwR40Unimplemented r40_unimplemented[] = {
     { "usb2-host",  0x01c1c000, 4 * KiB },
     { "cs1",        0x01c1d000, 4 * KiB },
     { "spi3",       0x01c1f000, 4 * KiB },
-    { "ccu",        0x01c20000, 1 * KiB },
     { "rtc",        0x01c20400, 1 * KiB },
     { "pio",        0x01c20800, 1 * KiB },
     { "owa",        0x01c21000, 1 * KiB },
@@ -246,6 +246,7 @@ static void allwinner_r40_init(Object *obj)
     object_property_add_alias(obj, "clk1-freq", OBJECT(&s->timer),
                               "clk1-freq");
 
+    object_initialize_child(obj, "ccu", &s->ccu, TYPE_AW_R40_CCU);
     object_initialize_child(obj, "mmc0", &s->mmc0, TYPE_AW_SDHOST_SUN5I);
     object_initialize_child(obj, "mmc1", &s->mmc1, TYPE_AW_SDHOST_SUN5I);
     object_initialize_child(obj, "mmc2", &s->mmc2, TYPE_AW_SDHOST_SUN5I);
@@ -358,6 +359,10 @@ static void allwinner_r40_realize(DeviceState *dev, Error **errp)
     memory_region_add_subregion(get_system_memory(), s->memmap[AW_R40_DEV_SRAM_A4],
                                 &s->sram_a4);
 
+    /* Clock Control Unit */
+    sysbus_realize(SYS_BUS_DEVICE(&s->ccu), &error_fatal);
+    sysbus_mmio_map(SYS_BUS_DEVICE(&s->ccu), 0, s->memmap[AW_R40_DEV_CCU]);
+
     /* SD/MMC */
     object_property_set_link(OBJECT(&s->mmc0), "dma-memory",
                              OBJECT(get_system_memory()), &error_fatal);
diff --git a/hw/misc/allwinner-r40-ccu.c b/hw/misc/allwinner-r40-ccu.c
new file mode 100644
index 0000000000..0abe006874
--- /dev/null
+++ b/hw/misc/allwinner-r40-ccu.c
@@ -0,0 +1,207 @@
+/*
+ * Allwinner R40 Clock Control Unit emulation
+ *
+ * Copyright (C) 2023 qianfan Zhao <qianfanguijin@163.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/units.h"
+#include "hw/sysbus.h"
+#include "migration/vmstate.h"
+#include "qemu/log.h"
+#include "qemu/module.h"
+#include "hw/misc/allwinner-r40-ccu.h"
+
+/* CCU register offsets */
+enum {
+    REG_PLL_CPUX_CTRL           = 0x0000,
+    REG_PLL_AUDIO_CTRL          = 0x0008,
+    REG_PLL_VIDEO0_CTRL         = 0x0010,
+    REG_PLL_VE_CTRL             = 0x0018,
+    REG_PLL_DDR0_CTRL           = 0x0020,
+    REG_PLL_PERIPH0_CTRL        = 0x0028,
+    REG_PLL_PERIPH1_CTRL        = 0x002c,
+    REG_PLL_VIDEO1_CTRL         = 0x0030,
+    REG_PLL_SATA_CTRL           = 0x0034,
+    REG_PLL_GPU_CTRL            = 0x0038,
+    REG_PLL_MIPI_CTRL           = 0x0040,
+    REG_PLL_DE_CTRL             = 0x0048,
+    REG_PLL_DDR1_CTRL           = 0x004c,
+    REG_AHB1_APB1_CFG           = 0x0054,
+    REG_APB2_CFG                = 0x0058,
+    REG_MMC0_CLK                = 0x0088,
+    REG_MMC1_CLK                = 0x008c,
+    REG_MMC2_CLK                = 0x0090,
+    REG_MMC3_CLK                = 0x0094,
+    REG_USBPHY_CFG              = 0x00cc,
+    REG_PLL_DDR_AUX             = 0x00f0,
+    REG_DRAM_CFG                = 0x00f4,
+    REG_PLL_DDR1_CFG            = 0x00f8,
+    REG_DRAM_CLK_GATING         = 0x0100,
+    REG_GMAC_CLK                = 0x0164,
+    REG_SYS_32K_CLK             = 0x0310,
+    REG_PLL_LOCK_CTRL           = 0x0320,
+};
+
+#define REG_INDEX(offset)    (offset / sizeof(uint32_t))
+
+/* CCU register flags */
+enum {
+    REG_PLL_ENABLE           = (1 << 31),
+    REG_PLL_LOCK             = (1 << 28),
+};
+
+static uint64_t allwinner_r40_ccu_read(void *opaque, hwaddr offset,
+                                       unsigned size)
+{
+    const AwR40ClockCtlState *s = AW_R40_CCU(opaque);
+    const uint32_t idx = REG_INDEX(offset);
+
+    switch (offset) {
+    case 0x324 ... AW_R40_CCU_IOSIZE:
+        qemu_log_mask(LOG_GUEST_ERROR, "%s: out-of-bounds offset 0x%04x\n",
+                      __func__, (uint32_t)offset);
+        return 0;
+    }
+
+    return s->regs[idx];
+}
+
+static void allwinner_r40_ccu_write(void *opaque, hwaddr offset,
+                                    uint64_t val, unsigned size)
+{
+    AwR40ClockCtlState *s = AW_R40_CCU(opaque);
+
+    switch (offset) {
+    case REG_DRAM_CFG:    /* DRAM Configuration(for DDR0) */
+        /* bit16: SDRCLK_UPD (SDRCLK configuration 0 update) */
+        val &= ~(1 << 16);
+        break;
+    case REG_PLL_DDR1_CTRL: /* DDR1 Control register */
+        /* bit30: SDRPLL_UPD */
+        val &= ~(1 << 30);
+        if (val & REG_PLL_ENABLE)
+            val |= REG_PLL_LOCK;
+        break;
+    case REG_PLL_CPUX_CTRL:
+    case REG_PLL_AUDIO_CTRL:
+    case REG_PLL_VE_CTRL:
+    case REG_PLL_VIDEO0_CTRL:
+    case REG_PLL_DDR0_CTRL:
+    case REG_PLL_PERIPH0_CTRL:
+    case REG_PLL_PERIPH1_CTRL:
+    case REG_PLL_VIDEO1_CTRL:
+    case REG_PLL_SATA_CTRL:
+    case REG_PLL_GPU_CTRL:
+    case REG_PLL_MIPI_CTRL:
+    case REG_PLL_DE_CTRL:
+        if (val & REG_PLL_ENABLE)
+            val |= REG_PLL_LOCK;
+        break;
+    case 0x324 ... AW_R40_CCU_IOSIZE:
+        qemu_log_mask(LOG_GUEST_ERROR, "%s: out-of-bounds offset 0x%04x\n",
+                      __func__, (uint32_t)offset);
+        break;
+    default:
+        qemu_log_mask(LOG_UNIMP, "%s: unimplemented write offset 0x%04x\n",
+                      __func__, (uint32_t)offset);
+        break;
+    }
+
+    s->regs[REG_INDEX(offset)] = (uint32_t) val;
+}
+
+static const MemoryRegionOps allwinner_r40_ccu_ops = {
+    .read = allwinner_r40_ccu_read,
+    .write = allwinner_r40_ccu_write,
+    .endianness = DEVICE_NATIVE_ENDIAN,
+    .valid = {
+        .min_access_size = 4,
+        .max_access_size = 4,
+    },
+    .impl.min_access_size = 4,
+};
+
+static void allwinner_r40_ccu_reset(DeviceState *dev)
+{
+    AwR40ClockCtlState *s = AW_R40_CCU(dev);
+
+    memset(s->regs, 0, sizeof(s->regs));
+
+    /* Set default values for registers */
+    s->regs[REG_INDEX(REG_PLL_CPUX_CTRL)]       = 0x00001000;
+    s->regs[REG_INDEX(REG_PLL_AUDIO_CTRL)]      = 0x00035514;
+    s->regs[REG_INDEX(REG_PLL_VIDEO0_CTRL)]     = 0x03006207;
+    s->regs[REG_INDEX(REG_PLL_VE_CTRL)]         = 0x03006207;
+    s->regs[REG_INDEX(REG_PLL_DDR0_CTRL)]       = 0x00001000,
+    s->regs[REG_INDEX(REG_PLL_PERIPH0_CTRL)]    = 0x00041811;
+    s->regs[REG_INDEX(REG_PLL_PERIPH1_CTRL)]    = 0x00041811;
+    s->regs[REG_INDEX(REG_PLL_VIDEO1_CTRL)]     = 0x03006207;
+    s->regs[REG_INDEX(REG_PLL_SATA_CTRL)]       = 0x00001811;
+    s->regs[REG_INDEX(REG_PLL_GPU_CTRL)]        = 0x03006207;
+    s->regs[REG_INDEX(REG_PLL_MIPI_CTRL)]       = 0x00000515;
+    s->regs[REG_INDEX(REG_PLL_DE_CTRL)]         = 0x03006207;
+    s->regs[REG_INDEX(REG_PLL_DDR1_CTRL)]       = 0x00001800;
+    s->regs[REG_INDEX(REG_AHB1_APB1_CFG)]       = 0x00001010;
+    s->regs[REG_INDEX(REG_APB2_CFG)]            = 0x01000000;
+    s->regs[REG_INDEX(REG_PLL_DDR_AUX)]         = 0x00000001;
+    s->regs[REG_INDEX(REG_PLL_DDR1_CFG)]        = 0x0ccca000;
+    s->regs[REG_INDEX(REG_SYS_32K_CLK)]         = 0x0000000f;
+}
+
+static void allwinner_r40_ccu_init(Object *obj)
+{
+    SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
+    AwR40ClockCtlState *s = AW_R40_CCU(obj);
+
+    /* Memory mapping */
+    memory_region_init_io(&s->iomem, OBJECT(s), &allwinner_r40_ccu_ops, s,
+                          TYPE_AW_R40_CCU, AW_R40_CCU_IOSIZE);
+    sysbus_init_mmio(sbd, &s->iomem);
+}
+
+static const VMStateDescription allwinner_r40_ccu_vmstate = {
+    .name = "allwinner-r40-ccu",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .fields = (VMStateField[]) {
+        VMSTATE_UINT32_ARRAY(regs, AwR40ClockCtlState, AW_R40_CCU_REGS_NUM),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static void allwinner_r40_ccu_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+
+    dc->reset = allwinner_r40_ccu_reset;
+    dc->vmsd = &allwinner_r40_ccu_vmstate;
+}
+
+static const TypeInfo allwinner_r40_ccu_info = {
+    .name          = TYPE_AW_R40_CCU,
+    .parent        = TYPE_SYS_BUS_DEVICE,
+    .instance_init = allwinner_r40_ccu_init,
+    .instance_size = sizeof(AwR40ClockCtlState),
+    .class_init    = allwinner_r40_ccu_class_init,
+};
+
+static void allwinner_r40_ccu_register(void)
+{
+    type_register_static(&allwinner_r40_ccu_info);
+}
+
+type_init(allwinner_r40_ccu_register)
diff --git a/hw/misc/meson.build b/hw/misc/meson.build
index a40245ad44..96e35f1cdb 100644
--- a/hw/misc/meson.build
+++ b/hw/misc/meson.build
@@ -44,6 +44,7 @@ specific_ss.add(when: 'CONFIG_ALLWINNER_H3', if_true: files('allwinner-cpucfg.c'
 softmmu_ss.add(when: 'CONFIG_ALLWINNER_H3', if_true: files('allwinner-h3-dramc.c'))
 softmmu_ss.add(when: 'CONFIG_ALLWINNER_H3', if_true: files('allwinner-h3-sysctrl.c'))
 softmmu_ss.add(when: 'CONFIG_ALLWINNER_H3', if_true: files('allwinner-sid.c'))
+softmmu_ss.add(when: 'CONFIG_ALLWINNER_R40', if_true: files('allwinner-r40-ccu.c'))
 softmmu_ss.add(when: 'CONFIG_AXP209_PMU', if_true: files('axp209.c'))
 softmmu_ss.add(when: 'CONFIG_REALVIEW', if_true: files('arm_sysctl.c'))
 softmmu_ss.add(when: 'CONFIG_NSERIES', if_true: files('cbus.c'))
diff --git a/include/hw/arm/allwinner-r40.h b/include/hw/arm/allwinner-r40.h
index 46bda2c54e..b355af2c4f 100644
--- a/include/hw/arm/allwinner-r40.h
+++ b/include/hw/arm/allwinner-r40.h
@@ -25,6 +25,7 @@
 #include "hw/timer/allwinner-a10-pit.h"
 #include "hw/intc/arm_gic.h"
 #include "hw/sd/allwinner-sdhost.h"
+#include "hw/misc/allwinner-r40-ccu.h"
 #include "target/arm/cpu.h"
 #include "sysemu/block-backend.h"
 
@@ -80,6 +81,7 @@ struct AwR40State {
     AwSdHostState mmc1;
     AwSdHostState mmc2;
     AwSdHostState mmc3;
+    AwR40ClockCtlState ccu;
     GICState gic;
     MemoryRegion sram_a1;
     MemoryRegion sram_a2;
diff --git a/include/hw/misc/allwinner-r40-ccu.h b/include/hw/misc/allwinner-r40-ccu.h
new file mode 100644
index 0000000000..ceb74eff92
--- /dev/null
+++ b/include/hw/misc/allwinner-r40-ccu.h
@@ -0,0 +1,65 @@
+/*
+ * Allwinner R40 Clock Control Unit emulation
+ *
+ * Copyright (C) 2023 qianfan Zhao <qianfanguijin@163.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_R40_CCU_H
+#define HW_MISC_ALLWINNER_R40_CCU_H
+
+#include "qom/object.h"
+#include "hw/sysbus.h"
+
+/**
+ * @name Constants
+ * @{
+ */
+
+/** Size of register I/O address space used by CCU device */
+#define AW_R40_CCU_IOSIZE        (0x400)
+
+/** Total number of known registers */
+#define AW_R40_CCU_REGS_NUM      (AW_R40_CCU_IOSIZE / sizeof(uint32_t))
+
+/** @} */
+
+/**
+ * @name Object model
+ * @{
+ */
+
+#define TYPE_AW_R40_CCU    "allwinner-r40-ccu"
+OBJECT_DECLARE_SIMPLE_TYPE(AwR40ClockCtlState, AW_R40_CCU)
+
+/** @} */
+
+/**
+ * Allwinner R40 CCU object instance state.
+ */
+struct AwR40ClockCtlState {
+    /*< private >*/
+    SysBusDevice parent_obj;
+    /*< public >*/
+
+    /** Maps I/O registers in physical memory */
+    MemoryRegion iomem;
+
+    /** Array of hardware registers */
+    uint32_t regs[AW_R40_CCU_REGS_NUM];
+
+};
+
+#endif /* HW_MISC_ALLWINNER_R40_CCU_H */
-- 
2.25.1



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

* [PATCH v1 03/11] hw: allwinner-r40: Complete uart devices
  2023-03-21 10:24 [PATCH v1 00/11] *** add allwinner-r40 support *** qianfanguijin
  2023-03-21 10:25 ` [PATCH v1 01/11] hw: arm: Add bananapi M2-Ultra and allwinner-r40 support qianfanguijin
  2023-03-21 10:25 ` [PATCH v1 02/11] hw/arm/allwinner-r40: add Clock Control Unit qianfanguijin
@ 2023-03-21 10:25 ` qianfanguijin
  2023-03-25 21:15   ` Strahinja Jankovic
  2023-03-21 10:25 ` [PATCH v1 04/11] hw: arm: allwinner-r40: Add 5 TWI controllers qianfanguijin
                   ` (7 subsequent siblings)
  10 siblings, 1 reply; 17+ messages in thread
From: qianfanguijin @ 2023-03-21 10:25 UTC (permalink / raw)
  To: qemu-arm, qemu-devel
  Cc: Strahinja Jankovic, Peter Maydell, Beniamino Galvani,
	Philippe Mathieu-Daudé,
	Niek Linnenbank, qianfan Zhao

From: qianfan Zhao <qianfanguijin@163.com>

R40 has eight UARTs, support both 16450 and 16550 compatible modes.

Signed-off-by: qianfan Zhao <qianfanguijin@163.com>
---
 hw/arm/allwinner-r40.c         | 32 ++++++++++++++++++++++++++++++++
 include/hw/arm/allwinner-r40.h |  7 +++++++
 2 files changed, 39 insertions(+)

diff --git a/hw/arm/allwinner-r40.c b/hw/arm/allwinner-r40.c
index 3517682aed..fde01783b1 100644
--- a/hw/arm/allwinner-r40.c
+++ b/hw/arm/allwinner-r40.c
@@ -45,6 +45,13 @@ const hwaddr allwinner_r40_memmap[] = {
     [AW_R40_DEV_CCU]        = 0x01c20000,
     [AW_R40_DEV_PIT]        = 0x01c20c00,
     [AW_R40_DEV_UART0]      = 0x01c28000,
+    [AW_R40_DEV_UART1]      = 0x01c28400,
+    [AW_R40_DEV_UART2]      = 0x01c28800,
+    [AW_R40_DEV_UART3]      = 0x01c28c00,
+    [AW_R40_DEV_UART4]      = 0x01c29000,
+    [AW_R40_DEV_UART5]      = 0x01c29400,
+    [AW_R40_DEV_UART6]      = 0x01c29800,
+    [AW_R40_DEV_UART7]      = 0x01c29c00,
     [AW_R40_DEV_GIC_DIST]   = 0x01c81000,
     [AW_R40_DEV_GIC_CPU]    = 0x01c82000,
     [AW_R40_DEV_GIC_HYP]    = 0x01c84000,
@@ -160,6 +167,10 @@ enum {
     AW_R40_GIC_SPI_UART1     =  2,
     AW_R40_GIC_SPI_UART2     =  3,
     AW_R40_GIC_SPI_UART3     =  4,
+    AW_R40_GIC_SPI_UART4     = 17,
+    AW_R40_GIC_SPI_UART5     = 18,
+    AW_R40_GIC_SPI_UART6     = 19,
+    AW_R40_GIC_SPI_UART7     = 20,
     AW_R40_GIC_SPI_TIMER0    = 22,
     AW_R40_GIC_SPI_TIMER1    = 23,
     AW_R40_GIC_SPI_MMC0      = 32,
@@ -396,6 +407,27 @@ static void allwinner_r40_realize(DeviceState *dev, Error **errp)
     serial_mm_init(get_system_memory(), s->memmap[AW_R40_DEV_UART0], 2,
                    qdev_get_gpio_in(DEVICE(&s->gic), AW_R40_GIC_SPI_UART0),
                    115200, serial_hd(0), DEVICE_NATIVE_ENDIAN);
+    serial_mm_init(get_system_memory(), s->memmap[AW_R40_DEV_UART1], 2,
+                   qdev_get_gpio_in(DEVICE(&s->gic), AW_R40_GIC_SPI_UART1),
+                   115200, serial_hd(1), DEVICE_NATIVE_ENDIAN);
+    serial_mm_init(get_system_memory(), s->memmap[AW_R40_DEV_UART2], 2,
+                   qdev_get_gpio_in(DEVICE(&s->gic), AW_R40_GIC_SPI_UART2),
+                   115200, serial_hd(2), DEVICE_NATIVE_ENDIAN);
+    serial_mm_init(get_system_memory(), s->memmap[AW_R40_DEV_UART3], 2,
+                   qdev_get_gpio_in(DEVICE(&s->gic), AW_R40_GIC_SPI_UART3),
+                   115200, serial_hd(3), DEVICE_NATIVE_ENDIAN);
+    serial_mm_init(get_system_memory(), s->memmap[AW_R40_DEV_UART4], 2,
+                   qdev_get_gpio_in(DEVICE(&s->gic), AW_R40_GIC_SPI_UART4),
+                   115200, serial_hd(4), DEVICE_NATIVE_ENDIAN);
+    serial_mm_init(get_system_memory(), s->memmap[AW_R40_DEV_UART5], 2,
+                   qdev_get_gpio_in(DEVICE(&s->gic), AW_R40_GIC_SPI_UART5),
+                   115200, serial_hd(5), DEVICE_NATIVE_ENDIAN);
+    serial_mm_init(get_system_memory(), s->memmap[AW_R40_DEV_UART6], 2,
+                   qdev_get_gpio_in(DEVICE(&s->gic), AW_R40_GIC_SPI_UART6),
+                   115200, serial_hd(6), DEVICE_NATIVE_ENDIAN);
+    serial_mm_init(get_system_memory(), s->memmap[AW_R40_DEV_UART7], 2,
+                   qdev_get_gpio_in(DEVICE(&s->gic), AW_R40_GIC_SPI_UART7),
+                   115200, serial_hd(7), DEVICE_NATIVE_ENDIAN);
 
     /* Unimplemented devices */
     for (i = 0; i < ARRAY_SIZE(r40_unimplemented); i++) {
diff --git a/include/hw/arm/allwinner-r40.h b/include/hw/arm/allwinner-r40.h
index b355af2c4f..dfb5eb609c 100644
--- a/include/hw/arm/allwinner-r40.h
+++ b/include/hw/arm/allwinner-r40.h
@@ -41,6 +41,13 @@ enum {
     AW_R40_DEV_CCU,
     AW_R40_DEV_PIT,
     AW_R40_DEV_UART0,
+    AW_R40_DEV_UART1,
+    AW_R40_DEV_UART2,
+    AW_R40_DEV_UART3,
+    AW_R40_DEV_UART4,
+    AW_R40_DEV_UART5,
+    AW_R40_DEV_UART6,
+    AW_R40_DEV_UART7,
     AW_R40_DEV_GIC_DIST,
     AW_R40_DEV_GIC_CPU,
     AW_R40_DEV_GIC_HYP,
-- 
2.25.1



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

* [PATCH v1 04/11] hw: arm: allwinner-r40: Add 5 TWI controllers
  2023-03-21 10:24 [PATCH v1 00/11] *** add allwinner-r40 support *** qianfanguijin
                   ` (2 preceding siblings ...)
  2023-03-21 10:25 ` [PATCH v1 03/11] hw: allwinner-r40: Complete uart devices qianfanguijin
@ 2023-03-21 10:25 ` qianfanguijin
  2023-03-25 21:21   ` Strahinja Jankovic
  2023-03-21 10:25 ` [PATCH v1 05/11] hw/misc: AXP221 PMU Emulation qianfanguijin
                   ` (6 subsequent siblings)
  10 siblings, 1 reply; 17+ messages in thread
From: qianfanguijin @ 2023-03-21 10:25 UTC (permalink / raw)
  To: qemu-arm, qemu-devel
  Cc: Strahinja Jankovic, Peter Maydell, Beniamino Galvani,
	Philippe Mathieu-Daudé,
	Niek Linnenbank, qianfan Zhao

From: qianfan Zhao <qianfanguijin@163.com>

TWI(i2c) is designed to be used as an interface between CPU host and the
serial 2-Wire bus. It can support all standard 2-Wire transfer, can be
operated in standard mode(100kbit/s) or fast-mode, supporting data rate
up to 400kbit/s.

Signed-off-by: qianfan Zhao <qianfanguijin@163.com>
---
 hw/arm/allwinner-r40.c         | 47 ++++++++++++++++++++++++++++++----
 include/hw/arm/allwinner-r40.h | 11 ++++++++
 2 files changed, 53 insertions(+), 5 deletions(-)

diff --git a/hw/arm/allwinner-r40.c b/hw/arm/allwinner-r40.c
index fde01783b1..9fa23e1f33 100644
--- a/hw/arm/allwinner-r40.c
+++ b/hw/arm/allwinner-r40.c
@@ -52,6 +52,11 @@ const hwaddr allwinner_r40_memmap[] = {
     [AW_R40_DEV_UART5]      = 0x01c29400,
     [AW_R40_DEV_UART6]      = 0x01c29800,
     [AW_R40_DEV_UART7]      = 0x01c29c00,
+    [AW_R40_DEV_TWI0]       = 0x01c2ac00,
+    [AW_R40_DEV_TWI1]       = 0x01c2b000,
+    [AW_R40_DEV_TWI2]       = 0x01c2b400,
+    [AW_R40_DEV_TWI3]       = 0x01c2b800,
+    [AW_R40_DEV_TWI4]       = 0x01c2c000,
     [AW_R40_DEV_GIC_DIST]   = 0x01c81000,
     [AW_R40_DEV_GIC_CPU]    = 0x01c82000,
     [AW_R40_DEV_GIC_HYP]    = 0x01c84000,
@@ -115,11 +120,6 @@ static struct AwR40Unimplemented r40_unimplemented[] = {
     { "uart7",      0x01c29c00, 1 * KiB },
     { "ps20",       0x01c2a000, 1 * KiB },
     { "ps21",       0x01c2a400, 1 * KiB },
-    { "twi0",       0x01c2ac00, 1 * KiB },
-    { "twi1",       0x01c2b000, 1 * KiB },
-    { "twi2",       0x01c2b400, 1 * KiB },
-    { "twi3",       0x01c2b800, 1 * KiB },
-    { "twi4",       0x01c2c000, 1 * KiB },
     { "scr",        0x01c2c400, 1 * KiB },
     { "tvd-top",    0x01c30000, 4 * KiB },
     { "tvd0",       0x01c31000, 4 * KiB },
@@ -167,6 +167,9 @@ enum {
     AW_R40_GIC_SPI_UART1     =  2,
     AW_R40_GIC_SPI_UART2     =  3,
     AW_R40_GIC_SPI_UART3     =  4,
+    AW_R40_GIC_SPI_TWI0      =  7,
+    AW_R40_GIC_SPI_TWI1      =  8,
+    AW_R40_GIC_SPI_TWI2      =  9,
     AW_R40_GIC_SPI_UART4     = 17,
     AW_R40_GIC_SPI_UART5     = 18,
     AW_R40_GIC_SPI_UART6     = 19,
@@ -177,6 +180,8 @@ enum {
     AW_R40_GIC_SPI_MMC1      = 33,
     AW_R40_GIC_SPI_MMC2      = 34,
     AW_R40_GIC_SPI_MMC3      = 35,
+    AW_R40_GIC_SPI_TWI3      = 88,
+    AW_R40_GIC_SPI_TWI4      = 89,
 };
 
 /* Allwinner R40 general constants */
@@ -262,6 +267,12 @@ static void allwinner_r40_init(Object *obj)
     object_initialize_child(obj, "mmc1", &s->mmc1, TYPE_AW_SDHOST_SUN5I);
     object_initialize_child(obj, "mmc2", &s->mmc2, TYPE_AW_SDHOST_SUN5I);
     object_initialize_child(obj, "mmc3", &s->mmc3, TYPE_AW_SDHOST_SUN5I);
+
+    object_initialize_child(obj, "twi0", &s->i2c0, TYPE_AW_I2C_SUN6I);
+    object_initialize_child(obj, "twi1", &s->i2c1, TYPE_AW_I2C_SUN6I);
+    object_initialize_child(obj, "twi2", &s->i2c2, TYPE_AW_I2C_SUN6I);
+    object_initialize_child(obj, "twi3", &s->i2c3, TYPE_AW_I2C_SUN6I);
+    object_initialize_child(obj, "twi4", &s->i2c4, TYPE_AW_I2C_SUN6I);
 }
 
 static void allwinner_r40_realize(DeviceState *dev, Error **errp)
@@ -429,6 +440,32 @@ static void allwinner_r40_realize(DeviceState *dev, Error **errp)
                    qdev_get_gpio_in(DEVICE(&s->gic), AW_R40_GIC_SPI_UART7),
                    115200, serial_hd(7), DEVICE_NATIVE_ENDIAN);
 
+    /* I2C */
+    sysbus_realize(SYS_BUS_DEVICE(&s->i2c0), &error_fatal);
+    sysbus_mmio_map(SYS_BUS_DEVICE(&s->i2c0), 0, s->memmap[AW_R40_DEV_TWI0]);
+    sysbus_connect_irq(SYS_BUS_DEVICE(&s->i2c0), 0,
+                       qdev_get_gpio_in(DEVICE(&s->gic), AW_R40_GIC_SPI_TWI0));
+
+    sysbus_realize(SYS_BUS_DEVICE(&s->i2c1), &error_fatal);
+    sysbus_mmio_map(SYS_BUS_DEVICE(&s->i2c1), 0, s->memmap[AW_R40_DEV_TWI1]);
+    sysbus_connect_irq(SYS_BUS_DEVICE(&s->i2c1), 0,
+                       qdev_get_gpio_in(DEVICE(&s->gic), AW_R40_GIC_SPI_TWI1));
+
+    sysbus_realize(SYS_BUS_DEVICE(&s->i2c2), &error_fatal);
+    sysbus_mmio_map(SYS_BUS_DEVICE(&s->i2c2), 0, s->memmap[AW_R40_DEV_TWI2]);
+    sysbus_connect_irq(SYS_BUS_DEVICE(&s->i2c2), 0,
+                       qdev_get_gpio_in(DEVICE(&s->gic), AW_R40_GIC_SPI_TWI2));
+
+    sysbus_realize(SYS_BUS_DEVICE(&s->i2c3), &error_fatal);
+    sysbus_mmio_map(SYS_BUS_DEVICE(&s->i2c3), 0, s->memmap[AW_R40_DEV_TWI3]);
+    sysbus_connect_irq(SYS_BUS_DEVICE(&s->i2c3), 0,
+                       qdev_get_gpio_in(DEVICE(&s->gic), AW_R40_GIC_SPI_TWI3));
+
+    sysbus_realize(SYS_BUS_DEVICE(&s->i2c4), &error_fatal);
+    sysbus_mmio_map(SYS_BUS_DEVICE(&s->i2c4), 0, s->memmap[AW_R40_DEV_TWI4]);
+    sysbus_connect_irq(SYS_BUS_DEVICE(&s->i2c4), 0,
+                       qdev_get_gpio_in(DEVICE(&s->gic), AW_R40_GIC_SPI_TWI4));
+
     /* Unimplemented devices */
     for (i = 0; i < ARRAY_SIZE(r40_unimplemented); i++) {
         create_unimplemented_device(r40_unimplemented[i].device_name,
diff --git a/include/hw/arm/allwinner-r40.h b/include/hw/arm/allwinner-r40.h
index dfb5eb609c..6a7e5c1e31 100644
--- a/include/hw/arm/allwinner-r40.h
+++ b/include/hw/arm/allwinner-r40.h
@@ -26,6 +26,7 @@
 #include "hw/intc/arm_gic.h"
 #include "hw/sd/allwinner-sdhost.h"
 #include "hw/misc/allwinner-r40-ccu.h"
+#include "hw/i2c/allwinner-i2c.h"
 #include "target/arm/cpu.h"
 #include "sysemu/block-backend.h"
 
@@ -48,6 +49,11 @@ enum {
     AW_R40_DEV_UART5,
     AW_R40_DEV_UART6,
     AW_R40_DEV_UART7,
+    AW_R40_DEV_TWI0,
+    AW_R40_DEV_TWI1,
+    AW_R40_DEV_TWI2,
+    AW_R40_DEV_TWI3,
+    AW_R40_DEV_TWI4,
     AW_R40_DEV_GIC_DIST,
     AW_R40_DEV_GIC_CPU,
     AW_R40_DEV_GIC_HYP,
@@ -89,6 +95,11 @@ struct AwR40State {
     AwSdHostState mmc2;
     AwSdHostState mmc3;
     AwR40ClockCtlState ccu;
+    AWI2CState i2c0;
+    AWI2CState i2c1;
+    AWI2CState i2c2;
+    AWI2CState i2c3;
+    AWI2CState i2c4;
     GICState gic;
     MemoryRegion sram_a1;
     MemoryRegion sram_a2;
-- 
2.25.1



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

* [PATCH v1 05/11] hw/misc: AXP221 PMU Emulation
  2023-03-21 10:24 [PATCH v1 00/11] *** add allwinner-r40 support *** qianfanguijin
                   ` (3 preceding siblings ...)
  2023-03-21 10:25 ` [PATCH v1 04/11] hw: arm: allwinner-r40: Add 5 TWI controllers qianfanguijin
@ 2023-03-21 10:25 ` qianfanguijin
  2023-03-25 21:25   ` Strahinja Jankovic
  2023-03-21 10:25 ` [PATCH v1 06/11] hw/arm/allwinner-r40: add SDRAM controller device qianfanguijin
                   ` (5 subsequent siblings)
  10 siblings, 1 reply; 17+ messages in thread
From: qianfanguijin @ 2023-03-21 10:25 UTC (permalink / raw)
  To: qemu-arm, qemu-devel
  Cc: Strahinja Jankovic, Peter Maydell, Beniamino Galvani,
	Philippe Mathieu-Daudé,
	Niek Linnenbank, qianfan Zhao

From: qianfan Zhao <qianfanguijin@163.com>

This patch adds minimal support for AXP-221 PMU and connect it to
bananapi M2U board.

Signed-off-by: qianfan Zhao <qianfanguijin@163.com>
---
 hw/arm/Kconfig        |   1 +
 hw/arm/bananapi_m2u.c |   5 ++
 hw/misc/Kconfig       |   4 +
 hw/misc/axp221.c      | 196 ++++++++++++++++++++++++++++++++++++++++++
 hw/misc/meson.build   |   1 +
 hw/misc/trace-events  |   5 ++
 6 files changed, 212 insertions(+)
 create mode 100644 hw/misc/axp221.c

diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig
index 9e14c3427e..cf8fb083f8 100644
--- a/hw/arm/Kconfig
+++ b/hw/arm/Kconfig
@@ -347,6 +347,7 @@ config ALLWINNER_H3
 config ALLWINNER_R40
     bool
     select ALLWINNER_A10_PIT
+    select AXP221_PMU
     select SERIAL
     select ARM_TIMER
     select ARM_GIC
diff --git a/hw/arm/bananapi_m2u.c b/hw/arm/bananapi_m2u.c
index 1b6241719d..bdee12efd3 100644
--- a/hw/arm/bananapi_m2u.c
+++ b/hw/arm/bananapi_m2u.c
@@ -22,6 +22,7 @@
 #include "exec/address-spaces.h"
 #include "qapi/error.h"
 #include "hw/boards.h"
+#include "hw/i2c/i2c.h"
 #include "hw/qdev-properties.h"
 #include "hw/arm/allwinner-r40.h"
 
@@ -91,6 +92,10 @@ static void bpim2u_init(MachineState *machine)
                      &bootroom_loaded);
     mmc_attach_drive(r40, &r40->mmc3, 3, false, NULL);
 
+    /* Connect AXP221 */
+    i2c = I2C_BUS(qdev_get_child_bus(DEVICE(&r40->i2c0), "i2c"));
+    i2c_slave_create_simple(i2c, "axp221_pmu", 0x34);
+
     /* SDRAM */
     memory_region_add_subregion(get_system_memory(), r40->memmap[AW_R40_DEV_SDRAM],
                                 machine->ram);
diff --git a/hw/misc/Kconfig b/hw/misc/Kconfig
index 2ef5781ef8..f66ac390b1 100644
--- a/hw/misc/Kconfig
+++ b/hw/misc/Kconfig
@@ -180,4 +180,8 @@ config AXP209_PMU
     bool
     depends on I2C
 
+config AXP221_PMU
+    bool
+    depends on I2C
+
 source macio/Kconfig
diff --git a/hw/misc/axp221.c b/hw/misc/axp221.c
new file mode 100644
index 0000000000..47784bb085
--- /dev/null
+++ b/hw/misc/axp221.c
@@ -0,0 +1,196 @@
+/*
+ * AXP-221/221s PMU Emulation
+ *
+ * Copyright (C) 2023 qianfan Zhao <qianfanguijin@163.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * SPDX-License-Identifier: MIT
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/log.h"
+#include "qemu/bitops.h"
+#include "trace.h"
+#include "hw/i2c/i2c.h"
+#include "migration/vmstate.h"
+
+#define TYPE_AXP221_PMU "axp221_pmu"
+
+#define AXP221(obj) \
+    OBJECT_CHECK(AXP221I2CState, (obj), TYPE_AXP221_PMU)
+
+#define NR_REGS                         0xff
+
+/* A simple I2C slave which returns values of ID or CNT register. */
+typedef struct AXP221I2CState {
+    /*< private >*/
+    I2CSlave i2c;
+    /*< public >*/
+    uint8_t regs[NR_REGS];  /* peripheral registers */
+    uint8_t ptr;            /* current register index */
+    uint8_t count;          /* counter used for tx/rx */
+} AXP221I2CState;
+
+#define AXP221_PWR_STATUS_ACIN_PRESENT          BIT(7)
+#define AXP221_PWR_STATUS_ACIN_AVAIL            BIT(6)
+#define AXP221_PWR_STATUS_VBUS_PRESENT          BIT(5)
+#define AXP221_PWR_STATUS_VBUS_USED             BIT(4)
+#define AXP221_PWR_STATUS_BAT_CHARGING          BIT(2)
+#define AXP221_PWR_STATUS_ACIN_VBUS_POWERED     BIT(1)
+
+/* Reset all counters and load ID register */
+static void axp221_reset_enter(Object *obj, ResetType type)
+{
+    AXP221I2CState *s = AXP221(obj);
+
+    memset(s->regs, 0, NR_REGS);
+    s->ptr = 0;
+    s->count = 0;
+
+    /* input power status register */
+    s->regs[0x00] = AXP221_PWR_STATUS_ACIN_PRESENT
+                    | AXP221_PWR_STATUS_ACIN_AVAIL
+                    | AXP221_PWR_STATUS_ACIN_VBUS_POWERED;
+
+    s->regs[0x01] = 0x00; /* no battery is connected */
+
+    /* CHIPID register, no documented on datasheet, but it is checked in
+     * u-boot spl. I had read it from AXP221s and got 0x06 value.
+     * So leave 06h here.
+     */
+    s->regs[0x03] = 0x06;
+
+    s->regs[0x10] = 0xbf;
+    s->regs[0x13] = 0x01;
+    s->regs[0x30] = 0x60;
+    s->regs[0x31] = 0x03;
+    s->regs[0x32] = 0x43;
+    s->regs[0x33] = 0xc6;
+    s->regs[0x34] = 0x45;
+    s->regs[0x35] = 0x0e;
+    s->regs[0x36] = 0x5d;
+    s->regs[0x37] = 0x08;
+    s->regs[0x38] = 0xa5;
+    s->regs[0x39] = 0x1f;
+    s->regs[0x3c] = 0xfc;
+    s->regs[0x3d] = 0x16;
+    s->regs[0x80] = 0x80;
+    s->regs[0x82] = 0xe0;
+    s->regs[0x84] = 0x32;
+    s->regs[0x8f] = 0x01;
+
+    s->regs[0x90] = 0x07;
+    s->regs[0x91] = 0x1f;
+    s->regs[0x92] = 0x07;
+    s->regs[0x93] = 0x1f;
+
+    s->regs[0x40] = 0xd8;
+    s->regs[0x41] = 0xff;
+    s->regs[0x42] = 0x03;
+    s->regs[0x43] = 0x03;
+
+    s->regs[0xb8] = 0xc0;
+    s->regs[0xb9] = 0x64;
+    s->regs[0xe6] = 0xa0;
+}
+
+/* Handle events from master. */
+static int axp221_event(I2CSlave *i2c, enum i2c_event event)
+{
+    AXP221I2CState *s = AXP221(i2c);
+
+    s->count = 0;
+
+    return 0;
+}
+
+/* Called when master requests read */
+static uint8_t axp221_rx(I2CSlave *i2c)
+{
+    AXP221I2CState *s = AXP221(i2c);
+    uint8_t ret = 0xff;
+
+    if (s->ptr < NR_REGS) {
+        ret = s->regs[s->ptr];
+        trace_axp221_rx(s->ptr, ret);
+        s->ptr++;
+    }
+
+    return ret;
+}
+
+/*
+ * Called when master sends write.
+ * Update ptr with byte 0, then perform write with second byte.
+ */
+static int axp221_tx(I2CSlave *i2c, uint8_t data)
+{
+    AXP221I2CState *s = AXP221(i2c);
+
+    if (s->count == 0) {
+        /* Store register address */
+        s->ptr = data;
+        s->count++;
+        trace_axp221_select(data);
+    } else {
+        trace_axp221_tx(s->ptr, data);
+        s->regs[s->ptr++] = data;
+    }
+
+    return 0;
+}
+
+static const VMStateDescription vmstate_axp221 = {
+    .name = TYPE_AXP221_PMU,
+    .version_id = 1,
+    .fields = (VMStateField[]) {
+        VMSTATE_UINT8_ARRAY(regs, AXP221I2CState, NR_REGS),
+        VMSTATE_UINT8(count, AXP221I2CState),
+        VMSTATE_UINT8(ptr, AXP221I2CState),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static void axp221_class_init(ObjectClass *oc, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(oc);
+    I2CSlaveClass *isc = I2C_SLAVE_CLASS(oc);
+    ResettableClass *rc = RESETTABLE_CLASS(oc);
+
+    rc->phases.enter = axp221_reset_enter;
+    dc->vmsd = &vmstate_axp221;
+    isc->event = axp221_event;
+    isc->recv = axp221_rx;
+    isc->send = axp221_tx;
+}
+
+static const TypeInfo axp221_info = {
+    .name = TYPE_AXP221_PMU,
+    .parent = TYPE_I2C_SLAVE,
+    .instance_size = sizeof(AXP221I2CState),
+    .class_init = axp221_class_init
+};
+
+static void axp221_register_devices(void)
+{
+    type_register_static(&axp221_info);
+}
+
+type_init(axp221_register_devices);
diff --git a/hw/misc/meson.build b/hw/misc/meson.build
index 96e35f1cdb..d407a2df50 100644
--- a/hw/misc/meson.build
+++ b/hw/misc/meson.build
@@ -46,6 +46,7 @@ softmmu_ss.add(when: 'CONFIG_ALLWINNER_H3', if_true: files('allwinner-h3-sysctrl
 softmmu_ss.add(when: 'CONFIG_ALLWINNER_H3', if_true: files('allwinner-sid.c'))
 softmmu_ss.add(when: 'CONFIG_ALLWINNER_R40', if_true: files('allwinner-r40-ccu.c'))
 softmmu_ss.add(when: 'CONFIG_AXP209_PMU', if_true: files('axp209.c'))
+softmmu_ss.add(when: 'CONFIG_AXP221_PMU', if_true: files('axp221.c'))
 softmmu_ss.add(when: 'CONFIG_REALVIEW', if_true: files('arm_sysctl.c'))
 softmmu_ss.add(when: 'CONFIG_NSERIES', if_true: files('cbus.c'))
 softmmu_ss.add(when: 'CONFIG_ECCMEMCTL', if_true: files('eccmemctl.c'))
diff --git a/hw/misc/trace-events b/hw/misc/trace-events
index c47876a902..63b072d2d8 100644
--- a/hw/misc/trace-events
+++ b/hw/misc/trace-events
@@ -28,6 +28,11 @@ axp209_rx(uint8_t reg, uint8_t data) "Read reg 0x%" PRIx8 " : 0x%" PRIx8
 axp209_select(uint8_t reg) "Accessing reg 0x%" PRIx8
 axp209_tx(uint8_t reg, uint8_t data) "Write reg 0x%" PRIx8 " : 0x%" PRIx8
 
+# axp221.c
+axp221_rx(uint8_t reg, uint8_t data) "Read reg 0x%" PRIx8 " : 0x%" PRIx8
+axp221_select(uint8_t reg) "Accessing reg 0x%" PRIx8
+axp221_tx(uint8_t reg, uint8_t data) "Write reg 0x%" PRIx8 " : 0x%" PRIx8
+
 # 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"
-- 
2.25.1



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

* [PATCH v1 06/11] hw/arm/allwinner-r40: add SDRAM controller device
  2023-03-21 10:24 [PATCH v1 00/11] *** add allwinner-r40 support *** qianfanguijin
                   ` (4 preceding siblings ...)
  2023-03-21 10:25 ` [PATCH v1 05/11] hw/misc: AXP221 PMU Emulation qianfanguijin
@ 2023-03-21 10:25 ` qianfanguijin
  2023-03-21 10:25 ` [PATCH v1 07/11] hw: sd: allwinner-sdhost: Add sun50i-a64 SoC support qianfanguijin
                   ` (4 subsequent siblings)
  10 siblings, 0 replies; 17+ messages in thread
From: qianfanguijin @ 2023-03-21 10:25 UTC (permalink / raw)
  To: qemu-arm, qemu-devel
  Cc: Strahinja Jankovic, Peter Maydell, Beniamino Galvani,
	Philippe Mathieu-Daudé,
	Niek Linnenbank, qianfan Zhao

From: qianfan Zhao <qianfanguijin@163.com>

Types of memory that the SDRAM controller supports are DDR2/DDR3
and capacities of up to 2GiB. This commit adds emulation support
of the Allwinner R40 SDRAM controller.

This driver only support 256M, 512M and 1024M memory now.

Signed-off-by: qianfan Zhao <qianfanguijin@163.com>
---
 hw/arm/allwinner-r40.c                |  18 +-
 hw/arm/bananapi_m2u.c                 |   7 +
 hw/misc/allwinner-r40-dramc.c         | 499 ++++++++++++++++++++++++++
 hw/misc/meson.build                   |   1 +
 hw/misc/trace-events                  |  14 +
 include/hw/arm/allwinner-r40.h        |  13 +-
 include/hw/misc/allwinner-r40-dramc.h | 108 ++++++
 7 files changed, 657 insertions(+), 3 deletions(-)
 create mode 100644 hw/misc/allwinner-r40-dramc.c
 create mode 100644 include/hw/misc/allwinner-r40-dramc.h

diff --git a/hw/arm/allwinner-r40.c b/hw/arm/allwinner-r40.c
index 9fa23e1f33..f1f6803cf7 100644
--- a/hw/arm/allwinner-r40.c
+++ b/hw/arm/allwinner-r40.c
@@ -31,6 +31,7 @@
 #include "hw/loader.h"
 #include "sysemu/sysemu.h"
 #include "hw/arm/allwinner-r40.h"
+#include "hw/misc/allwinner-r40-dramc.h"
 
 /* Memory map */
 const hwaddr allwinner_r40_memmap[] = {
@@ -57,6 +58,9 @@ const hwaddr allwinner_r40_memmap[] = {
     [AW_R40_DEV_TWI2]       = 0x01c2b400,
     [AW_R40_DEV_TWI3]       = 0x01c2b800,
     [AW_R40_DEV_TWI4]       = 0x01c2c000,
+    [AW_R40_DEV_DRAMCOM]    = 0x01c62000,
+    [AW_R40_DEV_DRAMCTL]    = 0x01c63000,
+    [AW_R40_DEV_DRAMPHY]    = 0x01c65000,
     [AW_R40_DEV_GIC_DIST]   = 0x01c81000,
     [AW_R40_DEV_GIC_CPU]    = 0x01c82000,
     [AW_R40_DEV_GIC_HYP]    = 0x01c84000,
@@ -129,8 +133,6 @@ static struct AwR40Unimplemented r40_unimplemented[] = {
     { "gpu",        0x01c40000, 64 * KiB },
     { "gmac",       0x01c50000, 64 * KiB },
     { "hstmr",      0x01c60000, 4 * KiB },
-    { "dram-com",   0x01c62000, 4 * KiB },
-    { "dram-ctl",   0x01c63000, 4 * KiB },
     { "tcon-top",   0x01c70000, 4 * KiB },
     { "lcd0",       0x01c71000, 4 * KiB },
     { "lcd1",       0x01c72000, 4 * KiB },
@@ -273,6 +275,12 @@ static void allwinner_r40_init(Object *obj)
     object_initialize_child(obj, "twi2", &s->i2c2, TYPE_AW_I2C_SUN6I);
     object_initialize_child(obj, "twi3", &s->i2c3, TYPE_AW_I2C_SUN6I);
     object_initialize_child(obj, "twi4", &s->i2c4, TYPE_AW_I2C_SUN6I);
+
+    object_initialize_child(obj, "dramc", &s->dramc, TYPE_AW_R40_DRAMC);
+    object_property_add_alias(obj, "ram-addr", OBJECT(&s->dramc),
+                             "ram-addr");
+    object_property_add_alias(obj, "ram-size", OBJECT(&s->dramc),
+                              "ram-size");
 }
 
 static void allwinner_r40_realize(DeviceState *dev, Error **errp)
@@ -466,6 +474,12 @@ static void allwinner_r40_realize(DeviceState *dev, Error **errp)
     sysbus_connect_irq(SYS_BUS_DEVICE(&s->i2c4), 0,
                        qdev_get_gpio_in(DEVICE(&s->gic), AW_R40_GIC_SPI_TWI4));
 
+    /* DRAMC */
+    sysbus_realize(SYS_BUS_DEVICE(&s->dramc), &error_fatal);
+    sysbus_mmio_map(SYS_BUS_DEVICE(&s->dramc), 0, s->memmap[AW_R40_DEV_DRAMCOM]);
+    sysbus_mmio_map(SYS_BUS_DEVICE(&s->dramc), 1, s->memmap[AW_R40_DEV_DRAMCTL]);
+    sysbus_mmio_map(SYS_BUS_DEVICE(&s->dramc), 2, s->memmap[AW_R40_DEV_DRAMPHY]);
+
     /* Unimplemented devices */
     for (i = 0; i < ARRAY_SIZE(r40_unimplemented); i++) {
         create_unimplemented_device(r40_unimplemented[i].device_name,
diff --git a/hw/arm/bananapi_m2u.c b/hw/arm/bananapi_m2u.c
index bdee12efd3..d185f979c0 100644
--- a/hw/arm/bananapi_m2u.c
+++ b/hw/arm/bananapi_m2u.c
@@ -79,6 +79,13 @@ static void bpim2u_init(MachineState *machine)
     object_property_set_int(OBJECT(r40), "clk1-freq", 24 * 1000 * 1000,
                             &error_abort);
 
+    /* DRAMC */
+    r40->ram_size = machine->ram_size / MiB;
+    object_property_set_uint(OBJECT(r40), "ram-addr", r40->memmap[AW_R40_DEV_SDRAM],
+                             &error_abort);
+    object_property_set_int(OBJECT(r40), "ram-size", r40->ram_size,
+                            &error_abort);
+
     /* Mark R40 object realized */
     qdev_realize(DEVICE(r40), NULL, &error_abort);
 
diff --git a/hw/misc/allwinner-r40-dramc.c b/hw/misc/allwinner-r40-dramc.c
new file mode 100644
index 0000000000..67f9031149
--- /dev/null
+++ b/hw/misc/allwinner-r40-dramc.c
@@ -0,0 +1,499 @@
+/*
+ * Allwinner R40 SDRAM Controller emulation
+ *
+ * CCopyright (C) 2023 qianfan Zhao <qianfanguijin@163.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/units.h"
+#include "qemu/error-report.h"
+#include "hw/sysbus.h"
+#include "migration/vmstate.h"
+#include "qemu/log.h"
+#include "qemu/module.h"
+#include "exec/address-spaces.h"
+#include "hw/qdev-properties.h"
+#include "qapi/error.h"
+#include "qemu/bitops.h"
+#include "hw/misc/allwinner-r40-dramc.h"
+#include "trace.h"
+
+#define REG_INDEX(offset)    (offset / sizeof(uint32_t))
+
+/* DRAMCOM register offsets */
+enum {
+    REG_DRAMCOM_CR    = 0x0000, /* Control Register */
+};
+
+/* DRAMCOMM register flags */
+enum {
+    REG_DRAMCOM_CR_DUAL_RANK = (1 << 0),
+};
+
+/* DRAMCTL register offsets */
+enum {
+    REG_DRAMCTL_PIR   = 0x0000, /* PHY Initialization Register */
+    REG_DRAMCTL_PGSR  = 0x0010, /* PHY General Status Register */
+    REG_DRAMCTL_STATR = 0x0018, /* Status Register */
+    REG_DRAMCTL_PGCR  = 0x0100, /* PHY general configuration registers */
+};
+
+/* DRAMCTL register flags */
+enum {
+    REG_DRAMCTL_PGSR_INITDONE = (1 << 0),
+    REG_DRAMCTL_PGSR_READ_TIMEOUT = (1 << 13),
+    REG_DRAMCTL_PGCR_ENABLE_READ_TIMEOUT = (1 << 25),
+};
+
+enum {
+    REG_DRAMCTL_STATR_ACTIVE  = (1 << 0),
+};
+
+#define DRAM_MAX_ROW_BITS       16
+#define DRAM_MAX_COL_BITS       13  /* 8192 */
+#define DRAM_MAX_BANK            3
+
+static uint64_t dram_autodetect_cells[DRAM_MAX_ROW_BITS]
+                                     [DRAM_MAX_BANK]
+                                     [DRAM_MAX_COL_BITS];
+struct VirtualDDRChip {
+    uint32_t    ram_size;
+    uint8_t     bank_bits;
+    uint8_t     row_bits;
+    uint8_t     col_bits;
+};
+
+/* Only power of 2 RAM sizes from 256MiB up to 2048MiB are supported,
+ * 2GiB memory is not supported due to dual rank feature.
+ */
+static const struct VirtualDDRChip dummy_ddr_chips[] = {
+    {
+        .ram_size   = 256,
+        .bank_bits  = 3,
+        .row_bits   = 12,
+        .col_bits   = 13,
+    }, {
+        .ram_size   = 512,
+        .bank_bits  = 3,
+        .row_bits   = 13,
+        .col_bits   = 13,
+    }, {
+        .ram_size   = 1024,
+        .bank_bits  = 3,
+        .row_bits   = 14,
+        .col_bits   = 13,
+    }, {
+        0
+    }
+};
+
+static const struct VirtualDDRChip *get_match_ddr(uint32_t ram_size)
+{
+    const struct VirtualDDRChip *ddr;
+
+    for (ddr = &dummy_ddr_chips[0]; ddr->ram_size; ddr++) {
+        if (ddr->ram_size == ram_size)
+            return ddr;
+    }
+
+    return NULL;
+}
+
+static uint64_t *address_to_autodetect_cells(AwR40DramCtlState *s,
+                                             const struct VirtualDDRChip *ddr,
+                                             uint32_t offset)
+{
+    int row_index = 0, bank_index = 0, col_index = 0;
+    uint32_t row_addr, bank_addr, col_addr;
+
+    row_addr = extract32(offset, s->set_col_bits + s->set_bank_bits,
+                         s->set_row_bits);
+    bank_addr = extract32(offset, s->set_col_bits, s->set_bank_bits);
+    col_addr = extract32(offset, 0, s->set_col_bits);
+
+    for (int i = 0; i < ddr->row_bits; i++) {
+        if (row_addr & BIT(i))
+            row_index = i;
+    }
+
+    for (int i = 0; i < ddr->bank_bits; i++) {
+        if (bank_addr & BIT(i))
+            bank_index = i;
+    }
+
+    for (int i = 0; i < ddr->col_bits; i++) {
+        if (col_addr & BIT(i))
+            col_index = i;
+    }
+
+    trace_allwinner_r40_dramc_offset_to_cell(offset, row_index, bank_index,
+                                             col_index);
+    return &dram_autodetect_cells[row_index][bank_index][col_index];
+}
+
+static void allwinner_r40_dramc_map_rows(AwR40DramCtlState *s, uint8_t row_bits,
+                                         uint8_t bank_bits, uint8_t col_bits)
+{
+    const struct VirtualDDRChip *ddr = get_match_ddr(s->ram_size);
+    bool enable_detect_cells;
+
+    trace_allwinner_r40_dramc_map_rows(row_bits, bank_bits, col_bits);
+
+    if (!ddr)
+        return;
+
+    s->set_row_bits = row_bits;
+    s->set_bank_bits = bank_bits;
+    s->set_col_bits = col_bits;
+
+    enable_detect_cells = ddr->bank_bits != bank_bits
+                        || ddr->row_bits != row_bits
+                        || ddr->col_bits != col_bits;
+
+    if (enable_detect_cells)
+        trace_allwinner_r40_dramc_detect_cells_enable();
+    else
+        trace_allwinner_r40_dramc_detect_cells_disable();
+
+    memory_region_set_enabled(&s->detect_cells, enable_detect_cells);
+}
+
+static uint64_t allwinner_r40_dramcom_read(void *opaque, hwaddr offset,
+                                           unsigned size)
+{
+    const AwR40DramCtlState *s = AW_R40_DRAMC(opaque);
+    const uint32_t idx = REG_INDEX(offset);
+
+    if (idx >= AW_R40_DRAMCOM_REGS_NUM) {
+        qemu_log_mask(LOG_GUEST_ERROR, "%s: out-of-bounds offset 0x%04x\n",
+                      __func__, (uint32_t)offset);
+        return 0;
+    }
+
+    trace_allwinner_r40_dramcom_read(offset, s->dramcom[idx], size);
+    return s->dramcom[idx];
+}
+
+static void allwinner_r40_dramcom_write(void *opaque, hwaddr offset,
+                                        uint64_t val, unsigned size)
+{
+    AwR40DramCtlState *s = AW_R40_DRAMC(opaque);
+    const uint32_t idx = REG_INDEX(offset);
+
+    trace_allwinner_r40_dramcom_write(offset, val, size);
+
+    if (idx >= AW_R40_DRAMCOM_REGS_NUM) {
+        qemu_log_mask(LOG_GUEST_ERROR, "%s: out-of-bounds offset 0x%04x\n",
+                      __func__, (uint32_t)offset);
+        return;
+    }
+
+    switch (offset) {
+    case REG_DRAMCOM_CR:   /* Control Register */
+        if (!(val & REG_DRAMCOM_CR_DUAL_RANK)) {
+            allwinner_r40_dramc_map_rows(s, ((val >> 4) & 0xf) + 1,
+                                         ((val >> 2) & 0x1) + 2,
+                                         (((val >> 8) & 0xf) + 3));
+        }
+        break;
+    };
+
+    s->dramcom[idx] = (uint32_t) val;
+}
+
+static uint64_t allwinner_r40_dramctl_read(void *opaque, hwaddr offset,
+                                           unsigned size)
+{
+    const AwR40DramCtlState *s = AW_R40_DRAMC(opaque);
+    const uint32_t idx = REG_INDEX(offset);
+
+    if (idx >= AW_R40_DRAMCTL_REGS_NUM) {
+        qemu_log_mask(LOG_GUEST_ERROR, "%s: out-of-bounds offset 0x%04x\n",
+                      __func__, (uint32_t)offset);
+        return 0;
+    }
+
+    trace_allwinner_r40_dramctl_read(offset, s->dramctl[idx], size);
+    return s->dramctl[idx];
+}
+
+static void allwinner_r40_dramctl_write(void *opaque, hwaddr offset,
+                                        uint64_t val, unsigned size)
+{
+    AwR40DramCtlState *s = AW_R40_DRAMC(opaque);
+    const uint32_t idx = REG_INDEX(offset);
+
+    trace_allwinner_r40_dramctl_write(offset, val, size);
+
+    if (idx >= AW_R40_DRAMCTL_REGS_NUM) {
+        qemu_log_mask(LOG_GUEST_ERROR, "%s: out-of-bounds offset 0x%04x\n",
+                      __func__, (uint32_t)offset);
+        return;
+    }
+
+    switch (offset) {
+    case REG_DRAMCTL_PIR:    /* PHY Initialization Register */
+        s->dramctl[REG_INDEX(REG_DRAMCTL_PGSR)] |= REG_DRAMCTL_PGSR_INITDONE;
+        s->dramctl[REG_INDEX(REG_DRAMCTL_STATR)] |= REG_DRAMCTL_STATR_ACTIVE;
+        break;
+    }
+
+    s->dramctl[idx] = (uint32_t) val;
+}
+
+static uint64_t allwinner_r40_dramphy_read(void *opaque, hwaddr offset,
+                                           unsigned size)
+{
+    const AwR40DramCtlState *s = AW_R40_DRAMC(opaque);
+    const uint32_t idx = REG_INDEX(offset);
+
+    if (idx >= AW_R40_DRAMPHY_REGS_NUM) {
+        qemu_log_mask(LOG_GUEST_ERROR, "%s: out-of-bounds offset 0x%04x\n",
+                      __func__, (uint32_t)offset);
+        return 0;
+    }
+
+    trace_allwinner_r40_dramphy_read(offset, s->dramphy[idx], size);
+    return s->dramphy[idx];
+}
+
+static void allwinner_r40_dramphy_write(void *opaque, hwaddr offset,
+                                        uint64_t val, unsigned size)
+{
+    AwR40DramCtlState *s = AW_R40_DRAMC(opaque);
+    const uint32_t idx = REG_INDEX(offset);
+
+    trace_allwinner_r40_dramphy_write(offset, val, size);
+
+    if (idx >= AW_R40_DRAMPHY_REGS_NUM) {
+        qemu_log_mask(LOG_GUEST_ERROR, "%s: out-of-bounds offset 0x%04x\n",
+                      __func__, (uint32_t)offset);
+        return;
+    }
+
+    s->dramphy[idx] = (uint32_t) val;
+}
+
+static const MemoryRegionOps allwinner_r40_dramcom_ops = {
+    .read = allwinner_r40_dramcom_read,
+    .write = allwinner_r40_dramcom_write,
+    .endianness = DEVICE_NATIVE_ENDIAN,
+    .valid = {
+        .min_access_size = 4,
+        .max_access_size = 4,
+    },
+    .impl.min_access_size = 4,
+};
+
+static const MemoryRegionOps allwinner_r40_dramctl_ops = {
+    .read = allwinner_r40_dramctl_read,
+    .write = allwinner_r40_dramctl_write,
+    .endianness = DEVICE_NATIVE_ENDIAN,
+    .valid = {
+        .min_access_size = 4,
+        .max_access_size = 4,
+    },
+    .impl.min_access_size = 4,
+};
+
+static const MemoryRegionOps allwinner_r40_dramphy_ops = {
+    .read = allwinner_r40_dramphy_read,
+    .write = allwinner_r40_dramphy_write,
+    .endianness = DEVICE_NATIVE_ENDIAN,
+    .valid = {
+        .min_access_size = 4,
+        .max_access_size = 4,
+    },
+    .impl.min_access_size = 4,
+};
+
+static uint64_t allwinner_r40_detect_read(void *opaque, hwaddr offset,
+                                          unsigned size)
+{
+    AwR40DramCtlState *s = AW_R40_DRAMC(opaque);
+    const struct VirtualDDRChip *ddr = get_match_ddr(s->ram_size);
+    uint64_t data = 0;
+
+    if (ddr)
+        data = *address_to_autodetect_cells(s, ddr, (uint32_t)offset);
+
+    trace_allwinner_r40_dramc_detect_cell_read(offset, data);
+    return data;
+}
+
+static void allwinner_r40_detect_write(void *opaque, hwaddr offset,
+                                       uint64_t data, unsigned size)
+{
+    AwR40DramCtlState *s = AW_R40_DRAMC(opaque);
+    const struct VirtualDDRChip *ddr = get_match_ddr(s->ram_size);
+
+    if (ddr) {
+        uint64_t *cell = address_to_autodetect_cells(s, ddr, (uint32_t)offset);
+        trace_allwinner_r40_dramc_detect_cell_write(offset, data);
+        *cell = data;
+    }
+}
+
+static const MemoryRegionOps allwinner_r40_detect_ops = {
+    .read = allwinner_r40_detect_read,
+    .write = allwinner_r40_detect_write,
+    .endianness = DEVICE_NATIVE_ENDIAN,
+    .valid = {
+        .min_access_size = 4,
+        .max_access_size = 4,
+    },
+    .impl.min_access_size = 4,
+};
+
+/* mctl_r40_detect_rank_count in u-boot will write the high 1G of DDR
+ * to detect wether the board support dual_rank or not. Create a virtual memory
+ * if the board's ram_size less or equal than 1G, and set read time out flag of
+ * REG_DRAMCTL_PGSR when the user touch this high dram.
+ */
+static uint64_t allwinner_r40_dualrank_detect_read(void *opaque, hwaddr offset,
+                                                   unsigned size)
+{
+    AwR40DramCtlState *s = AW_R40_DRAMC(opaque);
+    uint32_t reg;
+
+    reg = s->dramctl[REG_INDEX(REG_DRAMCTL_PGCR)];
+    if (reg & REG_DRAMCTL_PGCR_ENABLE_READ_TIMEOUT) { /* Enable read time out */
+        /* this driver only support one rank, mark READ_TIMEOUT when try
+         * read the second rank.
+         */
+        s->dramctl[REG_INDEX(REG_DRAMCTL_PGSR)] |= REG_DRAMCTL_PGSR_READ_TIMEOUT;
+    }
+
+    return 0;
+}
+
+static const MemoryRegionOps allwinner_r40_dualrank_detect_ops = {
+    .read = allwinner_r40_dualrank_detect_read,
+    .endianness = DEVICE_NATIVE_ENDIAN,
+    .valid = {
+        .min_access_size = 4,
+        .max_access_size = 4,
+    },
+    .impl.min_access_size = 4,
+};
+
+static void allwinner_r40_dramc_reset(DeviceState *dev)
+{
+    AwR40DramCtlState *s = AW_R40_DRAMC(dev);
+
+    /* Set default values for registers */
+    memset(&s->dramcom, 0, sizeof(s->dramcom));
+    memset(&s->dramctl, 0, sizeof(s->dramctl));
+    memset(&s->dramphy, 0, sizeof(s->dramphy));
+}
+
+static void allwinner_r40_dramc_realize(DeviceState *dev, Error **errp)
+{
+    AwR40DramCtlState *s = AW_R40_DRAMC(dev);
+
+    if (!get_match_ddr(s->ram_size)) {
+        error_report("%s: ram-size %u MiB is not supported",
+                        __func__, s->ram_size);
+        exit(1);
+    }
+
+    /* detect_cells */
+    sysbus_mmio_map_overlap(SYS_BUS_DEVICE(s), 3, s->ram_addr, 10);
+    memory_region_set_enabled(&s->detect_cells, false);
+
+    if (s->ram_size < 2048) {
+        /* the high memory used for dualrank detect, index 4 */
+        memory_region_init_io(&s->dram_high, OBJECT(s),
+                              &allwinner_r40_dualrank_detect_ops, s,
+                              "DRAMHIGH", KiB);
+        sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->dram_high);
+
+        sysbus_mmio_map(SYS_BUS_DEVICE(s), 4, s->ram_addr + GiB);
+    }
+}
+
+static void allwinner_r40_dramc_init(Object *obj)
+{
+    SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
+    AwR40DramCtlState *s = AW_R40_DRAMC(obj);
+
+    /* DRAMCOM registers, index 0 */
+    memory_region_init_io(&s->dramcom_iomem, OBJECT(s),
+                          &allwinner_r40_dramcom_ops, s,
+                          "DRAMCOM", 4 * KiB);
+    sysbus_init_mmio(sbd, &s->dramcom_iomem);
+
+    /* DRAMCTL registers, index 1 */
+    memory_region_init_io(&s->dramctl_iomem, OBJECT(s),
+                          &allwinner_r40_dramctl_ops, s,
+                          "DRAMCTL", 4 * KiB);
+    sysbus_init_mmio(sbd, &s->dramctl_iomem);
+
+    /* DRAMPHY registers. index 2 */
+    memory_region_init_io(&s->dramphy_iomem, OBJECT(s),
+                          &allwinner_r40_dramphy_ops, s,
+                          "DRAMPHY", 4 * KiB);
+    sysbus_init_mmio(sbd, &s->dramphy_iomem);
+
+    /* R40 support max 2GiB dram memory, index 3 */
+    memory_region_init_io(&s->detect_cells, OBJECT(s),
+                          &allwinner_r40_detect_ops, s,
+                          "DRAMCELLS", 2 * GiB);
+    sysbus_init_mmio(sbd, &s->detect_cells);
+}
+
+static Property allwinner_r40_dramc_properties[] = {
+    DEFINE_PROP_UINT64("ram-addr", AwR40DramCtlState, ram_addr, 0x0),
+    DEFINE_PROP_UINT32("ram-size", AwR40DramCtlState, ram_size, 256), /* MiB */
+    DEFINE_PROP_END_OF_LIST()
+};
+
+static const VMStateDescription allwinner_r40_dramc_vmstate = {
+    .name = "allwinner-r40-dramc",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .fields = (VMStateField[]) {
+        VMSTATE_UINT32_ARRAY(dramcom, AwR40DramCtlState, AW_R40_DRAMCOM_REGS_NUM),
+        VMSTATE_UINT32_ARRAY(dramctl, AwR40DramCtlState, AW_R40_DRAMCTL_REGS_NUM),
+        VMSTATE_UINT32_ARRAY(dramphy, AwR40DramCtlState, AW_R40_DRAMPHY_REGS_NUM),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static void allwinner_r40_dramc_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+
+    dc->reset = allwinner_r40_dramc_reset;
+    dc->vmsd = &allwinner_r40_dramc_vmstate;
+    dc->realize = allwinner_r40_dramc_realize;
+    device_class_set_props(dc, allwinner_r40_dramc_properties);
+}
+
+static const TypeInfo allwinner_r40_dramc_info = {
+    .name          = TYPE_AW_R40_DRAMC,
+    .parent        = TYPE_SYS_BUS_DEVICE,
+    .instance_init = allwinner_r40_dramc_init,
+    .instance_size = sizeof(AwR40DramCtlState),
+    .class_init    = allwinner_r40_dramc_class_init,
+};
+
+static void allwinner_r40_dramc_register(void)
+{
+    type_register_static(&allwinner_r40_dramc_info);
+}
+
+type_init(allwinner_r40_dramc_register)
diff --git a/hw/misc/meson.build b/hw/misc/meson.build
index d407a2df50..18b68be0a1 100644
--- a/hw/misc/meson.build
+++ b/hw/misc/meson.build
@@ -45,6 +45,7 @@ softmmu_ss.add(when: 'CONFIG_ALLWINNER_H3', if_true: files('allwinner-h3-dramc.c
 softmmu_ss.add(when: 'CONFIG_ALLWINNER_H3', if_true: files('allwinner-h3-sysctrl.c'))
 softmmu_ss.add(when: 'CONFIG_ALLWINNER_H3', if_true: files('allwinner-sid.c'))
 softmmu_ss.add(when: 'CONFIG_ALLWINNER_R40', if_true: files('allwinner-r40-ccu.c'))
+softmmu_ss.add(when: 'CONFIG_ALLWINNER_R40', if_true: files('allwinner-r40-dramc.c'))
 softmmu_ss.add(when: 'CONFIG_AXP209_PMU', if_true: files('axp209.c'))
 softmmu_ss.add(when: 'CONFIG_AXP221_PMU', if_true: files('axp221.c'))
 softmmu_ss.add(when: 'CONFIG_REALVIEW', if_true: files('arm_sysctl.c'))
diff --git a/hw/misc/trace-events b/hw/misc/trace-events
index 63b072d2d8..17ef6c722c 100644
--- a/hw/misc/trace-events
+++ b/hw/misc/trace-events
@@ -15,6 +15,20 @@ allwinner_h3_dramctl_write(uint64_t offset, uint64_t data, unsigned size) "Write
 allwinner_h3_dramphy_read(uint64_t offset, uint64_t data, unsigned size) "Read: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %" PRIu32
 allwinner_h3_dramphy_write(uint64_t offset, uint64_t data, unsigned size) "write: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %" PRIu32
 
+# allwinner-r40-dramc.c
+allwinner_r40_dramc_detect_cells_disable(void) "Disable detect cells"
+allwinner_r40_dramc_detect_cells_enable(void) "Enable detect cells"
+allwinner_r40_dramc_map_rows(uint8_t row_bits, uint8_t bank_bits, uint8_t col_bits) "DRAM layout: row_bits %d, bank_bits %d, col_bits %d"
+allwinner_r40_dramc_offset_to_cell(uint64_t offset, int row, int bank, int col) "offset 0x%" PRIx64 " row %d bank %d col %d"
+allwinner_r40_dramc_detect_cell_write(uint64_t offset, uint64_t data) "offset 0x%" PRIx64 " data 0x%" PRIx64 ""
+allwinner_r40_dramc_detect_cell_read(uint64_t offset, uint64_t data) "offset 0x%" PRIx64 " data 0x%" PRIx64 ""
+allwinner_r40_dramcom_read(uint64_t offset, uint64_t data, unsigned size) "Read: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %" PRIu32
+allwinner_r40_dramcom_write(uint64_t offset, uint64_t data, unsigned size) "Write: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %" PRIu32
+allwinner_r40_dramctl_read(uint64_t offset, uint64_t data, unsigned size) "Read: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %" PRIu32
+allwinner_r40_dramctl_write(uint64_t offset, uint64_t data, unsigned size) "Write: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %" PRIu32
+allwinner_r40_dramphy_read(uint64_t offset, uint64_t data, unsigned size) "Read: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %" PRIu32
+allwinner_r40_dramphy_write(uint64_t offset, uint64_t data, unsigned size) "write: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %" PRIu32
+
 # allwinner-sid.c
 allwinner_sid_read(uint64_t offset, uint64_t data, unsigned size) "offset 0x%" PRIx64 " data 0x%" PRIx64 " size %" PRIu32
 allwinner_sid_write(uint64_t offset, uint64_t data, unsigned size) "offset 0x%" PRIx64 " data 0x%" PRIx64 " size %" PRIu32
diff --git a/include/hw/arm/allwinner-r40.h b/include/hw/arm/allwinner-r40.h
index 6a7e5c1e31..6fc9691bb5 100644
--- a/include/hw/arm/allwinner-r40.h
+++ b/include/hw/arm/allwinner-r40.h
@@ -26,6 +26,7 @@
 #include "hw/intc/arm_gic.h"
 #include "hw/sd/allwinner-sdhost.h"
 #include "hw/misc/allwinner-r40-ccu.h"
+#include "hw/misc/allwinner-r40-dramc.h"
 #include "hw/i2c/allwinner-i2c.h"
 #include "target/arm/cpu.h"
 #include "sysemu/block-backend.h"
@@ -58,7 +59,10 @@ enum {
     AW_R40_DEV_GIC_CPU,
     AW_R40_DEV_GIC_HYP,
     AW_R40_DEV_GIC_VCPU,
-    AW_R40_DEV_SDRAM
+    AW_R40_DEV_SDRAM,
+    AW_R40_DEV_DRAMCOM,
+    AW_R40_DEV_DRAMCTL,
+    AW_R40_DEV_DRAMPHY,
 };
 
 #define AW_R40_NUM_CPUS      (4)
@@ -87,6 +91,12 @@ struct AwR40State {
     DeviceState parent_obj;
     /*< public >*/
 
+    /** Physical base address for start of RAM */
+    hwaddr ram_addr;
+
+    /** Total RAM size in megabytes */
+    uint32_t ram_size;
+
     ARMCPU cpus[AW_R40_NUM_CPUS];
     const hwaddr *memmap;
     AwA10PITState timer;
@@ -95,6 +105,7 @@ struct AwR40State {
     AwSdHostState mmc2;
     AwSdHostState mmc3;
     AwR40ClockCtlState ccu;
+    AwR40DramCtlState dramc;
     AWI2CState i2c0;
     AWI2CState i2c1;
     AWI2CState i2c2;
diff --git a/include/hw/misc/allwinner-r40-dramc.h b/include/hw/misc/allwinner-r40-dramc.h
new file mode 100644
index 0000000000..34267785f2
--- /dev/null
+++ b/include/hw/misc/allwinner-r40-dramc.h
@@ -0,0 +1,108 @@
+/*
+ * Allwinner R40 SDRAM Controller emulation
+ *
+ * Copyright (C) 2023 qianfan Zhao <qianfanguijin@163.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_R40_DRAMC_H
+#define HW_MISC_ALLWINNER_R40_DRAMC_H
+
+#include "qom/object.h"
+#include "hw/sysbus.h"
+#include "exec/hwaddr.h"
+
+/**
+ * Constants
+ * @{
+ */
+
+/** Highest register address used by DRAMCOM module */
+#define AW_R40_DRAMCOM_REGS_MAXADDR  (0x804)
+
+/** Total number of known DRAMCOM registers */
+#define AW_R40_DRAMCOM_REGS_NUM      (AW_R40_DRAMCOM_REGS_MAXADDR / \
+                                     sizeof(uint32_t))
+
+/** Highest register address used by DRAMCTL module */
+#define AW_R40_DRAMCTL_REGS_MAXADDR  (0x88c)
+
+/** Total number of known DRAMCTL registers */
+#define AW_R40_DRAMCTL_REGS_NUM      (AW_R40_DRAMCTL_REGS_MAXADDR / \
+                                     sizeof(uint32_t))
+
+/** Highest register address used by DRAMPHY module */
+#define AW_R40_DRAMPHY_REGS_MAXADDR  (0x4)
+
+/** Total number of known DRAMPHY registers */
+#define AW_R40_DRAMPHY_REGS_NUM      (AW_R40_DRAMPHY_REGS_MAXADDR / \
+                                     sizeof(uint32_t))
+
+/** @} */
+
+/**
+ * Object model
+ * @{
+ */
+
+#define TYPE_AW_R40_DRAMC "allwinner-r40-dramc"
+OBJECT_DECLARE_SIMPLE_TYPE(AwR40DramCtlState, AW_R40_DRAMC)
+
+/** @} */
+
+/**
+ * Allwinner R40 SDRAM Controller object instance state.
+ */
+struct AwR40DramCtlState {
+    /*< private >*/
+    SysBusDevice parent_obj;
+    /*< public >*/
+
+    /** Physical base address for start of RAM */
+    hwaddr ram_addr;
+
+    /** Total RAM size in megabytes */
+    uint32_t ram_size;
+
+    uint8_t set_row_bits;
+    uint8_t set_bank_bits;
+    uint8_t set_col_bits;
+
+    /**
+     * @name Memory Regions
+     * @{
+     */
+    MemoryRegion dramcom_iomem;    /**< DRAMCOM module I/O registers */
+    MemoryRegion dramctl_iomem;    /**< DRAMCTL module I/O registers */
+    MemoryRegion dramphy_iomem;    /**< DRAMPHY module I/O registers */
+    MemoryRegion dram_high;        /**< The high 1G dram for dualrank detect */
+    MemoryRegion detect_cells;     /**< DRAM memory cells for auto detect */
+
+    /** @} */
+
+    /**
+     * @name Hardware Registers
+     * @{
+     */
+
+    uint32_t dramcom[AW_R40_DRAMCOM_REGS_NUM]; /**< Array of DRAMCOM registers */
+    uint32_t dramctl[AW_R40_DRAMCTL_REGS_NUM]; /**< Array of DRAMCTL registers */
+    uint32_t dramphy[AW_R40_DRAMPHY_REGS_NUM] ;/**< Array of DRAMPHY registers */
+
+    /** @} */
+
+};
+
+#endif /* HW_MISC_ALLWINNER_R40_DRAMC_H */
-- 
2.25.1



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

* [PATCH v1 07/11] hw: sd: allwinner-sdhost: Add sun50i-a64 SoC support
  2023-03-21 10:24 [PATCH v1 00/11] *** add allwinner-r40 support *** qianfanguijin
                   ` (5 preceding siblings ...)
  2023-03-21 10:25 ` [PATCH v1 06/11] hw/arm/allwinner-r40: add SDRAM controller device qianfanguijin
@ 2023-03-21 10:25 ` qianfanguijin
  2023-03-21 10:25 ` [PATCH v1 08/11] hw: arm: allwinner-r40: Fix the mmc controller's type qianfanguijin
                   ` (3 subsequent siblings)
  10 siblings, 0 replies; 17+ messages in thread
From: qianfanguijin @ 2023-03-21 10:25 UTC (permalink / raw)
  To: qemu-arm, qemu-devel
  Cc: Strahinja Jankovic, Peter Maydell, Beniamino Galvani,
	Philippe Mathieu-Daudé,
	Niek Linnenbank, qianfan Zhao

From: qianfan Zhao <qianfanguijin@163.com>

A64's sd register was similar to H3, and it introduced a new register
named SAMP_DL_REG location at 0x144. The dma descriptor buffer size of
mmc2 is only 8K and the other mmc controllers has 64K.

Signed-off-by: qianfan Zhao <qianfanguijin@163.com>
---
 hw/sd/allwinner-sdhost.c         | 70 ++++++++++++++++++++++++++++++--
 include/hw/sd/allwinner-sdhost.h |  9 ++++
 2 files changed, 76 insertions(+), 3 deletions(-)

diff --git a/hw/sd/allwinner-sdhost.c b/hw/sd/allwinner-sdhost.c
index 51e5e90830..38e7844399 100644
--- a/hw/sd/allwinner-sdhost.c
+++ b/hw/sd/allwinner-sdhost.c
@@ -77,6 +77,7 @@ enum {
     REG_SD_DATA1_CRC  = 0x12C, /* CRC Data 1 from card/eMMC */
     REG_SD_DATA0_CRC  = 0x130, /* CRC Data 0 from card/eMMC */
     REG_SD_CRC_STA    = 0x134, /* CRC status from card/eMMC during write */
+    REG_SD_SAMP_DL    = 0x144, /* Sample Delay Control (sun50i-a64) */
     REG_SD_FIFO       = 0x200, /* Read/Write FIFO */
 };
 
@@ -158,6 +159,7 @@ enum {
     REG_SD_RES_CRC_RST      = 0x0,
     REG_SD_DATA_CRC_RST     = 0x0,
     REG_SD_CRC_STA_RST      = 0x0,
+    REG_SD_SAMPLE_DL_RST    = 0x00002000,
     REG_SD_FIFO_RST         = 0x0,
 };
 
@@ -438,6 +440,7 @@ static uint64_t allwinner_sdhost_read(void *opaque, hwaddr offset,
 {
     AwSdHostState *s = AW_SDHOST(opaque);
     AwSdHostClass *sc = AW_SDHOST_GET_CLASS(s);
+    bool out_of_bounds = false;
     uint32_t res = 0;
 
     switch (offset) {
@@ -556,13 +559,24 @@ static uint64_t allwinner_sdhost_read(void *opaque, hwaddr offset,
     case REG_SD_FIFO:      /* Read/Write FIFO */
         res = allwinner_sdhost_fifo_read(s);
         break;
+    case REG_SD_SAMP_DL: /* Sample Delay */
+        if (sc->can_calibrate) {
+            res = s->sample_delay;
+        } else {
+            out_of_bounds = true;
+        }
+        break;
     default:
-        qemu_log_mask(LOG_GUEST_ERROR, "%s: out-of-bounds offset %"
-                      HWADDR_PRIx"\n", __func__, offset);
+        out_of_bounds = true;
         res = 0;
         break;
     }
 
+    if (out_of_bounds) {
+        qemu_log_mask(LOG_GUEST_ERROR, "%s: out-of-bounds offset %"
+                      HWADDR_PRIx"\n", __func__, offset);
+    }
+
     trace_allwinner_sdhost_read(offset, res, size);
     return res;
 }
@@ -581,6 +595,7 @@ static void allwinner_sdhost_write(void *opaque, hwaddr offset,
 {
     AwSdHostState *s = AW_SDHOST(opaque);
     AwSdHostClass *sc = AW_SDHOST_GET_CLASS(s);
+    bool out_of_bounds = false;
 
     trace_allwinner_sdhost_write(offset, value, size);
 
@@ -704,10 +719,21 @@ static void allwinner_sdhost_write(void *opaque, hwaddr offset,
     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;
+    case REG_SD_SAMP_DL: /* Sample delay control */
+        if (sc->can_calibrate) {
+            s->sample_delay = value;
+        } else {
+            out_of_bounds = true;
+        }
+        break;
     default:
+        out_of_bounds = true;
+        break;
+    }
+
+    if (out_of_bounds) {
         qemu_log_mask(LOG_GUEST_ERROR, "%s: out-of-bounds offset %"
                       HWADDR_PRIx"\n", __func__, offset);
-        break;
     }
 }
 
@@ -756,6 +782,7 @@ static const VMStateDescription vmstate_allwinner_sdhost = {
         VMSTATE_UINT32(response_crc, AwSdHostState),
         VMSTATE_UINT32_ARRAY(data_crc, AwSdHostState, 8),
         VMSTATE_UINT32(status_crc, AwSdHostState),
+        VMSTATE_UINT32(sample_delay, AwSdHostState),
         VMSTATE_END_OF_LIST()
     }
 };
@@ -794,6 +821,7 @@ static void allwinner_sdhost_realize(DeviceState *dev, Error **errp)
 static void allwinner_sdhost_reset(DeviceState *dev)
 {
     AwSdHostState *s = AW_SDHOST(dev);
+    AwSdHostClass *sc = AW_SDHOST_GET_CLASS(s);
 
     s->global_ctl = REG_SD_GCTL_RST;
     s->clock_ctl = REG_SD_CKCR_RST;
@@ -834,6 +862,10 @@ static void allwinner_sdhost_reset(DeviceState *dev)
     }
 
     s->status_crc = REG_SD_CRC_STA_RST;
+
+    if (sc->can_calibrate) {
+        s->sample_delay = REG_SD_SAMPLE_DL_RST;
+    }
 }
 
 static void allwinner_sdhost_bus_class_init(ObjectClass *klass, void *data)
@@ -867,6 +899,24 @@ static void allwinner_sdhost_sun5i_class_init(ObjectClass *klass, void *data)
     sc->is_sun4i = false;
 }
 
+static void allwinner_sdhost_sun50i_a64_class_init(ObjectClass *klass,
+                                                   void *data)
+{
+    AwSdHostClass *sc = AW_SDHOST_CLASS(klass);
+    sc->max_desc_size = 64 * KiB;
+    sc->is_sun4i = false;
+    sc->can_calibrate = true;
+}
+
+static void allwinner_sdhost_sun50i_a64_emmc_class_init(ObjectClass *klass,
+                                                        void *data)
+{
+    AwSdHostClass *sc = AW_SDHOST_CLASS(klass);
+    sc->max_desc_size = 8 * KiB;
+    sc->is_sun4i = false;
+    sc->can_calibrate = true;
+}
+
 static const TypeInfo allwinner_sdhost_info = {
     .name          = TYPE_AW_SDHOST,
     .parent        = TYPE_SYS_BUS_DEVICE,
@@ -889,6 +939,18 @@ static const TypeInfo allwinner_sdhost_sun5i_info = {
     .class_init    = allwinner_sdhost_sun5i_class_init,
 };
 
+static const TypeInfo allwinner_sdhost_sun50i_a64_info = {
+    .name          = TYPE_AW_SDHOST_SUN50I_A64,
+    .parent        = TYPE_AW_SDHOST,
+    .class_init    = allwinner_sdhost_sun50i_a64_class_init,
+};
+
+static const TypeInfo allwinner_sdhost_sun50i_a64_emmc_info = {
+    .name          = TYPE_AW_SDHOST_SUN50I_A64_EMMC,
+    .parent        = TYPE_AW_SDHOST,
+    .class_init    = allwinner_sdhost_sun50i_a64_emmc_class_init,
+};
+
 static const TypeInfo allwinner_sdhost_bus_info = {
     .name = TYPE_AW_SDHOST_BUS,
     .parent = TYPE_SD_BUS,
@@ -901,6 +963,8 @@ static void allwinner_sdhost_register_types(void)
     type_register_static(&allwinner_sdhost_info);
     type_register_static(&allwinner_sdhost_sun4i_info);
     type_register_static(&allwinner_sdhost_sun5i_info);
+    type_register_static(&allwinner_sdhost_sun50i_a64_info);
+    type_register_static(&allwinner_sdhost_sun50i_a64_emmc_info);
     type_register_static(&allwinner_sdhost_bus_info);
 }
 
diff --git a/include/hw/sd/allwinner-sdhost.h b/include/hw/sd/allwinner-sdhost.h
index 30c1e60404..1b951177dd 100644
--- a/include/hw/sd/allwinner-sdhost.h
+++ b/include/hw/sd/allwinner-sdhost.h
@@ -38,6 +38,12 @@
 /** Allwinner sun5i family and newer (A13, H2+, H3, etc) */
 #define TYPE_AW_SDHOST_SUN5I TYPE_AW_SDHOST "-sun5i"
 
+/** Allwinner sun50i-a64 */
+#define TYPE_AW_SDHOST_SUN50I_A64 TYPE_AW_SDHOST "-sun50i-a64"
+
+/** Allwinner sun50i-a64 emmc */
+#define TYPE_AW_SDHOST_SUN50I_A64_EMMC  TYPE_AW_SDHOST "-sun50i-a64-emmc"
+
 /** @} */
 
 /**
@@ -110,6 +116,7 @@ struct AwSdHostState {
     uint32_t startbit_detect;   /**< eMMC DDR Start Bit Detection Control */
     uint32_t response_crc;      /**< Response CRC */
     uint32_t data_crc[8];       /**< Data CRC */
+    uint32_t sample_delay;      /**< Sample delay control */
     uint32_t status_crc;        /**< Status CRC */
 
     /** @} */
@@ -132,6 +139,8 @@ struct AwSdHostClass {
     size_t max_desc_size;
     bool   is_sun4i;
 
+    /** does the IP block support autocalibration? */
+    bool can_calibrate;
 };
 
 #endif /* HW_SD_ALLWINNER_SDHOST_H */
-- 
2.25.1



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

* [PATCH v1 08/11] hw: arm: allwinner-r40: Fix the mmc controller's type
  2023-03-21 10:24 [PATCH v1 00/11] *** add allwinner-r40 support *** qianfanguijin
                   ` (6 preceding siblings ...)
  2023-03-21 10:25 ` [PATCH v1 07/11] hw: sd: allwinner-sdhost: Add sun50i-a64 SoC support qianfanguijin
@ 2023-03-21 10:25 ` qianfanguijin
  2023-03-21 10:25 ` [PATCH v1 09/11] hw: arm: allwinner-r40: Add emac and gmac support qianfanguijin
                   ` (2 subsequent siblings)
  10 siblings, 0 replies; 17+ messages in thread
From: qianfanguijin @ 2023-03-21 10:25 UTC (permalink / raw)
  To: qemu-arm, qemu-devel
  Cc: Strahinja Jankovic, Peter Maydell, Beniamino Galvani,
	Philippe Mathieu-Daudé,
	Niek Linnenbank, qianfan Zhao

From: qianfan Zhao <qianfanguijin@163.com>

R40 has SAMP_DL_REG register and mmc2 controller has only 8K dma buffer.
Fix it's compatible string.

Signed-off-by: qianfan Zhao <qianfanguijin@163.com>
---
 hw/arm/allwinner-r40.c | 9 +++++----
 1 file changed, 5 insertions(+), 4 deletions(-)

diff --git a/hw/arm/allwinner-r40.c b/hw/arm/allwinner-r40.c
index f1f6803cf7..191ce8b93c 100644
--- a/hw/arm/allwinner-r40.c
+++ b/hw/arm/allwinner-r40.c
@@ -265,10 +265,11 @@ static void allwinner_r40_init(Object *obj)
                               "clk1-freq");
 
     object_initialize_child(obj, "ccu", &s->ccu, TYPE_AW_R40_CCU);
-    object_initialize_child(obj, "mmc0", &s->mmc0, TYPE_AW_SDHOST_SUN5I);
-    object_initialize_child(obj, "mmc1", &s->mmc1, TYPE_AW_SDHOST_SUN5I);
-    object_initialize_child(obj, "mmc2", &s->mmc2, TYPE_AW_SDHOST_SUN5I);
-    object_initialize_child(obj, "mmc3", &s->mmc3, TYPE_AW_SDHOST_SUN5I);
+    object_initialize_child(obj, "mmc0", &s->mmc0, TYPE_AW_SDHOST_SUN50I_A64);
+    object_initialize_child(obj, "mmc1", &s->mmc1, TYPE_AW_SDHOST_SUN50I_A64);
+    object_initialize_child(obj, "mmc2", &s->mmc2,
+                            TYPE_AW_SDHOST_SUN50I_A64_EMMC);
+    object_initialize_child(obj, "mmc3", &s->mmc3, TYPE_AW_SDHOST_SUN50I_A64);
 
     object_initialize_child(obj, "twi0", &s->i2c0, TYPE_AW_I2C_SUN6I);
     object_initialize_child(obj, "twi1", &s->i2c1, TYPE_AW_I2C_SUN6I);
-- 
2.25.1



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

* [PATCH v1 09/11] hw: arm: allwinner-r40: Add emac and gmac support
  2023-03-21 10:24 [PATCH v1 00/11] *** add allwinner-r40 support *** qianfanguijin
                   ` (7 preceding siblings ...)
  2023-03-21 10:25 ` [PATCH v1 08/11] hw: arm: allwinner-r40: Fix the mmc controller's type qianfanguijin
@ 2023-03-21 10:25 ` qianfanguijin
  2023-03-21 10:25 ` [PATCH v1 10/11] tests: avocado: boot_linux_console: Add test case for bpim2u qianfanguijin
  2023-03-25 14:36 ` [PATCH v1 00/11] *** add allwinner-r40 support *** Strahinja Jankovic
  10 siblings, 0 replies; 17+ messages in thread
From: qianfanguijin @ 2023-03-21 10:25 UTC (permalink / raw)
  To: qemu-arm, qemu-devel
  Cc: Strahinja Jankovic, Peter Maydell, Beniamino Galvani,
	Philippe Mathieu-Daudé,
	Niek Linnenbank, qianfan Zhao

From: qianfan Zhao <qianfanguijin@163.com>

R40 has two ethernet controllers named as emac and gmac. The emac is
compatibled with A10, and the GMAC is compatibled with H3.

Signed-off-by: qianfan Zhao <qianfanguijin@163.com>
---
 hw/arm/allwinner-r40.c         | 48 ++++++++++++++++++++++++++++++++--
 hw/arm/bananapi_m2u.c          |  3 +++
 include/hw/arm/allwinner-r40.h |  6 +++++
 3 files changed, 55 insertions(+), 2 deletions(-)

diff --git a/hw/arm/allwinner-r40.c b/hw/arm/allwinner-r40.c
index 191ce8b93c..a81614c581 100644
--- a/hw/arm/allwinner-r40.c
+++ b/hw/arm/allwinner-r40.c
@@ -39,6 +39,7 @@ const hwaddr allwinner_r40_memmap[] = {
     [AW_R40_DEV_SRAM_A2]    = 0x00004000,
     [AW_R40_DEV_SRAM_A3]    = 0x00008000,
     [AW_R40_DEV_SRAM_A4]    = 0x0000b400,
+    [AW_R40_DEV_EMAC]       = 0x01c0b000,
     [AW_R40_DEV_MMC0]       = 0x01c0f000,
     [AW_R40_DEV_MMC1]       = 0x01c10000,
     [AW_R40_DEV_MMC2]       = 0x01c11000,
@@ -58,6 +59,7 @@ const hwaddr allwinner_r40_memmap[] = {
     [AW_R40_DEV_TWI2]       = 0x01c2b400,
     [AW_R40_DEV_TWI3]       = 0x01c2b800,
     [AW_R40_DEV_TWI4]       = 0x01c2c000,
+    [AW_R40_DEV_GMAC]       = 0x01c50000,
     [AW_R40_DEV_DRAMCOM]    = 0x01c62000,
     [AW_R40_DEV_DRAMCTL]    = 0x01c63000,
     [AW_R40_DEV_DRAMPHY]    = 0x01c65000,
@@ -86,7 +88,6 @@ static struct AwR40Unimplemented r40_unimplemented[] = {
     { "spi1",       0x01c06000, 4 * KiB },
     { "cs0",        0x01c09000, 4 * KiB },
     { "keymem",     0x01c0a000, 4 * KiB },
-    { "emac",       0x01c0b000, 4 * KiB },
     { "usb0-otg",   0x01c13000, 4 * KiB },
     { "usb0-host",  0x01c14000, 4 * KiB },
     { "crypto",     0x01c15000, 4 * KiB },
@@ -131,7 +132,6 @@ static struct AwR40Unimplemented r40_unimplemented[] = {
     { "tvd2",       0x01c33000, 4 * KiB },
     { "tvd3",       0x01c34000, 4 * KiB },
     { "gpu",        0x01c40000, 64 * KiB },
-    { "gmac",       0x01c50000, 64 * KiB },
     { "hstmr",      0x01c60000, 4 * KiB },
     { "tcon-top",   0x01c70000, 4 * KiB },
     { "lcd0",       0x01c71000, 4 * KiB },
@@ -182,6 +182,8 @@ enum {
     AW_R40_GIC_SPI_MMC1      = 33,
     AW_R40_GIC_SPI_MMC2      = 34,
     AW_R40_GIC_SPI_MMC3      = 35,
+    AW_R40_GIC_SPI_EMAC      = 55,
+    AW_R40_GIC_SPI_GMAC      = 85,
     AW_R40_GIC_SPI_TWI3      = 88,
     AW_R40_GIC_SPI_TWI4      = 89,
 };
@@ -277,6 +279,11 @@ static void allwinner_r40_init(Object *obj)
     object_initialize_child(obj, "twi3", &s->i2c3, TYPE_AW_I2C_SUN6I);
     object_initialize_child(obj, "twi4", &s->i2c4, TYPE_AW_I2C_SUN6I);
 
+    object_initialize_child(obj, "emac", &s->emac, TYPE_AW_EMAC);
+    object_initialize_child(obj, "gmac", &s->gmac, TYPE_AW_SUN8I_EMAC);
+    object_property_add_alias(obj, "gmac-phy-addr",
+                              OBJECT(&s->gmac), "phy-addr");
+
     object_initialize_child(obj, "dramc", &s->dramc, TYPE_AW_R40_DRAMC);
     object_property_add_alias(obj, "ram-addr", OBJECT(&s->dramc),
                              "ram-addr");
@@ -286,6 +293,7 @@ static void allwinner_r40_init(Object *obj)
 
 static void allwinner_r40_realize(DeviceState *dev, Error **errp)
 {
+    const char *r40_nic_models[] = { "gmac", "emac", NULL };
     AwR40State *s = AW_R40(dev);
     unsigned i;
 
@@ -481,6 +489,42 @@ static void allwinner_r40_realize(DeviceState *dev, Error **errp)
     sysbus_mmio_map(SYS_BUS_DEVICE(&s->dramc), 1, s->memmap[AW_R40_DEV_DRAMCTL]);
     sysbus_mmio_map(SYS_BUS_DEVICE(&s->dramc), 2, s->memmap[AW_R40_DEV_DRAMPHY]);
 
+    /* nic support gmac and emac */
+    for (int i = 0; i < ARRAY_SIZE(r40_nic_models) - 1; i++) {
+        NICInfo *nic = &nd_table[i];
+
+        if (!nic->used)
+            continue;
+        if (qemu_show_nic_models(nic->model, r40_nic_models))
+            exit(0);
+
+        switch (qemu_find_nic_model(nic, r40_nic_models, r40_nic_models[0])) {
+        case 0: /* gmac */
+            qdev_set_nic_properties(DEVICE(&s->gmac), nic);
+            break;
+        case 1: /* emac */
+            qdev_set_nic_properties(DEVICE(&s->emac), nic);
+            break;
+        default:
+            exit(1);
+            break;
+        }
+    }
+
+    /* GMAC */
+    object_property_set_link(OBJECT(&s->gmac), "dma-memory",
+                                     OBJECT(get_system_memory()), &error_fatal);
+    sysbus_realize(SYS_BUS_DEVICE(&s->gmac), &error_fatal);
+    sysbus_mmio_map(SYS_BUS_DEVICE(&s->gmac), 0, s->memmap[AW_R40_DEV_GMAC]);
+    sysbus_connect_irq(SYS_BUS_DEVICE(&s->gmac), 0,
+                       qdev_get_gpio_in(DEVICE(&s->gic), AW_R40_GIC_SPI_GMAC));
+
+    /* EMAC */
+    sysbus_realize(SYS_BUS_DEVICE(&s->emac), &error_fatal);
+    sysbus_mmio_map(SYS_BUS_DEVICE(&s->emac), 0, s->memmap[AW_R40_DEV_EMAC]);
+    sysbus_connect_irq(SYS_BUS_DEVICE(&s->emac), 0,
+                       qdev_get_gpio_in(DEVICE(&s->gic), AW_R40_GIC_SPI_EMAC));
+
     /* Unimplemented devices */
     for (i = 0; i < ARRAY_SIZE(r40_unimplemented); i++) {
         create_unimplemented_device(r40_unimplemented[i].device_name,
diff --git a/hw/arm/bananapi_m2u.c b/hw/arm/bananapi_m2u.c
index d185f979c0..7207f673e2 100644
--- a/hw/arm/bananapi_m2u.c
+++ b/hw/arm/bananapi_m2u.c
@@ -86,6 +86,9 @@ static void bpim2u_init(MachineState *machine)
     object_property_set_int(OBJECT(r40), "ram-size", r40->ram_size,
                             &error_abort);
 
+    /* GMAC PHY */
+    object_property_set_uint(OBJECT(r40), "gmac-phy-addr", 1, &error_abort);
+
     /* Mark R40 object realized */
     qdev_realize(DEVICE(r40), NULL, &error_abort);
 
diff --git a/include/hw/arm/allwinner-r40.h b/include/hw/arm/allwinner-r40.h
index 6fc9691bb5..c97392d12f 100644
--- a/include/hw/arm/allwinner-r40.h
+++ b/include/hw/arm/allwinner-r40.h
@@ -28,6 +28,8 @@
 #include "hw/misc/allwinner-r40-ccu.h"
 #include "hw/misc/allwinner-r40-dramc.h"
 #include "hw/i2c/allwinner-i2c.h"
+#include "hw/net/allwinner_emac.h"
+#include "hw/net/allwinner-sun8i-emac.h"
 #include "target/arm/cpu.h"
 #include "sysemu/block-backend.h"
 
@@ -36,6 +38,7 @@ enum {
     AW_R40_DEV_SRAM_A2,
     AW_R40_DEV_SRAM_A3,
     AW_R40_DEV_SRAM_A4,
+    AW_R40_DEV_EMAC,
     AW_R40_DEV_MMC0,
     AW_R40_DEV_MMC1,
     AW_R40_DEV_MMC2,
@@ -55,6 +58,7 @@ enum {
     AW_R40_DEV_TWI2,
     AW_R40_DEV_TWI3,
     AW_R40_DEV_TWI4,
+    AW_R40_DEV_GMAC,
     AW_R40_DEV_GIC_DIST,
     AW_R40_DEV_GIC_CPU,
     AW_R40_DEV_GIC_HYP,
@@ -111,6 +115,8 @@ struct AwR40State {
     AWI2CState i2c2;
     AWI2CState i2c3;
     AWI2CState i2c4;
+    AwEmacState emac;
+    AwSun8iEmacState gmac;
     GICState gic;
     MemoryRegion sram_a1;
     MemoryRegion sram_a2;
-- 
2.25.1



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

* [PATCH v1 10/11] tests: avocado: boot_linux_console: Add test case for bpim2u
  2023-03-21 10:24 [PATCH v1 00/11] *** add allwinner-r40 support *** qianfanguijin
                   ` (8 preceding siblings ...)
  2023-03-21 10:25 ` [PATCH v1 09/11] hw: arm: allwinner-r40: Add emac and gmac support qianfanguijin
@ 2023-03-21 10:25 ` qianfanguijin
  2023-03-25 14:36 ` [PATCH v1 00/11] *** add allwinner-r40 support *** Strahinja Jankovic
  10 siblings, 0 replies; 17+ messages in thread
From: qianfanguijin @ 2023-03-21 10:25 UTC (permalink / raw)
  To: qemu-arm, qemu-devel
  Cc: Strahinja Jankovic, Peter Maydell, Beniamino Galvani,
	Philippe Mathieu-Daudé,
	Niek Linnenbank, qianfan Zhao

From: qianfan Zhao <qianfanguijin@163.com>

Add test case for booting from initrd and sd card.

Signed-off-by: qianfan Zhao <qianfanguijin@163.com>
---
 tests/avocado/boot_linux_console.py | 173 ++++++++++++++++++++++++++++
 1 file changed, 173 insertions(+)

diff --git a/tests/avocado/boot_linux_console.py b/tests/avocado/boot_linux_console.py
index 574609bf43..9758fc2b68 100644
--- a/tests/avocado/boot_linux_console.py
+++ b/tests/avocado/boot_linux_console.py
@@ -760,6 +760,179 @@ def test_arm_quanta_gsj_initrd(self):
         self.wait_for_console_pattern(
                 'Give root password for system maintenance')
 
+    def test_arm_bpim2u(self):
+        """
+        :avocado: tags=arch:arm
+        :avocado: tags=machine:bpim2u
+        :avocado: tags=accel:tcg
+        """
+        deb_url = ('https://apt.armbian.com/pool/main/l/'
+                   'linux-5.10.16-sunxi/linux-image-current-sunxi_21.02.2_armhf.deb')
+        deb_hash = '9fa84beda245cabf0b4fa84cf6eaa7738ead1da0'
+        deb_path = self.fetch_asset(deb_url, asset_hash=deb_hash)
+        kernel_path = self.extract_from_deb(deb_path,
+                                            '/boot/vmlinuz-5.10.16-sunxi')
+        dtb_path = '/usr/lib/linux-image-current-sunxi/sun8i-r40-bananapi-m2-ultra.dtb'
+        dtb_path = self.extract_from_deb(deb_path, dtb_path)
+
+        self.vm.set_console()
+        kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE +
+                               'console=ttyS0,115200n8 '
+                               'earlycon=uart,mmio32,0x1c28000')
+        self.vm.add_args('-kernel', kernel_path,
+                         '-dtb', dtb_path,
+                         '-append', kernel_command_line)
+        self.vm.launch()
+        console_pattern = 'Kernel command line: %s' % kernel_command_line
+        self.wait_for_console_pattern(console_pattern)
+
+    def test_arm_bpim2u_initrd(self):
+        """
+        :avocado: tags=arch:arm
+        :avocado: tags=accel:tcg
+        :avocado: tags=machine:bpim2u
+        """
+        deb_url = ('https://apt.armbian.com/pool/main/l/'
+                   'linux-5.10.16-sunxi/linux-image-current-sunxi_21.02.2_armhf.deb')
+        deb_hash = '9fa84beda245cabf0b4fa84cf6eaa7738ead1da0'
+        deb_path = self.fetch_asset(deb_url, asset_hash=deb_hash)
+        kernel_path = self.extract_from_deb(deb_path,
+                                            '/boot/vmlinuz-5.10.16-sunxi')
+        dtb_path = '/usr/lib/linux-image-current-sunxi/sun8i-r40-bananapi-m2-ultra.dtb'
+        dtb_path = self.extract_from_deb(deb_path, dtb_path)
+        initrd_url = ('https://github.com/groeck/linux-build-test/raw/'
+                      '2eb0a73b5d5a28df3170c546ddaaa9757e1e0848/rootfs/'
+                      'arm/rootfs-armv7a.cpio.gz')
+        initrd_hash = '604b2e45cdf35045846b8bbfbf2129b1891bdc9c'
+        initrd_path_gz = self.fetch_asset(initrd_url, asset_hash=initrd_hash)
+        initrd_path = os.path.join(self.workdir, 'rootfs.cpio')
+        archive.gzip_uncompress(initrd_path_gz, initrd_path)
+
+        self.vm.set_console()
+        kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE +
+                               'console=ttyS0,115200 '
+                               'panic=-1 noreboot')
+        self.vm.add_args('-kernel', kernel_path,
+                         '-dtb', dtb_path,
+                         '-initrd', initrd_path,
+                         '-append', kernel_command_line,
+                         '-no-reboot')
+        self.vm.launch()
+        self.wait_for_console_pattern('Boot successful.')
+
+        exec_command_and_wait_for_pattern(self, 'cat /proc/cpuinfo',
+                                                'Allwinner sun8i Family')
+        exec_command_and_wait_for_pattern(self, 'cat /proc/iomem',
+                                                'system-control@1c00000')
+        exec_command_and_wait_for_pattern(self, 'reboot',
+                                                'reboot: Restarting system')
+        # Wait for VM to shut down gracefully
+        self.vm.wait()
+
+    def test_arm_bpim2u_gmac(self):
+        """
+        :avocado: tags=arch:arm
+        :avocado: tags=accel:tcg
+        :avocado: tags=machine:bpim2u
+        :avocado: tags=device:sd
+        """
+        self.require_netdev('user')
+
+        deb_url = ('https://apt.armbian.com/pool/main/l/'
+                   'linux-5.10.16-sunxi/linux-image-current-sunxi_21.02.2_armhf.deb')
+        deb_hash = '9fa84beda245cabf0b4fa84cf6eaa7738ead1da0'
+        deb_path = self.fetch_asset(deb_url, asset_hash=deb_hash)
+        kernel_path = self.extract_from_deb(deb_path,
+                                            '/boot/vmlinuz-5.10.16-sunxi')
+        dtb_path = '/usr/lib/linux-image-current-sunxi/sun8i-r40-bananapi-m2-ultra.dtb'
+        dtb_path = self.extract_from_deb(deb_path, dtb_path)
+        rootfs_url = ('http://storage.kernelci.org/images/rootfs/buildroot/'
+                      'buildroot-baseline/20221116.0/armel/rootfs.ext2.xz')
+        rootfs_hash = 'fae32f337c7b87547b10f42599acf109da8b6d9a'
+        rootfs_path_xz = self.fetch_asset(rootfs_url, asset_hash=rootfs_hash)
+        rootfs_path = os.path.join(self.workdir, 'rootfs.cpio')
+        archive.lzma_uncompress(rootfs_path_xz, rootfs_path)
+        image_pow2ceil_expand(rootfs_path)
+
+        self.vm.set_console()
+        kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE +
+                               'console=ttyS0,115200 '
+                               'root=/dev/mmcblk0 rootwait rw '
+                               'panic=-1 noreboot')
+        self.vm.add_args('-kernel', kernel_path,
+                         '-dtb', dtb_path,
+                         '-drive', 'file=' + rootfs_path + ',if=sd,format=raw',
+                         '-net', 'nic,model=gmac,netdev=host_gmac',
+                         '-netdev', 'user,id=host_gmac',
+                         '-append', kernel_command_line,
+                         '-no-reboot')
+        self.vm.launch()
+        shell_ready = "/bin/sh: can't access tty; job control turned off"
+        self.wait_for_console_pattern(shell_ready)
+
+        exec_command_and_wait_for_pattern(self, 'cat /proc/cpuinfo',
+                                                'Allwinner sun8i Family')
+        exec_command_and_wait_for_pattern(self, 'cat /proc/partitions',
+                                                'mmcblk0')
+        exec_command_and_wait_for_pattern(self, 'ifconfig eth0 up',
+                                                 'eth0: Link is Up')
+        exec_command_and_wait_for_pattern(self, 'udhcpc eth0',
+            'udhcpc: lease of 10.0.2.15 obtained')
+        exec_command_and_wait_for_pattern(self, 'ping -c 3 10.0.2.2',
+            '3 packets transmitted, 3 packets received, 0% packet loss')
+        exec_command_and_wait_for_pattern(self, 'reboot',
+                                                'reboot: Restarting system')
+        # Wait for VM to shut down gracefully
+        self.vm.wait()
+
+    @skipUnless(os.getenv('AVOCADO_ALLOW_LARGE_STORAGE'), 'storage limited')
+    def test_arm_bpim2u_openwrt_22_03_3(self):
+        """
+        :avocado: tags=arch:arm
+        :avocado: tags=machine:bpim2u
+        :avocado: tags=device:sd
+        """
+
+        # This test download a 8.9 MiB compressed image and expand it
+        # to 127 MiB.
+        image_url = ('https://downloads.openwrt.org/releases/22.03.3/targets/'
+                     'sunxi/cortexa7/openwrt-22.03.3-sunxi-cortexa7-'
+                     'sinovoip_bananapi-m2-ultra-ext4-sdcard.img.gz')
+        image_hash = ('5b41b4e11423e562c6011640f9a7cd3b'
+                      'dd0a3d42b83430f7caa70a432e6cd82c')
+        image_path_gz = self.fetch_asset(image_url, asset_hash=image_hash,
+                                         algorithm='sha256')
+        image_path = archive.extract(image_path_gz, self.workdir)
+        image_pow2ceil_expand(image_path)
+
+        self.vm.set_console()
+        self.vm.add_args('-drive', 'file=' + image_path + ',if=sd,format=raw',
+                         '-nic', 'user',
+                         '-no-reboot')
+        self.vm.launch()
+
+        kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE +
+                               'usbcore.nousb '
+                               'noreboot')
+
+        self.wait_for_console_pattern('U-Boot SPL')
+
+        interrupt_interactive_console_until_pattern(
+                self, 'Hit any key to stop autoboot:', '=>')
+        exec_command_and_wait_for_pattern(self, "setenv extraargs '" +
+                                                kernel_command_line + "'", '=>')
+        exec_command_and_wait_for_pattern(self, 'boot', 'Starting kernel ...');
+
+        self.wait_for_console_pattern(
+            'Please press Enter to activate this console.')
+
+        exec_command_and_wait_for_pattern(self, ' ', 'root@')
+
+        exec_command_and_wait_for_pattern(self, 'cat /proc/cpuinfo',
+                                                'Allwinner sun8i Family')
+        exec_command_and_wait_for_pattern(self, 'cat /proc/iomem',
+                                                'system-control@1c00000')
+
     def test_arm_orangepi(self):
         """
         :avocado: tags=arch:arm
-- 
2.25.1



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

* Re: [PATCH v1 00/11] *** add allwinner-r40 support ***
  2023-03-21 10:24 [PATCH v1 00/11] *** add allwinner-r40 support *** qianfanguijin
                   ` (9 preceding siblings ...)
  2023-03-21 10:25 ` [PATCH v1 10/11] tests: avocado: boot_linux_console: Add test case for bpim2u qianfanguijin
@ 2023-03-25 14:36 ` Strahinja Jankovic
  10 siblings, 0 replies; 17+ messages in thread
From: Strahinja Jankovic @ 2023-03-25 14:36 UTC (permalink / raw)
  To: qianfanguijin
  Cc: qemu-arm, qemu-devel, Peter Maydell, Beniamino Galvani,
	Philippe Mathieu-Daudé,
	Niek Linnenbank

Hi,

It's great that you are adding support for a new SoC/Board!

On Tue, Mar 21, 2023 at 11:25 AM <qianfanguijin@163.com> wrote:
>
> From: qianfan Zhao <qianfanguijin@163.com>
>
> *** history ***
>
> # v1: 2023-03-21
>
> The first version which add allwinner-r40 support, supported features:
>
> + ccu
> + dram controller
> + uart
> + i2c and pmic(axp221)
> + sdcard
> + emac/gmac
>
> Also provide a test case under avocado, running quickly test:
>
> $ AVOCADO_ALLOW_LARGE_STORAGE=yes tests/venv/bin/avocado \
>     --verbose --show=app,console run -t machine:bpim2u \
>     ../tests/avocado/boot_linux_console.py

I tried running this on the latest QEMU source and compilation fails with

[1758/2912] Compiling C object libqemu-arm-softmmu.fa.p/hw_arm_bananapi_m2u.c.o
FAILED: libqemu-arm-softmmu.fa.p/hw_arm_bananapi_m2u.c.o
cc -m64 -mcx16 -Ilibqemu-arm-softmmu.fa.p -I. -I../.. -Itarget/arm
-I../../target/arm -Iqapi -Itrace -Iui -Iui/shader
-I/usr/include/pixman-1 -I/usr/include/capstone
-I/usr/include/glib-2.0 -I/usr/lib/x86_64-linux-gnu/glib-2.0/include
-fdiagnostics-color=auto -Wall -Winvalid-pch -Werror -std=gnu11 -O2 -g
-isystem /home/strahinja/work/qemu/linux-headers -isystem
linux-headers -iquote . -iquote /home/strahinja/work/qemu -iquote
/home/strahinja/work/qemu/include -iquote
/home/strahinja/work/qemu/tcg/i386 -pthread -U_FORTIFY_SOURCE
-D_FORTIFY_SOURCE=2 -D_GNU_SOURCE -D_FILE_OFFSET_BITS=64
-D_LARGEFILE_SOURCE -fno-strict-aliasing -fno-common -fwrapv -Wundef
-Wwrite-strings -Wmissing-prototypes -Wstrict-prototypes
-Wredundant-decls -Wold-style-declaration -Wold-style-definition
-Wtype-limits -Wformat-security -Wformat-y2k -Winit-self
-Wignored-qualifiers -Wempty-body -Wnested-externs -Wendif-labels
-Wexpansion-to-defined -Wimplicit-fallthrough=2
-Wmissing-format-attribute -Wno-missing-include-dirs
-Wno-shift-negative-value -Wno-psabi -fstack-protector-strong -fPIE
-isystem../../linux-headers -isystemlinux-headers -DNEED_CPU_H
'-DCONFIG_TARGET="arm-softmmu-config-target.h"'
'-DCONFIG_DEVICES="arm-softmmu-config-devices.h"' -MD -MQ
libqemu-arm-softmmu.fa.p/hw_arm_bananapi_m2u.c.o -MF
libqemu-arm-softmmu.fa.p/hw_arm_bananapi_m2u.c.o.d -o
libqemu-arm-softmmu.fa.p/hw_arm_bananapi_m2u.c.o -c
../../hw/arm/bananapi_m2u.c
../../hw/arm/bananapi_m2u.c: In function ‘mmc_attach_drive’:
../../hw/arm/bananapi_m2u.c:41:9: error: implicit declaration of
function ‘error_report’; did you mean ‘error_report_err’?
[-Werror=implicit-function-declaration]
   41 |         error_report("No SD bus found in SOC object");
      |         ^~~~~~~~~~~~
      |         error_report_err
../../hw/arm/bananapi_m2u.c:41:9: error: nested extern declaration of
‘error_report’ [-Werror=nested-externs]
cc1: all warnings being treated as errors

After adding

#include "qemu/error-report.h"

to hw/arm/bananapi_m2u.c, it passed and I was able to run avocado
tests for this board, as well as for cubieboard and orangepi-pc.

* Bananapi

 AVOCADO_ALLOW_LARGE_STORAGE=yes avocado     --verbose
--show=app,console run -t machine:bpim2u
tests/avocado/boot_linux_console.py
Fetching asset from
tests/avocado/boot_linux_console.py:BootLinuxConsole.test_arm_bpim2u
Fetching asset from
tests/avocado/boot_linux_console.py:BootLinuxConsole.test_arm_bpim2u_initrd
Fetching asset from
tests/avocado/boot_linux_console.py:BootLinuxConsole.test_arm_bpim2u_initrd
Fetching asset from
tests/avocado/boot_linux_console.py:BootLinuxConsole.test_arm_bpim2u_gmac
Fetching asset from
tests/avocado/boot_linux_console.py:BootLinuxConsole.test_arm_bpim2u_gmac
Fetching asset from
tests/avocado/boot_linux_console.py:BootLinuxConsole.test_arm_bpim2u_openwrt_22_03_3
JOB ID     : ed47ffc59b1f9ceb8b68a87405040d3af2fb9156
JOB LOG    : /home/strahinja/avocado/job-results/job-2023-03-25T15.24-ed47ffc/job.log
...
 RESULTS    : PASS 4 | ERROR 0 | FAIL 0 | SKIP 0 | WARN 0 | INTERRUPT
0 | CANCEL 0
JOB TIME   : 70.79 s

* Cubieboard

 AVOCADO_ALLOW_LARGE_STORAGE=yes avocado     --verbose
--show=app,console run -t machine:cubieboard
tests/avocado/boot_linux_console.py
Fetching asset from
tests/avocado/boot_linux_console.py:BootLinuxConsole.test_arm_cubieboard_initrd
Fetching asset from
tests/avocado/boot_linux_console.py:BootLinuxConsole.test_arm_cubieboard_initrd
Fetching asset from
tests/avocado/boot_linux_console.py:BootLinuxConsole.test_arm_cubieboard_sata
Fetching asset from
tests/avocado/boot_linux_console.py:BootLinuxConsole.test_arm_cubieboard_sata
Fetching asset from
tests/avocado/boot_linux_console.py:BootLinuxConsole.test_arm_cubieboard_openwrt_22_03_2
JOB ID     : 1510a9b5f36b74dc1378d50dbb40553e6fb76316
JOB LOG    : /home/strahinja/avocado/job-results/job-2023-03-25T15.27-1510a9b/job.log
...
 RESULTS    : PASS 3 | ERROR 0 | FAIL 0 | SKIP 0 | WARN 0 | INTERRUPT
0 | CANCEL 0
JOB TIME   : 42.69 s

* OrangePi PC

AVOCADO_ALLOW_LARGE_STORAGE=yes avocado     --verbose
--show=app,console run -t machine:orangepi-pc
tests/avocado/boot_linux_console.py
Fetching asset from
tests/avocado/boot_linux_console.py:BootLinuxConsole.test_arm_orangepi
Fetching asset from
tests/avocado/boot_linux_console.py:BootLinuxConsole.test_arm_orangepi_initrd
Fetching asset from
tests/avocado/boot_linux_console.py:BootLinuxConsole.test_arm_orangepi_initrd
Fetching asset from
tests/avocado/boot_linux_console.py:BootLinuxConsole.test_arm_orangepi_sd
Fetching asset from
tests/avocado/boot_linux_console.py:BootLinuxConsole.test_arm_orangepi_sd
Fetching asset from
tests/avocado/boot_linux_console.py:BootLinuxConsole.test_arm_orangepi_bionic_20_08
Fetching asset from
tests/avocado/boot_linux_console.py:BootLinuxConsole.test_arm_orangepi_uboot_netbsd9
Fetching asset from
tests/avocado/boot_linux_console.py:BootLinuxConsole.test_arm_orangepi_uboot_netbsd9
JOB ID     : 6655d0df3c2eecf2181d7fa2d7cd8aee9e4b504c
JOB LOG    : /home/strahinja/avocado/job-results/job-2023-03-25T15.31-6655d0d/job.log
...
RESULTS    : PASS 5 | ERROR 0 | FAIL 0 | SKIP 0 | WARN 0 | INTERRUPT 0
| CANCEL 0
JOB TIME   : 248.93 s

So I can add

Tested-by: Strahinja Jankovic <strahinja.p.jankovic@gmail.com>

I will go through the individual commits during the weekend.

Best regards,
Strahinja


>
> qianfan Zhao (11):
>   hw: arm: Add bananapi M2-Ultra and allwinner-r40 support
>   hw/arm/allwinner-r40: add Clock Control Unit
>   hw: allwinner-r40: Complete uart devices
>   hw: arm: allwinner-r40: Add 5 TWI controllers
>   hw/misc: AXP221 PMU Emulation
>   hw/arm/allwinner-r40: add SDRAM controller device
>   hw: sd: allwinner-sdhost: Add sun50i-a64 SoC support
>   hw: arm: allwinner-r40: Fix the mmc controller's type
>   hw: arm: allwinner-r40: Add emac and gmac support
>   tests: avocado: boot_linux_console: Add test case for bpim2u
>   docs: system: arm: Introduce bananapi_m2u
>
>  configs/devices/arm-softmmu/default.mak |   1 +
>  docs/system/arm/bananapi_m2u.rst        | 138 ++++++
>  hw/arm/Kconfig                          |  10 +
>  hw/arm/allwinner-r40.c                  | 558 ++++++++++++++++++++++++
>  hw/arm/bananapi_m2u.c                   | 131 ++++++
>  hw/arm/meson.build                      |   1 +
>  hw/misc/Kconfig                         |   4 +
>  hw/misc/allwinner-r40-ccu.c             | 207 +++++++++
>  hw/misc/allwinner-r40-dramc.c           | 499 +++++++++++++++++++++
>  hw/misc/axp221.c                        | 196 +++++++++
>  hw/misc/meson.build                     |   3 +
>  hw/misc/trace-events                    |  19 +
>  hw/sd/allwinner-sdhost.c                |  70 ++-
>  include/hw/arm/allwinner-r40.h          | 148 +++++++
>  include/hw/misc/allwinner-r40-ccu.h     |  65 +++
>  include/hw/misc/allwinner-r40-dramc.h   | 108 +++++
>  include/hw/sd/allwinner-sdhost.h        |   9 +
>  tests/avocado/boot_linux_console.py     | 173 ++++++++
>  18 files changed, 2337 insertions(+), 3 deletions(-)
>  create mode 100644 docs/system/arm/bananapi_m2u.rst
>  create mode 100644 hw/arm/allwinner-r40.c
>  create mode 100644 hw/arm/bananapi_m2u.c
>  create mode 100644 hw/misc/allwinner-r40-ccu.c
>  create mode 100644 hw/misc/allwinner-r40-dramc.c
>  create mode 100644 hw/misc/axp221.c
>  create mode 100644 include/hw/arm/allwinner-r40.h
>  create mode 100644 include/hw/misc/allwinner-r40-ccu.h
>  create mode 100644 include/hw/misc/allwinner-r40-dramc.h
>
> --
> 2.25.1
>


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

* Re: [PATCH v1 01/11] hw: arm: Add bananapi M2-Ultra and allwinner-r40 support
  2023-03-21 10:25 ` [PATCH v1 01/11] hw: arm: Add bananapi M2-Ultra and allwinner-r40 support qianfanguijin
@ 2023-03-25 20:59   ` Strahinja Jankovic
  0 siblings, 0 replies; 17+ messages in thread
From: Strahinja Jankovic @ 2023-03-25 20:59 UTC (permalink / raw)
  To: qianfanguijin
  Cc: qemu-arm, qemu-devel, Peter Maydell, Beniamino Galvani,
	Philippe Mathieu-Daudé,
	Niek Linnenbank

Hi,

On Tue, Mar 21, 2023 at 11:25 AM <qianfanguijin@163.com> wrote:
>
> From: qianfan Zhao <qianfanguijin@163.com>
>
> Allwinner R40 (sun8i) SoC features a Quad-Core Cortex-A7 ARM CPU,
> and a Mali400 MP2 GPU from ARM. It's also known as the Allwinner T3
> for In-Car Entertainment usage, A40i and A40pro are variants that
> differ in applicable temperatures range (industrial and military).
>
> This patch is a draft and provides very few features that we will
> improve late.
>
> Signed-off-by: qianfan Zhao <qianfanguijin@163.com>

Please run the `scripts/checkpatch.pl` on all patches before
resubmitting, there are several coding style items that should be
fixed.

> ---
>  configs/devices/arm-softmmu/default.mak |   1 +
>  hw/arm/Kconfig                          |   9 +
>  hw/arm/allwinner-r40.c                  | 425 ++++++++++++++++++++++++
>  hw/arm/bananapi_m2u.c                   | 116 +++++++
>  hw/arm/meson.build                      |   1 +
>  include/hw/arm/allwinner-r40.h          | 111 +++++++
>  6 files changed, 663 insertions(+)
>  create mode 100644 hw/arm/allwinner-r40.c
>  create mode 100644 hw/arm/bananapi_m2u.c
>  create mode 100644 include/hw/arm/allwinner-r40.h
>
> diff --git a/configs/devices/arm-softmmu/default.mak b/configs/devices/arm-softmmu/default.mak
> index 1b49a7830c..76a43add23 100644
> --- a/configs/devices/arm-softmmu/default.mak
> +++ b/configs/devices/arm-softmmu/default.mak
> @@ -43,3 +43,4 @@ CONFIG_FSL_IMX6UL=y
>  CONFIG_SEMIHOSTING=y
>  CONFIG_ARM_COMPATIBLE_SEMIHOSTING=y
>  CONFIG_ALLWINNER_H3=y
> +CONFIG_ALLWINNER_R40=y
> diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig
> index b5aed4aff5..9e14c3427e 100644
> --- a/hw/arm/Kconfig
> +++ b/hw/arm/Kconfig
> @@ -344,6 +344,15 @@ config ALLWINNER_H3
>      select USB_EHCI_SYSBUS
>      select SD
>
> +config ALLWINNER_R40
> +    bool
> +    select ALLWINNER_A10_PIT
> +    select SERIAL
> +    select ARM_TIMER
> +    select ARM_GIC
> +    select UNIMP
> +    select SD
> +
>  config RASPI
>      bool
>      select FRAMEBUFFER
> diff --git a/hw/arm/allwinner-r40.c b/hw/arm/allwinner-r40.c
> new file mode 100644
> index 0000000000..d0516f4e96
> --- /dev/null
> +++ b/hw/arm/allwinner-r40.c
> @@ -0,0 +1,425 @@
> +/*
> + * Allwinner R40/A40i/T3 System on Chip emulation
> + *
> + * Copyright (C) 2023 qianfan Zhao <qianfanguijin@163.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 "qapi/error.h"
> +#include "qemu/error-report.h"
> +#include "qemu/bswap.h"
> +#include "qemu/module.h"
> +#include "qemu/units.h"
> +#include "hw/qdev-core.h"
> +#include "hw/sysbus.h"
> +#include "hw/char/serial.h"
> +#include "hw/misc/unimp.h"
> +#include "hw/usb/hcd-ehci.h"
> +#include "hw/loader.h"
> +#include "sysemu/sysemu.h"
> +#include "hw/arm/allwinner-r40.h"
> +
> +/* Memory map */
> +const hwaddr allwinner_r40_memmap[] = {
> +    [AW_R40_DEV_SRAM_A1]    = 0x00000000,
> +    [AW_R40_DEV_SRAM_A2]    = 0x00004000,
> +    [AW_R40_DEV_SRAM_A3]    = 0x00008000,
> +    [AW_R40_DEV_SRAM_A4]    = 0x0000b400,
> +    [AW_R40_DEV_MMC0]       = 0x01c0f000,
> +    [AW_R40_DEV_MMC1]       = 0x01c10000,
> +    [AW_R40_DEV_MMC2]       = 0x01c11000,
> +    [AW_R40_DEV_MMC3]       = 0x01c12000,
> +    [AW_R40_DEV_PIT]        = 0x01c20c00,
> +    [AW_R40_DEV_UART0]      = 0x01c28000,
> +    [AW_R40_DEV_GIC_DIST]   = 0x01c81000,
> +    [AW_R40_DEV_GIC_CPU]    = 0x01c82000,
> +    [AW_R40_DEV_GIC_HYP]    = 0x01c84000,
> +    [AW_R40_DEV_GIC_VCPU]   = 0x01c86000,
> +    [AW_R40_DEV_SDRAM]      = 0x40000000
> +};
> +
> +/* List of unimplemented devices */
> +struct AwR40Unimplemented {
> +    const char *device_name;
> +    hwaddr base;
> +    hwaddr size;
> +};
> +
> +static struct AwR40Unimplemented r40_unimplemented[] = {
> +    { "d-engine",   0x01000000, 4 * MiB },
> +    { "d-inter",    0x01400000, 128 * KiB },
> +    { "sram-c",     0x01c00000, 4 * KiB },
> +    { "dma",        0x01c02000, 4 * KiB },
> +    { "nfdc",       0x01c03000, 4 * KiB },
> +    { "ts",         0x01c04000, 4 * KiB },
> +    { "spi0",       0x01c05000, 4 * KiB },
> +    { "spi1",       0x01c06000, 4 * KiB },
> +    { "cs0",        0x01c09000, 4 * KiB },
> +    { "keymem",     0x01c0a000, 4 * KiB },
> +    { "emac",       0x01c0b000, 4 * KiB },
> +    { "usb0-otg",   0x01c13000, 4 * KiB },
> +    { "usb0-host",  0x01c14000, 4 * KiB },
> +    { "crypto",     0x01c15000, 4 * KiB },
> +    { "spi2",       0x01c17000, 4 * KiB },
> +    { "sata",       0x01c18000, 4 * KiB },
> +    { "usb1-host",  0x01c19000, 4 * KiB },
> +    { "sid",        0x01c1b000, 4 * KiB },
> +    { "usb2-host",  0x01c1c000, 4 * KiB },
> +    { "cs1",        0x01c1d000, 4 * KiB },
> +    { "spi3",       0x01c1f000, 4 * KiB },
> +    { "ccu",        0x01c20000, 1 * KiB },
> +    { "rtc",        0x01c20400, 1 * KiB },
> +    { "pio",        0x01c20800, 1 * KiB },
> +    { "owa",        0x01c21000, 1 * KiB },
> +    { "ac97",       0x01c21400, 1 * KiB },
> +    { "cir0",       0x01c21800, 1 * KiB },
> +    { "cir1",       0x01c21c00, 1 * KiB },
> +    { "pcm0",       0x01c22000, 1 * KiB },
> +    { "pcm1",       0x01c22400, 1 * KiB },
> +    { "pcm2",       0x01c22800, 1 * KiB },
> +    { "audio",      0x01c22c00, 1 * KiB },
> +    { "keypad",     0x01c23000, 1 * KiB },
> +    { "pwm",        0x01c23400, 1 * KiB },
> +    { "keyadc",     0x01c24400, 1 * KiB },
> +    { "ths",        0x01c24c00, 1 * KiB },
> +    { "rtp",        0x01c25000, 1 * KiB },
> +    { "pmu",        0x01c25400, 1 * KiB },
> +    { "cpu-cfg",    0x01c25c00, 1 * KiB },
> +    { "uart0",      0x01c28000, 1 * KiB },
> +    { "uart1",      0x01c28400, 1 * KiB },
> +    { "uart2",      0x01c28800, 1 * KiB },
> +    { "uart3",      0x01c28c00, 1 * KiB },
> +    { "uart4",      0x01c29000, 1 * KiB },
> +    { "uart5",      0x01c29400, 1 * KiB },
> +    { "uart6",      0x01c29800, 1 * KiB },
> +    { "uart7",      0x01c29c00, 1 * KiB },
> +    { "ps20",       0x01c2a000, 1 * KiB },
> +    { "ps21",       0x01c2a400, 1 * KiB },
> +    { "twi0",       0x01c2ac00, 1 * KiB },
> +    { "twi1",       0x01c2b000, 1 * KiB },
> +    { "twi2",       0x01c2b400, 1 * KiB },
> +    { "twi3",       0x01c2b800, 1 * KiB },
> +    { "twi4",       0x01c2c000, 1 * KiB },
> +    { "scr",        0x01c2c400, 1 * KiB },
> +    { "tvd-top",    0x01c30000, 4 * KiB },
> +    { "tvd0",       0x01c31000, 4 * KiB },
> +    { "tvd1",       0x01c32000, 4 * KiB },
> +    { "tvd2",       0x01c33000, 4 * KiB },
> +    { "tvd3",       0x01c34000, 4 * KiB },
> +    { "gpu",        0x01c40000, 64 * KiB },
> +    { "gmac",       0x01c50000, 64 * KiB },
> +    { "hstmr",      0x01c60000, 4 * KiB },
> +    { "dram-com",   0x01c62000, 4 * KiB },
> +    { "dram-ctl",   0x01c63000, 4 * KiB },
> +    { "tcon-top",   0x01c70000, 4 * KiB },
> +    { "lcd0",       0x01c71000, 4 * KiB },
> +    { "lcd1",       0x01c72000, 4 * KiB },
> +    { "tv0",        0x01c73000, 4 * KiB },
> +    { "tv1",        0x01c74000, 4 * KiB },
> +    { "tve-top",    0x01c90000, 16 * KiB },
> +    { "tve0",       0x01c94000, 16 * KiB },
> +    { "tve1",       0x01c98000, 16 * KiB },
> +    { "mipi_dsi",   0x01ca0000, 4 * KiB },
> +    { "mipi_dphy",  0x01ca1000, 4 * KiB },
> +    { "ve",         0x01d00000, 1024 * KiB },
> +    { "mp",         0x01e80000, 128 * KiB },
> +    { "hdmi",       0x01ee0000, 128 * KiB },
> +    { "prcm",       0x01f01400, 1 * KiB },
> +    { "debug",      0x3f500000, 64 * KiB },
> +    { "cpubist",    0x3f501000, 4 * KiB },
> +    { "dcu",        0x3fff0000, 64 * KiB },
> +    { "hstmr",      0x01c60000, 4 * KiB },
> +    { "brom",       0xffff0000, 36 * KiB }
> +};
> +
> +/* Per Processor Interrupts */
> +enum {
> +    AW_R40_GIC_PPI_MAINT     =  9,
> +    AW_R40_GIC_PPI_HYPTIMER  = 10,
> +    AW_R40_GIC_PPI_VIRTTIMER = 11,
> +    AW_R40_GIC_PPI_SECTIMER  = 13,
> +    AW_R40_GIC_PPI_PHYSTIMER = 14
> +};
> +
> +/* Shared Processor Interrupts */
> +enum {
> +    AW_R40_GIC_SPI_UART0     =  1,
> +    AW_R40_GIC_SPI_UART1     =  2,
> +    AW_R40_GIC_SPI_UART2     =  3,
> +    AW_R40_GIC_SPI_UART3     =  4,
> +    AW_R40_GIC_SPI_TIMER0    = 22,
> +    AW_R40_GIC_SPI_TIMER1    = 23,
> +    AW_R40_GIC_SPI_MMC0      = 32,
> +    AW_R40_GIC_SPI_MMC1      = 33,
> +    AW_R40_GIC_SPI_MMC2      = 34,
> +    AW_R40_GIC_SPI_MMC3      = 35,
> +};
> +
> +/* Allwinner R40 general constants */
> +enum {
> +    AW_R40_GIC_NUM_SPI       = 128
> +};
> +
> +#define BOOT0_MAGIC             "eGON.BT0"
> +
> +/* The low 8-bits of the 'boot_media' field in the SPL header */
> +#define SUNXI_BOOTED_FROM_MMC0  0
> +#define SUNXI_BOOTED_FROM_NAND  1
> +#define SUNXI_BOOTED_FROM_MMC2  2
> +#define SUNXI_BOOTED_FROM_SPI   3
> +
> +struct boot_file_head {
> +    uint32_t            b_instruction;
> +    uint8_t             magic[8];
> +    uint32_t            check_sum;
> +    uint32_t            length;
> +    uint32_t            pub_head_size;
> +    uint32_t            fel_script_address;
> +    uint32_t            fel_uEnv_length;
> +    uint32_t            dt_name_offset;
> +    uint32_t            dram_size;
> +    uint32_t            boot_media;
> +    uint32_t            string_pool[13];
> +};
> +
> +bool allwinner_r40_bootrom_setup(AwR40State *s, BlockBackend *blk, int unit)
> +{
> +    const int64_t rom_size = 32 * KiB;
> +    g_autofree uint8_t *buffer = g_new0(uint8_t, rom_size);
> +    struct boot_file_head *head = (struct boot_file_head *)buffer;
> +
> +    if (blk_pread(blk, 8 * KiB, rom_size, buffer, 0) < 0) {
> +        error_setg(&error_fatal, "%s: failed to read BlockBackend data",
> +                   __func__);
> +        return false;
> +    }
> +
> +    /* we only check the magic string here. */
> +    if (memcmp(head->magic, BOOT0_MAGIC, sizeof(head->magic)))

Braces are missing.

> +        return false;
> +
> +    /* fix the boot media */

It would be good if more comments were added on why boot media is fixed.

> +    switch (unit) {

This would look nicer as if-else.

> +    case 2:
> +        head->boot_media = cpu_to_le32(SUNXI_BOOTED_FROM_MMC2);
> +        break;
> +    default:
> +        head->boot_media = cpu_to_le32(SUNXI_BOOTED_FROM_MMC0);
> +        break;
> +    }
> +
> +    rom_add_blob("allwinner-r40.bootrom", buffer, rom_size,
> +                  rom_size, s->memmap[AW_R40_DEV_SRAM_A1],
> +                  NULL, NULL, NULL, NULL, false);
> +    return true;
> +}
> +
> +static void allwinner_r40_init(Object *obj)
> +{
> +    AwR40State *s = AW_R40(obj);
> +
> +    s->memmap = allwinner_r40_memmap;
> +
> +    for (int i = 0; i < AW_R40_NUM_CPUS; i++) {
> +        object_initialize_child(obj, "cpu[*]", &s->cpus[i],
> +                                ARM_CPU_TYPE_NAME("cortex-a7"));
> +    }
> +
> +    object_initialize_child(obj, "gic", &s->gic, TYPE_ARM_GIC);
> +
> +    object_initialize_child(obj, "timer", &s->timer, TYPE_AW_A10_PIT);
> +    object_property_add_alias(obj, "clk0-freq", OBJECT(&s->timer),
> +                              "clk0-freq");
> +    object_property_add_alias(obj, "clk1-freq", OBJECT(&s->timer),
> +                              "clk1-freq");
> +
> +    object_initialize_child(obj, "mmc0", &s->mmc0, TYPE_AW_SDHOST_SUN5I);
> +    object_initialize_child(obj, "mmc1", &s->mmc1, TYPE_AW_SDHOST_SUN5I);
> +    object_initialize_child(obj, "mmc2", &s->mmc2, TYPE_AW_SDHOST_SUN5I);
> +    object_initialize_child(obj, "mmc3", &s->mmc3, TYPE_AW_SDHOST_SUN5I);
> +}
> +
> +static void allwinner_r40_realize(DeviceState *dev, Error **errp)
> +{
> +    AwR40State *s = AW_R40(dev);
> +    unsigned i;
> +
> +    /* CPUs */
> +    for (i = 0; i < AW_R40_NUM_CPUS; i++) {
> +
> +        /*
> +         * Disable secondary CPUs. Guest EL3 firmware will start
> +         * them via CPU reset control registers.
> +         */
> +        qdev_prop_set_bit(DEVICE(&s->cpus[i]), "start-powered-off",
> +                          i > 0);
> +
> +        /* All exception levels required */
> +        qdev_prop_set_bit(DEVICE(&s->cpus[i]), "has_el3", true);
> +        qdev_prop_set_bit(DEVICE(&s->cpus[i]), "has_el2", true);
> +
> +        /* Mark realized */
> +        qdev_realize(DEVICE(&s->cpus[i]), NULL, &error_fatal);
> +    }
> +
> +    /* Generic Interrupt Controller */
> +    qdev_prop_set_uint32(DEVICE(&s->gic), "num-irq", AW_R40_GIC_NUM_SPI +
> +                                                     GIC_INTERNAL);
> +    qdev_prop_set_uint32(DEVICE(&s->gic), "revision", 2);
> +    qdev_prop_set_uint32(DEVICE(&s->gic), "num-cpu", AW_R40_NUM_CPUS);
> +    qdev_prop_set_bit(DEVICE(&s->gic), "has-security-extensions", false);
> +    qdev_prop_set_bit(DEVICE(&s->gic), "has-virtualization-extensions", true);
> +    sysbus_realize(SYS_BUS_DEVICE(&s->gic), &error_fatal);
> +
> +    sysbus_mmio_map(SYS_BUS_DEVICE(&s->gic), 0, s->memmap[AW_R40_DEV_GIC_DIST]);
> +    sysbus_mmio_map(SYS_BUS_DEVICE(&s->gic), 1, s->memmap[AW_R40_DEV_GIC_CPU]);
> +    sysbus_mmio_map(SYS_BUS_DEVICE(&s->gic), 2, s->memmap[AW_R40_DEV_GIC_HYP]);
> +    sysbus_mmio_map(SYS_BUS_DEVICE(&s->gic), 3, s->memmap[AW_R40_DEV_GIC_VCPU]);
> +
> +    /*
> +     * Wire the outputs from each CPU's generic timer and the GICv3

I see that GIC revision is set to 2, but here it says GICv3. Maybe the
comment should be updated?

> +     * 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_R40_NUM_CPUS; i++) {
> +        DeviceState *cpudev = DEVICE(&s->cpus[i]);
> +        int ppibase = AW_R40_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_R40_GIC_PPI_PHYSTIMER,
> +            [GTIMER_VIRT] = AW_R40_GIC_PPI_VIRTTIMER,
> +            [GTIMER_HYP]  = AW_R40_GIC_PPI_HYPTIMER,
> +            [GTIMER_SEC]  = AW_R40_GIC_PPI_SECTIMER,
> +        };
> +
> +        /* Connect CPU timer outputs to GIC PPI inputs */
> +        for (irq = 0; irq < ARRAY_SIZE(timer_irq); irq++) {
> +            qdev_connect_gpio_out(cpudev, irq,
> +                                  qdev_get_gpio_in(DEVICE(&s->gic),
> +                                                   ppibase + timer_irq[irq]));
> +        }
> +
> +        /* Connect GIC outputs to CPU interrupt inputs */
> +        sysbus_connect_irq(SYS_BUS_DEVICE(&s->gic), i,
> +                           qdev_get_gpio_in(cpudev, ARM_CPU_IRQ));
> +        sysbus_connect_irq(SYS_BUS_DEVICE(&s->gic), i + AW_R40_NUM_CPUS,
> +                           qdev_get_gpio_in(cpudev, ARM_CPU_FIQ));
> +        sysbus_connect_irq(SYS_BUS_DEVICE(&s->gic), i + (2 * AW_R40_NUM_CPUS),
> +                           qdev_get_gpio_in(cpudev, ARM_CPU_VIRQ));
> +        sysbus_connect_irq(SYS_BUS_DEVICE(&s->gic), i + (3 * AW_R40_NUM_CPUS),
> +                           qdev_get_gpio_in(cpudev, ARM_CPU_VFIQ));
> +
> +        /* GIC maintenance signal */
> +        sysbus_connect_irq(SYS_BUS_DEVICE(&s->gic), i + (4 * AW_R40_NUM_CPUS),
> +                           qdev_get_gpio_in(DEVICE(&s->gic),
> +                                            ppibase + AW_R40_GIC_PPI_MAINT));
> +    }
> +
> +    /* Timer */
> +    sysbus_realize(SYS_BUS_DEVICE(&s->timer), &error_fatal);
> +    sysbus_mmio_map(SYS_BUS_DEVICE(&s->timer), 0, s->memmap[AW_R40_DEV_PIT]);
> +    sysbus_connect_irq(SYS_BUS_DEVICE(&s->timer), 0,
> +                       qdev_get_gpio_in(DEVICE(&s->gic), AW_R40_GIC_SPI_TIMER0));
> +    sysbus_connect_irq(SYS_BUS_DEVICE(&s->timer), 1,
> +                       qdev_get_gpio_in(DEVICE(&s->gic), AW_R40_GIC_SPI_TIMER1));
> +
> +    /* SRAM */
> +    memory_region_init_ram(&s->sram_a1, OBJECT(dev), "sram A1",
> +                            16 * KiB, &error_abort);
> +    memory_region_init_ram(&s->sram_a2, OBJECT(dev), "sram A2",
> +                            16 * KiB, &error_abort);
> +    memory_region_init_ram(&s->sram_a3, OBJECT(dev), "sram A3",
> +                            13 * KiB, &error_abort);
> +    memory_region_init_ram(&s->sram_a4, OBJECT(dev), "sram A4",
> +                            3 * KiB, &error_abort);
> +    memory_region_add_subregion(get_system_memory(), s->memmap[AW_R40_DEV_SRAM_A1],
> +                                &s->sram_a1);
> +    memory_region_add_subregion(get_system_memory(), s->memmap[AW_R40_DEV_SRAM_A2],
> +                                &s->sram_a2);
> +    memory_region_add_subregion(get_system_memory(), s->memmap[AW_R40_DEV_SRAM_A3],
> +                                &s->sram_a3);
> +    memory_region_add_subregion(get_system_memory(), s->memmap[AW_R40_DEV_SRAM_A4],
> +                                &s->sram_a4);
> +
> +    /* SD/MMC */
> +    object_property_set_link(OBJECT(&s->mmc0), "dma-memory",
> +                             OBJECT(get_system_memory()), &error_fatal);
> +    sysbus_realize(SYS_BUS_DEVICE(&s->mmc0), &error_fatal);
> +    sysbus_mmio_map(SYS_BUS_DEVICE(&s->mmc0), 0, s->memmap[AW_R40_DEV_MMC0]);
> +    sysbus_connect_irq(SYS_BUS_DEVICE(&s->mmc0), 0,
> +                       qdev_get_gpio_in(DEVICE(&s->gic), AW_R40_GIC_SPI_MMC0));
> +
> +    object_property_set_link(OBJECT(&s->mmc1), "dma-memory",
> +                             OBJECT(get_system_memory()), &error_fatal);
> +    sysbus_realize(SYS_BUS_DEVICE(&s->mmc1), &error_fatal);
> +    sysbus_mmio_map(SYS_BUS_DEVICE(&s->mmc1), 0, s->memmap[AW_R40_DEV_MMC1]);
> +    sysbus_connect_irq(SYS_BUS_DEVICE(&s->mmc1), 0,
> +                       qdev_get_gpio_in(DEVICE(&s->gic), AW_R40_DEV_MMC1));
> +
> +    object_property_set_link(OBJECT(&s->mmc2), "dma-memory",
> +                             OBJECT(get_system_memory()), &error_fatal);
> +    sysbus_realize(SYS_BUS_DEVICE(&s->mmc2), &error_fatal);
> +    sysbus_mmio_map(SYS_BUS_DEVICE(&s->mmc2), 0, s->memmap[AW_R40_DEV_MMC2]);
> +    sysbus_connect_irq(SYS_BUS_DEVICE(&s->mmc2), 0,
> +                       qdev_get_gpio_in(DEVICE(&s->gic), AW_R40_GIC_SPI_MMC2));
> +
> +    object_property_set_link(OBJECT(&s->mmc3), "dma-memory",
> +                             OBJECT(get_system_memory()), &error_fatal);
> +    sysbus_realize(SYS_BUS_DEVICE(&s->mmc3), &error_fatal);
> +    sysbus_mmio_map(SYS_BUS_DEVICE(&s->mmc3), 0, s->memmap[AW_R40_DEV_MMC3]);
> +    sysbus_connect_irq(SYS_BUS_DEVICE(&s->mmc3), 0,
> +                       qdev_get_gpio_in(DEVICE(&s->gic), AW_R40_GIC_SPI_MMC3));
> +
> +    /* UART0. For future clocktree API: All UARTS are connected to APB2_CLK. */
> +    serial_mm_init(get_system_memory(), s->memmap[AW_R40_DEV_UART0], 2,
> +                   qdev_get_gpio_in(DEVICE(&s->gic), AW_R40_GIC_SPI_UART0),
> +                   115200, serial_hd(0), DEVICE_NATIVE_ENDIAN);
> +
> +    /* Unimplemented devices */
> +    for (i = 0; i < ARRAY_SIZE(r40_unimplemented); i++) {
> +        create_unimplemented_device(r40_unimplemented[i].device_name,
> +                                    r40_unimplemented[i].base,
> +                                    r40_unimplemented[i].size);
> +    }
> +}
> +
> +static void allwinner_r40_class_init(ObjectClass *oc, void *data)
> +{
> +    DeviceClass *dc = DEVICE_CLASS(oc);
> +
> +    dc->realize = allwinner_r40_realize;
> +    /* Reason: uses serial_hd() in realize function */
> +    dc->user_creatable = false;
> +}
> +
> +static const TypeInfo allwinner_r40_type_info = {
> +    .name = TYPE_AW_R40,
> +    .parent = TYPE_DEVICE,
> +    .instance_size = sizeof(AwR40State),
> +    .instance_init = allwinner_r40_init,
> +    .class_init = allwinner_r40_class_init,
> +};
> +
> +static void allwinner_r40_register_types(void)
> +{
> +    type_register_static(&allwinner_r40_type_info);
> +}
> +
> +type_init(allwinner_r40_register_types)
> diff --git a/hw/arm/bananapi_m2u.c b/hw/arm/bananapi_m2u.c
> new file mode 100644
> index 0000000000..1b6241719d
> --- /dev/null
> +++ b/hw/arm/bananapi_m2u.c
> @@ -0,0 +1,116 @@
> +/*
> + * Bananapi M2U emulation
> + *
> + * Copyright (C) 2023 qianfan Zhao <qianfanguijin@163.com>
> + *
> + * This program is free software: you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation, either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program.  If not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#include "qemu/osdep.h"
> +#include "qemu/units.h"
> +#include "exec/address-spaces.h"
> +#include "qapi/error.h"

Missing #include "qemu/error-report.h"

> +#include "hw/boards.h"
> +#include "hw/qdev-properties.h"
> +#include "hw/arm/allwinner-r40.h"
> +
> +static struct arm_boot_info bpim2u_binfo;
> +
> +static void mmc_attach_drive(AwR40State *s, AwSdHostState *mmc, int unit,
> +                             bool load_bootroom, bool *bootroom_loaded)

A comment for this function would be useful.

> +{
> +    DriveInfo *di = drive_get(IF_SD, 0, unit);
> +    BlockBackend *blk = di ? blk_by_legacy_dinfo(di) : NULL;
> +    BusState *bus;
> +    DeviceState *carddev;
> +
> +    bus = qdev_get_child_bus(DEVICE(mmc), "sd-bus");
> +    if (bus == NULL) {
> +        error_report("No SD bus found in SOC object");
> +        exit(1);
> +    }
> +
> +    carddev = qdev_new(TYPE_SD_CARD);
> +    qdev_prop_set_drive_err(carddev, "drive", blk, &error_fatal);
> +    qdev_realize_and_unref(carddev, bus, &error_fatal);
> +
> +    if (load_bootroom && blk && blk_is_available(blk)) {
> +        /* Use Boot ROM to copy data from SD card to SRAM */
> +        *bootroom_loaded = allwinner_r40_bootrom_setup(s, blk, unit);
> +    }
> +}
> +
> +static void bpim2u_init(MachineState *machine)
> +{
> +    bool bootroom_loaded = false;
> +    AwR40State *r40;
> +    I2CBus *i2c;
> +
> +    /* BIOS is not supported by this board */
> +    if (machine->firmware) {
> +        error_report("BIOS not supported for this machine");
> +        exit(1);
> +    }
> +
> +    /* Only allow Cortex-A7 for this board */
> +    if (strcmp(machine->cpu_type, ARM_CPU_TYPE_NAME("cortex-a7")) != 0) {
> +        error_report("This board can only be used with cortex-a7 CPU");
> +        exit(1);
> +    }
> +
> +    r40 = AW_R40(object_new(TYPE_AW_R40));
> +    object_property_add_child(OBJECT(machine), "soc", OBJECT(r40));
> +    object_unref(OBJECT(r40));
> +
> +    /* Setup timer properties */
> +    object_property_set_int(OBJECT(r40), "clk0-freq", 32768, &error_abort);
> +    object_property_set_int(OBJECT(r40), "clk1-freq", 24 * 1000 * 1000,
> +                            &error_abort);
> +
> +    /* Mark R40 object realized */
> +    qdev_realize(DEVICE(r40), NULL, &error_abort);
> +
> +    /* Plug in SD card and try load bootrom, R40 can boot from mmc0 and mmc2 */
> +    mmc_attach_drive(r40, &r40->mmc0, 0,
> +                     !machine->kernel_filename && !bootroom_loaded,
> +                     &bootroom_loaded);
> +    mmc_attach_drive(r40, &r40->mmc1, 1, false, NULL);
> +    mmc_attach_drive(r40, &r40->mmc2, 2,
> +                     !machine->kernel_filename && !bootroom_loaded,
> +                     &bootroom_loaded);
> +    mmc_attach_drive(r40, &r40->mmc3, 3, false, NULL);
> +
> +    /* SDRAM */
> +    memory_region_add_subregion(get_system_memory(), r40->memmap[AW_R40_DEV_SDRAM],
> +                                machine->ram);
> +
> +    bpim2u_binfo.loader_start = r40->memmap[AW_R40_DEV_SDRAM];
> +    bpim2u_binfo.ram_size = machine->ram_size;
> +    bpim2u_binfo.psci_conduit = QEMU_PSCI_CONDUIT_SMC;
> +    arm_load_kernel(ARM_CPU(first_cpu), machine, &bpim2u_binfo);
> +}
> +
> +static void bpim2u_machine_init(MachineClass *mc)
> +{
> +    mc->desc = "Bananapi M2U (Cortex-A7)";
> +    mc->init = bpim2u_init;
> +    mc->min_cpus = AW_R40_NUM_CPUS;
> +    mc->max_cpus = AW_R40_NUM_CPUS;
> +    mc->default_cpus = AW_R40_NUM_CPUS;
> +    mc->default_cpu_type = ARM_CPU_TYPE_NAME("cortex-a7");
> +    mc->default_ram_size = 1 * GiB;
> +    mc->default_ram_id = "bpim2u.ram";
> +}
> +
> +DEFINE_MACHINE("bpim2u", bpim2u_machine_init)
> diff --git a/hw/arm/meson.build b/hw/arm/meson.build
> index b545ba0e4f..870ec67376 100644
> --- a/hw/arm/meson.build
> +++ b/hw/arm/meson.build
> @@ -37,6 +37,7 @@ arm_ss.add(when: 'CONFIG_OMAP', if_true: files('omap1.c', 'omap2.c'))
>  arm_ss.add(when: 'CONFIG_STRONGARM', if_true: files('strongarm.c'))
>  arm_ss.add(when: 'CONFIG_ALLWINNER_A10', if_true: files('allwinner-a10.c', 'cubieboard.c'))
>  arm_ss.add(when: 'CONFIG_ALLWINNER_H3', if_true: files('allwinner-h3.c', 'orangepi.c'))
> +arm_ss.add(when: 'CONFIG_ALLWINNER_R40', if_true: files('allwinner-r40.c', 'bananapi_m2u.c'))
>  arm_ss.add(when: 'CONFIG_RASPI', if_true: files('bcm2836.c', 'raspi.c'))
>  arm_ss.add(when: 'CONFIG_STM32F100_SOC', if_true: files('stm32f100_soc.c'))
>  arm_ss.add(when: 'CONFIG_STM32F205_SOC', if_true: files('stm32f205_soc.c'))
> diff --git a/include/hw/arm/allwinner-r40.h b/include/hw/arm/allwinner-r40.h
> new file mode 100644
> index 0000000000..46bda2c54e
> --- /dev/null
> +++ b/include/hw/arm/allwinner-r40.h
> @@ -0,0 +1,111 @@
> +/*
> + * Allwinner R40/A40i/T3 System on Chip emulation
> + *
> + * Copyright (C) 2023 qianfan Zhao <qianfanguijin@163.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_R40_H
> +#define HW_ARM_ALLWINNER_R40_H
> +
> +#include "qom/object.h"
> +#include "hw/arm/boot.h"
> +#include "hw/timer/allwinner-a10-pit.h"
> +#include "hw/intc/arm_gic.h"
> +#include "hw/sd/allwinner-sdhost.h"
> +#include "target/arm/cpu.h"
> +#include "sysemu/block-backend.h"
> +
> +enum {
> +    AW_R40_DEV_SRAM_A1,
> +    AW_R40_DEV_SRAM_A2,
> +    AW_R40_DEV_SRAM_A3,
> +    AW_R40_DEV_SRAM_A4,
> +    AW_R40_DEV_MMC0,
> +    AW_R40_DEV_MMC1,
> +    AW_R40_DEV_MMC2,
> +    AW_R40_DEV_MMC3,
> +    AW_R40_DEV_CCU,
> +    AW_R40_DEV_PIT,
> +    AW_R40_DEV_UART0,
> +    AW_R40_DEV_GIC_DIST,
> +    AW_R40_DEV_GIC_CPU,
> +    AW_R40_DEV_GIC_HYP,
> +    AW_R40_DEV_GIC_VCPU,
> +    AW_R40_DEV_SDRAM
> +};
> +
> +#define AW_R40_NUM_CPUS      (4)
> +
> +/**
> + * Allwinner R40 object model
> + * @{
> + */
> +
> +/** Object type for the Allwinner R40 SoC */
> +#define TYPE_AW_R40 "allwinner-r40"
> +
> +/** Convert input object to Allwinner R40 state object */
> +OBJECT_DECLARE_SIMPLE_TYPE(AwR40State, AW_R40)
> +
> +/** @} */
> +
> +/**
> + * Allwinner R40 object
> + *
> + * This struct contains the state of all the devices
> + * which are currently emulated by the R40 SoC code.
> + */
> +struct AwR40State {
> +    /*< private >*/
> +    DeviceState parent_obj;
> +    /*< public >*/
> +
> +    ARMCPU cpus[AW_R40_NUM_CPUS];
> +    const hwaddr *memmap;
> +    AwA10PITState timer;
> +    AwSdHostState mmc0;
> +    AwSdHostState mmc1;
> +    AwSdHostState mmc2;
> +    AwSdHostState mmc3;

I think this would look nicer if it were an array.

Best regards,
Strahinja

> +    GICState gic;
> +    MemoryRegion sram_a1;
> +    MemoryRegion sram_a2;
> +    MemoryRegion sram_a3;
> +    MemoryRegion sram_a4;
> +};
> +
> +/**
> + * Emulate Boot ROM firmware setup functionality.
> + *
> + * A real Allwinner R40 SoC contains a Boot ROM
> + * which is the first code that runs right after
> + * the SoC is powered on. The Boot ROM is responsible
> + * for loading user code (e.g. a bootloader) from any
> + * of the supported external devices and writing the
> + * downloaded code to internal SRAM. After loading the SoC
> + * begins executing the code written to SRAM.
> + *
> + * This function emulates the Boot ROM by copying 32 KiB
> + * of data from the given block device and writes it to
> + * the start of the first internal SRAM memory.
> + *
> + * @s: Allwinner R40 state object pointer
> + * @blk: Block backend device object pointer
> + * @unit: the mmc control's unit
> + */
> +bool allwinner_r40_bootrom_setup(AwR40State *s, BlockBackend *blk, int unit);
> +
> +#endif /* HW_ARM_ALLWINNER_R40_H */
> --
> 2.25.1
>


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

* Re: [PATCH v1 02/11] hw/arm/allwinner-r40: add Clock Control Unit
  2023-03-21 10:25 ` [PATCH v1 02/11] hw/arm/allwinner-r40: add Clock Control Unit qianfanguijin
@ 2023-03-25 21:12   ` Strahinja Jankovic
  0 siblings, 0 replies; 17+ messages in thread
From: Strahinja Jankovic @ 2023-03-25 21:12 UTC (permalink / raw)
  To: qianfanguijin
  Cc: qemu-arm, qemu-devel, Peter Maydell, Beniamino Galvani,
	Philippe Mathieu-Daudé,
	Niek Linnenbank

Hi,

On Tue, Mar 21, 2023 at 11:25 AM <qianfanguijin@163.com> wrote:
>
> From: qianfan Zhao <qianfanguijin@163.com>
>
> The CCU provides the registers to program the PLLs and the controls
> most of the clock generation, division, distribution, synchronization
> and gating.
>
> This commit adds support for the Clock Control Unit which emulates
> a simple read/write register interface.
>
> Signed-off-by: qianfan Zhao <qianfanguijin@163.com>
> ---
>  hw/arm/allwinner-r40.c              |   7 +-
>  hw/misc/allwinner-r40-ccu.c         | 207 ++++++++++++++++++++++++++++
>  hw/misc/meson.build                 |   1 +
>  include/hw/arm/allwinner-r40.h      |   2 +
>  include/hw/misc/allwinner-r40-ccu.h |  65 +++++++++
>  5 files changed, 281 insertions(+), 1 deletion(-)
>  create mode 100644 hw/misc/allwinner-r40-ccu.c
>  create mode 100644 include/hw/misc/allwinner-r40-ccu.h
>
> diff --git a/hw/arm/allwinner-r40.c b/hw/arm/allwinner-r40.c
> index d0516f4e96..3517682aed 100644
> --- a/hw/arm/allwinner-r40.c
> +++ b/hw/arm/allwinner-r40.c
> @@ -42,6 +42,7 @@ const hwaddr allwinner_r40_memmap[] = {
>      [AW_R40_DEV_MMC1]       = 0x01c10000,
>      [AW_R40_DEV_MMC2]       = 0x01c11000,
>      [AW_R40_DEV_MMC3]       = 0x01c12000,
> +    [AW_R40_DEV_CCU]        = 0x01c20000,
>      [AW_R40_DEV_PIT]        = 0x01c20c00,
>      [AW_R40_DEV_UART0]      = 0x01c28000,
>      [AW_R40_DEV_GIC_DIST]   = 0x01c81000,
> @@ -80,7 +81,6 @@ static struct AwR40Unimplemented r40_unimplemented[] = {
>      { "usb2-host",  0x01c1c000, 4 * KiB },
>      { "cs1",        0x01c1d000, 4 * KiB },
>      { "spi3",       0x01c1f000, 4 * KiB },
> -    { "ccu",        0x01c20000, 1 * KiB },
>      { "rtc",        0x01c20400, 1 * KiB },
>      { "pio",        0x01c20800, 1 * KiB },
>      { "owa",        0x01c21000, 1 * KiB },
> @@ -246,6 +246,7 @@ static void allwinner_r40_init(Object *obj)
>      object_property_add_alias(obj, "clk1-freq", OBJECT(&s->timer),
>                                "clk1-freq");
>
> +    object_initialize_child(obj, "ccu", &s->ccu, TYPE_AW_R40_CCU);
>      object_initialize_child(obj, "mmc0", &s->mmc0, TYPE_AW_SDHOST_SUN5I);
>      object_initialize_child(obj, "mmc1", &s->mmc1, TYPE_AW_SDHOST_SUN5I);
>      object_initialize_child(obj, "mmc2", &s->mmc2, TYPE_AW_SDHOST_SUN5I);
> @@ -358,6 +359,10 @@ static void allwinner_r40_realize(DeviceState *dev, Error **errp)
>      memory_region_add_subregion(get_system_memory(), s->memmap[AW_R40_DEV_SRAM_A4],
>                                  &s->sram_a4);
>
> +    /* Clock Control Unit */
> +    sysbus_realize(SYS_BUS_DEVICE(&s->ccu), &error_fatal);
> +    sysbus_mmio_map(SYS_BUS_DEVICE(&s->ccu), 0, s->memmap[AW_R40_DEV_CCU]);
> +
>      /* SD/MMC */
>      object_property_set_link(OBJECT(&s->mmc0), "dma-memory",
>                               OBJECT(get_system_memory()), &error_fatal);
> diff --git a/hw/misc/allwinner-r40-ccu.c b/hw/misc/allwinner-r40-ccu.c
> new file mode 100644
> index 0000000000..0abe006874
> --- /dev/null
> +++ b/hw/misc/allwinner-r40-ccu.c
> @@ -0,0 +1,207 @@
> +/*
> + * Allwinner R40 Clock Control Unit emulation
> + *
> + * Copyright (C) 2023 qianfan Zhao <qianfanguijin@163.com>
> + *
> + * This program is free software: you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation, either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program.  If not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#include "qemu/osdep.h"
> +#include "qemu/units.h"
> +#include "hw/sysbus.h"
> +#include "migration/vmstate.h"
> +#include "qemu/log.h"
> +#include "qemu/module.h"
> +#include "hw/misc/allwinner-r40-ccu.h"
> +
> +/* CCU register offsets */
> +enum {
> +    REG_PLL_CPUX_CTRL           = 0x0000,
> +    REG_PLL_AUDIO_CTRL          = 0x0008,
> +    REG_PLL_VIDEO0_CTRL         = 0x0010,
> +    REG_PLL_VE_CTRL             = 0x0018,
> +    REG_PLL_DDR0_CTRL           = 0x0020,
> +    REG_PLL_PERIPH0_CTRL        = 0x0028,
> +    REG_PLL_PERIPH1_CTRL        = 0x002c,
> +    REG_PLL_VIDEO1_CTRL         = 0x0030,
> +    REG_PLL_SATA_CTRL           = 0x0034,
> +    REG_PLL_GPU_CTRL            = 0x0038,
> +    REG_PLL_MIPI_CTRL           = 0x0040,
> +    REG_PLL_DE_CTRL             = 0x0048,
> +    REG_PLL_DDR1_CTRL           = 0x004c,
> +    REG_AHB1_APB1_CFG           = 0x0054,
> +    REG_APB2_CFG                = 0x0058,
> +    REG_MMC0_CLK                = 0x0088,
> +    REG_MMC1_CLK                = 0x008c,
> +    REG_MMC2_CLK                = 0x0090,
> +    REG_MMC3_CLK                = 0x0094,
> +    REG_USBPHY_CFG              = 0x00cc,
> +    REG_PLL_DDR_AUX             = 0x00f0,
> +    REG_DRAM_CFG                = 0x00f4,
> +    REG_PLL_DDR1_CFG            = 0x00f8,
> +    REG_DRAM_CLK_GATING         = 0x0100,
> +    REG_GMAC_CLK                = 0x0164,
> +    REG_SYS_32K_CLK             = 0x0310,
> +    REG_PLL_LOCK_CTRL           = 0x0320,
> +};
> +
> +#define REG_INDEX(offset)    (offset / sizeof(uint32_t))
> +
> +/* CCU register flags */
> +enum {
> +    REG_PLL_ENABLE           = (1 << 31),
> +    REG_PLL_LOCK             = (1 << 28),
> +};
> +
> +static uint64_t allwinner_r40_ccu_read(void *opaque, hwaddr offset,
> +                                       unsigned size)
> +{
> +    const AwR40ClockCtlState *s = AW_R40_CCU(opaque);
> +    const uint32_t idx = REG_INDEX(offset);
> +
> +    switch (offset) {

Perhaps, for completeness, it would be good to have all defined macros
here under cases, as well as the special case where register read is
unimplemented, like you have in write function.

> +    case 0x324 ... AW_R40_CCU_IOSIZE:
> +        qemu_log_mask(LOG_GUEST_ERROR, "%s: out-of-bounds offset 0x%04x\n",
> +                      __func__, (uint32_t)offset);
> +        return 0;
> +    }
> +
> +    return s->regs[idx];
> +}
> +
> +static void allwinner_r40_ccu_write(void *opaque, hwaddr offset,
> +                                    uint64_t val, unsigned size)
> +{
> +    AwR40ClockCtlState *s = AW_R40_CCU(opaque);
> +
> +    switch (offset) {
> +    case REG_DRAM_CFG:    /* DRAM Configuration(for DDR0) */
> +        /* bit16: SDRCLK_UPD (SDRCLK configuration 0 update) */
> +        val &= ~(1 << 16);

Please create a macro for this bitmask instead of comment.

> +        break;
> +    case REG_PLL_DDR1_CTRL: /* DDR1 Control register */
> +        /* bit30: SDRPLL_UPD */
> +        val &= ~(1 << 30);

Same here.

> +        if (val & REG_PLL_ENABLE)

Braces again (you will see all suggestions when you run the checkpatch.pl).

> +            val |= REG_PLL_LOCK;
> +        break;
> +    case REG_PLL_CPUX_CTRL:
> +    case REG_PLL_AUDIO_CTRL:
> +    case REG_PLL_VE_CTRL:
> +    case REG_PLL_VIDEO0_CTRL:
> +    case REG_PLL_DDR0_CTRL:
> +    case REG_PLL_PERIPH0_CTRL:
> +    case REG_PLL_PERIPH1_CTRL:
> +    case REG_PLL_VIDEO1_CTRL:
> +    case REG_PLL_SATA_CTRL:
> +    case REG_PLL_GPU_CTRL:
> +    case REG_PLL_MIPI_CTRL:
> +    case REG_PLL_DE_CTRL:
> +        if (val & REG_PLL_ENABLE)
> +            val |= REG_PLL_LOCK;
> +        break;
> +    case 0x324 ... AW_R40_CCU_IOSIZE:
> +        qemu_log_mask(LOG_GUEST_ERROR, "%s: out-of-bounds offset 0x%04x\n",
> +                      __func__, (uint32_t)offset);
> +        break;
> +    default:
> +        qemu_log_mask(LOG_UNIMP, "%s: unimplemented write offset 0x%04x\n",
> +                      __func__, (uint32_t)offset);
> +        break;
> +    }
> +
> +    s->regs[REG_INDEX(offset)] = (uint32_t) val;
> +}
> +
> +static const MemoryRegionOps allwinner_r40_ccu_ops = {
> +    .read = allwinner_r40_ccu_read,
> +    .write = allwinner_r40_ccu_write,
> +    .endianness = DEVICE_NATIVE_ENDIAN,
> +    .valid = {
> +        .min_access_size = 4,
> +        .max_access_size = 4,
> +    },
> +    .impl.min_access_size = 4,
> +};
> +
> +static void allwinner_r40_ccu_reset(DeviceState *dev)
> +{
> +    AwR40ClockCtlState *s = AW_R40_CCU(dev);
> +
> +    memset(s->regs, 0, sizeof(s->regs));
> +
> +    /* Set default values for registers */
> +    s->regs[REG_INDEX(REG_PLL_CPUX_CTRL)]       = 0x00001000;
> +    s->regs[REG_INDEX(REG_PLL_AUDIO_CTRL)]      = 0x00035514;
> +    s->regs[REG_INDEX(REG_PLL_VIDEO0_CTRL)]     = 0x03006207;
> +    s->regs[REG_INDEX(REG_PLL_VE_CTRL)]         = 0x03006207;
> +    s->regs[REG_INDEX(REG_PLL_DDR0_CTRL)]       = 0x00001000,
> +    s->regs[REG_INDEX(REG_PLL_PERIPH0_CTRL)]    = 0x00041811;
> +    s->regs[REG_INDEX(REG_PLL_PERIPH1_CTRL)]    = 0x00041811;
> +    s->regs[REG_INDEX(REG_PLL_VIDEO1_CTRL)]     = 0x03006207;
> +    s->regs[REG_INDEX(REG_PLL_SATA_CTRL)]       = 0x00001811;
> +    s->regs[REG_INDEX(REG_PLL_GPU_CTRL)]        = 0x03006207;
> +    s->regs[REG_INDEX(REG_PLL_MIPI_CTRL)]       = 0x00000515;
> +    s->regs[REG_INDEX(REG_PLL_DE_CTRL)]         = 0x03006207;
> +    s->regs[REG_INDEX(REG_PLL_DDR1_CTRL)]       = 0x00001800;
> +    s->regs[REG_INDEX(REG_AHB1_APB1_CFG)]       = 0x00001010;
> +    s->regs[REG_INDEX(REG_APB2_CFG)]            = 0x01000000;
> +    s->regs[REG_INDEX(REG_PLL_DDR_AUX)]         = 0x00000001;
> +    s->regs[REG_INDEX(REG_PLL_DDR1_CFG)]        = 0x0ccca000;
> +    s->regs[REG_INDEX(REG_SYS_32K_CLK)]         = 0x0000000f;
> +}
> +
> +static void allwinner_r40_ccu_init(Object *obj)
> +{
> +    SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
> +    AwR40ClockCtlState *s = AW_R40_CCU(obj);
> +
> +    /* Memory mapping */
> +    memory_region_init_io(&s->iomem, OBJECT(s), &allwinner_r40_ccu_ops, s,
> +                          TYPE_AW_R40_CCU, AW_R40_CCU_IOSIZE);
> +    sysbus_init_mmio(sbd, &s->iomem);
> +}
> +
> +static const VMStateDescription allwinner_r40_ccu_vmstate = {
> +    .name = "allwinner-r40-ccu",
> +    .version_id = 1,
> +    .minimum_version_id = 1,
> +    .fields = (VMStateField[]) {
> +        VMSTATE_UINT32_ARRAY(regs, AwR40ClockCtlState, AW_R40_CCU_REGS_NUM),
> +        VMSTATE_END_OF_LIST()
> +    }
> +};
> +
> +static void allwinner_r40_ccu_class_init(ObjectClass *klass, void *data)
> +{
> +    DeviceClass *dc = DEVICE_CLASS(klass);
> +
> +    dc->reset = allwinner_r40_ccu_reset;

I would guess that all new implementations should use the 'three-phase reset'?

Best regards,
Strahinja


> +    dc->vmsd = &allwinner_r40_ccu_vmstate;
> +}
> +
> +static const TypeInfo allwinner_r40_ccu_info = {
> +    .name          = TYPE_AW_R40_CCU,
> +    .parent        = TYPE_SYS_BUS_DEVICE,
> +    .instance_init = allwinner_r40_ccu_init,
> +    .instance_size = sizeof(AwR40ClockCtlState),
> +    .class_init    = allwinner_r40_ccu_class_init,
> +};
> +
> +static void allwinner_r40_ccu_register(void)
> +{
> +    type_register_static(&allwinner_r40_ccu_info);
> +}
> +
> +type_init(allwinner_r40_ccu_register)
> diff --git a/hw/misc/meson.build b/hw/misc/meson.build
> index a40245ad44..96e35f1cdb 100644
> --- a/hw/misc/meson.build
> +++ b/hw/misc/meson.build
> @@ -44,6 +44,7 @@ specific_ss.add(when: 'CONFIG_ALLWINNER_H3', if_true: files('allwinner-cpucfg.c'
>  softmmu_ss.add(when: 'CONFIG_ALLWINNER_H3', if_true: files('allwinner-h3-dramc.c'))
>  softmmu_ss.add(when: 'CONFIG_ALLWINNER_H3', if_true: files('allwinner-h3-sysctrl.c'))
>  softmmu_ss.add(when: 'CONFIG_ALLWINNER_H3', if_true: files('allwinner-sid.c'))
> +softmmu_ss.add(when: 'CONFIG_ALLWINNER_R40', if_true: files('allwinner-r40-ccu.c'))
>  softmmu_ss.add(when: 'CONFIG_AXP209_PMU', if_true: files('axp209.c'))
>  softmmu_ss.add(when: 'CONFIG_REALVIEW', if_true: files('arm_sysctl.c'))
>  softmmu_ss.add(when: 'CONFIG_NSERIES', if_true: files('cbus.c'))
> diff --git a/include/hw/arm/allwinner-r40.h b/include/hw/arm/allwinner-r40.h
> index 46bda2c54e..b355af2c4f 100644
> --- a/include/hw/arm/allwinner-r40.h
> +++ b/include/hw/arm/allwinner-r40.h
> @@ -25,6 +25,7 @@
>  #include "hw/timer/allwinner-a10-pit.h"
>  #include "hw/intc/arm_gic.h"
>  #include "hw/sd/allwinner-sdhost.h"
> +#include "hw/misc/allwinner-r40-ccu.h"
>  #include "target/arm/cpu.h"
>  #include "sysemu/block-backend.h"
>
> @@ -80,6 +81,7 @@ struct AwR40State {
>      AwSdHostState mmc1;
>      AwSdHostState mmc2;
>      AwSdHostState mmc3;
> +    AwR40ClockCtlState ccu;
>      GICState gic;
>      MemoryRegion sram_a1;
>      MemoryRegion sram_a2;
> diff --git a/include/hw/misc/allwinner-r40-ccu.h b/include/hw/misc/allwinner-r40-ccu.h
> new file mode 100644
> index 0000000000..ceb74eff92
> --- /dev/null
> +++ b/include/hw/misc/allwinner-r40-ccu.h
> @@ -0,0 +1,65 @@
> +/*
> + * Allwinner R40 Clock Control Unit emulation
> + *
> + * Copyright (C) 2023 qianfan Zhao <qianfanguijin@163.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_R40_CCU_H
> +#define HW_MISC_ALLWINNER_R40_CCU_H
> +
> +#include "qom/object.h"
> +#include "hw/sysbus.h"
> +
> +/**
> + * @name Constants
> + * @{
> + */
> +
> +/** Size of register I/O address space used by CCU device */
> +#define AW_R40_CCU_IOSIZE        (0x400)
> +
> +/** Total number of known registers */
> +#define AW_R40_CCU_REGS_NUM      (AW_R40_CCU_IOSIZE / sizeof(uint32_t))
> +
> +/** @} */
> +
> +/**
> + * @name Object model
> + * @{
> + */
> +
> +#define TYPE_AW_R40_CCU    "allwinner-r40-ccu"
> +OBJECT_DECLARE_SIMPLE_TYPE(AwR40ClockCtlState, AW_R40_CCU)
> +
> +/** @} */
> +
> +/**
> + * Allwinner R40 CCU object instance state.
> + */
> +struct AwR40ClockCtlState {
> +    /*< private >*/
> +    SysBusDevice parent_obj;
> +    /*< public >*/
> +
> +    /** Maps I/O registers in physical memory */
> +    MemoryRegion iomem;
> +
> +    /** Array of hardware registers */
> +    uint32_t regs[AW_R40_CCU_REGS_NUM];
> +
> +};
> +
> +#endif /* HW_MISC_ALLWINNER_R40_CCU_H */
> --
> 2.25.1
>


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

* Re: [PATCH v1 03/11] hw: allwinner-r40: Complete uart devices
  2023-03-21 10:25 ` [PATCH v1 03/11] hw: allwinner-r40: Complete uart devices qianfanguijin
@ 2023-03-25 21:15   ` Strahinja Jankovic
  0 siblings, 0 replies; 17+ messages in thread
From: Strahinja Jankovic @ 2023-03-25 21:15 UTC (permalink / raw)
  To: qianfanguijin
  Cc: qemu-arm, qemu-devel, Peter Maydell, Beniamino Galvani,
	Philippe Mathieu-Daudé,
	Niek Linnenbank

Hi,

On Tue, Mar 21, 2023 at 11:25 AM <qianfanguijin@163.com> wrote:
>
> From: qianfan Zhao <qianfanguijin@163.com>
>
> R40 has eight UARTs, support both 16450 and 16550 compatible modes.
>
> Signed-off-by: qianfan Zhao <qianfanguijin@163.com>
> ---
>  hw/arm/allwinner-r40.c         | 32 ++++++++++++++++++++++++++++++++
>  include/hw/arm/allwinner-r40.h |  7 +++++++
>  2 files changed, 39 insertions(+)
>
> diff --git a/hw/arm/allwinner-r40.c b/hw/arm/allwinner-r40.c
> index 3517682aed..fde01783b1 100644
> --- a/hw/arm/allwinner-r40.c
> +++ b/hw/arm/allwinner-r40.c
> @@ -45,6 +45,13 @@ const hwaddr allwinner_r40_memmap[] = {
>      [AW_R40_DEV_CCU]        = 0x01c20000,
>      [AW_R40_DEV_PIT]        = 0x01c20c00,
>      [AW_R40_DEV_UART0]      = 0x01c28000,
> +    [AW_R40_DEV_UART1]      = 0x01c28400,
> +    [AW_R40_DEV_UART2]      = 0x01c28800,
> +    [AW_R40_DEV_UART3]      = 0x01c28c00,
> +    [AW_R40_DEV_UART4]      = 0x01c29000,
> +    [AW_R40_DEV_UART5]      = 0x01c29400,
> +    [AW_R40_DEV_UART6]      = 0x01c29800,
> +    [AW_R40_DEV_UART7]      = 0x01c29c00,
>      [AW_R40_DEV_GIC_DIST]   = 0x01c81000,
>      [AW_R40_DEV_GIC_CPU]    = 0x01c82000,
>      [AW_R40_DEV_GIC_HYP]    = 0x01c84000,
> @@ -160,6 +167,10 @@ enum {
>      AW_R40_GIC_SPI_UART1     =  2,
>      AW_R40_GIC_SPI_UART2     =  3,
>      AW_R40_GIC_SPI_UART3     =  4,
> +    AW_R40_GIC_SPI_UART4     = 17,
> +    AW_R40_GIC_SPI_UART5     = 18,
> +    AW_R40_GIC_SPI_UART6     = 19,
> +    AW_R40_GIC_SPI_UART7     = 20,
>      AW_R40_GIC_SPI_TIMER0    = 22,
>      AW_R40_GIC_SPI_TIMER1    = 23,
>      AW_R40_GIC_SPI_MMC0      = 32,
> @@ -396,6 +407,27 @@ static void allwinner_r40_realize(DeviceState *dev, Error **errp)
>      serial_mm_init(get_system_memory(), s->memmap[AW_R40_DEV_UART0], 2,
>                     qdev_get_gpio_in(DEVICE(&s->gic), AW_R40_GIC_SPI_UART0),
>                     115200, serial_hd(0), DEVICE_NATIVE_ENDIAN);
> +    serial_mm_init(get_system_memory(), s->memmap[AW_R40_DEV_UART1], 2,
> +                   qdev_get_gpio_in(DEVICE(&s->gic), AW_R40_GIC_SPI_UART1),
> +                   115200, serial_hd(1), DEVICE_NATIVE_ENDIAN);
> +    serial_mm_init(get_system_memory(), s->memmap[AW_R40_DEV_UART2], 2,
> +                   qdev_get_gpio_in(DEVICE(&s->gic), AW_R40_GIC_SPI_UART2),
> +                   115200, serial_hd(2), DEVICE_NATIVE_ENDIAN);
> +    serial_mm_init(get_system_memory(), s->memmap[AW_R40_DEV_UART3], 2,
> +                   qdev_get_gpio_in(DEVICE(&s->gic), AW_R40_GIC_SPI_UART3),
> +                   115200, serial_hd(3), DEVICE_NATIVE_ENDIAN);
> +    serial_mm_init(get_system_memory(), s->memmap[AW_R40_DEV_UART4], 2,
> +                   qdev_get_gpio_in(DEVICE(&s->gic), AW_R40_GIC_SPI_UART4),
> +                   115200, serial_hd(4), DEVICE_NATIVE_ENDIAN);
> +    serial_mm_init(get_system_memory(), s->memmap[AW_R40_DEV_UART5], 2,
> +                   qdev_get_gpio_in(DEVICE(&s->gic), AW_R40_GIC_SPI_UART5),
> +                   115200, serial_hd(5), DEVICE_NATIVE_ENDIAN);
> +    serial_mm_init(get_system_memory(), s->memmap[AW_R40_DEV_UART6], 2,
> +                   qdev_get_gpio_in(DEVICE(&s->gic), AW_R40_GIC_SPI_UART6),
> +                   115200, serial_hd(6), DEVICE_NATIVE_ENDIAN);
> +    serial_mm_init(get_system_memory(), s->memmap[AW_R40_DEV_UART7], 2,
> +                   qdev_get_gpio_in(DEVICE(&s->gic), AW_R40_GIC_SPI_UART7),
> +                   115200, serial_hd(7), DEVICE_NATIVE_ENDIAN);
>
>      /* Unimplemented devices */
>      for (i = 0; i < ARRAY_SIZE(r40_unimplemented); i++) {
> diff --git a/include/hw/arm/allwinner-r40.h b/include/hw/arm/allwinner-r40.h
> index b355af2c4f..dfb5eb609c 100644
> --- a/include/hw/arm/allwinner-r40.h
> +++ b/include/hw/arm/allwinner-r40.h
> @@ -41,6 +41,13 @@ enum {
>      AW_R40_DEV_CCU,
>      AW_R40_DEV_PIT,
>      AW_R40_DEV_UART0,
> +    AW_R40_DEV_UART1,
> +    AW_R40_DEV_UART2,
> +    AW_R40_DEV_UART3,
> +    AW_R40_DEV_UART4,
> +    AW_R40_DEV_UART5,
> +    AW_R40_DEV_UART6,
> +    AW_R40_DEV_UART7,
>      AW_R40_DEV_GIC_DIST,
>      AW_R40_DEV_GIC_CPU,
>      AW_R40_DEV_GIC_HYP,
> --
> 2.25.1
>

Reviewed-by: Strahinja Jankovic <strahinja.p.jankovic@gmail.com>


Best regards,
Strahinja


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

* Re: [PATCH v1 04/11] hw: arm: allwinner-r40: Add 5 TWI controllers
  2023-03-21 10:25 ` [PATCH v1 04/11] hw: arm: allwinner-r40: Add 5 TWI controllers qianfanguijin
@ 2023-03-25 21:21   ` Strahinja Jankovic
  0 siblings, 0 replies; 17+ messages in thread
From: Strahinja Jankovic @ 2023-03-25 21:21 UTC (permalink / raw)
  To: qianfanguijin
  Cc: qemu-arm, qemu-devel, Peter Maydell, Beniamino Galvani,
	Philippe Mathieu-Daudé,
	Niek Linnenbank

Hi,

On Tue, Mar 21, 2023 at 11:25 AM <qianfanguijin@163.com> wrote:
>
> From: qianfan Zhao <qianfanguijin@163.com>
>
> TWI(i2c) is designed to be used as an interface between CPU host and the
> serial 2-Wire bus. It can support all standard 2-Wire transfer, can be
> operated in standard mode(100kbit/s) or fast-mode, supporting data rate
> up to 400kbit/s.
>
> Signed-off-by: qianfan Zhao <qianfanguijin@163.com>
> ---
>  hw/arm/allwinner-r40.c         | 47 ++++++++++++++++++++++++++++++----
>  include/hw/arm/allwinner-r40.h | 11 ++++++++
>  2 files changed, 53 insertions(+), 5 deletions(-)
>
> diff --git a/hw/arm/allwinner-r40.c b/hw/arm/allwinner-r40.c
> index fde01783b1..9fa23e1f33 100644
> --- a/hw/arm/allwinner-r40.c
> +++ b/hw/arm/allwinner-r40.c
> @@ -52,6 +52,11 @@ const hwaddr allwinner_r40_memmap[] = {
>      [AW_R40_DEV_UART5]      = 0x01c29400,
>      [AW_R40_DEV_UART6]      = 0x01c29800,
>      [AW_R40_DEV_UART7]      = 0x01c29c00,
> +    [AW_R40_DEV_TWI0]       = 0x01c2ac00,
> +    [AW_R40_DEV_TWI1]       = 0x01c2b000,
> +    [AW_R40_DEV_TWI2]       = 0x01c2b400,
> +    [AW_R40_DEV_TWI3]       = 0x01c2b800,
> +    [AW_R40_DEV_TWI4]       = 0x01c2c000,
>      [AW_R40_DEV_GIC_DIST]   = 0x01c81000,
>      [AW_R40_DEV_GIC_CPU]    = 0x01c82000,
>      [AW_R40_DEV_GIC_HYP]    = 0x01c84000,
> @@ -115,11 +120,6 @@ static struct AwR40Unimplemented r40_unimplemented[] = {
>      { "uart7",      0x01c29c00, 1 * KiB },
>      { "ps20",       0x01c2a000, 1 * KiB },
>      { "ps21",       0x01c2a400, 1 * KiB },
> -    { "twi0",       0x01c2ac00, 1 * KiB },
> -    { "twi1",       0x01c2b000, 1 * KiB },
> -    { "twi2",       0x01c2b400, 1 * KiB },
> -    { "twi3",       0x01c2b800, 1 * KiB },
> -    { "twi4",       0x01c2c000, 1 * KiB },
>      { "scr",        0x01c2c400, 1 * KiB },
>      { "tvd-top",    0x01c30000, 4 * KiB },
>      { "tvd0",       0x01c31000, 4 * KiB },
> @@ -167,6 +167,9 @@ enum {
>      AW_R40_GIC_SPI_UART1     =  2,
>      AW_R40_GIC_SPI_UART2     =  3,
>      AW_R40_GIC_SPI_UART3     =  4,
> +    AW_R40_GIC_SPI_TWI0      =  7,
> +    AW_R40_GIC_SPI_TWI1      =  8,
> +    AW_R40_GIC_SPI_TWI2      =  9,
>      AW_R40_GIC_SPI_UART4     = 17,
>      AW_R40_GIC_SPI_UART5     = 18,
>      AW_R40_GIC_SPI_UART6     = 19,
> @@ -177,6 +180,8 @@ enum {
>      AW_R40_GIC_SPI_MMC1      = 33,
>      AW_R40_GIC_SPI_MMC2      = 34,
>      AW_R40_GIC_SPI_MMC3      = 35,
> +    AW_R40_GIC_SPI_TWI3      = 88,
> +    AW_R40_GIC_SPI_TWI4      = 89,
>  };
>
>  /* Allwinner R40 general constants */
> @@ -262,6 +267,12 @@ static void allwinner_r40_init(Object *obj)
>      object_initialize_child(obj, "mmc1", &s->mmc1, TYPE_AW_SDHOST_SUN5I);
>      object_initialize_child(obj, "mmc2", &s->mmc2, TYPE_AW_SDHOST_SUN5I);
>      object_initialize_child(obj, "mmc3", &s->mmc3, TYPE_AW_SDHOST_SUN5I);
> +
> +    object_initialize_child(obj, "twi0", &s->i2c0, TYPE_AW_I2C_SUN6I);
> +    object_initialize_child(obj, "twi1", &s->i2c1, TYPE_AW_I2C_SUN6I);
> +    object_initialize_child(obj, "twi2", &s->i2c2, TYPE_AW_I2C_SUN6I);
> +    object_initialize_child(obj, "twi3", &s->i2c3, TYPE_AW_I2C_SUN6I);
> +    object_initialize_child(obj, "twi4", &s->i2c4, TYPE_AW_I2C_SUN6I);
>  }
>
>  static void allwinner_r40_realize(DeviceState *dev, Error **errp)
> @@ -429,6 +440,32 @@ static void allwinner_r40_realize(DeviceState *dev, Error **errp)
>                     qdev_get_gpio_in(DEVICE(&s->gic), AW_R40_GIC_SPI_UART7),
>                     115200, serial_hd(7), DEVICE_NATIVE_ENDIAN);
>
> +    /* I2C */
> +    sysbus_realize(SYS_BUS_DEVICE(&s->i2c0), &error_fatal);
> +    sysbus_mmio_map(SYS_BUS_DEVICE(&s->i2c0), 0, s->memmap[AW_R40_DEV_TWI0]);
> +    sysbus_connect_irq(SYS_BUS_DEVICE(&s->i2c0), 0,
> +                       qdev_get_gpio_in(DEVICE(&s->gic), AW_R40_GIC_SPI_TWI0));
> +
> +    sysbus_realize(SYS_BUS_DEVICE(&s->i2c1), &error_fatal);
> +    sysbus_mmio_map(SYS_BUS_DEVICE(&s->i2c1), 0, s->memmap[AW_R40_DEV_TWI1]);
> +    sysbus_connect_irq(SYS_BUS_DEVICE(&s->i2c1), 0,
> +                       qdev_get_gpio_in(DEVICE(&s->gic), AW_R40_GIC_SPI_TWI1));
> +
> +    sysbus_realize(SYS_BUS_DEVICE(&s->i2c2), &error_fatal);
> +    sysbus_mmio_map(SYS_BUS_DEVICE(&s->i2c2), 0, s->memmap[AW_R40_DEV_TWI2]);
> +    sysbus_connect_irq(SYS_BUS_DEVICE(&s->i2c2), 0,
> +                       qdev_get_gpio_in(DEVICE(&s->gic), AW_R40_GIC_SPI_TWI2));
> +
> +    sysbus_realize(SYS_BUS_DEVICE(&s->i2c3), &error_fatal);
> +    sysbus_mmio_map(SYS_BUS_DEVICE(&s->i2c3), 0, s->memmap[AW_R40_DEV_TWI3]);
> +    sysbus_connect_irq(SYS_BUS_DEVICE(&s->i2c3), 0,
> +                       qdev_get_gpio_in(DEVICE(&s->gic), AW_R40_GIC_SPI_TWI3));
> +
> +    sysbus_realize(SYS_BUS_DEVICE(&s->i2c4), &error_fatal);
> +    sysbus_mmio_map(SYS_BUS_DEVICE(&s->i2c4), 0, s->memmap[AW_R40_DEV_TWI4]);
> +    sysbus_connect_irq(SYS_BUS_DEVICE(&s->i2c4), 0,
> +                       qdev_get_gpio_in(DEVICE(&s->gic), AW_R40_GIC_SPI_TWI4));
> +
>      /* Unimplemented devices */
>      for (i = 0; i < ARRAY_SIZE(r40_unimplemented); i++) {
>          create_unimplemented_device(r40_unimplemented[i].device_name,
> diff --git a/include/hw/arm/allwinner-r40.h b/include/hw/arm/allwinner-r40.h
> index dfb5eb609c..6a7e5c1e31 100644
> --- a/include/hw/arm/allwinner-r40.h
> +++ b/include/hw/arm/allwinner-r40.h
> @@ -26,6 +26,7 @@
>  #include "hw/intc/arm_gic.h"
>  #include "hw/sd/allwinner-sdhost.h"
>  #include "hw/misc/allwinner-r40-ccu.h"
> +#include "hw/i2c/allwinner-i2c.h"
>  #include "target/arm/cpu.h"
>  #include "sysemu/block-backend.h"
>
> @@ -48,6 +49,11 @@ enum {
>      AW_R40_DEV_UART5,
>      AW_R40_DEV_UART6,
>      AW_R40_DEV_UART7,
> +    AW_R40_DEV_TWI0,
> +    AW_R40_DEV_TWI1,
> +    AW_R40_DEV_TWI2,
> +    AW_R40_DEV_TWI3,
> +    AW_R40_DEV_TWI4,
>      AW_R40_DEV_GIC_DIST,
>      AW_R40_DEV_GIC_CPU,
>      AW_R40_DEV_GIC_HYP,
> @@ -89,6 +95,11 @@ struct AwR40State {
>      AwSdHostState mmc2;
>      AwSdHostState mmc3;
>      AwR40ClockCtlState ccu;
> +    AWI2CState i2c0;
> +    AWI2CState i2c1;
> +    AWI2CState i2c2;
> +    AWI2CState i2c3;
> +    AWI2CState i2c4;

I am not sure what is the benefit of having all 5 I2C controllers
emulated? Unlike UARTs, where a user can decide at runtime how many
will be used, for I2C the decision is made at compile time.
Thus, I would suggest only creating those that have peripherals that
are emulated attached to them.

Best regards,
Strahinja


>      GICState gic;
>      MemoryRegion sram_a1;
>      MemoryRegion sram_a2;
> --
> 2.25.1
>


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

* Re: [PATCH v1 05/11] hw/misc: AXP221 PMU Emulation
  2023-03-21 10:25 ` [PATCH v1 05/11] hw/misc: AXP221 PMU Emulation qianfanguijin
@ 2023-03-25 21:25   ` Strahinja Jankovic
  0 siblings, 0 replies; 17+ messages in thread
From: Strahinja Jankovic @ 2023-03-25 21:25 UTC (permalink / raw)
  To: qianfanguijin
  Cc: qemu-arm, qemu-devel, Peter Maydell, Beniamino Galvani,
	Philippe Mathieu-Daudé,
	Niek Linnenbank

Hi,

On Tue, Mar 21, 2023 at 11:25 AM <qianfanguijin@163.com> wrote:
>
> From: qianfan Zhao <qianfanguijin@163.com>
>
> This patch adds minimal support for AXP-221 PMU and connect it to
> bananapi M2U board.
>
> Signed-off-by: qianfan Zhao <qianfanguijin@163.com>

As I wrote in the RFC patch, I would suggest renaming the axp209.c
file to axp2xx_pmu.c and extending it with support for AXP-221.
The difference in emulated AXP-209 and AXP-221 is only in registers,
so they can share the functions.

Best regards,
Strahinja


> ---
>  hw/arm/Kconfig        |   1 +
>  hw/arm/bananapi_m2u.c |   5 ++
>  hw/misc/Kconfig       |   4 +
>  hw/misc/axp221.c      | 196 ++++++++++++++++++++++++++++++++++++++++++
>  hw/misc/meson.build   |   1 +
>  hw/misc/trace-events  |   5 ++
>  6 files changed, 212 insertions(+)
>  create mode 100644 hw/misc/axp221.c
>
> diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig
> index 9e14c3427e..cf8fb083f8 100644
> --- a/hw/arm/Kconfig
> +++ b/hw/arm/Kconfig
> @@ -347,6 +347,7 @@ config ALLWINNER_H3
>  config ALLWINNER_R40
>      bool
>      select ALLWINNER_A10_PIT
> +    select AXP221_PMU
>      select SERIAL
>      select ARM_TIMER
>      select ARM_GIC
> diff --git a/hw/arm/bananapi_m2u.c b/hw/arm/bananapi_m2u.c
> index 1b6241719d..bdee12efd3 100644
> --- a/hw/arm/bananapi_m2u.c
> +++ b/hw/arm/bananapi_m2u.c
> @@ -22,6 +22,7 @@
>  #include "exec/address-spaces.h"
>  #include "qapi/error.h"
>  #include "hw/boards.h"
> +#include "hw/i2c/i2c.h"
>  #include "hw/qdev-properties.h"
>  #include "hw/arm/allwinner-r40.h"
>
> @@ -91,6 +92,10 @@ static void bpim2u_init(MachineState *machine)
>                       &bootroom_loaded);
>      mmc_attach_drive(r40, &r40->mmc3, 3, false, NULL);
>
> +    /* Connect AXP221 */
> +    i2c = I2C_BUS(qdev_get_child_bus(DEVICE(&r40->i2c0), "i2c"));
> +    i2c_slave_create_simple(i2c, "axp221_pmu", 0x34);
> +
>      /* SDRAM */
>      memory_region_add_subregion(get_system_memory(), r40->memmap[AW_R40_DEV_SDRAM],
>                                  machine->ram);
> diff --git a/hw/misc/Kconfig b/hw/misc/Kconfig
> index 2ef5781ef8..f66ac390b1 100644
> --- a/hw/misc/Kconfig
> +++ b/hw/misc/Kconfig
> @@ -180,4 +180,8 @@ config AXP209_PMU
>      bool
>      depends on I2C
>
> +config AXP221_PMU
> +    bool
> +    depends on I2C
> +
>  source macio/Kconfig
> diff --git a/hw/misc/axp221.c b/hw/misc/axp221.c
> new file mode 100644
> index 0000000000..47784bb085
> --- /dev/null
> +++ b/hw/misc/axp221.c
> @@ -0,0 +1,196 @@
> +/*
> + * AXP-221/221s PMU Emulation
> + *
> + * Copyright (C) 2023 qianfan Zhao <qianfanguijin@163.com>
> + *
> + * Permission is hereby granted, free of charge, to any person obtaining a
> + * copy of this software and associated documentation files (the "Software"),
> + * to deal in the Software without restriction, including without limitation
> + * the rights to use, copy, modify, merge, publish, distribute, sublicense,
> + * and/or sell copies of the Software, and to permit persons to whom the
> + * Software is furnished to do so, subject to the following conditions:
> + *
> + * The above copyright notice and this permission notice shall be included in
> + * all copies or substantial portions of the Software.
> + *
> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
> + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
> + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
> + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
> + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
> + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
> + * DEALINGS IN THE SOFTWARE.
> + *
> + * SPDX-License-Identifier: MIT
> + */
> +
> +#include "qemu/osdep.h"
> +#include "qemu/log.h"
> +#include "qemu/bitops.h"
> +#include "trace.h"
> +#include "hw/i2c/i2c.h"
> +#include "migration/vmstate.h"
> +
> +#define TYPE_AXP221_PMU "axp221_pmu"
> +
> +#define AXP221(obj) \
> +    OBJECT_CHECK(AXP221I2CState, (obj), TYPE_AXP221_PMU)
> +
> +#define NR_REGS                         0xff
> +
> +/* A simple I2C slave which returns values of ID or CNT register. */
> +typedef struct AXP221I2CState {
> +    /*< private >*/
> +    I2CSlave i2c;
> +    /*< public >*/
> +    uint8_t regs[NR_REGS];  /* peripheral registers */
> +    uint8_t ptr;            /* current register index */
> +    uint8_t count;          /* counter used for tx/rx */
> +} AXP221I2CState;
> +
> +#define AXP221_PWR_STATUS_ACIN_PRESENT          BIT(7)
> +#define AXP221_PWR_STATUS_ACIN_AVAIL            BIT(6)
> +#define AXP221_PWR_STATUS_VBUS_PRESENT          BIT(5)
> +#define AXP221_PWR_STATUS_VBUS_USED             BIT(4)
> +#define AXP221_PWR_STATUS_BAT_CHARGING          BIT(2)
> +#define AXP221_PWR_STATUS_ACIN_VBUS_POWERED     BIT(1)
> +
> +/* Reset all counters and load ID register */
> +static void axp221_reset_enter(Object *obj, ResetType type)
> +{
> +    AXP221I2CState *s = AXP221(obj);
> +
> +    memset(s->regs, 0, NR_REGS);
> +    s->ptr = 0;
> +    s->count = 0;
> +
> +    /* input power status register */
> +    s->regs[0x00] = AXP221_PWR_STATUS_ACIN_PRESENT
> +                    | AXP221_PWR_STATUS_ACIN_AVAIL
> +                    | AXP221_PWR_STATUS_ACIN_VBUS_POWERED;
> +
> +    s->regs[0x01] = 0x00; /* no battery is connected */
> +
> +    /* CHIPID register, no documented on datasheet, but it is checked in
> +     * u-boot spl. I had read it from AXP221s and got 0x06 value.
> +     * So leave 06h here.
> +     */
> +    s->regs[0x03] = 0x06;
> +
> +    s->regs[0x10] = 0xbf;
> +    s->regs[0x13] = 0x01;
> +    s->regs[0x30] = 0x60;
> +    s->regs[0x31] = 0x03;
> +    s->regs[0x32] = 0x43;
> +    s->regs[0x33] = 0xc6;
> +    s->regs[0x34] = 0x45;
> +    s->regs[0x35] = 0x0e;
> +    s->regs[0x36] = 0x5d;
> +    s->regs[0x37] = 0x08;
> +    s->regs[0x38] = 0xa5;
> +    s->regs[0x39] = 0x1f;
> +    s->regs[0x3c] = 0xfc;
> +    s->regs[0x3d] = 0x16;
> +    s->regs[0x80] = 0x80;
> +    s->regs[0x82] = 0xe0;
> +    s->regs[0x84] = 0x32;
> +    s->regs[0x8f] = 0x01;
> +
> +    s->regs[0x90] = 0x07;
> +    s->regs[0x91] = 0x1f;
> +    s->regs[0x92] = 0x07;
> +    s->regs[0x93] = 0x1f;
> +
> +    s->regs[0x40] = 0xd8;
> +    s->regs[0x41] = 0xff;
> +    s->regs[0x42] = 0x03;
> +    s->regs[0x43] = 0x03;
> +
> +    s->regs[0xb8] = 0xc0;
> +    s->regs[0xb9] = 0x64;
> +    s->regs[0xe6] = 0xa0;
> +}
> +
> +/* Handle events from master. */
> +static int axp221_event(I2CSlave *i2c, enum i2c_event event)
> +{
> +    AXP221I2CState *s = AXP221(i2c);
> +
> +    s->count = 0;
> +
> +    return 0;
> +}
> +
> +/* Called when master requests read */
> +static uint8_t axp221_rx(I2CSlave *i2c)
> +{
> +    AXP221I2CState *s = AXP221(i2c);
> +    uint8_t ret = 0xff;
> +
> +    if (s->ptr < NR_REGS) {
> +        ret = s->regs[s->ptr];
> +        trace_axp221_rx(s->ptr, ret);
> +        s->ptr++;
> +    }
> +
> +    return ret;
> +}
> +
> +/*
> + * Called when master sends write.
> + * Update ptr with byte 0, then perform write with second byte.
> + */
> +static int axp221_tx(I2CSlave *i2c, uint8_t data)
> +{
> +    AXP221I2CState *s = AXP221(i2c);
> +
> +    if (s->count == 0) {
> +        /* Store register address */
> +        s->ptr = data;
> +        s->count++;
> +        trace_axp221_select(data);
> +    } else {
> +        trace_axp221_tx(s->ptr, data);
> +        s->regs[s->ptr++] = data;
> +    }
> +
> +    return 0;
> +}
> +
> +static const VMStateDescription vmstate_axp221 = {
> +    .name = TYPE_AXP221_PMU,
> +    .version_id = 1,
> +    .fields = (VMStateField[]) {
> +        VMSTATE_UINT8_ARRAY(regs, AXP221I2CState, NR_REGS),
> +        VMSTATE_UINT8(count, AXP221I2CState),
> +        VMSTATE_UINT8(ptr, AXP221I2CState),
> +        VMSTATE_END_OF_LIST()
> +    }
> +};
> +
> +static void axp221_class_init(ObjectClass *oc, void *data)
> +{
> +    DeviceClass *dc = DEVICE_CLASS(oc);
> +    I2CSlaveClass *isc = I2C_SLAVE_CLASS(oc);
> +    ResettableClass *rc = RESETTABLE_CLASS(oc);
> +
> +    rc->phases.enter = axp221_reset_enter;
> +    dc->vmsd = &vmstate_axp221;
> +    isc->event = axp221_event;
> +    isc->recv = axp221_rx;
> +    isc->send = axp221_tx;
> +}
> +
> +static const TypeInfo axp221_info = {
> +    .name = TYPE_AXP221_PMU,
> +    .parent = TYPE_I2C_SLAVE,
> +    .instance_size = sizeof(AXP221I2CState),
> +    .class_init = axp221_class_init
> +};
> +
> +static void axp221_register_devices(void)
> +{
> +    type_register_static(&axp221_info);
> +}
> +
> +type_init(axp221_register_devices);
> diff --git a/hw/misc/meson.build b/hw/misc/meson.build
> index 96e35f1cdb..d407a2df50 100644
> --- a/hw/misc/meson.build
> +++ b/hw/misc/meson.build
> @@ -46,6 +46,7 @@ softmmu_ss.add(when: 'CONFIG_ALLWINNER_H3', if_true: files('allwinner-h3-sysctrl
>  softmmu_ss.add(when: 'CONFIG_ALLWINNER_H3', if_true: files('allwinner-sid.c'))
>  softmmu_ss.add(when: 'CONFIG_ALLWINNER_R40', if_true: files('allwinner-r40-ccu.c'))
>  softmmu_ss.add(when: 'CONFIG_AXP209_PMU', if_true: files('axp209.c'))
> +softmmu_ss.add(when: 'CONFIG_AXP221_PMU', if_true: files('axp221.c'))
>  softmmu_ss.add(when: 'CONFIG_REALVIEW', if_true: files('arm_sysctl.c'))
>  softmmu_ss.add(when: 'CONFIG_NSERIES', if_true: files('cbus.c'))
>  softmmu_ss.add(when: 'CONFIG_ECCMEMCTL', if_true: files('eccmemctl.c'))
> diff --git a/hw/misc/trace-events b/hw/misc/trace-events
> index c47876a902..63b072d2d8 100644
> --- a/hw/misc/trace-events
> +++ b/hw/misc/trace-events
> @@ -28,6 +28,11 @@ axp209_rx(uint8_t reg, uint8_t data) "Read reg 0x%" PRIx8 " : 0x%" PRIx8
>  axp209_select(uint8_t reg) "Accessing reg 0x%" PRIx8
>  axp209_tx(uint8_t reg, uint8_t data) "Write reg 0x%" PRIx8 " : 0x%" PRIx8
>
> +# axp221.c
> +axp221_rx(uint8_t reg, uint8_t data) "Read reg 0x%" PRIx8 " : 0x%" PRIx8
> +axp221_select(uint8_t reg) "Accessing reg 0x%" PRIx8
> +axp221_tx(uint8_t reg, uint8_t data) "Write reg 0x%" PRIx8 " : 0x%" PRIx8
> +
>  # 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"
> --
> 2.25.1
>


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

end of thread, other threads:[~2023-03-25 21:26 UTC | newest]

Thread overview: 17+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-03-21 10:24 [PATCH v1 00/11] *** add allwinner-r40 support *** qianfanguijin
2023-03-21 10:25 ` [PATCH v1 01/11] hw: arm: Add bananapi M2-Ultra and allwinner-r40 support qianfanguijin
2023-03-25 20:59   ` Strahinja Jankovic
2023-03-21 10:25 ` [PATCH v1 02/11] hw/arm/allwinner-r40: add Clock Control Unit qianfanguijin
2023-03-25 21:12   ` Strahinja Jankovic
2023-03-21 10:25 ` [PATCH v1 03/11] hw: allwinner-r40: Complete uart devices qianfanguijin
2023-03-25 21:15   ` Strahinja Jankovic
2023-03-21 10:25 ` [PATCH v1 04/11] hw: arm: allwinner-r40: Add 5 TWI controllers qianfanguijin
2023-03-25 21:21   ` Strahinja Jankovic
2023-03-21 10:25 ` [PATCH v1 05/11] hw/misc: AXP221 PMU Emulation qianfanguijin
2023-03-25 21:25   ` Strahinja Jankovic
2023-03-21 10:25 ` [PATCH v1 06/11] hw/arm/allwinner-r40: add SDRAM controller device qianfanguijin
2023-03-21 10:25 ` [PATCH v1 07/11] hw: sd: allwinner-sdhost: Add sun50i-a64 SoC support qianfanguijin
2023-03-21 10:25 ` [PATCH v1 08/11] hw: arm: allwinner-r40: Fix the mmc controller's type qianfanguijin
2023-03-21 10:25 ` [PATCH v1 09/11] hw: arm: allwinner-r40: Add emac and gmac support qianfanguijin
2023-03-21 10:25 ` [PATCH v1 10/11] tests: avocado: boot_linux_console: Add test case for bpim2u qianfanguijin
2023-03-25 14:36 ` [PATCH v1 00/11] *** add allwinner-r40 support *** Strahinja Jankovic

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