Selaa lähdekoodia

qcacmn: disable all apps CE irqs except wake_irq in bus suspend

In Moselle, currently CE interrupts are not disabled from apps
side during ipci bus suspend, so adding changes to disable all
the CE interrupts except wake_irq during bus suspend and do the
symmetric inverse operation during bus resume, also drain all
the pending FW diag logs from copy engine.

Change-Id: Ib54fc6660fd81aff18787b0b699f3a6cd2d7803d
CRs-Fixed: 2879752
Vevek Venkatesan 4 vuotta sitten
vanhempi
sitoutus
17660198d7

+ 22 - 0
hif/inc/hif.h

@@ -1306,6 +1306,28 @@ int hif_apps_enable_irq_wake(struct hif_opaque_softc *hif_ctx);
  */
 int hif_apps_disable_irq_wake(struct hif_opaque_softc *hif_ctx);
 
+/**
+ * hif_apps_enable_irqs_except_wake_irq() - Enables all irqs except wake_irq
+ * @hif_ctx: an opaque HIF handle to use
+ *
+ * As opposed to the standard hif_irq_enable, this function always applies to
+ * the APPS side kernel interrupt handling.
+ *
+ * Return: errno
+ */
+int hif_apps_enable_irqs_except_wake_irq(struct hif_opaque_softc *hif_ctx);
+
+/**
+ * hif_apps_disable_irqs_except_wake_irq() - Disables all irqs except wake_irq
+ * @hif_ctx: an opaque HIF handle to use
+ *
+ * As opposed to the standard hif_irq_disable, this function always applies to
+ * the APPS side kernel interrupt handling.
+ *
+ * Return: errno
+ */
+int hif_apps_disable_irqs_except_wake_irq(struct hif_opaque_softc *hif_ctx);
+
 #ifdef FEATURE_RUNTIME_PM
 int hif_pre_runtime_suspend(struct hif_opaque_softc *hif_ctx);
 void hif_pre_runtime_resume(struct hif_opaque_softc *hif_ctx);

+ 10 - 1
hif/src/ce/ce_internal.h

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013-2020 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2013-2021 The Linux Foundation. All rights reserved.
  *
  * Permission to use, copy, modify, and/or distribute this software for
  * any purpose with or without fee is hereby granted, provided that the
@@ -552,6 +552,15 @@ static inline void ce_t2h_msg_ce_cleanup(struct CE_handle *ce_hdl)
  */
 int hif_get_wake_ce_id(struct hif_softc *scn, uint8_t *ce_id);
 
+/**
+ * hif_get_fw_diag_ce_id() - gets the copy engine id used for FW diag
+ * @scn: The hif context to use
+ * @ce_id: a pointer where the copy engine Id should be populated
+ *
+ * Return: errno
+ */
+int hif_get_fw_diag_ce_id(struct hif_softc *scn, uint8_t *ce_id);
+
 #if defined(HIF_CONFIG_SLUB_DEBUG_ON) || defined(HIF_CE_DEBUG_DATA_BUF)
 
 #ifndef HIF_CE_HISTORY_MAX

+ 21 - 0
hif/src/ce/ce_main.c

@@ -4480,6 +4480,27 @@ int hif_get_wake_ce_id(struct hif_softc *scn, uint8_t *ce_id)
 	return 0;
 }
 
