From 6988b67d9af987a7ba47f82cdaa6c9107e55ec3f Mon Sep 17 00:00:00 2001 From: David Lin Date: Fri, 25 Mar 2016 11:16:14 +0800 Subject: Commit mwlwifi driver 10.3.0.17-20160324. 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 --- Makefile | 7 +- Makefile.external | 7 +- Makefile.kernel | 3 +- bin/firmware/88W8864.bin | Bin 116356 -> 117828 bytes debugfs.c | 476 +++++++++++++++++++++++++++++++++-------------- debugfs.h | 2 +- dev.h | 13 +- fwcmd.c | 2 +- fwcmd.h | 2 +- fwdl.c | 14 +- fwdl.h | 2 +- hostcmd.h | 2 +- isr.c | 26 ++- isr.h | 2 +- mac80211.c | 11 +- main.c | 26 ++- rx.c | 2 +- rx.h | 2 +- sysadpt.h | 10 +- test/AP+STA.zip | Bin 0 -> 4534 bytes thermal.c | 182 ++++++++++++++++++ thermal.h | 40 ++++ tx.c | 111 +++++------ tx.h | 3 +- 24 files changed, 716 insertions(+), 229 deletions(-) create mode 100755 test/AP+STA.zip create mode 100644 thermal.c create mode 100644 thermal.h 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 Binary files a/bin/firmware/88W8864.bin and b/bin/firmware/88W8864.bin 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 #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 Binary files /dev/null and b/test/AP+STA.zip 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 +#include +#include +#include +#include + +#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); -- cgit v1.2.3