Эх сурвалжийг харах

qcacmn: Drain group tasklets and reg write work for runtime PM

Currently as part of runtime PM, only the active
tasklets are being drained. For chips eg. QCA6390,
QCA6490 etc, there are grp_tasklets and delayed reg
write work which has to be drained before entering
runtime PM.

Add the logic to drain all the possible tasks
before entering runtime PM.

Change-Id: Ieb486f00fffd7346dcdc1faea6fed5850ef6daf7
CRs-Fixed: 2676000
Rakesh Pillai 5 жил өмнө
parent
commit
37cc4255e2

+ 15 - 0
hal/wifi3.0/hal_api.h

@@ -551,6 +551,16 @@ void hal_dump_reg_write_srng_stats(hal_soc_handle_t hal_soc_hdl);
  * Return: none
  * Return: none
  */
  */
 void hal_dump_reg_write_stats(hal_soc_handle_t hal_soc_hdl);
 void hal_dump_reg_write_stats(hal_soc_handle_t hal_soc_hdl);
+
+/**
+ * hal_get_reg_write_pending_work() - get the number of entries
+ *		pending in the workqueue to be processed.
+ * @hal_soc: HAL soc handle
+ *
+ * Returns: the number of entries pending to be processed
+ */
+int hal_get_reg_write_pending_work(void *hal_soc);
+
 #else
 #else
 static inline void hal_dump_reg_write_srng_stats(hal_soc_handle_t hal_soc_hdl)
 static inline void hal_dump_reg_write_srng_stats(hal_soc_handle_t hal_soc_hdl)
 {
 {
@@ -559,6 +569,11 @@ static inline void hal_dump_reg_write_srng_stats(hal_soc_handle_t hal_soc_hdl)
 static inline void hal_dump_reg_write_stats(hal_soc_handle_t hal_soc_hdl)
 static inline void hal_dump_reg_write_stats(hal_soc_handle_t hal_soc_hdl)
 {
 {
 }
 }
+
+static inline int hal_get_reg_write_pending_work(void *hal_soc)
+{
+	return 0;
+}
 #endif
 #endif
 
 
 /**
 /**

+ 1 - 0
hal/wifi3.0/hal_internal.h

@@ -718,6 +718,7 @@ struct hal_soc {
 	/* read index used by worker thread to dequeue/write registers */
 	/* read index used by worker thread to dequeue/write registers */
 	uint32_t read_idx;
 	uint32_t read_idx;
 #endif
 #endif
+	qdf_atomic_t active_work_cnt;
 };
 };
 
 
 #ifdef FEATURE_HAL_DELAYED_REG_WRITE
 #ifdef FEATURE_HAL_DELAYED_REG_WRITE

+ 11 - 0
hal/wifi3.0/hal_srng.c

@@ -480,6 +480,7 @@ static void hal_reg_write_work(void *arg)
 		hal_verbose_debug("read_idx %u srng 0x%x, addr 0x%pK dequeue_val %u sched delay %llu us",
 		hal_verbose_debug("read_idx %u srng 0x%x, addr 0x%pK dequeue_val %u sched delay %llu us",
 				  hal->read_idx, ring_id, addr, write_val, delta_us);
 				  hal->read_idx, ring_id, addr, write_val, delta_us);
 
 
+		qdf_atomic_dec(&hal->active_work_cnt);
 		hal->read_idx = (hal->read_idx + 1) &
 		hal->read_idx = (hal->read_idx + 1) &
 					(HAL_REG_WRITE_QUEUE_LEN - 1);
 					(HAL_REG_WRITE_QUEUE_LEN - 1);
 		q_elem = &hal->reg_write_queue[(hal->read_idx)];
 		q_elem = &hal->reg_write_queue[(hal->read_idx)];
