Hi Strahinja, On Sun, Dec 4, 2022 at 12:19 AM Strahinja Jankovic < strahinjapjanko...@gmail.com> wrote:
> During SPL boot several DRAM Controller registers are used. Most > important registers are those related to DRAM initialization and > calibration, where SPL initiates process and waits until certain bit is > set/cleared. > > This patch adds these registers, initializes reset values from user's > guide and updates state of registers as SPL expects it. > > Signed-off-by: Strahinja Jankovic <strahinja.p.janko...@gmail.com> > --- > hw/arm/Kconfig | 1 + > hw/arm/allwinner-a10.c | 7 + > hw/misc/Kconfig | 3 + > hw/misc/allwinner-a10-dramc.c | 179 ++++++++++++++++++++++++++ > hw/misc/meson.build | 1 + > include/hw/arm/allwinner-a10.h | 2 + > include/hw/misc/allwinner-a10-dramc.h | 68 ++++++++++ > 7 files changed, 261 insertions(+) > create mode 100644 hw/misc/allwinner-a10-dramc.c > create mode 100644 include/hw/misc/allwinner-a10-dramc.h > > diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig > index 14f52b41af..140f142ae5 100644 > --- a/hw/arm/Kconfig > +++ b/hw/arm/Kconfig > @@ -320,6 +320,7 @@ config ALLWINNER_A10 > select ALLWINNER_A10_PIT > select ALLWINNER_A10_PIC > select ALLWINNER_A10_CCM > + select ALLWINNER_A10_DRAMC > select ALLWINNER_EMAC > select SERIAL > select UNIMP > diff --git a/hw/arm/allwinner-a10.c b/hw/arm/allwinner-a10.c > index 86baeeeca2..a5f7a36ac9 100644 > --- a/hw/arm/allwinner-a10.c > +++ b/hw/arm/allwinner-a10.c > @@ -25,6 +25,7 @@ > #include "hw/boards.h" > #include "hw/usb/hcd-ohci.h" > > +#define AW_A10_DRAMC_BASE 0x01c01000 > #define AW_A10_MMC0_BASE 0x01c0f000 > #define AW_A10_CCM_BASE 0x01c20000 > #define AW_A10_PIC_REG_BASE 0x01c20400 > @@ -49,6 +50,8 @@ static void aw_a10_init(Object *obj) > > object_initialize_child(obj, "ccm", &s->ccm, TYPE_AW_A10_CCM); > > + object_initialize_child(obj, "dramc", &s->dramc, TYPE_AW_A10_DRAMC); > + > object_initialize_child(obj, "emac", &s->emac, TYPE_AW_EMAC); > > object_initialize_child(obj, "sata", &s->sata, TYPE_ALLWINNER_AHCI); > @@ -110,6 +113,10 @@ static void aw_a10_realize(DeviceState *dev, Error > **errp) > sysbus_realize(SYS_BUS_DEVICE(&s->ccm), &error_fatal); > sysbus_mmio_map(SYS_BUS_DEVICE(&s->ccm), 0, AW_A10_CCM_BASE); > > + /* DRAM Control Module */ > + sysbus_realize(SYS_BUS_DEVICE(&s->dramc), &error_fatal); > + sysbus_mmio_map(SYS_BUS_DEVICE(&s->dramc), 0, AW_A10_DRAMC_BASE); > + > /* FIXME use qdev NIC properties instead of nd_table[] */ > if (nd_table[0].used) { > qemu_check_nic_model(&nd_table[0], TYPE_AW_EMAC); > diff --git a/hw/misc/Kconfig b/hw/misc/Kconfig > index ed07bf4133..052fb54310 100644 > --- a/hw/misc/Kconfig > +++ b/hw/misc/Kconfig > @@ -177,4 +177,7 @@ config LASI > config ALLWINNER_A10_CCM > bool > > +config ALLWINNER_A10_DRAMC > + bool > + > source macio/Kconfig > diff --git a/hw/misc/allwinner-a10-dramc.c b/hw/misc/allwinner-a10-dramc.c > new file mode 100644 > index 0000000000..e118b0c2fd > --- /dev/null > +++ b/hw/misc/allwinner-a10-dramc.c > @@ -0,0 +1,179 @@ > +/* > + * Allwinner A10 DRAM Controller emulation > + * > + * Copyright (C) 2022 Strahinja Jankovic <strahinja.p.janko...@gmail.com> > + * > + * This file is derived from Allwinner H3 DRAMC, > + * by 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 <http://www.gnu.org/licenses/>. > + */ > + > +#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-a10-dramc.h" > + > +/* DRAMC register offsets */ > +enum { > + REG_SDR_CCR = 0x0000, > + REG_SDR_ZQCR0 = 0x00a8, > + REG_SDR_ZQSR = 0x00b0 > +}; > This matches indeed with what is currently implemented in u-boot in the file arch/arm/include/asm/arch-sunxi/dram_sun4i.h. And since the datasheets don't document the DRAM controllers, probably the best we can do is match with u-boot. Reviewed-by: Niek Linnenbank <nieklinnenb...@gmail.com> Regards, Niek > + > +#define REG_INDEX(offset) (offset / sizeof(uint32_t)) > + > +/* DRAMC register flags */ > +enum { > + REG_SDR_CCR_DATA_TRAINING = (1 << 30), > + REG_SDR_CCR_DRAM_INIT = (1 << 31), > +}; > +enum { > + REG_SDR_ZQSR_ZCAL = (1 << 31), > +}; > + > +/* DRAMC register reset values */ > +enum { > + REG_SDR_CCR_RESET = 0x80020000, > + REG_SDR_ZQCR0_RESET = 0x07b00000, > + REG_SDR_ZQSR_RESET = 0x80000000 > +}; > + > +static uint64_t allwinner_a10_dramc_read(void *opaque, hwaddr offset, > + unsigned size) > +{ > + const AwA10DramControllerState *s = AW_A10_DRAMC(opaque); > + const uint32_t idx = REG_INDEX(offset); > + > + switch (offset) { > + case REG_SDR_CCR: > + case REG_SDR_ZQCR0: > + case REG_SDR_ZQSR: > + break; > + case 0x2e4 ... AW_A10_DRAMC_IOSIZE: > + qemu_log_mask(LOG_GUEST_ERROR, "%s: out-of-bounds offset > 0x%04x\n", > + __func__, (uint32_t)offset); > + return 0; > + default: > + qemu_log_mask(LOG_UNIMP, "%s: unimplemented read offset 0x%04x\n", > + __func__, (uint32_t)offset); > + return 0; > + } > + > + return s->regs[idx]; > +} > + > +static void allwinner_a10_dramc_write(void *opaque, hwaddr offset, > + uint64_t val, unsigned size) > +{ > + AwA10DramControllerState *s = AW_A10_DRAMC(opaque); > + const uint32_t idx = REG_INDEX(offset); > + > + switch (offset) { > + case REG_SDR_CCR: > + if (val & REG_SDR_CCR_DRAM_INIT) { > + /* Clear DRAM_INIT to indicate process is done. */ > + val &= ~REG_SDR_CCR_DRAM_INIT; > + } > + if (val & REG_SDR_CCR_DATA_TRAINING) { > + /* Clear DATA_TRAINING to indicate process is done. */ > + val &= ~REG_SDR_CCR_DATA_TRAINING; > + } > + break; > + case REG_SDR_ZQCR0: > + /* Set ZCAL in ZQSR to indicate calibration is done. */ > + s->regs[REG_INDEX(REG_SDR_ZQSR)] |= REG_SDR_ZQSR_ZCAL; > + break; > + case 0x2e4 ... AW_A10_DRAMC_IOSIZE: > + qemu_log_mask(LOG_GUEST_ERROR, "%s: out-of-bounds offset > 0x%04x\n", > + __func__, (uint32_t)offset); > + break; > + default: > + qemu_log_mask(LOG_UNIMP, "%s: unimplemented write offset > 0x%04x\n", > + __func__, (uint32_t)offset); > + break; > + } > + > + s->regs[idx] = (uint32_t) val; > +} > + > +static const MemoryRegionOps allwinner_a10_dramc_ops = { > + .read = allwinner_a10_dramc_read, > + .write = allwinner_a10_dramc_write, > + .endianness = DEVICE_NATIVE_ENDIAN, > + .valid = { > + .min_access_size = 4, > + .max_access_size = 4, > + }, > + .impl.min_access_size = 4, > +}; > + > +static void allwinner_a10_dramc_reset_enter(Object *obj, ResetType type) > +{ > + AwA10DramControllerState *s = AW_A10_DRAMC(obj); > + > + /* Set default values for registers */ > + s->regs[REG_INDEX(REG_SDR_CCR)] = REG_SDR_CCR_RESET; > + s->regs[REG_INDEX(REG_SDR_ZQCR0)] = REG_SDR_ZQCR0_RESET; > + s->regs[REG_INDEX(REG_SDR_ZQSR)] = REG_SDR_ZQSR_RESET; > +} > + > +static void allwinner_a10_dramc_init(Object *obj) > +{ > + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); > + AwA10DramControllerState *s = AW_A10_DRAMC(obj); > + > + /* Memory mapping */ > + memory_region_init_io(&s->iomem, OBJECT(s), &allwinner_a10_dramc_ops, > s, > + TYPE_AW_A10_DRAMC, AW_A10_DRAMC_IOSIZE); > + sysbus_init_mmio(sbd, &s->iomem); > +} > + > +static const VMStateDescription allwinner_a10_dramc_vmstate = { > + .name = "allwinner-a10-dramc", > + .version_id = 1, > + .minimum_version_id = 1, > + .fields = (VMStateField[]) { > + VMSTATE_UINT32_ARRAY(regs, AwA10DramControllerState, > + AW_A10_DRAMC_REGS_NUM), > + VMSTATE_END_OF_LIST() > + } > +}; > + > +static void allwinner_a10_dramc_class_init(ObjectClass *klass, void *data) > +{ > + DeviceClass *dc = DEVICE_CLASS(klass); > + ResettableClass *rc = RESETTABLE_CLASS(klass); > + > + rc->phases.enter = allwinner_a10_dramc_reset_enter; > + dc->vmsd = &allwinner_a10_dramc_vmstate; > +} > + > +static const TypeInfo allwinner_a10_dramc_info = { > + .name = TYPE_AW_A10_DRAMC, > + .parent = TYPE_SYS_BUS_DEVICE, > + .instance_init = allwinner_a10_dramc_init, > + .instance_size = sizeof(AwA10DramControllerState), > + .class_init = allwinner_a10_dramc_class_init, > +}; > + > +static void allwinner_a10_dramc_register(void) > +{ > + type_register_static(&allwinner_a10_dramc_info); > +} > + > +type_init(allwinner_a10_dramc_register) > diff --git a/hw/misc/meson.build b/hw/misc/meson.build > index ebf216edbc..d7f49f0f81 100644 > --- a/hw/misc/meson.build > +++ b/hw/misc/meson.build > @@ -39,6 +39,7 @@ subdir('macio') > softmmu_ss.add(when: 'CONFIG_IVSHMEM_DEVICE', if_true: files('ivshmem.c')) > > softmmu_ss.add(when: 'CONFIG_ALLWINNER_A10_CCM', if_true: > files('allwinner-a10-ccm.c')) > +softmmu_ss.add(when: 'CONFIG_ALLWINNER_A10_DRAMC', if_true: > files('allwinner-a10-dramc.c')) > softmmu_ss.add(when: 'CONFIG_ALLWINNER_H3', if_true: > files('allwinner-h3-ccu.c')) > specific_ss.add(when: 'CONFIG_ALLWINNER_H3', if_true: > files('allwinner-cpucfg.c')) > softmmu_ss.add(when: 'CONFIG_ALLWINNER_H3', if_true: > files('allwinner-h3-dramc.c')) > diff --git a/include/hw/arm/allwinner-a10.h > b/include/hw/arm/allwinner-a10.h > index 45d0fc2f7e..abe4ff7066 100644 > --- a/include/hw/arm/allwinner-a10.h > +++ b/include/hw/arm/allwinner-a10.h > @@ -13,6 +13,7 @@ > #include "hw/usb/hcd-ehci.h" > #include "hw/rtc/allwinner-rtc.h" > #include "hw/misc/allwinner-a10-ccm.h" > +#include "hw/misc/allwinner-a10-dramc.h" > > #include "target/arm/cpu.h" > #include "qom/object.h" > @@ -32,6 +33,7 @@ struct AwA10State { > > ARMCPU cpu; > AwA10ClockCtlState ccm; > + AwA10DramControllerState dramc; > AwA10PITState timer; > AwA10PICState intc; > AwEmacState emac; > diff --git a/include/hw/misc/allwinner-a10-dramc.h > b/include/hw/misc/allwinner-a10-dramc.h > new file mode 100644 > index 0000000000..b61fbecbe7 > --- /dev/null > +++ b/include/hw/misc/allwinner-a10-dramc.h > @@ -0,0 +1,68 @@ > +/* > + * Allwinner A10 DRAM Controller emulation > + * > + * Copyright (C) 2022 Strahinja Jankovic <strahinja.p.janko...@gmail.com> > + * > + * This file is derived from Allwinner H3 DRAMC, > + * by 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 <http://www.gnu.org/licenses/>. > + */ > + > +#ifndef HW_MISC_ALLWINNER_A10_DRAMC_H > +#define HW_MISC_ALLWINNER_A10_DRAMC_H > + > +#include "qom/object.h" > +#include "hw/sysbus.h" > +#include "hw/register.h" > + > +/** > + * @name Constants > + * @{ > + */ > + > +/** Size of register I/O address space used by DRAMC device */ > +#define AW_A10_DRAMC_IOSIZE (0x1000) > + > +/** Total number of known registers */ > +#define AW_A10_DRAMC_REGS_NUM (AW_A10_DRAMC_IOSIZE / > sizeof(uint32_t)) > + > +/** @} */ > + > +/** > + * @name Object model > + * @{ > + */ > + > +#define TYPE_AW_A10_DRAMC "allwinner-a10-dramc" > +OBJECT_DECLARE_SIMPLE_TYPE(AwA10DramControllerState, AW_A10_DRAMC) > + > +/** @} */ > + > +/** > + * Allwinner A10 DRAMC object instance state. > + */ > +struct AwA10DramControllerState { > + /*< private >*/ > + SysBusDevice parent_obj; > + /*< public >*/ > + > + /** Maps I/O registers in physical memory */ > + MemoryRegion iomem; > + > + /** Array of hardware registers */ > + uint32_t regs[AW_A10_DRAMC_REGS_NUM]; > +}; > + > +#endif /* HW_MISC_ALLWINNER_A10_DRAMC_H */ > -- > 2.30.2 > > -- Niek Linnenbank