@samitouri / QOSamiQemu / commits / bbe6bc469a

hw/arm/tegra241-cmdqv: Emulate VCMDQ register writes

This is the write side counterpart of the VCMDQ read emulation. Add write handling for both the direct VCMDQ aperture and the VINTF logical aperture using the same index decoding and VINTF-to-VCMDQ translation logic as the read path. VINTF aperture writes are translated to their direct-aperture equivalent and update the same cached state. Page 1 registers (BASE, CONS_INDX_BASE) always update the cache. Per the CMDQV architecture, a VCMDQ must be allocated to a Virtual Interface before it is used to send commands to the SMMU. Until that allocation happens, MMIO writes only update cached register state - no command consumption, error handling, or interrupt activity is driven from these writes. Subsequent patches wire up IOMMU_HW_QUEUE_ALLOC, mmap the host VINTF Page 0, and install it into guest MMIO; after that, Page 0 writes from either aperture reach the hardware-backed mmap'd page instead of just the cache. Signed-off-by: Nicolin Chen <nicolinc@nvidia.com> Tested-by: Nicolin Chen <nicolinc@nvidia.com> Reviewed-by: Eric Auger <eric.auger@redhat.com> Tested-by: Eric Auger <eric.auger@redhat.com> Signed-off-by: Shameer Kolothum <skolothumtho@nvidia.com> Message-id: 20260609112552.378999-18-skolothumtho@nvidia.com Co-developed-by: Shameer Kolothum <skolothumtho@nvidia.com> Signed-off-by: Shameer Kolothum <skolothumtho@nvidia.com> Signed-off-by: Peter Maydell <peter.maydell@linaro.org>

