| 1 | /* |
| 2 | * Hexagon subsystem helpers shared between the machine models. |
| 3 | * |
| 4 | * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. |
| 5 | * SPDX-License-Identifier: GPL-2.0-or-later |
| 6 | */ |
| 7 | |
| 8 | #include "qemu/osdep.h" |
| 9 | #include "qapi/error.h" |
| 10 | #include "hw/hexagon/hex-subsys.h" |
| 11 | #include "hw/hexagon/hexagon_globalreg.h" |
| 12 | #include "hw/hexagon/hexagon_tlb.h" |
| 13 | #include "hw/intc/hex-l2vic.h" |
| 14 | #include "hw/timer/qct-qtimer.h" |
| 15 | #include "hw/cpu/cluster.h" |
| 16 | #include "hw/core/loader.h" |
| 17 | #include "hw/core/qdev-properties.h" |
| 18 | #include "hw/core/qdev.h" |
| 19 | #include "hw/core/sysbus.h" |
| 20 | #include "system/address-spaces.h" |
| 21 | |
| 22 | #define HEX_L2VIC_CPU_IRQS 8 |
| 23 | |
| 24 | /* Number of QTimer frames instantiated for every Hexagon machine. */ |
| 25 | #define HEX_QTIMER_NR_FRAMES 3 |
| 26 | |
| 27 | #define HEX_QTIMER_L2VIC_IRQ_BASE 2 |
| 28 | |
| 29 | static DeviceState *l2vic_create(HexagonCommonMachineState *hms, |
| 30 | const struct hexagon_machine_config *m_cfg) |
| 31 | { |
| 32 | DeviceState *l2vic = qdev_new(TYPE_HEX_L2VIC); |
| 33 | |
| 34 | object_property_add_child(OBJECT(hms), "l2vic", OBJECT(l2vic)); |
| 35 | sysbus_realize_and_unref(SYS_BUS_DEVICE(l2vic), &error_fatal); |
| 36 | sysbus_mmio_map(SYS_BUS_DEVICE(l2vic), 0, m_cfg->l2vic_base); |
| 37 | sysbus_mmio_map(SYS_BUS_DEVICE(l2vic), 1, |
| 38 | m_cfg->cfgtable.fastl2vic_base << 16); |
| 39 | |
| 40 | return l2vic; |
| 41 | } |
| 42 | |
| 43 | static void l2vic_connect_cpu(DeviceState *l2vic, DeviceState *cpu) |
| 44 | { |
| 45 | int i; |
| 46 | |
| 47 | for (i = 0; i < HEX_L2VIC_CPU_IRQS; i++) { |
| 48 | sysbus_connect_irq(SYS_BUS_DEVICE(l2vic), i, qdev_get_gpio_in(cpu, i)); |
| 49 | } |
| 50 | } |
| 51 | |
| 52 | static DeviceState *qtimer_create(HexagonCommonMachineState *hms, |
| 53 | const struct hexagon_machine_config *m_cfg) |
| 54 | { |
| 55 | DeviceState *qtimer = qdev_new(TYPE_QCT_QTIMER); |
| 56 | |
| 57 | object_property_add_child(OBJECT(hms), "qtimer", OBJECT(qtimer)); |
| 58 | qdev_prop_set_uint32(qtimer, "nr_frames", HEX_QTIMER_NR_FRAMES); |
| 59 | sysbus_realize_and_unref(SYS_BUS_DEVICE(qtimer), &error_fatal); |
| 60 | sysbus_mmio_map(SYS_BUS_DEVICE(qtimer), 0, m_cfg->csr_base); |
| 61 | sysbus_mmio_map(SYS_BUS_DEVICE(qtimer), 1, m_cfg->qtmr_region); |
| 62 | for (unsigned int i = 0; i < HEX_QTIMER_NR_FRAMES; i++) { |
| 63 | sysbus_connect_irq(SYS_BUS_DEVICE(qtimer), i, |
| 64 | qdev_get_gpio_in(hms->l2vic, |
| 65 | HEX_QTIMER_L2VIC_IRQ_BASE + i)); |
| 66 | } |
| 67 | |
| 68 | return qtimer; |
| 69 | } |
| 70 | |
| 71 | static DeviceState *globalreg_create(HexagonCommonMachineState *hms, |
| 72 | const struct hexagon_machine_config *m_cfg, |
| 73 | Rev_t rev) |
| 74 | { |
| 75 | DeviceState *glob_regs = qdev_new(TYPE_HEXAGON_GLOBALREG); |
| 76 | |
| 77 | object_property_add_child(OBJECT(hms), "global-regs", OBJECT(glob_regs)); |
| 78 | qdev_prop_set_uint64(glob_regs, "config-table-addr", m_cfg->cfgbase); |
| 79 | qdev_prop_set_uint32(glob_regs, "dsp-rev", rev); |
| 80 | object_property_set_link(OBJECT(glob_regs), "l2vic", OBJECT(hms->l2vic), |
| 81 | &error_fatal); |
| 82 | object_property_set_link(OBJECT(glob_regs), "qtimer", OBJECT(hms->qtimer), |
| 83 | &error_fatal); |
| 84 | sysbus_realize_and_unref(SYS_BUS_DEVICE(glob_regs), &error_fatal); |
| 85 | |
| 86 | return glob_regs; |
| 87 | } |
| 88 | |
| 89 | static DeviceState *tlb_create(HexagonCommonMachineState *hms, |
| 90 | const struct hexagon_machine_config *m_cfg) |
| 91 | { |
| 92 | DeviceState *tlb = qdev_new(TYPE_HEXAGON_TLB); |
| 93 | |
| 94 | object_property_add_child(OBJECT(hms), "tlb", OBJECT(tlb)); |
| 95 | qdev_prop_set_uint32(tlb, "num-entries", m_cfg->cfgtable.jtlb_size_entries); |
| 96 | sysbus_realize_and_unref(SYS_BUS_DEVICE(tlb), &error_fatal); |
| 97 | |
| 98 | return tlb; |
| 99 | } |
| 100 | |
| 101 | static DeviceState *cluster_create(HexagonCommonMachineState *hms) |
| 102 | { |
| 103 | DeviceState *cluster = qdev_new(TYPE_CPU_CLUSTER); |
| 104 | |
| 105 | object_property_add_child(OBJECT(hms), "cluster", OBJECT(cluster)); |
| 106 | qdev_prop_set_uint32(cluster, "cluster-id", 0); |
| 107 | |
| 108 | return cluster; |
| 109 | } |
| 110 | |
| 111 | void hex_subsys_create(HexagonCommonMachineState *hms, |
| 112 | const struct hexagon_machine_config *m_cfg, Rev_t rev) |
| 113 | { |
| 114 | MachineState *machine = MACHINE(hms); |
| 115 | MemoryRegion *sysmem = get_system_memory(); |
| 116 | |
| 117 | /* Main DDR at the reset vector. */ |
| 118 | memory_region_init_ram(&hms->ram, NULL, "ddr.ram", machine->ram_size, |
| 119 | &error_fatal); |
| 120 | memory_region_add_subregion(sysmem, 0x0, &hms->ram); |
| 121 | |
| 122 | /* Config-table ROM and the blob that backs it. */ |
| 123 | memory_region_init_rom(&hms->cfgtable_rom, NULL, "config_table.rom", |
| 124 | sizeof(m_cfg->cfgtable), &error_fatal); |
| 125 | memory_region_add_subregion(sysmem, m_cfg->cfgbase, &hms->cfgtable_rom); |
| 126 | rom_add_blob_fixed_as("config_table.rom", &m_cfg->cfgtable, |
| 127 | sizeof(m_cfg->cfgtable), m_cfg->cfgbase, |
| 128 | &address_space_memory); |
| 129 | |
| 130 | if (m_cfg->cfgtable.vtcm_size_kb > 0) { |
| 131 | memory_region_init_ram(&hms->vtcm, NULL, "vtcm.ram", |
| 132 | m_cfg->cfgtable.vtcm_size_kb * 1024, |
| 133 | &error_fatal); |
| 134 | memory_region_add_subregion(sysmem, m_cfg->cfgtable.vtcm_base << 16, |
| 135 | &hms->vtcm); |
| 136 | } |
| 137 | |
| 138 | hms->cluster = cluster_create(hms); |
| 139 | hms->l2vic = l2vic_create(hms, m_cfg); |
| 140 | hms->qtimer = qtimer_create(hms, m_cfg); |
| 141 | hms->glob_regs = globalreg_create(hms, m_cfg, rev); |
| 142 | hms->tlb = tlb_create(hms, m_cfg); |
| 143 | } |
| 144 | |
| 145 | void hex_subsys_add_cpu(HexagonCommonMachineState *hms, DeviceState *cpu) |
| 146 | { |
| 147 | object_property_add_child(OBJECT(hms->cluster), "cpu[*]", OBJECT(cpu)); |
| 148 | object_property_set_link(OBJECT(cpu), "global-regs", |
| 149 | OBJECT(hms->glob_regs), &error_fatal); |
| 150 | object_property_set_link(OBJECT(cpu), "tlb", OBJECT(hms->tlb), |
| 151 | &error_fatal); |
| 152 | object_property_set_link(OBJECT(cpu), "l2vic", OBJECT(hms->l2vic), |
| 153 | &error_fatal); |
| 154 | } |
| 155 | |
| 156 | void hex_subsys_realize_cluster(HexagonCommonMachineState *hms) |
| 157 | { |
| 158 | /* |
| 159 | * The cluster must be realized after its CPUs have been parented into it |
| 160 | * (see hex_subsys_add_cpu()) but before any CPU is itself realized, since |
| 161 | * qdev_realize_and_unref() on a CPU latches cluster_index into the TCG |
| 162 | * cflags at that point. |
| 163 | */ |
| 164 | qdev_realize_and_unref(hms->cluster, NULL, &error_fatal); |
| 165 | } |
| 166 | |
| 167 | void hex_subsys_realize_cpu(HexagonCommonMachineState *hms, DeviceState *cpu, |
| 168 | bool boot_cpu) |
| 169 | { |
| 170 | qdev_realize_and_unref(cpu, NULL, &error_fatal); |
| 171 | |
| 172 | if (boot_cpu) { |
| 173 | l2vic_connect_cpu(hms->l2vic, cpu); |
| 174 | } |
| 175 | } |