qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v4 00/11] *** Add allwinner r40 support ***
@ 2023-05-10 10:29 qianfanguijin
  2023-05-10 10:29 ` [PATCH v4 01/11] hw: arm: Add bananapi M2-Ultra and allwinner-r40 support qianfanguijin
                   ` (10 more replies)
  0 siblings, 11 replies; 31+ messages in thread
From: qianfanguijin @ 2023-05-10 10:29 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

# v2: 2023-03-28

1. Fix the waring and error reported by checkpatch.pl
2. Remove the other i2c controllers except that i2c0
3. Use an array to register mmc and uart devices
4. Rename axp209 to axp22x and add axp221 support
5. Add a basic SRAM controller

# v3: 2023-04-18

1. Update some commit messages
2. Squash those two commit about sdcard
   hw: sd: allwinner-sdhost: Add sun50i-a64 SoC support
   hw: arm: allwinner-r40: Fix the mmc controller's type

# v4: 2023-05-10

1. Rebase to master

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 i2c0 device
  hw/misc: Rename axp209 to axp22x and add support AXP221 PMU
  hw/arm/allwinner-r40: add SDRAM controller device
  hw: sd: allwinner-sdhost: Add sun50i-a64 SoC support
  hw: arm: allwinner-r40: Add emac and gmac support
  hw: arm: allwinner-sramc: Add SRAM Controller support for R40
  tests: avocado: boot_linux_console: Add test case for bpim2u
  docs: system: arm: Introduce bananapi_m2u

 docs/system/arm/bananapi_m2u.rst      | 138 +++++++
 hw/arm/Kconfig                        |  14 +-
 hw/arm/allwinner-r40.c                | 526 ++++++++++++++++++++++++++
 hw/arm/bananapi_m2u.c                 | 145 +++++++
 hw/arm/meson.build                    |   1 +
 hw/misc/Kconfig                       |   5 +-
 hw/misc/allwinner-r40-ccu.c           | 209 ++++++++++
 hw/misc/allwinner-r40-dramc.c         | 513 +++++++++++++++++++++++++
 hw/misc/allwinner-sramc.c             | 184 +++++++++
 hw/misc/axp209.c                      | 238 ------------
 hw/misc/axp2xx.c                      | 283 ++++++++++++++
 hw/misc/meson.build                   |   5 +-
 hw/misc/trace-events                  |  26 +-
 hw/sd/allwinner-sdhost.c              |  70 +++-
 include/hw/arm/allwinner-r40.h        | 143 +++++++
 include/hw/misc/allwinner-r40-ccu.h   |  65 ++++
 include/hw/misc/allwinner-r40-dramc.h | 108 ++++++
 include/hw/misc/allwinner-sramc.h     |  69 ++++
 include/hw/sd/allwinner-sdhost.h      |   9 +
 tests/avocado/boot_linux_console.py   | 176 +++++++++
 20 files changed, 2679 insertions(+), 248 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/allwinner-sramc.c
 delete mode 100644 hw/misc/axp209.c
 create mode 100644 hw/misc/axp2xx.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
 create mode 100644 include/hw/misc/allwinner-sramc.h

-- 
2.25.1



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

* [PATCH v4 01/11] hw: arm: Add bananapi M2-Ultra and allwinner-r40 support
  2023-05-10 10:29 [PATCH v4 00/11] *** Add allwinner r40 support *** qianfanguijin
@ 2023-05-10 10:29 ` qianfanguijin
  2023-05-14 18:38   ` Niek Linnenbank
  2023-05-14 18:50   ` Niek Linnenbank
  2023-05-10 10:29 ` [PATCH v4 02/11] hw/arm/allwinner-r40: add Clock Control Unit qianfanguijin
                   ` (9 subsequent siblings)
  10 siblings, 2 replies; 31+ messages in thread
From: qianfanguijin @ 2023-05-10 10:29 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).

Signed-off-by: qianfan Zhao <qianfanguijin@163.com>
---
 hw/arm/Kconfig                 |  10 +
 hw/arm/allwinner-r40.c         | 418 +++++++++++++++++++++++++++++++++
 hw/arm/bananapi_m2u.c          | 129 ++++++++++
 hw/arm/meson.build             |   1 +
 include/hw/arm/allwinner-r40.h | 110 +++++++++
 5 files changed, 668 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/hw/arm/Kconfig b/hw/arm/Kconfig
index 2d7c457955..b7a84f6e3f 100644
--- a/hw/arm/Kconfig
+++ b/hw/arm/Kconfig
@@ -374,6 +374,16 @@ config ALLWINNER_H3
     select USB_EHCI_SYSBUS
     select SD
 
+config ALLWINNER_R40
+    bool
+    default y if TCG && ARM
+    select ALLWINNER_A10_PIT
+    select SERIAL
+    select ARM_TIMER
+    select ARM_GIC
+    select UNIMP
+    select SD
+
 config RASPI
     bool
     default y if TCG && ARM
diff --git a/hw/arm/allwinner-r40.c b/hw/arm/allwinner-r40.c
new file mode 100644
index 0000000000..b743d64253
--- /dev/null
+++ b/hw/arm/allwinner-r40.c
@@ -0,0 +1,418 @@
+/*
+ * 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;
+    }
+
+    /*
+     * Simulate the behavior of the bootROM, it will change the boot_media
+     * flag to indicate where the chip is booting from. R40 can boot from
+     * mmc0 or mmc2, the default value of boot_media is zero
+     * (SUNXI_BOOTED_FROM_MMC0), let's fix this flag when it is booting from
+     * the others.
+     */
+    if (unit == 2) {
+        head->boot_media = cpu_to_le32(SUNXI_BOOTED_FROM_MMC2);
+    } else {
+        head->boot_media = cpu_to_le32(SUNXI_BOOTED_FROM_MMC0);
+    }
+
+    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)
+{
+    static const char *mmc_names[AW_R40_NUM_MMCS] = {
+        "mmc0", "mmc1", "mmc2", "mmc3"
+    };
+    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");
+
+    for (int i = 0; i < AW_R40_NUM_MMCS; i++) {
+        object_initialize_child(obj, mmc_names[i], &s->mmc[i],
+                                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 GICv2
+     * 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 */
+    for (int i = 0; i < AW_R40_NUM_MMCS; i++) {
+        qemu_irq irq = qdev_get_gpio_in(DEVICE(&s->gic),
+                                        AW_R40_GIC_SPI_MMC0 + i);
+        const hwaddr addr = s->memmap[AW_R40_DEV_MMC0 + i];
+
+        object_property_set_link(OBJECT(&s->mmc[i]), "dma-memory",
+                                 OBJECT(get_system_memory()), &error_fatal);
+        sysbus_realize(SYS_BUS_DEVICE(&s->mmc[i]), &error_fatal);
+        sysbus_mmio_map(SYS_BUS_DEVICE(&s->mmc[i]), 0, addr);
+        sysbus_connect_irq(SYS_BUS_DEVICE(&s->mmc[i]), 0, irq);
+    }
+
+    /* 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..1d49a006b5
--- /dev/null
+++ b/hw/arm/bananapi_m2u.c
@@ -0,0 +1,129 @@
+/*
+ * 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 "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;
+
+/*
+ * R40 can boot from mmc0 and mmc2, and bpim2u has two mmc interface, one is
+ * connected to sdcard and another mount an emmc media.
+ * Attach the mmc driver and try loading bootloader.
+ */
+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;
+
+    /* 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 has 4 mmc controllers but can
+     * only booting from mmc0 and mmc2.
+     */
+    for (int i = 0; i < AW_R40_NUM_MMCS; i++) {
+        switch (i) {
+        case 0:
+        case 2:
+            mmc_attach_drive(r40, &r40->mmc[i], i,
+                             !machine->kernel_filename && !bootroom_loaded,
+                             &bootroom_loaded);
+            break;
+        default:
+            mmc_attach_drive(r40, &r40->mmc[i], i, false, NULL);
+            break;
+        }
+    }
+
+    /* 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..348bf25d6b
--- /dev/null
+++ b/include/hw/arm/allwinner-r40.h
@@ -0,0 +1,110 @@
+/*
+ * 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.
+ */
+#define AW_R40_NUM_MMCS         4
+
+struct AwR40State {
+    /*< private >*/
+    DeviceState parent_obj;
+    /*< public >*/
+
+    ARMCPU cpus[AW_R40_NUM_CPUS];
+    const hwaddr *memmap;
+    AwA10PITState timer;
+    AwSdHostState mmc[AW_R40_NUM_MMCS];
+    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] 31+ messages in thread

* [PATCH v4 02/11] hw/arm/allwinner-r40: add Clock Control Unit
  2023-05-10 10:29 [PATCH v4 00/11] *** Add allwinner r40 support *** qianfanguijin
  2023-05-10 10:29 ` [PATCH v4 01/11] hw: arm: Add bananapi M2-Ultra and allwinner-r40 support qianfanguijin
@ 2023-05-10 10:29 ` qianfanguijin
  2023-05-14 18:40   ` Niek Linnenbank
  2023-05-10 10:29 ` [PATCH v4 03/11] hw: allwinner-r40: Complete uart devices qianfanguijin
                   ` (8 subsequent siblings)
  10 siblings, 1 reply; 31+ messages in thread
From: qianfanguijin @ 2023-05-10 10:29 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              |   8 +-
 hw/misc/allwinner-r40-ccu.c         | 209 ++++++++++++++++++++++++++++
 hw/misc/meson.build                 |   1 +
 include/hw/arm/allwinner-r40.h      |   2 +
 include/hw/misc/allwinner-r40-ccu.h |  65 +++++++++
 5 files changed, 284 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 b743d64253..128c0ca470 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 },
@@ -253,6 +253,8 @@ 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);
+
     for (int i = 0; i < AW_R40_NUM_MMCS; i++) {
         object_initialize_child(obj, mmc_names[i], &s->mmc[i],
                                 TYPE_AW_SDHOST_SUN5I);
@@ -367,6 +369,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 */
     for (int i = 0; i < AW_R40_NUM_MMCS; i++) {
         qemu_irq irq = qdev_get_gpio_in(DEVICE(&s->gic),
diff --git a/hw/misc/allwinner-r40-ccu.c b/hw/misc/allwinner-r40-ccu.c
new file mode 100644
index 0000000000..d82fee12db
--- /dev/null
+++ b/hw/misc/allwinner-r40-ccu.c
@@ -0,0 +1,209 @@
+/*
+ * 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 348bf25d6b..3be9dc962b 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"
 
@@ -79,6 +80,7 @@ struct AwR40State {
     const hwaddr *memmap;
     AwA10PITState timer;
     AwSdHostState mmc[AW_R40_NUM_MMCS];
+    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] 31+ messages in thread

* [PATCH v4 03/11] hw: allwinner-r40: Complete uart devices
  2023-05-10 10:29 [PATCH v4 00/11] *** Add allwinner r40 support *** qianfanguijin
  2023-05-10 10:29 ` [PATCH v4 01/11] hw: arm: Add bananapi M2-Ultra and allwinner-r40 support qianfanguijin
  2023-05-10 10:29 ` [PATCH v4 02/11] hw/arm/allwinner-r40: add Clock Control Unit qianfanguijin
@ 2023-05-10 10:29 ` qianfanguijin
  2023-05-14 18:55   ` Niek Linnenbank
  2023-05-10 10:29 ` [PATCH v4 04/11] hw: arm: allwinner-r40: Add i2c0 device qianfanguijin
                   ` (7 subsequent siblings)
  10 siblings, 1 reply; 31+ messages in thread
From: qianfanguijin @ 2023-05-10 10:29 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         | 31 ++++++++++++++++++++++++++++---
 include/hw/arm/allwinner-r40.h |  8 ++++++++
 2 files changed, 36 insertions(+), 3 deletions(-)

diff --git a/hw/arm/allwinner-r40.c b/hw/arm/allwinner-r40.c
index 128c0ca470..537a90b23d 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,
@@ -387,9 +398,23 @@ static void allwinner_r40_realize(DeviceState *dev, Error **errp)
     }
 
     /* 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);
+    for (int i = 0; i < AW_R40_NUM_UARTS; i++) {
+        static const int uart_irqs[AW_R40_NUM_UARTS] = {
+            AW_R40_GIC_SPI_UART0,
+            AW_R40_GIC_SPI_UART1,
+            AW_R40_GIC_SPI_UART2,
+            AW_R40_GIC_SPI_UART3,
+            AW_R40_GIC_SPI_UART4,
+            AW_R40_GIC_SPI_UART5,
+            AW_R40_GIC_SPI_UART6,
+            AW_R40_GIC_SPI_UART7,
+        };
+        const hwaddr addr = s->memmap[AW_R40_DEV_UART0 + i];
+
+        serial_mm_init(get_system_memory(), addr, 2,
+                       qdev_get_gpio_in(DEVICE(&s->gic), uart_irqs[i]),
+                       115200, serial_hd(i), 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 3be9dc962b..959b5dc4e0 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,
@@ -70,6 +77,7 @@ OBJECT_DECLARE_SIMPLE_TYPE(AwR40State, AW_R40)
  * which are currently emulated by the R40 SoC code.
  */
 #define AW_R40_NUM_MMCS         4
+#define AW_R40_NUM_UARTS        8
 
 struct AwR40State {
     /*< private >*/
-- 
2.25.1



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

* [PATCH v4 04/11] hw: arm: allwinner-r40: Add i2c0 device
  2023-05-10 10:29 [PATCH v4 00/11] *** Add allwinner r40 support *** qianfanguijin
                   ` (2 preceding siblings ...)
  2023-05-10 10:29 ` [PATCH v4 03/11] hw: allwinner-r40: Complete uart devices qianfanguijin
@ 2023-05-10 10:29 ` qianfanguijin
  2023-05-14 18:57   ` Niek Linnenbank
  2023-05-10 10:29 ` [PATCH v4 05/11] hw/misc: Rename axp209 to axp22x and add support AXP221 PMU qianfanguijin
                   ` (6 subsequent siblings)
  10 siblings, 1 reply; 31+ messages in thread
From: qianfanguijin @ 2023-05-10 10:29 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         | 11 ++++++++++-
 include/hw/arm/allwinner-r40.h |  3 +++
 2 files changed, 13 insertions(+), 1 deletion(-)

diff --git a/hw/arm/allwinner-r40.c b/hw/arm/allwinner-r40.c
index 537a90b23d..4bc582630c 100644
--- a/hw/arm/allwinner-r40.c
+++ b/hw/arm/allwinner-r40.c
@@ -52,6 +52,7 @@ 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_GIC_DIST]   = 0x01c81000,
     [AW_R40_DEV_GIC_CPU]    = 0x01c82000,
     [AW_R40_DEV_GIC_HYP]    = 0x01c84000,
@@ -115,7 +116,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 },
@@ -167,6 +167,7 @@ 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_UART4     = 17,
     AW_R40_GIC_SPI_UART5     = 18,
     AW_R40_GIC_SPI_UART6     = 19,
@@ -270,6 +271,8 @@ static void allwinner_r40_init(Object *obj)
         object_initialize_child(obj, mmc_names[i], &s->mmc[i],
                                 TYPE_AW_SDHOST_SUN5I);
     }
+
+    object_initialize_child(obj, "twi0", &s->i2c0, TYPE_AW_I2C_SUN6I);
 }
 
 static void allwinner_r40_realize(DeviceState *dev, Error **errp)
@@ -416,6 +419,12 @@ static void allwinner_r40_realize(DeviceState *dev, Error **errp)
                        115200, serial_hd(i), 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));
+
     /* 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 959b5dc4e0..95366f4eee 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,7 @@ enum {
     AW_R40_DEV_UART5,
     AW_R40_DEV_UART6,
     AW_R40_DEV_UART7,
+    AW_R40_DEV_TWI0,
     AW_R40_DEV_GIC_DIST,
     AW_R40_DEV_GIC_CPU,
     AW_R40_DEV_GIC_HYP,
@@ -89,6 +91,7 @@ struct AwR40State {
     AwA10PITState timer;
     AwSdHostState mmc[AW_R40_NUM_MMCS];
     AwR40ClockCtlState ccu;
+    AWI2CState i2c0;
     GICState gic;
     MemoryRegion sram_a1;
     MemoryRegion sram_a2;
-- 
2.25.1



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

* [PATCH v4 05/11] hw/misc: Rename axp209 to axp22x and add support AXP221 PMU
  2023-05-10 10:29 [PATCH v4 00/11] *** Add allwinner r40 support *** qianfanguijin
                   ` (3 preceding siblings ...)
  2023-05-10 10:29 ` [PATCH v4 04/11] hw: arm: allwinner-r40: Add i2c0 device qianfanguijin
@ 2023-05-10 10:29 ` qianfanguijin
  2023-05-15 19:29   ` Niek Linnenbank
  2023-05-10 10:29 ` [PATCH v4 06/11] hw/arm/allwinner-r40: add SDRAM controller device qianfanguijin
                   ` (5 subsequent siblings)
  10 siblings, 1 reply; 31+ messages in thread
From: qianfanguijin @ 2023-05-10 10:29 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        |   3 +-
 hw/arm/bananapi_m2u.c |   6 +
 hw/misc/Kconfig       |   2 +-
 hw/misc/axp209.c      | 238 -----------------------------------
 hw/misc/axp2xx.c      | 283 ++++++++++++++++++++++++++++++++++++++++++
 hw/misc/meson.build   |   2 +-
 hw/misc/trace-events  |   8 +-
 7 files changed, 297 insertions(+), 245 deletions(-)
 delete mode 100644 hw/misc/axp209.c
 create mode 100644 hw/misc/axp2xx.c

diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig
index b7a84f6e3f..bad4ea158c 100644
--- a/hw/arm/Kconfig
+++ b/hw/arm/Kconfig
@@ -355,7 +355,7 @@ config ALLWINNER_A10
     select ALLWINNER_WDT
     select ALLWINNER_EMAC
     select ALLWINNER_I2C
-    select AXP209_PMU
+    select AXP2XX_PMU
     select SERIAL
     select UNIMP
 
@@ -378,6 +378,7 @@ config ALLWINNER_R40
     bool
     default y if TCG && ARM
     select ALLWINNER_A10_PIT
+    select AXP2XX_PMU
     select SERIAL
     select ARM_TIMER
     select ARM_GIC
diff --git a/hw/arm/bananapi_m2u.c b/hw/arm/bananapi_m2u.c
index 1d49a006b5..9c5360a41b 100644
--- a/hw/arm/bananapi_m2u.c
+++ b/hw/arm/bananapi_m2u.c
@@ -23,6 +23,7 @@
 #include "qapi/error.h"
 #include "qemu/error-report.h"
 #include "hw/boards.h"
+#include "hw/i2c/i2c.h"
 #include "hw/qdev-properties.h"
 #include "hw/arm/allwinner-r40.h"
 
@@ -61,6 +62,7 @@ static void bpim2u_init(MachineState *machine)
 {
     bool bootroom_loaded = false;
     AwR40State *r40;
+    I2CBus *i2c;
 
     /* BIOS is not supported by this board */
     if (machine->firmware) {
@@ -104,6 +106,10 @@ static void bpim2u_init(MachineState *machine)
         }
     }
 
