diff --git a/hal/wifi3.0/hal_api.h b/hal/wifi3.0/hal_api.h index a2807df502..84393d6cc8 100644 --- a/hal/wifi3.0/hal_api.h +++ b/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 */ 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 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 int hal_get_reg_write_pending_work(void *hal_soc) +{ + return 0; +} #endif /** diff --git a/hal/wifi3.0/hal_internal.h b/hal/wifi3.0/hal_internal.h index ee7cb8be9b..98f58c2416 100644 --- a/hal/wifi3.0/hal_internal.h +++ b/hal/wifi3.0/hal_internal.h @@ -718,6 +718,7 @@ struct hal_soc { /* read index used by worker thread to dequeue/write registers */ uint32_t read_idx; #endif + qdf_atomic_t active_work_cnt; }; #ifdef FEATURE_HAL_DELAYED_REG_WRITE diff --git a/hal/wifi3.0/hal_srng.c b/hal/wifi3.0/hal_srng.c index 69ca137ccf..3f305ee200 100644 --- a/hal/wifi3.0/hal_srng.c +++ b/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->read_idx, ring_id, addr, write_val, delta_us); + qdf_atomic_dec(&hal->active_work_cnt); hal->read_idx = (hal->read_idx + 1) & (HAL_REG_WRITE_QUEUE_LEN - 1); 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; 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", 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_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 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_atomic_init(&hal->active_work_cnt); hal_delayed_reg_write_init(hal); return (void *)hal; diff --git a/hif/src/hif_main.c b/hif/src/hif_main.c index 3f45af8278..6594d7c5e1 100644 --- a/hif/src/hif_main.c +++ b/hif/src/hif_main.c @@ -700,6 +700,74 @@ void hif_close(struct hif_opaque_softc *hif_ctx) 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) || \ defined(QCA_WIFI_QCA6290) || defined(QCA_WIFI_QCA6390) || \ defined(QCA_WIFI_QCN9000) || defined(QCA_WIFI_QCA6490) || \ diff --git a/hif/src/hif_main.h b/hif/src/hif_main.h index 344f126973..a7de9beb4e 100644 --- a/hif/src/hif_main.h +++ b/hif/src/hif_main.h @@ -262,6 +262,25 @@ void *hif_get_hal_handle(struct hif_opaque_softc *hif_hdl) 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 static inline bool hif_is_nss_wifi_enabled(struct hif_softc *sc) { diff --git a/hif/src/ipcie/if_ipci.c b/hif/src/ipcie/if_ipci.c index 2e2646951f..1ec5c51084 100644 --- a/hif/src/ipcie/if_ipci.c +++ b/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) { + QDF_STATUS ret; + 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)); return -EBUSY; } diff --git a/hif/src/pcie/if_pci.c b/hif/src/pcie/if_pci.c index ea1518b73a..b1a29c0ae7 100644 --- a/hif/src/pcie/if_pci.c +++ b/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) { + QDF_STATUS ret; + 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)); return -EBUSY; }