Переглянути джерело

mm-drivers: hw_fence: add dtsi-based allocation of client queues

Update hw fence driver to support configurable parameters for
each client type, which can be set up through device-tree. This
allows configuring number of queues (e.g. only Tx Queue or both
Rx and Tx Queues), number of entries per client queue, and number
of sub-clients for each client-type.

Change-Id: I2d8f84ff2b7eb5322f9ca661cfd8f6a291db7b38
Signed-off-by: Grace An <[email protected]>
Grace An 2 роки тому
батько
коміт
b2efa8bc8b

+ 22 - 6
hw_fence/include/hw_fence_drv_priv.h

@@ -35,8 +35,8 @@
 #define HW_FENCE_HFI_CTRL_HEADERS_SIZE (HW_FENCE_HFI_TABLE_HEADER_SIZE + \
 			(HW_FENCE_HFI_QUEUE_HEADER_SIZE * HW_FENCE_CTRL_QUEUES))
 
-#define HW_FENCE_HFI_CLIENT_HEADERS_SIZE (HW_FENCE_HFI_TABLE_HEADER_SIZE + \
-			(HW_FENCE_HFI_QUEUE_HEADER_SIZE * HW_FENCE_CLIENT_QUEUES))
+#define HW_FENCE_HFI_CLIENT_HEADERS_SIZE(queues_num) (HW_FENCE_HFI_TABLE_HEADER_SIZE + \
+			(HW_FENCE_HFI_QUEUE_HEADER_SIZE * queues_num))
 
 /*
  * Max Payload size is the bigest size of the message that we can have in the CTRL queue
@@ -230,6 +230,22 @@ struct msm_hw_fence_dbg_data {
 	u64 lock_wake_cnt;
 };
 
+/**
+ * struct hw_fence_client_queue_size_desc - Structure holding client queue properties for a client.
+ *
+ * @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
+ */
+struct hw_fence_client_queue_size_desc {
+	u32 queues_num;
+	u32 queue_entries;
+	u32 mem_size;
+	u32 start_offset;
+};
+
 /**
  * struct hw_fence_driver_data - Structure holding internal hw-fence driver data
  *
@@ -240,8 +256,7 @@ struct msm_hw_fence_dbg_data {
  * @hw_fence_queue_entries: total number of entries that can be available in the queue
  * @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: size of the client queue for the payload
- * @hw_fence_mem_clients_queues_size: total size of client queues, including: header + rxq + txq
+ * @hw_fence_client_queue_size: descriptors of client queue properties for each hw fence client
  * @hw_fences_tbl: pointer to the hw-fences table
  * @hw_fences_tbl_cnt: number of elements in the hw-fence table
  * @client_lock_tbl: pointer to the per-client locks table
@@ -257,6 +272,7 @@ struct msm_hw_fence_dbg_data {
  * @peer_name: peer name for this carved-out memory
  * @rm_nb: hyp resource manager notifier
  * @memparcel: memparcel for the allocated memory
+ * @used_mem_size: total memory size of global table, lock region, and ctrl and client queues
  * @db_label: doorbell label
  * @rx_dbl: handle to the Rx doorbell
  * @debugfs_data: debugfs info
@@ -291,8 +307,7 @@ struct hw_fence_driver_data {
 	u32 hw_fence_ctrl_queue_size;
 	u32 hw_fence_mem_ctrl_queues_size;
 	/* client queues */
-	u32 hw_fence_client_queue_size;
-	u32 hw_fence_mem_clients_queues_size;
+	struct hw_fence_client_queue_size_desc hw_fence_client_queue_size[HW_FENCE_CLIENT_MAX];
 
 	/* HW Fences Table VA */
 	struct msm_hw_fence *hw_fences_tbl;
