summaryrefslogtreecommitdiffstats
path: root/drivers/net/wireless/ath/wil6210
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2014-10-08 21:40:54 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2014-10-08 21:40:54 -0400
commit35a9ad8af0bb0fa3525e6d0d20e32551d226f38e (patch)
tree15b4b33206818886d9cff371fd2163e073b70568 /drivers/net/wireless/ath/wil6210
parentd5935b07da53f74726e2a65dd4281d0f2c70e5d4 (diff)
parent64b1f00a0830e1c53874067273a096b228d83d36 (diff)
downloadop-kernel-dev-35a9ad8af0bb0fa3525e6d0d20e32551d226f38e.zip
op-kernel-dev-35a9ad8af0bb0fa3525e6d0d20e32551d226f38e.tar.gz
Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next
Pull networking updates from David Miller: "Most notable changes in here: 1) By far the biggest accomplishment, thanks to a large range of contributors, is the addition of multi-send for transmit. This is the result of discussions back in Chicago, and the hard work of several individuals. Now, when the ->ndo_start_xmit() method of a driver sees skb->xmit_more as true, it can choose to defer the doorbell telling the driver to start processing the new TX queue entires. skb->xmit_more means that the generic networking is guaranteed to call the driver immediately with another SKB to send. There is logic added to the qdisc layer to dequeue multiple packets at a time, and the handling mis-predicted offloads in software is now done with no locks held. Finally, pktgen is extended to have a "burst" parameter that can be used to test a multi-send implementation. Several drivers have xmit_more support: i40e, igb, ixgbe, mlx4, virtio_net Adding support is almost trivial, so export more drivers to support this optimization soon. I want to thank, in no particular or implied order, Jesper Dangaard Brouer, Eric Dumazet, Alexander Duyck, Tom Herbert, Jamal Hadi Salim, John Fastabend, Florian Westphal, Daniel Borkmann, David Tat, Hannes Frederic Sowa, and Rusty Russell. 2) PTP and timestamping support in bnx2x, from Michal Kalderon. 3) Allow adjusting the rx_copybreak threshold for a driver via ethtool, and add rx_copybreak support to enic driver. From Govindarajulu Varadarajan. 4) Significant enhancements to the generic PHY layer and the bcm7xxx driver in particular (EEE support, auto power down, etc.) from Florian Fainelli. 5) Allow raw buffers to be used for flow dissection, allowing drivers to determine the optimal "linear pull" size for devices that DMA into pools of pages. The objective is to get exactly the necessary amount of headers into the linear SKB area pre-pulled, but no more. The new interface drivers use is eth_get_headlen(). From WANG Cong, with driver conversions (several had their own by-hand duplicated implementations) by Alexander Duyck and Eric Dumazet. 6) Support checksumming more smoothly and efficiently for encapsulations, and add "foo over UDP" facility. From Tom Herbert. 7) Add Broadcom SF2 switch driver to DSA layer, from Florian Fainelli. 8) eBPF now can load programs via a system call and has an extensive testsuite. Alexei Starovoitov and Daniel Borkmann. 9) Major overhaul of the packet scheduler to use RCU in several major areas such as the classifiers and rate estimators. From John Fastabend. 10) Add driver for Intel FM10000 Ethernet Switch, from Alexander Duyck. 11) Rearrange TCP_SKB_CB() to reduce cache line misses, from Eric Dumazet. 12) Add Datacenter TCP congestion control algorithm support, From Florian Westphal. 13) Reorganize sk_buff so that __copy_skb_header() is significantly faster. From Eric Dumazet" * git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next: (1558 commits) netlabel: directly return netlbl_unlabel_genl_init() net: add netdev_txq_bql_{enqueue, complete}_prefetchw() helpers net: description of dma_cookie cause make xmldocs warning cxgb4: clean up a type issue cxgb4: potential shift wrapping bug i40e: skb->xmit_more support net: fs_enet: Add NAPI TX net: fs_enet: Remove non NAPI RX r8169:add support for RTL8168EP net_sched: copy exts->type in tcf_exts_change() wimax: convert printk to pr_foo() af_unix: remove 0 assignment on static ipv6: Do not warn for informational ICMP messages, regardless of type. Update Intel Ethernet Driver maintainers list bridge: Save frag_max_size between PRE_ROUTING and POST_ROUTING tipc: fix bug in multicast congestion handling net: better IFF_XMIT_DST_RELEASE support net/mlx4_en: remove NETDEV_TX_BUSY 3c59x: fix bad split of cpu_to_le32(pci_map_single()) net: bcmgenet: fix Tx ring priority programming ...
Diffstat (limited to 'drivers/net/wireless/ath/wil6210')
-rw-r--r--drivers/net/wireless/ath/wil6210/Kconfig9
-rw-r--r--drivers/net/wireless/ath/wil6210/Makefile5
-rw-r--r--drivers/net/wireless/ath/wil6210/cfg80211.c136
-rw-r--r--drivers/net/wireless/ath/wil6210/debug.c18
-rw-r--r--drivers/net/wireless/ath/wil6210/debugfs.c456
-rw-r--r--drivers/net/wireless/ath/wil6210/ethtool.c103
-rw-r--r--drivers/net/wireless/ath/wil6210/fw.c45
-rw-r--r--drivers/net/wireless/ath/wil6210/fw.h149
-rw-r--r--drivers/net/wireless/ath/wil6210/fw_inc.c495
-rw-r--r--drivers/net/wireless/ath/wil6210/interrupt.c45
-rw-r--r--drivers/net/wireless/ath/wil6210/ioctl.c173
-rw-r--r--drivers/net/wireless/ath/wil6210/main.c277
-rw-r--r--drivers/net/wireless/ath/wil6210/netdev.c38
-rw-r--r--drivers/net/wireless/ath/wil6210/pcie_bus.c46
-rw-r--r--drivers/net/wireless/ath/wil6210/rx_reorder.c29
-rw-r--r--drivers/net/wireless/ath/wil6210/txrx.c69
-rw-r--r--drivers/net/wireless/ath/wil6210/txrx.h11
-rw-r--r--drivers/net/wireless/ath/wil6210/wil6210.h94
-rw-r--r--drivers/net/wireless/ath/wil6210/wil_platform.c49
-rw-r--r--drivers/net/wireless/ath/wil6210/wil_platform.h34
-rw-r--r--drivers/net/wireless/ath/wil6210/wil_platform_msm.c257
-rw-r--r--drivers/net/wireless/ath/wil6210/wil_platform_msm.h24
-rw-r--r--drivers/net/wireless/ath/wil6210/wmi.c90
-rw-r--r--drivers/net/wireless/ath/wil6210/wmi.h22
24 files changed, 2324 insertions, 350 deletions
diff --git a/drivers/net/wireless/ath/wil6210/Kconfig b/drivers/net/wireless/ath/wil6210/Kconfig
index ce8c038..481680a 100644
--- a/drivers/net/wireless/ath/wil6210/Kconfig
+++ b/drivers/net/wireless/ath/wil6210/Kconfig
@@ -39,3 +39,12 @@ config WIL6210_TRACING
option if you are interested in debugging the driver.
If unsure, say Y to make it easier to debug problems.
+
+config WIL6210_PLATFORM_MSM
+ bool "wil6210 MSM platform specific support"
+ depends on WIL6210
+ depends on ARCH_MSM
+ default y
+ ---help---
+ Say Y here to enable wil6210 driver support for MSM
+ platform specific features
diff --git a/drivers/net/wireless/ath/wil6210/Makefile b/drivers/net/wireless/ath/wil6210/Makefile
index c7a3465..8ad4b5f 100644
--- a/drivers/net/wireless/ath/wil6210/Makefile
+++ b/drivers/net/wireless/ath/wil6210/Makefile
@@ -10,7 +10,12 @@ wil6210-y += interrupt.o
wil6210-y += txrx.o
wil6210-y += debug.o
wil6210-y += rx_reorder.o
+wil6210-y += ioctl.o
+wil6210-y += fw.o
wil6210-$(CONFIG_WIL6210_TRACING) += trace.o
+wil6210-y += wil_platform.o
+wil6210-$(CONFIG_WIL6210_PLATFORM_MSM) += wil_platform_msm.o
+wil6210-y += ethtool.o
# for tracing framework to find trace.h
CFLAGS_trace.o := -I$(src)
diff --git a/drivers/net/wireless/ath/wil6210/cfg80211.c b/drivers/net/wireless/ath/wil6210/cfg80211.c
index 4ac2c20..d9f4b30 100644
--- a/drivers/net/wireless/ath/wil6210/cfg80211.c
+++ b/drivers/net/wireless/ath/wil6210/cfg80211.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012 Qualcomm Atheros, Inc.
+ * Copyright (c) 2012-2014 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
@@ -296,6 +296,7 @@ static int wil_cfg80211_scan(struct wiphy *wiphy,
n = min(request->n_channels, 4U);
for (i = 0; i < n; i++) {
int ch = request->channels[i]->hw_value;
+
if (ch == 0) {
wil_err(wil,
"Scan requested for unknown frequency %dMhz\n",
@@ -308,15 +309,47 @@ static int wil_cfg80211_scan(struct wiphy *wiphy,
request->channels[i]->center_freq);
}
+ if (request->ie_len)
+ print_hex_dump_bytes("Scan IE ", DUMP_PREFIX_OFFSET,
+ request->ie, request->ie_len);
+ else
+ wil_dbg_misc(wil, "Scan has no IE's\n");
+
+ rc = wmi_set_ie(wil, WMI_FRAME_PROBE_REQ, request->ie_len,
+ request->ie);
+ if (rc) {
+ wil_err(wil, "Aborting scan, set_ie failed: %d\n", rc);
+ goto out;
+ }
+
rc = wmi_send(wil, WMI_START_SCAN_CMDID, &cmd, sizeof(cmd.cmd) +
cmd.cmd.num_channels * sizeof(cmd.cmd.channel_list[0]));
- if (rc)
+out:
+ if (rc) {
+ del_timer_sync(&wil->scan_timer);
wil->scan_request = NULL;
+ }
return rc;
}
+static void wil_print_connect_params(struct wil6210_priv *wil,
+ struct cfg80211_connect_params *sme)
+{
+ wil_info(wil, "Connecting to:\n");
+ if (sme->channel) {
+ wil_info(wil, " Channel: %d freq %d\n",
+ sme->channel->hw_value, sme->channel->center_freq);
+ }
+ if (sme->bssid)
+ wil_info(wil, " BSSID: %pM\n", sme->bssid);
+ if (sme->ssid)
+ print_hex_dump(KERN_INFO, " SSID: ", DUMP_PREFIX_OFFSET,
+ 16, 1, sme->ssid, sme->ssid_len, true);
+ wil_info(wil, " Privacy: %s\n", sme->privacy ? "secure" : "open");
+}
+
static int wil_cfg80211_connect(struct wiphy *wiphy,
struct net_device *ndev,
struct cfg80211_connect_params *sme)
@@ -333,6 +366,8 @@ static int wil_cfg80211_connect(struct wiphy *wiphy,
test_bit(wil_status_fwconnected, &wil->status))
return -EALREADY;
+ wil_print_connect_params(wil, sme);
+
bss = cfg80211_get_bss(wiphy, sme->channel, sme->bssid,
sme->ssid, sme->ssid_len,
WLAN_CAPABILITY_ESS, WLAN_CAPABILITY_ESS);
@@ -358,22 +393,22 @@ static int wil_cfg80211_connect(struct wiphy *wiphy,
sme->ie_len);
goto out;
}
- /*
- * For secure assoc, send:
- * (1) WMI_DELETE_CIPHER_KEY_CMD
- * (2) WMI_SET_APPIE_CMD
- */
+ /* For secure assoc, send WMI_DELETE_CIPHER_KEY_CMD */
rc = wmi_del_cipher_key(wil, 0, bss->bssid);
if (rc) {
wil_err(wil, "WMI_DELETE_CIPHER_KEY_CMD failed\n");
goto out;
}
- /* WMI_SET_APPIE_CMD */
- rc = wmi_set_ie(wil, WMI_FRAME_ASSOC_REQ, sme->ie_len, sme->ie);
- if (rc) {
- wil_err(wil, "WMI_SET_APPIE_CMD failed\n");
- goto out;
- }
+ }
+
+ /* WMI_SET_APPIE_CMD. ie may contain rsn info as well as other info
+ * elements. Send it also in case it's empty, to erase previously set
+ * ies in FW.
+ */
+ rc = wmi_set_ie(wil, WMI_FRAME_ASSOC_REQ, sme->ie_len, sme->ie);
+ if (rc) {
+ wil_err(wil, "WMI_SET_APPIE_CMD failed\n");
+ goto out;
}
/* WMI_CONNECT_CMD */
@@ -619,6 +654,45 @@ static int wil_fix_bcon(struct wil6210_priv *wil,
return rc;
}
+static int wil_cfg80211_change_beacon(struct wiphy *wiphy,
+ struct net_device *ndev,
+ struct cfg80211_beacon_data *bcon)
+{
+ struct wil6210_priv *wil = wiphy_to_wil(wiphy);
+ int rc;
+
+ wil_dbg_misc(wil, "%s()\n", __func__);
+
+ if (wil_fix_bcon(wil, bcon)) {
+ wil_dbg_misc(wil, "Fixed bcon\n");
+ wil_print_bcon_data(bcon);
+ }
+
+ /* FW do not form regular beacon, so bcon IE's are not set
+ * For the DMG bcon, when it will be supported, bcon IE's will
+ * be reused; add something like:
+ * 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);
+ if (rc) {
+ wil_err(wil, "set_ie(PROBE_RESP) failed\n");
+ return rc;
+ }
+
+ rc = wmi_set_ie(wil, WMI_FRAME_ASSOC_RESP,
+ bcon->assocresp_ies_len,
+ bcon->assocresp_ies);
+ if (rc) {
+ wil_err(wil, "set_ie(ASSOC_RESP) failed\n");
+ return rc;
+ }
+
+ return 0;
+}
+
static int wil_cfg80211_start_ap(struct wiphy *wiphy,
struct net_device *ndev,
struct cfg80211_ap_settings *info)
@@ -654,14 +728,12 @@ static int wil_cfg80211_start_ap(struct wiphy *wiphy,
wil_print_bcon_data(bcon);
}
- mutex_lock(&wil->mutex);
+ wil_set_recovery_state(wil, fw_recovery_idle);
- rc = wil_reset(wil);
- if (rc)
- goto out;
+ mutex_lock(&wil->mutex);
- /* Rx VRING. */
- rc = wil_rx_init(wil);
+ __wil_down(wil);
+ rc = __wil_up(wil);
if (rc)
goto out;
@@ -669,9 +741,6 @@ static int wil_cfg80211_start_ap(struct wiphy *wiphy,
if (rc)
goto out;
- /* MAC address - pre-requisite for other commands */
- wmi_set_mac_address(wil, ndev->dev_addr);
-
/* IE's */
/* bcon 'head IE's are not relevant for 60g band */
/*
@@ -693,7 +762,6 @@ static int wil_cfg80211_start_ap(struct wiphy *wiphy,
if (rc)
goto out;
-
netif_carrier_on(ndev);
out:
@@ -704,17 +772,23 @@ out:
static int wil_cfg80211_stop_ap(struct wiphy *wiphy,
struct net_device *ndev)
{
- int rc = 0;
+ int rc, rc1;
struct wil6210_priv *wil = wiphy_to_wil(wiphy);
wil_dbg_misc(wil, "%s()\n", __func__);
+ wil_set_recovery_state(wil, fw_recovery_idle);
+
mutex_lock(&wil->mutex);
rc = wmi_pcp_stop(wil);
+ __wil_down(wil);
+ rc1 = __wil_up(wil);
+
mutex_unlock(&wil->mutex);
- return rc;
+
+ return min(rc, rc1);
}
static int wil_cfg80211_del_station(struct wiphy *wiphy,
@@ -744,6 +818,7 @@ static struct cfg80211_ops wil_cfg80211_ops = {
.del_key = wil_cfg80211_del_key,
.set_default_key = wil_cfg80211_set_default_key,
/* AP mode */
+ .change_beacon = wil_cfg80211_change_beacon,
.start_ap = wil_cfg80211_start_ap,
.stop_ap = wil_cfg80211_stop_ap,
.del_station = wil_cfg80211_del_station,
@@ -753,6 +828,7 @@ static void wil_wiphy_init(struct wiphy *wiphy)
{
/* TODO: set real value */
wiphy->max_scan_ssids = 10;
+ wiphy->max_scan_ie_len = WMI_MAX_IE_LEN;
wiphy->max_num_pmkids = 0 /* TODO: */;
wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
BIT(NL80211_IFTYPE_AP) |
@@ -762,8 +838,8 @@ static void wil_wiphy_init(struct wiphy *wiphy)
*/
wiphy->flags |= WIPHY_FLAG_HAVE_AP_SME |
WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD;
- dev_warn(wiphy_dev(wiphy), "%s : flags = 0x%08x\n",
- __func__, wiphy->flags);
+ dev_dbg(wiphy_dev(wiphy), "%s : flags = 0x%08x\n",
+ __func__, wiphy->flags);
wiphy->probe_resp_offload =
NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS |
NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS2 |
@@ -784,7 +860,9 @@ struct wireless_dev *wil_cfg80211_init(struct device *dev)
int rc = 0;
struct wireless_dev *wdev;
- wdev = kzalloc(sizeof(struct wireless_dev), GFP_KERNEL);
+ dev_dbg(dev, "%s()\n", __func__);
+
+ wdev = kzalloc(sizeof(*wdev), GFP_KERNEL);
if (!wdev)
return ERR_PTR(-ENOMEM);
@@ -816,6 +894,8 @@ void wil_wdev_free(struct wil6210_priv *wil)
{
struct wireless_dev *wdev = wil_to_wdev(wil);
+ dev_dbg(wil_to_dev(wil), "%s()\n", __func__);
+
if (!wdev)
return;
diff --git a/drivers/net/wireless/ath/wil6210/debug.c b/drivers/net/wireless/ath/wil6210/debug.c
index 9eeabf4..8d99021 100644
--- a/drivers/net/wireless/ath/wil6210/debug.c
+++ b/drivers/net/wireless/ath/wil6210/debug.c
@@ -17,43 +17,37 @@
#include "wil6210.h"
#include "trace.h"
-int wil_err(struct wil6210_priv *wil, const char *fmt, ...)
+void wil_err(struct wil6210_priv *wil, const char *fmt, ...)
{
struct net_device *ndev = wil_to_ndev(wil);
struct va_format vaf = {
.fmt = fmt,
};
va_list args;
- int ret;
va_start(args, fmt);
vaf.va = &args;
- ret = netdev_err(ndev, "%pV", &vaf);
+ netdev_err(ndev, "%pV", &vaf);
trace_wil6210_log_err(&vaf);
va_end(args);
-
- return ret;
}
-int wil_info(struct wil6210_priv *wil, const char *fmt, ...)
+void wil_info(struct wil6210_priv *wil, const char *fmt, ...)
{
struct net_device *ndev = wil_to_ndev(wil);
struct va_format vaf = {
.fmt = fmt,
};
va_list args;
- int ret;
va_start(args, fmt);
vaf.va = &args;
- ret = netdev_info(ndev, "%pV", &vaf);
+ netdev_info(ndev, "%pV", &vaf);
trace_wil6210_log_info(&vaf);
va_end(args);
-
- return ret;
}
-int wil_dbg_trace(struct wil6210_priv *wil, const char *fmt, ...)
+void wil_dbg_trace(struct wil6210_priv *wil, const char *fmt, ...)
{
struct va_format vaf = {
.fmt = fmt,
@@ -64,6 +58,4 @@ int wil_dbg_trace(struct wil6210_priv *wil, const char *fmt, ...)
vaf.va = &args;
trace_wil6210_log_dbg(&vaf);
va_end(args);
-
- return 0;
}
diff --git a/drivers/net/wireless/ath/wil6210/debugfs.c b/drivers/net/wireless/ath/wil6210/debugfs.c
index 8f66186..54a6ddc 100644
--- a/drivers/net/wireless/ath/wil6210/debugfs.c
+++ b/drivers/net/wireless/ath/wil6210/debugfs.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012 Qualcomm Atheros, Inc.
+ * Copyright (c) 2012-2014 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
@@ -22,6 +22,7 @@
#include <linux/power_supply.h>
#include "wil6210.h"
+#include "wmi.h"
#include "txrx.h"
/* Nasty hack. Better have per device instances */
@@ -29,6 +30,21 @@ static u32 mem_addr;
static u32 dbg_txdesc_index;
static u32 dbg_vring_index; /* 24+ for Rx, 0..23 for Tx */
+enum dbg_off_type {
+ doff_u32 = 0,
+ doff_x32 = 1,
+ doff_ulong = 2,
+ doff_io32 = 3,
+};
+
+/* offset to "wil" */
+struct dbg_off {
+ const char *name;
+ umode_t mode;
+ ulong off;
+ enum dbg_off_type type;
+};
+
static void wil_print_vring(struct seq_file *s, struct wil6210_priv *wil,
const char *name, struct vring *vring,
char _s, char _h)
@@ -45,20 +61,22 @@ static void wil_print_vring(struct seq_file *s, struct wil6210_priv *wil,
if (x)
seq_printf(s, "0x%08x\n", ioread32(x));
else
- seq_printf(s, "???\n");
+ seq_puts(s, "???\n");
if (vring->va && (vring->size < 1025)) {
uint i;
+
for (i = 0; i < vring->size; i++) {
volatile struct vring_tx_desc *d = &vring->va[i].tx;
+
if ((i % 64) == 0 && (i != 0))
- seq_printf(s, "\n");
+ seq_puts(s, "\n");
seq_printf(s, "%c", (d->dma.status & BIT(0)) ?
_s : (vring->ctx[i].skb ? _h : 'h'));
}
- seq_printf(s, "\n");
+ seq_puts(s, "\n");
}
- seq_printf(s, "}\n");
+ seq_puts(s, "}\n");
}
static int wil_vring_debugfs_show(struct seq_file *s, void *data)
@@ -69,7 +87,7 @@ static int wil_vring_debugfs_show(struct seq_file *s, void *data)
wil_print_vring(s, wil, "rx", &wil->vring_rx, 'S', '_');
for (i = 0; i < ARRAY_SIZE(wil->vring_tx); i++) {
- struct vring *vring = &(wil->vring_tx[i]);
+ struct vring *vring = &wil->vring_tx[i];
struct vring_tx_data *txdata = &wil->vring_tx_data[i];
if (vring->va) {
@@ -147,7 +165,7 @@ static void wil_print_ring(struct seq_file *s, const char *prefix,
if (!wmi_addr(wil, r.base) ||
!wmi_addr(wil, r.tail) ||
!wmi_addr(wil, r.head)) {
- seq_printf(s, " ??? pointers are garbage?\n");
+ seq_puts(s, " ??? pointers are garbage?\n");
goto out;
}
@@ -166,6 +184,7 @@ static void wil_print_ring(struct seq_file *s, const char *prefix,
le32_to_cpu(d.addr));
if (0 == wmi_read_hdr(wil, d.addr, &hdr)) {
u16 len = le16_to_cpu(hdr.len);
+
seq_printf(s, " -> %04x %04x %04x %02x\n",
le16_to_cpu(hdr.seq), len,
le16_to_cpu(hdr.type), hdr.flags);
@@ -183,6 +202,7 @@ static void wil_print_ring(struct seq_file *s, const char *prefix,
wil_memcpy_fromio_32(databuf, src, len);
while (n < len) {
int l = min(len - n, 16);
+
hex_dump_to_buffer(databuf + n, l,
16, 1, printbuf,
sizeof(printbuf),
@@ -192,11 +212,11 @@ static void wil_print_ring(struct seq_file *s, const char *prefix,
}
}
} else {
- seq_printf(s, "\n");
+ seq_puts(s, "\n");
}
}
out:
- seq_printf(s, "}\n");
+ seq_puts(s, "}\n");
}
static int wil_mbox_debugfs_show(struct seq_file *s, void *data)
@@ -244,9 +264,9 @@ DEFINE_SIMPLE_ATTRIBUTE(fops_iomem_x32, wil_debugfs_iomem_x32_get,
static struct dentry *wil_debugfs_create_iomem_x32(const char *name,
umode_t mode,
struct dentry *parent,
- void __iomem *value)
+ void *value)
{
- return debugfs_create_file(name, mode, parent, (void * __force)value,
+ return debugfs_create_file(name, mode, parent, value,
&fops_iomem_x32);
}
@@ -255,11 +275,13 @@ static int wil_debugfs_ulong_set(void *data, u64 val)
*(ulong *)data = val;
return 0;
}
+
static int wil_debugfs_ulong_get(void *data, u64 *val)
{
*val = *(ulong *)data;
return 0;
}
+
DEFINE_SIMPLE_ATTRIBUTE(wil_fops_ulong, wil_debugfs_ulong_get,
wil_debugfs_ulong_set, "%llu\n");
@@ -270,6 +292,62 @@ static struct dentry *wil_debugfs_create_ulong(const char *name, umode_t mode,
return debugfs_create_file(name, mode, parent, value, &wil_fops_ulong);
}
+/**
+ * wil6210_debugfs_init_offset - create set of debugfs files
+ * @wil - driver's context, used for printing
+ * @dbg - directory on the debugfs, where files will be created
+ * @base - base address used in address calculation
+ * @tbl - table with file descriptions. Should be terminated with empty element.
+ *
+ * Creates files accordingly to the @tbl.
+ */
+static void wil6210_debugfs_init_offset(struct wil6210_priv *wil,
+ struct dentry *dbg, void *base,
+ const struct dbg_off * const tbl)
+{
+ int i;
+
+ for (i = 0; tbl[i].name; i++) {
+ struct dentry *f;
+
+ switch (tbl[i].type) {
+ case doff_u32:
+ f = debugfs_create_u32(tbl[i].name, tbl[i].mode, dbg,
+ base + tbl[i].off);
+ break;
+ case doff_x32:
+ f = debugfs_create_x32(tbl[i].name, tbl[i].mode, dbg,
+ base + tbl[i].off);
+ break;
+ case doff_ulong:
+ f = wil_debugfs_create_ulong(tbl[i].name, tbl[i].mode,
+ dbg, base + tbl[i].off);
+ break;
+ case doff_io32:
+ f = wil_debugfs_create_iomem_x32(tbl[i].name,
+ tbl[i].mode, dbg,
+ base + tbl[i].off);
+ break;
+ default:
+ f = ERR_PTR(-EINVAL);
+ }
+ if (IS_ERR_OR_NULL(f))
+ wil_err(wil, "Create file \"%s\": err %ld\n",
+ tbl[i].name, PTR_ERR(f));
+ }
+}
+
+static const struct dbg_off isr_off[] = {
+ {"ICC", S_IRUGO | S_IWUSR, offsetof(struct RGF_ICR, ICC), doff_io32},
+ {"ICR", S_IRUGO | S_IWUSR, offsetof(struct RGF_ICR, ICR), doff_io32},
+ {"ICM", S_IRUGO | S_IWUSR, offsetof(struct RGF_ICR, ICM), doff_io32},
+ {"ICS", S_IWUSR, offsetof(struct RGF_ICR, ICS), doff_io32},
+ {"IMV", S_IRUGO | S_IWUSR, offsetof(struct RGF_ICR, IMV), doff_io32},
+ {"IMS", S_IWUSR, offsetof(struct RGF_ICR, IMS), doff_io32},
+ {"IMC", S_IWUSR, offsetof(struct RGF_ICR, IMC), doff_io32},
+ {},
+};
+
static int wil6210_debugfs_create_ISR(struct wil6210_priv *wil,
const char *name,
struct dentry *parent, u32 off)
@@ -279,24 +357,19 @@ static int wil6210_debugfs_create_ISR(struct wil6210_priv *wil,
if (IS_ERR_OR_NULL(d))
return -ENODEV;
- wil_debugfs_create_iomem_x32("ICC", S_IRUGO | S_IWUSR, d,
- wil->csr + off);
- wil_debugfs_create_iomem_x32("ICR", S_IRUGO | S_IWUSR, d,
- wil->csr + off + 4);
- wil_debugfs_create_iomem_x32("ICM", S_IRUGO | S_IWUSR, d,
- wil->csr + off + 8);
- wil_debugfs_create_iomem_x32("ICS", S_IWUSR, d,
- wil->csr + off + 12);
- wil_debugfs_create_iomem_x32("IMV", S_IRUGO | S_IWUSR, d,
- wil->csr + off + 16);
- wil_debugfs_create_iomem_x32("IMS", S_IWUSR, d,
- wil->csr + off + 20);
- wil_debugfs_create_iomem_x32("IMC", S_IWUSR, d,
- wil->csr + off + 24);
+ wil6210_debugfs_init_offset(wil, d, (void * __force)wil->csr + off,
+ isr_off);
return 0;
}
+static const struct dbg_off pseudo_isr_off[] = {
+ {"CAUSE", S_IRUGO, HOSTADDR(RGF_DMA_PSEUDO_CAUSE), doff_io32},
+ {"MASK_SW", S_IRUGO, HOSTADDR(RGF_DMA_PSEUDO_CAUSE_MASK_SW), doff_io32},
+ {"MASK_FW", S_IRUGO, HOSTADDR(RGF_DMA_PSEUDO_CAUSE_MASK_FW), doff_io32},
+ {},
+};
+
static int wil6210_debugfs_create_pseudo_ISR(struct wil6210_priv *wil,
struct dentry *parent)
{
@@ -305,16 +378,19 @@ static int wil6210_debugfs_create_pseudo_ISR(struct wil6210_priv *wil,
if (IS_ERR_OR_NULL(d))
return -ENODEV;
- wil_debugfs_create_iomem_x32("CAUSE", S_IRUGO, d, wil->csr +
- HOSTADDR(RGF_DMA_PSEUDO_CAUSE));
- wil_debugfs_create_iomem_x32("MASK_SW", S_IRUGO, d, wil->csr +
- HOSTADDR(RGF_DMA_PSEUDO_CAUSE_MASK_SW));
- wil_debugfs_create_iomem_x32("MASK_FW", S_IRUGO, d, wil->csr +
- HOSTADDR(RGF_DMA_PSEUDO_CAUSE_MASK_FW));
+ wil6210_debugfs_init_offset(wil, d, (void * __force)wil->csr,
+ pseudo_isr_off);
return 0;
}
+static const struct dbg_off itr_cnt_off[] = {
+ {"TRSH", S_IRUGO | S_IWUSR, HOSTADDR(RGF_DMA_ITR_CNT_TRSH), doff_io32},
+ {"DATA", S_IRUGO | S_IWUSR, HOSTADDR(RGF_DMA_ITR_CNT_DATA), doff_io32},
+ {"CTL", S_IRUGO | S_IWUSR, HOSTADDR(RGF_DMA_ITR_CNT_CRL), doff_io32},
+ {},
+};
+
static int wil6210_debugfs_create_ITR_CNT(struct wil6210_priv *wil,
struct dentry *parent)
{
@@ -323,12 +399,8 @@ static int wil6210_debugfs_create_ITR_CNT(struct wil6210_priv *wil,
if (IS_ERR_OR_NULL(d))
return -ENODEV;
- wil_debugfs_create_iomem_x32("TRSH", S_IRUGO | S_IWUSR, d, wil->csr +
- HOSTADDR(RGF_DMA_ITR_CNT_TRSH));
- wil_debugfs_create_iomem_x32("DATA", S_IRUGO | S_IWUSR, d, wil->csr +
- HOSTADDR(RGF_DMA_ITR_CNT_DATA));
- wil_debugfs_create_iomem_x32("CTL", S_IRUGO | S_IWUSR, d, wil->csr +
- HOSTADDR(RGF_DMA_ITR_CNT_CRL));
+ wil6210_debugfs_init_offset(wil, d, (void * __force)wil->csr,
+ itr_cnt_off);
return 0;
}
@@ -359,7 +431,7 @@ static const struct file_operations fops_memread = {
};
static ssize_t wil_read_file_ioblob(struct file *file, char __user *user_buf,
- size_t count, loff_t *ppos)
+ size_t count, loff_t *ppos)
{
enum { max_count = 4096 };
struct debugfs_blob_wrapper *blob = file->private_data;
@@ -411,6 +483,7 @@ struct dentry *wil_debugfs_create_ioblob(const char *name,
{
return debugfs_create_file(name, mode, parent, blob, &fops_ioblob);
}
+
/*---reset---*/
static ssize_t wil_write_file_reset(struct file *file, const char __user *buf,
size_t len, loff_t *ppos)
@@ -436,6 +509,7 @@ static const struct file_operations fops_reset = {
.write = wil_write_file_reset,
.open = simple_open,
};
+
/*---write channel 1..4 to rxon for it, 0 to rxoff---*/
static ssize_t wil_write_file_rxon(struct file *file, const char __user *buf,
size_t len, loff_t *ppos)
@@ -446,6 +520,7 @@ static ssize_t wil_write_file_rxon(struct file *file, const char __user *buf,
bool on;
char *kbuf = kmalloc(len + 1, GFP_KERNEL);
+
if (!kbuf)
return -ENOMEM;
if (copy_from_user(kbuf, buf, len)) {
@@ -482,6 +557,7 @@ static const struct file_operations fops_rxon = {
.write = wil_write_file_rxon,
.open = simple_open,
};
+
/*---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,
@@ -492,8 +568,8 @@ static ssize_t wil_write_file_txmgmt(struct file *file, const char __user *buf,
struct wireless_dev *wdev = wil_to_wdev(wil);
struct cfg80211_mgmt_tx_params params;
int rc;
-
void *frame = kmalloc(len, GFP_KERNEL);
+
if (!frame)
return -ENOMEM;
@@ -562,8 +638,10 @@ static void wil_seq_hexdump(struct seq_file *s, void *p, int len,
{
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);
@@ -601,10 +679,8 @@ static int wil_txdesc_debugfs_show(struct seq_file *s, void *data)
struct wil6210_priv *wil = s->private;
struct vring *vring;
bool tx = (dbg_vring_index < WIL6210_MAX_TX_RINGS);
- if (tx)
- vring = &(wil->vring_tx[dbg_vring_index]);
- else
- vring = &wil->vring_rx;
+
+ vring = tx ? &wil->vring_tx[dbg_vring_index] : &wil->vring_rx;
if (!vring->va) {
if (tx)
@@ -619,7 +695,7 @@ static int wil_txdesc_debugfs_show(struct seq_file *s, void *data)
* only field used, .dma.length, is the same
*/
volatile struct vring_tx_desc *d =
- &(vring->va[dbg_txdesc_index].tx);
+ &vring->va[dbg_txdesc_index].tx;
volatile u32 *u = (volatile u32 *)d;
struct sk_buff *skb = vring->ctx[dbg_txdesc_index].skb;
@@ -639,7 +715,7 @@ static int wil_txdesc_debugfs_show(struct seq_file *s, void *data)
wil_seq_print_skb(s, skb);
kfree_skb(skb);
}
- seq_printf(s, "}\n");
+ seq_puts(s, "}\n");
} else {
if (tx)
seq_printf(s, "[%2d] TxDesc index (%d) >= size (%d)\n",
@@ -666,16 +742,79 @@ static const struct file_operations fops_txdesc = {
};
/*---------beamforming------------*/
+static char *wil_bfstatus_str(u32 status)
+{
+ switch (status) {
+ case 0:
+ return "Failed";
+ case 1:
+ return "OK";
+ case 2:
+ return "Retrying";
+ default:
+ return "??";
+ }
+}
+
+static bool is_all_zeros(void * const x_, size_t sz)
+{
+ /* if reply is all-0, ignore this CID */
+ u32 *x = x_;
+ int n;
+
+ for (n = 0; n < sz / sizeof(*x); n++)
+ if (x[n])
+ return false;
+
+ return true;
+}
+
static int wil_bf_debugfs_show(struct seq_file *s, void *data)
{
+ int rc;
+ int i;
struct wil6210_priv *wil = s->private;
- seq_printf(s,
- "TSF : 0x%016llx\n"
- "TxMCS : %d\n"
- "Sectors(rx:tx) my %2d:%2d peer %2d:%2d\n",
- wil->stats.tsf, wil->stats.bf_mcs,
- wil->stats.my_rx_sector, wil->stats.my_tx_sector,
- wil->stats.peer_rx_sector, wil->stats.peer_tx_sector);
+ struct wmi_notify_req_cmd cmd = {
+ .interval_usec = 0,
+ };
+ struct {
+ struct wil6210_mbox_hdr_wmi wmi;
+ struct wmi_notify_req_done_event evt;
+ } __packed reply;
+
+ for (i = 0; i < ARRAY_SIZE(wil->sta); i++) {
+ u32 status;
+
+ cmd.cid = i;
+ rc = wmi_call(wil, WMI_NOTIFY_REQ_CMDID, &cmd, sizeof(cmd),
+ WMI_NOTIFY_REQ_DONE_EVENTID, &reply,
+ sizeof(reply), 20);
+ /* if reply is all-0, ignore this CID */
+ if (rc || is_all_zeros(&reply.evt, sizeof(reply.evt)))
+ continue;
+
+ status = le32_to_cpu(reply.evt.status);
+ seq_printf(s, "CID %d {\n"
+ " TSF = 0x%016llx\n"
+ " TxMCS = %2d TxTpt = %4d\n"
+ " SQI = %4d\n"
+ " Status = 0x%08x %s\n"
+ " Sectors(rx:tx) my %2d:%2d peer %2d:%2d\n"
+ " Goodput(rx:tx) %4d:%4d\n"
+ "}\n",
+ i,
+ le64_to_cpu(reply.evt.tsf),
+ le16_to_cpu(reply.evt.bf_mcs),
+ le32_to_cpu(reply.evt.tx_tpt),
+ reply.evt.sqi,
+ status, wil_bfstatus_str(status),
+ le16_to_cpu(reply.evt.my_rx_sector),
+ le16_to_cpu(reply.evt.my_tx_sector),
+ le16_to_cpu(reply.evt.other_rx_sector),
+ le16_to_cpu(reply.evt.other_tx_sector),
+ le32_to_cpu(reply.evt.rx_goodput),
+ le32_to_cpu(reply.evt.tx_goodput));
+ }
return 0;
}
@@ -690,6 +829,7 @@ static const struct file_operations fops_bf = {
.read = seq_read,
.llseek = seq_lseek,
};
+
/*---------SSID------------*/
static ssize_t wil_read_file_ssid(struct file *file, char __user *user_buf,
size_t count, loff_t *ppos)
@@ -752,10 +892,10 @@ static int wil_temp_debugfs_show(struct seq_file *s, void *data)
{
struct wil6210_priv *wil = s->private;
u32 t_m, t_r;
-
int rc = wmi_get_temperature(wil, &t_m, &t_r);
+
if (rc) {
- seq_printf(s, "Failed\n");
+ seq_puts(s, "Failed\n");
return 0;
}
@@ -811,6 +951,7 @@ static int wil_link_debugfs_show(struct seq_file *s, void *data)
for (i = 0; i < ARRAY_SIZE(wil->sta); i++) {
struct wil_sta_info *p = &wil->sta[i];
char *status = "unknown";
+
switch (p->status) {
case wil_sta_unused:
status = "unused ";
@@ -871,7 +1012,6 @@ static int wil_info_debugfs_show(struct seq_file *s, void *data)
rxf_old = rxf;
txf_old = txf;
-
#define CHECK_QSTATE(x) (state & BIT(__QUEUE_STATE_ ## x)) ? \
" " __stringify(x) : ""
@@ -901,11 +1041,77 @@ static const struct file_operations fops_info = {
.llseek = seq_lseek,
};
+/*---------recovery------------*/
+/* mode = [manual|auto]
+ * state = [idle|pending|running]
+ */
+static ssize_t wil_read_file_recovery(struct file *file, char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct wil6210_priv *wil = file->private_data;
+ char buf[80];
+ int n;
+ static const char * const sstate[] = {"idle", "pending", "running"};
+
+ n = snprintf(buf, sizeof(buf), "mode = %s\nstate = %s\n",
+ no_fw_recovery ? "manual" : "auto",
+ sstate[wil->recovery_state]);
+
+ n = min_t(int, n, sizeof(buf));
+
+ return simple_read_from_buffer(user_buf, count, ppos,
+ buf, n);
+}
+
+static ssize_t wil_write_file_recovery(struct file *file,
+ const char __user *buf_,
+ size_t count, loff_t *ppos)
+{
+ struct wil6210_priv *wil = file->private_data;
+ static const char run_command[] = "run";
+ char buf[sizeof(run_command) + 1]; /* to detect "runx" */
+ ssize_t rc;
+
+ if (wil->recovery_state != fw_recovery_pending) {
+ wil_err(wil, "No recovery pending\n");
+ return -EINVAL;
+ }
+
+ if (*ppos != 0) {
+ wil_err(wil, "Offset [%d]\n", (int)*ppos);
+ return -EINVAL;
+ }
+
+ if (count > sizeof(buf)) {
+ wil_err(wil, "Input too long, len = %d\n", (int)count);
+ return -EINVAL;
+ }
+
+ rc = simple_write_to_buffer(buf, sizeof(buf) - 1, ppos, buf_, count);
+ if (rc < 0)
+ return rc;
+
+ buf[rc] = '\0';
+ if (0 == strcmp(buf, run_command))
+ wil_set_recovery_state(wil, fw_recovery_running);
+ else
+ wil_err(wil, "Bad recovery command \"%s\"\n", buf);
+
+ return rc;
+}
+
+static const struct file_operations fops_recovery = {
+ .read = wil_read_file_recovery,
+ .write = wil_write_file_recovery,
+ .open = simple_open,
+};
+
/*---------Station matrix------------*/
static void wil_print_rxtid(struct seq_file *s, struct wil_tid_ampdu_rx *r)
{
int i;
u16 index = ((r->head_seq_num - r->ssn) & 0xfff) % r->buf_size;
+
seq_printf(s, "0x%03x [", r->head_seq_num);
for (i = 0; i < r->buf_size; i++) {
if (i == index)
@@ -920,10 +1126,12 @@ static int wil_sta_debugfs_show(struct seq_file *s, void *data)
{
struct wil6210_priv *wil = s->private;
int i, tid;
+ unsigned long flags;
for (i = 0; i < ARRAY_SIZE(wil->sta); i++) {
struct wil_sta_info *p = &wil->sta[i];
char *status = "unknown";
+
switch (p->status) {
case wil_sta_unused:
status = "unused ";
@@ -939,13 +1147,16 @@ static int wil_sta_debugfs_show(struct seq_file *s, void *data)
(p->data_port_open ? " data_port_open" : ""));
if (p->status == wil_sta_connected) {
+ spin_lock_irqsave(&p->tid_rx_lock, flags);
for (tid = 0; tid < WIL_STA_TID_NUM; tid++) {
struct wil_tid_ampdu_rx *r = p->tid_rx[tid];
+
if (r) {
seq_printf(s, "[%2d] ", tid);
wil_print_rxtid(s, r);
}
}
+ spin_unlock_irqrestore(&p->tid_rx_lock, flags);
}
}
@@ -985,6 +1196,89 @@ static void wil6210_debugfs_init_blobs(struct wil6210_priv *wil,
}
}
+/* misc files */
+static const struct {
+ const char *name;
+ umode_t mode;
+ const struct file_operations *fops;
+} dbg_files[] = {
+ {"mbox", S_IRUGO, &fops_mbox},
+ {"vrings", S_IRUGO, &fops_vring},
+ {"stations", S_IRUGO, &fops_sta},
+ {"desc", S_IRUGO, &fops_txdesc},
+ {"bf", S_IRUGO, &fops_bf},
+ {"ssid", S_IRUGO | S_IWUSR, &fops_ssid},
+ {"mem_val", S_IRUGO, &fops_memread},
+ {"reset", S_IWUSR, &fops_reset},
+ {"rxon", S_IWUSR, &fops_rxon},
+ {"tx_mgmt", S_IWUSR, &fops_txmgmt},
+ {"wmi_send", S_IWUSR, &fops_wmi},
+ {"temp", S_IRUGO, &fops_temp},
+ {"freq", S_IRUGO, &fops_freq},
+ {"link", S_IRUGO, &fops_link},
+ {"info", S_IRUGO, &fops_info},
+ {"recovery", S_IRUGO | S_IWUSR, &fops_recovery},
+};
+
+static void wil6210_debugfs_init_files(struct wil6210_priv *wil,
+ struct dentry *dbg)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(dbg_files); i++)
+ debugfs_create_file(dbg_files[i].name, dbg_files[i].mode, dbg,
+ wil, dbg_files[i].fops);
+}
+
+/* interrupt control blocks */
+static const struct {
+ const char *name;
+ u32 icr_off;
+} dbg_icr[] = {
+ {"USER_ICR", HOSTADDR(RGF_USER_USER_ICR)},
+ {"DMA_EP_TX_ICR", HOSTADDR(RGF_DMA_EP_TX_ICR)},
+ {"DMA_EP_RX_ICR", HOSTADDR(RGF_DMA_EP_RX_ICR)},
+ {"DMA_EP_MISC_ICR", HOSTADDR(RGF_DMA_EP_MISC_ICR)},
+};
+
+static void wil6210_debugfs_init_isr(struct wil6210_priv *wil,
+ struct dentry *dbg)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(dbg_icr); i++)
+ wil6210_debugfs_create_ISR(wil, dbg_icr[i].name, dbg,
+ dbg_icr[i].icr_off);
+}
+
+#define WIL_FIELD(name, mode, type) { __stringify(name), mode, \
+ offsetof(struct wil6210_priv, name), type}
+
+/* fields in struct wil6210_priv */
+static const struct dbg_off dbg_wil_off[] = {
+ WIL_FIELD(secure_pcp, S_IRUGO | S_IWUSR, doff_u32),
+ WIL_FIELD(status, S_IRUGO | S_IWUSR, doff_ulong),
+ WIL_FIELD(fw_version, S_IRUGO, doff_u32),
+ WIL_FIELD(hw_version, S_IRUGO, doff_x32),
+ WIL_FIELD(recovery_count, S_IRUGO, doff_u32),
+ {},
+};
+
+static const struct dbg_off dbg_wil_regs[] = {
+ {"RGF_MAC_MTRL_COUNTER_0", S_IRUGO, HOSTADDR(RGF_MAC_MTRL_COUNTER_0),
+ doff_io32},
+ {"RGF_USER_USAGE_1", S_IRUGO, HOSTADDR(RGF_USER_USAGE_1), doff_io32},
+ {},
+};
+
+/* static parameters */
+static const struct dbg_off dbg_statics[] = {
+ {"desc_index", S_IRUGO | S_IWUSR, (ulong)&dbg_txdesc_index, doff_u32},
+ {"vring_index", S_IRUGO | S_IWUSR, (ulong)&dbg_vring_index, doff_u32},
+ {"mem_addr", S_IRUGO | S_IWUSR, (ulong)&mem_addr, doff_u32},
+ {},
+};
+
int wil6210_debugfs_init(struct wil6210_priv *wil)
{
struct dentry *dbg = wil->debug = debugfs_create_dir(WIL_NAME,
@@ -993,51 +1287,17 @@ int wil6210_debugfs_init(struct wil6210_priv *wil)
if (IS_ERR_OR_NULL(dbg))
return -ENODEV;
- debugfs_create_file("mbox", S_IRUGO, dbg, wil, &fops_mbox);
- debugfs_create_file("vrings", S_IRUGO, dbg, wil, &fops_vring);
- debugfs_create_file("stations", S_IRUGO, dbg, wil, &fops_sta);
- debugfs_create_file("desc", S_IRUGO, dbg, wil, &fops_txdesc);
- debugfs_create_u32("desc_index", S_IRUGO | S_IWUSR, dbg,
- &dbg_txdesc_index);
- debugfs_create_u32("vring_index", S_IRUGO | S_IWUSR, dbg,
- &dbg_vring_index);
-
- debugfs_create_file("bf", S_IRUGO, dbg, wil, &fops_bf);
- debugfs_create_file("ssid", S_IRUGO | S_IWUSR, dbg, wil, &fops_ssid);
- debugfs_create_u32("secure_pcp", S_IRUGO | S_IWUSR, dbg,
- &wil->secure_pcp);
- wil_debugfs_create_ulong("status", S_IRUGO | S_IWUSR, dbg,
- &wil->status);
- debugfs_create_u32("fw_version", S_IRUGO, dbg, &wil->fw_version);
- debugfs_create_x32("hw_version", S_IRUGO, dbg, &wil->hw_version);
-
- wil6210_debugfs_create_ISR(wil, "USER_ICR", dbg,
- HOSTADDR(RGF_USER_USER_ICR));
- wil6210_debugfs_create_ISR(wil, "DMA_EP_TX_ICR", dbg,
- HOSTADDR(RGF_DMA_EP_TX_ICR));
- wil6210_debugfs_create_ISR(wil, "DMA_EP_RX_ICR", dbg,
- HOSTADDR(RGF_DMA_EP_RX_ICR));
- wil6210_debugfs_create_ISR(wil, "DMA_EP_MISC_ICR", dbg,
- HOSTADDR(RGF_DMA_EP_MISC_ICR));
- wil6210_debugfs_create_pseudo_ISR(wil, dbg);
- wil6210_debugfs_create_ITR_CNT(wil, dbg);
+ wil6210_debugfs_init_files(wil, dbg);
+ wil6210_debugfs_init_isr(wil, dbg);
+ wil6210_debugfs_init_blobs(wil, dbg);
+ wil6210_debugfs_init_offset(wil, dbg, wil, dbg_wil_off);
+ wil6210_debugfs_init_offset(wil, dbg, (void * __force)wil->csr,
+ dbg_wil_regs);
+ wil6210_debugfs_init_offset(wil, dbg, NULL, dbg_statics);
- wil_debugfs_create_iomem_x32("RGF_USER_USAGE_1", S_IRUGO, dbg,
- wil->csr +
- HOSTADDR(RGF_USER_USAGE_1));
- debugfs_create_u32("mem_addr", S_IRUGO | S_IWUSR, dbg, &mem_addr);
- debugfs_create_file("mem_val", S_IRUGO, dbg, wil, &fops_memread);
-
- debugfs_create_file("reset", S_IWUSR, dbg, wil, &fops_reset);
- debugfs_create_file("rxon", S_IWUSR, dbg, wil, &fops_rxon);
- debugfs_create_file("tx_mgmt", S_IWUSR, dbg, wil, &fops_txmgmt);
- debugfs_create_file("wmi_send", S_IWUSR, dbg, wil, &fops_wmi);
- debugfs_create_file("temp", S_IRUGO, dbg, wil, &fops_temp);
- debugfs_create_file("freq", S_IRUGO, dbg, wil, &fops_freq);
- debugfs_create_file("link", S_IRUGO, dbg, wil, &fops_link);
- debugfs_create_file("info", S_IRUGO, dbg, wil, &fops_info);
+ wil6210_debugfs_create_pseudo_ISR(wil, dbg);
- wil6210_debugfs_init_blobs(wil, dbg);
+ wil6210_debugfs_create_ITR_CNT(wil, dbg);
return 0;
}
diff --git a/drivers/net/wireless/ath/wil6210/ethtool.c b/drivers/net/wireless/ath/wil6210/ethtool.c
new file mode 100644
index 0000000..d686638
--- /dev/null
+++ b/drivers/net/wireless/ath/wil6210/ethtool.c
@@ -0,0 +1,103 @@
+/*
+ * Copyright (c) 2014 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 <linux/etherdevice.h>
+#include <linux/pci.h>
+#include <linux/rtnetlink.h>
+#include <net/cfg80211.h>
+
+#include "wil6210.h"
+
+static int wil_ethtoolops_begin(struct net_device *ndev)
+{
+ struct wil6210_priv *wil = ndev_to_wil(ndev);
+
+ mutex_lock(&wil->mutex);
+
+ wil_dbg_misc(wil, "%s()\n", __func__);
+
+ return 0;
+}
+
+static void wil_ethtoolops_complete(struct net_device *ndev)
+{
+ struct wil6210_priv *wil = ndev_to_wil(ndev);
+
+ wil_dbg_misc(wil, "%s()\n", __func__);
+
+ mutex_unlock(&wil->mutex);
+}
+
+static int wil_ethtoolops_get_coalesce(struct net_device *ndev,
+ struct ethtool_coalesce *cp)
+{
+ struct wil6210_priv *wil = ndev_to_wil(ndev);
+ u32 itr_en, itr_val = 0;
+
+ wil_dbg_misc(wil, "%s()\n", __func__);
+
+ itr_en = ioread32(wil->csr + HOSTADDR(RGF_DMA_ITR_CNT_CRL));
+ if (itr_en & BIT_DMA_ITR_CNT_CRL_EN)
+ itr_val = ioread32(wil->csr + HOSTADDR(RGF_DMA_ITR_CNT_TRSH));
+
+ cp->rx_coalesce_usecs = itr_val;
+
+ return 0;
+}
+
+static int wil_ethtoolops_set_coalesce(struct net_device *ndev,
+ struct ethtool_coalesce *cp)
+{
+ struct wil6210_priv *wil = ndev_to_wil(ndev);
+
+ wil_dbg_misc(wil, "%s(%d usec)\n", __func__, cp->rx_coalesce_usecs);
+
+ if (wil->wdev->iftype == NL80211_IFTYPE_MONITOR) {
+ wil_dbg_misc(wil, "No IRQ coalescing in monitor mode\n");
+ return -EINVAL;
+ }
+
+ /* only @rx_coalesce_usecs supported, ignore
+ * other parameters
+ */
+
+ if (cp->rx_coalesce_usecs > WIL6210_ITR_TRSH_MAX)
+ goto out_bad;
+
+ wil->itr_trsh = cp->rx_coalesce_usecs;
+ wil_set_itr_trsh(wil);
+
+ return 0;
+
+out_bad:
+ wil_dbg_misc(wil, "Unsupported coalescing params. Raw command:\n");
+ print_hex_dump_debug("DBG[MISC] coal ", DUMP_PREFIX_OFFSET, 16, 4,
+ cp, sizeof(*cp), false);
+ return -EINVAL;
+}
+
+static const struct ethtool_ops wil_ethtool_ops = {
+ .begin = wil_ethtoolops_begin,
+ .complete = wil_ethtoolops_complete,
+ .get_drvinfo = cfg80211_get_drvinfo,
+ .get_coalesce = wil_ethtoolops_get_coalesce,
+ .set_coalesce = wil_ethtoolops_set_coalesce,
+};
+
+void wil_set_ethtoolops(struct net_device *ndev)
+{
+ ndev->ethtool_ops = &wil_ethtool_ops;
+}
diff --git a/drivers/net/wireless/ath/wil6210/fw.c b/drivers/net/wireless/ath/wil6210/fw.c
new file mode 100644
index 0000000..8c6f3b0
--- /dev/null
+++ b/drivers/net/wireless/ath/wil6210/fw.c
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2014 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 <linux/firmware.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/crc32.h>
+#include "wil6210.h"
+#include "fw.h"
+
+MODULE_FIRMWARE(WIL_FW_NAME);
+
+/* target operations */
+/* register read */
+#define R(a) ioread32(wil->csr + HOSTADDR(a))
+/* register write. wmb() to make sure it is completed */
+#define W(a, v) do { iowrite32(v, wil->csr + HOSTADDR(a)); wmb(); } while (0)
+/* register set = read, OR, write */
+#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)
+
+static
+void wil_memset_toio_32(volatile void __iomem *dst, u32 val,
+ size_t count)
+{
+ volatile u32 __iomem *d = dst;
+
+ for (count += 4; count > 4; count -= 4)
+ __raw_writel(val, d++);
+}
+
+#include "fw_inc.c"
diff --git a/drivers/net/wireless/ath/wil6210/fw.h b/drivers/net/wireless/ath/wil6210/fw.h
new file mode 100644
index 0000000..7a2c6c1
--- /dev/null
+++ b/drivers/net/wireless/ath/wil6210/fw.h
@@ -0,0 +1,149 @@
+/*
+ * Copyright (c) 2014 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.
+ */
+
+#define WIL_FW_SIGNATURE (0x36323130) /* '0126' */
+#define WIL_FW_FMT_VERSION (1) /* format version driver supports */
+
+enum wil_fw_record_type {
+ wil_fw_type_comment = 1,
+ wil_fw_type_data = 2,
+ wil_fw_type_fill = 3,
+ wil_fw_type_action = 4,
+ wil_fw_type_verify = 5,
+ wil_fw_type_file_header = 6,
+ wil_fw_type_direct_write = 7,
+ wil_fw_type_gateway_data = 8,
+ wil_fw_type_gateway_data4 = 9,
+};
+
+struct wil_fw_record_head {
+ __le16 type; /* enum wil_fw_record_type */
+ __le16 flags; /* to be defined */
+ __le32 size; /* whole record, bytes after head */
+} __packed;
+
+/* data block. write starting from @addr
+ * data_size inferred from the @head.size. For this case,
+ * data_size = @head.size - offsetof(struct wil_fw_record_data, data)
+ */
+struct wil_fw_record_data { /* type == wil_fw_type_data */
+ __le32 addr;
+ __le32 data[0]; /* [data_size], see above */
+} __packed;
+
+/* fill with constant @value, @size bytes starting from @addr */
+struct wil_fw_record_fill { /* type == wil_fw_type_fill */
+ __le32 addr;
+ __le32 value;
+ __le32 size;
+} __packed;
+
+/* free-form comment
+ * for informational purpose, data_size is @head.size from record header
+ */
+struct wil_fw_record_comment { /* type == wil_fw_type_comment */
+ u8 data[0]; /* free-form data [data_size], see above */
+} __packed;
+
+/* perform action
+ * data_size = @head.size - offsetof(struct wil_fw_record_action, data)
+ */
+struct wil_fw_record_action { /* type == wil_fw_type_action */
+ __le32 action; /* action to perform: reset, wait for fw ready etc. */
+ __le32 data[0]; /* action specific, [data_size], see above */
+} __packed;
+
+/* data block for struct wil_fw_record_direct_write */
+struct wil_fw_data_dwrite {
+ __le32 addr;
+ __le32 value;
+ __le32 mask;
+} __packed;
+
+/* write @value to the @addr,
+ * preserve original bits accordingly to the @mask
+ * data_size is @head.size where @head is record header
+ */
+struct wil_fw_record_direct_write { /* type == wil_fw_type_direct_write */
+ struct wil_fw_data_dwrite data[0];
+} __packed;
+
+/* verify condition: [@addr] & @mask == @value
+ * if condition not met, firmware download fails
+ */
+struct wil_fw_record_verify { /* type == wil_fw_verify */
+ __le32 addr; /* read from this address */
+ __le32 value; /* reference value */
+ __le32 mask; /* mask for verification */
+} __packed;
+
+/* file header
+ * First record of every file
+ */
+struct wil_fw_record_file_header {
+ __le32 signature ; /* Wilocity signature */
+ __le32 reserved;
+ __le32 crc; /* crc32 of the following data */
+ __le32 version; /* format version */
+ __le32 data_len; /* total data in file, including this record */
+ u8 comment[32]; /* short description */
+} __packed;
+
+/* 1-dword gateway */
+/* data block for the struct wil_fw_record_gateway_data */
+struct wil_fw_data_gw {
+ __le32 addr;
+ __le32 value;
+} __packed;
+
+/* gateway write block.
+ * write starting address and values from the data buffer
+ * through the gateway
+ * data_size inferred from the @head.size. For this case,
+ * data_size = @head.size - offsetof(struct wil_fw_record_gateway_data, data)
+ */
+struct wil_fw_record_gateway_data { /* type == wil_fw_type_gateway_data */
+ __le32 gateway_addr_addr;
+ __le32 gateway_value_addr;
+ __le32 gateway_cmd_addr;
+ __le32 gateway_ctrl_address;
+#define WIL_FW_GW_CTL_BUSY BIT(29) /* gateway busy performing operation */
+#define WIL_FW_GW_CTL_RUN BIT(30) /* start gateway operation */
+ __le32 command;
+ struct wil_fw_data_gw data[0]; /* total size [data_size], see above */
+} __packed;
+
+/* 4-dword gateway */
+/* data block for the struct wil_fw_record_gateway_data4 */
+struct wil_fw_data_gw4 {
+ __le32 addr;
+ __le32 value[4];
+} __packed;
+
+/* gateway write block.
+ * write starting address and values from the data buffer
+ * through the gateway
+ * data_size inferred from the @head.size. For this case,
+ * data_size = @head.size - offsetof(struct wil_fw_record_gateway_data4, data)
+ */
+struct wil_fw_record_gateway_data4 { /* type == wil_fw_type_gateway_data4 */
+ __le32 gateway_addr_addr;
+ __le32 gateway_value_addr[4];
+ __le32 gateway_cmd_addr;
+ __le32 gateway_ctrl_address; /* same logic as for 1-dword gw */
+ __le32 command;
+ struct wil_fw_data_gw4 data[0]; /* total size [data_size], see above */
+} __packed;
diff --git a/drivers/net/wireless/ath/wil6210/fw_inc.c b/drivers/net/wireless/ath/wil6210/fw_inc.c
new file mode 100644
index 0000000..44cb71f
--- /dev/null
+++ b/drivers/net/wireless/ath/wil6210/fw_inc.c
@@ -0,0 +1,495 @@
+/*
+ * Copyright (c) 2014 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.
+ */
+
+/* Algorithmic part of the firmware download.
+ * To be included in the container file providing framework
+ */
+
+#define wil_err_fw(wil, fmt, arg...) wil_err(wil, "ERR[ FW ]" fmt, ##arg)
+#define wil_dbg_fw(wil, fmt, arg...) wil_dbg(wil, "DBG[ FW ]" fmt, ##arg)
+#define wil_hex_dump_fw(prefix_str, prefix_type, rowsize, \
+ groupsize, buf, len, ascii) \
+ print_hex_dump_debug("DBG[ FW ]" prefix_str, \
+ prefix_type, rowsize, \
+ groupsize, buf, len, ascii)
+
+#define FW_ADDR_CHECK(ioaddr, val, msg) do { \
+ ioaddr = wmi_buffer(wil, val); \
+ if (!ioaddr) { \
+ wil_err_fw(wil, "bad " msg ": 0x%08x\n", \
+ le32_to_cpu(val)); \
+ return -EINVAL; \
+ } \
+ } while (0)
+
+/**
+ * wil_fw_verify - verify firmware file validity
+ *
+ * perform various checks for the firmware file header.
+ * records are not validated.
+ *
+ * Return file size or negative error
+ */
+static int wil_fw_verify(struct wil6210_priv *wil, const u8 *data, size_t size)
+{
+ const struct wil_fw_record_head *hdr = (const void *)data;
+ struct wil_fw_record_file_header fh;
+ const struct wil_fw_record_file_header *fh_;
+ u32 crc;
+ u32 dlen;
+
+ if (size % 4) {
+ wil_err_fw(wil, "image size not aligned: %zu\n", size);
+ return -EINVAL;
+ }
+ /* have enough data for the file header? */
+ if (size < sizeof(*hdr) + sizeof(fh)) {
+ wil_err_fw(wil, "file too short: %zu bytes\n", size);
+ return -EINVAL;
+ }
+
+ /* start with the file header? */
+ if (le16_to_cpu(hdr->type) != wil_fw_type_file_header) {
+ wil_err_fw(wil, "no file header\n");
+ return -EINVAL;
+ }
+
+ /* data_len */
+ fh_ = (struct wil_fw_record_file_header *)&hdr[1];
+ dlen = le32_to_cpu(fh_->data_len);
+ if (dlen % 4) {
+ wil_err_fw(wil, "data length not aligned: %lu\n", (ulong)dlen);
+ return -EINVAL;
+ }
+ if (size < dlen) {
+ wil_err_fw(wil, "file truncated at %zu/%lu\n",
+ size, (ulong)dlen);
+ return -EINVAL;
+ }
+ if (dlen < sizeof(*hdr) + sizeof(fh)) {
+ wil_err_fw(wil, "data length too short: %lu\n", (ulong)dlen);
+ return -EINVAL;
+ }
+
+ /* signature */
+ if (le32_to_cpu(fh_->signature) != WIL_FW_SIGNATURE) {
+ wil_err_fw(wil, "bad header signature: 0x%08x\n",
+ le32_to_cpu(fh_->signature));
+ return -EINVAL;
+ }
+
+ /* version */
+ if (le32_to_cpu(fh_->version) > WIL_FW_FMT_VERSION) {
+ wil_err_fw(wil, "unsupported header version: %d\n",
+ le32_to_cpu(fh_->version));
+ return -EINVAL;
+ }
+
+ /* checksum. ~crc32(~0, data, size) when fh.crc set to 0*/
+ fh = *fh_;
+ fh.crc = 0;
+
+ crc = crc32_le(~0, (unsigned char const *)hdr, sizeof(*hdr));
+ crc = crc32_le(crc, (unsigned char const *)&fh, sizeof(fh));
+ crc = crc32_le(crc, (unsigned char const *)&fh_[1],
+ dlen - sizeof(*hdr) - sizeof(fh));
+ crc = ~crc;
+
+ if (crc != le32_to_cpu(fh_->crc)) {
+ wil_err_fw(wil, "checksum mismatch:"
+ " calculated for %lu bytes 0x%08x != 0x%08x\n",
+ (ulong)dlen, crc, le32_to_cpu(fh_->crc));
+ return -EINVAL;
+ }
+
+ return (int)dlen;
+}
+
+static int fw_handle_comment(struct wil6210_priv *wil, const void *data,
+ size_t size)
+{
+ wil_hex_dump_fw("", DUMP_PREFIX_OFFSET, 16, 1, data, size, true);
+
+ return 0;
+}
+
+static int fw_handle_data(struct wil6210_priv *wil, const void *data,
+ size_t size)
+{
+ const struct wil_fw_record_data *d = data;
+ void __iomem *dst;
+ size_t s = size - sizeof(*d);
+
+ if (size < sizeof(*d) + sizeof(u32)) {
+ wil_err_fw(wil, "data record too short: %zu\n", size);
+ return -EINVAL;
+ }
+
+ FW_ADDR_CHECK(dst, d->addr, "address");
+ wil_dbg_fw(wil, "write [0x%08x] <== %zu bytes\n", le32_to_cpu(d->addr),
+ s);
+ wil_memcpy_toio_32(dst, d->data, s);
+ wmb(); /* finish before processing next record */
+
+ return 0;
+}
+
+static int fw_handle_fill(struct wil6210_priv *wil, const void *data,
+ size_t size)
+{
+ const struct wil_fw_record_fill *d = data;
+ void __iomem *dst;
+ u32 v;
+ size_t s = (size_t)le32_to_cpu(d->size);
+
+ if (size != sizeof(*d)) {
+ wil_err_fw(wil, "bad size for fill record: %zu\n", size);
+ return -EINVAL;
+ }
+
+ if (s < sizeof(u32)) {
+ wil_err_fw(wil, "fill size too short: %zu\n", s);
+ return -EINVAL;
+ }
+
+ if (s % sizeof(u32)) {
+ wil_err_fw(wil, "fill size not aligned: %zu\n", s);
+ return -EINVAL;
+ }
+
+ FW_ADDR_CHECK(dst, d->addr, "address");
+
+ v = le32_to_cpu(d->value);
+ wil_dbg_fw(wil, "fill [0x%08x] <== 0x%08x, %zu bytes\n",
+ le32_to_cpu(d->addr), v, s);
+ wil_memset_toio_32(dst, v, s);
+ wmb(); /* finish before processing next record */
+
+ return 0;
+}
+
+static int fw_handle_file_header(struct wil6210_priv *wil, const void *data,
+ size_t size)
+{
+ const struct wil_fw_record_file_header *d = data;
+
+ if (size != sizeof(*d)) {
+ wil_err_fw(wil, "file header length incorrect: %zu\n", size);
+ return -EINVAL;
+ }
+
+ wil_dbg_fw(wil, "new file, ver. %d, %i bytes\n",
+ d->version, d->data_len);
+ wil_hex_dump_fw("", DUMP_PREFIX_OFFSET, 16, 1, d->comment,
+ sizeof(d->comment), true);
+
+ return 0;
+}
+
+static int fw_handle_direct_write(struct wil6210_priv *wil, const void *data,
+ size_t size)
+{
+ const struct wil_fw_record_direct_write *d = data;
+ const struct wil_fw_data_dwrite *block = d->data;
+ int n, i;
+
+ if (size % sizeof(*block)) {
+ wil_err_fw(wil, "record size not aligned on %zu: %zu\n",
+ sizeof(*block), size);
+ return -EINVAL;
+ }
+ n = size / sizeof(*block);
+
+ for (i = 0; i < n; i++) {
+ void __iomem *dst;
+ u32 m = le32_to_cpu(block[i].mask);
+ u32 v = le32_to_cpu(block[i].value);
+ u32 x, y;
+
+ FW_ADDR_CHECK(dst, block[i].addr, "address");
+
+ x = ioread32(dst);
+ y = (x & m) | (v & ~m);
+ wil_dbg_fw(wil, "write [0x%08x] <== 0x%08x "
+ "(old 0x%08x val 0x%08x mask 0x%08x)\n",
+ le32_to_cpu(block[i].addr), y, x, v, m);
+ iowrite32(y, dst);
+ wmb(); /* finish before processing next record */
+ }
+
+ return 0;
+}
+
+static int gw_write(struct wil6210_priv *wil, void __iomem *gwa_addr,
+ void __iomem *gwa_cmd, void __iomem *gwa_ctl, u32 gw_cmd,
+ u32 a)
+{
+ unsigned delay = 0;
+
+ iowrite32(a, gwa_addr);
+ iowrite32(gw_cmd, gwa_cmd);
+ wmb(); /* finish before activate gw */
+
+ iowrite32(WIL_FW_GW_CTL_RUN, gwa_ctl); /* activate gw */
+ do {
+ udelay(1); /* typical time is few usec */
+ if (delay++ > 100) {
+ wil_err_fw(wil, "gw timeout\n");
+ return -EINVAL;
+ }
+ } while (ioread32(gwa_ctl) & WIL_FW_GW_CTL_BUSY); /* gw done? */
+
+ return 0;
+}
+
+static int fw_handle_gateway_data(struct wil6210_priv *wil, const void *data,
+ size_t size)
+{
+ const struct wil_fw_record_gateway_data *d = data;
+ const struct wil_fw_data_gw *block = d->data;
+ void __iomem *gwa_addr;
+ void __iomem *gwa_val;
+ void __iomem *gwa_cmd;
+ void __iomem *gwa_ctl;
+ u32 gw_cmd;
+ int n, i;
+
+ if (size < sizeof(*d) + sizeof(*block)) {
+ wil_err_fw(wil, "gateway record too short: %zu\n", size);
+ return -EINVAL;
+ }
+
+ if ((size - sizeof(*d)) % sizeof(*block)) {
+ wil_err_fw(wil, "gateway record data size"
+ " not aligned on %zu: %zu\n",
+ sizeof(*block), size - sizeof(*d));
+ return -EINVAL;
+ }
+ n = (size - sizeof(*d)) / sizeof(*block);
+
+ gw_cmd = le32_to_cpu(d->command);
+
+ wil_dbg_fw(wil, "gw write record [%3d] blocks, cmd 0x%08x\n",
+ n, gw_cmd);
+
+ FW_ADDR_CHECK(gwa_addr, d->gateway_addr_addr, "gateway_addr_addr");
+ FW_ADDR_CHECK(gwa_val, d->gateway_value_addr, "gateway_value_addr");
+ FW_ADDR_CHECK(gwa_cmd, d->gateway_cmd_addr, "gateway_cmd_addr");
+ FW_ADDR_CHECK(gwa_ctl, d->gateway_ctrl_address, "gateway_ctrl_address");
+
+ wil_dbg_fw(wil, "gw addresses: addr 0x%08x val 0x%08x"
+ " cmd 0x%08x ctl 0x%08x\n",
+ le32_to_cpu(d->gateway_addr_addr),
+ le32_to_cpu(d->gateway_value_addr),
+ le32_to_cpu(d->gateway_cmd_addr),
+ le32_to_cpu(d->gateway_ctrl_address));
+
+ for (i = 0; i < n; i++) {
+ int rc;
+ u32 a = le32_to_cpu(block[i].addr);
+ u32 v = le32_to_cpu(block[i].value);
+
+ wil_dbg_fw(wil, " gw write[%3d] [0x%08x] <== 0x%08x\n",
+ i, a, v);
+
+ iowrite32(v, gwa_val);
+ rc = gw_write(wil, gwa_addr, gwa_cmd, gwa_ctl, gw_cmd, a);
+ if (rc)
+ return rc;
+ }
+
+ return 0;
+}
+
+static int fw_handle_gateway_data4(struct wil6210_priv *wil, const void *data,
+ size_t size)
+{
+ const struct wil_fw_record_gateway_data4 *d = data;
+ const struct wil_fw_data_gw4 *block = d->data;
+ void __iomem *gwa_addr;
+ void __iomem *gwa_val[ARRAY_SIZE(block->value)];
+ void __iomem *gwa_cmd;
+ void __iomem *gwa_ctl;
+ u32 gw_cmd;
+ int n, i, k;
+
+ if (size < sizeof(*d) + sizeof(*block)) {
+ wil_err_fw(wil, "gateway4 record too short: %zu\n", size);
+ return -EINVAL;
+ }
+
+ if ((size - sizeof(*d)) % sizeof(*block)) {
+ wil_err_fw(wil, "gateway4 record data size"
+ " not aligned on %zu: %zu\n",
+ sizeof(*block), size - sizeof(*d));
+ return -EINVAL;
+ }
+ n = (size - sizeof(*d)) / sizeof(*block);
+
+ gw_cmd = le32_to_cpu(d->command);
+
+ wil_dbg_fw(wil, "gw4 write record [%3d] blocks, cmd 0x%08x\n",
+ n, gw_cmd);
+
+ FW_ADDR_CHECK(gwa_addr, d->gateway_addr_addr, "gateway_addr_addr");
+ for (k = 0; k < ARRAY_SIZE(block->value); k++)
+ FW_ADDR_CHECK(gwa_val[k], d->gateway_value_addr[k],
+ "gateway_value_addr");
+ FW_ADDR_CHECK(gwa_cmd, d->gateway_cmd_addr, "gateway_cmd_addr");
+ FW_ADDR_CHECK(gwa_ctl, d->gateway_ctrl_address, "gateway_ctrl_address");
+
+ wil_dbg_fw(wil, "gw4 addresses: addr 0x%08x cmd 0x%08x ctl 0x%08x\n",
+ le32_to_cpu(d->gateway_addr_addr),
+ le32_to_cpu(d->gateway_cmd_addr),
+ le32_to_cpu(d->gateway_ctrl_address));
+ wil_hex_dump_fw("val addresses: ", DUMP_PREFIX_NONE, 16, 4,
+ d->gateway_value_addr, sizeof(d->gateway_value_addr),
+ false);
+
+ for (i = 0; i < n; i++) {
+ int rc;
+ u32 a = le32_to_cpu(block[i].addr);
+ u32 v[ARRAY_SIZE(block->value)];
+
+ for (k = 0; k < ARRAY_SIZE(block->value); k++)
+ v[k] = le32_to_cpu(block[i].value[k]);
+
+ wil_dbg_fw(wil, " gw4 write[%3d] [0x%08x] <==\n", i, a);
+ wil_hex_dump_fw(" val ", DUMP_PREFIX_NONE, 16, 4, v,
+ sizeof(v), false);
+
+ for (k = 0; k < ARRAY_SIZE(block->value); k++)
+ iowrite32(v[k], gwa_val[k]);
+ rc = gw_write(wil, gwa_addr, gwa_cmd, gwa_ctl, gw_cmd, a);
+ if (rc)
+ return rc;
+ }
+
+ return 0;
+}
+
+static const struct {
+ int type;
+ int (*handler)(struct wil6210_priv *wil, const void *data, size_t size);
+} wil_fw_handlers[] = {
+ {wil_fw_type_comment, fw_handle_comment},
+ {wil_fw_type_data, fw_handle_data},
+ {wil_fw_type_fill, fw_handle_fill},
+ /* wil_fw_type_action */
+ /* wil_fw_type_verify */
+ {wil_fw_type_file_header, fw_handle_file_header},
+ {wil_fw_type_direct_write, fw_handle_direct_write},
+ {wil_fw_type_gateway_data, fw_handle_gateway_data},
+ {wil_fw_type_gateway_data4, fw_handle_gateway_data4},
+};
+
+static int wil_fw_handle_record(struct wil6210_priv *wil, int type,
+ const void *data, size_t size)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(wil_fw_handlers); i++) {
+ if (wil_fw_handlers[i].type == type)
+ return wil_fw_handlers[i].handler(wil, data, size);
+ }
+
+ wil_err_fw(wil, "unknown record type: %d\n", type);
+ return -EINVAL;
+}
+
+/**
+ * wil_fw_load - load FW into device
+ *
+ * Load the FW and uCode code and data to the corresponding device
+ * memory regions
+ *
+ * Return error code
+ */
+static int wil_fw_load(struct wil6210_priv *wil, const void *data, size_t size)
+{
+ int rc = 0;
+ const struct wil_fw_record_head *hdr;
+ size_t s, hdr_sz;
+
+ for (hdr = data;; hdr = (const void *)hdr + s, size -= s) {
+ if (size < sizeof(*hdr))
+ break;
+ hdr_sz = le32_to_cpu(hdr->size);
+ s = sizeof(*hdr) + hdr_sz;
+ if (s > size)
+ break;
+ if (hdr_sz % 4) {
+ wil_err_fw(wil, "unaligned record size: %zu\n",
+ hdr_sz);
+ return -EINVAL;
+ }
+ rc = wil_fw_handle_record(wil, le16_to_cpu(hdr->type),
+ &hdr[1], hdr_sz);
+ if (rc)
+ return rc;
+ }
+ if (size) {
+ wil_err_fw(wil, "unprocessed bytes: %zu\n", size);
+ if (size >= sizeof(*hdr)) {
+ wil_err_fw(wil, "Stop at offset %ld"
+ " record type %d [%zd bytes]\n",
+ (const void *)hdr - data,
+ le16_to_cpu(hdr->type), hdr_sz);
+ }
+ return -EINVAL;
+ }
+ /* Mark FW as loaded from host */
+ S(RGF_USER_USAGE_6, 1);
+
+ return rc;
+}
+
+/**
+ * wil_request_firmware - Request firmware and load to device
+ *
+ * Request firmware image from the file and load it to device
+ *
+ * Return error code
+ */
+int wil_request_firmware(struct wil6210_priv *wil, const char *name)
+{
+ int rc, rc1;
+ const struct firmware *fw;
+ size_t sz;
+ const void *d;
+
+ rc = request_firmware(&fw, name, wil_to_pcie_dev(wil));
+ if (rc) {
+ wil_err_fw(wil, "Failed to load firmware %s\n", name);
+ return rc;
+ }
+ wil_dbg_fw(wil, "Loading <%s>, %zu bytes\n", name, fw->size);
+
+ for (sz = fw->size, d = fw->data; sz; sz -= rc1, d += rc1) {
+ rc1 = wil_fw_verify(wil, d, sz);
+ if (rc1 < 0) {
+ rc = rc1;
+ goto out;
+ }
+ rc = wil_fw_load(wil, d, rc1);
+ if (rc < 0)
+ goto out;
+ }
+
+out:
+ release_firmware(fw);
+ return rc;
+}
diff --git a/drivers/net/wireless/ath/wil6210/interrupt.c b/drivers/net/wireless/ath/wil6210/interrupt.c
index 67f1002..90f416f 100644
--- a/drivers/net/wireless/ath/wil6210/interrupt.c
+++ b/drivers/net/wireless/ath/wil6210/interrupt.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012 Qualcomm Atheros, Inc.
+ * Copyright (c) 2012-2014 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
@@ -135,7 +135,7 @@ static void wil6210_unmask_irq_pseudo(struct wil6210_priv *wil)
HOSTADDR(RGF_DMA_PSEUDO_CAUSE_MASK_SW));
}
-void wil6210_disable_irq(struct wil6210_priv *wil)
+void wil_mask_irq(struct wil6210_priv *wil)
{
wil_dbg_irq(wil, "%s()\n", __func__);
@@ -145,7 +145,7 @@ void wil6210_disable_irq(struct wil6210_priv *wil)
wil6210_mask_irq_pseudo(wil);
}
-void wil6210_enable_irq(struct wil6210_priv *wil)
+void wil_unmask_irq(struct wil6210_priv *wil)
{
wil_dbg_irq(wil, "%s()\n", __func__);
@@ -157,17 +157,7 @@ void wil6210_enable_irq(struct wil6210_priv *wil)
offsetof(struct RGF_ICR, ICC));
/* interrupt moderation parameters */
- if (wil->wdev->iftype == NL80211_IFTYPE_MONITOR) {
- /* disable interrupt moderation for monitor
- * to get better timestamp precision
- */
- iowrite32(0, wil->csr + HOSTADDR(RGF_DMA_ITR_CNT_CRL));
- } else {
- iowrite32(WIL6210_ITR_TRSH,
- wil->csr + HOSTADDR(RGF_DMA_ITR_CNT_TRSH));
- iowrite32(BIT_DMA_ITR_CNT_CRL_EN,
- wil->csr + HOSTADDR(RGF_DMA_ITR_CNT_CRL));
- }
+ wil_set_itr_trsh(wil);
wil6210_unmask_irq_pseudo(wil);
wil6210_unmask_irq_tx(wil);
@@ -196,8 +186,13 @@ static irqreturn_t wil6210_irq_rx(int irq, void *cookie)
wil_dbg_irq(wil, "RX done\n");
isr &= ~BIT_DMA_EP_RX_ICR_RX_DONE;
if (test_bit(wil_status_reset_done, &wil->status)) {
- wil_dbg_txrx(wil, "NAPI(Rx) schedule\n");
- napi_schedule(&wil->napi_rx);
+ if (test_bit(wil_status_napi_en, &wil->status)) {
+ wil_dbg_txrx(wil, "NAPI(Rx) schedule\n");
+ napi_schedule(&wil->napi_rx);
+ } else {
+ wil_err(wil, "Got Rx interrupt while "
+ "stopping interface\n");
+ }
} else {
wil_err(wil, "Got Rx interrupt while in reset\n");
}
@@ -506,7 +501,8 @@ free0:
return rc;
}
-/* can't use wil_ioread32_and_clear because ICC value is not ser yet */
+
+/* can't use wil_ioread32_and_clear because ICC value is not set yet */
static inline void wil_clear32(void __iomem *addr)
{
u32 x = ioread32(addr);
@@ -522,11 +518,15 @@ void wil6210_clear_irq(struct wil6210_priv *wil)
offsetof(struct RGF_ICR, ICR));
wil_clear32(wil->csr + HOSTADDR(RGF_DMA_EP_MISC_ICR) +
offsetof(struct RGF_ICR, ICR));
+ wmb(); /* make sure write completed */
}
int wil6210_init_irq(struct wil6210_priv *wil, int irq)
{
int rc;
+
+ wil_dbg_misc(wil, "%s() n_msi=%d\n", __func__, wil->n_msi);
+
if (wil->n_msi == 3)
rc = wil6210_request_3msi(wil, irq);
else
@@ -534,17 +534,14 @@ int wil6210_init_irq(struct wil6210_priv *wil, int irq)
wil6210_thread_irq,
wil->n_msi ? 0 : IRQF_SHARED,
WIL_NAME, wil);
- if (rc)
- return rc;
-
- wil6210_enable_irq(wil);
-
- return 0;
+ return rc;
}
void wil6210_fini_irq(struct wil6210_priv *wil, int irq)
{
- wil6210_disable_irq(wil);
+ wil_dbg_misc(wil, "%s()\n", __func__);
+
+ wil_mask_irq(wil);
free_irq(irq, wil);
if (wil->n_msi == 3) {
free_irq(irq + 1, wil);
diff --git a/drivers/net/wireless/ath/wil6210/ioctl.c b/drivers/net/wireless/ath/wil6210/ioctl.c
new file mode 100644
index 0000000..e9c0673
--- /dev/null
+++ b/drivers/net/wireless/ath/wil6210/ioctl.c
@@ -0,0 +1,173 @@
+/*
+ * Copyright (c) 2014 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 <linux/uaccess.h>
+
+#include "wil6210.h"
+#include <uapi/linux/wil6210_uapi.h>
+
+#define wil_hex_dump_ioctl(prefix_str, buf, len) \
+ print_hex_dump_debug("DBG[IOC ]" prefix_str, \
+ DUMP_PREFIX_OFFSET, 16, 1, buf, len, true)
+#define wil_dbg_ioctl(wil, fmt, arg...) wil_dbg(wil, "DBG[IOC ]" fmt, ##arg)
+
+static void __iomem *wil_ioc_addr(struct wil6210_priv *wil, uint32_t addr,
+ uint32_t size, enum wil_memio_op op)
+{
+ void __iomem *a;
+ u32 off;
+
+ switch (op & wil_mmio_addr_mask) {
+ case wil_mmio_addr_linker:
+ a = wmi_buffer(wil, cpu_to_le32(addr));
+ break;
+ case wil_mmio_addr_ahb:
+ a = wmi_addr(wil, addr);
+ break;
+ case wil_mmio_addr_bar:
+ a = wmi_addr(wil, addr + WIL6210_FW_HOST_OFF);
+ break;
+ default:
+ wil_err(wil, "Unsupported address mode, op = 0x%08x\n", op);
+ return NULL;
+ }
+
+ off = a - wil->csr;
+ if (size >= WIL6210_MEM_SIZE - off) {
+ wil_err(wil, "Requested block does not fit into memory: "
+ "off = 0x%08x size = 0x%08x\n", off, size);
+ return NULL;
+ }
+
+ return a;
+}
+
+static int wil_ioc_memio_dword(struct wil6210_priv *wil, void __user *data)
+{
+ struct wil_memio io;
+ void __iomem *a;
+ bool need_copy = false;
+
+ if (copy_from_user(&io, data, sizeof(io)))
+ return -EFAULT;
+
+ wil_dbg_ioctl(wil, "IO: addr = 0x%08x val = 0x%08x op = 0x%08x\n",
+ io.addr, io.val, io.op);
+
+ a = wil_ioc_addr(wil, io.addr, sizeof(u32), io.op);
+ if (!a) {
+ wil_err(wil, "invalid address 0x%08x, op = 0x%08x\n", io.addr,
+ io.op);
+ return -EINVAL;
+ }
+ /* operation */
+ switch (io.op & wil_mmio_op_mask) {
+ case wil_mmio_read:
+ io.val = ioread32(a);
+ need_copy = true;
+ break;
+ case wil_mmio_write:
+ iowrite32(io.val, a);
+ wmb(); /* make sure write propagated to HW */
+ break;
+ default:
+ wil_err(wil, "Unsupported operation, op = 0x%08x\n", io.op);
+ return -EINVAL;
+ }
+
+ if (need_copy) {
+ wil_dbg_ioctl(wil, "IO done: addr = 0x%08x"
+ " val = 0x%08x op = 0x%08x\n",
+ io.addr, io.val, io.op);
+ if (copy_to_user(data, &io, sizeof(io)))
+ return -EFAULT;
+ }
+
+ return 0;
+}
+
+static int wil_ioc_memio_block(struct wil6210_priv *wil, void __user *data)
+{
+ struct wil_memio_block io;
+ void *block;
+ void __iomem *a;
+ int rc = 0;
+
+ if (copy_from_user(&io, data, sizeof(io)))
+ return -EFAULT;
+
+ wil_dbg_ioctl(wil, "IO: addr = 0x%08x size = 0x%08x op = 0x%08x\n",
+ io.addr, io.size, io.op);
+
+ /* size */
+ if (io.size % 4) {
+ wil_err(wil, "size is not multiple of 4: 0x%08x\n", io.size);
+ return -EINVAL;
+ }
+
+ a = wil_ioc_addr(wil, io.addr, io.size, io.op);
+ if (!a) {
+ wil_err(wil, "invalid address 0x%08x, op = 0x%08x\n", io.addr,
+ io.op);
+ return -EINVAL;
+ }
+
+ block = kmalloc(io.size, GFP_USER);
+ if (!block)
+ return -ENOMEM;
+
+ /* operation */
+ switch (io.op & wil_mmio_op_mask) {
+ case wil_mmio_read:
+ wil_memcpy_fromio_32(block, a, io.size);
+ wil_hex_dump_ioctl("Read ", block, io.size);
+ if (copy_to_user(io.block, block, io.size)) {
+ rc = -EFAULT;
+ goto out_free;
+ }
+ break;
+ case wil_mmio_write:
+ if (copy_from_user(block, io.block, io.size)) {
+ rc = -EFAULT;
+ goto out_free;
+ }
+ wil_memcpy_toio_32(a, block, io.size);
+ wmb(); /* make sure write propagated to HW */
+ wil_hex_dump_ioctl("Write ", block, io.size);
+ break;
+ default:
+ wil_err(wil, "Unsupported operation, op = 0x%08x\n", io.op);
+ rc = -EINVAL;
+ break;
+ }
+
+out_free:
+ kfree(block);
+ return rc;
+}
+
+int wil_ioctl(struct wil6210_priv *wil, void __user *data, int cmd)
+{
+ switch (cmd) {
+ case WIL_IOCTL_MEMIO:
+ return wil_ioc_memio_dword(wil, data);
+ case WIL_IOCTL_MEMIO_BLOCK:
+ return wil_ioc_memio_block(wil, data);
+ default:
+ wil_dbg_ioctl(wil, "Unsupported IOCTL 0x%04x\n", cmd);
+ return -ENOIOCTLCMD;
+ }
+}
diff --git a/drivers/net/wireless/ath/wil6210/main.c b/drivers/net/wireless/ath/wil6210/main.c
index 3704d2a..6500caf 100644
--- a/drivers/net/wireless/ath/wil6210/main.c
+++ b/drivers/net/wireless/ath/wil6210/main.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012 Qualcomm Atheros, Inc.
+ * Copyright (c) 2012-2014 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
@@ -20,10 +20,26 @@
#include "wil6210.h"
#include "txrx.h"
+#include "wmi.h"
-static bool no_fw_recovery;
+#define WAIT_FOR_DISCONNECT_TIMEOUT_MS 2000
+#define WAIT_FOR_DISCONNECT_INTERVAL_MS 10
+
+bool no_fw_recovery;
module_param(no_fw_recovery, bool, S_IRUGO | S_IWUSR);
-MODULE_PARM_DESC(no_fw_recovery, " disable FW error recovery");
+MODULE_PARM_DESC(no_fw_recovery, " disable automatic FW error recovery");
+
+static bool no_fw_load = true;
+module_param(no_fw_load, bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(no_fw_load, " do not download FW, use one in on-card flash.");
+
+static unsigned int itr_trsh = WIL6210_ITR_TRSH_DEFAULT;
+
+module_param(itr_trsh, uint, S_IRUGO);
+MODULE_PARM_DESC(itr_trsh, " Interrupt moderation threshold, usecs.");
+
+#define RST_DELAY (20) /* msec, for loop in @wil_target_reset */
+#define RST_COUNT (1 + 1000/RST_DELAY) /* round up to be above 1 sec total */
/*
* Due to a hardware issue,
@@ -64,6 +80,7 @@ static void wil_disconnect_cid(struct wil6210_priv *wil, int cid)
struct net_device *ndev = wil_to_ndev(wil);
struct wireless_dev *wdev = wil->wdev;
struct wil_sta_info *sta = &wil->sta[cid];
+
wil_dbg_misc(wil, "%s(CID %d, status %d)\n", __func__, cid,
sta->status);
@@ -83,9 +100,16 @@ static void wil_disconnect_cid(struct wil6210_priv *wil, int cid)
}
for (i = 0; i < WIL_STA_TID_NUM; i++) {
- struct wil_tid_ampdu_rx *r = sta->tid_rx[i];
+ struct wil_tid_ampdu_rx *r;
+ unsigned long flags;
+
+ spin_lock_irqsave(&sta->tid_rx_lock, flags);
+
+ r = sta->tid_rx[i];
sta->tid_rx[i] = NULL;
wil_tid_ampdu_rx_free(wil, r);
+
+ spin_unlock_irqrestore(&sta->tid_rx_lock, flags);
}
for (i = 0; i < ARRAY_SIZE(wil->vring_tx); i++) {
if (wil->vring2cid_tid[i][0] == cid)
@@ -167,17 +191,38 @@ static void wil_scan_timer_fn(ulong x)
schedule_work(&wil->fw_error_worker);
}
+static int wil_wait_for_recovery(struct wil6210_priv *wil)
+{
+ if (wait_event_interruptible(wil->wq, wil->recovery_state !=
+ fw_recovery_pending)) {
+ wil_err(wil, "Interrupt, canceling recovery\n");
+ return -ERESTARTSYS;
+ }
+ if (wil->recovery_state != fw_recovery_running) {
+ wil_info(wil, "Recovery cancelled\n");
+ return -EINTR;
+ }
+ wil_info(wil, "Proceed with recovery\n");
+ return 0;
+}
+
+void wil_set_recovery_state(struct wil6210_priv *wil, int state)
+{
+ wil_dbg_misc(wil, "%s(%d -> %d)\n", __func__,
+ wil->recovery_state, state);
+
+ wil->recovery_state = state;
+ wake_up_interruptible(&wil->wq);
+}
+
static void wil_fw_error_worker(struct work_struct *work)
{
- struct wil6210_priv *wil = container_of(work,
- struct wil6210_priv, fw_error_worker);
+ 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;
-
/* increment @recovery_count if less then WIL6210_FW_RECOVERY_TO
* passed since last recovery attempt
*/
@@ -200,12 +245,15 @@ static void wil_fw_error_worker(struct work_struct *work)
case NL80211_IFTYPE_STATION:
case NL80211_IFTYPE_P2P_CLIENT:
case NL80211_IFTYPE_MONITOR:
- wil_info(wil, "fw error recovery started (try %d)...\n",
+ wil_info(wil, "fw error recovery requested (try %d)...\n",
wil->recovery_count);
- wil_reset(wil);
+ if (!no_fw_recovery)
+ wil->recovery_state = fw_recovery_running;
+ if (0 != wil_wait_for_recovery(wil))
+ break;
- /* need to re-allocate Rx ring after reset */
- wil_rx_init(wil);
+ __wil_down(wil);
+ __wil_up(wil);
break;
case NL80211_IFTYPE_AP:
case NL80211_IFTYPE_P2P_GO:
@@ -220,6 +268,7 @@ static void wil_fw_error_worker(struct work_struct *work)
static int wil_find_free_vring(struct wil6210_priv *wil)
{
int i;
+
for (i = 0; i < WIL6210_MAX_TX_RINGS; i++) {
if (!wil->vring_tx[i].va)
return i;
@@ -254,14 +303,19 @@ static void wil_connect_worker(struct work_struct *work)
int wil_priv_init(struct wil6210_priv *wil)
{
+ uint i;
+
wil_dbg_misc(wil, "%s()\n", __func__);
memset(wil->sta, 0, sizeof(wil->sta));
+ for (i = 0; i < WIL6210_MAX_CID; i++)
+ spin_lock_init(&wil->sta[i].tid_rx_lock);
mutex_init(&wil->mutex);
mutex_init(&wil->wmi_mutex);
init_completion(&wil->wmi_ready);
+ init_completion(&wil->wmi_call);
wil->pending_connect_cid = -1;
setup_timer(&wil->connect_timer, wil_connect_timer_fn, (ulong)wil);
@@ -274,6 +328,7 @@ int wil_priv_init(struct wil6210_priv *wil)
INIT_LIST_HEAD(&wil->pending_wmi_ev);
spin_lock_init(&wil->wmi_ev_lock);
+ init_waitqueue_head(&wil->wq);
wil->wmi_wq = create_singlethread_workqueue(WIL_NAME"_wmi");
if (!wil->wmi_wq)
@@ -286,18 +341,24 @@ int wil_priv_init(struct wil6210_priv *wil)
}
wil->last_fw_recovery = jiffies;
+ wil->itr_trsh = itr_trsh;
return 0;
}
void wil6210_disconnect(struct wil6210_priv *wil, const u8 *bssid)
{
+ wil_dbg_misc(wil, "%s()\n", __func__);
+
del_timer_sync(&wil->connect_timer);
_wil6210_disconnect(wil, bssid);
}
void wil_priv_deinit(struct wil6210_priv *wil)
{
+ wil_dbg_misc(wil, "%s()\n", __func__);
+
+ wil_set_recovery_state(wil, fw_recovery_idle);
del_timer_sync(&wil->scan_timer);
cancel_work_sync(&wil->disconnect_worker);
cancel_work_sync(&wil->fw_error_worker);
@@ -309,7 +370,29 @@ void wil_priv_deinit(struct wil6210_priv *wil)
destroy_workqueue(wil->wmi_wq);
}
-static void wil_target_reset(struct wil6210_priv *wil)
+/* target operations */
+/* register read */
+#define R(a) ioread32(wil->csr + HOSTADDR(a))
+/* register write. wmb() to make sure it is completed */
+#define W(a, v) do { iowrite32(v, wil->csr + HOSTADDR(a)); wmb(); } while (0)
+/* register set = read, OR, write */
+#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)
+
+static inline void wil_halt_cpu(struct wil6210_priv *wil)
+{
+ W(RGF_USER_USER_CPU_0, BIT_USER_USER_CPU_MAN_RST);
+ W(RGF_USER_MAC_CPU_0, BIT_USER_MAC_CPU_MAN_RST);
+}
+
+static inline void wil_release_cpu(struct wil6210_priv *wil)
+{
+ /* Start CPU */
+ W(RGF_USER_USER_CPU_0, 1);
+}
+
+static int wil_target_reset(struct wil6210_priv *wil)
{
int delay = 0;
u32 hw_state;
@@ -318,57 +401,41 @@ static void wil_target_reset(struct wil6210_priv *wil)
wil_dbg_misc(wil, "Resetting \"%s\"...\n", wil->board->name);
- /* 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) 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;
/* Clear MAC link up */
S(RGF_HP_CTRL, BIT(15));
- /* hpal_perst_from_pad_src_n_mask */
- 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 */
+ S(RGF_USER_CLKS_CTL_SW_RST_MASK_0, BIT_HPAL_PERST_FROM_PAD);
+ S(RGF_USER_CLKS_CTL_SW_RST_MASK_0, BIT_CAR_PERST_RST);
+
+ wil_halt_cpu(wil);
+ C(RGF_USER_CLKS_CTL_0, BIT_USER_CLKS_CAR_AHB_SW_SEL); /* 40 MHz */
if (is_sparrow) {
W(RGF_USER_CLKS_CTL_EXT_SW_RST_VEC_0, 0x3ff81f);
- wmb(); /* order is important here */
+ W(RGF_USER_CLKS_CTL_EXT_SW_RST_VEC_1, 0xf);
}
- 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, is_sparrow ? 0x000000B0 : 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, is_sparrow ? 0x000000f0 : 0x00000170);
+ W(RGF_USER_CLKS_CTL_SW_RST_VEC_0, 0xFFE7FE00);
if (is_sparrow) {
W(RGF_USER_CLKS_CTL_EXT_SW_RST_VEC_0, 0x0);
- wmb(); /* order is important here */
+ W(RGF_USER_CLKS_CTL_EXT_SW_RST_VEC_1, 0x0);
}
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 */
if (is_sparrow) {
W(RGF_USER_CLKS_CTL_SW_RST_VEC_3, 0x00000003);
/* reset A2 PCIE AHB */
W(RGF_USER_CLKS_CTL_SW_RST_VEC_2, 0x00008000);
-
} else {
W(RGF_USER_CLKS_CTL_SW_RST_VEC_3, 0x00000001);
if (rev_id == 1) {
@@ -378,21 +445,19 @@ static void wil_target_reset(struct wil6210_priv *wil)
W(RGF_PCIE_LOS_COUNTER_CTL, BIT(6) | BIT(8));
W(RGF_USER_CLKS_CTL_SW_RST_VEC_2, 0x00008000);
}
-
}
/* TODO: check order here!!! Erez code is different */
W(RGF_USER_CLKS_CTL_SW_RST_VEC_0, 0);
- wmb(); /* order is important here */
- /* wait until device ready */
+ /* wait until device ready. typical time is 200..250 msec */
do {
- msleep(1);
+ msleep(RST_DELAY);
hw_state = R(RGF_USER_HW_MACHINE_STATE);
- if (delay++ > 100) {
+ if (delay++ > RST_COUNT) {
wil_err(wil, "Reset not completed, hw_state 0x%08x\n",
hw_state);
- return;
+ return -ETIME;
}
} while (hw_state != HW_MACHINE_BOOT_DONE);
@@ -401,15 +466,35 @@ 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);
+ wil_dbg_misc(wil, "Reset completed in %d ms\n", delay * RST_DELAY);
+ return 0;
+}
+
+/**
+ * wil_set_itr_trsh: - apply interrupt coalescing params
+ */
+void wil_set_itr_trsh(struct wil6210_priv *wil)
+{
+ /* disable, use usec resolution */
+ W(RGF_DMA_ITR_CNT_CRL, BIT_DMA_ITR_CNT_CRL_EXT_TICK);
+
+ /* disable interrupt moderation for monitor
+ * to get better timestamp precision
+ */
+ if (wil->wdev->iftype == NL80211_IFTYPE_MONITOR)
+ return;
+
+ wil_info(wil, "set ITR_TRSH = %d usec\n", wil->itr_trsh);
+ W(RGF_DMA_ITR_CNT_TRSH, wil->itr_trsh);
+ W(RGF_DMA_ITR_CNT_CRL, BIT_DMA_ITR_CNT_CRL_EN |
+ BIT_DMA_ITR_CNT_CRL_EXT_TICK); /* start it */
+}
#undef R
#undef W
#undef S
#undef C
-}
void wil_mbox_ring_le2cpus(struct wil6210_mbox_ring *r)
{
@@ -424,6 +509,7 @@ static int wil_wait_for_fw_ready(struct wil6210_priv *wil)
{
ulong to = msecs_to_jiffies(1000);
ulong left = wait_for_completion_timeout(&wil->wmi_ready, to);
+
if (0 == left) {
wil_err(wil, "Firmware not ready\n");
return -ETIME;
@@ -443,15 +529,15 @@ int wil_reset(struct wil6210_priv *wil)
{
int rc;
+ wil_dbg_misc(wil, "%s()\n", __func__);
+
WARN_ON(!mutex_is_locked(&wil->mutex));
+ WARN_ON(test_bit(wil_status_napi_en, &wil->status));
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);
- }
if (wil->scan_request) {
wil_dbg_misc(wil, "Abort scan_request 0x%p\n",
@@ -461,24 +547,50 @@ int wil_reset(struct wil6210_priv *wil)
wil->scan_request = NULL;
}
- wil6210_disable_irq(wil);
+ wil_mask_irq(wil);
wmi_event_flush(wil);
flush_workqueue(wil->wmi_wq_conn);
flush_workqueue(wil->wmi_wq);
- /* TODO: put MAC in reset */
- wil_target_reset(wil);
-
+ rc = wil_target_reset(wil);
wil_rx_fini(wil);
+ if (rc)
+ return rc;
+
+ if (!no_fw_load) {
+ wil_info(wil, "Use firmware <%s>\n", WIL_FW_NAME);
+ wil_halt_cpu(wil);
+ /* Loading f/w from the file */
+ rc = wil_request_firmware(wil, WIL_FW_NAME);
+ if (rc)
+ return rc;
+
+ /* clear any interrupts which on-card-firmware may have set */
+ wil6210_clear_irq(wil);
+ { /* CAF_ICR - clear and mask */
+ u32 a = HOSTADDR(RGF_CAF_ICR) +
+ offsetof(struct RGF_ICR, ICR);
+ u32 m = HOSTADDR(RGF_CAF_ICR) +
+ offsetof(struct RGF_ICR, IMV);
+ u32 icr = ioread32(wil->csr + a);
+
+ iowrite32(icr, wil->csr + a); /* W1C */
+ iowrite32(~0, wil->csr + m);
+ wmb(); /* wait for completion */
+ }
+ wil_release_cpu(wil);
+ } else {
+ wil_info(wil, "Use firmware from on-card flash\n");
+ }
/* init after reset */
wil->pending_connect_cid = -1;
reinit_completion(&wil->wmi_ready);
+ reinit_completion(&wil->wmi_call);
- /* TODO: release MAC reset */
- wil6210_enable_irq(wil);
+ wil_unmask_irq(wil);
/* we just started MAC, wait for FW ready */
rc = wil_wait_for_fw_ready(wil);
@@ -489,6 +601,7 @@ int wil_reset(struct wil6210_priv *wil)
void wil_fw_error_recovery(struct wil6210_priv *wil)
{
wil_dbg_misc(wil, "starting fw error recovery\n");
+ wil->recovery_state = fw_recovery_pending;
schedule_work(&wil->fw_error_worker);
}
@@ -514,7 +627,7 @@ void wil_link_off(struct wil6210_priv *wil)
netif_carrier_off(ndev);
}
-static int __wil_up(struct wil6210_priv *wil)
+int __wil_up(struct wil6210_priv *wil)
{
struct net_device *ndev = wil_to_ndev(wil);
struct wireless_dev *wdev = wil->wdev;
@@ -560,11 +673,15 @@ static int __wil_up(struct wil6210_priv *wil)
/* MAC address - pre-requisite for other commands */
wmi_set_mac_address(wil, ndev->dev_addr);
-
+ wil_dbg_misc(wil, "NAPI enable\n");
napi_enable(&wil->napi_rx);
napi_enable(&wil->napi_tx);
set_bit(wil_status_napi_en, &wil->status);
+ if (wil->platform_ops.bus_request)
+ wil->platform_ops.bus_request(wil->platform_handle,
+ WIL_MAX_BUS_REQUEST_KBPS);
+
return 0;
}
@@ -572,6 +689,8 @@ int wil_up(struct wil6210_priv *wil)
{
int rc;
+ wil_dbg_misc(wil, "%s()\n", __func__);
+
mutex_lock(&wil->mutex);
rc = __wil_up(wil);
mutex_unlock(&wil->mutex);
@@ -579,13 +698,23 @@ int wil_up(struct wil6210_priv *wil)
return rc;
}
-static int __wil_down(struct wil6210_priv *wil)
+int __wil_down(struct wil6210_priv *wil)
{
+ int iter = WAIT_FOR_DISCONNECT_TIMEOUT_MS /
+ WAIT_FOR_DISCONNECT_INTERVAL_MS;
+
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);
+ if (wil->platform_ops.bus_request)
+ wil->platform_ops.bus_request(wil->platform_handle, 0);
+
+ wil_disable_irq(wil);
+ if (test_and_clear_bit(wil_status_napi_en, &wil->status)) {
+ napi_disable(&wil->napi_rx);
+ napi_disable(&wil->napi_tx);
+ wil_dbg_misc(wil, "NAPI disable\n");
+ }
+ wil_enable_irq(wil);
if (wil->scan_request) {
wil_dbg_misc(wil, "Abort scan_request 0x%p\n",
@@ -595,7 +724,24 @@ static int __wil_down(struct wil6210_priv *wil)
wil->scan_request = NULL;
}
- wil6210_disconnect(wil, NULL);
+ if (test_bit(wil_status_fwconnected, &wil->status) ||
+ test_bit(wil_status_fwconnecting, &wil->status))
+ wmi_send(wil, WMI_DISCONNECT_CMDID, NULL, 0);
+
+ /* make sure wil is idle (not connected) */
+ mutex_unlock(&wil->mutex);
+ while (iter--) {
+ int idle = !test_bit(wil_status_fwconnected, &wil->status) &&
+ !test_bit(wil_status_fwconnecting, &wil->status);
+ if (idle)
+ break;
+ msleep(WAIT_FOR_DISCONNECT_INTERVAL_MS);
+ }
+ mutex_lock(&wil->mutex);
+
+ if (!iter)
+ wil_err(wil, "timeout waiting for idle FW/HW\n");
+
wil_rx_fini(wil);
return 0;
@@ -605,6 +751,9 @@ int wil_down(struct wil6210_priv *wil)
{
int rc;
+ wil_dbg_misc(wil, "%s()\n", __func__);
+
+ wil_set_recovery_state(wil, fw_recovery_idle);
mutex_lock(&wil->mutex);
rc = __wil_down(wil);
mutex_unlock(&wil->mutex);
diff --git a/drivers/net/wireless/ath/wil6210/netdev.c b/drivers/net/wireless/ath/wil6210/netdev.c
index 7afce6e..2399651 100644
--- a/drivers/net/wireless/ath/wil6210/netdev.c
+++ b/drivers/net/wireless/ath/wil6210/netdev.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012 Qualcomm Atheros, Inc.
+ * Copyright (c) 2012-2014 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
@@ -17,11 +17,14 @@
#include <linux/etherdevice.h>
#include "wil6210.h"
+#include "txrx.h"
static int wil_open(struct net_device *ndev)
{
struct wil6210_priv *wil = ndev_to_wil(ndev);
+ wil_dbg_misc(wil, "%s()\n", __func__);
+
return wil_up(wil);
}
@@ -29,6 +32,8 @@ static int wil_stop(struct net_device *ndev)
{
struct wil6210_priv *wil = ndev_to_wil(ndev);
+ wil_dbg_misc(wil, "%s()\n", __func__);
+
return wil_down(wil);
}
@@ -36,8 +41,10 @@ static int wil_change_mtu(struct net_device *ndev, int new_mtu)
{
struct wil6210_priv *wil = ndev_to_wil(ndev);
- if (new_mtu < 68 || new_mtu > IEEE80211_MAX_DATA_LEN_DMG)
+ if (new_mtu < 68 || new_mtu > (TX_BUF_LEN - ETH_HLEN)) {
+ wil_err(wil, "invalid MTU %d\n", new_mtu);
return -EINVAL;
+ }
wil_dbg_misc(wil, "change MTU %d -> %d\n", ndev->mtu, new_mtu);
ndev->mtu = new_mtu;
@@ -45,6 +52,17 @@ static int wil_change_mtu(struct net_device *ndev, int new_mtu)
return 0;
}
+static int wil_do_ioctl(struct net_device *ndev, struct ifreq *ifr, int cmd)
+{
+ struct wil6210_priv *wil = ndev_to_wil(ndev);
+
+ int ret = wil_ioctl(wil, ifr->ifr_data, cmd);
+
+ wil_dbg_misc(wil, "ioctl(0x%04x) -> %d\n", cmd, ret);
+
+ return ret;
+}
+
static const struct net_device_ops wil_netdev_ops = {
.ndo_open = wil_open,
.ndo_stop = wil_stop,
@@ -52,6 +70,7 @@ static const struct net_device_ops wil_netdev_ops = {
.ndo_set_mac_address = eth_mac_addr,
.ndo_validate_addr = eth_validate_addr,
.ndo_change_mtu = wil_change_mtu,
+ .ndo_do_ioctl = wil_do_ioctl,
};
static int wil6210_netdev_poll_rx(struct napi_struct *napi, int budget)
@@ -121,6 +140,8 @@ void *wil_if_alloc(struct device *dev, void __iomem *csr)
wil->csr = csr;
wil->wdev = wdev;
+ wil_dbg_misc(wil, "%s()\n", __func__);
+
rc = wil_priv_init(wil);
if (rc) {
dev_err(dev, "wil_priv_init failed\n");
@@ -140,6 +161,7 @@ void *wil_if_alloc(struct device *dev, void __iomem *csr)
}
ndev->netdev_ops = &wil_netdev_ops;
+ wil_set_ethtoolops(ndev);
ndev->ieee80211_ptr = wdev;
ndev->hw_features = NETIF_F_HW_CSUM | NETIF_F_RXCSUM |
NETIF_F_SG | NETIF_F_GRO;
@@ -168,11 +190,17 @@ void *wil_if_alloc(struct device *dev, void __iomem *csr)
void wil_if_free(struct wil6210_priv *wil)
{
struct net_device *ndev = wil_to_ndev(wil);
+
+ wil_dbg_misc(wil, "%s()\n", __func__);
+
if (!ndev)
return;
- free_netdev(ndev);
wil_priv_deinit(wil);
+
+ wil_to_ndev(wil) = NULL;
+ free_netdev(ndev);
+
wil_wdev_free(wil);
}
@@ -181,6 +209,8 @@ int wil_if_add(struct wil6210_priv *wil)
struct net_device *ndev = wil_to_ndev(wil);
int rc;
+ wil_dbg_misc(wil, "%s()\n", __func__);
+
rc = register_netdev(ndev);
if (rc < 0) {
dev_err(&ndev->dev, "Failed to register netdev: %d\n", rc);
@@ -196,5 +226,7 @@ void wil_if_remove(struct wil6210_priv *wil)
{
struct net_device *ndev = wil_to_ndev(wil);
+ wil_dbg_misc(wil, "%s()\n", __func__);
+
unregister_netdev(ndev);
}
diff --git a/drivers/net/wireless/ath/wil6210/pcie_bus.c b/drivers/net/wireless/ath/wil6210/pcie_bus.c
index d3fbfa2..66626a8 100644
--- a/drivers/net/wireless/ath/wil6210/pcie_bus.c
+++ b/drivers/net/wireless/ath/wil6210/pcie_bus.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012 Qualcomm Atheros, Inc.
+ * Copyright (c) 2012-2014 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
@@ -17,6 +17,7 @@
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/moduleparam.h>
+#include <linux/interrupt.h>
#include "wil6210.h"
@@ -30,6 +31,28 @@ 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");
+void wil_disable_irq(struct wil6210_priv *wil)
+{
+ int irq = wil->pdev->irq;
+
+ disable_irq(irq);
+ if (wil->n_msi == 3) {
+ disable_irq(irq + 1);
+ disable_irq(irq + 2);
+ }
+}
+
+void wil_enable_irq(struct wil6210_priv *wil)
+{
+ int irq = wil->pdev->irq;
+
+ enable_irq(irq);
+ if (wil->n_msi == 3) {
+ enable_irq(irq + 1);
+ enable_irq(irq + 2);
+ }
+}
+
/* Bus ops */
static int wil_if_pcie_enable(struct wil6210_priv *wil)
{
@@ -41,6 +64,8 @@ static int wil_if_pcie_enable(struct wil6210_priv *wil)
*/
int msi_only = pdev->msi_enabled;
+ wil_dbg_misc(wil, "%s()\n", __func__);
+
pdev->msi_enabled = 0;
pci_set_master(pdev);
@@ -107,6 +132,8 @@ static int wil_if_pcie_disable(struct wil6210_priv *wil)
{
struct pci_dev *pdev = wil->pdev;
+ wil_dbg_misc(wil, "%s()\n", __func__);
+
pci_clear_master(pdev);
/* disable and release IRQ */
wil6210_fini_irq(wil, pdev->irq);
@@ -180,6 +207,10 @@ static int wil_pcie_probe(struct pci_dev *pdev, const struct pci_device_id *id)
wil->board = board;
wil6210_clear_irq(wil);
+
+ wil->platform_handle =
+ wil_platform_init(&pdev->dev, &wil->platform_ops);
+
/* FW should raise IRQ when ready */
rc = wil_if_pcie_enable(wil);
if (rc) {
@@ -204,6 +235,8 @@ static int wil_pcie_probe(struct pci_dev *pdev, const struct pci_device_id *id)
bus_disable:
wil_if_pcie_disable(wil);
if_free:
+ if (wil->platform_ops.uninit)
+ wil->platform_ops.uninit(wil->platform_handle);
wil_if_free(wil);
err_iounmap:
pci_iounmap(pdev, csr);
@@ -218,12 +251,17 @@ static int wil_pcie_probe(struct pci_dev *pdev, const struct pci_device_id *id)
static void wil_pcie_remove(struct pci_dev *pdev)
{
struct wil6210_priv *wil = pci_get_drvdata(pdev);
+ void __iomem *csr = wil->csr;
+
+ wil_dbg_misc(wil, "%s()\n", __func__);
wil6210_debugfs_remove(wil);
- wil_if_pcie_disable(wil);
wil_if_remove(wil);
+ wil_if_pcie_disable(wil);
+ if (wil->platform_ops.uninit)
+ wil->platform_ops.uninit(wil->platform_handle);
wil_if_free(wil);
- pci_iounmap(pdev, wil->csr);
+ pci_iounmap(pdev, csr);
pci_release_region(pdev, 0);
pci_disable_device(pdev);
}
@@ -243,6 +281,8 @@ static const struct pci_device_id wil6210_pcie_ids[] = {
.driver_data = (kernel_ulong_t)&wil_board_marlon },
{ PCI_DEVICE(0x1ae9, 0x0310),
.driver_data = (kernel_ulong_t)&wil_board_sparrow },
+ { PCI_DEVICE(0x1ae9, 0x0302), /* same as above, firmware broken */
+ .driver_data = (kernel_ulong_t)&wil_board_sparrow },
{ /* end: all zeroes */ },
};
MODULE_DEVICE_TABLE(pci, wil6210_pcie_ids);
diff --git a/drivers/net/wireless/ath/wil6210/rx_reorder.c b/drivers/net/wireless/ath/wil6210/rx_reorder.c
index 180ca47..489cb73 100644
--- a/drivers/net/wireless/ath/wil6210/rx_reorder.c
+++ b/drivers/net/wireless/ath/wil6210/rx_reorder.c
@@ -1,3 +1,19 @@
+/*
+ * Copyright (c) 2014 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 "wil6210.h"
#include "txrx.h"
@@ -82,22 +98,25 @@ void wil_rx_reorder(struct wil6210_priv *wil, struct sk_buff *skb)
int mid = wil_rxdesc_mid(d);
u16 seq = wil_rxdesc_seq(d);
struct wil_sta_info *sta = &wil->sta[cid];
- struct wil_tid_ampdu_rx *r = sta->tid_rx[tid];
+ struct wil_tid_ampdu_rx *r;
u16 hseq;
int index;
+ unsigned long flags;
wil_dbg_txrx(wil, "MID %d CID %d TID %d Seq 0x%03x\n",
mid, cid, tid, seq);
+ spin_lock_irqsave(&sta->tid_rx_lock, flags);
+
+ r = sta->tid_rx[tid];
if (!r) {
+ spin_unlock_irqrestore(&sta->tid_rx_lock, flags);
wil_netif_rx_any(skb, ndev);
return;
}
hseq = r->head_seq_num;
- spin_lock(&r->reorder_lock);
-
/** Due to the race between WMI events, where BACK establishment
* reported, and data Rx, few packets may be pass up before reorder
* buffer get allocated. Catch up by pretending SSN is what we
@@ -160,13 +179,14 @@ void wil_rx_reorder(struct wil6210_priv *wil, struct sk_buff *skb)
wil_reorder_release(wil, r);
out:
- spin_unlock(&r->reorder_lock);
+ spin_unlock_irqrestore(&sta->tid_rx_lock, flags);
}
struct wil_tid_ampdu_rx *wil_tid_ampdu_rx_alloc(struct wil6210_priv *wil,
int size, u16 ssn)
{
struct wil_tid_ampdu_rx *r = kzalloc(sizeof(*r), GFP_KERNEL);
+
if (!r)
return NULL;
@@ -181,7 +201,6 @@ struct wil_tid_ampdu_rx *wil_tid_ampdu_rx_alloc(struct wil6210_priv *wil,
return NULL;
}
- spin_lock_init(&r->reorder_lock);
r->ssn = ssn;
r->head_seq_num = ssn;
r->buf_size = size;
diff --git a/drivers/net/wireless/ath/wil6210/txrx.c b/drivers/net/wireless/ath/wil6210/txrx.c
index d346794..2936ef0 100644
--- a/drivers/net/wireless/ath/wil6210/txrx.c
+++ b/drivers/net/wireless/ath/wil6210/txrx.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012 Qualcomm Atheros, Inc.
+ * Copyright (c) 2012-2014 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
@@ -52,6 +52,7 @@ static inline int wil_vring_is_full(struct vring *vring)
{
return wil_vring_next_tail(vring) == vring->swhead;
}
+
/*
* Available space in Tx Vring
*/
@@ -86,6 +87,8 @@ static int wil_vring_alloc(struct wil6210_priv *wil, struct vring *vring)
size_t sz = vring->size * sizeof(vring->va[0]);
uint i;
+ wil_dbg_misc(wil, "%s()\n", __func__);
+
BUILD_BUG_ON(sizeof(vring->va[0]) != 32);
vring->swhead = 0;
@@ -110,7 +113,8 @@ static int wil_vring_alloc(struct wil6210_priv *wil, struct vring *vring)
* we can use any
*/
for (i = 0; i < vring->size; i++) {
- volatile struct vring_tx_desc *_d = &(vring->va[i].tx);
+ volatile struct vring_tx_desc *_d = &vring->va[i].tx;
+
_d->dma.status = TX_DMA_STATUS_DU;
}
@@ -125,6 +129,7 @@ static void wil_txdesc_unmap(struct device *dev, struct vring_tx_desc *d,
{
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);
@@ -143,6 +148,18 @@ static void wil_vring_free(struct wil6210_priv *wil, struct vring *vring,
struct device *dev = wil_to_dev(wil);
size_t sz = vring->size * sizeof(vring->va[0]);
+ if (tx) {
+ int vring_index = vring - wil->vring_tx;
+
+ wil_dbg_misc(wil, "free Tx vring %d [%d] 0x%p:%pad 0x%p\n",
+ vring_index, vring->size, vring->va,
+ &vring->pa, vring->ctx);
+ } else {
+ wil_dbg_misc(wil, "free Rx vring [%d] 0x%p:%pad 0x%p\n",
+ vring->size, vring->va,
+ &vring->pa, vring->ctx);
+ }
+
while (!wil_vring_is_empty(vring)) {
dma_addr_t pa;
u16 dmalen;
@@ -191,11 +208,12 @@ static int wil_vring_alloc_skb(struct wil6210_priv *wil, struct vring *vring,
struct device *dev = wil_to_dev(wil);
unsigned int sz = RX_BUF_LEN;
struct vring_rx_desc dd, *d = &dd;
- volatile struct vring_rx_desc *_d = &(vring->va[i].rx);
+ volatile struct vring_rx_desc *_d = &vring->va[i].rx;
dma_addr_t pa;
/* TODO align */
struct sk_buff *skb = dev_alloc_skb(sz + headroom);
+
if (unlikely(!skb))
return -ENOMEM;
@@ -274,9 +292,11 @@ static void wil_rx_add_radiotap_header(struct wil6210_priv *wil,
*/
int len = min_t(int, 8 + sizeof(phy_data),
wil_rxdesc_phy_length(d));
+
if (len > 8) {
void *p = skb_tail_pointer(skb);
void *pa = PTR_ALIGN(p, 8);
+
if (skb_tailroom(skb) >= len + (pa - p)) {
phy_length = len - 8;
memcpy(phy_data, pa, phy_length);
@@ -372,13 +392,12 @@ static struct sk_buff *wil_vring_reap_rx(struct wil6210_priv *wil,
int cid;
struct wil_net_stats *stats;
-
BUILD_BUG_ON(sizeof(struct vring_rx_desc) > sizeof(skb->cb));
if (wil_vring_is_empty(vring))
return NULL;
- _d = &(vring->va[vring->swhead].rx);
+ _d = &vring->va[vring->swhead].rx;
if (!(_d->dma.status & RX_DMA_STATUS_DU)) {
/* it is not error, we just reached end of Rx done area */
return NULL;
@@ -414,7 +433,6 @@ static struct sk_buff *wil_vring_reap_rx(struct wil6210_priv *wil,
cid = wil_rxdesc_cid(d);
stats = &wil->sta[cid].stats;
stats->last_mcs_rx = wil_rxdesc_mcs(d);
- wil->stats.last_mcs_rx = stats->last_mcs_rx;
/* use radiotap header only if required */
if (ndev->type == ARPHRD_IEEE80211_RADIOTAP)
@@ -533,7 +551,7 @@ void wil_netif_rx_any(struct sk_buff *skb, struct net_device *ndev)
[GRO_NORMAL] = "GRO_NORMAL",
[GRO_DROP] = "GRO_DROP",
};
- wil_dbg_txrx(wil, "Rx complete %d bytes => %s,\n",
+ wil_dbg_txrx(wil, "Rx complete %d bytes => %s\n",
len, gro_res_str[rc]);
}
}
@@ -574,7 +592,6 @@ void wil_rx_handle(struct wil6210_priv *wil, int *quota)
else
wil_netif_rx_any(skb, ndev);
}
-
}
wil_rx_refill(wil, v->size);
}
@@ -584,6 +601,8 @@ int wil_rx_init(struct wil6210_priv *wil)
struct vring *vring = &wil->vring_rx;
int rc;
+ wil_dbg_misc(wil, "%s()\n", __func__);
+
if (vring->va) {
wil_err(wil, "Rx ring already allocated\n");
return -EINVAL;
@@ -613,6 +632,8 @@ void wil_rx_fini(struct wil6210_priv *wil)
{
struct vring *vring = &wil->vring_rx;
+ wil_dbg_misc(wil, "%s()\n", __func__);
+
if (vring->va)
wil_vring_free(wil, vring, 0);
}
@@ -647,6 +668,9 @@ int wil_vring_init_tx(struct wil6210_priv *wil, int id, int size,
struct vring *vring = &wil->vring_tx[id];
struct vring_tx_data *txdata = &wil->vring_tx_data[id];
+ wil_dbg_misc(wil, "%s() max_mpdu_size %d\n", __func__,
+ cmd.vring_cfg.tx_sw_ring.max_mpdu_size);
+
if (vring->va) {
wil_err(wil, "Tx ring [%d] already allocated\n", id);
rc = -EINVAL;
@@ -696,6 +720,8 @@ void wil_vring_fini_tx(struct wil6210_priv *wil, int id)
if (!vring->va)
return;
+ wil_dbg_misc(wil, "%s() id=%d\n", __func__, id);
+
/* make sure NAPI won't touch this vring */
wil->vring_tx_data[id].enabled = 0;
if (test_bit(wil_status_napi_en, &wil->status))
@@ -722,6 +748,7 @@ static struct vring *wil_find_tx_vring(struct wil6210_priv *wil,
for (i = 0; i < ARRAY_SIZE(wil->vring2cid_tid); i++) {
if (wil->vring2cid_tid[i][0] == cid) {
struct vring *v = &wil->vring_tx[i];
+
wil_dbg_txrx(wil, "%s(%pM) -> [%d]\n",
__func__, eth->h_dest, i);
if (v->va) {
@@ -741,6 +768,7 @@ static void wil_set_da_for_vring(struct wil6210_priv *wil,
{
struct ethhdr *eth = (void *)skb->data;
int cid = wil->vring2cid_tid[vring_index][0];
+
memcpy(eth->h_dest, wil->sta[cid].addr, ETH_ALEN);
}
@@ -751,7 +779,7 @@ static int wil_tx_vring(struct wil6210_priv *wil, struct vring *vring,
* duplicate skb and send it to other active vrings
*/
static struct vring *wil_tx_bcast(struct wil6210_priv *wil,
- struct sk_buff *skb)
+ struct sk_buff *skb)
{
struct vring *v, *v2;
struct sk_buff *skb2;
@@ -834,8 +862,8 @@ void wil_tx_desc_set_nr_frags(struct vring_tx_desc *d, int nr_frags)
}
static int wil_tx_desc_offload_cksum_set(struct wil6210_priv *wil,
- struct vring_tx_desc *d,
- struct sk_buff *skb)
+ struct vring_tx_desc *d,
+ struct sk_buff *skb)
{
int protocol;
@@ -903,10 +931,9 @@ static int wil_tx_vring(struct wil6210_priv *wil, struct vring *vring,
1 + nr_frags);
return -ENOMEM;
}
- _d = &(vring->va[i].tx);
+ _d = &vring->va[i].tx;
- pa = dma_map_single(dev, skb->data,
- skb_headlen(skb), DMA_TO_DEVICE);
+ pa = dma_map_single(dev, skb->data, skb_headlen(skb), DMA_TO_DEVICE);
wil_dbg_txrx(wil, "Tx skb %d bytes 0x%p -> %pad\n", skb_headlen(skb),
skb->data, &pa);
@@ -935,10 +962,11 @@ static int wil_tx_vring(struct wil6210_priv *wil, struct vring *vring,
const struct skb_frag_struct *frag =
&skb_shinfo(skb)->frags[f];
int len = skb_frag_size(frag);
+
i = (swhead + f + 1) % vring->size;
- _d = &(vring->va[i].tx);
+ _d = &vring->va[i].tx;
pa = skb_frag_dma_map(dev, frag, 0, skb_frag_size(frag),
- DMA_TO_DEVICE);
+ DMA_TO_DEVICE);
if (unlikely(dma_mapping_error(dev, pa)))
goto dma_error;
vring->ctx[i].mapped_as = wil_mapped_as_page;
@@ -983,7 +1011,7 @@ static int wil_tx_vring(struct wil6210_priv *wil, struct vring *vring,
i = (swhead + f) % vring->size;
ctx = &vring->ctx[i];
- _d = &(vring->va[i].tx);
+ _d = &vring->va[i].tx;
*d = *_d;
_d->dma.status = TX_DMA_STATUS_DU;
wil_txdesc_unmap(dev, d, ctx);
@@ -997,7 +1025,6 @@ static int wil_tx_vring(struct wil6210_priv *wil, struct vring *vring,
return -EINVAL;
}
-
netdev_tx_t wil_start_xmit(struct sk_buff *skb, struct net_device *ndev)
{
struct wil6210_priv *wil = ndev_to_wil(ndev);
@@ -1025,15 +1052,15 @@ netdev_tx_t wil_start_xmit(struct sk_buff *skb, struct net_device *ndev)
pr_once_fw = false;
/* find vring */
- if (is_unicast_ether_addr(eth->h_dest)) {
+ if (is_unicast_ether_addr(eth->h_dest))
vring = wil_find_tx_vring(wil, skb);
- } else {
+ else
vring = wil_tx_bcast(wil, skb);
- }
if (!vring) {
wil_dbg_txrx(wil, "No Tx VRING found for %pM\n", eth->h_dest);
goto drop;
}
+
/* set up vring entry */
rc = wil_tx_vring(wil, vring, skb);
diff --git a/drivers/net/wireless/ath/wil6210/txrx.h b/drivers/net/wireless/ath/wil6210/txrx.h
index bc5706a..de04671 100644
--- a/drivers/net/wireless/ath/wil6210/txrx.h
+++ b/drivers/net/wireless/ath/wil6210/txrx.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012 Qualcomm Atheros, Inc.
+ * Copyright (c) 2012-2014 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
@@ -20,9 +20,9 @@
#define BUF_SW_OWNED (1)
#define BUF_HW_OWNED (0)
-/* size of max. Rx packet */
-#define RX_BUF_LEN (2048)
-#define TX_BUF_LEN (2048)
+/* size of max. Tx/Rx buffers, as supported by FW */
+#define RX_BUF_LEN (2242)
+#define TX_BUF_LEN (2242)
/* how many bytes to reserve for rtap header? */
#define WIL6210_RTAP_SIZE (128)
@@ -237,7 +237,6 @@ struct vring_tx_mac {
#define DMA_CFG_DESC_TX_0_L4_TYPE_LEN 2
#define DMA_CFG_DESC_TX_0_L4_TYPE_MSK 0xC0000000 /* L4 type: 0-UDP, 2-TCP */
-
#define DMA_CFG_DESC_TX_OFFLOAD_CFG_MAC_LEN_POS 0
#define DMA_CFG_DESC_TX_OFFLOAD_CFG_MAC_LEN_LEN 7
#define DMA_CFG_DESC_TX_OFFLOAD_CFG_MAC_LEN_MSK 0x7F /* MAC hdr len */
@@ -246,7 +245,6 @@ struct vring_tx_mac {
#define DMA_CFG_DESC_TX_OFFLOAD_CFG_L3T_IPV4_LEN 1
#define DMA_CFG_DESC_TX_OFFLOAD_CFG_L3T_IPV4_MSK 0x80 /* 1-IPv4, 0-IPv6 */
-
#define TX_DMA_STATUS_DU BIT(0)
struct vring_tx_dma {
@@ -347,7 +345,6 @@ struct vring_rx_mac {
#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)
diff --git a/drivers/net/wireless/ath/wil6210/wil6210.h b/drivers/net/wireless/ath/wil6210/wil6210.h
index 67e9624..ce6488e 100644
--- a/drivers/net/wireless/ath/wil6210/wil6210.h
+++ b/drivers/net/wireless/ath/wil6210/wil6210.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012 Qualcomm Atheros, Inc.
+ * Copyright (c) 2012-2014 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
@@ -21,8 +21,14 @@
#include <linux/wireless.h>
#include <net/cfg80211.h>
#include <linux/timex.h>
+#include "wil_platform.h"
+
+extern bool no_fw_recovery;
#define WIL_NAME "wil6210"
+#define WIL_FW_NAME "wil6210.fw"
+
+#define WIL_MAX_BUS_REQUEST_KBPS 800000 /* ~6.1Gbps */
struct wil_board {
int board;
@@ -47,7 +53,9 @@ static inline u32 WIL_GET_BITS(u32 x, int b0, int b1)
#define WIL6210_MAX_TX_RINGS (24) /* HW limit */
#define WIL6210_MAX_CID (8) /* HW limit */
#define WIL6210_NAPI_BUDGET (16) /* arbitrary */
-#define WIL6210_ITR_TRSH (10000) /* arbitrary - about 15 IRQs/msec */
+/* Max supported by wil6210 value for interrupt threshold is 5sec. */
+#define WIL6210_ITR_TRSH_MAX (5000000)
+#define WIL6210_ITR_TRSH_DEFAULT (300) /* usec */
#define WIL6210_FW_RECOVERY_RETRIES (5) /* try to recover this many times */
#define WIL6210_FW_RECOVERY_TO msecs_to_jiffies(5000)
#define WIL6210_SCAN_TO msecs_to_jiffies(10000)
@@ -86,22 +94,29 @@ struct RGF_ICR {
/* registers - FW addresses */
#define RGF_USER_USAGE_1 (0x880004)
+#define RGF_USER_USAGE_6 (0x880018)
#define RGF_USER_HW_MACHINE_STATE (0x8801dc)
#define HW_MACHINE_BOOT_DONE (0x3fffffd)
#define RGF_USER_USER_CPU_0 (0x8801e0)
+ #define BIT_USER_USER_CPU_MAN_RST BIT(1) /* user_cpu_man_rst */
#define RGF_USER_MAC_CPU_0 (0x8801fc)
+ #define BIT_USER_MAC_CPU_MAN_RST BIT(1) /* mac_cpu_man_rst */
#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_CAR_AHB_SW_SEL BIT(1) /* ref clk/PLL */
#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)
#define RGF_USER_CLKS_CTL_SW_RST_VEC_3 (0x880b10)
#define RGF_USER_CLKS_CTL_SW_RST_MASK_0 (0x880b14)
+ #define BIT_HPAL_PERST_FROM_PAD BIT(6)
+ #define BIT_CAR_PERST_RST BIT(7)
#define RGF_USER_USER_ICR (0x880b4c) /* struct RGF_ICR */
#define BIT_USER_USER_ICR_SW_INT_2 BIT(18)
#define RGF_USER_CLKS_CTL_EXT_SW_RST_VEC_0 (0x880c18)
+#define RGF_USER_CLKS_CTL_EXT_SW_RST_VEC_1 (0x880c2c)
#define RGF_DMA_EP_TX_ICR (0x881bb4) /* struct RGF_ICR */
#define BIT_DMA_EP_TX_ICR_TX_DONE BIT(0)
@@ -133,6 +148,11 @@ struct RGF_ICR {
#define RGF_HP_CTRL (0x88265c)
#define RGF_PCIE_LOS_COUNTER_CTL (0x882dc4)
+/* MAC timer, usec, for packet lifetime */
+#define RGF_MAC_MTRL_COUNTER_0 (0x886aa8)
+
+#define RGF_CAF_ICR (0x88946c) /* struct RGF_ICR */
+
/* popular locations */
#define HOST_MBOX HOSTADDR(RGF_USER_USER_SCRATCH_PAD)
#define HOST_SW_INT (HOSTADDR(RGF_USER_USER_ICR) + \
@@ -151,6 +171,7 @@ struct fw_map {
u32 host; /* PCI/Host address - BAR0 + 0x880000 */
const char *name; /* for debugfs */
};
+
/* array size should be in sync with actual definition in the wmi.c */
extern const struct fw_map fw_mapping[7];
@@ -300,18 +321,12 @@ struct pci_dev;
* @timeout: reset timer value (in TUs).
* @dialog_token: dialog token for aggregation session
* @rcu_head: RCU head used for freeing this struct
- * @reorder_lock: serializes access to reorder buffer, see below.
*
* This structure's lifetime is managed by RCU, assignments to
* the array holding it must hold the aggregation mutex.
*
- * The @reorder_lock is used to protect the members of this
- * struct, except for @timeout, @buf_size and @dialog_token,
- * which are constant across the lifetime of the struct (the
- * dialog token being used only for debugging).
*/
struct wil_tid_ampdu_rx {
- spinlock_t reorder_lock; /* see above */
struct sk_buff **reorder_buf;
unsigned long *reorder_time;
struct timer_list session_timer;
@@ -327,17 +342,6 @@ struct wil_tid_ampdu_rx {
bool first_time; /* is it 1-st time this buffer used? */
};
-struct wil6210_stats {
- u64 tsf;
- u32 snr;
- u16 last_mcs_rx;
- u16 bf_mcs; /* last BF, used for Tx */
- u16 my_rx_sector;
- u16 my_tx_sector;
- u16 peer_rx_sector;
- u16 peer_tx_sector;
-};
-
enum wil_sta_status {
wil_sta_unused = 0,
wil_sta_conn_pending = 1,
@@ -371,10 +375,17 @@ struct wil_sta_info {
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 */
unsigned long tid_rx_timer_expired[BITS_TO_LONGS(WIL_STA_TID_NUM)];
unsigned long tid_rx_stop_requested[BITS_TO_LONGS(WIL_STA_TID_NUM)];
};
+enum {
+ fw_recovery_idle = 0,
+ fw_recovery_pending = 1,
+ fw_recovery_running = 2,
+};
+
struct wil6210_priv {
struct pci_dev *pdev;
int n_msi;
@@ -385,18 +396,22 @@ struct wil6210_priv {
u32 hw_version;
struct wil_board *board;
u8 n_mids; /* number of additional MIDs as reported by FW */
- int recovery_count; /* num of FW recovery attempts in a short time */
+ u32 recovery_count; /* num of FW recovery attempts in a short time */
+ u32 recovery_state; /* FW recovery state machine */
unsigned long last_fw_recovery; /* jiffies of last fw recovery */
+ wait_queue_head_t wq; /* for all wait_event() use */
/* profile */
u32 monitor_flags;
u32 secure_pcp; /* create secure PCP? */
int sinfo_gen;
+ u32 itr_trsh;
/* cached ISR registers */
u32 isr_misc;
/* mailbox related */
struct mutex wmi_mutex;
struct wil6210_mbox_ctl mbox_ctl;
struct completion wmi_ready;
+ struct completion wmi_call;
u16 wmi_seq;
u16 reply_id; /**< wait for this WMI event */
void *reply_buf;
@@ -430,11 +445,13 @@ struct wil6210_priv {
struct mutex mutex; /* for wil6210_priv access in wil_{up|down} */
/* statistics */
- struct wil6210_stats stats;
atomic_t isr_count_rx, isr_count_tx;
/* debugfs */
struct dentry *debug;
struct debugfs_blob_wrapper blobs[ARRAY_SIZE(fw_mapping)];
+
+ void *platform_handle;
+ struct wil_platform_ops platform_ops;
};
#define wil_to_wiphy(i) (i->wdev->wiphy)
@@ -444,10 +461,11 @@ struct wil6210_priv {
#define wdev_to_wil(w) (struct wil6210_priv *)(wdev_priv(w))
#define wil_to_ndev(i) (wil_to_wdev(i)->netdev)
#define ndev_to_wil(n) (wdev_to_wil(n->ieee80211_ptr))
+#define wil_to_pcie_dev(i) (&i->pdev->dev)
-int wil_dbg_trace(struct wil6210_priv *wil, const char *fmt, ...);
-int wil_err(struct wil6210_priv *wil, const char *fmt, ...);
-int wil_info(struct wil6210_priv *wil, const char *fmt, ...);
+void wil_dbg_trace(struct wil6210_priv *wil, const char *fmt, ...);
+void wil_err(struct wil6210_priv *wil, const char *fmt, ...);
+void wil_info(struct wil6210_priv *wil, const char *fmt, ...);
#define wil_dbg(wil, fmt, arg...) do { \
netdev_dbg(wil_to_ndev(wil), fmt, ##arg); \
wil_dbg_trace(wil, fmt, ##arg); \
@@ -458,6 +476,7 @@ int wil_info(struct wil6210_priv *wil, const char *fmt, ...);
#define wil_dbg_wmi(wil, fmt, arg...) wil_dbg(wil, "DBG[ WMI]" fmt, ##arg)
#define wil_dbg_misc(wil, fmt, arg...) wil_dbg(wil, "DBG[MISC]" fmt, ##arg)
+#if defined(CONFIG_DYNAMIC_DEBUG)
#define wil_hex_dump_txrx(prefix_str, prefix_type, rowsize, \
groupsize, buf, len, ascii) \
print_hex_dump_debug("DBG[TXRX]" prefix_str,\
@@ -469,6 +488,19 @@ int wil_info(struct wil6210_priv *wil, const char *fmt, ...);
print_hex_dump_debug("DBG[ WMI]" prefix_str,\
prefix_type, rowsize, \
groupsize, buf, len, ascii)
+#else /* defined(CONFIG_DYNAMIC_DEBUG) */
+static inline
+void wil_hex_dump_txrx(const char *prefix_str, int prefix_type, int rowsize,
+ int groupsize, const void *buf, size_t len, bool ascii)
+{
+}
+
+static inline
+void wil_hex_dump_wmi(const char *prefix_str, int prefix_type, int rowsize,
+ int groupsize, const void *buf, size_t len, bool ascii)
+{
+}
+#endif /* defined(CONFIG_DYNAMIC_DEBUG) */
void wil_memcpy_fromio_32(void *dst, const volatile void __iomem *src,
size_t count);
@@ -482,13 +514,18 @@ 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_set_itr_trsh(struct wil6210_priv *wil);
void wil_fw_error_recovery(struct wil6210_priv *wil);
+void wil_set_recovery_state(struct wil6210_priv *wil, int state);
void wil_link_on(struct wil6210_priv *wil);
void wil_link_off(struct wil6210_priv *wil);
int wil_up(struct wil6210_priv *wil);
+int __wil_up(struct wil6210_priv *wil);
int wil_down(struct wil6210_priv *wil);
+int __wil_down(struct wil6210_priv *wil);
void wil_mbox_ring_le2cpus(struct wil6210_mbox_ring *r);
int wil_find_cid(struct wil6210_priv *wil, const u8 *mac);
+void wil_set_ethtoolops(struct net_device *ndev);
void __iomem *wmi_buffer(struct wil6210_priv *wil, __le32 ptr);
void __iomem *wmi_addr(struct wil6210_priv *wil, u32 ptr);
@@ -519,8 +556,10 @@ 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);
-void wil6210_enable_irq(struct wil6210_priv *wil);
+void wil_mask_irq(struct wil6210_priv *wil);
+void wil_unmask_irq(struct wil6210_priv *wil);
+void wil_disable_irq(struct wil6210_priv *wil);
+void wil_enable_irq(struct wil6210_priv *wil);
int wil_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
struct cfg80211_mgmt_tx_params *params,
u64 *cookie);
@@ -556,4 +595,7 @@ void wil6210_unmask_irq_rx(struct wil6210_priv *wil);
int wil_iftype_nl2wmi(enum nl80211_iftype type);
+int wil_ioctl(struct wil6210_priv *wil, void __user *data, int cmd);
+int wil_request_firmware(struct wil6210_priv *wil, const char *name);
+
#endif /* __WIL6210_H__ */
diff --git a/drivers/net/wireless/ath/wil6210/wil_platform.c b/drivers/net/wireless/ath/wil6210/wil_platform.c
new file mode 100644
index 0000000..8f1d78f
--- /dev/null
+++ b/drivers/net/wireless/ath/wil6210/wil_platform.c
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2014 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 "linux/device.h"
+#include "wil_platform.h"
+
+#ifdef CONFIG_WIL6210_PLATFORM_MSM
+#include "wil_platform_msm.h"
+#endif
+
+/**
+ * wil_platform_init() - wil6210 platform module init
+ *
+ * The function must be called before all other functions in this module.
+ * It returns a handle which is used with the rest of the API
+ *
+ */
+void *wil_platform_init(struct device *dev, struct wil_platform_ops *ops)
+{
+ void *handle = NULL;
+
+ if (!ops) {
+ dev_err(dev, "Invalid parameter. Cannot init platform module\n");
+ return NULL;
+ }
+
+#ifdef CONFIG_WIL6210_PLATFORM_MSM
+ handle = wil_platform_msm_init(dev, ops);
+ if (handle)
+ return handle;
+#endif
+
+ /* other platform specific init functions should be called here */
+
+ return handle;
+}
diff --git a/drivers/net/wireless/ath/wil6210/wil_platform.h b/drivers/net/wireless/ath/wil6210/wil_platform.h
new file mode 100644
index 0000000..158c73b
--- /dev/null
+++ b/drivers/net/wireless/ath/wil6210/wil_platform.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2014 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.
+ */
+
+#ifndef __WIL_PLATFORM_H__
+#define __WIL_PLATFORM_H__
+
+struct device;
+
+/**
+ * struct wil_platform_ops - wil platform module callbacks
+ */
+struct wil_platform_ops {
+ int (*bus_request)(void *handle, uint32_t kbps /* KBytes/Sec */);
+ int (*suspend)(void *handle);
+ int (*resume)(void *handle);
+ void (*uninit)(void *handle);
+};
+
+void *wil_platform_init(struct device *dev, struct wil_platform_ops *ops);
+
+#endif /* __WIL_PLATFORM_H__ */
diff --git a/drivers/net/wireless/ath/wil6210/wil_platform_msm.c b/drivers/net/wireless/ath/wil6210/wil_platform_msm.c
new file mode 100644
index 0000000..b354a74
--- /dev/null
+++ b/drivers/net/wireless/ath/wil6210/wil_platform_msm.c
@@ -0,0 +1,257 @@
+/*
+ * Copyright (c) 2014 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 <linux/of.h>
+#include <linux/slab.h>
+#include <linux/msm-bus.h>
+
+#include "wil_platform.h"
+#include "wil_platform_msm.h"
+
+/**
+ * struct wil_platform_msm - wil6210 msm platform module info
+ *
+ * @dev: device object
+ * @msm_bus_handle: handle for using msm_bus API
+ * @pdata: bus scale info retrieved from DT
+ */
+struct wil_platform_msm {
+ struct device *dev;
+ uint32_t msm_bus_handle;
+ struct msm_bus_scale_pdata *pdata;
+};
+
+#define KBTOB(a) (a * 1000ULL)
+
+/**
+ * wil_platform_get_pdata() - Generate bus client data from device tree
+ * provided by clients.
+ *
+ * dev: device object
+ * of_node: Device tree node to extract information from
+ *
+ * The function returns a valid pointer to the allocated bus-scale-pdata
+ * if the vectors were correctly read from the client's device node.
+ * Any error in reading or parsing the device node will return NULL
+ * to the caller.
+ */
+static struct msm_bus_scale_pdata *wil_platform_get_pdata(
+ struct device *dev,
+ struct device_node *of_node)
+{
+ struct msm_bus_scale_pdata *pdata;
+ struct msm_bus_paths *usecase;
+ int i, j, ret, len;
+ unsigned int num_usecases, num_paths, mem_size;
+ const uint32_t *vec_arr;
+ struct msm_bus_vectors *vectors;
+
+ /* first read num_usecases and num_paths so we can calculate
+ * amount of memory to allocate
+ */
+ ret = of_property_read_u32(of_node, "qcom,msm-bus,num-cases",
+ &num_usecases);
+ if (ret) {
+ dev_err(dev, "Error: num-usecases not found\n");
+ return NULL;
+ }
+
+ ret = of_property_read_u32(of_node, "qcom,msm-bus,num-paths",
+ &num_paths);
+ if (ret) {
+ dev_err(dev, "Error: num_paths not found\n");
+ return NULL;
+ }
+
+ /* pdata memory layout:
+ * msm_bus_scale_pdata
+ * msm_bus_paths[num_usecases]
+ * msm_bus_vectors[num_usecases][num_paths]
+ */
+ mem_size = sizeof(struct msm_bus_scale_pdata) +
+ sizeof(struct msm_bus_paths) * num_usecases +
+ sizeof(struct msm_bus_vectors) * num_usecases * num_paths;
+
+ pdata = kzalloc(mem_size, GFP_KERNEL);
+ if (!pdata)
+ return NULL;
+
+ ret = of_property_read_string(of_node, "qcom,msm-bus,name",
+ &pdata->name);
+ if (ret) {
+ dev_err(dev, "Error: Client name not found\n");
+ goto err;
+ }
+
+ if (of_property_read_bool(of_node, "qcom,msm-bus,active-only")) {
+ pdata->active_only = 1;
+ } else {
+ dev_info(dev, "active_only flag absent.\n");
+ dev_info(dev, "Using dual context by default\n");
+ }
+
+ pdata->num_usecases = num_usecases;
+ pdata->usecase = (struct msm_bus_paths *)(pdata + 1);
+
+ vec_arr = of_get_property(of_node, "qcom,msm-bus,vectors-KBps", &len);
+ if (vec_arr == NULL) {
+ dev_err(dev, "Error: Vector array not found\n");
+ goto err;
+ }
+
+ if (len != num_usecases * num_paths * sizeof(uint32_t) * 4) {
+ dev_err(dev, "Error: Length-error on getting vectors\n");
+ goto err;
+ }
+
+ vectors = (struct msm_bus_vectors *)(pdata->usecase + num_usecases);
+ for (i = 0; i < num_usecases; i++) {
+ usecase = &pdata->usecase[i];
+ usecase->num_paths = num_paths;
+ usecase->vectors = &vectors[i];
+
+ for (j = 0; j < num_paths; j++) {
+ int index = ((i * num_paths) + j) * 4;
+
+ usecase->vectors[j].src = be32_to_cpu(vec_arr[index]);
+ usecase->vectors[j].dst =
+ be32_to_cpu(vec_arr[index + 1]);
+ usecase->vectors[j].ab = (uint64_t)
+ KBTOB(be32_to_cpu(vec_arr[index + 2]));
+ usecase->vectors[j].ib = (uint64_t)
+ KBTOB(be32_to_cpu(vec_arr[index + 3]));
+ }
+ }
+
+ return pdata;
+
+err:
+ kfree(pdata);
+
+ return NULL;
+}
+
+/* wil_platform API (callbacks) */
+
+static int wil_platform_bus_request(void *handle,
+ uint32_t kbps /* KBytes/Sec */)
+{
+ int rc, i;
+ struct wil_platform_msm *msm = (struct wil_platform_msm *)handle;
+ int vote = 0; /* vote 0 in case requested kbps cannot be satisfied */
+ struct msm_bus_paths *usecase;
+ uint32_t usecase_kbps;
+ uint32_t min_kbps = ~0;
+
+ /* find the lowest usecase that is bigger than requested kbps */
+ for (i = 0; i < msm->pdata->num_usecases; i++) {
+ usecase = &msm->pdata->usecase[i];
+ /* assume we have single path (vectors[0]). If we ever
+ * have multiple paths, need to define the behavior */
+ usecase_kbps = div64_u64(usecase->vectors[0].ib, 1000);
+ if (usecase_kbps >= kbps && usecase_kbps < min_kbps) {
+ min_kbps = usecase_kbps;
+ vote = i;
+ }
+ }
+
+ rc = msm_bus_scale_client_update_request(msm->msm_bus_handle, vote);
+ if (rc)
+ dev_err(msm->dev, "Failed msm_bus voting. kbps=%d vote=%d, rc=%d\n",
+ kbps, vote, rc);
+ else
+ /* TOOD: remove */
+ dev_info(msm->dev, "msm_bus_scale_client_update_request succeeded. kbps=%d vote=%d\n",
+ kbps, vote);
+
+ return rc;
+}
+
+static void wil_platform_uninit(void *handle)
+{
+ struct wil_platform_msm *msm = (struct wil_platform_msm *)handle;
+
+ dev_info(msm->dev, "wil_platform_uninit\n");
+
+ if (msm->msm_bus_handle)
+ msm_bus_scale_unregister_client(msm->msm_bus_handle);
+
+ kfree(msm->pdata);
+ kfree(msm);
+}
+
+static int wil_platform_msm_bus_register(struct wil_platform_msm *msm,
+ struct device_node *node)
+{
+ msm->pdata = wil_platform_get_pdata(msm->dev, node);
+ if (!msm->pdata) {
+ dev_err(msm->dev, "Failed getting DT info\n");
+ return -EINVAL;
+ }
+
+ msm->msm_bus_handle = msm_bus_scale_register_client(msm->pdata);
+ if (!msm->msm_bus_handle) {
+ dev_err(msm->dev, "Failed msm_bus registration\n");
+ return -EINVAL;
+ }
+
+ dev_info(msm->dev, "msm_bus registration succeeded! handle 0x%x\n",
+ msm->msm_bus_handle);
+
+ return 0;
+}
+
+/**
+ * wil_platform_msm_init() - wil6210 msm platform module init
+ *
+ * The function must be called before all other functions in this module.
+ * It returns a handle which is used with the rest of the API
+ *
+ */
+void *wil_platform_msm_init(struct device *dev, struct wil_platform_ops *ops)
+{
+ struct device_node *of_node;
+ struct wil_platform_msm *msm;
+ int rc;
+
+ of_node = of_find_compatible_node(NULL, NULL, "qcom,wil6210");
+ if (!of_node) {
+ /* this could mean non-msm platform */
+ dev_err(dev, "DT node not found\n");
+ return NULL;
+ }
+
+ msm = kzalloc(sizeof(*msm), GFP_KERNEL);
+ if (!msm)
+ return NULL;
+
+ msm->dev = dev;
+
+ /* register with msm_bus module for scaling requests */
+ rc = wil_platform_msm_bus_register(msm, of_node);
+ if (rc)
+ goto cleanup;
+
+ memset(ops, 0, sizeof(*ops));
+ ops->bus_request = wil_platform_bus_request;
+ ops->uninit = wil_platform_uninit;
+
+ return (void *)msm;
+
+cleanup:
+ kfree(msm);
+ return NULL;
+}
diff --git a/drivers/net/wireless/ath/wil6210/wil_platform_msm.h b/drivers/net/wireless/ath/wil6210/wil_platform_msm.h
new file mode 100644
index 0000000..2f2229e
--- /dev/null
+++ b/drivers/net/wireless/ath/wil6210/wil_platform_msm.h
@@ -0,0 +1,24 @@
+/*
+ * Copyright (c) 2014 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.
+ */
+
+#ifndef __WIL_PLATFORM__MSM_H__
+#define __WIL_PLATFORM_MSM_H__
+
+#include "wil_platform.h"
+
+void *wil_platform_msm_init(struct device *dev, struct wil_platform_ops *ops);
+
+#endif /* __WIL_PLATFORM__MSM_H__ */
diff --git a/drivers/net/wireless/ath/wil6210/wmi.c b/drivers/net/wireless/ath/wil6210/wmi.c
index 1d1d0af..4311df9 100644
--- a/drivers/net/wireless/ath/wil6210/wmi.c
+++ b/drivers/net/wireless/ath/wil6210/wmi.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012 Qualcomm Atheros, Inc.
+ * Copyright (c) 2012-2014 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
@@ -14,6 +14,7 @@
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
+#include <linux/moduleparam.h>
#include <linux/etherdevice.h>
#include <linux/if_arp.h>
@@ -22,6 +23,10 @@
#include "wmi.h"
#include "trace.h"
+static uint max_assoc_sta = 1;
+module_param(max_assoc_sta, uint, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(max_assoc_sta, " Max number of stations associated to the AP");
+
/**
* WMI event receiving - theory of operations
*
@@ -152,6 +157,7 @@ int wmi_read_hdr(struct wil6210_priv *wil, __le32 ptr,
struct wil6210_mbox_hdr *hdr)
{
void __iomem *src = wmi_buffer(wil, ptr);
+
if (!src)
return -EINVAL;
@@ -273,6 +279,7 @@ static void wmi_evt_ready(struct wil6210_priv *wil, int id, void *d, int len)
struct net_device *ndev = wil_to_ndev(wil);
struct wireless_dev *wdev = wil->wdev;
struct wmi_ready_event *evt = d;
+
wil->fw_version = le32_to_cpu(evt->sw_version);
wil->n_mids = evt->numof_additional_mids;
@@ -292,8 +299,9 @@ static void wmi_evt_fw_ready(struct wil6210_priv *wil, int id, void *d,
{
wil_dbg_wmi(wil, "WMI: got FW ready event\n");
+ wil_set_recovery_state(wil, fw_recovery_idle);
set_bit(wil_status_fwready, &wil->status);
- /* reuse wmi_ready for the firmware ready indication */
+ /* let the reset sequence continue */
complete(&wil->wmi_ready);
}
@@ -346,11 +354,11 @@ static void wmi_evt_rx_mgmt(struct wil6210_priv *wil, int id, void *d, int len)
rx_mgmt_frame->bssid);
cfg80211_put_bss(wiphy, bss);
} else {
- wil_err(wil, "cfg80211_inform_bss() failed\n");
+ wil_err(wil, "cfg80211_inform_bss_frame() failed\n");
}
} else {
cfg80211_rx_mgmt(wil->wdev, freq, signal,
- (void *)rx_mgmt_frame, d_len, 0, GFP_KERNEL);
+ (void *)rx_mgmt_frame, d_len, 0);
}
}
@@ -482,33 +490,6 @@ static void wmi_evt_disconnect(struct wil6210_priv *wil, int id,
mutex_unlock(&wil->mutex);
}
-static void wmi_evt_notify(struct wil6210_priv *wil, int id, void *d, int len)
-{
- struct wmi_notify_req_done_event *evt = d;
-
- if (len < sizeof(*evt)) {
- wil_err(wil, "Short NOTIFY event\n");
- return;
- }
-
- wil->stats.tsf = le64_to_cpu(evt->tsf);
- wil->stats.snr = le32_to_cpu(evt->snr_val);
- wil->stats.bf_mcs = le16_to_cpu(evt->bf_mcs);
- wil->stats.my_rx_sector = le16_to_cpu(evt->my_rx_sector);
- wil->stats.my_tx_sector = le16_to_cpu(evt->my_tx_sector);
- wil->stats.peer_rx_sector = le16_to_cpu(evt->other_rx_sector);
- wil->stats.peer_tx_sector = le16_to_cpu(evt->other_tx_sector);
- wil_dbg_wmi(wil, "Link status, MCS %d TSF 0x%016llx\n"
- "BF status 0x%08x SNR 0x%08x SQI %d%%\n"
- "Tx Tpt %d goodput %d Rx goodput %d\n"
- "Sectors(rx:tx) my %d:%d peer %d:%d\n",
- wil->stats.bf_mcs, wil->stats.tsf, evt->status,
- wil->stats.snr, evt->sqi, le32_to_cpu(evt->tx_tpt),
- le32_to_cpu(evt->tx_goodput), le32_to_cpu(evt->rx_goodput),
- wil->stats.my_rx_sector, wil->stats.my_tx_sector,
- wil->stats.peer_rx_sector, wil->stats.peer_tx_sector);
-}
-
/*
* Firmware reports EAPOL frame using WME event.
* Reconstruct Ethernet frame and deliver it via normal Rx
@@ -617,27 +598,40 @@ static void wmi_evt_ba_status(struct wil6210_priv *wil, int id, void *d,
return;
}
+ mutex_lock(&wil->mutex);
+
cid = wil->vring2cid_tid[evt->ringid][0];
if (cid >= WIL6210_MAX_CID) {
wil_err(wil, "invalid CID %d for vring %d\n", cid, evt->ringid);
- return;
+ goto out;
}
sta = &wil->sta[cid];
if (sta->status == wil_sta_unused) {
wil_err(wil, "CID %d unused\n", cid);
- return;
+ goto out;
}
wil_dbg_wmi(wil, "BACK for CID %d %pM\n", cid, sta->addr);
for (i = 0; i < WIL_STA_TID_NUM; i++) {
- struct wil_tid_ampdu_rx *r = sta->tid_rx[i];
+ struct wil_tid_ampdu_rx *r;
+ unsigned long flags;
+
+ spin_lock_irqsave(&sta->tid_rx_lock, flags);
+
+ r = sta->tid_rx[i];
sta->tid_rx[i] = NULL;
wil_tid_ampdu_rx_free(wil, r);
+
+ spin_unlock_irqrestore(&sta->tid_rx_lock, flags);
+
if ((evt->status == WMI_BA_AGREED) && evt->agg_wsize)
sta->tid_rx[i] = wil_tid_ampdu_rx_alloc(wil,
evt->agg_wsize, 0);
}
+
+out:
+ mutex_unlock(&wil->mutex);
}
static const struct {
@@ -651,7 +645,6 @@ static const struct {
{WMI_SCAN_COMPLETE_EVENTID, wmi_evt_scan_complete},
{WMI_CONNECT_EVENTID, wmi_evt_connect},
{WMI_DISCONNECT_EVENTID, wmi_evt_disconnect},
- {WMI_NOTIFY_REQ_DONE_EVENTID, wmi_evt_notify},
{WMI_EAPOL_RX_EVENTID, wmi_evt_eapol_rx},
{WMI_DATA_PORT_OPEN_EVENTID, wmi_evt_linkup},
{WMI_WBE_LINKDOWN_EVENTID, wmi_evt_linkdown},
@@ -676,7 +669,7 @@ void wmi_recv_cmd(struct wil6210_priv *wil)
unsigned n;
if (!test_bit(wil_status_reset_done, &wil->status)) {
- wil_err(wil, "Reset not completed\n");
+ wil_err(wil, "Reset in progress. Cannot handle WMI event\n");
return;
}
@@ -731,6 +724,7 @@ void wmi_recv_cmd(struct wil6210_priv *wil)
struct wil6210_mbox_hdr_wmi *wmi = &evt->event.wmi;
u16 id = le16_to_cpu(wmi->id);
u32 tstamp = le32_to_cpu(wmi->timestamp);
+
wil_dbg_wmi(wil, "WMI event 0x%04x MID %d @%d msec\n",
id, wmi->mid, tstamp);
trace_wil6210_wmi_event(wmi, &wmi[1],
@@ -771,8 +765,8 @@ int wmi_call(struct wil6210_priv *wil, u16 cmdid, void *buf, u16 len,
wil->reply_id = reply_id;
wil->reply_buf = reply;
wil->reply_size = reply_size;
- remain = wait_for_completion_timeout(&wil->wmi_ready,
- msecs_to_jiffies(to_msec));
+ remain = wait_for_completion_timeout(&wil->wmi_call,
+ msecs_to_jiffies(to_msec));
if (0 == remain) {
wil_err(wil, "wmi_call(0x%04x->0x%04x) timeout %d msec\n",
cmdid, reply_id, to_msec);
@@ -822,7 +816,7 @@ int wmi_pcp_start(struct wil6210_priv *wil, int bi, u8 wmi_nettype, u8 chan)
.network_type = wmi_nettype,
.disable_sec_offload = 1,
.channel = chan - 1,
- .pcp_max_assoc_sta = WIL6210_MAX_CID,
+ .pcp_max_assoc_sta = max_assoc_sta,
};
struct {
struct wil6210_mbox_hdr_wmi wmi;
@@ -832,6 +826,14 @@ int wmi_pcp_start(struct wil6210_priv *wil, int bi, u8 wmi_nettype, u8 chan)
if (!wil->secure_pcp)
cmd.disable_sec = 1;
+ if ((cmd.pcp_max_assoc_sta > WIL6210_MAX_CID) ||
+ (cmd.pcp_max_assoc_sta <= 0)) {
+ wil_info(wil,
+ "Requested connection limit %u, valid values are 1 - %d. Setting to %d\n",
+ max_assoc_sta, WIL6210_MAX_CID, WIL6210_MAX_CID);
+ cmd.pcp_max_assoc_sta = WIL6210_MAX_CID;
+ }
+
/*
* Processing time may be huge, in case of secure AP it takes about
* 3500ms for FW to start AP
@@ -968,8 +970,11 @@ int wmi_set_ie(struct wil6210_priv *wil, u8 type, u16 ie_len, const void *ie)
int rc;
u16 len = sizeof(struct wmi_set_appie_cmd) + ie_len;
struct wmi_set_appie_cmd *cmd = kzalloc(len, GFP_KERNEL);
+
if (!cmd)
return -ENOMEM;
+ if (!ie)
+ ie_len = 0;
cmd->mgmt_frm_type = type;
/* BUG: FW API define ieLen as u8. Will fix FW */
@@ -1143,6 +1148,9 @@ static void wmi_event_handle(struct wil6210_priv *wil,
struct wil6210_mbox_hdr_wmi *wmi = (void *)(&hdr[1]);
void *evt_data = (void *)(&wmi[1]);
u16 id = le16_to_cpu(wmi->id);
+
+ wil_dbg_wmi(wil, "Handle WMI 0x%04x (reply_id 0x%04x)\n",
+ id, wil->reply_id);
/* check if someone waits for this event */
if (wil->reply_id && wil->reply_id == id) {
if (wil->reply_buf) {
@@ -1153,7 +1161,7 @@ static void wmi_event_handle(struct wil6210_priv *wil,
len - sizeof(*wmi));
}
wil_dbg_wmi(wil, "Complete WMI 0x%04x\n", id);
- complete(&wil->wmi_ready);
+ complete(&wil->wmi_call);
return;
}
/* unsolicited event */
@@ -1199,9 +1207,11 @@ void wmi_event_worker(struct work_struct *work)
struct pending_wmi_event *evt;
struct list_head *lh;
+ wil_dbg_wmi(wil, "Start %s\n", __func__);
while ((lh = next_wmi_ev(wil)) != NULL) {
evt = list_entry(lh, struct pending_wmi_event, list);
wmi_event_handle(wil, &evt->event.hdr);
kfree(evt);
}
+ wil_dbg_wmi(wil, "Finished %s\n", __func__);
}
diff --git a/drivers/net/wireless/ath/wil6210/wmi.h b/drivers/net/wireless/ath/wil6210/wmi.h
index 17334c8..27b9743 100644
--- a/drivers/net/wireless/ath/wil6210/wmi.h
+++ b/drivers/net/wireless/ath/wil6210/wmi.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012 Qualcomm Atheros, Inc.
+ * Copyright (c) 2012-2014 Qualcomm Atheros, Inc.
* Copyright (c) 2006-2012 Wilocity .
*
* Permission to use, copy, modify, and/or distribute this software for any
@@ -179,7 +179,6 @@ enum wmi_crypto_type {
WMI_CRYPT_AES_GCMP = 0x20,
};
-
enum wmi_connect_ctrl_flag_bits {
WMI_CONNECT_ASSOC_POLICY_USER = 0x0001,
WMI_CONNECT_SEND_REASSOC = 0x0002,
@@ -219,7 +218,6 @@ struct wmi_disconnect_sta_cmd {
__le16 disconnect_reason;
} __packed;
-
/*
* WMI_SET_PMK_CMDID
*/
@@ -234,7 +232,6 @@ struct wmi_set_pmk_cmd {
u8 pmk[WMI_PMK_LEN];
} __packed;
-
/*
* WMI_SET_PASSPHRASE_CMDID
*/
@@ -273,7 +270,6 @@ struct wmi_delete_cipher_key_cmd {
u8 mac[WMI_MAC_LEN];
} __packed;
-
/*
* WMI_START_SCAN_CMDID
*
@@ -325,7 +321,6 @@ struct wmi_probed_ssid_cmd {
u8 ssid[WMI_MAX_SSID_LEN];
} __packed;
-
/*
* WMI_SET_APPIE_CMDID
* Add Application specified IE to a management frame
@@ -351,7 +346,6 @@ struct wmi_set_appie_cmd {
u8 ie_info[0];
} __packed;
-
/*
* WMI_PXMT_RANGE_CFG_CMDID
*/
@@ -380,7 +374,6 @@ struct wmi_rf_mgmt_cmd {
__le32 rf_mgmt_type;
} __packed;
-
/*
* WMI_RF_RX_TEST_CMDID
*/
@@ -426,7 +419,6 @@ struct wmi_bcon_ctrl_cmd {
u8 disable_sec;
} __packed;
-
/******* P2P ***********/
/*
@@ -797,7 +789,6 @@ struct wmi_temp_sense_cmd {
__le32 measure_marlon_r_en;
} __packed;
-
/*
* WMI Events
*/
@@ -887,7 +878,6 @@ enum wmi_event_id {
* Events data structures
*/
-
enum wmi_fw_status {
WMI_FW_STATUS_SUCCESS,
WMI_FW_STATUS_FAILURE,
@@ -980,7 +970,7 @@ struct wmi_ready_event {
* WMI_NOTIFY_REQ_DONE_EVENTID
*/
struct wmi_notify_req_done_event {
- __le32 status;
+ __le32 status; /* beamforming status, 0: fail; 1: OK; 2: retrying */
__le64 tsf;
__le32 snr_val;
__le32 tx_tpt;
@@ -1038,8 +1028,8 @@ struct wmi_disconnect_event {
__le16 protocol_reason_status; /* reason code, see 802.11 spec. */
u8 bssid[WMI_MAC_LEN]; /* set if known */
u8 disconnect_reason; /* see wmi_disconnect_reason */
- u8 assoc_resp_len; /* not in use */
- u8 assoc_info[0]; /* not in use */
+ u8 assoc_resp_len; /* not used */
+ u8 assoc_info[0]; /* not used */
} __packed;
/*
@@ -1081,7 +1071,6 @@ struct wmi_delba_event {
__le16 reason;
} __packed;
-
/*
* WMI_VRING_CFG_DONE_EVENTID
*/
@@ -1147,7 +1136,6 @@ struct wmi_data_port_open_event {
u8 reserved[3];
} __packed;
-
/*
* WMI_GET_PCP_CHANNEL_EVENTID
*/
@@ -1156,7 +1144,6 @@ struct wmi_get_pcp_channel_event {
u8 reserved[3];
} __packed;
-
/*
* WMI_PORT_ALLOCATED_EVENTID
*/
@@ -1260,7 +1247,6 @@ struct wmi_rx_mgmt_info {
u8 channel; /* From Radio MNGR */
} __packed;
-
/*
* WMI_TX_MGMT_PACKET_EVENTID
*/
OpenPOWER on IntegriCloud