+    /* 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..efeb430a6c 100644
--- a/hw/misc/Kconfig
+++ b/hw/misc/Kconfig
@@ -176,7 +176,7 @@ config ALLWINNER_A10_CCM
 config ALLWINNER_A10_DRAMC
     bool
 
-config AXP209_PMU
+config AXP2XX_PMU
     bool
     depends on I2C
 
diff --git a/hw/misc/axp209.c b/hw/misc/axp209.c
deleted file mode 100644
index 2908ed99a6..0000000000
--- a/hw/misc/axp209.c
+++ /dev/null
@@ -1,238 +0,0 @@
-/*
- * AXP-209 PMU Emulation
- *
- * Copyright (C) 2022 Strahinja Jankovic <strahinja.p.jankovic@gmail.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 "trace.h"
-#include "hw/i2c/i2c.h"
-#include "migration/vmstate.h"
-
-#define TYPE_AXP209_PMU "axp209_pmu"
-
-#define AXP209(obj) \
-    OBJECT_CHECK(AXP209I2CState, (obj), TYPE_AXP209_PMU)
-
-/* registers */
-enum {
-    REG_POWER_STATUS = 0x0u,
-    REG_OPERATING_MODE,
-    REG_OTG_VBUS_STATUS,
-    REG_CHIP_VERSION,
-    REG_DATA_CACHE_0,
-    REG_DATA_CACHE_1,
-    REG_DATA_CACHE_2,
-    REG_DATA_CACHE_3,
-    REG_DATA_CACHE_4,
-    REG_DATA_CACHE_5,
-    REG_DATA_CACHE_6,
-    REG_DATA_CACHE_7,
-    REG_DATA_CACHE_8,
-    REG_DATA_CACHE_9,
-    REG_DATA_CACHE_A,
-    REG_DATA_CACHE_B,
-    REG_POWER_OUTPUT_CTRL = 0x12u,
-    REG_DC_DC2_OUT_V_CTRL = 0x23u,
-    REG_DC_DC2_DVS_CTRL = 0x25u,
-    REG_DC_DC3_OUT_V_CTRL = 0x27u,
-    REG_LDO2_4_OUT_V_CTRL,
-    REG_LDO3_OUT_V_CTRL,
-    REG_VBUS_CH_MGMT = 0x30u,
-    REG_SHUTDOWN_V_CTRL,
-    REG_SHUTDOWN_CTRL,
-    REG_CHARGE_CTRL_1,
-    REG_CHARGE_CTRL_2,
-    REG_SPARE_CHARGE_CTRL,
-    REG_PEK_KEY_CTRL,
-    REG_DC_DC_FREQ_SET,
-    REG_CHR_TEMP_TH_SET,
-    REG_CHR_HIGH_TEMP_TH_CTRL,
-    REG_IPSOUT_WARN_L1,
-    REG_IPSOUT_WARN_L2,
-    REG_DISCHR_TEMP_TH_SET,
-    REG_DISCHR_HIGH_TEMP_TH_CTRL,
-    REG_IRQ_BANK_1_CTRL = 0x40u,
-    REG_IRQ_BANK_2_CTRL,
-    REG_IRQ_BANK_3_CTRL,
-    REG_IRQ_BANK_4_CTRL,
-    REG_IRQ_BANK_5_CTRL,
-    REG_IRQ_BANK_1_STAT = 0x48u,
-    REG_IRQ_BANK_2_STAT,
-    REG_IRQ_BANK_3_STAT,
-    REG_IRQ_BANK_4_STAT,
-    REG_IRQ_BANK_5_STAT,
-    REG_ADC_ACIN_V_H = 0x56u,
-    REG_ADC_ACIN_V_L,
-    REG_ADC_ACIN_CURR_H,
-    REG_ADC_ACIN_CURR_L,
-    REG_ADC_VBUS_V_H,
-    REG_ADC_VBUS_V_L,
-    REG_ADC_VBUS_CURR_H,
-    REG_ADC_VBUS_CURR_L,
-    REG_ADC_INT_TEMP_H,
-    REG_ADC_INT_TEMP_L,
-    REG_ADC_TEMP_SENS_V_H = 0x62u,
-    REG_ADC_TEMP_SENS_V_L,
-    REG_ADC_BAT_V_H = 0x78u,
-    REG_ADC_BAT_V_L,
-    REG_ADC_BAT_DISCHR_CURR_H,
-    REG_ADC_BAT_DISCHR_CURR_L,
-    REG_ADC_BAT_CHR_CURR_H,
-    REG_ADC_BAT_CHR_CURR_L,
-    REG_ADC_IPSOUT_V_H,
-    REG_ADC_IPSOUT_V_L,
-    REG_DC_DC_MOD_SEL = 0x80u,
-    REG_ADC_EN_1,
-    REG_ADC_EN_2,
-    REG_ADC_SR_CTRL,
-    REG_ADC_IN_RANGE,
-    REG_GPIO1_ADC_IRQ_RISING_TH,
-    REG_GPIO1_ADC_IRQ_FALLING_TH,
-    REG_TIMER_CTRL = 0x8au,
-    REG_VBUS_CTRL_MON_SRP,
-    REG_OVER_TEMP_SHUTDOWN = 0x8fu,
-    REG_GPIO0_FEAT_SET,
-    REG_GPIO_OUT_HIGH_SET,
-    REG_GPIO1_FEAT_SET,
-    REG_GPIO2_FEAT_SET,
-    REG_GPIO_SIG_STATE_SET_MON,
-    REG_GPIO3_SET,
-    REG_COULOMB_CNTR_CTRL = 0xb8u,
-    REG_POWER_MEAS_RES,
-    NR_REGS
-};
-
-#define AXP209_CHIP_VERSION_ID             (0x01)
-#define AXP209_DC_DC2_OUT_V_CTRL_RESET     (0x16)
-#define AXP209_IRQ_BANK_1_CTRL_RESET       (0xd8)
-
-/* A simple I2C slave which returns values of ID or CNT register. */
-typedef struct AXP209I2CState {
-    /*< 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 */
-} AXP209I2CState;
-
-/* Reset all counters and load ID register */
-static void axp209_reset_enter(Object *obj, ResetType type)
-{
-    AXP209I2CState *s = AXP209(obj);
-
-    memset(s->regs, 0, NR_REGS);
-    s->ptr = 0;
-    s->count = 0;
-    s->regs[REG_CHIP_VERSION] = AXP209_CHIP_VERSION_ID;
-    s->regs[REG_DC_DC2_OUT_V_CTRL] = AXP209_DC_DC2_OUT_V_CTRL_RESET;
-    s->regs[REG_IRQ_BANK_1_CTRL] = AXP209_IRQ_BANK_1_CTRL_RESET;
-}
-
-/* Handle events from master. */
-static int axp209_event(I2CSlave *i2c, enum i2c_event event)
-{
-    AXP209I2CState *s = AXP209(i2c);
-
-    s->count = 0;
-
-    return 0;
-}
-
-/* Called when master requests read */
-static uint8_t axp209_rx(I2CSlave *i2c)
-{
-    AXP209I2CState *s = AXP209(i2c);
-    uint8_t ret = 0xff;
-
-    if (s->ptr < NR_REGS) {
-        ret = s->regs[s->ptr++];
-    }
-
-    trace_axp209_rx(s->ptr - 1, ret);
-
-    return ret;
-}
-
-/*
- * Called when master sends write.
- * Update ptr with byte 0, then perform write with second byte.
- */
-static int axp209_tx(I2CSlave *i2c, uint8_t data)
-{
-    AXP209I2CState *s = AXP209(i2c);
-
-    if (s->count == 0) {
-        /* Store register address */
-        s->ptr = data;
-        s->count++;
-        trace_axp209_select(data);
-    } else {
-        trace_axp209_tx(s->ptr, data);
-        if (s->ptr == REG_DC_DC2_OUT_V_CTRL) {
-            s->regs[s->ptr++] = data;
-        }
-    }
-
-    return 0;
-}
-
-static const VMStateDescription vmstate_axp209 = {
-    .name = TYPE_AXP209_PMU,
-    .version_id = 1,
-    .fields = (VMStateField[]) {
-        VMSTATE_UINT8_ARRAY(regs, AXP209I2CState, NR_REGS),
-        VMSTATE_UINT8(count, AXP209I2CState),
-        VMSTATE_UINT8(ptr, AXP209I2CState),
-        VMSTATE_END_OF_LIST()
-    }
-};
-
-static void axp209_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 = axp209_reset_enter;
-    dc->vmsd = &vmstate_axp209;
-    isc->event = axp209_event;
-    isc->recv = axp209_rx;
-    isc->send = axp209_tx;
-}
-
-static const TypeInfo axp209_info = {
-    .name = TYPE_AXP209_PMU,
-    .parent = TYPE_I2C_SLAVE,
-    .instance_size = sizeof(AXP209I2CState),
-    .class_init = axp209_class_init
-};
-
-static void axp209_register_devices(void)
-{
-    type_register_static(&axp209_info);
-}
-
-type_init(axp209_register_devices);
diff --git a/hw/misc/axp2xx.c b/hw/misc/axp2xx.c
new file mode 100644
index 0000000000..52a6ffc7f3
--- /dev/null
+++ b/hw/misc/axp2xx.c
@@ -0,0 +1,283 @@
+/*
+ * AXP-2XX PMU Emulation, supported lists:
+ *   AXP209
+ *   AXP221
+ *
+ * Copyright (C) 2022 Strahinja Jankovic <strahinja.p.jankovic@gmail.com>
+ * 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 "qom/object.h"
+#include "trace.h"
+#include "hw/i2c/i2c.h"
+#include "migration/vmstate.h"
+
+#define TYPE_AXP2XX     "axp2xx_pmu"
+#define TYPE_AXP209_PMU "axp209_pmu"
+#define TYPE_AXP221_PMU "axp221_pmu"
+
+OBJECT_DECLARE_TYPE(AXP2xxI2CState, AXP2xxClass, AXP2XX)
+
+#define NR_REGS                            (0xff)
+
+/* A simple I2C slave which returns values of ID or CNT register. */
+typedef struct AXP2xxI2CState {
+    /*< 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 */
+} AXP2xxI2CState;
+
+typedef struct AXP2xxClass {
+    /*< private >*/
+    I2CSlaveClass parent_class;
+    /*< public >*/
+    void (*reset_enter)(AXP2xxI2CState *s, ResetType type);
+} AXP2xxClass;
+
+#define AXP209_CHIP_VERSION_ID             (0x01)
+#define AXP209_DC_DC2_OUT_V_CTRL_RESET     (0x16)
+
+/* Reset all counters and load ID register */
+static void axp209_reset_enter(AXP2xxI2CState *s, ResetType type)
+{
+    memset(s->regs, 0, NR_REGS);
+    s->ptr = 0;
+    s->count = 0;
+
+    s->regs[0x03] = AXP209_CHIP_VERSION_ID;
+    s->regs[0x23] = AXP209_DC_DC2_OUT_V_CTRL_RESET;
+
+    s->regs[0x30] = 0x60;
+    s->regs[0x32] = 0x46;
+    s->regs[0x34] = 0x41;
+    s->regs[0x35] = 0x22;
+    s->regs[0x36] = 0x5d;
+    s->regs[0x37] = 0x08;
+    s->regs[0x38] = 0xa5;
+    s->regs[0x39] = 0x1f;
+    s->regs[0x3a] = 0x68;
+    s->regs[0x3b] = 0x5f;
+    s->regs[0x3c] = 0xfc;
+    s->regs[0x3d] = 0x16;
+    s->regs[0x40] = 0xd8;
+    s->regs[0x42] = 0xff;
+    s->regs[0x43] = 0x3b;
+    s->regs[0x80] = 0xe0;
+    s->regs[0x82] = 0x83;
+    s->regs[0x83] = 0x80;
+    s->regs[0x84] = 0x32;
+    s->regs[0x86] = 0xff;
+    s->regs[0x90] = 0x07;
+    s->regs[0x91] = 0xa0;
+    s->regs[0x92] = 0x07;
+    s->regs[0x93] = 0x07;
+}
+
+#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(AXP2xxI2CState *s, ResetType type)
+{
+    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;
+}
+
+static void axp2xx_reset_enter(Object *obj, ResetType type)
+{
+    AXP2xxI2CState *s = AXP2XX(obj);
+    AXP2xxClass *sc = AXP2XX_GET_CLASS(s);
+
+    sc->reset_enter(s, type);
+}
+
+/* Handle events from master. */
+static int axp2xx_event(I2CSlave *i2c, enum i2c_event event)
+{
+    AXP2xxI2CState *s = AXP2XX(i2c);
+
+    s->count = 0;
+
+    return 0;
+}
+
+/* Called when master requests read */
+static uint8_t axp2xx_rx(I2CSlave *i2c)
+{
+    AXP2xxI2CState *s = AXP2XX(i2c);
+    uint8_t ret = 0xff;
+
+    if (s->ptr < NR_REGS) {
+        ret = s->regs[s->ptr++];
+    }
+
+    trace_axp2xx_rx(s->ptr - 1, ret);
+
+    return ret;
+}
+
+/*
+ * Called when master sends write.
+ * Update ptr with byte 0, then perform write with second byte.
+ */
+static int axp2xx_tx(I2CSlave *i2c, uint8_t data)
+{
+    AXP2xxI2CState *s = AXP2XX(i2c);
+
+    if (s->count == 0) {
+        /* Store register address */
+        s->ptr = data;
+        s->count++;
+        trace_axp2xx_select(data);
+    } else {
+        trace_axp2xx_tx(s->ptr, data);
+        s->regs[s->ptr++] = data;
+    }
+
+    return 0;
+}
+
+static const VMStateDescription vmstate_axp2xx = {
+    .name = TYPE_AXP209_PMU,
+    .version_id = 1,
+    .fields = (VMStateField[]) {
+        VMSTATE_UINT8_ARRAY(regs, AXP2xxI2CState, NR_REGS),
+        VMSTATE_UINT8(count, AXP2xxI2CState),
+        VMSTATE_UINT8(ptr, AXP2xxI2CState),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static void axp2xx_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 = axp2xx_reset_enter;
+    dc->vmsd = &vmstate_axp2xx;
+    isc->event = axp2xx_event;
+    isc->recv = axp2xx_rx;
+    isc->send = axp2xx_tx;
+}
+
+static const TypeInfo axp2xx_info = {
+    .name = TYPE_AXP2XX,
+    .parent = TYPE_I2C_SLAVE,
+    .instance_size = sizeof(AXP2xxI2CState),
+    .class_size = sizeof(AXP2xxClass),
+    .class_init = axp2xx_class_init,
+    .abstract = true,
+};
+
+static void axp209_class_init(ObjectClass *oc, void *data)
+{
+    AXP2xxClass *sc = AXP2XX_CLASS(oc);
+
+    sc->reset_enter = axp209_reset_enter;
+}
+
+static const TypeInfo axp209_info = {
+    .name = TYPE_AXP209_PMU,
+    .parent = TYPE_AXP2XX,
+    .class_init = axp209_class_init
+};
+
+static void axp221_class_init(ObjectClass *oc, void *data)
+{
+    AXP2xxClass *sc = AXP2XX_CLASS(oc);
+
+    sc->reset_enter = axp221_reset_enter;
+}
+
+static const TypeInfo axp221_info = {
+    .name = TYPE_AXP221_PMU,
+    .parent = TYPE_AXP2XX,
+    .class_init = axp221_class_init,
+};
+
+static void axp2xx_register_devices(void)
+{
+    type_register_static(&axp2xx_info);
+    type_register_static(&axp209_info);
+    type_register_static(&axp221_info);
+}
+
+type_init(axp2xx_register_devices);
diff --git a/hw/misc/meson.build b/hw/misc/meson.build
index 96e35f1cdb..1db0343333 100644
--- a/hw/misc/meson.build
+++ b/hw/misc/meson.build
@@ -45,7 +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_AXP209_PMU', if_true: files('axp209.c'))
+softmmu_ss.add(when: 'CONFIG_AXP2XX_PMU', if_true: files('axp2xx.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..24cdec83fe 100644
--- a/hw/misc/trace-events
+++ b/hw/misc/trace-events
@@ -23,10 +23,10 @@ allwinner_sid_write(uint64_t offset, uint64_t data, unsigned size) "offset 0x%"
 avr_power_read(uint8_t value) "power_reduc read value:%u"
 avr_power_write(uint8_t value) "power_reduc write value:%u"
 
-# axp209.c
-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
+# axp2xx
+axp2xx_rx(uint8_t reg, uint8_t data) "Read reg 0x%" PRIx8 " : 0x%" PRIx8
+axp2xx_select(uint8_t reg) "Accessing reg 0x%" PRIx8
+axp2xx_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"
-- 
2.25.1



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

* [PATCH v4 06/11] hw/arm/allwinner-r40: add SDRAM controller device
  2023-05-10 10:29 [PATCH v4 00/11] *** Add allwinner r40 support *** qianfanguijin
                   ` (4 preceding siblings ...)
  2023-05-10 10:29 ` [PATCH v4 05/11] hw/misc: Rename axp209 to axp22x and add support AXP221 PMU qianfanguijin
@ 2023-05-10 10:29 ` qianfanguijin
  2023-05-15 19:47   ` Niek Linnenbank
  2023-05-10 10:30 ` [PATCH v4 07/11] hw: sd: allwinner-sdhost: Add sun50i-a64 SoC support qianfanguijin
                   ` (4 subsequent siblings)
  10 siblings, 1 reply; 31+ messages in thread
From: qianfanguijin @ 2023-05-10 10:29 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                |  21 +-
 hw/arm/bananapi_m2u.c                 |   7 +
 hw/misc/allwinner-r40-dramc.c         | 513 ++++++++++++++++++++++++++
 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, 674 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 4bc582630c..0e4542d35f 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[] = {
@@ -53,6 +54,9 @@ const hwaddr allwinner_r40_memmap[] = {
     [AW_R40_DEV_UART6]      = 0x01c29800,
     [AW_R40_DEV_UART7]      = 0x01c29c00,
     [AW_R40_DEV_TWI0]       = 0x01c2ac00,
+    [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, "twi0", &s->i2c0, 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)
@@ -425,6 +433,15 @@ static void allwinner_r40_realize(DeviceState *dev, Error **errp)
     sysbus_connect_irq(SYS_BUS_DEVICE(&s->i2c0), 0,
                        qdev_get_gpio_in(DEVICE(&s->gic), AW_R40_GIC_SPI_TWI0));
 
+    /* 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 9c5360a41b..20a4550c68 100644
--- a/hw/arm/bananapi_m2u.c
+++ b/hw/arm/bananapi_m2u.c
@@ -85,6 +85,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..b102bcdaba
--- /dev/null
+++ b/hw/misc/allwinner-r40-dramc.c
@@ -0,0 +1,513 @@
+/*
+ * 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 1db0343333..b04d43e05a 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_AXP2XX_PMU', if_true: files('axp2xx.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/hw/misc/trace-events b/hw/misc/trace-events
index 24cdec83fe..8b68f07765 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 95366f4eee..8243e8903b 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"
@@ -54,7 +55,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)
@@ -86,11 +90,18 @@ 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;
     AwSdHostState mmc[AW_R40_NUM_MMCS];
     AwR40ClockCtlState ccu;
+    AwR40DramCtlState dramc;
     AWI2CState i2c0;
     GICState gic;
     MemoryRegion sram_a1;
diff --git a/include/hw/misc/allwinner-r40-dramc.h b/include/hw/misc/allwinner-r40-dramc.h
new file mode 100644
index 0000000000..6a1a3a7893
--- /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]; /**< DRAMCOM registers */
+    uint32_t dramctl[AW_R40_DRAMCTL_REGS_NUM]; /**< DRAMCTL registers */
+    uint32_t dramphy[AW_R40_DRAMPHY_REGS_NUM] ;/**< DRAMPHY registers */
+
+    /** @} */
+
+};
+
+#endif /* HW_MISC_ALLWINNER_R40_DRAMC_H */
-- 
2.25.1



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

* [PATCH v4 07/11] hw: sd: allwinner-sdhost: Add sun50i-a64 SoC support
  2023-05-10 10:29 [PATCH v4 00/11] *** Add allwinner r40 support *** qianfanguijin
                   ` (5 preceding siblings ...)
  2023-05-10 10:29 ` [PATCH v4 06/11] hw/arm/allwinner-r40: add SDRAM controller device qianfanguijin
@ 2023-05-10 10:30 ` qianfanguijin
  2023-05-15 19:54   ` Niek Linnenbank
  2023-05-10 10:30 ` [PATCH v4 08/11] hw: arm: allwinner-r40: Add emac and gmac support qianfanguijin
                   ` (3 subsequent siblings)
  10 siblings, 1 reply; 31+ messages in thread
From: qianfanguijin @ 2023-05-10 10:30 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.

Also fix allwinner-r40's mmc controller type.

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

diff --git a/hw/arm/allwinner-r40.c b/hw/arm/allwinner-r40.c
index 0e4542d35f..b148c56449 100644
--- a/hw/arm/allwinner-r40.c
+++ b/hw/arm/allwinner-r40.c
@@ -271,7 +271,7 @@ static void allwinner_r40_init(Object *obj)
 
     for (int i = 0; i < AW_R40_NUM_MMCS; i++) {
         object_initialize_child(obj, mmc_names[i], &s->mmc[i],
-                                TYPE_AW_SDHOST_SUN5I);
+                                TYPE_AW_SDHOST_SUN50I_A64);
     }
 
     object_initialize_child(obj, "twi0", &s->i2c0, TYPE_AW_I2C_SUN6I);
diff --git a/hw/sd/allwinner-sdhost.c b/hw/sd/allwinner-sdhost.c
index 92a0f42708..f4fa2d179b 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,
 };
 
@@ -459,6 +461,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) {
@@ -577,13 +580,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;
 }
@@ -602,6 +616,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);
 
@@ -725,10 +740,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;
     }
 }
 
@@ -777,6 +803,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()
     }
 };
@@ -815,6 +842,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;
@@ -855,6 +883,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)
@@ -888,6 +920,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,
@@ -910,6 +960,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,
@@ -922,6 +984,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] 31+ messages in thread

* [PATCH v4 08/11] hw: arm: allwinner-r40: Add emac and gmac support
  2023-05-10 10:29 [PATCH v4 00/11] *** Add allwinner r40 support *** qianfanguijin
                   ` (6 preceding siblings ...)
  2023-05-10 10:30 ` [PATCH v4 07/11] hw: sd: allwinner-sdhost: Add sun50i-a64 SoC support qianfanguijin
@ 2023-05-10 10:30 ` qianfanguijin
  2023-05-15 19:58   ` Niek Linnenbank
  2023-05-10 10:30 ` [PATCH v4 09/11] hw: arm: allwinner-sramc: Add SRAM Controller support for R40 qianfanguijin
                   ` (2 subsequent siblings)
  10 siblings, 1 reply; 31+ messages in thread
From: qianfanguijin @ 2023-05-10 10:30 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         | 50 ++++++++++++++++++++++++++++++++--
 hw/arm/bananapi_m2u.c          |  3 ++
 include/hw/arm/allwinner-r40.h |  6 ++++
 3 files changed, 57 insertions(+), 2 deletions(-)

diff --git a/hw/arm/allwinner-r40.c b/hw/arm/allwinner-r40.c
index b148c56449..c018ad231a 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,
@@ -54,6 +55,7 @@ const hwaddr allwinner_r40_memmap[] = {
     [AW_R40_DEV_UART6]      = 0x01c29800,
     [AW_R40_DEV_UART7]      = 0x01c29c00,
     [AW_R40_DEV_TWI0]       = 0x01c2ac00,
+    [AW_R40_DEV_GMAC]       = 0x01c50000,
     [AW_R40_DEV_DRAMCOM]    = 0x01c62000,
     [AW_R40_DEV_DRAMCTL]    = 0x01c63000,
     [AW_R40_DEV_DRAMPHY]    = 0x01c65000,
@@ -82,7 +84,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 },
@@ -180,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_EMAC      = 55,
+    AW_R40_GIC_SPI_GMAC      = 85,
 };
 
 /* Allwinner R40 general constants */
@@ -276,6 +278,11 @@ static void allwinner_r40_init(Object *obj)
 
     object_initialize_child(obj, "twi0", &s->i2c0, 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");
@@ -285,6 +292,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;
 
@@ -442,6 +450,44 @@ static void allwinner_r40_realize(DeviceState *dev, Error **errp)
     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 20a4550c68..74121d8966 100644
--- a/hw/arm/bananapi_m2u.c
+++ b/hw/arm/bananapi_m2u.c
@@ -92,6 +92,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 8243e8903b..5f2d08489e 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,
@@ -51,6 +54,7 @@ enum {
     AW_R40_DEV_UART6,
     AW_R40_DEV_UART7,
     AW_R40_DEV_TWI0,
+    AW_R40_DEV_GMAC,
     AW_R40_DEV_GIC_DIST,
     AW_R40_DEV_GIC_CPU,
     AW_R40_DEV_GIC_HYP,
@@ -103,6 +107,8 @@ struct AwR40State {
     AwR40ClockCtlState ccu;
     AwR40DramCtlState dramc;
     AWI2CState i2c0;
+    AwEmacState emac;
+    AwSun8iEmacState gmac;
     GICState gic;
     MemoryRegion sram_a1;
     MemoryRegion sram_a2;
-- 
2.25.1



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

* [PATCH v4 09/11] hw: arm: allwinner-sramc: Add SRAM Controller support for R40
  2023-05-10 10:29 [PATCH v4 00/11] *** Add allwinner r40 support *** qianfanguijin
                   ` (7 preceding siblings ...)
  2023-05-10 10:30 ` [PATCH v4 08/11] hw: arm: allwinner-r40: Add emac and gmac support qianfanguijin
@ 2023-05-10 10:30 ` qianfanguijin
  2023-05-15 20:05   ` Niek Linnenbank
  2023-05-10 10:30 ` [PATCH v4 10/11] tests: avocado: boot_linux_console: Add test case for bpim2u qianfanguijin
  2023-05-15 20:25 ` [PATCH v4 00/11] *** Add allwinner r40 support *** Niek Linnenbank
  10 siblings, 1 reply; 31+ messages in thread
From: qianfanguijin @ 2023-05-10 10:30 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>

Only a few important registers are added, especially the SRAM_VER
register.

Signed-off-by: qianfan Zhao <qianfanguijin@163.com>
---
 hw/arm/Kconfig                    |   1 +
 hw/arm/allwinner-r40.c            |   7 +-
 hw/misc/Kconfig                   |   3 +
 hw/misc/allwinner-sramc.c         | 184 ++++++++++++++++++++++++++++++
 hw/misc/meson.build               |   1 +
 hw/misc/trace-events              |   4 +
 include/hw/arm/allwinner-r40.h    |   3 +
 include/hw/misc/allwinner-sramc.h |  69 +++++++++++
 8 files changed, 271 insertions(+), 1 deletion(-)
 create mode 100644 hw/misc/allwinner-sramc.c
 create mode 100644 include/hw/misc/allwinner-sramc.h

diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig
index bad4ea158c..ae6f3911c5 100644
--- a/hw/arm/Kconfig
+++ b/hw/arm/Kconfig
@@ -377,6 +377,7 @@ config ALLWINNER_H3
 config ALLWINNER_R40
     bool
     default y if TCG && ARM
+    select ALLWINNER_SRAMC
     select ALLWINNER_A10_PIT
     select AXP2XX_PMU
     select SERIAL
diff --git a/hw/arm/allwinner-r40.c b/hw/arm/allwinner-r40.c
index c018ad231a..7d29eb224f 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_SRAMC]      = 0x01c00000,
     [AW_R40_DEV_EMAC]       = 0x01c0b000,
     [AW_R40_DEV_MMC0]       = 0x01c0f000,
     [AW_R40_DEV_MMC1]       = 0x01c10000,
@@ -76,7 +77,6 @@ struct AwR40Unimplemented {
 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 },
@@ -288,6 +288,8 @@ static void allwinner_r40_init(Object *obj)
                              "ram-addr");
     object_property_add_alias(obj, "ram-size", OBJECT(&s->dramc),
                               "ram-size");
+
+    object_initialize_child(obj, "sramc", &s->sramc, TYPE_AW_SRAMC_SUN8I_R40);
 }
 
 static void allwinner_r40_realize(DeviceState *dev, Error **errp)
