Browse Source

mm-drivers: hw-fence: add hardware fence driver validation ioctls

This change adds support to validate the hw_fence driver
by adding IOCTLs that expose the hw_fence interfaces so that
validation clients can register/unregister, create/destroy
and wait/signal fences. IOCTL's will be available for debug
purpose only when the debugfs config is set.

Change-Id: Idb0d04ee245718e9b19ccd12ac760829831426b0
Signed-off-by: Shirisha Kollapuram <[email protected]>
Shirisha Kollapuram 3 years ago
parent
commit
f73a4b179a

+ 2 - 0
hw_fence/Kbuild

@@ -14,5 +14,7 @@ msm_hw_fence-y := src/msm_hw_fence.o \
 		src/hw_fence_drv_debug.o \
 		src/hw_fence_drv_ipc.o
 
+msm_hw_fence-$(CONFIG_DEBUG_FS) += src/hw_fence_ioctl.o
+
 CDEFINES += -DBUILD_TIMESTAMP=\"$(shell date -u +'%Y-%m-%dT%H:%M:%SZ')\"
 endif

+ 133 - 0
hw_fence/include/hw_fence_drv_debug.h

@@ -6,6 +6,10 @@
 #ifndef __HW_FENCE_DRV_DEBUG
 #define __HW_FENCE_DRV_DEBUG
 
