[PATCH v4 4/5] drm/imagination: Limit access to the META firmware interface

Alexandru Dadu alexandru.dadu at imgtec.com
Tue Nov 3 01:00:43 PST 2026


From: Alessio Belle <alessio.belle at imgtec.com>

Ensure all accesses to the META firmware interface are done via the
generic firmware interface.

To achieve this:
- Introduce a new function to the firmware processor table to remove
  the last direct access to the META firmware interface from
  outside of the generic firmware logic.
- Remove the META firmware interface header.
- Rename the META firmware processor header adding an "internal" suffix.
- Add guard to the META firmware processor header to prevent accidental
  inclusion outside of the META firmware specific logic.
- Add a multi_dm_thread_assoc property to struct pvr_fw_defs to isolate
  that behaviour from the architecture.

Signed-off-by: Alessio Belle <alessio.belle at imgtec.com>
Signed-off-by: Alexandru Dadu <alexandru.dadu at imgtec.com>
---
 drivers/gpu/drm/imagination/pvr_fw.h               | 26 +++++++++
 drivers/gpu/drm/imagination/pvr_fw_meta.c          | 28 ++++++++--
 drivers/gpu/drm/imagination/pvr_fw_meta.h          | 14 -----
 drivers/gpu/drm/imagination/pvr_fw_mips.c          |  1 +
 drivers/gpu/drm/imagination/pvr_fw_riscv.c         |  1 +
 drivers/gpu/drm/imagination/pvr_fw_startstop.c     | 61 ++++++++++++----------
 drivers/gpu/drm/imagination/pvr_gem.h              |  1 -
 ...{pvr_rogue_meta.h => pvr_rogue_meta_internal.h} | 10 ++--
 8 files changed, 93 insertions(+), 49 deletions(-)

diff --git a/drivers/gpu/drm/imagination/pvr_fw.h b/drivers/gpu/drm/imagination/pvr_fw.h
index 4b25291135b6..610406668bbb 100644
--- a/drivers/gpu/drm/imagination/pvr_fw.h
+++ b/drivers/gpu/drm/imagination/pvr_fw.h
@@ -184,6 +184,23 @@ struct pvr_fw_defs {
 	 */
 	void (*irq_clear)(struct pvr_device *pvr_dev);
 
+	/**
+	 * @must_wait_fw_idle: Check if the FW stop sequence should wait for hardware
+	 *                     to report FW idle before proceeding.
+	 *
+	 * @pvr_dev: Target PowerVR device.
+	 * @wait[out]: Whether the FW stop sequence should wait for FW idle.
+	 *
+	 * This function is optional. The assumption is that we must always wait
+	 * for the firmware to reach idle; implement this function and return
+	 * %false to alter this behavior.
+	 *
+	 * Returns:
+	 *  * 0 on success.
+	 *  * Any appropriate error on failure.
+	 */
+	int (*must_wait_fw_idle)(struct pvr_device *pvr_dev, bool *wait);
+
 	/**
 	 * @has_fixed_data_addr: Specify whether the firmware fixed data must be loaded at the
 	 *                       address given by the firmware layout table.
@@ -191,6 +208,15 @@ struct pvr_fw_defs {
 	 * This value is mandatory.
 	 */
 	bool has_fixed_data_addr;
+
+	/**
+	 * @multi_dm_thread_assoc: Specify that all threads must be associated
+	 *                         with the DM separately rather than just the
+	 *                         first thread (the default behaviour).
+	 *
+	 * This value is mandatory.
+	 */
+	bool multi_dm_thread_assoc;
 };
 
 /**
diff --git a/drivers/gpu/drm/imagination/pvr_fw_meta.c b/drivers/gpu/drm/imagination/pvr_fw_meta.c
index 84f568b5510f..f451f97a4f2f 100644
--- a/drivers/gpu/drm/imagination/pvr_fw_meta.c
+++ b/drivers/gpu/drm/imagination/pvr_fw_meta.c
@@ -4,10 +4,11 @@
 #include "pvr_device.h"
 #include "pvr_fw.h"
 #include "pvr_fw_info.h"
-#include "pvr_fw_meta.h"
 #include "pvr_gem.h"
 #include "pvr_rogue_cr_defs.h"
-#include "pvr_rogue_meta.h"
+#define PVR_ROGUE_META_INTERNAL_H_EXPLICIT_INCLUDE
+#include "pvr_rogue_meta_internal.h"
+#undef PVR_ROGUE_META_INTERNAL_H_EXPLICIT_INCLUDE
 #include "pvr_vm.h"
 
 #include <linux/compiler.h>
@@ -32,7 +33,7 @@
  *  * 0 on success, or
  *  * Any error returned by pvr_cr_poll_reg32().
  */