@@ -382,6 +384,9 @@ static void allwinner_r40_realize(DeviceState *dev, Error **errp)
                        AW_R40_GIC_SPI_TIMER1));
 
     /* SRAM */
+    sysbus_realize(SYS_BUS_DEVICE(&s->sramc), &error_fatal);
+    sysbus_mmio_map(SYS_BUS_DEVICE(&s->sramc), 0, s->memmap[AW_R40_DEV_SRAMC]);
+
     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",
diff --git a/hw/misc/Kconfig b/hw/misc/Kconfig
index efeb430a6c..e4c2149175 100644
--- a/hw/misc/Kconfig
+++ b/hw/misc/Kconfig
@@ -170,6 +170,9 @@ config VIRT_CTRL
 config LASI
     bool
 
+config ALLWINNER_SRAMC
+    bool
+
 config ALLWINNER_A10_CCM
     bool
 
diff --git a/hw/misc/allwinner-sramc.c b/hw/misc/allwinner-sramc.c
new file mode 100644
index 0000000000..a8b731f8f2
--- /dev/null
+++ b/hw/misc/allwinner-sramc.c
@@ -0,0 +1,184 @@
+/*
+ * Allwinner R40 SRAM 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/>.
+ */
+
+#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 "qapi/error.h"
+#include "hw/qdev-properties.h"
+#include "hw/qdev-properties-system.h"
+#include "hw/misc/allwinner-sramc.h"
+#include "trace.h"
+
+/*
+ * register offsets
+ * https://linux-sunxi.org/SRAM_Controller_Register_Guide
+ */
+enum {
+    REG_SRAM_CTL1_CFG               = 0x04, /* SRAM Control register 1 */
+    REG_SRAM_VER                    = 0x24, /* SRAM Version register */
+    REG_SRAM_R40_SOFT_ENTRY_REG0    = 0xbc,
+};
+
+/* REG_SRAMC_VERSION bit defines */
+#define SRAM_VER_READ_ENABLE            (1 << 15)
+#define SRAM_VER_VERSION_SHIFT          16
+#define SRAM_VERSION_SUN8I_R40          0x1701
+
+static uint64_t allwinner_sramc_read(void *opaque, hwaddr offset,
+                                     unsigned size)
+{
+    AwSRAMCState *s = AW_SRAMC(opaque);
+    AwSRAMCClass *sc = AW_SRAMC_GET_CLASS(s);
+    uint64_t val = 0;
+
+    switch (offset) {
+    case REG_SRAM_CTL1_CFG:
+        val = s->sram_ctl1;
+        break;
+    case REG_SRAM_VER:
+        /* bit15: lock bit, set this bit before reading this register */
+        if (s->sram_ver & SRAM_VER_READ_ENABLE) {
+            val = SRAM_VER_READ_ENABLE |
+                    (sc->sram_version_code << SRAM_VER_VERSION_SHIFT);
+        }
+        break;
+    case REG_SRAM_R40_SOFT_ENTRY_REG0:
+        val = s->sram_soft_entry_reg0;
+        break;
+    default:
+        qemu_log_mask(LOG_GUEST_ERROR, "%s: out-of-bounds offset 0x%04x\n",
+                      __func__, (uint32_t)offset);
+        return 0;
+    }
+
+    trace_allwinner_sramc_read(offset, val);
+
+    return val;
+}
+
+static void allwinner_sramc_write(void *opaque, hwaddr offset,
+                                  uint64_t val, unsigned size)
+{
+    AwSRAMCState *s = AW_SRAMC(opaque);
+
+    trace_allwinner_sramc_write(offset, val);
+
+    switch (offset) {
+    case REG_SRAM_CTL1_CFG:
+        s->sram_ctl1 = val;
+        break;
+    case REG_SRAM_VER:
+        /* Only the READ_ENABLE bit is writeable */
+        s->sram_ver = val & SRAM_VER_READ_ENABLE;
+        break;
+    case REG_SRAM_R40_SOFT_ENTRY_REG0:
+        s->sram_soft_entry_reg0 = val;
+        break;
+    default:
+        qemu_log_mask(LOG_GUEST_ERROR, "%s: out-of-bounds offset 0x%04x\n",
+                      __func__, (uint32_t)offset);
+        break;
+    }
+}
+
+static const MemoryRegionOps allwinner_sramc_ops = {
+    .read = allwinner_sramc_read,
+    .write = allwinner_sramc_write,
+    .endianness = DEVICE_NATIVE_ENDIAN,
+    .valid = {
+        .min_access_size = 4,
+        .max_access_size = 4,
+    },
+    .impl.min_access_size = 4,
+};
+
+static const VMStateDescription allwinner_sramc_vmstate = {
+    .name = "allwinner-sramc",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .fields = (VMStateField[]) {
+        VMSTATE_UINT32(sram_ver, AwSRAMCState),
+        VMSTATE_UINT32(sram_soft_entry_reg0, AwSRAMCState),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static void allwinner_sramc_reset(DeviceState *dev)
+{
+    AwSRAMCState *s = AW_SRAMC(dev);
+    AwSRAMCClass *sc = AW_SRAMC_GET_CLASS(s);
+
+    switch (sc->sram_version_code) {
+    case SRAM_VERSION_SUN8I_R40:
+        s->sram_ctl1 = 0x1300;
+        break;
+    }
+}
+
+static void allwinner_sramc_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+
+    dc->reset = allwinner_sramc_reset;
+    dc->vmsd = &allwinner_sramc_vmstate;
+}
+
+static void allwinner_sramc_init(Object *obj)
+{
+    SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
+    AwSRAMCState *s = AW_SRAMC(obj);
+
+    /* Memory mapping */
+    memory_region_init_io(&s->iomem, OBJECT(s), &allwinner_sramc_ops, s,
+                           TYPE_AW_SRAMC, 1 * KiB);
+    sysbus_init_mmio(sbd, &s->iomem);
+}
+
+static const TypeInfo allwinner_sramc_info = {
+    .name          = TYPE_AW_SRAMC,
+    .parent        = TYPE_SYS_BUS_DEVICE,
+    .instance_init = allwinner_sramc_init,
+    .instance_size = sizeof(AwSRAMCState),
+    .class_init    = allwinner_sramc_class_init,
+};
+
+static void allwinner_r40_sramc_class_init(ObjectClass *klass, void *data)
+{
+    AwSRAMCClass *sc = AW_SRAMC_CLASS(klass);
+
+    sc->sram_version_code = SRAM_VERSION_SUN8I_R40;
+}
+
+static const TypeInfo allwinner_r40_sramc_info = {
+    .name          = TYPE_AW_SRAMC_SUN8I_R40,
+    .parent        = TYPE_AW_SRAMC,
+    .class_init    = allwinner_r40_sramc_class_init,
+};
+
+static void allwinner_sramc_register(void)
+{
+    type_register_static(&allwinner_sramc_info);
+    type_register_static(&allwinner_r40_sramc_info);
+}
+
+type_init(allwinner_sramc_register)
diff --git a/hw/misc/meson.build b/hw/misc/meson.build
index b04d43e05a..78ca857c9d 100644
--- a/hw/misc/meson.build
+++ b/hw/misc/meson.build
@@ -37,6 +37,7 @@ subdir('macio')
 
 softmmu_ss.add(when: 'CONFIG_IVSHMEM_DEVICE', if_true: files('ivshmem.c'))
 
+softmmu_ss.add(when: 'CONFIG_ALLWINNER_SRAMC', if_true: files('allwinner-sramc.c'))
 softmmu_ss.add(when: 'CONFIG_ALLWINNER_A10_CCM', if_true: files('allwinner-a10-ccm.c'))
 softmmu_ss.add(when: 'CONFIG_ALLWINNER_A10_DRAMC', if_true: files('allwinner-a10-dramc.c'))
 softmmu_ss.add(when: 'CONFIG_ALLWINNER_H3', if_true: files('allwinner-h3-ccu.c'))
diff --git a/hw/misc/trace-events b/hw/misc/trace-events
index 8b68f07765..4d1a0e17af 100644
--- a/hw/misc/trace-events
+++ b/hw/misc/trace-events
@@ -33,6 +33,10 @@ allwinner_r40_dramphy_write(uint64_t offset, uint64_t data, unsigned size) "writ
 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
 
+# allwinner-sramc.c
+allwinner_sramc_read(uint64_t offset, uint64_t data) "offset 0x%" PRIx64 " data 0x%" PRIx64
+allwinner_sramc_write(uint64_t offset, uint64_t data) "offset 0x%" PRIx64 " data 0x%" PRIx64
+
 # avr_power.c
 avr_power_read(uint8_t value) "power_reduc read value:%u"
 avr_power_write(uint8_t value) "power_reduc write value:%u"
diff --git a/include/hw/arm/allwinner-r40.h b/include/hw/arm/allwinner-r40.h
index 5f2d08489e..72710d3edc 100644
--- a/include/hw/arm/allwinner-r40.h
+++ b/include/hw/arm/allwinner-r40.h
@@ -27,6 +27,7 @@
 #include "hw/sd/allwinner-sdhost.h"
 #include "hw/misc/allwinner-r40-ccu.h"
 #include "hw/misc/allwinner-r40-dramc.h"
+#include "hw/misc/allwinner-sramc.h"
 #include "hw/i2c/allwinner-i2c.h"
 #include "hw/net/allwinner_emac.h"
 #include "hw/net/allwinner-sun8i-emac.h"
@@ -38,6 +39,7 @@ enum {
     AW_R40_DEV_SRAM_A2,
     AW_R40_DEV_SRAM_A3,
     AW_R40_DEV_SRAM_A4,
+    AW_R40_DEV_SRAMC,
     AW_R40_DEV_EMAC,
     AW_R40_DEV_MMC0,
     AW_R40_DEV_MMC1,
@@ -102,6 +104,7 @@ struct AwR40State {
 
     ARMCPU cpus[AW_R40_NUM_CPUS];
     const hwaddr *memmap;
+    AwSRAMCState sramc;
     AwA10PITState timer;
     AwSdHostState mmc[AW_R40_NUM_MMCS];
     AwR40ClockCtlState ccu;
diff --git a/include/hw/misc/allwinner-sramc.h b/include/hw/misc/allwinner-sramc.h
new file mode 100644
index 0000000000..66b01b8d04
--- /dev/null
+++ b/include/hw/misc/allwinner-sramc.h
@@ -0,0 +1,69 @@
+/*
+ * Allwinner SRAM 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_SRAMC_H
+#define HW_MISC_ALLWINNER_SRAMC_H
+
+#include "qom/object.h"
+#include "hw/sysbus.h"
+#include "qemu/uuid.h"
+
+/**
+ * Object model
+ * @{
+ */
+#define TYPE_AW_SRAMC               "allwinner-sramc"
+#define TYPE_AW_SRAMC_SUN8I_R40     TYPE_AW_SRAMC "-sun8i-r40"
+OBJECT_DECLARE_TYPE(AwSRAMCState, AwSRAMCClass, AW_SRAMC)
+
+/** @} */
+
+/**
+ * Allwinner SRAMC object instance state
+ */
+struct AwSRAMCState {
+    /*< private >*/
+    SysBusDevice parent_obj;
+    /*< public >*/
+
+    /** Maps I/O registers in physical memory */
+    MemoryRegion iomem;
+
+    /* registers */
+    uint32_t sram_ctl1;
+    uint32_t sram_ver;
+    uint32_t sram_soft_entry_reg0;
+};
+
+/**
+ * Allwinner SRAM Controller class-level struct.
+ *
+ * This struct is filled by each sunxi device specific code
+ * such that the generic code can use this struct to support
+ * all devices.
+ */
+struct AwSRAMCClass {
+    /*< private >*/
+    SysBusDeviceClass parent_class;
+    /*< public >*/
+
+    uint32_t sram_version_code;
+};
+
+#endif /* HW_MISC_ALLWINNER_SRAMC_H */
-- 
2.25.1



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

* [PATCH v4 10/11] tests: avocado: boot_linux_console: Add test case for bpim2u
  2023-05-10 10:29 [PATCH v4 00/11] *** Add allwinner r40 support *** qianfanguijin
                   ` (8 preceding siblings ...)
  2023-05-10 10:30 ` [PATCH v4 09/11] hw: arm: allwinner-sramc: Add SRAM Controller support for R40 qianfanguijin
@ 2023-05-10 10:30 ` qianfanguijin
  2023-05-15 20:09   ` Niek Linnenbank
  2023-05-15 20:25 ` [PATCH v4 00/11] *** Add allwinner r40 support *** Niek Linnenbank
  10 siblings, 1 reply; 31+ messages in thread
From: qianfanguijin @ 2023-05-10 10:30 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 | 176 ++++++++++++++++++++++++++++
 1 file changed, 176 insertions(+)

diff --git a/tests/avocado/boot_linux_console.py b/tests/avocado/boot_linux_console.py
index c0675809e6..6ed660611f 100644
--- a/tests/avocado/boot_linux_console.py
+++ b/tests/avocado/boot_linux_console.py
@@ -769,6 +769,182 @@ 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] 31+ messages in thread

* Re: [PATCH v4 01/11] hw: arm: Add bananapi M2-Ultra and allwinner-r40 support
  2023-05-10 10:29 ` [PATCH v4 01/11] hw: arm: Add bananapi M2-Ultra and allwinner-r40 support qianfanguijin
@ 2023-05-14 18:38   ` Niek Linnenbank
  2023-05-14 18:50   ` Niek Linnenbank
  1 sibling, 0 replies; 31+ messages in thread
From: Niek Linnenbank @ 2023-05-14 18:38 UTC (permalink / raw)
  To: qianfanguijin
  Cc: qemu-arm, qemu-devel, Strahinja Jankovic, Peter Maydell,
	Beniamino Galvani, Philippe Mathieu-Daudé

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

On Wed, May 10, 2023 at 12:30 PM <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).
>
> Signed-off-by: qianfan Zhao <qianfanguijin@163.com>
>
Reviewed-by: Niek Linnenbank <nieklinnenbank@gmail.com>


> ---
>  hw/arm/Kconfig                 |  10 +
>  hw/arm/allwinner-r40.c         | 418 +++++++++++++++++++++++++++++++++
>  hw/arm/bananapi_m2u.c          | 129 ++++++++++
>  hw/arm/meson.build             |   1 +
>  include/hw/arm/allwinner-r40.h | 110 +++++++++
>  5 files changed, 668 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/hw/arm/Kconfig b/hw/arm/Kconfig
> index 2d7c457955..b7a84f6e3f 100644
> --- a/hw/arm/Kconfig
> +++ b/hw/arm/Kconfig
> @@ -374,6 +374,16 @@ config ALLWINNER_H3
>      select USB_EHCI_SYSBUS
>      select SD
>
> +config ALLWINNER_R40
> +    bool
> +    default y if TCG && ARM
> +    select ALLWINNER_A10_PIT
> +    select SERIAL
> +    select ARM_TIMER
> +    select ARM_GIC
> +    select UNIMP
> +    select SD
> +
>  config RASPI
>      bool
>      default y if TCG && ARM
> diff --git a/hw/arm/allwinner-r40.c b/hw/arm/allwinner-r40.c
> new file mode 100644
> index 0000000000..b743d64253
> --- /dev/null
> +++ b/hw/arm/allwinner-r40.c
> @@ -0,0 +1,418 @@
> +/*
> + * 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;
> +    }
> +
> +    /*
> +     * Simulate the behavior of the bootROM, it will change the boot_media
> +     * flag to indicate where the chip is booting from. R40 can boot from
> +     * mmc0 or mmc2, the default value of boot_media is zero
> +     * (SUNXI_BOOTED_FROM_MMC0), let's fix this flag when it is booting
> from
> +     * the others.
> +     */
> +    if (unit == 2) {
> +        head->boot_media = cpu_to_le32(SUNXI_BOOTED_FROM_MMC2);
> +    } else {
> +        head->boot_media = cpu_to_le32(SUNXI_BOOTED_FROM_MMC0);
> +    }
> +
> +    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)
> +{
> +    static const char *mmc_names[AW_R40_NUM_MMCS] = {
> +        "mmc0", "mmc1", "mmc2", "mmc3"
> +    };
> +    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");
> +
> +    for (int i = 0; i < AW_R40_NUM_MMCS; i++) {
> +        object_initialize_child(obj, mmc_names[i], &s->mmc[i],
> +                                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 GICv2
> +     * 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 */
> +    for (int i = 0; i < AW_R40_NUM_MMCS; i++) {
> +        qemu_irq irq = qdev_get_gpio_in(DEVICE(&s->gic),
> +                                        AW_R40_GIC_SPI_MMC0 + i);
> +        const hwaddr addr = s->memmap[AW_R40_DEV_MMC0 + i];
> +
> +        object_property_set_link(OBJECT(&s->mmc[i]), "dma-memory",
> +                                 OBJECT(get_system_memory()),
> &error_fatal);
> +        sysbus_realize(SYS_BUS_DEVICE(&s->mmc[i]), &error_fatal);
> +        sysbus_mmio_map(SYS_BUS_DEVICE(&s->mmc[i]), 0, addr);
> +        sysbus_connect_irq(SYS_BUS_DEVICE(&s->mmc[i]), 0, irq);
> +    }
> +
> +    /* 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..1d49a006b5
> --- /dev/null
> +++ b/hw/arm/bananapi_m2u.c
> @@ -0,0 +1,129 @@
> +/*
> + * 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 "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;
> +
> +/*
> + * R40 can boot from mmc0 and mmc2, and bpim2u has two mmc interface, one
> is
> + * connected to sdcard and another mount an emmc media.
> + * Attach the mmc driver and try loading bootloader.
> + */
> +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;
> +
> +    /* 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 has 4 mmc controllers
> but can
> +     * only booting from mmc0 and mmc2.
> +     */
> +    for (int i = 0; i < AW_R40_NUM_MMCS; i++) {
> +        switch (i) {
> +        case 0:
> +        case 2:
> +            mmc_attach_drive(r40, &r40->mmc[i], i,
> +                             !machine->kernel_filename &&
> !bootroom_loaded,
> +                             &bootroom_loaded);
> +            break;
> +        default:
> +            mmc_attach_drive(r40, &r40->mmc[i], i, false, NULL);
> +            break;
> +        }
> +    }
> +
> +    /* 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..348bf25d6b
> --- /dev/null
> +++ b/include/hw/arm/allwinner-r40.h
> @@ -0,0 +1,110 @@
> +/*
> + * 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.
> + */
> +#define AW_R40_NUM_MMCS         4
> +
> +struct AwR40State {
> +    /*< private >*/
> +    DeviceState parent_obj;
> +    /*< public >*/
> +
> +    ARMCPU cpus[AW_R40_NUM_CPUS];
> +    const hwaddr *memmap;
> +    AwA10PITState timer;
> +    AwSdHostState mmc[AW_R40_NUM_MMCS];
> +    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
>
>

-- 
Niek Linnenbank

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

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

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

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

On Wed, May 10, 2023 at 12:30 PM <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>
>
Reviewed-by: Niek Linnenbank <nieklinnenbank@gmail.com>


> ---
>  hw/arm/allwinner-r40.c              |   8 +-
>  hw/misc/allwinner-r40-ccu.c         | 209 ++++++++++++++++++++++++++++
>  hw/misc/meson.build                 |   1 +
>  include/hw/arm/allwinner-r40.h      |   2 +
>  include/hw/misc/allwinner-r40-ccu.h |  65 +++++++++
>  5 files changed, 284 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 b743d64253..128c0ca470 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 },
> @@ -253,6 +253,8 @@ 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);
> +
>      for (int i = 0; i < AW_R40_NUM_MMCS; i++) {
>          object_initialize_child(obj, mmc_names[i], &s->mmc[i],
>                                  TYPE_AW_SDHOST_SUN5I);
> @@ -367,6 +369,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 */
>      for (int i = 0; i < AW_R40_NUM_MMCS; i++) {
>          qemu_irq irq = qdev_get_gpio_in(DEVICE(&s->gic),
> diff --git a/hw/misc/allwinner-r40-ccu.c b/hw/misc/allwinner-r40-ccu.c
> new file mode 100644
> index 0000000000..d82fee12db
> --- /dev/null
> +++ b/hw/misc/allwinner-r40-ccu.c
> @@ -0,0 +1,209 @@
> +/*
> + * 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 348bf25d6b..3be9dc962b 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"
>
> @@ -79,6 +80,7 @@ struct AwR40State {
>      const hwaddr *memmap;
>      AwA10PITState timer;
>      AwSdHostState mmc[AW_R40_NUM_MMCS];
> +    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
>
>

-- 
Niek Linnenbank

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

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

* Re: [PATCH v4 01/11] hw: arm: Add bananapi M2-Ultra and allwinner-r40 support
  2023-05-10 10:29 ` [PATCH v4 01/11] hw: arm: Add bananapi M2-Ultra and allwinner-r40 support qianfanguijin
  2023-05-14 18:38   ` Niek Linnenbank
@ 2023-05-14 18:50   ` Niek Linnenbank
  2023-05-23  1:21     ` qianfan
  1 sibling, 1 reply; 31+ messages in thread
From: Niek Linnenbank @ 2023-05-14 18:50 UTC (permalink / raw)
  To: qianfanguijin
  Cc: qemu-arm, qemu-devel, Strahinja Jankovic, Peter Maydell,
	Beniamino Galvani, Philippe Mathieu-Daudé

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

Hi Qianfan,


On Wed, May 10, 2023 at 12:30 PM <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).
>
> Signed-off-by: qianfan Zhao <qianfanguijin@163.com>
> ---
>  hw/arm/Kconfig                 |  10 +
>  hw/arm/allwinner-r40.c         | 418 +++++++++++++++++++++++++++++++++
>  hw/arm/bananapi_m2u.c          | 129 ++++++++++
>  hw/arm/meson.build             |   1 +
>  include/hw/arm/allwinner-r40.h | 110 +++++++++
>  5 files changed, 668 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/hw/arm/Kconfig b/hw/arm/Kconfig
> index 2d7c457955..b7a84f6e3f 100644
> --- a/hw/arm/Kconfig
> +++ b/hw/arm/Kconfig
> @@ -374,6 +374,16 @@ config ALLWINNER_H3
>      select USB_EHCI_SYSBUS
>      select SD
>
> +config ALLWINNER_R40
> +    bool
> +    default y if TCG && ARM
> +    select ALLWINNER_A10_PIT
> +    select SERIAL
> +    select ARM_TIMER
> +    select ARM_GIC
> +    select UNIMP
> +    select SD
> +
>  config RASPI
>      bool
>      default y if TCG && ARM
> diff --git a/hw/arm/allwinner-r40.c b/hw/arm/allwinner-r40.c
> new file mode 100644
> index 0000000000..b743d64253
> --- /dev/null
> +++ b/hw/arm/allwinner-r40.c
> @@ -0,0 +1,418 @@
> +/*
> + * 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 },
>

When I started reviewing patch 03 ("hw: allwinner-r40: Complete uart
devices"), I realized I missed pointing out here that
uart0 is inside the unimplemented array, while it is actually implemented
already in this patch.

Regards,
Niek

+    { "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;
> +    }
> +
> +    /*
> +     * Simulate the behavior of the bootROM, it will change the boot_media
> +     * flag to indicate where the chip is booting from. R40 can boot from
> +     * mmc0 or mmc2, the default value of boot_media is zero
> +     * (SUNXI_BOOTED_FROM_MMC0), let's fix this flag when it is booting
> from
> +     * the others.
> +     */
> +    if (unit == 2) {
> +        head->boot_media = cpu_to_le32(SUNXI_BOOTED_FROM_MMC2);
> +    } else {
> +        head->boot_media = cpu_to_le32(SUNXI_BOOTED_FROM_MMC0);
> +    }
> +
> +    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)
> +{
> +    static const char *mmc_names[AW_R40_NUM_MMCS] = {
> +        "mmc0", "mmc1", "mmc2", "mmc3"
> +    };
> +    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");
> +
> +    for (int i = 0; i < AW_R40_NUM_MMCS; i++) {
> +        object_initialize_child(obj, mmc_names[i], &s->mmc[i],
> +                                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 GICv2
> +     * 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 */
> +    for (int i = 0; i < AW_R40_NUM_MMCS; i++) {
> +        qemu_irq irq = qdev_get_gpio_in(DEVICE(&s->gic),
> +                                        AW_R40_GIC_SPI_MMC0 + i);
> +        const hwaddr addr = s->memmap[AW_R40_DEV_MMC0 + i];
> +
> +        object_property_set_link(OBJECT(&s->mmc[i]), "dma-memory",
> +                                 OBJECT(get_system_memory()),
> &error_fatal);
> +        sysbus_realize(SYS_BUS_DEVICE(&s->mmc[i]), &error_fatal);
> +        sysbus_mmio_map(SYS_BUS_DEVICE(&s->mmc[i]), 0, addr);
> +        sysbus_connect_irq(SYS_BUS_DEVICE(&s->mmc[i]), 0, irq);
> +    }
> +
> +    /* 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..1d49a006b5
> --- /dev/null
> +++ b/hw/arm/bananapi_m2u.c
> @@ -0,0 +1,129 @@
> +/*
> + * 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 "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;
> +
> +/*
> + * R40 can boot from mmc0 and mmc2, and bpim2u has two mmc interface, one
> is
> + * connected to sdcard and another mount an emmc media.
> + * Attach the mmc driver and try loading bootloader.
> + */
> +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;
> +
> +    /* 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 has 4 mmc controllers
> but can
> +     * only booting from mmc0 and mmc2.
> +     */
> +    for (int i = 0; i < AW_R40_NUM_MMCS; i++) {
> +        switch (i) {
> +        case 0:
> +        case 2:
> +            mmc_attach_drive(r40, &r40->mmc[i], i,
> +                             !machine->kernel_filename &&
> !bootroom_loaded,
> +                             &bootroom_loaded);
> +            break;
> +        default:
> +            mmc_attach_drive(r40, &r40->mmc[i], i, false, NULL);
> +            break;
> +        }
> +    }
> +
> +    /* 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..348bf25d6b
> --- /dev/null
> +++ b/include/hw/arm/allwinner-r40.h
> @@ -0,0 +1,110 @@
> +/*
> + * 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.
> + */
> +#define AW_R40_NUM_MMCS         4
> +
> +struct AwR40State {
> +    /*< private >*/
> +    DeviceState parent_obj;
> +    /*< public >*/
> +
> +    ARMCPU cpus[AW_R40_NUM_CPUS];
> +    const hwaddr *memmap;
> +    AwA10PITState timer;
> +    AwSdHostState mmc[AW_R40_NUM_MMCS];
> +    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
>
>

-- 
Niek Linnenbank

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

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

* Re: [PATCH v4 03/11] hw: allwinner-r40: Complete uart devices
  2023-05-10 10:29 ` [PATCH v4 03/11] hw: allwinner-r40: Complete uart devices qianfanguijin
@ 2023-05-14 18:55   ` Niek Linnenbank
  2023-05-23  1:24     ` qianfan
  2023-05-23  3:00     ` qianfan
  0 siblings, 2 replies; 31+ messages in thread
From: Niek Linnenbank @ 2023-05-14 18:55 UTC (permalink / raw)
  To: qianfanguijin
  Cc: qemu-arm, qemu-devel, Strahinja Jankovic, Peter Maydell,
	Beniamino Galvani, Philippe Mathieu-Daudé

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

Hi Qianfan,


On Wed, May 10, 2023 at 12:30 PM <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         | 31 ++++++++++++++++++++++++++++---
>  include/hw/arm/allwinner-r40.h |  8 ++++++++
>  2 files changed, 36 insertions(+), 3 deletions(-)
>
> diff --git a/hw/arm/allwinner-r40.c b/hw/arm/allwinner-r40.c
> index 128c0ca470..537a90b23d 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,
>

After adding the uarts to the memory map here, you should remove them from
the unimplemented array.

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

Since you put the addition of UART1-7 in this patch, probably it makes
sense to have adding the lines 'AW_R40_GIC_SPI_UART1/2/3' also part of this
patch.

With the two above remarks resolved, the patch looks good to me.

Reviewed-by: Niek Linnenbank <nieklinnenbank@gmail.com>

Regards,
Niek

> +    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,
> @@ -387,9 +398,23 @@ static void allwinner_r40_realize(DeviceState *dev,
> Error **errp)
>      }
>
>      /* 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);
> +    for (int i = 0; i < AW_R40_NUM_UARTS; i++) {
> +        static const int uart_irqs[AW_R40_NUM_UARTS] = {
> +            AW_R40_GIC_SPI_UART0,
> +            AW_R40_GIC_SPI_UART1,
> +            AW_R40_GIC_SPI_UART2,
> +            AW_R40_GIC_SPI_UART3,
> +            AW_R40_GIC_SPI_UART4,
> +            AW_R40_GIC_SPI_UART5,
> +            AW_R40_GIC_SPI_UART6,
> +            AW_R40_GIC_SPI_UART7,
> +        };
> +        const hwaddr addr = s->memmap[AW_R40_DEV_UART0 + i];
> +
> +        serial_mm_init(get_system_memory(), addr, 2,
> +                       qdev_get_gpio_in(DEVICE(&s->gic), uart_irqs[i]),
> +                       115200, serial_hd(i), 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 3be9dc962b..959b5dc4e0 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,
> @@ -70,6 +77,7 @@ OBJECT_DECLARE_SIMPLE_TYPE(AwR40State, AW_R40)
>   * which are currently emulated by the R40 SoC code.
>   */
>  #define AW_R40_NUM_MMCS         4
> +#define AW_R40_NUM_UARTS        8
>
>  struct AwR40State {
>      /*< private >*/
> --
> 2.25.1
>
>

-- 
Niek Linnenbank

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

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

* Re: [PATCH v4 04/11] hw: arm: allwinner-r40: Add i2c0 device
  2023-05-10 10:29 ` [PATCH v4 04/11] hw: arm: allwinner-r40: Add i2c0 device qianfanguijin
@ 2023-05-14 18:57   ` Niek Linnenbank
  0 siblings, 0 replies; 31+ messages in thread
From: Niek Linnenbank @ 2023-05-14 18:57 UTC (permalink / raw)
  To: qianfanguijin
  Cc: qemu-arm, qemu-devel, Strahinja Jankovic, Peter Maydell,
	Beniamino Galvani, Philippe Mathieu-Daudé

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

On Wed, May 10, 2023 at 12:30 PM <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>
>
Reviewed-by: Niek Linnenbank <nieklinnenbank@gmail.com>

> ---
>  hw/arm/allwinner-r40.c         | 11 ++++++++++-
>  include/hw/arm/allwinner-r40.h |  3 +++
>  2 files changed, 13 insertions(+), 1 deletion(-)
>
> diff --git a/hw/arm/allwinner-r40.c b/hw/arm/allwinner-r40.c
> index 537a90b23d..4bc582630c 100644
> --- a/hw/arm/allwinner-r40.c
> +++ b/hw/arm/allwinner-r40.c
> @@ -52,6 +52,7 @@ 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_GIC_DIST]   = 0x01c81000,
>      [AW_R40_DEV_GIC_CPU]    = 0x01c82000,
>      [AW_R40_DEV_GIC_HYP]    = 0x01c84000,
> @@ -115,7 +116,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 },
> @@ -167,6 +167,7 @@ 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_UART4     = 17,
>      AW_R40_GIC_SPI_UART5     = 18,
>      AW_R40_GIC_SPI_UART6     = 19,
> @@ -270,6 +271,8 @@ static void allwinner_r40_init(Object *obj)
>          object_initialize_child(obj, mmc_names[i], &s->mmc[i],
>                                  TYPE_AW_SDHOST_SUN5I);
>      }
> +
> +    object_initialize_child(obj, "twi0", &s->i2c0, TYPE_AW_I2C_SUN6I);
>  }
>
>  static void allwinner_r40_realize(DeviceState *dev, Error **errp)
> @@ -416,6 +419,12 @@ static void allwinner_r40_realize(DeviceState *dev,
> Error **errp)
>                         115200, serial_hd(i), 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));
> +
>      /* 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 959b5dc4e0..95366f4eee 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,7 @@ enum {
>      AW_R40_DEV_UART5,
>      AW_R40_DEV_UART6,
>      AW_R40_DEV_UART7,
> +    AW_R40_DEV_TWI0,
>      AW_R40_DEV_GIC_DIST,
>      AW_R40_DEV_GIC_CPU,
>      AW_R40_DEV_GIC_HYP,
> @@ -89,6 +91,7 @@ struct AwR40State {
>      AwA10PITState timer;
>      AwSdHostState mmc[AW_R40_NUM_MMCS];
>      AwR40ClockCtlState ccu;
> +    AWI2CState i2c0;
>      GICState gic;
>      MemoryRegion sram_a1;
>      MemoryRegion sram_a2;
> --
> 2.25.1
>
>

-- 
Niek Linnenbank

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

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

* Re: [PATCH v4 05/11] hw/misc: Rename axp209 to axp22x and add support AXP221 PMU
  2023-05-10 10:29 ` [PATCH v4 05/11] hw/misc: Rename axp209 to axp22x and add support AXP221 PMU qianfanguijin
@ 2023-05-15 19:29   ` Niek Linnenbank
  2023-05-23  0:40     ` qianfan
  0 siblings, 1 reply; 31+ messages in thread
From: Niek Linnenbank @ 2023-05-15 19:29 UTC (permalink / raw)
  To: qianfanguijin
  Cc: qemu-arm, qemu-devel, Strahinja Jankovic, Peter Maydell,
	Beniamino Galvani, Philippe Mathieu-Daudé

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

Hi Qianfan,

Good idea indeed to turn this driver into a more generic one. If possible,
its best to re-use code rather than adding new.

On Wed, May 10, 2023 at 12:30 PM <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>
> ---
>  hw/arm/Kconfig        |   3 +-
>  hw/arm/bananapi_m2u.c |   6 +
>  hw/misc/Kconfig       |   2 +-
>  hw/misc/axp209.c      | 238 -----------------------------------
>  hw/misc/axp2xx.c      | 283 ++++++++++++++++++++++++++++++++++++++++++
>  hw/misc/meson.build   |   2 +-
>  hw/misc/trace-events  |   8 +-
>  7 files changed, 297 insertions(+), 245 deletions(-)
>  delete mode 100644 hw/misc/axp209.c
>  create mode 100644 hw/misc/axp2xx.c
>
> diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig
> index b7a84f6e3f..bad4ea158c 100644
> --- a/hw/arm/Kconfig
> +++ b/hw/arm/Kconfig
> @@ -355,7 +355,7 @@ config ALLWINNER_A10
>      select ALLWINNER_WDT
>      select ALLWINNER_EMAC
>      select ALLWINNER_I2C
> -    select AXP209_PMU
> +    select AXP2XX_PMU
>      select SERIAL
>      select UNIMP
>
> @@ -378,6 +378,7 @@ config ALLWINNER_R40
>      bool
>      default y if TCG && ARM
>      select ALLWINNER_A10_PIT
> +    select AXP2XX_PMU
>      select SERIAL
>      select ARM_TIMER
>      select ARM_GIC
> diff --git a/hw/arm/bananapi_m2u.c b/hw/arm/bananapi_m2u.c
> index 1d49a006b5..9c5360a41b 100644
> --- a/hw/arm/bananapi_m2u.c
> +++ b/hw/arm/bananapi_m2u.c
> @@ -23,6 +23,7 @@
>  #include "qapi/error.h"
>  #include "qemu/error-report.h"
>  #include "hw/boards.h"
> +#include "hw/i2c/i2c.h"
>  #include "hw/qdev-properties.h"
>  #include "hw/arm/allwinner-r40.h"
>
> @@ -61,6 +62,7 @@ static void bpim2u_init(MachineState *machine)
>  {
>      bool bootroom_loaded = false;
>      AwR40State *r40;
> +    I2CBus *i2c;
>
>      /* BIOS is not supported by this board */
>      if (machine->firmware) {
> @@ -104,6 +106,10 @@ static void bpim2u_init(MachineState *machine)
>          }
>      }
>
> +    /* 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..efeb430a6c 100644
> --- a/hw/misc/Kconfig
> +++ b/hw/misc/Kconfig
> @@ -176,7 +176,7 @@ config ALLWINNER_A10_CCM
>  config ALLWINNER_A10_DRAMC
>      bool
>
> -config AXP209_PMU
> +config AXP2XX_PMU
>      bool
>      depends on I2C
>
> diff --git a/hw/misc/axp209.c b/hw/misc/axp209.c
> deleted file mode 100644
> index 2908ed99a6..0000000000
> --- a/hw/misc/axp209.c
> +++ /dev/null
> @@ -1,238 +0,0 @@
> -/*
> - * AXP-209 PMU Emulation
> - *
> - * Copyright (C) 2022 Strahinja Jankovic <strahinja.p.jankovic@gmail.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 "trace.h"
> -#include "hw/i2c/i2c.h"
> -#include "migration/vmstate.h"
> -
> -#define TYPE_AXP209_PMU "axp209_pmu"
> -
> -#define AXP209(obj) \
> -    OBJECT_CHECK(AXP209I2CState, (obj), TYPE_AXP209_PMU)
> -
> -/* registers */
> -enum {
> -    REG_POWER_STATUS = 0x0u,
> -    REG_OPERATING_MODE,
> -    REG_OTG_VBUS_STATUS,
> -    REG_CHIP_VERSION,
> -    REG_DATA_CACHE_0,
> -    REG_DATA_CACHE_1,
> -    REG_DATA_CACHE_2,
> -    REG_DATA_CACHE_3,
> -    REG_DATA_CACHE_4,
> -    REG_DATA_CACHE_5,
> -    REG_DATA_CACHE_6,
> -    REG_DATA_CACHE_7,
> -    REG_DATA_CACHE_8,
> -    REG_DATA_CACHE_9,
> -    REG_DATA_CACHE_A,
> -    REG_DATA_CACHE_B,
> -    REG_POWER_OUTPUT_CTRL = 0x12u,
> -    REG_DC_DC2_OUT_V_CTRL = 0x23u,
> -    REG_DC_DC2_DVS_CTRL = 0x25u,
> -    REG_DC_DC3_OUT_V_CTRL = 0x27u,
> -    REG_LDO2_4_OUT_V_CTRL,
> -    REG_LDO3_OUT_V_CTRL,
> -    REG_VBUS_CH_MGMT = 0x30u,
> -    REG_SHUTDOWN_V_CTRL,
> -    REG_SHUTDOWN_CTRL,
> -    REG_CHARGE_CTRL_1,
> -    REG_CHARGE_CTRL_2,
> -    REG_SPARE_CHARGE_CTRL,
> -    REG_PEK_KEY_CTRL,
> -    REG_DC_DC_FREQ_SET,
> -    REG_CHR_TEMP_TH_SET,
> -    REG_CHR_HIGH_TEMP_TH_CTRL,
> -    REG_IPSOUT_WARN_L1,
> -    REG_IPSOUT_WARN_L2,
> -    REG_DISCHR_TEMP_TH_SET,
> -    REG_DISCHR_HIGH_TEMP_TH_CTRL,
> -    REG_IRQ_BANK_1_CTRL = 0x40u,
> -    REG_IRQ_BANK_2_CTRL,
> -    REG_IRQ_BANK_3_CTRL,
> -    REG_IRQ_BANK_4_CTRL,
> -    REG_IRQ_BANK_5_CTRL,
> -    REG_IRQ_BANK_1_STAT = 0x48u,
> -    REG_IRQ_BANK_2_STAT,
> -    REG_IRQ_BANK_3_STAT,
> -    REG_IRQ_BANK_4_STAT,
> -    REG_IRQ_BANK_5_STAT,
> -    REG_ADC_ACIN_V_H = 0x56u,
> -    REG_ADC_ACIN_V_L,
> -    REG_ADC_ACIN_CURR_H,
> -    REG_ADC_ACIN_CURR_L,
> -    REG_ADC_VBUS_V_H,
> -    REG_ADC_VBUS_V_L,
> -    REG_ADC_VBUS_CURR_H,
> -    REG_ADC_VBUS_CURR_L,
> -    REG_ADC_INT_TEMP_H,
> -    REG_ADC_INT_TEMP_L,
> -    REG_ADC_TEMP_SENS_V_H = 0x62u,
> -    REG_ADC_TEMP_SENS_V_L,
> -    REG_ADC_BAT_V_H = 0x78u,
> -    REG_ADC_BAT_V_L,
> -    REG_ADC_BAT_DISCHR_CURR_H,
> -    REG_ADC_BAT_DISCHR_CURR_L,
> -    REG_ADC_BAT_CHR_CURR_H,
> -    REG_ADC_BAT_CHR_CURR_L,
> -    REG_ADC_IPSOUT_V_H,
> -    REG_ADC_IPSOUT_V_L,
> -    REG_DC_DC_MOD_SEL = 0x80u,
> -    REG_ADC_EN_1,
> -    REG_ADC_EN_2,
> -    REG_ADC_SR_CTRL,
> -    REG_ADC_IN_RANGE,
> -    REG_GPIO1_ADC_IRQ_RISING_TH,
> -    REG_GPIO1_ADC_IRQ_FALLING_TH,
> -    REG_TIMER_CTRL = 0x8au,
> -    REG_VBUS_CTRL_MON_SRP,
> -    REG_OVER_TEMP_SHUTDOWN = 0x8fu,
> -    REG_GPIO0_FEAT_SET,
> -    REG_GPIO_OUT_HIGH_SET,
> -    REG_GPIO1_FEAT_SET,
> -    REG_GPIO2_FEAT_SET,
> -    REG_GPIO_SIG_STATE_SET_MON,
> -    REG_GPIO3_SET,
> -    REG_COULOMB_CNTR_CTRL = 0xb8u,
> -    REG_POWER_MEAS_RES,
> -    NR_REGS
> -};
> -
> -#define AXP209_CHIP_VERSION_ID             (0x01)
> -#define AXP209_DC_DC2_OUT_V_CTRL_RESET     (0x16)
> -#define AXP209_IRQ_BANK_1_CTRL_RESET       (0xd8)
> -
> -/* A simple I2C slave which returns values of ID or CNT register. */
> -typedef struct AXP209I2CState {
> -    /*< 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 */
> -} AXP209I2CState;
> -
> -/* Reset all counters and load ID register */
> -static void axp209_reset_enter(Object *obj, ResetType type)
> -{
> -    AXP209I2CState *s = AXP209(obj);
> -
> -    memset(s->regs, 0, NR_REGS);
> -    s->ptr = 0;
> -    s->count = 0;
> -    s->regs[REG_CHIP_VERSION] = AXP209_CHIP_VERSION_ID;
> -    s->regs[REG_DC_DC2_OUT_V_CTRL] = AXP209_DC_DC2_OUT_V_CTRL_RESET;
> -    s->regs[REG_IRQ_BANK_1_CTRL] = AXP209_IRQ_BANK_1_CTRL_RESET;
> -}
> -
> -/* Handle events from master. */
> -static int axp209_event(I2CSlave *i2c, enum i2c_event event)
> -{
> -    AXP209I2CState *s = AXP209(i2c);
> -
> -    s->count = 0;
> -
> -    return 0;
> -}
> -
> -/* Called when master requests read */
> -static uint8_t axp209_rx(I2CSlave *i2c)
> -{
> -    AXP209I2CState *s = AXP209(i2c);
> -    uint8_t ret = 0xff;
> -
> -    if (s->ptr < NR_REGS) {
> -        ret = s->regs[s->ptr++];
> -    }
> -
> -    trace_axp209_rx(s->ptr - 1, ret);
> -
> -    return ret;
> -}
> -
> -/*
> - * Called when master sends write.
> - * Update ptr with byte 0, then perform write with second byte.
> - */
> -static int axp209_tx(I2CSlave *i2c, uint8_t data)
> -{
> -    AXP209I2CState *s = AXP209(i2c);
> -
> -    if (s->count == 0) {
> -        /* Store register address */
> -        s->ptr = data;
> -        s->count++;
> -        trace_axp209_select(data);
> -    } else {
> -        trace_axp209_tx(s->ptr, data);
> -        if (s->ptr == REG_DC_DC2_OUT_V_CTRL) {
> -            s->regs[s->ptr++] = data;
> -        }
> -    }
> -
> -    return 0;
> -}
> -
> -static const VMStateDescription vmstate_axp209 = {
> -    .name = TYPE_AXP209_PMU,
> -    .version_id = 1,
> -    .fields = (VMStateField[]) {
> -        VMSTATE_UINT8_ARRAY(regs, AXP209I2CState, NR_REGS),
> -        VMSTATE_UINT8(count, AXP209I2CState),
> -        VMSTATE_UINT8(ptr, AXP209I2CState),
> -        VMSTATE_END_OF_LIST()
> -    }
> -};
> -
> -static void axp209_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 = axp209_reset_enter;
> -    dc->vmsd = &vmstate_axp209;
> -    isc->event = axp209_event;
> -    isc->recv = axp209_rx;
> -    isc->send = axp209_tx;
> -}
> -
> -static const TypeInfo axp209_info = {
> -    .name = TYPE_AXP209_PMU,
> -    .parent = TYPE_I2C_SLAVE,
> -    .instance_size = sizeof(AXP209I2CState),
> -    .class_init = axp209_class_init
> -};
> -
> -static void axp209_register_devices(void)
> -{
> -    type_register_static(&axp209_info);
> -}
> -
> -type_init(axp209_register_devices);
> diff --git a/hw/misc/axp2xx.c b/hw/misc/axp2xx.c
> new file mode 100644
> index 0000000000..52a6ffc7f3
> --- /dev/null
> +++ b/hw/misc/axp2xx.c
> @@ -0,0 +1,283 @@
> +/*
> + * AXP-2XX PMU Emulation, supported lists:
> + *   AXP209
> + *   AXP221
> + *
> + * Copyright (C) 2022 Strahinja Jankovic <strahinja.p.jankovic@gmail.com>
> + * 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 "qom/object.h"
> +#include "trace.h"
> +#include "hw/i2c/i2c.h"
> +#include "migration/vmstate.h"
> +
> +#define TYPE_AXP2XX     "axp2xx_pmu"
> +#define TYPE_AXP209_PMU "axp209_pmu"
> +#define TYPE_AXP221_PMU "axp221_pmu"
> +
> +OBJECT_DECLARE_TYPE(AXP2xxI2CState, AXP2xxClass, AXP2XX)
> +
> +#define NR_REGS                            (0xff)
> +
> +/* A simple I2C slave which returns values of ID or CNT register. */
> +typedef struct AXP2xxI2CState {
> +    /*< 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 */
> +} AXP2xxI2CState;
> +
> +typedef struct AXP2xxClass {
> +    /*< private >*/
> +    I2CSlaveClass parent_class;
> +    /*< public >*/
> +    void (*reset_enter)(AXP2xxI2CState *s, ResetType type);
> +} AXP2xxClass;
> +
> +#define AXP209_CHIP_VERSION_ID             (0x01)
> +#define AXP209_DC_DC2_OUT_V_CTRL_RESET     (0x16)
> +
> +/* Reset all counters and load ID register */
> +static void axp209_reset_enter(AXP2xxI2CState *s, ResetType type)
> +{
> +    memset(s->regs, 0, NR_REGS);
> +    s->ptr = 0;
> +    s->count = 0;
> +
> +    s->regs[0x03] = AXP209_CHIP_VERSION_ID;
> +    s->regs[0x23] = AXP209_DC_DC2_OUT_V_CTRL_RESET;
> +
> +    s->regs[0x30] = 0x60;
> +    s->regs[0x32] = 0x46;
> +    s->regs[0x34] = 0x41;
> +    s->regs[0x35] = 0x22;
> +    s->regs[0x36] = 0x5d;
> +    s->regs[0x37] = 0x08;
> +    s->regs[0x38] = 0xa5;
> +    s->regs[0x39] = 0x1f;
> +    s->regs[0x3a] = 0x68;
> +    s->regs[0x3b] = 0x5f;
> +    s->regs[0x3c] = 0xfc;
> +    s->regs[0x3d] = 0x16;
> +    s->regs[0x40] = 0xd8;
> +    s->regs[0x42] = 0xff;
> +    s->regs[0x43] = 0x3b;
> +    s->regs[0x80] = 0xe0;
> +    s->regs[0x82] = 0x83;
> +    s->regs[0x83] = 0x80;
> +    s->regs[0x84] = 0x32;
> +    s->regs[0x86] = 0xff;
> +    s->regs[0x90] = 0x07;
> +    s->regs[0x91] = 0xa0;
> +    s->regs[0x92] = 0x07;
> +    s->regs[0x93] = 0x07;
> +}
> +
> +#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(AXP2xxI2CState *s, ResetType type)
> +{
> +    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;
>

Would it be possible to change this array to be more human readable?

Perhaps using the register names from the datasheet (if available or some
other source) as the names for defines / enums or just as comments can help.
That way we can know what the contents mean.


> +}
> +
> +static void axp2xx_reset_enter(Object *obj, ResetType type)
> +{
> +    AXP2xxI2CState *s = AXP2XX(obj);
> +    AXP2xxClass *sc = AXP2XX_GET_CLASS(s);
> +
> +    sc->reset_enter(s, type);
> +}
> +
> +/* Handle events from master. */
> +static int axp2xx_event(I2CSlave *i2c, enum i2c_event event)
> +{
> +    AXP2xxI2CState *s = AXP2XX(i2c);
> +
> +    s->count = 0;
> +
> +    return 0;
> +}
> +
> +/* Called when master requests read */
> +static uint8_t axp2xx_rx(I2CSlave *i2c)
> +{
> +    AXP2xxI2CState *s = AXP2XX(i2c);
> +    uint8_t ret = 0xff;
> +
> +    if (s->ptr < NR_REGS) {
> +        ret = s->regs[s->ptr++];
> +    }
> +
> +    trace_axp2xx_rx(s->ptr - 1, ret);
> +
> +    return ret;
> +}
> +
> +/*
> + * Called when master sends write.
> + * Update ptr with byte 0, then perform write with second byte.
> + */
> +static int axp2xx_tx(I2CSlave *i2c, uint8_t data)
> +{
> +    AXP2xxI2CState *s = AXP2XX(i2c);
> +
> +    if (s->count == 0) {
> +        /* Store register address */
> +        s->ptr = data;
> +        s->count++;
> +        trace_axp2xx_select(data);
> +    } else {
> +        trace_axp2xx_tx(s->ptr, data);
> +        s->regs[s->ptr++] = data;
> +    }
> +
> +    return 0;
> +}
> +
> +static const VMStateDescription vmstate_axp2xx = {
> +    .name = TYPE_AXP209_PMU,
>

Should the .name value be TYPE_AXP2xx_PMU instead, since we're describing
the new generic axp2xx here?

+    .version_id = 1,
> +    .fields = (VMStateField[]) {
> +        VMSTATE_UINT8_ARRAY(regs, AXP2xxI2CState, NR_REGS),
> +        VMSTATE_UINT8(count, AXP2xxI2CState),
> +        VMSTATE_UINT8(ptr, AXP2xxI2CState),
>

In the 'struct AXPxxI2CState'  definition above, the order of definition
is: regs, ptr, count. For consistency, ptr and count should then be swapped
here to match.


> +        VMSTATE_END_OF_LIST()
>

Another question that comes to my mind here: is it allowed by the migration
API to have a new axp2xx device
show up while the previous axp209 disappears? (in case for the allwinner
A10 cubieboard).

My expectation with this code would be that during such a migration, any
current state of the axp209 device would be lost
and a new fresh device would show up to the guest.

I don't know enough about the migration support to tell if this is OK or
not.
If another reviewer could help clarify, that would be great.


> +    }
> +};
> +
> +static void axp2xx_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 = axp2xx_reset_enter;
> +    dc->vmsd = &vmstate_axp2xx;
> +    isc->event = axp2xx_event;
> +    isc->recv = axp2xx_rx;
> +    isc->send = axp2xx_tx;
> +}
> +
> +static const TypeInfo axp2xx_info = {
> +    .name = TYPE_AXP2XX,
> +    .parent = TYPE_I2C_SLAVE,
> +    .instance_size = sizeof(AXP2xxI2CState),
> +    .class_size = sizeof(AXP2xxClass),
> +    .class_init = axp2xx_class_init,
> +    .abstract = true,
> +};
> +
> +static void axp209_class_init(ObjectClass *oc, void *data)
> +{
> +    AXP2xxClass *sc = AXP2XX_CLASS(oc);
> +
> +    sc->reset_enter = axp209_reset_enter;
> +}
> +
> +static const TypeInfo axp209_info = {
> +    .name = TYPE_AXP209_PMU,
> +    .parent = TYPE_AXP2XX,
> +    .class_init = axp209_class_init
> +};
> +
> +static void axp221_class_init(ObjectClass *oc, void *data)
> +{
> +    AXP2xxClass *sc = AXP2XX_CLASS(oc);
> +
> +    sc->reset_enter = axp221_reset_enter;
> +}
> +
> +static const TypeInfo axp221_info = {
> +    .name = TYPE_AXP221_PMU,
> +    .parent = TYPE_AXP2XX,
> +    .class_init = axp221_class_init,
> +};
> +
> +static void axp2xx_register_devices(void)
> +{
> +    type_register_static(&axp2xx_info);
> +    type_register_static(&axp209_info);
> +    type_register_static(&axp221_info);
> +}
> +
> +type_init(axp2xx_register_devices);
> diff --git a/hw/misc/meson.build b/hw/misc/meson.build
> index 96e35f1cdb..1db0343333 100644
> --- a/hw/misc/meson.build
> +++ b/hw/misc/meson.build
> @@ -45,7 +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_AXP209_PMU', if_true: files('axp209.c'))
> +softmmu_ss.add(when: 'CONFIG_AXP2XX_PMU', if_true: files('axp2xx.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..24cdec83fe 100644
> --- a/hw/misc/trace-events
> +++ b/hw/misc/trace-events
> @@ -23,10 +23,10 @@ allwinner_sid_write(uint64_t offset, uint64_t data,
> unsigned size) "offset 0x%"
>  avr_power_read(uint8_t value) "power_reduc read value:%u"
>  avr_power_write(uint8_t value) "power_reduc write value:%u"
>
> -# axp209.c
> -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
> +# axp2xx
> +axp2xx_rx(uint8_t reg, uint8_t data) "Read reg 0x%" PRIx8 " : 0x%" PRIx8
> +axp2xx_select(uint8_t reg) "Accessing reg 0x%" PRIx8
> +axp2xx_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"
> --
> 2.25.1
>
>

-- 
Niek Linnenbank

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

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

* Re: [PATCH v4 06/11] hw/arm/allwinner-r40: add SDRAM controller device
  2023-05-10 10:29 ` [PATCH v4 06/11] hw/arm/allwinner-r40: add SDRAM controller device qianfanguijin
@ 2023-05-15 19:47   ` Niek Linnenbank
  2023-05-23  0:53     ` qianfan
  0 siblings, 1 reply; 31+ messages in thread
From: Niek Linnenbank @ 2023-05-15 19:47 UTC (permalink / raw)
  To: qianfanguijin
  Cc: qemu-arm, qemu-devel, Strahinja Jankovic, Peter Maydell,
	Beniamino Galvani, Philippe Mathieu-Daudé

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

On Wed, May 10, 2023 at 12:30 PM <qianfanguijin@163.com> wrote:

> 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                |  21 +-
>  hw/arm/bananapi_m2u.c                 |   7 +
>  hw/misc/allwinner-r40-dramc.c         | 513 ++++++++++++++++++++++++++
>  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, 674 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 4bc582630c..0e4542d35f 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[] = {
> @@ -53,6 +54,9 @@ const hwaddr allwinner_r40_memmap[] = {
>      [AW_R40_DEV_UART6]      = 0x01c29800,
>      [AW_R40_DEV_UART7]      = 0x01c29c00,
>      [AW_R40_DEV_TWI0]       = 0x01c2ac00,
> +    [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, "twi0", &s->i2c0, 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)
> @@ -425,6 +433,15 @@ static void allwinner_r40_realize(DeviceState *dev,
> Error **errp)
>      sysbus_connect_irq(SYS_BUS_DEVICE(&s->i2c0), 0,
>                         qdev_get_gpio_in(DEVICE(&s->gic),
> AW_R40_GIC_SPI_TWI0));
>
> +    /* 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 9c5360a41b..20a4550c68 100644
> --- a/hw/arm/bananapi_m2u.c
> +++ b/hw/arm/bananapi_m2u.c
> @@ -85,6 +85,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..b102bcdaba
> --- /dev/null
> +++ b/hw/misc/allwinner-r40-dramc.c
> @@ -0,0 +1,513 @@
> +/*
> + * 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) {
>
Since the driver only supports up to 1GiB, you could decide to just remove
this if(..).


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

Same here: since only up to 1GiB is supported, do we need the DRAMCELLS
lines?

+}
> +
> +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 1db0343333..b04d43e05a 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_AXP2XX_PMU', if_true: files('axp2xx.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/hw/misc/trace-events b/hw/misc/trace-events
> index 24cdec83fe..8b68f07765 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 95366f4eee..8243e8903b 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"
> @@ -54,7 +55,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)
> @@ -86,11 +90,18 @@ 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;
>      AwSdHostState mmc[AW_R40_NUM_MMCS];
>      AwR40ClockCtlState ccu;
> +    AwR40DramCtlState dramc;
>      AWI2CState i2c0;
>      GICState gic;
>      MemoryRegion sram_a1;
> diff --git a/include/hw/misc/allwinner-r40-dramc.h
> b/include/hw/misc/allwinner-r40-dramc.h
> new file mode 100644
> index 0000000000..6a1a3a7893
> --- /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]; /**< DRAMCOM registers */
> +    uint32_t dramctl[AW_R40_DRAMCTL_REGS_NUM]; /**< DRAMCTL registers */
> +    uint32_t dramphy[AW_R40_DRAMPHY_REGS_NUM] ;/**< DRAMPHY registers */
> +
> +    /** @} */
> +
> +};
> +
> +#endif /* HW_MISC_ALLWINNER_R40_DRAMC_H */
> --
> 2.25.1
>
>
Looks good to me.

Reviewed-by: Niek Linnenbank <nieklinnenbank@gmail.com>

-- 
Niek Linnenbank

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

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

* Re: [PATCH v4 07/11] hw: sd: allwinner-sdhost: Add sun50i-a64 SoC support
  2023-05-10 10:30 ` [PATCH v4 07/11] hw: sd: allwinner-sdhost: Add sun50i-a64 SoC support qianfanguijin
@ 2023-05-15 19:54   ` Niek Linnenbank
  2023-05-23  0:53     ` qianfan
  0 siblings, 1 reply; 31+ messages in thread
From: Niek Linnenbank @ 2023-05-15 19:54 UTC (permalink / raw)
  To: qianfanguijin
  Cc: qemu-arm, qemu-devel, Strahinja Jankovic, Peter Maydell,
	Beniamino Galvani, Philippe Mathieu-Daudé

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

On Wed, May 10, 2023 at 12:30 PM <qianfanguijin@163.com> wrote:

> 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.
>
> Also fix allwinner-r40's mmc controller type.
>
> Signed-off-by: qianfan Zhao <qianfanguijin@163.com>
> ---
>  hw/arm/allwinner-r40.c           |  2 +-
>  hw/sd/allwinner-sdhost.c         | 70 ++++++++++++++++++++++++++++++--
>  include/hw/sd/allwinner-sdhost.h |  9 ++++
>  3 files changed, 77 insertions(+), 4 deletions(-)
>
> diff --git a/hw/arm/allwinner-r40.c b/hw/arm/allwinner-r40.c
> index 0e4542d35f..b148c56449 100644
> --- a/hw/arm/allwinner-r40.c
> +++ b/hw/arm/allwinner-r40.c
> @@ -271,7 +271,7 @@ static void allwinner_r40_init(Object *obj)
>
>      for (int i = 0; i < AW_R40_NUM_MMCS; i++) {
>          object_initialize_child(obj, mmc_names[i], &s->mmc[i],
> -                                TYPE_AW_SDHOST_SUN5I);
> +                                TYPE_AW_SDHOST_SUN50I_A64);
>      }
>
>      object_initialize_child(obj, "twi0", &s->i2c0, TYPE_AW_I2C_SUN6I);
> diff --git a/hw/sd/allwinner-sdhost.c b/hw/sd/allwinner-sdhost.c
> index 92a0f42708..f4fa2d179b 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,
>  };
>
> @@ -459,6 +461,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) {
> @@ -577,13 +580,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;
>  }
> @@ -602,6 +616,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);
>
> @@ -725,10 +740,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;
>      }
>  }
>
> @@ -777,6 +803,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()
>      }
>  };
> @@ -815,6 +842,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;
> @@ -855,6 +883,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)
> @@ -888,6 +920,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;
>

