[PATCH 2/4] drm/amdgpu: Add psp interrupt handling

Alex Deucher <[email protected]>
Newsgroups org.freedesktop.lists.amd-gfx
Message-ID <[email protected]>
From: Lijo Lazar <[email protected]>

Add interrupt manager for handling psp interrupts and callbacks to
enable/disable psp interrupts.

Signed-off-by: Lijo Lazar <[email protected]>
Reviewed-by: Felix Kuehling <[email protected]>
Signed-off-by: Alex Deucher <[email protected]>
---
 drivers/gpu/drm/amd/amdgpu/amdgpu_ih.h  |   1 +
 drivers/gpu/drm/amd/amdgpu/amdgpu_irq.c |  18 ++++
 drivers/gpu/drm/amd/amdgpu/amdgpu_irq.h |   8 +-
 drivers/gpu/drm/amd/amdgpu/amdgpu_psp.c | 132 +++++++++++++++++++++++-
 drivers/gpu/drm/amd/amdgpu/amdgpu_psp.h |  43 ++++++++
 5 files changed, 198 insertions(+), 4 deletions(-)

diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ih.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_ih.h
index 444437c300886..11f834c97d937 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ih.h
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ih.h
@@ -29,6 +29,7 @@
 
 #define IH_RING_SIZE	(256 * 1024)
 #define IH_SW_RING_SIZE	(16 * 1024)	/* enough for 512 CAM entries */
+#define IH_PSP_RING_SIZE (SZ_4K)
 
 struct amdgpu_device;
 struct amdgpu_iv_entry;
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_irq.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_irq.c
index 95cceed4e971a..91f1ca95ea543 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_irq.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_irq.c
@@ -257,6 +257,14 @@ static void amdgpu_irq_handle_ih_soft(struct work_struct *work)
 	amdgpu_ih_process(adev, &adev->irq.ih_soft);
 }
 
+static void amdgpu_irq_handle_ih_psp(struct work_struct *work)
+{
+	struct amdgpu_device *adev = container_of(work, struct amdgpu_device,
+						  irq.ih_psp_work);
+
+	amdgpu_ih_process(adev, &adev->irq.ih_psp);
+}
+
 /**
  * amdgpu_msi_ok - check whether MSI functionality is enabled
  *
@@ -332,6 +340,7 @@ int amdgpu_irq_init(struct amdgpu_device *adev)
 	INIT_WORK(&adev->irq.ih1_work, amdgpu_irq_handle_ih1);
 	INIT_WORK(&adev->irq.ih2_work, amdgpu_irq_handle_ih2);
 	INIT_WORK(&adev->irq.ih_soft_work, amdgpu_irq_handle_ih_soft);
+	INIT_WORK(&adev->irq.ih_psp_work, amdgpu_irq_handle_ih_psp);
 
 	/* Use vector 0 for MSI-X. */
 	r = pci_irq_vector(adev->pdev, 0);
@@ -370,6 +379,7 @@ void amdgpu_irq_fini_hw(struct amdgpu_device *adev)
 	}
 
 	amdgpu_ih_ring_fini(adev, &adev->irq.ih_soft);
+	amdgpu_ih_ring_fini(adev, &adev->irq.ih_psp);
 	amdgpu_ih_ring_fini(adev, &adev->irq.ih);
 	amdgpu_ih_ring_fini(adev, &adev->irq.ih1);
 	amdgpu_ih_ring_fini(adev, &adev->irq.ih2);
@@ -548,6 +558,14 @@ void amdgpu_irq_delegate(struct amdgpu_device *adev,
 	queue_work(system_dfl_wq, &adev->irq.ih_soft_work);
 }
 
+void amdgpu_irq_psp_delegate(struct amdgpu_device *adev,
+			     struct amdgpu_iv_entry *entry,
+			     unsigned int num_dw)
+{
+	amdgpu_ih_ring_write(adev, &adev->irq.ih_psp, entry->iv_entry, num_dw);
+	schedule_work(&adev->irq.ih_psp_work);
+}
+
 /**
  * amdgpu_irq_update - update hardware interrupt state
  *
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_irq.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_irq.h
index af72405a72262..deee64b69885d 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_irq.h
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_irq.h
@@ -90,9 +90,10 @@ struct amdgpu_irq {
 	bool				msi_enabled; /* msi enabled */
 
 	/* interrupt rings */
