提交 b8fa3bfb 编写于 作者: D David S. Miller

Merge tag 'wireless-drivers-next-for-davem-2017-12-18' of...

Merge tag 'wireless-drivers-next-for-davem-2017-12-18' of git://git.kernel.org/pub/scm/linux/kernel/git/kvalo/wireless-drivers-next

The drivers/net/wireless/intel/iwlwifi/pcie/drv.c conflict was
resolved using a diff provided by Kalle in his pull request.

Kalle Valo says:

====================
wireless-drivers-next patches for 4.16

A bigger pull request this time, the most visible change being the new
driver mt76. But there's also Kconfig refactoring in ath9k and ath10k,
work beginning in iwlwifi to have rate scaling in firmware/hardware,
wcn3990 support getting closer in ath10k and lots of smaller changes.

mt76

* a new driver for MT76x2e, a 2x2 PCIe 802.11ac chipset by MediaTek

ath10k

* enable multiqueue support for all hw using mac80211 wake_tx_queue op

* new Kconfig option ATH10K_SPECTRAL to save RAM

* show tx stats on QCA9880

* new qcom,ath10k-calibration-variant DT entry

* WMI layer support for wcn3990

ath9k

* new Kconfig option ATH9K_COMMON_SPECTRAL to save RAM

wcn36xx

* hardware scan offload support

wil6210

* run-time PM support when interface is down

iwlwifi

* initial work for rate-scaling offload

* Support for new FW API version 36

* Rename the temporary hw name A000 to 22000

ssb

* make SSB a menuconfig to ease disabling it all

mwl8k