-int
+static int
 pvr_meta_cr_read32(struct pvr_device *pvr_dev, u32 reg_addr, u32 *reg_value_out)
 {
 	int err;
@@ -563,6 +564,25 @@ pvr_meta_irq_clear(struct pvr_device *pvr_dev)
 		       ROGUE_CR_META_SP_MSLVIRQSTATUS_TRIGVECT2_CLRMSK);
 }
 
+static int
+pvr_meta_must_wait_fw_idle(struct pvr_device *pvr_dev, bool *wait)
+{
+	u32 reg_value;
+	int err;
+
+	err = pvr_meta_cr_read32(pvr_dev, META_CR_TxVECINT_BHALT, &reg_value);
+	if (err)
+		return err;
+
+	/*
+	 * Wait for Sidekick/Jones to signal IDLE including the Garten
+	 * Wrapper if there is no debugger attached (TxVECINT_BHALT = 0x0).
+	 */
+	*wait = !reg_value;
+
+	return 0;
+}
+
 const struct pvr_fw_defs pvr_fw_defs_meta = {
 	.init = pvr_meta_init,
 	.fini = pvr_meta_fini,
@@ -573,5 +593,7 @@ const struct pvr_fw_defs pvr_fw_defs_meta = {
 	.wrapper_init = pvr_meta_wrapper_init,
 	.irq_pending = pvr_meta_irq_pending,
 	.irq_clear = pvr_meta_irq_clear,
+	.must_wait_fw_idle = pvr_meta_must_wait_fw_idle,
 	.has_fixed_data_addr = false,
+	.multi_dm_thread_assoc = true,
 };
diff --git a/drivers/gpu/drm/imagination/pvr_fw_meta.h b/drivers/gpu/drm/imagination/pvr_fw_meta.h
deleted file mode 100644
index 911ad700cba6..000000000000
--- a/drivers/gpu/drm/imagination/pvr_fw_meta.h
+++ /dev/null
@@ -1,14 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-only OR MIT */
-/* Copyright (c) 2023 Imagination Technologies Ltd. */
-
-#ifndef PVR_FW_META_H
-#define PVR_FW_META_H
-
-#include <linux/types.h>
-
-/* Forward declaration from pvr_device.h */
-struct pvr_device;
-
-int pvr_meta_cr_read32(struct pvr_device *pvr_dev, u32 reg_addr, u32 *reg_value_out);
-
-#endif /* PVR_FW_META_H */
diff --git a/drivers/gpu/drm/imagination/pvr_fw_mips.c b/drivers/gpu/drm/imagination/pvr_fw_mips.c
index 6914fc46db50..ec0dad1692c3 100644
--- a/drivers/gpu/drm/imagination/pvr_fw_mips.c
+++ b/drivers/gpu/drm/imagination/pvr_fw_mips.c
@@ -196,4 +196,5 @@ const struct pvr_fw_defs pvr_fw_defs_mips = {
 	.irq_pending = pvr_mips_irq_pending,
 	.irq_clear = pvr_mips_irq_clear,
 	.has_fixed_data_addr = true,
+	.multi_dm_thread_assoc = false,
 };
diff --git a/drivers/gpu/drm/imagination/pvr_fw_riscv.c b/drivers/gpu/drm/imagination/pvr_fw_riscv.c
index 2b24c40bc20f..ef2801276252 100644
--- a/drivers/gpu/drm/imagination/pvr_fw_riscv.c
+++ b/drivers/gpu/drm/imagination/pvr_fw_riscv.c
@@ -182,4 +182,5 @@ const struct pvr_fw_defs pvr_fw_defs_riscv = {
 	.irq_pending = pvr_riscv_irq_pending,
 	.irq_clear = pvr_riscv_irq_clear,
 	.has_fixed_data_addr = false,
+	.multi_dm_thread_assoc = false,
 };
diff --git a/drivers/gpu/drm/imagination/pvr_fw_startstop.c b/drivers/gpu/drm/imagination/pvr_fw_startstop.c
index 98ca2692239d..703b9376cb9d 100644
--- a/drivers/gpu/drm/imagination/pvr_fw_startstop.c
+++ b/drivers/gpu/drm/imagination/pvr_fw_startstop.c
@@ -3,10 +3,8 @@
 
 #include "pvr_device.h"
 #include "pvr_fw.h"
-#include "pvr_fw_meta.h"
 #include "pvr_fw_startstop.h"
 #include "pvr_rogue_cr_defs.h"
-#include "pvr_rogue_meta.h"
 #include "pvr_vm.h"
 
 #include <linux/compiler.h>
@@ -199,6 +197,30 @@ pvr_fw_start(struct pvr_device *pvr_dev)
 	return err;
 }
 
