From bfc2dc7a69a41ba5ee268dac2c464d6b0e50eba7 Mon Sep 17 00:00:00 2001 From: Vladimir Kondratiev Date: Mon, 30 Mar 2015 11:28:50 +0300 Subject: [PATCH 01/20] wil6210: fw debug mode refactor module parameter debug_fw to act as "fw debug mode", where driver do nothing but allow card memory access. Signed-off-by: Vladimir Kondratiev Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/wil6210/main.c | 15 +++++++++++++++ drivers/net/wireless/ath/wil6210/netdev.c | 5 +++++ drivers/net/wireless/ath/wil6210/pcie_bus.c | 6 ------ drivers/net/wireless/ath/wil6210/wil6210.h | 1 + 4 files changed, 21 insertions(+), 6 deletions(-) diff --git a/drivers/net/wireless/ath/wil6210/main.c b/drivers/net/wireless/ath/wil6210/main.c index c2a238426425..0623d8c98956 100644 --- a/drivers/net/wireless/ath/wil6210/main.c +++ b/drivers/net/wireless/ath/wil6210/main.c @@ -25,6 +25,10 @@ #define WAIT_FOR_DISCONNECT_TIMEOUT_MS 2000 #define WAIT_FOR_DISCONNECT_INTERVAL_MS 10 +bool debug_fw; /* = false; */ +module_param(debug_fw, bool, S_IRUGO); +MODULE_PARM_DESC(debug_fw, " do not perform card reset. For FW debug"); + bool no_fw_recovery; module_param(no_fw_recovery, bool, S_IRUGO | S_IWUSR); MODULE_PARM_DESC(no_fw_recovery, " disable automatic FW error recovery"); @@ -686,6 +690,17 @@ int wil_reset(struct wil6210_priv *wil, bool load_fw) WARN_ON(!mutex_is_locked(&wil->mutex)); WARN_ON(test_bit(wil_status_napi_en, wil->status)); + if (debug_fw) { + static const u8 mac[ETH_ALEN] = { + 0x00, 0xde, 0xad, 0x12, 0x34, 0x56, + }; + struct net_device *ndev = wil_to_ndev(wil); + + ether_addr_copy(ndev->perm_addr, mac); + ether_addr_copy(ndev->dev_addr, ndev->perm_addr); + return 0; + } + cancel_work_sync(&wil->disconnect_worker); wil6210_disconnect(wil, NULL, WLAN_REASON_DEAUTH_LEAVING, false); wil_bcast_fini(wil); diff --git a/drivers/net/wireless/ath/wil6210/netdev.c b/drivers/net/wireless/ath/wil6210/netdev.c index f2f7ea29558e..6042f61b016c 100644 --- a/drivers/net/wireless/ath/wil6210/netdev.c +++ b/drivers/net/wireless/ath/wil6210/netdev.c @@ -24,6 +24,11 @@ static int wil_open(struct net_device *ndev) wil_dbg_misc(wil, "%s()\n", __func__); + if (debug_fw) { + wil_err(wil, "%s() while in debug_fw mode\n", __func__); + return -EINVAL; + } + return wil_up(wil); } diff --git a/drivers/net/wireless/ath/wil6210/pcie_bus.c b/drivers/net/wireless/ath/wil6210/pcie_bus.c index 109986114abf..58c79166a6d1 100644 --- a/drivers/net/wireless/ath/wil6210/pcie_bus.c +++ b/drivers/net/wireless/ath/wil6210/pcie_bus.c @@ -27,10 +27,6 @@ MODULE_PARM_DESC(use_msi, " Use MSI interrupt: " "0 - don't, 1 - (default) - single, or 3"); -static bool debug_fw; /* = false; */ -module_param(debug_fw, bool, S_IRUGO); -MODULE_PARM_DESC(debug_fw, " load driver if FW not ready. For FW debug"); - static void wil_set_capabilities(struct wil6210_priv *wil) { @@ -133,8 +129,6 @@ static int wil_if_pcie_enable(struct wil6210_priv *wil) mutex_lock(&wil->mutex); rc = wil_reset(wil, false); mutex_unlock(&wil->mutex); - if (debug_fw) - rc = 0; if (rc) goto release_irq; diff --git a/drivers/net/wireless/ath/wil6210/wil6210.h b/drivers/net/wireless/ath/wil6210/wil6210.h index 4310972c9e16..f4681e3975ff 100644 --- a/drivers/net/wireless/ath/wil6210/wil6210.h +++ b/drivers/net/wireless/ath/wil6210/wil6210.h @@ -29,6 +29,7 @@ extern unsigned short rx_ring_overflow_thrsh; extern int agg_wsize; extern u32 vring_idle_trsh; extern bool rx_align_2; +extern bool debug_fw; #define WIL_NAME "wil6210" #define WIL_FW_NAME "wil6210.fw" /* code */ From db8adcbf5e7a0d5e6577b53214e6655b81cea9df Mon Sep 17 00:00:00 2001 From: Vladimir Kondratiev Date: Mon, 30 Mar 2015 11:28:51 +0300 Subject: [PATCH 02/20] wil6210: debug [add|del]_key operations Provide info for [add|del]_key. Signed-off-by: Vladimir Kondratiev Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/wil6210/cfg80211.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/net/wireless/ath/wil6210/cfg80211.c b/drivers/net/wireless/ath/wil6210/cfg80211.c index b97172667bc7..6cc432d0b663 100644 --- a/drivers/net/wireless/ath/wil6210/cfg80211.c +++ b/drivers/net/wireless/ath/wil6210/cfg80211.c @@ -569,6 +569,9 @@ static int wil_cfg80211_add_key(struct wiphy *wiphy, { struct wil6210_priv *wil = wiphy_to_wil(wiphy); + wil_dbg_misc(wil, "%s(%pM[%d] %s)\n", __func__, mac_addr, key_index, + pairwise ? "PTK" : "GTK"); + /* group key is not used */ if (!pairwise) return 0; @@ -584,6 +587,9 @@ static int wil_cfg80211_del_key(struct wiphy *wiphy, { struct wil6210_priv *wil = wiphy_to_wil(wiphy); + wil_dbg_misc(wil, "%s(%pM[%d] %s)\n", __func__, mac_addr, key_index, + pairwise ? "PTK" : "GTK"); + /* group key is not used */ if (!pairwise) return 0; From de9084ef47eb4014a7ca3ccbffce285ddc2130ce Mon Sep 17 00:00:00 2001 From: Vladimir Kondratiev Date: Mon, 30 Mar 2015 11:28:52 +0300 Subject: [PATCH 03/20] wil6210: trace disconnect source Trace where wil6210_disconnect() is called from. Signed-off-by: Vladimir Kondratiev Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/wil6210/cfg80211.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/net/wireless/ath/wil6210/cfg80211.c b/drivers/net/wireless/ath/wil6210/cfg80211.c index 6cc432d0b663..37d5338ecfc7 100644 --- a/drivers/net/wireless/ath/wil6210/cfg80211.c +++ b/drivers/net/wireless/ath/wil6210/cfg80211.c @@ -507,6 +507,8 @@ static int wil_cfg80211_disconnect(struct wiphy *wiphy, int rc; struct wil6210_priv *wil = wiphy_to_wil(wiphy); + wil_dbg_misc(wil, "%s(reason=%d)\n", __func__, reason_code); + rc = wmi_send(wil, WMI_DISCONNECT_CMDID, NULL, 0); return rc; @@ -836,6 +838,9 @@ static int wil_cfg80211_del_station(struct wiphy *wiphy, { struct wil6210_priv *wil = wiphy_to_wil(wiphy); + wil_dbg_misc(wil, "%s(%pM, reason=%d)\n", __func__, params->mac, + params->reason_code); + mutex_lock(&wil->mutex); wil6210_disconnect(wil, params->mac, params->reason_code, false); mutex_unlock(&wil->mutex); From e853c93bd8e2d92c6586ceed15542c21c8766d54 Mon Sep 17 00:00:00 2001 From: Vladimir Kondratiev Date: Mon, 30 Mar 2015 11:28:53 +0300 Subject: [PATCH 04/20] wil6210: stop_ap to leave interface closed cfg80211_ops.stop_ap supposed to have interface carried turned off as post condition. Fulfill this requirement. Signed-off-by: Vladimir Kondratiev Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/wil6210/cfg80211.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/drivers/net/wireless/ath/wil6210/cfg80211.c b/drivers/net/wireless/ath/wil6210/cfg80211.c index 37d5338ecfc7..ea331e407e7f 100644 --- a/drivers/net/wireless/ath/wil6210/cfg80211.c +++ b/drivers/net/wireless/ath/wil6210/cfg80211.c @@ -822,13 +822,9 @@ static int wil_cfg80211_stop_ap(struct wiphy *wiphy, wmi_pcp_stop(wil); __wil_down(wil); - __wil_up(wil); mutex_unlock(&wil->mutex); - /* some functions above might fail (e.g. __wil_up). Nevertheless, we - * return success because AP has stopped - */ return 0; } From 137ce6104ffec1a86c8c14daf1cca15f8ef29d2c Mon Sep 17 00:00:00 2001 From: Vladimir Kondratiev Date: Mon, 30 Mar 2015 11:28:54 +0300 Subject: [PATCH 05/20] wil6210: update FW file name Firmware "board" file name has changed from wil6210.board to wil6210.brd by the FW generation tools. Reflect this in the driver. Signed-off-by: Vladimir Kondratiev Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/wil6210/wil6210.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/wil6210/wil6210.h b/drivers/net/wireless/ath/wil6210/wil6210.h index f4681e3975ff..abb2080d3fa1 100644 --- a/drivers/net/wireless/ath/wil6210/wil6210.h +++ b/drivers/net/wireless/ath/wil6210/wil6210.h @@ -33,7 +33,7 @@ extern bool debug_fw; #define WIL_NAME "wil6210" #define WIL_FW_NAME "wil6210.fw" /* code */ -#define WIL_FW2_NAME "wil6210.board" /* board & radio parameters */ +#define WIL_FW2_NAME "wil6210.brd" /* board & radio parameters */ #define WIL_MAX_BUS_REQUEST_KBPS 800000 /* ~6.1Gbps */ From d915354675a3baae834c9aae845bbb7a0fcd453d Mon Sep 17 00:00:00 2001 From: Kalle Valo Date: Tue, 28 Apr 2015 20:19:30 +0300 Subject: [PATCH 06/20] ath10k: add ATH10K_FW_FEATURE_IGNORE_OTP_RESULT qca6174 otp binary seems to always return an error to the host, even if the calibration succeeded. Add a firmware feature flag to detect if the firmware image which have this problem and workaround the issue in ath10k by ignoring the error code. I was also considering making this hw specific flag but as this is strictly a firmware issue it's best to handle this via a firmware feature flag so that it will be easy to disable the workaround. Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/core.c | 4 +++- drivers/net/wireless/ath/ath10k/core.h | 3 +++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c index 987b266278a8..bcccae19325d 100644 --- a/drivers/net/wireless/ath/ath10k/core.c +++ b/drivers/net/wireless/ath/ath10k/core.c @@ -387,7 +387,9 @@ static int ath10k_download_and_run_otp(struct ath10k *ar) ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot otp execute result %d\n", result); - if (!skip_otp && result != 0) { + if (!(skip_otp || test_bit(ATH10K_FW_FEATURE_IGNORE_OTP_RESULT, + ar->fw_features)) + && result != 0) { ath10k_err(ar, "otp calibration failed: %d", result); return -EINVAL; } diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h index 8444adf42195..827b3d79ed0c 100644 --- a/drivers/net/wireless/ath/ath10k/core.h +++ b/drivers/net/wireless/ath/ath10k/core.h @@ -460,6 +460,9 @@ enum ath10k_fw_features { */ ATH10K_FW_FEATURE_WOWLAN_SUPPORT = 6, + /* Don't trust error code from otp.bin */ + ATH10K_FW_FEATURE_IGNORE_OTP_RESULT, + /* keep last */ ATH10K_FW_FEATURE_COUNT, }; From a54a40dae67e4ee04df4dc36e7b89639131dd8e3 Mon Sep 17 00:00:00 2001 From: Vladimir Kondratiev Date: Thu, 30 Apr 2015 16:25:05 +0300 Subject: [PATCH 07/20] wil6210: increase timeout for the "echo" command Sometimes it takes for the firmware more than 20ms to react on "echo" command after reset. Increase timeout from 20 to 50ms Signed-off-by: Vladimir Kondratiev Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/wil6210/wmi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/wil6210/wmi.c b/drivers/net/wireless/ath/wil6210/wmi.c index 9fe2085be2c5..04fad5f4bbea 100644 --- a/drivers/net/wireless/ath/wil6210/wmi.c +++ b/drivers/net/wireless/ath/wil6210/wmi.c @@ -844,7 +844,7 @@ int wmi_echo(struct wil6210_priv *wil) }; return wmi_call(wil, WMI_ECHO_CMDID, &cmd, sizeof(cmd), - WMI_ECHO_RSP_EVENTID, NULL, 0, 20); + WMI_ECHO_RSP_EVENTID, NULL, 0, 50); } int wmi_set_mac_address(struct wil6210_priv *wil, void *addr) From 48c963af7432f974348d9a006914dbaf5e7c7140 Mon Sep 17 00:00:00 2001 From: Vladimir Kondratiev Date: Thu, 30 Apr 2015 16:25:06 +0300 Subject: [PATCH 08/20] wil6210: update Rx descriptor fields Rx descriptor fields accordingly to the updated hardware documentation Signed-off-by: Vladimir Kondratiev Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/wil6210/txrx.c | 2 +- drivers/net/wireless/ath/wil6210/txrx.h | 22 +++++++++++++++------- 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/drivers/net/wireless/ath/wil6210/txrx.c b/drivers/net/wireless/ath/wil6210/txrx.c index e8bd512d81a9..1fd34e5e9824 100644 --- a/drivers/net/wireless/ath/wil6210/txrx.c +++ b/drivers/net/wireless/ath/wil6210/txrx.c @@ -236,7 +236,7 @@ static int wil_vring_alloc_skb(struct wil6210_priv *wil, struct vring *vring, return -ENOMEM; } - d->dma.d0 = BIT(9) | RX_DMA_D0_CMD_DMA_IT; + d->dma.d0 = RX_DMA_D0_CMD_DMA_RT | RX_DMA_D0_CMD_DMA_IT; wil_desc_addr_set(&d->dma.addr, pa); /* ip_length don't care */ /* b11 don't care */ diff --git a/drivers/net/wireless/ath/wil6210/txrx.h b/drivers/net/wireless/ath/wil6210/txrx.h index d90c8aa20c15..0c4638487c74 100644 --- a/drivers/net/wireless/ath/wil6210/txrx.h +++ b/drivers/net/wireless/ath/wil6210/txrx.h @@ -384,19 +384,27 @@ struct vring_rx_mac { * [word 7] length */ -#define RX_DMA_D0_CMD_DMA_IT BIT(10) +#define RX_DMA_D0_CMD_DMA_EOP BIT(8) +#define RX_DMA_D0_CMD_DMA_RT BIT(9) /* always 1 */ +#define RX_DMA_D0_CMD_DMA_IT BIT(10) /* interrupt */ -/* Error field, offload bits */ -#define RX_DMA_ERROR_L3_ERR BIT(4) -#define RX_DMA_ERROR_L4_ERR BIT(5) +/* Error field */ +#define RX_DMA_ERROR_FCS BIT(0) +#define RX_DMA_ERROR_MIC BIT(1) +#define RX_DMA_ERROR_KEY BIT(2) /* Key missing */ +#define RX_DMA_ERROR_REPLAY BIT(3) +#define RX_DMA_ERROR_L3_ERR BIT(4) +#define RX_DMA_ERROR_L4_ERR BIT(5) /* Status field */ -#define RX_DMA_STATUS_DU BIT(0) -#define RX_DMA_STATUS_ERROR BIT(2) - +#define RX_DMA_STATUS_DU BIT(0) +#define RX_DMA_STATUS_EOP BIT(1) +#define RX_DMA_STATUS_ERROR BIT(2) +#define RX_DMA_STATUS_MI BIT(3) /* MAC Interrupt is asserted */ #define RX_DMA_STATUS_L3I BIT(4) #define RX_DMA_STATUS_L4I BIT(5) #define RX_DMA_STATUS_PHY_INFO BIT(6) +#define RX_DMA_STATUS_FFM BIT(7) /* EtherType Flex Filter Match */ struct vring_rx_dma { u32 d0; From bb6c8dccfe2302a97d8edd4ba5e6234a8fd3c626 Mon Sep 17 00:00:00 2001 From: Vladimir Kondratiev Date: Thu, 30 Apr 2015 16:25:07 +0300 Subject: [PATCH 09/20] wil6210: fix boot loader "ready" indication Boot loader "ready" indication has changed from "bit0 set" to "only bit0 set". This is to address hardware glitches. Due to glitches, sometimes right after reset register reads 0xffffffff, or (rarely) other garbage. Reflect this in the driver Signed-off-by: Vladimir Kondratiev Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/wil6210/main.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/ath/wil6210/main.c b/drivers/net/wireless/ath/wil6210/main.c index 0623d8c98956..146fd8088fde 100644 --- a/drivers/net/wireless/ath/wil6210/main.c +++ b/drivers/net/wireless/ath/wil6210/main.c @@ -551,7 +551,7 @@ static inline void wil_release_cpu(struct wil6210_priv *wil) static int wil_target_reset(struct wil6210_priv *wil) { int delay = 0; - u32 x; + u32 x, x1 = 0; wil_dbg_misc(wil, "Resetting \"%s\"...\n", wil->hw_name); @@ -606,12 +606,16 @@ static int wil_target_reset(struct wil6210_priv *wil) do { msleep(RST_DELAY); x = R(RGF_USER_BL + offsetof(struct RGF_BL, ready)); + if (x1 != x) { + wil_dbg_misc(wil, "BL.ready 0x%08x => 0x%08x\n", x1, x); + x1 = x; + } if (delay++ > RST_COUNT) { wil_err(wil, "Reset not completed, bl.ready 0x%08x\n", x); return -ETIME; } - } while (!(x & BIT_BL_READY)); + } while (x != BIT_BL_READY); C(RGF_USER_CLKS_CTL_0, BIT_USER_CLKS_RST_PWGD); From 1e7e5a0d318229b5ad12efeee690b8d0d5c980ff Mon Sep 17 00:00:00 2001 From: Vladimir Kondratiev Date: Thu, 30 Apr 2015 16:25:08 +0300 Subject: [PATCH 10/20] wil6210: fix for probe_resp IE's Parameters for the start_ap and change_bcon contains probe response template and probe response IE's. supplicant puts in the proberesp_ies only WPS related IE's, while firmware need all IE's, most notable is RSN that is not included in the proberesp_ies. Always use IE's provided by the bcon->probe_resp Signed-off-by: Vladimir Kondratiev Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/wil6210/cfg80211.c | 31 ++++++++++++++------- 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/drivers/net/wireless/ath/wil6210/cfg80211.c b/drivers/net/wireless/ath/wil6210/cfg80211.c index ea331e407e7f..f53a1908fae2 100644 --- a/drivers/net/wireless/ath/wil6210/cfg80211.c +++ b/drivers/net/wireless/ath/wil6210/cfg80211.c @@ -669,11 +669,6 @@ static int wil_fix_bcon(struct wil6210_priv *wil, if (bcon->probe_resp_len <= hlen) return 0; - if (!bcon->proberesp_ies) { - bcon->proberesp_ies = f->u.probe_resp.variable; - bcon->proberesp_ies_len = bcon->probe_resp_len - hlen; - rc = 1; - } if (!bcon->assocresp_ies) { bcon->assocresp_ies = f->u.probe_resp.variable; bcon->assocresp_ies_len = bcon->probe_resp_len - hlen; @@ -688,9 +683,19 @@ static int wil_cfg80211_change_beacon(struct wiphy *wiphy, struct cfg80211_beacon_data *bcon) { struct wil6210_priv *wil = wiphy_to_wil(wiphy); + struct ieee80211_mgmt *f = (struct ieee80211_mgmt *)bcon->probe_resp; + size_t hlen = offsetof(struct ieee80211_mgmt, u.probe_resp.variable); + const u8 *pr_ies = NULL; + size_t pr_ies_len = 0; int rc; wil_dbg_misc(wil, "%s()\n", __func__); + wil_print_bcon_data(bcon); + + if (bcon->probe_resp_len > hlen) { + pr_ies = f->u.probe_resp.variable; + pr_ies_len = bcon->probe_resp_len - hlen; + } if (wil_fix_bcon(wil, bcon)) { wil_dbg_misc(wil, "Fixed bcon\n"); @@ -703,9 +708,7 @@ static int wil_cfg80211_change_beacon(struct wiphy *wiphy, * wmi_set_ie(wil, WMI_FRAME_BEACON, bcon->beacon_ies_len, * bcon->beacon_ies); */ - rc = wmi_set_ie(wil, WMI_FRAME_PROBE_RESP, - bcon->proberesp_ies_len, - bcon->proberesp_ies); + rc = wmi_set_ie(wil, WMI_FRAME_PROBE_RESP, pr_ies_len, pr_ies); if (rc) { wil_err(wil, "set_ie(PROBE_RESP) failed\n"); return rc; @@ -733,6 +736,10 @@ static int wil_cfg80211_start_ap(struct wiphy *wiphy, struct cfg80211_beacon_data *bcon = &info->beacon; struct cfg80211_crypto_settings *crypto = &info->crypto; u8 wmi_nettype = wil_iftype_nl2wmi(wdev->iftype); + struct ieee80211_mgmt *f = (struct ieee80211_mgmt *)bcon->probe_resp; + size_t hlen = offsetof(struct ieee80211_mgmt, u.probe_resp.variable); + const u8 *pr_ies = NULL; + size_t pr_ies_len = 0; wil_dbg_misc(wil, "%s()\n", __func__); @@ -752,6 +759,11 @@ static int wil_cfg80211_start_ap(struct wiphy *wiphy, wil_print_bcon_data(bcon); wil_print_crypto(wil, crypto); + if (bcon->probe_resp_len > hlen) { + pr_ies = f->u.probe_resp.variable; + pr_ies_len = bcon->probe_resp_len - hlen; + } + if (wil_fix_bcon(wil, bcon)) { wil_dbg_misc(wil, "Fixed bcon\n"); wil_print_bcon_data(bcon); @@ -779,8 +791,7 @@ static int wil_cfg80211_start_ap(struct wiphy *wiphy, * wmi_set_ie(wil, WMI_FRAME_BEACON, bcon->beacon_ies_len, * bcon->beacon_ies); */ - wmi_set_ie(wil, WMI_FRAME_PROBE_RESP, bcon->proberesp_ies_len, - bcon->proberesp_ies); + wmi_set_ie(wil, WMI_FRAME_PROBE_RESP, pr_ies_len, pr_ies); wmi_set_ie(wil, WMI_FRAME_ASSOC_RESP, bcon->assocresp_ies_len, bcon->assocresp_ies); From dc16427bbe65aeec416f490df73cdeeef5ab62c0 Mon Sep 17 00:00:00 2001 From: Vladimir Kondratiev Date: Thu, 30 Apr 2015 16:25:09 +0300 Subject: [PATCH 11/20] wil6210: Add pmc debug mechanism memory management Pmc is a hardware debug mechanism which allows capturing phy data, packets, and internally generated events and messages synchronized and time stamped by the hardware. It requires memory buffers allocated by the driver in order to be used by hardware dma to upstream real time debug data to host memory. Driver will handle memory allocation and release, and fetching the data from the memory to application layer via debug file system. The configuration of pmc is handled entirely by the application layer. Signed-off-by: Vladimir Shulman Signed-off-by: Vladimir Kondratiev Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/wil6210/Makefile | 1 + drivers/net/wireless/ath/wil6210/debugfs.c | 93 +++++ drivers/net/wireless/ath/wil6210/pmc.c | 375 +++++++++++++++++++++ drivers/net/wireless/ath/wil6210/pmc.h | 27 ++ drivers/net/wireless/ath/wil6210/wil6210.h | 14 + drivers/net/wireless/ath/wil6210/wmi.h | 15 + 6 files changed, 525 insertions(+) create mode 100644 drivers/net/wireless/ath/wil6210/pmc.c create mode 100644 drivers/net/wireless/ath/wil6210/pmc.h diff --git a/drivers/net/wireless/ath/wil6210/Makefile b/drivers/net/wireless/ath/wil6210/Makefile index caa717bf52f3..050506f842e9 100644 --- a/drivers/net/wireless/ath/wil6210/Makefile +++ b/drivers/net/wireless/ath/wil6210/Makefile @@ -12,6 +12,7 @@ wil6210-y += debug.o wil6210-y += rx_reorder.o wil6210-y += ioctl.o wil6210-y += fw.o +wil6210-y += pmc.o wil6210-$(CONFIG_WIL6210_TRACING) += trace.o wil6210-y += wil_platform.o wil6210-y += ethtool.o diff --git a/drivers/net/wireless/ath/wil6210/debugfs.c b/drivers/net/wireless/ath/wil6210/debugfs.c index bbc22d88f78f..19f77e55dc53 100644 --- a/drivers/net/wireless/ath/wil6210/debugfs.c +++ b/drivers/net/wireless/ath/wil6210/debugfs.c @@ -24,6 +24,7 @@ #include "wil6210.h" #include "wmi.h" #include "txrx.h" +#include "pmc.h" /* Nasty hack. Better have per device instances */ static u32 mem_addr; @@ -702,6 +703,89 @@ static const struct file_operations fops_back = { .open = simple_open, }; +/* pmc control, write: + * - "alloc " to allocate PMC + * - "free" to release memory allocated for PMC + */ +static ssize_t wil_write_pmccfg(struct file *file, const char __user *buf, + size_t len, loff_t *ppos) +{ + struct wil6210_priv *wil = file->private_data; + int rc; + char *kbuf = kmalloc(len + 1, GFP_KERNEL); + char cmd[9]; + int num_descs, desc_size; + + if (!kbuf) + return -ENOMEM; + + rc = simple_write_to_buffer(kbuf, len, ppos, buf, len); + if (rc != len) { + kfree(kbuf); + return rc >= 0 ? -EIO : rc; + } + + kbuf[len] = '\0'; + rc = sscanf(kbuf, "%8s %d %d", cmd, &num_descs, &desc_size); + kfree(kbuf); + + if (rc < 0) + return rc; + + if (rc < 1) { + wil_err(wil, "pmccfg: no params given\n"); + return -EINVAL; + } + + if (0 == strcmp(cmd, "alloc")) { + if (rc != 3) { + wil_err(wil, "pmccfg: alloc requires 2 params\n"); + return -EINVAL; + } + wil_pmc_alloc(wil, num_descs, desc_size); + } else if (0 == strcmp(cmd, "free")) { + if (rc != 1) { + wil_err(wil, "pmccfg: free does not have any params\n"); + return -EINVAL; + } + wil_pmc_free(wil, true); + } else { + wil_err(wil, "pmccfg: Unrecognized command \"%s\"\n", cmd); + return -EINVAL; + } + + return len; +} + +static ssize_t wil_read_pmccfg(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct wil6210_priv *wil = file->private_data; + char text[256]; + char help[] = "pmc control, write:\n" + " - \"alloc \" to allocate pmc\n" + " - \"free\" to free memory allocated for pmc\n"; + + sprintf(text, "Last command status: %d\n\n%s", + wil_pmc_last_cmd_status(wil), + help); + + return simple_read_from_buffer(user_buf, count, ppos, text, + strlen(text) + 1); +} + +static const struct file_operations fops_pmccfg = { + .read = wil_read_pmccfg, + .write = wil_write_pmccfg, + .open = simple_open, +}; + +static const struct file_operations fops_pmcdata = { + .open = simple_open, + .read = wil_pmc_read, + .llseek = wil_pmc_llseek, +}; + /*---tx_mgmt---*/ /* Write mgmt frame to this file to send it */ static ssize_t wil_write_file_txmgmt(struct file *file, const char __user *buf, @@ -1363,6 +1447,8 @@ static const struct { {"tx_mgmt", S_IWUSR, &fops_txmgmt}, {"wmi_send", S_IWUSR, &fops_wmi}, {"back", S_IRUGO | S_IWUSR, &fops_back}, + {"pmccfg", S_IRUGO | S_IWUSR, &fops_pmccfg}, + {"pmcdata", S_IRUGO, &fops_pmcdata}, {"temp", S_IRUGO, &fops_temp}, {"freq", S_IRUGO, &fops_freq}, {"link", S_IRUGO, &fops_link}, @@ -1440,6 +1526,8 @@ int wil6210_debugfs_init(struct wil6210_priv *wil) if (IS_ERR_OR_NULL(dbg)) return -ENODEV; + wil_pmc_init(wil); + wil6210_debugfs_init_files(wil, dbg); wil6210_debugfs_init_isr(wil, dbg); wil6210_debugfs_init_blobs(wil, dbg); @@ -1459,4 +1547,9 @@ void wil6210_debugfs_remove(struct wil6210_priv *wil) { debugfs_remove_recursive(wil->debug); wil->debug = NULL; + + /* free pmc memory without sending command to fw, as it will + * be reset on the way down anyway + */ + wil_pmc_free(wil, false); } diff --git a/drivers/net/wireless/ath/wil6210/pmc.c b/drivers/net/wireless/ath/wil6210/pmc.c new file mode 100644 index 000000000000..3cb4f35ebd05 --- /dev/null +++ b/drivers/net/wireless/ath/wil6210/pmc.c @@ -0,0 +1,375 @@ +/* + * Copyright (c) 2012-2015 Qualcomm Atheros, Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include "wmi.h" +#include "wil6210.h" +#include "txrx.h" +#include "pmc.h" + +struct desc_alloc_info { + dma_addr_t pa; + void *va; +}; + +static int wil_is_pmc_allocated(struct pmc_ctx *pmc) +{ + return !!pmc->pring_va; +} + +void wil_pmc_init(struct wil6210_priv *wil) +{ + memset(&wil->pmc, 0, sizeof(struct pmc_ctx)); + mutex_init(&wil->pmc.lock); +} + +/** + * Allocate the physical ring (p-ring) and the required + * number of descriptors of required size. + * Initialize the descriptors as required by pmc dma. + * The descriptors' buffers dwords are initialized to hold + * dword's serial number in the lsw and reserved value + * PCM_DATA_INVALID_DW_VAL in the msw. + */ +void wil_pmc_alloc(struct wil6210_priv *wil, + int num_descriptors, + int descriptor_size) +{ + u32 i; + struct pmc_ctx *pmc = &wil->pmc; + struct device *dev = wil_to_dev(wil); + struct wmi_pmc_cmd pmc_cmd = {0}; + + mutex_lock(&pmc->lock); + + if (wil_is_pmc_allocated(pmc)) { + /* sanity check */ + wil_err(wil, "%s: ERROR pmc is already allocated\n", __func__); + goto no_release_err; + } + + pmc->num_descriptors = num_descriptors; + pmc->descriptor_size = descriptor_size; + + wil_dbg_misc(wil, "%s: %d descriptors x %d bytes each\n", + __func__, num_descriptors, descriptor_size); + + /* allocate descriptors info list in pmc context*/ + pmc->descriptors = kcalloc(num_descriptors, + sizeof(struct desc_alloc_info), + GFP_KERNEL); + if (!pmc->descriptors) { + wil_err(wil, "%s: ERROR allocating pmc skb list\n", __func__); + goto no_release_err; + } + + wil_dbg_misc(wil, + "%s: allocated descriptors info list %p\n", + __func__, pmc->descriptors); + + /* Allocate pring buffer and descriptors. + * vring->va should be aligned on its size rounded up to power of 2 + * This is granted by the dma_alloc_coherent + */ + pmc->pring_va = dma_alloc_coherent(dev, + sizeof(struct vring_tx_desc) * num_descriptors, + &pmc->pring_pa, + GFP_KERNEL); + + wil_dbg_misc(wil, + "%s: allocated pring %p. %zd x %d = total %zd bytes\n", + __func__, + (void *)pmc->pring_pa, + sizeof(struct vring_tx_desc), + num_descriptors, + sizeof(struct vring_tx_desc) * num_descriptors); + + if (!pmc->pring_va) { + wil_err(wil, "%s: ERROR allocating pmc pring\n", __func__); + goto release_pmc_skb_list; + } + + /* initially, all descriptors are SW owned + * For Tx, Rx, and PMC, ownership bit is at the same location, thus + * we can use any + */ + for (i = 0; i < num_descriptors; i++) { + struct vring_tx_desc *_d = &pmc->pring_va[i]; + struct vring_tx_desc dd, *d = ⅆ + int j = 0; + + pmc->descriptors[i].va = dma_alloc_coherent(dev, + descriptor_size, + &pmc->descriptors[i].pa, + GFP_KERNEL); + + if (unlikely(!pmc->descriptors[i].va)) { + wil_err(wil, + "%s: ERROR allocating pmc descriptor %d", + __func__, i); + goto release_pmc_skbs; + } + + for (j = 0; j < descriptor_size / sizeof(u32); j++) { + u32 *p = (u32 *)pmc->descriptors[i].va + j; + *p = PCM_DATA_INVALID_DW_VAL | j; + } + + /* configure dma descriptor */ + d->dma.addr.addr_low = + cpu_to_le32(lower_32_bits(pmc->descriptors[i].pa)); + d->dma.addr.addr_high = + cpu_to_le16((u16)upper_32_bits(pmc->descriptors[i].pa)); + d->dma.status = 0; /* 0 = HW_OWNED */ + d->dma.length = cpu_to_le16(descriptor_size); + d->dma.d0 = BIT(9) | RX_DMA_D0_CMD_DMA_IT; + *_d = *d; + } + + wil_dbg_misc(wil, "%s: allocated successfully\n", __func__); + + pmc_cmd.op = WMI_PMC_ALLOCATE; + pmc_cmd.ring_size = cpu_to_le16(pmc->num_descriptors); + pmc_cmd.mem_base = cpu_to_le64(pmc->pring_pa); + + wil_dbg_misc(wil, "%s: send WMI_PMC_CMD with ALLOCATE op\n", __func__); + pmc->last_cmd_status = wmi_send(wil, + WMI_PMC_CMDID, + &pmc_cmd, + sizeof(pmc_cmd)); + if (pmc->last_cmd_status) { + wil_err(wil, + "%s: WMI_PMC_CMD with ALLOCATE op failed with status %d", + __func__, pmc->last_cmd_status); + goto release_pmc_skbs; + } + + mutex_unlock(&pmc->lock); + + return; + +release_pmc_skbs: + wil_err(wil, "%s: exit on error: Releasing skbs...\n", __func__); + for (i = 0; pmc->descriptors[i].va && i < num_descriptors; i++) { + dma_free_coherent(dev, + descriptor_size, + pmc->descriptors[i].va, + pmc->descriptors[i].pa); + + pmc->descriptors[i].va = NULL; + } + wil_err(wil, "%s: exit on error: Releasing pring...\n", __func__); + + dma_free_coherent(dev, + sizeof(struct vring_tx_desc) * num_descriptors, + pmc->pring_va, + pmc->pring_pa); + + pmc->pring_va = NULL; + +release_pmc_skb_list: + wil_err(wil, "%s: exit on error: Releasing descriptors info list...\n", + __func__); + kfree(pmc->descriptors); + pmc->descriptors = NULL; + +no_release_err: + pmc->last_cmd_status = -ENOMEM; + mutex_unlock(&pmc->lock); +} + +/** + * Traverse the p-ring and release all buffers. + * At the end release the p-ring memory + */ +void wil_pmc_free(struct wil6210_priv *wil, int send_pmc_cmd) +{ + struct pmc_ctx *pmc = &wil->pmc; + struct device *dev = wil_to_dev(wil); + struct wmi_pmc_cmd pmc_cmd = {0}; + + mutex_lock(&pmc->lock); + + pmc->last_cmd_status = 0; + + if (!wil_is_pmc_allocated(pmc)) { + wil_dbg_misc(wil, "%s: Error, can't free - not allocated\n", + __func__); + pmc->last_cmd_status = -EPERM; + mutex_unlock(&pmc->lock); + return; + } + + if (send_pmc_cmd) { + wil_dbg_misc(wil, "%s: send WMI_PMC_CMD with RELEASE op\n", + __func__); + pmc_cmd.op = WMI_PMC_RELEASE; + pmc->last_cmd_status = + wmi_send(wil, WMI_PMC_CMDID, &pmc_cmd, + sizeof(pmc_cmd)); + if (pmc->last_cmd_status) { + wil_err(wil, + "%s WMI_PMC_CMD with RELEASE op failed, status %d", + __func__, pmc->last_cmd_status); + /* There's nothing we can do with this error. + * Normally, it should never occur. + * Continue to freeing all memory allocated for pmc. + */ + } + } + + if (pmc->pring_va) { + size_t buf_size = sizeof(struct vring_tx_desc) * + pmc->num_descriptors; + + wil_dbg_misc(wil, "%s: free pring va %p\n", + __func__, pmc->pring_va); + dma_free_coherent(dev, buf_size, pmc->pring_va, pmc->pring_pa); + + pmc->pring_va = NULL; + } else { + pmc->last_cmd_status = -ENOENT; + } + + if (pmc->descriptors) { + int i; + + for (i = 0; + pmc->descriptors[i].va && i < pmc->num_descriptors; i++) { + dma_free_coherent(dev, + pmc->descriptor_size, + pmc->descriptors[i].va, + pmc->descriptors[i].pa); + pmc->descriptors[i].va = NULL; + } + wil_dbg_misc(wil, "%s: free descriptor info %d/%d\n", + __func__, i, pmc->num_descriptors); + wil_dbg_misc(wil, + "%s: free pmc descriptors info list %p\n", + __func__, pmc->descriptors); + kfree(pmc->descriptors); + pmc->descriptors = NULL; + } else { + pmc->last_cmd_status = -ENOENT; + } + + mutex_unlock(&pmc->lock); +} + +/** + * Status of the last operation requested via debugfs: alloc/free/read. + * 0 - success or negative errno + */ +int wil_pmc_last_cmd_status(struct wil6210_priv *wil) +{ + wil_dbg_misc(wil, "%s: status %d\n", __func__, + wil->pmc.last_cmd_status); + + return wil->pmc.last_cmd_status; +} + +/** + * Read from required position up to the end of current descriptor, + * depends on descriptor size configured during alloc request. + */ +ssize_t wil_pmc_read(struct file *filp, char __user *buf, size_t count, + loff_t *f_pos) +{ + struct wil6210_priv *wil = filp->private_data; + struct pmc_ctx *pmc = &wil->pmc; + size_t retval = 0; + unsigned long long idx; + loff_t offset; + size_t pmc_size = pmc->descriptor_size * pmc->num_descriptors; + + mutex_lock(&pmc->lock); + + if (!wil_is_pmc_allocated(pmc)) { + wil_err(wil, "%s: error, pmc is not allocated!\n", __func__); + pmc->last_cmd_status = -EPERM; + mutex_unlock(&pmc->lock); + return -EPERM; + } + + wil_dbg_misc(wil, + "%s: size %u, pos %lld\n", + __func__, (unsigned)count, *f_pos); + + pmc->last_cmd_status = 0; + + idx = *f_pos; + do_div(idx, pmc->descriptor_size); + offset = *f_pos - (idx * pmc->descriptor_size); + + if (*f_pos >= pmc_size) { + wil_dbg_misc(wil, "%s: reached end of pmc buf: %lld >= %u\n", + __func__, *f_pos, (unsigned)pmc_size); + pmc->last_cmd_status = -ERANGE; + goto out; + } + + wil_dbg_misc(wil, + "%s: read from pos %lld (descriptor %llu, offset %llu) %zu bytes\n", + __func__, *f_pos, idx, offset, count); + + /* if no errors, return the copied byte count */ + retval = simple_read_from_buffer(buf, + count, + &offset, + pmc->descriptors[idx].va, + pmc->descriptor_size); + *f_pos += retval; +out: + mutex_unlock(&pmc->lock); + + return retval; +} + +loff_t wil_pmc_llseek(struct file *filp, loff_t off, int whence) +{ + loff_t newpos; + struct wil6210_priv *wil = filp->private_data; + struct pmc_ctx *pmc = &wil->pmc; + size_t pmc_size = pmc->descriptor_size * pmc->num_descriptors; + + switch (whence) { + case 0: /* SEEK_SET */ + newpos = off; + break; + + case 1: /* SEEK_CUR */ + newpos = filp->f_pos + off; + break; + + case 2: /* SEEK_END */ + newpos = pmc_size; + break; + + default: /* can't happen */ + return -EINVAL; + } + + if (newpos < 0) + return -EINVAL; + if (newpos > pmc_size) + newpos = pmc_size; + + filp->f_pos = newpos; + + return newpos; +} diff --git a/drivers/net/wireless/ath/wil6210/pmc.h b/drivers/net/wireless/ath/wil6210/pmc.h new file mode 100644 index 000000000000..bebc8d52e1e6 --- /dev/null +++ b/drivers/net/wireless/ath/wil6210/pmc.h @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2012-2015 Qualcomm Atheros, Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +#define PCM_DATA_INVALID_DW_VAL (0xB0BA0000) + +void wil_pmc_init(struct wil6210_priv *wil); +void wil_pmc_alloc(struct wil6210_priv *wil, + int num_descriptors, int descriptor_size); +void wil_pmc_free(struct wil6210_priv *wil, int send_pmc_cmd); +int wil_pmc_last_cmd_status(struct wil6210_priv *wil); +ssize_t wil_pmc_read(struct file *, char __user *, size_t, loff_t *); +loff_t wil_pmc_llseek(struct file *filp, loff_t off, int whence); diff --git a/drivers/net/wireless/ath/wil6210/wil6210.h b/drivers/net/wireless/ath/wil6210/wil6210.h index abb2080d3fa1..9d74bd819236 100644 --- a/drivers/net/wireless/ath/wil6210/wil6210.h +++ b/drivers/net/wireless/ath/wil6210/wil6210.h @@ -21,6 +21,7 @@ #include #include #include +#include #include "wil_platform.h" extern bool no_fw_recovery; @@ -527,6 +528,17 @@ struct wil_probe_client_req { u8 cid; }; +struct pmc_ctx { + /* alloc, free, and read operations must own the lock */ + struct mutex lock; + struct vring_tx_desc *pring_va; + dma_addr_t pring_pa; + struct desc_alloc_info *descriptors; + int last_cmd_status; + int num_descriptors; + int descriptor_size; +}; + struct wil6210_priv { struct pci_dev *pdev; int n_msi; @@ -611,6 +623,8 @@ struct wil6210_priv { void *platform_handle; struct wil_platform_ops platform_ops; + + struct pmc_ctx pmc; }; #define wil_to_wiphy(i) (i->wdev->wiphy) diff --git a/drivers/net/wireless/ath/wil6210/wmi.h b/drivers/net/wireless/ath/wil6210/wmi.h index b29055315350..24c253d91363 100644 --- a/drivers/net/wireless/ath/wil6210/wmi.h +++ b/drivers/net/wireless/ath/wil6210/wmi.h @@ -835,6 +835,21 @@ struct wmi_temp_sense_cmd { __le32 measure_mode; } __packed; +/* + * WMI_PMC_CMDID + */ +enum wmi_pmc_op_e { + WMI_PMC_ALLOCATE = 0, + WMI_PMC_RELEASE = 1, +}; + +struct wmi_pmc_cmd { + u8 op; /* enum wmi_pmc_cmd_op_type */ + u8 reserved; + __le16 ring_size; + __le64 mem_base; +} __packed; + /* * WMI Events */ From 230d8442f452509b371e8765218b3a876d5b575c Mon Sep 17 00:00:00 2001 From: Vladimir Kondratiev Date: Thu, 30 Apr 2015 16:25:10 +0300 Subject: [PATCH 12/20] wil6210: broadcast for secure link Introduce 2 types of GTK, Tx (for this STA) and Rx (for each peer). Now, AP has only Tx GTK, STA - only Rx one. PBSS not supported yet; for it, continue using pseudo-DMS. Handle per-vring .1x state, update it from WMI_VRING_EN_EVENTID event. This allows unification for unicast and broadcast vrings. This mechanism replaces former per-CID "data_port_open" Signed-off-by: Vladimir Kondratiev Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/wil6210/cfg80211.c | 64 ++++++++++++++----- drivers/net/wireless/ath/wil6210/debugfs.c | 14 ++--- drivers/net/wireless/ath/wil6210/main.c | 6 +- drivers/net/wireless/ath/wil6210/txrx.c | 46 +++++++------- drivers/net/wireless/ath/wil6210/wil6210.h | 7 ++- drivers/net/wireless/ath/wil6210/wmi.c | 68 ++++++--------------- drivers/net/wireless/ath/wil6210/wmi.h | 25 +++++--- 7 files changed, 124 insertions(+), 106 deletions(-) diff --git a/drivers/net/wireless/ath/wil6210/cfg80211.c b/drivers/net/wireless/ath/wil6210/cfg80211.c index f53a1908fae2..1b02b73c4212 100644 --- a/drivers/net/wireless/ath/wil6210/cfg80211.c +++ b/drivers/net/wireless/ath/wil6210/cfg80211.c @@ -425,10 +425,17 @@ static int wil_cfg80211_connect(struct wiphy *wiphy, wil->privacy = sme->privacy; if (wil->privacy) { - /* For secure assoc, send WMI_DELETE_CIPHER_KEY_CMD */ - rc = wmi_del_cipher_key(wil, 0, bss->bssid); + /* For secure assoc, remove old keys */ + rc = wmi_del_cipher_key(wil, 0, bss->bssid, + WMI_KEY_USE_PAIRWISE); if (rc) { - wil_err(wil, "WMI_DELETE_CIPHER_KEY_CMD failed\n"); + wil_err(wil, "WMI_DELETE_CIPHER_KEY_CMD(PTK) failed\n"); + goto out; + } + rc = wmi_del_cipher_key(wil, 0, bss->bssid, + WMI_KEY_USE_RX_GROUP); + if (rc) { + wil_err(wil, "WMI_DELETE_CIPHER_KEY_CMD(GTK) failed\n"); goto out; } } @@ -462,6 +469,8 @@ static int wil_cfg80211_connect(struct wiphy *wiphy, conn.auth_mode = WMI_AUTH_WPA2_PSK; conn.pairwise_crypto_type = WMI_CRYPT_AES_GCMP; conn.pairwise_crypto_len = 16; + conn.group_crypto_type = WMI_CRYPT_AES_GCMP; + conn.group_crypto_len = 16; } else { conn.dot11_auth_mode = WMI_AUTH11_OPEN; conn.auth_mode = WMI_AUTH_NONE; @@ -563,6 +572,39 @@ static int wil_cfg80211_set_channel(struct wiphy *wiphy, return 0; } +static enum wmi_key_usage wil_detect_key_usage(struct wil6210_priv *wil, + bool pairwise) +{ + struct wireless_dev *wdev = wil->wdev; + enum wmi_key_usage rc; + static const char * const key_usage_str[] = { + [WMI_KEY_USE_PAIRWISE] = "WMI_KEY_USE_PAIRWISE", + [WMI_KEY_USE_RX_GROUP] = "WMI_KEY_USE_RX_GROUP", + [WMI_KEY_USE_TX_GROUP] = "WMI_KEY_USE_TX_GROUP", + }; + + if (pairwise) { + rc = WMI_KEY_USE_PAIRWISE; + } else { + switch (wdev->iftype) { + case NL80211_IFTYPE_STATION: + rc = WMI_KEY_USE_RX_GROUP; + break; + case NL80211_IFTYPE_AP: + rc = WMI_KEY_USE_TX_GROUP; + break; + default: + /* TODO: Rx GTK or Tx GTK? */ + wil_err(wil, "Can't determine GTK type\n"); + rc = WMI_KEY_USE_RX_GROUP; + break; + } + } + wil_dbg_misc(wil, "%s() -> %s\n", __func__, key_usage_str[rc]); + + return rc; +} + static int wil_cfg80211_add_key(struct wiphy *wiphy, struct net_device *ndev, u8 key_index, bool pairwise, @@ -570,16 +612,13 @@ static int wil_cfg80211_add_key(struct wiphy *wiphy, struct key_params *params) { struct wil6210_priv *wil = wiphy_to_wil(wiphy); + enum wmi_key_usage key_usage = wil_detect_key_usage(wil, pairwise); wil_dbg_misc(wil, "%s(%pM[%d] %s)\n", __func__, mac_addr, key_index, pairwise ? "PTK" : "GTK"); - /* group key is not used */ - if (!pairwise) - return 0; - - return wmi_add_cipher_key(wil, key_index, mac_addr, - params->key_len, params->key); + return wmi_add_cipher_key(wil, key_index, mac_addr, params->key_len, + params->key, key_usage); } static int wil_cfg80211_del_key(struct wiphy *wiphy, @@ -588,15 +627,12 @@ static int wil_cfg80211_del_key(struct wiphy *wiphy, const u8 *mac_addr) { struct wil6210_priv *wil = wiphy_to_wil(wiphy); + enum wmi_key_usage key_usage = wil_detect_key_usage(wil, pairwise); wil_dbg_misc(wil, "%s(%pM[%d] %s)\n", __func__, mac_addr, key_index, pairwise ? "PTK" : "GTK"); - /* group key is not used */ - if (!pairwise) - return 0; - - return wmi_del_cipher_key(wil, key_index, mac_addr); + return wmi_del_cipher_key(wil, key_index, mac_addr, key_usage); } /* Need to be present or wiphy_new() will WARN */ diff --git a/drivers/net/wireless/ath/wil6210/debugfs.c b/drivers/net/wireless/ath/wil6210/debugfs.c index 19f77e55dc53..8f9c0722a801 100644 --- a/drivers/net/wireless/ath/wil6210/debugfs.c +++ b/drivers/net/wireless/ath/wil6210/debugfs.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2014 Qualcomm Atheros, Inc. + * Copyright (c) 2012-2015 Qualcomm Atheros, Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -124,15 +124,17 @@ static int wil_vring_debugfs_show(struct seq_file *s, void *data) if (cid < WIL6210_MAX_CID) seq_printf(s, - "\n%pM CID %d TID %d BACK([%u] %u TU A%s) [%3d|%3d] idle %s\n", + "\n%pM CID %d TID %d 1x%s BACK([%u] %u TU A%s) [%3d|%3d] idle %s\n", wil->sta[cid].addr, cid, tid, + txdata->dot1x_open ? "+" : "-", txdata->agg_wsize, txdata->agg_timeout, txdata->agg_amsdu ? "+" : "-", used, avail, sidle); else seq_printf(s, - "\nBroadcast [%3d|%3d] idle %s\n", + "\nBroadcast 1x%s [%3d|%3d] idle %s\n", + txdata->dot1x_open ? "+" : "-", used, avail, sidle); wil_print_vring(s, wil, name, vring, '_', 'H'); @@ -1195,8 +1197,7 @@ static int wil_link_debugfs_show(struct seq_file *s, void *data) status = "connected"; break; } - seq_printf(s, "[%d] %pM %s%s\n", i, p->addr, status, - (p->data_port_open ? " data_port_open" : "")); + seq_printf(s, "[%d] %pM %s\n", i, p->addr, status); if (p->status == wil_sta_connected) { rc = wil_cid_fill_sinfo(wil, i, &sinfo); @@ -1376,8 +1377,7 @@ __acquires(&p->tid_rx_lock) __releases(&p->tid_rx_lock) status = "connected"; break; } - seq_printf(s, "[%d] %pM %s%s\n", i, p->addr, status, - (p->data_port_open ? " data_port_open" : "")); + seq_printf(s, "[%d] %pM %s\n", i, p->addr, status); if (p->status == wil_sta_connected) { spin_lock_bh(&p->tid_rx_lock); diff --git a/drivers/net/wireless/ath/wil6210/main.c b/drivers/net/wireless/ath/wil6210/main.c index 146fd8088fde..ef3b6bca8cb5 100644 --- a/drivers/net/wireless/ath/wil6210/main.c +++ b/drivers/net/wireless/ath/wil6210/main.c @@ -150,7 +150,6 @@ __acquires(&sta->tid_rx_lock) __releases(&sta->tid_rx_lock) wil_dbg_misc(wil, "%s(CID %d, status %d)\n", __func__, cid, sta->status); - sta->data_port_open = false; if (sta->status != wil_sta_unused) { if (!from_event) wmi_disconnect_sta(wil, sta->addr, reason_code); @@ -377,9 +376,10 @@ int wil_bcast_init(struct wil6210_priv *wil) if (ri < 0) return ri; + wil->bcast_vring = ri; rc = wil_vring_init_bcast(wil, ri, 1 << bcast_ring_order); - if (rc == 0) - wil->bcast_vring = ri; + if (rc) + wil->bcast_vring = -1; return rc; } diff --git a/drivers/net/wireless/ath/wil6210/txrx.c b/drivers/net/wireless/ath/wil6210/txrx.c index 1fd34e5e9824..0113dac3a9a9 100644 --- a/drivers/net/wireless/ath/wil6210/txrx.c +++ b/drivers/net/wireless/ath/wil6210/txrx.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2014 Qualcomm Atheros, Inc. + * Copyright (c) 2012-2015 Qualcomm Atheros, Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -724,6 +724,8 @@ int wil_vring_init_tx(struct wil6210_priv *wil, int id, int size, cmd.vring_cfg.tx_sw_ring.ring_mem_base = cpu_to_le64(vring->pa); + if (!wil->privacy) + txdata->dot1x_open = true; rc = wmi_call(wil, WMI_VRING_CFG_CMDID, &cmd, sizeof(cmd), WMI_VRING_CFG_DONE_EVENTID, &reply, sizeof(reply), 100); if (rc) @@ -738,11 +740,13 @@ int wil_vring_init_tx(struct wil6210_priv *wil, int id, int size, vring->hwtail = le32_to_cpu(reply.cmd.tx_vring_tail_ptr); txdata->enabled = 1; - if (wil->sta[cid].data_port_open && (agg_wsize >= 0)) + if (txdata->dot1x_open && (agg_wsize >= 0)) wil_addba_tx_request(wil, id, agg_wsize); return 0; out_free: + txdata->dot1x_open = false; + txdata->enabled = 0; wil_vring_free(wil, vring, 1); out: @@ -792,6 +796,8 @@ int wil_vring_init_bcast(struct wil6210_priv *wil, int id, int size) cmd.vring_cfg.tx_sw_ring.ring_mem_base = cpu_to_le64(vring->pa); + if (!wil->privacy) + txdata->dot1x_open = true; rc = wmi_call(wil, WMI_BCAST_VRING_CFG_CMDID, &cmd, sizeof(cmd), WMI_VRING_CFG_DONE_EVENTID, &reply, sizeof(reply), 100); if (rc) @@ -809,6 +815,8 @@ int wil_vring_init_bcast(struct wil6210_priv *wil, int id, int size) return 0; out_free: + txdata->enabled = 0; + txdata->dot1x_open = false; wil_vring_free(wil, vring, 1); out: @@ -828,6 +836,7 @@ void wil_vring_fini_tx(struct wil6210_priv *wil, int id) wil_dbg_misc(wil, "%s() id=%d\n", __func__, id); spin_lock_bh(&txdata->lock); + txdata->dot1x_open = false; txdata->enabled = 0; /* no Tx can be in progress or start anew */ spin_unlock_bh(&txdata->lock); /* make sure NAPI won't touch this vring */ @@ -848,12 +857,11 @@ static struct vring *wil_find_tx_ucast(struct wil6210_priv *wil, if (cid < 0) return NULL; - if (!wil->sta[cid].data_port_open && - (skb->protocol != cpu_to_be16(ETH_P_PAE))) - return NULL; - /* TODO: fix for multiple TID */ for (i = 0; i < ARRAY_SIZE(wil->vring2cid_tid); i++) { + if (!wil->vring_tx_data[i].dot1x_open && + (skb->protocol != cpu_to_be16(ETH_P_PAE))) + continue; if (wil->vring2cid_tid[i][0] == cid) { struct vring *v = &wil->vring_tx[i]; @@ -883,7 +891,7 @@ static struct vring *wil_find_tx_vring_sta(struct wil6210_priv *wil, /* In the STA mode, it is expected to have only 1 VRING * for the AP we connected to. - * find 1-st vring and see whether it is eligible for data + * find 1-st vring eligible for this skb and use it. */ for (i = 0; i < WIL6210_MAX_TX_RINGS; i++) { v = &wil->vring_tx[i]; @@ -894,9 +902,9 @@ static struct vring *wil_find_tx_vring_sta(struct wil6210_priv *wil, if (cid >= WIL6210_MAX_CID) /* skip BCAST */ continue; - if (!wil->sta[cid].data_port_open && + if (!wil->vring_tx_data[i].dot1x_open && (skb->protocol != cpu_to_be16(ETH_P_PAE))) - break; + continue; wil_dbg_txrx(wil, "Tx -> ring %d\n", i); @@ -918,7 +926,6 @@ static struct vring *wil_find_tx_vring_sta(struct wil6210_priv *wil, * in all cases override dest address to unicast peer's address * Use old strategy when new is not supported yet: * - for PBSS - * - for secure link */ static struct vring *wil_find_tx_bcast_1(struct wil6210_priv *wil, struct sk_buff *skb) @@ -931,6 +938,9 @@ static struct vring *wil_find_tx_bcast_1(struct wil6210_priv *wil, v = &wil->vring_tx[i]; if (!v->va) return NULL; + if (!wil->vring_tx_data[i].dot1x_open && + (skb->protocol != cpu_to_be16(ETH_P_PAE))) + return NULL; return v; } @@ -963,7 +973,8 @@ static struct vring *wil_find_tx_bcast_2(struct wil6210_priv *wil, cid = wil->vring2cid_tid[i][0]; if (cid >= WIL6210_MAX_CID) /* skip BCAST */ continue; - if (!wil->sta[cid].data_port_open) + if (!wil->vring_tx_data[i].dot1x_open && + (skb->protocol != cpu_to_be16(ETH_P_PAE))) continue; /* don't Tx back to source when re-routing Rx->Tx at the AP */ @@ -989,7 +1000,8 @@ static struct vring *wil_find_tx_bcast_2(struct wil6210_priv *wil, cid = wil->vring2cid_tid[i][0]; if (cid >= WIL6210_MAX_CID) /* skip BCAST */ continue; - if (!wil->sta[cid].data_port_open) + if (!wil->vring_tx_data[i].dot1x_open && + (skb->protocol != cpu_to_be16(ETH_P_PAE))) continue; if (0 == memcmp(wil->sta[cid].addr, src, ETH_ALEN)) @@ -1016,9 +1028,6 @@ static struct vring *wil_find_tx_bcast(struct wil6210_priv *wil, if (wdev->iftype != NL80211_IFTYPE_AP) return wil_find_tx_bcast_2(wil, skb); - if (wil->privacy) - return wil_find_tx_bcast_2(wil, skb); - return wil_find_tx_bcast_1(wil, skb); } @@ -1144,13 +1153,8 @@ static int __wil_tx_vring(struct wil6210_priv *wil, struct vring *vring, wil_tx_desc_map(d, pa, len, vring_index); if (unlikely(mcast)) { d->mac.d[0] |= BIT(MAC_CFG_DESC_TX_0_MCS_EN_POS); /* MCS 0 */ - if (unlikely(len > WIL_BCAST_MCS0_LIMIT)) { - /* set MCS 1 */ + if (unlikely(len > WIL_BCAST_MCS0_LIMIT)) /* set MCS 1 */ d->mac.d[0] |= (1 << MAC_CFG_DESC_TX_0_MCS_INDEX_POS); - /* packet mode 2 */ - d->mac.d[1] |= BIT(MAC_CFG_DESC_TX_1_PKT_MODE_EN_POS) | - (2 << MAC_CFG_DESC_TX_1_PKT_MODE_POS); - } } /* Process TCP/UDP checksum offloading */ if (unlikely(wil_tx_desc_offload_cksum_set(wil, d, skb))) { diff --git a/drivers/net/wireless/ath/wil6210/wil6210.h b/drivers/net/wireless/ath/wil6210/wil6210.h index 9d74bd819236..f3513a1fa424 100644 --- a/drivers/net/wireless/ath/wil6210/wil6210.h +++ b/drivers/net/wireless/ath/wil6210/wil6210.h @@ -398,6 +398,7 @@ struct vring { * Additional data for Tx Vring */ struct vring_tx_data { + bool dot1x_open; int enabled; cycles_t idle, last_idle, begin; u8 agg_wsize; /* agreed aggregation window, 0 - no agg */ @@ -486,7 +487,6 @@ struct wil_sta_info { u8 addr[ETH_ALEN]; enum wil_sta_status status; struct wil_net_stats stats; - bool data_port_open; /* can send any data, not only EAPOL */ /* Rx BACK */ struct wil_tid_ampdu_rx *tid_rx[WIL_STA_TID_NUM]; spinlock_t tid_rx_lock; /* guarding tid_rx array */ @@ -716,9 +716,10 @@ int wmi_get_ssid(struct wil6210_priv *wil, u8 *ssid_len, void *ssid); int wmi_set_channel(struct wil6210_priv *wil, int channel); int wmi_get_channel(struct wil6210_priv *wil, int *channel); int wmi_del_cipher_key(struct wil6210_priv *wil, u8 key_index, - const void *mac_addr); + const void *mac_addr, int key_usage); int wmi_add_cipher_key(struct wil6210_priv *wil, u8 key_index, - const void *mac_addr, int key_len, const void *key); + const void *mac_addr, int key_len, const void *key, + int key_usage); int wmi_echo(struct wil6210_priv *wil); int wmi_set_ie(struct wil6210_priv *wil, u8 type, u16 ie_len, const void *ie); int wmi_rx_chain_add(struct wil6210_priv *wil, struct vring *vring); diff --git a/drivers/net/wireless/ath/wil6210/wmi.c b/drivers/net/wireless/ath/wil6210/wmi.c index 04fad5f4bbea..3dc8daf69bd2 100644 --- a/drivers/net/wireless/ath/wil6210/wmi.c +++ b/drivers/net/wireless/ath/wil6210/wmi.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2014 Qualcomm Atheros, Inc. + * Copyright (c) 2012-2015 Qualcomm Atheros, Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -543,55 +543,22 @@ static void wmi_evt_eapol_rx(struct wil6210_priv *wil, int id, } } -static void wil_addba_tx_cid(struct wil6210_priv *wil, u8 cid, u16 wsize) +static void wmi_evt_vring_en(struct wil6210_priv *wil, int id, void *d, int len) { - struct vring_tx_data *t; - int i; + struct wmi_vring_en_event *evt = d; + u8 vri = evt->vring_index; - for (i = 0; i < WIL6210_MAX_TX_RINGS; i++) { - if (cid != wil->vring2cid_tid[i][0]) - continue; - t = &wil->vring_tx_data[i]; - if (!t->enabled) - continue; + wil_dbg_wmi(wil, "Enable vring %d\n", vri); - wil_addba_tx_request(wil, i, wsize); - } -} - -static void wmi_evt_linkup(struct wil6210_priv *wil, int id, void *d, int len) -{ - struct wmi_data_port_open_event *evt = d; - u8 cid = evt->cid; - - wil_dbg_wmi(wil, "Link UP for CID %d\n", cid); - - if (cid >= ARRAY_SIZE(wil->sta)) { - wil_err(wil, "Link UP for invalid CID %d\n", cid); + if (vri >= ARRAY_SIZE(wil->vring_tx)) { + wil_err(wil, "Enable for invalid vring %d\n", vri); return; } - - wil->sta[cid].data_port_open = true; + wil->vring_tx_data[vri].dot1x_open = true; + if (vri == wil->bcast_vring) /* no BA for bcast */ + return; if (agg_wsize >= 0) - wil_addba_tx_cid(wil, cid, agg_wsize); -} - -static void wmi_evt_linkdown(struct wil6210_priv *wil, int id, void *d, int len) -{ - struct net_device *ndev = wil_to_ndev(wil); - struct wmi_wbe_link_down_event *evt = d; - u8 cid = evt->cid; - - wil_dbg_wmi(wil, "Link DOWN for CID %d, reason %d\n", - cid, le32_to_cpu(evt->reason)); - - if (cid >= ARRAY_SIZE(wil->sta)) { - wil_err(wil, "Link DOWN for invalid CID %d\n", cid); - return; - } - - wil->sta[cid].data_port_open = false; - netif_carrier_off(ndev); + wil_addba_tx_request(wil, vri, agg_wsize); } static void wmi_evt_ba_status(struct wil6210_priv *wil, int id, void *d, @@ -695,11 +662,10 @@ static const struct { {WMI_CONNECT_EVENTID, wmi_evt_connect}, {WMI_DISCONNECT_EVENTID, wmi_evt_disconnect}, {WMI_EAPOL_RX_EVENTID, wmi_evt_eapol_rx}, - {WMI_DATA_PORT_OPEN_EVENTID, wmi_evt_linkup}, - {WMI_WBE_LINKDOWN_EVENTID, wmi_evt_linkdown}, {WMI_BA_STATUS_EVENTID, wmi_evt_ba_status}, {WMI_RCP_ADDBA_REQ_EVENTID, wmi_evt_addba_rx_req}, {WMI_DELBA_EVENTID, wmi_evt_delba}, + {WMI_VRING_EN_EVENTID, wmi_evt_vring_en}, }; /* @@ -985,7 +951,7 @@ int wmi_p2p_cfg(struct wil6210_priv *wil, int channel) } int wmi_del_cipher_key(struct wil6210_priv *wil, u8 key_index, - const void *mac_addr) + const void *mac_addr, int key_usage) { struct wmi_delete_cipher_key_cmd cmd = { .key_index = key_index, @@ -998,11 +964,12 @@ int wmi_del_cipher_key(struct wil6210_priv *wil, u8 key_index, } int wmi_add_cipher_key(struct wil6210_priv *wil, u8 key_index, - const void *mac_addr, int key_len, const void *key) + const void *mac_addr, int key_len, const void *key, + int key_usage) { struct wmi_add_cipher_key_cmd cmd = { .key_index = key_index, - .key_usage = WMI_KEY_USE_PAIRWISE, + .key_usage = key_usage, .key_len = key_len, }; @@ -1238,7 +1205,8 @@ int wmi_addba_rx_resp(struct wil6210_priv *wil, u8 cid, u8 tid, u8 token, cid, tid, agg_wsize, timeout, status, amsdu ? "+" : "-"); rc = wmi_call(wil, WMI_RCP_ADDBA_RESP_CMDID, &cmd, sizeof(cmd), - WMI_ADDBA_RESP_SENT_EVENTID, &reply, sizeof(reply), 100); + WMI_RCP_ADDBA_RESP_SENT_EVENTID, &reply, sizeof(reply), + 100); if (rc) return rc; diff --git a/drivers/net/wireless/ath/wil6210/wmi.h b/drivers/net/wireless/ath/wil6210/wmi.h index 24c253d91363..cc04ab73b398 100644 --- a/drivers/net/wireless/ath/wil6210/wmi.h +++ b/drivers/net/wireless/ath/wil6210/wmi.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2014 Qualcomm Atheros, Inc. + * Copyright (c) 2012-2015 Qualcomm Atheros, Inc. * Copyright (c) 2006-2012 Wilocity . * * Permission to use, copy, modify, and/or distribute this software for any @@ -253,8 +253,8 @@ struct wmi_set_passphrase_cmd { */ enum wmi_key_usage { WMI_KEY_USE_PAIRWISE = 0, - WMI_KEY_USE_GROUP = 1, - WMI_KEY_USE_TX = 2, /* default Tx Key - Static WEP only */ + WMI_KEY_USE_RX_GROUP = 1, + WMI_KEY_USE_TX_GROUP = 2, }; struct wmi_add_cipher_key_cmd { @@ -885,7 +885,7 @@ enum wmi_event_id { WMI_VRING_CFG_DONE_EVENTID = 0x1821, WMI_BA_STATUS_EVENTID = 0x1823, WMI_RCP_ADDBA_REQ_EVENTID = 0x1824, - WMI_ADDBA_RESP_SENT_EVENTID = 0x1825, + WMI_RCP_ADDBA_RESP_SENT_EVENTID = 0x1825, WMI_DELBA_EVENTID = 0x1826, WMI_GET_SSID_EVENTID = 0x1828, WMI_GET_PCP_CHANNEL_EVENTID = 0x182a, @@ -897,7 +897,7 @@ enum wmi_event_id { WMI_WRITE_MAC_TXQ_EVENTID = 0x1833, WMI_WRITE_MAC_XQ_FIELD_EVENTID = 0x1834, - WMI_BEAFORMING_MGMT_DONE_EVENTID = 0x1836, + WMI_BEAMFORMING_MGMT_DONE_EVENTID = 0x1836, WMI_BF_TXSS_MGMT_DONE_EVENTID = 0x1837, WMI_BF_RXSS_MGMT_DONE_EVENTID = 0x1839, WMI_RS_MGMT_DONE_EVENTID = 0x1852, @@ -909,11 +909,12 @@ enum wmi_event_id { /* Performance monitoring events */ WMI_DATA_PORT_OPEN_EVENTID = 0x1860, - WMI_WBE_LINKDOWN_EVENTID = 0x1861, + WMI_WBE_LINK_DOWN_EVENTID = 0x1861, WMI_BF_CTRL_DONE_EVENTID = 0x1862, WMI_NOTIFY_REQ_DONE_EVENTID = 0x1863, WMI_GET_STATUS_DONE_EVENTID = 0x1864, + WMI_VRING_EN_EVENTID = 0x1865, WMI_UNIT_TEST_EVENTID = 0x1900, WMI_FLASH_READ_DONE_EVENTID = 0x1902, @@ -1162,7 +1163,7 @@ struct wmi_vring_cfg_done_event { } __packed; /* - * WMI_ADDBA_RESP_SENT_EVENTID + * WMI_RCP_ADDBA_RESP_SENT_EVENTID */ struct wmi_rcp_addba_resp_sent_event { u8 cidxtid; @@ -1194,7 +1195,7 @@ struct wmi_cfg_rx_chain_done_event { } __packed; /* - * WMI_WBE_LINKDOWN_EVENTID + * WMI_WBE_LINK_DOWN_EVENTID */ enum wmi_wbe_link_down_event_reason { WMI_WBE_REASON_USER_REQUEST = 0, @@ -1216,6 +1217,14 @@ struct wmi_data_port_open_event { u8 reserved[3]; } __packed; +/* + * WMI_VRING_EN_EVENTID + */ +struct wmi_vring_en_event { + u8 vring_index; + u8 reserved[3]; +} __packed; + /* * WMI_GET_PCP_CHANNEL_EVENTID */ From 27aa6b71d3ddd8e94613a890f06474db812cf591 Mon Sep 17 00:00:00 2001 From: Vladimir Kondratiev Date: Thu, 30 Apr 2015 16:25:11 +0300 Subject: [PATCH 13/20] wil6210: support WSC for STA mode WSC connection is secure (privacy bit set in various frames) but authentication suite, keys etc. are absent. Support this mode in the connect WMI command. Detect WSC by having 'sme->privacy' and no RSN IE. Inform firmware about WSC session by setting dot11_auth_mode to WMI_AUTH11_WSC. Signed-off-by: Vladimir Kondratiev Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/wil6210/cfg80211.c | 26 +++++++++++---------- 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/drivers/net/wireless/ath/wil6210/cfg80211.c b/drivers/net/wireless/ath/wil6210/cfg80211.c index 1b02b73c4212..dbfcdd16628a 100644 --- a/drivers/net/wireless/ath/wil6210/cfg80211.c +++ b/drivers/net/wireless/ath/wil6210/cfg80211.c @@ -402,11 +402,8 @@ static int wil_cfg80211_connect(struct wiphy *wiphy, rsn_eid = sme->ie ? cfg80211_find_ie(WLAN_EID_RSN, sme->ie, sme->ie_len) : NULL; - - if (sme->privacy && !rsn_eid) { - wil_err(wil, "Missing RSN IE for secure connection\n"); - return -EINVAL; - } + if (sme->privacy && !rsn_eid) + wil_info(wil, "WSC connection\n"); bss = cfg80211_get_bss(wiphy, sme->channel, sme->bssid, sme->ssid, sme->ssid_len, @@ -465,13 +462,18 @@ static int wil_cfg80211_connect(struct wiphy *wiphy, goto out; } if (wil->privacy) { - conn.dot11_auth_mode = WMI_AUTH11_SHARED; - conn.auth_mode = WMI_AUTH_WPA2_PSK; - conn.pairwise_crypto_type = WMI_CRYPT_AES_GCMP; - conn.pairwise_crypto_len = 16; - conn.group_crypto_type = WMI_CRYPT_AES_GCMP; - conn.group_crypto_len = 16; - } else { + if (rsn_eid) { /* regular secure connection */ + conn.dot11_auth_mode = WMI_AUTH11_SHARED; + conn.auth_mode = WMI_AUTH_WPA2_PSK; + conn.pairwise_crypto_type = WMI_CRYPT_AES_GCMP; + conn.pairwise_crypto_len = 16; + conn.group_crypto_type = WMI_CRYPT_AES_GCMP; + conn.group_crypto_len = 16; + } else { /* WSC */ + conn.dot11_auth_mode = WMI_AUTH11_WSC; + conn.auth_mode = WMI_AUTH_NONE; + } + } else { /* insecure connection */ conn.dot11_auth_mode = WMI_AUTH11_OPEN; conn.auth_mode = WMI_AUTH_NONE; } From 184a394eea16d61b85444c73349ced7f91f79af2 Mon Sep 17 00:00:00 2001 From: Peter Oh Date: Mon, 4 May 2015 11:21:24 -0700 Subject: [PATCH 14/20] ath10k: increase relay buffer size of spectral scan Spectral scan supported by ath10k has capability to monitor and report through whole bands and channels, but current buffer size is too small to save reported spectral scan data. This results in dropping 5G channel reports at all when dual band card is used, so that users are not able to analyze spectral environments. Hence increase the buffer size to fix the problem. A spectral data size is vary based on the number of bins, so the unit buffer size, 1140, is chose to minimize relay buffer fragmentation. The total buffer size is also chose in tradeoff with spectral scan support vs. kernel memory consumption. Since theoretical maximum buffer size, 9.5MB, can be consumed with 512 bins in dual bands, we target to cover up to 128 bins for all channels in dual bands and due to the buffer size limitation, spectral scan with bin number bigger than 128 needs to be run on single band each. Signed-off-by: Peter Oh Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/spectral.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath10k/spectral.c b/drivers/net/wireless/ath/ath10k/spectral.c index d22addf6118b..8dcd424aa502 100644 --- a/drivers/net/wireless/ath/ath10k/spectral.c +++ b/drivers/net/wireless/ath/ath10k/spectral.c @@ -519,9 +519,12 @@ int ath10k_spectral_vif_stop(struct ath10k_vif *arvif) int ath10k_spectral_create(struct ath10k *ar) { + /* The buffer size covers whole channels in dual bands up to 128 bins. + * Scan with bigger than 128 bins needs to be run on single band each. + */ ar->spectral.rfs_chan_spec_scan = relay_open("spectral_scan", ar->debug.debugfs_phy, - 1024, 256, + 1140, 2500, &rfs_spec_scan_cb, NULL); debugfs_create_file("spectral_scan_ctl", S_IRUSR | S_IWUSR, From 2c2d2fafe21652789437e5a1a8b2770a45adc259 Mon Sep 17 00:00:00 2001 From: Vasanthakumar Thiagarajan Date: Tue, 5 May 2015 18:00:48 +0530 Subject: [PATCH 15/20] ath10k: fix survey information reporting Rx clear count reported in wmi_chan_info_event is actually channel_busy_count not rx_frame_count. Send rx_clear_count through time_busy of survey_info and set SURVEY_INFO_TIME_BUSY in filled. iw wlan0 survey dump urvey data from wlan0 frequency: 5180 MHz [in use] noise: -103 dBm channel active time: 150 ms channel busy time: 22 ms Survey data from wlan0 frequency: 5200 MHz noise: -102 dBm channel active time: 146 ms channel busy time: 0 ms Signed-off-by: Vasanthakumar Thiagarajan Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/wmi.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/ath/ath10k/wmi.c b/drivers/net/wireless/ath/ath10k/wmi.c index ebaa096cf200..0fabe689179c 100644 --- a/drivers/net/wireless/ath/ath10k/wmi.c +++ b/drivers/net/wireless/ath/ath10k/wmi.c @@ -1645,10 +1645,10 @@ void ath10k_wmi_event_chan_info(struct ath10k *ar, struct sk_buff *skb) survey = &ar->survey[idx]; survey->time = WMI_CHAN_INFO_MSEC(cycle_count); - survey->time_rx = WMI_CHAN_INFO_MSEC(rx_clear_count); + survey->time_busy = WMI_CHAN_INFO_MSEC(rx_clear_count); survey->noise = noise_floor; survey->filled = SURVEY_INFO_TIME | - SURVEY_INFO_TIME_RX | + SURVEY_INFO_TIME_BUSY | SURVEY_INFO_NOISE_DBM; } From 80714b09bc574c5b23e50264e76e6e0f78d23549 Mon Sep 17 00:00:00 2001 From: Vladimir Kondratiev Date: Mon, 11 May 2015 19:10:48 +0300 Subject: [PATCH 16/20] wil6210: fix format specifier for dma_addr_t Fix format specifier used for dma_addr_t, namely use %pad Debug print virtual address for the same buffer as well. Fixes: dc16427bbe65 ("wil6210: Add pmc debug mechanism memory management") Signed-off-by: Vladimir Kondratiev Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/wil6210/pmc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/ath/wil6210/pmc.c b/drivers/net/wireless/ath/wil6210/pmc.c index 3cb4f35ebd05..8a8cdc61b25b 100644 --- a/drivers/net/wireless/ath/wil6210/pmc.c +++ b/drivers/net/wireless/ath/wil6210/pmc.c @@ -92,9 +92,9 @@ void wil_pmc_alloc(struct wil6210_priv *wil, GFP_KERNEL); wil_dbg_misc(wil, - "%s: allocated pring %p. %zd x %d = total %zd bytes\n", + "%s: allocated pring %p => %pad. %zd x %d = total %zd bytes\n", __func__, - (void *)pmc->pring_pa, + pmc->pring_va, &pmc->pring_pa, sizeof(struct vring_tx_desc), num_descriptors, sizeof(struct vring_tx_desc) * num_descriptors); From 76d870ed09ab34154454b1adb823ae75f173c2d2 Mon Sep 17 00:00:00 2001 From: Janusz Dziedzic Date: Mon, 18 May 2015 09:38:16 +0000 Subject: [PATCH 17/20] ath10k: enable ASPM It is actually safe to enable ASPM after the device is booted up. This reduces power drain of QCA61X4 when driver is simply loaded (no interface is up) from 31mA to 14mA. QCA988X wasn't measured but doesn't seem to regress in any other way. Signed-off-by: Janusz Dziedzic Signed-off-by: Michal Kazior Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/pci.c | 15 ++++++++++----- drivers/net/wireless/ath/ath10k/pci.h | 6 ++++++ 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/ath/ath10k/pci.c b/drivers/net/wireless/ath/ath10k/pci.c index 969a1231800e..8be07c653b2d 100644 --- a/drivers/net/wireless/ath/ath10k/pci.c +++ b/drivers/net/wireless/ath/ath10k/pci.c @@ -1227,11 +1227,15 @@ static void ath10k_pci_irq_enable(struct ath10k *ar) static int ath10k_pci_hif_start(struct ath10k *ar) { + struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot hif start\n"); ath10k_pci_irq_enable(ar); ath10k_pci_rx_post(ar); + pcie_capability_write_word(ar_pci->pdev, PCI_EXP_LNKCTL, + ar_pci->link_ctl); + return 0; } @@ -1981,6 +1985,7 @@ static int ath10k_pci_chip_reset(struct ath10k *ar) static int ath10k_pci_hif_power_up(struct ath10k *ar) { + struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); int ret; ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot hif power up\n"); @@ -1991,6 +1996,11 @@ static int ath10k_pci_hif_power_up(struct ath10k *ar) return ret; } + pcie_capability_read_word(ar_pci->pdev, PCI_EXP_LNKCTL, + &ar_pci->link_ctl); + pcie_capability_write_word(ar_pci->pdev, PCI_EXP_LNKCTL, + ar_pci->link_ctl & ~PCI_EXP_LNKCTL_ASPMC); + /* * Bring the target up cleanly. * @@ -2502,7 +2512,6 @@ static int ath10k_pci_claim(struct ath10k *ar) { struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); struct pci_dev *pdev = ar_pci->pdev; - u32 lcr_val; int ret; pci_set_drvdata(pdev, ar); @@ -2536,10 +2545,6 @@ static int ath10k_pci_claim(struct ath10k *ar) pci_set_master(pdev); - /* Workaround: Disable ASPM */ - pci_read_config_dword(pdev, 0x80, &lcr_val); - pci_write_config_dword(pdev, 0x80, (lcr_val & 0xffffff00)); - /* Arrange for access to Target SoC registers. */ ar_pci->mem = pci_iomap(pdev, BAR_NUM, 0); if (!ar_pci->mem) { diff --git a/drivers/net/wireless/ath/ath10k/pci.h b/drivers/net/wireless/ath/ath10k/pci.h index bddf54320160..ee2173d61257 100644 --- a/drivers/net/wireless/ath/ath10k/pci.h +++ b/drivers/net/wireless/ath/ath10k/pci.h @@ -185,6 +185,12 @@ struct ath10k_pci { /* Map CE id to ce_state */ struct ath10k_ce_pipe ce_states[CE_COUNT_MAX]; struct timer_list rx_post_retry; + + /* Due to HW quirks it is recommended to disable ASPM during device + * bootup. To do that the original PCI-E Link Control is stored before + * device bootup is executed and re-programmed later. + */ + u16 link_ctl; }; static inline struct ath10k_pci *ath10k_pci_priv(struct ath10k *ar) From 917826be9f46b13604fdfc9954dd9ce69dea93f3 Mon Sep 17 00:00:00 2001 From: Janusz Dziedzic Date: Mon, 18 May 2015 09:38:17 +0000 Subject: [PATCH 18/20] ath10k: fix idle power consumption mac80211 can update vif powersave state while disconnected. Firmware doesn't behave nicely and consumes more power than necessary if PS is disabled on a non-started vdev. Hence force-enable PS for non-running vdevs. This reduces power drain on QCA61X4 from 88mA to 36mA when interface is up and not associated. QCA988X wasn't measured. Signed-off-by: Janusz Dziedzic Signed-off-by: Michal Kazior Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/mac.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c index eaa0182e001d..2cc0cbadd379 100644 --- a/drivers/net/wireless/ath/ath10k/mac.c +++ b/drivers/net/wireless/ath/ath10k/mac.c @@ -1738,7 +1738,14 @@ static int ath10k_mac_vif_setup_ps(struct ath10k_vif *arvif) enable_ps = false; } - if (enable_ps) { + if (!arvif->is_started) { + /* mac80211 can update vif powersave state while disconnected. + * Firmware doesn't behave nicely and consumes more power than + * necessary if PS is disabled on a non-started vdev. Hence + * force-enable PS for non-running vdevs. + */ + psmode = WMI_STA_PS_MODE_ENABLED; + } else if (enable_ps) { psmode = WMI_STA_PS_MODE_ENABLED; param = WMI_STA_PS_PARAM_INACTIVITY_TIME; From 77258d409ce45890104e3da11d0261402c49aee1 Mon Sep 17 00:00:00 2001 From: Michal Kazior Date: Mon, 18 May 2015 09:38:18 +0000 Subject: [PATCH 19/20] ath10k: enable pci soc powersaving By using SOC_WAKE register it is possible to bring down power consumption of QCA61X4 from 36mA to 16mA when associated and idle. Currently the sleep threshold/grace period is at a very conservative value of 60ms. Contrary to QCA61X4 the QCA988X firmware doesn't have Rx/beacon filtering available for client mode and SWBA events are used for beaconing in AP/IBSS so the SoC needs to be woken up at least every ~100ms in most cases. This means that QCA988X is at a disadvantage and the power consumption won't drop as much as for QCA61X4. Due to putting irq-safe spinlocks on every MMIO read/write it is expected this can cause a little performance regression on some systems. I haven't done any thorough measurements but some of my tests don't show any extreme degradation. The patch removes some explicit pci_wake calls that were added in 320e14b8db51aa ("ath10k: fix some pci wake/sleep issues"). This is safe because all MMIO accesses are now wrapped and the device is woken up automatically if necessary. Signed-off-by: Michal Kazior Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/debug.h | 1 + drivers/net/wireless/ath/ath10k/pci.c | 304 +++++++++++++++++------- drivers/net/wireless/ath/ath10k/pci.h | 91 ++++--- 3 files changed, 261 insertions(+), 135 deletions(-) diff --git a/drivers/net/wireless/ath/ath10k/debug.h b/drivers/net/wireless/ath/ath10k/debug.h index a12b8323f9f1..53bd6a19eab6 100644 --- a/drivers/net/wireless/ath/ath10k/debug.h +++ b/drivers/net/wireless/ath/ath10k/debug.h @@ -36,6 +36,7 @@ enum ath10k_debug_mask { ATH10K_DBG_REGULATORY = 0x00000800, ATH10K_DBG_TESTMODE = 0x00001000, ATH10K_DBG_WMI_PRINT = 0x00002000, + ATH10K_DBG_PCI_PS = 0x00004000, ATH10K_DBG_ANY = 0xffffffff, }; diff --git a/drivers/net/wireless/ath/ath10k/pci.c b/drivers/net/wireless/ath/ath10k/pci.c index 8be07c653b2d..17a060e8efa2 100644 --- a/drivers/net/wireless/ath/ath10k/pci.c +++ b/drivers/net/wireless/ath/ath10k/pci.c @@ -330,6 +330,205 @@ static const struct service_to_pipe target_service_to_ce_map_wlan[] = { }, }; +static bool ath10k_pci_is_awake(struct ath10k *ar) +{ + struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); + u32 val = ioread32(ar_pci->mem + PCIE_LOCAL_BASE_ADDRESS + + RTC_STATE_ADDRESS); + + return RTC_STATE_V_GET(val) == RTC_STATE_V_ON; +} + +static void __ath10k_pci_wake(struct ath10k *ar) +{ + struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); + + lockdep_assert_held(&ar_pci->ps_lock); + + ath10k_dbg(ar, ATH10K_DBG_PCI_PS, "pci ps wake reg refcount %lu awake %d\n", + ar_pci->ps_wake_refcount, ar_pci->ps_awake); + + iowrite32(PCIE_SOC_WAKE_V_MASK, + ar_pci->mem + PCIE_LOCAL_BASE_ADDRESS + + PCIE_SOC_WAKE_ADDRESS); +} + +static void __ath10k_pci_sleep(struct ath10k *ar) +{ + struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); + + lockdep_assert_held(&ar_pci->ps_lock); + + ath10k_dbg(ar, ATH10K_DBG_PCI_PS, "pci ps sleep reg refcount %lu awake %d\n", + ar_pci->ps_wake_refcount, ar_pci->ps_awake); + + iowrite32(PCIE_SOC_WAKE_RESET, + ar_pci->mem + PCIE_LOCAL_BASE_ADDRESS + + PCIE_SOC_WAKE_ADDRESS); + ar_pci->ps_awake = false; +} + +static int ath10k_pci_wake_wait(struct ath10k *ar) +{ + int tot_delay = 0; + int curr_delay = 5; + + while (tot_delay < PCIE_WAKE_TIMEOUT) { + if (ath10k_pci_is_awake(ar)) + return 0; + + udelay(curr_delay); + tot_delay += curr_delay; + + if (curr_delay < 50) + curr_delay += 5; + } + + return -ETIMEDOUT; +} + +static int ath10k_pci_wake(struct ath10k *ar) +{ + struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); + unsigned long flags; + int ret = 0; + + spin_lock_irqsave(&ar_pci->ps_lock, flags); + + ath10k_dbg(ar, ATH10K_DBG_PCI_PS, "pci ps wake refcount %lu awake %d\n", + ar_pci->ps_wake_refcount, ar_pci->ps_awake); + + /* This function can be called very frequently. To avoid excessive + * CPU stalls for MMIO reads use a cache var to hold the device state. + */ + if (!ar_pci->ps_awake) { + __ath10k_pci_wake(ar); + + ret = ath10k_pci_wake_wait(ar); + if (ret == 0) + ar_pci->ps_awake = true; + } + + if (ret == 0) { + ar_pci->ps_wake_refcount++; + WARN_ON(ar_pci->ps_wake_refcount == 0); + } + + spin_unlock_irqrestore(&ar_pci->ps_lock, flags); + + return ret; +} + +static void ath10k_pci_sleep(struct ath10k *ar) +{ + struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); + unsigned long flags; + + spin_lock_irqsave(&ar_pci->ps_lock, flags); + + ath10k_dbg(ar, ATH10K_DBG_PCI_PS, "pci ps sleep refcount %lu awake %d\n", + ar_pci->ps_wake_refcount, ar_pci->ps_awake); + + if (WARN_ON(ar_pci->ps_wake_refcount == 0)) + goto skip; + + ar_pci->ps_wake_refcount--; + + mod_timer(&ar_pci->ps_timer, jiffies + + msecs_to_jiffies(ATH10K_PCI_SLEEP_GRACE_PERIOD_MSEC)); + +skip: + spin_unlock_irqrestore(&ar_pci->ps_lock, flags); +} + +static void ath10k_pci_ps_timer(unsigned long ptr) +{ + struct ath10k *ar = (void *)ptr; + struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); + unsigned long flags; + + spin_lock_irqsave(&ar_pci->ps_lock, flags); + + ath10k_dbg(ar, ATH10K_DBG_PCI_PS, "pci ps timer refcount %lu awake %d\n", + ar_pci->ps_wake_refcount, ar_pci->ps_awake); + + if (ar_pci->ps_wake_refcount > 0) + goto skip; + + __ath10k_pci_sleep(ar); + +skip: + spin_unlock_irqrestore(&ar_pci->ps_lock, flags); +} + +static void ath10k_pci_sleep_sync(struct ath10k *ar) +{ + struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); + unsigned long flags; + + del_timer_sync(&ar_pci->ps_timer); + + spin_lock_irqsave(&ar_pci->ps_lock, flags); + WARN_ON(ar_pci->ps_wake_refcount > 0); + __ath10k_pci_sleep(ar); + spin_unlock_irqrestore(&ar_pci->ps_lock, flags); +} + +void ath10k_pci_write32(struct ath10k *ar, u32 offset, u32 value) +{ + struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); + int ret; + + ret = ath10k_pci_wake(ar); + if (ret) { + ath10k_warn(ar, "failed to wake target for write32 of 0x%08x at 0x%08x: %d\n", + value, offset, ret); + return; + } + + iowrite32(value, ar_pci->mem + offset); + ath10k_pci_sleep(ar); +} + +u32 ath10k_pci_read32(struct ath10k *ar, u32 offset) +{ + struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); + u32 val; + int ret; + + ret = ath10k_pci_wake(ar); + if (ret) { + ath10k_warn(ar, "failed to wake target for read32 at 0x%08x: %d\n", + offset, ret); + return 0xffffffff; + } + + val = ioread32(ar_pci->mem + offset); + ath10k_pci_sleep(ar); + + return val; +} + +u32 ath10k_pci_soc_read32(struct ath10k *ar, u32 addr) +{ + return ath10k_pci_read32(ar, RTC_SOC_BASE_ADDRESS + addr); +} + +void ath10k_pci_soc_write32(struct ath10k *ar, u32 addr, u32 val) +{ + ath10k_pci_write32(ar, RTC_SOC_BASE_ADDRESS + addr, val); +} + +u32 ath10k_pci_reg_read32(struct ath10k *ar, u32 addr) +{ + return ath10k_pci_read32(ar, PCIE_LOCAL_BASE_ADDRESS + addr); +} + +void ath10k_pci_reg_write32(struct ath10k *ar, u32 addr, u32 val) +{ + ath10k_pci_write32(ar, PCIE_LOCAL_BASE_ADDRESS + addr, val); +} + static bool ath10k_pci_irq_pending(struct ath10k *ar) { u32 cause; @@ -793,60 +992,6 @@ static int ath10k_pci_diag_write32(struct ath10k *ar, u32 address, u32 value) return ath10k_pci_diag_write_mem(ar, address, &val, sizeof(val)); } -static bool ath10k_pci_is_awake(struct ath10k *ar) -{ - u32 val = ath10k_pci_reg_read32(ar, RTC_STATE_ADDRESS); - - return RTC_STATE_V_GET(val) == RTC_STATE_V_ON; -} - -static int ath10k_pci_wake_wait(struct ath10k *ar) -{ - int tot_delay = 0; - int curr_delay = 5; - - while (tot_delay < PCIE_WAKE_TIMEOUT) { - if (ath10k_pci_is_awake(ar)) - return 0; - - udelay(curr_delay); - tot_delay += curr_delay; - - if (curr_delay < 50) - curr_delay += 5; - } - - return -ETIMEDOUT; -} - -/* The rule is host is forbidden from accessing device registers while it's - * asleep. Currently ath10k_pci_wake() and ath10k_pci_sleep() calls aren't - * balanced and the device is kept awake all the time. This is intended for a - * simpler solution for the following problems: - * - * * device can enter sleep during s2ram without the host knowing, - * - * * irq handlers access registers which is a problem if other device asserts - * a shared irq line when ath10k is between hif_power_down() and - * hif_power_up(). - * - * FIXME: If power consumption is a concern (and there are *real* gains) then a - * refcounted wake/sleep needs to be implemented. - */ - -static int ath10k_pci_wake(struct ath10k *ar) -{ - ath10k_pci_reg_write32(ar, PCIE_SOC_WAKE_ADDRESS, - PCIE_SOC_WAKE_V_MASK); - return ath10k_pci_wake_wait(ar); -} - -static void ath10k_pci_sleep(struct ath10k *ar) -{ - ath10k_pci_reg_write32(ar, PCIE_SOC_WAKE_ADDRESS, - PCIE_SOC_WAKE_RESET); -} - /* Called by lower (CE) layer when a send to Target completes. */ static void ath10k_pci_ce_send_done(struct ath10k_ce_pipe *ce_state) { @@ -1348,6 +1493,9 @@ static void ath10k_pci_flush(struct ath10k *ar) static void ath10k_pci_hif_stop(struct ath10k *ar) { + struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); + unsigned long flags; + ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot hif stop\n"); /* Most likely the device has HTT Rx ring configured. The only way to @@ -1366,6 +1514,10 @@ static void ath10k_pci_hif_stop(struct ath10k *ar) ath10k_pci_irq_disable(ar); ath10k_pci_irq_sync(ar); ath10k_pci_flush(ar); + + spin_lock_irqsave(&ar_pci->ps_lock, flags); + WARN_ON(ar_pci->ps_wake_refcount > 0); + spin_unlock_irqrestore(&ar_pci->ps_lock, flags); } static int ath10k_pci_hif_exchange_bmi_msg(struct ath10k *ar, @@ -1990,12 +2142,6 @@ static int ath10k_pci_hif_power_up(struct ath10k *ar) ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot hif power up\n"); - ret = ath10k_pci_wake(ar); - if (ret) { - ath10k_err(ar, "failed to wake up target: %d\n", ret); - return ret; - } - pcie_capability_read_word(ar_pci->pdev, PCI_EXP_LNKCTL, &ar_pci->link_ctl); pcie_capability_write_word(ar_pci->pdev, PCI_EXP_LNKCTL, @@ -2047,7 +2193,6 @@ static int ath10k_pci_hif_power_up(struct ath10k *ar) ath10k_pci_ce_deinit(ar); err_sleep: - ath10k_pci_sleep(ar); return ret; } @@ -2064,7 +2209,12 @@ static void ath10k_pci_hif_power_down(struct ath10k *ar) static int ath10k_pci_hif_suspend(struct ath10k *ar) { - ath10k_pci_sleep(ar); + /* The grace timer can still be counting down and ar->ps_awake be true. + * It is known that the device may be asleep after resuming regardless + * of the SoC powersave state before suspending. Hence make sure the + * device is asleep before proceeding. + */ + ath10k_pci_sleep_sync(ar); return 0; } @@ -2074,13 +2224,6 @@ static int ath10k_pci_hif_resume(struct ath10k *ar) struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); struct pci_dev *pdev = ar_pci->pdev; u32 val; - int ret; - - ret = ath10k_pci_wake(ar); - if (ret) { - ath10k_err(ar, "failed to wake device up on resume: %d\n", ret); - return ret; - } /* Suspend/Resume resets the PCI configuration space, so we have to * re-disable the RETRY_TIMEOUT register (0x41) to keep PCI Tx retries @@ -2091,7 +2234,7 @@ static int ath10k_pci_hif_resume(struct ath10k *ar) if ((val & 0x0000ff00) != 0) pci_write_config_dword(pdev, 0x40, val & 0xffff00ff); - return ret; + return 0; } #endif @@ -2185,13 +2328,6 @@ static irqreturn_t ath10k_pci_interrupt_handler(int irq, void *arg) { struct ath10k *ar = arg; struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); - int ret; - - ret = ath10k_pci_wake(ar); - if (ret) { - ath10k_warn(ar, "failed to wake device up on irq: %d\n", ret); - return IRQ_NONE; - } if (ar_pci->num_msi_intrs == 0) { if (!ath10k_pci_irq_pending(ar)) @@ -2638,8 +2774,12 @@ static int ath10k_pci_probe(struct pci_dev *pdev, pdev->subsystem_vendor, pdev->subsystem_device); spin_lock_init(&ar_pci->ce_lock); + spin_lock_init(&ar_pci->ps_lock); + setup_timer(&ar_pci->rx_post_retry, ath10k_pci_rx_replenish_retry, (unsigned long)ar); + setup_timer(&ar_pci->ps_timer, ath10k_pci_ps_timer, + (unsigned long)ar); ret = ath10k_pci_claim(ar); if (ret) { @@ -2647,12 +2787,6 @@ static int ath10k_pci_probe(struct pci_dev *pdev, goto err_core_destroy; } - ret = ath10k_pci_wake(ar); - if (ret) { - ath10k_err(ar, "failed to wake up: %d\n", ret); - goto err_release; - } - ret = ath10k_pci_alloc_pipes(ar); if (ret) { ath10k_err(ar, "failed to allocate copy engine pipes: %d\n", @@ -2716,9 +2850,6 @@ static int ath10k_pci_probe(struct pci_dev *pdev, ath10k_pci_free_pipes(ar); err_sleep: - ath10k_pci_sleep(ar); - -err_release: ath10k_pci_release(ar); err_core_destroy: @@ -2748,6 +2879,7 @@ static void ath10k_pci_remove(struct pci_dev *pdev) ath10k_pci_deinit_irq(ar); ath10k_pci_ce_deinit(ar); ath10k_pci_free_pipes(ar); + ath10k_pci_sleep_sync(ar); ath10k_pci_release(ar); ath10k_core_destroy(ar); } diff --git a/drivers/net/wireless/ath/ath10k/pci.h b/drivers/net/wireless/ath/ath10k/pci.h index ee2173d61257..d7696ddc03c4 100644 --- a/drivers/net/wireless/ath/ath10k/pci.h +++ b/drivers/net/wireless/ath/ath10k/pci.h @@ -191,6 +191,35 @@ struct ath10k_pci { * device bootup is executed and re-programmed later. */ u16 link_ctl; + + /* Protects ps_awake and ps_wake_refcount */ + spinlock_t ps_lock; + + /* The device has a special powersave-oriented register. When device is + * considered asleep it drains less power and driver is forbidden from + * accessing most MMIO registers. If host were to access them without + * waking up the device might scribble over host memory or return + * 0xdeadbeef readouts. + */ + unsigned long ps_wake_refcount; + + /* Waking up takes some time (up to 2ms in some cases) so it can be bad + * for latency. To mitigate this the device isn't immediately allowed + * to sleep after all references are undone - instead there's a grace + * period after which the powersave register is updated unless some + * activity to/from device happened in the meantime. + * + * Also see comments on ATH10K_PCI_SLEEP_GRACE_PERIOD_MSEC. + */ + struct timer_list ps_timer; + + /* MMIO registers are used to communicate with the device. With + * intensive traffic accessing powersave register would be a bit + * wasteful overhead and would needlessly stall CPU. It is far more + * efficient to rely on a variable in RAM and update it only upon + * powersave register state changes. + */ + bool ps_awake; }; static inline struct ath10k_pci *ath10k_pci_priv(struct ath10k *ar) @@ -215,61 +244,25 @@ static inline struct ath10k_pci *ath10k_pci_priv(struct ath10k *ar) * for this device; but that's not guaranteed. */ #define TARG_CPU_SPACE_TO_CE_SPACE(ar, pci_addr, addr) \ - (((ioread32((pci_addr)+(SOC_CORE_BASE_ADDRESS| \ + (((ath10k_pci_read32(ar, (SOC_CORE_BASE_ADDRESS | \ CORE_CTRL_ADDRESS)) & 0x7ff) << 21) | \ 0x100000 | ((addr) & 0xfffff)) /* Wait up to this many Ms for a Diagnostic Access CE operation to complete */ #define DIAG_ACCESS_CE_TIMEOUT_MS 10 -/* Target exposes its registers for direct access. However before host can - * access them it needs to make sure the target is awake (ath10k_pci_wake, - * ath10k_pci_wake_wait, ath10k_pci_is_awake). Once target is awake it won't go - * to sleep unless host tells it to (ath10k_pci_sleep). - * - * If host tries to access target registers without waking it up it can - * scribble over host memory. - * - * If target is asleep waking it up may take up to even 2ms. +void ath10k_pci_write32(struct ath10k *ar, u32 offset, u32 value); +void ath10k_pci_soc_write32(struct ath10k *ar, u32 addr, u32 val); +void ath10k_pci_reg_write32(struct ath10k *ar, u32 addr, u32 val); + +u32 ath10k_pci_read32(struct ath10k *ar, u32 offset); +u32 ath10k_pci_soc_read32(struct ath10k *ar, u32 addr); +u32 ath10k_pci_reg_read32(struct ath10k *ar, u32 addr); + +/* QCA6174 is known to have Tx/Rx issues when SOC_WAKE register is poked too + * frequently. To avoid this put SoC to sleep after a very conservative grace + * period. Adjust with great care. */ - -static inline void ath10k_pci_write32(struct ath10k *ar, u32 offset, - u32 value) -{ - struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); - - iowrite32(value, ar_pci->mem + offset); -} - -static inline u32 ath10k_pci_read32(struct ath10k *ar, u32 offset) -{ - struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); - - return ioread32(ar_pci->mem + offset); -} - -static inline u32 ath10k_pci_soc_read32(struct ath10k *ar, u32 addr) -{ - return ath10k_pci_read32(ar, RTC_SOC_BASE_ADDRESS + addr); -} - -static inline void ath10k_pci_soc_write32(struct ath10k *ar, u32 addr, u32 val) -{ - ath10k_pci_write32(ar, RTC_SOC_BASE_ADDRESS + addr, val); -} - -static inline u32 ath10k_pci_reg_read32(struct ath10k *ar, u32 addr) -{ - struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); - - return ioread32(ar_pci->mem + PCIE_LOCAL_BASE_ADDRESS + addr); -} - -static inline void ath10k_pci_reg_write32(struct ath10k *ar, u32 addr, u32 val) -{ - struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); - - iowrite32(val, ar_pci->mem + PCIE_LOCAL_BASE_ADDRESS + addr); -} +#define ATH10K_PCI_SLEEP_GRACE_PERIOD_MSEC 60 #endif /* _PCI_H_ */ From 48f4ca34f36bb947d8a7cd4ff8c8e282f14d51e6 Mon Sep 17 00:00:00 2001 From: Michal Kazior Date: Tue, 19 May 2015 14:09:34 +0200 Subject: [PATCH 20/20] ath10k: add new 4addr related fw_feature Some firmware revisions pad 4th hw address in Native Wifi Rx decap. This is the case with 10.x and it was assumed that this is true for all firmware images. However QCA988X with 999.999.0.636 and QCA61X4 with WLAN.RM.2.0-00088 don't have the padding. Hence add a feature flag indicating that the padding isn't present so firmware images can advertise it appropriately. This way driver will behave as it was before with old firmware blobs and doesn't cause any regressions from user perspective. Effectively this patch enables QCA988X with 999.999.0.636 and QCA61X4 with WLAN.RM.2.0-00088 to set up client bridging provided user has an updated firmware blob. Signed-off-by: Michal Kazior Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/core.h | 5 +++++ drivers/net/wireless/ath/ath10k/htt_rx.c | 14 ++++++++++---- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h index 827b3d79ed0c..70fcdc9c2758 100644 --- a/drivers/net/wireless/ath/ath10k/core.h +++ b/drivers/net/wireless/ath/ath10k/core.h @@ -463,6 +463,11 @@ enum ath10k_fw_features { /* Don't trust error code from otp.bin */ ATH10K_FW_FEATURE_IGNORE_OTP_RESULT, + /* Some firmware revisions pad 4th hw address to 4 byte boundary making + * it 8 bytes long in Native Wifi Rx decap. + */ + ATH10K_FW_FEATURE_NO_NWIFI_DECAP_4ADDR_PADDING, + /* keep last */ ATH10K_FW_FEATURE_COUNT, }; diff --git a/drivers/net/wireless/ath/ath10k/htt_rx.c b/drivers/net/wireless/ath/ath10k/htt_rx.c index b26e32f42656..89eb16b30fc4 100644 --- a/drivers/net/wireless/ath/ath10k/htt_rx.c +++ b/drivers/net/wireless/ath/ath10k/htt_rx.c @@ -965,10 +965,16 @@ static void ath10k_process_rx(struct ath10k *ar, ieee80211_rx(ar->hw, skb); } -static int ath10k_htt_rx_nwifi_hdrlen(struct ieee80211_hdr *hdr) +static int ath10k_htt_rx_nwifi_hdrlen(struct ath10k *ar, + struct ieee80211_hdr *hdr) { - /* nwifi header is padded to 4 bytes. this fixes 4addr rx */ - return round_up(ieee80211_hdrlen(hdr->frame_control), 4); + int len = ieee80211_hdrlen(hdr->frame_control); + + if (!test_bit(ATH10K_FW_FEATURE_NO_NWIFI_DECAP_4ADDR_PADDING, + ar->fw_features)) + len = round_up(len, 4); + + return len; } static void ath10k_htt_rx_h_undecap_raw(struct ath10k *ar, @@ -1067,7 +1073,7 @@ static void ath10k_htt_rx_h_undecap_nwifi(struct ath10k *ar, /* pull decapped header and copy SA & DA */ hdr = (struct ieee80211_hdr *)msdu->data; - hdr_len = ath10k_htt_rx_nwifi_hdrlen(hdr); + hdr_len = ath10k_htt_rx_nwifi_hdrlen(ar, hdr); ether_addr_copy(da, ieee80211_get_DA(hdr)); ether_addr_copy(sa, ieee80211_get_SA(hdr)); skb_pull(msdu, hdr_len);