@@ -316,6 +331,7 @@ struct hw_fence_driver_data {
 	u32 peer_name;
 	struct notifier_block rm_nb;
 	u32 memparcel;
+	u32 used_mem_size;
 
 	/* doorbell */
 	u32 db_label;

+ 15 - 6
hw_fence/src/hw_fence_drv_priv.c

@@ -46,8 +46,14 @@ 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:
-		headers_size = HW_FENCE_HFI_CLIENT_HEADERS_SIZE;
-		queue_size = drv_data->hw_fence_client_queue_size;
+		if (client_id >= HW_FENCE_CLIENT_MAX) {
+			HWFNC_ERR("Invalid client_id: %d\n", client_id);
+			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;
 		payload_size = HW_FENCE_CLIENT_QUEUE_PAYLOAD;
 		break;
 	default:
@@ -244,8 +250,10 @@ int hw_fence_update_queue(struct hw_fence_driver_data *drv_data,
 	u64 timestamp;
 	int ret = 0;
 
-	if (queue_type >= HW_FENCE_CLIENT_QUEUES) {
-		HWFNC_ERR("Invalid queue type:%s\n", queue_type);
+	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);
 		return -EINVAL;
 	}
 
@@ -526,7 +534,8 @@ int hw_fence_alloc_client_resources(struct hw_fence_driver_data *drv_data,
 	/* 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,
-		HW_FENCE_CLIENT_QUEUES, hw_fence_client->client_id);
+		drv_data->hw_fence_client_queue_size[hw_fence_client->client_id].queues_num,
+		hw_fence_client->client_id);
 	if (ret) {
 		HWFNC_ERR("Failure to init the queue for client:%d\n",
 			hw_fence_client->client_id);
@@ -549,7 +558,7 @@ int hw_fence_init_controller_signal(struct hw_fence_driver_data *drv_data,
 	/*
 	 * Initialize IPCC Signals for this client
 	 *
-	 * NOTE: Fore each Client HW-Core, the client drivers might be the ones making
+	 * NOTE: For each Client HW-Core, the client drivers might be the ones making
 	 * it's own initialization (in case that any hw-sequence must be enforced),
 	 * however, if that is  not the case, any per-client ipcc init to enable the
 	 * signaling, can go here.

+ 176 - 22
hw_fence/src/hw_fence_drv_utils.c

@@ -15,6 +15,73 @@
 #include "hw_fence_drv_ipc.h"
 #include "hw_fence_drv_debug.h"
 
+/**
+ * MAX_CLIENT_QUEUE_MEM_SIZE:
+ * Maximum memory size for client queues of a hw fence client.
+ */
+#define MAX_CLIENT_QUEUE_MEM_SIZE 0x100000
+
+/**
+ * HW_FENCE_MAX_CLIENT_TYPE:
+ * Total number of client types (GFX, DPU, VAL)
+ */
+#define HW_FENCE_MAX_CLIENT_TYPE 3
+
+/* Maximum number of clients for each client type */
+#define HW_FENCE_CLIENT_TYPE_MAX_GPU 1
+#define HW_FENCE_CLIENT_TYPE_MAX_DPU 6
+#define HW_FENCE_CLIENT_TYPE_MAX_VAL 7
+
+/**
+ * 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
+ */
+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;
+};
+
+/**
+ * struct hw_fence_client_types - Table describing all supported client types, used to parse
+ *                                device-tree properties related to client queue size.
+ *
+ * The fields name, init_id, and max_clients_num are constants. Default values for clients_num and
+ * queues_num are provided in this table, and clients_num, queues_num, and queue_entries can be read
+ * from device-tree.
+ *
+ * If a value for queue entries is not parsed for the client type, then the default number of client
+ * queue entries (parsed from device-tree) is used.
+ *
+ * Notes:
+ * 1. Client types must be in the same order as client_ids within the enum 'hw_fence_client_id'.
+ * 2. Each HW Fence client ID must be described by one of the client types in this table.
+ * 3. A new client type must set: name, init_id, max_clients_num, clients_num, and queues_num.
+ * 4. HW_FENCE_MAX_CLIENT_TYPE must be incremented for new client types.
+ */
+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},
+	{"dpu", HW_FENCE_CLIENT_ID_CTL0, HW_FENCE_CLIENT_TYPE_MAX_DPU, HW_FENCE_CLIENT_TYPE_MAX_DPU,
+		HW_FENCE_CLIENT_QUEUES, 0, 0},
+	{"val", HW_FENCE_CLIENT_ID_VAL0, HW_FENCE_CLIENT_TYPE_MAX_VAL, HW_FENCE_CLIENT_TYPE_MAX_VAL,
+		HW_FENCE_CLIENT_QUEUES, 0, 0},
+};
+
 static void _lock(uint64_t *wait)
 {
 #if defined(__aarch64__)
@@ -399,6 +466,11 @@ int hw_fence_utils_alloc_mem(struct hw_fence_driver_data *drv_data)
 		return -ENXIO;
 	}
 	drv_data->size = resource_size(&drv_data->res);