+static inline bool layout_is_mars(struct pvr_device *pvr_dev)
+{
+	u64 layout_mars_value = 0;
+
+	if (PVR_FEATURE_VALUE(pvr_dev, layout_mars, &layout_mars_value))
+		return false;
+
+	return layout_mars_value > 0;
+}
+
+static inline int must_wait_fw_idle_before_stop(struct pvr_device *pvr_dev)
+{
+	bool wait_fw_idle = true;
+	int err;
+
+	if (pvr_dev->fw_dev.defs->must_wait_fw_idle) {
+		err = pvr_dev->fw_dev.defs->must_wait_fw_idle(pvr_dev, &wait_fw_idle);
+		if (err)
+			return err;
+	}
+
+	return wait_fw_idle;
+}
+
 /**
  * pvr_fw_stop() - Stop FW processor
  * @pvr_dev: Target PowerVR device.
@@ -214,15 +236,9 @@ pvr_fw_stop(struct pvr_device *pvr_dev)
 				       ~(ROGUE_CR_SIDEKICK_IDLE_GARTEN_EN |
 					 ROGUE_CR_SIDEKICK_IDLE_SOCIF_EN |
 					 ROGUE_CR_SIDEKICK_IDLE_HOSTIF_EN);
-	bool skip_garten_idle = false;
-	u64 layout_mars_value = 0;
-	bool layout_mars = false;
-	bool meta_fw = pvr_dev->fw_dev.processor_type == PVR_FW_PROCESSOR_TYPE_META;
-	u32 reg_value;
-	int err;
 
-	if (PVR_FEATURE_VALUE(pvr_dev, layout_mars, &layout_mars_value) == 0)
-		layout_mars = layout_mars_value > 0;
+	bool layout_mars = layout_is_mars(pvr_dev);
+	int err;
 
 	/*
 	 * For cores with the LAYOUT_MARS feature, SIDEKICK and SLC would have been
@@ -250,7 +266,7 @@ pvr_fw_stop(struct pvr_device *pvr_dev)
 		       ROGUE_CR_MTS_BGCTX_THREAD0_DM_ASSOC_MASKFULL &
 		       ROGUE_CR_MTS_BGCTX_THREAD0_DM_ASSOC_DM_ASSOC_CLRMSK);
 
-	if (meta_fw) {
+	if (pvr_dev->fw_dev.defs->multi_dm_thread_assoc) {
 		pvr_cr_write32(pvr_dev, ROGUE_CR_MTS_INTCTX_THREAD1_DM_ASSOC,
 			       ROGUE_CR_MTS_INTCTX_THREAD1_DM_ASSOC_MASKFULL &
 			       ROGUE_CR_MTS_INTCTX_THREAD1_DM_ASSOC_DM_ASSOC_CLRMSK);
@@ -297,6 +313,8 @@ pvr_fw_stop(struct pvr_device *pvr_dev)
 	 * powered down by the FW.
 	 */
 	if (!layout_mars) {
+		int wait_fw_idle;
+
 		/* Wait for SLC to signal IDLE. */
 		err = pvr_cr_poll_reg32(pvr_dev, ROGUE_CR_SLC_IDLE,
 					ROGUE_CR_SLC_IDLE_MASKFULL,
@@ -309,24 +327,12 @@ pvr_fw_stop(struct pvr_device *pvr_dev)
 					sidekick_idle_mask, POLL_TIMEOUT_USEC);
 		if (err)
 			return err;
-	}
 
