Hi Strahinja, On Sun, Dec 4, 2022 at 12:19 AM Strahinja Jankovic < strahinjapjanko...@gmail.com> wrote:
> During SPL boot several Clock Controller Module (CCM) registers are > read, most important are PLL and Tuning, as well as divisor registers. > > This patch adds these registers and initializes reset values from user's > guide. > > Signed-off-by: Strahinja Jankovic <strahinja.p.janko...@gmail.com> > Looks fine to me: Reviewed-by: Niek Linnenbank <nieklinnenb...@gmail.com> Regards, Niek > --- > hw/arm/Kconfig | 1 + > hw/arm/allwinner-a10.c | 7 + > hw/misc/Kconfig | 3 + > hw/misc/allwinner-a10-ccm.c | 224 ++++++++++++++++++++++++++++ > hw/misc/meson.build | 1 + > include/hw/arm/allwinner-a10.h | 2 + > include/hw/misc/allwinner-a10-ccm.h | 67 +++++++++ > 7 files changed, 305 insertions(+) > create mode 100644 hw/misc/allwinner-a10-ccm.c > create mode 100644 include/hw/misc/allwinner-a10-ccm.h > > diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig > index 17fcde8e1c..14f52b41af 100644 > --- a/hw/arm/Kconfig > +++ b/hw/arm/Kconfig > @@ -319,6 +319,7 @@ config ALLWINNER_A10 > select AHCI > select ALLWINNER_A10_PIT > select ALLWINNER_A10_PIC > + select ALLWINNER_A10_CCM > select ALLWINNER_EMAC > select SERIAL > select UNIMP > diff --git a/hw/arm/allwinner-a10.c b/hw/arm/allwinner-a10.c > index 79082289ea..86baeeeca2 100644 > --- a/hw/arm/allwinner-a10.c > +++ b/hw/arm/allwinner-a10.c > @@ -26,6 +26,7 @@ > #include "hw/usb/hcd-ohci.h" > > #define AW_A10_MMC0_BASE 0x01c0f000 > +#define AW_A10_CCM_BASE 0x01c20000 > #define AW_A10_PIC_REG_BASE 0x01c20400 > #define AW_A10_PIT_REG_BASE 0x01c20c00 > #define AW_A10_UART0_REG_BASE 0x01c28000 > @@ -46,6 +47,8 @@ static void aw_a10_init(Object *obj) > > object_initialize_child(obj, "timer", &s->timer, TYPE_AW_A10_PIT); > > + object_initialize_child(obj, "ccm", &s->ccm, TYPE_AW_A10_CCM); > + > object_initialize_child(obj, "emac", &s->emac, TYPE_AW_EMAC); > > object_initialize_child(obj, "sata", &s->sata, TYPE_ALLWINNER_AHCI); > @@ -103,6 +106,10 @@ static void aw_a10_realize(DeviceState *dev, Error > **errp) > memory_region_add_subregion(get_system_memory(), 0x00000000, > &s->sram_a); > create_unimplemented_device("a10-sram-ctrl", 0x01c00000, 4 * KiB); > > + /* Clock Control Module */ > + sysbus_realize(SYS_BUS_DEVICE(&s->ccm), &error_fatal); > + sysbus_mmio_map(SYS_BUS_DEVICE(&s->ccm), 0, AW_A10_CCM_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 cbabe9f78c..ed07bf4133 100644 > --- a/hw/misc/Kconfig > +++ b/hw/misc/Kconfig > @@ -174,4 +174,7 @@ config VIRT_CTRL > config LASI > bool > > +config ALLWINNER_A10_CCM > + bool > + > source macio/Kconfig > diff --git a/hw/misc/allwinner-a10-ccm.c b/hw/misc/allwinner-a10-ccm.c > new file mode 100644 > index 0000000000..68146ee340 > --- /dev/null > +++ b/hw/misc/allwinner-a10-ccm.c > @@ -0,0 +1,224 @@ > +/* > + * Allwinner A10 Clock Control Module emulation > + * > + * Copyright (C) 2022 Strahinja Jankovic <strahinja.p.janko...@gmail.com> > + * > + * This file is derived from Allwinner H3 CCU, > + * 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-ccm.h" > + > +/* CCM register offsets */ > +enum { > + REG_PLL1_CFG = 0x0000, /* PLL1 Control */ > + REG_PLL1_TUN = 0x0004, /* PLL1 Tuning */ > + REG_PLL2_CFG = 0x0008, /* PLL2 Control */ > + REG_PLL2_TUN = 0x000C, /* PLL2 Tuning */ > + REG_PLL3_CFG = 0x0010, /* PLL3 Control */ > + REG_PLL4_CFG = 0x0018, /* PLL4 Control */ > + REG_PLL5_CFG = 0x0020, /* PLL5 Control */ > + REG_PLL5_TUN = 0x0024, /* PLL5 Tuning */ > + REG_PLL6_CFG = 0x0028, /* PLL6 Control */ > + REG_PLL6_TUN = 0x002C, /* PLL6 Tuning */ > + REG_PLL7_CFG = 0x0030, /* PLL7 Control */ > + REG_PLL1_TUN2 = 0x0038, /* PLL1 Tuning2 */ > + REG_PLL5_TUN2 = 0x003C, /* PLL5 Tuning2 */ > + REG_PLL8_CFG = 0x0040, /* PLL8 Control */ > + REG_OSC24M_CFG = 0x0050, /* OSC24M Control */ > + REG_CPU_AHB_APB0_CFG = 0x0054, /* CPU, AHB and APB0 Divide Ratio > */ > +}; > + > +#define REG_INDEX(offset) (offset / sizeof(uint32_t)) > + > +/* CCM register reset values */ > +enum { > + REG_PLL1_CFG_RST = 0x21005000, > + REG_PLL1_TUN_RST = 0x0A101000, > + REG_PLL2_CFG_RST = 0x08100010, > + REG_PLL2_TUN_RST = 0x00000000, > + REG_PLL3_CFG_RST = 0x0010D063, > + REG_PLL4_CFG_RST = 0x21009911, > + REG_PLL5_CFG_RST = 0x11049280, > + REG_PLL5_TUN_RST = 0x14888000, > + REG_PLL6_CFG_RST = 0x21009911, > + REG_PLL6_TUN_RST = 0x00000000, > + REG_PLL7_CFG_RST = 0x0010D063, > + REG_PLL1_TUN2_RST = 0x00000000, > + REG_PLL5_TUN2_RST = 0x00000000, > + REG_PLL8_CFG_RST = 0x21009911, > + REG_OSC24M_CFG_RST = 0x00138013, > + REG_CPU_AHB_APB0_CFG_RST = 0x00010010, > +}; > + > +static uint64_t allwinner_a10_ccm_read(void *opaque, hwaddr offset, > + unsigned size) > +{ > + const AwA10ClockCtlState *s = AW_A10_CCM(opaque); > + const uint32_t idx = REG_INDEX(offset); > + > + switch (offset) { > + case REG_PLL1_CFG: > + case REG_PLL1_TUN: > + case REG_PLL2_CFG: > + case REG_PLL2_TUN: > + case REG_PLL3_CFG: > + case REG_PLL4_CFG: > + case REG_PLL5_CFG: > + case REG_PLL5_TUN: > + case REG_PLL6_CFG: > + case REG_PLL6_TUN: > + case REG_PLL7_CFG: > + case REG_PLL1_TUN2: > + case REG_PLL5_TUN2: > + case REG_PLL8_CFG: > + case REG_OSC24M_CFG: > + case REG_CPU_AHB_APB0_CFG: > + break; > + case 0x158 ... AW_A10_CCM_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_ccm_write(void *opaque, hwaddr offset, > + uint64_t val, unsigned size) > +{ > + AwA10ClockCtlState *s = AW_A10_CCM(opaque); > + const uint32_t idx = REG_INDEX(offset); > + > + switch (offset) { > + case REG_PLL1_CFG: > + case REG_PLL1_TUN: > + case REG_PLL2_CFG: > + case REG_PLL2_TUN: > + case REG_PLL3_CFG: > + case REG_PLL4_CFG: > + case REG_PLL5_CFG: > + case REG_PLL5_TUN: > + case REG_PLL6_CFG: > + case REG_PLL6_TUN: > + case REG_PLL7_CFG: > + case REG_PLL1_TUN2: > + case REG_PLL5_TUN2: > + case REG_PLL8_CFG: > + case REG_OSC24M_CFG: > + case REG_CPU_AHB_APB0_CFG: > + break; > + case 0x158 ... AW_A10_CCM_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_ccm_ops = { > + .read = allwinner_a10_ccm_read, > + .write = allwinner_a10_ccm_write, > + .endianness = DEVICE_NATIVE_ENDIAN, > + .valid = { > + .min_access_size = 4, > + .max_access_size = 4, > + }, > + .impl.min_access_size = 4, > +}; > + > +static void allwinner_a10_ccm_reset_enter(Object *obj, ResetType type) > +{ > + AwA10ClockCtlState *s = AW_A10_CCM(obj); > + > + /* Set default values for registers */ > + s->regs[REG_INDEX(REG_PLL1_CFG)] = REG_PLL1_CFG_RST; > + s->regs[REG_INDEX(REG_PLL1_TUN)] = REG_PLL1_TUN_RST; > + s->regs[REG_INDEX(REG_PLL2_CFG)] = REG_PLL2_CFG_RST; > + s->regs[REG_INDEX(REG_PLL2_TUN)] = REG_PLL2_TUN_RST; > + s->regs[REG_INDEX(REG_PLL3_CFG)] = REG_PLL3_CFG_RST; > + s->regs[REG_INDEX(REG_PLL4_CFG)] = REG_PLL4_CFG_RST; > + s->regs[REG_INDEX(REG_PLL5_CFG)] = REG_PLL5_CFG_RST; > + s->regs[REG_INDEX(REG_PLL5_TUN)] = REG_PLL5_TUN_RST; > + s->regs[REG_INDEX(REG_PLL6_CFG)] = REG_PLL6_CFG_RST; > + s->regs[REG_INDEX(REG_PLL6_TUN)] = REG_PLL6_TUN_RST; > + s->regs[REG_INDEX(REG_PLL7_CFG)] = REG_PLL7_CFG_RST; > + s->regs[REG_INDEX(REG_PLL1_TUN2)] = REG_PLL1_TUN2_RST; > + s->regs[REG_INDEX(REG_PLL5_TUN2)] = REG_PLL5_TUN2_RST; > + s->regs[REG_INDEX(REG_PLL8_CFG)] = REG_PLL8_CFG_RST; > + s->regs[REG_INDEX(REG_OSC24M_CFG)] = REG_OSC24M_CFG_RST; > + s->regs[REG_INDEX(REG_CPU_AHB_APB0_CFG)] = REG_CPU_AHB_APB0_CFG_RST; > +} > + > +static void allwinner_a10_ccm_init(Object *obj) > +{ > + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); > + AwA10ClockCtlState *s = AW_A10_CCM(obj); > + > + /* Memory mapping */ > + memory_region_init_io(&s->iomem, OBJECT(s), &allwinner_a10_ccm_ops, s, > + TYPE_AW_A10_CCM, AW_A10_CCM_IOSIZE); > + sysbus_init_mmio(sbd, &s->iomem); > +} > + > +static const VMStateDescription allwinner_a10_ccm_vmstate = { > + .name = "allwinner-a10-ccm", > + .version_id = 1, > + .minimum_version_id = 1, > + .fields = (VMStateField[]) { > + VMSTATE_UINT32_ARRAY(regs, AwA10ClockCtlState, > AW_A10_CCM_REGS_NUM), > + VMSTATE_END_OF_LIST() > + } > +}; > + > +static void allwinner_a10_ccm_class_init(ObjectClass *klass, void *data) > +{ > + DeviceClass *dc = DEVICE_CLASS(klass); > + ResettableClass *rc = RESETTABLE_CLASS(klass); > + > + rc->phases.enter = allwinner_a10_ccm_reset_enter; > + dc->vmsd = &allwinner_a10_ccm_vmstate; > +} > + > +static const TypeInfo allwinner_a10_ccm_info = { > + .name = TYPE_AW_A10_CCM, > + .parent = TYPE_SYS_BUS_DEVICE, > + .instance_init = allwinner_a10_ccm_init, > + .instance_size = sizeof(AwA10ClockCtlState), > + .class_init = allwinner_a10_ccm_class_init, > +}; > + > +static void allwinner_a10_ccm_register(void) > +{ > + type_register_static(&allwinner_a10_ccm_info); > +} > + > +type_init(allwinner_a10_ccm_register) > diff --git a/hw/misc/meson.build b/hw/misc/meson.build > index 95268eddc0..ebf216edbc 100644 > --- a/hw/misc/meson.build > +++ b/hw/misc/meson.build > @@ -38,6 +38,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_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 a76dc7b84d..45d0fc2f7e 100644 > --- a/include/hw/arm/allwinner-a10.h > +++ b/include/hw/arm/allwinner-a10.h > @@ -12,6 +12,7 @@ > #include "hw/usb/hcd-ohci.h" > #include "hw/usb/hcd-ehci.h" > #include "hw/rtc/allwinner-rtc.h" > +#include "hw/misc/allwinner-a10-ccm.h" > > #include "target/arm/cpu.h" > #include "qom/object.h" > @@ -30,6 +31,7 @@ struct AwA10State { > /*< public >*/ > > ARMCPU cpu; > + AwA10ClockCtlState ccm; > AwA10PITState timer; > AwA10PICState intc; > AwEmacState emac; > diff --git a/include/hw/misc/allwinner-a10-ccm.h > b/include/hw/misc/allwinner-a10-ccm.h > new file mode 100644 > index 0000000000..7f22532efa > --- /dev/null > +++ b/include/hw/misc/allwinner-a10-ccm.h > @@ -0,0 +1,67 @@ > +/* > + * Allwinner A10 Clock Control Module emulation > + * > + * Copyright (C) 2022 Strahinja Jankovic <strahinja.p.janko...@gmail.com> > + * > + * This file is derived from Allwinner H3 CCU, > + * 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_CCM_H > +#define HW_MISC_ALLWINNER_A10_CCM_H > + > +#include "qom/object.h" > +#include "hw/sysbus.h" > + > +/** > + * @name Constants > + * @{ > + */ > + > +/** Size of register I/O address space used by CCM device */ > +#define AW_A10_CCM_IOSIZE (0x400) > + > +/** Total number of known registers */ > +#define AW_A10_CCM_REGS_NUM (AW_A10_CCM_IOSIZE / sizeof(uint32_t)) > + > +/** @} */ > + > +/** > + * @name Object model > + * @{ > + */ > + > +#define TYPE_AW_A10_CCM "allwinner-a10-ccm" > +OBJECT_DECLARE_SIMPLE_TYPE(AwA10ClockCtlState, AW_A10_CCM) > + > +/** @} */ > + > +/** > + * Allwinner A10 CCM object instance state. > + */ > +struct AwA10ClockCtlState { > + /*< private >*/ > + SysBusDevice parent_obj; > + /*< public >*/ > + > + /** Maps I/O registers in physical memory */ > + MemoryRegion iomem; > + > + /** Array of hardware registers */ > + uint32_t regs[AW_A10_CCM_REGS_NUM]; > +}; > + > +#endif /* HW_MISC_ALLWINNER_H3_CCU_H */ > -- > 2.30.2 > > -- Niek Linnenbank