+#include "hw_fence_drv_ipc.h"
+
+#define HW_FENCE_NAME_SIZE 64
+
 enum hw_fence_drv_prio {
 	HW_FENCE_HIGH = 0x000001,	/* High density debug messages (noisy) */
 	HW_FENCE_LOW = 0x000002,	/* Low density debug messages */
@@ -58,4 +62,133 @@ extern u32 msm_hw_fence_debug_level;
 
 int hw_fence_debug_debugfs_register(struct hw_fence_driver_data *drv_data);
 
+#if IS_ENABLED(CONFIG_DEBUG_FS)
+
+int process_validation_client_loopback(struct hw_fence_driver_data *drv_data, int client_id);
+
+extern const struct file_operations hw_sync_debugfs_fops;
+
+struct hw_fence_out_clients_map {
+	int ipc_client_id; /* ipc client id for the hw fence client */
+	int ipc_signal_id; /* ipc signal id for the hw fence client */
+};
+
+/* These signals are the ones that the actual clients should be triggering, hw-fence driver
+ * does not need to have knowledge of these signals. Adding them here for debugging purposes.
+ * Only fence controller and the cliens know these id's, since these
+ * are to trigger the ipcc from the 'client hw-core' to the 'hw-fence controller'
+ * The index of this struct must match the enum hw_fence_client_id
+ */
+static const struct hw_fence_out_clients_map
+			dbg_out_clients_signal_map_no_dpu[HW_FENCE_CLIENT_MAX] = {
+	{HW_FENCE_IPC_CLIENT_ID_APPS, 0},  /* CTRL_LOOPBACK */
+	{HW_FENCE_IPC_CLIENT_ID_GPU, 0},  /* CTX0 */
+	{HW_FENCE_IPC_CLIENT_ID_APPS, 2},  /* CTL0 */
+	{HW_FENCE_IPC_CLIENT_ID_APPS, 4},  /* CTL1 */
+	{HW_FENCE_IPC_CLIENT_ID_APPS, 6},  /* CTL2 */
+	{HW_FENCE_IPC_CLIENT_ID_APPS, 8},  /* CTL3 */
+	{HW_FENCE_IPC_CLIENT_ID_APPS, 10}, /* CTL4 */
+	{HW_FENCE_IPC_CLIENT_ID_APPS, 12}, /* CTL5 */
+	{HW_FENCE_IPC_CLIENT_ID_APPS, 21}, /* VAL0 */
+	{HW_FENCE_IPC_CLIENT_ID_APPS, 22}, /* VAL1 */
+	{HW_FENCE_IPC_CLIENT_ID_APPS, 23}, /* VAL2 */
+	{HW_FENCE_IPC_CLIENT_ID_APPS, 24}, /* VAL3 */
+	{HW_FENCE_IPC_CLIENT_ID_APPS, 25}, /* VAL4 */
+	{HW_FENCE_IPC_CLIENT_ID_APPS, 26}, /* VAL5 */
+	{HW_FENCE_IPC_CLIENT_ID_APPS, 27}, /* VAL6 */
+};
+
+/**
+ * struct hw_dma_fence - fences created by hw-fence for debugging.
+ * @base: base dma-fence structure, this must remain at beginning of the struct.
+ * @name: name of each fence.
+ * @client_handle: handle for the client owner of this fence, this is returned by the hw-fence
+ *                 driver after a successful registration of the client and used by this fence
+ *                 during release.
+ */
+struct hw_dma_fence {
+	struct dma_fence base;
+	char name[HW_FENCE_NAME_SIZE];
+	void *client_handle;
+};
+
+static inline struct hw_dma_fence *to_hw_dma_fence(struct dma_fence *fence)
+{
+	return container_of(fence, struct hw_dma_fence, base);
+}
+
+static inline void _cleanup_fences(int i, struct dma_fence **fences, spinlock_t **fences_lock)
+{
+	struct hw_dma_fence *dma_fence;
+	int fence_idx;
+
+	for (fence_idx = i; fence_idx >= 0 ; fence_idx--) {
+		kfree(fences_lock[fence_idx]);
+
+		dma_fence = to_hw_dma_fence(fences[fence_idx]);
+		kfree(dma_fence);
+	}
+
+	kfree(fences_lock);
+	kfree(fences);
+}
+
+static const char *hw_fence_dbg_get_driver_name(struct dma_fence *fence)
+{
+	struct hw_dma_fence *hw_dma_fence = to_hw_dma_fence(fence);
+
+	return hw_dma_fence->name;
+}
+
+static const char *hw_fence_dbg_get_timeline_name(struct dma_fence *fence)
+{
+	struct hw_dma_fence *hw_dma_fence = to_hw_dma_fence(fence);
+
+	return hw_dma_fence->name;
+}
+
+static bool hw_fence_dbg_enable_signaling(struct dma_fence *fence)
+{
+	return true;
+}
+
+static void _hw_fence_release(struct hw_dma_fence *hw_dma_fence)
+{
+	if (IS_ERR_OR_NULL(hw_dma_fence->client_handle)) {
+		HWFNC_ERR("invalid hwfence data, won't release hw_fence!\n");
+		return;
+	}
+
+	/* release hw-fence */
+	if (msm_hw_fence_destroy(hw_dma_fence->client_handle, &hw_dma_fence->base))
+		HWFNC_ERR("failed to release hw_fence!\n");
+}
+
+static void hw_fence_dbg_release(struct dma_fence *fence)
+{
+	struct hw_dma_fence *hw_dma_fence;
+
+	if (!fence)
+		return;
+
+	HWFNC_DBG_H("release backing fence %pK\n", fence);
+	hw_dma_fence = to_hw_dma_fence(fence);
+
+	if (test_bit(MSM_HW_FENCE_FLAG_ENABLED_BIT, &fence->flags))
+		_hw_fence_release(hw_dma_fence);
+
+	kfree(fence->lock);
+	kfree(hw_dma_fence);
+}
+
+static struct dma_fence_ops hw_fence_dbg_ops = {
+	.get_driver_name = hw_fence_dbg_get_driver_name,
+	.get_timeline_name = hw_fence_dbg_get_timeline_name,
+	.enable_signaling = hw_fence_dbg_enable_signaling,
+	.wait = dma_fence_default_wait,
+	.release = hw_fence_dbg_release,
+};
+
+#endif /* CONFIG_DEBUG_FS */
+
 #endif /* __HW_FENCE_DRV_DEBUG */

+ 22 - 0
hw_fence/include/hw_fence_drv_priv.h

@@ -85,6 +85,13 @@ enum hw_fence_lookup_ops {
  * HW_FENCE_LOOPBACK_DPU_CTL_4: dpu client 4. Used in platforms with no dpu-ipc.
  * HW_FENCE_LOOPBACK_DPU_CTL_5: dpu client 5. Used in platforms with no dpu-ipc.
  * HW_FENCE_LOOPBACK_DPU_CTX_0: gfx client 0. Used in platforms with no gmu support.
+ * HW_FENCE_LOOPBACK_VAL_0: debug validation client 0.
+ * HW_FENCE_LOOPBACK_VAL_1: debug validation client 1.
+ * HW_FENCE_LOOPBACK_VAL_2: debug validation client 2.
+ * HW_FENCE_LOOPBACK_VAL_3: debug validation client 3.
+ * HW_FENCE_LOOPBACK_VAL_4: debug validation client 4.
+ * HW_FENCE_LOOPBACK_VAL_5: debug validation client 5.
+ * HW_FENCE_LOOPBACK_VAL_6: debug validation client 6.
  */
 enum hw_fence_loopback_id {
 	HW_FENCE_LOOPBACK_DPU_CTL_0,
@@ -94,6 +101,15 @@ enum hw_fence_loopback_id {
 	HW_FENCE_LOOPBACK_DPU_CTL_4,
 	HW_FENCE_LOOPBACK_DPU_CTL_5,
 	HW_FENCE_LOOPBACK_GFX_CTX_0,
+#if IS_ENABLED(CONFIG_DEBUG_FS)
+	HW_FENCE_LOOPBACK_VAL_0,
+	HW_FENCE_LOOPBACK_VAL_1,
+	HW_FENCE_LOOPBACK_VAL_2,
+	HW_FENCE_LOOPBACK_VAL_3,
+	HW_FENCE_LOOPBACK_VAL_4,
+	HW_FENCE_LOOPBACK_VAL_5,
+	HW_FENCE_LOOPBACK_VAL_6,
+#endif /* CONFIG_DEBUG_FS */
 	HW_FENCE_LOOPBACK_MAX,
 };
 
@@ -121,6 +137,8 @@ struct msm_hw_fence_queue {
  * @ipc_signal_id: id of the signal to be triggered for this client
  * @ipc_client_id: id of the ipc client for this hw fence driver client
  * @update_rxq: bool to indicate if client uses rx-queue
+ * @wait_queue: wait queue for the validation clients
+ * @val_signal: doorbell flag to signal the validation clients in the wait queue
  */
 struct msm_hw_fence_client {
 	enum hw_fence_client_id client_id;
@@ -129,6 +147,10 @@ struct msm_hw_fence_client {
 	int ipc_signal_id;
 	int ipc_client_id;
 	bool update_rxq;
+#if IS_ENABLED(CONFIG_DEBUG_FS)
+	wait_queue_head_t wait_queue;
+	atomic_t val_signal;
+#endif /* CONFIG_DEBUG_FS */
 };
 
 /**

+ 35 - 114
hw_fence/src/hw_fence_drv_debug.c

@@ -12,7 +12,6 @@
 #include "hw_fence_drv_ipc.h"
 #include "hw_fence_drv_utils.h"
 
-#define HW_FENCE_NAME_SIZE 64
 #define HW_FENCE_DEBUG_MAX_LOOPS 200
 
 u32 msm_hw_fence_debug_level = HW_FENCE_PRINTK;
@@ -38,20 +37,6 @@ struct client_data {
 	struct list_head list;
 };
 
-/**
- * struct hw_dma_fence - fences created by hw-fence for debugging.
- * @base: base dma-fence structure, this must remain at beginning of the struct.
- * @name: name of each fence.
- * @client_handle: handle for the client owner of this fence, this is returned by the hw-fence
- *                 driver after a successful registration of the client and used by this fence
- *                 during release.
- */
-struct hw_dma_fence {
-	struct dma_fence base;
-	char name[HW_FENCE_NAME_SIZE];
-	void *client_handle;
-};
-
 #if IS_ENABLED(CONFIG_DEBUG_FS)
 static int _get_debugfs_input_client(struct file *file,
 	const char __user *user_buf, size_t count, loff_t *ppos,
@@ -155,67 +140,6 @@ static const struct file_operations hw_fence_dbg_ipcc_fops = {
 	.write = hw_fence_dbg_ipcc_write,
 };
 
-static inline struct hw_dma_fence *to_hw_dma_fence(struct dma_fence *fence)
-{
-	return container_of(fence, struct hw_dma_fence, base);
-}
-
-static const char *hw_fence_dbg_get_driver_name(struct dma_fence *fence)
-{
-	struct hw_dma_fence *hw_dma_fence = to_hw_dma_fence(fence);
-
-	return hw_dma_fence->name;
-}
-
-static const char *hw_fence_dbg_get_timeline_name(struct dma_fence *fence)
-{
-	struct hw_dma_fence *hw_dma_fence = to_hw_dma_fence(fence);
-
-	return hw_dma_fence->name;
-}
-
-static bool hw_fence_dbg_enable_signaling(struct dma_fence *fence)
-{
-	return true;
-}
-
-static void _hw_fence_release(struct hw_dma_fence *hw_dma_fence)
-{
-	if (IS_ERR_OR_NULL(hw_dma_fence->client_handle)) {
-		HWFNC_ERR("invalid hwfence data, won't release hw_fence\n");
-		return;
-	}
-
-	/* release hw-fence */
-	if (msm_hw_fence_destroy(hw_dma_fence->client_handle, &hw_dma_fence->base))
-		HWFNC_ERR("failed to release hw_fence\n");
-}
-
-static void hw_fence_dbg_release(struct dma_fence *fence)
-{
-	struct hw_dma_fence *hw_dma_fence;
-
-	if (!fence)
-		return;
-
-	HWFNC_DBG_H("release backing fence %pK\n", fence);
-	hw_dma_fence = to_hw_dma_fence(fence);
-
-	if (test_bit(MSM_HW_FENCE_FLAG_ENABLED_BIT, &fence->flags))
-		_hw_fence_release(hw_dma_fence);
-
-	kfree(fence->lock);
-	kfree(hw_dma_fence);
-}
-
-static struct dma_fence_ops hw_fence_dbg_ops = {
-	.get_driver_name = hw_fence_dbg_get_driver_name,
-	.get_timeline_name = hw_fence_dbg_get_timeline_name,
-	.enable_signaling = hw_fence_dbg_enable_signaling,
-	.wait = dma_fence_default_wait,
-	.release = hw_fence_dbg_release,
-};
-
 struct client_data *_get_client_node(struct hw_fence_driver_data *drv_data, u32 client_id)
 {
 	struct client_data *node = NULL;
@@ -321,29 +245,6 @@ static ssize_t hw_fence_dbg_register_clients_wr(struct file *file,
 	return count;
 }
 
-struct hw_fence_out_clients_map {
-	int ipc_client_id; /* ipc client id for the hw fence client */
-	int ipc_signal_id; /* ipc signal id for the hw fence client */
-};
-
-/* NOTE: These signals are the ones that the actual clients should be triggering, hw-fence driver
- * does not need to have knowledge of these signals. Adding them here for debugging purposes.
- * Only fence controller and the cliens know these id's, since these
- * are to trigger the ipcc from the 'client hw-core' to the 'hw-fence controller'
- *
- * Note that the index of this struct must match the enum hw_fence_client_id
- */
-struct hw_fence_out_clients_map dbg_out_clients_signal_map_no_dpu[HW_FENCE_CLIENT_MAX] = {
-	{HW_FENCE_IPC_CLIENT_ID_APPS, 0},  /* CTRL_LOOPBACK */
-	{HW_FENCE_IPC_CLIENT_ID_GPU, 0},  /* CTX0 */
-	{HW_FENCE_IPC_CLIENT_ID_APPS, 2},  /* CTL0 */
-	{HW_FENCE_IPC_CLIENT_ID_APPS, 4},  /* CTL1 */
-	{HW_FENCE_IPC_CLIENT_ID_APPS, 6},  /* CTL2 */
-	{HW_FENCE_IPC_CLIENT_ID_APPS, 8},  /* CTL3 */
-	{HW_FENCE_IPC_CLIENT_ID_APPS, 10}, /* CTL4 */
-	{HW_FENCE_IPC_CLIENT_ID_APPS, 12} /* CTL5 */
-};
-
 /**
  * hw_fence_dbg_tx_and_signal_clients_wr() - debugfs write to simulate the lifecycle of a hw-fence.
  * @file: file handler.
@@ -761,21 +662,7 @@ static ssize_t hw_fence_dbg_dump_table_wr(struct file *file,
 	return user_buf_size;
 }
 
-static void _cleanup_fences(int i, struct dma_fence **fences, spinlock_t **fences_lock)
-{
-	struct hw_dma_fence *dma_fence;
-	int idx;
-
-	for (idx = i; idx >= 0 ; idx--) {
-		kfree(fences_lock[idx]);
-
-		dma_fence = to_hw_dma_fence(fences[idx]);
-		kfree(dma_fence);
-	}
 
-	kfree(fences_lock);
-	kfree(fences);
-}
 
 /**
  * hw_fence_dbg_create_join_fence() - debugfs write to simulate the lifecycle of a join hw-fence.
@@ -840,7 +727,7 @@ static ssize_t hw_fence_dbg_create_join_fence(struct file *file,
 	for (i = 0; i < num_fences; i++) {
 		struct hw_dma_fence *dma_fence;
 
-		fences_lock[i] = kzalloc(sizeof(*fences_lock), GFP_KERNEL);
+		fences_lock[i] = kzalloc(sizeof(spinlock_t), GFP_KERNEL);
 		if (!fences_lock[i]) {
 			_cleanup_fences(i, fences, fences_lock);
 			return -ENOMEM;
@@ -916,6 +803,39 @@ error:
 	return count;
 }
 
+int process_validation_client_loopback(struct hw_fence_driver_data *drv_data,
+		int client_id)
+{
+	struct msm_hw_fence_client *hw_fence_client;
+
+	if (client_id < HW_FENCE_LOOPBACK_VAL_0 || client_id > HW_FENCE_LOOPBACK_VAL_6) {
+		HWFNC_ERR("invalid client_id: %d min: %d max: %d\n", client_id,
+				HW_FENCE_LOOPBACK_VAL_0, HW_FENCE_LOOPBACK_VAL_6);
+		return -EINVAL;
+	}
+
+	mutex_lock(&drv_data->clients_mask_lock);
+
+	if (!drv_data->clients[client_id]) {
+		mutex_unlock(&drv_data->clients_mask_lock);
+		return -EINVAL;
+	}
+
+	hw_fence_client = drv_data->clients[client_id];
+
+	HWFNC_DBG_IRQ("Processing validation client workaround client_id:%d\n", client_id);
+
+	/* set the atomic flag, to signal the client wait */
+	atomic_set(&hw_fence_client->val_signal, 1);
+
+	/* wake-up waiting client */
+	wake_up_all(&hw_fence_client->wait_queue);
+
+	mutex_unlock(&drv_data->clients_mask_lock);
+
+	return 0;
+}
+
 static const struct file_operations hw_fence_reset_client_fops = {
 	.open = simple_open,
 	.write = hw_fence_dbg_reset_client_wr,
@@ -988,6 +908,7 @@ int hw_fence_debug_debugfs_register(struct hw_fence_driver_data *drv_data)
 	debugfs_create_u32("hw_fence_debug_level", 0600, debugfs_root, &msm_hw_fence_debug_level);
 	debugfs_create_file("hw_fence_dump_table", 0600, debugfs_root, drv_data,
 		&hw_fence_dump_table_fops);
+	debugfs_create_file("hw_sync", 0600, debugfs_root, NULL, &hw_sync_debugfs_fops);
 
 	return 0;
 }

+ 18 - 0
hw_fence/src/hw_fence_drv_ipc.c

@@ -40,6 +40,15 @@ struct hw_fence_client_ipc_map hw_fence_clients_ipc_map_no_dpu[HW_FENCE_CLIENT_M
 	{HW_FENCE_IPC_CLIENT_ID_APPS, 17, false}, /* ctl3 */
 	{HW_FENCE_IPC_CLIENT_ID_APPS, 18, false}, /* ctl4 */
 	{HW_FENCE_IPC_CLIENT_ID_APPS, 19, false}, /* ctl5 */
+#if IS_ENABLED(CONFIG_DEBUG_FS)
+	{HW_FENCE_IPC_CLIENT_ID_APPS, 21, true}, /* val0 */
+	{HW_FENCE_IPC_CLIENT_ID_APPS, 22, true}, /* val1 */
+	{HW_FENCE_IPC_CLIENT_ID_APPS, 23, true}, /* val2 */
+	{HW_FENCE_IPC_CLIENT_ID_APPS, 24, true}, /* val3 */
+	{HW_FENCE_IPC_CLIENT_ID_APPS, 25, true}, /* val4 */
+	{HW_FENCE_IPC_CLIENT_ID_APPS, 26, true}, /* val5 */
+	{HW_FENCE_IPC_CLIENT_ID_APPS, 27, true}, /* val6 */
+#endif /* CONFIG_DEBUG_FS */
 };
 
 /**
@@ -59,6 +68,15 @@ struct hw_fence_client_ipc_map hw_fence_clients_ipc_map[HW_FENCE_CLIENT_MAX] = {
 	{HW_FENCE_IPC_CLIENT_ID_DPU,  3, false}, /* ctl3 */
 	{HW_FENCE_IPC_CLIENT_ID_DPU,  4, false}, /* ctl4 */
 	{HW_FENCE_IPC_CLIENT_ID_DPU,  5, false}, /* ctl5 */
+#if IS_ENABLED(CONFIG_DEBUG_FS)
+	{HW_FENCE_IPC_CLIENT_ID_APPS, 21, true}, /* val0 */
+	{HW_FENCE_IPC_CLIENT_ID_APPS, 22, true}, /* val1 */
+	{HW_FENCE_IPC_CLIENT_ID_APPS, 23, true}, /* val2 */
+	{HW_FENCE_IPC_CLIENT_ID_APPS, 24, true}, /* val3 */
+	{HW_FENCE_IPC_CLIENT_ID_APPS, 25, true}, /* val4 */
+	{HW_FENCE_IPC_CLIENT_ID_APPS, 26, true}, /* val5 */
+	{HW_FENCE_IPC_CLIENT_ID_APPS, 27, true}, /* val6 */
+#endif /* CONFIG_DEBUG_FS */
 };
 
 int hw_fence_ipcc_get_client_id(struct hw_fence_driver_data *drv_data, u32 client_id)

+ 11 - 0
hw_fence/src/hw_fence_drv_priv.c

@@ -539,6 +539,17 @@ int hw_fence_init_controller_signal(struct hw_fence_driver_data *drv_data,
 	case HW_FENCE_CLIENT_ID_CTX0:
 		/* nothing to initialize for gpu client */
 		break;
+#if IS_ENABLED(CONFIG_DEBUG_FS)
+	case HW_FENCE_CLIENT_ID_VAL0:
+	case HW_FENCE_CLIENT_ID_VAL1:
+	case HW_FENCE_CLIENT_ID_VAL2:
+	case HW_FENCE_CLIENT_ID_VAL3:
+	case HW_FENCE_CLIENT_ID_VAL4:
+	case HW_FENCE_CLIENT_ID_VAL5:
+	case HW_FENCE_CLIENT_ID_VAL6:
+		/* nothing to initialize for validation clients */
+		break;
+#endif /* CONFIG_DEBUG_FS */
 	case HW_FENCE_CLIENT_ID_CTL0:
 	case HW_FENCE_CLIENT_ID_CTL1:
 	case HW_FENCE_CLIENT_ID_CTL2:

+ 11 - 0
hw_fence/src/hw_fence_drv_utils.c

@@ -134,6 +134,17 @@ static int _process_doorbell_client(struct hw_fence_driver_data *drv_data, int c
 	case HW_FENCE_LOOPBACK_GFX_CTX_0:
 		ret = _process_gfx_client_loopback(drv_data, client_id);
 		break;
+#if IS_ENABLED(CONFIG_DEBUG_FS)
+	case HW_FENCE_LOOPBACK_VAL_0:
+	case HW_FENCE_LOOPBACK_VAL_1:
+	case HW_FENCE_LOOPBACK_VAL_2:
+	case HW_FENCE_LOOPBACK_VAL_3:
+	case HW_FENCE_LOOPBACK_VAL_4:
+	case HW_FENCE_LOOPBACK_VAL_5:
+	case HW_FENCE_LOOPBACK_VAL_6:
+		ret = process_validation_client_loopback(drv_data, client_id);
+		break;
+#endif /* CONFIG_DEBUG_FS */
 	default:
 		HWFNC_ERR("unknown client:%d\n", client_id);
 		ret = -EINVAL;

+ 711 - 0
hw_fence/src/hw_fence_ioctl.c

@@ -0,0 +1,711 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#include <linux/file.h>
+#include <linux/fs.h>
+#include <linux/ioctl.h>
+#include <linux/types.h>
+#include <linux/sync_file.h>
+
+#include "hw_fence_drv_priv.h"
+#include "hw_fence_drv_utils.h"
+#include "hw_fence_drv_ipc.h"
+#include "hw_fence_drv_debug.h"
+
+#define HW_SYNC_IOCTL_COUNT		ARRAY_SIZE(hw_sync_debugfs_ioctls)
+#define HW_FENCE_ARRAY_SIZE		10
+#define HW_SYNC_IOC_MAGIC		'W'
+#define HW_SYNC_IOC_REG_CLIENT	_IOWR(HW_SYNC_IOC_MAGIC, 10, unsigned long)
+#define HW_SYNC_IOC_UNREG_CLIENT	_IOWR(HW_SYNC_IOC_MAGIC, 11, unsigned long)
+#define HW_SYNC_IOC_CREATE_FENCE	_IOWR(HW_SYNC_IOC_MAGIC, 12,\
+						struct hw_fence_sync_create_data)
+#define HW_SYNC_IOC_DESTROY_FENCE	_IOWR(HW_SYNC_IOC_MAGIC, 13,\
+						struct hw_fence_sync_create_data)
+#define HW_SYNC_IOC_CREATE_FENCE_ARRAY	_IOWR(HW_SYNC_IOC_MAGIC, 14,\
+						struct hw_fence_array_sync_create_data)
+#define HW_SYNC_IOC_DESTROY_FENCE_ARRAY	_IOWR(HW_SYNC_IOC_MAGIC, 15,\
+						struct hw_fence_array_sync_create_data)
+#define HW_SYNC_IOC_REG_FOR_WAIT	_IOWR(HW_SYNC_IOC_MAGIC, 16, int)
+#define HW_SYNC_IOC_FENCE_SIGNAL	_IOWR(HW_SYNC_IOC_MAGIC, 17, unsigned long)
+#define HW_SYNC_IOC_FENCE_WAIT	_IOWR(HW_SYNC_IOC_MAGIC, 18, int)
+#define HW_SYNC_IOC_RESET_CLIENT	_IOWR(HW_SYNC_IOC_MAGIC, 19, unsigned long)
+#define HW_FENCE_IOCTL_NR(n)			(_IOC_NR(n) - 2)
+#define HW_IOCTL_DEF(ioctl, _func)	\
+	[HW_FENCE_IOCTL_NR(ioctl)] = {		\
+		.cmd = ioctl,			\
+		.func = _func,			\
+		.name = #ioctl			\
+	}
+
+/**
+ * struct hw_sync_obj - per client hw sync object.
+ * @context: context id used to create fences.
+ * @client_id: to uniquely represent client.
+ * @client_handle: Pointer to the structure holding the resources
+ *                 allocated to the client.
+ * @mem_descriptor: Memory descriptor of the queue allocated by the
+ *                  hardware fence driver for each client during register.
+ */
+struct hw_sync_obj {
+	u64 context;
+	int client_id;
+	void *client_handle;
+	struct msm_hw_fence_mem_addr mem_descriptor;
+};
+
+/**
+ * struct hw_fence_sync_create_data - data used in creating fences.
+ * @seqno: sequence number.
+ * @incr_context: if set, then the context would be incremented.
+ * @fence: returns the fd of the new sync_file with the created fence.
+ * @hash: fence hash
+ */
+struct hw_fence_sync_create_data {
+	u64 seqno;
+	bool incr_context;
+	__s32 fence;
+	u64 hash;
+};
+
+/**
+ * struct hw_fence_array_sync_create_data - data used in creating multiple fences.
+ * @seqno: array of sequence numbers used to create fences.
+ * @num_fences: number of fences to be created.
+ * @fence: return the fd of the new sync_file with the created fence.
+ * @hash: array of fence hash
+ */
+struct hw_fence_array_sync_create_data {
+	u64 seqno[HW_FENCE_ARRAY_SIZE];
+	int num_fences;
+	__s32 fence;
+	u64 hash[HW_FENCE_ARRAY_SIZE];
+};
+
+/**
+ * struct hw_fence_sync_signal_data - data used to signal fences.
+ * @hash: hash of the fence.
+ * @error_flag: error flag
+ */
+struct hw_fence_sync_signal_data {
+	u64 hash;
+	u32 error_flag;
+};
+
+/**
+ * struct hw_fence_sync_wait_data - data used to wait on fences.
+ * @fence: fence fd.
+ * @timeout_ms: fence wait time out.
+ */
+struct hw_fence_sync_wait_data {
+	__s32 fence;
+	u64 timeout_ms;
+};
+
+/**
+ * struct hw_fence_sync_reset_data - data used to reset client.
+ * @client_id: client id.
+ * @reset_flag: reset flag
+ */
+struct hw_fence_sync_reset_data {
+	int client_id;
+	u32 reset_flag;
+};
+
+typedef long hw_fence_ioctl_t(struct hw_sync_obj *obj, unsigned long arg);
+
+/**
+ * struct hw_sync_ioctl_def - hw_sync driver ioctl entry
+ * @cmd: ioctl command number, without flags
+ * @func: handler for this ioctl
+ * @name: user-readable name for debug output
+ */
+struct hw_sync_ioctl_def {
+	unsigned int cmd;
+	hw_fence_ioctl_t *func;
+	const char *name;
+};
+
+static bool _is_valid_client(struct hw_sync_obj *obj)
+{
+	if (!obj)
+		return false;
+
+	if (obj->client_id < HW_FENCE_CLIENT_ID_VAL0 || obj->client_id >= HW_FENCE_CLIENT_MAX) {
+		HWFNC_ERR("invalid client_id:%d min:%d max:%d\n", obj->client_id,
+				HW_FENCE_CLIENT_ID_VAL0, HW_FENCE_CLIENT_MAX);
+		return false;
+	}
+
+	return true;
+}
+
+static int _get_client_id(struct hw_sync_obj *obj, unsigned long arg)
+{
+	int client_id;
+
+	if (copy_from_user(&client_id, (void __user *)arg, sizeof(client_id)))
+		return -EFAULT;
+
+	if (!obj)
+		return -EINVAL;
+
+	if (client_id < HW_FENCE_CLIENT_ID_VAL0 || client_id >= HW_FENCE_CLIENT_MAX) {
+		HWFNC_ERR("invalid client_id:%d min:%d max:%d\n", client_id,
+				HW_FENCE_CLIENT_ID_VAL0, HW_FENCE_CLIENT_MAX);
+		return -EINVAL;
+	}
+
+	return client_id;
+}
+
+static void *_hw_sync_get_fence(int fd)
+{
+	return fd >= 0 ? sync_file_get_fence(fd) : NULL;
+}
+
+static int hw_sync_debugfs_open(struct inode *inode, struct file *file)
+{
+	struct hw_sync_obj *obj;
+
+	obj = kzalloc(sizeof(*obj), GFP_KERNEL);
+	if (!obj)
+		return -ENOMEM;
+
+	obj->context = dma_fence_context_alloc(1);
+	file->private_data = obj;
+
+	return 0;
+}
+
+static int hw_sync_debugfs_release(struct inode *inode, struct file *file)
+{
+	struct hw_sync_obj *obj = file->private_data;
+
+	if (!obj)
+		return -EINVAL;
+
+	kfree(obj);
+
+	return 0;
+}
+
+static long hw_sync_ioctl_reg_client(struct hw_sync_obj *obj, unsigned long arg)
+{
+	int client_id = _get_client_id(obj, arg);
+
+	if (IS_ERR(&client_id)) {
+		return client_id;
+	} else if (obj->client_handle) {
+		HWFNC_ERR("client:%d already registered as validation client\n", client_id);
+		return -EINVAL;
+	}
+
+	obj->client_id = client_id;
+	obj->client_handle = msm_hw_fence_register(obj->client_id, &obj->mem_descriptor);
+	if (IS_ERR_OR_NULL(obj->client_handle))
+		return -EINVAL;
+
+	return 0;
+}
+
+static long hw_sync_ioctl_unreg_client(struct hw_sync_obj *obj, unsigned long arg)
+{
+	int client_id = _get_client_id(obj, arg);
+
+	if (IS_ERR(&client_id))
+		return client_id;
+
+	return msm_hw_fence_deregister(obj->client_handle);
+}
+
+static long hw_sync_ioctl_create_fence(struct hw_sync_obj *obj, unsigned long arg)
+{
+	struct msm_hw_fence_create_params params;
+	struct hw_fence_sync_create_data data;
+	struct hw_dma_fence *fence;
+	spinlock_t *fence_lock;
+	u64 hash;
+	struct sync_file *sync_file;
+	int fd, ret;
+
+	if (!_is_valid_client(obj)) {
+		return -EINVAL;
+	} else if (IS_ERR_OR_NULL(obj->client_handle)) {
+		HWFNC_ERR("client:%d is not register as validation client\n", obj->client_id);
+		return -EINVAL;
+	}
+
+	if (copy_from_user(&data, (void __user *)arg, sizeof(data)))
+		return -EFAULT;
+
+	/* create dma fence */
+	fence_lock = kzalloc(sizeof(*fence_lock), GFP_KERNEL);
+	if (!fence_lock)
+		return -ENOMEM;
+
+	fence = kzalloc(sizeof(*fence), GFP_KERNEL);
+	if (!fence) {
+		kfree(fence_lock);
+		return -ENOMEM;
+	}
+
+	snprintf(fence->name, HW_FENCE_NAME_SIZE, "hwfence:id:%d:ctx=%lu:seqno:%lu",
+			obj->client_id, obj->context, data.seqno);
+
+	spin_lock_init(fence_lock);
+	dma_fence_init(&fence->base, &hw_fence_dbg_ops, fence_lock, obj->context, data.seqno);
+
+	HWFNC_DBG_H("creating hw_fence for client:%d ctx:%llu seqno:%llu\n", obj->client_id,
+				obj->context, data.seqno);
+	params.fence = &fence->base;
+	params.handle = &hash;
+
+	/* create hw fence */
+	ret = msm_hw_fence_create(obj->client_handle, &params);
+	if (ret) {
+		HWFNC_ERR("failed to create hw_fence for client:%d ctx:%llu seqno:%llu\n",
+			obj->client_id, obj->context, data.seqno);
+		dma_fence_put(&fence->base);
+		return -EINVAL;
+	}
+
+	/* keep handle in dma_fence, to destroy hw-fence during release */
+	fence->client_handle = obj->client_handle;
+
+	if (data.incr_context)
+		obj->context = dma_fence_context_alloc(1);
+
+	/* create fd */
+	fd = get_unused_fd_flags(0);
+	if (fd < 0) {
+		HWFNC_ERR("failed to get fd for client:%d\n", obj->client_id);
+		dma_fence_put(&fence->base);
+		return fd;
+	}
+
+	sync_file = sync_file_create(&fence->base);
+	if (sync_file == NULL) {
+		HWFNC_ERR("couldn't create fence fd, %d\n", fd);
+		dma_fence_put(&fence->base);
+		ret = -EINVAL;
+		goto exit;
+	}
+
+	/* Decrement the refcount that sync_file_create increments */
+	dma_fence_put(&fence->base);
+
+	data.fence = fd;
+	data.hash = hash;
+	if (copy_to_user((void __user *)arg, &data, sizeof(data))) {
+		dma_fence_put(&fence->base);
+		fput(sync_file->file);
+		ret = -EFAULT;
+		goto exit;
+	}
+
+	fd_install(fd, sync_file->file);
+
+	return 0;
+
+exit:
+	put_unused_fd(fd);
+	return ret;
+}
+
+static long hw_sync_ioctl_destroy_fence(struct hw_sync_obj *obj, unsigned long arg)
+{
+	int fd;
+	struct hw_dma_fence *fence;
+	struct hw_fence_sync_create_data data;
+
+	if (!_is_valid_client(obj))
+		return -EINVAL;
+
+	if (copy_from_user(&data, (void __user *)arg, sizeof(data)))
+		return -EFAULT;
+
+	fd = data.fence;
+	fence = (struct hw_dma_fence *)_hw_sync_get_fence(fd);
+
+	if (!fence) {
+		HWFNC_ERR("fence for fd:%d not found\n", fd);
+		return -EINVAL;
+	}
+
+	/* Decrement the refcount that hw_sync_get_fence increments */
+	dma_fence_put(&fence->base);
+
+	/* To destroy fence */
+	dma_fence_put(&fence->base);
+
+	return 0;
+}
+
+static long hw_sync_ioctl_create_fence_array(struct hw_sync_obj *obj, unsigned long arg)
+{
+	struct dma_fence_array *fence_array;
+	struct hw_fence_array_sync_create_data data;
+	struct dma_fence **fences = NULL;
+	struct msm_hw_fence_create_params params;
+	struct sync_file *sync_file;
+	spinlock_t **fence_lock = NULL;
+	int num_fences, i, fd, ret;
+	u64 hash;
+
+	if (!_is_valid_client(obj)) {
+		return -EINVAL;
+	} else if (IS_ERR_OR_NULL(obj->client_handle)) {
+		HWFNC_ERR("client:%d is not register as validation client\n", obj->client_id);
+		return -EINVAL;
+	}
+
+	if (copy_from_user(&data, (void __user *)arg, sizeof(data)))
+		return -EFAULT;
+
+	num_fences = data.num_fences;
+	if (num_fences >= HW_FENCE_ARRAY_SIZE) {
+		HWFNC_ERR("Number of fences: %d is greater than allowed size: %d\n",
+					num_fences, HW_FENCE_ARRAY_SIZE);
+		return -EINVAL;
+	}
+	fence_lock = kcalloc(num_fences, sizeof(*fence_lock), GFP_KERNEL);
+	if (!fence_lock)
+		return -ENOMEM;
+
+	fences = kcalloc(num_fences, sizeof(*fences), GFP_KERNEL);
+	if (!fences) {
+		kfree(fence_lock);
+		return -ENOMEM;
+	}
+
+	/*
+	 * Create the array of dma fences
+	 * This API takes seqno[num_fences] as the seqno for the fence-array
+	 * and from 0 to (num_fences - 1) for the fences in the array.
+	 */
+	for (i = 0; i < num_fences; i++) {
+		struct hw_dma_fence *dma_fence;
+
+		fence_lock[i] = kzalloc(sizeof(spinlock_t), GFP_KERNEL);
+		if (!fence_lock[i]) {
+			_cleanup_fences(i, fences, fence_lock);
+			return -ENOMEM;
+		}
+
+		dma_fence = kzalloc(sizeof(*dma_fence), GFP_KERNEL);
+		if (!dma_fence) {
+			_cleanup_fences(i, fences, fence_lock);
+			return -ENOMEM;
+		}
+		fences[i] = &dma_fence->base;
+
+		spin_lock_init(fence_lock[i]);
+		dma_fence_init(fences[i], &hw_fence_dbg_ops, fence_lock[i],
+			obj->context, data.seqno[i]);
+	}
+
+	/* create the fence array from array of dma fences */
+	fence_array = dma_fence_array_create(num_fences, fences, obj->context, data.seqno[i], 0);
+	if (!fence_array) {
+		HWFNC_ERR("Error creating fence_array\n");
+		_cleanup_fences(num_fences - 1, fences, fence_lock);
+		return -EINVAL;
+	}
+
+	/* create hw fences */
+	for (i = 0; i < num_fences; i++) {
+		params.fence = fences[i];
+		params.handle = &hash;
+
+		ret = msm_hw_fence_create(obj->client_handle, &params);
+		if (ret) {
+			HWFNC_ERR("Error creating HW fence\n");
+			dma_fence_put(&fence_array->base);
+			/*
+			 * free array of pointers, no need to call kfree in 'fences',
+			 * since that is released from the fence-array release api
+			 */
+			kfree(fence_lock);
+			kfree(fence_array);
+			return -EINVAL;
+		}
+
+		/* keep handle in dma_fence, to destroy hw-fence during release */
+		to_hw_dma_fence(fences[i])->client_handle = obj->client_handle;
+		data.hash[i] = hash;
+	}
+
+	/* create fd */
+	fd = get_unused_fd_flags(0);
+	if (fd < 0) {
+		HWFNC_ERR("failed to get fd for client:%d\n", obj->client_id);
+		dma_fence_put(&fence_array->base);
+		kfree(fence_lock);
+		kfree(fence_array);
+		return fd;
+	}
+
+	sync_file = sync_file_create(&fence_array->base);
+	if (sync_file == NULL) {
+		HWFNC_ERR("couldn't create fence fd, %d\n", fd);
+		dma_fence_put(&fence_array->base);
+		kfree(fence_lock);
+		kfree(fence_array);
+		ret = -EINVAL;
+		goto exit;
+	}
+
+	/* Decrement the refcount that sync_file_create increments */
+	dma_fence_put(&fence_array->base);
+
+	data.fence = fd;
+	if (copy_to_user((void __user *)arg, &data, sizeof(data))) {
+		fput(sync_file->file);
+		dma_fence_put(&fence_array->base);
+		kfree(fence_lock);
+		kfree(fence_array);
+		ret = -EFAULT;
+		goto exit;
+	}
+
+	fd_install(fd, sync_file->file);
+
+	return 0;
+
+exit:
+	put_unused_fd(fd);
+	return ret;
+}
+
+static long hw_sync_ioctl_destroy_fence_array(struct hw_sync_obj *obj, unsigned long arg)
+{
+	struct dma_fence_array *fence_array;
+	struct dma_fence *fence;
+	struct hw_fence_array_sync_create_data data;
+	int fd;
+
+	if (!_is_valid_client(obj))
+		return -EINVAL;
+
+	if (copy_from_user(&data, (void __user *)arg, sizeof(data)))
+		return -EFAULT;
+
+	fd = data.fence;
+	fence = (struct dma_fence *)_hw_sync_get_fence(fd);
+	if (!fence) {
+		HWFNC_ERR("Invalid fence fd: %d\n", fd);
+		return -EINVAL;
+	}
+
+	/* Decrement the refcount that hw_sync_get_fence increments */
+	dma_fence_put(fence);
+
+	fence_array = to_dma_fence_array(fence);
+	if (!fence_array) {
+		HWFNC_ERR("Invalid fence array fd: %d\n", fd);
+		return -EINVAL;
+	}
+
+	/* Destroy fence array */
+	dma_fence_put(&fence_array->base);
+
+	return 0;
+}
+
+/*
+ * this IOCTL only supports receiving one fence as input-parameter, which can be
+ * either a "dma_fence" or a "dma_fence_array", but eventually we would expand
+ * this API to receive more fences
+ */
+static long hw_sync_ioctl_reg_for_wait(struct hw_sync_obj *obj, unsigned long arg)
+{
+	struct dma_fence *fence;
+	int ret, fd, num_fences = 1;
+
+	if (!_is_valid_client(obj))
+		return -EINVAL;
+
+	if (copy_from_user(&fd, (void __user *)arg, sizeof(fd)))
+		return -EFAULT;
+
+	fence = (struct dma_fence *)_hw_sync_get_fence(fd);
+	if (!fence) {
+		HWFNC_ERR("Invalid fence fd: %d\n", fd);
+		return -EINVAL;
+	}
+
+	ret = msm_hw_fence_wait_update(obj->client_handle, &fence, num_fences, 1);
+
+	/* Decrement the refcount that hw_sync_get_fence increments */
+	dma_fence_put(fence);
+
+	return ret;
+}
+
+static long hw_sync_ioctl_fence_signal(struct hw_sync_obj *obj, unsigned long arg)
+{
+	struct hw_fence_sync_signal_data data;
+	int ret, tx_client, rx_client, signal_id;
+
+	if (!_is_valid_client(obj)) {
+		return -EINVAL;
+	} else if (IS_ERR_OR_NULL(obj->client_handle)) {
+		HWFNC_ERR("invalid client handle for the client_id: %d\n", obj->client_id);
+		return -EINVAL;
+	}
+
+	if (copy_from_user(&data, (void __user *)arg, sizeof(data)))
+		return -EFAULT;
+
+	ret = msm_hw_fence_update_txq(obj->client_handle, data.hash, 0, data.error_flag);
+	if (ret) {
+		HWFNC_ERR("hw fence update txq has failed client_id: %d\n", obj->client_id);
+		return ret;
+	}
+
+	signal_id = dbg_out_clients_signal_map_no_dpu[obj->client_id].ipc_signal_id;
+	if (signal_id < 0)
+		return -EINVAL;
+
+	tx_client = HW_FENCE_IPC_CLIENT_ID_APPS;
+	rx_client = HW_FENCE_IPC_CLIENT_ID_APPS;
+	ret = msm_hw_fence_trigger_signal(obj->client_handle, tx_client, rx_client, signal_id);
+	if (ret) {
+		HWFNC_ERR("hw fence trigger signal has failed\n");
+		return ret;
+	}
+
+	return 0;
+}
+
+static long hw_sync_ioctl_fence_wait(struct hw_sync_obj *obj, unsigned long arg)
+{
+	struct msm_hw_fence_client *hw_fence_client;
+	struct msm_hw_fence_queue_payload payload;
+	struct hw_fence_sync_wait_data data;
+	struct dma_fence *fence;
+	int fd, ret, read = 1, queue_type = HW_FENCE_RX_QUEUE - 1;  /* rx queue index */
+
+	if (!_is_valid_client(obj))
+		return -EINVAL;
+
+	if (copy_from_user(&data, (void __user *)arg, sizeof(data)))
+		return -EFAULT;
+
+	fd = data.fence;
+	fence = (struct dma_fence *)_hw_sync_get_fence(fd);
+	if (!fence) {
+		HWFNC_ERR("Invalid fence fd: %d\n", fd);
+		return -EINVAL;
+	}
+
+	hw_fence_client = (struct msm_hw_fence_client *)obj->client_handle;
+	if (!hw_fence_client) {
+		HWFNC_ERR("invalid client handle for fd:%d\n", fd);
+		/* Decrement the refcount that hw_sync_get_fence increments */
+		dma_fence_put(fence);
+		return -EINVAL;
+	}
+
+	ret = wait_event_timeout(hw_fence_client->wait_queue,
+			atomic_read(&hw_fence_client->val_signal) > 0,
+			msecs_to_jiffies(data.timeout_ms));
+	if (!ret) {
+		HWFNC_ERR("timed out waiting for the client signal %d\n", data.timeout_ms);
+		/* Decrement the refcount that hw_sync_get_fence increments */
+		dma_fence_put(fence);
+		return -ETIMEDOUT;
+	}
+
+	/* clear doorbell signal flag */
+	atomic_set(&hw_fence_client->val_signal, 0);
+
+	while (read) {
+		read = hw_fence_read_queue(obj->client_handle, &payload, queue_type);
+		if (read < 0) {
+			HWFNC_ERR("unable to read client rxq client_id:%d\n", obj->client_id);
+			break;
+		}
+		HWFNC_DBG_L("rxq read: hash:%llu, flags:%llu, error:%lu\n",
+			payload.hash, payload.flags, payload.error);
+		if (payload.ctxt_id == fence->context && payload.seqno == fence->seqno) {
+			/* Decrement the refcount that hw_sync_get_fence increments */
+			dma_fence_put(fence);
+			return 0;
+		}
+	}
+
+	/* Decrement the refcount that hw_sync_get_fence increments */
+	dma_fence_put(fence);
+
+	HWFNC_ERR("fence received did not match the fence expected\n");
+	HWFNC_ERR("fence received: context:%d seqno:%d fence expected: context:%d seqno:%d\n",
+				payload.ctxt_id, payload.seqno, fence->context, fence->seqno);
+
+	return read;
+}
+
+static long hw_sync_ioctl_reset_client(struct hw_sync_obj *obj, unsigned long arg)
+{
+	int ret;
+	struct hw_fence_sync_reset_data data;
+
+	if (!_is_valid_client(obj)) {
+		return -EINVAL;
+	} else if (IS_ERR_OR_NULL(obj->client_handle)) {
+		HWFNC_ERR("client:%d handle doesn't exists\n", data.client_id);
+		return -EINVAL;
+	}
+
+	if (copy_from_user(&data, (void __user *)arg, sizeof(data)))
+		return -EFAULT;
+
+	ret = msm_hw_fence_reset_client(obj->client_handle, data.reset_flag);
+	if (ret) {
+		HWFNC_ERR("hw fence reset client has failed\n");
+		return ret;
+	}
+
+	return 0;
+}
+
+static const struct hw_sync_ioctl_def hw_sync_debugfs_ioctls[] = {
+	HW_IOCTL_DEF(HW_SYNC_IOC_REG_CLIENT, hw_sync_ioctl_reg_client),
+	HW_IOCTL_DEF(HW_SYNC_IOC_UNREG_CLIENT, hw_sync_ioctl_unreg_client),
+	HW_IOCTL_DEF(HW_SYNC_IOC_CREATE_FENCE, hw_sync_ioctl_create_fence),
+	HW_IOCTL_DEF(HW_SYNC_IOC_DESTROY_FENCE, hw_sync_ioctl_destroy_fence),
+	HW_IOCTL_DEF(HW_SYNC_IOC_CREATE_FENCE_ARRAY, hw_sync_ioctl_create_fence_array),
+	HW_IOCTL_DEF(HW_SYNC_IOC_DESTROY_FENCE_ARRAY, hw_sync_ioctl_destroy_fence_array),
+	HW_IOCTL_DEF(HW_SYNC_IOC_REG_FOR_WAIT, hw_sync_ioctl_reg_for_wait),
+	HW_IOCTL_DEF(HW_SYNC_IOC_FENCE_SIGNAL, hw_sync_ioctl_fence_signal),
+	HW_IOCTL_DEF(HW_SYNC_IOC_FENCE_WAIT, hw_sync_ioctl_fence_wait),
+	HW_IOCTL_DEF(HW_SYNC_IOC_RESET_CLIENT, hw_sync_ioctl_reset_client)
+};
+
+static long hw_sync_debugfs_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+	struct hw_sync_obj *obj = file->private_data;
+	int num = HW_FENCE_IOCTL_NR(cmd);
+	hw_fence_ioctl_t *func;
+
+	if (num >= HW_SYNC_IOCTL_COUNT) {
+		HWFNC_ERR("invalid ioctl num = %d\n", num);
+		return -EINVAL;
+	}
+
+	func = (&hw_sync_debugfs_ioctls[num])->func;
+	if (unlikely(!func)) {
+		HWFNC_ERR("no function num = %d\n", num);
+		return -ENOTTY;
+	}
+
+	return func(obj, arg);
+}
+
+const struct file_operations hw_sync_debugfs_fops = {
+	.open           = hw_sync_debugfs_open,
+	.release        = hw_sync_debugfs_release,
+	.unlocked_ioctl = hw_sync_debugfs_ioctl,
+};

+ 4 - 0
hw_fence/src/msm_hw_fence.c

@@ -98,6 +98,10 @@ void *msm_hw_fence_register(enum hw_fence_client_id client_id,
 		hw_fence_client, hw_fence_client->client_id, hw_fence_client->ipc_signal_id,
 		hw_fence_client->ipc_client_id);
 
+#if IS_ENABLED(CONFIG_DEBUG_FS)
+	init_waitqueue_head(&hw_fence_client->wait_queue);
+#endif /* CONFIG_DEBUG_FS */
+
 	return (void *)hw_fence_client;
 error: