Quellcode durchsuchen

Merge "mm-drivers: hw_fence: add support for client queue alternate indexing"

qctecmdr vor 2 Jahren
Ursprung
Commit
e93f7e55ff

+ 47 - 12
hw_fence/include/hw_fence_drv_priv.h

@@ -150,12 +150,19 @@ enum hw_fence_client_data_id {
  * @q_size_bytes: size of the queue
  * @va_header: pointer to the hfi header virtual address
  * @pa_queue: physical address of the queue
+ * @rd_wr_idx_start: start read and write indexes for client queue (zero by default)
+ * @rd_wr_idx_factor: factor to multiply custom index to get index in dwords (one by default)
+ * @skip_wr_idx: bool to indicate if update to write_index is skipped within hw fence driver and
+ *               hfi_header->tx_wm is updated instead
  */
 struct msm_hw_fence_queue {
 	void *va_queue;
 	u32 q_size_bytes;
 	void *va_header;
 	phys_addr_t pa_queue;
+	u32 rd_wr_idx_start;
+	u32 rd_wr_idx_factor;
+	bool skip_wr_idx;
 };
 
 /**
@@ -172,13 +179,12 @@ enum payload_type {
  *                 number of sub-clients (e.g. ife clients)
  * @mem_descriptor: hfi header memory descriptor
  * @queues: queues descriptor
+ * @queues_num: number of client queues
  * @ipc_signal_id: id of the signal to be triggered for this client
  * @ipc_client_vid: virtual id of the ipc client for this hw fence driver client
  * @ipc_client_pid: physical id of the ipc client for this hw fence driver client
  * @update_rxq: bool to indicate if client uses rx-queue
  * @send_ipc: bool to indicate if client requires ipc interrupt for already signaled fences
- * @skip_txq_wr_idx: bool to indicate if update to tx queue write_index is skipped within hw fence
- *                   driver and hfi_header->tx_wm is updated instead
  * @wait_queue: wait queue for the validation clients
  * @val_signal: doorbell flag to signal the validation clients in the wait queue
  */
@@ -187,12 +193,12 @@ struct msm_hw_fence_client {
 	enum hw_fence_client_id client_id_ext;
 	struct msm_hw_fence_mem_addr mem_descriptor;
 	struct msm_hw_fence_queue queues[HW_FENCE_CLIENT_QUEUES];
+	int queues_num;
 	int ipc_signal_id;
 	int ipc_client_vid;
 	int ipc_client_pid;
 	bool update_rxq;
 	bool send_ipc;
-	bool skip_txq_wr_idx;
 #if IS_ENABLED(CONFIG_DEBUG_FS)
 	wait_queue_head_t wait_queue;
 	atomic_t val_signal;
@@ -239,24 +245,52 @@ struct msm_hw_fence_dbg_data {
 };
 
 /**
- * struct hw_fence_client_queue_size_desc - Structure holding client queue properties for a client.
+ * struct hw_fence_client_type_desc - Structure holding client type properties, including static
+ *                                    properties and client queue properties read from device-tree.
  *
- * @queues_num: number of client queues
- * @queue_entries: number of queue entries per client queue
- * @mem_size: size of memory allocated for client queues
- * @start_offset: start offset of client queue memory region, from beginning of carved-out memory
- *                allocation for hw fence driver
+ * @name: name of client type, used to parse properties from device-tree
+ * @init_id: initial client_id for given client type within the 'hw_fence_client_id' enum, e.g.
+ *           HW_FENCE_CLIENT_ID_CTL0 for DPU clients
+ * @max_clients_num: maximum number of clients of given client type
+ * @clients_num: number of clients of given client type
+ * @queues_num: number of queues per client of given client type; either one (for only Tx Queue) or
+ *              two (for both Tx and Rx Queues)
+ * @queue_entries: number of entries per client queue of given client type
+ * @start_padding: size of padding between queue table header and first queue header in bytes
+ * @end_padding: size of padding between queue header(s) and first queue payload in bytes
+ * @mem_size: size of memory allocated for client queue(s) per client in bytes
+ * @txq_idx_start: start read and write indexes for client tx queue (zero by default)
+ * @txq_idx_factor: factor to multiply custom TxQ idx to get index in dwords (one by default)
  * @skip_txq_wr_idx: bool to indicate if update to tx queue write_index is skipped within hw fence
  *                   driver and hfi_header->tx_wm is updated instead
  */
-struct hw_fence_client_queue_size_desc {
+struct hw_fence_client_type_desc {
+	char *name;
+	enum hw_fence_client_id init_id;
+	u32 max_clients_num;
+	u32 clients_num;
 	u32 queues_num;
 	u32 queue_entries;
+	u32 start_padding;
+	u32 end_padding;
 	u32 mem_size;
-	u32 start_offset;
+	u32 txq_idx_start;
+	u32 txq_idx_factor;
 	bool skip_txq_wr_idx;
 };
 
+/**
+ * struct hw_fence_client_queue_desc - Structure holding client queue properties for a client.
+ *
+ * @type: pointer to client queue properties of client type
+ * @start_offset: start offset of client queue memory region, from beginning of carved-out memory
+ *                allocation for hw fence driver
+ */
+struct hw_fence_client_queue_desc {
+	struct hw_fence_client_type_desc *type;
+	u32 start_offset;
+};
+
 /**
  * struct hw_fence_driver_data - Structure holding internal hw-fence driver data
  *
@@ -268,6 +302,7 @@ struct hw_fence_client_queue_size_desc {
  * @hw_fence_ctrl_queue_size: size of the ctrl queue for the payload
  * @hw_fence_mem_ctrl_queues_size: total size of ctrl queues, including: header + rxq + txq
  * @hw_fence_client_queue_size: descriptors of client queue properties for each hw fence client
+ * @hw_fence_client_types: descriptors of properties for each hw fence client type
  * @rxq_clients_num: number of supported hw fence clients with rxq (configured based on device-tree)
  * @clients_num: number of supported hw fence clients (configured based on device-tree)
  * @hw_fences_tbl: pointer to the hw-fences table
@@ -320,7 +355,7 @@ struct hw_fence_driver_data {
 	u32 hw_fence_ctrl_queue_size;
 	u32 hw_fence_mem_ctrl_queues_size;
 	/* client queues */
-	struct hw_fence_client_queue_size_desc *hw_fence_client_queue_size;
+	struct hw_fence_client_queue_desc *hw_fence_client_queue_size;
 	struct hw_fence_client_type_desc *hw_fence_client_types;
 	u32 rxq_clients_num;
 	u32 clients_num;

+ 3 - 33
hw_fence/include/hw_fence_drv_utils.h

@@ -38,33 +38,6 @@ enum hw_fence_mem_reserve {
 	HW_FENCE_MEM_RESERVE_CLIENT_QUEUE
 };
 
-/**
- * struct hw_fence_client_type_desc - Structure holding client type properties, including static
- *                                    properties and client queue properties read from device-tree.
- *
- * @name: name of client type, used to parse properties from device-tree
- * @init_id: initial client_id for given client type within the 'hw_fence_client_id' enum, e.g.
- *           HW_FENCE_CLIENT_ID_CTL0 for DPU clients
- * @max_clients_num: maximum number of clients of given client type
- * @clients_num: number of clients of given client type
- * @queues_num: number of queues per client of given client type; either one (for only Tx Queue) or
- *              two (for both Tx and Rx Queues)
- * @queue_entries: number of entries per client queue of given client type
- * @mem_size: size of memory allocated for client queue(s) per client
- * @skip_txq_wr_idx: bool to indicate if update to tx queue write_index is skipped within hw fence
- *                   driver and hfi_header->tx_wm is updated instead
- */
-struct hw_fence_client_type_desc {
-	char *name;
-	enum hw_fence_client_id init_id;
-	u32 max_clients_num;
-	u32 clients_num;
-	u32 queues_num;
-	u32 queue_entries;
-	u32 mem_size;
-	bool skip_txq_wr_idx;
-};
-
 /**
  * global_atomic_store() - Inter-processor lock
  * @drv_data: hw fence driver data
@@ -174,16 +147,13 @@ enum hw_fence_client_id hw_fence_utils_get_client_id_priv(struct hw_fence_driver
 	enum hw_fence_client_id client_id);
 
 /**
- * hw_fence_utils_skips_txq_wr_index() - Returns bool to indicate if client Tx Queue write_index
- *                                       is not updated in hw fence driver. Instead,
- *                                       hfi_header->tx_wm tracks where payload is written within
- *                                       the queue.
+ * hw_fence_utils_get_queues_num() - Returns number of client queues for the client_id.
  *
  * @drv_data: driver data
  * @client_id: hw fence driver client id
  *
- * Returns: true if hw fence driver skips update to client tx queue write_index, false otherwise
+ * Returns: number of client queues
  */
-bool hw_fence_utils_skips_txq_wr_idx(struct hw_fence_driver_data *drv_data, int client_id);
+int hw_fence_utils_get_queues_num(struct hw_fence_driver_data *drv_data, int client_id);
 
 #endif /* __HW_FENCE_DRV_UTILS_H */

+ 100 - 22
hw_fence/src/hw_fence_drv_priv.c

@@ -15,6 +15,8 @@
 /* Global atomic lock */
 #define GLOBAL_ATOMIC_STORE(drv_data, lock, val) global_atomic_store(drv_data, lock, val)
 
+#define IS_HW_FENCE_TX_QUEUE(queue_type) ((queue_type) == HW_FENCE_TX_QUEUE - 1)
+
 inline u64 hw_fence_get_qtime(struct hw_fence_driver_data *drv_data)
 {
 #ifdef HWFENCE_USE_SLEEP_TIMER
@@ -32,11 +34,14 @@ static int init_hw_fences_queues(struct hw_fence_driver_data *drv_data,
 {
 	struct msm_hw_fence_hfi_queue_table_header *hfi_table_header;
 	struct msm_hw_fence_hfi_queue_header *hfi_queue_header;
+	struct hw_fence_client_type_desc *desc;
 	void *ptr, *qptr;
 	phys_addr_t phys, qphys;
-	u32 size, start_queue_offset;
+	u32 size, start_queue_offset, txq_idx_start = 0, txq_idx_factor = 1;
 	int headers_size, queue_size, payload_size;
+	int start_padding = 0, end_padding = 0;
 	int i, ret = 0;
+	bool skip_txq_wr_idx = false;
 
 	HWFNC_DBG_INIT("mem_reserve_id:%d client_id:%d\n", mem_reserve_id, client_id);
 	switch (mem_reserve_id) {
@@ -46,15 +51,23 @@ static int init_hw_fences_queues(struct hw_fence_driver_data *drv_data,
 		payload_size = HW_FENCE_CTRL_QUEUE_PAYLOAD;
 		break;
 	case HW_FENCE_MEM_RESERVE_CLIENT_QUEUE:
-		if (client_id >= drv_data->clients_num) {
-			HWFNC_ERR("Invalid client_id: %d\n", client_id);
+		if (client_id >= drv_data->clients_num ||
+				!drv_data->hw_fence_client_queue_size[client_id].type) {
+			HWFNC_ERR("Invalid client_id:%d for clients_num:%lu\n", client_id,
+				drv_data->clients_num);
 			return -EINVAL;
 		}
 
-		headers_size = HW_FENCE_HFI_CLIENT_HEADERS_SIZE(queues_num);
-		queue_size = HW_FENCE_CLIENT_QUEUE_PAYLOAD *
-			drv_data->hw_fence_client_queue_size[client_id].queue_entries;
+		desc = drv_data->hw_fence_client_queue_size[client_id].type;
+		start_padding = desc->start_padding;
+		end_padding = desc->end_padding;
+		headers_size = HW_FENCE_HFI_CLIENT_HEADERS_SIZE(queues_num) + start_padding +
+			end_padding;
+		queue_size = HW_FENCE_CLIENT_QUEUE_PAYLOAD * desc->queue_entries;
 		payload_size = HW_FENCE_CLIENT_QUEUE_PAYLOAD;
+		txq_idx_start = desc->txq_idx_start;
+		txq_idx_factor = desc->txq_idx_factor ? desc->txq_idx_factor : 1;
+		skip_txq_wr_idx = desc->skip_txq_wr_idx;
 		break;
 	default:
 		HWFNC_ERR("Unexpected mem reserve id: %d\n", mem_reserve_id);
@@ -75,16 +88,15 @@ static int init_hw_fences_queues(struct hw_fence_driver_data *drv_data,
 	mem_descriptor->size = size; /* bytes */
 	mem_descriptor->mem_data = NULL; /* Currently we don't need any special info */
 
-	HWFNC_DBG_INIT("Initialize headers\n");
+	HWFNC_DBG_INIT("Initialize headers: headers_size:%d start_padding:%d end_padding:%d\n",
+		headers_size, start_padding, end_padding);
 	/* Initialize headers info within hfi memory */
 	hfi_table_header = (struct msm_hw_fence_hfi_queue_table_header *)ptr;
 	hfi_table_header->version = 0;
 	hfi_table_header->size = size; /* bytes */
 	/* Offset, from the Base Address, where the first queue header starts */
-	hfi_table_header->qhdr0_offset =
-		sizeof(struct msm_hw_fence_hfi_queue_table_header);
-	hfi_table_header->qhdr_size =
-		sizeof(struct msm_hw_fence_hfi_queue_header);
+	hfi_table_header->qhdr0_offset = HW_FENCE_HFI_TABLE_HEADER_SIZE + start_padding;
+	hfi_table_header->qhdr_size = HW_FENCE_HFI_QUEUE_HEADER_SIZE;
 	hfi_table_header->num_q = queues_num; /* number of queues */
 	hfi_table_header->num_active_q = queues_num;
 
@@ -96,7 +108,7 @@ static int init_hw_fences_queues(struct hw_fence_driver_data *drv_data,
 	 */
 	HWFNC_DBG_INIT("Initialize queues\n");
 	hfi_queue_header = (struct msm_hw_fence_hfi_queue_header *)
-					   ((char *)ptr + HW_FENCE_HFI_TABLE_HEADER_SIZE);
+					   ((char *)ptr + hfi_table_header->qhdr0_offset);
 	for (i = 0; i < queues_num; i++) {
 		HWFNC_DBG_INIT("init queue[%d]\n", i);
 
@@ -109,7 +121,8 @@ static int init_hw_fences_queues(struct hw_fence_driver_data *drv_data,
 		hfi_queue_header->start_addr = qphys;
 
 		/* Set the queue type (i.e. RX or TX queue) */
-		hfi_queue_header->type = (i == 0) ? HW_FENCE_TX_QUEUE : HW_FENCE_RX_QUEUE;
+		hfi_queue_header->type = IS_HW_FENCE_TX_QUEUE(i) ? HW_FENCE_TX_QUEUE :
+			HW_FENCE_RX_QUEUE;
 
 		/* Set the size of this header */
 		hfi_queue_header->queue_size = queue_size;
@@ -117,6 +130,20 @@ static int init_hw_fences_queues(struct hw_fence_driver_data *drv_data,
 		/* Set the payload size */
 		hfi_queue_header->pkt_size = payload_size;
 
+		/* Set write index for clients' tx queues that index from nonzero value */
+		if (txq_idx_start && IS_HW_FENCE_TX_QUEUE(i) && !hfi_queue_header->write_index) {
+			if (skip_txq_wr_idx)
+				hfi_queue_header->tx_wm = txq_idx_start;
+			hfi_queue_header->read_index = txq_idx_start;
+			hfi_queue_header->write_index = txq_idx_start;
+			HWFNC_DBG_INIT("init:TX_QUEUE client:%d rd_idx=%s=%lu\n", client_id,
+				skip_txq_wr_idx ? "wr_idx=tx_wm" : "wr_idx",
+				txq_idx_start);
+		}
+
+		/* Update memory for hfi_queue_header */
+		wmb();
+
 		/* Store Memory info in the Client data */
 		queues[i].va_queue = qptr;
 		queues[i].pa_queue = qphys;
@@ -127,6 +154,18 @@ static int init_hw_fences_queues(struct hw_fence_driver_data *drv_data,
 			client_id, i, queues[i].va_queue, queues[i].pa_queue, queues[i].va_header,
 			queues[i].q_size_bytes, payload_size);
 
+		/* Store additional tx queue rd_wr_idx properties */
+		if (IS_HW_FENCE_TX_QUEUE(i)) {
+			queues[i].rd_wr_idx_start = txq_idx_start;
+			queues[i].rd_wr_idx_factor = txq_idx_factor;
+			queues[i].skip_wr_idx = skip_txq_wr_idx;
+		} else {
+			queues[i].rd_wr_idx_factor = 1;
+		}
+		HWFNC_DBG_INIT("rd_wr_idx_start:%lu rd_wr_idx_factor:%lu skip_wr_idx:%s\n",
+			queues[i].rd_wr_idx_start, queues[i].rd_wr_idx_factor,
+			queues[i].skip_wr_idx ? "true" : "false");
+
 		/* Next header */
 		hfi_queue_header++;
 	}
@@ -183,6 +222,14 @@ int hw_fence_read_queue(struct msm_hw_fence_client *hw_fence_client,
 	read_idx = readl_relaxed(&hfi_header->read_index);
 	write_idx = readl_relaxed(&hfi_header->write_index);
 
+	/* translate read and write indexes from custom indexing to dwords with no offset */
+	if (queue->rd_wr_idx_start || queue->rd_wr_idx_factor != 1) {
+		read_idx = (read_idx - queue->rd_wr_idx_start) * queue->rd_wr_idx_factor;
+		write_idx = (write_idx - queue->rd_wr_idx_start) * queue->rd_wr_idx_factor;
+		HWFNC_DBG_Q("rd_idx_u32:%lu wr_idx_u32:%lu rd_wr_idx start:%lu factor:%lu\n",
+			read_idx, write_idx, queue->rd_wr_idx_start, queue->rd_wr_idx_factor);
+	}
+
 	HWFNC_DBG_Q("read client:%d rd_ptr:0x%pK wr_ptr:0x%pK rd_idx:%d wr_idx:%d queue:0x%pK\n",
 		hw_fence_client->client_id, &hfi_header->read_index, &hfi_header->write_index,
 		read_idx, write_idx, queue);
@@ -209,6 +256,13 @@ int hw_fence_read_queue(struct msm_hw_fence_client *hw_fence_client,
 	if (to_read_idx >= q_size_u32)
 		to_read_idx = 0;
 
+	/* translate to_read_idx to custom indexing with offset */
+	if (queue->rd_wr_idx_start || queue->rd_wr_idx_factor != 1) {
+		to_read_idx = (to_read_idx / queue->rd_wr_idx_factor) + queue->rd_wr_idx_start;
+		HWFNC_DBG_Q("translated to_read_idx:%lu rd_wr_idx start:%lu factor:%lu\n",
+			to_read_idx, queue->rd_wr_idx_start, queue->rd_wr_idx_factor);
+	}
+
 	/* Read the Client Queue */
 	payload->ctxt_id = readq_relaxed(&read_ptr_payload->ctxt_id);
 	payload->seqno = readq_relaxed(&read_ptr_payload->seqno);
@@ -251,10 +305,9 @@ int hw_fence_update_queue(struct hw_fence_driver_data *drv_data,
 	u32 *wr_ptr;
 	int ret = 0;
 
-	if (queue_type >=
-		drv_data->hw_fence_client_queue_size[hw_fence_client->client_id].queues_num) {
-		HWFNC_ERR("Invalid queue type:%s client_id:%d\n", queue_type,
-			hw_fence_client->client_id);
+	if (queue_type >= hw_fence_client->queues_num) {
+		HWFNC_ERR("Invalid queue type:%d client_id:%d q_num:%lu\n", queue_type,
+			hw_fence_client->client_id, hw_fence_client->queues_num);
 		return -EINVAL;
 	}
 
@@ -270,8 +323,8 @@ int hw_fence_update_queue(struct hw_fence_driver_data *drv_data,
 		return -EINVAL;
 	}
 
-	/* if skipping update txq wr_index, then use hfi_header->tx_wm instead */
-	if (queue_type == (HW_FENCE_TX_QUEUE - 1) && hw_fence_client->skip_txq_wr_idx)
+	/* if skipping update wr_index, then use hfi_header->tx_wm instead */
+	if (queue->skip_wr_idx)
 		wr_ptr = &hfi_header->tx_wm;
 	else
 		wr_ptr = &hfi_header->write_index;
@@ -305,8 +358,15 @@ int hw_fence_update_queue(struct hw_fence_driver_data *drv_data,
 
 	HWFNC_DBG_Q("wr client:%d r_ptr:0x%pK w_ptr:0x%pK r_idx:%d w_idx:%d q:0x%pK type:%d s:%s\n",
 		hw_fence_client->client_id, &hfi_header->read_index, wr_ptr,
-		read_idx, write_idx, queue, queue_type,
-		hw_fence_client->skip_txq_wr_idx ? "true" : "false");
+		read_idx, write_idx, queue, queue_type, queue->skip_wr_idx ? "true" : "false");
+
+	/* translate read and write indexes from custom indexing to dwords with no offset */
+	if (queue->rd_wr_idx_start || queue->rd_wr_idx_factor != 1) {
+		read_idx = (read_idx - queue->rd_wr_idx_start) * queue->rd_wr_idx_factor;
+		write_idx = (write_idx - queue->rd_wr_idx_start) * queue->rd_wr_idx_factor;
+		HWFNC_DBG_Q("rd_idx_u32:%lu wr_idx_u32:%lu rd_wr_idx start:%lu factor:%lu\n",
+			read_idx, write_idx, queue->rd_wr_idx_start, queue->rd_wr_idx_factor);
+	}
 
 	/* Check queue to make sure message will fit */
 	q_free_u32 = read_idx <= write_idx ? (q_size_u32 - (write_idx - read_idx)) :
@@ -341,6 +401,13 @@ int hw_fence_update_queue(struct hw_fence_driver_data *drv_data,
 	if (to_write_idx >= q_size_u32)
 		to_write_idx = 0;
 
+	/* translate to_write_idx to custom indexing with offset */
+	if (queue->rd_wr_idx_start || queue->rd_wr_idx_factor != 1) {
+		to_write_idx = (to_write_idx / queue->rd_wr_idx_factor) + queue->rd_wr_idx_start;
+		HWFNC_DBG_Q("translated to_write_idx:%lu rd_wr_idx start:%lu factor:%lu\n",
+			to_write_idx, queue->rd_wr_idx_start, queue->rd_wr_idx_factor);
+	}
+
 	/* Update Client Queue */
 	writeq_relaxed(payload_size, &write_ptr_payload->size);
 	writew_relaxed(HW_FENCE_PAYLOAD_TYPE_1, &write_ptr_payload->type);
@@ -539,10 +606,16 @@ int hw_fence_alloc_client_resources(struct hw_fence_driver_data *drv_data,
 {
 	int ret;
 
+	if (!drv_data->hw_fence_client_queue_size[hw_fence_client->client_id].type) {
+		HWFNC_ERR("invalid client_id:%d not reserved client queue; check dt props\n",
+			hw_fence_client->client_id);
+		return -EINVAL;
+	}
+
 	/* Init client queues */
 	ret = init_hw_fences_queues(drv_data, HW_FENCE_MEM_RESERVE_CLIENT_QUEUE,
 		&hw_fence_client->mem_descriptor, hw_fence_client->queues,
-		drv_data->hw_fence_client_queue_size[hw_fence_client->client_id].queues_num,
+		drv_data->hw_fence_client_queue_size[hw_fence_client->client_id].type->queues_num,
 		hw_fence_client->client_id);
 	if (ret) {
 		HWFNC_ERR("Failure to init the queue for client:%d\n",
@@ -1451,8 +1524,12 @@ void hw_fence_utils_reset_queues(struct hw_fence_driver_data *drv_data,
 	/* For the client TxQ: set the read-index same as last write that was done by the client */
 	mb(); /* make sure data is ready before read */
 	wr_idx = readl_relaxed(&hfi_header->write_index);
+	if (queue->skip_wr_idx)
+		hfi_header->tx_wm = wr_idx;
 	writel_relaxed(wr_idx, &hfi_header->read_index);
 	wmb(); /* make sure data is updated after write the index*/
+	HWFNC_DBG_Q("update tx queue %s to match write_index:%lu\n",
+		queue->skip_wr_idx ? "read_index=tx_wm" : "read_index", wr_idx);
 
 	/* For the client RxQ: set the write-index same as last read done by the client */
 	if (hw_fence_client->update_rxq) {
@@ -1478,6 +1555,7 @@ void hw_fence_utils_reset_queues(struct hw_fence_driver_data *drv_data,
 
 		/* unlock */
 		GLOBAL_ATOMIC_STORE(drv_data, &drv_data->client_lock_tbl[lock_idx], 0);
+		HWFNC_DBG_Q("update rx queue write_index to match read_index:%lu\n", rd_idx);
 	}
 }
 

+ 137 - 41
hw_fence/src/hw_fence_drv_utils.c

@@ -77,23 +77,31 @@
  */
 struct hw_fence_client_type_desc hw_fence_client_types[HW_FENCE_MAX_CLIENT_TYPE] = {
 	{"gpu", HW_FENCE_CLIENT_ID_CTX0, HW_FENCE_CLIENT_TYPE_MAX_GPU, HW_FENCE_CLIENT_TYPE_MAX_GPU,
-		HW_FENCE_CLIENT_QUEUES, 0, 0, false},
+		HW_FENCE_CLIENT_QUEUES, 0, 0, 0, 0, 0, 0, false},
 	{"dpu", HW_FENCE_CLIENT_ID_CTL0, HW_FENCE_CLIENT_TYPE_MAX_DPU, HW_FENCE_CLIENT_TYPE_MAX_DPU,
-		HW_FENCE_CLIENT_QUEUES, 0, 0, false},
+		HW_FENCE_CLIENT_QUEUES, 0, 0, 0, 0, 0, 0, false},
 	{"val", HW_FENCE_CLIENT_ID_VAL0, HW_FENCE_CLIENT_TYPE_MAX_VAL, HW_FENCE_CLIENT_TYPE_MAX_VAL,
-		HW_FENCE_CLIENT_QUEUES, 0, 0, false},
-	{"ipe", HW_FENCE_CLIENT_ID_IPE, HW_FENCE_CLIENT_TYPE_MAX_IPE, 0,
-		HW_FENCE_CLIENT_QUEUES, 0, 0, false},
-	{"vpu", HW_FENCE_CLIENT_ID_VPU, HW_FENCE_CLIENT_TYPE_MAX_VPU, 0,
-		HW_FENCE_CLIENT_QUEUES, 0, 0, false},
-	{"ife0", HW_FENCE_CLIENT_ID_IFE0, HW_FENCE_CLIENT_TYPE_MAX_IFE, 0, 1, 0, 0, true},
-	{"ife1", HW_FENCE_CLIENT_ID_IFE1, HW_FENCE_CLIENT_TYPE_MAX_IFE, 0, 1, 0, 0, true},
-	{"ife2", HW_FENCE_CLIENT_ID_IFE2, HW_FENCE_CLIENT_TYPE_MAX_IFE, 0, 1, 0, 0, true},
-	{"ife3", HW_FENCE_CLIENT_ID_IFE3, HW_FENCE_CLIENT_TYPE_MAX_IFE, 0, 1, 0, 0, true},
-	{"ife4", HW_FENCE_CLIENT_ID_IFE4, HW_FENCE_CLIENT_TYPE_MAX_IFE, 0, 1, 0, 0, true},
-	{"ife5", HW_FENCE_CLIENT_ID_IFE5, HW_FENCE_CLIENT_TYPE_MAX_IFE, 0, 1, 0, 0, true},
-	{"ife6", HW_FENCE_CLIENT_ID_IFE6, HW_FENCE_CLIENT_TYPE_MAX_IFE, 0, 1, 0, 0, true},
-	{"ife7", HW_FENCE_CLIENT_ID_IFE7, HW_FENCE_CLIENT_TYPE_MAX_IFE, 0, 1, 0, 0, true},
+		HW_FENCE_CLIENT_QUEUES, 0, 0, 0, 0, 0, 0, false},
+	{"ipe", HW_FENCE_CLIENT_ID_IPE, HW_FENCE_CLIENT_TYPE_MAX_IPE, 0, HW_FENCE_CLIENT_QUEUES,
+		0, 0, 0, 0, 0, 0, false},
+	{"vpu", HW_FENCE_CLIENT_ID_VPU, HW_FENCE_CLIENT_TYPE_MAX_VPU, 0, HW_FENCE_CLIENT_QUEUES,
+		0, 0, 0, 0, 0, 0, false},
+	{"ife0", HW_FENCE_CLIENT_ID_IFE0, HW_FENCE_CLIENT_TYPE_MAX_IFE, 0, 1, 0, 0, 0, 0, 0, 0,
+		true},
+	{"ife1", HW_FENCE_CLIENT_ID_IFE1, HW_FENCE_CLIENT_TYPE_MAX_IFE, 0, 1, 0, 0, 0, 0, 0, 0,
+		true},
+	{"ife2", HW_FENCE_CLIENT_ID_IFE2, HW_FENCE_CLIENT_TYPE_MAX_IFE, 0, 1, 0, 0, 0, 0, 0, 0,
+		true},
+	{"ife3", HW_FENCE_CLIENT_ID_IFE3, HW_FENCE_CLIENT_TYPE_MAX_IFE, 0, 1, 0, 0, 0, 0, 0, 0,
+		true},
+	{"ife4", HW_FENCE_CLIENT_ID_IFE4, HW_FENCE_CLIENT_TYPE_MAX_IFE, 0, 1, 0, 0, 0, 0, 0, 0,
+		true},
+	{"ife5", HW_FENCE_CLIENT_ID_IFE5, HW_FENCE_CLIENT_TYPE_MAX_IFE, 0, 1, 0, 0, 0, 0, 0, 0,
+		true},
+	{"ife6", HW_FENCE_CLIENT_ID_IFE6, HW_FENCE_CLIENT_TYPE_MAX_IFE, 0, 1, 0, 0, 0, 0, 0, 0,
+		true},
+	{"ife7", HW_FENCE_CLIENT_ID_IFE7, HW_FENCE_CLIENT_TYPE_MAX_IFE, 0, 1, 0, 0, 0, 0, 0, 0,
+		true},
 };
 
 static void _lock(uint64_t *wait)
@@ -549,23 +557,16 @@ int hw_fence_utils_reserve_mem(struct hw_fence_driver_data *drv_data,
 		*size = drv_data->hw_fence_mem_fences_table_size;
 		break;
 	case HW_FENCE_MEM_RESERVE_CLIENT_QUEUE:
-		if (client_id >= drv_data->clients_num) {
-			HWFNC_ERR("unexpected client_id:%d\n", client_id);
+		if (client_id >= drv_data->clients_num ||
+				!drv_data->hw_fence_client_queue_size[client_id].type) {
+			HWFNC_ERR("unexpected client_id:%d for clients_num:%lu\n", client_id,
+				drv_data->clients_num);
 			ret = -EINVAL;
 			goto exit;
 		}
 
 		start_offset = drv_data->hw_fence_client_queue_size[client_id].start_offset;
-		*size = drv_data->hw_fence_client_queue_size[client_id].mem_size;
-
-		/*
-		 * If this error occurs when client should be valid, check that support for this
-		 * client has been configured in device-tree properties.
-		 */
-		if (!*size) {
-			HWFNC_ERR("invalid client_id:%d not reserved client queue\n", client_id);
-			ret = -EINVAL;
-		}
+		*size = drv_data->hw_fence_client_queue_size[client_id].type->mem_size;
 		break;
 	default:
 		HWFNC_ERR("Invalid mem reserve type:%d\n", type);
@@ -592,6 +593,92 @@ exit:
 	return ret;
 }
 
+static int _parse_client_queue_dt_props_extra(struct hw_fence_driver_data *drv_data,
+	struct hw_fence_client_type_desc *desc)
+{
+	u32 max_idx_from_zero, payload_size_u32 = HW_FENCE_CLIENT_QUEUE_PAYLOAD / sizeof(u32);
+	char name[40];
+	u32 tmp[4];
+	bool idx_by_payload = false;
+	int count, ret;
+
+	snprintf(name, sizeof(name), "qcom,hw-fence-client-type-%s-extra", desc->name);
+
+	/* check if property is present */
+	ret = of_property_read_bool(drv_data->dev->of_node, name);
+	if (!ret)
+		return 0;
+
+	count = of_property_count_u32_elems(drv_data->dev->of_node, name);
+	if (count <= 0 || count > 4) {
+		HWFNC_ERR("invalid %s extra dt props count:%d\n", desc->name, count);
+		return -EINVAL;
+	}
+
+	ret = of_property_read_u32_array(drv_data->dev->of_node, name, tmp, count);
+	if (ret) {
+		HWFNC_ERR("Failed to read %s extra dt properties ret=%d count=%d\n", desc->name,
+			ret, count);
+		ret = -EINVAL;
+		goto exit;
+	}
+
+	desc->start_padding = tmp[0];
+	if (count >= 2)
+		desc->end_padding = tmp[1];
+	if (count >= 3)
+		desc->txq_idx_start = tmp[2];
+	if (count >= 4) {
+		if (tmp[3] > 1) {
+			HWFNC_ERR("%s invalid txq_idx_by_payload prop:%lu\n", desc->name, tmp[3]);
+			ret = -EINVAL;
+			goto exit;
+		}
+		idx_by_payload = tmp[3];
+		desc->txq_idx_factor = idx_by_payload ? payload_size_u32 : 1;
+	}
+
+	if (desc->start_padding % sizeof(u32) || desc->end_padding % sizeof(u32) ||
+			(desc->start_padding + desc->end_padding) % sizeof(u64)) {
+		HWFNC_ERR("%s start_padding:%lu end_padding:%lu violates mem alignment\n",
+			desc->name, desc->start_padding, desc->end_padding);
+		ret = -EINVAL;
+		goto exit;
+	}
+
+	if (desc->start_padding >= U32_MAX - HW_FENCE_HFI_CLIENT_HEADERS_SIZE(desc->queues_num)) {
+		HWFNC_ERR("%s client queues_num:%lu start_padding:%lu will overflow mem_size\n",
+			desc->name, desc->queues_num, desc->start_padding);
+		ret = -EINVAL;
+		goto exit;
+	}
+
+	if (desc->end_padding >= U32_MAX - HW_FENCE_HFI_CLIENT_HEADERS_SIZE(desc->queues_num) -
+			desc->start_padding) {
+		HWFNC_ERR("%s client q_num:%lu start_p:%lu end_p:%lu will overflow mem_size\n",
+			desc->name, desc->queues_num, desc->start_padding, desc->end_padding);
+		ret = -EINVAL;
+		goto exit;
+	}
+
+	max_idx_from_zero = idx_by_payload ? desc->queue_entries :
+		desc->queue_entries * payload_size_u32;
+	if (desc->txq_idx_start >= U32_MAX - max_idx_from_zero) {
+		HWFNC_ERR("%s txq_idx start:%lu by_payload:%s q_entries:%d will overflow txq_idx\n",
+			desc->name, desc->txq_idx_start, idx_by_payload ? "true" : "false",
+			desc->queue_entries);
+		ret = -EINVAL;
+		goto exit;
+	}
+
+	HWFNC_DBG_INIT("%s: start_p=%lu end_p=%lu txq_idx_start:%lu txq_idx_by_payload:%s\n",
+		desc->name, desc->start_padding, desc->end_padding, desc->txq_idx_start,
+		idx_by_payload ? "true" : "false");
+
+exit:
+	return ret;
+}
+
 static int _parse_client_queue_dt_props_indv(struct hw_fence_driver_data *drv_data,
 	struct hw_fence_client_type_desc *desc)
 {
@@ -600,7 +687,7 @@ static int _parse_client_queue_dt_props_indv(struct hw_fence_driver_data *drv_da
 	u32 queue_size;
 	int ret;
 
-	/* parse client queue property from device-tree */
+	/* parse client queue properties from device-tree */
 	snprintf(name, sizeof(name), "qcom,hw-fence-client-type-%s", desc->name);
 	ret = of_property_read_u32_array(drv_data->dev->of_node, name, tmp, 4);
 	if (ret) {
@@ -626,6 +713,13 @@ static int _parse_client_queue_dt_props_indv(struct hw_fence_driver_data *drv_da
 		return -EINVAL;
 	}
 
+	/* parse extra client queue properties from device-tree */
+	ret = _parse_client_queue_dt_props_extra(drv_data, desc);
+	if (ret) {
+		HWFNC_ERR("%s failed to parse extra dt props\n", desc->name);
+		return -EINVAL;
+	}
+
 	/* compute mem_size */
 	if (desc->queue_entries >= U32_MAX / HW_FENCE_CLIENT_QUEUE_PAYLOAD) {
 		HWFNC_ERR("%s client queue entries:%lu will overflow client queue size\n",
@@ -635,17 +729,18 @@ static int _parse_client_queue_dt_props_indv(struct hw_fence_driver_data *drv_da
 
 	queue_size = HW_FENCE_CLIENT_QUEUE_PAYLOAD * desc->queue_entries;
 	if (queue_size >= ((U32_MAX & PAGE_MASK) -
-		HW_FENCE_HFI_CLIENT_HEADERS_SIZE(desc->queues_num)) / desc->queues_num) {
-		HWFNC_ERR("%s client queue size:%lu will overflow client queue mem size\n",
-			desc->name, queue_size);
+			(HW_FENCE_HFI_CLIENT_HEADERS_SIZE(desc->queues_num) +
+			desc->start_padding + desc->end_padding)) / desc->queues_num) {
+		HWFNC_ERR("%s client queue_sz:%lu start_p:%lu end_p:%lu will overflow mem size\n",
+			desc->name, queue_size, desc->start_padding, desc->end_padding);
 		return -EINVAL;
 	}
 
 	desc->mem_size = PAGE_ALIGN(HW_FENCE_HFI_CLIENT_HEADERS_SIZE(desc->queues_num) +
-		(queue_size * desc->queues_num));
+		(queue_size * desc->queues_num) + desc->start_padding + desc->end_padding);
 
 	if (desc->mem_size > MAX_CLIENT_QUEUE_MEM_SIZE) {
-		HWFNC_ERR("%s client queue mem_size:%lu greater than max client queue size:%lu\n",
+		HWFNC_ERR("%s client queue mem_size:%lu greater than max mem size:%lu\n",
 			desc->name, desc->mem_size, MAX_CLIENT_QUEUE_MEM_SIZE);
 		return -EINVAL;
 	}
@@ -690,7 +785,7 @@ static int _parse_client_queue_dt_props(struct hw_fence_driver_data *drv_data)
 	drv_data->clients_num = HW_FENCE_MAX_STATIC_CLIENTS_INDEX + configurable_clients_num;
 
 	/* allocate memory for client queue size descriptors */
-	size = drv_data->clients_num * sizeof(struct hw_fence_client_queue_size_desc);
+	size = drv_data->clients_num * sizeof(struct hw_fence_client_queue_desc);
 	drv_data->hw_fence_client_queue_size = kzalloc(size, GFP_KERNEL);
 	if (!drv_data->hw_fence_client_queue_size)
 		return -ENOMEM;
@@ -707,9 +802,7 @@ static int _parse_client_queue_dt_props(struct hw_fence_driver_data *drv_data)
 				hw_fence_utils_get_client_id_priv(drv_data, client_id_ext);
 
 			drv_data->hw_fence_client_queue_size[client_id] =
-				(struct hw_fence_client_queue_size_desc)
-				{desc->queues_num, desc->queue_entries, desc->mem_size,
-				start_offset, desc->skip_txq_wr_idx};
+				(struct hw_fence_client_queue_desc){desc, start_offset};
 			HWFNC_DBG_INIT("%s client_id_ext:%lu client_id:%lu start_offset:%lu\n",
 				desc->name, client_id_ext, client_id, start_offset);
 			start_offset += desc->mem_size;
@@ -929,10 +1022,13 @@ enum hw_fence_client_id hw_fence_utils_get_client_id_priv(struct hw_fence_driver
 	return client_id_priv;
 }
 
-bool hw_fence_utils_skips_txq_wr_idx(struct hw_fence_driver_data *drv_data, int client_id)
+int hw_fence_utils_get_queues_num(struct hw_fence_driver_data *drv_data, int client_id)
 {
-	if (!drv_data || client_id >= drv_data->clients_num)
-		return false;
+	if (!drv_data || client_id >= drv_data->clients_num ||
+			!drv_data->hw_fence_client_queue_size[client_id].type) {
+		HWFNC_ERR("invalid access to client:%d queues_num\n", client_id);
+		return 0;
+	}
 
-	return drv_data->hw_fence_client_queue_size[client_id].skip_txq_wr_idx;
+	return drv_data->hw_fence_client_queue_size[client_id].type->queues_num;
 }

+ 13 - 12
hw_fence/src/msm_hw_fence.c

@@ -87,25 +87,25 @@ void *msm_hw_fence_register(enum hw_fence_client_id client_id_ext,
 	}
 
 	hw_fence_client->update_rxq = hw_fence_ipcc_needs_rxq_update(hw_fence_drv_data, client_id);
-	if (hw_fence_client->update_rxq &&
-			hw_fence_drv_data->hw_fence_client_queue_size[client_id].queues_num <
-			HW_FENCE_CLIENT_QUEUES) {
-		HWFNC_ERR("Cannot update rx queue for tx queue-only client:%d\n", client_id);
+	hw_fence_client->send_ipc = hw_fence_ipcc_needs_ipc_irq(hw_fence_drv_data, client_id);
+
+	hw_fence_client->queues_num = hw_fence_utils_get_queues_num(hw_fence_drv_data, client_id);
+	if (!hw_fence_client->queues_num || (hw_fence_client->update_rxq &&
+			hw_fence_client->queues_num < HW_FENCE_CLIENT_QUEUES)) {
+		HWFNC_ERR("client:%d invalid q_num:%lu for updates_rxq:%s\n", client_id,
+			hw_fence_client->queues_num,
+			hw_fence_client->update_rxq ? "true" : "false");
 		ret = -EINVAL;
 		goto error;
 	}
 
-	hw_fence_client->send_ipc = hw_fence_ipcc_needs_ipc_irq(hw_fence_drv_data, client_id);
-	hw_fence_client->skip_txq_wr_idx = hw_fence_utils_skips_txq_wr_idx(hw_fence_drv_data,
-		client_id);
-
 	/* Alloc Client HFI Headers and Queues */
 	ret = hw_fence_alloc_client_resources(hw_fence_drv_data,
 		hw_fence_client, mem_descriptor);
 	if (ret)
 		goto error;
 
-	/* Initialize signal for communication withe FenceCTL */
+	/* Initialize signal for communication with FenceCTL */
 	ret = hw_fence_init_controller_signal(hw_fence_drv_data, hw_fence_client);
 	if (ret)
 		goto error;
@@ -118,9 +118,10 @@ void *msm_hw_fence_register(enum hw_fence_client_id client_id_ext,
 	if (ret)
 		goto error;
 
-	HWFNC_DBG_INIT("-- Initialized ptr:0x%p client_id:%d ipc_signal_id:%d ipc vid:%d pid:%d\n",
-		hw_fence_client, hw_fence_client->client_id, hw_fence_client->ipc_signal_id,
-		hw_fence_client->ipc_client_vid, hw_fence_client->ipc_client_pid);
+	HWFNC_DBG_INIT("Initialized ptr:0x%p client_id:%d q_num:%d ipc signal:%d vid:%d pid:%d\n",
+		hw_fence_client, hw_fence_client->client_id, hw_fence_client->queues_num,
+		hw_fence_client->ipc_signal_id, hw_fence_client->ipc_client_vid,
+		hw_fence_client->ipc_client_pid);
 
 #if IS_ENABLED(CONFIG_DEBUG_FS)
 	init_waitqueue_head(&hw_fence_client->wait_queue);