- HW optimization support: SG, GRO
- few updates for the card reset flow
- assorted bug fixes
Vladimir Kondratiev (21):
wil6210: Helpers to deal with 'cidxtid' fields
wil6210: Block data till "data port open" reported
wil6210: enable scan while connected
wil6210: add scatter-gather support
wil6210: generalize tx desc mapping
wil6210: update target reset to support new HW
wil6210: reduce dmesg pollution after FW crash
wil6210: report reset time
wil6210: fix for HW bug in interrupt setup logic
wil6210: sort HW registers definitions
wil6210: reset on power good
wil6210: reduce printing
wil6210: fix memory leak in the AP flow
wil6210: Fix kernel oops in reset flow
wil6210: fw error recovery
wil6210: fix secondary connect
wil6210: serialize fw_recovery and start_ap
wil6210: use GRO
wil6210: target reset flow update
wil6210: add memory barriers for the reset flow
wil6210: fix race between disconnect and Tx NAPI
drivers/net/wireless/ath/wil6210/cfg80211.c | 43 ++++--
drivers/net/wireless/ath/wil6210/debugfs.c | 64 +++++---
drivers/net/wireless/ath/wil6210/interrupt.c | 33 +++-
drivers/net/wireless/ath/wil6210/main.c | 119 +++++++++++++-
drivers/net/wireless/ath/wil6210/netdev.c | 5 +-
drivers/net/wireless/ath/wil6210/pcie_bus.c | 5 +
drivers/net/wireless/ath/wil6210/txrx.c | 223 +++++++++++++++++----------
drivers/net/wireless/ath/wil6210/wil6210.h | 82 ++++++++--
drivers/net/wireless/ath/wil6210/wmi.c | 20 ++-
9 files changed, 454 insertions(+), 140 deletions(-)
--
1.8.3.2
Hardware bug triggered by the MSI init while INTx asserted for some reason.
De-assert INTx prior to MSI set-up
Signed-off-by: Vladimir Kondratiev <[email protected]>
---
drivers/net/wireless/ath/wil6210/interrupt.c | 17 +++++++++++++++++
drivers/net/wireless/ath/wil6210/pcie_bus.c | 1 +
drivers/net/wireless/ath/wil6210/wil6210.h | 1 +
3 files changed, 19 insertions(+)
diff --git a/drivers/net/wireless/ath/wil6210/interrupt.c b/drivers/net/wireless/ath/wil6210/interrupt.c
index 10919f9..52c40e1 100644
--- a/drivers/net/wireless/ath/wil6210/interrupt.c
+++ b/drivers/net/wireless/ath/wil6210/interrupt.c
@@ -493,6 +493,23 @@ free0:
return rc;
}
+/* can't use wil_ioread32_and_clear because ICC value is not ser yet */
+static inline void wil_clear32(void __iomem *addr)
+{
+ u32 x = ioread32(addr);
+
+ iowrite32(x, addr);
+}
+
+void wil6210_clear_irq(struct wil6210_priv *wil)
+{
+ wil_clear32(wil->csr + HOSTADDR(RGF_DMA_EP_RX_ICR) +
+ offsetof(struct RGF_ICR, ICR));
+ wil_clear32(wil->csr + HOSTADDR(RGF_DMA_EP_TX_ICR) +
+ offsetof(struct RGF_ICR, ICR));
+ wil_clear32(wil->csr + HOSTADDR(RGF_DMA_EP_MISC_ICR) +
+ offsetof(struct RGF_ICR, ICR));
+}
int wil6210_init_irq(struct wil6210_priv *wil, int irq)
{
diff --git a/drivers/net/wireless/ath/wil6210/pcie_bus.c b/drivers/net/wireless/ath/wil6210/pcie_bus.c
index d96e811..c609761 100644
--- a/drivers/net/wireless/ath/wil6210/pcie_bus.c
+++ b/drivers/net/wireless/ath/wil6210/pcie_bus.c
@@ -153,6 +153,7 @@ static int wil_pcie_probe(struct pci_dev *pdev, const struct pci_device_id *id)
pci_set_drvdata(pdev, wil);
wil->pdev = pdev;
+ wil6210_clear_irq(wil);
/* FW should raise IRQ when ready */
rc = wil_if_pcie_enable(wil);
if (rc) {
diff --git a/drivers/net/wireless/ath/wil6210/wil6210.h b/drivers/net/wireless/ath/wil6210/wil6210.h
index 14f861c..317621c 100644
--- a/drivers/net/wireless/ath/wil6210/wil6210.h
+++ b/drivers/net/wireless/ath/wil6210/wil6210.h
@@ -476,6 +476,7 @@ int wmi_rxon(struct wil6210_priv *wil, bool on);
int wmi_get_temperature(struct wil6210_priv *wil, u32 *t_m, u32 *t_r);
int wmi_disconnect_sta(struct wil6210_priv *wil, const u8 *mac, u16 reason);
+void wil6210_clear_irq(struct wil6210_priv *wil);
int wil6210_init_irq(struct wil6210_priv *wil, int irq);
void wil6210_fini_irq(struct wil6210_priv *wil, int irq);
void wil6210_disable_irq(struct wil6210_priv *wil);
--
1.8.3.2
Convert 2 often printed messages to dynamic ones
Signed-off-by: Vladimir Kondratiev <[email protected]>
---
drivers/net/wireless/ath/wil6210/cfg80211.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/net/wireless/ath/wil6210/cfg80211.c b/drivers/net/wireless/ath/wil6210/cfg80211.c
index b1bc007..848ed85 100644
--- a/drivers/net/wireless/ath/wil6210/cfg80211.c
+++ b/drivers/net/wireless/ath/wil6210/cfg80211.c
@@ -179,7 +179,7 @@ static int wil_cfg80211_get_station(struct wiphy *wiphy,
int cid = wil_find_cid(wil, mac);
- wil_info(wil, "%s(%pM) CID %d\n", __func__, mac, cid);
+ wil_dbg_misc(wil, "%s(%pM) CID %d\n", __func__, mac, cid);
if (cid < 0)
return cid;
@@ -218,7 +218,7 @@ static int wil_cfg80211_dump_station(struct wiphy *wiphy,
return -ENOENT;
memcpy(mac, wil->sta[cid].addr, ETH_ALEN);
- wil_info(wil, "%s(%pM) CID %d\n", __func__, mac, cid);
+ wil_dbg_misc(wil, "%s(%pM) CID %d\n", __func__, mac, cid);
rc = wil_cid_fill_sinfo(wil, cid, sinfo);
--
1.8.3.2
GRO is easy to enable when already using NAPI framework,
and it improves CPU utilisation. Enable it by default.
Signed-off-by: Vladimir Kondratiev <[email protected]>
---
drivers/net/wireless/ath/wil6210/netdev.c | 2 +-
drivers/net/wireless/ath/wil6210/txrx.c | 14 +++++++-------
2 files changed, 8 insertions(+), 8 deletions(-)
diff --git a/drivers/net/wireless/ath/wil6210/netdev.c b/drivers/net/wireless/ath/wil6210/netdev.c
index 5991802..fdcaeb8 100644
--- a/drivers/net/wireless/ath/wil6210/netdev.c
+++ b/drivers/net/wireless/ath/wil6210/netdev.c
@@ -128,7 +128,7 @@ void *wil_if_alloc(struct device *dev, void __iomem *csr)
ndev->netdev_ops = &wil_netdev_ops;
ndev->ieee80211_ptr = wdev;
ndev->hw_features = NETIF_F_HW_CSUM | NETIF_F_RXCSUM |
- NETIF_F_SG;
+ NETIF_F_SG | NETIF_F_GRO;
ndev->features |= ndev->hw_features;
SET_NETDEV_DEV(ndev, wiphy_dev(wdev->wiphy));
wdev->netdev = ndev;
diff --git a/drivers/net/wireless/ath/wil6210/txrx.c b/drivers/net/wireless/ath/wil6210/txrx.c
index 97d036ad..cfd36cc 100644
--- a/drivers/net/wireless/ath/wil6210/txrx.c
+++ b/drivers/net/wireless/ath/wil6210/txrx.c
@@ -488,7 +488,7 @@ static int wil_rx_refill(struct wil6210_priv *wil, int count)
*/
void wil_netif_rx_any(struct sk_buff *skb, struct net_device *ndev)
{
- int rc;
+ gro_result_t rc;
struct wil6210_priv *wil = ndev_to_wil(ndev);
unsigned int len = skb->len;
struct vring_rx_desc *d = wil_skb_rxdesc(skb);
@@ -497,17 +497,17 @@ void wil_netif_rx_any(struct sk_buff *skb, struct net_device *ndev)
skb_orphan(skb);
- rc = netif_receive_skb(skb);
+ rc = napi_gro_receive(&wil->napi_rx, skb);
- if (likely(rc == NET_RX_SUCCESS)) {
+ if (unlikely(rc == GRO_DROP)) {
+ ndev->stats.rx_dropped++;
+ stats->rx_dropped++;
+ wil_dbg_txrx(wil, "Rx drop %d bytes\n", len);
+ } else {
ndev->stats.rx_packets++;
stats->rx_packets++;
ndev->stats.rx_bytes += len;
stats->rx_bytes += len;
-
- } else {
- ndev->stats.rx_dropped++;
- stats->rx_dropped++;
}
}
--
1.8.3.2
when STA receiving connect() when already connected,
it should return error
Signed-off-by: Vladimir Kondratiev <[email protected]>
---
drivers/net/wireless/ath/wil6210/cfg80211.c | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/drivers/net/wireless/ath/wil6210/cfg80211.c b/drivers/net/wireless/ath/wil6210/cfg80211.c
index f6a12e7..597540a 100644
--- a/drivers/net/wireless/ath/wil6210/cfg80211.c
+++ b/drivers/net/wireless/ath/wil6210/cfg80211.c
@@ -327,6 +327,10 @@ static int wil_cfg80211_connect(struct wiphy *wiphy,
int ch;
int rc = 0;
+ if (test_bit(wil_status_fwconnecting, &wil->status) ||
+ test_bit(wil_status_fwconnected, &wil->status))
+ return -EALREADY;
+
bss = cfg80211_get_bss(wiphy, sme->channel, sme->bssid,
sme->ssid, sme->ssid_len,
WLAN_CAPABILITY_ESS, WLAN_CAPABILITY_ESS);
--
1.8.3.2
When connection established, as reported by WMI_CONNECT_EVENTID,
4-way handshaking required for the secure connection is not done
yet. It is indicated by another WMI event. Wait for it and only then
allow data traffic. In case of non-secure connection, FW reports
"data port open" immediately after connection.
Signed-off-by: Vladimir Kondratiev <[email protected]>
---
drivers/net/wireless/ath/wil6210/debugfs.c | 3 ++-
drivers/net/wireless/ath/wil6210/main.c | 1 +
drivers/net/wireless/ath/wil6210/txrx.c | 21 ++++++++++++++++++---
drivers/net/wireless/ath/wil6210/wil6210.h | 1 +
drivers/net/wireless/ath/wil6210/wmi.c | 18 ++++++++++++++++--
5 files changed, 38 insertions(+), 6 deletions(-)
diff --git a/drivers/net/wireless/ath/wil6210/debugfs.c b/drivers/net/wireless/ath/wil6210/debugfs.c
index 1d09a4b..ea868be 100644
--- a/drivers/net/wireless/ath/wil6210/debugfs.c
+++ b/drivers/net/wireless/ath/wil6210/debugfs.c
@@ -631,7 +631,8 @@ static int wil_sta_debugfs_show(struct seq_file *s, void *data)
status = "connected";
break;
}
- seq_printf(s, "[%d] %pM %s\n", i, p->addr, status);
+ seq_printf(s, "[%d] %pM %s%s\n", i, p->addr, status,
+ (p->data_port_open ? " data_port_open" : ""));
if (p->status == wil_sta_connected) {
for (tid = 0; tid < WIL_STA_TID_NUM; tid++) {
diff --git a/drivers/net/wireless/ath/wil6210/main.c b/drivers/net/wireless/ath/wil6210/main.c
index 41c362d..f10a0b2 100644
--- a/drivers/net/wireless/ath/wil6210/main.c
+++ b/drivers/net/wireless/ath/wil6210/main.c
@@ -59,6 +59,7 @@ static void wil_disconnect_cid(struct wil6210_priv *wil, int cid)
uint i;
struct wil_sta_info *sta = &wil->sta[cid];
+ sta->data_port_open = false;
if (sta->status != wil_sta_unused) {
wmi_disconnect_sta(wil, sta->addr, WLAN_REASON_DEAUTH_LEAVING);
sta->status = wil_sta_unused;
diff --git a/drivers/net/wireless/ath/wil6210/txrx.c b/drivers/net/wireless/ath/wil6210/txrx.c
index 7afaa5e..29f13e0 100644
--- a/drivers/net/wireless/ath/wil6210/txrx.c
+++ b/drivers/net/wireless/ath/wil6210/txrx.c
@@ -662,6 +662,10 @@ static struct vring *wil_find_tx_vring(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->vring2cid_tid[i][0] == cid) {
@@ -700,12 +704,19 @@ static struct vring *wil_tx_bcast(struct wil6210_priv *wil,
struct vring *v, *v2;
struct sk_buff *skb2;
int i;
+ u8 cid;
- /* find 1-st vring */
+ /* find 1-st vring eligible for data */
for (i = 0; i < WIL6210_MAX_TX_RINGS; i++) {
v = &wil->vring_tx[i];
- if (v->va)
- goto found;
+ if (!v->va)
+ continue;
+
+ cid = wil->vring2cid_tid[i][0];
+ if (!wil->sta[cid].data_port_open)
+ continue;
+
+ goto found;
}
wil_err(wil, "Tx while no vrings active?\n");
@@ -721,6 +732,10 @@ found:
v2 = &wil->vring_tx[i];
if (!v2->va)
continue;
+ cid = wil->vring2cid_tid[i][0];
+ if (!wil->sta[cid].data_port_open)
+ continue;
+
skb2 = skb_copy(skb, GFP_ATOMIC);
if (skb2) {
wil_dbg_txrx(wil, "BCAST DUP -> ring %d\n", i);
diff --git a/drivers/net/wireless/ath/wil6210/wil6210.h b/drivers/net/wireless/ath/wil6210/wil6210.h
index 273d00f..f06f717 100644
--- a/drivers/net/wireless/ath/wil6210/wil6210.h
+++ b/drivers/net/wireless/ath/wil6210/wil6210.h
@@ -321,6 +321,7 @@ 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];
unsigned long tid_rx_timer_expired[BITS_TO_LONGS(WIL_STA_TID_NUM)];
diff --git a/drivers/net/wireless/ath/wil6210/wmi.c b/drivers/net/wireless/ath/wil6210/wmi.c
index 24eed09..58c3afc 100644
--- a/drivers/net/wireless/ath/wil6210/wmi.c
+++ b/drivers/net/wireless/ath/wil6210/wmi.c
@@ -550,9 +550,16 @@ static void wmi_evt_linkup(struct wil6210_priv *wil, int id, void *d, int len)
{
struct net_device *ndev = wil_to_ndev(wil);
struct wmi_data_port_open_event *evt = d;
+ u8 cid = evt->cid;
- wil_dbg_wmi(wil, "Link UP for CID %d\n", 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);
+ return;
+ }
+
+ wil->sta[cid].data_port_open = true;
netif_carrier_on(ndev);
}
@@ -560,10 +567,17 @@ 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",
- evt->cid, le32_to_cpu(evt->reason));
+ 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);
}
--
1.8.3.2
upon fw error interrupt - in STA mode, disconnect/cancel scan and
then reset FW/HW
added module param - no_fw_recovery which is false by default
Signed-off-by: Vladimir Kondratiev <[email protected]>
---
drivers/net/wireless/ath/wil6210/cfg80211.c | 8 ++++-
drivers/net/wireless/ath/wil6210/interrupt.c | 1 +
drivers/net/wireless/ath/wil6210/main.c | 48 ++++++++++++++++++++++++++++
drivers/net/wireless/ath/wil6210/wil6210.h | 2 ++
4 files changed, 58 insertions(+), 1 deletion(-)
diff --git a/drivers/net/wireless/ath/wil6210/cfg80211.c b/drivers/net/wireless/ath/wil6210/cfg80211.c
index 848ed85..f6a12e7 100644
--- a/drivers/net/wireless/ath/wil6210/cfg80211.c
+++ b/drivers/net/wireless/ath/wil6210/cfg80211.c
@@ -265,6 +265,7 @@ static int wil_cfg80211_scan(struct wiphy *wiphy,
u16 chnl[4];
} __packed cmd;
uint i, n;
+ int rc;
if (wil->scan_request) {
wil_err(wil, "Already scanning\n");
@@ -305,8 +306,13 @@ static int wil_cfg80211_scan(struct wiphy *wiphy,
request->channels[i]->center_freq);
}
- return wmi_send(wil, WMI_START_SCAN_CMDID, &cmd, sizeof(cmd.cmd) +
+ rc = wmi_send(wil, WMI_START_SCAN_CMDID, &cmd, sizeof(cmd.cmd) +
cmd.cmd.num_channels * sizeof(cmd.cmd.channel_list[0]));
+
+ if (rc)
+ wil->scan_request = NULL;
+
+ return rc;
}
static int wil_cfg80211_connect(struct wiphy *wiphy,
diff --git a/drivers/net/wireless/ath/wil6210/interrupt.c b/drivers/net/wireless/ath/wil6210/interrupt.c
index 201cf06..5824cd4 100644
--- a/drivers/net/wireless/ath/wil6210/interrupt.c
+++ b/drivers/net/wireless/ath/wil6210/interrupt.c
@@ -328,6 +328,7 @@ static irqreturn_t wil6210_irq_misc_thread(int irq, void *cookie)
if (isr & ISR_MISC_FW_ERROR) {
wil_notify_fw_error(wil);
isr &= ~ISR_MISC_FW_ERROR;
+ wil_fw_error_recovery(wil);
}
if (isr & ISR_MISC_MBOX_EVT) {
diff --git a/drivers/net/wireless/ath/wil6210/main.c b/drivers/net/wireless/ath/wil6210/main.c
index 0831d4c..32ac1b9 100644
--- a/drivers/net/wireless/ath/wil6210/main.c
+++ b/drivers/net/wireless/ath/wil6210/main.c
@@ -21,6 +21,10 @@
#include "wil6210.h"
#include "txrx.h"
+static bool no_fw_recovery;
+module_param(no_fw_recovery, bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(no_fw_recovery, " disable FW error recovery");
+
/*
* Due to a hardware issue,
* one has to read/write to/from NIC in 32-bit chunks;
@@ -144,6 +148,36 @@ static void wil_connect_timer_fn(ulong x)
schedule_work(&wil->disconnect_worker);
}
+static void wil_fw_error_worker(struct work_struct *work)
+{
+ struct wil6210_priv *wil = container_of(work,
+ struct wil6210_priv, fw_error_worker);
+ struct wireless_dev *wdev = wil->wdev;
+
+ wil_dbg_misc(wil, "fw error worker\n");
+
+ if (no_fw_recovery)
+ return;
+
+ switch (wdev->iftype) {
+ case NL80211_IFTYPE_STATION:
+ case NL80211_IFTYPE_P2P_CLIENT:
+ case NL80211_IFTYPE_MONITOR:
+ wil_info(wil, "fw error recovery started...\n");
+ wil_reset(wil);
+
+ /* need to re-allocate Rx ring after reset */
+ wil_rx_init(wil);
+ break;
+ case NL80211_IFTYPE_AP:
+ case NL80211_IFTYPE_P2P_GO:
+ /* recovery in these modes is done by upper layers */
+ break;
+ default:
+ break;
+ }
+}
+
static int wil_find_free_vring(struct wil6210_priv *wil)
{
int i;
@@ -196,6 +230,7 @@ int wil_priv_init(struct wil6210_priv *wil)
INIT_WORK(&wil->connect_worker, wil_connect_worker);
INIT_WORK(&wil->disconnect_worker, wil_disconnect_worker);
INIT_WORK(&wil->wmi_event_worker, wmi_event_worker);
+ INIT_WORK(&wil->fw_error_worker, wil_fw_error_worker);
INIT_LIST_HEAD(&wil->pending_wmi_ev);
spin_lock_init(&wil->wmi_ev_lock);
@@ -222,6 +257,7 @@ void wil6210_disconnect(struct wil6210_priv *wil, void *bssid)
void wil_priv_deinit(struct wil6210_priv *wil)
{
cancel_work_sync(&wil->disconnect_worker);
+ cancel_work_sync(&wil->fw_error_worker);
wil6210_disconnect(wil, NULL);
wmi_event_flush(wil);
destroy_workqueue(wil->wmi_wq_conn);
@@ -335,6 +371,13 @@ int wil_reset(struct wil6210_priv *wil)
napi_synchronize(&wil->napi_tx);
}
+ if (wil->scan_request) {
+ wil_dbg_misc(wil, "Abort scan_request 0x%p\n",
+ wil->scan_request);
+ cfg80211_scan_done(wil->scan_request, true);
+ wil->scan_request = NULL;
+ }
+
cancel_work_sync(&wil->disconnect_worker);
wil6210_disconnect(wil, NULL);
@@ -363,6 +406,11 @@ int wil_reset(struct wil6210_priv *wil)
return rc;
}
+void wil_fw_error_recovery(struct wil6210_priv *wil)
+{
+ wil_dbg_misc(wil, "starting fw error recovery\n");
+ schedule_work(&wil->fw_error_worker);
+}
void wil_link_on(struct wil6210_priv *wil)
{
diff --git a/drivers/net/wireless/ath/wil6210/wil6210.h b/drivers/net/wireless/ath/wil6210/wil6210.h
index 89cbdc3..80573f7 100644
--- a/drivers/net/wireless/ath/wil6210/wil6210.h
+++ b/drivers/net/wireless/ath/wil6210/wil6210.h
@@ -370,6 +370,7 @@ struct wil6210_priv {
struct workqueue_struct *wmi_wq_conn; /* for connect worker */
struct work_struct connect_worker;
struct work_struct disconnect_worker;
+ struct work_struct fw_error_worker; /* for FW error recovery */
struct timer_list connect_timer;
int pending_connect_cid;
struct list_head pending_wmi_ev;
@@ -447,6 +448,7 @@ void wil_if_remove(struct wil6210_priv *wil);
int wil_priv_init(struct wil6210_priv *wil);
void wil_priv_deinit(struct wil6210_priv *wil);
int wil_reset(struct wil6210_priv *wil);
+void wil_fw_error_recovery(struct wil6210_priv *wil);
void wil_link_on(struct wil6210_priv *wil);
void wil_link_off(struct wil6210_priv *wil);
int wil_up(struct wil6210_priv *wil);
--
1.8.3.2
New firmware do support scan while connected. Enable it.
Signed-off-by: Vladimir Kondratiev <[email protected]>
---
drivers/net/wireless/ath/wil6210/cfg80211.c | 8 ++------
drivers/net/wireless/ath/wil6210/main.c | 2 --
2 files changed, 2 insertions(+), 8 deletions(-)
diff --git a/drivers/net/wireless/ath/wil6210/cfg80211.c b/drivers/net/wireless/ath/wil6210/cfg80211.c
index 7439303..b1bc007 100644
--- a/drivers/net/wireless/ath/wil6210/cfg80211.c
+++ b/drivers/net/wireless/ath/wil6210/cfg80211.c
@@ -282,7 +282,7 @@ static int wil_cfg80211_scan(struct wiphy *wiphy,
/* FW don't support scan after connection attempt */
if (test_bit(wil_status_dontscan, &wil->status)) {
- wil_err(wil, "Scan after connect attempt not supported\n");
+ wil_err(wil, "Can't scan now\n");
return -EBUSY;
}
@@ -402,10 +402,7 @@ static int wil_cfg80211_connect(struct wiphy *wiphy,
memcpy(conn.bssid, bss->bssid, ETH_ALEN);
memcpy(conn.dst_mac, bss->bssid, ETH_ALEN);
- /*
- * FW don't support scan after connection attempt
- */
- set_bit(wil_status_dontscan, &wil->status);
+
set_bit(wil_status_fwconnecting, &wil->status);
rc = wmi_send(wil, WMI_CONNECT_CMDID, &conn, sizeof(conn));
@@ -414,7 +411,6 @@ static int wil_cfg80211_connect(struct wiphy *wiphy,
mod_timer(&wil->connect_timer,
jiffies + msecs_to_jiffies(2000));
} else {
- clear_bit(wil_status_dontscan, &wil->status);
clear_bit(wil_status_fwconnecting, &wil->status);
}
diff --git a/drivers/net/wireless/ath/wil6210/main.c b/drivers/net/wireless/ath/wil6210/main.c
index f10a0b2..4cc1b78 100644
--- a/drivers/net/wireless/ath/wil6210/main.c
+++ b/drivers/net/wireless/ath/wil6210/main.c
@@ -113,8 +113,6 @@ static void _wil6210_disconnect(struct wil6210_priv *wil, void *bssid)
GFP_KERNEL);
}
clear_bit(wil_status_fwconnecting, &wil->status);
- wil_dbg_misc(wil, "clear_bit(wil_status_dontscan)\n");
- clear_bit(wil_status_dontscan, &wil->status);
break;
default:
/* AP-like interface and monitor:
--
1.8.3.2
When FW crashes, dmesg get polluted with the "FW not ready"
error message. Print it only once per FW lifecycle
Signed-off-by: Vladimir Kondratiev <[email protected]>
---
drivers/net/wireless/ath/wil6210/txrx.c | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/drivers/net/wireless/ath/wil6210/txrx.c b/drivers/net/wireless/ath/wil6210/txrx.c
index 41f88ee..5cda0ea9 100644
--- a/drivers/net/wireless/ath/wil6210/txrx.c
+++ b/drivers/net/wireless/ath/wil6210/txrx.c
@@ -956,11 +956,15 @@ netdev_tx_t wil_start_xmit(struct sk_buff *skb, struct net_device *ndev)
struct wil6210_priv *wil = ndev_to_wil(ndev);
struct ethhdr *eth = (void *)skb->data;
struct vring *vring;
+ static bool pr_once_fw;
int rc;
wil_dbg_txrx(wil, "%s()\n", __func__);
if (!test_bit(wil_status_fwready, &wil->status)) {
- wil_err(wil, "FW not ready\n");
+ if (!pr_once_fw) {
+ wil_err(wil, "FW not ready\n");
+ pr_once_fw = true;
+ }
goto drop;
}
if (!test_bit(wil_status_fwconnected, &wil->status)) {
@@ -971,6 +975,7 @@ netdev_tx_t wil_start_xmit(struct sk_buff *skb, struct net_device *ndev)
wil_err(wil, "Xmit in monitor mode not supported\n");
goto drop;
}
+ pr_once_fw = false;
/* find vring */
if (is_unicast_ether_addr(eth->h_dest)) {
--
1.8.3.2
Configure hardware to perform full reset on "power good". This mean,
reset HW on system boot. This improves card stability.
By default this is off.
Signed-off-by: Vladimir Kondratiev <[email protected]>
---
drivers/net/wireless/ath/wil6210/main.c | 8 ++++++--
drivers/net/wireless/ath/wil6210/wil6210.h | 2 ++
2 files changed, 8 insertions(+), 2 deletions(-)
diff --git a/drivers/net/wireless/ath/wil6210/main.c b/drivers/net/wireless/ath/wil6210/main.c
index bd27bee..6847622 100644
--- a/drivers/net/wireless/ath/wil6210/main.c
+++ b/drivers/net/wireless/ath/wil6210/main.c
@@ -241,8 +241,9 @@ static void wil_target_reset(struct wil6210_priv *wil)
/* register write */
#define W(a, v) iowrite32(v, wil->csr + HOSTADDR(a))
/* register set = read, OR, write */
-#define S(a, v) iowrite32(ioread32(wil->csr + HOSTADDR(a)) | v, \
- wil->csr + HOSTADDR(a))
+#define S(a, v) W(a, R(a) | v)
+ /* register clear = read, AND with inverted, write */
+#define C(a, v) W(a, R(a) & ~v)
wil->hw_version = R(RGF_USER_FW_REV_ID);
rev_id = wil->hw_version & 0xff;
@@ -286,11 +287,14 @@ static void wil_target_reset(struct wil6210_priv *wil)
if (rev_id == 2)
W(RGF_PCIE_LOS_COUNTER_CTL, BIT(8));
+ C(RGF_USER_CLKS_CTL_0, BIT_USER_CLKS_RST_PWGD);
+
wil_dbg_misc(wil, "Reset completed in %d ms\n", delay);
#undef R
#undef W
#undef S
+#undef C
}
void wil_mbox_ring_le2cpus(struct wil6210_mbox_ring *r)
diff --git a/drivers/net/wireless/ath/wil6210/wil6210.h b/drivers/net/wireless/ath/wil6210/wil6210.h
index b376399..f7d8f0e 100644
--- a/drivers/net/wireless/ath/wil6210/wil6210.h
+++ b/drivers/net/wireless/ath/wil6210/wil6210.h
@@ -79,6 +79,8 @@ struct RGF_ICR {
#define RGF_USER_MAC_CPU_0 (0x8801fc)
#define RGF_USER_USER_SCRATCH_PAD (0x8802bc)
#define RGF_USER_FW_REV_ID (0x880a8c) /* chip revision */
+#define RGF_USER_CLKS_CTL_0 (0x880abc)
+ #define BIT_USER_CLKS_RST_PWGD BIT(11) /* reset on "power good" */
#define RGF_USER_CLKS_CTL_SW_RST_VEC_0 (0x880b04)
#define RGF_USER_CLKS_CTL_SW_RST_VEC_1 (0x880b08)
#define RGF_USER_CLKS_CTL_SW_RST_VEC_2 (0x880b0c)
--
1.8.3.2
Support for new chip revision. Revision read from the
internal register, PCIE config's "revision id" register
do not indicate HW version properly
Signed-off-by: Vladimir Kondratiev <[email protected]>
---
drivers/net/wireless/ath/wil6210/main.c | 29 ++++++++++++++++++++++++++++-
drivers/net/wireless/ath/wil6210/pcie_bus.c | 2 ++
drivers/net/wireless/ath/wil6210/wil6210.h | 4 ++++
3 files changed, 34 insertions(+), 1 deletion(-)
diff --git a/drivers/net/wireless/ath/wil6210/main.c b/drivers/net/wireless/ath/wil6210/main.c
index 4cc1b78..8644418 100644
--- a/drivers/net/wireless/ath/wil6210/main.c
+++ b/drivers/net/wireless/ath/wil6210/main.c
@@ -230,14 +230,22 @@ void wil_priv_deinit(struct wil6210_priv *wil)
static void wil_target_reset(struct wil6210_priv *wil)
{
+ int delay = 100;
+ u32 baud_rate;
+ u32 rev_id;
+
wil_dbg_misc(wil, "Resetting...\n");
+ /* register read */
+#define R(a) ioread32(wil->csr + HOSTADDR(a))
/* register write */
#define W(a, v) iowrite32(v, wil->csr + HOSTADDR(a))
/* register set = read, OR, write */
#define S(a, v) iowrite32(ioread32(wil->csr + HOSTADDR(a)) | v, \
wil->csr + HOSTADDR(a))
+ wil->hw_version = R(RGF_FW_REV_ID);
+ rev_id = wil->hw_version & 0xff;
/* hpal_perst_from_pad_src_n_mask */
S(RGF_USER_CLKS_CTL_SW_RST_MASK_0, BIT(6));
/* car_perst_rst_src_n_mask */
@@ -257,11 +265,30 @@ static void wil_target_reset(struct wil6210_priv *wil)
W(RGF_USER_CLKS_CTL_SW_RST_VEC_0, 0);
W(RGF_USER_CLKS_CTL_SW_RST_VEC_3, 0x00000001);
- W(RGF_USER_CLKS_CTL_SW_RST_VEC_2, 0x00000080);
+ if (rev_id == 1) {
+ W(RGF_USER_CLKS_CTL_SW_RST_VEC_2, 0x00000080);
+ } else {
+ W(RGF_LOS_COUNTER_CTL, BIT(6) | BIT(8));
+ W(RGF_USER_CLKS_CTL_SW_RST_VEC_2, 0x00008000);
+ }
W(RGF_USER_CLKS_CTL_SW_RST_VEC_0, 0);
+ /* wait until device ready. Use baud rate */
+ do {
+ msleep(1);
+ baud_rate = R(RGF_USER_SERIAL_BAUD_RATE);
+ if (delay-- < 0) {
+ wil_err(wil, "Reset not completed\n");
+ return;
+ }
+ } while (baud_rate != 0x15e);
+
+ if (rev_id == 2)
+ W(RGF_LOS_COUNTER_CTL, BIT(8));
+
wil_dbg_misc(wil, "Reset completed\n");
+#undef R
#undef W
#undef S
}
diff --git a/drivers/net/wireless/ath/wil6210/pcie_bus.c b/drivers/net/wireless/ath/wil6210/pcie_bus.c
index eeceab3..d96e811 100644
--- a/drivers/net/wireless/ath/wil6210/pcie_bus.c
+++ b/drivers/net/wireless/ath/wil6210/pcie_bus.c
@@ -74,6 +74,8 @@ static int wil_if_pcie_enable(struct wil6210_priv *wil)
if (rc)
goto release_irq;
+ wil_info(wil, "HW version: 0x%08x\n", wil->hw_version);
+
return 0;
release_irq:
diff --git a/drivers/net/wireless/ath/wil6210/wil6210.h b/drivers/net/wireless/ath/wil6210/wil6210.h
index 11e0898..14f861c 100644
--- a/drivers/net/wireless/ath/wil6210/wil6210.h
+++ b/drivers/net/wireless/ath/wil6210/wil6210.h
@@ -74,6 +74,9 @@ struct RGF_ICR {
} __packed;
/* registers - FW addresses */
+#define RGF_FW_REV_ID (0x880a8c) /* chip revision */
+#define RGF_USER_SERIAL_BAUD_RATE (0x880050)
+#define RGF_LOS_COUNTER_CTL (0x882dc4)
#define RGF_USER_USER_SCRATCH_PAD (0x8802bc)
#define RGF_USER_USER_ICR (0x880b4c) /* struct RGF_ICR */
#define BIT_USER_USER_ICR_SW_INT_2 BIT(18)
@@ -342,6 +345,7 @@ struct wil6210_priv {
void __iomem *csr;
ulong status;
u32 fw_version;
+ u32 hw_version;
u8 n_mids; /* number of additional MIDs as reported by FW */
/* profile */
u32 monitor_flags;
--
1.8.3.2
wil_reset() removes vring's
At the same time NAPI may be active performing Rx/Tx completion.
If this happens, Rx/Tx polling functions going to access already removed vrings
Make sure NAPI is idle and won't be started prior to vring removal.
For this, track NAPI enabled state
Signed-off-by: Vladimir Kondratiev <[email protected]>
---
drivers/net/wireless/ath/wil6210/interrupt.c | 15 ++++++++++++---
drivers/net/wireless/ath/wil6210/main.c | 9 ++++++++-
drivers/net/wireless/ath/wil6210/wil6210.h | 1 +
3 files changed, 21 insertions(+), 4 deletions(-)
diff --git a/drivers/net/wireless/ath/wil6210/interrupt.c b/drivers/net/wireless/ath/wil6210/interrupt.c
index 52c40e1..201cf06 100644
--- a/drivers/net/wireless/ath/wil6210/interrupt.c
+++ b/drivers/net/wireless/ath/wil6210/interrupt.c
@@ -195,8 +195,12 @@ static irqreturn_t wil6210_irq_rx(int irq, void *cookie)
if (isr & BIT_DMA_EP_RX_ICR_RX_DONE) {
wil_dbg_irq(wil, "RX done\n");
isr &= ~BIT_DMA_EP_RX_ICR_RX_DONE;
- wil_dbg_txrx(wil, "NAPI schedule\n");
- napi_schedule(&wil->napi_rx);
+ if (test_bit(wil_status_reset_done, &wil->status)) {
+ wil_dbg_txrx(wil, "NAPI(Rx) schedule\n");
+ napi_schedule(&wil->napi_rx);
+ } else {
+ wil_err(wil, "Got Rx interrupt while in reset\n");
+ }
}
if (isr)
@@ -226,10 +230,15 @@ static irqreturn_t wil6210_irq_tx(int irq, void *cookie)
if (isr & BIT_DMA_EP_TX_ICR_TX_DONE) {
wil_dbg_irq(wil, "TX done\n");
- napi_schedule(&wil->napi_tx);
isr &= ~BIT_DMA_EP_TX_ICR_TX_DONE;
/* clear also all VRING interrupts */
isr &= ~(BIT(25) - 1UL);
+ if (test_bit(wil_status_reset_done, &wil->status)) {
+ wil_dbg_txrx(wil, "NAPI(Tx) schedule\n");
+ napi_schedule(&wil->napi_tx);
+ } else {
+ wil_err(wil, "Got Tx interrupt while in reset\n");
+ }
}
if (isr)
diff --git a/drivers/net/wireless/ath/wil6210/main.c b/drivers/net/wireless/ath/wil6210/main.c
index de952ab..0831d4c 100644
--- a/drivers/net/wireless/ath/wil6210/main.c
+++ b/drivers/net/wireless/ath/wil6210/main.c
@@ -329,11 +329,16 @@ int wil_reset(struct wil6210_priv *wil)
{
int rc;
+ wil->status = 0; /* prevent NAPI from being scheduled */
+ if (test_bit(wil_status_napi_en, &wil->status)) {
+ napi_synchronize(&wil->napi_rx);
+ napi_synchronize(&wil->napi_tx);
+ }
+
cancel_work_sync(&wil->disconnect_worker);
wil6210_disconnect(wil, NULL);
wil6210_disable_irq(wil);
- wil->status = 0;
wmi_event_flush(wil);
@@ -426,6 +431,7 @@ static int __wil_up(struct wil6210_priv *wil)
napi_enable(&wil->napi_rx);
napi_enable(&wil->napi_tx);
+ set_bit(wil_status_napi_en, &wil->status);
return 0;
}
@@ -443,6 +449,7 @@ int wil_up(struct wil6210_priv *wil)
static int __wil_down(struct wil6210_priv *wil)
{
+ clear_bit(wil_status_napi_en, &wil->status);
napi_disable(&wil->napi_rx);
napi_disable(&wil->napi_tx);
diff --git a/drivers/net/wireless/ath/wil6210/wil6210.h b/drivers/net/wireless/ath/wil6210/wil6210.h
index f7d8f0e..89cbdc3 100644
--- a/drivers/net/wireless/ath/wil6210/wil6210.h
+++ b/drivers/net/wireless/ath/wil6210/wil6210.h
@@ -249,6 +249,7 @@ enum { /* for wil6210_priv.status */
wil_status_dontscan,
wil_status_reset_done,
wil_status_irqen, /* FIXME: interrupts enabled - for debug */
+ wil_status_napi_en, /* NAPI enabled protected by wil->mutex */
};
struct pci_dev;
--
1.8.3.2
When setting fragmented skb for Tx, assign skb to the last descriptor
and set number of fragments in the 1-st one
On Tx complete, HW sets "DU" bit in Tx descriptor only for the last
descriptor; so search for it using number of fragments field.
Middle descriptors may have "DU" bit not set by the hardware.
Signed-off-by: Vladimir Kondratiev <[email protected]>
---
drivers/net/wireless/ath/wil6210/debugfs.c | 61 ++++++++++-----
drivers/net/wireless/ath/wil6210/netdev.c | 5 +-
drivers/net/wireless/ath/wil6210/txrx.c | 115 ++++++++++++++++++-----------
drivers/net/wireless/ath/wil6210/wil6210.h | 1 +
4 files changed, 115 insertions(+), 67 deletions(-)
diff --git a/drivers/net/wireless/ath/wil6210/debugfs.c b/drivers/net/wireless/ath/wil6210/debugfs.c
index ea868be..ecdabe4 100644
--- a/drivers/net/wireless/ath/wil6210/debugfs.c
+++ b/drivers/net/wireless/ath/wil6210/debugfs.c
@@ -398,6 +398,44 @@ static const struct file_operations fops_reset = {
.open = simple_open,
};
+static void wil_seq_hexdump(struct seq_file *s, void *p, int len,
+ const char *prefix)
+{
+ char printbuf[16 * 3 + 2];
+ int i = 0;
+ while (i < len) {
+ int l = min(len - i, 16);
+ hex_dump_to_buffer(p + i, l, 16, 1, printbuf,
+ sizeof(printbuf), false);
+ seq_printf(s, "%s%s\n", prefix, printbuf);
+ i += l;
+ }
+}
+
+static void wil_seq_print_skb(struct seq_file *s, struct sk_buff *skb)
+{
+ int i = 0;
+ int len = skb_headlen(skb);
+ void *p = skb->data;
+ int nr_frags = skb_shinfo(skb)->nr_frags;
+
+ seq_printf(s, " len = %d\n", len);
+ wil_seq_hexdump(s, p, len, " : ");
+
+ if (nr_frags) {
+ seq_printf(s, " nr_frags = %d\n", nr_frags);
+ for (i = 0; i < nr_frags; i++) {
+ const struct skb_frag_struct *frag =
+ &skb_shinfo(skb)->frags[i];
+
+ len = skb_frag_size(frag);
+ p = skb_frag_address_safe(frag);
+ seq_printf(s, " [%2d] : len = %d\n", i, len);
+ wil_seq_hexdump(s, p, len, " : ");
+ }
+ }
+}
+
/*---------Tx/Rx descriptor------------*/
static int wil_txdesc_debugfs_show(struct seq_file *s, void *data)
{
@@ -438,26 +476,9 @@ static int wil_txdesc_debugfs_show(struct seq_file *s, void *data)
seq_printf(s, " SKB = %p\n", skb);
if (skb) {
- char printbuf[16 * 3 + 2];
- int i = 0;
- int len = le16_to_cpu(d->dma.length);
- void *p = skb->data;
-
- if (len != skb_headlen(skb)) {
- seq_printf(s, "!!! len: desc = %d skb = %d\n",
- len, skb_headlen(skb));
- len = min_t(int, len, skb_headlen(skb));
- }
-
- seq_printf(s, " len = %d\n", len);
-
- while (i < len) {
- int l = min(len - i, 16);
- hex_dump_to_buffer(p + i, l, 16, 1, printbuf,
- sizeof(printbuf), false);
- seq_printf(s, " : %s\n", printbuf);
- i += l;
- }
+ skb_get(skb);
+ wil_seq_print_skb(s, skb);
+ kfree_skb(skb);
}
seq_printf(s, "}\n");
} else {
diff --git a/drivers/net/wireless/ath/wil6210/netdev.c b/drivers/net/wireless/ath/wil6210/netdev.c
index 717178f..5991802 100644
--- a/drivers/net/wireless/ath/wil6210/netdev.c
+++ b/drivers/net/wireless/ath/wil6210/netdev.c
@@ -127,8 +127,9 @@ void *wil_if_alloc(struct device *dev, void __iomem *csr)
ndev->netdev_ops = &wil_netdev_ops;
ndev->ieee80211_ptr = wdev;
- ndev->hw_features = NETIF_F_HW_CSUM | NETIF_F_RXCSUM;
- ndev->features |= NETIF_F_HW_CSUM | NETIF_F_RXCSUM;
+ ndev->hw_features = NETIF_F_HW_CSUM | NETIF_F_RXCSUM |
+ NETIF_F_SG;
+ ndev->features |= ndev->hw_features;
SET_NETDEV_DEV(ndev, wiphy_dev(wdev->wiphy));
wdev->netdev = ndev;
diff --git a/drivers/net/wireless/ath/wil6210/txrx.c b/drivers/net/wireless/ath/wil6210/txrx.c
index 29f13e0..2eb545e 100644
--- a/drivers/net/wireless/ath/wil6210/txrx.c
+++ b/drivers/net/wireless/ath/wil6210/txrx.c
@@ -774,6 +774,13 @@ static int wil_tx_desc_map(struct vring_tx_desc *d, dma_addr_t pa, u32 len,
return 0;
}
+static inline
+void wil_tx_desc_set_nr_frags(struct vring_tx_desc *d, int nr_frags)
+{
+ d->mac.d[2] |= ((nr_frags + 1) <<
+ MAC_CFG_DESC_TX_2_NUM_OF_DESCRIPTORS_POS);
+}
+
static int wil_tx_desc_offload_cksum_set(struct wil6210_priv *wil,
struct vring_tx_desc *d,
struct sk_buff *skb)
@@ -866,8 +873,8 @@ static int wil_tx_vring(struct wil6210_priv *wil, struct vring *vring,
goto dma_error;
}
- d->mac.d[2] |= ((nr_frags + 1) <<
- MAC_CFG_DESC_TX_2_NUM_OF_DESCRIPTORS_POS);
+ vring->ctx[i].nr_frags = nr_frags;
+ wil_tx_desc_set_nr_frags(d, nr_frags);
if (nr_frags)
*_d = *d;
@@ -883,6 +890,11 @@ static int wil_tx_vring(struct wil6210_priv *wil, struct vring *vring,
if (unlikely(dma_mapping_error(dev, pa)))
goto dma_error;
wil_tx_desc_map(d, pa, len, vring_index);
+ /* no need to check return code -
+ * if it succeeded for 1-st descriptor,
+ * it will succeed here too
+ */
+ wil_tx_desc_offload_cksum_set(wil, d, skb);
vring->ctx[i].mapped_as_page = 1;
*_d = *d;
}
@@ -1003,6 +1015,7 @@ int wil_tx_complete(struct wil6210_priv *wil, int ringid)
int done = 0;
int cid = wil->vring2cid_tid[ringid][0];
struct wil_net_stats *stats = &wil->sta[cid].stats;
+ volatile struct vring_tx_desc *_d;
if (!vring->va) {
wil_err(wil, "Tx irq[%d]: vring not initialized\n", ringid);
@@ -1012,57 +1025,69 @@ int wil_tx_complete(struct wil6210_priv *wil, int ringid)
wil_dbg_txrx(wil, "%s(%d)\n", __func__, ringid);
while (!wil_vring_is_empty(vring)) {
- volatile struct vring_tx_desc *_d =
- &vring->va[vring->swtail].tx;
- struct vring_tx_desc dd, *d = ⅆ
- dma_addr_t pa;
- u16 dmalen;
+ int new_swtail;
struct wil_ctx *ctx = &vring->ctx[vring->swtail];
- struct sk_buff *skb = ctx->skb;
-
- *d = *_d;
+ /**
+ * For the fragmented skb, HW will set DU bit only for the
+ * last fragment. look for it
+ */
+ int lf = (vring->swtail + ctx->nr_frags) % vring->size;
+ /* TODO: check we are not past head */
- if (!(d->dma.status & TX_DMA_STATUS_DU))
+ _d = &vring->va[lf].tx;
+ if (!(_d->dma.status & TX_DMA_STATUS_DU))
break;
- dmalen = le16_to_cpu(d->dma.length);
- trace_wil6210_tx_done(ringid, vring->swtail, dmalen,
- d->dma.error);
- wil_dbg_txrx(wil,
- "Tx[%3d] : %d bytes, status 0x%02x err 0x%02x\n",
- vring->swtail, dmalen, d->dma.status,
- d->dma.error);
- wil_hex_dump_txrx("TxC ", DUMP_PREFIX_NONE, 32, 4,
- (const void *)d, sizeof(*d), false);
+ new_swtail = (lf + 1) % vring->size;
+ while (vring->swtail != new_swtail) {
+ struct vring_tx_desc dd, *d = ⅆ
+ dma_addr_t pa;
+ u16 dmalen;
+ struct wil_ctx *ctx = &vring->ctx[vring->swtail];
+ struct sk_buff *skb = ctx->skb;
+ _d = &vring->va[vring->swtail].tx;
- pa = wil_desc_addr(&d->dma.addr);
- if (ctx->mapped_as_page)
- dma_unmap_page(dev, pa, dmalen, DMA_TO_DEVICE);
- else
- dma_unmap_single(dev, pa, dmalen, DMA_TO_DEVICE);
+ *d = *_d;
- if (skb) {
- if (d->dma.error == 0) {
- ndev->stats.tx_packets++;
- stats->tx_packets++;
- ndev->stats.tx_bytes += skb->len;
- stats->tx_bytes += skb->len;
- } else {
- ndev->stats.tx_errors++;
- stats->tx_errors++;
- }
+ dmalen = le16_to_cpu(d->dma.length);
+ trace_wil6210_tx_done(ringid, vring->swtail, dmalen,
+ d->dma.error);
+ wil_dbg_txrx(wil,
+ "Tx[%3d] : %d bytes, status 0x%02x err 0x%02x\n",
+ vring->swtail, dmalen, d->dma.status,
+ d->dma.error);
+ wil_hex_dump_txrx("TxC ", DUMP_PREFIX_NONE, 32, 4,
+ (const void *)d, sizeof(*d), false);
- dev_kfree_skb_any(skb);
+ pa = wil_desc_addr(&d->dma.addr);
+ if (ctx->mapped_as_page)
+ dma_unmap_page(dev, pa, dmalen, DMA_TO_DEVICE);
+ else
+ dma_unmap_single(dev, pa, dmalen,
+ DMA_TO_DEVICE);
+
+ if (skb) {
+ if (d->dma.error == 0) {
+ ndev->stats.tx_packets++;
+ stats->tx_packets++;
+ ndev->stats.tx_bytes += skb->len;
+ stats->tx_bytes += skb->len;
+ } else {
+ ndev->stats.tx_errors++;
+ stats->tx_errors++;
+ }
+
+ dev_kfree_skb_any(skb);
+ }
+ memset(ctx, 0, sizeof(*ctx));
+ /* There is no need to touch HW descriptor:
+ * - ststus bit TX_DMA_STATUS_DU is set by design,
+ * so hardware will not try to process this desc.,
+ * - rest of descriptor will be initialized on Tx.
+ */
+ vring->swtail = wil_vring_next_tail(vring);
+ done++;
}
- memset(ctx, 0, sizeof(*ctx));
- /*
- * There is no need to touch HW descriptor:
- * - ststus bit TX_DMA_STATUS_DU is set by design,
- * so hardware will not try to process this desc.,
- * - rest of descriptor will be initialized on Tx.
- */
- vring->swtail = wil_vring_next_tail(vring);
- done++;
}
if (wil_vring_avail_tx(vring) > vring->size/4)
netif_tx_wake_all_queues(wil_to_ndev(wil));
diff --git a/drivers/net/wireless/ath/wil6210/wil6210.h b/drivers/net/wireless/ath/wil6210/wil6210.h
index f06f717..574f4d9 100644
--- a/drivers/net/wireless/ath/wil6210/wil6210.h
+++ b/drivers/net/wireless/ath/wil6210/wil6210.h
@@ -214,6 +214,7 @@ struct pending_wmi_event {
*/
struct wil_ctx {
struct sk_buff *skb;
+ u8 nr_frags;
u8 mapped_as_page:1;
};
--
1.8.3.2
Introduce enum to describe mapping type; allow 'none' in addition to
'single' and 'page'; this is preparation for GSO
Signed-off-by: Vladimir Kondratiev <[email protected]>
---
drivers/net/wireless/ath/wil6210/txrx.c | 52 +++++++++++++++---------------
drivers/net/wireless/ath/wil6210/wil6210.h | 8 ++++-
2 files changed, 33 insertions(+), 27 deletions(-)
diff --git a/drivers/net/wireless/ath/wil6210/txrx.c b/drivers/net/wireless/ath/wil6210/txrx.c
index 2eb545e..41f88ee 100644
--- a/drivers/net/wireless/ath/wil6210/txrx.c
+++ b/drivers/net/wireless/ath/wil6210/txrx.c
@@ -104,6 +104,23 @@ static int wil_vring_alloc(struct wil6210_priv *wil, struct vring *vring)
return 0;
}
+static void wil_txdesc_unmap(struct device *dev, struct vring_tx_desc *d,
+ struct wil_ctx *ctx)
+{
+ dma_addr_t pa = wil_desc_addr(&d->dma.addr);
+ u16 dmalen = le16_to_cpu(d->dma.length);
+ switch (ctx->mapped_as) {
+ case wil_mapped_as_single:
+ dma_unmap_single(dev, pa, dmalen, DMA_TO_DEVICE);
+ break;
+ case wil_mapped_as_page:
+ dma_unmap_page(dev, pa, dmalen, DMA_TO_DEVICE);
+ break;
+ default:
+ break;
+ }
+}
+
static void wil_vring_free(struct wil6210_priv *wil, struct vring *vring,
int tx)
{
@@ -122,15 +139,7 @@ static void wil_vring_free(struct wil6210_priv *wil, struct vring *vring,
ctx = &vring->ctx[vring->swtail];
*d = *_d;
- pa = wil_desc_addr(&d->dma.addr);
- dmalen = le16_to_cpu(d->dma.length);
- if (vring->ctx[vring->swtail].mapped_as_page) {
- dma_unmap_page(dev, pa, dmalen,
- DMA_TO_DEVICE);
- } else {
- dma_unmap_single(dev, pa, dmalen,
- DMA_TO_DEVICE);
- }
+ wil_txdesc_unmap(dev, d, ctx);
if (ctx->skb)
dev_kfree_skb_any(ctx->skb);
vring->swtail = wil_vring_next_tail(vring);
@@ -845,8 +854,6 @@ static int wil_tx_vring(struct wil6210_priv *wil, struct vring *vring,
wil_dbg_txrx(wil, "%s()\n", __func__);
- if (avail < vring->size/8)
- netif_tx_stop_all_queues(wil_to_ndev(wil));
if (avail < 1 + nr_frags) {
wil_err(wil, "Tx ring full. No space for %d fragments\n",
1 + nr_frags);
@@ -864,6 +871,7 @@ static int wil_tx_vring(struct wil6210_priv *wil, struct vring *vring,
if (unlikely(dma_mapping_error(dev, pa)))
return -EINVAL;
+ vring->ctx[i].mapped_as = wil_mapped_as_single;
/* 1-st segment */
wil_tx_desc_map(d, pa, skb_headlen(skb), vring_index);
/* Process TCP/UDP checksum offloading */
@@ -889,13 +897,13 @@ static int wil_tx_vring(struct wil6210_priv *wil, struct vring *vring,
DMA_TO_DEVICE);
if (unlikely(dma_mapping_error(dev, pa)))
goto dma_error;
+ vring->ctx[i].mapped_as = wil_mapped_as_page;
wil_tx_desc_map(d, pa, len, vring_index);
/* no need to check return code -
* if it succeeded for 1-st descriptor,
* it will succeed here too
*/
wil_tx_desc_offload_cksum_set(wil, d, skb);
- vring->ctx[i].mapped_as_page = 1;
*_d = *d;
}
/* for the last seg only */
@@ -924,7 +932,6 @@ static int wil_tx_vring(struct wil6210_priv *wil, struct vring *vring,
/* unmap what we have mapped */
nr_frags = f + 1; /* frags mapped + one for skb head */
for (f = 0; f < nr_frags; f++) {
- u16 dmalen;
struct wil_ctx *ctx;
i = (swhead + f) % vring->size;
@@ -932,12 +939,7 @@ static int wil_tx_vring(struct wil6210_priv *wil, struct vring *vring,
_d = &(vring->va[i].tx);
*d = *_d;
_d->dma.status = TX_DMA_STATUS_DU;
- pa = wil_desc_addr(&d->dma.addr);
- dmalen = le16_to_cpu(d->dma.length);
- if (ctx->mapped_as_page)
- dma_unmap_page(dev, pa, dmalen, DMA_TO_DEVICE);
- else
- dma_unmap_single(dev, pa, dmalen, DMA_TO_DEVICE);
+ wil_txdesc_unmap(dev, d, ctx);
if (ctx->skb)
dev_kfree_skb_any(ctx->skb);
@@ -983,6 +985,10 @@ netdev_tx_t wil_start_xmit(struct sk_buff *skb, struct net_device *ndev)
/* set up vring entry */
rc = wil_tx_vring(wil, vring, skb);
+ /* do we still have enough room in the vring? */
+ if (wil_vring_avail_tx(vring) < vring->size/8)
+ netif_tx_stop_all_queues(wil_to_ndev(wil));
+
switch (rc) {
case 0:
/* statistics will be updated on the tx_complete */
@@ -1041,7 +1047,6 @@ int wil_tx_complete(struct wil6210_priv *wil, int ringid)
new_swtail = (lf + 1) % vring->size;
while (vring->swtail != new_swtail) {
struct vring_tx_desc dd, *d = ⅆ
- dma_addr_t pa;
u16 dmalen;
struct wil_ctx *ctx = &vring->ctx[vring->swtail];
struct sk_buff *skb = ctx->skb;
@@ -1059,12 +1064,7 @@ int wil_tx_complete(struct wil6210_priv *wil, int ringid)
wil_hex_dump_txrx("TxC ", DUMP_PREFIX_NONE, 32, 4,
(const void *)d, sizeof(*d), false);
- pa = wil_desc_addr(&d->dma.addr);
- if (ctx->mapped_as_page)
- dma_unmap_page(dev, pa, dmalen, DMA_TO_DEVICE);
- else
- dma_unmap_single(dev, pa, dmalen,
- DMA_TO_DEVICE);
+ wil_txdesc_unmap(dev, d, ctx);
if (skb) {
if (d->dma.error == 0) {
diff --git a/drivers/net/wireless/ath/wil6210/wil6210.h b/drivers/net/wireless/ath/wil6210/wil6210.h
index 574f4d9..11e0898 100644
--- a/drivers/net/wireless/ath/wil6210/wil6210.h
+++ b/drivers/net/wireless/ath/wil6210/wil6210.h
@@ -209,13 +209,19 @@ struct pending_wmi_event {
} __packed event;
};
+enum { /* for wil_ctx.mapped_as */
+ wil_mapped_as_none = 0,
+ wil_mapped_as_single = 1,
+ wil_mapped_as_page = 2,
+};
+
/**
* struct wil_ctx - software context for Vring descriptor
*/
struct wil_ctx {
struct sk_buff *skb;
u8 nr_frags;
- u8 mapped_as_page:1;
+ u8 mapped_as;
};
union vring_desc;
--
1.8.3.2
Useful to detect hardware problems
Signed-off-by: Vladimir Kondratiev <[email protected]>
---
drivers/net/wireless/ath/wil6210/main.c | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/drivers/net/wireless/ath/wil6210/main.c b/drivers/net/wireless/ath/wil6210/main.c
index 8644418..1f9f1d2 100644
--- a/drivers/net/wireless/ath/wil6210/main.c
+++ b/drivers/net/wireless/ath/wil6210/main.c
@@ -230,7 +230,7 @@ void wil_priv_deinit(struct wil6210_priv *wil)
static void wil_target_reset(struct wil6210_priv *wil)
{
- int delay = 100;
+ int delay = 0;
u32 baud_rate;
u32 rev_id;
@@ -277,7 +277,7 @@ static void wil_target_reset(struct wil6210_priv *wil)
do {
msleep(1);
baud_rate = R(RGF_USER_SERIAL_BAUD_RATE);
- if (delay-- < 0) {
+ if (delay++ > 100) {
wil_err(wil, "Reset not completed\n");
return;
}
@@ -286,7 +286,7 @@ static void wil_target_reset(struct wil6210_priv *wil)
if (rev_id == 2)
W(RGF_LOS_COUNTER_CTL, BIT(8));
- wil_dbg_misc(wil, "Reset completed\n");
+ wil_dbg_misc(wil, "Reset completed in %d ms\n", delay);
#undef R
#undef W
--
1.8.3.2
make sure reset flow executed in order
Signed-off-by: Vladimir Kondratiev <[email protected]>
---
drivers/net/wireless/ath/wil6210/main.c | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/drivers/net/wireless/ath/wil6210/main.c b/drivers/net/wireless/ath/wil6210/main.c
index c782e25..0005d9b 100644
--- a/drivers/net/wireless/ath/wil6210/main.c
+++ b/drivers/net/wireless/ath/wil6210/main.c
@@ -289,19 +289,23 @@ static void wil_target_reset(struct wil6210_priv *wil)
S(RGF_USER_CLKS_CTL_SW_RST_MASK_0, BIT(6));
/* car_perst_rst_src_n_mask */
S(RGF_USER_CLKS_CTL_SW_RST_MASK_0, BIT(7));
+ wmb(); /* order is important here */
W(RGF_USER_MAC_CPU_0, BIT(1)); /* mac_cpu_man_rst */
W(RGF_USER_USER_CPU_0, BIT(1)); /* user_cpu_man_rst */
+ wmb(); /* order is important here */
W(RGF_USER_CLKS_CTL_SW_RST_VEC_2, 0xFE000000);
W(RGF_USER_CLKS_CTL_SW_RST_VEC_1, 0x0000003F);
W(RGF_USER_CLKS_CTL_SW_RST_VEC_3, 0x00000170);
W(RGF_USER_CLKS_CTL_SW_RST_VEC_0, 0xFFE7FC00);
+ wmb(); /* order is important here */
W(RGF_USER_CLKS_CTL_SW_RST_VEC_3, 0);
W(RGF_USER_CLKS_CTL_SW_RST_VEC_2, 0);
W(RGF_USER_CLKS_CTL_SW_RST_VEC_1, 0);
W(RGF_USER_CLKS_CTL_SW_RST_VEC_0, 0);
+ wmb(); /* order is important here */
W(RGF_USER_CLKS_CTL_SW_RST_VEC_3, 0x00000001);
if (rev_id == 1) {
@@ -311,6 +315,7 @@ static void wil_target_reset(struct wil6210_priv *wil)
W(RGF_USER_CLKS_CTL_SW_RST_VEC_2, 0x00008000);
}
W(RGF_USER_CLKS_CTL_SW_RST_VEC_0, 0);
+ wmb(); /* order is important here */
/* wait until device ready */
do {
@@ -327,6 +332,7 @@ static void wil_target_reset(struct wil6210_priv *wil)
W(RGF_PCIE_LOS_COUNTER_CTL, BIT(8));
C(RGF_USER_CLKS_CTL_0, BIT_USER_CLKS_RST_PWGD);
+ wmb(); /* order is important here */
wil_dbg_misc(wil, "Reset completed in %d ms\n", delay);
--
1.8.3.2
These methods can change device state, serialize with others
similar ones like up/down
Signed-off-by: Vladimir Kondratiev <[email protected]>
---
drivers/net/wireless/ath/wil6210/cfg80211.c | 15 +++++++++++----
drivers/net/wireless/ath/wil6210/main.c | 2 ++
2 files changed, 13 insertions(+), 4 deletions(-)
diff --git a/drivers/net/wireless/ath/wil6210/cfg80211.c b/drivers/net/wireless/ath/wil6210/cfg80211.c
index 597540a..ed5a7e1 100644
--- a/drivers/net/wireless/ath/wil6210/cfg80211.c
+++ b/drivers/net/wireless/ath/wil6210/cfg80211.c
@@ -609,18 +609,20 @@ static int wil_cfg80211_start_ap(struct wiphy *wiphy,
if (wil_fix_bcon(wil, bcon))
wil_dbg_misc(wil, "Fixed bcon\n");
+ mutex_lock(&wil->mutex);
+
rc = wil_reset(wil);
if (rc)
- return rc;
+ goto out;
/* Rx VRING. */
rc = wil_rx_init(wil);
if (rc)
- return rc;
+ goto out;
rc = wmi_set_ssid(wil, info->ssid_len, info->ssid);
if (rc)
- return rc;
+ goto out;
/* MAC address - pre-requisite for other commands */
wmi_set_mac_address(wil, ndev->dev_addr);
@@ -644,11 +646,13 @@ static int wil_cfg80211_start_ap(struct wiphy *wiphy,
rc = wmi_pcp_start(wil, info->beacon_interval, wmi_nettype,
channel->hw_value);
if (rc)
- return rc;
+ goto out;
netif_carrier_on(ndev);
+out:
+ mutex_unlock(&wil->mutex);
return rc;
}
@@ -658,8 +662,11 @@ static int wil_cfg80211_stop_ap(struct wiphy *wiphy,
int rc = 0;
struct wil6210_priv *wil = wiphy_to_wil(wiphy);
+ mutex_lock(&wil->mutex);
+
rc = wmi_pcp_stop(wil);
+ mutex_unlock(&wil->mutex);
return rc;
}
diff --git a/drivers/net/wireless/ath/wil6210/main.c b/drivers/net/wireless/ath/wil6210/main.c
index 32ac1b9..351925b 100644
--- a/drivers/net/wireless/ath/wil6210/main.c
+++ b/drivers/net/wireless/ath/wil6210/main.c
@@ -159,6 +159,7 @@ static void wil_fw_error_worker(struct work_struct *work)
if (no_fw_recovery)
return;
+ mutex_lock(&wil->mutex);
switch (wdev->iftype) {
case NL80211_IFTYPE_STATION:
case NL80211_IFTYPE_P2P_CLIENT:
@@ -176,6 +177,7 @@ static void wil_fw_error_worker(struct work_struct *work)
default:
break;
}
+ mutex_unlock(&wil->mutex);
}
static int wil_find_free_vring(struct wil6210_priv *wil)
--
1.8.3.2
When switching between STA and AP modes, memory allocated for Rx vring leaks
This is because start_ap() allocates Rx vring but stop_ap() do not free it.
Logically, Rx vring is not valid (HW can't use it anymore), so free it in reset()
Also, check double init for Rx vring and bail out with -EINVAL
Signed-off-by: Vladimir Kondratiev <[email protected]>
---
drivers/net/wireless/ath/wil6210/main.c | 2 ++
drivers/net/wireless/ath/wil6210/txrx.c | 5 +++++
2 files changed, 7 insertions(+)
diff --git a/drivers/net/wireless/ath/wil6210/main.c b/drivers/net/wireless/ath/wil6210/main.c
index 6847622..de952ab 100644
--- a/drivers/net/wireless/ath/wil6210/main.c
+++ b/drivers/net/wireless/ath/wil6210/main.c
@@ -343,6 +343,8 @@ int wil_reset(struct wil6210_priv *wil)
/* TODO: put MAC in reset */
wil_target_reset(wil);
+ wil_rx_fini(wil);
+
/* init after reset */
wil->pending_connect_cid = -1;
reinit_completion(&wil->wmi_ready);
diff --git a/drivers/net/wireless/ath/wil6210/txrx.c b/drivers/net/wireless/ath/wil6210/txrx.c
index 5cda0ea9..97d036ad 100644
--- a/drivers/net/wireless/ath/wil6210/txrx.c
+++ b/drivers/net/wireless/ath/wil6210/txrx.c
@@ -557,6 +557,11 @@ int wil_rx_init(struct wil6210_priv *wil)
struct vring *vring = &wil->vring_rx;
int rc;
+ if (vring->va) {
+ wil_err(wil, "Rx ring already allocated\n");
+ return -EINVAL;
+ }
+
vring->size = WIL6210_RX_RING_SIZE;
rc = wil_vring_alloc(wil, vring);
if (rc)
--
1.8.3.2
Put all registers in order for easier navigation;
fix naming to reflect hardware cluster
Signed-off-by: Vladimir Kondratiev <[email protected]>
---
drivers/net/wireless/ath/wil6210/main.c | 6 +++---
drivers/net/wireless/ath/wil6210/wil6210.h | 31 +++++++++++++++---------------
2 files changed, 19 insertions(+), 18 deletions(-)
diff --git a/drivers/net/wireless/ath/wil6210/main.c b/drivers/net/wireless/ath/wil6210/main.c
index 1f9f1d2..bd27bee 100644
--- a/drivers/net/wireless/ath/wil6210/main.c
+++ b/drivers/net/wireless/ath/wil6210/main.c
@@ -244,7 +244,7 @@ static void wil_target_reset(struct wil6210_priv *wil)
#define S(a, v) iowrite32(ioread32(wil->csr + HOSTADDR(a)) | v, \
wil->csr + HOSTADDR(a))
- wil->hw_version = R(RGF_FW_REV_ID);
+ wil->hw_version = R(RGF_USER_FW_REV_ID);
rev_id = wil->hw_version & 0xff;
/* hpal_perst_from_pad_src_n_mask */
S(RGF_USER_CLKS_CTL_SW_RST_MASK_0, BIT(6));
@@ -268,7 +268,7 @@ static void wil_target_reset(struct wil6210_priv *wil)
if (rev_id == 1) {
W(RGF_USER_CLKS_CTL_SW_RST_VEC_2, 0x00000080);
} else {
- W(RGF_LOS_COUNTER_CTL, BIT(6) | BIT(8));
+ W(RGF_PCIE_LOS_COUNTER_CTL, BIT(6) | BIT(8));
W(RGF_USER_CLKS_CTL_SW_RST_VEC_2, 0x00008000);
}
W(RGF_USER_CLKS_CTL_SW_RST_VEC_0, 0);
@@ -284,7 +284,7 @@ static void wil_target_reset(struct wil6210_priv *wil)
} while (baud_rate != 0x15e);
if (rev_id == 2)
- W(RGF_LOS_COUNTER_CTL, BIT(8));
+ W(RGF_PCIE_LOS_COUNTER_CTL, BIT(8));
wil_dbg_misc(wil, "Reset completed in %d ms\n", delay);
diff --git a/drivers/net/wireless/ath/wil6210/wil6210.h b/drivers/net/wireless/ath/wil6210/wil6210.h
index 317621c..b376399 100644
--- a/drivers/net/wireless/ath/wil6210/wil6210.h
+++ b/drivers/net/wireless/ath/wil6210/wil6210.h
@@ -74,26 +74,18 @@ struct RGF_ICR {
} __packed;
/* registers - FW addresses */
-#define RGF_FW_REV_ID (0x880a8c) /* chip revision */
#define RGF_USER_SERIAL_BAUD_RATE (0x880050)
-#define RGF_LOS_COUNTER_CTL (0x882dc4)
-#define RGF_USER_USER_SCRATCH_PAD (0x8802bc)
-#define RGF_USER_USER_ICR (0x880b4c) /* struct RGF_ICR */
- #define BIT_USER_USER_ICR_SW_INT_2 BIT(18)
-#define RGF_USER_CLKS_CTL_SW_RST_MASK_0 (0x880b14)
-#define RGF_USER_MAC_CPU_0 (0x8801fc)
#define RGF_USER_USER_CPU_0 (0x8801e0)
+#define RGF_USER_MAC_CPU_0 (0x8801fc)
+#define RGF_USER_USER_SCRATCH_PAD (0x8802bc)
+#define RGF_USER_FW_REV_ID (0x880a8c) /* chip revision */
#define RGF_USER_CLKS_CTL_SW_RST_VEC_0 (0x880b04)
#define RGF_USER_CLKS_CTL_SW_RST_VEC_1 (0x880b08)
#define RGF_USER_CLKS_CTL_SW_RST_VEC_2 (0x880b0c)
#define RGF_USER_CLKS_CTL_SW_RST_VEC_3 (0x880b10)
-
-#define RGF_DMA_PSEUDO_CAUSE (0x881c68)
-#define RGF_DMA_PSEUDO_CAUSE_MASK_SW (0x881c6c)
-#define RGF_DMA_PSEUDO_CAUSE_MASK_FW (0x881c70)
- #define BIT_DMA_PSEUDO_CAUSE_RX BIT(0)
- #define BIT_DMA_PSEUDO_CAUSE_TX BIT(1)
- #define BIT_DMA_PSEUDO_CAUSE_MISC BIT(2)
+#define RGF_USER_CLKS_CTL_SW_RST_MASK_0 (0x880b14)
+#define RGF_USER_USER_ICR (0x880b4c) /* struct RGF_ICR */
+ #define BIT_USER_USER_ICR_SW_INT_2 BIT(18)
#define RGF_DMA_EP_TX_ICR (0x881bb4) /* struct RGF_ICR */
#define BIT_DMA_EP_TX_ICR_TX_DONE BIT(0)
@@ -108,13 +100,22 @@ struct RGF_ICR {
/* Interrupt moderation control */
#define RGF_DMA_ITR_CNT_TRSH (0x881c5c)
#define RGF_DMA_ITR_CNT_DATA (0x881c60)
-#define RGF_DMA_ITR_CNT_CRL (0x881C64)
+#define RGF_DMA_ITR_CNT_CRL (0x881c64)
#define BIT_DMA_ITR_CNT_CRL_EN BIT(0)
#define BIT_DMA_ITR_CNT_CRL_EXT_TICK BIT(1)
#define BIT_DMA_ITR_CNT_CRL_FOREVER BIT(2)
#define BIT_DMA_ITR_CNT_CRL_CLR BIT(3)
#define BIT_DMA_ITR_CNT_CRL_REACH_TRSH BIT(4)
+#define RGF_DMA_PSEUDO_CAUSE (0x881c68)
+#define RGF_DMA_PSEUDO_CAUSE_MASK_SW (0x881c6c)
+#define RGF_DMA_PSEUDO_CAUSE_MASK_FW (0x881c70)
+ #define BIT_DMA_PSEUDO_CAUSE_RX BIT(0)
+ #define BIT_DMA_PSEUDO_CAUSE_TX BIT(1)
+ #define BIT_DMA_PSEUDO_CAUSE_MISC BIT(2)
+
+#define RGF_PCIE_LOS_COUNTER_CTL (0x882dc4)
+
/* popular locations */
#define HOST_MBOX HOSTADDR(RGF_USER_USER_SCRATCH_PAD)
#define HOST_SW_INT (HOSTADDR(RGF_USER_USER_ICR) + \
--
1.8.3.2
When disconnecting some CID, corresponded Tx vring get released. During vring
release, all descriptors get freed. It is possible that Tx NAPI working on the same
vring simultaneously. If it happens, descriptor may be double freed.
To protect from the race above, make sure NAPI won't process the same vring.
Introduce 'enabled' flag in the struct vring_tx_data. Proceed with Tx NAPI only if
'enabled' flag set. Prior to Tx vring release, clear this flag and make sure NAPI
get synchronized.
NAPI enablement status protected by wil->mutex, add protection where it was
missing and check for it.
During reset, disconnect all peers first, then proceed with the Rx vring. It allows for
the disconnect flow to observe proper 'wil->status' and correctly notify cfg80211 about
connection status change
Signed-off-by: Vladimir Kondratiev <[email protected]>
---
drivers/net/wireless/ath/wil6210/cfg80211.c | 4 ++++
drivers/net/wireless/ath/wil6210/main.c | 17 +++++++++++++----
drivers/net/wireless/ath/wil6210/pcie_bus.c | 2 ++
drivers/net/wireless/ath/wil6210/txrx.c | 17 +++++++++++++++++
drivers/net/wireless/ath/wil6210/wil6210.h | 9 +++++++++
drivers/net/wireless/ath/wil6210/wmi.c | 2 ++
6 files changed, 47 insertions(+), 4 deletions(-)
diff --git a/drivers/net/wireless/ath/wil6210/cfg80211.c b/drivers/net/wireless/ath/wil6210/cfg80211.c
index ed5a7e1..4806a49 100644
--- a/drivers/net/wireless/ath/wil6210/cfg80211.c
+++ b/drivers/net/wireless/ath/wil6210/cfg80211.c
@@ -674,7 +674,11 @@ static int wil_cfg80211_del_station(struct wiphy *wiphy,
struct net_device *dev, u8 *mac)
{
struct wil6210_priv *wil = wiphy_to_wil(wiphy);
+
+ mutex_lock(&wil->mutex);
wil6210_disconnect(wil, mac);
+ mutex_unlock(&wil->mutex);
+
return 0;
}
diff --git a/drivers/net/wireless/ath/wil6210/main.c b/drivers/net/wireless/ath/wil6210/main.c
index 0005d9b..95f4efe 100644
--- a/drivers/net/wireless/ath/wil6210/main.c
+++ b/drivers/net/wireless/ath/wil6210/main.c
@@ -133,7 +133,9 @@ static void wil_disconnect_worker(struct work_struct *work)
struct wil6210_priv *wil = container_of(work,
struct wil6210_priv, disconnect_worker);
+ mutex_lock(&wil->mutex);
_wil6210_disconnect(wil, NULL);
+ mutex_unlock(&wil->mutex);
}
static void wil_connect_timer_fn(ulong x)
@@ -260,7 +262,9 @@ void wil_priv_deinit(struct wil6210_priv *wil)
{
cancel_work_sync(&wil->disconnect_worker);
cancel_work_sync(&wil->fw_error_worker);
+ mutex_lock(&wil->mutex);
wil6210_disconnect(wil, NULL);
+ mutex_unlock(&wil->mutex);
wmi_event_flush(wil);
destroy_workqueue(wil->wmi_wq_conn);
destroy_workqueue(wil->wmi_wq);
@@ -374,10 +378,14 @@ int wil_reset(struct wil6210_priv *wil)
{
int rc;
+ WARN_ON(!mutex_is_locked(&wil->mutex));
+
+ cancel_work_sync(&wil->disconnect_worker);
+ wil6210_disconnect(wil, NULL);
+
wil->status = 0; /* prevent NAPI from being scheduled */
if (test_bit(wil_status_napi_en, &wil->status)) {
napi_synchronize(&wil->napi_rx);
- napi_synchronize(&wil->napi_tx);
}
if (wil->scan_request) {
@@ -387,9 +395,6 @@ int wil_reset(struct wil6210_priv *wil)
wil->scan_request = NULL;
}
- cancel_work_sync(&wil->disconnect_worker);
- wil6210_disconnect(wil, NULL);
-
wil6210_disable_irq(wil);
wmi_event_flush(wil);
@@ -447,6 +452,8 @@ static int __wil_up(struct wil6210_priv *wil)
struct wireless_dev *wdev = wil->wdev;
int rc;
+ WARN_ON(!mutex_is_locked(&wil->mutex));
+
rc = wil_reset(wil);
if (rc)
return rc;
@@ -506,6 +513,8 @@ int wil_up(struct wil6210_priv *wil)
static int __wil_down(struct wil6210_priv *wil)
{
+ WARN_ON(!mutex_is_locked(&wil->mutex));
+
clear_bit(wil_status_napi_en, &wil->status);
napi_disable(&wil->napi_rx);
napi_disable(&wil->napi_tx);
diff --git a/drivers/net/wireless/ath/wil6210/pcie_bus.c b/drivers/net/wireless/ath/wil6210/pcie_bus.c
index c609761..58fc096 100644
--- a/drivers/net/wireless/ath/wil6210/pcie_bus.c
+++ b/drivers/net/wireless/ath/wil6210/pcie_bus.c
@@ -70,7 +70,9 @@ static int wil_if_pcie_enable(struct wil6210_priv *wil)
goto stop_master;
/* need reset here to obtain MAC */
+ mutex_lock(&wil->mutex);
rc = wil_reset(wil);
+ mutex_unlock(&wil->mutex);
if (rc)
goto release_irq;
diff --git a/drivers/net/wireless/ath/wil6210/txrx.c b/drivers/net/wireless/ath/wil6210/txrx.c
index cfd36cc..c8c5474 100644
--- a/drivers/net/wireless/ath/wil6210/txrx.c
+++ b/drivers/net/wireless/ath/wil6210/txrx.c
@@ -618,6 +618,7 @@ int wil_vring_init_tx(struct wil6210_priv *wil, int id, int size,
struct wmi_vring_cfg_done_event cmd;
} __packed reply;
struct vring *vring = &wil->vring_tx[id];
+ struct vring_tx_data *txdata = &wil->vring_tx_data[id];
if (vring->va) {
wil_err(wil, "Tx ring [%d] already allocated\n", id);
@@ -625,6 +626,7 @@ int wil_vring_init_tx(struct wil6210_priv *wil, int id, int size,
goto out;
}
+ memset(txdata, 0, sizeof(*txdata));
vring->size = size;
rc = wil_vring_alloc(wil, vring);
if (rc)
@@ -648,6 +650,8 @@ 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;
+
return 0;
out_free:
wil_vring_free(wil, vring, 1);
@@ -660,9 +664,16 @@ void wil_vring_fini_tx(struct wil6210_priv *wil, int id)
{
struct vring *vring = &wil->vring_tx[id];
+ WARN_ON(!mutex_is_locked(&wil->mutex));
+
if (!vring->va)
return;
+ /* make sure NAPI won't touch this vring */
+ wil->vring_tx_data[id].enabled = 0;
+ if (test_bit(wil_status_napi_en, &wil->status))
+ napi_synchronize(&wil->napi_tx);
+
wil_vring_free(wil, vring, 1);
}
@@ -1028,6 +1039,7 @@ int wil_tx_complete(struct wil6210_priv *wil, int ringid)
struct net_device *ndev = wil_to_ndev(wil);
struct device *dev = wil_to_dev(wil);
struct vring *vring = &wil->vring_tx[ringid];
+ struct vring_tx_data *txdata = &wil->vring_tx_data[ringid];
int done = 0;
int cid = wil->vring2cid_tid[ringid][0];
struct wil_net_stats *stats = &wil->sta[cid].stats;
@@ -1038,6 +1050,11 @@ int wil_tx_complete(struct wil6210_priv *wil, int ringid)
return 0;
}
+ if (!txdata->enabled) {
+ wil_info(wil, "Tx irq[%d]: vring disabled\n", ringid);
+ return 0;
+ }
+
wil_dbg_txrx(wil, "%s(%d)\n", __func__, ringid);
while (!wil_vring_is_empty(vring)) {
diff --git a/drivers/net/wireless/ath/wil6210/wil6210.h b/drivers/net/wireless/ath/wil6210/wil6210.h
index fb1006b..2a2dec7 100644
--- a/drivers/net/wireless/ath/wil6210/wil6210.h
+++ b/drivers/net/wireless/ath/wil6210/wil6210.h
@@ -243,6 +243,14 @@ struct vring {
struct wil_ctx *ctx; /* ctx[size] - software context */
};
+/**
+ * Additional data for Tx Vring
+ */
+struct vring_tx_data {
+ int enabled;
+
+};
+
enum { /* for wil6210_priv.status */
wil_status_fwready = 0,
wil_status_fwconnecting,
@@ -386,6 +394,7 @@ struct wil6210_priv {
/* DMA related */
struct vring vring_rx;
struct vring vring_tx[WIL6210_MAX_TX_RINGS];
+ struct vring_tx_data vring_tx_data[WIL6210_MAX_TX_RINGS];
u8 vring2cid_tid[WIL6210_MAX_TX_RINGS][2]; /* [0] - CID, [1] - TID */
struct wil_sta_info sta[WIL6210_MAX_CID];
/* scan */
diff --git a/drivers/net/wireless/ath/wil6210/wmi.c b/drivers/net/wireless/ath/wil6210/wmi.c
index 58c3afc..2ba56ee 100644
--- a/drivers/net/wireless/ath/wil6210/wmi.c
+++ b/drivers/net/wireless/ath/wil6210/wmi.c
@@ -462,7 +462,9 @@ static void wmi_evt_disconnect(struct wil6210_priv *wil, int id,
wil->sinfo_gen++;
+ mutex_lock(&wil->mutex);
wil6210_disconnect(wil, evt->bssid);
+ mutex_unlock(&wil->mutex);
}
static void wmi_evt_notify(struct wil6210_priv *wil, int id, void *d, int len)
--
1.8.3.2
Use 'real' indication for hardware state.
Signed-off-by: Vladimir Kondratiev <[email protected]>
---
drivers/net/wireless/ath/wil6210/main.c | 11 ++++++-----
drivers/net/wireless/ath/wil6210/wil6210.h | 3 ++-
2 files changed, 8 insertions(+), 6 deletions(-)
diff --git a/drivers/net/wireless/ath/wil6210/main.c b/drivers/net/wireless/ath/wil6210/main.c
index 351925b..c782e25 100644
--- a/drivers/net/wireless/ath/wil6210/main.c
+++ b/drivers/net/wireless/ath/wil6210/main.c
@@ -269,7 +269,7 @@ void wil_priv_deinit(struct wil6210_priv *wil)
static void wil_target_reset(struct wil6210_priv *wil)
{
int delay = 0;
- u32 baud_rate;
+ u32 hw_state;
u32 rev_id;
wil_dbg_misc(wil, "Resetting...\n");
@@ -312,15 +312,16 @@ static void wil_target_reset(struct wil6210_priv *wil)
}
W(RGF_USER_CLKS_CTL_SW_RST_VEC_0, 0);
- /* wait until device ready. Use baud rate */
+ /* wait until device ready */
do {
msleep(1);
- baud_rate = R(RGF_USER_SERIAL_BAUD_RATE);
+ hw_state = R(RGF_USER_HW_MACHINE_STATE);
if (delay++ > 100) {
- wil_err(wil, "Reset not completed\n");
+ wil_err(wil, "Reset not completed, hw_state 0x%08x\n",
+ hw_state);
return;
}
- } while (baud_rate != 0x15e);
+ } while (hw_state != HW_MACHINE_BOOT_DONE);
if (rev_id == 2)
W(RGF_PCIE_LOS_COUNTER_CTL, BIT(8));
diff --git a/drivers/net/wireless/ath/wil6210/wil6210.h b/drivers/net/wireless/ath/wil6210/wil6210.h
index 80573f7..fb1006b 100644
--- a/drivers/net/wireless/ath/wil6210/wil6210.h
+++ b/drivers/net/wireless/ath/wil6210/wil6210.h
@@ -74,7 +74,8 @@ struct RGF_ICR {
} __packed;
/* registers - FW addresses */
-#define RGF_USER_SERIAL_BAUD_RATE (0x880050)
+#define RGF_USER_HW_MACHINE_STATE (0x8801dc)
+ #define HW_MACHINE_BOOT_DONE (0x3fffffd)
#define RGF_USER_USER_CPU_0 (0x8801e0)
#define RGF_USER_MAC_CPU_0 (0x8801fc)
#define RGF_USER_USER_SCRATCH_PAD (0x8802bc)
--
1.8.3.2
Encode/decode helpers
Signed-off-by: Vladimir Kondratiev <[email protected]>
---
drivers/net/wireless/ath/wil6210/txrx.c | 2 +-
drivers/net/wireless/ath/wil6210/wil6210.h | 25 +++++++++++++++++++++++++
2 files changed, 26 insertions(+), 1 deletion(-)
diff --git a/drivers/net/wireless/ath/wil6210/txrx.c b/drivers/net/wireless/ath/wil6210/txrx.c
index 092081e..7afaa5e 100644
--- a/drivers/net/wireless/ath/wil6210/txrx.c
+++ b/drivers/net/wireless/ath/wil6210/txrx.c
@@ -588,7 +588,7 @@ int wil_vring_init_tx(struct wil6210_priv *wil, int id, int size,
.ring_size = cpu_to_le16(size),
},
.ringid = id,
- .cidxtid = (cid & 0xf) | ((tid & 0xf) << 4),
+ .cidxtid = mk_cidxtid(cid, tid),
.encap_trans_type = WMI_VRING_ENC_TYPE_802_3,
.mac_ctrl = 0,
.to_resolution = 0,
diff --git a/drivers/net/wireless/ath/wil6210/wil6210.h b/drivers/net/wireless/ath/wil6210/wil6210.h
index 980dccc..273d00f 100644
--- a/drivers/net/wireless/ath/wil6210/wil6210.h
+++ b/drivers/net/wireless/ath/wil6210/wil6210.h
@@ -125,6 +125,31 @@ struct RGF_ICR {
/* Hardware definitions end */
+/**
+ * mk_cidxtid - construct @cidxtid field
+ * @cid: CID value
+ * @tid: TID value
+ *
+ * @cidxtid field encoded as bits 0..3 - CID; 4..7 - TID
+ */
+static inline u8 mk_cidxtid(u8 cid, u8 tid)
+{
+ return ((tid & 0xf) << 4) | (cid & 0xf);
+}
+
+/**
+ * parse_cidxtid - parse @cidxtid field
+ * @cid: store CID value here
+ * @tid: store TID value here
+ *
+ * @cidxtid field encoded as bits 0..3 - CID; 4..7 - TID
+ */
+static inline void parse_cidxtid(u8 cidxtid, u8 *cid, u8 *tid)
+{
+ *cid = cidxtid & 0xf;
+ *tid = (cidxtid >> 4) & 0xf;
+}
+
struct wil6210_mbox_ring {
u32 base;
u16 entry_size; /* max. size of mbox entry, incl. all headers */
--
1.8.3.2