Upstream: https://gitlab.com/qemu-project/qemu.git Upstream-Commit: d2e570cc0f97b936902a5b1b86b73c0f5998b475
176 lines
6.3 KiB
C
176 lines
6.3 KiB
C
/*
|
|
* 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);
|
|
}
|
|
}
|