/* * Hexagon subsystem helpers shared between the machine models. * * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. * SPDX-License-Identifier: GPL-2.0-or-later */ #include "qemu/osdep.h" #include "qapi/error.h" #include "hw/hexagon/hex-subsys.h" #include "hw/hexagon/hexagon_globalreg.h" #include "hw/hexagon/hexagon_tlb.h" #include "hw/intc/hex-l2vic.h" #include "hw/timer/qct-qtimer.h" #include "hw/cpu/cluster.h" #include "hw/core/loader.h" #include "hw/core/qdev-properties.h" #include "hw/core/qdev.h" #include "hw/core/sysbus.h" #include "system/address-spaces.h" #define HEX_L2VIC_CPU_IRQS 8 /* Number of QTimer frames instantiated for every Hexagon machine. */ #define HEX_QTIMER_NR_FRAMES 3 #define HEX_QTIMER_L2VIC_IRQ_BASE 2 static DeviceState *l2vic_create(HexagonCommonMachineState *hms, const struct hexagon_machine_config *m_cfg) { DeviceState *l2vic = qdev_new(TYPE_HEX_L2VIC); object_property_add_child(OBJECT(hms), "l2vic", OBJECT(l2vic)); sysbus_realize_and_unref(SYS_BUS_DEVICE(l2vic), &error_fatal); sysbus_mmio_map(SYS_BUS_DEVICE(l2vic), 0, m_cfg->l2vic_base); sysbus_mmio_map(SYS_BUS_DEVICE(l2vic), 1, m_cfg->cfgtable.fastl2vic_base << 16); return l2vic; } static void l2vic_connect_cpu(DeviceState *l2vic, DeviceState *cpu) { int i; for (i = 0; i < HEX_L2VIC_CPU_IRQS; i++) { sysbus_connect_irq(SYS_BUS_DEVICE(l2vic), i, qdev_get_gpio_in(cpu, i)); } } static DeviceState *qtimer_create(HexagonCommonMachineState *hms, const struct hexagon_machine_config *m_cfg) { DeviceState *qtimer = qdev_new(TYPE_QCT_QTIMER); object_property_add_child(OBJECT(hms), "qtimer", OBJECT(qtimer)); qdev_prop_set_uint32(qtimer, "nr_frames", HEX_QTIMER_NR_FRAMES); sysbus_realize_and_unref(SYS_BUS_DEVICE(qtimer), &error_fatal); sysbus_mmio_map(SYS_BUS_DEVICE(qtimer), 0, m_cfg->csr_base); sysbus_mmio_map(SYS_BUS_DEVICE(qtimer), 1, m_cfg->qtmr_region); for (unsigned int i = 0; i < HEX_QTIMER_NR_FRAMES; i++) { sysbus_connect_irq(SYS_BUS_DEVICE(qtimer), i, qdev_get_gpio_in(hms->l2vic, HEX_QTIMER_L2VIC_IRQ_BASE + i)); } return qtimer; } static DeviceState *globalreg_create(HexagonCommonMachineState *hms, const struct hexagon_machine_config *m_cfg, Rev_t rev) { DeviceState *glob_regs = qdev_new(TYPE_HEXAGON_GLOBALREG); object_property_add_child(OBJECT(hms), "global-regs", OBJECT(glob_regs)); qdev_prop_set_uint64(glob_regs, "config-table-addr", m_cfg->cfgbase); qdev_prop_set_uint32(glob_regs, "dsp-rev", rev); object_property_set_link(OBJECT(glob_regs), "l2vic", OBJECT(hms->l2vic), &error_fatal); object_property_set_link(OBJECT(glob_regs), "qtimer", OBJECT(hms->qtimer), &error_fatal); sysbus_realize_and_unref(SYS_BUS_DEVICE(glob_regs), &error_fatal); return glob_regs; } static DeviceState *tlb_create(HexagonCommonMachineState *hms, const struct hexagon_machine_config *m_cfg) { DeviceState *tlb = qdev_new(TYPE_HEXAGON_TLB); object_property_add_child(OBJECT(hms), "tlb", OBJECT(tlb)); qdev_prop_set_uint32(tlb, "num-entries", m_cfg->cfgtable.jtlb_size_entries); sysbus_realize_and_unref(SYS_BUS_DEVICE(tlb), &error_fatal); return tlb; } static DeviceState *cluster_create(HexagonCommonMachineState *hms) { DeviceState *cluster = qdev_new(TYPE_CPU_CLUSTER); object_property_add_child(OBJECT(hms), "cluster", OBJECT(cluster)); qdev_prop_set_uint32(cluster, "cluster-id", 0); return cluster; } void hex_subsys_create(HexagonCommonMachineState *hms, const struct hexagon_machine_config *m_cfg, Rev_t rev) { MachineState *machine = MACHINE(hms); MemoryRegion *sysmem = get_system_memory(); /* Main DDR at the reset vector. */ memory_region_init_ram(&hms->ram, NULL, "ddr.ram", machine->ram_size, &error_fatal); memory_region_add_subregion(sysmem, 0x0, &hms->ram); /* Config-table ROM and the blob that backs it. */ memory_region_init_rom(&hms->cfgtable_rom, NULL, "config_table.rom", sizeof(m_cfg->cfgtable), &error_fatal); memory_region_add_subregion(sysmem, m_cfg->cfgbase, &hms->cfgtable_rom); rom_add_blob_fixed_as("config_table.rom", &m_cfg->cfgtable, sizeof(m_cfg->cfgtable), m_cfg->cfgbase, &address_space_memory); if (m_cfg->cfgtable.vtcm_size_kb > 0) { memory_region_init_ram(&hms->vtcm, NULL, "vtcm.ram", m_cfg->cfgtable.vtcm_size_kb * 1024, &error_fatal); memory_region_add_subregion(sysmem, m_cfg->cfgtable.vtcm_base << 16, &hms->vtcm); } hms->cluster = cluster_create(hms); hms->l2vic = l2vic_create(hms, m_cfg); hms->qtimer = qtimer_create(hms, m_cfg); hms->glob_regs = globalreg_create(hms, m_cfg, rev); hms->tlb = tlb_create(hms, m_cfg); } void hex_subsys_add_cpu(HexagonCommonMachineState *hms, DeviceState *cpu) { object_property_add_child(OBJECT(hms->cluster), "cpu[*]", OBJECT(cpu)); object_property_set_link(OBJECT(cpu), "global-regs", OBJECT(hms->glob_regs), &error_fatal); object_property_set_link(OBJECT(cpu), "tlb", OBJECT(hms->tlb), &error_fatal); object_property_set_link(OBJECT(cpu), "l2vic", OBJECT(hms->l2vic), &error_fatal); } void hex_subsys_realize_cluster(HexagonCommonMachineState *hms) { /* * The cluster must be realized after its CPUs have been parented into it * (see hex_subsys_add_cpu()) but before any CPU is itself realized, since * qdev_realize_and_unref() on a CPU latches cluster_index into the TCG * cflags at that point. */ qdev_realize_and_unref(hms->cluster, NULL, &error_fatal); } void hex_subsys_realize_cpu(HexagonCommonMachineState *hms, DeviceState *cpu, bool boot_cpu) { qdev_realize_and_unref(cpu, NULL, &error_fatal); if (boot_cpu) { l2vic_connect_cpu(hms->l2vic, cpu); } }