@@ -560,6 +561,7 @@ static void hal_reg_write_enqueue(struct hal_soc *hal_soc,
 	q_elem->valid = true;
 	q_elem->valid = true;
 
 
 	srng->reg_write_in_progress  = true;
 	srng->reg_write_in_progress  = true;
+	qdf_atomic_inc(&hal_soc->active_work_cnt);
 
 
 	hal_verbose_debug("write_idx %u srng ring id 0x%x addr 0x%pK val %u",
 	hal_verbose_debug("write_idx %u srng ring id 0x%x addr 0x%pK val %u",
 			  write_idx, srng->ring_id, addr, value);
 			  write_idx, srng->ring_id, addr, value);
@@ -686,6 +688,14 @@ void hal_dump_reg_write_stats(hal_soc_handle_t hal_soc_hdl)
 		  hist[REG_WRITE_SCHED_DELAY_SUB_5000us],
 		  hist[REG_WRITE_SCHED_DELAY_SUB_5000us],
 		  hist[REG_WRITE_SCHED_DELAY_GT_5000us]);
 		  hist[REG_WRITE_SCHED_DELAY_GT_5000us]);
 }
 }
+
+int hal_get_reg_write_pending_work(void *hal_soc)
+{
+	struct hal_soc *hal = (struct hal_soc *)hal_soc;
+
+	return qdf_atomic_read(&hal->active_work_cnt);
+}
+
 #else
 #else
 static inline QDF_STATUS hal_delayed_reg_write_init(struct hal_soc *hal)
 static inline QDF_STATUS hal_delayed_reg_write_init(struct hal_soc *hal)
 {
 {
@@ -770,6 +780,7 @@ void *hal_attach(struct hif_opaque_softc *hif_handle, qdf_device_t qdf_dev)
 
 
 	qdf_minidump_log(hal, sizeof(*hal), "hal_soc");
 	qdf_minidump_log(hal, sizeof(*hal), "hal_soc");
 
 
+	qdf_atomic_init(&hal->active_work_cnt);
 	hal_delayed_reg_write_init(hal);
 	hal_delayed_reg_write_init(hal);
 
 
 	return (void *)hal;
 	return (void *)hal;

+ 68 - 0
hif/src/hif_main.c

@@ -700,6 +700,74 @@ void hif_close(struct hif_opaque_softc *hif_ctx)
 	qdf_mem_free(scn);
 	qdf_mem_free(scn);
 }
 }
 
 
+/**
+ * hif_get_num_active_tasklets() - get the number of active
+ *		tasklets pending to be completed.
+ * @scn: HIF context
+ *
+ * Returns: the number of tasklets which are active
+ */
+static inline int hif_get_num_active_tasklets(struct hif_softc *scn)
+{
+	return qdf_atomic_read(&scn->active_tasklet_cnt);
+}
+
+/**
+ * hif_get_num_active_grp_tasklets() - get the number of active
+ *		datapath group tasklets pending to be completed.
+ * @scn: HIF context
+ *
+ * Returns: the number of datapath group tasklets which are active
+ */
+static inline int hif_get_num_active_grp_tasklets(struct hif_softc *scn)
+{
+	return qdf_atomic_read(&scn->active_grp_tasklet_cnt);
+}
+
+#if (defined(QCA_WIFI_QCA8074) || defined(QCA_WIFI_QCA6018) || \
+	defined(QCA_WIFI_QCA6290) || defined(QCA_WIFI_QCA6390) || \
+	defined(QCA_WIFI_QCN9000) || defined(QCA_WIFI_QCA6490) || \
+	defined(QCA_WIFI_QCA6750) || defined(QCA_WIFI_QCA5018))
+/**
+ * hif_get_num_pending_work() - get the number of entries in
+ *		the workqueue pending to be completed.
+ * @scn: HIF context
+ *
+ * Returns: the number of tasklets which are active
+ */
+static inline int hif_get_num_pending_work(struct hif_softc *scn)
+{
+	return hal_get_reg_write_pending_work(scn->hal_soc);
+}
+#else
+
+static inline int hif_get_num_pending_work(struct hif_softc *scn)
+{
+	return 0;
+}
+#endif
+
+QDF_STATUS hif_try_complete_tasks(struct hif_softc *scn)
+{
+	uint32_t task_drain_wait_cnt = 0;
+	int tasklet = 0, grp_tasklet = 0, work = 0;
+
+	while ((tasklet = hif_get_num_active_tasklets(scn)) ||
+	       (grp_tasklet = hif_get_num_active_grp_tasklets(scn)) ||
+	       (work = hif_get_num_pending_work(scn))) {
+		if (++task_drain_wait_cnt > HIF_TASK_DRAIN_WAIT_CNT) {
+			hif_err("pending tasklets %d grp tasklets %d work %d",
+				tasklet, grp_tasklet, work);
+			return QDF_STATUS_E_FAULT;
+		}
+		hif_info("waiting for tasklets %d grp tasklets %d work %d",
+			 tasklet, grp_tasklet, work);
+		msleep(10);
+	}
+
+	return QDF_STATUS_SUCCESS;
+}
+
 #if (defined(QCA_WIFI_QCA8074) || defined(QCA_WIFI_QCA6018) || \
 #if (defined(QCA_WIFI_QCA8074) || defined(QCA_WIFI_QCA6018) || \
 	defined(QCA_WIFI_QCA6290) || defined(QCA_WIFI_QCA6390) || \
 	defined(QCA_WIFI_QCA6290) || defined(QCA_WIFI_QCA6390) || \
 	defined(QCA_WIFI_QCN9000) || defined(QCA_WIFI_QCA6490) || \
 	defined(QCA_WIFI_QCN9000) || defined(QCA_WIFI_QCA6490) || \

