On 2/26/26 11:50 AM, Shameer Kolothum wrote: > From: Nicolin Chen <[email protected]> > > This is the write side counterpart of the VCMDQ read emulation. > > Add write handling for global VCMDQ and VI_VCMDQ register windows. > Per-VCMDQ accesses are decoded into a VCMDQ index and normalized to > VCMDQ0_* offsets, reusing the same layout assumptions as the read path. > > VI_VCMDQ registers are treated as a logical alias of the global VCMDQ > registers and share the same decoding logic. > > Writes are backed by cached register state only; no hardware queue > mapping is performed yet. > > Signed-off-by: Nicolin Chen <[email protected]> > Signed-off-by: Shameer Kolothum <[email protected]> > --- > hw/arm/tegra241-cmdqv.c | 83 +++++++++++++++++++++++++++++++++++++++++ > 1 file changed, 83 insertions(+) > > diff --git a/hw/arm/tegra241-cmdqv.c b/hw/arm/tegra241-cmdqv.c > index d2e6938e44..e1f1562c44 100644 > --- a/hw/arm/tegra241-cmdqv.c > +++ b/hw/arm/tegra241-cmdqv.c > @@ -151,6 +151,70 @@ static uint64_t tegra241_cmdqv_read(void *opaque, hwaddr > offset, unsigned size) > } > } > > +/* > + * Write a VCMDQ register using VCMDQ0_* offsets. > + * > + * The caller normalizes the MMIO offset such that @offset0 always refers > + * to a VCMDQ0_* register, while @index selects the VCMDQ instance. > + */ > +static void > +tegra241_cmdqv_write_vcmdq(Tegra241CMDQV *cmdqv, hwaddr offset0, int index, > + uint64_t value, unsigned size) > +{ > + switch (offset0) { > + case A_VCMDQ0_CONS_INDX: > + cmdqv->vcmdq_cons_indx[index] = value; > + return; > + case A_VCMDQ0_PROD_INDX: > + cmdqv->vcmdq_prod_indx[index] = (uint32_t)value; > + return; > + case A_VCMDQ0_CONFIG: > + if (value & R_VCMDQ0_CONFIG_CMDQ_EN_MASK) { > + cmdqv->vcmdq_status[index] |= R_VCMDQ0_STATUS_CMDQ_EN_OK_MASK; > + } else { > + cmdqv->vcmdq_status[index] &= ~R_VCMDQ0_STATUS_CMDQ_EN_OK_MASK; > + } > + cmdqv->vcmdq_config[index] = (uint32_t)value; > + return; > + case A_VCMDQ0_GERRORN: > + cmdqv->vcmdq_gerrorn[index] = (uint32_t)value; > + return; > + case A_VCMDQ0_BASE_L: > + if (size == 8) { > + cmdqv->vcmdq_base[index] = value; > + } else if (size == 4) { > + cmdqv->vcmdq_base[index] = > + (cmdqv->vcmdq_base[index] & 0xffffffff00000000ULL) | > + (value & 0xffffffffULL); > + } If we compare to smmuv3 std command queue API, we miss some checks. See smmu_writel() if (!smmu_cmdq_base_writable(s, reg_sec_sid)) { for CONS if (!smmu_cmdq_disabled_stable(s, reg_sec_sid)) { In the spec I have access to I failed to find any details about equivalent restrictions Can you check if those write accesses are always allowed? Thanks Eric > + return; > + case A_VCMDQ0_BASE_H: > + cmdqv->vcmdq_base[index] = > + (cmdqv->vcmdq_base[index] & 0xffffffffULL) | > + ((uint64_t)value << 32); > + return; > + case A_VCMDQ0_CONS_INDX_BASE_DRAM_L: > + if (size == 8) { > + cmdqv->vcmdq_cons_indx_base[index] = value; > + } else if (size == 4) { > + cmdqv->vcmdq_cons_indx_base[index] = > + (cmdqv->vcmdq_cons_indx_base[index] & 0xffffffff00000000ULL) > | > + (value & 0xffffffffULL); > + } > + return; > + case A_VCMDQ0_CONS_INDX_BASE_DRAM_H: > + cmdqv->vcmdq_cons_indx_base[index] = > + (cmdqv->vcmdq_cons_indx_base[index] & 0xffffffffULL) | > + ((uint64_t)value << 32); > + return; > + default: > + qemu_log_mask(LOG_UNIMP, > + "%s unhandled write access at 0x%" PRIx64 "\n", > + __func__, offset0); > + return; > + } > +} > + > static void tegra241_cmdqv_write_vintf(Tegra241CMDQV *cmdqv, hwaddr offset, > uint64_t value) > { > @@ -187,6 +251,7 @@ static void tegra241_cmdqv_write(void *opaque, hwaddr > offset, uint64_t value, > unsigned size) > { > Tegra241CMDQV *cmdqv = (Tegra241CMDQV *)opaque; > + int index; > > if (offset >= TEGRA241_CMDQV_IO_LEN) { > qemu_log_mask(LOG_UNIMP, > @@ -213,6 +278,24 @@ static void tegra241_cmdqv_write(void *opaque, hwaddr > offset, uint64_t value, > case A_VINTF0_CONFIG ... A_VINTF0_LVCMDQ_ERR_MAP_3: > tegra241_cmdqv_write_vintf(cmdqv, offset, value); > break; > + case A_VI_VCMDQ0_CONS_INDX ... A_VI_VCMDQ1_GERRORN: > + /* Same decoding as read() case: See comments above */ > + offset -= 0x20000; > + QEMU_FALLTHROUGH; > + case A_VCMDQ0_CONS_INDX ... A_VCMDQ1_GERRORN: > + index = (offset - 0x10000) / 0x80; > + tegra241_cmdqv_write_vcmdq(cmdqv, offset - 0x80 * index, index, > value, > + size); > + break; > + case A_VI_VCMDQ0_BASE_L ... A_VI_VCMDQ1_CONS_INDX_BASE_DRAM_H: > + /* Same decoding as read() case: See comments above */ > + offset -= 0x20000; > + QEMU_FALLTHROUGH; > + case A_VCMDQ0_BASE_L ... A_VCMDQ1_CONS_INDX_BASE_DRAM_H: > + index = (offset - 0x20000) / 0x80; > + tegra241_cmdqv_write_vcmdq(cmdqv, offset - 0x80 * index, index, > value, > + size); > + break; > default: > qemu_log_mask(LOG_UNIMP, "%s unhandled write access at 0x%" PRIx64 > "\n", > __func__, offset);
