From patchwork Mon Dec 2 21:09:38 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Niek Linnenbank X-Patchwork-Id: 11269961 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 66875138D for ; Mon, 2 Dec 2019 21:53:10 +0000 (UTC) Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id 2BF43206E0 for ; Mon, 2 Dec 2019 21:53:10 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=fail reason="signature verification failed" (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="jUE3wTae" DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org 2BF43206E0 Authentication-Results: mail.kernel.org; dmarc=fail (p=none dis=none) header.from=gmail.com Authentication-Results: mail.kernel.org; spf=pass smtp.mailfrom=qemu-devel-bounces+patchwork-qemu-devel=patchwork.kernel.org@nongnu.org Received: from localhost ([::1]:44966 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1ibtcz-00084U-5v for patchwork-qemu-devel@patchwork.kernel.org; Mon, 02 Dec 2019 16:53:09 -0500 Received: from eggs.gnu.org ([2001:470:142:3::10]:38330) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1ibsxj-0002kA-Rl for qemu-devel@nongnu.org; Mon, 02 Dec 2019 16:10:34 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1ibsxg-0000mN-8Q for qemu-devel@nongnu.org; Mon, 02 Dec 2019 16:10:31 -0500 Received: from mail-wm1-x342.google.com ([2a00:1450:4864:20::342]:33689) by eggs.gnu.org with esmtps (TLS1.0:RSA_AES_128_CBC_SHA1:16) (Exim 4.71) (envelope-from ) id 1ibsxY-0000YO-Ji; Mon, 02 Dec 2019 16:10:22 -0500 Received: by mail-wm1-x342.google.com with SMTP id y23so780786wma.0; Mon, 02 Dec 2019 13:09:59 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=vNPnXm90420wfmia3L0ifKw7OOjXJ8KY6gwGS45HUo8=; b=jUE3wTae+CEXqBH+qobldNTykiKtKaI5y024fUALUm85+MBqeKaWT8wYx7isud0qlO ZG7n7o7ce3Kv+YtVH46HXLo1lIPR42HlGeihxjRQfzQ/S5ISAPpwtYO/dYFLPpaFfCKg 4xLjVJ4DAxaVYI+QxqBc71XJkTACs57pawYNnayTBycco/8R3uB6c32URRV+0mxh1BY5 Ljtmc6yy4aAzF07ZXBIaSo4XcFOWtgyuu4ndOunZ0etsfpio+yS6lrSuznf5dUIL1uRi OIWfxLmbV1i/DKljVEhqlqA2lH6nDqaGZCJoSHb/XixqSI7KPtsGJvl5ZdnmyZlFYE6O cawg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=vNPnXm90420wfmia3L0ifKw7OOjXJ8KY6gwGS45HUo8=; b=bU5Ghv34ozKgj1FBhdt7ZK1rr04uu9CUlqmu7ORhoBMA9LdXR7cxO3TW1A7V8MqKi7 kDn9MLGWwiTmvd+Wk8G+IfmI40wQwB+177Gfd+v0wADkIyIX1ODx+zmRFz7GMuN12Gx8 xEN6rwupgsFQVvMVfelbqfWBVuRhW28wFeNFayNQ6LU8OJwItZ0+vGsJbB1HYEG1Ib0r iOEqhbB7ViUYKFGxEgKSkYAYl1N3JcD05c5s+JjukWfoCzabqx/1zIslUQcgTz9zkcAc 9jTlHJv/m8JMoBkZfNFzjBe9ynzdthijBTUvYrRnU4jePEoBimEHwcFcmVIa45LrRopS gxpQ== X-Gm-Message-State: APjAAAVDvVxUWWBioggDQSbmQ+IqccJeR2HjxBeOPoBefZ7eaNcRtHV9 c1o63W2BYsPwm0t4yq2J81freb38 X-Google-Smtp-Source: APXvYqxeBoOb/sPHDEPYgUAT813SkyzEU6elzQvDLZAJpyE+HN96qGj+sGLy4QLAEmUNclTPbBDOCw== X-Received: by 2002:a1c:f416:: with SMTP id z22mr29848373wma.72.1575320997882; Mon, 02 Dec 2019 13:09:57 -0800 (PST) Received: from pavilion.home ([2a02:a456:6be8:1:8edc:d4ff:fe8b:18b7]) by smtp.gmail.com with ESMTPSA id f1sm770859wrp.93.2019.12.02.13.09.56 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 02 Dec 2019 13:09:56 -0800 (PST) From: Niek Linnenbank To: qemu-devel@nongnu.org Subject: [PATCH 01/10] hw: arm: add Allwinner H3 System-on-Chip Date: Mon, 2 Dec 2019 22:09:38 +0100 Message-Id: <20191202210947.3603-2-nieklinnenbank@gmail.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20191202210947.3603-1-nieklinnenbank@gmail.com> References: <20191202210947.3603-1-nieklinnenbank@gmail.com> X-detected-operating-system: by eggs.gnu.org: Genre and OS details not recognized. X-Received-From: 2a00:1450:4864:20::342 X-Mailman-Approved-At: Mon, 02 Dec 2019 16:38:23 -0500 X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: b.galvani@gmail.com, peter.maydell@linaro.org, Niek Linnenbank , qemu-arm@nongnu.org Errors-To: qemu-devel-bounces+patchwork-qemu-devel=patchwork.kernel.org@nongnu.org Sender: "Qemu-devel" The Allwinner H3 is a System on Chip containing four ARM Cortex A7 processor cores. Features and specifications include DDR2/DDR3 memory, SD/MMC storage cards, 10/100/1000Mbit ethernet, USB 2.0, HDMI and various I/O modules. This commit adds support for the Allwinner H3 System on Chip. Signed-off-by: Niek Linnenbank --- MAINTAINERS | 7 ++ default-configs/arm-softmmu.mak | 1 + hw/arm/Kconfig | 8 ++ hw/arm/Makefile.objs | 1 + hw/arm/allwinner-h3.c | 215 ++++++++++++++++++++++++++++++++ include/hw/arm/allwinner-h3.h | 118 ++++++++++++++++++ 6 files changed, 350 insertions(+) create mode 100644 hw/arm/allwinner-h3.c create mode 100644 include/hw/arm/allwinner-h3.h diff --git a/MAINTAINERS b/MAINTAINERS index 5e5e3e52d6..29c9936037 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -479,6 +479,13 @@ F: hw/*/allwinner* F: include/hw/*/allwinner* F: hw/arm/cubieboard.c +Allwinner-h3 +M: Niek Linnenbank +L: qemu-arm@nongnu.org +S: Maintained +F: hw/*/allwinner-h3* +F: include/hw/*/allwinner-h3* + ARM PrimeCell and CMSDK devices M: Peter Maydell L: qemu-arm@nongnu.org diff --git a/default-configs/arm-softmmu.mak b/default-configs/arm-softmmu.mak index 1f2e0e7fde..d75a239c2c 100644 --- a/default-configs/arm-softmmu.mak +++ b/default-configs/arm-softmmu.mak @@ -40,3 +40,4 @@ CONFIG_FSL_IMX25=y CONFIG_FSL_IMX7=y CONFIG_FSL_IMX6UL=y CONFIG_SEMIHOSTING=y +CONFIG_ALLWINNER_H3=y diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig index c6e7782580..ebf8d2325f 100644 --- a/hw/arm/Kconfig +++ b/hw/arm/Kconfig @@ -291,6 +291,14 @@ config ALLWINNER_A10 select SERIAL select UNIMP +config ALLWINNER_H3 + bool + select ALLWINNER_A10_PIT + select SERIAL + select ARM_TIMER + select ARM_GIC + select UNIMP + config RASPI bool select FRAMEBUFFER diff --git a/hw/arm/Makefile.objs b/hw/arm/Makefile.objs index fe749f65fd..956e496052 100644 --- a/hw/arm/Makefile.objs +++ b/hw/arm/Makefile.objs @@ -34,6 +34,7 @@ obj-$(CONFIG_DIGIC) += digic.o obj-$(CONFIG_OMAP) += omap1.o omap2.o obj-$(CONFIG_STRONGARM) += strongarm.o obj-$(CONFIG_ALLWINNER_A10) += allwinner-a10.o cubieboard.o +obj-$(CONFIG_ALLWINNER_H3) += allwinner-h3.o obj-$(CONFIG_RASPI) += bcm2835_peripherals.o bcm2836.o raspi.o obj-$(CONFIG_STM32F205_SOC) += stm32f205_soc.o obj-$(CONFIG_XLNX_ZYNQMP_ARM) += xlnx-zynqmp.o xlnx-zcu102.o diff --git a/hw/arm/allwinner-h3.c b/hw/arm/allwinner-h3.c new file mode 100644 index 0000000000..470fdfebef --- /dev/null +++ b/hw/arm/allwinner-h3.c @@ -0,0 +1,215 @@ +/* + * Allwinner H3 System on Chip emulation + * + * Copyright (C) 2019 Niek Linnenbank + * + * 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 "exec/address-spaces.h" +#include "qapi/error.h" +#include "qemu/module.h" +#include "qemu/units.h" +#include "cpu.h" +#include "hw/sysbus.h" +#include "hw/arm/allwinner-h3.h" +#include "hw/misc/unimp.h" +#include "sysemu/sysemu.h" + +static void aw_h3_init(Object *obj) +{ + AwH3State *s = AW_H3(obj); + + sysbus_init_child_obj(obj, "gic", &s->gic, sizeof(s->gic), + TYPE_ARM_GIC); + + sysbus_init_child_obj(obj, "timer", &s->timer, sizeof(s->timer), + TYPE_AW_A10_PIT); +} + +static void aw_h3_realize(DeviceState *dev, Error **errp) +{ + AwH3State *s = AW_H3(dev); + SysBusDevice *sysbusdev = NULL; + Error *err = NULL; + unsigned i = 0; + + /* CPUs */ + for (i = 0; i < AW_H3_NUM_CPUS; i++) { + Object *cpuobj = object_new(ARM_CPU_TYPE_NAME("cortex-a7")); + CPUState *cpustate = CPU(cpuobj); + + /* Set the proper CPU index */ + cpustate->cpu_index = i; + + /* Provide Power State Coordination Interface */ + object_property_set_int(cpuobj, QEMU_PSCI_CONDUIT_HVC, + "psci-conduit", &error_abort); + + /* Disable secondary CPUs */ + object_property_set_bool(cpuobj, i > 0, "start-powered-off", &err); + if (err != NULL) { + error_propagate(errp, err); + return; + } + + /* All exception levels required */ + object_property_set_bool(cpuobj, + true, "has_el3", NULL); + object_property_set_bool(cpuobj, + true, "has_el2", NULL); + + /* Mark realized */ + object_property_set_bool(cpuobj, true, "realized", &err); + if (err != NULL) { + error_propagate(errp, err); + return; + } + object_unref(cpuobj); + } + + /* Generic Interrupt Controller */ + qdev_prop_set_uint32(DEVICE(&s->gic), "num-irq", AW_H3_GIC_NUM_SPI + + GIC_INTERNAL); + qdev_prop_set_uint32(DEVICE(&s->gic), "revision", 2); + qdev_prop_set_uint32(DEVICE(&s->gic), "num-cpu", AW_H3_NUM_CPUS); + qdev_prop_set_bit(DEVICE(&s->gic), "has-security-extensions", false); + qdev_prop_set_bit(DEVICE(&s->gic), "has-virtualization-extensions", true); + + object_property_set_bool(OBJECT(&s->gic), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + + sysbusdev = SYS_BUS_DEVICE(&s->gic); + sysbus_mmio_map(sysbusdev, 0, AW_H3_GIC_DIST_BASE); + sysbus_mmio_map(sysbusdev, 1, AW_H3_GIC_CPU_BASE); + sysbus_mmio_map(sysbusdev, 2, AW_H3_GIC_HYP_BASE); + sysbus_mmio_map(sysbusdev, 3, AW_H3_GIC_VCPU_BASE); + + /* + * Wire the outputs from each CPU's generic timer and the GICv3 + * maintenance interrupt signal to the appropriate GIC PPI inputs, + * and the GIC's IRQ/FIQ/VIRQ/VFIQ interrupt outputs to the CPU's inputs. + */ + for (i = 0; i < AW_H3_NUM_CPUS; i++) { + DeviceState *cpudev = DEVICE(qemu_get_cpu(i)); + int ppibase = AW_H3_GIC_NUM_SPI + i * GIC_INTERNAL + GIC_NR_SGIS; + int irq; + /* + * Mapping from the output timer irq lines from the CPU to the + * GIC PPI inputs used for this board. + */ + const int timer_irq[] = { + [GTIMER_PHYS] = AW_H3_GIC_PPI_ARM_PHYSTIMER, + [GTIMER_VIRT] = AW_H3_GIC_PPI_ARM_VIRTTIMER, + [GTIMER_HYP] = AW_H3_GIC_PPI_ARM_HYPTIMER, + [GTIMER_SEC] = AW_H3_GIC_PPI_ARM_SECTIMER, + }; + + /* Connect CPU timer outputs to GIC PPI inputs */ + for (irq = 0; irq < ARRAY_SIZE(timer_irq); irq++) { + qdev_connect_gpio_out(cpudev, irq, + qdev_get_gpio_in(DEVICE(&s->gic), + ppibase + timer_irq[irq])); + } + + /* Connect GIC outputs to CPU interrupt inputs */ + sysbus_connect_irq(sysbusdev, i, + qdev_get_gpio_in(cpudev, ARM_CPU_IRQ)); + sysbus_connect_irq(sysbusdev, i + AW_H3_NUM_CPUS, + qdev_get_gpio_in(cpudev, ARM_CPU_FIQ)); + sysbus_connect_irq(sysbusdev, i + (2 * AW_H3_NUM_CPUS), + qdev_get_gpio_in(cpudev, ARM_CPU_VIRQ)); + sysbus_connect_irq(sysbusdev, i + (3 * AW_H3_NUM_CPUS), + qdev_get_gpio_in(cpudev, ARM_CPU_VFIQ)); + + /* GIC maintenance signal */ + sysbus_connect_irq(sysbusdev, i + (4 * AW_H3_NUM_CPUS), + qdev_get_gpio_in(DEVICE(&s->gic), + ppibase + AW_H3_GIC_PPI_MAINT)); + } + + for (i = 0; i < AW_H3_GIC_NUM_SPI; i++) { + s->irq[i] = qdev_get_gpio_in(DEVICE(&s->gic), i); + } + + /* Timer */ + object_property_set_bool(OBJECT(&s->timer), true, "realized", &err); + if (err != NULL) { + error_propagate(errp, err); + return; + } + sysbusdev = SYS_BUS_DEVICE(&s->timer); + sysbus_mmio_map(sysbusdev, 0, AW_H3_PIT_REG_BASE); + sysbus_connect_irq(sysbusdev, 0, s->irq[AW_H3_GIC_SPI_TIMER0]); + sysbus_connect_irq(sysbusdev, 1, s->irq[AW_H3_GIC_SPI_TIMER1]); + + /* SRAM */ + memory_region_init_ram(&s->sram_a1, OBJECT(dev), "sram A1", + AW_H3_SRAM_A1_SIZE, &error_fatal); + memory_region_init_ram(&s->sram_a2, OBJECT(dev), "sram A2", + AW_H3_SRAM_A2_SIZE, &error_fatal); + memory_region_init_ram(&s->sram_c, OBJECT(dev), "sram C", + AW_H3_SRAM_C_SIZE, &error_fatal); + memory_region_add_subregion(get_system_memory(), AW_H3_SRAM_A1_BASE, + &s->sram_a1); + memory_region_add_subregion(get_system_memory(), AW_H3_SRAM_A2_BASE, + &s->sram_a2); + memory_region_add_subregion(get_system_memory(), AW_H3_SRAM_C_BASE, + &s->sram_c); + + /* UART */ + if (serial_hd(0)) { + serial_mm_init(get_system_memory(), AW_H3_UART0_REG_BASE, 2, + s->irq[AW_H3_GIC_SPI_UART0], 115200, serial_hd(0), + DEVICE_NATIVE_ENDIAN); + } + + /* Unimplemented devices */ + create_unimplemented_device("display-engine", AW_H3_DE_BASE, AW_H3_DE_SIZE); + create_unimplemented_device("dma", AW_H3_DMA_BASE, AW_H3_DMA_SIZE); + create_unimplemented_device("lcd0", AW_H3_LCD0_BASE, AW_H3_LCD0_SIZE); + create_unimplemented_device("lcd1", AW_H3_LCD1_BASE, AW_H3_LCD1_SIZE); + create_unimplemented_device("gpu", AW_H3_GPU_BASE, AW_H3_GPU_SIZE); + create_unimplemented_device("hdmi", AW_H3_HDMI_BASE, AW_H3_HDMI_SIZE); + create_unimplemented_device("rtc", AW_H3_RTC_BASE, AW_H3_RTC_SIZE); + create_unimplemented_device("audio-codec", AW_H3_AC_BASE, AW_H3_AC_SIZE); +} + +static void aw_h3_class_init(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + + dc->realize = aw_h3_realize; + /* Reason: uses serial_hds and nd_table */ + dc->user_creatable = false; +} + +static const TypeInfo aw_h3_type_info = { + .name = TYPE_AW_H3, + .parent = TYPE_DEVICE, + .instance_size = sizeof(AwH3State), + .instance_init = aw_h3_init, + .class_init = aw_h3_class_init, +}; + +static void aw_h3_register_types(void) +{ + type_register_static(&aw_h3_type_info); +} + +type_init(aw_h3_register_types) diff --git a/include/hw/arm/allwinner-h3.h b/include/hw/arm/allwinner-h3.h new file mode 100644 index 0000000000..af368c2254 --- /dev/null +++ b/include/hw/arm/allwinner-h3.h @@ -0,0 +1,118 @@ +/* + * Allwinner H3 System on Chip emulation + * + * Copyright (C) 2019 Niek Linnenbank + * + * 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_H3_H +#define HW_ARM_ALLWINNER_H3_H + +#include "qemu/error-report.h" +#include "qemu/units.h" +#include "hw/char/serial.h" +#include "hw/arm/boot.h" +#include "hw/timer/allwinner-a10-pit.h" +#include "hw/intc/arm_gic.h" +#include "target/arm/cpu.h" + +#define AW_H3_SRAM_A1_BASE (0x00000000) +#define AW_H3_SRAM_A2_BASE (0x00044000) +#define AW_H3_SRAM_C_BASE (0x00010000) +#define AW_H3_DE_BASE (0x01000000) +#define AW_H3_SYSCON_BASE (0x01c00000) +#define AW_H3_DMA_BASE (0x01c02000) +#define AW_H3_LCD0_BASE (0x01c0c000) +#define AW_H3_LCD1_BASE (0x01c0d000) +#define AW_H3_SID_BASE (0x01c14000) +#define AW_H3_CCU_BASE (0x01c20000) +#define AW_H3_PIC_REG_BASE (0x01c20400) +#define AW_H3_PIT_REG_BASE (0x01c20c00) +#define AW_H3_AC_BASE (0x01c22c00) +#define AW_H3_UART0_REG_BASE (0x01c28000) +#define AW_H3_EMAC_BASE (0x01c30000) +#define AW_H3_MMC0_BASE (0x01c0f000) +#define AW_H3_EHCI0_BASE (0x01c1a000) +#define AW_H3_OHCI0_BASE (0x01c1a400) +#define AW_H3_EHCI1_BASE (0x01c1b000) +#define AW_H3_OHCI1_BASE (0x01c1b400) +#define AW_H3_EHCI2_BASE (0x01c1c000) +#define AW_H3_OHCI2_BASE (0x01c1c400) +#define AW_H3_EHCI3_BASE (0x01c1d000) +#define AW_H3_OHCI3_BASE (0x01c1d400) +#define AW_H3_GPU_BASE (0x01c40000) +#define AW_H3_GIC_DIST_BASE (0x01c81000) +#define AW_H3_GIC_CPU_BASE (0x01c82000) +#define AW_H3_GIC_HYP_BASE (0x01c84000) +#define AW_H3_GIC_VCPU_BASE (0x01c86000) +#define AW_H3_HDMI_BASE (0x01ee0000) +#define AW_H3_RTC_BASE (0x01f00000) +#define AW_H3_CPUCFG_BASE (0x01f01c00) +#define AW_H3_SDRAM_BASE (0x40000000) + +#define AW_H3_SRAM_A1_SIZE (64 * KiB) +#define AW_H3_SRAM_A2_SIZE (32 * KiB) +#define AW_H3_SRAM_C_SIZE (44 * KiB) +#define AW_H3_DE_SIZE (4 * MiB) +#define AW_H3_DMA_SIZE (4 * KiB) +#define AW_H3_LCD0_SIZE (4 * KiB) +#define AW_H3_LCD1_SIZE (4 * KiB) +#define AW_H3_GPU_SIZE (64 * KiB) +#define AW_H3_HDMI_SIZE (128 * KiB) +#define AW_H3_RTC_SIZE (1 * KiB) +#define AW_H3_AC_SIZE (2 * KiB) + +#define AW_H3_GIC_PPI_MAINT (9) +#define AW_H3_GIC_PPI_ARM_HYPTIMER (10) +#define AW_H3_GIC_PPI_ARM_VIRTTIMER (11) +#define AW_H3_GIC_PPI_ARM_SECTIMER (13) +#define AW_H3_GIC_PPI_ARM_PHYSTIMER (14) + +#define AW_H3_GIC_SPI_UART0 (0) +#define AW_H3_GIC_SPI_TIMER0 (18) +#define AW_H3_GIC_SPI_TIMER1 (19) +#define AW_H3_GIC_SPI_MMC0 (60) +#define AW_H3_GIC_SPI_MMC1 (61) +#define AW_H3_GIC_SPI_MMC2 (62) +#define AW_H3_GIC_SPI_EHCI0 (72) +#define AW_H3_GIC_SPI_OHCI0 (73) +#define AW_H3_GIC_SPI_EHCI1 (74) +#define AW_H3_GIC_SPI_OHCI1 (75) +#define AW_H3_GIC_SPI_EHCI2 (76) +#define AW_H3_GIC_SPI_OHCI2 (77) +#define AW_H3_GIC_SPI_EHCI3 (78) +#define AW_H3_GIC_SPI_OHCI3 (79) +#define AW_H3_GIC_SPI_EMAC (82) + +#define AW_H3_GIC_NUM_SPI (128) +#define AW_H3_NUM_CPUS (4) + +#define TYPE_AW_H3 "allwinner-h3" +#define AW_H3(obj) OBJECT_CHECK(AwH3State, (obj), TYPE_AW_H3) + +typedef struct AwH3State { + /*< private >*/ + DeviceState parent_obj; + /*< public >*/ + + qemu_irq irq[AW_H3_GIC_NUM_SPI]; + AwA10PITState timer; + GICState gic; + MemoryRegion sram_a1; + MemoryRegion sram_a2; + MemoryRegion sram_c; +} AwH3State; + +#endif From patchwork Mon Dec 2 21:09:39 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Niek Linnenbank X-Patchwork-Id: 11269941 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id EDEE4138D for ; Mon, 2 Dec 2019 21:42:05 +0000 (UTC) Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id C407C206F0 for ; Mon, 2 Dec 2019 21:42:05 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=fail reason="signature verification failed" (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="rgGZ9TSu" DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org C407C206F0 Authentication-Results: mail.kernel.org; dmarc=fail (p=none dis=none) header.from=gmail.com Authentication-Results: mail.kernel.org; spf=pass smtp.mailfrom=qemu-devel-bounces+patchwork-qemu-devel=patchwork.kernel.org@nongnu.org Received: from localhost ([::1]:44866 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1ibtSG-0007mr-7X for patchwork-qemu-devel@patchwork.kernel.org; Mon, 02 Dec 2019 16:42:04 -0500 Received: from eggs.gnu.org ([2001:470:142:3::10]:38242) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1ibsxc-0002e3-Qy for qemu-devel@nongnu.org; Mon, 02 Dec 2019 16:10:26 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1ibsxQ-0000h5-Q9 for qemu-devel@nongnu.org; Mon, 02 Dec 2019 16:10:19 -0500 Received: from mail-wm1-x343.google.com ([2a00:1450:4864:20::343]:51721) by eggs.gnu.org with esmtps (TLS1.0:RSA_AES_128_CBC_SHA1:16) (Exim 4.71) (envelope-from ) id 1ibsxM-0000YW-Jq; Mon, 02 Dec 2019 16:10:09 -0500 Received: by mail-wm1-x343.google.com with SMTP id g206so842231wme.1; Mon, 02 Dec 2019 13:09:59 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=AgPJ0iFJ+07Qrw6Zp/uHV5hRBVIzjEeNp0NWpzIpFCg=; b=rgGZ9TSu23Brs9CWi1uIQPcIbNmWeZcN/XhCELuSS2DL2iCh23d1NIEAaKqMbOGgOr bfwleHIBxy6v0TxJt1nLLwfFWshKFgnNr3o6glgpKWZ1oVERhdyadmzDf/BcstzKRVej iqB+EGR01Dsme50rkNuGO7i9HhFilODj08WIQH06Sw95oSu6I++jH4DDdsPdnJG7JE2Z JQwHOI2LGmgVp++FOEUV8kQBYy2PtVpUnMB1KReteWREc9M8yf7FPYEAUudrNy/xOHPH pZWSeIxeuR67xVPbdpWbX13WqEDiHcy4aPTLEof5571p7UjE43IwC2cQl2AYUIKv7+dR pgww== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=AgPJ0iFJ+07Qrw6Zp/uHV5hRBVIzjEeNp0NWpzIpFCg=; b=K1aqZI8rKogup5Jt3rCyye8u1GaWUl5yrrxtQrZgnPmVSHG9iDpSKUfKjxjyI44rKa 7HfiSfKAvPzU8191zwGWY670sk9rjaGPXn4yrliCM7T8mBYqc7Yxil5T2TnrPRPBJm6R RJCVpvO6tTPwHFKQgy6Q8PhoRlqkc9/WwNzdJiSIyIowB5jVgiEdWRdcReu4rnzpg/VR FJXD5//2KYq3nMesDWmMvVNT+x5riMaipOfF/p776rcF6NPNgQe8e5cFTGlq+vsNyuc3 vpK18lwe+WOo4Xe3Ihj2J/YPG/biEvD04YglTsY33FdXODOejIjpe1M0b/AfGmo8j2pb YcDw== X-Gm-Message-State: APjAAAXn96YHcHukpXDujDG+XrXtZrZQx8mCLUzytjvvkC9yfcUcq603 zYwgTNxZSfF8QGxZlZkUw2T+aNW9 X-Google-Smtp-Source: APXvYqw6rG3hbLOmq69AFvJQVLQvvQcyMToopuC6AXQD8ZPprAb/qasF0hNcORuNguocsSz5Yfnm9Q== X-Received: by 2002:a7b:cb0b:: with SMTP id u11mr22335109wmj.64.1575320998608; Mon, 02 Dec 2019 13:09:58 -0800 (PST) Received: from pavilion.home ([2a02:a456:6be8:1:8edc:d4ff:fe8b:18b7]) by smtp.gmail.com with ESMTPSA id f1sm770859wrp.93.2019.12.02.13.09.57 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 02 Dec 2019 13:09:58 -0800 (PST) From: Niek Linnenbank To: qemu-devel@nongnu.org Subject: [PATCH 02/10] hw: arm: add Xunlong Orange Pi PC machine Date: Mon, 2 Dec 2019 22:09:39 +0100 Message-Id: <20191202210947.3603-3-nieklinnenbank@gmail.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20191202210947.3603-1-nieklinnenbank@gmail.com> References: <20191202210947.3603-1-nieklinnenbank@gmail.com> X-detected-operating-system: by eggs.gnu.org: Genre and OS details not recognized. X-Received-From: 2a00:1450:4864:20::343 X-Mailman-Approved-At: Mon, 02 Dec 2019 16:38:22 -0500 X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: b.galvani@gmail.com, peter.maydell@linaro.org, Niek Linnenbank , qemu-arm@nongnu.org Errors-To: qemu-devel-bounces+patchwork-qemu-devel=patchwork.kernel.org@nongnu.org Sender: "Qemu-devel" The Xunlong Orange Pi PC is an Allwinner H3 System on Chip based embedded computer with mainline support in both U-Boot and Linux. The board comes with a Quad Core Cortex A7 @ 1.3GHz, 512MB RAM, 100Mbit ethernet, USB, SD/MMC, USB, HDMI and various other I/O. This commit add support for the Xunlong Orange Pi PC machine. Signed-off-by: Niek Linnenbank --- MAINTAINERS | 1 + hw/arm/Makefile.objs | 2 +- hw/arm/orangepi.c | 90 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 92 insertions(+), 1 deletion(-) create mode 100644 hw/arm/orangepi.c diff --git a/MAINTAINERS b/MAINTAINERS index 29c9936037..42c913d6cb 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -485,6 +485,7 @@ L: qemu-arm@nongnu.org S: Maintained F: hw/*/allwinner-h3* F: include/hw/*/allwinner-h3* +F: hw/arm/orangepi.c ARM PrimeCell and CMSDK devices M: Peter Maydell diff --git a/hw/arm/Makefile.objs b/hw/arm/Makefile.objs index 956e496052..8d5ea453d5 100644 --- a/hw/arm/Makefile.objs +++ b/hw/arm/Makefile.objs @@ -34,7 +34,7 @@ obj-$(CONFIG_DIGIC) += digic.o obj-$(CONFIG_OMAP) += omap1.o omap2.o obj-$(CONFIG_STRONGARM) += strongarm.o obj-$(CONFIG_ALLWINNER_A10) += allwinner-a10.o cubieboard.o -obj-$(CONFIG_ALLWINNER_H3) += allwinner-h3.o +obj-$(CONFIG_ALLWINNER_H3) += allwinner-h3.o orangepi.o obj-$(CONFIG_RASPI) += bcm2835_peripherals.o bcm2836.o raspi.o obj-$(CONFIG_STM32F205_SOC) += stm32f205_soc.o obj-$(CONFIG_XLNX_ZYNQMP_ARM) += xlnx-zynqmp.o xlnx-zcu102.o diff --git a/hw/arm/orangepi.c b/hw/arm/orangepi.c new file mode 100644 index 0000000000..5ef2735f81 --- /dev/null +++ b/hw/arm/orangepi.c @@ -0,0 +1,90 @@ +/* + * Orange Pi emulation + * + * Copyright (C) 2019 Niek Linnenbank + * + * 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 "exec/address-spaces.h" +#include "qapi/error.h" +#include "cpu.h" +#include "hw/sysbus.h" +#include "hw/boards.h" +#include "hw/qdev-properties.h" +#include "hw/arm/allwinner-h3.h" + +static struct arm_boot_info orangepi_binfo = { + .loader_start = AW_H3_SDRAM_BASE, + .board_id = -1, +}; + +typedef struct OrangePiState { + AwH3State *h3; + MemoryRegion sdram; +} OrangePiState; + +static void orangepi_init(MachineState *machine) +{ + OrangePiState *s = g_new(OrangePiState, 1); + Error *err = NULL; + + s->h3 = AW_H3(object_new(TYPE_AW_H3)); + + /* Setup timer properties */ + object_property_set_int(OBJECT(&s->h3->timer), 32768, "clk0-freq", &err); + if (err != NULL) { + error_reportf_err(err, "Couldn't set clk0 frequency: "); + exit(1); + } + + object_property_set_int(OBJECT(&s->h3->timer), 24000000, "clk1-freq", + &err); + if (err != NULL) { + error_reportf_err(err, "Couldn't set clk1 frequency: "); + exit(1); + } + + /* Mark H3 object realized */ + object_property_set_bool(OBJECT(s->h3), true, "realized", &err); + if (err != NULL) { + error_reportf_err(err, "Couldn't realize Allwinner H3: "); + exit(1); + } + + /* RAM */ + memory_region_allocate_system_memory(&s->sdram, NULL, "orangepi.ram", + machine->ram_size); + memory_region_add_subregion(get_system_memory(), AW_H3_SDRAM_BASE, + &s->sdram); + + /* Load target kernel */ + orangepi_binfo.ram_size = machine->ram_size; + orangepi_binfo.nb_cpus = AW_H3_NUM_CPUS; + arm_load_kernel(ARM_CPU(first_cpu), machine, &orangepi_binfo); +} + +static void orangepi_machine_init(MachineClass *mc) +{ + mc->desc = "Orange Pi PC"; + mc->init = orangepi_init; + mc->units_per_default_bus = 1; + mc->min_cpus = AW_H3_NUM_CPUS; + mc->max_cpus = AW_H3_NUM_CPUS; + mc->default_cpus = AW_H3_NUM_CPUS; + mc->ignore_memory_transaction_failures = true; +} + +DEFINE_MACHINE("orangepi", orangepi_machine_init) From patchwork Mon Dec 2 21:09:40 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Niek Linnenbank X-Patchwork-Id: 11269951 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 651E6138D for ; Mon, 2 Dec 2019 21:46:41 +0000 (UTC) Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id 2B54120705 for ; Mon, 2 Dec 2019 21:46:41 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=fail reason="signature verification failed" (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="nrfGIqVq" DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org 2B54120705 Authentication-Results: mail.kernel.org; dmarc=fail (p=none dis=none) header.from=gmail.com Authentication-Results: mail.kernel.org; spf=pass smtp.mailfrom=qemu-devel-bounces+patchwork-qemu-devel=patchwork.kernel.org@nongnu.org Received: from localhost ([::1]:44910 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1ibtWh-00033B-UO for patchwork-qemu-devel@patchwork.kernel.org; Mon, 02 Dec 2019 16:46:40 -0500 Received: from eggs.gnu.org ([2001:470:142:3::10]:38175) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1ibsxR-0002b7-C4 for qemu-devel@nongnu.org; Mon, 02 Dec 2019 16:10:16 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1ibsxN-0000cO-IP for qemu-devel@nongnu.org; Mon, 02 Dec 2019 16:10:12 -0500 Received: from mail-wm1-x344.google.com ([2a00:1450:4864:20::344]:51722) by eggs.gnu.org with esmtps (TLS1.0:RSA_AES_128_CBC_SHA1:16) (Exim 4.71) (envelope-from ) id 1ibsxK-0000Yq-Fi; Mon, 02 Dec 2019 16:10:06 -0500 Received: by mail-wm1-x344.google.com with SMTP id g206so842287wme.1; Mon, 02 Dec 2019 13:10:01 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=h+Tla/hLm55CHAFtteEGlDuBFiTlLj9DvIB7CMAPe0U=; b=nrfGIqVqRbt7KSSVeD1gS+X4WPnrIyzuiRlPn3nEaqGL417jGLEWJZDYs9/6XbSpLF 5qB+iykwbGI4Y1wFOrjxWC1ELQWSXBhnYI33igURPxPI7CpybY/jV/L7nFbIJzNF3nlv 9BqNCEZ4vlhMwgRzwtABSDJ/5IoRGlEO54niMC0t634UvtrcMG7G1K1KC32otlIBveja 7VoB2smZ7SBVqXBP+uPM4Ay86xvq56uljkKe53yK6QI+NPaPYn33QTqlSO1uI3/ZG3b3 n7DmYkieRYnhx4Qa9cVA+foV+KYWDh4tGPEVxow6q3BfLQl2VIVpR2WtfALaPnUq8QWO sasA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=h+Tla/hLm55CHAFtteEGlDuBFiTlLj9DvIB7CMAPe0U=; b=JMQ0ubyAIoLl/whuu8u0hMt9H9W/ez544SRhTXw/JGxjBleTQEvYUAIX19BK66gKZ+ 1vegkSQDMpOiZViNoGX3No0iNI7VwpLbfWHCQ0wVo2TlczIgd+ynPAVk484SZ7SJIniF 1UF8OtlkmAuzEuFXYiD6pLn0Q57Dsdy/jluTRJkIqvcc/emW45kSj3fqNFVuW1w+gfJH xEqUe80aP44kROrijwTXI8MOlUnS+gAgPsPqc9SuJ1kcWQaLSj8RUuumeJ7bC4r6f4cE w1JQsnZTNpBAZiPCfBR0/jFWNKbNxkW6iXTFsJqwGhCJnFM07d7hdf/88IUiHGZgvMUY f8sQ== X-Gm-Message-State: APjAAAUXJt5r5f4Wf5KrnDLGnK2Oq1DSgjHWTPCC8VcCcLvS4ZgYtECN QAZcxQXZ5DBlXvp75mpbv/v08znf X-Google-Smtp-Source: APXvYqw19bYDiNd1ZTbVxqeeSMuDEmV5T0DCFA0fHOto5GoVwwhCpN0VpgyUmURETx8spDwc4DBYJg== X-Received: by 2002:a7b:cb87:: with SMTP id m7mr9521215wmi.148.1575320999594; Mon, 02 Dec 2019 13:09:59 -0800 (PST) Received: from pavilion.home ([2a02:a456:6be8:1:8edc:d4ff:fe8b:18b7]) by smtp.gmail.com with ESMTPSA id f1sm770859wrp.93.2019.12.02.13.09.58 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 02 Dec 2019 13:09:58 -0800 (PST) From: Niek Linnenbank To: qemu-devel@nongnu.org Subject: [PATCH 03/10] arm: allwinner-h3: add Clock Control Unit Date: Mon, 2 Dec 2019 22:09:40 +0100 Message-Id: <20191202210947.3603-4-nieklinnenbank@gmail.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20191202210947.3603-1-nieklinnenbank@gmail.com> References: <20191202210947.3603-1-nieklinnenbank@gmail.com> X-detected-operating-system: by eggs.gnu.org: Genre and OS details not recognized. X-Received-From: 2a00:1450:4864:20::344 X-Mailman-Approved-At: Mon, 02 Dec 2019 16:38:21 -0500 X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: b.galvani@gmail.com, peter.maydell@linaro.org, Niek Linnenbank , qemu-arm@nongnu.org Errors-To: qemu-devel-bounces+patchwork-qemu-devel=patchwork.kernel.org@nongnu.org Sender: "Qemu-devel" The Clock Control Unit is responsible for clock signal generation, configuration and distribution in the Allwinner H3 System on Chip. This commit adds support for the Clock Control Unit which emulates a simple read/write register interface. Signed-off-by: Niek Linnenbank --- hw/arm/allwinner-h3.c | 11 ++ hw/misc/Makefile.objs | 1 + hw/misc/allwinner-h3-clk.c | 227 +++++++++++++++++++++++++++++ include/hw/arm/allwinner-h3.h | 2 + include/hw/misc/allwinner-h3-clk.h | 41 ++++++ 5 files changed, 282 insertions(+) create mode 100644 hw/misc/allwinner-h3-clk.c create mode 100644 include/hw/misc/allwinner-h3-clk.h diff --git a/hw/arm/allwinner-h3.c b/hw/arm/allwinner-h3.c index 470fdfebef..5566e979ec 100644 --- a/hw/arm/allwinner-h3.c +++ b/hw/arm/allwinner-h3.c @@ -37,6 +37,9 @@ static void aw_h3_init(Object *obj) sysbus_init_child_obj(obj, "timer", &s->timer, sizeof(s->timer), TYPE_AW_A10_PIT); + + sysbus_init_child_obj(obj, "ccu", &s->ccu, sizeof(s->ccu), + TYPE_AW_H3_CLK); } static void aw_h3_realize(DeviceState *dev, Error **errp) @@ -172,6 +175,14 @@ static void aw_h3_realize(DeviceState *dev, Error **errp) memory_region_add_subregion(get_system_memory(), AW_H3_SRAM_C_BASE, &s->sram_c); + /* Clock Control Unit */ + object_property_set_bool(OBJECT(&s->ccu), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + sysbus_mmio_map(SYS_BUS_DEVICE(&s->ccu), 0, AW_H3_CCU_BASE); + /* UART */ if (serial_hd(0)) { serial_mm_init(get_system_memory(), AW_H3_UART0_REG_BASE, 2, diff --git a/hw/misc/Makefile.objs b/hw/misc/Makefile.objs index ba898a5781..200ed44ce1 100644 --- a/hw/misc/Makefile.objs +++ b/hw/misc/Makefile.objs @@ -28,6 +28,7 @@ common-obj-$(CONFIG_MACIO) += macio/ common-obj-$(CONFIG_IVSHMEM_DEVICE) += ivshmem.o +common-obj-$(CONFIG_ALLWINNER_H3) += allwinner-h3-clk.o common-obj-$(CONFIG_REALVIEW) += arm_sysctl.o common-obj-$(CONFIG_NSERIES) += cbus.o common-obj-$(CONFIG_ECCMEMCTL) += eccmemctl.o diff --git a/hw/misc/allwinner-h3-clk.c b/hw/misc/allwinner-h3-clk.c new file mode 100644 index 0000000000..77c55b4f92 --- /dev/null +++ b/hw/misc/allwinner-h3-clk.c @@ -0,0 +1,227 @@ +/* + * Allwinner H3 Clock Control Unit emulation + * + * Copyright (C) 2019 Niek Linnenbank + * + * 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 "hw/sysbus.h" +#include "migration/vmstate.h" +#include "qemu/log.h" +#include "qemu/module.h" +#include "hw/misc/allwinner-h3-clk.h" + +/* CCU register offsets */ +#define REG_PLL_CPUX (0x0000) /* PLL CPUX Control */ +#define REG_PLL_AUDIO (0x0008) /* PLL Audio Control */ +#define REG_PLL_VIDEO (0x0010) /* PLL Video Control */ +#define REG_PLL_VE (0x0018) /* PLL VE Control */ +#define REG_PLL_DDR (0x0020) /* PLL DDR Control */ +#define REG_PLL_PERIPH0 (0x0028) /* PLL Peripherals 0 Control */ +#define REG_PLL_GPU (0x0038) /* PLL GPU Control */ +#define REG_PLL_PERIPH1 (0x0044) /* PLL Peripherals 1 Control */ +#define REG_PLL_DE (0x0048) /* PLL Display Engine Control */ +#define REG_CPUX_AXI (0x0050) /* CPUX/AXI Configuration */ +#define REG_APB1 (0x0054) /* ARM Peripheral Bus 1 Config */ +#define REG_APB2 (0x0058) /* ARM Peripheral Bus 2 Config */ +#define REG_MBUS (0x00FC) /* MBUS Reset */ +#define REG_PLL_TIME0 (0x0200) /* PLL Stable Time 0 */ +#define REG_PLL_TIME1 (0x0204) /* PLL Stable Time 1 */ +#define REG_PLL_CPUX_BIAS (0x0220) /* PLL CPUX Bias */ +#define REG_PLL_AUDIO_BIAS (0x0224) /* PLL Audio Bias */ +#define REG_PLL_VIDEO_BIAS (0x0228) /* PLL Video Bias */ +#define REG_PLL_VE_BIAS (0x022C) /* PLL VE Bias */ +#define REG_PLL_DDR_BIAS (0x0230) /* PLL DDR Bias */ +#define REG_PLL_PERIPH0_BIAS (0x0234) /* PLL Peripherals 0 Bias */ +#define REG_PLL_GPU_BIAS (0x023C) /* PLL GPU Bias */ +#define REG_PLL_PERIPH1_BIAS (0x0244) /* PLL Peripherals 1 Bias */ +#define REG_PLL_DE_BIAS (0x0248) /* PLL Display Engine Bias */ +#define REG_PLL_CPUX_TUNING (0x0250) /* PLL CPUX Tuning */ +#define REG_PLL_DDR_TUNING (0x0260) /* PLL DDR Tuning */ +#define REG_INDEX(offset) (offset / sizeof(uint32_t)) + +/* CCU register flags */ +#define REG_PLL_ENABLE (1 << 31) +#define REG_PLL_LOCK (1 << 28) + +/* CCU register reset values */ +#define REG_PLL_CPUX_RST (0x00001000) +#define REG_PLL_AUDIO_RST (0x00035514) +#define REG_PLL_VIDEO_RST (0x03006207) +#define REG_PLL_VE_RST (0x03006207) +#define REG_PLL_DDR_RST (0x00001000) +#define REG_PLL_PERIPH0_RST (0x00041811) +#define REG_PLL_GPU_RST (0x03006207) +#define REG_PLL_PERIPH1_RST (0x00041811) +#define REG_PLL_DE_RST (0x03006207) +#define REG_CPUX_AXI_RST (0x00010000) +#define REG_APB1_RST (0x00001010) +#define REG_APB2_RST (0x01000000) +#define REG_MBUS_RST (0x80000000) +#define REG_PLL_TIME0_RST (0x000000FF) +#define REG_PLL_TIME1_RST (0x000000FF) +#define REG_PLL_CPUX_BIAS_RST (0x08100200) +#define REG_PLL_AUDIO_BIAS_RST (0x10100000) +#define REG_PLL_VIDEO_BIAS_RST (0x10100000) +#define REG_PLL_VE_BIAS_RST (0x10100000) +#define REG_PLL_DDR_BIAS_RST (0x81104000) +#define REG_PLL_PERIPH0_BIAS_RST (0x10100010) +#define REG_PLL_GPU_BIAS_RST (0x10100000) +#define REG_PLL_PERIPH1_BIAS_RST (0x10100010) +#define REG_PLL_DE_BIAS_RST (0x10100000) +#define REG_PLL_CPUX_TUNING_RST (0x0A101000) +#define REG_PLL_DDR_TUNING_RST (0x14880000) + +static uint64_t allwinner_h3_clk_read(void *opaque, hwaddr offset, + unsigned size) +{ + const AwH3ClockState *s = (AwH3ClockState *)opaque; + const uint32_t idx = REG_INDEX(offset); + + if (idx >= AW_H3_CLK_REGS_NUM) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: bad read offset 0x%04x\n", + __func__, (uint32_t)offset); + return 0; + } + + return s->regs[idx]; +} + +static void allwinner_h3_clk_write(void *opaque, hwaddr offset, + uint64_t val, unsigned size) +{ + AwH3ClockState *s = (AwH3ClockState *)opaque; + const uint32_t idx = REG_INDEX(offset); + + if (idx >= AW_H3_CLK_REGS_NUM) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: bad write offset 0x%04x\n", + __func__, (uint32_t)offset); + return; + } + + switch (offset) { + case REG_PLL_CPUX: /* PLL CPUX Control */ + case REG_PLL_AUDIO: /* PLL Audio Control */ + case REG_PLL_VIDEO: /* PLL Video Control */ + case REG_PLL_VE: /* PLL VE Control */ + case REG_PLL_DDR: /* PLL DDR Control */ + case REG_PLL_PERIPH0: /* PLL Peripherals 0 Control */ + case REG_PLL_GPU: /* PLL GPU Control */ + case REG_PLL_PERIPH1: /* PLL Peripherals 1 Control */ + case REG_PLL_DE: /* PLL Display Engine Control */ + if (val & REG_PLL_ENABLE) { + val |= REG_PLL_LOCK; + } + break; + default: + break; + } + + s->regs[idx] = (uint32_t) val; +} + +static const MemoryRegionOps allwinner_h3_clk_ops = { + .read = allwinner_h3_clk_read, + .write = allwinner_h3_clk_write, + .endianness = DEVICE_NATIVE_ENDIAN, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + .unaligned = false + } +}; + +static void allwinner_h3_clk_reset(DeviceState *dev) +{ + AwH3ClockState *s = AW_H3_CLK(dev); + + /* Set default values for registers */ + s->regs[REG_INDEX(REG_PLL_CPUX)] = REG_PLL_CPUX_RST; + s->regs[REG_INDEX(REG_PLL_AUDIO)] = REG_PLL_AUDIO_RST; + s->regs[REG_INDEX(REG_PLL_VIDEO)] = REG_PLL_VIDEO_RST; + s->regs[REG_INDEX(REG_PLL_VE)] = REG_PLL_VE_RST; + s->regs[REG_INDEX(REG_PLL_DDR)] = REG_PLL_DDR_RST; + s->regs[REG_INDEX(REG_PLL_PERIPH0)] = REG_PLL_PERIPH0_RST; + s->regs[REG_INDEX(REG_PLL_GPU)] = REG_PLL_GPU_RST; + s->regs[REG_INDEX(REG_PLL_PERIPH1)] = REG_PLL_PERIPH1_RST; + s->regs[REG_INDEX(REG_PLL_DE)] = REG_PLL_DE_RST; + s->regs[REG_INDEX(REG_CPUX_AXI)] = REG_CPUX_AXI_RST; + s->regs[REG_INDEX(REG_APB1)] = REG_APB1_RST; + s->regs[REG_INDEX(REG_APB2)] = REG_APB2_RST; + s->regs[REG_INDEX(REG_MBUS)] = REG_MBUS_RST; + s->regs[REG_INDEX(REG_PLL_TIME0)] = REG_PLL_TIME0_RST; + s->regs[REG_INDEX(REG_PLL_TIME1)] = REG_PLL_TIME1_RST; + s->regs[REG_INDEX(REG_PLL_CPUX_BIAS)] = REG_PLL_CPUX_BIAS_RST; + s->regs[REG_INDEX(REG_PLL_AUDIO_BIAS)] = REG_PLL_AUDIO_BIAS_RST; + s->regs[REG_INDEX(REG_PLL_VIDEO_BIAS)] = REG_PLL_VIDEO_BIAS_RST; + s->regs[REG_INDEX(REG_PLL_VE_BIAS)] = REG_PLL_VE_BIAS_RST; + s->regs[REG_INDEX(REG_PLL_DDR_BIAS)] = REG_PLL_DDR_BIAS_RST; + s->regs[REG_INDEX(REG_PLL_PERIPH0_BIAS)] = REG_PLL_PERIPH0_BIAS_RST; + s->regs[REG_INDEX(REG_PLL_GPU_BIAS)] = REG_PLL_GPU_BIAS_RST; + s->regs[REG_INDEX(REG_PLL_PERIPH1_BIAS)] = REG_PLL_PERIPH1_BIAS_RST; + s->regs[REG_INDEX(REG_PLL_DE_BIAS)] = REG_PLL_DE_BIAS_RST; + s->regs[REG_INDEX(REG_PLL_CPUX_TUNING)] = REG_PLL_CPUX_TUNING_RST; + s->regs[REG_INDEX(REG_PLL_DDR_TUNING)] = REG_PLL_DDR_TUNING_RST; +} + +static void allwinner_h3_clk_realize(DeviceState *dev, Error **errp) +{ +} + +static void allwinner_h3_clk_init(Object *obj) +{ + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); + AwH3ClockState *s = AW_H3_CLK(obj); + + /* Memory mapping */ + memory_region_init_io(&s->iomem, OBJECT(s), &allwinner_h3_clk_ops, s, + TYPE_AW_H3_CLK, AW_H3_CLK_REGS_MEM_SIZE); + sysbus_init_mmio(sbd, &s->iomem); +} + +static const VMStateDescription allwinner_h3_clk_vmstate = { + .name = TYPE_AW_H3_CLK, + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32_ARRAY(regs, AwH3ClockState, AW_H3_CLK_REGS_NUM), + VMSTATE_END_OF_LIST() + } +}; + +static void allwinner_h3_clk_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->reset = allwinner_h3_clk_reset; + dc->realize = allwinner_h3_clk_realize; + dc->vmsd = &allwinner_h3_clk_vmstate; +} + +static const TypeInfo allwinner_h3_clk_info = { + .name = TYPE_AW_H3_CLK, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_init = allwinner_h3_clk_init, + .instance_size = sizeof(AwH3ClockState), + .class_init = allwinner_h3_clk_class_init, +}; + +static void allwinner_h3_clk_register(void) +{ + type_register_static(&allwinner_h3_clk_info); +} + +type_init(allwinner_h3_clk_register) diff --git a/include/hw/arm/allwinner-h3.h b/include/hw/arm/allwinner-h3.h index af368c2254..e596516c5c 100644 --- a/include/hw/arm/allwinner-h3.h +++ b/include/hw/arm/allwinner-h3.h @@ -26,6 +26,7 @@ #include "hw/arm/boot.h" #include "hw/timer/allwinner-a10-pit.h" #include "hw/intc/arm_gic.h" +#include "hw/misc/allwinner-h3-clk.h" #include "target/arm/cpu.h" #define AW_H3_SRAM_A1_BASE (0x00000000) @@ -109,6 +110,7 @@ typedef struct AwH3State { qemu_irq irq[AW_H3_GIC_NUM_SPI]; AwA10PITState timer; + AwH3ClockState ccu; GICState gic; MemoryRegion sram_a1; MemoryRegion sram_a2; diff --git a/include/hw/misc/allwinner-h3-clk.h b/include/hw/misc/allwinner-h3-clk.h new file mode 100644 index 0000000000..69ea559db1 --- /dev/null +++ b/include/hw/misc/allwinner-h3-clk.h @@ -0,0 +1,41 @@ +/* + * Allwinner H3 Clock Control Unit emulation + * + * Copyright (C) 2019 Niek Linnenbank + * + * 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_H3_CLK_H +#define HW_MISC_ALLWINNER_H3_CLK_H + +#include "hw/sysbus.h" + +#define AW_H3_CLK_REGS_MAX_ADDR (0x304) +#define AW_H3_CLK_REGS_NUM (AW_H3_CLK_REGS_MAX_ADDR / sizeof(uint32_t)) +#define AW_H3_CLK_REGS_MEM_SIZE (1024) + +#define TYPE_AW_H3_CLK "allwinner-h3-clk" +#define AW_H3_CLK(obj) OBJECT_CHECK(AwH3ClockState, (obj), TYPE_AW_H3_CLK) + +typedef struct AwH3ClockState { + /*< private >*/ + SysBusDevice parent_obj; + /*< public >*/ + + MemoryRegion iomem; + uint32_t regs[AW_H3_CLK_REGS_NUM]; +} AwH3ClockState; + +#endif From patchwork Mon Dec 2 21:09:41 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Niek Linnenbank X-Patchwork-Id: 11269957 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id BFE37138D for ; Mon, 2 Dec 2019 21:51:41 +0000 (UTC) Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id 9471A206E0 for ; Mon, 2 Dec 2019 21:51:41 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=fail reason="signature verification failed" (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="Zzf9Oofc" DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org 9471A206E0 Authentication-Results: mail.kernel.org; dmarc=fail (p=none dis=none) header.from=gmail.com Authentication-Results: mail.kernel.org; spf=pass smtp.mailfrom=qemu-devel-bounces+patchwork-qemu-devel=patchwork.kernel.org@nongnu.org Received: from localhost ([::1]:44961 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1ibtbY-000746-PG for patchwork-qemu-devel@patchwork.kernel.org; Mon, 02 Dec 2019 16:51:40 -0500 Received: from eggs.gnu.org ([2001:470:142:3::10]:38243) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1ibsxc-0002e4-R8 for qemu-devel@nongnu.org; Mon, 02 Dec 2019 16:10:26 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1ibsxQ-0000h7-QA for qemu-devel@nongnu.org; Mon, 02 Dec 2019 16:10:19 -0500 Received: from mail-wm1-x343.google.com ([2a00:1450:4864:20::343]:38631) by eggs.gnu.org with esmtps (TLS1.0:RSA_AES_128_CBC_SHA1:16) (Exim 4.71) (envelope-from ) id 1ibsxM-0000Yv-M5; Mon, 02 Dec 2019 16:10:09 -0500 Received: by mail-wm1-x343.google.com with SMTP id p17so1104368wmi.3; Mon, 02 Dec 2019 13:10:01 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=KLiQATngYVxKEhubgojMgx0rCFF3IHtRu91h6RNCkKo=; b=Zzf9OofcZJUPgAFukp7W3plQeSrCvtZlB3w08FQWUlrvc1cOJJKd/V9lfWDnvp/uvB DU4nmVsjqPdvbdW30q4RQwg386IMMyvgbSpZqqCaeouPfeRmaZWQQHEaOxa2frHitFE5 may7cKBTmnAu38A6Xp2ckW/dpHBWoRxtfEKwoNpIuBn+4duzdvgeiIRjjiMvhMHnCCl1 5Uaf53f6a9iyXnzff8IwqUanYIcGePlR8FHTm8rtBlNdWcRyMJhDIHBea1PGTmw4NxKK JH6ANxMWIS9yltWaRz/v4lal6C2zIktWISXu9fTbmdBxnIjwWdNK0R66Du5IG0M6DgrR aKhw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=KLiQATngYVxKEhubgojMgx0rCFF3IHtRu91h6RNCkKo=; b=j093eMlVXD5wl+sgDiYn6qaf9VWAMIacJdNtuqHaqYflM/XRVlQkkgV3v4/zzhlOju nEnpZ/pVQalKea3X4vHixlmo5io5gO2hn1tWhwVtgat05RUAOWw1xzoCy1kvlN3CkkTp Oc7nPnmo+hoOI6ywtzZBy5PtAhssuzuNUyECzqZLSo+Pfi8OzCre9ccBt1T297dLcQpy P20fSJ3C1nXpxO3ewDEOyU1yIaCaWWQnuK3+sIZH41O81Qzdwo538XWD6FDg0uKcPO9r 7UUBD4E8dlnwwScjx5dEkiKMPrWs8UToglzr1jsguGf3f/oLTcmmB6RnhtbUOrmzvJm4 Vxbg== X-Gm-Message-State: APjAAAUgnkGsGTodg9eIAVkjx6UolJT6P00MlVxFwD95X5Sd+Mrq5qvR 0xzHzugb39cHppMYIGhuKB4Ke0Rs X-Google-Smtp-Source: APXvYqzsCs/54zu5WwvX5AIRGqqFYZ/xh6JgPfmACi7UrEIyvtFQ2f81q5C4DBUfolCRWIjdzP6UGg== X-Received: by 2002:a1c:f610:: with SMTP id w16mr12606830wmc.34.1575321000343; Mon, 02 Dec 2019 13:10:00 -0800 (PST) Received: from pavilion.home ([2a02:a456:6be8:1:8edc:d4ff:fe8b:18b7]) by smtp.gmail.com with ESMTPSA id f1sm770859wrp.93.2019.12.02.13.09.59 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 02 Dec 2019 13:09:59 -0800 (PST) From: Niek Linnenbank To: qemu-devel@nongnu.org Subject: [PATCH 04/10] arm: allwinner-h3: add USB host controller Date: Mon, 2 Dec 2019 22:09:41 +0100 Message-Id: <20191202210947.3603-5-nieklinnenbank@gmail.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20191202210947.3603-1-nieklinnenbank@gmail.com> References: <20191202210947.3603-1-nieklinnenbank@gmail.com> X-detected-operating-system: by eggs.gnu.org: Genre and OS details not recognized. X-Received-From: 2a00:1450:4864:20::343 X-Mailman-Approved-At: Mon, 02 Dec 2019 16:38:22 -0500 X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: b.galvani@gmail.com, peter.maydell@linaro.org, Niek Linnenbank , qemu-arm@nongnu.org Errors-To: qemu-devel-bounces+patchwork-qemu-devel=patchwork.kernel.org@nongnu.org Sender: "Qemu-devel" The Allwinner H3 System on Chip contains multiple USB 2.0 bus connections which provide software access using the Enhanced Host Controller Interface (EHCI) and Open Host Controller Interface (OHCI) interfaces. This commit adds support for both interfaces in the Allwinner H3 System on Chip. Signed-off-by: Niek Linnenbank Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Gerd Hoffmann --- hw/arm/allwinner-h3.c | 20 ++++++++++++++++++++ hw/usb/hcd-ehci-sysbus.c | 17 +++++++++++++++++ hw/usb/hcd-ehci.h | 1 + 3 files changed, 38 insertions(+) diff --git a/hw/arm/allwinner-h3.c b/hw/arm/allwinner-h3.c index 5566e979ec..afeb49c0ac 100644 --- a/hw/arm/allwinner-h3.c +++ b/hw/arm/allwinner-h3.c @@ -26,6 +26,7 @@ #include "hw/sysbus.h" #include "hw/arm/allwinner-h3.h" #include "hw/misc/unimp.h" +#include "hw/usb/hcd-ehci.h" #include "sysemu/sysemu.h" static void aw_h3_init(Object *obj) @@ -183,6 +184,25 @@ static void aw_h3_realize(DeviceState *dev, Error **errp) } sysbus_mmio_map(SYS_BUS_DEVICE(&s->ccu), 0, AW_H3_CCU_BASE); + /* Universal Serial Bus */ + sysbus_create_simple(TYPE_AW_H3_EHCI, AW_H3_EHCI0_BASE, + s->irq[AW_H3_GIC_SPI_EHCI0]); + sysbus_create_simple(TYPE_AW_H3_EHCI, AW_H3_EHCI1_BASE, + s->irq[AW_H3_GIC_SPI_EHCI1]); + sysbus_create_simple(TYPE_AW_H3_EHCI, AW_H3_EHCI2_BASE, + s->irq[AW_H3_GIC_SPI_EHCI2]); + sysbus_create_simple(TYPE_AW_H3_EHCI, AW_H3_EHCI3_BASE, + s->irq[AW_H3_GIC_SPI_EHCI3]); + + sysbus_create_simple("sysbus-ohci", AW_H3_OHCI0_BASE, + s->irq[AW_H3_GIC_SPI_OHCI0]); + sysbus_create_simple("sysbus-ohci", AW_H3_OHCI1_BASE, + s->irq[AW_H3_GIC_SPI_OHCI1]); + sysbus_create_simple("sysbus-ohci", AW_H3_OHCI2_BASE, + s->irq[AW_H3_GIC_SPI_OHCI2]); + sysbus_create_simple("sysbus-ohci", AW_H3_OHCI3_BASE, + s->irq[AW_H3_GIC_SPI_OHCI3]); + /* UART */ if (serial_hd(0)) { serial_mm_init(get_system_memory(), AW_H3_UART0_REG_BASE, 2, diff --git a/hw/usb/hcd-ehci-sysbus.c b/hw/usb/hcd-ehci-sysbus.c index 020211fd10..174c3446ef 100644 --- a/hw/usb/hcd-ehci-sysbus.c +++ b/hw/usb/hcd-ehci-sysbus.c @@ -145,6 +145,22 @@ static const TypeInfo ehci_exynos4210_type_info = { .class_init = ehci_exynos4210_class_init, }; +static void ehci_aw_h3_class_init(ObjectClass *oc, void *data) +{ + SysBusEHCIClass *sec = SYS_BUS_EHCI_CLASS(oc); + DeviceClass *dc = DEVICE_CLASS(oc); + + sec->capsbase = 0x0; + sec->opregbase = 0x10; + set_bit(DEVICE_CATEGORY_USB, dc->categories); +} + +static const TypeInfo ehci_aw_h3_type_info = { + .name = TYPE_AW_H3_EHCI, + .parent = TYPE_SYS_BUS_EHCI, + .class_init = ehci_aw_h3_class_init, +}; + static void ehci_tegra2_class_init(ObjectClass *oc, void *data) { SysBusEHCIClass *sec = SYS_BUS_EHCI_CLASS(oc); @@ -267,6 +283,7 @@ static void ehci_sysbus_register_types(void) type_register_static(&ehci_platform_type_info); type_register_static(&ehci_xlnx_type_info); type_register_static(&ehci_exynos4210_type_info); + type_register_static(&ehci_aw_h3_type_info); type_register_static(&ehci_tegra2_type_info); type_register_static(&ehci_ppc4xx_type_info); type_register_static(&ehci_fusbh200_type_info); diff --git a/hw/usb/hcd-ehci.h b/hw/usb/hcd-ehci.h index 0298238f0b..edb59311c4 100644 --- a/hw/usb/hcd-ehci.h +++ b/hw/usb/hcd-ehci.h @@ -342,6 +342,7 @@ typedef struct EHCIPCIState { #define TYPE_SYS_BUS_EHCI "sysbus-ehci-usb" #define TYPE_PLATFORM_EHCI "platform-ehci-usb" #define TYPE_EXYNOS4210_EHCI "exynos4210-ehci-usb" +#define TYPE_AW_H3_EHCI "aw-h3-ehci-usb" #define TYPE_TEGRA2_EHCI "tegra2-ehci-usb" #define TYPE_PPC4xx_EHCI "ppc4xx-ehci-usb" #define TYPE_FUSBH200_EHCI "fusbh200-ehci-usb" From patchwork Mon Dec 2 21:09:42 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Niek Linnenbank X-Patchwork-Id: 11269967 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id E966E17EF for ; Mon, 2 Dec 2019 21:57:25 +0000 (UTC) Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id AD9D32071E for ; Mon, 2 Dec 2019 21:57:25 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=fail reason="signature verification failed" (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="gCiXGfOn" DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org AD9D32071E Authentication-Results: mail.kernel.org; dmarc=fail (p=none dis=none) header.from=gmail.com Authentication-Results: mail.kernel.org; spf=pass smtp.mailfrom=qemu-devel-bounces+patchwork-qemu-devel=patchwork.kernel.org@nongnu.org Received: from localhost ([::1]:45014 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1ibth6-0002y6-Qy for patchwork-qemu-devel@patchwork.kernel.org; Mon, 02 Dec 2019 16:57:24 -0500 Received: from eggs.gnu.org ([2001:470:142:3::10]:38174) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1ibsxR-0002b6-Bu for qemu-devel@nongnu.org; Mon, 02 Dec 2019 16:10:23 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1ibsxN-0000cD-HS for qemu-devel@nongnu.org; Mon, 02 Dec 2019 16:10:12 -0500 Received: from mail-wm1-x341.google.com ([2a00:1450:4864:20::341]:38630) by eggs.gnu.org with esmtps (TLS1.0:RSA_AES_128_CBC_SHA1:16) (Exim 4.71) (envelope-from ) id 1ibsxK-0000Z6-MT; Mon, 02 Dec 2019 16:10:06 -0500 Received: by mail-wm1-x341.google.com with SMTP id p17so1104421wmi.3; Mon, 02 Dec 2019 13:10:02 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=83StbJO9vc/PtHWWasIgmXL+LHo+Q8gv4+RVzsR74VQ=; b=gCiXGfOnKPgloIuyHuMaCPhD8S9iAMudxA9AQQt/u5v4kMTdj4vptn1OEHUBcKCCHQ csbBJqPb02EJBK4Uy433GrSxvySd0Cf1d7YqWPz7DzewkEQeBxkAIr+z27hFmPwcRfWn hg7doU9HUzUPHWUdhb4wwijTF7xp/1ugQuZ8rjz6A26yu8Cok860KBsd52bKoTu6w3oQ LdDdSzS7zCPpNQLHD0icoAlVEaTukdiiRW1VDkdxvgXoUBbctfM8cIpMfmlAtTKGTcQY DUa+yeH2V1y0Tw4azB0rArbR9zuafu+0xotqNxk15Z4iyV+l1CeYI34OfpSneJlsOYSS FrYA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=83StbJO9vc/PtHWWasIgmXL+LHo+Q8gv4+RVzsR74VQ=; b=ORpjx6ZpvxQDj+HHO/ryPpvFud+WyK71h/7+gSzpna6lc+YI46US2Q3lfByDC1dKzz +mtUYkjYBNykboGNTcfwaFKV9weW5dWvajvzOPAAUbdsOWyEBnyoOgjHJaso3TP1GpjX 0Eo8lHJTSSPJgPa8ve4iLFRRrZgG5oGwAhDWCD7GfazIZxZgA6XvFB2MvB347knX8jwQ jfdKv9pqaVohX9kF3yCwVAUDdT53ssCKsgAr+uKNtkUbKjugrjboVlaRJ9c9kYyGnTop idaLNZIPc8N6bC2gSKCct1CCcsFpz1aHuhugV4PZG5monH3vatuPFryyBRLP+E+Ut35w Wtaw== X-Gm-Message-State: APjAAAU8Uf6fdP1bcxF0G34GPFlRKZhEphT/Z5XjJ/EjiJeU46lqqUtQ i4ewYxxWpC+6KGG4PT1G2/tFdr6c X-Google-Smtp-Source: APXvYqwJLHQwAWW5duRMG7fSzTt431DYaSB/pQPPvt6Lguh49y+ClNL3iN8VJAoRaFQNaJz3T6V52Q== X-Received: by 2002:a1c:c906:: with SMTP id f6mr32010469wmb.14.1575321001109; Mon, 02 Dec 2019 13:10:01 -0800 (PST) Received: from pavilion.home ([2a02:a456:6be8:1:8edc:d4ff:fe8b:18b7]) by smtp.gmail.com with ESMTPSA id f1sm770859wrp.93.2019.12.02.13.10.00 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 02 Dec 2019 13:10:00 -0800 (PST) From: Niek Linnenbank To: qemu-devel@nongnu.org Subject: [PATCH 05/10] arm: allwinner-h3: add System Control module Date: Mon, 2 Dec 2019 22:09:42 +0100 Message-Id: <20191202210947.3603-6-nieklinnenbank@gmail.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20191202210947.3603-1-nieklinnenbank@gmail.com> References: <20191202210947.3603-1-nieklinnenbank@gmail.com> X-detected-operating-system: by eggs.gnu.org: Genre and OS details not recognized. X-Received-From: 2a00:1450:4864:20::341 X-Mailman-Approved-At: Mon, 02 Dec 2019 16:38:22 -0500 X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: b.galvani@gmail.com, peter.maydell@linaro.org, Niek Linnenbank , qemu-arm@nongnu.org Errors-To: qemu-devel-bounces+patchwork-qemu-devel=patchwork.kernel.org@nongnu.org Sender: "Qemu-devel" The Allwinner H3 System on Chip has an System Control module that provides system wide generic controls and device information. This commit adds support for the Allwinner H3 System Control module. Signed-off-by: Niek Linnenbank --- hw/arm/allwinner-h3.c | 11 ++ hw/misc/Makefile.objs | 1 + hw/misc/allwinner-h3-syscon.c | 139 ++++++++++++++++++++++++++ include/hw/arm/allwinner-h3.h | 2 + include/hw/misc/allwinner-h3-syscon.h | 43 ++++++++ 5 files changed, 196 insertions(+) create mode 100644 hw/misc/allwinner-h3-syscon.c create mode 100644 include/hw/misc/allwinner-h3-syscon.h diff --git a/hw/arm/allwinner-h3.c b/hw/arm/allwinner-h3.c index afeb49c0ac..ebd8fde412 100644 --- a/hw/arm/allwinner-h3.c +++ b/hw/arm/allwinner-h3.c @@ -41,6 +41,9 @@ static void aw_h3_init(Object *obj) sysbus_init_child_obj(obj, "ccu", &s->ccu, sizeof(s->ccu), TYPE_AW_H3_CLK); + + sysbus_init_child_obj(obj, "syscon", &s->syscon, sizeof(s->syscon), + TYPE_AW_H3_SYSCON); } static void aw_h3_realize(DeviceState *dev, Error **errp) @@ -184,6 +187,14 @@ static void aw_h3_realize(DeviceState *dev, Error **errp) } sysbus_mmio_map(SYS_BUS_DEVICE(&s->ccu), 0, AW_H3_CCU_BASE); + /* System Control */ + object_property_set_bool(OBJECT(&s->syscon), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + sysbus_mmio_map(SYS_BUS_DEVICE(&s->syscon), 0, AW_H3_SYSCON_BASE); + /* Universal Serial Bus */ sysbus_create_simple(TYPE_AW_H3_EHCI, AW_H3_EHCI0_BASE, s->irq[AW_H3_GIC_SPI_EHCI0]); diff --git a/hw/misc/Makefile.objs b/hw/misc/Makefile.objs index 200ed44ce1..b234aefba5 100644 --- a/hw/misc/Makefile.objs +++ b/hw/misc/Makefile.objs @@ -29,6 +29,7 @@ common-obj-$(CONFIG_MACIO) += macio/ common-obj-$(CONFIG_IVSHMEM_DEVICE) += ivshmem.o common-obj-$(CONFIG_ALLWINNER_H3) += allwinner-h3-clk.o +common-obj-$(CONFIG_ALLWINNER_H3) += allwinner-h3-syscon.o common-obj-$(CONFIG_REALVIEW) += arm_sysctl.o common-obj-$(CONFIG_NSERIES) += cbus.o common-obj-$(CONFIG_ECCMEMCTL) += eccmemctl.o diff --git a/hw/misc/allwinner-h3-syscon.c b/hw/misc/allwinner-h3-syscon.c new file mode 100644 index 0000000000..66bd518a05 --- /dev/null +++ b/hw/misc/allwinner-h3-syscon.c @@ -0,0 +1,139 @@ +/* + * Allwinner H3 System Control emulation + * + * Copyright (C) 2019 Niek Linnenbank + * + * 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 "hw/sysbus.h" +#include "migration/vmstate.h" +#include "qemu/log.h" +#include "qemu/module.h" +#include "hw/misc/allwinner-h3-syscon.h" + +/* SYSCON register offsets */ +#define REG_VER (0x24) /* Version */ +#define REG_EMAC_PHY_CLK (0x30) /* EMAC PHY Clock */ +#define REG_INDEX(offset) (offset / sizeof(uint32_t)) + +/* SYSCON register reset values */ +#define REG_VER_RST (0x0) +#define REG_EMAC_PHY_CLK_RST (0x58000) + +static uint64_t allwinner_h3_syscon_read(void *opaque, hwaddr offset, + unsigned size) +{ + const AwH3SysconState *s = (AwH3SysconState *)opaque; + const uint32_t idx = REG_INDEX(offset); + + if (idx >= AW_H3_SYSCON_REGS_NUM) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: bad read offset 0x%04x\n", + __func__, (uint32_t)offset); + return 0; + } + + return s->regs[idx]; +} + +static void allwinner_h3_syscon_write(void *opaque, hwaddr offset, + uint64_t val, unsigned size) +{ + AwH3SysconState *s = (AwH3SysconState *)opaque; + const uint32_t idx = REG_INDEX(offset); + + if (idx >= AW_H3_SYSCON_REGS_NUM) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: bad write offset 0x%04x\n", + __func__, (uint32_t)offset); + return; + } + + switch (offset) { + case REG_VER: /* Version */ + break; + default: + s->regs[idx] = (uint32_t) val; + break; + } +} + +static const MemoryRegionOps allwinner_h3_syscon_ops = { + .read = allwinner_h3_syscon_read, + .write = allwinner_h3_syscon_write, + .endianness = DEVICE_NATIVE_ENDIAN, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + .unaligned = false + } +}; + +static void allwinner_h3_syscon_reset(DeviceState *dev) +{ + AwH3SysconState *s = AW_H3_SYSCON(dev); + + /* Set default values for registers */ + s->regs[REG_INDEX(REG_VER)] = REG_VER_RST; + s->regs[REG_INDEX(REG_EMAC_PHY_CLK)] = REG_EMAC_PHY_CLK_RST; +} + +static void allwinner_h3_syscon_realize(DeviceState *dev, Error **errp) +{ +} + +static void allwinner_h3_syscon_init(Object *obj) +{ + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); + AwH3SysconState *s = AW_H3_SYSCON(obj); + + /* Memory mapping */ + memory_region_init_io(&s->iomem, OBJECT(s), &allwinner_h3_syscon_ops, s, + TYPE_AW_H3_SYSCON, AW_H3_SYSCON_REGS_MEM_SIZE); + sysbus_init_mmio(sbd, &s->iomem); +} + +static const VMStateDescription allwinner_h3_syscon_vmstate = { + .name = TYPE_AW_H3_SYSCON, + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32_ARRAY(regs, AwH3SysconState, AW_H3_SYSCON_REGS_NUM), + VMSTATE_END_OF_LIST() + } +}; + +static void allwinner_h3_syscon_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->reset = allwinner_h3_syscon_reset; + dc->realize = allwinner_h3_syscon_realize; + dc->vmsd = &allwinner_h3_syscon_vmstate; +} + +static const TypeInfo allwinner_h3_syscon_info = { + .name = TYPE_AW_H3_SYSCON, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_init = allwinner_h3_syscon_init, + .instance_size = sizeof(AwH3SysconState), + .class_init = allwinner_h3_syscon_class_init, +}; + +static void allwinner_h3_syscon_register(void) +{ + type_register_static(&allwinner_h3_syscon_info); +} + +type_init(allwinner_h3_syscon_register) diff --git a/include/hw/arm/allwinner-h3.h b/include/hw/arm/allwinner-h3.h index e596516c5c..2bc526b77b 100644 --- a/include/hw/arm/allwinner-h3.h +++ b/include/hw/arm/allwinner-h3.h @@ -27,6 +27,7 @@ #include "hw/timer/allwinner-a10-pit.h" #include "hw/intc/arm_gic.h" #include "hw/misc/allwinner-h3-clk.h" +#include "hw/misc/allwinner-h3-syscon.h" #include "target/arm/cpu.h" #define AW_H3_SRAM_A1_BASE (0x00000000) @@ -111,6 +112,7 @@ typedef struct AwH3State { qemu_irq irq[AW_H3_GIC_NUM_SPI]; AwA10PITState timer; AwH3ClockState ccu; + AwH3SysconState syscon; GICState gic; MemoryRegion sram_a1; MemoryRegion sram_a2; diff --git a/include/hw/misc/allwinner-h3-syscon.h b/include/hw/misc/allwinner-h3-syscon.h new file mode 100644 index 0000000000..22a2f2a11b --- /dev/null +++ b/include/hw/misc/allwinner-h3-syscon.h @@ -0,0 +1,43 @@ +/* + * Allwinner H3 System Control emulation + * + * Copyright (C) 2019 Niek Linnenbank + * + * 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_H3_SYSCON_H +#define HW_MISC_ALLWINNER_H3_SYSCON_H + +#include "hw/sysbus.h" + +#define AW_H3_SYSCON_REGS_MAX_ADDR (0x30) +#define AW_H3_SYSCON_REGS_NUM ((AW_H3_SYSCON_REGS_MAX_ADDR / \ + sizeof(uint32_t)) + 1) +#define AW_H3_SYSCON_REGS_MEM_SIZE (1024) + +#define TYPE_AW_H3_SYSCON "allwinner-h3-syscon" +#define AW_H3_SYSCON(obj) OBJECT_CHECK(AwH3SysconState, (obj), \ + TYPE_AW_H3_SYSCON) + +typedef struct AwH3SysconState { + /*< private >*/ + SysBusDevice parent_obj; + /*< public >*/ + + MemoryRegion iomem; + uint32_t regs[AW_H3_SYSCON_REGS_NUM]; +} AwH3SysconState; + +#endif From patchwork Mon Dec 2 21:09:43 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Niek Linnenbank X-Patchwork-Id: 11269939 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 435FC921 for ; Mon, 2 Dec 2019 21:41:17 +0000 (UTC) Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id 174C82068E for ; Mon, 2 Dec 2019 21:41:17 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=fail reason="signature verification failed" (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="K0XYsaK6" DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org 174C82068E Authentication-Results: mail.kernel.org; dmarc=fail (p=none dis=none) header.from=gmail.com Authentication-Results: mail.kernel.org; spf=pass smtp.mailfrom=qemu-devel-bounces+patchwork-qemu-devel=patchwork.kernel.org@nongnu.org Received: from localhost ([::1]:44864 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1ibtRU-000787-4T for patchwork-qemu-devel@patchwork.kernel.org; Mon, 02 Dec 2019 16:41:16 -0500 Received: from eggs.gnu.org ([2001:470:142:3::10]:38416) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1ibsxl-0002no-KU for qemu-devel@nongnu.org; Mon, 02 Dec 2019 16:10:35 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1ibsxk-0000pM-3V for qemu-devel@nongnu.org; Mon, 02 Dec 2019 16:10:33 -0500 Received: from mail-wm1-x341.google.com ([2a00:1450:4864:20::341]:40702) by eggs.gnu.org with esmtps (TLS1.0:RSA_AES_128_CBC_SHA1:16) (Exim 4.71) (envelope-from ) id 1ibsxj-0000ZG-Q2; Mon, 02 Dec 2019 16:10:32 -0500 Received: by mail-wm1-x341.google.com with SMTP id t14so1084653wmi.5; Mon, 02 Dec 2019 13:10:03 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=5I8SpK7yJTqrWa069AbQjRTm8TolcAxVRIFgMoj+ZJ0=; b=K0XYsaK6kM2j1a5/AYPnE5yHEwoqv/Uqc8s/bf10ipYar6WTmTvvcvC8M2Zr/bg+Xl nQzyrOCMe+rp5l8+Hd2DmSKibGI+CSE6Hph3S5EyqWAxZrLoASNMUBqUzTQqJsEBLS+W y1nDPNUWVg3SP7kbcXwRGL5Q6Bmoo8hu9deClMmJkCfkPcC8KOBKExSKrEHP/M3kECPO ipvadTlq1NRkHE0KDi41l52u0Sercdor5/cpPNomr+s0U27A7tDJBIJTApOZPtgyj7Zb QMZf0qFL6ajbi3u/erz0D6LpRPogDEmxILs8wIi3vAJvDTPbcV5F9B3a8qC9+rb/SZQ9 efsQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=5I8SpK7yJTqrWa069AbQjRTm8TolcAxVRIFgMoj+ZJ0=; b=fkhTKobMoTjcEMNwiRL7PdlDaARLhhuDbm9dzIqR5oxfZ/pas6T95I4+PXZqx4DsM9 VfGps5+HW+SlVSzXyzt9dDWjTQ2UUtvDdacuCZ9SZDBnIeWkeUqTneujWiBnT901CsvS wNNj/o48l8u+YFMOv1rW8icEJFMbnF+DIuScCmX1I+nCNNzelJbVtB3nGPRY6STjUuUd F0S/z2Nh8XKxX7imPX1HKRhZZwBr7VxNa75QaEHfaYGzmesJtdmoccLFrgAS0EQQGqw9 /z5NdQXRKooXSMHc5ARmL8hVDM+uxcyjpaVgAmp7fvOiOitYyQxEKabY/qZr34XL0Vp/ WnFg== X-Gm-Message-State: APjAAAUL6xafI/gv0i1ibQUtQMkrhhyw4hosfWVWplsZFxcGdYslGYs3 9ofhCkYAB3+rvgqci97iZEjUk1hu X-Google-Smtp-Source: APXvYqxc/726bx2suYPTiEg7ZbvZg+Xg+KlXFhLvvvLRKLoIKgOwhfzvjoI775km1xi0oLit71DQOQ== X-Received: by 2002:a7b:cb54:: with SMTP id v20mr30178448wmj.11.1575321001972; Mon, 02 Dec 2019 13:10:01 -0800 (PST) Received: from pavilion.home ([2a02:a456:6be8:1:8edc:d4ff:fe8b:18b7]) by smtp.gmail.com with ESMTPSA id f1sm770859wrp.93.2019.12.02.13.10.01 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 02 Dec 2019 13:10:01 -0800 (PST) From: Niek Linnenbank To: qemu-devel@nongnu.org Subject: [PATCH 06/10] arm/arm-powerctl: set NSACR.{CP11, CP10} bits in arm_set_cpu_on() Date: Mon, 2 Dec 2019 22:09:43 +0100 Message-Id: <20191202210947.3603-7-nieklinnenbank@gmail.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20191202210947.3603-1-nieklinnenbank@gmail.com> References: <20191202210947.3603-1-nieklinnenbank@gmail.com> X-detected-operating-system: by eggs.gnu.org: Genre and OS details not recognized. X-Received-From: 2a00:1450:4864:20::341 X-Mailman-Approved-At: Mon, 02 Dec 2019 16:38:23 -0500 X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: b.galvani@gmail.com, peter.maydell@linaro.org, Niek Linnenbank , qemu-arm@nongnu.org Errors-To: qemu-devel-bounces+patchwork-qemu-devel=patchwork.kernel.org@nongnu.org Sender: "Qemu-devel" This change ensures that the FPU can be accessed in Non-Secure mode when the CPU core is reset using the arm_set_cpu_on() function call. The NSACR.{CP11,CP10} bits define the exception level required to access the FPU in Non-Secure mode. Without these bits set, the CPU will give an undefined exception trap on the first FPU access for the secondary cores under Linux. Fixes: fc1120a7f5 Signed-off-by: Niek Linnenbank --- target/arm/arm-powerctl.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/target/arm/arm-powerctl.c b/target/arm/arm-powerctl.c index f77a950db6..b064513d44 100644 --- a/target/arm/arm-powerctl.c +++ b/target/arm/arm-powerctl.c @@ -104,6 +104,9 @@ static void arm_set_cpu_on_async_work(CPUState *target_cpu_state, /* Processor is not in secure mode */ target_cpu->env.cp15.scr_el3 |= SCR_NS; + /* Set NSACR.{CP11,CP10} so NS can access the FPU */ + target_cpu->env.cp15.nsacr |= 3 << 10; + /* * If QEMU is providing the equivalent of EL3 firmware, then we need * to make sure a CPU targeting EL2 comes out of reset with a From patchwork Mon Dec 2 21:09:44 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Niek Linnenbank X-Patchwork-Id: 11269937 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id C4285921 for ; Mon, 2 Dec 2019 21:41:14 +0000 (UTC) Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id 897BD2068E for ; Mon, 2 Dec 2019 21:41:14 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=fail reason="signature verification failed" (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="uSigWBGq" DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org 897BD2068E Authentication-Results: mail.kernel.org; dmarc=fail (p=none dis=none) header.from=gmail.com Authentication-Results: mail.kernel.org; spf=pass smtp.mailfrom=qemu-devel-bounces+patchwork-qemu-devel=patchwork.kernel.org@nongnu.org Received: from localhost ([::1]:44862 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1ibtRR-00074x-3q for patchwork-qemu-devel@patchwork.kernel.org; Mon, 02 Dec 2019 16:41:13 -0500 Received: from eggs.gnu.org ([2001:470:142:3::10]:38177) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1ibsxR-0002b8-CR for qemu-devel@nongnu.org; Mon, 02 Dec 2019 16:10:16 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1ibsxO-0000eY-B5 for qemu-devel@nongnu.org; Mon, 02 Dec 2019 16:10:12 -0500 Received: from mail-wm1-x341.google.com ([2a00:1450:4864:20::341]:53089) by eggs.gnu.org with esmtps (TLS1.0:RSA_AES_128_CBC_SHA1:16) (Exim 4.71) (envelope-from ) id 1ibsxK-0000Ze-T0; Mon, 02 Dec 2019 16:10:08 -0500 Received: by mail-wm1-x341.google.com with SMTP id p9so832211wmc.2; Mon, 02 Dec 2019 13:10:04 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=6mohYVc55+3KFtvWDD44Yy/lRiZu/L99ANQeBUfkI2w=; b=uSigWBGqzoAwI+P2C+q5Yx/llTHRDCinveHXuALIGGg/dQ9lx7TuvrliC5GXiHGAfo Wx+1NxP4wCm1F6nGQ89Ensb6ogUnDdheYxrqFRT4gN0SCtB105JK0YBAhndG9hzpvNjH y1vutiiYVXGx0JMSKbeuElb9mLNR08NjkNlj27VOZEpixX7P4YSI08rUJCpLKn5z5xRn 258vIVzoLMbwgQeQx2BN2gPICiloUWQMsxSY3LxSrYoDaKokdqEapzt0IFytWhFGSUDn vIXKk2C8ksJ9xNvE4KSCyRmbFsDioCsBpj40ANxfpk/jitW0hlJZsLO6EEwL8oHVPNRU 4dHQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=6mohYVc55+3KFtvWDD44Yy/lRiZu/L99ANQeBUfkI2w=; b=Q1zd1JhAeXucbbVp27IsA+X/sEowQXyQp1XUjavdZsqh+BAJ0UQHfLdu5aUWG9vTnP X/HIthpK6DUOf6OWkoHkBMJb8aNODeqY/hjPrwSiXEjkU6Eb2TlFurNOANaQY4rMrzBk 4CUKcgh45hysUaOzHpY7JB3o4ouKrJk2QA5/1ucIDIyePukG9ZlP6q1lgRDUSnHQqe53 g6kxbiDKrup/hNpSKXH7x+A7LNmkWp/FvtBGmbnnLkePNCiVaR4KPisAZfKwJxX7bMFd 6Yr8uGUi1VUQgXHr0oC3PTSeyKc4oa+ZXa2p+WmGQJgRMT7yVfkQ/saMEDMXWX7g6UnB iVkQ== X-Gm-Message-State: APjAAAXiKwx+sxdk2eMzf+XnzxfzDio1YClBSZ/o0dRJamHi9UEFYnGp Nqu9IVut+no/TayFr37ZMqPn793R X-Google-Smtp-Source: APXvYqxx6qXyvz4dKHhNT5NjsleT4hvl04XfmRIZy3SV8yBhFpL9HS6B1oR4Rdp5Cvz21rlgpGej8A== X-Received: by 2002:a05:600c:2318:: with SMTP id 24mr30400612wmo.21.1575321002800; Mon, 02 Dec 2019 13:10:02 -0800 (PST) Received: from pavilion.home ([2a02:a456:6be8:1:8edc:d4ff:fe8b:18b7]) by smtp.gmail.com with ESMTPSA id f1sm770859wrp.93.2019.12.02.13.10.01 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 02 Dec 2019 13:10:02 -0800 (PST) From: Niek Linnenbank To: qemu-devel@nongnu.org Subject: [PATCH 07/10] arm: allwinner-h3: add CPU Configuration module Date: Mon, 2 Dec 2019 22:09:44 +0100 Message-Id: <20191202210947.3603-8-nieklinnenbank@gmail.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20191202210947.3603-1-nieklinnenbank@gmail.com> References: <20191202210947.3603-1-nieklinnenbank@gmail.com> X-detected-operating-system: by eggs.gnu.org: Genre and OS details not recognized. X-Received-From: 2a00:1450:4864:20::341 X-Mailman-Approved-At: Mon, 02 Dec 2019 16:38:21 -0500 X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: b.galvani@gmail.com, peter.maydell@linaro.org, Niek Linnenbank , qemu-arm@nongnu.org Errors-To: qemu-devel-bounces+patchwork-qemu-devel=patchwork.kernel.org@nongnu.org Sender: "Qemu-devel" The Allwinner H3 System on Chip design contains four ARM Cortex A7 processors that can be configured and reset using the CPU Configuration module interface. This commit adds support for the CPU configuration interface which emulates the following features: * CPU reset * Shared 64-bit timer Signed-off-by: Niek Linnenbank --- hw/arm/allwinner-h3.c | 11 + hw/misc/Makefile.objs | 1 + hw/misc/allwinner-h3-cpucfg.c | 280 ++++++++++++++++++++++++++ include/hw/arm/allwinner-h3.h | 2 + include/hw/misc/allwinner-h3-cpucfg.h | 44 ++++ 5 files changed, 338 insertions(+) create mode 100644 hw/misc/allwinner-h3-cpucfg.c create mode 100644 include/hw/misc/allwinner-h3-cpucfg.h diff --git a/hw/arm/allwinner-h3.c b/hw/arm/allwinner-h3.c index ebd8fde412..44aba1de6a 100644 --- a/hw/arm/allwinner-h3.c +++ b/hw/arm/allwinner-h3.c @@ -44,6 +44,9 @@ static void aw_h3_init(Object *obj) sysbus_init_child_obj(obj, "syscon", &s->syscon, sizeof(s->syscon), TYPE_AW_H3_SYSCON); + + sysbus_init_child_obj(obj, "cpucfg", &s->cpucfg, sizeof(s->cpucfg), + TYPE_AW_H3_CPUCFG); } static void aw_h3_realize(DeviceState *dev, Error **errp) @@ -195,6 +198,14 @@ static void aw_h3_realize(DeviceState *dev, Error **errp) } sysbus_mmio_map(SYS_BUS_DEVICE(&s->syscon), 0, AW_H3_SYSCON_BASE); + /* CPU Configuration */ + object_property_set_bool(OBJECT(&s->cpucfg), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + sysbus_mmio_map(SYS_BUS_DEVICE(&s->cpucfg), 0, AW_H3_CPUCFG_BASE); + /* Universal Serial Bus */ sysbus_create_simple(TYPE_AW_H3_EHCI, AW_H3_EHCI0_BASE, s->irq[AW_H3_GIC_SPI_EHCI0]); diff --git a/hw/misc/Makefile.objs b/hw/misc/Makefile.objs index b234aefba5..c4ca2ed689 100644 --- a/hw/misc/Makefile.objs +++ b/hw/misc/Makefile.objs @@ -29,6 +29,7 @@ common-obj-$(CONFIG_MACIO) += macio/ common-obj-$(CONFIG_IVSHMEM_DEVICE) += ivshmem.o common-obj-$(CONFIG_ALLWINNER_H3) += allwinner-h3-clk.o +obj-$(CONFIG_ALLWINNER_H3) += allwinner-h3-cpucfg.o common-obj-$(CONFIG_ALLWINNER_H3) += allwinner-h3-syscon.o common-obj-$(CONFIG_REALVIEW) += arm_sysctl.o common-obj-$(CONFIG_NSERIES) += cbus.o diff --git a/hw/misc/allwinner-h3-cpucfg.c b/hw/misc/allwinner-h3-cpucfg.c new file mode 100644 index 0000000000..b47feebd73 --- /dev/null +++ b/hw/misc/allwinner-h3-cpucfg.c @@ -0,0 +1,280 @@ +/* + * Allwinner H3 CPU Configuration Module emulation + * + * Copyright (C) 2019 Niek Linnenbank + * + * 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 "hw/sysbus.h" +#include "migration/vmstate.h" +#include "qemu/log.h" +#include "qemu/module.h" +#include "qemu/error-report.h" +#include "qemu/timer.h" +#include "hw/core/cpu.h" +#include "arm-powerctl.h" +#include "hw/misc/allwinner-h3-cpucfg.h" +#include "trace.h" + +/* CPUCFG register offsets */ +#define REG_CPUS_RST_CTRL (0x0000) /* CPUs Reset Control */ +#define REG_CPU0_RST_CTRL (0x0040) /* CPU#0 Reset Control */ +#define REG_CPU0_CTRL (0x0044) /* CPU#0 Control */ +#define REG_CPU0_STATUS (0x0048) /* CPU#0 Status */ +#define REG_CPU1_RST_CTRL (0x0080) /* CPU#1 Reset Control */ +#define REG_CPU1_CTRL (0x0084) /* CPU#1 Control */ +#define REG_CPU1_STATUS (0x0088) /* CPU#1 Status */ +#define REG_CPU2_RST_CTRL (0x00C0) /* CPU#2 Reset Control */ +#define REG_CPU2_CTRL (0x00C4) /* CPU#2 Control */ +#define REG_CPU2_STATUS (0x00C8) /* CPU#2 Status */ +#define REG_CPU3_RST_CTRL (0x0100) /* CPU#3 Reset Control */ +#define REG_CPU3_CTRL (0x0104) /* CPU#3 Control */ +#define REG_CPU3_STATUS (0x0108) /* CPU#3 Status */ +#define REG_CPU_SYS_RST (0x0140) /* CPU System Reset */ +#define REG_CLK_GATING (0x0144) /* CPU Clock Gating */ +#define REG_GEN_CTRL (0x0184) /* General Control */ +#define REG_SUPER_STANDBY (0x01A0) /* Super Standby Flag */ +#define REG_ENTRY_ADDR (0x01A4) /* Reset Entry Address */ +#define REG_DBG_EXTERN (0x01E4) /* Debug External */ +#define REG_CNT64_CTRL (0x0280) /* 64-bit Counter Control */ +#define REG_CNT64_LOW (0x0284) /* 64-bit Counter Low */ +#define REG_CNT64_HIGH (0x0288) /* 64-bit Counter High */ + +/* CPUCFG register flags */ +#define CPUX_RESET_RELEASED ((1 << 1) | (1 << 0)) +#define CPUX_STATUS_SMP (1 << 0) +#define CPU_SYS_RESET_RELEASED (1 << 0) +#define CLK_GATING_ENABLE ((1 << 8) | 0xF) + +/* CPUCFG register reset values */ +#define REG_CLK_GATING_RST (0x0000010F) +#define REG_GEN_CTRL_RST (0x00000020) +#define REG_SUPER_STANDBY_RST (0x0) +#define REG_CNT64_CTRL_RST (0x0) + +static void allwinner_h3_cpucfg_cpu_reset(AwH3CpuCfgState *s, uint8_t cpu_id) +{ + int ret; + + trace_allwinner_h3_cpucfg_cpu_reset(cpu_id, s->entry_addr); + + ret = arm_set_cpu_on(cpu_id, s->entry_addr, 0, 3, false); + if (ret != QEMU_ARM_POWERCTL_RET_SUCCESS) { + error_report("%s: failed to bring up CPU %d: err %d", + __func__, cpu_id, ret); + return; + } +} + +static uint64_t allwinner_h3_cpucfg_read(void *opaque, hwaddr offset, + unsigned size) +{ + const AwH3CpuCfgState *s = (AwH3CpuCfgState *)opaque; + uint64_t val = 0; + + switch (offset) { + case REG_CPUS_RST_CTRL: /* CPUs Reset Control */ + case REG_CPU_SYS_RST: /* CPU System Reset */ + val = CPU_SYS_RESET_RELEASED; + break; + case REG_CPU0_RST_CTRL: /* CPU#0 Reset Control */ + case REG_CPU1_RST_CTRL: /* CPU#1 Reset Control */ + case REG_CPU2_RST_CTRL: /* CPU#2 Reset Control */ + case REG_CPU3_RST_CTRL: /* CPU#3 Reset Control */ + val = CPUX_RESET_RELEASED; + break; + case REG_CPU0_CTRL: /* CPU#0 Control */ + case REG_CPU1_CTRL: /* CPU#1 Control */ + case REG_CPU2_CTRL: /* CPU#2 Control */ + case REG_CPU3_CTRL: /* CPU#3 Control */ + val = 0; + break; + case REG_CPU0_STATUS: /* CPU#0 Status */ + case REG_CPU1_STATUS: /* CPU#1 Status */ + case REG_CPU2_STATUS: /* CPU#2 Status */ + case REG_CPU3_STATUS: /* CPU#3 Status */ + val = CPUX_STATUS_SMP; + break; + case REG_CLK_GATING: /* CPU Clock Gating */ + val = CLK_GATING_ENABLE; + break; + case REG_GEN_CTRL: /* General Control */ + val = s->gen_ctrl; + break; + case REG_SUPER_STANDBY: /* Super Standby Flag */ + val = s->super_standby; + break; + case REG_ENTRY_ADDR: /* Reset Entry Address */ + val = s->entry_addr; + break; + case REG_DBG_EXTERN: /* Debug External */ + break; + case REG_CNT64_CTRL: /* 64-bit Counter Control */ + val = s->counter_ctrl; + break; + case REG_CNT64_LOW: /* 64-bit Counter Low */ + val = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) & 0xffffffff; + break; + case REG_CNT64_HIGH: /* 64-bit Counter High */ + val = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) >> 32; + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, "%s: bad read offset 0x%04x\n", + __func__, (uint32_t)offset); + return 0; + } + + trace_allwinner_h3_cpucfg_read(offset, val, size); + + return val; +} + +static void allwinner_h3_cpucfg_write(void *opaque, hwaddr offset, + uint64_t val, unsigned size) +{ + AwH3CpuCfgState *s = (AwH3CpuCfgState *)opaque; + + trace_allwinner_h3_cpucfg_write(offset, val, size); + + switch (offset) { + case REG_CPUS_RST_CTRL: /* CPUs Reset Control */ + case REG_CPU_SYS_RST: /* CPU System Reset */ + break; + case REG_CPU0_RST_CTRL: /* CPU#0 Reset Control */ + if (val) { + allwinner_h3_cpucfg_cpu_reset(s, 0); + } + break; + case REG_CPU1_RST_CTRL: /* CPU#1 Reset Control */ + if (val) { + allwinner_h3_cpucfg_cpu_reset(s, 1); + } + break; + case REG_CPU2_RST_CTRL: /* CPU#2 Reset Control */ + if (val) { + allwinner_h3_cpucfg_cpu_reset(s, 2); + } + break; + case REG_CPU3_RST_CTRL: /* CPU#3 Reset Control */ + if (val) { + allwinner_h3_cpucfg_cpu_reset(s, 3); + } + break; + case REG_CPU0_CTRL: /* CPU#0 Control */ + case REG_CPU1_CTRL: /* CPU#1 Control */ + case REG_CPU2_CTRL: /* CPU#2 Control */ + case REG_CPU3_CTRL: /* CPU#3 Control */ + case REG_CPU0_STATUS: /* CPU#0 Status */ + case REG_CPU1_STATUS: /* CPU#1 Status */ + case REG_CPU2_STATUS: /* CPU#2 Status */ + case REG_CPU3_STATUS: /* CPU#3 Status */ + case REG_CLK_GATING: /* CPU Clock Gating */ + case REG_GEN_CTRL: /* General Control */ + s->gen_ctrl = val; + break; + case REG_SUPER_STANDBY: /* Super Standby Flag */ + s->super_standby = val; + break; + case REG_ENTRY_ADDR: /* Reset Entry Address */ + s->entry_addr = val; + break; + case REG_DBG_EXTERN: /* Debug External */ + break; + case REG_CNT64_CTRL: /* 64-bit Counter Control */ + s->counter_ctrl = val; + break; + case REG_CNT64_LOW: /* 64-bit Counter Low */ + case REG_CNT64_HIGH: /* 64-bit Counter High */ + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, "%s: bad write offset 0x%04x\n", + __func__, (uint32_t)offset); + return; + } +} + +static const MemoryRegionOps allwinner_h3_cpucfg_ops = { + .read = allwinner_h3_cpucfg_read, + .write = allwinner_h3_cpucfg_write, + .endianness = DEVICE_NATIVE_ENDIAN, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + .unaligned = false + } +}; + +static void allwinner_h3_cpucfg_reset(DeviceState *dev) +{ + AwH3CpuCfgState *s = AW_H3_CPUCFG(dev); + + /* Set default values for registers */ + s->gen_ctrl = REG_GEN_CTRL_RST; + s->super_standby = REG_SUPER_STANDBY_RST; + s->entry_addr = 0; + s->counter_ctrl = REG_CNT64_CTRL_RST; +} + +static void allwinner_h3_cpucfg_realize(DeviceState *dev, Error **errp) +{ +} + +static void allwinner_h3_cpucfg_init(Object *obj) +{ + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); + AwH3CpuCfgState *s = AW_H3_CPUCFG(obj); + + /* Memory mapping */ + memory_region_init_io(&s->iomem, OBJECT(s), &allwinner_h3_cpucfg_ops, s, + TYPE_AW_H3_CPUCFG, AW_H3_CPUCFG_REGS_MEM_SIZE); + sysbus_init_mmio(sbd, &s->iomem); +} + +static const VMStateDescription allwinner_h3_cpucfg_vmstate = { + .name = TYPE_AW_H3_CPUCFG, + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32(gen_ctrl, AwH3CpuCfgState), + VMSTATE_UINT32(super_standby, AwH3CpuCfgState), + VMSTATE_UINT32(counter_ctrl, AwH3CpuCfgState), + VMSTATE_END_OF_LIST() + } +}; + +static void allwinner_h3_cpucfg_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->reset = allwinner_h3_cpucfg_reset; + dc->realize = allwinner_h3_cpucfg_realize; + dc->vmsd = &allwinner_h3_cpucfg_vmstate; +} + +static const TypeInfo allwinner_h3_cpucfg_info = { + .name = TYPE_AW_H3_CPUCFG, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_init = allwinner_h3_cpucfg_init, + .instance_size = sizeof(AwH3CpuCfgState), + .class_init = allwinner_h3_cpucfg_class_init, +}; + +static void allwinner_h3_cpucfg_register(void) +{ + type_register_static(&allwinner_h3_cpucfg_info); +} + +type_init(allwinner_h3_cpucfg_register) diff --git a/include/hw/arm/allwinner-h3.h b/include/hw/arm/allwinner-h3.h index 2bc526b77b..374061b550 100644 --- a/include/hw/arm/allwinner-h3.h +++ b/include/hw/arm/allwinner-h3.h @@ -27,6 +27,7 @@ #include "hw/timer/allwinner-a10-pit.h" #include "hw/intc/arm_gic.h" #include "hw/misc/allwinner-h3-clk.h" +#include "hw/misc/allwinner-h3-cpucfg.h" #include "hw/misc/allwinner-h3-syscon.h" #include "target/arm/cpu.h" @@ -112,6 +113,7 @@ typedef struct AwH3State { qemu_irq irq[AW_H3_GIC_NUM_SPI]; AwA10PITState timer; AwH3ClockState ccu; + AwH3CpuCfgState cpucfg; AwH3SysconState syscon; GICState gic; MemoryRegion sram_a1; diff --git a/include/hw/misc/allwinner-h3-cpucfg.h b/include/hw/misc/allwinner-h3-cpucfg.h new file mode 100644 index 0000000000..808aaa90f6 --- /dev/null +++ b/include/hw/misc/allwinner-h3-cpucfg.h @@ -0,0 +1,44 @@ +/* + * Allwinner H3 CPU Configuration Module emulation + * + * Copyright (C) 2019 Niek Linnenbank + * + * 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_H3_CPUCFG_H +#define HW_MISC_ALLWINNER_H3_CPUCFG_H + +#include "hw/sysbus.h" + +#define AW_H3_CPUCFG_REGS_MEM_SIZE (1024) + +#define TYPE_AW_H3_CPUCFG "allwinner-h3-cpucfg" +#define AW_H3_CPUCFG(obj) OBJECT_CHECK(AwH3CpuCfgState, (obj), \ + TYPE_AW_H3_CPUCFG) + +typedef struct AwH3CpuCfgState { + /*< private >*/ + SysBusDevice parent_obj; + /*< public >*/ + + MemoryRegion iomem; + uint32_t gen_ctrl; + uint32_t super_standby; + uint32_t entry_addr; + uint32_t counter_ctrl; + +} AwH3CpuCfgState; + +#endif From patchwork Mon Dec 2 21:09:45 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Niek Linnenbank X-Patchwork-Id: 11269953 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id B69DF138D for ; Mon, 2 Dec 2019 21:46:43 +0000 (UTC) Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id 7B5C020705 for ; Mon, 2 Dec 2019 21:46:43 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=fail reason="signature verification failed" (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="IaaNGa/x" DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org 7B5C020705 Authentication-Results: mail.kernel.org; dmarc=fail (p=none dis=none) header.from=gmail.com Authentication-Results: mail.kernel.org; spf=pass smtp.mailfrom=qemu-devel-bounces+patchwork-qemu-devel=patchwork.kernel.org@nongnu.org Received: from localhost ([::1]:44912 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1ibtWk-0003Am-EK for patchwork-qemu-devel@patchwork.kernel.org; Mon, 02 Dec 2019 16:46:42 -0500 Received: from eggs.gnu.org ([2001:470:142:3::10]:38451) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1ibsxn-0002pc-4z for qemu-devel@nongnu.org; Mon, 02 Dec 2019 16:10:37 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1ibsxk-0000pS-4E for qemu-devel@nongnu.org; Mon, 02 Dec 2019 16:10:34 -0500 Received: from mail-wm1-x342.google.com ([2a00:1450:4864:20::342]:38631) by eggs.gnu.org with esmtps (TLS1.0:RSA_AES_128_CBC_SHA1:16) (Exim 4.71) (envelope-from ) id 1ibsxj-0000Zk-PN; Mon, 02 Dec 2019 16:10:32 -0500 Received: by mail-wm1-x342.google.com with SMTP id p17so1104582wmi.3; Mon, 02 Dec 2019 13:10:04 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=8lluLcXPi3hnYH6Ygv8lu4PnBF+2/P64LGe20+1b09Y=; b=IaaNGa/xTJJsau+FEd6bH3Orvmr7M2hmoP8N4NJOvvEdG5FBhAw7r5qoEF3O2E4oq4 T29F62P3aY/FBVASqu1vlTt6Y23w3xBZoaHA+6bPbx0NEssAcq1dNOqNcTLFVQWHHB53 rgX7XNIAV5m/TEsEpTVQyX//tVS0wpbPouzxhCtKtIbwFF1Bmu81qdlZQ9qDwGz8YL9q RGKFx2LyN3zHBWWRHuyq7Qj3rb6+TgNkcki6bcp97TNv6knjtjkVug0E7cG6lvsebw3n cVFtCATFktltFwI2eBDF7asFVTEr4jcRCp+Du+9fCYYx1s5wBTGF9hSKR0uIO9WhcLr/ adQw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=8lluLcXPi3hnYH6Ygv8lu4PnBF+2/P64LGe20+1b09Y=; b=Wq1okkFiPenTfcVusacAWlGnGr3n8T/DT2z2XgqO/56y2FUFX25TnkYy1f/N0Y2xTT rbV8tYwe0gsbx+HG79+fXASonCknMD8aBB7RNEI5ffQ8sGOdXA9JttQpLndXYvIqeUI9 aDOAWPEHKjC75vlxtle3+UIsPyHpkrMyXBnmrqi69ATKnYU5RkT9Il6aS5GwBwU2djfl ksnVGg5BKv0gmoZkz4D7BpM/5bv5QVE2WKNuWkhX4Jndq8IQ+YW0kZfChHkwaTIZhh7v krSfRGUYpE4TdV1ue6ihU2mco4aPQ/HKd8RhEUBrLtGju+jfQEzBpRGszRwqFDnBleWL oA/g== X-Gm-Message-State: APjAAAV2Dp9H9CXqhoRwzWM6XpJK85vgwliPAmd29ErrEIuwHeTOdzh6 6AOymjpBZvfR1w4aJUtZjLpgngQH X-Google-Smtp-Source: APXvYqytFB54mKI6R0m+7yMnJirBfUWuIzriMBd0azSgoDQMki+WNs15DxWXwzfcUHM7rH3SfeXAdw== X-Received: by 2002:a1c:a548:: with SMTP id o69mr30725863wme.31.1575321003600; Mon, 02 Dec 2019 13:10:03 -0800 (PST) Received: from pavilion.home ([2a02:a456:6be8:1:8edc:d4ff:fe8b:18b7]) by smtp.gmail.com with ESMTPSA id f1sm770859wrp.93.2019.12.02.13.10.02 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 02 Dec 2019 13:10:03 -0800 (PST) From: Niek Linnenbank To: qemu-devel@nongnu.org Subject: [PATCH 08/10] arm: allwinner-h3: add Security Identifier device Date: Mon, 2 Dec 2019 22:09:45 +0100 Message-Id: <20191202210947.3603-9-nieklinnenbank@gmail.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20191202210947.3603-1-nieklinnenbank@gmail.com> References: <20191202210947.3603-1-nieklinnenbank@gmail.com> X-detected-operating-system: by eggs.gnu.org: Genre and OS details not recognized. X-Received-From: 2a00:1450:4864:20::342 X-Mailman-Approved-At: Mon, 02 Dec 2019 16:38:25 -0500 X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: b.galvani@gmail.com, peter.maydell@linaro.org, Niek Linnenbank , qemu-arm@nongnu.org Errors-To: qemu-devel-bounces+patchwork-qemu-devel=patchwork.kernel.org@nongnu.org Sender: "Qemu-devel" The Security Identifier device in Allwinner H3 System on Chip gives applications a per-board unique identifier. This commit adds support for the Allwinner H3 Security Identifier using randomized data as input. Signed-off-by: Niek Linnenbank --- hw/arm/allwinner-h3.c | 11 ++ hw/misc/Makefile.objs | 1 + hw/misc/allwinner-h3-sid.c | 162 +++++++++++++++++++++++++++++ hw/misc/trace-events | 5 + include/hw/arm/allwinner-h3.h | 2 + include/hw/misc/allwinner-h3-sid.h | 42 ++++++++ 6 files changed, 223 insertions(+) create mode 100644 hw/misc/allwinner-h3-sid.c create mode 100644 include/hw/misc/allwinner-h3-sid.h diff --git a/hw/arm/allwinner-h3.c b/hw/arm/allwinner-h3.c index 44aba1de6a..4fc4c8c725 100644 --- a/hw/arm/allwinner-h3.c +++ b/hw/arm/allwinner-h3.c @@ -47,6 +47,9 @@ static void aw_h3_init(Object *obj) sysbus_init_child_obj(obj, "cpucfg", &s->cpucfg, sizeof(s->cpucfg), TYPE_AW_H3_CPUCFG); + + sysbus_init_child_obj(obj, "sid", &s->sid, sizeof(s->sid), + TYPE_AW_H3_SID); } static void aw_h3_realize(DeviceState *dev, Error **errp) @@ -206,6 +209,14 @@ static void aw_h3_realize(DeviceState *dev, Error **errp) } sysbus_mmio_map(SYS_BUS_DEVICE(&s->cpucfg), 0, AW_H3_CPUCFG_BASE); + /* Security Identifier */ + object_property_set_bool(OBJECT(&s->sid), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + sysbus_mmio_map(SYS_BUS_DEVICE(&s->sid), 0, AW_H3_SID_BASE); + /* Universal Serial Bus */ sysbus_create_simple(TYPE_AW_H3_EHCI, AW_H3_EHCI0_BASE, s->irq[AW_H3_GIC_SPI_EHCI0]); diff --git a/hw/misc/Makefile.objs b/hw/misc/Makefile.objs index c4ca2ed689..f3620eee4e 100644 --- a/hw/misc/Makefile.objs +++ b/hw/misc/Makefile.objs @@ -31,6 +31,7 @@ common-obj-$(CONFIG_IVSHMEM_DEVICE) += ivshmem.o common-obj-$(CONFIG_ALLWINNER_H3) += allwinner-h3-clk.o obj-$(CONFIG_ALLWINNER_H3) += allwinner-h3-cpucfg.o common-obj-$(CONFIG_ALLWINNER_H3) += allwinner-h3-syscon.o +common-obj-$(CONFIG_ALLWINNER_H3) += allwinner-h3-sid.o common-obj-$(CONFIG_REALVIEW) += arm_sysctl.o common-obj-$(CONFIG_NSERIES) += cbus.o common-obj-$(CONFIG_ECCMEMCTL) += eccmemctl.o diff --git a/hw/misc/allwinner-h3-sid.c b/hw/misc/allwinner-h3-sid.c new file mode 100644 index 0000000000..e13e0d9887 --- /dev/null +++ b/hw/misc/allwinner-h3-sid.c @@ -0,0 +1,162 @@ +/* + * Allwinner H3 Security ID emulation + * + * Copyright (C) 2019 Niek Linnenbank + * + * 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 "hw/sysbus.h" +#include "migration/vmstate.h" +#include "qemu/log.h" +#include "qemu/module.h" +#include "qemu/guest-random.h" +#include "qapi/error.h" +#include "hw/misc/allwinner-h3-sid.h" + +/* SID register offsets */ +#define REG_PRCTL (0x40) /* Control */ +#define REG_RDKEY (0x60) /* Read Key */ + +/* SID register flags */ +#define REG_PRCTL_WRITE (0x2) /* Unknown write flag */ +#define REG_PRCTL_OP_LOCK (0xAC) /* Lock operation */ + +static uint64_t allwinner_h3_sid_read(void *opaque, hwaddr offset, + unsigned size) +{ + const AwH3SidState *s = (AwH3SidState *)opaque; + uint64_t val = 0; + + switch (offset) { + case REG_PRCTL: /* Control */ + val = s->control; + break; + case REG_RDKEY: /* Read Key */ + val = s->rdkey; + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, "%s: bad read offset 0x%04x\n", + __func__, (uint32_t)offset); + return 0; + } + + return val; +} + +static void allwinner_h3_sid_write(void *opaque, hwaddr offset, + uint64_t val, unsigned size) +{ + AwH3SidState *s = (AwH3SidState *)opaque; + + switch (offset) { + case REG_PRCTL: /* Control */ + s->control = val & ~(REG_PRCTL_WRITE); + if (!(s->control & REG_PRCTL_OP_LOCK)) { + uint32_t id = (s->control >> 16) / sizeof(uint32_t); + if (id < AW_H3_SID_NUM_IDS) { + s->rdkey = s->identifier[id]; + } + } + break; + case REG_RDKEY: /* Read Key */ + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, "%s: bad write offset 0x%04x\n", + __func__, (uint32_t)offset); + break; + } +} + +static const MemoryRegionOps allwinner_h3_sid_ops = { + .read = allwinner_h3_sid_read, + .write = allwinner_h3_sid_write, + .endianness = DEVICE_NATIVE_ENDIAN, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + .unaligned = false + } +}; + +static void allwinner_h3_sid_reset(DeviceState *dev) +{ + AwH3SidState *s = AW_H3_SID(dev); + Error *err = NULL; + + /* Set default values for registers */ + s->control = 0; + s->rdkey = 0; + + /* Initialize identifier data */ + for (int i = 0; i < AW_H3_SID_NUM_IDS; i++) { + s->identifier[i] = 0; + } + + if (qemu_guest_getrandom(s->identifier, sizeof(s->identifier), &err)) { + error_report_err(err); + } +} + +static void allwinner_h3_sid_realize(DeviceState *dev, Error **errp) +{ +} + +static void allwinner_h3_sid_init(Object *obj) +{ + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); + AwH3SidState *s = AW_H3_SID(obj); + + /* Memory mapping */ + memory_region_init_io(&s->iomem, OBJECT(s), &allwinner_h3_sid_ops, s, + TYPE_AW_H3_SID, AW_H3_SID_REGS_MEM_SIZE); + sysbus_init_mmio(sbd, &s->iomem); +} + +static const VMStateDescription allwinner_h3_sid_vmstate = { + .name = TYPE_AW_H3_SID, + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32(control, AwH3SidState), + VMSTATE_UINT32(rdkey, AwH3SidState), + VMSTATE_UINT32_ARRAY(identifier, AwH3SidState, AW_H3_SID_NUM_IDS), + VMSTATE_END_OF_LIST() + } +}; + +static void allwinner_h3_sid_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->reset = allwinner_h3_sid_reset; + dc->realize = allwinner_h3_sid_realize; + dc->vmsd = &allwinner_h3_sid_vmstate; +} + +static const TypeInfo allwinner_h3_sid_info = { + .name = TYPE_AW_H3_SID, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_init = allwinner_h3_sid_init, + .instance_size = sizeof(AwH3SidState), + .class_init = allwinner_h3_sid_class_init, +}; + +static void allwinner_h3_sid_register(void) +{ + type_register_static(&allwinner_h3_sid_info); +} + +type_init(allwinner_h3_sid_register) diff --git a/hw/misc/trace-events b/hw/misc/trace-events index 1deb1d08c1..5d8a95816a 100644 --- a/hw/misc/trace-events +++ b/hw/misc/trace-events @@ -1,5 +1,10 @@ # See docs/devel/tracing.txt for syntax documentation. +# allwinner-h3-cpucfg.c +allwinner_h3_cpucfg_cpu_reset(uint8_t cpu_id, uint32_t reset_addr) "H3-CPUCFG: cpu_reset: id %u, reset_addr 0x%08x" +allwinner_h3_cpucfg_read(uint64_t offset, uint64_t data, unsigned size) "H3-CPUCFG: read: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u" +allwinner_h3_cpucfg_write(uint64_t offset, uint64_t data, unsigned size) "H3-CPUCFG: write: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u" + # eccmemctl.c ecc_mem_writel_mer(uint32_t val) "Write memory enable 0x%08x" ecc_mem_writel_mdr(uint32_t val) "Write memory delay 0x%08x" diff --git a/include/hw/arm/allwinner-h3.h b/include/hw/arm/allwinner-h3.h index 374061b550..33602599eb 100644 --- a/include/hw/arm/allwinner-h3.h +++ b/include/hw/arm/allwinner-h3.h @@ -29,6 +29,7 @@ #include "hw/misc/allwinner-h3-clk.h" #include "hw/misc/allwinner-h3-cpucfg.h" #include "hw/misc/allwinner-h3-syscon.h" +#include "hw/misc/allwinner-h3-sid.h" #include "target/arm/cpu.h" #define AW_H3_SRAM_A1_BASE (0x00000000) @@ -115,6 +116,7 @@ typedef struct AwH3State { AwH3ClockState ccu; AwH3CpuCfgState cpucfg; AwH3SysconState syscon; + AwH3SidState sid; GICState gic; MemoryRegion sram_a1; MemoryRegion sram_a2; diff --git a/include/hw/misc/allwinner-h3-sid.h b/include/hw/misc/allwinner-h3-sid.h new file mode 100644 index 0000000000..359cc86dfc --- /dev/null +++ b/include/hw/misc/allwinner-h3-sid.h @@ -0,0 +1,42 @@ +/* + * Allwinner H3 Security ID emulation + * + * Copyright (C) 2019 Niek Linnenbank + * + * 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_H3_SID_H +#define HW_MISC_ALLWINNER_H3_SID_H + +#include "hw/sysbus.h" + +#define AW_H3_SID_NUM_IDS (4) +#define AW_H3_SID_REGS_MEM_SIZE (1024) + +#define TYPE_AW_H3_SID "allwinner-h3-sid" +#define AW_H3_SID(obj) OBJECT_CHECK(AwH3SidState, (obj), TYPE_AW_H3_SID) + +typedef struct AwH3SidState { + /*< private >*/ + SysBusDevice parent_obj; + /*< public >*/ + + MemoryRegion iomem; + uint32_t control; + uint32_t rdkey; + uint32_t identifier[AW_H3_SID_NUM_IDS]; +} AwH3SidState; + +#endif From patchwork Mon Dec 2 21:09:46 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Niek Linnenbank X-Patchwork-Id: 11269963 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id A797B138D for ; Mon, 2 Dec 2019 21:55:22 +0000 (UTC) Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id 58F5E206E0 for ; Mon, 2 Dec 2019 21:55:22 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=fail reason="signature verification failed" (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="mmEM1tcu" DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org 58F5E206E0 Authentication-Results: mail.kernel.org; dmarc=fail (p=none dis=none) header.from=gmail.com Authentication-Results: mail.kernel.org; spf=pass smtp.mailfrom=qemu-devel-bounces+patchwork-qemu-devel=patchwork.kernel.org@nongnu.org Received: from localhost ([::1]:44988 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1ibtf7-0001Y6-EY for patchwork-qemu-devel@patchwork.kernel.org; Mon, 02 Dec 2019 16:55:21 -0500 Received: from eggs.gnu.org ([2001:470:142:3::10]:38407) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1ibsxl-0002nO-Hq for qemu-devel@nongnu.org; Mon, 02 Dec 2019 16:10:37 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1ibsxg-0000n0-Tt for qemu-devel@nongnu.org; Mon, 02 Dec 2019 16:10:33 -0500 Received: from mail-wm1-x342.google.com ([2a00:1450:4864:20::342]:33855) by eggs.gnu.org with esmtps (TLS1.0:RSA_AES_128_CBC_SHA1:16) (Exim 4.71) (envelope-from ) id 1ibsxc-0000aw-OK; Mon, 02 Dec 2019 16:10:27 -0500 Received: by mail-wm1-x342.google.com with SMTP id f4so776132wmj.1; Mon, 02 Dec 2019 13:10:06 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=FG2UWWMlSaxSuJKFPn2Bzn2BvM9nYHlOC0LTH9A/EEA=; b=mmEM1tcuBs1pApAkRW/Cs+IyRYZJHNvhCYySGcrz/cjA6wfqCZRnxI9gk6F0vWiMLB kLIIdvPM3vKLrUM6OeB8oAHcIJAksbVqFYU/gwHwNQu+g1fumxWAwck3B5Al2LVA2+ZC mseMqbt5UWRf8/Z1fLF3TuD5ebL0npanJ2SID5v4a+DQ5mhvH+wMpEGMN0+dhgOYlvRK VQzkVTiYjSbf32W+tWBnJ/dDfQ3hfc3B0i1NOhmfedoXTwQ+8V4RyPnBLx3EUJ7wsHgv 0DhyJP/yqIEULFpwfYlDMlnlfkeaUvhaa8E450dfzKIP12b8VQqWXACw5sVwt+1g2KVd 1W7w== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=FG2UWWMlSaxSuJKFPn2Bzn2BvM9nYHlOC0LTH9A/EEA=; b=mThSlxNsltOENPvCGIiuQQRX2lCh/efSf6/51Jr/HOy0bwowr1dp68TqiQ5CbHBjmH H/zMcYozxmIChwlYhvb1E6BaJsNte4CApxYiNi5Fi9YOB6jhUA5yRqWiuIvhZIjn1EM1 /OuI1J00cghilD9KKcl9rsWcIHrgN3rXo2W57uvV57CHX7l861j5NyB7yRuzkDaSBBjk f1gnguUYedmEHeUfb2PanxU6/vEj+eLloJsNprsSZUO7acWk16Aob0zUjf0btGfXHY1G lvAPaVzLhEoIO0quMav96EwEYRvI6cKilOCKn74ftj6dQgoN1NFjPTLfdrHLhQWTxI0J fe2Q== X-Gm-Message-State: APjAAAXig0mFDnu4MMHRPItgEgV3Vq/QTAfJ+sK3yijMoScMafHMJcqI S2oJTV57XkLRYZZRxicvJ5e7EoOE X-Google-Smtp-Source: APXvYqxkUVQbysCp0r9yWIfoUfymPGWiSpWIKEPr2FcyhlKmKCgodGSk+l5hQBH8JNXYTGwfSuApsQ== X-Received: by 2002:a1c:f416:: with SMTP id z22mr29848831wma.72.1575321005043; Mon, 02 Dec 2019 13:10:05 -0800 (PST) Received: from pavilion.home ([2a02:a456:6be8:1:8edc:d4ff:fe8b:18b7]) by smtp.gmail.com with ESMTPSA id f1sm770859wrp.93.2019.12.02.13.10.03 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 02 Dec 2019 13:10:03 -0800 (PST) From: Niek Linnenbank To: qemu-devel@nongnu.org Subject: [PATCH 09/10] arm: allwinner-h3: add SD/MMC host controller Date: Mon, 2 Dec 2019 22:09:46 +0100 Message-Id: <20191202210947.3603-10-nieklinnenbank@gmail.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20191202210947.3603-1-nieklinnenbank@gmail.com> References: <20191202210947.3603-1-nieklinnenbank@gmail.com> X-detected-operating-system: by eggs.gnu.org: Genre and OS details not recognized. X-Received-From: 2a00:1450:4864:20::342 X-Mailman-Approved-At: Mon, 02 Dec 2019 16:38:24 -0500 X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: b.galvani@gmail.com, peter.maydell@linaro.org, Niek Linnenbank , qemu-arm@nongnu.org Errors-To: qemu-devel-bounces+patchwork-qemu-devel=patchwork.kernel.org@nongnu.org Sender: "Qemu-devel" The Allwinner H3 System on Chip contains an integrated storage controller for Secure Digital (SD) and Multi Media Card (MMC) interfaces. This commit adds support for the Allwinner H3 SD/MMC storage controller with the following emulated features: * DMA transfers * Direct FIFO I/O * Short/Long format command responses * Auto-Stop command (CMD12) * Insert & remove card detection Signed-off-by: Niek Linnenbank --- hw/arm/allwinner-h3.c | 20 + hw/arm/orangepi.c | 17 + hw/sd/Makefile.objs | 1 + hw/sd/allwinner-h3-sdhost.c | 791 ++++++++++++++++++++++++++++ hw/sd/trace-events | 7 + include/hw/arm/allwinner-h3.h | 2 + include/hw/sd/allwinner-h3-sdhost.h | 73 +++ 7 files changed, 911 insertions(+) create mode 100644 hw/sd/allwinner-h3-sdhost.c create mode 100644 include/hw/sd/allwinner-h3-sdhost.h diff --git a/hw/arm/allwinner-h3.c b/hw/arm/allwinner-h3.c index 4fc4c8c725..c2972caf88 100644 --- a/hw/arm/allwinner-h3.c +++ b/hw/arm/allwinner-h3.c @@ -50,6 +50,9 @@ static void aw_h3_init(Object *obj) sysbus_init_child_obj(obj, "sid", &s->sid, sizeof(s->sid), TYPE_AW_H3_SID); + + sysbus_init_child_obj(obj, "mmc0", &s->mmc0, sizeof(s->mmc0), + TYPE_AW_H3_SDHOST); } static void aw_h3_realize(DeviceState *dev, Error **errp) @@ -217,6 +220,23 @@ static void aw_h3_realize(DeviceState *dev, Error **errp) } sysbus_mmio_map(SYS_BUS_DEVICE(&s->sid), 0, AW_H3_SID_BASE); + /* SD/MMC */ + object_property_set_bool(OBJECT(&s->mmc0), true, "realized", &err); + if (err != NULL) { + error_propagate(errp, err); + return; + } + sysbusdev = SYS_BUS_DEVICE(&s->mmc0); + sysbus_mmio_map(sysbusdev, 0, AW_H3_MMC0_BASE); + sysbus_connect_irq(sysbusdev, 0, s->irq[AW_H3_GIC_SPI_MMC0]); + + object_property_add_alias(OBJECT(s), "sd-bus", OBJECT(&s->mmc0), + "sd-bus", &err); + if (err) { + error_propagate(errp, err); + return; + } + /* Universal Serial Bus */ sysbus_create_simple(TYPE_AW_H3_EHCI, AW_H3_EHCI0_BASE, s->irq[AW_H3_GIC_SPI_EHCI0]); diff --git a/hw/arm/orangepi.c b/hw/arm/orangepi.c index 5ef2735f81..dee3efaf08 100644 --- a/hw/arm/orangepi.c +++ b/hw/arm/orangepi.c @@ -39,6 +39,10 @@ typedef struct OrangePiState { static void orangepi_init(MachineState *machine) { OrangePiState *s = g_new(OrangePiState, 1); + DriveInfo *di; + BlockBackend *blk; + BusState *bus; + DeviceState *carddev; Error *err = NULL; s->h3 = AW_H3(object_new(TYPE_AW_H3)); @@ -64,6 +68,18 @@ static void orangepi_init(MachineState *machine) exit(1); } + /* Create and plug in the SD card */ + di = drive_get_next(IF_SD); + blk = di ? blk_by_legacy_dinfo(di) : NULL; + bus = qdev_get_child_bus(DEVICE(s->h3), "sd-bus"); + if (bus == NULL) { + error_report("No SD/MMC found in H3 object"); + exit(1); + } + carddev = qdev_create(bus, TYPE_SD_CARD); + qdev_prop_set_drive(carddev, "drive", blk, &error_fatal); + object_property_set_bool(OBJECT(carddev), true, "realized", &error_fatal); + /* RAM */ memory_region_allocate_system_memory(&s->sdram, NULL, "orangepi.ram", machine->ram_size); @@ -80,6 +96,7 @@ static void orangepi_machine_init(MachineClass *mc) { mc->desc = "Orange Pi PC"; mc->init = orangepi_init; + mc->block_default_type = IF_SD; mc->units_per_default_bus = 1; mc->min_cpus = AW_H3_NUM_CPUS; mc->max_cpus = AW_H3_NUM_CPUS; diff --git a/hw/sd/Makefile.objs b/hw/sd/Makefile.objs index a884c238df..e7cc5ab739 100644 --- a/hw/sd/Makefile.objs +++ b/hw/sd/Makefile.objs @@ -4,6 +4,7 @@ common-obj-$(CONFIG_SD) += sd.o core.o sdmmc-internal.o common-obj-$(CONFIG_SDHCI) += sdhci.o common-obj-$(CONFIG_SDHCI_PCI) += sdhci-pci.o +obj-$(CONFIG_ALLWINNER_H3) += allwinner-h3-sdhost.o obj-$(CONFIG_MILKYMIST) += milkymist-memcard.o obj-$(CONFIG_OMAP) += omap_mmc.o obj-$(CONFIG_PXA2XX) += pxa2xx_mmci.o diff --git a/hw/sd/allwinner-h3-sdhost.c b/hw/sd/allwinner-h3-sdhost.c new file mode 100644 index 0000000000..26e113a144 --- /dev/null +++ b/hw/sd/allwinner-h3-sdhost.c @@ -0,0 +1,791 @@ +/* + * Allwinner H3 SD Host Controller emulation + * + * Copyright (C) 2019 Niek Linnenbank + * + * 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/log.h" +#include "qemu/module.h" +#include "sysemu/blockdev.h" +#include "hw/irq.h" +#include "hw/sd/allwinner-h3-sdhost.h" +#include "migration/vmstate.h" +#include "trace.h" + +#define TYPE_AW_H3_SDHOST_BUS "allwinner-h3-sdhost-bus" +#define AW_H3_SDHOST_BUS(obj) \ + OBJECT_CHECK(SDBus, (obj), TYPE_AW_H3_SDHOST_BUS) + +/* SD Host register offsets */ +#define REG_SD_GCTL (0x00) /* Global Control */ +#define REG_SD_CKCR (0x04) /* Clock Control */ +#define REG_SD_TMOR (0x08) /* Timeout */ +#define REG_SD_BWDR (0x0C) /* Bus Width */ +#define REG_SD_BKSR (0x10) /* Block Size */ +#define REG_SD_BYCR (0x14) /* Byte Count */ +#define REG_SD_CMDR (0x18) /* Command */ +#define REG_SD_CAGR (0x1C) /* Command Argument */ +#define REG_SD_RESP0 (0x20) /* Response Zero */ +#define REG_SD_RESP1 (0x24) /* Response One */ +#define REG_SD_RESP2 (0x28) /* Response Two */ +#define REG_SD_RESP3 (0x2C) /* Response Three */ +#define REG_SD_IMKR (0x30) /* Interrupt Mask */ +#define REG_SD_MISR (0x34) /* Masked Interrupt Status */ +#define REG_SD_RISR (0x38) /* Raw Interrupt Status */ +#define REG_SD_STAR (0x3C) /* Status */ +#define REG_SD_FWLR (0x40) /* FIFO Water Level */ +#define REG_SD_FUNS (0x44) /* FIFO Function Select */ +#define REG_SD_DBGC (0x50) /* Debug Enable */ +#define REG_SD_A12A (0x58) /* Auto command 12 argument */ +#define REG_SD_NTSR (0x5C) /* SD NewTiming Set */ +#define REG_SD_SDBG (0x60) /* SD newTiming Set Debug */ +#define REG_SD_HWRST (0x78) /* Hardware Reset Register */ +#define REG_SD_DMAC (0x80) /* Internal DMA Controller Control */ +#define REG_SD_DLBA (0x84) /* Descriptor List Base Address */ +#define REG_SD_IDST (0x88) /* Internal DMA Controller Status */ +#define REG_SD_IDIE (0x8C) /* Internal DMA Controller IRQ Enable */ +#define REG_SD_THLDC (0x100) /* Card Threshold Control */ +#define REG_SD_DSBD (0x10C) /* eMMC DDR Start Bit Detection Control */ +#define REG_SD_RES_CRC (0x110) /* Response CRC from card/eMMC */ +#define REG_SD_DATA7_CRC (0x114) /* CRC Data 7 from card/eMMC */ +#define REG_SD_DATA6_CRC (0x118) /* CRC Data 6 from card/eMMC */ +#define REG_SD_DATA5_CRC (0x11C) /* CRC Data 5 from card/eMMC */ +#define REG_SD_DATA4_CRC (0x120) /* CRC Data 4 from card/eMMC */ +#define REG_SD_DATA3_CRC (0x124) /* CRC Data 3 from card/eMMC */ +#define REG_SD_DATA2_CRC (0x128) /* CRC Data 2 from card/eMMC */ +#define REG_SD_DATA1_CRC (0x12C) /* CRC Data 1 from card/eMMC */ +#define REG_SD_DATA0_CRC (0x130) /* CRC Data 0 from card/eMMC */ +#define REG_SD_CRC_STA (0x134) /* CRC status from card/eMMC during write */ +#define REG_SD_FIFO (0x200) /* Read/Write FIFO */ + +/* SD Host register flags */ +#define SD_GCTL_FIFO_AC_MOD (1 << 31) +#define SD_GCTL_DDR_MOD_SEL (1 << 10) +#define SD_GCTL_CD_DBC_ENB (1 << 8) +#define SD_GCTL_DMA_ENB (1 << 5) +#define SD_GCTL_INT_ENB (1 << 4) +#define SD_GCTL_DMA_RST (1 << 2) +#define SD_GCTL_FIFO_RST (1 << 1) +#define SD_GCTL_SOFT_RST (1 << 0) + +#define SD_CMDR_LOAD (1 << 31) +#define SD_CMDR_CLKCHANGE (1 << 21) +#define SD_CMDR_WRITE (1 << 10) +#define SD_CMDR_AUTOSTOP (1 << 12) +#define SD_CMDR_DATA (1 << 9) +#define SD_CMDR_RESPONSE_LONG (1 << 7) +#define SD_CMDR_RESPONSE (1 << 6) +#define SD_CMDR_CMDID_MASK (0x3f) + +#define SD_RISR_CARD_REMOVE (1 << 31) +#define SD_RISR_CARD_INSERT (1 << 30) +#define SD_RISR_AUTOCMD_DONE (1 << 14) +#define SD_RISR_DATA_COMPLETE (1 << 3) +#define SD_RISR_CMD_COMPLETE (1 << 2) +#define SD_RISR_NO_RESPONSE (1 << 1) + +#define SD_STAR_CARD_PRESENT (1 << 8) + +#define SD_IDST_SUM_RECEIVE_IRQ (1 << 8) +#define SD_IDST_RECEIVE_IRQ (1 << 1) +#define SD_IDST_TRANSMIT_IRQ (1 << 0) +#define SD_IDST_IRQ_MASK (SD_IDST_RECEIVE_IRQ | SD_IDST_TRANSMIT_IRQ | \ + SD_IDST_SUM_RECEIVE_IRQ) +#define SD_IDST_WR_MASK (0x3ff) + +/* SD Host register reset values */ +#define REG_SD_GCTL_RST (0x00000300) +#define REG_SD_CKCR_RST (0x0) +#define REG_SD_TMOR_RST (0xFFFFFF40) +#define REG_SD_BWDR_RST (0x0) +#define REG_SD_BKSR_RST (0x00000200) +#define REG_SD_BYCR_RST (0x00000200) +#define REG_SD_CMDR_RST (0x0) +#define REG_SD_CAGR_RST (0x0) +#define REG_SD_RESP_RST (0x0) +#define REG_SD_IMKR_RST (0x0) +#define REG_SD_MISR_RST (0x0) +#define REG_SD_RISR_RST (0x0) +#define REG_SD_STAR_RST (0x00000100) +#define REG_SD_FWLR_RST (0x000F0000) +#define REG_SD_FUNS_RST (0x0) +#define REG_SD_DBGC_RST (0x0) +#define REG_SD_A12A_RST (0x0000FFFF) +#define REG_SD_NTSR_RST (0x00000001) +#define REG_SD_SDBG_RST (0x0) +#define REG_SD_HWRST_RST (0x00000001) +#define REG_SD_DMAC_RST (0x0) +#define REG_SD_DLBA_RST (0x0) +#define REG_SD_IDST_RST (0x0) +#define REG_SD_IDIE_RST (0x0) +#define REG_SD_THLDC_RST (0x0) +#define REG_SD_DSBD_RST (0x0) +#define REG_SD_RES_CRC_RST (0x0) +#define REG_SD_DATA_CRC_RST (0x0) +#define REG_SD_CRC_STA_RST (0x0) +#define REG_SD_FIFO_RST (0x0) + +/* Data transfer descriptor for DMA */ +typedef struct TransferDescriptor { + uint32_t status; /* Status flags */ + uint32_t size; /* Data buffer size */ + uint32_t addr; /* Data buffer address */ + uint32_t next; /* Physical address of next descriptor */ +} TransferDescriptor; + +/* Data transfer descriptor flags */ +#define DESC_STATUS_HOLD (1 << 31) /* Set when descriptor is in use by DMA */ +#define DESC_STATUS_ERROR (1 << 30) /* Set when DMA transfer error occurred */ +#define DESC_STATUS_CHAIN (1 << 4) /* Indicates chained descriptor. */ +#define DESC_STATUS_FIRST (1 << 3) /* Set on the first descriptor */ +#define DESC_STATUS_LAST (1 << 2) /* Set on the last descriptor */ +#define DESC_STATUS_NOIRQ (1 << 1) /* Skip raising interrupt after transfer */ + +#define DESC_SIZE_MASK (0xfffffffc) + +static void aw_h3_sdhost_update_irq(AwH3SDHostState *s) +{ + uint32_t irq_en = s->global_ctl & SD_GCTL_INT_ENB; + uint32_t irq = irq_en ? s->irq_status & s->irq_mask : 0; + + trace_aw_h3_sdhost_update_irq(irq); + qemu_set_irq(s->irq, irq); +} + +static void aw_h3_sdhost_update_transfer_cnt(AwH3SDHostState *s, uint32_t bytes) +{ + if (s->transfer_cnt > bytes) { + s->transfer_cnt -= bytes; + } else { + s->transfer_cnt = 0; + } + + if (!s->transfer_cnt) { + s->irq_status |= SD_RISR_DATA_COMPLETE | SD_RISR_AUTOCMD_DONE; + } +} + +static void aw_h3_sdhost_set_inserted(DeviceState *dev, bool inserted) +{ + AwH3SDHostState *s = AW_H3_SDHOST(dev); + + trace_aw_h3_sdhost_set_inserted(inserted); + + if (inserted) { + s->irq_status |= SD_RISR_CARD_INSERT; + s->irq_status &= ~SD_RISR_CARD_REMOVE; + s->status |= SD_STAR_CARD_PRESENT; + } else { + s->irq_status &= ~SD_RISR_CARD_INSERT; + s->irq_status |= SD_RISR_CARD_REMOVE; + s->status &= ~SD_STAR_CARD_PRESENT; + } + + aw_h3_sdhost_update_irq(s); +} + +static void aw_h3_sdhost_send_command(AwH3SDHostState *s) +{ + SDRequest request; + uint8_t resp[16]; + int rlen; + + /* Auto clear load flag */ + s->command &= ~SD_CMDR_LOAD; + + /* Clock change does not actually interact with the SD bus */ + if (!(s->command & SD_CMDR_CLKCHANGE)) { + + /* Prepare request */ + request.cmd = s->command & SD_CMDR_CMDID_MASK; + request.arg = s->command_arg; + + /* Send request to SD bus */ + rlen = sdbus_do_command(&s->sdbus, &request, resp); + if (rlen < 0) { + goto error; + } + + /* If the command has a response, store it in the response registers */ + if ((s->command & SD_CMDR_RESPONSE)) { + if (rlen == 0 || + (rlen == 4 && (s->command & SD_CMDR_RESPONSE_LONG))) { + goto error; + } + if (rlen != 4 && rlen != 16) { + goto error; + } + if (rlen == 4) { + s->response[0] = ldl_be_p(&resp[0]); + s->response[1] = s->response[2] = s->response[3] = 0; + } else { + s->response[0] = ldl_be_p(&resp[12]); + s->response[1] = ldl_be_p(&resp[8]); + s->response[2] = ldl_be_p(&resp[4]); + s->response[3] = ldl_be_p(&resp[0]); + } + } + } + + /* Set interrupt status bits */ + s->irq_status |= SD_RISR_CMD_COMPLETE; + return; + +error: + s->irq_status |= SD_RISR_NO_RESPONSE; +} + +static void aw_h3_sdhost_auto_stop(AwH3SDHostState *s) +{ + /* + * The stop command (CMD12) ensures the SD bus + * returns to the transfer state. + */ + if ((s->command & SD_CMDR_AUTOSTOP) && (s->transfer_cnt == 0)) { + /* First save current command registers */ + uint32_t saved_cmd = s->command; + uint32_t saved_arg = s->command_arg; + + /* Prepare stop command (CMD12) */ + s->command &= ~SD_CMDR_CMDID_MASK; + s->command |= 12; /* CMD12 */ + s->command_arg = 0; + + /* Put the command on SD bus */ + aw_h3_sdhost_send_command(s); + + /* Restore command values */ + s->command = saved_cmd; + s->command_arg = saved_arg; + } +} + +static uint32_t aw_h3_sdhost_process_desc(AwH3SDHostState *s, + hwaddr desc_addr, + TransferDescriptor *desc, + bool is_write, uint32_t max_bytes) +{ + uint32_t num_done = 0; + uint32_t num_bytes = max_bytes; + uint8_t buf[1024]; + + /* Read descriptor */ + cpu_physical_memory_read(desc_addr, desc, sizeof(*desc)); + if (desc->size == 0) { + desc->size = 0xffff + 1; + } + if (desc->size < num_bytes) { + num_bytes = desc->size; + } + + trace_aw_h3_sdhost_process_desc(desc_addr, desc->size, is_write, max_bytes); + + while (num_done < num_bytes) { + /* Try to completely fill the local buffer */ + uint32_t buf_bytes = num_bytes - num_done; + if (buf_bytes > sizeof(buf)) { + buf_bytes = sizeof(buf); + } + + /* Write to SD bus */ + if (is_write) { + cpu_physical_memory_read((desc->addr & DESC_SIZE_MASK) + num_done, + buf, buf_bytes); + + for (uint32_t i = 0; i < buf_bytes; i++) { + sdbus_write_data(&s->sdbus, buf[i]); + } + + /* Read from SD bus */ + } else { + for (uint32_t i = 0; i < buf_bytes; i++) { + buf[i] = sdbus_read_data(&s->sdbus); + } + cpu_physical_memory_write((desc->addr & DESC_SIZE_MASK) + num_done, + buf, buf_bytes); + } + num_done += buf_bytes; + } + + /* Clear hold flag and flush descriptor */ + desc->status &= ~DESC_STATUS_HOLD; + cpu_physical_memory_write(desc_addr, desc, sizeof(*desc)); + + return num_done; +} + +static void aw_h3_sdhost_dma(AwH3SDHostState *s) +{ + TransferDescriptor desc; + hwaddr desc_addr = s->desc_base; + bool is_write = (s->command & SD_CMDR_WRITE); + uint32_t bytes_done = 0; + + /* Check if DMA can be performed */ + if (s->byte_count == 0 || s->block_size == 0 || + !(s->global_ctl & SD_GCTL_DMA_ENB)) { + return; + } + + /* + * For read operations, data must be available on the SD bus + * If not, it is an error and we should not act at all + */ + if (!is_write && !sdbus_data_ready(&s->sdbus)) { + return; + } + + /* Process the DMA descriptors until all data is copied */ + while (s->byte_count > 0) { + bytes_done = aw_h3_sdhost_process_desc(s, desc_addr, &desc, + is_write, s->byte_count); + aw_h3_sdhost_update_transfer_cnt(s, bytes_done); + + if (bytes_done <= s->byte_count) { + s->byte_count -= bytes_done; + } else { + s->byte_count = 0; + } + + if (desc.status & DESC_STATUS_LAST) { + break; + } else { + desc_addr = desc.next; + } + } + + /* Raise IRQ to signal DMA is completed */ + s->irq_status |= SD_RISR_DATA_COMPLETE | SD_RISR_AUTOCMD_DONE; + + /* Update DMAC bits */ + if (is_write) { + s->dmac_status |= SD_IDST_TRANSMIT_IRQ; + } else { + s->dmac_status |= (SD_IDST_SUM_RECEIVE_IRQ | SD_IDST_RECEIVE_IRQ); + } +} + +static uint64_t aw_h3_sdhost_read(void *opaque, hwaddr offset, + unsigned size) +{ + AwH3SDHostState *s = (AwH3SDHostState *)opaque; + uint32_t res = 0; + + switch (offset) { + case REG_SD_GCTL: /* Global Control */ + res = s->global_ctl; + break; + case REG_SD_CKCR: /* Clock Control */ + res = s->clock_ctl; + break; + case REG_SD_TMOR: /* Timeout */ + res = s->timeout; + break; + case REG_SD_BWDR: /* Bus Width */ + res = s->bus_width; + break; + case REG_SD_BKSR: /* Block Size */ + res = s->block_size; + break; + case REG_SD_BYCR: /* Byte Count */ + res = s->byte_count; + break; + case REG_SD_CMDR: /* Command */ + res = s->command; + break; + case REG_SD_CAGR: /* Command Argument */ + res = s->command_arg; + break; + case REG_SD_RESP0: /* Response Zero */ + res = s->response[0]; + break; + case REG_SD_RESP1: /* Response One */ + res = s->response[1]; + break; + case REG_SD_RESP2: /* Response Two */ + res = s->response[2]; + break; + case REG_SD_RESP3: /* Response Three */ + res = s->response[3]; + break; + case REG_SD_IMKR: /* Interrupt Mask */ + res = s->irq_mask; + break; + case REG_SD_MISR: /* Masked Interrupt Status */ + res = s->irq_status & s->irq_mask; + break; + case REG_SD_RISR: /* Raw Interrupt Status */ + res = s->irq_status; + break; + case REG_SD_STAR: /* Status */ + res = s->status; + break; + case REG_SD_FWLR: /* FIFO Water Level */ + res = s->fifo_wlevel; + break; + case REG_SD_FUNS: /* FIFO Function Select */ + res = s->fifo_func_sel; + break; + case REG_SD_DBGC: /* Debug Enable */ + res = s->debug_enable; + break; + case REG_SD_A12A: /* Auto command 12 argument */ + res = s->auto12_arg; + break; + case REG_SD_NTSR: /* SD NewTiming Set */ + res = s->newtiming_set; + break; + case REG_SD_SDBG: /* SD newTiming Set Debug */ + res = s->newtiming_debug; + break; + case REG_SD_HWRST: /* Hardware Reset Register */ + res = s->hardware_rst; + break; + case REG_SD_DMAC: /* Internal DMA Controller Control */ + res = s->dmac; + break; + case REG_SD_DLBA: /* Descriptor List Base Address */ + res = s->desc_base; + break; + case REG_SD_IDST: /* Internal DMA Controller Status */ + res = s->dmac_status; + break; + case REG_SD_IDIE: /* Internal DMA Controller Interrupt Enable */ + res = s->dmac_irq; + break; + case REG_SD_THLDC: /* Card Threshold Control */ + res = s->card_threshold; + break; + case REG_SD_DSBD: /* eMMC DDR Start Bit Detection Control */ + res = s->startbit_detect; + break; + case REG_SD_RES_CRC: /* Response CRC from card/eMMC */ + res = s->response_crc; + break; + case REG_SD_DATA7_CRC: /* CRC Data 7 from card/eMMC */ + case REG_SD_DATA6_CRC: /* CRC Data 6 from card/eMMC */ + case REG_SD_DATA5_CRC: /* CRC Data 5 from card/eMMC */ + case REG_SD_DATA4_CRC: /* CRC Data 4 from card/eMMC */ + case REG_SD_DATA3_CRC: /* CRC Data 3 from card/eMMC */ + case REG_SD_DATA2_CRC: /* CRC Data 2 from card/eMMC */ + case REG_SD_DATA1_CRC: /* CRC Data 1 from card/eMMC */ + case REG_SD_DATA0_CRC: /* CRC Data 0 from card/eMMC */ + res = s->data_crc[((offset - REG_SD_DATA7_CRC) / sizeof(uint32_t))]; + break; + case REG_SD_CRC_STA: /* CRC status from card/eMMC in write operation */ + res = s->status_crc; + break; + case REG_SD_FIFO: /* Read/Write FIFO */ + if (sdbus_data_ready(&s->sdbus)) { + res = sdbus_read_data(&s->sdbus); + res |= sdbus_read_data(&s->sdbus) << 8; + res |= sdbus_read_data(&s->sdbus) << 16; + res |= sdbus_read_data(&s->sdbus) << 24; + aw_h3_sdhost_update_transfer_cnt(s, sizeof(uint32_t)); + aw_h3_sdhost_auto_stop(s); + aw_h3_sdhost_update_irq(s); + } else { + qemu_log_mask(LOG_GUEST_ERROR, "%s: no data ready on SD bus\n", + __func__); + } + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %"HWADDR_PRIx"\n", + __func__, offset); + res = 0; + break; + } + + trace_aw_h3_sdhost_read(offset, res, size); + return res; +} + +static void aw_h3_sdhost_write(void *opaque, hwaddr offset, + uint64_t value, unsigned size) +{ + AwH3SDHostState *s = (AwH3SDHostState *)opaque; + + trace_aw_h3_sdhost_write(offset, value, size); + + switch (offset) { + case REG_SD_GCTL: /* Global Control */ + s->global_ctl = value; + s->global_ctl &= ~(SD_GCTL_DMA_RST | SD_GCTL_FIFO_RST | + SD_GCTL_SOFT_RST); + aw_h3_sdhost_update_irq(s); + break; + case REG_SD_CKCR: /* Clock Control */ + s->clock_ctl = value; + break; + case REG_SD_TMOR: /* Timeout */ + s->timeout = value; + break; + case REG_SD_BWDR: /* Bus Width */ + s->bus_width = value; + break; + case REG_SD_BKSR: /* Block Size */ + s->block_size = value; + break; + case REG_SD_BYCR: /* Byte Count */ + s->byte_count = value; + s->transfer_cnt = value; + break; + case REG_SD_CMDR: /* Command */ + s->command = value; + if (value & SD_CMDR_LOAD) { + aw_h3_sdhost_send_command(s); + aw_h3_sdhost_dma(s); + aw_h3_sdhost_auto_stop(s); + } + aw_h3_sdhost_update_irq(s); + break; + case REG_SD_CAGR: /* Command Argument */ + s->command_arg = value; + break; + case REG_SD_RESP0: /* Response Zero */ + s->response[0] = value; + break; + case REG_SD_RESP1: /* Response One */ + s->response[1] = value; + break; + case REG_SD_RESP2: /* Response Two */ + s->response[2] = value; + break; + case REG_SD_RESP3: /* Response Three */ + s->response[3] = value; + break; + case REG_SD_IMKR: /* Interrupt Mask */ + s->irq_mask = value; + aw_h3_sdhost_update_irq(s); + break; + case REG_SD_MISR: /* Masked Interrupt Status */ + case REG_SD_RISR: /* Raw Interrupt Status */ + s->irq_status &= ~value; + aw_h3_sdhost_update_irq(s); + break; + case REG_SD_STAR: /* Status */ + s->status &= ~value; + aw_h3_sdhost_update_irq(s); + break; + case REG_SD_FWLR: /* FIFO Water Level */ + s->fifo_wlevel = value; + break; + case REG_SD_FUNS: /* FIFO Function Select */ + s->fifo_func_sel = value; + break; + case REG_SD_DBGC: /* Debug Enable */ + s->debug_enable = value; + break; + case REG_SD_A12A: /* Auto command 12 argument */ + s->auto12_arg = value; + break; + case REG_SD_NTSR: /* SD NewTiming Set */ + s->newtiming_set = value; + break; + case REG_SD_SDBG: /* SD newTiming Set Debug */ + s->newtiming_debug = value; + break; + case REG_SD_HWRST: /* Hardware Reset Register */ + s->hardware_rst = value; + break; + case REG_SD_DMAC: /* Internal DMA Controller Control */ + s->dmac = value; + aw_h3_sdhost_update_irq(s); + break; + case REG_SD_DLBA: /* Descriptor List Base Address */ + s->desc_base = value; + break; + case REG_SD_IDST: /* Internal DMA Controller Status */ + s->dmac_status &= (~SD_IDST_WR_MASK) | (~value & SD_IDST_WR_MASK); + aw_h3_sdhost_update_irq(s); + break; + case REG_SD_IDIE: /* Internal DMA Controller Interrupt Enable */ + s->dmac_irq = value; + aw_h3_sdhost_update_irq(s); + break; + case REG_SD_THLDC: /* Card Threshold Control */ + s->card_threshold = value; + break; + case REG_SD_DSBD: /* eMMC DDR Start Bit Detection Control */ + s->startbit_detect = value; + break; + case REG_SD_FIFO: /* Read/Write FIFO */ + sdbus_write_data(&s->sdbus, value & 0xff); + sdbus_write_data(&s->sdbus, (value >> 8) & 0xff); + sdbus_write_data(&s->sdbus, (value >> 16) & 0xff); + sdbus_write_data(&s->sdbus, (value >> 24) & 0xff); + aw_h3_sdhost_update_transfer_cnt(s, sizeof(uint32_t)); + aw_h3_sdhost_auto_stop(s); + aw_h3_sdhost_update_irq(s); + break; + case REG_SD_RES_CRC: /* Response CRC from card/eMMC */ + case REG_SD_DATA7_CRC: /* CRC Data 7 from card/eMMC */ + case REG_SD_DATA6_CRC: /* CRC Data 6 from card/eMMC */ + case REG_SD_DATA5_CRC: /* CRC Data 5 from card/eMMC */ + case REG_SD_DATA4_CRC: /* CRC Data 4 from card/eMMC */ + case REG_SD_DATA3_CRC: /* CRC Data 3 from card/eMMC */ + case REG_SD_DATA2_CRC: /* CRC Data 2 from card/eMMC */ + case REG_SD_DATA1_CRC: /* CRC Data 1 from card/eMMC */ + case REG_SD_DATA0_CRC: /* CRC Data 0 from card/eMMC */ + case REG_SD_CRC_STA: /* CRC status from card/eMMC in write operation */ + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %"HWADDR_PRIx"\n", + __func__, offset); + break; + } +} + +static const MemoryRegionOps aw_h3_sdhost_ops = { + .read = aw_h3_sdhost_read, + .write = aw_h3_sdhost_write, + .endianness = DEVICE_NATIVE_ENDIAN, +}; + +static const VMStateDescription vmstate_aw_h3_sdhost = { + .name = TYPE_AW_H3_SDHOST, + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32(global_ctl, AwH3SDHostState), + VMSTATE_UINT32(clock_ctl, AwH3SDHostState), + VMSTATE_UINT32(timeout, AwH3SDHostState), + VMSTATE_UINT32(bus_width, AwH3SDHostState), + VMSTATE_UINT32(block_size, AwH3SDHostState), + VMSTATE_UINT32(byte_count, AwH3SDHostState), + VMSTATE_UINT32(transfer_cnt, AwH3SDHostState), + VMSTATE_UINT32(command, AwH3SDHostState), + VMSTATE_UINT32(command_arg, AwH3SDHostState), + VMSTATE_UINT32_ARRAY(response, AwH3SDHostState, 4), + VMSTATE_UINT32(irq_mask, AwH3SDHostState), + VMSTATE_UINT32(irq_status, AwH3SDHostState), + VMSTATE_UINT32(status, AwH3SDHostState), + VMSTATE_UINT32(fifo_wlevel, AwH3SDHostState), + VMSTATE_UINT32(fifo_func_sel, AwH3SDHostState), + VMSTATE_UINT32(debug_enable, AwH3SDHostState), + VMSTATE_UINT32(auto12_arg, AwH3SDHostState), + VMSTATE_UINT32(newtiming_set, AwH3SDHostState), + VMSTATE_UINT32(newtiming_debug, AwH3SDHostState), + VMSTATE_UINT32(hardware_rst, AwH3SDHostState), + VMSTATE_UINT32(dmac, AwH3SDHostState), + VMSTATE_UINT32(desc_base, AwH3SDHostState), + VMSTATE_UINT32(dmac_status, AwH3SDHostState), + VMSTATE_UINT32(dmac_irq, AwH3SDHostState), + VMSTATE_UINT32(card_threshold, AwH3SDHostState), + VMSTATE_UINT32(startbit_detect, AwH3SDHostState), + VMSTATE_UINT32(response_crc, AwH3SDHostState), + VMSTATE_UINT32_ARRAY(data_crc, AwH3SDHostState, 8), + VMSTATE_UINT32(status_crc, AwH3SDHostState), + VMSTATE_END_OF_LIST() + } +}; + +static void aw_h3_sdhost_init(Object *obj) +{ + AwH3SDHostState *s = AW_H3_SDHOST(obj); + + qbus_create_inplace(&s->sdbus, sizeof(s->sdbus), + TYPE_AW_H3_SDHOST_BUS, DEVICE(s), "sd-bus"); + + memory_region_init_io(&s->iomem, obj, &aw_h3_sdhost_ops, s, + TYPE_AW_H3_SDHOST, AW_H3_SDHOST_REGS_MEM_SIZE); + sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->iomem); + sysbus_init_irq(SYS_BUS_DEVICE(s), &s->irq); +} + +static void aw_h3_sdhost_reset(DeviceState *dev) +{ + AwH3SDHostState *s = AW_H3_SDHOST(dev); + + s->global_ctl = REG_SD_GCTL_RST; + s->clock_ctl = REG_SD_CKCR_RST; + s->timeout = REG_SD_TMOR_RST; + s->bus_width = REG_SD_BWDR_RST; + s->block_size = REG_SD_BKSR_RST; + s->byte_count = REG_SD_BYCR_RST; + s->transfer_cnt = 0; + + s->command = REG_SD_CMDR_RST; + s->command_arg = REG_SD_CAGR_RST; + + for (int i = 0; i < sizeof(s->response) / sizeof(s->response[0]); i++) { + s->response[i] = REG_SD_RESP_RST; + } + + s->irq_mask = REG_SD_IMKR_RST; + s->irq_status = REG_SD_RISR_RST; + s->status = REG_SD_STAR_RST; + + s->fifo_wlevel = REG_SD_FWLR_RST; + s->fifo_func_sel = REG_SD_FUNS_RST; + s->debug_enable = REG_SD_DBGC_RST; + s->auto12_arg = REG_SD_A12A_RST; + s->newtiming_set = REG_SD_NTSR_RST; + s->newtiming_debug = REG_SD_SDBG_RST; + s->hardware_rst = REG_SD_HWRST_RST; + s->dmac = REG_SD_DMAC_RST; + s->desc_base = REG_SD_DLBA_RST; + s->dmac_status = REG_SD_IDST_RST; + s->dmac_irq = REG_SD_IDIE_RST; + s->card_threshold = REG_SD_THLDC_RST; + s->startbit_detect = REG_SD_DSBD_RST; + s->response_crc = REG_SD_RES_CRC_RST; + + for (int i = 0; i < sizeof(s->data_crc) / sizeof(s->data_crc[0]); i++) { + s->data_crc[i] = REG_SD_DATA_CRC_RST; + } + + s->status_crc = REG_SD_CRC_STA_RST; +} + +static void aw_h3_sdhost_bus_class_init(ObjectClass *klass, void *data) +{ + SDBusClass *sbc = SD_BUS_CLASS(klass); + + sbc->set_inserted = aw_h3_sdhost_set_inserted; +} + +static void aw_h3_sdhost_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->reset = aw_h3_sdhost_reset; + dc->vmsd = &vmstate_aw_h3_sdhost; +} + +static TypeInfo aw_h3_sdhost_info = { + .name = TYPE_AW_H3_SDHOST, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(AwH3SDHostState), + .class_init = aw_h3_sdhost_class_init, + .instance_init = aw_h3_sdhost_init, +}; + +static const TypeInfo aw_h3_sdhost_bus_info = { + .name = TYPE_AW_H3_SDHOST_BUS, + .parent = TYPE_SD_BUS, + .instance_size = sizeof(SDBus), + .class_init = aw_h3_sdhost_bus_class_init, +}; + +static void aw_h3_sdhost_register_types(void) +{ + type_register_static(&aw_h3_sdhost_info); + type_register_static(&aw_h3_sdhost_bus_info); +} + +type_init(aw_h3_sdhost_register_types) diff --git a/hw/sd/trace-events b/hw/sd/trace-events index efcff666a2..c672a201b5 100644 --- a/hw/sd/trace-events +++ b/hw/sd/trace-events @@ -1,5 +1,12 @@ # See docs/devel/tracing.txt for syntax documentation. +# allwinner-h3-sdhost.c +aw_h3_sdhost_set_inserted(bool inserted) "inserted %u" +aw_h3_sdhost_process_desc(uint64_t desc_addr, uint32_t desc_size, bool is_write, uint32_t max_bytes) "desc_addr 0x%" PRIx64 " desc_size %u is_write %u max_bytes %u" +aw_h3_sdhost_read(uint64_t offset, uint64_t data, unsigned size) "offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u" +aw_h3_sdhost_write(uint64_t offset, uint64_t data, unsigned size) "offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u" +aw_h3_sdhost_update_irq(uint32_t irq) "IRQ bits 0x%x" + # bcm2835_sdhost.c bcm2835_sdhost_read(uint64_t offset, uint64_t data, unsigned size) "offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u" bcm2835_sdhost_write(uint64_t offset, uint64_t data, unsigned size) "offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u" diff --git a/include/hw/arm/allwinner-h3.h b/include/hw/arm/allwinner-h3.h index 33602599eb..7aff4ebbd2 100644 --- a/include/hw/arm/allwinner-h3.h +++ b/include/hw/arm/allwinner-h3.h @@ -30,6 +30,7 @@ #include "hw/misc/allwinner-h3-cpucfg.h" #include "hw/misc/allwinner-h3-syscon.h" #include "hw/misc/allwinner-h3-sid.h" +#include "hw/sd/allwinner-h3-sdhost.h" #include "target/arm/cpu.h" #define AW_H3_SRAM_A1_BASE (0x00000000) @@ -117,6 +118,7 @@ typedef struct AwH3State { AwH3CpuCfgState cpucfg; AwH3SysconState syscon; AwH3SidState sid; + AwH3SDHostState mmc0; GICState gic; MemoryRegion sram_a1; MemoryRegion sram_a2; diff --git a/include/hw/sd/allwinner-h3-sdhost.h b/include/hw/sd/allwinner-h3-sdhost.h new file mode 100644 index 0000000000..6c898a3c84 --- /dev/null +++ b/include/hw/sd/allwinner-h3-sdhost.h @@ -0,0 +1,73 @@ +/* + * Allwinner H3 SD Host Controller emulation + * + * Copyright (C) 2019 Niek Linnenbank + * + * 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 ALLWINNER_H3_SDHOST_H +#define ALLWINNER_H3_SDHOST_H + +#include "hw/sysbus.h" +#include "hw/sd/sd.h" + +#define AW_H3_SDHOST_REGS_MEM_SIZE (1024) + +#define TYPE_AW_H3_SDHOST "allwinner-h3-sdhost" +#define AW_H3_SDHOST(obj) \ + OBJECT_CHECK(AwH3SDHostState, (obj), TYPE_AW_H3_SDHOST) + +typedef struct { + SysBusDevice busdev; + SDBus sdbus; + MemoryRegion iomem; + + uint32_t global_ctl; + uint32_t clock_ctl; + uint32_t timeout; + uint32_t bus_width; + uint32_t block_size; + uint32_t byte_count; + uint32_t transfer_cnt; + + uint32_t command; + uint32_t command_arg; + uint32_t response[4]; + + uint32_t irq_mask; + uint32_t irq_status; + uint32_t status; + + uint32_t fifo_wlevel; + uint32_t fifo_func_sel; + uint32_t debug_enable; + uint32_t auto12_arg; + uint32_t newtiming_set; + uint32_t newtiming_debug; + uint32_t hardware_rst; + uint32_t dmac; + uint32_t desc_base; + uint32_t dmac_status; + uint32_t dmac_irq; + uint32_t card_threshold; + uint32_t startbit_detect; + uint32_t response_crc; + uint32_t data_crc[8]; + uint32_t status_crc; + + qemu_irq irq; +} AwH3SDHostState; + +#endif From patchwork Mon Dec 2 21:09:47 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Niek Linnenbank X-Patchwork-Id: 11269955 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id D2F61138D for ; Mon, 2 Dec 2019 21:47:38 +0000 (UTC) Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id 994782071F for ; Mon, 2 Dec 2019 21:47:38 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=fail reason="signature verification failed" (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="L7ZszqUz" DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org 994782071F Authentication-Results: mail.kernel.org; dmarc=fail (p=none dis=none) header.from=gmail.com Authentication-Results: mail.kernel.org; spf=pass smtp.mailfrom=qemu-devel-bounces+patchwork-qemu-devel=patchwork.kernel.org@nongnu.org Received: from localhost ([::1]:44920 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1ibtXd-0003zu-J8 for patchwork-qemu-devel@patchwork.kernel.org; Mon, 02 Dec 2019 16:47:37 -0500 Received: from eggs.gnu.org ([2001:470:142:3::10]:38280) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1ibsxg-0002hs-Uy for qemu-devel@nongnu.org; Mon, 02 Dec 2019 16:10:32 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1ibsxa-0000jx-6P for qemu-devel@nongnu.org; Mon, 02 Dec 2019 16:10:27 -0500 Received: from mail-wr1-x441.google.com ([2a00:1450:4864:20::441]:41607) by eggs.gnu.org with esmtps (TLS1.0:RSA_AES_128_CBC_SHA1:16) (Exim 4.71) (envelope-from ) id 1ibsxQ-0000b8-Pf; Mon, 02 Dec 2019 16:10:13 -0500 Received: by mail-wr1-x441.google.com with SMTP id b18so983379wrj.8; Mon, 02 Dec 2019 13:10:07 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=YSSdKKG1i6JVSlPytRsAwGL4WTE2dxtGdKjtspalJFE=; b=L7ZszqUzkHcluseCQLaWzNFJkI+6teYGz8kuF4ZzyMxGNDrQjkB1q3yD4lB0aSz1t2 4WgoAtsG1TqhLzlPY6scu4WFW5vve946/3lHfjOcecVoDicSS53/rs7DIXcFQbEW+smO HoGj+UsxG+J/smctlkiTJnmBHksNt9KsnixK4M34VWwG9hrCbW/UO8LoCofv+6iB2pGs fmUecTmhJf5HdwFMGVdat0iCixJRq6nlFChReialxQJmlf8prJJxVlJiqzqivWEKkBI8 VWTkyLJb+olapWmqYemz1GpeTCJdfVrAN8Z9LT8BzMO6dfw7GDhUgJLLZCH47Cv7KOfw 5XNg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=YSSdKKG1i6JVSlPytRsAwGL4WTE2dxtGdKjtspalJFE=; b=FUYpHTd04WBfPD0Tzcr5h10VBlV+I6VcmDPSgmnGvV2rmw7DEhA8Q8do2R98IE5K4c vdzhLsbKmOSlsNuTocWq96VCD6oojdDlZ5fIDm2NJITE8NcJIQmBmKc3uGu85T22yXjr y5mCmkk7SVc2jL4DALsN/a051GEVPlQzLnF/excNYXE+XjZxCelPq1nN3RfY+Dtkcccs 8Zek+8Qmge7IvmAskSXT9Xkgrj0qWLJcLaPE5xmK5cmD1Dle+/QHrLaA+8Iiv2WjqPBQ +2Busx4fmHKuFx7fUVHHdGWcy2obLDmRlqODaG3LhMCarB5F/PgRUt19+C07TptPiAO2 Oitg== X-Gm-Message-State: APjAAAUIYTTcBSU9aMyCibpqxqdXHItBbH25brEIH2csRGgSHICmW9DL F0ml87b7K6lud4+9pj0zXIp15fQ4 X-Google-Smtp-Source: APXvYqwQkNFAKM4YhPm2hslVoloXVTCFDw47qe/cJqMSf2UxnufyFhxJppZZ3oE4wZQwnsa3gqhQ8g== X-Received: by 2002:adf:edd0:: with SMTP id v16mr1112264wro.310.1575321005843; Mon, 02 Dec 2019 13:10:05 -0800 (PST) Received: from pavilion.home ([2a02:a456:6be8:1:8edc:d4ff:fe8b:18b7]) by smtp.gmail.com with ESMTPSA id f1sm770859wrp.93.2019.12.02.13.10.05 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 02 Dec 2019 13:10:05 -0800 (PST) From: Niek Linnenbank To: qemu-devel@nongnu.org Subject: [PATCH 10/10] arm: allwinner-h3: add EMAC ethernet device Date: Mon, 2 Dec 2019 22:09:47 +0100 Message-Id: <20191202210947.3603-11-nieklinnenbank@gmail.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20191202210947.3603-1-nieklinnenbank@gmail.com> References: <20191202210947.3603-1-nieklinnenbank@gmail.com> X-detected-operating-system: by eggs.gnu.org: Genre and OS details not recognized. X-Received-From: 2a00:1450:4864:20::441 X-Mailman-Approved-At: Mon, 02 Dec 2019 16:38:22 -0500 X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: b.galvani@gmail.com, peter.maydell@linaro.org, Niek Linnenbank , qemu-arm@nongnu.org Errors-To: qemu-devel-bounces+patchwork-qemu-devel=patchwork.kernel.org@nongnu.org Sender: "Qemu-devel" The Allwinner H3 System on Chip includes an Ethernet MAC (EMAC) which provides 10M/100M/1000M Ethernet connectivity. This commit adds support for the Allwinner H3 EMAC, including emulation for the following functionality: * DMA transfers * MII interface * Transmit CRC calculation Signed-off-by: Niek Linnenbank --- hw/arm/Kconfig | 1 + hw/arm/allwinner-h3.c | 17 + hw/arm/orangepi.c | 7 + hw/net/Kconfig | 3 + hw/net/Makefile.objs | 1 + hw/net/allwinner-h3-emac.c | 786 +++++++++++++++++++++++++++++ hw/net/trace-events | 10 + include/hw/arm/allwinner-h3.h | 2 + include/hw/net/allwinner-h3-emac.h | 69 +++ 9 files changed, 896 insertions(+) create mode 100644 hw/net/allwinner-h3-emac.c create mode 100644 include/hw/net/allwinner-h3-emac.h diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig index ebf8d2325f..551cff3442 100644 --- a/hw/arm/Kconfig +++ b/hw/arm/Kconfig @@ -294,6 +294,7 @@ config ALLWINNER_A10 config ALLWINNER_H3 bool select ALLWINNER_A10_PIT + select ALLWINNER_H3_EMAC select SERIAL select ARM_TIMER select ARM_GIC diff --git a/hw/arm/allwinner-h3.c b/hw/arm/allwinner-h3.c index c2972caf88..274b8548c0 100644 --- a/hw/arm/allwinner-h3.c +++ b/hw/arm/allwinner-h3.c @@ -53,6 +53,9 @@ static void aw_h3_init(Object *obj) sysbus_init_child_obj(obj, "mmc0", &s->mmc0, sizeof(s->mmc0), TYPE_AW_H3_SDHOST); + + sysbus_init_child_obj(obj, "emac", &s->emac, sizeof(s->emac), + TYPE_AW_H3_EMAC); } static void aw_h3_realize(DeviceState *dev, Error **errp) @@ -237,6 +240,20 @@ static void aw_h3_realize(DeviceState *dev, Error **errp) return; } + /* EMAC */ + if (nd_table[0].used) { + qemu_check_nic_model(&nd_table[0], TYPE_AW_H3_EMAC); + qdev_set_nic_properties(DEVICE(&s->emac), &nd_table[0]); + } + object_property_set_bool(OBJECT(&s->emac), true, "realized", &err); + if (err != NULL) { + error_propagate(errp, err); + return; + } + sysbusdev = SYS_BUS_DEVICE(&s->emac); + sysbus_mmio_map(sysbusdev, 0, AW_H3_EMAC_BASE); + sysbus_connect_irq(sysbusdev, 0, s->irq[AW_H3_GIC_SPI_EMAC]); + /* Universal Serial Bus */ sysbus_create_simple(TYPE_AW_H3_EHCI, AW_H3_EHCI0_BASE, s->irq[AW_H3_GIC_SPI_EHCI0]); diff --git a/hw/arm/orangepi.c b/hw/arm/orangepi.c index dee3efaf08..8a61eb0e69 100644 --- a/hw/arm/orangepi.c +++ b/hw/arm/orangepi.c @@ -61,6 +61,13 @@ static void orangepi_init(MachineState *machine) exit(1); } + /* Setup EMAC properties */ + object_property_set_int(OBJECT(&s->h3->emac), 1, "phy-addr", &err); + if (err != NULL) { + error_reportf_err(err, "Couldn't set phy address: "); + exit(1); + } + /* Mark H3 object realized */ object_property_set_bool(OBJECT(s->h3), true, "realized", &err); if (err != NULL) { diff --git a/hw/net/Kconfig b/hw/net/Kconfig index 3856417d42..36d3923992 100644 --- a/hw/net/Kconfig +++ b/hw/net/Kconfig @@ -74,6 +74,9 @@ config MIPSNET config ALLWINNER_EMAC bool +config ALLWINNER_H3_EMAC + bool + config IMX_FEC bool diff --git a/hw/net/Makefile.objs b/hw/net/Makefile.objs index 7907d2c199..5548deb07a 100644 --- a/hw/net/Makefile.objs +++ b/hw/net/Makefile.objs @@ -23,6 +23,7 @@ common-obj-$(CONFIG_XGMAC) += xgmac.o common-obj-$(CONFIG_MIPSNET) += mipsnet.o common-obj-$(CONFIG_XILINX_AXI) += xilinx_axienet.o common-obj-$(CONFIG_ALLWINNER_EMAC) += allwinner_emac.o +common-obj-$(CONFIG_ALLWINNER_H3_EMAC) += allwinner-h3-emac.o common-obj-$(CONFIG_IMX_FEC) += imx_fec.o common-obj-$(CONFIG_CADENCE) += cadence_gem.o diff --git a/hw/net/allwinner-h3-emac.c b/hw/net/allwinner-h3-emac.c new file mode 100644 index 0000000000..37f6f44406 --- /dev/null +++ b/hw/net/allwinner-h3-emac.c @@ -0,0 +1,786 @@ +/* + * Allwinner H3 EMAC emulation + * + * Copyright (C) 2019 Niek Linnenbank + * + * 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 "hw/sysbus.h" +#include "migration/vmstate.h" +#include "net/net.h" +#include "hw/irq.h" +#include "hw/qdev-properties.h" +#include "qemu/log.h" +#include "trace.h" +#include "net/checksum.h" +#include "qemu/module.h" +#include "exec/cpu-common.h" +#include "hw/net/allwinner-h3-emac.h" + +/* EMAC register offsets */ +#define REG_BASIC_CTL_0 (0x0000) /* Basic Control 0 */ +#define REG_BASIC_CTL_1 (0x0004) /* Basic Control 1 */ +#define REG_INT_STA (0x0008) /* Interrupt Status */ +#define REG_INT_EN (0x000C) /* Interrupt Enable */ +#define REG_TX_CTL_0 (0x0010) /* Transmit Control 0 */ +#define REG_TX_CTL_1 (0x0014) /* Transmit Control 1 */ +#define REG_TX_FLOW_CTL (0x001C) /* Transmit Flow Control */ +#define REG_TX_DMA_DESC_LIST (0x0020) /* Transmit Descriptor List Address */ +#define REG_RX_CTL_0 (0x0024) /* Receive Control 0 */ +#define REG_RX_CTL_1 (0x0028) /* Receive Control 1 */ +#define REG_RX_DMA_DESC_LIST (0x0034) /* Receive Descriptor List Address */ +#define REG_FRM_FLT (0x0038) /* Receive Frame Filter */ +#define REG_RX_HASH_0 (0x0040) /* Receive Hash Table 0 */ +#define REG_RX_HASH_1 (0x0044) /* Receive Hash Table 1 */ +#define REG_MII_CMD (0x0048) /* Management Interface Command */ +#define REG_MII_DATA (0x004C) /* Management Interface Data */ +#define REG_ADDR_HIGH (0x0050) /* MAC Address High */ +#define REG_ADDR_LOW (0x0054) /* MAC Address Low */ +#define REG_TX_DMA_STA (0x00B0) /* Transmit DMA Status */ +#define REG_TX_CUR_DESC (0x00B4) /* Transmit Current Descriptor */ +#define REG_TX_CUR_BUF (0x00B8) /* Transmit Current Buffer */ +#define REG_RX_DMA_STA (0x00C0) /* Receive DMA Status */ +#define REG_RX_CUR_DESC (0x00C4) /* Receive Current Descriptor */ +#define REG_RX_CUR_BUF (0x00C8) /* Receive Current Buffer */ +#define REG_RGMII_STA (0x00D0) /* RGMII Status */ + +/* EMAC register flags */ +#define BASIC_CTL0_100Mbps (0b11 << 2) +#define BASIC_CTL0_FD (1 << 0) +#define BASIC_CTL1_SOFTRST (1 << 0) + +#define INT_STA_RGMII_LINK (1 << 16) +#define INT_STA_RX_EARLY (1 << 13) +#define INT_STA_RX_OVERFLOW (1 << 12) +#define INT_STA_RX_TIMEOUT (1 << 11) +#define INT_STA_RX_DMA_STOP (1 << 10) +#define INT_STA_RX_BUF_UA (1 << 9) +#define INT_STA_RX (1 << 8) +#define INT_STA_TX_EARLY (1 << 5) +#define INT_STA_TX_UNDERFLOW (1 << 4) +#define INT_STA_TX_TIMEOUT (1 << 3) +#define INT_STA_TX_BUF_UA (1 << 2) +#define INT_STA_TX_DMA_STOP (1 << 1) +#define INT_STA_TX (1 << 0) + +#define INT_EN_RX_EARLY (1 << 13) +#define INT_EN_RX_OVERFLOW (1 << 12) +#define INT_EN_RX_TIMEOUT (1 << 11) +#define INT_EN_RX_DMA_STOP (1 << 10) +#define INT_EN_RX_BUF_UA (1 << 9) +#define INT_EN_RX (1 << 8) +#define INT_EN_TX_EARLY (1 << 5) +#define INT_EN_TX_UNDERFLOW (1 << 4) +#define INT_EN_TX_TIMEOUT (1 << 3) +#define INT_EN_TX_BUF_UA (1 << 2) +#define INT_EN_TX_DMA_STOP (1 << 1) +#define INT_EN_TX (1 << 0) + +#define TX_CTL0_TX_EN (1 << 31) +#define TX_CTL1_TX_DMA_START (1 << 31) +#define TX_CTL1_TX_DMA_EN (1 << 30) +#define TX_CTL1_TX_FLUSH (1 << 0) + +#define RX_CTL0_RX_EN (1 << 31) +#define RX_CTL0_STRIP_FCS (1 << 28) +#define RX_CTL0_CRC_IPV4 (1 << 27) + +#define RX_CTL1_RX_DMA_START (1 << 31) +#define RX_CTL1_RX_DMA_EN (1 << 30) +#define RX_CTL1_RX_MD (1 << 1) + +#define RX_FRM_FLT_DIS_ADDR (1 << 31) + +#define MII_CMD_PHY_ADDR_SHIFT (12) +#define MII_CMD_PHY_ADDR_MASK (0xf000) +#define MII_CMD_PHY_REG_SHIFT (4) +#define MII_CMD_PHY_REG_MASK (0xf0) +#define MII_CMD_PHY_RW (1 << 1) +#define MII_CMD_PHY_BUSY (1 << 0) + +#define TX_DMA_STA_STOP (0b000) +#define TX_DMA_STA_RUN_FETCH (0b001) +#define TX_DMA_STA_WAIT_STA (0b010) + +#define RX_DMA_STA_STOP (0b000) +#define RX_DMA_STA_RUN_FETCH (0b001) +#define RX_DMA_STA_WAIT_FRM (0b011) + +#define RGMII_LINK_UP (1 << 3) +#define RGMII_FD (1 << 0) + +/* EMAC register reset values */ +#define REG_BASIC_CTL_1_RST (0x08000000) + +/* EMAC constants */ +#define AW_H3_EMAC_MIN_PKT_SZ (64) + +/* Transmit/receive frame descriptor */ +typedef struct FrameDescriptor { + uint32_t status; + uint32_t status2; + uint32_t addr; + uint32_t next; +} FrameDescriptor; + +/* Frame descriptor flags */ +#define DESC_STATUS_CTL (1 << 31) +#define DESC_STATUS2_BUF_SIZE_MASK (0x7ff) + +/* Transmit frame descriptor flags */ +#define TX_DESC_STATUS_LENGTH_ERR (1 << 14) +#define TX_DESC_STATUS2_FIRST_DESC (1 << 29) +#define TX_DESC_STATUS2_LAST_DESC (1 << 30) +#define TX_DESC_STATUS2_CHECKSUM_MASK (0x3 << 27) + +/* Receive frame descriptor flags */ +#define RX_DESC_STATUS_FIRST_DESC (1 << 9) +#define RX_DESC_STATUS_LAST_DESC (1 << 8) +#define RX_DESC_STATUS_FRM_LEN_MASK (0x3fff0000) +#define RX_DESC_STATUS_FRM_LEN_SHIFT (16) +#define RX_DESC_STATUS_NO_BUF (1 << 14) +#define RX_DESC_STATUS_HEADER_ERR (1 << 7) +#define RX_DESC_STATUS_LENGTH_ERR (1 << 4) +#define RX_DESC_STATUS_CRC_ERR (1 << 1) +#define RX_DESC_STATUS_PAYLOAD_ERR (1 << 0) +#define RX_DESC_STATUS2_RX_INT_CTL (1 << 31) + +/* MII register offsets */ +#define MII_REG_CR (0x0) +#define MII_REG_ST (0x1) +#define MII_REG_ID_HIGH (0x2) +#define MII_REG_ID_LOW (0x3) + +/* MII register flags */ +#define MII_REG_CR_RESET (1 << 15) +#define MII_REG_CR_POWERDOWN (1 << 11) +#define MII_REG_CR_10Mbit (0) +#define MII_REG_CR_100Mbit (1 << 13) +#define MII_REG_CR_1000Mbit (1 << 6) +#define MII_REG_CR_AUTO_NEG (1 << 12) +#define MII_REG_CR_AUTO_NEG_RESTART (1 << 9) +#define MII_REG_CR_FULLDUPLEX (1 << 8) + +#define MII_REG_ST_100BASE_T4 (1 << 15) +#define MII_REG_ST_100BASE_X_FD (1 << 14) +#define MII_REG_ST_100BASE_X_HD (1 << 13) +#define MII_REG_ST_10_FD (1 << 12) +#define MII_REG_ST_10_HD (1 << 11) +#define MII_REG_ST_100BASE_T2_FD (1 << 10) +#define MII_REG_ST_100BASE_T2_HD (1 << 9) +#define MII_REG_ST_AUTONEG_COMPLETE (1 << 5) +#define MII_REG_ST_AUTONEG_AVAIL (1 << 3) +#define MII_REG_ST_LINK_UP (1 << 2) + +/* MII constants */ +#define MII_PHY_ID_HIGH (0x0044) +#define MII_PHY_ID_LOW (0x1400) + +static void aw_h3_emac_mii_set_link(AwH3EmacState *s, bool link_active) +{ + if (link_active) { + s->mii_st |= MII_REG_ST_LINK_UP; + } else { + s->mii_st &= ~MII_REG_ST_LINK_UP; + } +} + +static void aw_h3_emac_mii_reset(AwH3EmacState *s, bool link_active) +{ + s->mii_cr = MII_REG_CR_100Mbit | MII_REG_CR_AUTO_NEG | + MII_REG_CR_FULLDUPLEX; + s->mii_st = MII_REG_ST_100BASE_T4 | MII_REG_ST_100BASE_X_FD | + MII_REG_ST_100BASE_X_HD | MII_REG_ST_10_FD | MII_REG_ST_10_HD | + MII_REG_ST_100BASE_T2_FD | MII_REG_ST_100BASE_T2_HD | + MII_REG_ST_AUTONEG_COMPLETE | MII_REG_ST_AUTONEG_AVAIL; + + aw_h3_emac_mii_set_link(s, link_active); +} + +static void aw_h3_emac_mii_cmd(AwH3EmacState *s) +{ + uint8_t addr, reg; + + addr = (s->mii_cmd & MII_CMD_PHY_ADDR_MASK) >> MII_CMD_PHY_ADDR_SHIFT; + reg = (s->mii_cmd & MII_CMD_PHY_REG_MASK) >> MII_CMD_PHY_REG_SHIFT; + + if (addr != s->mii_phy_addr) { + return; + } + + /* Read or write a PHY register? */ + if (s->mii_cmd & MII_CMD_PHY_RW) { + trace_aw_h3_emac_mii_write_reg(reg, s->mii_data); + + switch (reg) { + case MII_REG_CR: + if (s->mii_data & MII_REG_CR_RESET) { + aw_h3_emac_mii_reset(s, s->mii_st & MII_REG_ST_LINK_UP); + } else { + s->mii_cr = s->mii_data & ~(MII_REG_CR_RESET | + MII_REG_CR_AUTO_NEG_RESTART); + } + break; + default: + qemu_log_mask(LOG_UNIMP, "allwinner-h3-emac: write access to " + "unknown MII register 0x%x\n", reg); + break; + } + } else { + switch (reg) { + case MII_REG_CR: + s->mii_data = s->mii_cr; + break; + case MII_REG_ST: + s->mii_data = s->mii_st; + break; + case MII_REG_ID_HIGH: + s->mii_data = MII_PHY_ID_HIGH; + break; + case MII_REG_ID_LOW: + s->mii_data = MII_PHY_ID_LOW; + break; + default: + qemu_log_mask(LOG_UNIMP, "allwinner-h3-emac: read access to " + "unknown MII register 0x%x\n", reg); + s->mii_data = 0; + break; + } + + trace_aw_h3_emac_mii_read_reg(reg, s->mii_data); + } +} + +static void aw_h3_emac_update_irq(AwH3EmacState *s) +{ + qemu_set_irq(s->irq, (s->int_sta & s->int_en) != 0); +} + +static uint32_t aw_h3_emac_next_desc(FrameDescriptor *desc, size_t min_size) +{ + uint32_t paddr = desc->next; + + cpu_physical_memory_read(paddr, desc, sizeof(*desc)); + + if ((desc->status & DESC_STATUS_CTL) && + (desc->status2 & DESC_STATUS2_BUF_SIZE_MASK) >= min_size) { + return paddr; + } else { + return 0; + } +} + +static uint32_t aw_h3_emac_get_desc(FrameDescriptor *desc, uint32_t start_addr, + size_t min_size) +{ + uint32_t desc_addr = start_addr; + + /* Note that the list is a cycle. Last entry points back to the head. */ + while (desc_addr != 0) { + cpu_physical_memory_read(desc_addr, desc, sizeof(*desc)); + + if ((desc->status & DESC_STATUS_CTL) && + (desc->status2 & DESC_STATUS2_BUF_SIZE_MASK) >= min_size) { + return desc_addr; + } else if (desc->next == start_addr) { + break; + } else { + desc_addr = desc->next; + } + } + + return 0; +} + +static uint32_t aw_h3_emac_get_rx_desc(AwH3EmacState *s, FrameDescriptor *desc, + size_t min_size) +{ + return aw_h3_emac_get_desc(desc, s->rx_desc_curr, min_size); +} + +static uint32_t aw_h3_emac_get_tx_desc(AwH3EmacState *s, FrameDescriptor *desc, + size_t min_size) +{ + return aw_h3_emac_get_desc(desc, s->tx_desc_head, min_size); +} + +static void aw_h3_emac_flush_desc(FrameDescriptor *desc, uint32_t phys_addr) +{ + cpu_physical_memory_write(phys_addr, desc, sizeof(*desc)); +} + +static int aw_h3_emac_can_receive(NetClientState *nc) +{ + AwH3EmacState *s = qemu_get_nic_opaque(nc); + FrameDescriptor desc; + + return (s->rx_ctl0 & RX_CTL0_RX_EN) && + (aw_h3_emac_get_rx_desc(s, &desc, 0) != 0); +} + +static ssize_t aw_h3_emac_receive(NetClientState *nc, const uint8_t *buf, + size_t size) +{ + AwH3EmacState *s = qemu_get_nic_opaque(nc); + FrameDescriptor desc; + size_t bytes_left = size; + size_t desc_bytes = 0; + size_t pad_fcs_size = 4; + size_t padding = 0; + + if (!(s->rx_ctl0 & RX_CTL0_RX_EN)) { + return -1; + } + + s->rx_desc_curr = aw_h3_emac_get_rx_desc(s, &desc, AW_H3_EMAC_MIN_PKT_SZ); + if (!s->rx_desc_curr) { + s->int_sta |= INT_STA_RX_BUF_UA; + } + + /* Keep filling RX descriptors until the whole frame is written */ + while (s->rx_desc_curr && bytes_left > 0) { + desc.status &= ~DESC_STATUS_CTL; + desc.status &= ~RX_DESC_STATUS_FRM_LEN_MASK; + + if (bytes_left == size) { + desc.status |= RX_DESC_STATUS_FIRST_DESC; + } + + if ((desc.status2 & DESC_STATUS2_BUF_SIZE_MASK) < + (bytes_left + pad_fcs_size)) { + desc_bytes = desc.status2 & DESC_STATUS2_BUF_SIZE_MASK; + desc.status |= desc_bytes << RX_DESC_STATUS_FRM_LEN_SHIFT; + } else { + padding = pad_fcs_size; + if (bytes_left < AW_H3_EMAC_MIN_PKT_SZ) { + padding += (AW_H3_EMAC_MIN_PKT_SZ - bytes_left); + } + + desc_bytes = (bytes_left); + desc.status |= RX_DESC_STATUS_LAST_DESC; + desc.status |= (bytes_left + padding) + << RX_DESC_STATUS_FRM_LEN_SHIFT; + } + + cpu_physical_memory_write(desc.addr, buf, desc_bytes); + aw_h3_emac_flush_desc(&desc, s->rx_desc_curr); + trace_aw_h3_emac_receive(s->rx_desc_curr, desc.addr, desc_bytes); + + /* Check if frame needs to raise the receive interrupt */ + if (!(desc.status2 & RX_DESC_STATUS2_RX_INT_CTL)) { + s->int_sta |= INT_STA_RX; + } + + /* Increment variables */ + buf += desc_bytes; + bytes_left -= desc_bytes; + + /* Move to the next descriptor */ + s->rx_desc_curr = aw_h3_emac_next_desc(&desc, 64); + if (!s->rx_desc_curr) { + /* Not enough buffer space available */ + s->int_sta |= INT_STA_RX_BUF_UA; + s->rx_desc_curr = s->rx_desc_head; + break; + } + } + + /* Report receive DMA is finished */ + s->rx_ctl1 &= ~RX_CTL1_RX_DMA_START; + aw_h3_emac_update_irq(s); + + return size; +} + +static void aw_h3_emac_transmit(AwH3EmacState *s) +{ + NetClientState *nc = qemu_get_queue(s->nic); + FrameDescriptor desc; + size_t bytes = 0; + size_t packet_bytes = 0; + size_t transmitted = 0; + static uint8_t packet_buf[2048]; + + s->tx_desc_curr = aw_h3_emac_get_tx_desc(s, &desc, 0); + + /* Read all transmit descriptors */ + while (s->tx_desc_curr != 0) { + + /* Read from physical memory into packet buffer */ + bytes = desc.status2 & DESC_STATUS2_BUF_SIZE_MASK; + if (bytes + packet_bytes > sizeof(packet_buf)) { + desc.status |= TX_DESC_STATUS_LENGTH_ERR; + break; + } + cpu_physical_memory_read(desc.addr, packet_buf + packet_bytes, bytes); + packet_bytes += bytes; + desc.status &= ~DESC_STATUS_CTL; + aw_h3_emac_flush_desc(&desc, s->tx_desc_curr); + + /* After the last descriptor, send the packet */ + if (desc.status2 & TX_DESC_STATUS2_LAST_DESC) { + if (desc.status2 & TX_DESC_STATUS2_CHECKSUM_MASK) { + net_checksum_calculate(packet_buf, packet_bytes); + } + + qemu_send_packet(nc, packet_buf, packet_bytes); + trace_aw_h3_emac_transmit(s->tx_desc_curr, desc.addr, bytes); + + packet_bytes = 0; + transmitted++; + } + s->tx_desc_curr = aw_h3_emac_next_desc(&desc, 0); + } + + /* Raise transmit completed interrupt */ + if (transmitted > 0) { + s->int_sta |= INT_STA_TX; + s->tx_ctl1 &= ~TX_CTL1_TX_DMA_START; + aw_h3_emac_update_irq(s); + } +} + +static void aw_h3_emac_reset(DeviceState *dev) +{ + AwH3EmacState *s = AW_H3_EMAC(dev); + NetClientState *nc = qemu_get_queue(s->nic); + + trace_aw_h3_emac_reset(); + + s->mii_cmd = 0; + s->mii_data = 0; + s->basic_ctl0 = 0; + s->basic_ctl1 = 0; + s->int_en = 0; + s->int_sta = 0; + s->frm_flt = 0; + s->rx_ctl0 = 0; + s->rx_ctl1 = RX_CTL1_RX_MD; + s->rx_desc_head = 0; + s->rx_desc_curr = 0; + s->tx_ctl0 = 0; + s->tx_ctl1 = 0; + s->tx_desc_head = 0; + s->tx_desc_curr = 0; + s->tx_flowctl = 0; + + aw_h3_emac_mii_reset(s, !nc->link_down); +} + +static uint64_t aw_h3_emac_read(void *opaque, hwaddr offset, unsigned size) +{ + AwH3EmacState *s = opaque; + uint64_t value = 0; + FrameDescriptor desc; + + switch (offset) { + case REG_BASIC_CTL_0: /* Basic Control 0 */ + value = s->basic_ctl0; + break; + case REG_BASIC_CTL_1: /* Basic Control 1 */ + value = s->basic_ctl1; + break; + case REG_INT_STA: /* Interrupt Status */ + value = s->int_sta; + break; + case REG_INT_EN: /* Interupt Enable */ + value = s->int_en; + break; + case REG_TX_CTL_0: /* Transmit Control 0 */ + value = s->tx_ctl0; + break; + case REG_TX_CTL_1: /* Transmit Control 1 */ + value = s->tx_ctl1; + break; + case REG_TX_FLOW_CTL: /* Transmit Flow Control */ + value = s->tx_flowctl; + break; + case REG_TX_DMA_DESC_LIST: /* Transmit Descriptor List Address */ + value = s->tx_desc_head; + break; + case REG_RX_CTL_0: /* Receive Control 0 */ + value = s->rx_ctl0; + break; + case REG_RX_CTL_1: /* Receive Control 1 */ + value = s->rx_ctl1; + break; + case REG_RX_DMA_DESC_LIST: /* Receive Descriptor List Address */ + value = s->rx_desc_head; + break; + case REG_FRM_FLT: /* Receive Frame Filter */ + value = s->frm_flt; + break; + case REG_RX_HASH_0: /* Receive Hash Table 0 */ + case REG_RX_HASH_1: /* Receive Hash Table 1 */ + break; + case REG_MII_CMD: /* Management Interface Command */ + value = s->mii_cmd; + break; + case REG_MII_DATA: /* Management Interface Data */ + value = s->mii_data; + break; + case REG_ADDR_HIGH: /* MAC Address High */ + value = *(((uint32_t *) (s->conf.macaddr.a)) + 1); + break; + case REG_ADDR_LOW: /* MAC Address Low */ + value = *(uint32_t *) (s->conf.macaddr.a); + break; + case REG_TX_DMA_STA: /* Transmit DMA Status */ + break; + case REG_TX_CUR_DESC: /* Transmit Current Descriptor */ + value = s->tx_desc_curr; + break; + case REG_TX_CUR_BUF: /* Transmit Current Buffer */ + if (s->tx_desc_curr != 0) { + cpu_physical_memory_read(s->tx_desc_curr, &desc, sizeof(desc)); + value = desc.addr; + } else { + value = 0; + } + break; + case REG_RX_DMA_STA: /* Receive DMA Status */ + break; + case REG_RX_CUR_DESC: /* Receive Current Descriptor */ + value = s->rx_desc_curr; + break; + case REG_RX_CUR_BUF: /* Receive Current Buffer */ + if (s->rx_desc_curr != 0) { + cpu_physical_memory_read(s->rx_desc_curr, &desc, sizeof(desc)); + value = desc.addr; + } else { + value = 0; + } + break; + default: + qemu_log_mask(LOG_UNIMP, "allwinner-h3-emac: read access to unknown " + "EMAC register 0x" TARGET_FMT_plx "\n", + offset); + } + + trace_aw_h3_emac_read(offset, value); + return value; +} + +static void aw_h3_emac_write(void *opaque, hwaddr offset, uint64_t value, + unsigned size) +{ + AwH3EmacState *s = opaque; + NetClientState *nc = qemu_get_queue(s->nic); + + trace_aw_h3_emac_write(offset, value); + + switch (offset) { + case REG_BASIC_CTL_0: /* Basic Control 0 */ + s->basic_ctl0 = value; + break; + case REG_BASIC_CTL_1: /* Basic Control 1 */ + if (value & BASIC_CTL1_SOFTRST) { + aw_h3_emac_reset(DEVICE(s)); + value &= ~BASIC_CTL1_SOFTRST; + } + s->basic_ctl1 = value; + if (aw_h3_emac_can_receive(nc)) { + qemu_flush_queued_packets(nc); + } + break; + case REG_INT_STA: /* Interrupt Status */ + s->int_sta &= ~value; + aw_h3_emac_update_irq(s); + break; + case REG_INT_EN: /* Interrupt Enable */ + s->int_en = value; + aw_h3_emac_update_irq(s); + break; + case REG_TX_CTL_0: /* Transmit Control 0 */ + s->tx_ctl0 = value; + break; + case REG_TX_CTL_1: /* Transmit Control 1 */ + s->tx_ctl1 = value; + if (value & TX_CTL1_TX_DMA_EN) { + aw_h3_emac_transmit(s); + } + break; + case REG_TX_FLOW_CTL: /* Transmit Flow Control */ + s->tx_flowctl = value; + break; + case REG_TX_DMA_DESC_LIST: /* Transmit Descriptor List Address */ + s->tx_desc_head = value; + s->tx_desc_curr = value; + break; + case REG_RX_CTL_0: /* Receive Control 0 */ + s->rx_ctl0 = value; + break; + case REG_RX_CTL_1: /* Receive Control 1 */ + s->rx_ctl1 = value | RX_CTL1_RX_MD; + if ((value & RX_CTL1_RX_DMA_EN) && aw_h3_emac_can_receive(nc)) { + qemu_flush_queued_packets(nc); + } + break; + case REG_RX_DMA_DESC_LIST: /* Receive Descriptor List Address */ + s->rx_desc_head = value; + s->rx_desc_curr = value; + break; + case REG_FRM_FLT: /* Receive Frame Filter */ + s->frm_flt = value; + break; + case REG_RX_HASH_0: /* Receive Hash Table 0 */ + case REG_RX_HASH_1: /* Receive Hash Table 1 */ + break; + case REG_MII_CMD: /* Management Interface Command */ + s->mii_cmd = value & ~MII_CMD_PHY_BUSY; + aw_h3_emac_mii_cmd(s); + break; + case REG_MII_DATA: /* Management Interface Data */ + s->mii_data = value; + break; + case REG_ADDR_HIGH: /* MAC Address High */ + s->conf.macaddr.a[4] = (value & 0xff); + s->conf.macaddr.a[5] = (value & 0xff00) >> 8; + break; + case REG_ADDR_LOW: /* MAC Address Low */ + s->conf.macaddr.a[0] = (value & 0xff); + s->conf.macaddr.a[1] = (value & 0xff00) >> 8; + s->conf.macaddr.a[2] = (value & 0xff0000) >> 16; + s->conf.macaddr.a[3] = (value & 0xff000000) >> 24; + break; + case REG_TX_DMA_STA: /* Transmit DMA Status */ + case REG_TX_CUR_DESC: /* Transmit Current Descriptor */ + case REG_TX_CUR_BUF: /* Transmit Current Buffer */ + case REG_RX_DMA_STA: /* Receive DMA Status */ + case REG_RX_CUR_DESC: /* Receive Current Descriptor */ + case REG_RX_CUR_BUF: /* Receive Current Buffer */ + case REG_RGMII_STA: /* RGMII Status */ + break; + default: + qemu_log_mask(LOG_UNIMP, "allwinner-h3-emac: write access to unknown " + "EMAC register 0x" TARGET_FMT_plx "\n", + offset); + } +} + +static void aw_h3_emac_set_link(NetClientState *nc) +{ + AwH3EmacState *s = qemu_get_nic_opaque(nc); + + trace_aw_h3_emac_set_link(!nc->link_down); + aw_h3_emac_mii_set_link(s, !nc->link_down); +} + +static const MemoryRegionOps aw_h3_emac_mem_ops = { + .read = aw_h3_emac_read, + .write = aw_h3_emac_write, + .endianness = DEVICE_NATIVE_ENDIAN, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + }, +}; + +static NetClientInfo net_aw_h3_emac_info = { + .type = NET_CLIENT_DRIVER_NIC, + .size = sizeof(NICState), + .can_receive = aw_h3_emac_can_receive, + .receive = aw_h3_emac_receive, + .link_status_changed = aw_h3_emac_set_link, +}; + +static void aw_h3_emac_init(Object *obj) +{ + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); + AwH3EmacState *s = AW_H3_EMAC(obj); + + memory_region_init_io(&s->iomem, OBJECT(s), &aw_h3_emac_mem_ops, s, + TYPE_AW_H3_EMAC, AW_H3_EMAC_REGS_MEM_SIZE); + sysbus_init_mmio(sbd, &s->iomem); + sysbus_init_irq(sbd, &s->irq); +} + +static void aw_h3_emac_realize(DeviceState *dev, Error **errp) +{ + AwH3EmacState *s = AW_H3_EMAC(dev); + + qemu_macaddr_default_if_unset(&s->conf.macaddr); + s->nic = qemu_new_nic(&net_aw_h3_emac_info, &s->conf, + object_get_typename(OBJECT(dev)), dev->id, s); + qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a); +} + +static Property aw_h3_emac_properties[] = { + DEFINE_NIC_PROPERTIES(AwH3EmacState, conf), + DEFINE_PROP_UINT8("phy-addr", AwH3EmacState, mii_phy_addr, 0), + DEFINE_PROP_END_OF_LIST(), +}; + +static int aw_h3_emac_post_load(void *opaque, int version_id) +{ + AwH3EmacState *s = opaque; + + aw_h3_emac_set_link(qemu_get_queue(s->nic)); + + return 0; +} + +static const VMStateDescription vmstate_aw_emac = { + .name = TYPE_AW_H3_EMAC, + .version_id = 1, + .minimum_version_id = 1, + .post_load = aw_h3_emac_post_load, + .fields = (VMStateField[]) { + VMSTATE_UINT8(mii_phy_addr, AwH3EmacState), + VMSTATE_UINT32(mii_cmd, AwH3EmacState), + VMSTATE_UINT32(mii_data, AwH3EmacState), + VMSTATE_UINT32(basic_ctl0, AwH3EmacState), + VMSTATE_UINT32(basic_ctl1, AwH3EmacState), + VMSTATE_UINT32(int_en, AwH3EmacState), + VMSTATE_UINT32(int_sta, AwH3EmacState), + VMSTATE_UINT32(frm_flt, AwH3EmacState), + VMSTATE_UINT32(rx_ctl0, AwH3EmacState), + VMSTATE_UINT32(rx_ctl1, AwH3EmacState), + VMSTATE_UINT32(rx_desc_head, AwH3EmacState), + VMSTATE_UINT32(rx_desc_curr, AwH3EmacState), + VMSTATE_UINT32(tx_ctl0, AwH3EmacState), + VMSTATE_UINT32(tx_ctl1, AwH3EmacState), + VMSTATE_UINT32(tx_desc_head, AwH3EmacState), + VMSTATE_UINT32(tx_desc_curr, AwH3EmacState), + VMSTATE_UINT32(tx_flowctl, AwH3EmacState), + VMSTATE_END_OF_LIST() + } +}; + +static void aw_h3_emac_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->realize = aw_h3_emac_realize; + dc->props = aw_h3_emac_properties; + dc->reset = aw_h3_emac_reset; + dc->vmsd = &vmstate_aw_emac; +} + +static const TypeInfo aw_h3_emac_info = { + .name = TYPE_AW_H3_EMAC, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(AwH3EmacState), + .instance_init = aw_h3_emac_init, + .class_init = aw_h3_emac_class_init, +}; + +static void aw_h3_emac_register_types(void) +{ + type_register_static(&aw_h3_emac_info); +} + +type_init(aw_h3_emac_register_types) diff --git a/hw/net/trace-events b/hw/net/trace-events index e70f12bee1..e9e2f26f68 100644 --- a/hw/net/trace-events +++ b/hw/net/trace-events @@ -1,5 +1,15 @@ # See docs/devel/tracing.txt for syntax documentation. +# allwinner-h3-emac.c +aw_h3_emac_mii_write_reg(uint32_t reg, uint32_t value) "MII write: reg=0x%x value=0x%x" +aw_h3_emac_mii_read_reg(uint32_t reg, uint32_t value) "MII read: reg=0x%x value=0x%x" +aw_h3_emac_receive(uint32_t desc, uint32_t paddr, uint32_t bytes) "RX packet: desc=0x%08x paddr=0x%08x bytes=%u" +aw_h3_emac_transmit(uint32_t desc, uint32_t paddr, uint32_t bytes) "TX packet: desc=0x%08x paddr=0x%08x bytes=%u" +aw_h3_emac_reset(void) "HW reset" +aw_h3_emac_set_link(bool active) "Set link: active=%u" +aw_h3_emac_read(uint64_t offset, uint64_t val) "MMIO read: offset=0x%" PRIx64 " value=0x%" PRIx64 +aw_h3_emac_write(uint64_t offset, uint64_t val) "MMIO write: offset=0x%" PRIx64 " value=0x%" PRIx64 + # etraxfs_eth.c mdio_phy_read(int regnum, uint16_t value) "read phy_reg:%d value:0x%04x" mdio_phy_write(int regnum, uint16_t value) "write phy_reg:%d value:0x%04x" diff --git a/include/hw/arm/allwinner-h3.h b/include/hw/arm/allwinner-h3.h index 7aff4ebbd2..b964a60f41 100644 --- a/include/hw/arm/allwinner-h3.h +++ b/include/hw/arm/allwinner-h3.h @@ -31,6 +31,7 @@ #include "hw/misc/allwinner-h3-syscon.h" #include "hw/misc/allwinner-h3-sid.h" #include "hw/sd/allwinner-h3-sdhost.h" +#include "hw/net/allwinner-h3-emac.h" #include "target/arm/cpu.h" #define AW_H3_SRAM_A1_BASE (0x00000000) @@ -119,6 +120,7 @@ typedef struct AwH3State { AwH3SysconState syscon; AwH3SidState sid; AwH3SDHostState mmc0; + AwH3EmacState emac; GICState gic; MemoryRegion sram_a1; MemoryRegion sram_a2; diff --git a/include/hw/net/allwinner-h3-emac.h b/include/hw/net/allwinner-h3-emac.h new file mode 100644 index 0000000000..a007d54472 --- /dev/null +++ b/include/hw/net/allwinner-h3-emac.h @@ -0,0 +1,69 @@ +/* + * Allwinner H3 EMAC emulation + * + * Copyright (C) 2019 Niek Linnenbank + * + * 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 ALLWINNER_H3_EMAC_H +#define ALLWINNER_H3_EMAC_H + +#include "qemu/units.h" +#include "net/net.h" +#include "qemu/fifo8.h" +#include "hw/net/mii.h" +#include "hw/sysbus.h" + +#define AW_H3_EMAC_REGS_MEM_SIZE (1024) + +#define TYPE_AW_H3_EMAC "allwinner-h3-emac" +#define AW_H3_EMAC(obj) OBJECT_CHECK(AwH3EmacState, (obj), TYPE_AW_H3_EMAC) + +typedef struct AwH3EmacState { + /*< private >*/ + SysBusDevice parent_obj; + /*< public >*/ + + MemoryRegion iomem; + qemu_irq irq; + NICState *nic; + NICConf conf; + + uint8_t mii_phy_addr; + uint32_t mii_cmd; + uint32_t mii_data; + uint32_t mii_cr; + uint32_t mii_st; + + uint32_t basic_ctl0; + uint32_t basic_ctl1; + uint32_t int_en; + uint32_t int_sta; + uint32_t frm_flt; + + uint32_t rx_ctl0; + uint32_t rx_ctl1; + uint32_t rx_desc_head; + uint32_t rx_desc_curr; + + uint32_t tx_ctl0; + uint32_t tx_ctl1; + uint32_t tx_desc_head; + uint32_t tx_desc_curr; + uint32_t tx_flowctl; + +} AwH3EmacState; + +#endif