+ 19 - 0
hif/src/hif_main.h

@@ -262,6 +262,25 @@ void *hif_get_hal_handle(struct hif_opaque_softc *hif_hdl)
 	return sc->hal_soc;
 	return sc->hal_soc;
 }
 }
 
 
+/**
+ * Max waiting time during Runtime PM suspend to finish all
+ * the tasks. This is in the multiple of 10ms.
+ */
+#define HIF_TASK_DRAIN_WAIT_CNT 25
+
+/**
+ * hif_try_complete_tasks() - Try to complete all the pending tasks
+ * @scn: HIF context
+ *
+ * Try to complete all the pending datapath tasks, i.e. tasklets,
+ * DP group tasklets and works which are queued, in a given time
+ * slot.
+ *
+ * Returns: QDF_STATUS_SUCCESS if all the tasks were completed
+ *	QDF error code, if the time slot exhausted
+ */
+QDF_STATUS hif_try_complete_tasks(struct hif_softc *scn);
+
 #ifdef QCA_NSS_WIFI_OFFLOAD_SUPPORT
 #ifdef QCA_NSS_WIFI_OFFLOAD_SUPPORT
 static inline bool hif_is_nss_wifi_enabled(struct hif_softc *sc)
 static inline bool hif_is_nss_wifi_enabled(struct hif_softc *sc)
 {
 {

+ 4 - 1
hif/src/ipcie/if_ipci.c

@@ -289,9 +289,12 @@ void hif_ipci_prevent_linkdown(struct hif_softc *scn, bool flag)
 
 
 int hif_ipci_bus_suspend(struct hif_softc *scn)
 int hif_ipci_bus_suspend(struct hif_softc *scn)
 {
 {
+	QDF_STATUS ret;
+
 	hif_apps_irqs_disable(GET_HIF_OPAQUE_HDL(scn));
 	hif_apps_irqs_disable(GET_HIF_OPAQUE_HDL(scn));
 
 
-	if (hif_drain_tasklets(scn)) {
+	ret = hif_try_complete_tasks(scn);
+	if (QDF_IS_STATUS_ERROR(ret)) {
 		hif_apps_irqs_enable(GET_HIF_OPAQUE_HDL(scn));
 		hif_apps_irqs_enable(GET_HIF_OPAQUE_HDL(scn));
 		return -EBUSY;
 		return -EBUSY;
 	}
 	}

+ 4 - 1
hif/src/pcie/if_pci.c

@@ -2687,9 +2687,12 @@ void hif_pci_prevent_linkdown(struct hif_softc *scn, bool flag)
  */
  */
 int hif_pci_bus_suspend(struct hif_softc *scn)
 int hif_pci_bus_suspend(struct hif_softc *scn)
 {
 {
+	QDF_STATUS ret;
+
 	hif_apps_irqs_disable(GET_HIF_OPAQUE_HDL(scn));
 	hif_apps_irqs_disable(GET_HIF_OPAQUE_HDL(scn));
 
 
-	if (hif_drain_tasklets(scn)) {
+	ret = hif_try_complete_tasks(scn);
+	if (QDF_IS_STATUS_ERROR(ret)) {
 		hif_apps_irqs_enable(GET_HIF_OPAQUE_HDL(scn));
 		hif_apps_irqs_enable(GET_HIF_OPAQUE_HDL(scn));
 		return -EBUSY;
 		return -EBUSY;
 	}
 	}