From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.lore.kernel.org (Postfix) with ESMTPS id 94FB0C77B6C for ; Thu, 6 Apr 2023 19:14:17 +0000 (UTC) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1pkV2k-00028T-GL; Thu, 06 Apr 2023 15:13:10 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1pkV2Y-00027H-If; Thu, 06 Apr 2023 15:13:02 -0400 Received: from mail-yb1-xb2d.google.com ([2607:f8b0:4864:20::b2d]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1pkV2R-00039S-Iv; Thu, 06 Apr 2023 15:12:56 -0400 Received: by mail-yb1-xb2d.google.com with SMTP id h198so3495332ybg.12; Thu, 06 Apr 2023 12:12:49 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; t=1680808369; h=cc:to:subject:message-id:date:from:in-reply-to:references :mime-version:from:to:cc:subject:date:message-id:reply-to; bh=hT2HNZJ6AF6V7qoJK6ZfPhrUbWzec1Ita5jqJ7GoDlQ=; b=q3b8vzqV6AV0swTvRKM28sM//HYyeN+4L52oo+jAJd5chQIjzhvXQ3bqCIkhlOG6aJ OHZtb5EVHX/PragGqv7kiAVCvYiSWK8QXLysv7lR4vLpiq8aExsJpZkqaB1lmvPuCyPH 4RjNKem/DtOdwSLKbt7oWKZ4su3ADxbUKm51Dr8mOz+Jjwj3V18E62cACMOerk/Ys7IW 2w74VzpG7+DWD9xUTVNIGUK+luAfxgO4GyDtB0ffSQgbLEf+qqMBcLXBgbYf1TjEjjsQ wVY0EtV9RXttcPyK6cS3oDuB2Ni/57Eta8VylgXogfSPVUMIGKiz6R3eZFpjd1APRbp3 n56Q== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; t=1680808369; h=cc:to:subject:message-id:date:from:in-reply-to:references :mime-version:x-gm-message-state:from:to:cc:subject:date:message-id :reply-to; bh=hT2HNZJ6AF6V7qoJK6ZfPhrUbWzec1Ita5jqJ7GoDlQ=; b=t7cz+hJmsUB0A4kyzioIBrmJ+nDZXUiDMFm6J/b7VQQUc333uzwAIR/P/sd2yUEVFY zgk0YYT+xYSvfyX9n7lPJOXecgPhi+cAfsKN6x2FIUhtqdU4hYdaVC5Uh6jybAfyxvyx hcCrgy8v1PCY8j76mSryf/7uqNgCvJBwVMI7j6lLOhENqD0LWW3b/hfj3JBXmNZt+vPP QnCSjY8qCjJtE//DcuU3tEQy1eKhRbtKAMZM1Y7SVdXkgJX8JRt01S3t0UMKL+Z1xIZG ep/a2NBrZmV1bX+YpAMW3Zrd3JzGt7eJoCA7csdGGS8lvkoaOt0r8IaOkutdfJPGMaVH nCSw== X-Gm-Message-State: AAQBX9fkLvx59oYdhqwOQXgP8OwjFpkr1lDXD0RLhGbTfGsbqz5ELrkS zQ1cCa16wRxWlIHEcK6L/FKGpWvRcJn7S9M+88E= X-Google-Smtp-Source: AKy350ZJUANDE5pKxxXsACg6qSwNlsA280n5oTKOyOBRD31DIV6b9rKQ8i7ICFZo+2TWFZbYh7Gr4lcYUJRn0m6IDEc= X-Received: by 2002:a25:d742:0:b0:b45:5cbe:48b3 with SMTP id o63-20020a25d742000000b00b455cbe48b3mr328720ybg.0.1680808368905; Thu, 06 Apr 2023 12:12:48 -0700 (PDT) MIME-Version: 1.0 References: <20230328054654.18620-1-qianfanguijin@163.com> <20230328054654.18620-2-qianfanguijin@163.com> In-Reply-To: <20230328054654.18620-2-qianfanguijin@163.com> From: Niek Linnenbank Date: Thu, 6 Apr 2023 21:12:37 +0200 Message-ID: Subject: Re: [PATCH v2 01/12] hw: arm: Add bananapi M2-Ultra and allwinner-r40 support To: qianfanguijin@163.com Cc: qemu-arm@nongnu.org, qemu-devel@nongnu.org, Strahinja Jankovic , Peter Maydell , Beniamino Galvani , =?UTF-8?Q?Philippe_Mathieu=2DDaud=C3=A9?= Content-Type: multipart/alternative; boundary="000000000000c3a22705f8afb172" Received-SPF: pass client-ip=2607:f8b0:4864:20::b2d; envelope-from=nieklinnenbank@gmail.com; helo=mail-yb1-xb2d.google.com X-Spam_score_int: -20 X-Spam_score: -2.1 X-Spam_bar: -- X-Spam_report: (-2.1 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, FREEMAIL_FROM=0.001, HTML_MESSAGE=0.001, RCVD_IN_DNSWL_NONE=-0.0001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+qemu-devel=archiver.kernel.org@nongnu.org Sender: qemu-devel-bounces+qemu-devel=archiver.kernel.org@nongnu.org --000000000000c3a22705f8afb172 Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: quoted-printable Hi Qianfan Zhao, On Tue, Mar 28, 2023 at 7:47=E2=80=AFAM wrote: > From: qianfan Zhao > > Allwinner R40 (sun8i) SoC features a Quad-Core Cortex-A7 ARM CPU, > and a Mali400 MP2 GPU from ARM. It's also known as the Allwinner T3 > for In-Car Entertainment usage, A40i and A40pro are variants that > differ in applicable temperatures range (industrial and military). > > This patch is a draft and provides very few features that we will > improve late. > Perhaps this line above in the commit message can now be updated/removed? > > Signed-off-by: qianfan Zhao > --- > configs/devices/arm-softmmu/default.mak | 1 + > hw/arm/Kconfig | 9 + > hw/arm/allwinner-r40.c | 418 ++++++++++++++++++++++++ > hw/arm/bananapi_m2u.c | 129 ++++++++ > hw/arm/meson.build | 1 + > include/hw/arm/allwinner-r40.h | 110 +++++++ > 6 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/configs/devices/arm-softmmu/default.mak > b/configs/devices/arm-softmmu/default.mak > index 1b49a7830c..76a43add23 100644 > --- a/configs/devices/arm-softmmu/default.mak > +++ b/configs/devices/arm-softmmu/default.mak > @@ -43,3 +43,4 @@ CONFIG_FSL_IMX6UL=3Dy > CONFIG_SEMIHOSTING=3Dy > CONFIG_ARM_COMPATIBLE_SEMIHOSTING=3Dy > CONFIG_ALLWINNER_H3=3Dy > +CONFIG_ALLWINNER_R40=3Dy > diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig > index b5aed4aff5..9e14c3427e 100644 > --- a/hw/arm/Kconfig > +++ b/hw/arm/Kconfig > @@ -344,6 +344,15 @@ config ALLWINNER_H3 > select USB_EHCI_SYSBUS > select SD > > +config ALLWINNER_R40 > + bool > + select ALLWINNER_A10_PIT > + select SERIAL > + select ARM_TIMER > + select ARM_GIC > + select UNIMP > + select SD > + > config RASPI > bool > select FRAMEBUFFER > diff --git a/hw/arm/allwinner-r40.c b/hw/arm/allwinner-r40.c > new file mode 100644 > index 0000000000..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 > + * > + * 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 . > + */ > + > +#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[] =3D { > + [AW_R40_DEV_SRAM_A1] =3D 0x00000000, > + [AW_R40_DEV_SRAM_A2] =3D 0x00004000, > + [AW_R40_DEV_SRAM_A3] =3D 0x00008000, > + [AW_R40_DEV_SRAM_A4] =3D 0x0000b400, > + [AW_R40_DEV_MMC0] =3D 0x01c0f000, > + [AW_R40_DEV_MMC1] =3D 0x01c10000, > + [AW_R40_DEV_MMC2] =3D 0x01c11000, > + [AW_R40_DEV_MMC3] =3D 0x01c12000, > + [AW_R40_DEV_PIT] =3D 0x01c20c00, > + [AW_R40_DEV_UART0] =3D 0x01c28000, > + [AW_R40_DEV_GIC_DIST] =3D 0x01c81000, > + [AW_R40_DEV_GIC_CPU] =3D 0x01c82000, > + [AW_R40_DEV_GIC_HYP] =3D 0x01c84000, > + [AW_R40_DEV_GIC_VCPU] =3D 0x01c86000, > + [AW_R40_DEV_SDRAM] =3D 0x40000000 > +}; > + > +/* List of unimplemented devices */ > +struct AwR40Unimplemented { > + const char *device_name; > + hwaddr base; > + hwaddr size; > +}; > + > +static struct AwR40Unimplemented r40_unimplemented[] =3D { > + { "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 =3D 9, > + AW_R40_GIC_PPI_HYPTIMER =3D 10, > + AW_R40_GIC_PPI_VIRTTIMER =3D 11, > + AW_R40_GIC_PPI_SECTIMER =3D 13, > + AW_R40_GIC_PPI_PHYSTIMER =3D 14 > +}; > + > +/* Shared Processor Interrupts */ > +enum { > + AW_R40_GIC_SPI_UART0 =3D 1, > + AW_R40_GIC_SPI_UART1 =3D 2, > + AW_R40_GIC_SPI_UART2 =3D 3, > + AW_R40_GIC_SPI_UART3 =3D 4, > + AW_R40_GIC_SPI_TIMER0 =3D 22, > + AW_R40_GIC_SPI_TIMER1 =3D 23, > + AW_R40_GIC_SPI_MMC0 =3D 32, > + AW_R40_GIC_SPI_MMC1 =3D 33, > + AW_R40_GIC_SPI_MMC2 =3D 34, > + AW_R40_GIC_SPI_MMC3 =3D 35, > +}; > + > +/* Allwinner R40 general constants */ > +enum { > + AW_R40_GIC_NUM_SPI =3D 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 =3D 32 * KiB; > + g_autofree uint8_t *buffer =3D g_new0(uint8_t, rom_size); > + struct boot_file_head *head =3D (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_med= ia > + * flag to indicate where the chip is booting from. R40 can boot fro= m > + * 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 =3D=3D 2) { > + head->boot_media =3D cpu_to_le32(SUNXI_BOOTED_FROM_MMC2); > + } else { > + head->boot_media =3D 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] =3D { > + "mmc0", "mmc1", "mmc2", "mmc3" > + }; > + AwR40State *s =3D AW_R40(obj); > + > + s->memmap =3D allwinner_r40_memmap; > + > + for (int i =3D 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 =3D 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 =3D AW_R40(dev); > + unsigned i; > + > + /* CPUs */ > + for (i =3D 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 =3D 0; i < AW_R40_NUM_CPUS; i++) { > + DeviceState *cpudev =3D DEVICE(&s->cpus[i]); > + int ppibase =3D AW_R40_GIC_NUM_SPI + i * GIC_INTERNAL + GIC_NR_S= GIS; > + 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[] =3D { > + [GTIMER_PHYS] =3D AW_R40_GIC_PPI_PHYSTIMER, > + [GTIMER_VIRT] =3D AW_R40_GIC_PPI_VIRTTIMER, > + [GTIMER_HYP] =3D AW_R40_GIC_PPI_HYPTIMER, > + [GTIMER_SEC] =3D AW_R40_GIC_PPI_SECTIMER, > + }; > + > + /* Connect CPU timer outputs to GIC PPI inputs */ > + for (irq =3D 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 =3D 0; i < AW_R40_NUM_MMCS; i++) { > + qemu_irq irq =3D qdev_get_gpio_in(DEVICE(&s->gic), > + AW_R40_GIC_SPI_MMC0 + i); > + const hwaddr addr =3D 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 =3D 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 =3D DEVICE_CLASS(oc); > + > + dc->realize =3D allwinner_r40_realize; > + /* Reason: uses serial_hd() in realize function */ > + dc->user_creatable =3D false; > +} > + > +static const TypeInfo allwinner_r40_type_info =3D { > + .name =3D TYPE_AW_R40, > + .parent =3D TYPE_DEVICE, > + .instance_size =3D sizeof(AwR40State), > + .instance_init =3D allwinner_r40_init, > + .class_init =3D 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 > + * > + * 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 . > + */ > + > +#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, on= e > 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 =3D drive_get(IF_SD, 0, unit); > + BlockBackend *blk =3D di ? blk_by_legacy_dinfo(di) : NULL; > + BusState *bus; > + DeviceState *carddev; > + > + bus =3D qdev_get_child_bus(DEVICE(mmc), "sd-bus"); > + if (bus =3D=3D NULL) { > + error_report("No SD bus found in SOC object"); > + exit(1); > + } > + > + carddev =3D 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 =3D allwinner_r40_bootrom_setup(s, blk, unit); > + } > +} > + > +static void bpim2u_init(MachineState *machine) > +{ > + bool bootroom_loaded =3D 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")) !=3D 0= ) { > + error_report("This board can only be used with cortex-a7 CPU"); > + exit(1); > + } > + > + r40 =3D 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 =3D 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 =3D r40->memmap[AW_R40_DEV_SDRAM]; > + bpim2u_binfo.ram_size =3D machine->ram_size; > + bpim2u_binfo.psci_conduit =3D QEMU_PSCI_CONDUIT_SMC; > + arm_load_kernel(ARM_CPU(first_cpu), machine, &bpim2u_binfo); > +} > + > +static void bpim2u_machine_init(MachineClass *mc) > +{ > + mc->desc =3D "Bananapi M2U (Cortex-A7)"; > + mc->init =3D bpim2u_init; > + mc->min_cpus =3D AW_R40_NUM_CPUS; > + mc->max_cpus =3D AW_R40_NUM_CPUS; > + mc->default_cpus =3D AW_R40_NUM_CPUS; > + mc->default_cpu_type =3D ARM_CPU_TYPE_NAME("cortex-a7"); > + mc->default_ram_size =3D 1 * GiB; > The board you are adding, is it the Banana Pi BPI-M2 Ultra? If so, this page says the board has 2GiB of RAM: https://wiki.banana-pi.org/Banana_Pi_BPI-M2U > + mc->default_ram_id =3D "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 > + * > + * 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 . > + */ > + > +#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 > > With the above resolved/answered: Reviewed-by: Niek Linnenbank --=20 Niek Linnenbank --000000000000c3a22705f8afb172 Content-Type: text/html; charset="UTF-8" Content-Transfer-Encoding: quoted-printable
Hi Qianfan Zhao,



On Tue, Mar 28, 2023 at 7:47=E2=80=AFAM <qianfanguijin@163.com> wrote:
From: qianfan Zhao <qianfanguijin@163.com= >

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

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

Perhaps this line above i= n the commit message can now be updated/removed?
=C2=A0
=

Signed-off-by: qianfan Zhao <qianfanguijin@163.com>
---
=C2=A0configs/devices/arm-softmmu/default.mak |=C2=A0 =C2=A01 +
=C2=A0hw/arm/Kconfig=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0= =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 |=C2=A0 =C2=A09 +
=C2=A0hw/arm/allwinner-r40.c=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 | 418 ++++++++++++++++++++++++
=C2=A0hw/arm/bananapi_m2u.c=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0= =C2=A0 =C2=A0 =C2=A0| 129 ++++++++
=C2=A0hw/arm/meson.build=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0 =C2=A0 =C2=A0 |=C2=A0 =C2=A01 +
=C2=A0include/hw/arm/allwinner-r40.h=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 | 11= 0 +++++++
=C2=A06 files changed, 668 insertions(+)
=C2=A0create mode 100644 hw/arm/allwinner-r40.c
=C2=A0create mode 100644 hw/arm/bananapi_m2u.c
=C2=A0create mode 100644 include/hw/arm/allwinner-r40.h

diff --git a/configs/devices/arm-softmmu/default.mak b/configs/devices/arm-= softmmu/default.mak
index 1b49a7830c..76a43add23 100644
--- a/configs/devices/arm-softmmu/default.mak
+++ b/configs/devices/arm-softmmu/default.mak
@@ -43,3 +43,4 @@ CONFIG_FSL_IMX6UL=3Dy
=C2=A0CONFIG_SEMIHOSTING=3Dy
=C2=A0CONFIG_ARM_COMPATIBLE_SEMIHOSTING=3Dy
=C2=A0CONFIG_ALLWINNER_H3=3Dy
+CONFIG_ALLWINNER_R40=3Dy
diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig
index b5aed4aff5..9e14c3427e 100644
--- a/hw/arm/Kconfig
+++ b/hw/arm/Kconfig
@@ -344,6 +344,15 @@ config ALLWINNER_H3
=C2=A0 =C2=A0 =C2=A0select USB_EHCI_SYSBUS
=C2=A0 =C2=A0 =C2=A0select SD

+config ALLWINNER_R40
+=C2=A0 =C2=A0 bool
+=C2=A0 =C2=A0 select ALLWINNER_A10_PIT
+=C2=A0 =C2=A0 select SERIAL
+=C2=A0 =C2=A0 select ARM_TIMER
+=C2=A0 =C2=A0 select ARM_GIC
+=C2=A0 =C2=A0 select UNIMP
+=C2=A0 =C2=A0 select SD
+
=C2=A0config RASPI
=C2=A0 =C2=A0 =C2=A0bool
=C2=A0 =C2=A0 =C2=A0select FRAMEBUFFER
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.=C2=A0 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.=C2=A0 If not, see <http://www.gnu.org/li= censes/>.
+ */
+
+#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[] =3D {
+=C2=A0 =C2=A0 [AW_R40_DEV_SRAM_A1]=C2=A0 =C2=A0 =3D 0x00000000,
+=C2=A0 =C2=A0 [AW_R40_DEV_SRAM_A2]=C2=A0 =C2=A0 =3D 0x00004000,
+=C2=A0 =C2=A0 [AW_R40_DEV_SRAM_A3]=C2=A0 =C2=A0 =3D 0x00008000,
+=C2=A0 =C2=A0 [AW_R40_DEV_SRAM_A4]=C2=A0 =C2=A0 =3D 0x0000b400,
+=C2=A0 =C2=A0 [AW_R40_DEV_MMC0]=C2=A0 =C2=A0 =C2=A0 =C2=A0=3D 0x01c0f000,<= br> +=C2=A0 =C2=A0 [AW_R40_DEV_MMC1]=C2=A0 =C2=A0 =C2=A0 =C2=A0=3D 0x01c10000,<= br> +=C2=A0 =C2=A0 [AW_R40_DEV_MMC2]=C2=A0 =C2=A0 =C2=A0 =C2=A0=3D 0x01c11000,<= br> +=C2=A0 =C2=A0 [AW_R40_DEV_MMC3]=C2=A0 =C2=A0 =C2=A0 =C2=A0=3D 0x01c12000,<= br> +=C2=A0 =C2=A0 [AW_R40_DEV_PIT]=C2=A0 =C2=A0 =C2=A0 =C2=A0 =3D 0x01c20c00,<= br> +=C2=A0 =C2=A0 [AW_R40_DEV_UART0]=C2=A0 =C2=A0 =C2=A0 =3D 0x01c28000,
+=C2=A0 =C2=A0 [AW_R40_DEV_GIC_DIST]=C2=A0 =C2=A0=3D 0x01c81000,
+=C2=A0 =C2=A0 [AW_R40_DEV_GIC_CPU]=C2=A0 =C2=A0 =3D 0x01c82000,
+=C2=A0 =C2=A0 [AW_R40_DEV_GIC_HYP]=C2=A0 =C2=A0 =3D 0x01c84000,
+=C2=A0 =C2=A0 [AW_R40_DEV_GIC_VCPU]=C2=A0 =C2=A0=3D 0x01c86000,
+=C2=A0 =C2=A0 [AW_R40_DEV_SDRAM]=C2=A0 =C2=A0 =C2=A0 =3D 0x40000000
+};
+
+/* List of unimplemented devices */
+struct AwR40Unimplemented {
+=C2=A0 =C2=A0 const char *device_name;
+=C2=A0 =C2=A0 hwaddr base;
+=C2=A0 =C2=A0 hwaddr size;
+};
+
+static struct AwR40Unimplemented r40_unimplemented[] =3D {
+=C2=A0 =C2=A0 { "d-engine",=C2=A0 =C2=A00x01000000, 4 * MiB }, +=C2=A0 =C2=A0 { "d-inter",=C2=A0 =C2=A0 0x01400000, 128 * KiB },=
+=C2=A0 =C2=A0 { "sram-c",=C2=A0 =C2=A0 =C2=A00x01c00000, 4 * KiB= },
+=C2=A0 =C2=A0 { "dma",=C2=A0 =C2=A0 =C2=A0 =C2=A0 0x01c02000, 4 = * KiB },
+=C2=A0 =C2=A0 { "nfdc",=C2=A0 =C2=A0 =C2=A0 =C2=A00x01c03000, 4 = * KiB },
+=C2=A0 =C2=A0 { "ts",=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A00x01c0400= 0, 4 * KiB },
+=C2=A0 =C2=A0 { "spi0",=C2=A0 =C2=A0 =C2=A0 =C2=A00x01c05000, 4 = * KiB },
+=C2=A0 =C2=A0 { "spi1",=C2=A0 =C2=A0 =C2=A0 =C2=A00x01c06000, 4 = * KiB },
+=C2=A0 =C2=A0 { "cs0",=C2=A0 =C2=A0 =C2=A0 =C2=A0 0x01c09000, 4 = * KiB },
+=C2=A0 =C2=A0 { "keymem",=C2=A0 =C2=A0 =C2=A00x01c0a000, 4 * KiB= },
+=C2=A0 =C2=A0 { "emac",=C2=A0 =C2=A0 =C2=A0 =C2=A00x01c0b000, 4 = * KiB },
+=C2=A0 =C2=A0 { "usb0-otg",=C2=A0 =C2=A00x01c13000, 4 * KiB }, +=C2=A0 =C2=A0 { "usb0-host",=C2=A0 0x01c14000, 4 * KiB },
+=C2=A0 =C2=A0 { "crypto",=C2=A0 =C2=A0 =C2=A00x01c15000, 4 * KiB= },
+=C2=A0 =C2=A0 { "spi2",=C2=A0 =C2=A0 =C2=A0 =C2=A00x01c17000, 4 = * KiB },
+=C2=A0 =C2=A0 { "sata",=C2=A0 =C2=A0 =C2=A0 =C2=A00x01c18000, 4 = * KiB },
+=C2=A0 =C2=A0 { "usb1-host",=C2=A0 0x01c19000, 4 * KiB },
+=C2=A0 =C2=A0 { "sid",=C2=A0 =C2=A0 =C2=A0 =C2=A0 0x01c1b000, 4 = * KiB },
+=C2=A0 =C2=A0 { "usb2-host",=C2=A0 0x01c1c000, 4 * KiB },
+=C2=A0 =C2=A0 { "cs1",=C2=A0 =C2=A0 =C2=A0 =C2=A0 0x01c1d000, 4 = * KiB },
+=C2=A0 =C2=A0 { "spi3",=C2=A0 =C2=A0 =C2=A0 =C2=A00x01c1f000, 4 = * KiB },
+=C2=A0 =C2=A0 { "ccu",=C2=A0 =C2=A0 =C2=A0 =C2=A0 0x01c20000, 1 = * KiB },
+=C2=A0 =C2=A0 { "rtc",=C2=A0 =C2=A0 =C2=A0 =C2=A0 0x01c20400, 1 = * KiB },
+=C2=A0 =C2=A0 { "pio",=C2=A0 =C2=A0 =C2=A0 =C2=A0 0x01c20800, 1 = * KiB },
+=C2=A0 =C2=A0 { "owa",=C2=A0 =C2=A0 =C2=A0 =C2=A0 0x01c21000, 1 = * KiB },
+=C2=A0 =C2=A0 { "ac97",=C2=A0 =C2=A0 =C2=A0 =C2=A00x01c21400, 1 = * KiB },
+=C2=A0 =C2=A0 { "cir0",=C2=A0 =C2=A0 =C2=A0 =C2=A00x01c21800, 1 = * KiB },
+=C2=A0 =C2=A0 { "cir1",=C2=A0 =C2=A0 =C2=A0 =C2=A00x01c21c00, 1 = * KiB },
+=C2=A0 =C2=A0 { "pcm0",=C2=A0 =C2=A0 =C2=A0 =C2=A00x01c22000, 1 = * KiB },
+=C2=A0 =C2=A0 { "pcm1",=C2=A0 =C2=A0 =C2=A0 =C2=A00x01c22400, 1 = * KiB },
+=C2=A0 =C2=A0 { "pcm2",=C2=A0 =C2=A0 =C2=A0 =C2=A00x01c22800, 1 = * KiB },
+=C2=A0 =C2=A0 { "audio",=C2=A0 =C2=A0 =C2=A0 0x01c22c00, 1 * KiB= },
+=C2=A0 =C2=A0 { "keypad",=C2=A0 =C2=A0 =C2=A00x01c23000, 1 * KiB= },
+=C2=A0 =C2=A0 { "pwm",=C2=A0 =C2=A0 =C2=A0 =C2=A0 0x01c23400, 1 = * KiB },
+=C2=A0 =C2=A0 { "keyadc",=C2=A0 =C2=A0 =C2=A00x01c24400, 1 * KiB= },
+=C2=A0 =C2=A0 { "ths",=C2=A0 =C2=A0 =C2=A0 =C2=A0 0x01c24c00, 1 = * KiB },
+=C2=A0 =C2=A0 { "rtp",=C2=A0 =C2=A0 =C2=A0 =C2=A0 0x01c25000, 1 = * KiB },
+=C2=A0 =C2=A0 { "pmu",=C2=A0 =C2=A0 =C2=A0 =C2=A0 0x01c25400, 1 = * KiB },
+=C2=A0 =C2=A0 { "cpu-cfg",=C2=A0 =C2=A0 0x01c25c00, 1 * KiB }, +=C2=A0 =C2=A0 { "uart0",=C2=A0 =C2=A0 =C2=A0 0x01c28000, 1 * KiB= },
+=C2=A0 =C2=A0 { "uart1",=C2=A0 =C2=A0 =C2=A0 0x01c28400, 1 * KiB= },
+=C2=A0 =C2=A0 { "uart2",=C2=A0 =C2=A0 =C2=A0 0x01c28800, 1 * KiB= },
+=C2=A0 =C2=A0 { "uart3",=C2=A0 =C2=A0 =C2=A0 0x01c28c00, 1 * KiB= },
+=C2=A0 =C2=A0 { "uart4",=C2=A0 =C2=A0 =C2=A0 0x01c29000, 1 * KiB= },
+=C2=A0 =C2=A0 { "uart5",=C2=A0 =C2=A0 =C2=A0 0x01c29400, 1 * KiB= },
+=C2=A0 =C2=A0 { "uart6",=C2=A0 =C2=A0 =C2=A0 0x01c29800, 1 * KiB= },
+=C2=A0 =C2=A0 { "uart7",=C2=A0 =C2=A0 =C2=A0 0x01c29c00, 1 * KiB= },
+=C2=A0 =C2=A0 { "ps20",=C2=A0 =C2=A0 =C2=A0 =C2=A00x01c2a000, 1 = * KiB },
+=C2=A0 =C2=A0 { "ps21",=C2=A0 =C2=A0 =C2=A0 =C2=A00x01c2a400, 1 = * KiB },
+=C2=A0 =C2=A0 { "twi0",=C2=A0 =C2=A0 =C2=A0 =C2=A00x01c2ac00, 1 = * KiB },
+=C2=A0 =C2=A0 { "twi1",=C2=A0 =C2=A0 =C2=A0 =C2=A00x01c2b000, 1 = * KiB },
+=C2=A0 =C2=A0 { "twi2",=C2=A0 =C2=A0 =C2=A0 =C2=A00x01c2b400, 1 = * KiB },
+=C2=A0 =C2=A0 { "twi3",=C2=A0 =C2=A0 =C2=A0 =C2=A00x01c2b800, 1 = * KiB },
+=C2=A0 =C2=A0 { "twi4",=C2=A0 =C2=A0 =C2=A0 =C2=A00x01c2c000, 1 = * KiB },
+=C2=A0 =C2=A0 { "scr",=C2=A0 =C2=A0 =C2=A0 =C2=A0 0x01c2c400, 1 = * KiB },
+=C2=A0 =C2=A0 { "tvd-top",=C2=A0 =C2=A0 0x01c30000, 4 * KiB }, +=C2=A0 =C2=A0 { "tvd0",=C2=A0 =C2=A0 =C2=A0 =C2=A00x01c31000, 4 = * KiB },
+=C2=A0 =C2=A0 { "tvd1",=C2=A0 =C2=A0 =C2=A0 =C2=A00x01c32000, 4 = * KiB },
+=C2=A0 =C2=A0 { "tvd2",=C2=A0 =C2=A0 =C2=A0 =C2=A00x01c33000, 4 = * KiB },
+=C2=A0 =C2=A0 { "tvd3",=C2=A0 =C2=A0 =C2=A0 =C2=A00x01c34000, 4 = * KiB },
+=C2=A0 =C2=A0 { "gpu",=C2=A0 =C2=A0 =C2=A0 =C2=A0 0x01c40000, 64= * KiB },
+=C2=A0 =C2=A0 { "gmac",=C2=A0 =C2=A0 =C2=A0 =C2=A00x01c50000, 64= * KiB },
+=C2=A0 =C2=A0 { "hstmr",=C2=A0 =C2=A0 =C2=A0 0x01c60000, 4 * KiB= },
+=C2=A0 =C2=A0 { "dram-com",=C2=A0 =C2=A00x01c62000, 4 * KiB }, +=C2=A0 =C2=A0 { "dram-ctl",=C2=A0 =C2=A00x01c63000, 4 * KiB }, +=C2=A0 =C2=A0 { "tcon-top",=C2=A0 =C2=A00x01c70000, 4 * KiB }, +=C2=A0 =C2=A0 { "lcd0",=C2=A0 =C2=A0 =C2=A0 =C2=A00x01c71000, 4 = * KiB },
+=C2=A0 =C2=A0 { "lcd1",=C2=A0 =C2=A0 =C2=A0 =C2=A00x01c72000, 4 = * KiB },
+=C2=A0 =C2=A0 { "tv0",=C2=A0 =C2=A0 =C2=A0 =C2=A0 0x01c73000, 4 = * KiB },
+=C2=A0 =C2=A0 { "tv1",=C2=A0 =C2=A0 =C2=A0 =C2=A0 0x01c74000, 4 = * KiB },
+=C2=A0 =C2=A0 { "tve-top",=C2=A0 =C2=A0 0x01c90000, 16 * KiB },<= br> +=C2=A0 =C2=A0 { "tve0",=C2=A0 =C2=A0 =C2=A0 =C2=A00x01c94000, 16= * KiB },
+=C2=A0 =C2=A0 { "tve1",=C2=A0 =C2=A0 =C2=A0 =C2=A00x01c98000, 16= * KiB },
+=C2=A0 =C2=A0 { "mipi_dsi",=C2=A0 =C2=A00x01ca0000, 4 * KiB }, +=C2=A0 =C2=A0 { "mipi_dphy",=C2=A0 0x01ca1000, 4 * KiB },
+=C2=A0 =C2=A0 { "ve",=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A00x01d0000= 0, 1024 * KiB },
+=C2=A0 =C2=A0 { "mp",=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A00x01e8000= 0, 128 * KiB },
+=C2=A0 =C2=A0 { "hdmi",=C2=A0 =C2=A0 =C2=A0 =C2=A00x01ee0000, 12= 8 * KiB },
+=C2=A0 =C2=A0 { "prcm",=C2=A0 =C2=A0 =C2=A0 =C2=A00x01f01400, 1 = * KiB },
+=C2=A0 =C2=A0 { "debug",=C2=A0 =C2=A0 =C2=A0 0x3f500000, 64 * Ki= B },
+=C2=A0 =C2=A0 { "cpubist",=C2=A0 =C2=A0 0x3f501000, 4 * KiB }, +=C2=A0 =C2=A0 { "dcu",=C2=A0 =C2=A0 =C2=A0 =C2=A0 0x3fff0000, 64= * KiB },
+=C2=A0 =C2=A0 { "hstmr",=C2=A0 =C2=A0 =C2=A0 0x01c60000, 4 * KiB= },
+=C2=A0 =C2=A0 { "brom",=C2=A0 =C2=A0 =C2=A0 =C2=A00xffff0000, 36= * KiB }
+};
+
+/* Per Processor Interrupts */
+enum {
+=C2=A0 =C2=A0 AW_R40_GIC_PPI_MAINT=C2=A0 =C2=A0 =C2=A0=3D=C2=A0 9,
+=C2=A0 =C2=A0 AW_R40_GIC_PPI_HYPTIMER=C2=A0 =3D 10,
+=C2=A0 =C2=A0 AW_R40_GIC_PPI_VIRTTIMER =3D 11,
+=C2=A0 =C2=A0 AW_R40_GIC_PPI_SECTIMER=C2=A0 =3D 13,
+=C2=A0 =C2=A0 AW_R40_GIC_PPI_PHYSTIMER =3D 14
+};
+
+/* Shared Processor Interrupts */
+enum {
+=C2=A0 =C2=A0 AW_R40_GIC_SPI_UART0=C2=A0 =C2=A0 =C2=A0=3D=C2=A0 1,
+=C2=A0 =C2=A0 AW_R40_GIC_SPI_UART1=C2=A0 =C2=A0 =C2=A0=3D=C2=A0 2,
+=C2=A0 =C2=A0 AW_R40_GIC_SPI_UART2=C2=A0 =C2=A0 =C2=A0=3D=C2=A0 3,
+=C2=A0 =C2=A0 AW_R40_GIC_SPI_UART3=C2=A0 =C2=A0 =C2=A0=3D=C2=A0 4,
+=C2=A0 =C2=A0 AW_R40_GIC_SPI_TIMER0=C2=A0 =C2=A0 =3D 22,
+=C2=A0 =C2=A0 AW_R40_GIC_SPI_TIMER1=C2=A0 =C2=A0 =3D 23,
+=C2=A0 =C2=A0 AW_R40_GIC_SPI_MMC0=C2=A0 =C2=A0 =C2=A0 =3D 32,
+=C2=A0 =C2=A0 AW_R40_GIC_SPI_MMC1=C2=A0 =C2=A0 =C2=A0 =3D 33,
+=C2=A0 =C2=A0 AW_R40_GIC_SPI_MMC2=C2=A0 =C2=A0 =C2=A0 =3D 34,
+=C2=A0 =C2=A0 AW_R40_GIC_SPI_MMC3=C2=A0 =C2=A0 =C2=A0 =3D 35,
+};
+
+/* Allwinner R40 general constants */
+enum {
+=C2=A0 =C2=A0 AW_R40_GIC_NUM_SPI=C2=A0 =C2=A0 =C2=A0 =C2=A0=3D 128
+};
+
+#define BOOT0_MAGIC=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0"e= GON.BT0"
+
+/* The low 8-bits of the 'boot_media' field in the SPL header */ +#define SUNXI_BOOTED_FROM_MMC0=C2=A0 0
+#define SUNXI_BOOTED_FROM_NAND=C2=A0 1
+#define SUNXI_BOOTED_FROM_MMC2=C2=A0 2
+#define SUNXI_BOOTED_FROM_SPI=C2=A0 =C2=A03
+
+struct boot_file_head {
+=C2=A0 =C2=A0 uint32_t=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 b_instruct= ion;
+=C2=A0 =C2=A0 uint8_t=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0magic= [8];
+=C2=A0 =C2=A0 uint32_t=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 check_sum;=
+=C2=A0 =C2=A0 uint32_t=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 length; +=C2=A0 =C2=A0 uint32_t=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 pub_head_s= ize;
+=C2=A0 =C2=A0 uint32_t=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 fel_script= _address;
+=C2=A0 =C2=A0 uint32_t=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 fel_uEnv_l= ength;
+=C2=A0 =C2=A0 uint32_t=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 dt_name_of= fset;
+=C2=A0 =C2=A0 uint32_t=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 dram_size;=
+=C2=A0 =C2=A0 uint32_t=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 boot_media= ;
+=C2=A0 =C2=A0 uint32_t=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 string_poo= l[13];
+};
+
+bool allwinner_r40_bootrom_setup(AwR40State *s, BlockBackend *blk, int uni= t)
+{
+=C2=A0 =C2=A0 const int64_t rom_size =3D 32 * KiB;
+=C2=A0 =C2=A0 g_autofree uint8_t *buffer =3D g_new0(uint8_t, rom_size); +=C2=A0 =C2=A0 struct boot_file_head *head =3D (struct boot_file_head *)buf= fer;
+
+=C2=A0 =C2=A0 if (blk_pread(blk, 8 * KiB, rom_size, buffer, 0) < 0) { +=C2=A0 =C2=A0 =C2=A0 =C2=A0 error_setg(&error_fatal, "%s: failed = to read BlockBackend data",
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0__fun= c__);
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 return false;
+=C2=A0 =C2=A0 }
+
+=C2=A0 =C2=A0 /* we only check the magic string here. */
+=C2=A0 =C2=A0 if (memcmp(head->magic, BOOT0_MAGIC, sizeof(head->magi= c))) {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 return false;
+=C2=A0 =C2=A0 }
+
+=C2=A0 =C2=A0 /*
+=C2=A0 =C2=A0 =C2=A0* Simulate the behavior of the bootROM, it will change= the boot_media
+=C2=A0 =C2=A0 =C2=A0* flag to indicate where the chip is booting from. R40= can boot from
+=C2=A0 =C2=A0 =C2=A0* mmc0 or mmc2, the default value of boot_media is zer= o
+=C2=A0 =C2=A0 =C2=A0* (SUNXI_BOOTED_FROM_MMC0), let's fix this flag wh= en it is booting from
+=C2=A0 =C2=A0 =C2=A0* the others.
+=C2=A0 =C2=A0 =C2=A0*/
+=C2=A0 =C2=A0 if (unit =3D=3D 2) {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 head->boot_media =3D cpu_to_le32(SUNXI_BOOT= ED_FROM_MMC2);
+=C2=A0 =C2=A0 } else {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 head->boot_media =3D cpu_to_le32(SUNXI_BOOT= ED_FROM_MMC0);
+=C2=A0 =C2=A0 }
+
+=C2=A0 =C2=A0 rom_add_blob("allwinner-r40.bootrom", buffer, rom_= size,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 rom_size, s= ->memmap[AW_R40_DEV_SRAM_A1],
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 NULL, NULL,= NULL, NULL, false);
+=C2=A0 =C2=A0 return true;
+}
+
+static void allwinner_r40_init(Object *obj)
+{
+=C2=A0 =C2=A0 static const char *mmc_names[AW_R40_NUM_MMCS] =3D {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 "mmc0", "mmc1", "mmc2= ", "mmc3"
+=C2=A0 =C2=A0 };
+=C2=A0 =C2=A0 AwR40State *s =3D AW_R40(obj);
+
+=C2=A0 =C2=A0 s->memmap =3D allwinner_r40_memmap;
+
+=C2=A0 =C2=A0 for (int i =3D 0; i < AW_R40_NUM_CPUS; i++) {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 object_initialize_child(obj, "cpu[*]"= ;, &s->cpus[i],
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 ARM_CPU_TYPE_NAME("cortex-a7&qu= ot;));
+=C2=A0 =C2=A0 }
+
+=C2=A0 =C2=A0 object_initialize_child(obj, "gic", &s->gic= , TYPE_ARM_GIC);
+
+=C2=A0 =C2=A0 object_initialize_child(obj, "timer", &s->t= imer, TYPE_AW_A10_PIT);
+=C2=A0 =C2=A0 object_property_add_alias(obj, "clk0-freq", OBJECT= (&s->timer),
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 "clk0-freq");
+=C2=A0 =C2=A0 object_property_add_alias(obj, "clk1-freq", OBJECT= (&s->timer),
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 "clk1-freq");
+
+=C2=A0 =C2=A0 for (int i =3D 0; i < AW_R40_NUM_MMCS; i++) {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 object_initialize_child(obj, mmc_names[i], &am= p;s->mmc[i],
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 TYPE_AW_SDHOST_SUN5I);
+=C2=A0 =C2=A0 }
+}
+
+static void allwinner_r40_realize(DeviceState *dev, Error **errp)
+{
+=C2=A0 =C2=A0 AwR40State *s =3D AW_R40(dev);
+=C2=A0 =C2=A0 unsigned i;
+
+=C2=A0 =C2=A0 /* CPUs */
+=C2=A0 =C2=A0 for (i =3D 0; i < AW_R40_NUM_CPUS; i++) {
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 /*
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0* Disable secondary CPUs. Guest EL3 firm= ware will start
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0* them via CPU reset control registers.<= br> +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0*/
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 qdev_prop_set_bit(DEVICE(&s->cpus[i]), = "start-powered-off",
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 i > 0);
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 /* All exception levels required */
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 qdev_prop_set_bit(DEVICE(&s->cpus[i]), = "has_el3", true);
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 qdev_prop_set_bit(DEVICE(&s->cpus[i]), = "has_el2", true);
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 /* Mark realized */
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 qdev_realize(DEVICE(&s->cpus[i]), NULL,= &error_fatal);
+=C2=A0 =C2=A0 }
+
+=C2=A0 =C2=A0 /* Generic Interrupt Controller */
+=C2=A0 =C2=A0 qdev_prop_set_uint32(DEVICE(&s->gic), "num-irq&q= uot;, AW_R40_GIC_NUM_SPI +
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0GIC_INTERNAL);
+=C2=A0 =C2=A0 qdev_prop_set_uint32(DEVICE(&s->gic), "revision&= quot;, 2);
+=C2=A0 =C2=A0 qdev_prop_set_uint32(DEVICE(&s->gic), "num-cpu&q= uot;, AW_R40_NUM_CPUS);
+=C2=A0 =C2=A0 qdev_prop_set_bit(DEVICE(&s->gic), "has-security= -extensions", false);
+=C2=A0 =C2=A0 qdev_prop_set_bit(DEVICE(&s->gic), "has-virtuali= zation-extensions", true);
+=C2=A0 =C2=A0 sysbus_realize(SYS_BUS_DEVICE(&s->gic), &error_fa= tal);
+
+=C2=A0 =C2=A0 sysbus_mmio_map(SYS_BUS_DEVICE(&s->gic), 0, s->mem= map[AW_R40_DEV_GIC_DIST]);
+=C2=A0 =C2=A0 sysbus_mmio_map(SYS_BUS_DEVICE(&s->gic), 1, s->mem= map[AW_R40_DEV_GIC_CPU]);
+=C2=A0 =C2=A0 sysbus_mmio_map(SYS_BUS_DEVICE(&s->gic), 2, s->mem= map[AW_R40_DEV_GIC_HYP]);
+=C2=A0 =C2=A0 sysbus_mmio_map(SYS_BUS_DEVICE(&s->gic), 3, s->mem= map[AW_R40_DEV_GIC_VCPU]);
+
+=C2=A0 =C2=A0 /*
+=C2=A0 =C2=A0 =C2=A0* Wire the outputs from each CPU's generic timer a= nd the GICv2
+=C2=A0 =C2=A0 =C2=A0* maintenance interrupt signal to the appropriate GIC = PPI inputs,
+=C2=A0 =C2=A0 =C2=A0* and the GIC's IRQ/FIQ/VIRQ/VFIQ interrupt output= s to the CPU's inputs.
+=C2=A0 =C2=A0 =C2=A0*/
+=C2=A0 =C2=A0 for (i =3D 0; i < AW_R40_NUM_CPUS; i++) {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 DeviceState *cpudev =3D DEVICE(&s->cpus= [i]);
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 int ppibase =3D AW_R40_GIC_NUM_SPI + i * GIC_I= NTERNAL + GIC_NR_SGIS;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 int irq;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 /*
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0* Mapping from the output timer irq line= s from the CPU to the
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0* GIC PPI inputs used for this board. +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0*/
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 const int timer_irq[] =3D {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 [GTIMER_PHYS] =3D AW_R40_GIC_PPI= _PHYSTIMER,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 [GTIMER_VIRT] =3D AW_R40_GIC_PPI= _VIRTTIMER,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 [GTIMER_HYP]=C2=A0 =3D AW_R40_GI= C_PPI_HYPTIMER,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 [GTIMER_SEC]=C2=A0 =3D AW_R40_GI= C_PPI_SECTIMER,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 };
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 /* Connect CPU timer outputs to GIC PPI inputs= */
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 for (irq =3D 0; irq < ARRAY_SIZE(timer_irq)= ; irq++) {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 qdev_connect_gpio_out(cpudev, ir= q,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 qdev_get_gpio_in(DEVICE(&= s->gic),
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0ppibase + timer_irq[irq]));
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 }
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 /* Connect GIC outputs to CPU interrupt inputs= */
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 sysbus_connect_irq(SYS_BUS_DEVICE(&s->g= ic), i,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0qdev_get_gpio_in(cpudev, ARM_CPU_IRQ));
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 sysbus_connect_irq(SYS_BUS_DEVICE(&s->g= ic), i + AW_R40_NUM_CPUS,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0qdev_get_gpio_in(cpudev, ARM_CPU_FIQ));
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 sysbus_connect_irq(SYS_BUS_DEVICE(&s->g= ic), i + (2 * AW_R40_NUM_CPUS),
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0qdev_get_gpio_in(cpudev, ARM_CPU_VIRQ));
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 sysbus_connect_irq(SYS_BUS_DEVICE(&s->g= ic), i + (3 * AW_R40_NUM_CPUS),
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0qdev_get_gpio_in(cpudev, ARM_CPU_VFIQ));
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 /* GIC maintenance signal */
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 sysbus_connect_irq(SYS_BUS_DEVICE(&s->g= ic), i + (4 * AW_R40_NUM_CPUS),
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0qdev_get_gpio_in(DEVICE(&s->gic),
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 ppibase + AW_R40_GIC_PPI_MAINT));
+=C2=A0 =C2=A0 }
+
+=C2=A0 =C2=A0 /* Timer */
+=C2=A0 =C2=A0 sysbus_realize(SYS_BUS_DEVICE(&s->timer), &error_= fatal);
+=C2=A0 =C2=A0 sysbus_mmio_map(SYS_BUS_DEVICE(&s->timer), 0, s->m= emmap[AW_R40_DEV_PIT]);
+=C2=A0 =C2=A0 sysbus_connect_irq(SYS_BUS_DEVICE(&s->timer), 0,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0qdev_get_gpio_in(DEVICE(&s->gic),
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0AW_R40_GIC_SPI_TIMER0));
+=C2=A0 =C2=A0 sysbus_connect_irq(SYS_BUS_DEVICE(&s->timer), 1,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0qdev_get_gpio_in(DEVICE(&s->gic),
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0AW_R40_GIC_SPI_TIMER1));
+
+=C2=A0 =C2=A0 /* SRAM */
+=C2=A0 =C2=A0 memory_region_init_ram(&s->sram_a1, OBJECT(dev), &quo= t;sram A1",
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 16 * KiB, &error_abort);
+=C2=A0 =C2=A0 memory_region_init_ram(&s->sram_a2, OBJECT(dev), &quo= t;sram A2",
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 16 * KiB, &error_abort);
+=C2=A0 =C2=A0 memory_region_init_ram(&s->sram_a3, OBJECT(dev), &quo= t;sram A3",
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 13 * KiB, &error_abort);
+=C2=A0 =C2=A0 memory_region_init_ram(&s->sram_a4, OBJECT(dev), &quo= t;sram A4",
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 3 * KiB, &error_abort);
+=C2=A0 =C2=A0 memory_region_add_subregion(get_system_memory(),
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 s->memmap[AW_R40_DEV_SRAM_A1], &a= mp;s->sram_a1);
+=C2=A0 =C2=A0 memory_region_add_subregion(get_system_memory(),
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 s->memmap[AW_R40_DEV_SRAM_A2], &a= mp;s->sram_a2);
+=C2=A0 =C2=A0 memory_region_add_subregion(get_system_memory(),
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 s->memmap[AW_R40_DEV_SRAM_A3], &a= mp;s->sram_a3);
+=C2=A0 =C2=A0 memory_region_add_subregion(get_system_memory(),
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 s->memmap[AW_R40_DEV_SRAM_A4], &a= mp;s->sram_a4);
+
+=C2=A0 =C2=A0 /* SD/MMC */
+=C2=A0 =C2=A0 for (int i =3D 0; i < AW_R40_NUM_MMCS; i++) {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 qemu_irq irq =3D qdev_get_gpio_in(DEVICE(&= s->gic),
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 AW_R40_G= IC_SPI_MMC0 + i);
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 const hwaddr addr =3D s->memmap[AW_R40_DEV_= MMC0 + i];
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 object_property_set_link(OBJECT(&s->mmc= [i]), "dma-memory",
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0OBJECT(get_system_memory()), &= amp;error_fatal);
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 sysbus_realize(SYS_BUS_DEVICE(&s->mmc[i= ]), &error_fatal);
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 sysbus_mmio_map(SYS_BUS_DEVICE(&s->mmc[= i]), 0, addr);
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 sysbus_connect_irq(SYS_BUS_DEVICE(&s->m= mc[i]), 0, irq);
+=C2=A0 =C2=A0 }
+
+=C2=A0 =C2=A0 /* UART0. For future clocktree API: All UARTS are connected = to APB2_CLK. */
+=C2=A0 =C2=A0 serial_mm_init(get_system_memory(), s->memmap[AW_R40_DEV_= UART0], 2,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0qdev_= get_gpio_in(DEVICE(&s->gic), AW_R40_GIC_SPI_UART0),
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A011520= 0, serial_hd(0), DEVICE_NATIVE_ENDIAN);
+
+=C2=A0 =C2=A0 /* Unimplemented devices */
+=C2=A0 =C2=A0 for (i =3D 0; i < ARRAY_SIZE(r40_unimplemented); i++) { +=C2=A0 =C2=A0 =C2=A0 =C2=A0 create_unimplemented_device(r40_unimplemented[= i].device_name,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 r40_unimplemented[i].b= ase,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 r40_unimplemented[i].s= ize);
+=C2=A0 =C2=A0 }
+}
+
+static void allwinner_r40_class_init(ObjectClass *oc, void *data)
+{
+=C2=A0 =C2=A0 DeviceClass *dc =3D DEVICE_CLASS(oc);
+
+=C2=A0 =C2=A0 dc->realize =3D allwinner_r40_realize;
+=C2=A0 =C2=A0 /* Reason: uses serial_hd() in realize function */
+=C2=A0 =C2=A0 dc->user_creatable =3D false;
+}
+
+static const TypeInfo allwinner_r40_type_info =3D {
+=C2=A0 =C2=A0 .name =3D TYPE_AW_R40,
+=C2=A0 =C2=A0 .parent =3D TYPE_DEVICE,
+=C2=A0 =C2=A0 .instance_size =3D sizeof(AwR40State),
+=C2=A0 =C2=A0 .instance_init =3D allwinner_r40_init,
+=C2=A0 =C2=A0 .class_init =3D allwinner_r40_class_init,
+};
+
+static void allwinner_r40_register_types(void)
+{
+=C2=A0 =C2=A0 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.=C2=A0 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.=C2=A0 If not, see <http://www.gnu.org/li= censes/>.
+ */
+
+#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,<= br> +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0bool load_bootroom, bool *bootroom_loaded) +{
+=C2=A0 =C2=A0 DriveInfo *di =3D drive_get(IF_SD, 0, unit);
+=C2=A0 =C2=A0 BlockBackend *blk =3D di ? blk_by_legacy_dinfo(di) : NULL; +=C2=A0 =C2=A0 BusState *bus;
+=C2=A0 =C2=A0 DeviceState *carddev;
+
+=C2=A0 =C2=A0 bus =3D qdev_get_child_bus(DEVICE(mmc), "sd-bus");=
+=C2=A0 =C2=A0 if (bus =3D=3D NULL) {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 error_report("No SD bus found in SOC obje= ct");
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 exit(1);
+=C2=A0 =C2=A0 }
+
+=C2=A0 =C2=A0 carddev =3D qdev_new(TYPE_SD_CARD);
+=C2=A0 =C2=A0 qdev_prop_set_drive_err(carddev, "drive", blk, &am= p;error_fatal);
+=C2=A0 =C2=A0 qdev_realize_and_unref(carddev, bus, &error_fatal);
+
+=C2=A0 =C2=A0 if (load_bootroom && blk && blk_is_available= (blk)) {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 /* Use Boot ROM to copy data from SD card to S= RAM */
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 *bootroom_loaded =3D allwinner_r40_bootrom_set= up(s, blk, unit);
+=C2=A0 =C2=A0 }
+}
+
+static void bpim2u_init(MachineState *machine)
+{
+=C2=A0 =C2=A0 bool bootroom_loaded =3D false;
+=C2=A0 =C2=A0 AwR40State *r40;
+
+=C2=A0 =C2=A0 /* BIOS is not supported by this board */
+=C2=A0 =C2=A0 if (machine->firmware) {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 error_report("BIOS not supported for this= machine");
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 exit(1);
+=C2=A0 =C2=A0 }
+
+=C2=A0 =C2=A0 /* Only allow Cortex-A7 for this board */
+=C2=A0 =C2=A0 if (strcmp(machine->cpu_type, ARM_CPU_TYPE_NAME("cor= tex-a7")) !=3D 0) {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 error_report("This board can only be used= with cortex-a7 CPU");
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 exit(1);
+=C2=A0 =C2=A0 }
+
+=C2=A0 =C2=A0 r40 =3D AW_R40(object_new(TYPE_AW_R40));
+=C2=A0 =C2=A0 object_property_add_child(OBJECT(machine), "soc", = OBJECT(r40));
+=C2=A0 =C2=A0 object_unref(OBJECT(r40));
+
+=C2=A0 =C2=A0 /* Setup timer properties */
+=C2=A0 =C2=A0 object_property_set_int(OBJECT(r40), "clk0-freq", = 32768, &error_abort);
+=C2=A0 =C2=A0 object_property_set_int(OBJECT(r40), "clk1-freq", = 24 * 1000 * 1000,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 &error_abort);
+
+=C2=A0 =C2=A0 /* Mark R40 object realized */
+=C2=A0 =C2=A0 qdev_realize(DEVICE(r40), NULL, &error_abort);
+
+=C2=A0 =C2=A0 /*
+=C2=A0 =C2=A0 =C2=A0* Plug in SD card and try load bootrom, R40 has 4 mmc = controllers but can
+=C2=A0 =C2=A0 =C2=A0* only booting from mmc0 and mmc2.
+=C2=A0 =C2=A0 =C2=A0*/
+=C2=A0 =C2=A0 for (int i =3D 0; i < AW_R40_NUM_MMCS; i++) {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 switch (i) {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 case 0:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 case 2:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 mmc_attach_drive(r40, &r40-&= gt;mmc[i], i,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0!machine->kernel_filename && !boo= troom_loaded,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0&bootroom_loaded);
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 break;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 default:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 mmc_attach_drive(r40, &r40-&= gt;mmc[i], i, false, NULL);
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 break;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 }
+=C2=A0 =C2=A0 }
+
+=C2=A0 =C2=A0 /* SDRAM */
+=C2=A0 =C2=A0 memory_region_add_subregion(get_system_memory(),
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 r40->memmap[AW_R40_DEV_SDRAM], ma= chine->ram);
+
+=C2=A0 =C2=A0 bpim2u_binfo.loader_start =3D r40->memmap[AW_R40_DEV_SDRA= M];
+=C2=A0 =C2=A0 bpim2u_binfo.ram_size =3D machine->ram_size;
+=C2=A0 =C2=A0 bpim2u_binfo.psci_conduit =3D QEMU_PSCI_CONDUIT_SMC;
+=C2=A0 =C2=A0 arm_load_kernel(ARM_CPU(first_cpu), machine, &bpim2u_bin= fo);
+}
+
+static void bpim2u_machine_init(MachineClass *mc)
+{
+=C2=A0 =C2=A0 mc->desc =3D "Bananapi M2U (Cortex-A7)";
+=C2=A0 =C2=A0 mc->init =3D bpim2u_init;
+=C2=A0 =C2=A0 mc->min_cpus =3D AW_R40_NUM_CPUS;
+=C2=A0 =C2=A0 mc->max_cpus =3D AW_R40_NUM_CPUS;
+=C2=A0 =C2=A0 mc->default_cpus =3D AW_R40_NUM_CPUS;
+=C2=A0 =C2=A0 mc->default_cpu_type =3D ARM_CPU_TYPE_NAME("cortex-a= 7");
+=C2=A0 =C2=A0 mc->default_ram_size =3D 1 * GiB;

The board you are adding, is it the Banana Pi BPI-M2 Ultr= a?
If so, this page says the board has 2GiB of RAM:
=C2=A0
+=C2=A0 =C2=A0 mc->default_ram_id =3D "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(&#= 39;omap1.c', 'omap2.c'))
=C2=A0arm_ss.add(when: 'CONFIG_STRONGARM', if_true: files('stro= ngarm.c'))
=C2=A0arm_ss.add(when: 'CONFIG_ALLWINNER_A10', if_true: files('= allwinner-a10.c', 'cubieboard.c'))
=C2=A0arm_ss.add(when: 'CONFIG_ALLWINNER_H3', if_true: files('a= llwinner-h3.c', 'orangepi.c'))
+arm_ss.add(when: 'CONFIG_ALLWINNER_R40', if_true: files('allwi= nner-r40.c', 'bananapi_m2u.c'))
=C2=A0arm_ss.add(when: 'CONFIG_RASPI', if_true: files('bcm2836.= c', 'raspi.c'))
=C2=A0arm_ss.add(when: 'CONFIG_STM32F100_SOC', if_true: files('= stm32f100_soc.c'))
=C2=A0arm_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.=C2=A0 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.=C2=A0 If not, see <http://www.gnu.org/li= censes/>.
+ */
+
+#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 {
+=C2=A0 =C2=A0 AW_R40_DEV_SRAM_A1,
+=C2=A0 =C2=A0 AW_R40_DEV_SRAM_A2,
+=C2=A0 =C2=A0 AW_R40_DEV_SRAM_A3,
+=C2=A0 =C2=A0 AW_R40_DEV_SRAM_A4,
+=C2=A0 =C2=A0 AW_R40_DEV_MMC0,
+=C2=A0 =C2=A0 AW_R40_DEV_MMC1,
+=C2=A0 =C2=A0 AW_R40_DEV_MMC2,
+=C2=A0 =C2=A0 AW_R40_DEV_MMC3,
+=C2=A0 =C2=A0 AW_R40_DEV_CCU,
+=C2=A0 =C2=A0 AW_R40_DEV_PIT,
+=C2=A0 =C2=A0 AW_R40_DEV_UART0,
+=C2=A0 =C2=A0 AW_R40_DEV_GIC_DIST,
+=C2=A0 =C2=A0 AW_R40_DEV_GIC_CPU,
+=C2=A0 =C2=A0 AW_R40_DEV_GIC_HYP,
+=C2=A0 =C2=A0 AW_R40_DEV_GIC_VCPU,
+=C2=A0 =C2=A0 AW_R40_DEV_SDRAM
+};
+
+#define AW_R40_NUM_CPUS=C2=A0 =C2=A0 =C2=A0 (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=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A04
+
+struct AwR40State {
+=C2=A0 =C2=A0 /*< private >*/
+=C2=A0 =C2=A0 DeviceState parent_obj;
+=C2=A0 =C2=A0 /*< public >*/
+
+=C2=A0 =C2=A0 ARMCPU cpus[AW_R40_NUM_CPUS];
+=C2=A0 =C2=A0 const hwaddr *memmap;
+=C2=A0 =C2=A0 AwA10PITState timer;
+=C2=A0 =C2=A0 AwSdHostState mmc[AW_R40_NUM_MMCS];
+=C2=A0 =C2=A0 GICState gic;
+=C2=A0 =C2=A0 MemoryRegion sram_a1;
+=C2=A0 =C2=A0 MemoryRegion sram_a2;
+=C2=A0 =C2=A0 MemoryRegion sram_a3;
+=C2=A0 =C2=A0 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 uni= t);
+
+#endif /* HW_ARM_ALLWINNER_R40_H */
--
2.25.1


With the above resolved/answered:

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

--000000000000c3a22705f8afb172--