From nobody Tue Feb 10 23:55:08 2026 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass(p=none dis=none) header.from=163.com ARC-Seal: i=1; a=rsa-sha256; t=1681816948; cv=none; d=zohomail.com; s=zohoarc; b=IMRUT48A8AY3cU+8ENCkfypt/X3QSDN+7DRx8fEGHcytB45zG3vjH1OfSrdPaFWw0Xfr2UxP19C+433z6LY99GBHIUDEbgCNyNHilaWW+oqBgf9yd9KyoxyVYGjEdu75EUoV2KT1zOsBGkumpMofH5Ce9llTnnDckgL5phyq/AY= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1681816948; h=Content-Transfer-Encoding:Cc:Date:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:To; bh=UuGY0IiBcUTfj9TdIaOLhp2UW6jyui3uHheSw0k+mZI=; b=lE3Wzu7qh41MU1T6c0whf3fwqa+77aYn6+romKHrvxIAgaJsN1C+X6xwD6Bs4K5TY7pPtHF8QJE/IeTW77oIa64Afr9OKmvzMxwaPIHXw9eZCpTSJCyjFdKqCHw6ZkHLU+EbfXlsyiLjX7LPdLD/u02jSFsqNP2O5S/nlckzc74= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass header.from= (p=none dis=none) Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1681816948484299.8123805978897; Tue, 18 Apr 2023 04:22:28 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1pojOR-0006UW-6r; Tue, 18 Apr 2023 07:21:03 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1pojOO-0006Ru-PM; Tue, 18 Apr 2023 07:21:00 -0400 Received: from m12.mail.163.com ([220.181.12.198]) by eggs.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1pojOH-0006iv-Tw; Tue, 18 Apr 2023 07:21:00 -0400 Received: from DESKTOP-B1R4FVG.localdomain (unknown [218.201.129.19]) by zwqz-smtp-mta-g4-0 (Coremail) with SMTP id _____wDXy2EIfT5k6AUwBw--.24225S8; Tue, 18 Apr 2023 19:20:43 +0800 (CST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=163.com; s=s110527; h=From:Subject:Date:Message-Id:MIME-Version; bh=UuGY0 IiBcUTfj9TdIaOLhp2UW6jyui3uHheSw0k+mZI=; b=IFUKJzo4kTxNjWmCQnQh9 qHW/nE78VhK4OY9jP6M8RPg7p4RNHLu3xwD+rvD2Vbs0M3vNiWqnCJ1gWkpr3t/K oqYxlJDm6qfnQTeSOmlXlpjk6ssniNN5lzG/0/ESgqzjEIg6SZQGreCANzcj+D8Y mNSiTRAVTJyZMIQ4pvxcGo= From: qianfanguijin@163.com To: qemu-arm@nongnu.org, qemu-devel@nongnu.org Cc: Strahinja Jankovic , Peter Maydell , Beniamino Galvani , =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= , Niek Linnenbank , qianfan Zhao Subject: [PATCH v3 06/11] hw/arm/allwinner-r40: add SDRAM controller device Date: Tue, 18 Apr 2023 19:20:35 +0800 Message-Id: <20230418112040.12460-7-qianfanguijin@163.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20230418112040.12460-1-qianfanguijin@163.com> References: <20230418112040.12460-1-qianfanguijin@163.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-CM-TRANSID: _____wDXy2EIfT5k6AUwBw--.24225S8 X-Coremail-Antispam: 1Uf129KBjvAXoWftFWxCw15XrWxXrWxCr17ZFb_yoW5Gw48Xo WSgF45Zw4agw17Xr1Fgw1jyr13Kws8KrWxJw45GF43ua98JFs8G39xJwn8Xr4fWr4FkFn7 XFySgr1fZrWkCas3n29KB7ZKAUJUUUUU529EdanIXcx71UUUUU7v73VFW2AGmfu7bjvjm3 AaLaJ3UbIYCTnIWIevJa73UjIFyTuYvj4R1xRQUUUUU X-Originating-IP: [218.201.129.19] X-CM-SenderInfo: htld0w5dqj3xxmlqqiywtou0bp/1tbiXQxV7VWBpR1-OgAAsA Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Received-SPF: pass client-ip=220.181.12.198; envelope-from=qianfanguijin@163.com; helo=m12.mail.163.com X-Spam_score_int: -20 X-Spam_score: -2.1 X-Spam_bar: -- X-Spam_report: (-2.1 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, FREEMAIL_FROM=0.001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001, T_SCC_BODY_TEXT_LINE=-0.01 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: qemu-devel-bounces+importer=patchew.org@nongnu.org X-ZohoMail-DKIM: pass (identity @163.com) X-ZM-MESSAGEID: 1681816948989100001 Content-Type: text/plain; charset="utf-8" From: qianfan Zhao Types of memory that the SDRAM controller supports are DDR2/DDR3 and capacities of up to 2GiB. This commit adds emulation support of the Allwinner R40 SDRAM controller. This driver only support 256M, 512M and 1024M memory now. Signed-off-by: qianfan Zhao --- hw/arm/allwinner-r40.c | 21 +- hw/arm/bananapi_m2u.c | 7 + hw/misc/allwinner-r40-dramc.c | 513 ++++++++++++++++++++++++++ hw/misc/meson.build | 1 + hw/misc/trace-events | 14 + include/hw/arm/allwinner-r40.h | 13 +- include/hw/misc/allwinner-r40-dramc.h | 108 ++++++ 7 files changed, 674 insertions(+), 3 deletions(-) create mode 100644 hw/misc/allwinner-r40-dramc.c create mode 100644 include/hw/misc/allwinner-r40-dramc.h diff --git a/hw/arm/allwinner-r40.c b/hw/arm/allwinner-r40.c index 4bc582630c..0e4542d35f 100644 --- a/hw/arm/allwinner-r40.c +++ b/hw/arm/allwinner-r40.c @@ -31,6 +31,7 @@ #include "hw/loader.h" #include "sysemu/sysemu.h" #include "hw/arm/allwinner-r40.h" +#include "hw/misc/allwinner-r40-dramc.h" =20 /* Memory map */ const hwaddr allwinner_r40_memmap[] =3D { @@ -53,6 +54,9 @@ const hwaddr allwinner_r40_memmap[] =3D { [AW_R40_DEV_UART6] =3D 0x01c29800, [AW_R40_DEV_UART7] =3D 0x01c29c00, [AW_R40_DEV_TWI0] =3D 0x01c2ac00, + [AW_R40_DEV_DRAMCOM] =3D 0x01c62000, + [AW_R40_DEV_DRAMCTL] =3D 0x01c63000, + [AW_R40_DEV_DRAMPHY] =3D 0x01c65000, [AW_R40_DEV_GIC_DIST] =3D 0x01c81000, [AW_R40_DEV_GIC_CPU] =3D 0x01c82000, [AW_R40_DEV_GIC_HYP] =3D 0x01c84000, @@ -129,8 +133,6 @@ static struct AwR40Unimplemented r40_unimplemented[] = =3D { { "gpu", 0x01c40000, 64 * KiB }, { "gmac", 0x01c50000, 64 * KiB }, { "hstmr", 0x01c60000, 4 * KiB }, - { "dram-com", 0x01c62000, 4 * KiB }, - { "dram-ctl", 0x01c63000, 4 * KiB }, { "tcon-top", 0x01c70000, 4 * KiB }, { "lcd0", 0x01c71000, 4 * KiB }, { "lcd1", 0x01c72000, 4 * KiB }, @@ -273,6 +275,12 @@ static void allwinner_r40_init(Object *obj) } =20 object_initialize_child(obj, "twi0", &s->i2c0, TYPE_AW_I2C_SUN6I); + + object_initialize_child(obj, "dramc", &s->dramc, TYPE_AW_R40_DRAMC); + object_property_add_alias(obj, "ram-addr", OBJECT(&s->dramc), + "ram-addr"); + object_property_add_alias(obj, "ram-size", OBJECT(&s->dramc), + "ram-size"); } =20 static void allwinner_r40_realize(DeviceState *dev, Error **errp) @@ -425,6 +433,15 @@ static void allwinner_r40_realize(DeviceState *dev, Er= ror **errp) sysbus_connect_irq(SYS_BUS_DEVICE(&s->i2c0), 0, qdev_get_gpio_in(DEVICE(&s->gic), AW_R40_GIC_SPI_TW= I0)); =20 + /* DRAMC */ + sysbus_realize(SYS_BUS_DEVICE(&s->dramc), &error_fatal); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->dramc), 0, + s->memmap[AW_R40_DEV_DRAMCOM]); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->dramc), 1, + s->memmap[AW_R40_DEV_DRAMCTL]); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->dramc), 2, + s->memmap[AW_R40_DEV_DRAMPHY]); + /* Unimplemented devices */ for (i =3D 0; i < ARRAY_SIZE(r40_unimplemented); i++) { create_unimplemented_device(r40_unimplemented[i].device_name, diff --git a/hw/arm/bananapi_m2u.c b/hw/arm/bananapi_m2u.c index 9c5360a41b..20a4550c68 100644 --- a/hw/arm/bananapi_m2u.c +++ b/hw/arm/bananapi_m2u.c @@ -85,6 +85,13 @@ static void bpim2u_init(MachineState *machine) object_property_set_int(OBJECT(r40), "clk1-freq", 24 * 1000 * 1000, &error_abort); =20 + /* DRAMC */ + r40->ram_size =3D machine->ram_size / MiB; + object_property_set_uint(OBJECT(r40), "ram-addr", + r40->memmap[AW_R40_DEV_SDRAM], &error_abort); + object_property_set_int(OBJECT(r40), "ram-size", + r40->ram_size, &error_abort); + /* Mark R40 object realized */ qdev_realize(DEVICE(r40), NULL, &error_abort); =20 diff --git a/hw/misc/allwinner-r40-dramc.c b/hw/misc/allwinner-r40-dramc.c new file mode 100644 index 0000000000..b102bcdaba --- /dev/null +++ b/hw/misc/allwinner-r40-dramc.c @@ -0,0 +1,513 @@ +/* + * Allwinner R40 SDRAM Controller emulation + * + * CCopyright (C) 2023 qianfan Zhao + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "qemu/osdep.h" +#include "qemu/units.h" +#include "qemu/error-report.h" +#include "hw/sysbus.h" +#include "migration/vmstate.h" +#include "qemu/log.h" +#include "qemu/module.h" +#include "exec/address-spaces.h" +#include "hw/qdev-properties.h" +#include "qapi/error.h" +#include "qemu/bitops.h" +#include "hw/misc/allwinner-r40-dramc.h" +#include "trace.h" + +#define REG_INDEX(offset) (offset / sizeof(uint32_t)) + +/* DRAMCOM register offsets */ +enum { + REG_DRAMCOM_CR =3D 0x0000, /* Control Register */ +}; + +/* DRAMCOMM register flags */ +enum { + REG_DRAMCOM_CR_DUAL_RANK =3D (1 << 0), +}; + +/* DRAMCTL register offsets */ +enum { + REG_DRAMCTL_PIR =3D 0x0000, /* PHY Initialization Register */ + REG_DRAMCTL_PGSR =3D 0x0010, /* PHY General Status Register */ + REG_DRAMCTL_STATR =3D 0x0018, /* Status Register */ + REG_DRAMCTL_PGCR =3D 0x0100, /* PHY general configuration registers */ +}; + +/* DRAMCTL register flags */ +enum { + REG_DRAMCTL_PGSR_INITDONE =3D (1 << 0), + REG_DRAMCTL_PGSR_READ_TIMEOUT =3D (1 << 13), + REG_DRAMCTL_PGCR_ENABLE_READ_TIMEOUT =3D (1 << 25), +}; + +enum { + REG_DRAMCTL_STATR_ACTIVE =3D (1 << 0), +}; + +#define DRAM_MAX_ROW_BITS 16 +#define DRAM_MAX_COL_BITS 13 /* 8192 */ +#define DRAM_MAX_BANK 3 + +static uint64_t dram_autodetect_cells[DRAM_MAX_ROW_BITS] + [DRAM_MAX_BANK] + [DRAM_MAX_COL_BITS]; +struct VirtualDDRChip { + uint32_t ram_size; + uint8_t bank_bits; + uint8_t row_bits; + uint8_t col_bits; +}; + +/* + * Only power of 2 RAM sizes from 256MiB up to 2048MiB are supported, + * 2GiB memory is not supported due to dual rank feature. + */ +static const struct VirtualDDRChip dummy_ddr_chips[] =3D { + { + .ram_size =3D 256, + .bank_bits =3D 3, + .row_bits =3D 12, + .col_bits =3D 13, + }, { + .ram_size =3D 512, + .bank_bits =3D 3, + .row_bits =3D 13, + .col_bits =3D 13, + }, { + .ram_size =3D 1024, + .bank_bits =3D 3, + .row_bits =3D 14, + .col_bits =3D 13, + }, { + 0 + } +}; + +static const struct VirtualDDRChip *get_match_ddr(uint32_t ram_size) +{ + const struct VirtualDDRChip *ddr; + + for (ddr =3D &dummy_ddr_chips[0]; ddr->ram_size; ddr++) { + if (ddr->ram_size =3D=3D ram_size) { + return ddr; + } + } + + return NULL; +} + +static uint64_t *address_to_autodetect_cells(AwR40DramCtlState *s, + const struct VirtualDDRChip *= ddr, + uint32_t offset) +{ + int row_index =3D 0, bank_index =3D 0, col_index =3D 0; + uint32_t row_addr, bank_addr, col_addr; + + row_addr =3D extract32(offset, s->set_col_bits + s->set_bank_bits, + s->set_row_bits); + bank_addr =3D extract32(offset, s->set_col_bits, s->set_bank_bits); + col_addr =3D extract32(offset, 0, s->set_col_bits); + + for (int i =3D 0; i < ddr->row_bits; i++) { + if (row_addr & BIT(i)) { + row_index =3D i; + } + } + + for (int i =3D 0; i < ddr->bank_bits; i++) { + if (bank_addr & BIT(i)) { + bank_index =3D i; + } + } + + for (int i =3D 0; i < ddr->col_bits; i++) { + if (col_addr & BIT(i)) { + col_index =3D i; + } + } + + trace_allwinner_r40_dramc_offset_to_cell(offset, row_index, bank_index, + col_index); + return &dram_autodetect_cells[row_index][bank_index][col_index]; +} + +static void allwinner_r40_dramc_map_rows(AwR40DramCtlState *s, uint8_t row= _bits, + uint8_t bank_bits, uint8_t col_bi= ts) +{ + const struct VirtualDDRChip *ddr =3D get_match_ddr(s->ram_size); + bool enable_detect_cells; + + trace_allwinner_r40_dramc_map_rows(row_bits, bank_bits, col_bits); + + if (!ddr) { + return; + } + + s->set_row_bits =3D row_bits; + s->set_bank_bits =3D bank_bits; + s->set_col_bits =3D col_bits; + + enable_detect_cells =3D ddr->bank_bits !=3D bank_bits + || ddr->row_bits !=3D row_bits + || ddr->col_bits !=3D col_bits; + + if (enable_detect_cells) { + trace_allwinner_r40_dramc_detect_cells_enable(); + } else { + trace_allwinner_r40_dramc_detect_cells_disable(); + } + + memory_region_set_enabled(&s->detect_cells, enable_detect_cells); +} + +static uint64_t allwinner_r40_dramcom_read(void *opaque, hwaddr offset, + unsigned size) +{ + const AwR40DramCtlState *s =3D AW_R40_DRAMC(opaque); + const uint32_t idx =3D REG_INDEX(offset); + + if (idx >=3D AW_R40_DRAMCOM_REGS_NUM) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: out-of-bounds offset 0x%04x\n", + __func__, (uint32_t)offset); + return 0; + } + + trace_allwinner_r40_dramcom_read(offset, s->dramcom[idx], size); + return s->dramcom[idx]; +} + +static void allwinner_r40_dramcom_write(void *opaque, hwaddr offset, + uint64_t val, unsigned size) +{ + AwR40DramCtlState *s =3D AW_R40_DRAMC(opaque); + const uint32_t idx =3D REG_INDEX(offset); + + trace_allwinner_r40_dramcom_write(offset, val, size); + + if (idx >=3D AW_R40_DRAMCOM_REGS_NUM) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: out-of-bounds offset 0x%04x\n", + __func__, (uint32_t)offset); + return; + } + + switch (offset) { + case REG_DRAMCOM_CR: /* Control Register */ + if (!(val & REG_DRAMCOM_CR_DUAL_RANK)) { + allwinner_r40_dramc_map_rows(s, ((val >> 4) & 0xf) + 1, + ((val >> 2) & 0x1) + 2, + (((val >> 8) & 0xf) + 3)); + } + break; + }; + + s->dramcom[idx] =3D (uint32_t) val; +} + +static uint64_t allwinner_r40_dramctl_read(void *opaque, hwaddr offset, + unsigned size) +{ + const AwR40DramCtlState *s =3D AW_R40_DRAMC(opaque); + const uint32_t idx =3D REG_INDEX(offset); + + if (idx >=3D AW_R40_DRAMCTL_REGS_NUM) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: out-of-bounds offset 0x%04x\n", + __func__, (uint32_t)offset); + return 0; + } + + trace_allwinner_r40_dramctl_read(offset, s->dramctl[idx], size); + return s->dramctl[idx]; +} + +static void allwinner_r40_dramctl_write(void *opaque, hwaddr offset, + uint64_t val, unsigned size) +{ + AwR40DramCtlState *s =3D AW_R40_DRAMC(opaque); + const uint32_t idx =3D REG_INDEX(offset); + + trace_allwinner_r40_dramctl_write(offset, val, size); + + if (idx >=3D AW_R40_DRAMCTL_REGS_NUM) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: out-of-bounds offset 0x%04x\n", + __func__, (uint32_t)offset); + return; + } + + switch (offset) { + case REG_DRAMCTL_PIR: /* PHY Initialization Register */ + s->dramctl[REG_INDEX(REG_DRAMCTL_PGSR)] |=3D REG_DRAMCTL_PGSR_INIT= DONE; + s->dramctl[REG_INDEX(REG_DRAMCTL_STATR)] |=3D REG_DRAMCTL_STATR_AC= TIVE; + break; + } + + s->dramctl[idx] =3D (uint32_t) val; +} + +static uint64_t allwinner_r40_dramphy_read(void *opaque, hwaddr offset, + unsigned size) +{ + const AwR40DramCtlState *s =3D AW_R40_DRAMC(opaque); + const uint32_t idx =3D REG_INDEX(offset); + + if (idx >=3D AW_R40_DRAMPHY_REGS_NUM) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: out-of-bounds offset 0x%04x\n", + __func__, (uint32_t)offset); + return 0; + } + + trace_allwinner_r40_dramphy_read(offset, s->dramphy[idx], size); + return s->dramphy[idx]; +} + +static void allwinner_r40_dramphy_write(void *opaque, hwaddr offset, + uint64_t val, unsigned size) +{ + AwR40DramCtlState *s =3D AW_R40_DRAMC(opaque); + const uint32_t idx =3D REG_INDEX(offset); + + trace_allwinner_r40_dramphy_write(offset, val, size); + + if (idx >=3D AW_R40_DRAMPHY_REGS_NUM) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: out-of-bounds offset 0x%04x\n", + __func__, (uint32_t)offset); + return; + } + + s->dramphy[idx] =3D (uint32_t) val; +} + +static const MemoryRegionOps allwinner_r40_dramcom_ops =3D { + .read =3D allwinner_r40_dramcom_read, + .write =3D allwinner_r40_dramcom_write, + .endianness =3D DEVICE_NATIVE_ENDIAN, + .valid =3D { + .min_access_size =3D 4, + .max_access_size =3D 4, + }, + .impl.min_access_size =3D 4, +}; + +static const MemoryRegionOps allwinner_r40_dramctl_ops =3D { + .read =3D allwinner_r40_dramctl_read, + .write =3D allwinner_r40_dramctl_write, + .endianness =3D DEVICE_NATIVE_ENDIAN, + .valid =3D { + .min_access_size =3D 4, + .max_access_size =3D 4, + }, + .impl.min_access_size =3D 4, +}; + +static const MemoryRegionOps allwinner_r40_dramphy_ops =3D { + .read =3D allwinner_r40_dramphy_read, + .write =3D allwinner_r40_dramphy_write, + .endianness =3D DEVICE_NATIVE_ENDIAN, + .valid =3D { + .min_access_size =3D 4, + .max_access_size =3D 4, + }, + .impl.min_access_size =3D 4, +}; + +static uint64_t allwinner_r40_detect_read(void *opaque, hwaddr offset, + unsigned size) +{ + AwR40DramCtlState *s =3D AW_R40_DRAMC(opaque); + const struct VirtualDDRChip *ddr =3D get_match_ddr(s->ram_size); + uint64_t data =3D 0; + + if (ddr) { + data =3D *address_to_autodetect_cells(s, ddr, (uint32_t)offset); + } + + trace_allwinner_r40_dramc_detect_cell_read(offset, data); + return data; +} + +static void allwinner_r40_detect_write(void *opaque, hwaddr offset, + uint64_t data, unsigned size) +{ + AwR40DramCtlState *s =3D AW_R40_DRAMC(opaque); + const struct VirtualDDRChip *ddr =3D get_match_ddr(s->ram_size); + + if (ddr) { + uint64_t *cell =3D address_to_autodetect_cells(s, ddr, (uint32_t)o= ffset); + trace_allwinner_r40_dramc_detect_cell_write(offset, data); + *cell =3D data; + } +} + +static const MemoryRegionOps allwinner_r40_detect_ops =3D { + .read =3D allwinner_r40_detect_read, + .write =3D allwinner_r40_detect_write, + .endianness =3D DEVICE_NATIVE_ENDIAN, + .valid =3D { + .min_access_size =3D 4, + .max_access_size =3D 4, + }, + .impl.min_access_size =3D 4, +}; + +/* + * mctl_r40_detect_rank_count in u-boot will write the high 1G of DDR + * to detect wether the board support dual_rank or not. Create a virtual m= emory + * if the board's ram_size less or equal than 1G, and set read time out fl= ag of + * REG_DRAMCTL_PGSR when the user touch this high dram. + */ +static uint64_t allwinner_r40_dualrank_detect_read(void *opaque, hwaddr of= fset, + unsigned size) +{ + AwR40DramCtlState *s =3D AW_R40_DRAMC(opaque); + uint32_t reg; + + reg =3D s->dramctl[REG_INDEX(REG_DRAMCTL_PGCR)]; + if (reg & REG_DRAMCTL_PGCR_ENABLE_READ_TIMEOUT) { /* Enable read time = out */ + /* + * this driver only support one rank, mark READ_TIMEOUT when try + * read the second rank. + */ + s->dramctl[REG_INDEX(REG_DRAMCTL_PGSR)] + |=3D REG_DRAMCTL_PGSR_READ_TIMEOUT; + } + + return 0; +} + +static const MemoryRegionOps allwinner_r40_dualrank_detect_ops =3D { + .read =3D allwinner_r40_dualrank_detect_read, + .endianness =3D DEVICE_NATIVE_ENDIAN, + .valid =3D { + .min_access_size =3D 4, + .max_access_size =3D 4, + }, + .impl.min_access_size =3D 4, +}; + +static void allwinner_r40_dramc_reset(DeviceState *dev) +{ + AwR40DramCtlState *s =3D AW_R40_DRAMC(dev); + + /* Set default values for registers */ + memset(&s->dramcom, 0, sizeof(s->dramcom)); + memset(&s->dramctl, 0, sizeof(s->dramctl)); + memset(&s->dramphy, 0, sizeof(s->dramphy)); +} + +static void allwinner_r40_dramc_realize(DeviceState *dev, Error **errp) +{ + AwR40DramCtlState *s =3D AW_R40_DRAMC(dev); + + if (!get_match_ddr(s->ram_size)) { + error_report("%s: ram-size %u MiB is not supported", + __func__, s->ram_size); + exit(1); + } + + /* detect_cells */ + sysbus_mmio_map_overlap(SYS_BUS_DEVICE(s), 3, s->ram_addr, 10); + memory_region_set_enabled(&s->detect_cells, false); + + if (s->ram_size < 2048) { + /* the high memory used for dualrank detect, index 4 */ + memory_region_init_io(&s->dram_high, OBJECT(s), + &allwinner_r40_dualrank_detect_ops, s, + "DRAMHIGH", KiB); + sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->dram_high); + + sysbus_mmio_map(SYS_BUS_DEVICE(s), 4, s->ram_addr + GiB); + } +} + +static void allwinner_r40_dramc_init(Object *obj) +{ + SysBusDevice *sbd =3D SYS_BUS_DEVICE(obj); + AwR40DramCtlState *s =3D AW_R40_DRAMC(obj); + + /* DRAMCOM registers, index 0 */ + memory_region_init_io(&s->dramcom_iomem, OBJECT(s), + &allwinner_r40_dramcom_ops, s, + "DRAMCOM", 4 * KiB); + sysbus_init_mmio(sbd, &s->dramcom_iomem); + + /* DRAMCTL registers, index 1 */ + memory_region_init_io(&s->dramctl_iomem, OBJECT(s), + &allwinner_r40_dramctl_ops, s, + "DRAMCTL", 4 * KiB); + sysbus_init_mmio(sbd, &s->dramctl_iomem); + + /* DRAMPHY registers. index 2 */ + memory_region_init_io(&s->dramphy_iomem, OBJECT(s), + &allwinner_r40_dramphy_ops, s, + "DRAMPHY", 4 * KiB); + sysbus_init_mmio(sbd, &s->dramphy_iomem); + + /* R40 support max 2GiB dram memory, index 3 */ + memory_region_init_io(&s->detect_cells, OBJECT(s), + &allwinner_r40_detect_ops, s, + "DRAMCELLS", 2 * GiB); + sysbus_init_mmio(sbd, &s->detect_cells); +} + +static Property allwinner_r40_dramc_properties[] =3D { + DEFINE_PROP_UINT64("ram-addr", AwR40DramCtlState, ram_addr, 0x0), + DEFINE_PROP_UINT32("ram-size", AwR40DramCtlState, ram_size, 256), /* M= iB */ + DEFINE_PROP_END_OF_LIST() +}; + +static const VMStateDescription allwinner_r40_dramc_vmstate =3D { + .name =3D "allwinner-r40-dramc", + .version_id =3D 1, + .minimum_version_id =3D 1, + .fields =3D (VMStateField[]) { + VMSTATE_UINT32_ARRAY(dramcom, AwR40DramCtlState, + AW_R40_DRAMCOM_REGS_NUM), + VMSTATE_UINT32_ARRAY(dramctl, AwR40DramCtlState, + AW_R40_DRAMCTL_REGS_NUM), + VMSTATE_UINT32_ARRAY(dramphy, AwR40DramCtlState, + AW_R40_DRAMPHY_REGS_NUM), + VMSTATE_END_OF_LIST() + } +}; + +static void allwinner_r40_dramc_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc =3D DEVICE_CLASS(klass); + + dc->reset =3D allwinner_r40_dramc_reset; + dc->vmsd =3D &allwinner_r40_dramc_vmstate; + dc->realize =3D allwinner_r40_dramc_realize; + device_class_set_props(dc, allwinner_r40_dramc_properties); +} + +static const TypeInfo allwinner_r40_dramc_info =3D { + .name =3D TYPE_AW_R40_DRAMC, + .parent =3D TYPE_SYS_BUS_DEVICE, + .instance_init =3D allwinner_r40_dramc_init, + .instance_size =3D sizeof(AwR40DramCtlState), + .class_init =3D allwinner_r40_dramc_class_init, +}; + +static void allwinner_r40_dramc_register(void) +{ + type_register_static(&allwinner_r40_dramc_info); +} + +type_init(allwinner_r40_dramc_register) diff --git a/hw/misc/meson.build b/hw/misc/meson.build index 1db0343333..b04d43e05a 100644 --- a/hw/misc/meson.build +++ b/hw/misc/meson.build @@ -45,6 +45,7 @@ softmmu_ss.add(when: 'CONFIG_ALLWINNER_H3', if_true: file= s('allwinner-h3-dramc.c softmmu_ss.add(when: 'CONFIG_ALLWINNER_H3', if_true: files('allwinner-h3-s= ysctrl.c')) softmmu_ss.add(when: 'CONFIG_ALLWINNER_H3', if_true: files('allwinner-sid.= c')) softmmu_ss.add(when: 'CONFIG_ALLWINNER_R40', if_true: files('allwinner-r40= -ccu.c')) +softmmu_ss.add(when: 'CONFIG_ALLWINNER_R40', if_true: files('allwinner-r40= -dramc.c')) softmmu_ss.add(when: 'CONFIG_AXP2XX_PMU', if_true: files('axp2xx.c')) softmmu_ss.add(when: 'CONFIG_REALVIEW', if_true: files('arm_sysctl.c')) softmmu_ss.add(when: 'CONFIG_NSERIES', if_true: files('cbus.c')) diff --git a/hw/misc/trace-events b/hw/misc/trace-events index 24cdec83fe..8b68f07765 100644 --- a/hw/misc/trace-events +++ b/hw/misc/trace-events @@ -15,6 +15,20 @@ allwinner_h3_dramctl_write(uint64_t offset, uint64_t dat= a, unsigned size) "Write allwinner_h3_dramphy_read(uint64_t offset, uint64_t data, unsigned size) "= Read: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %" PRIu32 allwinner_h3_dramphy_write(uint64_t offset, uint64_t data, unsigned size) = "write: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %" PRIu32 =20 +# allwinner-r40-dramc.c +allwinner_r40_dramc_detect_cells_disable(void) "Disable detect cells" +allwinner_r40_dramc_detect_cells_enable(void) "Enable detect cells" +allwinner_r40_dramc_map_rows(uint8_t row_bits, uint8_t bank_bits, uint8_t = col_bits) "DRAM layout: row_bits %d, bank_bits %d, col_bits %d" +allwinner_r40_dramc_offset_to_cell(uint64_t offset, int row, int bank, int= col) "offset 0x%" PRIx64 " row %d bank %d col %d" +allwinner_r40_dramc_detect_cell_write(uint64_t offset, uint64_t data) "off= set 0x%" PRIx64 " data 0x%" PRIx64 "" +allwinner_r40_dramc_detect_cell_read(uint64_t offset, uint64_t data) "offs= et 0x%" PRIx64 " data 0x%" PRIx64 "" +allwinner_r40_dramcom_read(uint64_t offset, uint64_t data, unsigned size) = "Read: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %" PRIu32 +allwinner_r40_dramcom_write(uint64_t offset, uint64_t data, unsigned size)= "Write: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %" PRIu32 +allwinner_r40_dramctl_read(uint64_t offset, uint64_t data, unsigned size) = "Read: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %" PRIu32 +allwinner_r40_dramctl_write(uint64_t offset, uint64_t data, unsigned size)= "Write: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %" PRIu32 +allwinner_r40_dramphy_read(uint64_t offset, uint64_t data, unsigned size) = "Read: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %" PRIu32 +allwinner_r40_dramphy_write(uint64_t offset, uint64_t data, unsigned size)= "write: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %" PRIu32 + # allwinner-sid.c allwinner_sid_read(uint64_t offset, uint64_t data, unsigned size) "offset = 0x%" PRIx64 " data 0x%" PRIx64 " size %" PRIu32 allwinner_sid_write(uint64_t offset, uint64_t data, unsigned size) "offset= 0x%" PRIx64 " data 0x%" PRIx64 " size %" PRIu32 diff --git a/include/hw/arm/allwinner-r40.h b/include/hw/arm/allwinner-r40.h index 95366f4eee..8243e8903b 100644 --- a/include/hw/arm/allwinner-r40.h +++ b/include/hw/arm/allwinner-r40.h @@ -26,6 +26,7 @@ #include "hw/intc/arm_gic.h" #include "hw/sd/allwinner-sdhost.h" #include "hw/misc/allwinner-r40-ccu.h" +#include "hw/misc/allwinner-r40-dramc.h" #include "hw/i2c/allwinner-i2c.h" #include "target/arm/cpu.h" #include "sysemu/block-backend.h" @@ -54,7 +55,10 @@ enum { AW_R40_DEV_GIC_CPU, AW_R40_DEV_GIC_HYP, AW_R40_DEV_GIC_VCPU, - AW_R40_DEV_SDRAM + AW_R40_DEV_SDRAM, + AW_R40_DEV_DRAMCOM, + AW_R40_DEV_DRAMCTL, + AW_R40_DEV_DRAMPHY, }; =20 #define AW_R40_NUM_CPUS (4) @@ -86,11 +90,18 @@ struct AwR40State { DeviceState parent_obj; /*< public >*/ =20 + /** Physical base address for start of RAM */ + hwaddr ram_addr; + + /** Total RAM size in megabytes */ + uint32_t ram_size; + ARMCPU cpus[AW_R40_NUM_CPUS]; const hwaddr *memmap; AwA10PITState timer; AwSdHostState mmc[AW_R40_NUM_MMCS]; AwR40ClockCtlState ccu; + AwR40DramCtlState dramc; AWI2CState i2c0; GICState gic; MemoryRegion sram_a1; diff --git a/include/hw/misc/allwinner-r40-dramc.h b/include/hw/misc/allwin= ner-r40-dramc.h new file mode 100644 index 0000000000..6a1a3a7893 --- /dev/null +++ b/include/hw/misc/allwinner-r40-dramc.h @@ -0,0 +1,108 @@ +/* + * Allwinner R40 SDRAM Controller emulation + * + * Copyright (C) 2023 qianfan Zhao + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef HW_MISC_ALLWINNER_R40_DRAMC_H +#define HW_MISC_ALLWINNER_R40_DRAMC_H + +#include "qom/object.h" +#include "hw/sysbus.h" +#include "exec/hwaddr.h" + +/** + * Constants + * @{ + */ + +/** Highest register address used by DRAMCOM module */ +#define AW_R40_DRAMCOM_REGS_MAXADDR (0x804) + +/** Total number of known DRAMCOM registers */ +#define AW_R40_DRAMCOM_REGS_NUM (AW_R40_DRAMCOM_REGS_MAXADDR / \ + sizeof(uint32_t)) + +/** Highest register address used by DRAMCTL module */ +#define AW_R40_DRAMCTL_REGS_MAXADDR (0x88c) + +/** Total number of known DRAMCTL registers */ +#define AW_R40_DRAMCTL_REGS_NUM (AW_R40_DRAMCTL_REGS_MAXADDR / \ + sizeof(uint32_t)) + +/** Highest register address used by DRAMPHY module */ +#define AW_R40_DRAMPHY_REGS_MAXADDR (0x4) + +/** Total number of known DRAMPHY registers */ +#define AW_R40_DRAMPHY_REGS_NUM (AW_R40_DRAMPHY_REGS_MAXADDR / \ + sizeof(uint32_t)) + +/** @} */ + +/** + * Object model + * @{ + */ + +#define TYPE_AW_R40_DRAMC "allwinner-r40-dramc" +OBJECT_DECLARE_SIMPLE_TYPE(AwR40DramCtlState, AW_R40_DRAMC) + +/** @} */ + +/** + * Allwinner R40 SDRAM Controller object instance state. + */ +struct AwR40DramCtlState { + /*< private >*/ + SysBusDevice parent_obj; + /*< public >*/ + + /** Physical base address for start of RAM */ + hwaddr ram_addr; + + /** Total RAM size in megabytes */ + uint32_t ram_size; + + uint8_t set_row_bits; + uint8_t set_bank_bits; + uint8_t set_col_bits; + + /** + * @name Memory Regions + * @{ + */ + MemoryRegion dramcom_iomem; /**< DRAMCOM module I/O registers */ + MemoryRegion dramctl_iomem; /**< DRAMCTL module I/O registers */ + MemoryRegion dramphy_iomem; /**< DRAMPHY module I/O registers */ + MemoryRegion dram_high; /**< The high 1G dram for dualrank dete= ct */ + MemoryRegion detect_cells; /**< DRAM memory cells for auto detect = */ + + /** @} */ + + /** + * @name Hardware Registers + * @{ + */ + + uint32_t dramcom[AW_R40_DRAMCOM_REGS_NUM]; /**< DRAMCOM registers */ + uint32_t dramctl[AW_R40_DRAMCTL_REGS_NUM]; /**< DRAMCTL registers */ + uint32_t dramphy[AW_R40_DRAMPHY_REGS_NUM] ;/**< DRAMPHY registers */ + + /** @} */ + +}; + +#endif /* HW_MISC_ALLWINNER_R40_DRAMC_H */ --=20 2.25.1