For the existing functions allwinner_sdhost_sun4i_class_init() and
allwinner_sdhost_sun5i_class_init(), could you please add
'sc->can_calibrate = false'?
That way, we make it explicit that those types do not support the sample
delay register.

With that resolved, the code looks good to me:

Reviewed-by: Niek Linnenbank <nieklinnenbank@gmail.com>


> +}
> +
> +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,
> @@ -910,6 +960,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,
> @@ -922,6 +984,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
>
>

-- 
Niek Linnenbank

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

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

* Re: [PATCH v4 08/11] hw: arm: allwinner-r40: Add emac and gmac support
  2023-05-10 10:30 ` [PATCH v4 08/11] hw: arm: allwinner-r40: Add emac and gmac support qianfanguijin
@ 2023-05-15 19:58   ` Niek Linnenbank
  2023-05-23  1:01     ` qianfan
  0 siblings, 1 reply; 31+ messages in thread
From: Niek Linnenbank @ 2023-05-15 19:58 UTC (permalink / raw)
  To: qianfanguijin
  Cc: qemu-arm, qemu-devel, Strahinja Jankovic, Peter Maydell,
	Beniamino Galvani, Philippe Mathieu-Daudé

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

Hi Qianfan,

On Wed, May 10, 2023 at 12:30 PM <qianfanguijin@163.com> wrote:

> 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         | 50 ++++++++++++++++++++++++++++++++--
>  hw/arm/bananapi_m2u.c          |  3 ++
>  include/hw/arm/allwinner-r40.h |  6 ++++
>  3 files changed, 57 insertions(+), 2 deletions(-)
>
> diff --git a/hw/arm/allwinner-r40.c b/hw/arm/allwinner-r40.c
> index b148c56449..c018ad231a 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,
> @@ -54,6 +55,7 @@ const hwaddr allwinner_r40_memmap[] = {
>      [AW_R40_DEV_UART6]      = 0x01c29800,
>      [AW_R40_DEV_UART7]      = 0x01c29c00,
>      [AW_R40_DEV_TWI0]       = 0x01c2ac00,
> +    [AW_R40_DEV_GMAC]       = 0x01c50000,
>      [AW_R40_DEV_DRAMCOM]    = 0x01c62000,
>      [AW_R40_DEV_DRAMCTL]    = 0x01c63000,
>      [AW_R40_DEV_DRAMPHY]    = 0x01c65000,
> @@ -82,7 +84,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 },
> @@ -180,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_EMAC      = 55,
> +    AW_R40_GIC_SPI_GMAC      = 85,
>  };
>
>  /* Allwinner R40 general constants */
> @@ -276,6 +278,11 @@ static void allwinner_r40_init(Object *obj)
>
>      object_initialize_child(obj, "twi0", &s->i2c0, 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");
> @@ -285,6 +292,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;
>
> @@ -442,6 +450,44 @@ static void allwinner_r40_realize(DeviceState *dev,
> Error **errp)
>      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;
> +        }
>

Could you please clarify the lines below here? I'm not seeing the function
call 'qemu_show_nic_models()' in any of the other machines / soc
implementations.

Also, if you intend to catch a possible input error here, probably its best
to log/print the error for the user before calling exit()?


> +        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 20a4550c68..74121d8966 100644
> --- a/hw/arm/bananapi_m2u.c
> +++ b/hw/arm/bananapi_m2u.c
> @@ -92,6 +92,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 8243e8903b..5f2d08489e 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,
> @@ -51,6 +54,7 @@ enum {
>      AW_R40_DEV_UART6,
>      AW_R40_DEV_UART7,
>      AW_R40_DEV_TWI0,
> +    AW_R40_DEV_GMAC,
>      AW_R40_DEV_GIC_DIST,
>      AW_R40_DEV_GIC_CPU,
>      AW_R40_DEV_GIC_HYP,
> @@ -103,6 +107,8 @@ struct AwR40State {
>      AwR40ClockCtlState ccu;
>      AwR40DramCtlState dramc;
>      AWI2CState i2c0;
> +    AwEmacState emac;
> +    AwSun8iEmacState gmac;
>      GICState gic;
>      MemoryRegion sram_a1;
>      MemoryRegion sram_a2;
> --
> 2.25.1
>
>
Regards,
Niek
-- 
Niek Linnenbank

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

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

* Re: [PATCH v4 09/11] hw: arm: allwinner-sramc: Add SRAM Controller support for R40
  2023-05-10 10:30 ` [PATCH v4 09/11] hw: arm: allwinner-sramc: Add SRAM Controller support for R40 qianfanguijin
