From patchwork Mon Dec 16 23:35:10 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Niek Linnenbank X-Patchwork-Id: 11296209 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 BB12D6C1 for ; Mon, 16 Dec 2019 23:37:15 +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 8065520CC7 for ; Mon, 16 Dec 2019 23:37:15 +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="BpcxgEgt" DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org 8065520CC7 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]:33302 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1igzvO-0008Ka-7R for patchwork-qemu-devel@patchwork.kernel.org; Mon, 16 Dec 2019 18:37:14 -0500 Received: from eggs.gnu.org ([2001:470:142:3::10]:50171) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1igzuD-0006aF-Uz for qemu-devel@nongnu.org; Mon, 16 Dec 2019 18:36:04 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1igzuB-0002ce-IX for qemu-devel@nongnu.org; Mon, 16 Dec 2019 18:36:01 -0500 Received: from mail-wm1-x343.google.com ([2a00:1450:4864:20::343]:52291) by eggs.gnu.org with esmtps (TLS1.0:RSA_AES_128_CBC_SHA1:16) (Exim 4.71) (envelope-from ) id 1igzuB-0002aT-9i; Mon, 16 Dec 2019 18:35:59 -0500 Received: by mail-wm1-x343.google.com with SMTP id p9so1098959wmc.2; Mon, 16 Dec 2019 15:35: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=HFFxDgHNUT0gW2AN+l0EB3Cl8rnPc6ibLlNqwL0CJ+E=; b=BpcxgEgtneTECgBjWf1BeQ0Nz/UusX3TE+Attq3Ly3J86//V/3tBBCExmVp5FARH9r PGYP3Ffe1G5QSUoMeUzoL03g+HI3zJQvA/KrUjYJtF27Rk/0+IOnhA7Nd5PXZPxo26yC ZQETStuvC2bwxz76o5k3dy5/mm+A5kw1jE4l1WFurRNFNko89d+YPDkhYLqyo61Colzy yXRW+CWd/+D0ifAfDevtDuEb/CvE2azIaJS4Jo+KqPVv4B0r/XSVgY8fLOxfemdb6rtf 9GKS+6a6kbqiiSlnj3cNDjnZFJDRgxEmuPPmhmCu3OvV/xVCmuxh+rLWyE5gcXb4bxGL 1hsw== 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=HFFxDgHNUT0gW2AN+l0EB3Cl8rnPc6ibLlNqwL0CJ+E=; b=Xlb9zdjCfvVUuucM+1ruZResqzQCOLTZj2lzRkBqk5fOJUlln1mp8szKTepJhcivaj KIjK8oF6V4mx4FT6YEjmGaSyfSJpGGf0r0wS7KXgk8zlHREG+/8eTw2WVOe9VQBuiVhj MM2+CKJ4+vUOZsBzmPkoxrMFanRYmaHCbgPqd6UVhbmCxzuGUYacaHD2UNXZZlzde8M6 7omEZzDZ6/lW/MNpq442au+TaINLGzNaXl47AVUMl6T0dEz8/mKV5AHmy1kbKyMVxhyt c+TpV215SeJkIWUxL4b/t8Al/LnCV8kKGrFLSHMo08A8N2aZczVzfu4HaZfJw++eSsU/ OfUA== X-Gm-Message-State: APjAAAWpWrmU8Iw6TbRneZSzjKCtRc/eWAc68LwajWMJsWDVvI+jhyR8 uyuzviD/RKyEKA4/ReFG3ZQ0vDL5 X-Google-Smtp-Source: APXvYqyfBSPblrQr5s37p2VqBB7641HeVfeAy3j+NzYuJ7qS1qc+2w+179ldaZ8dfORT4mg2jSeB0w== X-Received: by 2002:a7b:cb4e:: with SMTP id v14mr1776593wmj.20.1576539357514; Mon, 16 Dec 2019 15:35:57 -0800 (PST) Received: from pavilion.home ([2a02:a456:6be8:1:8edc:d4ff:fe8b:18b7]) by smtp.gmail.com with ESMTPSA id z83sm984501wmg.2.2019.12.16.15.35.56 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 16 Dec 2019 15:35:56 -0800 (PST) From: Niek Linnenbank To: qemu-devel@nongnu.org Subject: [PATCH v2 01/10] hw: arm: add Allwinner H3 System-on-Chip Date: Tue, 17 Dec 2019 00:35:10 +0100 Message-Id: <20191216233519.29030-2-nieklinnenbank@gmail.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20191216233519.29030-1-nieklinnenbank@gmail.com> References: <20191216233519.29030-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-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: peter.maydell@linaro.org, Niek Linnenbank , qemu-arm@nongnu.org, philmd@redhat.com 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 --- default-configs/arm-softmmu.mak | 1 + include/hw/arm/allwinner-h3.h | 80 +++++++ hw/arm/allwinner-h3.c | 360 ++++++++++++++++++++++++++++++++ MAINTAINERS | 7 + hw/arm/Kconfig | 8 + hw/arm/Makefile.objs | 1 + 6 files changed, 457 insertions(+) create mode 100644 include/hw/arm/allwinner-h3.h create mode 100644 hw/arm/allwinner-h3.c 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/include/hw/arm/allwinner-h3.h b/include/hw/arm/allwinner-h3.h new file mode 100644 index 0000000000..e50caeffaa --- /dev/null +++ b/include/hw/arm/allwinner-h3.h @@ -0,0 +1,80 @@ +/* + * 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" + +enum { + AW_H3_SRAM_A1, + AW_H3_SRAM_A2, + AW_H3_SRAM_C, + AW_H3_SYSCON, + AW_H3_SID, + AW_H3_CCU, + AW_H3_PIT, + AW_H3_UART0, + AW_H3_UART1, + AW_H3_UART2, + AW_H3_UART3, + AW_H3_EMAC, + AW_H3_MMC0, + AW_H3_EHCI0, + AW_H3_OHCI0, + AW_H3_EHCI1, + AW_H3_OHCI1, + AW_H3_EHCI2, + AW_H3_OHCI2, + AW_H3_EHCI3, + AW_H3_OHCI3, + AW_H3_GIC_DIST, + AW_H3_GIC_CPU, + AW_H3_GIC_HYP, + AW_H3_GIC_VCPU, + AW_H3_CPUCFG, + AW_H3_SDRAM +}; + +#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 >*/ + + ARMCPU cpus[AW_H3_NUM_CPUS]; + const hwaddr *memmap; + AwA10PITState timer; + GICState gic; + MemoryRegion sram_a1; + MemoryRegion sram_a2; + MemoryRegion sram_c; +} AwH3State; + +#endif diff --git a/hw/arm/allwinner-h3.c b/hw/arm/allwinner-h3.c new file mode 100644 index 0000000000..1a47de56f5 --- /dev/null +++ b/hw/arm/allwinner-h3.c @@ -0,0 +1,360 @@ +/* + * 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" + +/* Memory map */ +const hwaddr aw_h3_memmap[] = { + [AW_H3_SRAM_A1] = 0x00000000, + [AW_H3_SRAM_A2] = 0x00044000, + [AW_H3_SRAM_C] = 0x00010000, + [AW_H3_SYSCON] = 0x01c00000, + [AW_H3_SID] = 0x01c14000, + [AW_H3_CCU] = 0x01c20000, + [AW_H3_PIT] = 0x01c20c00, + [AW_H3_UART0] = 0x01c28000, + [AW_H3_UART1] = 0x01c28400, + [AW_H3_UART2] = 0x01c28800, + [AW_H3_UART3] = 0x01c28c00, + [AW_H3_EMAC] = 0x01c30000, + [AW_H3_MMC0] = 0x01c0f000, + [AW_H3_EHCI0] = 0x01c1a000, + [AW_H3_OHCI0] = 0x01c1a400, + [AW_H3_EHCI1] = 0x01c1b000, + [AW_H3_OHCI1] = 0x01c1b400, + [AW_H3_EHCI2] = 0x01c1c000, + [AW_H3_OHCI2] = 0x01c1c400, + [AW_H3_EHCI3] = 0x01c1d000, + [AW_H3_OHCI3] = 0x01c1d400, + [AW_H3_GIC_DIST] = 0x01c81000, + [AW_H3_GIC_CPU] = 0x01c82000, + [AW_H3_GIC_HYP] = 0x01c84000, + [AW_H3_GIC_VCPU] = 0x01c86000, + [AW_H3_CPUCFG] = 0x01f01c00, + [AW_H3_SDRAM] = 0x40000000 +}; + +/* List of unimplemented devices */ +struct AwH3Unimplemented { + const char *device_name; + hwaddr base; + hwaddr size; +} unimplemented[] = { + { "d-engine", 0x01000000, 4 * MiB }, + { "d-inter", 0x01400000, 128 * KiB }, + { "dma", 0x01c02000, 4 * KiB }, + { "nfdc", 0x01c03000, 4 * KiB }, + { "ts", 0x01c06000, 4 * KiB }, + { "keymem", 0x01c0b000, 4 * KiB }, + { "lcd0", 0x01c0c000, 4 * KiB }, + { "lcd1", 0x01c0d000, 4 * KiB }, + { "ve", 0x01c0e000, 4 * KiB }, + { "mmc1", 0x01c10000, 4 * KiB }, + { "mmc2", 0x01c11000, 4 * KiB }, + { "crypto", 0x01c15000, 4 * KiB }, + { "msgbox", 0x01c17000, 4 * KiB }, + { "spinlock", 0x01c18000, 4 * KiB }, + { "usb0-otg", 0x01c19000, 4 * KiB }, + { "usb0-phy", 0x01c1a000, 4 * KiB }, + { "usb1-phy", 0x01c1b000, 4 * KiB }, + { "usb2-phy", 0x01c1c000, 4 * KiB }, + { "usb3-phy", 0x01c1d000, 4 * KiB }, + { "smc", 0x01c1e000, 4 * KiB }, + { "pio", 0x01c20800, 1 * KiB }, + { "owa", 0x01c21000, 1 * KiB }, + { "pwm", 0x01c21400, 1 * KiB }, + { "keyadc", 0x01c21800, 1 * KiB }, + { "pcm0", 0x01c22000, 1 * KiB }, + { "pcm1", 0x01c22400, 1 * KiB }, + { "pcm2", 0x01c22800, 1 * KiB }, + { "audio", 0x01c22c00, 2 * KiB }, + { "smta", 0x01c23400, 1 * KiB }, + { "ths", 0x01c25000, 1 * KiB }, + { "uart0", 0x01c28000, 1 * KiB }, + { "uart1", 0x01c28400, 1 * KiB }, + { "uart2", 0x01c28800, 1 * KiB }, + { "uart3", 0x01c28c00, 1 * KiB }, + { "twi0", 0x01c2ac00, 1 * KiB }, + { "twi1", 0x01c2b000, 1 * KiB }, + { "twi2", 0x01c2b400, 1 * KiB }, + { "scr", 0x01c2c400, 1 * KiB }, + { "gpu", 0x01c40000, 64 * KiB }, + { "hstmr", 0x01c60000, 4 * KiB }, + { "dramcom", 0x01c62000, 4 * KiB }, + { "dramctl0", 0x01c63000, 4 * KiB }, + { "dramphy0", 0x01c65000, 4 * KiB }, + { "spi0", 0x01c68000, 4 * KiB }, + { "spi1", 0x01c69000, 4 * KiB }, + { "csi", 0x01cb0000, 320 * KiB }, + { "tve", 0x01e00000, 64 * KiB }, + { "hdmi", 0x01ee0000, 128 * KiB }, + { "rtc", 0x01f00000, 1 * KiB }, + { "r_timer", 0x01f00800, 1 * KiB }, + { "r_intc", 0x01f00c00, 1 * KiB }, + { "r_wdog", 0x01f01000, 1 * KiB }, + { "r_prcm", 0x01f01400, 1 * KiB }, + { "r_twd", 0x01f01800, 1 * KiB }, + { "r_cpucfg", 0x01f01c00, 1 * KiB }, + { "r_cir-rx", 0x01f02000, 1 * KiB }, + { "r_twi", 0x01f02400, 1 * KiB }, + { "r_uart", 0x01f02800, 1 * KiB }, + { "r_pio", 0x01f02c00, 1 * KiB }, + { "r_pwm", 0x01f03800, 1 * KiB }, + { "core-dbg", 0x3f500000, 128 * KiB }, + { "tsgen-ro", 0x3f506000, 4 * KiB }, + { "tsgen-ctl", 0x3f507000, 4 * KiB }, + { "ddr-mem", 0x40000000, 2 * GiB }, + { "n-brom", 0xffff0000, 32 * KiB }, + { "s-brom", 0xffff0000, 64 * KiB } +}; + +/* Per Processor Interrupts */ +enum { + AW_H3_GIC_PPI_MAINT = 9, + AW_H3_GIC_PPI_HYPTIMER = 10, + AW_H3_GIC_PPI_VIRTTIMER = 11, + AW_H3_GIC_PPI_SECTIMER = 13, + AW_H3_GIC_PPI_PHYSTIMER = 14 +}; + +/* Shared Processor Interrupts */ +enum { + AW_H3_GIC_SPI_UART0 = 0, + AW_H3_GIC_SPI_UART1 = 1, + AW_H3_GIC_SPI_UART2 = 2, + AW_H3_GIC_SPI_UART3 = 3, + AW_H3_GIC_SPI_TIMER0 = 18, + AW_H3_GIC_SPI_TIMER1 = 19, + AW_H3_GIC_SPI_MMC0 = 60, + AW_H3_GIC_SPI_MMC1 = 61, + AW_H3_GIC_SPI_MMC2 = 62, + AW_H3_GIC_SPI_EHCI0 = 72, + AW_H3_GIC_SPI_OHCI0 = 73, + AW_H3_GIC_SPI_EHCI1 = 74, + AW_H3_GIC_SPI_OHCI1 = 75, + AW_H3_GIC_SPI_EHCI2 = 76, + AW_H3_GIC_SPI_OHCI2 = 77, + AW_H3_GIC_SPI_EHCI3 = 78, + AW_H3_GIC_SPI_OHCI3 = 79, + AW_H3_GIC_SPI_EMAC = 82 +}; + +/* Allwinner H3 constants */ +enum { + AW_H3_GIC_NUM_SPI = 128 +}; + +static void aw_h3_init(Object *obj) +{ + AwH3State *s = AW_H3(obj); + + s->memmap = aw_h3_memmap; + + for (int i = 0; i < AW_H3_NUM_CPUS; i++) { + object_initialize_child(obj, "cpu[*]", &s->cpus[i], sizeof(s->cpus[i]), + ARM_CPU_TYPE_NAME("cortex-a7"), + &error_abort, NULL); + } + + 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); + unsigned i = 0; + + /* CPUs */ + for (i = 0; i < AW_H3_NUM_CPUS; i++) { + + /* Provide Power State Coordination Interface */ + object_property_set_int(OBJECT(&s->cpus[i]), QEMU_PSCI_CONDUIT_HVC, + "psci-conduit", &error_abort); + if (error_abort != NULL) { + error_propagate(errp, error_abort); + return; + } + + /* Disable secondary CPUs */ + object_property_set_bool(OBJECT(&s->cpus[i]), i > 0, + "start-powered-off", &error_abort); + if (error_abort != NULL) { + error_propagate(errp, error_abort); + return; + } + + /* All exception levels required */ + object_property_set_bool(OBJECT(&s->cpus[i]), + true, "has_el3", &error_abort); + if (error_abort != NULL) { + error_propagate(errp, error_abort); + return; + } + + object_property_set_bool(OBJECT(&s->cpus[i]), + true, "has_el2", &error_abort); + if (error_abort != NULL) { + error_propagate(errp, error_abort); + return; + } + + /* Mark realized */ + qdev_init_nofail(DEVICE(&s->cpus[i])); + } + + /* 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); + qdev_init_nofail(DEVICE(&s->gic)); + + sysbus_mmio_map(SYS_BUS_DEVICE(&s->gic), 0, s->memmap[AW_H3_GIC_DIST]); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->gic), 1, s->memmap[AW_H3_GIC_CPU]); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->gic), 2, s->memmap[AW_H3_GIC_HYP]); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->gic), 3, s->memmap[AW_H3_GIC_VCPU]); + + /* + * Wire the outputs from each CPU's generic timer and the GICv3 + * maintenance interrupt signal to the appropriate GIC PPI inputs, + * and the GIC's IRQ/FIQ/VIRQ/VFIQ interrupt outputs to the CPU's inputs. + */ + for (i = 0; i < AW_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_PHYSTIMER, + [GTIMER_VIRT] = AW_H3_GIC_PPI_VIRTTIMER, + [GTIMER_HYP] = AW_H3_GIC_PPI_HYPTIMER, + [GTIMER_SEC] = AW_H3_GIC_PPI_SECTIMER, + }; + + /* Connect CPU timer outputs to GIC PPI inputs */ + for (irq = 0; irq < ARRAY_SIZE(timer_irq); irq++) { + qdev_connect_gpio_out(cpudev, irq, + qdev_get_gpio_in(DEVICE(&s->gic), + ppibase + timer_irq[irq])); + } + + /* Connect GIC outputs to CPU interrupt inputs */ + sysbus_connect_irq(SYS_BUS_DEVICE(&s->gic), i, + qdev_get_gpio_in(cpudev, ARM_CPU_IRQ)); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->gic), i + AW_H3_NUM_CPUS, + qdev_get_gpio_in(cpudev, ARM_CPU_FIQ)); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->gic), i + (2 * AW_H3_NUM_CPUS), + qdev_get_gpio_in(cpudev, ARM_CPU_VIRQ)); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->gic), i + (3 * AW_H3_NUM_CPUS), + qdev_get_gpio_in(cpudev, ARM_CPU_VFIQ)); + + /* GIC maintenance signal */ + sysbus_connect_irq(SYS_BUS_DEVICE(&s->gic), i + (4 * AW_H3_NUM_CPUS), + qdev_get_gpio_in(DEVICE(&s->gic), + ppibase + AW_H3_GIC_PPI_MAINT)); + } + + /* Timer */ + qdev_init_nofail(DEVICE(&s->timer)); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->timer), 0, s->memmap[AW_H3_PIT]); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->timer), 0, + qdev_get_gpio_in(DEVICE(&s->gic), AW_H3_GIC_SPI_TIMER0)); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->timer), 1, + qdev_get_gpio_in(DEVICE(&s->gic), AW_H3_GIC_SPI_TIMER1)); + + /* SRAM */ + memory_region_init_ram(&s->sram_a1, OBJECT(dev), "sram A1", + 64 * KiB, &error_abort); + memory_region_init_ram(&s->sram_a2, OBJECT(dev), "sram A2", + 32 * KiB, &error_abort); + memory_region_init_ram(&s->sram_c, OBJECT(dev), "sram C", + 44 * KiB, &error_abort); + memory_region_add_subregion(get_system_memory(), s->memmap[AW_H3_SRAM_A1], + &s->sram_a1); + memory_region_add_subregion(get_system_memory(), s->memmap[AW_H3_SRAM_A2], + &s->sram_a2); + memory_region_add_subregion(get_system_memory(), s->memmap[AW_H3_SRAM_C], + &s->sram_c); + + /* UART0 */ + serial_mm_init(get_system_memory(), s->memmap[AW_H3_UART0], 2, + qdev_get_gpio_in(DEVICE(&s->gic), AW_H3_GIC_SPI_UART0), + 115200, serial_hd(0), DEVICE_NATIVE_ENDIAN); + /* UART1 */ + serial_mm_init(get_system_memory(), s->memmap[AW_H3_UART1], 2, + qdev_get_gpio_in(DEVICE(&s->gic), AW_H3_GIC_SPI_UART1), + 115200, serial_hd(1), DEVICE_NATIVE_ENDIAN); + /* UART2 */ + serial_mm_init(get_system_memory(), s->memmap[AW_H3_UART2], 2, + qdev_get_gpio_in(DEVICE(&s->gic), AW_H3_GIC_SPI_UART2), + 115200, serial_hd(2), DEVICE_NATIVE_ENDIAN); + /* UART3 */ + serial_mm_init(get_system_memory(), s->memmap[AW_H3_UART3], 2, + qdev_get_gpio_in(DEVICE(&s->gic), AW_H3_GIC_SPI_UART3), + 115200, serial_hd(3), DEVICE_NATIVE_ENDIAN); + + /* Unimplemented devices */ + for (int i = 0; i < ARRAY_SIZE(unimplemented); i++) { + create_unimplemented_device(unimplemented[i].device_name, + unimplemented[i].base, + unimplemented[i].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/MAINTAINERS b/MAINTAINERS index 740401bcbb..aae1a049b4 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -480,6 +480,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/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 From patchwork Mon Dec 16 23:35:11 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Niek Linnenbank X-Patchwork-Id: 11296205 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 9C2E86C1 for ; Mon, 16 Dec 2019 23:37:08 +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 7113F20CC7 for ; Mon, 16 Dec 2019 23:37:08 +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="GjXqTY/i" DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org 7113F20CC7 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]:33298 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1igzvH-0008As-D4 for patchwork-qemu-devel@patchwork.kernel.org; Mon, 16 Dec 2019 18:37:07 -0500 Received: from eggs.gnu.org ([2001:470:142:3::10]:50162) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1igzuD-0006aD-Ae for qemu-devel@nongnu.org; Mon, 16 Dec 2019 18:36:02 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1igzuB-0002cs-P5 for qemu-devel@nongnu.org; Mon, 16 Dec 2019 18:36:01 -0500 Received: from mail-wm1-x341.google.com ([2a00:1450:4864:20::341]:40621) by eggs.gnu.org with esmtps (TLS1.0:RSA_AES_128_CBC_SHA1:16) (Exim 4.71) (envelope-from ) id 1igzuB-0002bL-Iq; Mon, 16 Dec 2019 18:35:59 -0500 Received: by mail-wm1-x341.google.com with SMTP id t14so1089529wmi.5; Mon, 16 Dec 2019 15:35: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=56k1gd9w0pzweQ0pdSRVAAe2bI3hcno8MwlnwkRM6ZM=; b=GjXqTY/ih7teLgDrI7tdcAfP6FdrS/tg4tyIQlG+Va8lA0ejPhApBtoeU7Gn17L/DB UAMECp1O8NZ4JyWBXK7AA6ucMHnLQLyLgJHnXICECNzNefL55Ix1u/hfhYxubxrNVRbs OBzczXp1m697AplJg7C9lcmCWEuwgVMxtE9BYVdHO5nIrDjn+6LwCBZ5o0mPQAP5nnJy eEd0ozY8kCoY4xrSbc1EFgR7+9RMpcChZZ5e5Nf5PdbapjP0b5OAHvSlPCT5Rsv20hmF CQveeZsttOomZ2miOnTGp6io2+ne51SNSp0MIdn6NaqztQWnRA2QnP12n4czglYhhGaA hV+w== 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=56k1gd9w0pzweQ0pdSRVAAe2bI3hcno8MwlnwkRM6ZM=; b=QvEuTV494i6AZdQC9PFKHENb+gotnQEpBxncHNpXXQbdn99Q/9GBOHiPmzrDXZ6j9l 17reOF3ps3ldrxgeXDwlh8g2M/CkFduZvE1aGSun8E8MzWhneNuCE3dDY64e04gW2RQR oxduiPkvlAiyD1kk4fBQRwYffK/LF6hnUzk/NfBazQKmdwISSzIe9cYgMknBJD9UcgNJ OziW/RuHekQ7X/2OaTneEUDV3vwjmQZM0xwM+MQlNWf5fO0fuw5RBjV45x0+3udf1lqC hwvxQORjkBtQ7i9PYyn7HN+oKxXAKG+WACB+95sYMzt9mbUeUDtYYgOq2dCwrcjzgeT1 jqrw== X-Gm-Message-State: APjAAAXxMv1JhxBWVRBuXW51QrSFjwmDc2Tq+RhC9O4tvQ3mDZ315g9M dKWOZg2LTK1bMZdCczl09nVm+k2u X-Google-Smtp-Source: APXvYqxHJsDUECQUP0Q50PY96e8rOKZaO8x7NBoR98Xby9ML67U+rg2/PncZ+/L4+lLM0glmJzXfvA== X-Received: by 2002:a7b:c114:: with SMTP id w20mr1660722wmi.151.1576539358199; Mon, 16 Dec 2019 15:35:58 -0800 (PST) Received: from pavilion.home ([2a02:a456:6be8:1:8edc:d4ff:fe8b:18b7]) by smtp.gmail.com with ESMTPSA id z83sm984501wmg.2.2019.12.16.15.35.57 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 16 Dec 2019 15:35:57 -0800 (PST) From: Niek Linnenbank To: qemu-devel@nongnu.org Subject: [PATCH v2 02/10] hw: arm: add Xunlong Orange Pi PC machine Date: Tue, 17 Dec 2019 00:35:11 +0100 Message-Id: <20191216233519.29030-3-nieklinnenbank@gmail.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20191216233519.29030-1-nieklinnenbank@gmail.com> References: <20191216233519.29030-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-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: peter.maydell@linaro.org, Niek Linnenbank , qemu-arm@nongnu.org, philmd@redhat.com 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 Tested-by: KONRAD Frederic --- hw/arm/orangepi.c | 101 +++++++++++++++++++++++++++++++++++++++++++ MAINTAINERS | 1 + hw/arm/Makefile.objs | 2 +- 3 files changed, 103 insertions(+), 1 deletion(-) create mode 100644 hw/arm/orangepi.c diff --git a/hw/arm/orangepi.c b/hw/arm/orangepi.c new file mode 100644 index 0000000000..62cefc8c06 --- /dev/null +++ b/hw/arm/orangepi.c @@ -0,0 +1,101 @@ +/* + * 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 = { + .board_id = -1, +}; + +typedef struct OrangePiState { + AwH3State *h3; + MemoryRegion sdram; +} OrangePiState; + +static void orangepi_init(MachineState *machine) +{ + OrangePiState *s = g_new(OrangePiState, 1); + + /* Only allow Cortex-A7 for this board */ + if (strcmp(machine->cpu_type, ARM_CPU_TYPE_NAME("cortex-a7")) != 0) { + error_report("This board can only be used with cortex-a7 CPU"); + exit(1); + } + + s->h3 = AW_H3(object_new(TYPE_AW_H3)); + + /* Setup timer properties */ + object_property_set_int(OBJECT(&s->h3->timer), 32768, "clk0-freq", + &error_abort); + if (error_abort != NULL) { + error_reportf_err(error_abort, "Couldn't set clk0 frequency: "); + exit(1); + } + + object_property_set_int(OBJECT(&s->h3->timer), 24000000, "clk1-freq", + &error_abort); + if (error_abort != NULL) { + error_reportf_err(error_abort, "Couldn't set clk1 frequency: "); + exit(1); + } + + /* Mark H3 object realized */ + object_property_set_bool(OBJECT(s->h3), true, "realized", &error_abort); + if (error_abort != NULL) { + error_reportf_err(error_abort, "Couldn't realize Allwinner H3: "); + exit(1); + } + + /* RAM */ + if (machine->ram_size > 1 * GiB) { + error_report("Requested ram size is too large for this machine: " + "maximum is 1GB"); + exit(1); + } + memory_region_allocate_system_memory(&s->sdram, NULL, "orangepi.ram", + machine->ram_size); + memory_region_add_subregion(get_system_memory(), s->h3->memmap[AW_H3_SDRAM], + &s->sdram); + + /* Load target kernel */ + orangepi_binfo.loader_start = s->h3->memmap[AW_H3_SDRAM]; + 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->default_cpu_type = ARM_CPU_TYPE_NAME("cortex-a7"); +} + +DEFINE_MACHINE("orangepi-pc", orangepi_machine_init) diff --git a/MAINTAINERS b/MAINTAINERS index aae1a049b4..db682e49ca 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -486,6 +486,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 From patchwork Mon Dec 16 23:35:12 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Niek Linnenbank X-Patchwork-Id: 11296225 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 58539112B for ; Mon, 16 Dec 2019 23:42:34 +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 1D00E24672 for ; Mon, 16 Dec 2019 23:42:34 +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="S1VjRl/F" DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org 1D00E24672 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]:33406 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1ih00X-0007v7-C1 for patchwork-qemu-devel@patchwork.kernel.org; Mon, 16 Dec 2019 18:42:33 -0500 Received: from eggs.gnu.org ([2001:470:142:3::10]:50198) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1igzuE-0006aJ-Si for qemu-devel@nongnu.org; Mon, 16 Dec 2019 18:36:05 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1igzuC-0002e8-Oi for qemu-devel@nongnu.org; Mon, 16 Dec 2019 18:36:02 -0500 Received: from mail-wr1-x442.google.com ([2a00:1450:4864:20::442]:40292) by eggs.gnu.org with esmtps (TLS1.0:RSA_AES_128_CBC_SHA1:16) (Exim 4.71) (envelope-from ) id 1igzuC-0002d1-HK; Mon, 16 Dec 2019 18:36:00 -0500 Received: by mail-wr1-x442.google.com with SMTP id c14so9329572wrn.7; Mon, 16 Dec 2019 15:36:00 -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=8G82YqiK+hkOg91TDiubF2yJBVZeI/ZlRlQXSYUa4tw=; b=S1VjRl/FOVAXEJODULvdLYATGlfS0xLSX3zMTrtyDgo/Je4pNOmWLM5V48uyZaFiCF P6YMuAnakAW7PaKWaZpNOzzTPpj2bPlgzUwOo+7+Wa++TB5LhlhqKc7ATn0aI1o14mit 1oHikv9l+AdfDhAM4ba7TvZFZM4xPcSz1yWbQ41/aFU9FKl5CFZp96/yWZ9416LcKw6U db2hbT+/XU+Sd3dj5L/oXeyNPsb8xpFCwHqe4LJ+gvFerynEiZFZZOvfyMSwLDIQazcL Pj65+0oRkq7JHB2KyKCIScBo/6aJBzz6Qxo5lZf4KPbmrdX4oVPW1y4gmVI8UNuV2keq VQOg== 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=8G82YqiK+hkOg91TDiubF2yJBVZeI/ZlRlQXSYUa4tw=; b=h0CZCdVrGOXN46Z8QmZjsSbN5aZeEin7yUhip7oV640jeGdBNb+eZZB2klhcblNzmC ubczwsCqhGwQK5T3LzEkP5LnxgdIouB6klNclldhIlQbf5VSwOfLCNQOF3fSsMHEHWZX layL601dmQH1NVszOH5Xg5ouDI1dPXatIUbyOqOVEOqK9WCeEb7QDDnl2lJfd+66yU6X XUUOFg1NmMPZdlfMjPVdN5rcOUIAeknTYadp4mht0hRpNSRNHeplCfS/PuPstpEfj/Ko ky+HeY/ASk547905ngUS3Kd69D+JdZteKpECb29BzwdQAkvQ1aJ2bFeYUSfRCc93NJjh +0KQ== X-Gm-Message-State: APjAAAUBY0LWPHNyTJTGksOkCFlDKvfM8mAHs9zO497iD5d39Sa2x73I YwIZ6gdV2nWukkgL2Bj0Ow6M0yhU X-Google-Smtp-Source: APXvYqyWFFQSwvzfpOAFOa5hYuz4jltyXtSi8N1Ea6Q+IjimjuA2Qn0E8atreyLWu6EdfeCOjpB4Bg== X-Received: by 2002:a5d:4983:: with SMTP id r3mr33543549wrq.134.1576539359133; Mon, 16 Dec 2019 15:35:59 -0800 (PST) Received: from pavilion.home ([2a02:a456:6be8:1:8edc:d4ff:fe8b:18b7]) by smtp.gmail.com with ESMTPSA id z83sm984501wmg.2.2019.12.16.15.35.58 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 16 Dec 2019 15:35:58 -0800 (PST) From: Niek Linnenbank To: qemu-devel@nongnu.org Subject: [PATCH v2 03/10] arm: allwinner-h3: add Clock Control Unit Date: Tue, 17 Dec 2019 00:35:12 +0100 Message-Id: <20191216233519.29030-4-nieklinnenbank@gmail.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20191216233519.29030-1-nieklinnenbank@gmail.com> References: <20191216233519.29030-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::442 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: peter.maydell@linaro.org, Niek Linnenbank , qemu-arm@nongnu.org, philmd@redhat.com 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 --- include/hw/arm/allwinner-h3.h | 2 + include/hw/misc/allwinner-h3-clk.h | 40 +++++ hw/arm/allwinner-h3.c | 7 + hw/misc/allwinner-h3-clk.c | 238 +++++++++++++++++++++++++++++ hw/misc/Makefile.objs | 1 + 5 files changed, 288 insertions(+) create mode 100644 include/hw/misc/allwinner-h3-clk.h create mode 100644 hw/misc/allwinner-h3-clk.c diff --git a/include/hw/arm/allwinner-h3.h b/include/hw/arm/allwinner-h3.h index e50caeffaa..47d6f82cc4 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" enum { @@ -71,6 +72,7 @@ typedef struct AwH3State { ARMCPU cpus[AW_H3_NUM_CPUS]; const hwaddr *memmap; 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..ce058a900b --- /dev/null +++ b/include/hw/misc/allwinner-h3-clk.h @@ -0,0 +1,40 @@ +/* + * 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 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 diff --git a/hw/arm/allwinner-h3.c b/hw/arm/allwinner-h3.c index 1a47de56f5..0da09188d1 100644 --- a/hw/arm/allwinner-h3.c +++ b/hw/arm/allwinner-h3.c @@ -186,6 +186,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) @@ -310,6 +313,10 @@ static void aw_h3_realize(DeviceState *dev, Error **errp) memory_region_add_subregion(get_system_memory(), s->memmap[AW_H3_SRAM_C], &s->sram_c); + /* Clock Control Unit */ + qdev_init_nofail(DEVICE(&s->ccu)); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->ccu), 0, s->memmap[AW_H3_CCU]); + /* UART0 */ serial_mm_init(get_system_memory(), s->memmap[AW_H3_UART0], 2, qdev_get_gpio_in(DEVICE(&s->gic), AW_H3_GIC_SPI_UART0), diff --git a/hw/misc/allwinner-h3-clk.c b/hw/misc/allwinner-h3-clk.c new file mode 100644 index 0000000000..4758cd4d7e --- /dev/null +++ b/hw/misc/allwinner-h3-clk.c @@ -0,0 +1,238 @@ +/* + * 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 "qemu/units.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 */ +enum { + REG_PLL_CPUX = 0x0000, /* PLL CPUX Control */ + REG_PLL_AUDIO = 0x0008, /* PLL Audio Control */ + REG_PLL_VIDEO = 0x0010, /* PLL Video Control */ + REG_PLL_VE = 0x0018, /* PLL VE Control */ + REG_PLL_DDR = 0x0020, /* PLL DDR Control */ + REG_PLL_PERIPH0 = 0x0028, /* PLL Peripherals 0 Control */ + REG_PLL_GPU = 0x0038, /* PLL GPU Control */ + REG_PLL_PERIPH1 = 0x0044, /* PLL Peripherals 1 Control */ + REG_PLL_DE = 0x0048, /* PLL Display Engine Control */ + REG_CPUX_AXI = 0x0050, /* CPUX/AXI Configuration */ + REG_APB1 = 0x0054, /* ARM Peripheral Bus 1 Config */ + REG_APB2 = 0x0058, /* ARM Peripheral Bus 2 Config */ + REG_MBUS = 0x00FC, /* MBUS Reset */ + REG_PLL_TIME0 = 0x0200, /* PLL Stable Time 0 */ + REG_PLL_TIME1 = 0x0204, /* PLL Stable Time 1 */ + REG_PLL_CPUX_BIAS = 0x0220, /* PLL CPUX Bias */ + REG_PLL_AUDIO_BIAS = 0x0224, /* PLL Audio Bias */ + REG_PLL_VIDEO_BIAS = 0x0228, /* PLL Video Bias */ + REG_PLL_VE_BIAS = 0x022C, /* PLL VE Bias */ + REG_PLL_DDR_BIAS = 0x0230, /* PLL DDR Bias */ + REG_PLL_PERIPH0_BIAS = 0x0234, /* PLL Peripherals 0 Bias */ + REG_PLL_GPU_BIAS = 0x023C, /* PLL GPU Bias */ + REG_PLL_PERIPH1_BIAS = 0x0244, /* PLL Peripherals 1 Bias */ + REG_PLL_DE_BIAS = 0x0248, /* PLL Display Engine Bias */ + REG_PLL_CPUX_TUNING = 0x0250, /* PLL CPUX Tuning */ + REG_PLL_DDR_TUNING = 0x0260, /* PLL DDR Tuning */ +}; + +#define REG_INDEX(offset) (offset / sizeof(uint32_t)) + +/* CCU register flags */ +enum { + REG_PLL_ENABLE = (1 << 31), + REG_PLL_LOCK = (1 << 28), +}; + +/* CCU register reset values */ +enum { + REG_PLL_CPUX_RST = 0x00001000, + REG_PLL_AUDIO_RST = 0x00035514, + REG_PLL_VIDEO_RST = 0x03006207, + REG_PLL_VE_RST = 0x03006207, + REG_PLL_DDR_RST = 0x00001000, + REG_PLL_PERIPH0_RST = 0x00041811, + REG_PLL_GPU_RST = 0x03006207, + REG_PLL_PERIPH1_RST = 0x00041811, + REG_PLL_DE_RST = 0x03006207, + REG_CPUX_AXI_RST = 0x00010000, + REG_APB1_RST = 0x00001010, + REG_APB2_RST = 0x01000000, + REG_MBUS_RST = 0x80000000, + REG_PLL_TIME0_RST = 0x000000FF, + REG_PLL_TIME1_RST = 0x000000FF, + REG_PLL_CPUX_BIAS_RST = 0x08100200, + REG_PLL_AUDIO_BIAS_RST = 0x10100000, + REG_PLL_VIDEO_BIAS_RST = 0x10100000, + REG_PLL_VE_BIAS_RST = 0x10100000, + REG_PLL_DDR_BIAS_RST = 0x81104000, + REG_PLL_PERIPH0_BIAS_RST = 0x10100010, + REG_PLL_GPU_BIAS_RST = 0x10100000, + REG_PLL_PERIPH1_BIAS_RST = 0x10100010, + REG_PLL_DE_BIAS_RST = 0x10100000, + REG_PLL_CPUX_TUNING_RST = 0x0A101000, + 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: + qemu_log_mask(LOG_UNIMP, "%s: unimplemented write offset 0x%04x\n", + __func__, (uint32_t)offset); + 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 + }, + .impl.min_access_size = 4, +}; + +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, 1 * KiB); + sysbus_init_mmio(sbd, &s->iomem); +} + +static const VMStateDescription allwinner_h3_clk_vmstate = { + .name = "allwinner-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/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 From patchwork Mon Dec 16 23:35:13 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Niek Linnenbank X-Patchwork-Id: 11296213 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 918236C1 for ; Mon, 16 Dec 2019 23:39:20 +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 66B9D24672 for ; Mon, 16 Dec 2019 23:39:20 +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="eebgMRtO" DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org 66B9D24672 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]:33348 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1igzxP-0003I9-8M for patchwork-qemu-devel@patchwork.kernel.org; Mon, 16 Dec 2019 18:39:19 -0500 Received: from eggs.gnu.org ([2001:470:142:3::10]:50188) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1igzuE-0006aG-GK for qemu-devel@nongnu.org; Mon, 16 Dec 2019 18:36:03 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1igzuD-0002ef-5P for qemu-devel@nongnu.org; Mon, 16 Dec 2019 18:36:02 -0500 Received: from mail-wm1-x342.google.com ([2a00:1450:4864:20::342]:40622) by eggs.gnu.org with esmtps (TLS1.0:RSA_AES_128_CBC_SHA1:16) (Exim 4.71) (envelope-from ) id 1igzuC-0002dj-VS; Mon, 16 Dec 2019 18:36:01 -0500 Received: by mail-wm1-x342.google.com with SMTP id t14so1089575wmi.5; Mon, 16 Dec 2019 15:36:00 -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=SmQGHNsBURH4ZGvWc+jJV85ra1jitK2x3YLYu4ESBHA=; b=eebgMRtOxy/MIixwrQ7v/tJFD5RvEvCgFrI/G5lyGQjhVl2R/WPOL+SmDdH8JW5AOI v6Y/5KgLr4tseL8EduoDkw3k0d2kgLJjJgUGrm+VchDM9FiDkOA5bsJp5MA4p3EwxNDi LmrnxWtA7DnLR37goPp2BVCEWi4+z2kL9wReqzyhR3RoVdXGanmjoTkWenVCMAYzJ/XR kUN1qKvxy3lEnU66DyOPDY02JaASCf/IBByDhJ5f9P5owl8pDCpTSnDbmTeV/oAq+ehU 5D0k+iJ2BP/ujHSOIlJAIs6DVNZjg7p1psrmK1/Bs9Zn15WZr4A8Pfmk1JTA1JW/ejyw Tamg== 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=SmQGHNsBURH4ZGvWc+jJV85ra1jitK2x3YLYu4ESBHA=; b=WoJCBBjXguzYGrkZyjOyGUB/CriY/l+NYAT+6JvA0okQnEF59DmTrozEBuCWLSyPFz 8C2/hHrRivPnlKgauO48XA/XH2mqHpAeSfO5eUKsIxGfgtO0qw/ssYQB/Fyn8Ef/55QN JWllBv99xGMNB96ho+0JVOWP+0SxO5bDuI1GTBeuQtHndu5r9R3S5j4IXaD+pStAWsyl DqbjbMRLeFz4LL3id3ZL6uM3RKHpEcmXuQ67+5C4/99oV5+5MwgAF82O3N4bGitZIyS8 QavC4Ql/EEIcWus9micOpCp+S39mEJNUGKBzTIDLt9U/7JmWgWYPX6MaQTyuqUQ5cA2S 8myg== X-Gm-Message-State: APjAAAVidfUI+XGUsopPjVn0DIuKm27uR4jOHJGT84TaMW3O3YoAQstN omBQxBh8FO061V262Ql+Lrmw+tD0 X-Google-Smtp-Source: APXvYqx6GvsRlwi2V+TZgCgxtfgIUs+UHh8h53/oJoHFj83YBteiUEYKoUF9Y+DCN04p0v/qGxwmPw== X-Received: by 2002:a1c:8095:: with SMTP id b143mr1759229wmd.7.1576539359747; Mon, 16 Dec 2019 15:35:59 -0800 (PST) Received: from pavilion.home ([2a02:a456:6be8:1:8edc:d4ff:fe8b:18b7]) by smtp.gmail.com with ESMTPSA id z83sm984501wmg.2.2019.12.16.15.35.59 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 16 Dec 2019 15:35:59 -0800 (PST) From: Niek Linnenbank To: qemu-devel@nongnu.org Subject: [PATCH v2 04/10] arm: allwinner-h3: add USB host controller Date: Tue, 17 Dec 2019 00:35:13 +0100 Message-Id: <20191216233519.29030-5-nieklinnenbank@gmail.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20191216233519.29030-1-nieklinnenbank@gmail.com> References: <20191216233519.29030-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-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: peter.maydell@linaro.org, Niek Linnenbank , qemu-arm@nongnu.org, philmd@redhat.com 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: Gerd Hoffmann --- hw/usb/hcd-ehci.h | 1 + hw/arm/allwinner-h3.c | 28 ++++++++++++++++++++++++++++ hw/usb/hcd-ehci-sysbus.c | 17 +++++++++++++++++ 3 files changed, 46 insertions(+) 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" diff --git a/hw/arm/allwinner-h3.c b/hw/arm/allwinner-h3.c index 0da09188d1..f0ea088852 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" /* Memory map */ @@ -317,6 +318,33 @@ static void aw_h3_realize(DeviceState *dev, Error **errp) qdev_init_nofail(DEVICE(&s->ccu)); sysbus_mmio_map(SYS_BUS_DEVICE(&s->ccu), 0, s->memmap[AW_H3_CCU]); + /* Universal Serial Bus */ + sysbus_create_simple(TYPE_AW_H3_EHCI, s->memmap[AW_H3_EHCI0], + qdev_get_gpio_in(DEVICE(&s->gic), + AW_H3_GIC_SPI_EHCI0)); + sysbus_create_simple(TYPE_AW_H3_EHCI, s->memmap[AW_H3_EHCI1], + qdev_get_gpio_in(DEVICE(&s->gic), + AW_H3_GIC_SPI_EHCI1)); + sysbus_create_simple(TYPE_AW_H3_EHCI, s->memmap[AW_H3_EHCI2], + qdev_get_gpio_in(DEVICE(&s->gic), + AW_H3_GIC_SPI_EHCI2)); + sysbus_create_simple(TYPE_AW_H3_EHCI, s->memmap[AW_H3_EHCI3], + qdev_get_gpio_in(DEVICE(&s->gic), + AW_H3_GIC_SPI_EHCI3)); + + sysbus_create_simple("sysbus-ohci", s->memmap[AW_H3_OHCI0], + qdev_get_gpio_in(DEVICE(&s->gic), + AW_H3_GIC_SPI_OHCI0)); + sysbus_create_simple("sysbus-ohci", s->memmap[AW_H3_OHCI1], + qdev_get_gpio_in(DEVICE(&s->gic), + AW_H3_GIC_SPI_OHCI1)); + sysbus_create_simple("sysbus-ohci", s->memmap[AW_H3_OHCI2], + qdev_get_gpio_in(DEVICE(&s->gic), + AW_H3_GIC_SPI_OHCI2)); + sysbus_create_simple("sysbus-ohci", s->memmap[AW_H3_OHCI3], + qdev_get_gpio_in(DEVICE(&s->gic), + AW_H3_GIC_SPI_OHCI3)); + /* UART0 */ serial_mm_init(get_system_memory(), s->memmap[AW_H3_UART0], 2, qdev_get_gpio_in(DEVICE(&s->gic), AW_H3_GIC_SPI_UART0), 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); From patchwork Mon Dec 16 23:35:14 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Niek Linnenbank X-Patchwork-Id: 11296211 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 9194B112B for ; Mon, 16 Dec 2019 23:37:19 +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 672C12467E for ; Mon, 16 Dec 2019 23:37:19 +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="SbOOAYwz" DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org 672C12467E 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]:33304 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1igzvS-0008SN-48 for patchwork-qemu-devel@patchwork.kernel.org; Mon, 16 Dec 2019 18:37:18 -0500 Received: from eggs.gnu.org ([2001:470:142:3::10]:50237) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1igzuG-0006b3-17 for qemu-devel@nongnu.org; Mon, 16 Dec 2019 18:36:06 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1igzuE-0002hD-3h for qemu-devel@nongnu.org; Mon, 16 Dec 2019 18:36:03 -0500 Received: from mail-wm1-x331.google.com ([2a00:1450:4864:20::331]:40519) by eggs.gnu.org with esmtps (TLS1.0:RSA_AES_128_CBC_SHA1:16) (Exim 4.71) (envelope-from ) id 1igzuD-0002fA-TJ; Mon, 16 Dec 2019 18:36:02 -0500 Received: by mail-wm1-x331.google.com with SMTP id t14so1089609wmi.5; Mon, 16 Dec 2019 15:36: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=TcOYVTpCVkXGz0jIEMCLCDxOkSGJsOIC7WroRTfuzB0=; b=SbOOAYwzbLGfQRwiKSM3GUvFUfzoHG9T1HXIgcRWtGmDN3bw7WowLRRI7vfqBGF4da nIEtcrPA/UPtDRyoHgzJymV4/zub4ZkpnOzazSkkEfupzUX6phyYfPQw7oKRDG/lRDrG /XYrFlSPOrxSPmS24Qc0fwUkCNLGXFft+p0vP5PNo6NILGQ3wlWgLvTrKZqOr7vPxyL1 iRoVc0Vh2Zbm6XYlpCbImuIXJG7pXuIkXddsaMl9iE6xHOR+6WCYfCByPblVRza/7ta+ oq9BFAcztKU5VVsEvYBtaUN8GT7erItBjcLSxdygjyQHj3kKawkRp73aGBBABapbdDAL tTXA== 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=TcOYVTpCVkXGz0jIEMCLCDxOkSGJsOIC7WroRTfuzB0=; b=rROoPG28e1+d/ICmRZxtotDVq7hYe353iHPLt34ieX0mlU3c6m2ecpHV8E0aPri4xd ErIGoGJWSQj37yf8SByu7aacBwCxlfNi2nS0FS+gRa8F/DpbqzGz2EW/6bZV/pJdMEDY ciJX55dIZ0CV5Dht8ybn7j0qKfuGg61Q7LoLUpjXK3dnaKG0LsQ3GGMTxXg+3vhEiovM dYIF+pi7HWQ2CQD4ZbEArl4DcGYY60IjjPcS0Fu8u1lTBJgDrt1bRW2d1f0UynMGS0Gz qb7HrMN/r5RMoEMbDA+IDbXDCuhwczhdv5WcUqtX+FhWSo010oZ7HEGW2X1m168ev3wo Nd4Q== X-Gm-Message-State: APjAAAUVL16XhbGhmeMLuRhwTw2OemcD9yu3QnCmCVBvkgyNa8kyoPoN sXfNwSPLU2k6I7ouOd2P/BcRfu7n X-Google-Smtp-Source: APXvYqyMRcYzQicdA6wA2DkNohCL7m2haz/AEuE3n1SVQcO4KKChmXTpchLtLBPAjl7/x/h2Z/ygnA== X-Received: by 2002:a05:600c:211:: with SMTP id 17mr1724746wmi.60.1576539360467; Mon, 16 Dec 2019 15:36:00 -0800 (PST) Received: from pavilion.home ([2a02:a456:6be8:1:8edc:d4ff:fe8b:18b7]) by smtp.gmail.com with ESMTPSA id z83sm984501wmg.2.2019.12.16.15.35.59 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 16 Dec 2019 15:35:59 -0800 (PST) From: Niek Linnenbank To: qemu-devel@nongnu.org Subject: [PATCH v2 05/10] arm: allwinner-h3: add System Control module Date: Tue, 17 Dec 2019 00:35:14 +0100 Message-Id: <20191216233519.29030-6-nieklinnenbank@gmail.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20191216233519.29030-1-nieklinnenbank@gmail.com> References: <20191216233519.29030-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::331 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: peter.maydell@linaro.org, Niek Linnenbank , qemu-arm@nongnu.org, philmd@redhat.com 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 --- include/hw/arm/allwinner-h3.h | 2 + include/hw/misc/allwinner-h3-syscon.h | 42 ++++++++ hw/arm/allwinner-h3.c | 7 ++ hw/misc/allwinner-h3-syscon.c | 146 ++++++++++++++++++++++++++ hw/misc/Makefile.objs | 1 + 5 files changed, 198 insertions(+) create mode 100644 include/hw/misc/allwinner-h3-syscon.h create mode 100644 hw/misc/allwinner-h3-syscon.c diff --git a/include/hw/arm/allwinner-h3.h b/include/hw/arm/allwinner-h3.h index 47d6f82cc4..bead6d4f85 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" enum { @@ -73,6 +74,7 @@ typedef struct AwH3State { const hwaddr *memmap; 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..830e1a5061 --- /dev/null +++ b/include/hw/misc/allwinner-h3-syscon.h @@ -0,0 +1,42 @@ +/* + * 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 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 diff --git a/hw/arm/allwinner-h3.c b/hw/arm/allwinner-h3.c index f0ea088852..8482d616e7 100644 --- a/hw/arm/allwinner-h3.c +++ b/hw/arm/allwinner-h3.c @@ -190,6 +190,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) @@ -318,6 +321,10 @@ static void aw_h3_realize(DeviceState *dev, Error **errp) qdev_init_nofail(DEVICE(&s->ccu)); sysbus_mmio_map(SYS_BUS_DEVICE(&s->ccu), 0, s->memmap[AW_H3_CCU]); + /* System Control */ + qdev_init_nofail(DEVICE(&s->syscon)); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->syscon), 0, s->memmap[AW_H3_SYSCON]); + /* Universal Serial Bus */ sysbus_create_simple(TYPE_AW_H3_EHCI, s->memmap[AW_H3_EHCI0], qdev_get_gpio_in(DEVICE(&s->gic), diff --git a/hw/misc/allwinner-h3-syscon.c b/hw/misc/allwinner-h3-syscon.c new file mode 100644 index 0000000000..9c5d42dd49 --- /dev/null +++ b/hw/misc/allwinner-h3-syscon.c @@ -0,0 +1,146 @@ +/* + * 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 "qemu/units.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 */ +enum { + REG_VER = 0x24, /* Version */ + REG_EMAC_PHY_CLK = 0x30, /* EMAC PHY Clock */ +}; + +#define REG_INDEX(offset) (offset / sizeof(uint32_t)) + +/* SYSCON register reset values */ +enum { + REG_VER_RST = 0x0, + 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 + }, + .impl.min_access_size = 4, +}; + +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, 4 * KiB); + sysbus_init_mmio(sbd, &s->iomem); +} + +static const VMStateDescription allwinner_h3_syscon_vmstate = { + .name = "allwinner-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/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 From patchwork Mon Dec 16 23:35:15 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Niek Linnenbank X-Patchwork-Id: 11296219 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 9B660112B for ; Mon, 16 Dec 2019 23:41:04 +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 70EFB24672 for ; Mon, 16 Dec 2019 23:41:04 +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="kSS/OUyM" DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org 70EFB24672 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]:33384 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1igzz5-0005ta-IM for patchwork-qemu-devel@patchwork.kernel.org; Mon, 16 Dec 2019 18:41:03 -0500 Received: from eggs.gnu.org ([2001:470:142:3::10]:50215) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1igzuF-0006aS-Cq for qemu-devel@nongnu.org; Mon, 16 Dec 2019 18:36:04 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1igzuE-0002ho-EX for qemu-devel@nongnu.org; Mon, 16 Dec 2019 18:36:03 -0500 Received: from mail-wm1-x341.google.com ([2a00:1450:4864:20::341]:56075) by eggs.gnu.org with esmtps (TLS1.0:RSA_AES_128_CBC_SHA1:16) (Exim 4.71) (envelope-from ) id 1igzuE-0002gF-8k; Mon, 16 Dec 2019 18:36:02 -0500 Received: by mail-wm1-x341.google.com with SMTP id q9so1074290wmj.5; Mon, 16 Dec 2019 15:36: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=naToarAXZe81gqVCr4Pwx0dIGqpD5gKSTaya0h/dQcM=; b=kSS/OUyM1IbWbDF2MPSc9wqgKjeBDqzh3nyHuyWKVOB6ikBXykqU2yMkDFP4yVaUsO UUQG4NBFYJLAnKUWqwiwHQib1S7LkidxAXnZcqfu4IKoQE4FGhB7norBUHejswpqS5jb T1VTaTHOF4oyaZnw9UkjEn5nQZ4IIdfJs+/NgXNNM3BFfUc6HGmPw+uI5pQCFvPiPBgz f3hapjJ/LgD38QWW7w5l+rRkto2qCP48X9IvpwR3wsfZWSJnL3MDci2d5DFSdEP98L7c GKSRjnoPMltCt8JMHgfoqRLjWbcNOa9BWYVxAKrvaNk0gm1YAyccShwvxrq4Sq4d64Go AHbA== 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=naToarAXZe81gqVCr4Pwx0dIGqpD5gKSTaya0h/dQcM=; b=ONlM5l07lciDAVFH8D2Vu24z+UW6LvFyhbyAb+HUTtF6T/sQSjDjdGIQZNAr3EIViV 00IwSNe/sRSeQ1FawSCym+1j16gLk2i+NIbBkhkrlZlFNQbyFYbN/hE+8UFhfRNnYKdb ElH69fcCttroD/DDM5b1zwo0LIXPJhWXBlYv6l5fzmKBuSBdGVxo6ecigBY7XZu32OvZ M1PgvBj04Y9Jf2+zH/RnaRuIrNJQVriwEpvBGnAPC8UBwoRBHICZcyGvB3L2u8kNhowR j+TMe2rOC26iMGofqroC5erYHyDpbPe3Spp5qVAFSPodCXNqn2XgwXat64DsW60a+T8o eVCw== X-Gm-Message-State: APjAAAVscDP2dCfFE4B8CsCPT2czvX6BuRoGwVuPMxBqybLWZHbetrB2 +SeABApoQwoOZctFSlJvcf/5fPwI X-Google-Smtp-Source: APXvYqzyvEwJJhJ+1etCJFn6NSfdQ3f2QKZNoBPlHzezojMNDZZ8BlSkXRi6Uv8BojJ7GfIugkOt1Q== X-Received: by 2002:a7b:c847:: with SMTP id c7mr1690287wml.3.1576539361116; Mon, 16 Dec 2019 15:36:01 -0800 (PST) Received: from pavilion.home ([2a02:a456:6be8:1:8edc:d4ff:fe8b:18b7]) by smtp.gmail.com with ESMTPSA id z83sm984501wmg.2.2019.12.16.15.36.00 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 16 Dec 2019 15:36:00 -0800 (PST) From: Niek Linnenbank To: qemu-devel@nongnu.org Subject: [PATCH v2 06/10] arm/arm-powerctl: rebuild hflags after setting CP15 bits in arm_set_cpu_on() Date: Tue, 17 Dec 2019 00:35:15 +0100 Message-Id: <20191216233519.29030-7-nieklinnenbank@gmail.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20191216233519.29030-1-nieklinnenbank@gmail.com> References: <20191216233519.29030-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-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: peter.maydell@linaro.org, Niek Linnenbank , qemu-arm@nongnu.org, philmd@redhat.com Errors-To: qemu-devel-bounces+patchwork-qemu-devel=patchwork.kernel.org@nongnu.org Sender: "Qemu-devel" After setting CP15 bits in arm_set_cpu_on() the cached hflags must be rebuild to reflect the changed processor state. Without rebuilding, the cached hflags would be inconsistent until the next call to arm_rebuild_hflags(). When QEMU is compiled with debugging enabled (--enable-debug), this problem is captured shortly after the first call to arm_set_cpu_on() for CPUs running in ARM 32-bit non-secure mode: qemu-system-arm: target/arm/helper.c:11359: cpu_get_tb_cpu_state: Assertion `flags == rebuild_hflags_internal(env)' failed. Aborted (core dumped) Fixes: 0c7f8c43daf65 Signed-off-by: Niek Linnenbank Reviewed-by: Richard Henderson --- 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 b064513d44..b75f813b40 100644 --- a/target/arm/arm-powerctl.c +++ b/target/arm/arm-powerctl.c @@ -127,6 +127,9 @@ static void arm_set_cpu_on_async_work(CPUState *target_cpu_state, target_cpu->env.regs[0] = info->context_id; } + /* CP15 update requires rebuilding hflags */ + arm_rebuild_hflags(&target_cpu->env); + /* Start the new CPU at the requested address */ cpu_set_pc(target_cpu_state, info->entry); From patchwork Mon Dec 16 23:35:16 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Niek Linnenbank X-Patchwork-Id: 11296221 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 6BA6F112B for ; Mon, 16 Dec 2019 23:41:15 +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 2FD8524672 for ; Mon, 16 Dec 2019 23:41:15 +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="tQTzpcuO" DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org 2FD8524672 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]:33386 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1igzzG-00064c-5f for patchwork-qemu-devel@patchwork.kernel.org; Mon, 16 Dec 2019 18:41:14 -0500 Received: from eggs.gnu.org ([2001:470:142:3::10]:50312) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1igzuI-0006ed-7H for qemu-devel@nongnu.org; Mon, 16 Dec 2019 18:36:08 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1igzuF-0002jz-FR for qemu-devel@nongnu.org; Mon, 16 Dec 2019 18:36:06 -0500 Received: from mail-wm1-x341.google.com ([2a00:1450:4864:20::341]:40622) by eggs.gnu.org with esmtps (TLS1.0:RSA_AES_128_CBC_SHA1:16) (Exim 4.71) (envelope-from ) id 1igzuF-0002i1-7j; Mon, 16 Dec 2019 18:36:03 -0500 Received: by mail-wm1-x341.google.com with SMTP id t14so1089647wmi.5; Mon, 16 Dec 2019 15:36: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=xDgLVEsUOOu8ERIxe7mP2M2IfpLwU7FHD+7ZqWBtdxs=; b=tQTzpcuOtPcXAw2MAF07vSuhqw2yDPZm1X7N7P+IyqWoIhqGVRuWn4TIrteOJ29kme uGYFfQi/N5iI9iZDzBpsw0GlvsptL22+EWIFL7p6YBHKaXO/dtCAJPbq7HfkXSWjXlb2 /C9xhETBRr8A6q5hMum1A7G41NL5wP4PBCUFg5rPfOttY3xxTMkzBPFx8z3SCEBJ93J/ xMM6n8YJ7HlcARSzwWbrdRCsa2BokLjI2y24TE3sIdWuugUB6sT230f3Gg0y+xrLzzDb aEC8C+BR3PIia2wcbb+pz7bda3bamguxOua/GIyrwWZ/BBLsMqj4HZc5w6xl/D5z+A89 kRFQ== 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=xDgLVEsUOOu8ERIxe7mP2M2IfpLwU7FHD+7ZqWBtdxs=; b=JwmL6RlgE03QBQpYh76mTtmbVTy3rJXTc89YCKc5p4o/xlOmoNCuYdj0IYhzfiOTBX 1rLrmXhNND/KZSnCbt+nSjtMn2q+x01j+yTx1aog0tlc/x7gh3bTsJwk4ZiFe7qD7du1 qZBu9BNxswGSiuVflEtilcw1ksJz9UMu9RdrT1VdZHhPBnCBijxK6nomarHHq/TDOQh9 4vtpyLLVQr3aXJnFLl0jwoYekyCE9nDcMm4N6yoL++8SZmVL5N5E1jCTQhfqjXrWZSas Hxiu36nw5qoxmH69M6jgElomUCzmMxbVZdEjrpBjSYm9fYEoPy+e6f0R1H0vTLLNKeNZ SMcA== X-Gm-Message-State: APjAAAVJ+YSrBSYemrhDpPpYCk3zo6Za9vcTXa19ygxjEdtATfM90cBM eeCVuhcL57xtl+juL0gCaBUOcdUp X-Google-Smtp-Source: APXvYqyEG4ZVr8xCJ2GeME54Io8tXy6p5RDf/s9nRSipABWc7RgbSIg5IyxJVCmq6j5Zc+QlgLGCOA== X-Received: by 2002:a1c:1d16:: with SMTP id d22mr1761951wmd.158.1576539361798; Mon, 16 Dec 2019 15:36:01 -0800 (PST) Received: from pavilion.home ([2a02:a456:6be8:1:8edc:d4ff:fe8b:18b7]) by smtp.gmail.com with ESMTPSA id z83sm984501wmg.2.2019.12.16.15.36.01 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 16 Dec 2019 15:36:01 -0800 (PST) From: Niek Linnenbank To: qemu-devel@nongnu.org Subject: [PATCH v2 07/10] arm: allwinner-h3: add CPU Configuration module Date: Tue, 17 Dec 2019 00:35:16 +0100 Message-Id: <20191216233519.29030-8-nieklinnenbank@gmail.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20191216233519.29030-1-nieklinnenbank@gmail.com> References: <20191216233519.29030-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-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: peter.maydell@linaro.org, Niek Linnenbank , qemu-arm@nongnu.org, philmd@redhat.com 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 --- include/hw/arm/allwinner-h3.h | 2 + include/hw/misc/allwinner-h3-cpucfg.h | 42 ++++ hw/arm/allwinner-h3.c | 7 + hw/misc/allwinner-h3-cpucfg.c | 288 ++++++++++++++++++++++++++ hw/misc/Makefile.objs | 1 + hw/misc/trace-events | 5 + 6 files changed, 345 insertions(+) create mode 100644 include/hw/misc/allwinner-h3-cpucfg.h create mode 100644 hw/misc/allwinner-h3-cpucfg.c diff --git a/include/hw/arm/allwinner-h3.h b/include/hw/arm/allwinner-h3.h index bead6d4f85..8128ae6131 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" @@ -74,6 +75,7 @@ typedef struct AwH3State { const hwaddr *memmap; 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..92b2dcbe2f --- /dev/null +++ b/include/hw/misc/allwinner-h3-cpucfg.h @@ -0,0 +1,42 @@ +/* + * 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 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 diff --git a/hw/arm/allwinner-h3.c b/hw/arm/allwinner-h3.c index 8482d616e7..1a9748ab2e 100644 --- a/hw/arm/allwinner-h3.c +++ b/hw/arm/allwinner-h3.c @@ -193,6 +193,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) @@ -325,6 +328,10 @@ static void aw_h3_realize(DeviceState *dev, Error **errp) qdev_init_nofail(DEVICE(&s->syscon)); sysbus_mmio_map(SYS_BUS_DEVICE(&s->syscon), 0, s->memmap[AW_H3_SYSCON]); + /* CPU Configuration */ + qdev_init_nofail(DEVICE(&s->cpucfg)); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->cpucfg), 0, s->memmap[AW_H3_CPUCFG]); + /* Universal Serial Bus */ sysbus_create_simple(TYPE_AW_H3_EHCI, s->memmap[AW_H3_EHCI0], qdev_get_gpio_in(DEVICE(&s->gic), diff --git a/hw/misc/allwinner-h3-cpucfg.c b/hw/misc/allwinner-h3-cpucfg.c new file mode 100644 index 0000000000..1d238c5c78 --- /dev/null +++ b/hw/misc/allwinner-h3-cpucfg.c @@ -0,0 +1,288 @@ +/* + * 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 "qemu/units.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 */ +enum { + REG_CPUS_RST_CTRL = 0x0000, /* CPUs Reset Control */ + REG_CPU0_RST_CTRL = 0x0040, /* CPU#0 Reset Control */ + REG_CPU0_CTRL = 0x0044, /* CPU#0 Control */ + REG_CPU0_STATUS = 0x0048, /* CPU#0 Status */ + REG_CPU1_RST_CTRL = 0x0080, /* CPU#1 Reset Control */ + REG_CPU1_CTRL = 0x0084, /* CPU#1 Control */ + REG_CPU1_STATUS = 0x0088, /* CPU#1 Status */ + REG_CPU2_RST_CTRL = 0x00C0, /* CPU#2 Reset Control */ + REG_CPU2_CTRL = 0x00C4, /* CPU#2 Control */ + REG_CPU2_STATUS = 0x00C8, /* CPU#2 Status */ + REG_CPU3_RST_CTRL = 0x0100, /* CPU#3 Reset Control */ + REG_CPU3_CTRL = 0x0104, /* CPU#3 Control */ + REG_CPU3_STATUS = 0x0108, /* CPU#3 Status */ + REG_CPU_SYS_RST = 0x0140, /* CPU System Reset */ + REG_CLK_GATING = 0x0144, /* CPU Clock Gating */ + REG_GEN_CTRL = 0x0184, /* General Control */ + REG_SUPER_STANDBY = 0x01A0, /* Super Standby Flag */ + REG_ENTRY_ADDR = 0x01A4, /* Reset Entry Address */ + REG_DBG_EXTERN = 0x01E4, /* Debug External */ + REG_CNT64_CTRL = 0x0280, /* 64-bit Counter Control */ + REG_CNT64_LOW = 0x0284, /* 64-bit Counter Low */ + REG_CNT64_HIGH = 0x0288, /* 64-bit Counter High */ +}; + +/* CPUCFG register flags */ +enum { + CPUX_RESET_RELEASED = ((1 << 1) | (1 << 0)), + CPUX_STATUS_SMP = (1 << 0), + CPU_SYS_RESET_RELEASED = (1 << 0), + CLK_GATING_ENABLE = ((1 << 8) | 0xF), +}; + +/* CPUCFG register reset values */ +enum { + REG_CLK_GATING_RST = 0x0000010F, + REG_GEN_CTRL_RST = 0x00000020, + REG_SUPER_STANDBY_RST = 0x0, + 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 + }, + .impl.min_access_size = 4, +}; + +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, 1 * KiB); + sysbus_init_mmio(sbd, &s->iomem); +} + +static const VMStateDescription allwinner_h3_cpucfg_vmstate = { + .name = "allwinner-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/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/trace-events b/hw/misc/trace-events index 1deb1d08c1..b93089d010 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%" PRIu32 +allwinner_h3_cpucfg_read(uint64_t offset, uint64_t data, unsigned size) "H3-CPUCFG: read: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %" PRIu32 +allwinner_h3_cpucfg_write(uint64_t offset, uint64_t data, unsigned size) "H3-CPUCFG: write: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %" PRIu32 + # 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" From patchwork Mon Dec 16 23:35:17 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Niek Linnenbank X-Patchwork-Id: 11296215 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 BC9896C1 for ; Mon, 16 Dec 2019 23:39:24 +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 82AEB24672 for ; Mon, 16 Dec 2019 23:39:24 +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="M1WXKp6f" DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org 82AEB24672 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]:33350 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1igzxT-0003NF-7J for patchwork-qemu-devel@patchwork.kernel.org; Mon, 16 Dec 2019 18:39:23 -0500 Received: from eggs.gnu.org ([2001:470:142:3::10]:50322) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1igzuI-0006fG-Ks for qemu-devel@nongnu.org; Mon, 16 Dec 2019 18:36:08 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1igzuG-0002mM-03 for qemu-devel@nongnu.org; Mon, 16 Dec 2019 18:36:06 -0500 Received: from mail-wm1-x342.google.com ([2a00:1450:4864:20::342]:39917) by eggs.gnu.org with esmtps (TLS1.0:RSA_AES_128_CBC_SHA1:16) (Exim 4.71) (envelope-from ) id 1igzuF-0002ix-PD; Mon, 16 Dec 2019 18:36:03 -0500 Received: by mail-wm1-x342.google.com with SMTP id b72so1095767wme.4; Mon, 16 Dec 2019 15:36: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=KgJxQ3LjCLFLnWv58xt5yNS0nKdFJ3g6xvCyzpqE8X4=; b=M1WXKp6fZxUthtIB492QWEUpCNnaTWHA21awHIsJxtuEMqSNqv0hKpd9xfVRLXppW7 IMy3+Ex3D8rqRUJ6O2eA8/LpAcLlMlxDTnfraeLHuWzIlTGj1cjiuj9suLegp2AtyYRw AKW+N8yVO4iBz7HgJ1P9ZHMo1Z1+N7Lht1rbah3gOwXsjy6/EzQJT8gen9mUhqkfo4NF H5af2XAcvT3G6dk8oh2VrsEtThzJWLaQ0USJ4wcj1VOrlN/uEykWkaG7X+W3Rq8MYoVZ JfW/vIY09k7Hm4Ct2LjZ6G2dxJ4zTv1kcVwcmwIgzIe/g9tm4GI+AjowiH4183ex253I nPqg== 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=KgJxQ3LjCLFLnWv58xt5yNS0nKdFJ3g6xvCyzpqE8X4=; b=NB8/5xQBNreBx5joxRkAtwWOq3bfoa/xLMCzk147LbhkH5+te44hf08ZGMgvoep92i s8Wd4sBWAsbosWLcEcZzTKIavositCAJkGJrfzxOIgqD9ZqlWOuEFKRg8GwqMh1C+5um mQErV3RN3Ubuj0pc47uVyQnevOjEX03EgLYoqJWRw3cN+TFRaSru+at2YccOR3I1GuIh 8rRBQKQj2nu6tawhsCBD4SkQyBhGE632MGjUH3IL0dB1OUTtX4FsQ9wazqPLaQsDzs+6 AWo1ugbdE0mERDRA6MsVzpp1413Ts3M3bMskZtEBGDpxgMf9MYhyDM0eGOSQarx3dg5r 9PCQ== X-Gm-Message-State: APjAAAV/KPbjEi+0fWzJLYxJ0wg07FEp+QoE1S8wMgfLWBRGOpzZjyti Malh5gtlMTKsYZT0kJ/bBtd4u57f X-Google-Smtp-Source: APXvYqzMhqcUw29hPAlGb+/ZtFIFjCincL+XSAbbhdMUbVWTgwrJVO46zzHT+FvX1FlilJDPnRLxdg== X-Received: by 2002:a7b:cc81:: with SMTP id p1mr1746618wma.62.1576539362453; Mon, 16 Dec 2019 15:36:02 -0800 (PST) Received: from pavilion.home ([2a02:a456:6be8:1:8edc:d4ff:fe8b:18b7]) by smtp.gmail.com with ESMTPSA id z83sm984501wmg.2.2019.12.16.15.36.01 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 16 Dec 2019 15:36:02 -0800 (PST) From: Niek Linnenbank To: qemu-devel@nongnu.org Subject: [PATCH v2 08/10] arm: allwinner-h3: add Security Identifier device Date: Tue, 17 Dec 2019 00:35:17 +0100 Message-Id: <20191216233519.29030-9-nieklinnenbank@gmail.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20191216233519.29030-1-nieklinnenbank@gmail.com> References: <20191216233519.29030-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-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: peter.maydell@linaro.org, Niek Linnenbank , qemu-arm@nongnu.org, philmd@redhat.com 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 a 128-bit UUID value as input. Signed-off-by: Niek Linnenbank --- include/hw/arm/allwinner-h3.h | 2 + include/hw/misc/allwinner-h3-sid.h | 40 +++++++ hw/arm/allwinner-h3.c | 7 ++ hw/arm/orangepi.c | 4 + hw/misc/allwinner-h3-sid.c | 179 +++++++++++++++++++++++++++++ hw/misc/Makefile.objs | 1 + hw/misc/trace-events | 4 + 7 files changed, 237 insertions(+) create mode 100644 include/hw/misc/allwinner-h3-sid.h create mode 100644 hw/misc/allwinner-h3-sid.c diff --git a/include/hw/arm/allwinner-h3.h b/include/hw/arm/allwinner-h3.h index 8128ae6131..c98c1972a6 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" enum { @@ -77,6 +78,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..79c9a24459 --- /dev/null +++ b/include/hw/misc/allwinner-h3-sid.h @@ -0,0 +1,40 @@ +/* + * 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" +#include "qemu/uuid.h" + +#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; + QemuUUID identifier; +} AwH3SidState; + +#endif diff --git a/hw/arm/allwinner-h3.c b/hw/arm/allwinner-h3.c index 1a9748ab2e..ba34f905cd 100644 --- a/hw/arm/allwinner-h3.c +++ b/hw/arm/allwinner-h3.c @@ -196,6 +196,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) @@ -332,6 +335,10 @@ static void aw_h3_realize(DeviceState *dev, Error **errp) qdev_init_nofail(DEVICE(&s->cpucfg)); sysbus_mmio_map(SYS_BUS_DEVICE(&s->cpucfg), 0, s->memmap[AW_H3_CPUCFG]); + /* Security Identifier */ + qdev_init_nofail(DEVICE(&s->sid)); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->sid), 0, s->memmap[AW_H3_SID]); + /* Universal Serial Bus */ sysbus_create_simple(TYPE_AW_H3_EHCI, s->memmap[AW_H3_EHCI0], qdev_get_gpio_in(DEVICE(&s->gic), diff --git a/hw/arm/orangepi.c b/hw/arm/orangepi.c index 62cefc8c06..b01c4b4f01 100644 --- a/hw/arm/orangepi.c +++ b/hw/arm/orangepi.c @@ -62,6 +62,10 @@ static void orangepi_init(MachineState *machine) exit(1); } + /* Setup SID properties */ + qdev_prop_set_string(DEVICE(&s->h3->sid), "identifier", + "8100c002-0001-0002-0003-000044556677"); + /* Mark H3 object realized */ object_property_set_bool(OBJECT(s->h3), true, "realized", &error_abort); if (error_abort != NULL) { diff --git a/hw/misc/allwinner-h3-sid.c b/hw/misc/allwinner-h3-sid.c new file mode 100644 index 0000000000..c472f2bcc6 --- /dev/null +++ b/hw/misc/allwinner-h3-sid.c @@ -0,0 +1,179 @@ +/* + * 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 "qemu/units.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/qdev-properties.h" +#include "hw/misc/allwinner-h3-sid.h" +#include "trace.h" + +/* SID register offsets */ +enum { + REG_PRCTL = 0x40, /* Control */ + REG_RDKEY = 0x60, /* Read Key */ +}; + +/* SID register flags */ +enum { + REG_PRCTL_WRITE = 0x0002, /* Unknown write flag */ + REG_PRCTL_OP_LOCK = 0xAC00, /* 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; + } + + trace_allwinner_h3_sid_read(offset, val, size); + + return val; +} + +static void allwinner_h3_sid_write(void *opaque, hwaddr offset, + uint64_t val, unsigned size) +{ + AwH3SidState *s = (AwH3SidState *)opaque; + + trace_allwinner_h3_sid_write(offset, val, size); + + switch (offset) { + case REG_PRCTL: /* Control */ + s->control = val; + + if ((s->control & REG_PRCTL_OP_LOCK) && + (s->control & REG_PRCTL_WRITE)) { + uint32_t id = s->control >> 16; + + if (id < sizeof(QemuUUID)) { + s->rdkey = (s->identifier.data[id]) | + (s->identifier.data[id + 1] << 8) | + (s->identifier.data[id + 2] << 16) | + (s->identifier.data[id + 3] << 24); + } + } + s->control &= ~REG_PRCTL_WRITE; + 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 + }, + .impl.min_access_size = 4, +}; + +static void allwinner_h3_sid_reset(DeviceState *dev) +{ + AwH3SidState *s = AW_H3_SID(dev); + + /* Set default values for registers */ + s->control = 0; + s->rdkey = 0; +} + +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); + + /* Fill UUID with zeroes by default */ + qemu_uuid_parse(UUID_NONE, &s->identifier); + + /* Memory mapping */ + memory_region_init_io(&s->iomem, OBJECT(s), &allwinner_h3_sid_ops, s, + TYPE_AW_H3_SID, 1 * KiB); + sysbus_init_mmio(sbd, &s->iomem); +} + +static Property allwinner_h3_sid_properties[] = { + DEFINE_PROP_UUID_NODEFAULT("identifier", AwH3SidState, identifier), + DEFINE_PROP_END_OF_LIST() +}; + +static const VMStateDescription allwinner_h3_sid_vmstate = { + .name = "allwinner-h3-sid", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32(control, AwH3SidState), + VMSTATE_UINT32(rdkey, AwH3SidState), + 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; + dc->props = allwinner_h3_sid_properties; +} + +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/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/trace-events b/hw/misc/trace-events index b93089d010..a777844ca3 100644 --- a/hw/misc/trace-events +++ b/hw/misc/trace-events @@ -5,6 +5,10 @@ allwinner_h3_cpucfg_cpu_reset(uint8_t cpu_id, uint32_t reset_addr) "H3-CPUCFG: c allwinner_h3_cpucfg_read(uint64_t offset, uint64_t data, unsigned size) "H3-CPUCFG: read: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %" PRIu32 allwinner_h3_cpucfg_write(uint64_t offset, uint64_t data, unsigned size) "H3-CPUCFG: write: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %" PRIu32 +# allwinner-h3-sid.c +allwinner_h3_sid_read(uint64_t offset, uint64_t data, unsigned size) "H3-SID: read: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %" PRIu32 +allwinner_h3_sid_write(uint64_t offset, uint64_t data, unsigned size) "H3-SID: write: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %" PRIu32 + # 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" From patchwork Mon Dec 16 23:35:18 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Niek Linnenbank X-Patchwork-Id: 11296227 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 29992930 for ; Mon, 16 Dec 2019 23:43:56 +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 D22FB24672 for ; Mon, 16 Dec 2019 23:43:55 +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="hzZ0d7Qb" DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org D22FB24672 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]:33422 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1ih01r-0000ky-3r for patchwork-qemu-devel@patchwork.kernel.org; Mon, 16 Dec 2019 18:43:55 -0500 Received: from eggs.gnu.org ([2001:470:142:3::10]:50391) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1igzuM-0006lQ-3P for qemu-devel@nongnu.org; Mon, 16 Dec 2019 18:36:13 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1igzuI-0002rk-5G for qemu-devel@nongnu.org; Mon, 16 Dec 2019 18:36:09 -0500 Received: from mail-wr1-x42d.google.com ([2a00:1450:4864:20::42d]:39769) by eggs.gnu.org with esmtps (TLS1.0:RSA_AES_128_CBC_SHA1:16) (Exim 4.71) (envelope-from ) id 1igzuH-0002p3-O5; Mon, 16 Dec 2019 18:36:06 -0500 Received: by mail-wr1-x42d.google.com with SMTP id y11so9314811wrt.6; Mon, 16 Dec 2019 15:36:05 -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=N2q6drgE+F85mQm/6rYbIFSJHCjfLseWOeR8emzD/I8=; b=hzZ0d7QbPmtukjChabMvs8iu9zsAmDK8UbeR35Jjiz3LlKhWtFk2OuF8tqOgxpXp80 PugpbXzy0h1v0RXN2idOr8SfSdZk9BvbWMCEAoA+vlyhwOLMYUOWFrEzk3gSfP+STBg5 AlxlEjQ0MwNL5HphZ01TsI4IEBaPj/lQRQKt+Q6UMKvfiUG6cOj5R3DxNAyvn4iT08eX NHX4i8gLMqogu6SyweJXgB9zchHZDpjwHgF0uNsRhOq8QnPphheuVoAfghYsAnoll36/ zDfTsZAOrWcQWsVHHGu7ghT0deab5c0O+o0TiKfvSUvxlynlmwFQO8IcxoAKtH+r38O+ RqYQ== 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=N2q6drgE+F85mQm/6rYbIFSJHCjfLseWOeR8emzD/I8=; b=YSbIpDjxAmkmyw0Lc6F4iRCo7CXfa8Moa8kdnGJL0f4n/FhBMgHOALhADvqw68Tgb4 ZSVTDj+pelpp3wAFAgRsfDsHEEqoa0wY1FdWGBAos19jDhAxXZwX/uUGKCZRQR0kntkl 845Bh50EXbuSyP7URyEdnrkNX2X5xKtHnFe37LXAUsX8JJvC5vK4XDXIjAWcrhwNn1AB M7HwE89QzEu/j8Ncl7qVJnq6naWLr79nssfLwwuI87fxpBwsTe26bZ1/MLarB8Eqz/2I Sa8Mbr38oIk87SpnkF0M3Im6Lz2xNcPoyjfmhW6beBjw43qTc1QgyAKVT9nAuLt8chAG n4dg== X-Gm-Message-State: APjAAAWeQfrgtb3Lw0vdUIcO1zv650N4/6Hax/atpiPaxYzMJFNdWHJg 5r4FhNX+urf3TzHuBgsfr8EiLLu8 X-Google-Smtp-Source: APXvYqyiwIrI1Cjcm/hO/fPusFS0Ogs3aopiCJ0miVlltq2BCX8gl5oAZ8KtlKkpe2/cxOE0Y2qEUQ== X-Received: by 2002:a5d:5283:: with SMTP id c3mr34817934wrv.148.1576539363592; Mon, 16 Dec 2019 15:36:03 -0800 (PST) Received: from pavilion.home ([2a02:a456:6be8:1:8edc:d4ff:fe8b:18b7]) by smtp.gmail.com with ESMTPSA id z83sm984501wmg.2.2019.12.16.15.36.02 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 16 Dec 2019 15:36:02 -0800 (PST) From: Niek Linnenbank To: qemu-devel@nongnu.org Subject: [PATCH v2 09/10] arm: allwinner-h3: add SD/MMC host controller Date: Tue, 17 Dec 2019 00:35:18 +0100 Message-Id: <20191216233519.29030-10-nieklinnenbank@gmail.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20191216233519.29030-1-nieklinnenbank@gmail.com> References: <20191216233519.29030-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::42d 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: peter.maydell@linaro.org, Niek Linnenbank , qemu-arm@nongnu.org, philmd@redhat.com 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 --- include/hw/arm/allwinner-h3.h | 2 + include/hw/sd/allwinner-h3-sdhost.h | 71 +++ hw/arm/allwinner-h3.c | 16 + hw/arm/orangepi.c | 15 + hw/sd/allwinner-h3-sdhost.c | 813 ++++++++++++++++++++++++++++ hw/sd/Makefile.objs | 1 + hw/sd/trace-events | 7 + 7 files changed, 925 insertions(+) create mode 100644 include/hw/sd/allwinner-h3-sdhost.h create mode 100644 hw/sd/allwinner-h3-sdhost.c diff --git a/include/hw/arm/allwinner-h3.h b/include/hw/arm/allwinner-h3.h index c98c1972a6..ab564987be 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" enum { @@ -79,6 +80,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..4a93405e6c --- /dev/null +++ b/include/hw/sd/allwinner-h3-sdhost.h @@ -0,0 +1,71 @@ +/* + * 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 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 diff --git a/hw/arm/allwinner-h3.c b/hw/arm/allwinner-h3.c index ba34f905cd..b4ee524ee0 100644 --- a/hw/arm/allwinner-h3.c +++ b/hw/arm/allwinner-h3.c @@ -199,6 +199,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) @@ -339,6 +342,19 @@ static void aw_h3_realize(DeviceState *dev, Error **errp) qdev_init_nofail(DEVICE(&s->sid)); sysbus_mmio_map(SYS_BUS_DEVICE(&s->sid), 0, s->memmap[AW_H3_SID]); + /* SD/MMC */ + qdev_init_nofail(DEVICE(&s->mmc0)); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->mmc0), 0, s->memmap[AW_H3_MMC0]); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->mmc0), 0, + qdev_get_gpio_in(DEVICE(&s->gic), AW_H3_GIC_SPI_MMC0)); + + object_property_add_alias(OBJECT(s), "sd-bus", OBJECT(&s->mmc0), + "sd-bus", &error_abort); + if (error_abort) { + error_propagate(errp, error_abort); + return; + } + /* Universal Serial Bus */ sysbus_create_simple(TYPE_AW_H3_EHCI, s->memmap[AW_H3_EHCI0], qdev_get_gpio_in(DEVICE(&s->gic), diff --git a/hw/arm/orangepi.c b/hw/arm/orangepi.c index b01c4b4f01..87968505ae 100644 --- a/hw/arm/orangepi.c +++ b/hw/arm/orangepi.c @@ -38,6 +38,10 @@ typedef struct OrangePiState { static void orangepi_init(MachineState *machine) { OrangePiState *s = g_new(OrangePiState, 1); + DriveInfo *di; + BlockBackend *blk; + BusState *bus; + DeviceState *carddev; /* Only allow Cortex-A7 for this board */ if (strcmp(machine->cpu_type, ARM_CPU_TYPE_NAME("cortex-a7")) != 0) { @@ -73,6 +77,16 @@ static void orangepi_init(MachineState *machine) exit(1); } + /* Retrieve SD bus */ + di = drive_get_next(IF_SD); + blk = di ? blk_by_legacy_dinfo(di) : NULL; + bus = qdev_get_child_bus(DEVICE(s->h3), "sd-bus"); + + /* Plug in SD card */ + 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 */ if (machine->ram_size > 1 * GiB) { error_report("Requested ram size is too large for this machine: " @@ -95,6 +109,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/allwinner-h3-sdhost.c b/hw/sd/allwinner-h3-sdhost.c new file mode 100644 index 0000000000..c6661af614 --- /dev/null +++ b/hw/sd/allwinner-h3-sdhost.c @@ -0,0 +1,813 @@ +/* + * 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 "qemu/units.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 */ +enum { + REG_SD_GCTL = 0x00, /* Global Control */ + REG_SD_CKCR = 0x04, /* Clock Control */ + REG_SD_TMOR = 0x08, /* Timeout */ + REG_SD_BWDR = 0x0C, /* Bus Width */ + REG_SD_BKSR = 0x10, /* Block Size */ + REG_SD_BYCR = 0x14, /* Byte Count */ + REG_SD_CMDR = 0x18, /* Command */ + REG_SD_CAGR = 0x1C, /* Command Argument */ + REG_SD_RESP0 = 0x20, /* Response Zero */ + REG_SD_RESP1 = 0x24, /* Response One */ + REG_SD_RESP2 = 0x28, /* Response Two */ + REG_SD_RESP3 = 0x2C, /* Response Three */ + REG_SD_IMKR = 0x30, /* Interrupt Mask */ + REG_SD_MISR = 0x34, /* Masked Interrupt Status */ + REG_SD_RISR = 0x38, /* Raw Interrupt Status */ + REG_SD_STAR = 0x3C, /* Status */ + REG_SD_FWLR = 0x40, /* FIFO Water Level */ + REG_SD_FUNS = 0x44, /* FIFO Function Select */ + REG_SD_DBGC = 0x50, /* Debug Enable */ + REG_SD_A12A = 0x58, /* Auto command 12 argument */ + REG_SD_NTSR = 0x5C, /* SD NewTiming Set */ + REG_SD_SDBG = 0x60, /* SD newTiming Set Debug */ + REG_SD_HWRST = 0x78, /* Hardware Reset Register */ + REG_SD_DMAC = 0x80, /* Internal DMA Controller Control */ + REG_SD_DLBA = 0x84, /* Descriptor List Base Address */ + REG_SD_IDST = 0x88, /* Internal DMA Controller Status */ + REG_SD_IDIE = 0x8C, /* Internal DMA Controller IRQ Enable */ + REG_SD_THLDC = 0x100, /* Card Threshold Control */ + REG_SD_DSBD = 0x10C, /* eMMC DDR Start Bit Detection Control */ + REG_SD_RES_CRC = 0x110, /* Response CRC from card/eMMC */ + REG_SD_DATA7_CRC = 0x114, /* CRC Data 7 from card/eMMC */ + REG_SD_DATA6_CRC = 0x118, /* CRC Data 6 from card/eMMC */ + REG_SD_DATA5_CRC = 0x11C, /* CRC Data 5 from card/eMMC */ + REG_SD_DATA4_CRC = 0x120, /* CRC Data 4 from card/eMMC */ + REG_SD_DATA3_CRC = 0x124, /* CRC Data 3 from card/eMMC */ + REG_SD_DATA2_CRC = 0x128, /* CRC Data 2 from card/eMMC */ + REG_SD_DATA1_CRC = 0x12C, /* CRC Data 1 from card/eMMC */ + REG_SD_DATA0_CRC = 0x130, /* CRC Data 0 from card/eMMC */ + REG_SD_CRC_STA = 0x134, /* CRC status from card/eMMC during write */ + REG_SD_FIFO = 0x200, /* Read/Write FIFO */ +}; + +/* SD Host register flags */ +enum { + SD_GCTL_FIFO_AC_MOD = (1 << 31), + SD_GCTL_DDR_MOD_SEL = (1 << 10), + SD_GCTL_CD_DBC_ENB = (1 << 8), + SD_GCTL_DMA_ENB = (1 << 5), + SD_GCTL_INT_ENB = (1 << 4), + SD_GCTL_DMA_RST = (1 << 2), + SD_GCTL_FIFO_RST = (1 << 1), + SD_GCTL_SOFT_RST = (1 << 0), +}; + +enum { + SD_CMDR_LOAD = (1 << 31), + SD_CMDR_CLKCHANGE = (1 << 21), + SD_CMDR_WRITE = (1 << 10), + SD_CMDR_AUTOSTOP = (1 << 12), + SD_CMDR_DATA = (1 << 9), + SD_CMDR_RESPONSE_LONG = (1 << 7), + SD_CMDR_RESPONSE = (1 << 6), + SD_CMDR_CMDID_MASK = (0x3f), +}; + +enum { + SD_RISR_CARD_REMOVE = (1 << 31), + SD_RISR_CARD_INSERT = (1 << 30), + SD_RISR_AUTOCMD_DONE = (1 << 14), + SD_RISR_DATA_COMPLETE = (1 << 3), + SD_RISR_CMD_COMPLETE = (1 << 2), + SD_RISR_NO_RESPONSE = (1 << 1), +}; + +enum { + SD_STAR_CARD_PRESENT = (1 << 8), +}; + +enum { + SD_IDST_SUM_RECEIVE_IRQ = (1 << 8), + SD_IDST_RECEIVE_IRQ = (1 << 1), + SD_IDST_TRANSMIT_IRQ = (1 << 0), + SD_IDST_IRQ_MASK = (1 << 1) | (1 << 0) | (1 << 8), + SD_IDST_WR_MASK = (0x3ff), +}; + +/* SD Host register reset values */ +enum { + REG_SD_GCTL_RST = 0x00000300, + REG_SD_CKCR_RST = 0x0, + REG_SD_TMOR_RST = 0xFFFFFF40, + REG_SD_BWDR_RST = 0x0, + REG_SD_BKSR_RST = 0x00000200, + REG_SD_BYCR_RST = 0x00000200, + REG_SD_CMDR_RST = 0x0, + REG_SD_CAGR_RST = 0x0, + REG_SD_RESP_RST = 0x0, + REG_SD_IMKR_RST = 0x0, + REG_SD_MISR_RST = 0x0, + REG_SD_RISR_RST = 0x0, + REG_SD_STAR_RST = 0x00000100, + REG_SD_FWLR_RST = 0x000F0000, + REG_SD_FUNS_RST = 0x0, + REG_SD_DBGC_RST = 0x0, + REG_SD_A12A_RST = 0x0000FFFF, + REG_SD_NTSR_RST = 0x00000001, + REG_SD_SDBG_RST = 0x0, + REG_SD_HWRST_RST = 0x00000001, + REG_SD_DMAC_RST = 0x0, + REG_SD_DLBA_RST = 0x0, + REG_SD_IDST_RST = 0x0, + REG_SD_IDIE_RST = 0x0, + REG_SD_THLDC_RST = 0x0, + REG_SD_DSBD_RST = 0x0, + REG_SD_RES_CRC_RST = 0x0, + REG_SD_DATA_CRC_RST = 0x0, + REG_SD_CRC_STA_RST = 0x0, + 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 */ +enum { + DESC_STATUS_HOLD = (1 << 31), /* Set when descriptor is in use by DMA */ + DESC_STATUS_ERROR = (1 << 30), /* Set when DMA transfer error occurred */ + DESC_STATUS_CHAIN = (1 << 4), /* Indicates chained descriptor. */ + DESC_STATUS_FIRST = (1 << 3), /* Set on the first descriptor */ + DESC_STATUS_LAST = (1 << 2), /* Set on the last descriptor */ + DESC_STATUS_NOIRQ = (1 << 1), /* Skip raising interrupt after transfer */ + DESC_SIZE_MASK = (0xfffffffc) +}; + +static void aw_h3_sdhost_update_irq(AwH3SDHostState *s) +{ + uint32_t irq; + + if (s->global_ctl & SD_GCTL_INT_ENB) { + irq = s->irq_status & s->irq_mask; + } else { + irq = 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 == 4 && !(s->command & SD_CMDR_RESPONSE_LONG)) { + s->response[0] = ldl_be_p(&resp[0]); + s->response[1] = s->response[2] = s->response[3] = 0; + + } else if (rlen == 16 && (s->command & SD_CMDR_RESPONSE_LONG)) { + 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]); + } else { + goto error; + } + } + } + + /* 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 = 64 * KiB; + } + 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, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + .unaligned = false + }, + .impl.min_access_size = 4, +}; + +static const VMStateDescription vmstate_aw_h3_sdhost = { + .name = "allwinner-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, 4 * KiB); + 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 < ARRAY_SIZE(s->response); 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 < ARRAY_SIZE(s->data_crc); 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/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/trace-events b/hw/sd/trace-events index efcff666a2..5016cefc78 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 %" PRIu32 " is_write %u max_bytes %" PRIu32 +aw_h3_sdhost_read(uint64_t offset, uint64_t data, unsigned size) "offset 0x%" PRIx64 " data 0x%" PRIx64 " size %" PRIu32 +aw_h3_sdhost_write(uint64_t offset, uint64_t data, unsigned size) "offset 0x%" PRIx64 " data 0x%" PRIx64 " size %" PRIu32 +aw_h3_sdhost_update_irq(uint32_t irq) "IRQ bits 0x%" PRIx32 + # 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" From patchwork Mon Dec 16 23:35:19 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Niek Linnenbank X-Patchwork-Id: 11296217 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 EF11A112B for ; Mon, 16 Dec 2019 23:40: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 AA1BC24672 for ; Mon, 16 Dec 2019 23:40: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="boBcb+hS" DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org AA1BC24672 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]:33374 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1igzyi-0005EV-EA for patchwork-qemu-devel@patchwork.kernel.org; Mon, 16 Dec 2019 18:40:40 -0500 Received: from eggs.gnu.org ([2001:470:142:3::10]:50400) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1igzuM-0006m9-Fy for qemu-devel@nongnu.org; Mon, 16 Dec 2019 18:36:13 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1igzuI-0002sP-Iz for qemu-devel@nongnu.org; Mon, 16 Dec 2019 18:36:10 -0500 Received: from mail-wm1-x343.google.com ([2a00:1450:4864:20::343]:35279) by eggs.gnu.org with esmtps (TLS1.0:RSA_AES_128_CBC_SHA1:16) (Exim 4.71) (envelope-from ) id 1igzuI-0002pt-6k; Mon, 16 Dec 2019 18:36:06 -0500 Received: by mail-wm1-x343.google.com with SMTP id p17so1119707wmb.0; Mon, 16 Dec 2019 15:36: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=hBGfbW/ixPnf//Y+YQgETNU2Ce5Q2hr9RLhxfiIN/Sg=; b=boBcb+hSU3vvWLoHh5aYtQCoNzpOYh1p7n6wYHnLl6YWW1GlChGrwMrgHpQcmBIdo6 RGQspFFCYv/Zlvm6x4ED7RIsEuQeDElNVP4DMexOmSs8pg+QT/v4ggVQQnScVjAND7Lf xKALDwW50+owTNErrYJLnapZ+cF64U0UvXK/NyA7x5YU5ezUSnUcugZhwLhRPBn31BI5 HM6D/aFiSnDiL+5w55Faae6JPiwcKEQwJiR2ZUIKIQxwg03pk780S+mQf6vbKVzpoyaK +b0WKG7JDE1YLUHe80Mw9QdcvmUtVtntLOmtFV/rz5mhKCzAIC6sj//o15C5GOagMmh+ o8OQ== 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=hBGfbW/ixPnf//Y+YQgETNU2Ce5Q2hr9RLhxfiIN/Sg=; b=ccG6xhb9jBuR25iOcDjpNwMhZ4+WbboebCjJ1H9xeIh3o5p3KFDPOQG9nUvamFKlyG ODMjX6yHZ04yEofu6ZSWgbVQNJNY8RnQNM2SwxayGD+bLOcUSKrLJjxg+dz5xURj+mzt lmBjyxpf1EE0h1hIjrxuAhs/rZmz4EFI3etoEPDoTQF4EkgvJxUsaSutbQ33aRsEeYAJ HC1q3izlVvqXRCaD6tga6Jg+B3mcGAzpFEK5uqXZxhh+AuXlfmbX8xgd3BqSx/QuYV3l HgkxMRw5m5wcr4i4Q+1R2lNm+ha+/1na8JPFoScqdcZKHJv8t4bfyI0P/+47OVFxaUmj Fahw== X-Gm-Message-State: APjAAAU/Sn/jUATsXkl1Ca/BXnnMGxPJdTOLIwMMKZXtfabfC6dmc0rw bgoiMu/O9JObQJU0fAtjaVVdeSMz X-Google-Smtp-Source: APXvYqzldI1qq1WBVrj3pR08YWBp8BfkmFZxh9uGcRIxIhs0/kQqwHEIyJZJs52qlC4PILeR3rhFcg== X-Received: by 2002:a1c:1fc5:: with SMTP id f188mr1771215wmf.55.1576539364254; Mon, 16 Dec 2019 15:36:04 -0800 (PST) Received: from pavilion.home ([2a02:a456:6be8:1:8edc:d4ff:fe8b:18b7]) by smtp.gmail.com with ESMTPSA id z83sm984501wmg.2.2019.12.16.15.36.03 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 16 Dec 2019 15:36:03 -0800 (PST) From: Niek Linnenbank To: qemu-devel@nongnu.org Subject: [PATCH v2 10/10] arm: allwinner-h3: add EMAC ethernet device Date: Tue, 17 Dec 2019 00:35:19 +0100 Message-Id: <20191216233519.29030-11-nieklinnenbank@gmail.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20191216233519.29030-1-nieklinnenbank@gmail.com> References: <20191216233519.29030-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-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: peter.maydell@linaro.org, Niek Linnenbank , qemu-arm@nongnu.org, philmd@redhat.com 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 --- include/hw/arm/allwinner-h3.h | 2 + include/hw/net/allwinner-h3-emac.h | 67 +++ hw/arm/allwinner-h3.c | 13 + hw/arm/orangepi.c | 7 + hw/net/allwinner-h3-emac.c | 831 +++++++++++++++++++++++++++++ hw/arm/Kconfig | 1 + hw/net/Kconfig | 3 + hw/net/Makefile.objs | 1 + hw/net/trace-events | 10 + 9 files changed, 935 insertions(+) create mode 100644 include/hw/net/allwinner-h3-emac.h create mode 100644 hw/net/allwinner-h3-emac.c diff --git a/include/hw/arm/allwinner-h3.h b/include/hw/arm/allwinner-h3.h index ab564987be..357bdfa711 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" enum { @@ -81,6 +82,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..8fb2bd6e87 --- /dev/null +++ b/include/hw/net/allwinner-h3-emac.h @@ -0,0 +1,67 @@ +/* + * 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 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 diff --git a/hw/arm/allwinner-h3.c b/hw/arm/allwinner-h3.c index b4ee524ee0..b49f8d81ac 100644 --- a/hw/arm/allwinner-h3.c +++ b/hw/arm/allwinner-h3.c @@ -202,6 +202,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) @@ -355,6 +358,16 @@ 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]); + } + qdev_init_nofail(DEVICE(&s->emac)); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->emac), 0, s->memmap[AW_H3_EMAC]); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->emac), 0, + qdev_get_gpio_in(DEVICE(&s->gic), AW_H3_GIC_SPI_EMAC)); + /* Universal Serial Bus */ sysbus_create_simple(TYPE_AW_H3_EHCI, s->memmap[AW_H3_EHCI0], qdev_get_gpio_in(DEVICE(&s->gic), diff --git a/hw/arm/orangepi.c b/hw/arm/orangepi.c index 87968505ae..119f370924 100644 --- a/hw/arm/orangepi.c +++ b/hw/arm/orangepi.c @@ -70,6 +70,13 @@ static void orangepi_init(MachineState *machine) qdev_prop_set_string(DEVICE(&s->h3->sid), "identifier", "8100c002-0001-0002-0003-000044556677"); + /* Setup EMAC properties */ + object_property_set_int(OBJECT(&s->h3->emac), 1, "phy-addr", &error_abort); + if (error_abort != NULL) { + error_reportf_err(error_abort, "Couldn't set phy address: "); + exit(1); + } + /* Mark H3 object realized */ object_property_set_bool(OBJECT(s->h3), true, "realized", &error_abort); if (error_abort != NULL) { diff --git a/hw/net/allwinner-h3-emac.c b/hw/net/allwinner-h3-emac.c new file mode 100644 index 0000000000..7a0daced83 --- /dev/null +++ b/hw/net/allwinner-h3-emac.c @@ -0,0 +1,831 @@ +/* + * 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 "qemu/units.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 */ +enum { + REG_BASIC_CTL_0 = 0x0000, /* Basic Control 0 */ + REG_BASIC_CTL_1 = 0x0004, /* Basic Control 1 */ + REG_INT_STA = 0x0008, /* Interrupt Status */ + REG_INT_EN = 0x000C, /* Interrupt Enable */ + REG_TX_CTL_0 = 0x0010, /* Transmit Control 0 */ + REG_TX_CTL_1 = 0x0014, /* Transmit Control 1 */ + REG_TX_FLOW_CTL = 0x001C, /* Transmit Flow Control */ + REG_TX_DMA_DESC_LIST = 0x0020, /* Transmit Descriptor List Address */ + REG_RX_CTL_0 = 0x0024, /* Receive Control 0 */ + REG_RX_CTL_1 = 0x0028, /* Receive Control 1 */ + REG_RX_DMA_DESC_LIST = 0x0034, /* Receive Descriptor List Address */ + REG_FRM_FLT = 0x0038, /* Receive Frame Filter */ + REG_RX_HASH_0 = 0x0040, /* Receive Hash Table 0 */ + REG_RX_HASH_1 = 0x0044, /* Receive Hash Table 1 */ + REG_MII_CMD = 0x0048, /* Management Interface Command */ + REG_MII_DATA = 0x004C, /* Management Interface Data */ + REG_ADDR_HIGH = 0x0050, /* MAC Address High */ + REG_ADDR_LOW = 0x0054, /* MAC Address Low */ + REG_TX_DMA_STA = 0x00B0, /* Transmit DMA Status */ + REG_TX_CUR_DESC = 0x00B4, /* Transmit Current Descriptor */ + REG_TX_CUR_BUF = 0x00B8, /* Transmit Current Buffer */ + REG_RX_DMA_STA = 0x00C0, /* Receive DMA Status */ + REG_RX_CUR_DESC = 0x00C4, /* Receive Current Descriptor */ + REG_RX_CUR_BUF = 0x00C8, /* Receive Current Buffer */ + REG_RGMII_STA = 0x00D0, /* RGMII Status */ +}; + +/* EMAC register flags */ +enum { + BASIC_CTL0_100Mbps = (0b11 << 2), + BASIC_CTL0_FD = (1 << 0), + BASIC_CTL1_SOFTRST = (1 << 0), +}; + +enum { + INT_STA_RGMII_LINK = (1 << 16), + INT_STA_RX_EARLY = (1 << 13), + INT_STA_RX_OVERFLOW = (1 << 12), + INT_STA_RX_TIMEOUT = (1 << 11), + INT_STA_RX_DMA_STOP = (1 << 10), + INT_STA_RX_BUF_UA = (1 << 9), + INT_STA_RX = (1 << 8), + INT_STA_TX_EARLY = (1 << 5), + INT_STA_TX_UNDERFLOW = (1 << 4), + INT_STA_TX_TIMEOUT = (1 << 3), + INT_STA_TX_BUF_UA = (1 << 2), + INT_STA_TX_DMA_STOP = (1 << 1), + INT_STA_TX = (1 << 0), +}; + +enum { + INT_EN_RX_EARLY = (1 << 13), + INT_EN_RX_OVERFLOW = (1 << 12), + INT_EN_RX_TIMEOUT = (1 << 11), + INT_EN_RX_DMA_STOP = (1 << 10), + INT_EN_RX_BUF_UA = (1 << 9), + INT_EN_RX = (1 << 8), + INT_EN_TX_EARLY = (1 << 5), + INT_EN_TX_UNDERFLOW = (1 << 4), + INT_EN_TX_TIMEOUT = (1 << 3), + INT_EN_TX_BUF_UA = (1 << 2), + INT_EN_TX_DMA_STOP = (1 << 1), + INT_EN_TX = (1 << 0), +}; + +enum { + TX_CTL0_TX_EN = (1 << 31), + TX_CTL1_TX_DMA_START = (1 << 31), + TX_CTL1_TX_DMA_EN = (1 << 30), + TX_CTL1_TX_FLUSH = (1 << 0), +}; + +enum { + RX_CTL0_RX_EN = (1 << 31), + RX_CTL0_STRIP_FCS = (1 << 28), + RX_CTL0_CRC_IPV4 = (1 << 27), +}; + +enum { + RX_CTL1_RX_DMA_START = (1 << 31), + RX_CTL1_RX_DMA_EN = (1 << 30), + RX_CTL1_RX_MD = (1 << 1), +}; + +enum { + RX_FRM_FLT_DIS_ADDR = (1 << 31), +}; + +enum { + MII_CMD_PHY_ADDR_SHIFT = (12), + MII_CMD_PHY_ADDR_MASK = (0xf000), + MII_CMD_PHY_REG_SHIFT = (4), + MII_CMD_PHY_REG_MASK = (0xf0), + MII_CMD_PHY_RW = (1 << 1), + MII_CMD_PHY_BUSY = (1 << 0), +}; + +enum { + TX_DMA_STA_STOP = (0b000), + TX_DMA_STA_RUN_FETCH = (0b001), + TX_DMA_STA_WAIT_STA = (0b010), +}; + +enum { + RX_DMA_STA_STOP = (0b000), + RX_DMA_STA_RUN_FETCH = (0b001), + RX_DMA_STA_WAIT_FRM = (0b011), +}; + +enum { + RGMII_LINK_UP = (1 << 3), + RGMII_FD = (1 << 0), +}; + +/* EMAC register reset values */ +enum { + REG_BASIC_CTL_1_RST = 0x08000000, +}; + +/* EMAC constants */ +enum { + 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 */ +enum { + DESC_STATUS_CTL = (1 << 31), + DESC_STATUS2_BUF_SIZE_MASK = (0x7ff), +}; + +/* Transmit frame descriptor flags */ +enum { + TX_DESC_STATUS_LENGTH_ERR = (1 << 14), + TX_DESC_STATUS2_FIRST_DESC = (1 << 29), + TX_DESC_STATUS2_LAST_DESC = (1 << 30), + TX_DESC_STATUS2_CHECKSUM_MASK = (0x3 << 27), +}; + +/* Receive frame descriptor flags */ +enum { + RX_DESC_STATUS_FIRST_DESC = (1 << 9), + RX_DESC_STATUS_LAST_DESC = (1 << 8), + RX_DESC_STATUS_FRM_LEN_MASK = (0x3fff0000), + RX_DESC_STATUS_FRM_LEN_SHIFT = (16), + RX_DESC_STATUS_NO_BUF = (1 << 14), + RX_DESC_STATUS_HEADER_ERR = (1 << 7), + RX_DESC_STATUS_LENGTH_ERR = (1 << 4), + RX_DESC_STATUS_CRC_ERR = (1 << 1), + RX_DESC_STATUS_PAYLOAD_ERR = (1 << 0), + RX_DESC_STATUS2_RX_INT_CTL = (1 << 31), +}; + +/* MII register offsets */ +enum { + MII_REG_CR = (0x0), + MII_REG_ST = (0x1), + MII_REG_ID_HIGH = (0x2), + MII_REG_ID_LOW = (0x3), +}; + +/* MII register flags */ +enum { + MII_REG_CR_RESET = (1 << 15), + MII_REG_CR_POWERDOWN = (1 << 11), + MII_REG_CR_10Mbit = (0), + MII_REG_CR_100Mbit = (1 << 13), + MII_REG_CR_1000Mbit = (1 << 6), + MII_REG_CR_AUTO_NEG = (1 << 12), + MII_REG_CR_AUTO_NEG_RESTART = (1 << 9), + MII_REG_CR_FULLDUPLEX = (1 << 8), +}; + +enum { + MII_REG_ST_100BASE_T4 = (1 << 15), + MII_REG_ST_100BASE_X_FD = (1 << 14), + MII_REG_ST_100BASE_X_HD = (1 << 13), + MII_REG_ST_10_FD = (1 << 12), + MII_REG_ST_10_HD = (1 << 11), + MII_REG_ST_100BASE_T2_FD = (1 << 10), + MII_REG_ST_100BASE_T2_HD = (1 << 9), + MII_REG_ST_AUTONEG_COMPLETE = (1 << 5), + MII_REG_ST_AUTONEG_AVAIL = (1 << 3), + MII_REG_ST_LINK_UP = (1 << 2), +}; + +/* MII constants */ +enum { + MII_PHY_ID_HIGH = 0x0044, + 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 = AW_H3_EMAC(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 = AW_H3_EMAC(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, + .unaligned = false, + }, + .impl.min_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, 64 * KiB); + 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 = "allwinner-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/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/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/trace-events b/hw/net/trace-events index e70f12bee1..b972c53f75 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%" PRIx32 " value=0x%" PRIx32 +aw_h3_emac_mii_read_reg(uint32_t reg, uint32_t value) "MII read: reg=0x%" PRIx32 " value=0x%" PRIx32 +aw_h3_emac_receive(uint32_t desc, uint32_t paddr, uint32_t bytes) "RX packet: desc=0x%" PRIx32 " paddr=0x%" PRIx32 " bytes=%" PRIu32 +aw_h3_emac_transmit(uint32_t desc, uint32_t paddr, uint32_t bytes) "TX packet: desc=0x%" PRIx32 " paddr=0x%" PRIx32 " bytes=%" PRIu32 +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"