From patchwork Tue May 23 10:04:58 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: qianfan X-Patchwork-Id: 1785033 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@legolas.ozlabs.org Authentication-Results: legolas.ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=nongnu.org (client-ip=209.51.188.17; helo=lists.gnu.org; envelope-from=qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org; receiver=) Authentication-Results: legolas.ozlabs.org; dkim=pass (1024-bit key; unprotected) header.d=163.com header.i=@163.com header.a=rsa-sha256 header.s=s110527 header.b=RPQMX/d7; dkim-atps=neutral Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1.2 with cipher ECDHE-ECDSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by legolas.ozlabs.org (Postfix) with ESMTPS id 4QQVPh55BVz20Pb for ; Tue, 23 May 2023 20:08:08 +1000 (AEST) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1q1Ota-0004qw-Hr; Tue, 23 May 2023 06:05:34 -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 1q1OtY-0004lk-Ck; Tue, 23 May 2023 06:05:32 -0400 Received: from m12.mail.163.com ([220.181.12.216]) by eggs.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1q1OtS-0006uI-W3; Tue, 23 May 2023 06:05:32 -0400 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=163.com; s=s110527; h=From:Subject:Date:Message-Id:MIME-Version; bh=yYal0 fkkLh8G8ISz5sakliMwOIckwWwrBygm2j+tuH4=; b=RPQMX/d7CcDm44LVxODex VI6HrcMZX6DlMn15L3y/fELxScpDHiVBvXiFSPkTIoE1CZRhvwPlKCWNPUU6lpx2 lj6sUqH/5SwiBmlwztTyKz+D9zn8JMQp83rR8DdvSiUaGOTJ9iKuJ9QKVcxx+Azk SxGpQATtSmRYmgflSP5VQ8= Received: from DESKTOP-B1R4FVG.localdomain (unknown [218.201.129.19]) by zwqz-smtp-mta-g0-3 (Coremail) with SMTP id _____wAnDC3Wj2xkMfafAQ--.65319S3; Tue, 23 May 2023 18:05:13 +0800 (CST) From: qianfanguijin@163.com To: qemu-arm@nongnu.org, qemu-devel@nongnu.org Cc: Strahinja Jankovic , Peter Maydell , Beniamino Galvani , =?utf-8?q?Philippe_Mathieu-Daud?= =?utf-8?q?=C3=A9?= , Niek Linnenbank , qianfan Zhao Subject: [PATCH v5 01/11] hw: arm: Add bananapi M2-Ultra and allwinner-r40 support Date: Tue, 23 May 2023 18:04:58 +0800 Message-Id: <20230523100508.32564-2-qianfanguijin@163.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20230523100508.32564-1-qianfanguijin@163.com> References: <20230523100508.32564-1-qianfanguijin@163.com> MIME-Version: 1.0 X-CM-TRANSID: _____wAnDC3Wj2xkMfafAQ--.65319S3 X-Coremail-Antispam: 1Uf129KBjvAXoWfurWxJw4DXr18urWUXFWDArb_yoW8Kw4Uuo WfZFsYq3ykK39rWFyrKw409rWUuw1kWr47Aw47uFWxu3WUJw47KryUC3s7Xr43Xr40kF97 X34Fvryfu3y7twn5n29KB7ZKAUJUUUUU529EdanIXcx71UUUUU7v73VFW2AGmfu7bjvjm3 AaLaJ3UbIYCTnIWIevJa73UjIFyTuYvj4RvksyUUUUU X-Originating-IP: [218.201.129.19] X-CM-SenderInfo: htld0w5dqj3xxmlqqiywtou0bp/1tbiXBt47VXl6riLGQAAsi Received-SPF: pass client-ip=220.181.12.216; envelope-from=qianfanguijin@163.com; helo=m12.mail.163.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, RCVD_IN_DNSWL_NONE=-0.0001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001, T_SCC_BODY_TEXT_LINE=-0.01 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+incoming=patchwork.ozlabs.org@nongnu.org Sender: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org 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). Signed-off-by: qianfan Zhao Reviewed-by: Niek Linnenbank --- hw/arm/Kconfig | 10 + hw/arm/allwinner-r40.c | 415 +++++++++++++++++++++++++++++++++ hw/arm/bananapi_m2u.c | 129 ++++++++++ hw/arm/meson.build | 1 + include/hw/arm/allwinner-r40.h | 110 +++++++++ 5 files changed, 665 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 0f42c556d7..0afb87ec1a 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..97f2aa92fd --- /dev/null +++ b/hw/arm/allwinner-r40.c @@ -0,0 +1,415 @@ +/* + * 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[] = { + [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_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 + * + * 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, 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 + * + * 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 */ From patchwork Tue May 23 10:04:59 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: qianfan X-Patchwork-Id: 1785026 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@legolas.ozlabs.org Authentication-Results: legolas.ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=nongnu.org (client-ip=209.51.188.17; helo=lists.gnu.org; envelope-from=qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org; receiver=) Authentication-Results: legolas.ozlabs.org; dkim=pass (1024-bit key; unprotected) header.d=163.com header.i=@163.com header.a=rsa-sha256 header.s=s110527 header.b=U9nYJbuL; dkim-atps=neutral Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1.2 with cipher ECDHE-ECDSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by legolas.ozlabs.org (Postfix) with ESMTPS id 4QQVN75zCmz20Q6 for ; Tue, 23 May 2023 20:06:47 +1000 (AEST) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1q1Otu-0004yl-Cm; Tue, 23 May 2023 06:05:54 -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 1q1Otq-0004vH-M9; Tue, 23 May 2023 06:05:50 -0400 Received: from m12.mail.163.com ([220.181.12.215]) by eggs.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1q1Otl-00077H-QJ; Tue, 23 May 2023 06:05:50 -0400 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=163.com; s=s110527; h=From:Subject:Date:Message-Id:MIME-Version; bh=PBKIb MyUYpHRbQoVT1GY7VDbWnJBs+0fqSRjR7QVXjI=; b=U9nYJbuLlR/xL9Du3Dp3h wFY4+KoHoAxnusf7gWlc+cR1p+ZtV8Q2y75YRtv5NVFkKuS2r5gEgwNEFQHTD6vB tioMPMwBLf9abXbRA2TJ2q8kmJyNgUV/MPVBMdzku83keVzBFejSY/3CJnaEmZIq qAA/fju9xwI43JWCPbMC1Q= Received: from DESKTOP-B1R4FVG.localdomain (unknown [218.201.129.19]) by zwqz-smtp-mta-g0-3 (Coremail) with SMTP id _____wAnDC3Wj2xkMfafAQ--.65319S4; Tue, 23 May 2023 18:05:16 +0800 (CST) From: qianfanguijin@163.com To: qemu-arm@nongnu.org, qemu-devel@nongnu.org Cc: Strahinja Jankovic , Peter Maydell , Beniamino Galvani , =?utf-8?q?Philippe_Mathieu-Daud?= =?utf-8?q?=C3=A9?= , Niek Linnenbank , qianfan Zhao Subject: [PATCH v5 02/11] hw/arm/allwinner-r40: add Clock Control Unit Date: Tue, 23 May 2023 18:04:59 +0800 Message-Id: <20230523100508.32564-3-qianfanguijin@163.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20230523100508.32564-1-qianfanguijin@163.com> References: <20230523100508.32564-1-qianfanguijin@163.com> MIME-Version: 1.0 X-CM-TRANSID: _____wAnDC3Wj2xkMfafAQ--.65319S4 X-Coremail-Antispam: 1Uf129KBjvAXoW3Zw43AFyfXr13GF1xKF4rXwb_yoW8JF17Ko WSqF4rXw4Ik3s3Jr1xC342yr17WFnY93yUJw4ayFWfC3WUJr4DK395tw1fXr4Sgw4FkF4k Jay3ZryfZFW8X3Z5n29KB7ZKAUJUUUUU529EdanIXcx71UUUUU7v73VFW2AGmfu7bjvjm3 AaLaJ3UbIYCTnIWIevJa73UjIFyTuYvj4RBa0mUUUUU X-Originating-IP: [218.201.129.19] X-CM-SenderInfo: htld0w5dqj3xxmlqqiywtou0bp/1tbiGh147VaEFUv5TgAAsp Received-SPF: pass client-ip=220.181.12.215; envelope-from=qianfanguijin@163.com; helo=m12.mail.163.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, RCVD_IN_DNSWL_NONE=-0.0001, RCVD_IN_MSPIKE_H2=-0.001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001, T_SCC_BODY_TEXT_LINE=-0.01 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+incoming=patchwork.ozlabs.org@nongnu.org Sender: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org From: qianfan Zhao 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 Reviewed-by: Niek Linnenbank --- 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 97f2aa92fd..72973f69ff 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 }, @@ -250,6 +250,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); @@ -364,6 +366,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 + * + * 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 "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 + * + * 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_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 */ From patchwork Tue May 23 10:05:00 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: qianfan X-Patchwork-Id: 1785028 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@legolas.ozlabs.org Authentication-Results: legolas.ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=nongnu.org (client-ip=209.51.188.17; helo=lists.gnu.org; envelope-from=qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org; receiver=) Authentication-Results: legolas.ozlabs.org; dkim=pass (1024-bit key; unprotected) header.d=163.com header.i=@163.com header.a=rsa-sha256 header.s=s110527 header.b=eRZnVcoR; dkim-atps=neutral Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1.2 with cipher ECDHE-ECDSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by legolas.ozlabs.org (Postfix) with ESMTPS id 4QQVNT3YLBz20Q6 for ; Tue, 23 May 2023 20:07:05 +1000 (AEST) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1q1Otz-00057K-K3; Tue, 23 May 2023 06:05:59 -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 1q1Ott-0004yE-5n; Tue, 23 May 2023 06:05:54 -0400 Received: from m12.mail.163.com ([220.181.12.197]) by eggs.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1q1Otm-00077b-UI; Tue, 23 May 2023 06:05:52 -0400 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=163.com; s=s110527; h=From:Subject:Date:Message-Id:MIME-Version; bh=uFah4 9/+5d/OhQwBHrTnC8+njyaDfvJEjZqRihCQQn8=; b=eRZnVcoRh8NYPyEVYD3rM RzY95TzBCYKuU44S+aF1Deyf0cJ6bC+hTIKI/NAESTJvS++D2fK1WF5ul+f48EQZ xmwFTmI3svkpzF9zUmqRa6/zzaJyugqtOfnRI3VFZr0QS3sAOxroNBiFOaE8j1jv 2Hvs+HON55EkMhZ61Td2R4= Received: from DESKTOP-B1R4FVG.localdomain (unknown [218.201.129.19]) by zwqz-smtp-mta-g0-3 (Coremail) with SMTP id _____wAnDC3Wj2xkMfafAQ--.65319S5; Tue, 23 May 2023 18:05:18 +0800 (CST) From: qianfanguijin@163.com To: qemu-arm@nongnu.org, qemu-devel@nongnu.org Cc: Strahinja Jankovic , Peter Maydell , Beniamino Galvani , =?utf-8?q?Philippe_Mathieu-Daud?= =?utf-8?q?=C3=A9?= , Niek Linnenbank , qianfan Zhao Subject: [PATCH v5 03/11] hw: allwinner-r40: Complete uart devices Date: Tue, 23 May 2023 18:05:00 +0800 Message-Id: <20230523100508.32564-4-qianfanguijin@163.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20230523100508.32564-1-qianfanguijin@163.com> References: <20230523100508.32564-1-qianfanguijin@163.com> MIME-Version: 1.0 X-CM-TRANSID: _____wAnDC3Wj2xkMfafAQ--.65319S5 X-Coremail-Antispam: 1Uf129KBjvJXoWxXFWUJw4DKryxJF1xWr47CFg_yoW5ZF18pr 9IkrZ5GFyjg3W3ArWxKws3ZrWSy3W8GrnFy3WIya13tF18Xr40vrW2qa1UGr43KrZ3JFW3 Xr98Gr47W3W2qwUanT9S1TB71UUUUUUqnTZGkaVYY2UrUUUUjbIjqfuFe4nvWSU5nxnvy2 9KBjDUYxBIdaVFxhVjvjDU0xZFpf9x0zRX_-QUUUUU= X-Originating-IP: [218.201.129.19] X-CM-SenderInfo: htld0w5dqj3xxmlqqiywtou0bp/1tbiGgB47VaEFUv5dgAAsM Received-SPF: pass client-ip=220.181.12.197; envelope-from=qianfanguijin@163.com; helo=m12.mail.163.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, RCVD_IN_DNSWL_NONE=-0.0001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001, T_SCC_BODY_TEXT_LINE=-0.01 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+incoming=patchwork.ozlabs.org@nongnu.org Sender: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org From: qianfan Zhao R40 has eight UARTs, support both 16450 and 16550 compatible modes. Signed-off-by: qianfan Zhao --- hw/arm/allwinner-r40.c | 34 +++++++++++++++++++++++++++++++--- include/hw/arm/allwinner-r40.h | 8 ++++++++ 2 files changed, 39 insertions(+), 3 deletions(-) diff --git a/hw/arm/allwinner-r40.c b/hw/arm/allwinner-r40.c index 72973f69ff..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, @@ -157,6 +164,13 @@ enum { /* 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_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, @@ -384,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 >*/ From patchwork Tue May 23 10:05:01 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: qianfan X-Patchwork-Id: 1785022 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@legolas.ozlabs.org Authentication-Results: legolas.ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=nongnu.org (client-ip=209.51.188.17; helo=lists.gnu.org; envelope-from=qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org; receiver=) Authentication-Results: legolas.ozlabs.org; dkim=pass (1024-bit key; unprotected) header.d=163.com header.i=@163.com header.a=rsa-sha256 header.s=s110527 header.b=YFmakWUn; dkim-atps=neutral Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1.2 with cipher ECDHE-ECDSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by legolas.ozlabs.org (Postfix) with ESMTPS id 4QQVM62wq8z20Pb for ; Tue, 23 May 2023 20:05:54 +1000 (AEST) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1q1OtZ-0004pG-Un; Tue, 23 May 2023 06:05:33 -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 1q1OtX-0004kq-2v; Tue, 23 May 2023 06:05:31 -0400 Received: from m12.mail.163.com ([220.181.12.196]) by eggs.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1q1OtV-00070L-4n; Tue, 23 May 2023 06:05:30 -0400 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=163.com; s=s110527; h=From:Subject:Date:Message-Id:MIME-Version; bh=3Xj1O yik9nleFeLnHwW9efP4x4Z2mcv8RHBqm/99bCo=; b=YFmakWUnUYmnc9VSpgxo/ UNkvpT5XnbWoLuCqNEKx0bbBdj0hnCKkaqMSu/alGpBbEecuXgE8g3KBvQ4FWPBo HtNsPuUGWtsJdxSLhJ/hDyQjIMOXqjv+YnL4aRBwacWLZg44bftGZyygXMiwKRiK CVFa4nLyDXrzHQuNWDEHUY= Received: from DESKTOP-B1R4FVG.localdomain (unknown [218.201.129.19]) by zwqz-smtp-mta-g0-3 (Coremail) with SMTP id _____wAnDC3Wj2xkMfafAQ--.65319S6; Tue, 23 May 2023 18:05:20 +0800 (CST) From: qianfanguijin@163.com To: qemu-arm@nongnu.org, qemu-devel@nongnu.org Cc: Strahinja Jankovic , Peter Maydell , Beniamino Galvani , =?utf-8?q?Philippe_Mathieu-Daud?= =?utf-8?q?=C3=A9?= , Niek Linnenbank , qianfan Zhao Subject: [PATCH v5 04/11] hw: arm: allwinner-r40: Add i2c0 device Date: Tue, 23 May 2023 18:05:01 +0800 Message-Id: <20230523100508.32564-5-qianfanguijin@163.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20230523100508.32564-1-qianfanguijin@163.com> References: <20230523100508.32564-1-qianfanguijin@163.com> MIME-Version: 1.0 X-CM-TRANSID: _____wAnDC3Wj2xkMfafAQ--.65319S6 X-Coremail-Antispam: 1Uf129KBjvJXoWxAr4xXF13GF1xGF1kAw1DZFb_yoW5uF1DpF W5C398Gryjg3WrAFW8Kws3Zry8t348Grnrt3W0kFWxtF1jgF4v9r42qa15GF45KFZ7WF47 XrZxtFW3W3W2qaDanT9S1TB71UUUUUUqnTZGkaVYY2UrUUUUjbIjqfuFe4nvWSU5nxnvy2 9KBjDUYxBIdaVFxhVjvjDU0xZFpf9x0ziec_PUUUUU= X-Originating-IP: [218.201.129.19] X-CM-SenderInfo: htld0w5dqj3xxmlqqiywtou0bp/1tbiQgN47VaEFabILwABsT Received-SPF: pass client-ip=220.181.12.196; envelope-from=qianfanguijin@163.com; helo=m12.mail.163.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, RCVD_IN_DNSWL_NONE=-0.0001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001, T_SCC_BODY_TEXT_LINE=-0.01 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+incoming=patchwork.ozlabs.org@nongnu.org Sender: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org From: qianfan Zhao 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 Reviewed-by: Niek Linnenbank --- 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; From patchwork Tue May 23 10:05:02 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: qianfan X-Patchwork-Id: 1785030 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@legolas.ozlabs.org Authentication-Results: legolas.ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=nongnu.org (client-ip=209.51.188.17; helo=lists.gnu.org; envelope-from=qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org; receiver=) Authentication-Results: legolas.ozlabs.org; dkim=pass (1024-bit key; unprotected) header.d=163.com header.i=@163.com header.a=rsa-sha256 header.s=s110527 header.b=MwbKrcCA; dkim-atps=neutral Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1.2 with cipher ECDHE-ECDSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by legolas.ozlabs.org (Postfix) with ESMTPS id 4QQVPB3qrQz20Pb for ; Tue, 23 May 2023 20:07:42 +1000 (AEST) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1q1Ou0-0005Ce-KE; Tue, 23 May 2023 06:06:00 -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 1q1Otu-0004zL-Ev; Tue, 23 May 2023 06:05:54 -0400 Received: from m12.mail.163.com ([220.181.12.215]) by eggs.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1q1Otp-00078R-Pd; Tue, 23 May 2023 06:05:54 -0400 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=163.com; s=s110527; h=From:Subject:Date:Message-Id:MIME-Version; bh=Dslg0 74OGYKg+oVUVtOiK62XXqjzz8YznDMJnBnvWvU=; b=MwbKrcCAEC2AjqoQQRX9X ZGqGKtoQXDkZcxwsv+fyzSxhdbs9cU6ktEdBJtkO/Ex9MFuLOffufRGtf0bFVor8 5AWL19cXG0mcr8JUKlXGbHBwPcMIVl6E/4nQxGsQvEeLySu8ZiI4K/Dj4Kf2ur0W +GHZeuLeJ+Xlapt3Go3weY= Received: from DESKTOP-B1R4FVG.localdomain (unknown [218.201.129.19]) by zwqz-smtp-mta-g0-3 (Coremail) with SMTP id _____wAnDC3Wj2xkMfafAQ--.65319S7; Tue, 23 May 2023 18:05:22 +0800 (CST) From: qianfanguijin@163.com To: qemu-arm@nongnu.org, qemu-devel@nongnu.org Cc: Strahinja Jankovic , Peter Maydell , Beniamino Galvani , =?utf-8?q?Philippe_Mathieu-Daud?= =?utf-8?q?=C3=A9?= , Niek Linnenbank , qianfan Zhao Subject: [PATCH v5 05/11] hw/misc: Rename axp209 to axp22x and add support AXP221 PMU Date: Tue, 23 May 2023 18:05:02 +0800 Message-Id: <20230523100508.32564-6-qianfanguijin@163.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20230523100508.32564-1-qianfanguijin@163.com> References: <20230523100508.32564-1-qianfanguijin@163.com> MIME-Version: 1.0 X-CM-TRANSID: _____wAnDC3Wj2xkMfafAQ--.65319S7 X-Coremail-Antispam: 1Uf129KBjvAXoW3uryDKr47XrW5KFWxJFyUAwb_yoW8CrW3Wo WFga9rZw1UKrWxAr1S9ry8trWfXr12kF4UXF47AFn29a1xAFn3ZrsIkw4kWr13KFs5KryU Xr1vy39xAFnIvas5n29KB7ZKAUJUUUUU529EdanIXcx71UUUUU7v73VFW2AGmfu7bjvjm3 AaLaJ3UbIYCTnIWIevJa73UjIFyTuYvj4RAyCXUUUUU X-Originating-IP: [218.201.129.19] X-CM-SenderInfo: htld0w5dqj3xxmlqqiywtou0bp/1tbiXAN47VXl6riLgAAAsj Received-SPF: pass client-ip=220.181.12.215; envelope-from=qianfanguijin@163.com; helo=m12.mail.163.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, RCVD_IN_DNSWL_NONE=-0.0001, RCVD_IN_MSPIKE_H2=-0.001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001, T_SCC_BODY_TEXT_LINE=-0.01 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+incoming=patchwork.ozlabs.org@nongnu.org Sender: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org From: qianfan Zhao This patch adds minimal support for AXP-221 PMU and connect it to bananapi M2U board. Signed-off-by: qianfan Zhao --- 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 0afb87ec1a..aacfbe17ad 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 - * - * 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..41538c1cd7 --- /dev/null +++ b/hw/misc/axp2xx.c @@ -0,0 +1,283 @@ +/* + * AXP-2XX PMU Emulation, supported lists: + * AXP209 + * AXP221 + * + * Copyright (C) 2022 Strahinja Jankovic + * Copyright (C) 2023 qianfan Zhao + * + * 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_AXP2XX, + .version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT8_ARRAY(regs, AXP2xxI2CState, NR_REGS), + VMSTATE_UINT8(ptr, AXP2xxI2CState), + VMSTATE_UINT8(count, 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" From patchwork Tue May 23 10:05:03 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: qianfan X-Patchwork-Id: 1785032 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@legolas.ozlabs.org Authentication-Results: legolas.ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=nongnu.org (client-ip=209.51.188.17; helo=lists.gnu.org; envelope-from=qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org; receiver=) Authentication-Results: legolas.ozlabs.org; dkim=pass (1024-bit key; unprotected) header.d=163.com header.i=@163.com header.a=rsa-sha256 header.s=s110527 header.b=kdplax0U; dkim-atps=neutral Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1.2 with cipher ECDHE-ECDSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by legolas.ozlabs.org (Postfix) with ESMTPS id 4QQVPJ0tNhz20Pb for ; Tue, 23 May 2023 20:07:48 +1000 (AEST) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1q1Ou1-0005Dq-Ks; Tue, 23 May 2023 06:06:01 -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 1q1Otw-00054F-V5; Tue, 23 May 2023 06:05:58 -0400 Received: from m12.mail.163.com ([220.181.12.215]) by eggs.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1q1Ott-00079I-B7; Tue, 23 May 2023 06:05:56 -0400 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=163.com; s=s110527; h=From:Subject:Date:Message-Id:MIME-Version; bh=vP8Cq KAV00ydKV1/c+Ir8Sc5aY0y4lMUdijiaaCTOSc=; b=kdplax0UCJ2yb/Fo0ghgs 3VmtyNUP7Q3KJXhhs1XYjuBFH68AsC1XHw3hLN7gPv0zFDW14Mor1UhmCG3qTfBD drA3yvxlPiXAIo4HXiADzhFrmu6pCejEn5CMIzUo3aWn6CJEuItxJ2nUvQOWRq5F bfBXLv0LuKAoVMF7ys6Itg= Received: from DESKTOP-B1R4FVG.localdomain (unknown [218.201.129.19]) by zwqz-smtp-mta-g0-3 (Coremail) with SMTP id _____wAnDC3Wj2xkMfafAQ--.65319S8; Tue, 23 May 2023 18:05:23 +0800 (CST) From: qianfanguijin@163.com To: qemu-arm@nongnu.org, qemu-devel@nongnu.org Cc: Strahinja Jankovic , Peter Maydell , Beniamino Galvani , =?utf-8?q?Philippe_Mathieu-Daud?= =?utf-8?q?=C3=A9?= , Niek Linnenbank , qianfan Zhao Subject: [PATCH v5 06/11] hw/arm/allwinner-r40: add SDRAM controller device Date: Tue, 23 May 2023 18:05:03 +0800 Message-Id: <20230523100508.32564-7-qianfanguijin@163.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20230523100508.32564-1-qianfanguijin@163.com> References: <20230523100508.32564-1-qianfanguijin@163.com> MIME-Version: 1.0 X-CM-TRANSID: _____wAnDC3Wj2xkMfafAQ--.65319S8 X-Coremail-Antispam: 1Uf129KBjvAXoWftFWxCw15XrWrCFy8ur45ZFb_yoW5Gw4xuo WSgF45Zw4agw12qr1Fgw1jyr13Kws8KrWxJw45GF43ua98JFs8G39xJwn5Xr4fWr4FkFn7 XFySgr1fZrWkCas3n29KB7ZKAUJUUUUU529EdanIXcx71UUUUU7v73VFW2AGmfu7bjvjm3 AaLaJ3UbIYCTnIWIevJa73UjIFyTuYvj4R1xRQUUUUU X-Originating-IP: [218.201.129.19] X-CM-SenderInfo: htld0w5dqj3xxmlqqiywtou0bp/xtbBzgZ47WI0ZReIvQAAsM Received-SPF: pass client-ip=220.181.12.215; envelope-from=qianfanguijin@163.com; helo=m12.mail.163.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, RCVD_IN_DNSWL_NONE=-0.0001, RCVD_IN_MSPIKE_H2=-0.001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001, T_SCC_BODY_TEXT_LINE=-0.01 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+incoming=patchwork.ozlabs.org@nongnu.org Sender: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org From: qianfan Zhao 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 --- 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..ea6124744f --- /dev/null +++ b/hw/misc/allwinner-r40-dramc.c @@ -0,0 +1,513 @@ +/* + * Allwinner R40 SDRAM Controller emulation + * + * CCopyright (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 "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); + + /* + * We only support DRAM size up to 1G now, so prepare a high memory page + * after 1G 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 2G memory but we only support up to 1G now. index 3 */ + memory_region_init_io(&s->detect_cells, OBJECT(s), + &allwinner_r40_detect_ops, s, + "DRAMCELLS", 1 * 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 + * + * 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_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 */ From patchwork Tue May 23 10:05:04 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: qianfan X-Patchwork-Id: 1785027 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@legolas.ozlabs.org Authentication-Results: legolas.ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=nongnu.org (client-ip=209.51.188.17; helo=lists.gnu.org; envelope-from=qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org; receiver=) Authentication-Results: legolas.ozlabs.org; dkim=pass (1024-bit key; unprotected) header.d=163.com header.i=@163.com header.a=rsa-sha256 header.s=s110527 header.b=K86sNcuI; dkim-atps=neutral Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1.2 with cipher ECDHE-ECDSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by legolas.ozlabs.org (Postfix) with ESMTPS id 4QQVN74wyxz20Pb for ; Tue, 23 May 2023 20:06:47 +1000 (AEST) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1q1Otg-0004sj-JF; Tue, 23 May 2023 06:05:40 -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 1q1Ote-0004s6-Td; Tue, 23 May 2023 06:05:38 -0400 Received: from m12.mail.163.com ([220.181.12.217]) by eggs.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1q1Otc-00074g-Tg; Tue, 23 May 2023 06:05:38 -0400 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=163.com; s=s110527; h=From:Subject:Date:Message-Id:MIME-Version; bh=VgY0P hCPXPsELfJ1N3Y7O3sibbpZFqwyeYcnZq7lV2c=; b=K86sNcuIMZDsmMIFiQmOh ECTc9tfSpY5NVkUSIgxTtYkPLNCOS09kWZRG9rwwFRhZmVnboXWkBgg0xsiCov2u K5sVKRhE1qQBH4pvYo1bf0XIHXQtvimKW181/MedZi021FtnR+Ne7vXKWaYMvqRJ ua2NBmbvYriXYXzjeFC9QA= Received: from DESKTOP-B1R4FVG.localdomain (unknown [218.201.129.19]) by zwqz-smtp-mta-g0-3 (Coremail) with SMTP id _____wAnDC3Wj2xkMfafAQ--.65319S9; Tue, 23 May 2023 18:05:26 +0800 (CST) From: qianfanguijin@163.com To: qemu-arm@nongnu.org, qemu-devel@nongnu.org Cc: Strahinja Jankovic , Peter Maydell , Beniamino Galvani , =?utf-8?q?Philippe_Mathieu-Daud?= =?utf-8?q?=C3=A9?= , Niek Linnenbank , qianfan Zhao Subject: [PATCH v5 07/11] hw: sd: allwinner-sdhost: Add sun50i-a64 SoC support Date: Tue, 23 May 2023 18:05:04 +0800 Message-Id: <20230523100508.32564-8-qianfanguijin@163.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20230523100508.32564-1-qianfanguijin@163.com> References: <20230523100508.32564-1-qianfanguijin@163.com> MIME-Version: 1.0 X-CM-TRANSID: _____wAnDC3Wj2xkMfafAQ--.65319S9 X-Coremail-Antispam: 1Uf129KBjvJXoW3Jr4DAr4DWFWruw45Gr1fCrg_yoW3Ar1DpF ZYkw45GF45Gan3Jr47KFnrKF1rG34kCr4UKrn3uF97AryDJ3Z0qw4DKay5JF4DGFZ8CF4U Aan0qFy5uFsxtaUanT9S1TB71UUUUUUqnTZGkaVYY2UrUUUUjbIjqfuFe4nvWSU5nxnvy2 9KBjDUYxBIdaVFxhVjvjDU0xZFpf9x0z_F4iPUUUUU= X-Originating-IP: [218.201.129.19] X-CM-SenderInfo: htld0w5dqj3xxmlqqiywtou0bp/xtbBzgp47WI0ZReI7gABsS Received-SPF: pass client-ip=220.181.12.217; envelope-from=qianfanguijin@163.com; helo=m12.mail.163.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, RCVD_IN_DNSWL_NONE=-0.0001, RCVD_IN_MSPIKE_H2=-0.001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001, T_SCC_BODY_TEXT_LINE=-0.01 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+incoming=patchwork.ozlabs.org@nongnu.org Sender: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org From: qianfan Zhao 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 --- hw/arm/allwinner-r40.c | 2 +- hw/sd/allwinner-sdhost.c | 72 ++++++++++++++++++++++++++++++-- include/hw/sd/allwinner-sdhost.h | 9 ++++ 3 files changed, 79 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..286e009509 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) @@ -879,6 +911,7 @@ static void allwinner_sdhost_sun4i_class_init(ObjectClass *klass, void *data) AwSdHostClass *sc = AW_SDHOST_CLASS(klass); sc->max_desc_size = 8 * KiB; sc->is_sun4i = true; + sc->can_calibrate = false; } static void allwinner_sdhost_sun5i_class_init(ObjectClass *klass, void *data) @@ -886,6 +919,25 @@ static void allwinner_sdhost_sun5i_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 = 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 = { @@ -910,6 +962,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 +986,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 */ From patchwork Tue May 23 10:05:05 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: qianfan X-Patchwork-Id: 1785031 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@legolas.ozlabs.org Authentication-Results: legolas.ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=nongnu.org (client-ip=209.51.188.17; helo=lists.gnu.org; envelope-from=qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org; receiver=) Authentication-Results: legolas.ozlabs.org; dkim=pass (1024-bit key; unprotected) header.d=163.com header.i=@163.com header.a=rsa-sha256 header.s=s110527 header.b=Zl7OnGyh; dkim-atps=neutral Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1.2 with cipher ECDHE-ECDSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by legolas.ozlabs.org (Postfix) with ESMTPS id 4QQVPH052xz20Pb for ; Tue, 23 May 2023 20:07:47 +1000 (AEST) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1q1Ou0-0005B6-77; Tue, 23 May 2023 06:06:00 -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 1q1Oty-00054J-0R; Tue, 23 May 2023 06:05:58 -0400 Received: from m12.mail.163.com ([220.181.12.216]) by eggs.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1q1Otv-0007AL-O2; Tue, 23 May 2023 06:05:57 -0400 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=163.com; s=s110527; h=From:Subject:Date:Message-Id:MIME-Version; bh=q7swa I098LxAcKrhBldMUS51FKxFQK+gihzOI2ANEso=; b=Zl7OnGyhLKSd2D9AryIQL N5qtOPxBn/ReSuzib9EXzH13hjHyA5avDnoM0BRgkZFhKcpptNAxzWcwp88nkXAw 7OeXo6cgFgg0I/P9ILZT6aIuj54uRUwjxYuiLaJLKugXNRY36hG1Lt50apvOAhz2 jXFUpXHVFlRmVxYsMkYazw= Received: from DESKTOP-B1R4FVG.localdomain (unknown [218.201.129.19]) by zwqz-smtp-mta-g0-3 (Coremail) with SMTP id _____wAnDC3Wj2xkMfafAQ--.65319S10; Tue, 23 May 2023 18:05:28 +0800 (CST) From: qianfanguijin@163.com To: qemu-arm@nongnu.org, qemu-devel@nongnu.org Cc: Strahinja Jankovic , Peter Maydell , Beniamino Galvani , =?utf-8?q?Philippe_Mathieu-Daud?= =?utf-8?q?=C3=A9?= , Niek Linnenbank , qianfan Zhao Subject: [PATCH v5 08/11] hw: arm: allwinner-r40: Add emac and gmac support Date: Tue, 23 May 2023 18:05:05 +0800 Message-Id: <20230523100508.32564-9-qianfanguijin@163.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20230523100508.32564-1-qianfanguijin@163.com> References: <20230523100508.32564-1-qianfanguijin@163.com> MIME-Version: 1.0 X-CM-TRANSID: _____wAnDC3Wj2xkMfafAQ--.65319S10 X-Coremail-Antispam: 1Uf129KBjvJXoW3Jr4kury8GFWkXw4UXr17GFg_yoW7Kw43pF 43Gr98KrWFg3WrAr4kKws3JFyftw18CrnrtF1SkF4xJF1DWr1kWr42ya17uFy5Krs7Ja13 WrZagFWSga17t3DanT9S1TB71UUUUUUqnTZGkaVYY2UrUUUUjbIjqfuFe4nvWSU5nxnvy2 9KBjDUYxBIdaVFxhVjvjDU0xZFpf9x0zRceOZUUUUU= X-Originating-IP: [218.201.129.19] X-CM-SenderInfo: htld0w5dqj3xxmlqqiywtou0bp/xtbBzgp47WI0ZReI7gAAsT Received-SPF: pass client-ip=220.181.12.216; envelope-from=qianfanguijin@163.com; helo=m12.mail.163.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, RCVD_IN_DNSWL_NONE=-0.0001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001, T_SCC_BODY_TEXT_LINE=-0.01 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+incoming=patchwork.ozlabs.org@nongnu.org Sender: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org From: qianfan Zhao 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 --- 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; From patchwork Tue May 23 10:05:06 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: qianfan X-Patchwork-Id: 1785023 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@legolas.ozlabs.org Authentication-Results: legolas.ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=nongnu.org (client-ip=209.51.188.17; helo=lists.gnu.org; envelope-from=qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org; receiver=) Authentication-Results: legolas.ozlabs.org; dkim=pass (1024-bit key; unprotected) header.d=163.com header.i=@163.com header.a=rsa-sha256 header.s=s110527 header.b=E4bhy7y9; dkim-atps=neutral Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1.2 with cipher ECDHE-ECDSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by legolas.ozlabs.org (Postfix) with ESMTPS id 4QQVMQ1cLWz20Pb for ; Tue, 23 May 2023 20:06:10 +1000 (AEST) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1q1Otk-0004uW-HW; Tue, 23 May 2023 06:05:44 -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 1q1Oti-0004tP-EX; Tue, 23 May 2023 06:05:42 -0400 Received: from m12.mail.163.com ([220.181.12.217]) by eggs.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1q1Otf-00075c-LR; Tue, 23 May 2023 06:05:42 -0400 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=163.com; s=s110527; h=From:Subject:Date:Message-Id:MIME-Version; bh=mOXE2 uaIcrp25NSnK4PI8/ZCZ1s0FQlugq54PpKya5Y=; b=E4bhy7y9a/y4VFGsrDVC6 ae2arNj9SmS6r88Ewor+WzhUPQXDusFPdHv+LMG5ht3r3yxyugf9+BtkeXNwtsBY 9F1KftP52LcHhLvJMVtsIgcg9ywSID0ybIAdTKGShJBrOSQBEr93a/bZB9Cfyqbc UOPEThRjAJx34C+vGEpp7g= Received: from DESKTOP-B1R4FVG.localdomain (unknown [218.201.129.19]) by zwqz-smtp-mta-g0-3 (Coremail) with SMTP id _____wAnDC3Wj2xkMfafAQ--.65319S11; Tue, 23 May 2023 18:05:30 +0800 (CST) From: qianfanguijin@163.com To: qemu-arm@nongnu.org, qemu-devel@nongnu.org Cc: Strahinja Jankovic , Peter Maydell , Beniamino Galvani , =?utf-8?q?Philippe_Mathieu-Daud?= =?utf-8?q?=C3=A9?= , Niek Linnenbank , qianfan Zhao Subject: [PATCH v5 09/11] hw: arm: allwinner-sramc: Add SRAM Controller support for R40 Date: Tue, 23 May 2023 18:05:06 +0800 Message-Id: <20230523100508.32564-10-qianfanguijin@163.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20230523100508.32564-1-qianfanguijin@163.com> References: <20230523100508.32564-1-qianfanguijin@163.com> MIME-Version: 1.0 X-CM-TRANSID: _____wAnDC3Wj2xkMfafAQ--.65319S11 X-Coremail-Antispam: 1Uf129KBjvAXoWfJFWDtr18GFyrAry3Wr17Jrb_yoW8JF43Zo WIgF45XrWIg3s7ZrnYkw1ayr17WrnxKr4xAw4YkF4aka45Awsrt348t3s3Ja1fWr4FyF1x Ja4SgryfC3ykG3s5n29KB7ZKAUJUUUUU529EdanIXcx71UUUUU7v73VFW2AGmfu7bjvjm3 AaLaJ3UbIYCTnIWIevJa73UjIFyTuYvj4R1xRQUUUUU X-Originating-IP: [218.201.129.19] X-CM-SenderInfo: htld0w5dqj3xxmlqqiywtou0bp/1tbiXAt47VXl6riL5QAAsO Received-SPF: pass client-ip=220.181.12.217; envelope-from=qianfanguijin@163.com; helo=m12.mail.163.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, RCVD_IN_DNSWL_NONE=-0.0001, RCVD_IN_MSPIKE_H2=-0.001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001, T_SCC_BODY_TEXT_LINE=-0.01 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+incoming=patchwork.ozlabs.org@nongnu.org Sender: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org From: qianfan Zhao Only a few important registers are added, especially the SRAM_VER register. Signed-off-by: qianfan Zhao Reviewed-by: Niek Linnenbank --- 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 aacfbe17ad..c64b6ea211 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 + * + * 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 "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 + * + * 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_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 */ From patchwork Tue May 23 10:05:07 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: qianfan X-Patchwork-Id: 1785029 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@legolas.ozlabs.org Authentication-Results: legolas.ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=nongnu.org (client-ip=209.51.188.17; helo=lists.gnu.org; envelope-from=qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org; receiver=) Authentication-Results: legolas.ozlabs.org; dkim=pass (1024-bit key; unprotected) header.d=163.com header.i=@163.com header.a=rsa-sha256 header.s=s110527 header.b=AdrKLiUT; dkim-atps=neutral Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1.2 with cipher ECDHE-ECDSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by legolas.ozlabs.org (Postfix) with ESMTPS id 4QQVP56fyjz20Pb for ; Tue, 23 May 2023 20:07:36 +1000 (AEST) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1q1Ou5-0005H3-9U; Tue, 23 May 2023 06:06:05 -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 1q1Ou3-0005En-Hh; Tue, 23 May 2023 06:06:03 -0400 Received: from m12.mail.163.com ([220.181.12.198]) by eggs.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1q1Ou1-0007BV-51; Tue, 23 May 2023 06:06:03 -0400 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=163.com; s=s110527; h=From:Subject:Date:Message-Id:MIME-Version; bh=fGHHE h0PxGx2UX3s/8ryluow5lerVjZCF1SV9VI4ffU=; b=AdrKLiUTrMm1/81r0orNg Pxf9RSWBpRd2FyLLfNiT4X0Z7sGCcANNh4KgVfV9uv4HNoiHiTPUlhUNqUibC+Xk fhja4HRRzf4DT/ppUdwlV0Qqu7L/gm3s401a+qObkSyRZmgbfC5zTSjJ/UwNd+gP G5rMhZmuM80hcroLO1atfg= Received: from DESKTOP-B1R4FVG.localdomain (unknown [218.201.129.19]) by zwqz-smtp-mta-g0-3 (Coremail) with SMTP id _____wAnDC3Wj2xkMfafAQ--.65319S12; Tue, 23 May 2023 18:05:32 +0800 (CST) From: qianfanguijin@163.com To: qemu-arm@nongnu.org, qemu-devel@nongnu.org Cc: Strahinja Jankovic , Peter Maydell , Beniamino Galvani , =?utf-8?q?Philippe_Mathieu-Daud?= =?utf-8?q?=C3=A9?= , Niek Linnenbank , qianfan Zhao Subject: [PATCH v5 10/11] tests: avocado: boot_linux_console: Add test case for bpim2u Date: Tue, 23 May 2023 18:05:07 +0800 Message-Id: <20230523100508.32564-11-qianfanguijin@163.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20230523100508.32564-1-qianfanguijin@163.com> References: <20230523100508.32564-1-qianfanguijin@163.com> MIME-Version: 1.0 X-CM-TRANSID: _____wAnDC3Wj2xkMfafAQ--.65319S12 X-Coremail-Antispam: 1Uf129KBjvJXoW3Wryxtr1xtw1UKw43Ar1rXrb_yoW3Zr4kpr WDGr15Kr18JFs8Jr4xKrWUu34UJw4vkay7JasrGrZ3WFsFk3yvgFyakr1F9rZ8Xa9avasx Cayqya1xWryUJFJanT9S1TB71UUUUUUqnTZGkaVYY2UrUUUUjbIjqfuFe4nvWSU5nxnvy2 9KBjDUYxBIdaVFxhVjvjDU0xZFpf9x0zR6by_UUUUU= X-Originating-IP: [218.201.129.19] X-CM-SenderInfo: htld0w5dqj3xxmlqqiywtou0bp/1tbiXA147VXl6riL-QAAsQ Received-SPF: pass client-ip=220.181.12.198; envelope-from=qianfanguijin@163.com; helo=m12.mail.163.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, RCVD_IN_DNSWL_NONE=-0.0001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001, T_SCC_BODY_TEXT_LINE=-0.01 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+incoming=patchwork.ozlabs.org@nongnu.org Sender: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org From: qianfan Zhao Add test case for booting from initrd and sd card. Signed-off-by: qianfan Zhao Reviewed-by: Niek Linnenbank Tested-by: Niek Linnenbank --- 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 From patchwork Tue May 23 10:05:55 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: qianfan X-Patchwork-Id: 1785025 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@legolas.ozlabs.org Authentication-Results: legolas.ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=nongnu.org (client-ip=209.51.188.17; helo=lists.gnu.org; envelope-from=qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org; receiver=) Authentication-Results: legolas.ozlabs.org; dkim=pass (1024-bit key; unprotected) header.d=163.com header.i=@163.com header.a=rsa-sha256 header.s=s110527 header.b=LCzSb0Xy; dkim-atps=neutral Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1.2 with cipher ECDHE-ECDSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by legolas.ozlabs.org (Postfix) with ESMTPS id 4QQVN01v6nz20Pb for ; Tue, 23 May 2023 20:06:40 +1000 (AEST) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1q1Ou9-0005Mg-Rn; Tue, 23 May 2023 06:06:09 -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 1q1Ou7-0005Ig-NR; Tue, 23 May 2023 06:06:07 -0400 Received: from m12.mail.163.com ([220.181.12.216]) by eggs.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1q1Ou5-0007DO-E1; Tue, 23 May 2023 06:06:07 -0400 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=163.com; s=s110527; h=From:Subject:Date:Message-Id:MIME-Version; bh=bA3h2 I7xP6Oj01W3XOIOrdMHbHFaP7w3meXx8CbMtuQ=; b=LCzSb0Xyarg6wLf3PF1Qq PebizUtr/u6dMtnPJolGXGtR1uDDUChAf2vqWjhnq/gqmKbRhc77a/iMm+982iER uRUDVj9vKOdf6lp9cRNT9NiFlAHNjDiSbQHm8JPBcV5Jp84JVslJbq4kDA71m89P VnDRTU3ZZlZ5mxJ3XjFQWQ= Received: from DESKTOP-B1R4FVG.localdomain (unknown [218.201.129.19]) by zwqz-smtp-mta-g1-2 (Coremail) with SMTP id _____wBXjpIDkGxk9rKiAQ--.54569S2; Tue, 23 May 2023 18:05:56 +0800 (CST) From: qianfanguijin@163.com To: qemu-arm@nongnu.org, qemu-devel@nongnu.org Cc: Strahinja Jankovic , Peter Maydell , Beniamino Galvani , =?utf-8?q?Philippe_Mathieu-Daud?= =?utf-8?q?=C3=A9?= , Niek Linnenbank , qianfan Zhao Subject: [PATCH v5 11/11] docs: system: arm: Introduce bananapi_m2u Date: Tue, 23 May 2023 18:05:55 +0800 Message-Id: <20230523100555.32718-1-qianfanguijin@163.com> X-Mailer: git-send-email 2.25.1 MIME-Version: 1.0 X-CM-TRANSID: _____wBXjpIDkGxk9rKiAQ--.54569S2 X-Coremail-Antispam: 1Uf129KBjvJXoWxuw4fCF4ruF4fCr1xZw47urg_yoW7CF1kpF yvka15KrWkJF1Fya97Kw1fWFy5Xas5ArWUJF1kJ34FqF98Kr1vqwn3Kwn8Wasrtw4Ivw10 qrW7KF12gwn8J3DanT9S1TB71UUUUUUqnTZGkaVYY2UrUUUUjbIjqfuFe4nvWSU5nxnvy2 9KBjDUYxBIdaVFxhVjvjDU0xZFpf9x0pM2NZrUUUUU= X-Originating-IP: [218.201.129.19] X-CM-SenderInfo: htld0w5dqj3xxmlqqiywtou0bp/1tbiQgh47VaEFabJwAAAs3 Received-SPF: pass client-ip=220.181.12.216; envelope-from=qianfanguijin@163.com; helo=m12.mail.163.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, RCVD_IN_DNSWL_NONE=-0.0001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001, T_SCC_BODY_TEXT_LINE=-0.01, WEIRD_QUOTING=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+incoming=patchwork.ozlabs.org@nongnu.org Sender: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org From: qianfan Zhao Add documents for Banana Pi M2U Signed-off-by: qianfan Zhao Reviewed-by: Niek Linnenbank --- docs/system/arm/bananapi_m2u.rst | 138 +++++++++++++++++++++++++++++++ 1 file changed, 138 insertions(+) create mode 100644 docs/system/arm/bananapi_m2u.rst diff --git a/docs/system/arm/bananapi_m2u.rst b/docs/system/arm/bananapi_m2u.rst new file mode 100644 index 0000000000..ae7194a9df --- /dev/null +++ b/docs/system/arm/bananapi_m2u.rst @@ -0,0 +1,138 @@ +Banana Pi BPI-M2U (``bpim2u``) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Banana Pi BPI-M2 Ultra is a quad-core mini single board computer built with +Allwinner A40i/R40/V40 SoC. It features 2GB of RAM and 8GB eMMC. It also +has onboard WiFi and BT. On the ports side, the BPI-M2 Ultra has 2 USB A +2.0 ports, 1 USB OTG port, 1 HDMI port, 1 audio jack, a DC power port, +and last but not least, a SATA port. + +Supported devices +""""""""""""""""" + +The Banana Pi M2U machine supports the following devices: + + * SMP (Quad Core Cortex-A7) + * Generic Interrupt Controller configuration + * SRAM mappings + * SDRAM controller + * Timer device (re-used from Allwinner A10) + * UART + * SD/MMC storage controller + * EMAC ethernet + * GMAC ethernet + * Clock Control Unit + * TWI (I2C) + +Limitations +""""""""""" + +Currently, Banana Pi M2U does *not* support the following features: + +- Graphical output via HDMI, GPU and/or the Display Engine +- Audio output +- Hardware Watchdog +- Real Time Clock +- USB 2.0 interfaces + +Also see the 'unimplemented' array in the Allwinner R40 SoC module +for a complete list of unimplemented I/O devices: ``./hw/arm/allwinner-r40.c`` + +Boot options +"""""""""""" + +The Banana Pi M2U machine can start using the standard -kernel functionality +for loading a Linux kernel or ELF executable. Additionally, the Banana Pi M2U +machine can also emulate the BootROM which is present on an actual Allwinner R40 +based SoC, which loads the bootloader from a SD card, specified via the -sd +argument to qemu-system-arm. + +Running mainline Linux +"""""""""""""""""""""" + +To build a Linux mainline kernel that can be booted by the Banana Pi M2U machine, +simply configure the kernel using the sunxi_defconfig configuration: + +.. code-block:: bash + + $ ARCH=arm CROSS_COMPILE=arm-linux-gnueabi- make mrproper + $ ARCH=arm CROSS_COMPILE=arm-linux-gnueabi- make sunxi_defconfig + +To boot the newly build linux kernel in QEMU with the Banana Pi M2U machine, use: + +.. code-block:: bash + + $ qemu-system-arm -M bpim2u -nographic \ + -kernel /path/to/linux/arch/arm/boot/zImage \ + -append 'console=ttyS0,115200' \ + -dtb /path/to/linux/arch/arm/boot/dts/sun8i-r40-bananapi-m2-ultra.dtb + +Banana Pi M2U images +"""""""""""""""""""" + +Note that the mainline kernel does not have a root filesystem. You can choose +to build you own image with buildroot using the bananapi_m2_ultra_defconfig. +Also see https://buildroot.org for more information. + +Another possibility is to run an OpenWrt image for Banana Pi M2U which +can be downloaded from: + + https://downloads.openwrt.org/releases/22.03.3/targets/sunxi/cortexa7/ + +When using an image as an SD card, it must be resized to a power of two. This can be +done with the ``qemu-img`` command. It is recommended to only increase the image size +instead of shrinking it to a power of two, to avoid loss of data. For example, +to prepare a downloaded Armbian image, first extract it and then increase +its size to one gigabyte as follows: + +.. code-block:: bash + + $ qemu-img resize \ + openwrt-22.03.3-sunxi-cortexa7-sinovoip_bananapi-m2-ultra-ext4-sdcard.img \ + 1G + +Instead of providing a custom Linux kernel via the -kernel command you may also +choose to let the Banana Pi M2U machine load the bootloader from SD card, just like +a real board would do using the BootROM. Simply pass the selected image via the -sd +argument and remove the -kernel, -append, -dbt and -initrd arguments: + +.. code-block:: bash + + $ qemu-system-arm -M bpim2u -nic user -nographic \ + -sd openwrt-22.03.3-sunxi-cortexa7-sinovoip_bananapi-m2-ultra-ext4-sdcard.img + +Running U-Boot +"""""""""""""" + +U-Boot mainline can be build and configured using the Bananapi_M2_Ultra_defconfig +using similar commands as describe above for Linux. Note that it is recommended +for development/testing to select the following configuration setting in U-Boot: + + Device Tree Control > Provider for DTB for DT Control > Embedded DTB + +The BootROM of allwinner R40 loading u-boot from the 8KiB offset of sdcard. +Let's create an bootable disk image: + +.. code-block:: bash + + $ dd if=/dev/zero of=sd.img bs=32M count=1 + $ dd if=u-boot-sunxi-with-spl.bin of=sd.img bs=1k seek=8 conv=notrunc + +And then boot it. + +.. code-block:: bash + $ qemu-system-arm -M bpim2u -nographic -sd sd.img + +Banana Pi M2U integration tests +"""""""""""""""""""""""""""""" + +The Banana Pi M2U machine has several integration tests included. +To run the whole set of tests, build QEMU from source and simply +provide the following command: + +.. code-block:: bash + + $ cd qemu-build-dir + $ AVOCADO_ALLOW_LARGE_STORAGE=yes tests/venv/bin/avocado \ + --verbose --show=app,console run -t machine:bpim2u \ + ../tests/avocado/boot_linux_console.py