+int hif_get_fw_diag_ce_id(struct hif_softc *scn, uint8_t *ce_id)
+{
+	int status;
+	uint8_t ul_pipe, dl_pipe;
+	int ul_is_polled, dl_is_polled;
+
+	/* DL pipe for WMI_CONTROL_DIAG_SVC should map to the FW DIAG CE_ID */
+	status = hif_map_service_to_pipe(GET_HIF_OPAQUE_HDL(scn),
+					 WMI_CONTROL_DIAG_SVC,
+					 &ul_pipe, &dl_pipe,
+					 &ul_is_polled, &dl_is_polled);
+	if (status) {
+		hif_err("Failed to map pipe: %d", status);
+		return status;
+	}
+
+	*ce_id = dl_pipe;
+
+	return 0;
+}
+
 #ifdef HIF_CE_LOG_INFO
 /**
  * ce_get_index_info(): Get CE index info

+ 58 - 0
hif/src/ce/ce_tasklet.c

@@ -612,6 +612,64 @@ static inline bool hif_tasklet_schedule(struct hif_opaque_softc *hif_ctx,
 	return true;
 }
 
+/**
+ * ce_poll_reap_by_id() - reap the available frames from CE by polling per ce_id
+ * @scn: hif context
+ * @ce_id: CE id
+ *
+ * This function needs to be called once after all the irqs are disabled
+ * and tasklets are drained during bus suspend.
+ *
+ * Return: 0 on success, unlikely -EBUSY if reaping goes infinite loop
+ */
+static int ce_poll_reap_by_id(struct hif_softc *scn, enum ce_id_type ce_id)
+{
+	struct HIF_CE_state *hif_ce_state = (struct HIF_CE_state *)scn;
+	struct CE_state *CE_state = scn->ce_id_to_state[ce_id];
+
+	if (scn->ce_latency_stats)
+		hif_record_tasklet_exec_entry_ts(scn, ce_id);
+
+	hif_record_ce_desc_event(scn, ce_id, HIF_CE_REAP_ENTRY,
+				 NULL, NULL, -1, 0);
+
+	ce_per_engine_service(scn, ce_id);
+
+	/*
+	 * In an unlikely case, if frames are still pending to reap,
+	 * could be an infinite loop, so return -EBUSY.
+	 */
+	if (ce_check_rx_pending(CE_state))
+		return -EBUSY;
+
+	hif_record_ce_desc_event(scn, ce_id, HIF_CE_REAP_EXIT,
+				 NULL, NULL, -1, 0);
+
+	if (scn->ce_latency_stats)
+		ce_tasklet_update_bucket(hif_ce_state, ce_id);
+
+	return 0;
+}
+
+/**
+ * hif_drain_fw_diag_ce() - reap all the available FW diag logs from CE
+ * @scn: hif context
+ *
+ * This function needs to be called once after all the irqs are disabled
+ * and tasklets are drained during bus suspend.
+ *
+ * Return: 0 on success, unlikely -EBUSY if reaping goes infinite loop
+ */
+int hif_drain_fw_diag_ce(struct hif_softc *scn)
+{
+	uint8_t ce_id;
+
+	if (hif_get_fw_diag_ce_id(scn, &ce_id))
+		return 0;
+
+	return ce_poll_reap_by_id(scn, ce_id);
+}
+
 /**
  * ce_dispatch_interrupt() - dispatch an interrupt to a processing context
  * @ce_id: ce_id

+ 3 - 1
hif/src/ce/ce_tasklet.h

@@ -1,5 +1,6 @@
 /*
- * Copyright (c) 2015-2016,2018,2020 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2015-2016,2018,2020-2021 The Linux Foundation.
+ * All rights reserved.
  *
  * Permission to use, copy, modify, and/or distribute this software for
  * any purpose with or without fee is hereby granted, provided that the
@@ -24,6 +25,7 @@ void deinit_tasklet_workers(struct hif_opaque_softc *scn);
 void ce_tasklet_init(struct HIF_CE_state *hif_ce_state, uint32_t mask);
 void ce_tasklet_kill(struct hif_softc *scn);
 int hif_drain_tasklets(struct hif_softc *scn);
+int hif_drain_fw_diag_ce(struct hif_softc *scn);
 QDF_STATUS ce_register_irq(struct HIF_CE_state *hif_ce_state, uint32_t mask);
 QDF_STATUS ce_unregister_irq(struct HIF_CE_state *hif_ce_state, uint32_t mask);
 irqreturn_t ce_dispatch_interrupt(int irq,

+ 41 - 1
hif/src/dispatcher/multibus.c

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016-2018, 2020 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2016-2018, 2020-2021 The Linux Foundation. All rights reserved.
  *
  * Permission to use, copy, modify, and/or distribute this software for
  * any purpose with or without fee is hereby granted, provided that the
@@ -555,6 +555,46 @@ int hif_apps_enable_irq_wake(struct hif_opaque_softc *hif_ctx)
 	return enable_irq_wake(scn->wake_irq);
 }
 
+int hif_apps_disable_irqs_except_wake_irq(struct hif_opaque_softc *hif_ctx)
+{
+	struct hif_softc *scn;
+	int i;
+
+	QDF_BUG(hif_ctx);
+	scn = HIF_GET_SOFTC(hif_ctx);
+	if (!scn)
+		return -EINVAL;
+
+	for (i = 0; i < scn->ce_count; ++i) {
+		int irq = scn->bus_ops.hif_map_ce_to_irq(scn, i);
+
+		if (irq != scn->wake_irq)
+			disable_irq(irq);
+	}
+
+	return 0;
+}
+
+int hif_apps_enable_irqs_except_wake_irq(struct hif_opaque_softc *hif_ctx)
+{
+	struct hif_softc *scn;
+	int i;
+
+	QDF_BUG(hif_ctx);
+	scn = HIF_GET_SOFTC(hif_ctx);
+	if (!scn)
+		return -EINVAL;
+
+	for (i = 0; i < scn->ce_count; ++i) {
+		int irq = scn->bus_ops.hif_map_ce_to_irq(scn, i);
+
+		if (irq != scn->wake_irq)
+			enable_irq(irq);
+	}
+
+	return 0;
+}
+
 #ifdef WLAN_FEATURE_BMI
 bool hif_needs_bmi(struct hif_opaque_softc *scn)
 {

+ 50 - 9
hif/src/ipcie/if_ipci.c

@@ -307,29 +307,70 @@ int hif_ipci_bus_suspend(struct hif_softc *scn)
 {
 	int ret;
 
+	ret = hif_apps_disable_irqs_except_wake_irq(GET_HIF_OPAQUE_HDL(scn));
+	if (ret) {
+		hif_err("Failed to disable IRQs");
+		goto disable_irq_fail;
+	}
+
 	ret = hif_apps_enable_irq_wake(GET_HIF_OPAQUE_HDL(scn));
+	if (ret) {
+		hif_err("Failed to enable Wake-IRQ");
+		goto enable_wake_irq_fail;
+	}
+
+	if (QDF_IS_STATUS_ERROR(hif_try_complete_tasks(scn))) {
+		hif_err("hif_try_complete_tasks timed-out, so abort suspend");
+		ret = -EBUSY;
+		goto drain_tasks_fail;
+	}
 
-	if (!ret)
-		scn->bus_suspended = true;
+	/*
+	 * In an unlikely case, if draining becomes infinite loop,
+	 * it returns an error, shall abort the bus suspend.
+	 */
+	ret = hif_drain_fw_diag_ce(scn);
+	if (ret) {
+		hif_err("draining fw_diag_ce goes infinite, so abort suspend");
+		goto drain_tasks_fail;
+	}
+
+	scn->bus_suspended = true;
+
+	return 0;
 
