On 7/17/2026 6:32 PM, Brian Cain wrote: > Instantiate the QTimer device in both machine models, map its > view region, connect its interrupt lines into l2vic, > and link it to the globalreg device. > > Store the QTimer DeviceState pointer in the shared > HexagonCommonMachineState. > > Signed-off-by: Brian Cain <[email protected]> > --- > include/hw/hexagon/hexagon.h | 1 + > include/hw/hexagon/hexagon_globalreg.h | 4 ++++ > hw/hexagon/hexagon_dsp.c | 14 ++++++++++++++ > hw/hexagon/hexagon_globalreg.c | 17 +++++++++++++++-- > hw/hexagon/virt.c | 16 ++++++++++++++++ > hw/hexagon/Kconfig | 1 + > 6 files changed, 51 insertions(+), 2 deletions(-) > > diff --git a/include/hw/hexagon/hexagon.h b/include/hw/hexagon/hexagon.h > index 6753ae32cde..3eea1e54ff2 100644 > --- a/include/hw/hexagon/hexagon.h > +++ b/include/hw/hexagon/hexagon.h > @@ -157,6 +157,7 @@ struct HexagonCommonMachineState { > MemoryRegion ram; > MemoryRegion cfgtable_rom; > DeviceState *l2vic_dev; > + DeviceState *qtimer_dev; > }; > > #endif > diff --git a/include/hw/hexagon/hexagon_globalreg.h > b/include/hw/hexagon/hexagon_globalreg.h > index 397dc6854c9..07437dfabb1 100644 > --- a/include/hw/hexagon/hexagon_globalreg.h > +++ b/include/hw/hexagon/hexagon_globalreg.h > @@ -11,6 +11,7 @@ > #include "hw/core/qdev.h" > #include "hw/core/sysbus.h" > #include "hw/intc/hex-l2vic.h" > +#include "hw/timer/qct-qtimer.h" > #include "qom/object.h" > #include "target/hexagon/cpu.h" > > @@ -26,6 +27,9 @@ struct HexagonGlobalRegState { > /* L2VIC interface used to back the VID/VID1 registers */ > HexL2VicInterface *l2vic; > > + /* QTimer interface used to back the TIMERLO/TIMERHI registers */ > + QctQtimerInterface *qtimer; > + > /* 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 4cf88627c8f..21bb80ac27a 100644 > --- a/hw/hexagon/hexagon_dsp.c > +++ b/hw/hexagon/hexagon_dsp.c > @@ -17,6 +17,7 @@ > #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/core/loader.h" > #include "qapi/error.h" > #include "qemu/error-report.h" > @@ -141,6 +142,17 @@ static void hexagon_common_init(MachineState *machine, > Rev_t rev, > sysbus_mmio_map(SYS_BUS_DEVICE(hms->l2vic_dev), 1, > m_cfg->cfgtable.fastl2vic_base << 16); > > + hms->qtimer_dev = qdev_new(TYPE_QCT_QTIMER); > + object_property_add_child(OBJECT(machine), "qtimer", > + OBJECT(hms->qtimer_dev)); > + qdev_prop_set_uint32(hms->qtimer_dev, "nr_frames", 3); > + sysbus_realize_and_unref(SYS_BUS_DEVICE(hms->qtimer_dev), &error_fatal); > + sysbus_mmio_map(SYS_BUS_DEVICE(hms->qtimer_dev), 1, m_cfg->qtmr_region); > + sysbus_connect_irq(SYS_BUS_DEVICE(hms->qtimer_dev), 0, > + qdev_get_gpio_in(hms->l2vic_dev, 3)); > + sysbus_connect_irq(SYS_BUS_DEVICE(hms->qtimer_dev), 1, > + qdev_get_gpio_in(hms->l2vic_dev, 4)); > + > glob_regs_dev = qdev_new(TYPE_HEXAGON_GLOBALREG); > object_property_add_child(OBJECT(machine), "global-regs", > OBJECT(glob_regs_dev)); > @@ -148,6 +160,8 @@ static void hexagon_common_init(MachineState *machine, > Rev_t rev, > 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); > + object_property_set_link(OBJECT(glob_regs_dev), "qtimer", > + OBJECT(hms->qtimer_dev), &error_fatal); > sysbus_realize_and_unref(SYS_BUS_DEVICE(glob_regs_dev), &error_fatal); > > tlb_dev = qdev_new(TYPE_HEXAGON_TLB); > diff --git a/hw/hexagon/hexagon_globalreg.c b/hw/hexagon/hexagon_globalreg.c > index 3d0d6f9516c..0353d89f87b 100644 > --- a/hw/hexagon/hexagon_globalreg.c > +++ b/hw/hexagon/hexagon_globalreg.c > @@ -141,15 +141,26 @@ static inline bool is_vid_reg(uint32_t reg) > return reg == HEX_SREG_VID || reg == HEX_SREG_VID1; > } > > +static inline bool is_timer_reg(uint32_t reg) > +{ > + return reg == HEX_SREG_TIMERLO || reg == HEX_SREG_TIMERHI; > +} > + > /* > - * 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. > + * HEX_SREG_VID/VID1 are backed by the L2VIC's VID group registers, and > + * HEX_SREG_TIMERLO/TIMERHI are backed by the QTimer's live counter, > + * rather than the plain regs[] array, when those devices are 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); > } > + if (is_timer_reg(reg) && s->qtimer) { > + return reg == HEX_SREG_TIMERLO ? > + qct_qtimer_get_timer_lo(s->qtimer) : > + qct_qtimer_get_timer_hi(s->qtimer); > + } > return s->regs[reg]; > } > > @@ -306,6 +317,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_LINK("qtimer", HexagonGlobalRegState, qtimer, > + TYPE_QCT_QTIMER_INTERFACE, QctQtimerInterface *), > 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 2256d3d53a5..7e32765d1cc 100644 > --- a/hw/hexagon/virt.c > +++ b/hw/hexagon/virt.c > @@ -16,6 +16,7 @@ > #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/core/loader.h" > #include "hw/core/qdev-properties.h" > #include "hw/core/qdev-clock.h" > @@ -279,12 +280,27 @@ static void virt_init(MachineState *ms) > sysbus_mmio_map(SYS_BUS_DEVICE(vms->parent_obj.l2vic_dev), 1, > m_cfg->cfgtable.fastl2vic_base << 16); > > + vms->parent_obj.qtimer_dev = qdev_new(TYPE_QCT_QTIMER); > + object_property_add_child(OBJECT(ms), "qtimer", > + OBJECT(vms->parent_obj.qtimer_dev)); > + qdev_prop_set_uint32(vms->parent_obj.qtimer_dev, "nr_frames", 2); > + sysbus_realize_and_unref(SYS_BUS_DEVICE(vms->parent_obj.qtimer_dev), > + &error_fatal); > + sysbus_mmio_map(SYS_BUS_DEVICE(vms->parent_obj.qtimer_dev), 1, > + m_cfg->qtmr_region); > + sysbus_connect_irq(SYS_BUS_DEVICE(vms->parent_obj.qtimer_dev), 0, > + qdev_get_gpio_in(vms->parent_obj.l2vic_dev, 3)); > + sysbus_connect_irq(SYS_BUS_DEVICE(vms->parent_obj.qtimer_dev), 1, > + qdev_get_gpio_in(vms->parent_obj.l2vic_dev, 4));
We should consider extract an init function for this, as it's mostly duplicated between virt and hexagon_dsp. Also, is the difference in nr_frames expected? > + > 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); > + object_property_set_link(OBJECT(gsregs_dev), "qtimer", > + OBJECT(vms->parent_obj.qtimer_dev), > &error_fatal); > sysbus_realize_and_unref(SYS_BUS_DEVICE(gsregs_dev), &error_fatal); > > tlb_dev = qdev_new(TYPE_HEXAGON_TLB); > diff --git a/hw/hexagon/Kconfig b/hw/hexagon/Kconfig > index 191c6c5d65c..56cd5b15c55 100644 > --- a/hw/hexagon/Kconfig > +++ b/hw/hexagon/Kconfig > @@ -3,6 +3,7 @@ config HEX_DSP > default y > depends on HEXAGON > select HEX_L2VIC > + select HEX_QTIMER > > config HEX_VIRT > bool