@ 2023-05-15 20:05   ` Niek Linnenbank
  0 siblings, 0 replies; 31+ messages in thread
From: Niek Linnenbank @ 2023-05-15 20:05 UTC (permalink / raw)
  To: qianfanguijin
  Cc: qemu-arm, qemu-devel, Strahinja Jankovic, Peter Maydell,
	Beniamino Galvani, Philippe Mathieu-Daudé

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

On Wed, May 10, 2023 at 12:30 PM <qianfanguijin@163.com> wrote:

> From: qianfan Zhao <qianfanguijin@163.com>
>
> Only a few important registers are added, especially the SRAM_VER
> register.
>
> Signed-off-by: qianfan Zhao <qianfanguijin@163.com>
>
Reviewed-by: Niek Linnenbank <nieklinnenbank@gmail.com>


> ---
>  hw/arm/Kconfig                    |   1 +
>  hw/arm/allwinner-r40.c            |   7 +-
>  hw/misc/Kconfig                   |   3 +
>  hw/misc/allwinner-sramc.c         | 184 ++++++++++++++++++++++++++++++
>  hw/misc/meson.build               |   1 +
>  hw/misc/trace-events              |   4 +
>  include/hw/arm/allwinner-r40.h    |   3 +
>  include/hw/misc/allwinner-sramc.h |  69 +++++++++++
>  8 files changed, 271 insertions(+), 1 deletion(-)
>  create mode 100644 hw/misc/allwinner-sramc.c
>  create mode 100644 include/hw/misc/allwinner-sramc.h
>
> diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig
> index bad4ea158c..ae6f3911c5 100644
> --- a/hw/arm/Kconfig
> +++ b/hw/arm/Kconfig
> @@ -377,6 +377,7 @@ config ALLWINNER_H3
>  config ALLWINNER_R40
>      bool
>      default y if TCG && ARM
> +    select ALLWINNER_SRAMC
>      select ALLWINNER_A10_PIT
>      select AXP2XX_PMU
>      select SERIAL
> diff --git a/hw/arm/allwinner-r40.c b/hw/arm/allwinner-r40.c
> index c018ad231a..7d29eb224f 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_SRAMC]      = 0x01c00000,
>      [AW_R40_DEV_EMAC]       = 0x01c0b000,
>      [AW_R40_DEV_MMC0]       = 0x01c0f000,
>      [AW_R40_DEV_MMC1]       = 0x01c10000,
> @@ -76,7 +77,6 @@ struct AwR40Unimplemented {
>  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 },
> @@ -288,6 +288,8 @@ static void allwinner_r40_init(Object *obj)
>                               "ram-addr");
>      object_property_add_alias(obj, "ram-size", OBJECT(&s->dramc),
>                                "ram-size");
> +
> +    object_initialize_child(obj, "sramc", &s->sramc,
> TYPE_AW_SRAMC_SUN8I_R40);
>  }
>
>  static void allwinner_r40_realize(DeviceState *dev, Error **errp)
> @@ -382,6 +384,9 @@ static void allwinner_r40_realize(DeviceState *dev,
> Error **errp)
>                         AW_R40_GIC_SPI_TIMER1));
>
>      /* SRAM */
> +    sysbus_realize(SYS_BUS_DEVICE(&s->sramc), &error_fatal);
> +    sysbus_mmio_map(SYS_BUS_DEVICE(&s->sramc), 0,
> s->memmap[AW_R40_DEV_SRAMC]);
> +
>      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",
> diff --git a/hw/misc/Kconfig b/hw/misc/Kconfig
> index efeb430a6c..e4c2149175 100644
> --- a/hw/misc/Kconfig
> +++ b/hw/misc/Kconfig
> @@ -170,6 +170,9 @@ config VIRT_CTRL
>  config LASI
>      bool
>
> +config ALLWINNER_SRAMC
> +    bool
> +
>  config ALLWINNER_A10_CCM
>      bool
>
> diff --git a/hw/misc/allwinner-sramc.c b/hw/misc/allwinner-sramc.c
> new file mode 100644
> index 0000000000..a8b731f8f2
> --- /dev/null
> +++ b/hw/misc/allwinner-sramc.c
> @@ -0,0 +1,184 @@
> +/*
> + * Allwinner R40 SRAM 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/>.
> + */
> +
> +#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 "qapi/error.h"
> +#include "hw/qdev-properties.h"
> +#include "hw/qdev-properties-system.h"
> +#include "hw/misc/allwinner-sramc.h"
> +#include "trace.h"
> +
> +/*
> + * register offsets
> + * https://linux-sunxi.org/SRAM_Controller_Register_Guide
> + */
> +enum {
> +    REG_SRAM_CTL1_CFG               = 0x04, /* SRAM Control register 1 */
> +    REG_SRAM_VER                    = 0x24, /* SRAM Version register */
> +    REG_SRAM_R40_SOFT_ENTRY_REG0    = 0xbc,
> +};
> +
> +/* REG_SRAMC_VERSION bit defines */
> +#define SRAM_VER_READ_ENABLE            (1 << 15)
> +#define SRAM_VER_VERSION_SHIFT          16
> +#define SRAM_VERSION_SUN8I_R40          0x1701
> +
> +static uint64_t allwinner_sramc_read(void *opaque, hwaddr offset,
> +                                     unsigned size)
> +{
> +    AwSRAMCState *s = AW_SRAMC(opaque);
> +    AwSRAMCClass *sc = AW_SRAMC_GET_CLASS(s);
> +    uint64_t val = 0;
> +
> +    switch (offset) {
> +    case REG_SRAM_CTL1_CFG:
> +        val = s->sram_ctl1;
> +        break;
> +    case REG_SRAM_VER:
> +        /* bit15: lock bit, set this bit before reading this register */
> +        if (s->sram_ver & SRAM_VER_READ_ENABLE) {
> +            val = SRAM_VER_READ_ENABLE |
> +                    (sc->sram_version_code << SRAM_VER_VERSION_SHIFT);
> +        }
> +        break;
> +    case REG_SRAM_R40_SOFT_ENTRY_REG0:
> +        val = s->sram_soft_entry_reg0;
> +        break;
> +    default:
> +        qemu_log_mask(LOG_GUEST_ERROR, "%s: out-of-bounds offset
> 0x%04x\n",
> +                      __func__, (uint32_t)offset);
> +        return 0;
> +    }
> +
> +    trace_allwinner_sramc_read(offset, val);
> +
> +    return val;
> +}
> +
> +static void allwinner_sramc_write(void *opaque, hwaddr offset,
> +                                  uint64_t val, unsigned size)
> +{
> +    AwSRAMCState *s = AW_SRAMC(opaque);
> +
> +    trace_allwinner_sramc_write(offset, val);
> +
> +    switch (offset) {
> +    case REG_SRAM_CTL1_CFG:
> +        s->sram_ctl1 = val;
> +        break;
> +    case REG_SRAM_VER:
> +        /* Only the READ_ENABLE bit is writeable */
> +        s->sram_ver = val & SRAM_VER_READ_ENABLE;
> +        break;
> +    case REG_SRAM_R40_SOFT_ENTRY_REG0:
> +        s->sram_soft_entry_reg0 = val;
> +        break;
> +    default:
> +        qemu_log_mask(LOG_GUEST_ERROR, "%s: out-of-bounds offset
> 0x%04x\n",
> +                      __func__, (uint32_t)offset);
> +        break;
> +    }
> +}
> +
> +static const MemoryRegionOps allwinner_sramc_ops = {
> +    .read = allwinner_sramc_read,
> +    .write = allwinner_sramc_write,
> +    .endianness = DEVICE_NATIVE_ENDIAN,
> +    .valid = {
> +        .min_access_size = 4,
> +        .max_access_size = 4,
> +    },
> +    .impl.min_access_size = 4,
> +};
> +
> +static const VMStateDescription allwinner_sramc_vmstate = {
> +    .name = "allwinner-sramc",
> +    .version_id = 1,
> +    .minimum_version_id = 1,
> +    .fields = (VMStateField[]) {
> +        VMSTATE_UINT32(sram_ver, AwSRAMCState),
> +        VMSTATE_UINT32(sram_soft_entry_reg0, AwSRAMCState),
> +        VMSTATE_END_OF_LIST()
> +    }
> +};
> +
> +static void allwinner_sramc_reset(DeviceState *dev)
> +{
> +    AwSRAMCState *s = AW_SRAMC(dev);
> +    AwSRAMCClass *sc = AW_SRAMC_GET_CLASS(s);
> +
> +    switch (sc->sram_version_code) {
> +    case SRAM_VERSION_SUN8I_R40:
> +        s->sram_ctl1 = 0x1300;
> +        break;
> +    }
> +}
> +
> +static void allwinner_sramc_class_init(ObjectClass *klass, void *data)
> +{
> +    DeviceClass *dc = DEVICE_CLASS(klass);
> +
> +    dc->reset = allwinner_sramc_reset;
> +    dc->vmsd = &allwinner_sramc_vmstate;
> +}
> +
> +static void allwinner_sramc_init(Object *obj)
> +{
> +    SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
> +    AwSRAMCState *s = AW_SRAMC(obj);
> +
> +    /* Memory mapping */
> +    memory_region_init_io(&s->iomem, OBJECT(s), &allwinner_sramc_ops, s,
> +                           TYPE_AW_SRAMC, 1 * KiB);
> +    sysbus_init_mmio(sbd, &s->iomem);
> +}
> +
> +static const TypeInfo allwinner_sramc_info = {
> +    .name          = TYPE_AW_SRAMC,
> +    .parent        = TYPE_SYS_BUS_DEVICE,
> +    .instance_init = allwinner_sramc_init,
> +    .instance_size = sizeof(AwSRAMCState),
> +    .class_init    = allwinner_sramc_class_init,
> +};
> +
> +static void allwinner_r40_sramc_class_init(ObjectClass *klass, void *data)
> +{
> +    AwSRAMCClass *sc = AW_SRAMC_CLASS(klass);
> +
> +    sc->sram_version_code = SRAM_VERSION_SUN8I_R40;
> +}
> +
> +static const TypeInfo allwinner_r40_sramc_info = {
> +    .name          = TYPE_AW_SRAMC_SUN8I_R40,
> +    .parent        = TYPE_AW_SRAMC,
> +    .class_init    = allwinner_r40_sramc_class_init,
> +};
> +
> +static void allwinner_sramc_register(void)
> +{
> +    type_register_static(&allwinner_sramc_info);
> +    type_register_static(&allwinner_r40_sramc_info);
> +}
> +
> +type_init(allwinner_sramc_register)
> diff --git a/hw/misc/meson.build b/hw/misc/meson.build
> index b04d43e05a..78ca857c9d 100644
> --- a/hw/misc/meson.build
> +++ b/hw/misc/meson.build
> @@ -37,6 +37,7 @@ subdir('macio')
>
>  softmmu_ss.add(when: 'CONFIG_IVSHMEM_DEVICE', if_true: files('ivshmem.c'))
>
> +softmmu_ss.add(when: 'CONFIG_ALLWINNER_SRAMC', if_true:
> files('allwinner-sramc.c'))
>  softmmu_ss.add(when: 'CONFIG_ALLWINNER_A10_CCM', if_true:
> files('allwinner-a10-ccm.c'))
>  softmmu_ss.add(when: 'CONFIG_ALLWINNER_A10_DRAMC', if_true:
> files('allwinner-a10-dramc.c'))
>  softmmu_ss.add(when: 'CONFIG_ALLWINNER_H3', if_true:
> files('allwinner-h3-ccu.c'))
> diff --git a/hw/misc/trace-events b/hw/misc/trace-events
> index 8b68f07765..4d1a0e17af 100644
> --- a/hw/misc/trace-events
> +++ b/hw/misc/trace-events
> @@ -33,6 +33,10 @@ allwinner_r40_dramphy_write(uint64_t offset, uint64_t
> data, unsigned size) "writ
>  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
>
> +# allwinner-sramc.c
> +allwinner_sramc_read(uint64_t offset, uint64_t data) "offset 0x%" PRIx64
> " data 0x%" PRIx64
> +allwinner_sramc_write(uint64_t offset, uint64_t data) "offset 0x%" PRIx64
> " data 0x%" PRIx64
> +
>  # avr_power.c
>  avr_power_read(uint8_t value) "power_reduc read value:%u"
>  avr_power_write(uint8_t value) "power_reduc write value:%u"
> diff --git a/include/hw/arm/allwinner-r40.h
> b/include/hw/arm/allwinner-r40.h
> index 5f2d08489e..72710d3edc 100644
> --- a/include/hw/arm/allwinner-r40.h
> +++ b/include/hw/arm/allwinner-r40.h
> @@ -27,6 +27,7 @@
>  #include "hw/sd/allwinner-sdhost.h"
>  #include "hw/misc/allwinner-r40-ccu.h"
>  #include "hw/misc/allwinner-r40-dramc.h"
> +#include "hw/misc/allwinner-sramc.h"
>  #include "hw/i2c/allwinner-i2c.h"
>  #include "hw/net/allwinner_emac.h"
>  #include "hw/net/allwinner-sun8i-emac.h"
> @@ -38,6 +39,7 @@ enum {
>      AW_R40_DEV_SRAM_A2,
>      AW_R40_DEV_SRAM_A3,
>      AW_R40_DEV_SRAM_A4,
> +    AW_R40_DEV_SRAMC,
>      AW_R40_DEV_EMAC,
>      AW_R40_DEV_MMC0,
>      AW_R40_DEV_MMC1,
> @@ -102,6 +104,7 @@ struct AwR40State {
>
>      ARMCPU cpus[AW_R40_NUM_CPUS];
>      const hwaddr *memmap;
> +    AwSRAMCState sramc;
>      AwA10PITState timer;
>      AwSdHostState mmc[AW_R40_NUM_MMCS];
>      AwR40ClockCtlState ccu;
> diff --git a/include/hw/misc/allwinner-sramc.h
> b/include/hw/misc/allwinner-sramc.h
> new file mode 100644
> index 0000000000..66b01b8d04
> --- /dev/null
> +++ b/include/hw/misc/allwinner-sramc.h
> @@ -0,0 +1,69 @@
> +/*
> + * Allwinner SRAM 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_SRAMC_H
> +#define HW_MISC_ALLWINNER_SRAMC_H
> +
> +#include "qom/object.h"
> +#include "hw/sysbus.h"
> +#include "qemu/uuid.h"
> +
> +/**
> + * Object model
> + * @{
> + */
> +#define TYPE_AW_SRAMC               "allwinner-sramc"
> +#define TYPE_AW_SRAMC_SUN8I_R40     TYPE_AW_SRAMC "-sun8i-r40"
> +OBJECT_DECLARE_TYPE(AwSRAMCState, AwSRAMCClass, AW_SRAMC)
> +
> +/** @} */
> +
> +/**
> + * Allwinner SRAMC object instance state
> + */
> +struct AwSRAMCState {
> +    /*< private >*/
> +    SysBusDevice parent_obj;
> +    /*< public >*/
> +
> +    /** Maps I/O registers in physical memory */
> +    MemoryRegion iomem;
> +
> +    /* registers */
> +    uint32_t sram_ctl1;
> +    uint32_t sram_ver;
> +    uint32_t sram_soft_entry_reg0;
> +};
> +
> +/**
> + * Allwinner SRAM Controller class-level struct.
> + *
> + * This struct is filled by each sunxi device specific code
> + * such that the generic code can use this struct to support
> + * all devices.
> + */
> +struct AwSRAMCClass {
> +    /*< private >*/
> +    SysBusDeviceClass parent_class;
> +    /*< public >*/
> +
> +    uint32_t sram_version_code;
> +};
> +
> +#endif /* HW_MISC_ALLWINNER_SRAMC_H */
> --
> 2.25.1
>
>

-- 
Niek Linnenbank

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

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

* Re: [PATCH v4 10/11] tests: avocado: boot_linux_console: Add test case for bpim2u
  2023-05-10 10:30 ` [PATCH v4 10/11] tests: avocado: boot_linux_console: Add test case for bpim2u qianfanguijin