* enable non-DFS 5G channels 149-165
====================
Signed-off-by: NDavid S. Miller <davem@davemloft.net>
* MediaTek mt76xx devices
This node provides properties for configuring the MediaTek mt76xx wireless
device. The node is expected to be specified as a child node of the PCI
controller to which the wireless chip is connected.
Optional properties:
- mac-address: See ethernet.txt in the parent directory
- local-mac-address: See ethernet.txt in the parent directory
- ieee80211-freq-limit: See ieee80211.txt
- mediatek,mtd-eeprom: Specify a MTD partition + offset containing EEPROM data
Optional nodes:
- led: Properties for a connected LED
Optional properties:
- led-sources: See Documentation/devicetree/bindings/leds/common.txt
&pcie {
pcie0 {
wifi@0,0 {
compatible = "mediatek,mt76";
reg = <0x0000 0 0 0 0>;
ieee80211-freq-limit = <5000000 6000000>;
mediatek,mtd-eeprom = <&factory 0x8000>;
led {
led-sources = <2>;
};
};
};
};
......@@ -41,6 +41,9 @@ Optional properties:
- qcom,msi_addr: MSI interrupt address.
- qcom,msi_base: Base value to add before writing MSI data into
MSI address register.
- qcom,ath10k-calibration-variant: string to search for in the board-2.bin
variant list with the same bus and device
specific ids
- qcom,ath10k-calibration-data : calibration data + board specific data
as an array, the length can vary between
hw versions.
......
......@@ -83,7 +83,8 @@ static void bcma_core_pcie2_hw_ltr_war(struct bcma_drv_pcie2 *pcie2)
bcma_core_pcie2_set_ltr_vals(pcie2);
/* TODO:
si_core_wrapperreg(pcie2, 3, 0x60, 0x8080, 0); */
*si_core_wrapperreg(pcie2, 3, 0x60, 0x8080, 0);
*/
/* enable the LTR */
devstsctr2 |= PCIE2_CAP_DEVSTSCTRL2_LTRENAB;
......
......@@ -47,12 +47,19 @@ config ATH10K_DEBUG
config ATH10K_DEBUGFS
bool "Atheros ath10k debugfs support"
depends on ATH10K && DEBUG_FS
select RELAY
---help---
Enabled debugfs support
If unsure, say Y to make it easier to debug problems.
config ATH10K_SPECTRAL
bool "Atheros ath10k spectral scan support"
depends on ATH10K_DEBUGFS
select RELAY
default n
---help---
Say Y to enable access to the FFT/spectral data via debugfs.
config ATH10K_TRACING
bool "Atheros ath10k tracing support"
depends on ATH10K
......
......@@ -15,7 +15,7 @@ ath10k_core-y += mac.o \
p2p.o \
swap.o
ath10k_core-$(CONFIG_ATH10K_DEBUGFS) += spectral.o
ath10k_core-$(CONFIG_ATH10K_SPECTRAL) += spectral.o
ath10k_core-$(CONFIG_NL80211_TESTMODE) += testmode.o
ath10k_core-$(CONFIG_ATH10K_TRACING) += trace.o
ath10k_core-$(CONFIG_THERMAL) += thermal.o
......
......@@ -75,6 +75,9 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = {
.vht160_mcs_rx_highest = 0,
.vht160_mcs_tx_highest = 0,
.n_cipher_suites = 8,
.num_peers = TARGET_TLV_NUM_PEERS,
.ast_skid_limit = 0x10,
.num_wds_entries = 0x20,
},
{
.id = QCA9887_HW_1_0_VERSION,
......@@ -99,6 +102,9 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = {
.vht160_mcs_rx_highest = 0,
.vht160_mcs_tx_highest = 0,
.n_cipher_suites = 8,
.num_peers = TARGET_TLV_NUM_PEERS,
.ast_skid_limit = 0x10,
.num_wds_entries = 0x20,
},
{
.id = QCA6174_HW_2_1_VERSION,
......@@ -122,6 +128,9 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = {
.vht160_mcs_rx_highest = 0,
.vht160_mcs_tx_highest = 0,
.n_cipher_suites = 8,
.num_peers = TARGET_TLV_NUM_PEERS,
.ast_skid_limit = 0x10,
.num_wds_entries = 0x20,
},
{
.id = QCA6174_HW_2_1_VERSION,
......@@ -145,6 +154,9 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = {
.vht160_mcs_rx_highest = 0,
.vht160_mcs_tx_highest = 0,
.n_cipher_suites = 8,
.num_peers = TARGET_TLV_NUM_PEERS,
.ast_skid_limit = 0x10,
.num_wds_entries = 0x20,
},
{
.id = QCA6174_HW_3_0_VERSION,
......@@ -168,6 +180,9 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = {
.vht160_mcs_rx_highest = 0,
.vht160_mcs_tx_highest = 0,
.n_cipher_suites = 8,
.num_peers = TARGET_TLV_NUM_PEERS,
.ast_skid_limit = 0x10,
.num_wds_entries = 0x20,
},
{
.id = QCA6174_HW_3_2_VERSION,
......@@ -194,6 +209,9 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = {
.vht160_mcs_rx_highest = 0,
.vht160_mcs_tx_highest = 0,
.n_cipher_suites = 8,
.num_peers = TARGET_TLV_NUM_PEERS,
.ast_skid_limit = 0x10,
.num_wds_entries = 0x20,
},
{
.id = QCA99X0_HW_2_0_DEV_VERSION,
......@@ -223,6 +241,9 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = {
.vht160_mcs_rx_highest = 0,
.vht160_mcs_tx_highest = 0,
.n_cipher_suites = 11,
.num_peers = TARGET_TLV_NUM_PEERS,
.ast_skid_limit = 0x10,
.num_wds_entries = 0x20,
},
{
.id = QCA9984_HW_1_0_DEV_VERSION,
......@@ -257,6 +278,9 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = {
.vht160_mcs_rx_highest = 1560,
.vht160_mcs_tx_highest = 1560,
.n_cipher_suites = 11,
.num_peers = TARGET_TLV_NUM_PEERS,
.ast_skid_limit = 0x10,
.num_wds_entries = 0x20,
},
{
.id = QCA9888_HW_2_0_DEV_VERSION,
......@@ -290,6 +314,9 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = {
.vht160_mcs_rx_highest = 780,
.vht160_mcs_tx_highest = 780,
.n_cipher_suites = 11,
.num_peers = TARGET_TLV_NUM_PEERS,
.ast_skid_limit = 0x10,
.num_wds_entries = 0x20,
},
{
.id = QCA9377_HW_1_0_DEV_VERSION,
......@@ -313,6 +340,9 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = {
.vht160_mcs_rx_highest = 0,
.vht160_mcs_tx_highest = 0,
.n_cipher_suites = 8,
.num_peers = TARGET_TLV_NUM_PEERS,
.ast_skid_limit = 0x10,
.num_wds_entries = 0x20,
},
{
.id = QCA9377_HW_1_1_DEV_VERSION,
......@@ -338,6 +368,9 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = {
.vht160_mcs_rx_highest = 0,
.vht160_mcs_tx_highest = 0,
.n_cipher_suites = 8,
.num_peers = TARGET_TLV_NUM_PEERS,
.ast_skid_limit = 0x10,
.num_wds_entries = 0x20,
},
{
.id = QCA4019_HW_1_0_DEV_VERSION,
......@@ -368,6 +401,27 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = {
.vht160_mcs_rx_highest = 0,
.vht160_mcs_tx_highest = 0,
.n_cipher_suites = 11,
.num_peers = TARGET_TLV_NUM_PEERS,
.ast_skid_limit = 0x10,
.num_wds_entries = 0x20,
},
{
.id = WCN3990_HW_1_0_DEV_VERSION,
.dev_id = 0,
.name = "wcn3990 hw1.0",
.continuous_frag_desc = true,
.tx_chain_mask = 0x7,
.rx_chain_mask = 0x7,
.max_spatial_stream = 4,
.fw = {
.dir = WCN3990_HW_1_0_FW_DIR,
},
.sw_decrypt_mcast_mgmt = true,
.hw_ops = &wcn3990_ops,
.decap_align_bytes = 1,
.num_peers = TARGET_HL_10_TLV_NUM_PEERS,
.ast_skid_limit = TARGET_HL_10_TLV_AST_SKID_LIMIT,
.num_wds_entries = TARGET_HL_10_TLV_NUM_WDS_ENTRIES,
},
};
......@@ -390,6 +444,7 @@ static const char *const ath10k_core_fw_feature_str[] = {
[ATH10K_FW_FEATURE_SKIP_NULL_FUNC_WAR] = "skip-null-func-war",
[ATH10K_FW_FEATURE_ALLOWS_MESH_BCAST] = "allows-mesh-bcast",
[ATH10K_FW_FEATURE_NO_PS] = "no-ps",
[ATH10K_FW_FEATURE_MGMT_TX_BY_REF] = "mgmt-tx-by-reference",
};
static unsigned int ath10k_core_get_fw_feature_str(char *buf,
......@@ -860,6 +915,28 @@ static int ath10k_core_check_smbios(struct ath10k *ar)
return 0;
}
static int ath10k_core_check_dt(struct ath10k *ar)
{
struct device_node *node;
const char *variant = NULL;
node = ar->dev->of_node;
if (!node)
return -ENOENT;
of_property_read_string(node, "qcom,ath10k-calibration-variant",
&variant);
if (!variant)
return -ENODATA;
if (strscpy(ar->id.bdf_ext, variant, sizeof(ar->id.bdf_ext)) < 0)
ath10k_dbg(ar, ATH10K_DBG_BOOT,
"bdf variant string is longer than the buffer can accommodate (variant: %s)\n",
variant);
return 0;
}
static int ath10k_download_and_run_otp(struct ath10k *ar)
{
u32 result, address = ar->hw_params.patch_load_addr;
......@@ -1231,19 +1308,19 @@ static int ath10k_core_create_board_name(struct ath10k *ar, char *name,
/* strlen(',variant=') + strlen(ar->id.bdf_ext) */
char variant[9 + ATH10K_SMBIOS_BDF_EXT_STR_LENGTH] = { 0 };
if (ar->id.bdf_ext[0] != '\0')
scnprintf(variant, sizeof(variant), ",variant=%s",
ar->id.bdf_ext);
if (ar->id.bmi_ids_valid) {
scnprintf(name, name_len,
"bus=%s,bmi-chip-id=%d,bmi-board-id=%d",
"bus=%s,bmi-chip-id=%d,bmi-board-id=%d%s",
ath10k_bus_str(ar->hif.bus),
ar->id.bmi_chip_id,
ar->id.bmi_board_id);
ar->id.bmi_board_id, variant);
goto out;
}
if (ar->id.bdf_ext[0] != '\0')
scnprintf(variant, sizeof(variant), ",variant=%s",
ar->id.bdf_ext);
scnprintf(name, name_len,
"bus=%s,vendor=%04x,device=%04x,subsystem-vendor=%04x,subsystem-device=%04x%s",
ath10k_bus_str(ar->hif.bus),
......@@ -2343,7 +2420,11 @@ static int ath10k_core_probe_fw(struct ath10k *ar)
ret = ath10k_core_check_smbios(ar);
if (ret)
ath10k_dbg(ar, ATH10K_DBG_BOOT, "bdf variant name not set.\n");
ath10k_dbg(ar, ATH10K_DBG_BOOT, "SMBIOS bdf variant name not set.\n");
ret = ath10k_core_check_dt(ar);
if (ret)
ath10k_dbg(ar, ATH10K_DBG_BOOT, "DT bdf variant name not set.\n");
ret = ath10k_core_fetch_board_file(ar);
if (ret) {
......
......@@ -67,7 +67,6 @@
/* NAPI poll budget */
#define ATH10K_NAPI_BUDGET 64
#define ATH10K_NAPI_QUOTA_LIMIT 60
/* SMBIOS type containing Board Data File Name Extension */
#define ATH10K_SMBIOS_BDF_EXT_TYPE 0xF8
......@@ -364,11 +363,11 @@ struct ath10k_sta {
struct rate_info txrate;
struct work_struct update_wk;
u64 rx_duration;
#ifdef CONFIG_MAC80211_DEBUGFS
/* protected by conf_mutex */
bool aggr_mode;
u64 rx_duration;
#endif
};
......@@ -463,7 +462,7 @@ struct ath10k_fw_crash_data {
bool crashed_since_read;
guid_t guid;
struct timespec timestamp;
struct timespec64 timestamp;
__le32 registers[REG_DUMP_COUNT_QCA988X];
struct ath10k_ce_crash_data ce_crash_data[CE_COUNT_MAX];
};
......@@ -488,7 +487,6 @@ struct ath10k_debug {
/* protected by conf_mutex */
u64 fw_dbglog_mask;
u32 fw_dbglog_level;
u32 pktlog_filter;
u32 reg_addr;
u32 nf_cal_period;
void *cal_data;
......@@ -615,6 +613,9 @@ enum ath10k_fw_features {
/* Firmware does not support power save in station mode. */
ATH10K_FW_FEATURE_NO_PS = 17,
/* Firmware allows management tx by reference instead of by value. */
ATH10K_FW_FEATURE_MGMT_TX_BY_REF = 18,
/* keep last */
ATH10K_FW_FEATURE_COUNT,
};
......@@ -963,6 +964,7 @@ struct ath10k {
} spectral;
#endif
u32 pktlog_filter;
struct {
/* protected by conf_mutex */
struct ath10k_fw_components utf_mode_fw;
......
......@@ -720,7 +720,7 @@ ath10k_debug_get_new_fw_crash_data(struct ath10k *ar)
crash_data->crashed_since_read = true;
guid_gen(&crash_data->guid);
getnstimeofday(&crash_data->timestamp);
ktime_get_real_ts64(&crash_data->timestamp);
return crash_data;
}
......@@ -1950,14 +1950,14 @@ int ath10k_debug_start(struct ath10k *ar)
ret);
}
if (ar->debug.pktlog_filter) {
if (ar->pktlog_filter) {
ret = ath10k_wmi_pdev_pktlog_enable(ar,
ar->debug.pktlog_filter);
ar->pktlog_filter);
if (ret)
/* not serious */
ath10k_warn(ar,
"failed to enable pktlog filter %x: %d\n",
ar->debug.pktlog_filter, ret);
ar->pktlog_filter, ret);
} else {
ret = ath10k_wmi_pdev_pktlog_disable(ar);
if (ret)
......@@ -2097,12 +2097,12 @@ static ssize_t ath10k_write_pktlog_filter(struct file *file,
mutex_lock(&ar->conf_mutex);
if (ar->state != ATH10K_STATE_ON) {
ar->debug.pktlog_filter = filter;
ar->pktlog_filter = filter;
ret = count;
goto out;
}
if (filter == ar->debug.pktlog_filter) {
if (filter == ar->pktlog_filter) {
ret = count;
goto out;
}
......@@ -2111,7 +2111,7 @@ static ssize_t ath10k_write_pktlog_filter(struct file *file,
ret = ath10k_wmi_pdev_pktlog_enable(ar, filter);
if (ret) {
ath10k_warn(ar, "failed to enable pktlog filter %x: %d\n",
ar->debug.pktlog_filter, ret);
ar->pktlog_filter, ret);
goto out;
}
} else {
......@@ -2122,7 +2122,7 @@ static ssize_t ath10k_write_pktlog_filter(struct file *file,
}
}
ar->debug.pktlog_filter = filter;
ar->pktlog_filter = filter;
ret = count;
out:
......@@ -2139,7 +2139,7 @@ static ssize_t ath10k_read_pktlog_filter(struct file *file, char __user *ubuf,
mutex_lock(&ar->conf_mutex);
len = scnprintf(buf, sizeof(buf) - len, "%08x\n",
ar->debug.pktlog_filter);
ar->pktlog_filter);
mutex_unlock(&ar->conf_mutex);
return simple_read_from_buffer(ubuf, count, ppos, buf, len);
......
......@@ -51,7 +51,8 @@ enum ath10k_pktlog_filter {
ATH10K_PKTLOG_RCFIND = 0x000000004,
ATH10K_PKTLOG_RCUPDATE = 0x000000008,
ATH10K_PKTLOG_DBG_PRINT = 0x000000010,
ATH10K_PKTLOG_ANY = 0x00000001f,
ATH10K_PKTLOG_PEER_STATS = 0x000000040,
ATH10K_PKTLOG_ANY = 0x00000005f,
};
enum ath10k_dbg_aggr_mode {
......@@ -60,6 +61,21 @@ enum ath10k_dbg_aggr_mode {
ATH10K_DBG_AGGR_MODE_MAX,
};
/* Types of packet log events */
enum ath_pktlog_type {
ATH_PKTLOG_TYPE_TX_CTRL = 1,
ATH_PKTLOG_TYPE_TX_STAT,
};
struct ath10k_pktlog_hdr {
__le16 flags;
__le16 missed_cnt;
__le16 log_type; /* Type of log information foll this header */
__le16 size; /* Size of variable length log information in bytes */
__le32 timestamp;
u8 payload[0];
} __packed;
/* FIXME: How to calculate the buffer size sanely? */
#define ATH10K_FW_STATS_BUF_SIZE (1024 * 1024)
......@@ -190,9 +206,6 @@ void ath10k_sta_add_debugfs(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
struct ieee80211_sta *sta, struct dentry *dir);
void ath10k_sta_update_rx_duration(struct ath10k *ar,
struct ath10k_fw_stats *stats);
void ath10k_sta_statistics(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
struct ieee80211_sta *sta,
struct station_info *sinfo);
#else
static inline
void ath10k_sta_update_rx_duration(struct ath10k *ar,
......
......@@ -65,33 +65,6 @@ void ath10k_sta_update_rx_duration(struct ath10k *ar,
ath10k_sta_update_stats_rx_duration(ar, stats);
}
void ath10k_sta_statistics(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
struct ieee80211_sta *sta,
struct station_info *sinfo)
{
struct ath10k_sta *arsta = (struct ath10k_sta *)sta->drv_priv;
struct ath10k *ar = arsta->arvif->ar;
if (!ath10k_peer_stats_enabled(ar))
return;
sinfo->rx_duration = arsta->rx_duration;
sinfo->filled |= 1ULL << NL80211_STA_INFO_RX_DURATION;
if (!arsta->txrate.legacy && !arsta->txrate.nss)
return;
if (arsta->txrate.legacy) {
sinfo->txrate.legacy = arsta->txrate.legacy;
} else {
sinfo->txrate.mcs = arsta->txrate.mcs;
sinfo->txrate.nss = arsta->txrate.nss;
sinfo->txrate.bw = arsta->txrate.bw;
}
sinfo->txrate.flags = arsta->txrate.flags;
sinfo->filled |= 1ULL << NL80211_STA_INFO_TX_BITRATE;
}
static ssize_t ath10k_dbg_sta_read_aggr_mode(struct file *file,
char __user *user_buf,
size_t count, loff_t *ppos)
......
......@@ -1497,6 +1497,23 @@ struct htt_peer_tx_stats {
u8 payload[0];
} __packed;
#define ATH10K_10_2_TX_STATS_OFFSET 136
#define PEER_STATS_FOR_NO_OF_PPDUS 4
struct ath10k_10_2_peer_tx_stats {
u8 ratecode[PEER_STATS_FOR_NO_OF_PPDUS];
u8 success_pkts[PEER_STATS_FOR_NO_OF_PPDUS];
__le16 success_bytes[PEER_STATS_FOR_NO_OF_PPDUS];
u8 retry_pkts[PEER_STATS_FOR_NO_OF_PPDUS];
__le16 retry_bytes[PEER_STATS_FOR_NO_OF_PPDUS];
u8 failed_pkts[PEER_STATS_FOR_NO_OF_PPDUS];
__le16 failed_bytes[PEER_STATS_FOR_NO_OF_PPDUS];
u8 flags[PEER_STATS_FOR_NO_OF_PPDUS];
__le32 tx_duration;
u8 tx_ppdu_cnt;
u8 peer_id;
} __packed;
union htt_rx_pn_t {
/* WEP: 24-bit PN */
u32 pn24;
......@@ -1695,7 +1712,7 @@ struct ath10k_htt {
/* This is used to group tx/rx completions separately and process them
* in batches to reduce cache stalls
*/
struct sk_buff_head rx_compl_q;
struct sk_buff_head rx_msdus_q;
struct sk_buff_head rx_in_ord_compl_q;
struct sk_buff_head tx_fetch_ind_q;
......
......@@ -227,7 +227,7 @@ void ath10k_htt_rx_free(struct ath10k_htt *htt)
{
del_timer_sync(&htt->rx_ring.refill_retry_timer);
skb_queue_purge(&htt->rx_compl_q);
skb_queue_purge(&htt->rx_msdus_q);
skb_queue_purge(&htt->rx_in_ord_compl_q);
skb_queue_purge(&htt->tx_fetch_ind_q);
......@@ -515,7 +515,7 @@ int ath10k_htt_rx_alloc(struct ath10k_htt *htt)
htt->rx_ring.sw_rd_idx.msdu_payld = 0;
hash_init(htt->rx_ring.skb_table);
skb_queue_head_init(&htt->rx_compl_q);
skb_queue_head_init(&htt->rx_msdus_q);
skb_queue_head_init(&htt->rx_in_ord_compl_q);
skb_queue_head_init(&htt->tx_fetch_ind_q);
atomic_set(&htt->num_mpdus_ready, 0);
......@@ -974,16 +974,25 @@ static char *ath10k_get_tid(struct ieee80211_hdr *hdr, char *out, size_t size)
return out;
}
static void ath10k_process_rx(struct ath10k *ar,
struct ieee80211_rx_status *rx_status,
struct sk_buff *skb)
static void ath10k_htt_rx_h_queue_msdu(struct ath10k *ar,
struct ieee80211_rx_status *rx_status,
struct sk_buff *skb)
{
struct ieee80211_rx_status *status;
status = IEEE80211_SKB_RXCB(skb);
*status = *rx_status;
__skb_queue_tail(&ar->htt.rx_msdus_q, skb);
}
static void ath10k_process_rx(struct ath10k *ar, struct sk_buff *skb)
{
struct ieee80211_rx_status *status;
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
char tid[32];
status = IEEE80211_SKB_RXCB(skb);
*status = *rx_status;
ath10k_dbg(ar, ATH10K_DBG_DATA,
"rx skb %pK len %u peer %pM %s %s sn %u %s%s%s%s%s%s %srate_idx %u vht_nss %u freq %u band %u flag 0x%x fcs-err %i mic-err %i amsdu-more %i\n",
......@@ -1517,7 +1526,7 @@ static void ath10k_htt_rx_h_mpdu(struct ath10k *ar,
}
}
static void ath10k_htt_rx_h_deliver(struct ath10k *ar,
static void ath10k_htt_rx_h_enqueue(struct ath10k *ar,
struct sk_buff_head *amsdu,
struct ieee80211_rx_status *status)
{
......@@ -1540,7 +1549,7 @@ static void ath10k_htt_rx_h_deliver(struct ath10k *ar,
status->flag |= RX_FLAG_ALLOW_SAME_PN;
}
ath10k_process_rx(ar, status, msdu);
ath10k_htt_rx_h_queue_msdu(ar, status, msdu);
}
}
......@@ -1652,7 +1661,7 @@ static int ath10k_htt_rx_handle_amsdu(struct ath10k_htt *htt)
struct ath10k *ar = htt->ar;
struct ieee80211_rx_status *rx_status = &htt->rx_status;
struct sk_buff_head amsdu;
int ret, num_msdus;
int ret;
__skb_queue_head_init(&amsdu);
......@@ -1674,7 +1683,6 @@ static int ath10k_htt_rx_handle_amsdu(struct ath10k_htt *htt)
return ret;
}
num_msdus = skb_queue_len(&amsdu);
ath10k_htt_rx_h_ppdu(ar, &amsdu, rx_status, 0xffff);
/* only for ret = 1 indicates chained msdus */
......@@ -1683,9 +1691,9 @@ static int ath10k_htt_rx_handle_amsdu(struct ath10k_htt *htt)
ath10k_htt_rx_h_filter(ar, &amsdu, rx_status);
ath10k_htt_rx_h_mpdu(ar, &amsdu, rx_status, true);
ath10k_htt_rx_h_deliver(ar, &amsdu, rx_status);
ath10k_htt_rx_h_enqueue(ar, &amsdu, rx_status);
return num_msdus;
return 0;
}
static void ath10k_htt_rx_proc_rx_ind(struct ath10k_htt *htt,
......@@ -1893,15 +1901,14 @@ static void ath10k_htt_rx_h_rx_offload_prot(struct ieee80211_rx_status *status,
RX_FLAG_MMIC_STRIPPED;
}
static int ath10k_htt_rx_h_rx_offload(struct ath10k *ar,
struct sk_buff_head *list)
static void ath10k_htt_rx_h_rx_offload(struct ath10k *ar,
struct sk_buff_head *list)
{
struct ath10k_htt *htt = &ar->htt;
struct ieee80211_rx_status *status = &htt->rx_status;
struct htt_rx_offload_msdu *rx;
struct sk_buff *msdu;
size_t offset;
int num_msdu = 0;
while ((msdu = __skb_dequeue(list))) {
/* Offloaded frames don't have Rx descriptor. Instead they have
......@@ -1940,10 +1947,8 @@ static int ath10k_htt_rx_h_rx_offload(struct ath10k *ar,
ath10k_htt_rx_h_rx_offload_prot(status, msdu);
ath10k_htt_rx_h_channel(ar, status, NULL, rx->vdev_id);
ath10k_process_rx(ar, status, msdu);
num_msdu++;
ath10k_htt_rx_h_queue_msdu(ar, status, msdu);
}
return num_msdu;
}
static int ath10k_htt_rx_in_ord_ind(struct ath10k *ar, struct sk_buff *skb)
......@@ -1959,7 +1964,7 @@ static int ath10k_htt_rx_in_ord_ind(struct ath10k *ar, struct sk_buff *skb)
u8 tid;
bool offload;
bool frag;
int ret, num_msdus = 0;
int ret;
lockdep_assert_held(&htt->rx_ring.lock);
......@@ -2001,7 +2006,7 @@ static int ath10k_htt_rx_in_ord_ind(struct ath10k *ar, struct sk_buff *skb)
* separately.
*/
if (offload)
num_msdus = ath10k_htt_rx_h_rx_offload(ar, &list);
ath10k_htt_rx_h_rx_offload(ar, &list);
while (!skb_queue_empty(&list)) {
__skb_queue_head_init(&amsdu);
......@@ -2014,11 +2019,10 @@ static int ath10k_htt_rx_in_ord_ind(struct ath10k *ar, struct sk_buff *skb)
* better to report something than nothing though. This
* should still give an idea about rx rate to the user.
*/
num_msdus += skb_queue_len(&amsdu);
ath10k_htt_rx_h_ppdu(ar, &amsdu, status, vdev_id);
ath10k_htt_rx_h_filter(ar, &amsdu, status);
ath10k_htt_rx_h_mpdu(ar, &amsdu, status, false);
ath10k_htt_rx_h_deliver(ar, &amsdu, status);
ath10k_htt_rx_h_enqueue(ar, &amsdu, status);
break;
case -EAGAIN:
/* fall through */
......@@ -2030,7 +2034,7 @@ static int ath10k_htt_rx_in_ord_ind(struct ath10k *ar, struct sk_buff *skb)
return -EIO;
}
}
return num_msdus;
return ret;
}
static void ath10k_htt_rx_tx_fetch_resp_id_confirm(struct ath10k *ar,
......@@ -2449,6 +2453,62 @@ static void ath10k_htt_fetch_peer_stats(struct ath10k *ar,
rcu_read_unlock();
}
static void ath10k_fetch_10_2_tx_stats(struct ath10k *ar, u8 *data)
{
struct ath10k_pktlog_hdr *hdr = (struct ath10k_pktlog_hdr *)data;
struct ath10k_per_peer_tx_stats *p_tx_stats = &ar->peer_tx_stats;
struct ath10k_10_2_peer_tx_stats *tx_stats;
struct ieee80211_sta *sta;
struct ath10k_peer *peer;
u16 log_type = __le16_to_cpu(hdr->log_type);
u32 peer_id = 0, i;
if (log_type != ATH_PKTLOG_TYPE_TX_STAT)
return;
tx_stats = (struct ath10k_10_2_peer_tx_stats *)((hdr->payload) +
ATH10K_10_2_TX_STATS_OFFSET);
if (!tx_stats->tx_ppdu_cnt)
return;
peer_id = tx_stats->peer_id;
rcu_read_lock();
spin_lock_bh(&ar->data_lock);
peer = ath10k_peer_find_by_id(ar, peer_id);
if (!peer) {
ath10k_warn(ar, "Invalid peer id %d in peer stats buffer\n",
peer_id);
goto out;
}
sta = peer->sta;
for (i = 0; i < tx_stats->tx_ppdu_cnt; i++) {
p_tx_stats->succ_bytes =
__le16_to_cpu(tx_stats->success_bytes[i]);
p_tx_stats->retry_bytes =
__le16_to_cpu(tx_stats->retry_bytes[i]);
p_tx_stats->failed_bytes =
__le16_to_cpu(tx_stats->failed_bytes[i]);
p_tx_stats->ratecode = tx_stats->ratecode[i];
p_tx_stats->flags = tx_stats->flags[i];
p_tx_stats->succ_pkts = tx_stats->success_pkts[i];
p_tx_stats->retry_pkts = tx_stats->retry_pkts[i];
p_tx_stats->failed_pkts = tx_stats->failed_pkts[i];
ath10k_update_per_peer_tx_stats(ar, sta, p_tx_stats);
}
spin_unlock_bh(&ar->data_lock);
rcu_read_unlock();
return;
out:
spin_unlock_bh(&ar->data_lock);
rcu_read_unlock();
}
bool ath10k_htt_t2h_msg_handler(struct ath10k *ar, struct sk_buff *skb)
{
struct ath10k_htt *htt = &ar->htt;
......@@ -2566,6 +2626,10 @@ bool ath10k_htt_t2h_msg_handler(struct ath10k *ar, struct sk_buff *skb)
skb->len -
offsetof(struct htt_resp,
pktlog_msg.payload));
if (ath10k_peer_stats_enabled(ar))
ath10k_fetch_10_2_tx_stats(ar,
resp->pktlog_msg.payload);
break;
}
case HTT_T2H_MSG_TYPE_RX_FLUSH: {
......@@ -2631,6 +2695,24 @@ void ath10k_htt_rx_pktlog_completion_handler(struct ath10k *ar,
}
EXPORT_SYMBOL(ath10k_htt_rx_pktlog_completion_handler);
static int ath10k_htt_rx_deliver_msdu(struct ath10k *ar, int quota, int budget)
{
struct sk_buff *skb;
while (quota < budget) {
if (skb_queue_empty(&ar->htt.rx_msdus_q))
break;
skb = __skb_dequeue(&ar->htt.rx_msdus_q);
if (!skb)
break;
ath10k_process_rx(ar, skb);
quota++;
}
return quota;
}
int ath10k_htt_txrx_compl_task(struct ath10k *ar, int budget)
{
struct ath10k_htt *htt = &ar->htt;
......@@ -2638,63 +2720,44 @@ int ath10k_htt_txrx_compl_task(struct ath10k *ar, int budget)
struct sk_buff_head tx_ind_q;
struct sk_buff *skb;
unsigned long flags;
int quota = 0, done, num_rx_msdus;
int quota = 0, done, ret;
bool resched_napi = false;
__skb_queue_head_init(&tx_ind_q);
/* Since in-ord-ind can deliver more than 1 A-MSDU in single event,
* process it first to utilize full available quota.
/* Process pending frames before dequeuing more data
* from hardware.
*/
while (quota < budget) {
if (skb_queue_empty(&htt->rx_in_ord_compl_q))
break;
skb = __skb_dequeue(&htt->rx_in_ord_compl_q);
if (!skb) {
resched_napi = true;
goto exit;
}
quota = ath10k_htt_rx_deliver_msdu(ar, quota, budget);
if (quota == budget) {
resched_napi = true;
goto exit;
}
while ((skb = __skb_dequeue(&htt->rx_in_ord_compl_q))) {
spin_lock_bh(&htt->rx_ring.lock);
num_rx_msdus = ath10k_htt_rx_in_ord_ind(ar, skb);
ret = ath10k_htt_rx_in_ord_ind(ar, skb);
spin_unlock_bh(&htt->rx_ring.lock);
if (num_rx_msdus < 0) {
resched_napi = true;
goto exit;
}
dev_kfree_skb_any(skb);
if (num_rx_msdus > 0)
quota += num_rx_msdus;
if ((quota > ATH10K_NAPI_QUOTA_LIMIT) &&
!skb_queue_empty(&htt->rx_in_ord_compl_q)) {
if (ret == -EIO) {
resched_napi = true;
goto exit;
}
}
while (quota < budget) {
/* no more data to receive */
if (!atomic_read(&htt->num_mpdus_ready))
break;
num_rx_msdus = ath10k_htt_rx_handle_amsdu(htt);
if (num_rx_msdus < 0) {
while (atomic_read(&htt->num_mpdus_ready)) {
ret = ath10k_htt_rx_handle_amsdu(htt);
if (ret == -EIO) {
resched_napi = true;
goto exit;
}
quota += num_rx_msdus;
atomic_dec(&htt->num_mpdus_ready);
if ((quota > ATH10K_NAPI_QUOTA_LIMIT) &&
atomic_read(&htt->num_mpdus_ready)) {
resched_napi = true;
goto exit;
}
}
/* Deliver received data after processing data from hardware */
quota = ath10k_htt_rx_deliver_msdu(ar, quota, budget);
/* From NAPI documentation:
* The napi poll() function may also process TX completions, in which
* case if it processes the entire TX ring then it should count that
......
......@@ -931,3 +931,5 @@ const struct ath10k_hw_ops qca6174_ops = {
.set_coverage_class = ath10k_hw_qca988x_set_coverage_class,
.enable_pll_clk = ath10k_hw_qca6174_enable_pll_clock,
};
const struct ath10k_hw_ops wcn3990_ops = {};
......@@ -128,6 +128,10 @@ enum qca9377_chip_id_rev {
#define QCA4019_HW_1_0_BOARD_DATA_FILE "board.bin"
#define QCA4019_HW_1_0_PATCH_LOAD_ADDR 0x1234
/* WCN3990 1.0 definitions */
#define WCN3990_HW_1_0_DEV_VERSION ATH10K_HW_WCN3990
#define WCN3990_HW_1_0_FW_DIR ATH10K_FW_DIR "/WCN3990/hw3.0"
#define ATH10K_FW_FILE_BASE "firmware"
#define ATH10K_FW_API_MAX 6
#define ATH10K_FW_API_MIN 2
......@@ -553,6 +557,10 @@ struct ath10k_hw_params {
/* Number of ciphers supported (i.e First N) in cipher_suites array */
int n_cipher_suites;
u32 num_peers;
u32 ast_skid_limit;
u32 num_wds_entries;
};
struct htt_rx_desc;
......@@ -567,6 +575,7 @@ struct ath10k_hw_ops {
extern const struct ath10k_hw_ops qca988x_ops;
extern const struct ath10k_hw_ops qca99x0_ops;
extern const struct ath10k_hw_ops qca6174_ops;
extern const struct ath10k_hw_ops wcn3990_ops;
extern const struct ath10k_hw_clk_params qca6174_clk[];
......@@ -663,6 +672,11 @@ ath10k_rx_desc_get_l3_pad_bytes(struct ath10k_hw_params *hw,
#define TARGET_TLV_NUM_MSDU_DESC (1024 + 32)
#define TARGET_TLV_NUM_WOW_PATTERNS 22
/* Target specific defines for WMI-HL-1.0 firmware */
#define TARGET_HL_10_TLV_NUM_PEERS 14
#define TARGET_HL_10_TLV_AST_SKID_LIMIT 6
#define TARGET_HL_10_TLV_NUM_WDS_ENTRIES 2
/* Diagnostic Window */
#define CE_DIAG_PIPE 7
......
......@@ -2563,7 +2563,7 @@ static void ath10k_peer_assoc_h_qos(struct ath10k *ar,
}
break;
case WMI_VDEV_TYPE_STA:
if (vif->bss_conf.qos)
if (sta->wme)
arg->peer_flags |= arvif->ar->wmi.peer_flags->qos;
break;
case WMI_VDEV_TYPE_IBSS:
......@@ -3574,7 +3574,9 @@ ath10k_mac_tx_h_get_txpath(struct ath10k *ar,
return ATH10K_MAC_TX_HTT;
case ATH10K_HW_TXRX_MGMT:
if (test_bit(ATH10K_FW_FEATURE_HAS_WMI_MGMT_TX,
ar->running_fw->fw_file.fw_features))
ar->running_fw->fw_file.fw_features) ||
test_bit(WMI_SERVICE_MGMT_TX_WMI,
ar->wmi.svc_map))
return ATH10K_MAC_TX_WMI_MGMT;
else if (ar->htt.target_version_major >= 3)
return ATH10K_MAC_TX_HTT;
......@@ -6201,6 +6203,16 @@ static int ath10k_sta_state(struct ieee80211_hw *hw,
"mac vdev %d peer delete %pM sta %pK (sta gone)\n",
arvif->vdev_id, sta->addr, sta);
if (sta->tdls) {
ret = ath10k_mac_tdls_peer_update(ar, arvif->vdev_id,
sta,
WMI_TDLS_PEER_STATE_TEARDOWN);
if (ret)
ath10k_warn(ar, "failed to update tdls peer state for %pM state %d: %i\n",
sta->addr,
WMI_TDLS_PEER_STATE_TEARDOWN, ret);
}
ret = ath10k_peer_delete(ar, arvif->vdev_id, sta->addr);
if (ret)
ath10k_warn(ar, "failed to delete peer %pM for vdev %d: %i\n",
......@@ -7536,6 +7548,16 @@ ath10k_mac_op_assign_vif_chanctx(struct ieee80211_hw *hw,
arvif->vdev_id, ret);
}
if (ath10k_peer_stats_enabled(ar)) {
ar->pktlog_filter |= ATH10K_PKTLOG_PEER_STATS;
ret = ath10k_wmi_pdev_pktlog_enable(ar,
ar->pktlog_filter);
if (ret) {
ath10k_warn(ar, "failed to enable pktlog %d\n", ret);
goto err_stop;
}
}
mutex_unlock(&ar->conf_mutex);
return 0;
......@@ -7620,6 +7642,34 @@ static void ath10k_mac_op_sta_pre_rcu_remove(struct ieee80211_hw *hw,
peer->removed = true;
}
static void ath10k_sta_statistics(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
struct ieee80211_sta *sta,
struct station_info *sinfo)
{
struct ath10k_sta *arsta = (struct ath10k_sta *)sta->drv_priv;
struct ath10k *ar = arsta->arvif->ar;
if (!ath10k_peer_stats_enabled(ar))
return;
sinfo->rx_duration = arsta->rx_duration;
sinfo->filled |= 1ULL << NL80211_STA_INFO_RX_DURATION;
if (!arsta->txrate.legacy && !arsta->txrate.nss)
return;
if (arsta->txrate.legacy) {
sinfo->txrate.legacy = arsta->txrate.legacy;
} else {
sinfo->txrate.mcs = arsta->txrate.mcs;
sinfo->txrate.nss = arsta->txrate.nss;
sinfo->txrate.bw = arsta->txrate.bw;
}
sinfo->txrate.flags = arsta->txrate.flags;
sinfo->filled |= 1ULL << NL80211_STA_INFO_TX_BITRATE;
}
static const struct ieee80211_ops ath10k_ops = {
.tx = ath10k_mac_op_tx,
.wake_tx_queue = ath10k_mac_op_wake_tx_queue,
......@@ -7661,6 +7711,7 @@ static const struct ieee80211_ops ath10k_ops = {
.unassign_vif_chanctx = ath10k_mac_op_unassign_vif_chanctx,
.switch_vif_chanctx = ath10k_mac_op_switch_vif_chanctx,
.sta_pre_rcu_remove = ath10k_mac_op_sta_pre_rcu_remove,
.sta_statistics = ath10k_sta_statistics,
CFG80211_TESTMODE_CMD(ath10k_tm_cmd)
......@@ -7671,7 +7722,6 @@ static const struct ieee80211_ops ath10k_ops = {
#endif
#ifdef CONFIG_MAC80211_DEBUGFS
.sta_add_debugfs = ath10k_sta_add_debugfs,
.sta_statistics = ath10k_sta_statistics,
#endif
};
......@@ -8329,15 +8379,6 @@ int ath10k_mac_register(struct ath10k *ar)
ath10k_warn(ar, "failed to initialise DFS pattern detector\n");
}
/* Current wake_tx_queue implementation imposes a significant
* performance penalty in some setups. The tx scheduling code needs
* more work anyway so disable the wake_tx_queue unless firmware
* supports the pull-push mechanism.
*/
if (!test_bit(ATH10K_FW_FEATURE_PEER_FLOW_CONTROL,
ar->running_fw->fw_file.fw_features))
ar->ops->wake_tx_queue = NULL;
ret = ath10k_mac_init_rd(ar);
if (ret) {
ath10k_err(ar, "failed to derive regdom: %d\n", ret);
......
......@@ -44,7 +44,7 @@ enum ath10k_spectral_mode {
SPECTRAL_MANUAL,
};
#ifdef CONFIG_ATH10K_DEBUGFS
#ifdef CONFIG_ATH10K_SPECTRAL
int ath10k_spectral_process_fft(struct ath10k *ar,
struct wmi_phyerr_ev_arg *phyerr,
......@@ -85,6 +85,6 @@ static inline void ath10k_spectral_destroy(struct ath10k *ar)
{
}
#endif /* CONFIG_ATH10K_DEBUGFS */
#endif /* CONFIG_ATH10K_SPECTRAL */
#endif /* SPECTRAL_H */
......@@ -377,6 +377,7 @@ ath10k_wmi_mgmt_tx(struct ath10k *ar, struct sk_buff *msdu)
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(msdu);
struct sk_buff *skb;
int ret;
u32 mgmt_tx_cmdid;
if (!ar->wmi.ops->gen_mgmt_tx)
return -EOPNOTSUPP;
......@@ -385,7 +386,13 @@ ath10k_wmi_mgmt_tx(struct ath10k *ar, struct sk_buff *msdu)
if (IS_ERR(skb))
return PTR_ERR(skb);
ret = ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->mgmt_tx_cmdid);
if (test_bit(ATH10K_FW_FEATURE_MGMT_TX_BY_REF,
ar->running_fw->fw_file.fw_features))
mgmt_tx_cmdid = ar->wmi.cmd->mgmt_tx_send_cmdid;
else
mgmt_tx_cmdid = ar->wmi.cmd->mgmt_tx_cmdid;
ret = ath10k_wmi_cmd_send(ar, skb, mgmt_tx_cmdid);
if (ret)
return ret;
......
......@@ -917,33 +917,69 @@ ath10k_wmi_tlv_parse_mem_reqs(struct ath10k *ar, u16 tag, u16 len,
return -ENOMEM;
}
struct wmi_tlv_svc_rdy_parse {
const struct hal_reg_capabilities *reg;
const struct wmi_tlv_svc_rdy_ev *ev;
const __le32 *svc_bmap;
const struct wlan_host_mem_req *mem_reqs;
bool svc_bmap_done;
bool dbs_hw_mode_done;
};
static int ath10k_wmi_tlv_svc_rdy_parse(struct ath10k *ar, u16 tag, u16 len,
const void *ptr, void *data)
{
struct wmi_tlv_svc_rdy_parse *svc_rdy = data;
switch (tag) {
case WMI_TLV_TAG_STRUCT_SERVICE_READY_EVENT:
svc_rdy->ev = ptr;
break;
case WMI_TLV_TAG_STRUCT_HAL_REG_CAPABILITIES:
svc_rdy->reg = ptr;
break;
case WMI_TLV_TAG_ARRAY_STRUCT:
svc_rdy->mem_reqs = ptr;
break;
case WMI_TLV_TAG_ARRAY_UINT32:
if (!svc_rdy->svc_bmap_done) {
svc_rdy->svc_bmap_done = true;
svc_rdy->svc_bmap = ptr;
} else if (!svc_rdy->dbs_hw_mode_done) {
svc_rdy->dbs_hw_mode_done = true;
}
break;
default:
break;
}
return 0;
}
static int ath10k_wmi_tlv_op_pull_svc_rdy_ev(struct ath10k *ar,
struct sk_buff *skb,
struct wmi_svc_rdy_ev_arg *arg)
{
const void **tb;
const struct hal_reg_capabilities *reg;
const struct wmi_tlv_svc_rdy_ev *ev;
const __le32 *svc_bmap;
const struct wlan_host_mem_req *mem_reqs;
struct wmi_tlv_svc_rdy_parse svc_rdy = { };
int ret;
tb = ath10k_wmi_tlv_parse_alloc(ar, skb->data, skb->len, GFP_ATOMIC);
if (IS_ERR(tb)) {
ret = PTR_ERR(tb);
ret = ath10k_wmi_tlv_iter(ar, skb->data, skb->len,
ath10k_wmi_tlv_svc_rdy_parse, &svc_rdy);
if (ret) {
ath10k_warn(ar, "failed to parse tlv: %d\n", ret);
return ret;
}
ev = tb[WMI_TLV_TAG_STRUCT_SERVICE_READY_EVENT];
reg = tb[WMI_TLV_TAG_STRUCT_HAL_REG_CAPABILITIES];
svc_bmap = tb[WMI_TLV_TAG_ARRAY_UINT32];
mem_reqs = tb[WMI_TLV_TAG_ARRAY_STRUCT];
ev = svc_rdy.ev;
reg = svc_rdy.reg;
svc_bmap = svc_rdy.svc_bmap;
mem_reqs = svc_rdy.mem_reqs;
if (!ev || !reg || !svc_bmap || !mem_reqs) {
kfree(tb);
if (!ev || !reg || !svc_bmap || !mem_reqs)
return -EPROTO;
}
/* This is an internal ABI compatibility check for WMI TLV so check it
* here instead of the generic WMI code.
......@@ -961,7 +997,6 @@ static int ath10k_wmi_tlv_op_pull_svc_rdy_ev(struct ath10k *ar,
__le32_to_cpu(ev->abi.abi_ver_ns1) != WMI_TLV_ABI_VER_NS1 ||
__le32_to_cpu(ev->abi.abi_ver_ns2) != WMI_TLV_ABI_VER_NS2 ||
__le32_to_cpu(ev->abi.abi_ver_ns3) != WMI_TLV_ABI_VER_NS3) {
kfree(tb);
return -ENOTSUPP;
}
......@@ -982,12 +1017,10 @@ static int ath10k_wmi_tlv_op_pull_svc_rdy_ev(struct ath10k *ar,
ret = ath10k_wmi_tlv_iter(ar, mem_reqs, ath10k_wmi_tlv_len(mem_reqs),
ath10k_wmi_tlv_parse_mem_reqs, arg);
if (ret) {
kfree(tb);
ath10k_warn(ar, "failed to parse mem_reqs tlv: %d\n", ret);
return ret;
}
kfree(tb);
return 0;
}
......@@ -1406,7 +1439,10 @@ static struct sk_buff *ath10k_wmi_tlv_op_gen_init(struct ath10k *ar)
cmd->num_host_mem_chunks = __cpu_to_le32(ar->wmi.num_mem_chunks);
cfg->num_vdevs = __cpu_to_le32(TARGET_TLV_NUM_VDEVS);
cfg->num_peers = __cpu_to_le32(TARGET_TLV_NUM_PEERS);
cfg->num_peers = __cpu_to_le32(ar->hw_params.num_peers);
cfg->ast_skid_limit = __cpu_to_le32(ar->hw_params.ast_skid_limit);
cfg->num_wds_entries = __cpu_to_le32(ar->hw_params.num_wds_entries);
if (test_bit(WMI_SERVICE_RX_FULL_REORDER, ar->wmi.svc_map)) {
cfg->num_offload_peers = __cpu_to_le32(TARGET_TLV_NUM_VDEVS);
......@@ -1418,7 +1454,6 @@ static struct sk_buff *ath10k_wmi_tlv_op_gen_init(struct ath10k *ar)
cfg->num_peer_keys = __cpu_to_le32(2);
cfg->num_tids = __cpu_to_le32(TARGET_TLV_NUM_TIDS);
cfg->ast_skid_limit = __cpu_to_le32(0x10);
cfg->tx_chain_mask = __cpu_to_le32(0x7);
cfg->rx_chain_mask = __cpu_to_le32(0x7);
cfg->rx_timeout_pri[0] = __cpu_to_le32(0x64);
......@@ -1434,7 +1469,6 @@ static struct sk_buff *ath10k_wmi_tlv_op_gen_init(struct ath10k *ar)
cfg->num_mcast_table_elems = __cpu_to_le32(0);
cfg->mcast2ucast_mode = __cpu_to_le32(0);
cfg->tx_dbg_log_size = __cpu_to_le32(0x400);
cfg->num_wds_entries = __cpu_to_le32(0x20);
cfg->dma_burst_size = __cpu_to_le32(0);
cfg->mac_aggr_delim = __cpu_to_le32(0);
cfg->rx_skip_defrag_timeout_dup_detection_check = __cpu_to_le32(0);
......@@ -2449,6 +2483,82 @@ ath10k_wmi_tlv_op_gen_request_stats(struct ath10k *ar, u32 stats_mask)
return skb;
}
static struct sk_buff *
ath10k_wmi_tlv_op_gen_mgmt_tx(struct ath10k *ar, struct sk_buff *msdu)
{
struct ath10k_skb_cb *cb = ATH10K_SKB_CB(msdu);
struct wmi_tlv_mgmt_tx_cmd *cmd;
struct wmi_tlv *tlv;
struct ieee80211_hdr *hdr;
struct sk_buff *skb;
void *ptr;
int len;
u32 buf_len = msdu->len;
u16 fc;
struct ath10k_vif *arvif;
dma_addr_t mgmt_frame_dma;
u32 vdev_id;
if (!cb->vif)
return ERR_PTR(-EINVAL);
hdr = (struct ieee80211_hdr *)msdu->data;
fc = le16_to_cpu(hdr->frame_control);
arvif = (void *)cb->vif->drv_priv;
vdev_id = arvif->vdev_id;
if (WARN_ON_ONCE(!ieee80211_is_mgmt(hdr->frame_control)))
return ERR_PTR(-EINVAL);
len = sizeof(*cmd) + 2 * sizeof(*tlv);
if ((ieee80211_is_action(hdr->frame_control) ||
ieee80211_is_deauth(hdr->frame_control) ||
ieee80211_is_disassoc(hdr->frame_control)) &&
ieee80211_has_protected(hdr->frame_control)) {
len += IEEE80211_CCMP_MIC_LEN;
buf_len += IEEE80211_CCMP_MIC_LEN;
}
buf_len = min_t(u32, buf_len, WMI_TLV_MGMT_TX_FRAME_MAX_LEN);
buf_len = round_up(buf_len, 4);
len += buf_len;
len = round_up(len, 4);
skb = ath10k_wmi_alloc_skb(ar, len);
if (!skb)
return ERR_PTR(-ENOMEM);
ptr = (void *)skb->data;
tlv = ptr;
tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_MGMT_TX_CMD);
tlv->len = __cpu_to_le16(sizeof(*cmd));
cmd = (void *)tlv->value;
cmd->vdev_id = __cpu_to_le32(vdev_id);
cmd->desc_id = 0;
cmd->chanfreq = 0;
cmd->buf_len = __cpu_to_le32(buf_len);
cmd->frame_len = __cpu_to_le32(msdu->len);
mgmt_frame_dma = dma_map_single(arvif->ar->dev, msdu->data,
msdu->len, DMA_TO_DEVICE);
if (!mgmt_frame_dma)
return ERR_PTR(-ENOMEM);
cmd->paddr = __cpu_to_le64(mgmt_frame_dma);
ptr += sizeof(*tlv);
ptr += sizeof(*cmd);
tlv = ptr;
tlv->tag = __cpu_to_le16(WMI_TLV_TAG_ARRAY_BYTE);
tlv->len = __cpu_to_le16(buf_len);
ptr += sizeof(*tlv);
memcpy(ptr, msdu->data, buf_len);
return skb;
}
static struct sk_buff *
ath10k_wmi_tlv_op_gen_force_fw_hang(struct ath10k *ar,
enum wmi_force_fw_hang_type type,
......@@ -3258,6 +3368,7 @@ static struct wmi_cmd_map wmi_tlv_cmd_map = {
.bcn_filter_rx_cmdid = WMI_TLV_BCN_FILTER_RX_CMDID,
.prb_req_filter_rx_cmdid = WMI_TLV_PRB_REQ_FILTER_RX_CMDID,
.mgmt_tx_cmdid = WMI_TLV_MGMT_TX_CMDID,
.mgmt_tx_send_cmdid = WMI_TLV_MGMT_TX_SEND_CMD,
.prb_tmpl_cmdid = WMI_TLV_PRB_TMPL_CMDID,
.addba_clear_resp_cmdid = WMI_TLV_ADDBA_CLEAR_RESP_CMDID,
.addba_send_cmdid = WMI_TLV_ADDBA_SEND_CMDID,
......@@ -3592,6 +3703,7 @@ static const struct wmi_ops wmi_tlv_ops = {
.gen_request_stats = ath10k_wmi_tlv_op_gen_request_stats,
.gen_force_fw_hang = ath10k_wmi_tlv_op_gen_force_fw_hang,
/* .gen_mgmt_tx = not implemented; HTT is used */
.gen_mgmt_tx = ath10k_wmi_tlv_op_gen_mgmt_tx,
.gen_dbglog_cfg = ath10k_wmi_tlv_op_gen_dbglog_cfg,
.gen_pktlog_enable = ath10k_wmi_tlv_op_gen_pktlog_enable,
.gen_pktlog_disable = ath10k_wmi_tlv_op_gen_pktlog_disable,
......
......@@ -22,6 +22,7 @@
#define WMI_TLV_CMD_UNSUPPORTED 0
#define WMI_TLV_PDEV_PARAM_UNSUPPORTED 0
#define WMI_TLV_VDEV_PARAM_UNSUPPORTED 0
#define WMI_TLV_MGMT_TX_FRAME_MAX_LEN 64
enum wmi_tlv_grp_id {
WMI_TLV_GRP_START = 0x3,
......@@ -132,6 +133,7 @@ enum wmi_tlv_cmd_id {
WMI_TLV_PRB_REQ_FILTER_RX_CMDID,
WMI_TLV_MGMT_TX_CMDID,
WMI_TLV_PRB_TMPL_CMDID,
WMI_TLV_MGMT_TX_SEND_CMD,
WMI_TLV_ADDBA_CLEAR_RESP_CMDID = WMI_TLV_CMD(WMI_TLV_GRP_BA_NEG),
WMI_TLV_ADDBA_SEND_CMDID,
WMI_TLV_ADDBA_STATUS_CMDID,
......@@ -890,6 +892,63 @@ enum wmi_tlv_tag {
WMI_TLV_TAG_STRUCT_SAP_OFL_DEL_STA_EVENT,
WMI_TLV_TAG_STRUCT_APFIND_CMD_PARAM,
WMI_TLV_TAG_STRUCT_APFIND_EVENT_HDR,
WMI_TLV_TAG_STRUCT_OCB_SET_SCHED_CMD,
WMI_TLV_TAG_STRUCT_OCB_SET_SCHED_EVENT,
WMI_TLV_TAG_STRUCT_OCB_SET_CONFIG_CMD,
WMI_TLV_TAG_STRUCT_OCB_SET_CONFIG_RESP_EVENT,
WMI_TLV_TAG_STRUCT_OCB_SET_UTC_TIME_CMD,
WMI_TLV_TAG_STRUCT_OCB_START_TIMING_ADVERT_CMD,
WMI_TLV_TAG_STRUCT_OCB_STOP_TIMING_ADVERT_CMD,
WMI_TLV_TAG_STRUCT_OCB_GET_TSF_TIMER_CMD,
WMI_TLV_TAG_STRUCT_OCB_GET_TSF_TIMER_RESP_EVENT,
WMI_TLV_TAG_STRUCT_DCC_GET_STATS_CMD,
WMI_TLV_TAG_STRUCT_DCC_CHANNEL_STATS_REQUEST,
WMI_TLV_TAG_STRUCT_DCC_GET_STATS_RESP_EVENT,
WMI_TLV_TAG_STRUCT_DCC_CLEAR_STATS_CMD,
WMI_TLV_TAG_STRUCT_DCC_UPDATE_NDL_CMD,
WMI_TLV_TAG_STRUCT_DCC_UPDATE_NDL_RESP_EVENT,
WMI_TLV_TAG_STRUCT_DCC_STATS_EVENT,
WMI_TLV_TAG_STRUCT_OCB_CHANNEL,
WMI_TLV_TAG_STRUCT_OCB_SCHEDULE_ELEMENT,
WMI_TLV_TAG_STRUCT_DCC_NDL_STATS_PER_CHANNEL,
WMI_TLV_TAG_STRUCT_DCC_NDL_CHAN,
WMI_TLV_TAG_STRUCT_QOS_PARAMETER,
WMI_TLV_TAG_STRUCT_DCC_NDL_ACTIVE_STATE_CONFIG,
WMI_TLV_TAG_STRUCT_ROAM_SCAN_EXTENDED_THRESHOLD_PARAM,
WMI_TLV_TAG_STRUCT_ROAM_FILTER_FIXED_PARAM,
WMI_TLV_TAG_STRUCT_PASSPOINT_CONFIG_CMD,
WMI_TLV_TAG_STRUCT_PASSPOINT_EVENT_HDR,
WMI_TLV_TAG_STRUCT_EXTSCAN_CONFIGURE_HOTLIST_SSID_MONITOR_CMD,
WMI_TLV_TAG_STRUCT_EXTSCAN_HOTLIST_SSID_MATCH_EVENT,
WMI_TLV_TAG_STRUCT_VDEV_TSF_TSTAMP_ACTION_CMD,
WMI_TLV_TAG_STRUCT_VDEV_TSF_REPORT_EVENT,
WMI_TLV_TAG_STRUCT_GET_FW_MEM_DUMP,
WMI_TLV_TAG_STRUCT_UPDATE_FW_MEM_DUMP,
WMI_TLV_TAG_STRUCT_FW_MEM_DUMP_PARAMS,
WMI_TLV_TAG_STRUCT_DEBUG_MESG_FLUSH,
WMI_TLV_TAG_STRUCT_DEBUG_MESG_FLUSH_COMPLETE,
WMI_TLV_TAG_STRUCT_PEER_SET_RATE_REPORT_CONDITION,
WMI_TLV_TAG_STRUCT_ROAM_SUBNET_CHANGE_CONFIG,
WMI_TLV_TAG_STRUCT_VDEV_SET_IE_CMD,
WMI_TLV_TAG_STRUCT_RSSI_BREACH_MONITOR_CONFIG,
WMI_TLV_TAG_STRUCT_RSSI_BREACH_EVENT,
WMI_TLV_TAG_STRUCT_EVENT_INITIAL_WAKEUP,
WMI_TLV_TAG_STRUCT_SOC_SET_PCL_CMD,
WMI_TLV_TAG_STRUCT_SOC_SET_HW_MODE_CMD,
WMI_TLV_TAG_STRUCT_SOC_SET_HW_MODE_RESPONSE_EVENT,
WMI_TLV_TAG_STRUCT_SOC_HW_MODE_TRANSITION_EVENT,
WMI_TLV_TAG_STRUCT_VDEV_TXRX_STREAMS,
WMI_TLV_TAG_STRUCT_SOC_SET_HW_MODE_RESPONSE_VDEV_MAC_ENTRY,
WMI_TLV_TAG_STRUCT_SOC_SET_DUAL_MAC_CONFIG_CMD,
WMI_TLV_TAG_STRUCT_SOC_SET_DUAL_MAC_CONFIG_RESPONSE_EVENT,
WMI_TLV_TAG_STRUCT_IOAC_SOCK_PATTERN_T,
WMI_TLV_TAG_STRUCT_WOW_ENABLE_ICMPV6_NA_FLT_CMD,
WMI_TLV_TAG_STRUCT_DIAG_EVENT_LOG_CONFIG,
WMI_TLV_TAG_STRUCT_DIAG_EVENT_LOG_SUPPORTED_EVENT,
WMI_TLV_TAG_STRUCT_PACKET_FILTER_CONFIG,
WMI_TLV_TAG_STRUCT_PACKET_FILTER_ENABLE,
WMI_TLV_TAG_STRUCT_SAP_SET_BLACKLIST_PARAM_CMD,
WMI_TLV_TAG_STRUCT_MGMT_TX_CMD,
WMI_TLV_TAG_MAX
};
......@@ -965,6 +1024,50 @@ enum wmi_tlv_service {
WMI_TLV_SERVICE_STA_RX_IPA_OFFLOAD_SUPPORT,
WMI_TLV_SERVICE_MDNS_OFFLOAD,
WMI_TLV_SERVICE_SAP_AUTH_OFFLOAD,
WMI_TLV_SERVICE_DUAL_BAND_SIMULTANEOUS_SUPPORT,
WMI_TLV_SERVICE_OCB,
WMI_TLV_SERVICE_AP_ARPNS_OFFLOAD,
WMI_TLV_SERVICE_PER_BAND_CHAINMASK_SUPPORT,
WMI_TLV_SERVICE_PACKET_FILTER_OFFLOAD,
WMI_TLV_SERVICE_MGMT_TX_HTT,
WMI_TLV_SERVICE_MGMT_TX_WMI,
WMI_TLV_SERVICE_EXT_MSG,
WMI_TLV_SERVICE_MAWC,
WMI_TLV_SERVICE_PEER_ASSOC_CONF,
WMI_TLV_SERVICE_EGAP,
WMI_TLV_SERVICE_STA_PMF_OFFLOAD,
WMI_TLV_SERVICE_UNIFIED_WOW_CAPABILITY,
WMI_TLV_SERVICE_ENHANCED_PROXY_STA,
WMI_TLV_SERVICE_ATF,
WMI_TLV_SERVICE_COEX_GPIO,
WMI_TLV_SERVICE_AUX_SPECTRAL_INTF,
WMI_TLV_SERVICE_AUX_CHAN_LOAD_INTF,
WMI_TLV_SERVICE_BSS_CHANNEL_INFO_64,
WMI_TLV_SERVICE_ENTERPRISE_MESH,
WMI_TLV_SERVICE_RESTRT_CHNL_SUPPORT,
WMI_TLV_SERVICE_BPF_OFFLOAD,
WMI_TLV_SERVICE_SYNC_DELETE_CMDS,
WMI_TLV_SERVICE_SMART_ANTENNA_SW_SUPPORT,
WMI_TLV_SERVICE_SMART_ANTENNA_HW_SUPPORT,
WMI_TLV_SERVICE_RATECTRL_LIMIT_MAX_MIN_RATES,
WMI_TLV_SERVICE_NAN_DATA,
WMI_TLV_SERVICE_NAN_RTT,
WMI_TLV_SERVICE_11AX,
WMI_TLV_SERVICE_DEPRECATED_REPLACE,
WMI_TLV_SERVICE_TDLS_CONN_TRACKER_IN_HOST_MODE,
WMI_TLV_SERVICE_ENHANCED_MCAST_FILTER,
WMI_TLV_SERVICE_PERIODIC_CHAN_STAT_SUPPORT,
WMI_TLV_SERVICE_MESH_11S,
WMI_TLV_SERVICE_HALF_RATE_QUARTER_RATE_SUPPORT,
WMI_TLV_SERVICE_VDEV_RX_FILTER,
WMI_TLV_SERVICE_P2P_LISTEN_OFFLOAD_SUPPORT,
WMI_TLV_SERVICE_MARK_FIRST_WAKEUP_PACKET,
WMI_TLV_SERVICE_MULTIPLE_MCAST_FILTER_SET,
WMI_TLV_SERVICE_HOST_MANAGED_RX_REORDER,
WMI_TLV_SERVICE_FLASH_RDWR_SUPPORT,
WMI_TLV_SERVICE_WLAN_STATS_REPORT,
WMI_TLV_SERVICE_TX_MSDU_ID_NEW_PARTITION_SUPPORT,
WMI_TLV_SERVICE_DFS_PHYERR_OFFLOAD,
};
#define WMI_SERVICE_IS_ENABLED(wmi_svc_bmap, svc_id, len) \
......@@ -1121,6 +1224,8 @@ wmi_tlv_svc_map(const __le32 *in, unsigned long *out, size_t len)
WMI_SERVICE_MDNS_OFFLOAD, len);
SVCMAP(WMI_TLV_SERVICE_SAP_AUTH_OFFLOAD,
WMI_SERVICE_SAP_AUTH_OFFLOAD, len);
SVCMAP(WMI_TLV_SERVICE_MGMT_TX_WMI,
WMI_SERVICE_MGMT_TX_WMI, len);
}
#undef SVCMAP
......@@ -1643,4 +1748,12 @@ struct wmi_tlv_tx_pause_ev {
void ath10k_wmi_tlv_attach(struct ath10k *ar);
struct wmi_tlv_mgmt_tx_cmd {
__le32 vdev_id;
__le32 desc_id;
__le32 chanfreq;
__le64 paddr;
__le32 frame_len;
__le32 buf_len;
} __packed;
#endif
......@@ -29,6 +29,7 @@
#include "p2p.h"
#include "hw.h"
#include "hif.h"
#include "txrx.h"
#define ATH10K_WMI_BARRIER_ECHO_ID 0xBA991E9
#define ATH10K_WMI_BARRIER_TIMEOUT_HZ (3 * HZ)
......@@ -4456,6 +4457,74 @@ void ath10k_wmi_event_pdev_tpc_config(struct ath10k *ar, struct sk_buff *skb)
__le32_to_cpu(ev->rate_max));
}
static void
ath10k_wmi_handle_tdls_peer_event(struct ath10k *ar, struct sk_buff *skb)
{
struct wmi_tdls_peer_event *ev;
struct ath10k_peer *peer;
struct ath10k_vif *arvif;
int vdev_id;
int peer_status;
int peer_reason;
u8 reason;
if (skb->len < sizeof(*ev)) {
ath10k_err(ar, "received tdls peer event with invalid size (%d bytes)\n",
skb->len);
return;
}
ev = (struct wmi_tdls_peer_event *)skb->data;
vdev_id = __le32_to_cpu(ev->vdev_id);
peer_status = __le32_to_cpu(ev->peer_status);
peer_reason = __le32_to_cpu(ev->peer_reason);
spin_lock_bh(&ar->data_lock);
peer = ath10k_peer_find(ar, vdev_id, ev->peer_macaddr.addr);
spin_unlock_bh(&ar->data_lock);
if (!peer) {
ath10k_warn(ar, "failed to find peer entry for %pM\n",
ev->peer_macaddr.addr);
return;
}
switch (peer_status) {
case WMI_TDLS_SHOULD_TEARDOWN:
switch (peer_reason) {
case WMI_TDLS_TEARDOWN_REASON_PTR_TIMEOUT:
case WMI_TDLS_TEARDOWN_REASON_NO_RESPONSE:
case WMI_TDLS_TEARDOWN_REASON_RSSI:
reason = WLAN_REASON_TDLS_TEARDOWN_UNREACHABLE;
break;
default:
reason = WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED;
break;
}
arvif = ath10k_get_arvif(ar, vdev_id);
if (!arvif) {
ath10k_warn(ar, "received tdls peer event for invalid vdev id %u\n",
vdev_id);
return;
}
ieee80211_tdls_oper_request(arvif->vif, ev->peer_macaddr.addr,
NL80211_TDLS_TEARDOWN, reason,
GFP_ATOMIC);
ath10k_dbg(ar, ATH10K_DBG_WMI,
"received tdls teardown event for peer %pM reason %u\n",
ev->peer_macaddr.addr, peer_reason);
break;
default:
ath10k_dbg(ar, ATH10K_DBG_WMI,
"received unknown tdls peer event %u\n",
peer_status);
break;
}
}
void ath10k_wmi_event_pdev_ftm_intg(struct ath10k *ar, struct sk_buff *skb)
{
ath10k_dbg(ar, ATH10K_DBG_WMI, "WMI_PDEV_FTM_INTG_EVENTID\n");
......@@ -5477,6 +5546,9 @@ static void ath10k_wmi_10_4_op_rx(struct ath10k *ar, struct sk_buff *skb)
case WMI_10_4_PDEV_TPC_CONFIG_EVENTID:
ath10k_wmi_event_pdev_tpc_config(ar, skb);
break;
case WMI_10_4_TDLS_PEER_EVENTID:
ath10k_wmi_handle_tdls_peer_event(ar, skb);
break;
default:
ath10k_warn(ar, "Unknown eventid: %d\n", id);
break;
......
......@@ -195,6 +195,7 @@ enum wmi_service {
WMI_SERVICE_SMART_LOGGING_SUPPORT,
WMI_SERVICE_TDLS_CONN_TRACKER_IN_HOST_MODE,
WMI_SERVICE_TDLS_EXPLICIT_MODE_ONLY,
WMI_SERVICE_MGMT_TX_WMI,
/* keep last */
WMI_SERVICE_MAX,
......@@ -797,6 +798,7 @@ struct wmi_cmd_map {
u32 bcn_filter_rx_cmdid;
u32 prb_req_filter_rx_cmdid;
u32 mgmt_tx_cmdid;
u32 mgmt_tx_send_cmdid;
u32 prb_tmpl_cmdid;
u32 addba_clear_resp_cmdid;
u32 addba_send_cmdid;
......@@ -5236,7 +5238,8 @@ enum wmi_10_4_vdev_param {
#define WMI_VDEV_PARAM_TXBF_MU_TX_BFER BIT(3)
#define WMI_TXBF_STS_CAP_OFFSET_LSB 4
#define WMI_TXBF_STS_CAP_OFFSET_MASK 0xf0
#define WMI_TXBF_STS_CAP_OFFSET_MASK 0x70
#define WMI_TXBF_CONF_IMPLICIT_BF BIT(7)
#define WMI_BF_SOUND_DIM_OFFSET_LSB 8
#define WMI_BF_SOUND_DIM_OFFSET_MASK 0xf00
......
......@@ -2766,7 +2766,6 @@ static int ath6kl_start_ap(struct wiphy *wiphy, struct net_device *dev,
struct ieee80211_mgmt *mgmt;
bool hidden = false;
u8 *ies;
int ies_len;
struct wmi_connect_cmd p;
int res;
int i, ret;
......@@ -2804,7 +2803,6 @@ static int ath6kl_start_ap(struct wiphy *wiphy, struct net_device *dev,
ies = mgmt->u.beacon.variable;
if (ies > info->beacon.head + info->beacon.head_len)
return -EINVAL;
ies_len = info->beacon.head + info->beacon.head_len - ies;
if (info->ssid == NULL)
return -EINVAL;
......
......@@ -1001,7 +1001,7 @@ static void aggr_slice_amsdu(struct aggr_info *p_aggr,
while (amsdu_len > mac_hdr_len) {
hdr = (struct ethhdr *) framep;
payload_8023_len = ntohs(hdr->h_proto);
payload_8023_len = be16_to_cpu(hdr->h_proto);
if (payload_8023_len < MIN_MSDU_SUBFRAME_PAYLOAD_LEN ||
payload_8023_len > MAX_MSDU_SUBFRAME_PAYLOAD_LEN) {
......
......@@ -61,13 +61,12 @@ config ATH9K_DEBUGFS
depends on ATH9K && DEBUG_FS
select MAC80211_DEBUGFS
select ATH9K_COMMON_DEBUG
select RELAY
---help---
Say Y, if you need access to ath9k's statistics for
interrupts, rate control, etc.
Also required for changing debug message flags at run time.
As well as access to the FFT/spectral data and TX99.
Also required for changing debug message flags at run time and for
TX99.
config ATH9K_STATION_STATISTICS
bool "Detailed station statistics"
......@@ -177,7 +176,6 @@ config ATH9K_HTC_DEBUGFS
bool "Atheros ath9k_htc debugging"
depends on ATH9K_HTC && DEBUG_FS
select ATH9K_COMMON_DEBUG
select RELAY
---help---
Say Y, if you need access to ath9k_htc's statistics.
As well as access to the FFT/spectral data.
......@@ -192,3 +190,11 @@ config ATH9K_HWRNG
Say Y, feeds the entropy directly from the WiFi driver to the input
pool.
config ATH9K_COMMON_SPECTRAL
bool "Atheros ath9k/ath9k_htc spectral scan support"
depends on ATH9K_DEBUGFS || ATH9K_HTC_DEBUGFS
select RELAY
default n
---help---
Say Y to enable access to the FFT/spectral data via debugfs.
......@@ -62,8 +62,8 @@ ath9k_common-y:= common.o \
common-init.o \
common-beacon.o \
ath9k_common-$(CONFIG_ATH9K_COMMON_DEBUG) += common-debug.o \
common-spectral.o
ath9k_common-$(CONFIG_ATH9K_COMMON_DEBUG) += common-debug.o
ath9k_common-$(CONFIG_ATH9K_COMMON_SPECTRAL) += common-spectral.o
ath9k_htc-y += htc_hst.o \
hif_usb.o \
......
......@@ -151,7 +151,7 @@ static inline u8 spectral_bitmap_weight(u8 *bins)
return bins[0] & 0x3f;
}
#ifdef CONFIG_ATH9K_COMMON_DEBUG
#ifdef CONFIG_ATH9K_COMMON_SPECTRAL
void ath9k_cmn_spectral_init_debug(struct ath_spec_scan_priv *spec_priv, struct dentry *debugfs_phy);
void ath9k_cmn_spectral_deinit_debug(struct ath_spec_scan_priv *spec_priv);
......@@ -183,6 +183,6 @@ static inline int ath_cmn_process_fft(struct ath_spec_scan_priv *spec_priv,
{
return 0;
}
#endif /* CONFIG_ATH9K_COMMON_DEBUG */
#endif /* CONFIG_ATH9K_COMMON_SPECTRAL */
#endif /* SPECTRAL_H */
......@@ -123,11 +123,9 @@ static bool ath9k_check_chirping(struct ath_softc *sc, u8 *data,
fft = (struct ath9k_dfs_fft_40 *) (data + 2);
ath_dbg(common, DFS, "fixing datalen by 2\n");
}
if (IS_CHAN_HT40MINUS(ah->curchan)) {
int temp = is_ctl;
is_ctl = is_ext;
is_ext = temp;
}
if (IS_CHAN_HT40MINUS(ah->curchan))
swap(is_ctl, is_ext);
for (i = 0; i < FFT_NUM_SAMPLES; i++)
max_bin[i] = ath9k_get_max_index_ht40(fft + i, is_ctl,
is_ext);
......
......@@ -1683,6 +1683,10 @@ static int ath9k_htc_ampdu_action(struct ieee80211_hw *hw,
ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid);
break;
case IEEE80211_AMPDU_TX_OPERATIONAL:
if (tid >= ATH9K_HTC_MAX_TID) {
ret = -EINVAL;
break;
}
ista = (struct ath9k_htc_sta *) sta->drv_priv;
spin_lock_bh(&priv->tx.tx_lock);
ista->tid_state[tid] = AGGR_OPERATIONAL;
......
......@@ -348,6 +348,13 @@ enum wcn36xx_hal_host_msg_type {
WCN36XX_HAL_DHCP_START_IND = 189,
WCN36XX_HAL_DHCP_STOP_IND = 190,
/* Scan Offload(hw) APIs */
WCN36XX_HAL_START_SCAN_OFFLOAD_REQ = 204,
WCN36XX_HAL_START_SCAN_OFFLOAD_RSP = 205,
WCN36XX_HAL_STOP_SCAN_OFFLOAD_REQ = 206,
WCN36XX_HAL_STOP_SCAN_OFFLOAD_RSP = 207,
WCN36XX_HAL_SCAN_OFFLOAD_IND = 210,
WCN36XX_HAL_AVOID_FREQ_RANGE_IND = 233,
WCN36XX_HAL_PRINT_REG_INFO_IND = 259,
......@@ -1115,6 +1122,101 @@ struct wcn36xx_hal_finish_scan_rsp_msg {
} __packed;
enum wcn36xx_hal_scan_type {
WCN36XX_HAL_SCAN_TYPE_PASSIVE = 0x00,
WCN36XX_HAL_SCAN_TYPE_ACTIVE = WCN36XX_HAL_MAX_ENUM_SIZE
};
struct wcn36xx_hal_mac_ssid {
u8 length;
u8 ssid[32];
} __packed;
struct wcn36xx_hal_start_scan_offload_req_msg {
struct wcn36xx_hal_msg_header header;
/* BSSIDs hot list */
u8 num_bssid;
u8 bssids[4][ETH_ALEN];
/* Directed probe-requests will be sent for listed SSIDs (max 10)*/
u8 num_ssid;
struct wcn36xx_hal_mac_ssid ssids[10];
/* Report AP with hidden ssid */
u8 scan_hidden;
/* Self MAC address */
u8 mac[ETH_ALEN];
/* BSS type */
enum wcn36xx_hal_bss_type bss_type;
/* Scan type */
enum wcn36xx_hal_scan_type scan_type;
/* Minimum scanning time on each channel (ms) */
u32 min_ch_time;
/* Maximum scanning time on each channel */
u32 max_ch_time;
/* Is a p2p search */
u8 p2p_search;
/* Channels to scan */
u8 num_channel;
u8 channels[80];
/* IE field */
u16 ie_len;
u8 ie[0];
} __packed;
struct wcn36xx_hal_start_scan_offload_rsp_msg {
struct wcn36xx_hal_msg_header header;
/* success or failure */
u32 status;
} __packed;
enum wcn36xx_hal_scan_offload_ind_type {
/* Scan has been started */
WCN36XX_HAL_SCAN_IND_STARTED = 0x01,
/* Scan has been completed */
WCN36XX_HAL_SCAN_IND_COMPLETED = 0x02,
/* Moved to foreign channel */
WCN36XX_HAL_SCAN_IND_FOREIGN_CHANNEL = 0x08,
/* scan request has been dequeued */
WCN36XX_HAL_SCAN_IND_DEQUEUED = 0x10,
/* preempted by other high priority scan */
WCN36XX_HAL_SCAN_IND_PREEMPTED = 0x20,
/* scan start failed */
WCN36XX_HAL_SCAN_IND_FAILED = 0x40,
/*scan restarted */
WCN36XX_HAL_SCAN_IND_RESTARTED = 0x80,
WCN36XX_HAL_SCAN_IND_MAX = WCN36XX_HAL_MAX_ENUM_SIZE
};
struct wcn36xx_hal_scan_offload_ind {
struct wcn36xx_hal_msg_header header;
u32 type;
u32 channel_mhz;
u32 scan_id;
} __packed;
struct wcn36xx_hal_stop_scan_offload_req_msg {
struct wcn36xx_hal_msg_header header;
} __packed;
struct wcn36xx_hal_stop_scan_offload_rsp_msg {
struct wcn36xx_hal_msg_header header;
/* success or failure */
u32 status;
} __packed;
enum wcn36xx_hal_rate_index {
HW_RATE_INDEX_1MBPS = 0x82,
HW_RATE_INDEX_2MBPS = 0x84,
......@@ -1507,11 +1609,6 @@ struct wcn36xx_hal_edca_param_record {
u16 txop_limit;
} __packed;
struct wcn36xx_hal_mac_ssid {
u8 length;
u8 ssid[32];
} __packed;
/* Concurrency role. These are generic IDs that identify the various roles
* in the software system. */
enum wcn36xx_hal_con_mode {
......
......@@ -629,7 +629,6 @@ static int wcn36xx_hw_scan(struct ieee80211_hw *hw,
struct ieee80211_scan_request *hw_req)
{
struct wcn36xx *wcn = hw->priv;
mutex_lock(&wcn->scan_lock);
if (wcn->scan_req) {
mutex_unlock(&wcn->scan_lock);
......@@ -638,11 +637,16 @@ static int wcn36xx_hw_scan(struct ieee80211_hw *hw,
wcn->scan_aborted = false;
wcn->scan_req = &hw_req->req;
mutex_unlock(&wcn->scan_lock);
schedule_work(&wcn->scan_work);
if (!get_feat_caps(wcn->fw_feat_caps, SCAN_OFFLOAD)) {
/* legacy manual/sw scan */
schedule_work(&wcn->scan_work);
return 0;
}
return 0;
return wcn36xx_smd_start_hw_scan(wcn, vif, &hw_req->req);
}
static void wcn36xx_cancel_hw_scan(struct ieee80211_hw *hw,
......@@ -650,6 +654,12 @@ static void wcn36xx_cancel_hw_scan(struct ieee80211_hw *hw,
{
struct wcn36xx *wcn = hw->priv;
if (!wcn36xx_smd_stop_hw_scan(wcn)) {
struct cfg80211_scan_info scan_info = { .aborted = true };
ieee80211_scan_completed(wcn->hw, &scan_info);
}
mutex_lock(&wcn->scan_lock);
wcn->scan_aborted = true;
mutex_unlock(&wcn->scan_lock);
......
......@@ -73,6 +73,8 @@ static struct wcn36xx_cfg_val wcn36xx_cfg_vals[] = {
WCN36XX_CFG_VAL(TX_PWR_CTRL_ENABLE, 1),
WCN36XX_CFG_VAL(ENABLE_CLOSE_LOOP, 1),
WCN36XX_CFG_VAL(ENABLE_LPWR_IMG_TRANSITION, 0),
WCN36XX_CFG_VAL(BTC_STATIC_LEN_LE_BT, 120000),
WCN36XX_CFG_VAL(BTC_STATIC_LEN_LE_WLAN, 30000),
WCN36XX_CFG_VAL(MAX_ASSOC_LIMIT, 10),
WCN36XX_CFG_VAL(ENABLE_MCC_ADAPTIVE_SCHEDULER, 0),
};
......@@ -613,6 +615,85 @@ int wcn36xx_smd_finish_scan(struct wcn36xx *wcn,
return ret;
}
int wcn36xx_smd_start_hw_scan(struct wcn36xx *wcn, struct ieee80211_vif *vif,
struct cfg80211_scan_request *req)
{
struct wcn36xx_hal_start_scan_offload_req_msg msg_body;
int ret, i;
mutex_lock(&wcn->hal_mutex);
INIT_HAL_MSG(msg_body, WCN36XX_HAL_START_SCAN_OFFLOAD_REQ);
msg_body.scan_type = WCN36XX_HAL_SCAN_TYPE_ACTIVE;
msg_body.min_ch_time = 30;
msg_body.min_ch_time = 100;
msg_body.scan_hidden = 1;
memcpy(msg_body.mac, vif->addr, ETH_ALEN);
msg_body.p2p_search = vif->p2p;
msg_body.num_ssid = min_t(u8, req->n_ssids, ARRAY_SIZE(msg_body.ssids));
for (i = 0; i < msg_body.num_ssid; i++) {
msg_body.ssids[i].length = min_t(u8, req->ssids[i].ssid_len,
sizeof(msg_body.ssids[i].ssid));
memcpy(msg_body.ssids[i].ssid, req->ssids[i].ssid,
msg_body.ssids[i].length);
}
msg_body.num_channel = min_t(u8, req->n_channels,
sizeof(msg_body.channels));
for (i = 0; i < msg_body.num_channel; i++)
msg_body.channels[i] = req->channels[i]->hw_value;
PREPARE_HAL_BUF(wcn->hal_buf, msg_body);
wcn36xx_dbg(WCN36XX_DBG_HAL,
"hal start hw-scan (channels: %u; ssids: %u; p2p: %s)\n",
msg_body.num_channel, msg_body.num_ssid,
msg_body.p2p_search ? "yes" : "no");
ret = wcn36xx_smd_send_and_wait(wcn, msg_body.header.len);
if (ret) {
wcn36xx_err("Sending hal_start_scan_offload failed\n");
goto out;
}
ret = wcn36xx_smd_rsp_status_check(wcn->hal_buf, wcn->hal_rsp_len);
if (ret) {
wcn36xx_err("hal_start_scan_offload response failed err=%d\n",
ret);
goto out;
}
out:
mutex_unlock(&wcn->hal_mutex);
return ret;
}
int wcn36xx_smd_stop_hw_scan(struct wcn36xx *wcn)
{
struct wcn36xx_hal_stop_scan_offload_req_msg msg_body;
int ret;
mutex_lock(&wcn->hal_mutex);
INIT_HAL_MSG(msg_body, WCN36XX_HAL_STOP_SCAN_OFFLOAD_REQ);
PREPARE_HAL_BUF(wcn->hal_buf, msg_body);
wcn36xx_dbg(WCN36XX_DBG_HAL, "hal stop hw-scan\n");
ret = wcn36xx_smd_send_and_wait(wcn, msg_body.header.len);
if (ret) {
wcn36xx_err("Sending hal_stop_scan_offload failed\n");
goto out;
}
ret = wcn36xx_smd_rsp_status_check(wcn->hal_buf, wcn->hal_rsp_len);
if (ret) {
wcn36xx_err("hal_stop_scan_offload response failed err=%d\n",
ret);
goto out;
}
out:
mutex_unlock(&wcn->hal_mutex);
return ret;
}
static int wcn36xx_smd_switch_channel_rsp(void *buf, size_t len)
{
struct wcn36xx_hal_switch_channel_rsp_msg *rsp;
......@@ -2039,6 +2120,40 @@ static int wcn36xx_smd_tx_compl_ind(struct wcn36xx *wcn, void *buf, size_t len)
return 0;
}
static int wcn36xx_smd_hw_scan_ind(struct wcn36xx *wcn, void *buf, size_t len)
{
struct wcn36xx_hal_scan_offload_ind *rsp = buf;
struct cfg80211_scan_info scan_info = {};
if (len != sizeof(*rsp)) {
wcn36xx_warn("Corrupted delete scan indication\n");
return -EIO;
}
wcn36xx_dbg(WCN36XX_DBG_HAL, "scan indication (type %x)", rsp->type);
switch (rsp->type) {
case WCN36XX_HAL_SCAN_IND_FAILED:
scan_info.aborted = true;
case WCN36XX_HAL_SCAN_IND_COMPLETED:
mutex_lock(&wcn->scan_lock);
wcn->scan_req = NULL;
mutex_unlock(&wcn->scan_lock);
ieee80211_scan_completed(wcn->hw, &scan_info);
break;
case WCN36XX_HAL_SCAN_IND_STARTED:
case WCN36XX_HAL_SCAN_IND_FOREIGN_CHANNEL:
case WCN36XX_HAL_SCAN_IND_DEQUEUED:
case WCN36XX_HAL_SCAN_IND_PREEMPTED:
case WCN36XX_HAL_SCAN_IND_RESTARTED:
break;
default:
wcn36xx_warn("Unknown scan indication type %x\n", rsp->type);
}
return 0;
}
static int wcn36xx_smd_missed_beacon_ind(struct wcn36xx *wcn,
void *buf,
size_t len)
......@@ -2250,6 +2365,8 @@ int wcn36xx_smd_rsp_process(struct rpmsg_device *rpdev,
case WCN36XX_HAL_CH_SWITCH_RSP:
case WCN36XX_HAL_FEATURE_CAPS_EXCHANGE_RSP:
case WCN36XX_HAL_8023_MULTICAST_LIST_RSP:
case WCN36XX_HAL_START_SCAN_OFFLOAD_RSP:
case WCN36XX_HAL_STOP_SCAN_OFFLOAD_RSP:
memcpy(wcn->hal_buf, buf, len);
wcn->hal_rsp_len = len;
complete(&wcn->hal_rsp_compl);
......@@ -2262,6 +2379,7 @@ int wcn36xx_smd_rsp_process(struct rpmsg_device *rpdev,
case WCN36XX_HAL_MISSED_BEACON_IND:
case WCN36XX_HAL_DELETE_STA_CONTEXT_IND:
case WCN36XX_HAL_PRINT_REG_INFO_IND:
case WCN36XX_HAL_SCAN_OFFLOAD_IND:
msg_ind = kmalloc(sizeof(*msg_ind) + len, GFP_ATOMIC);
if (!msg_ind) {
wcn36xx_err("Run out of memory while handling SMD_EVENT (%d)\n",
......@@ -2298,6 +2416,8 @@ static void wcn36xx_ind_smd_work(struct work_struct *work)
hal_ind_msg = list_first_entry(&wcn->hal_ind_queue,
struct wcn36xx_hal_ind_msg,
list);
list_del(wcn->hal_ind_queue.next);
spin_unlock_irqrestore(&wcn->hal_ind_lock, flags);
msg_header = (struct wcn36xx_hal_msg_header *)hal_ind_msg->msg;
......@@ -2326,12 +2446,14 @@ static void wcn36xx_ind_smd_work(struct work_struct *work)
hal_ind_msg->msg,
hal_ind_msg->msg_len);
break;
case WCN36XX_HAL_SCAN_OFFLOAD_IND:
wcn36xx_smd_hw_scan_ind(wcn, hal_ind_msg->msg,
hal_ind_msg->msg_len);
break;
default:
wcn36xx_err("SMD_EVENT (%d) not supported\n",
msg_header->msg_type);
}
list_del(wcn->hal_ind_queue.next);
spin_unlock_irqrestore(&wcn->hal_ind_lock, flags);
kfree(hal_ind_msg);
}
int wcn36xx_smd_open(struct wcn36xx *wcn)
......
......@@ -65,6 +65,9 @@ int wcn36xx_smd_end_scan(struct wcn36xx *wcn, u8 scan_channel);
int wcn36xx_smd_finish_scan(struct wcn36xx *wcn,
enum wcn36xx_hal_sys_mode mode);
int wcn36xx_smd_update_scan_params(struct wcn36xx *wcn, u8 *channels, size_t channel_count);
int wcn36xx_smd_start_hw_scan(struct wcn36xx *wcn, struct ieee80211_vif *vif,
struct cfg80211_scan_request *req);
int wcn36xx_smd_stop_hw_scan(struct wcn36xx *wcn);
int wcn36xx_smd_add_sta_self(struct wcn36xx *wcn, struct ieee80211_vif *vif);
int wcn36xx_smd_delete_sta_self(struct wcn36xx *wcn, u8 *addr);
int wcn36xx_smd_delete_sta(struct wcn36xx *wcn, u8 sta_index);
......
......@@ -901,7 +901,7 @@ int wil_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
u64 *cookie)
{
const u8 *buf = params->buf;
size_t len = params->len;
size_t len = params->len, total;
struct wil6210_priv *wil = wiphy_to_wil(wiphy);
int rc;
bool tx_status = false;
......@@ -926,7 +926,11 @@ int wil_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
if (len < sizeof(struct ieee80211_hdr_3addr))
return -EINVAL;
cmd = kmalloc(sizeof(*cmd) + len, GFP_KERNEL);
total = sizeof(*cmd) + len;
if (total < len)
return -EINVAL;
cmd = kmalloc(total, GFP_KERNEL);
if (!cmd) {
rc = -ENOMEM;
goto out;
......@@ -936,7 +940,7 @@ int wil_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
cmd->len = cpu_to_le16(len);
memcpy(cmd->payload, buf, len);
rc = wmi_call(wil, WMI_SW_TX_REQ_CMDID, cmd, sizeof(*cmd) + len,
rc = wmi_call(wil, WMI_SW_TX_REQ_CMDID, cmd, total,
WMI_SW_TX_COMPLETE_EVENTID, &evt, sizeof(evt), 2000);
if (rc == 0)
tx_status = !evt.evt.status;
......@@ -1727,9 +1731,12 @@ static int wil_cfg80211_suspend(struct wiphy *wiphy,
wil_dbg_pm(wil, "suspending\n");
wil_p2p_stop_discovery(wil);
mutex_lock(&wil->mutex);
mutex_lock(&wil->p2p_wdev_mutex);
wil_p2p_stop_radio_operations(wil);
wil_abort_scan(wil, true);
mutex_unlock(&wil->p2p_wdev_mutex);
mutex_unlock(&wil->mutex);
out:
return rc;
......
......@@ -242,12 +242,19 @@ static void wil_print_ring(struct seq_file *s, const char *prefix,
static int wil_mbox_debugfs_show(struct seq_file *s, void *data)
{
struct wil6210_priv *wil = s->private;
int ret;
ret = wil_pm_runtime_get(wil);
if (ret < 0)
return ret;
wil_print_ring(s, "tx", wil->csr + HOST_MBOX +
offsetof(struct wil6210_mbox_ctl, tx));
wil_print_ring(s, "rx", wil->csr + HOST_MBOX +
offsetof(struct wil6210_mbox_ctl, rx));
wil_pm_runtime_put(wil);
return 0;
}
......@@ -265,15 +272,37 @@ static const struct file_operations fops_mbox = {
static int wil_debugfs_iomem_x32_set(void *data, u64 val)
{
writel(val, (void __iomem *)data);
struct wil_debugfs_iomem_data *d = (struct
wil_debugfs_iomem_data *)data;
struct wil6210_priv *wil = d->wil;
int ret;
ret = wil_pm_runtime_get(wil);
if (ret < 0)
return ret;
writel(val, (void __iomem *)d->offset);
wmb(); /* make sure write propagated to HW */
wil_pm_runtime_put(wil);
return 0;
}
static int wil_debugfs_iomem_x32_get(void *data, u64 *val)
{
*val = readl((void __iomem *)data);
struct wil_debugfs_iomem_data *d = (struct
wil_debugfs_iomem_data *)data;
struct wil6210_priv *wil = d->wil;
int ret;
ret = wil_pm_runtime_get(wil);
if (ret < 0)
return ret;
*val = readl((void __iomem *)d->offset);
wil_pm_runtime_put(wil);
return 0;
}
......@@ -284,10 +313,21 @@ DEFINE_SIMPLE_ATTRIBUTE(fops_iomem_x32, wil_debugfs_iomem_x32_get,
static struct dentry *wil_debugfs_create_iomem_x32(const char *name,
umode_t mode,
struct dentry *parent,
void *value)
void *value,
struct wil6210_priv *wil)
{
return debugfs_create_file(name, mode, parent, value,
&fops_iomem_x32);
struct dentry *file;
struct wil_debugfs_iomem_data *data = &wil->dbg_data.data_arr[
wil->dbg_data.iomem_data_count];
data->wil = wil;
data->offset = value;
file = debugfs_create_file(name, mode, parent, data, &fops_iomem_x32);
if (!IS_ERR_OR_NULL(file))
wil->dbg_data.iomem_data_count++;
return file;
}
static int wil_debugfs_ulong_set(void *data, u64 val)
......@@ -346,7 +386,8 @@ static void wil6210_debugfs_init_offset(struct wil6210_priv *wil,
case doff_io32:
f = wil_debugfs_create_iomem_x32(tbl[i].name,
tbl[i].mode, dbg,
base + tbl[i].off);
base + tbl[i].off,
wil);
break;
case doff_u8:
f = debugfs_create_u8(tbl[i].name, tbl[i].mode, dbg,
......@@ -475,13 +516,22 @@ static int wil6210_debugfs_create_ITR_CNT(struct wil6210_priv *wil,
static int wil_memread_debugfs_show(struct seq_file *s, void *data)
{
struct wil6210_priv *wil = s->private;
void __iomem *a = wmi_buffer(wil, cpu_to_le32(mem_addr));
void __iomem *a;
int ret;
ret = wil_pm_runtime_get(wil);
if (ret < 0)
return ret;
a = wmi_buffer(wil, cpu_to_le32(mem_addr));
if (a)
seq_printf(s, "[0x%08x] = 0x%08x\n", mem_addr, readl(a));
else
seq_printf(s, "[0x%08x] = INVALID\n", mem_addr);
wil_pm_runtime_put(wil);
return 0;
}
......@@ -502,10 +552,12 @@ static ssize_t wil_read_file_ioblob(struct file *file, char __user *user_buf,
{
enum { max_count = 4096 };
struct wil_blob_wrapper *wil_blob = file->private_data;
struct wil6210_priv *wil = wil_blob->wil;
loff_t pos = *ppos;
size_t available = wil_blob->blob.size;
void *buf;
size_t ret;
int rc;
if (test_bit(wil_status_suspending, wil_blob->wil->status) ||
test_bit(wil_status_suspended, wil_blob->wil->status))
......@@ -526,10 +578,19 @@ static ssize_t wil_read_file_ioblob(struct file *file, char __user *user_buf,
if (!buf)
return -ENOMEM;
rc = wil_pm_runtime_get(wil);
if (rc < 0) {
kfree(buf);
return rc;
}
wil_memcpy_fromio_32(buf, (const void __iomem *)
wil_blob->blob.data + pos, count);
ret = copy_to_user(user_buf, buf, count);
wil_pm_runtime_put(wil);
kfree(buf);
if (ret == count)
return -EFAULT;
......@@ -1571,8 +1632,6 @@ static ssize_t wil_write_suspend_stats(struct file *file,
struct wil6210_priv *wil = file->private_data;
memset(&wil->suspend_stats, 0, sizeof(wil->suspend_stats));
wil->suspend_stats.min_suspend_time = ULONG_MAX;
wil->suspend_stats.collection_start = ktime_get();
return len;
}
......@@ -1582,33 +1641,41 @@ static ssize_t wil_read_suspend_stats(struct file *file,
size_t count, loff_t *ppos)
{
struct wil6210_priv *wil = file->private_data;
static char text[400];
int n;
unsigned long long stats_collection_time =
ktime_to_us(ktime_sub(ktime_get(),
wil->suspend_stats.collection_start));
char *text;
int n, ret, text_size = 500;
n = snprintf(text, sizeof(text),
"Suspend statistics:\n"
text = kmalloc(text_size, GFP_KERNEL);
if (!text)
return -ENOMEM;
n = snprintf(text, text_size,
"Radio on suspend statistics:\n"
"successful suspends:%ld failed suspends:%ld\n"
"successful resumes:%ld failed resumes:%ld\n"
"rejected by device:%ld\n"
"Radio off suspend statistics:\n"
"successful suspends:%ld failed suspends:%ld\n"
"successful resumes:%ld failed resumes:%ld\n"
"rejected by host:%ld rejected by device:%ld\n"
"total suspend time:%lld min suspend time:%lld\n"
"max suspend time:%lld stats collection time: %lld\n",
wil->suspend_stats.successful_suspends,
wil->suspend_stats.failed_suspends,
wil->suspend_stats.successful_resumes,
wil->suspend_stats.failed_resumes,
wil->suspend_stats.rejected_by_host,
"General statistics:\n"
"rejected by host:%ld\n",
wil->suspend_stats.r_on.successful_suspends,
wil->suspend_stats.r_on.failed_suspends,
wil->suspend_stats.r_on.successful_resumes,
wil->suspend_stats.r_on.failed_resumes,
wil->suspend_stats.rejected_by_device,
wil->suspend_stats.total_suspend_time,
wil->suspend_stats.min_suspend_time,
wil->suspend_stats.max_suspend_time,
stats_collection_time);
wil->suspend_stats.r_off.successful_suspends,
wil->suspend_stats.r_off.failed_suspends,
wil->suspend_stats.r_off.successful_resumes,
wil->suspend_stats.r_off.failed_resumes,
wil->suspend_stats.rejected_by_host);
n = min_t(int, n, text_size);
n = min_t(int, n, sizeof(text));
ret = simple_read_from_buffer(user_buf, count, ppos, text, n);
return simple_read_from_buffer(user_buf, count, ppos, text, n);
kfree(text);
return ret;
}
static const struct file_operations fops_suspend_stats = {
......@@ -1736,14 +1803,31 @@ static const struct dbg_off dbg_statics[] = {
{},
};
static const int dbg_off_count = 4 * (ARRAY_SIZE(isr_off) - 1) +
ARRAY_SIZE(dbg_wil_regs) - 1 +
ARRAY_SIZE(pseudo_isr_off) - 1 +
ARRAY_SIZE(lgc_itr_cnt_off) - 1 +
ARRAY_SIZE(tx_itr_cnt_off) - 1 +
ARRAY_SIZE(rx_itr_cnt_off) - 1;
int wil6210_debugfs_init(struct wil6210_priv *wil)
{
struct dentry *dbg = wil->debug = debugfs_create_dir(WIL_NAME,
wil_to_wiphy(wil)->debugfsdir);
if (IS_ERR_OR_NULL(dbg))
return -ENODEV;
wil->dbg_data.data_arr = kcalloc(dbg_off_count,
sizeof(struct wil_debugfs_iomem_data),
GFP_KERNEL);
if (!wil->dbg_data.data_arr) {
debugfs_remove_recursive(dbg);
wil->debug = NULL;
return -ENOMEM;
}
wil->dbg_data.iomem_data_count = 0;
wil_pmc_init(wil);
wil6210_debugfs_init_files(wil, dbg);
......@@ -1758,8 +1842,6 @@ int wil6210_debugfs_init(struct wil6210_priv *wil)
wil6210_debugfs_create_ITR_CNT(wil, dbg);
wil->suspend_stats.collection_start = ktime_get();
return 0;
}
......@@ -1768,6 +1850,8 @@ void wil6210_debugfs_remove(struct wil6210_priv *wil)
debugfs_remove_recursive(wil->debug);
wil->debug = NULL;
kfree(wil->dbg_data.data_arr);
/* free pmc memory without sending command to fw, as it will
* be reset on the way down anyway
*/
......
......@@ -47,9 +47,14 @@ static int wil_ethtoolops_get_coalesce(struct net_device *ndev,
struct wil6210_priv *wil = ndev_to_wil(ndev);
u32 tx_itr_en, tx_itr_val = 0;
u32 rx_itr_en, rx_itr_val = 0;
int ret;
wil_dbg_misc(wil, "ethtoolops_get_coalesce\n");
ret = wil_pm_runtime_get(wil);
if (ret < 0)
return ret;
tx_itr_en = wil_r(wil, RGF_DMA_ITR_TX_CNT_CTL);
if (tx_itr_en & BIT_DMA_ITR_TX_CNT_CTL_EN)
tx_itr_val = wil_r(wil, RGF_DMA_ITR_TX_CNT_TRSH);
......@@ -58,6 +63,8 @@ static int wil_ethtoolops_get_coalesce(struct net_device *ndev,
if (rx_itr_en & BIT_DMA_ITR_RX_CNT_CTL_EN)
rx_itr_val = wil_r(wil, RGF_DMA_ITR_RX_CNT_TRSH);
wil_pm_runtime_put(wil);
cp->tx_coalesce_usecs = tx_itr_val;
cp->rx_coalesce_usecs = rx_itr_val;
return 0;
......@@ -67,6 +74,7 @@ static int wil_ethtoolops_set_coalesce(struct net_device *ndev,
struct ethtool_coalesce *cp)
{
struct wil6210_priv *wil = ndev_to_wil(ndev);
int ret;
wil_dbg_misc(wil, "ethtoolops_set_coalesce: rx %d usec, tx %d usec\n",
cp->rx_coalesce_usecs, cp->tx_coalesce_usecs);
......@@ -86,8 +94,15 @@ static int wil_ethtoolops_set_coalesce(struct net_device *ndev,
wil->tx_max_burst_duration = cp->tx_coalesce_usecs;
wil->rx_max_burst_duration = cp->rx_coalesce_usecs;
ret = wil_pm_runtime_get(wil);
if (ret < 0)
return ret;
wil_configure_interrupt_moderation(wil);
wil_pm_runtime_put(wil);
return 0;
out_bad:
......
......@@ -26,14 +26,17 @@
prefix_type, rowsize, \
groupsize, buf, len, ascii)
#define FW_ADDR_CHECK(ioaddr, val, msg) do { \
ioaddr = wmi_buffer(wil, val); \
if (!ioaddr) { \
wil_err_fw(wil, "bad " msg ": 0x%08x\n", \
le32_to_cpu(val)); \
return -EINVAL; \
} \
} while (0)
static bool wil_fw_addr_check(struct wil6210_priv *wil,
void __iomem **ioaddr, __le32 val,
u32 size, const char *msg)
{
*ioaddr = wmi_buffer_block(wil, val, size);
if (!(*ioaddr)) {
wil_err_fw(wil, "bad %s: 0x%08x\n", msg, le32_to_cpu(val));
return false;
}
return true;
}
/**
* wil_fw_verify - verify firmware file validity
......@@ -124,24 +127,19 @@ static int fw_ignore_section(struct wil6210_priv *wil, const void *data,
return 0;
}
static int fw_handle_comment(struct wil6210_priv *wil, const void *data,
size_t size)
{
wil_hex_dump_fw("", DUMP_PREFIX_OFFSET, 16, 1, data, size, true);
return 0;
}
static int
fw_handle_capabilities(struct wil6210_priv *wil, const void *data,
size_t size)
fw_handle_comment(struct wil6210_priv *wil, const void *data,
size_t size)
{
const struct wil_fw_record_capabilities *rec = data;
size_t capa_size;
if (size < sizeof(*rec) ||
le32_to_cpu(rec->magic) != WIL_FW_CAPABILITIES_MAGIC)
le32_to_cpu(rec->magic) != WIL_FW_CAPABILITIES_MAGIC) {
wil_hex_dump_fw("", DUMP_PREFIX_OFFSET, 16, 1,
data, size, true);
return 0;
}
capa_size = size - offsetof(struct wil_fw_record_capabilities,
capabilities);
......@@ -165,7 +163,8 @@ static int fw_handle_data(struct wil6210_priv *wil, const void *data,
return -EINVAL;
}
FW_ADDR_CHECK(dst, d->addr, "address");
if (!wil_fw_addr_check(wil, &dst, d->addr, s, "address"))
return -EINVAL;
wil_dbg_fw(wil, "write [0x%08x] <== %zu bytes\n", le32_to_cpu(d->addr),
s);
wil_memcpy_toio_32(dst, d->data, s);
......@@ -197,7 +196,8 @@ static int fw_handle_fill(struct wil6210_priv *wil, const void *data,
return -EINVAL;
}
FW_ADDR_CHECK(dst, d->addr, "address");
if (!wil_fw_addr_check(wil, &dst, d->addr, s, "address"))
return -EINVAL;
v = le32_to_cpu(d->value);
wil_dbg_fw(wil, "fill [0x%08x] <== 0x%08x, %zu bytes\n",
......@@ -253,7 +253,8 @@ static int fw_handle_direct_write(struct wil6210_priv *wil, const void *data,
u32 v = le32_to_cpu(block[i].value);
u32 x, y;
FW_ADDR_CHECK(dst, block[i].addr, "address");
if (!wil_fw_addr_check(wil, &dst, block[i].addr, 0, "address"))
return -EINVAL;
x = readl(dst);
y = (x & m) | (v & ~m);
......@@ -319,10 +320,15 @@ static int fw_handle_gateway_data(struct wil6210_priv *wil, const void *data,
wil_dbg_fw(wil, "gw write record [%3d] blocks, cmd 0x%08x\n",
n, gw_cmd);
FW_ADDR_CHECK(gwa_addr, d->gateway_addr_addr, "gateway_addr_addr");
FW_ADDR_CHECK(gwa_val, d->gateway_value_addr, "gateway_value_addr");
FW_ADDR_CHECK(gwa_cmd, d->gateway_cmd_addr, "gateway_cmd_addr");
FW_ADDR_CHECK(gwa_ctl, d->gateway_ctrl_address, "gateway_ctrl_address");
if (!wil_fw_addr_check(wil, &gwa_addr, d->gateway_addr_addr, 0,
"gateway_addr_addr") ||
!wil_fw_addr_check(wil, &gwa_val, d->gateway_value_addr, 0,
"gateway_value_addr") ||
!wil_fw_addr_check(wil, &gwa_cmd, d->gateway_cmd_addr, 0,
"gateway_cmd_addr") ||
!wil_fw_addr_check(wil, &gwa_ctl, d->gateway_ctrl_address, 0,
"gateway_ctrl_address"))
return -EINVAL;
wil_dbg_fw(wil, "gw addresses: addr 0x%08x val 0x%08x"
" cmd 0x%08x ctl 0x%08x\n",
......@@ -378,12 +384,19 @@ static int fw_handle_gateway_data4(struct wil6210_priv *wil, const void *data,
wil_dbg_fw(wil, "gw4 write record [%3d] blocks, cmd 0x%08x\n",
n, gw_cmd);
FW_ADDR_CHECK(gwa_addr, d->gateway_addr_addr, "gateway_addr_addr");
if (!wil_fw_addr_check(wil, &gwa_addr, d->gateway_addr_addr, 0,
"gateway_addr_addr"))
return -EINVAL;
for (k = 0; k < ARRAY_SIZE(block->value); k++)
FW_ADDR_CHECK(gwa_val[k], d->gateway_value_addr[k],
"gateway_value_addr");
FW_ADDR_CHECK(gwa_cmd, d->gateway_cmd_addr, "gateway_cmd_addr");
FW_ADDR_CHECK(gwa_ctl, d->gateway_ctrl_address, "gateway_ctrl_address");
if (!wil_fw_addr_check(wil, &gwa_val[k],
d->gateway_value_addr[k],
0, "gateway_value_addr"))
return -EINVAL;
if (!wil_fw_addr_check(wil, &gwa_cmd, d->gateway_cmd_addr, 0,
"gateway_cmd_addr") ||
!wil_fw_addr_check(wil, &gwa_ctl, d->gateway_ctrl_address, 0,
"gateway_ctrl_address"))
return -EINVAL;
wil_dbg_fw(wil, "gw4 addresses: addr 0x%08x cmd 0x%08x ctl 0x%08x\n",
le32_to_cpu(d->gateway_addr_addr),
......@@ -422,7 +435,7 @@ static const struct {
int (*parse_handler)(struct wil6210_priv *wil, const void *data,
size_t size);
} wil_fw_handlers[] = {
{wil_fw_type_comment, fw_handle_comment, fw_handle_capabilities},
{wil_fw_type_comment, fw_handle_comment, fw_handle_comment},
{wil_fw_type_data, fw_handle_data, fw_ignore_section},
{wil_fw_type_fill, fw_handle_fill, fw_ignore_section},
/* wil_fw_type_action */
......@@ -517,7 +530,7 @@ int wil_request_firmware(struct wil6210_priv *wil, const char *name,
rc = request_firmware(&fw, name, wil_to_dev(wil));
if (rc) {
wil_err_fw(wil, "Failed to load firmware %s\n", name);
wil_err_fw(wil, "Failed to load firmware %s rc %d\n", name, rc);
return rc;
}
wil_dbg_fw(wil, "Loading <%s>, %zu bytes\n", name, fw->size);
......
......@@ -358,6 +358,25 @@ static void wil_cache_mbox_regs(struct wil6210_priv *wil)
wil_mbox_ring_le2cpus(&wil->mbox_ctl.tx);
}
static bool wil_validate_mbox_regs(struct wil6210_priv *wil)
{
size_t min_size = sizeof(struct wil6210_mbox_hdr) +
sizeof(struct wmi_cmd_hdr);
if (wil->mbox_ctl.rx.entry_size < min_size) {
wil_err(wil, "rx mbox entry too small (%d)\n",
wil->mbox_ctl.rx.entry_size);
return false;
}
if (wil->mbox_ctl.tx.entry_size < min_size) {
wil_err(wil, "tx mbox entry too small (%d)\n",
wil->mbox_ctl.tx.entry_size);
return false;
}
return true;
}
static irqreturn_t wil6210_irq_misc(int irq, void *cookie)
{
struct wil6210_priv *wil = cookie;
......@@ -393,7 +412,8 @@ static irqreturn_t wil6210_irq_misc(int irq, void *cookie)
if (isr & ISR_MISC_FW_READY) {
wil_dbg_irq(wil, "IRQ: FW ready\n");
wil_cache_mbox_regs(wil);
set_bit(wil_status_mbox_ready, wil->status);
if (wil_validate_mbox_regs(wil))
set_bit(wil_status_mbox_ready, wil->status);
/**
* Actual FW ready indicated by the
* WMI_FW_READY_EVENTID
......
......@@ -579,7 +579,6 @@ int wil_priv_init(struct wil6210_priv *wil)
wil->wakeup_trigger = WMI_WAKEUP_TRIGGER_UCAST |
WMI_WAKEUP_TRIGGER_BCAST;
memset(&wil->suspend_stats, 0, sizeof(wil->suspend_stats));
wil->suspend_stats.min_suspend_time = ULONG_MAX;
wil->vring_idle_trsh = 16;
return 0;
......@@ -760,6 +759,8 @@ static void wil_collect_fw_info(struct wil6210_priv *wil)
u8 retry_short;
int rc;
wil_refresh_fw_capabilities(wil);
rc = wmi_get_mgmt_retry(wil, &retry_short);
if (!rc) {
wiphy->retry_short = retry_short;
......@@ -767,6 +768,25 @@ static void wil_collect_fw_info(struct wil6210_priv *wil)
}
}
void wil_refresh_fw_capabilities(struct wil6210_priv *wil)
{
struct wiphy *wiphy = wil_to_wiphy(wil);
wil->keep_radio_on_during_sleep =
wil->platform_ops.keep_radio_on_during_sleep &&
wil->platform_ops.keep_radio_on_during_sleep(
wil->platform_handle) &&
test_bit(WMI_FW_CAPABILITY_D3_SUSPEND, wil->fw_capabilities);
wil_info(wil, "keep_radio_on_during_sleep (%d)\n",
wil->keep_radio_on_during_sleep);
if (test_bit(WMI_FW_CAPABILITY_RSSI_REPORTING, wil->fw_capabilities))
wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM;
else
wiphy->signal_type = CFG80211_SIGNAL_TYPE_UNSPEC;
}
void wil_mbox_ring_le2cpus(struct wil6210_mbox_ring *r)
{
le32_to_cpus(&r->base);
......@@ -1071,11 +1091,11 @@ int wil_reset(struct wil6210_priv *wil, bool load_fw)
return rc;
}
wil_collect_fw_info(wil);
if (wil->ps_profile != WMI_PS_PROFILE_TYPE_DEFAULT)
wil_ps_update(wil, wil->ps_profile);
wil_collect_fw_info(wil);
if (wil->platform_ops.notify) {
rc = wil->platform_ops.notify(wil->platform_handle,
WIL_PLATFORM_EVT_FW_RDY);
......
......@@ -21,6 +21,7 @@
static int wil_open(struct net_device *ndev)
{
struct wil6210_priv *wil = ndev_to_wil(ndev);
int rc;
wil_dbg_misc(wil, "open\n");
......@@ -30,16 +31,29 @@ static int wil_open(struct net_device *ndev)
return -EINVAL;
}
return wil_up(wil);
rc = wil_pm_runtime_get(wil);
if (rc < 0)
return rc;
rc = wil_up(wil);
if (rc)
wil_pm_runtime_put(wil);
return rc;
}
static int wil_stop(struct net_device *ndev)
{
struct wil6210_priv *wil = ndev_to_wil(ndev);
int rc;
wil_dbg_misc(wil, "stop\n");
return wil_down(wil);
rc = wil_down(wil);
if (!rc)
wil_pm_runtime_put(wil);
return rc;
}
static const struct net_device_ops wil_netdev_ops = {
......
......@@ -21,6 +21,7 @@
#include <linux/suspend.h>
#include "wil6210.h"
#include <linux/rtnetlink.h>
#include <linux/pm_runtime.h>
static bool use_msi = true;
module_param(use_msi, bool, 0444);
......@@ -31,10 +32,8 @@ module_param(ftm_mode, bool, 0444);
MODULE_PARM_DESC(ftm_mode, " Set factory test mode, default - false");
#ifdef CONFIG_PM
#ifdef CONFIG_PM_SLEEP
static int wil6210_pm_notify(struct notifier_block *notify_block,
unsigned long mode, void *unused);
#endif /* CONFIG_PM_SLEEP */
#endif /* CONFIG_PM */
static
......@@ -84,9 +83,7 @@ void wil_set_capabilities(struct wil6210_priv *wil)
/* extract FW capabilities from file without loading the FW */
wil_request_firmware(wil, wil->wil_fw_name, false);
if (test_bit(WMI_FW_CAPABILITY_RSSI_REPORTING, wil->fw_capabilities))
wil_to_wiphy(wil)->signal_type = CFG80211_SIGNAL_TYPE_MBM;
wil_refresh_fw_capabilities(wil);
}
void wil_disable_irq(struct wil6210_priv *wil)
......@@ -296,15 +293,6 @@ static int wil_pcie_probe(struct pci_dev *pdev, const struct pci_device_id *id)
wil_set_capabilities(wil);
wil6210_clear_irq(wil);
wil->keep_radio_on_during_sleep =
wil->platform_ops.keep_radio_on_during_sleep &&
wil->platform_ops.keep_radio_on_during_sleep(
wil->platform_handle) &&
test_bit(WMI_FW_CAPABILITY_D3_SUSPEND, wil->fw_capabilities);
wil_info(wil, "keep_radio_on_during_sleep (%d)\n",
wil->keep_radio_on_during_sleep);
/* FW should raise IRQ when ready */
rc = wil_if_pcie_enable(wil);
if (rc) {
......@@ -320,7 +308,6 @@ static int wil_pcie_probe(struct pci_dev *pdev, const struct pci_device_id *id)
}
#ifdef CONFIG_PM
#ifdef CONFIG_PM_SLEEP
wil->pm_notify.notifier_call = wil6210_pm_notify;
rc = register_pm_notifier(&wil->pm_notify);
if (rc)
......@@ -328,11 +315,11 @@ static int wil_pcie_probe(struct pci_dev *pdev, const struct pci_device_id *id)
* be prevented in a later phase if needed
*/
wil_err(wil, "register_pm_notifier failed: %d\n", rc);
#endif /* CONFIG_PM_SLEEP */
#endif /* CONFIG_PM */
wil6210_debugfs_init(wil);
wil_pm_runtime_allow(wil);
return 0;
......@@ -360,11 +347,11 @@ static void wil_pcie_remove(struct pci_dev *pdev)
wil_dbg_misc(wil, "pcie_remove\n");
#ifdef CONFIG_PM
#ifdef CONFIG_PM_SLEEP
unregister_pm_notifier(&wil->pm_notify);
#endif /* CONFIG_PM_SLEEP */
#endif /* CONFIG_PM */
wil_pm_runtime_forbid(wil);
wil6210_debugfs_remove(wil);
rtnl_lock();
wil_p2p_wdev_free(wil);
......@@ -386,13 +373,15 @@ static const struct pci_device_id wil6210_pcie_ids[] = {
MODULE_DEVICE_TABLE(pci, wil6210_pcie_ids);
#ifdef CONFIG_PM
#ifdef CONFIG_PM_SLEEP
static int wil6210_suspend(struct device *dev, bool is_runtime)
{
int rc = 0;
struct pci_dev *pdev = to_pci_dev(dev);
struct wil6210_priv *wil = pci_get_drvdata(pdev);
struct net_device *ndev = wil_to_ndev(wil);
bool keep_radio_on = ndev->flags & IFF_UP &&
wil->keep_radio_on_during_sleep;
wil_dbg_pm(wil, "suspend: %s\n", is_runtime ? "runtime" : "system");
......@@ -400,16 +389,18 @@ static int wil6210_suspend(struct device *dev, bool is_runtime)
if (rc)
goto out;
rc = wil_suspend(wil, is_runtime);
rc = wil_suspend(wil, is_runtime, keep_radio_on);
if (!rc) {
wil->suspend_stats.successful_suspends++;
/* If platform device supports keep_radio_on_during_sleep
* it will control PCIe master
/* In case radio stays on, platform device will control
* PCIe master
*/
if (!wil->keep_radio_on_during_sleep)
if (!keep_radio_on) {
/* disable bus mastering */
pci_clear_master(pdev);
wil->suspend_stats.r_off.successful_suspends++;
} else {
wil->suspend_stats.r_on.successful_suspends++;
}
}
out:
return rc;
......@@ -420,23 +411,32 @@ static int wil6210_resume(struct device *dev, bool is_runtime)
int rc = 0;
struct pci_dev *pdev = to_pci_dev(dev);
struct wil6210_priv *wil = pci_get_drvdata(pdev);
struct net_device *ndev = wil_to_ndev(wil);
bool keep_radio_on = ndev->flags & IFF_UP &&
wil->keep_radio_on_during_sleep;
wil_dbg_pm(wil, "resume: %s\n", is_runtime ? "runtime" : "system");
/* If platform device supports keep_radio_on_during_sleep it will
* control PCIe master
/* In case radio stays on, platform device will control
* PCIe master
*/
if (!wil->keep_radio_on_during_sleep)
if (!keep_radio_on)
/* allow master */
pci_set_master(pdev);
rc = wil_resume(wil, is_runtime);
rc = wil_resume(wil, is_runtime, keep_radio_on);
if (rc) {
wil_err(wil, "device failed to resume (%d)\n", rc);
wil->suspend_stats.failed_resumes++;
if (!wil->keep_radio_on_during_sleep)
if (!keep_radio_on) {
pci_clear_master(pdev);
wil->suspend_stats.r_off.failed_resumes++;
} else {
wil->suspend_stats.r_on.failed_resumes++;
}
} else {
wil->suspend_stats.successful_resumes++;
if (keep_radio_on)
wil->suspend_stats.r_on.successful_resumes++;
else
wil->suspend_stats.r_off.successful_resumes++;
}
return rc;
......@@ -490,12 +490,43 @@ static int wil6210_pm_resume(struct device *dev)
{
return wil6210_resume(dev, false);
}
#endif /* CONFIG_PM_SLEEP */
static int wil6210_pm_runtime_idle(struct device *dev)
{
struct pci_dev *pdev = to_pci_dev(dev);
struct wil6210_priv *wil = pci_get_drvdata(pdev);
wil_dbg_pm(wil, "Runtime idle\n");
return wil_can_suspend(wil, true);
}
static int wil6210_pm_runtime_resume(struct device *dev)
{
return wil6210_resume(dev, true);
}
static int wil6210_pm_runtime_suspend(struct device *dev)
{
struct pci_dev *pdev = to_pci_dev(dev);
struct wil6210_priv *wil = pci_get_drvdata(pdev);
if (test_bit(wil_status_suspended, wil->status)) {
wil_dbg_pm(wil, "trying to suspend while suspended\n");
return 1;
}
return wil6210_suspend(dev, true);
}
#endif /* CONFIG_PM */
static const struct dev_pm_ops wil6210_pm_ops = {
#ifdef CONFIG_PM
SET_SYSTEM_SLEEP_PM_OPS(wil6210_pm_suspend, wil6210_pm_resume)
SET_RUNTIME_PM_OPS(wil6210_pm_runtime_suspend,
wil6210_pm_runtime_resume,
wil6210_pm_runtime_idle)
#endif /* CONFIG_PM */
};
static struct pci_driver wil6210_driver = {
......
......@@ -16,15 +16,30 @@
#include "wil6210.h"
#include <linux/jiffies.h>
#include <linux/pm_runtime.h>
#define WIL6210_AUTOSUSPEND_DELAY_MS (1000)
int wil_can_suspend(struct wil6210_priv *wil, bool is_runtime)
{
int rc = 0;
struct wireless_dev *wdev = wil->wdev;
struct net_device *ndev = wil_to_ndev(wil);
bool wmi_only = test_bit(WMI_FW_CAPABILITY_WMI_ONLY,
wil->fw_capabilities);
wil_dbg_pm(wil, "can_suspend: %s\n", is_runtime ? "runtime" : "system");
if (wmi_only || debug_fw) {
wil_dbg_pm(wil, "Deny any suspend - %s mode\n",
wmi_only ? "wmi_only" : "debug_fw");
rc = -EBUSY;
goto out;
}
if (is_runtime && !wil->platform_ops.suspend) {
rc = -EBUSY;
goto out;
}
if (!(ndev->flags & IFF_UP)) {
/* can always sleep when down */
wil_dbg_pm(wil, "Interface is down\n");
......@@ -44,6 +59,10 @@ int wil_can_suspend(struct wil6210_priv *wil, bool is_runtime)
/* interface is running */
switch (wdev->iftype) {
case NL80211_IFTYPE_MONITOR:
wil_dbg_pm(wil, "Sniffer\n");
rc = -EBUSY;
goto out;
/* for STA-like interface, don't runtime suspend */
case NL80211_IFTYPE_STATION:
case NL80211_IFTYPE_P2P_CLIENT:
if (test_bit(wil_status_fwconnecting, wil->status)) {
......@@ -51,6 +70,12 @@ int wil_can_suspend(struct wil6210_priv *wil, bool is_runtime)
rc = -EBUSY;
goto out;
}
/* Runtime pm not supported in case the interface is up */
if (is_runtime) {
wil_dbg_pm(wil, "STA-like interface\n");
rc = -EBUSY;
goto out;
}
break;
/* AP-like interface - can't suspend */
default:
......@@ -158,7 +183,7 @@ static int wil_suspend_keep_radio_on(struct wil6210_priv *wil)
break;
wil_err(wil,
"TO waiting for idle RX, suspend failed\n");
wil->suspend_stats.failed_suspends++;
wil->suspend_stats.r_on.failed_suspends++;
goto resume_after_fail;
}
wil_dbg_ratelimited(wil, "rx vring is not empty -> NAPI\n");
......@@ -174,7 +199,7 @@ static int wil_suspend_keep_radio_on(struct wil6210_priv *wil)
*/
if (!wil_is_wmi_idle(wil)) {
wil_err(wil, "suspend failed due to pending WMI events\n");
wil->suspend_stats.failed_suspends++;
wil->suspend_stats.r_on.failed_suspends++;
goto resume_after_fail;
}
......@@ -188,7 +213,7 @@ static int wil_suspend_keep_radio_on(struct wil6210_priv *wil)
if (rc) {
wil_err(wil, "platform device failed to suspend (%d)\n",
rc);
wil->suspend_stats.failed_suspends++;
wil->suspend_stats.r_on.failed_suspends++;
wil_c(wil, RGF_USER_CLKS_CTL_0, BIT_USER_CLKS_RST_PWGD);
wil_unmask_irq(wil);
goto resume_after_fail;
......@@ -235,6 +260,7 @@ static int wil_suspend_radio_off(struct wil6210_priv *wil)
rc = wil_down(wil);
if (rc) {
wil_err(wil, "wil_down : %d\n", rc);
wil->suspend_stats.r_off.failed_suspends++;
goto out;
}
}
......@@ -247,6 +273,7 @@ static int wil_suspend_radio_off(struct wil6210_priv *wil)
rc = wil->platform_ops.suspend(wil->platform_handle, false);
if (rc) {
wil_enable_irq(wil);
wil->suspend_stats.r_off.failed_suspends++;
goto out;
}
}
......@@ -279,12 +306,9 @@ static int wil_resume_radio_off(struct wil6210_priv *wil)
return rc;
}
int wil_suspend(struct wil6210_priv *wil, bool is_runtime)
int wil_suspend(struct wil6210_priv *wil, bool is_runtime, bool keep_radio_on)
{
int rc = 0;
struct net_device *ndev = wil_to_ndev(wil);
bool keep_radio_on = ndev->flags & IFF_UP &&
wil->keep_radio_on_during_sleep;
wil_dbg_pm(wil, "suspend: %s\n", is_runtime ? "runtime" : "system");
......@@ -301,19 +325,12 @@ int wil_suspend(struct wil6210_priv *wil, bool is_runtime)
wil_dbg_pm(wil, "suspend: %s => %d\n",
is_runtime ? "runtime" : "system", rc);
if (!rc)
wil->suspend_stats.suspend_start_time = ktime_get();
return rc;
}
int wil_resume(struct wil6210_priv *wil, bool is_runtime)
int wil_resume(struct wil6210_priv *wil, bool is_runtime, bool keep_radio_on)
{
int rc = 0;
struct net_device *ndev = wil_to_ndev(wil);
bool keep_radio_on = ndev->flags & IFF_UP &&
wil->keep_radio_on_during_sleep;
unsigned long long suspend_time_usec = 0;
wil_dbg_pm(wil, "resume: %s\n", is_runtime ? "runtime" : "system");
......@@ -331,20 +348,49 @@ int wil_resume(struct wil6210_priv *wil, bool is_runtime)
else
rc = wil_resume_radio_off(wil);
if (rc)
goto out;
suspend_time_usec =
ktime_to_us(ktime_sub(ktime_get(),
wil->suspend_stats.suspend_start_time));
wil->suspend_stats.total_suspend_time += suspend_time_usec;
if (suspend_time_usec < wil->suspend_stats.min_suspend_time)
wil->suspend_stats.min_suspend_time = suspend_time_usec;
if (suspend_time_usec > wil->suspend_stats.max_suspend_time)
wil->suspend_stats.max_suspend_time = suspend_time_usec;
out:
wil_dbg_pm(wil, "resume: %s => %d, suspend time %lld usec\n",
is_runtime ? "runtime" : "system", rc, suspend_time_usec);
wil_dbg_pm(wil, "resume: %s => %d\n", is_runtime ? "runtime" : "system",
rc);
return rc;
}
void wil_pm_runtime_allow(struct wil6210_priv *wil)
{
struct device *dev = wil_to_dev(wil);
pm_runtime_put_noidle(dev);
pm_runtime_set_autosuspend_delay(dev, WIL6210_AUTOSUSPEND_DELAY_MS);
pm_runtime_use_autosuspend(dev);
pm_runtime_allow(dev);
}
void wil_pm_runtime_forbid(struct wil6210_priv *wil)
{
struct device *dev = wil_to_dev(wil);
pm_runtime_forbid(dev);
pm_runtime_get_noresume(dev);
}
int wil_pm_runtime_get(struct wil6210_priv *wil)
{
int rc;
struct device *dev = wil_to_dev(wil);
rc = pm_runtime_get_sync(dev);
if (rc < 0) {
wil_err(wil, "pm_runtime_get_sync() failed, rc = %d\n", rc);
pm_runtime_put_noidle(dev);
return rc;
}
return 0;
}
void wil_pm_runtime_put(struct wil6210_priv *wil)
{
struct device *dev = wil_to_dev(wil);
pm_runtime_mark_last_busy(dev);
pm_runtime_put_autosuspend(dev);
}
......@@ -82,18 +82,18 @@ static inline u32 WIL_GET_BITS(u32 x, int b0, int b1)
*/
#define WIL_MAX_MPDU_OVERHEAD (62)
struct wil_suspend_stats {
struct wil_suspend_count_stats {
unsigned long successful_suspends;
unsigned long failed_suspends;
unsigned long successful_resumes;
unsigned long failed_suspends;
unsigned long failed_resumes;
unsigned long rejected_by_device;
};
struct wil_suspend_stats {
struct wil_suspend_count_stats r_off;
struct wil_suspend_count_stats r_on;
unsigned long rejected_by_device; /* only radio on */
unsigned long rejected_by_host;
unsigned long long total_suspend_time;
unsigned long long min_suspend_time;
unsigned long long max_suspend_time;
ktime_t collection_start;
ktime_t suspend_start_time;
};
/* Calculate MAC buffer size for the firmware. It includes all overhead,
......@@ -616,6 +616,16 @@ struct blink_on_off_time {
u32 off_ms;
};
struct wil_debugfs_iomem_data {
void *offset;
struct wil6210_priv *wil;
};
struct wil_debugfs_data {
struct wil_debugfs_iomem_data *data_arr;
int iomem_data_count;
};
extern struct blink_on_off_time led_blink_time[WIL_LED_TIME_LAST];
extern u8 led_id;
extern u8 led_polarity;
......@@ -708,6 +718,7 @@ struct wil6210_priv {
u8 abft_len;
u8 wakeup_trigger;
struct wil_suspend_stats suspend_stats;
struct wil_debugfs_data dbg_data;
void *platform_handle;
struct wil_platform_ops platform_ops;
......@@ -732,9 +743,7 @@ struct wil6210_priv {
int fw_calib_result;
#ifdef CONFIG_PM
#ifdef CONFIG_PM_SLEEP
struct notifier_block pm_notify;
#endif /* CONFIG_PM_SLEEP */
#endif /* CONFIG_PM */
bool suspend_resp_rcvd;
......@@ -861,10 +870,12 @@ int wil_up(struct wil6210_priv *wil);
int __wil_up(struct wil6210_priv *wil);
int wil_down(struct wil6210_priv *wil);
int __wil_down(struct wil6210_priv *wil);
void wil_refresh_fw_capabilities(struct wil6210_priv *wil);
void wil_mbox_ring_le2cpus(struct wil6210_mbox_ring *r);
int wil_find_cid(struct wil6210_priv *wil, const u8 *mac);
void wil_set_ethtoolops(struct net_device *ndev);
void __iomem *wmi_buffer_block(struct wil6210_priv *wil, __le32 ptr, u32 size);
void __iomem *wmi_buffer(struct wil6210_priv *wil, __le32 ptr);
void __iomem *wmi_addr(struct wil6210_priv *wil, u32 ptr);
int wmi_read_hdr(struct wil6210_priv *wil, __le32 ptr,
......@@ -999,9 +1010,14 @@ int wil_request_firmware(struct wil6210_priv *wil, const char *name,
bool load);
bool wil_fw_verify_file_exists(struct wil6210_priv *wil, const char *name);
void wil_pm_runtime_allow(struct wil6210_priv *wil);
void wil_pm_runtime_forbid(struct wil6210_priv *wil);
int wil_pm_runtime_get(struct wil6210_priv *wil);
void wil_pm_runtime_put(struct wil6210_priv *wil);
int wil_can_suspend(struct wil6210_priv *wil, bool is_runtime);
int wil_suspend(struct wil6210_priv *wil, bool is_runtime);
int wil_resume(struct wil6210_priv *wil, bool is_runtime);
int wil_suspend(struct wil6210_priv *wil, bool is_runtime, bool keep_radio_on);
int wil_resume(struct wil6210_priv *wil, bool is_runtime, bool keep_radio_on);
bool wil_is_wmi_idle(struct wil6210_priv *wil);
int wmi_resume(struct wil6210_priv *wil);
int wmi_suspend(struct wil6210_priv *wil);
......
......@@ -140,13 +140,15 @@ static u32 wmi_addr_remap(u32 x)
/**
* Check address validity for WMI buffer; remap if needed
* @ptr - internal (linker) fw/ucode address
* @size - if non zero, validate the block does not
* exceed the device memory (bar)
*
* Valid buffer should be DWORD aligned
*
* return address for accessing buffer from the host;
* if buffer is not valid, return NULL.
*/
void __iomem *wmi_buffer(struct wil6210_priv *wil, __le32 ptr_)
void __iomem *wmi_buffer_block(struct wil6210_priv *wil, __le32 ptr_, u32 size)
{
u32 off;
u32 ptr = le32_to_cpu(ptr_);
......@@ -161,10 +163,17 @@ void __iomem *wmi_buffer(struct wil6210_priv *wil, __le32 ptr_)
off = HOSTADDR(ptr);
if (off > wil->bar_size - 4)
return NULL;
if (size && ((off + size > wil->bar_size) || (off + size < off)))
return NULL;
return wil->csr + off;
}
void __iomem *wmi_buffer(struct wil6210_priv *wil, __le32 ptr_)
{
return wmi_buffer_block(wil, ptr_, 0);
}
/**
* Check address validity
*/
......@@ -198,6 +207,232 @@ int wmi_read_hdr(struct wil6210_priv *wil, __le32 ptr,
return 0;
}
static const char *cmdid2name(u16 cmdid)
{
switch (cmdid) {
case WMI_NOTIFY_REQ_CMDID:
return "WMI_NOTIFY_REQ_CMD";
case WMI_START_SCAN_CMDID:
return "WMI_START_SCAN_CMD";
case WMI_CONNECT_CMDID:
return "WMI_CONNECT_CMD";
case WMI_DISCONNECT_CMDID:
return "WMI_DISCONNECT_CMD";
case WMI_SW_TX_REQ_CMDID:
return "WMI_SW_TX_REQ_CMD";
case WMI_GET_RF_SECTOR_PARAMS_CMDID:
return "WMI_GET_RF_SECTOR_PARAMS_CMD";
case WMI_SET_RF_SECTOR_PARAMS_CMDID:
return "WMI_SET_RF_SECTOR_PARAMS_CMD";
case WMI_GET_SELECTED_RF_SECTOR_INDEX_CMDID:
return "WMI_GET_SELECTED_RF_SECTOR_INDEX_CMD";
case WMI_SET_SELECTED_RF_SECTOR_INDEX_CMDID:
return "WMI_SET_SELECTED_RF_SECTOR_INDEX_CMD";
case WMI_BRP_SET_ANT_LIMIT_CMDID:
return "WMI_BRP_SET_ANT_LIMIT_CMD";
case WMI_TOF_SESSION_START_CMDID:
return "WMI_TOF_SESSION_START_CMD";
case WMI_AOA_MEAS_CMDID:
return "WMI_AOA_MEAS_CMD";
case WMI_PMC_CMDID:
return "WMI_PMC_CMD";
case WMI_TOF_GET_TX_RX_OFFSET_CMDID:
return "WMI_TOF_GET_TX_RX_OFFSET_CMD";
case WMI_TOF_SET_TX_RX_OFFSET_CMDID:
return "WMI_TOF_SET_TX_RX_OFFSET_CMD";
case WMI_VRING_CFG_CMDID:
return "WMI_VRING_CFG_CMD";
case WMI_BCAST_VRING_CFG_CMDID:
return "WMI_BCAST_VRING_CFG_CMD";
case WMI_TRAFFIC_SUSPEND_CMDID:
return "WMI_TRAFFIC_SUSPEND_CMD";
case WMI_TRAFFIC_RESUME_CMDID:
return "WMI_TRAFFIC_RESUME_CMD";
case WMI_ECHO_CMDID:
return "WMI_ECHO_CMD";
case WMI_SET_MAC_ADDRESS_CMDID:
return "WMI_SET_MAC_ADDRESS_CMD";
case WMI_LED_CFG_CMDID:
return "WMI_LED_CFG_CMD";
case WMI_PCP_START_CMDID:
return "WMI_PCP_START_CMD";
case WMI_PCP_STOP_CMDID:
return "WMI_PCP_STOP_CMD";
case WMI_SET_SSID_CMDID:
return "WMI_SET_SSID_CMD";
case WMI_GET_SSID_CMDID:
return "WMI_GET_SSID_CMD";
case WMI_SET_PCP_CHANNEL_CMDID:
return "WMI_SET_PCP_CHANNEL_CMD";
case WMI_GET_PCP_CHANNEL_CMDID:
return "WMI_GET_PCP_CHANNEL_CMD";
case WMI_P2P_CFG_CMDID:
return "WMI_P2P_CFG_CMD";
case WMI_START_LISTEN_CMDID:
return "WMI_START_LISTEN_CMD";
case WMI_START_SEARCH_CMDID:
return "WMI_START_SEARCH_CMD";
case WMI_DISCOVERY_STOP_CMDID:
return "WMI_DISCOVERY_STOP_CMD";
case WMI_DELETE_CIPHER_KEY_CMDID:
return "WMI_DELETE_CIPHER_KEY_CMD";
case WMI_ADD_CIPHER_KEY_CMDID:
return "WMI_ADD_CIPHER_KEY_CMD";
case WMI_SET_APPIE_CMDID:
return "WMI_SET_APPIE_CMD";
case WMI_CFG_RX_CHAIN_CMDID:
return "WMI_CFG_RX_CHAIN_CMD";
case WMI_TEMP_SENSE_CMDID:
return "WMI_TEMP_SENSE_CMD";
case WMI_DEL_STA_CMDID:
return "WMI_DEL_STA_CMD";
case WMI_DISCONNECT_STA_CMDID:
return "WMI_DISCONNECT_STA_CMD";
case WMI_VRING_BA_EN_CMDID:
return "WMI_VRING_BA_EN_CMD";
case WMI_VRING_BA_DIS_CMDID:
return "WMI_VRING_BA_DIS_CMD";
case WMI_RCP_DELBA_CMDID:
return "WMI_RCP_DELBA_CMD";
case WMI_RCP_ADDBA_RESP_CMDID:
return "WMI_RCP_ADDBA_RESP_CMD";
case WMI_PS_DEV_PROFILE_CFG_CMDID:
return "WMI_PS_DEV_PROFILE_CFG_CMD";
case WMI_SET_MGMT_RETRY_LIMIT_CMDID:
return "WMI_SET_MGMT_RETRY_LIMIT_CMD";
case WMI_GET_MGMT_RETRY_LIMIT_CMDID:
return "WMI_GET_MGMT_RETRY_LIMIT_CMD";
case WMI_ABORT_SCAN_CMDID:
return "WMI_ABORT_SCAN_CMD";
case WMI_NEW_STA_CMDID:
return "WMI_NEW_STA_CMD";
case WMI_SET_THERMAL_THROTTLING_CFG_CMDID:
return "WMI_SET_THERMAL_THROTTLING_CFG_CMD";
case WMI_GET_THERMAL_THROTTLING_CFG_CMDID:
return "WMI_GET_THERMAL_THROTTLING_CFG_CMD";
case WMI_LINK_MAINTAIN_CFG_WRITE_CMDID:
return "WMI_LINK_MAINTAIN_CFG_WRITE_CMD";
case WMI_LO_POWER_CALIB_FROM_OTP_CMDID:
return "WMI_LO_POWER_CALIB_FROM_OTP_CMD";
default:
return "Untracked CMD";
}
}
static const char *eventid2name(u16 eventid)
{
switch (eventid) {
case WMI_NOTIFY_REQ_DONE_EVENTID:
return "WMI_NOTIFY_REQ_DONE_EVENT";
case WMI_DISCONNECT_EVENTID:
return "WMI_DISCONNECT_EVENT";
case WMI_SW_TX_COMPLETE_EVENTID:
return "WMI_SW_TX_COMPLETE_EVENT";
case WMI_GET_RF_SECTOR_PARAMS_DONE_EVENTID:
return "WMI_GET_RF_SECTOR_PARAMS_DONE_EVENT";
case WMI_SET_RF_SECTOR_PARAMS_DONE_EVENTID:
return "WMI_SET_RF_SECTOR_PARAMS_DONE_EVENT";
case WMI_GET_SELECTED_RF_SECTOR_INDEX_DONE_EVENTID:
return "WMI_GET_SELECTED_RF_SECTOR_INDEX_DONE_EVENT";
case WMI_SET_SELECTED_RF_SECTOR_INDEX_DONE_EVENTID:
return "WMI_SET_SELECTED_RF_SECTOR_INDEX_DONE_EVENT";
case WMI_BRP_SET_ANT_LIMIT_EVENTID:
return "WMI_BRP_SET_ANT_LIMIT_EVENT";
case WMI_FW_READY_EVENTID:
return "WMI_FW_READY_EVENT";
case WMI_TRAFFIC_RESUME_EVENTID:
return "WMI_TRAFFIC_RESUME_EVENT";
case WMI_TOF_GET_TX_RX_OFFSET_EVENTID:
return "WMI_TOF_GET_TX_RX_OFFSET_EVENT";
case WMI_TOF_SET_TX_RX_OFFSET_EVENTID:
return "WMI_TOF_SET_TX_RX_OFFSET_EVENT";
case WMI_VRING_CFG_DONE_EVENTID:
return "WMI_VRING_CFG_DONE_EVENT";
case WMI_READY_EVENTID:
return "WMI_READY_EVENT";
case WMI_RX_MGMT_PACKET_EVENTID:
return "WMI_RX_MGMT_PACKET_EVENT";
case WMI_TX_MGMT_PACKET_EVENTID:
return "WMI_TX_MGMT_PACKET_EVENT";
case WMI_SCAN_COMPLETE_EVENTID:
return "WMI_SCAN_COMPLETE_EVENT";
case WMI_ACS_PASSIVE_SCAN_COMPLETE_EVENTID:
return "WMI_ACS_PASSIVE_SCAN_COMPLETE_EVENT";
case WMI_CONNECT_EVENTID:
return "WMI_CONNECT_EVENT";
case WMI_EAPOL_RX_EVENTID:
return "WMI_EAPOL_RX_EVENT";
case WMI_BA_STATUS_EVENTID:
return "WMI_BA_STATUS_EVENT";
case WMI_RCP_ADDBA_REQ_EVENTID:
return "WMI_RCP_ADDBA_REQ_EVENT";
case WMI_DELBA_EVENTID:
return "WMI_DELBA_EVENT";
case WMI_VRING_EN_EVENTID:
return "WMI_VRING_EN_EVENT";
case WMI_DATA_PORT_OPEN_EVENTID:
return "WMI_DATA_PORT_OPEN_EVENT";
case WMI_AOA_MEAS_EVENTID:
return "WMI_AOA_MEAS_EVENT";
case WMI_TOF_SESSION_END_EVENTID:
return "WMI_TOF_SESSION_END_EVENT";
case WMI_TOF_GET_CAPABILITIES_EVENTID:
return "WMI_TOF_GET_CAPABILITIES_EVENT";
case WMI_TOF_SET_LCR_EVENTID:
return "WMI_TOF_SET_LCR_EVENT";
case WMI_TOF_SET_LCI_EVENTID:
return "WMI_TOF_SET_LCI_EVENT";
case WMI_TOF_FTM_PER_DEST_RES_EVENTID:
return "WMI_TOF_FTM_PER_DEST_RES_EVENT";
case WMI_TOF_CHANNEL_INFO_EVENTID:
return "WMI_TOF_CHANNEL_INFO_EVENT";
case WMI_TRAFFIC_SUSPEND_EVENTID:
return "WMI_TRAFFIC_SUSPEND_EVENT";
case WMI_ECHO_RSP_EVENTID:
return "WMI_ECHO_RSP_EVENT";
case WMI_LED_CFG_DONE_EVENTID:
return "WMI_LED_CFG_DONE_EVENT";
case WMI_PCP_STARTED_EVENTID:
return "WMI_PCP_STARTED_EVENT";
case WMI_PCP_STOPPED_EVENTID:
return "WMI_PCP_STOPPED_EVENT";
case WMI_GET_SSID_EVENTID:
return "WMI_GET_SSID_EVENT";
case WMI_GET_PCP_CHANNEL_EVENTID:
return "WMI_GET_PCP_CHANNEL_EVENT";
case WMI_P2P_CFG_DONE_EVENTID:
return "WMI_P2P_CFG_DONE_EVENT";
case WMI_LISTEN_STARTED_EVENTID:
return "WMI_LISTEN_STARTED_EVENT";
case WMI_SEARCH_STARTED_EVENTID:
return "WMI_SEARCH_STARTED_EVENT";
case WMI_DISCOVERY_STOPPED_EVENTID:
return "WMI_DISCOVERY_STOPPED_EVENT";
case WMI_CFG_RX_CHAIN_DONE_EVENTID:
return "WMI_CFG_RX_CHAIN_DONE_EVENT";
case WMI_TEMP_SENSE_DONE_EVENTID:
return "WMI_TEMP_SENSE_DONE_EVENT";
case WMI_RCP_ADDBA_RESP_SENT_EVENTID:
return "WMI_RCP_ADDBA_RESP_SENT_EVENT";
case WMI_PS_DEV_PROFILE_CFG_EVENTID:
return "WMI_PS_DEV_PROFILE_CFG_EVENT";
case WMI_SET_MGMT_RETRY_LIMIT_EVENTID:
return "WMI_SET_MGMT_RETRY_LIMIT_EVENT";
case WMI_GET_MGMT_RETRY_LIMIT_EVENTID:
return "WMI_GET_MGMT_RETRY_LIMIT_EVENT";
case WMI_SET_THERMAL_THROTTLING_CFG_EVENTID:
return "WMI_SET_THERMAL_THROTTLING_CFG_EVENT";
case WMI_GET_THERMAL_THROTTLING_CFG_EVENTID:
return "WMI_GET_THERMAL_THROTTLING_CFG_EVENT";
case WMI_LINK_MAINTAIN_CFG_WRITE_DONE_EVENTID:
return "WMI_LINK_MAINTAIN_CFG_WRITE_DONE_EVENT";
case WMI_LO_POWER_CALIB_FROM_OTP_EVENTID:
return "WMI_LO_POWER_CALIB_FROM_OTP_EVENT";
default:
return "Untracked EVENT";
}
}
static int __wmi_send(struct wil6210_priv *wil, u16 cmdid, void *buf, u16 len)
{
struct {
......@@ -222,7 +457,7 @@ static int __wmi_send(struct wil6210_priv *wil, u16 cmdid, void *buf, u16 len)
uint retry;
int rc = 0;
if (sizeof(cmd) + len > r->entry_size) {
if (len > r->entry_size - sizeof(cmd)) {
wil_err(wil, "WMI size too large: %d bytes, max is %d\n",
(int)(sizeof(cmd) + len), r->entry_size);
return -ERANGE;
......@@ -294,7 +529,8 @@ static int __wmi_send(struct wil6210_priv *wil, u16 cmdid, void *buf, u16 len)
}
cmd.hdr.seq = cpu_to_le16(++wil->wmi_seq);
/* set command */
wil_dbg_wmi(wil, "WMI command 0x%04x [%d]\n", cmdid, len);
wil_dbg_wmi(wil, "sending %s (0x%04x) [%d]\n",
cmdid2name(cmdid), cmdid, len);
wil_hex_dump_wmi("Cmd ", DUMP_PREFIX_OFFSET, 16, 1, &cmd,
sizeof(cmd), true);
wil_hex_dump_wmi("cmd ", DUMP_PREFIX_OFFSET, 16, 1, buf,
......@@ -963,8 +1199,8 @@ void wmi_recv_cmd(struct wil6210_priv *wil)
}
spin_unlock_irqrestore(&wil->wmi_ev_lock, flags);
wil_dbg_wmi(wil, "WMI event 0x%04x MID %d @%d msec\n",
id, wmi->mid, tstamp);
wil_dbg_wmi(wil, "recv %s (0x%04x) MID %d @%d msec\n",
eventid2name(id), id, wmi->mid, tstamp);
trace_wil6210_wmi_event(wmi, &wmi[1],
len - sizeof(*wmi));
}
......@@ -1380,8 +1616,14 @@ int wmi_set_ie(struct wil6210_priv *wil, u8 type, u16 ie_len, const void *ie)
};
int rc;
u16 len = sizeof(struct wmi_set_appie_cmd) + ie_len;
struct wmi_set_appie_cmd *cmd = kzalloc(len, GFP_KERNEL);
struct wmi_set_appie_cmd *cmd;
if (len < ie_len) {
rc = -EINVAL;
goto out;
}
cmd = kzalloc(len, GFP_KERNEL);
if (!cmd) {
rc = -ENOMEM;
goto out;
......@@ -1801,6 +2043,16 @@ void wmi_event_flush(struct wil6210_priv *wil)
spin_unlock_irqrestore(&wil->wmi_ev_lock, flags);
}
static const char *suspend_status2name(u8 status)
{
switch (status) {
case WMI_TRAFFIC_SUSPEND_REJECTED_LINK_NOT_IDLE:
return "LINK_NOT_IDLE";
default:
return "Untracked status";
}
}
int wmi_suspend(struct wil6210_priv *wil)
{
int rc;
......@@ -1816,7 +2068,7 @@ int wmi_suspend(struct wil6210_priv *wil)
wil->suspend_resp_rcvd = false;
wil->suspend_resp_comp = false;
reply.evt.status = WMI_TRAFFIC_SUSPEND_REJECTED;
reply.evt.status = WMI_TRAFFIC_SUSPEND_REJECTED_LINK_NOT_IDLE;
rc = wmi_call(wil, WMI_TRAFFIC_SUSPEND_CMDID, &cmd, sizeof(cmd),
WMI_TRAFFIC_SUSPEND_EVENTID, &reply, sizeof(reply),
......@@ -1848,8 +2100,9 @@ int wmi_suspend(struct wil6210_priv *wil)
}
wil_dbg_wmi(wil, "suspend_response_completed rcvd\n");
if (reply.evt.status == WMI_TRAFFIC_SUSPEND_REJECTED) {
wil_dbg_pm(wil, "device rejected the suspend\n");
if (reply.evt.status != WMI_TRAFFIC_SUSPEND_APPROVED) {
wil_dbg_pm(wil, "device rejected the suspend, %s\n",
suspend_status2name(reply.evt.status));
wil->suspend_stats.rejected_by_device++;
}
rc = reply.evt.status;
......@@ -1861,21 +2114,50 @@ int wmi_suspend(struct wil6210_priv *wil)
return rc;
}
static void resume_triggers2string(u32 triggers, char *string, int str_size)
{
string[0] = '\0';
if (!triggers) {
strlcat(string, " UNKNOWN", str_size);
return;
}
if (triggers & WMI_RESUME_TRIGGER_HOST)
strlcat(string, " HOST", str_size);
if (triggers & WMI_RESUME_TRIGGER_UCAST_RX)
strlcat(string, " UCAST_RX", str_size);
if (triggers & WMI_RESUME_TRIGGER_BCAST_RX)
strlcat(string, " BCAST_RX", str_size);
if (triggers & WMI_RESUME_TRIGGER_WMI_EVT)
strlcat(string, " WMI_EVT", str_size);
}
int wmi_resume(struct wil6210_priv *wil)
{
int rc;
char string[100];
struct {
struct wmi_cmd_hdr wmi;
struct wmi_traffic_resume_event evt;
} __packed reply;
reply.evt.status = WMI_TRAFFIC_RESUME_FAILED;
reply.evt.resume_triggers = WMI_RESUME_TRIGGER_UNKNOWN;
rc = wmi_call(wil, WMI_TRAFFIC_RESUME_CMDID, NULL, 0,
WMI_TRAFFIC_RESUME_EVENTID, &reply, sizeof(reply),
WIL_WAIT_FOR_SUSPEND_RESUME_COMP);
if (rc)
return rc;
resume_triggers2string(le32_to_cpu(reply.evt.resume_triggers), string,
sizeof(string));
wil_dbg_pm(wil, "device resume %s, resume triggers:%s (0x%x)\n",
reply.evt.status ? "failed" : "passed", string,
le32_to_cpu(reply.evt.resume_triggers));
return reply.evt.status;
}
......@@ -1906,8 +2188,8 @@ static void wmi_event_handle(struct wil6210_priv *wil,
void *evt_data = (void *)(&wmi[1]);
u16 id = le16_to_cpu(wmi->command_id);
wil_dbg_wmi(wil, "Handle WMI 0x%04x (reply_id 0x%04x)\n",
id, wil->reply_id);
wil_dbg_wmi(wil, "Handle %s (0x%04x) (reply_id 0x%04x)\n",
eventid2name(id), id, wil->reply_id);
/* check if someone waits for this event */
if (wil->reply_id && wil->reply_id == id) {
WARN_ON(wil->reply_buf);
......
......@@ -2267,8 +2267,8 @@ struct wmi_link_maintain_cfg_read_done_event {
} __packed;
enum wmi_traffic_suspend_status {
WMI_TRAFFIC_SUSPEND_APPROVED = 0x0,
WMI_TRAFFIC_SUSPEND_REJECTED = 0x1,
WMI_TRAFFIC_SUSPEND_APPROVED = 0x0,
WMI_TRAFFIC_SUSPEND_REJECTED_LINK_NOT_IDLE = 0x1,
};
/* WMI_TRAFFIC_SUSPEND_EVENTID */
......@@ -2282,10 +2282,21 @@ enum wmi_traffic_resume_status {
WMI_TRAFFIC_RESUME_FAILED = 0x1,
};
enum wmi_resume_trigger {
WMI_RESUME_TRIGGER_UNKNOWN = 0x0,
WMI_RESUME_TRIGGER_HOST = 0x1,
WMI_RESUME_TRIGGER_UCAST_RX = 0x2,
WMI_RESUME_TRIGGER_BCAST_RX = 0x4,
WMI_RESUME_TRIGGER_WMI_EVT = 0x8,
};
/* WMI_TRAFFIC_RESUME_EVENTID */
struct wmi_traffic_resume_event {
/* enum wmi_traffic_resume_status_e */
/* enum wmi_traffic_resume_status */
u8 status;
u8 reserved[3];
/* enum wmi_resume_trigger bitmap */
__le32 resume_triggers;
} __packed;
/* Power Save command completion status codes */
......
......@@ -137,27 +137,27 @@ int brcmf_sdiod_intr_register(struct brcmf_sdio_dev *sdiodev)
if (sdiodev->bus_if->chip == BRCM_CC_43362_CHIP_ID) {
/* assign GPIO to SDIO core */
addr = CORE_CC_REG(SI_ENUM_BASE, gpiocontrol);
gpiocontrol = brcmf_sdiod_regrl(sdiodev, addr, &ret);
gpiocontrol = brcmf_sdiod_readl(sdiodev, addr, &ret);
gpiocontrol |= 0x2;
brcmf_sdiod_regwl(sdiodev, addr, gpiocontrol, &ret);
brcmf_sdiod_writel(sdiodev, addr, gpiocontrol, &ret);
brcmf_sdiod_regwb(sdiodev, SBSDIO_GPIO_SELECT, 0xf,
&ret);
brcmf_sdiod_regwb(sdiodev, SBSDIO_GPIO_OUT, 0, &ret);
brcmf_sdiod_regwb(sdiodev, SBSDIO_GPIO_EN, 0x2, &ret);
brcmf_sdiod_writeb(sdiodev, SBSDIO_GPIO_SELECT,
0xf, &ret);
brcmf_sdiod_writeb(sdiodev, SBSDIO_GPIO_OUT, 0, &ret);
brcmf_sdiod_writeb(sdiodev, SBSDIO_GPIO_EN, 0x2, &ret);
}
/* must configure SDIO_CCCR_IENx to enable irq */
data = brcmf_sdiod_regrb(sdiodev, SDIO_CCCR_IENx, &ret);
data = brcmf_sdiod_func0_rb(sdiodev, SDIO_CCCR_IENx, &ret);
data |= 1 << SDIO_FUNC_1 | 1 << SDIO_FUNC_2 | 1;
brcmf_sdiod_regwb(sdiodev, SDIO_CCCR_IENx, data, &ret);
brcmf_sdiod_func0_wb(sdiodev, SDIO_CCCR_IENx, data, &ret);
/* redirect, configure and enable io for interrupt signal */
data = SDIO_SEPINT_MASK | SDIO_SEPINT_OE;
data = SDIO_CCCR_BRCM_SEPINT_MASK | SDIO_CCCR_BRCM_SEPINT_OE;
if (pdata->oob_irq_flags & IRQF_TRIGGER_HIGH)
data |= SDIO_SEPINT_ACT_HI;
brcmf_sdiod_regwb(sdiodev, SDIO_CCCR_BRCM_SEPINT, data, &ret);
data |= SDIO_CCCR_BRCM_SEPINT_ACT_HI;
brcmf_sdiod_func0_wb(sdiodev, SDIO_CCCR_BRCM_SEPINT,
data, &ret);
sdio_release_host(sdiodev->func[1]);
} else {
brcmf_dbg(SDIO, "Entering\n");
......@@ -183,8 +183,8 @@ void brcmf_sdiod_intr_unregister(struct brcmf_sdio_dev *sdiodev)
pdata = &sdiodev->settings->bus.sdio;
sdio_claim_host(sdiodev->func[1]);
brcmf_sdiod_regwb(sdiodev, SDIO_CCCR_BRCM_SEPINT, 0, NULL);
brcmf_sdiod_regwb(sdiodev, SDIO_CCCR_IENx, 0, NULL);
brcmf_sdiod_func0_wb(sdiodev, SDIO_CCCR_BRCM_SEPINT, 0, NULL);
brcmf_sdiod_func0_wb(sdiodev, SDIO_CCCR_IENx, 0, NULL);
sdio_release_host(sdiodev->func[1]);
sdiodev->oob_irq_requested = false;
......@@ -230,244 +230,93 @@ void brcmf_sdiod_change_state(struct brcmf_sdio_dev *sdiodev,
sdiodev->state = state;
}
static inline int brcmf_sdiod_f0_writeb(struct sdio_func *func,
uint regaddr, u8 byte)
{
int err_ret;
/*
* Can only directly write to some F0 registers.
* Handle CCCR_IENx and CCCR_ABORT command
* as a special case.
*/
if ((regaddr == SDIO_CCCR_ABORT) ||
(regaddr == SDIO_CCCR_IENx))
sdio_writeb(func, byte, regaddr, &err_ret);
else
sdio_f0_writeb(func, byte, regaddr, &err_ret);
return err_ret;
}
static int brcmf_sdiod_request_data(struct brcmf_sdio_dev *sdiodev, u8 fn,
u32 addr, u8 regsz, void *data, bool write)
{
struct sdio_func *func;
int ret = -EINVAL;
brcmf_dbg(SDIO, "rw=%d, func=%d, addr=0x%05x, nbytes=%d\n",
write, fn, addr, regsz);
/* only allow byte access on F0 */
if (WARN_ON(regsz > 1 && !fn))
return -EINVAL;
func = sdiodev->func[fn];
switch (regsz) {
case sizeof(u8):
if (write) {
if (fn)
sdio_writeb(func, *(u8 *)data, addr, &ret);
else
ret = brcmf_sdiod_f0_writeb(func, addr,
*(u8 *)data);
} else {
if (fn)
*(u8 *)data = sdio_readb(func, addr, &ret);
else
*(u8 *)data = sdio_f0_readb(func, addr, &ret);
}
break;
case sizeof(u16):
if (write)
sdio_writew(func, *(u16 *)data, addr, &ret);
else
*(u16 *)data = sdio_readw(func, addr, &ret);
break;
case sizeof(u32):
if (write)
sdio_writel(func, *(u32 *)data, addr, &ret);
else
*(u32 *)data = sdio_readl(func, addr, &ret);
break;
default:
brcmf_err("invalid size: %d\n", regsz);
break;
}
if (ret)
brcmf_dbg(SDIO, "failed to %s data F%d@0x%05x, err: %d\n",
write ? "write" : "read", fn, addr, ret);
return ret;
}
static int brcmf_sdiod_regrw_helper(struct brcmf_sdio_dev *sdiodev, u32 addr,
u8 regsz, void *data, bool write)
{
u8 func;
s32 retry = 0;
int ret;
if (sdiodev->state == BRCMF_SDIOD_NOMEDIUM)
return -ENOMEDIUM;
/*
* figure out how to read the register based on address range
* 0x00 ~ 0x7FF: function 0 CCCR and FBR
* 0x10000 ~ 0x1FFFF: function 1 miscellaneous registers
* The rest: function 1 silicon backplane core registers
*/
if ((addr & ~REG_F0_REG_MASK) == 0)
func = SDIO_FUNC_0;
else
func = SDIO_FUNC_1;
do {
if (!write)
memset(data, 0, regsz);
/* for retry wait for 1 ms till bus get settled down */
if (retry)
usleep_range(1000, 2000);
ret = brcmf_sdiod_request_data(sdiodev, func, addr, regsz,
data, write);
} while (ret != 0 && ret != -ENOMEDIUM &&
retry++ < SDIOH_API_ACCESS_RETRY_LIMIT);
if (ret == -ENOMEDIUM)
brcmf_sdiod_change_state(sdiodev, BRCMF_SDIOD_NOMEDIUM);
else if (ret != 0) {
/*
* SleepCSR register access can fail when
* waking up the device so reduce this noise
* in the logs.
*/
if (addr != SBSDIO_FUNC1_SLEEPCSR)
brcmf_err("failed to %s data F%d@0x%05x, err: %d\n",
write ? "write" : "read", func, addr, ret);
else
brcmf_dbg(SDIO, "failed to %s data F%d@0x%05x, err: %d\n",
write ? "write" : "read", func, addr, ret);
}
return ret;
}
static int
brcmf_sdiod_set_sbaddr_window(struct brcmf_sdio_dev *sdiodev, u32 address)
static int brcmf_sdiod_set_backplane_window(struct brcmf_sdio_dev *sdiodev,
u32 addr)
{
u32 v, bar0 = addr & SBSDIO_SBWINDOW_MASK;
int err = 0, i;
u8 addr[3];
if (sdiodev->state == BRCMF_SDIOD_NOMEDIUM)
return -ENOMEDIUM;
addr[0] = (address >> 8) & SBSDIO_SBADDRLOW_MASK;
addr[1] = (address >> 16) & SBSDIO_SBADDRMID_MASK;
addr[2] = (address >> 24) & SBSDIO_SBADDRHIGH_MASK;
for (i = 0; i < 3; i++) {
err = brcmf_sdiod_regrw_helper(sdiodev,
SBSDIO_FUNC1_SBADDRLOW + i,
sizeof(u8), &addr[i], true);
if (err) {
brcmf_err("failed at addr: 0x%0x\n",
SBSDIO_FUNC1_SBADDRLOW + i);
break;
}
}
return err;
}
if (bar0 == sdiodev->sbwad)
return 0;
static int
brcmf_sdiod_addrprep(struct brcmf_sdio_dev *sdiodev, uint width, u32 *addr)
{
uint bar0 = *addr & ~SBSDIO_SB_OFT_ADDR_MASK;
int err = 0;
v = bar0 >> 8;
if (bar0 != sdiodev->sbwad) {
err = brcmf_sdiod_set_sbaddr_window(sdiodev, bar0);
if (err)
return err;
for (i = 0 ; i < 3 && !err ; i++, v >>= 8)
brcmf_sdiod_writeb(sdiodev, SBSDIO_FUNC1_SBADDRLOW + i,
v & 0xff, &err);
if (!err)
sdiodev->sbwad = bar0;
}
*addr &= SBSDIO_SB_OFT_ADDR_MASK;
if (width == 4)
*addr |= SBSDIO_SB_ACCESS_2_4B_FLAG;
return 0;
return err;
}
u8 brcmf_sdiod_regrb(struct brcmf_sdio_dev *sdiodev, u32 addr, int *ret)
u32 brcmf_sdiod_readl(struct brcmf_sdio_dev *sdiodev, u32 addr, int *ret)
{
u8 data;
u32 data = 0;
int retval;
brcmf_dbg(SDIO, "addr:0x%08x\n", addr);
retval = brcmf_sdiod_regrw_helper(sdiodev, addr, sizeof(data), &data,
false);
brcmf_dbg(SDIO, "data:0x%02x\n", data);
retval = brcmf_sdiod_set_backplane_window(sdiodev, addr);
if (retval)
goto out;
addr &= SBSDIO_SB_OFT_ADDR_MASK;
addr |= SBSDIO_SB_ACCESS_2_4B_FLAG;
data = sdio_readl(sdiodev->func[1], addr, &retval);
out:
if (ret)
*ret = retval;
return data;
}
u32 brcmf_sdiod_regrl(struct brcmf_sdio_dev *sdiodev, u32 addr, int *ret)
void brcmf_sdiod_writel(struct brcmf_sdio_dev *sdiodev, u32 addr,
u32 data, int *ret)
{
u32 data = 0;
int retval;
brcmf_dbg(SDIO, "addr:0x%08x\n", addr);
retval = brcmf_sdiod_addrprep(sdiodev, sizeof(data), &addr);
retval = brcmf_sdiod_set_backplane_window(sdiodev, addr);
if (retval)
goto done;
retval = brcmf_sdiod_regrw_helper(sdiodev, addr, sizeof(data), &data,
false);
brcmf_dbg(SDIO, "data:0x%08x\n", data);
done:
if (ret)
*ret = retval;
goto out;
return data;
}
addr &= SBSDIO_SB_OFT_ADDR_MASK;
addr |= SBSDIO_SB_ACCESS_2_4B_FLAG;
void brcmf_sdiod_regwb(struct brcmf_sdio_dev *sdiodev, u32 addr,
u8 data, int *ret)
{
int retval;
sdio_writel(sdiodev->func[1], data, addr, &retval);
brcmf_dbg(SDIO, "addr:0x%08x, data:0x%02x\n", addr, data);
retval = brcmf_sdiod_regrw_helper(sdiodev, addr, sizeof(data), &data,
true);
out:
if (ret)
*ret = retval;
}
void brcmf_sdiod_regwl(struct brcmf_sdio_dev *sdiodev, u32 addr,
u32 data, int *ret)
static int brcmf_sdiod_buff_read(struct brcmf_sdio_dev *sdiodev, uint fn,
u32 addr, struct sk_buff *pkt)
{
int retval;
unsigned int req_sz;
int err;
brcmf_dbg(SDIO, "addr:0x%08x, data:0x%08x\n", addr, data);
retval = brcmf_sdiod_addrprep(sdiodev, sizeof(data), &addr);
if (retval)
goto done;
retval = brcmf_sdiod_regrw_helper(sdiodev, addr, sizeof(data), &data,
true);
/* Single skb use the standard mmc interface */
req_sz = pkt->len + 3;
req_sz &= (uint)~3;
done:
if (ret)
*ret = retval;
if (fn == 1)
err = sdio_memcpy_fromio(sdiodev->func[fn],
((u8 *)(pkt->data)), addr, req_sz);
else
/* function 2 read is FIFO operation */
err = sdio_readsb(sdiodev->func[fn],
((u8 *)(pkt->data)), addr, req_sz);
if (err == -ENOMEDIUM)
brcmf_sdiod_change_state(sdiodev, BRCMF_SDIOD_NOMEDIUM);
return err;
}
static int brcmf_sdiod_buffrw(struct brcmf_sdio_dev *sdiodev, uint fn,
bool write, u32 addr, struct sk_buff *pkt)
static int brcmf_sdiod_buff_write(struct brcmf_sdio_dev *sdiodev, uint fn,
u32 addr, struct sk_buff *pkt)
{
unsigned int req_sz;
int err;
......@@ -476,18 +325,12 @@ static int brcmf_sdiod_buffrw(struct brcmf_sdio_dev *sdiodev, uint fn,
req_sz = pkt->len + 3;
req_sz &= (uint)~3;
if (write)
err = sdio_memcpy_toio(sdiodev->func[fn], addr,
((u8 *)(pkt->data)), req_sz);
else if (fn == 1)
err = sdio_memcpy_fromio(sdiodev->func[fn], ((u8 *)(pkt->data)),
addr, req_sz);
else
/* function 2 read is FIFO operation */
err = sdio_readsb(sdiodev->func[fn], ((u8 *)(pkt->data)), addr,
req_sz);
err = sdio_memcpy_toio(sdiodev->func[fn], addr,
((u8 *)(pkt->data)), req_sz);
if (err == -ENOMEDIUM)
brcmf_sdiod_change_state(sdiodev, BRCMF_SDIOD_NOMEDIUM);
return err;
}
......@@ -691,11 +534,14 @@ int brcmf_sdiod_recv_pkt(struct brcmf_sdio_dev *sdiodev, struct sk_buff *pkt)
brcmf_dbg(SDIO, "addr = 0x%x, size = %d\n", addr, pkt->len);
err = brcmf_sdiod_addrprep(sdiodev, 4, &addr);
err = brcmf_sdiod_set_backplane_window(sdiodev, addr);
if (err)
goto done;
err = brcmf_sdiod_buffrw(sdiodev, SDIO_FUNC_2, false, addr, pkt);
addr &= SBSDIO_SB_OFT_ADDR_MASK;
addr |= SBSDIO_SB_ACCESS_2_4B_FLAG;
err = brcmf_sdiod_buff_read(sdiodev, SDIO_FUNC_2, addr, pkt);
done:
return err;
......@@ -712,19 +558,22 @@ int brcmf_sdiod_recv_chain(struct brcmf_sdio_dev *sdiodev,
brcmf_dbg(SDIO, "addr = 0x%x, size = %d\n",
addr, pktq->qlen);
err = brcmf_sdiod_addrprep(sdiodev, 4, &addr);
err = brcmf_sdiod_set_backplane_window(sdiodev, addr);
if (err)
goto done;
addr &= SBSDIO_SB_OFT_ADDR_MASK;
addr |= SBSDIO_SB_ACCESS_2_4B_FLAG;
if (pktq->qlen == 1)
err = brcmf_sdiod_buffrw(sdiodev, SDIO_FUNC_2, false, addr,
pktq->next);
err = brcmf_sdiod_buff_read(sdiodev, SDIO_FUNC_2, addr,
pktq->next);
else if (!sdiodev->sg_support) {
glom_skb = brcmu_pkt_buf_get_skb(totlen);
if (!glom_skb)
return -ENOMEM;
err = brcmf_sdiod_buffrw(sdiodev, SDIO_FUNC_2, false, addr,
glom_skb);
err = brcmf_sdiod_buff_read(sdiodev, SDIO_FUNC_2, addr,
glom_skb);
if (err)
goto done;
......@@ -748,6 +597,7 @@ int brcmf_sdiod_send_buf(struct brcmf_sdio_dev *sdiodev, u8 *buf, uint nbytes)
int err;
mypkt = brcmu_pkt_buf_get_skb(nbytes);
if (!mypkt) {
brcmf_err("brcmu_pkt_buf_get_skb failed: len %d\n",
nbytes);
......@@ -756,15 +606,19 @@ int brcmf_sdiod_send_buf(struct brcmf_sdio_dev *sdiodev, u8 *buf, uint nbytes)
memcpy(mypkt->data, buf, nbytes);
err = brcmf_sdiod_addrprep(sdiodev, 4, &addr);
err = brcmf_sdiod_set_backplane_window(sdiodev, addr);
if (err)
return err;
addr &= SBSDIO_SB_OFT_ADDR_MASK;
addr |= SBSDIO_SB_ACCESS_2_4B_FLAG;
if (!err)
err = brcmf_sdiod_buffrw(sdiodev, SDIO_FUNC_2, true, addr,
mypkt);
err = brcmf_sdiod_buff_write(sdiodev, SDIO_FUNC_2, addr, mypkt);
brcmu_pkt_buf_free_skb(mypkt);
return err;
return err;
}
int brcmf_sdiod_send_pkt(struct brcmf_sdio_dev *sdiodev,
......@@ -776,20 +630,24 @@ int brcmf_sdiod_send_pkt(struct brcmf_sdio_dev *sdiodev,
brcmf_dbg(SDIO, "addr = 0x%x, size = %d\n", addr, pktq->qlen);
err = brcmf_sdiod_addrprep(sdiodev, 4, &addr);
err = brcmf_sdiod_set_backplane_window(sdiodev, addr);
if (err)
return err;
if (pktq->qlen == 1 || !sdiodev->sg_support)
addr &= SBSDIO_SB_OFT_ADDR_MASK;
addr |= SBSDIO_SB_ACCESS_2_4B_FLAG;
if (pktq->qlen == 1 || !sdiodev->sg_support) {
skb_queue_walk(pktq, skb) {
err = brcmf_sdiod_buffrw(sdiodev, SDIO_FUNC_2, true,
addr, skb);
err = brcmf_sdiod_buff_write(sdiodev, SDIO_FUNC_2,
addr, skb);
if (err)
break;
}
else
} else {
err = brcmf_sdiod_sglist_rw(sdiodev, SDIO_FUNC_2, true, addr,
pktq);
}
return err;
}
......@@ -798,7 +656,7 @@ int
brcmf_sdiod_ramrw(struct brcmf_sdio_dev *sdiodev, bool write, u32 address,
u8 *data, uint size)
{
int bcmerror = 0;
int err = 0;
struct sk_buff *pkt;
u32 sdaddr;
uint dsize;
......@@ -823,8 +681,8 @@ brcmf_sdiod_ramrw(struct brcmf_sdio_dev *sdiodev, bool write, u32 address,
/* Do the transfer(s) */
while (size) {
/* Set the backplane window to include the start address */
bcmerror = brcmf_sdiod_set_sbaddr_window(sdiodev, address);
if (bcmerror)
err = brcmf_sdiod_set_backplane_window(sdiodev, address);
if (err)
break;
brcmf_dbg(SDIO, "%s %d bytes at offset 0x%08x in window 0x%08x\n",
......@@ -835,11 +693,17 @@ brcmf_sdiod_ramrw(struct brcmf_sdio_dev *sdiodev, bool write, u32 address,
sdaddr |= SBSDIO_SB_ACCESS_2_4B_FLAG;
skb_put(pkt, dsize);
if (write)
if (write) {
memcpy(pkt->data, data, dsize);
bcmerror = brcmf_sdiod_buffrw(sdiodev, SDIO_FUNC_1, write,
sdaddr, pkt);
if (bcmerror) {
err = brcmf_sdiod_buff_write(sdiodev, SDIO_FUNC_1,
sdaddr, pkt);
} else {
err = brcmf_sdiod_buff_read(sdiodev, SDIO_FUNC_1,
sdaddr, pkt);
}
if (err) {
brcmf_err("membytes transfer failed\n");
break;
}
......@@ -859,24 +723,17 @@ brcmf_sdiod_ramrw(struct brcmf_sdio_dev *sdiodev, bool write, u32 address,
dev_kfree_skb(pkt);
/* Return the window to backplane enumeration space for core access */
if (brcmf_sdiod_set_sbaddr_window(sdiodev, sdiodev->sbwad))
brcmf_err("FAILED to set window back to 0x%x\n",
sdiodev->sbwad);
sdio_release_host(sdiodev->func[1]);
return bcmerror;
return err;
}
int brcmf_sdiod_abort(struct brcmf_sdio_dev *sdiodev, uint fn)
int brcmf_sdiod_abort(struct brcmf_sdio_dev *sdiodev, u8 fn)
{
char t_func = (char)fn;
brcmf_dbg(SDIO, "Enter\n");
/* issue abort cmd52 command through F0 */
brcmf_sdiod_request_data(sdiodev, SDIO_FUNC_0, SDIO_CCCR_ABORT,
sizeof(t_func), &t_func, true);
/* Issue abort cmd52 command through F0 */
brcmf_sdiod_func0_wb(sdiodev, SDIO_CCCR_ABORT, fn, NULL);
brcmf_dbg(SDIO, "Exit\n");
return 0;
......
......@@ -130,13 +130,19 @@ static void brcmf_feat_iovar_data_set(struct brcmf_if *ifp,
}
}
#define MAX_CAPS_BUFFER_SIZE 512
static void brcmf_feat_firmware_capabilities(struct brcmf_if *ifp)
{
char caps[256];
char caps[MAX_CAPS_BUFFER_SIZE];
enum brcmf_feat_id id;
int i;
int i, err;
err = brcmf_fil_iovar_data_get(ifp, "cap", caps, sizeof(caps));
if (err) {
brcmf_err("could not get firmware cap (%d)\n", err);
return;
}
brcmf_fil_iovar_data_get(ifp, "cap", caps, sizeof(caps));
brcmf_dbg(INFO, "[ %s]\n", caps);
for (i = 0; i < ARRAY_SIZE(brcmf_fwcap_map); i++) {
......
......@@ -50,17 +50,19 @@
#define SBSDIO_NUM_FUNCTION 3
/* function 0 vendor specific CCCR registers */
#define SDIO_CCCR_BRCM_CARDCAP 0xf0
#define SDIO_CCCR_BRCM_CARDCAP_CMD14_SUPPORT 0x02
#define SDIO_CCCR_BRCM_CARDCAP_CMD14_EXT 0x04
#define SDIO_CCCR_BRCM_CARDCAP_CMD_NODEC 0x08
#define SDIO_CCCR_BRCM_CARDCTRL 0xf1
#define SDIO_CCCR_BRCM_CARDCTRL_WLANRESET 0x02
#define SDIO_CCCR_BRCM_SEPINT 0xf2
#define SDIO_CCCR_BRCM_CARDCAP_CMD14_SUPPORT BIT(1)
#define SDIO_CCCR_BRCM_CARDCAP_CMD14_EXT BIT(2)
#define SDIO_CCCR_BRCM_CARDCAP_CMD_NODEC BIT(3)
#define SDIO_SEPINT_MASK 0x01
#define SDIO_SEPINT_OE 0x02
#define SDIO_SEPINT_ACT_HI 0x04
#define SDIO_CCCR_BRCM_CARDCTRL 0xf1
#define SDIO_CCCR_BRCM_CARDCTRL_WLANRESET BIT(1)
#define SDIO_CCCR_BRCM_SEPINT 0xf2
#define SDIO_CCCR_BRCM_SEPINT_MASK BIT(0)
#define SDIO_CCCR_BRCM_SEPINT_OE BIT(1)
#define SDIO_CCCR_BRCM_SEPINT_ACT_HI BIT(2)
/* function 1 miscellaneous registers */
......@@ -131,11 +133,6 @@
/* with b15, maps to 32-bit SB access */
#define SBSDIO_SB_ACCESS_2_4B_FLAG 0x08000
/* valid bits in SBSDIO_FUNC1_SBADDRxxx regs */
#define SBSDIO_SBADDRLOW_MASK 0x80 /* Valid bits in SBADDRLOW */
#define SBSDIO_SBADDRMID_MASK 0xff /* Valid bits in SBADDRMID */
#define SBSDIO_SBADDRHIGH_MASK 0xffU /* Valid bits in SBADDRHIGH */
/* Address bits from SBADDR regs */
#define SBSDIO_SBWINDOW_MASK 0xffff8000
......@@ -296,13 +293,24 @@ struct sdpcmd_regs {
int brcmf_sdiod_intr_register(struct brcmf_sdio_dev *sdiodev);
void brcmf_sdiod_intr_unregister(struct brcmf_sdio_dev *sdiodev);
/* sdio device register access interface */
u8 brcmf_sdiod_regrb(struct brcmf_sdio_dev *sdiodev, u32 addr, int *ret);
u32 brcmf_sdiod_regrl(struct brcmf_sdio_dev *sdiodev, u32 addr, int *ret);
void brcmf_sdiod_regwb(struct brcmf_sdio_dev *sdiodev, u32 addr, u8 data,
int *ret);
void brcmf_sdiod_regwl(struct brcmf_sdio_dev *sdiodev, u32 addr, u32 data,
int *ret);
/* SDIO device register access interface */
/* Accessors for SDIO Function 0 */
#define brcmf_sdiod_func0_rb(sdiodev, addr, r) \
sdio_readb((sdiodev)->func[0], (addr), (r))
#define brcmf_sdiod_func0_wb(sdiodev, addr, v, ret) \
sdio_writeb((sdiodev)->func[0], (v), (addr), (ret))
/* Accessors for SDIO Function 1 */
#define brcmf_sdiod_readb(sdiodev, addr, r) \
sdio_readb((sdiodev)->func[1], (addr), (r))
#define brcmf_sdiod_writeb(sdiodev, addr, v, ret) \
sdio_writeb((sdiodev)->func[1], (v), (addr), (ret))
u32 brcmf_sdiod_readl(struct brcmf_sdio_dev *sdiodev, u32 addr, int *ret);
void brcmf_sdiod_writel(struct brcmf_sdio_dev *sdiodev, u32 addr, u32 data,
int *ret);
/* Buffer transfer to/from device (client) core via cmd53.
* fn: function number
......@@ -342,7 +350,7 @@ int brcmf_sdiod_ramrw(struct brcmf_sdio_dev *sdiodev, bool write, u32 address,
u8 *data, uint size);
/* Issue an abort to the specified function */
int brcmf_sdiod_abort(struct brcmf_sdio_dev *sdiodev, uint fn);
int brcmf_sdiod_abort(struct brcmf_sdio_dev *sdiodev, u8 fn);
void brcmf_sdiod_sgtable_alloc(struct brcmf_sdio_dev *sdiodev);
void brcmf_sdiod_change_state(struct brcmf_sdio_dev *sdiodev,
enum brcmf_sdiod_state state);
......
......@@ -16049,8 +16049,7 @@ static void wlc_phy_workarounds_nphy_gainctrl(struct brcms_phy *pi)
wlc_phy_set_rfseq_nphy(pi, NPHY_RFSEQ_UPDATEGAINU,
rfseq_updategainu_events,
rfseq_updategainu_dlys,
sizeof(rfseq_updategainu_events) /
sizeof(rfseq_updategainu_events[0]));
ARRAY_SIZE(rfseq_updategainu_events));
mod_phy_reg(pi, 0x153, (0xff << 8), (90 << 8));
......
......@@ -9,7 +9,7 @@ iwlwifi-objs += iwl-phy-db.o iwl-nvm-parse.o
iwlwifi-objs += pcie/drv.o pcie/rx.o pcie/tx.o pcie/trans.o
iwlwifi-objs += pcie/ctxt-info.o pcie/trans-gen2.o pcie/tx-gen2.o
iwlwifi-$(CONFIG_IWLDVM) += cfg/1000.o cfg/2000.o cfg/5000.o cfg/6000.o
iwlwifi-$(CONFIG_IWLMVM) += cfg/7000.o cfg/8000.o cfg/9000.o cfg/a000.o
iwlwifi-$(CONFIG_IWLMVM) += cfg/7000.o cfg/8000.o cfg/9000.o cfg/22000.o
iwlwifi-objs += iwl-trans.o
iwlwifi-objs += fw/notif-wait.o
iwlwifi-$(CONFIG_IWLMVM) += fw/paging.o fw/smem.o fw/init.o fw/dbg.o
......
......@@ -70,8 +70,8 @@
#include "iwl-agn-hw.h"
/* Highest firmware API version supported */
#define IWL8000_UCODE_API_MAX 34
#define IWL8265_UCODE_API_MAX 34
#define IWL8000_UCODE_API_MAX 36
#define IWL8265_UCODE_API_MAX 36
/* Lowest firmware API version supported */
#define IWL8000_UCODE_API_MIN 22
......
......@@ -55,7 +55,7 @@
#include "iwl-agn-hw.h"
/* Highest firmware API version supported */
#define IWL9000_UCODE_API_MAX 34
#define IWL9000_UCODE_API_MAX 36
/* Lowest firmware API version supported */
#define IWL9000_UCODE_API_MIN 30
......
......@@ -95,8 +95,8 @@ enum {
#define IWL_ALIVE_FLG_RFKILL BIT(0)
struct iwl_lmac_alive {
__le32 ucode_minor;
__le32 ucode_major;
__le32 ucode_minor;
u8 ver_subtype;
u8 ver_type;
u8 mac;
......@@ -113,8 +113,8 @@ struct iwl_lmac_alive {
} __packed; /* UCODE_ALIVE_NTFY_API_S_VER_3 */
struct iwl_umac_alive {
__le32 umac_minor; /* UMAC version: minor */
__le32 umac_major; /* UMAC version: major */
__le32 umac_minor; /* UMAC version: minor */
__le32 error_info_addr; /* SRAM address for UMAC error log */
__le32 dbg_print_buff_addr;
} __packed; /* UMAC_ALIVE_DATA_API_S_VER_2 */
......
......@@ -188,11 +188,6 @@ enum iwl_bt_mxbox_dw3 {
BT_MBOX(3, UPDATE_REQUEST, 21, 1),
};
enum iwl_bt_mxbox_dw4 {
BT_MBOX(4, ATS_BT_INTERVAL, 0, 7),
BT_MBOX(4, ATS_BT_ACTIVE_MAX_TH, 7, 7),
};
#define BT_MBOX_MSG(_notif, _num, _field) \
((le32_to_cpu((_notif)->mbox_msg[(_num)]) & BT_MBOX##_num##_##_field)\
>> BT_MBOX##_num##_##_field##_POS)
......@@ -232,31 +227,6 @@ enum iwl_bt_ci_compliance {
* @reserved: reserved
*/
struct iwl_bt_coex_profile_notif {
__le32 mbox_msg[8];
__le32 msg_idx;
__le32 bt_ci_compliance;
__le32 primary_ch_lut;
__le32 secondary_ch_lut;
__le32 bt_activity_grading;
u8 ttc_status;
u8 rrc_status;
__le16 reserved;
} __packed; /* BT_COEX_PROFILE_NTFY_API_S_VER_5 */
/**
* struct iwl_bt_coex_profile_notif - notification about BT coex
* @mbox_msg: message from BT to WiFi
* @msg_idx: the index of the message
* @bt_ci_compliance: enum %iwl_bt_ci_compliance
* @primary_ch_lut: LUT used for primary channel &enum iwl_bt_coex_lut_type
* @secondary_ch_lut: LUT used for secondary channel &enum iwl_bt_coex_lut_type
* @bt_activity_grading: the activity of BT &enum iwl_bt_activity_grading
* @ttc_status: is TTC enabled - one bit per PHY
* @rrc_status: is RRC enabled - one bit per PHY
* @reserved: reserved
*/
struct iwl_bt_coex_profile_notif_v4 {
__le32 mbox_msg[4];
__le32 msg_idx;
__le32 bt_ci_compliance;
......
......@@ -215,7 +215,7 @@ enum iwl_legacy_cmds {
/**
* @SCD_QUEUE_CFG: &struct iwl_scd_txq_cfg_cmd for older hardware,
* &struct iwl_tx_queue_cfg_cmd with &struct iwl_tx_queue_cfg_rsp
* for newer (A000) hardware.
* for newer (22000) hardware.
*/
SCD_QUEUE_CFG = 0x1d,
......
......@@ -82,6 +82,21 @@ enum iwl_data_path_subcmd_ids {
*/
TRIGGER_RX_QUEUES_NOTIF_CMD = 0x2,
/**
* @TLC_MNG_CONFIG_CMD: &struct iwl_tlc_config_cmd
*/
TLC_MNG_CONFIG_CMD = 0xF,
/**
* @TLC_MNG_NOTIF_REQ_CMD: &struct iwl_tlc_notif_req_config_cmd
*/
TLC_MNG_NOTIF_REQ_CMD = 0x10,
/**
* @TLC_MNG_UPDATE_NOTIF: &struct iwl_tlc_update_notif
*/
TLC_MNG_UPDATE_NOTIF = 0xF7,
/**
* @STA_PM_NOTIF: &struct iwl_mvm_pm_state_notification
*/
......
......@@ -121,7 +121,7 @@ enum iwl_tx_flags {
}; /* TX_FLAGS_BITS_API_S_VER_1 */
/**
* enum iwl_tx_cmd_flags - bitmasks for tx_flags in TX command for a000
* enum iwl_tx_cmd_flags - bitmasks for tx_flags in TX command for 22000
* @IWL_TX_FLAGS_CMD_RATE: use rate from the TX command
* @IWL_TX_FLAGS_ENCRYPT_DIS: frame should not be encrypted, even if it belongs
* to a secured STA
......@@ -301,7 +301,7 @@ struct iwl_dram_sec_info {
} __packed; /* DRAM_SEC_INFO_API_S_VER_1 */
/**
* struct iwl_tx_cmd_gen2 - TX command struct to FW for a000 devices
* struct iwl_tx_cmd_gen2 - TX command struct to FW for 22000 devices
* ( TX_CMD = 0x1c )
* @len: in bytes of the payload, see below for details
* @offload_assist: TX offload configuration
......
......@@ -964,7 +964,20 @@ int iwl_fw_dbg_collect_desc(struct iwl_fw_runtime *fwrt,
if (trigger)
delay = msecs_to_jiffies(le32_to_cpu(trigger->stop_delay));
if (WARN(fwrt->trans->state == IWL_TRANS_NO_FW,
/*
* If the loading of the FW completed successfully, the next step is to
* get the SMEM config data. Thus, if fwrt->smem_cfg.num_lmacs is non
* zero, the FW was already loaded successully. If the state is "NO_FW"
* in such a case - WARN and exit, since FW may be dead. Otherwise, we
* can try to collect the data, since FW might just not be fully
* loaded (no "ALIVE" yet), and the debug data is accessible.
*
* Corner case: got the FW alive but crashed before getting the SMEM
* config. In such a case, due to HW access problems, we might
* collect garbage.
*/
if (WARN((fwrt->trans->state == IWL_TRANS_NO_FW) &&
fwrt->smem_cfg.num_lmacs,
"Can't collect dbg data when FW isn't alive\n"))
return -EIO;
......
......@@ -246,8 +246,6 @@ typedef unsigned int __bitwise iwl_ucode_tlv_api_t;
* @IWL_UCODE_TLV_API_STA_TYPE: This ucode supports station type assignement.
* @IWL_UCODE_TLV_API_NAN2_VER2: This ucode supports NAN API version 2
* @IWL_UCODE_TLV_API_NEW_RX_STATS: should new RX STATISTICS API be used
* @IWL_UCODE_TLV_API_ATS_COEX_EXTERNAL: the coex notification is enlared to
* include information about ACL time sharing.
* @IWL_UCODE_TLV_API_QUOTA_LOW_LATENCY: Quota command includes a field
* indicating low latency direction.
*
......@@ -267,7 +265,6 @@ enum iwl_ucode_tlv_api {
IWL_UCODE_TLV_API_ADAPTIVE_DWELL = (__force iwl_ucode_tlv_api_t)32,
IWL_UCODE_TLV_API_NEW_BEACON_TEMPLATE = (__force iwl_ucode_tlv_api_t)34,
IWL_UCODE_TLV_API_NEW_RX_STATS = (__force iwl_ucode_tlv_api_t)35,
IWL_UCODE_TLV_API_COEX_ATS_EXTERNAL = (__force iwl_ucode_tlv_api_t)37,
IWL_UCODE_TLV_API_QUOTA_LOW_LATENCY = (__force iwl_ucode_tlv_api_t)38,
NUM_IWL_UCODE_TLV_API
......@@ -313,6 +310,7 @@ typedef unsigned int __bitwise iwl_ucode_tlv_capa_t;
* @IWL_UCODE_TLV_CAPA_BT_COEX_RRC: supports BT Coex RRC
* @IWL_UCODE_TLV_CAPA_GSCAN_SUPPORT: supports gscan
* @IWL_UCODE_TLV_CAPA_STA_PM_NOTIF: firmware will send STA PM notification
* @IWL_UCODE_TLV_CAPA_TLC_OFFLOAD: firmware implements rate scaling algorithm
* @IWL_UCODE_TLV_CAPA_EXTENDED_DTS_MEASURE: extended DTS measurement
* @IWL_UCODE_TLV_CAPA_SHORT_PM_TIMEOUTS: supports short PM timeouts
* @IWL_UCODE_TLV_CAPA_BT_MPLUT_SUPPORT: supports bt-coex Multi-priority LUT
......@@ -367,6 +365,7 @@ enum iwl_ucode_tlv_capa {
IWL_UCODE_TLV_CAPA_BINDING_CDB_SUPPORT = (__force iwl_ucode_tlv_capa_t)39,
IWL_UCODE_TLV_CAPA_CDB_SUPPORT = (__force iwl_ucode_tlv_capa_t)40,
IWL_UCODE_TLV_CAPA_D0I3_END_FIRST = (__force iwl_ucode_tlv_capa_t)41,
IWL_UCODE_TLV_CAPA_TLC_OFFLOAD = (__force iwl_ucode_tlv_capa_t)43,
IWL_UCODE_TLV_CAPA_EXTENDED_DTS_MEASURE = (__force iwl_ucode_tlv_capa_t)64,
IWL_UCODE_TLV_CAPA_SHORT_PM_TIMEOUTS = (__force iwl_ucode_tlv_capa_t)65,
IWL_UCODE_TLV_CAPA_BT_MPLUT_SUPPORT = (__force iwl_ucode_tlv_capa_t)67,
......
......@@ -63,8 +63,8 @@
#include "runtime.h"
#include "fw/api/commands.h"
static void iwl_parse_shared_mem_a000(struct iwl_fw_runtime *fwrt,
struct iwl_rx_packet *pkt)
static void iwl_parse_shared_mem_22000(struct iwl_fw_runtime *fwrt,
struct iwl_rx_packet *pkt)
{
struct iwl_shared_mem_cfg *mem_cfg = (void *)pkt->data;
int i, lmac;
......@@ -143,8 +143,8 @@ void iwl_get_shared_mem_conf(struct iwl_fw_runtime *fwrt)
return;
pkt = cmd.resp_pkt;
if (fwrt->trans->cfg->device_family == IWL_DEVICE_FAMILY_A000)
iwl_parse_shared_mem_a000(fwrt, pkt);
if (fwrt->trans->cfg->device_family == IWL_DEVICE_FAMILY_22000)
iwl_parse_shared_mem_22000(fwrt, pkt);
else
iwl_parse_shared_mem(fwrt, pkt);
......
......@@ -89,7 +89,7 @@ enum iwl_device_family {
IWL_DEVICE_FAMILY_7000,
IWL_DEVICE_FAMILY_8000,
IWL_DEVICE_FAMILY_9000,
IWL_DEVICE_FAMILY_A000,
IWL_DEVICE_FAMILY_22000,
};
/*
......@@ -266,7 +266,7 @@ struct iwl_tt_params {
#define OTP_LOW_IMAGE_SIZE_FAMILY_7000 (16 * 512 * sizeof(u16)) /* 16 KB */
#define OTP_LOW_IMAGE_SIZE_FAMILY_8000 (32 * 512 * sizeof(u16)) /* 32 KB */
#define OTP_LOW_IMAGE_SIZE_FAMILY_9000 OTP_LOW_IMAGE_SIZE_FAMILY_8000
#define OTP_LOW_IMAGE_SIZE_FAMILY_A000 OTP_LOW_IMAGE_SIZE_FAMILY_9000
#define OTP_LOW_IMAGE_SIZE_FAMILY_22000 OTP_LOW_IMAGE_SIZE_FAMILY_9000
struct iwl_eeprom_params {
const u8 regulatory_bands[7];
......@@ -330,7 +330,7 @@ struct iwl_pwr_tx_backoff {
* @vht_mu_mimo_supported: VHT MU-MIMO support
* @rf_id: need to read rf_id to determine the firmware image
* @integrated: discrete or integrated
* @gen2: a000 and on transport operation
* @gen2: 22000 and on transport operation
* @cdb: CDB support
* @nvm_type: see &enum iwl_nvm_type
* @tx_cmd_queue_size: size of the cmd queue. If zero, use the same value as
......@@ -477,13 +477,13 @@ extern const struct iwl_cfg iwl9460_2ac_cfg_soc;
extern const struct iwl_cfg iwl9461_2ac_cfg_soc;
extern const struct iwl_cfg iwl9462_2ac_cfg_soc;
extern const struct iwl_cfg iwl9560_2ac_cfg_soc;
extern const struct iwl_cfg iwla000_2ac_cfg_hr;
extern const struct iwl_cfg iwla000_2ac_cfg_hr_cdb;
extern const struct iwl_cfg iwla000_2ac_cfg_jf;
extern const struct iwl_cfg iwla000_2ax_cfg_hr;
extern const struct iwl_cfg iwla000_2ax_cfg_qnj_hr_f0;
extern const struct iwl_cfg iwla000_2ax_cfg_qnj_jf_b0;
extern const struct iwl_cfg iwla000_2ax_cfg_qnj_hr_a0;
extern const struct iwl_cfg iwl22000_2ac_cfg_hr;
extern const struct iwl_cfg iwl22000_2ac_cfg_hr_cdb;
extern const struct iwl_cfg iwl22000_2ac_cfg_jf;
extern const struct iwl_cfg iwl22000_2ax_cfg_hr;
extern const struct iwl_cfg iwl22000_2ax_cfg_qnj_hr_f0;
extern const struct iwl_cfg iwl22000_2ax_cfg_qnj_jf_b0;
extern const struct iwl_cfg iwl22000_2ax_cfg_qnj_hr_a0;
#endif /* CONFIG_IWLMVM */
#endif /* __IWL_CONFIG_H__ */
......@@ -551,7 +551,7 @@ struct iwl_trans_ops {
unsigned int queue_wdg_timeout);
void (*txq_disable)(struct iwl_trans *trans, int queue,
bool configure_scd);
/* a000 functions */
/* 22000 functions */
int (*txq_alloc)(struct iwl_trans *trans,
struct iwl_tx_queue_cfg_cmd *cmd,
int cmd_id,
......
此差异已折叠。
......@@ -11,4 +11,5 @@ config WLAN_VENDOR_MEDIATEK
if WLAN_VENDOR_MEDIATEK
source "drivers/net/wireless/mediatek/mt7601u/Kconfig"
source "drivers/net/wireless/mediatek/mt76/Kconfig"
endif # WLAN_VENDOR_MEDIATEK
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册