+drain_tasks_fail:
+	hif_apps_disable_irq_wake(GET_HIF_OPAQUE_HDL(scn));
+
+enable_wake_irq_fail:
+	hif_apps_enable_irqs_except_wake_irq(GET_HIF_OPAQUE_HDL(scn));
+
+disable_irq_fail:
 	return ret;
 }
 
 int hif_ipci_bus_resume(struct hif_softc *scn)
 {
+	int ret = 0;
+
+	ret = hif_apps_disable_irq_wake(GET_HIF_OPAQUE_HDL(scn));
+	if (ret) {
+		hif_err("Failed to disable Wake-IRQ");
+		goto fail;
+	}
+
+	ret = hif_apps_enable_irqs_except_wake_irq(GET_HIF_OPAQUE_HDL(scn));
+	if (ret)
+		hif_err("Failed to enable IRQs");
+
 	scn->bus_suspended = false;
 
-	return hif_apps_disable_irq_wake(GET_HIF_OPAQUE_HDL(scn));
+fail:
+	return ret;
 }
 
 int hif_ipci_bus_suspend_noirq(struct hif_softc *scn)
 {
-	QDF_STATUS ret;
-
-	ret = hif_try_complete_tasks(scn);
-	if (QDF_IS_STATUS_ERROR(ret))
-		return -EBUSY;
-
 	return 0;
 }