diff mbox series

wifi: ath11k: Suspend hardware before firmware mode off for WCN6750

Message ID 20241029083340.3010798-1-quic_bpothuno@quicinc.com (mailing list archive)
State Accepted
Delegated to: Jeff Johnson
Headers show
Series wifi: ath11k: Suspend hardware before firmware mode off for WCN6750 | expand

Commit Message

Balaji Pothunoori Oct. 29, 2024, 8:33 a.m. UTC
During rmmod, the ath11k host driver sends a QMI MODE OFF command
to firmware.
As part of this command, firmware initiates WLAN de-initialization
and accesses certain UMAC registers during this process.
Currently, on WCN6750 WLAN hardware, the system is in a sleep state when
firmware receives the QMI MODE OFF command.
This results in a firmware/hardware reset while accessing the UMAC hardware
registers during sleep state.

To avoid this, add logic to send WCN6750 hardware specific
WMI_PDEV_SUSPEND_AND_DISABLE_INTR command to firmware prior to sending
the QMI MODE OFF command.
This will cause firmware to cease all activities and put the device in
a powered-on state that prevents access to registers which have been
powered off.

Signed-off-by: Balaji Pothunoori <quic_bpothuno@quicinc.com>
---
 drivers/net/wireless/ath/ath11k/core.c | 45 ++++++++++++++++++++++++++
 drivers/net/wireless/ath/ath11k/hw.h   |  1 +
 2 files changed, 46 insertions(+)

Comments

Jeff Johnson Oct. 29, 2024, 3:14 p.m. UTC | #1
On 10/29/2024 1:33 AM, Balaji Pothunoori wrote:
> During rmmod, the ath11k host driver sends a QMI MODE OFF command
> to firmware.
> As part of this command, firmware initiates WLAN de-initialization
> and accesses certain UMAC registers during this process.
> Currently, on WCN6750 WLAN hardware, the system is in a sleep state when
> firmware receives the QMI MODE OFF command.
> This results in a firmware/hardware reset while accessing the UMAC hardware
> registers during sleep state.
> 
> To avoid this, add logic to send WCN6750 hardware specific
> WMI_PDEV_SUSPEND_AND_DISABLE_INTR command to firmware prior to sending
> the QMI MODE OFF command.
> This will cause firmware to cease all activities and put the device in
> a powered-on state that prevents access to registers which have been
> powered off.
> 
> Signed-off-by: Balaji Pothunoori <quic_bpothuno@quicinc.com>
Acked-by: Jeff Johnson <quic_jjohnson@quicinc.com>
Jeff Johnson Nov. 4, 2024, 3:43 p.m. UTC | #2
On Tue, 29 Oct 2024 14:03:40 +0530, Balaji Pothunoori wrote:
> During rmmod, the ath11k host driver sends a QMI MODE OFF command
> to firmware.
> As part of this command, firmware initiates WLAN de-initialization
> and accesses certain UMAC registers during this process.
> Currently, on WCN6750 WLAN hardware, the system is in a sleep state when
> firmware receives the QMI MODE OFF command.
> This results in a firmware/hardware reset while accessing the UMAC hardware
> registers during sleep state.
> 
> [...]

Applied, thanks!

[1/1] wifi: ath11k: Suspend hardware before firmware mode off for WCN6750
      commit: b39f8deb8df9664f34aebd9c6c8e234a7417041b

Best regards,
diff mbox series

Patch