+	if (drv_data->size < drv_data->used_mem_size) {
+		HWFNC_ERR("0x%x size of carved-out memory region is less than required size:0x%x\n",
+			drv_data->size, drv_data->used_mem_size);
+		return -ENOMEM;
+	}
 
 	HWFNC_DBG_INIT("io_mem_base:0x%x start:0x%x end:0x%x size:0x%x name:%s\n",
 		drv_data->io_mem_base, drv_data->res.start,
@@ -469,12 +541,17 @@ int hw_fence_utils_reserve_mem(struct hw_fence_driver_data *drv_data,
 			goto exit;
 		}
 
-		start_offset = PAGE_ALIGN(drv_data->hw_fence_mem_ctrl_queues_size +
-			HW_FENCE_MEM_LOCKS_SIZE +
-			drv_data->hw_fence_mem_fences_table_size) +
-			((client_id - 1) * drv_data->hw_fence_mem_clients_queues_size);
-		*size = drv_data->hw_fence_mem_clients_queues_size;
+		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;
+		}
 		break;
 	default:
 		HWFNC_ERR("Invalid mem reserve type:%d\n", type);
@@ -501,6 +578,95 @@ 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)
+{
+	char name[31];
+	u32 tmp[3];
+	u32 queue_size;
+	int ret;
+
+	/* parse client queue property 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, 3);
+	if (ret) {
+		HWFNC_DBG_INIT("missing %s client queue entry or invalid ret:%d\n", desc->name,
+			ret);
+		desc->queue_entries = drv_data->hw_fence_queue_entries;
+	} else {
+		desc->clients_num = tmp[0];
+		desc->queues_num = tmp[1];
+		desc->queue_entries = tmp[2];
+	}
+
+	if (desc->clients_num > desc->max_clients_num || !desc->queues_num ||
+			desc->queues_num > HW_FENCE_CLIENT_QUEUES || !desc->queue_entries) {
+		HWFNC_ERR("%s invalid dt: clients_num:%lu queues_num:%lu, queue_entries:%lu\n",
+			desc->name, desc->clients_num, desc->queues_num, desc->queue_entries);
+		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",
+			desc->name, desc->queue_entries);
+		return -EINVAL;
+	}
+
+	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);
+		return -EINVAL;
+	}
+
+	desc->mem_size = PAGE_ALIGN(HW_FENCE_HFI_CLIENT_HEADERS_SIZE(desc->queues_num) +
+		(queue_size * desc->queues_num));
+
+	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",
+			desc->name, desc->mem_size, MAX_CLIENT_QUEUE_MEM_SIZE);
+		return -EINVAL;
+	}
+
+	HWFNC_DBG_INIT("%s: clients=%lu q_num=%lu q_entries=%lu mem_sz=%lu\n", desc->name,
+		desc->clients_num, desc->queues_num, desc->queue_entries, desc->mem_size);
+
+	return 0;
+}
+
+static int _parse_client_queue_dt_props(struct hw_fence_driver_data *drv_data)
+{
+	struct hw_fence_client_type_desc *desc;
+	int i, j, ret;
+	u32 start_offset;
+
+	start_offset = PAGE_ALIGN(drv_data->hw_fence_mem_ctrl_queues_size +
+		HW_FENCE_MEM_LOCKS_SIZE + drv_data->hw_fence_mem_fences_table_size);
+	for (i = 0; i < HW_FENCE_MAX_CLIENT_TYPE; i++) {
+		desc = &hw_fence_client_types[i];
+		ret = _parse_client_queue_dt_props_indv(drv_data, desc);
+		if (ret) {
+			HWFNC_ERR("failed to initialize %s client queue size properties\n",
+				desc->name);
+			return ret;
+		}
+
+		/* initialize client queue size desc for each client */
+		for (j = 0; j < desc->clients_num; j++) {
+			drv_data->hw_fence_client_queue_size[desc->init_id + j] =
+				(struct hw_fence_client_queue_size_desc)
+				{desc->queues_num, desc->queue_entries, desc->mem_size,
+				start_offset};
+			start_offset += desc->mem_size;
+		}
+	}
+	drv_data->used_mem_size = start_offset;
+
+	return 0;
+}
+
 int hw_fence_utils_parse_dt_props(struct hw_fence_driver_data *drv_data)
 {
 	int ret;
@@ -549,29 +715,17 @@ int hw_fence_utils_parse_dt_props(struct hw_fence_driver_data *drv_data)
 
 	/* clients queues init */
 
-	if (drv_data->hw_fence_queue_entries >= U32_MAX / HW_FENCE_CLIENT_QUEUE_PAYLOAD) {
-		HWFNC_ERR("queue entries:%lu will overflow client queue size\n",
-			drv_data->hw_fence_queue_entries);
-		return -EINVAL;
-	}
-	drv_data->hw_fence_client_queue_size = HW_FENCE_CLIENT_QUEUE_PAYLOAD *
-		drv_data->hw_fence_queue_entries;
-
-	if (drv_data->hw_fence_client_queue_size >= ((U32_MAX & PAGE_MASK) -
-			HW_FENCE_HFI_CLIENT_HEADERS_SIZE) / HW_FENCE_CLIENT_QUEUES) {
-		HWFNC_ERR("queue size:%lu will overflow client queue mem size\n",
-			drv_data->hw_fence_client_queue_size);
+	ret = _parse_client_queue_dt_props(drv_data);
+	if (ret) {
+		HWFNC_ERR("failed to parse client queue properties\n");
 		return -EINVAL;
 	}
-	drv_data->hw_fence_mem_clients_queues_size = PAGE_ALIGN(HW_FENCE_HFI_CLIENT_HEADERS_SIZE +
-		(HW_FENCE_CLIENT_QUEUES * drv_data->hw_fence_client_queue_size));
 
 	HWFNC_DBG_INIT("table: entries=%lu mem_size=%lu queue: entries=%lu\b",
 		drv_data->hw_fence_table_entries, drv_data->hw_fence_mem_fences_table_size,
 		drv_data->hw_fence_queue_entries);
-	HWFNC_DBG_INIT("ctrl queue: size=%lu mem_size=%lu clients queues: size=%lu mem_size=%lu\b",
-		drv_data->hw_fence_ctrl_queue_size, drv_data->hw_fence_mem_ctrl_queues_size,
-		drv_data->hw_fence_client_queue_size, drv_data->hw_fence_mem_clients_queues_size);
+	HWFNC_DBG_INIT("ctrl queue: size=%lu mem_size=%lu\b",
+		drv_data->hw_fence_ctrl_queue_size, drv_data->hw_fence_mem_ctrl_queues_size);
 
 	return 0;
 }

+ 8 - 0
hw_fence/src/msm_hw_fence.c

@@ -74,6 +74,14 @@ void *msm_hw_fence_register(enum hw_fence_client_id client_id,
 	}
 
 	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);
+		ret = -EINVAL;
+		goto error;
+	}
+
 	hw_fence_client->send_ipc = hw_fence_ipcc_needs_ipc_irq(hw_fence_drv_data, client_id);
 
 	/* Alloc Client HFI Headers and Queues */