[PATCH v2 15/34] media: iris: add handling for interrupt service routine(ISR) invoked by hardware

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Allocate interrupt resources, enable the interrupt line and IRQ handling.
Register the IRQ handler to be called when interrupt occurs and
the function to be called from IRQ handler thread.
The threads invoke the driver's response handler which handles
all different responses from firmware.

Signed-off-by: Dikshita Agarwal <quic_dikshita@xxxxxxxxxxx>
---
 drivers/media/platform/qcom/vcodec/iris/Makefile   |   1 +
 .../media/platform/qcom/vcodec/iris/hfi_defines.h  |  10 ++
 .../media/platform/qcom/vcodec/iris/iris_core.h    |   4 +
 drivers/media/platform/qcom/vcodec/iris/iris_hfi.c |  29 +++-
 drivers/media/platform/qcom/vcodec/iris/iris_hfi.h |   3 +
 .../platform/qcom/vcodec/iris/iris_hfi_response.c  | 184 +++++++++++++++++++++
 .../platform/qcom/vcodec/iris/iris_hfi_response.h  |  20 +++
 .../media/platform/qcom/vcodec/iris/iris_probe.c   |  27 +++
 .../media/platform/qcom/vcodec/iris/vpu_common.h   |   2 +
 .../media/platform/qcom/vcodec/iris/vpu_iris3.c    |  44 +++++
 10 files changed, 323 insertions(+), 1 deletion(-)
 create mode 100644 drivers/media/platform/qcom/vcodec/iris/iris_hfi_response.c
 create mode 100644 drivers/media/platform/qcom/vcodec/iris/iris_hfi_response.h

diff --git a/drivers/media/platform/qcom/vcodec/iris/Makefile b/drivers/media/platform/qcom/vcodec/iris/Makefile
index 3c076d0..85fdf63 100644
--- a/drivers/media/platform/qcom/vcodec/iris/Makefile
+++ b/drivers/media/platform/qcom/vcodec/iris/Makefile
@@ -8,6 +8,7 @@ iris-objs += iris_probe.o \
              iris_state.o \
              iris_helpers.o \
              iris_hfi.o \
+             iris_hfi_response.o \
              iris_hfi_packet.o \
              resources.o \
              vpu_common.o \
diff --git a/drivers/media/platform/qcom/vcodec/iris/hfi_defines.h b/drivers/media/platform/qcom/vcodec/iris/hfi_defines.h
index 423ba1a..9dd5f11 100644
--- a/drivers/media/platform/qcom/vcodec/iris/hfi_defines.h
+++ b/drivers/media/platform/qcom/vcodec/iris/hfi_defines.h
@@ -6,6 +6,8 @@
 #ifndef _HFI_DEFINES_H_
 #define _HFI_DEFINES_H_
 
+#include <linux/types.h>
+
 #define HFI_VIDEO_ARCH_LX			0x1
 
 #define HFI_CMD_INIT				0x01000001
@@ -50,4 +52,12 @@
 
 #define HFI_PROP_DEC_START_FROM_RAP_FRAME	0x03000169
 
+#define HFI_SYS_ERROR_WD_TIMEOUT		0x05000001
+
+struct hfi_debug_header {
+	u32   size;
+	u32   debug_level;
+	u32   reserved[2];
+};
+
 #endif
diff --git a/drivers/media/platform/qcom/vcodec/iris/iris_core.h b/drivers/media/platform/qcom/vcodec/iris/iris_core.h
index b47520a..c125bce 100644
--- a/drivers/media/platform/qcom/vcodec/iris/iris_core.h
+++ b/drivers/media/platform/qcom/vcodec/iris/iris_core.h
@@ -45,6 +45,7 @@
  * @use_tz: a flag that suggests presence of trustzone
  * @packet: pointer to packet from driver to fw
  * @packet_size: size of packet
+ * @response_packet: a pointer to response packet from fw to driver
  * @sys_init_id: id of sys init packet
  * @header_id: id of packet header
  * @packet_id: id of packet
@@ -52,6 +53,7 @@
  * @platform_data: a structure for platform data
  * @cap: an array for supported core capabilities
  * @instances: a list_head of all instances