@ 2023-05-15 20:09   ` Niek Linnenbank
  0 siblings, 0 replies; 31+ messages in thread
From: Niek Linnenbank @ 2023-05-15 20:09 UTC (permalink / raw)
  To: qianfanguijin
  Cc: qemu-arm, qemu-devel, Strahinja Jankovic, Peter Maydell,
	Beniamino Galvani, Philippe Mathieu-Daudé

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

On Wed, May 10, 2023 at 12:30 PM <qianfanguijin@163.com> wrote:

> From: qianfan Zhao <qianfanguijin@163.com>
>
> Add test case for booting from initrd and sd card.
>

To make the commit message more complete and reflect the full contents,
maybe also mention the gmac and openwrt test?

Tests look fine to me, and are running OK on my machine:

$ AVOCADO_ALLOW_LARGE_STORAGE=yes ./build/tests/venv/bin/avocado
--show=app,console run -t machine:bpim2u tests/avocado/boot_linux_console.py
...
RESULTS    : PASS 4 | ERROR 0 | FAIL 0 | SKIP 0 | WARN 0 | INTERRUPT 0 |
CANCEL 0
JOB TIME   : 26.46 s

So for me feel free to add:

Reviewed-by: Niek Linnenbank <nieklinnenbank@gmail.com>
Tested-by: Niek Linnenbank <nieklinnenbank@gmail.com>

Regards,
Niek


>
> Signed-off-by: qianfan Zhao <qianfanguijin@163.com>
> ---
>  tests/avocado/boot_linux_console.py | 176 ++++++++++++++++++++++++++++
>  1 file changed, 176 insertions(+)
>
> diff --git a/tests/avocado/boot_linux_console.py
> b/tests/avocado/boot_linux_console.py
> index c0675809e6..6ed660611f 100644
> --- a/tests/avocado/boot_linux_console.py
> +++ b/tests/avocado/boot_linux_console.py
> @@ -769,6 +769,182 @@ 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
>
>

-- 
Niek Linnenbank

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

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

* Re: [PATCH v4 00/11] *** Add allwinner r40 support ***
  2023-05-10 10:29 [PATCH v4 00/11] *** Add allwinner r40 support *** qianfanguijin
                   ` (9 preceding siblings ...)
  2023-05-10 10:30 ` [PATCH v4 10/11] tests: avocado: boot_linux_console: Add test case for bpim2u qianfanguijin
@ 2023-05-15 20:25 ` Niek Linnenbank
  2023-05-25  9:48   ` Peter Maydell
  10 siblings, 1 reply; 31+ messages in thread
From: Niek Linnenbank @ 2023-05-15 20:25 UTC (permalink / raw)
  To: qianfanguijin
  Cc: qemu-arm, qemu-devel, Strahinja Jankovic, Peter Maydell,
	Beniamino Galvani, Philippe Mathieu-Daudé

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

Hi Qianfan,

Thanks for sending the v4. I've reviewed all patches and replied to each
individual patch.

Great work so far!

One thing I wanted to mention is that when you receive a 'Reviewed-by:' or
'Tested-by:' line from a reviewer,
please feel free to include it in the commit message on the next version of
that patch. This helps you and reviewers
to keep track on what has been reviewed and what not.

For more details on this and the review process, also see this page:
https://www.qemu.org/docs/master/devel/submitting-a-patch.html#proper-use-of-reviewed-by-tags-can-aid-review

Kind regards,
Niek

On Wed, May 10, 2023 at 12:30 PM <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
>
> # v2: 2023-03-28
>
> 1. Fix the waring and error reported by checkpatch.pl
> 2. Remove the other i2c controllers except that i2c0
> 3. Use an array to register mmc and uart devices
> 4. Rename axp209 to axp22x and add axp221 support
> 5. Add a basic SRAM controller
>
> # v3: 2023-04-18
>
> 1. Update some commit messages
> 2. Squash those two commit about sdcard
>    hw: sd: allwinner-sdhost: Add sun50i-a64 SoC support
>    hw: arm: allwinner-r40: Fix the mmc controller's type
>
> # v4: 2023-05-10
>
> 1. Rebase to master
>
> 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 i2c0 device
>   hw/misc: Rename axp209 to axp22x and add support AXP221 PMU
>   hw/arm/allwinner-r40: add SDRAM controller device
>   hw: sd: allwinner-sdhost: Add sun50i-a64 SoC support
>   hw: arm: allwinner-r40: Add emac and gmac support
>   hw: arm: allwinner-sramc: Add SRAM Controller support for R40
>   tests: avocado: boot_linux_console: Add test case for bpim2u
>   docs: system: arm: Introduce bananapi_m2u
>
>  docs/system/arm/bananapi_m2u.rst      | 138 +++++++
>  hw/arm/Kconfig                        |  14 +-
>  hw/arm/allwinner-r40.c                | 526 ++++++++++++++++++++++++++
>  hw/arm/bananapi_m2u.c                 | 145 +++++++
>  hw/arm/meson.build                    |   1 +
>  hw/misc/Kconfig                       |   5 +-
>  hw/misc/allwinner-r40-ccu.c           | 209 ++++++++++
>  hw/misc/allwinner-r40-dramc.c         | 513 +++++++++++++++++++++++++
>  hw/misc/allwinner-sramc.c             | 184 +++++++++
>  hw/misc/axp209.c                      | 238 ------------
>  hw/misc/axp2xx.c                      | 283 ++++++++++++++
>  hw/misc/meson.build                   |   5 +-
>  hw/misc/trace-events                  |  26 +-
>  hw/sd/allwinner-sdhost.c              |  70 +++-
>  include/hw/arm/allwinner-r40.h        | 143 +++++++
>  include/hw/misc/allwinner-r40-ccu.h   |  65 ++++
>  include/hw/misc/allwinner-r40-dramc.h | 108 ++++++
>  include/hw/misc/allwinner-sramc.h     |  69 ++++
>  include/hw/sd/allwinner-sdhost.h      |   9 +
>  tests/avocado/boot_linux_console.py   | 176 +++++++++
>  20 files changed, 2679 insertions(+), 248 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/allwinner-sramc.c
>  delete mode 100644 hw/misc/axp209.c
>  create mode 100644 hw/misc/axp2xx.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
>  create mode 100644 include/hw/misc/allwinner-sramc.h
>
> --
> 2.25.1
>
>

-- 
Niek Linnenbank

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

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

* Re: [PATCH v4 05/11] hw/misc: Rename axp209 to axp22x and add support AXP221 PMU
  2023-05-15 19:29   ` Niek Linnenbank
