summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Lin <dlin@marvell.com>2016-03-25 11:16:14 +0800
committerDavid Lin <dlin@marvell.com>2016-03-25 11:16:14 +0800
commit6988b67d9af987a7ba47f82cdaa6c9107e55ec3f (patch)
tree385463d33e124873445b0fd82f9de3e2278fe137
parent99d3879cc72f2a25d44fb4bee96fd84eca028b04 (diff)
Commit mwlwifi driver 10.3.0.17-20160324.20160328
1. Fixed problem: tx throughput will become lower after running for a while. 2. Fixed problme: multicast out-of-order. 3. Upgraded firmware of 88W8864 to version 7.2.9.2. Signed-off-by: David Lin <dlin@marvell.com>
-rw-r--r--Makefile7
-rw-r--r--Makefile.external7
-rw-r--r--Makefile.kernel3
-rwxr-xr-xbin/firmware/88W8864.binbin116356 -> 117828 bytes
-rw-r--r--debugfs.c476
-rw-r--r--debugfs.h2
-rw-r--r--dev.h13
-rw-r--r--fwcmd.c2
-rw-r--r--fwcmd.h2
-rw-r--r--fwdl.c14
-rw-r--r--fwdl.h2
-rw-r--r--hostcmd.h2
-rw-r--r--isr.c26
-rw-r--r--isr.h2
-rw-r--r--mac80211.c11
-rw-r--r--main.c26
-rw-r--r--rx.c2
-rw-r--r--rx.h2
-rw-r--r--sysadpt.h10
-rwxr-xr-xtest/AP+STA.zipbin0 -> 4534 bytes
-rw-r--r--thermal.c182
-rw-r--r--thermal.h40
-rw-r--r--tx.c111
-rw-r--r--tx.h3
24 files changed, 716 insertions, 229 deletions
diff --git a/Makefile b/Makefile
index 11b812c..11cbf8e 100644
--- a/Makefile
+++ b/Makefile
@@ -7,7 +7,8 @@ mwlwifi-objs += fwcmd.o
mwlwifi-objs += tx.o
mwlwifi-objs += rx.o
mwlwifi-objs += isr.o
-mwlwifi-$(CONFIG_DEBUG_FS) += debugfs.o
+mwlwifi-$(CONFIG_THERMAL) += thermal.o
+mwlwifi-$(CONFIG_DEBUG_FS) += debugfs.o
ifeq (1, $(BUILD_MFG))
mwlwifi-objs += mfg.o
endif
@@ -23,6 +24,10 @@ ifeq (1, $(BUILD_MFG))
EXTRA_CFLAGS+= -DSUPPORT_MFG
endif
+ifeq (1, $(BUILD_BG4CT_A0))
+EXTRA_CFLAGS+= -DBG4CT_A0_WORKAROUND
+endif
+
EXTRA_CFLAGS+= -I${PWD}
all:
diff --git a/Makefile.external b/Makefile.external
index 11b812c..11cbf8e 100644
--- a/Makefile.external
+++ b/Makefile.external
@@ -7,7 +7,8 @@ mwlwifi-objs += fwcmd.o
mwlwifi-objs += tx.o
mwlwifi-objs += rx.o
mwlwifi-objs += isr.o
-mwlwifi-$(CONFIG_DEBUG_FS) += debugfs.o
+mwlwifi-$(CONFIG_THERMAL) += thermal.o
+mwlwifi-$(CONFIG_DEBUG_FS) += debugfs.o
ifeq (1, $(BUILD_MFG))
mwlwifi-objs += mfg.o
endif
@@ -23,6 +24,10 @@ ifeq (1, $(BUILD_MFG))
EXTRA_CFLAGS+= -DSUPPORT_MFG
endif
+ifeq (1, $(BUILD_BG4CT_A0))
+EXTRA_CFLAGS+= -DBG4CT_A0_WORKAROUND
+endif
+
EXTRA_CFLAGS+= -I${PWD}
all:
diff --git a/Makefile.kernel b/Makefile.kernel
index 88f7efd..37af74f 100644
--- a/Makefile.kernel
+++ b/Makefile.kernel
@@ -7,6 +7,7 @@ mwlwifi-objs += fwcmd.o
mwlwifi-objs += tx.o
mwlwifi-objs += rx.o
mwlwifi-objs += isr.o
-mwlwifi-$(CONFIG_DEBUG_FS) += debugfs.o
+mwlwifi-$(CONFIG_THERMAL) += thermal.o
+mwlwifi-$(CONFIG_DEBUG_FS) += debugfs.o
ccflags-y += -D__CHECK_ENDIAN__
diff --git a/bin/firmware/88W8864.bin b/bin/firmware/88W8864.bin
index 19b21a6..21bd0c0 100755
--- a/bin/firmware/88W8864.bin
+++ b/bin/firmware/88W8864.bin
Binary files differ
diff --git a/debugfs.c b/debugfs.c
index e2afa5e..8920ea2 100644
--- a/debugfs.c
+++ b/debugfs.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2006-2015, Marvell International Ltd.
+ * Copyright (C) 2006-2016, Marvell International Ltd.
*
* This software file (the "File") is distributed by Marvell International
* Ltd. under the terms of the GNU General Public License Version 2, June 1991
@@ -21,6 +21,7 @@
#include "dev.h"
#include "hostcmd.h"
#include "fwcmd.h"
+#include "thermal.h"
#include "debugfs.h"
#define MWLWIFI_DEBUGFS_ADD_FILE(name) do { \
@@ -48,29 +49,30 @@ static const struct file_operations mwl_debugfs_##name##_fops = { \
.open = simple_open, \
}
-static int dump_data(char *p, u8 *data, int len, char *title)
+static void dump_data(char *p, int size, int *len, u8 *data,
+ int data_len, char *title)
{
- char *str = p;
int cur_byte = 0;
int i;
- str += sprintf(str, "%s\n", title);
- for (cur_byte = 0; cur_byte < len; cur_byte += 8) {
- if ((cur_byte + 8) < len) {
+ *len += scnprintf(p + *len, size - *len, "%s\n", title);
+
+ for (cur_byte = 0; cur_byte < data_len; cur_byte += 8) {
+ if ((cur_byte + 8) < data_len) {
for (i = 0; i < 8; i++)
- str += sprintf(str, "0x%02x ",
- *(data + cur_byte + i));
- str += sprintf(str, "\n");
+ *len += scnprintf(p + *len, size - *len,
+ "0x%02x ",
+ *(data + cur_byte + i));
+ *len += scnprintf(p + *len, size - *len, "\n");
} else {
- for (i = 0; i < (len - cur_byte); i++)
- str += sprintf(str, "0x%02x ",
- *(data + cur_byte + i));
- str += sprintf(str, "\n");
+ for (i = 0; i < (data_len - cur_byte); i++)
+ *len += scnprintf(p + *len, size - *len,
+ "0x%02x ",
+ *(data + cur_byte + i));
+ *len += scnprintf(p + *len, size - *len, "\n");
break;
}
}
-
- return (str - p);
}
static ssize_t mwl_debugfs_info_read(struct file *file, char __user *ubuf,
@@ -79,41 +81,52 @@ static ssize_t mwl_debugfs_info_read(struct file *file, char __user *ubuf,
struct mwl_priv *priv = (struct mwl_priv *)file->private_data;
unsigned long page = get_zeroed_page(GFP_KERNEL);
char *p = (char *)page;
+ int len = 0, size = PAGE_SIZE;
ssize_t ret;
if (!p)
return -ENOMEM;
- p += sprintf(p, "\n");
- p += sprintf(p, "driver name: %s\n", MWL_DRV_NAME);
- p += sprintf(p, "chip type: %s\n",
- (priv->chip_type == MWL8864) ? "88W8864" : "88W8897");
- p += sprintf(p, "hw version: %X\n", priv->hw_data.hw_version);
- p += sprintf(p, "driver version: %s\n", MWL_DRV_VERSION);
- p += sprintf(p, "firmware version: 0x%08x\n",
- priv->hw_data.fw_release_num);
- p += sprintf(p, "mac address: %pM\n", priv->hw_data.mac_addr);
- p += sprintf(p, "2g: %s\n", priv->disable_2g ? "disable" : "enable");
- p += sprintf(p, "5g: %s\n", priv->disable_5g ? "disable" : "enable");
- p += sprintf(p, "antenna: %d %d\n",
- (priv->antenna_tx == ANTENNA_TX_4_AUTO) ? 4 : 2,
- (priv->antenna_rx == ANTENNA_TX_4_AUTO) ? 4 : 2);
- p += sprintf(p, "irq number: %d\n", priv->irq);
- p += sprintf(p, "iobase0: %p\n", priv->iobase0);
- p += sprintf(p, "iobase1: %p\n", priv->iobase1);
- p += sprintf(p, "tx limit: %d\n", priv->txq_limit);
- p += sprintf(p, "rx limit: %d\n", priv->recv_limit);
- p += sprintf(p, "ap macid support: %08x\n",
- priv->ap_macids_supported);
- p += sprintf(p, "sta macid support: %08x\n",
- priv->sta_macids_supported);
- p += sprintf(p, "macid used: %08x\n", priv->macids_used);
- p += sprintf(p, "qe trigger number: %d\n", priv->qe_trigger_num);
- p += sprintf(p, "mfg mode: %s\n", priv->mfg_mode ? "true" : "false");
- p += sprintf(p, "\n");
-
- ret = simple_read_from_buffer(ubuf, count, ppos, (char *)page,
- (unsigned long)p - page);
+ len += scnprintf(p + len, size - len, "\n");
+ len += scnprintf(p + len, size - len,
+ "driver name: %s\n", MWL_DRV_NAME);
+ len += scnprintf(p + len, size - len, "chip type: %s\n",
+ (priv->chip_type == MWL8864) ? "88W8864" : "88W8897");
+ len += scnprintf(p + len, size - len,
+ "hw version: %X\n", priv->hw_data.hw_version);
+ len += scnprintf(p + len, size - len,
+ "driver version: %s\n", MWL_DRV_VERSION);
+ len += scnprintf(p + len, size - len, "firmware version: 0x%08x\n",
+ priv->hw_data.fw_release_num);
+ len += scnprintf(p + len, size - len,
+ "mac address: %pM\n", priv->hw_data.mac_addr);
+ len += scnprintf(p + len, size - len,
+ "2g: %s\n", priv->disable_2g ? "disable" : "enable");
+ len += scnprintf(p + len, size - len,
+ "5g: %s\n", priv->disable_5g ? "disable" : "enable");
+ len += scnprintf(p + len, size - len, "antenna: %d %d\n",
+ (priv->antenna_tx == ANTENNA_TX_4_AUTO) ? 4 : 2,
+ (priv->antenna_rx == ANTENNA_TX_4_AUTO) ? 4 : 2);
+ len += scnprintf(p + len, size - len, "irq number: %d\n", priv->irq);
+ len += scnprintf(p + len, size - len, "iobase0: %p\n", priv->iobase0);
+ len += scnprintf(p + len, size - len, "iobase1: %p\n", priv->iobase1);
+ len += scnprintf(p + len, size - len,
+ "tx limit: %d\n", priv->txq_limit);
+ len += scnprintf(p + len, size - len,
+ "rx limit: %d\n", priv->recv_limit);
+ len += scnprintf(p + len, size - len, "ap macid support: %08x\n",
+ priv->ap_macids_supported);
+ len += scnprintf(p + len, size - len, "sta macid support: %08x\n",
+ priv->sta_macids_supported);
+ len += scnprintf(p + len, size - len,
+ "macid used: %08x\n", priv->macids_used);
+ len += scnprintf(p + len, size - len,
+ "qe trigger number: %d\n", priv->qe_trigger_num);
+ len += scnprintf(p + len, size - len,
+ "mfg mode: %s\n", priv->mfg_mode ? "true" : "false");
+ len += scnprintf(p + len, size - len, "\n");
+
+ ret = simple_read_from_buffer(ubuf, count, ppos, p, len);
free_page(page);
return ret;
@@ -125,6 +138,7 @@ static ssize_t mwl_debugfs_vif_read(struct file *file, char __user *ubuf,
struct mwl_priv *priv = (struct mwl_priv *)file->private_data;
unsigned long page = get_zeroed_page(GFP_KERNEL);
char *p = (char *)page;
+ int len = 0, size = PAGE_SIZE;
struct mwl_vif *mwl_vif;
struct ieee80211_vif *vif;
char ssid[IEEE80211_MAX_SSID_LEN + 1];
@@ -133,53 +147,63 @@ static ssize_t mwl_debugfs_vif_read(struct file *file, char __user *ubuf,
if (!p)
return -ENOMEM;
- p += sprintf(p, "\n");
+ len += scnprintf(p + len, size - len, "\n");
spin_lock_bh(&priv->vif_lock);
list_for_each_entry(mwl_vif, &priv->vif_list, list) {
vif = container_of((char *)mwl_vif, struct ieee80211_vif,
drv_priv[0]);
- p += sprintf(p, "macid: %d\n", mwl_vif->macid);
+ len += scnprintf(p + len, size - len,
+ "macid: %d\n", mwl_vif->macid);
switch (vif->type) {
case NL80211_IFTYPE_AP:
- p += sprintf(p, "type: ap\n");
+ len += scnprintf(p + len, size - len, "type: ap\n");
memcpy(ssid, vif->bss_conf.ssid,
vif->bss_conf.ssid_len);
ssid[vif->bss_conf.ssid_len] = 0;
- p += sprintf(p, "ssid: %s\n", ssid);
- p += sprintf(p, "mac address: %pM\n", mwl_vif->bssid);
+ len += scnprintf(p + len, size - len,
+ "ssid: %s\n", ssid);
+ len += scnprintf(p + len, size - len,
+ "mac address: %pM\n", mwl_vif->bssid);
break;
case NL80211_IFTYPE_MESH_POINT:
- p += sprintf(p, "type: mesh\n");
- p += sprintf(p, "mac address: %pM\n", mwl_vif->bssid);
+ len += scnprintf(p + len, size - len, "type: mesh\n");
+ len += scnprintf(p + len, size - len,
+ "mac address: %pM\n", mwl_vif->bssid);
break;
case NL80211_IFTYPE_STATION:
- p += sprintf(p, "type: sta\n");
- p += sprintf(p, "mac address: %pM\n", mwl_vif->sta_mac);
+ len += scnprintf(p + len, size - len, "type: sta\n");
+ len += scnprintf(p + len, size - len,
+ "mac address: %pM\n",
+ mwl_vif->sta_mac);
break;
default:
- p += sprintf(p, "type: unknown\n");
+ len += scnprintf(p + len, size - len,
+ "type: unknown\n");
break;
}
- p += sprintf(p, "hw_crypto_enabled: %s\n",
- mwl_vif->is_hw_crypto_enabled ? "true" : "false");
- p += sprintf(p, "key idx: %d\n", mwl_vif->keyidx);
- p += sprintf(p, "IV: %08x%04x\n", mwl_vif->iv32, mwl_vif->iv16);
- p += dump_data(p, mwl_vif->beacon_info.ie_wmm_ptr,
- mwl_vif->beacon_info.ie_wmm_len, "WMM:");
- p += dump_data(p, mwl_vif->beacon_info.ie_rsn_ptr,
- mwl_vif->beacon_info.ie_rsn_len, "RSN:");
- p += dump_data(p, mwl_vif->beacon_info.ie_rsn48_ptr,
- mwl_vif->beacon_info.ie_rsn48_len, "RSN48:");
- p += dump_data(p, mwl_vif->beacon_info.ie_ht_ptr,
- mwl_vif->beacon_info.ie_ht_len, "HT:");
- p += dump_data(p, mwl_vif->beacon_info.ie_vht_ptr,
- mwl_vif->beacon_info.ie_vht_len, "VHT:");
- p += sprintf(p, "\n");
+ len += scnprintf(p + len, size - len, "hw_crypto_enabled: %s\n",
+ mwl_vif->is_hw_crypto_enabled ?
+ "true" : "false");
+ len += scnprintf(p + len, size - len,
+ "key idx: %d\n", mwl_vif->keyidx);
+ len += scnprintf(p + len, size - len,
+ "IV: %08x%04x\n", mwl_vif->iv32,
+ mwl_vif->iv16);
+ dump_data(p, size, &len, mwl_vif->beacon_info.ie_wmm_ptr,
+ mwl_vif->beacon_info.ie_wmm_len, "WMM:");
+ dump_data(p, size, &len, mwl_vif->beacon_info.ie_rsn_ptr,
+ mwl_vif->beacon_info.ie_rsn_len, "RSN:");
+ dump_data(p, size, &len, mwl_vif->beacon_info.ie_rsn48_ptr,
+ mwl_vif->beacon_info.ie_rsn48_len, "RSN48:");
+ dump_data(p, size, &len, mwl_vif->beacon_info.ie_ht_ptr,
+ mwl_vif->beacon_info.ie_ht_len, "HT:");
+ dump_data(p, size, &len, mwl_vif->beacon_info.ie_vht_ptr,
+ mwl_vif->beacon_info.ie_vht_len, "VHT:");
+ len += scnprintf(p + len, size - len, "\n");
}
spin_unlock_bh(&priv->vif_lock);
- ret = simple_read_from_buffer(ubuf, count, ppos, (char *)page,
- (unsigned long)p - page);
+ ret = simple_read_from_buffer(ubuf, count, ppos, p, len);
free_page(page);
return ret;
@@ -191,6 +215,7 @@ static ssize_t mwl_debugfs_sta_read(struct file *file, char __user *ubuf,
struct mwl_priv *priv = (struct mwl_priv *)file->private_data;
unsigned long page = get_zeroed_page(GFP_KERNEL);
char *p = (char *)page;
+ int len = 0, size = PAGE_SIZE;
struct mwl_sta *sta_info;
struct ieee80211_sta *sta;
ssize_t ret;
@@ -198,29 +223,30 @@ static ssize_t mwl_debugfs_sta_read(struct file *file, char __user *ubuf,
if (!p)
return -ENOMEM;
- p += sprintf(p, "\n");
+ len += scnprintf(p + len, size - len, "\n");
spin_lock_bh(&priv->sta_lock);
list_for_each_entry(sta_info, &priv->sta_list, list) {
sta = container_of((char *)sta_info, struct ieee80211_sta,
drv_priv[0]);
- p += sprintf(p, "mac address: %pM\n", sta->addr);
- p += sprintf(p, "aid: %u\n", sta->aid);
- p += sprintf(p, "ampdu: %s\n",
- sta_info->is_ampdu_allowed ? "true" : "false");
- p += sprintf(p, "amsdu: %s\n",
- sta_info->is_amsdu_allowed ? "true" : "false");
+ len += scnprintf(p + len, size - len,
+ "mac address: %pM\n", sta->addr);
+ len += scnprintf(p + len, size - len, "aid: %u\n", sta->aid);
+ len += scnprintf(p + len, size - len, "ampdu: %s\n",
+ sta_info->is_ampdu_allowed ? "true" : "false");
+ len += scnprintf(p + len, size - len, "amsdu: %s\n",
+ sta_info->is_amsdu_allowed ? "true" : "false");
if (sta_info->is_amsdu_allowed) {
- p += sprintf(p, "amsdu cap: 0x%02x\n",
- sta_info->amsdu_ctrl.cap);
+ len += scnprintf(p + len, size - len,
+ "amsdu cap: 0x%02x\n",
+ sta_info->amsdu_ctrl.cap);
}
- p += sprintf(p, "IV: %08x%04x\n",
- sta_info->iv32, sta_info->iv16);
- p += sprintf(p, "\n");
+ len += scnprintf(p + len, size - len, "IV: %08x%04x\n",
+ sta_info->iv32, sta_info->iv16);
+ len += scnprintf(p + len, size - len, "\n");
}
spin_unlock_bh(&priv->sta_lock);
- ret = simple_read_from_buffer(ubuf, count, ppos, (char *)page,
- (unsigned long)p - page);
+ ret = simple_read_from_buffer(ubuf, count, ppos, p, len);
free_page(page);
return ret;
@@ -232,6 +258,7 @@ static ssize_t mwl_debugfs_ampdu_read(struct file *file, char __user *ubuf,
struct mwl_priv *priv = (struct mwl_priv *)file->private_data;
unsigned long page = get_zeroed_page(GFP_KERNEL);
char *p = (char *)page;
+ int len = 0, size = PAGE_SIZE;
struct mwl_ampdu_stream *stream;
int i;
ssize_t ret;
@@ -239,29 +266,106 @@ static ssize_t mwl_debugfs_ampdu_read(struct file *file, char __user *ubuf,
if (!p)
return -ENOMEM;
- p += sprintf(p, "\n");
+ len += scnprintf(p + len, size - len, "\n");
spin_lock_bh(&priv->stream_lock);
for (i = 0; i < SYSADPT_TX_AMPDU_QUEUES; i++) {
stream = &priv->ampdu[i];
- p += sprintf(p, "stream: %d\n", i);
- p += sprintf(p, "idx: %u\n", stream->idx);
- p += sprintf(p, "state: %u\n", stream->state);
+ len += scnprintf(p + len, size - len, "stream: %d\n", i);
+ len += scnprintf(p + len, size - len, "idx: %u\n", stream->idx);
+ len += scnprintf(p + len, size - len,
+ "state: %u\n", stream->state);
if (stream->sta) {
- p += sprintf(p, "mac address: %pM\n",
- stream->sta->addr);
- p += sprintf(p, "tid: %u\n", stream->tid);
+ len += scnprintf(p + len, size - len,
+ "mac address: %pM\n",
+ stream->sta->addr);
+ len += scnprintf(p + len, size - len,
+ "tid: %u\n", stream->tid);
}
}
spin_unlock_bh(&priv->stream_lock);
- p += sprintf(p, "\n");
+ len += scnprintf(p + len, size - len, "\n");
- ret = simple_read_from_buffer(ubuf, count, ppos, (char *)page,
- (unsigned long)p - page);
+ ret = simple_read_from_buffer(ubuf, count, ppos, p, len);
free_page(page);
return ret;
}
+static ssize_t mwl_debugfs_tx_desc_read(struct file *file,
+ char __user *ubuf,
+ size_t count, loff_t *ppos)
+{
+ struct mwl_priv *priv = (struct mwl_priv *)file->private_data;
+ unsigned long page = get_zeroed_page(GFP_KERNEL);
+ char *p = (char *)page;
+ int len = 0, size = PAGE_SIZE;
+ struct mwl_desc_data *desc;
+ int i, num, write_item = -1, free_item = -1;
+ ssize_t ret;
+
+ spin_lock_bh(&priv->tx_desc_lock);
+ num = priv->tx_desc_num;
+ desc = &priv->desc_data[num];
+ len += scnprintf(p + len, size - len, "num: %i fw_desc_cnt:%i\n",
+ num, priv->fw_desc_cnt[num]);
+ for (i = 0; i < SYSADPT_MAX_NUM_TX_DESC; i++) {
+ len += scnprintf(p + len, size - len, "%3i %x\n", i,
+ desc->tx_hndl[i].pdesc->status);
+ if (desc->pnext_tx_hndl == &desc->tx_hndl[i])
+ write_item = i;
+ if (desc->pstale_tx_hndl == &desc->tx_hndl[i])
+ free_item = i;
+ }
+ len += scnprintf(p + len, size - len, "next:%i stale:%i\n",
+ write_item, free_item);
+ len += scnprintf(p + len, size - len, "\n");
+ spin_unlock_bh(&priv->tx_desc_lock);
+
+ ret = simple_read_from_buffer(ubuf, count, ppos, p, len);
+ free_page(page);
+
+ return ret;
+}
+
+static ssize_t mwl_debugfs_tx_desc_write(struct file *file,
+ const char __user *ubuf,
+ size_t count, loff_t *ppos)
+{
+ struct mwl_priv *priv = (struct mwl_priv *)file->private_data;
+ unsigned long addr = get_zeroed_page(GFP_KERNEL);
+ char *buf = (char *)addr;
+ size_t buf_size = min_t(size_t, count, PAGE_SIZE - 1);
+ int tx_desc_num = 0;
+ ssize_t ret;
+
+ if (!buf)
+ return -ENOMEM;
+
+ if (copy_from_user(buf, ubuf, buf_size)) {
+ ret = -EFAULT;
+ goto err;
+ }
+
+ ret = sscanf(buf, "%d", &tx_desc_num);
+
+ if (ret != 1) {
+ ret = -EINVAL;
+ goto err;
+ }
+
+ if ((tx_desc_num < 0) || (tx_desc_num >= SYSADPT_NUM_OF_DESC_DATA)) {
+ ret = -EINVAL;
+ goto err;
+ }
+
+ priv->tx_desc_num = tx_desc_num;
+ ret = count;
+
+err:
+ free_page(addr);
+ return ret;
+}
+
static ssize_t mwl_debugfs_dfs_channel_read(struct file *file,
char __user *ubuf,
size_t count, loff_t *ppos)
@@ -269,8 +373,9 @@ static ssize_t mwl_debugfs_dfs_channel_read(struct file *file,
struct mwl_priv *priv = (struct mwl_priv *)file->private_data;
unsigned long page = get_zeroed_page(GFP_KERNEL);
char *p = (char *)page;
+ int len = 0, size = PAGE_SIZE;
struct ieee80211_supported_band *sband;
- struct ieee80211_channel*channel;
+ struct ieee80211_channel *channel;
int i;
ssize_t ret;
@@ -281,20 +386,25 @@ static ssize_t mwl_debugfs_dfs_channel_read(struct file *file,
if (!sband)
return -EINVAL;
- p += sprintf(p, "\n");
+ len += scnprintf(p + len, size - len, "\n");
for (i = 0; i < sband->n_channels; i++) {
channel = &sband->channels[i];
if (channel->flags & IEEE80211_CHAN_RADAR) {
- p += sprintf(p, "%d(%d): flags: %08x dfs_state: %d\n",
- channel->hw_value, channel->center_freq,
- channel->flags, channel->dfs_state);
- p += sprintf(p, "cac timer: %d ms\n", channel->dfs_cac_ms);
+ len += scnprintf(p + len, size - len,
+ "%d(%d): flags: %08x dfs_state: %d\n",
+ channel->hw_value,
+ channel->center_freq,
+ channel->flags, channel->dfs_state);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 15, 0)
+ len += scnprintf(p + len, size - len,
+ "cac timer: %d ms\n",
+ channel->dfs_cac_ms);
+#endif
}
}
- p += sprintf(p, "\n");
+ len += scnprintf(p + len, size - len, "\n");
- ret = simple_read_from_buffer(ubuf, count, ppos, (char *)page,
- (unsigned long)p - page);
+ ret = simple_read_from_buffer(ubuf, count, ppos, p, len);
free_page(page);
return ret;
@@ -311,9 +421,9 @@ static ssize_t mwl_debugfs_dfs_channel_write(struct file *file,
size_t buf_size = min_t(size_t, count, PAGE_SIZE - 1);
int dfs_state = 0;
int cac_time = -1;
- struct ieee80211_channel*channel;
+ struct ieee80211_channel *channel;
int i;
- ssize_t ret = count;
+ ssize_t ret;
if (!buf)
return -ENOMEM;
@@ -321,26 +431,34 @@ static ssize_t mwl_debugfs_dfs_channel_write(struct file *file,
sband = priv->hw->wiphy->bands[NL80211_BAND_5GHZ];
if (!sband) {
ret = -EINVAL;
- goto done;
+ goto err;
}
if (copy_from_user(buf, ubuf, buf_size)) {
ret = -EFAULT;
- goto done;
+ goto err;
}
- sscanf(buf, "%d %d", &dfs_state, &cac_time);
+ ret = sscanf(buf, "%d %d", &dfs_state, &cac_time);
+
+ if ((ret < 1) || (ret > 2)) {
+ ret = -EINVAL;
+ goto err;
+ }
for (i = 0; i < sband->n_channels; i++) {
channel = &sband->channels[i];
if (channel->flags & IEEE80211_CHAN_RADAR) {
channel->dfs_state = dfs_state;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 15, 0)
if (cac_time != -1)
channel->dfs_cac_ms = cac_time * 1000;
+#endif
}
}
+ ret = count;
-done:
+err:
free_page(addr);
return ret;
}
@@ -351,24 +469,30 @@ static ssize_t mwl_debugfs_dfs_radar_read(struct file *file, char __user *ubuf,
struct mwl_priv *priv = (struct mwl_priv *)file->private_data;
unsigned long page = get_zeroed_page(GFP_KERNEL);
char *p = (char *)page;
+ int len = 0, size = PAGE_SIZE;
ssize_t ret;
if (!p)
return -ENOMEM;
- p += sprintf(p, "\n");
- p += sprintf(p, "csa_active: %d\n", priv->csa_active);
- p += sprintf(p, "dfs_region: %d\n", priv->dfs_region);
- p += sprintf(p, "chirp_count_min: %d\n", priv->dfs_chirp_count_min);
- p += sprintf(p, "chirp_time_interval: %d\n",
- priv->dfs_chirp_time_interval);
- p += sprintf(p, "pw_filter: %d\n", priv->dfs_pw_filter);
- p += sprintf(p, "min_num_radar: %d\n", priv->dfs_min_num_radar);
- p += sprintf(p, "min_pri_count: %d\n", priv->dfs_min_pri_count);
- p += sprintf(p, "\n");
-
- ret = simple_read_from_buffer(ubuf, count, ppos, (char *)page,
- (unsigned long)p - page);
+ len += scnprintf(p + len, size - len, "\n");
+ len += scnprintf(p + len, size - len,
+ "csa_active: %d\n", priv->csa_active);
+ len += scnprintf(p + len, size - len,
+ "dfs_region: %d\n", priv->dfs_region);
+ len += scnprintf(p + len, size - len,
+ "chirp_count_min: %d\n", priv->dfs_chirp_count_min);
+ len += scnprintf(p + len, size - len, "chirp_time_interval: %d\n",
+ priv->dfs_chirp_time_interval);
+ len += scnprintf(p + len, size - len,
+ "pw_filter: %d\n", priv->dfs_pw_filter);
+ len += scnprintf(p + len, size - len,
+ "min_num_radar: %d\n", priv->dfs_min_num_radar);
+ len += scnprintf(p + len, size - len,
+ "min_pri_count: %d\n", priv->dfs_min_pri_count);
+ len += scnprintf(p + len, size - len, "\n");
+
+ ret = simple_read_from_buffer(ubuf, count, ppos, p, len);
free_page(page);
return ret;
@@ -386,6 +510,78 @@ static ssize_t mwl_debugfs_dfs_radar_write(struct file *file,
return count;
}
+static ssize_t mwl_debugfs_thermal_read(struct file *file,
+ char __user *ubuf,
+ size_t count, loff_t *ppos)
+{
+ struct mwl_priv *priv = (struct mwl_priv *)file->private_data;
+ unsigned long page = get_zeroed_page(GFP_KERNEL);
+ char *p = (char *)page;
+ int len = 0, size = PAGE_SIZE;
+ ssize_t ret;
+
+ if (!p)
+ return -ENOMEM;
+
+ mwl_fwcmd_get_temp(priv->hw, &priv->temperature);
+
+ len += scnprintf(p + len, size - len, "\n");
+ len += scnprintf(p + len, size - len, "quiet period: %d\n",
+ priv->quiet_period);
+ len += scnprintf(p + len, size - len, "throttle state: %d\n",
+ priv->throttle_state);
+ len += scnprintf(p + len, size - len, "temperature: %d\n",
+ priv->temperature);
+ len += scnprintf(p + len, size - len, "\n");
+
+ ret = simple_read_from_buffer(ubuf, count, ppos, p, len);
+ free_page(page);
+
+ return ret;
+}
+
+static ssize_t mwl_debugfs_thermal_write(struct file *file,
+ const char __user *ubuf,
+ size_t count, loff_t *ppos)
+{
+ struct mwl_priv *priv = (struct mwl_priv *)file->private_data;
+ unsigned long addr = get_zeroed_page(GFP_KERNEL);
+ char *buf = (char *)addr;
+ size_t buf_size = min_t(size_t, count, PAGE_SIZE - 1);
+ int throttle_state;
+ ssize_t ret;
+
+ if (!buf)
+ return -ENOMEM;
+
+ if (copy_from_user(buf, ubuf, buf_size)) {
+ ret = -EFAULT;
+ goto err;
+ }
+
+ if (kstrtoint(buf, 0, &throttle_state)) {
+ ret = -EINVAL;
+ goto err;
+ }
+
+ if (throttle_state > SYSADPT_THERMAL_THROTTLE_MAX) {
+ wiphy_warn(priv->hw->wiphy,
+ "throttle state %d is exceeding the limit %d\n",
+ throttle_state, SYSADPT_THERMAL_THROTTLE_MAX);
+ ret = -EINVAL;
+ goto err;
+ }
+
+ priv->throttle_state = throttle_state;
+ mwl_thermal_set_throttling(priv);
+ ret = count;
+
+err:
+ free_page(addr);
+ return ret;
+}
+
+
static int mwl_debugfs_reg_access(struct mwl_priv *priv, bool write)
{
struct ieee80211_hw *hw = priv->hw;
@@ -438,16 +634,17 @@ static ssize_t mwl_debugfs_regrdwr_read(struct file *file, char __user *ubuf,
size_t count, loff_t *ppos)
{
struct mwl_priv *priv = (struct mwl_priv *)file->private_data;
- unsigned long addr = get_zeroed_page(GFP_KERNEL);
- char *buf = (char *)addr;
- int pos = 0, ret = 0;
+ unsigned long page = get_zeroed_page(GFP_KERNEL);
+ char *p = (char *)page;
+ int len = 0, size = PAGE_SIZE;
+ int ret = 0;
- if (!buf)
+ if (!p)
return -ENOMEM;
if (!priv->reg_type) {
/* No command has been given */
- pos += snprintf(buf, PAGE_SIZE, "0");
+ len += scnprintf(p + len, size - len, "0");
goto none;
}
@@ -461,19 +658,20 @@ static ssize_t mwl_debugfs_regrdwr_read(struct file *file, char __user *ubuf,
done:
if (!ret)
- pos += snprintf(buf, PAGE_SIZE, "%u 0x%08x 0x%08x\n",
- priv->reg_type, priv->reg_offset,
- priv->reg_value);
+ len += scnprintf(p + len, size - len, "%u 0x%08x 0x%08x\n",
+ priv->reg_type, priv->reg_offset,
+ priv->reg_value);
else
- pos += snprintf(buf, PAGE_SIZE, "error: %d(%u 0x%08x 0x%08x)\n",
- ret, priv->reg_type, priv->reg_offset,
- priv->reg_value);
+ len += scnprintf(p + len, size - len,
+ "error: %d(%u 0x%08x 0x%08x)\n",
+ ret, priv->reg_type, priv->reg_offset,
+ priv->reg_value);
- ret = simple_read_from_buffer(ubuf, count, ppos, buf, pos);
+ ret = simple_read_from_buffer(ubuf, count, ppos, p, len);
none:
- free_page(addr);
+ free_page(page);
return ret;
}
@@ -517,8 +715,10 @@ MWLWIFI_DEBUGFS_FILE_READ_OPS(info);
MWLWIFI_DEBUGFS_FILE_READ_OPS(vif);
MWLWIFI_DEBUGFS_FILE_READ_OPS(sta);
MWLWIFI_DEBUGFS_FILE_READ_OPS(ampdu);
+MWLWIFI_DEBUGFS_FILE_OPS(tx_desc);
MWLWIFI_DEBUGFS_FILE_OPS(dfs_channel);
MWLWIFI_DEBUGFS_FILE_OPS(dfs_radar);
+MWLWIFI_DEBUGFS_FILE_OPS(thermal);
MWLWIFI_DEBUGFS_FILE_OPS(regrdwr);
void mwl_debugfs_init(struct ieee80211_hw *hw)
@@ -536,8 +736,10 @@ void mwl_debugfs_init(struct ieee80211_hw *hw)
MWLWIFI_DEBUGFS_ADD_FILE(vif);
MWLWIFI_DEBUGFS_ADD_FILE(sta);
MWLWIFI_DEBUGFS_ADD_FILE(ampdu);
+ MWLWIFI_DEBUGFS_ADD_FILE(tx_desc);
MWLWIFI_DEBUGFS_ADD_FILE(dfs_channel);
MWLWIFI_DEBUGFS_ADD_FILE(dfs_radar);
+ MWLWIFI_DEBUGFS_ADD_FILE(thermal);
MWLWIFI_DEBUGFS_ADD_FILE(regrdwr);
}
diff --git a/debugfs.h b/debugfs.h
index 90cfe66..b4c3eb3 100644
--- a/debugfs.h
+++ b/debugfs.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2006-2015, Marvell International Ltd.
+ * Copyright (C) 2006-2016, Marvell International Ltd.
*
* This software file (the "File") is distributed by Marvell International
* Ltd. under the terms of the GNU General Public License Version 2, June 1991
diff --git a/dev.h b/dev.h
index f97330f..9dbd6a6 100644
--- a/dev.h
+++ b/dev.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2006-2015, Marvell International Ltd.
+ * Copyright (C) 2006-2016, Marvell International Ltd.
*
* This software file (the "File") is distributed by Marvell International
* Ltd. under the terms of the GNU General Public License Version 2, June 1991
@@ -27,7 +27,7 @@
#include <net/mac80211.h>
#define MWL_DRV_NAME KBUILD_MODNAME
-#define MWL_DRV_VERSION "10.3.0.16-20160105"
+#define MWL_DRV_VERSION "10.3.0.17-20160324"
/* Map to 0x80000000 (Bus control) on BAR0 */
#define MACREG_REG_H2A_INTERRUPT_EVENTS 0x00000C18 /* (From host to ARM) */
@@ -319,10 +319,11 @@ struct mwl_priv {
int fw_desc_cnt[SYSADPT_NUM_OF_DESC_DATA];
struct tasklet_struct tx_task;
+ struct tasklet_struct tx_done_task;
struct tasklet_struct rx_task;
struct tasklet_struct qe_task;
int txq_limit;
- bool is_tx_schedule;
+ bool is_tx_done_schedule;
int recv_limit;
bool is_rx_schedule;
bool is_qe_schedule;
@@ -374,6 +375,11 @@ struct mwl_priv {
u16 dfs_min_num_radar;
u16 dfs_min_pri_count;
+ struct thermal_cooling_device *cdev;
+ u32 throttle_state;
+ u32 quiet_period;
+ int temperature;
+
bool mfg_mode;
#ifdef CONFIG_DEBUG_FS
@@ -381,6 +387,7 @@ struct mwl_priv {
u32 reg_type;
u32 reg_offset;
u32 reg_value;
+ int tx_desc_num;
#endif
};
diff --git a/fwcmd.c b/fwcmd.c
index 15bf20e..ef24be4 100644
--- a/fwcmd.c
+++ b/fwcmd.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2006-2015, Marvell International Ltd.
+ * Copyright (C) 2006-2016, Marvell International Ltd.
*
* This software file (the "File") is distributed by Marvell International
* Ltd. under the terms of the GNU General Public License Version 2, June 1991
diff --git a/fwcmd.h b/fwcmd.h
index 8b9809d..1f1249c 100644
--- a/fwcmd.h
+++ b/fwcmd.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2006-2015, Marvell International Ltd.
+ * Copyright (C) 2006-2016, Marvell International Ltd.
*
* This software file (the "File") is distributed by Marvell International
* Ltd. under the terms of the GNU General Public License Version 2, June 1991
diff --git a/fwdl.c b/fwdl.c
index a3fb84f..e305031 100644
--- a/fwdl.c
+++ b/fwdl.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2006-2015, Marvell International Ltd.
+ * Copyright (C) 2006-2016, Marvell International Ltd.
*
* This software file (the "File") is distributed by Marvell International
* Ltd. under the terms of the GNU General Public License Version 2, June 1991
@@ -61,7 +61,7 @@ int mwl_fwdl_download_firmware(struct ieee80211_hw *hw)
u32 int_code = 0;
u32 len = 0;
#ifdef SUPPORT_MFG
- u32 fwreadysignature = priv->mfg_mode ?
+ u32 fwreadysignature = (priv->mfg_mode && priv->chip_type == MWL8897) ?
MFG_FW_READY_SIGNATURE : HOSTCMD_SOFTAP_FWRDY_SIGNATURE;
#else
u32 fwreadysignature = HOSTCMD_SOFTAP_FWRDY_SIGNATURE;
@@ -162,18 +162,18 @@ int mwl_fwdl_download_firmware(struct ieee80211_hw *hw)
*((u32 *)&priv->pcmd_buf[1]) = 0;
mwl_fwdl_trig_pcicmd(priv);
curr_iteration = FW_MAX_NUM_CHECKS;
- if (priv->mfg_mode)
+ if (priv->mfg_mode && priv->chip_type == MWL8897)
writel(fwreadysignature, priv->iobase1 + 0xcf0);
do {
curr_iteration--;
- if (!priv->mfg_mode) {
+ if (priv->mfg_mode && priv->chip_type == MWL8897) {
+ mdelay(FW_CHECK_MSECS);
+ int_code = readl(priv->iobase1 + 0xc44);
+ } else {
writel(HOSTCMD_SOFTAP_MODE,
priv->iobase1 + MACREG_REG_GEN_PTR);
mdelay(FW_CHECK_MSECS);
int_code = readl(priv->iobase1 + MACREG_REG_INT_CODE);
- } else {
- mdelay(FW_CHECK_MSECS);
- int_code = readl(priv->iobase1 + 0xc44);
}
if (!(curr_iteration % 0xff) && (int_code != 0))
wiphy_err(hw->wiphy, "%x;", int_code);
diff --git a/fwdl.h b/fwdl.h
index c3e0e57..eedf4dd 100644
--- a/fwdl.h
+++ b/fwdl.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2006-2015, Marvell International Ltd.
+ * Copyright (C) 2006-2016, Marvell International Ltd.
*
* This software file (the "File") is distributed by Marvell International
* Ltd. under the terms of the GNU General Public License Version 2, June 1991
diff --git a/hostcmd.h b/hostcmd.h
index 132a18a..57c4dca 100644
--- a/hostcmd.h
+++ b/hostcmd.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2006-2015, Marvell International Ltd.
+ * Copyright (C) 2006-2016, Marvell International Ltd.
*
* This software file (the "File") is distributed by Marvell International
* Ltd. under the terms of the GNU General Public License Version 2, June 1991
diff --git a/isr.c b/isr.c
index 02d93d4..07bd7c5 100644
--- a/isr.c
+++ b/isr.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2006-2015, Marvell International Ltd.
+ * Copyright (C) 2006-2016, Marvell International Ltd.
*
* This software file (the "File") is distributed by Marvell International
* Ltd. under the terms of the GNU General Public License Version 2, June 1991
@@ -21,15 +21,24 @@
#include "isr.h"
#define INVALID_WATCHDOG 0xAA
+#ifdef BG4CT_A0_WORKAROUND
+#define MAX_ISR_ITERATION 2
+#endif
irqreturn_t mwl_isr(int irq, void *dev_id)
{
struct ieee80211_hw *hw = dev_id;
struct mwl_priv *priv = hw->priv;
void __iomem *int_status_mask;
- unsigned int int_status, clr_status;
+#ifdef BG4CT_A0_WORKAROUND
+ unsigned int currIteration = 0;
+#endif
+ u32 int_status, clr_status;
u32 status;
+#ifdef BG4CT_A0_WORKAROUND
+ do {
+#endif
int_status_mask = priv->iobase1 + MACREG_REG_A2H_INTERRUPT_STATUS_MASK;
int_status = readl(priv->iobase1 + MACREG_REG_A2H_INTERRUPT_CAUSE);
@@ -45,12 +54,12 @@ irqreturn_t mwl_isr(int irq, void *dev_id)
if (int_status & MACREG_A2HRIC_BIT_TX_DONE) {
int_status &= ~MACREG_A2HRIC_BIT_TX_DONE;
- if (!priv->is_tx_schedule) {
+ if (!priv->is_tx_done_schedule) {
status = readl(int_status_mask);
writel((status & ~MACREG_A2HRIC_BIT_TX_DONE),
int_status_mask);
- tasklet_schedule(&priv->tx_task);
- priv->is_tx_schedule = true;
+ tasklet_schedule(&priv->tx_done_task);
+ priv->is_tx_done_schedule = true;
}
}
@@ -95,14 +104,15 @@ irqreturn_t mwl_isr(int irq, void *dev_id)
ieee80211_queue_work(hw, &priv->chnl_switch_handle);
}
- if (int_status & MACREG_A2HRIC_BA_WATCHDOG) {
- status = readl(int_status_mask);
+ if (int_status & MACREG_A2HRIC_BA_WATCHDOG)
ieee80211_queue_work(hw, &priv->watchdog_ba_handle);
- }
writel(~clr_status,
priv->iobase1 + MACREG_REG_A2H_INTERRUPT_CAUSE);
}
+#ifdef BG4CT_A0_WORKAROUND
+ } while (currIteration++ < MAX_ISR_ITERATION);
+#endif
return IRQ_HANDLED;
}
diff --git a/isr.h b/isr.h
index ec916b8..adcc67f 100644
--- a/isr.h
+++ b/isr.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2006-2015, Marvell International Ltd.
+ * Copyright (C) 2006-2016, Marvell International Ltd.
*
* This software file (the "File") is distributed by Marvell International
* Ltd. under the terms of the GNU General Public License Version 2, June 1991
diff --git a/mac80211.c b/mac80211.c
index 75f813d..f15f3cc 100644
--- a/mac80211.c
+++ b/mac80211.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2006-2015, Marvell International Ltd.
+ * Copyright (C) 2006-2016, Marvell International Ltd.
*
* This software file (the "File") is distributed by Marvell International
* Ltd. under the terms of the GNU General Public License Version 2, June 1991
@@ -74,8 +74,9 @@ static int mwl_mac80211_start(struct ieee80211_hw *hw)
struct mwl_priv *priv = hw->priv;
int rc;
- /* Enable TX reclaim and RX tasklets. */
+ /* Enable TX and RX tasklets. */
tasklet_enable(&priv->tx_task);
+ tasklet_enable(&priv->tx_done_task);
tasklet_enable(&priv->rx_task);
tasklet_enable(&priv->qe_task);
@@ -110,6 +111,7 @@ static int mwl_mac80211_start(struct ieee80211_hw *hw)
fwcmd_fail:
mwl_fwcmd_int_disable(hw);
tasklet_disable(&priv->tx_task);
+ tasklet_disable(&priv->tx_done_task);
tasklet_disable(&priv->rx_task);
tasklet_disable(&priv->qe_task);
@@ -129,6 +131,7 @@ static void mwl_mac80211_stop(struct ieee80211_hw *hw)
/* Disable TX reclaim and RX tasklets. */
tasklet_disable(&priv->tx_task);
+ tasklet_disable(&priv->tx_done_task);
tasklet_disable(&priv->rx_task);
tasklet_disable(&priv->qe_task);
@@ -710,6 +713,7 @@ static int mwl_mac80211_ampdu_action(struct ieee80211_hw *hw,
return rc;
}
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 19, 0)
static int mwl_mac80211_chnl_switch(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
struct ieee80211_channel_switch *ch_switch)
@@ -721,6 +725,7 @@ static int mwl_mac80211_chnl_switch(struct ieee80211_hw *hw,
return rc;
}
+#endif
const struct ieee80211_ops mwl_mac80211_ops = {
.tx = mwl_mac80211_tx,
@@ -739,5 +744,7 @@ const struct ieee80211_ops mwl_mac80211_ops = {
.get_stats = mwl_mac80211_get_stats,
.get_survey = mwl_mac80211_get_survey,
.ampdu_action = mwl_mac80211_ampdu_action,
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 19, 0)
.pre_channel_switch = mwl_mac80211_chnl_switch,
+#endif
};
diff --git a/main.c b/main.c
index 3fbd596..2d03940 100644
--- a/main.c
+++ b/main.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2006-2015, Marvell International Ltd.
+ * Copyright (C) 2006-2016, Marvell International Ltd.
*
* This software file (the "File") is distributed by Marvell International
* Ltd. under the terms of the GNU General Public License Version 2, June 1991
@@ -27,12 +27,13 @@
#include "tx.h"
#include "rx.h"
#include "isr.h"
-#ifdef SUPPORT_MFG
-#include "mfg.h"
-#endif
+#include "thermal.h"
#ifdef CONFIG_DEBUG_FS
#include "debugfs.h"
#endif
+#ifdef SUPPORT_MFG
+#include "mfg.h"
+#endif
#define MWL_DESC "Marvell 802.11ac Wireless Network Driver"
#define MWL_DEV_NAME "Marvell 802.11ac Adapter"
@@ -582,15 +583,18 @@ static int mwl_wl_init(struct mwl_priv *priv)
INIT_WORK(&priv->watchdog_ba_handle, mwl_watchdog_ba_events);
INIT_WORK(&priv->chnl_switch_handle, mwl_chnl_switch_event);
- tasklet_init(&priv->tx_task, (void *)mwl_tx_done, (unsigned long)hw);
+ tasklet_init(&priv->tx_task, (void *)mwl_tx_skbs, (unsigned long)hw);
tasklet_disable(&priv->tx_task);
+ tasklet_init(&priv->tx_done_task,
+ (void *)mwl_tx_done, (unsigned long)hw);
+ tasklet_disable(&priv->tx_done_task);
tasklet_init(&priv->rx_task, (void *)mwl_rx_recv, (unsigned long)hw);
tasklet_disable(&priv->rx_task);
tasklet_init(&priv->qe_task,
(void *)mwl_tx_flush_amsdu, (unsigned long)hw);
tasklet_disable(&priv->qe_task);
priv->txq_limit = SYSADPT_TX_QUEUE_LIMIT;
- priv->is_tx_schedule = false;
+ priv->is_tx_done_schedule = false;
priv->recv_limit = SYSADPT_RECEIVE_LIMIT;
priv->is_rx_schedule = false;
priv->is_qe_schedule = false;
@@ -603,6 +607,13 @@ static int mwl_wl_init(struct mwl_priv *priv)
spin_lock_init(&priv->sta_lock);
spin_lock_init(&priv->stream_lock);
+ rc = mwl_thermal_register(priv);
+ if (rc) {
+ wiphy_err(hw->wiphy, "%s: fail to register thermal framework\n",
+ MWL_DRV_NAME);
+ goto err_thermal_register;
+ }
+
rc = mwl_tx_init(hw);
if (rc) {
wiphy_err(hw->wiphy, "%s: fail to initialize TX\n",
@@ -693,6 +704,7 @@ err_mwl_rx_init:
mwl_tx_deinit(hw);
err_mwl_tx_init:
+err_thermal_register:
wiphy_err(hw->wiphy, "init fail\n");
@@ -711,10 +723,12 @@ static void mwl_wl_deinit(struct mwl_priv *priv)
}
ieee80211_unregister_hw(hw);
+ mwl_thermal_unregister(priv);
mwl_rx_deinit(hw);
mwl_tx_deinit(hw);
tasklet_kill(&priv->qe_task);
tasklet_kill(&priv->rx_task);
+ tasklet_kill(&priv->tx_done_task);
tasklet_kill(&priv->tx_task);
cancel_work_sync(&priv->watchdog_ba_handle);
mwl_fwcmd_reset(hw);
diff --git a/rx.c b/rx.c
index 0845138..d71ea2d 100644
--- a/rx.c
+++ b/rx.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2006-2015, Marvell International Ltd.
+ * Copyright (C) 2006-2016, Marvell International Ltd.
*
* This software file (the "File") is distributed by Marvell International
* Ltd. under the terms of the GNU General Public License Version 2, June 1991
diff --git a/rx.h b/rx.h
index 5b9b647..f19e692 100644
--- a/rx.h
+++ b/rx.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2006-2015, Marvell International Ltd.
+ * Copyright (C) 2006-2016, Marvell International Ltd.
*
* This software file (the "File") is distributed by Marvell International
* Ltd. under the terms of the GNU General Public License Version 2, June 1991
diff --git a/sysadpt.h b/sysadpt.h
index 9944197..a5d2182 100644
--- a/sysadpt.h
+++ b/sysadpt.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2006-2015, Marvell International Ltd.
+ * Copyright (C) 2006-2016, Marvell International Ltd.
*
* This software file (the "File") is distributed by Marvell International
* Ltd. under the terms of the GNU General Public License Version 2, June 1991
@@ -70,4 +70,12 @@
#define SYSADPT_MAX_TID 8
+#define SYSADPT_QUIET_PERIOD_DEFAULT 100
+
+#define SYSADPT_QUIET_PERIOD_MIN 25
+
+#define SYSADPT_QUIET_START_OFFSET 10
+
+#define SYSADPT_THERMAL_THROTTLE_MAX 100
+
#endif /* _SYSADPT_H_ */
diff --git a/test/AP+STA.zip b/test/AP+STA.zip
new file mode 100755
index 0000000..8223745
--- /dev/null
+++ b/test/AP+STA.zip
Binary files differ
diff --git a/thermal.c b/thermal.c
new file mode 100644
index 0000000..dc05d4b
--- /dev/null
+++ b/thermal.c
@@ -0,0 +1,182 @@
+/*
+ * Copyright (C) 2006-2016, Marvell International Ltd.
+ *
+ * This software file (the "File") is distributed by Marvell International
+ * Ltd. under the terms of the GNU General Public License Version 2, June 1991
+ * (the "License"). You may use, redistribute and/or modify this File in
+ * accordance with the terms and conditions of the License, a copy of which
+ * is available by writing to the Free Software Foundation, Inc.
+ *
+ * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE
+ * ARE EXPRESSLY DISCLAIMED. The License provides additional details about
+ * this warranty disclaimer.
+ */
+
+/* Description: This file implements thermal framework related functions. */
+
+#include <linux/device.h>
+#include <linux/sysfs.h>
+#include <linux/thermal.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+
+#include "sysadpt.h"
+#include "dev.h"
+#include "fwcmd.h"
+#include "thermal.h"
+
+static int
+mwl_thermal_get_max_throttle_state(struct thermal_cooling_device *cdev,
+ unsigned long *state)
+{
+ *state = SYSADPT_THERMAL_THROTTLE_MAX;
+
+ return 0;
+}
+
+static int
+mwl_thermal_get_cur_throttle_state(struct thermal_cooling_device *cdev,
+ unsigned long *state)
+{
+ struct mwl_priv *priv = cdev->devdata;
+
+ *state = priv->throttle_state;
+
+ return 0;
+}
+
+static int
+mwl_thermal_set_cur_throttle_state(struct thermal_cooling_device *cdev,
+ unsigned long throttle_state)
+{
+ struct mwl_priv *priv = cdev->devdata;
+
+ if (throttle_state > SYSADPT_THERMAL_THROTTLE_MAX) {
+ wiphy_warn(priv->hw->wiphy,
+ "throttle state %ld is exceeding the limit %d\n",
+ throttle_state, SYSADPT_THERMAL_THROTTLE_MAX);
+ return -EINVAL;
+ }
+ priv->throttle_state = throttle_state;
+ mwl_thermal_set_throttling(priv);
+
+ return 0;
+}
+
+static struct thermal_cooling_device_ops mwl_thermal_ops = {
+ .get_max_state = mwl_thermal_get_max_throttle_state,
+ .get_cur_state = mwl_thermal_get_cur_throttle_state,
+ .set_cur_state = mwl_thermal_set_cur_throttle_state,
+};
+
+static ssize_t mwl_thermal_show_temp(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct mwl_priv *priv = dev_get_drvdata(dev);
+ int ret, temperature;
+
+ ret = mwl_fwcmd_get_temp(priv->hw, &priv->temperature);
+ if (ret) {
+ wiphy_warn(priv->hw->wiphy, "failed: can't get temperature\n");
+ goto out;
+ }
+
+ temperature = priv->temperature;
+
+ /* display in millidegree celcius */
+ ret = snprintf(buf, PAGE_SIZE, "%d\n", temperature * 1000);
+out:
+ return ret;
+}
+
+static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, mwl_thermal_show_temp,
+ NULL, 0);
+
+static struct attribute *mwl_hwmon_attrs[] = {
+ &sensor_dev_attr_temp1_input.dev_attr.attr,
+ NULL,
+};
+ATTRIBUTE_GROUPS(mwl_hwmon);
+
+void mwl_thermal_set_throttling(struct mwl_priv *priv)
+{
+ u32 period, duration, enabled;
+ int ret;
+
+ period = priv->quiet_period;
+ duration = (period * priv->throttle_state) / 100;
+ enabled = duration ? 1 : 0;
+
+ ret = mwl_fwcmd_quiet_mode(priv->hw, enabled, period,
+ duration, SYSADPT_QUIET_START_OFFSET);
+ if (ret) {
+ wiphy_warn(priv->hw->wiphy,
+ "failed: period %u duarion %u enabled %u ret %d\n",
+ period, duration, enabled, ret);
+ }
+}
+
+int mwl_thermal_register(struct mwl_priv *priv)
+{
+ struct thermal_cooling_device *cdev;
+ struct device *hwmon_dev;
+ int ret;
+
+ if (priv->chip_type != MWL8897)
+ return 0;
+
+ cdev = thermal_cooling_device_register("mwlwifi_thermal", priv,
+ &mwl_thermal_ops);
+ if (IS_ERR(cdev)) {
+ wiphy_err(priv->hw->wiphy,
+ "failed to setup thermal device result: %ld\n",
+ PTR_ERR(cdev));
+ return -EINVAL;
+ }
+
+ ret = sysfs_create_link(&priv->dev->kobj, &cdev->device.kobj,
+ "cooling_device");
+ if (ret) {
+ wiphy_err(priv->hw->wiphy,
+ "failed to create cooling device symlink\n");
+ goto err_cooling_destroy;
+ }
+
+ priv->cdev = cdev;
+ priv->quiet_period = SYSADPT_QUIET_PERIOD_DEFAULT;
+
+ if (!config_enabled(CONFIG_HWMON))
+ return 0;
+
+ hwmon_dev =
+ devm_hwmon_device_register_with_groups(priv->dev,
+ "mwlwifi_hwmon", priv,
+ mwl_hwmon_groups);
+ if (IS_ERR(hwmon_dev)) {
+ wiphy_err(priv->hw->wiphy,
+ "failed to register hwmon device: %ld\n",
+ PTR_ERR(hwmon_dev));
+ ret = -EINVAL;
+ goto err_remove_link;
+ }
+
+ return 0;
+
+err_remove_link:
+ sysfs_remove_link(&priv->dev->kobj, "cooling_device");
+err_cooling_destroy:
+ thermal_cooling_device_unregister(cdev);
+
+ return ret;
+}
+
+void mwl_thermal_unregister(struct mwl_priv *priv)
+{
+ if (priv->chip_type != MWL8897)
+ return;
+
+ sysfs_remove_link(&priv->dev->kobj, "cooling_device");
+ thermal_cooling_device_unregister(priv->cdev);
+}
diff --git a/thermal.h b/thermal.h
new file mode 100644
index 0000000..fcdcb47
--- /dev/null
+++ b/thermal.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2006-2016, Marvell International Ltd.
+ *
+ * This software file (the "File") is distributed by Marvell International
+ * Ltd. under the terms of the GNU General Public License Version 2, June 1991
+ * (the "License"). You may use, redistribute and/or modify this File in
+ * accordance with the terms and conditions of the License, a copy of which
+ * is available by writing to the Free Software Foundation, Inc.
+ *
+ * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE
+ * ARE EXPRESSLY DISCLAIMED. The License provides additional details about
+ * this warranty disclaimer.
+ */
+
+/* Description: This file defines Linux thermal framework related functions. */
+
+#ifndef _THERMAL_H_
+#define _THERMAL_H_
+
+#ifdef CONFIG_THERMAL
+int mwl_thermal_register(struct mwl_priv *priv);
+void mwl_thermal_unregister(struct mwl_priv *priv);
+void mwl_thermal_set_throttling(struct mwl_priv *priv);
+#else
+static inline int mwl_thermal_register(struct mwl_priv *priv)
+{
+ return 0;
+}
+
+static inline void mwl_thermal_unregister(struct mwl_priv *priv)
+{
+}
+
+static inline void mwl_thermal_set_throttling(struct mwl_priv *priv)
+{
+}
+#endif
+
+#endif /* _THERMAL_H_ */
diff --git a/tx.c b/tx.c
index 6c1651b..345cc78 100644
--- a/tx.c
+++ b/tx.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2006-2015, Marvell International Ltd.
+ * Copyright (C) 2006-2016, Marvell International Ltd.
*
* This software file (the "File") is distributed by Marvell International
* Ltd. under the terms of the GNU General Public License Version 2, June 1991
@@ -532,12 +532,6 @@ static inline struct sk_buff *mwl_tx_do_amsdu(struct mwl_priv *priv,
mwl_tx_skb(priv, desc_num, amsdu->skb);
amsdu->num = 0;
amsdu->cur_pos = NULL;
-
- if (!mwl_tx_available(priv, desc_num)) {
- skb_queue_head(&priv->txq[desc_num], tx_skb);
- spin_unlock_bh(&sta_info->amsdu_lock);
- return NULL;
- }
}
spin_unlock_bh(&sta_info->amsdu_lock);
return tx_skb;
@@ -637,47 +631,6 @@ static inline struct sk_buff *mwl_tx_do_amsdu(struct mwl_priv *priv,
return NULL;
}
-static inline void mwl_tx_skbs(struct ieee80211_hw *hw)
-{
- struct mwl_priv *priv = hw->priv;
- int num = SYSADPT_TX_WMM_QUEUES;
- struct sk_buff *tx_skb;
-
- spin_lock_bh(&priv->tx_desc_lock);
- while (num--) {
- while (skb_queue_len(&priv->txq[num]) > 0) {
- struct ieee80211_tx_info *tx_info;
- struct mwl_tx_ctrl *tx_ctrl;
-
- if (!mwl_tx_available(priv, num))
- break;
-
- tx_skb = skb_dequeue(&priv->txq[num]);
- tx_info = IEEE80211_SKB_CB(tx_skb);
- tx_ctrl = (struct mwl_tx_ctrl *)&tx_info->status;
-
- if ((tx_skb->protocol != cpu_to_be16(ETH_P_PAE)) &&
- (tx_ctrl->tx_priority >= SYSADPT_TX_WMM_QUEUES)) {
- tx_skb = mwl_tx_do_amsdu(priv, num,
- tx_skb, tx_info);
- }
-
- if (tx_skb)
- mwl_tx_skb(priv, num, tx_skb);
- }
-
- if (skb_queue_len(&priv->txq[num]) <
- SYSADPT_TX_WAKE_Q_THRESHOLD) {
- int queue;
-
- queue = SYSADPT_TX_WMM_QUEUES - num - 1;
- if (ieee80211_queue_stopped(hw, queue))
- ieee80211_wake_queue(hw, queue);
- }
- }
- spin_unlock_bh(&priv->tx_desc_lock);
-}
-
static inline void mwl_tx_prepare_info(struct ieee80211_hw *hw, u32 rate,
struct ieee80211_tx_info *info)
{
@@ -964,7 +917,7 @@ void mwl_tx_xmit(struct ieee80211_hw *hw,
skb_queue_tail(&priv->txq[index], skb);
- mwl_tx_skbs(hw);
+ tasklet_schedule(&priv->tx_task);
/* Initiate the ampdu session here */
if (start_ba_session) {
@@ -1086,6 +1039,52 @@ void mwl_tx_del_ampdu_pkts(struct ieee80211_hw *hw,
spin_unlock_bh(&sta_info->amsdu_lock);
}
+void mwl_tx_skbs(unsigned long data)
+{
+ struct ieee80211_hw *hw = (struct ieee80211_hw *)data;
+ struct mwl_priv *priv = hw->priv;
+ int num = SYSADPT_TX_WMM_QUEUES;
+ struct sk_buff *tx_skb;
+
+ spin_lock_bh(&priv->tx_desc_lock);
+ while (num--) {
+ while (skb_queue_len(&priv->txq[num]) > 0) {
+ struct ieee80211_tx_info *tx_info;
+ struct mwl_tx_ctrl *tx_ctrl;
+
+ if (!mwl_tx_available(priv, num))
+ break;
+
+ tx_skb = skb_dequeue(&priv->txq[num]);
+ tx_info = IEEE80211_SKB_CB(tx_skb);
+ tx_ctrl = (struct mwl_tx_ctrl *)&tx_info->status;
+
+ if ((tx_skb->protocol != cpu_to_be16(ETH_P_PAE)) &&
+ (tx_ctrl->tx_priority >= SYSADPT_TX_WMM_QUEUES)) {
+ tx_skb = mwl_tx_do_amsdu(priv, num,
+ tx_skb, tx_info);
+ }
+
+ if (tx_skb) {
+ if (mwl_tx_available(priv, num))
+ mwl_tx_skb(priv, num, tx_skb);
+ else
+ skb_queue_head(&priv->txq[num], tx_skb);
+ }
+ }
+
+ if (skb_queue_len(&priv->txq[num]) <
+ SYSADPT_TX_WAKE_Q_THRESHOLD) {
+ int queue;
+
+ queue = SYSADPT_TX_WMM_QUEUES - num - 1;
+ if (ieee80211_queue_stopped(hw, queue))
+ ieee80211_wake_queue(hw, queue);
+ }
+ }
+ spin_unlock_bh(&priv->tx_desc_lock);
+}
+
void mwl_tx_done(unsigned long data)
{
struct ieee80211_hw *hw = (struct ieee80211_hw *)data;
@@ -1108,6 +1107,12 @@ void mwl_tx_done(unsigned long data)
tx_hndl = desc->pstale_tx_hndl;
tx_desc = tx_hndl->pdesc;
+ if ((tx_desc->status &
+ cpu_to_le32(EAGLE_TXD_STATUS_FW_OWNED)) &&
+ (tx_hndl->pnext->pdesc->status &
+ cpu_to_le32(EAGLE_TXD_STATUS_OK)))
+ tx_desc->status = cpu_to_le32(EAGLE_TXD_STATUS_OK);
+
while (tx_hndl &&
(tx_desc->status & cpu_to_le32(EAGLE_TXD_STATUS_OK)) &&
(!(tx_desc->status &
@@ -1118,6 +1123,7 @@ void mwl_tx_done(unsigned long data)
PCI_DMA_TODEVICE);
done_skb = tx_hndl->psk_buff;
rate = le32_to_cpu(tx_desc->rate_info);
+ tx_desc->pkt_ptr = 0;
tx_desc->pkt_len = 0;
tx_desc->status =
cpu_to_le32(EAGLE_TXD_STATUS_IDLE);
@@ -1169,7 +1175,7 @@ void mwl_tx_done(unsigned long data)
}
spin_unlock_bh(&priv->tx_desc_lock);
- if (priv->irq != -1) {
+ if (priv->is_tx_done_schedule) {
u32 status_mask;
status_mask = readl(priv->iobase1 +
@@ -1177,10 +1183,9 @@ void mwl_tx_done(unsigned long data)
writel(status_mask | MACREG_A2HRIC_BIT_TX_DONE,
priv->iobase1 + MACREG_REG_A2H_INTERRUPT_STATUS_MASK);
- mwl_tx_skbs(hw);
+ tasklet_schedule(&priv->tx_task);
+ priv->is_tx_done_schedule = false;
}
-
- priv->is_tx_schedule = false;
}
void mwl_tx_flush_amsdu(unsigned long data)
diff --git a/tx.h b/tx.h
index e4b4ff4..eaacc3b 100644
--- a/tx.h
+++ b/tx.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2006-2015, Marvell International Ltd.
+ * Copyright (C) 2006-2016, Marvell International Ltd.
*
* This software file (the "File") is distributed by Marvell International
* Ltd. under the terms of the GNU General Public License Version 2, June 1991
@@ -29,6 +29,7 @@ void mwl_tx_del_pkts_via_sta(struct ieee80211_hw *hw,
struct ieee80211_sta *sta);
void mwl_tx_del_ampdu_pkts(struct ieee80211_hw *hw,
struct ieee80211_sta *sta, u8 tid);
+void mwl_tx_skbs(unsigned long data);
void mwl_tx_done(unsigned long data);
void mwl_tx_flush_amsdu(unsigned long data);
void mwl_tx_del_sta_amsdu_pkts(struct ieee80211_sta *sta);