-	struct amdgpu_ih_ring		ih, ih1, ih2, ih_soft;
+	struct amdgpu_ih_ring		ih, ih1, ih2, ih_soft, ih_psp;
 	const struct amdgpu_ih_funcs    *ih_funcs;
-	struct work_struct		ih1_work, ih2_work, ih_soft_work;
+	struct work_struct		ih1_work, ih2_work, ih_soft_work,
+					ih_psp_work;
 	struct amdgpu_irq_src		self_irq;
 
 	/* gen irq stuff */
@@ -134,6 +135,9 @@ void amdgpu_irq_dispatch(struct amdgpu_device *adev,
 void amdgpu_irq_delegate(struct amdgpu_device *adev,
 			 struct amdgpu_iv_entry *entry,
 			 unsigned int num_dw);
+void amdgpu_irq_psp_delegate(struct amdgpu_device *adev,
+			     struct amdgpu_iv_entry *entry,
+			     unsigned int num_dw);
 int amdgpu_irq_update(struct amdgpu_device *adev, struct amdgpu_irq_src *src,
 		      unsigned type);
 int amdgpu_irq_get(struct amdgpu_device *adev, struct amdgpu_irq_src *src,
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_psp.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_psp.c
index 55d293a2ee381..a0f6df5067e6e 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_psp.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_psp.c
@@ -48,6 +48,8 @@
 
 #define AMD_VBIOS_FILE_MAX_SIZE_B      (1024*1024*16)
 
+#define PSP_IRQ_DEFAULT_HANDLER_ID 0xFFFFFFFF
+
 static int psp_load_smu_fw(struct psp_context *psp);
 static int psp_rap_terminate(struct psp_context *psp);
 static int psp_securedisplay_terminate(struct psp_context *psp);
@@ -461,6 +463,128 @@ static bool psp_get_runtime_db_entry(struct amdgpu_device *adev,
 	return ret;
 }
 
+static int psp_register_irq_handler(struct amdgpu_psp_irq_mgr *mgr)
+{
+	struct psp_context *psp = mgr->psp;
+
+	if (!psp->funcs->register_irq_handler)
+		return 0;
+	return psp->funcs->register_irq_handler(mgr, &mgr->irq_src);
+}
+
+static int psp_irq_enable(struct psp_context *psp)
+{
+	if (!psp->irq_mgr.irq_src.funcs)
+		return 0;
+	dev_info(psp->adev->dev, "psp interrupt enabled");
+	return amdgpu_irq_get(psp->adev, &psp->irq_mgr.irq_src, 0);
+}
+
+static int psp_irq_disable(struct psp_context *psp)
+{
+	if (!psp->irq_mgr.irq_src.funcs)
+		return 0;
+	return amdgpu_irq_put(psp->adev, &psp->irq_mgr.irq_src, 0);
+}
+
+/**
+ * amdgpu_psp_irq_mgr_dispatch() - run the registered handler for a PSP event
+ * @mgr: IRQ manager
+ * @entry: IH entry from the PSP soft ring
+ *
+ * Invokes the handler for the entry's event id (or the default handler) in
+ * process context. Call from the .process callback for PSP soft ring entries.
+ */
+void amdgpu_psp_irq_mgr_dispatch(struct amdgpu_psp_irq_mgr *mgr,
+				 struct amdgpu_iv_entry *entry)
+{
+	struct amdgpu_psp_irq_handler *h;
+	amdgpu_psp_irq_handler_fn cb;
+	u32 event_id = entry->src_data[0];
+
+	cb = NULL;
+	xa_lock(&mgr->irq_bh_handlers);
+	h = xa_load(&mgr->irq_bh_handlers, (unsigned long)event_id);
+	if (h) {
+		cb = h->callback;
+	} else {
+		h = xa_load(&mgr->irq_bh_handlers, PSP_IRQ_DEFAULT_HANDLER_ID);
+		if (h) {
+			cb = h->callback;
+		} else {
+			dev_dbg(mgr->psp->adev->dev,
+				"PSP event: no handler for event_id:%x\n",
+				event_id);
+			xa_unlock(&mgr->irq_bh_handlers);
+			return;
+		}
+	}
+	xa_unlock(&mgr->irq_bh_handlers);
+
+	if (cb)
+		cb(mgr, event_id, entry);
+}
+
+static void psp_irq_mgr_init(struct psp_context *psp)
+{
+	struct amdgpu_psp_irq_mgr *mgr = &psp->irq_mgr;
+	int ret;
+
+	mgr->psp = psp;
+	xa_init(&mgr->irq_bh_handlers);
+
+	ret = psp_register_irq_handler(mgr);
+	if (ret)
+		dev_dbg(psp->adev->dev, "Failed to register IRQ handler!\n");
+}
+
+static void psp_irq_mgr_fini(struct amdgpu_psp_irq_mgr *mgr)
+{
+	xa_destroy(&mgr->irq_bh_handlers);
+}
+
+/**
+ * amdgpu_psp_irq_mgr_register() - register PSP interrupt handlers by event id
+ * @mgr: PSP interrupt manager
+ * @handlers: per-event handlers, or NULL
+ * @count: number of @handlers
+ * @default_handler: fallback for unregistered event ids, or NULL
+ *
+ * Handlers must be statically allocated and stay valid until psp_irq_mgr_fini().
+ *
+ * Return: 0 on success, negative error from xa_insert (e.g. duplicate event_id).
+ */
+int amdgpu_psp_irq_mgr_register(
+	struct amdgpu_psp_irq_mgr *mgr,
+	const struct amdgpu_psp_irq_handler *handlers, int count,
+	const struct amdgpu_psp_irq_handler *default_handler)
+{
+	int ret, i;
+
+	if (handlers) {
+		for (i = 0; i < count; i++) {
+			ret = xa_insert(&mgr->irq_bh_handlers,
+					(unsigned long)handlers[i].event_id,
+					(void *)&handlers[i], GFP_KERNEL);
+			if (ret)
+				dev_dbg(mgr->psp->adev->dev,
+					"PSP IRQ: handler already registered for event_id:%x\n",
+					handlers[i].event_id);
+		}
+	}
+
+	if (default_handler) {
+		ret = xa_insert(&mgr->irq_bh_handlers,
+				PSP_IRQ_DEFAULT_HANDLER_ID,
+				(void *)default_handler, GFP_KERNEL);
+		if (ret)
+			dev_dbg(mgr->psp->adev->dev,
+				"PSP IRQ: handler already registered for default handler");
+	}
+
+	return ret;
+}
+
 static int psp_sw_init(struct amdgpu_ip_block *ip_block)
 {
 	struct amdgpu_device *adev = ip_block->adev;
@@ -559,7 +683,7 @@ static int psp_sw_init(struct amdgpu_ip_block *ip_block)
 	/* Space for extended data in the tail of the cmd_buf allocation */
 	psp->cmd_ext_resp_mc_addr = psp->cmd_resp_buf_mc_addr + sizeof(struct psp_gfx_cmd_resp);
 	psp->cmd_ext_resp_mem = psp->cmd_resp_buf_mem + 1;
-
+	psp_irq_mgr_init(psp);
 	return 0;
 
 failed2:
@@ -576,6 +700,8 @@ static int psp_sw_fini(struct amdgpu_ip_block *ip_block)
 	struct amdgpu_device *adev = ip_block->adev;
 	struct psp_context *psp = &adev->psp;
 
+	psp_irq_mgr_fini(&psp->irq_mgr);
+
 	psp_memory_training_fini(psp);
 
 	amdgpu_ucode_release(&psp->sos_fw);
@@ -3063,6 +3189,7 @@ static int psp_hw_start(struct psp_context *psp)
 		}
 	}
 
+	psp_irq_enable(psp);
 	ret = psp_ring_create(psp, PSP_RING_TYPE__KM);
 	if (ret) {
 		dev_err(adev->dev, "PSP create ring failed!\n");
@@ -3742,6 +3869,7 @@ static int psp_hw_fini(struct amdgpu_ip_block *ip_block)
 	psp_asd_terminate(psp);
 	psp_tmr_terminate(psp);
 
+	psp_irq_disable(psp);
 	psp_ring_destroy(psp, PSP_RING_TYPE__KM);
 
 	return 0;
@@ -3801,7 +3929,7 @@ static int psp_suspend(struct amdgpu_ip_block *ip_block)
 		dev_err(adev->dev, "Failed to terminate tmr\n");
 		goto out;
 	}
-
+	psp_irq_disable(psp);
 	ret = psp_ring_stop(psp, PSP_RING_TYPE__KM);
 	if (ret)
 		dev_err(adev->dev, "PSP ring stop failed\n");
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_psp.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_psp.h
index e0ebce792021b..0aef9ee83bd0c 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_psp.h
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_psp.h
@@ -139,6 +139,8 @@ enum psp_reg_prog_id {
 #define PSP_WAITREG_CHANGED BIT(0) /* check if the value has changed */
 #define PSP_WAITREG_NOVERBOSE BIT(1) /* No error verbose */
 
+struct amdgpu_psp_irq_mgr;
+
 struct psp_funcs {
 	int (*init_microcode)(struct psp_context *psp);
 	int (*wait_for_bootloader)(struct psp_context *psp);
@@ -176,6 +178,8 @@ struct psp_funcs {
 				   enum psp_reg_prog_id id);
 	int (*get_fw_type)(struct amdgpu_firmware_info *ucode,
 			enum psp_gfx_fw_type *type);
+	int (*register_irq_handler)(struct amdgpu_psp_irq_mgr *mgr,
+				    struct amdgpu_irq_src *irq_src);
 };
 
 struct ta_funcs {
@@ -384,6 +388,37 @@ struct psp_ptl_perf_req {
 	uint32_t pref_format2;
 };
 
+/**
+ * typedef amdgpu_psp_irq_handler_fn - callback for a registered PSP event id
+ * @mgr: IRQ manager
+ * @entry: copy of the IH entry that triggered this event
+ */
+typedef void (*amdgpu_psp_irq_handler_fn)(struct amdgpu_psp_irq_mgr *mgr,
+					  u32 event_id,
+					  struct amdgpu_iv_entry *entry);
+
+/**
+ * struct amdgpu_psp_irq_handler - statically allocated handler node
+ *
+ * Callers define these statically; a single instance can be shared across
+ * multiple devices.  Indexed in &amdgpu_psp_irq_mgr.irq_bh_handlers by @event_id.
+ */
+struct amdgpu_psp_irq_handler {
+	u32				event_id;
+	amdgpu_psp_irq_handler_fn	callback;
+};
+
+/**
+ * struct amdgpu_psp_irq_mgr - deferred dispatch for PSP-driven IH events
+ *
+ * The xarray's own lock serializes handler lookup against registration.
+ */
+struct amdgpu_psp_irq_mgr {
+	struct psp_context		*psp;
+	struct amdgpu_irq_src		irq_src;
+	struct xarray			irq_bh_handlers;
+};
+
 struct psp_context {
 	struct amdgpu_device		*adev;
 	struct psp_ring			km_ring;
@@ -474,6 +509,7 @@ struct psp_context {
 	char				*vbflash_tmp_buf;
 	size_t				vbflash_image_size;
 	bool				vbflash_done;
+	struct amdgpu_psp_irq_mgr	irq_mgr;
 #if defined(CONFIG_DEBUG_FS)
 	struct spirom_bo *spirom_dump_trip;
 #endif
@@ -663,4 +699,11 @@ int amdgpu_psp_get_fw_type(struct amdgpu_firmware_info *ucode,
 int psp_set_mmhub_eco_sec_level(struct amdgpu_device *adev);
 int psp_init_rl_microcode(struct psp_context *psp, const char *chip_name);
 
+void amdgpu_psp_irq_mgr_dispatch(struct amdgpu_psp_irq_mgr *mgr,
+				 struct amdgpu_iv_entry *entry);
+int amdgpu_psp_irq_mgr_register(
+	struct amdgpu_psp_irq_mgr *mgr,
+	const struct amdgpu_psp_irq_handler *handlers, int count,
+	const struct amdgpu_psp_irq_handler *default_handler);
+
 #endif
-- 
2.55.0
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.