-	if (meta_fw) {
-		err = pvr_meta_cr_read32(pvr_dev, META_CR_TxVECINT_BHALT, &reg_value);
-		if (err)
-			return err;
-
-		/*
-		 * Wait for Sidekick/Jones to signal IDLE including the Garten
-		 * Wrapper if there is no debugger attached (TxVECINT_BHALT =
-		 * 0x0).
-		 */
-		if (reg_value)
-			skip_garten_idle = true;
-	}
+		wait_fw_idle = must_wait_fw_idle_before_stop(pvr_dev);
+		if (wait_fw_idle < 0)
+			return wait_fw_idle;
 
-	if (meta_fw || !layout_mars) {
-		if (!skip_garten_idle) {
+		if (wait_fw_idle) {
 			err = pvr_cr_poll_reg32(pvr_dev, ROGUE_CR_SIDEKICK_IDLE,
 						ROGUE_CR_SIDEKICK_IDLE_GARTEN_EN,
 						ROGUE_CR_SIDEKICK_IDLE_GARTEN_EN,
@@ -346,7 +352,6 @@ pvr_fw_stop(struct pvr_device *pvr_dev)
 					ROGUE_CR_MARS_IDLE_CPU_EN |
 					ROGUE_CR_MARS_IDLE_MH_SYSARB0_EN,
 					POLL_TIMEOUT_USEC);
-
 		if (err)
 			return err;
 	}
diff --git a/drivers/gpu/drm/imagination/pvr_gem.h b/drivers/gpu/drm/imagination/pvr_gem.h
index ae7af82b6f17..e84be25f8576 100644
--- a/drivers/gpu/drm/imagination/pvr_gem.h
+++ b/drivers/gpu/drm/imagination/pvr_gem.h
@@ -5,7 +5,6 @@
 #define PVR_GEM_H
 
 #include "pvr_heap_config.h"
-#include "pvr_rogue_meta.h"
 
 #include <uapi/drm/pvr_drm.h>
 
diff --git a/drivers/gpu/drm/imagination/pvr_rogue_meta.h b/drivers/gpu/drm/imagination/pvr_rogue_meta_internal.h
similarity index 98%
rename from drivers/gpu/drm/imagination/pvr_rogue_meta.h
rename to drivers/gpu/drm/imagination/pvr_rogue_meta_internal.h
index 3020e6582daa..dd978988ed40 100644
--- a/drivers/gpu/drm/imagination/pvr_rogue_meta.h
+++ b/drivers/gpu/drm/imagination/pvr_rogue_meta_internal.h
@@ -1,8 +1,12 @@
 /* SPDX-License-Identifier: GPL-2.0-only OR MIT */
 /* Copyright (c) 2023 Imagination Technologies Ltd. */
 
-#ifndef PVR_ROGUE_META_H
-#define PVR_ROGUE_META_H
+#ifndef PVR_ROGUE_META_INTERNAL_H
+#define PVR_ROGUE_META_INTERNAL_H
+
+#ifndef PVR_ROGUE_META_INTERNAL_H_EXPLICIT_INCLUDE
+#error This file may only be included if explicitly defined
+#endif
 
 /***** The META HW register definitions in the file are updated manually *****/
 
@@ -353,4 +357,4 @@ struct rogue_meta_ldr_cfg_blk {
 
 #define ROGUE_FW_PROCESSOR_META "META"
 
-#endif /* PVR_ROGUE_META_H */
+#endif /* PVR_ROGUE_META_INTERNAL_H */

-- 
2.43.0




More information about the linux-riscv mailing list