@ 2023-05-23  0:40     ` qianfan
  0 siblings, 0 replies; 31+ messages in thread
From: qianfan @ 2023-05-23  0:40 UTC (permalink / raw)
  To: Niek Linnenbank
  Cc: qemu-arm, qemu-devel, Strahinja Jankovic, Peter Maydell,
	Beniamino Galvani, Philippe Mathieu-Daudé



在 2023/5/16 3:29, Niek Linnenbank 写道:
> Hi Qianfan,
>
> Good idea indeed to turn this driver into a more generic one. If 
> possible, its best to re-use code rather than adding new.
>
> On Wed, May 10, 2023 at 12:30 PM <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>
>     ---
>      hw/arm/Kconfig        |   3 +-
>      hw/arm/bananapi_m2u.c |   6 +
>      hw/misc/Kconfig       |   2 +-
>      hw/misc/axp209.c      | 238 -----------------------------------
>      hw/misc/axp2xx.c      | 283
>     ++++++++++++++++++++++++++++++++++++++++++
>      hw/misc/meson.build   |   2 +-
>      hw/misc/trace-events  |   8 +-
>      7 files changed, 297 insertions(+), 245 deletions(-)
>      delete mode 100644 hw/misc/axp209.c
>      create mode 100644 hw/misc/axp2xx.c
>
>     diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig
>     index b7a84f6e3f..bad4ea158c 100644
>     --- a/hw/arm/Kconfig
>     +++ b/hw/arm/Kconfig
>     @@ -355,7 +355,7 @@ config ALLWINNER_A10
>          select ALLWINNER_WDT
>          select ALLWINNER_EMAC
>          select ALLWINNER_I2C
>     -    select AXP209_PMU
>     +    select AXP2XX_PMU
>          select SERIAL
>          select UNIMP
>
>     @@ -378,6 +378,7 @@ config ALLWINNER_R40
>          bool
>          default y if TCG && ARM
>          select ALLWINNER_A10_PIT
>     +    select AXP2XX_PMU
>          select SERIAL
>          select ARM_TIMER
>          select ARM_GIC
>     diff --git a/hw/arm/bananapi_m2u.c b/hw/arm/bananapi_m2u.c
>     index 1d49a006b5..9c5360a41b 100644
>     --- a/hw/arm/bananapi_m2u.c
>     +++ b/hw/arm/bananapi_m2u.c
>     @@ -23,6 +23,7 @@
>      #include "qapi/error.h"
>      #include "qemu/error-report.h"
>      #include "hw/boards.h"
>     +#include "hw/i2c/i2c.h"
>      #include "hw/qdev-properties.h"
>      #include "hw/arm/allwinner-r40.h"
>
>     @@ -61,6 +62,7 @@ static void bpim2u_init(MachineState *machine)
>      {
>          bool bootroom_loaded = false;
>          AwR40State *r40;
>     +    I2CBus *i2c;
>
>          /* BIOS is not supported by this board */
>          if (machine->firmware) {
>     @@ -104,6 +106,10 @@ static void bpim2u_init(MachineState *machine)
>              }
>          }
>
>     +    /* 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..efeb430a6c 100644
>     --- a/hw/misc/Kconfig
>     +++ b/hw/misc/Kconfig
>     @@ -176,7 +176,7 @@ config ALLWINNER_A10_CCM
>      config ALLWINNER_A10_DRAMC
>          bool
>
>     -config AXP209_PMU
>     +config AXP2XX_PMU
>          bool
>          depends on I2C
>
>     diff --git a/hw/misc/axp209.c b/hw/misc/axp209.c
>     deleted file mode 100644
>     index 2908ed99a6..0000000000
>     --- a/hw/misc/axp209.c
>     +++ /dev/null
>     @@ -1,238 +0,0 @@
>     -/*
>     - * AXP-209 PMU Emulation
>     - *
>     - * Copyright (C) 2022 Strahinja Jankovic
>     <strahinja.p.jankovic@gmail.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 "trace.h"
>     -#include "hw/i2c/i2c.h"
>     -#include "migration/vmstate.h"
>     -
>     -#define TYPE_AXP209_PMU "axp209_pmu"
>     -
>     -#define AXP209(obj) \
>     -    OBJECT_CHECK(AXP209I2CState, (obj), TYPE_AXP209_PMU)
>     -
>     -/* registers */
>     -enum {
>     -    REG_POWER_STATUS = 0x0u,
>     -    REG_OPERATING_MODE,
>     -    REG_OTG_VBUS_STATUS,
>     -    REG_CHIP_VERSION,
>     -    REG_DATA_CACHE_0,
>     -    REG_DATA_CACHE_1,
>     -    REG_DATA_CACHE_2,
>     -    REG_DATA_CACHE_3,
>     -    REG_DATA_CACHE_4,
>     -    REG_DATA_CACHE_5,
>     -    REG_DATA_CACHE_6,
>     -    REG_DATA_CACHE_7,
>     -    REG_DATA_CACHE_8,
>     -    REG_DATA_CACHE_9,
>     -    REG_DATA_CACHE_A,
>     -    REG_DATA_CACHE_B,
>     -    REG_POWER_OUTPUT_CTRL = 0x12u,
>     -    REG_DC_DC2_OUT_V_CTRL = 0x23u,
>     -    REG_DC_DC2_DVS_CTRL = 0x25u,
>     -    REG_DC_DC3_OUT_V_CTRL = 0x27u,
>     -    REG_LDO2_4_OUT_V_CTRL,
>     -    REG_LDO3_OUT_V_CTRL,
>     -    REG_VBUS_CH_MGMT = 0x30u,
>     -    REG_SHUTDOWN_V_CTRL,
>     -    REG_SHUTDOWN_CTRL,
>     -    REG_CHARGE_CTRL_1,
>     -    REG_CHARGE_CTRL_2,
>     -    REG_SPARE_CHARGE_CTRL,
>     -    REG_PEK_KEY_CTRL,
>     -    REG_DC_DC_FREQ_SET,
>     -    REG_CHR_TEMP_TH_SET,
>     -    REG_CHR_HIGH_TEMP_TH_CTRL,
>     -    REG_IPSOUT_WARN_L1,
>     -    REG_IPSOUT_WARN_L2,
>     -    REG_DISCHR_TEMP_TH_SET,
>     -    REG_DISCHR_HIGH_TEMP_TH_CTRL,
>     -    REG_IRQ_BANK_1_CTRL = 0x40u,
>     -    REG_IRQ_BANK_2_CTRL,
>     -    REG_IRQ_BANK_3_CTRL,
>     -    REG_IRQ_BANK_4_CTRL,
>     -    REG_IRQ_BANK_5_CTRL,
>     -    REG_IRQ_BANK_1_STAT = 0x48u,
>     -    REG_IRQ_BANK_2_STAT,
>     -    REG_IRQ_BANK_3_STAT,
>     -    REG_IRQ_BANK_4_STAT,
>     -    REG_IRQ_BANK_5_STAT,
>     -    REG_ADC_ACIN_V_H = 0x56u,
>     -    REG_ADC_ACIN_V_L,
>     -    REG_ADC_ACIN_CURR_H,
>     -    REG_ADC_ACIN_CURR_L,
>     -    REG_ADC_VBUS_V_H,
>     -    REG_ADC_VBUS_V_L,
>     -    REG_ADC_VBUS_CURR_H,
>     -    REG_ADC_VBUS_CURR_L,
>     -    REG_ADC_INT_TEMP_H,
>     -    REG_ADC_INT_TEMP_L,
>     -    REG_ADC_TEMP_SENS_V_H = 0x62u,
>     -    REG_ADC_TEMP_SENS_V_L,
>     -    REG_ADC_BAT_V_H = 0x78u,
>     -    REG_ADC_BAT_V_L,
>     -    REG_ADC_BAT_DISCHR_CURR_H,
>     -    REG_ADC_BAT_DISCHR_CURR_L,
>     -    REG_ADC_BAT_CHR_CURR_H,
>     -    REG_ADC_BAT_CHR_CURR_L,
>     -    REG_ADC_IPSOUT_V_H,
>     -    REG_ADC_IPSOUT_V_L,
>     -    REG_DC_DC_MOD_SEL = 0x80u,
>     -    REG_ADC_EN_1,
>     -    REG_ADC_EN_2,
>     -    REG_ADC_SR_CTRL,
>     -    REG_ADC_IN_RANGE,
>     -    REG_GPIO1_ADC_IRQ_RISING_TH,
>     -    REG_GPIO1_ADC_IRQ_FALLING_TH,
>     -    REG_TIMER_CTRL = 0x8au,
>     -    REG_VBUS_CTRL_MON_SRP,
>     -    REG_OVER_TEMP_SHUTDOWN = 0x8fu,
>     -    REG_GPIO0_FEAT_SET,
>     -    REG_GPIO_OUT_HIGH_SET,
>     -    REG_GPIO1_FEAT_SET,
>     -    REG_GPIO2_FEAT_SET,
>     -    REG_GPIO_SIG_STATE_SET_MON,
>     -    REG_GPIO3_SET,
>     -    REG_COULOMB_CNTR_CTRL = 0xb8u,
>     -    REG_POWER_MEAS_RES,
>     -    NR_REGS
>     -};
>     -
>     -#define AXP209_CHIP_VERSION_ID             (0x01)
>     -#define AXP209_DC_DC2_OUT_V_CTRL_RESET     (0x16)
>     -#define AXP209_IRQ_BANK_1_CTRL_RESET       (0xd8)
>     -
>     -/* A simple I2C slave which returns values of ID or CNT register. */
>     -typedef struct AXP209I2CState {
>     -    /*< 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 */
>     -} AXP209I2CState;
>     -
>     -/* Reset all counters and load ID register */
>     -static void axp209_reset_enter(Object *obj, ResetType type)
>     -{
>     -    AXP209I2CState *s = AXP209(obj);
>     -
>     -    memset(s->regs, 0, NR_REGS);
>     -    s->ptr = 0;
>     -    s->count = 0;
>     -    s->regs[REG_CHIP_VERSION] = AXP209_CHIP_VERSION_ID;
>     -    s->regs[REG_DC_DC2_OUT_V_CTRL] = AXP209_DC_DC2_OUT_V_CTRL_RESET;
>     -    s->regs[REG_IRQ_BANK_1_CTRL] = AXP209_IRQ_BANK_1_CTRL_RESET;
>     -}
>     -
>     -/* Handle events from master. */
>     -static int axp209_event(I2CSlave *i2c, enum i2c_event event)
>     -{
>     -    AXP209I2CState *s = AXP209(i2c);
>     -
>     -    s->count = 0;
>     -
>     -    return 0;
>     -}
>     -
>     -/* Called when master requests read */
>     -static uint8_t axp209_rx(I2CSlave *i2c)
>     -{
>     -    AXP209I2CState *s = AXP209(i2c);
>     -    uint8_t ret = 0xff;
>     -
>     -    if (s->ptr < NR_REGS) {
>     -        ret = s->regs[s->ptr++];
>     -    }
>     -
>     -    trace_axp209_rx(s->ptr - 1, ret);
>     -
>     -    return ret;
>     -}
>     -
>     -/*
>     - * Called when master sends write.
>     - * Update ptr with byte 0, then perform write with second byte.
>     - */
>     -static int axp209_tx(I2CSlave *i2c, uint8_t data)
>     -{
>     -    AXP209I2CState *s = AXP209(i2c);
>     -
>     -    if (s->count == 0) {
>     -        /* Store register address */
>     -        s->ptr = data;
>     -        s->count++;
>     -        trace_axp209_select(data);
>     -    } else {
>     -        trace_axp209_tx(s->ptr, data);
>     -        if (s->ptr == REG_DC_DC2_OUT_V_CTRL) {
>     -            s->regs[s->ptr++] = data;
>     -        }
>     -    }
>     -
>     -    return 0;
>     -}
>     -
>     -static const VMStateDescription vmstate_axp209 = {
>     -    .name = TYPE_AXP209_PMU,
>     -    .version_id = 1,
>     -    .fields = (VMStateField[]) {
>     -        VMSTATE_UINT8_ARRAY(regs, AXP209I2CState, NR_REGS),
>     -        VMSTATE_UINT8(count, AXP209I2CState),
>     -        VMSTATE_UINT8(ptr, AXP209I2CState),
>     -        VMSTATE_END_OF_LIST()
>     -    }
>     -};
>     -
>     -static void axp209_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 = axp209_reset_enter;
>     -    dc->vmsd = &vmstate_axp209;
>     -    isc->event = axp209_event;
>     -    isc->recv = axp209_rx;
>     -    isc->send = axp209_tx;
>     -}
>     -
>     -static const TypeInfo axp209_info = {
>     -    .name = TYPE_AXP209_PMU,
>     -    .parent = TYPE_I2C_SLAVE,
>     -    .instance_size = sizeof(AXP209I2CState),
>     -    .class_init = axp209_class_init
>     -};
>     -
>     -static void axp209_register_devices(void)
>     -{
>     -    type_register_static(&axp209_info);
>     -}
>     -
>     -type_init(axp209_register_devices);
>     diff --git a/hw/misc/axp2xx.c b/hw/misc/axp2xx.c
>     new file mode 100644
>     index 0000000000..52a6ffc7f3
>     --- /dev/null
>     +++ b/hw/misc/axp2xx.c
>     @@ -0,0 +1,283 @@
>     +/*
>     + * AXP-2XX PMU Emulation, supported lists:
>     + *   AXP209
>     + *   AXP221
>     + *
>     + * Copyright (C) 2022 Strahinja Jankovic
>     <strahinja.p.jankovic@gmail.com>
>     + * 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 "qom/object.h"
>     +#include "trace.h"
>     +#include "hw/i2c/i2c.h"
>     +#include "migration/vmstate.h"
>     +
>     +#define TYPE_AXP2XX     "axp2xx_pmu"
>     +#define TYPE_AXP209_PMU "axp209_pmu"
>     +#define TYPE_AXP221_PMU "axp221_pmu"
>     +
>     +OBJECT_DECLARE_TYPE(AXP2xxI2CState, AXP2xxClass, AXP2XX)
>     +
>     +#define NR_REGS                            (0xff)
>     +
>     +/* A simple I2C slave which returns values of ID or CNT register. */
>     +typedef struct AXP2xxI2CState {
>     +    /*< 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 */
>     +} AXP2xxI2CState;
>     +
>     +typedef struct AXP2xxClass {
>     +    /*< private >*/
>     +    I2CSlaveClass parent_class;
>     +    /*< public >*/
>     +    void (*reset_enter)(AXP2xxI2CState *s, ResetType type);
>     +} AXP2xxClass;
>     +
>     +#define AXP209_CHIP_VERSION_ID             (0x01)
>     +#define AXP209_DC_DC2_OUT_V_CTRL_RESET     (0x16)
>     +
>     +/* Reset all counters and load ID register */
>     +static void axp209_reset_enter(AXP2xxI2CState *s, ResetType type)
>     +{
>     +    memset(s->regs, 0, NR_REGS);
>     +    s->ptr = 0;
>     +    s->count = 0;
>     +
>     +    s->regs[0x03] = AXP209_CHIP_VERSION_ID;
>     +    s->regs[0x23] = AXP209_DC_DC2_OUT_V_CTRL_RESET;
>     +
>     +    s->regs[0x30] = 0x60;
>     +    s->regs[0x32] = 0x46;
>     +    s->regs[0x34] = 0x41;
>     +    s->regs[0x35] = 0x22;
>     +    s->regs[0x36] = 0x5d;
>     +    s->regs[0x37] = 0x08;
>     +    s->regs[0x38] = 0xa5;
>     +    s->regs[0x39] = 0x1f;
>     +    s->regs[0x3a] = 0x68;
>     +    s->regs[0x3b] = 0x5f;
>     +    s->regs[0x3c] = 0xfc;
>     +    s->regs[0x3d] = 0x16;
>     +    s->regs[0x40] = 0xd8;
>     +    s->regs[0x42] = 0xff;
>     +    s->regs[0x43] = 0x3b;
>     +    s->regs[0x80] = 0xe0;
>     +    s->regs[0x82] = 0x83;
>     +    s->regs[0x83] = 0x80;
>     +    s->regs[0x84] = 0x32;
>     +    s->regs[0x86] = 0xff;
>     +    s->regs[0x90] = 0x07;
>     +    s->regs[0x91] = 0xa0;
>     +    s->regs[0x92] = 0x07;
>     +    s->regs[0x93] = 0x07;
>     +}
>     +
>     +#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(AXP2xxI2CState *s, ResetType type)
>     +{
>     +    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;
>
>
> Would it be possible to change this array to be more human readable?
>
> Perhaps using the register names from the datasheet (if available or 
> some other source) as the names for defines / enums or just as 
> comments can help.
> That way we can know what the contents mean.
I have thought about this, but I can't find a datasheet write in 
english, name all registers
an english name is hard to me. Also there has difference name for some 
registers in linux
kernel drivers and qeum drivers.

In addition we need introduce very long register name lists and default 
value lists.
That will make the code like this:

#define REG_XXX 0xXX
#define REG_XXX_DEFVAL 0xXX
...
s->regs[REG_XXX] = REG_XXX_DEFVAL;

That also make the review work more complex, we should check all REG_XXX 
number and REG_XXX_DEFVAL
based on the datasheet.

It will be easier to check with the datasheet if we use a number here.
>
>     +}
>     +
>     +static void axp2xx_reset_enter(Object *obj, ResetType type)
>     +{
>     +    AXP2xxI2CState *s = AXP2XX(obj);
>     +    AXP2xxClass *sc = AXP2XX_GET_CLASS(s);
>     +
>     +    sc->reset_enter(s, type);
>     +}
>     +
>     +/* Handle events from master. */
>     +static int axp2xx_event(I2CSlave *i2c, enum i2c_event event)
>     +{
>     +    AXP2xxI2CState *s = AXP2XX(i2c);
>     +
>     +    s->count = 0;
>     +
>     +    return 0;
>     +}
>     +
>     +/* Called when master requests read */
>     +static uint8_t axp2xx_rx(I2CSlave *i2c)
>     +{
>     +    AXP2xxI2CState *s = AXP2XX(i2c);
>     +    uint8_t ret = 0xff;
>     +
>     +    if (s->ptr < NR_REGS) {
>     +        ret = s->regs[s->ptr++];
>     +    }
>     +
>     +    trace_axp2xx_rx(s->ptr - 1, ret);
>     +
>     +    return ret;
>     +}
>     +
>     +/*
>     + * Called when master sends write.
>     + * Update ptr with byte 0, then perform write with second byte.
>     + */
>     +static int axp2xx_tx(I2CSlave *i2c, uint8_t data)
>     +{
>     +    AXP2xxI2CState *s = AXP2XX(i2c);
>     +
>     +    if (s->count == 0) {
>     +        /* Store register address */
>     +        s->ptr = data;
>     +        s->count++;
>     +        trace_axp2xx_select(data);
>     +    } else {
>     +        trace_axp2xx_tx(s->ptr, data);
>     +        s->regs[s->ptr++] = data;
>     +    }
>     +
>     +    return 0;
>     +}
>     +
>     +static const VMStateDescription vmstate_axp2xx = {
>     +    .name = TYPE_AXP209_PMU,
>
>
> Should the .name value be TYPE_AXP2xx_PMU instead, since we're 
> describing the new generic axp2xx here?
>
>     +    .version_id = 1,
>     +    .fields = (VMStateField[]) {
>     +        VMSTATE_UINT8_ARRAY(regs, AXP2xxI2CState, NR_REGS),
>     +        VMSTATE_UINT8(count, AXP2xxI2CState),
>     +        VMSTATE_UINT8(ptr, AXP2xxI2CState),
>
>
> In the 'struct AXPxxI2CState'  definition above, the order of 
> definition is: regs, ptr, count. For consistency, ptr and count should 
> then be swapped here to match.
>
>     +        VMSTATE_END_OF_LIST()
>
>
> Another question that comes to my mind here: is it allowed by the 
> migration API to have a new axp2xx device
> show up while the previous axp209 disappears? (in case for the 
> allwinner A10 cubieboard).
>
> My expectation with this code would be that during such a migration, 
> any current state of the axp209 device would be lost
> and a new fresh device would show up to the guest.
>
> I don't know enough about the migration support to tell if this is OK 
> or not.
> If another reviewer could help clarify, that would be great.
>
>     +    }
>     +};
>     +
>     +static void axp2xx_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 = axp2xx_reset_enter;
>     +    dc->vmsd = &vmstate_axp2xx;
>     +    isc->event = axp2xx_event;
>     +    isc->recv = axp2xx_rx;
>     +    isc->send = axp2xx_tx;
>     +}
>     +
>     +static const TypeInfo axp2xx_info = {
>     +    .name = TYPE_AXP2XX,
>     +    .parent = TYPE_I2C_SLAVE,
>     +    .instance_size = sizeof(AXP2xxI2CState),
>     +    .class_size = sizeof(AXP2xxClass),
>     +    .class_init = axp2xx_class_init,
>     +    .abstract = true,
>     +};
>     +
>     +static void axp209_class_init(ObjectClass *oc, void *data)
>     +{
>     +    AXP2xxClass *sc = AXP2XX_CLASS(oc);
>     +
>     +    sc->reset_enter = axp209_reset_enter;
>     +}
>     +
>     +static const TypeInfo axp209_info = {
>     +    .name = TYPE_AXP209_PMU,
>     +    .parent = TYPE_AXP2XX,
>     +    .class_init = axp209_class_init
>     +};
>     +
>     +static void axp221_class_init(ObjectClass *oc, void *data)
>     +{
>     +    AXP2xxClass *sc = AXP2XX_CLASS(oc);
>     +
>     +    sc->reset_enter = axp221_reset_enter;
>     +}
>     +
>     +static const TypeInfo axp221_info = {
>     +    .name = TYPE_AXP221_PMU,
>     +    .parent = TYPE_AXP2XX,
>     +    .class_init = axp221_class_init,
>     +};
>     +
>     +static void axp2xx_register_devices(void)
>     +{
>     +    type_register_static(&axp2xx_info);
>     +    type_register_static(&axp209_info);
>     +    type_register_static(&axp221_info);
>     +}
>     +
>     +type_init(axp2xx_register_devices);
>     diff --git a/hw/misc/meson.build b/hw/misc/meson.build
>     index 96e35f1cdb..1db0343333 100644
>     --- a/hw/misc/meson.build
>     +++ b/hw/misc/meson.build
>     @@ -45,7 +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_AXP209_PMU', if_true: files('axp209.c'))
>     +softmmu_ss.add(when: 'CONFIG_AXP2XX_PMU', if_true: files('axp2xx.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..24cdec83fe 100644
>     --- a/hw/misc/trace-events
>     +++ b/hw/misc/trace-events
>     @@ -23,10 +23,10 @@ allwinner_sid_write(uint64_t offset, uint64_t
>     data, unsigned size) "offset 0x%"
>      avr_power_read(uint8_t value) "power_reduc read value:%u"
>      avr_power_write(uint8_t value) "power_reduc write value:%u"
>
>     -# axp209.c
>     -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
>     +# axp2xx
>     +axp2xx_rx(uint8_t reg, uint8_t data) "Read reg 0x%" PRIx8 " :
>     0x%" PRIx8
>     +axp2xx_select(uint8_t reg) "Accessing reg 0x%" PRIx8
>     +axp2xx_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"
>     -- 
>     2.25.1
>
>
>
> -- 
> Niek Linnenbank
>



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

* Re: [PATCH v4 06/11] hw/arm/allwinner-r40: add SDRAM controller device
  2023-05-15 19:47   ` Niek Linnenbank
@ 2023-05-23  0:53     ` qianfan
  0 siblings, 0 replies; 31+ messages in thread
From: qianfan @ 2023-05-23  0:53 UTC (permalink / raw)
  To: Niek Linnenbank
  Cc: qemu-arm, qemu-devel, Strahinja Jankovic, Peter Maydell,
	Beniamino Galvani, Philippe Mathieu-Daudé



在 2023/5/16 3:47, Niek Linnenbank 写道:
>
>
> On Wed, May 10, 2023 at 12:30 PM <qianfanguijin@163.com> wrote:
>
>     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                |  21 +-
>      hw/arm/bananapi_m2u.c                 |   7 +
>      hw/misc/allwinner-r40-dramc.c         | 513
>     ++++++++++++++++++++++++++
>      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, 674 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 4bc582630c..0e4542d35f 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[] = {
>     @@ -53,6 +54,9 @@ const hwaddr allwinner_r40_memmap[] = {
>          [AW_R40_DEV_UART6]      = 0x01c29800,
>          [AW_R40_DEV_UART7]      = 0x01c29c00,
>          [AW_R40_DEV_TWI0]       = 0x01c2ac00,
>     +    [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, "twi0", &s->i2c0,
>     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)
>     @@ -425,6 +433,15 @@ static void allwinner_r40_realize(DeviceState
>     *dev, Error **errp)
>          sysbus_connect_irq(SYS_BUS_DEVICE(&s->i2c0), 0,
>     qdev_get_gpio_in(DEVICE(&s->gic), AW_R40_GIC_SPI_TWI0));
>
>     +    /* 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 9c5360a41b..20a4550c68 100644
>     --- a/hw/arm/bananapi_m2u.c
>     +++ b/hw/arm/bananapi_m2u.c
>     @@ -85,6 +85,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..b102bcdaba
>     --- /dev/null
>     +++ b/hw/misc/allwinner-r40-dramc.c
>     @@ -0,0 +1,513 @@
>     +/*
>     + * 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) {
>
> Since the driver only supports up to 1GiB, you could decide to just 
> remove this if(..).
OK.
>
>     +        /* 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);
>
>
> Same here: since only up to 1GiB is supported, do we need the 
> DRAMCELLS lines?
Maybe the DRAMCELLS size can down to 1G. I will do more test.
>
>     +}
>     +
>     +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 1db0343333..b04d43e05a 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_AXP2XX_PMU', if_true: files('axp2xx.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/hw/misc/trace-events b/hw/misc/trace-events
>     index 24cdec83fe..8b68f07765 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 95366f4eee..8243e8903b 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"
>     @@ -54,7 +55,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)
>     @@ -86,11 +90,18 @@ 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;
>          AwSdHostState mmc[AW_R40_NUM_MMCS];
>          AwR40ClockCtlState ccu;
>     +    AwR40DramCtlState dramc;
>          AWI2CState i2c0;
>          GICState gic;
>          MemoryRegion sram_a1;
>     diff --git a/include/hw/misc/allwinner-r40-dramc.h
>     b/include/hw/misc/allwinner-r40-dramc.h
>     new file mode 100644
>     index 0000000000..6a1a3a7893
>     --- /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]; /**< DRAMCOM
>     registers */
>     +    uint32_t dramctl[AW_R40_DRAMCTL_REGS_NUM]; /**< DRAMCTL
>     registers */
>     +    uint32_t dramphy[AW_R40_DRAMPHY_REGS_NUM] ;/**< DRAMPHY
>     registers */
>     +
>     +    /** @} */
>     +
>     +};
>     +
>     +#endif /* HW_MISC_ALLWINNER_R40_DRAMC_H */
>     -- 
>     2.25.1
>
>
> Looks good to me.
>
> Reviewed-by: Niek Linnenbank <nieklinnenbank@gmail.com>
>
> -- 
> Niek Linnenbank
>



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

* Re: [PATCH v4 07/11] hw: sd: allwinner-sdhost: Add sun50i-a64 SoC support
  2023-05-15 19:54   ` Niek Linnenbank
