Commit 59317417 authored by Baochen Qiang's avatar Baochen Qiang Committed by Kalle Valo
Browse files

wifi: ath12k: implement WoW enable and wakeup commands



Implement WoW enable and WoW wakeup commands which are needed
for suspend/resume.

Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.0-03427-QCAHMTSWPL_V1.0_V2.0_SILICONZ-1.15378.4

Signed-off-by: default avatarBaochen Qiang <quic_bqiang@quicinc.com>
Acked-by: default avatarJeff Johnson <quic_jjohnson@quicinc.com>
Signed-off-by: default avatarKalle Valo <quic_kvalo@quicinc.com>
Link: https://patch.msgid.link/20240604055407.12506-3-quic_bqiang@quicinc.com
parent 3216b7bc
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -26,6 +26,7 @@ ath12k-y += core.o \
ath12k-$(CONFIG_ATH12K_DEBUGFS) += debugfs.o
ath12k-$(CONFIG_ACPI) += acpi.o
ath12k-$(CONFIG_ATH12K_TRACING) += trace.o
ath12k-$(CONFIG_PM) += wow.o

# for tracing framework to find trace.h
CFLAGS_trace.o := -I$(src)
+2 −0
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@
#include "hif.h"
#include "fw.h"
#include "debugfs.h"
#include "wow.h"