Nicolin Chen committed Jun 9, 2026 at 12:25 UTC bbe6bc469a96c64a66e221d83605fe71d248ff9f
2 files changed +159 -2
hw/arm/tegra241-cmdqv.c
+157 -2
@@ -120,6 +120,104 @@ static uint64_t tegra241_cmdqv_config_vintf_read(Tegra241CMDQV *cmdqv,
120 }
121 }
122
123 +/*
124 + * Write a VCMDQ Page 0 register (control/status) using VCMDQ0_* offsets.
125 + *
126 + * The caller normalizes the MMIO offset such that @offset0 always refers
127 + * to a VCMDQ0_* register, while @index selects the VCMDQ instance.
128 + *
129 + * Page 0 registers are all 32-bit; this helper is only called for 4-byte
130 + * writes.
131 + */
132 +static void tegra241_cmdqv_write_vcmdq_page0(Tegra241CMDQV *cmdqv,
133 + hwaddr offset0, int index,
134 + uint32_t value, bool direct)
135 +{
136 + switch (offset0) {
137 + case A_VCMDQ0_CONS_INDX:
138 + cmdqv->vcmdq_cons_indx[index] = value;
139 + break;
140 + case A_VCMDQ0_PROD_INDX:
141 + /* VCMDQ is functional only once allocated to a VINTF; cache only. */
142 + cmdqv->vcmdq_prod_indx[index] = value;
143 + break;
144 + case A_VCMDQ0_CONFIG:
145 + if (value & R_VCMDQ0_CONFIG_CMDQ_EN_MASK) {
146 + cmdqv->vcmdq_status[index] |= R_VCMDQ0_STATUS_CMDQ_EN_OK_MASK;
147 + } else {
148 + cmdqv->vcmdq_status[index] &= ~R_VCMDQ0_STATUS_CMDQ_EN_OK_MASK;
149 + }
150 + cmdqv->vcmdq_config[index] = value;
151 + break;
152 + case A_VCMDQ0_GERRORN:
153 + /* VCMDQ is functional only once allocated to a VINTF; cache only. */
154 + cmdqv->vcmdq_gerrorn[index] = value;
155 + break;
156 + default:
157 + qemu_log_mask(LOG_UNIMP,
158 + "%s unhandled write access at 0x%" PRIx64 "\n",
159 + __func__, offset0);
160 + }
161 + trace_tegra241_cmdqv_write_vcmdq_page0(index, direct ? "direct" : "vi",
162 + offset0, value);
163 +}
164 +
165 +/*
166 + * Write a VCMDQ Page 1 register (base / DRAM address) - 4-byte access.
167 + */
168 +static void tegra241_cmdqv_write_vcmdq_page1(Tegra241CMDQV *cmdqv,
169 + hwaddr offset0, int index,
170 + uint32_t value, bool direct)
171 +{
172 + switch (offset0) {
173 + case A_VCMDQ0_BASE_L:
174 + cmdqv->vcmdq_base[index] =
175 + deposit64(cmdqv->vcmdq_base[index], 0, 32, value);
176 + break;
177 + case A_VCMDQ0_BASE_H:
178 + cmdqv->vcmdq_base[index] =
179 + deposit64(cmdqv->vcmdq_base[index], 32, 32, value);
180 + break;
181 + case A_VCMDQ0_CONS_INDX_BASE_DRAM_L:
182 + cmdqv->vcmdq_cons_indx_base[index] =
183 + deposit64(cmdqv->vcmdq_cons_indx_base[index], 0, 32, value);
184 + break;
185 + case A_VCMDQ0_CONS_INDX_BASE_DRAM_H:
186 + cmdqv->vcmdq_cons_indx_base[index] =
187 + deposit64(cmdqv->vcmdq_cons_indx_base[index], 32, 32, value);
188 + break;
189 + default:
190 + qemu_log_mask(LOG_UNIMP,
191 + "%s unhandled write access at 0x%" PRIx64 "\n",
192 + __func__, offset0);
193 + }
194 + trace_tegra241_cmdqv_write_vcmdq_page1(index, direct ? "direct" : "vi",
195 + offset0, value);
196 +}
197 +
198 +/*
199 + * Write a VCMDQ Page 1 register - 8-byte access at BASE_L or DRAM_L.
200 + */
201 +static void tegra241_cmdqv_write_vcmdq_page1_64(Tegra241CMDQV *cmdqv,
202 + hwaddr offset0, int index,
203 + uint64_t value, bool direct)
204 +{
205 + switch (offset0) {
206 + case A_VCMDQ0_BASE_L:
207 + cmdqv->vcmdq_base[index] = value;
208 + break;
209 + case A_VCMDQ0_CONS_INDX_BASE_DRAM_L:
210 + cmdqv->vcmdq_cons_indx_base[index] = value;
211 + break;
212 + default:
213 + qemu_log_mask(LOG_UNIMP,
214 + "%s unhandled 64-bit write at 0x%" PRIx64 "\n",
215 + __func__, offset0);
216 + }
217 + trace_tegra241_cmdqv_write_vcmdq_page1(index, direct ? "direct" : "vi",
218 + offset0, value);
219 +}
220 +
221 static void tegra241_cmdqv_config_vintf_write(Tegra241CMDQV *cmdqv,
222 hwaddr offset, uint64_t value)
223 {
@@ -243,6 +341,8 @@ out:
341 static void tegra241_cmdqv_writel_mmio(Tegra241CMDQV *cmdqv, hwaddr offset,
342 uint32_t value)
343 {
344 + int index;
345 +
346 switch (offset) {
347 case A_CONFIG:
348 cmdqv->config = value;
@@ -261,6 +361,39 @@ static void tegra241_cmdqv_writel_mmio(Tegra241CMDQV *cmdqv, hwaddr offset,
361 case A_VINTF0_CONFIG ... A_VINTF0_LVCMDQ_ERR_MAP_3:
362 tegra241_cmdqv_config_vintf_write(cmdqv, offset, value);
363 break;
364 + case A_VI_VCMDQ0_CONS_INDX ... A_VI_VCMDQ1_GERRORN:
365 + /*
366 + * VINTF Page0 registers are hardware aliases of VCMDQ Page0 registers.
367 + * Translate the VINTF aperture offset to its VCMDQ Page0 equivalent
368 + * before dispatching to the Page 0 helper.
369 + */
370 + offset -= CMDQV_VINTF_PAGE0_BASE - CMDQV_VCMDQ_PAGE0_BASE;
371 + index = (offset - CMDQV_VCMDQ_PAGE0_BASE) / CMDQV_VCMDQ_STRIDE;
372 + tegra241_cmdqv_write_vcmdq_page0(cmdqv,
373 + offset - index * CMDQV_VCMDQ_STRIDE, index, value, false);
374 + break;
375 + case A_VCMDQ0_CONS_INDX ... A_VCMDQ1_GERRORN:
376 + /*
377 + * Decode a per-VCMDQ Page 0 access. Each VCMDQ occupies a
378 + * CMDQV_VCMDQ_STRIDE-byte window; extract the index and normalize
379 + * to the VCMDQ0_* offset before calling the Page 0 helper.
380 + */
381 + index = (offset - CMDQV_VCMDQ_PAGE0_BASE) / CMDQV_VCMDQ_STRIDE;
382 + tegra241_cmdqv_write_vcmdq_page0(cmdqv,
383 + offset - index * CMDQV_VCMDQ_STRIDE, index, value, true);
384 + break;
385 + case A_VI_VCMDQ0_BASE_L ... A_VI_VCMDQ1_CONS_INDX_BASE_DRAM_H:
386 + /* Same VINTF-to-VCMDQ translation as VINTF Page0 case above. */
387 + offset -= CMDQV_VINTF_PAGE1_BASE - CMDQV_VCMDQ_PAGE1_BASE;
388 + index = (offset - CMDQV_VCMDQ_PAGE1_BASE) / CMDQV_VCMDQ_STRIDE;
389 + tegra241_cmdqv_write_vcmdq_page1(cmdqv,
390 + offset - index * CMDQV_VCMDQ_STRIDE, index, value, false);
391 + break;
392 + case A_VCMDQ0_BASE_L ... A_VCMDQ1_CONS_INDX_BASE_DRAM_H:
393 + index = (offset - CMDQV_VCMDQ_PAGE1_BASE) / CMDQV_VCMDQ_STRIDE;
394 + tegra241_cmdqv_write_vcmdq_page1(cmdqv,
395 + offset - index * CMDQV_VCMDQ_STRIDE, index, value, true);
396 + break;
397 default:
398 qemu_log_mask(LOG_UNIMP, "%s unhandled write access at 0x%" PRIx64 "\n",
399 __func__, offset);
@@ -268,14 +401,36 @@ static void tegra241_cmdqv_writel_mmio(Tegra241CMDQV *cmdqv, hwaddr offset,
401 }
402
403 /*
271 - * 8-byte MMIO write handler.
404 + * 8-byte MMIO write handler. Only Page 1 BASE / CONS_INDX_BASE_DRAM accept
405 + * full 64-bit writes; other offsets are write-ignored.
406 */
407 static void tegra241_cmdqv_writell_mmio(Tegra241CMDQV *cmdqv, hwaddr offset,
408 uint64_t value)
409 {
276 - qemu_log_mask(LOG_UNIMP,
410 + int index;
411 +
412 + switch (offset) {
413 + case A_VI_VCMDQ0_BASE_L ... A_VI_VCMDQ1_CONS_INDX_BASE_DRAM_H:
414 + /*
415 + * VINTF Page1 registers are hardware aliases of VCMDQ Page1 registers.
416 + * Translate the VINTF aperture offset to its VCMDQ Page1 equivalent
417 + * before dispatching to the Page 1 helper.
418 + */
419 + offset -= CMDQV_VINTF_PAGE1_BASE - CMDQV_VCMDQ_PAGE1_BASE;
420 + index = (offset - CMDQV_VCMDQ_PAGE1_BASE) / CMDQV_VCMDQ_STRIDE;
421 + tegra241_cmdqv_write_vcmdq_page1_64(cmdqv,
422 + offset - index * CMDQV_VCMDQ_STRIDE, index, value, false);
423 + break;
424 + case A_VCMDQ0_BASE_L ... A_VCMDQ1_CONS_INDX_BASE_DRAM_H:
425 + index = (offset - CMDQV_VCMDQ_PAGE1_BASE) / CMDQV_VCMDQ_STRIDE;
426 + tegra241_cmdqv_write_vcmdq_page1_64(cmdqv,
427 + offset - index * CMDQV_VCMDQ_STRIDE, index, value, true);
428 + break;
429 + default:
430 + qemu_log_mask(LOG_UNIMP,
431 "%s unhandled 64-bit write at 0x%" PRIx64 " (WI)\n",
432 __func__, offset);
433 + }
434 }
435
436 static void tegra241_cmdqv_write_mmio(void *opaque, hwaddr offset,
hw/arm/trace-events
+2
@@ -77,6 +77,8 @@ tegra241_cmdqv_read_mmio(uint64_t offset, uint64_t val, unsigned size) "offset:
77 tegra241_cmdqv_write_mmio(uint64_t offset, uint64_t val, unsigned size) "offset: 0x%"PRIx64" val: 0x%"PRIx64" size: 0x%x"
78 tegra241_cmdqv_read_vcmdq_page0(int index, const char *aperture, uint64_t offset0, uint64_t val) "vcmdq[%d] %s offset0: 0x%"PRIx64" val: 0x%"PRIx64
79 tegra241_cmdqv_read_vcmdq_page1(int index, const char *aperture, uint64_t offset0, uint64_t val) "vcmdq[%d] %s offset0: 0x%"PRIx64" val: 0x%"PRIx64
80 +tegra241_cmdqv_write_vcmdq_page0(int index, const char *aperture, uint64_t offset0, uint64_t val) "vcmdq[%d] %s offset0: 0x%"PRIx64" val: 0x%"PRIx64
81 +tegra241_cmdqv_write_vcmdq_page1(int index, const char *aperture, uint64_t offset0, uint64_t val) "vcmdq[%d] %s offset0: 0x%"PRIx64" val: 0x%"PRIx64
82
83 # strongarm.c
84 strongarm_uart_update_parameters(const char *label, int speed, char parity, int data_bits, int stop_bits) "%s speed=%d parity=%c data=%d stop=%d"