diff --git a/drivers/net/wireless/ath/ath11k/core.c b/drivers/net/wireless/ath/ath11k/core.c
index be67382c00f6..a9aefb1a705d 100644
--- a/drivers/net/wireless/ath/ath11k/core.c
+++ b/drivers/net/wireless/ath/ath11k/core.c
@@ -123,6 +123,7 @@  static const struct ath11k_hw_params ath11k_hw_params[] = {
 		.tx_ring_size = DP_TCL_DATA_RING_SIZE,
 		.smp2p_wow_exit = false,
 		.support_dual_stations = false,
+		.pdev_suspend = false,
 	},
 	{
 		.hw_rev = ATH11K_HW_IPQ6018_HW10,
@@ -207,6 +208,7 @@  static const struct ath11k_hw_params ath11k_hw_params[] = {
 		.smp2p_wow_exit = false,
 		.support_fw_mac_sequence = false,
 		.support_dual_stations = false,
+		.pdev_suspend = false,
 	},
 	{
 		.name = "qca6390 hw2.0",
@@ -296,6 +298,7 @@  static const struct ath11k_hw_params ath11k_hw_params[] = {
 		.smp2p_wow_exit = false,
 		.support_fw_mac_sequence = true,
 		.support_dual_stations = true,
+		.pdev_suspend = false,
 	},
 	{
 		.name = "qcn9074 hw1.0",
@@ -379,6 +382,7 @@  static const struct ath11k_hw_params ath11k_hw_params[] = {
 		.smp2p_wow_exit = false,
 		.support_fw_mac_sequence = false,
 		.support_dual_stations = false,
+		.pdev_suspend = false,
 	},
 	{
 		.name = "wcn6855 hw2.0",
@@ -468,6 +472,7 @@  static const struct ath11k_hw_params ath11k_hw_params[] = {
 		.smp2p_wow_exit = false,
 		.support_fw_mac_sequence = true,
 		.support_dual_stations = true,
+		.pdev_suspend = false,
 	},
 	{
 		.name = "wcn6855 hw2.1",
@@ -555,6 +560,7 @@  static const struct ath11k_hw_params ath11k_hw_params[] = {
 		.smp2p_wow_exit = false,
 		.support_fw_mac_sequence = true,
 		.support_dual_stations = true,
+		.pdev_suspend = false,
 	},
 	{
 		.name = "wcn6750 hw1.0",
@@ -637,6 +643,7 @@  static const struct ath11k_hw_params ath11k_hw_params[] = {
 		.smp2p_wow_exit = true,
 		.support_fw_mac_sequence = true,
 		.support_dual_stations = false,
+		.pdev_suspend = true,
 	},
 	{
 		.hw_rev = ATH11K_HW_IPQ5018_HW10,
@@ -719,6 +726,7 @@  static const struct ath11k_hw_params ath11k_hw_params[] = {
 		.smp2p_wow_exit = false,
 		.support_fw_mac_sequence = false,
 		.support_dual_stations = false,
+		.pdev_suspend = false,
 	},
 	{
 		.name = "qca2066 hw2.1",
@@ -808,6 +816,7 @@  static const struct ath11k_hw_params ath11k_hw_params[] = {
 		.smp2p_wow_exit = false,
 		.support_fw_mac_sequence = true,
 		.support_dual_stations = true,
+		.pdev_suspend = false,
 	},
 };
 
@@ -1669,11 +1678,47 @@  static int ath11k_core_pdev_create(struct ath11k_base *ab)
 	return ret;
 }
 
+static void ath11k_core_pdev_suspend_target(struct ath11k_base *ab)
+{
+	struct ath11k *ar;
+	struct ath11k_pdev *pdev;
+	unsigned long time_left;
+	int ret;
+	int i;
+
+	if (!ab->hw_params.pdev_suspend)
+		return;
+
+	for (i = 0; i < ab->num_radios; i++) {
+		pdev = &ab->pdevs[i];
+		ar = pdev->ar;
+
+		reinit_completion(&ab->htc_suspend);
+
+		ret = ath11k_wmi_pdev_suspend(ar, WMI_PDEV_SUSPEND_AND_DISABLE_INTR,
+					      pdev->pdev_id);
+		if (ret) {
+			ath11k_warn(ab, "could not suspend target :%d\n", ret);
+			/* pointless to try other pdevs */
+			return;
+		}
+
+		time_left = wait_for_completion_timeout(&ab->htc_suspend, 3 * HZ);
+
+		if (!time_left) {
+			ath11k_warn(ab, "suspend timed out - target pause event never came\n");
+			/* pointless to try other pdevs */
+			return;
+		}
+	}
+}
+
 static void ath11k_core_pdev_destroy(struct ath11k_base *ab)
 {
 	ath11k_spectral_deinit(ab);
 	ath11k_thermal_unregister(ab);
 	ath11k_mac_unregister(ab);
+	ath11k_core_pdev_suspend_target(ab);
 	ath11k_hif_irq_disable(ab);
 	ath11k_dp_pdev_free(ab);
 	ath11k_debugfs_pdev_destroy(ab);
diff --git a/drivers/net/wireless/ath/ath11k/hw.h b/drivers/net/wireless/ath/ath11k/hw.h
index 300322535766..52d9f4c13b13 100644
--- a/drivers/net/wireless/ath/ath11k/hw.h
+++ b/drivers/net/wireless/ath/ath11k/hw.h
@@ -227,6 +227,7 @@  struct ath11k_hw_params {
 	bool smp2p_wow_exit;
 	bool support_fw_mac_sequence;
 	bool support_dual_stations;
+	bool pdev_suspend;
 };
 
 struct ath11k_hw_ops {