unsigned int ath12k_debug_mask;
module_param_named(debug_mask, ath12k_debug_mask, uint, 0644);
@@ -1285,6 +1286,7 @@ struct ath12k_base *ath12k_core_alloc(struct device *dev, size_t priv_size,
	timer_setup(&ab->rx_replenish_retry, ath12k_ce_rx_replenish_retry, 0);
	init_completion(&ab->htc_suspend);
	init_completion(&ab->restart_completed);
	init_completion(&ab->wow.wakeup_completed);

	ab->dev = dev;
	ab->hif.bus = bus;
+4 −0
Original line number Diff line number Diff line
@@ -765,6 +765,10 @@ struct ath12k_base {
		const struct ath12k_hif_ops *ops;
	} hif;

	struct {
		struct completion wakeup_completed;
	} wow;

	struct ath12k_ce ce;
	struct timer_list rx_replenish_retry;
	struct ath12k_hal hal;
+105 −0
Original line number Diff line number Diff line
@@ -7030,6 +7030,66 @@ static void ath12k_wmi_twt_disable_event(struct ath12k_base *ab,
	kfree(tb);
}

static int ath12k_wmi_wow_wakeup_host_parse(struct ath12k_base *ab,
					    u16 tag, u16 len,
					    const void *ptr, void *data)
{
	const struct wmi_wow_ev_pg_fault_param *pf_param;
	const struct wmi_wow_ev_param *param;
	struct wmi_wow_ev_arg *arg = data;
	int pf_len;

	switch (tag) {
	case WMI_TAG_WOW_EVENT_INFO:
		param = ptr;
		arg->wake_reason = le32_to_cpu(param->wake_reason);
		ath12k_dbg(ab, ATH12K_DBG_WMI, "wow wakeup host reason %d %s\n",
			   arg->wake_reason, wow_reason(arg->wake_reason));
		break;

	case WMI_TAG_ARRAY_BYTE:
		if (arg && arg->wake_reason == WOW_REASON_PAGE_FAULT) {
			pf_param = ptr;
			pf_len = le32_to_cpu(pf_param->len);
			if (pf_len > len - sizeof(pf_len) ||
			    pf_len < 0) {
				ath12k_warn(ab, "invalid wo reason page fault buffer len %d\n",
					    pf_len);
				return -EINVAL;
			}
			ath12k_dbg(ab, ATH12K_DBG_WMI, "wow_reason_page_fault len %d\n",
				   pf_len);
			ath12k_dbg_dump(ab, ATH12K_DBG_WMI,
					"wow_reason_page_fault packet present",
					"wow_pg_fault ",
					pf_param->data,
					pf_len);
		}
		break;
	default:
		break;
	}

	return 0;
}

static void ath12k_wmi_event_wow_wakeup_host(struct ath12k_base *ab, struct sk_buff *skb)
{
	struct wmi_wow_ev_arg arg = { };
	int ret;

	ret = ath12k_wmi_tlv_iter(ab, skb->data, skb->len,
				  ath12k_wmi_wow_wakeup_host_parse,
				  &arg);
	if (ret) {
		ath12k_warn(ab, "failed to parse wmi wow wakeup host event tlv: %d\n",
			    ret);
		return;
	}

	complete(&ab->wow.wakeup_completed);
}

static void ath12k_wmi_op_rx(struct ath12k_base *ab, struct sk_buff *skb)
{
	struct wmi_cmd_hdr *cmd_hdr;
@@ -7150,6 +7210,9 @@ static void ath12k_wmi_op_rx(struct ath12k_base *ab, struct sk_buff *skb)
	case WMI_DIAG_EVENTID:
		ath12k_wmi_diag_event(ab, skb);
		break;
	case WMI_WOW_WAKEUP_HOST_EVENTID:
		ath12k_wmi_event_wow_wakeup_host(ab, skb);
		break;
	/* TODO: Add remaining events */
	default:
		ath12k_dbg(ab, ATH12K_DBG_WMI, "Unknown eventid: 0x%x\n", id);
@@ -7359,3 +7422,45 @@ void ath12k_wmi_detach(struct ath12k_base *ab)

	ath12k_wmi_free_dbring_caps(ab);
}

int ath12k_wmi_wow_host_wakeup_ind(struct ath12k *ar)
{
	struct wmi_wow_host_wakeup_cmd *cmd;
	struct sk_buff *skb;
	size_t len;

	len = sizeof(*cmd);
	skb = ath12k_wmi_alloc_skb(ar->wmi->wmi_ab, len);
	if (!skb)
		return -ENOMEM;

	cmd = (struct wmi_wow_host_wakeup_cmd *)skb->data;
	cmd->tlv_header = ath12k_wmi_tlv_cmd_hdr(WMI_TAG_WOW_HOSTWAKEUP_FROM_SLEEP_CMD,
						 sizeof(*cmd));

	ath12k_dbg(ar->ab, ATH12K_DBG_WMI, "wmi tlv wow host wakeup ind\n");

	return ath12k_wmi_cmd_send(ar->wmi, skb, WMI_WOW_HOSTWAKEUP_FROM_SLEEP_CMDID);
}

int ath12k_wmi_wow_enable(struct ath12k *ar)
{
	struct wmi_wow_enable_cmd *cmd;
	struct sk_buff *skb;
	int len;

	len = sizeof(*cmd);
	skb = ath12k_wmi_alloc_skb(ar->wmi->wmi_ab, len);
	if (!skb)
		return -ENOMEM;

	cmd = (struct wmi_wow_enable_cmd *)skb->data;
	cmd->tlv_header = ath12k_wmi_tlv_cmd_hdr(WMI_TAG_WOW_ENABLE_CMD,
						 sizeof(*cmd));

	cmd->enable = cpu_to_le32(1);
	cmd->pause_iface_config = cpu_to_le32(WOW_IFACE_PAUSE_ENABLED);
	ath12k_dbg(ar->ab, ATH12K_DBG_WMI, "wmi tlv wow enable\n");

	return ath12k_wmi_cmd_send(ar->wmi, skb, WMI_WOW_ENABLE_CMDID);
}
+174 −0
Original line number Diff line number Diff line
@@ -4902,6 +4902,178 @@ struct wmi_twt_disable_event {
	__le32 status;
} __packed;

/* WOW structures */
enum wmi_wow_wakeup_event {
	WOW_BMISS_EVENT = 0,
	WOW_BETTER_AP_EVENT,
	WOW_DEAUTH_RECVD_EVENT,
	WOW_MAGIC_PKT_RECVD_EVENT,
	WOW_GTK_ERR_EVENT,
	WOW_FOURWAY_HSHAKE_EVENT,
	WOW_EAPOL_RECVD_EVENT,
	WOW_NLO_DETECTED_EVENT,
	WOW_DISASSOC_RECVD_EVENT,
	WOW_PATTERN_MATCH_EVENT,
	WOW_CSA_IE_EVENT,
	WOW_PROBE_REQ_WPS_IE_EVENT,
	WOW_AUTH_REQ_EVENT,
	WOW_ASSOC_REQ_EVENT,
	WOW_HTT_EVENT,
	WOW_RA_MATCH_EVENT,
	WOW_HOST_AUTO_SHUTDOWN_EVENT,
	WOW_IOAC_MAGIC_EVENT,
	WOW_IOAC_SHORT_EVENT,
	WOW_IOAC_EXTEND_EVENT,
	WOW_IOAC_TIMER_EVENT,
	WOW_DFS_PHYERR_RADAR_EVENT,
	WOW_BEACON_EVENT,
	WOW_CLIENT_KICKOUT_EVENT,
	WOW_EVENT_MAX,
};

enum wmi_wow_interface_cfg {
	WOW_IFACE_PAUSE_ENABLED,
	WOW_IFACE_PAUSE_DISABLED
};

#define C2S(x) case x: return #x

static inline const char *wow_wakeup_event(enum wmi_wow_wakeup_event ev)
{
	switch (ev) {
	C2S(WOW_BMISS_EVENT);
	C2S(WOW_BETTER_AP_EVENT);
	C2S(WOW_DEAUTH_RECVD_EVENT);
	C2S(WOW_MAGIC_PKT_RECVD_EVENT);
	C2S(WOW_GTK_ERR_EVENT);
	C2S(WOW_FOURWAY_HSHAKE_EVENT);
	C2S(WOW_EAPOL_RECVD_EVENT);
	C2S(WOW_NLO_DETECTED_EVENT);
	C2S(WOW_DISASSOC_RECVD_EVENT);
	C2S(WOW_PATTERN_MATCH_EVENT);
	C2S(WOW_CSA_IE_EVENT);
	C2S(WOW_PROBE_REQ_WPS_IE_EVENT);
	C2S(WOW_AUTH_REQ_EVENT);
	C2S(WOW_ASSOC_REQ_EVENT);
	C2S(WOW_HTT_EVENT);
	C2S(WOW_RA_MATCH_EVENT);
	C2S(WOW_HOST_AUTO_SHUTDOWN_EVENT);
	C2S(WOW_IOAC_MAGIC_EVENT);
	C2S(WOW_IOAC_SHORT_EVENT);
	C2S(WOW_IOAC_EXTEND_EVENT);
	C2S(WOW_IOAC_TIMER_EVENT);
	C2S(WOW_DFS_PHYERR_RADAR_EVENT);
	C2S(WOW_BEACON_EVENT);
	C2S(WOW_CLIENT_KICKOUT_EVENT);
	C2S(WOW_EVENT_MAX);
	default:
		return NULL;
	}
}

enum wmi_wow_wake_reason {
	WOW_REASON_UNSPECIFIED = -1,
	WOW_REASON_NLOD = 0,
	WOW_REASON_AP_ASSOC_LOST,
	WOW_REASON_LOW_RSSI,
	WOW_REASON_DEAUTH_RECVD,
	WOW_REASON_DISASSOC_RECVD,
	WOW_REASON_GTK_HS_ERR,
	WOW_REASON_EAP_REQ,
	WOW_REASON_FOURWAY_HS_RECV,
	WOW_REASON_TIMER_INTR_RECV,
	WOW_REASON_PATTERN_MATCH_FOUND,
	WOW_REASON_RECV_MAGIC_PATTERN,
	WOW_REASON_P2P_DISC,
	WOW_REASON_WLAN_HB,
	WOW_REASON_CSA_EVENT,
	WOW_REASON_PROBE_REQ_WPS_IE_RECV,
	WOW_REASON_AUTH_REQ_RECV,
	WOW_REASON_ASSOC_REQ_RECV,
	WOW_REASON_HTT_EVENT,
	WOW_REASON_RA_MATCH,
	WOW_REASON_HOST_AUTO_SHUTDOWN,
	WOW_REASON_IOAC_MAGIC_EVENT,
	WOW_REASON_IOAC_SHORT_EVENT,
	WOW_REASON_IOAC_EXTEND_EVENT,
	WOW_REASON_IOAC_TIMER_EVENT,
	WOW_REASON_ROAM_HO,
	WOW_REASON_DFS_PHYERR_RADADR_EVENT,
	WOW_REASON_BEACON_RECV,
	WOW_REASON_CLIENT_KICKOUT_EVENT,
	WOW_REASON_PAGE_FAULT = 0x3a,
	WOW_REASON_DEBUG_TEST = 0xFF,
};

static inline const char *wow_reason(enum wmi_wow_wake_reason reason)
{
	switch (reason) {
	C2S(WOW_REASON_UNSPECIFIED);
	C2S(WOW_REASON_NLOD);
	C2S(WOW_REASON_AP_ASSOC_LOST);
	C2S(WOW_REASON_LOW_RSSI);
	C2S(WOW_REASON_DEAUTH_RECVD);
	C2S(WOW_REASON_DISASSOC_RECVD);
	C2S(WOW_REASON_GTK_HS_ERR);
	C2S(WOW_REASON_EAP_REQ);
	C2S(WOW_REASON_FOURWAY_HS_RECV);
	C2S(WOW_REASON_TIMER_INTR_RECV);
	C2S(WOW_REASON_PATTERN_MATCH_FOUND);
	C2S(WOW_REASON_RECV_MAGIC_PATTERN);
	C2S(WOW_REASON_P2P_DISC);
	C2S(WOW_REASON_WLAN_HB);
	C2S(WOW_REASON_CSA_EVENT);
	C2S(WOW_REASON_PROBE_REQ_WPS_IE_RECV);
	C2S(WOW_REASON_AUTH_REQ_RECV);
	C2S(WOW_REASON_ASSOC_REQ_RECV);
	C2S(WOW_REASON_HTT_EVENT);
	C2S(WOW_REASON_RA_MATCH);
	C2S(WOW_REASON_HOST_AUTO_SHUTDOWN);
	C2S(WOW_REASON_IOAC_MAGIC_EVENT);
	C2S(WOW_REASON_IOAC_SHORT_EVENT);
	C2S(WOW_REASON_IOAC_EXTEND_EVENT);
	C2S(WOW_REASON_IOAC_TIMER_EVENT);
	C2S(WOW_REASON_ROAM_HO);
	C2S(WOW_REASON_DFS_PHYERR_RADADR_EVENT);
	C2S(WOW_REASON_BEACON_RECV);
	C2S(WOW_REASON_CLIENT_KICKOUT_EVENT);
	C2S(WOW_REASON_PAGE_FAULT);
	C2S(WOW_REASON_DEBUG_TEST);
	default:
		return NULL;
	}
}

#undef C2S

struct wmi_wow_enable_cmd {
	__le32 tlv_header;
	__le32 enable;
	__le32 pause_iface_config;
	__le32 flags;
}  __packed;

struct wmi_wow_host_wakeup_cmd {
	__le32 tlv_header;
	__le32 reserved;
} __packed;

struct wmi_wow_ev_param {
	__le32 vdev_id;
	__le32 flag;
	__le32 wake_reason;
	__le32 data_len;
} __packed;

struct wmi_wow_ev_pg_fault_param {
	__le32 len;
	u8 data[];
} __packed;

struct wmi_wow_ev_arg {
	enum wmi_wow_wake_reason wake_reason;
};

void ath12k_wmi_init_qcn9274(struct ath12k_base *ab,
			     struct ath12k_wmi_resource_config_arg *config);
void ath12k_wmi_init_wcn7850(struct ath12k_base *ab,
@@ -5054,4 +5226,6 @@ ath12k_wmi_mac_phy_get_hw_link_id(const struct ath12k_wmi_mac_phy_caps_params *p
			     WMI_CAPS_PARAMS_HW_LINK_ID);
}

int ath12k_wmi_wow_host_wakeup_ind(struct ath12k *ar);
int ath12k_wmi_wow_enable(struct ath12k *ar);
#endif
Loading