@ 2023-05-23  0:53     ` qianfan
  0 siblings, 0 replies; 31+ messages in thread
From: qianfan @ 2023-05-23  0:53 UTC (permalink / raw)
  To: Niek Linnenbank
  Cc: qemu-arm, qemu-devel, Strahinja Jankovic, Peter Maydell,
	Beniamino Galvani, Philippe Mathieu-Daudé



在 2023/5/16 3:54, Niek Linnenbank 写道:
>
>
> On Wed, May 10, 2023 at 12:30 PM <qianfanguijin@163.com> wrote:
>
>     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.
>
>     Also fix allwinner-r40's mmc controller type.
>
>     Signed-off-by: qianfan Zhao <qianfanguijin@163.com>
>     ---
>      hw/arm/allwinner-r40.c           |  2 +-
>      hw/sd/allwinner-sdhost.c         | 70
>     ++++++++++++++++++++++++++++++--
>      include/hw/sd/allwinner-sdhost.h |  9 ++++
>      3 files changed, 77 insertions(+), 4 deletions(-)
>
>     diff --git a/hw/arm/allwinner-r40.c b/hw/arm/allwinner-r40.c
>     index 0e4542d35f..b148c56449 100644
>     --- a/hw/arm/allwinner-r40.c
>     +++ b/hw/arm/allwinner-r40.c
>     @@ -271,7 +271,7 @@ static void allwinner_r40_init(Object *obj)
>
>          for (int i = 0; i < AW_R40_NUM_MMCS; i++) {
>              object_initialize_child(obj, mmc_names[i], &s->mmc[i],
>     -                                TYPE_AW_SDHOST_SUN5I);
>     +                                TYPE_AW_SDHOST_SUN50I_A64);
>          }
>
>          object_initialize_child(obj, "twi0", &s->i2c0,
>     TYPE_AW_I2C_SUN6I);
>     diff --git a/hw/sd/allwinner-sdhost.c b/hw/sd/allwinner-sdhost.c
>     index 92a0f42708..f4fa2d179b 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,
>      };
>
>     @@ -459,6 +461,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) {
>     @@ -577,13 +580,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;
>      }
>     @@ -602,6 +616,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);
>
>     @@ -725,10 +740,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;
>          }
>      }
>
>     @@ -777,6 +803,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()
>          }
>      };
>     @@ -815,6 +842,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;
>     @@ -855,6 +883,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)
>     @@ -888,6 +920,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;
>
>
> For the existing functions allwinner_sdhost_sun4i_class_init() and 
> allwinner_sdhost_sun5i_class_init(), could you please add 
> 'sc->can_calibrate = false'?
> That way, we make it explicit that those types do not support the 
> sample delay register.
Sure.
>
> With that resolved, the code looks good to me:
>
> Reviewed-by: Niek Linnenbank <nieklinnenbank@gmail.com>
>
>     +}
>     +
>     +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,
>     @@ -910,6 +960,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,
>     @@ -922,6 +984,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
>
>
>
> -- 
> Niek Linnenbank
>



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

* Re: [PATCH v4 08/11] hw: arm: allwinner-r40: Add emac and gmac support
  2023-05-15 19:58   ` Niek Linnenbank
@ 2023-05-23  1:01     ` qianfan
  0 siblings, 0 replies; 31+ messages in thread
From: qianfan @ 2023-05-23  1:01 UTC (permalink / raw)
  To: Niek Linnenbank
  Cc: qemu-arm, qemu-devel, Strahinja Jankovic, Peter Maydell,
	Beniamino Galvani, Philippe Mathieu-Daudé



在 2023/5/16 3:58, Niek Linnenbank 写道:
> Hi Qianfan,
>
> On Wed, May 10, 2023 at 12:30 PM <qianfanguijin@163.com> wrote:
>
>     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         | 50
>     ++++++++++++++++++++++++++++++++--
>      hw/arm/bananapi_m2u.c          |  3 ++
>      include/hw/arm/allwinner-r40.h |  6 ++++
>      3 files changed, 57 insertions(+), 2 deletions(-)
>
>     diff --git a/hw/arm/allwinner-r40.c b/hw/arm/allwinner-r40.c
>     index b148c56449..c018ad231a 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,
>     @@ -54,6 +55,7 @@ const hwaddr allwinner_r40_memmap[] = {
>          [AW_R40_DEV_UART6]      = 0x01c29800,
>          [AW_R40_DEV_UART7]      = 0x01c29c00,
>          [AW_R40_DEV_TWI0]       = 0x01c2ac00,
>     +    [AW_R40_DEV_GMAC]       = 0x01c50000,
>          [AW_R40_DEV_DRAMCOM]    = 0x01c62000,
>          [AW_R40_DEV_DRAMCTL]    = 0x01c63000,
>          [AW_R40_DEV_DRAMPHY]    = 0x01c65000,
>     @@ -82,7 +84,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 },
>     @@ -180,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_EMAC      = 55,
>     +    AW_R40_GIC_SPI_GMAC      = 85,
>      };
>
>      /* Allwinner R40 general constants */
>     @@ -276,6 +278,11 @@ static void allwinner_r40_init(Object *obj)
>
>          object_initialize_child(obj, "twi0", &s->i2c0,
>     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");
>     @@ -285,6 +292,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;
>
>     @@ -442,6 +450,44 @@ static void allwinner_r40_realize(DeviceState
>     *dev, Error **errp)
>          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;
>     +        }
>
>
> Could you please clarify the lines below here? I'm not seeing the 
> function call 'qemu_show_nic_models()' in any of the other machines / 
> soc implementations.
>
> Also, if you intend to catch a possible input error here, probably its 
> best to log/print the error for the user before calling exit()?
This is useful if the user don't known nic model names. Can running this 
command for a helper message:

$ qemu-system-arm -M bpim2u -net nic,model=help
Unable to init server: Could not connect: Connection refused
Available NIC models:
gmac
emac
>
>     +        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 20a4550c68..74121d8966 100644
>     --- a/hw/arm/bananapi_m2u.c
>     +++ b/hw/arm/bananapi_m2u.c
>     @@ -92,6 +92,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 8243e8903b..5f2d08489e 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,
>     @@ -51,6 +54,7 @@ enum {
>          AW_R40_DEV_UART6,
>          AW_R40_DEV_UART7,
>          AW_R40_DEV_TWI0,
>     +    AW_R40_DEV_GMAC,
>          AW_R40_DEV_GIC_DIST,
>          AW_R40_DEV_GIC_CPU,
>          AW_R40_DEV_GIC_HYP,
>     @@ -103,6 +107,8 @@ struct AwR40State {
>          AwR40ClockCtlState ccu;
>          AwR40DramCtlState dramc;
>          AWI2CState i2c0;
>     +    AwEmacState emac;
>     +    AwSun8iEmacState gmac;
>          GICState gic;
>          MemoryRegion sram_a1;
>          MemoryRegion sram_a2;
>     -- 
>     2.25.1
>
>
> Regards,
> Niek
> -- 
> Niek Linnenbank
>



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

* Re: [PATCH v4 01/11] hw: arm: Add bananapi M2-Ultra and allwinner-r40 support
  2023-05-14 18:50   ` Niek Linnenbank
@ 2023-05-23  1:21     ` qianfan
  0 siblings, 0 replies; 31+ messages in thread
From: qianfan @ 2023-05-23  1:21 UTC (permalink / raw)
  To: Niek Linnenbank
  Cc: qemu-arm, qemu-devel, Strahinja Jankovic, Peter Maydell,
	Beniamino Galvani, Philippe Mathieu-Daudé



在 2023/5/15 2:50, Niek Linnenbank 写道:
> Hi Qianfan,
>
>
> On Wed, May 10, 2023 at 12:30 PM <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).
>
>     Signed-off-by: qianfan Zhao <qianfanguijin@163.com>
>     ---
>      hw/arm/Kconfig                 |  10 +
>      hw/arm/allwinner-r40.c         | 418
>     +++++++++++++++++++++++++++++++++
>      hw/arm/bananapi_m2u.c          | 129 ++++++++++
>      hw/arm/meson.build             |   1 +
>      include/hw/arm/allwinner-r40.h | 110 +++++++++
>      5 files changed, 668 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/hw/arm/Kconfig b/hw/arm/Kconfig
>     index 2d7c457955..b7a84f6e3f 100644
>     --- a/hw/arm/Kconfig
>     +++ b/hw/arm/Kconfig
>     @@ -374,6 +374,16 @@ config ALLWINNER_H3
>          select USB_EHCI_SYSBUS
>          select SD
>
>     +config ALLWINNER_R40
>     +    bool
>     +    default y if TCG && ARM
>     +    select ALLWINNER_A10_PIT
>     +    select SERIAL
>     +    select ARM_TIMER
>     +    select ARM_GIC
>     +    select UNIMP
>     +    select SD
>     +
>      config RASPI
>          bool
>          default y if TCG && ARM
>     diff --git a/hw/arm/allwinner-r40.c b/hw/arm/allwinner-r40.c
>     new file mode 100644
>     index 0000000000..b743d64253
>     --- /dev/null
>     +++ b/hw/arm/allwinner-r40.c
>     @@ -0,0 +1,418 @@
>     +/*
>     + * 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 },
>
>
> When I started reviewing patch 03 ("hw: allwinner-r40: Complete uart 
> devices"), I realized I missed pointing out here that
> uart0 is inside the unimplemented array, while it is actually 
> implemented already in this patch.
Thanks and this will be fixed on the next version.
> Regards,
> Niek
>
>     +    { "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;
>     +    }
>     +
>     +    /*
>     +     * Simulate the behavior of the bootROM, it will change the
>     boot_media
>     +     * flag to indicate where the chip is booting from. R40 can
>     boot from
>     +     * mmc0 or mmc2, the default value of boot_media is zero
>     +     * (SUNXI_BOOTED_FROM_MMC0), let's fix this flag when it is
>     booting from
>     +     * the others.
>     +     */
>     +    if (unit == 2) {
>     +        head->boot_media = cpu_to_le32(SUNXI_BOOTED_FROM_MMC2);
>     +    } else {
>     +        head->boot_media = cpu_to_le32(SUNXI_BOOTED_FROM_MMC0);
>     +    }
>     +
>     +    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)
>     +{
>     +    static const char *mmc_names[AW_R40_NUM_MMCS] = {
>     +        "mmc0", "mmc1", "mmc2", "mmc3"
>     +    };
>     +    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");
>     +
>     +    for (int i = 0; i < AW_R40_NUM_MMCS; i++) {
>     +        object_initialize_child(obj, mmc_names[i], &s->mmc[i],
>     +                                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 GICv2
>     +     * 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 */
>     +    for (int i = 0; i < AW_R40_NUM_MMCS; i++) {
>     +        qemu_irq irq = qdev_get_gpio_in(DEVICE(&s->gic),
>     +                                        AW_R40_GIC_SPI_MMC0 + i);
>     +        const hwaddr addr = s->memmap[AW_R40_DEV_MMC0 + i];
>     +
>     +        object_property_set_link(OBJECT(&s->mmc[i]), "dma-memory",
>     +  OBJECT(get_system_memory()), &error_fatal);
>     +        sysbus_realize(SYS_BUS_DEVICE(&s->mmc[i]), &error_fatal);
>     +        sysbus_mmio_map(SYS_BUS_DEVICE(&s->mmc[i]), 0, addr);
>     + sysbus_connect_irq(SYS_BUS_DEVICE(&s->mmc[i]), 0, irq);
>     +    }
>     +
>     +    /* 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..1d49a006b5
>     --- /dev/null
>     +++ b/hw/arm/bananapi_m2u.c
>     @@ -0,0 +1,129 @@
>     +/*
>     + * 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 "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;
>     +
>     +/*
>     + * R40 can boot from mmc0 and mmc2, and bpim2u has two mmc
>     interface, one is
>     + * connected to sdcard and another mount an emmc media.
>     + * Attach the mmc driver and try loading bootloader.
>     + */
>     +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;
>     +
>     +    /* 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 has 4 mmc
>     controllers but can
>     +     * only booting from mmc0 and mmc2.
>     +     */
>     +    for (int i = 0; i < AW_R40_NUM_MMCS; i++) {
>     +        switch (i) {
>     +        case 0:
>     +        case 2:
>     +            mmc_attach_drive(r40, &r40->mmc[i], i,
>     +                             !machine->kernel_filename &&
>     !bootroom_loaded,
>     +                             &bootroom_loaded);
>     +            break;
>     +        default:
>     +            mmc_attach_drive(r40, &r40->mmc[i], i, false, NULL);
>     +            break;
>     +        }
>     +    }
>     +
>     +    /* 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..348bf25d6b
>     --- /dev/null
>     +++ b/include/hw/arm/allwinner-r40.h
>     @@ -0,0 +1,110 @@
>     +/*
>     + * 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.
>     + */
>     +#define AW_R40_NUM_MMCS         4
>     +
>     +struct AwR40State {
>     +    /*< private >*/
>     +    DeviceState parent_obj;
>     +    /*< public >*/
>     +
>     +    ARMCPU cpus[AW_R40_NUM_CPUS];
>     +    const hwaddr *memmap;
>     +    AwA10PITState timer;
>     +    AwSdHostState mmc[AW_R40_NUM_MMCS];
>     +    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
>
>
>
> -- 
> Niek Linnenbank
>



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

* Re: [PATCH v4 03/11] hw: allwinner-r40: Complete uart devices
  2023-05-14 18:55   ` Niek Linnenbank
@ 2023-05-23  1:24     ` qianfan
  2023-05-23  3:00     ` qianfan
  1 sibling, 0 replies; 31+ messages in thread
From: qianfan @ 2023-05-23  1:24 UTC (permalink / raw)
  To: Niek Linnenbank
  Cc: qemu-arm, qemu-devel, Strahinja Jankovic, Peter Maydell,
	Beniamino Galvani, Philippe Mathieu-Daudé



在 2023/5/15 2:55, Niek Linnenbank 写道:
> Hi Qianfan,
>
>
> On Wed, May 10, 2023 at 12:30 PM <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         | 31 ++++++++++++++++++++++++++++---
>      include/hw/arm/allwinner-r40.h |  8 ++++++++
>      2 files changed, 36 insertions(+), 3 deletions(-)
>
>     diff --git a/hw/arm/allwinner-r40.c b/hw/arm/allwinner-r40.c
>     index 128c0ca470..537a90b23d 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,
>
>
> After adding the uarts to the memory map here, you should remove them 
> from the unimplemented array.
OK.
>
>          [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,
>
>
> Since you put the addition of UART1-7 in this patch, probably it makes 
> sense to have adding the lines 'AW_R40_GIC_SPI_UART1/2/3' also part of 
> this patch.
OK
>
> With the two above remarks resolved, the patch looks good to me.
>
> Reviewed-by: Niek Linnenbank <nieklinnenbank@gmail.com>
>
> Regards,
> Niek
>
>     +    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,
>     @@ -387,9 +398,23 @@ static void allwinner_r40_realize(DeviceState
>     *dev, Error **errp)
>          }
>
>          /* 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);
>     +    for (int i = 0; i < AW_R40_NUM_UARTS; i++) {
>     +        static const int uart_irqs[AW_R40_NUM_UARTS] = {
>     +            AW_R40_GIC_SPI_UART0,
>     +            AW_R40_GIC_SPI_UART1,
>     +            AW_R40_GIC_SPI_UART2,
>     +            AW_R40_GIC_SPI_UART3,
>     +            AW_R40_GIC_SPI_UART4,
>     +            AW_R40_GIC_SPI_UART5,
>     +            AW_R40_GIC_SPI_UART6,
>     +            AW_R40_GIC_SPI_UART7,
>     +        };
>     +        const hwaddr addr = s->memmap[AW_R40_DEV_UART0 + i];
>     +
>     +        serial_mm_init(get_system_memory(), addr, 2,
>     +  qdev_get_gpio_in(DEVICE(&s->gic), uart_irqs[i]),
>     +                       115200, serial_hd(i), 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 3be9dc962b..959b5dc4e0 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,
>     @@ -70,6 +77,7 @@ OBJECT_DECLARE_SIMPLE_TYPE(AwR40State, AW_R40)
>       * which are currently emulated by the R40 SoC code.
>       */
>      #define AW_R40_NUM_MMCS         4
>     +#define AW_R40_NUM_UARTS        8
>
>      struct AwR40State {
>          /*< private >*/
>     -- 
>     2.25.1
>
>
>
> -- 
> Niek Linnenbank
>



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

* Re: [PATCH v4 03/11] hw: allwinner-r40: Complete uart devices
  2023-05-14 18:55   ` Niek Linnenbank
  2023-05-23  1:24     ` qianfan
@ 2023-05-23  3:00     ` qianfan
  1 sibling, 0 replies; 31+ messages in thread
From: qianfan @ 2023-05-23  3:00 UTC (permalink / raw)
  To: Niek Linnenbank
  Cc: qemu-arm, qemu-devel, Strahinja Jankovic, Peter Maydell,
	Beniamino Galvani, Philippe Mathieu-Daudé



在 2023/5/15 2:55, Niek Linnenbank 写道:
> Hi Qianfan,
>
>
> On Wed, May 10, 2023 at 12:30 PM <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         | 31 ++++++++++++++++++++++++++++---
>      include/hw/arm/allwinner-r40.h |  8 ++++++++
>      2 files changed, 36 insertions(+), 3 deletions(-)
>
>     diff --git a/hw/arm/allwinner-r40.c b/hw/arm/allwinner-r40.c
>     index 128c0ca470..537a90b23d 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,
>
>
> After adding the uarts to the memory map here, you should remove them 
> from the unimplemented array.
Hi:

I had tried this including remove UART0 from allwinner_r40_memmap, but 
that will make qemu
for R40 doesn't work again. Only a few registers are implemented in 
hw/char/serial.c,
so we still need this.
>
>          [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,
>
>
> Since you put the addition of UART1-7 in this patch, probably it makes 
> sense to have adding the lines 'AW_R40_GIC_SPI_UART1/2/3' also part of 
> this patch.
>
> With the two above remarks resolved, the patch looks good to me.
>
> Reviewed-by: Niek Linnenbank <nieklinnenbank@gmail.com>
>
> Regards,
> Niek
>
>     +    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,
>     @@ -387,9 +398,23 @@ static void allwinner_r40_realize(DeviceState
>     *dev, Error **errp)
>          }
>
>          /* 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);
>     +    for (int i = 0; i < AW_R40_NUM_UARTS; i++) {
>     +        static const int uart_irqs[AW_R40_NUM_UARTS] = {
>     +            AW_R40_GIC_SPI_UART0,
>     +            AW_R40_GIC_SPI_UART1,
>     +            AW_R40_GIC_SPI_UART2,
>     +            AW_R40_GIC_SPI_UART3,
>     +            AW_R40_GIC_SPI_UART4,
>     +            AW_R40_GIC_SPI_UART5,
>     +            AW_R40_GIC_SPI_UART6,
>     +            AW_R40_GIC_SPI_UART7,
>     +        };
>     +        const hwaddr addr = s->memmap[AW_R40_DEV_UART0 + i];
>     +
>     +        serial_mm_init(get_system_memory(), addr, 2,
>     +  qdev_get_gpio_in(DEVICE(&s->gic), uart_irqs[i]),
>     +                       115200, serial_hd(i), 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 3be9dc962b..959b5dc4e0 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,
>     @@ -70,6 +77,7 @@ OBJECT_DECLARE_SIMPLE_TYPE(AwR40State, AW_R40)
>       * which are currently emulated by the R40 SoC code.
>       */
>      #define AW_R40_NUM_MMCS         4
>     +#define AW_R40_NUM_UARTS        8
>
>      struct AwR40State {
>          /*< private >*/
>     -- 
>     2.25.1
>
>
>
> -- 
> Niek Linnenbank
>



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

* Re: [PATCH v4 00/11] *** Add allwinner r40 support ***
  2023-05-15 20:25 ` [PATCH v4 00/11] *** Add allwinner r40 support *** Niek Linnenbank
@ 2023-05-25  9:48   ` Peter Maydell
  0 siblings, 0 replies; 31+ messages in thread
From: Peter Maydell @ 2023-05-25  9:48 UTC (permalink / raw)
  To: Niek Linnenbank
  Cc: qianfanguijin, qemu-arm, qemu-devel, Strahinja Jankovic,
	Beniamino Galvani, Philippe Mathieu-Daudé

On Mon, 15 May 2023 at 21:25, Niek Linnenbank <nieklinnenbank@gmail.com> wrote:
>
> Hi Qianfan,
>
> Thanks for sending the v4. I've reviewed all patches and replied to each individual patch.
>
> Great work so far!
>
> One thing I wanted to mention is that when you receive a 'Reviewed-by:' or 'Tested-by:' line from a reviewer,
> please feel free to include it in the commit message on the next version of that patch. This helps you and reviewers
> to keep track on what has been reviewed and what not.

Looks like this didn't happen for v5 :-(  (If it had, I'd
probably have been able to take v5 into target-arm.next...)

thanks
-- PMM


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

end of thread, other threads:[~2023-05-25  9:50 UTC | newest]

Thread overview: 31+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-05-10 10:29 [PATCH v4 00/11] *** Add allwinner r40 support *** qianfanguijin
2023-05-10 10:29 ` [PATCH v4 01/11] hw: arm: Add bananapi M2-Ultra and allwinner-r40 support qianfanguijin
2023-05-14 18:38   ` Niek Linnenbank
2023-05-14 18:50   ` Niek Linnenbank
2023-05-23  1:21     ` qianfan
2023-05-10 10:29 ` [PATCH v4 02/11] hw/arm/allwinner-r40: add Clock Control Unit qianfanguijin
2023-05-14 18:40   ` Niek Linnenbank
2023-05-10 10:29 ` [PATCH v4 03/11] hw: allwinner-r40: Complete uart devices qianfanguijin
2023-05-14 18:55   ` Niek Linnenbank
2023-05-23  1:24     ` qianfan
2023-05-23  3:00     ` qianfan
2023-05-10 10:29 ` [PATCH v4 04/11] hw: arm: allwinner-r40: Add i2c0 device qianfanguijin
2023-05-14 18:57   ` Niek Linnenbank
2023-05-10 10:29 ` [PATCH v4 05/11] hw/misc: Rename axp209 to axp22x and add support AXP221 PMU qianfanguijin
2023-05-15 19:29   ` Niek Linnenbank
2023-05-23  0:40     ` qianfan
2023-05-10 10:29 ` [PATCH v4 06/11] hw/arm/allwinner-r40: add SDRAM controller device qianfanguijin
2023-05-15 19:47   ` Niek Linnenbank
2023-05-23  0:53     ` qianfan
2023-05-10 10:30 ` [PATCH v4 07/11] hw: sd: allwinner-sdhost: Add sun50i-a64 SoC support qianfanguijin
2023-05-15 19:54   ` Niek Linnenbank
2023-05-23  0:53     ` qianfan
2023-05-10 10:30 ` [PATCH v4 08/11] hw: arm: allwinner-r40: Add emac and gmac support qianfanguijin
2023-05-15 19:58   ` Niek Linnenbank
2023-05-23  1:01     ` qianfan
2023-05-10 10:30 ` [PATCH v4 09/11] hw: arm: allwinner-sramc: Add SRAM Controller support for R40 qianfanguijin
2023-05-15 20:05   ` Niek Linnenbank
2023-05-10 10:30 ` [PATCH v4 10/11] tests: avocado: boot_linux_console: Add test case for bpim2u qianfanguijin
2023-05-15 20:09   ` Niek Linnenbank
2023-05-15 20:25 ` [PATCH v4 00/11] *** Add allwinner r40 support *** Niek Linnenbank
2023-05-25  9:48   ` Peter Maydell

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