Instantiate the L2VIC device in both machine models, map its slow and
MMIO regions, and link it to each CPU via the "l2vic" property.
Signed-off-by: Brian Cain <brian.cain@oss.qualcomm.com>
---
include/hw/hexagon/hexagon.h | 1 +
include/hw/hexagon/hexagon_globalreg.h | 4 +++
hw/hexagon/hexagon_dsp.c | 20 +++++++++++++
hw/hexagon/hexagon_globalreg.c | 41 ++++++++++++++++++++++----
hw/hexagon/virt.c | 22 ++++++++++++++
5 files changed, 83 insertions(+), 5 deletions(-)
diff --git a/include/hw/hexagon/hexagon.h b/include/hw/hexagon/hexagon.h
index 1034b09c2ac..6753ae32cde 100644
--- a/include/hw/hexagon/hexagon.h
+++ b/include/hw/hexagon/hexagon.h
@@ -156,6 +156,7 @@ struct HexagonCommonMachineState {
MemoryRegion ram;
MemoryRegion cfgtable_rom;
+ DeviceState *l2vic_dev;
};
#endif
diff --git a/include/hw/hexagon/hexagon_globalreg.h b/include/hw/hexagon/hexagon_globalreg.h
index 950099808fd..397dc6854c9 100644
--- a/include/hw/hexagon/hexagon_globalreg.h
+++ b/include/hw/hexagon/hexagon_globalreg.h
@@ -10,6 +10,7 @@
#include "hw/core/qdev.h"
#include "hw/core/sysbus.h"
+#include "hw/intc/hex-l2vic.h"
#include "qom/object.h"
#include "target/hexagon/cpu.h"
@@ -22,6 +23,9 @@ struct HexagonGlobalRegState {
/* Array of system registers */
uint32_t regs[NUM_SREGS];
+ /* L2VIC interface used to back the VID/VID1 registers */
+ HexL2VicInterface *l2vic;
+
/* Global performance cycle counter base */
uint64_t g_pcycle_base;
diff --git a/hw/hexagon/hexagon_dsp.c b/hw/hexagon/hexagon_dsp.c
index aa493993229..4cf88627c8f 100644
--- a/hw/hexagon/hexagon_dsp.c
+++ b/hw/hexagon/hexagon_dsp.c
@@ -16,6 +16,7 @@
#include "hw/hexagon/hexagon.h"
#include "hw/hexagon/hexagon_globalreg.h"
#include "hw/hexagon/hexagon_tlb.h"
+#include "hw/intc/hex-l2vic.h"
#include "hw/core/loader.h"
#include "qapi/error.h"
#include "qemu/error-report.h"
@@ -111,6 +112,7 @@ static void hexagon_common_init(MachineState *machine, Rev_t rev,
MemoryRegion *address_space;
DeviceState *glob_regs_dev;
DeviceState *tlb_dev;
+ DeviceState *cpu0 = NULL;
memset(&hexagon_binfo, 0, sizeof(hexagon_binfo));
if (machine->kernel_filename) {
@@ -131,11 +133,21 @@ static void hexagon_common_init(MachineState *machine, Rev_t rev,
machine->ram_size, &error_fatal);
memory_region_add_subregion(address_space, 0x0, &hms->ram);
+ hms->l2vic_dev = qdev_new(TYPE_HEX_L2VIC);
+ object_property_add_child(OBJECT(machine), "l2vic",
+ OBJECT(hms->l2vic_dev));
+ sysbus_realize_and_unref(SYS_BUS_DEVICE(hms->l2vic_dev), &error_fatal);
+ sysbus_mmio_map(SYS_BUS_DEVICE(hms->l2vic_dev), 0, m_cfg->l2vic_base);
+ sysbus_mmio_map(SYS_BUS_DEVICE(hms->l2vic_dev), 1,
+ m_cfg->cfgtable.fastl2vic_base << 16);
+
glob_regs_dev = qdev_new(TYPE_HEXAGON_GLOBALREG);
object_property_add_child(OBJECT(machine), "global-regs",
OBJECT(glob_regs_dev));
qdev_prop_set_uint64(glob_regs_dev, "config-table-addr", m_cfg->cfgbase);
qdev_prop_set_uint32(glob_regs_dev, "dsp-rev", rev);
+ object_property_set_link(OBJECT(glob_regs_dev), "l2vic",
+ OBJECT(hms->l2vic_dev), &error_fatal);
sysbus_realize_and_unref(SYS_BUS_DEVICE(glob_regs_dev), &error_fatal);
tlb_dev = qdev_new(TYPE_HEXAGON_TLB);
@@ -154,15 +166,23 @@ static void hexagon_common_init(MachineState *machine, Rev_t rev,
*/
qdev_prop_set_bit(DEVICE(cpu), "start-powered-off", (i != 0));
if (i == 0) {
+ cpu0 = DEVICE(cpu);
hexagon_init_bootstrap(dms, cpu);
}
object_property_set_link(OBJECT(cpu), "global-regs",
OBJECT(glob_regs_dev), &error_fatal);
object_property_set_link(OBJECT(cpu), "tlb",
OBJECT(tlb_dev), &error_fatal);
+ object_property_set_link(OBJECT(cpu), "l2vic",
+ OBJECT(hms->l2vic_dev), &error_fatal);
qdev_realize_and_unref(DEVICE(cpu), NULL, &error_fatal);
}
+ for (int i = 0; i < 8; i++) {
+ sysbus_connect_irq(SYS_BUS_DEVICE(hms->l2vic_dev), i,
+ qdev_get_gpio_in(cpu0, i));
+ }
+
rom_add_blob_fixed_as("config_table.rom", &m_cfg->cfgtable,
sizeof(m_cfg->cfgtable), m_cfg->cfgbase,
&address_space_memory);
diff --git a/hw/hexagon/hexagon_globalreg.c b/hw/hexagon/hexagon_globalreg.c
index b5e5913507e..3d0d6f9516c 100644
--- a/hw/hexagon/hexagon_globalreg.c
+++ b/hw/hexagon/hexagon_globalreg.c
@@ -11,6 +11,7 @@
#include "hw/core/qdev-properties.h"
#include "hw/core/sysbus.h"
#include "hw/core/resettable.h"
+#include "hw/intc/hex-l2vic.h"
#include "migration/vmstate.h"
#include "qom/object.h"
#include "target/hexagon/cpu.h"
@@ -135,6 +136,32 @@ static inline uint32_t apply_write_mask(uint32_t new_val, uint32_t cur_val,
return new_val;
}
+static inline bool is_vid_reg(uint32_t reg)
+{
+ return reg == HEX_SREG_VID || reg == HEX_SREG_VID1;
+}
+
+/*
+ * HEX_SREG_VID/VID1 are backed by the L2VIC's VID group registers
+ * rather than the plain regs[] array, when an L2VIC is wired up.
+ */
+static uint32_t get_reg_value(HexagonGlobalRegState *s, uint32_t reg)
+{
+ if (is_vid_reg(reg) && s->l2vic) {
+ return l2vic_read_vid(s->l2vic, reg == HEX_SREG_VID ? 0 : 1);
+ }
+ return s->regs[reg];
+}
+
+static void set_reg_value(HexagonGlobalRegState *s, uint32_t reg,
+ uint32_t value)
+{
+ s->regs[reg] = value;
+ if (is_vid_reg(reg) && s->l2vic) {
+ l2vic_update_vid(s->l2vic, reg == HEX_SREG_VID ? 0 : 1, value);
+ }
+}
+
uint32_t hexagon_globalreg_read(HexagonGlobalRegState *s, uint32_t reg,
uint32_t htid)
{
@@ -146,7 +173,7 @@ uint32_t hexagon_globalreg_read(HexagonGlobalRegState *s, uint32_t reg,
g_assert(reg < NUM_SREGS);
g_assert(reg >= HEX_SREG_GLB_START);
- value = s->regs[reg];
+ value = get_reg_value(s, reg);
trace_hexagon_globalreg_read(htid, get_sreg_name(reg), value);
return value;
@@ -160,7 +187,7 @@ void hexagon_globalreg_write(HexagonGlobalRegState *s, uint32_t reg,
}
g_assert(reg < NUM_SREGS);
g_assert(reg >= HEX_SREG_GLB_START);
- s->regs[reg] = value;
+ set_reg_value(s, reg, value);
trace_hexagon_globalreg_write(htid, get_sreg_name(reg), value);
}
@@ -168,6 +195,7 @@ uint32_t hexagon_globalreg_masked_value(HexagonGlobalRegState *s, uint32_t reg,
uint32_t value)
{
uint32_t reg_mask;
+ uint32_t cur_val;
if (!s) {
return value;
@@ -175,9 +203,10 @@ uint32_t hexagon_globalreg_masked_value(HexagonGlobalRegState *s, uint32_t reg,
g_assert(reg < NUM_SREGS);
g_assert(reg >= HEX_SREG_GLB_START);
reg_mask = global_sreg_immut_masks[reg];
+ cur_val = get_reg_value(s, reg);
return reg_mask == IMMUTABLE ?
- s->regs[reg] :
- apply_write_mask(value, s->regs[reg], reg_mask);
+ cur_val :
+ apply_write_mask(value, cur_val, reg_mask);
}
void hexagon_globalreg_write_masked(HexagonGlobalRegState *s, uint32_t reg,
@@ -186,7 +215,7 @@ void hexagon_globalreg_write_masked(HexagonGlobalRegState *s, uint32_t reg,
if (!s) {
return;
}
- s->regs[reg] = hexagon_globalreg_masked_value(s, reg, value);
+ set_reg_value(s, reg, hexagon_globalreg_masked_value(s, reg, value));
}
uint64_t hexagon_globalreg_get_pcycle_base(HexagonGlobalRegState *s)
@@ -275,6 +304,8 @@ static const VMStateDescription vmstate_hexagon_globalreg = {
};
static const Property hexagon_globalreg_properties[] = {
+ DEFINE_PROP_LINK("l2vic", HexagonGlobalRegState, l2vic,
+ TYPE_HEX_L2VIC_INTERFACE, HexL2VicInterface *),
DEFINE_PROP_UINT32("boot-evb", HexagonGlobalRegState, boot_evb, 0x0),
DEFINE_PROP_UINT64("config-table-addr", HexagonGlobalRegState,
config_table_addr, 0xffffffffULL),
diff --git a/hw/hexagon/virt.c b/hw/hexagon/virt.c
index a3638998b87..2256d3d53a5 100644
--- a/hw/hexagon/virt.c
+++ b/hw/hexagon/virt.c
@@ -15,6 +15,7 @@
#include "hw/hexagon/hexagon.h"
#include "hw/hexagon/hexagon_globalreg.h"
#include "hw/hexagon/hexagon_tlb.h"
+#include "hw/intc/hex-l2vic.h"
#include "hw/core/loader.h"
#include "hw/core/qdev-properties.h"
#include "hw/core/qdev-clock.h"
@@ -268,10 +269,22 @@ static void virt_init(MachineState *ms)
&vms->parent_obj.cfgtable_rom);
fdt_add_hvx(vms, m_cfg);
+ vms->parent_obj.l2vic_dev = qdev_new(TYPE_HEX_L2VIC);
+ object_property_add_child(OBJECT(ms), "l2vic",
+ OBJECT(vms->parent_obj.l2vic_dev));
+ sysbus_realize_and_unref(SYS_BUS_DEVICE(vms->parent_obj.l2vic_dev),
+ &error_fatal);
+ sysbus_mmio_map(SYS_BUS_DEVICE(vms->parent_obj.l2vic_dev), 0,
+ m_cfg->l2vic_base);
+ sysbus_mmio_map(SYS_BUS_DEVICE(vms->parent_obj.l2vic_dev), 1,
+ m_cfg->cfgtable.fastl2vic_base << 16);
+
gsregs_dev = qdev_new(TYPE_HEXAGON_GLOBALREG);
object_property_add_child(OBJECT(ms), "global-regs", OBJECT(gsregs_dev));
qdev_prop_set_uint64(gsregs_dev, "config-table-addr", m_cfg->cfgbase);
qdev_prop_set_uint32(gsregs_dev, "dsp-rev", v68_rev);
+ object_property_set_link(OBJECT(gsregs_dev), "l2vic",
+ OBJECT(vms->parent_obj.l2vic_dev), &error_fatal);
sysbus_realize_and_unref(SYS_BUS_DEVICE(gsregs_dev), &error_fatal);
tlb_dev = qdev_new(TYPE_HEXAGON_TLB);
@@ -301,9 +314,18 @@ static void virt_init(MachineState *ms)
OBJECT(gsregs_dev), &error_fatal);
object_property_set_link(OBJECT(cpu), "tlb",
OBJECT(tlb_dev), &error_fatal);
+ object_property_set_link(OBJECT(cpu), "l2vic",
+ OBJECT(vms->parent_obj.l2vic_dev),
+ &error_fatal);
qdev_realize_and_unref(DEVICE(cpu), NULL, &error_fatal);
}
+
+ for (int i = 0; i < 8; i++) {
+ sysbus_connect_irq(SYS_BUS_DEVICE(vms->parent_obj.l2vic_dev), i,
+ qdev_get_gpio_in(cpu0, i));
+ }
+
fdt_add_cpu_nodes(vms);
clk_phandle = fdt_add_clocks(vms);
fdt_add_uart(vms, VIRT_UART0, clk_phandle);
--
2.34.1
On 7/14/2026 8:51 AM, Brian Cain wrote: > Instantiate the L2VIC device in both machine models, map its slow and > MMIO regions, and link it to each CPU via the "l2vic" property. > > Signed-off-by: Brian Cain <brian.cain@oss.qualcomm.com> > --- > include/hw/hexagon/hexagon.h | 1 + > include/hw/hexagon/hexagon_globalreg.h | 4 +++ > hw/hexagon/hexagon_dsp.c | 20 +++++++++++++ > hw/hexagon/hexagon_globalreg.c | 41 ++++++++++++++++++++++---- > hw/hexagon/virt.c | 22 ++++++++++++++ > 5 files changed, 83 insertions(+), 5 deletions(-) > Reviewed-by: Pierrick Bouvier <pierrick.bouvier@oss.qualcomm.com>
© 2016 - 2026 Red Hat, Inc.