+ * @intr_status: interrupt status
  */
 
 struct iris_core {
@@ -82,6 +84,7 @@ struct iris_core {
 	unsigned int				use_tz;
 	u8					*packet;
 	u32					packet_size;
+	u8					*response_packet;
 	u32					sys_init_id;
 	u32					header_id;
 	u32					packet_id;
@@ -89,6 +92,7 @@ struct iris_core {
 	struct platform_data			*platform_data;
 	struct plat_core_cap			cap[CORE_CAP_MAX + 1];
 	struct list_head			instances;
+	u32					intr_status;
 };
 
 int iris_core_init(struct iris_core *core);
diff --git a/drivers/media/platform/qcom/vcodec/iris/iris_hfi.c b/drivers/media/platform/qcom/vcodec/iris/iris_hfi.c
index d144ae5..4b8d8c7 100644
--- a/drivers/media/platform/qcom/vcodec/iris/iris_hfi.c
+++ b/drivers/media/platform/qcom/vcodec/iris/iris_hfi.c
@@ -5,10 +5,11 @@
 
 #include "../firmware.h"
 #include "../hfi_queue.h"
+#include "hfi_defines.h"
 #include "iris_helpers.h"
 #include "iris_hfi.h"
 #include "iris_hfi_packet.h"
-#include "hfi_defines.h"
+#include "iris_hfi_response.h"
 #include "vpu_common.h"
 
 static int iris_hfi_queue_cmd_write(struct iris_core *core, void *pkt)
@@ -253,3 +254,29 @@ int iris_hfi_session_close(struct iris_inst *inst)
 
 	return ret;
 }
+
+irqreturn_t iris_hfi_isr(int irq, void *data)
+{
+	disable_irq_nosync(irq);
+
+	return IRQ_WAKE_THREAD;
+}
+
+irqreturn_t iris_hfi_isr_handler(int irq, void *data)
+{
+	struct iris_core *core = data;
+
+	if (!core)
+		return IRQ_NONE;
+
+	mutex_lock(&core->lock);
+	call_vpu_op(core, clear_interrupt, core);
+	mutex_unlock(&core->lock);
+
+	__response_handler(core);
+
+	if (!call_vpu_op(core, watchdog, core, core->intr_status))
+		enable_irq(irq);
+
+	return IRQ_HANDLED;
+}
diff --git a/drivers/media/platform/qcom/vcodec/iris/iris_hfi.h b/drivers/media/platform/qcom/vcodec/iris/iris_hfi.h
index 8a057cc..8a62986 100644
--- a/drivers/media/platform/qcom/vcodec/iris/iris_hfi.h
+++ b/drivers/media/platform/qcom/vcodec/iris/iris_hfi.h
@@ -14,4 +14,7 @@ int iris_hfi_core_deinit(struct iris_core *core);
 int iris_hfi_session_open(struct iris_inst *inst);
 int iris_hfi_session_close(struct iris_inst *inst);
 
+irqreturn_t iris_hfi_isr(int irq, void *data);
+irqreturn_t iris_hfi_isr_handler(int irq, void *data);
+
 #endif
diff --git a/drivers/media/platform/qcom/vcodec/iris/iris_hfi_response.c b/drivers/media/platform/qcom/vcodec/iris/iris_hfi_response.c
new file mode 100644
index 0000000..829f3f6
--- /dev/null
+++ b/drivers/media/platform/qcom/vcodec/iris/iris_hfi_response.c
@@ -0,0 +1,184 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#include "hfi_defines.h"
+#include "iris_hfi_packet.h"
+#include "iris_hfi_response.h"
+
+static void print_sfr_message(struct iris_core *core)
+{
+	struct sfr_buffer *vsfr = NULL;
+	u32 vsfr_size = 0;
+	void *p = NULL;
+
+	vsfr = (struct sfr_buffer *)core->sfr.kernel_vaddr;
+	if (vsfr) {
+		if (vsfr->bufsize != core->sfr.size)
+			return;
+		vsfr_size = vsfr->bufsize - sizeof(u32);
+		p = memchr(vsfr->rg_data, '\0', vsfr_size);
+	/* SFR isn't guaranteed to be NULL terminated */
+		if (!p)
+			vsfr->rg_data[vsfr_size - 1] = '\0';
+	}
+}
+
+static int validate_packet(u8 *response_pkt, u8 *core_resp_pkt,
+			   u32 core_resp_pkt_size)
+{
+	u32 response_pkt_size = 0;
+	u8 *response_limit;
+
+	if (!response_pkt || !core_resp_pkt || !core_resp_pkt_size)
+		return -EINVAL;
+
+	response_limit = core_resp_pkt + core_resp_pkt_size;
+
+	if (response_pkt < core_resp_pkt || response_pkt > response_limit)
+		return -EINVAL;
+
+	response_pkt_size = *(u32 *)response_pkt;
+	if (!response_pkt_size)
+		return -EINVAL;
+
+	if (response_pkt_size < sizeof(struct hfi_packet))
+		return -EINVAL;
+
+	if (response_pkt + response_pkt_size > response_limit)
+		return -EINVAL;
+
+	return 0;
+}
+
+static int validate_hdr_packet(struct iris_core *core,
+			       struct hfi_header *hdr)
+{
+	struct hfi_packet *packet;
+	int i, ret = 0;
+	u8 *pkt;
+
+	if (hdr->size < sizeof(*hdr) + sizeof(*packet))
+		return -EINVAL;
+
+	pkt = (u8 *)((u8 *)hdr + sizeof(*hdr));
+
+	for (i = 0; i < hdr->num_packets; i++) {
+		packet = (struct hfi_packet *)pkt;
+		ret = validate_packet(pkt, core->response_packet, core->packet_size);
+		if (ret)
+			return ret;
+
+		pkt += packet->size;
+	}
+
+	return ret;
+}
+
+static int handle_system_error(struct iris_core *core,
+			       struct hfi_packet *pkt)
+{
+	print_sfr_message(core);
+
+	iris_core_deinit(core);
+
+	return 0;
+}
+
+static int handle_response(struct iris_core *core, void *response)
+{
+	struct hfi_header *hdr;
+	int ret;
+
+	hdr = (struct hfi_header *)response;
+	ret = validate_hdr_packet(core, hdr);
+	if (ret)
+		return handle_system_error(core, NULL);
+
+	return ret;
+}
+
+static int iris_hfi_queue_read(struct iris_core *core, void *pkt,
+			       struct iface_q_info *q_info)
+{
+	u32 tx_req;
+
+	if (!core_in_valid_state(core))
+		return -EINVAL;
+
+	if (!q_info || !q_info->q_array.kernel_vaddr || !pkt) {
+		dev_err(core->dev, "cannot read from shared Q's\n");
+		return -ENODATA;
+	}
+
+	if (read_queue(q_info, pkt, &tx_req))
+		return -ENODATA;
+
+	return 0;
+}
+
+int __response_handler(struct iris_core *core)
+{
+	int ret = 0;
+
+	if (call_vpu_op(core, watchdog, core, core->intr_status)) {
+		struct hfi_packet pkt = {.type = HFI_SYS_ERROR_WD_TIMEOUT};
+
+		mutex_lock(&core->lock);
+		iris_change_core_state(core, IRIS_CORE_ERROR);
+		dev_err(core->dev, "%s: CPU WD error received\n", __func__);
+		mutex_unlock(&core->lock);
+
+		return handle_system_error(core, &pkt);
+	}
+
+	memset(core->response_packet, 0, core->packet_size);
+	while (!iris_hfi_queue_read(core, core->response_packet, &core->message_queue)) {
+		ret = handle_response(core, core->response_packet);
+		if (ret)
+			continue;
+		if (core->state != IRIS_CORE_INIT)
+			break;
+		memset(core->response_packet, 0, core->packet_size);
+	}
+
+	iris_flush_debug_queue(core, core->response_packet, core->packet_size);
+
+	return ret;
+}
+
+void iris_flush_debug_queue(struct iris_core *core,
+			    u8 *packet, u32 packet_size)
+{
+	struct hfi_debug_header *pkt;
+	bool local_packet = false;
+	u8 *log;
+
+	if (!packet || !packet_size) {
+		packet = kzalloc(IFACEQ_CORE_PKT_SIZE, GFP_KERNEL);
+		if (!packet)
+			return;
+
+		packet_size = IFACEQ_CORE_PKT_SIZE;
+
+		local_packet = true;
+	}
+
+	while (!iris_hfi_queue_read(core, packet, &core->debug_queue)) {
+		pkt = (struct hfi_debug_header *)packet;
+
+		if (pkt->size < sizeof(*pkt))
+			continue;
+
+		if (pkt->size >= packet_size)
+			continue;
+
+		packet[pkt->size] = '\0';
+		log = (u8 *)packet + sizeof(*pkt) + 1;
+		dev_dbg(core->dev, "%s", log);
+	}
+
+	if (local_packet)
+		kfree(packet);
+}
diff --git a/drivers/media/platform/qcom/vcodec/iris/iris_hfi_response.h b/drivers/media/platform/qcom/vcodec/iris/iris_hfi_response.h
new file mode 100644
index 0000000..c1d2d27
--- /dev/null
+++ b/drivers/media/platform/qcom/vcodec/iris/iris_hfi_response.h
@@ -0,0 +1,20 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#ifndef _IRIS_HFI_RESPONSE_H_
+#define _IRIS_HFI_RESPONSE_H_
+
+#include "iris_core.h"
+
+struct sfr_buffer {
+	u32 bufsize;
+	u8 rg_data[];
+};
+
+int __response_handler(struct iris_core *core);
+void iris_flush_debug_queue(struct iris_core *core,
+			    u8 *packet, u32 packet_size);
+
+#endif
diff --git a/drivers/media/platform/qcom/vcodec/iris/iris_probe.c b/drivers/media/platform/qcom/vcodec/iris/iris_probe.c
index 4f20da8..8c591da 100644
--- a/drivers/media/platform/qcom/vcodec/iris/iris_probe.c
+++ b/drivers/media/platform/qcom/vcodec/iris/iris_probe.c
@@ -10,9 +10,25 @@
 #include "../hfi_queue.h"
 #include "iris_core.h"
 #include "iris_helpers.h"
+#include "iris_hfi.h"
 #include "resources.h"
 #include "iris_vidc.h"
 
+static int init_iris_isr(struct iris_core *core)
+{
+	int ret;
+
+	ret = devm_request_threaded_irq(core->dev, core->irq, iris_hfi_isr,
+					iris_hfi_isr_handler, IRQF_TRIGGER_HIGH, "iris", core);
+	if (ret) {
+		dev_err(core->dev, "%s: Failed to allocate iris IRQ\n", __func__);
+		return ret;
+	}
+	disable_irq_nosync(core->irq);
+
+	return ret;
+}
+
 static int iris_register_video_device(struct iris_core *core)
 {
 	struct video_device *vdev;
@@ -86,6 +102,10 @@ static int iris_probe(struct platform_device *pdev)
 	if (!core->packet)
 		return -ENOMEM;
 
+	core->response_packet = devm_kzalloc(core->dev, core->packet_size, GFP_KERNEL);
+	if (!core->response_packet)
+		return -ENOMEM;
+
 	INIT_LIST_HEAD(&core->instances);
 
 	core->reg_base = devm_platform_ioremap_resource(pdev, 0);
@@ -96,6 +116,13 @@ static int iris_probe(struct platform_device *pdev)
 	if (core->irq < 0)
 		return core->irq;
 
+	ret = init_iris_isr(core);
+	if (ret) {
+		dev_err_probe(core->dev, ret,
+			      "%s: Failed to init isr with %d\n", __func__, ret);
+		return ret;
+	}
+
 	ret = init_platform(core);
 	if (ret) {
 		dev_err_probe(core->dev, ret,
diff --git a/drivers/media/platform/qcom/vcodec/iris/vpu_common.h b/drivers/media/platform/qcom/vcodec/iris/vpu_common.h
index 790496a..6512039 100644
--- a/drivers/media/platform/qcom/vcodec/iris/vpu_common.h
+++ b/drivers/media/platform/qcom/vcodec/iris/vpu_common.h
@@ -22,6 +22,8 @@ struct compat_handle {
 struct vpu_ops {
 	int (*boot_firmware)(struct iris_core *core);
 	int (*raise_interrupt)(struct iris_core *core);
+	int (*clear_interrupt)(struct iris_core *core);
+	int (*watchdog)(struct iris_core *core, u32 intr_status);
 };
 
 int init_vpu(struct iris_core *core);
diff --git a/drivers/media/platform/qcom/vcodec/iris/vpu_iris3.c b/drivers/media/platform/qcom/vcodec/iris/vpu_iris3.c
index 95bf223..a34d0ed 100644
--- a/drivers/media/platform/qcom/vcodec/iris/vpu_iris3.c
+++ b/drivers/media/platform/qcom/vcodec/iris/vpu_iris3.c
@@ -17,6 +17,8 @@
 #define CPU_CS_VCICMDARG0_IRIS3     (CPU_CS_BASE_OFFS_IRIS3 + 0x24)
 #define CPU_CS_VCICMDARG1_IRIS3     (CPU_CS_BASE_OFFS_IRIS3 + 0x28)
 
+#define CPU_CS_A2HSOFTINTCLR_IRIS3  (CPU_CS_BASE_OFFS_IRIS3 + 0x1C)
+
 /* HFI_CTRL_INIT */
 #define CPU_CS_SCIACMD_IRIS3        (CPU_CS_BASE_OFFS_IRIS3 + 0x48)
 
@@ -57,10 +59,19 @@
 #define CPU_CS_SCIACMDARG0_HFI_CTRL_ERROR_STATUS_BMSK_IRIS3	0xfe
 #define CTRL_ERROR_STATUS__M_IRIS3 \
 		CPU_CS_SCIACMDARG0_HFI_CTRL_ERROR_STATUS_BMSK_IRIS3
+#define CTRL_INIT_IDLE_MSG_BMSK_IRIS3 \
+		CPU_CS_SCIACMDARG0_HFI_CTRL_INIT_IDLE_MSG_BMSK_IRIS3
+
+#define WRAPPER_BASE_OFFS_IRIS3		         0x000B0000
+#define WRAPPER_INTR_STATUS_IRIS3	         (WRAPPER_BASE_OFFS_IRIS3 + 0x0C)
+#define WRAPPER_INTR_STATUS_A2HWD_BMSK_IRIS3  0x8
+#define WRAPPER_INTR_STATUS_A2H_BMSK_IRIS3	  0x4
 
 #define CPU_IC_SOFTINT_IRIS3        (CPU_IC_BASE_OFFS_IRIS3 + 0x150)
 #define CPU_IC_SOFTINT_H2A_SHFT_IRIS3	0x0
 
+#define WRAPPER_INTR_STATUS_A2HWD_BMSK_IRIS3  0x8
+
 static int setup_ucregion_memory_map_iris3(struct iris_core *core)
 {
 	int ret;
@@ -153,9 +164,42 @@ static int raise_interrupt_iris3(struct iris_core *core)
 	return write_register(core, CPU_IC_SOFTINT_IRIS3, 1 << CPU_IC_SOFTINT_H2A_SHFT_IRIS3);
 }
 
+static int clear_interrupt_iris3(struct iris_core *core)
+{
+	u32 intr_status = 0, mask = 0;
+	int ret;
+
+	ret = read_register(core, WRAPPER_INTR_STATUS_IRIS3, &intr_status);
+	if (ret)
+		return ret;
+
+	mask = (WRAPPER_INTR_STATUS_A2H_BMSK_IRIS3 |
+		WRAPPER_INTR_STATUS_A2HWD_BMSK_IRIS3 |
+		CTRL_INIT_IDLE_MSG_BMSK_IRIS3);
+
+	if (intr_status & mask)
+		core->intr_status |= intr_status;
+
+	ret = write_register(core, CPU_CS_A2HSOFTINTCLR_IRIS3, 1);
+
+	return ret;
+}
+
+static int watchdog_iris3(struct iris_core *core, u32 intr_status)
+{
+	if (intr_status & WRAPPER_INTR_STATUS_A2HWD_BMSK_IRIS3) {
+		dev_err(core->dev, "%s: received watchdog interrupt\n", __func__);
+		return -ETIME;
+	}
+
+	return 0;
+}
+
 static const struct vpu_ops iris3_ops = {
 	.boot_firmware = boot_firmware_iris3,
 	.raise_interrupt = raise_interrupt_iris3,
+	.clear_interrupt = clear_interrupt_iris3,
+	.watchdog = watchdog_iris3,
 };
 
 int init_iris3(struct iris_core *core)
-- 
2.7.4





[Index of Archives]     [Linux Input]     [Video for Linux]     [Gstreamer Embedded]     [Mplayer Users]     [Linux USB Devel]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [Yosemite Backpacking]

  Powered by Linux