#include "v3d_simulator_wrapper.h"
#include "util/macros.h"
+#include "util/bitscan.h"
#include "drm-uapi/v3d_drm.h"
#define HW_REGISTER_RO(x) (x)
static struct v3d_hw *v3d_isr_hw;
+
+static void
+v3d_isr_core(struct v3d_hw *v3d,
+ unsigned core)
+{
+ /* FIXME: so far we are assuming just one core, and using only the _0_
+ * registers. If we add multiple-core on the simulator, we would need
+ * to pass core as a parameter, and chose the proper registers.
+ */
+ assert(core == 0);
+ uint32_t core_status = V3D_READ(V3D_CTL_0_INT_STS);
+ V3D_WRITE(V3D_CTL_0_INT_CLR, core_status);
+
+ if (core_status & V3D_CTL_0_INT_STS_INT_OUTOMEM_SET) {
+ uint32_t size = 256 * 1024;
+ uint32_t offset = v3d_simulator_get_spill(size);
+
+ v3d_reload_gmp(v3d);
+
+ V3D_WRITE(V3D_PTB_0_BPOA, offset);
+ V3D_WRITE(V3D_PTB_0_BPOS, size);
+ return;
+ }
+
+ if (core_status & V3D_CTL_0_INT_STS_INT_GMPV_SET) {
+ fprintf(stderr, "GMP violation at 0x%08x\n",
+ V3D_READ(V3D_GMP_VIO_ADDR));
+ abort();
+ } else {
+ fprintf(stderr,
+ "Unexpected ISR with core status 0x%08x\n",
+ core_status);
+ }
+ abort();
+}
+
+static void
+handle_mmu_interruptions(struct v3d_hw *v3d,
+ uint32_t hub_status)
+{
+ bool wrv = hub_status & V3D_HUB_CTL_INT_STS_INT_MMU_WRV_SET;
+ bool pti = hub_status & V3D_HUB_CTL_INT_STS_INT_MMU_PTI_SET;
+ bool cap = hub_status & V3D_HUB_CTL_INT_STS_INT_MMU_CAP_SET;
+
+ if (!(pti || cap || wrv))
+ return;
+
+ const char *client = "?";
+ uint32_t axi_id = V3D_READ(V3D_MMU_VIO_ID);
+ uint32_t va_width = 30;
+
+#if V3D_VERSION >= 41
+ static const char *const v3d41_axi_ids[] = {
+ "L2T",
+ "PTB",
+ "PSE",
+ "TLB",
+ "CLE",
+ "TFU",
+ "MMU",
+ "GMP",
+ };
+
+ axi_id = axi_id >> 5;
+ if (axi_id < ARRAY_SIZE(v3d41_axi_ids))
+ client = v3d41_axi_ids[axi_id];
+
+ uint32_t mmu_debug = V3D_READ(V3D_MMU_DEBUG_INFO);
+
+ va_width += ((mmu_debug & V3D_MMU_DEBUG_INFO_VA_WIDTH_SET)
+ >> V3D_MMU_DEBUG_INFO_VA_WIDTH_LSB);
+#endif
+ /* Only the top bits (final number depends on the gen) of the virtual
+ * address are reported in the MMU VIO_ADDR register.
+ */
+ uint64_t vio_addr = ((uint64_t)V3D_READ(V3D_MMU_VIO_ADDR) <<
+ (va_width - 32));
+
+ /* Difference with the kernal: here were are going to abort after
+ * logging, so we don't bother with some stuff that the kernel does,
+ * like restoring the MMU ctrl bits
+ */
+
+ fprintf(stderr, "MMU error from client %s (%d) at 0x%llx%s%s%s\n",
+ client, axi_id, (long long) vio_addr,
+ wrv ? ", write violation" : "",
+ pti ? ", pte invalid" : "",
+ cap ? ", cap exceeded" : "");
+
+ abort();
+}
+
+static void
+v3d_isr_hub(struct v3d_hw *v3d)
+{
+ uint32_t hub_status = V3D_READ(V3D_HUB_CTL_INT_STS);
+
+ /* Acknowledge the interrupts we're handling here */
+ V3D_WRITE(V3D_HUB_CTL_INT_CLR, hub_status);
+
+ if (hub_status & V3D_HUB_CTL_INT_STS_INT_TFUC_SET) {
+ /* FIXME: we were not able to raise this exception. We let the
+ * unreachable here, so we could get one if it is raised on
+ * the future. In any case, note that for this case we would
+ * only be doing debugging log.
+ */
+ unreachable("TFU Conversion Complete interrupt not handled");
+ }
+
+ handle_mmu_interruptions(v3d, hub_status);
+}
+
static void
v3d_isr(uint32_t hub_status)
{
struct v3d_hw *v3d = v3d_isr_hw;
+ uint32_t mask = hub_status;
- /* Check the per-core bits */
- if (hub_status & (1 << 0)) {
- uint32_t core_status = V3D_READ(V3D_CTL_0_INT_STS);
- V3D_WRITE(V3D_CTL_0_INT_CLR, core_status);
-
- if (core_status & V3D_CTL_0_INT_STS_INT_OUTOMEM_SET) {
- uint32_t size = 256 * 1024;
- uint32_t offset = v3d_simulator_get_spill(size);
-
- v3d_reload_gmp(v3d);
-
- V3D_WRITE(V3D_PTB_0_BPOA, offset);
- V3D_WRITE(V3D_PTB_0_BPOS, size);
- return;
- }
-
- if (core_status & V3D_CTL_0_INT_STS_INT_GMPV_SET) {
- fprintf(stderr, "GMP violation at 0x%08x\n",
- V3D_READ(V3D_GMP_VIO_ADDR));
- abort();
- } else {
- fprintf(stderr,
- "Unexpected ISR with core status 0x%08x\n",
- core_status);
- }
- abort();
+ /* Check the hub_status bits */
+ while (mask) {
+ unsigned core = u_bit_scan(&mask);
+
+ if (core == v3d_hw_get_hub_core())
+ v3d_isr_hub(v3d);
+ else
+ v3d_isr_core(v3d, core);
}
return;
V3D_WRITE(V3D_CTL_0_MISCCFG, V3D_CTL_1_MISCCFG_OVRTMUOUT_SET);
#endif
+ /* FIXME: the kernel captures some additional core interrupts here,
+ * for tracing. Perhaps we should evaluate to do the same here and add
+ * some debug options.
+ */
uint32_t core_interrupts = (V3D_CTL_0_INT_STS_INT_GMPV_SET |
V3D_CTL_0_INT_STS_INT_OUTOMEM_SET);
V3D_WRITE(V3D_CTL_0_INT_MSK_SET, ~core_interrupts);
V3D_WRITE(V3D_CTL_0_INT_MSK_CLR, core_interrupts);
+ uint32_t hub_interrupts =
+ (V3D_HUB_CTL_INT_STS_INT_MMU_WRV_SET | /* write violation */
+ V3D_HUB_CTL_INT_STS_INT_MMU_PTI_SET | /* page table invalid */
+ V3D_HUB_CTL_INT_STS_INT_MMU_CAP_SET | /* CAP exceeded */
+ V3D_HUB_CTL_INT_STS_INT_TFUC_SET); /* TFU conversion */
+
+ V3D_WRITE(V3D_HUB_CTL_INT_MSK_SET, ~hub_interrupts);
+ V3D_WRITE(V3D_HUB_CTL_INT_MSK_CLR, hub_interrupts);
+
v3d_isr_hw = v3d;
v3d_hw_set_isr(v3d, v3d_isr);
}