Commit d1b0c338 authored by Carl Huang's avatar Carl Huang Committed by Kalle Valo
Browse files

ath11k: implement suspend for QCA6390 PCI devices



Now that all the needed pieces are in place implement suspend support QCA6390
PCI devices. All other devices will return -EOPNOTSUPP during suspend. The
suspend is implemented by switching the firmware to WoW mode during suspend, so
the firmware will be running on low power mode while host is in suspend.

At the moment we are not able to shutdown and fully power off the device due to
bugs in MHI subsystem, so WoW mode is a workaround for the time being.

During suspend we enable WoW mode, disable CE irq and DP irq, then put MHI to
suspend state.  During resume, driver resumes MHI firstly, then enables CE irq
and dp IRQ, and sends WoW wakeup command to firmware.

Tested-on: QCA6390 hw2.0 PCI WLAN.HST.1.0.1-01740-QCAHSTSWPLZ_V2_TO_X86-1

Signed-off-by: default avatarCarl Huang <cjhuang@codeaurora.org>
Signed-off-by: default avatarKalle Valo <kvalo@codeaurora.org>
Link: https://lore.kernel.org/r/1607708150-21066-11-git-send-email-kvalo@codeaurora.org
parent d578ec2a
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -195,7 +195,7 @@ static bool ath11k_ce_need_shadow_fix(int ce_id)
	return false;
}

static void ath11k_ce_stop_shadow_timers(struct ath11k_base *ab)
void ath11k_ce_stop_shadow_timers(struct ath11k_base *ab)
{
	int i;

+2 −0
Original line number Diff line number Diff line
@@ -190,4 +190,6 @@ int ath11k_ce_map_service_to_pipe(struct ath11k_base *ab, u16 service_id,
int ath11k_ce_attr_attach(struct ath11k_base *ab);
void ath11k_ce_get_shadow_config(struct ath11k_base *ab,
				 u32 **shadow_cfg, u32 *shadow_cfg_len);
void ath11k_ce_stop_shadow_timers(struct ath11k_base *ab);

#endif
+85 −0
Original line number Diff line number Diff line
@@ -13,6 +13,7 @@
#include "dp_rx.h"
#include "debug.h"
#include "hif.h"
#include "wow.h"

unsigned int ath11k_debug_mask;
EXPORT_SYMBOL(ath11k_debug_mask);
@@ -66,6 +67,7 @@ static const struct ath11k_hw_params ath11k_hw_params[] = {
		.supports_shadow_regs = false,
		.idle_ps = false,
		.cold_boot_calib = true,
		.supports_suspend = false,
	},
	{
		.hw_rev = ATH11K_HW_IPQ6018_HW10,
@@ -103,6 +105,7 @@ static const struct ath11k_hw_params ath11k_hw_params[] = {
		.supports_shadow_regs = false,
		.idle_ps = false,
		.cold_boot_calib = true,
		.supports_suspend = false,
	},
	{
		.name = "qca6390 hw2.0",
@@ -139,9 +142,91 @@ static const struct ath11k_hw_params ath11k_hw_params[] = {
		.supports_shadow_regs = true,
		.idle_ps = true,
		.cold_boot_calib = false,
		.supports_suspend = true,
	},
};

int ath11k_core_suspend(struct ath11k_base *ab)
{
	int ret;

	if (!ab->hw_params.supports_suspend)
		return -EOPNOTSUPP;

	/* TODO: there can frames in queues so for now add delay as a hack.
	 * Need to implement to handle and remove this delay.
	 */
	msleep(500);

	ret = ath11k_dp_rx_pktlog_stop(ab, true);
	if (ret) {
		ath11k_warn(ab, "failed to stop dp rx (and timer) pktlog during suspend: %d\n",
			    ret);
		return ret;
	}

	ret = ath11k_wow_enable(ab);
	if (ret) {
		ath11k_warn(ab, "failed to enable wow during suspend: %d\n", ret);
		return ret;
	}

	ret = ath11k_dp_rx_pktlog_stop(ab, false);
	if (ret) {
		ath11k_warn(ab, "failed to stop dp rx pktlog during suspend: %d\n",
			    ret);
		return ret;
	}

	ath11k_ce_stop_shadow_timers(ab);
	ath11k_dp_stop_shadow_timers(ab);

	ath11k_hif_irq_disable(ab);
	ath11k_hif_ce_irq_disable(ab);

	ret = ath11k_hif_suspend(ab);
	if (!ret) {
		ath11k_warn(ab, "failed to suspend hif: %d\n", ret);
		return ret;
	}

	return 0;
}
EXPORT_SYMBOL(ath11k_core_suspend);

int ath11k_core_resume(struct ath11k_base *ab)
{
	int ret;

	if (!ab->hw_params.supports_suspend)
		return -EOPNOTSUPP;

	ret = ath11k_hif_resume(ab);
	if (ret) {
		ath11k_warn(ab, "failed to resume hif during resume: %d\n", ret);
		return ret;
	}

	ath11k_hif_ce_irq_enable(ab);
	ath11k_hif_irq_enable(ab);

	ret = ath11k_dp_rx_pktlog_start(ab);
	if (ret) {
		ath11k_warn(ab, "failed to start rx pktlog during resume: %d\n",
			    ret);
		return ret;
	}

	ret = ath11k_wow_wakeup(ab);
	if (ret) {
		ath11k_warn(ab, "failed to wakeup wow during resume: %d\n", ret);
		return ret;
	}

	return 0;
}
EXPORT_SYMBOL(ath11k_core_resume);

int ath11k_core_check_dt(struct ath11k_base *ab)
{
	size_t max_len = sizeof(ab->qmi.target.bdf_ext);
+2 −0
Original line number Diff line number Diff line
@@ -897,6 +897,8 @@ void ath11k_core_free_bdf(struct ath11k_base *ab, struct ath11k_board_data *bd);
int ath11k_core_check_dt(struct ath11k_base *ath11k);

void ath11k_core_halt(struct ath11k *ar);
int ath11k_core_resume(struct ath11k_base *ab);
int ath11k_core_suspend(struct ath11k_base *ab);

const struct firmware *ath11k_core_firmware_request(struct ath11k_base *ab,
						    const char *filename);
+1 −1
Original line number Diff line number Diff line
@@ -304,7 +304,7 @@ int ath11k_dp_srng_setup(struct ath11k_base *ab, struct dp_srng *ring,
	return 0;
}

static void ath11k_dp_stop_shadow_timers(struct ath11k_base *ab)
void ath11k_dp_stop_shadow_timers(struct ath11k_base *ab)
{
	int i;

Loading